Update Linux to v5.10.109
Sourced from [1]
[1] https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.10.109.tar.xz
Change-Id: I19bca9fc6762d4e63bcf3e4cba88bbe560d9c76c
Signed-off-by: Olivier Deprez <olivier.deprez@arm.com>
diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
index f1f6141..7e152bb 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 @@
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_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_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_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_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_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,8 +156,10 @@
config VIDEO_TI_CAL
tristate "TI CAL (Camera Adaptation Layer) driver"
- depends on VIDEO_DEV && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
- depends on SOC_DRA7XX || COMPILE_TEST
+ 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
help
@@ -165,7 +173,6 @@
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 @@
select SRAM
select VIDEOBUF2_DMA_CONTIG
select VIDEOBUF2_VMALLOC
+ select V4L2_JPEG_HELPER
select V4L2_MEM2MEM_DEV
select GENERIC_ALLOCATOR
help
@@ -200,7 +208,7 @@
config VIDEO_MEDIATEK_JPEG
tristate "Mediatek JPEG Codec driver"
- depends on MTK_IOMMU_V1 || COMPILE_TEST
+ depends on MTK_IOMMU_V1 || MTK_IOMMU || COMPILE_TEST
depends on VIDEO_DEV && VIDEO_V4L2
depends on ARCH_MEDIATEK || COMPILE_TEST
select VIDEOBUF2_DMA_CONTIG
@@ -245,16 +253,31 @@
depends on MTK_IOMMU || COMPILE_TEST
depends on VIDEO_DEV && VIDEO_V4L2
depends on ARCH_MEDIATEK || COMPILE_TEST
+ depends on VIDEO_MEDIATEK_VPU || MTK_SCP
+ # The two following lines ensure we have the same state ("m" or "y") as
+ # our dependencies, to avoid missing symbols during link.
+ depends on VIDEO_MEDIATEK_VPU || !VIDEO_MEDIATEK_VPU
+ depends on MTK_SCP || !MTK_SCP
select VIDEOBUF2_DMA_CONTIG
select V4L2_MEM2MEM_DEV
- select VIDEO_MEDIATEK_VPU
+ select VIDEO_MEDIATEK_VCODEC_VPU if VIDEO_MEDIATEK_VPU
+ select VIDEO_MEDIATEK_VCODEC_SCP if MTK_SCP
help
- Mediatek video codec driver provides HW capability to
- encode and decode in a range of video formats
- This driver rely on VPU driver to communicate with VPU.
+ Mediatek video codec driver provides HW capability to
+ encode and decode in a range of video formats on MT8173
+ and MT8183.
- To compile this driver as a module, choose M here: the
- module will be called mtk-vcodec
+ Note that support for MT8173 requires VIDEO_MEDIATEK_VPU to
+ also be selected. Support for MT8183 depends on MTK_SCP.
+
+ To compile this driver as modules, choose M here: the
+ modules will be called mtk-vcodec-dec and mtk-vcodec-enc.
+
+config VIDEO_MEDIATEK_VCODEC_VPU
+ bool
+
+config VIDEO_MEDIATEK_VCODEC_SCP
+ bool
config VIDEO_MEM2MEM_DEINTERLACE
tristate "Deinterlace support"
@@ -385,15 +408,6 @@
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
@@ -427,17 +441,19 @@
help
This is a driver for the Renesas Frame Compression Processor (FCP).
The FCP is a companion module of video processing modules in the
- Renesas R-Car Gen3 SoCs. It handles memory access for the codec,
- VSP and FDP modules.
+ Renesas R-Car Gen3 and RZ/G2 SoCs. It handles memory access for
+ the codec, VSP and FDP modules.
To compile this driver as a module, choose M here: the module
will be called rcar-fcp.
config VIDEO_RENESAS_VSP1
tristate "Renesas VSP1 Video Processing Engine"
- depends on VIDEO_V4L2 && 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
@@ -483,6 +499,7 @@
tristate "Qualcomm Venus V4L2 encoder/decoder driver"
depends on VIDEO_DEV && VIDEO_V4L2
depends on (ARCH_QCOM && IOMMU_DMA) || COMPILE_TEST
+ depends on INTERCONNECT || !INTERCONNECT
select QCOM_MDT_LOADER if ARCH_QCOM
select QCOM_SCM if ARCH_QCOM
select VIDEOBUF2_DMA_SG
@@ -493,6 +510,31 @@
on various Qualcomm SoCs.
To compile this driver as a module choose m here.
+config VIDEO_SUN8I_DEINTERLACE
+ tristate "Allwinner Deinterlace driver"
+ depends on VIDEO_DEV && VIDEO_V4L2
+ depends on ARCH_SUNXI || COMPILE_TEST
+ depends on COMMON_CLK && OF
+ depends on PM
+ select VIDEOBUF2_DMA_CONTIG
+ select V4L2_MEM2MEM_DEV
+ help
+ Support for the Allwinner deinterlace unit with scaling
+ capability found on some SoCs, like H3.
+ To compile this driver as a module choose m here.
+
+config VIDEO_SUN8I_ROTATE
+ tristate "Allwinner DE2 rotation driver"
+ depends on VIDEO_DEV && VIDEO_V4L2
+ depends on ARCH_SUNXI || COMPILE_TEST
+ depends on COMMON_CLK && OF
+ depends on PM
+ select VIDEOBUF2_DMA_CONTIG
+ select V4L2_MEM2MEM_DEV
+ help
+ Support for the Allwinner DE2 rotation unit.
+ To compile this driver as a module choose m here.
+
endif # V4L_MEM2MEM_DRIVERS
# TI VIDEO PORT Helper Modules
@@ -506,29 +548,6 @@
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
@@ -539,130 +558,6 @@
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 PREEMPT || COMPILE_TEST
- select CEC_CORE
- select CEC_PIN
- 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 6ee7eb0..62b6cdc 100644
--- a/drivers/media/platform/Makefile
+++ b/drivers/media/platform/Makefile
@@ -14,24 +14,13 @@
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-$(CONFIG_VIDEO_TI_VPE) += ti-vpe/
-
-obj-$(CONFIG_VIDEO_TI_CAL) += ti-vpe/
+obj-y += ti-vpe/
obj-$(CONFIG_VIDEO_MX2_EMMAPRP) += mx2_emmaprp.o
obj-$(CONFIG_VIDEO_CODA) += coda/
obj-$(CONFIG_VIDEO_IMX_PXP) += imx-pxp.o
-obj-$(CONFIG_VIDEO_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
@@ -42,22 +31,16 @@
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
@@ -96,8 +79,4 @@
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 d6f2e3d..9ef898f 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/am437x/am437x-vpfe.c b/drivers/media/platform/am437x/am437x-vpfe.c
index e13dbf2..31cee69 100644
--- a/drivers/media/platform/am437x/am437x-vpfe.c
+++ b/drivers/media/platform/am437x/am437x-vpfe.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* TI VPFE capture Driver
*
@@ -5,19 +6,6 @@
*
* Benoit Parrot <bparrot@ti.com>
* Lad, Prabhakar <prabhakar.csengg@gmail.com>
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
*/
#include <linux/delay.h>
@@ -38,6 +26,7 @@
#include <media/v4l2-ctrls.h>
#include <media/v4l2-event.h>
#include <media/v4l2-fwnode.h>
+#include <media/v4l2-rect.h>
#include "am437x-vpfe.h"
@@ -69,125 +58,64 @@
{V4L2_STD_625_50, 720, 576, {54, 59}, 1},
};
-struct bus_format {
- unsigned int width;
- unsigned int bpp;
-};
-
-/*
- * struct vpfe_fmt - VPFE media bus format information
- * @code: V4L2 media bus format code
- * @shifted: V4L2 media bus format code for the same pixel layout but
- * shifted to be 8 bits per pixel. =0 if format is not shiftable.
- * @pixelformat: V4L2 pixel format FCC identifier
- * @width: Bits per pixel (when transferred over a bus)
- * @bpp: Bytes per pixel (when stored in memory)
- * @supported: Indicates format supported by subdev
- */
-struct vpfe_fmt {
- u32 fourcc;
- u32 code;
- struct bus_format l;
- struct bus_format s;
- bool supported;
- u32 index;
-};
-
-static struct vpfe_fmt formats[] = {
+static struct vpfe_fmt formats[VPFE_NUM_FORMATS] = {
{
.fourcc = V4L2_PIX_FMT_YUYV,
.code = MEDIA_BUS_FMT_YUYV8_2X8,
- .l.width = 10,
- .l.bpp = 4,
- .s.width = 8,
- .s.bpp = 2,
- .supported = false,
+ .bitsperpixel = 16,
}, {
.fourcc = V4L2_PIX_FMT_UYVY,
.code = MEDIA_BUS_FMT_UYVY8_2X8,
- .l.width = 10,
- .l.bpp = 4,
- .s.width = 8,
- .s.bpp = 2,
- .supported = false,
+ .bitsperpixel = 16,
}, {
.fourcc = V4L2_PIX_FMT_YVYU,
.code = MEDIA_BUS_FMT_YVYU8_2X8,
- .l.width = 10,
- .l.bpp = 4,
- .s.width = 8,
- .s.bpp = 2,
- .supported = false,
+ .bitsperpixel = 16,
}, {
.fourcc = V4L2_PIX_FMT_VYUY,
.code = MEDIA_BUS_FMT_VYUY8_2X8,
- .l.width = 10,
- .l.bpp = 4,
- .s.width = 8,
- .s.bpp = 2,
- .supported = false,
+ .bitsperpixel = 16,
}, {
.fourcc = V4L2_PIX_FMT_SBGGR8,
.code = MEDIA_BUS_FMT_SBGGR8_1X8,
- .l.width = 10,
- .l.bpp = 2,
- .s.width = 8,
- .s.bpp = 1,
- .supported = false,
+ .bitsperpixel = 8,
}, {
.fourcc = V4L2_PIX_FMT_SGBRG8,
.code = MEDIA_BUS_FMT_SGBRG8_1X8,
- .l.width = 10,
- .l.bpp = 2,
- .s.width = 8,
- .s.bpp = 1,
- .supported = false,
+ .bitsperpixel = 8,
}, {
.fourcc = V4L2_PIX_FMT_SGRBG8,
.code = MEDIA_BUS_FMT_SGRBG8_1X8,
- .l.width = 10,
- .l.bpp = 2,
- .s.width = 8,
- .s.bpp = 1,
- .supported = false,
+ .bitsperpixel = 8,
}, {
.fourcc = V4L2_PIX_FMT_SRGGB8,
.code = MEDIA_BUS_FMT_SRGGB8_1X8,
- .l.width = 10,
- .l.bpp = 2,
- .s.width = 8,
- .s.bpp = 1,
- .supported = false,
+ .bitsperpixel = 8,
}, {
.fourcc = V4L2_PIX_FMT_RGB565,
.code = MEDIA_BUS_FMT_RGB565_2X8_LE,
- .l.width = 10,
- .l.bpp = 4,
- .s.width = 8,
- .s.bpp = 2,
- .supported = false,
+ .bitsperpixel = 16,
}, {
.fourcc = V4L2_PIX_FMT_RGB565X,
.code = MEDIA_BUS_FMT_RGB565_2X8_BE,
- .l.width = 10,
- .l.bpp = 4,
- .s.width = 8,
- .s.bpp = 2,
- .supported = false,
+ .bitsperpixel = 16,
},
};
-static int
-__vpfe_get_format(struct vpfe_device *vpfe,
- struct v4l2_format *format, unsigned int *bpp);
+static int __subdev_get_format(struct vpfe_device *vpfe,
+ struct v4l2_mbus_framefmt *fmt);
+static int vpfe_calc_format_size(struct vpfe_device *vpfe,
+ const struct vpfe_fmt *fmt,
+ struct v4l2_format *f);
-static struct vpfe_fmt *find_format_by_code(unsigned int code)
+static struct vpfe_fmt *find_format_by_code(struct vpfe_device *vpfe,
+ unsigned int code)
{
struct vpfe_fmt *fmt;
unsigned int k;
- for (k = 0; k < ARRAY_SIZE(formats); k++) {
- fmt = &formats[k];
+ for (k = 0; k < vpfe->num_active_fmt; k++) {
+ fmt = vpfe->active_fmt[k];
if (fmt->code == code)
return fmt;
}
@@ -195,13 +123,14 @@
return NULL;
}
-static struct vpfe_fmt *find_format_by_pix(unsigned int pixelformat)
+static struct vpfe_fmt *find_format_by_pix(struct vpfe_device *vpfe,
+ unsigned int pixelformat)
{
struct vpfe_fmt *fmt;
unsigned int k;
- for (k = 0; k < ARRAY_SIZE(formats); k++) {
- fmt = &formats[k];
+ for (k = 0; k < vpfe->num_active_fmt; k++) {
+ fmt = vpfe->active_fmt[k];
if (fmt->fourcc == pixelformat)
return fmt;
}
@@ -209,48 +138,18 @@
return NULL;
}
-static void
-mbus_to_pix(struct vpfe_device *vpfe,
- const struct v4l2_mbus_framefmt *mbus,
- struct v4l2_pix_format *pix, unsigned int *bpp)
+static unsigned int __get_bytesperpixel(struct vpfe_device *vpfe,
+ const struct vpfe_fmt *fmt)
{
struct vpfe_subdev_info *sdinfo = vpfe->current_subdev;
unsigned int bus_width = sdinfo->vpfe_param.bus_width;
- struct vpfe_fmt *fmt;
+ u32 bpp, bus_width_bytes, clocksperpixel;
- fmt = find_format_by_code(mbus->code);
- if (WARN_ON(fmt == NULL)) {
- pr_err("Invalid mbus code set\n");
- *bpp = 1;
- return;
- }
+ bus_width_bytes = ALIGN(bus_width, 8) >> 3;
+ clocksperpixel = DIV_ROUND_UP(fmt->bitsperpixel, bus_width);
+ bpp = clocksperpixel * bus_width_bytes;
- memset(pix, 0, sizeof(*pix));
- v4l2_fill_pix_format(pix, mbus);
- pix->pixelformat = fmt->fourcc;
- *bpp = (bus_width == 10) ? fmt->l.bpp : fmt->s.bpp;
-
- /* pitch should be 32 bytes aligned */
- pix->bytesperline = ALIGN(pix->width * *bpp, 32);
- pix->sizeimage = pix->bytesperline * pix->height;
-}
-
-static void pix_to_mbus(struct vpfe_device *vpfe,
- struct v4l2_pix_format *pix_fmt,
- struct v4l2_mbus_framefmt *mbus_fmt)
-{
- struct vpfe_fmt *fmt;
-
- fmt = find_format_by_pix(pix_fmt->pixelformat);
- if (!fmt) {
- /* default to first entry */
- vpfe_dbg(3, vpfe, "Invalid pixel code: %x, default used instead\n",
- pix_fmt->pixelformat);
- fmt = &formats[0];
- }
-
- memset(mbus_fmt, 0, sizeof(*mbus_fmt));
- v4l2_fill_mbus_format(mbus_fmt, pix_fmt, fmt->code);
+ return bpp;
}
/* Print Four-character-code (FOURCC) */
@@ -267,20 +166,6 @@
return code;
}
-static int
-cmp_v4l2_format(const struct v4l2_format *lhs, const struct v4l2_format *rhs)
-{
- return lhs->type == rhs->type &&
- lhs->fmt.pix.width == rhs->fmt.pix.width &&
- lhs->fmt.pix.height == rhs->fmt.pix.height &&
- lhs->fmt.pix.pixelformat == rhs->fmt.pix.pixelformat &&
- lhs->fmt.pix.field == rhs->fmt.pix.field &&
- lhs->fmt.pix.colorspace == rhs->fmt.pix.colorspace &&
- lhs->fmt.pix.ycbcr_enc == rhs->fmt.pix.ycbcr_enc &&
- lhs->fmt.pix.quantization == rhs->fmt.pix.quantization &&
- lhs->fmt.pix.xfer_func == rhs->fmt.pix.xfer_func;
-}
-
static inline u32 vpfe_reg_read(struct vpfe_ccdc *ccdc, u32 offset)
{
return ioread32(ccdc->ccdc_cfg.base_addr + offset);
@@ -345,13 +230,9 @@
if (frm_fmt == CCDC_FRMFMT_INTERLACED) {
vert_nr_lines = (image_win->height >> 1) - 1;
vert_start >>= 1;
- /* Since first line doesn't have any data */
- vert_start += 1;
/* configure VDINT0 */
val = (vert_start << VPFE_VDINT_VDINT0_SHIFT);
} else {
- /* Since first line doesn't have any data */
- vert_start += 1;
vert_nr_lines = image_win->height - 1;
/*
* configure VDINT0 and VDINT1. VDINT1 will be at half
@@ -405,7 +286,7 @@
max_data = ccdc_data_size_max_bit(ccdcparam->data_sz);
if (ccdcparam->alaw.gamma_wd > VPFE_CCDC_GAMMA_BITS_09_0 ||
- ccdcparam->alaw.gamma_wd < VPFE_CCDC_GAMMA_BITS_15_6 ||
+ ccdcparam->data_sz > VPFE_CCDC_DATA_8BITS ||
max_gamma > max_data) {
vpfe_dbg(1, vpfe, "Invalid data line select\n");
return -EINVAL;
@@ -445,46 +326,31 @@
static int vpfe_ccdc_close(struct vpfe_ccdc *ccdc, struct device *dev)
{
- int dma_cntl, i, pcr;
+ struct vpfe_device *vpfe = to_vpfe(ccdc);
+ u32 dma_cntl, pcr;
- /* If the CCDC module is still busy wait for it to be done */
- for (i = 0; i < 10; i++) {
- usleep_range(5000, 6000);
- pcr = vpfe_reg_read(ccdc, VPFE_PCR);
- if (!pcr)
- break;
+ pcr = vpfe_reg_read(ccdc, VPFE_PCR);
+ if (pcr)
+ vpfe_dbg(1, vpfe, "VPFE_PCR is still set (%x)", pcr);
- /* make sure it it is disabled */
- vpfe_pcr_enable(ccdc, 0);
- }
+ dma_cntl = vpfe_reg_read(ccdc, VPFE_DMA_CNTL);
+ if ((dma_cntl & VPFE_DMA_CNTL_OVERFLOW))
+ vpfe_dbg(1, vpfe, "VPFE_DMA_CNTL_OVERFLOW is still set (%x)",
+ dma_cntl);
/* Disable CCDC by resetting all register to default POR values */
vpfe_ccdc_restore_defaults(ccdc);
- /* if DMA_CNTL overflow bit is set. Clear it
- * It appears to take a while for this to become quiescent ~20ms
- */
- for (i = 0; i < 10; i++) {
- dma_cntl = vpfe_reg_read(ccdc, VPFE_DMA_CNTL);
- if (!(dma_cntl & VPFE_DMA_CNTL_OVERFLOW))
- break;
-
- /* Clear the overflow bit */
- vpfe_reg_write(ccdc, dma_cntl, VPFE_DMA_CNTL);
- usleep_range(5000, 6000);
- }
-
/* Disabled the module at the CONFIG level */
vpfe_config_enable(ccdc, 0);
pm_runtime_put_sync(dev);
-
return 0;
}
static int vpfe_ccdc_set_params(struct vpfe_ccdc *ccdc, void __user *params)
{
- struct vpfe_device *vpfe = container_of(ccdc, struct vpfe_device, ccdc);
+ struct vpfe_device *vpfe = to_vpfe(ccdc);
struct vpfe_ccdc_config_params_raw raw_params;
int x;
@@ -494,8 +360,8 @@
x = copy_from_user(&raw_params, params, sizeof(raw_params));
if (x) {
vpfe_dbg(1, vpfe,
- "vpfe_ccdc_set_params: error in copying ccdc params, %d\n",
- x);
+ "%s: error in copying ccdc params, %d\n",
+ __func__, x);
return -EFAULT;
}
@@ -513,11 +379,9 @@
*/
static void vpfe_ccdc_config_ycbcr(struct vpfe_ccdc *ccdc)
{
- struct vpfe_device *vpfe = container_of(ccdc, struct vpfe_device, ccdc);
struct ccdc_params_ycbcr *params = &ccdc->ccdc_cfg.ycbcr;
u32 syn_mode;
- vpfe_dbg(3, vpfe, "vpfe_ccdc_config_ycbcr:\n");
/*
* first restore the CCDC registers to default values
* This is important since we assume default values to be set in
@@ -642,15 +506,13 @@
*/
static void vpfe_ccdc_config_raw(struct vpfe_ccdc *ccdc)
{
- struct vpfe_device *vpfe = container_of(ccdc, struct vpfe_device, ccdc);
+ struct vpfe_device *vpfe = to_vpfe(ccdc);
struct vpfe_ccdc_config_params_raw *config_params =
&ccdc->ccdc_cfg.bayer.config_params;
struct ccdc_params_raw *params = &ccdc->ccdc_cfg.bayer;
unsigned int syn_mode;
unsigned int val;
- vpfe_dbg(3, vpfe, "vpfe_ccdc_config_raw:\n");
-
/* Reset CCDC */
vpfe_ccdc_restore_defaults(ccdc);
@@ -749,10 +611,10 @@
static int vpfe_ccdc_set_pixel_format(struct vpfe_ccdc *ccdc, u32 pixfmt)
{
- struct vpfe_device *vpfe = container_of(ccdc, struct vpfe_device, ccdc);
+ struct vpfe_device *vpfe = to_vpfe(ccdc);
- vpfe_dbg(1, vpfe, "vpfe_ccdc_set_pixel_format: if_type: %d, pixfmt:%s\n",
- ccdc->ccdc_cfg.if_type, print_fourcc(pixfmt));
+ vpfe_dbg(1, vpfe, "%s: if_type: %d, pixfmt:%s\n",
+ __func__, ccdc->ccdc_cfg.if_type, print_fourcc(pixfmt));
if (ccdc->ccdc_cfg.if_type == VPFE_RAW_BAYER) {
ccdc->ccdc_cfg.bayer.pix_fmt = CCDC_PIXFMT_RAW;
@@ -881,7 +743,7 @@
static int vpfe_ccdc_set_hw_if_params(struct vpfe_ccdc *ccdc,
struct vpfe_hw_if_param *params)
{
- struct vpfe_device *vpfe = container_of(ccdc, struct vpfe_device, ccdc);
+ struct vpfe_device *vpfe = to_vpfe(ccdc);
ccdc->ccdc_cfg.if_type = params->if_type;
@@ -1036,10 +898,9 @@
static int vpfe_config_ccdc_image_format(struct vpfe_device *vpfe)
{
enum ccdc_frmfmt frm_fmt = CCDC_FRMFMT_INTERLACED;
+ u32 bpp;
int ret = 0;
- vpfe_dbg(2, vpfe, "vpfe_config_ccdc_image_format\n");
-
vpfe_dbg(1, vpfe, "pixelformat: %s\n",
print_fourcc(vpfe->fmt.fmt.pix.pixelformat));
@@ -1050,7 +911,8 @@
}
/* configure the image window */
- vpfe_ccdc_set_image_window(&vpfe->ccdc, &vpfe->crop, vpfe->bpp);
+ bpp = __get_bytesperpixel(vpfe, vpfe->current_vpfe_fmt);
+ vpfe_ccdc_set_image_window(&vpfe->ccdc, &vpfe->crop, bpp);
switch (vpfe->fmt.fmt.pix.field) {
case V4L2_FIELD_INTERLACED:
@@ -1094,7 +956,8 @@
static int vpfe_config_image_format(struct vpfe_device *vpfe,
v4l2_std_id std_id)
{
- struct v4l2_pix_format *pix = &vpfe->fmt.fmt.pix;
+ struct vpfe_fmt *fmt;
+ struct v4l2_mbus_framefmt mbus_fmt;
int i, ret;
for (i = 0; i < ARRAY_SIZE(vpfe_standards); i++) {
@@ -1116,26 +979,29 @@
return -EINVAL;
}
- vpfe->crop.top = vpfe->crop.left = 0;
- vpfe->crop.width = vpfe->std_info.active_pixels;
- vpfe->crop.height = vpfe->std_info.active_lines;
- pix->width = vpfe->crop.width;
- pix->height = vpfe->crop.height;
- pix->pixelformat = V4L2_PIX_FMT_YUYV;
-
- /* first field and frame format based on standard frame format */
- if (vpfe->std_info.frame_format)
- pix->field = V4L2_FIELD_INTERLACED;
- else
- pix->field = V4L2_FIELD_NONE;
-
- ret = __vpfe_get_format(vpfe, &vpfe->fmt, &vpfe->bpp);
+ ret = __subdev_get_format(vpfe, &mbus_fmt);
if (ret)
return ret;
+ fmt = find_format_by_code(vpfe, mbus_fmt.code);
+ if (!fmt) {
+ vpfe_dbg(3, vpfe, "mbus code format (0x%08x) not found.\n",
+ mbus_fmt.code);
+ return -EINVAL;
+ }
+
+ /* Save current subdev format */
+ v4l2_fill_pix_format(&vpfe->fmt.fmt.pix, &mbus_fmt);
+ vpfe->fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ vpfe->fmt.fmt.pix.pixelformat = fmt->fourcc;
+ vpfe_calc_format_size(vpfe, fmt, &vpfe->fmt);
+ vpfe->current_vpfe_fmt = fmt;
+
/* Update the crop window based on found values */
- vpfe->crop.width = pix->width;
- vpfe->crop.height = pix->height;
+ vpfe->crop.top = 0;
+ vpfe->crop.left = 0;
+ vpfe->crop.width = mbus_fmt.width;
+ vpfe->crop.height = mbus_fmt.height;
return vpfe_config_ccdc_image_format(vpfe);
}
@@ -1155,7 +1021,9 @@
if (ret)
return ret;
- pm_runtime_get_sync(vpfe->pdev);
+ ret = pm_runtime_resume_and_get(vpfe->pdev);
+ if (ret < 0)
+ return ret;
vpfe_config_enable(&vpfe->ccdc, 1);
@@ -1237,22 +1105,29 @@
* This function will get next buffer from the dma queue and
* set the buffer address in the vpfe register for capture.
* the buffer is marked active
- *
- * Assumes caller is holding vpfe->dma_queue_lock already
*/
-static inline void vpfe_schedule_next_buffer(struct vpfe_device *vpfe)
+static void vpfe_schedule_next_buffer(struct vpfe_device *vpfe)
{
+ dma_addr_t addr;
+
+ spin_lock(&vpfe->dma_queue_lock);
+ if (list_empty(&vpfe->dma_queue)) {
+ spin_unlock(&vpfe->dma_queue_lock);
+ return;
+ }
+
vpfe->next_frm = list_entry(vpfe->dma_queue.next,
struct vpfe_cap_buffer, list);
list_del(&vpfe->next_frm->list);
+ spin_unlock(&vpfe->dma_queue_lock);
- vpfe_set_sdr_addr(&vpfe->ccdc,
- vb2_dma_contig_plane_dma_addr(&vpfe->next_frm->vb.vb2_buf, 0));
+ addr = vb2_dma_contig_plane_dma_addr(&vpfe->next_frm->vb.vb2_buf, 0);
+ vpfe_set_sdr_addr(&vpfe->ccdc, addr);
}
static inline void vpfe_schedule_bottom_field(struct vpfe_device *vpfe)
{
- unsigned long addr;
+ dma_addr_t addr;
addr = vb2_dma_contig_plane_dma_addr(&vpfe->next_frm->vb.vb2_buf, 0) +
vpfe->field_off;
@@ -1277,6 +1152,58 @@
vpfe->cur_frm = vpfe->next_frm;
}
+static void vpfe_handle_interlaced_irq(struct vpfe_device *vpfe,
+ enum v4l2_field field)
+{
+ int fid;
+
+ /* interlaced or TB capture check which field
+ * we are in hardware
+ */
+ fid = vpfe_ccdc_getfid(&vpfe->ccdc);
+
+ /* switch the software maintained field id */
+ vpfe->field ^= 1;
+ if (fid == vpfe->field) {
+ /* we are in-sync here,continue */
+ if (fid == 0) {
+ /*
+ * One frame is just being captured. If the
+ * next frame is available, release the
+ * current frame and move on
+ */
+ if (vpfe->cur_frm != vpfe->next_frm)
+ vpfe_process_buffer_complete(vpfe);
+
+ if (vpfe->stopping)
+ return;
+
+ /*
+ * based on whether the two fields are stored
+ * interleave or separately in memory,
+ * reconfigure the CCDC memory address
+ */
+ if (field == V4L2_FIELD_SEQ_TB)
+ vpfe_schedule_bottom_field(vpfe);
+ } else {
+ /*
+ * if one field is just being captured configure
+ * the next frame get the next frame from the empty
+ * queue if no frame is available hold on to the
+ * current buffer
+ */
+ if (vpfe->cur_frm == vpfe->next_frm)
+ vpfe_schedule_next_buffer(vpfe);
+ }
+ } else if (fid == 0) {
+ /*
+ * out of sync. Recover from any hardware out-of-sync.
+ * May loose one frame
+ */
+ vpfe->field = fid;
+ }
+}
+
/*
* vpfe_isr : ISR handler for vpfe capture (VINT0)
* @irq: irq number
@@ -1288,76 +1215,28 @@
static irqreturn_t vpfe_isr(int irq, void *dev)
{
struct vpfe_device *vpfe = (struct vpfe_device *)dev;
- enum v4l2_field field;
- int intr_status;
- int fid;
+ enum v4l2_field field = vpfe->fmt.fmt.pix.field;
+ int intr_status, stopping = vpfe->stopping;
intr_status = vpfe_reg_read(&vpfe->ccdc, VPFE_IRQ_STS);
if (intr_status & VPFE_VDINT0) {
- field = vpfe->fmt.fmt.pix.field;
-
if (field == V4L2_FIELD_NONE) {
- /* handle progressive frame capture */
if (vpfe->cur_frm != vpfe->next_frm)
vpfe_process_buffer_complete(vpfe);
- goto next_intr;
+ } else {
+ vpfe_handle_interlaced_irq(vpfe, field);
}
-
- /* interlaced or TB capture check which field
- we are in hardware */
- fid = vpfe_ccdc_getfid(&vpfe->ccdc);
-
- /* switch the software maintained field id */
- vpfe->field ^= 1;
- if (fid == vpfe->field) {
- /* we are in-sync here,continue */
- if (fid == 0) {
- /*
- * One frame is just being captured. If the
- * next frame is available, release the
- * current frame and move on
- */
- if (vpfe->cur_frm != vpfe->next_frm)
- vpfe_process_buffer_complete(vpfe);
- /*
- * based on whether the two fields are stored
- * interleave or separately in memory,
- * reconfigure the CCDC memory address
- */
- if (field == V4L2_FIELD_SEQ_TB)
- vpfe_schedule_bottom_field(vpfe);
-
- goto next_intr;
- }
- /*
- * if one field is just being captured configure
- * the next frame get the next frame from the empty
- * queue if no frame is available hold on to the
- * current buffer
- */
- spin_lock(&vpfe->dma_queue_lock);
- if (!list_empty(&vpfe->dma_queue) &&
- vpfe->cur_frm == vpfe->next_frm)
- vpfe_schedule_next_buffer(vpfe);
- spin_unlock(&vpfe->dma_queue_lock);
- } else if (fid == 0) {
- /*
- * out of sync. Recover from any hardware out-of-sync.
- * May loose one frame
- */
- vpfe->field = fid;
+ if (stopping) {
+ vpfe->stopping = false;
+ complete(&vpfe->capture_stop);
}
}
-next_intr:
- if (intr_status & VPFE_VDINT1) {
- spin_lock(&vpfe->dma_queue_lock);
- if (vpfe->fmt.fmt.pix.field == V4L2_FIELD_NONE &&
- !list_empty(&vpfe->dma_queue) &&
+ if (intr_status & VPFE_VDINT1 && !stopping) {
+ if (field == V4L2_FIELD_NONE &&
vpfe->cur_frm == vpfe->next_frm)
vpfe_schedule_next_buffer(vpfe);
- spin_unlock(&vpfe->dma_queue_lock);
}
vpfe_clear_intr(&vpfe->ccdc, intr_status);
@@ -1394,8 +1273,6 @@
{
struct vpfe_device *vpfe = video_drvdata(file);
- vpfe_dbg(2, vpfe, "vpfe_querycap\n");
-
strscpy(cap->driver, VPFE_MODULE_NAME, sizeof(cap->driver));
strscpy(cap->card, "TI AM437x VPFE", sizeof(cap->card));
snprintf(cap->bus_info, sizeof(cap->bus_info),
@@ -1404,83 +1281,74 @@
}
/* get the format set at output pad of the adjacent subdev */
-static int __vpfe_get_format(struct vpfe_device *vpfe,
- struct v4l2_format *format, unsigned int *bpp)
+static int __subdev_get_format(struct vpfe_device *vpfe,
+ struct v4l2_mbus_framefmt *fmt)
{
- struct v4l2_mbus_framefmt mbus_fmt;
- struct vpfe_subdev_info *sdinfo;
- struct v4l2_subdev_format fmt;
+ struct v4l2_subdev *sd = vpfe->current_subdev->sd;
+ struct v4l2_subdev_format sd_fmt;
+ struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format;
int ret;
- sdinfo = vpfe->current_subdev;
- if (!sdinfo->sd)
- return -EINVAL;
+ sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ sd_fmt.pad = 0;
- fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
- fmt.pad = 0;
-
- ret = v4l2_subdev_call(sdinfo->sd, pad, get_fmt, NULL, &fmt);
- if (ret && ret != -ENOIOCTLCMD && ret != -ENODEV)
+ ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &sd_fmt);
+ if (ret)
return ret;
- if (!ret) {
- v4l2_fill_pix_format(&format->fmt.pix, &fmt.format);
- mbus_to_pix(vpfe, &fmt.format, &format->fmt.pix, bpp);
- } else {
- ret = v4l2_device_call_until_err(&vpfe->v4l2_dev,
- sdinfo->grp_id,
- pad, get_fmt,
- NULL, &fmt);
- if (ret && ret != -ENOIOCTLCMD && ret != -ENODEV)
- return ret;
- v4l2_fill_pix_format(&format->fmt.pix, &mbus_fmt);
- mbus_to_pix(vpfe, &mbus_fmt, &format->fmt.pix, bpp);
- }
+ *fmt = *mbus_fmt;
- format->type = vpfe->fmt.type;
-
- vpfe_dbg(1, vpfe,
- "%s size %dx%d (%s) bytesperline = %d, size = %d, bpp = %d\n",
- __func__, format->fmt.pix.width, format->fmt.pix.height,
- print_fourcc(format->fmt.pix.pixelformat),
- format->fmt.pix.bytesperline, format->fmt.pix.sizeimage, *bpp);
+ vpfe_dbg(1, vpfe, "%s: %dx%d code:%04X\n", __func__,
+ fmt->width, fmt->height, fmt->code);
return 0;
}
/* set the format at output pad of the adjacent subdev */
-static int __vpfe_set_format(struct vpfe_device *vpfe,
- struct v4l2_format *format, unsigned int *bpp)
+static int __subdev_set_format(struct vpfe_device *vpfe,
+ struct v4l2_mbus_framefmt *fmt)
{
- struct vpfe_subdev_info *sdinfo;
- struct v4l2_subdev_format fmt;
+ struct v4l2_subdev *sd = vpfe->current_subdev->sd;
+ struct v4l2_subdev_format sd_fmt;
+ struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format;
int ret;
- vpfe_dbg(2, vpfe, "__vpfe_set_format\n");
+ sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ sd_fmt.pad = 0;
+ *mbus_fmt = *fmt;
- sdinfo = vpfe->current_subdev;
- if (!sdinfo->sd)
- return -EINVAL;
-
- fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
- fmt.pad = 0;
-
- pix_to_mbus(vpfe, &format->fmt.pix, &fmt.format);
-
- ret = v4l2_subdev_call(sdinfo->sd, pad, set_fmt, NULL, &fmt);
+ ret = v4l2_subdev_call(sd, pad, set_fmt, NULL, &sd_fmt);
if (ret)
return ret;
- v4l2_fill_pix_format(&format->fmt.pix, &fmt.format);
- mbus_to_pix(vpfe, &fmt.format, &format->fmt.pix, bpp);
+ vpfe_dbg(1, vpfe, "%s %dx%d code:%04X\n", __func__,
+ fmt->width, fmt->height, fmt->code);
- format->type = vpfe->fmt.type;
+ return 0;
+}
- vpfe_dbg(1, vpfe,
- "%s size %dx%d (%s) bytesperline = %d, size = %d, bpp = %d\n",
- __func__, format->fmt.pix.width, format->fmt.pix.height,
- print_fourcc(format->fmt.pix.pixelformat),
- format->fmt.pix.bytesperline, format->fmt.pix.sizeimage, *bpp);
+static int vpfe_calc_format_size(struct vpfe_device *vpfe,
+ const struct vpfe_fmt *fmt,
+ struct v4l2_format *f)
+{
+ u32 bpp;
+
+ if (!fmt) {
+ vpfe_dbg(3, vpfe, "No vpfe_fmt provided!\n");
+ return -EINVAL;
+ }
+
+ bpp = __get_bytesperpixel(vpfe, fmt);
+
+ /* pitch should be 32 bytes aligned */
+ f->fmt.pix.bytesperline = ALIGN(f->fmt.pix.width * bpp, 32);
+ f->fmt.pix.sizeimage = f->fmt.pix.bytesperline *
+ f->fmt.pix.height;
+
+ vpfe_dbg(3, vpfe, "%s: fourcc: %s size: %dx%d bpl:%d img_size:%d\n",
+ __func__, print_fourcc(f->fmt.pix.pixelformat),
+ f->fmt.pix.width, f->fmt.pix.height,
+ f->fmt.pix.bytesperline, f->fmt.pix.sizeimage);
return 0;
}
@@ -1490,8 +1358,6 @@
{
struct vpfe_device *vpfe = video_drvdata(file);
- vpfe_dbg(2, vpfe, "vpfe_g_fmt\n");
-
*fmt = vpfe->fmt;
return 0;
@@ -1502,82 +1368,124 @@
{
struct vpfe_device *vpfe = video_drvdata(file);
struct vpfe_subdev_info *sdinfo;
- struct vpfe_fmt *fmt = NULL;
- unsigned int k;
-
- vpfe_dbg(2, vpfe, "vpfe_enum_format index:%d\n",
- f->index);
+ struct vpfe_fmt *fmt;
sdinfo = vpfe->current_subdev;
if (!sdinfo->sd)
return -EINVAL;
- if (f->index > ARRAY_SIZE(formats))
+ if (f->index >= vpfe->num_active_fmt)
return -EINVAL;
- for (k = 0; k < ARRAY_SIZE(formats); k++) {
- if (formats[k].index == f->index) {
- fmt = &formats[k];
- break;
- }
- }
- if (!fmt)
- return -EINVAL;
+ fmt = vpfe->active_fmt[f->index];
f->pixelformat = fmt->fourcc;
- vpfe_dbg(1, vpfe, "vpfe_enum_format: mbus index: %d code: %x pixelformat: %s\n",
- f->index, fmt->code, print_fourcc(fmt->fourcc));
+ vpfe_dbg(1, vpfe, "%s: mbus index: %d code: %x pixelformat: %s\n",
+ __func__, f->index, fmt->code, print_fourcc(fmt->fourcc));
return 0;
}
static int vpfe_try_fmt(struct file *file, void *priv,
- struct v4l2_format *fmt)
+ struct v4l2_format *f)
{
struct vpfe_device *vpfe = video_drvdata(file);
- unsigned int bpp;
+ struct v4l2_subdev *sd = vpfe->current_subdev->sd;
+ const struct vpfe_fmt *fmt;
+ struct v4l2_subdev_frame_size_enum fse;
+ int ret, found;
- vpfe_dbg(2, vpfe, "vpfe_try_fmt\n");
+ fmt = find_format_by_pix(vpfe, f->fmt.pix.pixelformat);
+ if (!fmt) {
+ /* default to first entry */
+ vpfe_dbg(3, vpfe, "Invalid pixel code: %x, default used instead\n",
+ f->fmt.pix.pixelformat);
+ fmt = vpfe->active_fmt[0];
+ f->fmt.pix.pixelformat = fmt->fourcc;
+ }
- return __vpfe_get_format(vpfe, fmt, &bpp);
+ f->fmt.pix.field = vpfe->fmt.fmt.pix.field;
+
+ /* check for/find a valid width/height */
+ ret = 0;
+ found = false;
+ fse.pad = 0;
+ fse.code = fmt->code;
+ fse.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ for (fse.index = 0; ; fse.index++) {
+ ret = v4l2_subdev_call(sd, pad, enum_frame_size,
+ NULL, &fse);
+ if (ret)
+ break;
+
+ if (f->fmt.pix.width == fse.max_width &&
+ f->fmt.pix.height == fse.max_height) {
+ found = true;
+ break;
+ } else if (f->fmt.pix.width >= fse.min_width &&
+ f->fmt.pix.width <= fse.max_width &&
+ f->fmt.pix.height >= fse.min_height &&
+ f->fmt.pix.height <= fse.max_height) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ /* use existing values as default */
+ f->fmt.pix.width = vpfe->fmt.fmt.pix.width;
+ f->fmt.pix.height = vpfe->fmt.fmt.pix.height;
+ }
+
+ /*
+ * Use current colorspace for now, it will get
+ * updated properly during s_fmt
+ */
+ f->fmt.pix.colorspace = vpfe->fmt.fmt.pix.colorspace;
+ return vpfe_calc_format_size(vpfe, fmt, f);
}
static int vpfe_s_fmt(struct file *file, void *priv,
struct v4l2_format *fmt)
{
struct vpfe_device *vpfe = video_drvdata(file);
- struct v4l2_format format;
- unsigned int bpp;
+ struct vpfe_fmt *f;
+ struct v4l2_mbus_framefmt mbus_fmt;
int ret;
- vpfe_dbg(2, vpfe, "vpfe_s_fmt\n");
-
/* If streaming is started, return error */
if (vb2_is_busy(&vpfe->buffer_queue)) {
vpfe_err(vpfe, "%s device busy\n", __func__);
return -EBUSY;
}
- ret = __vpfe_get_format(vpfe, &format, &bpp);
+ ret = vpfe_try_fmt(file, priv, fmt);
+ if (ret < 0)
+ return ret;
+
+ f = find_format_by_pix(vpfe, fmt->fmt.pix.pixelformat);
+
+ v4l2_fill_mbus_format(&mbus_fmt, &fmt->fmt.pix, f->code);
+
+ ret = __subdev_set_format(vpfe, &mbus_fmt);
if (ret)
return ret;
+ /* Just double check nothing has gone wrong */
+ if (mbus_fmt.code != f->code) {
+ vpfe_dbg(3, vpfe,
+ "%s subdev changed format on us, this should not happen\n",
+ __func__);
+ return -EINVAL;
+ }
- if (!cmp_v4l2_format(fmt, &format)) {
- /* Sensor format is different from the requested format
- * so we need to change it
- */
- ret = __vpfe_set_format(vpfe, fmt, &bpp);
- if (ret)
- return ret;
- } else /* Just make sure all of the fields are consistent */
- *fmt = format;
-
- /* First detach any IRQ if currently attached */
- vpfe_detach_irq(vpfe);
- vpfe->fmt = *fmt;
- vpfe->bpp = bpp;
+ v4l2_fill_pix_format(&vpfe->fmt.fmt.pix, &mbus_fmt);
+ vpfe->fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ vpfe->fmt.fmt.pix.pixelformat = f->fourcc;
+ vpfe_calc_format_size(vpfe, f, &vpfe->fmt);
+ *fmt = vpfe->fmt;
+ vpfe->current_vpfe_fmt = f;
/* Update the crop window based on found values */
vpfe->crop.width = fmt->fmt.pix.width;
@@ -1592,57 +1500,40 @@
{
struct vpfe_device *vpfe = video_drvdata(file);
struct v4l2_subdev_frame_size_enum fse;
- struct vpfe_subdev_info *sdinfo;
- struct v4l2_mbus_framefmt mbus;
- struct v4l2_pix_format pix;
+ struct v4l2_subdev *sd = vpfe->current_subdev->sd;
struct vpfe_fmt *fmt;
int ret;
- vpfe_dbg(2, vpfe, "vpfe_enum_size\n");
-
/* check for valid format */
- fmt = find_format_by_pix(fsize->pixel_format);
+ fmt = find_format_by_pix(vpfe, fsize->pixel_format);
if (!fmt) {
- vpfe_dbg(3, vpfe, "Invalid pixel code: %x, default used instead\n",
- fsize->pixel_format);
+ vpfe_dbg(3, vpfe, "Invalid pixel code: %x\n",
+ fsize->pixel_format);
return -EINVAL;
}
memset(fsize->reserved, 0x0, sizeof(fsize->reserved));
- sdinfo = vpfe->current_subdev;
- if (!sdinfo->sd)
- return -EINVAL;
-
- memset(&pix, 0x0, sizeof(pix));
- /* Construct pix from parameter and use default for the rest */
- pix.pixelformat = fsize->pixel_format;
- pix.width = 640;
- pix.height = 480;
- pix.colorspace = V4L2_COLORSPACE_SRGB;
- pix.field = V4L2_FIELD_NONE;
- pix_to_mbus(vpfe, &pix, &mbus);
-
memset(&fse, 0x0, sizeof(fse));
fse.index = fsize->index;
fse.pad = 0;
- fse.code = mbus.code;
+ fse.code = fmt->code;
fse.which = V4L2_SUBDEV_FORMAT_ACTIVE;
- ret = v4l2_subdev_call(sdinfo->sd, pad, enum_frame_size, NULL, &fse);
+ ret = v4l2_subdev_call(sd, pad, enum_frame_size, NULL, &fse);
if (ret)
- return -EINVAL;
+ return ret;
- vpfe_dbg(1, vpfe, "vpfe_enum_size: index: %d code: %x W:[%d,%d] H:[%d,%d]\n",
- fse.index, fse.code, fse.min_width, fse.max_width,
- fse.min_height, fse.max_height);
+ vpfe_dbg(1, vpfe, "%s: index: %d code: %x W:[%d,%d] H:[%d,%d]\n",
+ __func__, fse.index, fse.code, fse.min_width, fse.max_width,
+ fse.min_height, fse.max_height);
fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
fsize->discrete.width = fse.max_width;
fsize->discrete.height = fse.max_height;
- vpfe_dbg(1, vpfe, "vpfe_enum_size: index: %d pixformat: %s size: %dx%d\n",
- fsize->index, print_fourcc(fsize->pixel_format),
- fsize->discrete.width, fsize->discrete.height);
+ vpfe_dbg(1, vpfe, "%s: index: %d pixformat: %s size: %dx%d\n",
+ __func__, fsize->index, print_fourcc(fsize->pixel_format),
+ fsize->discrete.width, fsize->discrete.height);
return 0;
}
@@ -1707,8 +1598,6 @@
struct vpfe_subdev_info *sdinfo;
int subdev, index;
- vpfe_dbg(2, vpfe, "vpfe_enum_input\n");
-
if (vpfe_get_subdev_input_index(vpfe, &subdev, &index,
inp->index) < 0) {
vpfe_dbg(1, vpfe,
@@ -1725,8 +1614,6 @@
{
struct vpfe_device *vpfe = video_drvdata(file);
- vpfe_dbg(2, vpfe, "vpfe_g_input\n");
-
return vpfe_get_app_input_index(vpfe, index);
}
@@ -1739,8 +1626,6 @@
u32 input, output;
int ret;
- vpfe_dbg(2, vpfe, "vpfe_set_input: index: %d\n", index);
-
/* If streaming is started, return error */
if (vb2_is_busy(&vpfe->buffer_queue)) {
vpfe_err(vpfe, "%s device busy\n", __func__);
@@ -1796,9 +1681,6 @@
{
struct vpfe_device *vpfe = video_drvdata(file);
- vpfe_dbg(2, vpfe,
- "vpfe_s_input: index: %d\n", index);
-
return vpfe_set_input(vpfe, index);
}
@@ -1807,8 +1689,6 @@
struct vpfe_device *vpfe = video_drvdata(file);
struct vpfe_subdev_info *sdinfo;
- vpfe_dbg(2, vpfe, "vpfe_querystd\n");
-
sdinfo = vpfe->current_subdev;
if (!(sdinfo->inputs[0].capabilities & V4L2_IN_CAP_STD))
return -ENODATA;
@@ -1824,8 +1704,6 @@
struct vpfe_subdev_info *sdinfo;
int ret;
- vpfe_dbg(2, vpfe, "vpfe_s_std\n");
-
sdinfo = vpfe->current_subdev;
if (!(sdinfo->inputs[0].capabilities & V4L2_IN_CAP_STD))
return -ENODATA;
@@ -1857,8 +1735,6 @@
struct vpfe_device *vpfe = video_drvdata(file);
struct vpfe_subdev_info *sdinfo;
- vpfe_dbg(2, vpfe, "vpfe_g_std\n");
-
sdinfo = vpfe->current_subdev;
if (sdinfo->inputs[0].capabilities != V4L2_IN_CAP_STD)
return -ENODATA;
@@ -1876,8 +1752,6 @@
{
struct v4l2_rect image_win;
- vpfe_dbg(2, vpfe, "vpfe_calculate_offsets\n");
-
vpfe_ccdc_get_image_window(&vpfe->ccdc, &image_win);
vpfe->field_off = image_win.height * image_win.width;
}
@@ -1961,6 +1835,29 @@
spin_unlock_irqrestore(&vpfe->dma_queue_lock, flags);
}
+static void vpfe_return_all_buffers(struct vpfe_device *vpfe,
+ enum vb2_buffer_state state)
+{
+ struct vpfe_cap_buffer *buf, *node;
+ unsigned long flags;
+
+ spin_lock_irqsave(&vpfe->dma_queue_lock, flags);
+ list_for_each_entry_safe(buf, node, &vpfe->dma_queue, list) {
+ vb2_buffer_done(&buf->vb.vb2_buf, state);
+ list_del(&buf->list);
+ }
+
+ if (vpfe->cur_frm)
+ vb2_buffer_done(&vpfe->cur_frm->vb.vb2_buf, state);
+
+ if (vpfe->next_frm && vpfe->next_frm != vpfe->cur_frm)
+ vb2_buffer_done(&vpfe->next_frm->vb.vb2_buf, state);
+
+ vpfe->cur_frm = NULL;
+ vpfe->next_frm = NULL;
+ spin_unlock_irqrestore(&vpfe->dma_queue_lock, flags);
+}
+
/*
* vpfe_start_streaming : Starts the DMA engine for streaming
* @vb: ptr to vb2_buffer
@@ -1969,7 +1866,6 @@
static int vpfe_start_streaming(struct vb2_queue *vq, unsigned int count)
{
struct vpfe_device *vpfe = vb2_get_drv_priv(vq);
- struct vpfe_cap_buffer *buf, *tmp;
struct vpfe_subdev_info *sdinfo;
unsigned long flags;
unsigned long addr;
@@ -1984,6 +1880,9 @@
vpfe_attach_irq(vpfe);
+ vpfe->stopping = false;
+ init_completion(&vpfe->capture_stop);
+
if (vpfe->ccdc.ccdc_cfg.if_type == VPFE_RAW_BAYER)
vpfe_ccdc_config_raw(&vpfe->ccdc);
else
@@ -2012,11 +1911,8 @@
return 0;
err:
- list_for_each_entry_safe(buf, tmp, &vpfe->dma_queue, list) {
- list_del(&buf->list);
- vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED);
- }
-
+ vpfe_return_all_buffers(vpfe, VB2_BUF_STATE_QUEUED);
+ vpfe_pcr_enable(&vpfe->ccdc, 0);
return ret;
}
@@ -2031,11 +1927,15 @@
{
struct vpfe_device *vpfe = vb2_get_drv_priv(vq);
struct vpfe_subdev_info *sdinfo;
- unsigned long flags;
int ret;
vpfe_pcr_enable(&vpfe->ccdc, 0);
+ /* Wait for the last frame to be captured */
+ vpfe->stopping = true;
+ wait_for_completion_timeout(&vpfe->capture_stop,
+ msecs_to_jiffies(250));
+
vpfe_detach_irq(vpfe);
sdinfo = vpfe->current_subdev;
@@ -2044,27 +1944,7 @@
vpfe_dbg(1, vpfe, "stream off failed in subdev\n");
/* release all active buffers */
- spin_lock_irqsave(&vpfe->dma_queue_lock, flags);
- if (vpfe->cur_frm == vpfe->next_frm) {
- vb2_buffer_done(&vpfe->cur_frm->vb.vb2_buf,
- VB2_BUF_STATE_ERROR);
- } else {
- if (vpfe->cur_frm != NULL)
- vb2_buffer_done(&vpfe->cur_frm->vb.vb2_buf,
- VB2_BUF_STATE_ERROR);
- if (vpfe->next_frm != NULL)
- vb2_buffer_done(&vpfe->next_frm->vb.vb2_buf,
- VB2_BUF_STATE_ERROR);
- }
-
- while (!list_empty(&vpfe->dma_queue)) {
- vpfe->next_frm = list_entry(vpfe->dma_queue.next,
- struct vpfe_cap_buffer, list);
- list_del(&vpfe->next_frm->list);
- vb2_buffer_done(&vpfe->next_frm->vb.vb2_buf,
- VB2_BUF_STATE_ERROR);
- }
- spin_unlock_irqrestore(&vpfe->dma_queue_lock, flags);
+ vpfe_return_all_buffers(vpfe, VB2_BUF_STATE_ERROR);
}
static int vpfe_g_pixelaspect(struct file *file, void *priv,
@@ -2072,8 +1952,6 @@
{
struct vpfe_device *vpfe = video_drvdata(file);
- vpfe_dbg(2, vpfe, "vpfe_g_pixelaspect\n");
-
if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
vpfe->std_index >= ARRAY_SIZE(vpfe_standards))
return -EINVAL;
@@ -2112,26 +1990,13 @@
return 0;
}
-static int enclosed_rectangle(struct v4l2_rect *a, struct v4l2_rect *b)
-{
- if (a->left < b->left || a->top < b->top)
- return 0;
-
- if (a->left + a->width > b->left + b->width)
- return 0;
-
- if (a->top + a->height > b->top + b->height)
- return 0;
-
- return 1;
-}
-
static int
vpfe_s_selection(struct file *file, void *fh, struct v4l2_selection *s)
{
struct vpfe_device *vpfe = video_drvdata(file);
struct v4l2_rect cr = vpfe->crop;
struct v4l2_rect r = s->r;
+ u32 bpp;
/* If streaming is started, return error */
if (vb2_is_busy(&vpfe->buffer_queue)) {
@@ -2149,18 +2014,20 @@
r.left = clamp_t(unsigned int, r.left, 0, cr.width - r.width);
r.top = clamp_t(unsigned int, r.top, 0, cr.height - r.height);
- if (s->flags & V4L2_SEL_FLAG_LE && !enclosed_rectangle(&r, &s->r))
+ if (s->flags & V4L2_SEL_FLAG_LE && !v4l2_rect_enclosed(&r, &s->r))
return -ERANGE;
- if (s->flags & V4L2_SEL_FLAG_GE && !enclosed_rectangle(&s->r, &r))
+ if (s->flags & V4L2_SEL_FLAG_GE && !v4l2_rect_enclosed(&s->r, &r))
return -ERANGE;
s->r = vpfe->crop = r;
- vpfe_ccdc_set_image_window(&vpfe->ccdc, &r, vpfe->bpp);
+ bpp = __get_bytesperpixel(vpfe, vpfe->current_vpfe_fmt);
+ vpfe_ccdc_set_image_window(&vpfe->ccdc, &r, bpp);
vpfe->fmt.fmt.pix.width = r.width;
vpfe->fmt.fmt.pix.height = r.height;
- vpfe->fmt.fmt.pix.bytesperline = vpfe_ccdc_get_line_length(&vpfe->ccdc);
+ vpfe->fmt.fmt.pix.bytesperline =
+ vpfe_ccdc_get_line_length(&vpfe->ccdc);
vpfe->fmt.fmt.pix.sizeimage = vpfe->fmt.fmt.pix.bytesperline *
vpfe->fmt.fmt.pix.height;
@@ -2176,8 +2043,6 @@
struct vpfe_device *vpfe = video_drvdata(file);
int ret;
- vpfe_dbg(2, vpfe, "vpfe_ioctl_default\n");
-
if (!valid_prio) {
vpfe_err(vpfe, "%s device busy\n", __func__);
return -EBUSY;
@@ -2283,10 +2148,10 @@
struct vpfe_device, v4l2_dev);
struct v4l2_subdev_mbus_code_enum mbus_code;
struct vpfe_subdev_info *sdinfo;
+ struct vpfe_fmt *fmt;
+ int ret = 0;
bool found = false;
- int i, j;
-
- vpfe_dbg(1, vpfe, "vpfe_async_bound\n");
+ int i, j, k;
for (i = 0; i < ARRAY_SIZE(vpfe->cfg->asd); i++) {
if (vpfe->cfg->asd[i]->match.fwnode ==
@@ -2306,27 +2171,37 @@
vpfe->video_dev.tvnorms |= sdinfo->inputs[0].std;
- /* setup the supported formats & indexes */
- for (j = 0, i = 0; ; ++j) {
- struct vpfe_fmt *fmt;
- int ret;
-
+ vpfe->num_active_fmt = 0;
+ for (j = 0, i = 0; (ret != -EINVAL); ++j) {
memset(&mbus_code, 0, sizeof(mbus_code));
mbus_code.index = j;
mbus_code.which = V4L2_SUBDEV_FORMAT_ACTIVE;
ret = v4l2_subdev_call(subdev, pad, enum_mbus_code,
- NULL, &mbus_code);
+ NULL, &mbus_code);
if (ret)
- break;
-
- fmt = find_format_by_code(mbus_code.code);
- if (!fmt)
continue;
- fmt->supported = true;
- fmt->index = i++;
+ vpfe_dbg(3, vpfe,
+ "subdev %s: code: %04x idx: %d\n",
+ subdev->name, mbus_code.code, j);
+
+ for (k = 0; k < ARRAY_SIZE(formats); k++) {
+ fmt = &formats[k];
+ if (mbus_code.code != fmt->code)
+ continue;
+ vpfe->active_fmt[i] = fmt;
+ vpfe_dbg(3, vpfe,
+ "matched fourcc: %s code: %04x idx: %d\n",
+ print_fourcc(fmt->fourcc), mbus_code.code, i);
+ vpfe->num_active_fmt = ++i;
+ }
}
+ if (!i) {
+ vpfe_err(vpfe, "No suitable format reported by subdev %s\n",
+ subdev->name);
+ return -EINVAL;
+ }
return 0;
}
@@ -2382,7 +2257,7 @@
vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING |
V4L2_CAP_READWRITE;
video_set_drvdata(vdev, vpfe);
- err = video_register_device(&vpfe->video_dev, VFL_TYPE_GRABBER, -1);
+ err = video_register_device(&vpfe->video_dev, VFL_TYPE_VIDEO, -1);
if (err) {
vpfe_err(vpfe,
"Unable to register video device.\n");
@@ -2570,7 +2445,11 @@
pm_runtime_enable(&pdev->dev);
/* for now just enable it here instead of waiting for the open */
- pm_runtime_get_sync(&pdev->dev);
+ ret = pm_runtime_resume_and_get(&pdev->dev);
+ if (ret < 0) {
+ vpfe_err(vpfe, "Unable to resume device.\n");
+ goto probe_out_v4l2_unregister;
+ }
vpfe_ccdc_config_defaults(ccdc);
@@ -2609,8 +2488,6 @@
{
struct vpfe_device *vpfe = platform_get_drvdata(pdev);
- vpfe_dbg(2, vpfe, "vpfe_remove\n");
-
pm_runtime_disable(&pdev->dev);
v4l2_async_notifier_unregister(&vpfe->notifier);
@@ -2657,22 +2534,26 @@
struct vpfe_device *vpfe = dev_get_drvdata(dev);
struct vpfe_ccdc *ccdc = &vpfe->ccdc;
- /* if streaming has not started we don't care */
- if (!vb2_start_streaming_called(&vpfe->buffer_queue))
- return 0;
+ /* only do full suspend if streaming has started */
+ if (vb2_start_streaming_called(&vpfe->buffer_queue)) {
+ /*
+ * ignore RPM resume errors here, as it is already too late.
+ * A check like that should happen earlier, either at
+ * open() or just before start streaming.
+ */
+ pm_runtime_get_sync(dev);
+ vpfe_config_enable(ccdc, 1);
- pm_runtime_get_sync(dev);
- vpfe_config_enable(ccdc, 1);
+ /* Save VPFE context */
+ vpfe_save_context(ccdc);
- /* Save VPFE context */
- vpfe_save_context(ccdc);
+ /* Disable CCDC */
+ vpfe_pcr_enable(ccdc, 0);
+ vpfe_config_enable(ccdc, 0);
- /* Disable CCDC */
- vpfe_pcr_enable(ccdc, 0);
- vpfe_config_enable(ccdc, 0);
-
- /* Disable both master and slave clock */
- pm_runtime_put_sync(dev);
+ /* Disable both master and slave clock */
+ pm_runtime_put_sync(dev);
+ }
/* Select sleep pin state */
pinctrl_pm_select_sleep_state(dev);
@@ -2714,19 +2595,18 @@
struct vpfe_device *vpfe = dev_get_drvdata(dev);
struct vpfe_ccdc *ccdc = &vpfe->ccdc;
- /* if streaming has not started we don't care */
- if (!vb2_start_streaming_called(&vpfe->buffer_queue))
- return 0;
+ /* only do full resume if streaming has started */
+ if (vb2_start_streaming_called(&vpfe->buffer_queue)) {
+ /* Enable both master and slave clock */
+ pm_runtime_get_sync(dev);
+ vpfe_config_enable(ccdc, 1);
- /* Enable both master and slave clock */
- pm_runtime_get_sync(dev);
- vpfe_config_enable(ccdc, 1);
+ /* Restore VPFE context */
+ vpfe_restore_context(ccdc);
- /* Restore VPFE context */
- vpfe_restore_context(ccdc);
-
- vpfe_config_enable(ccdc, 0);
- pm_runtime_put_sync(dev);
+ vpfe_config_enable(ccdc, 0);
+ pm_runtime_put_sync(dev);
+ }
/* Select default pin state */
pinctrl_pm_select_default_state(dev);
diff --git a/drivers/media/platform/am437x/am437x-vpfe.h b/drivers/media/platform/am437x/am437x-vpfe.h
index 4678285..05ee37d 100644
--- a/drivers/media/platform/am437x/am437x-vpfe.h
+++ b/drivers/media/platform/am437x/am437x-vpfe.h
@@ -1,21 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) 2013 - 2014 Texas Instruments, Inc.
*
* Benoit Parrot <bparrot@ti.com>
* Lad, Prabhakar <prabhakar.csengg@gmail.com>
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
*/
#ifndef AM437X_VPFE_H
@@ -23,6 +11,7 @@
#include <linux/am437x-vpfe.h>
#include <linux/clk.h>
+#include <linux/completion.h>
#include <linux/device.h>
#include <linux/io.h>
#include <linux/i2c.h>
@@ -214,6 +203,25 @@
u32 ccdc_ctx[VPFE_REG_END / sizeof(u32)];
};
+/*
+ * struct vpfe_fmt - VPFE media bus format information
+ * fourcc: V4L2 pixel format code
+ * code: V4L2 media bus format code
+ * bitsperpixel: Bits per pixel over the bus
+ */
+struct vpfe_fmt {
+ u32 fourcc;
+ u32 code;
+ u32 bitsperpixel;
+};
+
+/*
+ * When formats[] is modified make sure to adjust this value also.
+ * Expect compile time warnings if VPFE_NUM_FORMATS is smaller then
+ * the number of elements in formats[].
+ */
+#define VPFE_NUM_FORMATS 10
+
struct vpfe_device {
/* V4l2 specific parameters */
/* Identifies video device for this channel */
@@ -249,8 +257,11 @@
struct vpfe_cap_buffer *next_frm;
/* Used to store pixel format */
struct v4l2_format fmt;
- /* Used to store current bytes per pixel based on current format */
- unsigned int bpp;
+ /* Used to keep a reference to the current vpfe_fmt */
+ struct vpfe_fmt *current_vpfe_fmt;
+ struct vpfe_fmt *active_fmt[VPFE_NUM_FORMATS];
+ unsigned int num_active_fmt;
+
/*
* used when IMP is chained to store the crop window which
* is different from the image window
@@ -270,6 +281,8 @@
*/
u32 field_off;
struct vpfe_ccdc ccdc;
+ int stopping;
+ struct completion capture_stop;
};
#endif /* AM437X_VPFE_H */
diff --git a/drivers/media/platform/am437x/am437x-vpfe_regs.h b/drivers/media/platform/am437x/am437x-vpfe_regs.h
index 0746c48..63ecdca 100644
--- a/drivers/media/platform/am437x/am437x-vpfe_regs.h
+++ b/drivers/media/platform/am437x/am437x-vpfe_regs.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* TI AM437x Image Sensor Interface Registers
*
@@ -5,15 +6,6 @@
*
* Benoit Parrot <bparrot@ti.com>
* Lad, Prabhakar <prabhakar.csengg@gmail.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * This program is distributed "as is" WITHOUT ANY WARRANTY of any
- * kind, whether express or implied; without even the implied warranty
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#ifndef AM437X_VPFE_REGS_H
diff --git a/drivers/media/platform/aspeed-video.c b/drivers/media/platform/aspeed-video.c
index 6dde49d..debc750 100644
--- a/drivers/media/platform/aspeed-video.c
+++ b/drivers/media/platform/aspeed-video.c
@@ -1,4 +1,6 @@
-// SPDX-License-Identifier: GPL-2.0+
+// SPDX-License-Identifier: GPL-2.0-or-later
+// Copyright 2020 IBM Corp.
+// Copyright (c) 2019-2020 Intel Corporation
#include <linux/atomic.h>
#include <linux/bitfield.h>
@@ -72,11 +74,8 @@
#define VE_SEQ_CTRL_CAP_BUSY BIT(16)
#define VE_SEQ_CTRL_COMP_BUSY BIT(18)
-#ifdef CONFIG_MACH_ASPEED_G5
-#define VE_SEQ_CTRL_JPEG_MODE BIT(13) /* AST2500 */
-#else
-#define VE_SEQ_CTRL_JPEG_MODE BIT(8) /* AST2400 */
-#endif /* CONFIG_MACH_ASPEED_G5 */
+#define AST2500_VE_SEQ_CTRL_JPEG_MODE BIT(13)
+#define AST2400_VE_SEQ_CTRL_JPEG_MODE BIT(8)
#define VE_CTRL 0x008
#define VE_CTRL_HSYNC_POL BIT(0)
@@ -133,7 +132,8 @@
#define VE_COMP_CTRL_HQ_DCT_CHR GENMASK(26, 22)
#define VE_COMP_CTRL_HQ_DCT_LUM GENMASK(31, 27)
-#define VE_OFFSET_COMP_STREAM 0x078
+#define AST2400_VE_COMP_SIZE_READ_BACK 0x078
+#define AST2600_VE_COMP_SIZE_READ_BACK 0x084
#define VE_SRC_LR_EDGE_DET 0x090
#define VE_SRC_LR_EDGE_DET_LEFT GENMASK(11, 0)
@@ -220,6 +220,9 @@
struct video_device vdev;
struct mutex video_lock; /* v4l2 and videobuf2 lock */
+ u32 jpeg_mode;
+ u32 comp_size_read;
+
wait_queue_head_t wait;
spinlock_t lock; /* buffer list lock */
struct delayed_work res_work;
@@ -243,6 +246,26 @@
#define to_aspeed_video(x) container_of((x), struct aspeed_video, v4l2_dev)
+struct aspeed_video_config {
+ u32 jpeg_mode;
+ u32 comp_size_read;
+};
+
+static const struct aspeed_video_config ast2400_config = {
+ .jpeg_mode = AST2400_VE_SEQ_CTRL_JPEG_MODE,
+ .comp_size_read = AST2400_VE_COMP_SIZE_READ_BACK,
+};
+
+static const struct aspeed_video_config ast2500_config = {
+ .jpeg_mode = AST2500_VE_SEQ_CTRL_JPEG_MODE,
+ .comp_size_read = AST2400_VE_COMP_SIZE_READ_BACK,
+};
+
+static const struct aspeed_video_config ast2600_config = {
+ .jpeg_mode = AST2500_VE_SEQ_CTRL_JPEG_MODE,
+ .comp_size_read = AST2600_VE_COMP_SIZE_READ_BACK,
+};
+
static const u32 aspeed_video_jpeg_header[ASPEED_VIDEO_JPEG_HEADER_SIZE] = {
0xe0ffd8ff, 0x464a1000, 0x01004649, 0x60000101, 0x00006000, 0x0f00feff,
0x00002d05, 0x00000000, 0x00000000, 0x00dbff00
@@ -477,6 +500,10 @@
aspeed_video_update(video, VE_INTERRUPT_CTRL, 0,
VE_INTERRUPT_MODE_DETECT);
+ /* Disable mode detect in order to re-trigger */
+ aspeed_video_update(video, VE_SEQ_CTRL,
+ VE_SEQ_CTRL_TRIG_MODE_DET, 0);
+
/* Trigger mode detect */
aspeed_video_update(video, VE_SEQ_CTRL, 0, VE_SEQ_CTRL_TRIG_MODE_DET);
}
@@ -529,6 +556,8 @@
set_bit(VIDEO_RES_CHANGE, &video->flags);
clear_bit(VIDEO_FRAME_INPRG, &video->flags);
+ video->v4l2_input_status = V4L2_IN_ST_NO_SIGNAL;
+
aspeed_video_off(video);
aspeed_video_bufs_done(video, VB2_BUF_STATE_ERROR);
@@ -572,7 +601,7 @@
if (sts & VE_INTERRUPT_COMP_COMPLETE) {
struct aspeed_video_buffer *buf;
u32 frame_size = aspeed_video_read(video,
- VE_OFFSET_COMP_STREAM);
+ video->comp_size_read);
spin_lock(&video->lock);
clear_bit(VIDEO_FRAME_INPRG, &video->flags);
@@ -624,7 +653,7 @@
int i;
int hsync_counter = 0;
int vsync_counter = 0;
- u32 sts;
+ u32 sts, ctrl;
for (i = 0; i < NUM_POLARITY_CHECKS; ++i) {
sts = aspeed_video_read(video, VE_MODE_DETECT_STATUS);
@@ -639,30 +668,29 @@
hsync_counter++;
}
- if (hsync_counter < 0 || vsync_counter < 0) {
- u32 ctrl = 0;
+ ctrl = aspeed_video_read(video, VE_CTRL);
- if (hsync_counter < 0) {
- ctrl = VE_CTRL_HSYNC_POL;
- video->detected_timings.polarities &=
- ~V4L2_DV_HSYNC_POS_POL;
- } else {
- video->detected_timings.polarities |=
- V4L2_DV_HSYNC_POS_POL;
- }
-
- if (vsync_counter < 0) {
- ctrl = VE_CTRL_VSYNC_POL;
- video->detected_timings.polarities &=
- ~V4L2_DV_VSYNC_POS_POL;
- } else {
- video->detected_timings.polarities |=
- V4L2_DV_VSYNC_POS_POL;
- }
-
- if (ctrl)
- aspeed_video_update(video, VE_CTRL, 0, ctrl);
+ if (hsync_counter < 0) {
+ ctrl |= VE_CTRL_HSYNC_POL;
+ video->detected_timings.polarities &=
+ ~V4L2_DV_HSYNC_POS_POL;
+ } else {
+ ctrl &= ~VE_CTRL_HSYNC_POL;
+ video->detected_timings.polarities |=
+ V4L2_DV_HSYNC_POS_POL;
}
+
+ if (vsync_counter < 0) {
+ ctrl |= VE_CTRL_VSYNC_POL;
+ video->detected_timings.polarities &=
+ ~V4L2_DV_VSYNC_POS_POL;
+ } else {
+ ctrl &= ~VE_CTRL_VSYNC_POL;
+ video->detected_timings.polarities |=
+ V4L2_DV_VSYNC_POS_POL;
+ }
+
+ aspeed_video_write(video, VE_CTRL, ctrl);
}
static bool aspeed_video_alloc_buf(struct aspeed_video *video,
@@ -764,10 +792,6 @@
return;
}
- /* Disable mode detect in order to re-trigger */
- aspeed_video_update(video, VE_SEQ_CTRL,
- VE_SEQ_CTRL_TRIG_MODE_DET, 0);
-
aspeed_video_check_and_set_polarity(video);
aspeed_video_enable_mode_detect(video);
@@ -908,7 +932,7 @@
FIELD_PREP(VE_COMP_CTRL_DCT_LUM, video->jpeg_quality) |
FIELD_PREP(VE_COMP_CTRL_DCT_CHR, video->jpeg_quality | 0x10);
u32 ctrl = VE_CTRL_AUTO_OR_CURSOR;
- u32 seq_ctrl = VE_SEQ_CTRL_JPEG_MODE;
+ u32 seq_ctrl = video->jpeg_mode;
if (video->frame_rate)
ctrl |= FIELD_PREP(VE_CTRL_FRC, video->frame_rate);
@@ -1315,7 +1339,6 @@
struct delayed_work *dwork = to_delayed_work(work);
struct aspeed_video *video = container_of(dwork, struct aspeed_video,
res_work);
- u32 input_status = video->v4l2_input_status;
aspeed_video_on(video);
@@ -1328,8 +1351,7 @@
aspeed_video_get_resolution(video);
if (video->detected_timings.width != video->active_timings.width ||
- video->detected_timings.height != video->active_timings.height ||
- input_status != video->v4l2_input_status) {
+ video->detected_timings.height != video->active_timings.height) {
static const struct v4l2_event ev = {
.type = V4L2_EVENT_SOURCE_CHANGE,
.u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION,
@@ -1566,16 +1588,15 @@
V4L2_CAP_STREAMING;
vdev->v4l2_dev = v4l2_dev;
strscpy(vdev->name, DEVICE_NAME, sizeof(vdev->name));
- vdev->vfl_type = VFL_TYPE_GRABBER;
+ vdev->vfl_type = VFL_TYPE_VIDEO;
vdev->vfl_dir = VFL_DIR_RX;
vdev->release = video_device_release_empty;
vdev->ioctl_ops = &aspeed_video_ioctl_ops;
vdev->lock = &video->video_lock;
video_set_drvdata(vdev, video);
- rc = video_register_device(vdev, VFL_TYPE_GRABBER, 0);
+ rc = video_register_device(vdev, VFL_TYPE_VIDEO, 0);
if (rc) {
- vb2_queue_release(vbq);
v4l2_ctrl_handler_free(&video->ctrl_handler);
v4l2_device_unregister(v4l2_dev);
@@ -1654,16 +1675,37 @@
return rc;
}
+static const struct of_device_id aspeed_video_of_match[] = {
+ { .compatible = "aspeed,ast2400-video-engine", .data = &ast2400_config },
+ { .compatible = "aspeed,ast2500-video-engine", .data = &ast2500_config },
+ { .compatible = "aspeed,ast2600-video-engine", .data = &ast2600_config },
+ {}
+};
+MODULE_DEVICE_TABLE(of, aspeed_video_of_match);
+
static int aspeed_video_probe(struct platform_device *pdev)
{
+ const struct aspeed_video_config *config;
+ const struct of_device_id *match;
+ struct aspeed_video *video;
int rc;
- struct resource *res;
- struct aspeed_video *video =
- devm_kzalloc(&pdev->dev, sizeof(*video), GFP_KERNEL);
+ video = devm_kzalloc(&pdev->dev, sizeof(*video), GFP_KERNEL);
if (!video)
return -ENOMEM;
+ video->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(video->base))
+ return PTR_ERR(video->base);
+
+ match = of_match_node(aspeed_video_of_match, pdev->dev.of_node);
+ if (!match)
+ return -EINVAL;
+
+ config = match->data;
+ video->jpeg_mode = config->jpeg_mode;
+ video->comp_size_read = config->comp_size_read;
+
video->frame_rate = 30;
video->dev = &pdev->dev;
spin_lock_init(&video->lock);
@@ -1672,13 +1714,6 @@
INIT_DELAYED_WORK(&video->res_work, aspeed_video_resolution_work);
INIT_LIST_HEAD(&video->buffers);
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-
- video->base = devm_ioremap_resource(video->dev, res);
-
- if (IS_ERR(video->base))
- return PTR_ERR(video->base);
-
rc = aspeed_video_init(video);
if (rc)
return rc;
@@ -1704,9 +1739,7 @@
clk_unprepare(video->vclk);
clk_unprepare(video->eclk);
- video_unregister_device(&video->vdev);
-
- vb2_queue_release(&video->queue);
+ vb2_video_unregister_device(&video->vdev);
v4l2_ctrl_handler_free(&video->ctrl_handler);
@@ -1720,13 +1753,6 @@
return 0;
}
-static const struct of_device_id aspeed_video_of_match[] = {
- { .compatible = "aspeed,ast2400-video-engine" },
- { .compatible = "aspeed,ast2500-video-engine" },
- {}
-};
-MODULE_DEVICE_TABLE(of, aspeed_video_of_match);
-
static struct platform_driver aspeed_video_driver = {
.driver = {
.name = DEVICE_NAME,
diff --git a/drivers/media/platform/atmel/Kconfig b/drivers/media/platform/atmel/Kconfig
index 5ae3f60..1850fe7 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/atmel/atmel-isc-base.c b/drivers/media/platform/atmel/atmel-isc-base.c
index c1c776b..fe3ec8d 100644
--- a/drivers/media/platform/atmel/atmel-isc-base.c
+++ b/drivers/media/platform/atmel/atmel-isc-base.c
@@ -22,6 +22,7 @@
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/videodev2.h>
+#include <linux/atmel-isc-media.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
@@ -73,6 +74,9 @@
{
.fourcc = V4L2_PIX_FMT_GREY,
},
+ {
+ .fourcc = V4L2_PIX_FMT_Y10,
+ },
};
/* This is a list of formats that the ISC can receive as *input* */
@@ -164,6 +168,12 @@
.mbus_code = MEDIA_BUS_FMT_RGB565_2X8_LE,
.pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT,
},
+ {
+ .fourcc = V4L2_PIX_FMT_Y10,
+ .mbus_code = MEDIA_BUS_FMT_Y10_1X10,
+ .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN,
+ },
+
};
/* Gamma table with gamma 1/2.2 */
@@ -211,16 +221,38 @@
#define ISC_IS_FORMAT_RAW(mbus_code) \
(((mbus_code) & 0xf000) == 0x3000)
+#define ISC_IS_FORMAT_GREY(mbus_code) \
+ (((mbus_code) == MEDIA_BUS_FMT_Y10_1X10) | \
+ (((mbus_code) == MEDIA_BUS_FMT_Y8_1X8)))
+
+static inline void isc_update_v4l2_ctrls(struct isc_device *isc)
+{
+ struct isc_ctrls *ctrls = &isc->ctrls;
+
+ /* In here we set the v4l2 controls w.r.t. our pipeline config */
+ v4l2_ctrl_s_ctrl(isc->r_gain_ctrl, ctrls->gain[ISC_HIS_CFG_MODE_R]);
+ v4l2_ctrl_s_ctrl(isc->b_gain_ctrl, ctrls->gain[ISC_HIS_CFG_MODE_B]);
+ v4l2_ctrl_s_ctrl(isc->gr_gain_ctrl, ctrls->gain[ISC_HIS_CFG_MODE_GR]);
+ v4l2_ctrl_s_ctrl(isc->gb_gain_ctrl, ctrls->gain[ISC_HIS_CFG_MODE_GB]);
+
+ v4l2_ctrl_s_ctrl(isc->r_off_ctrl, ctrls->offset[ISC_HIS_CFG_MODE_R]);
+ v4l2_ctrl_s_ctrl(isc->b_off_ctrl, ctrls->offset[ISC_HIS_CFG_MODE_B]);
+ v4l2_ctrl_s_ctrl(isc->gr_off_ctrl, ctrls->offset[ISC_HIS_CFG_MODE_GR]);
+ v4l2_ctrl_s_ctrl(isc->gb_off_ctrl, ctrls->offset[ISC_HIS_CFG_MODE_GB]);
+}
+
static inline void isc_update_awb_ctrls(struct isc_device *isc)
{
struct isc_ctrls *ctrls = &isc->ctrls;
+ /* In here we set our actual hw pipeline config */
+
regmap_write(isc->regmap, ISC_WB_O_RGR,
- (ISC_WB_O_ZERO_VAL - (ctrls->offset[ISC_HIS_CFG_MODE_R])) |
- ((ISC_WB_O_ZERO_VAL - ctrls->offset[ISC_HIS_CFG_MODE_GR]) << 16));
+ ((ctrls->offset[ISC_HIS_CFG_MODE_R])) |
+ ((ctrls->offset[ISC_HIS_CFG_MODE_GR]) << 16));
regmap_write(isc->regmap, ISC_WB_O_BGB,
- (ISC_WB_O_ZERO_VAL - (ctrls->offset[ISC_HIS_CFG_MODE_B])) |
- ((ISC_WB_O_ZERO_VAL - ctrls->offset[ISC_HIS_CFG_MODE_GB]) << 16));
+ ((ctrls->offset[ISC_HIS_CFG_MODE_B])) |
+ ((ctrls->offset[ISC_HIS_CFG_MODE_GB]) << 16));
regmap_write(isc->regmap, ISC_WB_G_RGR,
ctrls->gain[ISC_HIS_CFG_MODE_R] |
(ctrls->gain[ISC_HIS_CFG_MODE_GR] << 16));
@@ -236,12 +268,8 @@
for (c = ISC_HIS_CFG_MODE_GR; c <= ISC_HIS_CFG_MODE_B; c++) {
/* gains have a fixed point at 9 decimals */
isc->ctrls.gain[c] = 1 << 9;
- /* offsets are in 2's complements, the value
- * will be substracted from ISC_WB_O_ZERO_VAL to obtain
- * 2's complement of a value between 0 and
- * ISC_WB_O_ZERO_VAL >> 1
- */
- isc->ctrls.offset[c] = ISC_WB_O_ZERO_VAL;
+ /* offsets are in 2's complements */
+ isc->ctrls.offset[c] = 0;
}
}
@@ -649,11 +677,9 @@
bay_cfg = isc->config.sd_format->cfa_baycfg;
- if (ctrls->awb == ISC_WB_NONE)
- isc_reset_awb_ctrls(isc);
-
regmap_write(regmap, ISC_WB_CFG, bay_cfg);
isc_update_awb_ctrls(isc);
+ isc_update_v4l2_ctrls(isc);
regmap_write(regmap, ISC_CFA_CFG, bay_cfg | ISC_CFA_CFG_EITPOL);
@@ -1003,6 +1029,7 @@
rgb = true;
break;
case V4L2_PIX_FMT_GREY:
+ case V4L2_PIX_FMT_Y10:
ret = 0;
grey = true;
break;
@@ -1010,34 +1037,29 @@
/* any other different formats are not supported */
ret = -EINVAL;
}
-
- /* we cannot output RAW/Grey if we do not receive RAW */
- if ((bayer || grey) &&
- !ISC_IS_FORMAT_RAW(isc->try_config.sd_format->mbus_code))
- return -EINVAL;
-
v4l2_dbg(1, debug, &isc->v4l2_dev,
"Format validation, requested rgb=%u, yuv=%u, grey=%u, bayer=%u\n",
rgb, yuv, grey, bayer);
+ /* we cannot output RAW if we do not receive RAW */
+ if ((bayer) && !ISC_IS_FORMAT_RAW(isc->try_config.sd_format->mbus_code))
+ return -EINVAL;
+
+ /* we cannot output GREY if we do not receive RAW/GREY */
+ if (grey && !ISC_IS_FORMAT_RAW(isc->try_config.sd_format->mbus_code) &&
+ !ISC_IS_FORMAT_GREY(isc->try_config.sd_format->mbus_code))
+ return -EINVAL;
+
return ret;
}
/*
* Configures the RLP and DMA modules, depending on the output format
* configured for the ISC.
- * If direct_dump == true, just dump raw data 8 bits.
+ * If direct_dump == true, just dump raw data 8/16 bits depending on format.
*/
static int isc_try_configure_rlp_dma(struct isc_device *isc, bool direct_dump)
{
- if (direct_dump) {
- isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_DAT8;
- isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED8;
- isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED;
- isc->try_config.bpp = 16;
- return 0;
- }
-
switch (isc->try_config.fourcc) {
case V4L2_PIX_FMT_SBGGR8:
case V4L2_PIX_FMT_SGBRG8:
@@ -1115,9 +1137,23 @@
isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED;
isc->try_config.bpp = 8;
break;
+ case V4L2_PIX_FMT_Y10:
+ isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_DATY10;
+ isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED16;
+ isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED;
+ isc->try_config.bpp = 16;
+ break;
default:
return -EINVAL;
}
+
+ if (direct_dump) {
+ isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_DAT8;
+ isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED8;
+ isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED;
+ return 0;
+ }
+
return 0;
}
@@ -1187,13 +1223,44 @@
return 0;
}
+static void isc_try_fse(struct isc_device *isc,
+ struct v4l2_subdev_pad_config *pad_cfg)
+{
+ int ret;
+ struct v4l2_subdev_frame_size_enum fse = {};
+
+ /*
+ * If we do not know yet which format the subdev is using, we cannot
+ * do anything.
+ */
+ if (!isc->try_config.sd_format)
+ return;
+
+ fse.code = isc->try_config.sd_format->mbus_code;
+ fse.which = V4L2_SUBDEV_FORMAT_TRY;
+
+ ret = v4l2_subdev_call(isc->current_subdev->sd, pad, enum_frame_size,
+ pad_cfg, &fse);
+ /*
+ * Attempt to obtain format size from subdev. If not available,
+ * just use the maximum ISC can receive.
+ */
+ if (ret) {
+ pad_cfg->try_crop.width = ISC_MAX_SUPPORT_WIDTH;
+ pad_cfg->try_crop.height = ISC_MAX_SUPPORT_HEIGHT;
+ } else {
+ pad_cfg->try_crop.width = fse.max_width;
+ pad_cfg->try_crop.height = fse.max_height;
+ }
+}
+
static int isc_try_fmt(struct isc_device *isc, struct v4l2_format *f,
u32 *code)
{
int i;
struct isc_format *sd_fmt = NULL, *direct_fmt = NULL;
struct v4l2_pix_format *pixfmt = &f->fmt.pix;
- struct v4l2_subdev_pad_config pad_cfg;
+ struct v4l2_subdev_pad_config pad_cfg = {};
struct v4l2_subdev_format format = {
.which = V4L2_SUBDEV_FORMAT_TRY,
};
@@ -1290,6 +1357,9 @@
if (ret)
goto isc_try_fmt_err;
+ /* Obtain frame sizes if possible to have crop requirements ready */
+ isc_try_fse(isc, &pad_cfg);
+
v4l2_fill_mbus_format(&format.format, pixfmt, mbus_code);
ret = v4l2_subdev_call(isc->current_subdev->sd, pad, set_fmt,
&pad_cfg, &format);
@@ -1339,6 +1409,7 @@
isc->try_config.sd_format != isc->config.sd_format) {
isc->ctrls.hist_stat = HIST_INIT;
isc_reset_awb_ctrls(isc);
+ isc_update_v4l2_ctrls(isc);
}
/* make the try configuration active */
isc->config = isc->try_config;
@@ -1414,6 +1485,7 @@
{
struct isc_device *isc = video_drvdata(file);
struct v4l2_subdev_frame_size_enum fse = {
+ .code = isc->config.sd_format->mbus_code,
.index = fsize->index,
.which = V4L2_SUBDEV_FORMAT_ACTIVE,
};
@@ -1436,8 +1508,6 @@
if (ret)
return ret;
- fse.code = isc->config.sd_format->mbus_code;
-
fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
fsize->discrete.width = fse.max_width;
fsize->discrete.height = fse.max_height;
@@ -1450,6 +1520,7 @@
{
struct isc_device *isc = video_drvdata(file);
struct v4l2_subdev_frame_interval_enum fie = {
+ .code = isc->config.sd_format->mbus_code,
.index = fival->index,
.width = fival->width,
.height = fival->height,
@@ -1474,7 +1545,6 @@
if (ret)
return ret;
- fie.code = isc->config.sd_format->mbus_code;
fival->type = V4L2_FRMIVAL_TYPE_DISCRETE;
fival->discrete = fie.interval;
@@ -1693,9 +1763,12 @@
*/
ctrls->offset[c] = (offset[c] - 1) << 3;
- /* the offset is then taken and converted to 2's complements */
- if (!ctrls->offset[c])
- ctrls->offset[c] = ISC_WB_O_ZERO_VAL;
+ /*
+ * the offset is then taken and converted to 2's complements,
+ * and must be negative, as we subtract this value from the
+ * color components
+ */
+ ctrls->offset[c] = -ctrls->offset[c];
/*
* the stretch gain is the total number of histogram bins
@@ -1758,10 +1831,6 @@
ctrls->hist_id = hist_id;
baysel = isc->config.sd_format->cfa_baycfg << ISC_HIS_CFG_BAYSEL_SHIFT;
- /* if no more auto white balance, reset controls. */
- if (ctrls->awb == ISC_WB_NONE)
- isc_reset_awb_ctrls(isc);
-
pm_runtime_get_sync(isc->dev);
/*
@@ -1786,6 +1855,8 @@
if (ctrls->awb == ISC_WB_ONETIME) {
v4l2_info(&isc->v4l2_dev,
"Completed one time white-balance adjustment.\n");
+ /* update the v4l2 controls values */
+ isc_update_v4l2_ctrls(isc);
ctrls->awb = ISC_WB_NONE;
}
}
@@ -1817,35 +1888,6 @@
case V4L2_CID_GAMMA:
ctrls->gamma_index = ctrl->val;
break;
- case V4L2_CID_AUTO_WHITE_BALANCE:
- if (ctrl->val == 1)
- ctrls->awb = ISC_WB_AUTO;
- else
- ctrls->awb = ISC_WB_NONE;
-
- /* we did not configure ISC yet */
- if (!isc->config.sd_format)
- break;
-
- if (ctrls->hist_stat != HIST_ENABLED)
- isc_reset_awb_ctrls(isc);
-
- if (isc->ctrls.awb == ISC_WB_AUTO &&
- vb2_is_streaming(&isc->vb2_vidq) &&
- ISC_IS_FORMAT_RAW(isc->config.sd_format->mbus_code))
- isc_set_histogram(isc, true);
-
- break;
- case V4L2_CID_DO_WHITE_BALANCE:
- /* if AWB is enabled, do nothing */
- if (ctrls->awb == ISC_WB_AUTO)
- return 0;
-
- ctrls->awb = ISC_WB_ONETIME;
- isc_set_histogram(isc, true);
- v4l2_dbg(1, debug, &isc->v4l2_dev,
- "One time white-balance started.\n");
- break;
default:
return -EINVAL;
}
@@ -1857,6 +1899,158 @@
.s_ctrl = isc_s_ctrl,
};
+static int isc_s_awb_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct isc_device *isc = container_of(ctrl->handler,
+ struct isc_device, ctrls.handler);
+ struct isc_ctrls *ctrls = &isc->ctrls;
+
+ if (ctrl->flags & V4L2_CTRL_FLAG_INACTIVE)
+ return 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUTO_WHITE_BALANCE:
+ if (ctrl->val == 1)
+ ctrls->awb = ISC_WB_AUTO;
+ else
+ ctrls->awb = ISC_WB_NONE;
+
+ /* we did not configure ISC yet */
+ if (!isc->config.sd_format)
+ break;
+
+ /* configure the controls with new values from v4l2 */
+ if (ctrl->cluster[ISC_CTRL_R_GAIN]->is_new)
+ ctrls->gain[ISC_HIS_CFG_MODE_R] = isc->r_gain_ctrl->val;
+ if (ctrl->cluster[ISC_CTRL_B_GAIN]->is_new)
+ ctrls->gain[ISC_HIS_CFG_MODE_B] = isc->b_gain_ctrl->val;
+ if (ctrl->cluster[ISC_CTRL_GR_GAIN]->is_new)
+ ctrls->gain[ISC_HIS_CFG_MODE_GR] = isc->gr_gain_ctrl->val;
+ if (ctrl->cluster[ISC_CTRL_GB_GAIN]->is_new)
+ ctrls->gain[ISC_HIS_CFG_MODE_GB] = isc->gb_gain_ctrl->val;
+
+ if (ctrl->cluster[ISC_CTRL_R_OFF]->is_new)
+ ctrls->offset[ISC_HIS_CFG_MODE_R] = isc->r_off_ctrl->val;
+ if (ctrl->cluster[ISC_CTRL_B_OFF]->is_new)
+ ctrls->offset[ISC_HIS_CFG_MODE_B] = isc->b_off_ctrl->val;
+ if (ctrl->cluster[ISC_CTRL_GR_OFF]->is_new)
+ ctrls->offset[ISC_HIS_CFG_MODE_GR] = isc->gr_off_ctrl->val;
+ if (ctrl->cluster[ISC_CTRL_GB_OFF]->is_new)
+ ctrls->offset[ISC_HIS_CFG_MODE_GB] = isc->gb_off_ctrl->val;
+
+ isc_update_awb_ctrls(isc);
+
+ if (vb2_is_streaming(&isc->vb2_vidq)) {
+ /*
+ * If we are streaming, we can update profile to
+ * have the new settings in place.
+ */
+ isc_update_profile(isc);
+ } else {
+ /*
+ * The auto cluster will activate automatically this
+ * control. This has to be deactivated when not
+ * streaming.
+ */
+ v4l2_ctrl_activate(isc->do_wb_ctrl, false);
+ }
+
+ /* if we have autowhitebalance on, start histogram procedure */
+ if (ctrls->awb == ISC_WB_AUTO &&
+ vb2_is_streaming(&isc->vb2_vidq) &&
+ ISC_IS_FORMAT_RAW(isc->config.sd_format->mbus_code))
+ isc_set_histogram(isc, true);
+
+ /*
+ * for one time whitebalance adjustment, check the button,
+ * if it's pressed, perform the one time operation.
+ */
+ if (ctrls->awb == ISC_WB_NONE &&
+ ctrl->cluster[ISC_CTRL_DO_WB]->is_new &&
+ !(ctrl->cluster[ISC_CTRL_DO_WB]->flags &
+ V4L2_CTRL_FLAG_INACTIVE)) {
+ ctrls->awb = ISC_WB_ONETIME;
+ isc_set_histogram(isc, true);
+ v4l2_dbg(1, debug, &isc->v4l2_dev,
+ "One time white-balance started.\n");
+ }
+ return 0;
+ }
+ return 0;
+}
+
+static int isc_g_volatile_awb_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct isc_device *isc = container_of(ctrl->handler,
+ struct isc_device, ctrls.handler);
+ struct isc_ctrls *ctrls = &isc->ctrls;
+
+ switch (ctrl->id) {
+ /* being a cluster, this id will be called for every control */
+ case V4L2_CID_AUTO_WHITE_BALANCE:
+ ctrl->cluster[ISC_CTRL_R_GAIN]->val =
+ ctrls->gain[ISC_HIS_CFG_MODE_R];
+ ctrl->cluster[ISC_CTRL_B_GAIN]->val =
+ ctrls->gain[ISC_HIS_CFG_MODE_B];
+ ctrl->cluster[ISC_CTRL_GR_GAIN]->val =
+ ctrls->gain[ISC_HIS_CFG_MODE_GR];
+ ctrl->cluster[ISC_CTRL_GB_GAIN]->val =
+ ctrls->gain[ISC_HIS_CFG_MODE_GB];
+
+ ctrl->cluster[ISC_CTRL_R_OFF]->val =
+ ctrls->offset[ISC_HIS_CFG_MODE_R];
+ ctrl->cluster[ISC_CTRL_B_OFF]->val =
+ ctrls->offset[ISC_HIS_CFG_MODE_B];
+ ctrl->cluster[ISC_CTRL_GR_OFF]->val =
+ ctrls->offset[ISC_HIS_CFG_MODE_GR];
+ ctrl->cluster[ISC_CTRL_GB_OFF]->val =
+ ctrls->offset[ISC_HIS_CFG_MODE_GB];
+ break;
+ }
+ return 0;
+}
+
+static const struct v4l2_ctrl_ops isc_awb_ops = {
+ .s_ctrl = isc_s_awb_ctrl,
+ .g_volatile_ctrl = isc_g_volatile_awb_ctrl,
+};
+
+#define ISC_CTRL_OFF(_name, _id, _name_str) \
+ static const struct v4l2_ctrl_config _name = { \
+ .ops = &isc_awb_ops, \
+ .id = _id, \
+ .name = _name_str, \
+ .type = V4L2_CTRL_TYPE_INTEGER, \
+ .flags = V4L2_CTRL_FLAG_SLIDER, \
+ .min = -4095, \
+ .max = 4095, \
+ .step = 1, \
+ .def = 0, \
+ }
+
+ISC_CTRL_OFF(isc_r_off_ctrl, ISC_CID_R_OFFSET, "Red Component Offset");
+ISC_CTRL_OFF(isc_b_off_ctrl, ISC_CID_B_OFFSET, "Blue Component Offset");
+ISC_CTRL_OFF(isc_gr_off_ctrl, ISC_CID_GR_OFFSET, "Green Red Component Offset");
+ISC_CTRL_OFF(isc_gb_off_ctrl, ISC_CID_GB_OFFSET, "Green Blue Component Offset");
+
+#define ISC_CTRL_GAIN(_name, _id, _name_str) \
+ static const struct v4l2_ctrl_config _name = { \
+ .ops = &isc_awb_ops, \
+ .id = _id, \
+ .name = _name_str, \
+ .type = V4L2_CTRL_TYPE_INTEGER, \
+ .flags = V4L2_CTRL_FLAG_SLIDER, \
+ .min = 0, \
+ .max = 8191, \
+ .step = 1, \
+ .def = 512, \
+ }
+
+ISC_CTRL_GAIN(isc_r_gain_ctrl, ISC_CID_R_GAIN, "Red Component Gain");
+ISC_CTRL_GAIN(isc_b_gain_ctrl, ISC_CID_B_GAIN, "Blue Component Gain");
+ISC_CTRL_GAIN(isc_gr_gain_ctrl, ISC_CID_GR_GAIN, "Green Red Component Gain");
+ISC_CTRL_GAIN(isc_gb_gain_ctrl, ISC_CID_GB_GAIN, "Green Blue Component Gain");
+
static int isc_ctrl_init(struct isc_device *isc)
{
const struct v4l2_ctrl_ops *ops = &isc_ctrl_ops;
@@ -1867,7 +2061,7 @@
ctrls->hist_stat = HIST_INIT;
isc_reset_awb_ctrls(isc);
- ret = v4l2_ctrl_handler_init(hdl, 5);
+ ret = v4l2_ctrl_handler_init(hdl, 13);
if (ret < 0)
return ret;
@@ -1877,10 +2071,13 @@
v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BRIGHTNESS, -1024, 1023, 1, 0);
v4l2_ctrl_new_std(hdl, ops, V4L2_CID_CONTRAST, -2048, 2047, 1, 256);
v4l2_ctrl_new_std(hdl, ops, V4L2_CID_GAMMA, 0, GAMMA_MAX, 1, 2);
- v4l2_ctrl_new_std(hdl, ops, V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1);
+ isc->awb_ctrl = v4l2_ctrl_new_std(hdl, &isc_awb_ops,
+ V4L2_CID_AUTO_WHITE_BALANCE,
+ 0, 1, 1, 1);
/* do_white_balance is a button, so min,max,step,default are ignored */
- isc->do_wb_ctrl = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_DO_WHITE_BALANCE,
+ isc->do_wb_ctrl = v4l2_ctrl_new_std(hdl, &isc_awb_ops,
+ V4L2_CID_DO_WHITE_BALANCE,
0, 0, 0, 0);
if (!isc->do_wb_ctrl) {
@@ -1891,6 +2088,21 @@
v4l2_ctrl_activate(isc->do_wb_ctrl, false);
+ isc->r_gain_ctrl = v4l2_ctrl_new_custom(hdl, &isc_r_gain_ctrl, NULL);
+ isc->b_gain_ctrl = v4l2_ctrl_new_custom(hdl, &isc_b_gain_ctrl, NULL);
+ isc->gr_gain_ctrl = v4l2_ctrl_new_custom(hdl, &isc_gr_gain_ctrl, NULL);
+ isc->gb_gain_ctrl = v4l2_ctrl_new_custom(hdl, &isc_gb_gain_ctrl, NULL);
+ isc->r_off_ctrl = v4l2_ctrl_new_custom(hdl, &isc_r_off_ctrl, NULL);
+ isc->b_off_ctrl = v4l2_ctrl_new_custom(hdl, &isc_b_off_ctrl, NULL);
+ isc->gr_off_ctrl = v4l2_ctrl_new_custom(hdl, &isc_gr_off_ctrl, NULL);
+ isc->gb_off_ctrl = v4l2_ctrl_new_custom(hdl, &isc_gb_off_ctrl, NULL);
+
+ /*
+ * The cluster is in auto mode with autowhitebalance enabled
+ * and manual mode otherwise.
+ */
+ v4l2_ctrl_auto_cluster(10, &isc->awb_ctrl, 0, true);
+
v4l2_ctrl_handler_setup(hdl);
return 0;
@@ -2087,7 +2299,7 @@
vdev->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE;
video_set_drvdata(vdev, isc);
- ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
+ ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
if (ret < 0) {
v4l2_err(&isc->v4l2_dev,
"video_register_device failed: %d\n", ret);
diff --git a/drivers/media/platform/atmel/atmel-isc-regs.h b/drivers/media/platform/atmel/atmel-isc-regs.h
index c1283fb..f1e160e 100644
--- a/drivers/media/platform/atmel/atmel-isc-regs.h
+++ b/drivers/media/platform/atmel/atmel-isc-regs.h
@@ -108,8 +108,6 @@
/* ISC White Balance Gain for B, GB Register */
#define ISC_WB_G_BGB 0x0000006c
-#define ISC_WB_O_ZERO_VAL (1 << 13)
-
/* ISC Color Filter Array Control Register */
#define ISC_CFA_CTRL 0x00000070
diff --git a/drivers/media/platform/atmel/atmel-isc.h b/drivers/media/platform/atmel/atmel-isc.h
index bfaed2f..24b784b 100644
--- a/drivers/media/platform/atmel/atmel-isc.h
+++ b/drivers/media/platform/atmel/atmel-isc.h
@@ -133,7 +133,7 @@
/* one for each component : GR, R, GB, B */
u32 gain[HIST_BAYER];
- u32 offset[HIST_BAYER];
+ s32 offset[HIST_BAYER];
u32 hist_entry[HIST_ENTRIES];
u32 hist_count[HIST_BAYER];
@@ -213,7 +213,6 @@
struct fmt_config try_config;
struct isc_ctrls ctrls;
- struct v4l2_ctrl *do_wb_ctrl;
struct work_struct awb_work;
struct mutex lock; /* serialize access to file operations */
@@ -223,6 +222,28 @@
struct isc_subdev_entity *current_subdev;
struct list_head subdev_entities;
+
+ struct {
+#define ISC_CTRL_DO_WB 1
+#define ISC_CTRL_R_GAIN 2
+#define ISC_CTRL_B_GAIN 3
+#define ISC_CTRL_GR_GAIN 4
+#define ISC_CTRL_GB_GAIN 5
+#define ISC_CTRL_R_OFF 6
+#define ISC_CTRL_B_OFF 7
+#define ISC_CTRL_GR_OFF 8
+#define ISC_CTRL_GB_OFF 9
+ struct v4l2_ctrl *awb_ctrl;
+ struct v4l2_ctrl *do_wb_ctrl;
+ struct v4l2_ctrl *r_gain_ctrl;
+ struct v4l2_ctrl *b_gain_ctrl;
+ struct v4l2_ctrl *gr_gain_ctrl;
+ struct v4l2_ctrl *gb_gain_ctrl;
+ struct v4l2_ctrl *r_off_ctrl;
+ struct v4l2_ctrl *b_off_ctrl;
+ struct v4l2_ctrl *gr_off_ctrl;
+ struct v4l2_ctrl *gb_off_ctrl;
+ };
};
#define GAMMA_MAX 2
diff --git a/drivers/media/platform/atmel/atmel-isi.c b/drivers/media/platform/atmel/atmel-isi.c
index 428f117..d74aa73 100644
--- a/drivers/media/platform/atmel/atmel-isi.c
+++ b/drivers/media/platform/atmel/atmel-isi.c
@@ -148,7 +148,8 @@
u32 fourcc = isi->current_fmt->fourcc;
isi->enable_preview_path = fourcc == V4L2_PIX_FMT_RGB565 ||
- fourcc == V4L2_PIX_FMT_RGB32;
+ fourcc == V4L2_PIX_FMT_RGB32 ||
+ fourcc == V4L2_PIX_FMT_Y16;
/* According to sensor's output format to set cfg2 */
cfg2 = isi->current_fmt->swap;
@@ -554,12 +555,36 @@
return NULL;
}
+static void isi_try_fse(struct atmel_isi *isi, const struct isi_format *isi_fmt,
+ struct v4l2_subdev_pad_config *pad_cfg)
+{
+ int ret;
+ struct v4l2_subdev_frame_size_enum fse = {
+ .code = isi_fmt->mbus_code,
+ .which = V4L2_SUBDEV_FORMAT_TRY,
+ };
+
+ ret = v4l2_subdev_call(isi->entity.subdev, pad, enum_frame_size,
+ pad_cfg, &fse);
+ /*
+ * Attempt to obtain format size from subdev. If not available,
+ * just use the maximum ISI can receive.
+ */
+ if (ret) {
+ pad_cfg->try_crop.width = MAX_SUPPORT_WIDTH;
+ pad_cfg->try_crop.height = MAX_SUPPORT_HEIGHT;
+ } else {
+ pad_cfg->try_crop.width = fse.max_width;
+ pad_cfg->try_crop.height = fse.max_height;
+ }
+}
+
static int isi_try_fmt(struct atmel_isi *isi, struct v4l2_format *f,
const struct isi_format **current_fmt)
{
const struct isi_format *isi_fmt;
struct v4l2_pix_format *pixfmt = &f->fmt.pix;
- struct v4l2_subdev_pad_config pad_cfg;
+ struct v4l2_subdev_pad_config pad_cfg = {};
struct v4l2_subdev_format format = {
.which = V4L2_SUBDEV_FORMAT_TRY,
};
@@ -576,6 +601,9 @@
pixfmt->height = clamp(pixfmt->height, 0U, MAX_SUPPORT_HEIGHT);
v4l2_fill_mbus_format(&format.format, pixfmt, isi_fmt->mbus_code);
+
+ isi_try_fse(isi, isi_fmt, &pad_cfg);
+
ret = v4l2_subdev_call(isi->entity.subdev, pad, set_fmt,
&pad_cfg, &format);
if (ret < 0)
@@ -990,6 +1018,16 @@
.mbus_code = MEDIA_BUS_FMT_VYUY8_2X8,
.bpp = 2,
.swap = ISI_CFG2_YCC_SWAP_MODE_1,
+ }, {
+ .fourcc = V4L2_PIX_FMT_GREY,
+ .mbus_code = MEDIA_BUS_FMT_Y10_1X10,
+ .bpp = 1,
+ .swap = ISI_CFG2_GS_MODE_2_PIXEL | ISI_CFG2_GRAYSCALE,
+ }, {
+ .fourcc = V4L2_PIX_FMT_Y16,
+ .mbus_code = MEDIA_BUS_FMT_Y10_1X10,
+ .bpp = 2,
+ .swap = ISI_CFG2_GS_MODE_2_PIXEL | ISI_CFG2_GRAYSCALE,
},
};
@@ -1056,7 +1094,7 @@
return ret;
}
- ret = video_register_device(isi->vdev, VFL_TYPE_GRABBER, -1);
+ ret = video_register_device(isi->vdev, VFL_TYPE_VIDEO, -1);
if (ret) {
dev_err(isi->dev, "Failed to register video device\n");
return ret;
diff --git a/drivers/media/platform/atmel/atmel-isi.h b/drivers/media/platform/atmel/atmel-isi.h
index 47a9108..7ad3895 100644
--- a/drivers/media/platform/atmel/atmel-isi.h
+++ b/drivers/media/platform/atmel/atmel-isi.h
@@ -62,6 +62,8 @@
#define ISI_CFG1_THMASK_BEATS_16 (2 << 13)
/* Bitfields in CFG2 */
+#define ISI_CFG2_GS_MODE_2_PIXEL (0 << 11)
+#define ISI_CFG2_GS_MODE_1_PIXEL (1 << 11)
#define ISI_CFG2_GRAYSCALE (1 << 13)
#define ISI_CFG2_COL_SPACE_YCbCr (0 << 15)
#define ISI_CFG2_COL_SPACE_RGB (1 << 15)
diff --git a/drivers/media/platform/atmel/atmel-sama5d2-isc.c b/drivers/media/platform/atmel/atmel-sama5d2-isc.c
index 7838165..a3304f4 100644
--- a/drivers/media/platform/atmel/atmel-sama5d2-isc.c
+++ b/drivers/media/platform/atmel/atmel-sama5d2-isc.c
@@ -321,11 +321,13 @@
SET_RUNTIME_PM_OPS(isc_runtime_suspend, isc_runtime_resume, NULL)
};
+#if IS_ENABLED(CONFIG_OF)
static const struct of_device_id atmel_isc_of_match[] = {
{ .compatible = "atmel,sama5d2-isc" },
{ }
};
MODULE_DEVICE_TABLE(of, atmel_isc_of_match);
+#endif
static struct platform_driver atmel_isc_driver = {
.probe = atmel_isc_probe,
diff --git a/drivers/media/platform/cadence/Kconfig b/drivers/media/platform/cadence/Kconfig
index c154e36..80cf601 100644
--- a/drivers/media/platform/cadence/Kconfig
+++ b/drivers/media/platform/cadence/Kconfig
@@ -13,8 +13,8 @@
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_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/cec-gpio/Makefile b/drivers/media/platform/cec-gpio/Makefile
deleted file mode 100644
index a40c621..0000000
--- a/drivers/media/platform/cec-gpio/Makefile
+++ /dev/null
@@ -1,2 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0-only
-obj-$(CONFIG_CEC_GPIO) += cec-gpio.o
diff --git a/drivers/media/platform/cec-gpio/cec-gpio.c b/drivers/media/platform/cec-gpio/cec-gpio.c
deleted file mode 100644
index 5b17d3a..0000000
--- a/drivers/media/platform/cec-gpio/cec-gpio.c
+++ /dev/null
@@ -1,275 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * Copyright 2017 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- */
-
-#include <linux/module.h>
-#include <linux/interrupt.h>
-#include <linux/delay.h>
-#include <linux/platform_device.h>
-#include <linux/gpio/consumer.h>
-#include <media/cec-pin.h>
-
-struct cec_gpio {
- struct cec_adapter *adap;
- struct device *dev;
-
- struct gpio_desc *cec_gpio;
- int cec_irq;
- bool cec_is_low;
-
- struct gpio_desc *hpd_gpio;
- int hpd_irq;
- bool hpd_is_high;
- ktime_t hpd_ts;
-
- struct gpio_desc *v5_gpio;
- int v5_irq;
- bool v5_is_high;
- ktime_t v5_ts;
-};
-
-static bool cec_gpio_read(struct cec_adapter *adap)
-{
- struct cec_gpio *cec = cec_get_drvdata(adap);
-
- if (cec->cec_is_low)
- return false;
- return gpiod_get_value(cec->cec_gpio);
-}
-
-static void cec_gpio_high(struct cec_adapter *adap)
-{
- struct cec_gpio *cec = cec_get_drvdata(adap);
-
- if (!cec->cec_is_low)
- return;
- cec->cec_is_low = false;
- gpiod_set_value(cec->cec_gpio, 1);
-}
-
-static void cec_gpio_low(struct cec_adapter *adap)
-{
- struct cec_gpio *cec = cec_get_drvdata(adap);
-
- if (cec->cec_is_low)
- return;
- cec->cec_is_low = true;
- gpiod_set_value(cec->cec_gpio, 0);
-}
-
-static irqreturn_t cec_hpd_gpio_irq_handler_thread(int irq, void *priv)
-{
- struct cec_gpio *cec = priv;
-
- cec_queue_pin_hpd_event(cec->adap, cec->hpd_is_high, cec->hpd_ts);
- return IRQ_HANDLED;
-}
-
-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);
-
- if (is_high == cec->v5_is_high)
- return IRQ_HANDLED;
- cec->v5_ts = ktime_get();
- cec->v5_is_high = is_high;
- return IRQ_WAKE_THREAD;
-}
-
-static irqreturn_t cec_5v_gpio_irq_handler_thread(int irq, void *priv)
-{
- struct cec_gpio *cec = priv;
-
- cec_queue_pin_5v_event(cec->adap, cec->v5_is_high, cec->v5_ts);
- return IRQ_HANDLED;
-}
-
-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);
-
- if (is_high == cec->hpd_is_high)
- return IRQ_HANDLED;
- cec->hpd_ts = ktime_get();
- cec->hpd_is_high = is_high;
- return IRQ_WAKE_THREAD;
-}
-
-static irqreturn_t cec_gpio_irq_handler(int irq, void *priv)
-{
- struct cec_gpio *cec = priv;
-
- cec_pin_changed(cec->adap, gpiod_get_value(cec->cec_gpio));
- return IRQ_HANDLED;
-}
-
-static bool cec_gpio_enable_irq(struct cec_adapter *adap)
-{
- struct cec_gpio *cec = cec_get_drvdata(adap);
-
- enable_irq(cec->cec_irq);
- return true;
-}
-
-static void cec_gpio_disable_irq(struct cec_adapter *adap)
-{
- struct cec_gpio *cec = cec_get_drvdata(adap);
-
- disable_irq(cec->cec_irq);
-}
-
-static void cec_gpio_status(struct cec_adapter *adap, struct seq_file *file)
-{
- struct cec_gpio *cec = cec_get_drvdata(adap);
-
- seq_printf(file, "mode: %s\n", cec->cec_is_low ? "low-drive" : "read");
- seq_printf(file, "using irq: %d\n", cec->cec_irq);
- if (cec->hpd_gpio)
- seq_printf(file, "hpd: %s\n",
- cec->hpd_is_high ? "high" : "low");
- if (cec->v5_gpio)
- seq_printf(file, "5V: %s\n",
- cec->v5_is_high ? "high" : "low");
-}
-
-static int cec_gpio_read_hpd(struct cec_adapter *adap)
-{
- struct cec_gpio *cec = cec_get_drvdata(adap);
-
- if (!cec->hpd_gpio)
- return -ENOTTY;
- return gpiod_get_value(cec->hpd_gpio);
-}
-
-static int cec_gpio_read_5v(struct cec_adapter *adap)
-{
- struct cec_gpio *cec = cec_get_drvdata(adap);
-
- if (!cec->v5_gpio)
- return -ENOTTY;
- return gpiod_get_value(cec->v5_gpio);
-}
-
-static void cec_gpio_free(struct cec_adapter *adap)
-{
- cec_gpio_disable_irq(adap);
-}
-
-static const struct cec_pin_ops cec_gpio_pin_ops = {
- .read = cec_gpio_read,
- .low = cec_gpio_low,
- .high = cec_gpio_high,
- .enable_irq = cec_gpio_enable_irq,
- .disable_irq = cec_gpio_disable_irq,
- .status = cec_gpio_status,
- .free = cec_gpio_free,
- .read_hpd = cec_gpio_read_hpd,
- .read_5v = cec_gpio_read_5v,
-};
-
-static int cec_gpio_probe(struct platform_device *pdev)
-{
- struct device *dev = &pdev->dev;
- struct cec_gpio *cec;
- int ret;
-
- cec = devm_kzalloc(dev, sizeof(*cec), GFP_KERNEL);
- if (!cec)
- return -ENOMEM;
-
- cec->dev = dev;
-
- cec->cec_gpio = devm_gpiod_get(dev, "cec", GPIOD_OUT_HIGH_OPEN_DRAIN);
- if (IS_ERR(cec->cec_gpio))
- return PTR_ERR(cec->cec_gpio);
- cec->cec_irq = gpiod_to_irq(cec->cec_gpio);
-
- cec->hpd_gpio = devm_gpiod_get_optional(dev, "hpd", GPIOD_IN);
- if (IS_ERR(cec->hpd_gpio))
- return PTR_ERR(cec->hpd_gpio);
-
- cec->v5_gpio = devm_gpiod_get_optional(dev, "v5", GPIOD_IN);
- if (IS_ERR(cec->v5_gpio))
- return PTR_ERR(cec->v5_gpio);
-
- cec->adap = cec_pin_allocate_adapter(&cec_gpio_pin_ops,
- cec, pdev->name, CEC_CAP_DEFAULTS | CEC_CAP_PHYS_ADDR |
- CEC_CAP_MONITOR_ALL | CEC_CAP_MONITOR_PIN);
- if (IS_ERR(cec->adap))
- return PTR_ERR(cec->adap);
-
- ret = devm_request_irq(dev, cec->cec_irq, cec_gpio_irq_handler,
- IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
- cec->adap->name, cec);
- if (ret)
- return ret;
-
- cec_gpio_disable_irq(cec->adap);
-
- if (cec->hpd_gpio) {
- cec->hpd_irq = gpiod_to_irq(cec->hpd_gpio);
- ret = devm_request_threaded_irq(dev, cec->hpd_irq,
- cec_hpd_gpio_irq_handler,
- cec_hpd_gpio_irq_handler_thread,
- IRQF_ONESHOT |
- IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
- "hpd-gpio", cec);
- if (ret)
- return ret;
- }
-
- if (cec->v5_gpio) {
- cec->v5_irq = gpiod_to_irq(cec->v5_gpio);
- ret = devm_request_threaded_irq(dev, cec->v5_irq,
- cec_5v_gpio_irq_handler,
- cec_5v_gpio_irq_handler_thread,
- IRQF_ONESHOT |
- IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
- "v5-gpio", cec);
- if (ret)
- return ret;
- }
-
- ret = cec_register_adapter(cec->adap, &pdev->dev);
- if (ret) {
- cec_delete_adapter(cec->adap);
- return ret;
- }
-
- platform_set_drvdata(pdev, cec);
- return 0;
-}
-
-static int cec_gpio_remove(struct platform_device *pdev)
-{
- struct cec_gpio *cec = platform_get_drvdata(pdev);
-
- cec_unregister_adapter(cec->adap);
- return 0;
-}
-
-static const struct of_device_id cec_gpio_match[] = {
- {
- .compatible = "cec-gpio",
- },
- {},
-};
-MODULE_DEVICE_TABLE(of, cec_gpio_match);
-
-static struct platform_driver cec_gpio_pdrv = {
- .probe = cec_gpio_probe,
- .remove = cec_gpio_remove,
- .driver = {
- .name = "cec-gpio",
- .of_match_table = cec_gpio_match,
- },
-};
-
-module_platform_driver(cec_gpio_pdrv);
-
-MODULE_AUTHOR("Hans Verkuil <hans.verkuil@cisco.com>");
-MODULE_LICENSE("GPL v2");
-MODULE_DESCRIPTION("CEC GPIO driver");
diff --git a/drivers/media/platform/coda/coda-bit.c b/drivers/media/platform/coda/coda-bit.c
index e6b68be..159c9de 100644
--- a/drivers/media/platform/coda/coda-bit.c
+++ b/drivers/media/platform/coda/coda-bit.c
@@ -1101,7 +1101,7 @@
break;
case CODA_960:
coda_write(dev, 0, CODA9_GDI_WPROT_RGN_EN);
- /* fallthrough */
+ fallthrough;
case CODA_HX4:
case CODA_7541:
coda_write(dev, CODA7_STREAM_BUF_DYNALLOC_EN |
@@ -1141,7 +1141,7 @@
CODA7_PICHEIGHT_MASK) << CODA_PICHEIGHT_OFFSET;
break;
}
- /* fallthrough */
+ fallthrough;
case CODA_960:
value = (q_data_src->rect.width & CODA7_PICWIDTH_MASK)
<< CODA7_PICWIDTH_OFFSET;
@@ -1215,7 +1215,8 @@
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 @@
}
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);
@@ -1629,6 +1634,9 @@
struct coda_dev *dev = ctx->dev;
u32 wr_ptr, start_ptr;
+ if (ctx->aborting)
+ return;
+
/*
* Lock to make sure that an encoder stop command running in parallel
* will either already have marked src_buf as last, or it will wake up
@@ -2173,16 +2181,21 @@
} else {
if (dev->devtype->product == CODA_960) {
/*
- * The CODA960 seems to have an internal list of
- * buffers with 64 entries that includes the
- * registered frame buffers as well as the rotator
- * buffer output.
- *
- * ROT_INDEX needs to be < 0x40, but >
- * ctx->num_internal_frames.
+ * It was previously assumed that the CODA960 has an
+ * internal list of 64 buffer entries that contains
+ * both the registered internal frame buffers as well
+ * as the rotator buffer output, and that the ROT_INDEX
+ * register must be set to a value between the last
+ * internal frame buffers' index and 64.
+ * At least on firmware version 3.1.1 it turns out that
+ * setting ROT_INDEX to any value >= 32 causes CODA
+ * hangups that it can not recover from with the SRC VPU
+ * reset.
+ * It does appear to work however, to just set it to a
+ * fixed value in the [ctx->num_internal_frames, 31]
+ * range, for example CODA_MAX_FRAMEBUFFERS.
*/
- coda_write(dev,
- CODA_MAX_FRAMEBUFFERS + dst_buf->vb2_buf.index,
+ coda_write(dev, CODA_MAX_FRAMEBUFFERS,
CODA9_CMD_DEC_PIC_ROT_INDEX);
reg_addr = CODA9_CMD_DEC_PIC_ROT_ADDR_Y;
@@ -2274,6 +2287,9 @@
int err_vdoa = 0;
u32 val;
+ if (ctx->aborting)
+ return;
+
/* Update kfifo out pointer from coda bitstream read pointer */
coda_kfifo_sync_from_device(ctx);
diff --git a/drivers/media/platform/coda/coda-common.c b/drivers/media/platform/coda/coda-common.c
index 834f11f..1eed69d 100644
--- a/drivers/media/platform/coda/coda-common.c
+++ b/drivers/media/platform/coda/coda-common.c
@@ -155,9 +155,11 @@
static const struct coda_codec coda9_codecs[] = {
CODA_CODEC(CODA9_MODE_ENCODE_H264, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_H264, 1920, 1088),
CODA_CODEC(CODA9_MODE_ENCODE_MP4, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_MPEG4, 1920, 1088),
+ CODA_CODEC(CODA9_MODE_ENCODE_MJPG, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_JPEG, 8192, 8192),
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 {
@@ -235,6 +237,38 @@
},
};
+static const struct coda_video_device coda9_jpeg_encoder = {
+ .name = "coda-jpeg-encoder",
+ .type = CODA_INST_ENCODER,
+ .ops = &coda9_jpeg_encode_ops,
+ .direct = true,
+ .src_formats = {
+ V4L2_PIX_FMT_NV12,
+ V4L2_PIX_FMT_YUV420,
+ V4L2_PIX_FMT_YVU420,
+ V4L2_PIX_FMT_YUV422P,
+ },
+ .dst_formats = {
+ V4L2_PIX_FMT_JPEG,
+ },
+};
+
+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,
};
@@ -252,6 +286,8 @@
};
static const struct coda_video_device *coda9_video_devices[] = {
+ &coda9_jpeg_encoder,
+ &coda9_jpeg_decoder,
&coda_bit_encoder,
&coda_bit_decoder,
};
@@ -393,6 +429,12 @@
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)
{
@@ -403,10 +445,33 @@
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;
@@ -596,12 +661,23 @@
/*
* 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;
@@ -619,12 +695,18 @@
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)
@@ -721,13 +803,15 @@
ctx->tiled_map_type = GDI_TILED_FRAME_MB_RASTER_MAP;
break;
case V4L2_PIX_FMT_NV12:
- if (!disable_tiling && ctx->dev->devtype->product == CODA_960) {
+ if (!disable_tiling && ctx->use_bit &&
+ ctx->dev->devtype->product == CODA_960) {
ctx->tiled_map_type = GDI_TILED_FRAME_MB_RASTER_MAP;
break;
}
- /* else fall through */
+ fallthrough;
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:
@@ -931,18 +1015,20 @@
case V4L2_SEL_TGT_CROP_DEFAULT:
case V4L2_SEL_TGT_CROP_BOUNDS:
rsel = &r;
- /* fallthrough */
+ fallthrough;
case V4L2_SEL_TGT_CROP:
- if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
+ if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT ||
+ ctx->inst_type == CODA_INST_DECODER)
return -EINVAL;
break;
case V4L2_SEL_TGT_COMPOSE_BOUNDS:
case V4L2_SEL_TGT_COMPOSE_PADDED:
rsel = &r;
- /* fallthrough */
+ fallthrough;
case V4L2_SEL_TGT_COMPOSE:
case V4L2_SEL_TGT_COMPOSE_DEFAULT:
- if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+ ctx->inst_type == CODA_INST_ENCODER)
return -EINVAL;
break;
default:
@@ -988,7 +1074,7 @@
return 0;
}
- /* else fall through */
+ fallthrough;
case V4L2_SEL_TGT_NATIVE_SIZE:
case V4L2_SEL_TGT_COMPOSE:
return coda_g_selection(file, fh, s);
@@ -1067,6 +1153,51 @@
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)
{
@@ -1099,6 +1230,8 @@
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");
@@ -1111,22 +1244,14 @@
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) {
@@ -1143,6 +1268,7 @@
coda_wake_up_capture_queue(ctx);
}
+ mutex_unlock(&ctx->wakeup_mutex);
break;
default:
return -EINVAL;
@@ -1411,15 +1537,17 @@
if (!wait_for_completion_timeout(&ctx->completion,
msecs_to_jiffies(1000))) {
- dev_err(dev->dev, "CODA PIC_RUN timeout\n");
+ if (ctx->use_bit) {
+ dev_err(dev->dev, "CODA PIC_RUN timeout\n");
- ctx->hold = true;
+ ctx->hold = true;
- coda_hw_reset(ctx);
+ coda_hw_reset(ctx);
+ }
if (ctx->ops->run_timeout)
ctx->ops->run_timeout(ctx);
- } else if (!ctx->aborting) {
+ } else {
ctx->ops->finish_run(ctx);
}
@@ -1785,7 +1913,7 @@
coda_queue_source_change_event(ctx);
}
} else {
- if (ctx->inst_type == CODA_INST_ENCODER &&
+ if ((ctx->inst_type == CODA_INST_ENCODER || !ctx->use_bit) &&
vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
vbuf->sequence = ctx->qsequence++;
v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf);
@@ -1811,9 +1939,6 @@
buf->blob.size = size;
buf->dentry = debugfs_create_blob(name, 0644, parent,
&buf->blob);
- if (!buf->dentry)
- dev_warn(dev->dev,
- "failed to create debugfs entry %s\n", name);
}
return 0;
@@ -1873,6 +1998,42 @@
}
}
+ /*
+ * 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;
@@ -2061,6 +2222,12 @@
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;
@@ -2160,6 +2327,10 @@
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,
@@ -2387,6 +2558,7 @@
dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
dst_vq->io_modes = VB2_DMABUF | VB2_MMAP;
+ dst_vq->dma_attrs = DMA_ATTR_NO_KERNEL_MAPPING;
dst_vq->mem_ops = &vb2_dma_contig_memops;
return coda_queue_init(priv, dst_vq);
@@ -2455,7 +2627,7 @@
*/
if (enable_bwb || ctx->inst_type == CODA_INST_ENCODER)
ctx->frame_mem_ctrl = CODA9_FRAME_ENABLE_BWB;
- /* fallthrough */
+ fallthrough;
case CODA_HX4:
case CODA_7541:
ctx->reg_idx = 0;
@@ -2480,7 +2652,7 @@
ret = clk_prepare_enable(dev->clk_per);
if (ret)
- goto err_clk_per;
+ goto err_pm_get;
ret = clk_prepare_enable(dev->clk_ahb);
if (ret)
@@ -2519,9 +2691,8 @@
clk_disable_unprepare(dev->clk_ahb);
err_clk_ahb:
clk_disable_unprepare(dev->clk_per);
-err_clk_per:
- pm_runtime_put_sync(dev->dev);
err_pm_get:
+ pm_runtime_put_sync(dev->dev);
v4l2_fh_del(&ctx->fh);
v4l2_fh_exit(&ctx->fh);
err_coda_name_init:
@@ -2704,7 +2875,7 @@
v4l2_disable_ioctl(vfd, VIDIOC_G_CROP);
v4l2_disable_ioctl(vfd, VIDIOC_S_CROP);
- ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0);
+ ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0);
if (!ret)
v4l2_info(&dev->v4l2_dev, "%s registered as %s\n",
type == CODA_INST_ENCODER ? "encoder" : "decoder",
@@ -2959,8 +3130,6 @@
else
return -EINVAL;
- spin_lock_init(&dev->irqlock);
-
dev->dev = &pdev->dev;
dev->clk_per = devm_clk_get(&pdev->dev, "per");
if (IS_ERR(dev->clk_per)) {
@@ -2983,10 +3152,8 @@
irq = platform_get_irq_byname(pdev, "bit");
if (irq < 0)
irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- dev_err(&pdev->dev, "failed to get irq resource\n");
+ if (irq < 0)
return irq;
- }
ret = devm_request_irq(&pdev->dev, irq, coda_irq_handler, 0,
dev_name(&pdev->dev), dev);
@@ -2995,6 +3162,22 @@
return ret;
}
+ /* JPEG IRQ */
+ if (dev->devtype->product == CODA_960) {
+ irq = platform_get_irq_byname(pdev, "jpeg");
+ if (irq < 0)
+ return irq;
+
+ ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
+ coda9_jpeg_irq_handler,
+ IRQF_ONESHOT, CODA_NAME " jpeg",
+ dev);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to request jpeg irq\n");
+ return ret;
+ }
+ }
+
dev->rstc = devm_reset_control_get_optional_exclusive(&pdev->dev,
NULL);
if (IS_ERR(dev->rstc)) {
@@ -3027,8 +3210,6 @@
ida_init(&dev->ida);
dev->debugfs_root = debugfs_create_dir("coda", NULL);
- if (!dev->debugfs_root)
- dev_warn(&pdev->dev, "failed to create debugfs root\n");
/* allocate auxiliary per-device buffers for the BIT processor */
if (dev->devtype->product == CODA_DX6) {
@@ -3085,6 +3266,8 @@
return 0;
err_alloc_workqueue:
+ pm_runtime_disable(&pdev->dev);
+ pm_runtime_put_noidle(&pdev->dev);
destroy_workqueue(dev->workqueue);
err_v4l2_register:
v4l2_device_unregister(&dev->v4l2_dev);
diff --git a/drivers/media/platform/coda/coda-jpeg.c b/drivers/media/platform/coda/coda-jpeg.c
index bf61a3e..a72f465 100644
--- a/drivers/media/platform/coda/coda-jpeg.c
+++ b/drivers/media/platform/coda/coda-jpeg.c
@@ -5,46 +5,83 @@
* Copyright (C) 2014 Philipp Zabel, Pengutronix
*/
+#include <asm/unaligned.h>
+#include <linux/irqreturn.h>
#include <linux/kernel.h>
+#include <linux/ktime.h>
+#include <linux/slab.h>
#include <linux/swab.h>
+#include <linux/videodev2.h>
+
+#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>
#include "coda.h"
#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 {
+ CODA9_JPEG_FORMAT_420,
+ CODA9_JPEG_FORMAT_422,
+ CODA9_JPEG_FORMAT_224,
+ CODA9_JPEG_FORMAT_444,
+ 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)
+
/*
* Typical Huffman tables for 8-bit precision luminance and
* chrominance from JPEG ITU-T.81 (ISO/IEC 10918-1) Annex K.3
*/
-static const unsigned char luma_dc_bits[16] = {
+static const unsigned char luma_dc[16 + 12] = {
+ /* bits */
0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-};
-
-static const unsigned char luma_dc_value[12] = {
+ /* values */
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b,
};
-static const unsigned char chroma_dc_bits[16] = {
+static const unsigned char chroma_dc[16 + 12] = {
+ /* bits */
0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
-};
-
-static const unsigned char chroma_dc_value[12] = {
+ /* values */
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b,
};
-static const unsigned char luma_ac_bits[16] = {
+static const unsigned char luma_ac[16 + 162 + 2] = {
+ /* bits */
0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03,
0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7d,
-};
-
-static const unsigned char luma_ac_value[162 + 2] = {
+ /* values */
0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12,
0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07,
0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08,
@@ -68,12 +105,11 @@
0xf9, 0xfa, /* padded to 32-bit */
};
-static const unsigned char chroma_ac_bits[16] = {
+static const unsigned char chroma_ac[16 + 162 + 2] = {
+ /* bits */
0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04,
0x07, 0x05, 0x04, 0x04, 0x00, 0x01, 0x02, 0x77,
-};
-
-static const unsigned char chroma_ac_value[162 + 2] = {
+ /* values */
0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21,
0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71,
0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91,
@@ -124,6 +160,38 @@
0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
};
+static const unsigned char width_align[] = {
+ [CODA9_JPEG_FORMAT_420] = 16,
+ [CODA9_JPEG_FORMAT_422] = 16,
+ [CODA9_JPEG_FORMAT_224] = 8,
+ [CODA9_JPEG_FORMAT_444] = 8,
+ [CODA9_JPEG_FORMAT_400] = 8,
+};
+
+static const unsigned char height_align[] = {
+ [CODA9_JPEG_FORMAT_420] = 16,
+ [CODA9_JPEG_FORMAT_422] = 8,
+ [CODA9_JPEG_FORMAT_224] = 16,
+ [CODA9_JPEG_FORMAT_444] = 8,
+ [CODA9_JPEG_FORMAT_400] = 8,
+};
+
+static int coda9_jpeg_chroma_format(u32 pixfmt)
+{
+ switch (pixfmt) {
+ case V4L2_PIX_FMT_YUV420:
+ case V4L2_PIX_FMT_NV12:
+ return CODA9_JPEG_FORMAT_420;
+ case V4L2_PIX_FMT_YUV422P:
+ return CODA9_JPEG_FORMAT_422;
+ case V4L2_PIX_FMT_YUV444:
+ return CODA9_JPEG_FORMAT_444;
+ case V4L2_PIX_FMT_GREY:
+ return CODA9_JPEG_FORMAT_400;
+ }
+ return -EINVAL;
+}
+
struct coda_memcpy_desc {
int offset;
const void *src;
@@ -148,14 +216,10 @@
{
int i;
static const struct coda_memcpy_desc huff[8] = {
- { 0, luma_dc_bits, sizeof(luma_dc_bits) },
- { 16, luma_dc_value, sizeof(luma_dc_value) },
- { 32, luma_ac_bits, sizeof(luma_ac_bits) },
- { 48, luma_ac_value, sizeof(luma_ac_value) },
- { 216, chroma_dc_bits, sizeof(chroma_dc_bits) },
- { 232, chroma_dc_value, sizeof(chroma_dc_value) },
- { 248, chroma_ac_bits, sizeof(chroma_ac_bits) },
- { 264, chroma_ac_value, sizeof(chroma_ac_value) },
+ { 0, luma_dc, sizeof(luma_dc) },
+ { 32, luma_ac, sizeof(luma_ac) },
+ { 216, chroma_dc, sizeof(chroma_dc) },
+ { 248, chroma_ac, sizeof(chroma_ac) },
};
struct coda_memcpy_desc qmat[3] = {
{ 512, ctx->params.jpeg_qmat_tab[0], 64 },
@@ -198,6 +262,732 @@
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);
+ if (!ctx->params.jpeg_qmat_tab[i])
+ return -ENOMEM;
+ }
+ 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,
+ [CODA9_JPEG_FORMAT_224] = 3,
+ [CODA9_JPEG_FORMAT_444] = 4,
+ [CODA9_JPEG_FORMAT_400] = 4,
+};
+
+#define MCU_INFO(mcu_block_num, comp_num, comp0_info, comp1_info, comp2_info) \
+ (((mcu_block_num) << CODA9_JPEG_MCU_BLOCK_NUM_OFFSET) | \
+ ((comp_num) << CODA9_JPEG_COMP_NUM_OFFSET) | \
+ ((comp0_info) << CODA9_JPEG_COMP0_INFO_OFFSET) | \
+ ((comp1_info) << CODA9_JPEG_COMP1_INFO_OFFSET) | \
+ ((comp2_info) << CODA9_JPEG_COMP2_INFO_OFFSET))
+
+static const u32 mcu_info[] = {
+ [CODA9_JPEG_FORMAT_420] = MCU_INFO(6, 3, 10, 5, 5),
+ [CODA9_JPEG_FORMAT_422] = MCU_INFO(4, 3, 9, 5, 5),
+ [CODA9_JPEG_FORMAT_224] = MCU_INFO(4, 3, 6, 5, 5),
+ [CODA9_JPEG_FORMAT_444] = MCU_INFO(3, 3, 5, 5, 5),
+ [CODA9_JPEG_FORMAT_400] = MCU_INFO(1, 1, 5, 0, 0),
+};
+
+/*
+ * Convert Huffman table specifcations to tables of codes and code lengths.
+ * For reference, see JPEG ITU-T.81 (ISO/IEC 10918-1) [1]
+ *
+ * [1] https://www.w3.org/Graphics/JPEG/itu-t81.pdf
+ */
+static int coda9_jpeg_gen_enc_huff_tab(struct coda_ctx *ctx, int tab_num,
+ int *ehufsi, int *ehufco)
+{
+ int i, j, k, lastk, si, code, maxsymbol;
+ const u8 *bits, *huffval;
+ struct {
+ int size[256];
+ int code[256];
+ } *huff;
+ static const unsigned char *huff_tabs[4] = {
+ luma_dc, luma_ac, chroma_dc, chroma_ac,
+ };
+ int ret = -EINVAL;
+
+ huff = kzalloc(sizeof(*huff), GFP_KERNEL);
+ if (!huff)
+ return -ENOMEM;
+
+ bits = huff_tabs[tab_num];
+ huffval = huff_tabs[tab_num] + 16;
+
+ maxsymbol = tab_num & 1 ? 256 : 16;
+
+ /* Figure C.1 - Generation of table of Huffman code sizes */
+ k = 0;
+ for (i = 1; i <= 16; i++) {
+ j = bits[i - 1];
+ if (k + j > maxsymbol)
+ goto out;
+ while (j--)
+ huff->size[k++] = i;
+ }
+ lastk = k;
+
+ /* Figure C.2 - Generation of table of Huffman codes */
+ k = 0;
+ code = 0;
+ si = huff->size[0];
+ while (k < lastk) {
+ while (huff->size[k] == si) {
+ huff->code[k++] = code;
+ code++;
+ }
+ if (code >= (1 << si))
+ goto out;
+ code <<= 1;
+ si++;
+ }
+
+ /* Figure C.3 - Ordering procedure for encoding procedure code tables */
+ for (k = 0; k < lastk; k++) {
+ i = huffval[k];
+ if (i >= maxsymbol || ehufsi[i])
+ goto out;
+ ehufco[i] = huff->code[k];
+ ehufsi[i] = huff->size[k];
+ }
+
+ ret = 0;
+out:
+ kfree(huff);
+ return ret;
+}
+
+#define DC_TABLE_INDEX0 0
+#define AC_TABLE_INDEX0 1
+#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 {
+ int size[4][256];
+ int code[4][256];
+ } *huff;
+ u32 *huff_data;
+ int i, j;
+ int ret;
+
+ huff = kzalloc(sizeof(*huff), GFP_KERNEL);
+ if (!huff)
+ return -ENOMEM;
+
+ /* Generate all four (luma/chroma DC/AC) code/size lookup tables */
+ for (i = 0; i < 4; i++) {
+ ret = coda9_jpeg_gen_enc_huff_tab(ctx, i, huff->size[i],
+ huff->code[i]);
+ if (ret)
+ goto out;
+ }
+
+ if (!ctx->params.jpeg_huff_data) {
+ ctx->params.jpeg_huff_data =
+ kzalloc(sizeof(u32) * CODA9_JPEG_ENC_HUFF_DATA_SIZE,
+ GFP_KERNEL);
+ if (!ctx->params.jpeg_huff_data) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ }
+ huff_data = ctx->params.jpeg_huff_data;
+
+ for (j = 0; j < 4; j++) {
+ /* Store Huffman lookup tables in AC0, AC1, DC0, DC1 order */
+ int t = (j == 0) ? AC_TABLE_INDEX0 :
+ (j == 1) ? AC_TABLE_INDEX1 :
+ (j == 2) ? DC_TABLE_INDEX0 :
+ DC_TABLE_INDEX1;
+ /* DC tables only have 16 entries */
+ int len = (j < 2) ? 256 : 16;
+
+ for (i = 0; i < len; i++) {
+ if (huff->size[t][i] == 0 && huff->code[t][i] == 0)
+ *(huff_data++) = 0;
+ else
+ *(huff_data++) =
+ ((huff->size[t][i] - 1) << 16) |
+ huff->code[t][i];
+ }
+ }
+
+ ret = 0;
+out:
+ kfree(huff);
+ return ret;
+}
+
+static void coda9_jpeg_write_huff_tab(struct coda_ctx *ctx)
+{
+ struct coda_dev *dev = ctx->dev;
+ u32 *huff_data = ctx->params.jpeg_huff_data;
+ int i;
+
+ /* Write Huffman size/code lookup tables in AC0, AC1, DC0, DC1 order */
+ coda_write(dev, 0x3, CODA9_REG_JPEG_HUFF_CTRL);
+ for (i = 0; i < CODA9_JPEG_ENC_HUFF_DATA_SIZE; i++)
+ coda_write(dev, *(huff_data++), CODA9_REG_JPEG_HUFF_DATA);
+ coda_write(dev, 0x0, CODA9_REG_JPEG_HUFF_CTRL);
+}
+
+static inline void coda9_jpeg_write_qmat_quotients(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, 0x80000 / qmat[i], CODA9_REG_JPEG_QMAT_DATA);
+ coda_write(dev, index, CODA9_REG_JPEG_QMAT_CTRL);
+}
+
+static void coda9_jpeg_load_qmat_tab(struct coda_ctx *ctx)
+{
+ struct coda_dev *dev = ctx->dev;
+ u8 *luma_tab;
+ u8 *chroma_tab;
+
+ luma_tab = ctx->params.jpeg_qmat_tab[0];
+ if (!luma_tab)
+ luma_tab = luma_q;
+
+ chroma_tab = ctx->params.jpeg_qmat_tab[1];
+ if (!chroma_tab)
+ chroma_tab = chroma_q;
+
+ coda9_jpeg_write_qmat_quotients(dev, luma_tab, 0x00);
+ coda9_jpeg_write_qmat_quotients(dev, chroma_tab, 0x40);
+ coda9_jpeg_write_qmat_quotients(dev, chroma_tab, 0x80);
+}
+
+struct coda_jpeg_stream {
+ u8 *curr;
+ u8 *end;
+};
+
+static inline int coda_jpeg_put_byte(u8 byte, struct coda_jpeg_stream *stream)
+{
+ if (stream->curr >= stream->end)
+ return -EINVAL;
+
+ *stream->curr++ = byte;
+
+ return 0;
+}
+
+static inline int coda_jpeg_put_word(u16 word, struct coda_jpeg_stream *stream)
+{
+ if (stream->curr + sizeof(__be16) > stream->end)
+ return -EINVAL;
+
+ put_unaligned_be16(word, stream->curr);
+ stream->curr += sizeof(__be16);
+
+ return 0;
+}
+
+static int coda_jpeg_put_table(u16 marker, u8 index, const u8 *table,
+ size_t len, struct coda_jpeg_stream *stream)
+{
+ int i, ret;
+
+ ret = coda_jpeg_put_word(marker, stream);
+ if (ret < 0)
+ return ret;
+ ret = coda_jpeg_put_word(3 + len, stream);
+ if (ret < 0)
+ return ret;
+ ret = coda_jpeg_put_byte(index, stream);
+ for (i = 0; i < len && ret == 0; i++)
+ ret = coda_jpeg_put_byte(table[i], stream);
+
+ return ret;
+}
+
+static int coda_jpeg_define_quantization_table(struct coda_ctx *ctx, u8 index,
+ struct coda_jpeg_stream *stream)
+{
+ return coda_jpeg_put_table(DQT_MARKER, index,
+ ctx->params.jpeg_qmat_tab[index], 64,
+ stream);
+}
+
+static int coda_jpeg_define_huffman_table(u8 index, const u8 *table, size_t len,
+ struct coda_jpeg_stream *stream)
+{
+ return coda_jpeg_put_table(DHT_MARKER, index, table, len, stream);
+}
+
+static int coda9_jpeg_encode_header(struct coda_ctx *ctx, int len, u8 *buf)
+{
+ struct coda_jpeg_stream stream = { buf, buf + len };
+ struct coda_q_data *q_data_src;
+ int chroma_format, comp_num;
+ int i, ret, pad;
+
+ q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
+ chroma_format = coda9_jpeg_chroma_format(q_data_src->fourcc);
+ if (chroma_format < 0)
+ return 0;
+
+ /* Start Of Image */
+ ret = coda_jpeg_put_word(SOI_MARKER, &stream);
+ if (ret < 0)
+ return ret;
+
+ /* Define Restart Interval */
+ if (ctx->params.jpeg_restart_interval) {
+ ret = coda_jpeg_put_word(DRI_MARKER, &stream);
+ if (ret < 0)
+ return ret;
+ ret = coda_jpeg_put_word(4, &stream);
+ if (ret < 0)
+ return ret;
+ ret = coda_jpeg_put_word(ctx->params.jpeg_restart_interval,
+ &stream);
+ if (ret < 0)
+ return ret;
+ }
+
+ /* Define Quantization Tables */
+ ret = coda_jpeg_define_quantization_table(ctx, 0x00, &stream);
+ if (ret < 0)
+ return ret;
+ if (chroma_format != CODA9_JPEG_FORMAT_400) {
+ ret = coda_jpeg_define_quantization_table(ctx, 0x01, &stream);
+ if (ret < 0)
+ return ret;
+ }
+
+ /* Define Huffman Tables */
+ ret = coda_jpeg_define_huffman_table(0x00, luma_dc, 16 + 12, &stream);
+ if (ret < 0)
+ return ret;
+ ret = coda_jpeg_define_huffman_table(0x10, luma_ac, 16 + 162, &stream);
+ if (ret < 0)
+ return ret;
+ if (chroma_format != CODA9_JPEG_FORMAT_400) {
+ ret = coda_jpeg_define_huffman_table(0x01, chroma_dc, 16 + 12,
+ &stream);
+ if (ret < 0)
+ return ret;
+ ret = coda_jpeg_define_huffman_table(0x11, chroma_ac, 16 + 162,
+ &stream);
+ if (ret < 0)
+ return ret;
+ }
+
+ /* Start Of Frame */
+ ret = coda_jpeg_put_word(SOF_MARKER, &stream);
+ if (ret < 0)
+ return ret;
+ comp_num = (chroma_format == CODA9_JPEG_FORMAT_400) ? 1 : 3;
+ ret = coda_jpeg_put_word(8 + comp_num * 3, &stream);
+ if (ret < 0)
+ return ret;
+ ret = coda_jpeg_put_byte(0x08, &stream);
+ if (ret < 0)
+ return ret;
+ ret = coda_jpeg_put_word(q_data_src->height, &stream);
+ if (ret < 0)
+ return ret;
+ ret = coda_jpeg_put_word(q_data_src->width, &stream);
+ if (ret < 0)
+ return ret;
+ ret = coda_jpeg_put_byte(comp_num, &stream);
+ if (ret < 0)
+ return ret;
+ for (i = 0; i < comp_num; i++) {
+ static unsigned char subsampling[5][3] = {
+ [CODA9_JPEG_FORMAT_420] = { 0x22, 0x11, 0x11 },
+ [CODA9_JPEG_FORMAT_422] = { 0x21, 0x11, 0x11 },
+ [CODA9_JPEG_FORMAT_224] = { 0x12, 0x11, 0x11 },
+ [CODA9_JPEG_FORMAT_444] = { 0x11, 0x11, 0x11 },
+ [CODA9_JPEG_FORMAT_400] = { 0x11 },
+ };
+
+ /* Component identifier, matches SOS */
+ ret = coda_jpeg_put_byte(i + 1, &stream);
+ if (ret < 0)
+ return ret;
+ ret = coda_jpeg_put_byte(subsampling[chroma_format][i],
+ &stream);
+ if (ret < 0)
+ return ret;
+ /* Chroma table index */
+ ret = coda_jpeg_put_byte((i == 0) ? 0 : 1, &stream);
+ if (ret < 0)
+ return ret;
+ }
+
+ /* Pad to multiple of 8 bytes */
+ pad = (stream.curr - buf) % 8;
+ if (pad) {
+ pad = 8 - pad;
+ while (pad--) {
+ ret = coda_jpeg_put_byte(0x00, &stream);
+ if (ret < 0)
+ return ret;
+ }
+ }
+
+ return stream.curr - buf;
+}
+
/*
* Scale quantization table using nonlinear scaling factor
* u8 qtab[64], scale [50,190]
@@ -247,3 +1037,510 @@
coda_scale_quant_table(ctx->params.jpeg_qmat_tab[1], scale);
}
}
+
+/*
+ * Encoder context operations
+ */
+
+static int coda9_jpeg_start_encoding(struct coda_ctx *ctx)
+{
+ struct coda_dev *dev = ctx->dev;
+ int ret;
+
+ ret = coda9_jpeg_load_huff_tab(ctx);
+ if (ret < 0) {
+ v4l2_err(&dev->v4l2_dev, "error loading Huffman tables\n");
+ return ret;
+ }
+ if (!ctx->params.jpeg_qmat_tab[0])
+ ctx->params.jpeg_qmat_tab[0] = kmalloc(64, GFP_KERNEL);
+ if (!ctx->params.jpeg_qmat_tab[1])
+ ctx->params.jpeg_qmat_tab[1] = kmalloc(64, GFP_KERNEL);
+ coda_set_jpeg_compression_quality(ctx, ctx->params.jpeg_quality);
+
+ return 0;
+}
+
+static int coda9_jpeg_prepare_encode(struct coda_ctx *ctx)
+{
+ struct coda_q_data *q_data_src;
+ struct vb2_v4l2_buffer *src_buf, *dst_buf;
+ struct coda_dev *dev = ctx->dev;
+ u32 start_addr, end_addr;
+ u16 aligned_width, aligned_height;
+ bool chroma_interleave;
+ int chroma_format;
+ int header_len;
+ int ret;
+ ktime_t timeout;
+
+ 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);
+
+ 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));
+
+ src_buf->sequence = ctx->osequence;
+ dst_buf->sequence = ctx->osequence;
+ ctx->osequence++;
+
+ src_buf->flags |= V4L2_BUF_FLAG_KEYFRAME;
+ src_buf->flags &= ~V4L2_BUF_FLAG_PFRAME;
+
+ coda_set_gdi_regs(ctx);
+
+ start_addr = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0);
+ end_addr = start_addr + vb2_plane_size(&dst_buf->vb2_buf, 0);
+
+ chroma_format = coda9_jpeg_chroma_format(q_data_src->fourcc);
+ if (chroma_format < 0)
+ return chroma_format;
+
+ /* 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_src->bytesperline) {
+ v4l2_err(&dev->v4l2_dev, "wrong stride: %d instead of %d\n",
+ aligned_width, q_data_src->bytesperline);
+ }
+
+ header_len =
+ coda9_jpeg_encode_header(ctx,
+ vb2_plane_size(&dst_buf->vb2_buf, 0),
+ vb2_plane_vaddr(&dst_buf->vb2_buf, 0));
+ if (header_len < 0)
+ return header_len;
+
+ coda_write(dev, start_addr + header_len, CODA9_REG_JPEG_BBC_BAS_ADDR);
+ coda_write(dev, end_addr, CODA9_REG_JPEG_BBC_END_ADDR);
+ coda_write(dev, start_addr + header_len, CODA9_REG_JPEG_BBC_WR_PTR);
+ coda_write(dev, start_addr + header_len, CODA9_REG_JPEG_BBC_RD_PTR);
+ coda_write(dev, 0, CODA9_REG_JPEG_BBC_CUR_POS);
+ /* 64 words per 256-byte page */
+ coda_write(dev, 64, CODA9_REG_JPEG_BBC_DATA_CNT);
+ coda_write(dev, start_addr, CODA9_REG_JPEG_BBC_EXT_ADDR);
+ coda_write(dev, 0, CODA9_REG_JPEG_BBC_INT_ADDR);
+
+ coda_write(dev, 0, CODA9_REG_JPEG_GBU_BT_PTR);
+ coda_write(dev, 0, CODA9_REG_JPEG_GBU_WD_PTR);
+ coda_write(dev, 0, CODA9_REG_JPEG_GBU_BBSR);
+ coda_write(dev, BIT(31) | ((end_addr - start_addr - header_len) / 256),
+ CODA9_REG_JPEG_BBC_STRM_CTRL);
+ coda_write(dev, 0, CODA9_REG_JPEG_GBU_CTRL);
+ coda_write(dev, 0, CODA9_REG_JPEG_GBU_FF_RPTR);
+ coda_write(dev, 127, CODA9_REG_JPEG_GBU_BBER);
+ coda_write(dev, 64, CODA9_REG_JPEG_GBU_BBIR);
+ coda_write(dev, 64, CODA9_REG_JPEG_GBU_BBHR);
+
+ chroma_interleave = (q_data_src->fourcc == V4L2_PIX_FMT_NV12);
+ coda_write(dev, CODA9_JPEG_PIC_CTRL_TC_DIRECTION |
+ CODA9_JPEG_PIC_CTRL_ENCODER_EN, CODA9_REG_JPEG_PIC_CTRL);
+ 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);
+ coda_write(dev, 1, CODA9_REG_JPEG_BBC_CTRL);
+
+ coda_write(dev, bus_req_num[chroma_format], CODA9_REG_JPEG_OP_INFO);
+
+ coda9_jpeg_write_huff_tab(ctx);
+ coda9_jpeg_load_qmat_tab(ctx);
+
+ if (ctx->params.rot_mode & CODA_ROT_90) {
+ aligned_width = aligned_height;
+ aligned_height = q_data_src->bytesperline;
+ if (chroma_format == CODA9_JPEG_FORMAT_422)
+ chroma_format = CODA9_JPEG_FORMAT_224;
+ else if (chroma_format == CODA9_JPEG_FORMAT_224)
+ chroma_format = CODA9_JPEG_FORMAT_422;
+ }
+ /* These need to be multiples of MCU size */
+ coda_write(dev, aligned_width << 16 | aligned_height,
+ CODA9_REG_JPEG_PIC_SIZE);
+ coda_write(dev, ctx->params.rot_mode ?
+ (CODA_ROT_MIR_ENABLE | ctx->params.rot_mode) : 0,
+ CODA9_REG_JPEG_ROT_INFO);
+
+ coda_write(dev, mcu_info[chroma_format], CODA9_REG_JPEG_MCU_INFO);
+
+ coda_write(dev, 1, CODA9_GDI_CONTROL);
+ timeout = ktime_add_us(ktime_get(), 100000);
+ do {
+ ret = coda_read(dev, CODA9_GDI_STATUS);
+ if (ktime_compare(ktime_get(), timeout) > 0) {
+ v4l2_err(&dev->v4l2_dev, "timeout waiting for GDI\n");
+ return -ETIMEDOUT;
+ }
+ } while (!ret);
+
+ coda_write(dev, (chroma_format << 17) | (chroma_interleave << 16) |
+ q_data_src->bytesperline, CODA9_GDI_INFO_CONTROL);
+ /* The content of this register seems to be irrelevant: */
+ coda_write(dev, aligned_width << 16 | aligned_height,
+ CODA9_GDI_INFO_PIC_SIZE);
+
+ coda_write_base(ctx, q_data_src, src_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);
+
+ coda_write(dev, 1, CODA9_GDI_WPROT_ERR_CLR);
+ coda_write(dev, 0, CODA9_GDI_WPROT_RGN_EN);
+
+ trace_coda_jpeg_run(ctx, src_buf);
+
+ coda_write(dev, 1, CODA9_REG_JPEG_PIC_START);
+
+ return 0;
+}
+
+static void coda9_jpeg_finish_encode(struct coda_ctx *ctx)
+{
+ struct vb2_v4l2_buffer *src_buf, *dst_buf;
+ struct coda_dev *dev = ctx->dev;
+ u32 wr_ptr, start_ptr;
+ u32 err_mb;
+
+ if (ctx->aborting) {
+ coda_write(ctx->dev, 0, CODA9_REG_JPEG_BBC_FLUSH_CMD);
+ return;
+ }
+
+ /*
+ * Lock to make sure that an encoder 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);
+
+ trace_coda_jpeg_done(ctx, dst_buf);
+
+ /*
+ * Set plane payload to the number of bytes written out
+ * by the JPEG processing unit
+ */
+ start_ptr = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0);
+ wr_ptr = coda_read(dev, CODA9_REG_JPEG_BBC_WR_PTR);
+ vb2_set_plane_payload(&dst_buf->vb2_buf, 0, wr_ptr - start_ptr);
+
+ err_mb = coda_read(dev, CODA9_REG_JPEG_PIC_ERRMB);
+ if (err_mb)
+ coda_dbg(1, ctx, "ERRMB: 0x%x\n", err_mb);
+
+ coda_write(dev, 0, CODA9_REG_JPEG_BBC_FLUSH_CMD);
+
+ 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);
+
+ 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: 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_encode_timeout(struct coda_ctx *ctx)
+{
+ struct coda_dev *dev = ctx->dev;
+ u32 end_addr, wr_ptr;
+
+ /* Handle missing BBC overflow interrupt via timeout */
+ end_addr = coda_read(dev, CODA9_REG_JPEG_BBC_END_ADDR);
+ wr_ptr = coda_read(dev, CODA9_REG_JPEG_BBC_WR_PTR);
+ if (wr_ptr >= end_addr - 256) {
+ v4l2_err(&dev->v4l2_dev, "JPEG too large for capture buffer\n");
+ coda9_jpeg_finish_encode(ctx);
+ return;
+ }
+
+ coda_hw_reset(ctx);
+}
+
+static void coda9_jpeg_release(struct coda_ctx *ctx)
+{
+ int i;
+
+ if (ctx->params.jpeg_qmat_tab[0] == luma_q)
+ ctx->params.jpeg_qmat_tab[0] = NULL;
+ if (ctx->params.jpeg_qmat_tab[1] == chroma_q)
+ ctx->params.jpeg_qmat_tab[1] = NULL;
+ 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 = {
+ .queue_init = coda_encoder_queue_init,
+ .start_streaming = coda9_jpeg_start_encoding,
+ .prepare_run = coda9_jpeg_prepare_encode,
+ .finish_run = coda9_jpeg_finish_encode,
+ .run_timeout = coda9_jpeg_encode_timeout,
+ .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;
+ struct coda_ctx *ctx;
+ int status;
+ int err_mb;
+
+ status = coda_read(dev, CODA9_REG_JPEG_PIC_STATUS);
+ if (status == 0)
+ return IRQ_HANDLED;
+ coda_write(dev, status, CODA9_REG_JPEG_PIC_STATUS);
+
+ if (status & CODA9_JPEG_STATUS_OVERFLOW)
+ v4l2_err(&dev->v4l2_dev, "JPEG overflow\n");
+
+ if (status & CODA9_JPEG_STATUS_BBC_INT)
+ v4l2_err(&dev->v4l2_dev, "JPEG BBC interrupt\n");
+
+ if (status & CODA9_JPEG_STATUS_ERROR) {
+ v4l2_err(&dev->v4l2_dev, "JPEG error\n");
+
+ err_mb = coda_read(dev, CODA9_REG_JPEG_PIC_ERRMB);
+ if (err_mb) {
+ v4l2_err(&dev->v4l2_dev,
+ "ERRMB: 0x%x: rst idx %d, mcu pos (%d,%d)\n",
+ err_mb, err_mb >> 24, (err_mb >> 12) & 0xfff,
+ err_mb & 0xfff);
+ }
+ }
+
+ ctx = v4l2_m2m_get_curr_priv(dev->m2m_dev);
+ if (!ctx) {
+ v4l2_err(&dev->v4l2_dev,
+ "Instance released before the end of transaction\n");
+ mutex_unlock(&dev->coda_mutex);
+ return IRQ_HANDLED;
+ }
+
+ complete(&ctx->completion);
+
+ return IRQ_HANDLED;
+}
diff --git a/drivers/media/platform/coda/coda.h b/drivers/media/platform/coda/coda.h
index 848bf1d..b81f3ac 100644
--- a/drivers/media/platform/coda/coda.h
+++ b/drivers/media/platform/coda/coda.h
@@ -69,7 +69,7 @@
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;
@@ -86,7 +86,6 @@
struct gen_pool *iram_pool;
struct coda_aux_buf iram;
- spinlock_t irqlock;
struct mutex dev_mutex;
struct mutex coda_mutex;
struct workqueue_struct *workqueue;
@@ -124,9 +123,15 @@
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 @@
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 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,16 @@
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);
#endif /* __CODA_H__ */
diff --git a/drivers/media/platform/coda/coda_regs.h b/drivers/media/platform/coda/coda_regs.h
index b17464b..da5bb32 100644
--- a/drivers/media/platform/coda/coda_regs.h
+++ b/drivers/media/platform/coda/coda_regs.h
@@ -451,12 +451,21 @@
#define CODA9_CMD_FIRMWARE_CODE_REV 0x1c4
#define CODA9_GDMA_BASE 0x1000
+#define CODA9_GDI_CONTROL (CODA9_GDMA_BASE + 0x034)
+#define CODA9_GDI_PIC_INIT_HOST (CODA9_GDMA_BASE + 0x038)
+#define CODA9_GDI_STATUS (CODA9_GDMA_BASE + 0x080)
#define CODA9_GDI_WPROT_ERR_CLR (CODA9_GDMA_BASE + 0x0a0)
#define CODA9_GDI_WPROT_RGN_EN (CODA9_GDMA_BASE + 0x0ac)
#define CODA9_GDI_BUS_CTRL (CODA9_GDMA_BASE + 0x0f0)
#define CODA9_GDI_BUS_STATUS (CODA9_GDMA_BASE + 0x0f4)
+#define CODA9_GDI_INFO_CONTROL (CODA9_GDMA_BASE + 0x400)
+#define CODA9_GDI_INFO_PIC_SIZE (CODA9_GDMA_BASE + 0x404)
+#define CODA9_GDI_INFO_BASE_Y (CODA9_GDMA_BASE + 0x408)
+#define CODA9_GDI_INFO_BASE_CB (CODA9_GDMA_BASE + 0x40c)
+#define CODA9_GDI_INFO_BASE_CR (CODA9_GDMA_BASE + 0x410)
+
#define CODA9_GDI_XY2_CAS_0 (CODA9_GDMA_BASE + 0x800)
#define CODA9_GDI_XY2_CAS_F (CODA9_GDMA_BASE + 0x83c)
@@ -477,4 +486,78 @@
#define CODA9_GDI_RBC2_AXI_1F (CODA9_GDMA_BASE + 0x91c)
#define CODA9_GDI_TILEDBUF_BASE (CODA9_GDMA_BASE + 0x920)
+#define CODA9_JPEG_BASE 0x3000
+#define CODA9_REG_JPEG_PIC_START (CODA9_JPEG_BASE + 0x000)
+#define CODA9_REG_JPEG_PIC_STATUS (CODA9_JPEG_BASE + 0x004)
+#define CODA9_JPEG_STATUS_OVERFLOW BIT(3)
+#define CODA9_JPEG_STATUS_BBC_INT BIT(2)
+#define CODA9_JPEG_STATUS_ERROR BIT(1)
+#define CODA9_JPEG_STATUS_DONE BIT(0)
+#define CODA9_REG_JPEG_PIC_ERRMB (CODA9_JPEG_BASE + 0x008)
+#define CODA9_JPEG_ERRMB_RESTART_IDX_MASK (0xf << 24)
+#define CODA9_JPEG_ERRMB_MCU_POS_X_MASK (0xfff << 12)
+#define CODA9_JPEG_ERRMB_MCU_POS_Y_MASK 0xfff
+#define CODA9_REG_JPEG_PIC_CTRL (CODA9_JPEG_BASE + 0x010)
+#define CODA9_JPEG_PIC_CTRL_USER_HUFFMAN_EN BIT(6)
+#define CODA9_JPEG_PIC_CTRL_TC_DIRECTION BIT(4)
+#define CODA9_JPEG_PIC_CTRL_ENCODER_EN BIT(3)
+#define CODA9_REG_JPEG_PIC_SIZE (CODA9_JPEG_BASE + 0x014)
+#define CODA9_REG_JPEG_MCU_INFO (CODA9_JPEG_BASE + 0x018)
+#define CODA9_JPEG_MCU_BLOCK_NUM_OFFSET 16
+#define CODA9_JPEG_COMP_NUM_OFFSET 12
+#define CODA9_JPEG_COMP0_INFO_OFFSET 8
+#define CODA9_JPEG_COMP1_INFO_OFFSET 4
+#define CODA9_JPEG_COMP2_INFO_OFFSET 0
+#define CODA9_REG_JPEG_ROT_INFO (CODA9_JPEG_BASE + 0x01c)
+#define CODA9_JPEG_ROT_MIR_ENABLE BIT(4)
+#define CODA9_JPEG_ROT_MIR_MODE_MASK 0xf
+#define CODA9_REG_JPEG_SCL_INFO (CODA9_JPEG_BASE + 0x020)
+#define CODA9_JPEG_SCL_ENABLE BIT(4)
+#define CODA9_JPEG_SCL_HOR_MODE_MASK (0x3 << 2)
+#define CODA9_JPEG_SCL_VER_MODE_MASK (0x3 << 0)
+#define CODA9_REG_JPEG_IF_INFO (CODA9_JPEG_BASE + 0x024)
+#define CODA9_JPEG_SENS_IF_CLR BIT(1)
+#define CODA9_JPEG_DISP_IF_CLR BIT(0)
+#define CODA9_REG_JPEG_OP_INFO (CODA9_JPEG_BASE + 0x02c)
+#define CODA9_JPEG_BUS_REQ_NUM_OFFSET 0
+#define CODA9_JPEG_BUS_REQ_NUM_MASK 0x7
+#define CODA9_REG_JPEG_DPB_CONFIG (CODA9_JPEG_BASE + 0x030)
+#define CODA9_REG_JPEG_DPB_BASE00 (CODA9_JPEG_BASE + 0x040)
+#define CODA9_REG_JPEG_HUFF_CTRL (CODA9_JPEG_BASE + 0x080)
+#define CODA9_REG_JPEG_HUFF_ADDR (CODA9_JPEG_BASE + 0x084)
+#define CODA9_REG_JPEG_HUFF_DATA (CODA9_JPEG_BASE + 0x088)
+#define CODA9_REG_JPEG_QMAT_CTRL (CODA9_JPEG_BASE + 0x090)
+#define CODA9_REG_JPEG_QMAT_ADDR (CODA9_JPEG_BASE + 0x094)
+#define CODA9_REG_JPEG_QMAT_DATA (CODA9_JPEG_BASE + 0x098)
+#define CODA9_REG_JPEG_RST_INTVAL (CODA9_JPEG_BASE + 0x0b0)
+#define CODA9_REG_JPEG_RST_INDEX (CODA9_JPEG_BASE + 0x0b4)
+#define CODA9_REG_JPEG_RST_COUNT (CODA9_JPEG_BASE + 0x0b8)
+#define CODA9_REG_JPEG_DPCM_DIFF_Y (CODA9_JPEG_BASE + 0x0f0)
+#define CODA9_REG_JPEG_DPCM_DIFF_CB (CODA9_JPEG_BASE + 0x0f4)
+#define CODA9_REG_JPEG_DPCM_DIFF_CR (CODA9_JPEG_BASE + 0x0f8)
+#define CODA9_REG_JPEG_GBU_CTRL (CODA9_JPEG_BASE + 0x100)
+#define CODA9_REG_JPEG_GBU_BT_PTR (CODA9_JPEG_BASE + 0x110)
+#define CODA9_REG_JPEG_GBU_WD_PTR (CODA9_JPEG_BASE + 0x114)
+#define CODA9_REG_JPEG_GBU_TT_CNT (CODA9_JPEG_BASE + 0x118)
+#define CODA9_REG_JPEG_GBU_BBSR (CODA9_JPEG_BASE + 0x140)
+#define CODA9_REG_JPEG_GBU_BBER (CODA9_JPEG_BASE + 0x144)
+#define CODA9_REG_JPEG_GBU_BBIR (CODA9_JPEG_BASE + 0x148)
+#define CODA9_REG_JPEG_GBU_BBHR (CODA9_JPEG_BASE + 0x14c)
+#define CODA9_REG_JPEG_GBU_BCNT (CODA9_JPEG_BASE + 0x158)
+#define CODA9_REG_JPEG_GBU_FF_RPTR (CODA9_JPEG_BASE + 0x160)
+#define CODA9_REG_JPEG_GBU_FF_WPTR (CODA9_JPEG_BASE + 0x164)
+#define CODA9_REG_JPEG_BBC_END_ADDR (CODA9_JPEG_BASE + 0x208)
+#define CODA9_REG_JPEG_BBC_WR_PTR (CODA9_JPEG_BASE + 0x20c)
+#define CODA9_REG_JPEG_BBC_RD_PTR (CODA9_JPEG_BASE + 0x210)
+#define CODA9_REG_JPEG_BBC_EXT_ADDR (CODA9_JPEG_BASE + 0x214)
+#define CODA9_REG_JPEG_BBC_INT_ADDR (CODA9_JPEG_BASE + 0x218)
+#define CODA9_REG_JPEG_BBC_DATA_CNT (CODA9_JPEG_BASE + 0x21c)
+#define CODA9_REG_JPEG_BBC_COMMAND (CODA9_JPEG_BASE + 0x220)
+#define CODA9_REG_JPEG_BBC_BUSY (CODA9_JPEG_BASE + 0x224)
+#define CODA9_REG_JPEG_BBC_CTRL (CODA9_JPEG_BASE + 0x228)
+#define CODA9_REG_JPEG_BBC_CUR_POS (CODA9_JPEG_BASE + 0x22c)
+#define CODA9_REG_JPEG_BBC_BAS_ADDR (CODA9_JPEG_BASE + 0x230)
+#define CODA9_REG_JPEG_BBC_STRM_CTRL (CODA9_JPEG_BASE + 0x234)
+#define CODA9_REG_JPEG_BBC_FLUSH_CMD (CODA9_JPEG_BASE + 0x238)
+
#endif
diff --git a/drivers/media/platform/coda/imx-vdoa.c b/drivers/media/platform/coda/imx-vdoa.c
index 8bc0d83..dd6e2e3 100644
--- a/drivers/media/platform/coda/imx-vdoa.c
+++ b/drivers/media/platform/coda/imx-vdoa.c
@@ -287,7 +287,11 @@
struct resource *res;
int ret;
- dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
+ ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
+ if (ret) {
+ dev_err(&pdev->dev, "DMA enable failed\n");
+ return ret;
+ }
vdoa = devm_kzalloc(&pdev->dev, sizeof(*vdoa), GFP_KERNEL);
if (!vdoa)
diff --git a/drivers/media/platform/coda/trace.h b/drivers/media/platform/coda/trace.h
index 6cf5823..c0791c8 100644
--- a/drivers/media/platform/coda/trace.h
+++ b/drivers/media/platform/coda/trace.h
@@ -154,6 +154,16 @@
TP_ARGS(ctx, buf, meta)
);
+DEFINE_EVENT(coda_buf_class, coda_jpeg_run,
+ TP_PROTO(struct coda_ctx *ctx, struct vb2_v4l2_buffer *buf),
+ TP_ARGS(ctx, buf)
+);
+
+DEFINE_EVENT(coda_buf_class, coda_jpeg_done,
+ TP_PROTO(struct coda_ctx *ctx, struct vb2_v4l2_buffer *buf),
+ TP_ARGS(ctx, buf)
+);
+
#endif /* __CODA_TRACE_H__ */
#undef TRACE_INCLUDE_PATH
diff --git a/drivers/media/platform/cros-ec-cec/Makefile b/drivers/media/platform/cros-ec-cec/Makefile
deleted file mode 100644
index 2615cdc..0000000
--- 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/cros-ec-cec/cros-ec-cec.c b/drivers/media/platform/cros-ec-cec/cros-ec-cec.c
deleted file mode 100644
index 31390ce..0000000
--- a/drivers/media/platform/cros-ec-cec/cros-ec-cec.c
+++ /dev/null
@@ -1,354 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * CEC driver for ChromeOS Embedded Controller
- *
- * Copyright (c) 2018 BayLibre, SAS
- * Author: Neil Armstrong <narmstrong@baylibre.com>
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/dmi.h>
-#include <linux/pci.h>
-#include <linux/cec.h>
-#include <linux/slab.h>
-#include <linux/interrupt.h>
-#include <linux/mfd/cros_ec.h>
-#include <linux/platform_data/cros_ec_commands.h>
-#include <linux/platform_data/cros_ec_proto.h>
-#include <media/cec.h>
-#include <media/cec-notifier.h>
-
-#define DRV_NAME "cros-ec-cec"
-
-/**
- * struct cros_ec_cec - Driver data for EC CEC
- *
- * @cros_ec: Pointer to EC device
- * @notifier: Notifier info for responding to EC events
- * @adap: CEC adapter
- * @notify: CEC notifier pointer
- * @rx_msg: storage for a received message
- */
-struct cros_ec_cec {
- struct cros_ec_device *cros_ec;
- struct notifier_block notifier;
- struct cec_adapter *adap;
- struct cec_notifier *notify;
- struct cec_msg rx_msg;
-};
-
-static void handle_cec_message(struct cros_ec_cec *cros_ec_cec)
-{
- struct cros_ec_device *cros_ec = cros_ec_cec->cros_ec;
- uint8_t *cec_message = cros_ec->event_data.data.cec_message;
- unsigned int len = cros_ec->event_size;
-
- cros_ec_cec->rx_msg.len = len;
- memcpy(cros_ec_cec->rx_msg.msg, cec_message, len);
-
- cec_received_msg(cros_ec_cec->adap, &cros_ec_cec->rx_msg);
-}
-
-static void handle_cec_event(struct cros_ec_cec *cros_ec_cec)
-{
- struct cros_ec_device *cros_ec = cros_ec_cec->cros_ec;
- uint32_t events = cros_ec->event_data.data.cec_events;
-
- if (events & EC_MKBP_CEC_SEND_OK)
- cec_transmit_attempt_done(cros_ec_cec->adap,
- CEC_TX_STATUS_OK);
-
- /* FW takes care of all retries, tell core to avoid more retries */
- if (events & EC_MKBP_CEC_SEND_FAILED)
- cec_transmit_attempt_done(cros_ec_cec->adap,
- CEC_TX_STATUS_MAX_RETRIES |
- CEC_TX_STATUS_NACK);
-}
-
-static int cros_ec_cec_event(struct notifier_block *nb,
- unsigned long queued_during_suspend,
- void *_notify)
-{
- struct cros_ec_cec *cros_ec_cec;
- struct cros_ec_device *cros_ec;
-
- cros_ec_cec = container_of(nb, struct cros_ec_cec, notifier);
- cros_ec = cros_ec_cec->cros_ec;
-
- if (cros_ec->event_data.event_type == EC_MKBP_EVENT_CEC_EVENT) {
- handle_cec_event(cros_ec_cec);
- return NOTIFY_OK;
- }
-
- if (cros_ec->event_data.event_type == EC_MKBP_EVENT_CEC_MESSAGE) {
- handle_cec_message(cros_ec_cec);
- return NOTIFY_OK;
- }
-
- return NOTIFY_DONE;
-}
-
-static int cros_ec_cec_set_log_addr(struct cec_adapter *adap, u8 logical_addr)
-{
- struct cros_ec_cec *cros_ec_cec = adap->priv;
- struct cros_ec_device *cros_ec = cros_ec_cec->cros_ec;
- struct {
- struct cros_ec_command msg;
- struct ec_params_cec_set data;
- } __packed msg = {};
- int ret;
-
- msg.msg.command = EC_CMD_CEC_SET;
- msg.msg.outsize = sizeof(msg.data);
- msg.data.cmd = CEC_CMD_LOGICAL_ADDRESS;
- msg.data.val = logical_addr;
-
- ret = cros_ec_cmd_xfer_status(cros_ec, &msg.msg);
- if (ret < 0) {
- dev_err(cros_ec->dev,
- "error setting CEC logical address on EC: %d\n", ret);
- return ret;
- }
-
- return 0;
-}
-
-static int cros_ec_cec_transmit(struct cec_adapter *adap, u8 attempts,
- u32 signal_free_time, struct cec_msg *cec_msg)
-{
- struct cros_ec_cec *cros_ec_cec = adap->priv;
- struct cros_ec_device *cros_ec = cros_ec_cec->cros_ec;
- struct {
- struct cros_ec_command msg;
- struct ec_params_cec_write data;
- } __packed msg = {};
- int ret;
-
- msg.msg.command = EC_CMD_CEC_WRITE_MSG;
- msg.msg.outsize = cec_msg->len;
- memcpy(msg.data.msg, cec_msg->msg, cec_msg->len);
-
- ret = cros_ec_cmd_xfer_status(cros_ec, &msg.msg);
- if (ret < 0) {
- dev_err(cros_ec->dev,
- "error writing CEC msg on EC: %d\n", ret);
- return ret;
- }
-
- return 0;
-}
-
-static int cros_ec_cec_adap_enable(struct cec_adapter *adap, bool enable)
-{
- struct cros_ec_cec *cros_ec_cec = adap->priv;
- struct cros_ec_device *cros_ec = cros_ec_cec->cros_ec;
- struct {
- struct cros_ec_command msg;
- struct ec_params_cec_set data;
- } __packed msg = {};
- int ret;
-
- msg.msg.command = EC_CMD_CEC_SET;
- msg.msg.outsize = sizeof(msg.data);
- msg.data.cmd = CEC_CMD_ENABLE;
- msg.data.val = enable;
-
- ret = cros_ec_cmd_xfer_status(cros_ec, &msg.msg);
- if (ret < 0) {
- dev_err(cros_ec->dev,
- "error %sabling CEC on EC: %d\n",
- (enable ? "en" : "dis"), ret);
- return ret;
- }
-
- return 0;
-}
-
-static const struct cec_adap_ops cros_ec_cec_ops = {
- .adap_enable = cros_ec_cec_adap_enable,
- .adap_log_addr = cros_ec_cec_set_log_addr,
- .adap_transmit = cros_ec_cec_transmit,
-};
-
-#ifdef CONFIG_PM_SLEEP
-static int cros_ec_cec_suspend(struct device *dev)
-{
- struct platform_device *pdev = to_platform_device(dev);
- struct cros_ec_cec *cros_ec_cec = dev_get_drvdata(&pdev->dev);
-
- if (device_may_wakeup(dev))
- enable_irq_wake(cros_ec_cec->cros_ec->irq);
-
- return 0;
-}
-
-static int cros_ec_cec_resume(struct device *dev)
-{
- struct platform_device *pdev = to_platform_device(dev);
- struct cros_ec_cec *cros_ec_cec = dev_get_drvdata(&pdev->dev);
-
- if (device_may_wakeup(dev))
- disable_irq_wake(cros_ec_cec->cros_ec->irq);
-
- return 0;
-}
-#endif
-
-static SIMPLE_DEV_PM_OPS(cros_ec_cec_pm_ops,
- cros_ec_cec_suspend, cros_ec_cec_resume);
-
-#if IS_ENABLED(CONFIG_PCI) && IS_ENABLED(CONFIG_DMI)
-
-/*
- * The Firmware only handles a single CEC interface tied to a single HDMI
- * connector we specify along with the DRM device name handling the HDMI output
- */
-
-struct cec_dmi_match {
- const char *sys_vendor;
- const char *product_name;
- const char *devname;
- const char *conn;
-};
-
-static const struct cec_dmi_match cec_dmi_match_table[] = {
- /* Google Fizz */
- { "Google", "Fizz", "0000:00:02.0", "Port B" },
-};
-
-static struct device *cros_ec_cec_find_hdmi_dev(struct device *dev,
- const char **conn)
-{
- int i;
-
- for (i = 0 ; i < ARRAY_SIZE(cec_dmi_match_table) ; ++i) {
- const struct cec_dmi_match *m = &cec_dmi_match_table[i];
-
- if (dmi_match(DMI_SYS_VENDOR, m->sys_vendor) &&
- dmi_match(DMI_PRODUCT_NAME, m->product_name)) {
- struct device *d;
-
- /* Find the device, bail out if not yet registered */
- d = bus_find_device_by_name(&pci_bus_type, NULL,
- m->devname);
- if (!d)
- return ERR_PTR(-EPROBE_DEFER);
- put_device(d);
- *conn = m->conn;
- return d;
- }
- }
-
- /* Hardware support must be added in the cec_dmi_match_table */
- dev_warn(dev, "CEC notifier not configured for this hardware\n");
-
- return ERR_PTR(-ENODEV);
-}
-
-#else
-
-static struct device *cros_ec_cec_find_hdmi_dev(struct device *dev,
- const char **conn)
-{
- return ERR_PTR(-ENODEV);
-}
-
-#endif
-
-static int cros_ec_cec_probe(struct platform_device *pdev)
-{
- struct cros_ec_dev *ec_dev = dev_get_drvdata(pdev->dev.parent);
- struct cros_ec_device *cros_ec = ec_dev->ec_dev;
- struct cros_ec_cec *cros_ec_cec;
- struct device *hdmi_dev;
- const char *conn = NULL;
- int ret;
-
- hdmi_dev = cros_ec_cec_find_hdmi_dev(&pdev->dev, &conn);
- if (IS_ERR(hdmi_dev))
- return PTR_ERR(hdmi_dev);
-
- cros_ec_cec = devm_kzalloc(&pdev->dev, sizeof(*cros_ec_cec),
- GFP_KERNEL);
- if (!cros_ec_cec)
- return -ENOMEM;
-
- platform_set_drvdata(pdev, cros_ec_cec);
- cros_ec_cec->cros_ec = cros_ec;
-
- device_init_wakeup(&pdev->dev, 1);
-
- cros_ec_cec->adap = cec_allocate_adapter(&cros_ec_cec_ops, cros_ec_cec,
- DRV_NAME,
- CEC_CAP_DEFAULTS |
- CEC_CAP_CONNECTOR_INFO, 1);
- if (IS_ERR(cros_ec_cec->adap))
- return PTR_ERR(cros_ec_cec->adap);
-
- cros_ec_cec->notify = cec_notifier_cec_adap_register(hdmi_dev, conn,
- cros_ec_cec->adap);
- if (!cros_ec_cec->notify) {
- ret = -ENOMEM;
- goto out_probe_adapter;
- }
-
- /* Get CEC events from the EC. */
- cros_ec_cec->notifier.notifier_call = cros_ec_cec_event;
- ret = blocking_notifier_chain_register(&cros_ec->event_notifier,
- &cros_ec_cec->notifier);
- if (ret) {
- dev_err(&pdev->dev, "failed to register notifier\n");
- goto out_probe_notify;
- }
-
- ret = cec_register_adapter(cros_ec_cec->adap, &pdev->dev);
- if (ret < 0)
- goto out_probe_notify;
-
- return 0;
-
-out_probe_notify:
- cec_notifier_cec_adap_unregister(cros_ec_cec->notify);
-out_probe_adapter:
- cec_delete_adapter(cros_ec_cec->adap);
- return ret;
-}
-
-static int cros_ec_cec_remove(struct platform_device *pdev)
-{
- struct cros_ec_cec *cros_ec_cec = platform_get_drvdata(pdev);
- struct device *dev = &pdev->dev;
- int ret;
-
- ret = blocking_notifier_chain_unregister(
- &cros_ec_cec->cros_ec->event_notifier,
- &cros_ec_cec->notifier);
-
- if (ret) {
- dev_err(dev, "failed to unregister notifier\n");
- return ret;
- }
-
- cec_notifier_cec_adap_unregister(cros_ec_cec->notify);
- cec_unregister_adapter(cros_ec_cec->adap);
-
- return 0;
-}
-
-static struct platform_driver cros_ec_cec_driver = {
- .probe = cros_ec_cec_probe,
- .remove = cros_ec_cec_remove,
- .driver = {
- .name = DRV_NAME,
- .pm = &cros_ec_cec_pm_ops,
- },
-};
-
-module_platform_driver(cros_ec_cec_driver);
-
-MODULE_DESCRIPTION("CEC driver for ChromeOS ECs");
-MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/drivers/media/platform/davinci/dm355_ccdc.c b/drivers/media/platform/davinci/dm355_ccdc.c
index f299baf..e06d113 100644
--- a/drivers/media/platform/davinci/dm355_ccdc.c
+++ b/drivers/media/platform/davinci/dm355_ccdc.c
@@ -883,7 +883,7 @@
goto fail_nores;
}
- ccdc_cfg.base_addr = ioremap_nocache(res->start, resource_size(res));
+ ccdc_cfg.base_addr = ioremap(res->start, resource_size(res));
if (!ccdc_cfg.base_addr) {
status = -ENOMEM;
goto fail_nomem;
diff --git a/drivers/media/platform/davinci/dm644x_ccdc.c b/drivers/media/platform/davinci/dm644x_ccdc.c
index 2fc6c9c..c6378c4 100644
--- a/drivers/media/platform/davinci/dm644x_ccdc.c
+++ b/drivers/media/platform/davinci/dm644x_ccdc.c
@@ -817,7 +817,7 @@
goto fail_nores;
}
- ccdc_cfg.base_addr = ioremap_nocache(res->start, resource_size(res));
+ ccdc_cfg.base_addr = ioremap(res->start, resource_size(res));
if (!ccdc_cfg.base_addr) {
status = -ENOMEM;
goto fail_nomem;
diff --git a/drivers/media/platform/davinci/isif.c b/drivers/media/platform/davinci/isif.c
index e2e7ab7..c98edb6 100644
--- a/drivers/media/platform/davinci/isif.c
+++ b/drivers/media/platform/davinci/isif.c
@@ -29,7 +29,7 @@
#include "ccdc_hw_device.h"
/* Defaults for module configuration parameters */
-static struct isif_config_params_raw isif_config_defaults = {
+static const struct isif_config_params_raw isif_config_defaults = {
.linearize = {
.en = 0,
.corr_shft = ISIF_NO_SHIFT,
@@ -1045,7 +1045,7 @@
status = -EBUSY;
goto fail_nobase_res;
}
- addr = ioremap_nocache(res->start, resource_size(res));
+ addr = ioremap(res->start, resource_size(res));
if (!addr) {
status = -ENOMEM;
goto fail_base_iomap;
diff --git a/drivers/media/platform/davinci/vpbe_display.c b/drivers/media/platform/davinci/vpbe_display.c
index 7fbd22d..bf3c3e7 100644
--- a/drivers/media/platform/davinci/vpbe_display.c
+++ b/drivers/media/platform/davinci/vpbe_display.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com/
+ * Copyright (C) 2010 Texas Instruments Incorporated - https://www.ti.com/
*/
#include <linux/kernel.h>
#include <linux/init.h>
@@ -17,7 +17,6 @@
#include <linux/videodev2.h>
#include <linux/slab.h>
-#include <asm/pgtable.h>
#include <media/v4l2-dev.h>
#include <media/v4l2-common.h>
@@ -1339,7 +1338,7 @@
vpbe_display_layer->video_dev.queue = &vpbe_display_layer->buffer_queue;
err = video_register_device(&vpbe_display_layer->video_dev,
- VFL_TYPE_GRABBER,
+ VFL_TYPE_VIDEO,
-1);
if (err)
return -ENODEV;
diff --git a/drivers/media/platform/davinci/vpfe_capture.c b/drivers/media/platform/davinci/vpfe_capture.c
index 916ed74..f9f7dd1 100644
--- a/drivers/media/platform/davinci/vpfe_capture.c
+++ b/drivers/media/platform/davinci/vpfe_capture.c
@@ -168,21 +168,22 @@
int ret = 0;
printk(KERN_NOTICE "vpfe_register_ccdc_device: %s\n", dev->name);
- BUG_ON(!dev->hw_ops.open);
- BUG_ON(!dev->hw_ops.enable);
- BUG_ON(!dev->hw_ops.set_hw_if_params);
- BUG_ON(!dev->hw_ops.configure);
- BUG_ON(!dev->hw_ops.set_buftype);
- BUG_ON(!dev->hw_ops.get_buftype);
- BUG_ON(!dev->hw_ops.enum_pix);
- BUG_ON(!dev->hw_ops.set_frame_format);
- BUG_ON(!dev->hw_ops.get_frame_format);
- BUG_ON(!dev->hw_ops.get_pixel_format);
- BUG_ON(!dev->hw_ops.set_pixel_format);
- BUG_ON(!dev->hw_ops.set_image_window);
- BUG_ON(!dev->hw_ops.get_image_window);
- BUG_ON(!dev->hw_ops.get_line_length);
- BUG_ON(!dev->hw_ops.getfid);
+ if (!dev->hw_ops.open ||
+ !dev->hw_ops.enable ||
+ !dev->hw_ops.set_hw_if_params ||
+ !dev->hw_ops.configure ||
+ !dev->hw_ops.set_buftype ||
+ !dev->hw_ops.get_buftype ||
+ !dev->hw_ops.enum_pix ||
+ !dev->hw_ops.set_frame_format ||
+ !dev->hw_ops.get_frame_format ||
+ !dev->hw_ops.get_pixel_format ||
+ !dev->hw_ops.set_pixel_format ||
+ !dev->hw_ops.set_image_window ||
+ !dev->hw_ops.get_image_window ||
+ !dev->hw_ops.get_line_length ||
+ !dev->hw_ops.getfid)
+ return -EINVAL;
mutex_lock(&ccdc_lock);
if (!ccdc_cfg) {
@@ -879,7 +880,7 @@
/* Fill in the information about format */
pix_fmt = vpfe_lookup_pix_format(pix);
if (pix_fmt) {
- fmt->pixelformat = fmt->pixelformat;
+ fmt->pixelformat = pix_fmt->pixelformat;
return 0;
}
return -EINVAL;
@@ -1779,7 +1780,7 @@
"video_dev=%p\n", &vpfe_dev->video_dev);
vpfe_dev->fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ret = video_register_device(&vpfe_dev->video_dev,
- VFL_TYPE_GRABBER, -1);
+ VFL_TYPE_VIDEO, -1);
if (ret) {
v4l2_err(pdev->dev.driver,
diff --git a/drivers/media/platform/davinci/vpif.c b/drivers/media/platform/davinci/vpif.c
index df66461..5e67994 100644
--- a/drivers/media/platform/davinci/vpif.c
+++ b/drivers/media/platform/davinci/vpif.c
@@ -5,7 +5,7 @@
* The hardware supports SDTV, HDTV formats, raw data capture.
* Currently, the driver supports NTSC and PAL standards.
*
- * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/
+ * Copyright (C) 2009 Texas Instruments Incorporated - https://www.ti.com/
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -458,6 +458,7 @@
res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (!res_irq) {
dev_warn(&pdev->dev, "Missing IRQ resource.\n");
+ pm_runtime_put(&pdev->dev);
return -EINVAL;
}
diff --git a/drivers/media/platform/davinci/vpif.h b/drivers/media/platform/davinci/vpif.h
index 2466c7c..c6d1d89 100644
--- a/drivers/media/platform/davinci/vpif.h
+++ b/drivers/media/platform/davinci/vpif.h
@@ -1,7 +1,7 @@
/*
* VPIF header file
*
- * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/
+ * Copyright (C) 2009 Texas Instruments Incorporated - https://www.ti.com/
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
diff --git a/drivers/media/platform/davinci/vpif_capture.c b/drivers/media/platform/davinci/vpif_capture.c
index 74f68ac..72a0e94 100644
--- a/drivers/media/platform/davinci/vpif_capture.c
+++ b/drivers/media/platform/davinci/vpif_capture.c
@@ -1466,7 +1466,7 @@
vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
video_set_drvdata(&ch->video_dev, ch);
err = video_register_device(vdev,
- VFL_TYPE_GRABBER, (j ? 1 : 0));
+ VFL_TYPE_VIDEO, (j ? 1 : 0));
if (err)
goto probe_out;
}
diff --git a/drivers/media/platform/davinci/vpif_display.c b/drivers/media/platform/davinci/vpif_display.c
index abbdbac..46afc02 100644
--- a/drivers/media/platform/davinci/vpif_display.c
+++ b/drivers/media/platform/davinci/vpif_display.c
@@ -2,7 +2,7 @@
* vpif-display - VPIF display driver
* Display driver for TI DaVinci VPIF
*
- * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/
+ * Copyright (C) 2009 Texas Instruments Incorporated - https://www.ti.com/
* Copyright (C) 2014 Lad, Prabhakar <prabhakar.csengg@gmail.com>
*
* This program is free software; you can redistribute it and/or
@@ -1214,7 +1214,7 @@
vdev->lock = &common->lock;
vdev->device_caps = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING;
video_set_drvdata(&ch->video_dev, ch);
- err = video_register_device(vdev, VFL_TYPE_GRABBER,
+ err = video_register_device(vdev, VFL_TYPE_VIDEO,
(j ? 3 : 2));
if (err < 0)
goto probe_out;
@@ -1225,7 +1225,6 @@
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/davinci/vpif_display.h b/drivers/media/platform/davinci/vpif_display.h
index af2765f..f731a65 100644
--- a/drivers/media/platform/davinci/vpif_display.h
+++ b/drivers/media/platform/davinci/vpif_display.h
@@ -1,7 +1,7 @@
/*
* VPIF display header file
*
- * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/
+ * Copyright (C) 2009 Texas Instruments Incorporated - https://www.ti.com/
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
diff --git a/drivers/media/platform/exynos-gsc/gsc-core.c b/drivers/media/platform/exynos-gsc/gsc-core.c
index f6650b4..9f41c2e 100644
--- a/drivers/media/platform/exynos-gsc/gsc-core.c
+++ b/drivers/media/platform/exynos-gsc/gsc-core.c
@@ -577,7 +577,7 @@
v4l_bound_align_image(&tmp_w, min_w, max_w, mod_x,
&tmp_h, min_h, max_h, mod_y, 0);
- if (!V4L2_TYPE_IS_OUTPUT(s->type) &&
+ if (V4L2_TYPE_IS_CAPTURE(s->type) &&
(ctx->gsc_ctrls.rotate->val == 90 ||
ctx->gsc_ctrls.rotate->val == 270))
gsc_check_crop_change(tmp_h, tmp_w,
diff --git a/drivers/media/platform/exynos-gsc/gsc-m2m.c b/drivers/media/platform/exynos-gsc/gsc-m2m.c
index 42d1e44..f1cf847 100644
--- a/drivers/media/platform/exynos-gsc/gsc-m2m.c
+++ b/drivers/media/platform/exynos-gsc/gsc-m2m.c
@@ -253,7 +253,7 @@
if (IS_ERR(frame))
return PTR_ERR(frame);
- if (!V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) {
+ if (V4L2_TYPE_IS_CAPTURE(vb->vb2_queue->type)) {
for (i = 0; i < frame->fmt->num_planes; i++)
vb2_set_plane_payload(vb, i, frame->payload[i]);
}
@@ -769,7 +769,7 @@
return PTR_ERR(gsc->m2m.m2m_dev);
}
- ret = video_register_device(&gsc->vdev, VFL_TYPE_GRABBER, -1);
+ ret = video_register_device(&gsc->vdev, VFL_TYPE_VIDEO, -1);
if (ret) {
dev_err(&pdev->dev,
"%s(): failed to register video device\n", __func__);
diff --git a/drivers/media/platform/exynos4-is/Kconfig b/drivers/media/platform/exynos4-is/Kconfig
index 989cb34..136d3b2 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
@@ -13,7 +14,7 @@
if VIDEO_SAMSUNG_EXYNOS4_IS
config VIDEO_EXYNOS4_IS_COMMON
- tristate
+ tristate
config VIDEO_S5P_FIMC
tristate "S5P/EXYNOS4 FIMC/CAMIF camera interface driver"
diff --git a/drivers/media/platform/exynos4-is/fimc-capture.c b/drivers/media/platform/exynos4-is/fimc-capture.c
index 121d609..808b490 100644
--- a/drivers/media/platform/exynos4-is/fimc-capture.c
+++ b/drivers/media/platform/exynos4-is/fimc-capture.c
@@ -21,6 +21,7 @@
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-mem2mem.h>
+#include <media/v4l2-rect.h>
#include <media/videobuf2-v4l2.h>
#include <media/videobuf2-dma-contig.h>
@@ -477,7 +478,7 @@
goto unlock;
set_bit(ST_CAPT_BUSY, &fimc->state);
- ret = pm_runtime_get_sync(&fimc->pdev->dev);
+ ret = pm_runtime_resume_and_get(&fimc->pdev->dev);
if (ret < 0)
goto unlock;
@@ -492,17 +493,6 @@
ret = fimc_pipeline_call(ve, open, &ve->vdev.entity, true);
- if (ret == 0 && vc->user_subdev_api && vc->inh_sensor_ctrls) {
- /*
- * Recreate controls of the the video node to drop
- * any controls inherited from the sensor subdev.
- */
- fimc_ctrls_delete(vc->ctx);
-
- ret = fimc_ctrls_create(vc->ctx);
- if (ret == 0)
- vc->inh_sensor_ctrls = false;
- }
if (ret == 0)
ve->vdev.entity.use_count++;
@@ -1243,8 +1233,11 @@
if (ret < 0)
return ret;
- media_pipeline_stop(&vc->ve.vdev.entity);
- vc->streaming = false;
+ if (vc->streaming) {
+ media_pipeline_stop(&vc->ve.vdev.entity);
+ vc->streaming = false;
+ }
+
return 0;
}
@@ -1276,7 +1269,7 @@
case V4L2_SEL_TGT_COMPOSE_DEFAULT:
case V4L2_SEL_TGT_COMPOSE_BOUNDS:
f = &ctx->d_frame;
- /* fall through */
+ fallthrough;
case V4L2_SEL_TGT_CROP_BOUNDS:
case V4L2_SEL_TGT_CROP_DEFAULT:
s->r.left = 0;
@@ -1287,7 +1280,7 @@
case V4L2_SEL_TGT_COMPOSE:
f = &ctx->d_frame;
- /* fall through */
+ fallthrough;
case V4L2_SEL_TGT_CROP:
s->r.left = f->offs_h;
s->r.top = f->offs_v;
@@ -1299,19 +1292,6 @@
return -EINVAL;
}
-/* Return 1 if rectangle a is enclosed in rectangle b, or 0 otherwise. */
-static int enclosed_rectangle(struct v4l2_rect *a, struct v4l2_rect *b)
-{
- if (a->left < b->left || a->top < b->top)
- return 0;
- if (a->left + a->width > b->left + b->width)
- return 0;
- if (a->top + a->height > b->top + b->height)
- return 0;
-
- return 1;
-}
-
static int fimc_cap_s_selection(struct file *file, void *fh,
struct v4l2_selection *s)
{
@@ -1334,11 +1314,11 @@
fimc_capture_try_selection(ctx, &rect, s->target);
if (s->flags & V4L2_SEL_FLAG_LE &&
- !enclosed_rectangle(&rect, &s->r))
+ !v4l2_rect_enclosed(&rect, &s->r))
return -ERANGE;
if (s->flags & V4L2_SEL_FLAG_GE &&
- !enclosed_rectangle(&s->r, &rect))
+ !v4l2_rect_enclosed(&s->r, &rect))
return -ERANGE;
s->r = rect;
@@ -1408,7 +1388,7 @@
vc->input = sd->grp_id;
- if (vc->user_subdev_api || vc->inh_sensor_ctrls)
+ if (vc->user_subdev_api)
return 0;
/* Inherit V4L2 controls from the image sensor subdev. */
@@ -1611,7 +1591,7 @@
switch (sel->target) {
case V4L2_SEL_TGT_COMPOSE_BOUNDS:
f = &ctx->d_frame;
- /* fall through */
+ fallthrough;
case V4L2_SEL_TGT_CROP_BOUNDS:
r->width = f->o_width;
r->height = f->o_height;
@@ -1808,7 +1788,7 @@
if (ret)
goto err_me_cleanup;
- ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1);
+ ret = video_register_device(vfd, VFL_TYPE_VIDEO, -1);
if (ret)
goto err_ctrl_free;
@@ -1898,6 +1878,7 @@
return ret;
sd->entity.ops = &fimc_sd_media_ops;
+ sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_SCALER;
sd->internal_ops = &fimc_capture_sd_internal_ops;
v4l2_set_subdevdata(sd, fimc);
return 0;
diff --git a/drivers/media/platform/exynos4-is/fimc-core.c b/drivers/media/platform/exynos4-is/fimc-core.c
index cde60fb..08d1f39 100644
--- a/drivers/media/platform/exynos4-is/fimc-core.c
+++ b/drivers/media/platform/exynos4-is/fimc-core.c
@@ -954,9 +954,11 @@
spin_lock_init(&fimc->slock);
mutex_init(&fimc->lock);
- fimc->sysreg = fimc_get_sysreg_regmap(dev->of_node);
- if (IS_ERR(fimc->sysreg))
- return PTR_ERR(fimc->sysreg);
+ if (fimc->variant->has_isp_wb) {
+ fimc->sysreg = fimc_get_sysreg_regmap(dev->of_node);
+ if (IS_ERR(fimc->sysreg))
+ return PTR_ERR(fimc->sysreg);
+ }
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
fimc->regs = devm_ioremap_resource(dev, res);
@@ -1110,67 +1112,8 @@
return 0;
}
-/* Image pixel limits, similar across several FIMC HW revisions. */
-static const struct fimc_pix_limit s5p_pix_limit[4] = {
- [0] = {
- .scaler_en_w = 3264,
- .scaler_dis_w = 8192,
- .out_rot_en_w = 1920,
- .out_rot_dis_w = 4224,
- },
- [1] = {
- .scaler_en_w = 4224,
- .scaler_dis_w = 8192,
- .out_rot_en_w = 1920,
- .out_rot_dis_w = 4224,
- },
- [2] = {
- .scaler_en_w = 1920,
- .scaler_dis_w = 8192,
- .out_rot_en_w = 1280,
- .out_rot_dis_w = 1920,
- },
-};
-
-static const struct fimc_variant fimc0_variant_s5pv210 = {
- .has_inp_rot = 1,
- .has_out_rot = 1,
- .has_cam_if = 1,
- .min_inp_pixsize = 16,
- .min_out_pixsize = 16,
- .hor_offs_align = 8,
- .min_vsize_align = 16,
- .pix_limit = &s5p_pix_limit[1],
-};
-
-static const struct fimc_variant fimc1_variant_s5pv210 = {
- .has_inp_rot = 1,
- .has_out_rot = 1,
- .has_cam_if = 1,
- .has_mainscaler_ext = 1,
- .min_inp_pixsize = 16,
- .min_out_pixsize = 16,
- .hor_offs_align = 1,
- .min_vsize_align = 1,
- .pix_limit = &s5p_pix_limit[2],
-};
-
-static const struct fimc_variant fimc2_variant_s5pv210 = {
- .has_cam_if = 1,
- .min_inp_pixsize = 16,
- .min_out_pixsize = 16,
- .hor_offs_align = 8,
- .min_vsize_align = 16,
- .pix_limit = &s5p_pix_limit[2],
-};
-
/* S5PV210, S5PC110 */
static const struct fimc_drvdata fimc_drvdata_s5pv210 = {
- .variant = {
- [0] = &fimc0_variant_s5pv210,
- [1] = &fimc1_variant_s5pv210,
- [2] = &fimc2_variant_s5pv210,
- },
.num_entities = 3,
.lclk_frequency = 166000000UL,
.out_buf_count = 4,
diff --git a/drivers/media/platform/exynos4-is/fimc-core.h b/drivers/media/platform/exynos4-is/fimc-core.h
index d130f66..e4a5623 100644
--- a/drivers/media/platform/exynos4-is/fimc-core.h
+++ b/drivers/media/platform/exynos4-is/fimc-core.h
@@ -296,11 +296,8 @@
* @buf_index: index for managing the output DMA buffers
* @frame_count: the frame counter for statistics
* @reqbufs_count: the number of buffers requested in REQBUFS ioctl
- * @input_index: input (camera sensor) index
* @input: capture input type, grp_id of the attached subdev
* @user_subdev_api: true if subdevs are not configured by the host driver
- * @inh_sensor_ctrls: a flag indicating v4l2 controls are inherited from
- * an image sensor subdev
*/
struct fimc_vid_cap {
struct fimc_ctx *ctx;
@@ -319,10 +316,8 @@
unsigned int frame_count;
unsigned int reqbufs_count;
bool streaming;
- int input_index;
u32 input;
bool user_subdev_api;
- bool inh_sensor_ctrls;
};
/**
diff --git a/drivers/media/platform/exynos4-is/fimc-is.c b/drivers/media/platform/exynos4-is/fimc-is.c
index 64148b7..d26fa59 100644
--- a/drivers/media/platform/exynos4-is/fimc-is.c
+++ b/drivers/media/platform/exynos4-is/fimc-is.c
@@ -12,7 +12,6 @@
#include <linux/device.h>
#include <linux/debugfs.h>
#include <linux/delay.h>
-#include <linux/dma-contiguous.h>
#include <linux/errno.h>
#include <linux/firmware.h>
#include <linux/interrupt.h>
@@ -756,18 +755,12 @@
is->debugfs_entry = NULL;
}
-static int fimc_is_debugfs_create(struct fimc_is *is)
+static void fimc_is_debugfs_create(struct fimc_is *is)
{
- struct dentry *dentry;
-
is->debugfs_entry = debugfs_create_dir("fimc_is", NULL);
- dentry = debugfs_create_file("fw_log", S_IRUGO, is->debugfs_entry,
- is, &fimc_is_fops);
- if (!dentry)
- fimc_is_debugfs_remove(is);
-
- return is->debugfs_entry == NULL ? -EIO : 0;
+ debugfs_create_file("fw_log", S_IRUGO, is->debugfs_entry, is,
+ &fimc_is_fops);
}
static int fimc_is_runtime_resume(struct device *dev);
@@ -835,9 +828,9 @@
goto err_irq;
}
- ret = pm_runtime_get_sync(dev);
+ ret = pm_runtime_resume_and_get(dev);
if (ret < 0)
- goto err_pm;
+ goto err_irq;
vb2_dma_contig_set_max_seg_size(dev, DMA_BIT_MASK(32));
@@ -853,9 +846,7 @@
if (ret < 0)
goto err_pm;
- ret = fimc_is_debugfs_create(is);
- if (ret < 0)
- goto err_sd;
+ fimc_is_debugfs_create(is);
ret = fimc_is_request_firmware(is, FIMC_IS_FW_FILENAME);
if (ret < 0)
@@ -868,9 +859,9 @@
err_dfs:
fimc_is_debugfs_remove(is);
-err_sd:
fimc_is_unregister_subdevs(is);
err_pm:
+ pm_runtime_put_noidle(dev);
if (!pm_runtime_enabled(dev))
fimc_is_runtime_suspend(dev);
err_irq:
diff --git a/drivers/media/platform/exynos4-is/fimc-isp-video.c b/drivers/media/platform/exynos4-is/fimc-isp-video.c
index 370cdf0..83688a7 100644
--- a/drivers/media/platform/exynos4-is/fimc-isp-video.c
+++ b/drivers/media/platform/exynos4-is/fimc-isp-video.c
@@ -275,7 +275,7 @@
if (ret < 0)
goto unlock;
- ret = pm_runtime_get_sync(&isp->pdev->dev);
+ ret = pm_runtime_resume_and_get(&isp->pdev->dev);
if (ret < 0)
goto rel_fh;
@@ -622,7 +622,7 @@
video_set_drvdata(vdev, isp);
- ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
+ ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
if (ret < 0) {
media_entity_cleanup(&vdev->entity);
return ret;
diff --git a/drivers/media/platform/exynos4-is/fimc-isp.c b/drivers/media/platform/exynos4-is/fimc-isp.c
index a77c49b..74b49d3 100644
--- a/drivers/media/platform/exynos4-is/fimc-isp.c
+++ b/drivers/media/platform/exynos4-is/fimc-isp.c
@@ -304,11 +304,10 @@
pr_debug("on: %d\n", on);
if (on) {
- ret = pm_runtime_get_sync(&is->pdev->dev);
- if (ret < 0) {
- pm_runtime_put(&is->pdev->dev);
+ ret = pm_runtime_resume_and_get(&is->pdev->dev);
+ if (ret < 0)
return ret;
- }
+
set_bit(IS_ST_PWR_ON, &is->state);
ret = fimc_is_start_firmware(is);
diff --git a/drivers/media/platform/exynos4-is/fimc-lite.c b/drivers/media/platform/exynos4-is/fimc-lite.c
index efd0662..d279f28 100644
--- a/drivers/media/platform/exynos4-is/fimc-lite.c
+++ b/drivers/media/platform/exynos4-is/fimc-lite.c
@@ -25,6 +25,7 @@
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-mem2mem.h>
+#include <media/v4l2-rect.h>
#include <media/videobuf2-v4l2.h>
#include <media/videobuf2-dma-contig.h>
#include <media/drv-intf/exynos-fimc.h>
@@ -468,9 +469,9 @@
}
set_bit(ST_FLITE_IN_USE, &fimc->state);
- ret = pm_runtime_get_sync(&fimc->pdev->dev);
+ ret = pm_runtime_resume_and_get(&fimc->pdev->dev);
if (ret < 0)
- goto err_pm;
+ goto err_in_use;
ret = v4l2_fh_open(file);
if (ret < 0)
@@ -498,6 +499,7 @@
v4l2_fh_release(file);
err_pm:
pm_runtime_put_sync(&fimc->pdev->dev);
+err_in_use:
clear_bit(ST_FLITE_IN_USE, &fimc->state);
unlock:
mutex_unlock(&fimc->lock);
@@ -868,19 +870,6 @@
return ret;
}
-/* Return 1 if rectangle a is enclosed in rectangle b, or 0 otherwise. */
-static int enclosed_rectangle(struct v4l2_rect *a, struct v4l2_rect *b)
-{
- if (a->left < b->left || a->top < b->top)
- return 0;
- if (a->left + a->width > b->left + b->width)
- return 0;
- if (a->top + a->height > b->top + b->height)
- return 0;
-
- return 1;
-}
-
static int fimc_lite_g_selection(struct file *file, void *fh,
struct v4l2_selection *sel)
{
@@ -922,11 +911,11 @@
fimc_lite_try_compose(fimc, &rect);
if ((sel->flags & V4L2_SEL_FLAG_LE) &&
- !enclosed_rectangle(&rect, &sel->r))
+ !v4l2_rect_enclosed(&rect, &sel->r))
return -ERANGE;
if ((sel->flags & V4L2_SEL_FLAG_GE) &&
- !enclosed_rectangle(&sel->r, &rect))
+ !v4l2_rect_enclosed(&sel->r, &rect))
return -ERANGE;
sel->r = rect;
@@ -1297,7 +1286,7 @@
video_set_drvdata(vfd, fimc);
fimc->ve.pipe = v4l2_get_subdev_hostdata(sd);
- ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1);
+ ret = video_register_device(vfd, VFL_TYPE_VIDEO, -1);
if (ret < 0) {
media_entity_cleanup(&vfd->entity);
fimc->ve.pipe = NULL;
@@ -1614,6 +1603,9 @@
struct fimc_lite *fimc = platform_get_drvdata(pdev);
struct device *dev = &pdev->dev;
+ if (!pm_runtime_enabled(dev))
+ clk_disable_unprepare(fimc->clock);
+
pm_runtime_disable(dev);
pm_runtime_set_suspended(dev);
fimc_lite_unregister_capture_subdev(fimc);
diff --git a/drivers/media/platform/exynos4-is/fimc-m2m.c b/drivers/media/platform/exynos4-is/fimc-m2m.c
index c70c2cb..24b1bad 100644
--- a/drivers/media/platform/exynos4-is/fimc-m2m.c
+++ b/drivers/media/platform/exynos4-is/fimc-m2m.c
@@ -73,17 +73,14 @@
static int start_streaming(struct vb2_queue *q, unsigned int count)
{
struct fimc_ctx *ctx = q->drv_priv;
- int ret;
- ret = pm_runtime_get_sync(&ctx->fimc_dev->pdev->dev);
- return ret > 0 ? 0 : ret;
+ return pm_runtime_resume_and_get(&ctx->fimc_dev->pdev->dev);
}
static void stop_streaming(struct vb2_queue *q)
{
struct fimc_ctx *ctx = q->drv_priv;
-
fimc_m2m_shutdown(ctx);
fimc_m2m_job_finish(ctx, VB2_BUF_STATE_ERROR);
pm_runtime_put(&ctx->fimc_dev->pdev->dev);
@@ -746,7 +743,7 @@
if (ret)
goto err_me;
- ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1);
+ ret = video_register_device(vfd, VFL_TYPE_VIDEO, -1);
if (ret)
goto err_vd;
diff --git a/drivers/media/platform/exynos4-is/fimc-reg.c b/drivers/media/platform/exynos4-is/fimc-reg.c
index 5ce2bde..8764999 100644
--- a/drivers/media/platform/exynos4-is/fimc-reg.c
+++ b/drivers/media/platform/exynos4-is/fimc-reg.c
@@ -606,6 +606,11 @@
switch (source->fimc_bus_type) {
case FIMC_BUS_TYPE_ITU_601:
case FIMC_BUS_TYPE_ITU_656:
+ if (fimc_fmt_is_user_defined(f->fmt->color)) {
+ cfg |= FIMC_REG_CISRCFMT_ITU601_8BIT;
+ break;
+ }
+
for (i = 0; i < ARRAY_SIZE(pix_desc); i++) {
if (vc->ci_fmt.code == pix_desc[i].pixelcode) {
cfg = pix_desc[i].cisrcfmt;
@@ -707,10 +712,12 @@
case FIMC_BUS_TYPE_ITU_601...FIMC_BUS_TYPE_ITU_656:
if (source->mux_id == 0) /* ITU-A, ITU-B: 0, 1 */
cfg |= FIMC_REG_CIGCTRL_SELCAM_ITU_A;
+ if (vid_cap->ci_fmt.code == MEDIA_BUS_FMT_JPEG_1X8)
+ cfg |= FIMC_REG_CIGCTRL_CAM_JPEG;
break;
case FIMC_BUS_TYPE_LCD_WRITEBACK_A:
cfg |= FIMC_REG_CIGCTRL_CAMIF_SELWB;
- /* fall through */
+ fallthrough;
case FIMC_BUS_TYPE_ISP_WRITEBACK:
if (fimc->variant->has_isp_wb)
cfg |= FIMC_REG_CIGCTRL_CAMIF_SELWB;
diff --git a/drivers/media/platform/exynos4-is/media-dev.c b/drivers/media/platform/exynos4-is/media-dev.c
index a07d796..a9a8f04 100644
--- a/drivers/media/platform/exynos4-is/media-dev.c
+++ b/drivers/media/platform/exynos4-is/media-dev.c
@@ -19,6 +19,7 @@
#include <linux/of_platform.h>
#include <linux/of_device.h>
#include <linux/of_graph.h>
+#include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/types.h>
@@ -92,7 +93,7 @@
switch (sd->grp_id) {
case GRP_ID_SENSOR:
sensor = sd;
- /* fall through */
+ fallthrough;
case GRP_ID_FIMC_IS_SENSOR:
p->subdevs[IDX_SENSOR] = sd;
break;
@@ -289,11 +290,26 @@
{ IDX_CSIS, IDX_FLITE, IDX_FIMC, IDX_SENSOR, IDX_IS_ISP },
};
struct fimc_pipeline *p = to_fimc_pipeline(ep);
- struct fimc_md *fmd = entity_to_fimc_mdev(&p->subdevs[IDX_CSIS]->entity);
enum fimc_subdev_index sd_id;
int i, ret = 0;
if (p->subdevs[IDX_SENSOR] == NULL) {
+ struct fimc_md *fmd;
+ struct v4l2_subdev *sd = p->subdevs[IDX_CSIS];
+
+ if (!sd)
+ sd = p->subdevs[IDX_FIMC];
+
+ if (!sd) {
+ /*
+ * If neither CSIS nor FIMC was set up,
+ * it's impossible to have any sensors
+ */
+ return -ENODEV;
+ }
+
+ fmd = entity_to_fimc_mdev(&sd->entity);
+
if (!fmd->user_subdev_api) {
/*
* Sensor must be already discovered if we
@@ -379,21 +395,15 @@
}
}
-/* Parse port node and register as a sub-device any sensor specified there. */
-static int fimc_md_parse_port_node(struct fimc_md *fmd,
- struct device_node *port,
- unsigned int index)
+static int fimc_md_parse_one_endpoint(struct fimc_md *fmd,
+ struct device_node *ep)
{
+ int index = fmd->num_sensors;
struct fimc_source_info *pd = &fmd->sensor[index].pdata;
- struct device_node *rem, *ep, *np;
+ struct device_node *rem, *np;
struct v4l2_fwnode_endpoint endpoint = { .bus_type = 0 };
int ret;
- /* Assume here a port node can have only one endpoint node. */
- ep = of_get_next_child(port, NULL);
- if (!ep)
- return 0;
-
ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep), &endpoint);
if (ret) {
of_node_put(ep);
@@ -467,13 +477,28 @@
return 0;
}
+/* Parse port node and register as a sub-device any sensor specified there. */
+static int fimc_md_parse_port_node(struct fimc_md *fmd,
+ struct device_node *port)
+{
+ struct device_node *ep;
+ int ret;
+
+ for_each_child_of_node(port, ep) {
+ ret = fimc_md_parse_one_endpoint(fmd, ep);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
/* Register all SoC external sub-devices */
static int fimc_md_register_sensor_entities(struct fimc_md *fmd)
{
struct device_node *parent = fmd->pdev->dev.of_node;
struct device_node *ports = NULL;
struct device_node *node;
- int index = 0;
int ret;
/*
@@ -483,11 +508,9 @@
if (!fmd->pmf)
return -ENXIO;
- ret = pm_runtime_get_sync(fmd->pmf);
- if (ret < 0) {
- pm_runtime_put(fmd->pmf);
+ ret = pm_runtime_resume_and_get(fmd->pmf);
+ if (ret < 0)
return ret;
- }
fmd->num_sensors = 0;
@@ -502,13 +525,12 @@
if (!port)
continue;
- ret = fimc_md_parse_port_node(fmd, port, index);
+ ret = fimc_md_parse_port_node(fmd, port);
of_node_put(port);
if (ret < 0) {
of_node_put(node);
goto cleanup;
}
- index++;
}
/* Attach sensors listed in the parallel-ports node */
@@ -517,12 +539,11 @@
goto rpm_put;
for_each_child_of_node(ports, node) {
- ret = fimc_md_parse_port_node(fmd, node, index);
+ ret = fimc_md_parse_port_node(fmd, node);
if (ret < 0) {
of_node_put(node);
goto cleanup;
}
- index++;
}
of_node_put(ports);
@@ -1256,36 +1277,14 @@
static DEVICE_ATTR(subdev_conf_mode, S_IWUSR | S_IRUGO,
fimc_md_sysfs_show, fimc_md_sysfs_store);
-static int fimc_md_get_pinctrl(struct fimc_md *fmd)
-{
- struct device *dev = &fmd->pdev->dev;
- struct fimc_pinctrl *pctl = &fmd->pinctl;
-
- pctl->pinctrl = devm_pinctrl_get(dev);
- if (IS_ERR(pctl->pinctrl))
- return PTR_ERR(pctl->pinctrl);
-
- pctl->state_default = pinctrl_lookup_state(pctl->pinctrl,
- PINCTRL_STATE_DEFAULT);
- if (IS_ERR(pctl->state_default))
- return PTR_ERR(pctl->state_default);
-
- /* PINCTRL_STATE_IDLE is optional */
- pctl->state_idle = pinctrl_lookup_state(pctl->pinctrl,
- PINCTRL_STATE_IDLE);
- return 0;
-}
-
static int cam_clk_prepare(struct clk_hw *hw)
{
struct cam_clk *camclk = to_cam_clk(hw);
- int ret;
if (camclk->fmd->pmf == NULL)
return -ENODEV;
- ret = pm_runtime_get_sync(camclk->fmd->pmf);
- return ret < 0 ? ret : 0;
+ return pm_runtime_resume_and_get(camclk->fmd->pmf);
}
static void cam_clk_unprepare(struct clk_hw *hw)
@@ -1431,6 +1430,7 @@
{
struct device *dev = &pdev->dev;
struct v4l2_device *v4l2_dev;
+ struct pinctrl *pinctrl;
struct fimc_md *fmd;
int ret;
@@ -1442,7 +1442,7 @@
INIT_LIST_HEAD(&fmd->pipelines);
fmd->pdev = pdev;
- strscpy(fmd->media_dev.model, "SAMSUNG S5P FIMC",
+ strscpy(fmd->media_dev.model, "Samsung S5P FIMC",
sizeof(fmd->media_dev.model));
fmd->media_dev.ops = &fimc_md_ops;
fmd->media_dev.dev = dev;
@@ -1467,8 +1467,9 @@
if (ret)
goto err_v4l2dev;
- ret = fimc_md_get_pinctrl(fmd);
- if (ret < 0) {
+ pinctrl = devm_pinctrl_get(dev);
+ if (IS_ERR(pinctrl)) {
+ ret = PTR_ERR(pinctrl);
if (ret != EPROBE_DEFER)
dev_err(dev, "Failed to get pinctrl: %d\n", ret);
goto err_clk;
diff --git a/drivers/media/platform/exynos4-is/media-dev.h b/drivers/media/platform/exynos4-is/media-dev.h
index 4b8f9ac..9447faf 100644
--- a/drivers/media/platform/exynos4-is/media-dev.h
+++ b/drivers/media/platform/exynos4-is/media-dev.h
@@ -27,8 +27,6 @@
#define FIMC_IS_OF_NODE_NAME "fimc-is"
#define CSIS_OF_NODE_NAME "csis"
-#define PINCTRL_STATE_IDLE "idle"
-
#define FIMC_MAX_SENSORS 4
#define FIMC_MAX_CAMCLKS 2
#define DEFAULT_SENSOR_CLK_FREQ 24000000U
@@ -109,9 +107,6 @@
* @media_dev: top level media device
* @v4l2_dev: top level v4l2_device holding up the subdevs
* @pdev: platform device this media device is hooked up into
- * @pinctrl: camera port pinctrl handle
- * @state_default: pinctrl default state handle
- * @state_idle: pinctrl idle state handle
* @cam_clk_provider: CAMCLK clock provider structure
* @user_subdev_api: true if subdevs are not configured by the host driver
* @slock: spinlock protecting @sensor array
@@ -131,12 +126,6 @@
struct v4l2_device v4l2_dev;
struct platform_device *pdev;
- struct fimc_pinctrl {
- struct pinctrl *pinctrl;
- struct pinctrl_state *state_default;
- struct pinctrl_state *state_idle;
- } pinctl;
-
struct cam_clk_provider {
struct clk *clks[FIMC_MAX_CAMCLKS];
struct clk_onecell_data clk_data;
diff --git a/drivers/media/platform/exynos4-is/mipi-csis.c b/drivers/media/platform/exynos4-is/mipi-csis.c
index 1aac167..ebf39c8 100644
--- a/drivers/media/platform/exynos4-is/mipi-csis.c
+++ b/drivers/media/platform/exynos4-is/mipi-csis.c
@@ -494,7 +494,7 @@
struct device *dev = &state->pdev->dev;
if (on)
- return pm_runtime_get_sync(dev);
+ return pm_runtime_resume_and_get(dev);
return pm_runtime_put_sync(dev);
}
@@ -509,11 +509,9 @@
if (enable) {
s5pcsis_clear_counters(state);
- ret = pm_runtime_get_sync(&state->pdev->dev);
- if (ret && ret != 1) {
- pm_runtime_put_noidle(&state->pdev->dev);
+ ret = pm_runtime_resume_and_get(&state->pdev->dev);
+ if (ret < 0)
return ret;
- }
}
mutex_lock(&state->lock);
@@ -535,7 +533,7 @@
if (!enable)
pm_runtime_put(&state->pdev->dev);
- return ret == 1 ? 0 : ret;
+ return ret;
}
static int s5pcsis_enum_mbus_code(struct v4l2_subdev *sd,
diff --git a/drivers/media/platform/fsl-viu.c b/drivers/media/platform/fsl-viu.c
index 81a8fae..4f2a0f9 100644
--- a/drivers/media/platform/fsl-viu.c
+++ b/drivers/media/platform/fsl-viu.c
@@ -32,7 +32,7 @@
#define VIU_VERSION "0.5.1"
/* Allow building this driver with COMPILE_TEST */
-#if !defined(CONFIG_PPC) && !defined(CONFIG_MICROBLAZE)
+#if !defined(CONFIG_PPC) && !defined(CONFIG_MICROBLAZE) && !defined(CONFIG_M68K)
#define out_be32(v, a) iowrite32be(a, (void __iomem *)v)
#define in_be32(a) ioread32be((void __iomem *)a)
#endif
@@ -1486,7 +1486,7 @@
mutex_lock(&viu_dev->lock);
- ret = video_register_device(viu_dev->vdev, VFL_TYPE_GRABBER, -1);
+ ret = video_register_device(viu_dev->vdev, VFL_TYPE_VIDEO, -1);
if (ret < 0) {
video_device_release(viu_dev->vdev);
goto err_unlock;
diff --git a/drivers/media/platform/imx-pxp.c b/drivers/media/platform/imx-pxp.c
index 38d9423..62356ad 100644
--- a/drivers/media/platform/imx-pxp.c
+++ b/drivers/media/platform/imx-pxp.c
@@ -1664,6 +1664,8 @@
if (irq < 0)
return irq;
+ spin_lock_init(&dev->irqlock);
+
ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, pxp_irq_handler,
IRQF_ONESHOT, dev_name(&pdev->dev), dev);
if (ret < 0) {
@@ -1681,8 +1683,6 @@
goto err_clk;
}
- spin_lock_init(&dev->irqlock);
-
ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
if (ret)
goto err_clk;
@@ -1709,7 +1709,7 @@
goto err_v4l2;
}
- ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0);
+ ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0);
if (ret) {
v4l2_err(&dev->v4l2_dev, "Failed to register video device\n");
goto err_m2m;
diff --git a/drivers/media/platform/m2m-deinterlace.c b/drivers/media/platform/m2m-deinterlace.c
index 9ad24c8..1f89e71 100644
--- a/drivers/media/platform/m2m-deinterlace.c
+++ b/drivers/media/platform/m2m-deinterlace.c
@@ -953,7 +953,7 @@
vfd->lock = &pcdev->dev_mutex;
vfd->v4l2_dev = &pcdev->v4l2_dev;
- ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0);
+ ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0);
if (ret) {
v4l2_err(&pcdev->v4l2_dev, "Failed to register video device\n");
goto unreg_dev;
diff --git a/drivers/media/platform/marvell-ccic/cafe-driver.c b/drivers/media/platform/marvell-ccic/cafe-driver.c
index 37fdcc5..00f623d 100644
--- a/drivers/media/platform/marvell-ccic/cafe-driver.c
+++ b/drivers/media/platform/marvell-ccic/cafe-driver.c
@@ -497,6 +497,7 @@
cam = kzalloc(sizeof(struct cafe_camera), GFP_KERNEL);
if (cam == NULL)
goto out;
+ pci_set_drvdata(pdev, cam);
cam->pdev = pdev;
mcam = &cam->mcam;
mcam->chip_id = MCAM_CAFE;
@@ -556,7 +557,7 @@
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;
}
@@ -592,8 +593,7 @@
static void cafe_pci_remove(struct pci_dev *pdev)
{
- struct v4l2_device *v4l2_dev = dev_get_drvdata(&pdev->dev);
- struct cafe_camera *cam = to_cam(v4l2_dev);
+ struct cafe_camera *cam = pci_get_drvdata(pdev);
if (cam == NULL) {
printk(KERN_WARNING "pci_remove on unknown pdev %p\n", pdev);
@@ -604,44 +604,26 @@
}
-#ifdef CONFIG_PM
/*
* Basic power management.
*/
-static int cafe_pci_suspend(struct pci_dev *pdev, pm_message_t state)
+static int __maybe_unused cafe_pci_suspend(struct device *dev)
{
- struct v4l2_device *v4l2_dev = dev_get_drvdata(&pdev->dev);
- struct cafe_camera *cam = to_cam(v4l2_dev);
- int ret;
+ struct cafe_camera *cam = dev_get_drvdata(dev);
- ret = pci_save_state(pdev);
- if (ret)
- return ret;
mccic_suspend(&cam->mcam);
- pci_disable_device(pdev);
return 0;
}
-static int cafe_pci_resume(struct pci_dev *pdev)
+static int __maybe_unused cafe_pci_resume(struct device *dev)
{
- struct v4l2_device *v4l2_dev = dev_get_drvdata(&pdev->dev);
- struct cafe_camera *cam = to_cam(v4l2_dev);
- int ret = 0;
+ struct cafe_camera *cam = dev_get_drvdata(dev);
- pci_restore_state(pdev);
- ret = pci_enable_device(pdev);
-
- if (ret) {
- cam_warn(cam, "Unable to re-enable device on resume!\n");
- return ret;
- }
cafe_ctlr_init(&cam->mcam);
return mccic_resume(&cam->mcam);
}
-#endif /* CONFIG_PM */
-
static const struct pci_device_id cafe_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_MARVELL,
PCI_DEVICE_ID_MARVELL_88ALP01_CCIC) },
@@ -650,15 +632,14 @@
MODULE_DEVICE_TABLE(pci, cafe_ids);
+static SIMPLE_DEV_PM_OPS(cafe_pci_pm_ops, cafe_pci_suspend, cafe_pci_resume);
+
static struct pci_driver cafe_pci_driver = {
.name = "cafe1000-ccic",
.id_table = cafe_ids,
.probe = cafe_pci_probe,
.remove = cafe_pci_remove,
-#ifdef CONFIG_PM
- .suspend = cafe_pci_suspend,
- .resume = cafe_pci_resume,
-#endif
+ .driver.pm = &cafe_pci_pm_ops,
};
diff --git a/drivers/media/platform/marvell-ccic/mcam-core.c b/drivers/media/platform/marvell-ccic/mcam-core.c
index 6de8b3d..e56c5e5 100644
--- a/drivers/media/platform/marvell-ccic/mcam-core.c
+++ b/drivers/media/platform/marvell-ccic/mcam-core.c
@@ -24,6 +24,7 @@
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/videodev2.h>
+#include <linux/pm_runtime.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-ctrls.h>
@@ -388,7 +389,7 @@
dma_free_coherent(cam->dev, cam->dma_buf_size,
cam->dma_bufs[0], cam->dma_handles[0]);
cam->nbufs = 0;
- /* fall-through */
+ fallthrough;
case 0:
cam_err(cam, "Insufficient DMA buffers, cannot operate\n");
return -ENOMEM;
@@ -438,9 +439,9 @@
/*
* Copy data out to user space in the vmalloc case
*/
-static void mcam_frame_tasklet(unsigned long data)
+static void mcam_frame_tasklet(struct tasklet_struct *t)
{
- struct mcam_camera *cam = (struct mcam_camera *) data;
+ struct mcam_camera *cam = from_tasklet(cam, t, s_tasklet);
int i;
unsigned long flags;
struct mcam_vb_buffer *buf;
@@ -895,30 +896,6 @@
/* ---------------------------------------------------------------------- */
/*
- * Controller clocks.
- */
-static void mcam_clk_enable(struct mcam_camera *mcam)
-{
- unsigned int i;
-
- for (i = 0; i < NR_MCAM_CLK; i++) {
- if (!IS_ERR(mcam->clk[i]))
- clk_prepare_enable(mcam->clk[i]);
- }
-}
-
-static void mcam_clk_disable(struct mcam_camera *mcam)
-{
- int i;
-
- for (i = NR_MCAM_CLK - 1; i >= 0; i--) {
- if (!IS_ERR(mcam->clk[i]))
- clk_disable_unprepare(mcam->clk[i]);
- }
-}
-
-/* ---------------------------------------------------------------------- */
-/*
* Master sensor clock.
*/
static int mclk_prepare(struct clk_hw *hw)
@@ -941,6 +918,7 @@
struct mcam_camera *cam = container_of(hw, struct mcam_camera, mclk_hw);
int mclk_src;
int mclk_div;
+ int ret;
/*
* Clock the sensor appropriately. Controller clock should
@@ -954,6 +932,9 @@
mclk_div = 2;
}
+ ret = pm_runtime_resume_and_get(cam->dev);
+ if (ret < 0)
+ return ret;
clk_enable(cam->clk[0]);
mcam_reg_write(cam, REG_CLKCTRL, (mclk_src << 29) | mclk_div);
mcam_ctlr_power_up(cam);
@@ -967,6 +948,7 @@
mcam_ctlr_power_down(cam);
clk_disable(cam->clk[0]);
+ pm_runtime_put(cam->dev);
}
static unsigned long mclk_recalc_rate(struct clk_hw *hw,
@@ -1323,8 +1305,7 @@
break;
case B_vmalloc:
#ifdef MCAM_MODE_VMALLOC
- tasklet_init(&cam->s_tasklet, mcam_frame_tasklet,
- (unsigned long) cam);
+ tasklet_setup(&cam->s_tasklet, mcam_frame_tasklet);
vq->ops = &mcam_vb2_ops;
vq->mem_ops = &vb2_vmalloc_memops;
cam->dma_setup = mcam_ctlr_dma_vmalloc;
@@ -1633,7 +1614,9 @@
ret = sensor_call(cam, core, s_power, 1);
if (ret)
goto out;
- mcam_clk_enable(cam);
+ ret = pm_runtime_resume_and_get(cam->dev);
+ if (ret < 0)
+ goto out;
__mcam_cam_reset(cam);
mcam_set_config_needed(cam, 1);
}
@@ -1656,7 +1639,7 @@
if (last_open) {
mcam_disable_mipi(cam);
sensor_call(cam, core, s_power, 0);
- mcam_clk_disable(cam);
+ pm_runtime_put(cam->dev);
if (cam->buffer_mode == B_vmalloc && alloc_bufs_at_read)
mcam_free_dma_bufs(cam);
}
@@ -1802,7 +1785,7 @@
cam->vdev.lock = &cam->s_mutex;
cam->vdev.queue = &cam->vb_queue;
video_set_drvdata(&cam->vdev, cam);
- ret = video_register_device(&cam->vdev, VFL_TYPE_GRABBER, -1);
+ ret = video_register_device(&cam->vdev, VFL_TYPE_VIDEO, -1);
if (ret) {
cam->sensor = NULL;
goto out;
@@ -1969,8 +1952,6 @@
/*
* Power management
*/
-#ifdef CONFIG_PM
-
void mccic_suspend(struct mcam_camera *cam)
{
mutex_lock(&cam->s_mutex);
@@ -1979,7 +1960,6 @@
mcam_ctlr_stop_dma(cam);
sensor_call(cam, core, s_power, 0);
- mcam_clk_disable(cam);
cam->state = cstate;
}
mutex_unlock(&cam->s_mutex);
@@ -1992,7 +1972,6 @@
mutex_lock(&cam->s_mutex);
if (!list_empty(&cam->vdev.fh_list)) {
- mcam_clk_enable(cam);
ret = sensor_call(cam, core, s_power, 1);
if (ret) {
mutex_unlock(&cam->s_mutex);
@@ -2017,7 +1996,6 @@
return ret;
}
EXPORT_SYMBOL_GPL(mccic_resume);
-#endif /* CONFIG_PM */
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Jonathan Corbet <corbet@lwn.net>");
diff --git a/drivers/media/platform/marvell-ccic/mcam-core.h b/drivers/media/platform/marvell-ccic/mcam-core.h
index 2e3a756..b555458 100644
--- a/drivers/media/platform/marvell-ccic/mcam-core.h
+++ b/drivers/media/platform/marvell-ccic/mcam-core.h
@@ -236,10 +236,8 @@
int mccic_register(struct mcam_camera *cam);
int mccic_irq(struct mcam_camera *cam, unsigned int irqs);
void mccic_shutdown(struct mcam_camera *cam);
-#ifdef CONFIG_PM
void mccic_suspend(struct mcam_camera *cam);
int mccic_resume(struct mcam_camera *cam);
-#endif
/*
* Register definitions for the m88alp01 camera interface. Offsets in bytes
diff --git a/drivers/media/platform/marvell-ccic/mmp-driver.c b/drivers/media/platform/marvell-ccic/mmp-driver.c
index 92b9225..63fce1b 100644
--- a/drivers/media/platform/marvell-ccic/mmp-driver.c
+++ b/drivers/media/platform/marvell-ccic/mmp-driver.c
@@ -20,6 +20,7 @@
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
#include <linux/io.h>
#include <linux/list.h>
#include <linux/pm.h>
@@ -47,49 +48,6 @@
}
/*
- * A silly little infrastructure so we can keep track of our devices.
- * Chances are that we will never have more than one of them, but
- * the Armada 610 *does* have two controllers...
- */
-
-static LIST_HEAD(mmpcam_devices);
-static struct mutex mmpcam_devices_lock;
-
-static void mmpcam_add_device(struct mmp_camera *cam)
-{
- mutex_lock(&mmpcam_devices_lock);
- list_add(&cam->devlist, &mmpcam_devices);
- mutex_unlock(&mmpcam_devices_lock);
-}
-
-static void mmpcam_remove_device(struct mmp_camera *cam)
-{
- mutex_lock(&mmpcam_devices_lock);
- list_del(&cam->devlist);
- mutex_unlock(&mmpcam_devices_lock);
-}
-
-/*
- * Platform dev remove passes us a platform_device, and there's
- * no handy unused drvdata to stash a backpointer in. So just
- * dig it out of our list.
- */
-static struct mmp_camera *mmpcam_find_device(struct platform_device *pdev)
-{
- struct mmp_camera *cam;
-
- mutex_lock(&mmpcam_devices_lock);
- list_for_each_entry(cam, &mmpcam_devices, devlist) {
- if (cam->pdev == pdev) {
- mutex_unlock(&mmpcam_devices_lock);
- return cam;
- }
- }
- mutex_unlock(&mmpcam_devices_lock);
- return NULL;
-}
-
-/*
* calc the dphy register values
* There are three dphy registers being used.
* dphy[0] - CSI2_DPHY3
@@ -227,6 +185,7 @@
cam = devm_kzalloc(&pdev->dev, sizeof(*cam), GFP_KERNEL);
if (cam == NULL)
return -ENOMEM;
+ platform_set_drvdata(pdev, cam);
cam->pdev = pdev;
INIT_LIST_HEAD(&cam->devlist);
@@ -313,11 +272,11 @@
cam->irq = res->start;
ret = devm_request_irq(&pdev->dev, cam->irq, mmpcam_irq, IRQF_SHARED,
"mmp-camera", mcam);
- if (ret == 0) {
- mmpcam_add_device(cam);
- return 0;
- }
+ if (ret)
+ goto out;
+ pm_runtime_enable(&pdev->dev);
+ return 0;
out:
fwnode_handle_put(mcam->asd.match.fwnode);
mccic_shutdown(mcam);
@@ -330,14 +289,14 @@
{
struct mcam_camera *mcam = &cam->mcam;
- mmpcam_remove_device(cam);
mccic_shutdown(mcam);
+ pm_runtime_force_suspend(mcam->dev);
return 0;
}
static int mmpcam_platform_remove(struct platform_device *pdev)
{
- struct mmp_camera *cam = mmpcam_find_device(pdev);
+ struct mmp_camera *cam = platform_get_drvdata(pdev);
if (cam == NULL)
return -ENODEV;
@@ -347,27 +306,60 @@
/*
* Suspend/resume support.
*/
+
#ifdef CONFIG_PM
-
-static int mmpcam_suspend(struct platform_device *pdev, pm_message_t state)
+static int mmpcam_runtime_resume(struct device *dev)
{
- struct mmp_camera *cam = mmpcam_find_device(pdev);
+ struct mmp_camera *cam = dev_get_drvdata(dev);
+ struct mcam_camera *mcam = &cam->mcam;
+ unsigned int i;
- if (state.event != PM_EVENT_SUSPEND)
- return 0;
- mccic_suspend(&cam->mcam);
+ for (i = 0; i < NR_MCAM_CLK; i++) {
+ if (!IS_ERR(mcam->clk[i]))
+ clk_prepare_enable(mcam->clk[i]);
+ }
+
return 0;
}
-static int mmpcam_resume(struct platform_device *pdev)
+static int mmpcam_runtime_suspend(struct device *dev)
{
- struct mmp_camera *cam = mmpcam_find_device(pdev);
+ struct mmp_camera *cam = dev_get_drvdata(dev);
+ struct mcam_camera *mcam = &cam->mcam;
+ int i;
- return mccic_resume(&cam->mcam);
+ for (i = NR_MCAM_CLK - 1; i >= 0; i--) {
+ if (!IS_ERR(mcam->clk[i]))
+ clk_disable_unprepare(mcam->clk[i]);
+ }
+
+ return 0;
}
+static int __maybe_unused mmpcam_suspend(struct device *dev)
+{
+ struct mmp_camera *cam = dev_get_drvdata(dev);
+
+ if (!pm_runtime_suspended(dev))
+ mccic_suspend(&cam->mcam);
+ return 0;
+}
+
+static int __maybe_unused mmpcam_resume(struct device *dev)
+{
+ struct mmp_camera *cam = dev_get_drvdata(dev);
+
+ if (!pm_runtime_suspended(dev))
+ return mccic_resume(&cam->mcam);
+ return 0;
+}
#endif
+static const struct dev_pm_ops mmpcam_pm_ops = {
+ SET_RUNTIME_PM_OPS(mmpcam_runtime_suspend, mmpcam_runtime_resume, NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(mmpcam_suspend, mmpcam_resume)
+};
+
static const struct of_device_id mmpcam_of_match[] = {
{ .compatible = "marvell,mmp2-ccic", },
{},
@@ -377,32 +369,11 @@
static struct platform_driver mmpcam_driver = {
.probe = mmpcam_probe,
.remove = mmpcam_platform_remove,
-#ifdef CONFIG_PM
- .suspend = mmpcam_suspend,
- .resume = mmpcam_resume,
-#endif
.driver = {
.name = "mmp-camera",
.of_match_table = of_match_ptr(mmpcam_of_match),
+ .pm = &mmpcam_pm_ops,
}
};
-
-static int __init mmpcam_init_module(void)
-{
- mutex_init(&mmpcam_devices_lock);
- return platform_driver_register(&mmpcam_driver);
-}
-
-static void __exit mmpcam_exit_module(void)
-{
- platform_driver_unregister(&mmpcam_driver);
- /*
- * platform_driver_unregister() should have emptied the list
- */
- if (!list_empty(&mmpcam_devices))
- printk(KERN_ERR "mmp_camera leaving devices behind\n");
-}
-
-module_init(mmpcam_init_module);
-module_exit(mmpcam_exit_module);
+module_platform_driver(mmpcam_driver);
diff --git a/drivers/media/platform/meson/Makefile b/drivers/media/platform/meson/Makefile
deleted file mode 100644
index 6bf728a..0000000
--- 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/meson/ao-cec-g12a.c b/drivers/media/platform/meson/ao-cec-g12a.c
deleted file mode 100644
index 3d8fe85..0000000
--- a/drivers/media/platform/meson/ao-cec-g12a.c
+++ /dev/null
@@ -1,796 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * Driver for Amlogic Meson AO CEC G12A Controller
- *
- * Copyright (C) 2017 Amlogic, Inc. All rights reserved
- * Copyright (C) 2019 BayLibre, SAS
- * Author: Neil Armstrong <narmstrong@baylibre.com>
- */
-
-#include <linux/bitfield.h>
-#include <linux/clk.h>
-#include <linux/device.h>
-#include <linux/io.h>
-#include <linux/delay.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/of.h>
-#include <linux/of_platform.h>
-#include <linux/platform_device.h>
-#include <linux/types.h>
-#include <linux/interrupt.h>
-#include <linux/reset.h>
-#include <linux/slab.h>
-#include <linux/regmap.h>
-#include <media/cec.h>
-#include <media/cec-notifier.h>
-#include <linux/clk-provider.h>
-
-/* CEC Registers */
-
-#define CECB_CLK_CNTL_REG0 0x00
-
-#define CECB_CLK_CNTL_N1 GENMASK(11, 0)
-#define CECB_CLK_CNTL_N2 GENMASK(23, 12)
-#define CECB_CLK_CNTL_DUAL_EN BIT(28)
-#define CECB_CLK_CNTL_OUTPUT_EN BIT(30)
-#define CECB_CLK_CNTL_INPUT_EN BIT(31)
-
-#define CECB_CLK_CNTL_REG1 0x04
-
-#define CECB_CLK_CNTL_M1 GENMASK(11, 0)
-#define CECB_CLK_CNTL_M2 GENMASK(23, 12)
-#define CECB_CLK_CNTL_BYPASS_EN BIT(24)
-
-/*
- * [14:12] Filter_del. For glitch-filtering CEC line, ignore signal
- * change pulse width < filter_del * T(filter_tick) * 3.
- * [9:8] Filter_tick_sel: Select which periodical pulse for
- * glitch-filtering CEC line signal.
- * - 0=Use T(xtal)*3 = 125ns;
- * - 1=Use once-per-1us pulse;
- * - 2=Use once-per-10us pulse;
- * - 3=Use once-per-100us pulse.
- * [3] Sysclk_en. 0=Disable system clock; 1=Enable system clock.
- * [2:1] cntl_clk
- * - 0 = Disable clk (Power-off mode)
- * - 1 = Enable gated clock (Normal mode)
- * - 2 = Enable free-run clk (Debug mode)
- * [0] SW_RESET 1=Apply reset; 0=No reset.
- */
-#define CECB_GEN_CNTL_REG 0x08
-
-#define CECB_GEN_CNTL_RESET BIT(0)
-#define CECB_GEN_CNTL_CLK_DISABLE 0
-#define CECB_GEN_CNTL_CLK_ENABLE 1
-#define CECB_GEN_CNTL_CLK_ENABLE_DBG 2
-#define CECB_GEN_CNTL_CLK_CTRL_MASK GENMASK(2, 1)
-#define CECB_GEN_CNTL_SYS_CLK_EN BIT(3)
-#define CECB_GEN_CNTL_FILTER_TICK_125NS 0
-#define CECB_GEN_CNTL_FILTER_TICK_1US 1
-#define CECB_GEN_CNTL_FILTER_TICK_10US 2
-#define CECB_GEN_CNTL_FILTER_TICK_100US 3
-#define CECB_GEN_CNTL_FILTER_TICK_SEL GENMASK(9, 8)
-#define CECB_GEN_CNTL_FILTER_DEL GENMASK(14, 12)
-
-/*
- * [7:0] cec_reg_addr
- * [15:8] cec_reg_wrdata
- * [16] cec_reg_wr
- * - 0 = Read
- * - 1 = Write
- * [31:24] cec_reg_rddata
- */
-#define CECB_RW_REG 0x0c
-
-#define CECB_RW_ADDR GENMASK(7, 0)
-#define CECB_RW_WR_DATA GENMASK(15, 8)
-#define CECB_RW_WRITE_EN BIT(16)
-#define CECB_RW_BUS_BUSY BIT(23)
-#define CECB_RW_RD_DATA GENMASK(31, 24)
-
-/*
- * [0] DONE Interrupt
- * [1] End Of Message Interrupt
- * [2] Not Acknowlegde Interrupt
- * [3] Arbitration Loss Interrupt
- * [4] Initiator Error Interrupt
- * [5] Follower Error Interrupt
- * [6] Wake-Up Interrupt
- */
-#define CECB_INTR_MASKN_REG 0x10
-#define CECB_INTR_CLR_REG 0x14
-#define CECB_INTR_STAT_REG 0x18
-
-#define CECB_INTR_DONE BIT(0)
-#define CECB_INTR_EOM BIT(1)
-#define CECB_INTR_NACK BIT(2)
-#define CECB_INTR_ARB_LOSS BIT(3)
-#define CECB_INTR_INITIATOR_ERR BIT(4)
-#define CECB_INTR_FOLLOWER_ERR BIT(5)
-#define CECB_INTR_WAKE_UP BIT(6)
-
-/* CEC Commands */
-
-#define CECB_CTRL 0x00
-
-#define CECB_CTRL_SEND BIT(0)
-#define CECB_CTRL_TYPE GENMASK(2, 1)
-#define CECB_CTRL_TYPE_RETRY 0
-#define CECB_CTRL_TYPE_NEW 1
-#define CECB_CTRL_TYPE_NEXT 2
-
-#define CECB_CTRL2 0x01
-
-#define CECB_CTRL2_RISE_DEL_MAX GENMASK(4, 0)
-
-#define CECB_INTR_MASK 0x02
-#define CECB_LADD_LOW 0x05
-#define CECB_LADD_HIGH 0x06
-#define CECB_TX_CNT 0x07
-#define CECB_RX_CNT 0x08
-#define CECB_STAT0 0x09
-#define CECB_TX_DATA00 0x10
-#define CECB_TX_DATA01 0x11
-#define CECB_TX_DATA02 0x12
-#define CECB_TX_DATA03 0x13
-#define CECB_TX_DATA04 0x14
-#define CECB_TX_DATA05 0x15
-#define CECB_TX_DATA06 0x16
-#define CECB_TX_DATA07 0x17
-#define CECB_TX_DATA08 0x18
-#define CECB_TX_DATA09 0x19
-#define CECB_TX_DATA10 0x1A
-#define CECB_TX_DATA11 0x1B
-#define CECB_TX_DATA12 0x1C
-#define CECB_TX_DATA13 0x1D
-#define CECB_TX_DATA14 0x1E
-#define CECB_TX_DATA15 0x1F
-#define CECB_RX_DATA00 0x20
-#define CECB_RX_DATA01 0x21
-#define CECB_RX_DATA02 0x22
-#define CECB_RX_DATA03 0x23
-#define CECB_RX_DATA04 0x24
-#define CECB_RX_DATA05 0x25
-#define CECB_RX_DATA06 0x26
-#define CECB_RX_DATA07 0x27
-#define CECB_RX_DATA08 0x28
-#define CECB_RX_DATA09 0x29
-#define CECB_RX_DATA10 0x2A
-#define CECB_RX_DATA11 0x2B
-#define CECB_RX_DATA12 0x2C
-#define CECB_RX_DATA13 0x2D
-#define CECB_RX_DATA14 0x2E
-#define CECB_RX_DATA15 0x2F
-#define CECB_LOCK_BUF 0x30
-
-#define CECB_LOCK_BUF_EN BIT(0)
-
-#define CECB_WAKEUPCTRL 0x31
-
-struct meson_ao_cec_g12a_data {
- /* Setup the internal CECB_CTRL2 register */
- bool ctrl2_setup;
-};
-
-struct meson_ao_cec_g12a_device {
- struct platform_device *pdev;
- struct regmap *regmap;
- struct regmap *regmap_cec;
- spinlock_t cec_reg_lock;
- struct cec_notifier *notify;
- struct cec_adapter *adap;
- struct cec_msg rx_msg;
- struct clk *oscin;
- struct clk *core;
- const struct meson_ao_cec_g12a_data *data;
-};
-
-static const struct regmap_config meson_ao_cec_g12a_regmap_conf = {
- .reg_bits = 8,
- .val_bits = 32,
- .reg_stride = 4,
- .max_register = CECB_INTR_STAT_REG,
-};
-
-/*
- * The AO-CECB embeds a dual/divider to generate a more precise
- * 32,768KHz clock for CEC core clock.
- * ______ ______
- * | | | |
- * ______ | Div1 |-| Cnt1 | ______
- * | | /|______| |______|\ | |
- * Xtal-->| Gate |---| ______ ______ X-X--| Gate |-->
- * |______| | \| | | |/ | |______|
- * | | Div2 |-| Cnt2 | |
- * | |______| |______| |
- * |_______________________|
- *
- * The dividing can be switched to single or dual, with a counter
- * for each divider to set when the switching is done.
- * The entire dividing mechanism can be also bypassed.
- */
-
-struct meson_ao_cec_g12a_dualdiv_clk {
- struct clk_hw hw;
- struct regmap *regmap;
-};
-
-#define hw_to_meson_ao_cec_g12a_dualdiv_clk(_hw) \
- container_of(_hw, struct meson_ao_cec_g12a_dualdiv_clk, hw) \
-
-static unsigned long
-meson_ao_cec_g12a_dualdiv_clk_recalc_rate(struct clk_hw *hw,
- unsigned long parent_rate)
-{
- struct meson_ao_cec_g12a_dualdiv_clk *dualdiv_clk =
- hw_to_meson_ao_cec_g12a_dualdiv_clk(hw);
- unsigned long n1;
- u32 reg0, reg1;
-
- regmap_read(dualdiv_clk->regmap, CECB_CLK_CNTL_REG0, ®0);
- regmap_read(dualdiv_clk->regmap, CECB_CLK_CNTL_REG0, ®1);
-
- if (reg1 & CECB_CLK_CNTL_BYPASS_EN)
- return parent_rate;
-
- if (reg0 & CECB_CLK_CNTL_DUAL_EN) {
- unsigned long n2, m1, m2, f1, f2, p1, p2;
-
- n1 = FIELD_GET(CECB_CLK_CNTL_N1, reg0) + 1;
- n2 = FIELD_GET(CECB_CLK_CNTL_N2, reg0) + 1;
-
- m1 = FIELD_GET(CECB_CLK_CNTL_M1, reg1) + 1;
- m2 = FIELD_GET(CECB_CLK_CNTL_M1, reg1) + 1;
-
- f1 = DIV_ROUND_CLOSEST(parent_rate, n1);
- f2 = DIV_ROUND_CLOSEST(parent_rate, n2);
-
- p1 = DIV_ROUND_CLOSEST(100000000 * m1, f1 * (m1 + m2));
- p2 = DIV_ROUND_CLOSEST(100000000 * m2, f2 * (m1 + m2));
-
- return DIV_ROUND_UP(100000000, p1 + p2);
- }
-
- n1 = FIELD_GET(CECB_CLK_CNTL_N1, reg0) + 1;
-
- return DIV_ROUND_CLOSEST(parent_rate, n1);
-}
-
-static int meson_ao_cec_g12a_dualdiv_clk_enable(struct clk_hw *hw)
-{
- struct meson_ao_cec_g12a_dualdiv_clk *dualdiv_clk =
- hw_to_meson_ao_cec_g12a_dualdiv_clk(hw);
-
-
- /* Disable Input & Output */
- regmap_update_bits(dualdiv_clk->regmap, CECB_CLK_CNTL_REG0,
- CECB_CLK_CNTL_INPUT_EN | CECB_CLK_CNTL_OUTPUT_EN,
- 0);
-
- /* Set N1 & N2 */
- regmap_update_bits(dualdiv_clk->regmap, CECB_CLK_CNTL_REG0,
- CECB_CLK_CNTL_N1,
- FIELD_PREP(CECB_CLK_CNTL_N1, 733 - 1));
-
- regmap_update_bits(dualdiv_clk->regmap, CECB_CLK_CNTL_REG0,
- CECB_CLK_CNTL_N2,
- FIELD_PREP(CECB_CLK_CNTL_N2, 732 - 1));
-
- /* Set M1 & M2 */
- regmap_update_bits(dualdiv_clk->regmap, CECB_CLK_CNTL_REG1,
- CECB_CLK_CNTL_M1,
- FIELD_PREP(CECB_CLK_CNTL_M1, 8 - 1));
-
- regmap_update_bits(dualdiv_clk->regmap, CECB_CLK_CNTL_REG1,
- CECB_CLK_CNTL_M2,
- FIELD_PREP(CECB_CLK_CNTL_M2, 11 - 1));
-
- /* Enable Dual divisor */
- regmap_update_bits(dualdiv_clk->regmap, CECB_CLK_CNTL_REG0,
- CECB_CLK_CNTL_DUAL_EN, CECB_CLK_CNTL_DUAL_EN);
-
- /* Disable divisor bypass */
- regmap_update_bits(dualdiv_clk->regmap, CECB_CLK_CNTL_REG1,
- CECB_CLK_CNTL_BYPASS_EN, 0);
-
- /* Enable Input & Output */
- regmap_update_bits(dualdiv_clk->regmap, CECB_CLK_CNTL_REG0,
- CECB_CLK_CNTL_INPUT_EN | CECB_CLK_CNTL_OUTPUT_EN,
- CECB_CLK_CNTL_INPUT_EN | CECB_CLK_CNTL_OUTPUT_EN);
-
- return 0;
-}
-
-static void meson_ao_cec_g12a_dualdiv_clk_disable(struct clk_hw *hw)
-{
- struct meson_ao_cec_g12a_dualdiv_clk *dualdiv_clk =
- hw_to_meson_ao_cec_g12a_dualdiv_clk(hw);
-
- regmap_update_bits(dualdiv_clk->regmap, CECB_CLK_CNTL_REG0,
- CECB_CLK_CNTL_INPUT_EN | CECB_CLK_CNTL_OUTPUT_EN,
- 0);
-}
-
-static int meson_ao_cec_g12a_dualdiv_clk_is_enabled(struct clk_hw *hw)
-{
- struct meson_ao_cec_g12a_dualdiv_clk *dualdiv_clk =
- hw_to_meson_ao_cec_g12a_dualdiv_clk(hw);
- int val;
-
- regmap_read(dualdiv_clk->regmap, CECB_CLK_CNTL_REG0, &val);
-
- return !!(val & (CECB_CLK_CNTL_INPUT_EN | CECB_CLK_CNTL_OUTPUT_EN));
-}
-
-static const struct clk_ops meson_ao_cec_g12a_dualdiv_clk_ops = {
- .recalc_rate = meson_ao_cec_g12a_dualdiv_clk_recalc_rate,
- .is_enabled = meson_ao_cec_g12a_dualdiv_clk_is_enabled,
- .enable = meson_ao_cec_g12a_dualdiv_clk_enable,
- .disable = meson_ao_cec_g12a_dualdiv_clk_disable,
-};
-
-static int meson_ao_cec_g12a_setup_clk(struct meson_ao_cec_g12a_device *ao_cec)
-{
- struct meson_ao_cec_g12a_dualdiv_clk *dualdiv_clk;
- struct device *dev = &ao_cec->pdev->dev;
- struct clk_init_data init;
- const char *parent_name;
- struct clk *clk;
- char *name;
-
- dualdiv_clk = devm_kzalloc(dev, sizeof(*dualdiv_clk), GFP_KERNEL);
- if (!dualdiv_clk)
- return -ENOMEM;
-
- name = kasprintf(GFP_KERNEL, "%s#dualdiv_clk", dev_name(dev));
- if (!name)
- return -ENOMEM;
-
- parent_name = __clk_get_name(ao_cec->oscin);
-
- init.name = name;
- init.ops = &meson_ao_cec_g12a_dualdiv_clk_ops;
- init.flags = 0;
- init.parent_names = &parent_name;
- init.num_parents = 1;
- dualdiv_clk->regmap = ao_cec->regmap;
- dualdiv_clk->hw.init = &init;
-
- clk = devm_clk_register(dev, &dualdiv_clk->hw);
- kfree(name);
- if (IS_ERR(clk)) {
- dev_err(dev, "failed to register clock\n");
- return PTR_ERR(clk);
- }
-
- ao_cec->core = clk;
-
- return 0;
-}
-
-static int meson_ao_cec_g12a_read(void *context, unsigned int addr,
- unsigned int *data)
-{
- struct meson_ao_cec_g12a_device *ao_cec = context;
- u32 reg = FIELD_PREP(CECB_RW_ADDR, addr);
- int ret = 0;
-
- ret = regmap_write(ao_cec->regmap, CECB_RW_REG, reg);
- if (ret)
- return ret;
-
- ret = regmap_read_poll_timeout(ao_cec->regmap, CECB_RW_REG, reg,
- !(reg & CECB_RW_BUS_BUSY),
- 5, 1000);
- if (ret)
- return ret;
-
- ret = regmap_read(ao_cec->regmap, CECB_RW_REG, ®);
-
- *data = FIELD_GET(CECB_RW_RD_DATA, reg);
-
- return ret;
-}
-
-static int meson_ao_cec_g12a_write(void *context, unsigned int addr,
- unsigned int data)
-{
- struct meson_ao_cec_g12a_device *ao_cec = context;
- u32 reg = FIELD_PREP(CECB_RW_ADDR, addr) |
- FIELD_PREP(CECB_RW_WR_DATA, data) |
- CECB_RW_WRITE_EN;
-
- return regmap_write(ao_cec->regmap, CECB_RW_REG, reg);
-}
-
-static const struct regmap_config meson_ao_cec_g12a_cec_regmap_conf = {
- .reg_bits = 8,
- .val_bits = 8,
- .reg_read = meson_ao_cec_g12a_read,
- .reg_write = meson_ao_cec_g12a_write,
- .max_register = 0xffff,
-};
-
-static inline void
-meson_ao_cec_g12a_irq_setup(struct meson_ao_cec_g12a_device *ao_cec,
- bool enable)
-{
- u32 cfg = CECB_INTR_DONE | CECB_INTR_EOM | CECB_INTR_NACK |
- CECB_INTR_ARB_LOSS | CECB_INTR_INITIATOR_ERR |
- CECB_INTR_FOLLOWER_ERR;
-
- regmap_write(ao_cec->regmap, CECB_INTR_MASKN_REG,
- enable ? cfg : 0);
-}
-
-static void meson_ao_cec_g12a_irq_rx(struct meson_ao_cec_g12a_device *ao_cec)
-{
- int i, ret = 0;
- u32 val;
-
- ret = regmap_read(ao_cec->regmap_cec, CECB_RX_CNT, &val);
-
- ao_cec->rx_msg.len = val;
- if (ao_cec->rx_msg.len > CEC_MAX_MSG_SIZE)
- ao_cec->rx_msg.len = CEC_MAX_MSG_SIZE;
-
- for (i = 0; i < ao_cec->rx_msg.len; i++) {
- ret |= regmap_read(ao_cec->regmap_cec,
- CECB_RX_DATA00 + i, &val);
-
- ao_cec->rx_msg.msg[i] = val & 0xff;
- }
-
- ret |= regmap_write(ao_cec->regmap_cec, CECB_LOCK_BUF, 0);
- if (ret)
- return;
-
- cec_received_msg(ao_cec->adap, &ao_cec->rx_msg);
-}
-
-static irqreturn_t meson_ao_cec_g12a_irq(int irq, void *data)
-{
- struct meson_ao_cec_g12a_device *ao_cec = data;
- u32 stat;
-
- regmap_read(ao_cec->regmap, CECB_INTR_STAT_REG, &stat);
- if (stat)
- return IRQ_WAKE_THREAD;
-
- return IRQ_NONE;
-}
-
-static irqreturn_t meson_ao_cec_g12a_irq_thread(int irq, void *data)
-{
- struct meson_ao_cec_g12a_device *ao_cec = data;
- u32 stat;
-
- regmap_read(ao_cec->regmap, CECB_INTR_STAT_REG, &stat);
- regmap_write(ao_cec->regmap, CECB_INTR_CLR_REG, stat);
-
- if (stat & CECB_INTR_DONE)
- cec_transmit_attempt_done(ao_cec->adap, CEC_TX_STATUS_OK);
-
- if (stat & CECB_INTR_EOM)
- meson_ao_cec_g12a_irq_rx(ao_cec);
-
- if (stat & CECB_INTR_NACK)
- cec_transmit_attempt_done(ao_cec->adap, CEC_TX_STATUS_NACK);
-
- if (stat & CECB_INTR_ARB_LOSS) {
- regmap_write(ao_cec->regmap_cec, CECB_TX_CNT, 0);
- regmap_update_bits(ao_cec->regmap_cec, CECB_CTRL,
- CECB_CTRL_SEND | CECB_CTRL_TYPE, 0);
- cec_transmit_attempt_done(ao_cec->adap, CEC_TX_STATUS_ARB_LOST);
- }
-
- /* Initiator reports an error on the CEC bus */
- if (stat & CECB_INTR_INITIATOR_ERR)
- cec_transmit_attempt_done(ao_cec->adap, CEC_TX_STATUS_ERROR);
-
- /* Follower reports a receive error, just reset RX buffer */
- if (stat & CECB_INTR_FOLLOWER_ERR)
- regmap_write(ao_cec->regmap_cec, CECB_LOCK_BUF, 0);
-
- return IRQ_HANDLED;
-}
-
-static int
-meson_ao_cec_g12a_set_log_addr(struct cec_adapter *adap, u8 logical_addr)
-{
- struct meson_ao_cec_g12a_device *ao_cec = adap->priv;
- int ret = 0;
-
- if (logical_addr == CEC_LOG_ADDR_INVALID) {
- /* Assume this will allways succeed */
- regmap_write(ao_cec->regmap_cec, CECB_LADD_LOW, 0);
- regmap_write(ao_cec->regmap_cec, CECB_LADD_HIGH, 0);
-
- return 0;
- } else if (logical_addr < 8) {
- ret = regmap_update_bits(ao_cec->regmap_cec, CECB_LADD_LOW,
- BIT(logical_addr),
- BIT(logical_addr));
- } else {
- ret = regmap_update_bits(ao_cec->regmap_cec, CECB_LADD_HIGH,
- BIT(logical_addr - 8),
- BIT(logical_addr - 8));
- }
-
- /* Always set Broadcast/Unregistered 15 address */
- ret |= regmap_update_bits(ao_cec->regmap_cec, CECB_LADD_HIGH,
- BIT(CEC_LOG_ADDR_UNREGISTERED - 8),
- BIT(CEC_LOG_ADDR_UNREGISTERED - 8));
-
- return ret ? -EIO : 0;
-}
-
-static int meson_ao_cec_g12a_transmit(struct cec_adapter *adap, u8 attempts,
- u32 signal_free_time, struct cec_msg *msg)
-{
- struct meson_ao_cec_g12a_device *ao_cec = adap->priv;
- unsigned int type;
- int ret = 0;
- u32 val;
- int i;
-
- /* Check if RX is in progress */
- ret = regmap_read(ao_cec->regmap_cec, CECB_LOCK_BUF, &val);
- if (ret)
- return ret;
- if (val & CECB_LOCK_BUF_EN)
- return -EBUSY;
-
- /* Check if TX Busy */
- ret = regmap_read(ao_cec->regmap_cec, CECB_CTRL, &val);
- if (ret)
- return ret;
- if (val & CECB_CTRL_SEND)
- return -EBUSY;
-
- switch (signal_free_time) {
- case CEC_SIGNAL_FREE_TIME_RETRY:
- type = CECB_CTRL_TYPE_RETRY;
- break;
- case CEC_SIGNAL_FREE_TIME_NEXT_XFER:
- type = CECB_CTRL_TYPE_NEXT;
- break;
- case CEC_SIGNAL_FREE_TIME_NEW_INITIATOR:
- default:
- type = CECB_CTRL_TYPE_NEW;
- break;
- }
-
- for (i = 0; i < msg->len; i++)
- ret |= regmap_write(ao_cec->regmap_cec, CECB_TX_DATA00 + i,
- msg->msg[i]);
-
- ret |= regmap_write(ao_cec->regmap_cec, CECB_TX_CNT, msg->len);
- if (ret)
- return -EIO;
-
- ret = regmap_update_bits(ao_cec->regmap_cec, CECB_CTRL,
- CECB_CTRL_SEND |
- CECB_CTRL_TYPE,
- CECB_CTRL_SEND |
- FIELD_PREP(CECB_CTRL_TYPE, type));
-
- return ret;
-}
-
-static int meson_ao_cec_g12a_adap_enable(struct cec_adapter *adap, bool enable)
-{
- struct meson_ao_cec_g12a_device *ao_cec = adap->priv;
-
- meson_ao_cec_g12a_irq_setup(ao_cec, false);
-
- regmap_update_bits(ao_cec->regmap, CECB_GEN_CNTL_REG,
- CECB_GEN_CNTL_RESET, CECB_GEN_CNTL_RESET);
-
- if (!enable)
- return 0;
-
- /* Setup Filter */
- regmap_update_bits(ao_cec->regmap, CECB_GEN_CNTL_REG,
- CECB_GEN_CNTL_FILTER_TICK_SEL |
- CECB_GEN_CNTL_FILTER_DEL,
- FIELD_PREP(CECB_GEN_CNTL_FILTER_TICK_SEL,
- CECB_GEN_CNTL_FILTER_TICK_1US) |
- FIELD_PREP(CECB_GEN_CNTL_FILTER_DEL, 7));
-
- /* Enable System Clock */
- regmap_update_bits(ao_cec->regmap, CECB_GEN_CNTL_REG,
- CECB_GEN_CNTL_SYS_CLK_EN,
- CECB_GEN_CNTL_SYS_CLK_EN);
-
- /* Enable gated clock (Normal mode). */
- regmap_update_bits(ao_cec->regmap, CECB_GEN_CNTL_REG,
- CECB_GEN_CNTL_CLK_CTRL_MASK,
- FIELD_PREP(CECB_GEN_CNTL_CLK_CTRL_MASK,
- CECB_GEN_CNTL_CLK_ENABLE));
-
- /* Release Reset */
- regmap_update_bits(ao_cec->regmap, CECB_GEN_CNTL_REG,
- CECB_GEN_CNTL_RESET, 0);
-
- if (ao_cec->data->ctrl2_setup)
- regmap_write(ao_cec->regmap_cec, CECB_CTRL2,
- FIELD_PREP(CECB_CTRL2_RISE_DEL_MAX, 2));
-
- meson_ao_cec_g12a_irq_setup(ao_cec, true);
-
- return 0;
-}
-
-static const struct cec_adap_ops meson_ao_cec_g12a_ops = {
- .adap_enable = meson_ao_cec_g12a_adap_enable,
- .adap_log_addr = meson_ao_cec_g12a_set_log_addr,
- .adap_transmit = meson_ao_cec_g12a_transmit,
-};
-
-static int meson_ao_cec_g12a_probe(struct platform_device *pdev)
-{
- struct meson_ao_cec_g12a_device *ao_cec;
- struct device *hdmi_dev;
- struct resource *res;
- void __iomem *base;
- int ret, irq;
-
- hdmi_dev = cec_notifier_parse_hdmi_phandle(&pdev->dev);
- if (IS_ERR(hdmi_dev))
- return PTR_ERR(hdmi_dev);
-
- ao_cec = devm_kzalloc(&pdev->dev, sizeof(*ao_cec), GFP_KERNEL);
- if (!ao_cec)
- return -ENOMEM;
-
- ao_cec->data = of_device_get_match_data(&pdev->dev);
- if (!ao_cec->data) {
- dev_err(&pdev->dev, "failed to get match data\n");
- return -ENODEV;
- }
-
- spin_lock_init(&ao_cec->cec_reg_lock);
- ao_cec->pdev = pdev;
-
- ao_cec->adap = cec_allocate_adapter(&meson_ao_cec_g12a_ops, ao_cec,
- "meson_g12a_ao_cec",
- CEC_CAP_DEFAULTS |
- CEC_CAP_CONNECTOR_INFO,
- CEC_MAX_LOG_ADDRS);
- if (IS_ERR(ao_cec->adap))
- return PTR_ERR(ao_cec->adap);
-
- ao_cec->adap->owner = THIS_MODULE;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- base = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(base)) {
- ret = PTR_ERR(base);
- goto out_probe_adapter;
- }
-
- ao_cec->regmap = devm_regmap_init_mmio(&pdev->dev, base,
- &meson_ao_cec_g12a_regmap_conf);
- if (IS_ERR(ao_cec->regmap)) {
- ret = PTR_ERR(ao_cec->regmap);
- goto out_probe_adapter;
- }
-
- ao_cec->regmap_cec = devm_regmap_init(&pdev->dev, NULL, ao_cec,
- &meson_ao_cec_g12a_cec_regmap_conf);
- if (IS_ERR(ao_cec->regmap_cec)) {
- ret = PTR_ERR(ao_cec->regmap_cec);
- goto out_probe_adapter;
- }
-
- irq = platform_get_irq(pdev, 0);
- ret = devm_request_threaded_irq(&pdev->dev, irq,
- meson_ao_cec_g12a_irq,
- meson_ao_cec_g12a_irq_thread,
- 0, NULL, ao_cec);
- if (ret) {
- dev_err(&pdev->dev, "irq request failed\n");
- goto out_probe_adapter;
- }
-
- ao_cec->oscin = devm_clk_get(&pdev->dev, "oscin");
- if (IS_ERR(ao_cec->oscin)) {
- dev_err(&pdev->dev, "oscin clock request failed\n");
- ret = PTR_ERR(ao_cec->oscin);
- goto out_probe_adapter;
- }
-
- ret = meson_ao_cec_g12a_setup_clk(ao_cec);
- if (ret)
- goto out_probe_adapter;
-
- ret = clk_prepare_enable(ao_cec->core);
- if (ret) {
- dev_err(&pdev->dev, "core clock enable failed\n");
- goto out_probe_adapter;
- }
-
- device_reset_optional(&pdev->dev);
-
- platform_set_drvdata(pdev, ao_cec);
-
- ao_cec->notify = cec_notifier_cec_adap_register(hdmi_dev, NULL,
- ao_cec->adap);
- if (!ao_cec->notify) {
- ret = -ENOMEM;
- goto out_probe_core_clk;
- }
-
- ret = cec_register_adapter(ao_cec->adap, &pdev->dev);
- if (ret < 0)
- goto out_probe_notify;
-
- /* Setup Hardware */
- regmap_write(ao_cec->regmap, CECB_GEN_CNTL_REG, CECB_GEN_CNTL_RESET);
-
- return 0;
-
-out_probe_notify:
- cec_notifier_cec_adap_unregister(ao_cec->notify);
-
-out_probe_core_clk:
- clk_disable_unprepare(ao_cec->core);
-
-out_probe_adapter:
- cec_delete_adapter(ao_cec->adap);
-
- dev_err(&pdev->dev, "CEC controller registration failed\n");
-
- return ret;
-}
-
-static int meson_ao_cec_g12a_remove(struct platform_device *pdev)
-{
- struct meson_ao_cec_g12a_device *ao_cec = platform_get_drvdata(pdev);
-
- clk_disable_unprepare(ao_cec->core);
-
- cec_notifier_cec_adap_unregister(ao_cec->notify);
-
- cec_unregister_adapter(ao_cec->adap);
-
- return 0;
-}
-
-static const struct meson_ao_cec_g12a_data ao_cec_g12a_data = {
- .ctrl2_setup = false,
-};
-
-static const struct meson_ao_cec_g12a_data ao_cec_sm1_data = {
- .ctrl2_setup = true,
-};
-
-static const struct of_device_id meson_ao_cec_g12a_of_match[] = {
- {
- .compatible = "amlogic,meson-g12a-ao-cec",
- .data = &ao_cec_g12a_data,
- },
- {
- .compatible = "amlogic,meson-sm1-ao-cec",
- .data = &ao_cec_sm1_data,
- },
- { /* sentinel */ }
-};
-MODULE_DEVICE_TABLE(of, meson_ao_cec_g12a_of_match);
-
-static struct platform_driver meson_ao_cec_g12a_driver = {
- .probe = meson_ao_cec_g12a_probe,
- .remove = meson_ao_cec_g12a_remove,
- .driver = {
- .name = "meson-ao-cec-g12a",
- .of_match_table = of_match_ptr(meson_ao_cec_g12a_of_match),
- },
-};
-
-module_platform_driver(meson_ao_cec_g12a_driver);
-
-MODULE_DESCRIPTION("Meson AO CEC G12A Controller driver");
-MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/platform/meson/ao-cec.c b/drivers/media/platform/meson/ao-cec.c
deleted file mode 100644
index 03600e8..0000000
--- a/drivers/media/platform/meson/ao-cec.c
+++ /dev/null
@@ -1,732 +0,0 @@
-/*
- * Driver for Amlogic Meson AO CEC Controller
- *
- * Copyright (C) 2015 Amlogic, Inc. All rights reserved
- * Copyright (C) 2017 BayLibre, SAS
- * Author: Neil Armstrong <narmstrong@baylibre.com>
- *
- * SPDX-License-Identifier: GPL-2.0+
- */
-
-#include <linux/bitfield.h>
-#include <linux/clk.h>
-#include <linux/device.h>
-#include <linux/io.h>
-#include <linux/delay.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/of.h>
-#include <linux/of_platform.h>
-#include <linux/platform_device.h>
-#include <linux/types.h>
-#include <linux/interrupt.h>
-#include <linux/reset.h>
-#include <media/cec.h>
-#include <media/cec-notifier.h>
-
-/* CEC Registers */
-
-/*
- * [2:1] cntl_clk
- * - 0 = Disable clk (Power-off mode)
- * - 1 = Enable gated clock (Normal mode)
- * - 2 = Enable free-run clk (Debug mode)
- */
-#define CEC_GEN_CNTL_REG 0x00
-
-#define CEC_GEN_CNTL_RESET BIT(0)
-#define CEC_GEN_CNTL_CLK_DISABLE 0
-#define CEC_GEN_CNTL_CLK_ENABLE 1
-#define CEC_GEN_CNTL_CLK_ENABLE_DBG 2
-#define CEC_GEN_CNTL_CLK_CTRL_MASK GENMASK(2, 1)
-
-/*
- * [7:0] cec_reg_addr
- * [15:8] cec_reg_wrdata
- * [16] cec_reg_wr
- * - 0 = Read
- * - 1 = Write
- * [23] bus free
- * [31:24] cec_reg_rddata
- */
-#define CEC_RW_REG 0x04
-
-#define CEC_RW_ADDR GENMASK(7, 0)
-#define CEC_RW_WR_DATA GENMASK(15, 8)
-#define CEC_RW_WRITE_EN BIT(16)
-#define CEC_RW_BUS_BUSY BIT(23)
-#define CEC_RW_RD_DATA GENMASK(31, 24)
-
-/*
- * [1] tx intr
- * [2] rx intr
- */
-#define CEC_INTR_MASKN_REG 0x08
-#define CEC_INTR_CLR_REG 0x0c
-#define CEC_INTR_STAT_REG 0x10
-
-#define CEC_INTR_TX BIT(1)
-#define CEC_INTR_RX BIT(2)
-
-/* CEC Commands */
-
-#define CEC_TX_MSG_0_HEADER 0x00
-#define CEC_TX_MSG_1_OPCODE 0x01
-#define CEC_TX_MSG_2_OP1 0x02
-#define CEC_TX_MSG_3_OP2 0x03
-#define CEC_TX_MSG_4_OP3 0x04
-#define CEC_TX_MSG_5_OP4 0x05
-#define CEC_TX_MSG_6_OP5 0x06
-#define CEC_TX_MSG_7_OP6 0x07
-#define CEC_TX_MSG_8_OP7 0x08
-#define CEC_TX_MSG_9_OP8 0x09
-#define CEC_TX_MSG_A_OP9 0x0A
-#define CEC_TX_MSG_B_OP10 0x0B
-#define CEC_TX_MSG_C_OP11 0x0C
-#define CEC_TX_MSG_D_OP12 0x0D
-#define CEC_TX_MSG_E_OP13 0x0E
-#define CEC_TX_MSG_F_OP14 0x0F
-#define CEC_TX_MSG_LENGTH 0x10
-#define CEC_TX_MSG_CMD 0x11
-#define CEC_TX_WRITE_BUF 0x12
-#define CEC_TX_CLEAR_BUF 0x13
-#define CEC_RX_MSG_CMD 0x14
-#define CEC_RX_CLEAR_BUF 0x15
-#define CEC_LOGICAL_ADDR0 0x16
-#define CEC_LOGICAL_ADDR1 0x17
-#define CEC_LOGICAL_ADDR2 0x18
-#define CEC_LOGICAL_ADDR3 0x19
-#define CEC_LOGICAL_ADDR4 0x1A
-#define CEC_CLOCK_DIV_H 0x1B
-#define CEC_CLOCK_DIV_L 0x1C
-#define CEC_QUIESCENT_25MS_BIT7_0 0x20
-#define CEC_QUIESCENT_25MS_BIT11_8 0x21
-#define CEC_STARTBITMINL2H_3MS5_BIT7_0 0x22
-#define CEC_STARTBITMINL2H_3MS5_BIT8 0x23
-#define CEC_STARTBITMAXL2H_3MS9_BIT7_0 0x24
-#define CEC_STARTBITMAXL2H_3MS9_BIT8 0x25
-#define CEC_STARTBITMINH_0MS6_BIT7_0 0x26
-#define CEC_STARTBITMINH_0MS6_BIT8 0x27
-#define CEC_STARTBITMAXH_1MS0_BIT7_0 0x28
-#define CEC_STARTBITMAXH_1MS0_BIT8 0x29
-#define CEC_STARTBITMINTOT_4MS3_BIT7_0 0x2A
-#define CEC_STARTBITMINTOT_4MS3_BIT9_8 0x2B
-#define CEC_STARTBITMAXTOT_4MS7_BIT7_0 0x2C
-#define CEC_STARTBITMAXTOT_4MS7_BIT9_8 0x2D
-#define CEC_LOGIC1MINL2H_0MS4_BIT7_0 0x2E
-#define CEC_LOGIC1MINL2H_0MS4_BIT8 0x2F
-#define CEC_LOGIC1MAXL2H_0MS8_BIT7_0 0x30
-#define CEC_LOGIC1MAXL2H_0MS8_BIT8 0x31
-#define CEC_LOGIC0MINL2H_1MS3_BIT7_0 0x32
-#define CEC_LOGIC0MINL2H_1MS3_BIT8 0x33
-#define CEC_LOGIC0MAXL2H_1MS7_BIT7_0 0x34
-#define CEC_LOGIC0MAXL2H_1MS7_BIT8 0x35
-#define CEC_LOGICMINTOTAL_2MS05_BIT7_0 0x36
-#define CEC_LOGICMINTOTAL_2MS05_BIT9_8 0x37
-#define CEC_LOGICMAXHIGH_2MS8_BIT7_0 0x38
-#define CEC_LOGICMAXHIGH_2MS8_BIT8 0x39
-#define CEC_LOGICERRLOW_3MS4_BIT7_0 0x3A
-#define CEC_LOGICERRLOW_3MS4_BIT8 0x3B
-#define CEC_NOMSMPPOINT_1MS05 0x3C
-#define CEC_DELCNTR_LOGICERR 0x3E
-#define CEC_TXTIME_17MS_BIT7_0 0x40
-#define CEC_TXTIME_17MS_BIT10_8 0x41
-#define CEC_TXTIME_2BIT_BIT7_0 0x42
-#define CEC_TXTIME_2BIT_BIT10_8 0x43
-#define CEC_TXTIME_4BIT_BIT7_0 0x44
-#define CEC_TXTIME_4BIT_BIT10_8 0x45
-#define CEC_STARTBITNOML2H_3MS7_BIT7_0 0x46
-#define CEC_STARTBITNOML2H_3MS7_BIT8 0x47
-#define CEC_STARTBITNOMH_0MS8_BIT7_0 0x48
-#define CEC_STARTBITNOMH_0MS8_BIT8 0x49
-#define CEC_LOGIC1NOML2H_0MS6_BIT7_0 0x4A
-#define CEC_LOGIC1NOML2H_0MS6_BIT8 0x4B
-#define CEC_LOGIC0NOML2H_1MS5_BIT7_0 0x4C
-#define CEC_LOGIC0NOML2H_1MS5_BIT8 0x4D
-#define CEC_LOGIC1NOMH_1MS8_BIT7_0 0x4E
-#define CEC_LOGIC1NOMH_1MS8_BIT8 0x4F
-#define CEC_LOGIC0NOMH_0MS9_BIT7_0 0x50
-#define CEC_LOGIC0NOMH_0MS9_BIT8 0x51
-#define CEC_LOGICERRLOW_3MS6_BIT7_0 0x52
-#define CEC_LOGICERRLOW_3MS6_BIT8 0x53
-#define CEC_CHKCONTENTION_0MS1 0x54
-#define CEC_PREPARENXTBIT_0MS05_BIT7_0 0x56
-#define CEC_PREPARENXTBIT_0MS05_BIT8 0x57
-#define CEC_NOMSMPACKPOINT_0MS45 0x58
-#define CEC_ACK0NOML2H_1MS5_BIT7_0 0x5A
-#define CEC_ACK0NOML2H_1MS5_BIT8 0x5B
-#define CEC_BUGFIX_DISABLE_0 0x60
-#define CEC_BUGFIX_DISABLE_1 0x61
-#define CEC_RX_MSG_0_HEADER 0x80
-#define CEC_RX_MSG_1_OPCODE 0x81
-#define CEC_RX_MSG_2_OP1 0x82
-#define CEC_RX_MSG_3_OP2 0x83
-#define CEC_RX_MSG_4_OP3 0x84
-#define CEC_RX_MSG_5_OP4 0x85
-#define CEC_RX_MSG_6_OP5 0x86
-#define CEC_RX_MSG_7_OP6 0x87
-#define CEC_RX_MSG_8_OP7 0x88
-#define CEC_RX_MSG_9_OP8 0x89
-#define CEC_RX_MSG_A_OP9 0x8A
-#define CEC_RX_MSG_B_OP10 0x8B
-#define CEC_RX_MSG_C_OP11 0x8C
-#define CEC_RX_MSG_D_OP12 0x8D
-#define CEC_RX_MSG_E_OP13 0x8E
-#define CEC_RX_MSG_F_OP14 0x8F
-#define CEC_RX_MSG_LENGTH 0x90
-#define CEC_RX_MSG_STATUS 0x91
-#define CEC_RX_NUM_MSG 0x92
-#define CEC_TX_MSG_STATUS 0x93
-#define CEC_TX_NUM_MSG 0x94
-
-
-/* CEC_TX_MSG_CMD definition */
-#define TX_NO_OP 0 /* No transaction */
-#define TX_REQ_CURRENT 1 /* Transmit earliest message in buffer */
-#define TX_ABORT 2 /* Abort transmitting earliest message */
-#define TX_REQ_NEXT 3 /* Overwrite earliest msg, transmit next */
-
-/* tx_msg_status definition */
-#define TX_IDLE 0 /* No transaction */
-#define TX_BUSY 1 /* Transmitter is busy */
-#define TX_DONE 2 /* Message successfully transmitted */
-#define TX_ERROR 3 /* Message transmitted with error */
-
-/* rx_msg_cmd */
-#define RX_NO_OP 0 /* No transaction */
-#define RX_ACK_CURRENT 1 /* Read earliest message in buffer */
-#define RX_DISABLE 2 /* Disable receiving latest message */
-#define RX_ACK_NEXT 3 /* Clear earliest msg, read next */
-
-/* rx_msg_status */
-#define RX_IDLE 0 /* No transaction */
-#define RX_BUSY 1 /* Receiver is busy */
-#define RX_DONE 2 /* Message has been received successfully */
-#define RX_ERROR 3 /* Message has been received with error */
-
-/* RX_CLEAR_BUF options */
-#define CLEAR_START 1
-#define CLEAR_STOP 0
-
-/* CEC_LOGICAL_ADDRx options */
-#define LOGICAL_ADDR_MASK 0xf
-#define LOGICAL_ADDR_VALID BIT(4)
-#define LOGICAL_ADDR_DISABLE 0
-
-#define CEC_CLK_RATE 32768
-
-struct meson_ao_cec_device {
- struct platform_device *pdev;
- void __iomem *base;
- struct clk *core;
- spinlock_t cec_reg_lock;
- struct cec_notifier *notify;
- struct cec_adapter *adap;
- struct cec_msg rx_msg;
-};
-
-#define writel_bits_relaxed(mask, val, addr) \
- writel_relaxed((readl_relaxed(addr) & ~(mask)) | (val), addr)
-
-static inline int meson_ao_cec_wait_busy(struct meson_ao_cec_device *ao_cec)
-{
- ktime_t timeout = ktime_add_us(ktime_get(), 5000);
-
- while (readl_relaxed(ao_cec->base + CEC_RW_REG) & CEC_RW_BUS_BUSY) {
- if (ktime_compare(ktime_get(), timeout) > 0)
- return -ETIMEDOUT;
- }
-
- return 0;
-}
-
-static void meson_ao_cec_read(struct meson_ao_cec_device *ao_cec,
- unsigned long address, u8 *data,
- int *res)
-{
- unsigned long flags;
- u32 reg = FIELD_PREP(CEC_RW_ADDR, address);
- int ret = 0;
-
- if (res && *res)
- return;
-
- spin_lock_irqsave(&ao_cec->cec_reg_lock, flags);
-
- ret = meson_ao_cec_wait_busy(ao_cec);
- if (ret)
- goto read_out;
-
- writel_relaxed(reg, ao_cec->base + CEC_RW_REG);
-
- ret = meson_ao_cec_wait_busy(ao_cec);
- if (ret)
- goto read_out;
-
- *data = FIELD_GET(CEC_RW_RD_DATA,
- readl_relaxed(ao_cec->base + CEC_RW_REG));
-
-read_out:
- spin_unlock_irqrestore(&ao_cec->cec_reg_lock, flags);
-
- if (res)
- *res = ret;
-}
-
-static void meson_ao_cec_write(struct meson_ao_cec_device *ao_cec,
- unsigned long address, u8 data,
- int *res)
-{
- unsigned long flags;
- u32 reg = FIELD_PREP(CEC_RW_ADDR, address) |
- FIELD_PREP(CEC_RW_WR_DATA, data) |
- CEC_RW_WRITE_EN;
- int ret = 0;
-
- if (res && *res)
- return;
-
- spin_lock_irqsave(&ao_cec->cec_reg_lock, flags);
-
- ret = meson_ao_cec_wait_busy(ao_cec);
- if (ret)
- goto write_out;
-
- writel_relaxed(reg, ao_cec->base + CEC_RW_REG);
-
-write_out:
- spin_unlock_irqrestore(&ao_cec->cec_reg_lock, flags);
-
- if (res)
- *res = ret;
-}
-
-static inline void meson_ao_cec_irq_setup(struct meson_ao_cec_device *ao_cec,
- bool enable)
-{
- u32 cfg = CEC_INTR_TX | CEC_INTR_RX;
-
- writel_bits_relaxed(cfg, enable ? cfg : 0,
- ao_cec->base + CEC_INTR_MASKN_REG);
-}
-
-static inline int meson_ao_cec_clear(struct meson_ao_cec_device *ao_cec)
-{
- int ret = 0;
-
- meson_ao_cec_write(ao_cec, CEC_RX_MSG_CMD, RX_DISABLE, &ret);
- meson_ao_cec_write(ao_cec, CEC_TX_MSG_CMD, TX_ABORT, &ret);
- meson_ao_cec_write(ao_cec, CEC_RX_CLEAR_BUF, 1, &ret);
- meson_ao_cec_write(ao_cec, CEC_TX_CLEAR_BUF, 1, &ret);
- if (ret)
- return ret;
-
- udelay(100);
-
- meson_ao_cec_write(ao_cec, CEC_RX_CLEAR_BUF, 0, &ret);
- meson_ao_cec_write(ao_cec, CEC_TX_CLEAR_BUF, 0, &ret);
- if (ret)
- return ret;
-
- udelay(100);
-
- meson_ao_cec_write(ao_cec, CEC_RX_MSG_CMD, RX_NO_OP, &ret);
- meson_ao_cec_write(ao_cec, CEC_TX_MSG_CMD, TX_NO_OP, &ret);
-
- return ret;
-}
-
-static int meson_ao_cec_arbit_bit_time_set(struct meson_ao_cec_device *ao_cec,
- unsigned int bit_set,
- unsigned int time_set)
-{
- int ret = 0;
-
- switch (bit_set) {
- case CEC_SIGNAL_FREE_TIME_RETRY:
- meson_ao_cec_write(ao_cec, CEC_TXTIME_4BIT_BIT7_0,
- time_set & 0xff, &ret);
- meson_ao_cec_write(ao_cec, CEC_TXTIME_4BIT_BIT10_8,
- (time_set >> 8) & 0x7, &ret);
- break;
-
- case CEC_SIGNAL_FREE_TIME_NEW_INITIATOR:
- meson_ao_cec_write(ao_cec, CEC_TXTIME_2BIT_BIT7_0,
- time_set & 0xff, &ret);
- meson_ao_cec_write(ao_cec, CEC_TXTIME_2BIT_BIT10_8,
- (time_set >> 8) & 0x7, &ret);
- break;
-
- case CEC_SIGNAL_FREE_TIME_NEXT_XFER:
- meson_ao_cec_write(ao_cec, CEC_TXTIME_17MS_BIT7_0,
- time_set & 0xff, &ret);
- meson_ao_cec_write(ao_cec, CEC_TXTIME_17MS_BIT10_8,
- (time_set >> 8) & 0x7, &ret);
- break;
- }
-
- return ret;
-}
-
-static irqreturn_t meson_ao_cec_irq(int irq, void *data)
-{
- struct meson_ao_cec_device *ao_cec = data;
- u32 stat = readl_relaxed(ao_cec->base + CEC_INTR_STAT_REG);
-
- if (stat)
- return IRQ_WAKE_THREAD;
-
- return IRQ_NONE;
-}
-
-static void meson_ao_cec_irq_tx(struct meson_ao_cec_device *ao_cec)
-{
- unsigned long tx_status = 0;
- u8 stat;
- int ret = 0;
-
- meson_ao_cec_read(ao_cec, CEC_TX_MSG_STATUS, &stat, &ret);
- if (ret)
- goto tx_reg_err;
-
- switch (stat) {
- case TX_DONE:
- tx_status = CEC_TX_STATUS_OK;
- break;
-
- case TX_BUSY:
- tx_status = CEC_TX_STATUS_ARB_LOST;
- break;
-
- case TX_IDLE:
- tx_status = CEC_TX_STATUS_LOW_DRIVE;
- break;
-
- case TX_ERROR:
- default:
- tx_status = CEC_TX_STATUS_NACK;
- break;
- }
-
- /* Clear Interruption */
- writel_relaxed(CEC_INTR_TX, ao_cec->base + CEC_INTR_CLR_REG);
-
- /* Stop TX */
- meson_ao_cec_write(ao_cec, CEC_TX_MSG_CMD, TX_NO_OP, &ret);
- if (ret)
- goto tx_reg_err;
-
- cec_transmit_attempt_done(ao_cec->adap, tx_status);
- return;
-
-tx_reg_err:
- cec_transmit_attempt_done(ao_cec->adap, CEC_TX_STATUS_ERROR);
-}
-
-static void meson_ao_cec_irq_rx(struct meson_ao_cec_device *ao_cec)
-{
- int i, ret = 0;
- u8 reg;
-
- meson_ao_cec_read(ao_cec, CEC_RX_MSG_STATUS, ®, &ret);
- if (reg != RX_DONE)
- goto rx_out;
-
- meson_ao_cec_read(ao_cec, CEC_RX_NUM_MSG, ®, &ret);
- if (reg != 1)
- goto rx_out;
-
- meson_ao_cec_read(ao_cec, CEC_RX_MSG_LENGTH, ®, &ret);
-
- ao_cec->rx_msg.len = reg + 1;
- if (ao_cec->rx_msg.len > CEC_MAX_MSG_SIZE)
- ao_cec->rx_msg.len = CEC_MAX_MSG_SIZE;
-
- for (i = 0; i < ao_cec->rx_msg.len; i++) {
- u8 byte;
-
- meson_ao_cec_read(ao_cec, CEC_RX_MSG_0_HEADER + i, &byte, &ret);
-
- ao_cec->rx_msg.msg[i] = byte;
- }
-
- if (ret)
- goto rx_out;
-
- cec_received_msg(ao_cec->adap, &ao_cec->rx_msg);
-
-rx_out:
- /* Clear Interruption */
- writel_relaxed(CEC_INTR_RX, ao_cec->base + CEC_INTR_CLR_REG);
-
- /* Ack RX message */
- meson_ao_cec_write(ao_cec, CEC_RX_MSG_CMD, RX_ACK_CURRENT, &ret);
- meson_ao_cec_write(ao_cec, CEC_RX_MSG_CMD, RX_NO_OP, &ret);
-
- /* Clear RX buffer */
- meson_ao_cec_write(ao_cec, CEC_RX_CLEAR_BUF, CLEAR_START, &ret);
- meson_ao_cec_write(ao_cec, CEC_RX_CLEAR_BUF, CLEAR_STOP, &ret);
-}
-
-static irqreturn_t meson_ao_cec_irq_thread(int irq, void *data)
-{
- struct meson_ao_cec_device *ao_cec = data;
- u32 stat = readl_relaxed(ao_cec->base + CEC_INTR_STAT_REG);
-
- if (stat & CEC_INTR_TX)
- meson_ao_cec_irq_tx(ao_cec);
-
- meson_ao_cec_irq_rx(ao_cec);
-
- return IRQ_HANDLED;
-}
-
-static int meson_ao_cec_set_log_addr(struct cec_adapter *adap, u8 logical_addr)
-{
- struct meson_ao_cec_device *ao_cec = adap->priv;
- int ret = 0;
-
- meson_ao_cec_write(ao_cec, CEC_LOGICAL_ADDR0,
- LOGICAL_ADDR_DISABLE, &ret);
- if (ret)
- return ret;
-
- ret = meson_ao_cec_clear(ao_cec);
- if (ret)
- return ret;
-
- if (logical_addr == CEC_LOG_ADDR_INVALID)
- return 0;
-
- meson_ao_cec_write(ao_cec, CEC_LOGICAL_ADDR0,
- logical_addr & LOGICAL_ADDR_MASK, &ret);
- if (ret)
- return ret;
-
- udelay(100);
-
- meson_ao_cec_write(ao_cec, CEC_LOGICAL_ADDR0,
- (logical_addr & LOGICAL_ADDR_MASK) |
- LOGICAL_ADDR_VALID, &ret);
-
- return ret;
-}
-
-static int meson_ao_cec_transmit(struct cec_adapter *adap, u8 attempts,
- u32 signal_free_time, struct cec_msg *msg)
-{
- struct meson_ao_cec_device *ao_cec = adap->priv;
- int i, ret = 0;
- u8 reg;
-
- meson_ao_cec_read(ao_cec, CEC_TX_MSG_STATUS, ®, &ret);
- if (ret)
- return ret;
-
- if (reg == TX_BUSY) {
- dev_dbg(&ao_cec->pdev->dev, "%s: busy TX: aborting\n",
- __func__);
- meson_ao_cec_write(ao_cec, CEC_TX_MSG_CMD, TX_ABORT, &ret);
- }
-
- for (i = 0; i < msg->len; i++) {
- meson_ao_cec_write(ao_cec, CEC_TX_MSG_0_HEADER + i,
- msg->msg[i], &ret);
- }
-
- meson_ao_cec_write(ao_cec, CEC_TX_MSG_LENGTH, msg->len - 1, &ret);
- meson_ao_cec_write(ao_cec, CEC_TX_MSG_CMD, TX_REQ_CURRENT, &ret);
-
- return ret;
-}
-
-static int meson_ao_cec_adap_enable(struct cec_adapter *adap, bool enable)
-{
- struct meson_ao_cec_device *ao_cec = adap->priv;
- int ret;
-
- meson_ao_cec_irq_setup(ao_cec, false);
-
- writel_bits_relaxed(CEC_GEN_CNTL_RESET, CEC_GEN_CNTL_RESET,
- ao_cec->base + CEC_GEN_CNTL_REG);
-
- if (!enable)
- return 0;
-
- /* Enable gated clock (Normal mode). */
- writel_bits_relaxed(CEC_GEN_CNTL_CLK_CTRL_MASK,
- FIELD_PREP(CEC_GEN_CNTL_CLK_CTRL_MASK,
- CEC_GEN_CNTL_CLK_ENABLE),
- ao_cec->base + CEC_GEN_CNTL_REG);
-
- udelay(100);
-
- /* Release Reset */
- writel_bits_relaxed(CEC_GEN_CNTL_RESET, 0,
- ao_cec->base + CEC_GEN_CNTL_REG);
-
- /* Clear buffers */
- ret = meson_ao_cec_clear(ao_cec);
- if (ret)
- return ret;
-
- /* CEC arbitration 3/5/7 bit time set. */
- ret = meson_ao_cec_arbit_bit_time_set(ao_cec,
- CEC_SIGNAL_FREE_TIME_RETRY,
- 0x118);
- if (ret)
- return ret;
- ret = meson_ao_cec_arbit_bit_time_set(ao_cec,
- CEC_SIGNAL_FREE_TIME_NEW_INITIATOR,
- 0x000);
- if (ret)
- return ret;
- ret = meson_ao_cec_arbit_bit_time_set(ao_cec,
- CEC_SIGNAL_FREE_TIME_NEXT_XFER,
- 0x2aa);
- if (ret)
- return ret;
-
- meson_ao_cec_irq_setup(ao_cec, true);
-
- return 0;
-}
-
-static const struct cec_adap_ops meson_ao_cec_ops = {
- .adap_enable = meson_ao_cec_adap_enable,
- .adap_log_addr = meson_ao_cec_set_log_addr,
- .adap_transmit = meson_ao_cec_transmit,
-};
-
-static int meson_ao_cec_probe(struct platform_device *pdev)
-{
- struct meson_ao_cec_device *ao_cec;
- struct device *hdmi_dev;
- struct resource *res;
- int ret, irq;
-
- hdmi_dev = cec_notifier_parse_hdmi_phandle(&pdev->dev);
-
- if (IS_ERR(hdmi_dev))
- return PTR_ERR(hdmi_dev);
-
- ao_cec = devm_kzalloc(&pdev->dev, sizeof(*ao_cec), GFP_KERNEL);
- if (!ao_cec)
- return -ENOMEM;
-
- spin_lock_init(&ao_cec->cec_reg_lock);
-
- ao_cec->adap = cec_allocate_adapter(&meson_ao_cec_ops, ao_cec,
- "meson_ao_cec",
- CEC_CAP_DEFAULTS |
- CEC_CAP_CONNECTOR_INFO,
- 1); /* Use 1 for now */
- if (IS_ERR(ao_cec->adap))
- return PTR_ERR(ao_cec->adap);
-
- ao_cec->adap->owner = THIS_MODULE;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- ao_cec->base = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(ao_cec->base)) {
- ret = PTR_ERR(ao_cec->base);
- goto out_probe_adapter;
- }
-
- irq = platform_get_irq(pdev, 0);
- ret = devm_request_threaded_irq(&pdev->dev, irq,
- meson_ao_cec_irq,
- meson_ao_cec_irq_thread,
- 0, NULL, ao_cec);
- if (ret) {
- dev_err(&pdev->dev, "irq request failed\n");
- goto out_probe_adapter;
- }
-
- ao_cec->core = devm_clk_get(&pdev->dev, "core");
- if (IS_ERR(ao_cec->core)) {
- dev_err(&pdev->dev, "core clock request failed\n");
- ret = PTR_ERR(ao_cec->core);
- goto out_probe_adapter;
- }
-
- ret = clk_prepare_enable(ao_cec->core);
- if (ret) {
- dev_err(&pdev->dev, "core clock enable failed\n");
- goto out_probe_adapter;
- }
-
- ret = clk_set_rate(ao_cec->core, CEC_CLK_RATE);
- if (ret) {
- dev_err(&pdev->dev, "core clock set rate failed\n");
- goto out_probe_clk;
- }
-
- device_reset_optional(&pdev->dev);
-
- ao_cec->pdev = pdev;
- platform_set_drvdata(pdev, ao_cec);
-
- ao_cec->notify = cec_notifier_cec_adap_register(hdmi_dev, NULL,
- ao_cec->adap);
- if (!ao_cec->notify) {
- ret = -ENOMEM;
- goto out_probe_clk;
- }
-
- ret = cec_register_adapter(ao_cec->adap, &pdev->dev);
- if (ret < 0)
- goto out_probe_notify;
-
- /* Setup Hardware */
- writel_relaxed(CEC_GEN_CNTL_RESET,
- ao_cec->base + CEC_GEN_CNTL_REG);
-
- return 0;
-
-out_probe_notify:
- cec_notifier_cec_adap_unregister(ao_cec->notify);
-
-out_probe_clk:
- clk_disable_unprepare(ao_cec->core);
-
-out_probe_adapter:
- cec_delete_adapter(ao_cec->adap);
-
- dev_err(&pdev->dev, "CEC controller registration failed\n");
-
- return ret;
-}
-
-static int meson_ao_cec_remove(struct platform_device *pdev)
-{
- struct meson_ao_cec_device *ao_cec = platform_get_drvdata(pdev);
-
- clk_disable_unprepare(ao_cec->core);
-
- cec_notifier_cec_adap_unregister(ao_cec->notify);
- cec_unregister_adapter(ao_cec->adap);
-
- return 0;
-}
-
-static const struct of_device_id meson_ao_cec_of_match[] = {
- { .compatible = "amlogic,meson-gx-ao-cec", },
- { /* sentinel */ }
-};
-MODULE_DEVICE_TABLE(of, meson_ao_cec_of_match);
-
-static struct platform_driver meson_ao_cec_driver = {
- .probe = meson_ao_cec_probe,
- .remove = meson_ao_cec_remove,
- .driver = {
- .name = "meson-ao-cec",
- .of_match_table = of_match_ptr(meson_ao_cec_of_match),
- },
-};
-
-module_platform_driver(meson_ao_cec_driver);
-
-MODULE_DESCRIPTION("Meson AO CEC Controller driver");
-MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/platform/mtk-jpeg/Makefile b/drivers/media/platform/mtk-jpeg/Makefile
index 92a4fc0..76c33aa 100644
--- a/drivers/media/platform/mtk-jpeg/Makefile
+++ b/drivers/media/platform/mtk-jpeg/Makefile
@@ -1,3 +1,6 @@
# SPDX-License-Identifier: GPL-2.0-only
-mtk_jpeg-objs := mtk_jpeg_core.o mtk_jpeg_hw.o mtk_jpeg_parse.o
+mtk_jpeg-objs := mtk_jpeg_core.o \
+ mtk_jpeg_dec_hw.o \
+ mtk_jpeg_dec_parse.o \
+ mtk_jpeg_enc_hw.o
obj-$(CONFIG_VIDEO_MEDIATEK_JPEG) += mtk_jpeg.o
diff --git a/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c b/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c
index 9fa1bc5..88a23bc 100644
--- a/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c
+++ b/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c
@@ -3,6 +3,7 @@
* Copyright (c) 2016 MediaTek Inc.
* Author: Ming Hsiu Tsai <minghsiu.tsai@mediatek.com>
* Rick Chang <rick.chang@mediatek.com>
+ * Xia Jiang <xia.jiang@mediatek.com>
*/
#include <linux/clk.h>
@@ -23,15 +24,64 @@
#include <media/videobuf2-dma-contig.h>
#include <soc/mediatek/smi.h>
-#include "mtk_jpeg_hw.h"
+#include "mtk_jpeg_enc_hw.h"
+#include "mtk_jpeg_dec_hw.h"
#include "mtk_jpeg_core.h"
-#include "mtk_jpeg_parse.h"
+#include "mtk_jpeg_dec_parse.h"
-static struct mtk_jpeg_fmt mtk_jpeg_formats[] = {
+static struct mtk_jpeg_fmt mtk_jpeg_enc_formats[] = {
{
.fourcc = V4L2_PIX_FMT_JPEG,
.colplanes = 1,
- .flags = MTK_JPEG_FMT_FLAG_DEC_OUTPUT,
+ .flags = MTK_JPEG_FMT_FLAG_CAPTURE,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_NV12M,
+ .hw_format = JPEG_ENC_YUV_FORMAT_NV12,
+ .h_sample = {4, 4},
+ .v_sample = {4, 2},
+ .colplanes = 2,
+ .h_align = 4,
+ .v_align = 4,
+ .flags = MTK_JPEG_FMT_FLAG_OUTPUT,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_NV21M,
+ .hw_format = JEPG_ENC_YUV_FORMAT_NV21,
+ .h_sample = {4, 4},
+ .v_sample = {4, 2},
+ .colplanes = 2,
+ .h_align = 4,
+ .v_align = 4,
+ .flags = MTK_JPEG_FMT_FLAG_OUTPUT,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .hw_format = JPEG_ENC_YUV_FORMAT_YUYV,
+ .h_sample = {8},
+ .v_sample = {4},
+ .colplanes = 1,
+ .h_align = 5,
+ .v_align = 3,
+ .flags = MTK_JPEG_FMT_FLAG_OUTPUT,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_YVYU,
+ .hw_format = JPEG_ENC_YUV_FORMAT_YVYU,
+ .h_sample = {8},
+ .v_sample = {4},
+ .colplanes = 1,
+ .h_align = 5,
+ .v_align = 3,
+ .flags = MTK_JPEG_FMT_FLAG_OUTPUT,
+ },
+};
+
+static struct mtk_jpeg_fmt mtk_jpeg_dec_formats[] = {
+ {
+ .fourcc = V4L2_PIX_FMT_JPEG,
+ .colplanes = 1,
+ .flags = MTK_JPEG_FMT_FLAG_OUTPUT,
},
{
.fourcc = V4L2_PIX_FMT_YUV420M,
@@ -40,7 +90,7 @@
.colplanes = 3,
.h_align = 5,
.v_align = 4,
- .flags = MTK_JPEG_FMT_FLAG_DEC_CAPTURE,
+ .flags = MTK_JPEG_FMT_FLAG_CAPTURE,
},
{
.fourcc = V4L2_PIX_FMT_YUV422M,
@@ -49,27 +99,27 @@
.colplanes = 3,
.h_align = 5,
.v_align = 3,
- .flags = MTK_JPEG_FMT_FLAG_DEC_CAPTURE,
+ .flags = MTK_JPEG_FMT_FLAG_CAPTURE,
},
};
-#define MTK_JPEG_NUM_FORMATS ARRAY_SIZE(mtk_jpeg_formats)
-
-enum {
- MTK_JPEG_BUF_FLAGS_INIT = 0,
- MTK_JPEG_BUF_FLAGS_LAST_FRAME = 1,
-};
+#define MTK_JPEG_ENC_NUM_FORMATS ARRAY_SIZE(mtk_jpeg_enc_formats)
+#define MTK_JPEG_DEC_NUM_FORMATS ARRAY_SIZE(mtk_jpeg_dec_formats)
struct mtk_jpeg_src_buf {
struct vb2_v4l2_buffer b;
struct list_head list;
- int flags;
struct mtk_jpeg_dec_param dec_param;
};
static int debug;
module_param(debug, int, 0644);
+static inline struct mtk_jpeg_ctx *ctrl_to_ctx(struct v4l2_ctrl *ctrl)
+{
+ return container_of(ctrl->handler, struct mtk_jpeg_ctx, ctrl_hdl);
+}
+
static inline struct mtk_jpeg_ctx *mtk_jpeg_fh_to_ctx(struct v4l2_fh *fh)
{
return container_of(fh, struct mtk_jpeg_ctx, fh);
@@ -86,14 +136,61 @@
{
struct mtk_jpeg_dev *jpeg = video_drvdata(file);
- strscpy(cap->driver, MTK_JPEG_NAME " decoder", sizeof(cap->driver));
- strscpy(cap->card, MTK_JPEG_NAME " decoder", sizeof(cap->card));
+ strscpy(cap->driver, jpeg->variant->dev_name, sizeof(cap->driver));
+ strscpy(cap->card, jpeg->variant->dev_name, sizeof(cap->card));
snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s",
dev_name(jpeg->dev));
return 0;
}
+static int vidioc_jpeg_enc_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct mtk_jpeg_ctx *ctx = ctrl_to_ctx(ctrl);
+
+ switch (ctrl->id) {
+ case V4L2_CID_JPEG_RESTART_INTERVAL:
+ ctx->restart_interval = ctrl->val;
+ break;
+ case V4L2_CID_JPEG_COMPRESSION_QUALITY:
+ ctx->enc_quality = ctrl->val;
+ break;
+ case V4L2_CID_JPEG_ACTIVE_MARKER:
+ ctx->enable_exif = ctrl->val & V4L2_JPEG_ACTIVE_MARKER_APP1;
+ break;
+ }
+
+ return 0;
+}
+
+static const struct v4l2_ctrl_ops mtk_jpeg_enc_ctrl_ops = {
+ .s_ctrl = vidioc_jpeg_enc_s_ctrl,
+};
+
+static int mtk_jpeg_enc_ctrls_setup(struct mtk_jpeg_ctx *ctx)
+{
+ const struct v4l2_ctrl_ops *ops = &mtk_jpeg_enc_ctrl_ops;
+ struct v4l2_ctrl_handler *handler = &ctx->ctrl_hdl;
+
+ v4l2_ctrl_handler_init(handler, 3);
+
+ v4l2_ctrl_new_std(handler, ops, V4L2_CID_JPEG_RESTART_INTERVAL, 0, 100,
+ 1, 0);
+ v4l2_ctrl_new_std(handler, ops, V4L2_CID_JPEG_COMPRESSION_QUALITY, 48,
+ 100, 1, 90);
+ v4l2_ctrl_new_std(handler, ops, V4L2_CID_JPEG_ACTIVE_MARKER, 0,
+ V4L2_JPEG_ACTIVE_MARKER_APP1, 0, 0);
+
+ if (handler->error) {
+ v4l2_ctrl_handler_free(&ctx->ctrl_hdl);
+ return handler->error;
+ }
+
+ v4l2_ctrl_handler_setup(&ctx->ctrl_hdl);
+
+ return 0;
+}
+
static int mtk_jpeg_enum_fmt(struct mtk_jpeg_fmt *mtk_jpeg_formats, int n,
struct v4l2_fmtdesc *f, u32 type)
{
@@ -118,15 +215,23 @@
static int mtk_jpeg_enum_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_fmtdesc *f)
{
- return mtk_jpeg_enum_fmt(mtk_jpeg_formats, MTK_JPEG_NUM_FORMATS, f,
- MTK_JPEG_FMT_FLAG_DEC_CAPTURE);
+ struct mtk_jpeg_ctx *ctx = mtk_jpeg_fh_to_ctx(priv);
+ struct mtk_jpeg_dev *jpeg = ctx->jpeg;
+
+ return mtk_jpeg_enum_fmt(jpeg->variant->formats,
+ jpeg->variant->num_formats, f,
+ MTK_JPEG_FMT_FLAG_CAPTURE);
}
static int mtk_jpeg_enum_fmt_vid_out(struct file *file, void *priv,
struct v4l2_fmtdesc *f)
{
- return mtk_jpeg_enum_fmt(mtk_jpeg_formats, MTK_JPEG_NUM_FORMATS, f,
- MTK_JPEG_FMT_FLAG_DEC_OUTPUT);
+ struct mtk_jpeg_ctx *ctx = mtk_jpeg_fh_to_ctx(priv);
+ struct mtk_jpeg_dev *jpeg = ctx->jpeg;
+
+ return mtk_jpeg_enum_fmt(jpeg->variant->formats,
+ jpeg->variant->num_formats, f,
+ MTK_JPEG_FMT_FLAG_OUTPUT);
}
static struct mtk_jpeg_q_data *mtk_jpeg_get_q_data(struct mtk_jpeg_ctx *ctx,
@@ -137,126 +242,63 @@
return &ctx->cap_q;
}
-static struct mtk_jpeg_fmt *mtk_jpeg_find_format(struct mtk_jpeg_ctx *ctx,
- u32 pixelformat,
- unsigned int fmt_type)
+static struct mtk_jpeg_fmt *
+mtk_jpeg_find_format(struct mtk_jpeg_fmt *mtk_jpeg_formats, int num_formats,
+ u32 pixelformat, unsigned int fmt_type)
{
- unsigned int k, fmt_flag;
+ unsigned int k;
+ struct mtk_jpeg_fmt *fmt;
- fmt_flag = (fmt_type == MTK_JPEG_FMT_TYPE_OUTPUT) ?
- MTK_JPEG_FMT_FLAG_DEC_OUTPUT :
- MTK_JPEG_FMT_FLAG_DEC_CAPTURE;
+ for (k = 0; k < num_formats; k++) {
+ fmt = &mtk_jpeg_formats[k];
- for (k = 0; k < MTK_JPEG_NUM_FORMATS; k++) {
- struct mtk_jpeg_fmt *fmt = &mtk_jpeg_formats[k];
-
- if (fmt->fourcc == pixelformat && fmt->flags & fmt_flag)
+ if (fmt->fourcc == pixelformat && fmt->flags & fmt_type)
return fmt;
}
return NULL;
}
-static void mtk_jpeg_bound_align_image(u32 *w, unsigned int wmin,
- unsigned int wmax, unsigned int walign,
- u32 *h, unsigned int hmin,
- unsigned int hmax, unsigned int halign)
+static int mtk_jpeg_try_fmt_mplane(struct v4l2_pix_format_mplane *pix_mp,
+ struct mtk_jpeg_fmt *fmt)
{
- int width, height, w_step, h_step;
-
- width = *w;
- height = *h;
- w_step = 1 << walign;
- h_step = 1 << halign;
-
- v4l_bound_align_image(w, wmin, wmax, walign, h, hmin, hmax, halign, 0);
- if (*w < width && (*w + w_step) <= wmax)
- *w += w_step;
- if (*h < height && (*h + h_step) <= hmax)
- *h += h_step;
-}
-
-static void mtk_jpeg_adjust_fmt_mplane(struct mtk_jpeg_ctx *ctx,
- struct v4l2_format *f)
-{
- struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp;
- struct mtk_jpeg_q_data *q_data;
int i;
- q_data = mtk_jpeg_get_q_data(ctx, f->type);
-
- pix_mp->width = q_data->w;
- pix_mp->height = q_data->h;
- pix_mp->pixelformat = q_data->fmt->fourcc;
- pix_mp->num_planes = q_data->fmt->colplanes;
-
- for (i = 0; i < pix_mp->num_planes; i++) {
- pix_mp->plane_fmt[i].bytesperline = q_data->bytesperline[i];
- pix_mp->plane_fmt[i].sizeimage = q_data->sizeimage[i];
- }
-}
-
-static int mtk_jpeg_try_fmt_mplane(struct v4l2_format *f,
- struct mtk_jpeg_fmt *fmt,
- struct mtk_jpeg_ctx *ctx, int q_type)
-{
- struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp;
- struct mtk_jpeg_dev *jpeg = ctx->jpeg;
- int i;
-
- memset(pix_mp->reserved, 0, sizeof(pix_mp->reserved));
pix_mp->field = V4L2_FIELD_NONE;
- if (ctx->state != MTK_JPEG_INIT) {
- mtk_jpeg_adjust_fmt_mplane(ctx, f);
- goto end;
- }
-
pix_mp->num_planes = fmt->colplanes;
pix_mp->pixelformat = fmt->fourcc;
- if (q_type == MTK_JPEG_FMT_TYPE_OUTPUT) {
+ if (fmt->fourcc == V4L2_PIX_FMT_JPEG) {
struct v4l2_plane_pix_format *pfmt = &pix_mp->plane_fmt[0];
- mtk_jpeg_bound_align_image(&pix_mp->width, MTK_JPEG_MIN_WIDTH,
- MTK_JPEG_MAX_WIDTH, 0,
- &pix_mp->height, MTK_JPEG_MIN_HEIGHT,
- MTK_JPEG_MAX_HEIGHT, 0);
+ pix_mp->height = clamp(pix_mp->height, MTK_JPEG_MIN_HEIGHT,
+ MTK_JPEG_MAX_HEIGHT);
+ pix_mp->width = clamp(pix_mp->width, MTK_JPEG_MIN_WIDTH,
+ MTK_JPEG_MAX_WIDTH);
- memset(pfmt->reserved, 0, sizeof(pfmt->reserved));
pfmt->bytesperline = 0;
/* Source size must be aligned to 128 */
- pfmt->sizeimage = mtk_jpeg_align(pfmt->sizeimage, 128);
+ pfmt->sizeimage = round_up(pfmt->sizeimage, 128);
if (pfmt->sizeimage == 0)
pfmt->sizeimage = MTK_JPEG_DEFAULT_SIZEIMAGE;
- goto end;
+ return 0;
}
- /* type is MTK_JPEG_FMT_TYPE_CAPTURE */
- mtk_jpeg_bound_align_image(&pix_mp->width, MTK_JPEG_MIN_WIDTH,
- MTK_JPEG_MAX_WIDTH, fmt->h_align,
- &pix_mp->height, MTK_JPEG_MIN_HEIGHT,
- MTK_JPEG_MAX_HEIGHT, fmt->v_align);
+ /* other fourcc */
+ pix_mp->height = clamp(round_up(pix_mp->height, fmt->v_align),
+ MTK_JPEG_MIN_HEIGHT, MTK_JPEG_MAX_HEIGHT);
+ pix_mp->width = clamp(round_up(pix_mp->width, fmt->h_align),
+ MTK_JPEG_MIN_WIDTH, MTK_JPEG_MAX_WIDTH);
for (i = 0; i < fmt->colplanes; i++) {
struct v4l2_plane_pix_format *pfmt = &pix_mp->plane_fmt[i];
u32 stride = pix_mp->width * fmt->h_sample[i] / 4;
u32 h = pix_mp->height * fmt->v_sample[i] / 4;
- memset(pfmt->reserved, 0, sizeof(pfmt->reserved));
pfmt->bytesperline = stride;
pfmt->sizeimage = stride * h;
}
-end:
- v4l2_dbg(2, debug, &jpeg->v4l2_dev, "wxh:%ux%u\n",
- pix_mp->width, pix_mp->height);
- for (i = 0; i < pix_mp->num_planes; i++) {
- v4l2_dbg(2, debug, &jpeg->v4l2_dev,
- "plane[%d] bpl=%u, size=%u\n",
- i,
- pix_mp->plane_fmt[i].bytesperline,
- pix_mp->plane_fmt[i].sizeimage);
- }
return 0;
}
@@ -276,16 +318,15 @@
q_data = mtk_jpeg_get_q_data(ctx, f->type);
- memset(pix_mp->reserved, 0, sizeof(pix_mp->reserved));
- pix_mp->width = q_data->w;
- pix_mp->height = q_data->h;
+ pix_mp->width = q_data->pix_mp.width;
+ pix_mp->height = q_data->pix_mp.height;
pix_mp->field = V4L2_FIELD_NONE;
pix_mp->pixelformat = q_data->fmt->fourcc;
pix_mp->num_planes = q_data->fmt->colplanes;
- pix_mp->colorspace = ctx->colorspace;
- pix_mp->ycbcr_enc = ctx->ycbcr_enc;
- pix_mp->xfer_func = ctx->xfer_func;
- pix_mp->quantization = ctx->quantization;
+ pix_mp->colorspace = q_data->pix_mp.colorspace;
+ pix_mp->ycbcr_enc = q_data->pix_mp.ycbcr_enc;
+ pix_mp->xfer_func = q_data->pix_mp.xfer_func;
+ pix_mp->quantization = q_data->pix_mp.quantization;
v4l2_dbg(1, debug, &jpeg->v4l2_dev, "(%d) g_fmt:%c%c%c%c wxh:%ux%u\n",
f->type,
@@ -298,9 +339,8 @@
for (i = 0; i < pix_mp->num_planes; i++) {
struct v4l2_plane_pix_format *pfmt = &pix_mp->plane_fmt[i];
- pfmt->bytesperline = q_data->bytesperline[i];
- pfmt->sizeimage = q_data->sizeimage[i];
- memset(pfmt->reserved, 0, sizeof(pfmt->reserved));
+ pfmt->bytesperline = q_data->pix_mp.plane_fmt[i].bytesperline;
+ pfmt->sizeimage = q_data->pix_mp.plane_fmt[i].sizeimage;
v4l2_dbg(1, debug, &jpeg->v4l2_dev,
"plane[%d] bpl=%u, size=%u\n",
@@ -315,10 +355,13 @@
struct v4l2_format *f)
{
struct mtk_jpeg_ctx *ctx = mtk_jpeg_fh_to_ctx(priv);
+ struct mtk_jpeg_dev *jpeg = ctx->jpeg;
struct mtk_jpeg_fmt *fmt;
- fmt = mtk_jpeg_find_format(ctx, f->fmt.pix_mp.pixelformat,
- MTK_JPEG_FMT_TYPE_CAPTURE);
+ fmt = mtk_jpeg_find_format(jpeg->variant->formats,
+ jpeg->variant->num_formats,
+ f->fmt.pix_mp.pixelformat,
+ MTK_JPEG_FMT_FLAG_CAPTURE);
if (!fmt)
fmt = ctx->cap_q.fmt;
@@ -329,17 +372,25 @@
(fmt->fourcc >> 16 & 0xff),
(fmt->fourcc >> 24 & 0xff));
- return mtk_jpeg_try_fmt_mplane(f, fmt, ctx, MTK_JPEG_FMT_TYPE_CAPTURE);
+ if (ctx->state != MTK_JPEG_INIT) {
+ mtk_jpeg_g_fmt_vid_mplane(file, priv, f);
+ return 0;
+ }
+
+ return mtk_jpeg_try_fmt_mplane(&f->fmt.pix_mp, fmt);
}
static int mtk_jpeg_try_fmt_vid_out_mplane(struct file *file, void *priv,
struct v4l2_format *f)
{
struct mtk_jpeg_ctx *ctx = mtk_jpeg_fh_to_ctx(priv);
+ struct mtk_jpeg_dev *jpeg = ctx->jpeg;
struct mtk_jpeg_fmt *fmt;
- fmt = mtk_jpeg_find_format(ctx, f->fmt.pix_mp.pixelformat,
- MTK_JPEG_FMT_TYPE_OUTPUT);
+ fmt = mtk_jpeg_find_format(jpeg->variant->formats,
+ jpeg->variant->num_formats,
+ f->fmt.pix_mp.pixelformat,
+ MTK_JPEG_FMT_FLAG_OUTPUT);
if (!fmt)
fmt = ctx->out_q.fmt;
@@ -350,17 +401,21 @@
(fmt->fourcc >> 16 & 0xff),
(fmt->fourcc >> 24 & 0xff));
- return mtk_jpeg_try_fmt_mplane(f, fmt, ctx, MTK_JPEG_FMT_TYPE_OUTPUT);
+ if (ctx->state != MTK_JPEG_INIT) {
+ mtk_jpeg_g_fmt_vid_mplane(file, priv, f);
+ return 0;
+ }
+
+ return mtk_jpeg_try_fmt_mplane(&f->fmt.pix_mp, fmt);
}
static int mtk_jpeg_s_fmt_mplane(struct mtk_jpeg_ctx *ctx,
- struct v4l2_format *f)
+ struct v4l2_format *f, unsigned int fmt_type)
{
struct vb2_queue *vq;
struct mtk_jpeg_q_data *q_data = NULL;
struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp;
struct mtk_jpeg_dev *jpeg = ctx->jpeg;
- unsigned int f_type;
int i;
vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
@@ -374,16 +429,17 @@
return -EBUSY;
}
- f_type = V4L2_TYPE_IS_OUTPUT(f->type) ?
- MTK_JPEG_FMT_TYPE_OUTPUT : MTK_JPEG_FMT_TYPE_CAPTURE;
-
- q_data->fmt = mtk_jpeg_find_format(ctx, pix_mp->pixelformat, f_type);
- q_data->w = pix_mp->width;
- q_data->h = pix_mp->height;
- ctx->colorspace = pix_mp->colorspace;
- ctx->ycbcr_enc = pix_mp->ycbcr_enc;
- ctx->xfer_func = pix_mp->xfer_func;
- ctx->quantization = pix_mp->quantization;
+ q_data->fmt = mtk_jpeg_find_format(jpeg->variant->formats,
+ jpeg->variant->num_formats,
+ pix_mp->pixelformat, fmt_type);
+ q_data->pix_mp.width = pix_mp->width;
+ q_data->pix_mp.height = pix_mp->height;
+ q_data->enc_crop_rect.width = pix_mp->width;
+ q_data->enc_crop_rect.height = pix_mp->height;
+ q_data->pix_mp.colorspace = V4L2_COLORSPACE_SRGB;
+ q_data->pix_mp.ycbcr_enc = V4L2_YCBCR_ENC_601;
+ q_data->pix_mp.xfer_func = V4L2_XFER_FUNC_SRGB;
+ q_data->pix_mp.quantization = V4L2_QUANTIZATION_FULL_RANGE;
v4l2_dbg(1, debug, &jpeg->v4l2_dev, "(%d) s_fmt:%c%c%c%c wxh:%ux%u\n",
f->type,
@@ -391,15 +447,18 @@
(q_data->fmt->fourcc >> 8 & 0xff),
(q_data->fmt->fourcc >> 16 & 0xff),
(q_data->fmt->fourcc >> 24 & 0xff),
- q_data->w, q_data->h);
+ q_data->pix_mp.width, q_data->pix_mp.height);
for (i = 0; i < q_data->fmt->colplanes; i++) {
- q_data->bytesperline[i] = pix_mp->plane_fmt[i].bytesperline;
- q_data->sizeimage[i] = pix_mp->plane_fmt[i].sizeimage;
+ q_data->pix_mp.plane_fmt[i].bytesperline =
+ pix_mp->plane_fmt[i].bytesperline;
+ q_data->pix_mp.plane_fmt[i].sizeimage =
+ pix_mp->plane_fmt[i].sizeimage;
v4l2_dbg(1, debug, &jpeg->v4l2_dev,
"plane[%d] bpl=%u, size=%u\n",
- i, q_data->bytesperline[i], q_data->sizeimage[i]);
+ i, q_data->pix_mp.plane_fmt[i].bytesperline,
+ q_data->pix_mp.plane_fmt[i].sizeimage);
}
return 0;
@@ -414,7 +473,8 @@
if (ret)
return ret;
- return mtk_jpeg_s_fmt_mplane(mtk_jpeg_fh_to_ctx(priv), f);
+ return mtk_jpeg_s_fmt_mplane(mtk_jpeg_fh_to_ctx(priv), f,
+ MTK_JPEG_FMT_FLAG_OUTPUT);
}
static int mtk_jpeg_s_fmt_vid_cap_mplane(struct file *file, void *priv,
@@ -426,7 +486,8 @@
if (ret)
return ret;
- return mtk_jpeg_s_fmt_mplane(mtk_jpeg_fh_to_ctx(priv), f);
+ return mtk_jpeg_s_fmt_mplane(mtk_jpeg_fh_to_ctx(priv), f,
+ MTK_JPEG_FMT_FLAG_CAPTURE);
}
static void mtk_jpeg_queue_src_chg_event(struct mtk_jpeg_ctx *ctx)
@@ -446,13 +507,38 @@
switch (sub->type) {
case V4L2_EVENT_SOURCE_CHANGE:
return v4l2_src_change_event_subscribe(fh, sub);
+ }
+
+ return v4l2_ctrl_subscribe_event(fh, sub);
+}
+
+static int mtk_jpeg_enc_g_selection(struct file *file, void *priv,
+ struct v4l2_selection *s)
+{
+ struct mtk_jpeg_ctx *ctx = mtk_jpeg_fh_to_ctx(priv);
+
+ if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
+ return -EINVAL;
+
+ switch (s->target) {
+ case V4L2_SEL_TGT_CROP:
+ s->r = ctx->out_q.enc_crop_rect;
+ break;
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ case V4L2_SEL_TGT_CROP_DEFAULT:
+ s->r.width = ctx->out_q.pix_mp.width;
+ s->r.height = ctx->out_q.pix_mp.height;
+ s->r.left = 0;
+ s->r.top = 0;
+ break;
default:
return -EINVAL;
}
+ return 0;
}
-static int mtk_jpeg_g_selection(struct file *file, void *priv,
- struct v4l2_selection *s)
+static int mtk_jpeg_dec_g_selection(struct file *file, void *priv,
+ struct v4l2_selection *s)
{
struct mtk_jpeg_ctx *ctx = mtk_jpeg_fh_to_ctx(priv);
@@ -462,15 +548,15 @@
switch (s->target) {
case V4L2_SEL_TGT_COMPOSE:
case V4L2_SEL_TGT_COMPOSE_DEFAULT:
- s->r.width = ctx->out_q.w;
- s->r.height = ctx->out_q.h;
+ s->r.width = ctx->out_q.pix_mp.width;
+ s->r.height = ctx->out_q.pix_mp.height;
s->r.left = 0;
s->r.top = 0;
break;
case V4L2_SEL_TGT_COMPOSE_BOUNDS:
case V4L2_SEL_TGT_COMPOSE_PADDED:
- s->r.width = ctx->cap_q.w;
- s->r.height = ctx->cap_q.h;
+ s->r.width = ctx->cap_q.pix_mp.width;
+ s->r.height = ctx->cap_q.pix_mp.height;
s->r.left = 0;
s->r.top = 0;
break;
@@ -480,53 +566,30 @@
return 0;
}
-static int mtk_jpeg_s_selection(struct file *file, void *priv,
- struct v4l2_selection *s)
+static int mtk_jpeg_enc_s_selection(struct file *file, void *priv,
+ struct v4l2_selection *s)
{
struct mtk_jpeg_ctx *ctx = mtk_jpeg_fh_to_ctx(priv);
- if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
return -EINVAL;
switch (s->target) {
- case V4L2_SEL_TGT_COMPOSE:
+ case V4L2_SEL_TGT_CROP:
s->r.left = 0;
s->r.top = 0;
- s->r.width = ctx->out_q.w;
- s->r.height = ctx->out_q.h;
+ s->r.width = min(s->r.width, ctx->out_q.pix_mp.width);
+ s->r.height = min(s->r.height, ctx->out_q.pix_mp.height);
+ ctx->out_q.enc_crop_rect = s->r;
break;
default:
return -EINVAL;
}
+
return 0;
}
-static int mtk_jpeg_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
-{
- struct v4l2_fh *fh = file->private_data;
- struct mtk_jpeg_ctx *ctx = mtk_jpeg_fh_to_ctx(priv);
- struct vb2_queue *vq;
- struct vb2_buffer *vb;
- struct mtk_jpeg_src_buf *jpeg_src_buf;
-
- if (buf->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
- goto end;
-
- vq = v4l2_m2m_get_vq(fh->m2m_ctx, buf->type);
- if (buf->index >= vq->num_buffers) {
- dev_err(ctx->jpeg->dev, "buffer index out of range\n");
- return -EINVAL;
- }
-
- vb = vb2_get_buffer(vq, buf->index);
- jpeg_src_buf = mtk_jpeg_vb2_to_srcbuf(vb);
- jpeg_src_buf->flags = (buf->m.planes[0].bytesused == 0) ?
- MTK_JPEG_BUF_FLAGS_LAST_FRAME : MTK_JPEG_BUF_FLAGS_INIT;
-end:
- return v4l2_m2m_qbuf(file, fh->m2m_ctx, buf);
-}
-
-static const struct v4l2_ioctl_ops mtk_jpeg_ioctl_ops = {
+static const struct v4l2_ioctl_ops mtk_jpeg_enc_ioctl_ops = {
.vidioc_querycap = mtk_jpeg_querycap,
.vidioc_enum_fmt_vid_cap = mtk_jpeg_enum_fmt_vid_cap,
.vidioc_enum_fmt_vid_out = mtk_jpeg_enum_fmt_vid_out,
@@ -536,10 +599,36 @@
.vidioc_g_fmt_vid_out_mplane = mtk_jpeg_g_fmt_vid_mplane,
.vidioc_s_fmt_vid_cap_mplane = mtk_jpeg_s_fmt_vid_cap_mplane,
.vidioc_s_fmt_vid_out_mplane = mtk_jpeg_s_fmt_vid_out_mplane,
- .vidioc_qbuf = mtk_jpeg_qbuf,
+ .vidioc_qbuf = v4l2_m2m_ioctl_qbuf,
.vidioc_subscribe_event = mtk_jpeg_subscribe_event,
- .vidioc_g_selection = mtk_jpeg_g_selection,
- .vidioc_s_selection = mtk_jpeg_s_selection,
+ .vidioc_g_selection = mtk_jpeg_enc_g_selection,
+ .vidioc_s_selection = mtk_jpeg_enc_s_selection,
+
+ .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs,
+ .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf,
+ .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs,
+ .vidioc_querybuf = v4l2_m2m_ioctl_querybuf,
+ .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf,
+ .vidioc_expbuf = v4l2_m2m_ioctl_expbuf,
+ .vidioc_streamon = v4l2_m2m_ioctl_streamon,
+ .vidioc_streamoff = v4l2_m2m_ioctl_streamoff,
+
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+static const struct v4l2_ioctl_ops mtk_jpeg_dec_ioctl_ops = {
+ .vidioc_querycap = mtk_jpeg_querycap,
+ .vidioc_enum_fmt_vid_cap = mtk_jpeg_enum_fmt_vid_cap,
+ .vidioc_enum_fmt_vid_out = mtk_jpeg_enum_fmt_vid_out,
+ .vidioc_try_fmt_vid_cap_mplane = mtk_jpeg_try_fmt_vid_cap_mplane,
+ .vidioc_try_fmt_vid_out_mplane = mtk_jpeg_try_fmt_vid_out_mplane,
+ .vidioc_g_fmt_vid_cap_mplane = mtk_jpeg_g_fmt_vid_mplane,
+ .vidioc_g_fmt_vid_out_mplane = mtk_jpeg_g_fmt_vid_mplane,
+ .vidioc_s_fmt_vid_cap_mplane = mtk_jpeg_s_fmt_vid_cap_mplane,
+ .vidioc_s_fmt_vid_out_mplane = mtk_jpeg_s_fmt_vid_out_mplane,
+ .vidioc_qbuf = v4l2_m2m_ioctl_qbuf,
+ .vidioc_subscribe_event = mtk_jpeg_subscribe_event,
+ .vidioc_g_selection = mtk_jpeg_dec_g_selection,
.vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs,
.vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf,
@@ -573,14 +662,14 @@
if (*num_planes) {
for (i = 0; i < *num_planes; i++)
- if (sizes[i] < q_data->sizeimage[i])
+ if (sizes[i] < q_data->pix_mp.plane_fmt[i].sizeimage)
return -EINVAL;
return 0;
}
*num_planes = q_data->fmt->colplanes;
for (i = 0; i < q_data->fmt->colplanes; i++) {
- sizes[i] = q_data->sizeimage[i];
+ sizes[i] = q_data->pix_mp.plane_fmt[i].sizeimage;
v4l2_dbg(1, debug, &jpeg->v4l2_dev, "sizeimage[%d]=%u\n",
i, sizes[i]);
}
@@ -592,14 +681,22 @@
{
struct mtk_jpeg_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
struct mtk_jpeg_q_data *q_data = NULL;
+ struct v4l2_plane_pix_format plane_fmt = {};
int i;
q_data = mtk_jpeg_get_q_data(ctx, vb->vb2_queue->type);
if (!q_data)
return -EINVAL;
- for (i = 0; i < q_data->fmt->colplanes; i++)
- vb2_set_plane_payload(vb, i, q_data->sizeimage[i]);
+ for (i = 0; i < q_data->fmt->colplanes; i++) {
+ plane_fmt = q_data->pix_mp.plane_fmt[i];
+ if (ctx->enable_exif &&
+ q_data->fmt->fourcc == V4L2_PIX_FMT_JPEG)
+ vb2_set_plane_payload(vb, i, plane_fmt.sizeimage +
+ MTK_JPEG_MAX_EXIF_SIZE);
+ else
+ vb2_set_plane_payload(vb, i, plane_fmt.sizeimage);
+ }
return 0;
}
@@ -611,14 +708,17 @@
struct mtk_jpeg_q_data *q_data;
q_data = &ctx->out_q;
- if (q_data->w != param->pic_w || q_data->h != param->pic_h) {
+ if (q_data->pix_mp.width != param->pic_w ||
+ q_data->pix_mp.height != param->pic_h) {
v4l2_dbg(1, debug, &jpeg->v4l2_dev, "Picture size change\n");
return true;
}
q_data = &ctx->cap_q;
- if (q_data->fmt != mtk_jpeg_find_format(ctx, param->dst_fourcc,
- MTK_JPEG_FMT_TYPE_CAPTURE)) {
+ if (q_data->fmt !=
+ mtk_jpeg_find_format(jpeg->variant->formats,
+ jpeg->variant->num_formats, param->dst_fourcc,
+ MTK_JPEG_FMT_FLAG_CAPTURE)) {
v4l2_dbg(1, debug, &jpeg->v4l2_dev, "format change\n");
return true;
}
@@ -633,19 +733,20 @@
int i;
q_data = &ctx->out_q;
- q_data->w = param->pic_w;
- q_data->h = param->pic_h;
+ q_data->pix_mp.width = param->pic_w;
+ q_data->pix_mp.height = param->pic_h;
q_data = &ctx->cap_q;
- q_data->w = param->dec_w;
- q_data->h = param->dec_h;
- q_data->fmt = mtk_jpeg_find_format(ctx,
+ q_data->pix_mp.width = param->dec_w;
+ q_data->pix_mp.height = param->dec_h;
+ q_data->fmt = mtk_jpeg_find_format(jpeg->variant->formats,
+ jpeg->variant->num_formats,
param->dst_fourcc,
- MTK_JPEG_FMT_TYPE_CAPTURE);
+ MTK_JPEG_FMT_FLAG_CAPTURE);
for (i = 0; i < q_data->fmt->colplanes; i++) {
- q_data->bytesperline[i] = param->mem_stride[i];
- q_data->sizeimage[i] = param->comp_size[i];
+ q_data->pix_mp.plane_fmt[i].bytesperline = param->mem_stride[i];
+ q_data->pix_mp.plane_fmt[i].sizeimage = param->comp_size[i];
}
v4l2_dbg(1, debug, &jpeg->v4l2_dev,
@@ -658,7 +759,18 @@
param->dec_w, param->dec_h);
}
-static void mtk_jpeg_buf_queue(struct vb2_buffer *vb)
+static void mtk_jpeg_enc_buf_queue(struct vb2_buffer *vb)
+{
+ struct mtk_jpeg_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+ struct mtk_jpeg_dev *jpeg = ctx->jpeg;
+
+ v4l2_dbg(2, debug, &jpeg->v4l2_dev, "(%d) buf_q id=%d, vb=%p\n",
+ vb->vb2_queue->type, vb->index, vb);
+
+ v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, to_vb2_v4l2_buffer(vb));
+}
+
+static void mtk_jpeg_dec_buf_queue(struct vb2_buffer *vb)
{
struct mtk_jpeg_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
struct mtk_jpeg_dec_param *param;
@@ -676,10 +788,6 @@
param = &jpeg_src_buf->dec_param;
memset(param, 0, sizeof(*param));
- if (jpeg_src_buf->flags & MTK_JPEG_BUF_FLAGS_LAST_FRAME) {
- v4l2_dbg(1, debug, &jpeg->v4l2_dev, "Got eos\n");
- goto end;
- }
header_valid = mtk_jpeg_parse(param, (u8 *)vb2_plane_vaddr(vb, 0),
vb2_get_plane_payload(vb, 0));
if (!header_valid) {
@@ -710,24 +818,16 @@
return v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
}
-static int mtk_jpeg_start_streaming(struct vb2_queue *q, unsigned int count)
+static void mtk_jpeg_enc_stop_streaming(struct vb2_queue *q)
{
struct mtk_jpeg_ctx *ctx = vb2_get_drv_priv(q);
struct vb2_v4l2_buffer *vb;
- int ret = 0;
- ret = pm_runtime_get_sync(ctx->jpeg->dev);
- if (ret < 0)
- goto err;
-
- return 0;
-err:
while ((vb = mtk_jpeg_buf_remove(ctx, q->type)))
- v4l2_m2m_buf_done(vb, VB2_BUF_STATE_QUEUED);
- return ret;
+ v4l2_m2m_buf_done(vb, VB2_BUF_STATE_ERROR);
}
-static void mtk_jpeg_stop_streaming(struct vb2_queue *q)
+static void mtk_jpeg_dec_stop_streaming(struct vb2_queue *q)
{
struct mtk_jpeg_ctx *ctx = vb2_get_drv_priv(q);
struct vb2_v4l2_buffer *vb;
@@ -738,7 +838,7 @@
* subsampling. Update capture queue when the stream is off.
*/
if (ctx->state == MTK_JPEG_SOURCE_CHANGE &&
- !V4L2_TYPE_IS_OUTPUT(q->type)) {
+ V4L2_TYPE_IS_CAPTURE(q->type)) {
struct mtk_jpeg_src_buf *src_buf;
vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
@@ -751,18 +851,24 @@
while ((vb = mtk_jpeg_buf_remove(ctx, q->type)))
v4l2_m2m_buf_done(vb, VB2_BUF_STATE_ERROR);
-
- pm_runtime_put_sync(ctx->jpeg->dev);
}
-static const struct vb2_ops mtk_jpeg_qops = {
+static const struct vb2_ops mtk_jpeg_dec_qops = {
.queue_setup = mtk_jpeg_queue_setup,
.buf_prepare = mtk_jpeg_buf_prepare,
- .buf_queue = mtk_jpeg_buf_queue,
+ .buf_queue = mtk_jpeg_dec_buf_queue,
.wait_prepare = vb2_ops_wait_prepare,
.wait_finish = vb2_ops_wait_finish,
- .start_streaming = mtk_jpeg_start_streaming,
- .stop_streaming = mtk_jpeg_stop_streaming,
+ .stop_streaming = mtk_jpeg_dec_stop_streaming,
+};
+
+static const struct vb2_ops mtk_jpeg_enc_qops = {
+ .queue_setup = mtk_jpeg_queue_setup,
+ .buf_prepare = mtk_jpeg_buf_prepare,
+ .buf_queue = mtk_jpeg_enc_buf_queue,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
+ .stop_streaming = mtk_jpeg_enc_stop_streaming,
};
static void mtk_jpeg_set_dec_src(struct mtk_jpeg_ctx *ctx,
@@ -771,8 +877,8 @@
{
bs->str_addr = vb2_dma_contig_plane_dma_addr(src_buf, 0);
bs->end_addr = bs->str_addr +
- mtk_jpeg_align(vb2_get_plane_payload(src_buf, 0), 16);
- bs->size = mtk_jpeg_align(vb2_plane_size(src_buf, 0), 128);
+ round_up(vb2_get_plane_payload(src_buf, 0), 16);
+ bs->size = round_up(vb2_plane_size(src_buf, 0), 128);
}
static int mtk_jpeg_set_dec_dst(struct mtk_jpeg_ctx *ctx,
@@ -802,7 +908,49 @@
return 0;
}
-static void mtk_jpeg_device_run(void *priv)
+static void mtk_jpeg_enc_device_run(void *priv)
+{
+ struct mtk_jpeg_ctx *ctx = priv;
+ struct mtk_jpeg_dev *jpeg = ctx->jpeg;
+ struct vb2_v4l2_buffer *src_buf, *dst_buf;
+ enum vb2_buffer_state buf_state = VB2_BUF_STATE_ERROR;
+ unsigned long flags;
+ int ret;
+
+ src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
+ dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
+
+ ret = pm_runtime_get_sync(jpeg->dev);
+ if (ret < 0)
+ goto enc_end;
+
+ schedule_delayed_work(&jpeg->job_timeout_work,
+ msecs_to_jiffies(MTK_JPEG_HW_TIMEOUT_MSEC));
+
+ spin_lock_irqsave(&jpeg->hw_lock, flags);
+
+ /*
+ * Resetting the hardware every frame is to ensure that all the
+ * registers are cleared. This is a hardware requirement.
+ */
+ mtk_jpeg_enc_reset(jpeg->reg_base);
+
+ mtk_jpeg_set_enc_src(ctx, jpeg->reg_base, &src_buf->vb2_buf);
+ mtk_jpeg_set_enc_dst(ctx, jpeg->reg_base, &dst_buf->vb2_buf);
+ mtk_jpeg_set_enc_params(ctx, jpeg->reg_base);
+ mtk_jpeg_enc_start(jpeg->reg_base);
+ spin_unlock_irqrestore(&jpeg->hw_lock, flags);
+ return;
+
+enc_end:
+ v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
+ v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
+ v4l2_m2m_buf_done(src_buf, buf_state);
+ v4l2_m2m_buf_done(dst_buf, buf_state);
+ v4l2_m2m_job_finish(jpeg->m2m_dev, ctx->fh.m2m_ctx);
+}
+
+static void mtk_jpeg_dec_device_run(void *priv)
{
struct mtk_jpeg_ctx *ctx = priv;
struct mtk_jpeg_dev *jpeg = ctx->jpeg;
@@ -812,19 +960,12 @@
struct mtk_jpeg_src_buf *jpeg_src_buf;
struct mtk_jpeg_bs bs;
struct mtk_jpeg_fb fb;
- int i;
+ int ret;
src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
jpeg_src_buf = mtk_jpeg_vb2_to_srcbuf(&src_buf->vb2_buf);
- if (jpeg_src_buf->flags & MTK_JPEG_BUF_FLAGS_LAST_FRAME) {
- for (i = 0; i < dst_buf->vb2_buf.num_planes; i++)
- vb2_set_plane_payload(&dst_buf->vb2_buf, i, 0);
- buf_state = VB2_BUF_STATE_DONE;
- goto dec_end;
- }
-
if (mtk_jpeg_check_resolution_change(ctx, &jpeg_src_buf->dec_param)) {
mtk_jpeg_queue_src_chg_event(ctx);
ctx->state = MTK_JPEG_SOURCE_CHANGE;
@@ -832,16 +973,23 @@
return;
}
+ ret = pm_runtime_get_sync(jpeg->dev);
+ if (ret < 0)
+ goto dec_end;
+
+ schedule_delayed_work(&jpeg->job_timeout_work,
+ msecs_to_jiffies(MTK_JPEG_HW_TIMEOUT_MSEC));
+
mtk_jpeg_set_dec_src(ctx, &src_buf->vb2_buf, &bs);
if (mtk_jpeg_set_dec_dst(ctx, &jpeg_src_buf->dec_param, &dst_buf->vb2_buf, &fb))
goto dec_end;
spin_lock_irqsave(&jpeg->hw_lock, flags);
- mtk_jpeg_dec_reset(jpeg->dec_reg_base);
- mtk_jpeg_dec_set_config(jpeg->dec_reg_base,
+ mtk_jpeg_dec_reset(jpeg->reg_base);
+ mtk_jpeg_dec_set_config(jpeg->reg_base,
&jpeg_src_buf->dec_param, &bs, &fb);
- mtk_jpeg_dec_start(jpeg->dec_reg_base);
+ mtk_jpeg_dec_start(jpeg->reg_base);
spin_unlock_irqrestore(&jpeg->hw_lock, flags);
return;
@@ -853,29 +1001,34 @@
v4l2_m2m_job_finish(jpeg->m2m_dev, ctx->fh.m2m_ctx);
}
-static int mtk_jpeg_job_ready(void *priv)
+static int mtk_jpeg_dec_job_ready(void *priv)
{
struct mtk_jpeg_ctx *ctx = priv;
return (ctx->state == MTK_JPEG_RUNNING) ? 1 : 0;
}
-static const struct v4l2_m2m_ops mtk_jpeg_m2m_ops = {
- .device_run = mtk_jpeg_device_run,
- .job_ready = mtk_jpeg_job_ready,
+static const struct v4l2_m2m_ops mtk_jpeg_enc_m2m_ops = {
+ .device_run = mtk_jpeg_enc_device_run,
+};
+
+static const struct v4l2_m2m_ops mtk_jpeg_dec_m2m_ops = {
+ .device_run = mtk_jpeg_dec_device_run,
+ .job_ready = mtk_jpeg_dec_job_ready,
};
static int mtk_jpeg_queue_init(void *priv, struct vb2_queue *src_vq,
struct vb2_queue *dst_vq)
{
struct mtk_jpeg_ctx *ctx = priv;
+ struct mtk_jpeg_dev *jpeg = ctx->jpeg;
int ret;
src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
src_vq->io_modes = VB2_DMABUF | VB2_MMAP;
src_vq->drv_priv = ctx;
src_vq->buf_struct_size = sizeof(struct mtk_jpeg_src_buf);
- src_vq->ops = &mtk_jpeg_qops;
+ src_vq->ops = jpeg->variant->qops;
src_vq->mem_ops = &vb2_dma_contig_memops;
src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
src_vq->lock = &ctx->jpeg->lock;
@@ -888,7 +1041,7 @@
dst_vq->io_modes = VB2_DMABUF | VB2_MMAP;
dst_vq->drv_priv = ctx;
dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
- dst_vq->ops = &mtk_jpeg_qops;
+ dst_vq->ops = jpeg->variant->qops;
dst_vq->mem_ops = &vb2_dma_contig_memops;
dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
dst_vq->lock = &ctx->jpeg->lock;
@@ -905,17 +1058,68 @@
ret = mtk_smi_larb_get(jpeg->larb);
if (ret)
dev_err(jpeg->dev, "mtk_smi_larb_get larbvdec fail %d\n", ret);
- clk_prepare_enable(jpeg->clk_jdec_smi);
- clk_prepare_enable(jpeg->clk_jdec);
+
+ ret = clk_bulk_prepare_enable(jpeg->variant->num_clks,
+ jpeg->variant->clks);
+ if (ret)
+ dev_err(jpeg->dev, "Failed to open jpeg clk: %d\n", ret);
}
static void mtk_jpeg_clk_off(struct mtk_jpeg_dev *jpeg)
{
- clk_disable_unprepare(jpeg->clk_jdec);
- clk_disable_unprepare(jpeg->clk_jdec_smi);
+ clk_bulk_disable_unprepare(jpeg->variant->num_clks,
+ jpeg->variant->clks);
mtk_smi_larb_put(jpeg->larb);
}
+static irqreturn_t mtk_jpeg_enc_done(struct mtk_jpeg_dev *jpeg)
+{
+ struct mtk_jpeg_ctx *ctx;
+ struct vb2_v4l2_buffer *src_buf, *dst_buf;
+ enum vb2_buffer_state buf_state = VB2_BUF_STATE_ERROR;
+ u32 result_size;
+
+ ctx = v4l2_m2m_get_curr_priv(jpeg->m2m_dev);
+ if (!ctx) {
+ v4l2_err(&jpeg->v4l2_dev, "Context is NULL\n");
+ return IRQ_HANDLED;
+ }
+
+ src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
+ dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
+
+ result_size = mtk_jpeg_enc_get_file_size(jpeg->reg_base);
+ vb2_set_plane_payload(&dst_buf->vb2_buf, 0, result_size);
+
+ buf_state = VB2_BUF_STATE_DONE;
+
+ v4l2_m2m_buf_done(src_buf, buf_state);
+ v4l2_m2m_buf_done(dst_buf, buf_state);
+ v4l2_m2m_job_finish(jpeg->m2m_dev, ctx->fh.m2m_ctx);
+ pm_runtime_put(ctx->jpeg->dev);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t mtk_jpeg_enc_irq(int irq, void *priv)
+{
+ struct mtk_jpeg_dev *jpeg = priv;
+ u32 irq_status;
+ irqreturn_t ret = IRQ_NONE;
+
+ cancel_delayed_work(&jpeg->job_timeout_work);
+
+ irq_status = readl(jpeg->reg_base + JPEG_ENC_INT_STS) &
+ JPEG_ENC_INT_STATUS_MASK_ALLIRQ;
+ if (irq_status)
+ writel(0, jpeg->reg_base + JPEG_ENC_INT_STS);
+
+ if (!(irq_status & JPEG_ENC_INT_STATUS_DONE))
+ return ret;
+
+ ret = mtk_jpeg_enc_done(jpeg);
+ return ret;
+}
+
static irqreturn_t mtk_jpeg_dec_irq(int irq, void *priv)
{
struct mtk_jpeg_dev *jpeg = priv;
@@ -927,7 +1131,9 @@
u32 dec_ret;
int i;
- dec_ret = mtk_jpeg_dec_get_int_status(jpeg->dec_reg_base);
+ cancel_delayed_work(&jpeg->job_timeout_work);
+
+ dec_ret = mtk_jpeg_dec_get_int_status(jpeg->reg_base);
dec_irq_ret = mtk_jpeg_dec_enum_result(dec_ret);
ctx = v4l2_m2m_get_curr_priv(jpeg->m2m_dev);
if (!ctx) {
@@ -940,7 +1146,7 @@
jpeg_src_buf = mtk_jpeg_vb2_to_srcbuf(&src_buf->vb2_buf);
if (dec_irq_ret >= MTK_JPEG_DEC_RESULT_UNDERFLOW)
- mtk_jpeg_dec_reset(jpeg->dec_reg_base);
+ mtk_jpeg_dec_reset(jpeg->reg_base);
if (dec_irq_ret != MTK_JPEG_DEC_RESULT_EOF_DONE) {
dev_err(jpeg->dev, "decode failed\n");
@@ -957,39 +1163,42 @@
v4l2_m2m_buf_done(src_buf, buf_state);
v4l2_m2m_buf_done(dst_buf, buf_state);
v4l2_m2m_job_finish(jpeg->m2m_dev, ctx->fh.m2m_ctx);
+ pm_runtime_put(ctx->jpeg->dev);
return IRQ_HANDLED;
}
static void mtk_jpeg_set_default_params(struct mtk_jpeg_ctx *ctx)
{
struct mtk_jpeg_q_data *q = &ctx->out_q;
- int i;
+ struct mtk_jpeg_dev *jpeg = ctx->jpeg;
- ctx->colorspace = V4L2_COLORSPACE_JPEG,
- ctx->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
- ctx->quantization = V4L2_QUANTIZATION_DEFAULT;
- ctx->xfer_func = V4L2_XFER_FUNC_DEFAULT;
+ ctx->fh.ctrl_handler = &ctx->ctrl_hdl;
+ q->pix_mp.colorspace = V4L2_COLORSPACE_SRGB;
+ q->pix_mp.ycbcr_enc = V4L2_YCBCR_ENC_601;
+ q->pix_mp.quantization = V4L2_QUANTIZATION_FULL_RANGE;
+ q->pix_mp.xfer_func = V4L2_XFER_FUNC_SRGB;
- q->fmt = mtk_jpeg_find_format(ctx, V4L2_PIX_FMT_JPEG,
- MTK_JPEG_FMT_TYPE_OUTPUT);
- q->w = MTK_JPEG_MIN_WIDTH;
- q->h = MTK_JPEG_MIN_HEIGHT;
- q->bytesperline[0] = 0;
- q->sizeimage[0] = MTK_JPEG_DEFAULT_SIZEIMAGE;
+ q->fmt = mtk_jpeg_find_format(jpeg->variant->formats,
+ jpeg->variant->num_formats,
+ jpeg->variant->out_q_default_fourcc,
+ MTK_JPEG_FMT_FLAG_OUTPUT);
+ q->pix_mp.width = MTK_JPEG_MIN_WIDTH;
+ q->pix_mp.height = MTK_JPEG_MIN_HEIGHT;
+ mtk_jpeg_try_fmt_mplane(&q->pix_mp, q->fmt);
q = &ctx->cap_q;
- q->fmt = mtk_jpeg_find_format(ctx, V4L2_PIX_FMT_YUV420M,
- MTK_JPEG_FMT_TYPE_CAPTURE);
- q->w = MTK_JPEG_MIN_WIDTH;
- q->h = MTK_JPEG_MIN_HEIGHT;
+ q->fmt = mtk_jpeg_find_format(jpeg->variant->formats,
+ jpeg->variant->num_formats,
+ jpeg->variant->cap_q_default_fourcc,
+ MTK_JPEG_FMT_FLAG_CAPTURE);
+ q->pix_mp.colorspace = V4L2_COLORSPACE_SRGB;
+ q->pix_mp.ycbcr_enc = V4L2_YCBCR_ENC_601;
+ q->pix_mp.quantization = V4L2_QUANTIZATION_FULL_RANGE;
+ q->pix_mp.xfer_func = V4L2_XFER_FUNC_SRGB;
+ q->pix_mp.width = MTK_JPEG_MIN_WIDTH;
+ q->pix_mp.height = MTK_JPEG_MIN_HEIGHT;
- for (i = 0; i < q->fmt->colplanes; i++) {
- u32 stride = q->w * q->fmt->h_sample[i] / 4;
- u32 h = q->h * q->fmt->v_sample[i] / 4;
-
- q->bytesperline[i] = stride;
- q->sizeimage[i] = stride * h;
- }
+ mtk_jpeg_try_fmt_mplane(&q->pix_mp, q->fmt);
}
static int mtk_jpeg_open(struct file *file)
@@ -1020,6 +1229,15 @@
goto error;
}
+ if (jpeg->variant->cap_q_default_fourcc == V4L2_PIX_FMT_JPEG) {
+ ret = mtk_jpeg_enc_ctrls_setup(ctx);
+ if (ret) {
+ v4l2_err(&jpeg->v4l2_dev, "Failed to setup jpeg enc controls\n");
+ goto error;
+ }
+ } else {
+ v4l2_ctrl_handler_init(&ctx->ctrl_hdl, 0);
+ }
mtk_jpeg_set_default_params(ctx);
mutex_unlock(&jpeg->lock);
return 0;
@@ -1040,6 +1258,7 @@
mutex_lock(&jpeg->lock);
v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
+ v4l2_ctrl_handler_free(&ctx->ctrl_hdl);
v4l2_fh_del(&ctx->fh);
v4l2_fh_exit(&ctx->fh);
kfree(ctx);
@@ -1056,10 +1275,20 @@
.mmap = v4l2_m2m_fop_mmap,
};
+static struct clk_bulk_data mt8173_jpeg_dec_clocks[] = {
+ { .id = "jpgdec-smi" },
+ { .id = "jpgdec" },
+};
+
+static struct clk_bulk_data mtk_jpeg_clocks[] = {
+ { .id = "jpgenc" },
+};
+
static int mtk_jpeg_clk_init(struct mtk_jpeg_dev *jpeg)
{
struct device_node *node;
struct platform_device *pdev;
+ int ret;
node = of_parse_phandle(jpeg->dev->of_node, "mediatek,larb", 0);
if (!node)
@@ -1073,19 +1302,47 @@
jpeg->larb = &pdev->dev;
- jpeg->clk_jdec = devm_clk_get(jpeg->dev, "jpgdec");
- if (IS_ERR(jpeg->clk_jdec))
- return PTR_ERR(jpeg->clk_jdec);
+ ret = devm_clk_bulk_get(jpeg->dev, jpeg->variant->num_clks,
+ jpeg->variant->clks);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to get jpeg clock:%d\n", ret);
+ put_device(&pdev->dev);
+ return ret;
+ }
- jpeg->clk_jdec_smi = devm_clk_get(jpeg->dev, "jpgdec-smi");
- return PTR_ERR_OR_ZERO(jpeg->clk_jdec_smi);
+ return 0;
+}
+
+static void mtk_jpeg_job_timeout_work(struct work_struct *work)
+{
+ struct mtk_jpeg_dev *jpeg = container_of(work, struct mtk_jpeg_dev,
+ job_timeout_work.work);
+ struct mtk_jpeg_ctx *ctx;
+ struct vb2_v4l2_buffer *src_buf, *dst_buf;
+
+ ctx = v4l2_m2m_get_curr_priv(jpeg->m2m_dev);
+ src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
+ dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
+
+ jpeg->variant->hw_reset(jpeg->reg_base);
+
+ pm_runtime_put(jpeg->dev);
+
+ v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_ERROR);
+ v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_ERROR);
+ v4l2_m2m_job_finish(jpeg->m2m_dev, ctx->fh.m2m_ctx);
+}
+
+static inline void mtk_jpeg_clk_release(struct mtk_jpeg_dev *jpeg)
+{
+ put_device(jpeg->larb);
}
static int mtk_jpeg_probe(struct platform_device *pdev)
{
struct mtk_jpeg_dev *jpeg;
struct resource *res;
- int dec_irq;
+ int jpeg_irq;
int ret;
jpeg = devm_kzalloc(&pdev->dev, sizeof(*jpeg), GFP_KERNEL);
@@ -1095,28 +1352,27 @@
mutex_init(&jpeg->lock);
spin_lock_init(&jpeg->hw_lock);
jpeg->dev = &pdev->dev;
+ jpeg->variant = of_device_get_match_data(jpeg->dev);
+ INIT_DELAYED_WORK(&jpeg->job_timeout_work, mtk_jpeg_job_timeout_work);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- jpeg->dec_reg_base = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(jpeg->dec_reg_base)) {
- ret = PTR_ERR(jpeg->dec_reg_base);
+ jpeg->reg_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(jpeg->reg_base)) {
+ ret = PTR_ERR(jpeg->reg_base);
return ret;
}
- res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- dec_irq = platform_get_irq(pdev, 0);
- if (!res || dec_irq < 0) {
- dev_err(&pdev->dev, "Failed to get dec_irq %d.\n", dec_irq);
- ret = -EINVAL;
- return ret;
+ jpeg_irq = platform_get_irq(pdev, 0);
+ if (jpeg_irq < 0) {
+ dev_err(&pdev->dev, "Failed to get jpeg_irq %d.\n", jpeg_irq);
+ return jpeg_irq;
}
- ret = devm_request_irq(&pdev->dev, dec_irq, mtk_jpeg_dec_irq, 0,
- pdev->name, jpeg);
+ ret = devm_request_irq(&pdev->dev, jpeg_irq,
+ jpeg->variant->irq_handler, 0, pdev->name, jpeg);
if (ret) {
- dev_err(&pdev->dev, "Failed to request dec_irq %d (%d)\n",
- dec_irq, ret);
- ret = -EINVAL;
+ dev_err(&pdev->dev, "Failed to request jpeg_irq %d (%d)\n",
+ jpeg_irq, ret);
goto err_req_irq;
}
@@ -1133,40 +1389,42 @@
goto err_dev_register;
}
- jpeg->m2m_dev = v4l2_m2m_init(&mtk_jpeg_m2m_ops);
+ jpeg->m2m_dev = v4l2_m2m_init(jpeg->variant->m2m_ops);
+
if (IS_ERR(jpeg->m2m_dev)) {
v4l2_err(&jpeg->v4l2_dev, "Failed to init mem2mem device\n");
ret = PTR_ERR(jpeg->m2m_dev);
goto err_m2m_init;
}
- jpeg->dec_vdev = video_device_alloc();
- if (!jpeg->dec_vdev) {
+ jpeg->vdev = video_device_alloc();
+ if (!jpeg->vdev) {
ret = -ENOMEM;
- goto err_dec_vdev_alloc;
+ goto err_vfd_jpeg_alloc;
}
- snprintf(jpeg->dec_vdev->name, sizeof(jpeg->dec_vdev->name),
- "%s-dec", MTK_JPEG_NAME);
- jpeg->dec_vdev->fops = &mtk_jpeg_fops;
- jpeg->dec_vdev->ioctl_ops = &mtk_jpeg_ioctl_ops;
- jpeg->dec_vdev->minor = -1;
- jpeg->dec_vdev->release = video_device_release;
- jpeg->dec_vdev->lock = &jpeg->lock;
- jpeg->dec_vdev->v4l2_dev = &jpeg->v4l2_dev;
- jpeg->dec_vdev->vfl_dir = VFL_DIR_M2M;
- jpeg->dec_vdev->device_caps = V4L2_CAP_STREAMING |
- V4L2_CAP_VIDEO_M2M_MPLANE;
+ snprintf(jpeg->vdev->name, sizeof(jpeg->vdev->name),
+ "%s", jpeg->variant->dev_name);
+ jpeg->vdev->fops = &mtk_jpeg_fops;
+ jpeg->vdev->ioctl_ops = jpeg->variant->ioctl_ops;
+ jpeg->vdev->minor = -1;
+ jpeg->vdev->release = video_device_release;
+ jpeg->vdev->lock = &jpeg->lock;
+ jpeg->vdev->v4l2_dev = &jpeg->v4l2_dev;
+ jpeg->vdev->vfl_dir = VFL_DIR_M2M;
+ jpeg->vdev->device_caps = V4L2_CAP_STREAMING |
+ V4L2_CAP_VIDEO_M2M_MPLANE;
- ret = video_register_device(jpeg->dec_vdev, VFL_TYPE_GRABBER, 3);
+ ret = video_register_device(jpeg->vdev, VFL_TYPE_VIDEO, -1);
if (ret) {
v4l2_err(&jpeg->v4l2_dev, "Failed to register video device\n");
- goto err_dec_vdev_register;
+ goto err_vfd_jpeg_register;
}
- video_set_drvdata(jpeg->dec_vdev, jpeg);
+ video_set_drvdata(jpeg->vdev, jpeg);
v4l2_info(&jpeg->v4l2_dev,
- "decoder device registered as /dev/video%d (%d,%d)\n",
- jpeg->dec_vdev->num, VIDEO_MAJOR, jpeg->dec_vdev->minor);
+ "%s device registered as /dev/video%d (%d,%d)\n",
+ jpeg->variant->dev_name, jpeg->vdev->num,
+ VIDEO_MAJOR, jpeg->vdev->minor);
platform_set_drvdata(pdev, jpeg);
@@ -1174,16 +1432,17 @@
return 0;
-err_dec_vdev_register:
- video_device_release(jpeg->dec_vdev);
+err_vfd_jpeg_register:
+ video_device_release(jpeg->vdev);
-err_dec_vdev_alloc:
+err_vfd_jpeg_alloc:
v4l2_m2m_release(jpeg->m2m_dev);
err_m2m_init:
v4l2_device_unregister(&jpeg->v4l2_dev);
err_dev_register:
+ mtk_jpeg_clk_release(jpeg);
err_clk_init:
@@ -1197,10 +1456,11 @@
struct mtk_jpeg_dev *jpeg = platform_get_drvdata(pdev);
pm_runtime_disable(&pdev->dev);
- video_unregister_device(jpeg->dec_vdev);
- video_device_release(jpeg->dec_vdev);
+ video_unregister_device(jpeg->vdev);
+ video_device_release(jpeg->vdev);
v4l2_m2m_release(jpeg->m2m_dev);
v4l2_device_unregister(&jpeg->v4l2_dev);
+ mtk_jpeg_clk_release(jpeg);
return 0;
}
@@ -1209,7 +1469,6 @@
{
struct mtk_jpeg_dev *jpeg = dev_get_drvdata(dev);
- mtk_jpeg_dec_reset(jpeg->dec_reg_base);
mtk_jpeg_clk_off(jpeg);
return 0;
@@ -1220,31 +1479,28 @@
struct mtk_jpeg_dev *jpeg = dev_get_drvdata(dev);
mtk_jpeg_clk_on(jpeg);
- mtk_jpeg_dec_reset(jpeg->dec_reg_base);
return 0;
}
static __maybe_unused int mtk_jpeg_suspend(struct device *dev)
{
- int ret;
+ struct mtk_jpeg_dev *jpeg = dev_get_drvdata(dev);
- if (pm_runtime_suspended(dev))
- return 0;
-
- ret = mtk_jpeg_pm_suspend(dev);
- return ret;
+ v4l2_m2m_suspend(jpeg->m2m_dev);
+ return pm_runtime_force_suspend(dev);
}
static __maybe_unused int mtk_jpeg_resume(struct device *dev)
{
+ struct mtk_jpeg_dev *jpeg = dev_get_drvdata(dev);
int ret;
- if (pm_runtime_suspended(dev))
- return 0;
+ ret = pm_runtime_force_resume(dev);
+ if (ret < 0)
+ return ret;
- ret = mtk_jpeg_pm_resume(dev);
-
+ v4l2_m2m_resume(jpeg->m2m_dev);
return ret;
}
@@ -1253,14 +1509,48 @@
SET_RUNTIME_PM_OPS(mtk_jpeg_pm_suspend, mtk_jpeg_pm_resume, NULL)
};
+static const struct mtk_jpeg_variant mt8173_jpeg_drvdata = {
+ .clks = mt8173_jpeg_dec_clocks,
+ .num_clks = ARRAY_SIZE(mt8173_jpeg_dec_clocks),
+ .formats = mtk_jpeg_dec_formats,
+ .num_formats = MTK_JPEG_DEC_NUM_FORMATS,
+ .qops = &mtk_jpeg_dec_qops,
+ .irq_handler = mtk_jpeg_dec_irq,
+ .hw_reset = mtk_jpeg_dec_reset,
+ .m2m_ops = &mtk_jpeg_dec_m2m_ops,
+ .dev_name = "mtk-jpeg-dec",
+ .ioctl_ops = &mtk_jpeg_dec_ioctl_ops,
+ .out_q_default_fourcc = V4L2_PIX_FMT_JPEG,
+ .cap_q_default_fourcc = V4L2_PIX_FMT_YUV420M,
+};
+
+static const struct mtk_jpeg_variant mtk_jpeg_drvdata = {
+ .clks = mtk_jpeg_clocks,
+ .num_clks = ARRAY_SIZE(mtk_jpeg_clocks),
+ .formats = mtk_jpeg_enc_formats,
+ .num_formats = MTK_JPEG_ENC_NUM_FORMATS,
+ .qops = &mtk_jpeg_enc_qops,
+ .irq_handler = mtk_jpeg_enc_irq,
+ .hw_reset = mtk_jpeg_enc_reset,
+ .m2m_ops = &mtk_jpeg_enc_m2m_ops,
+ .dev_name = "mtk-jpeg-enc",
+ .ioctl_ops = &mtk_jpeg_enc_ioctl_ops,
+ .out_q_default_fourcc = V4L2_PIX_FMT_YUYV,
+ .cap_q_default_fourcc = V4L2_PIX_FMT_JPEG,
+};
+
static const struct of_device_id mtk_jpeg_match[] = {
{
.compatible = "mediatek,mt8173-jpgdec",
- .data = NULL,
+ .data = &mt8173_jpeg_drvdata,
},
{
.compatible = "mediatek,mt2701-jpgdec",
- .data = NULL,
+ .data = &mt8173_jpeg_drvdata,
+ },
+ {
+ .compatible = "mediatek,mtk-jpgenc",
+ .data = &mtk_jpeg_drvdata,
},
{},
};
diff --git a/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.h b/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.h
index 999bd14..68e634f 100644
--- a/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.h
+++ b/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.h
@@ -3,6 +3,7 @@
* Copyright (c) 2016 MediaTek Inc.
* Author: Ming Hsiu Tsai <minghsiu.tsai@mediatek.com>
* Rick Chang <rick.chang@mediatek.com>
+ * Xia Jiang <xia.jiang@mediatek.com>
*/
#ifndef _MTK_JPEG_CORE_H
@@ -15,19 +16,28 @@
#define MTK_JPEG_NAME "mtk-jpeg"
-#define MTK_JPEG_FMT_FLAG_DEC_OUTPUT BIT(0)
-#define MTK_JPEG_FMT_FLAG_DEC_CAPTURE BIT(1)
+#define MTK_JPEG_COMP_MAX 3
-#define MTK_JPEG_FMT_TYPE_OUTPUT 1
-#define MTK_JPEG_FMT_TYPE_CAPTURE 2
+#define MTK_JPEG_FMT_FLAG_OUTPUT BIT(0)
+#define MTK_JPEG_FMT_FLAG_CAPTURE BIT(1)
-#define MTK_JPEG_MIN_WIDTH 32
-#define MTK_JPEG_MIN_HEIGHT 32
-#define MTK_JPEG_MAX_WIDTH 8192
-#define MTK_JPEG_MAX_HEIGHT 8192
+#define MTK_JPEG_MIN_WIDTH 32U
+#define MTK_JPEG_MIN_HEIGHT 32U
+#define MTK_JPEG_MAX_WIDTH 65535U
+#define MTK_JPEG_MAX_HEIGHT 65535U
#define MTK_JPEG_DEFAULT_SIZEIMAGE (1 * 1024 * 1024)
+#define MTK_JPEG_HW_TIMEOUT_MSEC 1000
+
+#define MTK_JPEG_MAX_EXIF_SIZE (64 * 1024)
+
+/**
+ * enum mtk_jpeg_ctx_state - states of the context state machine
+ * @MTK_JPEG_INIT: current state is initialized
+ * @MTK_JPEG_RUNNING: current state is running
+ * @MTK_JPEG_SOURCE_CHANGE: current state is source resolution change
+ */
enum mtk_jpeg_ctx_state {
MTK_JPEG_INIT = 0,
MTK_JPEG_RUNNING,
@@ -35,6 +45,36 @@
};
/**
+ * mtk_jpeg_variant - mtk jpeg driver variant
+ * @clks: clock names
+ * @num_clks: numbers of clock
+ * @format: jpeg driver's internal color format
+ * @num_format: number of format
+ * @qops: the callback of jpeg vb2_ops
+ * @irq_handler: jpeg irq handler callback
+ * @hw_reset: jpeg hardware reset callback
+ * @m2m_ops: the callback of jpeg v4l2_m2m_ops
+ * @dev_name: jpeg device name
+ * @ioctl_ops: the callback of jpeg v4l2_ioctl_ops
+ * @out_q_default_fourcc: output queue default fourcc
+ * @cap_q_default_fourcc: capture queue default fourcc
+ */
+struct mtk_jpeg_variant {
+ struct clk_bulk_data *clks;
+ int num_clks;
+ struct mtk_jpeg_fmt *formats;
+ int num_formats;
+ const struct vb2_ops *qops;
+ irqreturn_t (*irq_handler)(int irq, void *priv);
+ void (*hw_reset)(void __iomem *base);
+ const struct v4l2_m2m_ops *m2m_ops;
+ const char *dev_name;
+ const struct v4l2_ioctl_ops *ioctl_ops;
+ u32 out_q_default_fourcc;
+ u32 cap_q_default_fourcc;
+};
+
+/**
* struct mt_jpeg - JPEG IP abstraction
* @lock: the mutex protecting this structure
* @hw_lock: spinlock protecting the hw device resource
@@ -43,11 +83,11 @@
* @v4l2_dev: v4l2 device for mem2mem mode
* @m2m_dev: v4l2 mem2mem device data
* @alloc_ctx: videobuf2 memory allocator's context
- * @dec_vdev: video device node for decoder mem2mem mode
- * @dec_reg_base: JPEG registers mapping
- * @clk_jdec: JPEG hw working clock
- * @clk_jdec_smi: JPEG SMI bus clock
+ * @vdev: video device node for jpeg mem2mem mode
+ * @reg_base: JPEG registers mapping
* @larb: SMI device
+ * @job_timeout_work: IRQ timeout structure
+ * @variant: driver variant to be used
*/
struct mtk_jpeg_dev {
struct mutex lock;
@@ -57,16 +97,17 @@
struct v4l2_device v4l2_dev;
struct v4l2_m2m_dev *m2m_dev;
void *alloc_ctx;
- struct video_device *dec_vdev;
- void __iomem *dec_reg_base;
- struct clk *clk_jdec;
- struct clk *clk_jdec_smi;
+ struct video_device *vdev;
+ void __iomem *reg_base;
struct device *larb;
+ struct delayed_work job_timeout_work;
+ const struct mtk_jpeg_variant *variant;
};
/**
* struct jpeg_fmt - driver's internal color format data
* @fourcc: the fourcc code, 0 if not applicable
+ * @hw_format: hardware format value
* @h_sample: horizontal sample count of plane in 4 * 4 pixel image
* @v_sample: vertical sample count of plane in 4 * 4 pixel image
* @colplanes: number of color planes (1 for packed formats)
@@ -76,6 +117,7 @@
*/
struct mtk_jpeg_fmt {
u32 fourcc;
+ u32 hw_format;
int h_sample[VIDEO_MAX_PLANES];
int v_sample[VIDEO_MAX_PLANES];
int colplanes;
@@ -87,18 +129,13 @@
/**
* mtk_jpeg_q_data - parameters of one queue
* @fmt: driver-specific format of this queue
- * @w: image width
- * @h: image height
- * @bytesperline: distance in bytes between the leftmost pixels in two adjacent
- * lines
- * @sizeimage: image buffer size in bytes
+ * @pix_mp: multiplanar format
+ * @enc_crop_rect: jpeg encoder crop information
*/
struct mtk_jpeg_q_data {
struct mtk_jpeg_fmt *fmt;
- u32 w;
- u32 h;
- u32 bytesperline[VIDEO_MAX_PLANES];
- u32 sizeimage[VIDEO_MAX_PLANES];
+ struct v4l2_pix_format_mplane pix_mp;
+ struct v4l2_rect enc_crop_rect;
};
/**
@@ -107,13 +144,11 @@
* @out_q: source (output) queue information
* @cap_q: destination (capture) queue queue information
* @fh: V4L2 file handle
- * @dec_param parameters for HW decoding
* @state: state of the context
- * @header_valid: set if header has been parsed and valid
- * @colorspace: enum v4l2_colorspace; supplemental to pixelformat
- * @ycbcr_enc: enum v4l2_ycbcr_encoding, Y'CbCr encoding
- * @quantization: enum v4l2_quantization, colorspace quantization
- * @xfer_func: enum v4l2_xfer_func, colorspace transfer function
+ * @enable_exif: enable exif mode of jpeg encoder
+ * @enc_quality: jpeg encoder quality
+ * @restart_interval: jpeg encoder restart interval
+ * @ctrl_hdl: controls handler
*/
struct mtk_jpeg_ctx {
struct mtk_jpeg_dev *jpeg;
@@ -121,11 +156,10 @@
struct mtk_jpeg_q_data cap_q;
struct v4l2_fh fh;
enum mtk_jpeg_ctx_state state;
-
- enum v4l2_colorspace colorspace;
- enum v4l2_ycbcr_encoding ycbcr_enc;
- enum v4l2_quantization quantization;
- enum v4l2_xfer_func xfer_func;
+ bool enable_exif;
+ u8 enc_quality;
+ u8 restart_interval;
+ struct v4l2_ctrl_handler ctrl_hdl;
};
#endif /* _MTK_JPEG_CORE_H */
diff --git a/drivers/media/platform/mtk-jpeg/mtk_jpeg_hw.c b/drivers/media/platform/mtk-jpeg/mtk_jpeg_dec_hw.c
similarity index 97%
rename from drivers/media/platform/mtk-jpeg/mtk_jpeg_hw.c
rename to drivers/media/platform/mtk-jpeg/mtk_jpeg_dec_hw.c
index ddf0dfa..afbbfd5 100644
--- a/drivers/media/platform/mtk-jpeg/mtk_jpeg_hw.c
+++ b/drivers/media/platform/mtk-jpeg/mtk_jpeg_dec_hw.c
@@ -9,7 +9,7 @@
#include <linux/kernel.h>
#include <media/videobuf2-core.h>
-#include "mtk_jpeg_hw.h"
+#include "mtk_jpeg_dec_hw.h"
#define MTK_JPEG_DUNUM_MASK(val) (((val) - 1) & 0x3)
@@ -153,10 +153,10 @@
param->sampling_w[i];
/* output format is 420/422 */
param->comp_w[i] = padding_w >> brz_w[i];
- param->comp_w[i] = mtk_jpeg_align(param->comp_w[i],
- MTK_JPEG_DCTSIZE);
- param->img_stride[i] = i ? mtk_jpeg_align(param->comp_w[i], 16)
- : mtk_jpeg_align(param->comp_w[i], 32);
+ param->comp_w[i] = round_up(param->comp_w[i],
+ MTK_JPEG_DCTSIZE);
+ param->img_stride[i] = i ? round_up(param->comp_w[i], 16)
+ : round_up(param->comp_w[i], 32);
ds_row_h[i] = (MTK_JPEG_DCTSIZE * param->sampling_h[i]);
}
param->dec_w = param->img_stride[0];
diff --git a/drivers/media/platform/mtk-jpeg/mtk_jpeg_hw.h b/drivers/media/platform/mtk-jpeg/mtk_jpeg_dec_hw.h
similarity index 90%
rename from drivers/media/platform/mtk-jpeg/mtk_jpeg_hw.h
rename to drivers/media/platform/mtk-jpeg/mtk_jpeg_dec_hw.h
index 9c6584e..fa0d45f 100644
--- a/drivers/media/platform/mtk-jpeg/mtk_jpeg_hw.h
+++ b/drivers/media/platform/mtk-jpeg/mtk_jpeg_dec_hw.h
@@ -3,15 +3,16 @@
* Copyright (c) 2016 MediaTek Inc.
* Author: Ming Hsiu Tsai <minghsiu.tsai@mediatek.com>
* Rick Chang <rick.chang@mediatek.com>
+ * Xia Jiang <xia.jiang@mediatek.com>
*/
-#ifndef _MTK_JPEG_HW_H
-#define _MTK_JPEG_HW_H
+#ifndef _MTK_JPEG_DEC_HW_H
+#define _MTK_JPEG_DEC_HW_H
#include <media/videobuf2-core.h>
#include "mtk_jpeg_core.h"
-#include "mtk_jpeg_reg.h"
+#include "mtk_jpeg_dec_reg.h"
enum {
MTK_JPEG_DEC_RESULT_EOF_DONE = 0,
@@ -54,11 +55,6 @@
u8 uv_brz_w;
};
-static inline u32 mtk_jpeg_align(u32 val, u32 align)
-{
- return (val + align - 1) & ~(align - 1);
-}
-
struct mtk_jpeg_bs {
dma_addr_t str_addr;
dma_addr_t end_addr;
diff --git a/drivers/media/platform/mtk-jpeg/mtk_jpeg_parse.c b/drivers/media/platform/mtk-jpeg/mtk_jpeg_dec_parse.c
similarity index 98%
rename from drivers/media/platform/mtk-jpeg/mtk_jpeg_parse.c
rename to drivers/media/platform/mtk-jpeg/mtk_jpeg_dec_parse.c
index f862d38..b95c457 100644
--- a/drivers/media/platform/mtk-jpeg/mtk_jpeg_parse.c
+++ b/drivers/media/platform/mtk-jpeg/mtk_jpeg_dec_parse.c
@@ -8,7 +8,7 @@
#include <linux/kernel.h>
#include <linux/videodev2.h>
-#include "mtk_jpeg_parse.h"
+#include "mtk_jpeg_dec_parse.h"
#define TEM 0x01
#define SOF0 0xc0
diff --git a/drivers/media/platform/mtk-jpeg/mtk_jpeg_parse.h b/drivers/media/platform/mtk-jpeg/mtk_jpeg_dec_parse.h
similarity index 92%
rename from drivers/media/platform/mtk-jpeg/mtk_jpeg_parse.h
rename to drivers/media/platform/mtk-jpeg/mtk_jpeg_dec_parse.h
index 0a48eea..2918f15 100644
--- a/drivers/media/platform/mtk-jpeg/mtk_jpeg_parse.h
+++ b/drivers/media/platform/mtk-jpeg/mtk_jpeg_dec_parse.h
@@ -8,7 +8,7 @@
#ifndef _MTK_JPEG_PARSE_H
#define _MTK_JPEG_PARSE_H
-#include "mtk_jpeg_hw.h"
+#include "mtk_jpeg_dec_hw.h"
bool mtk_jpeg_parse(struct mtk_jpeg_dec_param *param, u8 *src_addr_va,
u32 src_size);
diff --git a/drivers/media/platform/mtk-jpeg/mtk_jpeg_reg.h b/drivers/media/platform/mtk-jpeg/mtk_jpeg_dec_reg.h
similarity index 76%
rename from drivers/media/platform/mtk-jpeg/mtk_jpeg_reg.h
rename to drivers/media/platform/mtk-jpeg/mtk_jpeg_dec_reg.h
index 94db04e..21ec8f9 100644
--- a/drivers/media/platform/mtk-jpeg/mtk_jpeg_reg.h
+++ b/drivers/media/platform/mtk-jpeg/mtk_jpeg_dec_reg.h
@@ -8,7 +8,6 @@
#ifndef _MTK_JPEG_REG_H
#define _MTK_JPEG_REG_H
-#define MTK_JPEG_COMP_MAX 3
#define MTK_JPEG_BLOCK_MAX 10
#define MTK_JPEG_DCTSIZE 8
@@ -20,29 +19,29 @@
#define BIT_INQST_MASK_ALLIRQ 0x37
#define JPGDEC_REG_RESET 0x0090
-#define JPGDEC_REG_BRZ_FACTOR 0x00F8
-#define JPGDEC_REG_DU_NUM 0x00FC
+#define JPGDEC_REG_BRZ_FACTOR 0x00f8
+#define JPGDEC_REG_DU_NUM 0x00fc
#define JPGDEC_REG_DEST_ADDR0_Y 0x0140
#define JPGDEC_REG_DEST_ADDR0_U 0x0144
#define JPGDEC_REG_DEST_ADDR0_V 0x0148
-#define JPGDEC_REG_DEST_ADDR1_Y 0x014C
+#define JPGDEC_REG_DEST_ADDR1_Y 0x014c
#define JPGDEC_REG_DEST_ADDR1_U 0x0150
#define JPGDEC_REG_DEST_ADDR1_V 0x0154
#define JPGDEC_REG_STRIDE_Y 0x0158
-#define JPGDEC_REG_STRIDE_UV 0x015C
+#define JPGDEC_REG_STRIDE_UV 0x015c
#define JPGDEC_REG_IMG_STRIDE_Y 0x0160
#define JPGDEC_REG_IMG_STRIDE_UV 0x0164
-#define JPGDEC_REG_WDMA_CTRL 0x016C
+#define JPGDEC_REG_WDMA_CTRL 0x016c
#define JPGDEC_REG_PAUSE_MCU_NUM 0x0170
-#define JPGDEC_REG_OPERATION_MODE 0x017C
+#define JPGDEC_REG_OPERATION_MODE 0x017c
#define JPGDEC_REG_FILE_ADDR 0x0200
-#define JPGDEC_REG_COMP_ID 0x020C
+#define JPGDEC_REG_COMP_ID 0x020c
#define JPGDEC_REG_TOTAL_MCU_NUM 0x0210
#define JPGDEC_REG_COMP0_DATA_UNIT_NUM 0x0224
-#define JPGDEC_REG_DU_CTRL 0x023C
+#define JPGDEC_REG_DU_CTRL 0x023c
#define JPGDEC_REG_TRIG 0x0240
#define JPGDEC_REG_FILE_BRP 0x0248
-#define JPGDEC_REG_FILE_TOTAL_SIZE 0x024C
+#define JPGDEC_REG_FILE_TOTAL_SIZE 0x024c
#define JPGDEC_REG_QT_ID 0x0270
#define JPGDEC_REG_INTERRUPT_STATUS 0x0274
#define JPGDEC_REG_STATUS 0x0278
diff --git a/drivers/media/platform/mtk-jpeg/mtk_jpeg_enc_hw.c b/drivers/media/platform/mtk-jpeg/mtk_jpeg_enc_hw.c
new file mode 100644
index 0000000..1cf037b
--- /dev/null
+++ b/drivers/media/platform/mtk-jpeg/mtk_jpeg_enc_hw.c
@@ -0,0 +1,154 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2019 MediaTek Inc.
+ * Author: Xia Jiang <xia.jiang@mediatek.com>
+ *
+ */
+
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include "mtk_jpeg_enc_hw.h"
+
+static const struct mtk_jpeg_enc_qlt mtk_jpeg_enc_quality[] = {
+ {.quality_param = 34, .hardware_value = JPEG_ENC_QUALITY_Q34},
+ {.quality_param = 39, .hardware_value = JPEG_ENC_QUALITY_Q39},
+ {.quality_param = 48, .hardware_value = JPEG_ENC_QUALITY_Q48},
+ {.quality_param = 60, .hardware_value = JPEG_ENC_QUALITY_Q60},
+ {.quality_param = 64, .hardware_value = JPEG_ENC_QUALITY_Q64},
+ {.quality_param = 68, .hardware_value = JPEG_ENC_QUALITY_Q68},
+ {.quality_param = 74, .hardware_value = JPEG_ENC_QUALITY_Q74},
+ {.quality_param = 80, .hardware_value = JPEG_ENC_QUALITY_Q80},
+ {.quality_param = 82, .hardware_value = JPEG_ENC_QUALITY_Q82},
+ {.quality_param = 84, .hardware_value = JPEG_ENC_QUALITY_Q84},
+ {.quality_param = 87, .hardware_value = JPEG_ENC_QUALITY_Q87},
+ {.quality_param = 90, .hardware_value = JPEG_ENC_QUALITY_Q90},
+ {.quality_param = 92, .hardware_value = JPEG_ENC_QUALITY_Q92},
+ {.quality_param = 95, .hardware_value = JPEG_ENC_QUALITY_Q95},
+ {.quality_param = 97, .hardware_value = JPEG_ENC_QUALITY_Q97},
+};
+
+void mtk_jpeg_enc_reset(void __iomem *base)
+{
+ writel(0, base + JPEG_ENC_RSTB);
+ writel(JPEG_ENC_RESET_BIT, base + JPEG_ENC_RSTB);
+ writel(0, base + JPEG_ENC_CODEC_SEL);
+}
+
+u32 mtk_jpeg_enc_get_file_size(void __iomem *base)
+{
+ return readl(base + JPEG_ENC_DMA_ADDR0) -
+ readl(base + JPEG_ENC_DST_ADDR0);
+}
+
+void mtk_jpeg_enc_start(void __iomem *base)
+{
+ u32 value;
+
+ value = readl(base + JPEG_ENC_CTRL);
+ value |= JPEG_ENC_CTRL_INT_EN_BIT | JPEG_ENC_CTRL_ENABLE_BIT;
+ writel(value, base + JPEG_ENC_CTRL);
+}
+
+void mtk_jpeg_set_enc_src(struct mtk_jpeg_ctx *ctx, void __iomem *base,
+ struct vb2_buffer *src_buf)
+{
+ int i;
+ dma_addr_t dma_addr;
+
+ for (i = 0; i < src_buf->num_planes; i++) {
+ dma_addr = vb2_dma_contig_plane_dma_addr(src_buf, i) +
+ src_buf->planes[i].data_offset;
+ if (!i)
+ writel(dma_addr, base + JPEG_ENC_SRC_LUMA_ADDR);
+ else
+ writel(dma_addr, base + JPEG_ENC_SRC_CHROMA_ADDR);
+ }
+}
+
+void mtk_jpeg_set_enc_dst(struct mtk_jpeg_ctx *ctx, void __iomem *base,
+ struct vb2_buffer *dst_buf)
+{
+ dma_addr_t dma_addr;
+ size_t size;
+ u32 dma_addr_offset;
+ u32 dma_addr_offsetmask;
+
+ dma_addr = vb2_dma_contig_plane_dma_addr(dst_buf, 0);
+ dma_addr_offset = ctx->enable_exif ? MTK_JPEG_MAX_EXIF_SIZE : 0;
+ dma_addr_offsetmask = dma_addr & JPEG_ENC_DST_ADDR_OFFSET_MASK;
+ size = vb2_plane_size(dst_buf, 0);
+
+ writel(dma_addr_offset & ~0xf, base + JPEG_ENC_OFFSET_ADDR);
+ writel(dma_addr_offsetmask & 0xf, base + JPEG_ENC_BYTE_OFFSET_MASK);
+ writel(dma_addr & ~0xf, base + JPEG_ENC_DST_ADDR0);
+ writel((dma_addr + size) & ~0xf, base + JPEG_ENC_STALL_ADDR0);
+}
+
+void mtk_jpeg_set_enc_params(struct mtk_jpeg_ctx *ctx, void __iomem *base)
+{
+ u32 value;
+ u32 width = ctx->out_q.enc_crop_rect.width;
+ u32 height = ctx->out_q.enc_crop_rect.height;
+ u32 enc_format = ctx->out_q.fmt->fourcc;
+ u32 bytesperline = ctx->out_q.pix_mp.plane_fmt[0].bytesperline;
+ u32 blk_num;
+ u32 img_stride;
+ u32 mem_stride;
+ u32 i, enc_quality;
+
+ value = width << 16 | height;
+ writel(value, base + JPEG_ENC_IMG_SIZE);
+
+ if (enc_format == V4L2_PIX_FMT_NV12M ||
+ enc_format == V4L2_PIX_FMT_NV21M)
+ /*
+ * Total 8 x 8 block number of luma and chroma.
+ * The number of blocks is counted from 0.
+ */
+ blk_num = DIV_ROUND_UP(width, 16) *
+ DIV_ROUND_UP(height, 16) * 6 - 1;
+ else
+ blk_num = DIV_ROUND_UP(width, 16) *
+ DIV_ROUND_UP(height, 8) * 4 - 1;
+ writel(blk_num, base + JPEG_ENC_BLK_NUM);
+
+ if (enc_format == V4L2_PIX_FMT_NV12M ||
+ enc_format == V4L2_PIX_FMT_NV21M) {
+ /* 4:2:0 */
+ img_stride = round_up(width, 16);
+ mem_stride = bytesperline;
+ } else {
+ /* 4:2:2 */
+ img_stride = round_up(width * 2, 32);
+ mem_stride = img_stride;
+ }
+ writel(img_stride, base + JPEG_ENC_IMG_STRIDE);
+ writel(mem_stride, base + JPEG_ENC_STRIDE);
+
+ enc_quality = mtk_jpeg_enc_quality[0].hardware_value;
+ for (i = 0; i < ARRAY_SIZE(mtk_jpeg_enc_quality); i++) {
+ if (ctx->enc_quality <= mtk_jpeg_enc_quality[i].quality_param) {
+ enc_quality = mtk_jpeg_enc_quality[i].hardware_value;
+ break;
+ }
+ }
+ writel(enc_quality, base + JPEG_ENC_QUALITY);
+
+ value = readl(base + JPEG_ENC_CTRL);
+ value &= ~JPEG_ENC_CTRL_YUV_FORMAT_MASK;
+ value |= (ctx->out_q.fmt->hw_format & 3) << 3;
+ if (ctx->enable_exif)
+ value |= JPEG_ENC_CTRL_FILE_FORMAT_BIT;
+ else
+ value &= ~JPEG_ENC_CTRL_FILE_FORMAT_BIT;
+ if (ctx->restart_interval)
+ value |= JPEG_ENC_CTRL_RESTART_EN_BIT;
+ else
+ value &= ~JPEG_ENC_CTRL_RESTART_EN_BIT;
+ writel(value, base + JPEG_ENC_CTRL);
+
+ writel(ctx->restart_interval, base + JPEG_ENC_RST_MCU_NUM);
+}
diff --git a/drivers/media/platform/mtk-jpeg/mtk_jpeg_enc_hw.h b/drivers/media/platform/mtk-jpeg/mtk_jpeg_enc_hw.h
new file mode 100644
index 0000000..61c60e4
--- /dev/null
+++ b/drivers/media/platform/mtk-jpeg/mtk_jpeg_enc_hw.h
@@ -0,0 +1,91 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2019 MediaTek Inc.
+ * Author: Xia Jiang <xia.jiang@mediatek.com>
+ *
+ */
+
+#ifndef _MTK_JPEG_ENC_HW_H
+#define _MTK_JPEG_ENC_HW_H
+
+#include <media/videobuf2-core.h>
+
+#include "mtk_jpeg_core.h"
+
+#define JPEG_ENC_INT_STATUS_DONE BIT(0)
+#define JPEG_ENC_INT_STATUS_MASK_ALLIRQ 0x13
+
+#define JPEG_ENC_DST_ADDR_OFFSET_MASK GENMASK(3, 0)
+
+#define JPEG_ENC_CTRL_YUV_FORMAT_MASK 0x18
+#define JPEG_ENC_CTRL_RESTART_EN_BIT BIT(10)
+#define JPEG_ENC_CTRL_FILE_FORMAT_BIT BIT(5)
+#define JPEG_ENC_CTRL_INT_EN_BIT BIT(2)
+#define JPEG_ENC_CTRL_ENABLE_BIT BIT(0)
+#define JPEG_ENC_RESET_BIT BIT(0)
+
+#define JPEG_ENC_YUV_FORMAT_YUYV 0
+#define JPEG_ENC_YUV_FORMAT_YVYU 1
+#define JPEG_ENC_YUV_FORMAT_NV12 2
+#define JEPG_ENC_YUV_FORMAT_NV21 3
+
+#define JPEG_ENC_QUALITY_Q60 0x0
+#define JPEG_ENC_QUALITY_Q80 0x1
+#define JPEG_ENC_QUALITY_Q90 0x2
+#define JPEG_ENC_QUALITY_Q95 0x3
+#define JPEG_ENC_QUALITY_Q39 0x4
+#define JPEG_ENC_QUALITY_Q68 0x5
+#define JPEG_ENC_QUALITY_Q84 0x6
+#define JPEG_ENC_QUALITY_Q92 0x7
+#define JPEG_ENC_QUALITY_Q48 0x8
+#define JPEG_ENC_QUALITY_Q74 0xa
+#define JPEG_ENC_QUALITY_Q87 0xb
+#define JPEG_ENC_QUALITY_Q34 0xc
+#define JPEG_ENC_QUALITY_Q64 0xe
+#define JPEG_ENC_QUALITY_Q82 0xf
+#define JPEG_ENC_QUALITY_Q97 0x10
+
+#define JPEG_ENC_RSTB 0x100
+#define JPEG_ENC_CTRL 0x104
+#define JPEG_ENC_QUALITY 0x108
+#define JPEG_ENC_BLK_NUM 0x10C
+#define JPEG_ENC_BLK_CNT 0x110
+#define JPEG_ENC_INT_STS 0x11c
+#define JPEG_ENC_DST_ADDR0 0x120
+#define JPEG_ENC_DMA_ADDR0 0x124
+#define JPEG_ENC_STALL_ADDR0 0x128
+#define JPEG_ENC_OFFSET_ADDR 0x138
+#define JPEG_ENC_RST_MCU_NUM 0x150
+#define JPEG_ENC_IMG_SIZE 0x154
+#define JPEG_ENC_DEBUG_INFO0 0x160
+#define JPEG_ENC_DEBUG_INFO1 0x164
+#define JPEG_ENC_TOTAL_CYCLE 0x168
+#define JPEG_ENC_BYTE_OFFSET_MASK 0x16c
+#define JPEG_ENC_SRC_LUMA_ADDR 0x170
+#define JPEG_ENC_SRC_CHROMA_ADDR 0x174
+#define JPEG_ENC_STRIDE 0x178
+#define JPEG_ENC_IMG_STRIDE 0x17c
+#define JPEG_ENC_DCM_CTRL 0x300
+#define JPEG_ENC_CODEC_SEL 0x314
+#define JPEG_ENC_ULTRA_THRES 0x318
+
+/**
+ * struct mtk_jpeg_enc_qlt - JPEG encoder quality data
+ * @quality_param: quality value
+ * @hardware_value: hardware value of quality
+ */
+struct mtk_jpeg_enc_qlt {
+ u8 quality_param;
+ u8 hardware_value;
+};
+
+void mtk_jpeg_enc_reset(void __iomem *base);
+u32 mtk_jpeg_enc_get_file_size(void __iomem *base);
+void mtk_jpeg_enc_start(void __iomem *enc_reg_base);
+void mtk_jpeg_set_enc_src(struct mtk_jpeg_ctx *ctx, void __iomem *base,
+ struct vb2_buffer *src_buf);
+void mtk_jpeg_set_enc_dst(struct mtk_jpeg_ctx *ctx, void __iomem *base,
+ struct vb2_buffer *dst_buf);
+void mtk_jpeg_set_enc_params(struct mtk_jpeg_ctx *ctx, void __iomem *base);
+
+#endif /* _MTK_JPEG_ENC_HW_H */
diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_comp.c b/drivers/media/platform/mtk-mdp/mtk_mdp_comp.c
index 9afe816..b3426a5 100644
--- a/drivers/media/platform/mtk-mdp/mtk_mdp_comp.c
+++ b/drivers/media/platform/mtk-mdp/mtk_mdp_comp.c
@@ -14,46 +14,6 @@
#include "mtk_mdp_comp.h"
-static const char * const mtk_mdp_comp_stem[MTK_MDP_COMP_TYPE_MAX] = {
- "mdp_rdma",
- "mdp_rsz",
- "mdp_wdma",
- "mdp_wrot",
-};
-
-struct mtk_mdp_comp_match {
- enum mtk_mdp_comp_type type;
- int alias_id;
-};
-
-static const struct mtk_mdp_comp_match mtk_mdp_matches[MTK_MDP_COMP_ID_MAX] = {
- { MTK_MDP_RDMA, 0 },
- { MTK_MDP_RDMA, 1 },
- { MTK_MDP_RSZ, 0 },
- { MTK_MDP_RSZ, 1 },
- { MTK_MDP_RSZ, 2 },
- { MTK_MDP_WDMA, 0 },
- { MTK_MDP_WROT, 0 },
- { MTK_MDP_WROT, 1 },
-};
-
-int mtk_mdp_comp_get_id(struct device *dev, struct device_node *node,
- enum mtk_mdp_comp_type comp_type)
-{
- int id = of_alias_get_id(node, mtk_mdp_comp_stem[comp_type]);
- int i;
-
- for (i = 0; i < ARRAY_SIZE(mtk_mdp_matches); i++) {
- if (comp_type == mtk_mdp_matches[i].type &&
- id == mtk_mdp_matches[i].alias_id)
- return i;
- }
-
- dev_err(dev, "Failed to get id. type: %d, id: %d\n", comp_type, id);
-
- return -EINVAL;
-}
-
void mtk_mdp_comp_clock_on(struct device *dev, struct mtk_mdp_comp *comp)
{
int i, err;
@@ -62,8 +22,8 @@
err = mtk_smi_larb_get(comp->larb_dev);
if (err)
dev_err(dev,
- "failed to get larb, err %d. type:%d id:%d\n",
- err, comp->type, comp->id);
+ "failed to get larb, err %d. type:%d\n",
+ err, comp->type);
}
for (i = 0; i < ARRAY_SIZE(comp->clk); i++) {
@@ -72,8 +32,8 @@
err = clk_prepare_enable(comp->clk[i]);
if (err)
dev_err(dev,
- "failed to enable clock, err %d. type:%d id:%d i:%d\n",
- err, comp->type, comp->id, i);
+ "failed to enable clock, err %d. type:%d i:%d\n",
+ err, comp->type, i);
}
}
@@ -92,24 +52,25 @@
}
int mtk_mdp_comp_init(struct device *dev, struct device_node *node,
- struct mtk_mdp_comp *comp, enum mtk_mdp_comp_id comp_id)
+ struct mtk_mdp_comp *comp,
+ enum mtk_mdp_comp_type comp_type)
{
struct device_node *larb_node;
struct platform_device *larb_pdev;
+ int ret;
int i;
- if (comp_id < 0 || comp_id >= MTK_MDP_COMP_ID_MAX) {
- dev_err(dev, "Invalid comp_id %d\n", comp_id);
- return -EINVAL;
- }
-
comp->dev_node = of_node_get(node);
- comp->id = comp_id;
- comp->type = mtk_mdp_matches[comp_id].type;
- comp->regs = of_iomap(node, 0);
+ comp->type = comp_type;
for (i = 0; i < ARRAY_SIZE(comp->clk); i++) {
comp->clk[i] = of_clk_get(node, i);
+ if (IS_ERR(comp->clk[i])) {
+ if (PTR_ERR(comp->clk[i]) != -EPROBE_DEFER)
+ dev_err(dev, "Failed to get clock\n");
+ ret = PTR_ERR(comp->clk[i]);
+ goto put_dev;
+ }
/* Only RDMA needs two clocks */
if (comp->type != MTK_MDP_RDMA)
@@ -127,20 +88,27 @@
if (!larb_node) {
dev_err(dev,
"Missing mediadek,larb phandle in %pOF node\n", node);
- return -EINVAL;
+ ret = -EINVAL;
+ goto put_dev;
}
larb_pdev = of_find_device_by_node(larb_node);
if (!larb_pdev) {
dev_warn(dev, "Waiting for larb device %pOF\n", larb_node);
of_node_put(larb_node);
- return -EPROBE_DEFER;
+ ret = -EPROBE_DEFER;
+ goto put_dev;
}
of_node_put(larb_node);
comp->larb_dev = &larb_pdev->dev;
return 0;
+
+put_dev:
+ of_node_put(comp->dev_node);
+
+ return ret;
}
void mtk_mdp_comp_deinit(struct device *dev, struct mtk_mdp_comp *comp)
diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_comp.h b/drivers/media/platform/mtk-mdp/mtk_mdp_comp.h
index 998a4b9..1bf0242 100644
--- a/drivers/media/platform/mtk-mdp/mtk_mdp_comp.h
+++ b/drivers/media/platform/mtk-mdp/mtk_mdp_comp.h
@@ -22,41 +22,26 @@
MTK_MDP_COMP_TYPE_MAX,
};
-enum mtk_mdp_comp_id {
- MTK_MDP_COMP_RDMA0,
- MTK_MDP_COMP_RDMA1,
- MTK_MDP_COMP_RSZ0,
- MTK_MDP_COMP_RSZ1,
- MTK_MDP_COMP_RSZ2,
- MTK_MDP_COMP_WDMA,
- MTK_MDP_COMP_WROT0,
- MTK_MDP_COMP_WROT1,
- MTK_MDP_COMP_ID_MAX,
-};
-
/**
* struct mtk_mdp_comp - the MDP's function component data
+ * @node: list node to track sibing MDP components
* @dev_node: component device node
* @clk: clocks required for component
- * @regs: Mapped address of component registers.
* @larb_dev: SMI device required for component
* @type: component type
- * @id: component ID
*/
struct mtk_mdp_comp {
+ struct list_head node;
struct device_node *dev_node;
struct clk *clk[2];
- void __iomem *regs;
struct device *larb_dev;
enum mtk_mdp_comp_type type;
- enum mtk_mdp_comp_id id;
};
int mtk_mdp_comp_init(struct device *dev, struct device_node *node,
- struct mtk_mdp_comp *comp, enum mtk_mdp_comp_id comp_id);
+ struct mtk_mdp_comp *comp,
+ enum mtk_mdp_comp_type comp_type);
void mtk_mdp_comp_deinit(struct device *dev, struct mtk_mdp_comp *comp);
-int mtk_mdp_comp_get_id(struct device *dev, struct device_node *node,
- enum mtk_mdp_comp_type comp_type);
void mtk_mdp_comp_clock_on(struct device *dev, struct mtk_mdp_comp *comp);
void mtk_mdp_comp_clock_off(struct device *dev, struct mtk_mdp_comp *comp);
diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_core.c b/drivers/media/platform/mtk-mdp/mtk_mdp_core.c
index c1e29a4..976aa1f 100644
--- a/drivers/media/platform/mtk-mdp/mtk_mdp_core.c
+++ b/drivers/media/platform/mtk-mdp/mtk_mdp_core.c
@@ -55,19 +55,19 @@
static void mtk_mdp_clock_on(struct mtk_mdp_dev *mdp)
{
struct device *dev = &mdp->pdev->dev;
- int i;
+ struct mtk_mdp_comp *comp_node;
- for (i = 0; i < ARRAY_SIZE(mdp->comp); i++)
- mtk_mdp_comp_clock_on(dev, mdp->comp[i]);
+ list_for_each_entry(comp_node, &mdp->comp_list, node)
+ mtk_mdp_comp_clock_on(dev, comp_node);
}
static void mtk_mdp_clock_off(struct mtk_mdp_dev *mdp)
{
struct device *dev = &mdp->pdev->dev;
- int i;
+ struct mtk_mdp_comp *comp_node;
- for (i = 0; i < ARRAY_SIZE(mdp->comp); i++)
- mtk_mdp_comp_clock_off(dev, mdp->comp[i]);
+ list_for_each_entry(comp_node, &mdp->comp_list, node)
+ mtk_mdp_comp_clock_off(dev, comp_node);
}
static void mtk_mdp_wdt_worker(struct work_struct *work)
@@ -91,12 +91,25 @@
queue_work(mdp->wdt_wq, &mdp->wdt_work);
}
+void mtk_mdp_register_component(struct mtk_mdp_dev *mdp,
+ struct mtk_mdp_comp *comp)
+{
+ list_add(&comp->node, &mdp->comp_list);
+}
+
+void mtk_mdp_unregister_component(struct mtk_mdp_dev *mdp,
+ struct mtk_mdp_comp *comp)
+{
+ list_del(&comp->node);
+}
+
static int mtk_mdp_probe(struct platform_device *pdev)
{
struct mtk_mdp_dev *mdp;
struct device *dev = &pdev->dev;
struct device_node *node, *parent;
- int i, ret = 0;
+ struct mtk_mdp_comp *comp, *comp_temp;
+ int ret = 0;
mdp = devm_kzalloc(dev, sizeof(*mdp), GFP_KERNEL);
if (!mdp)
@@ -104,6 +117,7 @@
mdp->id = pdev->id;
mdp->pdev = pdev;
+ INIT_LIST_HEAD(&mdp->comp_list);
INIT_LIST_HEAD(&mdp->ctx_list);
mutex_init(&mdp->lock);
@@ -123,8 +137,6 @@
for_each_child_of_node(parent, node) {
const struct of_device_id *of_id;
enum mtk_mdp_comp_type comp_type;
- int comp_id;
- struct mtk_mdp_comp *comp;
of_id = of_match_node(mtk_mdp_comp_dt_ids, node);
if (!of_id)
@@ -137,12 +149,6 @@
}
comp_type = (enum mtk_mdp_comp_type)of_id->data;
- comp_id = mtk_mdp_comp_get_id(dev, node, comp_type);
- if (comp_id < 0) {
- dev_warn(dev, "Skipping unknown component %pOF\n",
- node);
- continue;
- }
comp = devm_kzalloc(dev, sizeof(*comp), GFP_KERNEL);
if (!comp) {
@@ -150,13 +156,14 @@
of_node_put(node);
goto err_comp;
}
- mdp->comp[comp_id] = comp;
- ret = mtk_mdp_comp_init(dev, node, comp, comp_id);
+ ret = mtk_mdp_comp_init(dev, node, comp, comp_type);
if (ret) {
of_node_put(node);
goto err_comp;
}
+
+ mtk_mdp_register_component(mdp, comp);
}
mdp->job_wq = create_singlethread_workqueue(MTK_MDP_MODULE_NAME);
@@ -188,12 +195,20 @@
}
mdp->vpu_dev = vpu_get_plat_device(pdev);
- vpu_wdt_reg_handler(mdp->vpu_dev, mtk_mdp_reset_handler, mdp,
- VPU_RST_MDP);
+ ret = vpu_wdt_reg_handler(mdp->vpu_dev, mtk_mdp_reset_handler, mdp,
+ VPU_RST_MDP);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to register reset handler\n");
+ goto err_m2m_register;
+ }
platform_set_drvdata(pdev, mdp);
- vb2_dma_contig_set_max_seg_size(&pdev->dev, DMA_BIT_MASK(32));
+ ret = vb2_dma_contig_set_max_seg_size(&pdev->dev, DMA_BIT_MASK(32));
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to set vb2 dma mag seg size\n");
+ goto err_m2m_register;
+ }
pm_runtime_enable(dev);
dev_dbg(dev, "mdp-%d registered successfully\n", mdp->id);
@@ -212,8 +227,10 @@
err_alloc_job_wq:
err_comp:
- for (i = 0; i < ARRAY_SIZE(mdp->comp); i++)
- mtk_mdp_comp_deinit(dev, mdp->comp[i]);
+ list_for_each_entry_safe(comp, comp_temp, &mdp->comp_list, node) {
+ mtk_mdp_unregister_component(mdp, comp);
+ mtk_mdp_comp_deinit(dev, comp);
+ }
dev_dbg(dev, "err %d\n", ret);
return ret;
@@ -222,18 +239,23 @@
static int mtk_mdp_remove(struct platform_device *pdev)
{
struct mtk_mdp_dev *mdp = platform_get_drvdata(pdev);
- int i;
+ struct mtk_mdp_comp *comp, *comp_temp;
pm_runtime_disable(&pdev->dev);
vb2_dma_contig_clear_max_seg_size(&pdev->dev);
mtk_mdp_unregister_m2m_device(mdp);
v4l2_device_unregister(&mdp->v4l2_dev);
+ flush_workqueue(mdp->wdt_wq);
+ destroy_workqueue(mdp->wdt_wq);
+
flush_workqueue(mdp->job_wq);
destroy_workqueue(mdp->job_wq);
- for (i = 0; i < ARRAY_SIZE(mdp->comp); i++)
- mtk_mdp_comp_deinit(&pdev->dev, mdp->comp[i]);
+ list_for_each_entry_safe(comp, comp_temp, &mdp->comp_list, node) {
+ mtk_mdp_unregister_component(mdp, comp);
+ mtk_mdp_comp_deinit(&pdev->dev, comp);
+ }
dev_dbg(&pdev->dev, "%s driver unloaded\n", pdev->name);
return 0;
diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_core.h b/drivers/media/platform/mtk-mdp/mtk_mdp_core.h
index bafcccd..a7da14b 100644
--- a/drivers/media/platform/mtk-mdp/mtk_mdp_core.h
+++ b/drivers/media/platform/mtk-mdp/mtk_mdp_core.h
@@ -28,8 +28,6 @@
#define MTK_MDP_FMT_FLAG_CAPTURE BIT(1)
#define MTK_MDP_VPU_INIT BIT(0)
-#define MTK_MDP_SRC_FMT BIT(1)
-#define MTK_MDP_DST_FMT BIT(2)
#define MTK_MDP_CTX_ERROR BIT(5)
/**
@@ -138,7 +136,7 @@
* @pdev: pointer to the image processor platform device
* @variant: the IP variant information
* @id: image processor device index (0..MTK_MDP_MAX_DEVS)
- * @comp: MDP function components
+ * @comp_list: list of MDP function components
* @m2m_dev: v4l2 memory-to-memory device data
* @ctx_list: list of struct mtk_mdp_ctx
* @vdev: video device for image processor driver
@@ -156,7 +154,7 @@
struct platform_device *pdev;
struct mtk_mdp_variant *variant;
u16 id;
- struct mtk_mdp_comp *comp[MTK_MDP_COMP_ID_MAX];
+ struct list_head comp_list;
struct v4l2_m2m_dev *m2m_dev;
struct list_head ctx_list;
struct video_device *vdev;
@@ -223,6 +221,12 @@
extern int mtk_mdp_dbg_level;
+void mtk_mdp_register_component(struct mtk_mdp_dev *mdp,
+ struct mtk_mdp_comp *comp);
+
+void mtk_mdp_unregister_component(struct mtk_mdp_dev *mdp,
+ struct mtk_mdp_comp *comp);
+
#if defined(DEBUG)
#define mtk_mdp_dbg(level, fmt, args...) \
diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c b/drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c
index 34bc294..45fc741 100644
--- a/drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c
+++ b/drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c
@@ -193,7 +193,7 @@
pix_mp->field = V4L2_FIELD_NONE;
pix_mp->pixelformat = fmt->pixelformat;
- if (!V4L2_TYPE_IS_OUTPUT(f->type)) {
+ if (V4L2_TYPE_IS_CAPTURE(f->type)) {
pix_mp->colorspace = ctx->colorspace;
pix_mp->xfer_func = ctx->xfer_func;
pix_mp->ycbcr_enc = ctx->ycbcr_enc;
@@ -327,9 +327,8 @@
mtk_mdp_bound_align_image(&new_w, min_w, max_w, align_w,
&new_h, min_h, max_h, align_h);
- if (!V4L2_TYPE_IS_OUTPUT(type) &&
- (ctx->ctrls.rotate->val == 90 ||
- ctx->ctrls.rotate->val == 270))
+ if (V4L2_TYPE_IS_CAPTURE(type) &&
+ (ctx->ctrls.rotate->val == 90 || ctx->ctrls.rotate->val == 270))
mtk_mdp_check_crop_change(new_h, new_w,
&r->width, &r->height);
else
@@ -369,13 +368,6 @@
mutex_unlock(&ctx->slock);
}
-static void mtk_mdp_ctx_state_lock_clear(struct mtk_mdp_ctx *ctx, u32 state)
-{
- mutex_lock(&ctx->slock);
- ctx->state &= ~state;
- mutex_unlock(&ctx->slock);
-}
-
static bool mtk_mdp_ctx_state_is_set(struct mtk_mdp_ctx *ctx, u32 mask)
{
bool ret;
@@ -726,11 +718,6 @@
ctx->quant = pix_mp->quantization;
}
- if (V4L2_TYPE_IS_OUTPUT(f->type))
- mtk_mdp_ctx_state_lock_set(ctx, MTK_MDP_SRC_FMT);
- else
- mtk_mdp_ctx_state_lock_set(ctx, MTK_MDP_DST_FMT);
-
mtk_mdp_dbg(2, "[%d] type:%d, frame:%dx%d", ctx->id, f->type,
frame->width, frame->height);
@@ -742,13 +729,6 @@
{
struct mtk_mdp_ctx *ctx = fh_to_ctx(fh);
- if (reqbufs->count == 0) {
- if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
- mtk_mdp_ctx_state_lock_clear(ctx, MTK_MDP_SRC_FMT);
- else
- mtk_mdp_ctx_state_lock_clear(ctx, MTK_MDP_DST_FMT);
- }
-
return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs);
}
@@ -758,14 +738,6 @@
struct mtk_mdp_ctx *ctx = fh_to_ctx(fh);
int ret;
- /* The source and target color format need to be set */
- if (V4L2_TYPE_IS_OUTPUT(type)) {
- if (!mtk_mdp_ctx_state_is_set(ctx, MTK_MDP_SRC_FMT))
- return -EINVAL;
- } else if (!mtk_mdp_ctx_state_is_set(ctx, MTK_MDP_DST_FMT)) {
- return -EINVAL;
- }
-
if (!mtk_mdp_ctx_state_is_set(ctx, MTK_MDP_VPU_INIT)) {
ret = mtk_mdp_vpu_init(&ctx->vpu);
if (ret < 0) {
@@ -899,24 +871,21 @@
frame = &ctx->d_frame;
/* Check to see if scaling ratio is within supported range */
- if (mtk_mdp_ctx_state_is_set(ctx, MTK_MDP_DST_FMT | MTK_MDP_SRC_FMT)) {
- if (V4L2_TYPE_IS_OUTPUT(s->type)) {
- ret = mtk_mdp_check_scaler_ratio(variant, new_r.width,
- new_r.height, ctx->d_frame.crop.width,
- ctx->d_frame.crop.height,
- ctx->ctrls.rotate->val);
- } else {
- ret = mtk_mdp_check_scaler_ratio(variant,
- ctx->s_frame.crop.width,
- ctx->s_frame.crop.height, new_r.width,
- new_r.height, ctx->ctrls.rotate->val);
- }
+ if (V4L2_TYPE_IS_OUTPUT(s->type))
+ ret = mtk_mdp_check_scaler_ratio(variant, new_r.width,
+ new_r.height, ctx->d_frame.crop.width,
+ ctx->d_frame.crop.height,
+ ctx->ctrls.rotate->val);
+ else
+ ret = mtk_mdp_check_scaler_ratio(variant,
+ ctx->s_frame.crop.width,
+ ctx->s_frame.crop.height, new_r.width,
+ new_r.height, ctx->ctrls.rotate->val);
- if (ret) {
- dev_info(&ctx->mdp_dev->pdev->dev,
- "Out of scaler range");
- return -EINVAL;
- }
+ if (ret) {
+ dev_info(&ctx->mdp_dev->pdev->dev,
+ "Out of scaler range");
+ return -EINVAL;
}
s->r = new_r;
@@ -989,7 +958,6 @@
struct mtk_mdp_ctx *ctx = ctrl_to_ctx(ctrl);
struct mtk_mdp_dev *mdp = ctx->mdp_dev;
struct mtk_mdp_variant *variant = mdp->variant;
- u32 state = MTK_MDP_DST_FMT | MTK_MDP_SRC_FMT;
int ret = 0;
if (ctrl->flags & V4L2_CTRL_FLAG_INACTIVE)
@@ -1003,17 +971,15 @@
ctx->vflip = ctrl->val;
break;
case V4L2_CID_ROTATE:
- if (mtk_mdp_ctx_state_is_set(ctx, state)) {
- ret = mtk_mdp_check_scaler_ratio(variant,
- ctx->s_frame.crop.width,
- ctx->s_frame.crop.height,
- ctx->d_frame.crop.width,
- ctx->d_frame.crop.height,
- ctx->ctrls.rotate->val);
+ ret = mtk_mdp_check_scaler_ratio(variant,
+ ctx->s_frame.crop.width,
+ ctx->s_frame.crop.height,
+ ctx->d_frame.crop.width,
+ ctx->d_frame.crop.height,
+ ctx->ctrls.rotate->val);
- if (ret)
- return -EINVAL;
- }
+ if (ret)
+ return -EINVAL;
ctx->rotation = ctrl->val;
break;
@@ -1090,6 +1056,7 @@
struct video_device *vfd = video_devdata(file);
struct mtk_mdp_ctx *ctx = NULL;
int ret;
+ struct v4l2_format default_format;
ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
if (!ctx)
@@ -1144,6 +1111,16 @@
list_add(&ctx->list, &mdp->ctx_list);
mutex_unlock(&mdp->lock);
+ /* Default format */
+ memset(&default_format, 0, sizeof(default_format));
+ default_format.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+ default_format.fmt.pix_mp.width = 32;
+ default_format.fmt.pix_mp.height = 32;
+ default_format.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_YUV420M;
+ mtk_mdp_m2m_s_fmt_mplane(file, &ctx->fh, &default_format);
+ default_format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ mtk_mdp_m2m_s_fmt_mplane(file, &ctx->fh, &default_format);
+
mtk_mdp_dbg(0, "%s [%d]", dev_name(&mdp->pdev->dev), ctx->id);
return 0;
@@ -1229,7 +1206,7 @@
goto err_m2m_init;
}
- ret = video_register_device(mdp->vdev, VFL_TYPE_GRABBER, 2);
+ ret = video_register_device(mdp->vdev, VFL_TYPE_VIDEO, 2);
if (ret) {
dev_err(dev, "failed to register video device\n");
goto err_vdev_register;
diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_vpu.c b/drivers/media/platform/mtk-mdp/mtk_mdp_vpu.c
index 6720d11..b065ccd 100644
--- a/drivers/media/platform/mtk-mdp/mtk_mdp_vpu.c
+++ b/drivers/media/platform/mtk-mdp/mtk_mdp_vpu.c
@@ -15,7 +15,7 @@
return container_of(vpu, struct mtk_mdp_ctx, vpu);
}
-static void mtk_mdp_vpu_handle_init_ack(struct mdp_ipi_comm_ack *msg)
+static void mtk_mdp_vpu_handle_init_ack(const struct mdp_ipi_comm_ack *msg)
{
struct mtk_mdp_vpu *vpu = (struct mtk_mdp_vpu *)
(unsigned long)msg->ap_inst;
@@ -26,10 +26,11 @@
vpu->inst_addr = msg->vpu_inst_addr;
}
-static void mtk_mdp_vpu_ipi_handler(void *data, unsigned int len, void *priv)
+static void mtk_mdp_vpu_ipi_handler(const void *data, unsigned int len,
+ void *priv)
{
- unsigned int msg_id = *(unsigned int *)data;
- struct mdp_ipi_comm_ack *msg = (struct mdp_ipi_comm_ack *)data;
+ const struct mdp_ipi_comm_ack *msg = data;
+ unsigned int msg_id = msg->msg_id;
struct mtk_mdp_vpu *vpu = (struct mtk_mdp_vpu *)
(unsigned long)msg->ap_inst;
struct mtk_mdp_ctx *ctx;
diff --git a/drivers/media/platform/mtk-vcodec/Makefile b/drivers/media/platform/mtk-vcodec/Makefile
index 37b94b5..4618d43 100644
--- a/drivers/media/platform/mtk-vcodec/Makefile
+++ b/drivers/media/platform/mtk-vcodec/Makefile
@@ -13,7 +13,6 @@
mtk_vcodec_dec.o \
mtk_vcodec_dec_pm.o \
-
mtk-vcodec-enc-y := venc/venc_vp8_if.o \
venc/venc_h264_if.o \
mtk_vcodec_enc.o \
@@ -24,6 +23,13 @@
mtk-vcodec-common-y := mtk_vcodec_intr.o \
- mtk_vcodec_util.o\
+ mtk_vcodec_util.o \
+ mtk_vcodec_fw.o \
-ccflags-y += -I$(srctree)/drivers/media/platform/mtk-vpu
+ifneq ($(CONFIG_VIDEO_MEDIATEK_VCODEC_VPU),)
+mtk-vcodec-common-y += mtk_vcodec_fw_vpu.o
+endif
+
+ifneq ($(CONFIG_VIDEO_MEDIATEK_VCODEC_SCP),)
+mtk-vcodec-common-y += mtk_vcodec_fw_scp.o
+endif
diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c
index 26a55c3..c768a58 100644
--- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c
+++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c
@@ -104,6 +104,7 @@
{
struct vdec_fb *disp_frame_buffer = NULL;
struct mtk_video_dec_buf *dstbuf;
+ struct vb2_v4l2_buffer *vb;
mtk_v4l2_debug(3, "[%d]", ctx->id);
if (vdec_if_get_param(ctx,
@@ -121,25 +122,26 @@
dstbuf = container_of(disp_frame_buffer, struct mtk_video_dec_buf,
frame_buffer);
+ vb = &dstbuf->m2m_buf.vb;
mutex_lock(&ctx->lock);
if (dstbuf->used) {
- vb2_set_plane_payload(&dstbuf->vb.vb2_buf, 0,
- ctx->picinfo.fb_sz[0]);
+ vb2_set_plane_payload(&vb->vb2_buf, 0,
+ ctx->picinfo.fb_sz[0]);
if (ctx->q_data[MTK_Q_DATA_DST].fmt->num_planes == 2)
- vb2_set_plane_payload(&dstbuf->vb.vb2_buf, 1,
+ vb2_set_plane_payload(&vb->vb2_buf, 1,
ctx->picinfo.fb_sz[1]);
mtk_v4l2_debug(2,
"[%d]status=%x queue id=%d to done_list %d",
ctx->id, disp_frame_buffer->status,
- dstbuf->vb.vb2_buf.index,
+ vb->vb2_buf.index,
dstbuf->queued_in_vb2);
- v4l2_m2m_buf_done(&dstbuf->vb, VB2_BUF_STATE_DONE);
+ v4l2_m2m_buf_done(vb, VB2_BUF_STATE_DONE);
ctx->decoded_frame_cnt++;
}
mutex_unlock(&ctx->lock);
- return &dstbuf->vb.vb2_buf;
+ return &vb->vb2_buf;
}
/*
@@ -154,6 +156,7 @@
{
struct mtk_video_dec_buf *dstbuf;
struct vdec_fb *free_frame_buffer = NULL;
+ struct vb2_v4l2_buffer *vb;
if (vdec_if_get_param(ctx,
GET_PARAM_FREE_FRAME_BUFFER,
@@ -171,6 +174,7 @@
dstbuf = container_of(free_frame_buffer, struct mtk_video_dec_buf,
frame_buffer);
+ vb = &dstbuf->m2m_buf.vb;
mutex_lock(&ctx->lock);
if (dstbuf->used) {
@@ -187,11 +191,10 @@
mtk_v4l2_debug(2,
"[%d]status=%x queue id=%d to rdy_queue %d",
ctx->id, free_frame_buffer->status,
- dstbuf->vb.vb2_buf.index,
+ vb->vb2_buf.index,
dstbuf->queued_in_vb2);
- v4l2_m2m_buf_queue(ctx->m2m_ctx, &dstbuf->vb);
- } else if ((dstbuf->queued_in_vb2 == false) &&
- (dstbuf->queued_in_v4l2 == true)) {
+ v4l2_m2m_buf_queue(ctx->m2m_ctx, vb);
+ } else if (!dstbuf->queued_in_vb2 && dstbuf->queued_in_v4l2) {
/*
* If buffer in v4l2 driver but not in vb2 queue yet,
* and we get this buffer from free_list, it means
@@ -205,8 +208,8 @@
mtk_v4l2_debug(2,
"[%d]status=%x queue id=%d to rdy_queue",
ctx->id, free_frame_buffer->status,
- dstbuf->vb.vb2_buf.index);
- v4l2_m2m_buf_queue(ctx->m2m_ctx, &dstbuf->vb);
+ vb->vb2_buf.index);
+ v4l2_m2m_buf_queue(ctx->m2m_ctx, vb);
dstbuf->queued_in_vb2 = true;
} else {
/*
@@ -219,14 +222,14 @@
*/
mtk_v4l2_debug(3, "[%d]status=%x err queue id=%d %d %d",
ctx->id, free_frame_buffer->status,
- dstbuf->vb.vb2_buf.index,
+ vb->vb2_buf.index,
dstbuf->queued_in_vb2,
dstbuf->queued_in_v4l2);
}
dstbuf->used = false;
}
mutex_unlock(&ctx->lock);
- return &dstbuf->vb.vb2_buf;
+ return &vb->vb2_buf;
}
static void clean_display_buffer(struct mtk_vcodec_ctx *ctx)
@@ -284,7 +287,7 @@
fmt = &mtk_video_formats[k];
if (fmt->fourcc == pixelformat) {
mtk_v4l2_debug(1, "Update cap fourcc(%d -> %d)",
- dst_q_data->fmt.fourcc, pixelformat);
+ dst_q_data->fmt->fourcc, pixelformat);
dst_q_data->fmt = fmt;
return;
}
@@ -365,8 +368,10 @@
return;
}
- src_buf_info = container_of(src_buf, struct mtk_video_dec_buf, vb);
- dst_buf_info = container_of(dst_buf, struct mtk_video_dec_buf, vb);
+ src_buf_info = container_of(src_buf, struct mtk_video_dec_buf,
+ m2m_buf.vb);
+ dst_buf_info = container_of(dst_buf, struct mtk_video_dec_buf,
+ m2m_buf.vb);
pfb = &dst_buf_info->frame_buffer;
pfb->base_y.va = vb2_plane_vaddr(&dst_buf->vb2_buf, 0);
@@ -397,11 +402,11 @@
vdec_if_decode(ctx, NULL, NULL, &res_chg);
clean_display_buffer(ctx);
- vb2_set_plane_payload(&dst_buf_info->vb.vb2_buf, 0, 0);
+ vb2_set_plane_payload(&dst_buf->vb2_buf, 0, 0);
if (ctx->q_data[MTK_Q_DATA_DST].fmt->num_planes == 2)
- vb2_set_plane_payload(&dst_buf_info->vb.vb2_buf, 1, 0);
+ vb2_set_plane_payload(&dst_buf->vb2_buf, 1, 0);
dst_buf->flags |= V4L2_BUF_FLAG_LAST;
- v4l2_m2m_buf_done(&dst_buf_info->vb, VB2_BUF_STATE_DONE);
+ v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_DONE);
clean_free_buffer(ctx);
v4l2_m2m_job_finish(dev->m2m_dev_dec, ctx->m2m_ctx);
return;
@@ -417,10 +422,8 @@
}
mtk_v4l2_debug(3, "[%d] Bitstream VA=%p DMA=%pad Size=%zx vb=%p",
ctx->id, buf.va, &buf.dma_addr, buf.size, src_buf);
- dst_buf_info->vb.vb2_buf.timestamp
- = src_buf_info->vb.vb2_buf.timestamp;
- dst_buf_info->vb.timecode
- = src_buf_info->vb.timecode;
+ dst_buf->vb2_buf.timestamp = src_buf->vb2_buf.timestamp;
+ dst_buf->timecode = src_buf->timecode;
mutex_lock(&ctx->lock);
dst_buf_info->used = true;
mutex_unlock(&ctx->lock);
@@ -434,7 +437,7 @@
ctx->id,
src_buf->vb2_buf.index,
buf.size,
- src_buf_info->vb.vb2_buf.timestamp,
+ src_buf->vb2_buf.timestamp,
dst_buf->vb2_buf.index,
ret, res_chg);
src_buf = v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
@@ -443,14 +446,14 @@
src_buf_info->error = true;
mutex_unlock(&ctx->lock);
}
- v4l2_m2m_buf_done(&src_buf_info->vb, VB2_BUF_STATE_ERROR);
- } else if (res_chg == false) {
+ v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_ERROR);
+ } else if (!res_chg) {
/*
* we only return src buffer with VB2_BUF_STATE_DONE
* when decode success without resolution change
*/
src_buf = v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
- v4l2_m2m_buf_done(&src_buf_info->vb, VB2_BUF_STATE_DONE);
+ v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE);
}
dst_buf = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx);
@@ -522,7 +525,8 @@
mtk_v4l2_debug(1, "Capture stream is off. No need to flush.");
return 0;
}
- v4l2_m2m_buf_queue(ctx->m2m_ctx, &ctx->empty_flush_buf->vb);
+ v4l2_m2m_buf_queue(ctx->m2m_ctx,
+ &ctx->empty_flush_buf->m2m_buf.vb);
v4l2_m2m_try_schedule(ctx->m2m_ctx);
break;
@@ -841,12 +845,20 @@
return -EINVAL;
pix_mp = &f->fmt.pix_mp;
+ /*
+ * Setting OUTPUT format after OUTPUT buffers are allocated is invalid
+ * if using the stateful API.
+ */
if ((f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) &&
vb2_is_busy(&ctx->m2m_ctx->out_q_ctx.q)) {
mtk_v4l2_err("out_q_ctx buffers already requested");
ret = -EBUSY;
}
+ /*
+ * Setting CAPTURE format after CAPTURE buffers are allocated is
+ * invalid.
+ */
if ((f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) &&
vb2_is_busy(&ctx->m2m_ctx->cap_q_ctx.q)) {
mtk_v4l2_err("cap_q_ctx buffers already requested");
@@ -865,6 +877,8 @@
fmt = mtk_vdec_find_format(f);
}
}
+ if (fmt == NULL)
+ return -EINVAL;
q_data->fmt = fmt;
vidioc_try_fmt(f, q_data->fmt);
@@ -873,10 +887,10 @@
q_data->coded_width = pix_mp->width;
q_data->coded_height = pix_mp->height;
- ctx->colorspace = f->fmt.pix_mp.colorspace;
- ctx->ycbcr_enc = f->fmt.pix_mp.ycbcr_enc;
- ctx->quantization = f->fmt.pix_mp.quantization;
- ctx->xfer_func = f->fmt.pix_mp.xfer_func;
+ ctx->colorspace = pix_mp->colorspace;
+ ctx->ycbcr_enc = pix_mp->ycbcr_enc;
+ ctx->quantization = pix_mp->quantization;
+ ctx->xfer_func = pix_mp->xfer_func;
if (ctx->state == MTK_STATE_FREE) {
ret = vdec_if_init(ctx, q_data->fmt->fourcc);
@@ -1138,9 +1152,10 @@
*/
if (vb->vb2_queue->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
vb2_v4l2 = to_vb2_v4l2_buffer(vb);
- buf = container_of(vb2_v4l2, struct mtk_video_dec_buf, vb);
+ buf = container_of(vb2_v4l2, struct mtk_video_dec_buf,
+ m2m_buf.vb);
mutex_lock(&ctx->lock);
- if (buf->used == false) {
+ if (!buf->used) {
v4l2_m2m_buf_queue(ctx->m2m_ctx, vb2_v4l2);
buf->queued_in_vb2 = true;
buf->queued_in_v4l2 = true;
@@ -1165,7 +1180,7 @@
mtk_v4l2_err("No src buffer");
return;
}
- buf = container_of(src_buf, struct mtk_video_dec_buf, vb);
+ buf = container_of(src_buf, struct mtk_video_dec_buf, m2m_buf.vb);
if (buf->lastframe) {
/* This shouldn't happen. Just in case. */
mtk_v4l2_err("Invalid flush buffer.");
@@ -1246,7 +1261,7 @@
bool buf_error;
vb2_v4l2 = container_of(vb, struct vb2_v4l2_buffer, vb2_buf);
- buf = container_of(vb2_v4l2, struct mtk_video_dec_buf, vb);
+ buf = container_of(vb2_v4l2, struct mtk_video_dec_buf, m2m_buf.vb);
mutex_lock(&ctx->lock);
if (vb->vb2_queue->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
buf->queued_in_v4l2 = false;
@@ -1266,7 +1281,7 @@
struct vb2_v4l2_buffer *vb2_v4l2 = container_of(vb,
struct vb2_v4l2_buffer, vb2_buf);
struct mtk_video_dec_buf *buf = container_of(vb2_v4l2,
- struct mtk_video_dec_buf, vb);
+ struct mtk_video_dec_buf, m2m_buf.vb);
if (vb->vb2_queue->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
buf->used = false;
@@ -1299,7 +1314,7 @@
if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
while ((src_buf = v4l2_m2m_src_buf_remove(ctx->m2m_ctx))) {
struct mtk_video_dec_buf *buf_info = container_of(
- src_buf, struct mtk_video_dec_buf, vb);
+ src_buf, struct mtk_video_dec_buf, m2m_buf.vb);
if (!buf_info->lastframe)
v4l2_m2m_buf_done(src_buf,
VB2_BUF_STATE_ERROR);
@@ -1509,10 +1524,8 @@
dst_vq->dev = &ctx->dev->plat_dev->dev;
ret = vb2_queue_init(dst_vq);
- if (ret) {
- vb2_queue_release(src_vq);
+ if (ret)
mtk_v4l2_err("Failed to initialize videobuf2 queue(capture)");
- }
return ret;
}
diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.h b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.h
index e0c5338..cf26b6c 100644
--- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.h
+++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.h
@@ -9,7 +9,7 @@
#define _MTK_VCODEC_DEC_H_
#include <media/videobuf2-core.h>
-#include <media/videobuf2-v4l2.h>
+#include <media/v4l2-mem2mem.h>
#define VCODEC_CAPABILITY_4K_DISABLED 0x10
#define VCODEC_DEC_4K_CODED_WIDTH 4096U
@@ -33,7 +33,7 @@
/**
* struct mtk_video_dec_buf - Private data related to each VB2 buffer.
- * @b: VB2 buffer
+ * @m2m_buf: M2M buffer
* @list: link list
* @used: Capture buffer contain decoded frame data and keep in
* codec data structure
@@ -47,8 +47,7 @@
* Note : These status information help us track and debug buffer state
*/
struct mtk_video_dec_buf {
- struct vb2_v4l2_buffer vb;
- struct list_head list;
+ struct v4l2_m2m_buffer m2m_buf;
bool used;
bool queued_in_vb2;
diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_drv.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_drv.c
index 4cde1a5..f59ef8c 100644
--- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_drv.c
+++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_drv.c
@@ -20,7 +20,7 @@
#include "mtk_vcodec_dec_pm.h"
#include "mtk_vcodec_intr.h"
#include "mtk_vcodec_util.h"
-#include "mtk_vpu.h"
+#include "mtk_vcodec_fw.h"
#define VDEC_HW_ACTIVE 0x10
#define VDEC_IRQ_CFG 0x11
@@ -77,22 +77,6 @@
return IRQ_HANDLED;
}
-static void mtk_vcodec_dec_reset_handler(void *priv)
-{
- struct mtk_vcodec_dev *dev = priv;
- struct mtk_vcodec_ctx *ctx;
-
- mtk_v4l2_err("Watchdog timeout!!");
-
- mutex_lock(&dev->dev_mutex);
- list_for_each_entry(ctx, &dev->ctx_list, list) {
- ctx->state = MTK_STATE_ABORT;
- mtk_v4l2_debug(0, "[%d] Change to state MTK_STATE_ERROR",
- ctx->id);
- }
- mutex_unlock(&dev->dev_mutex);
-}
-
static int fops_vcodec_open(struct file *file)
{
struct mtk_vcodec_dev *dev = video_drvdata(file);
@@ -137,7 +121,7 @@
}
src_vq = v4l2_m2m_get_vq(ctx->m2m_ctx,
V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
- ctx->empty_flush_buf->vb.vb2_buf.vb2_queue = src_vq;
+ ctx->empty_flush_buf->m2m_buf.vb.vb2_buf.vb2_queue = src_vq;
ctx->empty_flush_buf->lastframe = true;
mtk_vcodec_dec_set_default_params(ctx);
@@ -146,21 +130,20 @@
if (ret < 0)
goto err_load_fw;
/*
- * vpu_load_firmware checks if it was loaded already and
- * does nothing in that case
+ * Does nothing if firmware was already loaded.
*/
- ret = vpu_load_firmware(dev->vpu_plat_dev);
+ ret = mtk_vcodec_fw_load_firmware(dev->fw_handler);
if (ret < 0) {
/*
* Return 0 if downloading firmware successfully,
* otherwise it is failed
*/
- mtk_v4l2_err("vpu_load_firmware failed!");
+ mtk_v4l2_err("failed to load firmware!");
goto err_load_fw;
}
dev->dec_capability =
- vpu_get_vdec_hw_capa(dev->vpu_plat_dev);
+ mtk_vcodec_fw_get_vdec_capa(dev->fw_handler);
mtk_v4l2_debug(0, "decoder capability %x", dev->dec_capability);
}
@@ -230,6 +213,8 @@
struct mtk_vcodec_dev *dev;
struct video_device *vfd_dec;
struct resource *res;
+ phandle rproc_phandle;
+ enum mtk_vcodec_fw_type fw_type;
int i, ret;
dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
@@ -239,29 +224,37 @@
INIT_LIST_HEAD(&dev->ctx_list);
dev->plat_dev = pdev;
- dev->vpu_plat_dev = vpu_get_plat_device(dev->plat_dev);
- if (dev->vpu_plat_dev == NULL) {
- mtk_v4l2_err("[VPU] vpu device in not ready");
- return -EPROBE_DEFER;
+ if (!of_property_read_u32(pdev->dev.of_node, "mediatek,vpu",
+ &rproc_phandle)) {
+ fw_type = VPU;
+ } else if (!of_property_read_u32(pdev->dev.of_node, "mediatek,scp",
+ &rproc_phandle)) {
+ fw_type = SCP;
+ } else {
+ mtk_v4l2_err("Could not get vdec IPI device");
+ return -ENODEV;
}
+ if (!pdev->dev.dma_parms) {
+ pdev->dev.dma_parms = devm_kzalloc(&pdev->dev,
+ sizeof(*pdev->dev.dma_parms),
+ GFP_KERNEL);
+ if (!pdev->dev.dma_parms)
+ return -ENOMEM;
+ }
+ dma_set_max_seg_size(&pdev->dev, DMA_BIT_MASK(32));
- vpu_wdt_reg_handler(dev->vpu_plat_dev, mtk_vcodec_dec_reset_handler,
- dev, VPU_RST_DEC);
+ dev->fw_handler = mtk_vcodec_fw_select(dev, fw_type, DECODER);
+ if (IS_ERR(dev->fw_handler))
+ return PTR_ERR(dev->fw_handler);
ret = mtk_vcodec_init_dec_pm(dev);
if (ret < 0) {
dev_err(&pdev->dev, "Failed to get mt vcodec clock source");
- return ret;
+ goto err_dec_pm;
}
for (i = 0; i < NUM_MAX_VDEC_REG_BASE; i++) {
- res = platform_get_resource(pdev, IORESOURCE_MEM, i);
- if (res == NULL) {
- dev_err(&pdev->dev, "get memory resource failed.");
- ret = -ENXIO;
- goto err_res;
- }
- dev->reg_base[i] = devm_ioremap_resource(&pdev->dev, res);
+ dev->reg_base[i] = devm_platform_ioremap_resource(pdev, i);
if (IS_ERR((__force void *)dev->reg_base[i])) {
ret = PTR_ERR((__force void *)dev->reg_base[i]);
goto err_res;
@@ -277,6 +270,7 @@
}
dev->dec_irq = platform_get_irq(pdev, 0);
+ irq_set_status_flags(dev->dec_irq, IRQ_NOAUTOEN);
ret = devm_request_irq(&pdev->dev, dev->dec_irq,
mtk_vcodec_dec_irq_handler, 0, pdev->name, dev);
if (ret) {
@@ -286,7 +280,6 @@
goto err_res;
}
- disable_irq(dev->dec_irq);
mutex_init(&dev->dec_mutex);
mutex_init(&dev->dev_mutex);
spin_lock_init(&dev->irqlock);
@@ -339,7 +332,7 @@
goto err_event_workq;
}
- ret = video_register_device(vfd_dec, VFL_TYPE_GRABBER, 0);
+ ret = video_register_device(vfd_dec, VFL_TYPE_VIDEO, 0);
if (ret) {
mtk_v4l2_err("Failed to register video device");
goto err_dec_reg;
@@ -360,6 +353,8 @@
v4l2_device_unregister(&dev->v4l2_dev);
err_res:
mtk_vcodec_release_dec_pm(dev);
+err_dec_pm:
+ mtk_vcodec_fw_release(dev->fw_handler);
return ret;
}
@@ -384,6 +379,7 @@
v4l2_device_unregister(&dev->v4l2_dev);
mtk_vcodec_release_dec_pm(dev);
+ mtk_vcodec_fw_release(dev->fw_handler);
return 0;
}
diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_pm.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_pm.c
index 34e9e06..6038db9 100644
--- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_pm.c
+++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_pm.c
@@ -12,7 +12,6 @@
#include "mtk_vcodec_dec_pm.h"
#include "mtk_vcodec_util.h"
-#include "mtk_vpu.h"
int mtk_vcodec_init_dec_pm(struct mtk_vcodec_dev *mtkdev)
{
diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_drv.h b/drivers/media/platform/mtk-vcodec/mtk_vcodec_drv.h
index 9fd56de..3dd010c 100644
--- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_drv.h
+++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_drv.h
@@ -300,6 +300,40 @@
};
+enum mtk_chip {
+ MTK_MT8173,
+ MTK_MT8183,
+};
+
+/**
+ * struct mtk_vcodec_enc_pdata - compatible data for each IC
+ *
+ * @chip: chip this encoder is compatible with
+ *
+ * @uses_ext: whether the encoder uses the extended firmware messaging format
+ * @has_lt_irq: whether the encoder uses the LT irq
+ * @min_birate: minimum supported encoding bitrate
+ * @max_bitrate: maximum supported encoding bitrate
+ * @capture_formats: array of supported capture formats
+ * @num_capture_formats: number of entries in capture_formats
+ * @output_formats: array of supported output formats
+ * @num_output_formats: number of entries in output_formats
+ */
+struct mtk_vcodec_enc_pdata {
+ enum mtk_chip chip;
+
+ bool uses_ext;
+ bool has_lt_irq;
+ unsigned long min_bitrate;
+ unsigned long max_bitrate;
+ const struct mtk_video_fmt *capture_formats;
+ size_t num_capture_formats;
+ const struct mtk_video_fmt *output_formats;
+ size_t num_output_formats;
+};
+
+#define MTK_ENC_CTX_IS_EXT(ctx) ((ctx)->dev->venc_pdata->uses_ext)
+
/**
* struct mtk_vcodec_dev - driver data
* @v4l2_dev: V4L2 device to register video devices for.
@@ -309,13 +343,13 @@
* @m2m_dev_dec: m2m device for decoder
* @m2m_dev_enc: m2m device for encoder.
* @plat_dev: platform device
- * @vpu_plat_dev: mtk vpu platform device
* @ctx_list: list of struct mtk_vcodec_ctx
* @irqlock: protect data access by irq handler and work thread
* @curr_ctx: The context that is waiting for codec hardware
*
* @reg_base: Mapped address of MTK Vcodec registers.
*
+ * @fw_handler: used to communicate with the firmware.
* @id_counter: used to identify current opened instance
*
* @encode_workqueue: encode work queue
@@ -344,11 +378,13 @@
struct v4l2_m2m_dev *m2m_dev_dec;
struct v4l2_m2m_dev *m2m_dev_enc;
struct platform_device *plat_dev;
- struct platform_device *vpu_plat_dev;
struct list_head ctx_list;
spinlock_t irqlock;
struct mtk_vcodec_ctx *curr_ctx;
void __iomem *reg_base[NUM_MAX_VCODEC_REG_BASE];
+ const struct mtk_vcodec_enc_pdata *venc_pdata;
+
+ struct mtk_vcodec_fw *fw_handler;
unsigned long id_counter;
diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c
index fd8de02..21de143 100644
--- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c
+++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c
@@ -23,58 +23,15 @@
#define DFT_CFG_WIDTH MTK_VENC_MIN_W
#define DFT_CFG_HEIGHT MTK_VENC_MIN_H
#define MTK_MAX_CTRLS_HINT 20
-#define OUT_FMT_IDX 0
-#define CAP_FMT_IDX 4
+#define MTK_DEFAULT_FRAMERATE_NUM 1001
+#define MTK_DEFAULT_FRAMERATE_DENOM 30000
static void mtk_venc_worker(struct work_struct *work);
-static const struct mtk_video_fmt mtk_video_formats[] = {
- {
- .fourcc = V4L2_PIX_FMT_NV12M,
- .type = MTK_FMT_FRAME,
- .num_planes = 2,
- },
- {
- .fourcc = V4L2_PIX_FMT_NV21M,
- .type = MTK_FMT_FRAME,
- .num_planes = 2,
- },
- {
- .fourcc = V4L2_PIX_FMT_YUV420M,
- .type = MTK_FMT_FRAME,
- .num_planes = 3,
- },
- {
- .fourcc = V4L2_PIX_FMT_YVU420M,
- .type = MTK_FMT_FRAME,
- .num_planes = 3,
- },
- {
- .fourcc = V4L2_PIX_FMT_H264,
- .type = MTK_FMT_ENC,
- .num_planes = 1,
- },
- {
- .fourcc = V4L2_PIX_FMT_VP8,
- .type = MTK_FMT_ENC,
- .num_planes = 1,
- },
-};
-
-#define NUM_FORMATS ARRAY_SIZE(mtk_video_formats)
-
-static const struct mtk_codec_framesizes mtk_venc_framesizes[] = {
- {
- .fourcc = V4L2_PIX_FMT_H264,
- .stepwise = { MTK_VENC_MIN_W, MTK_VENC_MAX_W, 16,
- MTK_VENC_MIN_H, MTK_VENC_MAX_H, 16 },
- },
- {
- .fourcc = V4L2_PIX_FMT_VP8,
- .stepwise = { MTK_VENC_MIN_W, MTK_VENC_MAX_W, 16,
- MTK_VENC_MIN_H, MTK_VENC_MAX_H, 16 },
- },
+static const struct v4l2_frmsize_stepwise mtk_venc_framesizes = {
+ MTK_VENC_MIN_W, MTK_VENC_MAX_W, 16,
+ MTK_VENC_MIN_H, MTK_VENC_MAX_H, 16,
};
#define NUM_SUPPORTED_FRAMESIZE ARRAY_SIZE(mtk_venc_framesizes)
@@ -156,59 +113,77 @@
.s_ctrl = vidioc_venc_s_ctrl,
};
-static int vidioc_enum_fmt(struct v4l2_fmtdesc *f, bool output_queue)
+static int vidioc_enum_fmt(struct v4l2_fmtdesc *f,
+ const struct mtk_video_fmt *formats,
+ size_t num_formats)
+{
+ if (f->index >= num_formats)
+ return -EINVAL;
+
+ f->pixelformat = formats[f->index].fourcc;
+ memset(f->reserved, 0, sizeof(f->reserved));
+
+ return 0;
+}
+
+static const struct mtk_video_fmt *
+mtk_venc_find_format(u32 fourcc, const struct mtk_vcodec_enc_pdata *pdata)
{
const struct mtk_video_fmt *fmt;
- int i, j = 0;
+ unsigned int k;
- for (i = 0; i < NUM_FORMATS; ++i) {
- if (output_queue && mtk_video_formats[i].type != MTK_FMT_FRAME)
- continue;
- if (!output_queue && mtk_video_formats[i].type != MTK_FMT_ENC)
- continue;
-
- if (j == f->index) {
- fmt = &mtk_video_formats[i];
- f->pixelformat = fmt->fourcc;
- memset(f->reserved, 0, sizeof(f->reserved));
- return 0;
- }
- ++j;
+ for (k = 0; k < pdata->num_capture_formats; k++) {
+ fmt = &pdata->capture_formats[k];
+ if (fmt->fourcc == fourcc)
+ return fmt;
}
- return -EINVAL;
+ for (k = 0; k < pdata->num_output_formats; k++) {
+ fmt = &pdata->output_formats[k];
+ if (fmt->fourcc == fourcc)
+ return fmt;
+ }
+
+ return NULL;
}
static int vidioc_enum_framesizes(struct file *file, void *fh,
struct v4l2_frmsizeenum *fsize)
{
- int i = 0;
+ const struct mtk_video_fmt *fmt;
if (fsize->index != 0)
return -EINVAL;
- for (i = 0; i < NUM_SUPPORTED_FRAMESIZE; ++i) {
- if (fsize->pixel_format != mtk_venc_framesizes[i].fourcc)
- continue;
+ fmt = mtk_venc_find_format(fsize->pixel_format,
+ fh_to_ctx(fh)->dev->venc_pdata);
+ if (!fmt)
+ return -EINVAL;
- fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
- fsize->stepwise = mtk_venc_framesizes[i].stepwise;
- return 0;
- }
+ fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
+ fsize->stepwise = mtk_venc_framesizes;
- return -EINVAL;
+ return 0;
}
static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_fmtdesc *f)
{
- return vidioc_enum_fmt(f, false);
+ const struct mtk_vcodec_enc_pdata *pdata =
+ fh_to_ctx(priv)->dev->venc_pdata;
+
+ return vidioc_enum_fmt(f, pdata->capture_formats,
+ pdata->num_capture_formats);
}
static int vidioc_enum_fmt_vid_out(struct file *file, void *priv,
struct v4l2_fmtdesc *f)
{
- return vidioc_enum_fmt(f, true);
+ const struct mtk_vcodec_enc_pdata *pdata =
+ fh_to_ctx(priv)->dev->venc_pdata;
+
+ return vidioc_enum_fmt(f, pdata->output_formats,
+ pdata->num_output_formats);
}
static int vidioc_venc_querycap(struct file *file, void *priv,
@@ -225,14 +200,18 @@
struct v4l2_streamparm *a)
{
struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv);
+ struct v4l2_fract *timeperframe = &a->parm.output.timeperframe;
if (a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
return -EINVAL;
- ctx->enc_params.framerate_num =
- a->parm.output.timeperframe.denominator;
- ctx->enc_params.framerate_denom =
- a->parm.output.timeperframe.numerator;
+ if (timeperframe->numerator == 0 || timeperframe->denominator == 0) {
+ timeperframe->numerator = MTK_DEFAULT_FRAMERATE_NUM;
+ timeperframe->denominator = MTK_DEFAULT_FRAMERATE_DENOM;
+ }
+
+ ctx->enc_params.framerate_num = timeperframe->denominator;
+ ctx->enc_params.framerate_denom = timeperframe->numerator;
ctx->param_change |= MTK_ENCODE_PARAM_FRAMERATE;
a->parm.output.capability = V4L2_CAP_TIMEPERFRAME;
@@ -266,20 +245,6 @@
return &ctx->q_data[MTK_Q_DATA_DST];
}
-static const struct mtk_video_fmt *mtk_venc_find_format(struct v4l2_format *f)
-{
- const struct mtk_video_fmt *fmt;
- unsigned int k;
-
- for (k = 0; k < NUM_FORMATS; k++) {
- fmt = &mtk_video_formats[k];
- if (fmt->fourcc == f->fmt.pix.pixelformat)
- return fmt;
- }
-
- return NULL;
-}
-
/* V4L2 specification suggests the driver corrects the format struct if any of
* the dimensions is unsupported
*/
@@ -417,6 +382,7 @@
struct v4l2_format *f)
{
struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv);
+ const struct mtk_vcodec_enc_pdata *pdata = ctx->dev->venc_pdata;
struct vb2_queue *vq;
struct mtk_q_data *q_data;
int i, ret;
@@ -439,10 +405,10 @@
return -EINVAL;
}
- fmt = mtk_venc_find_format(f);
+ fmt = mtk_venc_find_format(f->fmt.pix.pixelformat, pdata);
if (!fmt) {
- f->fmt.pix.pixelformat = mtk_video_formats[CAP_FMT_IDX].fourcc;
- fmt = mtk_venc_find_format(f);
+ fmt = &ctx->dev->venc_pdata->capture_formats[0];
+ f->fmt.pix.pixelformat = fmt->fourcc;
}
q_data->fmt = fmt;
@@ -479,6 +445,7 @@
struct v4l2_format *f)
{
struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv);
+ const struct mtk_vcodec_enc_pdata *pdata = ctx->dev->venc_pdata;
struct vb2_queue *vq;
struct mtk_q_data *q_data;
int ret, i;
@@ -502,10 +469,10 @@
return -EINVAL;
}
- fmt = mtk_venc_find_format(f);
+ fmt = mtk_venc_find_format(f->fmt.pix.pixelformat, pdata);
if (!fmt) {
- f->fmt.pix.pixelformat = mtk_video_formats[OUT_FMT_IDX].fourcc;
- fmt = mtk_venc_find_format(f);
+ fmt = &ctx->dev->venc_pdata->output_formats[0];
+ f->fmt.pix.pixelformat = fmt->fourcc;
}
pix_fmt_mp->height = clamp(pix_fmt_mp->height,
@@ -583,11 +550,12 @@
{
const struct mtk_video_fmt *fmt;
struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv);
+ const struct mtk_vcodec_enc_pdata *pdata = ctx->dev->venc_pdata;
- fmt = mtk_venc_find_format(f);
+ fmt = mtk_venc_find_format(f->fmt.pix.pixelformat, pdata);
if (!fmt) {
- f->fmt.pix.pixelformat = mtk_video_formats[CAP_FMT_IDX].fourcc;
- fmt = mtk_venc_find_format(f);
+ fmt = &ctx->dev->venc_pdata->capture_formats[0];
+ f->fmt.pix.pixelformat = fmt->fourcc;
}
f->fmt.pix_mp.colorspace = ctx->colorspace;
f->fmt.pix_mp.ycbcr_enc = ctx->ycbcr_enc;
@@ -601,11 +569,13 @@
struct v4l2_format *f)
{
const struct mtk_video_fmt *fmt;
+ struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv);
+ const struct mtk_vcodec_enc_pdata *pdata = ctx->dev->venc_pdata;
- fmt = mtk_venc_find_format(f);
+ fmt = mtk_venc_find_format(f->fmt.pix.pixelformat, pdata);
if (!fmt) {
- f->fmt.pix.pixelformat = mtk_video_formats[OUT_FMT_IDX].fourcc;
- fmt = mtk_venc_find_format(f);
+ fmt = &ctx->dev->venc_pdata->output_formats[0];
+ f->fmt.pix.pixelformat = fmt->fourcc;
}
if (!f->fmt.pix_mp.colorspace) {
f->fmt.pix_mp.colorspace = V4L2_COLORSPACE_REC709;
@@ -798,13 +768,14 @@
container_of(vb, struct vb2_v4l2_buffer, vb2_buf);
struct mtk_video_enc_buf *mtk_buf =
- container_of(vb2_v4l2, struct mtk_video_enc_buf, vb);
+ container_of(vb2_v4l2, struct mtk_video_enc_buf,
+ m2m_buf.vb);
if ((vb->vb2_queue->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) &&
(ctx->param_change != MTK_ENCODE_PARAM_NONE)) {
mtk_v4l2_debug(1, "[%d] Before id=%d encode parameter change %x",
ctx->id,
- mtk_buf->vb.vb2_buf.index,
+ vb2_v4l2->vb2_buf.index,
ctx->param_change);
mtk_buf->param_change = ctx->param_change;
mtk_buf->enc_params = ctx->enc_params;
@@ -920,8 +891,17 @@
ctx->state = MTK_STATE_FREE;
}
+static int vb2ops_venc_buf_out_validate(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+
+ vbuf->field = V4L2_FIELD_NONE;
+ return 0;
+}
+
static const struct vb2_ops mtk_venc_vb2_ops = {
.queue_setup = vb2ops_venc_queue_setup,
+ .buf_out_validate = vb2ops_venc_buf_out_validate,
.buf_prepare = vb2ops_venc_buf_prepare,
.buf_queue = vb2ops_venc_buf_queue,
.wait_prepare = vb2_ops_wait_prepare,
@@ -986,7 +966,8 @@
struct venc_enc_param enc_prm;
struct vb2_v4l2_buffer *vb2_v4l2 = v4l2_m2m_next_src_buf(ctx->m2m_ctx);
struct mtk_video_enc_buf *mtk_buf =
- container_of(vb2_v4l2, struct mtk_video_enc_buf, vb);
+ container_of(vb2_v4l2, struct mtk_video_enc_buf,
+ m2m_buf.vb);
int ret = 0;
@@ -998,7 +979,7 @@
enc_prm.bitrate = mtk_buf->enc_params.bitrate;
mtk_v4l2_debug(1, "[%d] id=%d, change param br=%d",
ctx->id,
- mtk_buf->vb.vb2_buf.index,
+ vb2_v4l2->vb2_buf.index,
enc_prm.bitrate);
ret |= venc_if_set_param(ctx,
VENC_SET_PARAM_ADJUST_BITRATE,
@@ -1009,7 +990,7 @@
mtk_buf->enc_params.framerate_denom;
mtk_v4l2_debug(1, "[%d] id=%d, change param fr=%d",
ctx->id,
- mtk_buf->vb.vb2_buf.index,
+ vb2_v4l2->vb2_buf.index,
enc_prm.frm_rate);
ret |= venc_if_set_param(ctx,
VENC_SET_PARAM_ADJUST_FRAMERATE,
@@ -1026,7 +1007,7 @@
if (!ret && mtk_buf->param_change & MTK_ENCODE_PARAM_FORCE_INTRA) {
mtk_v4l2_debug(1, "[%d] id=%d, change param force I=%d",
ctx->id,
- mtk_buf->vb.vb2_buf.index,
+ vb2_v4l2->vb2_buf.index,
mtk_buf->enc_params.force_intra);
if (mtk_buf->enc_params.force_intra)
ret |= venc_if_set_param(ctx,
@@ -1188,7 +1169,7 @@
q_data->coded_height = DFT_CFG_HEIGHT;
q_data->field = V4L2_FIELD_NONE;
- q_data->fmt = &mtk_video_formats[OUT_FMT_IDX];
+ q_data->fmt = &ctx->dev->venc_pdata->output_formats[0];
v4l_bound_align_image(&q_data->coded_width,
MTK_VENC_MIN_W,
@@ -1217,12 +1198,14 @@
memset(q_data, 0, sizeof(struct mtk_q_data));
q_data->coded_width = DFT_CFG_WIDTH;
q_data->coded_height = DFT_CFG_HEIGHT;
- q_data->fmt = &mtk_video_formats[CAP_FMT_IDX];
+ q_data->fmt = &ctx->dev->venc_pdata->capture_formats[0];
q_data->field = V4L2_FIELD_NONE;
ctx->q_data[MTK_Q_DATA_DST].sizeimage[0] =
DFT_CFG_WIDTH * DFT_CFG_HEIGHT;
ctx->q_data[MTK_Q_DATA_DST].bytesperline[0] = 0;
+ ctx->enc_params.framerate_num = MTK_DEFAULT_FRAMERATE_NUM;
+ ctx->enc_params.framerate_denom = MTK_DEFAULT_FRAMERATE_DENOM;
}
int mtk_vcodec_enc_ctrls_setup(struct mtk_vcodec_ctx *ctx)
@@ -1232,8 +1215,11 @@
v4l2_ctrl_handler_init(handler, MTK_MAX_CTRLS_HINT);
+ v4l2_ctrl_new_std(handler, ops, V4L2_CID_MIN_BUFFERS_FOR_OUTPUT,
+ 1, 1, 1, 1);
v4l2_ctrl_new_std(handler, ops, V4L2_CID_MPEG_VIDEO_BITRATE,
- 1, 4000000, 1, 4000000);
+ ctx->dev->venc_pdata->min_bitrate,
+ ctx->dev->venc_pdata->max_bitrate, 1, 4000000);
v4l2_ctrl_new_std(handler, ops, V4L2_CID_MPEG_VIDEO_B_FRAMES,
0, 2, 1, 0);
v4l2_ctrl_new_std(handler, ops, V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE,
diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.h b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.h
index a9c9f86..513ee79 100644
--- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.h
+++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.h
@@ -9,7 +9,7 @@
#define _MTK_VCODEC_ENC_H_
#include <media/videobuf2-core.h>
-#include <media/videobuf2-v4l2.h>
+#include <media/v4l2-mem2mem.h>
#define MTK_VENC_IRQ_STATUS_SPS 0x1
#define MTK_VENC_IRQ_STATUS_PPS 0x2
@@ -23,15 +23,15 @@
/**
* struct mtk_video_enc_buf - Private data related to each VB2 buffer.
- * @vb: Pointer to related VB2 buffer.
+ * @m2m_buf: M2M buffer
* @list: list that buffer link to
* @param_change: Types of encode parameter change before encoding this
* buffer
* @enc_params: Encode parameters changed before encode this buffer
*/
struct mtk_video_enc_buf {
- struct vb2_v4l2_buffer vb;
- struct list_head list;
+ struct v4l2_m2m_buffer m2m_buf;
+
u32 param_change;
struct mtk_enc_params enc_params;
};
diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_drv.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_drv.c
index 1d82aa2..5f93bc6 100644
--- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_drv.c
+++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_drv.c
@@ -21,11 +21,55 @@
#include "mtk_vcodec_enc_pm.h"
#include "mtk_vcodec_intr.h"
#include "mtk_vcodec_util.h"
-#include "mtk_vpu.h"
+#include "mtk_vcodec_fw.h"
module_param(mtk_v4l2_dbg_level, int, S_IRUGO | S_IWUSR);
module_param(mtk_vcodec_dbg, bool, S_IRUGO | S_IWUSR);
+static const struct mtk_video_fmt mtk_video_formats_output_mt8173[] = {
+ {
+ .fourcc = V4L2_PIX_FMT_NV12M,
+ .type = MTK_FMT_FRAME,
+ .num_planes = 2,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_NV21M,
+ .type = MTK_FMT_FRAME,
+ .num_planes = 2,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_YUV420M,
+ .type = MTK_FMT_FRAME,
+ .num_planes = 3,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_YVU420M,
+ .type = MTK_FMT_FRAME,
+ .num_planes = 3,
+ },
+};
+
+static const struct mtk_video_fmt mtk_video_formats_capture_mt8173[] = {
+ {
+ .fourcc = V4L2_PIX_FMT_H264,
+ .type = MTK_FMT_ENC,
+ .num_planes = 1,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_VP8,
+ .type = MTK_FMT_ENC,
+ .num_planes = 1,
+ },
+};
+
+static const struct mtk_video_fmt mtk_video_formats_capture_mt8183[] = {
+ {
+ .fourcc = V4L2_PIX_FMT_H264,
+ .type = MTK_FMT_ENC,
+ .num_planes = 1,
+ },
+};
+
/* Wake up context wait_queue */
static void wake_up_ctx(struct mtk_vcodec_ctx *ctx, unsigned int reason)
{
@@ -101,22 +145,6 @@
return IRQ_HANDLED;
}
-static void mtk_vcodec_enc_reset_handler(void *priv)
-{
- struct mtk_vcodec_dev *dev = priv;
- struct mtk_vcodec_ctx *ctx;
-
- mtk_v4l2_debug(0, "Watchdog timeout!!");
-
- mutex_lock(&dev->dev_mutex);
- list_for_each_entry(ctx, &dev->ctx_list, list) {
- ctx->state = MTK_STATE_ABORT;
- mtk_v4l2_debug(0, "[%d] Change to state MTK_STATE_ABORT",
- ctx->id);
- }
- mutex_unlock(&dev->dev_mutex);
-}
-
static int fops_vcodec_open(struct file *file)
{
struct mtk_vcodec_dev *dev = video_drvdata(file);
@@ -159,10 +187,10 @@
if (v4l2_fh_is_singular(&ctx->fh)) {
/*
- * vpu_load_firmware checks if it was loaded already and
+ * load fireware to checks if it was loaded already and
* does nothing in that case
*/
- ret = vpu_load_firmware(dev->vpu_plat_dev);
+ ret = mtk_vcodec_fw_load_firmware(dev->fw_handler);
if (ret < 0) {
/*
* Return 0 if downloading firmware successfully,
@@ -173,7 +201,7 @@
}
dev->enc_capability =
- vpu_get_venc_hw_capa(dev->vpu_plat_dev);
+ mtk_vcodec_fw_get_venc_capa(dev->fw_handler);
mtk_v4l2_debug(0, "encoder capability %x", dev->enc_capability);
}
@@ -209,11 +237,11 @@
mtk_v4l2_debug(1, "[%d] encoder", ctx->id);
mutex_lock(&dev->dev_mutex);
+ v4l2_m2m_ctx_release(ctx->m2m_ctx);
mtk_vcodec_enc_release(ctx);
v4l2_fh_del(&ctx->fh);
v4l2_fh_exit(&ctx->fh);
v4l2_ctrl_handler_free(&ctx->ctrl_hdl);
- v4l2_m2m_ctx_release(ctx->m2m_ctx);
list_del_init(&ctx->list);
kfree(ctx);
@@ -235,7 +263,9 @@
struct mtk_vcodec_dev *dev;
struct video_device *vfd_enc;
struct resource *res;
- int i, j, ret;
+ phandle rproc_phandle;
+ enum mtk_vcodec_fw_type fw_type;
+ int ret;
dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
if (!dev)
@@ -244,30 +274,43 @@
INIT_LIST_HEAD(&dev->ctx_list);
dev->plat_dev = pdev;
- dev->vpu_plat_dev = vpu_get_plat_device(dev->plat_dev);
- if (dev->vpu_plat_dev == NULL) {
- mtk_v4l2_err("[VPU] vpu device in not ready");
- return -EPROBE_DEFER;
+ if (!of_property_read_u32(pdev->dev.of_node, "mediatek,vpu",
+ &rproc_phandle)) {
+ fw_type = VPU;
+ } else if (!of_property_read_u32(pdev->dev.of_node, "mediatek,scp",
+ &rproc_phandle)) {
+ fw_type = SCP;
+ } else {
+ mtk_v4l2_err("Could not get venc IPI device");
+ return -ENODEV;
}
+ if (!pdev->dev.dma_parms) {
+ pdev->dev.dma_parms = devm_kzalloc(&pdev->dev,
+ sizeof(*pdev->dev.dma_parms),
+ GFP_KERNEL);
+ if (!pdev->dev.dma_parms)
+ return -ENOMEM;
+ }
+ dma_set_max_seg_size(&pdev->dev, DMA_BIT_MASK(32));
- vpu_wdt_reg_handler(dev->vpu_plat_dev, mtk_vcodec_enc_reset_handler,
- dev, VPU_RST_ENC);
+ dev->fw_handler = mtk_vcodec_fw_select(dev, fw_type, ENCODER);
+ if (IS_ERR(dev->fw_handler))
+ return PTR_ERR(dev->fw_handler);
+ dev->venc_pdata = of_device_get_match_data(&pdev->dev);
ret = mtk_vcodec_init_enc_pm(dev);
if (ret < 0) {
dev_err(&pdev->dev, "Failed to get mt vcodec clock source!");
- return ret;
+ goto err_enc_pm;
}
- for (i = VENC_SYS, j = 0; i < NUM_MAX_VCODEC_REG_BASE; i++, j++) {
- res = platform_get_resource(pdev, IORESOURCE_MEM, j);
- dev->reg_base[i] = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR((__force void *)dev->reg_base[i])) {
- ret = PTR_ERR((__force void *)dev->reg_base[i]);
- goto err_res;
- }
- mtk_v4l2_debug(2, "reg[%d] base=0x%p", i, dev->reg_base[i]);
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ dev->reg_base[VENC_SYS] = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR((__force void *)dev->reg_base[VENC_SYS])) {
+ ret = PTR_ERR((__force void *)dev->reg_base[VENC_SYS]);
+ goto err_res;
}
+ mtk_v4l2_debug(2, "reg[%d] base=0x%p", VENC_SYS, dev->reg_base[VENC_SYS]);
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (res == NULL) {
@@ -277,6 +320,7 @@
}
dev->enc_irq = platform_get_irq(pdev, 0);
+ irq_set_status_flags(dev->enc_irq, IRQ_NOAUTOEN);
ret = devm_request_irq(&pdev->dev, dev->enc_irq,
mtk_vcodec_enc_irq_handler,
0, pdev->name, dev);
@@ -288,20 +332,30 @@
goto err_res;
}
- dev->enc_lt_irq = platform_get_irq(pdev, 1);
- ret = devm_request_irq(&pdev->dev,
- dev->enc_lt_irq, mtk_vcodec_enc_lt_irq_handler,
- 0, pdev->name, dev);
- if (ret) {
- dev_err(&pdev->dev,
- "Failed to install dev->enc_lt_irq %d (%d)",
- dev->enc_lt_irq, ret);
- ret = -EINVAL;
- goto err_res;
+ if (dev->venc_pdata->has_lt_irq) {
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ dev->reg_base[VENC_LT_SYS] = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR((__force void *)dev->reg_base[VENC_LT_SYS])) {
+ ret = PTR_ERR((__force void *)dev->reg_base[VENC_LT_SYS]);
+ goto err_res;
+ }
+ mtk_v4l2_debug(2, "reg[%d] base=0x%p", VENC_LT_SYS, dev->reg_base[VENC_LT_SYS]);
+
+ dev->enc_lt_irq = platform_get_irq(pdev, 1);
+ irq_set_status_flags(dev->enc_lt_irq, IRQ_NOAUTOEN);
+ ret = devm_request_irq(&pdev->dev,
+ dev->enc_lt_irq,
+ mtk_vcodec_enc_lt_irq_handler,
+ 0, pdev->name, dev);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "Failed to install dev->enc_lt_irq %d (%d)",
+ dev->enc_lt_irq, ret);
+ ret = -EINVAL;
+ goto err_res;
+ }
}
- disable_irq(dev->enc_irq);
- disable_irq(dev->enc_lt_irq); /* VENC_LT */
mutex_init(&dev->enc_mutex);
mutex_init(&dev->dev_mutex);
spin_lock_init(&dev->irqlock);
@@ -356,7 +410,7 @@
goto err_event_workq;
}
- ret = video_register_device(vfd_enc, VFL_TYPE_GRABBER, 1);
+ ret = video_register_device(vfd_enc, VFL_TYPE_VIDEO, 1);
if (ret) {
mtk_v4l2_err("Failed to register video device");
goto err_enc_reg;
@@ -377,11 +431,38 @@
v4l2_device_unregister(&dev->v4l2_dev);
err_res:
mtk_vcodec_release_enc_pm(dev);
+err_enc_pm:
+ mtk_vcodec_fw_release(dev->fw_handler);
return ret;
}
+static const struct mtk_vcodec_enc_pdata mt8173_pdata = {
+ .chip = MTK_MT8173,
+ .has_lt_irq = true,
+ .capture_formats = mtk_video_formats_capture_mt8173,
+ .num_capture_formats = ARRAY_SIZE(mtk_video_formats_capture_mt8173),
+ .output_formats = mtk_video_formats_output_mt8173,
+ .num_output_formats = ARRAY_SIZE(mtk_video_formats_output_mt8173),
+ .min_bitrate = 1,
+ .max_bitrate = 4000000,
+};
+
+static const struct mtk_vcodec_enc_pdata mt8183_pdata = {
+ .chip = MTK_MT8183,
+ .has_lt_irq = false,
+ .uses_ext = true,
+ .capture_formats = mtk_video_formats_capture_mt8183,
+ .num_capture_formats = ARRAY_SIZE(mtk_video_formats_capture_mt8183),
+ /* MT8183 supports the same output formats as MT8173 */
+ .output_formats = mtk_video_formats_output_mt8173,
+ .num_output_formats = ARRAY_SIZE(mtk_video_formats_output_mt8173),
+ .min_bitrate = 64,
+ .max_bitrate = 40000000,
+};
+
static const struct of_device_id mtk_vcodec_enc_match[] = {
- {.compatible = "mediatek,mt8173-vcodec-enc",},
+ {.compatible = "mediatek,mt8173-vcodec-enc", .data = &mt8173_pdata},
+ {.compatible = "mediatek,mt8183-vcodec-enc", .data = &mt8183_pdata},
{},
};
MODULE_DEVICE_TABLE(of, mtk_vcodec_enc_match);
@@ -401,6 +482,7 @@
v4l2_device_unregister(&dev->v4l2_dev);
mtk_vcodec_release_enc_pm(dev);
+ mtk_vcodec_fw_release(dev->fw_handler);
return 0;
}
diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_pm.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_pm.c
index e682bdb..1a047c2 100644
--- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_pm.c
+++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_pm.c
@@ -12,8 +12,6 @@
#include "mtk_vcodec_enc_pm.h"
#include "mtk_vcodec_util.h"
-#include "mtk_vpu.h"
-
int mtk_vcodec_init_enc_pm(struct mtk_vcodec_dev *mtkdev)
{
diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_fw.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_fw.c
new file mode 100644
index 0000000..94b39ae
--- /dev/null
+++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_fw.c
@@ -0,0 +1,67 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include "mtk_vcodec_fw.h"
+#include "mtk_vcodec_fw_priv.h"
+#include "mtk_vcodec_util.h"
+#include "mtk_vcodec_drv.h"
+
+struct mtk_vcodec_fw *mtk_vcodec_fw_select(struct mtk_vcodec_dev *dev,
+ enum mtk_vcodec_fw_type type,
+ enum mtk_vcodec_fw_use fw_use)
+{
+ switch (type) {
+ case VPU:
+ return mtk_vcodec_fw_vpu_init(dev, fw_use);
+ case SCP:
+ return mtk_vcodec_fw_scp_init(dev);
+ default:
+ mtk_v4l2_err("invalid vcodec fw type");
+ return ERR_PTR(-EINVAL);
+ }
+}
+EXPORT_SYMBOL_GPL(mtk_vcodec_fw_select);
+
+void mtk_vcodec_fw_release(struct mtk_vcodec_fw *fw)
+{
+ fw->ops->release(fw);
+}
+EXPORT_SYMBOL_GPL(mtk_vcodec_fw_release);
+
+int mtk_vcodec_fw_load_firmware(struct mtk_vcodec_fw *fw)
+{
+ return fw->ops->load_firmware(fw);
+}
+EXPORT_SYMBOL_GPL(mtk_vcodec_fw_load_firmware);
+
+unsigned int mtk_vcodec_fw_get_vdec_capa(struct mtk_vcodec_fw *fw)
+{
+ return fw->ops->get_vdec_capa(fw);
+}
+EXPORT_SYMBOL_GPL(mtk_vcodec_fw_get_vdec_capa);
+
+unsigned int mtk_vcodec_fw_get_venc_capa(struct mtk_vcodec_fw *fw)
+{
+ return fw->ops->get_venc_capa(fw);
+}
+EXPORT_SYMBOL_GPL(mtk_vcodec_fw_get_venc_capa);
+
+void *mtk_vcodec_fw_map_dm_addr(struct mtk_vcodec_fw *fw, u32 mem_addr)
+{
+ return fw->ops->map_dm_addr(fw, mem_addr);
+}
+EXPORT_SYMBOL_GPL(mtk_vcodec_fw_map_dm_addr);
+
+int mtk_vcodec_fw_ipi_register(struct mtk_vcodec_fw *fw, int id,
+ mtk_vcodec_ipi_handler handler,
+ const char *name, void *priv)
+{
+ return fw->ops->ipi_register(fw, id, handler, name, priv);
+}
+EXPORT_SYMBOL_GPL(mtk_vcodec_fw_ipi_register);
+
+int mtk_vcodec_fw_ipi_send(struct mtk_vcodec_fw *fw, int id, void *buf,
+ unsigned int len, unsigned int wait)
+{
+ return fw->ops->ipi_send(fw, id, buf, len, wait);
+}
+EXPORT_SYMBOL_GPL(mtk_vcodec_fw_ipi_send);
diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_fw.h b/drivers/media/platform/mtk-vcodec/mtk_vcodec_fw.h
new file mode 100644
index 0000000..539bb62
--- /dev/null
+++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_fw.h
@@ -0,0 +1,43 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef _MTK_VCODEC_FW_H_
+#define _MTK_VCODEC_FW_H_
+
+#include <linux/remoteproc.h>
+#include <linux/remoteproc/mtk_scp.h>
+
+#include "../mtk-vpu/mtk_vpu.h"
+
+struct mtk_vcodec_dev;
+
+enum mtk_vcodec_fw_type {
+ VPU,
+ SCP,
+};
+
+enum mtk_vcodec_fw_use {
+ DECODER,
+ ENCODER,
+};
+
+struct mtk_vcodec_fw;
+
+typedef void (*mtk_vcodec_ipi_handler) (void *data,
+ unsigned int len, void *priv);
+
+struct mtk_vcodec_fw *mtk_vcodec_fw_select(struct mtk_vcodec_dev *dev,
+ enum mtk_vcodec_fw_type type,
+ enum mtk_vcodec_fw_use fw_use);
+void mtk_vcodec_fw_release(struct mtk_vcodec_fw *fw);
+
+int mtk_vcodec_fw_load_firmware(struct mtk_vcodec_fw *fw);
+unsigned int mtk_vcodec_fw_get_vdec_capa(struct mtk_vcodec_fw *fw);
+unsigned int mtk_vcodec_fw_get_venc_capa(struct mtk_vcodec_fw *fw);
+void *mtk_vcodec_fw_map_dm_addr(struct mtk_vcodec_fw *fw, u32 mem_addr);
+int mtk_vcodec_fw_ipi_register(struct mtk_vcodec_fw *fw, int id,
+ mtk_vcodec_ipi_handler handler,
+ const char *name, void *priv);
+int mtk_vcodec_fw_ipi_send(struct mtk_vcodec_fw *fw, int id,
+ void *buf, unsigned int len, unsigned int wait);
+
+#endif /* _MTK_VCODEC_FW_H_ */
diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_fw_priv.h b/drivers/media/platform/mtk-vcodec/mtk_vcodec_fw_priv.h
new file mode 100644
index 0000000..b41e661
--- /dev/null
+++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_fw_priv.h
@@ -0,0 +1,52 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef _MTK_VCODEC_FW_PRIV_H_
+#define _MTK_VCODEC_FW_PRIV_H_
+
+#include "mtk_vcodec_fw.h"
+
+struct mtk_vcodec_dev;
+
+struct mtk_vcodec_fw {
+ enum mtk_vcodec_fw_type type;
+ const struct mtk_vcodec_fw_ops *ops;
+ struct platform_device *pdev;
+ struct mtk_scp *scp;
+};
+
+struct mtk_vcodec_fw_ops {
+ int (*load_firmware)(struct mtk_vcodec_fw *fw);
+ unsigned int (*get_vdec_capa)(struct mtk_vcodec_fw *fw);
+ unsigned int (*get_venc_capa)(struct mtk_vcodec_fw *fw);
+ void *(*map_dm_addr)(struct mtk_vcodec_fw *fw, u32 dtcm_dmem_addr);
+ int (*ipi_register)(struct mtk_vcodec_fw *fw, int id,
+ mtk_vcodec_ipi_handler handler, const char *name,
+ void *priv);
+ int (*ipi_send)(struct mtk_vcodec_fw *fw, int id, void *buf,
+ unsigned int len, unsigned int wait);
+ void (*release)(struct mtk_vcodec_fw *fw);
+};
+
+#if IS_ENABLED(CONFIG_VIDEO_MEDIATEK_VCODEC_VPU)
+struct mtk_vcodec_fw *mtk_vcodec_fw_vpu_init(struct mtk_vcodec_dev *dev,
+ enum mtk_vcodec_fw_use fw_use);
+#else
+static inline struct mtk_vcodec_fw *
+mtk_vcodec_fw_vpu_init(struct mtk_vcodec_dev *dev,
+ enum mtk_vcodec_fw_use fw_use)
+{
+ return ERR_PTR(-ENODEV);
+}
+#endif /* CONFIG_VIDEO_MEDIATEK_VCODEC_VPU */
+
+#if IS_ENABLED(CONFIG_VIDEO_MEDIATEK_VCODEC_SCP)
+struct mtk_vcodec_fw *mtk_vcodec_fw_scp_init(struct mtk_vcodec_dev *dev);
+#else
+static inline struct mtk_vcodec_fw *
+mtk_vcodec_fw_scp_init(struct mtk_vcodec_dev *dev)
+{
+ return ERR_PTR(-ENODEV);
+}
+#endif /* CONFIG_VIDEO_MEDIATEK_VCODEC_SCP */
+
+#endif /* _MTK_VCODEC_FW_PRIV_H_ */
diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_fw_scp.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_fw_scp.c
new file mode 100644
index 0000000..d8e66b6
--- /dev/null
+++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_fw_scp.c
@@ -0,0 +1,73 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include "mtk_vcodec_fw_priv.h"
+#include "mtk_vcodec_util.h"
+#include "mtk_vcodec_drv.h"
+
+static int mtk_vcodec_scp_load_firmware(struct mtk_vcodec_fw *fw)
+{
+ return rproc_boot(scp_get_rproc(fw->scp));
+}
+
+static unsigned int mtk_vcodec_scp_get_vdec_capa(struct mtk_vcodec_fw *fw)
+{
+ return scp_get_vdec_hw_capa(fw->scp);
+}
+
+static unsigned int mtk_vcodec_scp_get_venc_capa(struct mtk_vcodec_fw *fw)
+{
+ return scp_get_venc_hw_capa(fw->scp);
+}
+
+static void *mtk_vcodec_vpu_scp_dm_addr(struct mtk_vcodec_fw *fw,
+ u32 dtcm_dmem_addr)
+{
+ return scp_mapping_dm_addr(fw->scp, dtcm_dmem_addr);
+}
+
+static int mtk_vcodec_scp_set_ipi_register(struct mtk_vcodec_fw *fw, int id,
+ mtk_vcodec_ipi_handler handler,
+ const char *name, void *priv)
+{
+ return scp_ipi_register(fw->scp, id, handler, priv);
+}
+
+static int mtk_vcodec_scp_ipi_send(struct mtk_vcodec_fw *fw, int id, void *buf,
+ unsigned int len, unsigned int wait)
+{
+ return scp_ipi_send(fw->scp, id, buf, len, wait);
+}
+
+static void mtk_vcodec_scp_release(struct mtk_vcodec_fw *fw)
+{
+ scp_put(fw->scp);
+}
+
+static const struct mtk_vcodec_fw_ops mtk_vcodec_rproc_msg = {
+ .load_firmware = mtk_vcodec_scp_load_firmware,
+ .get_vdec_capa = mtk_vcodec_scp_get_vdec_capa,
+ .get_venc_capa = mtk_vcodec_scp_get_venc_capa,
+ .map_dm_addr = mtk_vcodec_vpu_scp_dm_addr,
+ .ipi_register = mtk_vcodec_scp_set_ipi_register,
+ .ipi_send = mtk_vcodec_scp_ipi_send,
+ .release = mtk_vcodec_scp_release,
+};
+
+struct mtk_vcodec_fw *mtk_vcodec_fw_scp_init(struct mtk_vcodec_dev *dev)
+{
+ struct mtk_vcodec_fw *fw;
+ struct mtk_scp *scp;
+
+ scp = scp_get(dev->plat_dev);
+ if (!scp) {
+ mtk_v4l2_err("could not get vdec scp handle");
+ return ERR_PTR(-EPROBE_DEFER);
+ }
+
+ fw = devm_kzalloc(&dev->plat_dev->dev, sizeof(*fw), GFP_KERNEL);
+ fw->type = SCP;
+ fw->ops = &mtk_vcodec_rproc_msg;
+ fw->scp = scp;
+
+ return fw;
+}
diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_fw_vpu.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_fw_vpu.c
new file mode 100644
index 0000000..cd27f63
--- /dev/null
+++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_fw_vpu.c
@@ -0,0 +1,110 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include "mtk_vcodec_fw_priv.h"
+#include "mtk_vcodec_util.h"
+#include "mtk_vcodec_drv.h"
+
+static int mtk_vcodec_vpu_load_firmware(struct mtk_vcodec_fw *fw)
+{
+ return vpu_load_firmware(fw->pdev);
+}
+
+static unsigned int mtk_vcodec_vpu_get_vdec_capa(struct mtk_vcodec_fw *fw)
+{
+ return vpu_get_vdec_hw_capa(fw->pdev);
+}
+
+static unsigned int mtk_vcodec_vpu_get_venc_capa(struct mtk_vcodec_fw *fw)
+{
+ return vpu_get_venc_hw_capa(fw->pdev);
+}
+
+static void *mtk_vcodec_vpu_map_dm_addr(struct mtk_vcodec_fw *fw,
+ u32 dtcm_dmem_addr)
+{
+ return vpu_mapping_dm_addr(fw->pdev, dtcm_dmem_addr);
+}
+
+static int mtk_vcodec_vpu_set_ipi_register(struct mtk_vcodec_fw *fw, int id,
+ mtk_vcodec_ipi_handler handler,
+ const char *name, void *priv)
+{
+ /*
+ * The handler we receive takes a void * as its first argument. We
+ * cannot change this because it needs to be passed down to the rproc
+ * subsystem when SCP is used. VPU takes a const argument, which is
+ * more constrained, so the conversion below is safe.
+ */
+ ipi_handler_t handler_const = (ipi_handler_t)handler;
+
+ return vpu_ipi_register(fw->pdev, id, handler_const, name, priv);
+}
+
+static int mtk_vcodec_vpu_ipi_send(struct mtk_vcodec_fw *fw, int id, void *buf,
+ unsigned int len, unsigned int wait)
+{
+ return vpu_ipi_send(fw->pdev, id, buf, len);
+}
+
+static void mtk_vcodec_vpu_release(struct mtk_vcodec_fw *fw)
+{
+ put_device(&fw->pdev->dev);
+}
+
+static void mtk_vcodec_vpu_reset_handler(void *priv)
+{
+ struct mtk_vcodec_dev *dev = priv;
+ struct mtk_vcodec_ctx *ctx;
+
+ mtk_v4l2_err("Watchdog timeout!!");
+
+ mutex_lock(&dev->dev_mutex);
+ list_for_each_entry(ctx, &dev->ctx_list, list) {
+ ctx->state = MTK_STATE_ABORT;
+ mtk_v4l2_debug(0, "[%d] Change to state MTK_STATE_ABORT",
+ ctx->id);
+ }
+ mutex_unlock(&dev->dev_mutex);
+}
+
+static const struct mtk_vcodec_fw_ops mtk_vcodec_vpu_msg = {
+ .load_firmware = mtk_vcodec_vpu_load_firmware,
+ .get_vdec_capa = mtk_vcodec_vpu_get_vdec_capa,
+ .get_venc_capa = mtk_vcodec_vpu_get_venc_capa,
+ .map_dm_addr = mtk_vcodec_vpu_map_dm_addr,
+ .ipi_register = mtk_vcodec_vpu_set_ipi_register,
+ .ipi_send = mtk_vcodec_vpu_ipi_send,
+ .release = mtk_vcodec_vpu_release,
+};
+
+struct mtk_vcodec_fw *mtk_vcodec_fw_vpu_init(struct mtk_vcodec_dev *dev,
+ enum mtk_vcodec_fw_use fw_use)
+{
+ struct platform_device *fw_pdev;
+ struct mtk_vcodec_fw *fw;
+ enum rst_id rst_id;
+
+ switch (fw_use) {
+ case ENCODER:
+ rst_id = VPU_RST_ENC;
+ break;
+ case DECODER:
+ default:
+ rst_id = VPU_RST_DEC;
+ break;
+ }
+
+ fw_pdev = vpu_get_plat_device(dev->plat_dev);
+ if (!fw_pdev) {
+ mtk_v4l2_err("firmware device is not ready");
+ return ERR_PTR(-EINVAL);
+ }
+ vpu_wdt_reg_handler(fw_pdev, mtk_vcodec_vpu_reset_handler, dev, rst_id);
+
+ fw = devm_kzalloc(&dev->plat_dev->dev, sizeof(*fw), GFP_KERNEL);
+ fw->type = VPU;
+ fw->ops = &mtk_vcodec_vpu_msg;
+ fw->pdev = fw_pdev;
+
+ return fw;
+}
diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_util.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_util.c
index d48f542..ac5973b 100644
--- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_util.c
+++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_util.c
@@ -9,7 +9,6 @@
#include "mtk_vcodec_drv.h"
#include "mtk_vcodec_util.h"
-#include "mtk_vpu.h"
/* For encoder, this will enable logs in venc/*/
bool mtk_vcodec_dbg;
diff --git a/drivers/media/platform/mtk-vcodec/vdec/vdec_h264_if.c b/drivers/media/platform/mtk-vcodec/vdec/vdec_h264_if.c
index 49aa85a..40d6e6c 100644
--- a/drivers/media/platform/mtk-vcodec/vdec/vdec_h264_if.c
+++ b/drivers/media/platform/mtk-vcodec/vdec/vdec_h264_if.c
@@ -281,9 +281,7 @@
inst->ctx = ctx;
inst->vpu.id = IPI_VDEC_H264;
- inst->vpu.dev = ctx->dev->vpu_plat_dev;
inst->vpu.ctx = ctx;
- inst->vpu.handler = vpu_dec_ipi_handler;
err = vpu_dec_init(&inst->vpu);
if (err) {
diff --git a/drivers/media/platform/mtk-vcodec/vdec/vdec_vp8_if.c b/drivers/media/platform/mtk-vcodec/vdec/vdec_vp8_if.c
index 63a8708..e5393f8 100644
--- a/drivers/media/platform/mtk-vcodec/vdec/vdec_vp8_if.c
+++ b/drivers/media/platform/mtk-vcodec/vdec/vdec_vp8_if.c
@@ -400,9 +400,7 @@
inst->ctx = ctx;
inst->vpu.id = IPI_VDEC_VP8;
- inst->vpu.dev = ctx->dev->vpu_plat_dev;
inst->vpu.ctx = ctx;
- inst->vpu.handler = vpu_dec_ipi_handler;
err = vpu_dec_init(&inst->vpu);
if (err) {
diff --git a/drivers/media/platform/mtk-vcodec/vdec/vdec_vp9_if.c b/drivers/media/platform/mtk-vcodec/vdec/vdec_vp9_if.c
index 5066c28..d988021 100644
--- a/drivers/media/platform/mtk-vcodec/vdec/vdec_vp9_if.c
+++ b/drivers/media/platform/mtk-vcodec/vdec/vdec_vp9_if.c
@@ -110,7 +110,11 @@
* @buf_len_sz_c : size used to store cbcr plane ufo info (AP-R, VPU-W)
* @profile : profile sparsed from vpu (AP-R, VPU-W)
- * @show_frame : display this frame or not (AP-R, VPU-W)
+ * @show_frame : [BIT(0)] display this frame or not (AP-R, VPU-W)
+ * [BIT(1)] reset segment data or not (AP-R, VPU-W)
+ * [BIT(2)] trig decoder hardware or not (AP-R, VPU-W)
+ * [BIT(3)] ask VPU to set bits(0~4) accordingly (AP-W, VPU-R)
+ * [BIT(4)] do not reset segment data before every frame (AP-R, VPU-W)
* @show_existing_frame : inform this frame is show existing frame
* (AP-R, VPU-W)
* @frm_to_show_idx : index to show frame (AP-R, VPU-W)
@@ -494,12 +498,12 @@
frm_to_show->fb->base_y.size);
}
if (!vp9_is_sf_ref_fb(inst, inst->cur_fb)) {
- if (vsi->show_frame)
+ if (vsi->show_frame & BIT(0))
vp9_add_to_fb_disp_list(inst, inst->cur_fb);
}
} else {
if (!vp9_is_sf_ref_fb(inst, inst->cur_fb)) {
- if (vsi->show_frame)
+ if (vsi->show_frame & BIT(0))
vp9_add_to_fb_disp_list(inst, frm_to_show->fb);
}
}
@@ -791,9 +795,7 @@
inst->ctx = ctx;
inst->vpu.id = IPI_VDEC_VP9;
- inst->vpu.dev = ctx->dev->vpu_plat_dev;
inst->vpu.ctx = ctx;
- inst->vpu.handler = vpu_dec_ipi_handler;
if (vpu_dec_init(&inst->vpu)) {
mtk_vcodec_err(inst, "vp9_dec_vpu_init failed");
@@ -801,6 +803,9 @@
}
inst->vsi = (struct vdec_vp9_vsi *)inst->vpu.vsi;
+
+ inst->vsi->show_frame |= BIT(3);
+
init_all_fb_lists(inst);
ctx->drv_handle = inst;
@@ -871,13 +876,28 @@
vsi->sf_frm_sz[idx]);
}
}
- memset(inst->seg_id_buf.va, 0, inst->seg_id_buf.size);
+
+ if (!(vsi->show_frame & BIT(4)))
+ memset(inst->seg_id_buf.va, 0, inst->seg_id_buf.size);
+
ret = vpu_dec_start(&inst->vpu, data, 3);
if (ret) {
mtk_vcodec_err(inst, "vpu_dec_start failed");
goto DECODE_ERROR;
}
+ if (vsi->show_frame & BIT(1)) {
+ memset(inst->seg_id_buf.va, 0, inst->seg_id_buf.size);
+
+ if (vsi->show_frame & BIT(2)) {
+ ret = vpu_dec_start(&inst->vpu, NULL, 0);
+ if (ret) {
+ mtk_vcodec_err(inst, "vpu trig decoder failed");
+ goto DECODE_ERROR;
+ }
+ }
+ }
+
ret = validate_vsi_array_indexes(inst, vsi);
if (ret) {
mtk_vcodec_err(inst, "Invalid values from VPU.");
@@ -940,7 +960,7 @@
goto DECODE_ERROR;
}
- if (vp9_decode_end_proc(inst) != true) {
+ if (!vp9_decode_end_proc(inst)) {
mtk_vcodec_err(inst, "vp9_decode_end_proc");
ret = -EINVAL;
goto DECODE_ERROR;
diff --git a/drivers/media/platform/mtk-vcodec/vdec_drv_base.h b/drivers/media/platform/mtk-vcodec/vdec_drv_base.h
index ceb4db4..e913f96 100644
--- a/drivers/media/platform/mtk-vcodec/vdec_drv_base.h
+++ b/drivers/media/platform/mtk-vcodec/vdec_drv_base.h
@@ -7,8 +7,6 @@
#ifndef _VDEC_DRV_BASE_
#define _VDEC_DRV_BASE_
-#include "mtk_vcodec_drv.h"
-
#include "vdec_drv_if.h"
struct vdec_common_if {
diff --git a/drivers/media/platform/mtk-vcodec/vdec_drv_if.c b/drivers/media/platform/mtk-vcodec/vdec_drv_if.c
index 2e43dd4..b18743b 100644
--- a/drivers/media/platform/mtk-vcodec/vdec_drv_if.c
+++ b/drivers/media/platform/mtk-vcodec/vdec_drv_if.c
@@ -13,7 +13,6 @@
#include "mtk_vcodec_dec.h"
#include "vdec_drv_base.h"
#include "mtk_vcodec_dec_pm.h"
-#include "mtk_vpu.h"
int vdec_if_init(struct mtk_vcodec_ctx *ctx, unsigned int fourcc)
{
diff --git a/drivers/media/platform/mtk-vcodec/vdec_vpu_if.c b/drivers/media/platform/mtk-vcodec/vdec_vpu_if.c
index 3f38cc4..58b0e6f 100644
--- a/drivers/media/platform/mtk-vcodec/vdec_vpu_if.c
+++ b/drivers/media/platform/mtk-vcodec/vdec_vpu_if.c
@@ -8,8 +8,9 @@
#include "mtk_vcodec_util.h"
#include "vdec_ipi_msg.h"
#include "vdec_vpu_if.h"
+#include "mtk_vcodec_fw.h"
-static void handle_init_ack_msg(struct vdec_vpu_ipi_init_ack *msg)
+static void handle_init_ack_msg(const struct vdec_vpu_ipi_init_ack *msg)
{
struct vdec_vpu_inst *vpu = (struct vdec_vpu_inst *)
(unsigned long)msg->ap_inst_addr;
@@ -18,19 +19,26 @@
/* mapping VPU address to kernel virtual address */
/* the content in vsi is initialized to 0 in VPU */
- vpu->vsi = vpu_mapping_dm_addr(vpu->dev, msg->vpu_inst_addr);
+ vpu->vsi = mtk_vcodec_fw_map_dm_addr(vpu->ctx->dev->fw_handler,
+ msg->vpu_inst_addr);
vpu->inst_addr = msg->vpu_inst_addr;
mtk_vcodec_debug(vpu, "- vpu_inst_addr = 0x%x", vpu->inst_addr);
}
/*
+ * vpu_dec_ipi_handler - Handler for VPU ipi message.
+ *
+ * @data: ipi message
+ * @len : length of ipi message
+ * @priv: callback private data which is passed by decoder when register.
+ *
* This function runs in interrupt context and it means there's an IPI MSG
* from VPU.
*/
-void vpu_dec_ipi_handler(void *data, unsigned int len, void *priv)
+static void vpu_dec_ipi_handler(void *data, unsigned int len, void *priv)
{
- struct vdec_vpu_ipi_ack *msg = data;
+ const struct vdec_vpu_ipi_ack *msg = data;
struct vdec_vpu_inst *vpu = (struct vdec_vpu_inst *)
(unsigned long)msg->ap_inst_addr;
@@ -68,7 +76,8 @@
vpu->failure = 0;
vpu->signaled = 0;
- err = vpu_ipi_send(vpu->dev, vpu->id, msg, len);
+ err = mtk_vcodec_fw_ipi_send(vpu->ctx->dev->fw_handler, vpu->id, msg,
+ len, 2000);
if (err) {
mtk_vcodec_err(vpu, "send fail vpu_id=%d msg_id=%X status=%d",
vpu->id, *(uint32_t *)msg, err);
@@ -102,8 +111,10 @@
mtk_vcodec_debug_enter(vpu);
init_waitqueue_head(&vpu->wq);
+ vpu->handler = vpu_dec_ipi_handler;
- err = vpu_ipi_register(vpu->dev, vpu->id, vpu->handler, "vdec", NULL);
+ err = mtk_vcodec_fw_ipi_register(vpu->ctx->dev->fw_handler, vpu->id,
+ vpu->handler, "vdec", NULL);
if (err != 0) {
mtk_vcodec_err(vpu, "vpu_ipi_register fail status=%d", err);
return err;
diff --git a/drivers/media/platform/mtk-vcodec/vdec_vpu_if.h b/drivers/media/platform/mtk-vcodec/vdec_vpu_if.h
index b76f717..85224eb 100644
--- a/drivers/media/platform/mtk-vcodec/vdec_vpu_if.h
+++ b/drivers/media/platform/mtk-vcodec/vdec_vpu_if.h
@@ -7,11 +7,13 @@
#ifndef _VDEC_VPU_IF_H_
#define _VDEC_VPU_IF_H_
-#include "mtk_vpu.h"
+#include "mtk_vcodec_fw.h"
+
+struct mtk_vcodec_ctx;
/**
* struct vdec_vpu_inst - VPU instance for video codec
- * @ipi_id : ipi id for each decoder
+ * @id : ipi msg id for each decoder
* @vsi : driver structure allocated by VPU side and shared to AP side
* for control and info share
* @failure : VPU execution result status, 0: success, others: fail
@@ -23,15 +25,14 @@
* @handler : ipi handler for each decoder
*/
struct vdec_vpu_inst {
- enum ipi_id id;
+ int id;
void *vsi;
int32_t failure;
uint32_t inst_addr;
unsigned int signaled;
struct mtk_vcodec_ctx *ctx;
- struct platform_device *dev;
wait_queue_head_t wq;
- ipi_handler_t handler;
+ mtk_vcodec_ipi_handler handler;
};
/**
@@ -76,13 +77,4 @@
*/
int vpu_dec_reset(struct vdec_vpu_inst *vpu);
-/**
- * vpu_dec_ipi_handler - Handler for VPU ipi message.
- *
- * @data: ipi message
- * @len : length of ipi message
- * @priv: callback private data which is passed by decoder when register.
- */
-void vpu_dec_ipi_handler(void *data, unsigned int len, void *priv);
-
#endif
diff --git a/drivers/media/platform/mtk-vcodec/venc/venc_h264_if.c b/drivers/media/platform/mtk-vcodec/venc/venc_h264_if.c
index b9624f8..d0123df 100644
--- a/drivers/media/platform/mtk-vcodec/venc/venc_h264_if.c
+++ b/drivers/media/platform/mtk-vcodec/venc/venc_h264_if.c
@@ -18,7 +18,6 @@
#include "../venc_drv_base.h"
#include "../venc_ipi_msg.h"
#include "../venc_vpu_if.h"
-#include "mtk_vpu.h"
static const char h264_filler_marker[] = {0x0, 0x0, 0x0, 0x1, 0xc};
@@ -26,6 +25,16 @@
#define VENC_PIC_BITSTREAM_BYTE_CNT 0x0098
/*
+ * enum venc_h264_frame_type - h264 encoder output bitstream frame type
+ */
+enum venc_h264_frame_type {
+ VENC_H264_IDR_FRM,
+ VENC_H264_I_FRM,
+ VENC_H264_P_FRM,
+ VENC_H264_B_FRM,
+};
+
+/*
* enum venc_h264_vpu_work_buf - h264 encoder buffer index
*/
enum venc_h264_vpu_work_buf {
@@ -139,6 +148,7 @@
struct mtk_vcodec_mem pps_buf;
bool work_buf_allocated;
unsigned int frm_cnt;
+ unsigned int skip_frm_cnt;
unsigned int prepend_hdr;
struct venc_vpu_inst vpu_inst;
struct venc_h264_vsi *vsi;
@@ -257,8 +267,11 @@
*/
inst->work_bufs[i].size = wb[i].size;
if (i == VENC_H264_VPU_WORK_BUF_SKIP_FRAME) {
- inst->work_bufs[i].va = vpu_mapping_dm_addr(
- inst->vpu_inst.dev, wb[i].vpua);
+ struct mtk_vcodec_fw *handler;
+
+ handler = inst->vpu_inst.ctx->dev->fw_handler;
+ inst->work_bufs[i].va =
+ mtk_vcodec_fw_map_dm_addr(handler, wb[i].vpua);
inst->work_bufs[i].dma_addr = 0;
} else {
ret = mtk_vcodec_mem_alloc(inst->ctx,
@@ -275,10 +288,12 @@
* setting in VPU side.
*/
if (i == VENC_H264_VPU_WORK_BUF_RC_CODE) {
+ struct mtk_vcodec_fw *handler;
void *tmp_va;
- tmp_va = vpu_mapping_dm_addr(inst->vpu_inst.dev,
- wb[i].vpua);
+ handler = inst->vpu_inst.ctx->dev->fw_handler;
+ tmp_va = mtk_vcodec_fw_map_dm_addr(handler,
+ wb[i].vpua);
memcpy(inst->work_bufs[i].va, tmp_va,
wb[i].size);
}
@@ -323,6 +338,22 @@
return irq_status;
}
+static int h264_frame_type(struct venc_h264_inst *inst)
+{
+ if ((inst->vsi->config.gop_size != 0 &&
+ (inst->frm_cnt % inst->vsi->config.gop_size) == 0) ||
+ (inst->frm_cnt == 0 && inst->vsi->config.gop_size == 0)) {
+ /* IDR frame */
+ return VENC_H264_IDR_FRM;
+ } else if ((inst->vsi->config.intra_period != 0 &&
+ (inst->frm_cnt % inst->vsi->config.intra_period) == 0) ||
+ (inst->frm_cnt == 0 && inst->vsi->config.intra_period == 0)) {
+ /* I frame */
+ return VENC_H264_I_FRM;
+ } else {
+ return VENC_H264_P_FRM; /* Note: B frames are not supported */
+ }
+}
static int h264_encode_sps(struct venc_h264_inst *inst,
struct mtk_vcodec_mem *bs_buf,
unsigned int *bs_size)
@@ -333,7 +364,7 @@
mtk_vcodec_debug_enter(inst);
ret = vpu_enc_encode(&inst->vpu_inst, H264_BS_MODE_SPS, NULL,
- bs_buf, bs_size);
+ bs_buf, bs_size, NULL);
if (ret)
return ret;
@@ -360,7 +391,7 @@
mtk_vcodec_debug_enter(inst);
ret = vpu_enc_encode(&inst->vpu_inst, H264_BS_MODE_PPS, NULL,
- bs_buf, bs_size);
+ bs_buf, bs_size, NULL);
if (ret)
return ret;
@@ -406,11 +437,18 @@
{
int ret = 0;
unsigned int irq_status;
+ struct venc_frame_info frame_info;
mtk_vcodec_debug_enter(inst);
-
+ mtk_vcodec_debug(inst, "frm_cnt = %d\n ", inst->frm_cnt);
+ frame_info.frm_count = inst->frm_cnt;
+ frame_info.skip_frm_count = inst->skip_frm_cnt;
+ frame_info.frm_type = h264_frame_type(inst);
+ mtk_vcodec_debug(inst, "frm_count = %d,skip_frm_count =%d,frm_type=%d.\n",
+ frame_info.frm_count, frame_info.skip_frm_count,
+ frame_info.frm_type);
ret = vpu_enc_encode(&inst->vpu_inst, H264_BS_MODE_FRAME, frm_buf,
- bs_buf, bs_size);
+ bs_buf, bs_size, &frame_info);
if (ret)
return ret;
@@ -424,6 +462,7 @@
inst->work_bufs[VENC_H264_VPU_WORK_BUF_SKIP_FRAME].va,
*bs_size);
++inst->frm_cnt;
+ ++inst->skip_frm_cnt;
return ret;
}
@@ -460,6 +499,7 @@
static int h264_enc_init(struct mtk_vcodec_ctx *ctx)
{
+ const bool is_ext = MTK_ENC_CTX_IS_EXT(ctx);
int ret = 0;
struct venc_h264_inst *inst;
@@ -469,8 +509,7 @@
inst->ctx = ctx;
inst->vpu_inst.ctx = ctx;
- inst->vpu_inst.dev = ctx->dev->vpu_plat_dev;
- inst->vpu_inst.id = IPI_VENC_H264;
+ inst->vpu_inst.id = is_ext ? SCP_IPI_VENC_H264 : IPI_VENC_H264;
inst->hw_base = mtk_vcodec_get_reg_addr(inst->ctx, VENC_SYS);
mtk_vcodec_debug_enter(inst);
@@ -626,7 +665,12 @@
inst->prepend_hdr = 1;
mtk_vcodec_debug(inst, "set prepend header mode");
break;
-
+ case VENC_SET_PARAM_FORCE_INTRA:
+ case VENC_SET_PARAM_GOP_SIZE:
+ case VENC_SET_PARAM_INTRA_PERIOD:
+ inst->frm_cnt = 0;
+ inst->skip_frm_cnt = 0;
+ fallthrough;
default:
ret = vpu_enc_set_param(&inst->vpu_inst, type, enc_prm);
break;
diff --git a/drivers/media/platform/mtk-vcodec/venc/venc_vp8_if.c b/drivers/media/platform/mtk-vcodec/venc/venc_vp8_if.c
index 8d36f03..11abb19 100644
--- a/drivers/media/platform/mtk-vcodec/venc/venc_vp8_if.c
+++ b/drivers/media/platform/mtk-vcodec/venc/venc_vp8_if.c
@@ -17,7 +17,6 @@
#include "../venc_drv_base.h"
#include "../venc_ipi_msg.h"
#include "../venc_vpu_if.h"
-#include "mtk_vpu.h"
#define VENC_BITSTREAM_FRAME_SIZE 0x0098
#define VENC_BITSTREAM_HEADER_LEN 0x00e8
@@ -190,10 +189,12 @@
if (i == VENC_VP8_VPU_WORK_BUF_RC_CODE ||
i == VENC_VP8_VPU_WORK_BUF_RC_CODE2 ||
i == VENC_VP8_VPU_WORK_BUF_RC_CODE3) {
+ struct mtk_vcodec_fw *handler;
void *tmp_va;
- tmp_va = vpu_mapping_dm_addr(inst->vpu_inst.dev,
- wb[i].vpua);
+ handler = inst->vpu_inst.ctx->dev->fw_handler;
+ tmp_va = mtk_vcodec_fw_map_dm_addr(handler,
+ wb[i].vpua);
memcpy(inst->work_bufs[i].va, tmp_va, wb[i].size);
}
wb[i].iova = inst->work_bufs[i].dma_addr;
@@ -301,7 +302,8 @@
mtk_vcodec_debug(inst, "->frm_cnt=%d", inst->frm_cnt);
- ret = vpu_enc_encode(&inst->vpu_inst, 0, frm_buf, bs_buf, bs_size);
+ ret = vpu_enc_encode(&inst->vpu_inst, 0, frm_buf, bs_buf, bs_size,
+ NULL);
if (ret)
return ret;
@@ -334,7 +336,6 @@
inst->ctx = ctx;
inst->vpu_inst.ctx = ctx;
- inst->vpu_inst.dev = ctx->dev->vpu_plat_dev;
inst->vpu_inst.id = IPI_VENC_VP8;
inst->hw_base = mtk_vcodec_get_reg_addr(inst->ctx, VENC_LT_SYS);
diff --git a/drivers/media/platform/mtk-vcodec/venc_drv_if.c b/drivers/media/platform/mtk-vcodec/venc_drv_if.c
index c6bb82a..ce0bce8 100644
--- a/drivers/media/platform/mtk-vcodec/venc_drv_if.c
+++ b/drivers/media/platform/mtk-vcodec/venc_drv_if.c
@@ -15,7 +15,6 @@
#include "mtk_vcodec_enc.h"
#include "mtk_vcodec_enc_pm.h"
-#include "mtk_vpu.h"
int venc_if_init(struct mtk_vcodec_ctx *ctx, unsigned int fourcc)
{
diff --git a/drivers/media/platform/mtk-vcodec/venc_drv_if.h b/drivers/media/platform/mtk-vcodec/venc_drv_if.h
index 52fc9cc..0b04a10 100644
--- a/drivers/media/platform/mtk-vcodec/venc_drv_if.h
+++ b/drivers/media/platform/mtk-vcodec/venc_drv_if.h
@@ -92,6 +92,19 @@
unsigned int gop_size;
};
+/**
+ * struct venc_frame_info - per-frame information to pass to the firmware.
+ *
+ * @frm_count: sequential number for this frame
+ * @skip_frm_count: number of frames skipped so far while decoding
+ * @frm_type: type of the frame, from enum venc_h264_frame_type
+ */
+struct venc_frame_info {
+ unsigned int frm_count; /* per frame update */
+ unsigned int skip_frm_count; /* per frame update */
+ unsigned int frm_type; /* per frame update */
+};
+
/*
* struct venc_frm_buf - frame buffer information used in venc_if_encode()
* @fb_addr: plane frame buffer addresses
diff --git a/drivers/media/platform/mtk-vcodec/venc_ipi_msg.h b/drivers/media/platform/mtk-vcodec/venc_ipi_msg.h
index 28ee04c..2feb036 100644
--- a/drivers/media/platform/mtk-vcodec/venc_ipi_msg.h
+++ b/drivers/media/platform/mtk-vcodec/venc_ipi_msg.h
@@ -62,6 +62,11 @@
uint32_t data[8];
};
+struct venc_ap_ipi_msg_set_param_ext {
+ struct venc_ap_ipi_msg_set_param base;
+ uint32_t data_ext[24];
+};
+
/**
* struct venc_ap_ipi_msg_enc - AP to VPU enc cmd structure
* @msg_id: message id (AP_IPIMSG_XXX_ENC_ENCODE)
@@ -83,6 +88,19 @@
};
/**
+ * struct venc_ap_ipi_msg_enc_ext - AP to SCP extended enc cmd structure
+ *
+ * @base: base msg structure
+ * @data_item: number of items in the data array
+ * @data[8]: data array to store the set parameters
+ */
+struct venc_ap_ipi_msg_enc_ext {
+ struct venc_ap_ipi_msg_enc base;
+ uint32_t data_item;
+ uint32_t data[32];
+};
+
+/**
* struct venc_ap_ipi_msg_deinit - AP to VPU deinit cmd structure
* @msg_id: message id (AP_IPIMSG_XXX_ENC_DEINIT)
* @vpu_inst_addr: VPU encoder instance addr
@@ -120,16 +138,17 @@
* @venc_inst: AP encoder instance (struct venc_vp8_inst/venc_h264_inst *)
* @vpu_inst_addr: VPU encoder instance addr
* (struct venc_vp8_vsi/venc_h264_vsi *)
- * @reserved: reserved for future use. vpu is running in 32bit. Without
- * this reserved field, if kernel run in 64bit. this struct size
- * will be different between kernel and vpu
+ * @venc_abi_version: ABI version of the firmware. Kernel can use it to
+ * ensure that it is compatible with the firmware.
+ * For MT8173 the value of this field is undefined and
+ * should not be used.
*/
struct venc_vpu_ipi_msg_init {
uint32_t msg_id;
uint32_t status;
uint64_t venc_inst;
uint32_t vpu_inst_addr;
- uint32_t reserved;
+ uint32_t venc_abi_version;
};
/**
diff --git a/drivers/media/platform/mtk-vcodec/venc_vpu_if.c b/drivers/media/platform/mtk-vcodec/venc_vpu_if.c
index 3e931b0..be6d879 100644
--- a/drivers/media/platform/mtk-vcodec/venc_vpu_if.c
+++ b/drivers/media/platform/mtk-vcodec/venc_vpu_if.c
@@ -4,21 +4,40 @@
* Author: PoChun Lin <pochun.lin@mediatek.com>
*/
-#include "mtk_vpu.h"
+#include "mtk_vcodec_drv.h"
+#include "mtk_vcodec_fw.h"
#include "venc_ipi_msg.h"
#include "venc_vpu_if.h"
-static void handle_enc_init_msg(struct venc_vpu_inst *vpu, void *data)
+static void handle_enc_init_msg(struct venc_vpu_inst *vpu, const void *data)
{
- struct venc_vpu_ipi_msg_init *msg = data;
+ const struct venc_vpu_ipi_msg_init *msg = data;
vpu->inst_addr = msg->vpu_inst_addr;
- vpu->vsi = vpu_mapping_dm_addr(vpu->dev, msg->vpu_inst_addr);
+ vpu->vsi = mtk_vcodec_fw_map_dm_addr(vpu->ctx->dev->fw_handler,
+ msg->vpu_inst_addr);
+
+ /* Firmware version field value is unspecified on MT8173. */
+ if (vpu->ctx->dev->venc_pdata->chip == MTK_MT8173)
+ return;
+
+ /* Check firmware version. */
+ mtk_vcodec_debug(vpu, "firmware version: 0x%x\n",
+ msg->venc_abi_version);
+ switch (msg->venc_abi_version) {
+ case 1:
+ break;
+ default:
+ mtk_vcodec_err(vpu, "unhandled firmware version 0x%x\n",
+ msg->venc_abi_version);
+ vpu->failure = 1;
+ break;
+ }
}
-static void handle_enc_encode_msg(struct venc_vpu_inst *vpu, void *data)
+static void handle_enc_encode_msg(struct venc_vpu_inst *vpu, const void *data)
{
- struct venc_vpu_ipi_msg_enc *msg = data;
+ const struct venc_vpu_ipi_msg_enc *msg = data;
vpu->state = msg->state;
vpu->bs_size = msg->bs_size;
@@ -27,13 +46,18 @@
static void vpu_enc_ipi_handler(void *data, unsigned int len, void *priv)
{
- struct venc_vpu_ipi_msg_common *msg = data;
+ const struct venc_vpu_ipi_msg_common *msg = data;
struct venc_vpu_inst *vpu =
(struct venc_vpu_inst *)(unsigned long)msg->venc_inst;
mtk_vcodec_debug(vpu, "msg_id %x inst %p status %d",
msg->msg_id, vpu, msg->status);
+ vpu->signaled = 1;
+ vpu->failure = (msg->status != VENC_IPI_MSG_STATUS_OK);
+ if (vpu->failure)
+ goto failure;
+
switch (msg->msg_id) {
case VPU_IPIMSG_ENC_INIT_DONE:
handle_enc_init_msg(vpu, data);
@@ -50,9 +74,7 @@
break;
}
- vpu->signaled = 1;
- vpu->failure = (msg->status != VENC_IPI_MSG_STATUS_OK);
-
+failure:
mtk_vcodec_debug_leave(vpu);
}
@@ -63,12 +85,13 @@
mtk_vcodec_debug_enter(vpu);
- if (!vpu->dev) {
+ if (!vpu->ctx->dev->fw_handler) {
mtk_vcodec_err(vpu, "inst dev is NULL");
return -EINVAL;
}
- status = vpu_ipi_send(vpu->dev, vpu->id, msg, len);
+ status = mtk_vcodec_fw_ipi_send(vpu->ctx->dev->fw_handler, vpu->id, msg,
+ len, 2000);
if (status) {
mtk_vcodec_err(vpu, "vpu_ipi_send msg_id %x len %d fail %d",
*(uint32_t *)msg, len, status);
@@ -93,8 +116,9 @@
vpu->signaled = 0;
vpu->failure = 0;
- status = vpu_ipi_register(vpu->dev, vpu->id, vpu_enc_ipi_handler,
- NULL, NULL);
+ status = mtk_vcodec_fw_ipi_register(vpu->ctx->dev->fw_handler, vpu->id,
+ vpu_enc_ipi_handler, "venc", NULL);
+
if (status) {
mtk_vcodec_err(vpu, "vpu_ipi_register fail %d", status);
return -EINVAL;
@@ -113,49 +137,81 @@
return 0;
}
+static unsigned int venc_enc_param_crop_right(struct venc_vpu_inst *vpu,
+ struct venc_enc_param *enc_prm)
+{
+ unsigned int img_crop_right = enc_prm->buf_width - enc_prm->width;
+
+ return img_crop_right % 16;
+}
+
+static unsigned int venc_enc_param_crop_bottom(struct venc_enc_param *enc_prm)
+{
+ return round_up(enc_prm->height, 16) - enc_prm->height;
+}
+
+static unsigned int venc_enc_param_num_mb(struct venc_enc_param *enc_prm)
+{
+ return DIV_ROUND_UP(enc_prm->width, 16) *
+ DIV_ROUND_UP(enc_prm->height, 16);
+}
+
int vpu_enc_set_param(struct venc_vpu_inst *vpu,
enum venc_set_param_type id,
struct venc_enc_param *enc_param)
{
- struct venc_ap_ipi_msg_set_param out;
+ const bool is_ext = MTK_ENC_CTX_IS_EXT(vpu->ctx);
+ size_t msg_size = is_ext ?
+ sizeof(struct venc_ap_ipi_msg_set_param_ext) :
+ sizeof(struct venc_ap_ipi_msg_set_param);
+ struct venc_ap_ipi_msg_set_param_ext out;
mtk_vcodec_debug(vpu, "id %d ->", id);
memset(&out, 0, sizeof(out));
- out.msg_id = AP_IPIMSG_ENC_SET_PARAM;
- out.vpu_inst_addr = vpu->inst_addr;
- out.param_id = id;
+ out.base.msg_id = AP_IPIMSG_ENC_SET_PARAM;
+ out.base.vpu_inst_addr = vpu->inst_addr;
+ out.base.param_id = id;
switch (id) {
case VENC_SET_PARAM_ENC:
- out.data_item = 0;
+ if (is_ext) {
+ out.base.data_item = 3;
+ out.base.data[0] =
+ venc_enc_param_crop_right(vpu, enc_param);
+ out.base.data[1] =
+ venc_enc_param_crop_bottom(enc_param);
+ out.base.data[2] = venc_enc_param_num_mb(enc_param);
+ } else {
+ out.base.data_item = 0;
+ }
break;
case VENC_SET_PARAM_FORCE_INTRA:
- out.data_item = 0;
+ out.base.data_item = 0;
break;
case VENC_SET_PARAM_ADJUST_BITRATE:
- out.data_item = 1;
- out.data[0] = enc_param->bitrate;
+ out.base.data_item = 1;
+ out.base.data[0] = enc_param->bitrate;
break;
case VENC_SET_PARAM_ADJUST_FRAMERATE:
- out.data_item = 1;
- out.data[0] = enc_param->frm_rate;
+ out.base.data_item = 1;
+ out.base.data[0] = enc_param->frm_rate;
break;
case VENC_SET_PARAM_GOP_SIZE:
- out.data_item = 1;
- out.data[0] = enc_param->gop_size;
+ out.base.data_item = 1;
+ out.base.data[0] = enc_param->gop_size;
break;
case VENC_SET_PARAM_INTRA_PERIOD:
- out.data_item = 1;
- out.data[0] = enc_param->intra_period;
+ out.base.data_item = 1;
+ out.base.data[0] = enc_param->intra_period;
break;
case VENC_SET_PARAM_SKIP_FRAME:
- out.data_item = 0;
+ out.base.data_item = 0;
break;
default:
mtk_vcodec_err(vpu, "id %d not supported", id);
return -EINVAL;
}
- if (vpu_enc_send_msg(vpu, &out, sizeof(out))) {
+ if (vpu_enc_send_msg(vpu, &out, msg_size)) {
mtk_vcodec_err(vpu,
"AP_IPIMSG_ENC_SET_PARAM %d fail", id);
return -EINVAL;
@@ -169,33 +225,44 @@
int vpu_enc_encode(struct venc_vpu_inst *vpu, unsigned int bs_mode,
struct venc_frm_buf *frm_buf,
struct mtk_vcodec_mem *bs_buf,
- unsigned int *bs_size)
+ unsigned int *bs_size,
+ struct venc_frame_info *frame_info)
{
- struct venc_ap_ipi_msg_enc out;
+ const bool is_ext = MTK_ENC_CTX_IS_EXT(vpu->ctx);
+ size_t msg_size = is_ext ?
+ sizeof(struct venc_ap_ipi_msg_enc_ext) :
+ sizeof(struct venc_ap_ipi_msg_enc);
+ struct venc_ap_ipi_msg_enc_ext out;
mtk_vcodec_debug(vpu, "bs_mode %d ->", bs_mode);
memset(&out, 0, sizeof(out));
- out.msg_id = AP_IPIMSG_ENC_ENCODE;
- out.vpu_inst_addr = vpu->inst_addr;
- out.bs_mode = bs_mode;
+ out.base.msg_id = AP_IPIMSG_ENC_ENCODE;
+ out.base.vpu_inst_addr = vpu->inst_addr;
+ out.base.bs_mode = bs_mode;
if (frm_buf) {
if ((frm_buf->fb_addr[0].dma_addr % 16 == 0) &&
(frm_buf->fb_addr[1].dma_addr % 16 == 0) &&
(frm_buf->fb_addr[2].dma_addr % 16 == 0)) {
- out.input_addr[0] = frm_buf->fb_addr[0].dma_addr;
- out.input_addr[1] = frm_buf->fb_addr[1].dma_addr;
- out.input_addr[2] = frm_buf->fb_addr[2].dma_addr;
+ out.base.input_addr[0] = frm_buf->fb_addr[0].dma_addr;
+ out.base.input_addr[1] = frm_buf->fb_addr[1].dma_addr;
+ out.base.input_addr[2] = frm_buf->fb_addr[2].dma_addr;
} else {
mtk_vcodec_err(vpu, "dma_addr not align to 16");
return -EINVAL;
}
}
if (bs_buf) {
- out.bs_addr = bs_buf->dma_addr;
- out.bs_size = bs_buf->size;
+ out.base.bs_addr = bs_buf->dma_addr;
+ out.base.bs_size = bs_buf->size;
}
- if (vpu_enc_send_msg(vpu, &out, sizeof(out))) {
+ if (is_ext && frame_info) {
+ out.data_item = 3;
+ out.data[0] = frame_info->frm_count;
+ out.data[1] = frame_info->skip_frm_count;
+ out.data[2] = frame_info->frm_type;
+ }
+ if (vpu_enc_send_msg(vpu, &out, msg_size)) {
mtk_vcodec_err(vpu, "AP_IPIMSG_ENC_ENCODE %d fail",
bs_mode);
return -EINVAL;
diff --git a/drivers/media/platform/mtk-vcodec/venc_vpu_if.h b/drivers/media/platform/mtk-vcodec/venc_vpu_if.h
index ba301a1..f9be9ca 100644
--- a/drivers/media/platform/mtk-vcodec/venc_vpu_if.h
+++ b/drivers/media/platform/mtk-vcodec/venc_vpu_if.h
@@ -7,7 +7,7 @@
#ifndef _VENC_VPU_IF_H_
#define _VENC_VPU_IF_H_
-#include "mtk_vpu.h"
+#include "mtk_vcodec_fw.h"
#include "venc_drv_if.h"
/*
@@ -34,9 +34,8 @@
int is_key_frm;
unsigned int inst_addr;
void *vsi;
- enum ipi_id id;
+ int id;
struct mtk_vcodec_ctx *ctx;
- struct platform_device *dev;
};
int vpu_enc_init(struct venc_vpu_inst *vpu);
@@ -46,7 +45,8 @@
int vpu_enc_encode(struct venc_vpu_inst *vpu, unsigned int bs_mode,
struct venc_frm_buf *frm_buf,
struct mtk_vcodec_mem *bs_buf,
- unsigned int *bs_size);
+ unsigned int *bs_size,
+ struct venc_frame_info *frame_info);
int vpu_enc_deinit(struct venc_vpu_inst *vpu);
#endif
diff --git a/drivers/media/platform/mtk-vpu/mtk_vpu.c b/drivers/media/platform/mtk-vpu/mtk_vpu.c
index cc2ff40..c62eb21 100644
--- a/drivers/media/platform/mtk-vpu/mtk_vpu.c
+++ b/drivers/media/platform/mtk-vpu/mtk_vpu.c
@@ -46,6 +46,8 @@
/* binary firmware name */
#define VPU_P_FW "vpu_p.bin"
#define VPU_D_FW "vpu_d.bin"
+#define VPU_P_FW_NEW "mediatek/mt8173/vpu_p.bin"
+#define VPU_D_FW_NEW "mediatek/mt8173/vpu_d.bin"
#define VPU_RESET 0x0
#define VPU_TCM_CFG 0x0008
@@ -203,8 +205,8 @@
struct vpu_run run;
struct vpu_wdt wdt;
struct vpu_ipi_desc ipi_desc[IPI_MAX];
- struct share_obj *recv_buf;
- struct share_obj *send_buf;
+ struct share_obj __iomem *recv_buf;
+ struct share_obj __iomem *send_buf;
struct device *dev;
struct clk *clk;
bool fw_loaded;
@@ -273,7 +275,7 @@
return -EPROBE_DEFER;
}
- if (id >= 0 && id < IPI_MAX && handler) {
+ if (id < IPI_MAX && handler) {
ipi_desc = vpu->ipi_desc;
ipi_desc[id].name = name;
ipi_desc[id].handler = handler;
@@ -292,7 +294,7 @@
unsigned int len)
{
struct mtk_vpu *vpu = platform_get_drvdata(pdev);
- struct share_obj *send_obj = vpu->send_buf;
+ struct share_obj __iomem *send_obj = vpu->send_buf;
unsigned long timeout;
int ret = 0;
@@ -325,9 +327,9 @@
}
} while (vpu_cfg_readl(vpu, HOST_TO_VPU));
- memcpy((void *)send_obj->share_buf, buf, len);
- send_obj->len = len;
- send_obj->id = id;
+ memcpy_toio(send_obj->share_buf, buf, len);
+ writel(len, &send_obj->len);
+ writel(id, &send_obj->id);
vpu->ipi_id_ack[id] = false;
/* send the command to VPU */
@@ -398,7 +400,7 @@
handler = vpu->wdt.handler;
- if (id >= 0 && id < VPU_RST_MAX && wdt_reset) {
+ if (id < VPU_RST_MAX && wdt_reset) {
dev_dbg(vpu->dev, "wdt register id %d\n", id);
mutex_lock(&vpu->vpu_mutex);
handler[id].reset_func = wdt_reset;
@@ -477,16 +479,24 @@
size_t tcm_size = fw_type ? VPU_DTCM_SIZE : VPU_PTCM_SIZE;
size_t fw_size = fw_type ? VPU_D_FW_SIZE : VPU_P_FW_SIZE;
char *fw_name = fw_type ? VPU_D_FW : VPU_P_FW;
+ char *fw_new_name = fw_type ? VPU_D_FW_NEW : VPU_P_FW_NEW;
const struct firmware *vpu_fw;
size_t dl_size = 0;
size_t extra_fw_size = 0;
void *dest;
int ret;
- ret = request_firmware(&vpu_fw, fw_name, vpu->dev);
+ ret = request_firmware(&vpu_fw, fw_new_name, vpu->dev);
if (ret < 0) {
- dev_err(vpu->dev, "Failed to load %s, %d\n", fw_name, ret);
- return ret;
+ dev_info(vpu->dev, "Failed to load %s, %d, retry\n",
+ fw_new_name, ret);
+
+ ret = request_firmware(&vpu_fw, fw_name, vpu->dev);
+ if (ret < 0) {
+ dev_err(vpu->dev, "Failed to load %s, %d\n", fw_name,
+ ret);
+ return ret;
+ }
}
dl_size = vpu_fw->size;
if (dl_size > fw_size) {
@@ -600,10 +610,10 @@
}
EXPORT_SYMBOL_GPL(vpu_load_firmware);
-static void vpu_init_ipi_handler(void *data, unsigned int len, void *priv)
+static void vpu_init_ipi_handler(const void *data, unsigned int len, void *priv)
{
- struct mtk_vpu *vpu = (struct mtk_vpu *)priv;
- struct vpu_run *run = (struct vpu_run *)data;
+ struct mtk_vpu *vpu = priv;
+ const struct vpu_run *run = data;
vpu->run.signaled = run->signaled;
strscpy(vpu->run.fw_ver, run->fw_ver, sizeof(vpu->run.fw_ver));
@@ -700,19 +710,21 @@
static void vpu_ipi_handler(struct mtk_vpu *vpu)
{
- struct share_obj *rcv_obj = vpu->recv_buf;
+ struct share_obj __iomem *rcv_obj = vpu->recv_buf;
struct vpu_ipi_desc *ipi_desc = vpu->ipi_desc;
+ unsigned char data[SHARE_BUF_SIZE];
+ s32 id = readl(&rcv_obj->id);
- if (rcv_obj->id < IPI_MAX && ipi_desc[rcv_obj->id].handler) {
- ipi_desc[rcv_obj->id].handler(rcv_obj->share_buf,
- rcv_obj->len,
- ipi_desc[rcv_obj->id].priv);
- if (rcv_obj->id > IPI_VPU_INIT) {
- vpu->ipi_id_ack[rcv_obj->id] = true;
+ memcpy_fromio(data, rcv_obj->share_buf, sizeof(data));
+ if (id < IPI_MAX && ipi_desc[id].handler) {
+ ipi_desc[id].handler(data, readl(&rcv_obj->len),
+ ipi_desc[id].priv);
+ if (id > IPI_VPU_INIT) {
+ vpu->ipi_id_ack[id] = true;
wake_up(&vpu->ack_wq);
}
} else {
- dev_err(vpu->dev, "No such ipi id = %d\n", rcv_obj->id);
+ dev_err(vpu->dev, "No such ipi id = %d\n", id);
}
}
@@ -722,11 +734,10 @@
vpu_cfg_writel(vpu, 0x0, VPU_TO_HOST);
/* shared buffer initialization */
- vpu->recv_buf = (__force struct share_obj *)(vpu->reg.tcm +
- VPU_DTCM_OFFSET);
+ vpu->recv_buf = vpu->reg.tcm + VPU_DTCM_OFFSET;
vpu->send_buf = vpu->recv_buf + 1;
- memset(vpu->recv_buf, 0, sizeof(struct share_obj));
- memset(vpu->send_buf, 0, sizeof(struct share_obj));
+ memset_io(vpu->recv_buf, 0, sizeof(struct share_obj));
+ memset_io(vpu->send_buf, 0, sizeof(struct share_obj));
return 0;
}
@@ -809,7 +820,8 @@
vpu->wdt.wq = create_singlethread_workqueue("vpu_wdt");
if (!vpu->wdt.wq) {
dev_err(dev, "initialize wdt workqueue failed\n");
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto clk_unprepare;
}
INIT_WORK(&vpu->wdt.ws, vpu_wdt_reset_func);
mutex_init(&vpu->vpu_mutex);
@@ -838,10 +850,6 @@
#ifdef CONFIG_DEBUG_FS
vpu_debugfs = debugfs_create_file("mtk_vpu", S_IRUGO, NULL, (void *)dev,
&vpu_debug_fops);
- if (!vpu_debugfs) {
- ret = -ENOMEM;
- goto cleanup_ipi;
- }
#endif
/* Set PTCM to 96K and DTCM to 32K */
@@ -899,7 +907,6 @@
of_reserved_mem_device_release(dev);
#ifdef CONFIG_DEBUG_FS
debugfs_remove(vpu_debugfs);
-cleanup_ipi:
#endif
memset(vpu->ipi_desc, 0, sizeof(struct vpu_ipi_desc) * IPI_MAX);
vpu_mutex_destroy:
@@ -908,6 +915,8 @@
vpu_clock_disable(vpu);
workqueue_destroy:
destroy_workqueue(vpu->wdt.wq);
+clk_unprepare:
+ clk_unprepare(vpu->clk);
return ret;
}
diff --git a/drivers/media/platform/mtk-vpu/mtk_vpu.h b/drivers/media/platform/mtk-vpu/mtk_vpu.h
index d4453b4..ee7c552 100644
--- a/drivers/media/platform/mtk-vpu/mtk_vpu.h
+++ b/drivers/media/platform/mtk-vpu/mtk_vpu.h
@@ -15,7 +15,7 @@
* VPU interfaces with other blocks by share memory and interrupt.
**/
-typedef void (*ipi_handler_t) (void *data,
+typedef void (*ipi_handler_t) (const void *data,
unsigned int len,
void *priv);
diff --git a/drivers/media/platform/mx2_emmaprp.c b/drivers/media/platform/mx2_emmaprp.c
index ac112cf..08a5473 100644
--- a/drivers/media/platform/mx2_emmaprp.c
+++ b/drivers/media/platform/mx2_emmaprp.c
@@ -869,7 +869,7 @@
goto rel_vdev;
}
- ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0);
+ ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0);
if (ret) {
v4l2_err(&pcdev->v4l2_dev, "Failed to register video device\n");
goto rel_m2m;
diff --git a/drivers/media/platform/omap/omap_vout.c b/drivers/media/platform/omap/omap_vout.c
index 513b99b..21193f0 100644
--- a/drivers/media/platform/omap/omap_vout.c
+++ b/drivers/media/platform/omap/omap_vout.c
@@ -1500,7 +1500,7 @@
/* Register the Video device with V4L2
*/
vfd = vout->vfd;
- if (video_register_device(vfd, VFL_TYPE_GRABBER, -1) < 0) {
+ if (video_register_device(vfd, VFL_TYPE_VIDEO, -1) < 0) {
dev_err(&pdev->dev,
": Could not register Video for Linux device\n");
vfd->minor = -1;
diff --git a/drivers/media/platform/omap3isp/isp.c b/drivers/media/platform/omap3isp/isp.c
index dce6b36..1311b49 100644
--- a/drivers/media/platform/omap3isp/isp.c
+++ b/drivers/media/platform/omap3isp/isp.c
@@ -39,8 +39,6 @@
* Troy Laramy <t-laramy@ti.com>
*/
-#include <asm/cacheflush.h>
-
#include <linux/clk.h>
#include <linux/clkdev.h>
#include <linux/delay.h>
@@ -144,7 +142,7 @@
* readback the same register, in this case the revision register.
*
* See this link for reference:
- * http://www.mail-archive.com/linux-omap@vger.kernel.org/msg08149.html
+ * https://www.mail-archive.com/linux-omap@vger.kernel.org/msg08149.html
*/
void omap3isp_flush(struct isp_device *isp)
{
@@ -810,6 +808,10 @@
ret = v4l2_subdev_call(subdev, video, s_stream, 0);
+ /* Stop at the first external sub-device. */
+ if (subdev->dev != isp->dev)
+ break;
+
if (subdev == &isp->isp_res.subdev)
ret |= isp_pipeline_wait(isp, isp_pipeline_wait_resizer);
else if (subdev == &isp->isp_prev.subdev)
@@ -837,10 +839,6 @@
&subdev->entity);
failure = -ETIMEDOUT;
}
-
- /* Stop at the first external sub-device. */
- if (subdev->dev != isp->dev)
- break;
}
return failure;
@@ -2128,21 +2126,6 @@
buscfg->bus.ccp2.crc = 1;
}
-static int isp_alloc_isd(struct isp_async_subdev **isd,
- struct isp_bus_cfg **buscfg)
-{
- struct isp_async_subdev *__isd;
-
- __isd = kzalloc(sizeof(*__isd), GFP_KERNEL);
- if (!__isd)
- return -ENOMEM;
-
- *isd = __isd;
- *buscfg = &__isd->bus;
-
- return 0;
-}
-
static struct {
u32 phy;
u32 csi2_if;
@@ -2158,7 +2141,7 @@
{
struct fwnode_handle *ep;
struct isp_async_subdev *isd = NULL;
- struct isp_bus_cfg *buscfg;
+ struct v4l2_async_subdev *asd;
unsigned int i;
ep = fwnode_graph_get_endpoint_by_id(
@@ -2176,20 +2159,15 @@
ret = v4l2_fwnode_endpoint_parse(ep, &vep);
if (!ret) {
- ret = isp_alloc_isd(&isd, &buscfg);
- if (ret)
- return ret;
- }
-
- if (!ret) {
- isp_parse_of_parallel_endpoint(isp->dev, &vep, buscfg);
- ret = v4l2_async_notifier_add_fwnode_remote_subdev(
- &isp->notifier, ep, &isd->asd);
+ asd = v4l2_async_notifier_add_fwnode_remote_subdev(
+ &isp->notifier, ep, sizeof(*isd));
+ if (!IS_ERR(asd)) {
+ isd = container_of(asd, struct isp_async_subdev, asd);
+ isp_parse_of_parallel_endpoint(isp->dev, &vep, &isd->bus);
+ }
}
fwnode_handle_put(ep);
- if (ret)
- kfree(isd);
}
for (i = 0; i < ARRAY_SIZE(isp_bus_interfaces); i++) {
@@ -2208,15 +2186,8 @@
dev_dbg(isp->dev, "parsing serial interface %u, node %pOF\n", i,
to_of_node(ep));
- ret = isp_alloc_isd(&isd, &buscfg);
- if (ret)
- return ret;
-
ret = v4l2_fwnode_endpoint_parse(ep, &vep);
- if (!ret) {
- buscfg->interface = isp_bus_interfaces[i].csi2_if;
- isp_parse_of_csi2_endpoint(isp->dev, &vep, buscfg);
- } else if (ret == -ENXIO) {
+ if (ret == -ENXIO) {
vep = (struct v4l2_fwnode_endpoint)
{ .bus_type = V4L2_MBUS_CSI1 };
ret = v4l2_fwnode_endpoint_parse(ep, &vep);
@@ -2226,21 +2197,35 @@
{ .bus_type = V4L2_MBUS_CCP2 };
ret = v4l2_fwnode_endpoint_parse(ep, &vep);
}
- if (!ret) {
- buscfg->interface =
- isp_bus_interfaces[i].csi1_if;
- isp_parse_of_csi1_endpoint(isp->dev, &vep,
- buscfg);
+ }
+
+ if (!ret) {
+ asd = v4l2_async_notifier_add_fwnode_remote_subdev(
+ &isp->notifier, ep, sizeof(*isd));
+
+ if (!IS_ERR(asd)) {
+ isd = container_of(asd, struct isp_async_subdev, asd);
+
+ switch (vep.bus_type) {
+ case V4L2_MBUS_CSI2_DPHY:
+ isd->bus.interface =
+ isp_bus_interfaces[i].csi2_if;
+ isp_parse_of_csi2_endpoint(isp->dev, &vep, &isd->bus);
+ break;
+ case V4L2_MBUS_CSI1:
+ case V4L2_MBUS_CCP2:
+ isd->bus.interface =
+ isp_bus_interfaces[i].csi1_if;
+ isp_parse_of_csi1_endpoint(isp->dev, &vep,
+ &isd->bus);
+ break;
+ default:
+ break;
+ }
}
}
- if (!ret)
- ret = v4l2_async_notifier_add_fwnode_remote_subdev(
- &isp->notifier, ep, &isd->asd);
-
fwnode_handle_put(ep);
- if (ret)
- kfree(isd);
}
return 0;
diff --git a/drivers/media/platform/omap3isp/ispccdc.c b/drivers/media/platform/omap3isp/ispccdc.c
index e2f336c..0fbb2aa 100644
--- a/drivers/media/platform/omap3isp/ispccdc.c
+++ b/drivers/media/platform/omap3isp/ispccdc.c
@@ -1312,6 +1312,10 @@
{
struct isp_device *isp = to_isp_device(ccdc);
+ /* Avoid restarting the CCDC when streaming is stopping. */
+ if (enable && ccdc->stopping & CCDC_STOP_REQUEST)
+ return;
+
isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_PCR,
ISPCCDC_PCR_EN, enable ? ISPCCDC_PCR_EN : 0);
@@ -1607,6 +1611,11 @@
return 0;
}
+ /* Don't restart CCDC if we're just about to stop streaming. */
+ if (ccdc->state == ISP_PIPELINE_STREAM_CONTINUOUS &&
+ ccdc->stopping & CCDC_STOP_REQUEST)
+ return 0;
+
if (!ccdc_has_all_fields(ccdc))
return 1;
@@ -1661,16 +1670,15 @@
spin_unlock_irqrestore(&ccdc->lock, flags);
}
- if (ccdc->output & CCDC_OUTPUT_MEMORY)
- restart = ccdc_isr_buffer(ccdc);
-
spin_lock_irqsave(&ccdc->lock, flags);
-
if (ccdc_handle_stopping(ccdc, CCDC_EVENT_VD0)) {
spin_unlock_irqrestore(&ccdc->lock, flags);
return;
}
+ if (ccdc->output & CCDC_OUTPUT_MEMORY)
+ restart = ccdc_isr_buffer(ccdc);
+
if (!ccdc->shadow_update)
ccdc_apply_controls(ccdc);
spin_unlock_irqrestore(&ccdc->lock, flags);
diff --git a/drivers/media/platform/omap3isp/isppreview.c b/drivers/media/platform/omap3isp/isppreview.c
index b8c2b8b..607b768 100644
--- a/drivers/media/platform/omap3isp/isppreview.c
+++ b/drivers/media/platform/omap3isp/isppreview.c
@@ -753,7 +753,7 @@
preview_config_luma_enhancement,
preview_enable_luma_enhancement,
offsetof(struct prev_params, luma),
- FIELD_SIZEOF(struct prev_params, luma),
+ sizeof_field(struct prev_params, luma),
offsetof(struct omap3isp_prev_update_config, luma),
}, /* OMAP3ISP_PREV_INVALAW */ {
NULL,
@@ -762,55 +762,55 @@
preview_config_hmed,
preview_enable_hmed,
offsetof(struct prev_params, hmed),
- FIELD_SIZEOF(struct prev_params, hmed),
+ sizeof_field(struct prev_params, hmed),
offsetof(struct omap3isp_prev_update_config, hmed),
}, /* OMAP3ISP_PREV_CFA */ {
preview_config_cfa,
NULL,
offsetof(struct prev_params, cfa),
- FIELD_SIZEOF(struct prev_params, cfa),
+ sizeof_field(struct prev_params, cfa),
offsetof(struct omap3isp_prev_update_config, cfa),
}, /* OMAP3ISP_PREV_CHROMA_SUPP */ {
preview_config_chroma_suppression,
preview_enable_chroma_suppression,
offsetof(struct prev_params, csup),
- FIELD_SIZEOF(struct prev_params, csup),
+ sizeof_field(struct prev_params, csup),
offsetof(struct omap3isp_prev_update_config, csup),
}, /* OMAP3ISP_PREV_WB */ {
preview_config_whitebalance,
NULL,
offsetof(struct prev_params, wbal),
- FIELD_SIZEOF(struct prev_params, wbal),
+ sizeof_field(struct prev_params, wbal),
offsetof(struct omap3isp_prev_update_config, wbal),
}, /* OMAP3ISP_PREV_BLKADJ */ {
preview_config_blkadj,
NULL,
offsetof(struct prev_params, blkadj),
- FIELD_SIZEOF(struct prev_params, blkadj),
+ sizeof_field(struct prev_params, blkadj),
offsetof(struct omap3isp_prev_update_config, blkadj),
}, /* OMAP3ISP_PREV_RGB2RGB */ {
preview_config_rgb_blending,
NULL,
offsetof(struct prev_params, rgb2rgb),
- FIELD_SIZEOF(struct prev_params, rgb2rgb),
+ sizeof_field(struct prev_params, rgb2rgb),
offsetof(struct omap3isp_prev_update_config, rgb2rgb),
}, /* OMAP3ISP_PREV_COLOR_CONV */ {
preview_config_csc,
NULL,
offsetof(struct prev_params, csc),
- FIELD_SIZEOF(struct prev_params, csc),
+ sizeof_field(struct prev_params, csc),
offsetof(struct omap3isp_prev_update_config, csc),
}, /* OMAP3ISP_PREV_YC_LIMIT */ {
preview_config_yc_range,
NULL,
offsetof(struct prev_params, yclimit),
- FIELD_SIZEOF(struct prev_params, yclimit),
+ sizeof_field(struct prev_params, yclimit),
offsetof(struct omap3isp_prev_update_config, yclimit),
}, /* OMAP3ISP_PREV_DEFECT_COR */ {
preview_config_dcor,
preview_enable_dcor,
offsetof(struct prev_params, dcor),
- FIELD_SIZEOF(struct prev_params, dcor),
+ sizeof_field(struct prev_params, dcor),
offsetof(struct omap3isp_prev_update_config, dcor),
}, /* Previously OMAP3ISP_PREV_GAMMABYPASS, not used anymore */ {
NULL,
@@ -828,13 +828,13 @@
preview_config_noisefilter,
preview_enable_noisefilter,
offsetof(struct prev_params, nf),
- FIELD_SIZEOF(struct prev_params, nf),
+ sizeof_field(struct prev_params, nf),
offsetof(struct omap3isp_prev_update_config, nf),
}, /* OMAP3ISP_PREV_GAMMA */ {
preview_config_gammacorrn,
preview_enable_gammacorrn,
offsetof(struct prev_params, gamma),
- FIELD_SIZEOF(struct prev_params, gamma),
+ sizeof_field(struct prev_params, gamma),
offsetof(struct omap3isp_prev_update_config, gamma),
}, /* OMAP3ISP_PREV_CONTRAST */ {
preview_config_contrast,
diff --git a/drivers/media/platform/omap3isp/ispvideo.c b/drivers/media/platform/omap3isp/ispvideo.c
index ee183c3..8811d6d 100644
--- a/drivers/media/platform/omap3isp/ispvideo.c
+++ b/drivers/media/platform/omap3isp/ispvideo.c
@@ -10,7 +10,6 @@
* Sakari Ailus <sakari.ailus@iki.fi>
*/
-#include <asm/cacheflush.h>
#include <linux/clk.h>
#include <linux/mm.h>
#include <linux/module.h>
@@ -704,7 +703,7 @@
* requested.
*/
format->fmt.pix.field = V4L2_FIELD_INTERLACED_TB;
- /* Fall-through */
+ fallthrough;
case V4L2_FIELD_INTERLACED_TB:
case V4L2_FIELD_INTERLACED_BT:
/* Interlaced orders are only supported at the CCDC output. */
@@ -1311,7 +1310,7 @@
goto done;
}
- ret = v4l2_pipeline_pm_use(&video->video.entity, 1);
+ ret = v4l2_pipeline_pm_get(&video->video.entity);
if (ret < 0) {
omap3isp_put(video->isp);
goto done;
@@ -1363,7 +1362,7 @@
vb2_queue_release(&handle->queue);
mutex_unlock(&video->queue_lock);
- v4l2_pipeline_pm_use(&video->video.entity, 0);
+ v4l2_pipeline_pm_put(&video->video.entity);
/* Release the file handle. */
v4l2_fh_del(vfh);
@@ -1453,7 +1452,7 @@
video->video.fops = &isp_video_fops;
snprintf(video->video.name, sizeof(video->video.name),
"OMAP3 ISP %s %s", name, direction);
- video->video.vfl_type = VFL_TYPE_GRABBER;
+ video->video.vfl_type = VFL_TYPE_VIDEO;
video->video.release = video_device_release_empty;
video->video.ioctl_ops = &isp_video_ioctl_ops;
if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
@@ -1484,7 +1483,7 @@
video->video.v4l2_dev = vdev;
- ret = video_register_device(&video->video, VFL_TYPE_GRABBER, -1);
+ ret = video_register_device(&video->video, VFL_TYPE_VIDEO, -1);
if (ret < 0)
dev_err(video->isp->dev,
"%s: could not register video device (%d)\n",
diff --git a/drivers/media/platform/pxa_camera.c b/drivers/media/platform/pxa_camera.c
index 6e04e3e..4ee7d53 100644
--- a/drivers/media/platform/pxa_camera.c
+++ b/drivers/media/platform/pxa_camera.c
@@ -605,42 +605,6 @@
return pxa_mbus_find_fmtdesc(code, mbus_fmt, ARRAY_SIZE(mbus_fmt));
}
-static unsigned int pxa_mbus_config_compatible(const struct v4l2_mbus_config *cfg,
- unsigned int flags)
-{
- unsigned long common_flags;
- bool hsync = true, vsync = true, pclk, data, mode;
- bool mipi_lanes, mipi_clock;
-
- common_flags = cfg->flags & flags;
-
- switch (cfg->type) {
- case V4L2_MBUS_PARALLEL:
- hsync = common_flags & (V4L2_MBUS_HSYNC_ACTIVE_HIGH |
- V4L2_MBUS_HSYNC_ACTIVE_LOW);
- vsync = common_flags & (V4L2_MBUS_VSYNC_ACTIVE_HIGH |
- V4L2_MBUS_VSYNC_ACTIVE_LOW);
- /* fall through */
- case V4L2_MBUS_BT656:
- pclk = common_flags & (V4L2_MBUS_PCLK_SAMPLE_RISING |
- V4L2_MBUS_PCLK_SAMPLE_FALLING);
- data = common_flags & (V4L2_MBUS_DATA_ACTIVE_HIGH |
- V4L2_MBUS_DATA_ACTIVE_LOW);
- mode = common_flags & (V4L2_MBUS_MASTER | V4L2_MBUS_SLAVE);
- return (!hsync || !vsync || !pclk || !data || !mode) ?
- 0 : common_flags;
- case V4L2_MBUS_CSI2_DPHY:
- mipi_lanes = common_flags & V4L2_MBUS_CSI2_LANES;
- mipi_clock = common_flags & (V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK |
- V4L2_MBUS_CSI2_CONTINUOUS_CLOCK);
- return (!mipi_lanes || !mipi_clock) ? 0 : common_flags;
- default:
- WARN_ON(1);
- return -EINVAL;
- }
- return 0;
-}
-
/**
* struct pxa_camera_format_xlate - match between host and sensor formats
* @code: code of a sensor provided format
@@ -1016,7 +980,7 @@
* - 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
*/
@@ -1186,9 +1150,9 @@
clk_disable_unprepare(pcdev->clk);
}
-static void pxa_camera_eof(unsigned long arg)
+static void pxa_camera_eof(struct tasklet_struct *t)
{
- struct pxa_camera_dev *pcdev = (struct pxa_camera_dev *)arg;
+ struct pxa_camera_dev *pcdev = from_tasklet(pcdev, t, task_eof);
unsigned long cifr;
struct pxa_buffer *buf;
@@ -1231,31 +1195,6 @@
return IRQ_HANDLED;
}
-static int test_platform_param(struct pxa_camera_dev *pcdev,
- unsigned char buswidth, unsigned long *flags)
-{
- /*
- * Platform specified synchronization and pixel clock polarities are
- * only a recommendation and are only used during probing. The PXA270
- * quick capture interface supports both.
- */
- *flags = (pcdev->platform_flags & PXA_CAMERA_MASTER ?
- V4L2_MBUS_MASTER : V4L2_MBUS_SLAVE) |
- V4L2_MBUS_HSYNC_ACTIVE_HIGH |
- V4L2_MBUS_HSYNC_ACTIVE_LOW |
- V4L2_MBUS_VSYNC_ACTIVE_HIGH |
- V4L2_MBUS_VSYNC_ACTIVE_LOW |
- V4L2_MBUS_DATA_ACTIVE_HIGH |
- V4L2_MBUS_PCLK_SAMPLE_RISING |
- V4L2_MBUS_PCLK_SAMPLE_FALLING;
-
- /* If requested data width is supported by the platform, use it */
- if ((1 << (buswidth - 1)) & pcdev->width_flags)
- return 0;
-
- return -EINVAL;
-}
-
static void pxa_camera_setup_cicr(struct pxa_camera_dev *pcdev,
unsigned long flags, __u32 pixfmt)
{
@@ -1438,7 +1377,7 @@
/*
* 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.
*/
@@ -1601,99 +1540,78 @@
*/
static int pxa_camera_set_bus_param(struct pxa_camera_dev *pcdev)
{
+ unsigned int bus_width = pcdev->current_fmt->host_fmt->bits_per_sample;
struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
u32 pixfmt = pcdev->current_fmt->host_fmt->fourcc;
- unsigned long bus_flags, common_flags;
+ int mbus_config;
int ret;
- ret = test_platform_param(pcdev,
- pcdev->current_fmt->host_fmt->bits_per_sample,
- &bus_flags);
- if (ret < 0)
- return ret;
-
- ret = sensor_call(pcdev, video, g_mbus_config, &cfg);
- if (!ret) {
- common_flags = pxa_mbus_config_compatible(&cfg,
- bus_flags);
- if (!common_flags) {
- dev_warn(pcdev_to_dev(pcdev),
- "Flags incompatible: camera 0x%x, host 0x%lx\n",
- cfg.flags, bus_flags);
- return -EINVAL;
- }
- } else if (ret != -ENOIOCTLCMD) {
- return ret;
- } else {
- common_flags = bus_flags;
+ if (!((1 << (bus_width - 1)) & pcdev->width_flags)) {
+ dev_err(pcdev_to_dev(pcdev), "Unsupported bus width %u",
+ bus_width);
+ return -EINVAL;
}
pcdev->channels = 1;
/* Make choices, based on platform preferences */
- if ((common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) &&
- (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) {
- if (pcdev->platform_flags & PXA_CAMERA_HSP)
- common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_HIGH;
- else
- common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_LOW;
- }
+ mbus_config = 0;
+ if (pcdev->platform_flags & PXA_CAMERA_MASTER)
+ mbus_config |= V4L2_MBUS_MASTER;
+ else
+ mbus_config |= V4L2_MBUS_SLAVE;
- if ((common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) &&
- (common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) {
- if (pcdev->platform_flags & PXA_CAMERA_VSP)
- common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_HIGH;
- else
- common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_LOW;
- }
+ if (pcdev->platform_flags & PXA_CAMERA_HSP)
+ mbus_config |= V4L2_MBUS_HSYNC_ACTIVE_HIGH;
+ else
+ mbus_config |= V4L2_MBUS_HSYNC_ACTIVE_LOW;
- if ((common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) &&
- (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)) {
- if (pcdev->platform_flags & PXA_CAMERA_PCP)
- common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_RISING;
- else
- common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_FALLING;
- }
+ if (pcdev->platform_flags & PXA_CAMERA_VSP)
+ mbus_config |= V4L2_MBUS_VSYNC_ACTIVE_HIGH;
+ else
+ mbus_config |= V4L2_MBUS_VSYNC_ACTIVE_LOW;
- cfg.flags = common_flags;
- ret = sensor_call(pcdev, video, s_mbus_config, &cfg);
+ if (pcdev->platform_flags & PXA_CAMERA_PCP)
+ mbus_config |= V4L2_MBUS_PCLK_SAMPLE_RISING;
+ else
+ mbus_config |= V4L2_MBUS_PCLK_SAMPLE_FALLING;
+ mbus_config |= V4L2_MBUS_DATA_ACTIVE_HIGH;
+
+ cfg.flags = mbus_config;
+ ret = sensor_call(pcdev, pad, set_mbus_config, 0, &cfg);
if (ret < 0 && ret != -ENOIOCTLCMD) {
- dev_dbg(pcdev_to_dev(pcdev),
- "camera s_mbus_config(0x%lx) returned %d\n",
- common_flags, ret);
+ dev_err(pcdev_to_dev(pcdev),
+ "Failed to call set_mbus_config: %d\n", ret);
return ret;
}
- pxa_camera_setup_cicr(pcdev, common_flags, pixfmt);
-
- return 0;
-}
-
-static int pxa_camera_try_bus_param(struct pxa_camera_dev *pcdev,
- unsigned char buswidth)
-{
- struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
- unsigned long bus_flags, common_flags;
- int ret = test_platform_param(pcdev, buswidth, &bus_flags);
-
- if (ret < 0)
- return ret;
-
- ret = sensor_call(pcdev, video, g_mbus_config, &cfg);
- if (!ret) {
- common_flags = pxa_mbus_config_compatible(&cfg,
- bus_flags);
- if (!common_flags) {
- dev_warn(pcdev_to_dev(pcdev),
- "Flags incompatible: camera 0x%x, host 0x%lx\n",
- cfg.flags, bus_flags);
+ /*
+ * If the requested media bus configuration has not been fully applied
+ * make sure it is supported by the platform.
+ *
+ * PXA does not support V4L2_MBUS_DATA_ACTIVE_LOW and the bus mastering
+ * roles should match.
+ */
+ if (cfg.flags != mbus_config) {
+ unsigned int pxa_mbus_role = mbus_config & (V4L2_MBUS_MASTER |
+ V4L2_MBUS_SLAVE);
+ if (pxa_mbus_role != (cfg.flags & (V4L2_MBUS_MASTER |
+ V4L2_MBUS_SLAVE))) {
+ dev_err(pcdev_to_dev(pcdev),
+ "Unsupported mbus configuration: bus mastering\n");
return -EINVAL;
}
- } else if (ret == -ENOIOCTLCMD) {
- ret = 0;
+
+ if (cfg.flags & V4L2_MBUS_DATA_ACTIVE_LOW) {
+ dev_err(pcdev_to_dev(pcdev),
+ "Unsupported mbus configuration: DATA_ACTIVE_LOW\n");
+ return -EINVAL;
+ }
}
- return ret;
+ pxa_camera_setup_cicr(pcdev, cfg.flags, pixfmt);
+
+ return 0;
}
static const struct pxa_mbus_pixelfmt pxa_camera_formats[] = {
@@ -1741,11 +1659,6 @@
return 0;
}
- /* This also checks support for the requested bits-per-sample */
- ret = pxa_camera_try_bus_param(pcdev, fmt->bits_per_sample);
- if (ret < 0)
- return 0;
-
switch (code.code) {
case MEDIA_BUS_FMT_UYVY8_2X8:
formats++;
@@ -2194,7 +2107,7 @@
if (err)
goto out_sensor_poweroff;
- err = video_register_device(&pcdev->vdev, VFL_TYPE_GRABBER, -1);
+ err = video_register_device(&pcdev->vdev, VFL_TYPE_VIDEO, -1);
if (err) {
v4l2_err(v4l2_dev, "register video device failed: %d\n", err);
pcdev->sensor = NULL;
@@ -2443,23 +2356,23 @@
pcdev->base = base;
/* request dma */
- pcdev->dma_chans[0] = dma_request_slave_channel(&pdev->dev, "CI_Y");
- if (!pcdev->dma_chans[0]) {
+ pcdev->dma_chans[0] = dma_request_chan(&pdev->dev, "CI_Y");
+ if (IS_ERR(pcdev->dma_chans[0])) {
dev_err(&pdev->dev, "Can't request DMA for Y\n");
- return -ENODEV;
+ return PTR_ERR(pcdev->dma_chans[0]);
}
- pcdev->dma_chans[1] = dma_request_slave_channel(&pdev->dev, "CI_U");
- if (!pcdev->dma_chans[1]) {
- dev_err(&pdev->dev, "Can't request DMA for Y\n");
- err = -ENODEV;
+ pcdev->dma_chans[1] = dma_request_chan(&pdev->dev, "CI_U");
+ if (IS_ERR(pcdev->dma_chans[1])) {
+ dev_err(&pdev->dev, "Can't request DMA for U\n");
+ err = PTR_ERR(pcdev->dma_chans[1]);
goto exit_free_dma_y;
}
- pcdev->dma_chans[2] = dma_request_slave_channel(&pdev->dev, "CI_V");
- if (!pcdev->dma_chans[2]) {
+ pcdev->dma_chans[2] = dma_request_chan(&pdev->dev, "CI_V");
+ if (IS_ERR(pcdev->dma_chans[2])) {
dev_err(&pdev->dev, "Can't request DMA for V\n");
- err = -ENODEV;
+ err = PTR_ERR(pcdev->dma_chans[2]);
goto exit_free_dma_u;
}
@@ -2481,7 +2394,7 @@
goto exit_free_dma;
}
- tasklet_init(&pcdev->task_eof, pxa_camera_eof, (unsigned long)pcdev);
+ tasklet_setup(&pcdev->task_eof, pxa_camera_eof);
pxa_camera_activate(pcdev);
@@ -2507,17 +2420,14 @@
if (err)
goto exit_notifier_cleanup;
- if (pcdev->mclk) {
- v4l2_clk_name_i2c(clk_name, sizeof(clk_name),
- pcdev->asd.match.i2c.adapter_id,
- pcdev->asd.match.i2c.address);
+ v4l2_clk_name_i2c(clk_name, sizeof(clk_name),
+ pcdev->asd.match.i2c.adapter_id,
+ pcdev->asd.match.i2c.address);
- pcdev->mclk_clk = v4l2_clk_register(&pxa_camera_mclk_ops,
- clk_name, NULL);
- if (IS_ERR(pcdev->mclk_clk)) {
- err = PTR_ERR(pcdev->mclk_clk);
- goto exit_notifier_cleanup;
- }
+ pcdev->mclk_clk = v4l2_clk_register(&pxa_camera_mclk_ops, clk_name, NULL);
+ if (IS_ERR(pcdev->mclk_clk)) {
+ err = PTR_ERR(pcdev->mclk_clk);
+ goto exit_notifier_cleanup;
}
err = v4l2_async_notifier_register(&pcdev->v4l2_dev, &pcdev->notifier);
@@ -2533,6 +2443,7 @@
v4l2_device_unregister(&pcdev->v4l2_dev);
exit_deactivate:
pxa_camera_deactivate(pcdev);
+ tasklet_kill(&pcdev->task_eof);
exit_free_dma:
dma_release_channel(pcdev->dma_chans[2]);
exit_free_dma_u:
@@ -2547,6 +2458,7 @@
struct pxa_camera_dev *pcdev = dev_get_drvdata(&pdev->dev);
pxa_camera_deactivate(pcdev);
+ tasklet_kill(&pcdev->task_eof);
dma_release_channel(pcdev->dma_chans[0]);
dma_release_channel(pcdev->dma_chans[1]);
dma_release_channel(pcdev->dma_chans[2]);
@@ -2589,7 +2501,7 @@
module_platform_driver(pxa_camera_driver);
-MODULE_DESCRIPTION("PXA27x SoC Camera Host driver");
+MODULE_DESCRIPTION("PXA27x Camera Driver");
MODULE_AUTHOR("Guennadi Liakhovetski <kernel@pengutronix.de>");
MODULE_LICENSE("GPL");
MODULE_VERSION(PXA_CAM_VERSION);
diff --git a/drivers/media/platform/qcom/camss/camss-csid.c b/drivers/media/platform/qcom/camss/camss-csid.c
index a5ae856..2ffcda0 100644
--- a/drivers/media/platform/qcom/camss/camss-csid.c
+++ b/drivers/media/platform/qcom/camss/camss-csid.c
@@ -562,8 +562,10 @@
u32 hw_version;
ret = pm_runtime_get_sync(dev);
- if (ret < 0)
+ if (ret < 0) {
+ pm_runtime_put_sync(dev);
return ret;
+ }
ret = regulator_enable(csid->vdda);
if (ret < 0) {
@@ -1356,7 +1358,7 @@
pads[MSM_CSID_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
pads[MSM_CSID_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE;
- sd->entity.function = MEDIA_ENT_F_IO_V4L;
+ sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER;
sd->entity.ops = &csid_media_ops;
ret = media_entity_pads_init(&sd->entity, MSM_CSID_PADS_NUM, pads);
if (ret < 0) {
diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.c b/drivers/media/platform/qcom/camss/camss-csiphy.c
index 3c5b908..85b2405 100644
--- a/drivers/media/platform/qcom/camss/camss-csiphy.c
+++ b/drivers/media/platform/qcom/camss/camss-csiphy.c
@@ -739,7 +739,7 @@
pads[MSM_CSIPHY_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
pads[MSM_CSIPHY_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE;
- sd->entity.function = MEDIA_ENT_F_IO_V4L;
+ sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER;
sd->entity.ops = &csiphy_media_ops;
ret = media_entity_pads_init(&sd->entity, MSM_CSIPHY_PADS_NUM, pads);
if (ret < 0) {
diff --git a/drivers/media/platform/qcom/camss/camss-ispif.c b/drivers/media/platform/qcom/camss/camss-ispif.c
index 1f33b4e..db94cfd 100644
--- a/drivers/media/platform/qcom/camss/camss-ispif.c
+++ b/drivers/media/platform/qcom/camss/camss-ispif.c
@@ -344,8 +344,10 @@
}
ret = pm_runtime_get_sync(dev);
- if (ret < 0)
+ if (ret < 0) {
+ pm_runtime_put_sync(dev);
goto exit;
+ }
ret = camss_enable_clocks(ispif->nclocks, ispif->clock, dev);
if (ret < 0) {
@@ -1323,7 +1325,7 @@
pads[MSM_ISPIF_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
pads[MSM_ISPIF_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE;
- sd->entity.function = MEDIA_ENT_F_IO_V4L;
+ sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER;
sd->entity.ops = &ispif_media_ops;
ret = media_entity_pads_init(&sd->entity, MSM_ISPIF_PADS_NUM,
pads);
diff --git a/drivers/media/platform/qcom/camss/camss-vfe.c b/drivers/media/platform/qcom/camss/camss-vfe.c
index a8c542f..b7d2293 100644
--- a/drivers/media/platform/qcom/camss/camss-vfe.c
+++ b/drivers/media/platform/qcom/camss/camss-vfe.c
@@ -1265,12 +1265,12 @@
ret = vfe_set_clock_rates(vfe);
if (ret < 0)
- goto error_clocks;
+ goto error_pm_runtime_get;
ret = camss_enable_clocks(vfe->nclocks, vfe->clock,
vfe->camss->dev);
if (ret < 0)
- goto error_clocks;
+ goto error_pm_runtime_get;
ret = vfe_reset(vfe);
if (ret < 0)
@@ -1282,7 +1282,7 @@
} else {
ret = vfe_check_clock_rates(vfe);
if (ret < 0)
- goto error_clocks;
+ goto error_pm_runtime_get;
}
vfe->power_count++;
@@ -1293,10 +1293,8 @@
error_reset:
camss_disable_clocks(vfe->nclocks, vfe->clock);
-error_clocks:
- pm_runtime_put_sync(vfe->camss->dev);
-
error_pm_runtime_get:
+ pm_runtime_put_sync(vfe->camss->dev);
camss_pm_domain_off(vfe->camss, vfe->id);
error_pm_domain:
@@ -2207,14 +2205,6 @@
.flush_buffers = vfe_flush_buffers,
};
-void msm_vfe_stop_streaming(struct vfe_device *vfe)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(vfe->line); i++)
- msm_video_stop_streaming(&vfe->line[i].video_out);
-}
-
/*
* msm_vfe_register_entities - Register subdev node for VFE module
* @vfe: VFE device
diff --git a/drivers/media/platform/qcom/camss/camss-vfe.h b/drivers/media/platform/qcom/camss/camss-vfe.h
index 0d10071..a90b0d2 100644
--- a/drivers/media/platform/qcom/camss/camss-vfe.h
+++ b/drivers/media/platform/qcom/camss/camss-vfe.h
@@ -178,8 +178,6 @@
void msm_vfe_get_vfe_id(struct media_entity *entity, u8 *id);
void msm_vfe_get_vfe_line_id(struct media_entity *entity, enum vfe_line_id *id);
-void msm_vfe_stop_streaming(struct vfe_device *vfe);
-
extern const struct vfe_hw_ops vfe_ops_4_1;
extern const struct vfe_hw_ops vfe_ops_4_7;
diff --git a/drivers/media/platform/qcom/camss/camss-video.c b/drivers/media/platform/qcom/camss/camss-video.c
index 4c2675b..15965e6 100644
--- a/drivers/media/platform/qcom/camss/camss-video.c
+++ b/drivers/media/platform/qcom/camss/camss-video.c
@@ -18,6 +18,12 @@
#include "camss-video.h"
#include "camss.h"
+#define CAMSS_FRAME_MIN_WIDTH 1
+#define CAMSS_FRAME_MAX_WIDTH 8191
+#define CAMSS_FRAME_MIN_HEIGHT 1
+#define CAMSS_FRAME_MAX_HEIGHT_RDI 8191
+#define CAMSS_FRAME_MAX_HEIGHT_PIX 4096
+
struct fract {
u8 numerator;
u8 denominator;
@@ -529,17 +535,16 @@
return 0;
}
-static int video_enum_fmt(struct file *file, void *fh, struct v4l2_fmtdesc *f)
+/*
+ * Returns the index in the video->formats[] array of the element which
+ * has the "ndx"th unique value of pixelformat field.
+ * If not found (no more unique pixelformat's) returns -EINVAL.
+ */
+static int video_get_unique_pixelformat_by_index(struct camss_video *video,
+ int ndx)
{
- struct camss_video *video = video_drvdata(file);
int i, j, k;
- if (f->type != video->type)
- return -EINVAL;
-
- if (f->index >= video->nformats)
- return -EINVAL;
-
/* find index "i" of "k"th unique pixelformat in formats array */
k = -1;
for (i = 0; i < video->nformats; i++) {
@@ -552,11 +557,53 @@
if (j == i)
k++;
- if (k == f->index)
- break;
+ if (k == ndx)
+ return i;
}
- if (k < f->index)
+ return -EINVAL;
+}
+
+/*
+ * Returns the index in the video->formats[] array of the element which
+ * has code equal to mcode.
+ * If not found returns -EINVAL.
+ */
+static int video_get_pixelformat_by_mbus_code(struct camss_video *video,
+ u32 mcode)
+{
+ int i;
+
+ for (i = 0; i < video->nformats; i++) {
+ if (video->formats[i].code == mcode)
+ return i;
+ }
+
+ return -EINVAL;
+}
+
+static int video_enum_fmt(struct file *file, void *fh, struct v4l2_fmtdesc *f)
+{
+ struct camss_video *video = video_drvdata(file);
+ int i;
+
+ if (f->type != video->type)
+ return -EINVAL;
+
+ if (f->index >= video->nformats)
+ return -EINVAL;
+
+ if (f->mbus_code) {
+ /* Each entry in formats[] table has unique mbus_code */
+ if (f->index > 0)
+ return -EINVAL;
+
+ i = video_get_pixelformat_by_mbus_code(video, f->mbus_code);
+ } else {
+ i = video_get_unique_pixelformat_by_index(video, f->index);
+ }
+
+ if (i < 0)
return -EINVAL;
f->pixelformat = video->formats[i].pixelformat;
@@ -564,6 +611,36 @@
return 0;
}
+static int video_enum_framesizes(struct file *file, void *fh,
+ struct v4l2_frmsizeenum *fsize)
+{
+ struct camss_video *video = video_drvdata(file);
+ int i;
+
+ if (fsize->index)
+ return -EINVAL;
+
+ /* Only accept pixel format present in the formats[] table */
+ for (i = 0; i < video->nformats; i++) {
+ if (video->formats[i].pixelformat == fsize->pixel_format)
+ break;
+ }
+
+ if (i == video->nformats)
+ return -EINVAL;
+
+ fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS;
+ fsize->stepwise.min_width = CAMSS_FRAME_MIN_WIDTH;
+ fsize->stepwise.max_width = CAMSS_FRAME_MAX_WIDTH;
+ fsize->stepwise.min_height = CAMSS_FRAME_MIN_HEIGHT;
+ fsize->stepwise.max_height = (video->line_based) ?
+ CAMSS_FRAME_MAX_HEIGHT_PIX : CAMSS_FRAME_MAX_HEIGHT_RDI;
+ fsize->stepwise.step_width = 1;
+ fsize->stepwise.step_height = 1;
+
+ return 0;
+}
+
static int video_g_fmt(struct file *file, void *fh, struct v4l2_format *f)
{
struct camss_video *video = video_drvdata(file);
@@ -593,7 +670,7 @@
1, 65528);
sizeimage[i] = clamp_t(u32, p->sizeimage,
bytesperline[i],
- bytesperline[i] * 4096);
+ bytesperline[i] * CAMSS_FRAME_MAX_HEIGHT_PIX);
}
for (j = 0; j < video->nformats; j++)
@@ -610,8 +687,8 @@
memset(pix_mp, 0, sizeof(*pix_mp));
pix_mp->pixelformat = fi->pixelformat;
- pix_mp->width = clamp_t(u32, width, 1, 8191);
- pix_mp->height = clamp_t(u32, height, 1, 8191);
+ pix_mp->width = clamp_t(u32, width, 1, CAMSS_FRAME_MAX_WIDTH);
+ pix_mp->height = clamp_t(u32, height, 1, CAMSS_FRAME_MAX_HEIGHT_RDI);
pix_mp->num_planes = fi->planes;
for (i = 0; i < pix_mp->num_planes; i++) {
bpl = pix_mp->width / fi->hsub[i].numerator *
@@ -637,7 +714,7 @@
1, 65528);
p->sizeimage = clamp_t(u32, p->sizeimage,
p->bytesperline,
- p->bytesperline * 4096);
+ p->bytesperline * CAMSS_FRAME_MAX_HEIGHT_PIX);
lines = p->sizeimage / p->bytesperline;
if (p->bytesperline < bytesperline[i])
@@ -704,6 +781,7 @@
static const struct v4l2_ioctl_ops msm_vid_ioctl_ops = {
.vidioc_querycap = video_querycap,
.vidioc_enum_fmt_vid_cap = video_enum_fmt,
+ .vidioc_enum_framesizes = video_enum_framesizes,
.vidioc_g_fmt_vid_cap_mplane = video_g_fmt,
.vidioc_s_fmt_vid_cap_mplane = video_s_fmt,
.vidioc_try_fmt_vid_cap_mplane = video_try_fmt,
@@ -745,7 +823,7 @@
file->private_data = vfh;
- ret = v4l2_pipeline_pm_use(&vdev->entity, 1);
+ ret = v4l2_pipeline_pm_get(&vdev->entity);
if (ret < 0) {
dev_err(video->camss->dev, "Failed to power up pipeline: %d\n",
ret);
@@ -771,7 +849,7 @@
vb2_fop_release(file);
- v4l2_pipeline_pm_use(&vdev->entity, 0);
+ v4l2_pipeline_pm_put(&vdev->entity);
file->private_data = NULL;
@@ -879,7 +957,7 @@
if (ret < 0) {
dev_err(v4l2_dev->dev, "Failed to init video entity: %d\n",
ret);
- goto error_media_init;
+ goto error_vb2_init;
}
mutex_init(&video->lock);
@@ -912,8 +990,8 @@
}
vdev->fops = &msm_vid_fops;
- vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_STREAMING |
- V4L2_CAP_READWRITE;
+ vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_STREAMING
+ | V4L2_CAP_READWRITE | V4L2_CAP_IO_MC;
vdev->ioctl_ops = &msm_vid_ioctl_ops;
vdev->release = msm_video_release;
vdev->v4l2_dev = v4l2_dev;
@@ -922,7 +1000,7 @@
vdev->lock = &video->lock;
strscpy(vdev->name, name, sizeof(vdev->name));
- ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
+ ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
if (ret < 0) {
dev_err(v4l2_dev->dev, "Failed to register video device: %d\n",
ret);
@@ -937,23 +1015,15 @@
error_video_register:
media_entity_cleanup(&vdev->entity);
mutex_destroy(&video->lock);
-error_media_init:
- vb2_queue_release(&video->vb2_q);
error_vb2_init:
mutex_destroy(&video->q_lock);
return ret;
}
-void msm_video_stop_streaming(struct camss_video *video)
-{
- if (vb2_is_streaming(&video->vb2_q))
- vb2_queue_release(&video->vb2_q);
-}
-
void msm_video_unregister(struct camss_video *video)
{
atomic_inc(&video->camss->ref_count);
- video_unregister_device(&video->vdev);
+ vb2_video_unregister_device(&video->vdev);
atomic_dec(&video->camss->ref_count);
}
diff --git a/drivers/media/platform/qcom/camss/camss-video.h b/drivers/media/platform/qcom/camss/camss-video.h
index aa35e8c..bdbae84 100644
--- a/drivers/media/platform/qcom/camss/camss-video.h
+++ b/drivers/media/platform/qcom/camss/camss-video.h
@@ -52,8 +52,6 @@
unsigned int nformats;
};
-void msm_video_stop_streaming(struct camss_video *video);
-
int msm_video_register(struct camss_video *video, struct v4l2_device *v4l2_dev,
const char *name, int is_pix);
diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c
index 2483641..9186881 100644
--- a/drivers/media/platform/qcom/camss/camss.c
+++ b/drivers/media/platform/qcom/camss/camss.c
@@ -974,13 +974,8 @@
*/
static int camss_remove(struct platform_device *pdev)
{
- unsigned int i;
-
struct camss *camss = platform_get_drvdata(pdev);
- for (i = 0; i < camss->vfe_num; i++)
- msm_vfe_stop_streaming(&camss->vfe[i]);
-
v4l2_async_notifier_unregister(&camss->notifier);
v4l2_async_notifier_cleanup(&camss->notifier);
camss_unregister_entities(camss);
diff --git a/drivers/media/platform/qcom/venus/Makefile b/drivers/media/platform/qcom/venus/Makefile
index b44b11b..dfc6368 100644
--- a/drivers/media/platform/qcom/venus/Makefile
+++ b/drivers/media/platform/qcom/venus/Makefile
@@ -3,7 +3,7 @@
venus-core-objs += core.o helpers.o firmware.o \
hfi_venus.o hfi_msgs.o hfi_cmds.o hfi.o \
- hfi_parser.o
+ hfi_parser.o pm_helpers.o dbgfs.o
venus-dec-objs += vdec.o vdec_ctrls.o
venus-enc-objs += venc.o venc_ctrls.o
diff --git a/drivers/media/platform/qcom/venus/core.c b/drivers/media/platform/qcom/venus/core.c
index bbc430a..62d11c6 100644
--- a/drivers/media/platform/qcom/venus/core.c
+++ b/drivers/media/platform/qcom/venus/core.c
@@ -3,9 +3,10 @@
* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
* Copyright (C) 2017 Linaro Ltd.
*/
-#include <linux/clk.h>
#include <linux/init.h>
+#include <linux/interconnect.h>
#include <linux/ioctl.h>
+#include <linux/delay.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/of_device.h>
@@ -18,9 +19,8 @@
#include <media/v4l2-ioctl.h>
#include "core.h"
-#include "vdec.h"
-#include "venc.h"
#include "firmware.h"
+#include "pm_helpers.h"
static void venus_event_notify(struct venus_core *core, u32 event)
{
@@ -41,108 +41,98 @@
mutex_unlock(&core->lock);
disable_irq_nosync(core->irq);
-
- /*
- * Delay recovery to ensure venus has completed any pending cache
- * operations. Without this sleep, we see device reset when firmware is
- * unloaded after a system error.
- */
- schedule_delayed_work(&core->work, msecs_to_jiffies(100));
+ schedule_delayed_work(&core->work, msecs_to_jiffies(10));
}
static const struct hfi_core_ops venus_core_ops = {
.event_notify = venus_event_notify,
};
+#define RPM_WAIT_FOR_IDLE_MAX_ATTEMPTS 10
+
static void venus_sys_error_handler(struct work_struct *work)
{
struct venus_core *core =
container_of(work, struct venus_core, work.work);
- int ret = 0;
+ int ret, i, max_attempts = RPM_WAIT_FOR_IDLE_MAX_ATTEMPTS;
+ const char *err_msg = "";
+ bool failed = false;
- dev_warn(core->dev, "system error has occurred, starting recovery!\n");
-
- pm_runtime_get_sync(core->dev);
+ ret = pm_runtime_get_sync(core->dev);
+ if (ret < 0) {
+ err_msg = "resume runtime PM";
+ max_attempts = 0;
+ failed = true;
+ }
hfi_core_deinit(core, true);
- hfi_destroy(core);
+
mutex_lock(&core->lock);
+
+ for (i = 0; i < max_attempts; i++) {
+ if (!pm_runtime_active(core->dev_dec) && !pm_runtime_active(core->dev_enc))
+ break;
+ msleep(10);
+ }
+
venus_shutdown(core);
pm_runtime_put_sync(core->dev);
- ret |= hfi_create(core, &venus_core_ops);
+ for (i = 0; i < max_attempts; i++) {
+ if (!core->pmdomains[0] || !pm_runtime_active(core->pmdomains[0]))
+ break;
+ usleep_range(1000, 1500);
+ }
- pm_runtime_get_sync(core->dev);
+ hfi_reinit(core);
- ret |= venus_boot(core);
+ ret = pm_runtime_get_sync(core->dev);
+ if (ret < 0) {
+ err_msg = "resume runtime PM";
+ failed = true;
+ }
- ret |= hfi_core_resume(core, true);
+ ret = venus_boot(core);
+ if (ret && !failed) {
+ err_msg = "boot Venus";
+ failed = true;
+ }
+
+ ret = hfi_core_resume(core, true);
+ if (ret && !failed) {
+ err_msg = "resume HFI";
+ failed = true;
+ }
enable_irq(core->irq);
mutex_unlock(&core->lock);
- ret |= hfi_core_init(core);
+ ret = hfi_core_init(core);
+ if (ret && !failed) {
+ err_msg = "init HFI";
+ failed = true;
+ }
pm_runtime_put_sync(core->dev);
- if (ret) {
+ if (failed) {
disable_irq_nosync(core->irq);
- dev_warn(core->dev, "recovery failed (%d)\n", ret);
+ dev_warn_ratelimited(core->dev,
+ "System error has occurred, recovery failed to %s\n",
+ err_msg);
schedule_delayed_work(&core->work, msecs_to_jiffies(10));
return;
}
+ dev_warn(core->dev, "system error has occurred (recovered)\n");
+
mutex_lock(&core->lock);
core->sys_error = false;
mutex_unlock(&core->lock);
}
-static int venus_clks_get(struct venus_core *core)
-{
- const struct venus_resources *res = core->res;
- struct device *dev = core->dev;
- unsigned int i;
-
- for (i = 0; i < res->clks_num; i++) {
- core->clks[i] = devm_clk_get(dev, res->clks[i]);
- if (IS_ERR(core->clks[i]))
- return PTR_ERR(core->clks[i]);
- }
-
- return 0;
-}
-
-static int venus_clks_enable(struct venus_core *core)
-{
- const struct venus_resources *res = core->res;
- unsigned int i;
- int ret;
-
- for (i = 0; i < res->clks_num; i++) {
- ret = clk_prepare_enable(core->clks[i]);
- if (ret)
- goto err;
- }
-
- return 0;
-err:
- while (i--)
- clk_disable_unprepare(core->clks[i]);
-
- return ret;
-}
-
-static void venus_clks_disable(struct venus_core *core)
-{
- const struct venus_resources *res = core->res;
- unsigned int i = res->clks_num;
-
- while (i--)
- clk_disable_unprepare(core->clks[i]);
-}
-
static u32 to_v4l2_codec_type(u32 codec)
{
switch (codec) {
@@ -232,13 +222,20 @@
return -ENOMEM;
core->dev = dev;
- platform_set_drvdata(pdev, core);
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
core->base = devm_ioremap_resource(dev, r);
if (IS_ERR(core->base))
return PTR_ERR(core->base);
+ core->video_path = devm_of_icc_get(dev, "video-mem");
+ if (IS_ERR(core->video_path))
+ return PTR_ERR(core->video_path);
+
+ core->cpucfg_path = devm_of_icc_get(dev, "cpu-cfg");
+ if (IS_ERR(core->cpucfg_path))
+ return PTR_ERR(core->cpucfg_path);
+
core->irq = platform_get_irq(pdev, 0);
if (core->irq < 0)
return core->irq;
@@ -247,21 +244,23 @@
if (!core->res)
return -ENODEV;
- ret = venus_clks_get(core);
- if (ret)
- return ret;
+ mutex_init(&core->pm_lock);
+
+ core->pm_ops = venus_pm_get(core->res->hfi_version);
+ if (!core->pm_ops)
+ return -ENODEV;
+
+ if (core->pm_ops->core_get) {
+ ret = core->pm_ops->core_get(core);
+ if (ret)
+ return ret;
+ }
ret = dma_set_mask_and_coherent(dev, core->res->dma_mask);
if (ret)
- return ret;
+ goto err_core_put;
- if (!dev->dma_parms) {
- dev->dma_parms = devm_kzalloc(dev, sizeof(*dev->dma_parms),
- GFP_KERNEL);
- if (!dev->dma_parms)
- return -ENOMEM;
- }
- dma_set_max_seg_size(dev, DMA_BIT_MASK(32));
+ dma_set_max_seg_size(dev, UINT_MAX);
INIT_LIST_HEAD(&core->instances);
mutex_init(&core->lock);
@@ -271,11 +270,17 @@
IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
"venus", core);
if (ret)
- return ret;
+ goto err_core_put;
ret = hfi_create(core, &venus_core_ops);
if (ret)
- return ret;
+ goto err_core_put;
+
+ ret = v4l2_device_register(dev, &core->v4l2_dev);
+ if (ret)
+ goto err_core_deinit;
+
+ platform_set_drvdata(pdev, core);
pm_runtime_enable(dev);
@@ -289,11 +294,11 @@
ret = venus_firmware_init(core);
if (ret)
- goto err_runtime_disable;
+ goto err_of_depopulate;
ret = venus_boot(core);
if (ret)
- goto err_runtime_disable;
+ goto err_firmware_deinit;
ret = hfi_core_resume(core, true);
if (ret)
@@ -311,35 +316,41 @@
if (ret)
goto err_venus_shutdown;
- ret = v4l2_device_register(dev, &core->v4l2_dev);
- if (ret)
- goto err_core_deinit;
-
ret = pm_runtime_put_sync(dev);
if (ret) {
pm_runtime_get_noresume(dev);
goto err_dev_unregister;
}
+ venus_dbgfs_init(core);
+
return 0;
err_dev_unregister:
v4l2_device_unregister(&core->v4l2_dev);
-err_core_deinit:
- hfi_core_deinit(core, false);
err_venus_shutdown:
venus_shutdown(core);
+err_firmware_deinit:
+ venus_firmware_deinit(core);
+err_of_depopulate:
+ of_platform_depopulate(dev);
err_runtime_disable:
pm_runtime_put_noidle(dev);
pm_runtime_set_suspended(dev);
pm_runtime_disable(dev);
hfi_destroy(core);
+err_core_deinit:
+ hfi_core_deinit(core, false);
+err_core_put:
+ if (core->pm_ops->core_put)
+ core->pm_ops->core_put(core);
return ret;
}
static int venus_remove(struct platform_device *pdev)
{
struct venus_core *core = platform_get_drvdata(pdev);
+ const struct venus_pm_ops *pm_ops = core->pm_ops;
struct device *dev = core->dev;
int ret;
@@ -349,7 +360,6 @@
ret = hfi_core_deinit(core, true);
WARN_ON(ret);
- hfi_destroy(core);
venus_shutdown(core);
of_platform_depopulate(dev);
@@ -358,19 +368,51 @@
pm_runtime_put_sync(dev);
pm_runtime_disable(dev);
+ if (pm_ops->core_put)
+ pm_ops->core_put(core);
+
v4l2_device_unregister(&core->v4l2_dev);
+ hfi_destroy(core);
+
+ mutex_destroy(&core->pm_lock);
+ mutex_destroy(&core->lock);
+ venus_dbgfs_deinit(core);
+
return ret;
}
static __maybe_unused int venus_runtime_suspend(struct device *dev)
{
struct venus_core *core = dev_get_drvdata(dev);
+ const struct venus_pm_ops *pm_ops = core->pm_ops;
int ret;
ret = hfi_core_suspend(core);
+ if (ret)
+ return ret;
- venus_clks_disable(core);
+ if (pm_ops->core_power) {
+ ret = pm_ops->core_power(core, POWER_OFF);
+ if (ret)
+ return ret;
+ }
+
+ ret = icc_set_bw(core->cpucfg_path, 0, 0);
+ if (ret)
+ goto err_cpucfg_path;
+
+ ret = icc_set_bw(core->video_path, 0, 0);
+ if (ret)
+ goto err_video_path;
+
+ return ret;
+
+err_video_path:
+ icc_set_bw(core->cpucfg_path, kbps_to_icc(1000), 0);
+err_cpucfg_path:
+ if (pm_ops->core_power)
+ pm_ops->core_power(core, POWER_ON);
return ret;
}
@@ -378,21 +420,24 @@
static __maybe_unused int venus_runtime_resume(struct device *dev)
{
struct venus_core *core = dev_get_drvdata(dev);
+ const struct venus_pm_ops *pm_ops = core->pm_ops;
int ret;
- ret = venus_clks_enable(core);
+ ret = icc_set_bw(core->video_path, kbps_to_icc(20000), 0);
if (ret)
return ret;
- ret = hfi_core_resume(core, false);
+ ret = icc_set_bw(core->cpucfg_path, kbps_to_icc(1000), 0);
if (ret)
- goto err_clks_disable;
+ return ret;
- return 0;
+ if (pm_ops->core_power) {
+ ret = pm_ops->core_power(core, POWER_ON);
+ if (ret)
+ return ret;
+ }
-err_clks_disable:
- venus_clks_disable(core);
- return ret;
+ return hfi_core_resume(core, false);
}
static const struct dev_pm_ops venus_pm_ops = {
@@ -450,6 +495,9 @@
.reg_tbl_size = ARRAY_SIZE(msm8996_reg_preset),
.clks = {"core", "iface", "bus", "mbus" },
.clks_num = 4,
+ .vcodec0_clks = { "core" },
+ .vcodec1_clks = { "core" },
+ .vcodec_clks_num = 1,
.max_load = 2563200,
.hfi_version = HFI_VERSION_3XX,
.vmem_id = VIDC_RESOURCE_NONE,
@@ -468,11 +516,45 @@
{ 244800, 100000000 }, /* 1920x1080@30 */
};
+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 },
+ { V4L2_PIX_FMT_MPEG2, VIDC_SESSION_TYPE_DEC, 200, 10 },
+ { V4L2_PIX_FMT_H264, VIDC_SESSION_TYPE_DEC, 200, 10 },
+ { V4L2_PIX_FMT_HEVC, VIDC_SESSION_TYPE_DEC, 200, 10 },
+ { V4L2_PIX_FMT_VP8, VIDC_SESSION_TYPE_DEC, 200, 10 },
+ { V4L2_PIX_FMT_VP9, VIDC_SESSION_TYPE_DEC, 200, 10 },
+};
+
+static const struct bw_tbl sdm845_bw_table_enc[] = {
+ { 1944000, 1612000, 0, 2416000, 0 }, /* 3840x2160@60 */
+ { 972000, 951000, 0, 1434000, 0 }, /* 3840x2160@30 */
+ { 489600, 723000, 0, 973000, 0 }, /* 1920x1080@60 */
+ { 244800, 370000, 0, 495000, 0 }, /* 1920x1080@30 */
+};
+
+static const struct bw_tbl sdm845_bw_table_dec[] = {
+ { 2073600, 3929000, 0, 5551000, 0 }, /* 4096x2160@60 */
+ { 1036800, 1987000, 0, 2797000, 0 }, /* 4096x2160@30 */
+ { 489600, 1040000, 0, 1298000, 0 }, /* 1920x1080@60 */
+ { 244800, 530000, 0, 659000, 0 }, /* 1920x1080@30 */
+};
+
static const struct venus_resources sdm845_res = {
.freq_tbl = sdm845_freq_table,
.freq_tbl_size = ARRAY_SIZE(sdm845_freq_table),
+ .bw_tbl_enc = sdm845_bw_table_enc,
+ .bw_tbl_enc_size = ARRAY_SIZE(sdm845_bw_table_enc),
+ .bw_tbl_dec = sdm845_bw_table_dec,
+ .bw_tbl_dec_size = ARRAY_SIZE(sdm845_bw_table_dec),
+ .codec_freq_data = sdm845_codec_freq_data,
+ .codec_freq_data_size = ARRAY_SIZE(sdm845_codec_freq_data),
.clks = {"core", "iface", "bus" },
.clks_num = 3,
+ .vcodec0_clks = { "core", "bus" },
+ .vcodec1_clks = { "core", "bus" },
+ .vcodec_clks_num = 2,
.max_load = 3110400, /* 4096x2160@90 */
.hfi_version = HFI_VERSION_4XX,
.vmem_id = VIDC_RESOURCE_NONE,
@@ -482,10 +564,88 @@
.fwname = "qcom/venus-5.2/venus.mdt",
};
+static const struct venus_resources sdm845_res_v2 = {
+ .freq_tbl = sdm845_freq_table,
+ .freq_tbl_size = ARRAY_SIZE(sdm845_freq_table),
+ .bw_tbl_enc = sdm845_bw_table_enc,
+ .bw_tbl_enc_size = ARRAY_SIZE(sdm845_bw_table_enc),
+ .bw_tbl_dec = sdm845_bw_table_dec,
+ .bw_tbl_dec_size = ARRAY_SIZE(sdm845_bw_table_dec),
+ .codec_freq_data = sdm845_codec_freq_data,
+ .codec_freq_data_size = ARRAY_SIZE(sdm845_codec_freq_data),
+ .clks = {"core", "iface", "bus" },
+ .clks_num = 3,
+ .vcodec0_clks = { "vcodec0_core", "vcodec0_bus" },
+ .vcodec1_clks = { "vcodec1_core", "vcodec1_bus" },
+ .vcodec_clks_num = 2,
+ .vcodec_pmdomains = { "venus", "vcodec0", "vcodec1" },
+ .vcodec_pmdomains_num = 3,
+ .opp_pmdomain = (const char *[]) { "cx", NULL },
+ .vcodec_num = 2,
+ .max_load = 3110400, /* 4096x2160@90 */
+ .hfi_version = HFI_VERSION_4XX,
+ .vmem_id = VIDC_RESOURCE_NONE,
+ .vmem_size = 0,
+ .vmem_addr = 0,
+ .dma_mask = 0xe0000000 - 1,
+ .cp_start = 0,
+ .cp_size = 0x70800000,
+ .cp_nonpixel_start = 0x1000000,
+ .cp_nonpixel_size = 0x24800000,
+ .fwname = "qcom/venus-5.2/venus.mdt",
+};
+
+static const struct freq_tbl sc7180_freq_table[] = {
+ { 0, 500000000 },
+ { 0, 434000000 },
+ { 0, 340000000 },
+ { 0, 270000000 },
+ { 0, 150000000 },
+};
+
+static const struct bw_tbl sc7180_bw_table_enc[] = {
+ { 972000, 750000, 0, 0, 0 }, /* 3840x2160@30 */
+ { 489600, 451000, 0, 0, 0 }, /* 1920x1080@60 */
+ { 244800, 234000, 0, 0, 0 }, /* 1920x1080@30 */
+};
+
+static const struct bw_tbl sc7180_bw_table_dec[] = {
+ { 1036800, 1386000, 0, 1875000, 0 }, /* 4096x2160@30 */
+ { 489600, 865000, 0, 1146000, 0 }, /* 1920x1080@60 */
+ { 244800, 530000, 0, 583000, 0 }, /* 1920x1080@30 */
+};
+
+static const struct venus_resources sc7180_res = {
+ .freq_tbl = sc7180_freq_table,
+ .freq_tbl_size = ARRAY_SIZE(sc7180_freq_table),
+ .bw_tbl_enc = sc7180_bw_table_enc,
+ .bw_tbl_enc_size = ARRAY_SIZE(sc7180_bw_table_enc),
+ .bw_tbl_dec = sc7180_bw_table_dec,
+ .bw_tbl_dec_size = ARRAY_SIZE(sc7180_bw_table_dec),
+ .codec_freq_data = sdm845_codec_freq_data,
+ .codec_freq_data_size = ARRAY_SIZE(sdm845_codec_freq_data),
+ .clks = {"core", "iface", "bus" },
+ .clks_num = 3,
+ .vcodec0_clks = { "vcodec0_core", "vcodec0_bus" },
+ .vcodec_clks_num = 2,
+ .vcodec_pmdomains = { "venus", "vcodec0" },
+ .vcodec_pmdomains_num = 2,
+ .opp_pmdomain = (const char *[]) { "cx", NULL },
+ .vcodec_num = 1,
+ .hfi_version = HFI_VERSION_4XX,
+ .vmem_id = VIDC_RESOURCE_NONE,
+ .vmem_size = 0,
+ .vmem_addr = 0,
+ .dma_mask = 0xe0000000 - 1,
+ .fwname = "qcom/venus-5.4/venus.mdt",
+};
+
static const struct of_device_id venus_dt_match[] = {
{ .compatible = "qcom,msm8916-venus", .data = &msm8916_res, },
{ .compatible = "qcom,msm8996-venus", .data = &msm8996_res, },
{ .compatible = "qcom,sdm845-venus", .data = &sdm845_res, },
+ { .compatible = "qcom,sdm845-venus-v2", .data = &sdm845_res_v2, },
+ { .compatible = "qcom,sc7180-venus", .data = &sc7180_res, },
{ }
};
MODULE_DEVICE_TABLE(of, venus_dt_match);
diff --git a/drivers/media/platform/qcom/venus/core.h b/drivers/media/platform/qcom/venus/core.h
index 922cb7e..f2a0ef9 100644
--- a/drivers/media/platform/qcom/venus/core.h
+++ b/drivers/media/platform/qcom/venus/core.h
@@ -12,9 +12,19 @@
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
+#include "dbgfs.h"
#include "hfi.h"
-#define VIDC_CLKS_NUM_MAX 4
+#define VDBGL "VenusLow : "
+#define VDBGM "VenusMed : "
+#define VDBGH "VenusHigh: "
+#define VDBGFW "VenusFW : "
+
+#define VIDC_CLKS_NUM_MAX 4
+#define VIDC_VCODEC_CLKS_NUM_MAX 2
+#define VIDC_PMDOMAINS_NUM_MAX 3
+
+extern int venus_fw_debug;
struct freq_tbl {
unsigned int load;
@@ -26,19 +36,51 @@
u32 value;
};
+struct codec_freq_data {
+ u32 pixfmt;
+ u32 session_type;
+ unsigned long vpp_freq;
+ unsigned long vsp_freq;
+};
+
+struct bw_tbl {
+ u32 mbs_per_sec;
+ u32 avg;
+ u32 peak;
+ u32 avg_10bit;
+ u32 peak_10bit;
+};
+
struct venus_resources {
u64 dma_mask;
const struct freq_tbl *freq_tbl;
unsigned int freq_tbl_size;
+ const struct bw_tbl *bw_tbl_enc;
+ unsigned int bw_tbl_enc_size;
+ const struct bw_tbl *bw_tbl_dec;
+ unsigned int bw_tbl_dec_size;
const struct reg_val *reg_tbl;
unsigned int reg_tbl_size;
+ const struct codec_freq_data *codec_freq_data;
+ unsigned int codec_freq_data_size;
const char * const clks[VIDC_CLKS_NUM_MAX];
unsigned int clks_num;
+ const char * const vcodec0_clks[VIDC_VCODEC_CLKS_NUM_MAX];
+ const char * const vcodec1_clks[VIDC_VCODEC_CLKS_NUM_MAX];
+ unsigned int vcodec_clks_num;
+ const char * const vcodec_pmdomains[VIDC_PMDOMAINS_NUM_MAX];
+ unsigned int vcodec_pmdomains_num;
+ const char **opp_pmdomain;
+ unsigned int vcodec_num;
enum hfi_version hfi_version;
u32 max_load;
unsigned int vmem_id;
u32 vmem_size;
u32 vmem_addr;
+ u32 cp_start;
+ u32 cp_size;
+ u32 cp_nonpixel_start;
+ u32 cp_nonpixel_size;
const char *fwname;
};
@@ -79,10 +121,9 @@
* @base: IO memory base address
* @irq: Venus irq
* @clks: an array of struct clk pointers
- * @core0_clk: a struct clk pointer for core0
- * @core1_clk: a struct clk pointer for core1
- * @core0_bus_clk: a struct clk pointer for core0 bus clock
- * @core1_bus_clk: a struct clk pointer for core1 bus clock
+ * @vcodec0_clks: an array of vcodec0 struct clk pointers
+ * @vcodec1_clks: an array of vcodec1 struct clk pointers
+ * @pmdomains: an array of pmdomains struct device pointers
* @vdev_dec: a reference to video device structure for decoder instances
* @vdev_enc: a reference to video device structure for encoder instances
* @v4l2_dev: a holder for v4l2 device structure
@@ -99,6 +140,7 @@
* @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
@@ -106,15 +148,21 @@
* @priv: a private filed for HFI operations
* @ops: the core HFI operations
* @work: a delayed work for handling system fatal error
+ * @root: debugfs root directory
*/
struct venus_core {
void __iomem *base;
int irq;
struct clk *clks[VIDC_CLKS_NUM_MAX];
- struct clk *core0_clk;
- struct clk *core1_clk;
- struct clk *core0_bus_clk;
- struct clk *core1_bus_clk;
+ struct clk *vcodec0_clks[VIDC_VCODEC_CLKS_NUM_MAX];
+ struct clk *vcodec1_clks[VIDC_VCODEC_CLKS_NUM_MAX];
+ struct icc_path *video_path;
+ struct icc_path *cpucfg_path;
+ struct opp_table *opp_table;
+ bool has_opp_table;
+ struct device *pmdomains[VIDC_PMDOMAINS_NUM_MAX];
+ struct device_link *opp_dl_venus;
+ struct device *opp_pmdomain;
struct video_device *vdev_dec;
struct video_device *vdev_enc;
struct v4l2_device v4l2_dev;
@@ -136,6 +184,8 @@
unsigned int error;
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;
@@ -149,6 +199,9 @@
struct delayed_work work;
struct venus_caps caps[MAX_CODEC_NUM];
unsigned int codecs_count;
+ unsigned int core0_usage_count;
+ unsigned int core1_usage_count;
+ struct dentry *root;
};
struct vdec_controls {
@@ -164,6 +217,9 @@
u32 bitrate_mode;
u32 bitrate;
u32 bitrate_peak;
+ u32 rc_enable;
+ u32 const_quality;
+ u32 frame_skip_mode;
u32 h264_i_period;
u32 h264_entropy_mode;
@@ -186,15 +242,17 @@
u32 header_mode;
struct {
- u32 mpeg4;
u32 h264;
- u32 vpx;
+ u32 mpeg4;
u32 hevc;
+ u32 vp8;
+ u32 vp9;
} profile;
struct {
- u32 mpeg4;
u32 h264;
+ u32 mpeg4;
u32 hevc;
+ u32 vp9;
} level;
};
@@ -208,6 +266,12 @@
struct list_head ref_list;
};
+struct clock_data {
+ u32 core_id;
+ unsigned long freq;
+ const struct codec_freq_data *codec_freq_data;
+};
+
#define to_venus_buffer(ptr) container_of(ptr, struct venus_buffer, vb)
enum venus_dec_state {
@@ -218,7 +282,8 @@
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 {
@@ -283,11 +348,13 @@
* @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;
struct mutex lock;
struct venus_core *core;
+ struct clock_data clk_data;
struct list_head dpbbufs;
struct list_head internalbufs;
struct list_head registeredbufs;
@@ -314,6 +381,7 @@
unsigned int subscriptions;
int buf_count;
struct venus_ts_metadata tss[VIDEO_MAX_FRAME];
+ unsigned long payloads[VIDEO_MAX_FRAME];
u64 fps;
struct v4l2_fract timeperframe;
const struct venus_format *fmt_out;
@@ -340,6 +408,9 @@
const struct hfi_inst_ops *ops;
u32 session_type;
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/dbgfs.c b/drivers/media/platform/qcom/venus/dbgfs.c
new file mode 100644
index 0000000..52de47f
--- /dev/null
+++ b/drivers/media/platform/qcom/venus/dbgfs.c
@@ -0,0 +1,19 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2020 Linaro Ltd.
+ */
+
+#include <linux/debugfs.h>
+
+#include "core.h"
+
+void venus_dbgfs_init(struct venus_core *core)
+{
+ core->root = debugfs_create_dir("venus", NULL);
+ debugfs_create_x32("fw_level", 0644, core->root, &venus_fw_debug);
+}
+
+void venus_dbgfs_deinit(struct venus_core *core)
+{
+ debugfs_remove_recursive(core->root);
+}
diff --git a/drivers/media/platform/qcom/venus/dbgfs.h b/drivers/media/platform/qcom/venus/dbgfs.h
new file mode 100644
index 0000000..b7b621a
--- /dev/null
+++ b/drivers/media/platform/qcom/venus/dbgfs.h
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (C) 2020 Linaro Ltd. */
+
+#ifndef __VENUS_DBGFS_H__
+#define __VENUS_DBGFS_H__
+
+struct venus_core;
+
+void venus_dbgfs_init(struct venus_core *core);
+void venus_dbgfs_deinit(struct venus_core *core);
+
+#endif
diff --git a/drivers/media/platform/qcom/venus/firmware.c b/drivers/media/platform/qcom/venus/firmware.c
index 33f70e1..1db64a8 100644
--- a/drivers/media/platform/qcom/venus/firmware.c
+++ b/drivers/media/platform/qcom/venus/firmware.c
@@ -106,8 +106,7 @@
mem_va = memremap(r.start, *mem_size, MEMREMAP_WC);
if (!mem_va) {
- dev_err(dev, "unable to map memory region: %pa+%zx\n",
- &r.start, *mem_size);
+ dev_err(dev, "unable to map memory region: %pR\n", &r);
ret = -ENOMEM;
goto err_release_fw;
}
@@ -182,6 +181,7 @@
int venus_boot(struct venus_core *core)
{
struct device *dev = core->dev;
+ const struct venus_resources *res = core->res;
phys_addr_t mem_phys;
size_t mem_size;
int ret;
@@ -201,7 +201,23 @@
else
ret = venus_boot_no_tz(core, mem_phys, mem_size);
- return ret;
+ if (ret)
+ return ret;
+
+ if (core->use_tz && res->cp_size) {
+ ret = qcom_scm_mem_protect_video_var(res->cp_start,
+ res->cp_size,
+ res->cp_nonpixel_start,
+ res->cp_nonpixel_size);
+ if (ret) {
+ qcom_scm_pas_shutdown(VENUS_PAS_ID);
+ dev_err(dev, "set virtual address ranges fail (%d)\n",
+ ret);
+ return ret;
+ }
+ }
+
+ return 0;
}
int venus_shutdown(struct venus_core *core)
diff --git a/drivers/media/platform/qcom/venus/helpers.c b/drivers/media/platform/qcom/venus/helpers.c
index 1ad96c2..50439eb 100644
--- a/drivers/media/platform/qcom/venus/helpers.c
+++ b/drivers/media/platform/qcom/venus/helpers.c
@@ -3,12 +3,10 @@
* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
* Copyright (C) 2017 Linaro Ltd.
*/
-#include <linux/clk.h>
-#include <linux/iopoll.h>
#include <linux/list.h>
#include <linux/mutex.h>
-#include <linux/pm_runtime.h>
#include <linux/slab.h>
+#include <linux/kernel.h>
#include <media/videobuf2-dma-sg.h>
#include <media/v4l2-mem2mem.h>
#include <asm/div64.h>
@@ -16,7 +14,7 @@
#include "core.h"
#include "helpers.h"
#include "hfi_helper.h"
-#include "hfi_venus_io.h"
+#include "pm_helpers.h"
struct intbuf {
struct list_head list;
@@ -359,86 +357,6 @@
}
EXPORT_SYMBOL_GPL(venus_helper_intbufs_realloc);
-static u32 load_per_instance(struct venus_inst *inst)
-{
- u32 mbs;
-
- if (!inst || !(inst->state >= INST_INIT && inst->state < INST_STOP))
- return 0;
-
- mbs = (ALIGN(inst->width, 16) / 16) * (ALIGN(inst->height, 16) / 16);
-
- return mbs * inst->fps;
-}
-
-static u32 load_per_type(struct venus_core *core, u32 session_type)
-{
- struct venus_inst *inst = NULL;
- u32 mbs_per_sec = 0;
-
- mutex_lock(&core->lock);
- list_for_each_entry(inst, &core->instances, list) {
- if (inst->session_type != session_type)
- continue;
-
- mbs_per_sec += load_per_instance(inst);
- }
- mutex_unlock(&core->lock);
-
- return mbs_per_sec;
-}
-
-int venus_helper_load_scale_clocks(struct venus_core *core)
-{
- const struct freq_tbl *table = core->res->freq_tbl;
- unsigned int num_rows = core->res->freq_tbl_size;
- unsigned long freq = table[0].freq;
- struct clk *clk = core->clks[0];
- struct device *dev = core->dev;
- u32 mbs_per_sec;
- unsigned int i;
- int ret;
-
- mbs_per_sec = load_per_type(core, VIDC_SESSION_TYPE_ENC) +
- load_per_type(core, VIDC_SESSION_TYPE_DEC);
-
- if (mbs_per_sec > core->res->max_load)
- dev_warn(dev, "HW is overloaded, needed: %d max: %d\n",
- mbs_per_sec, core->res->max_load);
-
- if (!mbs_per_sec && num_rows > 1) {
- freq = table[num_rows - 1].freq;
- goto set_freq;
- }
-
- for (i = 0; i < num_rows; i++) {
- if (mbs_per_sec > table[i].load)
- break;
- freq = table[i].freq;
- }
-
-set_freq:
-
- ret = clk_set_rate(clk, freq);
- if (ret)
- goto err;
-
- ret = clk_set_rate(core->core0_clk, freq);
- if (ret)
- goto err;
-
- ret = clk_set_rate(core->core1_clk, freq);
- if (ret)
- goto err;
-
- return 0;
-
-err:
- dev_err(dev, "failed to set clock rate %lu (%d)\n", freq, ret);
- return ret;
-}
-EXPORT_SYMBOL_GPL(venus_helper_load_scale_clocks);
-
static void fill_buffer_desc(const struct venus_buffer *buf,
struct hfi_buffer_desc *bd, bool response)
{
@@ -479,7 +397,7 @@
}
if (slot == -1) {
- dev_dbg(inst->core->dev, "%s: no free slot\n", __func__);
+ dev_dbg(inst->core->dev, VDBGL "no free slot\n");
return;
}
@@ -541,6 +459,8 @@
if (inst->session_type == VIDC_SESSION_TYPE_DEC)
put_ts_metadata(inst, vbuf);
+
+ venus_pm_load_scale(inst);
} else if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
if (inst->session_type == VIDC_SESSION_TYPE_ENC)
fdata.buffer_type = HFI_BUFFER_OUTPUT;
@@ -663,6 +583,244 @@
}
EXPORT_SYMBOL_GPL(venus_helper_get_bufreq);
+struct id_mapping {
+ u32 hfi_id;
+ u32 v4l2_id;
+};
+
+static const struct id_mapping mpeg4_profiles[] = {
+ { HFI_MPEG4_PROFILE_SIMPLE, V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE },
+ { HFI_MPEG4_PROFILE_ADVANCEDSIMPLE, V4L2_MPEG_VIDEO_MPEG4_PROFILE_ADVANCED_SIMPLE },
+};
+
+static const struct id_mapping mpeg4_levels[] = {
+ { HFI_MPEG4_LEVEL_0, V4L2_MPEG_VIDEO_MPEG4_LEVEL_0 },
+ { HFI_MPEG4_LEVEL_0b, V4L2_MPEG_VIDEO_MPEG4_LEVEL_0B },
+ { HFI_MPEG4_LEVEL_1, V4L2_MPEG_VIDEO_MPEG4_LEVEL_1 },
+ { HFI_MPEG4_LEVEL_2, V4L2_MPEG_VIDEO_MPEG4_LEVEL_2 },
+ { HFI_MPEG4_LEVEL_3, V4L2_MPEG_VIDEO_MPEG4_LEVEL_3 },
+ { HFI_MPEG4_LEVEL_4, V4L2_MPEG_VIDEO_MPEG4_LEVEL_4 },
+ { HFI_MPEG4_LEVEL_5, V4L2_MPEG_VIDEO_MPEG4_LEVEL_5 },
+};
+
+static const struct id_mapping mpeg2_profiles[] = {
+ { HFI_MPEG2_PROFILE_SIMPLE, V4L2_MPEG_VIDEO_MPEG2_PROFILE_SIMPLE },
+ { HFI_MPEG2_PROFILE_MAIN, V4L2_MPEG_VIDEO_MPEG2_PROFILE_MAIN },
+ { HFI_MPEG2_PROFILE_SNR, V4L2_MPEG_VIDEO_MPEG2_PROFILE_SNR_SCALABLE },
+ { HFI_MPEG2_PROFILE_SPATIAL, V4L2_MPEG_VIDEO_MPEG2_PROFILE_SPATIALLY_SCALABLE },
+ { HFI_MPEG2_PROFILE_HIGH, V4L2_MPEG_VIDEO_MPEG2_PROFILE_HIGH },
+};
+
+static const struct id_mapping mpeg2_levels[] = {
+ { HFI_MPEG2_LEVEL_LL, V4L2_MPEG_VIDEO_MPEG2_LEVEL_LOW },
+ { HFI_MPEG2_LEVEL_ML, V4L2_MPEG_VIDEO_MPEG2_LEVEL_MAIN },
+ { HFI_MPEG2_LEVEL_H14, V4L2_MPEG_VIDEO_MPEG2_LEVEL_HIGH_1440 },
+ { HFI_MPEG2_LEVEL_HL, V4L2_MPEG_VIDEO_MPEG2_LEVEL_HIGH },
+};
+
+static const struct id_mapping h264_profiles[] = {
+ { HFI_H264_PROFILE_BASELINE, V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE },
+ { HFI_H264_PROFILE_MAIN, V4L2_MPEG_VIDEO_H264_PROFILE_MAIN },
+ { HFI_H264_PROFILE_HIGH, V4L2_MPEG_VIDEO_H264_PROFILE_HIGH },
+ { HFI_H264_PROFILE_STEREO_HIGH, V4L2_MPEG_VIDEO_H264_PROFILE_STEREO_HIGH },
+ { HFI_H264_PROFILE_MULTIVIEW_HIGH, V4L2_MPEG_VIDEO_H264_PROFILE_MULTIVIEW_HIGH },
+ { HFI_H264_PROFILE_CONSTRAINED_BASE, V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE },
+ { HFI_H264_PROFILE_CONSTRAINED_HIGH, V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_HIGH },
+};
+
+static const struct id_mapping h264_levels[] = {
+ { HFI_H264_LEVEL_1, V4L2_MPEG_VIDEO_H264_LEVEL_1_0 },
+ { HFI_H264_LEVEL_1b, V4L2_MPEG_VIDEO_H264_LEVEL_1B },
+ { HFI_H264_LEVEL_11, V4L2_MPEG_VIDEO_H264_LEVEL_1_1 },
+ { HFI_H264_LEVEL_12, V4L2_MPEG_VIDEO_H264_LEVEL_1_2 },
+ { HFI_H264_LEVEL_13, V4L2_MPEG_VIDEO_H264_LEVEL_1_3 },
+ { HFI_H264_LEVEL_2, V4L2_MPEG_VIDEO_H264_LEVEL_2_0 },
+ { HFI_H264_LEVEL_21, V4L2_MPEG_VIDEO_H264_LEVEL_2_1 },
+ { HFI_H264_LEVEL_22, V4L2_MPEG_VIDEO_H264_LEVEL_2_2 },
+ { HFI_H264_LEVEL_3, V4L2_MPEG_VIDEO_H264_LEVEL_3_0 },
+ { HFI_H264_LEVEL_31, V4L2_MPEG_VIDEO_H264_LEVEL_3_1 },
+ { HFI_H264_LEVEL_32, V4L2_MPEG_VIDEO_H264_LEVEL_3_2 },
+ { HFI_H264_LEVEL_4, V4L2_MPEG_VIDEO_H264_LEVEL_4_0 },
+ { HFI_H264_LEVEL_41, V4L2_MPEG_VIDEO_H264_LEVEL_4_1 },
+ { HFI_H264_LEVEL_42, V4L2_MPEG_VIDEO_H264_LEVEL_4_2 },
+ { HFI_H264_LEVEL_5, V4L2_MPEG_VIDEO_H264_LEVEL_5_0 },
+ { HFI_H264_LEVEL_51, V4L2_MPEG_VIDEO_H264_LEVEL_5_1 },
+ { HFI_H264_LEVEL_52, V4L2_MPEG_VIDEO_H264_LEVEL_5_1 },
+};
+
+static const struct id_mapping hevc_profiles[] = {
+ { HFI_HEVC_PROFILE_MAIN, V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN },
+ { HFI_HEVC_PROFILE_MAIN_STILL_PIC, V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE },
+ { HFI_HEVC_PROFILE_MAIN10, V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10 },
+};
+
+static const struct id_mapping hevc_levels[] = {
+ { HFI_HEVC_LEVEL_1, V4L2_MPEG_VIDEO_HEVC_LEVEL_1 },
+ { HFI_HEVC_LEVEL_2, V4L2_MPEG_VIDEO_HEVC_LEVEL_2 },
+ { HFI_HEVC_LEVEL_21, V4L2_MPEG_VIDEO_HEVC_LEVEL_2_1 },
+ { HFI_HEVC_LEVEL_3, V4L2_MPEG_VIDEO_HEVC_LEVEL_3 },
+ { HFI_HEVC_LEVEL_31, V4L2_MPEG_VIDEO_HEVC_LEVEL_3_1 },
+ { HFI_HEVC_LEVEL_4, V4L2_MPEG_VIDEO_HEVC_LEVEL_4 },
+ { HFI_HEVC_LEVEL_41, V4L2_MPEG_VIDEO_HEVC_LEVEL_4_1 },
+ { HFI_HEVC_LEVEL_5, V4L2_MPEG_VIDEO_HEVC_LEVEL_5 },
+ { HFI_HEVC_LEVEL_51, V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1 },
+ { HFI_HEVC_LEVEL_52, V4L2_MPEG_VIDEO_HEVC_LEVEL_5_2 },
+ { HFI_HEVC_LEVEL_6, V4L2_MPEG_VIDEO_HEVC_LEVEL_6 },
+ { HFI_HEVC_LEVEL_61, V4L2_MPEG_VIDEO_HEVC_LEVEL_6_1 },
+ { HFI_HEVC_LEVEL_62, V4L2_MPEG_VIDEO_HEVC_LEVEL_6_2 },
+};
+
+static const struct id_mapping vp8_profiles[] = {
+ { HFI_VPX_PROFILE_VERSION_0, V4L2_MPEG_VIDEO_VP8_PROFILE_0 },
+ { HFI_VPX_PROFILE_VERSION_1, V4L2_MPEG_VIDEO_VP8_PROFILE_1 },
+ { HFI_VPX_PROFILE_VERSION_2, V4L2_MPEG_VIDEO_VP8_PROFILE_2 },
+ { HFI_VPX_PROFILE_VERSION_3, V4L2_MPEG_VIDEO_VP8_PROFILE_3 },
+};
+
+static const struct id_mapping vp9_profiles[] = {
+ { HFI_VP9_PROFILE_P0, V4L2_MPEG_VIDEO_VP9_PROFILE_0 },
+ { HFI_VP9_PROFILE_P2_10B, V4L2_MPEG_VIDEO_VP9_PROFILE_2 },
+};
+
+static const struct id_mapping vp9_levels[] = {
+ { HFI_VP9_LEVEL_1, V4L2_MPEG_VIDEO_VP9_LEVEL_1_0 },
+ { HFI_VP9_LEVEL_11, V4L2_MPEG_VIDEO_VP9_LEVEL_1_1 },
+ { HFI_VP9_LEVEL_2, V4L2_MPEG_VIDEO_VP9_LEVEL_2_0},
+ { HFI_VP9_LEVEL_21, V4L2_MPEG_VIDEO_VP9_LEVEL_2_1 },
+ { HFI_VP9_LEVEL_3, V4L2_MPEG_VIDEO_VP9_LEVEL_3_0},
+ { HFI_VP9_LEVEL_31, V4L2_MPEG_VIDEO_VP9_LEVEL_3_1 },
+ { HFI_VP9_LEVEL_4, V4L2_MPEG_VIDEO_VP9_LEVEL_4_0 },
+ { HFI_VP9_LEVEL_41, V4L2_MPEG_VIDEO_VP9_LEVEL_4_1 },
+ { HFI_VP9_LEVEL_5, V4L2_MPEG_VIDEO_VP9_LEVEL_5_0 },
+ { HFI_VP9_LEVEL_51, V4L2_MPEG_VIDEO_VP9_LEVEL_5_1 },
+ { HFI_VP9_LEVEL_6, V4L2_MPEG_VIDEO_VP9_LEVEL_6_0 },
+ { HFI_VP9_LEVEL_61, V4L2_MPEG_VIDEO_VP9_LEVEL_6_1 },
+};
+
+static u32 find_v4l2_id(u32 hfi_id, const struct id_mapping *array, unsigned int array_sz)
+{
+ unsigned int i;
+
+ if (!array || !array_sz)
+ return 0;
+
+ for (i = 0; i < array_sz; i++)
+ if (hfi_id == array[i].hfi_id)
+ return array[i].v4l2_id;
+
+ return 0;
+}
+
+static u32 find_hfi_id(u32 v4l2_id, const struct id_mapping *array, unsigned int array_sz)
+{
+ unsigned int i;
+
+ if (!array || !array_sz)
+ return 0;
+
+ for (i = 0; i < array_sz; i++)
+ if (v4l2_id == array[i].v4l2_id)
+ return array[i].hfi_id;
+
+ return 0;
+}
+
+static void
+v4l2_id_profile_level(u32 hfi_codec, struct hfi_profile_level *pl, u32 *profile, u32 *level)
+{
+ u32 hfi_pf = pl->profile;
+ u32 hfi_lvl = pl->level;
+
+ switch (hfi_codec) {
+ case HFI_VIDEO_CODEC_H264:
+ *profile = find_v4l2_id(hfi_pf, h264_profiles, ARRAY_SIZE(h264_profiles));
+ *level = find_v4l2_id(hfi_lvl, h264_levels, ARRAY_SIZE(h264_levels));
+ break;
+ case HFI_VIDEO_CODEC_MPEG2:
+ *profile = find_v4l2_id(hfi_pf, mpeg2_profiles, ARRAY_SIZE(mpeg2_profiles));
+ *level = find_v4l2_id(hfi_lvl, mpeg2_levels, ARRAY_SIZE(mpeg2_levels));
+ break;
+ case HFI_VIDEO_CODEC_MPEG4:
+ *profile = find_v4l2_id(hfi_pf, mpeg4_profiles, ARRAY_SIZE(mpeg4_profiles));
+ *level = find_v4l2_id(hfi_lvl, mpeg4_levels, ARRAY_SIZE(mpeg4_levels));
+ break;
+ case HFI_VIDEO_CODEC_VP8:
+ *profile = find_v4l2_id(hfi_pf, vp8_profiles, ARRAY_SIZE(vp8_profiles));
+ *level = 0;
+ break;
+ case HFI_VIDEO_CODEC_VP9:
+ *profile = find_v4l2_id(hfi_pf, vp9_profiles, ARRAY_SIZE(vp9_profiles));
+ *level = find_v4l2_id(hfi_lvl, vp9_levels, ARRAY_SIZE(vp9_levels));
+ break;
+ case HFI_VIDEO_CODEC_HEVC:
+ *profile = find_v4l2_id(hfi_pf, hevc_profiles, ARRAY_SIZE(hevc_profiles));
+ *level = find_v4l2_id(hfi_lvl, hevc_levels, ARRAY_SIZE(hevc_levels));
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+hfi_id_profile_level(u32 hfi_codec, u32 v4l2_pf, u32 v4l2_lvl, struct hfi_profile_level *pl)
+{
+ switch (hfi_codec) {
+ case HFI_VIDEO_CODEC_H264:
+ pl->profile = find_hfi_id(v4l2_pf, h264_profiles, ARRAY_SIZE(h264_profiles));
+ pl->level = find_hfi_id(v4l2_lvl, h264_levels, ARRAY_SIZE(h264_levels));
+ break;
+ case HFI_VIDEO_CODEC_MPEG2:
+ pl->profile = find_hfi_id(v4l2_pf, mpeg2_profiles, ARRAY_SIZE(mpeg2_profiles));
+ pl->level = find_hfi_id(v4l2_lvl, mpeg2_levels, ARRAY_SIZE(mpeg2_levels));
+ break;
+ case HFI_VIDEO_CODEC_MPEG4:
+ pl->profile = find_hfi_id(v4l2_pf, mpeg4_profiles, ARRAY_SIZE(mpeg4_profiles));
+ pl->level = find_hfi_id(v4l2_lvl, mpeg4_levels, ARRAY_SIZE(mpeg4_levels));
+ break;
+ case HFI_VIDEO_CODEC_VP8:
+ pl->profile = find_hfi_id(v4l2_pf, vp8_profiles, ARRAY_SIZE(vp8_profiles));
+ pl->level = 0;
+ break;
+ case HFI_VIDEO_CODEC_VP9:
+ pl->profile = find_hfi_id(v4l2_pf, vp9_profiles, ARRAY_SIZE(vp9_profiles));
+ pl->level = find_hfi_id(v4l2_lvl, vp9_levels, ARRAY_SIZE(vp9_levels));
+ break;
+ case HFI_VIDEO_CODEC_HEVC:
+ pl->profile = find_hfi_id(v4l2_pf, hevc_profiles, ARRAY_SIZE(hevc_profiles));
+ pl->level = find_hfi_id(v4l2_lvl, hevc_levels, ARRAY_SIZE(hevc_levels));
+ break;
+ default:
+ break;
+ }
+}
+
+int venus_helper_get_profile_level(struct venus_inst *inst, u32 *profile, u32 *level)
+{
+ const u32 ptype = HFI_PROPERTY_PARAM_PROFILE_LEVEL_CURRENT;
+ union hfi_get_property hprop;
+ int ret;
+
+ ret = hfi_session_get_property(inst, ptype, &hprop);
+ if (ret)
+ return ret;
+
+ v4l2_id_profile_level(inst->hfi_codec, &hprop.profile_level, profile, level);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(venus_helper_get_profile_level);
+
+int venus_helper_set_profile_level(struct venus_inst *inst, u32 profile, u32 level)
+{
+ const u32 ptype = HFI_PROPERTY_PARAM_PROFILE_LEVEL_CURRENT;
+ struct hfi_profile_level pl;
+
+ hfi_id_profile_level(inst->hfi_codec, profile, level, &pl);
+
+ return hfi_session_set_property(inst, ptype, &pl);
+}
+EXPORT_SYMBOL_GPL(venus_helper_set_profile_level);
+
static u32 get_framesize_raw_nv12(u32 width, u32 height)
{
u32 y_stride, uv_stride, y_plane;
@@ -707,6 +865,78 @@
max(extradata, y_stride * 48), SZ_4K);
}
+static u32 get_framesize_raw_p010(u32 width, u32 height)
+{
+ u32 y_plane, uv_plane, y_stride, uv_stride, y_sclines, uv_sclines;
+
+ y_stride = ALIGN(width * 2, 256);
+ uv_stride = ALIGN(width * 2, 256);
+ y_sclines = ALIGN(height, 32);
+ uv_sclines = ALIGN((height + 1) >> 1, 16);
+ y_plane = y_stride * y_sclines;
+ uv_plane = uv_stride * uv_sclines;
+
+ return ALIGN((y_plane + uv_plane), SZ_4K);
+}
+
+static u32 get_framesize_raw_p010_ubwc(u32 width, u32 height)
+{
+ u32 y_stride, uv_stride, y_sclines, uv_sclines;
+ u32 y_ubwc_plane, uv_ubwc_plane;
+ u32 y_meta_stride, y_meta_scanlines;
+ u32 uv_meta_stride, uv_meta_scanlines;
+ u32 y_meta_plane, uv_meta_plane;
+ u32 size;
+
+ y_stride = ALIGN(width * 2, 256);
+ uv_stride = ALIGN(width * 2, 256);
+ y_sclines = ALIGN(height, 16);
+ uv_sclines = ALIGN((height + 1) >> 1, 16);
+
+ y_ubwc_plane = ALIGN(y_stride * y_sclines, SZ_4K);
+ uv_ubwc_plane = ALIGN(uv_stride * uv_sclines, SZ_4K);
+ y_meta_stride = ALIGN(DIV_ROUND_UP(width, 32), 64);
+ y_meta_scanlines = ALIGN(DIV_ROUND_UP(height, 4), 16);
+ y_meta_plane = ALIGN(y_meta_stride * y_meta_scanlines, SZ_4K);
+ uv_meta_stride = ALIGN(DIV_ROUND_UP((width + 1) >> 1, 16), 64);
+ uv_meta_scanlines = ALIGN(DIV_ROUND_UP((height + 1) >> 1, 4), 16);
+ uv_meta_plane = ALIGN(uv_meta_stride * uv_meta_scanlines, SZ_4K);
+
+ size = y_ubwc_plane + uv_ubwc_plane + y_meta_plane + uv_meta_plane;
+
+ return ALIGN(size, SZ_4K);
+}
+
+static u32 get_framesize_raw_yuv420_tp10_ubwc(u32 width, u32 height)
+{
+ u32 y_stride, uv_stride, y_sclines, uv_sclines;
+ u32 y_ubwc_plane, uv_ubwc_plane;
+ u32 y_meta_stride, y_meta_scanlines;
+ u32 uv_meta_stride, uv_meta_scanlines;
+ u32 y_meta_plane, uv_meta_plane;
+ u32 extradata = SZ_16K;
+ u32 size;
+
+ y_stride = ALIGN(ALIGN(width, 192) * 4 / 3, 256);
+ uv_stride = ALIGN(ALIGN(width, 192) * 4 / 3, 256);
+ y_sclines = ALIGN(height, 16);
+ uv_sclines = ALIGN((height + 1) >> 1, 16);
+
+ y_ubwc_plane = ALIGN(y_stride * y_sclines, SZ_4K);
+ uv_ubwc_plane = ALIGN(uv_stride * uv_sclines, SZ_4K);
+ y_meta_stride = ALIGN(DIV_ROUND_UP(width, 48), 64);
+ y_meta_scanlines = ALIGN(DIV_ROUND_UP(height, 4), 16);
+ y_meta_plane = ALIGN(y_meta_stride * y_meta_scanlines, SZ_4K);
+ uv_meta_stride = ALIGN(DIV_ROUND_UP((width + 1) >> 1, 24), 64);
+ uv_meta_scanlines = ALIGN(DIV_ROUND_UP((height + 1) >> 1, 4), 16);
+ uv_meta_plane = ALIGN(uv_meta_stride * uv_meta_scanlines, SZ_4K);
+
+ size = y_ubwc_plane + uv_ubwc_plane + y_meta_plane + uv_meta_plane;
+ size += max(extradata + SZ_8K, y_stride * 48);
+
+ return ALIGN(size, SZ_4K);
+}
+
u32 venus_helper_get_framesz_raw(u32 hfi_fmt, u32 width, u32 height)
{
switch (hfi_fmt) {
@@ -715,6 +945,12 @@
return get_framesize_raw_nv12(width, height);
case HFI_COLOR_FORMAT_NV12_UBWC:
return get_framesize_raw_nv12_ubwc(width, height);
+ case HFI_COLOR_FORMAT_P010:
+ return get_framesize_raw_p010(width, height);
+ case HFI_COLOR_FORMAT_P010_UBWC:
+ return get_framesize_raw_p010_ubwc(width, height);
+ case HFI_COLOR_FORMAT_YUV420_TP10_UBWC:
+ return get_framesize_raw_yuv420_tp10_ubwc(width, height);
default:
return 0;
}
@@ -804,19 +1040,35 @@
}
EXPORT_SYMBOL_GPL(venus_helper_set_work_mode);
-int venus_helper_set_core_usage(struct venus_inst *inst, u32 usage)
+int venus_helper_init_codec_freq_data(struct venus_inst *inst)
{
- const u32 ptype = HFI_PROPERTY_CONFIG_VIDEOCORES_USAGE;
- struct hfi_videocores_usage_type cu;
+ const struct codec_freq_data *data;
+ unsigned int i, data_size;
+ u32 pixfmt;
+ int ret = 0;
if (!IS_V4(inst->core))
return 0;
- cu.video_core_enable_mask = usage;
+ data = inst->core->res->codec_freq_data;
+ data_size = inst->core->res->codec_freq_data_size;
+ pixfmt = inst->session_type == VIDC_SESSION_TYPE_DEC ?
+ inst->fmt_out->pixfmt : inst->fmt_cap->pixfmt;
- return hfi_session_set_property(inst, ptype, &cu);
+ for (i = 0; i < data_size; i++) {
+ if (data[i].pixfmt == pixfmt &&
+ data[i].session_type == inst->session_type) {
+ inst->clk_data.codec_freq_data = &data[i];
+ break;
+ }
+ }
+
+ if (!inst->clk_data.codec_freq_data)
+ ret = -EINVAL;
+
+ return ret;
}
-EXPORT_SYMBOL_GPL(venus_helper_set_core_usage);
+EXPORT_SYMBOL_GPL(venus_helper_init_codec_freq_data);
int venus_helper_set_num_bufs(struct venus_inst *inst, unsigned int input_bufs,
unsigned int output_bufs,
@@ -1075,6 +1327,15 @@
}
EXPORT_SYMBOL_GPL(venus_helper_vb2_buf_prepare);
+static void cache_payload(struct venus_inst *inst, struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ unsigned int idx = vbuf->vb2_buf.index;
+
+ if (vbuf->vb2_buf.type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ inst->payloads[idx] = vb2_get_plane_payload(vb, 0);
+}
+
void venus_helper_vb2_buf_queue(struct vb2_buffer *vb)
{
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
@@ -1086,6 +1347,8 @@
v4l2_m2m_buf_queue(m2m_ctx, vbuf);
+ cache_payload(inst, vb);
+
if (inst->session_type == VIDC_SESSION_TYPE_ENC &&
!(inst->streamon_out && inst->streamon_cap))
goto unlock;
@@ -1105,15 +1368,18 @@
}
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);
@@ -1140,17 +1406,22 @@
venus_helper_free_dpb_bufs(inst);
- venus_helper_load_scale_clocks(core);
+ venus_pm_load_scale(inst);
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;
else
inst->streamon_cap = 0;
+ venus_pm_release_core(inst);
+
mutex_unlock(&inst->lock);
}
EXPORT_SYMBOL_GPL(venus_helper_vb2_stop_streaming);
@@ -1193,7 +1464,6 @@
int venus_helper_vb2_start_streaming(struct venus_inst *inst)
{
- struct venus_core *core = inst->core;
int ret;
ret = venus_helper_intbufs_alloc(inst);
@@ -1204,7 +1474,7 @@
if (ret)
goto err_bufs_free;
- venus_helper_load_scale_clocks(core);
+ venus_pm_load_scale(inst);
ret = hfi_session_load_res(inst);
if (ret)
@@ -1299,6 +1569,27 @@
if (!caps)
return -EINVAL;
+ if (inst->bit_depth == VIDC_BITDEPTH_10 &&
+ inst->session_type == VIDC_SESSION_TYPE_DEC) {
+ found_ubwc =
+ find_fmt_from_caps(caps, HFI_BUFFER_OUTPUT,
+ HFI_COLOR_FORMAT_YUV420_TP10_UBWC);
+ found = find_fmt_from_caps(caps, HFI_BUFFER_OUTPUT2,
+ HFI_COLOR_FORMAT_NV12);
+ if (found_ubwc && found) {
+ /*
+ * Hard-code DPB buffers to be 10bit UBWC and decoder
+ * output buffers in 8bit NV12 until V4L2 is able to
+ * expose compressed/tiled formats to applications.
+ */
+ *out_fmt = HFI_COLOR_FORMAT_YUV420_TP10_UBWC;
+ *out2_fmt = HFI_COLOR_FORMAT_NV12;
+ return 0;
+ }
+
+ return -EINVAL;
+ }
+
if (ubwc) {
ubwc_fmt = fmt | HFI_COLOR_FORMAT_UBWC_BASE;
found_ubwc = find_fmt_from_caps(caps, HFI_BUFFER_OUTPUT,
@@ -1329,52 +1620,3 @@
return -EINVAL;
}
EXPORT_SYMBOL_GPL(venus_helper_get_out_fmts);
-
-int venus_helper_power_enable(struct venus_core *core, u32 session_type,
- bool enable)
-{
- void __iomem *ctrl, *stat;
- u32 val;
- int ret;
-
- if (!IS_V3(core) && !IS_V4(core))
- return 0;
-
- if (IS_V3(core)) {
- if (session_type == VIDC_SESSION_TYPE_DEC)
- ctrl = core->base + WRAPPER_VDEC_VCODEC_POWER_CONTROL;
- else
- ctrl = core->base + WRAPPER_VENC_VCODEC_POWER_CONTROL;
- if (enable)
- writel(0, ctrl);
- else
- writel(1, ctrl);
-
- return 0;
- }
-
- if (session_type == VIDC_SESSION_TYPE_DEC) {
- ctrl = core->base + WRAPPER_VCODEC0_MMCC_POWER_CONTROL;
- stat = core->base + WRAPPER_VCODEC0_MMCC_POWER_STATUS;
- } else {
- ctrl = core->base + WRAPPER_VCODEC1_MMCC_POWER_CONTROL;
- stat = core->base + WRAPPER_VCODEC1_MMCC_POWER_STATUS;
- }
-
- if (enable) {
- writel(0, ctrl);
-
- ret = readl_poll_timeout(stat, val, val & BIT(1), 1, 100);
- if (ret)
- return ret;
- } else {
- writel(1, ctrl);
-
- ret = readl_poll_timeout(stat, val, !(val & BIT(1)), 1, 100);
- if (ret)
- return ret;
- }
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(venus_helper_power_enable);
diff --git a/drivers/media/platform/qcom/venus/helpers.h b/drivers/media/platform/qcom/venus/helpers.h
index 01f411b..a4a0562 100644
--- a/drivers/media/platform/qcom/venus/helpers.h
+++ b/drivers/media/platform/qcom/venus/helpers.h
@@ -14,7 +14,7 @@
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);
@@ -33,7 +33,7 @@
unsigned int width, unsigned int height,
u32 buftype);
int venus_helper_set_work_mode(struct venus_inst *inst, u32 mode);
-int venus_helper_set_core_usage(struct venus_inst *inst, u32 usage);
+int venus_helper_init_codec_freq_data(struct venus_inst *inst);
int venus_helper_set_num_bufs(struct venus_inst *inst, unsigned int input_bufs,
unsigned int output_bufs,
unsigned int output2_bufs);
@@ -52,16 +52,15 @@
u32 *out2_fmt, bool ubwc);
int venus_helper_alloc_dpb_bufs(struct venus_inst *inst);
int venus_helper_free_dpb_bufs(struct venus_inst *inst);
-int venus_helper_power_enable(struct venus_core *core, u32 session_type,
- bool enable);
int venus_helper_intbufs_alloc(struct venus_inst *inst);
int venus_helper_intbufs_free(struct venus_inst *inst);
int venus_helper_intbufs_realloc(struct venus_inst *inst);
int venus_helper_queue_dpb_bufs(struct venus_inst *inst);
int venus_helper_unregister_bufs(struct venus_inst *inst);
-int venus_helper_load_scale_clocks(struct venus_core *core);
int venus_helper_process_initial_cap_bufs(struct venus_inst *inst);
int venus_helper_process_initial_out_bufs(struct venus_inst *inst);
void venus_helper_get_ts_metadata(struct venus_inst *inst, u64 timestamp_us,
struct vb2_v4l2_buffer *vbuf);
+int venus_helper_get_profile_level(struct venus_inst *inst, u32 *profile, u32 *level);
+int venus_helper_set_profile_level(struct venus_inst *inst, u32 profile, u32 level);
#endif
diff --git a/drivers/media/platform/qcom/venus/hfi.c b/drivers/media/platform/qcom/venus/hfi.c
index 3d8b128..a59022a 100644
--- a/drivers/media/platform/qcom/venus/hfi.c
+++ b/drivers/media/platform/qcom/venus/hfi.c
@@ -382,7 +382,7 @@
}
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 @@
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;
}
@@ -515,3 +517,8 @@
{
venus_hfi_destroy(core);
}
+
+void hfi_reinit(struct venus_core *core)
+{
+ venus_hfi_queues_reinit(core);
+}
diff --git a/drivers/media/platform/qcom/venus/hfi.h b/drivers/media/platform/qcom/venus/hfi.h
index 855822c..f25d412 100644
--- a/drivers/media/platform/qcom/venus/hfi.h
+++ b/drivers/media/platform/qcom/venus/hfi.h
@@ -102,6 +102,7 @@
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 {
@@ -144,6 +145,7 @@
int hfi_create(struct venus_core *core, const struct hfi_core_ops *ops);
void hfi_destroy(struct venus_core *core);
+void hfi_reinit(struct venus_core *core);
int hfi_core_init(struct venus_core *core);
int hfi_core_deinit(struct venus_core *core, bool blocking);
@@ -161,7 +163,7 @@
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.c b/drivers/media/platform/qcom/venus/hfi_cmds.c
index 4f64507..7022368 100644
--- a/drivers/media/platform/qcom/venus/hfi_cmds.c
+++ b/drivers/media/platform/qcom/venus/hfi_cmds.c
@@ -640,6 +640,7 @@
case HFI_RATE_CONTROL_CBR_VFR:
case HFI_RATE_CONTROL_VBR_CFR:
case HFI_RATE_CONTROL_VBR_VFR:
+ case HFI_RATE_CONTROL_CQ:
break;
default:
ret = -EINVAL;
@@ -1207,6 +1208,8 @@
case HFI_PROPERTY_CONFIG_VENC_MAX_BITRATE:
case HFI_PROPERTY_CONFIG_VDEC_POST_LOOP_DEBLOCKER:
case HFI_PROPERTY_PARAM_BUFFER_ALLOC_MODE:
+ case HFI_PROPERTY_PARAM_VENC_SESSION_QP:
+ case HFI_PROPERTY_PARAM_VENC_SESSION_QP_RANGE:
/* not implemented on Venus 4xx */
return -ENOTSUPP;
default:
@@ -1216,6 +1219,37 @@
return 0;
}
+static int
+pkt_session_set_property_6xx(struct hfi_session_set_property_pkt *pkt,
+ void *cookie, u32 ptype, void *pdata)
+{
+ void *prop_data;
+
+ if (!pkt || !cookie || !pdata)
+ return -EINVAL;
+
+ prop_data = &pkt->data[1];
+
+ pkt->shdr.hdr.size = sizeof(*pkt);
+ pkt->shdr.hdr.pkt_type = HFI_CMD_SESSION_SET_PROPERTY;
+ pkt->shdr.session_id = hash32_ptr(cookie);
+ pkt->num_properties = 1;
+ pkt->data[0] = ptype;
+
+ switch (ptype) {
+ case HFI_PROPERTY_CONFIG_HEIC_FRAME_QUALITY: {
+ struct hfi_heic_frame_quality *in = pdata, *cq = prop_data;
+
+ cq->frame_quality = in->frame_quality;
+ pkt->shdr.hdr.size += sizeof(u32) + sizeof(*cq);
+ break;
+ } default:
+ return pkt_session_set_property_4xx(pkt, cookie, ptype, pdata);
+ }
+
+ return 0;
+}
+
int pkt_session_get_property(struct hfi_session_get_property_pkt *pkt,
void *cookie, u32 ptype)
{
@@ -1234,7 +1268,10 @@
if (hfi_ver == HFI_VERSION_3XX)
return pkt_session_set_property_3xx(pkt, cookie, ptype, pdata);
- return pkt_session_set_property_4xx(pkt, cookie, ptype, pdata);
+ if (hfi_ver == HFI_VERSION_4XX)
+ return pkt_session_set_property_4xx(pkt, cookie, ptype, pdata);
+
+ return pkt_session_set_property_6xx(pkt, cookie, ptype, pdata);
}
void pkt_set_version(enum hfi_version version)
diff --git a/drivers/media/platform/qcom/venus/hfi_cmds.h b/drivers/media/platform/qcom/venus/hfi_cmds.h
index cae9d5d..83705e2 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_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_helper.h b/drivers/media/platform/qcom/venus/hfi_helper.h
index b70551e..60ee247 100644
--- a/drivers/media/platform/qcom/venus/hfi_helper.h
+++ b/drivers/media/platform/qcom/venus/hfi_helper.h
@@ -231,6 +231,7 @@
#define HFI_RATE_CONTROL_VBR_CFR 0x1000003
#define HFI_RATE_CONTROL_CBR_VFR 0x1000004
#define HFI_RATE_CONTROL_CBR_CFR 0x1000005
+#define HFI_RATE_CONTROL_CQ 0x1000008
#define HFI_VIDEO_CODEC_H264 0x00000002
#define HFI_VIDEO_CODEC_H263 0x00000004
@@ -363,6 +364,24 @@
#define HFI_HEVC_TIER_MAIN 0x1
#define HFI_HEVC_TIER_HIGH0 0x2
+/* VP9 Profile 0, 8-bit */
+#define HFI_VP9_PROFILE_P0 0x00000001
+/* VP9 Profile 2, 10-bit */
+#define HFI_VP9_PROFILE_P2_10B 0x00000004
+
+#define HFI_VP9_LEVEL_1 0x00000001
+#define HFI_VP9_LEVEL_11 0x00000002
+#define HFI_VP9_LEVEL_2 0x00000004
+#define HFI_VP9_LEVEL_21 0x00000008
+#define HFI_VP9_LEVEL_3 0x00000010
+#define HFI_VP9_LEVEL_31 0x00000020
+#define HFI_VP9_LEVEL_4 0x00000040
+#define HFI_VP9_LEVEL_41 0x00000080
+#define HFI_VP9_LEVEL_5 0x00000100
+#define HFI_VP9_LEVEL_51 0x00000200
+#define HFI_VP9_LEVEL_6 0x00000400
+#define HFI_VP9_LEVEL_61 0x00000800
+
#define HFI_BUFFER_INPUT 0x1
#define HFI_BUFFER_OUTPUT 0x2
#define HFI_BUFFER_OUTPUT2 0x3
@@ -504,6 +523,7 @@
#define HFI_PROPERTY_CONFIG_VENC_HIER_P_ENH_LAYER 0x200600b
#define HFI_PROPERTY_CONFIG_VENC_LTRPERIOD 0x200600c
#define HFI_PROPERTY_CONFIG_VENC_PERF_MODE 0x200600e
+#define HFI_PROPERTY_CONFIG_HEIC_FRAME_QUALITY 0x2006014
/*
* HFI_PROPERTY_PARAM_VPE_COMMON_START
@@ -520,7 +540,8 @@
enum hfi_version {
HFI_VERSION_1XX,
HFI_VERSION_3XX,
- HFI_VERSION_4XX
+ HFI_VERSION_4XX,
+ HFI_VERSION_6XX,
};
struct hfi_buffer_info {
@@ -550,6 +571,7 @@
#define HFI_CAPABILITY_LCU_SIZE 0x14
#define HFI_CAPABILITY_HIER_P_HYBRID_NUM_ENH_LAYERS 0x15
#define HFI_CAPABILITY_MBS_PER_SECOND_POWERSAVE 0x16
+#define HFI_CAPABILITY_MAX_VIDEOCORES 0x2b
struct hfi_capability {
u32 capability_type;
@@ -724,6 +746,11 @@
u32 quality_vs_speed;
};
+struct hfi_heic_frame_quality {
+ u32 frame_quality;
+ u32 reserved[3];
+};
+
struct hfi_quantization {
u32 qp_i;
u32 qp_p;
@@ -792,6 +819,9 @@
u32 time_scale;
};
+#define VIDC_BITDEPTH_8 0x00000
+#define VIDC_BITDEPTH_10 0x20002
+
struct hfi_bit_depth {
u32 buffer_type;
u32 bit_depth;
@@ -840,8 +870,10 @@
#define HFI_COLOR_FORMAT_10_BIT_BASE 0x4000
#define HFI_COLOR_FORMAT_YUV420_TP10 0x4002
+#define HFI_COLOR_FORMAT_P010 0x4003
#define HFI_COLOR_FORMAT_NV12_UBWC 0x8002
#define HFI_COLOR_FORMAT_YUV420_TP10_UBWC 0xc002
+#define HFI_COLOR_FORMAT_P010_UBWC 0xc003
#define HFI_COLOR_FORMAT_RGBA8888_UBWC 0x8010
struct hfi_uncompressed_format_select {
diff --git a/drivers/media/platform/qcom/venus/hfi_msgs.c b/drivers/media/platform/qcom/venus/hfi_msgs.c
index 04ef228..06a1908 100644
--- a/drivers/media/platform/qcom/venus/hfi_msgs.c
+++ b/drivers/media/platform/qcom/venus/hfi_msgs.c
@@ -138,7 +138,7 @@
struct hfi_msg_event_notify_pkt *pkt)
{
if (pkt)
- dev_dbg(core->dev,
+ dev_dbg(core->dev, VDBGH
"sys error (session id:%x, data1:%x, data2:%x)\n",
pkt->shdr.session_id, pkt->event_data1,
pkt->event_data2);
@@ -152,7 +152,7 @@
{
struct device *dev = core->dev;
- dev_dbg(dev, "session error: event id:%x, session id:%x\n",
+ dev_dbg(dev, VDBGH "session error: event id:%x, session id:%x\n",
pkt->event_data1, pkt->shdr.session_id);
if (!inst)
@@ -247,7 +247,7 @@
/* bad packet */
return;
- dev_dbg(dev, "F/W version: %s\n", (u8 *)&pkt->data[1]);
+ dev_dbg(dev, VDBGL "F/W version: %s\n", (u8 *)&pkt->data[1]);
}
static void hfi_sys_property_info(struct venus_core *core,
@@ -257,7 +257,7 @@
struct device *dev = core->dev;
if (!pkt->num_properties) {
- dev_dbg(dev, "%s: no properties\n", __func__);
+ dev_dbg(dev, VDBGL "no properties\n");
return;
}
@@ -266,7 +266,7 @@
sys_get_prop_image_version(dev, pkt);
break;
default:
- dev_dbg(dev, "%s: unknown property data\n", __func__);
+ dev_dbg(dev, VDBGL "unknown property data\n");
break;
}
}
@@ -297,7 +297,7 @@
static void hfi_sys_idle_done(struct venus_core *core, struct venus_inst *inst,
void *packet)
{
- dev_dbg(core->dev, "sys idle\n");
+ dev_dbg(core->dev, VDBGL "sys idle\n");
}
static void hfi_sys_pc_prepare_done(struct venus_core *core,
@@ -305,7 +305,8 @@
{
struct hfi_msg_sys_pc_prep_done_pkt *pkt = packet;
- dev_dbg(core->dev, "pc prepare done (error %x)\n", pkt->error_type);
+ dev_dbg(core->dev, VDBGL "pc prepare done (error %x)\n",
+ pkt->error_type);
}
static unsigned int
@@ -387,8 +388,7 @@
case HFI_PROPERTY_CONFIG_VDEC_ENTROPY:
break;
default:
- dev_dbg(dev, "%s: unknown property id:%x\n", __func__,
- pkt->data[0]);
+ dev_dbg(dev, VDBGM "unknown property id:%x\n", pkt->data[0]);
return;
}
@@ -439,6 +439,8 @@
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 7694b1d..526d9f5 100644
--- a/drivers/media/platform/qcom/venus/hfi_msgs.h
+++ b/drivers/media/platform/qcom/venus/hfi_msgs.h
@@ -155,7 +155,7 @@
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 @@
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 @@
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 @@
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 @@
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/hfi_parser.c b/drivers/media/platform/qcom/venus/hfi_parser.c
index 7f515a4..2dcf7ea 100644
--- a/drivers/media/platform/qcom/venus/hfi_parser.c
+++ b/drivers/media/platform/qcom/venus/hfi_parser.c
@@ -239,6 +239,11 @@
parser_init(inst, &codecs, &domain);
+ if (core->res->hfi_version > HFI_VERSION_1XX) {
+ core->codecs_count = 0;
+ memset(core->caps, 0, sizeof(core->caps));
+ }
+
while (words_count) {
data = word + 1;
diff --git a/drivers/media/platform/qcom/venus/hfi_parser.h b/drivers/media/platform/qcom/venus/hfi_parser.h
index 3e931c7..264e6dd 100644
--- a/drivers/media/platform/qcom/venus/hfi_parser.h
+++ b/drivers/media/platform/qcom/venus/hfi_parser.h
@@ -107,4 +107,9 @@
return cap_step(inst, HFI_CAPABILITY_FRAMERATE);
}
+static inline u32 core_num_max(struct venus_inst *inst)
+{
+ return cap_max(inst, HFI_CAPABILITY_MAX_VIDEOCORES);
+}
+
#endif
diff --git a/drivers/media/platform/qcom/venus/hfi_venus.c b/drivers/media/platform/qcom/venus/hfi_venus.c
index 0d88550..4be4a75 100644
--- a/drivers/media/platform/qcom/venus/hfi_venus.c
+++ b/drivers/media/platform/qcom/venus/hfi_venus.c
@@ -130,7 +130,7 @@
};
static bool venus_pkt_debug;
-static int venus_fw_debug = HFI_DEBUG_MSG_ERROR | HFI_DEBUG_MSG_FATAL;
+int venus_fw_debug = HFI_DEBUG_MSG_ERROR | HFI_DEBUG_MSG_FATAL;
static bool venus_sys_idle_indicator;
static bool venus_fw_low_power_mode = true;
static int venus_hw_rsp_timeout = 1000;
@@ -477,7 +477,7 @@
minor = minor >> WRAPPER_HW_VERSION_MINOR_VERSION_SHIFT;
step = ver & WRAPPER_HW_VERSION_STEP_VERSION_MASK;
- dev_dbg(dev, "venus hw version %x.%x.%x\n", major, minor, step);
+ dev_dbg(dev, VDBGL "venus hw version %x.%x.%x\n", major, minor, step);
return major;
}
@@ -906,7 +906,7 @@
if (pkt->hdr.pkt_type != HFI_MSG_SYS_COV) {
struct hfi_msg_sys_debug_pkt *pkt = packet;
- dev_dbg(dev, "%s", pkt->msg_data);
+ dev_dbg(dev, VDBGFW "%s", pkt->msg_data);
}
}
}
@@ -986,13 +986,6 @@
venus_set_state(hdev, VENUS_STATE_DEINIT);
- /*
- * Once SYS_ERROR received from HW, it is safe to halt the AXI.
- * With SYS_ERROR, Venus FW may have crashed and HW might be
- * active and causing unnecessary transactions. Hence it is
- * safe to stop all AXI transactions from venus subsystem.
- */
- venus_halt_axi(hdev);
venus_sfr_print(hdev);
}
@@ -1009,10 +1002,6 @@
res = hdev->core->res;
pkt = hdev->pkt_buf;
- if (hdev->irq_status & WRAPPER_INTR_STATUS_A2HWD_MASK) {
- venus_sfr_print(hdev);
- hfi_process_watchdog_timeout(core);
- }
while (!venus_iface_msgq_read(hdev, pkt)) {
msg_ret = hfi_process_msg_packet(core, pkt);
@@ -1133,6 +1122,10 @@
struct hfi_session_init_pkt pkt;
int ret;
+ ret = venus_sys_set_debug(hdev, venus_fw_debug);
+ if (ret)
+ goto err;
+
ret = pkt_session_init(&pkt, inst, session_type, codec);
if (ret)
goto err;
@@ -1614,3 +1607,54 @@
core->ops = NULL;
return ret;
}
+
+void venus_hfi_queues_reinit(struct venus_core *core)
+{
+ struct venus_hfi_device *hdev = to_hfi_priv(core);
+ struct hfi_queue_table_header *tbl_hdr;
+ struct iface_queue *queue;
+ struct hfi_sfr *sfr;
+ unsigned int i;
+
+ mutex_lock(&hdev->lock);
+
+ for (i = 0; i < IFACEQ_NUM; i++) {
+ queue = &hdev->queues[i];
+ queue->qhdr =
+ IFACEQ_GET_QHDR_START_ADDR(hdev->ifaceq_table.kva, i);
+
+ venus_set_qhdr_defaults(queue->qhdr);
+
+ queue->qhdr->start_addr = queue->qmem.da;
+
+ if (i == IFACEQ_CMD_IDX)
+ queue->qhdr->type |= HFI_HOST_TO_CTRL_CMD_Q;
+ else if (i == IFACEQ_MSG_IDX)
+ queue->qhdr->type |= HFI_CTRL_TO_HOST_MSG_Q;
+ else if (i == IFACEQ_DBG_IDX)
+ queue->qhdr->type |= HFI_CTRL_TO_HOST_DBG_Q;
+ }
+
+ tbl_hdr = hdev->ifaceq_table.kva;
+ tbl_hdr->version = 0;
+ tbl_hdr->size = IFACEQ_TABLE_SIZE;
+ tbl_hdr->qhdr0_offset = sizeof(struct hfi_queue_table_header);
+ tbl_hdr->qhdr_size = sizeof(struct hfi_queue_header);
+ tbl_hdr->num_q = IFACEQ_NUM;
+ tbl_hdr->num_active_q = IFACEQ_NUM;
+
+ /*
+ * Set receive request to zero on debug queue as there is no
+ * need of interrupt from video hardware for debug messages
+ */
+ queue = &hdev->queues[IFACEQ_DBG_IDX];
+ queue->qhdr->rx_req = 0;
+
+ sfr = hdev->sfr.kva;
+ sfr->buf_size = ALIGNED_SFR_SIZE;
+
+ /* ensure table and queue header structs are settled in memory */
+ wmb();
+
+ mutex_unlock(&hdev->lock);
+}
diff --git a/drivers/media/platform/qcom/venus/hfi_venus.h b/drivers/media/platform/qcom/venus/hfi_venus.h
index 5715483..1b656ef 100644
--- a/drivers/media/platform/qcom/venus/hfi_venus.h
+++ b/drivers/media/platform/qcom/venus/hfi_venus.h
@@ -10,5 +10,6 @@
void venus_hfi_destroy(struct venus_core *core);
int venus_hfi_create(struct venus_core *core);
+void venus_hfi_queues_reinit(struct venus_core *core);
#endif
diff --git a/drivers/media/platform/qcom/venus/pm_helpers.c b/drivers/media/platform/qcom/venus/pm_helpers.c
new file mode 100644
index 0000000..710f9a2
--- /dev/null
+++ b/drivers/media/platform/qcom/venus/pm_helpers.c
@@ -0,0 +1,1049 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2019 Linaro Ltd.
+ *
+ * Author: Stanimir Varbanov <stanimir.varbanov@linaro.org>
+ */
+#include <linux/clk.h>
+#include <linux/interconnect.h>
+#include <linux/iopoll.h>
+#include <linux/kernel.h>
+#include <linux/pm_domain.h>
+#include <linux/pm_opp.h>
+#include <linux/pm_runtime.h>
+#include <linux/types.h>
+#include <media/v4l2-mem2mem.h>
+
+#include "core.h"
+#include "hfi_parser.h"
+#include "hfi_venus_io.h"
+#include "pm_helpers.h"
+
+static bool legacy_binding;
+
+static int core_clks_get(struct venus_core *core)
+{
+ const struct venus_resources *res = core->res;
+ struct device *dev = core->dev;
+ unsigned int i;
+
+ for (i = 0; i < res->clks_num; i++) {
+ core->clks[i] = devm_clk_get(dev, res->clks[i]);
+ if (IS_ERR(core->clks[i]))
+ return PTR_ERR(core->clks[i]);
+ }
+
+ return 0;
+}
+
+static int core_clks_enable(struct venus_core *core)
+{
+ const struct venus_resources *res = core->res;
+ unsigned int i;
+ int ret;
+
+ for (i = 0; i < res->clks_num; i++) {
+ ret = clk_prepare_enable(core->clks[i]);
+ if (ret)
+ goto err;
+ }
+
+ return 0;
+err:
+ while (i--)
+ clk_disable_unprepare(core->clks[i]);
+
+ return ret;
+}
+
+static void core_clks_disable(struct venus_core *core)
+{
+ const struct venus_resources *res = core->res;
+ unsigned int i = res->clks_num;
+
+ while (i--)
+ clk_disable_unprepare(core->clks[i]);
+}
+
+static int core_clks_set_rate(struct venus_core *core, unsigned long freq)
+{
+ int ret;
+
+ ret = dev_pm_opp_set_rate(core->dev, freq);
+ if (ret)
+ return ret;
+
+ ret = clk_set_rate(core->vcodec0_clks[0], freq);
+ if (ret)
+ return ret;
+
+ ret = clk_set_rate(core->vcodec1_clks[0], freq);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int vcodec_clks_get(struct venus_core *core, struct device *dev,
+ struct clk **clks, const char * const *id)
+{
+ const struct venus_resources *res = core->res;
+ unsigned int i;
+
+ for (i = 0; i < res->vcodec_clks_num; i++) {
+ if (!id[i])
+ continue;
+ clks[i] = devm_clk_get(dev, id[i]);
+ if (IS_ERR(clks[i]))
+ return PTR_ERR(clks[i]);
+ }
+
+ return 0;
+}
+
+static int vcodec_clks_enable(struct venus_core *core, struct clk **clks)
+{
+ const struct venus_resources *res = core->res;
+ unsigned int i;
+ int ret;
+
+ for (i = 0; i < res->vcodec_clks_num; i++) {
+ ret = clk_prepare_enable(clks[i]);
+ if (ret)
+ goto err;
+ }
+
+ return 0;
+err:
+ while (i--)
+ clk_disable_unprepare(clks[i]);
+
+ return ret;
+}
+
+static void vcodec_clks_disable(struct venus_core *core, struct clk **clks)
+{
+ const struct venus_resources *res = core->res;
+ unsigned int i = res->vcodec_clks_num;
+
+ while (i--)
+ clk_disable_unprepare(clks[i]);
+}
+
+static u32 load_per_instance(struct venus_inst *inst)
+{
+ u32 mbs;
+
+ if (!inst || !(inst->state >= INST_INIT && inst->state < INST_STOP))
+ return 0;
+
+ mbs = (ALIGN(inst->width, 16) / 16) * (ALIGN(inst->height, 16) / 16);
+
+ return mbs * inst->fps;
+}
+
+static u32 load_per_type(struct venus_core *core, u32 session_type)
+{
+ struct venus_inst *inst = NULL;
+ u32 mbs_per_sec = 0;
+
+ list_for_each_entry(inst, &core->instances, list) {
+ if (inst->session_type != session_type)
+ continue;
+
+ mbs_per_sec += load_per_instance(inst);
+ }
+
+ return mbs_per_sec;
+}
+
+static void mbs_to_bw(struct venus_inst *inst, u32 mbs, u32 *avg, u32 *peak)
+{
+ const struct venus_resources *res = inst->core->res;
+ const struct bw_tbl *bw_tbl;
+ unsigned int num_rows, i;
+
+ *avg = 0;
+ *peak = 0;
+
+ if (mbs == 0)
+ return;
+
+ if (inst->session_type == VIDC_SESSION_TYPE_ENC) {
+ num_rows = res->bw_tbl_enc_size;
+ bw_tbl = res->bw_tbl_enc;
+ } else if (inst->session_type == VIDC_SESSION_TYPE_DEC) {
+ num_rows = res->bw_tbl_dec_size;
+ bw_tbl = res->bw_tbl_dec;
+ } else {
+ return;
+ }
+
+ if (!bw_tbl || num_rows == 0)
+ return;
+
+ for (i = 0; i < num_rows; i++) {
+ if (mbs > bw_tbl[i].mbs_per_sec)
+ break;
+
+ if (inst->dpb_fmt & HFI_COLOR_FORMAT_10_BIT_BASE) {
+ *avg = bw_tbl[i].avg_10bit;
+ *peak = bw_tbl[i].peak_10bit;
+ } else {
+ *avg = bw_tbl[i].avg;
+ *peak = bw_tbl[i].peak;
+ }
+ }
+}
+
+static int load_scale_bw(struct venus_core *core)
+{
+ struct venus_inst *inst = NULL;
+ u32 mbs_per_sec, avg, peak, total_avg = 0, total_peak = 0;
+
+ list_for_each_entry(inst, &core->instances, list) {
+ mbs_per_sec = load_per_instance(inst);
+ mbs_to_bw(inst, mbs_per_sec, &avg, &peak);
+ total_avg += avg;
+ total_peak += peak;
+ }
+
+ /*
+ * keep minimum bandwidth vote for "video-mem" path,
+ * so that clks can be disabled during vdec_session_release().
+ * Actual bandwidth drop will be done during device supend
+ * so that device can power down without any warnings.
+ */
+
+ if (!total_avg && !total_peak)
+ total_avg = kbps_to_icc(1000);
+
+ dev_dbg(core->dev, VDBGL "total: avg_bw: %u, peak_bw: %u\n",
+ total_avg, total_peak);
+
+ return icc_set_bw(core->video_path, total_avg, total_peak);
+}
+
+static int load_scale_v1(struct venus_inst *inst)
+{
+ struct venus_core *core = inst->core;
+ const struct freq_tbl *table = core->res->freq_tbl;
+ unsigned int num_rows = core->res->freq_tbl_size;
+ unsigned long freq = table[0].freq;
+ struct device *dev = core->dev;
+ u32 mbs_per_sec;
+ unsigned int i;
+ int ret = 0;
+
+ mutex_lock(&core->lock);
+ mbs_per_sec = load_per_type(core, VIDC_SESSION_TYPE_ENC) +
+ load_per_type(core, VIDC_SESSION_TYPE_DEC);
+
+ if (mbs_per_sec > core->res->max_load)
+ dev_warn(dev, "HW is overloaded, needed: %d max: %d\n",
+ mbs_per_sec, core->res->max_load);
+
+ if (!mbs_per_sec && num_rows > 1) {
+ freq = table[num_rows - 1].freq;
+ goto set_freq;
+ }
+
+ for (i = 0; i < num_rows; i++) {
+ if (mbs_per_sec > table[i].load)
+ break;
+ freq = table[i].freq;
+ }
+
+set_freq:
+
+ ret = core_clks_set_rate(core, freq);
+ if (ret) {
+ dev_err(dev, "failed to set clock rate %lu (%d)\n",
+ freq, ret);
+ goto exit;
+ }
+
+ ret = load_scale_bw(core);
+ if (ret) {
+ dev_err(dev, "failed to set bandwidth (%d)\n",
+ ret);
+ goto exit;
+ }
+
+exit:
+ mutex_unlock(&core->lock);
+ return ret;
+}
+
+static int core_get_v1(struct venus_core *core)
+{
+ return core_clks_get(core);
+}
+
+static int core_power_v1(struct venus_core *core, int on)
+{
+ int ret = 0;
+
+ if (on == POWER_ON)
+ ret = core_clks_enable(core);
+ else
+ core_clks_disable(core);
+
+ return ret;
+}
+
+static const struct venus_pm_ops pm_ops_v1 = {
+ .core_get = core_get_v1,
+ .core_power = core_power_v1,
+ .load_scale = load_scale_v1,
+};
+
+static void
+vcodec_control_v3(struct venus_core *core, u32 session_type, bool enable)
+{
+ void __iomem *ctrl;
+
+ if (session_type == VIDC_SESSION_TYPE_DEC)
+ ctrl = core->base + WRAPPER_VDEC_VCODEC_POWER_CONTROL;
+ else
+ ctrl = core->base + WRAPPER_VENC_VCODEC_POWER_CONTROL;
+
+ if (enable)
+ writel(0, ctrl);
+ else
+ writel(1, ctrl);
+}
+
+static int vdec_get_v3(struct device *dev)
+{
+ struct venus_core *core = dev_get_drvdata(dev);
+
+ return vcodec_clks_get(core, dev, core->vcodec0_clks,
+ core->res->vcodec0_clks);
+}
+
+static int vdec_power_v3(struct device *dev, int on)
+{
+ struct venus_core *core = dev_get_drvdata(dev);
+ int ret = 0;
+
+ vcodec_control_v3(core, VIDC_SESSION_TYPE_DEC, true);
+
+ if (on == POWER_ON)
+ ret = vcodec_clks_enable(core, core->vcodec0_clks);
+ else
+ vcodec_clks_disable(core, core->vcodec0_clks);
+
+ vcodec_control_v3(core, VIDC_SESSION_TYPE_DEC, false);
+
+ return ret;
+}
+
+static int venc_get_v3(struct device *dev)
+{
+ struct venus_core *core = dev_get_drvdata(dev);
+
+ return vcodec_clks_get(core, dev, core->vcodec1_clks,
+ core->res->vcodec1_clks);
+}
+
+static int venc_power_v3(struct device *dev, int on)
+{
+ struct venus_core *core = dev_get_drvdata(dev);
+ int ret = 0;
+
+ vcodec_control_v3(core, VIDC_SESSION_TYPE_ENC, true);
+
+ if (on == POWER_ON)
+ ret = vcodec_clks_enable(core, core->vcodec1_clks);
+ else
+ vcodec_clks_disable(core, core->vcodec1_clks);
+
+ vcodec_control_v3(core, VIDC_SESSION_TYPE_ENC, false);
+
+ return ret;
+}
+
+static const struct venus_pm_ops pm_ops_v3 = {
+ .core_get = core_get_v1,
+ .core_power = core_power_v1,
+ .vdec_get = vdec_get_v3,
+ .vdec_power = vdec_power_v3,
+ .venc_get = venc_get_v3,
+ .venc_power = venc_power_v3,
+ .load_scale = load_scale_v1,
+};
+
+static int vcodec_control_v4(struct venus_core *core, u32 coreid, bool enable)
+{
+ void __iomem *ctrl, *stat;
+ u32 val;
+ int ret;
+
+ if (coreid == VIDC_CORE_ID_1) {
+ ctrl = core->base + WRAPPER_VCODEC0_MMCC_POWER_CONTROL;
+ stat = core->base + WRAPPER_VCODEC0_MMCC_POWER_STATUS;
+ } else {
+ ctrl = core->base + WRAPPER_VCODEC1_MMCC_POWER_CONTROL;
+ stat = core->base + WRAPPER_VCODEC1_MMCC_POWER_STATUS;
+ }
+
+ if (enable) {
+ writel(0, ctrl);
+
+ ret = readl_poll_timeout(stat, val, val & BIT(1), 1, 100);
+ if (ret)
+ return ret;
+ } else {
+ writel(1, ctrl);
+
+ ret = readl_poll_timeout(stat, val, !(val & BIT(1)), 1, 100);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int poweroff_coreid(struct venus_core *core, unsigned int coreid_mask)
+{
+ int ret;
+
+ if (coreid_mask & VIDC_CORE_ID_1) {
+ ret = vcodec_control_v4(core, VIDC_CORE_ID_1, true);
+ if (ret)
+ return ret;
+
+ vcodec_clks_disable(core, core->vcodec0_clks);
+
+ ret = vcodec_control_v4(core, VIDC_CORE_ID_1, false);
+ if (ret)
+ return ret;
+
+ ret = pm_runtime_put_sync(core->pmdomains[1]);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (coreid_mask & VIDC_CORE_ID_2) {
+ ret = vcodec_control_v4(core, VIDC_CORE_ID_2, true);
+ if (ret)
+ return ret;
+
+ vcodec_clks_disable(core, core->vcodec1_clks);
+
+ ret = vcodec_control_v4(core, VIDC_CORE_ID_2, false);
+ if (ret)
+ return ret;
+
+ ret = pm_runtime_put_sync(core->pmdomains[2]);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int poweron_coreid(struct venus_core *core, unsigned int coreid_mask)
+{
+ int ret;
+
+ if (coreid_mask & VIDC_CORE_ID_1) {
+ ret = pm_runtime_get_sync(core->pmdomains[1]);
+ if (ret < 0)
+ return ret;
+
+ ret = vcodec_control_v4(core, VIDC_CORE_ID_1, true);
+ if (ret)
+ return ret;
+
+ ret = vcodec_clks_enable(core, core->vcodec0_clks);
+ if (ret)
+ return ret;
+
+ ret = vcodec_control_v4(core, VIDC_CORE_ID_1, false);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (coreid_mask & VIDC_CORE_ID_2) {
+ ret = pm_runtime_get_sync(core->pmdomains[2]);
+ if (ret < 0)
+ return ret;
+
+ ret = vcodec_control_v4(core, VIDC_CORE_ID_2, true);
+ if (ret)
+ return ret;
+
+ ret = vcodec_clks_enable(core, core->vcodec1_clks);
+ if (ret)
+ return ret;
+
+ ret = vcodec_control_v4(core, VIDC_CORE_ID_2, false);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static void
+min_loaded_core(struct venus_inst *inst, u32 *min_coreid, u32 *min_load)
+{
+ u32 mbs_per_sec, load, core1_load = 0, core2_load = 0;
+ u32 cores_max = core_num_max(inst);
+ struct venus_core *core = inst->core;
+ struct venus_inst *inst_pos;
+ unsigned long vpp_freq;
+ u32 coreid;
+
+ mutex_lock(&core->lock);
+
+ list_for_each_entry(inst_pos, &core->instances, list) {
+ if (inst_pos == inst)
+ continue;
+
+ if (inst_pos->state != INST_START)
+ continue;
+
+ vpp_freq = inst_pos->clk_data.codec_freq_data->vpp_freq;
+ coreid = inst_pos->clk_data.core_id;
+
+ mbs_per_sec = load_per_instance(inst_pos);
+ load = mbs_per_sec * vpp_freq;
+
+ if ((coreid & VIDC_CORE_ID_3) == VIDC_CORE_ID_3) {
+ core1_load += load / 2;
+ core2_load += load / 2;
+ } else if (coreid & VIDC_CORE_ID_1) {
+ core1_load += load;
+ } else if (coreid & VIDC_CORE_ID_2) {
+ core2_load += load;
+ }
+ }
+
+ *min_coreid = core1_load <= core2_load ?
+ VIDC_CORE_ID_1 : VIDC_CORE_ID_2;
+ *min_load = min(core1_load, core2_load);
+
+ if (cores_max < VIDC_CORE_ID_2 || core->res->vcodec_num < 2) {
+ *min_coreid = VIDC_CORE_ID_1;
+ *min_load = core1_load;
+ }
+
+ mutex_unlock(&core->lock);
+}
+
+static int decide_core(struct venus_inst *inst)
+{
+ const u32 ptype = HFI_PROPERTY_CONFIG_VIDEOCORES_USAGE;
+ struct venus_core *core = inst->core;
+ u32 min_coreid, min_load, inst_load;
+ struct hfi_videocores_usage_type cu;
+ unsigned long max_freq;
+
+ if (legacy_binding) {
+ if (inst->session_type == VIDC_SESSION_TYPE_DEC)
+ cu.video_core_enable_mask = VIDC_CORE_ID_1;
+ else
+ cu.video_core_enable_mask = VIDC_CORE_ID_2;
+
+ goto done;
+ }
+
+ if (inst->clk_data.core_id != VIDC_CORE_ID_DEFAULT)
+ return 0;
+
+ inst_load = load_per_instance(inst);
+ inst_load *= inst->clk_data.codec_freq_data->vpp_freq;
+ max_freq = core->res->freq_tbl[0].freq;
+
+ min_loaded_core(inst, &min_coreid, &min_load);
+
+ if ((inst_load + min_load) > max_freq) {
+ dev_warn(core->dev, "HW is overloaded, needed: %u max: %lu\n",
+ inst_load, max_freq);
+ return -EINVAL;
+ }
+
+ inst->clk_data.core_id = min_coreid;
+ cu.video_core_enable_mask = min_coreid;
+
+done:
+ return hfi_session_set_property(inst, ptype, &cu);
+}
+
+static int acquire_core(struct venus_inst *inst)
+{
+ struct venus_core *core = inst->core;
+ unsigned int coreid_mask = 0;
+
+ if (inst->core_acquired)
+ return 0;
+
+ inst->core_acquired = true;
+
+ if (inst->clk_data.core_id & VIDC_CORE_ID_1) {
+ if (core->core0_usage_count++)
+ return 0;
+
+ coreid_mask = VIDC_CORE_ID_1;
+ }
+
+ if (inst->clk_data.core_id & VIDC_CORE_ID_2) {
+ if (core->core1_usage_count++)
+ return 0;
+
+ coreid_mask |= VIDC_CORE_ID_2;
+ }
+
+ return poweron_coreid(core, coreid_mask);
+}
+
+static int release_core(struct venus_inst *inst)
+{
+ struct venus_core *core = inst->core;
+ unsigned int coreid_mask = 0;
+ int ret;
+
+ if (!inst->core_acquired)
+ return 0;
+
+ if (inst->clk_data.core_id & VIDC_CORE_ID_1) {
+ if (--core->core0_usage_count)
+ goto done;
+
+ coreid_mask = VIDC_CORE_ID_1;
+ }
+
+ if (inst->clk_data.core_id & VIDC_CORE_ID_2) {
+ if (--core->core1_usage_count)
+ goto done;
+
+ coreid_mask |= VIDC_CORE_ID_2;
+ }
+
+ ret = poweroff_coreid(core, coreid_mask);
+ if (ret)
+ return ret;
+
+done:
+ inst->clk_data.core_id = VIDC_CORE_ID_DEFAULT;
+ inst->core_acquired = false;
+ return 0;
+}
+
+static int coreid_power_v4(struct venus_inst *inst, int on)
+{
+ struct venus_core *core = inst->core;
+ int ret;
+
+ if (legacy_binding)
+ return 0;
+
+ if (on == POWER_ON) {
+ ret = decide_core(inst);
+ if (ret)
+ return ret;
+
+ mutex_lock(&core->lock);
+ ret = acquire_core(inst);
+ mutex_unlock(&core->lock);
+ } else {
+ mutex_lock(&core->lock);
+ ret = release_core(inst);
+ mutex_unlock(&core->lock);
+ }
+
+ return ret;
+}
+
+static int vdec_get_v4(struct device *dev)
+{
+ struct venus_core *core = dev_get_drvdata(dev);
+
+ if (!legacy_binding)
+ return 0;
+
+ return vcodec_clks_get(core, dev, core->vcodec0_clks,
+ core->res->vcodec0_clks);
+}
+
+static void vdec_put_v4(struct device *dev)
+{
+ struct venus_core *core = dev_get_drvdata(dev);
+ unsigned int i;
+
+ if (!legacy_binding)
+ return;
+
+ for (i = 0; i < core->res->vcodec_clks_num; i++)
+ core->vcodec0_clks[i] = NULL;
+}
+
+static int vdec_power_v4(struct device *dev, int on)
+{
+ struct venus_core *core = dev_get_drvdata(dev);
+ int ret;
+
+ if (!legacy_binding)
+ return 0;
+
+ ret = vcodec_control_v4(core, VIDC_CORE_ID_1, true);
+ if (ret)
+ return ret;
+
+ if (on == POWER_ON)
+ ret = vcodec_clks_enable(core, core->vcodec0_clks);
+ else
+ vcodec_clks_disable(core, core->vcodec0_clks);
+
+ vcodec_control_v4(core, VIDC_CORE_ID_1, false);
+
+ return ret;
+}
+
+static int venc_get_v4(struct device *dev)
+{
+ struct venus_core *core = dev_get_drvdata(dev);
+
+ if (!legacy_binding)
+ return 0;
+
+ return vcodec_clks_get(core, dev, core->vcodec1_clks,
+ core->res->vcodec1_clks);
+}
+
+static void venc_put_v4(struct device *dev)
+{
+ struct venus_core *core = dev_get_drvdata(dev);
+ unsigned int i;
+
+ if (!legacy_binding)
+ return;
+
+ for (i = 0; i < core->res->vcodec_clks_num; i++)
+ core->vcodec1_clks[i] = NULL;
+}
+
+static int venc_power_v4(struct device *dev, int on)
+{
+ struct venus_core *core = dev_get_drvdata(dev);
+ int ret;
+
+ if (!legacy_binding)
+ return 0;
+
+ ret = vcodec_control_v4(core, VIDC_CORE_ID_2, true);
+ if (ret)
+ return ret;
+
+ if (on == POWER_ON)
+ ret = vcodec_clks_enable(core, core->vcodec1_clks);
+ else
+ vcodec_clks_disable(core, core->vcodec1_clks);
+
+ vcodec_control_v4(core, VIDC_CORE_ID_2, false);
+
+ return ret;
+}
+
+static int vcodec_domains_get(struct venus_core *core)
+{
+ int ret;
+ struct opp_table *opp_table;
+ struct device **opp_virt_dev;
+ struct device *dev = core->dev;
+ const struct venus_resources *res = core->res;
+ struct device *pd;
+ unsigned int i;
+
+ if (!res->vcodec_pmdomains_num)
+ goto skip_pmdomains;
+
+ for (i = 0; i < res->vcodec_pmdomains_num; i++) {
+ pd = dev_pm_domain_attach_by_name(dev,
+ res->vcodec_pmdomains[i]);
+ if (IS_ERR(pd))
+ return PTR_ERR(pd);
+ core->pmdomains[i] = pd;
+ }
+
+skip_pmdomains:
+ if (!core->has_opp_table)
+ return 0;
+
+ /* Attach the power domain for setting performance state */
+ opp_table = dev_pm_opp_attach_genpd(dev, res->opp_pmdomain, &opp_virt_dev);
+ if (IS_ERR(opp_table)) {
+ ret = PTR_ERR(opp_table);
+ goto opp_attach_err;
+ }
+
+ core->opp_pmdomain = *opp_virt_dev;
+ core->opp_dl_venus = device_link_add(dev, core->opp_pmdomain,
+ DL_FLAG_RPM_ACTIVE |
+ DL_FLAG_PM_RUNTIME |
+ DL_FLAG_STATELESS);
+ if (!core->opp_dl_venus) {
+ ret = -ENODEV;
+ goto opp_dl_add_err;
+ }
+
+ return 0;
+
+opp_dl_add_err:
+ dev_pm_opp_detach_genpd(core->opp_table);
+opp_attach_err:
+ for (i = 0; i < res->vcodec_pmdomains_num; i++) {
+ if (IS_ERR_OR_NULL(core->pmdomains[i]))
+ continue;
+ dev_pm_domain_detach(core->pmdomains[i], true);
+ }
+
+ return ret;
+}
+
+static void vcodec_domains_put(struct venus_core *core)
+{
+ const struct venus_resources *res = core->res;
+ unsigned int i;
+
+ if (!res->vcodec_pmdomains_num)
+ goto skip_pmdomains;
+
+ for (i = 0; i < res->vcodec_pmdomains_num; i++) {
+ if (IS_ERR_OR_NULL(core->pmdomains[i]))
+ continue;
+ dev_pm_domain_detach(core->pmdomains[i], true);
+ }
+
+skip_pmdomains:
+ if (!core->has_opp_table)
+ return;
+
+ if (core->opp_dl_venus)
+ device_link_del(core->opp_dl_venus);
+
+ dev_pm_opp_detach_genpd(core->opp_table);
+}
+
+static int core_get_v4(struct venus_core *core)
+{
+ struct device *dev = core->dev;
+ const struct venus_resources *res = core->res;
+ int ret;
+
+ ret = core_clks_get(core);
+ if (ret)
+ return ret;
+
+ if (!res->vcodec_pmdomains_num)
+ legacy_binding = true;
+
+ dev_info(dev, "%s legacy binding\n", legacy_binding ? "" : "non");
+
+ ret = vcodec_clks_get(core, dev, core->vcodec0_clks, res->vcodec0_clks);
+ if (ret)
+ return ret;
+
+ ret = vcodec_clks_get(core, dev, core->vcodec1_clks, res->vcodec1_clks);
+ if (ret)
+ return ret;
+
+ if (legacy_binding)
+ return 0;
+
+ core->opp_table = dev_pm_opp_set_clkname(dev, "core");
+ if (IS_ERR(core->opp_table))
+ return PTR_ERR(core->opp_table);
+
+ if (core->res->opp_pmdomain) {
+ ret = dev_pm_opp_of_add_table(dev);
+ if (!ret) {
+ core->has_opp_table = true;
+ } else if (ret != -ENODEV) {
+ dev_err(dev, "invalid OPP table in device tree\n");
+ dev_pm_opp_put_clkname(core->opp_table);
+ return ret;
+ }
+ }
+
+ ret = vcodec_domains_get(core);
+ if (ret) {
+ if (core->has_opp_table)
+ dev_pm_opp_of_remove_table(dev);
+ dev_pm_opp_put_clkname(core->opp_table);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void core_put_v4(struct venus_core *core)
+{
+ struct device *dev = core->dev;
+
+ if (legacy_binding)
+ return;
+
+ vcodec_domains_put(core);
+
+ if (core->has_opp_table)
+ dev_pm_opp_of_remove_table(dev);
+ if (core->opp_table)
+ dev_pm_opp_put_clkname(core->opp_table);
+
+}
+
+static int core_power_v4(struct venus_core *core, int on)
+{
+ struct device *dev = core->dev;
+ struct device *pmctrl = core->pmdomains[0];
+ int ret = 0;
+
+ if (on == POWER_ON) {
+ if (pmctrl) {
+ ret = pm_runtime_get_sync(pmctrl);
+ if (ret < 0) {
+ pm_runtime_put_noidle(pmctrl);
+ return ret;
+ }
+ }
+
+ ret = core_clks_enable(core);
+ if (ret < 0 && pmctrl)
+ pm_runtime_put_sync(pmctrl);
+ } else {
+ /* Drop the performance state vote */
+ if (core->opp_pmdomain)
+ dev_pm_opp_set_rate(dev, 0);
+
+ core_clks_disable(core);
+
+ if (pmctrl)
+ pm_runtime_put_sync(pmctrl);
+ }
+
+ return ret;
+}
+
+static unsigned long calculate_inst_freq(struct venus_inst *inst,
+ unsigned long filled_len)
+{
+ unsigned long vpp_freq = 0, vsp_freq = 0;
+ u32 fps = (u32)inst->fps;
+ u32 mbs_per_sec;
+
+ mbs_per_sec = load_per_instance(inst) / fps;
+
+ vpp_freq = mbs_per_sec * inst->clk_data.codec_freq_data->vpp_freq;
+ /* 21 / 20 is overhead factor */
+ vpp_freq += vpp_freq / 20;
+ vsp_freq = mbs_per_sec * inst->clk_data.codec_freq_data->vsp_freq;
+
+ /* 10 / 7 is overhead factor */
+ if (inst->session_type == VIDC_SESSION_TYPE_ENC)
+ vsp_freq += (inst->controls.enc.bitrate * 10) / 7;
+ else
+ vsp_freq += ((fps * filled_len * 8) * 10) / 7;
+
+ return max(vpp_freq, vsp_freq);
+}
+
+static int load_scale_v4(struct venus_inst *inst)
+{
+ struct venus_core *core = inst->core;
+ const struct freq_tbl *table = core->res->freq_tbl;
+ unsigned int num_rows = core->res->freq_tbl_size;
+ struct device *dev = core->dev;
+ unsigned long freq = 0, freq_core1 = 0, freq_core2 = 0;
+ unsigned long filled_len = 0;
+ int i, ret = 0;
+
+ for (i = 0; i < inst->num_input_bufs; i++)
+ filled_len = max(filled_len, inst->payloads[i]);
+
+ if (inst->session_type == VIDC_SESSION_TYPE_DEC && !filled_len)
+ return ret;
+
+ freq = calculate_inst_freq(inst, filled_len);
+ inst->clk_data.freq = freq;
+
+ mutex_lock(&core->lock);
+ list_for_each_entry(inst, &core->instances, list) {
+ if (inst->clk_data.core_id == VIDC_CORE_ID_1) {
+ freq_core1 += inst->clk_data.freq;
+ } else if (inst->clk_data.core_id == VIDC_CORE_ID_2) {
+ freq_core2 += inst->clk_data.freq;
+ } else if (inst->clk_data.core_id == VIDC_CORE_ID_3) {
+ freq_core1 += inst->clk_data.freq;
+ freq_core2 += inst->clk_data.freq;
+ }
+ }
+
+ freq = max(freq_core1, freq_core2);
+
+ if (freq >= table[0].freq) {
+ freq = table[0].freq;
+ dev_warn(dev, "HW is overloaded, needed: %lu max: %lu\n",
+ freq, table[0].freq);
+ goto set_freq;
+ }
+
+ for (i = num_rows - 1 ; i >= 0; i--) {
+ if (freq <= table[i].freq) {
+ freq = table[i].freq;
+ break;
+ }
+ }
+
+set_freq:
+
+ ret = core_clks_set_rate(core, freq);
+ if (ret) {
+ dev_err(dev, "failed to set clock rate %lu (%d)\n",
+ freq, ret);
+ goto exit;
+ }
+
+ ret = load_scale_bw(core);
+ if (ret) {
+ dev_err(dev, "failed to set bandwidth (%d)\n",
+ ret);
+ goto exit;
+ }
+
+exit:
+ mutex_unlock(&core->lock);
+ return ret;
+}
+
+static const struct venus_pm_ops pm_ops_v4 = {
+ .core_get = core_get_v4,
+ .core_put = core_put_v4,
+ .core_power = core_power_v4,
+ .vdec_get = vdec_get_v4,
+ .vdec_put = vdec_put_v4,
+ .vdec_power = vdec_power_v4,
+ .venc_get = venc_get_v4,
+ .venc_put = venc_put_v4,
+ .venc_power = venc_power_v4,
+ .coreid_power = coreid_power_v4,
+ .load_scale = load_scale_v4,
+};
+
+const struct venus_pm_ops *venus_pm_get(enum hfi_version version)
+{
+ switch (version) {
+ case HFI_VERSION_1XX:
+ default:
+ return &pm_ops_v1;
+ case HFI_VERSION_3XX:
+ return &pm_ops_v3;
+ case HFI_VERSION_4XX:
+ return &pm_ops_v4;
+ }
+
+ return NULL;
+}
diff --git a/drivers/media/platform/qcom/venus/pm_helpers.h b/drivers/media/platform/qcom/venus/pm_helpers.h
new file mode 100644
index 0000000..a492c50
--- /dev/null
+++ b/drivers/media/platform/qcom/venus/pm_helpers.h
@@ -0,0 +1,66 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright (C) 2019 Linaro Ltd. */
+#ifndef __VENUS_PM_HELPERS_H__
+#define __VENUS_PM_HELPERS_H__
+
+struct device;
+struct venus_core;
+
+#define POWER_ON 1
+#define POWER_OFF 0
+
+struct venus_pm_ops {
+ int (*core_get)(struct venus_core *core);
+ void (*core_put)(struct venus_core *core);
+ int (*core_power)(struct venus_core *core, int on);
+
+ int (*vdec_get)(struct device *dev);
+ void (*vdec_put)(struct device *dev);
+ int (*vdec_power)(struct device *dev, int on);
+
+ int (*venc_get)(struct device *dev);
+ void (*venc_put)(struct device *dev);
+ int (*venc_power)(struct device *dev, int on);
+
+ int (*coreid_power)(struct venus_inst *inst, int on);
+
+ int (*load_scale)(struct venus_inst *inst);
+};
+
+const struct venus_pm_ops *venus_pm_get(enum hfi_version version);
+
+static inline int venus_pm_load_scale(struct venus_inst *inst)
+{
+ struct venus_core *core = inst->core;
+
+ if (!core->pm_ops || !core->pm_ops->load_scale)
+ return 0;
+
+ return core->pm_ops->load_scale(inst);
+}
+
+static inline int venus_pm_acquire_core(struct venus_inst *inst)
+{
+ struct venus_core *core = inst->core;
+ const struct venus_pm_ops *pm_ops = core->pm_ops;
+ int ret = 0;
+
+ if (pm_ops && pm_ops->coreid_power)
+ ret = pm_ops->coreid_power(inst, POWER_ON);
+
+ return ret;
+}
+
+static inline int venus_pm_release_core(struct venus_inst *inst)
+{
+ struct venus_core *core = inst->core;
+ const struct venus_pm_ops *pm_ops = core->pm_ops;
+ int ret = 0;
+
+ if (pm_ops && pm_ops->coreid_power)
+ ret = pm_ops->coreid_power(inst, POWER_OFF);
+
+ return ret;
+}
+
+#endif
diff --git a/drivers/media/platform/qcom/venus/vdec.c b/drivers/media/platform/qcom/venus/vdec.c
index 658825b..ea13170 100644
--- a/drivers/media/platform/qcom/venus/vdec.c
+++ b/drivers/media/platform/qcom/venus/vdec.c
@@ -20,6 +20,7 @@
#include "core.h"
#include "helpers.h"
#include "vdec.h"
+#include "pm_helpers.h"
/*
* Three resons to keep MPLANE formats (despite that the number of planes
@@ -224,7 +225,7 @@
if (!(inst->codec_state == VENUS_DEC_STATE_CAPTURE_SETUP) ||
!inst->reconfig)
- dev_dbg(inst->core->dev, "%s: wrong state\n", __func__);
+ dev_dbg(inst->core->dev, VDBGH "wrong state\n");
done:
return 0;
@@ -275,6 +276,14 @@
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;
@@ -544,6 +553,64 @@
.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;
@@ -578,10 +645,6 @@
if (ret)
return ret;
- ret = venus_helper_set_core_usage(inst, VIDC_CORE_ID_1);
- if (ret)
- return ret;
-
if (core->res->hfi_version == HFI_VERSION_1XX) {
ptype = HFI_PROPERTY_PARAM_VDEC_CONTINUE_DATA_TRANSFER;
ret = hfi_session_set_property(inst, ptype, &en);
@@ -685,6 +748,10 @@
if (ret)
goto deinit;
+ ret = venus_helper_init_codec_freq_data(inst);
+ if (ret)
+ goto deinit;
+
return 0;
deinit:
hfi_session_deinit(inst);
@@ -745,12 +812,20 @@
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) {
@@ -785,6 +860,10 @@
}
return ret;
+
+put_power:
+ vdec_pm_put(inst, false);
+ return ret;
}
static int vdec_verify_conf(struct venus_inst *inst)
@@ -835,7 +914,7 @@
return 0;
reconfigure:
- ret = hfi_session_flush(inst, HFI_FLUSH_OUTPUT);
+ ret = hfi_session_flush(inst, HFI_FLUSH_OUTPUT, true);
if (ret)
return ret;
@@ -864,7 +943,7 @@
if (ret)
goto free_dpb_bufs;
- venus_helper_load_scale_clocks(inst->core);
+ venus_pm_load_scale(inst);
ret = hfi_session_continue(inst);
if (ret)
@@ -946,10 +1025,23 @@
mutex_lock(&inst->lock);
- 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;
@@ -957,8 +1049,10 @@
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;
}
@@ -977,15 +1071,16 @@
switch (inst->codec_state) {
case VENUS_DEC_STATE_DECODING:
- ret = hfi_session_flush(inst, HFI_FLUSH_ALL);
- /* fallthrough */
+ 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;
venus_helper_free_dpb_bufs(inst);
break;
@@ -1004,12 +1099,12 @@
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;
@@ -1030,7 +1125,7 @@
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;
@@ -1049,8 +1144,9 @@
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);
@@ -1071,10 +1167,12 @@
hfi_session_abort(inst);
venus_helper_free_dpb_bufs(inst);
- venus_helper_load_scale_clocks(core);
+ venus_pm_load_scale(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)
@@ -1103,6 +1201,15 @@
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,
@@ -1110,7 +1217,7 @@
.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,
@@ -1122,6 +1229,8 @@
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
@@ -1141,6 +1250,13 @@
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 };
@@ -1149,6 +1265,9 @@
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++;
}
@@ -1194,7 +1313,10 @@
inst->out_width = ev_data->width;
inst->out_height = ev_data->height;
- dev_dbg(dev, "event %s sufficient resources (%ux%u)\n",
+ if (inst->bit_depth != ev_data->bit_depth)
+ inst->bit_depth = ev_data->bit_depth;
+
+ dev_dbg(dev, VDBGM "event %s sufficient resources (%ux%u)\n",
sufficient ? "" : "not", ev_data->width, ev_data->height);
if (sufficient) {
@@ -1212,6 +1334,25 @@
}
}
+ /*
+ * 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, VDBGH "flush output error %d\n", ret);
+ }
+
inst->reconfig = true;
v4l2_event_queue_fh(&inst->fh, &ev);
wake_up(&inst->reconf_wait);
@@ -1225,6 +1366,8 @@
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;
@@ -1250,9 +1393,16 @@
}
}
+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)
@@ -1309,13 +1459,7 @@
dst_vq->allow_zero_bytesused = 1;
dst_vq->min_buffers_needed = 0;
dst_vq->dev = inst->core->dev;
- ret = vb2_queue_init(dst_vq);
- if (ret) {
- vb2_queue_release(src_vq);
- return ret;
- }
-
- return 0;
+ return vb2_queue_init(dst_vq);
}
static int vdec_open(struct file *file)
@@ -1339,16 +1483,15 @@
inst->num_output_bufs = 1;
inst->codec_state = VENUS_DEC_STATE_DEINIT;
inst->buf_count = 0;
+ inst->clk_data.core_id = VIDC_CORE_ID_DEFAULT;
+ inst->core_acquired = false;
+ inst->bit_depth = VIDC_BITDEPTH_8;
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)
@@ -1387,9 +1530,7 @@
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;
}
@@ -1398,6 +1539,8 @@
{
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);
@@ -1406,7 +1549,7 @@
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;
@@ -1435,20 +1578,14 @@
if (!core)
return -EPROBE_DEFER;
- if (IS_V3(core) || IS_V4(core)) {
- core->core0_clk = devm_clk_get(dev, "core");
- if (IS_ERR(core->core0_clk))
- return PTR_ERR(core->core0_clk);
- }
-
- if (IS_V4(core)) {
- core->core0_bus_clk = devm_clk_get(dev, "bus");
- if (IS_ERR(core->core0_bus_clk))
- return PTR_ERR(core->core0_bus_clk);
- }
-
platform_set_drvdata(pdev, core);
+ if (core->pm_ops->vdec_get) {
+ ret = core->pm_ops->vdec_get(dev);
+ if (ret)
+ return ret;
+ }
+
vdev = video_device_alloc();
if (!vdev)
return -ENOMEM;
@@ -1461,7 +1598,7 @@
vdev->v4l2_dev = &core->v4l2_dev;
vdev->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING;
- ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
+ ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
if (ret)
goto err_vdev_release;
@@ -1469,6 +1606,8 @@
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;
@@ -1485,57 +1624,33 @@
video_unregister_device(core->vdev_dec);
pm_runtime_disable(core->dev_dec);
+ if (core->pm_ops->vdec_put)
+ core->pm_ops->vdec_put(core->dev_dec);
+
return 0;
}
static __maybe_unused int vdec_runtime_suspend(struct device *dev)
{
struct venus_core *core = dev_get_drvdata(dev);
- int ret;
+ const struct venus_pm_ops *pm_ops = core->pm_ops;
+ int ret = 0;
- if (IS_V1(core))
- return 0;
+ if (pm_ops->vdec_power)
+ ret = pm_ops->vdec_power(dev, POWER_OFF);
- ret = venus_helper_power_enable(core, VIDC_SESSION_TYPE_DEC, true);
- if (ret)
- return ret;
-
- if (IS_V4(core))
- clk_disable_unprepare(core->core0_bus_clk);
-
- clk_disable_unprepare(core->core0_clk);
-
- return venus_helper_power_enable(core, VIDC_SESSION_TYPE_DEC, false);
+ return ret;
}
static __maybe_unused int vdec_runtime_resume(struct device *dev)
{
struct venus_core *core = dev_get_drvdata(dev);
- int ret;
+ const struct venus_pm_ops *pm_ops = core->pm_ops;
+ int ret = 0;
- if (IS_V1(core))
- return 0;
+ if (pm_ops->vdec_power)
+ ret = pm_ops->vdec_power(dev, POWER_ON);
- ret = venus_helper_power_enable(core, VIDC_SESSION_TYPE_DEC, true);
- if (ret)
- return ret;
-
- ret = clk_prepare_enable(core->core0_clk);
- if (ret)
- goto err_power_disable;
-
- if (IS_V4(core))
- ret = clk_prepare_enable(core->core0_bus_clk);
-
- if (ret)
- goto err_unprepare_core0;
-
- return venus_helper_power_enable(core, VIDC_SESSION_TYPE_DEC, false);
-
-err_unprepare_core0:
- clk_disable_unprepare(core->core0_clk);
-err_power_disable:
- venus_helper_power_enable(core, VIDC_SESSION_TYPE_DEC, false);
return ret;
}
diff --git a/drivers/media/platform/qcom/venus/vdec_ctrls.c b/drivers/media/platform/qcom/venus/vdec_ctrls.c
index 3a963cb..974110b 100644
--- a/drivers/media/platform/qcom/venus/vdec_ctrls.c
+++ b/drivers/media/platform/qcom/venus/vdec_ctrls.c
@@ -22,10 +22,12 @@
case V4L2_CID_MPEG_VIDEO_H264_PROFILE:
case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE:
case V4L2_CID_MPEG_VIDEO_VP8_PROFILE:
+ case V4L2_CID_MPEG_VIDEO_VP9_PROFILE:
ctr->profile = ctrl->val;
break;
case V4L2_CID_MPEG_VIDEO_H264_LEVEL:
case V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL:
+ case V4L2_CID_MPEG_VIDEO_VP9_LEVEL:
ctr->level = ctrl->val;
break;
default:
@@ -40,25 +42,26 @@
struct venus_inst *inst = ctrl_to_inst(ctrl);
struct vdec_controls *ctr = &inst->controls.dec;
struct hfi_buffer_requirements bufreq;
- union hfi_get_property hprop;
enum hfi_version ver = inst->core->res->hfi_version;
- u32 ptype = HFI_PROPERTY_PARAM_PROFILE_LEVEL_CURRENT;
+ u32 profile, level;
int ret;
switch (ctrl->id) {
case V4L2_CID_MPEG_VIDEO_H264_PROFILE:
case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE:
case V4L2_CID_MPEG_VIDEO_VP8_PROFILE:
- ret = hfi_session_get_property(inst, ptype, &hprop);
+ case V4L2_CID_MPEG_VIDEO_VP9_PROFILE:
+ ret = venus_helper_get_profile_level(inst, &profile, &level);
if (!ret)
- ctr->profile = hprop.profile_level.profile;
+ ctr->profile = profile;
ctrl->val = ctr->profile;
break;
case V4L2_CID_MPEG_VIDEO_H264_LEVEL:
case V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL:
- ret = hfi_session_get_property(inst, ptype, &hprop);
+ case V4L2_CID_MPEG_VIDEO_VP9_LEVEL:
+ ret = venus_helper_get_profile_level(inst, &profile, &level);
if (!ret)
- ctr->level = hprop.profile_level.level;
+ ctr->level = level;
ctrl->val = ctr->level;
break;
case V4L2_CID_MPEG_VIDEO_DECODER_MPEG4_DEBLOCK_FILTER:
@@ -86,7 +89,7 @@
struct v4l2_ctrl *ctrl;
int ret;
- ret = v4l2_ctrl_handler_init(&inst->ctrl_handler, 7);
+ ret = v4l2_ctrl_handler_init(&inst->ctrl_handler, 9);
if (ret)
return ret;
@@ -133,6 +136,20 @@
if (ctrl)
ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
+ ctrl = v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &vdec_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_VP9_PROFILE,
+ V4L2_MPEG_VIDEO_VP9_PROFILE_3,
+ 0, V4L2_MPEG_VIDEO_VP9_PROFILE_0);
+ if (ctrl)
+ ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
+
+ ctrl = v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &vdec_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_VP9_LEVEL,
+ V4L2_MPEG_VIDEO_VP9_LEVEL_6_2,
+ 0, V4L2_MPEG_VIDEO_VP9_LEVEL_1_0);
+ if (ctrl)
+ ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
+
v4l2_ctrl_new_std(&inst->ctrl_handler, &vdec_ctrl_ops,
V4L2_CID_MPEG_VIDEO_DECODER_MPEG4_DEBLOCK_FILTER, 0, 1, 1, 0);
diff --git a/drivers/media/platform/qcom/venus/venc.c b/drivers/media/platform/qcom/venus/venc.c
index 766ca49..e2d0fd5 100644
--- a/drivers/media/platform/qcom/venus/venc.c
+++ b/drivers/media/platform/qcom/venus/venc.c
@@ -20,6 +20,7 @@
#include "core.h"
#include "helpers.h"
#include "venc.h"
+#include "pm_helpers.h"
#define NUM_B_FRAMES_MAX 4
@@ -112,80 +113,6 @@
static int venc_v4l2_to_hfi(int id, int value)
{
switch (id) {
- case V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL:
- switch (value) {
- case V4L2_MPEG_VIDEO_MPEG4_LEVEL_0:
- default:
- return HFI_MPEG4_LEVEL_0;
- case V4L2_MPEG_VIDEO_MPEG4_LEVEL_0B:
- return HFI_MPEG4_LEVEL_0b;
- case V4L2_MPEG_VIDEO_MPEG4_LEVEL_1:
- return HFI_MPEG4_LEVEL_1;
- case V4L2_MPEG_VIDEO_MPEG4_LEVEL_2:
- return HFI_MPEG4_LEVEL_2;
- case V4L2_MPEG_VIDEO_MPEG4_LEVEL_3:
- return HFI_MPEG4_LEVEL_3;
- case V4L2_MPEG_VIDEO_MPEG4_LEVEL_4:
- return HFI_MPEG4_LEVEL_4;
- case V4L2_MPEG_VIDEO_MPEG4_LEVEL_5:
- return HFI_MPEG4_LEVEL_5;
- }
- case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE:
- switch (value) {
- case V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE:
- default:
- return HFI_MPEG4_PROFILE_SIMPLE;
- case V4L2_MPEG_VIDEO_MPEG4_PROFILE_ADVANCED_SIMPLE:
- return HFI_MPEG4_PROFILE_ADVANCEDSIMPLE;
- }
- case V4L2_CID_MPEG_VIDEO_H264_PROFILE:
- switch (value) {
- case V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE:
- return HFI_H264_PROFILE_BASELINE;
- case V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE:
- return HFI_H264_PROFILE_CONSTRAINED_BASE;
- case V4L2_MPEG_VIDEO_H264_PROFILE_MAIN:
- return HFI_H264_PROFILE_MAIN;
- case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH:
- default:
- return HFI_H264_PROFILE_HIGH;
- }
- case V4L2_CID_MPEG_VIDEO_H264_LEVEL:
- switch (value) {
- case V4L2_MPEG_VIDEO_H264_LEVEL_1_0:
- return HFI_H264_LEVEL_1;
- case V4L2_MPEG_VIDEO_H264_LEVEL_1B:
- return HFI_H264_LEVEL_1b;
- case V4L2_MPEG_VIDEO_H264_LEVEL_1_1:
- return HFI_H264_LEVEL_11;
- case V4L2_MPEG_VIDEO_H264_LEVEL_1_2:
- return HFI_H264_LEVEL_12;
- case V4L2_MPEG_VIDEO_H264_LEVEL_1_3:
- return HFI_H264_LEVEL_13;
- case V4L2_MPEG_VIDEO_H264_LEVEL_2_0:
- return HFI_H264_LEVEL_2;
- case V4L2_MPEG_VIDEO_H264_LEVEL_2_1:
- return HFI_H264_LEVEL_21;
- case V4L2_MPEG_VIDEO_H264_LEVEL_2_2:
- return HFI_H264_LEVEL_22;
- case V4L2_MPEG_VIDEO_H264_LEVEL_3_0:
- return HFI_H264_LEVEL_3;
- case V4L2_MPEG_VIDEO_H264_LEVEL_3_1:
- return HFI_H264_LEVEL_31;
- case V4L2_MPEG_VIDEO_H264_LEVEL_3_2:
- return HFI_H264_LEVEL_32;
- case V4L2_MPEG_VIDEO_H264_LEVEL_4_0:
- return HFI_H264_LEVEL_4;
- case V4L2_MPEG_VIDEO_H264_LEVEL_4_1:
- return HFI_H264_LEVEL_41;
- case V4L2_MPEG_VIDEO_H264_LEVEL_4_2:
- return HFI_H264_LEVEL_42;
- case V4L2_MPEG_VIDEO_H264_LEVEL_5_0:
- default:
- return HFI_H264_LEVEL_5;
- case V4L2_MPEG_VIDEO_H264_LEVEL_5_1:
- return HFI_H264_LEVEL_51;
- }
case V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE:
switch (value) {
case V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC:
@@ -194,18 +121,6 @@
case V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC:
return HFI_H264_ENTROPY_CABAC;
}
- case V4L2_CID_MPEG_VIDEO_VP8_PROFILE:
- switch (value) {
- case 0:
- default:
- return HFI_VPX_PROFILE_VERSION_0;
- case 1:
- return HFI_VPX_PROFILE_VERSION_1;
- case 2:
- return HFI_VPX_PROFILE_VERSION_2;
- case 3:
- return HFI_VPX_PROFILE_VERSION_3;
- }
case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE:
switch (value) {
case V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_ENABLED:
@@ -216,46 +131,6 @@
case V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY:
return HFI_H264_DB_MODE_SKIP_SLICE_BOUNDARY;
}
- case V4L2_CID_MPEG_VIDEO_HEVC_PROFILE:
- switch (value) {
- case V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN:
- default:
- return HFI_HEVC_PROFILE_MAIN;
- case V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE:
- return HFI_HEVC_PROFILE_MAIN_STILL_PIC;
- case V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10:
- return HFI_HEVC_PROFILE_MAIN10;
- }
- case V4L2_CID_MPEG_VIDEO_HEVC_LEVEL:
- switch (value) {
- case V4L2_MPEG_VIDEO_HEVC_LEVEL_1:
- default:
- return HFI_HEVC_LEVEL_1;
- case V4L2_MPEG_VIDEO_HEVC_LEVEL_2:
- return HFI_HEVC_LEVEL_2;
- case V4L2_MPEG_VIDEO_HEVC_LEVEL_2_1:
- return HFI_HEVC_LEVEL_21;
- case V4L2_MPEG_VIDEO_HEVC_LEVEL_3:
- return HFI_HEVC_LEVEL_3;
- case V4L2_MPEG_VIDEO_HEVC_LEVEL_3_1:
- return HFI_HEVC_LEVEL_31;
- case V4L2_MPEG_VIDEO_HEVC_LEVEL_4:
- return HFI_HEVC_LEVEL_4;
- case V4L2_MPEG_VIDEO_HEVC_LEVEL_4_1:
- return HFI_HEVC_LEVEL_41;
- case V4L2_MPEG_VIDEO_HEVC_LEVEL_5:
- return HFI_HEVC_LEVEL_5;
- case V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1:
- return HFI_HEVC_LEVEL_51;
- case V4L2_MPEG_VIDEO_HEVC_LEVEL_5_2:
- return HFI_HEVC_LEVEL_52;
- case V4L2_MPEG_VIDEO_HEVC_LEVEL_6:
- return HFI_HEVC_LEVEL_6;
- case V4L2_MPEG_VIDEO_HEVC_LEVEL_6_1:
- return HFI_HEVC_LEVEL_61;
- case V4L2_MPEG_VIDEO_HEVC_LEVEL_6_2:
- return HFI_HEVC_LEVEL_62;
- }
}
return 0;
@@ -358,6 +233,14 @@
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;
@@ -577,6 +460,7 @@
{
struct venus_inst *inst = to_inst(file);
const struct venus_format *fmt;
+ unsigned int framerate_factor = 1;
fival->type = V4L2_FRMIVAL_TYPE_STEPWISE;
@@ -601,12 +485,17 @@
fival->height < frame_height_min(inst))
return -EINVAL;
+ if (IS_V1(inst->core)) {
+ /* framerate is reported in 1/65535 fps unit */
+ framerate_factor = (1 << 16);
+ }
+
fival->stepwise.min.numerator = 1;
- fival->stepwise.min.denominator = frate_max(inst);
+ fival->stepwise.min.denominator = frate_max(inst) / framerate_factor;
fival->stepwise.max.numerator = 1;
- fival->stepwise.max.denominator = frate_min(inst);
+ fival->stepwise.max.denominator = frate_min(inst) / framerate_factor;
fival->stepwise.step.numerator = 1;
- fival->stepwise.step.denominator = frate_max(inst);
+ fival->stepwise.step.denominator = frate_max(inst) / framerate_factor;
return 0;
}
@@ -644,23 +533,19 @@
{
struct venc_controls *ctr = &inst->controls.enc;
struct hfi_intra_period intra_period;
- struct hfi_profile_level pl;
struct hfi_framerate frate;
struct hfi_bitrate brate;
struct hfi_idr_period idrp;
struct hfi_quantization quant;
struct hfi_quantization_range quant_range;
- u32 ptype, rate_control, bitrate, profile = 0, level = 0;
+ u32 ptype, rate_control, bitrate;
+ u32 profile, level;
int ret;
ret = venus_helper_set_work_mode(inst, VIDC_WORK_MODE_2);
if (ret)
return ret;
- ret = venus_helper_set_core_usage(inst, VIDC_CORE_ID_2);
- if (ret)
- return ret;
-
ptype = HFI_PROPERTY_CONFIG_FRAME_RATE;
frate.buffer_type = HFI_BUFFER_OUTPUT;
frate.framerate = inst->fps * (1 << 16);
@@ -733,16 +618,32 @@
if (ret)
return ret;
- if (ctr->bitrate_mode == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR)
- rate_control = HFI_RATE_CONTROL_VBR_CFR;
- else
- rate_control = HFI_RATE_CONTROL_CBR_CFR;
+ if (!ctr->rc_enable)
+ rate_control = HFI_RATE_CONTROL_OFF;
+ else if (ctr->bitrate_mode == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR)
+ rate_control = ctr->frame_skip_mode ? HFI_RATE_CONTROL_VBR_VFR :
+ HFI_RATE_CONTROL_VBR_CFR;
+ else if (ctr->bitrate_mode == V4L2_MPEG_VIDEO_BITRATE_MODE_CBR)
+ rate_control = ctr->frame_skip_mode ? HFI_RATE_CONTROL_CBR_VFR :
+ HFI_RATE_CONTROL_CBR_CFR;
+ else if (ctr->bitrate_mode == V4L2_MPEG_VIDEO_BITRATE_MODE_CQ)
+ rate_control = HFI_RATE_CONTROL_CQ;
ptype = HFI_PROPERTY_PARAM_VENC_RATE_CONTROL;
ret = hfi_session_set_property(inst, ptype, &rate_control);
if (ret)
return ret;
+ if (rate_control == HFI_RATE_CONTROL_CQ && ctr->const_quality) {
+ struct hfi_heic_frame_quality quality = {};
+
+ ptype = HFI_PROPERTY_CONFIG_HEIC_FRAME_QUALITY;
+ quality.frame_quality = ctr->const_quality;
+ ret = hfi_session_set_property(inst, ptype, &quality);
+ if (ret)
+ return ret;
+ }
+
if (!ctr->bitrate)
bitrate = 64000;
else
@@ -786,35 +687,35 @@
if (ret)
return ret;
- if (inst->fmt_cap->pixfmt == V4L2_PIX_FMT_H264) {
- profile = venc_v4l2_to_hfi(V4L2_CID_MPEG_VIDEO_H264_PROFILE,
- ctr->profile.h264);
- level = venc_v4l2_to_hfi(V4L2_CID_MPEG_VIDEO_H264_LEVEL,
- ctr->level.h264);
- } else if (inst->fmt_cap->pixfmt == V4L2_PIX_FMT_VP8) {
- profile = venc_v4l2_to_hfi(V4L2_CID_MPEG_VIDEO_VP8_PROFILE,
- ctr->profile.vpx);
+ switch (inst->hfi_codec) {
+ case HFI_VIDEO_CODEC_H264:
+ profile = ctr->profile.h264;
+ level = ctr->level.h264;
+ break;
+ case HFI_VIDEO_CODEC_MPEG4:
+ profile = ctr->profile.mpeg4;
+ level = ctr->level.mpeg4;
+ break;
+ case HFI_VIDEO_CODEC_VP8:
+ profile = ctr->profile.vp8;
level = 0;
- } else if (inst->fmt_cap->pixfmt == V4L2_PIX_FMT_MPEG4) {
- profile = venc_v4l2_to_hfi(V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE,
- ctr->profile.mpeg4);
- level = venc_v4l2_to_hfi(V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL,
- ctr->level.mpeg4);
- } else if (inst->fmt_cap->pixfmt == V4L2_PIX_FMT_H263) {
+ break;
+ case HFI_VIDEO_CODEC_VP9:
+ profile = ctr->profile.vp9;
+ level = ctr->level.vp9;
+ break;
+ case HFI_VIDEO_CODEC_HEVC:
+ profile = ctr->profile.hevc;
+ level = ctr->level.hevc;
+ break;
+ case HFI_VIDEO_CODEC_MPEG2:
+ default:
profile = 0;
level = 0;
- } else if (inst->fmt_cap->pixfmt == V4L2_PIX_FMT_HEVC) {
- profile = venc_v4l2_to_hfi(V4L2_CID_MPEG_VIDEO_HEVC_PROFILE,
- ctr->profile.hevc);
- level = venc_v4l2_to_hfi(V4L2_CID_MPEG_VIDEO_HEVC_LEVEL,
- ctr->level.hevc);
+ break;
}
- ptype = HFI_PROPERTY_PARAM_PROFILE_LEVEL_CURRENT;
- pl.profile = profile;
- pl.level = level;
-
- ret = hfi_session_set_property(inst, ptype, &pl);
+ ret = venus_helper_set_profile_level(inst, profile, level);
if (ret)
return ret;
@@ -844,6 +745,10 @@
if (ret)
goto deinit;
+ ret = venus_helper_init_codec_freq_data(inst);
+ if (ret)
+ goto deinit;
+
ret = venc_set_properties(inst);
if (ret)
goto deinit;
@@ -989,6 +894,10 @@
if (ret)
goto bufs_done;
+ ret = venus_pm_acquire_core(inst);
+ if (ret)
+ goto deinit_sess;
+
ret = venc_set_properties(inst);
if (ret)
goto deinit_sess;
@@ -1013,7 +922,7 @@
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
@@ -1116,13 +1025,7 @@
dst_vq->allow_zero_bytesused = 1;
dst_vq->min_buffers_needed = 1;
dst_vq->dev = inst->core->dev;
- ret = vb2_queue_init(dst_vq);
- if (ret) {
- vb2_queue_release(src_vq);
- return ret;
- }
-
- return 0;
+ return vb2_queue_init(dst_vq);
}
static void venc_inst_init(struct venus_inst *inst)
@@ -1157,12 +1060,14 @@
inst->core = core;
inst->session_type = VIDC_SESSION_TYPE_ENC;
+ inst->clk_data.core_id = VIDC_CORE_ID_DEFAULT;
+ inst->core_acquired = false;
venus_helper_init_instance(inst);
ret = pm_runtime_get_sync(core->dev_enc);
if (ret < 0)
- goto err_free_inst;
+ goto err_put_sync;
ret = venc_ctrl_init(inst);
if (ret)
@@ -1207,7 +1112,6 @@
venc_ctrl_deinit(inst);
err_put_sync:
pm_runtime_put_sync(core->dev_enc);
-err_free_inst:
kfree(inst);
return ret;
}
@@ -1253,20 +1157,14 @@
if (!core)
return -EPROBE_DEFER;
- if (IS_V3(core) || IS_V4(core)) {
- core->core1_clk = devm_clk_get(dev, "core");
- if (IS_ERR(core->core1_clk))
- return PTR_ERR(core->core1_clk);
- }
-
- if (IS_V4(core)) {
- core->core1_bus_clk = devm_clk_get(dev, "bus");
- if (IS_ERR(core->core1_bus_clk))
- return PTR_ERR(core->core1_bus_clk);
- }
-
platform_set_drvdata(pdev, core);
+ if (core->pm_ops->venc_get) {
+ ret = core->pm_ops->venc_get(dev);
+ if (ret)
+ return ret;
+ }
+
vdev = video_device_alloc();
if (!vdev)
return -ENOMEM;
@@ -1279,7 +1177,7 @@
vdev->v4l2_dev = &core->v4l2_dev;
vdev->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING;
- ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
+ ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
if (ret)
goto err_vdev_release;
@@ -1303,57 +1201,33 @@
video_unregister_device(core->vdev_enc);
pm_runtime_disable(core->dev_enc);
+ if (core->pm_ops->venc_put)
+ core->pm_ops->venc_put(core->dev_enc);
+
return 0;
}
static __maybe_unused int venc_runtime_suspend(struct device *dev)
{
struct venus_core *core = dev_get_drvdata(dev);
- int ret;
+ const struct venus_pm_ops *pm_ops = core->pm_ops;
+ int ret = 0;
- if (IS_V1(core))
- return 0;
+ if (pm_ops->venc_power)
+ ret = pm_ops->venc_power(dev, POWER_OFF);
- ret = venus_helper_power_enable(core, VIDC_SESSION_TYPE_ENC, true);
- if (ret)
- return ret;
-
- if (IS_V4(core))
- clk_disable_unprepare(core->core1_bus_clk);
-
- clk_disable_unprepare(core->core1_clk);
-
- return venus_helper_power_enable(core, VIDC_SESSION_TYPE_ENC, false);
+ return ret;
}
static __maybe_unused int venc_runtime_resume(struct device *dev)
{
struct venus_core *core = dev_get_drvdata(dev);
- int ret;
+ const struct venus_pm_ops *pm_ops = core->pm_ops;
+ int ret = 0;
- if (IS_V1(core))
- return 0;
+ if (pm_ops->venc_power)
+ ret = pm_ops->venc_power(dev, POWER_ON);
- ret = venus_helper_power_enable(core, VIDC_SESSION_TYPE_ENC, true);
- if (ret)
- return ret;
-
- ret = clk_prepare_enable(core->core1_clk);
- if (ret)
- goto err_power_disable;
-
- if (IS_V4(core))
- ret = clk_prepare_enable(core->core1_bus_clk);
-
- if (ret)
- goto err_unprepare_core1;
-
- return venus_helper_power_enable(core, VIDC_SESSION_TYPE_ENC, false);
-
-err_unprepare_core1:
- clk_disable_unprepare(core->core1_clk);
-err_power_disable:
- venus_helper_power_enable(core, VIDC_SESSION_TYPE_ENC, false);
return ret;
}
diff --git a/drivers/media/platform/qcom/venus/venc_ctrls.c b/drivers/media/platform/qcom/venus/venc_ctrls.c
index 877c0b3..cf860e6 100644
--- a/drivers/media/platform/qcom/venus/venc_ctrls.c
+++ b/drivers/media/platform/qcom/venus/venc_ctrls.c
@@ -112,7 +112,7 @@
ctr->profile.hevc = ctrl->val;
break;
case V4L2_CID_MPEG_VIDEO_VP8_PROFILE:
- ctr->profile.vpx = ctrl->val;
+ ctr->profile.vp8 = ctrl->val;
break;
case V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL:
ctr->level.mpeg4 = ctrl->val;
@@ -199,6 +199,15 @@
}
mutex_unlock(&inst->lock);
break;
+ case V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE:
+ ctr->rc_enable = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_CONSTANT_QUALITY:
+ ctr->const_quality = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_FRAME_SKIP_MODE:
+ ctr->frame_skip_mode = ctrl->val;
+ break;
default:
return -EINVAL;
}
@@ -214,7 +223,7 @@
{
int ret;
- ret = v4l2_ctrl_handler_init(&inst->ctrl_handler, 30);
+ ret = v4l2_ctrl_handler_init(&inst->ctrl_handler, 33);
if (ret)
return ret;
@@ -222,7 +231,8 @@
V4L2_CID_MPEG_VIDEO_BITRATE_MODE,
V4L2_MPEG_VIDEO_BITRATE_MODE_CBR,
~((1 << V4L2_MPEG_VIDEO_BITRATE_MODE_VBR) |
- (1 << V4L2_MPEG_VIDEO_BITRATE_MODE_CBR)),
+ (1 << V4L2_MPEG_VIDEO_BITRATE_MODE_CBR) |
+ (1 << V4L2_MPEG_VIDEO_BITRATE_MODE_CQ)),
V4L2_MPEG_VIDEO_BITRATE_MODE_VBR);
v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops,
@@ -351,6 +361,19 @@
v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME, 0, 0, 0, 0);
+ v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE, 0, 1, 1, 1);
+
+ v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_CONSTANT_QUALITY, 0, 100, 1, 0);
+
+ v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_FRAME_SKIP_MODE,
+ V4L2_MPEG_VIDEO_FRAME_SKIP_MODE_BUF_LIMIT,
+ ~((1 << V4L2_MPEG_VIDEO_FRAME_SKIP_MODE_DISABLED) |
+ (1 << V4L2_MPEG_VIDEO_FRAME_SKIP_MODE_BUF_LIMIT)),
+ V4L2_MPEG_VIDEO_FRAME_SKIP_MODE_DISABLED);
+
ret = inst->ctrl_handler.error;
if (ret)
goto err;
diff --git a/drivers/media/platform/rcar-fcp.c b/drivers/media/platform/rcar-fcp.c
index 05c712e..5c03318 100644
--- a/drivers/media/platform/rcar-fcp.c
+++ b/drivers/media/platform/rcar-fcp.c
@@ -22,7 +22,6 @@
struct rcar_fcp_device {
struct list_head list;
struct device *dev;
- struct device_dma_parameters dma_parms;
};
static LIST_HEAD(fcp_devices);
@@ -140,8 +139,7 @@
fcp->dev = &pdev->dev;
- fcp->dev->dma_parms = &fcp->dma_parms;
- dma_set_max_seg_size(fcp->dev, DMA_BIT_MASK(32));
+ dma_set_max_seg_size(fcp->dev, UINT_MAX);
pm_runtime_enable(&pdev->dev);
diff --git a/drivers/media/platform/rcar-vin/Kconfig b/drivers/media/platform/rcar-vin/Kconfig
index 240ac3f..030312d 100644
--- a/drivers/media/platform/rcar-vin/Kconfig
+++ b/drivers/media/platform/rcar-vin/Kconfig
@@ -1,26 +1,30 @@
# 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
Support for Renesas R-Car MIPI CSI-2 receiver.
- Supports R-Car Gen3 SoCs.
+ Supports R-Car Gen3 and RZ/G2 SoCs.
To compile this driver as a module, choose M here: the
module will be called 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
Support for Renesas R-Car Video Input (VIN) driver.
- Supports R-Car Gen2 and Gen3 SoCs.
+ Supports R-Car Gen{2,3} and RZ/G{1,2} SoCs.
To compile this driver as a module, choose M here: the
module will be called rcar-vin.
diff --git a/drivers/media/platform/rcar-vin/rcar-core.c b/drivers/media/platform/rcar-vin/rcar-core.c
index 6993484..34d003e 100644
--- a/drivers/media/platform/rcar-vin/rcar-core.c
+++ b/drivers/media/platform/rcar-vin/rcar-core.c
@@ -243,7 +243,6 @@
static void rvin_group_cleanup(struct rvin_group *group)
{
- media_device_unregister(&group->mdev);
media_device_cleanup(&group->mdev);
mutex_destroy(&group->lock);
}
@@ -253,7 +252,6 @@
struct media_device *mdev = &group->mdev;
const struct of_device_id *match;
struct device_node *np;
- int ret;
mutex_init(&group->lock);
@@ -278,11 +276,7 @@
media_device_init(mdev);
- ret = media_device_register(&group->mdev);
- if (ret)
- rvin_group_cleanup(group);
-
- return ret;
+ return 0;
}
static void rvin_group_release(struct kref *kref)
@@ -626,12 +620,11 @@
switch (vin->parallel->mbus_type) {
case V4L2_MBUS_PARALLEL:
- vin_dbg(vin, "Found PARALLEL media bus\n");
- vin->parallel->mbus_flags = vep->bus.parallel.flags;
- break;
case V4L2_MBUS_BT656:
- vin_dbg(vin, "Found BT656 media bus\n");
- vin->parallel->mbus_flags = 0;
+ vin_dbg(vin, "Found %s media bus\n",
+ vin->parallel->mbus_type == V4L2_MBUS_PARALLEL ?
+ "PARALLEL" : "BT656");
+ vin->parallel->bus = vep->bus.parallel;
break;
default:
vin_err(vin, "Unknown media bus type\n");
@@ -682,6 +675,10 @@
unsigned int i;
int ret;
+ ret = media_device_register(&vin->group->mdev);
+ if (ret)
+ return ret;
+
ret = v4l2_device_register_subdev_nodes(&vin->v4l2_dev);
if (ret) {
vin_err(vin, "Failed to register subdev nodes\n");
@@ -762,6 +759,8 @@
}
mutex_unlock(&vin->group->lock);
+
+ media_device_unregister(&vin->group->mdev);
}
static int rvin_group_notify_bound(struct v4l2_async_notifier *notifier,
@@ -944,6 +943,42 @@
.max_height = 2048,
};
+static const struct rvin_group_route rcar_info_r8a774e1_routes[] = {
+ { .csi = RVIN_CSI40, .channel = 0, .vin = 0, .mask = BIT(0) | BIT(3) },
+ { .csi = RVIN_CSI20, .channel = 0, .vin = 0, .mask = BIT(1) | BIT(4) },
+ { .csi = RVIN_CSI40, .channel = 1, .vin = 0, .mask = BIT(2) },
+ { .csi = RVIN_CSI20, .channel = 0, .vin = 1, .mask = BIT(0) },
+ { .csi = RVIN_CSI40, .channel = 1, .vin = 1, .mask = BIT(1) | BIT(3) },
+ { .csi = RVIN_CSI40, .channel = 0, .vin = 1, .mask = BIT(2) },
+ { .csi = RVIN_CSI20, .channel = 1, .vin = 1, .mask = BIT(4) },
+ { .csi = RVIN_CSI20, .channel = 1, .vin = 2, .mask = BIT(0) },
+ { .csi = RVIN_CSI40, .channel = 0, .vin = 2, .mask = BIT(1) },
+ { .csi = RVIN_CSI20, .channel = 0, .vin = 2, .mask = BIT(2) },
+ { .csi = RVIN_CSI40, .channel = 2, .vin = 2, .mask = BIT(3) },
+ { .csi = RVIN_CSI20, .channel = 2, .vin = 2, .mask = BIT(4) },
+ { .csi = RVIN_CSI40, .channel = 1, .vin = 3, .mask = BIT(0) },
+ { .csi = RVIN_CSI20, .channel = 1, .vin = 3, .mask = BIT(1) | BIT(2) },
+ { .csi = RVIN_CSI40, .channel = 3, .vin = 3, .mask = BIT(3) },
+ { .csi = RVIN_CSI20, .channel = 3, .vin = 3, .mask = BIT(4) },
+ { .csi = RVIN_CSI20, .channel = 0, .vin = 4, .mask = BIT(1) | BIT(4) },
+ { .csi = RVIN_CSI20, .channel = 0, .vin = 5, .mask = BIT(0) },
+ { .csi = RVIN_CSI20, .channel = 1, .vin = 5, .mask = BIT(4) },
+ { .csi = RVIN_CSI20, .channel = 1, .vin = 6, .mask = BIT(0) },
+ { .csi = RVIN_CSI20, .channel = 0, .vin = 6, .mask = BIT(2) },
+ { .csi = RVIN_CSI20, .channel = 2, .vin = 6, .mask = BIT(4) },
+ { .csi = RVIN_CSI20, .channel = 1, .vin = 7, .mask = BIT(1) | BIT(2) },
+ { .csi = RVIN_CSI20, .channel = 3, .vin = 7, .mask = BIT(4) },
+ { /* Sentinel */ }
+};
+
+static const struct rvin_info rcar_info_r8a774e1 = {
+ .model = RCAR_GEN3,
+ .use_mc = true,
+ .max_width = 4096,
+ .max_height = 4096,
+ .routes = rcar_info_r8a774e1_routes,
+};
+
static const struct rvin_group_route rcar_info_r8a7795_routes[] = {
{ .csi = RVIN_CSI40, .channel = 0, .vin = 0, .mask = BIT(0) | BIT(3) },
{ .csi = RVIN_CSI20, .channel = 0, .vin = 0, .mask = BIT(1) | BIT(4) },
@@ -983,6 +1018,7 @@
static const struct rvin_info rcar_info_r8a7795 = {
.model = RCAR_GEN3,
.use_mc = true,
+ .nv12 = true,
.max_width = 4096,
.max_height = 4096,
.routes = rcar_info_r8a7795_routes,
@@ -1077,6 +1113,7 @@
static const struct rvin_info rcar_info_r8a7796 = {
.model = RCAR_GEN3,
.use_mc = true,
+ .nv12 = true,
.max_width = 4096,
.max_height = 4096,
.routes = rcar_info_r8a7796_routes,
@@ -1121,6 +1158,7 @@
static const struct rvin_info rcar_info_r8a77965 = {
.model = RCAR_GEN3,
.use_mc = true,
+ .nv12 = true,
.max_width = 4096,
.max_height = 4096,
.routes = rcar_info_r8a77965_routes,
@@ -1168,6 +1206,7 @@
static const struct rvin_info rcar_info_r8a77980 = {
.model = RCAR_GEN3,
.use_mc = true,
+ .nv12 = true,
.max_width = 4096,
.max_height = 4096,
.routes = rcar_info_r8a77980_routes,
@@ -1184,6 +1223,7 @@
static const struct rvin_info rcar_info_r8a77990 = {
.model = RCAR_GEN3,
.use_mc = true,
+ .nv12 = true,
.max_width = 4096,
.max_height = 4096,
.routes = rcar_info_r8a77990_routes,
@@ -1196,6 +1236,7 @@
static const struct rvin_info rcar_info_r8a77995 = {
.model = RCAR_GEN3,
.use_mc = true,
+ .nv12 = true,
.max_width = 4096,
.max_height = 4096,
.routes = rcar_info_r8a77995_routes,
@@ -1207,10 +1248,18 @@
.data = &rcar_info_r8a7796,
},
{
+ .compatible = "renesas,vin-r8a774b1",
+ .data = &rcar_info_r8a77965,
+ },
+ {
.compatible = "renesas,vin-r8a774c0",
.data = &rcar_info_r8a77990,
},
{
+ .compatible = "renesas,vin-r8a774e1",
+ .data = &rcar_info_r8a774e1,
+ },
+ {
.compatible = "renesas,vin-r8a7778",
.data = &rcar_info_m1,
},
@@ -1282,7 +1331,6 @@
{
const struct soc_device_attribute *attr;
struct rvin_dev *vin;
- struct resource *mem;
int irq, ret;
vin = devm_kzalloc(&pdev->dev, sizeof(*vin), GFP_KERNEL);
@@ -1301,11 +1349,7 @@
if (attr)
vin->info = attr->data;
- mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (mem == NULL)
- return -EINVAL;
-
- vin->base = devm_ioremap_resource(vin->dev, mem);
+ vin->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(vin->base))
return PTR_ERR(vin->base);
@@ -1365,12 +1409,8 @@
v4l2_async_notifier_cleanup(&vin->notifier);
if (vin->info->use_mc) {
- mutex_lock(&vin->group->lock);
- if (&vin->v4l2_dev == vin->group->notifier.v4l2_dev) {
- v4l2_async_notifier_unregister(&vin->group->notifier);
- v4l2_async_notifier_cleanup(&vin->group->notifier);
- }
- mutex_unlock(&vin->group->lock);
+ v4l2_async_notifier_unregister(&vin->group->notifier);
+ v4l2_async_notifier_cleanup(&vin->group->notifier);
rvin_group_put(vin);
}
diff --git a/drivers/media/platform/rcar-vin/rcar-csi2.c b/drivers/media/platform/rcar-vin/rcar-csi2.c
index d27eccf..5e8e48a 100644
--- a/drivers/media/platform/rcar-vin/rcar-csi2.c
+++ b/drivers/media/platform/rcar-vin/rcar-csi2.c
@@ -52,8 +52,8 @@
/*
* 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
@@ -320,6 +320,10 @@
{ .code = MEDIA_BUS_FMT_YUYV8_1X16, .datatype = 0x1e, .bpp = 16 },
{ .code = MEDIA_BUS_FMT_UYVY8_2X8, .datatype = 0x1e, .bpp = 16 },
{ .code = MEDIA_BUS_FMT_YUYV10_2X10, .datatype = 0x1e, .bpp = 20 },
+ { .code = MEDIA_BUS_FMT_SBGGR8_1X8, .datatype = 0x2a, .bpp = 8 },
+ { .code = MEDIA_BUS_FMT_SGBRG8_1X8, .datatype = 0x2a, .bpp = 8 },
+ { .code = MEDIA_BUS_FMT_SGRBG8_1X8, .datatype = 0x2a, .bpp = 8 },
+ { .code = MEDIA_BUS_FMT_SRGGB8_1X8, .datatype = 0x2a, .bpp = 8 },
};
static const struct rcar_csi2_format *rcsi2_code_to_fmt(unsigned int code)
@@ -344,7 +348,7 @@
struct rcar_csi2_info {
int (*init_phtw)(struct rcar_csi2 *priv, unsigned int mbps);
- int (*confirm_start)(struct rcar_csi2 *priv);
+ int (*phy_post_init)(struct rcar_csi2 *priv);
const struct rcsi2_mbps_reg *hsfreqrange;
unsigned int csi0clkfreqrange;
unsigned int num_channels;
@@ -362,6 +366,7 @@
struct v4l2_async_notifier notifier;
struct v4l2_subdev *remote;
+ unsigned int remote_pad;
struct v4l2_mbus_framefmt mf;
@@ -407,13 +412,14 @@
reset_control_deassert(priv->rstc);
}
-static int rcsi2_wait_phy_start(struct rcar_csi2 *priv)
+static int rcsi2_wait_phy_start(struct rcar_csi2 *priv,
+ unsigned int lanes)
{
unsigned int timeout;
/* Wait for the clock and data lanes to enter LP-11 state. */
for (timeout = 0; timeout <= 20; timeout++) {
- const u32 lane_mask = (1 << priv->lanes) - 1;
+ const u32 lane_mask = (1 << lanes) - 1;
if ((rcsi2_read(priv, PHCLM_REG) & PHCLM_STOPSTATECKL) &&
(rcsi2_read(priv, PHDLM_REG) & lane_mask) == lane_mask)
@@ -430,22 +436,30 @@
static int rcsi2_set_phypll(struct rcar_csi2 *priv, unsigned int mbps)
{
const struct rcsi2_mbps_reg *hsfreq;
+ const struct rcsi2_mbps_reg *hsfreq_prev = NULL;
- for (hsfreq = priv->info->hsfreqrange; hsfreq->mbps != 0; hsfreq++)
+ for (hsfreq = priv->info->hsfreqrange; hsfreq->mbps != 0; hsfreq++) {
if (hsfreq->mbps >= mbps)
break;
+ hsfreq_prev = hsfreq;
+ }
if (!hsfreq->mbps) {
dev_err(priv->dev, "Unsupported PHY speed (%u Mbps)", mbps);
return -ERANGE;
}
+ if (hsfreq_prev &&
+ ((mbps - hsfreq_prev->mbps) <= (hsfreq->mbps - mbps)))
+ hsfreq = hsfreq_prev;
+
rcsi2_write(priv, PHYPLL_REG, PHYPLL_HSFREQRANGE(hsfreq->reg));
return 0;
}
-static int rcsi2_calc_mbps(struct rcar_csi2 *priv, unsigned int bpp)
+static int rcsi2_calc_mbps(struct rcar_csi2 *priv, unsigned int bpp,
+ unsigned int lanes)
{
struct v4l2_subdev *source;
struct v4l2_ctrl *ctrl;
@@ -470,15 +484,64 @@
* bps = link_freq * 2
*/
mbps = v4l2_ctrl_g_ctrl_int64(ctrl) * bpp;
- do_div(mbps, priv->lanes * 1000000);
+ do_div(mbps, lanes * 1000000);
return mbps;
}
+static int rcsi2_get_active_lanes(struct rcar_csi2 *priv,
+ unsigned int *lanes)
+{
+ struct v4l2_mbus_config mbus_config = { 0 };
+ unsigned int num_lanes = UINT_MAX;
+ int ret;
+
+ *lanes = priv->lanes;
+
+ ret = v4l2_subdev_call(priv->remote, pad, get_mbus_config,
+ priv->remote_pad, &mbus_config);
+ if (ret == -ENOIOCTLCMD) {
+ dev_dbg(priv->dev, "No remote mbus configuration available\n");
+ return 0;
+ }
+
+ if (ret) {
+ dev_err(priv->dev, "Failed to get remote mbus configuration\n");
+ return ret;
+ }
+
+ if (mbus_config.type != V4L2_MBUS_CSI2_DPHY) {
+ dev_err(priv->dev, "Unsupported media bus type %u\n",
+ mbus_config.type);
+ return -EINVAL;
+ }
+
+ if (mbus_config.flags & V4L2_MBUS_CSI2_1_LANE)
+ num_lanes = 1;
+ else if (mbus_config.flags & V4L2_MBUS_CSI2_2_LANE)
+ num_lanes = 2;
+ else if (mbus_config.flags & V4L2_MBUS_CSI2_3_LANE)
+ num_lanes = 3;
+ else if (mbus_config.flags & V4L2_MBUS_CSI2_4_LANE)
+ num_lanes = 4;
+
+ if (num_lanes > priv->lanes) {
+ dev_err(priv->dev,
+ "Unsupported mbus config: too many data lanes %u\n",
+ num_lanes);
+ return -EINVAL;
+ }
+
+ *lanes = num_lanes;
+
+ return 0;
+}
+
static int rcsi2_start_receiver(struct rcar_csi2 *priv)
{
const struct rcar_csi2_format *format;
u32 phycnt, vcdt = 0, vcdt2 = 0, fld = 0;
+ unsigned int lanes;
unsigned int i;
int mbps, ret;
@@ -488,6 +551,8 @@
/* Code is validated in set_fmt. */
format = rcsi2_code_to_fmt(priv->mf.code);
+ if (!format)
+ return -EINVAL;
/*
* Enable all supported CSI-2 channels with virtual channel and
@@ -520,10 +585,18 @@
fld |= FLD_FLD_NUM(1);
}
- phycnt = PHYCNT_ENABLECLK;
- phycnt |= (1 << priv->lanes) - 1;
+ /*
+ * Get the number of active data lanes inspecting the remote mbus
+ * configuration.
+ */
+ ret = rcsi2_get_active_lanes(priv, &lanes);
+ if (ret)
+ return ret;
- mbps = rcsi2_calc_mbps(priv, format->bpp);
+ phycnt = PHYCNT_ENABLECLK;
+ phycnt |= (1 << lanes) - 1;
+
+ mbps = rcsi2_calc_mbps(priv, format->bpp, lanes);
if (mbps < 0)
return mbps;
@@ -570,13 +643,13 @@
rcsi2_write(priv, PHYCNT_REG, phycnt | PHYCNT_SHUTDOWNZ);
rcsi2_write(priv, PHYCNT_REG, phycnt | PHYCNT_SHUTDOWNZ | PHYCNT_RSTZ);
- ret = rcsi2_wait_phy_start(priv);
+ ret = rcsi2_wait_phy_start(priv, lanes);
if (ret)
return ret;
- /* Confirm start */
- if (priv->info->confirm_start) {
- ret = priv->info->confirm_start(priv);
+ /* Run post PHY start initialization, if needed. */
+ if (priv->info->phy_post_init) {
+ ret = priv->info->phy_post_init(priv);
if (ret)
return ret;
}
@@ -747,6 +820,7 @@
}
priv->remote = subdev;
+ priv->remote_pad = pad;
dev_dbg(priv->dev, "Bound %s pad: %d\n", subdev->name, pad);
@@ -902,10 +976,17 @@
const struct rcsi2_mbps_reg *values, u16 code)
{
const struct rcsi2_mbps_reg *value;
+ const struct rcsi2_mbps_reg *prev_value = NULL;
- for (value = values; value->mbps; value++)
+ for (value = values; value->mbps; value++) {
if (value->mbps >= mbps)
break;
+ prev_value = value;
+ }
+
+ if (prev_value &&
+ ((mbps - prev_value->mbps) <= (value->mbps - mbps)))
+ value = prev_value;
if (!value->mbps) {
dev_err(priv->dev, "Unsupported PHY speed (%u Mbps)", mbps);
@@ -971,7 +1052,7 @@
return rcsi2_phtw_write_mbps(priv, mbps, phtw_mbps_v3m_e3, 0x44);
}
-static int rcsi2_confirm_start_v3m_e3(struct rcar_csi2 *priv)
+static int rcsi2_phy_post_init_v3m_e3(struct rcar_csi2 *priv)
{
static const struct phtw_value step1[] = {
{ .data = 0xee, .code = 0x34 },
@@ -1055,7 +1136,7 @@
static const struct rcar_csi2_info rcar_csi2_info_r8a77970 = {
.init_phtw = rcsi2_init_phtw_v3m_e3,
- .confirm_start = rcsi2_confirm_start_v3m_e3,
+ .phy_post_init = rcsi2_phy_post_init_v3m_e3,
.num_channels = 4,
};
@@ -1068,7 +1149,7 @@
static const struct rcar_csi2_info rcar_csi2_info_r8a77990 = {
.init_phtw = rcsi2_init_phtw_v3m_e3,
- .confirm_start = rcsi2_confirm_start_v3m_e3,
+ .phy_post_init = rcsi2_phy_post_init_v3m_e3,
.num_channels = 2,
};
@@ -1078,10 +1159,18 @@
.data = &rcar_csi2_info_r8a7796,
},
{
+ .compatible = "renesas,r8a774b1-csi2",
+ .data = &rcar_csi2_info_r8a77965,
+ },
+ {
.compatible = "renesas,r8a774c0-csi2",
.data = &rcar_csi2_info_r8a77990,
},
{
+ .compatible = "renesas,r8a774e1-csi2",
+ .data = &rcar_csi2_info_r8a7795,
+ },
+ {
.compatible = "renesas,r8a7795-csi2",
.data = &rcar_csi2_info_r8a7795,
},
diff --git a/drivers/media/platform/rcar-vin/rcar-dma.c b/drivers/media/platform/rcar-vin/rcar-dma.c
index e5f6360..692dea3 100644
--- a/drivers/media/platform/rcar-vin/rcar-dma.c
+++ b/drivers/media/platform/rcar-vin/rcar-dma.c
@@ -85,6 +85,7 @@
#define VNMC_INF_YUV8_BT601 (1 << 16)
#define VNMC_INF_YUV10_BT656 (2 << 16)
#define VNMC_INF_YUV10_BT601 (3 << 16)
+#define VNMC_INF_RAW8 (4 << 16)
#define VNMC_INF_YUV16 (5 << 16)
#define VNMC_INF_RGB888 (6 << 16)
#define VNMC_VUP (1 << 10)
@@ -118,11 +119,13 @@
#define VNDMR_ABIT (1 << 2)
#define VNDMR_DTMD_YCSEP (1 << 1)
#define VNDMR_DTMD_ARGB (1 << 0)
+#define VNDMR_DTMD_YCSEP_420 (3 << 0)
/* Video n Data Mode Register 2 bits */
#define VNDMR2_VPS (1 << 30)
#define VNDMR2_HPS (1 << 29)
#define VNDMR2_CES (1 << 28)
+#define VNDMR2_YDS (1 << 22)
#define VNDMR2_FTEV (1 << 17)
#define VNDMR2_VLV(n) ((n & 0xf) << 12)
@@ -529,12 +532,17 @@
static void rvin_crop_scale_comp_gen2(struct rvin_dev *vin)
{
+ unsigned int crop_height;
u32 xs, ys;
/* Set scaling coefficient */
+ crop_height = vin->crop.height;
+ if (V4L2_FIELD_HAS_BOTH(vin->format.field))
+ crop_height *= 2;
+
ys = 0;
- if (vin->crop.height != vin->compose.height)
- ys = (4096 * vin->crop.height) / vin->compose.height;
+ if (crop_height != vin->compose.height)
+ ys = (4096 * crop_height) / vin->compose.height;
rvin_write(vin, ys, VNYS_REG);
xs = 0;
@@ -557,16 +565,11 @@
rvin_write(vin, 0, VNSPPOC_REG);
rvin_write(vin, 0, VNSLPOC_REG);
rvin_write(vin, vin->format.width - 1, VNEPPOC_REG);
- switch (vin->format.field) {
- case V4L2_FIELD_INTERLACED:
- case V4L2_FIELD_INTERLACED_TB:
- case V4L2_FIELD_INTERLACED_BT:
+
+ if (V4L2_FIELD_HAS_BOTH(vin->format.field))
rvin_write(vin, vin->format.height / 2 - 1, VNELPOC_REG);
- break;
- default:
+ else
rvin_write(vin, vin->format.height - 1, VNELPOC_REG);
- break;
- }
vin_dbg(vin,
"Pre-Clip: %ux%u@%u:%u YS: %d XS: %d Post-Clip: %ux%u@%u:%u\n",
@@ -583,21 +586,8 @@
/* Set Start/End Pixel/Line Pre-Clip */
rvin_write(vin, vin->crop.left, VNSPPRC_REG);
rvin_write(vin, vin->crop.left + vin->crop.width - 1, VNEPPRC_REG);
-
- switch (vin->format.field) {
- case V4L2_FIELD_INTERLACED:
- case V4L2_FIELD_INTERLACED_TB:
- case V4L2_FIELD_INTERLACED_BT:
- rvin_write(vin, vin->crop.top / 2, VNSLPRC_REG);
- rvin_write(vin, (vin->crop.top + vin->crop.height) / 2 - 1,
- VNELPRC_REG);
- break;
- default:
- rvin_write(vin, vin->crop.top, VNSLPRC_REG);
- rvin_write(vin, vin->crop.top + vin->crop.height - 1,
- VNELPRC_REG);
- break;
- }
+ rvin_write(vin, vin->crop.top, VNSLPRC_REG);
+ rvin_write(vin, vin->crop.top + vin->crop.height - 1, VNELPRC_REG);
/* TODO: Add support for the UDS scaler. */
if (vin->info->model != RCAR_GEN3)
@@ -605,6 +595,21 @@
fmt = rvin_format_from_pixel(vin, vin->format.pixelformat);
stride = vin->format.bytesperline / fmt->bpp;
+
+ /* For RAW8 format bpp is 1, but the hardware process RAW8
+ * format in 2 pixel unit hence configure VNIS_REG as stride / 2.
+ */
+ switch (vin->format.pixelformat) {
+ case V4L2_PIX_FMT_SBGGR8:
+ case V4L2_PIX_FMT_SGBRG8:
+ case V4L2_PIX_FMT_SGRBG8:
+ case V4L2_PIX_FMT_SRGGB8:
+ stride /= 2;
+ break;
+ default:
+ break;
+ }
+
rvin_write(vin, stride, VNIS_REG);
}
@@ -637,10 +642,15 @@
case V4L2_FIELD_INTERLACED_BT:
vnmc = VNMC_IM_FULL | VNMC_FOC;
break;
+ case V4L2_FIELD_SEQ_TB:
+ case V4L2_FIELD_SEQ_BT:
case V4L2_FIELD_NONE:
vnmc = VNMC_IM_ODD_EVEN;
progressive = true;
break;
+ case V4L2_FIELD_ALTERNATE:
+ vnmc = VNMC_IM_ODD_EVEN;
+ break;
default:
vnmc = VNMC_IM_ODD;
break;
@@ -682,6 +692,12 @@
input_is_yuv = true;
break;
+ case MEDIA_BUS_FMT_SBGGR8_1X8:
+ case MEDIA_BUS_FMT_SGBRG8_1X8:
+ case MEDIA_BUS_FMT_SGRBG8_1X8:
+ case MEDIA_BUS_FMT_SRGGB8_1X8:
+ vnmc |= VNMC_INF_RAW8;
+ break;
default:
break;
}
@@ -694,27 +710,39 @@
if (!vin->is_csi) {
/* Hsync Signal Polarity Select */
- if (!(vin->parallel->mbus_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW))
+ if (!(vin->parallel->bus.flags & V4L2_MBUS_HSYNC_ACTIVE_LOW))
dmr2 |= VNDMR2_HPS;
/* Vsync Signal Polarity Select */
- if (!(vin->parallel->mbus_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW))
+ if (!(vin->parallel->bus.flags & V4L2_MBUS_VSYNC_ACTIVE_LOW))
dmr2 |= VNDMR2_VPS;
/* Data Enable Polarity Select */
- if (vin->parallel->mbus_flags & V4L2_MBUS_DATA_ENABLE_LOW)
+ if (vin->parallel->bus.flags & V4L2_MBUS_DATA_ENABLE_LOW)
dmr2 |= VNDMR2_CES;
+
+ switch (vin->mbus_code) {
+ case MEDIA_BUS_FMT_UYVY8_2X8:
+ if (vin->parallel->bus.bus_width == 8 &&
+ vin->parallel->bus.data_shift == 8)
+ dmr2 |= VNDMR2_YDS;
+ break;
+ default:
+ break;
+ }
}
/*
* Output format
*/
switch (vin->format.pixelformat) {
+ case V4L2_PIX_FMT_NV12:
case V4L2_PIX_FMT_NV16:
rvin_write(vin,
- ALIGN(vin->format.width * vin->format.height, 0x80),
- VNUVAOF_REG);
- dmr = VNDMR_DTMD_YCSEP;
+ ALIGN(vin->format.bytesperline * vin->format.height,
+ 0x80), VNUVAOF_REG);
+ dmr = vin->format.pixelformat == V4L2_PIX_FMT_NV12 ?
+ VNDMR_DTMD_YCSEP_420 : VNDMR_DTMD_YCSEP;
output_is_yuv = true;
break;
case V4L2_PIX_FMT_YUYV:
@@ -741,6 +769,12 @@
case V4L2_PIX_FMT_ABGR32:
dmr = VNDMR_A8BIT(vin->alpha) | VNDMR_EXRGB | VNDMR_DTMD_ARGB;
break;
+ case V4L2_PIX_FMT_SBGGR8:
+ case V4L2_PIX_FMT_SGBRG8:
+ case V4L2_PIX_FMT_SGRBG8:
+ case V4L2_PIX_FMT_SRGGB8:
+ dmr = 0;
+ break;
default:
vin_err(vin, "Invalid pixelformat (0x%x)\n",
vin->format.pixelformat);
@@ -799,6 +833,18 @@
return rvin_read(vin, VNMS_REG) & VNMS_CA;
}
+static enum v4l2_field rvin_get_active_field(struct rvin_dev *vin, u32 vnms)
+{
+ if (vin->format.field == V4L2_FIELD_ALTERNATE) {
+ /* If FS is set it is an Even field. */
+ if (vnms & VNMS_FS)
+ return V4L2_FIELD_BOTTOM;
+ return V4L2_FIELD_TOP;
+ }
+
+ return vin->format.field;
+}
+
static void rvin_set_slot_addr(struct rvin_dev *vin, int slot, dma_addr_t addr)
{
const struct rvin_video_format *fmt;
@@ -836,27 +882,52 @@
struct rvin_buffer *buf;
struct vb2_v4l2_buffer *vbuf;
dma_addr_t phys_addr;
+ int prev;
/* A already populated slot shall never be overwritten. */
- if (WARN_ON(vin->queue_buf[slot] != NULL))
+ if (WARN_ON(vin->buf_hw[slot].buffer))
return;
- vin_dbg(vin, "Filling HW slot: %d\n", slot);
+ prev = (slot == 0 ? HW_BUFFER_NUM : slot) - 1;
- if (list_empty(&vin->buf_list)) {
- vin->queue_buf[slot] = NULL;
+ if (vin->buf_hw[prev].type == HALF_TOP) {
+ vbuf = vin->buf_hw[prev].buffer;
+ vin->buf_hw[slot].buffer = vbuf;
+ vin->buf_hw[slot].type = HALF_BOTTOM;
+ switch (vin->format.pixelformat) {
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV16:
+ phys_addr = vin->buf_hw[prev].phys +
+ vin->format.sizeimage / 4;
+ break;
+ default:
+ phys_addr = vin->buf_hw[prev].phys +
+ vin->format.sizeimage / 2;
+ break;
+ }
+ } else if (list_empty(&vin->buf_list)) {
+ vin->buf_hw[slot].buffer = NULL;
+ vin->buf_hw[slot].type = FULL;
phys_addr = vin->scratch_phys;
} else {
/* Keep track of buffer we give to HW */
buf = list_entry(vin->buf_list.next, struct rvin_buffer, list);
vbuf = &buf->vb;
list_del_init(to_buf_list(vbuf));
- vin->queue_buf[slot] = vbuf;
+ vin->buf_hw[slot].buffer = vbuf;
+
+ vin->buf_hw[slot].type =
+ V4L2_FIELD_IS_SEQUENTIAL(vin->format.field) ?
+ HALF_TOP : FULL;
/* Setup DMA */
phys_addr = vb2_dma_contig_plane_dma_addr(&vbuf->vb2_buf, 0);
}
+ vin_dbg(vin, "Filling HW slot: %d type: %d buffer: %p\n",
+ slot, vin->buf_hw[slot].type, vin->buf_hw[slot].buffer);
+
+ vin->buf_hw[slot].phys = phys_addr;
rvin_set_slot_addr(vin, slot, phys_addr);
}
@@ -864,6 +935,11 @@
{
int slot, ret;
+ for (slot = 0; slot < HW_BUFFER_NUM; slot++) {
+ vin->buf_hw[slot].buffer = NULL;
+ vin->buf_hw[slot].type = FULL;
+ }
+
for (slot = 0; slot < HW_BUFFER_NUM; slot++)
rvin_fill_hw_slot(vin, slot);
@@ -947,13 +1023,24 @@
}
/* Capture frame */
- if (vin->queue_buf[slot]) {
- vin->queue_buf[slot]->field = vin->format.field;
- vin->queue_buf[slot]->sequence = vin->sequence;
- vin->queue_buf[slot]->vb2_buf.timestamp = ktime_get_ns();
- vb2_buffer_done(&vin->queue_buf[slot]->vb2_buf,
+ if (vin->buf_hw[slot].buffer) {
+ /*
+ * Nothing to do but refill the hardware slot if
+ * capture only filled first half of vb2 buffer.
+ */
+ if (vin->buf_hw[slot].type == HALF_TOP) {
+ vin->buf_hw[slot].buffer = NULL;
+ rvin_fill_hw_slot(vin, slot);
+ goto done;
+ }
+
+ vin->buf_hw[slot].buffer->field =
+ rvin_get_active_field(vin, vnms);
+ vin->buf_hw[slot].buffer->sequence = vin->sequence;
+ vin->buf_hw[slot].buffer->vb2_buf.timestamp = ktime_get_ns();
+ vb2_buffer_done(&vin->buf_hw[slot].buffer->vb2_buf,
VB2_BUF_STATE_DONE);
- vin->queue_buf[slot] = NULL;
+ vin->buf_hw[slot].buffer = NULL;
} else {
/* Scratch buffer was used, dropping frame. */
vin_dbg(vin, "Dropping frame %u\n", vin->sequence);
@@ -974,14 +1061,22 @@
enum vb2_buffer_state state)
{
struct rvin_buffer *buf, *node;
- int i;
+ struct vb2_v4l2_buffer *freed[HW_BUFFER_NUM];
+ unsigned int i, n;
for (i = 0; i < HW_BUFFER_NUM; i++) {
- if (vin->queue_buf[i]) {
- vb2_buffer_done(&vin->queue_buf[i]->vb2_buf,
- state);
- vin->queue_buf[i] = NULL;
+ freed[i] = vin->buf_hw[i].buffer;
+ vin->buf_hw[i].buffer = NULL;
+
+ for (n = 0; n < i; n++) {
+ if (freed[i] == freed[n]) {
+ freed[i] = NULL;
+ break;
+ }
}
+
+ if (freed[i])
+ vb2_buffer_done(&freed[i]->vb2_buf, state);
}
list_for_each_entry_safe(buf, node, &vin->buf_list, list) {
@@ -1053,11 +1148,27 @@
case MEDIA_BUS_FMT_UYVY8_2X8:
case MEDIA_BUS_FMT_UYVY10_2X10:
case MEDIA_BUS_FMT_RGB888_1X24:
- vin->mbus_code = fmt.format.code;
+ break;
+ case MEDIA_BUS_FMT_SBGGR8_1X8:
+ if (vin->format.pixelformat != V4L2_PIX_FMT_SBGGR8)
+ return -EPIPE;
+ break;
+ case MEDIA_BUS_FMT_SGBRG8_1X8:
+ if (vin->format.pixelformat != V4L2_PIX_FMT_SGBRG8)
+ return -EPIPE;
+ break;
+ case MEDIA_BUS_FMT_SGRBG8_1X8:
+ if (vin->format.pixelformat != V4L2_PIX_FMT_SGRBG8)
+ return -EPIPE;
+ break;
+ case MEDIA_BUS_FMT_SRGGB8_1X8:
+ if (vin->format.pixelformat != V4L2_PIX_FMT_SRGGB8)
+ return -EPIPE;
break;
default:
return -EPIPE;
}
+ vin->mbus_code = fmt.format.code;
switch (fmt.format.field) {
case V4L2_FIELD_TOP:
@@ -1075,6 +1186,7 @@
case V4L2_FIELD_TOP:
case V4L2_FIELD_BOTTOM:
case V4L2_FIELD_NONE:
+ case V4L2_FIELD_ALTERNATE:
break;
case V4L2_FIELD_INTERLACED_TB:
case V4L2_FIELD_INTERLACED_BT:
@@ -1284,7 +1396,7 @@
vin->state = STOPPED;
for (i = 0; i < HW_BUFFER_NUM; i++)
- vin->queue_buf[i] = NULL;
+ vin->buf_hw[i].buffer = NULL;
/* buffer queue */
q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
diff --git a/drivers/media/platform/rcar-vin/rcar-v4l2.c b/drivers/media/platform/rcar-vin/rcar-v4l2.c
index ec27964..0bbe6f9 100644
--- a/drivers/media/platform/rcar-vin/rcar-v4l2.c
+++ b/drivers/media/platform/rcar-vin/rcar-v4l2.c
@@ -31,6 +31,10 @@
static const struct rvin_video_format rvin_formats[] = {
{
+ .fourcc = V4L2_PIX_FMT_NV12,
+ .bpp = 1,
+ },
+ {
.fourcc = V4L2_PIX_FMT_NV16,
.bpp = 1,
},
@@ -62,6 +66,22 @@
.fourcc = V4L2_PIX_FMT_ABGR32,
.bpp = 4,
},
+ {
+ .fourcc = V4L2_PIX_FMT_SBGGR8,
+ .bpp = 1,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SGBRG8,
+ .bpp = 1,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SGRBG8,
+ .bpp = 1,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SRGGB8,
+ .bpp = 1,
+ },
};
const struct rvin_video_format *rvin_format_from_pixel(struct rvin_dev *vin,
@@ -69,8 +89,22 @@
{
int i;
- if (vin->info->model == RCAR_M1 && pixelformat == V4L2_PIX_FMT_XBGR32)
- return NULL;
+ switch (pixelformat) {
+ case V4L2_PIX_FMT_XBGR32:
+ if (vin->info->model == RCAR_M1)
+ return NULL;
+ break;
+ case V4L2_PIX_FMT_NV12:
+ /*
+ * If NV12 is supported it's only supported on channels 0, 1, 4,
+ * 5, 8, 9, 12 and 13.
+ */
+ if (!vin->info->nv12 || !(BIT(vin->id) & 0x3333))
+ return NULL;
+ break;
+ default:
+ break;
+ }
for (i = 0; i < ARRAY_SIZE(rvin_formats); i++)
if (rvin_formats[i].fourcc == pixelformat)
@@ -90,17 +124,32 @@
if (WARN_ON(!fmt))
return -EINVAL;
- align = pix->pixelformat == V4L2_PIX_FMT_NV16 ? 0x20 : 0x10;
+ switch (pix->pixelformat) {
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV16:
+ align = 0x20;
+ break;
+ default:
+ align = 0x10;
+ break;
+ }
+
+ if (V4L2_FIELD_IS_SEQUENTIAL(pix->field))
+ align = 0x80;
return ALIGN(pix->width, align) * fmt->bpp;
}
static u32 rvin_format_sizeimage(struct v4l2_pix_format *pix)
{
- if (pix->pixelformat == V4L2_PIX_FMT_NV16)
+ switch (pix->pixelformat) {
+ case V4L2_PIX_FMT_NV12:
+ return pix->bytesperline * pix->height * 3 / 2;
+ case V4L2_PIX_FMT_NV16:
return pix->bytesperline * pix->height * 2;
-
- return pix->bytesperline * pix->height;
+ default:
+ return pix->bytesperline * pix->height;
+ }
}
static void rvin_format_align(struct rvin_dev *vin, struct v4l2_pix_format *pix)
@@ -117,27 +166,36 @@
case V4L2_FIELD_INTERLACED_TB:
case V4L2_FIELD_INTERLACED_BT:
case V4L2_FIELD_INTERLACED:
- break;
case V4L2_FIELD_ALTERNATE:
- /*
- * Driver does not (yet) support outputting ALTERNATE to a
- * userspace. It does support outputting INTERLACED so use
- * the VIN hardware to combine the two fields.
- */
- pix->field = V4L2_FIELD_INTERLACED;
- pix->height *= 2;
+ case V4L2_FIELD_SEQ_TB:
+ case V4L2_FIELD_SEQ_BT:
break;
default:
pix->field = RVIN_DEFAULT_FIELD;
break;
}
- /* HW limit width to a multiple of 32 (2^5) for NV16 else 2 (2^1) */
- walign = vin->format.pixelformat == V4L2_PIX_FMT_NV16 ? 5 : 1;
+ /* Hardware limits width alignment based on format. */
+ switch (pix->pixelformat) {
+ /* Multiple of 32 (2^5) for NV12/16. */
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV16:
+ walign = 5;
+ break;
+ /* Multiple of 2 (2^1) for YUV. */
+ case V4L2_PIX_FMT_YUYV:
+ case V4L2_PIX_FMT_UYVY:
+ walign = 1;
+ break;
+ /* No multiple for RGB. */
+ default:
+ walign = 0;
+ break;
+ }
/* Limit to VIN capabilities */
- v4l_bound_align_image(&pix->width, 2, vin->info->max_width, walign,
- &pix->height, 4, vin->info->max_height, 2, 0);
+ v4l_bound_align_image(&pix->width, 5, vin->info->max_width, walign,
+ &pix->height, 2, vin->info->max_height, 0, 0);
pix->bytesperline = rvin_format_bytesperline(vin, pix);
pix->sizeimage = rvin_format_sizeimage(pix);
@@ -164,22 +222,32 @@
v4l2_fill_pix_format(&vin->format, &fmt.format);
+ vin->src_rect.top = 0;
+ vin->src_rect.left = 0;
+ vin->src_rect.width = vin->format.width;
+ vin->src_rect.height = vin->format.height;
+
+ /* Make use of the hardware interlacer by default. */
+ if (vin->format.field == V4L2_FIELD_ALTERNATE) {
+ vin->format.field = V4L2_FIELD_INTERLACED;
+ vin->format.height *= 2;
+ }
+
rvin_format_align(vin, &vin->format);
- vin->source.top = 0;
- vin->source.left = 0;
- vin->source.width = vin->format.width;
- vin->source.height = vin->format.height;
+ vin->crop = vin->src_rect;
- vin->crop = vin->source;
- vin->compose = vin->source;
+ vin->compose.top = 0;
+ vin->compose.left = 0;
+ vin->compose.width = vin->format.width;
+ vin->compose.height = vin->format.height;
return 0;
}
static int rvin_try_format(struct rvin_dev *vin, u32 which,
struct v4l2_pix_format *pix,
- struct v4l2_rect *crop, struct v4l2_rect *compose)
+ struct v4l2_rect *src_rect)
{
struct v4l2_subdev *sd = vin_to_source(vin);
struct v4l2_subdev_pad_config *pad_cfg;
@@ -212,18 +280,11 @@
v4l2_fill_pix_format(pix, &format.format);
- if (crop) {
- crop->top = 0;
- crop->left = 0;
- crop->width = pix->width;
- crop->height = pix->height;
-
- /*
- * If source is ALTERNATE the driver will use the VIN hardware
- * to INTERLACE it. The crop height then needs to be doubled.
- */
- if (pix->field == V4L2_FIELD_ALTERNATE)
- crop->height *= 2;
+ if (src_rect) {
+ src_rect->top = 0;
+ src_rect->left = 0;
+ src_rect->width = pix->width;
+ src_rect->height = pix->height;
}
if (field != V4L2_FIELD_ANY)
@@ -233,13 +294,6 @@
pix->height = height;
rvin_format_align(vin, pix);
-
- if (compose) {
- compose->top = 0;
- compose->left = 0;
- compose->width = pix->width;
- compose->height = pix->height;
- }
done:
v4l2_subdev_free_pad_config(pad_cfg);
@@ -263,29 +317,34 @@
{
struct rvin_dev *vin = video_drvdata(file);
- return rvin_try_format(vin, V4L2_SUBDEV_FORMAT_TRY, &f->fmt.pix, NULL,
- NULL);
+ return rvin_try_format(vin, V4L2_SUBDEV_FORMAT_TRY, &f->fmt.pix, NULL);
}
static int rvin_s_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
struct rvin_dev *vin = video_drvdata(file);
- struct v4l2_rect crop, compose;
+ struct v4l2_rect fmt_rect, src_rect;
int ret;
if (vb2_is_busy(&vin->queue))
return -EBUSY;
ret = rvin_try_format(vin, V4L2_SUBDEV_FORMAT_ACTIVE, &f->fmt.pix,
- &crop, &compose);
+ &src_rect);
if (ret)
return ret;
vin->format = f->fmt.pix;
- vin->crop = crop;
- vin->compose = compose;
- vin->source = crop;
+
+ fmt_rect.top = 0;
+ fmt_rect.left = 0;
+ fmt_rect.width = vin->format.width;
+ fmt_rect.height = vin->format.height;
+
+ v4l2_rect_map_inside(&vin->crop, &src_rect);
+ v4l2_rect_map_inside(&vin->compose, &fmt_rect);
+ vin->src_rect = src_rect;
return 0;
}
@@ -303,12 +362,65 @@
static int rvin_enum_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_fmtdesc *f)
{
- if (f->index >= ARRAY_SIZE(rvin_formats))
+ struct rvin_dev *vin = video_drvdata(file);
+ 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 MEDIA_BUS_FMT_SRGGB12_1X12 format is added to the
+ * driver this needs to be extended so raw media bus code only result in
+ * raw pixel format.
+ */
+ 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;
+ case MEDIA_BUS_FMT_SBGGR8_1X8:
+ if (f->index)
+ return -EINVAL;
+ f->pixelformat = V4L2_PIX_FMT_SBGGR8;
+ return 0;
+ case MEDIA_BUS_FMT_SGBRG8_1X8:
+ if (f->index)
+ return -EINVAL;
+ f->pixelformat = V4L2_PIX_FMT_SGBRG8;
+ return 0;
+ case MEDIA_BUS_FMT_SGRBG8_1X8:
+ if (f->index)
+ return -EINVAL;
+ f->pixelformat = V4L2_PIX_FMT_SGRBG8;
+ return 0;
+ case MEDIA_BUS_FMT_SRGGB8_1X8:
+ if (f->index)
+ return -EINVAL;
+ f->pixelformat = V4L2_PIX_FMT_SRGGB8;
+ return 0;
+ default:
return -EINVAL;
+ }
- f->pixelformat = rvin_formats[f->index].fourcc;
+ matched = -1;
+ for (i = 0; i < ARRAY_SIZE(rvin_formats); i++) {
+ if (rvin_format_from_pixel(vin, rvin_formats[i].fourcc))
+ matched++;
- return 0;
+ if (matched == f->index) {
+ f->pixelformat = rvin_formats[i].fourcc;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
}
static int rvin_g_selection(struct file *file, void *fh,
@@ -323,8 +435,8 @@
case V4L2_SEL_TGT_CROP_BOUNDS:
case V4L2_SEL_TGT_CROP_DEFAULT:
s->r.left = s->r.top = 0;
- s->r.width = vin->source.width;
- s->r.height = vin->source.height;
+ s->r.width = vin->src_rect.width;
+ s->r.height = vin->src_rect.height;
break;
case V4L2_SEL_TGT_CROP:
s->r = vin->crop;
@@ -366,21 +478,22 @@
case V4L2_SEL_TGT_CROP:
/* Can't crop outside of source input */
max_rect.top = max_rect.left = 0;
- max_rect.width = vin->source.width;
- max_rect.height = vin->source.height;
+ max_rect.width = vin->src_rect.width;
+ max_rect.height = vin->src_rect.height;
v4l2_rect_map_inside(&r, &max_rect);
- v4l_bound_align_image(&r.width, 6, vin->source.width, 0,
- &r.height, 2, vin->source.height, 0, 0);
+ v4l_bound_align_image(&r.width, 6, vin->src_rect.width, 0,
+ &r.height, 2, vin->src_rect.height, 0, 0);
- r.top = clamp_t(s32, r.top, 0, vin->source.height - r.height);
- r.left = clamp_t(s32, r.left, 0, vin->source.width - r.width);
+ r.top = clamp_t(s32, r.top, 0,
+ vin->src_rect.height - r.height);
+ r.left = clamp_t(s32, r.left, 0, vin->src_rect.width - r.width);
vin->crop = s->r = r;
vin_dbg(vin, "Cropped %dx%d@%d:%d of %dx%d\n",
r.width, r.height, r.left, r.top,
- vin->source.width, vin->source.height);
+ vin->src_rect.width, vin->src_rect.height);
break;
case V4L2_SEL_TGT_COMPOSE:
/* Make sure compose rect fits inside output format */
@@ -720,18 +833,6 @@
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,
@@ -739,10 +840,6 @@
.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,
@@ -781,8 +878,10 @@
int ret;
ret = pm_runtime_get_sync(vin->dev);
- if (ret < 0)
+ if (ret < 0) {
+ pm_runtime_put_noidle(vin->dev);
return ret;
+ }
ret = mutex_lock_interruptible(&vin->lock);
if (ret)
@@ -795,7 +894,7 @@
goto err_unlock;
if (vin->info->use_mc)
- ret = v4l2_pipeline_pm_use(&vin->vdev.entity, 1);
+ ret = v4l2_pipeline_pm_get(&vin->vdev.entity);
else if (v4l2_fh_is_singular_file(file))
ret = rvin_power_parallel(vin, true);
@@ -811,7 +910,7 @@
return 0;
err_power:
if (vin->info->use_mc)
- v4l2_pipeline_pm_use(&vin->vdev.entity, 0);
+ v4l2_pipeline_pm_put(&vin->vdev.entity);
else if (v4l2_fh_is_singular_file(file))
rvin_power_parallel(vin, false);
err_open:
@@ -839,7 +938,7 @@
ret = _vb2_fop_release(file, NULL);
if (vin->info->use_mc) {
- v4l2_pipeline_pm_use(&vin->vdev.entity, 0);
+ v4l2_pipeline_pm_put(&vin->vdev.entity);
} else {
if (fh_singular)
rvin_power_parallel(vin, false);
@@ -914,6 +1013,7 @@
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;
@@ -922,7 +1022,7 @@
rvin_format_align(vin, &vin->format);
- ret = video_register_device(&vin->vdev, VFL_TYPE_GRABBER, -1);
+ ret = video_register_device(&vin->vdev, VFL_TYPE_VIDEO, -1);
if (ret) {
vin_err(vin, "Failed to register video device\n");
return ret;
diff --git a/drivers/media/platform/rcar-vin/rcar-vin.h b/drivers/media/platform/rcar-vin/rcar-vin.h
index e562c2f..8396e0e 100644
--- a/drivers/media/platform/rcar-vin/rcar-vin.h
+++ b/drivers/media/platform/rcar-vin/rcar-vin.h
@@ -19,6 +19,7 @@
#include <media/v4l2-ctrls.h>
#include <media/v4l2-dev.h>
#include <media/v4l2-device.h>
+#include <media/v4l2-fwnode.h>
#include <media/videobuf2-v4l2.h>
/* Number of HW buffers */
@@ -61,6 +62,23 @@
};
/**
+ * enum rvin_buffer_type
+ *
+ * Describes how a buffer is given to the hardware. To be able
+ * to capture SEQ_TB/BT it's needed to capture to the same vb2
+ * buffer twice so the type of buffer needs to be kept.
+ *
+ * FULL - One capture fills the whole vb2 buffer
+ * HALF_TOP - One capture fills the top half of the vb2 buffer
+ * HALF_BOTTOM - One capture fills the bottom half of the vb2 buffer
+ */
+enum rvin_buffer_type {
+ FULL,
+ HALF_TOP,
+ HALF_BOTTOM,
+};
+
+/**
* struct rvin_video_format - Data format stored in memory
* @fourcc: Pixelformat
* @bpp: Bytes per pixel
@@ -75,7 +93,7 @@
* @asd: sub-device descriptor for async framework
* @subdev: subdevice matched using async framework
* @mbus_type: media bus type
- * @mbus_flags: media bus configuration flags
+ * @bus: media bus parallel configuration
* @source_pad: source pad of remote subdevice
* @sink_pad: sink pad of remote subdevice
*
@@ -85,7 +103,7 @@
struct v4l2_subdev *subdev;
enum v4l2_mbus_type mbus_type;
- unsigned int mbus_flags;
+ struct v4l2_fwnode_bus_parallel bus;
unsigned int source_pad;
unsigned int sink_pad;
@@ -126,6 +144,7 @@
* struct rvin_info - Information about the particular VIN implementation
* @model: VIN model
* @use_mc: use media controller instead of controlling subdevice
+ * @nv12: support outputing NV12 pixel format
* @max_width: max input width the VIN supports
* @max_height: max input height the VIN supports
* @routes: list of possible routes from the CSI-2 recivers to
@@ -134,6 +153,7 @@
struct rvin_info {
enum model_id model;
bool use_mc;
+ bool nv12;
unsigned int max_width;
unsigned int max_height;
@@ -162,9 +182,8 @@
* @scratch: cpu address for scratch buffer
* @scratch_phys: physical address of the scratch buffer
*
- * @qlock: protects @queue_buf, @buf_list, @sequence
- * @state
- * @queue_buf: Keeps track of buffers given to HW slot
+ * @qlock: protects @buf_hw, @buf_list, @sequence and @state
+ * @buf_hw: Keeps track of buffers given to HW slot
* @buf_list: list of queued buffers
* @sequence: V4L2 buffers sequence number
* @state: keeps track of operation state
@@ -176,7 +195,7 @@
*
* @crop: active cropping
* @compose: active composing
- * @source: active size of the video source
+ * @src_rect: active size of the video source
* @std: active video standard of the video source
*
* @alpha: Alpha component to fill in for supported pixel formats
@@ -203,7 +222,11 @@
dma_addr_t scratch_phys;
spinlock_t qlock;
- struct vb2_v4l2_buffer *queue_buf[HW_BUFFER_NUM];
+ struct {
+ struct vb2_v4l2_buffer *buffer;
+ enum rvin_buffer_type type;
+ dma_addr_t phys;
+ } buf_hw[HW_BUFFER_NUM];
struct list_head buf_list;
unsigned int sequence;
enum rvin_dma_state state;
@@ -215,7 +238,7 @@
struct v4l2_rect crop;
struct v4l2_rect compose;
- struct v4l2_rect source;
+ struct v4l2_rect src_rect;
v4l2_std_id std;
unsigned int alpha;
diff --git a/drivers/media/platform/rcar_drif.c b/drivers/media/platform/rcar_drif.c
index ae9a5f3..083dba9 100644
--- a/drivers/media/platform/rcar_drif.c
+++ b/drivers/media/platform/rcar_drif.c
@@ -274,10 +274,14 @@
for_each_rcar_drif_channel(i, &sdr->cur_ch_mask) {
struct rcar_drif *ch = sdr->ch[i];
- ch->dmach = dma_request_slave_channel(&ch->pdev->dev, "rx");
- if (!ch->dmach) {
- rdrif_err(sdr, "ch%u: dma channel req failed\n", i);
- ret = -ENODEV;
+ ch->dmach = dma_request_chan(&ch->pdev->dev, "rx");
+ if (IS_ERR(ch->dmach)) {
+ ret = PTR_ERR(ch->dmach);
+ if (ret != -EPROBE_DEFER)
+ rdrif_err(sdr,
+ "ch%u: dma channel req failed: %pe\n",
+ i, ch->dmach);
+ ch->dmach = NULL;
goto dmach_error;
}
diff --git a/drivers/media/platform/rcar_fdp1.c b/drivers/media/platform/rcar_fdp1.c
index 97bed45..c9448de 100644
--- a/drivers/media/platform/rcar_fdp1.c
+++ b/drivers/media/platform/rcar_fdp1.c
@@ -2344,7 +2344,7 @@
video_set_drvdata(vfd, fdp1);
strscpy(vfd->name, fdp1_videodev.name, sizeof(vfd->name));
- ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0);
+ ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0);
if (ret) {
v4l2_err(&fdp1->v4l2_dev, "Failed to register video device\n");
goto release_m2m;
diff --git a/drivers/media/platform/rcar_jpu.c b/drivers/media/platform/rcar_jpu.c
index 1c3f507..9b99ff3 100644
--- a/drivers/media/platform/rcar_jpu.c
+++ b/drivers/media/platform/rcar_jpu.c
@@ -1066,7 +1066,7 @@
}
/* decoder capture queue */
- if (!ctx->encoder && !V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type))
+ if (!ctx->encoder && V4L2_TYPE_IS_CAPTURE(vb->vb2_queue->type))
vb2_set_plane_payload(vb, i, size);
}
@@ -1663,7 +1663,7 @@
jpu->vfd_encoder.device_caps = V4L2_CAP_STREAMING |
V4L2_CAP_VIDEO_M2M_MPLANE;
- ret = video_register_device(&jpu->vfd_encoder, VFL_TYPE_GRABBER, -1);
+ ret = video_register_device(&jpu->vfd_encoder, VFL_TYPE_VIDEO, -1);
if (ret) {
v4l2_err(&jpu->v4l2_dev, "Failed to register video device\n");
goto m2m_init_rollback;
@@ -1682,7 +1682,7 @@
jpu->vfd_decoder.device_caps = V4L2_CAP_STREAMING |
V4L2_CAP_VIDEO_M2M_MPLANE;
- ret = video_register_device(&jpu->vfd_decoder, VFL_TYPE_GRABBER, -1);
+ ret = video_register_device(&jpu->vfd_decoder, VFL_TYPE_VIDEO, -1);
if (ret) {
v4l2_err(&jpu->v4l2_dev, "Failed to register video device\n");
goto enc_vdev_register_rollback;
diff --git a/drivers/media/platform/renesas-ceu.c b/drivers/media/platform/renesas-ceu.c
index 197b399..4a633ad 100644
--- a/drivers/media/platform/renesas-ceu.c
+++ b/drivers/media/platform/renesas-ceu.c
@@ -405,7 +405,7 @@
/* Non-swapped planar image capture mode. */
case V4L2_PIX_FMT_NV16:
cdocr |= CEU_CDOCR_NO_DOWSAMPLE;
- /* fall-through */
+ fallthrough;
case V4L2_PIX_FMT_NV12:
if (mbus_fmt->swapped)
camcr = mbus_fmt->fmt_order_swap;
@@ -419,7 +419,7 @@
/* Swapped planar image capture mode. */
case V4L2_PIX_FMT_NV61:
cdocr |= CEU_CDOCR_NO_DOWSAMPLE;
- /* fall-through */
+ fallthrough;
case V4L2_PIX_FMT_NV21:
if (mbus_fmt->swapped)
camcr = mbus_fmt->fmt_order;
@@ -1450,7 +1450,7 @@
V4L2_CAP_STREAMING;
video_set_drvdata(vdev, ceudev);
- ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
+ ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
if (ret < 0) {
v4l2_err(vdev->v4l2_dev,
"video_register_device failed: %d\n", ret);
diff --git a/drivers/media/platform/rockchip/rga/rga-buf.c b/drivers/media/platform/rockchip/rga/rga-buf.c
index bf9a75b..81508ed 100644
--- a/drivers/media/platform/rockchip/rga/rga-buf.c
+++ b/drivers/media/platform/rockchip/rga/rga-buf.c
@@ -79,9 +79,8 @@
struct rockchip_rga *rga = ctx->rga;
int ret;
- ret = pm_runtime_get_sync(rga->dev);
+ ret = pm_runtime_resume_and_get(rga->dev);
if (ret < 0) {
- pm_runtime_put_noidle(rga->dev);
rga_buf_return_buffers(q, VB2_BUF_STATE_QUEUED);
return ret;
}
diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c
index e9ff12b..6759091 100644
--- a/drivers/media/platform/rockchip/rga/rga.c
+++ b/drivers/media/platform/rockchip/rga/rga.c
@@ -863,10 +863,12 @@
if (IS_ERR(rga->m2m_dev)) {
v4l2_err(&rga->v4l2_dev, "Failed to init mem2mem device\n");
ret = PTR_ERR(rga->m2m_dev);
- goto unreg_video_dev;
+ goto rel_vdev;
}
- pm_runtime_get_sync(rga->dev);
+ ret = pm_runtime_resume_and_get(rga->dev);
+ if (ret < 0)
+ goto rel_vdev;
rga->version.major = (rga_read(rga, RGA_VERSION_INFO) >> 24) & 0xFF;
rga->version.minor = (rga_read(rga, RGA_VERSION_INFO) >> 20) & 0x0F;
@@ -880,19 +882,31 @@
rga->cmdbuf_virt = dma_alloc_attrs(rga->dev, RGA_CMDBUF_SIZE,
&rga->cmdbuf_phy, GFP_KERNEL,
DMA_ATTR_WRITE_COMBINE);
+ if (!rga->cmdbuf_virt) {
+ ret = -ENOMEM;
+ goto rel_vdev;
+ }
rga->src_mmu_pages =
(unsigned int *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, 3);
+ if (!rga->src_mmu_pages) {
+ ret = -ENOMEM;
+ goto free_dma;
+ }
rga->dst_mmu_pages =
(unsigned int *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, 3);
+ if (rga->dst_mmu_pages) {
+ ret = -ENOMEM;
+ goto free_src_pages;
+ }
def_frame.stride = (def_frame.width * def_frame.fmt->depth) >> 3;
def_frame.size = def_frame.stride * def_frame.height;
- ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1);
+ ret = video_register_device(vfd, VFL_TYPE_VIDEO, -1);
if (ret) {
v4l2_err(&rga->v4l2_dev, "Failed to register video device\n");
- goto rel_vdev;
+ goto free_dst_pages;
}
v4l2_info(&rga->v4l2_dev, "Registered %s as /dev/%s\n",
@@ -900,10 +914,15 @@
return 0;
+free_dst_pages:
+ free_pages((unsigned long)rga->dst_mmu_pages, 3);
+free_src_pages:
+ free_pages((unsigned long)rga->src_mmu_pages, 3);
+free_dma:
+ dma_free_attrs(rga->dev, RGA_CMDBUF_SIZE, rga->cmdbuf_virt,
+ rga->cmdbuf_phy, DMA_ATTR_WRITE_COMBINE);
rel_vdev:
video_device_release(vfd);
-unreg_video_dev:
- video_unregister_device(rga->vfd);
unreg_v4l2_dev:
v4l2_device_unregister(&rga->v4l2_dev);
err_put_clk:
diff --git a/drivers/media/platform/s3c-camif/camif-capture.c b/drivers/media/platform/s3c-camif/camif-capture.c
index 2fb45db..9ca49af 100644
--- a/drivers/media/platform/s3c-camif/camif-capture.c
+++ b/drivers/media/platform/s3c-camif/camif-capture.c
@@ -1158,7 +1158,7 @@
vfd->ctrl_handler = &vp->ctrl_handler;
vfd->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE;
- ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1);
+ ret = video_register_device(vfd, VFL_TYPE_VIDEO, -1);
if (ret)
goto err_ctrlh_free;
diff --git a/drivers/media/platform/s3c-camif/camif-core.c b/drivers/media/platform/s3c-camif/camif-core.c
index ee62480..422fd54 100644
--- a/drivers/media/platform/s3c-camif/camif-core.c
+++ b/drivers/media/platform/s3c-camif/camif-core.c
@@ -304,7 +304,7 @@
int ret;
memset(md, 0, sizeof(*md));
- snprintf(md->model, sizeof(md->model), "SAMSUNG S3C%s CAMIF",
+ snprintf(md->model, sizeof(md->model), "Samsung S3C%s CAMIF",
ip_rev == S3C6410_CAMIF_IP_REV ? "6410" : "244X");
strscpy(md->bus_info, "platform", sizeof(md->bus_info));
md->hw_revision = ip_rev;
diff --git a/drivers/media/platform/s3c-camif/camif-regs.c b/drivers/media/platform/s3c-camif/camif-regs.c
index 1a65532..e80204f 100644
--- a/drivers/media/platform/s3c-camif/camif-regs.c
+++ b/drivers/media/platform/s3c-camif/camif-regs.c
@@ -553,7 +553,7 @@
void camif_hw_dump_regs(struct camif_dev *camif, const char *label)
{
- struct {
+ static const struct {
u32 offset;
const char * const name;
} registers[] = {
diff --git a/drivers/media/platform/s5p-cec/Makefile b/drivers/media/platform/s5p-cec/Makefile
deleted file mode 100644
index bd0103b..0000000
--- a/drivers/media/platform/s5p-cec/Makefile
+++ /dev/null
@@ -1,3 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0-only
-obj-$(CONFIG_VIDEO_SAMSUNG_S5P_CEC) += 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/platform/s5p-cec/exynos_hdmi_cec.h
deleted file mode 100644
index 325db8c..0000000
--- a/drivers/media/platform/s5p-cec/exynos_hdmi_cec.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-/* drivers/media/platform/s5p-cec/exynos_hdmi_cec.h
- *
- * Copyright (c) 2010, 2014 Samsung Electronics
- * http://www.samsung.com/
- *
- * Header file for interface of Samsung Exynos hdmi cec hardware
- */
-
-#ifndef _EXYNOS_HDMI_CEC_H_
-#define _EXYNOS_HDMI_CEC_H_ __FILE__
-
-#include <linux/regmap.h>
-#include "s5p_cec.h"
-
-void s5p_cec_set_divider(struct s5p_cec_dev *cec);
-void s5p_cec_enable_rx(struct s5p_cec_dev *cec);
-void s5p_cec_mask_rx_interrupts(struct s5p_cec_dev *cec);
-void s5p_cec_unmask_rx_interrupts(struct s5p_cec_dev *cec);
-void s5p_cec_mask_tx_interrupts(struct s5p_cec_dev *cec);
-void s5p_cec_unmask_tx_interrupts(struct s5p_cec_dev *cec);
-void s5p_cec_reset(struct s5p_cec_dev *cec);
-void s5p_cec_tx_reset(struct s5p_cec_dev *cec);
-void s5p_cec_rx_reset(struct s5p_cec_dev *cec);
-void s5p_cec_threshold(struct s5p_cec_dev *cec);
-void s5p_cec_copy_packet(struct s5p_cec_dev *cec, char *data,
- size_t count, u8 retries);
-void s5p_cec_set_addr(struct s5p_cec_dev *cec, u32 addr);
-u32 s5p_cec_get_status(struct s5p_cec_dev *cec);
-void s5p_clr_pending_tx(struct s5p_cec_dev *cec);
-void s5p_clr_pending_rx(struct s5p_cec_dev *cec);
-void s5p_cec_get_rx_buf(struct s5p_cec_dev *cec, u32 size, u8 *buffer);
-
-#endif /* _EXYNOS_HDMI_CEC_H_ */
diff --git a/drivers/media/platform/s5p-cec/exynos_hdmi_cecctrl.c b/drivers/media/platform/s5p-cec/exynos_hdmi_cecctrl.c
deleted file mode 100644
index eb981eb..0000000
--- a/drivers/media/platform/s5p-cec/exynos_hdmi_cecctrl.c
+++ /dev/null
@@ -1,206 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/* drivers/media/platform/s5p-cec/exynos_hdmi_cecctrl.c
- *
- * Copyright (c) 2009, 2014 Samsung Electronics
- * http://www.samsung.com/
- *
- * cec ftn file for Samsung TVOUT driver
- */
-
-#include <linux/io.h>
-#include <linux/device.h>
-
-#include "exynos_hdmi_cec.h"
-#include "regs-cec.h"
-
-#define S5P_HDMI_FIN 24000000
-#define CEC_DIV_RATIO 320000
-
-#define CEC_MESSAGE_BROADCAST_MASK 0x0F
-#define CEC_MESSAGE_BROADCAST 0x0F
-#define CEC_FILTER_THRESHOLD 0x15
-
-void s5p_cec_set_divider(struct s5p_cec_dev *cec)
-{
- u32 div_ratio, div_val;
- unsigned int reg;
-
- div_ratio = S5P_HDMI_FIN / CEC_DIV_RATIO - 1;
-
- if (regmap_read(cec->pmu, EXYNOS_HDMI_PHY_CONTROL, ®)) {
- dev_err(cec->dev, "failed to read phy control\n");
- return;
- }
-
- reg = (reg & ~(0x3FF << 16)) | (div_ratio << 16);
-
- if (regmap_write(cec->pmu, EXYNOS_HDMI_PHY_CONTROL, reg)) {
- dev_err(cec->dev, "failed to write phy control\n");
- return;
- }
-
- div_val = CEC_DIV_RATIO * 0.00005 - 1;
-
- writeb(0x0, cec->reg + S5P_CEC_DIVISOR_3);
- writeb(0x0, cec->reg + S5P_CEC_DIVISOR_2);
- writeb(0x0, cec->reg + S5P_CEC_DIVISOR_1);
- writeb(div_val, cec->reg + S5P_CEC_DIVISOR_0);
-}
-
-void s5p_cec_enable_rx(struct s5p_cec_dev *cec)
-{
- u8 reg;
-
- reg = readb(cec->reg + S5P_CEC_RX_CTRL);
- reg |= S5P_CEC_RX_CTRL_ENABLE;
- writeb(reg, cec->reg + S5P_CEC_RX_CTRL);
-}
-
-void s5p_cec_mask_rx_interrupts(struct s5p_cec_dev *cec)
-{
- u8 reg;
-
- reg = readb(cec->reg + S5P_CEC_IRQ_MASK);
- reg |= S5P_CEC_IRQ_RX_DONE;
- reg |= S5P_CEC_IRQ_RX_ERROR;
- writeb(reg, cec->reg + S5P_CEC_IRQ_MASK);
-}
-
-void s5p_cec_unmask_rx_interrupts(struct s5p_cec_dev *cec)
-{
- u8 reg;
-
- reg = readb(cec->reg + S5P_CEC_IRQ_MASK);
- reg &= ~S5P_CEC_IRQ_RX_DONE;
- reg &= ~S5P_CEC_IRQ_RX_ERROR;
- writeb(reg, cec->reg + S5P_CEC_IRQ_MASK);
-}
-
-void s5p_cec_mask_tx_interrupts(struct s5p_cec_dev *cec)
-{
- u8 reg;
-
- reg = readb(cec->reg + S5P_CEC_IRQ_MASK);
- reg |= S5P_CEC_IRQ_TX_DONE;
- reg |= S5P_CEC_IRQ_TX_ERROR;
- writeb(reg, cec->reg + S5P_CEC_IRQ_MASK);
-}
-
-void s5p_cec_unmask_tx_interrupts(struct s5p_cec_dev *cec)
-{
- u8 reg;
-
- reg = readb(cec->reg + S5P_CEC_IRQ_MASK);
- reg &= ~S5P_CEC_IRQ_TX_DONE;
- reg &= ~S5P_CEC_IRQ_TX_ERROR;
- writeb(reg, cec->reg + S5P_CEC_IRQ_MASK);
-}
-
-void s5p_cec_reset(struct s5p_cec_dev *cec)
-{
- u8 reg;
-
- writeb(S5P_CEC_RX_CTRL_RESET, cec->reg + S5P_CEC_RX_CTRL);
- writeb(S5P_CEC_TX_CTRL_RESET, cec->reg + S5P_CEC_TX_CTRL);
-
- reg = readb(cec->reg + 0xc4);
- reg &= ~0x1;
- writeb(reg, cec->reg + 0xc4);
-}
-
-void s5p_cec_tx_reset(struct s5p_cec_dev *cec)
-{
- writeb(S5P_CEC_TX_CTRL_RESET, cec->reg + S5P_CEC_TX_CTRL);
-}
-
-void s5p_cec_rx_reset(struct s5p_cec_dev *cec)
-{
- u8 reg;
-
- writeb(S5P_CEC_RX_CTRL_RESET, cec->reg + S5P_CEC_RX_CTRL);
-
- reg = readb(cec->reg + 0xc4);
- reg &= ~0x1;
- writeb(reg, cec->reg + 0xc4);
-}
-
-void s5p_cec_threshold(struct s5p_cec_dev *cec)
-{
- writeb(CEC_FILTER_THRESHOLD, cec->reg + S5P_CEC_RX_FILTER_TH);
- writeb(0, cec->reg + S5P_CEC_RX_FILTER_CTRL);
-}
-
-void s5p_cec_copy_packet(struct s5p_cec_dev *cec, char *data,
- size_t count, u8 retries)
-{
- int i = 0;
- u8 reg;
-
- while (i < count) {
- writeb(data[i], cec->reg + (S5P_CEC_TX_BUFF0 + (i * 4)));
- i++;
- }
-
- writeb(count, cec->reg + S5P_CEC_TX_BYTES);
- reg = readb(cec->reg + S5P_CEC_TX_CTRL);
- reg |= S5P_CEC_TX_CTRL_START;
- reg &= ~0x70;
- reg |= retries << 4;
-
- if ((data[0] & CEC_MESSAGE_BROADCAST_MASK) == CEC_MESSAGE_BROADCAST) {
- dev_dbg(cec->dev, "Broadcast");
- reg |= S5P_CEC_TX_CTRL_BCAST;
- } else {
- dev_dbg(cec->dev, "No Broadcast");
- reg &= ~S5P_CEC_TX_CTRL_BCAST;
- }
-
- writeb(reg, cec->reg + S5P_CEC_TX_CTRL);
- dev_dbg(cec->dev, "cec-tx: cec count (%zu): %*ph", count,
- (int)count, data);
-}
-
-void s5p_cec_set_addr(struct s5p_cec_dev *cec, u32 addr)
-{
- writeb(addr & 0x0F, cec->reg + S5P_CEC_LOGIC_ADDR);
-}
-
-u32 s5p_cec_get_status(struct s5p_cec_dev *cec)
-{
- u32 status = 0;
-
- status = readb(cec->reg + S5P_CEC_STATUS_0) & 0xf;
- status |= (readb(cec->reg + S5P_CEC_TX_STAT1) & 0xf) << 4;
- status |= readb(cec->reg + S5P_CEC_STATUS_1) << 8;
- status |= readb(cec->reg + S5P_CEC_STATUS_2) << 16;
- status |= readb(cec->reg + S5P_CEC_STATUS_3) << 24;
-
- dev_dbg(cec->dev, "status = 0x%x!\n", status);
-
- return status;
-}
-
-void s5p_clr_pending_tx(struct s5p_cec_dev *cec)
-{
- writeb(S5P_CEC_IRQ_TX_DONE | S5P_CEC_IRQ_TX_ERROR,
- cec->reg + S5P_CEC_IRQ_CLEAR);
-}
-
-void s5p_clr_pending_rx(struct s5p_cec_dev *cec)
-{
- writeb(S5P_CEC_IRQ_RX_DONE | S5P_CEC_IRQ_RX_ERROR,
- cec->reg + S5P_CEC_IRQ_CLEAR);
-}
-
-void s5p_cec_get_rx_buf(struct s5p_cec_dev *cec, u32 size, u8 *buffer)
-{
- u32 i = 0;
- char debug[40];
-
- while (i < size) {
- buffer[i] = readb(cec->reg + S5P_CEC_RX_BUFF0 + (i * 4));
- sprintf(debug + i * 2, "%02x ", buffer[i]);
- i++;
- }
- dev_dbg(cec->dev, "cec-rx: cec size(%d): %s", size, debug);
-}
diff --git a/drivers/media/platform/s5p-cec/regs-cec.h b/drivers/media/platform/s5p-cec/regs-cec.h
deleted file mode 100644
index 447f717..0000000
--- a/drivers/media/platform/s5p-cec/regs-cec.h
+++ /dev/null
@@ -1,93 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-/* drivers/media/platform/s5p-cec/regs-cec.h
- *
- * Copyright (c) 2010 Samsung Electronics
- * http://www.samsung.com/
- *
- * register header file for Samsung TVOUT driver
- */
-
-#ifndef __EXYNOS_REGS__H
-#define __EXYNOS_REGS__H
-
-/*
- * Register part
- */
-#define S5P_CEC_STATUS_0 (0x0000)
-#define S5P_CEC_STATUS_1 (0x0004)
-#define S5P_CEC_STATUS_2 (0x0008)
-#define S5P_CEC_STATUS_3 (0x000C)
-#define S5P_CEC_IRQ_MASK (0x0010)
-#define S5P_CEC_IRQ_CLEAR (0x0014)
-#define S5P_CEC_LOGIC_ADDR (0x0020)
-#define S5P_CEC_DIVISOR_0 (0x0030)
-#define S5P_CEC_DIVISOR_1 (0x0034)
-#define S5P_CEC_DIVISOR_2 (0x0038)
-#define S5P_CEC_DIVISOR_3 (0x003C)
-
-#define S5P_CEC_TX_CTRL (0x0040)
-#define S5P_CEC_TX_BYTES (0x0044)
-#define S5P_CEC_TX_STAT0 (0x0060)
-#define S5P_CEC_TX_STAT1 (0x0064)
-#define S5P_CEC_TX_BUFF0 (0x0080)
-#define S5P_CEC_TX_BUFF1 (0x0084)
-#define S5P_CEC_TX_BUFF2 (0x0088)
-#define S5P_CEC_TX_BUFF3 (0x008C)
-#define S5P_CEC_TX_BUFF4 (0x0090)
-#define S5P_CEC_TX_BUFF5 (0x0094)
-#define S5P_CEC_TX_BUFF6 (0x0098)
-#define S5P_CEC_TX_BUFF7 (0x009C)
-#define S5P_CEC_TX_BUFF8 (0x00A0)
-#define S5P_CEC_TX_BUFF9 (0x00A4)
-#define S5P_CEC_TX_BUFF10 (0x00A8)
-#define S5P_CEC_TX_BUFF11 (0x00AC)
-#define S5P_CEC_TX_BUFF12 (0x00B0)
-#define S5P_CEC_TX_BUFF13 (0x00B4)
-#define S5P_CEC_TX_BUFF14 (0x00B8)
-#define S5P_CEC_TX_BUFF15 (0x00BC)
-
-#define S5P_CEC_RX_CTRL (0x00C0)
-#define S5P_CEC_RX_STAT0 (0x00E0)
-#define S5P_CEC_RX_STAT1 (0x00E4)
-#define S5P_CEC_RX_BUFF0 (0x0100)
-#define S5P_CEC_RX_BUFF1 (0x0104)
-#define S5P_CEC_RX_BUFF2 (0x0108)
-#define S5P_CEC_RX_BUFF3 (0x010C)
-#define S5P_CEC_RX_BUFF4 (0x0110)
-#define S5P_CEC_RX_BUFF5 (0x0114)
-#define S5P_CEC_RX_BUFF6 (0x0118)
-#define S5P_CEC_RX_BUFF7 (0x011C)
-#define S5P_CEC_RX_BUFF8 (0x0120)
-#define S5P_CEC_RX_BUFF9 (0x0124)
-#define S5P_CEC_RX_BUFF10 (0x0128)
-#define S5P_CEC_RX_BUFF11 (0x012C)
-#define S5P_CEC_RX_BUFF12 (0x0130)
-#define S5P_CEC_RX_BUFF13 (0x0134)
-#define S5P_CEC_RX_BUFF14 (0x0138)
-#define S5P_CEC_RX_BUFF15 (0x013C)
-
-#define S5P_CEC_RX_FILTER_CTRL (0x0180)
-#define S5P_CEC_RX_FILTER_TH (0x0184)
-
-/*
- * Bit definition part
- */
-#define S5P_CEC_IRQ_TX_DONE (1<<0)
-#define S5P_CEC_IRQ_TX_ERROR (1<<1)
-#define S5P_CEC_IRQ_RX_DONE (1<<4)
-#define S5P_CEC_IRQ_RX_ERROR (1<<5)
-
-#define S5P_CEC_TX_CTRL_START (1<<0)
-#define S5P_CEC_TX_CTRL_BCAST (1<<1)
-#define S5P_CEC_TX_CTRL_RETRY (0x04<<4)
-#define S5P_CEC_TX_CTRL_RESET (1<<7)
-
-#define S5P_CEC_RX_CTRL_ENABLE (1<<0)
-#define S5P_CEC_RX_CTRL_RESET (1<<7)
-
-#define S5P_CEC_LOGIC_ADDR_MASK (0xF)
-
-/* PMU Registers for PHY */
-#define EXYNOS_HDMI_PHY_CONTROL 0x700
-
-#endif /* __EXYNOS_REGS__H */
diff --git a/drivers/media/platform/s5p-cec/s5p_cec.c b/drivers/media/platform/s5p-cec/s5p_cec.c
deleted file mode 100644
index 828792b..0000000
--- a/drivers/media/platform/s5p-cec/s5p_cec.c
+++ /dev/null
@@ -1,310 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/* drivers/media/platform/s5p-cec/s5p_cec.c
- *
- * Samsung S5P CEC driver
- *
- * Copyright (c) 2014 Samsung Electronics Co., Ltd.
- *
- * This driver is based on the "cec interface driver for exynos soc" by
- * SangPil Moon.
- */
-
-#include <linux/clk.h>
-#include <linux/interrupt.h>
-#include <linux/kernel.h>
-#include <linux/mfd/syscon.h>
-#include <linux/module.h>
-#include <linux/of.h>
-#include <linux/of_platform.h>
-#include <linux/platform_device.h>
-#include <linux/pm_runtime.h>
-#include <linux/timer.h>
-#include <linux/workqueue.h>
-#include <media/cec.h>
-#include <media/cec-notifier.h>
-
-#include "exynos_hdmi_cec.h"
-#include "regs-cec.h"
-#include "s5p_cec.h"
-
-#define CEC_NAME "s5p-cec"
-
-static int debug;
-module_param(debug, int, 0644);
-MODULE_PARM_DESC(debug, "debug level (0-2)");
-
-static int s5p_cec_adap_enable(struct cec_adapter *adap, bool enable)
-{
- int ret;
- struct s5p_cec_dev *cec = cec_get_drvdata(adap);
-
- if (enable) {
- ret = pm_runtime_resume_and_get(cec->dev);
- if (ret < 0)
- return ret;
-
- s5p_cec_reset(cec);
-
- s5p_cec_set_divider(cec);
- s5p_cec_threshold(cec);
-
- s5p_cec_unmask_tx_interrupts(cec);
- s5p_cec_unmask_rx_interrupts(cec);
- s5p_cec_enable_rx(cec);
- } else {
- s5p_cec_mask_tx_interrupts(cec);
- s5p_cec_mask_rx_interrupts(cec);
- pm_runtime_put(cec->dev);
- }
-
- return 0;
-}
-
-static int s5p_cec_adap_log_addr(struct cec_adapter *adap, u8 addr)
-{
- struct s5p_cec_dev *cec = cec_get_drvdata(adap);
-
- s5p_cec_set_addr(cec, addr);
- return 0;
-}
-
-static int s5p_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,
- u32 signal_free_time, struct cec_msg *msg)
-{
- struct s5p_cec_dev *cec = cec_get_drvdata(adap);
-
- /*
- * Unclear if 0 retries are allowed by the hardware, so have 1 as
- * the minimum.
- */
- s5p_cec_copy_packet(cec, msg->msg, msg->len, max(1, attempts - 1));
- return 0;
-}
-
-static irqreturn_t s5p_cec_irq_handler(int irq, void *priv)
-{
- struct s5p_cec_dev *cec = priv;
- u32 status = 0;
-
- status = s5p_cec_get_status(cec);
-
- dev_dbg(cec->dev, "irq received\n");
-
- if (status & CEC_STATUS_TX_DONE) {
- if (status & CEC_STATUS_TX_NACK) {
- dev_dbg(cec->dev, "CEC_STATUS_TX_NACK set\n");
- cec->tx = STATE_NACK;
- } else if (status & CEC_STATUS_TX_ERROR) {
- dev_dbg(cec->dev, "CEC_STATUS_TX_ERROR set\n");
- cec->tx = STATE_ERROR;
- } else {
- dev_dbg(cec->dev, "CEC_STATUS_TX_DONE\n");
- cec->tx = STATE_DONE;
- }
- s5p_clr_pending_tx(cec);
- }
-
- if (status & CEC_STATUS_RX_DONE) {
- if (status & CEC_STATUS_RX_ERROR) {
- dev_dbg(cec->dev, "CEC_STATUS_RX_ERROR set\n");
- s5p_cec_rx_reset(cec);
- s5p_cec_enable_rx(cec);
- } else {
- dev_dbg(cec->dev, "CEC_STATUS_RX_DONE set\n");
- if (cec->rx != STATE_IDLE)
- dev_dbg(cec->dev, "Buffer overrun (worker did not process previous message)\n");
- cec->rx = STATE_BUSY;
- cec->msg.len = status >> 24;
- cec->msg.rx_status = CEC_RX_STATUS_OK;
- s5p_cec_get_rx_buf(cec, cec->msg.len,
- cec->msg.msg);
- cec->rx = STATE_DONE;
- s5p_cec_enable_rx(cec);
- }
- /* Clear interrupt pending bit */
- s5p_clr_pending_rx(cec);
- }
- return IRQ_WAKE_THREAD;
-}
-
-static irqreturn_t s5p_cec_irq_handler_thread(int irq, void *priv)
-{
- struct s5p_cec_dev *cec = priv;
-
- dev_dbg(cec->dev, "irq processing thread\n");
- switch (cec->tx) {
- case STATE_DONE:
- cec_transmit_done(cec->adap, CEC_TX_STATUS_OK, 0, 0, 0, 0);
- cec->tx = STATE_IDLE;
- break;
- case STATE_NACK:
- cec_transmit_done(cec->adap,
- CEC_TX_STATUS_MAX_RETRIES | CEC_TX_STATUS_NACK,
- 0, 1, 0, 0);
- cec->tx = STATE_IDLE;
- break;
- case STATE_ERROR:
- cec_transmit_done(cec->adap,
- CEC_TX_STATUS_MAX_RETRIES | CEC_TX_STATUS_ERROR,
- 0, 0, 0, 1);
- cec->tx = STATE_IDLE;
- break;
- case STATE_BUSY:
- dev_err(cec->dev, "state set to busy, this should not occur here\n");
- break;
- default:
- break;
- }
-
- switch (cec->rx) {
- case STATE_DONE:
- cec_received_msg(cec->adap, &cec->msg);
- cec->rx = STATE_IDLE;
- break;
- default:
- break;
- }
-
- return IRQ_HANDLED;
-}
-
-static const struct cec_adap_ops s5p_cec_adap_ops = {
- .adap_enable = s5p_cec_adap_enable,
- .adap_log_addr = s5p_cec_adap_log_addr,
- .adap_transmit = s5p_cec_adap_transmit,
-};
-
-static int s5p_cec_probe(struct platform_device *pdev)
-{
- struct device *dev = &pdev->dev;
- struct device *hdmi_dev;
- struct resource *res;
- struct s5p_cec_dev *cec;
- bool needs_hpd = of_property_read_bool(pdev->dev.of_node, "needs-hpd");
- int ret;
-
- hdmi_dev = cec_notifier_parse_hdmi_phandle(dev);
-
- if (IS_ERR(hdmi_dev))
- return PTR_ERR(hdmi_dev);
-
- cec = devm_kzalloc(&pdev->dev, sizeof(*cec), GFP_KERNEL);
- if (!cec)
- return -ENOMEM;
-
- cec->dev = dev;
-
- cec->irq = platform_get_irq(pdev, 0);
- if (cec->irq < 0)
- return cec->irq;
-
- ret = devm_request_threaded_irq(dev, cec->irq, s5p_cec_irq_handler,
- s5p_cec_irq_handler_thread, 0, pdev->name, cec);
- if (ret)
- return ret;
-
- cec->clk = devm_clk_get(dev, "hdmicec");
- if (IS_ERR(cec->clk))
- return PTR_ERR(cec->clk);
-
- cec->pmu = syscon_regmap_lookup_by_phandle(dev->of_node,
- "samsung,syscon-phandle");
- if (IS_ERR(cec->pmu))
- return -EPROBE_DEFER;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- cec->reg = devm_ioremap_resource(dev, res);
- if (IS_ERR(cec->reg))
- return PTR_ERR(cec->reg);
-
- cec->adap = cec_allocate_adapter(&s5p_cec_adap_ops, cec, CEC_NAME,
- CEC_CAP_DEFAULTS | (needs_hpd ? CEC_CAP_NEEDS_HPD : 0) |
- CEC_CAP_CONNECTOR_INFO, 1);
- ret = PTR_ERR_OR_ZERO(cec->adap);
- if (ret)
- return ret;
-
- cec->notifier = cec_notifier_cec_adap_register(hdmi_dev, NULL,
- cec->adap);
- if (!cec->notifier) {
- ret = -ENOMEM;
- goto err_delete_adapter;
- }
-
- ret = cec_register_adapter(cec->adap, &pdev->dev);
- if (ret)
- goto err_notifier;
-
- platform_set_drvdata(pdev, cec);
- pm_runtime_enable(dev);
-
- dev_dbg(dev, "successfully probed\n");
- return 0;
-
-err_notifier:
- cec_notifier_cec_adap_unregister(cec->notifier);
-
-err_delete_adapter:
- cec_delete_adapter(cec->adap);
- return ret;
-}
-
-static int s5p_cec_remove(struct platform_device *pdev)
-{
- struct s5p_cec_dev *cec = platform_get_drvdata(pdev);
-
- cec_notifier_cec_adap_unregister(cec->notifier);
- cec_unregister_adapter(cec->adap);
- pm_runtime_disable(&pdev->dev);
- return 0;
-}
-
-static int __maybe_unused s5p_cec_runtime_suspend(struct device *dev)
-{
- struct s5p_cec_dev *cec = dev_get_drvdata(dev);
-
- clk_disable_unprepare(cec->clk);
- return 0;
-}
-
-static int __maybe_unused s5p_cec_runtime_resume(struct device *dev)
-{
- struct s5p_cec_dev *cec = dev_get_drvdata(dev);
- int ret;
-
- ret = clk_prepare_enable(cec->clk);
- if (ret < 0)
- return ret;
- return 0;
-}
-
-static const struct dev_pm_ops s5p_cec_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
- pm_runtime_force_resume)
- SET_RUNTIME_PM_OPS(s5p_cec_runtime_suspend, s5p_cec_runtime_resume,
- NULL)
-};
-
-static const struct of_device_id s5p_cec_match[] = {
- {
- .compatible = "samsung,s5p-cec",
- },
- {},
-};
-MODULE_DEVICE_TABLE(of, s5p_cec_match);
-
-static struct platform_driver s5p_cec_pdrv = {
- .probe = s5p_cec_probe,
- .remove = s5p_cec_remove,
- .driver = {
- .name = CEC_NAME,
- .of_match_table = s5p_cec_match,
- .pm = &s5p_cec_pm_ops,
- },
-};
-
-module_platform_driver(s5p_cec_pdrv);
-
-MODULE_AUTHOR("Kamil Debski <kamil@wypas.org>");
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("Samsung S5P CEC driver");
diff --git a/drivers/media/platform/s5p-cec/s5p_cec.h b/drivers/media/platform/s5p-cec/s5p_cec.h
deleted file mode 100644
index 34d033b..0000000
--- a/drivers/media/platform/s5p-cec/s5p_cec.h
+++ /dev/null
@@ -1,76 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* drivers/media/platform/s5p-cec/s5p_cec.h
- *
- * Samsung S5P HDMI CEC driver
- *
- * Copyright (c) 2014 Samsung Electronics Co., Ltd.
- */
-
-#ifndef _S5P_CEC_H_
-#define _S5P_CEC_H_ __FILE__
-
-#include <linux/clk.h>
-#include <linux/interrupt.h>
-#include <linux/kernel.h>
-#include <linux/mfd/syscon.h>
-#include <linux/module.h>
-#include <linux/of.h>
-#include <linux/platform_device.h>
-#include <linux/pm_runtime.h>
-#include <linux/timer.h>
-#include <linux/workqueue.h>
-#include <media/cec.h>
-
-#include "exynos_hdmi_cec.h"
-#include "regs-cec.h"
-#include "s5p_cec.h"
-
-#define CEC_NAME "s5p-cec"
-
-#define CEC_STATUS_TX_RUNNING (1 << 0)
-#define CEC_STATUS_TX_TRANSFERRING (1 << 1)
-#define CEC_STATUS_TX_DONE (1 << 2)
-#define CEC_STATUS_TX_ERROR (1 << 3)
-#define CEC_STATUS_TX_NACK (1 << 4)
-#define CEC_STATUS_TX_BYTES (0xFF << 8)
-#define CEC_STATUS_RX_RUNNING (1 << 16)
-#define CEC_STATUS_RX_RECEIVING (1 << 17)
-#define CEC_STATUS_RX_DONE (1 << 18)
-#define CEC_STATUS_RX_ERROR (1 << 19)
-#define CEC_STATUS_RX_BCAST (1 << 20)
-#define CEC_STATUS_RX_BYTES (0xFF << 24)
-
-#define CEC_WORKER_TX_DONE (1 << 0)
-#define CEC_WORKER_RX_MSG (1 << 1)
-
-/* CEC Rx buffer size */
-#define CEC_RX_BUFF_SIZE 16
-/* CEC Tx buffer size */
-#define CEC_TX_BUFF_SIZE 16
-
-enum cec_state {
- STATE_IDLE,
- STATE_BUSY,
- STATE_DONE,
- STATE_NACK,
- STATE_ERROR
-};
-
-struct cec_notifier;
-
-struct s5p_cec_dev {
- struct cec_adapter *adap;
- struct clk *clk;
- struct device *dev;
- struct mutex lock;
- struct regmap *pmu;
- struct cec_notifier *notifier;
- int irq;
- void __iomem *reg;
-
- enum cec_state rx;
- enum cec_state tx;
- struct cec_msg msg;
-};
-
-#endif /* _S5P_CEC_H_ */
diff --git a/drivers/media/platform/s5p-g2d/g2d.c b/drivers/media/platform/s5p-g2d/g2d.c
index 6cfc2de..1cb5eaa 100644
--- a/drivers/media/platform/s5p-g2d/g2d.c
+++ b/drivers/media/platform/s5p-g2d/g2d.c
@@ -698,21 +698,13 @@
vfd->lock = &dev->mutex;
vfd->v4l2_dev = &dev->v4l2_dev;
vfd->device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING;
- ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0);
- if (ret) {
- v4l2_err(&dev->v4l2_dev, "Failed to register video device\n");
- goto rel_vdev;
- }
- video_set_drvdata(vfd, dev);
- dev->vfd = vfd;
- v4l2_info(&dev->v4l2_dev, "device registered as /dev/video%d\n",
- vfd->num);
+
platform_set_drvdata(pdev, dev);
dev->m2m_dev = v4l2_m2m_init(&g2d_m2m_ops);
if (IS_ERR(dev->m2m_dev)) {
v4l2_err(&dev->v4l2_dev, "Failed to init mem2mem device\n");
ret = PTR_ERR(dev->m2m_dev);
- goto unreg_video_dev;
+ goto rel_vdev;
}
def_frame.stride = (def_frame.width * def_frame.fmt->depth) >> 3;
@@ -720,14 +712,24 @@
of_id = of_match_node(exynos_g2d_match, pdev->dev.of_node);
if (!of_id) {
ret = -ENODEV;
- goto unreg_video_dev;
+ goto free_m2m;
}
dev->variant = (struct g2d_variant *)of_id->data;
+ ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0);
+ if (ret) {
+ v4l2_err(&dev->v4l2_dev, "Failed to register video device\n");
+ goto free_m2m;
+ }
+ video_set_drvdata(vfd, dev);
+ dev->vfd = vfd;
+ v4l2_info(&dev->v4l2_dev, "device registered as /dev/video%d\n",
+ vfd->num);
+
return 0;
-unreg_video_dev:
- video_unregister_device(dev->vfd);
+free_m2m:
+ v4l2_m2m_release(dev->m2m_dev);
rel_vdev:
video_device_release(vfd);
unreg_v4l2_dev:
diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.c b/drivers/media/platform/s5p-jpeg/jpeg-core.c
index 06e1794..d515eb0 100644
--- a/drivers/media/platform/s5p-jpeg/jpeg-core.c
+++ b/drivers/media/platform/s5p-jpeg/jpeg-core.c
@@ -24,6 +24,7 @@
#include <media/v4l2-event.h>
#include <media/v4l2-mem2mem.h>
#include <media/v4l2-ioctl.h>
+#include <media/v4l2-rect.h>
#include <media/videobuf2-v4l2.h>
#include <media/videobuf2-dma-contig.h>
@@ -1236,7 +1237,6 @@
}
result->sof = sof;
result->sof_len = sof_len;
- result->components = components;
return true;
}
@@ -1736,19 +1736,6 @@
return 0;
}
-/* Return 1 if rectangle a is enclosed in rectangle b, or 0 otherwise. */
-static int enclosed_rectangle(struct v4l2_rect *a, struct v4l2_rect *b)
-{
- if (a->left < b->left || a->top < b->top)
- return 0;
- if (a->left + a->width > b->left + b->width)
- return 0;
- if (a->top + a->height > b->top + b->height)
- return 0;
-
- return 1;
-}
-
static int exynos3250_jpeg_try_crop(struct s5p_jpeg_ctx *ctx,
struct v4l2_rect *r)
{
@@ -1781,7 +1768,7 @@
r->left = round_down(r->left, 2);
r->top = round_down(r->top, 2);
- if (!enclosed_rectangle(r, &base_rect))
+ if (!v4l2_rect_enclosed(r, &base_rect))
return -EINVAL;
ctx->crop_rect.left = r->left;
@@ -2944,7 +2931,7 @@
jpeg->vfd_encoder->vfl_dir = VFL_DIR_M2M;
jpeg->vfd_encoder->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M;
- ret = video_register_device(jpeg->vfd_encoder, VFL_TYPE_GRABBER, -1);
+ ret = video_register_device(jpeg->vfd_encoder, VFL_TYPE_VIDEO, -1);
if (ret) {
v4l2_err(&jpeg->v4l2_dev, "Failed to register video device\n");
video_device_release(jpeg->vfd_encoder);
@@ -2974,7 +2961,7 @@
jpeg->vfd_decoder->vfl_dir = VFL_DIR_M2M;
jpeg->vfd_decoder->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M;
- ret = video_register_device(jpeg->vfd_decoder, VFL_TYPE_GRABBER, -1);
+ ret = video_register_device(jpeg->vfd_decoder, VFL_TYPE_VIDEO, -1);
if (ret) {
v4l2_err(&jpeg->v4l2_dev, "Failed to register video device\n");
video_device_release(jpeg->vfd_decoder);
diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.h b/drivers/media/platform/s5p-jpeg/jpeg-core.h
index 3bc52f8..4407fe7 100644
--- a/drivers/media/platform/s5p-jpeg/jpeg-core.h
+++ b/drivers/media/platform/s5p-jpeg/jpeg-core.h
@@ -190,7 +190,6 @@
* @dqt: DQT markers' positions relative to the buffer beginning
* @sof: SOF0 marker's position relative to the buffer beginning
* @sof_len: SOF0 marker's payload length (without length field itself)
- * @components: number of image components
* @size: image buffer size in bytes
*/
struct s5p_jpeg_q_data {
@@ -202,7 +201,6 @@
struct s5p_jpeg_marker dqt;
u32 sof;
u32 sof_len;
- u32 components;
u32 size;
};
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c
index b776f83..f336a95 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c
@@ -1089,6 +1089,10 @@
child->coherent_dma_mask = dev->coherent_dma_mask;
child->dma_mask = dev->dma_mask;
child->release = s5p_mfc_memdev_release;
+ child->dma_parms = devm_kzalloc(dev, sizeof(*child->dma_parms),
+ GFP_KERNEL);
+ if (!child->dma_parms)
+ goto err;
/*
* The memdevs are not proper OF platform devices, so in order for them
@@ -1104,7 +1108,7 @@
return child;
device_del(child);
}
-
+err:
put_device(child);
return NULL;
}
@@ -1279,11 +1283,15 @@
spin_lock_init(&dev->condlock);
dev->plat_dev = pdev;
if (!dev->plat_dev) {
- dev_err(&pdev->dev, "No platform data specified\n");
+ mfc_err("No platform data specified\n");
return -ENODEV;
}
dev->variant = of_device_get_match_data(&pdev->dev);
+ if (!dev->variant) {
+ dev_err(&pdev->dev, "Failed to get device MFC hardware variant information\n");
+ return -ENOENT;
+ }
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
dev->regs_base = devm_ioremap_resource(&pdev->dev, res);
@@ -1376,7 +1384,7 @@
s5p_mfc_init_regs(dev);
/* Register decoder and encoder */
- ret = video_register_device(dev->vfd_dec, VFL_TYPE_GRABBER, 0);
+ ret = video_register_device(dev->vfd_dec, VFL_TYPE_VIDEO, 0);
if (ret) {
v4l2_err(&dev->v4l2_dev, "Failed to register video device\n");
goto err_dec_reg;
@@ -1384,7 +1392,7 @@
v4l2_info(&dev->v4l2_dev,
"decoder registered as /dev/video%d\n", dev->vfd_dec->num);
- ret = video_register_device(dev->vfd_enc, VFL_TYPE_GRABBER, 0);
+ ret = video_register_device(dev->vfd_enc, VFL_TYPE_VIDEO, 0);
if (ret) {
v4l2_err(&dev->v4l2_dev, "Failed to register video device\n");
goto err_enc_reg;
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c
index 912fe0c..acc2217 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c
@@ -262,6 +262,12 @@
.default_value = V4L2_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE_DISABLED,
},
{
+ .id = V4L2_CID_MPEG_VIDEO_FRAME_SKIP_MODE,
+ .type = V4L2_CTRL_TYPE_MENU,
+ .maximum = V4L2_MPEG_VIDEO_FRAME_SKIP_MODE_BUF_LIMIT,
+ .default_value = V4L2_MPEG_VIDEO_FRAME_SKIP_MODE_DISABLED,
+ },
+ {
.id = V4L2_CID_MPEG_MFC51_VIDEO_RC_FIXED_TARGET_BIT,
.type = V4L2_CTRL_TYPE_BOOLEAN,
.name = "Fixed Target Bit Enable",
@@ -1849,6 +1855,7 @@
p->seq_hdr_mode = ctrl->val;
break;
case V4L2_CID_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE:
+ case V4L2_CID_MPEG_VIDEO_FRAME_SKIP_MODE:
p->frame_skip_mode = ctrl->val;
break;
case V4L2_CID_MPEG_MFC51_VIDEO_RC_FIXED_TARGET_BIT:
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_iommu.h b/drivers/media/platform/s5p-mfc/s5p_mfc_iommu.h
index 152a713..1a32266 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_iommu.h
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_iommu.h
@@ -9,9 +9,11 @@
#if defined(CONFIG_EXYNOS_IOMMU)
+#include <linux/iommu.h>
+
static inline bool exynos_is_iommu_available(struct device *dev)
{
- return dev->archdata.iommu != NULL;
+ return dev_iommu_priv_get(dev) != NULL;
}
#else
diff --git a/drivers/media/platform/seco-cec/Makefile b/drivers/media/platform/seco-cec/Makefile
deleted file mode 100644
index 79fde69..0000000
--- 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/seco-cec/seco-cec.c b/drivers/media/platform/seco-cec/seco-cec.c
deleted file mode 100644
index a86b6e8..0000000
--- a/drivers/media/platform/seco-cec/seco-cec.c
+++ /dev/null
@@ -1,803 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
-/*
- * CEC driver for SECO X86 Boards
- *
- * Author: Ettore Chimenti <ek5.chimenti@gmail.com>
- * Copyright (C) 2018, SECO SpA.
- * Copyright (C) 2018, Aidilab Srl.
- */
-
-#include <linux/module.h>
-#include <linux/acpi.h>
-#include <linux/delay.h>
-#include <linux/dmi.h>
-#include <linux/gpio/consumer.h>
-#include <linux/gpio.h>
-#include <linux/interrupt.h>
-#include <linux/pci.h>
-#include <linux/platform_device.h>
-
-/* CEC Framework */
-#include <media/cec-notifier.h>
-
-#include "seco-cec.h"
-
-struct secocec_data {
- struct device *dev;
- struct platform_device *pdev;
- struct cec_adapter *cec_adap;
- struct cec_notifier *notifier;
- struct rc_dev *ir;
- char ir_input_phys[32];
- int irq;
-};
-
-#define smb_wr16(cmd, data) smb_word_op(CMD_WORD_DATA, SECOCEC_MICRO_ADDRESS, \
- cmd, data, SMBUS_WRITE, NULL)
-#define smb_rd16(cmd, res) smb_word_op(CMD_WORD_DATA, SECOCEC_MICRO_ADDRESS, \
- cmd, 0, SMBUS_READ, res)
-
-static int smb_word_op(short data_format, u16 slave_addr, u8 cmd, u16 data,
- u8 operation, u16 *result)
-{
- unsigned int count;
- short _data_format;
- int status = 0;
-
- switch (data_format) {
- case CMD_BYTE_DATA:
- _data_format = BRA_SMB_CMD_BYTE_DATA;
- break;
- case CMD_WORD_DATA:
- _data_format = BRA_SMB_CMD_WORD_DATA;
- break;
- default:
- return -EINVAL;
- }
-
- /* Active wait until ready */
- for (count = 0; count <= SMBTIMEOUT; ++count) {
- if (!(inb(HSTS) & BRA_INUSE_STS))
- break;
- udelay(SMB_POLL_UDELAY);
- }
-
- if (count > SMBTIMEOUT)
- /* Reset the lock instead of failing */
- outb(0xff, HSTS);
-
- outb(0x00, HCNT);
- outb((u8)(slave_addr & 0xfe) | operation, XMIT_SLVA);
- outb(cmd, HCMD);
- inb(HCNT);
-
- if (operation == SMBUS_WRITE) {
- outb((u8)data, HDAT0);
- outb((u8)(data >> 8), HDAT1);
- }
-
- outb(BRA_START + _data_format, HCNT);
-
- for (count = 0; count <= SMBTIMEOUT; count++) {
- if (!(inb(HSTS) & BRA_HOST_BUSY))
- break;
- udelay(SMB_POLL_UDELAY);
- }
-
- if (count > SMBTIMEOUT) {
- status = -EBUSY;
- goto err;
- }
-
- if (inb(HSTS) & BRA_HSTS_ERR_MASK) {
- status = -EIO;
- goto err;
- }
-
- if (operation == SMBUS_READ)
- *result = ((inb(HDAT0) & 0xff) + ((inb(HDAT1) & 0xff) << 8));
-
-err:
- outb(0xff, HSTS);
- return status;
-}
-
-static int secocec_adap_enable(struct cec_adapter *adap, bool enable)
-{
- struct secocec_data *cec = cec_get_drvdata(adap);
- struct device *dev = cec->dev;
- u16 val = 0;
- int status;
-
- if (enable) {
- /* Clear the status register */
- status = smb_rd16(SECOCEC_STATUS_REG_1, &val);
- if (status)
- goto err;
-
- status = smb_wr16(SECOCEC_STATUS_REG_1, val);
- if (status)
- goto err;
-
- /* Enable the interrupts */
- status = smb_rd16(SECOCEC_ENABLE_REG_1, &val);
- if (status)
- goto err;
-
- status = smb_wr16(SECOCEC_ENABLE_REG_1,
- val | SECOCEC_ENABLE_REG_1_CEC);
- if (status)
- goto err;
-
- dev_dbg(dev, "Device enabled");
- } else {
- /* Clear the status register */
- status = smb_rd16(SECOCEC_STATUS_REG_1, &val);
- status = smb_wr16(SECOCEC_STATUS_REG_1, val);
-
- /* Disable the interrupts */
- status = smb_rd16(SECOCEC_ENABLE_REG_1, &val);
- status = smb_wr16(SECOCEC_ENABLE_REG_1, val &
- ~SECOCEC_ENABLE_REG_1_CEC &
- ~SECOCEC_ENABLE_REG_1_IR);
-
- dev_dbg(dev, "Device disabled");
- }
-
- return 0;
-err:
- return status;
-}
-
-static int secocec_adap_log_addr(struct cec_adapter *adap, u8 logical_addr)
-{
- u16 enable_val = 0;
- int status;
-
- /* Disable device */
- status = smb_rd16(SECOCEC_ENABLE_REG_1, &enable_val);
- if (status)
- return status;
-
- status = smb_wr16(SECOCEC_ENABLE_REG_1,
- enable_val & ~SECOCEC_ENABLE_REG_1_CEC);
- if (status)
- return status;
-
- /* Write logical address
- * NOTE: CEC_LOG_ADDR_INVALID is mapped to the 'Unregistered' LA
- */
- status = smb_wr16(SECOCEC_DEVICE_LA, logical_addr & 0xf);
- if (status)
- return status;
-
- /* Re-enable device */
- status = smb_wr16(SECOCEC_ENABLE_REG_1,
- enable_val | SECOCEC_ENABLE_REG_1_CEC);
- if (status)
- return status;
-
- return 0;
-}
-
-static int secocec_adap_transmit(struct cec_adapter *adap, u8 attempts,
- u32 signal_free_time, struct cec_msg *msg)
-{
- u16 payload_len, payload_id_len, destination, val = 0;
- u8 *payload_msg;
- int status;
- u8 i;
-
- /* Device msg len already accounts for header */
- payload_id_len = msg->len - 1;
-
- /* Send data length */
- status = smb_wr16(SECOCEC_WRITE_DATA_LENGTH, payload_id_len);
- if (status)
- goto err;
-
- /* Send Operation ID if present */
- if (payload_id_len > 0) {
- status = smb_wr16(SECOCEC_WRITE_OPERATION_ID, msg->msg[1]);
- if (status)
- goto err;
- }
- /* Send data if present */
- if (payload_id_len > 1) {
- /* Only data; */
- payload_len = msg->len - 2;
- payload_msg = &msg->msg[2];
-
- /* Copy message into registers */
- for (i = 0; i < payload_len; i += 2) {
- /* hi byte */
- val = payload_msg[i + 1] << 8;
-
- /* lo byte */
- val |= payload_msg[i];
-
- status = smb_wr16(SECOCEC_WRITE_DATA_00 + i / 2, val);
- if (status)
- goto err;
- }
- }
- /* Send msg source/destination and fire msg */
- destination = msg->msg[0];
- status = smb_wr16(SECOCEC_WRITE_BYTE0, destination);
- if (status)
- goto err;
-
- return 0;
-
-err:
- return status;
-}
-
-static void secocec_tx_done(struct cec_adapter *adap, u16 status_val)
-{
- if (status_val & SECOCEC_STATUS_TX_ERROR_MASK) {
- if (status_val & SECOCEC_STATUS_TX_NACK_ERROR)
- cec_transmit_attempt_done(adap, CEC_TX_STATUS_NACK);
- else
- cec_transmit_attempt_done(adap, CEC_TX_STATUS_ERROR);
- } else {
- cec_transmit_attempt_done(adap, CEC_TX_STATUS_OK);
- }
-
- /* Reset status reg */
- status_val = SECOCEC_STATUS_TX_ERROR_MASK |
- SECOCEC_STATUS_MSG_SENT_MASK |
- SECOCEC_STATUS_TX_NACK_ERROR;
- smb_wr16(SECOCEC_STATUS, status_val);
-}
-
-static void secocec_rx_done(struct cec_adapter *adap, u16 status_val)
-{
- struct secocec_data *cec = cec_get_drvdata(adap);
- struct device *dev = cec->dev;
- struct cec_msg msg = { };
- bool flag_overflow = false;
- u8 payload_len, i = 0;
- u8 *payload_msg;
- u16 val = 0;
- int status;
-
- if (status_val & SECOCEC_STATUS_RX_OVERFLOW_MASK) {
- /* NOTE: Untested, it also might not be necessary */
- dev_warn(dev, "Received more than 16 bytes. Discarding");
- flag_overflow = true;
- }
-
- if (status_val & SECOCEC_STATUS_RX_ERROR_MASK) {
- dev_warn(dev, "Message received with errors. Discarding");
- status = -EIO;
- goto rxerr;
- }
-
- /* Read message length */
- status = smb_rd16(SECOCEC_READ_DATA_LENGTH, &val);
- if (status)
- return;
-
- /* Device msg len already accounts for the header */
- msg.len = min(val + 1, CEC_MAX_MSG_SIZE);
-
- /* Read logical address */
- status = smb_rd16(SECOCEC_READ_BYTE0, &val);
- if (status)
- return;
-
- /* device stores source LA and destination */
- msg.msg[0] = val;
-
- /* Read operation ID */
- status = smb_rd16(SECOCEC_READ_OPERATION_ID, &val);
- if (status)
- return;
-
- msg.msg[1] = val;
-
- /* Read data if present */
- if (msg.len > 1) {
- payload_len = msg.len - 2;
- payload_msg = &msg.msg[2];
-
- /* device stores 2 bytes in every 16-bit val */
- for (i = 0; i < payload_len; i += 2) {
- status = smb_rd16(SECOCEC_READ_DATA_00 + i / 2, &val);
- if (status)
- return;
-
- /* low byte, skipping header */
- payload_msg[i] = val & 0x00ff;
-
- /* hi byte */
- payload_msg[i + 1] = (val & 0xff00) >> 8;
- }
- }
-
- cec_received_msg(cec->cec_adap, &msg);
-
- /* Reset status reg */
- status_val = SECOCEC_STATUS_MSG_RECEIVED_MASK;
- if (flag_overflow)
- status_val |= SECOCEC_STATUS_RX_OVERFLOW_MASK;
-
- status = smb_wr16(SECOCEC_STATUS, status_val);
-
- return;
-
-rxerr:
- /* Reset error reg */
- status_val = SECOCEC_STATUS_MSG_RECEIVED_MASK |
- SECOCEC_STATUS_RX_ERROR_MASK;
- if (flag_overflow)
- status_val |= SECOCEC_STATUS_RX_OVERFLOW_MASK;
- smb_wr16(SECOCEC_STATUS, status_val);
-}
-
-static const struct cec_adap_ops secocec_cec_adap_ops = {
- /* Low-level callbacks */
- .adap_enable = secocec_adap_enable,
- .adap_log_addr = secocec_adap_log_addr,
- .adap_transmit = secocec_adap_transmit,
-};
-
-#ifdef CONFIG_VIDEO_SECO_RC
-static int secocec_ir_probe(void *priv)
-{
- struct secocec_data *cec = priv;
- struct device *dev = cec->dev;
- int status;
- u16 val;
-
- /* Prepare the RC input device */
- cec->ir = devm_rc_allocate_device(dev, RC_DRIVER_SCANCODE);
- if (!cec->ir)
- return -ENOMEM;
-
- snprintf(cec->ir_input_phys, sizeof(cec->ir_input_phys),
- "%s/input0", dev_name(dev));
-
- cec->ir->device_name = dev_name(dev);
- cec->ir->input_phys = cec->ir_input_phys;
- cec->ir->input_id.bustype = BUS_HOST;
- cec->ir->input_id.vendor = 0;
- cec->ir->input_id.product = 0;
- cec->ir->input_id.version = 1;
- cec->ir->driver_name = SECOCEC_DEV_NAME;
- cec->ir->allowed_protocols = RC_PROTO_BIT_RC5;
- cec->ir->priv = cec;
- cec->ir->map_name = RC_MAP_HAUPPAUGE;
- cec->ir->timeout = MS_TO_NS(100);
-
- /* Clear the status register */
- status = smb_rd16(SECOCEC_STATUS_REG_1, &val);
- if (status != 0)
- goto err;
-
- status = smb_wr16(SECOCEC_STATUS_REG_1, val);
- if (status != 0)
- goto err;
-
- /* Enable the interrupts */
- status = smb_rd16(SECOCEC_ENABLE_REG_1, &val);
- if (status != 0)
- goto err;
-
- status = smb_wr16(SECOCEC_ENABLE_REG_1,
- val | SECOCEC_ENABLE_REG_1_IR);
- if (status != 0)
- goto err;
-
- dev_dbg(dev, "IR enabled");
-
- status = devm_rc_register_device(dev, cec->ir);
-
- if (status) {
- dev_err(dev, "Failed to prepare input device");
- cec->ir = NULL;
- goto err;
- }
-
- return 0;
-
-err:
- smb_rd16(SECOCEC_ENABLE_REG_1, &val);
-
- smb_wr16(SECOCEC_ENABLE_REG_1,
- val & ~SECOCEC_ENABLE_REG_1_IR);
-
- dev_dbg(dev, "IR disabled");
- return status;
-}
-
-static int secocec_ir_rx(struct secocec_data *priv)
-{
- struct secocec_data *cec = priv;
- struct device *dev = cec->dev;
- u16 val, status, key, addr, toggle;
-
- if (!cec->ir)
- return -ENODEV;
-
- status = smb_rd16(SECOCEC_IR_READ_DATA, &val);
- if (status != 0)
- goto err;
-
- key = val & SECOCEC_IR_COMMAND_MASK;
- addr = (val & SECOCEC_IR_ADDRESS_MASK) >> SECOCEC_IR_ADDRESS_SHL;
- toggle = (val & SECOCEC_IR_TOGGLE_MASK) >> SECOCEC_IR_TOGGLE_SHL;
-
- rc_keydown(cec->ir, RC_PROTO_RC5, RC_SCANCODE_RC5(addr, key), toggle);
-
- dev_dbg(dev, "IR key pressed: 0x%02x addr 0x%02x toggle 0x%02x", key,
- addr, toggle);
-
- return 0;
-
-err:
- dev_err(dev, "IR Receive message failed (%d)", status);
- return -EIO;
-}
-#else
-static void secocec_ir_rx(struct secocec_data *priv)
-{
-}
-
-static int secocec_ir_probe(void *priv)
-{
- return 0;
-}
-#endif
-
-static irqreturn_t secocec_irq_handler(int irq, void *priv)
-{
- struct secocec_data *cec = priv;
- struct device *dev = cec->dev;
- u16 status_val, cec_val, val = 0;
- int status;
-
- /* Read status register */
- status = smb_rd16(SECOCEC_STATUS_REG_1, &status_val);
- if (status)
- goto err;
-
- if (status_val & SECOCEC_STATUS_REG_1_CEC) {
- /* Read CEC status register */
- status = smb_rd16(SECOCEC_STATUS, &cec_val);
- if (status)
- goto err;
-
- if (cec_val & SECOCEC_STATUS_MSG_RECEIVED_MASK)
- secocec_rx_done(cec->cec_adap, cec_val);
-
- if (cec_val & SECOCEC_STATUS_MSG_SENT_MASK)
- secocec_tx_done(cec->cec_adap, cec_val);
-
- if ((~cec_val & SECOCEC_STATUS_MSG_SENT_MASK) &&
- (~cec_val & SECOCEC_STATUS_MSG_RECEIVED_MASK))
- dev_warn_once(dev,
- "Message not received or sent, but interrupt fired");
-
- val = SECOCEC_STATUS_REG_1_CEC;
- }
-
- if (status_val & SECOCEC_STATUS_REG_1_IR) {
- val |= SECOCEC_STATUS_REG_1_IR;
-
- secocec_ir_rx(cec);
- }
-
- /* Reset status register */
- status = smb_wr16(SECOCEC_STATUS_REG_1, val);
- if (status)
- goto err;
-
- return IRQ_HANDLED;
-
-err:
- dev_err_once(dev, "IRQ: R/W SMBus operation failed (%d)", status);
-
- /* Reset status register */
- val = SECOCEC_STATUS_REG_1_CEC | SECOCEC_STATUS_REG_1_IR;
- smb_wr16(SECOCEC_STATUS_REG_1, val);
-
- return IRQ_HANDLED;
-}
-
-struct cec_dmi_match {
- const char *sys_vendor;
- const char *product_name;
- const char *devname;
- const char *conn;
-};
-
-static const struct cec_dmi_match secocec_dmi_match_table[] = {
- /* UDOO X86 */
- { "SECO", "UDOO x86", "0000:00:02.0", "Port B" },
-};
-
-static struct device *secocec_cec_find_hdmi_dev(struct device *dev,
- const char **conn)
-{
- int i;
-
- for (i = 0 ; i < ARRAY_SIZE(secocec_dmi_match_table) ; ++i) {
- const struct cec_dmi_match *m = &secocec_dmi_match_table[i];
-
- if (dmi_match(DMI_SYS_VENDOR, m->sys_vendor) &&
- dmi_match(DMI_PRODUCT_NAME, m->product_name)) {
- struct device *d;
-
- /* Find the device, bail out if not yet registered */
- d = bus_find_device_by_name(&pci_bus_type, NULL,
- m->devname);
- if (!d)
- return ERR_PTR(-EPROBE_DEFER);
-
- put_device(d);
- *conn = m->conn;
- return d;
- }
- }
-
- return ERR_PTR(-EINVAL);
-}
-
-static int secocec_acpi_probe(struct secocec_data *sdev)
-{
- struct device *dev = sdev->dev;
- struct gpio_desc *gpio;
- int irq = 0;
-
- gpio = devm_gpiod_get(dev, NULL, GPIOF_IN);
- if (IS_ERR(gpio)) {
- dev_err(dev, "Cannot request interrupt gpio");
- return PTR_ERR(gpio);
- }
-
- irq = gpiod_to_irq(gpio);
- if (irq < 0) {
- dev_err(dev, "Cannot find valid irq");
- return -ENODEV;
- }
- dev_dbg(dev, "irq-gpio is bound to IRQ %d", irq);
-
- sdev->irq = irq;
-
- return 0;
-}
-
-static int secocec_probe(struct platform_device *pdev)
-{
- struct secocec_data *secocec;
- struct device *dev = &pdev->dev;
- struct device *hdmi_dev;
- const char *conn = NULL;
- int ret;
- u16 val;
-
- hdmi_dev = secocec_cec_find_hdmi_dev(&pdev->dev, &conn);
- if (IS_ERR(hdmi_dev))
- return PTR_ERR(hdmi_dev);
-
- secocec = devm_kzalloc(dev, sizeof(*secocec), GFP_KERNEL);
- if (!secocec)
- return -ENOMEM;
-
- dev_set_drvdata(dev, secocec);
-
- /* Request SMBus regions */
- if (!request_muxed_region(BRA_SMB_BASE_ADDR, 7, "CEC00001")) {
- dev_err(dev, "Request memory region failed");
- return -ENXIO;
- }
-
- secocec->pdev = pdev;
- secocec->dev = dev;
-
- if (!has_acpi_companion(dev)) {
- dev_dbg(dev, "Cannot find any ACPI companion");
- ret = -ENODEV;
- goto err;
- }
-
- ret = secocec_acpi_probe(secocec);
- if (ret) {
- dev_err(dev, "Cannot assign gpio to IRQ");
- ret = -ENODEV;
- goto err;
- }
-
- /* Firmware version check */
- ret = smb_rd16(SECOCEC_VERSION, &val);
- if (ret) {
- dev_err(dev, "Cannot check fw version");
- goto err;
- }
- if (val < SECOCEC_LATEST_FW) {
- dev_err(dev, "CEC Firmware not supported (v.%04x). Use ver > v.%04x",
- val, SECOCEC_LATEST_FW);
- ret = -EINVAL;
- goto err;
- }
-
- ret = devm_request_threaded_irq(dev,
- secocec->irq,
- NULL,
- secocec_irq_handler,
- IRQF_TRIGGER_RISING | IRQF_ONESHOT,
- dev_name(&pdev->dev), secocec);
-
- if (ret) {
- dev_err(dev, "Cannot request IRQ %d", secocec->irq);
- ret = -EIO;
- goto err;
- }
-
- /* Allocate CEC adapter */
- secocec->cec_adap = cec_allocate_adapter(&secocec_cec_adap_ops,
- secocec,
- dev_name(dev),
- CEC_CAP_DEFAULTS |
- CEC_CAP_CONNECTOR_INFO,
- SECOCEC_MAX_ADDRS);
-
- if (IS_ERR(secocec->cec_adap)) {
- ret = PTR_ERR(secocec->cec_adap);
- goto err;
- }
-
- secocec->notifier = cec_notifier_cec_adap_register(hdmi_dev, conn,
- secocec->cec_adap);
- if (!secocec->notifier) {
- ret = -ENOMEM;
- goto err_delete_adapter;
- }
-
- ret = cec_register_adapter(secocec->cec_adap, dev);
- if (ret)
- goto err_notifier;
-
- ret = secocec_ir_probe(secocec);
- if (ret)
- goto err_notifier;
-
- platform_set_drvdata(pdev, secocec);
-
- dev_dbg(dev, "Device registered");
-
- return ret;
-
-err_notifier:
- cec_notifier_cec_adap_unregister(secocec->notifier);
-err_delete_adapter:
- cec_delete_adapter(secocec->cec_adap);
-err:
- release_region(BRA_SMB_BASE_ADDR, 7);
- dev_err(dev, "%s device probe failed\n", dev_name(dev));
-
- return ret;
-}
-
-static int secocec_remove(struct platform_device *pdev)
-{
- struct secocec_data *secocec = platform_get_drvdata(pdev);
- u16 val;
-
- if (secocec->ir) {
- smb_rd16(SECOCEC_ENABLE_REG_1, &val);
-
- smb_wr16(SECOCEC_ENABLE_REG_1, val & ~SECOCEC_ENABLE_REG_1_IR);
-
- dev_dbg(&pdev->dev, "IR disabled");
- }
- cec_notifier_cec_adap_unregister(secocec->notifier);
- cec_unregister_adapter(secocec->cec_adap);
-
- release_region(BRA_SMB_BASE_ADDR, 7);
-
- dev_dbg(&pdev->dev, "CEC device removed");
-
- return 0;
-}
-
-#ifdef CONFIG_PM_SLEEP
-static int secocec_suspend(struct device *dev)
-{
- int status;
- u16 val;
-
- dev_dbg(dev, "Device going to suspend, disabling");
-
- /* Clear the status register */
- status = smb_rd16(SECOCEC_STATUS_REG_1, &val);
- if (status)
- goto err;
-
- status = smb_wr16(SECOCEC_STATUS_REG_1, val);
- if (status)
- goto err;
-
- /* Disable the interrupts */
- status = smb_rd16(SECOCEC_ENABLE_REG_1, &val);
- if (status)
- goto err;
-
- status = smb_wr16(SECOCEC_ENABLE_REG_1, val &
- ~SECOCEC_ENABLE_REG_1_CEC & ~SECOCEC_ENABLE_REG_1_IR);
- if (status)
- goto err;
-
- return 0;
-
-err:
- dev_err(dev, "Suspend failed (err: %d)", status);
- return status;
-}
-
-static int secocec_resume(struct device *dev)
-{
- int status;
- u16 val;
-
- dev_dbg(dev, "Resuming device from suspend");
-
- /* Clear the status register */
- status = smb_rd16(SECOCEC_STATUS_REG_1, &val);
- if (status)
- goto err;
-
- status = smb_wr16(SECOCEC_STATUS_REG_1, val);
- if (status)
- goto err;
-
- /* Enable the interrupts */
- status = smb_rd16(SECOCEC_ENABLE_REG_1, &val);
- if (status)
- goto err;
-
- status = smb_wr16(SECOCEC_ENABLE_REG_1, val | SECOCEC_ENABLE_REG_1_CEC);
- if (status)
- goto err;
-
- dev_dbg(dev, "Device resumed from suspend");
-
- return 0;
-
-err:
- dev_err(dev, "Resume failed (err: %d)", status);
- return status;
-}
-
-static SIMPLE_DEV_PM_OPS(secocec_pm_ops, secocec_suspend, secocec_resume);
-#define SECOCEC_PM_OPS (&secocec_pm_ops)
-#else
-#define SECOCEC_PM_OPS NULL
-#endif
-
-#ifdef CONFIG_ACPI
-static const struct acpi_device_id secocec_acpi_match[] = {
- {"CEC00001", 0},
- {},
-};
-
-MODULE_DEVICE_TABLE(acpi, secocec_acpi_match);
-#endif
-
-static struct platform_driver secocec_driver = {
- .driver = {
- .name = SECOCEC_DEV_NAME,
- .acpi_match_table = ACPI_PTR(secocec_acpi_match),
- .pm = SECOCEC_PM_OPS,
- },
- .probe = secocec_probe,
- .remove = secocec_remove,
-};
-
-module_platform_driver(secocec_driver);
-
-MODULE_DESCRIPTION("SECO CEC X86 Driver");
-MODULE_AUTHOR("Ettore Chimenti <ek5.chimenti@gmail.com>");
-MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/media/platform/seco-cec/seco-cec.h b/drivers/media/platform/seco-cec/seco-cec.h
deleted file mode 100644
index 843de8c..0000000
--- a/drivers/media/platform/seco-cec/seco-cec.h
+++ /dev/null
@@ -1,141 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
-/*
- * SECO X86 Boards CEC register defines
- *
- * Author: Ettore Chimenti <ek5.chimenti@gmail.com>
- * Copyright (C) 2018, SECO Spa.
- * Copyright (C) 2018, Aidilab Srl.
- */
-
-#ifndef __SECO_CEC_H__
-#define __SECO_CEC_H__
-
-#define SECOCEC_MAX_ADDRS 1
-#define SECOCEC_DEV_NAME "secocec"
-#define SECOCEC_LATEST_FW 0x0f0b
-
-#define SMBTIMEOUT 0xfff
-#define SMB_POLL_UDELAY 10
-
-#define SMBUS_WRITE 0
-#define SMBUS_READ 1
-
-#define CMD_BYTE_DATA 0
-#define CMD_WORD_DATA 1
-
-/*
- * SMBus definitons for Braswell
- */
-
-#define BRA_DONE_STATUS BIT(7)
-#define BRA_INUSE_STS BIT(6)
-#define BRA_FAILED_OP BIT(4)
-#define BRA_BUS_ERR BIT(3)
-#define BRA_DEV_ERR BIT(2)
-#define BRA_INTR BIT(1)
-#define BRA_HOST_BUSY BIT(0)
-#define BRA_HSTS_ERR_MASK (BRA_FAILED_OP | BRA_BUS_ERR | BRA_DEV_ERR)
-
-#define BRA_PEC_EN BIT(7)
-#define BRA_START BIT(6)
-#define BRA_LAST__BYTE BIT(5)
-#define BRA_INTREN BIT(0)
-#define BRA_SMB_CMD (7 << 2)
-#define BRA_SMB_CMD_QUICK (0 << 2)
-#define BRA_SMB_CMD_BYTE (1 << 2)
-#define BRA_SMB_CMD_BYTE_DATA (2 << 2)
-#define BRA_SMB_CMD_WORD_DATA (3 << 2)
-#define BRA_SMB_CMD_PROCESS_CALL (4 << 2)
-#define BRA_SMB_CMD_BLOCK (5 << 2)
-#define BRA_SMB_CMD_I2CREAD (6 << 2)
-#define BRA_SMB_CMD_BLOCK_PROCESS (7 << 2)
-
-#define BRA_SMB_BASE_ADDR 0x2040
-#define HSTS (BRA_SMB_BASE_ADDR + 0)
-#define HCNT (BRA_SMB_BASE_ADDR + 2)
-#define HCMD (BRA_SMB_BASE_ADDR + 3)
-#define XMIT_SLVA (BRA_SMB_BASE_ADDR + 4)
-#define HDAT0 (BRA_SMB_BASE_ADDR + 5)
-#define HDAT1 (BRA_SMB_BASE_ADDR + 6)
-
-/*
- * Microcontroller Address
- */
-
-#define SECOCEC_MICRO_ADDRESS 0x40
-
-/*
- * STM32 SMBus Registers
- */
-
-#define SECOCEC_VERSION 0x00
-#define SECOCEC_ENABLE_REG_1 0x01
-#define SECOCEC_ENABLE_REG_2 0x02
-#define SECOCEC_STATUS_REG_1 0x03
-#define SECOCEC_STATUS_REG_2 0x04
-
-#define SECOCEC_STATUS 0x28
-#define SECOCEC_DEVICE_LA 0x29
-#define SECOCEC_READ_OPERATION_ID 0x2a
-#define SECOCEC_READ_DATA_LENGTH 0x2b
-#define SECOCEC_READ_DATA_00 0x2c
-#define SECOCEC_READ_DATA_02 0x2d
-#define SECOCEC_READ_DATA_04 0x2e
-#define SECOCEC_READ_DATA_06 0x2f
-#define SECOCEC_READ_DATA_08 0x30
-#define SECOCEC_READ_DATA_10 0x31
-#define SECOCEC_READ_DATA_12 0x32
-#define SECOCEC_READ_BYTE0 0x33
-#define SECOCEC_WRITE_OPERATION_ID 0x34
-#define SECOCEC_WRITE_DATA_LENGTH 0x35
-#define SECOCEC_WRITE_DATA_00 0x36
-#define SECOCEC_WRITE_DATA_02 0x37
-#define SECOCEC_WRITE_DATA_04 0x38
-#define SECOCEC_WRITE_DATA_06 0x39
-#define SECOCEC_WRITE_DATA_08 0x3a
-#define SECOCEC_WRITE_DATA_10 0x3b
-#define SECOCEC_WRITE_DATA_12 0x3c
-#define SECOCEC_WRITE_BYTE0 0x3d
-
-#define SECOCEC_IR_READ_DATA 0x3e
-
-/*
- * IR
- */
-
-#define SECOCEC_IR_COMMAND_MASK 0x007F
-#define SECOCEC_IR_COMMAND_SHL 0
-#define SECOCEC_IR_ADDRESS_MASK 0x1F00
-#define SECOCEC_IR_ADDRESS_SHL 8
-#define SECOCEC_IR_TOGGLE_MASK 0x8000
-#define SECOCEC_IR_TOGGLE_SHL 15
-
-/*
- * Enabling register
- */
-
-#define SECOCEC_ENABLE_REG_1_CEC 0x1000
-#define SECOCEC_ENABLE_REG_1_IR 0x2000
-#define SECOCEC_ENABLE_REG_1_IR_PASSTHROUGH 0x4000
-
-/*
- * Status register
- */
-
-#define SECOCEC_STATUS_REG_1_CEC SECOCEC_ENABLE_REG_1_CEC
-#define SECOCEC_STATUS_REG_1_IR SECOCEC_ENABLE_REG_1_IR
-#define SECOCEC_STATUS_REG_1_IR_PASSTHR SECOCEC_ENABLE_REG_1_IR_PASSTHR
-
-/*
- * Status data
- */
-
-#define SECOCEC_STATUS_MSG_RECEIVED_MASK BIT(0)
-#define SECOCEC_STATUS_RX_ERROR_MASK BIT(1)
-#define SECOCEC_STATUS_MSG_SENT_MASK BIT(2)
-#define SECOCEC_STATUS_TX_ERROR_MASK BIT(3)
-
-#define SECOCEC_STATUS_TX_NACK_ERROR BIT(4)
-#define SECOCEC_STATUS_RX_OVERFLOW_MASK BIT(5)
-
-#endif /* __SECO_CEC_H__ */
diff --git a/drivers/media/platform/sh_veu.c b/drivers/media/platform/sh_veu.c
deleted file mode 100644
index 2b4c0d9..0000000
--- 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_GRABBER, -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/sh_vou.c b/drivers/media/platform/sh_vou.c
index 84c4b19..7d30e0c 100644
--- a/drivers/media/platform/sh_vou.c
+++ b/drivers/media/platform/sh_vou.c
@@ -220,7 +220,7 @@
break;
case V4L2_PIX_FMT_RGB565:
dataswap ^= 1;
- /* fall through */
+ fallthrough;
case V4L2_PIX_FMT_RGB565X:
row_coeff = 2;
break;
@@ -802,7 +802,7 @@
default:
pr_warn("%s(): Invalid bus-format code %d, using default 8-bit\n",
__func__, bus_fmt);
- /* fall through */
+ fallthrough;
case SH_VOU_BUS_8BIT:
return 1;
case SH_VOU_BUS_16BIT:
@@ -1327,7 +1327,7 @@
goto ei2cnd;
}
- ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
+ ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
if (ret < 0)
goto evregdev;
diff --git a/drivers/media/platform/sti/bdisp/bdisp-debug.c b/drivers/media/platform/sti/bdisp/bdisp-debug.c
index bae62af..a27f638 100644
--- a/drivers/media/platform/sti/bdisp/bdisp-debug.c
+++ b/drivers/media/platform/sti/bdisp/bdisp-debug.c
@@ -637,35 +637,18 @@
DEFINE_SHOW_ATTRIBUTE(last_request);
DEFINE_SHOW_ATTRIBUTE(perf);
-int bdisp_debugfs_create(struct bdisp_dev *bdisp)
+void bdisp_debugfs_create(struct bdisp_dev *bdisp)
{
char dirname[16];
snprintf(dirname, sizeof(dirname), "%s%d", BDISP_NAME, bdisp->id);
bdisp->dbg.debugfs_entry = debugfs_create_dir(dirname, NULL);
- if (!bdisp->dbg.debugfs_entry)
- goto err;
- if (!bdisp_dbg_create_entry(regs))
- goto err;
-
- if (!bdisp_dbg_create_entry(last_nodes))
- goto err;
-
- if (!bdisp_dbg_create_entry(last_nodes_raw))
- goto err;
-
- if (!bdisp_dbg_create_entry(last_request))
- goto err;
-
- if (!bdisp_dbg_create_entry(perf))
- goto err;
-
- return 0;
-
-err:
- bdisp_debugfs_remove(bdisp);
- return -ENOMEM;
+ bdisp_dbg_create_entry(regs);
+ bdisp_dbg_create_entry(last_nodes);
+ bdisp_dbg_create_entry(last_nodes_raw);
+ bdisp_dbg_create_entry(last_request);
+ bdisp_dbg_create_entry(perf);
}
void bdisp_debugfs_remove(struct bdisp_dev *bdisp)
diff --git a/drivers/media/platform/sti/bdisp/bdisp-v4l2.c b/drivers/media/platform/sti/bdisp/bdisp-v4l2.c
index 16a097f..85288da 100644
--- a/drivers/media/platform/sti/bdisp/bdisp-v4l2.c
+++ b/drivers/media/platform/sti/bdisp/bdisp-v4l2.c
@@ -1066,7 +1066,7 @@
return PTR_ERR(bdisp->m2m.m2m_dev);
}
- ret = video_register_device(&bdisp->vdev, VFL_TYPE_GRABBER, -1);
+ ret = video_register_device(&bdisp->vdev, VFL_TYPE_VIDEO, -1);
if (ret) {
dev_err(bdisp->dev,
"%s(): failed to register video device\n", __func__);
@@ -1274,6 +1274,8 @@
if (!IS_ERR(bdisp->clock))
clk_unprepare(bdisp->clock);
+ destroy_workqueue(bdisp->work_queue);
+
dev_dbg(&pdev->dev, "%s driver unloaded\n", pdev->name);
return 0;
@@ -1317,20 +1319,22 @@
bdisp->regs = devm_ioremap_resource(dev, res);
if (IS_ERR(bdisp->regs)) {
dev_err(dev, "failed to get regs\n");
- return PTR_ERR(bdisp->regs);
+ ret = PTR_ERR(bdisp->regs);
+ goto err_wq;
}
bdisp->clock = devm_clk_get(dev, BDISP_NAME);
if (IS_ERR(bdisp->clock)) {
dev_err(dev, "failed to get clock\n");
- return PTR_ERR(bdisp->clock);
+ ret = PTR_ERR(bdisp->clock);
+ goto err_wq;
}
ret = clk_prepare(bdisp->clock);
if (ret < 0) {
dev_err(dev, "clock prepare failed\n");
bdisp->clock = ERR_PTR(-EINVAL);
- return ret;
+ goto err_wq;
}
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
@@ -1356,11 +1360,7 @@
}
/* Debug */
- ret = bdisp_debugfs_create(bdisp);
- if (ret) {
- dev_err(dev, "failed to create debugfs\n");
- goto err_v4l2;
- }
+ bdisp_debugfs_create(bdisp);
/* Power management */
pm_runtime_enable(dev);
@@ -1397,12 +1397,12 @@
pm_runtime_put(dev);
err_remove:
bdisp_debugfs_remove(bdisp);
-err_v4l2:
v4l2_device_unregister(&bdisp->v4l2_dev);
err_clk:
if (!IS_ERR(bdisp->clock))
clk_unprepare(bdisp->clock);
-
+err_wq:
+ destroy_workqueue(bdisp->work_queue);
return ret;
}
diff --git a/drivers/media/platform/sti/bdisp/bdisp.h b/drivers/media/platform/sti/bdisp/bdisp.h
index e309cde..3fb009d 100644
--- a/drivers/media/platform/sti/bdisp/bdisp.h
+++ b/drivers/media/platform/sti/bdisp/bdisp.h
@@ -209,6 +209,6 @@
int bdisp_hw_update(struct bdisp_ctx *ctx);
void bdisp_debugfs_remove(struct bdisp_dev *bdisp);
-int bdisp_debugfs_create(struct bdisp_dev *bdisp);
+void bdisp_debugfs_create(struct bdisp_dev *bdisp);
void bdisp_dbg_perf_begin(struct bdisp_dev *bdisp);
void bdisp_dbg_perf_end(struct bdisp_dev *bdisp);
diff --git a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c
index 5baada4..dbe7788 100644
--- a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c
+++ b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c
@@ -77,9 +77,9 @@
add_timer(&fei->timer);
}
-static void channel_swdemux_tsklet(unsigned long data)
+static void channel_swdemux_tsklet(struct tasklet_struct *t)
{
- struct channel_info *channel = (struct channel_info *)data;
+ struct channel_info *channel = from_tasklet(channel, t, tsklet);
struct c8sectpfei *fei;
unsigned long wp, rp;
int pos, num_packets, n, size;
@@ -208,8 +208,7 @@
dev_dbg(fei->dev, "Starting channel=%p\n", channel);
- tasklet_init(&channel->tsklet, channel_swdemux_tsklet,
- (unsigned long) channel);
+ tasklet_setup(&channel->tsklet, channel_swdemux_tsklet);
/* Reset the internal inputblock sram pointers */
writel(channel->fifo,
@@ -638,8 +637,7 @@
writel(tsin->back_buffer_busaddr, tsin->irec + DMA_PRDS_BUSRP_TP(0));
/* initialize tasklet */
- tasklet_init(&tsin->tsklet, channel_swdemux_tsklet,
- (unsigned long) tsin);
+ tasklet_setup(&tsin->tsklet, channel_swdemux_tsklet);
return 0;
diff --git a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-debugfs.c b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-debugfs.c
index 8f0ddcb..301fa10 100644
--- a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-debugfs.c
+++ b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-debugfs.c
@@ -225,36 +225,16 @@
void c8sectpfe_debugfs_init(struct c8sectpfei *fei)
{
- struct dentry *root;
- struct dentry *file;
-
- root = debugfs_create_dir("c8sectpfe", NULL);
- if (!root)
- goto err;
-
- fei->root = root;
-
fei->regset = devm_kzalloc(fei->dev, sizeof(*fei->regset), GFP_KERNEL);
if (!fei->regset)
- goto err;
+ return;
fei->regset->regs = fei_sys_regs;
fei->regset->nregs = ARRAY_SIZE(fei_sys_regs);
fei->regset->base = fei->io;
- file = debugfs_create_regset32("registers", S_IRUGO, root,
- fei->regset);
- if (!file) {
- dev_err(fei->dev,
- "%s not able to create 'registers' debugfs\n"
- , __func__);
- goto err;
- }
-
- return;
-
-err:
- debugfs_remove_recursive(root);
+ fei->root = debugfs_create_dir("c8sectpfe", NULL);
+ debugfs_create_regset32("registers", S_IRUGO, fei->root, fei->regset);
}
void c8sectpfe_debugfs_exit(struct c8sectpfei *fei)
diff --git a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-dvb.c b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-dvb.c
index a79250a..0560a9c 100644
--- a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-dvb.c
+++ b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-dvb.c
@@ -170,8 +170,9 @@
/* attach tuner */
request_module("tda18212");
- client = i2c_new_device(tsin->i2c_adapter, &tda18212_info);
- if (!client || !client->dev.driver) {
+ client = i2c_new_client_device(tsin->i2c_adapter,
+ &tda18212_info);
+ if (!i2c_client_has_driver(client)) {
dvb_frontend_detach(*fe);
return -ENODEV;
}
diff --git a/drivers/media/platform/sti/cec/Makefile b/drivers/media/platform/sti/cec/Makefile
deleted file mode 100644
index d0c6b4a..0000000
--- 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/sti/cec/stih-cec.c b/drivers/media/platform/sti/cec/stih-cec.c
deleted file mode 100644
index 8118c73..0000000
--- a/drivers/media/platform/sti/cec/stih-cec.c
+++ /dev/null
@@ -1,400 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * STIH4xx CEC driver
- * Copyright (C) STMicroelectronics SA 2016
- *
- */
-#include <linux/clk.h>
-#include <linux/interrupt.h>
-#include <linux/kernel.h>
-#include <linux/mfd/syscon.h>
-#include <linux/module.h>
-#include <linux/of.h>
-#include <linux/of_platform.h>
-#include <linux/platform_device.h>
-
-#include <media/cec.h>
-#include <media/cec-notifier.h>
-
-#define CEC_NAME "stih-cec"
-
-/* CEC registers */
-#define CEC_CLK_DIV 0x0
-#define CEC_CTRL 0x4
-#define CEC_IRQ_CTRL 0x8
-#define CEC_STATUS 0xC
-#define CEC_EXT_STATUS 0x10
-#define CEC_TX_CTRL 0x14
-#define CEC_FREE_TIME_THRESH 0x18
-#define CEC_BIT_TOUT_THRESH 0x1C
-#define CEC_BIT_PULSE_THRESH 0x20
-#define CEC_DATA 0x24
-#define CEC_TX_ARRAY_CTRL 0x28
-#define CEC_CTRL2 0x2C
-#define CEC_TX_ERROR_STS 0x30
-#define CEC_ADDR_TABLE 0x34
-#define CEC_DATA_ARRAY_CTRL 0x38
-#define CEC_DATA_ARRAY_STATUS 0x3C
-#define CEC_TX_DATA_BASE 0x40
-#define CEC_TX_DATA_TOP 0x50
-#define CEC_TX_DATA_SIZE 0x1
-#define CEC_RX_DATA_BASE 0x54
-#define CEC_RX_DATA_TOP 0x64
-#define CEC_RX_DATA_SIZE 0x1
-
-/* CEC_CTRL2 */
-#define CEC_LINE_INACTIVE_EN BIT(0)
-#define CEC_AUTO_BUS_ERR_EN BIT(1)
-#define CEC_STOP_ON_ARB_ERR_EN BIT(2)
-#define CEC_TX_REQ_WAIT_EN BIT(3)
-
-/* CEC_DATA_ARRAY_CTRL */
-#define CEC_TX_ARRAY_EN BIT(0)
-#define CEC_RX_ARRAY_EN BIT(1)
-#define CEC_TX_ARRAY_RESET BIT(2)
-#define CEC_RX_ARRAY_RESET BIT(3)
-#define CEC_TX_N_OF_BYTES_IRQ_EN BIT(4)
-#define CEC_TX_STOP_ON_NACK BIT(7)
-
-/* CEC_TX_ARRAY_CTRL */
-#define CEC_TX_N_OF_BYTES 0x1F
-#define CEC_TX_START BIT(5)
-#define CEC_TX_AUTO_SOM_EN BIT(6)
-#define CEC_TX_AUTO_EOM_EN BIT(7)
-
-/* CEC_IRQ_CTRL */
-#define CEC_TX_DONE_IRQ_EN BIT(0)
-#define CEC_ERROR_IRQ_EN BIT(2)
-#define CEC_RX_DONE_IRQ_EN BIT(3)
-#define CEC_RX_SOM_IRQ_EN BIT(4)
-#define CEC_RX_EOM_IRQ_EN BIT(5)
-#define CEC_FREE_TIME_IRQ_EN BIT(6)
-#define CEC_PIN_STS_IRQ_EN BIT(7)
-
-/* CEC_CTRL */
-#define CEC_IN_FILTER_EN BIT(0)
-#define CEC_PWR_SAVE_EN BIT(1)
-#define CEC_EN BIT(4)
-#define CEC_ACK_CTRL BIT(5)
-#define CEC_RX_RESET_EN BIT(6)
-#define CEC_IGNORE_RX_ERROR BIT(7)
-
-/* CEC_STATUS */
-#define CEC_TX_DONE_STS BIT(0)
-#define CEC_TX_ACK_GET_STS BIT(1)
-#define CEC_ERROR_STS BIT(2)
-#define CEC_RX_DONE_STS BIT(3)
-#define CEC_RX_SOM_STS BIT(4)
-#define CEC_RX_EOM_STS BIT(5)
-#define CEC_FREE_TIME_IRQ_STS BIT(6)
-#define CEC_PIN_STS BIT(7)
-#define CEC_SBIT_TOUT_STS BIT(8)
-#define CEC_DBIT_TOUT_STS BIT(9)
-#define CEC_LPULSE_ERROR_STS BIT(10)
-#define CEC_HPULSE_ERROR_STS BIT(11)
-#define CEC_TX_ERROR BIT(12)
-#define CEC_TX_ARB_ERROR BIT(13)
-#define CEC_RX_ERROR_MIN BIT(14)
-#define CEC_RX_ERROR_MAX BIT(15)
-
-/* Signal free time in bit periods (2.4ms) */
-#define CEC_PRESENT_INIT_SFT 7
-#define CEC_NEW_INIT_SFT 5
-#define CEC_RETRANSMIT_SFT 3
-
-/* Constants for CEC_BIT_TOUT_THRESH register */
-#define CEC_SBIT_TOUT_47MS BIT(1)
-#define CEC_SBIT_TOUT_48MS (BIT(0) | BIT(1))
-#define CEC_SBIT_TOUT_50MS BIT(2)
-#define CEC_DBIT_TOUT_27MS BIT(0)
-#define CEC_DBIT_TOUT_28MS BIT(1)
-#define CEC_DBIT_TOUT_29MS (BIT(0) | BIT(1))
-
-/* Constants for CEC_BIT_PULSE_THRESH register */
-#define CEC_BIT_LPULSE_03MS BIT(1)
-#define CEC_BIT_HPULSE_03MS BIT(3)
-
-/* Constants for CEC_DATA_ARRAY_STATUS register */
-#define CEC_RX_N_OF_BYTES 0x1F
-#define CEC_TX_N_OF_BYTES_SENT BIT(5)
-#define CEC_RX_OVERRUN BIT(6)
-
-struct stih_cec {
- struct cec_adapter *adap;
- struct device *dev;
- struct clk *clk;
- void __iomem *regs;
- int irq;
- u32 irq_status;
- struct cec_notifier *notifier;
-};
-
-static int stih_cec_adap_enable(struct cec_adapter *adap, bool enable)
-{
- struct stih_cec *cec = cec_get_drvdata(adap);
-
- if (enable) {
- /* The doc says (input TCLK_PERIOD * CEC_CLK_DIV) = 0.1ms */
- unsigned long clk_freq = clk_get_rate(cec->clk);
- u32 cec_clk_div = clk_freq / 10000;
-
- writel(cec_clk_div, cec->regs + CEC_CLK_DIV);
-
- /* Configuration of the durations activating a timeout */
- writel(CEC_SBIT_TOUT_47MS | (CEC_DBIT_TOUT_28MS << 4),
- cec->regs + CEC_BIT_TOUT_THRESH);
-
- /* Configuration of the smallest allowed duration for pulses */
- writel(CEC_BIT_LPULSE_03MS | CEC_BIT_HPULSE_03MS,
- cec->regs + CEC_BIT_PULSE_THRESH);
-
- /* Minimum received bit period threshold */
- writel(BIT(5) | BIT(7), cec->regs + CEC_TX_CTRL);
-
- /* Configuration of transceiver data arrays */
- writel(CEC_TX_ARRAY_EN | CEC_RX_ARRAY_EN | CEC_TX_STOP_ON_NACK,
- cec->regs + CEC_DATA_ARRAY_CTRL);
-
- /* Configuration of the control bits for CEC Transceiver */
- writel(CEC_IN_FILTER_EN | CEC_EN | CEC_RX_RESET_EN,
- cec->regs + CEC_CTRL);
-
- /* Clear logical addresses */
- writel(0, cec->regs + CEC_ADDR_TABLE);
-
- /* Clear the status register */
- writel(0x0, cec->regs + CEC_STATUS);
-
- /* Enable the interrupts */
- writel(CEC_TX_DONE_IRQ_EN | CEC_RX_DONE_IRQ_EN |
- CEC_RX_SOM_IRQ_EN | CEC_RX_EOM_IRQ_EN |
- CEC_ERROR_IRQ_EN,
- cec->regs + CEC_IRQ_CTRL);
-
- } else {
- /* Clear logical addresses */
- writel(0, cec->regs + CEC_ADDR_TABLE);
-
- /* Clear the status register */
- writel(0x0, cec->regs + CEC_STATUS);
-
- /* Disable the interrupts */
- writel(0, cec->regs + CEC_IRQ_CTRL);
- }
-
- return 0;
-}
-
-static int stih_cec_adap_log_addr(struct cec_adapter *adap, u8 logical_addr)
-{
- struct stih_cec *cec = cec_get_drvdata(adap);
- u32 reg = readl(cec->regs + CEC_ADDR_TABLE);
-
- reg |= 1 << logical_addr;
-
- if (logical_addr == CEC_LOG_ADDR_INVALID)
- reg = 0;
-
- writel(reg, cec->regs + CEC_ADDR_TABLE);
-
- return 0;
-}
-
-static int stih_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,
- u32 signal_free_time, struct cec_msg *msg)
-{
- struct stih_cec *cec = cec_get_drvdata(adap);
- int i;
-
- /* Copy message into registers */
- for (i = 0; i < msg->len; i++)
- writeb(msg->msg[i], cec->regs + CEC_TX_DATA_BASE + i);
-
- /*
- * Start transmission, configure hardware to add start and stop bits
- * Signal free time is handled by the hardware
- */
- writel(CEC_TX_AUTO_SOM_EN | CEC_TX_AUTO_EOM_EN | CEC_TX_START |
- msg->len, cec->regs + CEC_TX_ARRAY_CTRL);
-
- return 0;
-}
-
-static void stih_tx_done(struct stih_cec *cec, u32 status)
-{
- if (status & CEC_TX_ERROR) {
- cec_transmit_attempt_done(cec->adap, CEC_TX_STATUS_ERROR);
- return;
- }
-
- if (status & CEC_TX_ARB_ERROR) {
- cec_transmit_attempt_done(cec->adap, CEC_TX_STATUS_ARB_LOST);
- return;
- }
-
- if (!(status & CEC_TX_ACK_GET_STS)) {
- cec_transmit_attempt_done(cec->adap, CEC_TX_STATUS_NACK);
- return;
- }
-
- cec_transmit_attempt_done(cec->adap, CEC_TX_STATUS_OK);
-}
-
-static void stih_rx_done(struct stih_cec *cec, u32 status)
-{
- struct cec_msg msg = {};
- u8 i;
-
- if (status & CEC_RX_ERROR_MIN)
- return;
-
- if (status & CEC_RX_ERROR_MAX)
- return;
-
- msg.len = readl(cec->regs + CEC_DATA_ARRAY_STATUS) & 0x1f;
-
- if (!msg.len)
- return;
-
- if (msg.len > 16)
- msg.len = 16;
-
- for (i = 0; i < msg.len; i++)
- msg.msg[i] = readl(cec->regs + CEC_RX_DATA_BASE + i);
-
- cec_received_msg(cec->adap, &msg);
-}
-
-static irqreturn_t stih_cec_irq_handler_thread(int irq, void *priv)
-{
- struct stih_cec *cec = priv;
-
- if (cec->irq_status & CEC_TX_DONE_STS)
- stih_tx_done(cec, cec->irq_status);
-
- if (cec->irq_status & CEC_RX_DONE_STS)
- stih_rx_done(cec, cec->irq_status);
-
- cec->irq_status = 0;
-
- return IRQ_HANDLED;
-}
-
-static irqreturn_t stih_cec_irq_handler(int irq, void *priv)
-{
- struct stih_cec *cec = priv;
-
- cec->irq_status = readl(cec->regs + CEC_STATUS);
- writel(cec->irq_status, cec->regs + CEC_STATUS);
-
- return IRQ_WAKE_THREAD;
-}
-
-static const struct cec_adap_ops sti_cec_adap_ops = {
- .adap_enable = stih_cec_adap_enable,
- .adap_log_addr = stih_cec_adap_log_addr,
- .adap_transmit = stih_cec_adap_transmit,
-};
-
-static int stih_cec_probe(struct platform_device *pdev)
-{
- struct device *dev = &pdev->dev;
- struct resource *res;
- struct stih_cec *cec;
- struct device *hdmi_dev;
- int ret;
-
- hdmi_dev = cec_notifier_parse_hdmi_phandle(dev);
-
- if (IS_ERR(hdmi_dev))
- return PTR_ERR(hdmi_dev);
-
- cec = devm_kzalloc(dev, sizeof(*cec), GFP_KERNEL);
- if (!cec)
- return -ENOMEM;
-
- cec->dev = dev;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- cec->regs = devm_ioremap_resource(dev, res);
- if (IS_ERR(cec->regs))
- return PTR_ERR(cec->regs);
-
- cec->irq = platform_get_irq(pdev, 0);
- if (cec->irq < 0)
- return cec->irq;
-
- ret = devm_request_threaded_irq(dev, cec->irq, stih_cec_irq_handler,
- stih_cec_irq_handler_thread, 0,
- pdev->name, cec);
- if (ret)
- return ret;
-
- cec->clk = devm_clk_get(dev, "cec-clk");
- if (IS_ERR(cec->clk)) {
- dev_err(dev, "Cannot get cec clock\n");
- return PTR_ERR(cec->clk);
- }
-
- cec->adap = cec_allocate_adapter(&sti_cec_adap_ops, cec, CEC_NAME,
- CEC_CAP_DEFAULTS |
- CEC_CAP_CONNECTOR_INFO,
- CEC_MAX_LOG_ADDRS);
- ret = PTR_ERR_OR_ZERO(cec->adap);
- if (ret)
- return ret;
-
- cec->notifier = cec_notifier_cec_adap_register(hdmi_dev, NULL,
- cec->adap);
- if (!cec->notifier) {
- ret = -ENOMEM;
- goto err_delete_adapter;
- }
-
- ret = cec_register_adapter(cec->adap, &pdev->dev);
- if (ret)
- goto err_notifier;
-
- platform_set_drvdata(pdev, cec);
- return 0;
-
-err_notifier:
- cec_notifier_cec_adap_unregister(cec->notifier);
-
-err_delete_adapter:
- cec_delete_adapter(cec->adap);
- return ret;
-}
-
-static int stih_cec_remove(struct platform_device *pdev)
-{
- struct stih_cec *cec = platform_get_drvdata(pdev);
-
- cec_notifier_cec_adap_unregister(cec->notifier);
- cec_unregister_adapter(cec->adap);
-
- return 0;
-}
-
-static const struct of_device_id stih_cec_match[] = {
- {
- .compatible = "st,stih-cec",
- },
- {},
-};
-MODULE_DEVICE_TABLE(of, stih_cec_match);
-
-static struct platform_driver stih_cec_pdrv = {
- .probe = stih_cec_probe,
- .remove = stih_cec_remove,
- .driver = {
- .name = CEC_NAME,
- .of_match_table = stih_cec_match,
- },
-};
-
-module_platform_driver(stih_cec_pdrv);
-
-MODULE_AUTHOR("Benjamin Gaignard <benjamin.gaignard@linaro.org>");
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("STIH4xx CEC driver");
diff --git a/drivers/media/platform/sti/delta/delta-v4l2.c b/drivers/media/platform/sti/delta/delta-v4l2.c
index 2791107..c691b3d 100644
--- a/drivers/media/platform/sti/delta/delta-v4l2.c
+++ b/drivers/media/platform/sti/delta/delta-v4l2.c
@@ -1783,7 +1783,7 @@
snprintf(vdev->name, sizeof(vdev->name), "%s-%s",
DELTA_NAME, DELTA_FW_VERSION);
- ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
+ ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
if (ret) {
dev_err(delta->dev, "%s failed to register video device\n",
DELTA_PREFIX);
diff --git a/drivers/media/platform/sti/hva/hva-debugfs.c b/drivers/media/platform/sti/hva/hva-debugfs.c
index 7d12a5b..a86a07b 100644
--- a/drivers/media/platform/sti/hva/hva-debugfs.c
+++ b/drivers/media/platform/sti/hva/hva-debugfs.c
@@ -337,25 +337,11 @@
void hva_debugfs_create(struct hva_dev *hva)
{
hva->dbg.debugfs_entry = debugfs_create_dir(HVA_NAME, NULL);
- if (!hva->dbg.debugfs_entry)
- goto err;
- if (!hva_dbg_create_entry(device))
- goto err;
-
- if (!hva_dbg_create_entry(encoders))
- goto err;
-
- if (!hva_dbg_create_entry(last))
- goto err;
-
- if (!hva_dbg_create_entry(regs))
- goto err;
-
- return;
-
-err:
- hva_debugfs_remove(hva);
+ hva_dbg_create_entry(device);
+ hva_dbg_create_entry(encoders);
+ hva_dbg_create_entry(last);
+ hva_dbg_create_entry(regs);
}
void hva_debugfs_remove(struct hva_dev *hva)
diff --git a/drivers/media/platform/sti/hva/hva-v4l2.c b/drivers/media/platform/sti/hva/hva-v4l2.c
index 64004d1..bb34d69 100644
--- a/drivers/media/platform/sti/hva/hva-v4l2.c
+++ b/drivers/media/platform/sti/hva/hva-v4l2.c
@@ -1087,7 +1087,7 @@
if ((V4L2_TYPE_IS_OUTPUT(vq->type) &&
vb2_is_streaming(&ctx->fh.m2m_ctx->cap_q_ctx.q)) ||
- (!V4L2_TYPE_IS_OUTPUT(vq->type) &&
+ (V4L2_TYPE_IS_CAPTURE(vq->type) &&
vb2_is_streaming(&ctx->fh.m2m_ctx->out_q_ctx.q))) {
dev_dbg(dev, "%s %s out=%d cap=%d\n",
ctx->name, to_type_str(vq->type),
@@ -1316,7 +1316,7 @@
snprintf(vdev->name, sizeof(vdev->name), "%s%lx", HVA_NAME,
hva->ip_version);
- ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
+ ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
if (ret) {
dev_err(dev, "%s failed to register video device\n",
HVA_PREFIX);
diff --git a/drivers/media/platform/stm32/Makefile b/drivers/media/platform/stm32/Makefile
index 5ed7359..48b36db 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/stm32/stm32-cec.c b/drivers/media/platform/stm32/stm32-cec.c
deleted file mode 100644
index 8a86b2c..0000000
--- a/drivers/media/platform/stm32/stm32-cec.c
+++ /dev/null
@@ -1,368 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * STM32 CEC driver
- * Copyright (C) STMicroelectronics SA 2017
- *
- */
-
-#include <linux/clk.h>
-#include <linux/interrupt.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/of.h>
-#include <linux/of_device.h>
-#include <linux/platform_device.h>
-#include <linux/regmap.h>
-
-#include <media/cec.h>
-
-#define CEC_NAME "stm32-cec"
-
-/* CEC registers */
-#define CEC_CR 0x0000 /* Control Register */
-#define CEC_CFGR 0x0004 /* ConFiGuration Register */
-#define CEC_TXDR 0x0008 /* Rx data Register */
-#define CEC_RXDR 0x000C /* Rx data Register */
-#define CEC_ISR 0x0010 /* Interrupt and status Register */
-#define CEC_IER 0x0014 /* Interrupt enable Register */
-
-#define TXEOM BIT(2)
-#define TXSOM BIT(1)
-#define CECEN BIT(0)
-
-#define LSTN BIT(31)
-#define OAR GENMASK(30, 16)
-#define SFTOP BIT(8)
-#define BRDNOGEN BIT(7)
-#define LBPEGEN BIT(6)
-#define BREGEN BIT(5)
-#define BRESTP BIT(4)
-#define RXTOL BIT(3)
-#define SFT GENMASK(2, 0)
-#define FULL_CFG (LSTN | SFTOP | BRDNOGEN | LBPEGEN | BREGEN | BRESTP \
- | RXTOL)
-
-#define TXACKE BIT(12)
-#define TXERR BIT(11)
-#define TXUDR BIT(10)
-#define TXEND BIT(9)
-#define TXBR BIT(8)
-#define ARBLST BIT(7)
-#define RXACKE BIT(6)
-#define RXOVR BIT(2)
-#define RXEND BIT(1)
-#define RXBR BIT(0)
-
-#define ALL_TX_IT (TXEND | TXBR | TXACKE | TXERR | TXUDR | ARBLST)
-#define ALL_RX_IT (RXEND | RXBR | RXACKE | RXOVR)
-
-/*
- * 400 ms is the time it takes for one 16 byte message to be
- * transferred and 5 is the maximum number of retries. Add
- * another 100 ms as a margin.
- */
-#define CEC_XFER_TIMEOUT_MS (5 * 400 + 100)
-
-struct stm32_cec {
- struct cec_adapter *adap;
- struct device *dev;
- struct clk *clk_cec;
- struct clk *clk_hdmi_cec;
- struct reset_control *rstc;
- struct regmap *regmap;
- int irq;
- u32 irq_status;
- struct cec_msg rx_msg;
- struct cec_msg tx_msg;
- int tx_cnt;
-};
-
-static void cec_hw_init(struct stm32_cec *cec)
-{
- regmap_update_bits(cec->regmap, CEC_CR, TXEOM | TXSOM | CECEN, 0);
-
- regmap_update_bits(cec->regmap, CEC_IER, ALL_TX_IT | ALL_RX_IT,
- ALL_TX_IT | ALL_RX_IT);
-
- regmap_update_bits(cec->regmap, CEC_CFGR, FULL_CFG, FULL_CFG);
-}
-
-static void stm32_tx_done(struct stm32_cec *cec, u32 status)
-{
- if (status & (TXERR | TXUDR)) {
- cec_transmit_done(cec->adap, CEC_TX_STATUS_ERROR,
- 0, 0, 0, 1);
- return;
- }
-
- if (status & ARBLST) {
- cec_transmit_done(cec->adap, CEC_TX_STATUS_ARB_LOST,
- 1, 0, 0, 0);
- return;
- }
-
- if (status & TXACKE) {
- cec_transmit_done(cec->adap, CEC_TX_STATUS_NACK,
- 0, 1, 0, 0);
- return;
- }
-
- if (cec->irq_status & TXBR) {
- /* send next byte */
- if (cec->tx_cnt < cec->tx_msg.len)
- regmap_write(cec->regmap, CEC_TXDR,
- cec->tx_msg.msg[cec->tx_cnt++]);
-
- /* TXEOM is set to command transmission of the last byte */
- if (cec->tx_cnt == cec->tx_msg.len)
- regmap_update_bits(cec->regmap, CEC_CR, TXEOM, TXEOM);
- }
-
- if (cec->irq_status & TXEND)
- cec_transmit_done(cec->adap, CEC_TX_STATUS_OK, 0, 0, 0, 0);
-}
-
-static void stm32_rx_done(struct stm32_cec *cec, u32 status)
-{
- if (cec->irq_status & (RXACKE | RXOVR)) {
- cec->rx_msg.len = 0;
- return;
- }
-
- if (cec->irq_status & RXBR) {
- u32 val;
-
- regmap_read(cec->regmap, CEC_RXDR, &val);
- cec->rx_msg.msg[cec->rx_msg.len++] = val & 0xFF;
- }
-
- if (cec->irq_status & RXEND) {
- cec_received_msg(cec->adap, &cec->rx_msg);
- cec->rx_msg.len = 0;
- }
-}
-
-static irqreturn_t stm32_cec_irq_thread(int irq, void *arg)
-{
- struct stm32_cec *cec = arg;
-
- if (cec->irq_status & ALL_TX_IT)
- stm32_tx_done(cec, cec->irq_status);
-
- if (cec->irq_status & ALL_RX_IT)
- stm32_rx_done(cec, cec->irq_status);
-
- cec->irq_status = 0;
-
- return IRQ_HANDLED;
-}
-
-static irqreturn_t stm32_cec_irq_handler(int irq, void *arg)
-{
- struct stm32_cec *cec = arg;
-
- regmap_read(cec->regmap, CEC_ISR, &cec->irq_status);
-
- regmap_update_bits(cec->regmap, CEC_ISR,
- ALL_TX_IT | ALL_RX_IT,
- ALL_TX_IT | ALL_RX_IT);
-
- return IRQ_WAKE_THREAD;
-}
-
-static int stm32_cec_adap_enable(struct cec_adapter *adap, bool enable)
-{
- struct stm32_cec *cec = adap->priv;
- int ret = 0;
-
- if (enable) {
- ret = clk_enable(cec->clk_cec);
- if (ret)
- dev_err(cec->dev, "fail to enable cec clock\n");
-
- clk_enable(cec->clk_hdmi_cec);
- regmap_update_bits(cec->regmap, CEC_CR, CECEN, CECEN);
- } else {
- clk_disable(cec->clk_cec);
- clk_disable(cec->clk_hdmi_cec);
- regmap_update_bits(cec->regmap, CEC_CR, CECEN, 0);
- }
-
- return ret;
-}
-
-static int stm32_cec_adap_log_addr(struct cec_adapter *adap, u8 logical_addr)
-{
- struct stm32_cec *cec = adap->priv;
- u32 oar = (1 << logical_addr) << 16;
- u32 val;
-
- /* Poll every 100µs the register CEC_CR to wait end of transmission */
- regmap_read_poll_timeout(cec->regmap, CEC_CR, val, !(val & TXSOM),
- 100, CEC_XFER_TIMEOUT_MS * 1000);
- regmap_update_bits(cec->regmap, CEC_CR, CECEN, 0);
-
- if (logical_addr == CEC_LOG_ADDR_INVALID)
- regmap_update_bits(cec->regmap, CEC_CFGR, OAR, 0);
- else
- regmap_update_bits(cec->regmap, CEC_CFGR, oar, oar);
-
- regmap_update_bits(cec->regmap, CEC_CR, CECEN, CECEN);
-
- return 0;
-}
-
-static int stm32_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,
- u32 signal_free_time, struct cec_msg *msg)
-{
- struct stm32_cec *cec = adap->priv;
-
- /* Copy message */
- cec->tx_msg = *msg;
- cec->tx_cnt = 0;
-
- /*
- * If the CEC message consists of only one byte,
- * TXEOM must be set before of TXSOM.
- */
- if (cec->tx_msg.len == 1)
- regmap_update_bits(cec->regmap, CEC_CR, TXEOM, TXEOM);
-
- /* TXSOM is set to command transmission of the first byte */
- regmap_update_bits(cec->regmap, CEC_CR, TXSOM, TXSOM);
-
- /* Write the header (first byte of message) */
- regmap_write(cec->regmap, CEC_TXDR, cec->tx_msg.msg[0]);
- cec->tx_cnt++;
-
- return 0;
-}
-
-static const struct cec_adap_ops stm32_cec_adap_ops = {
- .adap_enable = stm32_cec_adap_enable,
- .adap_log_addr = stm32_cec_adap_log_addr,
- .adap_transmit = stm32_cec_adap_transmit,
-};
-
-static const struct regmap_config stm32_cec_regmap_cfg = {
- .reg_bits = 32,
- .val_bits = 32,
- .reg_stride = sizeof(u32),
- .max_register = 0x14,
- .fast_io = true,
-};
-
-static int stm32_cec_probe(struct platform_device *pdev)
-{
- u32 caps = CEC_CAP_DEFAULTS | CEC_CAP_PHYS_ADDR | CEC_MODE_MONITOR_ALL;
- struct resource *res;
- struct stm32_cec *cec;
- void __iomem *mmio;
- int ret;
-
- cec = devm_kzalloc(&pdev->dev, sizeof(*cec), GFP_KERNEL);
- if (!cec)
- return -ENOMEM;
-
- cec->dev = &pdev->dev;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- mmio = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(mmio))
- return PTR_ERR(mmio);
-
- cec->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "cec", mmio,
- &stm32_cec_regmap_cfg);
-
- if (IS_ERR(cec->regmap))
- return PTR_ERR(cec->regmap);
-
- cec->irq = platform_get_irq(pdev, 0);
- if (cec->irq < 0)
- return cec->irq;
-
- ret = devm_request_threaded_irq(&pdev->dev, cec->irq,
- stm32_cec_irq_handler,
- stm32_cec_irq_thread,
- 0,
- pdev->name, cec);
- if (ret)
- return ret;
-
- cec->clk_cec = devm_clk_get(&pdev->dev, "cec");
- if (IS_ERR(cec->clk_cec)) {
- dev_err(&pdev->dev, "Cannot get cec clock\n");
- return PTR_ERR(cec->clk_cec);
- }
-
- ret = clk_prepare(cec->clk_cec);
- if (ret) {
- dev_err(&pdev->dev, "Unable to prepare cec clock\n");
- return ret;
- }
-
- cec->clk_hdmi_cec = devm_clk_get(&pdev->dev, "hdmi-cec");
- if (!IS_ERR(cec->clk_hdmi_cec)) {
- ret = clk_prepare(cec->clk_hdmi_cec);
- if (ret) {
- dev_err(&pdev->dev, "Unable to prepare hdmi-cec clock\n");
- return ret;
- }
- }
-
- /*
- * CEC_CAP_PHYS_ADDR caps should be removed when a cec notifier is
- * available for example when a drm driver can provide edid
- */
- cec->adap = cec_allocate_adapter(&stm32_cec_adap_ops, cec,
- CEC_NAME, caps, CEC_MAX_LOG_ADDRS);
- ret = PTR_ERR_OR_ZERO(cec->adap);
- if (ret)
- return ret;
-
- ret = cec_register_adapter(cec->adap, &pdev->dev);
- if (ret) {
- cec_delete_adapter(cec->adap);
- return ret;
- }
-
- cec_hw_init(cec);
-
- platform_set_drvdata(pdev, cec);
-
- return 0;
-}
-
-static int stm32_cec_remove(struct platform_device *pdev)
-{
- struct stm32_cec *cec = platform_get_drvdata(pdev);
-
- clk_unprepare(cec->clk_cec);
- clk_unprepare(cec->clk_hdmi_cec);
-
- cec_unregister_adapter(cec->adap);
-
- return 0;
-}
-
-static const struct of_device_id stm32_cec_of_match[] = {
- { .compatible = "st,stm32-cec" },
- { /* end node */ }
-};
-MODULE_DEVICE_TABLE(of, stm32_cec_of_match);
-
-static struct platform_driver stm32_cec_driver = {
- .probe = stm32_cec_probe,
- .remove = stm32_cec_remove,
- .driver = {
- .name = CEC_NAME,
- .of_match_table = stm32_cec_of_match,
- },
-};
-
-module_platform_driver(stm32_cec_driver);
-
-MODULE_AUTHOR("Benjamin Gaignard <benjamin.gaignard@st.com>");
-MODULE_AUTHOR("Yannick Fertre <yannick.fertre@st.com>");
-MODULE_DESCRIPTION("STMicroelectronics STM32 Consumer Electronics Control");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/stm32/stm32-dcmi.c b/drivers/media/platform/stm32/stm32-dcmi.c
index d41475f..233e4d3 100644
--- a/drivers/media/platform/stm32/stm32-dcmi.c
+++ b/drivers/media/platform/stm32/stm32-dcmi.c
@@ -135,6 +135,7 @@
int sequence;
struct list_head buffers;
struct dcmi_buf *active;
+ int irq;
struct v4l2_device v4l2_dev;
struct video_device *vdev;
@@ -1720,6 +1721,14 @@
return ret;
}
+ ret = devm_request_threaded_irq(dcmi->dev, dcmi->irq, dcmi_irq_callback,
+ dcmi_irq_thread, IRQF_ONESHOT,
+ dev_name(dcmi->dev), dcmi);
+ if (ret) {
+ dev_err(dcmi->dev, "Unable to request irq %d\n", dcmi->irq);
+ return ret;
+ }
+
return 0;
}
@@ -1881,6 +1890,8 @@
if (irq <= 0)
return irq ? irq : -ENXIO;
+ dcmi->irq = irq;
+
dcmi->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!dcmi->res) {
dev_err(&pdev->dev, "Could not get resource\n");
@@ -1893,14 +1904,6 @@
return PTR_ERR(dcmi->regs);
}
- ret = devm_request_threaded_irq(&pdev->dev, irq, dcmi_irq_callback,
- dcmi_irq_thread, IRQF_ONESHOT,
- dev_name(&pdev->dev), dcmi);
- if (ret) {
- dev_err(&pdev->dev, "Unable to request irq %d\n", irq);
- return ret;
- }
-
mclk = devm_clk_get(&pdev->dev, "mclk");
if (IS_ERR(mclk)) {
if (PTR_ERR(mclk) != -EPROBE_DEFER)
@@ -1908,10 +1911,13 @@
return PTR_ERR(mclk);
}
- chan = dma_request_slave_channel(&pdev->dev, "tx");
- if (!chan) {
- dev_info(&pdev->dev, "Unable to request DMA channel, defer probing\n");
- return -EPROBE_DEFER;
+ chan = dma_request_chan(&pdev->dev, "tx");
+ if (IS_ERR(chan)) {
+ ret = PTR_ERR(chan);
+ if (ret != -EPROBE_DEFER)
+ dev_err(&pdev->dev,
+ "Failed to request DMA channel: %d\n", ret);
+ return ret;
}
spin_lock_init(&dcmi->irqlock);
@@ -1969,7 +1975,7 @@
}
dcmi->vdev->entity.flags |= MEDIA_ENT_FL_DEFAULT;
- ret = video_register_device(dcmi->vdev, VFL_TYPE_GRABBER, -1);
+ ret = video_register_device(dcmi->vdev, VFL_TYPE_VIDEO, -1);
if (ret) {
dev_err(dcmi->dev, "Failed to register video device\n");
goto err_media_entity_cleanup;
diff --git a/drivers/media/platform/sunxi/Kconfig b/drivers/media/platform/sunxi/Kconfig
index 71808e9..7151cc2 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 a051275..fc537c9 100644
--- a/drivers/media/platform/sunxi/Makefile
+++ b/drivers/media/platform/sunxi/Makefile
@@ -1,2 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0
+
obj-y += sun4i-csi/
obj-y += sun6i-csi/
+obj-y += sun8i-di/
+obj-y += sun8i-rotate/
diff --git a/drivers/media/platform/sunxi/sun4i-csi/Kconfig b/drivers/media/platform/sunxi/sun4i-csi/Kconfig
index e86e29b..903c615 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 7c790a5..5062b00 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/sun4i-csi/sun4i_csi.c b/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c
index b8b07c1..64f2592 100644
--- a/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c
+++ b/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c
@@ -7,6 +7,7 @@
*/
#include <linux/clk.h>
+#include <linux/dma-mapping.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/mutex.h>
@@ -29,6 +30,12 @@
#include "sun4i_csi.h"
+struct sun4i_csi_traits {
+ unsigned int channels;
+ unsigned int max_width;
+ bool has_isp;
+};
+
static const struct media_entity_operations sun4i_csi_video_entity_ops = {
.link_validate = v4l2_subdev_link_validate,
};
@@ -111,6 +118,7 @@
struct v4l2_fwnode_endpoint vep = {
.bus_type = V4L2_MBUS_PARALLEL,
};
+ struct v4l2_async_subdev *asd;
struct fwnode_handle *ep;
int ret;
@@ -127,10 +135,12 @@
csi->bus = vep.bus.parallel;
- ret = v4l2_async_notifier_add_fwnode_remote_subdev(&csi->notifier,
- ep, &csi->asd);
- if (ret)
+ asd = v4l2_async_notifier_add_fwnode_remote_subdev(&csi->notifier,
+ ep, sizeof(*asd));
+ if (IS_ERR(asd)) {
+ ret = PTR_ERR(asd);
goto out;
+ }
csi->notifier.ops = &sun4i_csi_notify_ops;
@@ -156,6 +166,10 @@
subdev = &csi->subdev;
vdev = &csi->vdev;
+ csi->traits = of_device_get_match_data(&pdev->dev);
+ if (!csi->traits)
+ return -EINVAL;
+
/*
* On Allwinner SoCs, some high memory bandwidth devices do DMA
* directly over the memory bus (called MBUS), instead of the
@@ -172,8 +186,14 @@
if (ret)
return ret;
} else {
+ /*
+ * XXX(hch): this has no business in a driver and needs to move
+ * to the device tree.
+ */
#ifdef PHYS_PFN_OFFSET
- csi->dev->dma_pfn_offset = PHYS_PFN_OFFSET;
+ ret = dma_direct_set_offset(csi->dev, PHYS_OFFSET, 0, SZ_4G);
+ if (ret)
+ return ret;
#endif
}
@@ -181,6 +201,8 @@
strscpy(csi->mdev.model, "Allwinner Video Capture Device",
sizeof(csi->mdev.model));
csi->mdev.hw_revision = 0;
+ snprintf(csi->mdev.bus_info, sizeof(csi->mdev.bus_info), "platform:%s",
+ dev_name(csi->dev));
media_device_init(&csi->mdev);
csi->v4l.mdev = &csi->mdev;
@@ -199,10 +221,12 @@
return PTR_ERR(csi->bus_clk);
}
- csi->isp_clk = devm_clk_get(&pdev->dev, "isp");
- if (IS_ERR(csi->isp_clk)) {
- dev_err(&pdev->dev, "Couldn't get our ISP clock\n");
- return PTR_ERR(csi->isp_clk);
+ if (csi->traits->has_isp) {
+ csi->isp_clk = devm_clk_get(&pdev->dev, "isp");
+ if (IS_ERR(csi->isp_clk)) {
+ dev_err(&pdev->dev, "Couldn't get our ISP clock\n");
+ return PTR_ERR(csi->isp_clk);
+ }
}
csi->ram_clk = devm_clk_get(&pdev->dev, "ram");
@@ -273,6 +297,7 @@
v4l2_async_notifier_unregister(&csi->notifier);
v4l2_async_notifier_cleanup(&csi->notifier);
+ vb2_video_unregister_device(&csi->vdev);
media_device_unregister(&csi->mdev);
sun4i_csi_dma_unregister(csi);
media_device_cleanup(&csi->mdev);
@@ -280,8 +305,21 @@
return 0;
}
+static const struct sun4i_csi_traits sun4i_a10_csi1_traits = {
+ .channels = 1,
+ .max_width = 24,
+ .has_isp = false,
+};
+
+static const struct sun4i_csi_traits sun7i_a20_csi0_traits = {
+ .channels = 4,
+ .max_width = 16,
+ .has_isp = true,
+};
+
static const struct of_device_id sun4i_csi_of_match[] = {
- { .compatible = "allwinner,sun7i-a20-csi0" },
+ { .compatible = "allwinner,sun4i-a10-csi1", .data = &sun4i_a10_csi1_traits },
+ { .compatible = "allwinner,sun7i-a20-csi0", .data = &sun7i_a20_csi0_traits },
{ /* Sentinel */ }
};
MODULE_DEVICE_TABLE(of, sun4i_csi_of_match);
diff --git a/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.h b/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.h
index 88d39b3..a5f61ee 100644
--- a/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.h
+++ b/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.h
@@ -108,6 +108,8 @@
/* Device resources */
struct device *dev;
+ const struct sun4i_csi_traits *traits;
+
void __iomem *regs;
struct clk *bus_clk;
struct clk *isp_clk;
@@ -137,7 +139,6 @@
struct v4l2_mbus_framefmt subdev_fmt;
/* V4L2 Async variables */
- struct v4l2_async_subdev asd;
struct v4l2_async_notifier notifier;
struct v4l2_subdev *src_subdev;
int src_pad;
diff --git a/drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c b/drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c
index 78fa1c5..2c39cd7 100644
--- a/drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c
+++ b/drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c
@@ -413,7 +413,7 @@
q->min_buffers_needed = 3;
q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
- q->io_modes = VB2_MMAP;
+ q->io_modes = VB2_MMAP | VB2_DMABUF;
q->lock = &csi->lock;
q->drv_priv = csi;
q->buf_struct_size = sizeof(struct sun4i_csi_buffer);
@@ -431,7 +431,7 @@
ret = v4l2_device_register(csi->dev, &csi->v4l);
if (ret) {
dev_err(csi->dev, "Couldn't register the v4l2 device\n");
- goto err_free_queue;
+ goto err_free_mutex;
}
ret = devm_request_irq(csi->dev, irq, sun4i_csi_irq, 0,
@@ -446,9 +446,6 @@
err_unregister_device:
v4l2_device_unregister(&csi->v4l);
-err_free_queue:
- vb2_queue_release(q);
-
err_free_mutex:
mutex_destroy(&csi->lock);
return ret;
@@ -457,6 +454,5 @@
void sun4i_csi_dma_unregister(struct sun4i_csi *csi)
{
v4l2_device_unregister(&csi->v4l);
- vb2_queue_release(&csi->queue);
mutex_destroy(&csi->lock);
}
diff --git a/drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c b/drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c
index 83a3a02..8f4e254 100644
--- a/drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c
+++ b/drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c
@@ -214,7 +214,7 @@
if (ret < 0)
goto err_pm_put;
- ret = v4l2_pipeline_pm_use(&csi->vdev.entity, 1);
+ ret = v4l2_pipeline_pm_get(&csi->vdev.entity);
if (ret)
goto err_pm_put;
@@ -227,7 +227,7 @@
return 0;
err_pipeline_pm_put:
- v4l2_pipeline_pm_use(&csi->vdev.entity, 0);
+ v4l2_pipeline_pm_put(&csi->vdev.entity);
err_pm_put:
pm_runtime_put(csi->dev);
@@ -242,8 +242,9 @@
mutex_lock(&csi->lock);
- v4l2_fh_release(file);
- v4l2_pipeline_pm_use(&csi->vdev.entity, 0);
+ _vb2_fop_release(file, NULL);
+
+ v4l2_pipeline_pm_put(&csi->vdev.entity);
pm_runtime_put(csi->dev);
mutex_unlock(&csi->lock);
@@ -256,8 +257,6 @@
.open = sun4i_csi_open,
.release = sun4i_csi_release,
.unlocked_ioctl = video_ioctl2,
- .read = vb2_fop_read,
- .write = vb2_fop_write,
.poll = vb2_fop_poll,
.mmap = vb2_fop_mmap,
};
@@ -374,7 +373,7 @@
vdev->ioctl_ops = &sun4i_csi_ioctl_ops;
video_set_drvdata(vdev, csi);
- ret = video_register_device(&csi->vdev, VFL_TYPE_GRABBER, -1);
+ ret = video_register_device(&csi->vdev, VFL_TYPE_VIDEO, -1);
if (ret)
return ret;
diff --git a/drivers/media/platform/sunxi/sun6i-csi/Kconfig b/drivers/media/platform/sunxi/sun6i-csi/Kconfig
index 269b3eb..586e3fb 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/sun6i-csi/sun6i_csi.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c
index 055eb0b..e69e143 100644
--- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c
+++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c
@@ -733,6 +733,8 @@
strscpy(csi->media_dev.model, "Allwinner Video Capture Device",
sizeof(csi->media_dev.model));
csi->media_dev.hw_revision = 0;
+ snprintf(csi->media_dev.bus_info, sizeof(csi->media_dev.bus_info),
+ "platform:%s", dev_name(csi->dev));
media_device_init(&csi->media_dev);
v4l2_async_notifier_init(&csi->notifier);
@@ -897,8 +899,15 @@
return -ENOMEM;
sdev->dev = &pdev->dev;
- /* The DMA bus has the memory mapped at 0 */
- sdev->dev->dma_pfn_offset = PHYS_OFFSET >> PAGE_SHIFT;
+ /*
+ * The DMA bus has the memory mapped at 0.
+ *
+ * XXX(hch): this has no business in a driver and needs to move
+ * to the device tree.
+ */
+ ret = dma_direct_set_offset(sdev->dev, PHYS_OFFSET, 0, SZ_4G);
+ if (ret)
+ return ret;
ret = sun6i_csi_resource_request(sdev, pdev);
if (ret)
diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c
index c43a35d..3181d07 100644
--- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c
+++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c
@@ -476,7 +476,7 @@
if (ret < 0)
goto unlock;
- ret = v4l2_pipeline_pm_use(&video->vdev.entity, 1);
+ ret = v4l2_pipeline_pm_get(&video->vdev.entity);
if (ret < 0)
goto fh_release;
@@ -509,7 +509,7 @@
_vb2_fop_release(file, NULL);
- v4l2_pipeline_pm_use(&video->vdev.entity, 0);
+ v4l2_pipeline_pm_put(&video->vdev.entity);
if (last_fh)
sun6i_csi_set_power(video->csi, false);
@@ -650,7 +650,7 @@
vdev->release = video_device_release_empty;
vdev->fops = &sun6i_video_fops;
vdev->ioctl_ops = &sun6i_video_ioctl_ops;
- vdev->vfl_type = VFL_TYPE_GRABBER;
+ vdev->vfl_type = VFL_TYPE_VIDEO;
vdev->vfl_dir = VFL_DIR_RX;
vdev->v4l2_dev = &csi->v4l2_dev;
vdev->queue = vidq;
@@ -658,17 +658,15 @@
vdev->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE;
video_set_drvdata(vdev, video);
- ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
+ ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
if (ret < 0) {
v4l2_err(&csi->v4l2_dev,
"video_register_device failed: %d\n", ret);
- goto release_vb2;
+ goto clean_entity;
}
return 0;
-release_vb2:
- vb2_queue_release(&video->vb2_vidq);
clean_entity:
media_entity_cleanup(&video->vdev.entity);
mutex_destroy(&video->lock);
@@ -677,8 +675,7 @@
void sun6i_video_cleanup(struct sun6i_video *video)
{
- video_unregister_device(&video->vdev);
+ vb2_video_unregister_device(&video->vdev);
media_entity_cleanup(&video->vdev.entity);
- vb2_queue_release(&video->vb2_vidq);
mutex_destroy(&video->lock);
}
diff --git a/drivers/media/platform/sunxi/sun8i-di/Makefile b/drivers/media/platform/sunxi/sun8i-di/Makefile
new file mode 100644
index 0000000..109f7e5
--- /dev/null
+++ b/drivers/media/platform/sunxi/sun8i-di/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_VIDEO_SUN8I_DEINTERLACE) += sun8i-di.o
diff --git a/drivers/media/platform/sunxi/sun8i-di/sun8i-di.c b/drivers/media/platform/sunxi/sun8i-di/sun8i-di.c
new file mode 100644
index 0000000..2c15948
--- /dev/null
+++ b/drivers/media/platform/sunxi/sun8i-di/sun8i-di.c
@@ -0,0 +1,1022 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Allwinner sun8i deinterlacer with scaler driver
+ *
+ * Copyright (C) 2019 Jernej Skrabec <jernej.skrabec@siol.net>
+ *
+ * Based on vim2m driver.
+ */
+
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/reset.h>
+
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-mem2mem.h>
+
+#include "sun8i-di.h"
+
+#define FLAG_SIZE (DEINTERLACE_MAX_WIDTH * DEINTERLACE_MAX_HEIGHT / 4)
+
+static u32 deinterlace_formats[] = {
+ V4L2_PIX_FMT_NV12,
+ V4L2_PIX_FMT_NV21,
+};
+
+static inline u32 deinterlace_read(struct deinterlace_dev *dev, u32 reg)
+{
+ return readl(dev->base + reg);
+}
+
+static inline void deinterlace_write(struct deinterlace_dev *dev,
+ u32 reg, u32 value)
+{
+ writel(value, dev->base + reg);
+}
+
+static inline void deinterlace_set_bits(struct deinterlace_dev *dev,
+ u32 reg, u32 bits)
+{
+ writel(readl(dev->base + reg) | bits, dev->base + reg);
+}
+
+static inline void deinterlace_clr_set_bits(struct deinterlace_dev *dev,
+ u32 reg, u32 clr, u32 set)
+{
+ u32 val = readl(dev->base + reg);
+
+ val &= ~clr;
+ val |= set;
+
+ writel(val, dev->base + reg);
+}
+
+static void deinterlace_device_run(void *priv)
+{
+ struct deinterlace_ctx *ctx = priv;
+ struct deinterlace_dev *dev = ctx->dev;
+ u32 size, stride, width, height, val;
+ struct vb2_v4l2_buffer *src, *dst;
+ unsigned int hstep, vstep;
+ dma_addr_t addr;
+
+ src = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
+ dst = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
+
+ v4l2_m2m_buf_copy_metadata(src, dst, true);
+
+ deinterlace_write(dev, DEINTERLACE_MOD_ENABLE,
+ DEINTERLACE_MOD_ENABLE_EN);
+
+ if (ctx->field) {
+ deinterlace_write(dev, DEINTERLACE_TILE_FLAG0,
+ ctx->flag1_buf_dma);
+ deinterlace_write(dev, DEINTERLACE_TILE_FLAG1,
+ ctx->flag2_buf_dma);
+ } else {
+ deinterlace_write(dev, DEINTERLACE_TILE_FLAG0,
+ ctx->flag2_buf_dma);
+ deinterlace_write(dev, DEINTERLACE_TILE_FLAG1,
+ ctx->flag1_buf_dma);
+ }
+ deinterlace_write(dev, DEINTERLACE_FLAG_LINE_STRIDE, 0x200);
+
+ width = ctx->src_fmt.width;
+ height = ctx->src_fmt.height;
+ stride = ctx->src_fmt.bytesperline;
+ size = stride * height;
+
+ addr = vb2_dma_contig_plane_dma_addr(&src->vb2_buf, 0);
+ deinterlace_write(dev, DEINTERLACE_BUF_ADDR0, addr);
+ deinterlace_write(dev, DEINTERLACE_BUF_ADDR1, addr + size);
+ deinterlace_write(dev, DEINTERLACE_BUF_ADDR2, 0);
+
+ deinterlace_write(dev, DEINTERLACE_LINE_STRIDE0, stride);
+ deinterlace_write(dev, DEINTERLACE_LINE_STRIDE1, stride);
+
+ deinterlace_write(dev, DEINTERLACE_CH0_IN_SIZE,
+ DEINTERLACE_SIZE(width, height));
+ deinterlace_write(dev, DEINTERLACE_CH1_IN_SIZE,
+ DEINTERLACE_SIZE(width / 2, height / 2));
+
+ val = DEINTERLACE_IN_FMT_FMT(DEINTERLACE_IN_FMT_YUV420) |
+ DEINTERLACE_IN_FMT_MOD(DEINTERLACE_MODE_UV_COMBINED);
+ switch (ctx->src_fmt.pixelformat) {
+ case V4L2_PIX_FMT_NV12:
+ val |= DEINTERLACE_IN_FMT_PS(DEINTERLACE_PS_UVUV);
+ break;
+ case V4L2_PIX_FMT_NV21:
+ val |= DEINTERLACE_IN_FMT_PS(DEINTERLACE_PS_VUVU);
+ break;
+ }
+ deinterlace_write(dev, DEINTERLACE_IN_FMT, val);
+
+ if (ctx->prev)
+ addr = vb2_dma_contig_plane_dma_addr(&ctx->prev->vb2_buf, 0);
+
+ deinterlace_write(dev, DEINTERLACE_PRELUMA, addr);
+ deinterlace_write(dev, DEINTERLACE_PRECHROMA, addr + size);
+
+ val = DEINTERLACE_OUT_FMT_FMT(DEINTERLACE_OUT_FMT_YUV420SP);
+ switch (ctx->src_fmt.pixelformat) {
+ case V4L2_PIX_FMT_NV12:
+ val |= DEINTERLACE_OUT_FMT_PS(DEINTERLACE_PS_UVUV);
+ break;
+ case V4L2_PIX_FMT_NV21:
+ val |= DEINTERLACE_OUT_FMT_PS(DEINTERLACE_PS_VUVU);
+ break;
+ }
+ deinterlace_write(dev, DEINTERLACE_OUT_FMT, val);
+
+ width = ctx->dst_fmt.width;
+ height = ctx->dst_fmt.height;
+ stride = ctx->dst_fmt.bytesperline;
+ size = stride * height;
+
+ deinterlace_write(dev, DEINTERLACE_CH0_OUT_SIZE,
+ DEINTERLACE_SIZE(width, height));
+ deinterlace_write(dev, DEINTERLACE_CH1_OUT_SIZE,
+ DEINTERLACE_SIZE(width / 2, height / 2));
+
+ deinterlace_write(dev, DEINTERLACE_WB_LINE_STRIDE0, stride);
+ deinterlace_write(dev, DEINTERLACE_WB_LINE_STRIDE1, stride);
+
+ addr = vb2_dma_contig_plane_dma_addr(&dst->vb2_buf, 0);
+ deinterlace_write(dev, DEINTERLACE_WB_ADDR0, addr);
+ deinterlace_write(dev, DEINTERLACE_WB_ADDR1, addr + size);
+ deinterlace_write(dev, DEINTERLACE_WB_ADDR2, 0);
+
+ hstep = (ctx->src_fmt.width << 16) / ctx->dst_fmt.width;
+ vstep = (ctx->src_fmt.height << 16) / ctx->dst_fmt.height;
+ deinterlace_write(dev, DEINTERLACE_CH0_HORZ_FACT, hstep);
+ deinterlace_write(dev, DEINTERLACE_CH0_VERT_FACT, vstep);
+ deinterlace_write(dev, DEINTERLACE_CH1_HORZ_FACT, hstep);
+ deinterlace_write(dev, DEINTERLACE_CH1_VERT_FACT, vstep);
+
+ deinterlace_clr_set_bits(dev, DEINTERLACE_FIELD_CTRL,
+ DEINTERLACE_FIELD_CTRL_FIELD_CNT_MSK,
+ DEINTERLACE_FIELD_CTRL_FIELD_CNT(ctx->field));
+
+ deinterlace_set_bits(dev, DEINTERLACE_FRM_CTRL,
+ DEINTERLACE_FRM_CTRL_START);
+
+ deinterlace_set_bits(dev, DEINTERLACE_FRM_CTRL,
+ DEINTERLACE_FRM_CTRL_REG_READY);
+
+ deinterlace_set_bits(dev, DEINTERLACE_INT_ENABLE,
+ DEINTERLACE_INT_ENABLE_WB_EN);
+
+ deinterlace_set_bits(dev, DEINTERLACE_FRM_CTRL,
+ DEINTERLACE_FRM_CTRL_WB_EN);
+}
+
+static int deinterlace_job_ready(void *priv)
+{
+ struct deinterlace_ctx *ctx = priv;
+
+ return v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx) >= 1 &&
+ v4l2_m2m_num_dst_bufs_ready(ctx->fh.m2m_ctx) >= 2;
+}
+
+static void deinterlace_job_abort(void *priv)
+{
+ struct deinterlace_ctx *ctx = priv;
+
+ /* Will cancel the transaction in the next interrupt handler */
+ ctx->aborting = 1;
+}
+
+static irqreturn_t deinterlace_irq(int irq, void *data)
+{
+ struct deinterlace_dev *dev = data;
+ struct vb2_v4l2_buffer *src, *dst;
+ enum vb2_buffer_state state;
+ struct deinterlace_ctx *ctx;
+ unsigned int val;
+
+ ctx = v4l2_m2m_get_curr_priv(dev->m2m_dev);
+ if (!ctx) {
+ v4l2_err(&dev->v4l2_dev,
+ "Instance released before the end of transaction\n");
+ return IRQ_NONE;
+ }
+
+ val = deinterlace_read(dev, DEINTERLACE_INT_STATUS);
+ if (!(val & DEINTERLACE_INT_STATUS_WRITEBACK))
+ return IRQ_NONE;
+
+ deinterlace_write(dev, DEINTERLACE_INT_ENABLE, 0);
+ deinterlace_set_bits(dev, DEINTERLACE_INT_STATUS,
+ DEINTERLACE_INT_STATUS_WRITEBACK);
+ deinterlace_write(dev, DEINTERLACE_MOD_ENABLE, 0);
+ deinterlace_clr_set_bits(dev, DEINTERLACE_FRM_CTRL,
+ DEINTERLACE_FRM_CTRL_START, 0);
+
+ val = deinterlace_read(dev, DEINTERLACE_STATUS);
+ if (val & DEINTERLACE_STATUS_WB_ERROR)
+ state = VB2_BUF_STATE_ERROR;
+ else
+ state = VB2_BUF_STATE_DONE;
+
+ dst = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
+ v4l2_m2m_buf_done(dst, state);
+
+ if (ctx->field != ctx->first_field || ctx->aborting) {
+ ctx->field = ctx->first_field;
+
+ src = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
+ if (ctx->prev)
+ v4l2_m2m_buf_done(ctx->prev, state);
+ ctx->prev = src;
+
+ v4l2_m2m_job_finish(ctx->dev->m2m_dev, ctx->fh.m2m_ctx);
+ } else {
+ ctx->field = !ctx->first_field;
+ deinterlace_device_run(ctx);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static void deinterlace_init(struct deinterlace_dev *dev)
+{
+ u32 val;
+ int i;
+
+ deinterlace_write(dev, DEINTERLACE_BYPASS,
+ DEINTERLACE_BYPASS_CSC);
+ deinterlace_write(dev, DEINTERLACE_WB_LINE_STRIDE_CTRL,
+ DEINTERLACE_WB_LINE_STRIDE_CTRL_EN);
+ deinterlace_set_bits(dev, DEINTERLACE_FRM_CTRL,
+ DEINTERLACE_FRM_CTRL_OUT_CTRL);
+ deinterlace_write(dev, DEINTERLACE_AGTH_SEL,
+ DEINTERLACE_AGTH_SEL_LINEBUF);
+
+ val = DEINTERLACE_CTRL_EN |
+ DEINTERLACE_CTRL_MODE_MIXED |
+ DEINTERLACE_CTRL_DIAG_INTP_EN |
+ DEINTERLACE_CTRL_TEMP_DIFF_EN;
+ deinterlace_write(dev, DEINTERLACE_CTRL, val);
+
+ deinterlace_clr_set_bits(dev, DEINTERLACE_LUMA_TH,
+ DEINTERLACE_LUMA_TH_MIN_LUMA_MSK,
+ DEINTERLACE_LUMA_TH_MIN_LUMA(4));
+
+ deinterlace_clr_set_bits(dev, DEINTERLACE_SPAT_COMP,
+ DEINTERLACE_SPAT_COMP_TH2_MSK,
+ DEINTERLACE_SPAT_COMP_TH2(5));
+
+ deinterlace_clr_set_bits(dev, DEINTERLACE_TEMP_DIFF,
+ DEINTERLACE_TEMP_DIFF_AMBIGUITY_TH_MSK,
+ DEINTERLACE_TEMP_DIFF_AMBIGUITY_TH(5));
+
+ val = DEINTERLACE_DIAG_INTP_TH0(60) |
+ DEINTERLACE_DIAG_INTP_TH1(0) |
+ DEINTERLACE_DIAG_INTP_TH3(30);
+ deinterlace_write(dev, DEINTERLACE_DIAG_INTP, val);
+
+ deinterlace_clr_set_bits(dev, DEINTERLACE_CHROMA_DIFF,
+ DEINTERLACE_CHROMA_DIFF_TH_MSK,
+ DEINTERLACE_CHROMA_DIFF_TH(5));
+
+ /* neutral filter coefficients */
+ deinterlace_set_bits(dev, DEINTERLACE_FRM_CTRL,
+ DEINTERLACE_FRM_CTRL_COEF_ACCESS);
+ readl_poll_timeout(dev->base + DEINTERLACE_STATUS, val,
+ val & DEINTERLACE_STATUS_COEF_STATUS, 2, 40);
+
+ for (i = 0; i < 32; i++) {
+ deinterlace_write(dev, DEINTERLACE_CH0_HORZ_COEF0 + i * 4,
+ DEINTERLACE_IDENTITY_COEF);
+ deinterlace_write(dev, DEINTERLACE_CH0_VERT_COEF + i * 4,
+ DEINTERLACE_IDENTITY_COEF);
+ deinterlace_write(dev, DEINTERLACE_CH1_HORZ_COEF0 + i * 4,
+ DEINTERLACE_IDENTITY_COEF);
+ deinterlace_write(dev, DEINTERLACE_CH1_VERT_COEF + i * 4,
+ DEINTERLACE_IDENTITY_COEF);
+ }
+
+ deinterlace_clr_set_bits(dev, DEINTERLACE_FRM_CTRL,
+ DEINTERLACE_FRM_CTRL_COEF_ACCESS, 0);
+}
+
+static inline struct deinterlace_ctx *deinterlace_file2ctx(struct file *file)
+{
+ return container_of(file->private_data, struct deinterlace_ctx, fh);
+}
+
+static bool deinterlace_check_format(u32 pixelformat)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(deinterlace_formats); i++)
+ if (deinterlace_formats[i] == pixelformat)
+ return true;
+
+ return false;
+}
+
+static void deinterlace_prepare_format(struct v4l2_pix_format *pix_fmt)
+{
+ unsigned int height = pix_fmt->height;
+ unsigned int width = pix_fmt->width;
+ unsigned int bytesperline;
+ unsigned int sizeimage;
+
+ width = clamp(width, DEINTERLACE_MIN_WIDTH,
+ DEINTERLACE_MAX_WIDTH);
+ height = clamp(height, DEINTERLACE_MIN_HEIGHT,
+ DEINTERLACE_MAX_HEIGHT);
+
+ bytesperline = ALIGN(width, 2);
+ /* luma */
+ sizeimage = bytesperline * height;
+ /* chroma */
+ sizeimage += bytesperline * height / 2;
+
+ pix_fmt->width = width;
+ pix_fmt->height = height;
+ pix_fmt->bytesperline = bytesperline;
+ pix_fmt->sizeimage = sizeimage;
+}
+
+static int deinterlace_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ strscpy(cap->driver, DEINTERLACE_NAME, sizeof(cap->driver));
+ strscpy(cap->card, DEINTERLACE_NAME, sizeof(cap->card));
+ snprintf(cap->bus_info, sizeof(cap->bus_info),
+ "platform:%s", DEINTERLACE_NAME);
+
+ return 0;
+}
+
+static int deinterlace_enum_fmt(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ if (f->index < ARRAY_SIZE(deinterlace_formats)) {
+ f->pixelformat = deinterlace_formats[f->index];
+
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int deinterlace_enum_framesizes(struct file *file, void *priv,
+ struct v4l2_frmsizeenum *fsize)
+{
+ if (fsize->index != 0)
+ return -EINVAL;
+
+ if (!deinterlace_check_format(fsize->pixel_format))
+ return -EINVAL;
+
+ fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
+ fsize->stepwise.min_width = DEINTERLACE_MIN_WIDTH;
+ fsize->stepwise.min_height = DEINTERLACE_MIN_HEIGHT;
+ fsize->stepwise.max_width = DEINTERLACE_MAX_WIDTH;
+ fsize->stepwise.max_height = DEINTERLACE_MAX_HEIGHT;
+ fsize->stepwise.step_width = 2;
+ fsize->stepwise.step_height = 1;
+
+ return 0;
+}
+
+static int deinterlace_g_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct deinterlace_ctx *ctx = deinterlace_file2ctx(file);
+
+ f->fmt.pix = ctx->dst_fmt;
+
+ return 0;
+}
+
+static int deinterlace_g_fmt_vid_out(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct deinterlace_ctx *ctx = deinterlace_file2ctx(file);
+
+ f->fmt.pix = ctx->src_fmt;
+
+ return 0;
+}
+
+static int deinterlace_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ if (!deinterlace_check_format(f->fmt.pix.pixelformat))
+ f->fmt.pix.pixelformat = deinterlace_formats[0];
+
+ if (f->fmt.pix.field != V4L2_FIELD_NONE)
+ f->fmt.pix.field = V4L2_FIELD_NONE;
+
+ deinterlace_prepare_format(&f->fmt.pix);
+
+ return 0;
+}
+
+static int deinterlace_try_fmt_vid_out(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ if (!deinterlace_check_format(f->fmt.pix.pixelformat))
+ f->fmt.pix.pixelformat = deinterlace_formats[0];
+
+ if (f->fmt.pix.field != V4L2_FIELD_INTERLACED_TB &&
+ f->fmt.pix.field != V4L2_FIELD_INTERLACED_BT &&
+ f->fmt.pix.field != V4L2_FIELD_INTERLACED)
+ f->fmt.pix.field = V4L2_FIELD_INTERLACED;
+
+ deinterlace_prepare_format(&f->fmt.pix);
+
+ return 0;
+}
+
+static int deinterlace_s_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct deinterlace_ctx *ctx = deinterlace_file2ctx(file);
+ struct vb2_queue *vq;
+ int ret;
+
+ ret = deinterlace_try_fmt_vid_cap(file, priv, f);
+ if (ret)
+ return ret;
+
+ vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
+ if (vb2_is_busy(vq))
+ return -EBUSY;
+
+ ctx->dst_fmt = f->fmt.pix;
+
+ return 0;
+}
+
+static int deinterlace_s_fmt_vid_out(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct deinterlace_ctx *ctx = deinterlace_file2ctx(file);
+ struct vb2_queue *vq;
+ int ret;
+
+ ret = deinterlace_try_fmt_vid_out(file, priv, f);
+ if (ret)
+ return ret;
+
+ vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
+ if (vb2_is_busy(vq))
+ return -EBUSY;
+
+ ctx->src_fmt = f->fmt.pix;
+
+ /* Propagate colorspace information to capture. */
+ ctx->dst_fmt.colorspace = f->fmt.pix.colorspace;
+ ctx->dst_fmt.xfer_func = f->fmt.pix.xfer_func;
+ ctx->dst_fmt.ycbcr_enc = f->fmt.pix.ycbcr_enc;
+ ctx->dst_fmt.quantization = f->fmt.pix.quantization;
+
+ return 0;
+}
+
+static const struct v4l2_ioctl_ops deinterlace_ioctl_ops = {
+ .vidioc_querycap = deinterlace_querycap,
+
+ .vidioc_enum_framesizes = deinterlace_enum_framesizes,
+
+ .vidioc_enum_fmt_vid_cap = deinterlace_enum_fmt,
+ .vidioc_g_fmt_vid_cap = deinterlace_g_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = deinterlace_try_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = deinterlace_s_fmt_vid_cap,
+
+ .vidioc_enum_fmt_vid_out = deinterlace_enum_fmt,
+ .vidioc_g_fmt_vid_out = deinterlace_g_fmt_vid_out,
+ .vidioc_try_fmt_vid_out = deinterlace_try_fmt_vid_out,
+ .vidioc_s_fmt_vid_out = deinterlace_s_fmt_vid_out,
+
+ .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs,
+ .vidioc_querybuf = v4l2_m2m_ioctl_querybuf,
+ .vidioc_qbuf = v4l2_m2m_ioctl_qbuf,
+ .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf,
+ .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf,
+ .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs,
+ .vidioc_expbuf = v4l2_m2m_ioctl_expbuf,
+
+ .vidioc_streamon = v4l2_m2m_ioctl_streamon,
+ .vidioc_streamoff = v4l2_m2m_ioctl_streamoff,
+};
+
+static int deinterlace_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers,
+ unsigned int *nplanes, unsigned int sizes[],
+ struct device *alloc_devs[])
+{
+ struct deinterlace_ctx *ctx = vb2_get_drv_priv(vq);
+ struct v4l2_pix_format *pix_fmt;
+
+ if (V4L2_TYPE_IS_OUTPUT(vq->type))
+ pix_fmt = &ctx->src_fmt;
+ else
+ pix_fmt = &ctx->dst_fmt;
+
+ if (*nplanes) {
+ if (sizes[0] < pix_fmt->sizeimage)
+ return -EINVAL;
+ } else {
+ sizes[0] = pix_fmt->sizeimage;
+ *nplanes = 1;
+ }
+
+ return 0;
+}
+
+static int deinterlace_buf_prepare(struct vb2_buffer *vb)
+{
+ struct vb2_queue *vq = vb->vb2_queue;
+ struct deinterlace_ctx *ctx = vb2_get_drv_priv(vq);
+ struct v4l2_pix_format *pix_fmt;
+
+ if (V4L2_TYPE_IS_OUTPUT(vq->type))
+ pix_fmt = &ctx->src_fmt;
+ else
+ pix_fmt = &ctx->dst_fmt;
+
+ if (vb2_plane_size(vb, 0) < pix_fmt->sizeimage)
+ return -EINVAL;
+
+ vb2_set_plane_payload(vb, 0, pix_fmt->sizeimage);
+
+ return 0;
+}
+
+static void deinterlace_buf_queue(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct deinterlace_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+
+ v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf);
+}
+
+static void deinterlace_queue_cleanup(struct vb2_queue *vq, u32 state)
+{
+ struct deinterlace_ctx *ctx = vb2_get_drv_priv(vq);
+ struct vb2_v4l2_buffer *vbuf;
+
+ do {
+ if (V4L2_TYPE_IS_OUTPUT(vq->type))
+ vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
+ else
+ vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
+
+ if (vbuf)
+ v4l2_m2m_buf_done(vbuf, state);
+ } while (vbuf);
+
+ if (V4L2_TYPE_IS_OUTPUT(vq->type) && ctx->prev)
+ v4l2_m2m_buf_done(ctx->prev, state);
+}
+
+static int deinterlace_start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+ struct deinterlace_ctx *ctx = vb2_get_drv_priv(vq);
+ struct device *dev = ctx->dev->dev;
+ int ret;
+
+ if (V4L2_TYPE_IS_OUTPUT(vq->type)) {
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret < 0) {
+ dev_err(dev, "Failed to enable module\n");
+
+ goto err_runtime_get;
+ }
+
+ ctx->first_field =
+ ctx->src_fmt.field == V4L2_FIELD_INTERLACED_BT;
+ ctx->field = ctx->first_field;
+
+ ctx->prev = NULL;
+ ctx->aborting = 0;
+
+ ctx->flag1_buf = dma_alloc_coherent(dev, FLAG_SIZE,
+ &ctx->flag1_buf_dma,
+ GFP_KERNEL);
+ if (!ctx->flag1_buf) {
+ ret = -ENOMEM;
+
+ goto err_no_mem1;
+ }
+
+ ctx->flag2_buf = dma_alloc_coherent(dev, FLAG_SIZE,
+ &ctx->flag2_buf_dma,
+ GFP_KERNEL);
+ if (!ctx->flag2_buf) {
+ ret = -ENOMEM;
+
+ goto err_no_mem2;
+ }
+ }
+
+ return 0;
+
+err_no_mem2:
+ dma_free_coherent(dev, FLAG_SIZE, ctx->flag1_buf,
+ ctx->flag1_buf_dma);
+err_no_mem1:
+ pm_runtime_put(dev);
+err_runtime_get:
+ deinterlace_queue_cleanup(vq, VB2_BUF_STATE_QUEUED);
+
+ return ret;
+}
+
+static void deinterlace_stop_streaming(struct vb2_queue *vq)
+{
+ struct deinterlace_ctx *ctx = vb2_get_drv_priv(vq);
+
+ if (V4L2_TYPE_IS_OUTPUT(vq->type)) {
+ struct device *dev = ctx->dev->dev;
+
+ dma_free_coherent(dev, FLAG_SIZE, ctx->flag1_buf,
+ ctx->flag1_buf_dma);
+ dma_free_coherent(dev, FLAG_SIZE, ctx->flag2_buf,
+ ctx->flag2_buf_dma);
+
+ pm_runtime_put(dev);
+ }
+
+ deinterlace_queue_cleanup(vq, VB2_BUF_STATE_ERROR);
+}
+
+static const struct vb2_ops deinterlace_qops = {
+ .queue_setup = deinterlace_queue_setup,
+ .buf_prepare = deinterlace_buf_prepare,
+ .buf_queue = deinterlace_buf_queue,
+ .start_streaming = deinterlace_start_streaming,
+ .stop_streaming = deinterlace_stop_streaming,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
+};
+
+static int deinterlace_queue_init(void *priv, struct vb2_queue *src_vq,
+ struct vb2_queue *dst_vq)
+{
+ struct deinterlace_ctx *ctx = priv;
+ int ret;
+
+ src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ src_vq->io_modes = VB2_MMAP | VB2_DMABUF;
+ src_vq->drv_priv = ctx;
+ src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
+ src_vq->min_buffers_needed = 1;
+ src_vq->ops = &deinterlace_qops;
+ src_vq->mem_ops = &vb2_dma_contig_memops;
+ src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ src_vq->lock = &ctx->dev->dev_mutex;
+ src_vq->dev = ctx->dev->dev;
+
+ ret = vb2_queue_init(src_vq);
+ if (ret)
+ return ret;
+
+ dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ dst_vq->io_modes = VB2_MMAP | VB2_DMABUF;
+ dst_vq->drv_priv = ctx;
+ dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
+ dst_vq->min_buffers_needed = 2;
+ dst_vq->ops = &deinterlace_qops;
+ dst_vq->mem_ops = &vb2_dma_contig_memops;
+ dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ dst_vq->lock = &ctx->dev->dev_mutex;
+ dst_vq->dev = ctx->dev->dev;
+
+ ret = vb2_queue_init(dst_vq);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int deinterlace_open(struct file *file)
+{
+ struct deinterlace_dev *dev = video_drvdata(file);
+ struct deinterlace_ctx *ctx = NULL;
+ int ret;
+
+ if (mutex_lock_interruptible(&dev->dev_mutex))
+ return -ERESTARTSYS;
+
+ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+ if (!ctx) {
+ mutex_unlock(&dev->dev_mutex);
+ return -ENOMEM;
+ }
+
+ /* default output format */
+ ctx->src_fmt.pixelformat = deinterlace_formats[0];
+ ctx->src_fmt.field = V4L2_FIELD_INTERLACED;
+ ctx->src_fmt.width = 640;
+ ctx->src_fmt.height = 480;
+ deinterlace_prepare_format(&ctx->src_fmt);
+
+ /* default capture format */
+ ctx->dst_fmt.pixelformat = deinterlace_formats[0];
+ ctx->dst_fmt.field = V4L2_FIELD_NONE;
+ ctx->dst_fmt.width = 640;
+ ctx->dst_fmt.height = 480;
+ deinterlace_prepare_format(&ctx->dst_fmt);
+
+ v4l2_fh_init(&ctx->fh, video_devdata(file));
+ file->private_data = &ctx->fh;
+ ctx->dev = dev;
+
+ ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx,
+ &deinterlace_queue_init);
+ if (IS_ERR(ctx->fh.m2m_ctx)) {
+ ret = PTR_ERR(ctx->fh.m2m_ctx);
+ goto err_free;
+ }
+
+ v4l2_fh_add(&ctx->fh);
+
+ mutex_unlock(&dev->dev_mutex);
+
+ return 0;
+
+err_free:
+ kfree(ctx);
+ mutex_unlock(&dev->dev_mutex);
+
+ return ret;
+}
+
+static int deinterlace_release(struct file *file)
+{
+ struct deinterlace_dev *dev = video_drvdata(file);
+ struct deinterlace_ctx *ctx = container_of(file->private_data,
+ struct deinterlace_ctx, fh);
+
+ mutex_lock(&dev->dev_mutex);
+
+ v4l2_fh_del(&ctx->fh);
+ v4l2_fh_exit(&ctx->fh);
+ v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
+
+ kfree(ctx);
+
+ mutex_unlock(&dev->dev_mutex);
+
+ return 0;
+}
+
+static const struct v4l2_file_operations deinterlace_fops = {
+ .owner = THIS_MODULE,
+ .open = deinterlace_open,
+ .release = deinterlace_release,
+ .poll = v4l2_m2m_fop_poll,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = v4l2_m2m_fop_mmap,
+};
+
+static const struct video_device deinterlace_video_device = {
+ .name = DEINTERLACE_NAME,
+ .vfl_dir = VFL_DIR_M2M,
+ .fops = &deinterlace_fops,
+ .ioctl_ops = &deinterlace_ioctl_ops,
+ .minor = -1,
+ .release = video_device_release_empty,
+ .device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING,
+};
+
+static const struct v4l2_m2m_ops deinterlace_m2m_ops = {
+ .device_run = deinterlace_device_run,
+ .job_ready = deinterlace_job_ready,
+ .job_abort = deinterlace_job_abort,
+};
+
+static int deinterlace_probe(struct platform_device *pdev)
+{
+ struct deinterlace_dev *dev;
+ struct video_device *vfd;
+ struct resource *res;
+ int irq, ret;
+
+ dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+
+ dev->vfd = deinterlace_video_device;
+ dev->dev = &pdev->dev;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq <= 0)
+ return irq;
+
+ ret = devm_request_irq(dev->dev, irq, deinterlace_irq,
+ 0, dev_name(dev->dev), dev);
+ if (ret) {
+ dev_err(dev->dev, "Failed to request IRQ\n");
+
+ return ret;
+ }
+
+ ret = of_dma_configure(dev->dev, dev->dev->of_node, true);
+ if (ret)
+ return ret;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ dev->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(dev->base))
+ return PTR_ERR(dev->base);
+
+ dev->bus_clk = devm_clk_get(dev->dev, "bus");
+ if (IS_ERR(dev->bus_clk)) {
+ dev_err(dev->dev, "Failed to get bus clock\n");
+
+ return PTR_ERR(dev->bus_clk);
+ }
+
+ dev->mod_clk = devm_clk_get(dev->dev, "mod");
+ if (IS_ERR(dev->mod_clk)) {
+ dev_err(dev->dev, "Failed to get mod clock\n");
+
+ return PTR_ERR(dev->mod_clk);
+ }
+
+ dev->ram_clk = devm_clk_get(dev->dev, "ram");
+ if (IS_ERR(dev->ram_clk)) {
+ dev_err(dev->dev, "Failed to get ram clock\n");
+
+ return PTR_ERR(dev->ram_clk);
+ }
+
+ dev->rstc = devm_reset_control_get(dev->dev, NULL);
+ if (IS_ERR(dev->rstc)) {
+ dev_err(dev->dev, "Failed to get reset control\n");
+
+ return PTR_ERR(dev->rstc);
+ }
+
+ mutex_init(&dev->dev_mutex);
+
+ ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
+ if (ret) {
+ dev_err(dev->dev, "Failed to register V4L2 device\n");
+
+ return ret;
+ }
+
+ vfd = &dev->vfd;
+ vfd->lock = &dev->dev_mutex;
+ vfd->v4l2_dev = &dev->v4l2_dev;
+
+ snprintf(vfd->name, sizeof(vfd->name), "%s",
+ deinterlace_video_device.name);
+ video_set_drvdata(vfd, dev);
+
+ ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0);
+ if (ret) {
+ v4l2_err(&dev->v4l2_dev, "Failed to register video device\n");
+
+ goto err_v4l2;
+ }
+
+ v4l2_info(&dev->v4l2_dev,
+ "Device registered as /dev/video%d\n", vfd->num);
+
+ dev->m2m_dev = v4l2_m2m_init(&deinterlace_m2m_ops);
+ if (IS_ERR(dev->m2m_dev)) {
+ v4l2_err(&dev->v4l2_dev,
+ "Failed to initialize V4L2 M2M device\n");
+ ret = PTR_ERR(dev->m2m_dev);
+
+ goto err_video;
+ }
+
+ platform_set_drvdata(pdev, dev);
+
+ pm_runtime_enable(dev->dev);
+
+ return 0;
+
+err_video:
+ video_unregister_device(&dev->vfd);
+err_v4l2:
+ v4l2_device_unregister(&dev->v4l2_dev);
+
+ return ret;
+}
+
+static int deinterlace_remove(struct platform_device *pdev)
+{
+ struct deinterlace_dev *dev = platform_get_drvdata(pdev);
+
+ v4l2_m2m_release(dev->m2m_dev);
+ video_unregister_device(&dev->vfd);
+ v4l2_device_unregister(&dev->v4l2_dev);
+
+ pm_runtime_force_suspend(&pdev->dev);
+
+ return 0;
+}
+
+static int deinterlace_runtime_resume(struct device *device)
+{
+ struct deinterlace_dev *dev = dev_get_drvdata(device);
+ int ret;
+
+ ret = clk_set_rate_exclusive(dev->mod_clk, 300000000);
+ if (ret) {
+ dev_err(dev->dev, "Failed to set exclusive mod clock rate\n");
+
+ return ret;
+ }
+
+ ret = clk_prepare_enable(dev->bus_clk);
+ if (ret) {
+ dev_err(dev->dev, "Failed to enable bus clock\n");
+
+ goto err_exclusive_rate;
+ }
+
+ ret = clk_prepare_enable(dev->mod_clk);
+ if (ret) {
+ dev_err(dev->dev, "Failed to enable mod clock\n");
+
+ goto err_bus_clk;
+ }
+
+ ret = clk_prepare_enable(dev->ram_clk);
+ if (ret) {
+ dev_err(dev->dev, "Failed to enable ram clock\n");
+
+ goto err_mod_clk;
+ }
+
+ ret = reset_control_deassert(dev->rstc);
+ if (ret) {
+ dev_err(dev->dev, "Failed to apply reset\n");
+
+ goto err_ram_clk;
+ }
+
+ deinterlace_init(dev);
+
+ return 0;
+
+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;
+}
+
+static int deinterlace_runtime_suspend(struct device *device)
+{
+ struct deinterlace_dev *dev = dev_get_drvdata(device);
+
+ reset_control_assert(dev->rstc);
+
+ clk_disable_unprepare(dev->ram_clk);
+ clk_disable_unprepare(dev->mod_clk);
+ clk_disable_unprepare(dev->bus_clk);
+ clk_rate_exclusive_put(dev->mod_clk);
+
+ return 0;
+}
+
+static const struct of_device_id deinterlace_dt_match[] = {
+ { .compatible = "allwinner,sun8i-h3-deinterlace" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, deinterlace_dt_match);
+
+static const struct dev_pm_ops deinterlace_pm_ops = {
+ .runtime_resume = deinterlace_runtime_resume,
+ .runtime_suspend = deinterlace_runtime_suspend,
+};
+
+static struct platform_driver deinterlace_driver = {
+ .probe = deinterlace_probe,
+ .remove = deinterlace_remove,
+ .driver = {
+ .name = DEINTERLACE_NAME,
+ .of_match_table = deinterlace_dt_match,
+ .pm = &deinterlace_pm_ops,
+ },
+};
+module_platform_driver(deinterlace_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Jernej Skrabec <jernej.skrabec@siol.net>");
+MODULE_DESCRIPTION("Allwinner Deinterlace driver");
diff --git a/drivers/media/platform/sunxi/sun8i-di/sun8i-di.h b/drivers/media/platform/sunxi/sun8i-di/sun8i-di.h
new file mode 100644
index 0000000..0254251
--- /dev/null
+++ b/drivers/media/platform/sunxi/sun8i-di/sun8i-di.h
@@ -0,0 +1,237 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Allwinner Deinterlace driver
+ *
+ * Copyright (C) 2019 Jernej Skrabec <jernej.skrabec@siol.net>
+ */
+
+#ifndef _SUN8I_DEINTERLACE_H_
+#define _SUN8I_DEINTERLACE_H_
+
+#include <media/v4l2-device.h>
+#include <media/v4l2-mem2mem.h>
+#include <media/videobuf2-v4l2.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include <linux/platform_device.h>
+
+#define DEINTERLACE_NAME "sun8i-di"
+
+#define DEINTERLACE_MOD_ENABLE 0x00
+#define DEINTERLACE_MOD_ENABLE_EN BIT(0)
+
+#define DEINTERLACE_FRM_CTRL 0x04
+#define DEINTERLACE_FRM_CTRL_REG_READY BIT(0)
+#define DEINTERLACE_FRM_CTRL_WB_EN BIT(2)
+#define DEINTERLACE_FRM_CTRL_OUT_CTRL BIT(11)
+#define DEINTERLACE_FRM_CTRL_START BIT(16)
+#define DEINTERLACE_FRM_CTRL_COEF_ACCESS BIT(23)
+
+#define DEINTERLACE_BYPASS 0x08
+#define DEINTERLACE_BYPASS_CSC BIT(1)
+
+#define DEINTERLACE_AGTH_SEL 0x0c
+#define DEINTERLACE_AGTH_SEL_LINEBUF BIT(8)
+
+#define DEINTERLACE_LINT_CTRL 0x10
+#define DEINTERLACE_TRD_PRELUMA 0x1c
+#define DEINTERLACE_BUF_ADDR0 0x20
+#define DEINTERLACE_BUF_ADDR1 0x24
+#define DEINTERLACE_BUF_ADDR2 0x28
+
+#define DEINTERLACE_FIELD_CTRL 0x2c
+#define DEINTERLACE_FIELD_CTRL_FIELD_CNT(v) ((v) & 0xff)
+#define DEINTERLACE_FIELD_CTRL_FIELD_CNT_MSK (0xff)
+
+#define DEINTERLACE_TB_OFFSET0 0x30
+#define DEINTERLACE_TB_OFFSET1 0x34
+#define DEINTERLACE_TB_OFFSET2 0x38
+#define DEINTERLACE_TRD_PRECHROMA 0x3c
+#define DEINTERLACE_LINE_STRIDE0 0x40
+#define DEINTERLACE_LINE_STRIDE1 0x44
+#define DEINTERLACE_LINE_STRIDE2 0x48
+
+#define DEINTERLACE_IN_FMT 0x4c
+#define DEINTERLACE_IN_FMT_PS(v) ((v) & 3)
+#define DEINTERLACE_IN_FMT_FMT(v) (((v) & 7) << 4)
+#define DEINTERLACE_IN_FMT_MOD(v) (((v) & 7) << 8)
+
+#define DEINTERLACE_WB_ADDR0 0x50
+#define DEINTERLACE_WB_ADDR1 0x54
+#define DEINTERLACE_WB_ADDR2 0x58
+
+#define DEINTERLACE_OUT_FMT 0x5c
+#define DEINTERLACE_OUT_FMT_FMT(v) ((v) & 0xf)
+#define DEINTERLACE_OUT_FMT_PS(v) (((v) & 3) << 5)
+
+#define DEINTERLACE_INT_ENABLE 0x60
+#define DEINTERLACE_INT_ENABLE_WB_EN BIT(7)
+
+#define DEINTERLACE_INT_STATUS 0x64
+#define DEINTERLACE_INT_STATUS_WRITEBACK BIT(7)
+
+#define DEINTERLACE_STATUS 0x68
+#define DEINTERLACE_STATUS_COEF_STATUS BIT(11)
+#define DEINTERLACE_STATUS_WB_ERROR BIT(12)
+
+#define DEINTERLACE_CSC_COEF 0x70 /* 12 registers */
+
+#define DEINTERLACE_CTRL 0xa0
+#define DEINTERLACE_CTRL_EN BIT(0)
+#define DEINTERLACE_CTRL_FLAG_OUT_EN BIT(8)
+#define DEINTERLACE_CTRL_MODE_PASSTROUGH (0 << 16)
+#define DEINTERLACE_CTRL_MODE_WEAVE (1 << 16)
+#define DEINTERLACE_CTRL_MODE_BOB (2 << 16)
+#define DEINTERLACE_CTRL_MODE_MIXED (3 << 16)
+#define DEINTERLACE_CTRL_DIAG_INTP_EN BIT(24)
+#define DEINTERLACE_CTRL_TEMP_DIFF_EN BIT(25)
+
+#define DEINTERLACE_DIAG_INTP 0xa4
+#define DEINTERLACE_DIAG_INTP_TH0(v) ((v) & 0x7f)
+#define DEINTERLACE_DIAG_INTP_TH0_MSK (0x7f)
+#define DEINTERLACE_DIAG_INTP_TH1(v) (((v) & 0x7f) << 8)
+#define DEINTERLACE_DIAG_INTP_TH1_MSK (0x7f << 8)
+#define DEINTERLACE_DIAG_INTP_TH3(v) (((v) & 0xff) << 24)
+#define DEINTERLACE_DIAG_INTP_TH3_MSK (0xff << 24)
+
+#define DEINTERLACE_TEMP_DIFF 0xa8
+#define DEINTERLACE_TEMP_DIFF_SAD_CENTRAL_TH(v) ((v) & 0x7f)
+#define DEINTERLACE_TEMP_DIFF_SAD_CENTRAL_TH_MSK (0x7f)
+#define DEINTERLACE_TEMP_DIFF_AMBIGUITY_TH(v) (((v) & 0x7f) << 8)
+#define DEINTERLACE_TEMP_DIFF_AMBIGUITY_TH_MSK (0x7f << 8)
+#define DEINTERLACE_TEMP_DIFF_DIRECT_DITHER_TH(v) (((v) & 0x7ff) << 16)
+#define DEINTERLACE_TEMP_DIFF_DIRECT_DITHER_TH_MSK (0x7ff << 16)
+
+#define DEINTERLACE_LUMA_TH 0xac
+#define DEINTERLACE_LUMA_TH_MIN_LUMA(v) ((v) & 0xff)
+#define DEINTERLACE_LUMA_TH_MIN_LUMA_MSK (0xff)
+#define DEINTERLACE_LUMA_TH_MAX_LUMA(v) (((v) & 0xff) << 8)
+#define DEINTERLACE_LUMA_TH_MAX_LUMA_MSK (0xff << 8)
+#define DEINTERLACE_LUMA_TH_AVG_LUMA_SHIFT(v) (((v) & 0xff) << 16)
+#define DEINTERLACE_LUMA_TH_AVG_LUMA_SHIFT_MSK (0xff << 16)
+#define DEINTERLACE_LUMA_TH_PIXEL_STATIC(v) (((v) & 3) << 24)
+#define DEINTERLACE_LUMA_TH_PIXEL_STATIC_MSK (3 << 24)
+
+#define DEINTERLACE_SPAT_COMP 0xb0
+#define DEINTERLACE_SPAT_COMP_TH2(v) ((v) & 0xff)
+#define DEINTERLACE_SPAT_COMP_TH2_MSK (0xff)
+#define DEINTERLACE_SPAT_COMP_TH3(v) (((v) & 0xff) << 16)
+#define DEINTERLACE_SPAT_COMP_TH3_MSK (0xff << 16)
+
+#define DEINTERLACE_CHROMA_DIFF 0xb4
+#define DEINTERLACE_CHROMA_DIFF_TH(v) ((v) & 0xff)
+#define DEINTERLACE_CHROMA_DIFF_TH_MSK (0xff)
+#define DEINTERLACE_CHROMA_DIFF_LUMA(v) (((v) & 0x3f) << 16)
+#define DEINTERLACE_CHROMA_DIFF_LUMA_MSK (0x3f << 16)
+#define DEINTERLACE_CHROMA_DIFF_CHROMA(v) (((v) & 0x3f) << 24)
+#define DEINTERLACE_CHROMA_DIFF_CHROMA_MSK (0x3f << 24)
+
+#define DEINTERLACE_PRELUMA 0xb8
+#define DEINTERLACE_PRECHROMA 0xbc
+#define DEINTERLACE_TILE_FLAG0 0xc0
+#define DEINTERLACE_TILE_FLAG1 0xc4
+#define DEINTERLACE_FLAG_LINE_STRIDE 0xc8
+#define DEINTERLACE_FLAG_SEQ 0xcc
+
+#define DEINTERLACE_WB_LINE_STRIDE_CTRL 0xd0
+#define DEINTERLACE_WB_LINE_STRIDE_CTRL_EN BIT(0)
+
+#define DEINTERLACE_WB_LINE_STRIDE0 0xd4
+#define DEINTERLACE_WB_LINE_STRIDE1 0xd8
+#define DEINTERLACE_WB_LINE_STRIDE2 0xdc
+#define DEINTERLACE_TRD_CTRL 0xe0
+#define DEINTERLACE_TRD_BUF_ADDR0 0xe4
+#define DEINTERLACE_TRD_BUF_ADDR1 0xe8
+#define DEINTERLACE_TRD_BUF_ADDR2 0xec
+#define DEINTERLACE_TRD_TB_OFF0 0xf0
+#define DEINTERLACE_TRD_TB_OFF1 0xf4
+#define DEINTERLACE_TRD_TB_OFF2 0xf8
+#define DEINTERLACE_TRD_WB_STRIDE 0xfc
+#define DEINTERLACE_CH0_IN_SIZE 0x100
+#define DEINTERLACE_CH0_OUT_SIZE 0x104
+#define DEINTERLACE_CH0_HORZ_FACT 0x108
+#define DEINTERLACE_CH0_VERT_FACT 0x10c
+#define DEINTERLACE_CH0_HORZ_PHASE 0x110
+#define DEINTERLACE_CH0_VERT_PHASE0 0x114
+#define DEINTERLACE_CH0_VERT_PHASE1 0x118
+#define DEINTERLACE_CH0_HORZ_TAP0 0x120
+#define DEINTERLACE_CH0_HORZ_TAP1 0x124
+#define DEINTERLACE_CH0_VERT_TAP 0x128
+#define DEINTERLACE_CH1_IN_SIZE 0x200
+#define DEINTERLACE_CH1_OUT_SIZE 0x204
+#define DEINTERLACE_CH1_HORZ_FACT 0x208
+#define DEINTERLACE_CH1_VERT_FACT 0x20c
+#define DEINTERLACE_CH1_HORZ_PHASE 0x210
+#define DEINTERLACE_CH1_VERT_PHASE0 0x214
+#define DEINTERLACE_CH1_VERT_PHASE1 0x218
+#define DEINTERLACE_CH1_HORZ_TAP0 0x220
+#define DEINTERLACE_CH1_HORZ_TAP1 0x224
+#define DEINTERLACE_CH1_VERT_TAP 0x228
+#define DEINTERLACE_CH0_HORZ_COEF0 0x400 /* 32 registers */
+#define DEINTERLACE_CH0_HORZ_COEF1 0x480 /* 32 registers */
+#define DEINTERLACE_CH0_VERT_COEF 0x500 /* 32 registers */
+#define DEINTERLACE_CH1_HORZ_COEF0 0x600 /* 32 registers */
+#define DEINTERLACE_CH1_HORZ_COEF1 0x680 /* 32 registers */
+#define DEINTERLACE_CH1_VERT_COEF 0x700 /* 32 registers */
+#define DEINTERLACE_CH3_HORZ_COEF0 0x800 /* 32 registers */
+#define DEINTERLACE_CH3_HORZ_COEF1 0x880 /* 32 registers */
+#define DEINTERLACE_CH3_VERT_COEF 0x900 /* 32 registers */
+
+#define DEINTERLACE_MIN_WIDTH 2U
+#define DEINTERLACE_MIN_HEIGHT 2U
+#define DEINTERLACE_MAX_WIDTH 2048U
+#define DEINTERLACE_MAX_HEIGHT 1100U
+
+#define DEINTERLACE_MODE_UV_COMBINED 2
+
+#define DEINTERLACE_IN_FMT_YUV420 2
+
+#define DEINTERLACE_OUT_FMT_YUV420SP 13
+
+#define DEINTERLACE_PS_UVUV 0
+#define DEINTERLACE_PS_VUVU 1
+
+#define DEINTERLACE_IDENTITY_COEF 0x4000
+
+#define DEINTERLACE_SIZE(w, h) (((h) - 1) << 16 | ((w) - 1))
+
+struct deinterlace_ctx {
+ struct v4l2_fh fh;
+ struct deinterlace_dev *dev;
+
+ struct v4l2_pix_format src_fmt;
+ struct v4l2_pix_format dst_fmt;
+
+ void *flag1_buf;
+ dma_addr_t flag1_buf_dma;
+
+ void *flag2_buf;
+ dma_addr_t flag2_buf_dma;
+
+ struct vb2_v4l2_buffer *prev;
+
+ unsigned int first_field;
+ unsigned int field;
+
+ int aborting;
+};
+
+struct deinterlace_dev {
+ struct v4l2_device v4l2_dev;
+ struct video_device vfd;
+ struct device *dev;
+ struct v4l2_m2m_dev *m2m_dev;
+
+ /* Device file mutex */
+ struct mutex dev_mutex;
+
+ void __iomem *base;
+
+ struct clk *bus_clk;
+ struct clk *mod_clk;
+ struct clk *ram_clk;
+
+ struct reset_control *rstc;
+};
+
+#endif
diff --git a/drivers/media/platform/sunxi/sun8i-rotate/Makefile b/drivers/media/platform/sunxi/sun8i-rotate/Makefile
new file mode 100644
index 0000000..40f9cf3
--- /dev/null
+++ b/drivers/media/platform/sunxi/sun8i-rotate/Makefile
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0
+sun8i-rotate-y += sun8i_rotate.o
+sun8i-rotate-y += sun8i_formats.o
+
+obj-$(CONFIG_VIDEO_SUN8I_ROTATE) += sun8i-rotate.o
diff --git a/drivers/media/platform/sunxi/sun8i-rotate/sun8i-formats.h b/drivers/media/platform/sunxi/sun8i-rotate/sun8i-formats.h
new file mode 100644
index 0000000..697cd5f
--- /dev/null
+++ b/drivers/media/platform/sunxi/sun8i-rotate/sun8i-formats.h
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (C) 2020 Jernej Skrabec <jernej.skrabec@siol.net> */
+
+#ifndef _SUN8I_FORMATS_H_
+#define _SUN8I_FORMATS_H_
+
+#include <linux/videodev2.h>
+
+#define ROTATE_FLAG_YUV BIT(0)
+#define ROTATE_FLAG_OUTPUT BIT(1)
+
+struct rotate_format {
+ u32 fourcc;
+ u32 hw_format;
+ int planes;
+ int bpp[3];
+ int hsub;
+ int vsub;
+ unsigned int flags;
+};
+
+const struct rotate_format *rotate_find_format(u32 pixelformat);
+int rotate_enum_fmt(struct v4l2_fmtdesc *f, bool dst);
+
+#endif
diff --git a/drivers/media/platform/sunxi/sun8i-rotate/sun8i-rotate.h b/drivers/media/platform/sunxi/sun8i-rotate/sun8i-rotate.h
new file mode 100644
index 0000000..32ade97
--- /dev/null
+++ b/drivers/media/platform/sunxi/sun8i-rotate/sun8i-rotate.h
@@ -0,0 +1,135 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Allwinner DE2 rotation driver
+ *
+ * Copyright (C) 2020 Jernej Skrabec <jernej.skrabec@siol.net>
+ */
+
+#ifndef _SUN8I_ROTATE_H_
+#define _SUN8I_ROTATE_H_
+
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-mem2mem.h>
+#include <media/videobuf2-v4l2.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include <linux/platform_device.h>
+
+#define ROTATE_NAME "sun8i-rotate"
+
+#define ROTATE_GLB_CTL 0x00
+#define ROTATE_GLB_CTL_START BIT(31)
+#define ROTATE_GLB_CTL_RESET BIT(30)
+#define ROTATE_GLB_CTL_BURST_LEN(x) ((x) << 16)
+#define ROTATE_GLB_CTL_HFLIP BIT(7)
+#define ROTATE_GLB_CTL_VFLIP BIT(6)
+#define ROTATE_GLB_CTL_ROTATION(x) ((x) << 4)
+#define ROTATE_GLB_CTL_MODE(x) ((x) << 0)
+
+#define ROTATE_INT 0x04
+#define ROTATE_INT_FINISH_IRQ_EN BIT(16)
+#define ROTATE_INT_FINISH_IRQ BIT(0)
+
+#define ROTATE_IN_FMT 0x20
+#define ROTATE_IN_FMT_FORMAT(x) ((x) << 0)
+
+#define ROTATE_IN_SIZE 0x24
+#define ROTATE_IN_PITCH0 0x30
+#define ROTATE_IN_PITCH1 0x34
+#define ROTATE_IN_PITCH2 0x38
+#define ROTATE_IN_ADDRL0 0x40
+#define ROTATE_IN_ADDRH0 0x44
+#define ROTATE_IN_ADDRL1 0x48
+#define ROTATE_IN_ADDRH1 0x4c
+#define ROTATE_IN_ADDRL2 0x50
+#define ROTATE_IN_ADDRH2 0x54
+#define ROTATE_OUT_SIZE 0x84
+#define ROTATE_OUT_PITCH0 0x90
+#define ROTATE_OUT_PITCH1 0x94
+#define ROTATE_OUT_PITCH2 0x98
+#define ROTATE_OUT_ADDRL0 0xA0
+#define ROTATE_OUT_ADDRH0 0xA4
+#define ROTATE_OUT_ADDRL1 0xA8
+#define ROTATE_OUT_ADDRH1 0xAC
+#define ROTATE_OUT_ADDRL2 0xB0
+#define ROTATE_OUT_ADDRH2 0xB4
+
+#define ROTATE_BURST_8 0x07
+#define ROTATE_BURST_16 0x0f
+#define ROTATE_BURST_32 0x1f
+#define ROTATE_BURST_64 0x3f
+
+#define ROTATE_MODE_COPY_ROTATE 0x01
+
+#define ROTATE_FORMAT_ARGB32 0x00
+#define ROTATE_FORMAT_ABGR32 0x01
+#define ROTATE_FORMAT_RGBA32 0x02
+#define ROTATE_FORMAT_BGRA32 0x03
+#define ROTATE_FORMAT_XRGB32 0x04
+#define ROTATE_FORMAT_XBGR32 0x05
+#define ROTATE_FORMAT_RGBX32 0x06
+#define ROTATE_FORMAT_BGRX32 0x07
+#define ROTATE_FORMAT_RGB24 0x08
+#define ROTATE_FORMAT_BGR24 0x09
+#define ROTATE_FORMAT_RGB565 0x0a
+#define ROTATE_FORMAT_BGR565 0x0b
+#define ROTATE_FORMAT_ARGB4444 0x0c
+#define ROTATE_FORMAT_ABGR4444 0x0d
+#define ROTATE_FORMAT_RGBA4444 0x0e
+#define ROTATE_FORMAT_BGRA4444 0x0f
+#define ROTATE_FORMAT_ARGB1555 0x10
+#define ROTATE_FORMAT_ABGR1555 0x11
+#define ROTATE_FORMAT_RGBA5551 0x12
+#define ROTATE_FORMAT_BGRA5551 0x13
+
+#define ROTATE_FORMAT_YUYV 0x20
+#define ROTATE_FORMAT_UYVY 0x21
+#define ROTATE_FORMAT_YVYU 0x22
+#define ROTATE_FORMAT_VYUV 0x23
+#define ROTATE_FORMAT_NV61 0x24
+#define ROTATE_FORMAT_NV16 0x25
+#define ROTATE_FORMAT_YUV422P 0x26
+#define ROTATE_FORMAT_NV21 0x28
+#define ROTATE_FORMAT_NV12 0x29
+#define ROTATE_FORMAT_YUV420P 0x2A
+
+#define ROTATE_SIZE(w, h) (((h) - 1) << 16 | ((w) - 1))
+
+#define ROTATE_MIN_WIDTH 8U
+#define ROTATE_MIN_HEIGHT 8U
+#define ROTATE_MAX_WIDTH 4096U
+#define ROTATE_MAX_HEIGHT 4096U
+
+struct rotate_ctx {
+ struct v4l2_fh fh;
+ struct rotate_dev *dev;
+
+ struct v4l2_pix_format src_fmt;
+ struct v4l2_pix_format dst_fmt;
+
+ struct v4l2_ctrl_handler ctrl_handler;
+
+ u32 hflip;
+ u32 vflip;
+ u32 rotate;
+};
+
+struct rotate_dev {
+ struct v4l2_device v4l2_dev;
+ struct video_device vfd;
+ struct device *dev;
+ struct v4l2_m2m_dev *m2m_dev;
+
+ /* Device file mutex */
+ struct mutex dev_mutex;
+
+ void __iomem *base;
+
+ struct clk *bus_clk;
+ struct clk *mod_clk;
+
+ struct reset_control *rstc;
+};
+
+#endif
diff --git a/drivers/media/platform/sunxi/sun8i-rotate/sun8i_formats.c b/drivers/media/platform/sunxi/sun8i-rotate/sun8i_formats.c
new file mode 100644
index 0000000..cebfbc5
--- /dev/null
+++ b/drivers/media/platform/sunxi/sun8i-rotate/sun8i_formats.c
@@ -0,0 +1,273 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) 2020 Jernej Skrabec <jernej.skrabec@siol.net> */
+
+#include "sun8i-formats.h"
+#include "sun8i-rotate.h"
+
+/*
+ * Formats not included in array:
+ * ROTATE_FORMAT_BGR565
+ * ROTATE_FORMAT_VYUV
+ */
+
+static const struct rotate_format rotate_formats[] = {
+ {
+ .fourcc = V4L2_PIX_FMT_ARGB32,
+ .hw_format = ROTATE_FORMAT_ARGB32,
+ .planes = 1,
+ .bpp = { 4, 0, 0 },
+ .hsub = 1,
+ .vsub = 1,
+ .flags = ROTATE_FLAG_OUTPUT
+ }, {
+ .fourcc = V4L2_PIX_FMT_ABGR32,
+ .hw_format = ROTATE_FORMAT_ABGR32,
+ .planes = 1,
+ .bpp = { 4, 0, 0 },
+ .hsub = 1,
+ .vsub = 1,
+ .flags = ROTATE_FLAG_OUTPUT
+ }, {
+ .fourcc = V4L2_PIX_FMT_RGBA32,
+ .hw_format = ROTATE_FORMAT_RGBA32,
+ .planes = 1,
+ .bpp = { 4, 0, 0 },
+ .hsub = 1,
+ .vsub = 1,
+ .flags = ROTATE_FLAG_OUTPUT
+ }, {
+ .fourcc = V4L2_PIX_FMT_BGRA32,
+ .hw_format = ROTATE_FORMAT_BGRA32,
+ .planes = 1,
+ .bpp = { 4, 0, 0 },
+ .hsub = 1,
+ .vsub = 1,
+ .flags = ROTATE_FLAG_OUTPUT
+ }, {
+ .fourcc = V4L2_PIX_FMT_XRGB32,
+ .hw_format = ROTATE_FORMAT_XRGB32,
+ .planes = 1,
+ .bpp = { 4, 0, 0 },
+ .hsub = 1,
+ .vsub = 1,
+ .flags = ROTATE_FLAG_OUTPUT
+ }, {
+ .fourcc = V4L2_PIX_FMT_XBGR32,
+ .hw_format = ROTATE_FORMAT_XBGR32,
+ .planes = 1,
+ .bpp = { 4, 0, 0 },
+ .hsub = 1,
+ .vsub = 1,
+ .flags = ROTATE_FLAG_OUTPUT
+ }, {
+ .fourcc = V4L2_PIX_FMT_RGB32,
+ .hw_format = ROTATE_FORMAT_RGBX32,
+ .planes = 1,
+ .bpp = { 4, 0, 0 },
+ .hsub = 1,
+ .vsub = 1,
+ .flags = ROTATE_FLAG_OUTPUT
+ }, {
+ .fourcc = V4L2_PIX_FMT_BGR32,
+ .hw_format = ROTATE_FORMAT_BGRX32,
+ .planes = 1,
+ .bpp = { 4, 0, 0 },
+ .hsub = 1,
+ .vsub = 1,
+ .flags = ROTATE_FLAG_OUTPUT
+ }, {
+ .fourcc = V4L2_PIX_FMT_RGB24,
+ .hw_format = ROTATE_FORMAT_RGB24,
+ .planes = 1,
+ .bpp = { 3, 0, 0 },
+ .hsub = 1,
+ .vsub = 1,
+ .flags = ROTATE_FLAG_OUTPUT
+ }, {
+ .fourcc = V4L2_PIX_FMT_BGR24,
+ .hw_format = ROTATE_FORMAT_BGR24,
+ .planes = 1,
+ .bpp = { 3, 0, 0 },
+ .hsub = 1,
+ .vsub = 1,
+ .flags = ROTATE_FLAG_OUTPUT
+ }, {
+ .fourcc = V4L2_PIX_FMT_RGB565,
+ .hw_format = ROTATE_FORMAT_RGB565,
+ .planes = 1,
+ .bpp = { 2, 0, 0 },
+ .hsub = 1,
+ .vsub = 1,
+ .flags = ROTATE_FLAG_OUTPUT
+ }, {
+ .fourcc = V4L2_PIX_FMT_ARGB444,
+ .hw_format = ROTATE_FORMAT_ARGB4444,
+ .planes = 1,
+ .bpp = { 2, 0, 0 },
+ .hsub = 1,
+ .vsub = 1,
+ .flags = ROTATE_FLAG_OUTPUT
+ }, {
+ .fourcc = V4L2_PIX_FMT_ABGR444,
+ .hw_format = ROTATE_FORMAT_ABGR4444,
+ .planes = 1,
+ .bpp = { 2, 0, 0 },
+ .hsub = 1,
+ .vsub = 1,
+ .flags = ROTATE_FLAG_OUTPUT
+ }, {
+ .fourcc = V4L2_PIX_FMT_RGBA444,
+ .hw_format = ROTATE_FORMAT_RGBA4444,
+ .planes = 1,
+ .bpp = { 2, 0, 0 },
+ .hsub = 1,
+ .vsub = 1,
+ .flags = ROTATE_FLAG_OUTPUT
+ }, {
+ .fourcc = V4L2_PIX_FMT_BGRA444,
+ .hw_format = ROTATE_FORMAT_BGRA4444,
+ .planes = 1,
+ .bpp = { 2, 0, 0 },
+ .hsub = 1,
+ .vsub = 1,
+ .flags = ROTATE_FLAG_OUTPUT
+ }, {
+ .fourcc = V4L2_PIX_FMT_ARGB555,
+ .hw_format = ROTATE_FORMAT_ARGB1555,
+ .planes = 1,
+ .bpp = { 2, 0, 0 },
+ .hsub = 1,
+ .vsub = 1,
+ .flags = ROTATE_FLAG_OUTPUT
+ }, {
+ .fourcc = V4L2_PIX_FMT_ABGR555,
+ .hw_format = ROTATE_FORMAT_ABGR1555,
+ .planes = 1,
+ .bpp = { 2, 0, 0 },
+ .hsub = 1,
+ .vsub = 1,
+ .flags = ROTATE_FLAG_OUTPUT
+ }, {
+ .fourcc = V4L2_PIX_FMT_RGBA555,
+ .hw_format = ROTATE_FORMAT_RGBA5551,
+ .planes = 1,
+ .bpp = { 2, 0, 0 },
+ .hsub = 1,
+ .vsub = 1,
+ .flags = ROTATE_FLAG_OUTPUT
+ }, {
+ .fourcc = V4L2_PIX_FMT_BGRA555,
+ .hw_format = ROTATE_FORMAT_BGRA5551,
+ .planes = 1,
+ .bpp = { 2, 0, 0 },
+ .hsub = 1,
+ .vsub = 1,
+ .flags = ROTATE_FLAG_OUTPUT
+ }, {
+ .fourcc = V4L2_PIX_FMT_YVYU,
+ .hw_format = ROTATE_FORMAT_YVYU,
+ .planes = 1,
+ .bpp = { 2, 0, 0 },
+ .hsub = 2,
+ .vsub = 1,
+ .flags = ROTATE_FLAG_YUV
+ }, {
+ .fourcc = V4L2_PIX_FMT_UYVY,
+ .hw_format = ROTATE_FORMAT_UYVY,
+ .planes = 1,
+ .bpp = { 2, 0, 0 },
+ .hsub = 2,
+ .vsub = 1,
+ .flags = ROTATE_FLAG_YUV
+ }, {
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .hw_format = ROTATE_FORMAT_YUYV,
+ .planes = 1,
+ .bpp = { 2, 0, 0 },
+ .hsub = 2,
+ .vsub = 1,
+ .flags = ROTATE_FLAG_YUV
+ }, {
+ .fourcc = V4L2_PIX_FMT_NV61,
+ .hw_format = ROTATE_FORMAT_NV61,
+ .planes = 2,
+ .bpp = { 1, 2, 0 },
+ .hsub = 2,
+ .vsub = 1,
+ .flags = ROTATE_FLAG_YUV
+ }, {
+ .fourcc = V4L2_PIX_FMT_NV16,
+ .hw_format = ROTATE_FORMAT_NV16,
+ .planes = 2,
+ .bpp = { 1, 2, 0 },
+ .hsub = 2,
+ .vsub = 1,
+ .flags = ROTATE_FLAG_YUV
+ }, {
+ .fourcc = V4L2_PIX_FMT_YUV422P,
+ .hw_format = ROTATE_FORMAT_YUV422P,
+ .planes = 3,
+ .bpp = { 1, 1, 1 },
+ .hsub = 2,
+ .vsub = 1,
+ .flags = ROTATE_FLAG_YUV
+ }, {
+ .fourcc = V4L2_PIX_FMT_NV21,
+ .hw_format = ROTATE_FORMAT_NV21,
+ .planes = 2,
+ .bpp = { 1, 2, 0 },
+ .hsub = 2,
+ .vsub = 2,
+ .flags = ROTATE_FLAG_YUV
+ }, {
+ .fourcc = V4L2_PIX_FMT_NV12,
+ .hw_format = ROTATE_FORMAT_NV12,
+ .planes = 2,
+ .bpp = { 1, 2, 0 },
+ .hsub = 2,
+ .vsub = 2,
+ .flags = ROTATE_FLAG_YUV
+ }, {
+ .fourcc = V4L2_PIX_FMT_YUV420,
+ .hw_format = ROTATE_FORMAT_YUV420P,
+ .planes = 3,
+ .bpp = { 1, 1, 1 },
+ .hsub = 2,
+ .vsub = 2,
+ .flags = ROTATE_FLAG_YUV | ROTATE_FLAG_OUTPUT
+ },
+};
+
+const struct rotate_format *rotate_find_format(u32 pixelformat)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(rotate_formats); i++)
+ if (rotate_formats[i].fourcc == pixelformat)
+ return &rotate_formats[i];
+
+ return NULL;
+}
+
+int rotate_enum_fmt(struct v4l2_fmtdesc *f, bool dst)
+{
+ int i, index;
+
+ index = 0;
+
+ for (i = 0; i < ARRAY_SIZE(rotate_formats); i++) {
+ /* not all formats can be used for capture buffers */
+ if (dst && !(rotate_formats[i].flags & ROTATE_FLAG_OUTPUT))
+ continue;
+
+ if (index == f->index) {
+ f->pixelformat = rotate_formats[i].fourcc;
+
+ return 0;
+ }
+
+ index++;
+ }
+
+ return -EINVAL;
+}
diff --git a/drivers/media/platform/sunxi/sun8i-rotate/sun8i_rotate.c b/drivers/media/platform/sunxi/sun8i-rotate/sun8i_rotate.c
new file mode 100644
index 0000000..fbcca59
--- /dev/null
+++ b/drivers/media/platform/sunxi/sun8i-rotate/sun8i_rotate.c
@@ -0,0 +1,921 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Allwinner sun8i DE2 rotation driver
+ *
+ * Copyright (C) 2020 Jernej Skrabec <jernej.skrabec@siol.net>
+ */
+
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/reset.h>
+
+#include <media/v4l2-device.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-mem2mem.h>
+
+#include "sun8i-formats.h"
+#include "sun8i-rotate.h"
+
+static inline u32 rotate_read(struct rotate_dev *dev, u32 reg)
+{
+ return readl(dev->base + reg);
+}
+
+static inline void rotate_write(struct rotate_dev *dev, u32 reg, u32 value)
+{
+ writel(value, dev->base + reg);
+}
+
+static inline void rotate_set_bits(struct rotate_dev *dev, u32 reg, u32 bits)
+{
+ writel(readl(dev->base + reg) | bits, dev->base + reg);
+}
+
+static void rotate_calc_addr_pitch(dma_addr_t buffer,
+ u32 bytesperline, u32 height,
+ const struct rotate_format *fmt,
+ dma_addr_t *addr, u32 *pitch)
+{
+ u32 size;
+ int i;
+
+ for (i = 0; i < fmt->planes; i++) {
+ pitch[i] = bytesperline;
+ addr[i] = buffer;
+ if (i > 0)
+ pitch[i] /= fmt->hsub / fmt->bpp[i];
+ size = pitch[i] * height;
+ if (i > 0)
+ size /= fmt->vsub;
+ buffer += size;
+ }
+}
+
+static void rotate_device_run(void *priv)
+{
+ struct rotate_ctx *ctx = priv;
+ struct rotate_dev *dev = ctx->dev;
+ struct vb2_v4l2_buffer *src, *dst;
+ const struct rotate_format *fmt;
+ dma_addr_t addr[3];
+ u32 val, pitch[3];
+
+ src = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
+ dst = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
+
+ v4l2_m2m_buf_copy_metadata(src, dst, true);
+
+ val = ROTATE_GLB_CTL_MODE(ROTATE_MODE_COPY_ROTATE);
+ if (ctx->hflip)
+ val |= ROTATE_GLB_CTL_HFLIP;
+ if (ctx->vflip)
+ val |= ROTATE_GLB_CTL_VFLIP;
+ val |= ROTATE_GLB_CTL_ROTATION(ctx->rotate / 90);
+ if (ctx->rotate != 90 && ctx->rotate != 270)
+ val |= ROTATE_GLB_CTL_BURST_LEN(ROTATE_BURST_64);
+ else
+ val |= ROTATE_GLB_CTL_BURST_LEN(ROTATE_BURST_8);
+ rotate_write(dev, ROTATE_GLB_CTL, val);
+
+ fmt = rotate_find_format(ctx->src_fmt.pixelformat);
+ if (!fmt)
+ return;
+
+ rotate_write(dev, ROTATE_IN_FMT, ROTATE_IN_FMT_FORMAT(fmt->hw_format));
+
+ rotate_calc_addr_pitch(vb2_dma_contig_plane_dma_addr(&src->vb2_buf, 0),
+ ctx->src_fmt.bytesperline, ctx->src_fmt.height,
+ fmt, addr, pitch);
+
+ rotate_write(dev, ROTATE_IN_SIZE,
+ ROTATE_SIZE(ctx->src_fmt.width, ctx->src_fmt.height));
+
+ rotate_write(dev, ROTATE_IN_PITCH0, pitch[0]);
+ rotate_write(dev, ROTATE_IN_PITCH1, pitch[1]);
+ rotate_write(dev, ROTATE_IN_PITCH2, pitch[2]);
+
+ rotate_write(dev, ROTATE_IN_ADDRL0, addr[0]);
+ rotate_write(dev, ROTATE_IN_ADDRL1, addr[1]);
+ rotate_write(dev, ROTATE_IN_ADDRL2, addr[2]);
+
+ rotate_write(dev, ROTATE_IN_ADDRH0, 0);
+ rotate_write(dev, ROTATE_IN_ADDRH1, 0);
+ rotate_write(dev, ROTATE_IN_ADDRH2, 0);
+
+ fmt = rotate_find_format(ctx->dst_fmt.pixelformat);
+ if (!fmt)
+ return;
+
+ rotate_calc_addr_pitch(vb2_dma_contig_plane_dma_addr(&dst->vb2_buf, 0),
+ ctx->dst_fmt.bytesperline, ctx->dst_fmt.height,
+ fmt, addr, pitch);
+
+ rotate_write(dev, ROTATE_OUT_SIZE,
+ ROTATE_SIZE(ctx->dst_fmt.width, ctx->dst_fmt.height));
+
+ rotate_write(dev, ROTATE_OUT_PITCH0, pitch[0]);
+ rotate_write(dev, ROTATE_OUT_PITCH1, pitch[1]);
+ rotate_write(dev, ROTATE_OUT_PITCH2, pitch[2]);
+
+ rotate_write(dev, ROTATE_OUT_ADDRL0, addr[0]);
+ rotate_write(dev, ROTATE_OUT_ADDRL1, addr[1]);
+ rotate_write(dev, ROTATE_OUT_ADDRL2, addr[2]);
+
+ rotate_write(dev, ROTATE_OUT_ADDRH0, 0);
+ rotate_write(dev, ROTATE_OUT_ADDRH1, 0);
+ rotate_write(dev, ROTATE_OUT_ADDRH2, 0);
+
+ rotate_set_bits(dev, ROTATE_INT, ROTATE_INT_FINISH_IRQ_EN);
+ rotate_set_bits(dev, ROTATE_GLB_CTL, ROTATE_GLB_CTL_START);
+}
+
+static irqreturn_t rotate_irq(int irq, void *data)
+{
+ struct vb2_v4l2_buffer *buffer;
+ struct rotate_dev *dev = data;
+ struct rotate_ctx *ctx;
+ unsigned int val;
+
+ ctx = v4l2_m2m_get_curr_priv(dev->m2m_dev);
+ if (!ctx) {
+ v4l2_err(&dev->v4l2_dev,
+ "Instance released before the end of transaction\n");
+ return IRQ_NONE;
+ }
+
+ val = rotate_read(dev, ROTATE_INT);
+ if (!(val & ROTATE_INT_FINISH_IRQ))
+ return IRQ_NONE;
+
+ /* clear flag and disable irq */
+ rotate_write(dev, ROTATE_INT, ROTATE_INT_FINISH_IRQ);
+
+ buffer = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
+ v4l2_m2m_buf_done(buffer, VB2_BUF_STATE_DONE);
+
+ buffer = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
+ v4l2_m2m_buf_done(buffer, VB2_BUF_STATE_DONE);
+
+ v4l2_m2m_job_finish(ctx->dev->m2m_dev, ctx->fh.m2m_ctx);
+
+ return IRQ_HANDLED;
+}
+
+static inline struct rotate_ctx *rotate_file2ctx(struct file *file)
+{
+ return container_of(file->private_data, struct rotate_ctx, fh);
+}
+
+static void rotate_prepare_format(struct v4l2_pix_format *pix_fmt)
+{
+ unsigned int height, width, alignment, sizeimage, size, bpl;
+ const struct rotate_format *fmt;
+ int i;
+
+ fmt = rotate_find_format(pix_fmt->pixelformat);
+ if (!fmt)
+ return;
+
+ width = ALIGN(pix_fmt->width, fmt->hsub);
+ height = ALIGN(pix_fmt->height, fmt->vsub);
+
+ /* all pitches have to be 16 byte aligned */
+ alignment = 16;
+ if (fmt->planes > 1)
+ alignment *= fmt->hsub / fmt->bpp[1];
+ bpl = ALIGN(width * fmt->bpp[0], alignment);
+
+ sizeimage = 0;
+ for (i = 0; i < fmt->planes; i++) {
+ size = bpl * height;
+ if (i > 0) {
+ size *= fmt->bpp[i];
+ size /= fmt->hsub;
+ size /= fmt->vsub;
+ }
+ sizeimage += size;
+ }
+
+ pix_fmt->width = width;
+ pix_fmt->height = height;
+ pix_fmt->bytesperline = bpl;
+ pix_fmt->sizeimage = sizeimage;
+}
+
+static int rotate_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ strscpy(cap->driver, ROTATE_NAME, sizeof(cap->driver));
+ strscpy(cap->card, ROTATE_NAME, sizeof(cap->card));
+ snprintf(cap->bus_info, sizeof(cap->bus_info),
+ "platform:%s", ROTATE_NAME);
+
+ return 0;
+}
+
+static int rotate_enum_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ return rotate_enum_fmt(f, true);
+}
+
+static int rotate_enum_fmt_vid_out(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ return rotate_enum_fmt(f, false);
+}
+
+static int rotate_enum_framesizes(struct file *file, void *priv,
+ struct v4l2_frmsizeenum *fsize)
+{
+ const struct rotate_format *fmt;
+
+ if (fsize->index != 0)
+ return -EINVAL;
+
+ fmt = rotate_find_format(fsize->pixel_format);
+ if (!fmt)
+ return -EINVAL;
+
+ fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
+ fsize->stepwise.min_width = ROTATE_MIN_WIDTH;
+ fsize->stepwise.min_height = ROTATE_MIN_HEIGHT;
+ fsize->stepwise.max_width = ROTATE_MAX_WIDTH;
+ fsize->stepwise.max_height = ROTATE_MAX_HEIGHT;
+ fsize->stepwise.step_width = fmt->hsub;
+ fsize->stepwise.step_height = fmt->vsub;
+
+ return 0;
+}
+
+static int rotate_set_cap_format(struct rotate_ctx *ctx,
+ struct v4l2_pix_format *f,
+ u32 rotate)
+{
+ const struct rotate_format *fmt;
+
+ fmt = rotate_find_format(ctx->src_fmt.pixelformat);
+ if (!fmt)
+ return -EINVAL;
+
+ if (fmt->flags & ROTATE_FLAG_YUV)
+ f->pixelformat = V4L2_PIX_FMT_YUV420;
+ else
+ f->pixelformat = ctx->src_fmt.pixelformat;
+
+ f->field = V4L2_FIELD_NONE;
+
+ if (rotate == 90 || rotate == 270) {
+ f->width = ctx->src_fmt.height;
+ f->height = ctx->src_fmt.width;
+ } else {
+ f->width = ctx->src_fmt.width;
+ f->height = ctx->src_fmt.height;
+ }
+
+ rotate_prepare_format(f);
+
+ return 0;
+}
+
+static int rotate_g_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct rotate_ctx *ctx = rotate_file2ctx(file);
+
+ f->fmt.pix = ctx->dst_fmt;
+
+ return 0;
+}
+
+static int rotate_g_fmt_vid_out(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct rotate_ctx *ctx = rotate_file2ctx(file);
+
+ f->fmt.pix = ctx->src_fmt;
+
+ return 0;
+}
+
+static int rotate_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct rotate_ctx *ctx = rotate_file2ctx(file);
+
+ return rotate_set_cap_format(ctx, &f->fmt.pix, ctx->rotate);
+}
+
+static int rotate_try_fmt_vid_out(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ if (!rotate_find_format(f->fmt.pix.pixelformat))
+ f->fmt.pix.pixelformat = V4L2_PIX_FMT_ARGB32;
+
+ if (f->fmt.pix.width < ROTATE_MIN_WIDTH)
+ f->fmt.pix.width = ROTATE_MIN_WIDTH;
+ if (f->fmt.pix.height < ROTATE_MIN_HEIGHT)
+ f->fmt.pix.height = ROTATE_MIN_HEIGHT;
+
+ if (f->fmt.pix.width > ROTATE_MAX_WIDTH)
+ f->fmt.pix.width = ROTATE_MAX_WIDTH;
+ if (f->fmt.pix.height > ROTATE_MAX_HEIGHT)
+ f->fmt.pix.height = ROTATE_MAX_HEIGHT;
+
+ f->fmt.pix.field = V4L2_FIELD_NONE;
+
+ rotate_prepare_format(&f->fmt.pix);
+
+ return 0;
+}
+
+static int rotate_s_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct rotate_ctx *ctx = rotate_file2ctx(file);
+ struct vb2_queue *vq;
+ int ret;
+
+ ret = rotate_try_fmt_vid_cap(file, priv, f);
+ if (ret)
+ return ret;
+
+ vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
+ if (vb2_is_busy(vq))
+ return -EBUSY;
+
+ ctx->dst_fmt = f->fmt.pix;
+
+ return 0;
+}
+
+static int rotate_s_fmt_vid_out(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct rotate_ctx *ctx = rotate_file2ctx(file);
+ struct vb2_queue *vq;
+ int ret;
+
+ ret = rotate_try_fmt_vid_out(file, priv, f);
+ if (ret)
+ return ret;
+
+ vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
+ if (vb2_is_busy(vq))
+ return -EBUSY;
+
+ /*
+ * Capture queue has to be also checked, because format and size
+ * depends on output format and size.
+ */
+ vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+ if (vb2_is_busy(vq))
+ return -EBUSY;
+
+ ctx->src_fmt = f->fmt.pix;
+
+ /* Propagate colorspace information to capture. */
+ ctx->dst_fmt.colorspace = f->fmt.pix.colorspace;
+ ctx->dst_fmt.xfer_func = f->fmt.pix.xfer_func;
+ ctx->dst_fmt.ycbcr_enc = f->fmt.pix.ycbcr_enc;
+ ctx->dst_fmt.quantization = f->fmt.pix.quantization;
+
+ return rotate_set_cap_format(ctx, &ctx->dst_fmt, ctx->rotate);
+}
+
+static const struct v4l2_ioctl_ops rotate_ioctl_ops = {
+ .vidioc_querycap = rotate_querycap,
+
+ .vidioc_enum_framesizes = rotate_enum_framesizes,
+
+ .vidioc_enum_fmt_vid_cap = rotate_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = rotate_g_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = rotate_try_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = rotate_s_fmt_vid_cap,
+
+ .vidioc_enum_fmt_vid_out = rotate_enum_fmt_vid_out,
+ .vidioc_g_fmt_vid_out = rotate_g_fmt_vid_out,
+ .vidioc_try_fmt_vid_out = rotate_try_fmt_vid_out,
+ .vidioc_s_fmt_vid_out = rotate_s_fmt_vid_out,
+
+ .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs,
+ .vidioc_querybuf = v4l2_m2m_ioctl_querybuf,
+ .vidioc_qbuf = v4l2_m2m_ioctl_qbuf,
+ .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf,
+ .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf,
+ .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs,
+ .vidioc_expbuf = v4l2_m2m_ioctl_expbuf,
+
+ .vidioc_streamon = v4l2_m2m_ioctl_streamon,
+ .vidioc_streamoff = v4l2_m2m_ioctl_streamoff,
+
+ .vidioc_log_status = v4l2_ctrl_log_status,
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+static int rotate_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers,
+ unsigned int *nplanes, unsigned int sizes[],
+ struct device *alloc_devs[])
+{
+ struct rotate_ctx *ctx = vb2_get_drv_priv(vq);
+ struct v4l2_pix_format *pix_fmt;
+
+ if (V4L2_TYPE_IS_OUTPUT(vq->type))
+ pix_fmt = &ctx->src_fmt;
+ else
+ pix_fmt = &ctx->dst_fmt;
+
+ if (*nplanes) {
+ if (sizes[0] < pix_fmt->sizeimage)
+ return -EINVAL;
+ } else {
+ sizes[0] = pix_fmt->sizeimage;
+ *nplanes = 1;
+ }
+
+ return 0;
+}
+
+static int rotate_buf_prepare(struct vb2_buffer *vb)
+{
+ struct vb2_queue *vq = vb->vb2_queue;
+ struct rotate_ctx *ctx = vb2_get_drv_priv(vq);
+ struct v4l2_pix_format *pix_fmt;
+
+ if (V4L2_TYPE_IS_OUTPUT(vq->type))
+ pix_fmt = &ctx->src_fmt;
+ else
+ pix_fmt = &ctx->dst_fmt;
+
+ if (vb2_plane_size(vb, 0) < pix_fmt->sizeimage)
+ return -EINVAL;
+
+ vb2_set_plane_payload(vb, 0, pix_fmt->sizeimage);
+
+ return 0;
+}
+
+static void rotate_buf_queue(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct rotate_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+
+ v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf);
+}
+
+static void rotate_queue_cleanup(struct vb2_queue *vq, u32 state)
+{
+ struct rotate_ctx *ctx = vb2_get_drv_priv(vq);
+ struct vb2_v4l2_buffer *vbuf;
+
+ do {
+ if (V4L2_TYPE_IS_OUTPUT(vq->type))
+ vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
+ else
+ vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
+
+ if (vbuf)
+ v4l2_m2m_buf_done(vbuf, state);
+ } while (vbuf);
+}
+
+static int rotate_start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+ if (V4L2_TYPE_IS_OUTPUT(vq->type)) {
+ struct rotate_ctx *ctx = vb2_get_drv_priv(vq);
+ struct device *dev = ctx->dev->dev;
+ int ret;
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret < 0) {
+ dev_err(dev, "Failed to enable module\n");
+
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static void rotate_stop_streaming(struct vb2_queue *vq)
+{
+ if (V4L2_TYPE_IS_OUTPUT(vq->type)) {
+ struct rotate_ctx *ctx = vb2_get_drv_priv(vq);
+
+ pm_runtime_put(ctx->dev->dev);
+ }
+
+ rotate_queue_cleanup(vq, VB2_BUF_STATE_ERROR);
+}
+
+static const struct vb2_ops rotate_qops = {
+ .queue_setup = rotate_queue_setup,
+ .buf_prepare = rotate_buf_prepare,
+ .buf_queue = rotate_buf_queue,
+ .start_streaming = rotate_start_streaming,
+ .stop_streaming = rotate_stop_streaming,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
+};
+
+static int rotate_queue_init(void *priv, struct vb2_queue *src_vq,
+ struct vb2_queue *dst_vq)
+{
+ struct rotate_ctx *ctx = priv;
+ int ret;
+
+ src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ src_vq->io_modes = VB2_MMAP | VB2_DMABUF;
+ src_vq->drv_priv = ctx;
+ src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
+ src_vq->min_buffers_needed = 1;
+ src_vq->ops = &rotate_qops;
+ src_vq->mem_ops = &vb2_dma_contig_memops;
+ src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ src_vq->lock = &ctx->dev->dev_mutex;
+ src_vq->dev = ctx->dev->dev;
+
+ ret = vb2_queue_init(src_vq);
+ if (ret)
+ return ret;
+
+ dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ dst_vq->io_modes = VB2_MMAP | VB2_DMABUF;
+ dst_vq->drv_priv = ctx;
+ dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
+ dst_vq->min_buffers_needed = 2;
+ dst_vq->ops = &rotate_qops;
+ dst_vq->mem_ops = &vb2_dma_contig_memops;
+ dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ dst_vq->lock = &ctx->dev->dev_mutex;
+ dst_vq->dev = ctx->dev->dev;
+
+ ret = vb2_queue_init(dst_vq);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int rotate_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct rotate_ctx *ctx = container_of(ctrl->handler,
+ struct rotate_ctx,
+ ctrl_handler);
+ struct v4l2_pix_format fmt;
+
+ switch (ctrl->id) {
+ case V4L2_CID_HFLIP:
+ ctx->hflip = ctrl->val;
+ break;
+ case V4L2_CID_VFLIP:
+ ctx->vflip = ctrl->val;
+ break;
+ case V4L2_CID_ROTATE:
+ rotate_set_cap_format(ctx, &fmt, ctrl->val);
+
+ /* Check if capture format needs to be changed */
+ if (fmt.width != ctx->dst_fmt.width ||
+ fmt.height != ctx->dst_fmt.height ||
+ fmt.bytesperline != ctx->dst_fmt.bytesperline ||
+ fmt.sizeimage != ctx->dst_fmt.sizeimage) {
+ struct vb2_queue *vq;
+
+ vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE);
+ if (vb2_is_busy(vq))
+ return -EBUSY;
+
+ rotate_set_cap_format(ctx, &ctx->dst_fmt, ctrl->val);
+ }
+
+ ctx->rotate = ctrl->val;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct v4l2_ctrl_ops rotate_ctrl_ops = {
+ .s_ctrl = rotate_s_ctrl,
+};
+
+static int rotate_setup_ctrls(struct rotate_ctx *ctx)
+{
+ v4l2_ctrl_handler_init(&ctx->ctrl_handler, 3);
+
+ v4l2_ctrl_new_std(&ctx->ctrl_handler, &rotate_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+
+ v4l2_ctrl_new_std(&ctx->ctrl_handler, &rotate_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+
+ v4l2_ctrl_new_std(&ctx->ctrl_handler, &rotate_ctrl_ops,
+ V4L2_CID_ROTATE, 0, 270, 90, 0);
+
+ if (ctx->ctrl_handler.error) {
+ int err = ctx->ctrl_handler.error;
+
+ v4l2_err(&ctx->dev->v4l2_dev, "control setup failed!\n");
+ v4l2_ctrl_handler_free(&ctx->ctrl_handler);
+
+ return err;
+ }
+
+ return v4l2_ctrl_handler_setup(&ctx->ctrl_handler);
+}
+
+static int rotate_open(struct file *file)
+{
+ struct rotate_dev *dev = video_drvdata(file);
+ struct rotate_ctx *ctx = NULL;
+ int ret;
+
+ if (mutex_lock_interruptible(&dev->dev_mutex))
+ return -ERESTARTSYS;
+
+ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+ if (!ctx) {
+ mutex_unlock(&dev->dev_mutex);
+ return -ENOMEM;
+ }
+
+ /* default output format */
+ ctx->src_fmt.pixelformat = V4L2_PIX_FMT_ARGB32;
+ ctx->src_fmt.field = V4L2_FIELD_NONE;
+ ctx->src_fmt.width = 640;
+ ctx->src_fmt.height = 480;
+ rotate_prepare_format(&ctx->src_fmt);
+
+ /* default capture format */
+ rotate_set_cap_format(ctx, &ctx->dst_fmt, ctx->rotate);
+
+ v4l2_fh_init(&ctx->fh, video_devdata(file));
+ file->private_data = &ctx->fh;
+ ctx->dev = dev;
+
+ ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx,
+ &rotate_queue_init);
+ if (IS_ERR(ctx->fh.m2m_ctx)) {
+ ret = PTR_ERR(ctx->fh.m2m_ctx);
+ goto err_free;
+ }
+
+ v4l2_fh_add(&ctx->fh);
+
+ ret = rotate_setup_ctrls(ctx);
+ if (ret)
+ goto err_free;
+
+ ctx->fh.ctrl_handler = &ctx->ctrl_handler;
+
+ mutex_unlock(&dev->dev_mutex);
+
+ return 0;
+
+err_free:
+ kfree(ctx);
+ mutex_unlock(&dev->dev_mutex);
+
+ return ret;
+}
+
+static int rotate_release(struct file *file)
+{
+ struct rotate_dev *dev = video_drvdata(file);
+ struct rotate_ctx *ctx = container_of(file->private_data,
+ struct rotate_ctx, fh);
+
+ mutex_lock(&dev->dev_mutex);
+
+ v4l2_ctrl_handler_free(&ctx->ctrl_handler);
+ v4l2_fh_del(&ctx->fh);
+ v4l2_fh_exit(&ctx->fh);
+ v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
+
+ kfree(ctx);
+
+ mutex_unlock(&dev->dev_mutex);
+
+ return 0;
+}
+
+static const struct v4l2_file_operations rotate_fops = {
+ .owner = THIS_MODULE,
+ .open = rotate_open,
+ .release = rotate_release,
+ .poll = v4l2_m2m_fop_poll,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = v4l2_m2m_fop_mmap,
+};
+
+static const struct video_device rotate_video_device = {
+ .name = ROTATE_NAME,
+ .vfl_dir = VFL_DIR_M2M,
+ .fops = &rotate_fops,
+ .ioctl_ops = &rotate_ioctl_ops,
+ .minor = -1,
+ .release = video_device_release_empty,
+ .device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING,
+};
+
+static const struct v4l2_m2m_ops rotate_m2m_ops = {
+ .device_run = rotate_device_run,
+};
+
+static int rotate_probe(struct platform_device *pdev)
+{
+ struct rotate_dev *dev;
+ struct video_device *vfd;
+ int irq, ret;
+
+ dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+
+ dev->vfd = rotate_video_device;
+ dev->dev = &pdev->dev;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq <= 0)
+ return irq;
+
+ ret = devm_request_irq(dev->dev, irq, rotate_irq,
+ 0, dev_name(dev->dev), dev);
+ if (ret) {
+ dev_err(dev->dev, "Failed to request IRQ\n");
+
+ return ret;
+ }
+
+ dev->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(dev->base))
+ return PTR_ERR(dev->base);
+
+ dev->bus_clk = devm_clk_get(dev->dev, "bus");
+ if (IS_ERR(dev->bus_clk)) {
+ dev_err(dev->dev, "Failed to get bus clock\n");
+
+ return PTR_ERR(dev->bus_clk);
+ }
+
+ dev->mod_clk = devm_clk_get(dev->dev, "mod");
+ if (IS_ERR(dev->mod_clk)) {
+ dev_err(dev->dev, "Failed to get mod clock\n");
+
+ return PTR_ERR(dev->mod_clk);
+ }
+
+ dev->rstc = devm_reset_control_get(dev->dev, NULL);
+ if (IS_ERR(dev->rstc)) {
+ dev_err(dev->dev, "Failed to get reset control\n");
+
+ return PTR_ERR(dev->rstc);
+ }
+
+ mutex_init(&dev->dev_mutex);
+
+ ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
+ if (ret) {
+ dev_err(dev->dev, "Failed to register V4L2 device\n");
+
+ return ret;
+ }
+
+ vfd = &dev->vfd;
+ vfd->lock = &dev->dev_mutex;
+ vfd->v4l2_dev = &dev->v4l2_dev;
+
+ snprintf(vfd->name, sizeof(vfd->name), "%s",
+ rotate_video_device.name);
+ video_set_drvdata(vfd, dev);
+
+ ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0);
+ if (ret) {
+ v4l2_err(&dev->v4l2_dev, "Failed to register video device\n");
+
+ goto err_v4l2;
+ }
+
+ v4l2_info(&dev->v4l2_dev,
+ "Device registered as /dev/video%d\n", vfd->num);
+
+ dev->m2m_dev = v4l2_m2m_init(&rotate_m2m_ops);
+ if (IS_ERR(dev->m2m_dev)) {
+ v4l2_err(&dev->v4l2_dev,
+ "Failed to initialize V4L2 M2M device\n");
+ ret = PTR_ERR(dev->m2m_dev);
+
+ goto err_video;
+ }
+
+ platform_set_drvdata(pdev, dev);
+
+ pm_runtime_enable(dev->dev);
+
+ return 0;
+
+err_video:
+ video_unregister_device(&dev->vfd);
+err_v4l2:
+ v4l2_device_unregister(&dev->v4l2_dev);
+
+ return ret;
+}
+
+static int rotate_remove(struct platform_device *pdev)
+{
+ struct rotate_dev *dev = platform_get_drvdata(pdev);
+
+ v4l2_m2m_release(dev->m2m_dev);
+ video_unregister_device(&dev->vfd);
+ v4l2_device_unregister(&dev->v4l2_dev);
+
+ pm_runtime_force_suspend(&pdev->dev);
+
+ return 0;
+}
+
+static int rotate_runtime_resume(struct device *device)
+{
+ struct rotate_dev *dev = dev_get_drvdata(device);
+ int ret;
+
+ ret = clk_prepare_enable(dev->bus_clk);
+ if (ret) {
+ dev_err(dev->dev, "Failed to enable bus clock\n");
+
+ return ret;
+ }
+
+ ret = clk_prepare_enable(dev->mod_clk);
+ if (ret) {
+ dev_err(dev->dev, "Failed to enable mod clock\n");
+
+ goto err_bus_clk;
+ }
+
+ ret = reset_control_deassert(dev->rstc);
+ if (ret) {
+ dev_err(dev->dev, "Failed to apply reset\n");
+
+ goto err_mod_clk;
+ }
+
+ return 0;
+
+err_mod_clk:
+ clk_disable_unprepare(dev->mod_clk);
+err_bus_clk:
+ clk_disable_unprepare(dev->bus_clk);
+
+ return ret;
+}
+
+static int rotate_runtime_suspend(struct device *device)
+{
+ struct rotate_dev *dev = dev_get_drvdata(device);
+
+ reset_control_assert(dev->rstc);
+
+ clk_disable_unprepare(dev->mod_clk);
+ clk_disable_unprepare(dev->bus_clk);
+
+ return 0;
+}
+
+static const struct of_device_id rotate_dt_match[] = {
+ { .compatible = "allwinner,sun8i-a83t-de2-rotate" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, rotate_dt_match);
+
+static const struct dev_pm_ops rotate_pm_ops = {
+ .runtime_resume = rotate_runtime_resume,
+ .runtime_suspend = rotate_runtime_suspend,
+};
+
+static struct platform_driver rotate_driver = {
+ .probe = rotate_probe,
+ .remove = rotate_remove,
+ .driver = {
+ .name = ROTATE_NAME,
+ .of_match_table = rotate_dt_match,
+ .pm = &rotate_pm_ops,
+ },
+};
+module_platform_driver(rotate_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Jernej Skrabec <jernej.skrabec@siol.net>");
+MODULE_DESCRIPTION("Allwinner DE2 rotate driver");
diff --git a/drivers/media/platform/tegra-cec/Makefile b/drivers/media/platform/tegra-cec/Makefile
deleted file mode 100644
index 97e57c7..0000000
--- 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/tegra-cec/tegra_cec.c b/drivers/media/platform/tegra-cec/tegra_cec.c
deleted file mode 100644
index efb80a7..0000000
--- a/drivers/media/platform/tegra-cec/tegra_cec.c
+++ /dev/null
@@ -1,483 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * Tegra CEC implementation
- *
- * The original 3.10 CEC driver using a custom API:
- *
- * Copyright (c) 2012-2015, NVIDIA CORPORATION. All rights reserved.
- *
- * Conversion to the CEC framework and to the mainline kernel:
- *
- * Copyright 2016-2017 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/err.h>
-#include <linux/errno.h>
-#include <linux/interrupt.h>
-#include <linux/slab.h>
-#include <linux/io.h>
-#include <linux/clk.h>
-#include <linux/delay.h>
-#include <linux/pm.h>
-#include <linux/of.h>
-#include <linux/of_platform.h>
-#include <linux/platform_device.h>
-#include <linux/clk/tegra.h>
-
-#include <media/cec-notifier.h>
-
-#include "tegra_cec.h"
-
-#define TEGRA_CEC_NAME "tegra-cec"
-
-struct tegra_cec {
- struct cec_adapter *adap;
- struct device *dev;
- struct clk *clk;
- void __iomem *cec_base;
- struct cec_notifier *notifier;
- int tegra_cec_irq;
- bool rx_done;
- bool tx_done;
- int tx_status;
- u8 rx_buf[CEC_MAX_MSG_SIZE];
- u8 rx_buf_cnt;
- u32 tx_buf[CEC_MAX_MSG_SIZE];
- u8 tx_buf_cur;
- u8 tx_buf_cnt;
-};
-
-static inline u32 cec_read(struct tegra_cec *cec, u32 reg)
-{
- return readl(cec->cec_base + reg);
-}
-
-static inline void cec_write(struct tegra_cec *cec, u32 reg, u32 val)
-{
- writel(val, cec->cec_base + reg);
-}
-
-static void tegra_cec_error_recovery(struct tegra_cec *cec)
-{
- u32 hw_ctrl;
-
- hw_ctrl = cec_read(cec, TEGRA_CEC_HW_CONTROL);
- cec_write(cec, TEGRA_CEC_HW_CONTROL, 0);
- cec_write(cec, TEGRA_CEC_INT_STAT, 0xffffffff);
- cec_write(cec, TEGRA_CEC_HW_CONTROL, hw_ctrl);
-}
-
-static irqreturn_t tegra_cec_irq_thread_handler(int irq, void *data)
-{
- struct device *dev = data;
- struct tegra_cec *cec = dev_get_drvdata(dev);
-
- if (cec->tx_done) {
- cec_transmit_attempt_done(cec->adap, cec->tx_status);
- cec->tx_done = false;
- }
- if (cec->rx_done) {
- struct cec_msg msg = {};
-
- msg.len = cec->rx_buf_cnt;
- memcpy(msg.msg, cec->rx_buf, msg.len);
- cec_received_msg(cec->adap, &msg);
- cec->rx_done = false;
- cec->rx_buf_cnt = 0;
- }
- return IRQ_HANDLED;
-}
-
-static irqreturn_t tegra_cec_irq_handler(int irq, void *data)
-{
- struct device *dev = data;
- struct tegra_cec *cec = dev_get_drvdata(dev);
- u32 status, mask;
-
- status = cec_read(cec, TEGRA_CEC_INT_STAT);
- mask = cec_read(cec, TEGRA_CEC_INT_MASK);
-
- status &= mask;
-
- if (!status)
- return IRQ_HANDLED;
-
- if (status & TEGRA_CEC_INT_STAT_TX_REGISTER_UNDERRUN) {
- dev_err(dev, "TX underrun, interrupt timing issue!\n");
-
- tegra_cec_error_recovery(cec);
- cec_write(cec, TEGRA_CEC_INT_MASK,
- mask & ~TEGRA_CEC_INT_MASK_TX_REGISTER_EMPTY);
-
- cec->tx_done = true;
- cec->tx_status = CEC_TX_STATUS_ERROR;
- return IRQ_WAKE_THREAD;
- }
-
- if ((status & TEGRA_CEC_INT_STAT_TX_ARBITRATION_FAILED) ||
- (status & TEGRA_CEC_INT_STAT_TX_BUS_ANOMALY_DETECTED)) {
- tegra_cec_error_recovery(cec);
- cec_write(cec, TEGRA_CEC_INT_MASK,
- mask & ~TEGRA_CEC_INT_MASK_TX_REGISTER_EMPTY);
-
- cec->tx_done = true;
- if (status & TEGRA_CEC_INT_STAT_TX_BUS_ANOMALY_DETECTED)
- cec->tx_status = CEC_TX_STATUS_LOW_DRIVE;
- else
- cec->tx_status = CEC_TX_STATUS_ARB_LOST;
- return IRQ_WAKE_THREAD;
- }
-
- if (status & TEGRA_CEC_INT_STAT_TX_FRAME_TRANSMITTED) {
- cec_write(cec, TEGRA_CEC_INT_STAT,
- TEGRA_CEC_INT_STAT_TX_FRAME_TRANSMITTED);
-
- if (status & TEGRA_CEC_INT_STAT_TX_FRAME_OR_BLOCK_NAKD) {
- tegra_cec_error_recovery(cec);
-
- cec->tx_done = true;
- cec->tx_status = CEC_TX_STATUS_NACK;
- } else {
- cec->tx_done = true;
- cec->tx_status = CEC_TX_STATUS_OK;
- }
- return IRQ_WAKE_THREAD;
- }
-
- if (status & TEGRA_CEC_INT_STAT_TX_FRAME_OR_BLOCK_NAKD)
- dev_warn(dev, "TX NAKed on the fly!\n");
-
- if (status & TEGRA_CEC_INT_STAT_TX_REGISTER_EMPTY) {
- if (cec->tx_buf_cur == cec->tx_buf_cnt) {
- cec_write(cec, TEGRA_CEC_INT_MASK,
- mask & ~TEGRA_CEC_INT_MASK_TX_REGISTER_EMPTY);
- } else {
- cec_write(cec, TEGRA_CEC_TX_REGISTER,
- cec->tx_buf[cec->tx_buf_cur++]);
- cec_write(cec, TEGRA_CEC_INT_STAT,
- TEGRA_CEC_INT_STAT_TX_REGISTER_EMPTY);
- }
- }
-
- if (status & TEGRA_CEC_INT_STAT_RX_START_BIT_DETECTED) {
- cec_write(cec, TEGRA_CEC_INT_STAT,
- TEGRA_CEC_INT_STAT_RX_START_BIT_DETECTED);
- cec->rx_done = false;
- cec->rx_buf_cnt = 0;
- }
- if (status & TEGRA_CEC_INT_STAT_RX_REGISTER_FULL) {
- u32 v;
-
- cec_write(cec, TEGRA_CEC_INT_STAT,
- TEGRA_CEC_INT_STAT_RX_REGISTER_FULL);
- v = cec_read(cec, TEGRA_CEC_RX_REGISTER);
- if (cec->rx_buf_cnt < CEC_MAX_MSG_SIZE)
- cec->rx_buf[cec->rx_buf_cnt++] = v & 0xff;
- if (v & TEGRA_CEC_RX_REGISTER_EOM) {
- cec->rx_done = true;
- return IRQ_WAKE_THREAD;
- }
- }
-
- return IRQ_HANDLED;
-}
-
-static int tegra_cec_adap_enable(struct cec_adapter *adap, bool enable)
-{
- struct tegra_cec *cec = adap->priv;
-
- cec->rx_buf_cnt = 0;
- cec->tx_buf_cnt = 0;
- cec->tx_buf_cur = 0;
-
- cec_write(cec, TEGRA_CEC_HW_CONTROL, 0);
- cec_write(cec, TEGRA_CEC_INT_MASK, 0);
- cec_write(cec, TEGRA_CEC_INT_STAT, 0xffffffff);
- cec_write(cec, TEGRA_CEC_SW_CONTROL, 0);
-
- if (!enable)
- return 0;
-
- cec_write(cec, TEGRA_CEC_INPUT_FILTER, (1U << 31) | 0x20);
-
- cec_write(cec, TEGRA_CEC_RX_TIMING_0,
- (0x7a << TEGRA_CEC_RX_TIM0_START_BIT_MAX_LO_TIME_SHIFT) |
- (0x6d << TEGRA_CEC_RX_TIM0_START_BIT_MIN_LO_TIME_SHIFT) |
- (0x93 << TEGRA_CEC_RX_TIM0_START_BIT_MAX_DURATION_SHIFT) |
- (0x86 << TEGRA_CEC_RX_TIM0_START_BIT_MIN_DURATION_SHIFT));
-
- cec_write(cec, TEGRA_CEC_RX_TIMING_1,
- (0x35 << TEGRA_CEC_RX_TIM1_DATA_BIT_MAX_LO_TIME_SHIFT) |
- (0x21 << TEGRA_CEC_RX_TIM1_DATA_BIT_SAMPLE_TIME_SHIFT) |
- (0x56 << TEGRA_CEC_RX_TIM1_DATA_BIT_MAX_DURATION_SHIFT) |
- (0x40 << TEGRA_CEC_RX_TIM1_DATA_BIT_MIN_DURATION_SHIFT));
-
- cec_write(cec, TEGRA_CEC_RX_TIMING_2,
- (0x50 << TEGRA_CEC_RX_TIM2_END_OF_BLOCK_TIME_SHIFT));
-
- cec_write(cec, TEGRA_CEC_TX_TIMING_0,
- (0x74 << TEGRA_CEC_TX_TIM0_START_BIT_LO_TIME_SHIFT) |
- (0x8d << TEGRA_CEC_TX_TIM0_START_BIT_DURATION_SHIFT) |
- (0x08 << TEGRA_CEC_TX_TIM0_BUS_XITION_TIME_SHIFT) |
- (0x71 << TEGRA_CEC_TX_TIM0_BUS_ERROR_LO_TIME_SHIFT));
-
- cec_write(cec, TEGRA_CEC_TX_TIMING_1,
- (0x2f << TEGRA_CEC_TX_TIM1_LO_DATA_BIT_LO_TIME_SHIFT) |
- (0x13 << TEGRA_CEC_TX_TIM1_HI_DATA_BIT_LO_TIME_SHIFT) |
- (0x4b << TEGRA_CEC_TX_TIM1_DATA_BIT_DURATION_SHIFT) |
- (0x21 << TEGRA_CEC_TX_TIM1_ACK_NAK_BIT_SAMPLE_TIME_SHIFT));
-
- cec_write(cec, TEGRA_CEC_TX_TIMING_2,
- (0x07 << TEGRA_CEC_TX_TIM2_BUS_IDLE_TIME_ADDITIONAL_FRAME_SHIFT) |
- (0x05 << TEGRA_CEC_TX_TIM2_BUS_IDLE_TIME_NEW_FRAME_SHIFT) |
- (0x03 << TEGRA_CEC_TX_TIM2_BUS_IDLE_TIME_RETRY_FRAME_SHIFT));
-
- cec_write(cec, TEGRA_CEC_INT_MASK,
- TEGRA_CEC_INT_MASK_TX_REGISTER_UNDERRUN |
- TEGRA_CEC_INT_MASK_TX_FRAME_OR_BLOCK_NAKD |
- TEGRA_CEC_INT_MASK_TX_ARBITRATION_FAILED |
- TEGRA_CEC_INT_MASK_TX_BUS_ANOMALY_DETECTED |
- TEGRA_CEC_INT_MASK_TX_FRAME_TRANSMITTED |
- TEGRA_CEC_INT_MASK_RX_REGISTER_FULL |
- TEGRA_CEC_INT_MASK_RX_START_BIT_DETECTED);
-
- cec_write(cec, TEGRA_CEC_HW_CONTROL, TEGRA_CEC_HWCTRL_TX_RX_MODE);
- return 0;
-}
-
-static int tegra_cec_adap_log_addr(struct cec_adapter *adap, u8 logical_addr)
-{
- struct tegra_cec *cec = adap->priv;
- u32 state = cec_read(cec, TEGRA_CEC_HW_CONTROL);
-
- if (logical_addr == CEC_LOG_ADDR_INVALID)
- state &= ~TEGRA_CEC_HWCTRL_RX_LADDR_MASK;
- else
- state |= TEGRA_CEC_HWCTRL_RX_LADDR((1 << logical_addr));
-
- cec_write(cec, TEGRA_CEC_HW_CONTROL, state);
- return 0;
-}
-
-static int tegra_cec_adap_monitor_all_enable(struct cec_adapter *adap,
- bool enable)
-{
- struct tegra_cec *cec = adap->priv;
- u32 reg = cec_read(cec, TEGRA_CEC_HW_CONTROL);
-
- if (enable)
- reg |= TEGRA_CEC_HWCTRL_RX_SNOOP;
- else
- reg &= ~TEGRA_CEC_HWCTRL_RX_SNOOP;
- cec_write(cec, TEGRA_CEC_HW_CONTROL, reg);
- return 0;
-}
-
-static int tegra_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,
- u32 signal_free_time_ms, struct cec_msg *msg)
-{
- bool retry_xfer = signal_free_time_ms == CEC_SIGNAL_FREE_TIME_RETRY;
- struct tegra_cec *cec = adap->priv;
- unsigned int i;
- u32 mode = 0;
- u32 mask;
-
- if (cec_msg_is_broadcast(msg))
- mode = TEGRA_CEC_TX_REG_BCAST;
-
- cec->tx_buf_cur = 0;
- cec->tx_buf_cnt = msg->len;
-
- for (i = 0; i < msg->len; i++) {
- cec->tx_buf[i] = mode | msg->msg[i];
- if (i == 0)
- cec->tx_buf[i] |= TEGRA_CEC_TX_REG_START_BIT;
- if (i == msg->len - 1)
- cec->tx_buf[i] |= TEGRA_CEC_TX_REG_EOM;
- if (i == 0 && retry_xfer)
- cec->tx_buf[i] |= TEGRA_CEC_TX_REG_RETRY;
- }
-
- mask = cec_read(cec, TEGRA_CEC_INT_MASK);
- cec_write(cec, TEGRA_CEC_INT_MASK,
- mask | TEGRA_CEC_INT_MASK_TX_REGISTER_EMPTY);
-
- return 0;
-}
-
-static const struct cec_adap_ops tegra_cec_ops = {
- .adap_enable = tegra_cec_adap_enable,
- .adap_log_addr = tegra_cec_adap_log_addr,
- .adap_transmit = tegra_cec_adap_transmit,
- .adap_monitor_all_enable = tegra_cec_adap_monitor_all_enable,
-};
-
-static int tegra_cec_probe(struct platform_device *pdev)
-{
- struct device *hdmi_dev;
- struct tegra_cec *cec;
- struct resource *res;
- int ret = 0;
-
- hdmi_dev = cec_notifier_parse_hdmi_phandle(&pdev->dev);
-
- if (IS_ERR(hdmi_dev))
- return PTR_ERR(hdmi_dev);
-
- cec = devm_kzalloc(&pdev->dev, sizeof(struct tegra_cec), GFP_KERNEL);
-
- if (!cec)
- return -ENOMEM;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-
- if (!res) {
- dev_err(&pdev->dev,
- "Unable to allocate resources for device\n");
- return -EBUSY;
- }
-
- if (!devm_request_mem_region(&pdev->dev, res->start, resource_size(res),
- pdev->name)) {
- dev_err(&pdev->dev,
- "Unable to request mem region for device\n");
- return -EBUSY;
- }
-
- cec->tegra_cec_irq = platform_get_irq(pdev, 0);
-
- if (cec->tegra_cec_irq <= 0)
- return -EBUSY;
-
- cec->cec_base = devm_ioremap_nocache(&pdev->dev, res->start,
- resource_size(res));
-
- if (!cec->cec_base) {
- dev_err(&pdev->dev, "Unable to grab IOs for device\n");
- return -EBUSY;
- }
-
- cec->clk = devm_clk_get(&pdev->dev, "cec");
-
- if (IS_ERR_OR_NULL(cec->clk)) {
- dev_err(&pdev->dev, "Can't get clock for CEC\n");
- return -ENOENT;
- }
-
- ret = clk_prepare_enable(cec->clk);
- if (ret) {
- dev_err(&pdev->dev, "Unable to prepare clock for CEC\n");
- return ret;
- }
-
- /* set context info. */
- cec->dev = &pdev->dev;
-
- platform_set_drvdata(pdev, cec);
-
- ret = devm_request_threaded_irq(&pdev->dev, cec->tegra_cec_irq,
- tegra_cec_irq_handler, tegra_cec_irq_thread_handler,
- 0, "cec_irq", &pdev->dev);
-
- if (ret) {
- dev_err(&pdev->dev,
- "Unable to request interrupt for device\n");
- goto err_clk;
- }
-
- cec->adap = cec_allocate_adapter(&tegra_cec_ops, cec, TEGRA_CEC_NAME,
- CEC_CAP_DEFAULTS | CEC_CAP_MONITOR_ALL |
- CEC_CAP_CONNECTOR_INFO,
- CEC_MAX_LOG_ADDRS);
- if (IS_ERR(cec->adap)) {
- ret = -ENOMEM;
- dev_err(&pdev->dev, "Couldn't create cec adapter\n");
- goto err_clk;
- }
-
- cec->notifier = cec_notifier_cec_adap_register(hdmi_dev, NULL,
- cec->adap);
- if (!cec->notifier) {
- ret = -ENOMEM;
- goto err_adapter;
- }
-
- ret = cec_register_adapter(cec->adap, &pdev->dev);
- if (ret) {
- dev_err(&pdev->dev, "Couldn't register device\n");
- goto err_notifier;
- }
-
- return 0;
-
-err_notifier:
- cec_notifier_cec_adap_unregister(cec->notifier);
-err_adapter:
- cec_delete_adapter(cec->adap);
-err_clk:
- clk_disable_unprepare(cec->clk);
- return ret;
-}
-
-static int tegra_cec_remove(struct platform_device *pdev)
-{
- struct tegra_cec *cec = platform_get_drvdata(pdev);
-
- clk_disable_unprepare(cec->clk);
-
- cec_notifier_cec_adap_unregister(cec->notifier);
- cec_unregister_adapter(cec->adap);
-
- return 0;
-}
-
-#ifdef CONFIG_PM
-static int tegra_cec_suspend(struct platform_device *pdev, pm_message_t state)
-{
- struct tegra_cec *cec = platform_get_drvdata(pdev);
-
- clk_disable_unprepare(cec->clk);
-
- dev_notice(&pdev->dev, "suspended\n");
- return 0;
-}
-
-static int tegra_cec_resume(struct platform_device *pdev)
-{
- struct tegra_cec *cec = platform_get_drvdata(pdev);
-
- dev_notice(&pdev->dev, "Resuming\n");
-
- return clk_prepare_enable(cec->clk);
-}
-#endif
-
-static const struct of_device_id tegra_cec_of_match[] = {
- { .compatible = "nvidia,tegra114-cec", },
- { .compatible = "nvidia,tegra124-cec", },
- { .compatible = "nvidia,tegra210-cec", },
- {},
-};
-
-static struct platform_driver tegra_cec_driver = {
- .driver = {
- .name = TEGRA_CEC_NAME,
- .of_match_table = of_match_ptr(tegra_cec_of_match),
- },
- .probe = tegra_cec_probe,
- .remove = tegra_cec_remove,
-
-#ifdef CONFIG_PM
- .suspend = tegra_cec_suspend,
- .resume = tegra_cec_resume,
-#endif
-};
-
-module_platform_driver(tegra_cec_driver);
-
-MODULE_DESCRIPTION("Tegra HDMI CEC driver");
-MODULE_AUTHOR("NVIDIA CORPORATION");
-MODULE_AUTHOR("Cisco Systems, Inc. and/or its affiliates");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/tegra-cec/tegra_cec.h b/drivers/media/platform/tegra-cec/tegra_cec.h
deleted file mode 100644
index 8c370be..0000000
--- a/drivers/media/platform/tegra-cec/tegra_cec.h
+++ /dev/null
@@ -1,116 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-/*
- * Tegra CEC register definitions
- *
- * The original 3.10 CEC driver using a custom API:
- *
- * Copyright (c) 2012-2015, NVIDIA CORPORATION. All rights reserved.
- *
- * Conversion to the CEC framework and to the mainline kernel:
- *
- * Copyright 2016-2017 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- */
-
-#ifndef TEGRA_CEC_H
-#define TEGRA_CEC_H
-
-/* CEC registers */
-#define TEGRA_CEC_SW_CONTROL 0x000
-#define TEGRA_CEC_HW_CONTROL 0x004
-#define TEGRA_CEC_INPUT_FILTER 0x008
-#define TEGRA_CEC_TX_REGISTER 0x010
-#define TEGRA_CEC_RX_REGISTER 0x014
-#define TEGRA_CEC_RX_TIMING_0 0x018
-#define TEGRA_CEC_RX_TIMING_1 0x01c
-#define TEGRA_CEC_RX_TIMING_2 0x020
-#define TEGRA_CEC_TX_TIMING_0 0x024
-#define TEGRA_CEC_TX_TIMING_1 0x028
-#define TEGRA_CEC_TX_TIMING_2 0x02c
-#define TEGRA_CEC_INT_STAT 0x030
-#define TEGRA_CEC_INT_MASK 0x034
-#define TEGRA_CEC_HW_DEBUG_RX 0x038
-#define TEGRA_CEC_HW_DEBUG_TX 0x03c
-
-#define TEGRA_CEC_HWCTRL_RX_LADDR_MASK 0x7fff
-#define TEGRA_CEC_HWCTRL_RX_LADDR(x) \
- ((x) & TEGRA_CEC_HWCTRL_RX_LADDR_MASK)
-#define TEGRA_CEC_HWCTRL_RX_SNOOP BIT(15)
-#define TEGRA_CEC_HWCTRL_RX_NAK_MODE BIT(16)
-#define TEGRA_CEC_HWCTRL_TX_NAK_MODE BIT(24)
-#define TEGRA_CEC_HWCTRL_FAST_SIM_MODE BIT(30)
-#define TEGRA_CEC_HWCTRL_TX_RX_MODE BIT(31)
-
-#define TEGRA_CEC_INPUT_FILTER_MODE BIT(31)
-#define TEGRA_CEC_INPUT_FILTER_FIFO_LENGTH_SHIFT 0
-
-#define TEGRA_CEC_TX_REG_DATA_SHIFT 0
-#define TEGRA_CEC_TX_REG_EOM BIT(8)
-#define TEGRA_CEC_TX_REG_BCAST BIT(12)
-#define TEGRA_CEC_TX_REG_START_BIT BIT(16)
-#define TEGRA_CEC_TX_REG_RETRY BIT(17)
-
-#define TEGRA_CEC_RX_REGISTER_SHIFT 0
-#define TEGRA_CEC_RX_REGISTER_EOM BIT(8)
-#define TEGRA_CEC_RX_REGISTER_ACK BIT(9)
-
-#define TEGRA_CEC_RX_TIM0_START_BIT_MAX_LO_TIME_SHIFT 0
-#define TEGRA_CEC_RX_TIM0_START_BIT_MIN_LO_TIME_SHIFT 8
-#define TEGRA_CEC_RX_TIM0_START_BIT_MAX_DURATION_SHIFT 16
-#define TEGRA_CEC_RX_TIM0_START_BIT_MIN_DURATION_SHIFT 24
-
-#define TEGRA_CEC_RX_TIM1_DATA_BIT_MAX_LO_TIME_SHIFT 0
-#define TEGRA_CEC_RX_TIM1_DATA_BIT_SAMPLE_TIME_SHIFT 8
-#define TEGRA_CEC_RX_TIM1_DATA_BIT_MAX_DURATION_SHIFT 16
-#define TEGRA_CEC_RX_TIM1_DATA_BIT_MIN_DURATION_SHIFT 24
-
-#define TEGRA_CEC_RX_TIM2_END_OF_BLOCK_TIME_SHIFT 0
-
-#define TEGRA_CEC_TX_TIM0_START_BIT_LO_TIME_SHIFT 0
-#define TEGRA_CEC_TX_TIM0_START_BIT_DURATION_SHIFT 8
-#define TEGRA_CEC_TX_TIM0_BUS_XITION_TIME_SHIFT 16
-#define TEGRA_CEC_TX_TIM0_BUS_ERROR_LO_TIME_SHIFT 24
-
-#define TEGRA_CEC_TX_TIM1_LO_DATA_BIT_LO_TIME_SHIFT 0
-#define TEGRA_CEC_TX_TIM1_HI_DATA_BIT_LO_TIME_SHIFT 8
-#define TEGRA_CEC_TX_TIM1_DATA_BIT_DURATION_SHIFT 16
-#define TEGRA_CEC_TX_TIM1_ACK_NAK_BIT_SAMPLE_TIME_SHIFT 24
-
-#define TEGRA_CEC_TX_TIM2_BUS_IDLE_TIME_ADDITIONAL_FRAME_SHIFT 0
-#define TEGRA_CEC_TX_TIM2_BUS_IDLE_TIME_NEW_FRAME_SHIFT 4
-#define TEGRA_CEC_TX_TIM2_BUS_IDLE_TIME_RETRY_FRAME_SHIFT 8
-
-#define TEGRA_CEC_INT_STAT_TX_REGISTER_EMPTY BIT(0)
-#define TEGRA_CEC_INT_STAT_TX_REGISTER_UNDERRUN BIT(1)
-#define TEGRA_CEC_INT_STAT_TX_FRAME_OR_BLOCK_NAKD BIT(2)
-#define TEGRA_CEC_INT_STAT_TX_ARBITRATION_FAILED BIT(3)
-#define TEGRA_CEC_INT_STAT_TX_BUS_ANOMALY_DETECTED BIT(4)
-#define TEGRA_CEC_INT_STAT_TX_FRAME_TRANSMITTED BIT(5)
-#define TEGRA_CEC_INT_STAT_RX_REGISTER_FULL BIT(8)
-#define TEGRA_CEC_INT_STAT_RX_REGISTER_OVERRUN BIT(9)
-#define TEGRA_CEC_INT_STAT_RX_START_BIT_DETECTED BIT(10)
-#define TEGRA_CEC_INT_STAT_RX_BUS_ANOMALY_DETECTED BIT(11)
-#define TEGRA_CEC_INT_STAT_RX_BUS_ERROR_DETECTED BIT(12)
-#define TEGRA_CEC_INT_STAT_FILTERED_RX_DATA_PIN_TRANSITION_H2L BIT(13)
-#define TEGRA_CEC_INT_STAT_FILTERED_RX_DATA_PIN_TRANSITION_L2H BIT(14)
-
-#define TEGRA_CEC_INT_MASK_TX_REGISTER_EMPTY BIT(0)
-#define TEGRA_CEC_INT_MASK_TX_REGISTER_UNDERRUN BIT(1)
-#define TEGRA_CEC_INT_MASK_TX_FRAME_OR_BLOCK_NAKD BIT(2)
-#define TEGRA_CEC_INT_MASK_TX_ARBITRATION_FAILED BIT(3)
-#define TEGRA_CEC_INT_MASK_TX_BUS_ANOMALY_DETECTED BIT(4)
-#define TEGRA_CEC_INT_MASK_TX_FRAME_TRANSMITTED BIT(5)
-#define TEGRA_CEC_INT_MASK_RX_REGISTER_FULL BIT(8)
-#define TEGRA_CEC_INT_MASK_RX_REGISTER_OVERRUN BIT(9)
-#define TEGRA_CEC_INT_MASK_RX_START_BIT_DETECTED BIT(10)
-#define TEGRA_CEC_INT_MASK_RX_BUS_ANOMALY_DETECTED BIT(11)
-#define TEGRA_CEC_INT_MASK_RX_BUS_ERROR_DETECTED BIT(12)
-#define TEGRA_CEC_INT_MASK_FILTERED_RX_DATA_PIN_TRANSITION_H2L BIT(13)
-#define TEGRA_CEC_INT_MASK_FILTERED_RX_DATA_PIN_TRANSITION_L2H BIT(14)
-
-#define TEGRA_CEC_HW_DEBUG_TX_DURATION_COUNT_SHIFT 0
-#define TEGRA_CEC_HW_DEBUG_TX_TXBIT_COUNT_SHIFT 17
-#define TEGRA_CEC_HW_DEBUG_TX_STATE_SHIFT 21
-#define TEGRA_CEC_HW_DEBUG_TX_FORCELOOUT BIT(25)
-#define TEGRA_CEC_HW_DEBUG_TX_TXDATABIT_SAMPLE_TIMER BIT(26)
-
-#endif /* TEGRA_CEC_H */
diff --git a/drivers/media/platform/ti-vpe/Makefile b/drivers/media/platform/ti-vpe/Makefile
index 886ac5e..ad62405 100644
--- a/drivers/media/platform/ti-vpe/Makefile
+++ b/drivers/media/platform/ti-vpe/Makefile
@@ -13,4 +13,4 @@
obj-$(CONFIG_VIDEO_TI_CAL) += ti-cal.o
-ti-cal-y := cal.o
+ti-cal-y := cal.o cal-camerarx.o cal-video.o
diff --git a/drivers/media/platform/ti-vpe/cal-camerarx.c b/drivers/media/platform/ti-vpe/cal-camerarx.c
new file mode 100644
index 0000000..806cbf1
--- /dev/null
+++ b/drivers/media/platform/ti-vpe/cal-camerarx.c
@@ -0,0 +1,649 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * TI Camera Access Layer (CAL) - CAMERARX
+ *
+ * Copyright (c) 2015-2020 Texas Instruments Inc.
+ *
+ * Authors:
+ * Benoit Parrot <bparrot@ti.com>
+ * Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of_graph.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-subdev.h>
+
+#include "cal.h"
+#include "cal_regs.h"
+
+/* ------------------------------------------------------------------
+ * I/O Register Accessors
+ * ------------------------------------------------------------------
+ */
+
+static inline u32 camerarx_read(struct cal_camerarx *phy, u32 offset)
+{
+ return ioread32(phy->base + offset);
+}
+
+static inline void camerarx_write(struct cal_camerarx *phy, u32 offset, u32 val)
+{
+ iowrite32(val, phy->base + offset);
+}
+
+/* ------------------------------------------------------------------
+ * CAMERARX Management
+ * ------------------------------------------------------------------
+ */
+
+static s64 cal_camerarx_get_external_rate(struct cal_camerarx *phy)
+{
+ struct v4l2_ctrl *ctrl;
+ s64 rate;
+
+ ctrl = v4l2_ctrl_find(phy->sensor->ctrl_handler, V4L2_CID_PIXEL_RATE);
+ if (!ctrl) {
+ phy_err(phy, "no pixel rate control in subdev: %s\n",
+ phy->sensor->name);
+ return -EPIPE;
+ }
+
+ rate = v4l2_ctrl_g_ctrl_int64(ctrl);
+ phy_dbg(3, phy, "sensor Pixel Rate: %llu\n", rate);
+
+ return rate;
+}
+
+static void cal_camerarx_lane_config(struct cal_camerarx *phy)
+{
+ u32 val = cal_read(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance));
+ u32 lane_mask = CAL_CSI2_COMPLEXIO_CFG_CLOCK_POSITION_MASK;
+ u32 polarity_mask = CAL_CSI2_COMPLEXIO_CFG_CLOCK_POL_MASK;
+ struct v4l2_fwnode_bus_mipi_csi2 *mipi_csi2 =
+ &phy->endpoint.bus.mipi_csi2;
+ int lane;
+
+ cal_set_field(&val, mipi_csi2->clock_lane + 1, lane_mask);
+ cal_set_field(&val, mipi_csi2->lane_polarities[0], polarity_mask);
+ for (lane = 0; lane < mipi_csi2->num_data_lanes; lane++) {
+ /*
+ * Every lane are one nibble apart starting with the
+ * clock followed by the data lanes so shift masks by 4.
+ */
+ lane_mask <<= 4;
+ polarity_mask <<= 4;
+ cal_set_field(&val, mipi_csi2->data_lanes[lane] + 1, lane_mask);
+ cal_set_field(&val, mipi_csi2->lane_polarities[lane + 1],
+ polarity_mask);
+ }
+
+ cal_write(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), val);
+ phy_dbg(3, phy, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x\n",
+ phy->instance, val);
+}
+
+static void cal_camerarx_enable(struct cal_camerarx *phy)
+{
+ u32 num_lanes = phy->cal->data->camerarx[phy->instance].num_lanes;
+
+ regmap_field_write(phy->fields[F_CAMMODE], 0);
+ /* Always enable all lanes at the phy control level */
+ regmap_field_write(phy->fields[F_LANEENABLE], (1 << num_lanes) - 1);
+ /* F_CSI_MODE is not present on every architecture */
+ if (phy->fields[F_CSI_MODE])
+ regmap_field_write(phy->fields[F_CSI_MODE], 1);
+ regmap_field_write(phy->fields[F_CTRLCLKEN], 1);
+}
+
+void cal_camerarx_disable(struct cal_camerarx *phy)
+{
+ regmap_field_write(phy->fields[F_CTRLCLKEN], 0);
+}
+
+/*
+ * TCLK values are OK at their reset values
+ */
+#define TCLK_TERM 0
+#define TCLK_MISS 1
+#define TCLK_SETTLE 14
+
+static void cal_camerarx_config(struct cal_camerarx *phy, s64 external_rate,
+ const struct cal_fmt *fmt)
+{
+ unsigned int reg0, reg1;
+ unsigned int ths_term, ths_settle;
+ unsigned int csi2_ddrclk_khz;
+ struct v4l2_fwnode_bus_mipi_csi2 *mipi_csi2 =
+ &phy->endpoint.bus.mipi_csi2;
+ u32 num_lanes = mipi_csi2->num_data_lanes;
+
+ /* DPHY timing configuration */
+
+ /*
+ * CSI-2 is DDR and we only count used lanes.
+ *
+ * csi2_ddrclk_khz = external_rate / 1000
+ * / (2 * num_lanes) * fmt->bpp;
+ */
+ csi2_ddrclk_khz = div_s64(external_rate * fmt->bpp,
+ 2 * num_lanes * 1000);
+
+ phy_dbg(1, phy, "csi2_ddrclk_khz: %d\n", csi2_ddrclk_khz);
+
+ /* THS_TERM: Programmed value = floor(20 ns/DDRClk period) */
+ ths_term = 20 * csi2_ddrclk_khz / 1000000;
+ phy_dbg(1, phy, "ths_term: %d (0x%02x)\n", ths_term, ths_term);
+
+ /* THS_SETTLE: Programmed value = floor(105 ns/DDRClk period) + 4 */
+ ths_settle = (105 * csi2_ddrclk_khz / 1000000) + 4;
+ phy_dbg(1, phy, "ths_settle: %d (0x%02x)\n", ths_settle, ths_settle);
+
+ reg0 = camerarx_read(phy, CAL_CSI2_PHY_REG0);
+ cal_set_field(®0, CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_DISABLE,
+ CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_MASK);
+ cal_set_field(®0, ths_term, CAL_CSI2_PHY_REG0_THS_TERM_MASK);
+ cal_set_field(®0, ths_settle, CAL_CSI2_PHY_REG0_THS_SETTLE_MASK);
+
+ phy_dbg(1, phy, "CSI2_%d_REG0 = 0x%08x\n", phy->instance, reg0);
+ camerarx_write(phy, CAL_CSI2_PHY_REG0, reg0);
+
+ reg1 = camerarx_read(phy, CAL_CSI2_PHY_REG1);
+ cal_set_field(®1, TCLK_TERM, CAL_CSI2_PHY_REG1_TCLK_TERM_MASK);
+ cal_set_field(®1, 0xb8, CAL_CSI2_PHY_REG1_DPHY_HS_SYNC_PATTERN_MASK);
+ cal_set_field(®1, TCLK_MISS,
+ CAL_CSI2_PHY_REG1_CTRLCLK_DIV_FACTOR_MASK);
+ cal_set_field(®1, TCLK_SETTLE, CAL_CSI2_PHY_REG1_TCLK_SETTLE_MASK);
+
+ phy_dbg(1, phy, "CSI2_%d_REG1 = 0x%08x\n", phy->instance, reg1);
+ camerarx_write(phy, CAL_CSI2_PHY_REG1, reg1);
+}
+
+static void cal_camerarx_power(struct cal_camerarx *phy, 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;
+
+ cal_write_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance),
+ target_state, CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_MASK);
+
+ for (i = 0; i < 10; i++) {
+ u32 current_state;
+
+ current_state = cal_read_field(phy->cal,
+ CAL_CSI2_COMPLEXIO_CFG(phy->instance),
+ CAL_CSI2_COMPLEXIO_CFG_PWR_STATUS_MASK);
+
+ if (current_state == target_state)
+ break;
+
+ usleep_range(1000, 1100);
+ }
+
+ if (i == 10)
+ phy_err(phy, "Failed to power %s complexio\n",
+ enable ? "up" : "down");
+}
+
+static void cal_camerarx_wait_reset(struct cal_camerarx *phy)
+{
+ unsigned long timeout;
+
+ timeout = jiffies + msecs_to_jiffies(750);
+ while (time_before(jiffies, timeout)) {
+ if (cal_read_field(phy->cal,
+ CAL_CSI2_COMPLEXIO_CFG(phy->instance),
+ CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_MASK) ==
+ CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETCOMPLETED)
+ break;
+ usleep_range(500, 5000);
+ }
+
+ if (cal_read_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance),
+ CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_MASK) !=
+ CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETCOMPLETED)
+ phy_err(phy, "Timeout waiting for Complex IO reset done\n");
+}
+
+static void cal_camerarx_wait_stop_state(struct cal_camerarx *phy)
+{
+ unsigned long timeout;
+
+ timeout = jiffies + msecs_to_jiffies(750);
+ while (time_before(jiffies, timeout)) {
+ if (cal_read_field(phy->cal,
+ CAL_CSI2_TIMING(phy->instance),
+ CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK) == 0)
+ break;
+ usleep_range(500, 5000);
+ }
+
+ if (cal_read_field(phy->cal, CAL_CSI2_TIMING(phy->instance),
+ CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK) != 0)
+ phy_err(phy, "Timeout waiting for stop state\n");
+}
+
+int cal_camerarx_start(struct cal_camerarx *phy, const struct cal_fmt *fmt)
+{
+ s64 external_rate;
+ u32 sscounter;
+ u32 val;
+ int ret;
+
+ external_rate = cal_camerarx_get_external_rate(phy);
+ if (external_rate < 0)
+ return external_rate;
+
+ ret = v4l2_subdev_call(phy->sensor, core, s_power, 1);
+ if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) {
+ phy_err(phy, "power on failed in subdev\n");
+ return ret;
+ }
+
+ /*
+ * CSI-2 PHY Link Initialization Sequence, according to the DRA74xP /
+ * DRA75xP / DRA76xP / DRA77xP TRM. The DRA71x / DRA72x and the AM65x /
+ * DRA80xM TRMs have a a slightly simplified sequence.
+ */
+
+ /*
+ * 1. Configure all CSI-2 low level protocol registers to be ready to
+ * receive signals/data from the CSI-2 PHY.
+ *
+ * i.-v. Configure the lanes position and polarity.
+ */
+ cal_camerarx_lane_config(phy);
+
+ /*
+ * vi.-vii. Configure D-PHY mode, enable the required lanes and
+ * enable the CAMERARX clock.
+ */
+ cal_camerarx_enable(phy);
+
+ /*
+ * 2. CSI PHY and link initialization sequence.
+ *
+ * a. Deassert the CSI-2 PHY reset. Do not wait for reset completion
+ * at this point, as it requires the external sensor to send the
+ * CSI-2 HS clock.
+ */
+ cal_write_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance),
+ CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_OPERATIONAL,
+ CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_MASK);
+ phy_dbg(3, phy, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x De-assert Complex IO Reset\n",
+ phy->instance,
+ cal_read(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance)));
+
+ /* Dummy read to allow SCP reset to complete. */
+ camerarx_read(phy, CAL_CSI2_PHY_REG0);
+
+ /* Program the PHY timing parameters. */
+ cal_camerarx_config(phy, external_rate, fmt);
+
+ /*
+ * b. Assert the FORCERXMODE signal.
+ *
+ * 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 CSI-2 spec, so we
+ * calculate a timeout that's 100us (rounding up).
+ */
+ sscounter = DIV_ROUND_UP(clk_get_rate(phy->cal->fclk), 10000 * 16 * 4);
+
+ val = cal_read(phy->cal, CAL_CSI2_TIMING(phy->instance));
+ cal_set_field(&val, 1, CAL_CSI2_TIMING_STOP_STATE_X16_IO1_MASK);
+ cal_set_field(&val, 1, CAL_CSI2_TIMING_STOP_STATE_X4_IO1_MASK);
+ cal_set_field(&val, sscounter,
+ CAL_CSI2_TIMING_STOP_STATE_COUNTER_IO1_MASK);
+ cal_write(phy->cal, CAL_CSI2_TIMING(phy->instance), val);
+ phy_dbg(3, phy, "CAL_CSI2_TIMING(%d) = 0x%08x Stop States\n",
+ phy->instance,
+ cal_read(phy->cal, CAL_CSI2_TIMING(phy->instance)));
+
+ /* Assert the FORCERXMODE signal. */
+ cal_write_field(phy->cal, CAL_CSI2_TIMING(phy->instance),
+ 1, CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK);
+ phy_dbg(3, phy, "CAL_CSI2_TIMING(%d) = 0x%08x Force RXMODE\n",
+ phy->instance,
+ cal_read(phy->cal, CAL_CSI2_TIMING(phy->instance)));
+
+ /*
+ * c. Connect pull-down on CSI-2 PHY link (using pad control).
+ *
+ * This is not required on DRA71x, DRA72x, AM65x and DRA80xM. Not
+ * implemented.
+ */
+
+ /*
+ * d. Power up the CSI-2 PHY.
+ * e. Check whether the state status reaches the ON state.
+ */
+ cal_camerarx_power(phy, true);
+
+ /*
+ * Start the sensor to enable the CSI-2 HS clock. We can now wait for
+ * CSI-2 PHY reset to complete.
+ */
+ ret = v4l2_subdev_call(phy->sensor, video, s_stream, 1);
+ if (ret) {
+ v4l2_subdev_call(phy->sensor, core, s_power, 0);
+ phy_err(phy, "stream on failed in subdev\n");
+ return ret;
+ }
+
+ cal_camerarx_wait_reset(phy);
+
+ /* f. Wait for STOPSTATE=1 for all enabled lane modules. */
+ cal_camerarx_wait_stop_state(phy);
+
+ phy_dbg(1, phy, "CSI2_%u_REG1 = 0x%08x (bits 31-28 should be set)\n",
+ phy->instance, camerarx_read(phy, CAL_CSI2_PHY_REG1));
+
+ /*
+ * g. Disable pull-down on CSI-2 PHY link (using pad control).
+ *
+ * This is not required on DRA71x, DRA72x, AM65x and DRA80xM. Not
+ * implemented.
+ */
+
+ return 0;
+}
+
+void cal_camerarx_stop(struct cal_camerarx *phy)
+{
+ unsigned int i;
+ int ret;
+
+ cal_camerarx_power(phy, false);
+
+ /* Assert Complex IO Reset */
+ cal_write_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance),
+ CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL,
+ CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_MASK);
+
+ /* Wait for power down completion */
+ for (i = 0; i < 10; i++) {
+ if (cal_read_field(phy->cal,
+ CAL_CSI2_COMPLEXIO_CFG(phy->instance),
+ CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_MASK) ==
+ CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETONGOING)
+ break;
+ usleep_range(1000, 1100);
+ }
+ phy_dbg(3, phy, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x Complex IO in Reset (%d) %s\n",
+ phy->instance,
+ cal_read(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance)), i,
+ (i >= 10) ? "(timeout)" : "");
+
+ /* Disable the phy */
+ cal_camerarx_disable(phy);
+
+ if (v4l2_subdev_call(phy->sensor, video, s_stream, 0))
+ phy_err(phy, "stream off failed in subdev\n");
+
+ ret = v4l2_subdev_call(phy->sensor, core, s_power, 0);
+ if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV)
+ phy_err(phy, "power off failed in subdev\n");
+}
+
+/*
+ * Errata i913: CSI2 LDO Needs to be disabled when module is powered on
+ *
+ * Enabling CSI2 LDO shorts it to core supply. It is crucial the 2 CSI2
+ * LDOs on the device are disabled if CSI-2 module is powered on
+ * (0x4845 B304 | 0x4845 B384 [28:27] = 0x1) or in ULPS (0x4845 B304
+ * | 0x4845 B384 [28:27] = 0x2) mode. Common concerns include: high
+ * current draw on the module supply in active mode.
+ *
+ * Errata does not apply when CSI-2 module is powered off
+ * (0x4845 B304 | 0x4845 B384 [28:27] = 0x0).
+ *
+ * SW Workaround:
+ * Set the following register bits to disable the LDO,
+ * which is essentially CSI2 REG10 bit 6:
+ *
+ * Core 0: 0x4845 B828 = 0x0000 0040
+ * Core 1: 0x4845 B928 = 0x0000 0040
+ */
+void cal_camerarx_i913_errata(struct cal_camerarx *phy)
+{
+ u32 reg10 = camerarx_read(phy, CAL_CSI2_PHY_REG10);
+
+ cal_set_field(®10, 1, CAL_CSI2_PHY_REG10_I933_LDO_DISABLE_MASK);
+
+ phy_dbg(1, phy, "CSI2_%d_REG10 = 0x%08x\n", phy->instance, reg10);
+ camerarx_write(phy, CAL_CSI2_PHY_REG10, reg10);
+}
+
+/*
+ * Enable the expected IRQ sources
+ */
+void cal_camerarx_enable_irqs(struct cal_camerarx *phy)
+{
+ 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 */
+ cal_write(phy->cal, CAL_HL_IRQENABLE_SET(0),
+ CAL_HL_IRQ_CIO_MASK(phy->instance));
+ cal_write(phy->cal, CAL_CSI2_COMPLEXIO_IRQENABLE(phy->instance),
+ cio_err_mask);
+
+ /* Always enable OCPO error */
+ cal_write(phy->cal, CAL_HL_IRQENABLE_SET(0), CAL_HL_IRQ_OCPO_ERR_MASK);
+
+ /* Enable IRQ_WDMA_END 0/1 */
+ val = 0;
+ cal_set_field(&val, 1, CAL_HL_IRQ_MASK(phy->instance));
+ cal_write(phy->cal, CAL_HL_IRQENABLE_SET(1), val);
+ /* Enable IRQ_WDMA_START 0/1 */
+ val = 0;
+ cal_set_field(&val, 1, CAL_HL_IRQ_MASK(phy->instance));
+ cal_write(phy->cal, CAL_HL_IRQENABLE_SET(2), val);
+ /* Todo: Add VC_IRQ and CSI2_COMPLEXIO_IRQ handling */
+ cal_write(phy->cal, CAL_CSI2_VC_IRQENABLE(0), 0xFF000000);
+}
+
+void cal_camerarx_disable_irqs(struct cal_camerarx *phy)
+{
+ u32 val;
+
+ /* Disable CIO error irqs */
+ cal_write(phy->cal, CAL_HL_IRQENABLE_CLR(0),
+ CAL_HL_IRQ_CIO_MASK(phy->instance));
+ cal_write(phy->cal, CAL_CSI2_COMPLEXIO_IRQENABLE(phy->instance), 0);
+
+ /* Disable IRQ_WDMA_END 0/1 */
+ val = 0;
+ cal_set_field(&val, 1, CAL_HL_IRQ_MASK(phy->instance));
+ cal_write(phy->cal, CAL_HL_IRQENABLE_CLR(1), val);
+ /* Disable IRQ_WDMA_START 0/1 */
+ val = 0;
+ cal_set_field(&val, 1, CAL_HL_IRQ_MASK(phy->instance));
+ cal_write(phy->cal, CAL_HL_IRQENABLE_CLR(2), val);
+ /* Todo: Add VC_IRQ and CSI2_COMPLEXIO_IRQ handling */
+ cal_write(phy->cal, CAL_CSI2_VC_IRQENABLE(0), 0);
+}
+
+void cal_camerarx_ppi_enable(struct cal_camerarx *phy)
+{
+ cal_write(phy->cal, CAL_CSI2_PPI_CTRL(phy->instance), BIT(3));
+ cal_write_field(phy->cal, CAL_CSI2_PPI_CTRL(phy->instance),
+ 1, CAL_CSI2_PPI_CTRL_IF_EN_MASK);
+}
+
+void cal_camerarx_ppi_disable(struct cal_camerarx *phy)
+{
+ cal_write_field(phy->cal, CAL_CSI2_PPI_CTRL(phy->instance),
+ 0, CAL_CSI2_PPI_CTRL_IF_EN_MASK);
+}
+
+static int cal_camerarx_regmap_init(struct cal_dev *cal,
+ struct cal_camerarx *phy)
+{
+ const struct cal_camerarx_data *phy_data;
+ unsigned int i;
+
+ if (!cal->data)
+ return -EINVAL;
+
+ phy_data = &cal->data->camerarx[phy->instance];
+
+ for (i = 0; i < F_MAX_FIELDS; i++) {
+ struct reg_field field = {
+ .reg = cal->syscon_camerrx_offset,
+ .lsb = phy_data->fields[i].lsb,
+ .msb = phy_data->fields[i].msb,
+ };
+
+ /*
+ * Here we update the reg offset with the
+ * value found in DT
+ */
+ phy->fields[i] = devm_regmap_field_alloc(cal->dev,
+ cal->syscon_camerrx,
+ field);
+ if (IS_ERR(phy->fields[i])) {
+ cal_err(cal, "Unable to allocate regmap fields\n");
+ return PTR_ERR(phy->fields[i]);
+ }
+ }
+
+ return 0;
+}
+
+static int cal_camerarx_parse_dt(struct cal_camerarx *phy)
+{
+ struct v4l2_fwnode_endpoint *endpoint = &phy->endpoint;
+ struct device_node *ep_node;
+ char data_lanes[V4L2_FWNODE_CSI2_MAX_DATA_LANES * 2];
+ unsigned int i;
+ int ret;
+
+ /*
+ * Find the endpoint node for the port corresponding to the PHY
+ * instance, and parse its CSI-2-related properties.
+ */
+ ep_node = of_graph_get_endpoint_by_regs(phy->cal->dev->of_node,
+ phy->instance, 0);
+ if (!ep_node) {
+ /*
+ * The endpoint is not mandatory, not all PHY instances need to
+ * be connected in DT.
+ */
+ phy_dbg(3, phy, "Port has no endpoint\n");
+ return 0;
+ }
+
+ endpoint->bus_type = V4L2_MBUS_CSI2_DPHY;
+ ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep_node), endpoint);
+ if (ret < 0) {
+ phy_err(phy, "Failed to parse endpoint\n");
+ goto done;
+ }
+
+ for (i = 0; i < endpoint->bus.mipi_csi2.num_data_lanes; i++) {
+ unsigned int lane = endpoint->bus.mipi_csi2.data_lanes[i];
+
+ if (lane > 4) {
+ phy_err(phy, "Invalid position %u for data lane %u\n",
+ lane, i);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ data_lanes[i*2] = '0' + lane;
+ data_lanes[i*2+1] = ' ';
+ }
+
+ data_lanes[i*2-1] = '\0';
+
+ phy_dbg(3, phy,
+ "CSI-2 bus: clock lane <%u>, data lanes <%s>, flags 0x%08x\n",
+ endpoint->bus.mipi_csi2.clock_lane, data_lanes,
+ endpoint->bus.mipi_csi2.flags);
+
+ /* Retrieve the connected device and store it for later use. */
+ phy->sensor_node = of_graph_get_remote_port_parent(ep_node);
+ if (!phy->sensor_node) {
+ phy_dbg(3, phy, "Can't get remote parent\n");
+ ret = -EINVAL;
+ goto done;
+ }
+
+ phy_dbg(1, phy, "Found connected device %pOFn\n", phy->sensor_node);
+
+done:
+ of_node_put(ep_node);
+ return ret;
+}
+
+struct cal_camerarx *cal_camerarx_create(struct cal_dev *cal,
+ unsigned int instance)
+{
+ struct platform_device *pdev = to_platform_device(cal->dev);
+ struct cal_camerarx *phy;
+ int ret;
+
+ phy = kzalloc(sizeof(*phy), GFP_KERNEL);
+ if (!phy)
+ return ERR_PTR(-ENOMEM);
+
+ phy->cal = cal;
+ phy->instance = instance;
+
+ phy->res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ (instance == 0) ?
+ "cal_rx_core0" :
+ "cal_rx_core1");
+ phy->base = devm_ioremap_resource(cal->dev, phy->res);
+ if (IS_ERR(phy->base)) {
+ cal_err(cal, "failed to ioremap\n");
+ ret = PTR_ERR(phy->base);
+ goto error;
+ }
+
+ cal_dbg(1, cal, "ioresource %s at %pa - %pa\n",
+ phy->res->name, &phy->res->start, &phy->res->end);
+
+ ret = cal_camerarx_regmap_init(cal, phy);
+ if (ret)
+ goto error;
+
+ ret = cal_camerarx_parse_dt(phy);
+ if (ret)
+ goto error;
+
+ return phy;
+
+error:
+ kfree(phy);
+ return ERR_PTR(ret);
+}
+
+void cal_camerarx_destroy(struct cal_camerarx *phy)
+{
+ if (!phy)
+ return;
+
+ of_node_put(phy->sensor_node);
+ kfree(phy);
+}
diff --git a/drivers/media/platform/ti-vpe/cal-video.c b/drivers/media/platform/ti-vpe/cal-video.c
new file mode 100644
index 0000000..df472a1
--- /dev/null
+++ b/drivers/media/platform/ti-vpe/cal-video.c
@@ -0,0 +1,886 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * TI Camera Access Layer (CAL) - Video Device
+ *
+ * Copyright (c) 2015-2020 Texas Instruments Inc.
+ *
+ * Authors:
+ * Benoit Parrot <bparrot@ti.com>
+ * Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ */
+
+#include <linux/delay.h>
+#include <linux/ioctl.h>
+#include <linux/pm_runtime.h>
+#include <linux/videodev2.h>
+
+#include <media/media-device.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include "cal.h"
+
+/* ------------------------------------------------------------------
+ * Format Handling
+ * ------------------------------------------------------------------
+ */
+
+static const struct cal_fmt cal_formats[] = {
+ {
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .code = MEDIA_BUS_FMT_YUYV8_2X8,
+ .bpp = 16,
+ }, {
+ .fourcc = V4L2_PIX_FMT_UYVY,
+ .code = MEDIA_BUS_FMT_UYVY8_2X8,
+ .bpp = 16,
+ }, {
+ .fourcc = V4L2_PIX_FMT_YVYU,
+ .code = MEDIA_BUS_FMT_YVYU8_2X8,
+ .bpp = 16,
+ }, {
+ .fourcc = V4L2_PIX_FMT_VYUY,
+ .code = MEDIA_BUS_FMT_VYUY8_2X8,
+ .bpp = 16,
+ }, {
+ .fourcc = V4L2_PIX_FMT_RGB565, /* gggbbbbb rrrrrggg */
+ .code = MEDIA_BUS_FMT_RGB565_2X8_LE,
+ .bpp = 16,
+ }, {
+ .fourcc = V4L2_PIX_FMT_RGB565X, /* rrrrrggg gggbbbbb */
+ .code = MEDIA_BUS_FMT_RGB565_2X8_BE,
+ .bpp = 16,
+ }, {
+ .fourcc = V4L2_PIX_FMT_RGB555, /* gggbbbbb arrrrrgg */
+ .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE,
+ .bpp = 16,
+ }, {
+ .fourcc = V4L2_PIX_FMT_RGB555X, /* arrrrrgg gggbbbbb */
+ .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE,
+ .bpp = 16,
+ }, {
+ .fourcc = V4L2_PIX_FMT_RGB24, /* rgb */
+ .code = MEDIA_BUS_FMT_RGB888_2X12_LE,
+ .bpp = 24,
+ }, {
+ .fourcc = V4L2_PIX_FMT_BGR24, /* bgr */
+ .code = MEDIA_BUS_FMT_RGB888_2X12_BE,
+ .bpp = 24,
+ }, {
+ .fourcc = V4L2_PIX_FMT_RGB32, /* argb */
+ .code = MEDIA_BUS_FMT_ARGB8888_1X32,
+ .bpp = 32,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SBGGR8,
+ .code = MEDIA_BUS_FMT_SBGGR8_1X8,
+ .bpp = 8,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SGBRG8,
+ .code = MEDIA_BUS_FMT_SGBRG8_1X8,
+ .bpp = 8,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SGRBG8,
+ .code = MEDIA_BUS_FMT_SGRBG8_1X8,
+ .bpp = 8,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SRGGB8,
+ .code = MEDIA_BUS_FMT_SRGGB8_1X8,
+ .bpp = 8,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SBGGR10,
+ .code = MEDIA_BUS_FMT_SBGGR10_1X10,
+ .bpp = 10,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SGBRG10,
+ .code = MEDIA_BUS_FMT_SGBRG10_1X10,
+ .bpp = 10,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SGRBG10,
+ .code = MEDIA_BUS_FMT_SGRBG10_1X10,
+ .bpp = 10,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SRGGB10,
+ .code = MEDIA_BUS_FMT_SRGGB10_1X10,
+ .bpp = 10,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SBGGR12,
+ .code = MEDIA_BUS_FMT_SBGGR12_1X12,
+ .bpp = 12,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SGBRG12,
+ .code = MEDIA_BUS_FMT_SGBRG12_1X12,
+ .bpp = 12,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SGRBG12,
+ .code = MEDIA_BUS_FMT_SGRBG12_1X12,
+ .bpp = 12,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SRGGB12,
+ .code = MEDIA_BUS_FMT_SRGGB12_1X12,
+ .bpp = 12,
+ },
+};
+
+/* Print Four-character-code (FOURCC) */
+static char *fourcc_to_str(u32 fmt)
+{
+ static char code[5];
+
+ code[0] = (unsigned char)(fmt & 0xff);
+ code[1] = (unsigned char)((fmt >> 8) & 0xff);
+ code[2] = (unsigned char)((fmt >> 16) & 0xff);
+ code[3] = (unsigned char)((fmt >> 24) & 0xff);
+ code[4] = '\0';
+
+ return code;
+}
+
+/* ------------------------------------------------------------------
+ * V4L2 Video IOCTLs
+ * ------------------------------------------------------------------
+ */
+
+static const struct cal_fmt *find_format_by_pix(struct cal_ctx *ctx,
+ u32 pixelformat)
+{
+ const struct cal_fmt *fmt;
+ unsigned int k;
+
+ for (k = 0; k < ctx->num_active_fmt; k++) {
+ fmt = ctx->active_fmt[k];
+ if (fmt->fourcc == pixelformat)
+ return fmt;
+ }
+
+ return NULL;
+}
+
+static const struct cal_fmt *find_format_by_code(struct cal_ctx *ctx,
+ u32 code)
+{
+ const struct cal_fmt *fmt;
+ unsigned int k;
+
+ for (k = 0; k < ctx->num_active_fmt; k++) {
+ fmt = ctx->active_fmt[k];
+ if (fmt->code == code)
+ return fmt;
+ }
+
+ return NULL;
+}
+
+static int cal_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct cal_ctx *ctx = video_drvdata(file);
+
+ strscpy(cap->driver, CAL_MODULE_NAME, sizeof(cap->driver));
+ strscpy(cap->card, CAL_MODULE_NAME, sizeof(cap->card));
+
+ snprintf(cap->bus_info, sizeof(cap->bus_info),
+ "platform:%s", dev_name(ctx->cal->dev));
+ return 0;
+}
+
+static int cal_enum_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ struct cal_ctx *ctx = video_drvdata(file);
+ const struct cal_fmt *fmt;
+
+ if (f->index >= ctx->num_active_fmt)
+ return -EINVAL;
+
+ fmt = ctx->active_fmt[f->index];
+
+ f->pixelformat = fmt->fourcc;
+ f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ return 0;
+}
+
+static int __subdev_get_format(struct cal_ctx *ctx,
+ struct v4l2_mbus_framefmt *fmt)
+{
+ struct v4l2_subdev_format sd_fmt;
+ struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format;
+ int ret;
+
+ sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ sd_fmt.pad = 0;
+
+ ret = v4l2_subdev_call(ctx->phy->sensor, pad, get_fmt, NULL, &sd_fmt);
+ if (ret)
+ return ret;
+
+ *fmt = *mbus_fmt;
+
+ ctx_dbg(1, ctx, "%s %dx%d code:%04X\n", __func__,
+ fmt->width, fmt->height, fmt->code);
+
+ return 0;
+}
+
+static int __subdev_set_format(struct cal_ctx *ctx,
+ struct v4l2_mbus_framefmt *fmt)
+{
+ struct v4l2_subdev_format sd_fmt;
+ struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format;
+ int ret;
+
+ sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ sd_fmt.pad = 0;
+ *mbus_fmt = *fmt;
+
+ ret = v4l2_subdev_call(ctx->phy->sensor, pad, set_fmt, NULL, &sd_fmt);
+ if (ret)
+ return ret;
+
+ ctx_dbg(1, ctx, "%s %dx%d code:%04X\n", __func__,
+ fmt->width, fmt->height, fmt->code);
+
+ return 0;
+}
+
+static int cal_calc_format_size(struct cal_ctx *ctx,
+ const struct cal_fmt *fmt,
+ struct v4l2_format *f)
+{
+ u32 bpl, max_width;
+
+ if (!fmt) {
+ ctx_dbg(3, ctx, "No cal_fmt provided!\n");
+ return -EINVAL;
+ }
+
+ /*
+ * Maximum width is bound by the DMA max width in bytes.
+ * We need to recalculate the actual maxi width depending on the
+ * number of bytes per pixels required.
+ */
+ max_width = MAX_WIDTH_BYTES / (ALIGN(fmt->bpp, 8) >> 3);
+ v4l_bound_align_image(&f->fmt.pix.width, 48, max_width, 2,
+ &f->fmt.pix.height, 32, MAX_HEIGHT_LINES, 0, 0);
+
+ bpl = (f->fmt.pix.width * ALIGN(fmt->bpp, 8)) >> 3;
+ f->fmt.pix.bytesperline = ALIGN(bpl, 16);
+
+ f->fmt.pix.sizeimage = f->fmt.pix.height *
+ f->fmt.pix.bytesperline;
+
+ ctx_dbg(3, ctx, "%s: fourcc: %s size: %dx%d bpl:%d img_size:%d\n",
+ __func__, fourcc_to_str(f->fmt.pix.pixelformat),
+ f->fmt.pix.width, f->fmt.pix.height,
+ f->fmt.pix.bytesperline, f->fmt.pix.sizeimage);
+
+ return 0;
+}
+
+static int cal_g_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct cal_ctx *ctx = video_drvdata(file);
+
+ *f = ctx->v_fmt;
+
+ return 0;
+}
+
+static int cal_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct cal_ctx *ctx = video_drvdata(file);
+ const struct cal_fmt *fmt;
+ struct v4l2_subdev_frame_size_enum fse;
+ int ret, found;
+
+ fmt = find_format_by_pix(ctx, f->fmt.pix.pixelformat);
+ if (!fmt) {
+ ctx_dbg(3, ctx, "Fourcc format (0x%08x) not found.\n",
+ f->fmt.pix.pixelformat);
+
+ /* Just get the first one enumerated */
+ fmt = ctx->active_fmt[0];
+ f->fmt.pix.pixelformat = fmt->fourcc;
+ }
+
+ f->fmt.pix.field = ctx->v_fmt.fmt.pix.field;
+
+ /* check for/find a valid width/height */
+ ret = 0;
+ found = false;
+ fse.pad = 0;
+ fse.code = fmt->code;
+ fse.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ for (fse.index = 0; ; fse.index++) {
+ ret = v4l2_subdev_call(ctx->phy->sensor, pad, enum_frame_size,
+ NULL, &fse);
+ if (ret)
+ break;
+
+ if ((f->fmt.pix.width == fse.max_width) &&
+ (f->fmt.pix.height == fse.max_height)) {
+ found = true;
+ break;
+ } else if ((f->fmt.pix.width >= fse.min_width) &&
+ (f->fmt.pix.width <= fse.max_width) &&
+ (f->fmt.pix.height >= fse.min_height) &&
+ (f->fmt.pix.height <= fse.max_height)) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ /* use existing values as default */
+ f->fmt.pix.width = ctx->v_fmt.fmt.pix.width;
+ f->fmt.pix.height = ctx->v_fmt.fmt.pix.height;
+ }
+
+ /*
+ * Use current colorspace for now, it will get
+ * updated properly during s_fmt
+ */
+ f->fmt.pix.colorspace = ctx->v_fmt.fmt.pix.colorspace;
+ return cal_calc_format_size(ctx, fmt, f);
+}
+
+static int cal_s_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct cal_ctx *ctx = video_drvdata(file);
+ struct vb2_queue *q = &ctx->vb_vidq;
+ const struct cal_fmt *fmt;
+ struct v4l2_mbus_framefmt mbus_fmt;
+ int ret;
+
+ if (vb2_is_busy(q)) {
+ ctx_dbg(3, ctx, "%s device busy\n", __func__);
+ return -EBUSY;
+ }
+
+ ret = cal_try_fmt_vid_cap(file, priv, f);
+ if (ret < 0)
+ return ret;
+
+ fmt = find_format_by_pix(ctx, f->fmt.pix.pixelformat);
+
+ v4l2_fill_mbus_format(&mbus_fmt, &f->fmt.pix, fmt->code);
+
+ ret = __subdev_set_format(ctx, &mbus_fmt);
+ if (ret)
+ return ret;
+
+ /* Just double check nothing has gone wrong */
+ if (mbus_fmt.code != fmt->code) {
+ ctx_dbg(3, ctx,
+ "%s subdev changed format on us, this should not happen\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ v4l2_fill_pix_format(&ctx->v_fmt.fmt.pix, &mbus_fmt);
+ ctx->v_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ ctx->v_fmt.fmt.pix.pixelformat = fmt->fourcc;
+ cal_calc_format_size(ctx, fmt, &ctx->v_fmt);
+ ctx->fmt = fmt;
+ ctx->m_fmt = mbus_fmt;
+ *f = ctx->v_fmt;
+
+ return 0;
+}
+
+static int cal_enum_framesizes(struct file *file, void *fh,
+ struct v4l2_frmsizeenum *fsize)
+{
+ struct cal_ctx *ctx = video_drvdata(file);
+ const struct cal_fmt *fmt;
+ struct v4l2_subdev_frame_size_enum fse;
+ int ret;
+
+ /* check for valid format */
+ fmt = find_format_by_pix(ctx, fsize->pixel_format);
+ if (!fmt) {
+ ctx_dbg(3, ctx, "Invalid pixel code: %x\n",
+ fsize->pixel_format);
+ return -EINVAL;
+ }
+
+ fse.index = fsize->index;
+ fse.pad = 0;
+ fse.code = fmt->code;
+ fse.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+
+ ret = v4l2_subdev_call(ctx->phy->sensor, pad, enum_frame_size, NULL,
+ &fse);
+ if (ret)
+ return ret;
+
+ ctx_dbg(1, ctx, "%s: index: %d code: %x W:[%d,%d] H:[%d,%d]\n",
+ __func__, fse.index, fse.code, fse.min_width, fse.max_width,
+ fse.min_height, fse.max_height);
+
+ fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+ fsize->discrete.width = fse.max_width;
+ fsize->discrete.height = fse.max_height;
+
+ return 0;
+}
+
+static int cal_enum_input(struct file *file, void *priv,
+ struct v4l2_input *inp)
+{
+ if (inp->index > 0)
+ return -EINVAL;
+
+ inp->type = V4L2_INPUT_TYPE_CAMERA;
+ sprintf(inp->name, "Camera %u", inp->index);
+ return 0;
+}
+
+static int cal_g_input(struct file *file, void *priv, unsigned int *i)
+{
+ *i = 0;
+ return 0;
+}
+
+static int cal_s_input(struct file *file, void *priv, unsigned int i)
+{
+ return i > 0 ? -EINVAL : 0;
+}
+
+/* timeperframe is arbitrary and continuous */
+static int cal_enum_frameintervals(struct file *file, void *priv,
+ struct v4l2_frmivalenum *fival)
+{
+ struct cal_ctx *ctx = video_drvdata(file);
+ const struct cal_fmt *fmt;
+ struct v4l2_subdev_frame_interval_enum fie = {
+ .index = fival->index,
+ .width = fival->width,
+ .height = fival->height,
+ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
+ };
+ int ret;
+
+ fmt = find_format_by_pix(ctx, fival->pixel_format);
+ if (!fmt)
+ return -EINVAL;
+
+ fie.code = fmt->code;
+ ret = v4l2_subdev_call(ctx->phy->sensor, pad, enum_frame_interval,
+ NULL, &fie);
+ if (ret)
+ return ret;
+ fival->type = V4L2_FRMIVAL_TYPE_DISCRETE;
+ fival->discrete = fie.interval;
+
+ return 0;
+}
+
+static const struct v4l2_file_operations cal_fops = {
+ .owner = THIS_MODULE,
+ .open = v4l2_fh_open,
+ .release = vb2_fop_release,
+ .read = vb2_fop_read,
+ .poll = vb2_fop_poll,
+ .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */
+ .mmap = vb2_fop_mmap,
+};
+
+static const struct v4l2_ioctl_ops cal_ioctl_ops = {
+ .vidioc_querycap = cal_querycap,
+ .vidioc_enum_fmt_vid_cap = cal_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = cal_g_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = cal_try_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = cal_s_fmt_vid_cap,
+ .vidioc_enum_framesizes = cal_enum_framesizes,
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_create_bufs = vb2_ioctl_create_bufs,
+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_expbuf = vb2_ioctl_expbuf,
+ .vidioc_enum_input = cal_enum_input,
+ .vidioc_g_input = cal_g_input,
+ .vidioc_s_input = cal_s_input,
+ .vidioc_enum_frameintervals = cal_enum_frameintervals,
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
+ .vidioc_log_status = v4l2_ctrl_log_status,
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+/* ------------------------------------------------------------------
+ * videobuf2 Operations
+ * ------------------------------------------------------------------
+ */
+
+static int cal_queue_setup(struct vb2_queue *vq,
+ unsigned int *nbuffers, unsigned int *nplanes,
+ unsigned int sizes[], struct device *alloc_devs[])
+{
+ struct cal_ctx *ctx = vb2_get_drv_priv(vq);
+ unsigned int size = ctx->v_fmt.fmt.pix.sizeimage;
+
+ if (vq->num_buffers + *nbuffers < 3)
+ *nbuffers = 3 - vq->num_buffers;
+
+ if (*nplanes) {
+ if (sizes[0] < size)
+ return -EINVAL;
+ size = sizes[0];
+ }
+
+ *nplanes = 1;
+ sizes[0] = size;
+
+ ctx_dbg(3, ctx, "nbuffers=%d, size=%d\n", *nbuffers, sizes[0]);
+
+ return 0;
+}
+
+static int cal_buffer_prepare(struct vb2_buffer *vb)
+{
+ struct cal_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+ struct cal_buffer *buf = container_of(vb, struct cal_buffer,
+ vb.vb2_buf);
+ unsigned long size;
+
+ if (WARN_ON(!ctx->fmt))
+ return -EINVAL;
+
+ size = ctx->v_fmt.fmt.pix.sizeimage;
+ if (vb2_plane_size(vb, 0) < size) {
+ ctx_err(ctx,
+ "data will not fit into plane (%lu < %lu)\n",
+ vb2_plane_size(vb, 0), size);
+ return -EINVAL;
+ }
+
+ vb2_set_plane_payload(&buf->vb.vb2_buf, 0, size);
+ return 0;
+}
+
+static void cal_buffer_queue(struct vb2_buffer *vb)
+{
+ struct cal_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+ struct cal_buffer *buf = container_of(vb, struct cal_buffer,
+ vb.vb2_buf);
+ struct cal_dmaqueue *vidq = &ctx->vidq;
+ unsigned long flags;
+
+ /* recheck locking */
+ spin_lock_irqsave(&ctx->slock, flags);
+ list_add_tail(&buf->list, &vidq->active);
+ spin_unlock_irqrestore(&ctx->slock, flags);
+}
+
+static int cal_start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+ struct cal_ctx *ctx = vb2_get_drv_priv(vq);
+ struct cal_dmaqueue *dma_q = &ctx->vidq;
+ struct cal_buffer *buf, *tmp;
+ unsigned long addr;
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&ctx->slock, flags);
+ if (list_empty(&dma_q->active)) {
+ spin_unlock_irqrestore(&ctx->slock, flags);
+ ctx_dbg(3, ctx, "buffer queue is empty\n");
+ return -EIO;
+ }
+
+ buf = list_entry(dma_q->active.next, struct cal_buffer, list);
+ ctx->cur_frm = buf;
+ ctx->next_frm = buf;
+ list_del(&buf->list);
+ spin_unlock_irqrestore(&ctx->slock, flags);
+
+ addr = vb2_dma_contig_plane_dma_addr(&ctx->cur_frm->vb.vb2_buf, 0);
+ ctx->sequence = 0;
+
+ pm_runtime_get_sync(ctx->cal->dev);
+
+ cal_ctx_csi2_config(ctx);
+ cal_ctx_pix_proc_config(ctx);
+ cal_ctx_wr_dma_config(ctx, ctx->v_fmt.fmt.pix.bytesperline,
+ ctx->v_fmt.fmt.pix.height);
+
+ cal_camerarx_enable_irqs(ctx->phy);
+
+ ret = cal_camerarx_start(ctx->phy, ctx->fmt);
+ if (ret)
+ goto err;
+
+ cal_ctx_wr_dma_addr(ctx, addr);
+ cal_camerarx_ppi_enable(ctx->phy);
+
+ if (cal_debug >= 4)
+ cal_quickdump_regs(ctx->cal);
+
+ return 0;
+
+err:
+ spin_lock_irqsave(&ctx->slock, flags);
+ vb2_buffer_done(&ctx->cur_frm->vb.vb2_buf, VB2_BUF_STATE_QUEUED);
+ ctx->cur_frm = NULL;
+ ctx->next_frm = NULL;
+ list_for_each_entry_safe(buf, tmp, &dma_q->active, list) {
+ list_del(&buf->list);
+ vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED);
+ }
+ spin_unlock_irqrestore(&ctx->slock, flags);
+ return ret;
+}
+
+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;
+ bool dma_act;
+
+ cal_camerarx_ppi_disable(ctx->phy);
+
+ /* 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");
+
+ cal_camerarx_disable_irqs(ctx->phy);
+ cal_camerarx_stop(ctx->phy);
+
+ /* Release all active buffers */
+ spin_lock_irqsave(&ctx->slock, flags);
+ list_for_each_entry_safe(buf, tmp, &dma_q->active, list) {
+ list_del(&buf->list);
+ vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+ }
+
+ if (ctx->cur_frm == ctx->next_frm) {
+ vb2_buffer_done(&ctx->cur_frm->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+ } else {
+ vb2_buffer_done(&ctx->cur_frm->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+ vb2_buffer_done(&ctx->next_frm->vb.vb2_buf,
+ VB2_BUF_STATE_ERROR);
+ }
+ ctx->cur_frm = NULL;
+ ctx->next_frm = NULL;
+ spin_unlock_irqrestore(&ctx->slock, flags);
+
+ pm_runtime_put_sync(ctx->cal->dev);
+}
+
+static const struct vb2_ops cal_video_qops = {
+ .queue_setup = cal_queue_setup,
+ .buf_prepare = cal_buffer_prepare,
+ .buf_queue = cal_buffer_queue,
+ .start_streaming = cal_start_streaming,
+ .stop_streaming = cal_stop_streaming,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
+};
+
+/* ------------------------------------------------------------------
+ * V4L2 Initialization and Registration
+ * ------------------------------------------------------------------
+ */
+
+static const struct video_device cal_videodev = {
+ .name = CAL_MODULE_NAME,
+ .fops = &cal_fops,
+ .ioctl_ops = &cal_ioctl_ops,
+ .minor = -1,
+ .release = video_device_release_empty,
+ .device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING |
+ V4L2_CAP_READWRITE,
+};
+
+static int cal_ctx_v4l2_init_formats(struct cal_ctx *ctx)
+{
+ struct v4l2_subdev_mbus_code_enum mbus_code;
+ struct v4l2_mbus_framefmt mbus_fmt;
+ const struct cal_fmt *fmt;
+ unsigned int i, j, k;
+ int ret = 0;
+
+ /* Enumerate sub device formats and enable all matching local formats */
+ ctx->active_fmt = devm_kcalloc(ctx->cal->dev, ARRAY_SIZE(cal_formats),
+ sizeof(*ctx->active_fmt), GFP_KERNEL);
+ ctx->num_active_fmt = 0;
+
+ for (j = 0, i = 0; ret != -EINVAL; ++j) {
+
+ memset(&mbus_code, 0, sizeof(mbus_code));
+ mbus_code.index = j;
+ mbus_code.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ ret = v4l2_subdev_call(ctx->phy->sensor, pad, enum_mbus_code,
+ NULL, &mbus_code);
+ if (ret)
+ continue;
+
+ ctx_dbg(2, ctx,
+ "subdev %s: code: %04x idx: %u\n",
+ ctx->phy->sensor->name, mbus_code.code, j);
+
+ for (k = 0; k < ARRAY_SIZE(cal_formats); k++) {
+ const struct cal_fmt *fmt = &cal_formats[k];
+
+ if (mbus_code.code == fmt->code) {
+ ctx->active_fmt[i] = fmt;
+ ctx_dbg(2, ctx,
+ "matched fourcc: %s: code: %04x idx: %u\n",
+ fourcc_to_str(fmt->fourcc),
+ fmt->code, i);
+ ctx->num_active_fmt = ++i;
+ }
+ }
+ }
+
+ if (i == 0) {
+ ctx_err(ctx, "No suitable format reported by subdev %s\n",
+ ctx->phy->sensor->name);
+ return -EINVAL;
+ }
+
+ ret = __subdev_get_format(ctx, &mbus_fmt);
+ if (ret)
+ return ret;
+
+ fmt = find_format_by_code(ctx, mbus_fmt.code);
+ if (!fmt) {
+ ctx_dbg(3, ctx, "mbus code format (0x%08x) not found.\n",
+ mbus_fmt.code);
+ return -EINVAL;
+ }
+
+ /* Save current subdev format */
+ v4l2_fill_pix_format(&ctx->v_fmt.fmt.pix, &mbus_fmt);
+ ctx->v_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ ctx->v_fmt.fmt.pix.pixelformat = fmt->fourcc;
+ cal_calc_format_size(ctx, fmt, &ctx->v_fmt);
+ ctx->fmt = fmt;
+ ctx->m_fmt = mbus_fmt;
+
+ return 0;
+}
+
+int cal_ctx_v4l2_register(struct cal_ctx *ctx)
+{
+ struct v4l2_ctrl_handler *hdl = &ctx->ctrl_handler;
+ struct video_device *vfd = &ctx->vdev;
+ int ret;
+
+ ret = cal_ctx_v4l2_init_formats(ctx);
+ if (ret)
+ return ret;
+
+ ret = v4l2_ctrl_add_handler(hdl, ctx->phy->sensor->ctrl_handler, NULL,
+ true);
+ if (ret < 0) {
+ ctx_err(ctx, "Failed to add sensor ctrl handler\n");
+ return ret;
+ }
+
+ ret = video_register_device(vfd, VFL_TYPE_VIDEO, cal_video_nr);
+ if (ret < 0) {
+ ctx_err(ctx, "Failed to register video device\n");
+ return ret;
+ }
+
+ ctx_info(ctx, "V4L2 device registered as %s\n",
+ video_device_node_name(vfd));
+
+ return 0;
+}
+
+void cal_ctx_v4l2_unregister(struct cal_ctx *ctx)
+{
+ ctx_dbg(1, ctx, "unregistering %s\n",
+ video_device_node_name(&ctx->vdev));
+
+ video_unregister_device(&ctx->vdev);
+}
+
+int cal_ctx_v4l2_init(struct cal_ctx *ctx)
+{
+ struct v4l2_ctrl_handler *hdl = &ctx->ctrl_handler;
+ struct video_device *vfd = &ctx->vdev;
+ struct vb2_queue *q = &ctx->vb_vidq;
+ int ret;
+
+ INIT_LIST_HEAD(&ctx->vidq.active);
+ spin_lock_init(&ctx->slock);
+ mutex_init(&ctx->mutex);
+
+ /* Initialize the vb2 queue. */
+ q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ;
+ q->drv_priv = ctx;
+ q->buf_struct_size = sizeof(struct cal_buffer);
+ q->ops = &cal_video_qops;
+ q->mem_ops = &vb2_dma_contig_memops;
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ q->lock = &ctx->mutex;
+ q->min_buffers_needed = 3;
+ q->dev = ctx->cal->dev;
+
+ ret = vb2_queue_init(q);
+ if (ret)
+ return ret;
+
+ /* Initialize the video device and media entity. */
+ *vfd = cal_videodev;
+ vfd->v4l2_dev = &ctx->cal->v4l2_dev;
+ vfd->queue = q;
+ snprintf(vfd->name, sizeof(vfd->name), "CAL output %u", ctx->index);
+ vfd->lock = &ctx->mutex;
+ video_set_drvdata(vfd, ctx);
+
+ ctx->pad.flags = MEDIA_PAD_FL_SINK;
+ ret = media_entity_pads_init(&vfd->entity, 1, &ctx->pad);
+ if (ret < 0)
+ return ret;
+
+ /* Initialize the control handler. */
+ ret = v4l2_ctrl_handler_init(hdl, 11);
+ if (ret < 0) {
+ ctx_err(ctx, "Failed to init ctrl handler\n");
+ goto error;
+ }
+
+ vfd->ctrl_handler = hdl;
+
+ return 0;
+
+error:
+ media_entity_cleanup(&vfd->entity);
+ return ret;
+}
+
+void cal_ctx_v4l2_cleanup(struct cal_ctx *ctx)
+{
+ v4l2_ctrl_handler_free(&ctx->ctrl_handler);
+ media_entity_cleanup(&ctx->vdev.entity);
+}
diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c
index f064080..2eef245 100644
--- a/drivers/media/platform/ti-vpe/cal.c
+++ b/drivers/media/platform/ti-vpe/cal.c
@@ -1,645 +1,167 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * TI CAL camera interface driver
+ * TI Camera Access Layer (CAL) - Driver
*
- * Copyright (c) 2015 Texas Instruments Inc.
- * Benoit Parrot, <bparrot@ti.com>
+ * Copyright (c) 2015-2020 Texas Instruments Inc.
+ *
+ * Authors:
+ * Benoit Parrot <bparrot@ti.com>
+ * Laurent Pinchart <laurent.pinchart@ideasonboard.com>
*/
+#include <linux/clk.h>
#include <linux/interrupt.h>
-#include <linux/io.h>
-#include <linux/ioctl.h>
+#include <linux/mfd/syscon.h>
#include <linux/module.h>
+#include <linux/of_device.h>
#include <linux/platform_device.h>
-#include <linux/delay.h>
#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/videodev2.h>
-#include <linux/of_device.h>
-#include <linux/of_graph.h>
-#include <media/v4l2-fwnode.h>
+#include <media/media-device.h>
#include <media/v4l2-async.h>
#include <media/v4l2-common.h>
-#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
-#include <media/v4l2-event.h>
-#include <media/v4l2-ioctl.h>
-#include <media/v4l2-fh.h>
#include <media/videobuf2-core.h>
#include <media/videobuf2-dma-contig.h>
+
+#include "cal.h"
#include "cal_regs.h"
-#define CAL_MODULE_NAME "cal"
-
-#define MAX_WIDTH 1920
-#define MAX_HEIGHT 1200
-
-#define CAL_VERSION "0.1.0"
-
MODULE_DESCRIPTION("TI CAL driver");
MODULE_AUTHOR("Benoit Parrot, <bparrot@ti.com>");
MODULE_LICENSE("GPL v2");
-MODULE_VERSION(CAL_VERSION);
+MODULE_VERSION("0.1.0");
-static unsigned video_nr = -1;
-module_param(video_nr, uint, 0644);
+int cal_video_nr = -1;
+module_param_named(video_nr, cal_video_nr, uint, 0644);
MODULE_PARM_DESC(video_nr, "videoX start number, -1 is autodetect");
-static unsigned debug;
-module_param(debug, uint, 0644);
+unsigned int cal_debug;
+module_param_named(debug, cal_debug, uint, 0644);
MODULE_PARM_DESC(debug, "activates debug info");
-/* timeperframe: min/max and default */
-static const struct v4l2_fract
- tpf_default = {.numerator = 1001, .denominator = 30000};
-
-#define cal_dbg(level, caldev, fmt, arg...) \
- v4l2_dbg(level, debug, &caldev->v4l2_dev, fmt, ##arg)
-#define cal_info(caldev, fmt, arg...) \
- v4l2_info(&caldev->v4l2_dev, fmt, ##arg)
-#define cal_err(caldev, fmt, arg...) \
- v4l2_err(&caldev->v4l2_dev, fmt, ##arg)
-
-#define ctx_dbg(level, ctx, fmt, arg...) \
- v4l2_dbg(level, debug, &ctx->v4l2_dev, fmt, ##arg)
-#define ctx_info(ctx, fmt, arg...) \
- v4l2_info(&ctx->v4l2_dev, fmt, ##arg)
-#define ctx_err(ctx, fmt, arg...) \
- v4l2_err(&ctx->v4l2_dev, fmt, ##arg)
-
-#define CAL_NUM_INPUT 1
-#define CAL_NUM_CONTEXT 2
-
-#define bytes_per_line(pixel, bpp) (ALIGN(pixel * bpp, 16))
-
-#define reg_read(dev, offset) ioread32(dev->base + offset)
-#define reg_write(dev, offset, val) iowrite32(val, dev->base + offset)
-
-#define reg_read_field(dev, offset, mask) get_field(reg_read(dev, offset), \
- mask)
-#define reg_write_field(dev, offset, field, mask) { \
- u32 val = reg_read(dev, offset); \
- set_field(&val, field, mask); \
- reg_write(dev, offset, val); }
-
/* ------------------------------------------------------------------
- * Basic structures
+ * Platform Data
* ------------------------------------------------------------------
*/
-struct cal_fmt {
- u32 fourcc;
- u32 code;
- u8 depth;
-};
-
-static struct cal_fmt cal_formats[] = {
+static const struct cal_camerarx_data dra72x_cal_camerarx[] = {
{
- .fourcc = V4L2_PIX_FMT_YUYV,
- .code = MEDIA_BUS_FMT_YUYV8_2X8,
- .depth = 16,
- }, {
- .fourcc = V4L2_PIX_FMT_UYVY,
- .code = MEDIA_BUS_FMT_UYVY8_2X8,
- .depth = 16,
- }, {
- .fourcc = V4L2_PIX_FMT_YVYU,
- .code = MEDIA_BUS_FMT_YVYU8_2X8,
- .depth = 16,
- }, {
- .fourcc = V4L2_PIX_FMT_VYUY,
- .code = MEDIA_BUS_FMT_VYUY8_2X8,
- .depth = 16,
- }, {
- .fourcc = V4L2_PIX_FMT_RGB565, /* gggbbbbb rrrrrggg */
- .code = MEDIA_BUS_FMT_RGB565_2X8_LE,
- .depth = 16,
- }, {
- .fourcc = V4L2_PIX_FMT_RGB565X, /* rrrrrggg gggbbbbb */
- .code = MEDIA_BUS_FMT_RGB565_2X8_BE,
- .depth = 16,
- }, {
- .fourcc = V4L2_PIX_FMT_RGB555, /* gggbbbbb arrrrrgg */
- .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE,
- .depth = 16,
- }, {
- .fourcc = V4L2_PIX_FMT_RGB555X, /* arrrrrgg gggbbbbb */
- .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE,
- .depth = 16,
- }, {
- .fourcc = V4L2_PIX_FMT_RGB24, /* rgb */
- .code = MEDIA_BUS_FMT_RGB888_2X12_LE,
- .depth = 24,
- }, {
- .fourcc = V4L2_PIX_FMT_BGR24, /* bgr */
- .code = MEDIA_BUS_FMT_RGB888_2X12_BE,
- .depth = 24,
- }, {
- .fourcc = V4L2_PIX_FMT_RGB32, /* argb */
- .code = MEDIA_BUS_FMT_ARGB8888_1X32,
- .depth = 32,
- }, {
- .fourcc = V4L2_PIX_FMT_SBGGR8,
- .code = MEDIA_BUS_FMT_SBGGR8_1X8,
- .depth = 8,
- }, {
- .fourcc = V4L2_PIX_FMT_SGBRG8,
- .code = MEDIA_BUS_FMT_SGBRG8_1X8,
- .depth = 8,
- }, {
- .fourcc = V4L2_PIX_FMT_SGRBG8,
- .code = MEDIA_BUS_FMT_SGRBG8_1X8,
- .depth = 8,
- }, {
- .fourcc = V4L2_PIX_FMT_SRGGB8,
- .code = MEDIA_BUS_FMT_SRGGB8_1X8,
- .depth = 8,
- }, {
- .fourcc = V4L2_PIX_FMT_SBGGR10,
- .code = MEDIA_BUS_FMT_SBGGR10_1X10,
- .depth = 16,
- }, {
- .fourcc = V4L2_PIX_FMT_SGBRG10,
- .code = MEDIA_BUS_FMT_SGBRG10_1X10,
- .depth = 16,
- }, {
- .fourcc = V4L2_PIX_FMT_SGRBG10,
- .code = MEDIA_BUS_FMT_SGRBG10_1X10,
- .depth = 16,
- }, {
- .fourcc = V4L2_PIX_FMT_SRGGB10,
- .code = MEDIA_BUS_FMT_SRGGB10_1X10,
- .depth = 16,
- }, {
- .fourcc = V4L2_PIX_FMT_SBGGR12,
- .code = MEDIA_BUS_FMT_SBGGR12_1X12,
- .depth = 16,
- }, {
- .fourcc = V4L2_PIX_FMT_SGBRG12,
- .code = MEDIA_BUS_FMT_SGBRG12_1X12,
- .depth = 16,
- }, {
- .fourcc = V4L2_PIX_FMT_SGRBG12,
- .code = MEDIA_BUS_FMT_SGRBG12_1X12,
- .depth = 16,
- }, {
- .fourcc = V4L2_PIX_FMT_SRGGB12,
- .code = MEDIA_BUS_FMT_SRGGB12_1X12,
- .depth = 16,
+ .fields = {
+ [F_CTRLCLKEN] = { 10, 10 },
+ [F_CAMMODE] = { 11, 12 },
+ [F_LANEENABLE] = { 13, 16 },
+ [F_CSI_MODE] = { 17, 17 },
+ },
+ .num_lanes = 4,
+ },
+ {
+ .fields = {
+ [F_CTRLCLKEN] = { 0, 0 },
+ [F_CAMMODE] = { 1, 2 },
+ [F_LANEENABLE] = { 3, 4 },
+ [F_CSI_MODE] = { 5, 5 },
+ },
+ .num_lanes = 2,
},
};
-/* Print Four-character-code (FOURCC) */
-static char *fourcc_to_str(u32 fmt)
-{
- static char code[5];
-
- code[0] = (unsigned char)(fmt & 0xff);
- code[1] = (unsigned char)((fmt >> 8) & 0xff);
- code[2] = (unsigned char)((fmt >> 16) & 0xff);
- code[3] = (unsigned char)((fmt >> 24) & 0xff);
- code[4] = '\0';
-
- return code;
-}
-
-/* buffer for one video frame */
-struct cal_buffer {
- /* common v4l buffer stuff -- must be first */
- struct vb2_v4l2_buffer vb;
- struct list_head list;
- const struct cal_fmt *fmt;
+static const struct cal_data dra72x_cal_data = {
+ .camerarx = dra72x_cal_camerarx,
+ .num_csi2_phy = ARRAY_SIZE(dra72x_cal_camerarx),
};
-struct cal_dmaqueue {
- struct list_head active;
-
- /* Counters to control fps rate */
- int frame;
- int ini_jiffies;
+static const struct cal_data dra72x_es1_cal_data = {
+ .camerarx = dra72x_cal_camerarx,
+ .num_csi2_phy = ARRAY_SIZE(dra72x_cal_camerarx),
+ .flags = DRA72_CAL_PRE_ES2_LDO_DISABLE,
};
-struct cm_data {
- void __iomem *base;
- struct resource *res;
-
- unsigned int camerrx_control;
-
- struct platform_device *pdev;
+static const struct cal_camerarx_data dra76x_cal_csi_phy[] = {
+ {
+ .fields = {
+ [F_CTRLCLKEN] = { 8, 8 },
+ [F_CAMMODE] = { 9, 10 },
+ [F_CSI_MODE] = { 11, 11 },
+ [F_LANEENABLE] = { 27, 31 },
+ },
+ .num_lanes = 5,
+ },
+ {
+ .fields = {
+ [F_CTRLCLKEN] = { 0, 0 },
+ [F_CAMMODE] = { 1, 2 },
+ [F_CSI_MODE] = { 3, 3 },
+ [F_LANEENABLE] = { 24, 26 },
+ },
+ .num_lanes = 3,
+ },
};
-struct cc_data {
- void __iomem *base;
- struct resource *res;
-
- struct platform_device *pdev;
+static const struct cal_data dra76x_cal_data = {
+ .camerarx = dra76x_cal_csi_phy,
+ .num_csi2_phy = ARRAY_SIZE(dra76x_cal_csi_phy),
};
-/*
- * there is one cal_dev structure in the driver, it is shared by
- * all instances.
+static const struct cal_camerarx_data am654_cal_csi_phy[] = {
+ {
+ .fields = {
+ [F_CTRLCLKEN] = { 15, 15 },
+ [F_CAMMODE] = { 24, 25 },
+ [F_LANEENABLE] = { 0, 4 },
+ },
+ .num_lanes = 5,
+ },
+};
+
+static const struct cal_data am654_cal_data = {
+ .camerarx = am654_cal_csi_phy,
+ .num_csi2_phy = ARRAY_SIZE(am654_cal_csi_phy),
+};
+
+/* ------------------------------------------------------------------
+ * I/O Register Accessors
+ * ------------------------------------------------------------------
*/
-struct cal_dev {
- int irq;
- void __iomem *base;
- struct resource *res;
- struct platform_device *pdev;
- struct v4l2_device v4l2_dev;
- /* Control Module handle */
- struct cm_data *cm;
- /* Camera Core Module handle */
- struct cc_data *cc[CAL_NUM_CSI2_PORTS];
-
- struct cal_ctx *ctx[CAL_NUM_CONTEXT];
-};
-
-/*
- * There is one cal_ctx structure for each camera core context.
- */
-struct cal_ctx {
- struct v4l2_device v4l2_dev;
- struct v4l2_ctrl_handler ctrl_handler;
- struct video_device vdev;
- struct v4l2_async_notifier notifier;
- struct v4l2_subdev *sensor;
- struct v4l2_fwnode_endpoint endpoint;
-
- struct v4l2_fh fh;
- struct cal_dev *dev;
- struct cc_data *cc;
-
- /* v4l2_ioctl mutex */
- struct mutex mutex;
- /* v4l2 buffers lock */
- spinlock_t slock;
-
- /* Several counters */
- unsigned long jiffies;
-
- struct cal_dmaqueue vidq;
-
- /* Input Number */
- int input;
-
- /* video capture */
- const struct cal_fmt *fmt;
- /* Used to store current pixel format */
- struct v4l2_format v_fmt;
- /* Used to store current mbus frame format */
- struct v4l2_mbus_framefmt m_fmt;
-
- /* Current subdev enumerated format */
- struct cal_fmt *active_fmt[ARRAY_SIZE(cal_formats)];
- int num_active_fmt;
-
- struct v4l2_fract timeperframe;
- unsigned int sequence;
- unsigned int external_rate;
- struct vb2_queue vb_vidq;
- unsigned int seq_count;
- unsigned int csi2_port;
- unsigned int virtual_channel;
-
- /* Pointer pointing to current v4l2_buffer */
- struct cal_buffer *cur_frm;
- /* Pointer pointing to next v4l2_buffer */
- struct cal_buffer *next_frm;
-};
-
-static const struct cal_fmt *find_format_by_pix(struct cal_ctx *ctx,
- u32 pixelformat)
+void cal_quickdump_regs(struct cal_dev *cal)
{
- const struct cal_fmt *fmt;
- unsigned int k;
+ unsigned int i;
- for (k = 0; k < ctx->num_active_fmt; k++) {
- fmt = ctx->active_fmt[k];
- if (fmt->fourcc == pixelformat)
- return fmt;
- }
-
- return NULL;
-}
-
-static const struct cal_fmt *find_format_by_code(struct cal_ctx *ctx,
- u32 code)
-{
- const struct cal_fmt *fmt;
- unsigned int k;
-
- for (k = 0; k < ctx->num_active_fmt; k++) {
- fmt = ctx->active_fmt[k];
- if (fmt->code == code)
- return fmt;
- }
-
- return NULL;
-}
-
-static inline struct cal_ctx *notifier_to_ctx(struct v4l2_async_notifier *n)
-{
- return container_of(n, struct cal_ctx, notifier);
-}
-
-static inline int get_field(u32 value, u32 mask)
-{
- return (value & mask) >> __ffs(mask);
-}
-
-static inline void set_field(u32 *valp, u32 field, u32 mask)
-{
- u32 val = *valp;
-
- val &= ~mask;
- val |= (field << __ffs(mask)) & mask;
- *valp = val;
-}
-
-/*
- * Control Module block access
- */
-static struct cm_data *cm_create(struct cal_dev *dev)
-{
- struct platform_device *pdev = dev->pdev;
- struct cm_data *cm;
-
- cm = devm_kzalloc(&pdev->dev, sizeof(*cm), GFP_KERNEL);
- if (!cm)
- return ERR_PTR(-ENOMEM);
-
- cm->res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
- "camerrx_control");
- cm->base = devm_ioremap_resource(&pdev->dev, cm->res);
- if (IS_ERR(cm->base)) {
- cal_err(dev, "failed to ioremap\n");
- return ERR_CAST(cm->base);
- }
-
- cal_dbg(1, dev, "ioresource %s at %pa - %pa\n",
- cm->res->name, &cm->res->start, &cm->res->end);
-
- return cm;
-}
-
-static void camerarx_phy_enable(struct cal_ctx *ctx)
-{
- u32 val;
-
- if (!ctx->dev->cm->base) {
- ctx_err(ctx, "cm not mapped\n");
- return;
- }
-
- val = reg_read(ctx->dev->cm, CM_CTRL_CORE_CAMERRX_CONTROL);
- if (ctx->csi2_port == 1) {
- set_field(&val, 1, CM_CAMERRX_CTRL_CSI0_CTRLCLKEN_MASK);
- set_field(&val, 0, CM_CAMERRX_CTRL_CSI0_CAMMODE_MASK);
- /* enable all lanes by default */
- set_field(&val, 0xf, CM_CAMERRX_CTRL_CSI0_LANEENABLE_MASK);
- set_field(&val, 1, CM_CAMERRX_CTRL_CSI0_MODE_MASK);
- } else if (ctx->csi2_port == 2) {
- set_field(&val, 1, CM_CAMERRX_CTRL_CSI1_CTRLCLKEN_MASK);
- set_field(&val, 0, CM_CAMERRX_CTRL_CSI1_CAMMODE_MASK);
- /* enable all lanes by default */
- set_field(&val, 0x3, CM_CAMERRX_CTRL_CSI1_LANEENABLE_MASK);
- set_field(&val, 1, CM_CAMERRX_CTRL_CSI1_MODE_MASK);
- }
- reg_write(ctx->dev->cm, CM_CTRL_CORE_CAMERRX_CONTROL, val);
-}
-
-static void camerarx_phy_disable(struct cal_ctx *ctx)
-{
- u32 val;
-
- if (!ctx->dev->cm->base) {
- ctx_err(ctx, "cm not mapped\n");
- return;
- }
-
- val = reg_read(ctx->dev->cm, CM_CTRL_CORE_CAMERRX_CONTROL);
- if (ctx->csi2_port == 1)
- set_field(&val, 0x0, CM_CAMERRX_CTRL_CSI0_CTRLCLKEN_MASK);
- else if (ctx->csi2_port == 2)
- set_field(&val, 0x0, CM_CAMERRX_CTRL_CSI1_CTRLCLKEN_MASK);
- reg_write(ctx->dev->cm, CM_CTRL_CORE_CAMERRX_CONTROL, val);
-}
-
-/*
- * Camera Instance access block
- */
-static struct cc_data *cc_create(struct cal_dev *dev, unsigned int core)
-{
- struct platform_device *pdev = dev->pdev;
- struct cc_data *cc;
-
- cc = devm_kzalloc(&pdev->dev, sizeof(*cc), GFP_KERNEL);
- if (!cc)
- return ERR_PTR(-ENOMEM);
-
- cc->res = platform_get_resource_byname(pdev,
- IORESOURCE_MEM,
- (core == 0) ?
- "cal_rx_core0" :
- "cal_rx_core1");
- cc->base = devm_ioremap_resource(&pdev->dev, cc->res);
- if (IS_ERR(cc->base)) {
- cal_err(dev, "failed to ioremap\n");
- return ERR_CAST(cc->base);
- }
-
- cal_dbg(1, dev, "ioresource %s at %pa - %pa\n",
- cc->res->name, &cc->res->start, &cc->res->end);
-
- return cc;
-}
-
-/*
- * Get Revision and HW info
- */
-static void cal_get_hwinfo(struct cal_dev *dev)
-{
- u32 revision = 0;
- u32 hwinfo = 0;
-
- revision = reg_read(dev, CAL_HL_REVISION);
- cal_dbg(3, dev, "CAL_HL_REVISION = 0x%08x (expecting 0x40000200)\n",
- revision);
-
- hwinfo = reg_read(dev, CAL_HL_HWINFO);
- cal_dbg(3, dev, "CAL_HL_HWINFO = 0x%08x (expecting 0xA3C90469)\n",
- hwinfo);
-}
-
-static inline int cal_runtime_get(struct cal_dev *dev)
-{
- return pm_runtime_get_sync(&dev->pdev->dev);
-}
-
-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);
+ cal_info(cal, "CAL Registers @ 0x%pa:\n", &cal->res->start);
print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 4,
- (__force const void *)dev->base,
- resource_size(dev->res), false);
+ (__force const void *)cal->base,
+ resource_size(cal->res), false);
- if (dev->ctx[0]) {
- cal_info(dev, "CSI2 Core 0 Registers @ %pa:\n",
- &dev->ctx[0]->cc->res->start);
+ for (i = 0; i < ARRAY_SIZE(cal->phy); ++i) {
+ struct cal_camerarx *phy = cal->phy[i];
+
+ if (!phy)
+ continue;
+
+ cal_info(cal, "CSI2 Core %u Registers @ %pa:\n", i,
+ &phy->res->start);
print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 4,
- (__force const void *)dev->ctx[0]->cc->base,
- resource_size(dev->ctx[0]->cc->res),
+ (__force const void *)phy->base,
+ resource_size(phy->res),
false);
}
-
- if (dev->ctx[1]) {
- cal_info(dev, "CSI2 Core 1 Registers @ %pa:\n",
- &dev->ctx[1]->cc->res->start);
- print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 4,
- (__force const void *)dev->ctx[1]->cc->base,
- resource_size(dev->ctx[1]->cc->res),
- false);
- }
-
- cal_info(dev, "CAMERRX_Control Registers @ %pa:\n",
- &dev->cm->res->start);
- print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 4,
- (__force const void *)dev->cm->base,
- resource_size(dev->cm->res), false);
}
-/*
- * Enable the expected IRQ sources
+/* ------------------------------------------------------------------
+ * Context Management
+ * ------------------------------------------------------------------
*/
-static void enable_irqs(struct cal_ctx *ctx)
-{
- /* 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));
- /* 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));
- /* Todo: Add VC_IRQ and CSI2_COMPLEXIO_IRQ handling */
- reg_write(ctx->dev, CAL_CSI2_VC_IRQENABLE(1), 0xFF000000);
-}
-static void disable_irqs(struct cal_ctx *ctx)
+void cal_ctx_csi2_config(struct cal_ctx *ctx)
{
u32 val;
- /* Disable IRQ_WDMA_END 0/1 */
- val = 0;
- set_field(&val, CAL_HL_IRQ_CLEAR, 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));
- 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_init(struct cal_ctx *ctx)
-{
- int i;
- u32 val;
-
- 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);
- 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);
- reg_write(ctx->dev, CAL_CSI2_TIMING(ctx->csi2_port), val);
- ctx_dbg(3, ctx, "CAL_CSI2_TIMING(%d) = 0x%08x\n", ctx->csi2_port,
- reg_read(ctx->dev, CAL_CSI2_TIMING(ctx->csi2_port)));
-
- 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);
- 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);
- 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_ON)
- break;
- usleep_range(1000, 1100);
- }
- ctx_dbg(3, ctx, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x\n", ctx->csi2_port,
- reg_read(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port)));
-
- val = reg_read(ctx->dev, CAL_CTRL);
- set_field(&val, CAL_CTRL_BURSTSIZE_BURST128, CAL_CTRL_BURSTSIZE_MASK);
- set_field(&val, 0xF, CAL_CTRL_TAGCNT_MASK);
- set_field(&val, CAL_CTRL_POSTED_WRITES_NONPOSTED,
- CAL_CTRL_POSTED_WRITES_MASK);
- set_field(&val, 0xFF, CAL_CTRL_MFLAGL_MASK);
- set_field(&val, 0xFF, CAL_CTRL_MFLAGH_MASK);
- reg_write(ctx->dev, CAL_CTRL, val);
- ctx_dbg(3, ctx, "CAL_CTRL = 0x%08x\n", reg_read(ctx->dev, CAL_CTRL));
-}
-
-static void csi2_lane_config(struct cal_ctx *ctx)
-{
- u32 val = reg_read(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port));
- u32 lane_mask = CAL_CSI2_COMPLEXIO_CFG_CLOCK_POSITION_MASK;
- u32 polarity_mask = CAL_CSI2_COMPLEXIO_CFG_CLOCK_POL_MASK;
- struct v4l2_fwnode_bus_mipi_csi2 *mipi_csi2 =
- &ctx->endpoint.bus.mipi_csi2;
- int lane;
-
- set_field(&val, mipi_csi2->clock_lane + 1, lane_mask);
- set_field(&val, mipi_csi2->lane_polarities[0], polarity_mask);
- for (lane = 0; lane < mipi_csi2->num_data_lanes; lane++) {
- /*
- * Every lane are one nibble apart starting with the
- * clock followed by the data lanes so shift masks by 4.
- */
- lane_mask <<= 4;
- polarity_mask <<= 4;
- set_field(&val, mipi_csi2->data_lanes[lane] + 1, lane_mask);
- set_field(&val, mipi_csi2->lane_polarities[lane + 1],
- polarity_mask);
- }
-
- reg_write(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), val);
- ctx_dbg(3, ctx, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x\n",
- ctx->csi2_port, val);
-}
-
-static void csi2_ppi_enable(struct cal_ctx *ctx)
-{
- reg_write_field(ctx->dev, CAL_CSI2_PPI_CTRL(ctx->csi2_port),
- CAL_GEN_ENABLE, 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);
-}
-
-static void csi2_ctx_config(struct cal_ctx *ctx)
-{
- u32 val;
-
- val = reg_read(ctx->dev, CAL_CSI2_CTX0(ctx->csi2_port));
- set_field(&val, ctx->csi2_port, CAL_CSI2_CTX_CPORT_MASK);
+ val = cal_read(ctx->cal, CAL_CSI2_CTX0(ctx->index));
+ cal_set_field(&val, ctx->cport, CAL_CSI2_CTX_CPORT_MASK);
/*
* DT type: MIPI CSI-2 Specs
* 0x1: All - DT filter is disabled
@@ -648,165 +170,132 @@
* 0x2A: RAW8 1 pixel = 1 byte
* 0x1E: YUV422 2 pixels = 4 bytes
*/
- 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, 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);
- reg_write(ctx->dev, CAL_CSI2_CTX0(ctx->csi2_port), val);
- ctx_dbg(3, ctx, "CAL_CSI2_CTX0(%d) = 0x%08x\n", ctx->csi2_port,
- reg_read(ctx->dev, CAL_CSI2_CTX0(ctx->csi2_port)));
+ cal_set_field(&val, 0x1, CAL_CSI2_CTX_DT_MASK);
+ cal_set_field(&val, 0, CAL_CSI2_CTX_VC_MASK);
+ cal_set_field(&val, ctx->v_fmt.fmt.pix.height, CAL_CSI2_CTX_LINES_MASK);
+ cal_set_field(&val, CAL_CSI2_CTX_ATT_PIX, CAL_CSI2_CTX_ATT_MASK);
+ cal_set_field(&val, CAL_CSI2_CTX_PACK_MODE_LINE,
+ CAL_CSI2_CTX_PACK_MODE_MASK);
+ cal_write(ctx->cal, CAL_CSI2_CTX0(ctx->index), val);
+ ctx_dbg(3, ctx, "CAL_CSI2_CTX0(%d) = 0x%08x\n", ctx->index,
+ cal_read(ctx->cal, CAL_CSI2_CTX0(ctx->index)));
}
-static void pix_proc_config(struct cal_ctx *ctx)
+void cal_ctx_pix_proc_config(struct cal_ctx *ctx)
+{
+ u32 val, extract, pack;
+
+ switch (ctx->fmt->bpp) {
+ case 8:
+ extract = CAL_PIX_PROC_EXTRACT_B8;
+ pack = CAL_PIX_PROC_PACK_B8;
+ break;
+ case 10:
+ extract = CAL_PIX_PROC_EXTRACT_B10_MIPI;
+ pack = CAL_PIX_PROC_PACK_B16;
+ break;
+ case 12:
+ extract = CAL_PIX_PROC_EXTRACT_B12_MIPI;
+ pack = CAL_PIX_PROC_PACK_B16;
+ break;
+ case 16:
+ extract = CAL_PIX_PROC_EXTRACT_B16_LE;
+ pack = CAL_PIX_PROC_PACK_B16;
+ break;
+ default:
+ /*
+ * If you see this warning then it means that you added
+ * some new entry in the cal_formats[] array with a different
+ * bit per pixel values then the one supported below.
+ * Either add support for the new bpp value below or adjust
+ * the new entry to use one of the value below.
+ *
+ * Instead of failing here just use 8 bpp as a default.
+ */
+ dev_warn_once(ctx->cal->dev,
+ "%s:%d:%s: bpp:%d unsupported! Overwritten with 8.\n",
+ __FILE__, __LINE__, __func__, ctx->fmt->bpp);
+ extract = CAL_PIX_PROC_EXTRACT_B8;
+ pack = CAL_PIX_PROC_PACK_B8;
+ break;
+ }
+
+ val = cal_read(ctx->cal, CAL_PIX_PROC(ctx->index));
+ cal_set_field(&val, extract, CAL_PIX_PROC_EXTRACT_MASK);
+ cal_set_field(&val, CAL_PIX_PROC_DPCMD_BYPASS, CAL_PIX_PROC_DPCMD_MASK);
+ cal_set_field(&val, CAL_PIX_PROC_DPCME_BYPASS, CAL_PIX_PROC_DPCME_MASK);
+ cal_set_field(&val, pack, CAL_PIX_PROC_PACK_MASK);
+ cal_set_field(&val, ctx->cport, CAL_PIX_PROC_CPORT_MASK);
+ cal_set_field(&val, 1, CAL_PIX_PROC_EN_MASK);
+ cal_write(ctx->cal, CAL_PIX_PROC(ctx->index), val);
+ ctx_dbg(3, ctx, "CAL_PIX_PROC(%d) = 0x%08x\n", ctx->index,
+ cal_read(ctx->cal, CAL_PIX_PROC(ctx->index)));
+}
+
+void cal_ctx_wr_dma_config(struct cal_ctx *ctx, unsigned int width,
+ unsigned int height)
{
u32 val;
- val = reg_read(ctx->dev, CAL_PIX_PROC(ctx->csi2_port));
- set_field(&val, CAL_PIX_PROC_EXTRACT_B8, CAL_PIX_PROC_EXTRACT_MASK);
- set_field(&val, CAL_PIX_PROC_DPCMD_BYPASS, CAL_PIX_PROC_DPCMD_MASK);
- set_field(&val, CAL_PIX_PROC_DPCME_BYPASS, CAL_PIX_PROC_DPCME_MASK);
- set_field(&val, CAL_PIX_PROC_PACK_B8, 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);
- 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)));
-}
-
-static void cal_wr_dma_config(struct cal_ctx *ctx,
- unsigned int width, unsigned int height)
-{
- u32 val;
-
- val = reg_read(ctx->dev, CAL_WR_DMA_CTRL(ctx->csi2_port));
- set_field(&val, ctx->csi2_port, CAL_WR_DMA_CTRL_CPORT_MASK);
- set_field(&val, height, CAL_WR_DMA_CTRL_YSIZE_MASK);
- set_field(&val, CAL_WR_DMA_CTRL_DTAG_PIX_DAT,
- CAL_WR_DMA_CTRL_DTAG_MASK);
- set_field(&val, CAL_WR_DMA_CTRL_MODE_CONST,
- 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);
- 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)));
+ val = cal_read(ctx->cal, CAL_WR_DMA_CTRL(ctx->index));
+ cal_set_field(&val, ctx->cport, CAL_WR_DMA_CTRL_CPORT_MASK);
+ cal_set_field(&val, height, CAL_WR_DMA_CTRL_YSIZE_MASK);
+ cal_set_field(&val, CAL_WR_DMA_CTRL_DTAG_PIX_DAT,
+ CAL_WR_DMA_CTRL_DTAG_MASK);
+ cal_set_field(&val, CAL_WR_DMA_CTRL_MODE_CONST,
+ CAL_WR_DMA_CTRL_MODE_MASK);
+ cal_set_field(&val, CAL_WR_DMA_CTRL_PATTERN_LINEAR,
+ CAL_WR_DMA_CTRL_PATTERN_MASK);
+ cal_set_field(&val, 1, CAL_WR_DMA_CTRL_STALL_RD_MASK);
+ cal_write(ctx->cal, CAL_WR_DMA_CTRL(ctx->index), val);
+ ctx_dbg(3, ctx, "CAL_WR_DMA_CTRL(%d) = 0x%08x\n", ctx->index,
+ cal_read(ctx->cal, CAL_WR_DMA_CTRL(ctx->index)));
/*
* width/16 not sure but giving it a whirl.
* zero does not work right
*/
- reg_write_field(ctx->dev,
- CAL_WR_DMA_OFST(ctx->csi2_port),
+ cal_write_field(ctx->cal,
+ CAL_WR_DMA_OFST(ctx->index),
(width / 16),
CAL_WR_DMA_OFST_MASK);
- ctx_dbg(3, ctx, "CAL_WR_DMA_OFST(%d) = 0x%08x\n", ctx->csi2_port,
- reg_read(ctx->dev, CAL_WR_DMA_OFST(ctx->csi2_port)));
+ ctx_dbg(3, ctx, "CAL_WR_DMA_OFST(%d) = 0x%08x\n", ctx->index,
+ cal_read(ctx->cal, CAL_WR_DMA_OFST(ctx->index)));
- val = reg_read(ctx->dev, CAL_WR_DMA_XSIZE(ctx->csi2_port));
+ val = cal_read(ctx->cal, CAL_WR_DMA_XSIZE(ctx->index));
/* 64 bit word means no skipping */
- set_field(&val, 0, CAL_WR_DMA_XSIZE_XSKIP_MASK);
+ cal_set_field(&val, 0, CAL_WR_DMA_XSIZE_XSKIP_MASK);
/*
* (width*8)/64 this should be size of an entire line
* in 64bit word but 0 means all data until the end
* is detected automagically
*/
- set_field(&val, (width / 8), CAL_WR_DMA_XSIZE_MASK);
- reg_write(ctx->dev, CAL_WR_DMA_XSIZE(ctx->csi2_port), val);
- ctx_dbg(3, ctx, "CAL_WR_DMA_XSIZE(%d) = 0x%08x\n", ctx->csi2_port,
- reg_read(ctx->dev, CAL_WR_DMA_XSIZE(ctx->csi2_port)));
+ cal_set_field(&val, (width / 8), CAL_WR_DMA_XSIZE_MASK);
+ cal_write(ctx->cal, CAL_WR_DMA_XSIZE(ctx->index), val);
+ ctx_dbg(3, ctx, "CAL_WR_DMA_XSIZE(%d) = 0x%08x\n", ctx->index,
+ cal_read(ctx->cal, CAL_WR_DMA_XSIZE(ctx->index)));
+
+ val = cal_read(ctx->cal, CAL_CTRL);
+ cal_set_field(&val, CAL_CTRL_BURSTSIZE_BURST128,
+ CAL_CTRL_BURSTSIZE_MASK);
+ cal_set_field(&val, 0xF, CAL_CTRL_TAGCNT_MASK);
+ cal_set_field(&val, CAL_CTRL_POSTED_WRITES_NONPOSTED,
+ CAL_CTRL_POSTED_WRITES_MASK);
+ cal_set_field(&val, 0xFF, CAL_CTRL_MFLAGL_MASK);
+ cal_set_field(&val, 0xFF, CAL_CTRL_MFLAGH_MASK);
+ cal_write(ctx->cal, CAL_CTRL, val);
+ ctx_dbg(3, ctx, "CAL_CTRL = 0x%08x\n", cal_read(ctx->cal, CAL_CTRL));
}
-static void cal_wr_dma_addr(struct cal_ctx *ctx, unsigned int dmaaddr)
+void cal_ctx_wr_dma_addr(struct cal_ctx *ctx, unsigned int dmaaddr)
{
- reg_write(ctx->dev, CAL_WR_DMA_ADDR(ctx->csi2_port), dmaaddr);
+ cal_write(ctx->cal, CAL_WR_DMA_ADDR(ctx->index), dmaaddr);
}
-/*
- * TCLK values are OK at their reset values
+/* ------------------------------------------------------------------
+ * IRQ Handling
+ * ------------------------------------------------------------------
*/
-#define TCLK_TERM 0
-#define TCLK_MISS 1
-#define TCLK_SETTLE 14
-#define THS_SETTLE 15
-
-static void csi2_phy_config(struct cal_ctx *ctx)
-{
- unsigned int reg0, reg1;
- unsigned int ths_term, ths_settle;
- unsigned int ddrclkperiod_us;
-
- /*
- * THS_TERM: Programmed value = floor(20 ns/DDRClk period) - 2.
- */
- ddrclkperiod_us = ctx->external_rate / 2000000;
- ddrclkperiod_us = 1000000 / ddrclkperiod_us;
- ctx_dbg(1, ctx, "ddrclkperiod_us: %d\n", ddrclkperiod_us);
-
- ths_term = 20000 / ddrclkperiod_us;
- ths_term = (ths_term >= 2) ? ths_term - 2 : ths_term;
- ctx_dbg(1, ctx, "ths_term: %d (0x%02x)\n", ths_term, ths_term);
-
- /*
- * THS_SETTLE: Programmed value = floor(176.3 ns/CtrlClk period) - 1.
- * Since CtrlClk is fixed at 96Mhz then we get
- * ths_settle = floor(176.3 / 10.416) - 1 = 15
- * If we ever switch to a dynamic clock then this code might be useful
- *
- * unsigned int ctrlclkperiod_us;
- * ctrlclkperiod_us = 96000000 / 1000000;
- * ctrlclkperiod_us = 1000000 / ctrlclkperiod_us;
- * ctx_dbg(1, ctx, "ctrlclkperiod_us: %d\n", ctrlclkperiod_us);
-
- * ths_settle = 176300 / ctrlclkperiod_us;
- * ths_settle = (ths_settle > 1) ? ths_settle - 1 : ths_settle;
- */
-
- ths_settle = THS_SETTLE;
- ctx_dbg(1, ctx, "ths_settle: %d (0x%02x)\n", ths_settle, ths_settle);
-
- reg0 = reg_read(ctx->cc, CAL_CSI2_PHY_REG0);
- set_field(®0, CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_DISABLE,
- CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_MASK);
- set_field(®0, ths_term, CAL_CSI2_PHY_REG0_THS_TERM_MASK);
- set_field(®0, ths_settle, CAL_CSI2_PHY_REG0_THS_SETTLE_MASK);
-
- ctx_dbg(1, ctx, "CSI2_%d_REG0 = 0x%08x\n", (ctx->csi2_port - 1), reg0);
- reg_write(ctx->cc, CAL_CSI2_PHY_REG0, reg0);
-
- reg1 = reg_read(ctx->cc, CAL_CSI2_PHY_REG1);
- set_field(®1, TCLK_TERM, CAL_CSI2_PHY_REG1_TCLK_TERM_MASK);
- set_field(®1, 0xb8, CAL_CSI2_PHY_REG1_DPHY_HS_SYNC_PATTERN_MASK);
- set_field(®1, TCLK_MISS, CAL_CSI2_PHY_REG1_CTRLCLK_DIV_FACTOR_MASK);
- set_field(®1, TCLK_SETTLE, CAL_CSI2_PHY_REG1_TCLK_SETTLE_MASK);
-
- ctx_dbg(1, ctx, "CSI2_%d_REG1 = 0x%08x\n", (ctx->csi2_port - 1), reg1);
- reg_write(ctx->cc, CAL_CSI2_PHY_REG1, reg1);
-}
-
-static int cal_get_external_info(struct cal_ctx *ctx)
-{
- struct v4l2_ctrl *ctrl;
-
- if (!ctx->sensor)
- return -ENODEV;
-
- ctrl = v4l2_ctrl_find(ctx->sensor->ctrl_handler, V4L2_CID_PIXEL_RATE);
- if (!ctrl) {
- ctx_err(ctx, "no pixel rate control in subdev: %s\n",
- ctx->sensor->name);
- return -EPIPE;
- }
-
- ctx->external_rate = v4l2_ctrl_g_ctrl_int64(ctrl);
- ctx_dbg(3, ctx, "sensor Pixel Rate: %d\n", ctx->external_rate);
-
- return 0;
-}
static inline void cal_schedule_next_buffer(struct cal_ctx *ctx)
{
@@ -819,7 +308,7 @@
list_del(&buf->list);
addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0);
- cal_wr_dma_addr(ctx, addr);
+ cal_ctx_wr_dma_addr(ctx, addr);
}
static inline void cal_process_buffer_complete(struct cal_ctx *ctx)
@@ -832,1115 +321,599 @@
ctx->cur_frm = ctx->next_frm;
}
-#define isvcirqset(irq, vc, ff) (irq & \
- (CAL_CSI2_VC_IRQENABLE_ ##ff ##_IRQ_##vc ##_MASK))
-
-#define isportirqset(irq, port) (irq & CAL_HL_IRQ_MASK(port))
-
static irqreturn_t cal_irq(int irq_cal, void *data)
{
- struct cal_dev *dev = (struct cal_dev *)data;
+ struct cal_dev *cal = data;
struct cal_ctx *ctx;
struct cal_dmaqueue *dma_q;
- u32 irqst2, irqst3;
+ u32 status;
+
+ status = cal_read(cal, CAL_HL_IRQSTATUS(0));
+ if (status) {
+ unsigned int i;
+
+ cal_write(cal, CAL_HL_IRQSTATUS(0), status);
+
+ if (status & CAL_HL_IRQ_OCPO_ERR_MASK)
+ dev_err_ratelimited(cal->dev, "OCPO ERROR\n");
+
+ for (i = 0; i < CAL_NUM_CSI2_PORTS; ++i) {
+ if (status & CAL_HL_IRQ_CIO_MASK(i)) {
+ u32 cio_stat = cal_read(cal,
+ CAL_CSI2_COMPLEXIO_IRQSTATUS(i));
+
+ dev_err_ratelimited(cal->dev,
+ "CIO%u error: %#08x\n", i, cio_stat);
+
+ cal_write(cal, CAL_CSI2_COMPLEXIO_IRQSTATUS(i),
+ cio_stat);
+ }
+ }
+ }
/* Check which DMA just finished */
- irqst2 = reg_read(dev, CAL_HL_IRQSTATUS(2));
- if (irqst2) {
+ status = cal_read(cal, CAL_HL_IRQSTATUS(1));
+ if (status) {
+ unsigned int i;
+
/* Clear Interrupt status */
- reg_write(dev, CAL_HL_IRQSTATUS(2), irqst2);
+ cal_write(cal, CAL_HL_IRQSTATUS(1), status);
- /* Need to check both port */
- if (isportirqset(irqst2, 1)) {
- ctx = dev->ctx[0];
+ for (i = 0; i < ARRAY_SIZE(cal->ctx); ++i) {
+ if (status & CAL_HL_IRQ_MASK(i)) {
+ ctx = cal->ctx[i];
- 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) {
+ status = cal_read(cal, CAL_HL_IRQSTATUS(2));
+ if (status) {
+ unsigned int i;
+
/* Clear Interrupt status */
- reg_write(dev, CAL_HL_IRQSTATUS(3), irqst3);
+ cal_write(cal, CAL_HL_IRQSTATUS(2), status);
- /* Need to check both port */
- if (isportirqset(irqst3, 1)) {
- ctx = dev->ctx[0];
- dma_q = &ctx->vidq;
+ for (i = 0; i < ARRAY_SIZE(cal->ctx); ++i) {
+ if (status & CAL_HL_IRQ_MASK(i)) {
+ ctx = cal->ctx[i];
+ 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);
+ 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);
+ }
}
}
return IRQ_HANDLED;
}
-/*
- * video ioctls
- */
-static int cal_querycap(struct file *file, void *priv,
- struct v4l2_capability *cap)
-{
- struct cal_ctx *ctx = video_drvdata(file);
-
- strscpy(cap->driver, CAL_MODULE_NAME, sizeof(cap->driver));
- strscpy(cap->card, CAL_MODULE_NAME, sizeof(cap->card));
-
- snprintf(cap->bus_info, sizeof(cap->bus_info),
- "platform:%s", ctx->v4l2_dev.name);
- return 0;
-}
-
-static int cal_enum_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_fmtdesc *f)
-{
- struct cal_ctx *ctx = video_drvdata(file);
- const struct cal_fmt *fmt = NULL;
-
- if (f->index >= ctx->num_active_fmt)
- return -EINVAL;
-
- fmt = ctx->active_fmt[f->index];
-
- f->pixelformat = fmt->fourcc;
- f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- return 0;
-}
-
-static int __subdev_get_format(struct cal_ctx *ctx,
- struct v4l2_mbus_framefmt *fmt)
-{
- struct v4l2_subdev_format sd_fmt;
- struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format;
- int ret;
-
- sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
- sd_fmt.pad = 0;
-
- ret = v4l2_subdev_call(ctx->sensor, pad, get_fmt, NULL, &sd_fmt);
- if (ret)
- return ret;
-
- *fmt = *mbus_fmt;
-
- ctx_dbg(1, ctx, "%s %dx%d code:%04X\n", __func__,
- fmt->width, fmt->height, fmt->code);
-
- return 0;
-}
-
-static int __subdev_set_format(struct cal_ctx *ctx,
- struct v4l2_mbus_framefmt *fmt)
-{
- struct v4l2_subdev_format sd_fmt;
- struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format;
- int ret;
-
- sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
- sd_fmt.pad = 0;
- *mbus_fmt = *fmt;
-
- ret = v4l2_subdev_call(ctx->sensor, pad, set_fmt, NULL, &sd_fmt);
- if (ret)
- return ret;
-
- ctx_dbg(1, ctx, "%s %dx%d code:%04X\n", __func__,
- fmt->width, fmt->height, fmt->code);
-
- return 0;
-}
-
-static int cal_calc_format_size(struct cal_ctx *ctx,
- const struct cal_fmt *fmt,
- struct v4l2_format *f)
-{
- if (!fmt) {
- ctx_dbg(3, ctx, "No cal_fmt provided!\n");
- return -EINVAL;
- }
-
- v4l_bound_align_image(&f->fmt.pix.width, 48, MAX_WIDTH, 2,
- &f->fmt.pix.height, 32, MAX_HEIGHT, 0, 0);
- f->fmt.pix.bytesperline = bytes_per_line(f->fmt.pix.width,
- fmt->depth >> 3);
- f->fmt.pix.sizeimage = f->fmt.pix.height *
- f->fmt.pix.bytesperline;
-
- ctx_dbg(3, ctx, "%s: fourcc: %s size: %dx%d bpl:%d img_size:%d\n",
- __func__, fourcc_to_str(f->fmt.pix.pixelformat),
- f->fmt.pix.width, f->fmt.pix.height,
- f->fmt.pix.bytesperline, f->fmt.pix.sizeimage);
-
- return 0;
-}
-
-static int cal_g_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct cal_ctx *ctx = video_drvdata(file);
-
- *f = ctx->v_fmt;
-
- return 0;
-}
-
-static int cal_try_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct cal_ctx *ctx = video_drvdata(file);
- const struct cal_fmt *fmt;
- struct v4l2_subdev_frame_size_enum fse;
- int ret, found;
-
- fmt = find_format_by_pix(ctx, f->fmt.pix.pixelformat);
- if (!fmt) {
- ctx_dbg(3, ctx, "Fourcc format (0x%08x) not found.\n",
- f->fmt.pix.pixelformat);
-
- /* Just get the first one enumerated */
- fmt = ctx->active_fmt[0];
- f->fmt.pix.pixelformat = fmt->fourcc;
- }
-
- f->fmt.pix.field = ctx->v_fmt.fmt.pix.field;
-
- /* check for/find a valid width/height */
- ret = 0;
- found = false;
- fse.pad = 0;
- fse.code = fmt->code;
- fse.which = V4L2_SUBDEV_FORMAT_ACTIVE;
- for (fse.index = 0; ; fse.index++) {
- ret = v4l2_subdev_call(ctx->sensor, pad, enum_frame_size,
- NULL, &fse);
- if (ret)
- break;
-
- if ((f->fmt.pix.width == fse.max_width) &&
- (f->fmt.pix.height == fse.max_height)) {
- found = true;
- break;
- } else if ((f->fmt.pix.width >= fse.min_width) &&
- (f->fmt.pix.width <= fse.max_width) &&
- (f->fmt.pix.height >= fse.min_height) &&
- (f->fmt.pix.height <= fse.max_height)) {
- found = true;
- break;
- }
- }
-
- if (!found) {
- /* use existing values as default */
- f->fmt.pix.width = ctx->v_fmt.fmt.pix.width;
- f->fmt.pix.height = ctx->v_fmt.fmt.pix.height;
- }
-
- /*
- * Use current colorspace for now, it will get
- * updated properly during s_fmt
- */
- f->fmt.pix.colorspace = ctx->v_fmt.fmt.pix.colorspace;
- return cal_calc_format_size(ctx, fmt, f);
-}
-
-static int cal_s_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct cal_ctx *ctx = video_drvdata(file);
- struct vb2_queue *q = &ctx->vb_vidq;
- const struct cal_fmt *fmt;
- struct v4l2_mbus_framefmt mbus_fmt;
- int ret;
-
- if (vb2_is_busy(q)) {
- ctx_dbg(3, ctx, "%s device busy\n", __func__);
- return -EBUSY;
- }
-
- ret = cal_try_fmt_vid_cap(file, priv, f);
- if (ret < 0)
- return ret;
-
- fmt = find_format_by_pix(ctx, f->fmt.pix.pixelformat);
-
- v4l2_fill_mbus_format(&mbus_fmt, &f->fmt.pix, fmt->code);
-
- ret = __subdev_set_format(ctx, &mbus_fmt);
- if (ret)
- return ret;
-
- /* Just double check nothing has gone wrong */
- if (mbus_fmt.code != fmt->code) {
- ctx_dbg(3, ctx,
- "%s subdev changed format on us, this should not happen\n",
- __func__);
- return -EINVAL;
- }
-
- v4l2_fill_pix_format(&ctx->v_fmt.fmt.pix, &mbus_fmt);
- ctx->v_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- ctx->v_fmt.fmt.pix.pixelformat = fmt->fourcc;
- cal_calc_format_size(ctx, fmt, &ctx->v_fmt);
- ctx->fmt = fmt;
- ctx->m_fmt = mbus_fmt;
- *f = ctx->v_fmt;
-
- return 0;
-}
-
-static int cal_enum_framesizes(struct file *file, void *fh,
- struct v4l2_frmsizeenum *fsize)
-{
- struct cal_ctx *ctx = video_drvdata(file);
- const struct cal_fmt *fmt;
- struct v4l2_subdev_frame_size_enum fse;
- int ret;
-
- /* check for valid format */
- fmt = find_format_by_pix(ctx, fsize->pixel_format);
- if (!fmt) {
- ctx_dbg(3, ctx, "Invalid pixel code: %x\n",
- fsize->pixel_format);
- return -EINVAL;
- }
-
- fse.index = fsize->index;
- fse.pad = 0;
- fse.code = fmt->code;
-
- ret = v4l2_subdev_call(ctx->sensor, pad, enum_frame_size, NULL, &fse);
- if (ret)
- return ret;
-
- ctx_dbg(1, ctx, "%s: index: %d code: %x W:[%d,%d] H:[%d,%d]\n",
- __func__, fse.index, fse.code, fse.min_width, fse.max_width,
- fse.min_height, fse.max_height);
-
- fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
- fsize->discrete.width = fse.max_width;
- fsize->discrete.height = fse.max_height;
-
- return 0;
-}
-
-static int cal_enum_input(struct file *file, void *priv,
- struct v4l2_input *inp)
-{
- if (inp->index >= CAL_NUM_INPUT)
- return -EINVAL;
-
- inp->type = V4L2_INPUT_TYPE_CAMERA;
- sprintf(inp->name, "Camera %u", inp->index);
- return 0;
-}
-
-static int cal_g_input(struct file *file, void *priv, unsigned int *i)
-{
- struct cal_ctx *ctx = video_drvdata(file);
-
- *i = ctx->input;
- return 0;
-}
-
-static int cal_s_input(struct file *file, void *priv, unsigned int i)
-{
- struct cal_ctx *ctx = video_drvdata(file);
-
- if (i >= CAL_NUM_INPUT)
- return -EINVAL;
-
- ctx->input = i;
- return 0;
-}
-
-/* timeperframe is arbitrary and continuous */
-static int cal_enum_frameintervals(struct file *file, void *priv,
- struct v4l2_frmivalenum *fival)
-{
- struct cal_ctx *ctx = video_drvdata(file);
- const struct cal_fmt *fmt;
- struct v4l2_subdev_frame_interval_enum fie = {
- .index = fival->index,
- .width = fival->width,
- .height = fival->height,
- .which = V4L2_SUBDEV_FORMAT_ACTIVE,
- };
- int ret;
-
- fmt = find_format_by_pix(ctx, fival->pixel_format);
- if (!fmt)
- return -EINVAL;
-
- fie.code = fmt->code;
- ret = v4l2_subdev_call(ctx->sensor, pad, enum_frame_interval,
- NULL, &fie);
- if (ret)
- return ret;
- fival->type = V4L2_FRMIVAL_TYPE_DISCRETE;
- fival->discrete = fie.interval;
-
- return 0;
-}
-
-/*
- * Videobuf operations
- */
-static int cal_queue_setup(struct vb2_queue *vq,
- unsigned int *nbuffers, unsigned int *nplanes,
- unsigned int sizes[], struct device *alloc_devs[])
-{
- struct cal_ctx *ctx = vb2_get_drv_priv(vq);
- unsigned size = ctx->v_fmt.fmt.pix.sizeimage;
-
- if (vq->num_buffers + *nbuffers < 3)
- *nbuffers = 3 - vq->num_buffers;
-
- if (*nplanes) {
- if (sizes[0] < size)
- return -EINVAL;
- size = sizes[0];
- }
-
- *nplanes = 1;
- sizes[0] = size;
-
- ctx_dbg(3, ctx, "nbuffers=%d, size=%d\n", *nbuffers, sizes[0]);
-
- return 0;
-}
-
-static int cal_buffer_prepare(struct vb2_buffer *vb)
-{
- struct cal_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
- struct cal_buffer *buf = container_of(vb, struct cal_buffer,
- vb.vb2_buf);
- unsigned long size;
-
- if (WARN_ON(!ctx->fmt))
- return -EINVAL;
-
- size = ctx->v_fmt.fmt.pix.sizeimage;
- if (vb2_plane_size(vb, 0) < size) {
- ctx_err(ctx,
- "data will not fit into plane (%lu < %lu)\n",
- vb2_plane_size(vb, 0), size);
- return -EINVAL;
- }
-
- vb2_set_plane_payload(&buf->vb.vb2_buf, 0, size);
- return 0;
-}
-
-static void cal_buffer_queue(struct vb2_buffer *vb)
-{
- struct cal_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
- struct cal_buffer *buf = container_of(vb, struct cal_buffer,
- vb.vb2_buf);
- struct cal_dmaqueue *vidq = &ctx->vidq;
- unsigned long flags = 0;
-
- /* recheck locking */
- spin_lock_irqsave(&ctx->slock, flags);
- list_add_tail(&buf->list, &vidq->active);
- spin_unlock_irqrestore(&ctx->slock, flags);
-}
-
-static int cal_start_streaming(struct vb2_queue *vq, unsigned int count)
-{
- struct cal_ctx *ctx = vb2_get_drv_priv(vq);
- struct cal_dmaqueue *dma_q = &ctx->vidq;
- struct cal_buffer *buf, *tmp;
- unsigned long addr = 0;
- unsigned long flags;
- int ret;
-
- spin_lock_irqsave(&ctx->slock, flags);
- if (list_empty(&dma_q->active)) {
- spin_unlock_irqrestore(&ctx->slock, flags);
- ctx_dbg(3, ctx, "buffer queue is empty\n");
- return -EIO;
- }
-
- buf = list_entry(dma_q->active.next, struct cal_buffer, list);
- ctx->cur_frm = buf;
- ctx->next_frm = buf;
- list_del(&buf->list);
- spin_unlock_irqrestore(&ctx->slock, flags);
-
- addr = vb2_dma_contig_plane_dma_addr(&ctx->cur_frm->vb.vb2_buf, 0);
- ctx->sequence = 0;
-
- ret = cal_get_external_info(ctx);
- if (ret < 0)
- goto err;
-
- cal_runtime_get(ctx->dev);
-
- enable_irqs(ctx);
- camerarx_phy_enable(ctx);
- csi2_init(ctx);
- csi2_phy_config(ctx);
- csi2_lane_config(ctx);
- csi2_ctx_config(ctx);
- pix_proc_config(ctx);
- cal_wr_dma_config(ctx, ctx->v_fmt.fmt.pix.bytesperline,
- ctx->v_fmt.fmt.pix.height);
- cal_wr_dma_addr(ctx, addr);
- csi2_ppi_enable(ctx);
-
- ret = v4l2_subdev_call(ctx->sensor, video, s_stream, 1);
- if (ret) {
- ctx_err(ctx, "stream on failed in subdev\n");
- cal_runtime_put(ctx->dev);
- goto err;
- }
-
- if (debug >= 4)
- cal_quickdump_regs(ctx->dev);
-
- return 0;
-
-err:
- list_for_each_entry_safe(buf, tmp, &dma_q->active, list) {
- list_del(&buf->list);
- vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED);
- }
- return ret;
-}
-
-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 flags;
-
- if (v4l2_subdev_call(ctx->sensor, video, s_stream, 0))
- ctx_err(ctx, "stream off failed in subdev\n");
-
- csi2_ppi_disable(ctx);
- disable_irqs(ctx);
-
- /* Release all active buffers */
- spin_lock_irqsave(&ctx->slock, flags);
- list_for_each_entry_safe(buf, tmp, &dma_q->active, list) {
- list_del(&buf->list);
- vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
- }
-
- if (ctx->cur_frm == ctx->next_frm) {
- vb2_buffer_done(&ctx->cur_frm->vb.vb2_buf, VB2_BUF_STATE_ERROR);
- } else {
- vb2_buffer_done(&ctx->cur_frm->vb.vb2_buf, VB2_BUF_STATE_ERROR);
- vb2_buffer_done(&ctx->next_frm->vb.vb2_buf,
- VB2_BUF_STATE_ERROR);
- }
- ctx->cur_frm = NULL;
- ctx->next_frm = NULL;
- spin_unlock_irqrestore(&ctx->slock, flags);
-
- cal_runtime_put(ctx->dev);
-}
-
-static const struct vb2_ops cal_video_qops = {
- .queue_setup = cal_queue_setup,
- .buf_prepare = cal_buffer_prepare,
- .buf_queue = cal_buffer_queue,
- .start_streaming = cal_start_streaming,
- .stop_streaming = cal_stop_streaming,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
-};
-
-static const struct v4l2_file_operations cal_fops = {
- .owner = THIS_MODULE,
- .open = v4l2_fh_open,
- .release = vb2_fop_release,
- .read = vb2_fop_read,
- .poll = vb2_fop_poll,
- .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */
- .mmap = vb2_fop_mmap,
-};
-
-static const struct v4l2_ioctl_ops cal_ioctl_ops = {
- .vidioc_querycap = cal_querycap,
- .vidioc_enum_fmt_vid_cap = cal_enum_fmt_vid_cap,
- .vidioc_g_fmt_vid_cap = cal_g_fmt_vid_cap,
- .vidioc_try_fmt_vid_cap = cal_try_fmt_vid_cap,
- .vidioc_s_fmt_vid_cap = cal_s_fmt_vid_cap,
- .vidioc_enum_framesizes = cal_enum_framesizes,
- .vidioc_reqbufs = vb2_ioctl_reqbufs,
- .vidioc_create_bufs = vb2_ioctl_create_bufs,
- .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
- .vidioc_querybuf = vb2_ioctl_querybuf,
- .vidioc_qbuf = vb2_ioctl_qbuf,
- .vidioc_dqbuf = vb2_ioctl_dqbuf,
- .vidioc_enum_input = cal_enum_input,
- .vidioc_g_input = cal_g_input,
- .vidioc_s_input = cal_s_input,
- .vidioc_enum_frameintervals = cal_enum_frameintervals,
- .vidioc_streamon = vb2_ioctl_streamon,
- .vidioc_streamoff = vb2_ioctl_streamoff,
- .vidioc_log_status = v4l2_ctrl_log_status,
- .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
- .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
-};
-
-static const struct video_device cal_videodev = {
- .name = CAL_MODULE_NAME,
- .fops = &cal_fops,
- .ioctl_ops = &cal_ioctl_ops,
- .minor = -1,
- .release = video_device_release_empty,
- .device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING |
- V4L2_CAP_READWRITE,
-};
-
-/* -----------------------------------------------------------------
- * Initialization and module stuff
+/* ------------------------------------------------------------------
+ * Asynchronous V4L2 subdev binding
* ------------------------------------------------------------------
*/
-static int cal_complete_ctx(struct cal_ctx *ctx);
-static int cal_async_bound(struct v4l2_async_notifier *notifier,
- struct v4l2_subdev *subdev,
- struct v4l2_async_subdev *asd)
+struct cal_v4l2_async_subdev {
+ struct v4l2_async_subdev asd; /* Must be first */
+ struct cal_camerarx *phy;
+};
+
+static inline struct cal_v4l2_async_subdev *
+to_cal_asd(struct v4l2_async_subdev *asd)
{
- struct cal_ctx *ctx = notifier_to_ctx(notifier);
- struct v4l2_subdev_mbus_code_enum mbus_code;
- int ret = 0;
- int i, j, k;
+ return container_of(asd, struct cal_v4l2_async_subdev, asd);
+}
- if (ctx->sensor) {
- ctx_info(ctx, "Rejecting subdev %s (Already set!!)",
+static int cal_async_notifier_bound(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *subdev,
+ struct v4l2_async_subdev *asd)
+{
+ struct cal_camerarx *phy = to_cal_asd(asd)->phy;
+
+ if (phy->sensor) {
+ phy_info(phy, "Rejecting subdev %s (Already set!!)",
subdev->name);
return 0;
}
- ctx->sensor = subdev;
- ctx_dbg(1, ctx, "Using sensor %s for capture\n", subdev->name);
-
- /* Enumerate sub device formats and enable all matching local formats */
- ctx->num_active_fmt = 0;
- for (j = 0, i = 0; ret != -EINVAL; ++j) {
- struct cal_fmt *fmt;
-
- memset(&mbus_code, 0, sizeof(mbus_code));
- mbus_code.index = j;
- ret = v4l2_subdev_call(subdev, pad, enum_mbus_code,
- NULL, &mbus_code);
- if (ret)
- continue;
-
- ctx_dbg(2, ctx,
- "subdev %s: code: %04x idx: %d\n",
- subdev->name, mbus_code.code, j);
-
- for (k = 0; k < ARRAY_SIZE(cal_formats); k++) {
- fmt = &cal_formats[k];
-
- if (mbus_code.code == fmt->code) {
- ctx->active_fmt[i] = fmt;
- ctx_dbg(2, ctx,
- "matched fourcc: %s: code: %04x idx: %d\n",
- fourcc_to_str(fmt->fourcc),
- fmt->code, i);
- ctx->num_active_fmt = ++i;
- }
- }
- }
-
- if (i == 0) {
- ctx_err(ctx, "No suitable format reported by subdev %s\n",
- subdev->name);
- return -EINVAL;
- }
-
- cal_complete_ctx(ctx);
+ phy->sensor = subdev;
+ phy_dbg(1, phy, "Using sensor %s for capture\n", subdev->name);
return 0;
}
-static int cal_async_complete(struct v4l2_async_notifier *notifier)
+static int cal_async_notifier_complete(struct v4l2_async_notifier *notifier)
{
- struct cal_ctx *ctx = notifier_to_ctx(notifier);
- const struct cal_fmt *fmt;
- struct v4l2_mbus_framefmt mbus_fmt;
- int ret;
+ struct cal_dev *cal = container_of(notifier, struct cal_dev, notifier);
+ unsigned int i;
- ret = __subdev_get_format(ctx, &mbus_fmt);
- if (ret)
- return ret;
-
- fmt = find_format_by_code(ctx, mbus_fmt.code);
- if (!fmt) {
- ctx_dbg(3, ctx, "mbus code format (0x%08x) not found.\n",
- mbus_fmt.code);
- return -EINVAL;
+ for (i = 0; i < ARRAY_SIZE(cal->ctx); ++i) {
+ if (cal->ctx[i])
+ cal_ctx_v4l2_register(cal->ctx[i]);
}
- /* Save current subdev format */
- v4l2_fill_pix_format(&ctx->v_fmt.fmt.pix, &mbus_fmt);
- ctx->v_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- ctx->v_fmt.fmt.pix.pixelformat = fmt->fourcc;
- cal_calc_format_size(ctx, fmt, &ctx->v_fmt);
- ctx->fmt = fmt;
- ctx->m_fmt = mbus_fmt;
-
return 0;
}
-static const struct v4l2_async_notifier_operations cal_async_ops = {
- .bound = cal_async_bound,
- .complete = cal_async_complete,
+static const struct v4l2_async_notifier_operations cal_async_notifier_ops = {
+ .bound = cal_async_notifier_bound,
+ .complete = cal_async_notifier_complete,
};
-static int cal_complete_ctx(struct cal_ctx *ctx)
+static int cal_async_notifier_register(struct cal_dev *cal)
{
- struct video_device *vfd;
- struct vb2_queue *q;
+ unsigned int i;
int ret;
- ctx->timeperframe = tpf_default;
- ctx->external_rate = 192000000;
+ v4l2_async_notifier_init(&cal->notifier);
+ cal->notifier.ops = &cal_async_notifier_ops;
- /* initialize locks */
- spin_lock_init(&ctx->slock);
- mutex_init(&ctx->mutex);
+ for (i = 0; i < ARRAY_SIZE(cal->phy); ++i) {
+ struct cal_camerarx *phy = cal->phy[i];
+ struct cal_v4l2_async_subdev *casd;
+ struct v4l2_async_subdev *asd;
+ struct fwnode_handle *fwnode;
- /* initialize queue */
- q = &ctx->vb_vidq;
- q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ;
- q->drv_priv = ctx;
- q->buf_struct_size = sizeof(struct cal_buffer);
- q->ops = &cal_video_qops;
- q->mem_ops = &vb2_dma_contig_memops;
- q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
- q->lock = &ctx->mutex;
- q->min_buffers_needed = 3;
- q->dev = ctx->v4l2_dev.dev;
+ if (!phy || !phy->sensor_node)
+ continue;
- ret = vb2_queue_init(q);
- if (ret)
- return ret;
+ fwnode = of_fwnode_handle(phy->sensor_node);
+ asd = v4l2_async_notifier_add_fwnode_subdev(&cal->notifier,
+ fwnode,
+ sizeof(*casd));
+ if (IS_ERR(asd)) {
+ phy_err(phy, "Failed to add subdev to notifier\n");
+ ret = PTR_ERR(asd);
+ goto error;
+ }
- /* init video dma queues */
- INIT_LIST_HEAD(&ctx->vidq.active);
+ casd = to_cal_asd(asd);
+ casd->phy = phy;
+ }
- vfd = &ctx->vdev;
- *vfd = cal_videodev;
- vfd->v4l2_dev = &ctx->v4l2_dev;
- vfd->queue = q;
-
- /*
- * Provide a mutex to v4l2 core. It will be used to protect
- * all fops and v4l2 ioctls.
- */
- vfd->lock = &ctx->mutex;
- video_set_drvdata(vfd, ctx);
-
- ret = video_register_device(vfd, VFL_TYPE_GRABBER, video_nr);
- if (ret < 0)
- return ret;
-
- v4l2_info(&ctx->v4l2_dev, "V4L2 device registered as %s\n",
- video_device_node_name(vfd));
+ ret = v4l2_async_notifier_register(&cal->v4l2_dev, &cal->notifier);
+ if (ret) {
+ cal_err(cal, "Error registering async notifier\n");
+ goto error;
+ }
return 0;
-}
-static struct device_node *
-of_get_next_port(const struct device_node *parent,
- struct device_node *prev)
-{
- struct device_node *port = NULL;
-
- if (!parent)
- return NULL;
-
- if (!prev) {
- struct device_node *ports;
- /*
- * It's the first call, we have to find a port subnode
- * within this node or within an optional 'ports' node.
- */
- ports = of_get_child_by_name(parent, "ports");
- if (ports)
- parent = ports;
-
- port = of_get_child_by_name(parent, "port");
-
- /* release the 'ports' node */
- of_node_put(ports);
- } else {
- struct device_node *ports;
-
- ports = of_get_parent(prev);
- if (!ports)
- return NULL;
-
- do {
- port = of_get_next_child(ports, prev);
- if (!port) {
- of_node_put(ports);
- return NULL;
- }
- prev = port;
- } while (!of_node_name_eq(port, "port"));
- of_node_put(ports);
- }
-
- return port;
-}
-
-static struct device_node *
-of_get_next_endpoint(const struct device_node *parent,
- struct device_node *prev)
-{
- struct device_node *ep = NULL;
-
- if (!parent)
- return NULL;
-
- do {
- ep = of_get_next_child(parent, prev);
- if (!ep)
- return NULL;
- prev = ep;
- } while (!of_node_name_eq(ep, "endpoint"));
-
- return ep;
-}
-
-static int of_cal_create_instance(struct cal_ctx *ctx, int inst)
-{
- struct platform_device *pdev = ctx->dev->pdev;
- struct device_node *ep_node, *port, *sensor_node, *parent;
- struct v4l2_fwnode_endpoint *endpoint;
- struct v4l2_async_subdev *asd;
- u32 regval = 0;
- int ret, index, found_port = 0, lane;
-
- parent = pdev->dev.of_node;
-
- endpoint = &ctx->endpoint;
-
- ep_node = NULL;
- port = NULL;
- sensor_node = NULL;
- ret = -EINVAL;
-
- ctx_dbg(3, ctx, "Scanning Port node for csi2 port: %d\n", inst);
- for (index = 0; index < CAL_NUM_CSI2_PORTS; index++) {
- port = of_get_next_port(parent, port);
- if (!port) {
- ctx_dbg(1, ctx, "No port node found for csi2 port:%d\n",
- index);
- goto cleanup_exit;
- }
-
- /* Match the slice number with <REG> */
- of_property_read_u32(port, "reg", ®val);
- ctx_dbg(3, ctx, "port:%d inst:%d <reg>:%d\n",
- index, inst, regval);
- if ((regval == inst) && (index == inst)) {
- found_port = 1;
- break;
- }
- }
-
- if (!found_port) {
- ctx_dbg(1, ctx, "No port node matches csi2 port:%d\n",
- inst);
- goto cleanup_exit;
- }
-
- ctx_dbg(3, ctx, "Scanning sub-device for csi2 port: %d\n",
- inst);
-
- ep_node = of_get_next_endpoint(port, ep_node);
- if (!ep_node) {
- ctx_dbg(3, ctx, "can't get next endpoint\n");
- goto cleanup_exit;
- }
-
- sensor_node = of_graph_get_remote_port_parent(ep_node);
- if (!sensor_node) {
- ctx_dbg(3, ctx, "can't get remote parent\n");
- goto cleanup_exit;
- }
-
- v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep_node), endpoint);
-
- if (endpoint->bus_type != V4L2_MBUS_CSI2_DPHY) {
- ctx_err(ctx, "Port:%d sub-device %pOFn is not a CSI2 device\n",
- inst, sensor_node);
- goto cleanup_exit;
- }
-
- /* Store Virtual Channel number */
- ctx->virtual_channel = endpoint->base.id;
-
- ctx_dbg(3, ctx, "Port:%d v4l2-endpoint: CSI2\n", inst);
- ctx_dbg(3, ctx, "Virtual Channel=%d\n", ctx->virtual_channel);
- ctx_dbg(3, ctx, "flags=0x%08x\n", endpoint->bus.mipi_csi2.flags);
- ctx_dbg(3, ctx, "clock_lane=%d\n", endpoint->bus.mipi_csi2.clock_lane);
- ctx_dbg(3, ctx, "num_data_lanes=%d\n",
- endpoint->bus.mipi_csi2.num_data_lanes);
- ctx_dbg(3, ctx, "data_lanes= <\n");
- for (lane = 0; lane < endpoint->bus.mipi_csi2.num_data_lanes; lane++)
- ctx_dbg(3, ctx, "\t%d\n",
- endpoint->bus.mipi_csi2.data_lanes[lane]);
- ctx_dbg(3, ctx, "\t>\n");
-
- ctx_dbg(1, ctx, "Port: %d found sub-device %pOFn\n",
- inst, sensor_node);
-
- v4l2_async_notifier_init(&ctx->notifier);
-
- asd = kzalloc(sizeof(*asd), GFP_KERNEL);
- if (!asd)
- goto cleanup_exit;
-
- asd->match_type = V4L2_ASYNC_MATCH_FWNODE;
- asd->match.fwnode = of_fwnode_handle(sensor_node);
-
- ret = v4l2_async_notifier_add_subdev(&ctx->notifier, asd);
- if (ret) {
- ctx_err(ctx, "Error adding asd\n");
- kfree(asd);
- goto cleanup_exit;
- }
-
- ctx->notifier.ops = &cal_async_ops;
- ret = v4l2_async_notifier_register(&ctx->v4l2_dev,
- &ctx->notifier);
- if (ret) {
- ctx_err(ctx, "Error registering async notifier\n");
- v4l2_async_notifier_cleanup(&ctx->notifier);
- ret = -EINVAL;
- }
-
- /*
- * On success we need to keep reference on sensor_node, or
- * if notifier_cleanup was called above, sensor_node was
- * already put.
- */
- sensor_node = NULL;
-
-cleanup_exit:
- of_node_put(sensor_node);
- of_node_put(ep_node);
- of_node_put(port);
-
+error:
+ v4l2_async_notifier_cleanup(&cal->notifier);
return ret;
}
-static struct cal_ctx *cal_create_instance(struct cal_dev *dev, int inst)
+static void cal_async_notifier_unregister(struct cal_dev *cal)
{
- struct cal_ctx *ctx;
- struct v4l2_ctrl_handler *hdl;
+ v4l2_async_notifier_unregister(&cal->notifier);
+ v4l2_async_notifier_cleanup(&cal->notifier);
+}
+
+/* ------------------------------------------------------------------
+ * Media and V4L2 device handling
+ * ------------------------------------------------------------------
+ */
+
+/*
+ * Register user-facing devices. To be called at the end of the probe function
+ * when all resources are initialized and ready.
+ */
+static int cal_media_register(struct cal_dev *cal)
+{
int ret;
- ctx = devm_kzalloc(&dev->pdev->dev, sizeof(*ctx), GFP_KERNEL);
+ ret = media_device_register(&cal->mdev);
+ if (ret) {
+ cal_err(cal, "Failed to register media device\n");
+ return ret;
+ }
+
+ /*
+ * Register the async notifier. This may trigger registration of the
+ * V4L2 video devices if all subdevs are ready.
+ */
+ ret = cal_async_notifier_register(cal);
+ if (ret) {
+ media_device_unregister(&cal->mdev);
+ return ret;
+ }
+
+ return 0;
+}
+
+/*
+ * Unregister the user-facing devices, but don't free memory yet. To be called
+ * at the beginning of the remove function, to disallow access from userspace.
+ */
+static void cal_media_unregister(struct cal_dev *cal)
+{
+ unsigned int i;
+
+ /* Unregister all the V4L2 video devices. */
+ for (i = 0; i < ARRAY_SIZE(cal->ctx); i++) {
+ if (cal->ctx[i])
+ cal_ctx_v4l2_unregister(cal->ctx[i]);
+ }
+
+ cal_async_notifier_unregister(cal);
+ media_device_unregister(&cal->mdev);
+}
+
+/*
+ * Initialize the in-kernel objects. To be called at the beginning of the probe
+ * function, before the V4L2 device is used by the driver.
+ */
+static int cal_media_init(struct cal_dev *cal)
+{
+ struct media_device *mdev = &cal->mdev;
+ int ret;
+
+ mdev->dev = cal->dev;
+ mdev->hw_revision = cal->revision;
+ strscpy(mdev->model, "CAL", sizeof(mdev->model));
+ snprintf(mdev->bus_info, sizeof(mdev->bus_info), "platform:%s",
+ dev_name(mdev->dev));
+ media_device_init(mdev);
+
+ /*
+ * Initialize the V4L2 device (despite the function name, this performs
+ * initialization, not registration).
+ */
+ cal->v4l2_dev.mdev = mdev;
+ ret = v4l2_device_register(cal->dev, &cal->v4l2_dev);
+ if (ret) {
+ cal_err(cal, "Failed to register V4L2 device\n");
+ return ret;
+ }
+
+ vb2_dma_contig_set_max_seg_size(cal->dev, DMA_BIT_MASK(32));
+
+ return 0;
+}
+
+/*
+ * Cleanup the in-kernel objects, freeing memory. To be called at the very end
+ * of the remove sequence, when nothing (including userspace) can access the
+ * objects anymore.
+ */
+static void cal_media_cleanup(struct cal_dev *cal)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(cal->ctx); i++) {
+ if (cal->ctx[i])
+ cal_ctx_v4l2_cleanup(cal->ctx[i]);
+ }
+
+ v4l2_device_unregister(&cal->v4l2_dev);
+ media_device_cleanup(&cal->mdev);
+
+ vb2_dma_contig_clear_max_seg_size(cal->dev);
+}
+
+/* ------------------------------------------------------------------
+ * Initialization and module stuff
+ * ------------------------------------------------------------------
+ */
+
+static struct cal_ctx *cal_ctx_create(struct cal_dev *cal, int inst)
+{
+ struct cal_ctx *ctx;
+ int ret;
+
+ ctx = devm_kzalloc(cal->dev, sizeof(*ctx), GFP_KERNEL);
if (!ctx)
return NULL;
- /* save the cal_dev * for future ref */
- ctx->dev = dev;
+ ctx->cal = cal;
+ ctx->phy = cal->phy[inst];
+ ctx->index = inst;
+ ctx->cport = inst;
- snprintf(ctx->v4l2_dev.name, sizeof(ctx->v4l2_dev.name),
- "%s-%03d", CAL_MODULE_NAME, inst);
- ret = v4l2_device_register(&dev->pdev->dev, &ctx->v4l2_dev);
+ ret = cal_ctx_v4l2_init(ctx);
if (ret)
- goto err_exit;
+ return NULL;
- hdl = &ctx->ctrl_handler;
- ret = v4l2_ctrl_handler_init(hdl, 11);
- if (ret) {
- ctx_err(ctx, "Failed to init ctrl handler\n");
- goto unreg_dev;
- }
- ctx->v4l2_dev.ctrl_handler = hdl;
-
- /* Make sure Camera Core H/W register area is available */
- ctx->cc = dev->cc[inst];
-
- /* Store the instance id */
- ctx->csi2_port = inst + 1;
-
- ret = of_cal_create_instance(ctx, inst);
- if (ret) {
- ret = -EINVAL;
- goto free_hdl;
- }
return ctx;
+}
-free_hdl:
- v4l2_ctrl_handler_free(hdl);
-unreg_dev:
- v4l2_device_unregister(&ctx->v4l2_dev);
-err_exit:
- return NULL;
+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);
+
+/* Get hardware revision and info. */
+
+#define CAL_HL_HWINFO_VALUE 0xa3c90469
+
+static void cal_get_hwinfo(struct cal_dev *cal)
+{
+ u32 hwinfo;
+
+ cal->revision = cal_read(cal, CAL_HL_REVISION);
+ switch (FIELD_GET(CAL_HL_REVISION_SCHEME_MASK, cal->revision)) {
+ case CAL_HL_REVISION_SCHEME_H08:
+ cal_dbg(3, cal, "CAL HW revision %lu.%lu.%lu (0x%08x)\n",
+ FIELD_GET(CAL_HL_REVISION_MAJOR_MASK, cal->revision),
+ FIELD_GET(CAL_HL_REVISION_MINOR_MASK, cal->revision),
+ FIELD_GET(CAL_HL_REVISION_RTL_MASK, cal->revision),
+ cal->revision);
+ break;
+
+ case CAL_HL_REVISION_SCHEME_LEGACY:
+ default:
+ cal_info(cal, "Unexpected CAL HW revision 0x%08x\n",
+ cal->revision);
+ break;
+ }
+
+ hwinfo = cal_read(cal, CAL_HL_HWINFO);
+ if (hwinfo != CAL_HL_HWINFO_VALUE)
+ cal_info(cal, "CAL_HL_HWINFO = 0x%08x, expected 0x%08x\n",
+ hwinfo, CAL_HL_HWINFO_VALUE);
+}
+
+static int cal_init_camerarx_regmap(struct cal_dev *cal)
+{
+ struct platform_device *pdev = to_platform_device(cal->dev);
+ struct device_node *np = cal->dev->of_node;
+ struct regmap_config config = { };
+ struct regmap *syscon;
+ struct resource *res;
+ unsigned int offset;
+ void __iomem *base;
+
+ syscon = syscon_regmap_lookup_by_phandle_args(np, "ti,camerrx-control",
+ 1, &offset);
+ if (!IS_ERR(syscon)) {
+ cal->syscon_camerrx = syscon;
+ cal->syscon_camerrx_offset = offset;
+ return 0;
+ }
+
+ dev_warn(cal->dev, "failed to get ti,camerrx-control: %ld\n",
+ PTR_ERR(syscon));
+
+ /*
+ * Backward DTS compatibility. If syscon entry is not present then
+ * check if the camerrx_control resource is present.
+ */
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "camerrx_control");
+ base = devm_ioremap_resource(cal->dev, res);
+ if (IS_ERR(base)) {
+ cal_err(cal, "failed to ioremap camerrx_control\n");
+ return PTR_ERR(base);
+ }
+
+ cal_dbg(1, cal, "ioresource %s at %pa - %pa\n",
+ res->name, &res->start, &res->end);
+
+ config.reg_bits = 32;
+ config.reg_stride = 4;
+ config.val_bits = 32;
+ config.max_register = resource_size(res) - 4;
+
+ syscon = regmap_init_mmio(NULL, base, &config);
+ if (IS_ERR(syscon)) {
+ pr_err("regmap init failed\n");
+ return PTR_ERR(syscon);
+ }
+
+ /*
+ * In this case the base already point to the direct CM register so no
+ * need for an offset.
+ */
+ cal->syscon_camerrx = syscon;
+ cal->syscon_camerrx_offset = 0;
+
+ return 0;
}
static int cal_probe(struct platform_device *pdev)
{
- struct cal_dev *dev;
+ struct cal_dev *cal;
struct cal_ctx *ctx;
+ bool connected = false;
+ unsigned int i;
int ret;
int irq;
- int i;
- dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
- if (!dev)
+ cal = devm_kzalloc(&pdev->dev, sizeof(*cal), GFP_KERNEL);
+ if (!cal)
return -ENOMEM;
- /* set pseudo v4l2 device name so we can use v4l2_printk */
- strscpy(dev->v4l2_dev.name, CAL_MODULE_NAME,
- sizeof(dev->v4l2_dev.name));
-
- /* save pdev pointer */
- dev->pdev = pdev;
-
- dev->res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
- "cal_top");
- dev->base = devm_ioremap_resource(&pdev->dev, dev->res);
- if (IS_ERR(dev->base))
- return PTR_ERR(dev->base);
-
- cal_dbg(1, dev, "ioresource %s at %pa - %pa\n",
- dev->res->name, &dev->res->start, &dev->res->end);
-
- irq = platform_get_irq(pdev, 0);
- cal_dbg(1, dev, "got irq# %d\n", irq);
- ret = devm_request_irq(&pdev->dev, irq, cal_irq, 0, CAL_MODULE_NAME,
- dev);
- if (ret)
- return ret;
-
- platform_set_drvdata(pdev, dev);
-
- dev->cm = cm_create(dev);
- if (IS_ERR(dev->cm))
- return PTR_ERR(dev->cm);
-
- dev->cc[0] = cc_create(dev, 0);
- if (IS_ERR(dev->cc[0]))
- return PTR_ERR(dev->cc[0]);
-
- dev->cc[1] = cc_create(dev, 1);
- if (IS_ERR(dev->cc[1]))
- return PTR_ERR(dev->cc[1]);
-
- dev->ctx[0] = NULL;
- dev->ctx[1] = NULL;
-
- dev->ctx[0] = cal_create_instance(dev, 0);
- dev->ctx[1] = cal_create_instance(dev, 1);
- if (!dev->ctx[0] && !dev->ctx[1]) {
- cal_err(dev, "Neither port is configured, no point in staying up\n");
+ cal->data = of_device_get_match_data(&pdev->dev);
+ if (!cal->data) {
+ dev_err(&pdev->dev, "Could not get feature data based on compatible version\n");
return -ENODEV;
}
- pm_runtime_enable(&pdev->dev);
+ cal->dev = &pdev->dev;
+ platform_set_drvdata(pdev, cal);
- ret = cal_runtime_get(dev);
+ /* Acquire resources: clocks, CAMERARX regmap, I/O memory and IRQ. */
+ cal->fclk = devm_clk_get(&pdev->dev, "fck");
+ if (IS_ERR(cal->fclk)) {
+ dev_err(&pdev->dev, "cannot get CAL fclk\n");
+ return PTR_ERR(cal->fclk);
+ }
+
+ ret = cal_init_camerarx_regmap(cal);
+ if (ret < 0)
+ return ret;
+
+ cal->res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "cal_top");
+ cal->base = devm_ioremap_resource(&pdev->dev, cal->res);
+ if (IS_ERR(cal->base))
+ return PTR_ERR(cal->base);
+
+ cal_dbg(1, cal, "ioresource %s at %pa - %pa\n",
+ cal->res->name, &cal->res->start, &cal->res->end);
+
+ irq = platform_get_irq(pdev, 0);
+ cal_dbg(1, cal, "got irq# %d\n", irq);
+ ret = devm_request_irq(&pdev->dev, irq, cal_irq, 0, CAL_MODULE_NAME,
+ cal);
if (ret)
- goto runtime_disable;
+ return ret;
- /* Just check we can actually access the module */
- cal_get_hwinfo(dev);
+ /* Read the revision and hardware info to verify hardware access. */
+ pm_runtime_enable(&pdev->dev);
+ ret = pm_runtime_get_sync(&pdev->dev);
+ if (ret)
+ goto error_pm_runtime;
- cal_runtime_put(dev);
+ cal_get_hwinfo(cal);
+ pm_runtime_put_sync(&pdev->dev);
+
+ /* Create CAMERARX PHYs. */
+ for (i = 0; i < cal->data->num_csi2_phy; ++i) {
+ cal->phy[i] = cal_camerarx_create(cal, i);
+ if (IS_ERR(cal->phy[i])) {
+ ret = PTR_ERR(cal->phy[i]);
+ cal->phy[i] = NULL;
+ goto error_camerarx;
+ }
+
+ if (cal->phy[i]->sensor_node)
+ connected = true;
+ }
+
+ if (!connected) {
+ cal_err(cal, "Neither port is configured, no point in staying up\n");
+ ret = -ENODEV;
+ goto error_camerarx;
+ }
+
+ /* Initialize the media device. */
+ ret = cal_media_init(cal);
+ if (ret < 0)
+ goto error_camerarx;
+
+ /* Create contexts. */
+ for (i = 0; i < cal->data->num_csi2_phy; ++i) {
+ if (!cal->phy[i]->sensor_node)
+ continue;
+
+ cal->ctx[i] = cal_ctx_create(cal, i);
+ if (!cal->ctx[i]) {
+ cal_err(cal, "Failed to create context %u\n", i);
+ ret = -ENODEV;
+ goto error_context;
+ }
+ }
+
+ /* Register the media device. */
+ ret = cal_media_register(cal);
+ if (ret)
+ goto error_context;
return 0;
-runtime_disable:
- pm_runtime_disable(&pdev->dev);
- for (i = 0; i < CAL_NUM_CONTEXT; i++) {
- ctx = dev->ctx[i];
- if (ctx) {
- v4l2_async_notifier_unregister(&ctx->notifier);
- v4l2_async_notifier_cleanup(&ctx->notifier);
- v4l2_ctrl_handler_free(&ctx->ctrl_handler);
- v4l2_device_unregister(&ctx->v4l2_dev);
- }
+error_context:
+ for (i = 0; i < ARRAY_SIZE(cal->ctx); i++) {
+ ctx = cal->ctx[i];
+ if (ctx)
+ cal_ctx_v4l2_cleanup(ctx);
}
+ cal_media_cleanup(cal);
+
+error_camerarx:
+ for (i = 0; i < ARRAY_SIZE(cal->phy); i++)
+ cal_camerarx_destroy(cal->phy[i]);
+
+error_pm_runtime:
+ pm_runtime_disable(&pdev->dev);
+
return ret;
}
static int cal_remove(struct platform_device *pdev)
{
- struct cal_dev *dev =
- (struct cal_dev *)platform_get_drvdata(pdev);
- struct cal_ctx *ctx;
- int i;
+ struct cal_dev *cal = platform_get_drvdata(pdev);
+ unsigned int i;
- cal_dbg(1, dev, "Removing %s\n", CAL_MODULE_NAME);
+ cal_dbg(1, cal, "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];
- if (ctx) {
- ctx_dbg(1, ctx, "unregistering %s\n",
- video_device_node_name(&ctx->vdev));
- camerarx_phy_disable(ctx);
- v4l2_async_notifier_unregister(&ctx->notifier);
- v4l2_async_notifier_cleanup(&ctx->notifier);
- v4l2_ctrl_handler_free(&ctx->ctrl_handler);
- v4l2_device_unregister(&ctx->v4l2_dev);
- video_unregister_device(&ctx->vdev);
- }
+ cal_media_unregister(cal);
+
+ for (i = 0; i < ARRAY_SIZE(cal->phy); i++) {
+ if (cal->phy[i])
+ cal_camerarx_disable(cal->phy[i]);
}
- cal_runtime_put(dev);
+ cal_media_cleanup(cal);
+
+ for (i = 0; i < ARRAY_SIZE(cal->phy); i++)
+ cal_camerarx_destroy(cal->phy[i]);
+
+ pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
return 0;
}
-#if defined(CONFIG_OF)
-static const struct of_device_id cal_of_match[] = {
- { .compatible = "ti,dra72-cal", },
- {},
+static int cal_runtime_resume(struct device *dev)
+{
+ struct cal_dev *cal = dev_get_drvdata(dev);
+
+ if (cal->data->flags & DRA72_CAL_PRE_ES2_LDO_DISABLE) {
+ /*
+ * Apply errata on both port everytime we (re-)enable
+ * the clock
+ */
+ cal_camerarx_i913_errata(cal->phy[0]);
+ cal_camerarx_i913_errata(cal->phy[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.h b/drivers/media/platform/ti-vpe/cal.h
new file mode 100644
index 0000000..4123405
--- /dev/null
+++ b/drivers/media/platform/ti-vpe/cal.h
@@ -0,0 +1,267 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * TI Camera Access Layer (CAL)
+ *
+ * Copyright (c) 2015-2020 Texas Instruments Inc.
+ *
+ * Authors:
+ * Benoit Parrot <bparrot@ti.com>
+ * Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ */
+#ifndef __TI_CAL_H__
+#define __TI_CAL_H__
+
+#include <linux/bitfield.h>
+#include <linux/io.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+#include <linux/videodev2.h>
+
+#include <media/media-device.h>
+#include <media/v4l2-async.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-fwnode.h>
+#include <media/videobuf2-v4l2.h>
+
+#define CAL_MODULE_NAME "cal"
+#define CAL_NUM_CONTEXT 2
+#define CAL_NUM_CSI2_PORTS 2
+
+#define MAX_WIDTH_BYTES (8192 * 8)
+#define MAX_HEIGHT_LINES 16383
+
+struct device;
+struct device_node;
+struct resource;
+struct regmap;
+struct regmap_fied;
+struct v4l2_subdev;
+
+/* CTRL_CORE_CAMERRX_CONTROL register field id */
+enum cal_camerarx_field {
+ F_CTRLCLKEN,
+ F_CAMMODE,
+ F_LANEENABLE,
+ F_CSI_MODE,
+ F_MAX_FIELDS,
+};
+
+struct cal_fmt {
+ u32 fourcc;
+ u32 code;
+ /* Bits per pixel */
+ u8 bpp;
+};
+
+/* buffer for one video frame */
+struct cal_buffer {
+ /* common v4l buffer stuff -- must be first */
+ struct vb2_v4l2_buffer vb;
+ struct list_head list;
+};
+
+struct cal_dmaqueue {
+ struct list_head active;
+};
+
+struct cal_camerarx_data {
+ struct {
+ unsigned int lsb;
+ unsigned int msb;
+ } fields[F_MAX_FIELDS];
+ unsigned int num_lanes;
+};
+
+struct cal_data {
+ const struct cal_camerarx_data *camerarx;
+ unsigned int num_csi2_phy;
+ unsigned int flags;
+};
+
+/*
+ * The Camera Adaptation Layer (CAL) module is paired with one or more complex
+ * I/O PHYs (CAMERARX). It contains multiple instances of CSI-2, processing and
+ * DMA contexts.
+ *
+ * The cal_dev structure represents the whole subsystem, including the CAL and
+ * the CAMERARX instances. Instances of struct cal_dev are named cal through the
+ * driver.
+ *
+ * The cal_camerarx structure represents one CAMERARX instance. Instances of
+ * cal_camerarx are named phy through the driver.
+ *
+ * The cal_ctx structure represents the combination of one CSI-2 context, one
+ * processing context and one DMA context. Instance of struct cal_ctx are named
+ * ctx through the driver.
+ */
+
+struct cal_camerarx {
+ void __iomem *base;
+ struct resource *res;
+ struct device *dev;
+ struct regmap_field *fields[F_MAX_FIELDS];
+
+ struct cal_dev *cal;
+ unsigned int instance;
+
+ struct v4l2_fwnode_endpoint endpoint;
+ struct device_node *sensor_node;
+ struct v4l2_subdev *sensor;
+};
+
+struct cal_dev {
+ struct clk *fclk;
+ int irq;
+ void __iomem *base;
+ struct resource *res;
+ struct device *dev;
+
+ const struct cal_data *data;
+ u32 revision;
+
+ /* Control Module handle */
+ struct regmap *syscon_camerrx;
+ u32 syscon_camerrx_offset;
+
+ /* Camera Core Module handle */
+ struct cal_camerarx *phy[CAL_NUM_CSI2_PORTS];
+
+ struct cal_ctx *ctx[CAL_NUM_CONTEXT];
+
+ struct media_device mdev;
+ struct v4l2_device v4l2_dev;
+ struct v4l2_async_notifier notifier;
+};
+
+/*
+ * There is one cal_ctx structure for each camera core context.
+ */
+struct cal_ctx {
+ struct v4l2_ctrl_handler ctrl_handler;
+ struct video_device vdev;
+ struct media_pad pad;
+
+ struct cal_dev *cal;
+ struct cal_camerarx *phy;
+
+ /* v4l2_ioctl mutex */
+ struct mutex mutex;
+ /* v4l2 buffers lock */
+ spinlock_t slock;
+
+ struct cal_dmaqueue vidq;
+
+ /* video capture */
+ const struct cal_fmt *fmt;
+ /* Used to store current pixel format */
+ struct v4l2_format v_fmt;
+ /* Used to store current mbus frame format */
+ struct v4l2_mbus_framefmt m_fmt;
+
+ /* Current subdev enumerated format */
+ const struct cal_fmt **active_fmt;
+ unsigned int num_active_fmt;
+
+ unsigned int sequence;
+ struct vb2_queue vb_vidq;
+ unsigned int index;
+ unsigned int cport;
+
+ /* Pointer pointing to current v4l2_buffer */
+ struct cal_buffer *cur_frm;
+ /* Pointer pointing to next v4l2_buffer */
+ struct cal_buffer *next_frm;
+
+ bool dma_act;
+};
+
+extern unsigned int cal_debug;
+extern int cal_video_nr;
+
+#define cal_dbg(level, cal, fmt, arg...) \
+ do { \
+ if (cal_debug >= (level)) \
+ dev_printk(KERN_DEBUG, (cal)->dev, fmt, ##arg); \
+ } while (0)
+#define cal_info(cal, fmt, arg...) \
+ dev_info((cal)->dev, fmt, ##arg)
+#define cal_err(cal, fmt, arg...) \
+ dev_err((cal)->dev, fmt, ##arg)
+
+#define ctx_dbg(level, ctx, fmt, arg...) \
+ cal_dbg(level, (ctx)->cal, "ctx%u: " fmt, (ctx)->index, ##arg)
+#define ctx_info(ctx, fmt, arg...) \
+ cal_info((ctx)->cal, "ctx%u: " fmt, (ctx)->index, ##arg)
+#define ctx_err(ctx, fmt, arg...) \
+ cal_err((ctx)->cal, "ctx%u: " fmt, (ctx)->index, ##arg)
+
+#define phy_dbg(level, phy, fmt, arg...) \
+ cal_dbg(level, (phy)->cal, "phy%u: " fmt, (phy)->instance, ##arg)
+#define phy_info(phy, fmt, arg...) \
+ cal_info((phy)->cal, "phy%u: " fmt, (phy)->instance, ##arg)
+#define phy_err(phy, fmt, arg...) \
+ cal_err((phy)->cal, "phy%u: " fmt, (phy)->instance, ##arg)
+
+static inline u32 cal_read(struct cal_dev *cal, u32 offset)
+{
+ return ioread32(cal->base + offset);
+}
+
+static inline void cal_write(struct cal_dev *cal, u32 offset, u32 val)
+{
+ iowrite32(val, cal->base + offset);
+}
+
+static inline u32 cal_read_field(struct cal_dev *cal, u32 offset, u32 mask)
+{
+ return FIELD_GET(mask, cal_read(cal, offset));
+}
+
+static inline void cal_write_field(struct cal_dev *cal, u32 offset, u32 value,
+ u32 mask)
+{
+ u32 val = cal_read(cal, offset);
+
+ val &= ~mask;
+ val |= (value << __ffs(mask)) & mask;
+ cal_write(cal, offset, val);
+}
+
+static inline void cal_set_field(u32 *valp, u32 field, u32 mask)
+{
+ u32 val = *valp;
+
+ val &= ~mask;
+ val |= (field << __ffs(mask)) & mask;
+ *valp = val;
+}
+
+void cal_quickdump_regs(struct cal_dev *cal);
+
+void cal_camerarx_disable(struct cal_camerarx *phy);
+int cal_camerarx_start(struct cal_camerarx *phy, const struct cal_fmt *fmt);
+void cal_camerarx_stop(struct cal_camerarx *phy);
+void cal_camerarx_enable_irqs(struct cal_camerarx *phy);
+void cal_camerarx_disable_irqs(struct cal_camerarx *phy);
+void cal_camerarx_ppi_enable(struct cal_camerarx *phy);
+void cal_camerarx_ppi_disable(struct cal_camerarx *phy);
+void cal_camerarx_i913_errata(struct cal_camerarx *phy);
+struct cal_camerarx *cal_camerarx_create(struct cal_dev *cal,
+ unsigned int instance);
+void cal_camerarx_destroy(struct cal_camerarx *phy);
+
+void cal_ctx_csi2_config(struct cal_ctx *ctx);
+void cal_ctx_pix_proc_config(struct cal_ctx *ctx);
+void cal_ctx_wr_dma_config(struct cal_ctx *ctx, unsigned int width,
+ unsigned int height);
+void cal_ctx_wr_dma_addr(struct cal_ctx *ctx, unsigned int dmaaddr);
+
+int cal_ctx_v4l2_register(struct cal_ctx *ctx);
+void cal_ctx_v4l2_unregister(struct cal_ctx *ctx);
+int cal_ctx_v4l2_init(struct cal_ctx *ctx);
+void cal_ctx_v4l2_cleanup(struct cal_ctx *ctx);
+
+#endif /* __TI_CAL_H__ */
diff --git a/drivers/media/platform/ti-vpe/cal_regs.h b/drivers/media/platform/ti-vpe/cal_regs.h
index 68cfc92..f752096 100644
--- a/drivers/media/platform/ti-vpe/cal_regs.h
+++ b/drivers/media/platform/ti-vpe/cal_regs.h
@@ -10,7 +10,29 @@
#ifndef __TI_CAL_REGS_H
#define __TI_CAL_REGS_H
-#define CAL_NUM_CSI2_PORTS 2
+/*
+ * struct cal_dev.flags possibilities
+ *
+ * DRA72_CAL_PRE_ES2_LDO_DISABLE:
+ * Errata i913: CSI2 LDO Needs to be disabled when module is powered on
+ *
+ * Enabling CSI2 LDO shorts it to core supply. It is crucial the 2 CSI2
+ * LDOs on the device are disabled if CSI-2 module is powered on
+ * (0x4845 B304 | 0x4845 B384 [28:27] = 0x1) or in ULPS (0x4845 B304
+ * | 0x4845 B384 [28:27] = 0x2) mode. Common concerns include: high
+ * current draw on the module supply in active mode.
+ *
+ * Errata does not apply when CSI-2 module is powered off
+ * (0x4845 B304 | 0x4845 B384 [28:27] = 0x0).
+ *
+ * SW Workaround:
+ * Set the following register bits to disable the LDO,
+ * which is essentially CSI2 REG10 bit 6:
+ *
+ * Core 0: 0x4845 B828 = 0x0000 0040
+ * Core 1: 0x4845 B928 = 0x0000 0040
+ */
+#define DRA72_CAL_PRE_ES2_LDO_DISABLE BIT(0)
/* CAL register offsets */
@@ -18,11 +40,11 @@
#define CAL_HL_HWINFO 0x0004
#define CAL_HL_SYSCONFIG 0x0010
#define CAL_HL_IRQ_EOI 0x001c
-#define CAL_HL_IRQSTATUS_RAW(m) (0x20U + ((m-1) * 0x10U))
-#define CAL_HL_IRQSTATUS(m) (0x24U + ((m-1) * 0x10U))
-#define CAL_HL_IRQENABLE_SET(m) (0x28U + ((m-1) * 0x10U))
-#define CAL_HL_IRQENABLE_CLR(m) (0x2cU + ((m-1) * 0x10U))
-#define CAL_PIX_PROC(m) (0xc0U + ((m-1) * 0x4U))
+#define CAL_HL_IRQSTATUS_RAW(m) (0x20U + (m) * 0x10U)
+#define CAL_HL_IRQSTATUS(m) (0x24U + (m) * 0x10U)
+#define CAL_HL_IRQENABLE_SET(m) (0x28U + (m) * 0x10U)
+#define CAL_HL_IRQENABLE_CLR(m) (0x2cU + (m) * 0x10U)
+#define CAL_PIX_PROC(m) (0xc0U + (m) * 0x4U)
#define CAL_CTRL 0x100
#define CAL_CTRL1 0x104
#define CAL_LINE_NUMBER_EVT 0x108
@@ -38,53 +60,45 @@
#define CAL_RD_DMA_INIT_ADDR 0x154
#define CAL_RD_DMA_INIT_OFST 0x168
#define CAL_RD_DMA_CTRL2 0x16c
-#define CAL_WR_DMA_CTRL(m) (0x200U + ((m-1) * 0x10U))
-#define CAL_WR_DMA_ADDR(m) (0x204U + ((m-1) * 0x10U))
-#define CAL_WR_DMA_OFST(m) (0x208U + ((m-1) * 0x10U))
-#define CAL_WR_DMA_XSIZE(m) (0x20cU + ((m-1) * 0x10U))
-#define CAL_CSI2_PPI_CTRL(m) (0x300U + ((m-1) * 0x80U))
-#define CAL_CSI2_COMPLEXIO_CFG(m) (0x304U + ((m-1) * 0x80U))
-#define CAL_CSI2_COMPLEXIO_IRQSTATUS(m) (0x308U + ((m-1) * 0x80U))
-#define CAL_CSI2_SHORT_PACKET(m) (0x30cU + ((m-1) * 0x80U))
-#define CAL_CSI2_COMPLEXIO_IRQENABLE(m) (0x310U + ((m-1) * 0x80U))
-#define CAL_CSI2_TIMING(m) (0x314U + ((m-1) * 0x80U))
-#define CAL_CSI2_VC_IRQENABLE(m) (0x318U + ((m-1) * 0x80U))
-#define CAL_CSI2_VC_IRQSTATUS(m) (0x328U + ((m-1) * 0x80U))
-#define CAL_CSI2_CTX0(m) (0x330U + ((m-1) * 0x80U))
-#define CAL_CSI2_CTX1(m) (0x334U + ((m-1) * 0x80U))
-#define CAL_CSI2_CTX2(m) (0x338U + ((m-1) * 0x80U))
-#define CAL_CSI2_CTX3(m) (0x33cU + ((m-1) * 0x80U))
-#define CAL_CSI2_CTX4(m) (0x340U + ((m-1) * 0x80U))
-#define CAL_CSI2_CTX5(m) (0x344U + ((m-1) * 0x80U))
-#define CAL_CSI2_CTX6(m) (0x348U + ((m-1) * 0x80U))
-#define CAL_CSI2_CTX7(m) (0x34cU + ((m-1) * 0x80U))
-#define CAL_CSI2_STATUS0(m) (0x350U + ((m-1) * 0x80U))
-#define CAL_CSI2_STATUS1(m) (0x354U + ((m-1) * 0x80U))
-#define CAL_CSI2_STATUS2(m) (0x358U + ((m-1) * 0x80U))
-#define CAL_CSI2_STATUS3(m) (0x35cU + ((m-1) * 0x80U))
-#define CAL_CSI2_STATUS4(m) (0x360U + ((m-1) * 0x80U))
-#define CAL_CSI2_STATUS5(m) (0x364U + ((m-1) * 0x80U))
-#define CAL_CSI2_STATUS6(m) (0x368U + ((m-1) * 0x80U))
-#define CAL_CSI2_STATUS7(m) (0x36cU + ((m-1) * 0x80U))
+#define CAL_WR_DMA_CTRL(m) (0x200U + (m) * 0x10U)
+#define CAL_WR_DMA_ADDR(m) (0x204U + (m) * 0x10U)
+#define CAL_WR_DMA_OFST(m) (0x208U + (m) * 0x10U)
+#define CAL_WR_DMA_XSIZE(m) (0x20cU + (m) * 0x10U)
+#define CAL_CSI2_PPI_CTRL(m) (0x300U + (m) * 0x80U)
+#define CAL_CSI2_COMPLEXIO_CFG(m) (0x304U + (m) * 0x80U)
+#define CAL_CSI2_COMPLEXIO_IRQSTATUS(m) (0x308U + (m) * 0x80U)
+#define CAL_CSI2_SHORT_PACKET(m) (0x30cU + (m) * 0x80U)
+#define CAL_CSI2_COMPLEXIO_IRQENABLE(m) (0x310U + (m) * 0x80U)
+#define CAL_CSI2_TIMING(m) (0x314U + (m) * 0x80U)
+#define CAL_CSI2_VC_IRQENABLE(m) (0x318U + (m) * 0x80U)
+#define CAL_CSI2_VC_IRQSTATUS(m) (0x328U + (m) * 0x80U)
+#define CAL_CSI2_CTX0(m) (0x330U + (m) * 0x80U)
+#define CAL_CSI2_CTX1(m) (0x334U + (m) * 0x80U)
+#define CAL_CSI2_CTX2(m) (0x338U + (m) * 0x80U)
+#define CAL_CSI2_CTX3(m) (0x33cU + (m) * 0x80U)
+#define CAL_CSI2_CTX4(m) (0x340U + (m) * 0x80U)
+#define CAL_CSI2_CTX5(m) (0x344U + (m) * 0x80U)
+#define CAL_CSI2_CTX6(m) (0x348U + (m) * 0x80U)
+#define CAL_CSI2_CTX7(m) (0x34cU + (m) * 0x80U)
+#define CAL_CSI2_STATUS0(m) (0x350U + (m) * 0x80U)
+#define CAL_CSI2_STATUS1(m) (0x354U + (m) * 0x80U)
+#define CAL_CSI2_STATUS2(m) (0x358U + (m) * 0x80U)
+#define CAL_CSI2_STATUS3(m) (0x35cU + (m) * 0x80U)
+#define CAL_CSI2_STATUS4(m) (0x360U + (m) * 0x80U)
+#define CAL_CSI2_STATUS5(m) (0x364U + (m) * 0x80U)
+#define CAL_CSI2_STATUS6(m) (0x368U + (m) * 0x80U)
+#define CAL_CSI2_STATUS7(m) (0x36cU + (m) * 0x80U)
/* CAL CSI2 PHY register offsets */
#define CAL_CSI2_PHY_REG0 0x000
#define CAL_CSI2_PHY_REG1 0x004
#define CAL_CSI2_PHY_REG2 0x008
+#define CAL_CSI2_PHY_REG10 0x028
/* CAL Control Module Core Camerrx Control register offsets */
#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
*********************************************************************/
@@ -110,7 +124,7 @@
#define CAL_HL_HWINFO_NPPI_CONTEXTS_EIGHT 2
#define CAL_HL_HWINFO_NPPI_CONTEXTS_RESERVED 3
-#define CAL_HL_SYSCONFIG_SOFTRESET_MASK BIT_MASK(0)
+#define CAL_HL_SYSCONFIG_SOFTRESET_MASK BIT(0)
#define CAL_HL_SYSCONFIG_SOFTRESET_DONE 0x0
#define CAL_HL_SYSCONFIG_SOFTRESET_PENDING 0x1
#define CAL_HL_SYSCONFIG_SOFTRESET_NOACTION 0x0
@@ -121,19 +135,18 @@
#define CAL_HL_SYSCONFIG_IDLEMODE_SMART1 2
#define CAL_HL_SYSCONFIG_IDLEMODE_SMART2 3
-#define CAL_HL_IRQ_EOI_LINE_NUMBER_MASK BIT_MASK(0)
+#define CAL_HL_IRQ_EOI_LINE_NUMBER_MASK BIT(0)
#define CAL_HL_IRQ_EOI_LINE_NUMBER_READ0 0
#define CAL_HL_IRQ_EOI_LINE_NUMBER_EOI0 0
-#define CAL_HL_IRQ_MASK(m) BIT_MASK(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_MASK(m) BIT(m)
-#define CAL_PIX_PROC_EN_MASK BIT_MASK(0)
+#define CAL_HL_IRQ_OCPO_ERR_MASK BIT(6)
+
+#define CAL_HL_IRQ_CIO_MASK(i) BIT(16 + (i) * 8)
+#define CAL_HL_IRQ_VC_MASK(i) BIT(17 + (i) * 8)
+
+#define CAL_PIX_PROC_EN_MASK BIT(0)
#define CAL_PIX_PROC_EXTRACT_MASK GENMASK(4, 1)
#define CAL_PIX_PROC_EXTRACT_B6 0x0
#define CAL_PIX_PROC_EXTRACT_B7 0x1
@@ -179,7 +192,7 @@
#define CAL_PIX_PROC_PACK_ARGB 0x6
#define CAL_PIX_PROC_CPORT_MASK GENMASK(23, 19)
-#define CAL_CTRL_POSTED_WRITES_MASK BIT_MASK(0)
+#define CAL_CTRL_POSTED_WRITES_MASK BIT(0)
#define CAL_CTRL_POSTED_WRITES_NONPOSTED 0
#define CAL_CTRL_POSTED_WRITES 1
#define CAL_CTRL_TAGCNT_MASK GENMASK(4, 1)
@@ -190,10 +203,10 @@
#define CAL_CTRL_BURSTSIZE_BURST128 0x3
#define CAL_CTRL_LL_FORCE_STATE_MASK GENMASK(12, 7)
#define CAL_CTRL_MFLAGL_MASK GENMASK(20, 13)
-#define CAL_CTRL_PWRSCPCLK_MASK BIT_MASK(21)
+#define CAL_CTRL_PWRSCPCLK_MASK BIT(21)
#define CAL_CTRL_PWRSCPCLK_AUTO 0
#define CAL_CTRL_PWRSCPCLK_FORCE 1
-#define CAL_CTRL_RD_DMA_STALL_MASK BIT_MASK(22)
+#define CAL_CTRL_RD_DMA_STALL_MASK BIT(22)
#define CAL_CTRL_MFLAGH_MASK GENMASK(31, 24)
#define CAL_CTRL1_PPI_GROUPING_MASK GENMASK(1, 0)
@@ -218,18 +231,18 @@
#define CAL_VPORT_CTRL1_PCLK_MASK GENMASK(16, 0)
#define CAL_VPORT_CTRL1_XBLK_MASK GENMASK(24, 17)
#define CAL_VPORT_CTRL1_YBLK_MASK GENMASK(30, 25)
-#define CAL_VPORT_CTRL1_WIDTH_MASK BIT_MASK(31)
+#define CAL_VPORT_CTRL1_WIDTH_MASK BIT(31)
#define CAL_VPORT_CTRL1_WIDTH_ONE 0
#define CAL_VPORT_CTRL1_WIDTH_TWO 1
#define CAL_VPORT_CTRL2_CPORT_MASK GENMASK(4, 0)
-#define CAL_VPORT_CTRL2_FREERUNNING_MASK BIT_MASK(15)
+#define CAL_VPORT_CTRL2_FREERUNNING_MASK BIT(15)
#define CAL_VPORT_CTRL2_FREERUNNING_GATED 0
#define CAL_VPORT_CTRL2_FREERUNNING_FREE 1
-#define CAL_VPORT_CTRL2_FS_RESETS_MASK BIT_MASK(16)
+#define CAL_VPORT_CTRL2_FS_RESETS_MASK BIT(16)
#define CAL_VPORT_CTRL2_FS_RESETS_NO 0
#define CAL_VPORT_CTRL2_FS_RESETS_YES 1
-#define CAL_VPORT_CTRL2_FSM_RESET_MASK BIT_MASK(17)
+#define CAL_VPORT_CTRL2_FSM_RESET_MASK BIT(17)
#define CAL_VPORT_CTRL2_FSM_RESET_NOEFFECT 0
#define CAL_VPORT_CTRL2_FSM_RESET 1
#define CAL_VPORT_CTRL2_RDY_THR_MASK GENMASK(31, 18)
@@ -237,23 +250,23 @@
#define CAL_BYS_CTRL1_PCLK_MASK GENMASK(16, 0)
#define CAL_BYS_CTRL1_XBLK_MASK GENMASK(24, 17)
#define CAL_BYS_CTRL1_YBLK_MASK GENMASK(30, 25)
-#define CAL_BYS_CTRL1_BYSINEN_MASK BIT_MASK(31)
+#define CAL_BYS_CTRL1_BYSINEN_MASK BIT(31)
#define CAL_BYS_CTRL2_CPORTIN_MASK GENMASK(4, 0)
#define CAL_BYS_CTRL2_CPORTOUT_MASK GENMASK(9, 5)
-#define CAL_BYS_CTRL2_DUPLICATEDDATA_MASK BIT_MASK(10)
+#define CAL_BYS_CTRL2_DUPLICATEDDATA_MASK BIT(10)
#define CAL_BYS_CTRL2_DUPLICATEDDATA_NO 0
#define CAL_BYS_CTRL2_DUPLICATEDDATA_YES 1
-#define CAL_BYS_CTRL2_FREERUNNING_MASK BIT_MASK(11)
+#define CAL_BYS_CTRL2_FREERUNNING_MASK BIT(11)
#define CAL_BYS_CTRL2_FREERUNNING_NO 0
#define CAL_BYS_CTRL2_FREERUNNING_YES 1
-#define CAL_RD_DMA_CTRL_GO_MASK BIT_MASK(0)
+#define CAL_RD_DMA_CTRL_GO_MASK BIT(0)
#define CAL_RD_DMA_CTRL_GO_DIS 0
#define CAL_RD_DMA_CTRL_GO_EN 1
#define CAL_RD_DMA_CTRL_GO_IDLE 0
#define CAL_RD_DMA_CTRL_GO_BUSY 1
-#define CAL_RD_DMA_CTRL_INIT_MASK BIT_MASK(1)
+#define CAL_RD_DMA_CTRL_INIT_MASK BIT(1)
#define CAL_RD_DMA_CTRL_BW_LIMITER_MASK GENMASK(10, 2)
#define CAL_RD_DMA_CTRL_OCP_TAG_CNT_MASK GENMASK(14, 11)
#define CAL_RD_DMA_CTRL_PCLK_MASK GENMASK(31, 15)
@@ -277,13 +290,13 @@
#define CAL_RD_DMA_CTRL2_CIRC_MODE_SIXTEEN 3
#define CAL_RD_DMA_CTRL2_CIRC_MODE_SIXTYFOUR 4
#define CAL_RD_DMA_CTRL2_CIRC_MODE_RESERVED 5
-#define CAL_RD_DMA_CTRL2_ICM_CSTART_MASK BIT_MASK(3)
+#define CAL_RD_DMA_CTRL2_ICM_CSTART_MASK BIT(3)
#define CAL_RD_DMA_CTRL2_PATTERN_MASK GENMASK(5, 4)
#define CAL_RD_DMA_CTRL2_PATTERN_LINEAR 0
#define CAL_RD_DMA_CTRL2_PATTERN_YUV420 1
#define CAL_RD_DMA_CTRL2_PATTERN_RD2SKIP2 2
#define CAL_RD_DMA_CTRL2_PATTERN_RD2SKIP4 3
-#define CAL_RD_DMA_CTRL2_BYSOUT_LE_WAIT_MASK BIT_MASK(6)
+#define CAL_RD_DMA_CTRL2_BYSOUT_LE_WAIT_MASK BIT(6)
#define CAL_RD_DMA_CTRL2_BYSOUT_LE_WAIT_FREERUNNING 0
#define CAL_RD_DMA_CTRL2_BYSOUT_LE_WAIT_WAITFORBYSOUT 1
#define CAL_RD_DMA_CTRL2_CIRC_SIZE_MASK GENMASK(29, 16)
@@ -300,7 +313,7 @@
#define CAL_WR_DMA_CTRL_PATTERN_WR2SKIP2 2
#define CAL_WR_DMA_CTRL_PATTERN_WR2SKIP4 3
#define CAL_WR_DMA_CTRL_PATTERN_RESERVED 1
-#define CAL_WR_DMA_CTRL_ICM_PSTART_MASK BIT_MASK(5)
+#define CAL_WR_DMA_CTRL_ICM_PSTART_MASK BIT(5)
#define CAL_WR_DMA_CTRL_DTAG_MASK GENMASK(8, 6)
#define CAL_WR_DMA_CTRL_DTAG_ATT_HDR 0
#define CAL_WR_DMA_CTRL_DTAG_ATT_DAT 1
@@ -311,7 +324,7 @@
#define CAL_WR_DMA_CTRL_DTAG_D6 6
#define CAL_WR_DMA_CTRL_DTAG_D7 7
#define CAL_WR_DMA_CTRL_CPORT_MASK GENMASK(13, 9)
-#define CAL_WR_DMA_CTRL_STALL_RD_MASK BIT_MASK(14)
+#define CAL_WR_DMA_CTRL_STALL_RD_MASK BIT(14)
#define CAL_WR_DMA_CTRL_YSIZE_MASK GENMASK(31, 18)
#define CAL_WR_DMA_ADDR_MASK GENMASK(31, 4)
@@ -327,9 +340,9 @@
#define CAL_WR_DMA_XSIZE_XSKIP_MASK GENMASK(15, 3)
#define CAL_WR_DMA_XSIZE_MASK GENMASK(31, 19)
-#define CAL_CSI2_PPI_CTRL_IF_EN_MASK BIT_MASK(0)
-#define CAL_CSI2_PPI_CTRL_ECC_EN_MASK BIT_MASK(2)
-#define CAL_CSI2_PPI_CTRL_FRAME_MASK BIT_MASK(3)
+#define CAL_CSI2_PPI_CTRL_IF_EN_MASK BIT(0)
+#define CAL_CSI2_PPI_CTRL_ECC_EN_MASK BIT(2)
+#define CAL_CSI2_PPI_CTRL_FRAME_MASK BIT(3)
#define CAL_CSI2_PPI_CTRL_FRAME_IMMEDIATE 0
#define CAL_CSI2_PPI_CTRL_FRAME 1
@@ -340,18 +353,18 @@
#define CAL_CSI2_COMPLEXIO_CFG_POSITION_2 2
#define CAL_CSI2_COMPLEXIO_CFG_POSITION_1 1
#define CAL_CSI2_COMPLEXIO_CFG_POSITION_NOT_USED 0
-#define CAL_CSI2_COMPLEXIO_CFG_CLOCK_POL_MASK BIT_MASK(3)
+#define CAL_CSI2_COMPLEXIO_CFG_CLOCK_POL_MASK BIT(3)
#define CAL_CSI2_COMPLEXIO_CFG_POL_PLUSMINUS 0
#define CAL_CSI2_COMPLEXIO_CFG_POL_MINUSPLUS 1
#define CAL_CSI2_COMPLEXIO_CFG_DATA1_POSITION_MASK GENMASK(6, 4)
-#define CAL_CSI2_COMPLEXIO_CFG_DATA1_POL_MASK BIT_MASK(7)
+#define CAL_CSI2_COMPLEXIO_CFG_DATA1_POL_MASK BIT(7)
#define CAL_CSI2_COMPLEXIO_CFG_DATA2_POSITION_MASK GENMASK(10, 8)
-#define CAL_CSI2_COMPLEXIO_CFG_DATA2_POL_MASK BIT_MASK(11)
+#define CAL_CSI2_COMPLEXIO_CFG_DATA2_POL_MASK BIT(11)
#define CAL_CSI2_COMPLEXIO_CFG_DATA3_POSITION_MASK GENMASK(14, 12)
-#define CAL_CSI2_COMPLEXIO_CFG_DATA3_POL_MASK BIT_MASK(15)
+#define CAL_CSI2_COMPLEXIO_CFG_DATA3_POL_MASK BIT(15)
#define CAL_CSI2_COMPLEXIO_CFG_DATA4_POSITION_MASK GENMASK(18, 16)
-#define CAL_CSI2_COMPLEXIO_CFG_DATA4_POL_MASK BIT_MASK(19)
-#define CAL_CSI2_COMPLEXIO_CFG_PWR_AUTO_MASK BIT_MASK(24)
+#define CAL_CSI2_COMPLEXIO_CFG_DATA4_POL_MASK BIT(19)
+#define CAL_CSI2_COMPLEXIO_CFG_PWR_AUTO_MASK BIT(24)
#define CAL_CSI2_COMPLEXIO_CFG_PWR_STATUS_MASK GENMASK(26, 25)
#define CAL_CSI2_COMPLEXIO_CFG_PWR_STATUS_STATE_OFF 0
#define CAL_CSI2_COMPLEXIO_CFG_PWR_STATUS_STATE_ON 1
@@ -360,83 +373,84 @@
#define CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_STATE_OFF 0
#define CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_STATE_ON 1
#define CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_STATE_ULP 2
-#define CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_MASK BIT_MASK(29)
+#define CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_MASK BIT(29)
#define CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETCOMPLETED 1
#define CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETONGOING 0
-#define CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_MASK BIT_MASK(30)
+#define CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_MASK BIT(30)
#define CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL 0
#define CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_OPERATIONAL 1
#define CAL_CSI2_SHORT_PACKET_MASK GENMASK(23, 0)
-#define CAL_CSI2_COMPLEXIO_IRQ_ERRSOTHS1_MASK BIT_MASK(0)
-#define CAL_CSI2_COMPLEXIO_IRQ_ERRSOTHS2_MASK BIT_MASK(1)
-#define CAL_CSI2_COMPLEXIO_IRQ_ERRSOTHS3_MASK BIT_MASK(2)
-#define CAL_CSI2_COMPLEXIO_IRQ_ERRSOTHS4_MASK BIT_MASK(3)
-#define CAL_CSI2_COMPLEXIO_IRQ_ERRSOTHS5_MASK BIT_MASK(4)
-#define CAL_CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS1_MASK BIT_MASK(5)
-#define CAL_CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS2_MASK BIT_MASK(6)
-#define CAL_CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS3_MASK BIT_MASK(7)
-#define CAL_CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS4_MASK BIT_MASK(8)
-#define CAL_CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS5_MASK BIT_MASK(9)
-#define CAL_CSI2_COMPLEXIO_IRQ_ERRESC1_MASK BIT_MASK(10)
-#define CAL_CSI2_COMPLEXIO_IRQ_ERRESC2_MASK BIT_MASK(11)
-#define CAL_CSI2_COMPLEXIO_IRQ_ERRESC3_MASK BIT_MASK(12)
-#define CAL_CSI2_COMPLEXIO_IRQ_ERRESC4_MASK BIT_MASK(13)
-#define CAL_CSI2_COMPLEXIO_IRQ_ERRESC5_MASK BIT_MASK(14)
-#define CAL_CSI2_COMPLEXIO_IRQ_ERRCONTROL1_MASK BIT_MASK(15)
-#define CAL_CSI2_COMPLEXIO_IRQ_ERRCONTROL2_MASK BIT_MASK(16)
-#define CAL_CSI2_COMPLEXIO_IRQ_ERRCONTROL3_MASK BIT_MASK(17)
-#define CAL_CSI2_COMPLEXIO_IRQ_ERRCONTROL4_MASK BIT_MASK(18)
-#define CAL_CSI2_COMPLEXIO_IRQ_ERRCONTROL5_MASK BIT_MASK(19)
-#define CAL_CSI2_COMPLEXIO_IRQ_STATEULPM1_MASK BIT_MASK(20)
-#define CAL_CSI2_COMPLEXIO_IRQ_STATEULPM2_MASK BIT_MASK(21)
-#define CAL_CSI2_COMPLEXIO_IRQ_STATEULPM3_MASK BIT_MASK(22)
-#define CAL_CSI2_COMPLEXIO_IRQ_STATEULPM4_MASK BIT_MASK(23)
-#define CAL_CSI2_COMPLEXIO_IRQ_STATEULPM5_MASK BIT_MASK(24)
-#define CAL_CSI2_COMPLEXIO_IRQ_STATEALLULPMENTER_MASK BIT_MASK(25)
-#define CAL_CSI2_COMPLEXIO_IRQ_STATEALLULPMEXIT_MASK BIT_MASK(26)
-#define CAL_CSI2_COMPLEXIO_IRQ_FIFO_OVR_MASK BIT_MASK(27)
-#define CAL_CSI2_COMPLEXIO_IRQ_SHORT_PACKET_MASK BIT_MASK(28)
-#define CAL_CSI2_COMPLEXIO_IRQ_ECC_NO_CORRECTION_MASK BIT_MASK(30)
+#define CAL_CSI2_COMPLEXIO_IRQ_ERRSOTHS1_MASK BIT(0)
+#define CAL_CSI2_COMPLEXIO_IRQ_ERRSOTHS2_MASK BIT(1)
+#define CAL_CSI2_COMPLEXIO_IRQ_ERRSOTHS3_MASK BIT(2)
+#define CAL_CSI2_COMPLEXIO_IRQ_ERRSOTHS4_MASK BIT(3)
+#define CAL_CSI2_COMPLEXIO_IRQ_ERRSOTHS5_MASK BIT(4)
+#define CAL_CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS1_MASK BIT(5)
+#define CAL_CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS2_MASK BIT(6)
+#define CAL_CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS3_MASK BIT(7)
+#define CAL_CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS4_MASK BIT(8)
+#define CAL_CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS5_MASK BIT(9)
+#define CAL_CSI2_COMPLEXIO_IRQ_ERRESC1_MASK BIT(10)
+#define CAL_CSI2_COMPLEXIO_IRQ_ERRESC2_MASK BIT(11)
+#define CAL_CSI2_COMPLEXIO_IRQ_ERRESC3_MASK BIT(12)
+#define CAL_CSI2_COMPLEXIO_IRQ_ERRESC4_MASK BIT(13)
+#define CAL_CSI2_COMPLEXIO_IRQ_ERRESC5_MASK BIT(14)
+#define CAL_CSI2_COMPLEXIO_IRQ_ERRCONTROL1_MASK BIT(15)
+#define CAL_CSI2_COMPLEXIO_IRQ_ERRCONTROL2_MASK BIT(16)
+#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)
+#define CAL_CSI2_COMPLEXIO_IRQ_STATEULPM4_MASK BIT(23)
+#define CAL_CSI2_COMPLEXIO_IRQ_STATEULPM5_MASK BIT(24)
+#define CAL_CSI2_COMPLEXIO_IRQ_STATEALLULPMENTER_MASK BIT(25)
+#define CAL_CSI2_COMPLEXIO_IRQ_STATEALLULPMEXIT_MASK BIT(26)
+#define CAL_CSI2_COMPLEXIO_IRQ_FIFO_OVR_MASK BIT(27)
+#define CAL_CSI2_COMPLEXIO_IRQ_SHORT_PACKET_MASK BIT(28)
+#define CAL_CSI2_COMPLEXIO_IRQ_ECC_NO_CORRECTION_MASK BIT(30)
#define CAL_CSI2_TIMING_STOP_STATE_COUNTER_IO1_MASK GENMASK(12, 0)
-#define CAL_CSI2_TIMING_STOP_STATE_X4_IO1_MASK BIT_MASK(13)
-#define CAL_CSI2_TIMING_STOP_STATE_X16_IO1_MASK BIT_MASK(14)
-#define CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK BIT_MASK(15)
+#define CAL_CSI2_TIMING_STOP_STATE_X4_IO1_MASK BIT(13)
+#define CAL_CSI2_TIMING_STOP_STATE_X16_IO1_MASK BIT(14)
+#define CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK BIT(15)
-#define CAL_CSI2_VC_IRQ_FS_IRQ_0_MASK BIT_MASK(0)
-#define CAL_CSI2_VC_IRQ_FE_IRQ_0_MASK BIT_MASK(1)
-#define CAL_CSI2_VC_IRQ_LS_IRQ_0_MASK BIT_MASK(2)
-#define CAL_CSI2_VC_IRQ_LE_IRQ_0_MASK BIT_MASK(3)
-#define CAL_CSI2_VC_IRQ_CS_IRQ_0_MASK BIT_MASK(4)
-#define CAL_CSI2_VC_IRQ_ECC_CORRECTION0_IRQ_0_MASK BIT_MASK(5)
-#define CAL_CSI2_VC_IRQ_FS_IRQ_1_MASK BIT_MASK(8)
-#define CAL_CSI2_VC_IRQ_FE_IRQ_1_MASK BIT_MASK(9)
-#define CAL_CSI2_VC_IRQ_LS_IRQ_1_MASK BIT_MASK(10)
-#define CAL_CSI2_VC_IRQ_LE_IRQ_1_MASK BIT_MASK(11)
-#define CAL_CSI2_VC_IRQ_CS_IRQ_1_MASK BIT_MASK(12)
-#define CAL_CSI2_VC_IRQ_ECC_CORRECTION0_IRQ_1_MASK BIT_MASK(13)
-#define CAL_CSI2_VC_IRQ_FS_IRQ_2_MASK BIT_MASK(16)
-#define CAL_CSI2_VC_IRQ_FE_IRQ_2_MASK BIT_MASK(17)
-#define CAL_CSI2_VC_IRQ_LS_IRQ_2_MASK BIT_MASK(18)
-#define CAL_CSI2_VC_IRQ_LE_IRQ_2_MASK BIT_MASK(19)
-#define CAL_CSI2_VC_IRQ_CS_IRQ_2_MASK BIT_MASK(20)
-#define CAL_CSI2_VC_IRQ_ECC_CORRECTION0_IRQ_2_MASK BIT_MASK(21)
-#define CAL_CSI2_VC_IRQ_FS_IRQ_3_MASK BIT_MASK(24)
-#define CAL_CSI2_VC_IRQ_FE_IRQ_3_MASK BIT_MASK(25)
-#define CAL_CSI2_VC_IRQ_LS_IRQ_3_MASK BIT_MASK(26)
-#define CAL_CSI2_VC_IRQ_LE_IRQ_3_MASK BIT_MASK(27)
-#define CAL_CSI2_VC_IRQ_CS_IRQ_3_MASK BIT_MASK(28)
-#define CAL_CSI2_VC_IRQ_ECC_CORRECTION0_IRQ_3_MASK BIT_MASK(29)
+#define CAL_CSI2_VC_IRQ_FS_IRQ_0_MASK BIT(0)
+#define CAL_CSI2_VC_IRQ_FE_IRQ_0_MASK BIT(1)
+#define CAL_CSI2_VC_IRQ_LS_IRQ_0_MASK BIT(2)
+#define CAL_CSI2_VC_IRQ_LE_IRQ_0_MASK BIT(3)
+#define CAL_CSI2_VC_IRQ_CS_IRQ_0_MASK BIT(4)
+#define CAL_CSI2_VC_IRQ_ECC_CORRECTION0_IRQ_0_MASK BIT(5)
+#define CAL_CSI2_VC_IRQ_FS_IRQ_1_MASK BIT(8)
+#define CAL_CSI2_VC_IRQ_FE_IRQ_1_MASK BIT(9)
+#define CAL_CSI2_VC_IRQ_LS_IRQ_1_MASK BIT(10)
+#define CAL_CSI2_VC_IRQ_LE_IRQ_1_MASK BIT(11)
+#define CAL_CSI2_VC_IRQ_CS_IRQ_1_MASK BIT(12)
+#define CAL_CSI2_VC_IRQ_ECC_CORRECTION0_IRQ_1_MASK BIT(13)
+#define CAL_CSI2_VC_IRQ_FS_IRQ_2_MASK BIT(16)
+#define CAL_CSI2_VC_IRQ_FE_IRQ_2_MASK BIT(17)
+#define CAL_CSI2_VC_IRQ_LS_IRQ_2_MASK BIT(18)
+#define CAL_CSI2_VC_IRQ_LE_IRQ_2_MASK BIT(19)
+#define CAL_CSI2_VC_IRQ_CS_IRQ_2_MASK BIT(20)
+#define CAL_CSI2_VC_IRQ_ECC_CORRECTION0_IRQ_2_MASK BIT(21)
+#define CAL_CSI2_VC_IRQ_FS_IRQ_3_MASK BIT(24)
+#define CAL_CSI2_VC_IRQ_FE_IRQ_3_MASK BIT(25)
+#define CAL_CSI2_VC_IRQ_LS_IRQ_3_MASK BIT(26)
+#define CAL_CSI2_VC_IRQ_LE_IRQ_3_MASK BIT(27)
+#define CAL_CSI2_VC_IRQ_CS_IRQ_3_MASK BIT(28)
+#define CAL_CSI2_VC_IRQ_ECC_CORRECTION0_IRQ_3_MASK BIT(29)
#define CAL_CSI2_CTX_DT_MASK GENMASK(5, 0)
#define CAL_CSI2_CTX_VC_MASK GENMASK(7, 6)
#define CAL_CSI2_CTX_CPORT_MASK GENMASK(12, 8)
-#define CAL_CSI2_CTX_ATT_MASK BIT_MASK(13)
+#define CAL_CSI2_CTX_ATT_MASK BIT(13)
#define CAL_CSI2_CTX_ATT_PIX 0
#define CAL_CSI2_CTX_ATT 1
-#define CAL_CSI2_CTX_PACK_MODE_MASK BIT_MASK(14)
+#define CAL_CSI2_CTX_PACK_MODE_MASK BIT(14)
#define CAL_CSI2_CTX_PACK_MODE_LINE 0
#define CAL_CSI2_CTX_PACK_MODE_FRAME 1
#define CAL_CSI2_CTX_LINES_MASK GENMASK(29, 16)
@@ -445,7 +459,7 @@
#define CAL_CSI2_PHY_REG0_THS_SETTLE_MASK GENMASK(7, 0)
#define CAL_CSI2_PHY_REG0_THS_TERM_MASK GENMASK(15, 8)
-#define CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_MASK BIT_MASK(24)
+#define CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_MASK BIT(24)
#define CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_DISABLE 1
#define CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_ENABLE 0
@@ -453,24 +467,26 @@
#define CAL_CSI2_PHY_REG1_CTRLCLK_DIV_FACTOR_MASK GENMASK(9, 8)
#define CAL_CSI2_PHY_REG1_DPHY_HS_SYNC_PATTERN_MASK GENMASK(17, 10)
#define CAL_CSI2_PHY_REG1_TCLK_TERM_MASK GENMASK(24, 18)
-#define CAL_CSI2_PHY_REG1_CLOCK_MISS_DETECTOR_STATUS_MASK BIT_MASK(25)
+#define CAL_CSI2_PHY_REG1_CLOCK_MISS_DETECTOR_STATUS_MASK BIT(25)
#define CAL_CSI2_PHY_REG1_CLOCK_MISS_DETECTOR_STATUS_ERROR 1
#define CAL_CSI2_PHY_REG1_CLOCK_MISS_DETECTOR_STATUS_SUCCESS 0
#define CAL_CSI2_PHY_REG1_RESET_DONE_STATUS_MASK GENMASK(29, 28)
+#define CAL_CSI2_PHY_REG10_I933_LDO_DISABLE_MASK BIT(6)
+
#define CAL_CSI2_PHY_REG2_CCP2_SYNC_PATTERN_MASK GENMASK(23, 0)
#define CAL_CSI2_PHY_REG2_TRIGGER_CMD_RXTRIGESC3_MASK GENMASK(25, 24)
#define CAL_CSI2_PHY_REG2_TRIGGER_CMD_RXTRIGESC2_MASK GENMASK(27, 26)
#define CAL_CSI2_PHY_REG2_TRIGGER_CMD_RXTRIGESC1_MASK GENMASK(29, 28)
#define CAL_CSI2_PHY_REG2_TRIGGER_CMD_RXTRIGESC0_MASK GENMASK(31, 30)
-#define CM_CAMERRX_CTRL_CSI1_CTRLCLKEN_MASK BIT_MASK(0)
+#define CM_CAMERRX_CTRL_CSI1_CTRLCLKEN_MASK BIT(0)
#define CM_CAMERRX_CTRL_CSI1_CAMMODE_MASK GENMASK(2, 1)
#define CM_CAMERRX_CTRL_CSI1_LANEENABLE_MASK GENMASK(4, 3)
-#define CM_CAMERRX_CTRL_CSI1_MODE_MASK BIT_MASK(5)
-#define CM_CAMERRX_CTRL_CSI0_CTRLCLKEN_MASK BIT_MASK(10)
+#define CM_CAMERRX_CTRL_CSI1_MODE_MASK BIT(5)
+#define CM_CAMERRX_CTRL_CSI0_CTRLCLKEN_MASK BIT(10)
#define CM_CAMERRX_CTRL_CSI0_CAMMODE_MASK GENMASK(12, 11)
#define CM_CAMERRX_CTRL_CSI0_LANEENABLE_MASK GENMASK(16, 13)
-#define CM_CAMERRX_CTRL_CSI0_MODE_MASK BIT_MASK(17)
+#define CM_CAMERRX_CTRL_CSI0_MODE_MASK BIT(17)
#endif
diff --git a/drivers/media/platform/ti-vpe/csc.c b/drivers/media/platform/ti-vpe/csc.c
index eda2a59..f4e0cf7 100644
--- a/drivers/media/platform/ti-vpe/csc.c
+++ b/drivers/media/platform/ti-vpe/csc.c
@@ -15,76 +15,96 @@
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/videodev2.h>
+#include <media/v4l2-common.h>
#include "csc.h"
/*
- * 16 coefficients in the order:
+ * 12 coefficients in the order:
* a0, b0, c0, a1, b1, c1, a2, b2, c2, d0, d1, d2
- * (we may need to pass non-default values from user space later on, we might
- * need to make the coefficient struct more easy to populate)
*/
-struct colorspace_coeffs {
- u16 sd[12];
- u16 hd[12];
+struct quantization {
+ u16 coeff[12];
};
-/* VIDEO_RANGE: limited range, GRAPHICS_RANGE: full range */
-#define CSC_COEFFS_VIDEO_RANGE_Y2R 0
-#define CSC_COEFFS_GRAPHICS_RANGE_Y2R 1
-#define CSC_COEFFS_VIDEO_RANGE_R2Y 2
-#define CSC_COEFFS_GRAPHICS_RANGE_R2Y 3
+struct colorspace {
+ struct quantization limited;
+ struct quantization full;
+};
+
+struct encoding_direction {
+ struct colorspace r601;
+ struct colorspace r709;
+};
+
+struct csc_coeffs {
+ struct encoding_direction y2r;
+ struct encoding_direction r2y;
+};
/* default colorspace coefficients */
-static struct colorspace_coeffs colorspace_coeffs[4] = {
- [CSC_COEFFS_VIDEO_RANGE_Y2R] = {
- {
- /* SDTV */
- 0x0400, 0x0000, 0x057D, 0x0400, 0x1EA7, 0x1D35,
- 0x0400, 0x06EF, 0x1FFE, 0x0D40, 0x0210, 0x0C88,
+static struct csc_coeffs csc_coeffs = {
+ .y2r = {
+ .r601 = {
+ .limited = {
+ { /* SDTV */
+ 0x0400, 0x0000, 0x057D, 0x0400, 0x1EA7, 0x1D35,
+ 0x0400, 0x06EF, 0x1FFE, 0x0D40, 0x0210, 0x0C88,
+ }
+ },
+ .full = {
+ { /* SDTV */
+ 0x04A8, 0x1FFE, 0x0662, 0x04A8, 0x1E6F, 0x1CBF,
+ 0x04A8, 0x0812, 0x1FFF, 0x0C84, 0x0220, 0x0BAC,
+ }
+ },
},
- {
- /* HDTV */
- 0x0400, 0x0000, 0x0629, 0x0400, 0x1F45, 0x1E2B,
- 0x0400, 0x0742, 0x0000, 0x0CEC, 0x0148, 0x0C60,
+ .r709 = {
+ .limited = {
+ { /* HDTV */
+ 0x0400, 0x0000, 0x0629, 0x0400, 0x1F45, 0x1E2B,
+ 0x0400, 0x0742, 0x0000, 0x0CEC, 0x0148, 0x0C60,
+ }
+ },
+ .full = {
+ { /* HDTV */
+ 0x04A8, 0x0000, 0x072C, 0x04A8, 0x1F26, 0x1DDE,
+ 0x04A8, 0x0873, 0x0000, 0x0C20, 0x0134, 0x0B7C,
+ }
+ },
},
},
- [CSC_COEFFS_GRAPHICS_RANGE_Y2R] = {
- {
- /* SDTV */
- 0x04A8, 0x1FFE, 0x0662, 0x04A8, 0x1E6F, 0x1CBF,
- 0x04A8, 0x0812, 0x1FFF, 0x0C84, 0x0220, 0x0BAC,
+ .r2y = {
+ .r601 = {
+ .limited = {
+ { /* SDTV */
+ 0x0132, 0x0259, 0x0075, 0x1F50, 0x1EA5, 0x020B,
+ 0x020B, 0x1E4A, 0x1FAB, 0x0000, 0x0200, 0x0200,
+ }
+ },
+ .full = {
+ { /* SDTV */
+ 0x0107, 0x0204, 0x0064, 0x1F68, 0x1ED6, 0x01C2,
+ 0x01C2, 0x1E87, 0x1FB7, 0x0040, 0x0200, 0x0200,
+ }
+ },
},
- {
- /* HDTV */
- 0x04A8, 0x0000, 0x072C, 0x04A8, 0x1F26, 0x1DDE,
- 0x04A8, 0x0873, 0x0000, 0x0C20, 0x0134, 0x0B7C,
+ .r709 = {
+ .limited = {
+ { /* HDTV */
+ 0x00DA, 0x02DC, 0x004A, 0x1F88, 0x1E6C, 0x020C,
+ 0x020C, 0x1E24, 0x1FD0, 0x0000, 0x0200, 0x0200,
+ }
+ },
+ .full = {
+ { /* HDTV */
+ 0x00bb, 0x0275, 0x003f, 0x1f99, 0x1ea5, 0x01c2,
+ 0x01c2, 0x1e67, 0x1fd7, 0x0040, 0x0200, 0x0200,
+ }
+ },
},
},
- [CSC_COEFFS_VIDEO_RANGE_R2Y] = {
- {
- /* SDTV */
- 0x0132, 0x0259, 0x0075, 0x1F50, 0x1EA5, 0x020B,
- 0x020B, 0x1E4A, 0x1FAB, 0x0000, 0x0200, 0x0200,
- },
- {
- /* HDTV */
- 0x00DA, 0x02DC, 0x004A, 0x1F88, 0x1E6C, 0x020C,
- 0x020C, 0x1E24, 0x1FD0, 0x0000, 0x0200, 0x0200,
- },
- },
- [CSC_COEFFS_GRAPHICS_RANGE_R2Y] = {
- {
- /* SDTV */
- 0x0107, 0x0204, 0x0064, 0x1F68, 0x1ED6, 0x01C2,
- 0x01C2, 0x1E87, 0x1FB7, 0x0040, 0x0200, 0x0200,
- },
- {
- /* HDTV */
- 0x04A8, 0x0000, 0x072C, 0x04A8, 0x1F26, 0x1DDE,
- 0x04A8, 0x0873, 0x0000, 0x0C20, 0x0134, 0x0B7C,
- },
- },
+
};
void csc_dump_regs(struct csc_data *csc)
@@ -117,46 +137,106 @@
* set the color space converter coefficient shadow register values
*/
void csc_set_coeff(struct csc_data *csc, u32 *csc_reg0,
- enum v4l2_colorspace src_colorspace,
- enum v4l2_colorspace dst_colorspace)
+ struct v4l2_format *src_fmt, struct v4l2_format *dst_fmt)
{
u32 *csc_reg5 = csc_reg0 + 5;
u32 *shadow_csc = csc_reg0;
- struct colorspace_coeffs *sd_hd_coeffs;
u16 *coeff, *end_coeff;
- enum v4l2_colorspace yuv_colorspace;
- int sel = 0;
+ const struct v4l2_pix_format *pix;
+ const struct v4l2_pix_format_mplane *mp;
+ const struct v4l2_format_info *src_finfo, *dst_finfo;
+ enum v4l2_ycbcr_encoding src_ycbcr_enc, dst_ycbcr_enc;
+ enum v4l2_quantization src_quantization, dst_quantization;
+ u32 src_pixelformat, dst_pixelformat;
- /*
- * support only graphics data range(full range) for now, a control ioctl
- * would be nice here
- */
- /* Y2R */
- if (dst_colorspace == V4L2_COLORSPACE_SRGB &&
- (src_colorspace == V4L2_COLORSPACE_SMPTE170M ||
- src_colorspace == V4L2_COLORSPACE_REC709)) {
+ if (V4L2_TYPE_IS_MULTIPLANAR(src_fmt->type)) {
+ mp = &src_fmt->fmt.pix_mp;
+ src_pixelformat = mp->pixelformat;
+ src_ycbcr_enc = mp->ycbcr_enc;
+ src_quantization = mp->quantization;
+ } else {
+ pix = &src_fmt->fmt.pix;
+ src_pixelformat = pix->pixelformat;
+ src_ycbcr_enc = pix->ycbcr_enc;
+ src_quantization = pix->quantization;
+ }
+
+ if (V4L2_TYPE_IS_MULTIPLANAR(dst_fmt->type)) {
+ mp = &dst_fmt->fmt.pix_mp;
+ dst_pixelformat = mp->pixelformat;
+ dst_ycbcr_enc = mp->ycbcr_enc;
+ dst_quantization = mp->quantization;
+ } else {
+ pix = &dst_fmt->fmt.pix;
+ dst_pixelformat = pix->pixelformat;
+ dst_ycbcr_enc = pix->ycbcr_enc;
+ dst_quantization = pix->quantization;
+ }
+
+ src_finfo = v4l2_format_info(src_pixelformat);
+ dst_finfo = v4l2_format_info(dst_pixelformat);
+
+ if (v4l2_is_format_yuv(src_finfo) &&
+ v4l2_is_format_rgb(dst_finfo)) {
/* Y2R */
- sel = 1;
- yuv_colorspace = src_colorspace;
- } else if ((dst_colorspace == V4L2_COLORSPACE_SMPTE170M ||
- dst_colorspace == V4L2_COLORSPACE_REC709) &&
- src_colorspace == V4L2_COLORSPACE_SRGB) {
+
+ /*
+ * These are not the standard default values but are
+ * set this way for historical compatibility
+ */
+ if (src_ycbcr_enc == V4L2_YCBCR_ENC_DEFAULT)
+ src_ycbcr_enc = V4L2_YCBCR_ENC_601;
+
+ if (src_quantization == V4L2_QUANTIZATION_DEFAULT)
+ src_quantization = V4L2_QUANTIZATION_FULL_RANGE;
+
+ if (src_ycbcr_enc == V4L2_YCBCR_ENC_601) {
+ if (src_quantization == V4L2_QUANTIZATION_FULL_RANGE)
+ coeff = csc_coeffs.y2r.r601.full.coeff;
+ else
+ coeff = csc_coeffs.y2r.r601.limited.coeff;
+ } else if (src_ycbcr_enc == V4L2_YCBCR_ENC_709) {
+ if (src_quantization == V4L2_QUANTIZATION_FULL_RANGE)
+ coeff = csc_coeffs.y2r.r709.full.coeff;
+ else
+ coeff = csc_coeffs.y2r.r709.limited.coeff;
+ } else {
+ /* Should never reach this, but it keeps gcc happy */
+ coeff = csc_coeffs.y2r.r601.full.coeff;
+ }
+ } else if (v4l2_is_format_rgb(src_finfo) &&
+ v4l2_is_format_yuv(dst_finfo)) {
/* R2Y */
- sel = 3;
- yuv_colorspace = dst_colorspace;
+
+ /*
+ * These are not the standard default values but are
+ * set this way for historical compatibility
+ */
+ if (dst_ycbcr_enc == V4L2_YCBCR_ENC_DEFAULT)
+ dst_ycbcr_enc = V4L2_YCBCR_ENC_601;
+
+ if (dst_quantization == V4L2_QUANTIZATION_DEFAULT)
+ dst_quantization = V4L2_QUANTIZATION_FULL_RANGE;
+
+ if (dst_ycbcr_enc == V4L2_YCBCR_ENC_601) {
+ if (dst_quantization == V4L2_QUANTIZATION_FULL_RANGE)
+ coeff = csc_coeffs.r2y.r601.full.coeff;
+ else
+ coeff = csc_coeffs.r2y.r601.limited.coeff;
+ } else if (dst_ycbcr_enc == V4L2_YCBCR_ENC_709) {
+ if (dst_quantization == V4L2_QUANTIZATION_FULL_RANGE)
+ coeff = csc_coeffs.r2y.r709.full.coeff;
+ else
+ coeff = csc_coeffs.r2y.r709.limited.coeff;
+ } else {
+ /* Should never reach this, but it keeps gcc happy */
+ coeff = csc_coeffs.r2y.r601.full.coeff;
+ }
} else {
*csc_reg5 |= CSC_BYPASS;
return;
}
- sd_hd_coeffs = &colorspace_coeffs[sel];
-
- /* select between SD or HD coefficients */
- if (yuv_colorspace == V4L2_COLORSPACE_SMPTE170M)
- coeff = sd_hd_coeffs->sd;
- else
- coeff = sd_hd_coeffs->hd;
-
end_coeff = coeff + 12;
for (; coeff < end_coeff; coeff += 2)
diff --git a/drivers/media/platform/ti-vpe/csc.h b/drivers/media/platform/ti-vpe/csc.h
index de9a58a..af2e86b 100644
--- a/drivers/media/platform/ti-vpe/csc.h
+++ b/drivers/media/platform/ti-vpe/csc.h
@@ -58,8 +58,8 @@
void csc_dump_regs(struct csc_data *csc);
void csc_set_coeff_bypass(struct csc_data *csc, u32 *csc_reg5);
void csc_set_coeff(struct csc_data *csc, u32 *csc_reg0,
- enum v4l2_colorspace src_colorspace,
- enum v4l2_colorspace dst_colorspace);
+ struct v4l2_format *src_fmt, struct v4l2_format *dst_fmt);
+
struct csc_data *csc_create(struct platform_device *pdev, const char *res_name);
#endif
diff --git a/drivers/media/platform/ti-vpe/vpdma.c b/drivers/media/platform/ti-vpe/vpdma.c
index 53d27cd..2e5148a 100644
--- a/drivers/media/platform/ti-vpe/vpdma.c
+++ b/drivers/media/platform/ti-vpe/vpdma.c
@@ -56,6 +56,11 @@
.data_type = DATA_TYPE_C420,
.depth = 4,
},
+ [VPDMA_DATA_FMT_CB420] = {
+ .type = VPDMA_DATA_FMT_TYPE_YUV,
+ .data_type = DATA_TYPE_CB420,
+ .depth = 4,
+ },
[VPDMA_DATA_FMT_YCR422] = {
.type = VPDMA_DATA_FMT_TYPE_YUV,
.data_type = DATA_TYPE_YCR422,
@@ -759,7 +764,7 @@
pr_debug("word1: line_length = %d, xfer_height = %d\n",
dtd_get_line_length(dtd), dtd_get_xfer_height(dtd));
- pr_debug("word2: start_addr = %pad\n", &dtd->start_addr);
+ pr_debug("word2: start_addr = %x\n", dtd->start_addr);
pr_debug("word3: pkt_type = %d, mode = %d, dir = %d, chan = %d, pri = %d, next_chan = %d\n",
dtd_get_pkt_type(dtd),
@@ -825,7 +830,8 @@
channel = next_chan = raw_vpdma_chan;
if (fmt->type == VPDMA_DATA_FMT_TYPE_YUV &&
- fmt->data_type == DATA_TYPE_C420) {
+ (fmt->data_type == DATA_TYPE_C420 ||
+ fmt->data_type == DATA_TYPE_CB420)) {
rect.height >>= 1;
rect.top >>= 1;
depth = 8;
@@ -893,7 +899,8 @@
channel = next_chan = chan_info[chan].num;
if (fmt->type == VPDMA_DATA_FMT_TYPE_YUV &&
- fmt->data_type == DATA_TYPE_C420) {
+ (fmt->data_type == DATA_TYPE_C420 ||
+ fmt->data_type == DATA_TYPE_CB420)) {
rect.height >>= 1;
rect.top >>= 1;
depth = 8;
diff --git a/drivers/media/platform/ti-vpe/vpdma.h b/drivers/media/platform/ti-vpe/vpdma.h
index 9bacfd6..393fcbb 100644
--- a/drivers/media/platform/ti-vpe/vpdma.h
+++ b/drivers/media/platform/ti-vpe/vpdma.h
@@ -72,6 +72,7 @@
VPDMA_DATA_FMT_C444,
VPDMA_DATA_FMT_C422,
VPDMA_DATA_FMT_C420,
+ VPDMA_DATA_FMT_CB420,
VPDMA_DATA_FMT_YCR422,
VPDMA_DATA_FMT_YC444,
VPDMA_DATA_FMT_CRY422,
diff --git a/drivers/media/platform/ti-vpe/vpdma_priv.h b/drivers/media/platform/ti-vpe/vpdma_priv.h
index c488609..0bbee45 100644
--- a/drivers/media/platform/ti-vpe/vpdma_priv.h
+++ b/drivers/media/platform/ti-vpe/vpdma_priv.h
@@ -92,6 +92,7 @@
#define DATA_TYPE_C444 0x4
#define DATA_TYPE_C422 0x5
#define DATA_TYPE_C420 0x6
+#define DATA_TYPE_CB420 0x16
#define DATA_TYPE_YC444 0x8
#define DATA_TYPE_YCB422 0x7
#define DATA_TYPE_YCR422 0x17
@@ -165,11 +166,11 @@
u32 xfer_length_height;
u32 w1;
};
- dma_addr_t start_addr;
+ u32 start_addr;
u32 pkt_ctl;
union {
u32 frame_width_height; /* inbound */
- dma_addr_t desc_write_addr; /* outbound */
+ u32 desc_write_addr; /* outbound */
};
union {
u32 start_h_v; /* inbound */
diff --git a/drivers/media/platform/ti-vpe/vpe.c b/drivers/media/platform/ti-vpe/vpe.c
index 817bd13..779dd74 100644
--- a/drivers/media/platform/ti-vpe/vpe.c
+++ b/drivers/media/platform/ti-vpe/vpe.c
@@ -52,7 +52,7 @@
#define MIN_W 32
#define MIN_H 32
#define MAX_W 2048
-#define MAX_H 1184
+#define MAX_H 2048
/* required alignments */
#define S_ALIGN 0 /* multiple of 1 */
@@ -249,6 +249,14 @@
},
},
{
+ .fourcc = V4L2_PIX_FMT_NV21,
+ .types = VPE_FMT_TYPE_CAPTURE | VPE_FMT_TYPE_OUTPUT,
+ .coplanar = 1,
+ .vpdma_fmt = { &vpdma_yuv_fmts[VPDMA_DATA_FMT_Y420],
+ &vpdma_yuv_fmts[VPDMA_DATA_FMT_CB420],
+ },
+ },
+ {
.fourcc = V4L2_PIX_FMT_YUYV,
.types = VPE_FMT_TYPE_CAPTURE | VPE_FMT_TYPE_OUTPUT,
.coplanar = 0,
@@ -311,14 +319,9 @@
* there is one source queue and one destination queue for each m2m context.
*/
struct vpe_q_data {
- unsigned int width; /* frame width */
- unsigned int height; /* frame height */
- unsigned int nplanes; /* Current number of planes */
- unsigned int bytesperline[VPE_MAX_PLANES]; /* bytes per line in memory */
- enum v4l2_colorspace colorspace;
- enum v4l2_field field; /* supported field value */
+ /* current v4l2 format info */
+ struct v4l2_format format;
unsigned int flags;
- unsigned int sizeimage[VPE_MAX_PLANES]; /* image size in memory */
struct v4l2_rect c_rect; /* crop/compose rectangle */
struct vpe_fmt *fmt; /* format info */
};
@@ -328,9 +331,14 @@
#define Q_DATA_MODE_TILED BIT(1)
#define Q_DATA_INTERLACED_ALTERNATE BIT(2)
#define Q_DATA_INTERLACED_SEQ_TB BIT(3)
+#define Q_DATA_INTERLACED_SEQ_BT BIT(4)
+
+#define Q_IS_SEQ_XX (Q_DATA_INTERLACED_SEQ_TB | \
+ Q_DATA_INTERLACED_SEQ_BT)
#define Q_IS_INTERLACED (Q_DATA_INTERLACED_ALTERNATE | \
- Q_DATA_INTERLACED_SEQ_TB)
+ Q_DATA_INTERLACED_SEQ_TB | \
+ Q_DATA_INTERLACED_SEQ_BT)
enum {
Q_DATA_SRC = 0,
@@ -686,7 +694,8 @@
* Cfg Mode 1: YUV422 source, disable upsampler, DEI is de-interlacing.
*/
- if (fmt->fourcc == V4L2_PIX_FMT_NV12)
+ if (fmt->fourcc == V4L2_PIX_FMT_NV12 ||
+ fmt->fourcc == V4L2_PIX_FMT_NV21)
cfg_mode = 0;
write_field(us1_reg0, cfg_mode, VPE_US_MODE_MASK, VPE_US_MODE_SHIFT);
@@ -701,7 +710,8 @@
struct vpe_fmt *fmt = ctx->q_data[Q_DATA_SRC].fmt;
int line_mode = 1;
- if (fmt->fourcc == V4L2_PIX_FMT_NV12)
+ if (fmt->fourcc == V4L2_PIX_FMT_NV12 ||
+ fmt->fourcc == V4L2_PIX_FMT_NV21)
line_mode = 0; /* double lines to line buffer */
/* regs for now */
@@ -746,11 +756,12 @@
static void set_dst_registers(struct vpe_ctx *ctx)
{
struct vpe_mmr_adb *mmr_adb = ctx->mmr_adb.addr;
- enum v4l2_colorspace clrspc = ctx->q_data[Q_DATA_DST].colorspace;
struct vpe_fmt *fmt = ctx->q_data[Q_DATA_DST].fmt;
+ const struct v4l2_format_info *finfo;
u32 val = 0;
- if (clrspc == V4L2_COLORSPACE_SRGB) {
+ finfo = v4l2_format_info(fmt->fourcc);
+ if (v4l2_is_format_rgb(finfo)) {
val |= VPE_RGB_OUT_SELECT;
vpdma_set_bg_color(ctx->dev->vpdma,
(struct vpdma_data_format *)fmt->vpdma_fmt[0], 0xff);
@@ -763,7 +774,8 @@
*/
val |= VPE_DS_SRC_DEI_SCALER | VPE_CSC_SRC_DEI_SCALER;
- if (fmt->fourcc != V4L2_PIX_FMT_NV12)
+ if (fmt->fourcc != V4L2_PIX_FMT_NV12 &&
+ fmt->fourcc != V4L2_PIX_FMT_NV21)
val |= VPE_DS_BYPASS;
mmr_adb->out_fmt_reg[0] = val;
@@ -852,11 +864,13 @@
unsigned int src_h = s_q_data->c_rect.height;
unsigned int dst_w = d_q_data->c_rect.width;
unsigned int dst_h = d_q_data->c_rect.height;
+ struct v4l2_pix_format_mplane *spix;
size_t mv_buf_size;
int ret;
ctx->sequence = 0;
ctx->field = V4L2_FIELD_TOP;
+ spix = &s_q_data->format.fmt.pix_mp;
if ((s_q_data->flags & Q_IS_INTERLACED) &&
!(d_q_data->flags & Q_IS_INTERLACED)) {
@@ -871,9 +885,9 @@
* extra space will not be used by the de-interlacer, but will
* ensure that vpdma operates correctly
*/
- bytes_per_line = ALIGN((s_q_data->width * mv->depth) >> 3,
- VPDMA_STRIDE_ALIGN);
- mv_buf_size = bytes_per_line * s_q_data->height;
+ bytes_per_line = ALIGN((spix->width * mv->depth) >> 3,
+ VPDMA_STRIDE_ALIGN);
+ mv_buf_size = bytes_per_line * spix->height;
ctx->deinterlacing = true;
src_h <<= 1;
@@ -893,7 +907,7 @@
set_dei_regs(ctx);
csc_set_coeff(ctx->dev->csc, &mmr_adb->csc_regs[0],
- s_q_data->colorspace, d_q_data->colorspace);
+ &s_q_data->format, &d_q_data->format);
sc_set_hs_coeffs(ctx->dev->sc, ctx->sc_coeff_h.addr, src_w, dst_w);
sc_set_vs_coeffs(ctx->dev->sc, ctx->sc_coeff_v.addr, src_h, dst_h);
@@ -906,14 +920,6 @@
}
/*
- * Return the vpe_ctx structure for a given struct file
- */
-static struct vpe_ctx *file2ctx(struct file *file)
-{
- return container_of(file->private_data, struct vpe_ctx, fh);
-}
-
-/*
* mem2mem callbacks
*/
@@ -1015,6 +1021,7 @@
struct vpe_fmt *fmt = q_data->fmt;
const struct vpdma_data_format *vpdma_fmt;
int mv_buf_selector = !ctx->src_mv_buf_selector;
+ struct v4l2_pix_format_mplane *pix;
dma_addr_t dma_addr;
u32 flags = 0;
u32 offset = 0;
@@ -1024,21 +1031,23 @@
vpdma_fmt = &vpdma_misc_fmts[VPDMA_DATA_FMT_MV];
dma_addr = ctx->mv_buf_dma[mv_buf_selector];
q_data = &ctx->q_data[Q_DATA_SRC];
- stride = ALIGN((q_data->width * vpdma_fmt->depth) >> 3,
+ pix = &q_data->format.fmt.pix_mp;
+ stride = ALIGN((pix->width * vpdma_fmt->depth) >> 3,
VPDMA_STRIDE_ALIGN);
} else {
/* to incorporate interleaved formats */
int plane = fmt->coplanar ? p_data->vb_part : 0;
+ pix = &q_data->format.fmt.pix_mp;
vpdma_fmt = fmt->vpdma_fmt[plane];
/*
* If we are using a single plane buffer and
* we need to set a separate vpdma chroma channel.
*/
- if (q_data->nplanes == 1 && plane) {
+ if (pix->num_planes == 1 && plane) {
dma_addr = vb2_dma_contig_plane_dma_addr(vb, 0);
/* Compute required offset */
- offset = q_data->bytesperline[0] * q_data->height;
+ offset = pix->plane_fmt[0].bytesperline * pix->height;
} else {
dma_addr = vb2_dma_contig_plane_dma_addr(vb, plane);
/* Use address as is, no offset */
@@ -1052,7 +1061,7 @@
}
/* Apply the offset */
dma_addr += offset;
- stride = q_data->bytesperline[VPE_LUMA];
+ stride = pix->plane_fmt[VPE_LUMA].bytesperline;
}
if (q_data->flags & Q_DATA_FRAME_1D)
@@ -1063,7 +1072,7 @@
vpdma_set_max_size(ctx->dev->vpdma, VPDMA_MAX_SIZE1,
MAX_W, MAX_H);
- vpdma_add_out_dtd(&ctx->desc_list, q_data->width,
+ vpdma_add_out_dtd(&ctx->desc_list, pix->width,
stride, &q_data->c_rect,
vpdma_fmt, dma_addr, MAX_OUT_WIDTH_REG1,
MAX_OUT_HEIGHT_REG1, p_data->channel, flags);
@@ -1076,6 +1085,7 @@
struct vb2_buffer *vb = &ctx->src_vbs[p_data->vb_index]->vb2_buf;
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
struct vpe_fmt *fmt = q_data->fmt;
+ struct v4l2_pix_format_mplane *pix;
const struct vpdma_data_format *vpdma_fmt;
int mv_buf_selector = ctx->src_mv_buf_selector;
int field = vbuf->field == V4L2_FIELD_BOTTOM;
@@ -1085,10 +1095,11 @@
u32 offset = 0;
u32 stride;
+ pix = &q_data->format.fmt.pix_mp;
if (port == VPE_PORT_MV_IN) {
vpdma_fmt = &vpdma_misc_fmts[VPDMA_DATA_FMT_MV];
dma_addr = ctx->mv_buf_dma[mv_buf_selector];
- stride = ALIGN((q_data->width * vpdma_fmt->depth) >> 3,
+ stride = ALIGN((pix->width * vpdma_fmt->depth) >> 3,
VPDMA_STRIDE_ALIGN);
} else {
/* to incorporate interleaved formats */
@@ -1099,10 +1110,10 @@
* If we are using a single plane buffer and
* we need to set a separate vpdma chroma channel.
*/
- if (q_data->nplanes == 1 && plane) {
+ if (pix->num_planes == 1 && plane) {
dma_addr = vb2_dma_contig_plane_dma_addr(vb, 0);
/* Compute required offset */
- offset = q_data->bytesperline[0] * q_data->height;
+ offset = pix->plane_fmt[0].bytesperline * pix->height;
} else {
dma_addr = vb2_dma_contig_plane_dma_addr(vb, plane);
/* Use address as is, no offset */
@@ -1116,27 +1127,39 @@
}
/* Apply the offset */
dma_addr += offset;
- stride = q_data->bytesperline[VPE_LUMA];
+ stride = pix->plane_fmt[VPE_LUMA].bytesperline;
- if (q_data->flags & Q_DATA_INTERLACED_SEQ_TB) {
- /*
- * Use top or bottom field from same vb alternately
- * f,f-1,f-2 = TBT when seq is even
- * f,f-1,f-2 = BTB when seq is odd
- */
- field = (p_data->vb_index + (ctx->sequence % 2)) % 2;
+ /*
+ * field used in VPDMA desc = 0 (top) / 1 (bottom)
+ * Use top or bottom field from same vb alternately
+ * For each de-interlacing operation, f,f-1,f-2 should be one
+ * of TBT or BTB
+ */
+ if (q_data->flags & Q_DATA_INTERLACED_SEQ_TB ||
+ q_data->flags & Q_DATA_INTERLACED_SEQ_BT) {
+ /* Select initial value based on format */
+ if (q_data->flags & Q_DATA_INTERLACED_SEQ_BT)
+ field = 1;
+ else
+ field = 0;
+
+ /* Toggle for each vb_index and each operation */
+ field = (field + p_data->vb_index + ctx->sequence) % 2;
if (field) {
- /*
- * bottom field of a SEQ_TB buffer
- * Skip the top field data by
- */
- int height = q_data->height / 2;
- int bpp = fmt->fourcc == V4L2_PIX_FMT_NV12 ?
- 1 : (vpdma_fmt->depth >> 3);
+ int height = pix->height / 2;
+ int bpp;
+
+ if (fmt->fourcc == V4L2_PIX_FMT_NV12 ||
+ fmt->fourcc == V4L2_PIX_FMT_NV21)
+ bpp = 1;
+ else
+ bpp = vpdma_fmt->depth >> 3;
+
if (plane)
height /= 2;
- dma_addr += q_data->width * height * bpp;
+
+ dma_addr += pix->width * height * bpp;
}
}
}
@@ -1149,10 +1172,11 @@
frame_width = q_data->c_rect.width;
frame_height = q_data->c_rect.height;
- if (p_data->vb_part && fmt->fourcc == V4L2_PIX_FMT_NV12)
+ if (p_data->vb_part && (fmt->fourcc == V4L2_PIX_FMT_NV12 ||
+ fmt->fourcc == V4L2_PIX_FMT_NV21))
frame_height /= 2;
- vpdma_add_in_dtd(&ctx->desc_list, q_data->width, stride,
+ vpdma_add_in_dtd(&ctx->desc_list, pix->width, stride,
&q_data->c_rect, vpdma_fmt, dma_addr,
p_data->channel, field, flags, frame_width,
frame_height, 0, 0);
@@ -1189,13 +1213,18 @@
struct sc_data *sc = ctx->dev->sc;
struct vpe_q_data *d_q_data = &ctx->q_data[Q_DATA_DST];
struct vpe_q_data *s_q_data = &ctx->q_data[Q_DATA_SRC];
+ const struct v4l2_format_info *d_finfo;
- if (ctx->deinterlacing && s_q_data->flags & Q_DATA_INTERLACED_SEQ_TB &&
- ctx->sequence % 2 == 0) {
- /* When using SEQ_TB buffers, When using it first time,
- * No need to remove the buffer as the next field is present
- * in the same buffer. (so that job_ready won't fail)
- * It will be removed when using bottom field
+ d_finfo = v4l2_format_info(d_q_data->fmt->fourcc);
+
+ if (ctx->deinterlacing && s_q_data->flags & Q_IS_SEQ_XX &&
+ ctx->sequence % 2 == 0) {
+ /* When using SEQ_XX type buffers, each buffer has two fields
+ * each buffer has two fields (top & bottom)
+ * Removing one buffer is actually getting two fields
+ * Alternate between two operations:-
+ * Even : consume one field but DO NOT REMOVE from queue
+ * Odd : consume other field and REMOVE from queue
*/
ctx->src_vbs[0] = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
WARN_ON(ctx->src_vbs[0] == NULL);
@@ -1259,7 +1288,7 @@
if (ctx->deinterlacing)
add_out_dtd(ctx, VPE_PORT_MV_OUT);
- if (d_q_data->colorspace == V4L2_COLORSPACE_SRGB) {
+ if (v4l2_is_format_rgb(d_finfo)) {
add_out_dtd(ctx, VPE_PORT_RGB_OUT);
} else {
add_out_dtd(ctx, VPE_PORT_LUMA_OUT);
@@ -1301,7 +1330,7 @@
}
/* sync on channel control descriptors for output ports */
- if (d_q_data->colorspace == V4L2_COLORSPACE_SRGB) {
+ if (v4l2_is_format_rgb(d_finfo)) {
vpdma_add_sync_on_channel_ctd(&ctx->desc_list,
VPE_CHAN_RGB_OUT);
} else {
@@ -1533,38 +1562,32 @@
static int vpe_g_fmt(struct file *file, void *priv, struct v4l2_format *f)
{
struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp;
- struct vpe_ctx *ctx = file2ctx(file);
+ struct vpe_ctx *ctx = file->private_data;
struct vb2_queue *vq;
struct vpe_q_data *q_data;
- int i;
vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
if (!vq)
return -EINVAL;
q_data = get_q_data(ctx, f->type);
+ if (!q_data)
+ return -EINVAL;
- pix->width = q_data->width;
- pix->height = q_data->height;
- pix->pixelformat = q_data->fmt->fourcc;
- pix->field = q_data->field;
+ *f = q_data->format;
- if (V4L2_TYPE_IS_OUTPUT(f->type)) {
- pix->colorspace = q_data->colorspace;
- } else {
+ if (V4L2_TYPE_IS_CAPTURE(f->type)) {
struct vpe_q_data *s_q_data;
+ struct v4l2_pix_format_mplane *spix;
- /* get colorspace from the source queue */
+ /* get colorimetry from the source queue */
s_q_data = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+ spix = &s_q_data->format.fmt.pix_mp;
- pix->colorspace = s_q_data->colorspace;
- }
-
- pix->num_planes = q_data->nplanes;
-
- for (i = 0; i < pix->num_planes; i++) {
- pix->plane_fmt[i].bytesperline = q_data->bytesperline[i];
- pix->plane_fmt[i].sizeimage = q_data->sizeimage[i];
+ pix->colorspace = spix->colorspace;
+ pix->xfer_func = spix->xfer_func;
+ pix->ycbcr_enc = spix->ycbcr_enc;
+ pix->quantization = spix->quantization;
}
return 0;
@@ -1578,6 +1601,7 @@
unsigned int w_align;
int i, depth, depth_bytes, height;
unsigned int stride = 0;
+ const struct v4l2_format_info *finfo;
if (!fmt || !(fmt->types & type)) {
vpe_dbg(ctx->dev, "Fourcc format (0x%08x) invalid.\n",
@@ -1585,8 +1609,10 @@
fmt = __find_format(V4L2_PIX_FMT_YUYV);
}
- if (pix->field != V4L2_FIELD_NONE && pix->field != V4L2_FIELD_ALTERNATE
- && pix->field != V4L2_FIELD_SEQ_TB)
+ if (pix->field != V4L2_FIELD_NONE &&
+ pix->field != V4L2_FIELD_ALTERNATE &&
+ pix->field != V4L2_FIELD_SEQ_TB &&
+ pix->field != V4L2_FIELD_SEQ_BT)
pix->field = V4L2_FIELD_NONE;
depth = fmt->vpdma_fmt[VPE_LUMA]->depth;
@@ -1635,21 +1661,19 @@
pix->num_planes = 1;
pix->pixelformat = fmt->fourcc;
+ finfo = v4l2_format_info(fmt->fourcc);
/*
* For the actual image parameters, we need to consider the field
- * height of the image for SEQ_TB buffers.
+ * height of the image for SEQ_XX buffers.
*/
- if (pix->field == V4L2_FIELD_SEQ_TB)
+ if (pix->field == V4L2_FIELD_SEQ_TB || pix->field == V4L2_FIELD_SEQ_BT)
height = pix->height / 2;
else
height = pix->height;
if (!pix->colorspace) {
- if (fmt->fourcc == V4L2_PIX_FMT_RGB24 ||
- fmt->fourcc == V4L2_PIX_FMT_BGR24 ||
- fmt->fourcc == V4L2_PIX_FMT_RGB32 ||
- fmt->fourcc == V4L2_PIX_FMT_BGR32) {
+ if (v4l2_is_format_rgb(finfo)) {
pix->colorspace = V4L2_COLORSPACE_SRGB;
} else {
if (height > 1280) /* HD */
@@ -1697,7 +1721,7 @@
static int vpe_try_fmt(struct file *file, void *priv, struct v4l2_format *f)
{
- struct vpe_ctx *ctx = file2ctx(file);
+ struct vpe_ctx *ctx = file->private_data;
struct vpe_fmt *fmt = find_format(f);
if (V4L2_TYPE_IS_OUTPUT(f->type))
@@ -1709,10 +1733,9 @@
static int __vpe_s_fmt(struct vpe_ctx *ctx, struct v4l2_format *f)
{
struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp;
- struct v4l2_plane_pix_format *plane_fmt;
+ struct v4l2_pix_format_mplane *qpix;
struct vpe_q_data *q_data;
struct vb2_queue *vq;
- int i;
vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
if (!vq)
@@ -1727,42 +1750,34 @@
if (!q_data)
return -EINVAL;
+ qpix = &q_data->format.fmt.pix_mp;
q_data->fmt = find_format(f);
- q_data->width = pix->width;
- q_data->height = pix->height;
- q_data->colorspace = pix->colorspace;
- q_data->field = pix->field;
- q_data->nplanes = pix->num_planes;
-
- for (i = 0; i < pix->num_planes; i++) {
- plane_fmt = &pix->plane_fmt[i];
-
- q_data->bytesperline[i] = plane_fmt->bytesperline;
- q_data->sizeimage[i] = plane_fmt->sizeimage;
- }
+ q_data->format = *f;
q_data->c_rect.left = 0;
q_data->c_rect.top = 0;
- q_data->c_rect.width = q_data->width;
- q_data->c_rect.height = q_data->height;
+ q_data->c_rect.width = pix->width;
+ q_data->c_rect.height = pix->height;
- if (q_data->field == V4L2_FIELD_ALTERNATE)
+ if (qpix->field == V4L2_FIELD_ALTERNATE)
q_data->flags |= Q_DATA_INTERLACED_ALTERNATE;
- else if (q_data->field == V4L2_FIELD_SEQ_TB)
+ else if (qpix->field == V4L2_FIELD_SEQ_TB)
q_data->flags |= Q_DATA_INTERLACED_SEQ_TB;
+ else if (qpix->field == V4L2_FIELD_SEQ_BT)
+ q_data->flags |= Q_DATA_INTERLACED_SEQ_BT;
else
q_data->flags &= ~Q_IS_INTERLACED;
- /* the crop height is halved for the case of SEQ_TB buffers */
- if (q_data->flags & Q_DATA_INTERLACED_SEQ_TB)
+ /* the crop height is halved for the case of SEQ_XX buffers */
+ if (q_data->flags & Q_IS_SEQ_XX)
q_data->c_rect.height /= 2;
vpe_dbg(ctx->dev, "Setting format for type %d, wxh: %dx%d, fmt: %d bpl_y %d",
- f->type, q_data->width, q_data->height, q_data->fmt->fourcc,
- q_data->bytesperline[VPE_LUMA]);
- if (q_data->nplanes == 2)
+ f->type, pix->width, pix->height, pix->pixelformat,
+ pix->plane_fmt[0].bytesperline);
+ if (pix->num_planes == 2)
vpe_dbg(ctx->dev, " bpl_uv %d\n",
- q_data->bytesperline[VPE_CHROMA]);
+ pix->plane_fmt[1].bytesperline);
return 0;
}
@@ -1770,7 +1785,7 @@
static int vpe_s_fmt(struct file *file, void *priv, struct v4l2_format *f)
{
int ret;
- struct vpe_ctx *ctx = file2ctx(file);
+ struct vpe_ctx *ctx = file->private_data;
ret = vpe_try_fmt(file, priv, f);
if (ret)
@@ -1791,6 +1806,7 @@
static int __vpe_try_selection(struct vpe_ctx *ctx, struct v4l2_selection *s)
{
struct vpe_q_data *q_data;
+ struct v4l2_pix_format_mplane *pix;
int height;
if ((s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
@@ -1801,6 +1817,8 @@
if (!q_data)
return -EINVAL;
+ pix = &q_data->format.fmt.pix_mp;
+
switch (s->target) {
case V4L2_SEL_TGT_COMPOSE:
/*
@@ -1827,27 +1845,27 @@
}
/*
- * For SEQ_TB buffers, crop height should be less than the height of
+ * For SEQ_XX buffers, crop height should be less than the height of
* the field height, not the buffer height
*/
- if (q_data->flags & Q_DATA_INTERLACED_SEQ_TB)
- height = q_data->height / 2;
+ if (q_data->flags & Q_IS_SEQ_XX)
+ height = pix->height / 2;
else
- height = q_data->height;
+ height = pix->height;
if (s->r.top < 0 || s->r.left < 0) {
vpe_err(ctx->dev, "negative values for top and left\n");
s->r.top = s->r.left = 0;
}
- v4l_bound_align_image(&s->r.width, MIN_W, q_data->width, 1,
+ v4l_bound_align_image(&s->r.width, MIN_W, pix->width, 1,
&s->r.height, MIN_H, height, H_ALIGN, S_ALIGN);
/* adjust left/top if cropping rectangle is out of bounds */
- if (s->r.left + s->r.width > q_data->width)
- s->r.left = q_data->width - s->r.width;
- if (s->r.top + s->r.height > q_data->height)
- s->r.top = q_data->height - s->r.height;
+ if (s->r.left + s->r.width > pix->width)
+ s->r.left = pix->width - s->r.width;
+ if (s->r.top + s->r.height > pix->height)
+ s->r.top = pix->height - s->r.height;
return 0;
}
@@ -1855,8 +1873,9 @@
static int vpe_g_selection(struct file *file, void *fh,
struct v4l2_selection *s)
{
- struct vpe_ctx *ctx = file2ctx(file);
+ struct vpe_ctx *ctx = file->private_data;
struct vpe_q_data *q_data;
+ struct v4l2_pix_format_mplane *pix;
bool use_c_rect = false;
if ((s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
@@ -1867,6 +1886,8 @@
if (!q_data)
return -EINVAL;
+ pix = &q_data->format.fmt.pix_mp;
+
switch (s->target) {
case V4L2_SEL_TGT_COMPOSE_DEFAULT:
case V4L2_SEL_TGT_COMPOSE_BOUNDS:
@@ -1905,8 +1926,8 @@
*/
s->r.left = 0;
s->r.top = 0;
- s->r.width = q_data->width;
- s->r.height = q_data->height;
+ s->r.width = pix->width;
+ s->r.height = pix->height;
}
return 0;
@@ -1916,7 +1937,7 @@
static int vpe_s_selection(struct file *file, void *fh,
struct v4l2_selection *s)
{
- struct vpe_ctx *ctx = file2ctx(file);
+ struct vpe_ctx *ctx = file->private_data;
struct vpe_q_data *q_data;
struct v4l2_selection sel = *s;
int ret;
@@ -2009,17 +2030,21 @@
int i;
struct vpe_ctx *ctx = vb2_get_drv_priv(vq);
struct vpe_q_data *q_data;
+ struct v4l2_pix_format_mplane *pix;
q_data = get_q_data(ctx, vq->type);
+ if (!q_data)
+ return -EINVAL;
- *nplanes = q_data->nplanes;
+ pix = &q_data->format.fmt.pix_mp;
+ *nplanes = pix->num_planes;
for (i = 0; i < *nplanes; i++)
- sizes[i] = q_data->sizeimage[i];
+ sizes[i] = pix->plane_fmt[i].sizeimage;
vpe_dbg(ctx->dev, "get %d buffer(s) of size %d", *nbuffers,
sizes[VPE_LUMA]);
- if (q_data->nplanes == 2)
+ if (*nplanes == 2)
vpe_dbg(ctx->dev, " and %d\n", sizes[VPE_CHROMA]);
return 0;
@@ -2030,12 +2055,16 @@
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
struct vpe_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
struct vpe_q_data *q_data;
- int i, num_planes;
+ struct v4l2_pix_format_mplane *pix;
+ int i;
vpe_dbg(ctx->dev, "type: %d\n", vb->vb2_queue->type);
q_data = get_q_data(ctx, vb->vb2_queue->type);
- num_planes = q_data->nplanes;
+ if (!q_data)
+ return -EINVAL;
+
+ pix = &q_data->format.fmt.pix_mp;
if (vb->vb2_queue->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
if (!(q_data->flags & Q_IS_INTERLACED)) {
@@ -2043,23 +2072,24 @@
} else {
if (vbuf->field != V4L2_FIELD_TOP &&
vbuf->field != V4L2_FIELD_BOTTOM &&
- vbuf->field != V4L2_FIELD_SEQ_TB)
+ vbuf->field != V4L2_FIELD_SEQ_TB &&
+ vbuf->field != V4L2_FIELD_SEQ_BT)
return -EINVAL;
}
}
- for (i = 0; i < num_planes; i++) {
- if (vb2_plane_size(vb, i) < q_data->sizeimage[i]) {
+ for (i = 0; i < pix->num_planes; i++) {
+ if (vb2_plane_size(vb, i) < pix->plane_fmt[i].sizeimage) {
vpe_err(ctx->dev,
"data will not fit into plane (%lu < %lu)\n",
vb2_plane_size(vb, i),
- (long) q_data->sizeimage[i]);
+ (long)pix->plane_fmt[i].sizeimage);
return -EINVAL;
}
}
- for (i = 0; i < num_planes; i++)
- vb2_set_plane_payload(vb, i, q_data->sizeimage[i]);
+ for (i = 0; i < pix->num_planes; i++)
+ vb2_set_plane_payload(vb, i, pix->plane_fmt[i].sizeimage);
return 0;
}
@@ -2244,6 +2274,7 @@
struct vpe_q_data *s_q_data;
struct v4l2_ctrl_handler *hdl;
struct vpe_ctx *ctx;
+ struct v4l2_pix_format_mplane *pix;
int ret;
vpe_dbg(dev, "vpe_open\n");
@@ -2279,7 +2310,7 @@
init_adb_hdrs(ctx);
v4l2_fh_init(&ctx->fh, video_devdata(file));
- file->private_data = &ctx->fh;
+ file->private_data = ctx;
hdl = &ctx->hdl;
v4l2_ctrl_handler_init(hdl, 1);
@@ -2292,23 +2323,32 @@
v4l2_ctrl_handler_setup(hdl);
s_q_data = &ctx->q_data[Q_DATA_SRC];
+ pix = &s_q_data->format.fmt.pix_mp;
s_q_data->fmt = __find_format(V4L2_PIX_FMT_YUYV);
- s_q_data->width = 1920;
- s_q_data->height = 1080;
- s_q_data->nplanes = 1;
- s_q_data->bytesperline[VPE_LUMA] = (s_q_data->width *
+ pix->pixelformat = s_q_data->fmt->fourcc;
+ s_q_data->format.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+ pix->width = 1920;
+ pix->height = 1080;
+ pix->num_planes = 1;
+ pix->plane_fmt[VPE_LUMA].bytesperline = (pix->width *
s_q_data->fmt->vpdma_fmt[VPE_LUMA]->depth) >> 3;
- s_q_data->sizeimage[VPE_LUMA] = (s_q_data->bytesperline[VPE_LUMA] *
- s_q_data->height);
- s_q_data->colorspace = V4L2_COLORSPACE_REC709;
- s_q_data->field = V4L2_FIELD_NONE;
+ pix->plane_fmt[VPE_LUMA].sizeimage =
+ pix->plane_fmt[VPE_LUMA].bytesperline *
+ pix->height;
+ pix->colorspace = V4L2_COLORSPACE_REC709;
+ pix->xfer_func = V4L2_XFER_FUNC_DEFAULT;
+ pix->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
+ pix->quantization = V4L2_QUANTIZATION_DEFAULT;
+ pix->field = V4L2_FIELD_NONE;
s_q_data->c_rect.left = 0;
s_q_data->c_rect.top = 0;
- s_q_data->c_rect.width = s_q_data->width;
- s_q_data->c_rect.height = s_q_data->height;
+ s_q_data->c_rect.width = pix->width;
+ s_q_data->c_rect.height = pix->height;
s_q_data->flags = 0;
ctx->q_data[Q_DATA_DST] = *s_q_data;
+ ctx->q_data[Q_DATA_DST].format.type =
+ V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
set_dei_shadow_registers(ctx);
set_src_registers(ctx);
@@ -2364,7 +2404,7 @@
static int vpe_release(struct file *file)
{
struct vpe_dev *dev = video_drvdata(file);
- struct vpe_ctx *ctx = file2ctx(file);
+ struct vpe_ctx *ctx = file->private_data;
vpe_dbg(dev, "releasing instance %p\n", ctx);
@@ -2462,7 +2502,7 @@
vfd->lock = &dev->dev_mutex;
vfd->v4l2_dev = &dev->v4l2_dev;
- ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0);
+ ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0);
if (ret) {
vpe_err(dev, "Failed to register video device\n");
@@ -2485,6 +2525,13 @@
struct vpe_dev *dev;
int ret, irq, func;
+ ret = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
+ if (ret) {
+ dev_err(&pdev->dev,
+ "32-bit consistent DMA enable failed\n");
+ return ret;
+ }
+
dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
if (!dev)
return -ENOMEM;
@@ -2499,7 +2546,12 @@
mutex_init(&dev->dev_mutex);
dev->res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
- "vpe_top");
+ "vpe_top");
+ if (!dev->res) {
+ dev_err(&pdev->dev, "missing 'vpe_top' resources data\n");
+ return -ENODEV;
+ }
+
/*
* HACK: we get resource info from device tree in the form of a list of
* VPE sub blocks, the driver currently uses only the base of vpe_top
@@ -2594,7 +2646,7 @@
#if defined(CONFIG_OF)
static const struct of_device_id vpe_of_match[] = {
{
- .compatible = "ti,vpe",
+ .compatible = "ti,dra7-vpe",
},
{},
};
diff --git a/drivers/media/platform/via-camera.c b/drivers/media/platform/via-camera.c
index 78841b9..ed0ad68 100644
--- a/drivers/media/platform/via-camera.c
+++ b/drivers/media/platform/via-camera.c
@@ -646,7 +646,7 @@
* requirement which will keep the CPU out of the deeper sleep
* states.
*/
- pm_qos_add_request(&cam->qos_request, PM_QOS_CPU_DMA_LATENCY, 50);
+ cpu_latency_qos_add_request(&cam->qos_request, 50);
viacam_start_engine(cam);
return 0;
out:
@@ -662,7 +662,7 @@
struct via_camera *cam = vb2_get_drv_priv(vq);
struct via_buffer *buf, *tmp;
- pm_qos_remove_request(&cam->qos_request);
+ cpu_latency_qos_remove_request(&cam->qos_request);
viacam_stop_engine(cam);
list_for_each_entry_safe(buf, tmp, &cam->buffer_queue, queue) {
@@ -1262,7 +1262,7 @@
cam->vdev.lock = &cam->lock;
cam->vdev.queue = vq;
video_set_drvdata(&cam->vdev, cam);
- ret = video_register_device(&cam->vdev, VFL_TYPE_GRABBER, -1);
+ ret = video_register_device(&cam->vdev, VFL_TYPE_VIDEO, -1);
if (ret)
goto out_irq;
diff --git a/drivers/media/platform/vicodec/Kconfig b/drivers/media/platform/vicodec/Kconfig
deleted file mode 100644
index 8945666..0000000
--- a/drivers/media/platform/vicodec/Kconfig
+++ /dev/null
@@ -1,13 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0-only
-config VIDEO_VICODEC
- tristate "Virtual Codec Driver"
- depends on VIDEO_DEV && VIDEO_V4L2
- select VIDEOBUF2_VMALLOC
- select V4L2_MEM2MEM_DEV
- help
- Driver for a Virtual Codec
-
- This driver can be compared to the vim2m driver for emulating
- a video device node that exposes an emulated hardware codec.
-
- When in doubt, say N.
diff --git a/drivers/media/platform/vicodec/Makefile b/drivers/media/platform/vicodec/Makefile
deleted file mode 100644
index 01bf7e9..0000000
--- a/drivers/media/platform/vicodec/Makefile
+++ /dev/null
@@ -1,4 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0
-vicodec-objs := vicodec-core.o codec-fwht.o codec-v4l2-fwht.o
-
-obj-$(CONFIG_VIDEO_VICODEC) += vicodec.o
diff --git a/drivers/media/platform/vicodec/codec-fwht.c b/drivers/media/platform/vicodec/codec-fwht.c
deleted file mode 100644
index 31faf31..0000000
--- a/drivers/media/platform/vicodec/codec-fwht.c
+++ /dev/null
@@ -1,958 +0,0 @@
-// SPDX-License-Identifier: LGPL-2.1+
-/*
- * Copyright 2016 Tom aan de Wiel
- * Copyright 2018 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- *
- * 8x8 Fast Walsh Hadamard Transform in sequency order based on the paper:
- *
- * A Recursive Algorithm for Sequency-Ordered Fast Walsh Transforms,
- * R.D. Brown, 1977
- */
-
-#include <linux/string.h>
-#include <linux/kernel.h>
-#include "codec-fwht.h"
-
-#define OVERFLOW_BIT BIT(14)
-
-/*
- * Note: bit 0 of the header must always be 0. Otherwise it cannot
- * be guaranteed that the magic 8 byte sequence (see below) can
- * never occur in the rlc output.
- */
-#define PFRAME_BIT BIT(15)
-#define DUPS_MASK 0x1ffe
-
-#define PBLOCK 0
-#define IBLOCK 1
-
-#define ALL_ZEROS 15
-
-static const uint8_t zigzag[64] = {
- 0,
- 1, 8,
- 2, 9, 16,
- 3, 10, 17, 24,
- 4, 11, 18, 25, 32,
- 5, 12, 19, 26, 33, 40,
- 6, 13, 20, 27, 34, 41, 48,
- 7, 14, 21, 28, 35, 42, 49, 56,
- 15, 22, 29, 36, 43, 50, 57,
- 23, 30, 37, 44, 51, 58,
- 31, 38, 45, 52, 59,
- 39, 46, 53, 60,
- 47, 54, 61,
- 55, 62,
- 63,
-};
-
-/*
- * noinline_for_stack to work around
- * https://bugs.llvm.org/show_bug.cgi?id=38809
- */
-static int noinline_for_stack
-rlc(const s16 *in, __be16 *output, int blocktype)
-{
- s16 block[8 * 8];
- s16 *wp = block;
- int i = 0;
- int x, y;
- int ret = 0;
-
- /* read in block from framebuffer */
- int lastzero_run = 0;
- int to_encode;
-
- for (y = 0; y < 8; y++) {
- for (x = 0; x < 8; x++) {
- *wp = in[x + y * 8];
- wp++;
- }
- }
-
- /* keep track of amount of trailing zeros */
- for (i = 63; i >= 0 && !block[zigzag[i]]; i--)
- lastzero_run++;
-
- *output++ = (blocktype == PBLOCK ? htons(PFRAME_BIT) : 0);
- ret++;
-
- to_encode = 8 * 8 - (lastzero_run > 14 ? lastzero_run : 0);
-
- i = 0;
- while (i < to_encode) {
- int cnt = 0;
- int tmp;
-
- /* count leading zeros */
- while ((tmp = block[zigzag[i]]) == 0 && cnt < 14) {
- cnt++;
- i++;
- if (i == to_encode) {
- cnt--;
- break;
- }
- }
- /* 4 bits for run, 12 for coefficient (quantization by 4) */
- *output++ = htons((cnt | tmp << 4));
- i++;
- ret++;
- }
- if (lastzero_run > 14) {
- *output = htons(ALL_ZEROS | 0);
- ret++;
- }
-
- return ret;
-}
-
-/*
- * This function will worst-case increase rlc_in by 65*2 bytes:
- * one s16 value for the header and 8 * 8 coefficients of type s16.
- */
-static noinline_for_stack u16
-derlc(const __be16 **rlc_in, s16 *dwht_out, const __be16 *end_of_input)
-{
- /* header */
- const __be16 *input = *rlc_in;
- u16 stat;
- int dec_count = 0;
- s16 block[8 * 8 + 16];
- s16 *wp = block;
- int i;
-
- if (input > end_of_input)
- return OVERFLOW_BIT;
- stat = ntohs(*input++);
-
- /*
- * Now de-compress, it expands one byte to up to 15 bytes
- * (or fills the remainder of the 64 bytes with zeroes if it
- * is the last byte to expand).
- *
- * So block has to be 8 * 8 + 16 bytes, the '+ 16' is to
- * allow for overflow if the incoming data was malformed.
- */
- while (dec_count < 8 * 8) {
- s16 in;
- int length;
- int coeff;
-
- if (input > end_of_input)
- return OVERFLOW_BIT;
- in = ntohs(*input++);
- length = in & 0xf;
- coeff = in >> 4;
-
- /* fill remainder with zeros */
- if (length == 15) {
- for (i = 0; i < 64 - dec_count; i++)
- *wp++ = 0;
- break;
- }
-
- for (i = 0; i < length; i++)
- *wp++ = 0;
- *wp++ = coeff;
- dec_count += length + 1;
- }
-
- wp = block;
-
- for (i = 0; i < 64; i++) {
- int pos = zigzag[i];
- int y = pos / 8;
- int x = pos % 8;
-
- dwht_out[x + y * 8] = *wp++;
- }
- *rlc_in = input;
- return stat;
-}
-
-static const int quant_table[] = {
- 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 3,
- 2, 2, 2, 2, 2, 2, 3, 6,
- 2, 2, 2, 2, 2, 3, 6, 6,
- 2, 2, 2, 2, 3, 6, 6, 6,
- 2, 2, 2, 3, 6, 6, 6, 6,
- 2, 2, 3, 6, 6, 6, 6, 8,
-};
-
-static const int quant_table_p[] = {
- 3, 3, 3, 3, 3, 3, 3, 3,
- 3, 3, 3, 3, 3, 3, 3, 3,
- 3, 3, 3, 3, 3, 3, 3, 3,
- 3, 3, 3, 3, 3, 3, 3, 6,
- 3, 3, 3, 3, 3, 3, 6, 6,
- 3, 3, 3, 3, 3, 6, 6, 9,
- 3, 3, 3, 3, 6, 6, 9, 9,
- 3, 3, 3, 6, 6, 9, 9, 10,
-};
-
-static void quantize_intra(s16 *coeff, s16 *de_coeff, u16 qp)
-{
- const int *quant = quant_table;
- int i, j;
-
- for (j = 0; j < 8; j++) {
- for (i = 0; i < 8; i++, quant++, coeff++, de_coeff++) {
- *coeff >>= *quant;
- if (*coeff >= -qp && *coeff <= qp)
- *coeff = *de_coeff = 0;
- else
- *de_coeff = *coeff << *quant;
- }
- }
-}
-
-static void dequantize_intra(s16 *coeff)
-{
- const int *quant = quant_table;
- int i, j;
-
- for (j = 0; j < 8; j++)
- for (i = 0; i < 8; i++, quant++, coeff++)
- *coeff <<= *quant;
-}
-
-static void quantize_inter(s16 *coeff, s16 *de_coeff, u16 qp)
-{
- const int *quant = quant_table_p;
- int i, j;
-
- for (j = 0; j < 8; j++) {
- for (i = 0; i < 8; i++, quant++, coeff++, de_coeff++) {
- *coeff >>= *quant;
- if (*coeff >= -qp && *coeff <= qp)
- *coeff = *de_coeff = 0;
- else
- *de_coeff = *coeff << *quant;
- }
- }
-}
-
-static void dequantize_inter(s16 *coeff)
-{
- const int *quant = quant_table_p;
- int i, j;
-
- for (j = 0; j < 8; j++)
- for (i = 0; i < 8; i++, quant++, coeff++)
- *coeff <<= *quant;
-}
-
-static void noinline_for_stack fwht(const u8 *block, s16 *output_block,
- unsigned int stride,
- unsigned int input_step, bool intra)
-{
- /* we'll need more than 8 bits for the transformed coefficients */
- s32 workspace1[8], workspace2[8];
- const u8 *tmp = block;
- s16 *out = output_block;
- int add = intra ? 256 : 0;
- unsigned int i;
-
- /* stage 1 */
- for (i = 0; i < 8; i++, tmp += stride, out += 8) {
- switch (input_step) {
- case 1:
- workspace1[0] = tmp[0] + tmp[1] - add;
- workspace1[1] = tmp[0] - tmp[1];
-
- workspace1[2] = tmp[2] + tmp[3] - add;
- workspace1[3] = tmp[2] - tmp[3];
-
- workspace1[4] = tmp[4] + tmp[5] - add;
- workspace1[5] = tmp[4] - tmp[5];
-
- workspace1[6] = tmp[6] + tmp[7] - add;
- workspace1[7] = tmp[6] - tmp[7];
- break;
- case 2:
- workspace1[0] = tmp[0] + tmp[2] - add;
- workspace1[1] = tmp[0] - tmp[2];
-
- workspace1[2] = tmp[4] + tmp[6] - add;
- workspace1[3] = tmp[4] - tmp[6];
-
- workspace1[4] = tmp[8] + tmp[10] - add;
- workspace1[5] = tmp[8] - tmp[10];
-
- workspace1[6] = tmp[12] + tmp[14] - add;
- workspace1[7] = tmp[12] - tmp[14];
- break;
- case 3:
- workspace1[0] = tmp[0] + tmp[3] - add;
- workspace1[1] = tmp[0] - tmp[3];
-
- workspace1[2] = tmp[6] + tmp[9] - add;
- workspace1[3] = tmp[6] - tmp[9];
-
- workspace1[4] = tmp[12] + tmp[15] - add;
- workspace1[5] = tmp[12] - tmp[15];
-
- workspace1[6] = tmp[18] + tmp[21] - add;
- workspace1[7] = tmp[18] - tmp[21];
- break;
- default:
- workspace1[0] = tmp[0] + tmp[4] - add;
- workspace1[1] = tmp[0] - tmp[4];
-
- workspace1[2] = tmp[8] + tmp[12] - add;
- workspace1[3] = tmp[8] - tmp[12];
-
- workspace1[4] = tmp[16] + tmp[20] - add;
- workspace1[5] = tmp[16] - tmp[20];
-
- workspace1[6] = tmp[24] + tmp[28] - add;
- workspace1[7] = tmp[24] - tmp[28];
- break;
- }
-
- /* stage 2 */
- workspace2[0] = workspace1[0] + workspace1[2];
- workspace2[1] = workspace1[0] - workspace1[2];
- workspace2[2] = workspace1[1] - workspace1[3];
- workspace2[3] = workspace1[1] + workspace1[3];
-
- workspace2[4] = workspace1[4] + workspace1[6];
- workspace2[5] = workspace1[4] - workspace1[6];
- workspace2[6] = workspace1[5] - workspace1[7];
- workspace2[7] = workspace1[5] + workspace1[7];
-
- /* stage 3 */
- out[0] = workspace2[0] + workspace2[4];
- out[1] = workspace2[0] - workspace2[4];
- out[2] = workspace2[1] - workspace2[5];
- out[3] = workspace2[1] + workspace2[5];
- out[4] = workspace2[2] + workspace2[6];
- out[5] = workspace2[2] - workspace2[6];
- out[6] = workspace2[3] - workspace2[7];
- out[7] = workspace2[3] + workspace2[7];
- }
-
- out = output_block;
-
- for (i = 0; i < 8; i++, out++) {
- /* stage 1 */
- workspace1[0] = out[0] + out[1 * 8];
- workspace1[1] = out[0] - out[1 * 8];
-
- workspace1[2] = out[2 * 8] + out[3 * 8];
- workspace1[3] = out[2 * 8] - out[3 * 8];
-
- workspace1[4] = out[4 * 8] + out[5 * 8];
- workspace1[5] = out[4 * 8] - out[5 * 8];
-
- workspace1[6] = out[6 * 8] + out[7 * 8];
- workspace1[7] = out[6 * 8] - out[7 * 8];
-
- /* stage 2 */
- workspace2[0] = workspace1[0] + workspace1[2];
- workspace2[1] = workspace1[0] - workspace1[2];
- workspace2[2] = workspace1[1] - workspace1[3];
- workspace2[3] = workspace1[1] + workspace1[3];
-
- workspace2[4] = workspace1[4] + workspace1[6];
- workspace2[5] = workspace1[4] - workspace1[6];
- workspace2[6] = workspace1[5] - workspace1[7];
- workspace2[7] = workspace1[5] + workspace1[7];
- /* stage 3 */
- out[0 * 8] = workspace2[0] + workspace2[4];
- out[1 * 8] = workspace2[0] - workspace2[4];
- out[2 * 8] = workspace2[1] - workspace2[5];
- out[3 * 8] = workspace2[1] + workspace2[5];
- out[4 * 8] = workspace2[2] + workspace2[6];
- out[5 * 8] = workspace2[2] - workspace2[6];
- out[6 * 8] = workspace2[3] - workspace2[7];
- out[7 * 8] = workspace2[3] + workspace2[7];
- }
-}
-
-/*
- * Not the nicest way of doing it, but P-blocks get twice the range of
- * that of the I-blocks. Therefore we need a type bigger than 8 bits.
- * Furthermore values can be negative... This is just a version that
- * works with 16 signed data
- */
-static void noinline_for_stack
-fwht16(const s16 *block, s16 *output_block, int stride, int intra)
-{
- /* we'll need more than 8 bits for the transformed coefficients */
- s32 workspace1[8], workspace2[8];
- const s16 *tmp = block;
- s16 *out = output_block;
- int i;
-
- for (i = 0; i < 8; i++, tmp += stride, out += 8) {
- /* stage 1 */
- workspace1[0] = tmp[0] + tmp[1];
- workspace1[1] = tmp[0] - tmp[1];
-
- workspace1[2] = tmp[2] + tmp[3];
- workspace1[3] = tmp[2] - tmp[3];
-
- workspace1[4] = tmp[4] + tmp[5];
- workspace1[5] = tmp[4] - tmp[5];
-
- workspace1[6] = tmp[6] + tmp[7];
- workspace1[7] = tmp[6] - tmp[7];
-
- /* stage 2 */
- workspace2[0] = workspace1[0] + workspace1[2];
- workspace2[1] = workspace1[0] - workspace1[2];
- workspace2[2] = workspace1[1] - workspace1[3];
- workspace2[3] = workspace1[1] + workspace1[3];
-
- workspace2[4] = workspace1[4] + workspace1[6];
- workspace2[5] = workspace1[4] - workspace1[6];
- workspace2[6] = workspace1[5] - workspace1[7];
- workspace2[7] = workspace1[5] + workspace1[7];
-
- /* stage 3 */
- out[0] = workspace2[0] + workspace2[4];
- out[1] = workspace2[0] - workspace2[4];
- out[2] = workspace2[1] - workspace2[5];
- out[3] = workspace2[1] + workspace2[5];
- out[4] = workspace2[2] + workspace2[6];
- out[5] = workspace2[2] - workspace2[6];
- out[6] = workspace2[3] - workspace2[7];
- out[7] = workspace2[3] + workspace2[7];
- }
-
- out = output_block;
-
- for (i = 0; i < 8; i++, out++) {
- /* stage 1 */
- workspace1[0] = out[0] + out[1*8];
- workspace1[1] = out[0] - out[1*8];
-
- workspace1[2] = out[2*8] + out[3*8];
- workspace1[3] = out[2*8] - out[3*8];
-
- workspace1[4] = out[4*8] + out[5*8];
- workspace1[5] = out[4*8] - out[5*8];
-
- workspace1[6] = out[6*8] + out[7*8];
- workspace1[7] = out[6*8] - out[7*8];
-
- /* stage 2 */
- workspace2[0] = workspace1[0] + workspace1[2];
- workspace2[1] = workspace1[0] - workspace1[2];
- workspace2[2] = workspace1[1] - workspace1[3];
- workspace2[3] = workspace1[1] + workspace1[3];
-
- workspace2[4] = workspace1[4] + workspace1[6];
- workspace2[5] = workspace1[4] - workspace1[6];
- workspace2[6] = workspace1[5] - workspace1[7];
- workspace2[7] = workspace1[5] + workspace1[7];
-
- /* stage 3 */
- out[0*8] = workspace2[0] + workspace2[4];
- out[1*8] = workspace2[0] - workspace2[4];
- out[2*8] = workspace2[1] - workspace2[5];
- out[3*8] = workspace2[1] + workspace2[5];
- out[4*8] = workspace2[2] + workspace2[6];
- out[5*8] = workspace2[2] - workspace2[6];
- out[6*8] = workspace2[3] - workspace2[7];
- out[7*8] = workspace2[3] + workspace2[7];
- }
-}
-
-static noinline_for_stack void
-ifwht(const s16 *block, s16 *output_block, int intra)
-{
- /*
- * we'll need more than 8 bits for the transformed coefficients
- * use native unit of cpu
- */
- int workspace1[8], workspace2[8];
- int inter = intra ? 0 : 1;
- const s16 *tmp = block;
- s16 *out = output_block;
- int i;
-
- for (i = 0; i < 8; i++, tmp += 8, out += 8) {
- /* stage 1 */
- workspace1[0] = tmp[0] + tmp[1];
- workspace1[1] = tmp[0] - tmp[1];
-
- workspace1[2] = tmp[2] + tmp[3];
- workspace1[3] = tmp[2] - tmp[3];
-
- workspace1[4] = tmp[4] + tmp[5];
- workspace1[5] = tmp[4] - tmp[5];
-
- workspace1[6] = tmp[6] + tmp[7];
- workspace1[7] = tmp[6] - tmp[7];
-
- /* stage 2 */
- workspace2[0] = workspace1[0] + workspace1[2];
- workspace2[1] = workspace1[0] - workspace1[2];
- workspace2[2] = workspace1[1] - workspace1[3];
- workspace2[3] = workspace1[1] + workspace1[3];
-
- workspace2[4] = workspace1[4] + workspace1[6];
- workspace2[5] = workspace1[4] - workspace1[6];
- workspace2[6] = workspace1[5] - workspace1[7];
- workspace2[7] = workspace1[5] + workspace1[7];
-
- /* stage 3 */
- out[0] = workspace2[0] + workspace2[4];
- out[1] = workspace2[0] - workspace2[4];
- out[2] = workspace2[1] - workspace2[5];
- out[3] = workspace2[1] + workspace2[5];
- out[4] = workspace2[2] + workspace2[6];
- out[5] = workspace2[2] - workspace2[6];
- out[6] = workspace2[3] - workspace2[7];
- out[7] = workspace2[3] + workspace2[7];
- }
-
- out = output_block;
-
- for (i = 0; i < 8; i++, out++) {
- /* stage 1 */
- workspace1[0] = out[0] + out[1 * 8];
- workspace1[1] = out[0] - out[1 * 8];
-
- workspace1[2] = out[2 * 8] + out[3 * 8];
- workspace1[3] = out[2 * 8] - out[3 * 8];
-
- workspace1[4] = out[4 * 8] + out[5 * 8];
- workspace1[5] = out[4 * 8] - out[5 * 8];
-
- workspace1[6] = out[6 * 8] + out[7 * 8];
- workspace1[7] = out[6 * 8] - out[7 * 8];
-
- /* stage 2 */
- workspace2[0] = workspace1[0] + workspace1[2];
- workspace2[1] = workspace1[0] - workspace1[2];
- workspace2[2] = workspace1[1] - workspace1[3];
- workspace2[3] = workspace1[1] + workspace1[3];
-
- workspace2[4] = workspace1[4] + workspace1[6];
- workspace2[5] = workspace1[4] - workspace1[6];
- workspace2[6] = workspace1[5] - workspace1[7];
- workspace2[7] = workspace1[5] + workspace1[7];
-
- /* stage 3 */
- if (inter) {
- int d;
-
- out[0 * 8] = workspace2[0] + workspace2[4];
- out[1 * 8] = workspace2[0] - workspace2[4];
- out[2 * 8] = workspace2[1] - workspace2[5];
- out[3 * 8] = workspace2[1] + workspace2[5];
- out[4 * 8] = workspace2[2] + workspace2[6];
- out[5 * 8] = workspace2[2] - workspace2[6];
- out[6 * 8] = workspace2[3] - workspace2[7];
- out[7 * 8] = workspace2[3] + workspace2[7];
-
- for (d = 0; d < 8; d++)
- out[8 * d] >>= 6;
- } else {
- int d;
-
- out[0 * 8] = workspace2[0] + workspace2[4];
- out[1 * 8] = workspace2[0] - workspace2[4];
- out[2 * 8] = workspace2[1] - workspace2[5];
- out[3 * 8] = workspace2[1] + workspace2[5];
- out[4 * 8] = workspace2[2] + workspace2[6];
- out[5 * 8] = workspace2[2] - workspace2[6];
- out[6 * 8] = workspace2[3] - workspace2[7];
- out[7 * 8] = workspace2[3] + workspace2[7];
-
- for (d = 0; d < 8; d++) {
- out[8 * d] >>= 6;
- out[8 * d] += 128;
- }
- }
- }
-}
-
-static void fill_encoder_block(const u8 *input, s16 *dst,
- unsigned int stride, unsigned int input_step)
-{
- int i, j;
-
- for (i = 0; i < 8; i++) {
- for (j = 0; j < 8; j++, input += input_step)
- *dst++ = *input;
- input += stride - 8 * input_step;
- }
-}
-
-static int var_intra(const s16 *input)
-{
- int32_t mean = 0;
- int32_t ret = 0;
- const s16 *tmp = input;
- int i;
-
- for (i = 0; i < 8 * 8; i++, tmp++)
- mean += *tmp;
- mean /= 64;
- tmp = input;
- for (i = 0; i < 8 * 8; i++, tmp++)
- ret += (*tmp - mean) < 0 ? -(*tmp - mean) : (*tmp - mean);
- return ret;
-}
-
-static int var_inter(const s16 *old, const s16 *new)
-{
- int32_t ret = 0;
- int i;
-
- for (i = 0; i < 8 * 8; i++, old++, new++)
- ret += (*old - *new) < 0 ? -(*old - *new) : (*old - *new);
- return ret;
-}
-
-static noinline_for_stack int
-decide_blocktype(const u8 *cur, const u8 *reference, s16 *deltablock,
- unsigned int stride, unsigned int input_step)
-{
- s16 tmp[64];
- s16 old[64];
- s16 *work = tmp;
- unsigned int k, l;
- int vari;
- int vard;
-
- fill_encoder_block(cur, tmp, stride, input_step);
- fill_encoder_block(reference, old, 8, 1);
- vari = var_intra(tmp);
-
- for (k = 0; k < 8; k++) {
- for (l = 0; l < 8; l++) {
- *deltablock = *work - *reference;
- deltablock++;
- work++;
- reference++;
- }
- }
- deltablock -= 64;
- vard = var_inter(old, tmp);
- return vari <= vard ? IBLOCK : PBLOCK;
-}
-
-static void fill_decoder_block(u8 *dst, const s16 *input, int stride,
- unsigned int dst_step)
-{
- int i, j;
-
- for (i = 0; i < 8; i++) {
- for (j = 0; j < 8; j++, input++, dst += dst_step) {
- if (*input < 0)
- *dst = 0;
- else if (*input > 255)
- *dst = 255;
- else
- *dst = *input;
- }
- dst += stride - (8 * dst_step);
- }
-}
-
-static void add_deltas(s16 *deltas, const u8 *ref, int stride,
- unsigned int ref_step)
-{
- int k, l;
-
- for (k = 0; k < 8; k++) {
- for (l = 0; l < 8; l++) {
- *deltas += *ref;
- ref += ref_step;
- /*
- * Due to quantizing, it might possible that the
- * decoded coefficients are slightly out of range
- */
- if (*deltas < 0)
- *deltas = 0;
- else if (*deltas > 255)
- *deltas = 255;
- deltas++;
- }
- ref += stride - (8 * ref_step);
- }
-}
-
-static u32 encode_plane(u8 *input, u8 *refp, __be16 **rlco, __be16 *rlco_max,
- struct fwht_cframe *cf, u32 height, u32 width,
- u32 stride, unsigned int input_step,
- bool is_intra, bool next_is_intra)
-{
- u8 *input_start = input;
- __be16 *rlco_start = *rlco;
- s16 deltablock[64];
- __be16 pframe_bit = htons(PFRAME_BIT);
- u32 encoding = 0;
- unsigned int last_size = 0;
- unsigned int i, j;
-
- width = round_up(width, 8);
- height = round_up(height, 8);
-
- for (j = 0; j < height / 8; j++) {
- input = input_start + j * 8 * stride;
- for (i = 0; i < width / 8; i++) {
- /* intra code, first frame is always intra coded. */
- int blocktype = IBLOCK;
- unsigned int size;
-
- if (!is_intra)
- blocktype = decide_blocktype(input, refp,
- deltablock, stride, input_step);
- if (blocktype == IBLOCK) {
- fwht(input, cf->coeffs, stride, input_step, 1);
- quantize_intra(cf->coeffs, cf->de_coeffs,
- cf->i_frame_qp);
- } else {
- /* inter code */
- encoding |= FWHT_FRAME_PCODED;
- fwht16(deltablock, cf->coeffs, 8, 0);
- quantize_inter(cf->coeffs, cf->de_coeffs,
- cf->p_frame_qp);
- }
- if (!next_is_intra) {
- ifwht(cf->de_coeffs, cf->de_fwht, blocktype);
-
- if (blocktype == PBLOCK)
- add_deltas(cf->de_fwht, refp, 8, 1);
- fill_decoder_block(refp, cf->de_fwht, 8, 1);
- }
-
- input += 8 * input_step;
- refp += 8 * 8;
-
- size = rlc(cf->coeffs, *rlco, blocktype);
- if (last_size == size &&
- !memcmp(*rlco + 1, *rlco - size + 1, 2 * size - 2)) {
- __be16 *last_rlco = *rlco - size;
- s16 hdr = ntohs(*last_rlco);
-
- if (!((*last_rlco ^ **rlco) & pframe_bit) &&
- (hdr & DUPS_MASK) < DUPS_MASK)
- *last_rlco = htons(hdr + 2);
- else
- *rlco += size;
- } else {
- *rlco += size;
- }
- if (*rlco >= rlco_max) {
- encoding |= FWHT_FRAME_UNENCODED;
- goto exit_loop;
- }
- last_size = size;
- }
- }
-
-exit_loop:
- if (encoding & FWHT_FRAME_UNENCODED) {
- u8 *out = (u8 *)rlco_start;
- u8 *p;
-
- input = input_start;
- /*
- * The compressed stream should never contain the magic
- * header, so when we copy the YUV data we replace 0xff
- * by 0xfe. Since YUV is limited range such values
- * shouldn't appear anyway.
- */
- for (j = 0; j < height; j++) {
- for (i = 0, p = input; i < width; i++, p += input_step)
- *out++ = (*p == 0xff) ? 0xfe : *p;
- input += stride;
- }
- *rlco = (__be16 *)out;
- encoding &= ~FWHT_FRAME_PCODED;
- }
- return encoding;
-}
-
-u32 fwht_encode_frame(struct fwht_raw_frame *frm,
- struct fwht_raw_frame *ref_frm,
- struct fwht_cframe *cf,
- bool is_intra, bool next_is_intra,
- unsigned int width, unsigned int height,
- unsigned int stride, unsigned int chroma_stride)
-{
- unsigned int size = height * width;
- __be16 *rlco = cf->rlc_data;
- __be16 *rlco_max;
- u32 encoding;
-
- rlco_max = rlco + size / 2 - 256;
- encoding = encode_plane(frm->luma, ref_frm->luma, &rlco, rlco_max, cf,
- height, width, stride,
- frm->luma_alpha_step, is_intra, next_is_intra);
- if (encoding & FWHT_FRAME_UNENCODED)
- encoding |= FWHT_LUMA_UNENCODED;
- encoding &= ~FWHT_FRAME_UNENCODED;
-
- if (frm->components_num >= 3) {
- u32 chroma_h = height / frm->height_div;
- u32 chroma_w = width / frm->width_div;
- unsigned int chroma_size = chroma_h * chroma_w;
-
- rlco_max = rlco + chroma_size / 2 - 256;
- encoding |= encode_plane(frm->cb, ref_frm->cb, &rlco, rlco_max,
- cf, chroma_h, chroma_w,
- chroma_stride, frm->chroma_step,
- is_intra, next_is_intra);
- if (encoding & FWHT_FRAME_UNENCODED)
- encoding |= FWHT_CB_UNENCODED;
- encoding &= ~FWHT_FRAME_UNENCODED;
- rlco_max = rlco + chroma_size / 2 - 256;
- encoding |= encode_plane(frm->cr, ref_frm->cr, &rlco, rlco_max,
- cf, chroma_h, chroma_w,
- chroma_stride, frm->chroma_step,
- is_intra, next_is_intra);
- if (encoding & FWHT_FRAME_UNENCODED)
- encoding |= FWHT_CR_UNENCODED;
- encoding &= ~FWHT_FRAME_UNENCODED;
- }
-
- if (frm->components_num == 4) {
- rlco_max = rlco + size / 2 - 256;
- encoding |= encode_plane(frm->alpha, ref_frm->alpha, &rlco,
- rlco_max, cf, height, width,
- stride, frm->luma_alpha_step,
- is_intra, next_is_intra);
- if (encoding & FWHT_FRAME_UNENCODED)
- encoding |= FWHT_ALPHA_UNENCODED;
- encoding &= ~FWHT_FRAME_UNENCODED;
- }
-
- cf->size = (rlco - cf->rlc_data) * sizeof(*rlco);
- return encoding;
-}
-
-static bool decode_plane(struct fwht_cframe *cf, const __be16 **rlco,
- u32 height, u32 width, const u8 *ref, u32 ref_stride,
- unsigned int ref_step, u8 *dst,
- unsigned int dst_stride, unsigned int dst_step,
- bool uncompressed, const __be16 *end_of_rlco_buf)
-{
- unsigned int copies = 0;
- s16 copy[8 * 8];
- u16 stat;
- unsigned int i, j;
- bool is_intra = !ref;
-
- width = round_up(width, 8);
- height = round_up(height, 8);
-
- if (uncompressed) {
- int i;
-
- if (end_of_rlco_buf + 1 < *rlco + width * height / 2)
- return false;
- for (i = 0; i < height; i++) {
- memcpy(dst, *rlco, width);
- dst += dst_stride;
- *rlco += width / 2;
- }
- return true;
- }
-
- /*
- * When decoding each macroblock the rlco pointer will be increased
- * by 65 * 2 bytes worst-case.
- * To avoid overflow the buffer has to be 65/64th of the actual raw
- * image size, just in case someone feeds it malicious data.
- */
- for (j = 0; j < height / 8; j++) {
- for (i = 0; i < width / 8; i++) {
- const u8 *refp = ref + j * 8 * ref_stride +
- i * 8 * ref_step;
- u8 *dstp = dst + j * 8 * dst_stride + i * 8 * dst_step;
-
- if (copies) {
- memcpy(cf->de_fwht, copy, sizeof(copy));
- if ((stat & PFRAME_BIT) && !is_intra)
- add_deltas(cf->de_fwht, refp,
- ref_stride, ref_step);
- fill_decoder_block(dstp, cf->de_fwht,
- dst_stride, dst_step);
- copies--;
- continue;
- }
-
- stat = derlc(rlco, cf->coeffs, end_of_rlco_buf);
- if (stat & OVERFLOW_BIT)
- return false;
- if ((stat & PFRAME_BIT) && !is_intra)
- dequantize_inter(cf->coeffs);
- else
- dequantize_intra(cf->coeffs);
-
- ifwht(cf->coeffs, cf->de_fwht,
- ((stat & PFRAME_BIT) && !is_intra) ? 0 : 1);
-
- copies = (stat & DUPS_MASK) >> 1;
- if (copies)
- memcpy(copy, cf->de_fwht, sizeof(copy));
- if ((stat & PFRAME_BIT) && !is_intra)
- add_deltas(cf->de_fwht, refp,
- ref_stride, ref_step);
- fill_decoder_block(dstp, cf->de_fwht, dst_stride,
- dst_step);
- }
- }
- return true;
-}
-
-bool fwht_decode_frame(struct fwht_cframe *cf, u32 hdr_flags,
- unsigned int components_num, unsigned int width,
- unsigned int height, const struct fwht_raw_frame *ref,
- unsigned int ref_stride, unsigned int ref_chroma_stride,
- struct fwht_raw_frame *dst, unsigned int dst_stride,
- unsigned int dst_chroma_stride)
-{
- const __be16 *rlco = cf->rlc_data;
- const __be16 *end_of_rlco_buf = cf->rlc_data +
- (cf->size / sizeof(*rlco)) - 1;
-
- if (!decode_plane(cf, &rlco, height, width, ref->luma, ref_stride,
- ref->luma_alpha_step, dst->luma, dst_stride,
- dst->luma_alpha_step,
- hdr_flags & FWHT_FL_LUMA_IS_UNCOMPRESSED,
- end_of_rlco_buf))
- return false;
-
- if (components_num >= 3) {
- u32 h = height;
- u32 w = width;
-
- if (!(hdr_flags & FWHT_FL_CHROMA_FULL_HEIGHT))
- h /= 2;
- if (!(hdr_flags & FWHT_FL_CHROMA_FULL_WIDTH))
- w /= 2;
-
- if (!decode_plane(cf, &rlco, h, w, ref->cb, ref_chroma_stride,
- ref->chroma_step, dst->cb, dst_chroma_stride,
- dst->chroma_step,
- hdr_flags & FWHT_FL_CB_IS_UNCOMPRESSED,
- end_of_rlco_buf))
- return false;
- if (!decode_plane(cf, &rlco, h, w, ref->cr, ref_chroma_stride,
- ref->chroma_step, dst->cr, dst_chroma_stride,
- dst->chroma_step,
- hdr_flags & FWHT_FL_CR_IS_UNCOMPRESSED,
- end_of_rlco_buf))
- return false;
- }
-
- if (components_num == 4)
- if (!decode_plane(cf, &rlco, height, width, ref->alpha, ref_stride,
- ref->luma_alpha_step, dst->alpha, dst_stride,
- dst->luma_alpha_step,
- hdr_flags & FWHT_FL_ALPHA_IS_UNCOMPRESSED,
- end_of_rlco_buf))
- return false;
- return true;
-}
diff --git a/drivers/media/platform/vicodec/codec-fwht.h b/drivers/media/platform/vicodec/codec-fwht.h
deleted file mode 100644
index b6fec2b..0000000
--- a/drivers/media/platform/vicodec/codec-fwht.h
+++ /dev/null
@@ -1,150 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
-/*
- * Copyright 2016 Tom aan de Wiel
- * Copyright 2018 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- */
-
-#ifndef CODEC_FWHT_H
-#define CODEC_FWHT_H
-
-#include <linux/types.h>
-#include <linux/bitops.h>
-#include <asm/byteorder.h>
-
-/*
- * The compressed format consists of a fwht_cframe_hdr struct followed by the
- * compressed frame data. The header contains the size of that data.
- * Each Y, Cb and Cr plane is compressed separately. If the compressed
- * size of each plane becomes larger than the uncompressed size, then
- * that plane is stored uncompressed and the corresponding bit is set
- * in the flags field of the header.
- *
- * Each compressed plane consists of macroblocks and each macroblock
- * is run-length-encoded. Each macroblock starts with a 16 bit value.
- * Bit 15 indicates if this is a P-coded macroblock (1) or not (0).
- * P-coded macroblocks contain a delta against the previous frame.
- *
- * Bits 1-12 contain a number. If non-zero, then this same macroblock
- * repeats that number of times. This results in a high degree of
- * compression for generated images like colorbars.
- *
- * Following this macroblock header the MB coefficients are run-length
- * encoded: the top 12 bits contain the coefficient, the bottom 4 bits
- * tell how many times this coefficient occurs. The value 0xf indicates
- * that the remainder of the macroblock should be filled with zeroes.
- *
- * All 16 and 32 bit values are stored in big-endian (network) order.
- *
- * Each fwht_cframe_hdr starts with an 8 byte magic header that is
- * guaranteed not to occur in the compressed frame data. This header
- * can be used to sync to the next frame.
- *
- * This codec uses the Fast Walsh Hadamard Transform. Tom aan de Wiel
- * developed this as part of a university project, specifically for use
- * with this driver. His project report can be found here:
- *
- * https://hverkuil.home.xs4all.nl/fwht.pdf
- */
-
-/*
- * This is a sequence of 8 bytes with the low 4 bits set to 0xf.
- *
- * This sequence cannot occur in the encoded data
- *
- * Note that these two magic values are symmetrical so endian issues here.
- */
-#define FWHT_MAGIC1 0x4f4f4f4f
-#define FWHT_MAGIC2 0xffffffff
-
-#define FWHT_VERSION 3
-
-/* Set if this is an interlaced format */
-#define FWHT_FL_IS_INTERLACED BIT(0)
-/* Set if this is a bottom-first (NTSC) interlaced format */
-#define FWHT_FL_IS_BOTTOM_FIRST BIT(1)
-/* Set if each 'frame' contains just one field */
-#define FWHT_FL_IS_ALTERNATE BIT(2)
-/*
- * If FWHT_FL_IS_ALTERNATE was set, then this is set if this
- * 'frame' is the bottom field, else it is the top field.
- */
-#define FWHT_FL_IS_BOTTOM_FIELD BIT(3)
-/* Set if this frame is uncompressed */
-#define FWHT_FL_LUMA_IS_UNCOMPRESSED BIT(4)
-#define FWHT_FL_CB_IS_UNCOMPRESSED BIT(5)
-#define FWHT_FL_CR_IS_UNCOMPRESSED BIT(6)
-#define FWHT_FL_CHROMA_FULL_HEIGHT BIT(7)
-#define FWHT_FL_CHROMA_FULL_WIDTH BIT(8)
-#define FWHT_FL_ALPHA_IS_UNCOMPRESSED BIT(9)
-#define FWHT_FL_I_FRAME BIT(10)
-
-/* A 4-values flag - the number of components - 1 */
-#define FWHT_FL_COMPONENTS_NUM_MSK GENMASK(18, 16)
-#define FWHT_FL_COMPONENTS_NUM_OFFSET 16
-
-#define FWHT_FL_PIXENC_MSK GENMASK(20, 19)
-#define FWHT_FL_PIXENC_OFFSET 19
-#define FWHT_FL_PIXENC_YUV (1 << FWHT_FL_PIXENC_OFFSET)
-#define FWHT_FL_PIXENC_RGB (2 << FWHT_FL_PIXENC_OFFSET)
-#define FWHT_FL_PIXENC_HSV (3 << FWHT_FL_PIXENC_OFFSET)
-
-/*
- * A macro to calculate the needed padding in order to make sure
- * both luma and chroma components resolutions are rounded up to
- * a multiple of 8
- */
-#define vic_round_dim(dim, div) (round_up((dim) / (div), 8) * (div))
-
-struct fwht_cframe_hdr {
- u32 magic1;
- u32 magic2;
- __be32 version;
- __be32 width, height;
- __be32 flags;
- __be32 colorspace;
- __be32 xfer_func;
- __be32 ycbcr_enc;
- __be32 quantization;
- __be32 size;
-};
-
-struct fwht_cframe {
- u16 i_frame_qp;
- u16 p_frame_qp;
- __be16 *rlc_data;
- s16 coeffs[8 * 8];
- s16 de_coeffs[8 * 8];
- s16 de_fwht[8 * 8];
- u32 size;
-};
-
-struct fwht_raw_frame {
- unsigned int width_div;
- unsigned int height_div;
- unsigned int luma_alpha_step;
- unsigned int chroma_step;
- unsigned int components_num;
- u8 *buf;
- u8 *luma, *cb, *cr, *alpha;
-};
-
-#define FWHT_FRAME_PCODED BIT(0)
-#define FWHT_FRAME_UNENCODED BIT(1)
-#define FWHT_LUMA_UNENCODED BIT(2)
-#define FWHT_CB_UNENCODED BIT(3)
-#define FWHT_CR_UNENCODED BIT(4)
-#define FWHT_ALPHA_UNENCODED BIT(5)
-
-u32 fwht_encode_frame(struct fwht_raw_frame *frm,
- struct fwht_raw_frame *ref_frm,
- struct fwht_cframe *cf,
- bool is_intra, bool next_is_intra,
- unsigned int width, unsigned int height,
- unsigned int stride, unsigned int chroma_stride);
-bool fwht_decode_frame(struct fwht_cframe *cf, u32 hdr_flags,
- unsigned int components_num, unsigned int width,
- unsigned int height, const struct fwht_raw_frame *ref,
- unsigned int ref_stride, unsigned int ref_chroma_stride,
- struct fwht_raw_frame *dst, unsigned int dst_stride,
- unsigned int dst_chroma_stride);
-#endif
diff --git a/drivers/media/platform/vicodec/codec-v4l2-fwht.c b/drivers/media/platform/vicodec/codec-v4l2-fwht.c
deleted file mode 100644
index b6e39fb..0000000
--- a/drivers/media/platform/vicodec/codec-v4l2-fwht.c
+++ /dev/null
@@ -1,367 +0,0 @@
-// SPDX-License-Identifier: LGPL-2.1
-/*
- * A V4L2 frontend for the FWHT codec
- *
- * Copyright 2018 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- */
-
-#include <linux/errno.h>
-#include <linux/string.h>
-#include <linux/videodev2.h>
-#include "codec-v4l2-fwht.h"
-
-static const struct v4l2_fwht_pixfmt_info v4l2_fwht_pixfmts[] = {
- { V4L2_PIX_FMT_YUV420, 1, 3, 2, 1, 1, 2, 2, 3, 3, FWHT_FL_PIXENC_YUV},
- { V4L2_PIX_FMT_YVU420, 1, 3, 2, 1, 1, 2, 2, 3, 3, FWHT_FL_PIXENC_YUV},
- { V4L2_PIX_FMT_YUV422P, 1, 2, 1, 1, 1, 2, 1, 3, 3, FWHT_FL_PIXENC_YUV},
- { V4L2_PIX_FMT_NV12, 1, 3, 2, 1, 2, 2, 2, 3, 2, FWHT_FL_PIXENC_YUV},
- { V4L2_PIX_FMT_NV21, 1, 3, 2, 1, 2, 2, 2, 3, 2, FWHT_FL_PIXENC_YUV},
- { V4L2_PIX_FMT_NV16, 1, 2, 1, 1, 2, 2, 1, 3, 2, FWHT_FL_PIXENC_YUV},
- { V4L2_PIX_FMT_NV61, 1, 2, 1, 1, 2, 2, 1, 3, 2, FWHT_FL_PIXENC_YUV},
- { V4L2_PIX_FMT_NV24, 1, 3, 1, 1, 2, 1, 1, 3, 2, FWHT_FL_PIXENC_YUV},
- { V4L2_PIX_FMT_NV42, 1, 3, 1, 1, 2, 1, 1, 3, 2, FWHT_FL_PIXENC_YUV},
- { V4L2_PIX_FMT_YUYV, 2, 2, 1, 2, 4, 2, 1, 3, 1, FWHT_FL_PIXENC_YUV},
- { V4L2_PIX_FMT_YVYU, 2, 2, 1, 2, 4, 2, 1, 3, 1, FWHT_FL_PIXENC_YUV},
- { V4L2_PIX_FMT_UYVY, 2, 2, 1, 2, 4, 2, 1, 3, 1, FWHT_FL_PIXENC_YUV},
- { V4L2_PIX_FMT_VYUY, 2, 2, 1, 2, 4, 2, 1, 3, 1, FWHT_FL_PIXENC_YUV},
- { V4L2_PIX_FMT_BGR24, 3, 3, 1, 3, 3, 1, 1, 3, 1, FWHT_FL_PIXENC_RGB},
- { V4L2_PIX_FMT_RGB24, 3, 3, 1, 3, 3, 1, 1, 3, 1, FWHT_FL_PIXENC_RGB},
- { V4L2_PIX_FMT_HSV24, 3, 3, 1, 3, 3, 1, 1, 3, 1, FWHT_FL_PIXENC_HSV},
- { V4L2_PIX_FMT_BGR32, 4, 4, 1, 4, 4, 1, 1, 4, 1, FWHT_FL_PIXENC_RGB},
- { V4L2_PIX_FMT_XBGR32, 4, 4, 1, 4, 4, 1, 1, 4, 1, FWHT_FL_PIXENC_RGB},
- { V4L2_PIX_FMT_ABGR32, 4, 4, 1, 4, 4, 1, 1, 4, 1, FWHT_FL_PIXENC_RGB},
- { V4L2_PIX_FMT_RGB32, 4, 4, 1, 4, 4, 1, 1, 4, 1, FWHT_FL_PIXENC_RGB},
- { V4L2_PIX_FMT_XRGB32, 4, 4, 1, 4, 4, 1, 1, 4, 1, FWHT_FL_PIXENC_RGB},
- { V4L2_PIX_FMT_ARGB32, 4, 4, 1, 4, 4, 1, 1, 4, 1, FWHT_FL_PIXENC_RGB},
- { V4L2_PIX_FMT_BGRX32, 4, 4, 1, 4, 4, 1, 1, 4, 1, FWHT_FL_PIXENC_RGB},
- { V4L2_PIX_FMT_BGRA32, 4, 4, 1, 4, 4, 1, 1, 4, 1, FWHT_FL_PIXENC_RGB},
- { V4L2_PIX_FMT_RGBX32, 4, 4, 1, 4, 4, 1, 1, 4, 1, FWHT_FL_PIXENC_RGB},
- { V4L2_PIX_FMT_RGBA32, 4, 4, 1, 4, 4, 1, 1, 4, 1, FWHT_FL_PIXENC_RGB},
- { V4L2_PIX_FMT_HSV32, 4, 4, 1, 4, 4, 1, 1, 4, 1, FWHT_FL_PIXENC_HSV},
- { V4L2_PIX_FMT_GREY, 1, 1, 1, 1, 0, 1, 1, 1, 1, FWHT_FL_PIXENC_RGB},
-};
-
-bool v4l2_fwht_validate_fmt(const struct v4l2_fwht_pixfmt_info *info,
- u32 width_div, u32 height_div, u32 components_num,
- u32 pixenc)
-{
- if (info->width_div == width_div &&
- info->height_div == height_div &&
- (!pixenc || info->pixenc == pixenc) &&
- info->components_num == components_num)
- return true;
- return false;
-}
-
-const struct v4l2_fwht_pixfmt_info *v4l2_fwht_find_nth_fmt(u32 width_div,
- u32 height_div,
- u32 components_num,
- u32 pixenc,
- unsigned int start_idx)
-{
- unsigned int i;
-
- for (i = 0; i < ARRAY_SIZE(v4l2_fwht_pixfmts); i++) {
- bool is_valid = v4l2_fwht_validate_fmt(&v4l2_fwht_pixfmts[i],
- width_div, height_div,
- components_num, pixenc);
- if (is_valid) {
- if (start_idx == 0)
- return v4l2_fwht_pixfmts + i;
- start_idx--;
- }
- }
- return NULL;
-}
-
-const struct v4l2_fwht_pixfmt_info *v4l2_fwht_find_pixfmt(u32 pixelformat)
-{
- unsigned int i;
-
- for (i = 0; i < ARRAY_SIZE(v4l2_fwht_pixfmts); i++)
- if (v4l2_fwht_pixfmts[i].id == pixelformat)
- return v4l2_fwht_pixfmts + i;
- return NULL;
-}
-
-const struct v4l2_fwht_pixfmt_info *v4l2_fwht_get_pixfmt(u32 idx)
-{
- if (idx >= ARRAY_SIZE(v4l2_fwht_pixfmts))
- return NULL;
- return v4l2_fwht_pixfmts + idx;
-}
-
-static int prepare_raw_frame(struct fwht_raw_frame *rf,
- const struct v4l2_fwht_pixfmt_info *info, u8 *buf,
- unsigned int size)
-{
- rf->luma = buf;
- rf->width_div = info->width_div;
- rf->height_div = info->height_div;
- rf->luma_alpha_step = info->luma_alpha_step;
- rf->chroma_step = info->chroma_step;
- rf->alpha = NULL;
- rf->components_num = info->components_num;
-
- /*
- * The buffer is NULL if it is the reference
- * frame of an I-frame in the stateless decoder
- */
- if (!buf) {
- rf->luma = NULL;
- rf->cb = NULL;
- rf->cr = NULL;
- rf->alpha = NULL;
- return 0;
- }
- switch (info->id) {
- case V4L2_PIX_FMT_GREY:
- rf->cb = NULL;
- rf->cr = NULL;
- break;
- case V4L2_PIX_FMT_YUV420:
- rf->cb = rf->luma + size;
- rf->cr = rf->cb + size / 4;
- break;
- case V4L2_PIX_FMT_YVU420:
- rf->cr = rf->luma + size;
- rf->cb = rf->cr + size / 4;
- break;
- case V4L2_PIX_FMT_YUV422P:
- rf->cb = rf->luma + size;
- rf->cr = rf->cb + size / 2;
- break;
- case V4L2_PIX_FMT_NV12:
- case V4L2_PIX_FMT_NV16:
- case V4L2_PIX_FMT_NV24:
- rf->cb = rf->luma + size;
- rf->cr = rf->cb + 1;
- break;
- case V4L2_PIX_FMT_NV21:
- case V4L2_PIX_FMT_NV61:
- case V4L2_PIX_FMT_NV42:
- rf->cr = rf->luma + size;
- rf->cb = rf->cr + 1;
- break;
- case V4L2_PIX_FMT_YUYV:
- rf->cb = rf->luma + 1;
- rf->cr = rf->cb + 2;
- break;
- case V4L2_PIX_FMT_YVYU:
- rf->cr = rf->luma + 1;
- rf->cb = rf->cr + 2;
- break;
- case V4L2_PIX_FMT_UYVY:
- rf->cb = rf->luma;
- rf->cr = rf->cb + 2;
- rf->luma++;
- break;
- case V4L2_PIX_FMT_VYUY:
- rf->cr = rf->luma;
- rf->cb = rf->cr + 2;
- rf->luma++;
- break;
- case V4L2_PIX_FMT_RGB24:
- case V4L2_PIX_FMT_HSV24:
- rf->cr = rf->luma;
- rf->cb = rf->cr + 2;
- rf->luma++;
- break;
- case V4L2_PIX_FMT_BGR24:
- rf->cb = rf->luma;
- rf->cr = rf->cb + 2;
- rf->luma++;
- break;
- case V4L2_PIX_FMT_RGB32:
- case V4L2_PIX_FMT_XRGB32:
- case V4L2_PIX_FMT_HSV32:
- case V4L2_PIX_FMT_ARGB32:
- rf->alpha = rf->luma;
- rf->cr = rf->luma + 1;
- rf->cb = rf->cr + 2;
- rf->luma += 2;
- break;
- case V4L2_PIX_FMT_BGR32:
- case V4L2_PIX_FMT_XBGR32:
- case V4L2_PIX_FMT_ABGR32:
- rf->cb = rf->luma;
- rf->cr = rf->cb + 2;
- rf->luma++;
- rf->alpha = rf->cr + 1;
- break;
- case V4L2_PIX_FMT_BGRX32:
- case V4L2_PIX_FMT_BGRA32:
- rf->alpha = rf->luma;
- rf->cb = rf->luma + 1;
- rf->cr = rf->cb + 2;
- rf->luma += 2;
- break;
- case V4L2_PIX_FMT_RGBX32:
- case V4L2_PIX_FMT_RGBA32:
- rf->alpha = rf->luma + 3;
- rf->cr = rf->luma;
- rf->cb = rf->cr + 2;
- rf->luma++;
- break;
- default:
- return -EINVAL;
- }
- return 0;
-}
-
-int v4l2_fwht_encode(struct v4l2_fwht_state *state, u8 *p_in, u8 *p_out)
-{
- unsigned int size = state->stride * state->coded_height;
- unsigned int chroma_stride = state->stride;
- const struct v4l2_fwht_pixfmt_info *info = state->info;
- struct fwht_cframe_hdr *p_hdr;
- struct fwht_cframe cf;
- struct fwht_raw_frame rf;
- u32 encoding;
- u32 flags = 0;
-
- if (!info)
- return -EINVAL;
-
- if (prepare_raw_frame(&rf, info, p_in, size))
- return -EINVAL;
-
- if (info->planes_num == 3)
- chroma_stride /= 2;
-
- if (info->id == V4L2_PIX_FMT_NV24 ||
- info->id == V4L2_PIX_FMT_NV42)
- chroma_stride *= 2;
-
- cf.i_frame_qp = state->i_frame_qp;
- cf.p_frame_qp = state->p_frame_qp;
- cf.rlc_data = (__be16 *)(p_out + sizeof(*p_hdr));
-
- encoding = fwht_encode_frame(&rf, &state->ref_frame, &cf,
- !state->gop_cnt,
- state->gop_cnt == state->gop_size - 1,
- state->visible_width,
- state->visible_height,
- state->stride, chroma_stride);
- if (!(encoding & FWHT_FRAME_PCODED))
- state->gop_cnt = 0;
- if (++state->gop_cnt >= state->gop_size)
- state->gop_cnt = 0;
-
- p_hdr = (struct fwht_cframe_hdr *)p_out;
- p_hdr->magic1 = FWHT_MAGIC1;
- p_hdr->magic2 = FWHT_MAGIC2;
- p_hdr->version = htonl(FWHT_VERSION);
- p_hdr->width = htonl(state->visible_width);
- p_hdr->height = htonl(state->visible_height);
- flags |= (info->components_num - 1) << FWHT_FL_COMPONENTS_NUM_OFFSET;
- flags |= info->pixenc;
- if (encoding & FWHT_LUMA_UNENCODED)
- flags |= FWHT_FL_LUMA_IS_UNCOMPRESSED;
- if (encoding & FWHT_CB_UNENCODED)
- flags |= FWHT_FL_CB_IS_UNCOMPRESSED;
- if (encoding & FWHT_CR_UNENCODED)
- flags |= FWHT_FL_CR_IS_UNCOMPRESSED;
- if (encoding & FWHT_ALPHA_UNENCODED)
- flags |= FWHT_FL_ALPHA_IS_UNCOMPRESSED;
- if (!(encoding & FWHT_FRAME_PCODED))
- flags |= FWHT_FL_I_FRAME;
- if (rf.height_div == 1)
- flags |= FWHT_FL_CHROMA_FULL_HEIGHT;
- if (rf.width_div == 1)
- flags |= FWHT_FL_CHROMA_FULL_WIDTH;
- p_hdr->flags = htonl(flags);
- p_hdr->colorspace = htonl(state->colorspace);
- p_hdr->xfer_func = htonl(state->xfer_func);
- p_hdr->ycbcr_enc = htonl(state->ycbcr_enc);
- p_hdr->quantization = htonl(state->quantization);
- p_hdr->size = htonl(cf.size);
- return cf.size + sizeof(*p_hdr);
-}
-
-int v4l2_fwht_decode(struct v4l2_fwht_state *state, u8 *p_in, u8 *p_out)
-{
- u32 flags;
- struct fwht_cframe cf;
- unsigned int components_num = 3;
- unsigned int version;
- const struct v4l2_fwht_pixfmt_info *info;
- unsigned int hdr_width_div, hdr_height_div;
- struct fwht_raw_frame dst_rf;
- unsigned int dst_chroma_stride = state->stride;
- unsigned int ref_chroma_stride = state->ref_stride;
- unsigned int dst_size = state->stride * state->coded_height;
- unsigned int ref_size;
-
- if (!state->info)
- return -EINVAL;
-
- info = state->info;
-
- version = ntohl(state->header.version);
- if (!version || version > FWHT_VERSION) {
- pr_err("version %d is not supported, current version is %d\n",
- version, FWHT_VERSION);
- return -EINVAL;
- }
-
- if (state->header.magic1 != FWHT_MAGIC1 ||
- state->header.magic2 != FWHT_MAGIC2)
- return -EINVAL;
-
- /* TODO: support resolution changes */
- if (ntohl(state->header.width) != state->visible_width ||
- ntohl(state->header.height) != state->visible_height)
- return -EINVAL;
-
- flags = ntohl(state->header.flags);
-
- if (version >= 2) {
- if ((flags & FWHT_FL_PIXENC_MSK) != info->pixenc)
- return -EINVAL;
- components_num = 1 + ((flags & FWHT_FL_COMPONENTS_NUM_MSK) >>
- FWHT_FL_COMPONENTS_NUM_OFFSET);
- }
-
- if (components_num != info->components_num)
- return -EINVAL;
-
- state->colorspace = ntohl(state->header.colorspace);
- state->xfer_func = ntohl(state->header.xfer_func);
- state->ycbcr_enc = ntohl(state->header.ycbcr_enc);
- state->quantization = ntohl(state->header.quantization);
- cf.rlc_data = (__be16 *)p_in;
- cf.size = ntohl(state->header.size);
-
- hdr_width_div = (flags & FWHT_FL_CHROMA_FULL_WIDTH) ? 1 : 2;
- hdr_height_div = (flags & FWHT_FL_CHROMA_FULL_HEIGHT) ? 1 : 2;
- if (hdr_width_div != info->width_div ||
- hdr_height_div != info->height_div)
- return -EINVAL;
-
- if (prepare_raw_frame(&dst_rf, info, p_out, dst_size))
- return -EINVAL;
- if (info->planes_num == 3) {
- dst_chroma_stride /= 2;
- ref_chroma_stride /= 2;
- }
- if (info->id == V4L2_PIX_FMT_NV24 ||
- info->id == V4L2_PIX_FMT_NV42) {
- dst_chroma_stride *= 2;
- ref_chroma_stride *= 2;
- }
-
-
- ref_size = state->ref_stride * state->coded_height;
-
- if (prepare_raw_frame(&state->ref_frame, info, state->ref_frame.buf,
- ref_size))
- return -EINVAL;
-
- if (!fwht_decode_frame(&cf, flags, components_num,
- state->visible_width, state->visible_height,
- &state->ref_frame, state->ref_stride, ref_chroma_stride,
- &dst_rf, state->stride, dst_chroma_stride))
- return -EINVAL;
- return 0;
-}
diff --git a/drivers/media/platform/vicodec/codec-v4l2-fwht.h b/drivers/media/platform/vicodec/codec-v4l2-fwht.h
deleted file mode 100644
index 1a0d2a9..0000000
--- a/drivers/media/platform/vicodec/codec-v4l2-fwht.h
+++ /dev/null
@@ -1,64 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1 */
-/*
- * Copyright 2018 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- */
-
-#ifndef CODEC_V4L2_FWHT_H
-#define CODEC_V4L2_FWHT_H
-
-#include "codec-fwht.h"
-
-struct v4l2_fwht_pixfmt_info {
- u32 id;
- unsigned int bytesperline_mult;
- unsigned int sizeimage_mult;
- unsigned int sizeimage_div;
- unsigned int luma_alpha_step;
- unsigned int chroma_step;
- /* Chroma plane subsampling */
- unsigned int width_div;
- unsigned int height_div;
- unsigned int components_num;
- unsigned int planes_num;
- unsigned int pixenc;
-};
-
-struct v4l2_fwht_state {
- const struct v4l2_fwht_pixfmt_info *info;
- unsigned int visible_width;
- unsigned int visible_height;
- unsigned int coded_width;
- unsigned int coded_height;
- unsigned int stride;
- unsigned int ref_stride;
- unsigned int gop_size;
- unsigned int gop_cnt;
- u16 i_frame_qp;
- u16 p_frame_qp;
-
- enum v4l2_colorspace colorspace;
- enum v4l2_ycbcr_encoding ycbcr_enc;
- enum v4l2_xfer_func xfer_func;
- enum v4l2_quantization quantization;
-
- struct fwht_raw_frame ref_frame;
- struct fwht_cframe_hdr header;
- u8 *compressed_frame;
- u64 ref_frame_ts;
-};
-
-const struct v4l2_fwht_pixfmt_info *v4l2_fwht_find_pixfmt(u32 pixelformat);
-const struct v4l2_fwht_pixfmt_info *v4l2_fwht_get_pixfmt(u32 idx);
-bool v4l2_fwht_validate_fmt(const struct v4l2_fwht_pixfmt_info *info,
- u32 width_div, u32 height_div, u32 components_num,
- u32 pixenc);
-const struct v4l2_fwht_pixfmt_info *v4l2_fwht_find_nth_fmt(u32 width_div,
- u32 height_div,
- u32 components_num,
- u32 pixenc,
- unsigned int start_idx);
-
-int v4l2_fwht_encode(struct v4l2_fwht_state *state, u8 *p_in, u8 *p_out);
-int v4l2_fwht_decode(struct v4l2_fwht_state *state, u8 *p_in, u8 *p_out);
-
-#endif
diff --git a/drivers/media/platform/vicodec/vicodec-core.c b/drivers/media/platform/vicodec/vicodec-core.c
deleted file mode 100644
index c77281d..0000000
--- a/drivers/media/platform/vicodec/vicodec-core.c
+++ /dev/null
@@ -1,2300 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * A virtual codec example device.
- *
- * Copyright 2018 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- *
- * This is a virtual codec device driver for testing the codec framework.
- * It simulates a device that uses memory buffers for both source and
- * destination and encodes or decodes the data.
- */
-
-#include <linux/module.h>
-#include <linux/delay.h>
-#include <linux/fs.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-
-#include <linux/platform_device.h>
-#include <media/v4l2-mem2mem.h>
-#include <media/v4l2-device.h>
-#include <media/v4l2-ioctl.h>
-#include <media/v4l2-ctrls.h>
-#include <media/v4l2-event.h>
-#include <media/videobuf2-vmalloc.h>
-
-#include "codec-v4l2-fwht.h"
-
-MODULE_DESCRIPTION("Virtual codec device");
-MODULE_AUTHOR("Hans Verkuil <hans.verkuil@cisco.com>");
-MODULE_LICENSE("GPL v2");
-
-static bool multiplanar;
-module_param(multiplanar, bool, 0444);
-MODULE_PARM_DESC(multiplanar,
- " use multi-planar API instead of single-planar API");
-
-static unsigned int debug;
-module_param(debug, uint, 0644);
-MODULE_PARM_DESC(debug, " activates debug info");
-
-#define VICODEC_NAME "vicodec"
-#define MAX_WIDTH 4096U
-#define MIN_WIDTH 640U
-#define MAX_HEIGHT 2160U
-#define MIN_HEIGHT 360U
-
-#define dprintk(dev, fmt, arg...) \
- v4l2_dbg(1, debug, &dev->v4l2_dev, "%s: " fmt, __func__, ## arg)
-
-
-struct pixfmt_info {
- u32 id;
- unsigned int bytesperline_mult;
- unsigned int sizeimage_mult;
- unsigned int sizeimage_div;
- unsigned int luma_step;
- unsigned int chroma_step;
- /* Chroma plane subsampling */
- unsigned int width_div;
- unsigned int height_div;
-};
-
-static const struct v4l2_fwht_pixfmt_info pixfmt_fwht = {
- V4L2_PIX_FMT_FWHT, 0, 3, 1, 1, 1, 1, 1, 0, 1
-};
-
-static const struct v4l2_fwht_pixfmt_info pixfmt_stateless_fwht = {
- V4L2_PIX_FMT_FWHT_STATELESS, 0, 3, 1, 1, 1, 1, 1, 0, 1
-};
-
-static void vicodec_dev_release(struct device *dev)
-{
-}
-
-static struct platform_device vicodec_pdev = {
- .name = VICODEC_NAME,
- .dev.release = vicodec_dev_release,
-};
-
-/* Per-queue, driver-specific private data */
-struct vicodec_q_data {
- unsigned int coded_width;
- unsigned int coded_height;
- unsigned int visible_width;
- unsigned int visible_height;
- unsigned int sizeimage;
- unsigned int vb2_sizeimage;
- unsigned int sequence;
- const struct v4l2_fwht_pixfmt_info *info;
-};
-
-enum {
- V4L2_M2M_SRC = 0,
- V4L2_M2M_DST = 1,
-};
-
-struct vicodec_dev_instance {
- struct video_device vfd;
- struct mutex mutex;
- spinlock_t lock;
- struct v4l2_m2m_dev *m2m_dev;
-};
-
-struct vicodec_dev {
- struct v4l2_device v4l2_dev;
- struct vicodec_dev_instance stateful_enc;
- struct vicodec_dev_instance stateful_dec;
- struct vicodec_dev_instance stateless_dec;
-#ifdef CONFIG_MEDIA_CONTROLLER
- struct media_device mdev;
-#endif
-
-};
-
-struct vicodec_ctx {
- struct v4l2_fh fh;
- struct vicodec_dev *dev;
- bool is_enc;
- bool is_stateless;
- bool is_draining;
- bool next_is_last;
- bool has_stopped;
- spinlock_t *lock;
-
- struct v4l2_ctrl_handler hdl;
-
- struct vb2_v4l2_buffer *last_src_buf;
-
- /* Source and destination queue data */
- struct vicodec_q_data q_data[2];
- struct v4l2_fwht_state state;
-
- u32 cur_buf_offset;
- u32 comp_max_size;
- u32 comp_size;
- u32 header_size;
- u32 comp_magic_cnt;
- bool comp_has_frame;
- bool comp_has_next_frame;
- bool first_source_change_sent;
- bool source_changed;
-};
-
-static const struct v4l2_event vicodec_eos_event = {
- .type = V4L2_EVENT_EOS
-};
-
-static inline struct vicodec_ctx *file2ctx(struct file *file)
-{
- return container_of(file->private_data, struct vicodec_ctx, fh);
-}
-
-static struct vicodec_q_data *get_q_data(struct vicodec_ctx *ctx,
- enum v4l2_buf_type type)
-{
- switch (type) {
- case V4L2_BUF_TYPE_VIDEO_OUTPUT:
- case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
- return &ctx->q_data[V4L2_M2M_SRC];
- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
- case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
- return &ctx->q_data[V4L2_M2M_DST];
- default:
- break;
- }
- return NULL;
-}
-
-static void copy_cap_to_ref(const u8 *cap, const struct v4l2_fwht_pixfmt_info *info,
- struct v4l2_fwht_state *state)
-{
- int plane_idx;
- u8 *p_ref = state->ref_frame.buf;
- unsigned int cap_stride = state->stride;
- unsigned int ref_stride = state->ref_stride;
-
- for (plane_idx = 0; plane_idx < info->planes_num; plane_idx++) {
- int i;
- unsigned int h_div = (plane_idx == 1 || plane_idx == 2) ?
- info->height_div : 1;
- const u8 *row_cap = cap;
- u8 *row_ref = p_ref;
-
- if (info->planes_num == 3 && plane_idx == 1) {
- cap_stride /= 2;
- ref_stride /= 2;
- }
-
- if (plane_idx == 1 &&
- (info->id == V4L2_PIX_FMT_NV24 ||
- info->id == V4L2_PIX_FMT_NV42)) {
- cap_stride *= 2;
- ref_stride *= 2;
- }
-
- for (i = 0; i < state->visible_height / h_div; i++) {
- memcpy(row_ref, row_cap, ref_stride);
- row_ref += ref_stride;
- row_cap += cap_stride;
- }
- cap += cap_stride * (state->coded_height / h_div);
- p_ref += ref_stride * (state->coded_height / h_div);
- }
-}
-
-static bool validate_by_version(unsigned int flags, unsigned int version)
-{
- if (!version || version > FWHT_VERSION)
- return false;
-
- if (version >= 2) {
- unsigned int components_num = 1 +
- ((flags & FWHT_FL_COMPONENTS_NUM_MSK) >>
- FWHT_FL_COMPONENTS_NUM_OFFSET);
- unsigned int pixenc = flags & FWHT_FL_PIXENC_MSK;
-
- if (components_num == 0 || components_num > 4 || !pixenc)
- return false;
- }
- return true;
-}
-
-static bool validate_stateless_params_flags(const struct v4l2_ctrl_fwht_params *params,
- const struct v4l2_fwht_pixfmt_info *cur_info)
-{
- unsigned int width_div =
- (params->flags & FWHT_FL_CHROMA_FULL_WIDTH) ? 1 : 2;
- unsigned int height_div =
- (params->flags & FWHT_FL_CHROMA_FULL_HEIGHT) ? 1 : 2;
- unsigned int components_num = 3;
- unsigned int pixenc = 0;
-
- if (params->version < 3)
- return false;
-
- components_num = 1 + ((params->flags & FWHT_FL_COMPONENTS_NUM_MSK) >>
- FWHT_FL_COMPONENTS_NUM_OFFSET);
- pixenc = (params->flags & FWHT_FL_PIXENC_MSK);
- if (v4l2_fwht_validate_fmt(cur_info, width_div, height_div,
- components_num, pixenc))
- return true;
- return false;
-}
-
-
-static void update_state_from_header(struct vicodec_ctx *ctx)
-{
- const struct fwht_cframe_hdr *p_hdr = &ctx->state.header;
-
- ctx->state.visible_width = ntohl(p_hdr->width);
- ctx->state.visible_height = ntohl(p_hdr->height);
- ctx->state.colorspace = ntohl(p_hdr->colorspace);
- ctx->state.xfer_func = ntohl(p_hdr->xfer_func);
- ctx->state.ycbcr_enc = ntohl(p_hdr->ycbcr_enc);
- ctx->state.quantization = ntohl(p_hdr->quantization);
-}
-
-static int device_process(struct vicodec_ctx *ctx,
- struct vb2_v4l2_buffer *src_vb,
- struct vb2_v4l2_buffer *dst_vb)
-{
- struct vicodec_dev *dev = ctx->dev;
- struct v4l2_fwht_state *state = &ctx->state;
- u8 *p_src, *p_dst;
- int ret = 0;
-
- if (ctx->is_enc || ctx->is_stateless)
- p_src = vb2_plane_vaddr(&src_vb->vb2_buf, 0);
- else
- p_src = state->compressed_frame;
-
- if (ctx->is_stateless) {
- struct media_request *src_req = src_vb->vb2_buf.req_obj.req;
-
- ret = v4l2_ctrl_request_setup(src_req, &ctx->hdl);
- if (ret)
- return ret;
- update_state_from_header(ctx);
-
- ctx->state.header.size =
- htonl(vb2_get_plane_payload(&src_vb->vb2_buf, 0));
- /*
- * set the reference buffer from the reference timestamp
- * only if this is a P-frame
- */
- if (!(ntohl(ctx->state.header.flags) & FWHT_FL_I_FRAME)) {
- struct vb2_buffer *ref_vb2_buf;
- int ref_buf_idx;
- struct vb2_queue *vq_cap =
- v4l2_m2m_get_vq(ctx->fh.m2m_ctx,
- V4L2_BUF_TYPE_VIDEO_CAPTURE);
-
- ref_buf_idx = vb2_find_timestamp(vq_cap,
- ctx->state.ref_frame_ts, 0);
- if (ref_buf_idx < 0)
- return -EINVAL;
-
- ref_vb2_buf = vq_cap->bufs[ref_buf_idx];
- if (ref_vb2_buf->state == VB2_BUF_STATE_ERROR)
- ret = -EINVAL;
- ctx->state.ref_frame.buf =
- vb2_plane_vaddr(ref_vb2_buf, 0);
- } else {
- ctx->state.ref_frame.buf = NULL;
- }
- }
- p_dst = vb2_plane_vaddr(&dst_vb->vb2_buf, 0);
- if (!p_src || !p_dst) {
- v4l2_err(&dev->v4l2_dev,
- "Acquiring kernel pointers to buffers failed\n");
- return -EFAULT;
- }
-
- if (ctx->is_enc) {
- struct vicodec_q_data *q_src;
- int comp_sz_or_errcode;
-
- q_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
- state->info = q_src->info;
- comp_sz_or_errcode = v4l2_fwht_encode(state, p_src, p_dst);
- if (comp_sz_or_errcode < 0)
- return comp_sz_or_errcode;
- vb2_set_plane_payload(&dst_vb->vb2_buf, 0, comp_sz_or_errcode);
- } else {
- struct vicodec_q_data *q_dst;
- unsigned int comp_frame_size = ntohl(ctx->state.header.size);
-
- q_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
- if (comp_frame_size > ctx->comp_max_size)
- return -EINVAL;
- state->info = q_dst->info;
- ret = v4l2_fwht_decode(state, p_src, p_dst);
- if (ret < 0)
- return ret;
- if (!ctx->is_stateless)
- copy_cap_to_ref(p_dst, ctx->state.info, &ctx->state);
-
- vb2_set_plane_payload(&dst_vb->vb2_buf, 0, q_dst->sizeimage);
- if (ntohl(ctx->state.header.flags) & FWHT_FL_I_FRAME)
- dst_vb->flags |= V4L2_BUF_FLAG_KEYFRAME;
- else
- dst_vb->flags |= V4L2_BUF_FLAG_PFRAME;
- }
- return ret;
-}
-
-/*
- * mem2mem callbacks
- */
-static enum vb2_buffer_state get_next_header(struct vicodec_ctx *ctx,
- u8 **pp, u32 sz)
-{
- static const u8 magic[] = {
- 0x4f, 0x4f, 0x4f, 0x4f, 0xff, 0xff, 0xff, 0xff
- };
- u8 *p = *pp;
- u32 state;
- u8 *header = (u8 *)&ctx->state.header;
-
- state = VB2_BUF_STATE_DONE;
-
- if (!ctx->header_size) {
- state = VB2_BUF_STATE_ERROR;
- for (; p < *pp + sz; p++) {
- u32 copy;
-
- p = memchr(p, magic[ctx->comp_magic_cnt],
- *pp + sz - p);
- if (!p) {
- ctx->comp_magic_cnt = 0;
- p = *pp + sz;
- break;
- }
- copy = sizeof(magic) - ctx->comp_magic_cnt;
- if (*pp + sz - p < copy)
- copy = *pp + sz - p;
-
- memcpy(header + ctx->comp_magic_cnt, p, copy);
- ctx->comp_magic_cnt += copy;
- if (!memcmp(header, magic, ctx->comp_magic_cnt)) {
- p += copy;
- state = VB2_BUF_STATE_DONE;
- break;
- }
- ctx->comp_magic_cnt = 0;
- }
- if (ctx->comp_magic_cnt < sizeof(magic)) {
- *pp = p;
- return state;
- }
- ctx->header_size = sizeof(magic);
- }
-
- if (ctx->header_size < sizeof(struct fwht_cframe_hdr)) {
- u32 copy = sizeof(struct fwht_cframe_hdr) - ctx->header_size;
-
- if (*pp + sz - p < copy)
- copy = *pp + sz - p;
-
- memcpy(header + ctx->header_size, p, copy);
- p += copy;
- ctx->header_size += copy;
- }
- *pp = p;
- return state;
-}
-
-/* device_run() - prepares and starts the device */
-static void device_run(void *priv)
-{
- struct vicodec_ctx *ctx = priv;
- struct vicodec_dev *dev = ctx->dev;
- struct vb2_v4l2_buffer *src_buf, *dst_buf;
- struct vicodec_q_data *q_src, *q_dst;
- u32 state;
- struct media_request *src_req;
-
- src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
- dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
- src_req = src_buf->vb2_buf.req_obj.req;
-
- q_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
- q_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
-
- state = VB2_BUF_STATE_DONE;
- if (device_process(ctx, src_buf, dst_buf))
- state = VB2_BUF_STATE_ERROR;
- else
- dst_buf->sequence = q_dst->sequence++;
- dst_buf->flags &= ~V4L2_BUF_FLAG_LAST;
- v4l2_m2m_buf_copy_metadata(src_buf, dst_buf, false);
-
- spin_lock(ctx->lock);
- if (!ctx->comp_has_next_frame && src_buf == ctx->last_src_buf) {
- dst_buf->flags |= V4L2_BUF_FLAG_LAST;
- v4l2_event_queue_fh(&ctx->fh, &vicodec_eos_event);
- ctx->is_draining = false;
- ctx->has_stopped = true;
- }
- if (ctx->is_enc || ctx->is_stateless) {
- src_buf->sequence = q_src->sequence++;
- src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
- v4l2_m2m_buf_done(src_buf, state);
- } else if (vb2_get_plane_payload(&src_buf->vb2_buf, 0) == ctx->cur_buf_offset) {
- src_buf->sequence = q_src->sequence++;
- src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
- v4l2_m2m_buf_done(src_buf, state);
- ctx->cur_buf_offset = 0;
- ctx->comp_has_next_frame = false;
- }
- v4l2_m2m_buf_done(dst_buf, state);
-
- ctx->comp_size = 0;
- ctx->header_size = 0;
- ctx->comp_magic_cnt = 0;
- ctx->comp_has_frame = false;
- spin_unlock(ctx->lock);
- if (ctx->is_stateless && src_req)
- v4l2_ctrl_request_complete(src_req, &ctx->hdl);
-
- if (ctx->is_enc)
- v4l2_m2m_job_finish(dev->stateful_enc.m2m_dev, ctx->fh.m2m_ctx);
- else if (ctx->is_stateless)
- v4l2_m2m_job_finish(dev->stateless_dec.m2m_dev,
- ctx->fh.m2m_ctx);
- else
- v4l2_m2m_job_finish(dev->stateful_dec.m2m_dev, ctx->fh.m2m_ctx);
-}
-
-static void job_remove_src_buf(struct vicodec_ctx *ctx, u32 state)
-{
- struct vb2_v4l2_buffer *src_buf;
- struct vicodec_q_data *q_src;
-
- q_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
- spin_lock(ctx->lock);
- src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
- src_buf->sequence = q_src->sequence++;
- v4l2_m2m_buf_done(src_buf, state);
- ctx->cur_buf_offset = 0;
- spin_unlock(ctx->lock);
-}
-
-static const struct v4l2_fwht_pixfmt_info *
-info_from_header(const struct fwht_cframe_hdr *p_hdr)
-{
- unsigned int flags = ntohl(p_hdr->flags);
- unsigned int width_div = (flags & FWHT_FL_CHROMA_FULL_WIDTH) ? 1 : 2;
- unsigned int height_div = (flags & FWHT_FL_CHROMA_FULL_HEIGHT) ? 1 : 2;
- unsigned int components_num = 3;
- unsigned int pixenc = 0;
- unsigned int version = ntohl(p_hdr->version);
-
- if (version >= 2) {
- components_num = 1 + ((flags & FWHT_FL_COMPONENTS_NUM_MSK) >>
- FWHT_FL_COMPONENTS_NUM_OFFSET);
- pixenc = (flags & FWHT_FL_PIXENC_MSK);
- }
- return v4l2_fwht_find_nth_fmt(width_div, height_div,
- components_num, pixenc, 0);
-}
-
-static bool is_header_valid(const struct fwht_cframe_hdr *p_hdr)
-{
- const struct v4l2_fwht_pixfmt_info *info;
- unsigned int w = ntohl(p_hdr->width);
- unsigned int h = ntohl(p_hdr->height);
- unsigned int version = ntohl(p_hdr->version);
- unsigned int flags = ntohl(p_hdr->flags);
-
- if (w < MIN_WIDTH || w > MAX_WIDTH || h < MIN_HEIGHT || h > MAX_HEIGHT)
- return false;
-
- if (!validate_by_version(flags, version))
- return false;
-
- info = info_from_header(p_hdr);
- if (!info)
- return false;
- return true;
-}
-
-static void update_capture_data_from_header(struct vicodec_ctx *ctx)
-{
- struct vicodec_q_data *q_dst = get_q_data(ctx,
- V4L2_BUF_TYPE_VIDEO_CAPTURE);
- const struct fwht_cframe_hdr *p_hdr = &ctx->state.header;
- const struct v4l2_fwht_pixfmt_info *info = info_from_header(p_hdr);
- unsigned int flags = ntohl(p_hdr->flags);
- unsigned int hdr_width_div = (flags & FWHT_FL_CHROMA_FULL_WIDTH) ? 1 : 2;
- unsigned int hdr_height_div = (flags & FWHT_FL_CHROMA_FULL_HEIGHT) ? 1 : 2;
-
- /*
- * This function should not be used by a stateless codec since
- * it changes values in q_data that are not request specific
- */
- WARN_ON(ctx->is_stateless);
-
- q_dst->info = info;
- q_dst->visible_width = ntohl(p_hdr->width);
- q_dst->visible_height = ntohl(p_hdr->height);
- q_dst->coded_width = vic_round_dim(q_dst->visible_width, hdr_width_div);
- q_dst->coded_height = vic_round_dim(q_dst->visible_height,
- hdr_height_div);
-
- q_dst->sizeimage = q_dst->coded_width * q_dst->coded_height *
- q_dst->info->sizeimage_mult / q_dst->info->sizeimage_div;
- ctx->state.colorspace = ntohl(p_hdr->colorspace);
-
- ctx->state.xfer_func = ntohl(p_hdr->xfer_func);
- ctx->state.ycbcr_enc = ntohl(p_hdr->ycbcr_enc);
- ctx->state.quantization = ntohl(p_hdr->quantization);
-}
-
-static void set_last_buffer(struct vb2_v4l2_buffer *dst_buf,
- const struct vb2_v4l2_buffer *src_buf,
- struct vicodec_ctx *ctx)
-{
- struct vicodec_q_data *q_dst = get_q_data(ctx,
- V4L2_BUF_TYPE_VIDEO_CAPTURE);
-
- vb2_set_plane_payload(&dst_buf->vb2_buf, 0, 0);
- dst_buf->sequence = q_dst->sequence++;
-
- v4l2_m2m_buf_copy_metadata(src_buf, dst_buf, !ctx->is_enc);
- dst_buf->flags |= V4L2_BUF_FLAG_LAST;
- v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_DONE);
-}
-
-static int job_ready(void *priv)
-{
- static const u8 magic[] = {
- 0x4f, 0x4f, 0x4f, 0x4f, 0xff, 0xff, 0xff, 0xff
- };
- struct vicodec_ctx *ctx = priv;
- struct vb2_v4l2_buffer *src_buf;
- u8 *p_src;
- u8 *p;
- u32 sz;
- u32 state;
- struct vicodec_q_data *q_dst = get_q_data(ctx,
- V4L2_BUF_TYPE_VIDEO_CAPTURE);
- unsigned int flags;
- unsigned int hdr_width_div;
- unsigned int hdr_height_div;
- unsigned int max_to_copy;
- unsigned int comp_frame_size;
-
- if (ctx->has_stopped)
- return 0;
- if (ctx->source_changed)
- return 0;
- if (ctx->is_stateless || ctx->is_enc || ctx->comp_has_frame)
- return 1;
-
-restart:
- ctx->comp_has_next_frame = false;
- src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
- if (!src_buf)
- return 0;
- p_src = vb2_plane_vaddr(&src_buf->vb2_buf, 0);
- sz = vb2_get_plane_payload(&src_buf->vb2_buf, 0);
- p = p_src + ctx->cur_buf_offset;
-
- state = VB2_BUF_STATE_DONE;
-
- if (ctx->header_size < sizeof(struct fwht_cframe_hdr)) {
- state = get_next_header(ctx, &p, p_src + sz - p);
- if (ctx->header_size < sizeof(struct fwht_cframe_hdr)) {
- if (ctx->is_draining && src_buf == ctx->last_src_buf)
- return 1;
- job_remove_src_buf(ctx, state);
- goto restart;
- }
- }
-
- comp_frame_size = ntohl(ctx->state.header.size);
-
- /*
- * The current scanned frame might be the first frame of a new
- * resolution so its size might be larger than ctx->comp_max_size.
- * In that case it is copied up to the current buffer capacity and
- * the copy will continue after allocating new large enough buffer
- * when restreaming
- */
- max_to_copy = min(comp_frame_size, ctx->comp_max_size);
-
- if (ctx->comp_size < max_to_copy) {
- u32 copy = max_to_copy - ctx->comp_size;
-
- if (copy > p_src + sz - p)
- copy = p_src + sz - p;
-
- memcpy(ctx->state.compressed_frame + ctx->comp_size,
- p, copy);
- p += copy;
- ctx->comp_size += copy;
- if (ctx->comp_size < max_to_copy) {
- if (ctx->is_draining && src_buf == ctx->last_src_buf)
- return 1;
- job_remove_src_buf(ctx, state);
- goto restart;
- }
- }
- ctx->cur_buf_offset = p - p_src;
- if (ctx->comp_size == comp_frame_size)
- ctx->comp_has_frame = true;
- ctx->comp_has_next_frame = false;
- if (ctx->comp_has_frame && sz - ctx->cur_buf_offset >=
- sizeof(struct fwht_cframe_hdr)) {
- struct fwht_cframe_hdr *p_hdr = (struct fwht_cframe_hdr *)p;
- u32 frame_size = ntohl(p_hdr->size);
- u32 remaining = sz - ctx->cur_buf_offset - sizeof(*p_hdr);
-
- if (!memcmp(p, magic, sizeof(magic)))
- ctx->comp_has_next_frame = remaining >= frame_size;
- }
- /*
- * if the header is invalid the device_run will just drop the frame
- * with an error
- */
- if (!is_header_valid(&ctx->state.header) && ctx->comp_has_frame)
- return 1;
- flags = ntohl(ctx->state.header.flags);
- hdr_width_div = (flags & FWHT_FL_CHROMA_FULL_WIDTH) ? 1 : 2;
- hdr_height_div = (flags & FWHT_FL_CHROMA_FULL_HEIGHT) ? 1 : 2;
-
- if (ntohl(ctx->state.header.width) != q_dst->visible_width ||
- ntohl(ctx->state.header.height) != q_dst->visible_height ||
- !q_dst->info ||
- hdr_width_div != q_dst->info->width_div ||
- hdr_height_div != q_dst->info->height_div) {
- static const struct v4l2_event rs_event = {
- .type = V4L2_EVENT_SOURCE_CHANGE,
- .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION,
- };
-
- struct vb2_v4l2_buffer *dst_buf =
- v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
-
- update_capture_data_from_header(ctx);
- v4l2_event_queue_fh(&ctx->fh, &rs_event);
- set_last_buffer(dst_buf, src_buf, ctx);
- ctx->source_changed = true;
- return 0;
- }
- return 1;
-}
-
-/*
- * video ioctls
- */
-
-static const struct v4l2_fwht_pixfmt_info *find_fmt(u32 fmt)
-{
- const struct v4l2_fwht_pixfmt_info *info =
- v4l2_fwht_find_pixfmt(fmt);
-
- if (!info)
- info = v4l2_fwht_get_pixfmt(0);
- return info;
-}
-
-static int vidioc_querycap(struct file *file, void *priv,
- struct v4l2_capability *cap)
-{
- strscpy(cap->driver, VICODEC_NAME, sizeof(cap->driver));
- strscpy(cap->card, VICODEC_NAME, sizeof(cap->card));
- snprintf(cap->bus_info, sizeof(cap->bus_info),
- "platform:%s", VICODEC_NAME);
- return 0;
-}
-
-static int enum_fmt(struct v4l2_fmtdesc *f, struct vicodec_ctx *ctx,
- bool is_out)
-{
- bool is_uncomp = (ctx->is_enc && is_out) || (!ctx->is_enc && !is_out);
-
- if (V4L2_TYPE_IS_MULTIPLANAR(f->type) && !multiplanar)
- return -EINVAL;
- if (!V4L2_TYPE_IS_MULTIPLANAR(f->type) && multiplanar)
- return -EINVAL;
-
- if (is_uncomp) {
- const struct v4l2_fwht_pixfmt_info *info =
- get_q_data(ctx, f->type)->info;
-
- if (ctx->is_enc ||
- !vb2_is_streaming(&ctx->fh.m2m_ctx->cap_q_ctx.q))
- info = v4l2_fwht_get_pixfmt(f->index);
- else
- info = v4l2_fwht_find_nth_fmt(info->width_div,
- info->height_div,
- info->components_num,
- info->pixenc,
- f->index);
- if (!info)
- return -EINVAL;
- f->pixelformat = info->id;
- } else {
- if (f->index)
- return -EINVAL;
- f->pixelformat = ctx->is_stateless ?
- V4L2_PIX_FMT_FWHT_STATELESS : V4L2_PIX_FMT_FWHT;
- if (!ctx->is_enc && !ctx->is_stateless)
- f->flags = V4L2_FMT_FLAG_DYN_RESOLUTION |
- V4L2_FMT_FLAG_CONTINUOUS_BYTESTREAM;
- }
- return 0;
-}
-
-static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_fmtdesc *f)
-{
- struct vicodec_ctx *ctx = file2ctx(file);
-
- return enum_fmt(f, ctx, false);
-}
-
-static int vidioc_enum_fmt_vid_out(struct file *file, void *priv,
- struct v4l2_fmtdesc *f)
-{
- struct vicodec_ctx *ctx = file2ctx(file);
-
- return enum_fmt(f, ctx, true);
-}
-
-static int vidioc_g_fmt(struct vicodec_ctx *ctx, struct v4l2_format *f)
-{
- struct vb2_queue *vq;
- struct vicodec_q_data *q_data;
- struct v4l2_pix_format_mplane *pix_mp;
- struct v4l2_pix_format *pix;
- const struct v4l2_fwht_pixfmt_info *info;
-
- vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
- if (!vq)
- return -EINVAL;
-
- q_data = get_q_data(ctx, f->type);
- info = q_data->info;
-
- switch (f->type) {
- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
- case V4L2_BUF_TYPE_VIDEO_OUTPUT:
- if (multiplanar)
- return -EINVAL;
- pix = &f->fmt.pix;
- pix->width = q_data->coded_width;
- pix->height = q_data->coded_height;
- pix->field = V4L2_FIELD_NONE;
- pix->pixelformat = info->id;
- pix->bytesperline = q_data->coded_width *
- info->bytesperline_mult;
- pix->sizeimage = q_data->sizeimage;
- pix->colorspace = ctx->state.colorspace;
- pix->xfer_func = ctx->state.xfer_func;
- pix->ycbcr_enc = ctx->state.ycbcr_enc;
- pix->quantization = ctx->state.quantization;
- break;
-
- case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
- case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
- if (!multiplanar)
- return -EINVAL;
- pix_mp = &f->fmt.pix_mp;
- pix_mp->width = q_data->coded_width;
- pix_mp->height = q_data->coded_height;
- pix_mp->field = V4L2_FIELD_NONE;
- pix_mp->pixelformat = info->id;
- pix_mp->num_planes = 1;
- pix_mp->plane_fmt[0].bytesperline =
- q_data->coded_width * info->bytesperline_mult;
- pix_mp->plane_fmt[0].sizeimage = q_data->sizeimage;
- pix_mp->colorspace = ctx->state.colorspace;
- pix_mp->xfer_func = ctx->state.xfer_func;
- pix_mp->ycbcr_enc = ctx->state.ycbcr_enc;
- pix_mp->quantization = ctx->state.quantization;
- memset(pix_mp->reserved, 0, sizeof(pix_mp->reserved));
- memset(pix_mp->plane_fmt[0].reserved, 0,
- sizeof(pix_mp->plane_fmt[0].reserved));
- break;
- default:
- return -EINVAL;
- }
- return 0;
-}
-
-static int vidioc_g_fmt_vid_out(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- return vidioc_g_fmt(file2ctx(file), f);
-}
-
-static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- return vidioc_g_fmt(file2ctx(file), f);
-}
-
-static int vidioc_try_fmt(struct vicodec_ctx *ctx, struct v4l2_format *f)
-{
- struct v4l2_pix_format_mplane *pix_mp;
- struct v4l2_pix_format *pix;
- struct v4l2_plane_pix_format *plane;
- const struct v4l2_fwht_pixfmt_info *info = ctx->is_stateless ?
- &pixfmt_stateless_fwht : &pixfmt_fwht;
-
- switch (f->type) {
- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
- case V4L2_BUF_TYPE_VIDEO_OUTPUT:
- pix = &f->fmt.pix;
- if (pix->pixelformat != V4L2_PIX_FMT_FWHT &&
- pix->pixelformat != V4L2_PIX_FMT_FWHT_STATELESS)
- info = find_fmt(pix->pixelformat);
-
- pix->width = clamp(pix->width, MIN_WIDTH, MAX_WIDTH);
- pix->width = vic_round_dim(pix->width, info->width_div);
-
- pix->height = clamp(pix->height, MIN_HEIGHT, MAX_HEIGHT);
- pix->height = vic_round_dim(pix->height, info->height_div);
-
- pix->field = V4L2_FIELD_NONE;
- pix->bytesperline =
- pix->width * info->bytesperline_mult;
- pix->sizeimage = pix->width * pix->height *
- info->sizeimage_mult / info->sizeimage_div;
- if (pix->pixelformat == V4L2_PIX_FMT_FWHT)
- pix->sizeimage += sizeof(struct fwht_cframe_hdr);
- break;
- case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
- case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
- pix_mp = &f->fmt.pix_mp;
- plane = pix_mp->plane_fmt;
- if (pix_mp->pixelformat != V4L2_PIX_FMT_FWHT &&
- pix_mp->pixelformat != V4L2_PIX_FMT_FWHT_STATELESS)
- info = find_fmt(pix_mp->pixelformat);
- pix_mp->num_planes = 1;
-
- pix_mp->width = clamp(pix_mp->width, MIN_WIDTH, MAX_WIDTH);
- pix_mp->width = vic_round_dim(pix_mp->width, info->width_div);
-
- pix_mp->height = clamp(pix_mp->height, MIN_HEIGHT, MAX_HEIGHT);
- pix_mp->height = vic_round_dim(pix_mp->height,
- info->height_div);
-
- pix_mp->field = V4L2_FIELD_NONE;
- plane->bytesperline =
- pix_mp->width * info->bytesperline_mult;
- plane->sizeimage = pix_mp->width * pix_mp->height *
- info->sizeimage_mult / info->sizeimage_div;
- if (pix_mp->pixelformat == V4L2_PIX_FMT_FWHT)
- plane->sizeimage += sizeof(struct fwht_cframe_hdr);
- memset(pix_mp->reserved, 0, sizeof(pix_mp->reserved));
- memset(plane->reserved, 0, sizeof(plane->reserved));
- break;
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct vicodec_ctx *ctx = file2ctx(file);
- struct v4l2_pix_format_mplane *pix_mp;
- struct v4l2_pix_format *pix;
-
- switch (f->type) {
- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
- if (multiplanar)
- return -EINVAL;
- pix = &f->fmt.pix;
- pix->pixelformat = ctx->is_enc ? V4L2_PIX_FMT_FWHT :
- find_fmt(f->fmt.pix.pixelformat)->id;
- pix->colorspace = ctx->state.colorspace;
- pix->xfer_func = ctx->state.xfer_func;
- pix->ycbcr_enc = ctx->state.ycbcr_enc;
- pix->quantization = ctx->state.quantization;
- break;
- case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
- if (!multiplanar)
- return -EINVAL;
- pix_mp = &f->fmt.pix_mp;
- pix_mp->pixelformat = ctx->is_enc ? V4L2_PIX_FMT_FWHT :
- find_fmt(pix_mp->pixelformat)->id;
- pix_mp->colorspace = ctx->state.colorspace;
- pix_mp->xfer_func = ctx->state.xfer_func;
- pix_mp->ycbcr_enc = ctx->state.ycbcr_enc;
- pix_mp->quantization = ctx->state.quantization;
- break;
- default:
- return -EINVAL;
- }
-
- return vidioc_try_fmt(ctx, f);
-}
-
-static int vidioc_try_fmt_vid_out(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct vicodec_ctx *ctx = file2ctx(file);
- struct v4l2_pix_format_mplane *pix_mp;
- struct v4l2_pix_format *pix;
-
- switch (f->type) {
- case V4L2_BUF_TYPE_VIDEO_OUTPUT:
- if (multiplanar)
- return -EINVAL;
- pix = &f->fmt.pix;
- if (ctx->is_enc)
- pix->pixelformat = find_fmt(pix->pixelformat)->id;
- else if (ctx->is_stateless)
- pix->pixelformat = V4L2_PIX_FMT_FWHT_STATELESS;
- else
- pix->pixelformat = V4L2_PIX_FMT_FWHT;
- if (!pix->colorspace)
- pix->colorspace = V4L2_COLORSPACE_REC709;
- break;
- case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
- if (!multiplanar)
- return -EINVAL;
- pix_mp = &f->fmt.pix_mp;
- if (ctx->is_enc)
- pix_mp->pixelformat = find_fmt(pix_mp->pixelformat)->id;
- else if (ctx->is_stateless)
- pix_mp->pixelformat = V4L2_PIX_FMT_FWHT_STATELESS;
- else
- pix_mp->pixelformat = V4L2_PIX_FMT_FWHT;
- if (!pix_mp->colorspace)
- pix_mp->colorspace = V4L2_COLORSPACE_REC709;
- break;
- default:
- return -EINVAL;
- }
-
- return vidioc_try_fmt(ctx, f);
-}
-
-static int vidioc_s_fmt(struct vicodec_ctx *ctx, struct v4l2_format *f)
-{
- struct vicodec_q_data *q_data;
- struct vb2_queue *vq;
- bool fmt_changed = true;
- struct v4l2_pix_format_mplane *pix_mp;
- struct v4l2_pix_format *pix;
-
- vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
- if (!vq)
- return -EINVAL;
-
- q_data = get_q_data(ctx, f->type);
- if (!q_data)
- return -EINVAL;
-
- switch (f->type) {
- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
- case V4L2_BUF_TYPE_VIDEO_OUTPUT:
- pix = &f->fmt.pix;
- if (ctx->is_enc && V4L2_TYPE_IS_OUTPUT(f->type))
- fmt_changed =
- !q_data->info ||
- q_data->info->id != pix->pixelformat ||
- q_data->coded_width != pix->width ||
- q_data->coded_height != pix->height;
-
- if (vb2_is_busy(vq) && fmt_changed)
- return -EBUSY;
-
- if (pix->pixelformat == V4L2_PIX_FMT_FWHT)
- q_data->info = &pixfmt_fwht;
- else if (pix->pixelformat == V4L2_PIX_FMT_FWHT_STATELESS)
- q_data->info = &pixfmt_stateless_fwht;
- else
- q_data->info = find_fmt(pix->pixelformat);
- q_data->coded_width = pix->width;
- q_data->coded_height = pix->height;
- q_data->sizeimage = pix->sizeimage;
- break;
- case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
- case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
- pix_mp = &f->fmt.pix_mp;
- if (ctx->is_enc && V4L2_TYPE_IS_OUTPUT(f->type))
- fmt_changed =
- !q_data->info ||
- q_data->info->id != pix_mp->pixelformat ||
- q_data->coded_width != pix_mp->width ||
- q_data->coded_height != pix_mp->height;
-
- if (vb2_is_busy(vq) && fmt_changed)
- return -EBUSY;
-
- if (pix_mp->pixelformat == V4L2_PIX_FMT_FWHT)
- q_data->info = &pixfmt_fwht;
- else if (pix_mp->pixelformat == V4L2_PIX_FMT_FWHT_STATELESS)
- q_data->info = &pixfmt_stateless_fwht;
- else
- q_data->info = find_fmt(pix_mp->pixelformat);
- q_data->coded_width = pix_mp->width;
- q_data->coded_height = pix_mp->height;
- q_data->sizeimage = pix_mp->plane_fmt[0].sizeimage;
- break;
- default:
- return -EINVAL;
- }
-
- dprintk(ctx->dev,
- "Setting format for type %d, coded wxh: %dx%d, fourcc: 0x%08x\n",
- f->type, q_data->coded_width, q_data->coded_height,
- q_data->info->id);
-
- return 0;
-}
-
-static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- int ret;
-
- ret = vidioc_try_fmt_vid_cap(file, priv, f);
- if (ret)
- return ret;
-
- return vidioc_s_fmt(file2ctx(file), f);
-}
-
-static int vidioc_s_fmt_vid_out(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct vicodec_ctx *ctx = file2ctx(file);
- struct vicodec_q_data *q_data;
- struct vicodec_q_data *q_data_cap;
- struct v4l2_pix_format *pix;
- struct v4l2_pix_format_mplane *pix_mp;
- u32 coded_w = 0, coded_h = 0;
- unsigned int size = 0;
- int ret;
-
- q_data = get_q_data(ctx, f->type);
- q_data_cap = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
-
- ret = vidioc_try_fmt_vid_out(file, priv, f);
- if (ret)
- return ret;
-
- if (ctx->is_enc) {
- struct vb2_queue *vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
- struct vb2_queue *vq_cap = v4l2_m2m_get_vq(ctx->fh.m2m_ctx,
- V4L2_BUF_TYPE_VIDEO_CAPTURE);
- const struct v4l2_fwht_pixfmt_info *info = ctx->is_stateless ?
- &pixfmt_stateless_fwht : &pixfmt_fwht;
-
- if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
- coded_w = f->fmt.pix.width;
- coded_h = f->fmt.pix.height;
- } else {
- coded_w = f->fmt.pix_mp.width;
- coded_h = f->fmt.pix_mp.height;
- }
- if (vb2_is_busy(vq) && (coded_w != q_data->coded_width ||
- coded_h != q_data->coded_height))
- return -EBUSY;
- size = coded_w * coded_h *
- info->sizeimage_mult / info->sizeimage_div;
- if (!ctx->is_stateless)
- size += sizeof(struct fwht_cframe_hdr);
-
- if (vb2_is_busy(vq_cap) && size > q_data_cap->sizeimage)
- return -EBUSY;
- }
-
- ret = vidioc_s_fmt(file2ctx(file), f);
- if (!ret) {
- if (ctx->is_enc) {
- q_data->visible_width = coded_w;
- q_data->visible_height = coded_h;
- q_data_cap->coded_width = coded_w;
- q_data_cap->coded_height = coded_h;
- q_data_cap->sizeimage = size;
- }
-
- switch (f->type) {
- case V4L2_BUF_TYPE_VIDEO_OUTPUT:
- pix = &f->fmt.pix;
- ctx->state.colorspace = pix->colorspace;
- ctx->state.xfer_func = pix->xfer_func;
- ctx->state.ycbcr_enc = pix->ycbcr_enc;
- ctx->state.quantization = pix->quantization;
- break;
- case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
- pix_mp = &f->fmt.pix_mp;
- ctx->state.colorspace = pix_mp->colorspace;
- ctx->state.xfer_func = pix_mp->xfer_func;
- ctx->state.ycbcr_enc = pix_mp->ycbcr_enc;
- ctx->state.quantization = pix_mp->quantization;
- break;
- default:
- break;
- }
- }
- return ret;
-}
-
-static int vidioc_g_selection(struct file *file, void *priv,
- struct v4l2_selection *s)
-{
- struct vicodec_ctx *ctx = file2ctx(file);
- struct vicodec_q_data *q_data;
-
- q_data = get_q_data(ctx, s->type);
- if (!q_data)
- return -EINVAL;
- /*
- * encoder supports only cropping on the OUTPUT buffer
- * decoder supports only composing on the CAPTURE buffer
- */
- if (ctx->is_enc && s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
- switch (s->target) {
- case V4L2_SEL_TGT_CROP:
- s->r.left = 0;
- s->r.top = 0;
- s->r.width = q_data->visible_width;
- s->r.height = q_data->visible_height;
- return 0;
- case V4L2_SEL_TGT_CROP_DEFAULT:
- case V4L2_SEL_TGT_CROP_BOUNDS:
- s->r.left = 0;
- s->r.top = 0;
- s->r.width = q_data->coded_width;
- s->r.height = q_data->coded_height;
- return 0;
- }
- } else if (!ctx->is_enc && s->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
- switch (s->target) {
- case V4L2_SEL_TGT_COMPOSE:
- s->r.left = 0;
- s->r.top = 0;
- s->r.width = q_data->visible_width;
- s->r.height = q_data->visible_height;
- return 0;
- case V4L2_SEL_TGT_COMPOSE_DEFAULT:
- case V4L2_SEL_TGT_COMPOSE_BOUNDS:
- s->r.left = 0;
- s->r.top = 0;
- s->r.width = q_data->coded_width;
- s->r.height = q_data->coded_height;
- return 0;
- }
- }
- return -EINVAL;
-}
-
-static int vidioc_s_selection(struct file *file, void *priv,
- struct v4l2_selection *s)
-{
- struct vicodec_ctx *ctx = file2ctx(file);
- struct vicodec_q_data *q_data;
-
- if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
- return -EINVAL;
-
- q_data = get_q_data(ctx, s->type);
- if (!q_data)
- return -EINVAL;
-
- if (!ctx->is_enc || s->target != V4L2_SEL_TGT_CROP)
- return -EINVAL;
-
- s->r.left = 0;
- s->r.top = 0;
- q_data->visible_width = clamp(s->r.width, MIN_WIDTH,
- q_data->coded_width);
- s->r.width = q_data->visible_width;
- q_data->visible_height = clamp(s->r.height, MIN_HEIGHT,
- q_data->coded_height);
- s->r.height = q_data->visible_height;
- return 0;
-}
-
-static int vicodec_mark_last_buf(struct vicodec_ctx *ctx)
-{
- struct vb2_v4l2_buffer *next_dst_buf;
- int ret = 0;
-
- spin_lock(ctx->lock);
- if (ctx->is_draining) {
- ret = -EBUSY;
- goto unlock;
- }
- if (ctx->has_stopped)
- goto unlock;
-
- ctx->last_src_buf = v4l2_m2m_last_src_buf(ctx->fh.m2m_ctx);
- ctx->is_draining = true;
- if (ctx->last_src_buf)
- goto unlock;
-
- next_dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
- if (!next_dst_buf) {
- ctx->next_is_last = true;
- goto unlock;
- }
-
- next_dst_buf->flags |= V4L2_BUF_FLAG_LAST;
- vb2_buffer_done(&next_dst_buf->vb2_buf, VB2_BUF_STATE_DONE);
- ctx->is_draining = false;
- ctx->has_stopped = true;
- v4l2_event_queue_fh(&ctx->fh, &vicodec_eos_event);
-
-unlock:
- spin_unlock(ctx->lock);
- return ret;
-}
-
-static int vicodec_encoder_cmd(struct file *file, void *fh,
- struct v4l2_encoder_cmd *ec)
-{
- struct vicodec_ctx *ctx = file2ctx(file);
- int ret;
-
- ret = v4l2_m2m_ioctl_try_encoder_cmd(file, fh, ec);
- if (ret < 0)
- return ret;
-
- if (!vb2_is_streaming(&ctx->fh.m2m_ctx->cap_q_ctx.q) ||
- !vb2_is_streaming(&ctx->fh.m2m_ctx->out_q_ctx.q))
- return 0;
-
- if (ec->cmd == V4L2_ENC_CMD_STOP)
- return vicodec_mark_last_buf(ctx);
- ret = 0;
- spin_lock(ctx->lock);
- if (ctx->is_draining) {
- ret = -EBUSY;
- } else if (ctx->has_stopped) {
- ctx->has_stopped = false;
- vb2_clear_last_buffer_dequeued(&ctx->fh.m2m_ctx->cap_q_ctx.q);
- }
- spin_unlock(ctx->lock);
- return ret;
-}
-
-static int vicodec_decoder_cmd(struct file *file, void *fh,
- struct v4l2_decoder_cmd *dc)
-{
- struct vicodec_ctx *ctx = file2ctx(file);
- int ret;
-
- ret = v4l2_m2m_ioctl_try_decoder_cmd(file, fh, dc);
- if (ret < 0)
- return ret;
-
- if (!vb2_is_streaming(&ctx->fh.m2m_ctx->cap_q_ctx.q) ||
- !vb2_is_streaming(&ctx->fh.m2m_ctx->out_q_ctx.q))
- return 0;
-
- if (dc->cmd == V4L2_DEC_CMD_STOP)
- return vicodec_mark_last_buf(ctx);
- ret = 0;
- spin_lock(ctx->lock);
- if (ctx->is_draining) {
- ret = -EBUSY;
- } else if (ctx->has_stopped) {
- ctx->has_stopped = false;
- vb2_clear_last_buffer_dequeued(&ctx->fh.m2m_ctx->cap_q_ctx.q);
- }
- spin_unlock(ctx->lock);
- return ret;
-}
-
-static int vicodec_enum_framesizes(struct file *file, void *fh,
- struct v4l2_frmsizeenum *fsize)
-{
- switch (fsize->pixel_format) {
- case V4L2_PIX_FMT_FWHT_STATELESS:
- break;
- case V4L2_PIX_FMT_FWHT:
- break;
- default:
- if (find_fmt(fsize->pixel_format)->id == fsize->pixel_format)
- break;
- return -EINVAL;
- }
-
- if (fsize->index)
- return -EINVAL;
-
- fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
-
- fsize->stepwise.min_width = MIN_WIDTH;
- fsize->stepwise.max_width = MAX_WIDTH;
- fsize->stepwise.step_width = 8;
- fsize->stepwise.min_height = MIN_HEIGHT;
- fsize->stepwise.max_height = MAX_HEIGHT;
- fsize->stepwise.step_height = 8;
-
- return 0;
-}
-
-static int vicodec_subscribe_event(struct v4l2_fh *fh,
- const struct v4l2_event_subscription *sub)
-{
- struct vicodec_ctx *ctx = container_of(fh, struct vicodec_ctx, fh);
-
- switch (sub->type) {
- case V4L2_EVENT_SOURCE_CHANGE:
- if (ctx->is_enc)
- return -EINVAL;
- /* fall through */
- case V4L2_EVENT_EOS:
- if (ctx->is_stateless)
- return -EINVAL;
- return v4l2_event_subscribe(fh, sub, 0, NULL);
- default:
- return v4l2_ctrl_subscribe_event(fh, sub);
- }
-}
-
-static const struct v4l2_ioctl_ops vicodec_ioctl_ops = {
- .vidioc_querycap = vidioc_querycap,
-
- .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
- .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
- .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
- .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
-
- .vidioc_g_fmt_vid_cap_mplane = vidioc_g_fmt_vid_cap,
- .vidioc_try_fmt_vid_cap_mplane = vidioc_try_fmt_vid_cap,
- .vidioc_s_fmt_vid_cap_mplane = vidioc_s_fmt_vid_cap,
-
- .vidioc_enum_fmt_vid_out = vidioc_enum_fmt_vid_out,
- .vidioc_g_fmt_vid_out = vidioc_g_fmt_vid_out,
- .vidioc_try_fmt_vid_out = vidioc_try_fmt_vid_out,
- .vidioc_s_fmt_vid_out = vidioc_s_fmt_vid_out,
-
- .vidioc_g_fmt_vid_out_mplane = vidioc_g_fmt_vid_out,
- .vidioc_try_fmt_vid_out_mplane = vidioc_try_fmt_vid_out,
- .vidioc_s_fmt_vid_out_mplane = vidioc_s_fmt_vid_out,
-
- .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs,
- .vidioc_querybuf = v4l2_m2m_ioctl_querybuf,
- .vidioc_qbuf = v4l2_m2m_ioctl_qbuf,
- .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf,
- .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf,
- .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs,
- .vidioc_expbuf = v4l2_m2m_ioctl_expbuf,
-
- .vidioc_streamon = v4l2_m2m_ioctl_streamon,
- .vidioc_streamoff = v4l2_m2m_ioctl_streamoff,
-
- .vidioc_g_selection = vidioc_g_selection,
- .vidioc_s_selection = vidioc_s_selection,
-
- .vidioc_try_encoder_cmd = v4l2_m2m_ioctl_try_encoder_cmd,
- .vidioc_encoder_cmd = vicodec_encoder_cmd,
- .vidioc_try_decoder_cmd = v4l2_m2m_ioctl_try_decoder_cmd,
- .vidioc_decoder_cmd = vicodec_decoder_cmd,
- .vidioc_enum_framesizes = vicodec_enum_framesizes,
-
- .vidioc_subscribe_event = vicodec_subscribe_event,
- .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
-};
-
-
-/*
- * Queue operations
- */
-
-static int vicodec_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers,
- unsigned int *nplanes, unsigned int sizes[],
- struct device *alloc_devs[])
-{
- struct vicodec_ctx *ctx = vb2_get_drv_priv(vq);
- struct vicodec_q_data *q_data = get_q_data(ctx, vq->type);
- unsigned int size = q_data->sizeimage;
-
- if (*nplanes)
- return sizes[0] < size ? -EINVAL : 0;
-
- *nplanes = 1;
- sizes[0] = size;
- q_data->vb2_sizeimage = size;
- return 0;
-}
-
-static int vicodec_buf_out_validate(struct vb2_buffer *vb)
-{
- struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
-
- vbuf->field = V4L2_FIELD_NONE;
- return 0;
-}
-
-static int vicodec_buf_prepare(struct vb2_buffer *vb)
-{
- struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
- struct vicodec_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
- struct vicodec_q_data *q_data;
-
- dprintk(ctx->dev, "type: %d\n", vb->vb2_queue->type);
-
- q_data = get_q_data(ctx, vb->vb2_queue->type);
- if (V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) {
- if (vbuf->field == V4L2_FIELD_ANY)
- vbuf->field = V4L2_FIELD_NONE;
- if (vbuf->field != V4L2_FIELD_NONE) {
- dprintk(ctx->dev, "%s field isn't supported\n",
- __func__);
- return -EINVAL;
- }
- }
-
- if (vb2_plane_size(vb, 0) < q_data->vb2_sizeimage) {
- dprintk(ctx->dev,
- "%s data will not fit into plane (%lu < %lu)\n",
- __func__, vb2_plane_size(vb, 0),
- (long)q_data->vb2_sizeimage);
- return -EINVAL;
- }
-
- return 0;
-}
-
-static void vicodec_buf_queue(struct vb2_buffer *vb)
-{
- struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
- struct vicodec_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
- unsigned int sz = vb2_get_plane_payload(&vbuf->vb2_buf, 0);
- u8 *p_src = vb2_plane_vaddr(&vbuf->vb2_buf, 0);
- u8 *p = p_src;
- struct vb2_queue *vq_out = v4l2_m2m_get_vq(ctx->fh.m2m_ctx,
- V4L2_BUF_TYPE_VIDEO_OUTPUT);
- struct vb2_queue *vq_cap = v4l2_m2m_get_vq(ctx->fh.m2m_ctx,
- V4L2_BUF_TYPE_VIDEO_CAPTURE);
- bool header_valid = false;
- static const struct v4l2_event rs_event = {
- .type = V4L2_EVENT_SOURCE_CHANGE,
- .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION,
- };
-
- if (vb2_is_streaming(vq_cap)) {
- if (!V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type) &&
- ctx->next_is_last) {
- unsigned int i;
-
- for (i = 0; i < vb->num_planes; i++)
- vb->planes[i].bytesused = 0;
- vbuf->flags = V4L2_BUF_FLAG_LAST;
- vbuf->field = V4L2_FIELD_NONE;
- vbuf->sequence = get_q_data(ctx, vb->vb2_queue->type)->sequence++;
- vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
- ctx->is_draining = false;
- ctx->has_stopped = true;
- ctx->next_is_last = false;
- v4l2_event_queue_fh(&ctx->fh, &vicodec_eos_event);
- return;
- }
- }
-
- /* buf_queue handles only the first source change event */
- if (ctx->first_source_change_sent) {
- v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf);
- return;
- }
-
- /*
- * if both queues are streaming, the source change event is
- * handled in job_ready
- */
- if (vb2_is_streaming(vq_cap) && vb2_is_streaming(vq_out)) {
- v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf);
- return;
- }
-
- /*
- * source change event is relevant only for the stateful decoder
- * in the compressed stream
- */
- if (ctx->is_stateless || ctx->is_enc ||
- !V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) {
- v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf);
- return;
- }
-
- do {
- enum vb2_buffer_state state =
- get_next_header(ctx, &p, p_src + sz - p);
-
- if (ctx->header_size < sizeof(struct fwht_cframe_hdr)) {
- v4l2_m2m_buf_done(vbuf, state);
- return;
- }
- header_valid = is_header_valid(&ctx->state.header);
- /*
- * p points right after the end of the header in the
- * buffer. If the header is invalid we set p to point
- * to the next byte after the start of the header
- */
- if (!header_valid) {
- p = p - sizeof(struct fwht_cframe_hdr) + 1;
- if (p < p_src)
- p = p_src;
- ctx->header_size = 0;
- ctx->comp_magic_cnt = 0;
- }
-
- } while (!header_valid);
-
- ctx->cur_buf_offset = p - p_src;
- update_capture_data_from_header(ctx);
- ctx->first_source_change_sent = true;
- v4l2_event_queue_fh(&ctx->fh, &rs_event);
- v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf);
-}
-
-static void vicodec_return_bufs(struct vb2_queue *q, u32 state)
-{
- struct vicodec_ctx *ctx = vb2_get_drv_priv(q);
- struct vb2_v4l2_buffer *vbuf;
-
- for (;;) {
- if (V4L2_TYPE_IS_OUTPUT(q->type))
- vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
- else
- vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
- if (vbuf == NULL)
- return;
- v4l2_ctrl_request_complete(vbuf->vb2_buf.req_obj.req,
- &ctx->hdl);
- spin_lock(ctx->lock);
- v4l2_m2m_buf_done(vbuf, state);
- spin_unlock(ctx->lock);
- }
-}
-
-static unsigned int total_frame_size(struct vicodec_q_data *q_data)
-{
- unsigned int size;
- unsigned int chroma_div;
-
- if (!q_data->info) {
- WARN_ON(1);
- return 0;
- }
- size = q_data->coded_width * q_data->coded_height;
- chroma_div = q_data->info->width_div * q_data->info->height_div;
-
- if (q_data->info->components_num == 4)
- return 2 * size + 2 * (size / chroma_div);
- else if (q_data->info->components_num == 3)
- return size + 2 * (size / chroma_div);
- return size;
-}
-
-static int vicodec_start_streaming(struct vb2_queue *q,
- unsigned int count)
-{
- struct vicodec_ctx *ctx = vb2_get_drv_priv(q);
- struct vicodec_q_data *q_data = get_q_data(ctx, q->type);
- struct v4l2_fwht_state *state = &ctx->state;
- const struct v4l2_fwht_pixfmt_info *info = q_data->info;
- unsigned int size = q_data->coded_width * q_data->coded_height;
- unsigned int chroma_div;
- unsigned int total_planes_size;
- u8 *new_comp_frame = NULL;
-
- chroma_div = info->width_div * info->height_div;
- q_data->sequence = 0;
-
- if (V4L2_TYPE_IS_OUTPUT(q->type))
- ctx->last_src_buf = NULL;
-
- state->gop_cnt = 0;
-
- if ((V4L2_TYPE_IS_OUTPUT(q->type) && !ctx->is_enc) ||
- (!V4L2_TYPE_IS_OUTPUT(q->type) && ctx->is_enc))
- return 0;
-
- if (info->id == V4L2_PIX_FMT_FWHT ||
- info->id == V4L2_PIX_FMT_FWHT_STATELESS) {
- vicodec_return_bufs(q, VB2_BUF_STATE_QUEUED);
- return -EINVAL;
- }
- total_planes_size = total_frame_size(q_data);
- ctx->comp_max_size = total_planes_size;
-
- state->visible_width = q_data->visible_width;
- state->visible_height = q_data->visible_height;
- state->coded_width = q_data->coded_width;
- state->coded_height = q_data->coded_height;
- state->stride = q_data->coded_width *
- info->bytesperline_mult;
-
- if (ctx->is_stateless) {
- state->ref_stride = state->stride;
- return 0;
- }
- state->ref_stride = q_data->coded_width * info->luma_alpha_step;
-
- state->ref_frame.buf = kvmalloc(total_planes_size, GFP_KERNEL);
- state->ref_frame.luma = state->ref_frame.buf;
- new_comp_frame = kvmalloc(ctx->comp_max_size, GFP_KERNEL);
-
- if (!state->ref_frame.luma || !new_comp_frame) {
- kvfree(state->ref_frame.luma);
- kvfree(new_comp_frame);
- vicodec_return_bufs(q, VB2_BUF_STATE_QUEUED);
- return -ENOMEM;
- }
- /*
- * if state->compressed_frame was already allocated then
- * it contain data of the first frame of the new resolution
- */
- if (state->compressed_frame) {
- if (ctx->comp_size > ctx->comp_max_size)
- ctx->comp_size = ctx->comp_max_size;
-
- memcpy(new_comp_frame,
- state->compressed_frame, ctx->comp_size);
- }
-
- kvfree(state->compressed_frame);
- state->compressed_frame = new_comp_frame;
-
- if (info->components_num < 3) {
- state->ref_frame.cb = NULL;
- state->ref_frame.cr = NULL;
- state->ref_frame.alpha = NULL;
- return 0;
- }
-
- state->ref_frame.cb = state->ref_frame.luma + size;
- state->ref_frame.cr = state->ref_frame.cb + size / chroma_div;
-
- if (info->components_num == 4)
- state->ref_frame.alpha =
- state->ref_frame.cr + size / chroma_div;
- else
- state->ref_frame.alpha = NULL;
-
- return 0;
-}
-
-static void vicodec_stop_streaming(struct vb2_queue *q)
-{
- struct vicodec_ctx *ctx = vb2_get_drv_priv(q);
-
- vicodec_return_bufs(q, VB2_BUF_STATE_ERROR);
-
- if (V4L2_TYPE_IS_OUTPUT(q->type)) {
- if (ctx->is_draining) {
- struct vb2_v4l2_buffer *next_dst_buf;
-
- spin_lock(ctx->lock);
- ctx->last_src_buf = NULL;
- next_dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
- if (!next_dst_buf) {
- ctx->next_is_last = true;
- } else {
- next_dst_buf->flags |= V4L2_BUF_FLAG_LAST;
- vb2_buffer_done(&next_dst_buf->vb2_buf, VB2_BUF_STATE_DONE);
- ctx->is_draining = false;
- ctx->has_stopped = true;
- v4l2_event_queue_fh(&ctx->fh, &vicodec_eos_event);
- }
- spin_unlock(ctx->lock);
- }
- } else {
- ctx->is_draining = false;
- ctx->has_stopped = false;
- ctx->next_is_last = false;
- }
- if (!ctx->is_enc && V4L2_TYPE_IS_OUTPUT(q->type))
- ctx->first_source_change_sent = false;
-
- if ((!V4L2_TYPE_IS_OUTPUT(q->type) && !ctx->is_enc) ||
- (V4L2_TYPE_IS_OUTPUT(q->type) && ctx->is_enc)) {
- if (!ctx->is_stateless)
- kvfree(ctx->state.ref_frame.buf);
- ctx->state.ref_frame.buf = NULL;
- ctx->state.ref_frame.luma = NULL;
- ctx->comp_max_size = 0;
- ctx->source_changed = false;
- }
- if (V4L2_TYPE_IS_OUTPUT(q->type) && !ctx->is_enc) {
- ctx->cur_buf_offset = 0;
- ctx->comp_size = 0;
- ctx->header_size = 0;
- ctx->comp_magic_cnt = 0;
- ctx->comp_has_frame = 0;
- ctx->comp_has_next_frame = 0;
- }
-}
-
-static void vicodec_buf_request_complete(struct vb2_buffer *vb)
-{
- struct vicodec_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
-
- v4l2_ctrl_request_complete(vb->req_obj.req, &ctx->hdl);
-}
-
-
-static const struct vb2_ops vicodec_qops = {
- .queue_setup = vicodec_queue_setup,
- .buf_out_validate = vicodec_buf_out_validate,
- .buf_prepare = vicodec_buf_prepare,
- .buf_queue = vicodec_buf_queue,
- .buf_request_complete = vicodec_buf_request_complete,
- .start_streaming = vicodec_start_streaming,
- .stop_streaming = vicodec_stop_streaming,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
-};
-
-static int queue_init(void *priv, struct vb2_queue *src_vq,
- struct vb2_queue *dst_vq)
-{
- struct vicodec_ctx *ctx = priv;
- int ret;
-
- src_vq->type = (multiplanar ?
- V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE :
- V4L2_BUF_TYPE_VIDEO_OUTPUT);
- src_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
- src_vq->drv_priv = ctx;
- src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
- src_vq->ops = &vicodec_qops;
- src_vq->mem_ops = &vb2_vmalloc_memops;
- src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
- if (ctx->is_enc)
- src_vq->lock = &ctx->dev->stateful_enc.mutex;
- else if (ctx->is_stateless)
- src_vq->lock = &ctx->dev->stateless_dec.mutex;
- else
- src_vq->lock = &ctx->dev->stateful_dec.mutex;
- src_vq->supports_requests = ctx->is_stateless;
- src_vq->requires_requests = ctx->is_stateless;
- ret = vb2_queue_init(src_vq);
- if (ret)
- return ret;
-
- dst_vq->type = (multiplanar ?
- V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE :
- V4L2_BUF_TYPE_VIDEO_CAPTURE);
- dst_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
- dst_vq->drv_priv = ctx;
- dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
- dst_vq->ops = &vicodec_qops;
- dst_vq->mem_ops = &vb2_vmalloc_memops;
- dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
- dst_vq->lock = src_vq->lock;
-
- return vb2_queue_init(dst_vq);
-}
-
-static int vicodec_try_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct vicodec_ctx *ctx = container_of(ctrl->handler,
- struct vicodec_ctx, hdl);
- const struct v4l2_ctrl_fwht_params *params;
- struct vicodec_q_data *q_dst = get_q_data(ctx,
- V4L2_BUF_TYPE_VIDEO_CAPTURE);
-
- switch (ctrl->id) {
- case V4L2_CID_MPEG_VIDEO_FWHT_PARAMS:
- if (!q_dst->info)
- return -EINVAL;
- params = ctrl->p_new.p_fwht_params;
- if (params->width > q_dst->coded_width ||
- params->width < MIN_WIDTH ||
- params->height > q_dst->coded_height ||
- params->height < MIN_HEIGHT)
- return -EINVAL;
- if (!validate_by_version(params->flags, params->version))
- return -EINVAL;
- if (!validate_stateless_params_flags(params, q_dst->info))
- return -EINVAL;
- return 0;
- default:
- return 0;
- }
- return 0;
-}
-
-static void update_header_from_stateless_params(struct vicodec_ctx *ctx,
- const struct v4l2_ctrl_fwht_params *params)
-{
- struct fwht_cframe_hdr *p_hdr = &ctx->state.header;
-
- p_hdr->magic1 = FWHT_MAGIC1;
- p_hdr->magic2 = FWHT_MAGIC2;
- p_hdr->version = htonl(params->version);
- p_hdr->width = htonl(params->width);
- p_hdr->height = htonl(params->height);
- p_hdr->flags = htonl(params->flags);
- p_hdr->colorspace = htonl(params->colorspace);
- p_hdr->xfer_func = htonl(params->xfer_func);
- p_hdr->ycbcr_enc = htonl(params->ycbcr_enc);
- p_hdr->quantization = htonl(params->quantization);
-}
-
-static int vicodec_s_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct vicodec_ctx *ctx = container_of(ctrl->handler,
- struct vicodec_ctx, hdl);
- const struct v4l2_ctrl_fwht_params *params;
-
- switch (ctrl->id) {
- case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
- ctx->state.gop_size = ctrl->val;
- return 0;
- case V4L2_CID_FWHT_I_FRAME_QP:
- ctx->state.i_frame_qp = ctrl->val;
- return 0;
- case V4L2_CID_FWHT_P_FRAME_QP:
- ctx->state.p_frame_qp = ctrl->val;
- return 0;
- case V4L2_CID_MPEG_VIDEO_FWHT_PARAMS:
- params = ctrl->p_new.p_fwht_params;
- update_header_from_stateless_params(ctx, params);
- ctx->state.ref_frame_ts = params->backward_ref_ts;
- return 0;
- }
- return -EINVAL;
-}
-
-static const struct v4l2_ctrl_ops vicodec_ctrl_ops = {
- .s_ctrl = vicodec_s_ctrl,
- .try_ctrl = vicodec_try_ctrl,
-};
-
-static const struct v4l2_ctrl_config vicodec_ctrl_stateless_state = {
- .ops = &vicodec_ctrl_ops,
- .id = V4L2_CID_MPEG_VIDEO_FWHT_PARAMS,
- .elem_size = sizeof(struct v4l2_ctrl_fwht_params),
-};
-
-/*
- * File operations
- */
-static int vicodec_open(struct file *file)
-{
- const struct v4l2_fwht_pixfmt_info *info = v4l2_fwht_get_pixfmt(0);
- struct video_device *vfd = video_devdata(file);
- struct vicodec_dev *dev = video_drvdata(file);
- struct vicodec_ctx *ctx = NULL;
- struct v4l2_ctrl_handler *hdl;
- unsigned int raw_size;
- unsigned int comp_size;
- int rc = 0;
-
- if (mutex_lock_interruptible(vfd->lock))
- return -ERESTARTSYS;
- ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
- if (!ctx) {
- rc = -ENOMEM;
- goto open_unlock;
- }
-
- if (vfd == &dev->stateful_enc.vfd)
- ctx->is_enc = true;
- else if (vfd == &dev->stateless_dec.vfd)
- ctx->is_stateless = true;
-
- v4l2_fh_init(&ctx->fh, video_devdata(file));
- file->private_data = &ctx->fh;
- ctx->dev = dev;
- hdl = &ctx->hdl;
- v4l2_ctrl_handler_init(hdl, 5);
- v4l2_ctrl_new_std(hdl, &vicodec_ctrl_ops, V4L2_CID_MPEG_VIDEO_GOP_SIZE,
- 1, 16, 1, 10);
- v4l2_ctrl_new_std(hdl, &vicodec_ctrl_ops, V4L2_CID_FWHT_I_FRAME_QP,
- 1, 31, 1, 20);
- v4l2_ctrl_new_std(hdl, &vicodec_ctrl_ops, V4L2_CID_FWHT_P_FRAME_QP,
- 1, 31, 1, 20);
- if (ctx->is_enc)
- v4l2_ctrl_new_std(hdl, &vicodec_ctrl_ops,
- V4L2_CID_MIN_BUFFERS_FOR_OUTPUT, 1, 1, 1, 1);
- if (ctx->is_stateless)
- v4l2_ctrl_new_custom(hdl, &vicodec_ctrl_stateless_state, NULL);
- if (hdl->error) {
- rc = hdl->error;
- v4l2_ctrl_handler_free(hdl);
- kfree(ctx);
- goto open_unlock;
- }
- ctx->fh.ctrl_handler = hdl;
- v4l2_ctrl_handler_setup(hdl);
-
- if (ctx->is_enc)
- ctx->q_data[V4L2_M2M_SRC].info = info;
- else if (ctx->is_stateless)
- ctx->q_data[V4L2_M2M_SRC].info = &pixfmt_stateless_fwht;
- else
- ctx->q_data[V4L2_M2M_SRC].info = &pixfmt_fwht;
- ctx->q_data[V4L2_M2M_SRC].coded_width = 1280;
- ctx->q_data[V4L2_M2M_SRC].coded_height = 720;
- ctx->q_data[V4L2_M2M_SRC].visible_width = 1280;
- ctx->q_data[V4L2_M2M_SRC].visible_height = 720;
- raw_size = 1280 * 720 * info->sizeimage_mult / info->sizeimage_div;
- comp_size = 1280 * 720 * pixfmt_fwht.sizeimage_mult /
- pixfmt_fwht.sizeimage_div;
- if (ctx->is_enc)
- ctx->q_data[V4L2_M2M_SRC].sizeimage = raw_size;
- else if (ctx->is_stateless)
- ctx->q_data[V4L2_M2M_SRC].sizeimage = comp_size;
- else
- ctx->q_data[V4L2_M2M_SRC].sizeimage =
- comp_size + sizeof(struct fwht_cframe_hdr);
- ctx->q_data[V4L2_M2M_DST] = ctx->q_data[V4L2_M2M_SRC];
- if (ctx->is_enc) {
- ctx->q_data[V4L2_M2M_DST].info = &pixfmt_fwht;
- ctx->q_data[V4L2_M2M_DST].sizeimage =
- comp_size + sizeof(struct fwht_cframe_hdr);
- } else {
- ctx->q_data[V4L2_M2M_DST].info = info;
- ctx->q_data[V4L2_M2M_DST].sizeimage = raw_size;
- }
-
- ctx->state.colorspace = V4L2_COLORSPACE_REC709;
-
- if (ctx->is_enc) {
- ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->stateful_enc.m2m_dev,
- ctx, &queue_init);
- ctx->lock = &dev->stateful_enc.lock;
- } else if (ctx->is_stateless) {
- ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->stateless_dec.m2m_dev,
- ctx, &queue_init);
- ctx->lock = &dev->stateless_dec.lock;
- } else {
- ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->stateful_dec.m2m_dev,
- ctx, &queue_init);
- ctx->lock = &dev->stateful_dec.lock;
- }
-
- if (IS_ERR(ctx->fh.m2m_ctx)) {
- rc = PTR_ERR(ctx->fh.m2m_ctx);
-
- v4l2_ctrl_handler_free(hdl);
- v4l2_fh_exit(&ctx->fh);
- kfree(ctx);
- goto open_unlock;
- }
-
- v4l2_fh_add(&ctx->fh);
-
-open_unlock:
- mutex_unlock(vfd->lock);
- return rc;
-}
-
-static int vicodec_release(struct file *file)
-{
- struct video_device *vfd = video_devdata(file);
- struct vicodec_ctx *ctx = file2ctx(file);
-
- mutex_lock(vfd->lock);
- v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
- mutex_unlock(vfd->lock);
- v4l2_fh_del(&ctx->fh);
- v4l2_fh_exit(&ctx->fh);
- v4l2_ctrl_handler_free(&ctx->hdl);
- kvfree(ctx->state.compressed_frame);
- kfree(ctx);
-
- return 0;
-}
-
-static int vicodec_request_validate(struct media_request *req)
-{
- struct media_request_object *obj;
- struct v4l2_ctrl_handler *parent_hdl, *hdl;
- struct vicodec_ctx *ctx = NULL;
- struct v4l2_ctrl *ctrl;
- unsigned int count;
-
- list_for_each_entry(obj, &req->objects, list) {
- struct vb2_buffer *vb;
-
- if (vb2_request_object_is_buffer(obj)) {
- vb = container_of(obj, struct vb2_buffer, req_obj);
- ctx = vb2_get_drv_priv(vb->vb2_queue);
-
- break;
- }
- }
-
- if (!ctx) {
- pr_err("No buffer was provided with the request\n");
- return -ENOENT;
- }
-
- count = vb2_request_buffer_cnt(req);
- if (!count) {
- v4l2_info(&ctx->dev->v4l2_dev,
- "No buffer was provided with the request\n");
- return -ENOENT;
- } else if (count > 1) {
- v4l2_info(&ctx->dev->v4l2_dev,
- "More than one buffer was provided with the request\n");
- return -EINVAL;
- }
-
- parent_hdl = &ctx->hdl;
-
- hdl = v4l2_ctrl_request_hdl_find(req, parent_hdl);
- if (!hdl) {
- v4l2_info(&ctx->dev->v4l2_dev, "Missing codec control\n");
- return -ENOENT;
- }
- ctrl = v4l2_ctrl_request_hdl_ctrl_find(hdl,
- vicodec_ctrl_stateless_state.id);
- v4l2_ctrl_request_hdl_put(hdl);
- if (!ctrl) {
- v4l2_info(&ctx->dev->v4l2_dev,
- "Missing required codec control\n");
- return -ENOENT;
- }
-
- return vb2_request_validate(req);
-}
-
-static const struct v4l2_file_operations vicodec_fops = {
- .owner = THIS_MODULE,
- .open = vicodec_open,
- .release = vicodec_release,
- .poll = v4l2_m2m_fop_poll,
- .unlocked_ioctl = video_ioctl2,
- .mmap = v4l2_m2m_fop_mmap,
-};
-
-static const struct video_device vicodec_videodev = {
- .name = VICODEC_NAME,
- .vfl_dir = VFL_DIR_M2M,
- .fops = &vicodec_fops,
- .ioctl_ops = &vicodec_ioctl_ops,
- .minor = -1,
- .release = video_device_release_empty,
-};
-
-static const struct media_device_ops vicodec_m2m_media_ops = {
- .req_validate = vicodec_request_validate,
- .req_queue = v4l2_m2m_request_queue,
-};
-
-static const struct v4l2_m2m_ops m2m_ops = {
- .device_run = device_run,
- .job_ready = job_ready,
-};
-
-static int register_instance(struct vicodec_dev *dev,
- struct vicodec_dev_instance *dev_instance,
- const char *name, bool is_enc)
-{
- struct video_device *vfd;
- int ret;
-
- spin_lock_init(&dev_instance->lock);
- mutex_init(&dev_instance->mutex);
- dev_instance->m2m_dev = v4l2_m2m_init(&m2m_ops);
- if (IS_ERR(dev_instance->m2m_dev)) {
- v4l2_err(&dev->v4l2_dev, "Failed to init vicodec enc device\n");
- return PTR_ERR(dev_instance->m2m_dev);
- }
-
- dev_instance->vfd = vicodec_videodev;
- vfd = &dev_instance->vfd;
- vfd->lock = &dev_instance->mutex;
- vfd->v4l2_dev = &dev->v4l2_dev;
- strscpy(vfd->name, name, sizeof(vfd->name));
- vfd->device_caps = V4L2_CAP_STREAMING |
- (multiplanar ? V4L2_CAP_VIDEO_M2M_MPLANE : V4L2_CAP_VIDEO_M2M);
- if (is_enc) {
- v4l2_disable_ioctl(vfd, VIDIOC_DECODER_CMD);
- v4l2_disable_ioctl(vfd, VIDIOC_TRY_DECODER_CMD);
- } else {
- v4l2_disable_ioctl(vfd, VIDIOC_ENCODER_CMD);
- v4l2_disable_ioctl(vfd, VIDIOC_TRY_ENCODER_CMD);
- }
- video_set_drvdata(vfd, dev);
-
- ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0);
- if (ret) {
- v4l2_err(&dev->v4l2_dev, "Failed to register video device '%s'\n", name);
- v4l2_m2m_release(dev_instance->m2m_dev);
- return ret;
- }
- v4l2_info(&dev->v4l2_dev, "Device '%s' registered as /dev/video%d\n",
- name, vfd->num);
- return 0;
-}
-
-static void vicodec_v4l2_dev_release(struct v4l2_device *v4l2_dev)
-{
- struct vicodec_dev *dev = container_of(v4l2_dev, struct vicodec_dev, v4l2_dev);
-
- v4l2_device_unregister(&dev->v4l2_dev);
- v4l2_m2m_release(dev->stateful_enc.m2m_dev);
- v4l2_m2m_release(dev->stateful_dec.m2m_dev);
- v4l2_m2m_release(dev->stateless_dec.m2m_dev);
-#ifdef CONFIG_MEDIA_CONTROLLER
- media_device_cleanup(&dev->mdev);
-#endif
- kfree(dev);
-}
-
-static int vicodec_probe(struct platform_device *pdev)
-{
- struct vicodec_dev *dev;
- int ret;
-
- dev = kzalloc(sizeof(*dev), GFP_KERNEL);
- if (!dev)
- return -ENOMEM;
-
- ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
- if (ret)
- goto free_dev;
-
- dev->v4l2_dev.release = vicodec_v4l2_dev_release;
-
-#ifdef CONFIG_MEDIA_CONTROLLER
- dev->mdev.dev = &pdev->dev;
- strscpy(dev->mdev.model, "vicodec", sizeof(dev->mdev.model));
- strscpy(dev->mdev.bus_info, "platform:vicodec",
- sizeof(dev->mdev.bus_info));
- media_device_init(&dev->mdev);
- dev->mdev.ops = &vicodec_m2m_media_ops;
- dev->v4l2_dev.mdev = &dev->mdev;
-#endif
-
- platform_set_drvdata(pdev, dev);
-
- ret = register_instance(dev, &dev->stateful_enc, "stateful-encoder",
- true);
- if (ret)
- goto unreg_dev;
-
- ret = register_instance(dev, &dev->stateful_dec, "stateful-decoder",
- false);
- if (ret)
- goto unreg_sf_enc;
-
- ret = register_instance(dev, &dev->stateless_dec, "stateless-decoder",
- false);
- if (ret)
- goto unreg_sf_dec;
-
-#ifdef CONFIG_MEDIA_CONTROLLER
- ret = v4l2_m2m_register_media_controller(dev->stateful_enc.m2m_dev,
- &dev->stateful_enc.vfd,
- MEDIA_ENT_F_PROC_VIDEO_ENCODER);
- if (ret) {
- v4l2_err(&dev->v4l2_dev, "Failed to init mem2mem media controller for enc\n");
- goto unreg_m2m;
- }
-
- ret = v4l2_m2m_register_media_controller(dev->stateful_dec.m2m_dev,
- &dev->stateful_dec.vfd,
- MEDIA_ENT_F_PROC_VIDEO_DECODER);
- if (ret) {
- v4l2_err(&dev->v4l2_dev, "Failed to init mem2mem media controller for dec\n");
- goto unreg_m2m_sf_enc_mc;
- }
-
- ret = v4l2_m2m_register_media_controller(dev->stateless_dec.m2m_dev,
- &dev->stateless_dec.vfd,
- MEDIA_ENT_F_PROC_VIDEO_DECODER);
- if (ret) {
- v4l2_err(&dev->v4l2_dev, "Failed to init mem2mem media controller for stateless dec\n");
- goto unreg_m2m_sf_dec_mc;
- }
-
- ret = media_device_register(&dev->mdev);
- if (ret) {
- v4l2_err(&dev->v4l2_dev, "Failed to register mem2mem media device\n");
- goto unreg_m2m_sl_dec_mc;
- }
-#endif
- return 0;
-
-#ifdef CONFIG_MEDIA_CONTROLLER
-unreg_m2m_sl_dec_mc:
- v4l2_m2m_unregister_media_controller(dev->stateless_dec.m2m_dev);
-unreg_m2m_sf_dec_mc:
- v4l2_m2m_unregister_media_controller(dev->stateful_dec.m2m_dev);
-unreg_m2m_sf_enc_mc:
- v4l2_m2m_unregister_media_controller(dev->stateful_enc.m2m_dev);
-unreg_m2m:
- video_unregister_device(&dev->stateless_dec.vfd);
- v4l2_m2m_release(dev->stateless_dec.m2m_dev);
-#endif
-unreg_sf_dec:
- video_unregister_device(&dev->stateful_dec.vfd);
- v4l2_m2m_release(dev->stateful_dec.m2m_dev);
-unreg_sf_enc:
- video_unregister_device(&dev->stateful_enc.vfd);
- v4l2_m2m_release(dev->stateful_enc.m2m_dev);
-unreg_dev:
- v4l2_device_unregister(&dev->v4l2_dev);
-free_dev:
- kfree(dev);
-
- return ret;
-}
-
-static int vicodec_remove(struct platform_device *pdev)
-{
- struct vicodec_dev *dev = platform_get_drvdata(pdev);
-
- v4l2_info(&dev->v4l2_dev, "Removing " VICODEC_NAME);
-
-#ifdef CONFIG_MEDIA_CONTROLLER
- media_device_unregister(&dev->mdev);
- v4l2_m2m_unregister_media_controller(dev->stateful_enc.m2m_dev);
- v4l2_m2m_unregister_media_controller(dev->stateful_dec.m2m_dev);
- v4l2_m2m_unregister_media_controller(dev->stateless_dec.m2m_dev);
-#endif
-
- video_unregister_device(&dev->stateful_enc.vfd);
- video_unregister_device(&dev->stateful_dec.vfd);
- video_unregister_device(&dev->stateless_dec.vfd);
- v4l2_device_put(&dev->v4l2_dev);
-
- return 0;
-}
-
-static struct platform_driver vicodec_pdrv = {
- .probe = vicodec_probe,
- .remove = vicodec_remove,
- .driver = {
- .name = VICODEC_NAME,
- },
-};
-
-static void __exit vicodec_exit(void)
-{
- platform_driver_unregister(&vicodec_pdrv);
- platform_device_unregister(&vicodec_pdev);
-}
-
-static int __init vicodec_init(void)
-{
- int ret;
-
- ret = platform_device_register(&vicodec_pdev);
- if (ret)
- return ret;
-
- ret = platform_driver_register(&vicodec_pdrv);
- if (ret)
- platform_device_unregister(&vicodec_pdev);
-
- return ret;
-}
-
-module_init(vicodec_init);
-module_exit(vicodec_exit);
diff --git a/drivers/media/platform/video-mux.c b/drivers/media/platform/video-mux.c
index ddd0e33..640ce76 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 @@
.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 @@
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,66 @@
.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, *remote_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;
+
+ /* Skip dangling endpoints for backwards compatibility */
+ remote_ep = fwnode_graph_get_remote_endpoint(ep);
+ if (!remote_ep) {
+ fwnode_handle_put(ep);
+ continue;
+ }
+ fwnode_handle_put(remote_ep);
+
+ asd = v4l2_async_notifier_add_fwnode_remote_subdev(
+ &vmux->notifier, ep, sizeof(*asd));
+
+ fwnode_handle_put(ep);
+
+ if (IS_ERR(asd)) {
+ ret = PTR_ERR(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 +473,13 @@
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 +487,8 @@
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/vim2m.c b/drivers/media/platform/vim2m.c
deleted file mode 100644
index 8d6b096..0000000
--- a/drivers/media/platform/vim2m.c
+++ /dev/null
@@ -1,1441 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * A virtual v4l2-mem2mem example device.
- *
- * This is a virtual device driver for testing mem-to-mem videobuf framework.
- * It simulates a device that uses memory buffers for both source and
- * destination, processes the data and issues an "irq" (simulated by a delayed
- * workqueue).
- * The device is capable of multi-instance, multi-buffer-per-transaction
- * operation (via the mem2mem framework).
- *
- * Copyright (c) 2009-2010 Samsung Electronics Co., Ltd.
- * Pawel Osciak, <pawel@osciak.com>
- * Marek Szyprowski, <m.szyprowski@samsung.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the
- * License, or (at your option) any later version
- */
-#include <linux/module.h>
-#include <linux/delay.h>
-#include <linux/fs.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-
-#include <linux/platform_device.h>
-#include <media/v4l2-mem2mem.h>
-#include <media/v4l2-device.h>
-#include <media/v4l2-ioctl.h>
-#include <media/v4l2-ctrls.h>
-#include <media/v4l2-event.h>
-#include <media/videobuf2-vmalloc.h>
-
-MODULE_DESCRIPTION("Virtual device for mem2mem framework testing");
-MODULE_AUTHOR("Pawel Osciak, <pawel@osciak.com>");
-MODULE_LICENSE("GPL");
-MODULE_VERSION("0.2");
-MODULE_ALIAS("mem2mem_testdev");
-
-static unsigned int debug;
-module_param(debug, uint, 0644);
-MODULE_PARM_DESC(debug, "debug level");
-
-/* Default transaction time in msec */
-static unsigned int default_transtime = 40; /* Max 25 fps */
-module_param(default_transtime, uint, 0644);
-MODULE_PARM_DESC(default_transtime, "default transaction time in ms");
-
-#define MIN_W 32
-#define MIN_H 32
-#define MAX_W 640
-#define MAX_H 480
-
-/* Pixel alignment for non-bayer formats */
-#define WIDTH_ALIGN 2
-#define HEIGHT_ALIGN 1
-
-/* Pixel alignment for bayer formats */
-#define BAYER_WIDTH_ALIGN 2
-#define BAYER_HEIGHT_ALIGN 2
-
-/* Flags that indicate a format can be used for capture/output */
-#define MEM2MEM_CAPTURE BIT(0)
-#define MEM2MEM_OUTPUT BIT(1)
-
-#define MEM2MEM_NAME "vim2m"
-
-/* Per queue */
-#define MEM2MEM_DEF_NUM_BUFS VIDEO_MAX_FRAME
-/* In bytes, per queue */
-#define MEM2MEM_VID_MEM_LIMIT (16 * 1024 * 1024)
-
-/* Flags that indicate processing mode */
-#define MEM2MEM_HFLIP BIT(0)
-#define MEM2MEM_VFLIP BIT(1)
-
-#define dprintk(dev, lvl, fmt, arg...) \
- v4l2_dbg(lvl, debug, &(dev)->v4l2_dev, "%s: " fmt, __func__, ## arg)
-
-static void vim2m_dev_release(struct device *dev)
-{}
-
-static struct platform_device vim2m_pdev = {
- .name = MEM2MEM_NAME,
- .dev.release = vim2m_dev_release,
-};
-
-struct vim2m_fmt {
- u32 fourcc;
- int depth;
- /* Types the format can be used for */
- u32 types;
-};
-
-static struct vim2m_fmt formats[] = {
- {
- .fourcc = V4L2_PIX_FMT_RGB565, /* rrrrrggg gggbbbbb */
- .depth = 16,
- .types = MEM2MEM_CAPTURE | MEM2MEM_OUTPUT,
- }, {
- .fourcc = V4L2_PIX_FMT_RGB565X, /* gggbbbbb rrrrrggg */
- .depth = 16,
- .types = MEM2MEM_CAPTURE | MEM2MEM_OUTPUT,
- }, {
- .fourcc = V4L2_PIX_FMT_RGB24,
- .depth = 24,
- .types = MEM2MEM_CAPTURE | MEM2MEM_OUTPUT,
- }, {
- .fourcc = V4L2_PIX_FMT_BGR24,
- .depth = 24,
- .types = MEM2MEM_CAPTURE | MEM2MEM_OUTPUT,
- }, {
- .fourcc = V4L2_PIX_FMT_YUYV,
- .depth = 16,
- .types = MEM2MEM_CAPTURE,
- }, {
- .fourcc = V4L2_PIX_FMT_SBGGR8,
- .depth = 8,
- .types = MEM2MEM_CAPTURE,
- }, {
- .fourcc = V4L2_PIX_FMT_SGBRG8,
- .depth = 8,
- .types = MEM2MEM_CAPTURE,
- }, {
- .fourcc = V4L2_PIX_FMT_SGRBG8,
- .depth = 8,
- .types = MEM2MEM_CAPTURE,
- }, {
- .fourcc = V4L2_PIX_FMT_SRGGB8,
- .depth = 8,
- .types = MEM2MEM_CAPTURE,
- },
-};
-
-#define NUM_FORMATS ARRAY_SIZE(formats)
-
-/* Per-queue, driver-specific private data */
-struct vim2m_q_data {
- unsigned int width;
- unsigned int height;
- unsigned int sizeimage;
- unsigned int sequence;
- struct vim2m_fmt *fmt;
-};
-
-enum {
- V4L2_M2M_SRC = 0,
- V4L2_M2M_DST = 1,
-};
-
-#define V4L2_CID_TRANS_TIME_MSEC (V4L2_CID_USER_BASE + 0x1000)
-#define V4L2_CID_TRANS_NUM_BUFS (V4L2_CID_USER_BASE + 0x1001)
-
-static struct vim2m_fmt *find_format(u32 fourcc)
-{
- struct vim2m_fmt *fmt;
- unsigned int k;
-
- for (k = 0; k < NUM_FORMATS; k++) {
- fmt = &formats[k];
- if (fmt->fourcc == fourcc)
- break;
- }
-
- if (k == NUM_FORMATS)
- return NULL;
-
- return &formats[k];
-}
-
-static void get_alignment(u32 fourcc,
- unsigned int *walign, unsigned int *halign)
-{
- switch (fourcc) {
- case V4L2_PIX_FMT_SBGGR8:
- case V4L2_PIX_FMT_SGBRG8:
- case V4L2_PIX_FMT_SGRBG8:
- case V4L2_PIX_FMT_SRGGB8:
- *walign = BAYER_WIDTH_ALIGN;
- *halign = BAYER_HEIGHT_ALIGN;
- return;
- default:
- *walign = WIDTH_ALIGN;
- *halign = HEIGHT_ALIGN;
- return;
- }
-}
-
-struct vim2m_dev {
- struct v4l2_device v4l2_dev;
- struct video_device vfd;
-#ifdef CONFIG_MEDIA_CONTROLLER
- struct media_device mdev;
-#endif
-
- atomic_t num_inst;
- struct mutex dev_mutex;
-
- struct v4l2_m2m_dev *m2m_dev;
-};
-
-struct vim2m_ctx {
- struct v4l2_fh fh;
- struct vim2m_dev *dev;
-
- struct v4l2_ctrl_handler hdl;
-
- /* Processed buffers in this transaction */
- u8 num_processed;
-
- /* Transaction length (i.e. how many buffers per transaction) */
- u32 translen;
- /* Transaction time (i.e. simulated processing time) in milliseconds */
- u32 transtime;
-
- struct mutex vb_mutex;
- struct delayed_work work_run;
- spinlock_t irqlock;
-
- /* Abort requested by m2m */
- int aborting;
-
- /* Processing mode */
- int mode;
-
- enum v4l2_colorspace colorspace;
- enum v4l2_ycbcr_encoding ycbcr_enc;
- enum v4l2_xfer_func xfer_func;
- enum v4l2_quantization quant;
-
- /* Source and destination queue data */
- struct vim2m_q_data q_data[2];
-};
-
-static inline struct vim2m_ctx *file2ctx(struct file *file)
-{
- return container_of(file->private_data, struct vim2m_ctx, fh);
-}
-
-static struct vim2m_q_data *get_q_data(struct vim2m_ctx *ctx,
- enum v4l2_buf_type type)
-{
- switch (type) {
- case V4L2_BUF_TYPE_VIDEO_OUTPUT:
- return &ctx->q_data[V4L2_M2M_SRC];
- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
- return &ctx->q_data[V4L2_M2M_DST];
- default:
- return NULL;
- }
-}
-
-static const char *type_name(enum v4l2_buf_type type)
-{
- switch (type) {
- case V4L2_BUF_TYPE_VIDEO_OUTPUT:
- return "Output";
- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
- return "Capture";
- default:
- return "Invalid";
- }
-}
-
-#define CLIP(__color) \
- (u8)(((__color) > 0xff) ? 0xff : (((__color) < 0) ? 0 : (__color)))
-
-static void copy_line(struct vim2m_q_data *q_data_out,
- u8 *src, u8 *dst, bool reverse)
-{
- int x, depth = q_data_out->fmt->depth >> 3;
-
- if (!reverse) {
- memcpy(dst, src, q_data_out->width * depth);
- } else {
- for (x = 0; x < q_data_out->width >> 1; x++) {
- memcpy(dst, src, depth);
- memcpy(dst + depth, src - depth, depth);
- src -= depth << 1;
- dst += depth << 1;
- }
- return;
- }
-}
-
-static void copy_two_pixels(struct vim2m_q_data *q_data_in,
- struct vim2m_q_data *q_data_out,
- u8 *src[2], u8 **dst, int ypos, bool reverse)
-{
- struct vim2m_fmt *out = q_data_out->fmt;
- struct vim2m_fmt *in = q_data_in->fmt;
- u8 _r[2], _g[2], _b[2], *r, *g, *b;
- int i;
-
- /* Step 1: read two consecutive pixels from src pointer */
-
- r = _r;
- g = _g;
- b = _b;
-
- switch (in->fourcc) {
- case V4L2_PIX_FMT_RGB565: /* rrrrrggg gggbbbbb */
- for (i = 0; i < 2; i++) {
- u16 pix = le16_to_cpu(*(__le16 *)(src[i]));
-
- *r++ = (u8)(((pix & 0xf800) >> 11) << 3) | 0x07;
- *g++ = (u8)((((pix & 0x07e0) >> 5)) << 2) | 0x03;
- *b++ = (u8)((pix & 0x1f) << 3) | 0x07;
- }
- break;
- case V4L2_PIX_FMT_RGB565X: /* gggbbbbb rrrrrggg */
- for (i = 0; i < 2; i++) {
- u16 pix = be16_to_cpu(*(__be16 *)(src[i]));
-
- *r++ = (u8)(((pix & 0xf800) >> 11) << 3) | 0x07;
- *g++ = (u8)((((pix & 0x07e0) >> 5)) << 2) | 0x03;
- *b++ = (u8)((pix & 0x1f) << 3) | 0x07;
- }
- break;
- default:
- case V4L2_PIX_FMT_RGB24:
- for (i = 0; i < 2; i++) {
- *r++ = src[i][0];
- *g++ = src[i][1];
- *b++ = src[i][2];
- }
- break;
- case V4L2_PIX_FMT_BGR24:
- for (i = 0; i < 2; i++) {
- *b++ = src[i][0];
- *g++ = src[i][1];
- *r++ = src[i][2];
- }
- break;
- }
-
- /* Step 2: store two consecutive points, reversing them if needed */
-
- r = _r;
- g = _g;
- b = _b;
-
- switch (out->fourcc) {
- case V4L2_PIX_FMT_RGB565: /* rrrrrggg gggbbbbb */
- for (i = 0; i < 2; i++) {
- u16 pix;
- __le16 *dst_pix = (__le16 *)*dst;
-
- pix = ((*r << 8) & 0xf800) | ((*g << 3) & 0x07e0) |
- (*b >> 3);
-
- *dst_pix = cpu_to_le16(pix);
-
- *dst += 2;
- }
- return;
- case V4L2_PIX_FMT_RGB565X: /* gggbbbbb rrrrrggg */
- for (i = 0; i < 2; i++) {
- u16 pix;
- __be16 *dst_pix = (__be16 *)*dst;
-
- pix = ((*r << 8) & 0xf800) | ((*g << 3) & 0x07e0) |
- (*b >> 3);
-
- *dst_pix = cpu_to_be16(pix);
-
- *dst += 2;
- }
- return;
- case V4L2_PIX_FMT_RGB24:
- for (i = 0; i < 2; i++) {
- *(*dst)++ = *r++;
- *(*dst)++ = *g++;
- *(*dst)++ = *b++;
- }
- return;
- case V4L2_PIX_FMT_BGR24:
- for (i = 0; i < 2; i++) {
- *(*dst)++ = *b++;
- *(*dst)++ = *g++;
- *(*dst)++ = *r++;
- }
- return;
- case V4L2_PIX_FMT_YUYV:
- default:
- {
- u8 y, y1, u, v;
-
- y = ((8453 * (*r) + 16594 * (*g) + 3223 * (*b)
- + 524288) >> 15);
- u = ((-4878 * (*r) - 9578 * (*g) + 14456 * (*b)
- + 4210688) >> 15);
- v = ((14456 * (*r++) - 12105 * (*g++) - 2351 * (*b++)
- + 4210688) >> 15);
- y1 = ((8453 * (*r) + 16594 * (*g) + 3223 * (*b)
- + 524288) >> 15);
-
- *(*dst)++ = y;
- *(*dst)++ = u;
-
- *(*dst)++ = y1;
- *(*dst)++ = v;
- return;
- }
- case V4L2_PIX_FMT_SBGGR8:
- if (!(ypos & 1)) {
- *(*dst)++ = *b;
- *(*dst)++ = *++g;
- } else {
- *(*dst)++ = *g;
- *(*dst)++ = *++r;
- }
- return;
- case V4L2_PIX_FMT_SGBRG8:
- if (!(ypos & 1)) {
- *(*dst)++ = *g;
- *(*dst)++ = *++b;
- } else {
- *(*dst)++ = *r;
- *(*dst)++ = *++g;
- }
- return;
- case V4L2_PIX_FMT_SGRBG8:
- if (!(ypos & 1)) {
- *(*dst)++ = *g;
- *(*dst)++ = *++r;
- } else {
- *(*dst)++ = *b;
- *(*dst)++ = *++g;
- }
- return;
- case V4L2_PIX_FMT_SRGGB8:
- if (!(ypos & 1)) {
- *(*dst)++ = *r;
- *(*dst)++ = *++g;
- } else {
- *(*dst)++ = *g;
- *(*dst)++ = *++b;
- }
- return;
- }
-}
-
-static int device_process(struct vim2m_ctx *ctx,
- struct vb2_v4l2_buffer *in_vb,
- struct vb2_v4l2_buffer *out_vb)
-{
- struct vim2m_dev *dev = ctx->dev;
- struct vim2m_q_data *q_data_in, *q_data_out;
- u8 *p_in, *p_line, *p_in_x[2], *p, *p_out;
- unsigned int width, height, bytesperline, bytes_per_pixel;
- unsigned int x, y, y_in, y_out, x_int, x_fract, x_err, x_offset;
- int start, end, step;
-
- q_data_in = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
- if (!q_data_in)
- return 0;
- bytesperline = (q_data_in->width * q_data_in->fmt->depth) >> 3;
- bytes_per_pixel = q_data_in->fmt->depth >> 3;
-
- q_data_out = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
- if (!q_data_out)
- return 0;
-
- /* As we're doing scaling, use the output dimensions here */
- height = q_data_out->height;
- width = q_data_out->width;
-
- p_in = vb2_plane_vaddr(&in_vb->vb2_buf, 0);
- p_out = vb2_plane_vaddr(&out_vb->vb2_buf, 0);
- if (!p_in || !p_out) {
- v4l2_err(&dev->v4l2_dev,
- "Acquiring kernel pointers to buffers failed\n");
- return -EFAULT;
- }
-
- out_vb->sequence = q_data_out->sequence++;
- in_vb->sequence = q_data_in->sequence++;
- v4l2_m2m_buf_copy_metadata(in_vb, out_vb, true);
-
- if (ctx->mode & MEM2MEM_VFLIP) {
- start = height - 1;
- end = -1;
- step = -1;
- } else {
- start = 0;
- end = height;
- step = 1;
- }
- y_out = 0;
-
- /*
- * When format and resolution are identical,
- * we can use a faster copy logic
- */
- if (q_data_in->fmt->fourcc == q_data_out->fmt->fourcc &&
- q_data_in->width == q_data_out->width &&
- q_data_in->height == q_data_out->height) {
- for (y = start; y != end; y += step, y_out++) {
- p = p_in + (y * bytesperline);
- if (ctx->mode & MEM2MEM_HFLIP)
- p += bytesperline - (q_data_in->fmt->depth >> 3);
-
- copy_line(q_data_out, p, p_out,
- ctx->mode & MEM2MEM_HFLIP);
-
- p_out += bytesperline;
- }
- return 0;
- }
-
- /* Slower algorithm with format conversion, hflip, vflip and scaler */
-
- /* To speed scaler up, use Bresenham for X dimension */
- x_int = q_data_in->width / q_data_out->width;
- x_fract = q_data_in->width % q_data_out->width;
-
- for (y = start; y != end; y += step, y_out++) {
- y_in = (y * q_data_in->height) / q_data_out->height;
- x_offset = 0;
- x_err = 0;
-
- p_line = p_in + (y_in * bytesperline);
- if (ctx->mode & MEM2MEM_HFLIP)
- p_line += bytesperline - (q_data_in->fmt->depth >> 3);
- p_in_x[0] = p_line;
-
- for (x = 0; x < width >> 1; x++) {
- x_offset += x_int;
- x_err += x_fract;
- if (x_err > width) {
- x_offset++;
- x_err -= width;
- }
-
- if (ctx->mode & MEM2MEM_HFLIP)
- p_in_x[1] = p_line - x_offset * bytes_per_pixel;
- else
- p_in_x[1] = p_line + x_offset * bytes_per_pixel;
-
- copy_two_pixels(q_data_in, q_data_out,
- p_in_x, &p_out, y_out,
- ctx->mode & MEM2MEM_HFLIP);
-
- /* Calculate the next p_in_x0 */
- x_offset += x_int;
- x_err += x_fract;
- if (x_err > width) {
- x_offset++;
- x_err -= width;
- }
-
- if (ctx->mode & MEM2MEM_HFLIP)
- p_in_x[0] = p_line - x_offset * bytes_per_pixel;
- else
- p_in_x[0] = p_line + x_offset * bytes_per_pixel;
- }
- }
-
- return 0;
-}
-
-/*
- * mem2mem callbacks
- */
-
-/*
- * job_ready() - check whether an instance is ready to be scheduled to run
- */
-static int job_ready(void *priv)
-{
- struct vim2m_ctx *ctx = priv;
-
- if (v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx) < ctx->translen
- || v4l2_m2m_num_dst_bufs_ready(ctx->fh.m2m_ctx) < ctx->translen) {
- dprintk(ctx->dev, 1, "Not enough buffers available\n");
- return 0;
- }
-
- return 1;
-}
-
-static void job_abort(void *priv)
-{
- struct vim2m_ctx *ctx = priv;
-
- /* Will cancel the transaction in the next interrupt handler */
- ctx->aborting = 1;
-}
-
-/* device_run() - prepares and starts the device
- *
- * This simulates all the immediate preparations required before starting
- * a device. This will be called by the framework when it decides to schedule
- * a particular instance.
- */
-static void device_run(void *priv)
-{
- struct vim2m_ctx *ctx = priv;
- struct vb2_v4l2_buffer *src_buf, *dst_buf;
-
- src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
- dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
-
- /* Apply request controls if any */
- v4l2_ctrl_request_setup(src_buf->vb2_buf.req_obj.req,
- &ctx->hdl);
-
- device_process(ctx, src_buf, dst_buf);
-
- /* Complete request controls if any */
- v4l2_ctrl_request_complete(src_buf->vb2_buf.req_obj.req,
- &ctx->hdl);
-
- /* Run delayed work, which simulates a hardware irq */
- schedule_delayed_work(&ctx->work_run, msecs_to_jiffies(ctx->transtime));
-}
-
-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);
-
- if (!curr_ctx) {
- pr_err("Instance released before the end of transaction\n");
- return;
- }
-
- vim2m_dev = curr_ctx->dev;
-
- src_vb = v4l2_m2m_src_buf_remove(curr_ctx->fh.m2m_ctx);
- dst_vb = v4l2_m2m_dst_buf_remove(curr_ctx->fh.m2m_ctx);
-
- 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) {
- dprintk(curr_ctx->dev, 2, "Finishing capture buffer fill\n");
- curr_ctx->num_processed = 0;
- v4l2_m2m_job_finish(vim2m_dev->m2m_dev, curr_ctx->fh.m2m_ctx);
- } else {
- device_run(curr_ctx);
- }
-}
-
-/*
- * video ioctls
- */
-static int vidioc_querycap(struct file *file, void *priv,
- struct v4l2_capability *cap)
-{
- strscpy(cap->driver, MEM2MEM_NAME, sizeof(cap->driver));
- strscpy(cap->card, MEM2MEM_NAME, sizeof(cap->card));
- snprintf(cap->bus_info, sizeof(cap->bus_info),
- "platform:%s", MEM2MEM_NAME);
- return 0;
-}
-
-static int enum_fmt(struct v4l2_fmtdesc *f, u32 type)
-{
- int i, num;
- struct vim2m_fmt *fmt;
-
- num = 0;
-
- for (i = 0; i < NUM_FORMATS; ++i) {
- if (formats[i].types & type) {
- /* index-th format of type type found ? */
- if (num == f->index)
- break;
- /*
- * Correct type but haven't reached our index yet,
- * just increment per-type index
- */
- ++num;
- }
- }
-
- if (i < NUM_FORMATS) {
- /* Format found */
- fmt = &formats[i];
- f->pixelformat = fmt->fourcc;
- return 0;
- }
-
- /* Format not found */
- return -EINVAL;
-}
-
-static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_fmtdesc *f)
-{
- return enum_fmt(f, MEM2MEM_CAPTURE);
-}
-
-static int vidioc_enum_fmt_vid_out(struct file *file, void *priv,
- struct v4l2_fmtdesc *f)
-{
- return enum_fmt(f, MEM2MEM_OUTPUT);
-}
-
-static int vidioc_enum_framesizes(struct file *file, void *priv,
- struct v4l2_frmsizeenum *fsize)
-{
- if (fsize->index != 0)
- return -EINVAL;
-
- if (!find_format(fsize->pixel_format))
- return -EINVAL;
-
- fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
- fsize->stepwise.min_width = MIN_W;
- fsize->stepwise.min_height = MIN_H;
- fsize->stepwise.max_width = MAX_W;
- fsize->stepwise.max_height = MAX_H;
-
- get_alignment(fsize->pixel_format,
- &fsize->stepwise.step_width,
- &fsize->stepwise.step_height);
- return 0;
-}
-
-static int vidioc_g_fmt(struct vim2m_ctx *ctx, struct v4l2_format *f)
-{
- struct vb2_queue *vq;
- struct vim2m_q_data *q_data;
-
- vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
- if (!vq)
- return -EINVAL;
-
- q_data = get_q_data(ctx, f->type);
- if (!q_data)
- return -EINVAL;
-
- f->fmt.pix.width = q_data->width;
- f->fmt.pix.height = q_data->height;
- f->fmt.pix.field = V4L2_FIELD_NONE;
- f->fmt.pix.pixelformat = q_data->fmt->fourcc;
- f->fmt.pix.bytesperline = (q_data->width * q_data->fmt->depth) >> 3;
- f->fmt.pix.sizeimage = q_data->sizeimage;
- f->fmt.pix.colorspace = ctx->colorspace;
- f->fmt.pix.xfer_func = ctx->xfer_func;
- f->fmt.pix.ycbcr_enc = ctx->ycbcr_enc;
- f->fmt.pix.quantization = ctx->quant;
-
- return 0;
-}
-
-static int vidioc_g_fmt_vid_out(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- return vidioc_g_fmt(file2ctx(file), f);
-}
-
-static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- return vidioc_g_fmt(file2ctx(file), f);
-}
-
-static int vidioc_try_fmt(struct v4l2_format *f, struct vim2m_fmt *fmt)
-{
- int walign, halign;
- /*
- * V4L2 specification specifies the driver corrects the
- * format struct if any of the dimensions is unsupported
- */
- if (f->fmt.pix.height < MIN_H)
- f->fmt.pix.height = MIN_H;
- else if (f->fmt.pix.height > MAX_H)
- f->fmt.pix.height = MAX_H;
-
- if (f->fmt.pix.width < MIN_W)
- f->fmt.pix.width = MIN_W;
- else if (f->fmt.pix.width > MAX_W)
- f->fmt.pix.width = MAX_W;
-
- get_alignment(f->fmt.pix.pixelformat, &walign, &halign);
- f->fmt.pix.width &= ~(walign - 1);
- f->fmt.pix.height &= ~(halign - 1);
- f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3;
- f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
- f->fmt.pix.field = V4L2_FIELD_NONE;
-
- return 0;
-}
-
-static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct vim2m_fmt *fmt;
- struct vim2m_ctx *ctx = file2ctx(file);
-
- fmt = find_format(f->fmt.pix.pixelformat);
- if (!fmt) {
- f->fmt.pix.pixelformat = formats[0].fourcc;
- fmt = find_format(f->fmt.pix.pixelformat);
- }
- if (!(fmt->types & MEM2MEM_CAPTURE)) {
- v4l2_err(&ctx->dev->v4l2_dev,
- "Fourcc format (0x%08x) invalid.\n",
- f->fmt.pix.pixelformat);
- return -EINVAL;
- }
- f->fmt.pix.colorspace = ctx->colorspace;
- f->fmt.pix.xfer_func = ctx->xfer_func;
- f->fmt.pix.ycbcr_enc = ctx->ycbcr_enc;
- f->fmt.pix.quantization = ctx->quant;
-
- return vidioc_try_fmt(f, fmt);
-}
-
-static int vidioc_try_fmt_vid_out(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct vim2m_fmt *fmt;
- struct vim2m_ctx *ctx = file2ctx(file);
-
- fmt = find_format(f->fmt.pix.pixelformat);
- if (!fmt) {
- f->fmt.pix.pixelformat = formats[0].fourcc;
- fmt = find_format(f->fmt.pix.pixelformat);
- }
- if (!(fmt->types & MEM2MEM_OUTPUT)) {
- v4l2_err(&ctx->dev->v4l2_dev,
- "Fourcc format (0x%08x) invalid.\n",
- f->fmt.pix.pixelformat);
- return -EINVAL;
- }
- if (!f->fmt.pix.colorspace)
- f->fmt.pix.colorspace = V4L2_COLORSPACE_REC709;
-
- return vidioc_try_fmt(f, fmt);
-}
-
-static int vidioc_s_fmt(struct vim2m_ctx *ctx, struct v4l2_format *f)
-{
- struct vim2m_q_data *q_data;
- struct vb2_queue *vq;
-
- vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
- if (!vq)
- return -EINVAL;
-
- q_data = get_q_data(ctx, f->type);
- if (!q_data)
- return -EINVAL;
-
- if (vb2_is_busy(vq)) {
- v4l2_err(&ctx->dev->v4l2_dev, "%s queue busy\n", __func__);
- return -EBUSY;
- }
-
- q_data->fmt = find_format(f->fmt.pix.pixelformat);
- q_data->width = f->fmt.pix.width;
- q_data->height = f->fmt.pix.height;
- q_data->sizeimage = q_data->width * q_data->height
- * q_data->fmt->depth >> 3;
-
- dprintk(ctx->dev, 1,
- "Format for type %s: %dx%d (%d bpp), fmt: %c%c%c%c\n",
- type_name(f->type), q_data->width, q_data->height,
- q_data->fmt->depth,
- (q_data->fmt->fourcc & 0xff),
- (q_data->fmt->fourcc >> 8) & 0xff,
- (q_data->fmt->fourcc >> 16) & 0xff,
- (q_data->fmt->fourcc >> 24) & 0xff);
-
- return 0;
-}
-
-static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- int ret;
-
- ret = vidioc_try_fmt_vid_cap(file, priv, f);
- if (ret)
- return ret;
-
- return vidioc_s_fmt(file2ctx(file), f);
-}
-
-static int vidioc_s_fmt_vid_out(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct vim2m_ctx *ctx = file2ctx(file);
- int ret;
-
- ret = vidioc_try_fmt_vid_out(file, priv, f);
- if (ret)
- return ret;
-
- ret = vidioc_s_fmt(file2ctx(file), f);
- if (!ret) {
- ctx->colorspace = f->fmt.pix.colorspace;
- ctx->xfer_func = f->fmt.pix.xfer_func;
- ctx->ycbcr_enc = f->fmt.pix.ycbcr_enc;
- ctx->quant = f->fmt.pix.quantization;
- }
- return ret;
-}
-
-static int vim2m_s_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct vim2m_ctx *ctx =
- container_of(ctrl->handler, struct vim2m_ctx, hdl);
-
- switch (ctrl->id) {
- case V4L2_CID_HFLIP:
- if (ctrl->val)
- ctx->mode |= MEM2MEM_HFLIP;
- else
- ctx->mode &= ~MEM2MEM_HFLIP;
- break;
-
- case V4L2_CID_VFLIP:
- if (ctrl->val)
- ctx->mode |= MEM2MEM_VFLIP;
- else
- ctx->mode &= ~MEM2MEM_VFLIP;
- break;
-
- case V4L2_CID_TRANS_TIME_MSEC:
- ctx->transtime = ctrl->val;
- if (ctx->transtime < 1)
- ctx->transtime = 1;
- break;
-
- case V4L2_CID_TRANS_NUM_BUFS:
- ctx->translen = ctrl->val;
- break;
-
- default:
- v4l2_err(&ctx->dev->v4l2_dev, "Invalid control\n");
- return -EINVAL;
- }
-
- return 0;
-}
-
-static const struct v4l2_ctrl_ops vim2m_ctrl_ops = {
- .s_ctrl = vim2m_s_ctrl,
-};
-
-static const struct v4l2_ioctl_ops vim2m_ioctl_ops = {
- .vidioc_querycap = vidioc_querycap,
-
- .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
- .vidioc_enum_framesizes = vidioc_enum_framesizes,
- .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
- .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
- .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
-
- .vidioc_enum_fmt_vid_out = vidioc_enum_fmt_vid_out,
- .vidioc_g_fmt_vid_out = vidioc_g_fmt_vid_out,
- .vidioc_try_fmt_vid_out = vidioc_try_fmt_vid_out,
- .vidioc_s_fmt_vid_out = vidioc_s_fmt_vid_out,
-
- .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs,
- .vidioc_querybuf = v4l2_m2m_ioctl_querybuf,
- .vidioc_qbuf = v4l2_m2m_ioctl_qbuf,
- .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf,
- .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf,
- .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs,
- .vidioc_expbuf = v4l2_m2m_ioctl_expbuf,
-
- .vidioc_streamon = v4l2_m2m_ioctl_streamon,
- .vidioc_streamoff = v4l2_m2m_ioctl_streamoff,
-
- .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
- .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
-};
-
-/*
- * Queue operations
- */
-
-static int vim2m_queue_setup(struct vb2_queue *vq,
- unsigned int *nbuffers,
- unsigned int *nplanes,
- unsigned int sizes[],
- struct device *alloc_devs[])
-{
- struct vim2m_ctx *ctx = vb2_get_drv_priv(vq);
- struct vim2m_q_data *q_data;
- unsigned int size, count = *nbuffers;
-
- q_data = get_q_data(ctx, vq->type);
- if (!q_data)
- return -EINVAL;
-
- size = q_data->width * q_data->height * q_data->fmt->depth >> 3;
-
- while (size * count > MEM2MEM_VID_MEM_LIMIT)
- (count)--;
- *nbuffers = count;
-
- if (*nplanes)
- return sizes[0] < size ? -EINVAL : 0;
-
- *nplanes = 1;
- sizes[0] = size;
-
- dprintk(ctx->dev, 1, "%s: get %d buffer(s) of size %d each.\n",
- type_name(vq->type), count, size);
-
- return 0;
-}
-
-static int vim2m_buf_out_validate(struct vb2_buffer *vb)
-{
- struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
- struct vim2m_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
-
- if (vbuf->field == V4L2_FIELD_ANY)
- vbuf->field = V4L2_FIELD_NONE;
- if (vbuf->field != V4L2_FIELD_NONE) {
- dprintk(ctx->dev, 1, "%s field isn't supported\n", __func__);
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int vim2m_buf_prepare(struct vb2_buffer *vb)
-{
- struct vim2m_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
- struct vim2m_q_data *q_data;
-
- dprintk(ctx->dev, 2, "type: %s\n", type_name(vb->vb2_queue->type));
-
- q_data = get_q_data(ctx, vb->vb2_queue->type);
- if (!q_data)
- return -EINVAL;
- if (vb2_plane_size(vb, 0) < q_data->sizeimage) {
- dprintk(ctx->dev, 1,
- "%s data will not fit into plane (%lu < %lu)\n",
- __func__, vb2_plane_size(vb, 0),
- (long)q_data->sizeimage);
- return -EINVAL;
- }
-
- vb2_set_plane_payload(vb, 0, q_data->sizeimage);
-
- return 0;
-}
-
-static void vim2m_buf_queue(struct vb2_buffer *vb)
-{
- struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
- struct vim2m_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
-
- v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf);
-}
-
-static int vim2m_start_streaming(struct vb2_queue *q, unsigned int count)
-{
- struct vim2m_ctx *ctx = vb2_get_drv_priv(q);
- struct vim2m_q_data *q_data = get_q_data(ctx, q->type);
-
- if (!q_data)
- return -EINVAL;
-
- if (V4L2_TYPE_IS_OUTPUT(q->type))
- ctx->aborting = 0;
-
- q_data->sequence = 0;
- return 0;
-}
-
-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);
-
- for (;;) {
- if (V4L2_TYPE_IS_OUTPUT(q->type))
- vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
- else
- vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
- if (!vbuf)
- 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);
- }
-}
-
-static void vim2m_buf_request_complete(struct vb2_buffer *vb)
-{
- struct vim2m_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
-
- v4l2_ctrl_request_complete(vb->req_obj.req, &ctx->hdl);
-}
-
-static const struct vb2_ops vim2m_qops = {
- .queue_setup = vim2m_queue_setup,
- .buf_out_validate = vim2m_buf_out_validate,
- .buf_prepare = vim2m_buf_prepare,
- .buf_queue = vim2m_buf_queue,
- .start_streaming = vim2m_start_streaming,
- .stop_streaming = vim2m_stop_streaming,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
- .buf_request_complete = vim2m_buf_request_complete,
-};
-
-static int queue_init(void *priv, struct vb2_queue *src_vq,
- struct vb2_queue *dst_vq)
-{
- struct vim2m_ctx *ctx = priv;
- int ret;
-
- src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
- src_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
- src_vq->drv_priv = ctx;
- src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
- src_vq->ops = &vim2m_qops;
- src_vq->mem_ops = &vb2_vmalloc_memops;
- src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
- src_vq->lock = &ctx->vb_mutex;
- src_vq->supports_requests = true;
-
- ret = vb2_queue_init(src_vq);
- if (ret)
- return ret;
-
- dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- dst_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
- dst_vq->drv_priv = ctx;
- dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
- dst_vq->ops = &vim2m_qops;
- dst_vq->mem_ops = &vb2_vmalloc_memops;
- dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
- dst_vq->lock = &ctx->vb_mutex;
-
- return vb2_queue_init(dst_vq);
-}
-
-static struct v4l2_ctrl_config vim2m_ctrl_trans_time_msec = {
- .ops = &vim2m_ctrl_ops,
- .id = V4L2_CID_TRANS_TIME_MSEC,
- .name = "Transaction Time (msec)",
- .type = V4L2_CTRL_TYPE_INTEGER,
- .min = 1,
- .max = 10001,
- .step = 1,
-};
-
-static const struct v4l2_ctrl_config vim2m_ctrl_trans_num_bufs = {
- .ops = &vim2m_ctrl_ops,
- .id = V4L2_CID_TRANS_NUM_BUFS,
- .name = "Buffers Per Transaction",
- .type = V4L2_CTRL_TYPE_INTEGER,
- .def = 1,
- .min = 1,
- .max = MEM2MEM_DEF_NUM_BUFS,
- .step = 1,
-};
-
-/*
- * File operations
- */
-static int vim2m_open(struct file *file)
-{
- struct vim2m_dev *dev = video_drvdata(file);
- struct vim2m_ctx *ctx = NULL;
- struct v4l2_ctrl_handler *hdl;
- int rc = 0;
-
- if (mutex_lock_interruptible(&dev->dev_mutex))
- return -ERESTARTSYS;
- ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
- if (!ctx) {
- rc = -ENOMEM;
- goto open_unlock;
- }
-
- v4l2_fh_init(&ctx->fh, video_devdata(file));
- file->private_data = &ctx->fh;
- ctx->dev = dev;
- hdl = &ctx->hdl;
- v4l2_ctrl_handler_init(hdl, 4);
- v4l2_ctrl_new_std(hdl, &vim2m_ctrl_ops, V4L2_CID_HFLIP, 0, 1, 1, 0);
- v4l2_ctrl_new_std(hdl, &vim2m_ctrl_ops, V4L2_CID_VFLIP, 0, 1, 1, 0);
-
- vim2m_ctrl_trans_time_msec.def = default_transtime;
- v4l2_ctrl_new_custom(hdl, &vim2m_ctrl_trans_time_msec, NULL);
- v4l2_ctrl_new_custom(hdl, &vim2m_ctrl_trans_num_bufs, NULL);
- if (hdl->error) {
- rc = hdl->error;
- v4l2_ctrl_handler_free(hdl);
- kfree(ctx);
- goto open_unlock;
- }
- ctx->fh.ctrl_handler = hdl;
- v4l2_ctrl_handler_setup(hdl);
-
- ctx->q_data[V4L2_M2M_SRC].fmt = &formats[0];
- ctx->q_data[V4L2_M2M_SRC].width = 640;
- ctx->q_data[V4L2_M2M_SRC].height = 480;
- ctx->q_data[V4L2_M2M_SRC].sizeimage =
- ctx->q_data[V4L2_M2M_SRC].width *
- ctx->q_data[V4L2_M2M_SRC].height *
- (ctx->q_data[V4L2_M2M_SRC].fmt->depth >> 3);
- ctx->q_data[V4L2_M2M_DST] = ctx->q_data[V4L2_M2M_SRC];
- ctx->colorspace = V4L2_COLORSPACE_REC709;
-
- 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)) {
- rc = PTR_ERR(ctx->fh.m2m_ctx);
-
- v4l2_ctrl_handler_free(hdl);
- v4l2_fh_exit(&ctx->fh);
- kfree(ctx);
- goto open_unlock;
- }
-
- v4l2_fh_add(&ctx->fh);
- atomic_inc(&dev->num_inst);
-
- dprintk(dev, 1, "Created instance: %p, m2m_ctx: %p\n",
- ctx, ctx->fh.m2m_ctx);
-
-open_unlock:
- mutex_unlock(&dev->dev_mutex);
- return rc;
-}
-
-static int vim2m_release(struct file *file)
-{
- struct vim2m_dev *dev = video_drvdata(file);
- struct vim2m_ctx *ctx = file2ctx(file);
-
- dprintk(dev, 1, "Releasing instance %p\n", ctx);
-
- v4l2_fh_del(&ctx->fh);
- v4l2_fh_exit(&ctx->fh);
- v4l2_ctrl_handler_free(&ctx->hdl);
- mutex_lock(&dev->dev_mutex);
- v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
- mutex_unlock(&dev->dev_mutex);
- kfree(ctx);
-
- atomic_dec(&dev->num_inst);
-
- return 0;
-}
-
-static void vim2m_device_release(struct video_device *vdev)
-{
- struct vim2m_dev *dev = container_of(vdev, struct vim2m_dev, vfd);
-
- v4l2_device_unregister(&dev->v4l2_dev);
- v4l2_m2m_release(dev->m2m_dev);
-#ifdef CONFIG_MEDIA_CONTROLLER
- media_device_cleanup(&dev->mdev);
-#endif
- kfree(dev);
-}
-
-static const struct v4l2_file_operations vim2m_fops = {
- .owner = THIS_MODULE,
- .open = vim2m_open,
- .release = vim2m_release,
- .poll = v4l2_m2m_fop_poll,
- .unlocked_ioctl = video_ioctl2,
- .mmap = v4l2_m2m_fop_mmap,
-};
-
-static const struct video_device vim2m_videodev = {
- .name = MEM2MEM_NAME,
- .vfl_dir = VFL_DIR_M2M,
- .fops = &vim2m_fops,
- .ioctl_ops = &vim2m_ioctl_ops,
- .minor = -1,
- .release = vim2m_device_release,
- .device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING,
-};
-
-static const struct v4l2_m2m_ops m2m_ops = {
- .device_run = device_run,
- .job_ready = job_ready,
- .job_abort = job_abort,
-};
-
-static const struct media_device_ops m2m_media_ops = {
- .req_validate = vb2_request_validate,
- .req_queue = v4l2_m2m_request_queue,
-};
-
-static int vim2m_probe(struct platform_device *pdev)
-{
- struct vim2m_dev *dev;
- struct video_device *vfd;
- int ret;
-
- dev = kzalloc(sizeof(*dev), GFP_KERNEL);
- if (!dev)
- return -ENOMEM;
-
- ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
- if (ret)
- goto error_free;
-
- atomic_set(&dev->num_inst, 0);
- mutex_init(&dev->dev_mutex);
-
- dev->vfd = vim2m_videodev;
- vfd = &dev->vfd;
- vfd->lock = &dev->dev_mutex;
- vfd->v4l2_dev = &dev->v4l2_dev;
-
- ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0);
- if (ret) {
- v4l2_err(&dev->v4l2_dev, "Failed to register video device\n");
- goto error_v4l2;
- }
-
- video_set_drvdata(vfd, dev);
- v4l2_info(&dev->v4l2_dev,
- "Device registered as /dev/video%d\n", vfd->num);
-
- platform_set_drvdata(pdev, dev);
-
- dev->m2m_dev = v4l2_m2m_init(&m2m_ops);
- if (IS_ERR(dev->m2m_dev)) {
- v4l2_err(&dev->v4l2_dev, "Failed to init mem2mem device\n");
- ret = PTR_ERR(dev->m2m_dev);
- dev->m2m_dev = NULL;
- goto error_dev;
- }
-
-#ifdef CONFIG_MEDIA_CONTROLLER
- dev->mdev.dev = &pdev->dev;
- strscpy(dev->mdev.model, "vim2m", sizeof(dev->mdev.model));
- strscpy(dev->mdev.bus_info, "platform:vim2m",
- sizeof(dev->mdev.bus_info));
- media_device_init(&dev->mdev);
- dev->mdev.ops = &m2m_media_ops;
- dev->v4l2_dev.mdev = &dev->mdev;
-
- ret = v4l2_m2m_register_media_controller(dev->m2m_dev, vfd,
- MEDIA_ENT_F_PROC_VIDEO_SCALER);
- if (ret) {
- v4l2_err(&dev->v4l2_dev, "Failed to init mem2mem media controller\n");
- goto error_dev;
- }
-
- ret = media_device_register(&dev->mdev);
- if (ret) {
- v4l2_err(&dev->v4l2_dev, "Failed to register mem2mem media device\n");
- goto error_m2m_mc;
- }
-#endif
- return 0;
-
-#ifdef CONFIG_MEDIA_CONTROLLER
-error_m2m_mc:
- v4l2_m2m_unregister_media_controller(dev->m2m_dev);
-#endif
-error_dev:
- video_unregister_device(&dev->vfd);
- /* vim2m_device_release called by video_unregister_device to release various objects */
- return ret;
-error_v4l2:
- v4l2_device_unregister(&dev->v4l2_dev);
-error_free:
- kfree(dev);
-
- return ret;
-}
-
-static int vim2m_remove(struct platform_device *pdev)
-{
- struct vim2m_dev *dev = platform_get_drvdata(pdev);
-
- v4l2_info(&dev->v4l2_dev, "Removing " MEM2MEM_NAME);
-
-#ifdef CONFIG_MEDIA_CONTROLLER
- media_device_unregister(&dev->mdev);
- v4l2_m2m_unregister_media_controller(dev->m2m_dev);
-#endif
- video_unregister_device(&dev->vfd);
-
- return 0;
-}
-
-static struct platform_driver vim2m_pdrv = {
- .probe = vim2m_probe,
- .remove = vim2m_remove,
- .driver = {
- .name = MEM2MEM_NAME,
- },
-};
-
-static void __exit vim2m_exit(void)
-{
- platform_driver_unregister(&vim2m_pdrv);
- platform_device_unregister(&vim2m_pdev);
-}
-
-static int __init vim2m_init(void)
-{
- int ret;
-
- ret = platform_device_register(&vim2m_pdev);
- if (ret)
- return ret;
-
- ret = platform_driver_register(&vim2m_pdrv);
- if (ret)
- platform_device_unregister(&vim2m_pdev);
-
- return ret;
-}
-
-module_init(vim2m_init);
-module_exit(vim2m_exit);
diff --git a/drivers/media/platform/vimc/Kconfig b/drivers/media/platform/vimc/Kconfig
deleted file mode 100644
index bd221d3..0000000
--- a/drivers/media/platform/vimc/Kconfig
+++ /dev/null
@@ -1,15 +0,0 @@
-# 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
- select VIDEOBUF2_VMALLOC
- select VIDEO_V4L2_TPG
- help
- Skeleton driver for Virtual Media Controller
-
- This driver can be compared to the vivid driver for emulating
- a media node that exposes a complex media topology. The topology
- is hard coded for now but is meant to be highly configurable in
- the future.
-
- When in doubt, say N.
diff --git a/drivers/media/platform/vimc/Makefile b/drivers/media/platform/vimc/Makefile
deleted file mode 100644
index 96d06f0..0000000
--- a/drivers/media/platform/vimc/Makefile
+++ /dev/null
@@ -1,5 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0
-vimc-y := vimc-core.o vimc-common.o vimc-streamer.o
-
-obj-$(CONFIG_VIDEO_VIMC) += vimc.o vimc-capture.o vimc-debayer.o \
- vimc-scaler.o vimc-sensor.o
diff --git a/drivers/media/platform/vimc/vimc-capture.c b/drivers/media/platform/vimc/vimc-capture.c
deleted file mode 100644
index 1d56b91..0000000
--- a/drivers/media/platform/vimc/vimc-capture.c
+++ /dev/null
@@ -1,541 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * vimc-capture.c Virtual Media Controller Driver
- *
- * Copyright (C) 2015-2017 Helen Koike <helen.fornazier@gmail.com>
- */
-
-#include <linux/component.h>
-#include <linux/module.h>
-#include <linux/mod_devicetable.h>
-#include <linux/platform_device.h>
-#include <media/v4l2-ioctl.h>
-#include <media/videobuf2-core.h>
-#include <media/videobuf2-vmalloc.h>
-
-#include "vimc-common.h"
-#include "vimc-streamer.h"
-
-#define VIMC_CAP_DRV_NAME "vimc-capture"
-
-struct vimc_cap_device {
- struct vimc_ent_device ved;
- struct video_device vdev;
- struct device *dev;
- struct v4l2_pix_format format;
- struct vb2_queue queue;
- struct list_head buf_list;
- /*
- * NOTE: in a real driver, a spin lock must be used to access the
- * queue because the frames are generated from a hardware interruption
- * and the isr is not allowed to sleep.
- * Even if it is not necessary a spinlock in the vimc driver, we
- * use it here as a code reference
- */
- spinlock_t qlock;
- struct mutex lock;
- u32 sequence;
- struct vimc_stream stream;
-};
-
-static const struct v4l2_pix_format fmt_default = {
- .width = 640,
- .height = 480,
- .pixelformat = V4L2_PIX_FMT_RGB24,
- .field = V4L2_FIELD_NONE,
- .colorspace = V4L2_COLORSPACE_DEFAULT,
-};
-
-struct vimc_cap_buffer {
- /*
- * struct vb2_v4l2_buffer must be the first element
- * the videobuf2 framework will allocate this struct based on
- * buf_struct_size and use the first sizeof(struct vb2_buffer) bytes of
- * memory as a vb2_buffer
- */
- struct vb2_v4l2_buffer vb2;
- struct list_head list;
-};
-
-static int vimc_cap_querycap(struct file *file, void *priv,
- struct v4l2_capability *cap)
-{
- strscpy(cap->driver, VIMC_PDEV_NAME, sizeof(cap->driver));
- strscpy(cap->card, KBUILD_MODNAME, sizeof(cap->card));
- snprintf(cap->bus_info, sizeof(cap->bus_info),
- "platform:%s", VIMC_PDEV_NAME);
-
- return 0;
-}
-
-static void vimc_cap_get_format(struct vimc_ent_device *ved,
- struct v4l2_pix_format *fmt)
-{
- struct vimc_cap_device *vcap = container_of(ved, struct vimc_cap_device,
- ved);
-
- *fmt = vcap->format;
-}
-
-static int vimc_cap_g_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct vimc_cap_device *vcap = video_drvdata(file);
-
- f->fmt.pix = vcap->format;
-
- return 0;
-}
-
-static int vimc_cap_try_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct v4l2_pix_format *format = &f->fmt.pix;
- const struct vimc_pix_map *vpix;
-
- format->width = clamp_t(u32, format->width, VIMC_FRAME_MIN_WIDTH,
- VIMC_FRAME_MAX_WIDTH) & ~1;
- format->height = clamp_t(u32, format->height, VIMC_FRAME_MIN_HEIGHT,
- VIMC_FRAME_MAX_HEIGHT) & ~1;
-
- /* Don't accept a pixelformat that is not on the table */
- vpix = vimc_pix_map_by_pixelformat(format->pixelformat);
- if (!vpix) {
- format->pixelformat = fmt_default.pixelformat;
- vpix = vimc_pix_map_by_pixelformat(format->pixelformat);
- }
- /* TODO: Add support for custom bytesperline values */
- format->bytesperline = format->width * vpix->bpp;
- format->sizeimage = format->bytesperline * format->height;
-
- if (format->field == V4L2_FIELD_ANY)
- format->field = fmt_default.field;
-
- vimc_colorimetry_clamp(format);
-
- return 0;
-}
-
-static int vimc_cap_s_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct vimc_cap_device *vcap = video_drvdata(file);
- int ret;
-
- /* Do not change the format while stream is on */
- if (vb2_is_busy(&vcap->queue))
- return -EBUSY;
-
- ret = vimc_cap_try_fmt_vid_cap(file, priv, f);
- if (ret)
- return ret;
-
- dev_dbg(vcap->dev, "%s: format update: "
- "old:%dx%d (0x%x, %d, %d, %d, %d) "
- "new:%dx%d (0x%x, %d, %d, %d, %d)\n", vcap->vdev.name,
- /* old */
- vcap->format.width, vcap->format.height,
- vcap->format.pixelformat, vcap->format.colorspace,
- vcap->format.quantization, vcap->format.xfer_func,
- vcap->format.ycbcr_enc,
- /* new */
- f->fmt.pix.width, f->fmt.pix.height,
- f->fmt.pix.pixelformat, f->fmt.pix.colorspace,
- f->fmt.pix.quantization, f->fmt.pix.xfer_func,
- f->fmt.pix.ycbcr_enc);
-
- vcap->format = f->fmt.pix;
-
- return 0;
-}
-
-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);
-
- if (!vpix)
- return -EINVAL;
-
- f->pixelformat = vpix->pixelformat;
-
- return 0;
-}
-
-static int vimc_cap_enum_framesizes(struct file *file, void *fh,
- struct v4l2_frmsizeenum *fsize)
-{
- const struct vimc_pix_map *vpix;
-
- if (fsize->index)
- return -EINVAL;
-
- /* Only accept code in the pix map table */
- vpix = vimc_pix_map_by_code(fsize->pixel_format);
- if (!vpix)
- return -EINVAL;
-
- fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS;
- fsize->stepwise.min_width = VIMC_FRAME_MIN_WIDTH;
- fsize->stepwise.max_width = VIMC_FRAME_MAX_WIDTH;
- fsize->stepwise.min_height = VIMC_FRAME_MIN_HEIGHT;
- fsize->stepwise.max_height = VIMC_FRAME_MAX_HEIGHT;
- fsize->stepwise.step_width = 1;
- fsize->stepwise.step_height = 1;
-
- return 0;
-}
-
-static const struct v4l2_file_operations vimc_cap_fops = {
- .owner = THIS_MODULE,
- .open = v4l2_fh_open,
- .release = vb2_fop_release,
- .read = vb2_fop_read,
- .poll = vb2_fop_poll,
- .unlocked_ioctl = video_ioctl2,
- .mmap = vb2_fop_mmap,
-};
-
-static const struct v4l2_ioctl_ops vimc_cap_ioctl_ops = {
- .vidioc_querycap = vimc_cap_querycap,
-
- .vidioc_g_fmt_vid_cap = vimc_cap_g_fmt_vid_cap,
- .vidioc_s_fmt_vid_cap = vimc_cap_s_fmt_vid_cap,
- .vidioc_try_fmt_vid_cap = vimc_cap_try_fmt_vid_cap,
- .vidioc_enum_fmt_vid_cap = vimc_cap_enum_fmt_vid_cap,
- .vidioc_enum_framesizes = vimc_cap_enum_framesizes,
-
- .vidioc_reqbufs = vb2_ioctl_reqbufs,
- .vidioc_create_bufs = vb2_ioctl_create_bufs,
- .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
- .vidioc_querybuf = vb2_ioctl_querybuf,
- .vidioc_qbuf = vb2_ioctl_qbuf,
- .vidioc_dqbuf = vb2_ioctl_dqbuf,
- .vidioc_expbuf = vb2_ioctl_expbuf,
- .vidioc_streamon = vb2_ioctl_streamon,
- .vidioc_streamoff = vb2_ioctl_streamoff,
-};
-
-static void vimc_cap_return_all_buffers(struct vimc_cap_device *vcap,
- enum vb2_buffer_state state)
-{
- struct vimc_cap_buffer *vbuf, *node;
-
- spin_lock(&vcap->qlock);
-
- list_for_each_entry_safe(vbuf, node, &vcap->buf_list, list) {
- list_del(&vbuf->list);
- vb2_buffer_done(&vbuf->vb2.vb2_buf, state);
- }
-
- spin_unlock(&vcap->qlock);
-}
-
-static int vimc_cap_start_streaming(struct vb2_queue *vq, unsigned int count)
-{
- struct vimc_cap_device *vcap = vb2_get_drv_priv(vq);
- struct media_entity *entity = &vcap->vdev.entity;
- int ret;
-
- vcap->sequence = 0;
-
- /* Start the media pipeline */
- ret = media_pipeline_start(entity, &vcap->stream.pipe);
- if (ret) {
- vimc_cap_return_all_buffers(vcap, VB2_BUF_STATE_QUEUED);
- return ret;
- }
-
- ret = vimc_streamer_s_stream(&vcap->stream, &vcap->ved, 1);
- if (ret) {
- media_pipeline_stop(entity);
- vimc_cap_return_all_buffers(vcap, VB2_BUF_STATE_QUEUED);
- return ret;
- }
-
- return 0;
-}
-
-/*
- * Stop the stream engine. Any remaining buffers in the stream queue are
- * dequeued and passed on to the vb2 framework marked as STATE_ERROR.
- */
-static void vimc_cap_stop_streaming(struct vb2_queue *vq)
-{
- struct vimc_cap_device *vcap = vb2_get_drv_priv(vq);
-
- vimc_streamer_s_stream(&vcap->stream, &vcap->ved, 0);
-
- /* Stop the media pipeline */
- media_pipeline_stop(&vcap->vdev.entity);
-
- /* Release all active buffers */
- vimc_cap_return_all_buffers(vcap, VB2_BUF_STATE_ERROR);
-}
-
-static void vimc_cap_buf_queue(struct vb2_buffer *vb2_buf)
-{
- struct vimc_cap_device *vcap = vb2_get_drv_priv(vb2_buf->vb2_queue);
- struct vimc_cap_buffer *buf = container_of(vb2_buf,
- struct vimc_cap_buffer,
- vb2.vb2_buf);
-
- spin_lock(&vcap->qlock);
- list_add_tail(&buf->list, &vcap->buf_list);
- spin_unlock(&vcap->qlock);
-}
-
-static int vimc_cap_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers,
- unsigned int *nplanes, unsigned int sizes[],
- struct device *alloc_devs[])
-{
- struct vimc_cap_device *vcap = vb2_get_drv_priv(vq);
-
- if (*nplanes)
- return sizes[0] < vcap->format.sizeimage ? -EINVAL : 0;
- /* We don't support multiplanes for now */
- *nplanes = 1;
- sizes[0] = vcap->format.sizeimage;
-
- return 0;
-}
-
-static int vimc_cap_buffer_prepare(struct vb2_buffer *vb)
-{
- struct vimc_cap_device *vcap = vb2_get_drv_priv(vb->vb2_queue);
- unsigned long size = vcap->format.sizeimage;
-
- if (vb2_plane_size(vb, 0) < size) {
- dev_err(vcap->dev, "%s: buffer too small (%lu < %lu)\n",
- vcap->vdev.name, vb2_plane_size(vb, 0), size);
- return -EINVAL;
- }
- return 0;
-}
-
-static const struct vb2_ops vimc_cap_qops = {
- .start_streaming = vimc_cap_start_streaming,
- .stop_streaming = vimc_cap_stop_streaming,
- .buf_queue = vimc_cap_buf_queue,
- .queue_setup = vimc_cap_queue_setup,
- .buf_prepare = vimc_cap_buffer_prepare,
- /*
- * Since q->lock is set we can use the standard
- * vb2_ops_wait_prepare/finish helper functions.
- */
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
-};
-
-static const struct media_entity_operations vimc_cap_mops = {
- .link_validate = vimc_link_validate,
-};
-
-static void vimc_cap_release(struct video_device *vdev)
-{
- struct vimc_cap_device *vcap =
- container_of(vdev, struct vimc_cap_device, vdev);
-
- vimc_pads_cleanup(vcap->ved.pads);
- kfree(vcap);
-}
-
-static void vimc_cap_comp_unbind(struct device *comp, struct device *master,
- void *master_data)
-{
- struct vimc_ent_device *ved = dev_get_drvdata(comp);
- struct vimc_cap_device *vcap = container_of(ved, struct vimc_cap_device,
- ved);
-
- vb2_queue_release(&vcap->queue);
- media_entity_cleanup(ved->ent);
- video_unregister_device(&vcap->vdev);
-}
-
-static void *vimc_cap_process_frame(struct vimc_ent_device *ved,
- const void *frame)
-{
- struct vimc_cap_device *vcap = container_of(ved, struct vimc_cap_device,
- ved);
- struct vimc_cap_buffer *vimc_buf;
- void *vbuf;
-
- spin_lock(&vcap->qlock);
-
- /* Get the first entry of the list */
- vimc_buf = list_first_entry_or_null(&vcap->buf_list,
- typeof(*vimc_buf), list);
- if (!vimc_buf) {
- spin_unlock(&vcap->qlock);
- return ERR_PTR(-EAGAIN);
- }
-
- /* Remove this entry from the list */
- list_del(&vimc_buf->list);
-
- spin_unlock(&vcap->qlock);
-
- /* Fill the buffer */
- vimc_buf->vb2.vb2_buf.timestamp = ktime_get_ns();
- vimc_buf->vb2.sequence = vcap->sequence++;
- vimc_buf->vb2.field = vcap->format.field;
-
- vbuf = vb2_plane_vaddr(&vimc_buf->vb2.vb2_buf, 0);
-
- memcpy(vbuf, frame, vcap->format.sizeimage);
-
- /* Set it as ready */
- vb2_set_plane_payload(&vimc_buf->vb2.vb2_buf, 0,
- vcap->format.sizeimage);
- vb2_buffer_done(&vimc_buf->vb2.vb2_buf, VB2_BUF_STATE_DONE);
- return NULL;
-}
-
-static int vimc_cap_comp_bind(struct device *comp, struct device *master,
- void *master_data)
-{
- struct v4l2_device *v4l2_dev = master_data;
- struct vimc_platform_data *pdata = comp->platform_data;
- const struct vimc_pix_map *vpix;
- struct vimc_cap_device *vcap;
- struct video_device *vdev;
- struct vb2_queue *q;
- int ret;
-
- /* Allocate the vimc_cap_device struct */
- vcap = kzalloc(sizeof(*vcap), GFP_KERNEL);
- if (!vcap)
- return -ENOMEM;
-
- /* Allocate the pads */
- vcap->ved.pads =
- vimc_pads_init(1, (const unsigned long[1]) {MEDIA_PAD_FL_SINK});
- if (IS_ERR(vcap->ved.pads)) {
- ret = PTR_ERR(vcap->ved.pads);
- goto err_free_vcap;
- }
-
- /* Initialize the media entity */
- vcap->vdev.entity.name = pdata->entity_name;
- vcap->vdev.entity.function = MEDIA_ENT_F_IO_V4L;
- ret = media_entity_pads_init(&vcap->vdev.entity,
- 1, vcap->ved.pads);
- if (ret)
- goto err_clean_pads;
-
- /* Initialize the lock */
- mutex_init(&vcap->lock);
-
- /* Initialize the vb2 queue */
- q = &vcap->queue;
- q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_USERPTR;
- q->drv_priv = vcap;
- q->buf_struct_size = sizeof(struct vimc_cap_buffer);
- q->ops = &vimc_cap_qops;
- q->mem_ops = &vb2_vmalloc_memops;
- q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
- q->min_buffers_needed = 2;
- q->lock = &vcap->lock;
-
- ret = vb2_queue_init(q);
- if (ret) {
- dev_err(comp, "%s: vb2 queue init failed (err=%d)\n",
- pdata->entity_name, ret);
- goto err_clean_m_ent;
- }
-
- /* Initialize buffer list and its lock */
- INIT_LIST_HEAD(&vcap->buf_list);
- spin_lock_init(&vcap->qlock);
-
- /* Set default frame format */
- vcap->format = fmt_default;
- vpix = vimc_pix_map_by_pixelformat(vcap->format.pixelformat);
- vcap->format.bytesperline = vcap->format.width * vpix->bpp;
- vcap->format.sizeimage = vcap->format.bytesperline *
- vcap->format.height;
-
- /* Fill the vimc_ent_device struct */
- vcap->ved.ent = &vcap->vdev.entity;
- vcap->ved.process_frame = vimc_cap_process_frame;
- vcap->ved.vdev_get_format = vimc_cap_get_format;
- dev_set_drvdata(comp, &vcap->ved);
- vcap->dev = comp;
-
- /* Initialize the video_device struct */
- vdev = &vcap->vdev;
- vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
- vdev->entity.ops = &vimc_cap_mops;
- vdev->release = vimc_cap_release;
- vdev->fops = &vimc_cap_fops;
- vdev->ioctl_ops = &vimc_cap_ioctl_ops;
- vdev->lock = &vcap->lock;
- vdev->queue = q;
- vdev->v4l2_dev = v4l2_dev;
- vdev->vfl_dir = VFL_DIR_RX;
- strscpy(vdev->name, pdata->entity_name, sizeof(vdev->name));
- video_set_drvdata(vdev, &vcap->ved);
-
- /* Register the video_device with the v4l2 and the media framework */
- ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
- if (ret) {
- dev_err(comp, "%s: video register failed (err=%d)\n",
- vcap->vdev.name, ret);
- goto err_release_queue;
- }
-
- return 0;
-
-err_release_queue:
- vb2_queue_release(q);
-err_clean_m_ent:
- media_entity_cleanup(&vcap->vdev.entity);
-err_clean_pads:
- vimc_pads_cleanup(vcap->ved.pads);
-err_free_vcap:
- kfree(vcap);
-
- return ret;
-}
-
-static const struct component_ops vimc_cap_comp_ops = {
- .bind = vimc_cap_comp_bind,
- .unbind = vimc_cap_comp_unbind,
-};
-
-static int vimc_cap_probe(struct platform_device *pdev)
-{
- return component_add(&pdev->dev, &vimc_cap_comp_ops);
-}
-
-static int vimc_cap_remove(struct platform_device *pdev)
-{
- component_del(&pdev->dev, &vimc_cap_comp_ops);
-
- return 0;
-}
-
-static const struct platform_device_id vimc_cap_driver_ids[] = {
- {
- .name = VIMC_CAP_DRV_NAME,
- },
- { }
-};
-
-static struct platform_driver vimc_cap_pdrv = {
- .probe = vimc_cap_probe,
- .remove = vimc_cap_remove,
- .id_table = vimc_cap_driver_ids,
- .driver = {
- .name = VIMC_CAP_DRV_NAME,
- },
-};
-
-module_platform_driver(vimc_cap_pdrv);
-
-MODULE_DEVICE_TABLE(platform, vimc_cap_driver_ids);
-
-MODULE_DESCRIPTION("Virtual Media Controller Driver (VIMC) Capture");
-MODULE_AUTHOR("Helen Mae Koike Fornazier <helen.fornazier@gmail.com>");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/platform/vimc/vimc-common.c b/drivers/media/platform/vimc/vimc-common.c
deleted file mode 100644
index a3120f4..0000000
--- a/drivers/media/platform/vimc/vimc-common.c
+++ /dev/null
@@ -1,429 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * vimc-common.c Virtual Media Controller Driver
- *
- * Copyright (C) 2015-2017 Helen Koike <helen.fornazier@gmail.com>
- */
-
-#include <linux/init.h>
-#include <linux/module.h>
-
-#include "vimc-common.h"
-
-/*
- * NOTE: non-bayer formats need to come first (necessary for enum_mbus_code
- * in the scaler)
- */
-static const struct vimc_pix_map vimc_pix_map_list[] = {
- /* TODO: add all missing formats */
-
- /* RGB formats */
- {
- .code = MEDIA_BUS_FMT_BGR888_1X24,
- .pixelformat = V4L2_PIX_FMT_BGR24,
- .bpp = 3,
- .bayer = false,
- },
- {
- .code = MEDIA_BUS_FMT_RGB888_1X24,
- .pixelformat = V4L2_PIX_FMT_RGB24,
- .bpp = 3,
- .bayer = false,
- },
- {
- .code = MEDIA_BUS_FMT_ARGB8888_1X32,
- .pixelformat = V4L2_PIX_FMT_ARGB32,
- .bpp = 4,
- .bayer = false,
- },
-
- /* Bayer formats */
- {
- .code = MEDIA_BUS_FMT_SBGGR8_1X8,
- .pixelformat = V4L2_PIX_FMT_SBGGR8,
- .bpp = 1,
- .bayer = true,
- },
- {
- .code = MEDIA_BUS_FMT_SGBRG8_1X8,
- .pixelformat = V4L2_PIX_FMT_SGBRG8,
- .bpp = 1,
- .bayer = true,
- },
- {
- .code = MEDIA_BUS_FMT_SGRBG8_1X8,
- .pixelformat = V4L2_PIX_FMT_SGRBG8,
- .bpp = 1,
- .bayer = true,
- },
- {
- .code = MEDIA_BUS_FMT_SRGGB8_1X8,
- .pixelformat = V4L2_PIX_FMT_SRGGB8,
- .bpp = 1,
- .bayer = true,
- },
- {
- .code = MEDIA_BUS_FMT_SBGGR10_1X10,
- .pixelformat = V4L2_PIX_FMT_SBGGR10,
- .bpp = 2,
- .bayer = true,
- },
- {
- .code = MEDIA_BUS_FMT_SGBRG10_1X10,
- .pixelformat = V4L2_PIX_FMT_SGBRG10,
- .bpp = 2,
- .bayer = true,
- },
- {
- .code = MEDIA_BUS_FMT_SGRBG10_1X10,
- .pixelformat = V4L2_PIX_FMT_SGRBG10,
- .bpp = 2,
- .bayer = true,
- },
- {
- .code = MEDIA_BUS_FMT_SRGGB10_1X10,
- .pixelformat = V4L2_PIX_FMT_SRGGB10,
- .bpp = 2,
- .bayer = true,
- },
-
- /* 10bit raw bayer a-law compressed to 8 bits */
- {
- .code = MEDIA_BUS_FMT_SBGGR10_ALAW8_1X8,
- .pixelformat = V4L2_PIX_FMT_SBGGR10ALAW8,
- .bpp = 1,
- .bayer = true,
- },
- {
- .code = MEDIA_BUS_FMT_SGBRG10_ALAW8_1X8,
- .pixelformat = V4L2_PIX_FMT_SGBRG10ALAW8,
- .bpp = 1,
- .bayer = true,
- },
- {
- .code = MEDIA_BUS_FMT_SGRBG10_ALAW8_1X8,
- .pixelformat = V4L2_PIX_FMT_SGRBG10ALAW8,
- .bpp = 1,
- .bayer = true,
- },
- {
- .code = MEDIA_BUS_FMT_SRGGB10_ALAW8_1X8,
- .pixelformat = V4L2_PIX_FMT_SRGGB10ALAW8,
- .bpp = 1,
- .bayer = true,
- },
-
- /* 10bit raw bayer DPCM compressed to 8 bits */
- {
- .code = MEDIA_BUS_FMT_SBGGR10_DPCM8_1X8,
- .pixelformat = V4L2_PIX_FMT_SBGGR10DPCM8,
- .bpp = 1,
- .bayer = true,
- },
- {
- .code = MEDIA_BUS_FMT_SGBRG10_DPCM8_1X8,
- .pixelformat = V4L2_PIX_FMT_SGBRG10DPCM8,
- .bpp = 1,
- .bayer = true,
- },
- {
- .code = MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8,
- .pixelformat = V4L2_PIX_FMT_SGRBG10DPCM8,
- .bpp = 1,
- .bayer = true,
- },
- {
- .code = MEDIA_BUS_FMT_SRGGB10_DPCM8_1X8,
- .pixelformat = V4L2_PIX_FMT_SRGGB10DPCM8,
- .bpp = 1,
- .bayer = true,
- },
- {
- .code = MEDIA_BUS_FMT_SBGGR12_1X12,
- .pixelformat = V4L2_PIX_FMT_SBGGR12,
- .bpp = 2,
- .bayer = true,
- },
- {
- .code = MEDIA_BUS_FMT_SGBRG12_1X12,
- .pixelformat = V4L2_PIX_FMT_SGBRG12,
- .bpp = 2,
- .bayer = true,
- },
- {
- .code = MEDIA_BUS_FMT_SGRBG12_1X12,
- .pixelformat = V4L2_PIX_FMT_SGRBG12,
- .bpp = 2,
- .bayer = true,
- },
- {
- .code = MEDIA_BUS_FMT_SRGGB12_1X12,
- .pixelformat = V4L2_PIX_FMT_SRGGB12,
- .bpp = 2,
- .bayer = true,
- },
-};
-
-const struct vimc_pix_map *vimc_pix_map_by_index(unsigned int i)
-{
- if (i >= ARRAY_SIZE(vimc_pix_map_list))
- return NULL;
-
- return &vimc_pix_map_list[i];
-}
-EXPORT_SYMBOL_GPL(vimc_pix_map_by_index);
-
-const struct vimc_pix_map *vimc_pix_map_by_code(u32 code)
-{
- unsigned int i;
-
- 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];
- }
- return NULL;
-}
-EXPORT_SYMBOL_GPL(vimc_pix_map_by_code);
-
-const struct vimc_pix_map *vimc_pix_map_by_pixelformat(u32 pixelformat)
-{
- unsigned int i;
-
- for (i = 0; i < ARRAY_SIZE(vimc_pix_map_list); i++) {
- if (vimc_pix_map_list[i].pixelformat == pixelformat)
- return &vimc_pix_map_list[i];
- }
- return NULL;
-}
-EXPORT_SYMBOL_GPL(vimc_pix_map_by_pixelformat);
-
-/* Helper function to allocate and initialize pads */
-struct media_pad *vimc_pads_init(u16 num_pads, const unsigned long *pads_flag)
-{
- struct media_pad *pads;
- unsigned int i;
-
- /* Allocate memory for the pads */
- pads = kcalloc(num_pads, sizeof(*pads), GFP_KERNEL);
- if (!pads)
- return ERR_PTR(-ENOMEM);
-
- /* Initialize the pads */
- for (i = 0; i < num_pads; i++) {
- pads[i].index = i;
- pads[i].flags = pads_flag[i];
- }
-
- return pads;
-}
-EXPORT_SYMBOL_GPL(vimc_pads_init);
-
-int vimc_pipeline_s_stream(struct media_entity *ent, int enable)
-{
- struct v4l2_subdev *sd;
- struct media_pad *pad;
- unsigned int i;
- int ret;
-
- for (i = 0; i < ent->num_pads; i++) {
- if (ent->pads[i].flags & MEDIA_PAD_FL_SOURCE)
- continue;
-
- /* Start the stream in the subdevice direct connected */
- pad = media_entity_remote_pad(&ent->pads[i]);
- if (!pad)
- continue;
-
- if (!is_media_entity_v4l2_subdev(pad->entity))
- return -EINVAL;
-
- sd = media_entity_to_v4l2_subdev(pad->entity);
- ret = v4l2_subdev_call(sd, video, s_stream, enable);
- if (ret && ret != -ENOIOCTLCMD)
- return ret;
- }
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(vimc_pipeline_s_stream);
-
-static int vimc_get_mbus_format(struct media_pad *pad,
- struct v4l2_subdev_format *fmt)
-{
- if (is_media_entity_v4l2_subdev(pad->entity)) {
- struct v4l2_subdev *sd =
- media_entity_to_v4l2_subdev(pad->entity);
- int ret;
-
- fmt->which = V4L2_SUBDEV_FORMAT_ACTIVE;
- fmt->pad = pad->index;
-
- ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, fmt);
- if (ret)
- return ret;
-
- } else if (is_media_entity_v4l2_video_device(pad->entity)) {
- struct video_device *vdev = container_of(pad->entity,
- struct video_device,
- entity);
- struct vimc_ent_device *ved = video_get_drvdata(vdev);
- const struct vimc_pix_map *vpix;
- struct v4l2_pix_format vdev_fmt;
-
- if (!ved->vdev_get_format)
- return -ENOIOCTLCMD;
-
- ved->vdev_get_format(ved, &vdev_fmt);
- vpix = vimc_pix_map_by_pixelformat(vdev_fmt.pixelformat);
- v4l2_fill_mbus_format(&fmt->format, &vdev_fmt, vpix->code);
- } else {
- return -EINVAL;
- }
-
- return 0;
-}
-
-int vimc_link_validate(struct media_link *link)
-{
- struct v4l2_subdev_format source_fmt, sink_fmt;
- int ret;
-
- ret = vimc_get_mbus_format(link->source, &source_fmt);
- if (ret)
- return ret;
-
- ret = vimc_get_mbus_format(link->sink, &sink_fmt);
- if (ret)
- return ret;
-
- pr_info("vimc link validate: "
- "%s:src:%dx%d (0x%x, %d, %d, %d, %d) "
- "%s:snk:%dx%d (0x%x, %d, %d, %d, %d)\n",
- /* src */
- link->source->entity->name,
- source_fmt.format.width, source_fmt.format.height,
- source_fmt.format.code, source_fmt.format.colorspace,
- source_fmt.format.quantization, source_fmt.format.xfer_func,
- source_fmt.format.ycbcr_enc,
- /* sink */
- link->sink->entity->name,
- sink_fmt.format.width, sink_fmt.format.height,
- sink_fmt.format.code, sink_fmt.format.colorspace,
- sink_fmt.format.quantization, sink_fmt.format.xfer_func,
- sink_fmt.format.ycbcr_enc);
-
- /* The width, height and code must match. */
- if (source_fmt.format.width != sink_fmt.format.width
- || source_fmt.format.height != sink_fmt.format.height
- || source_fmt.format.code != sink_fmt.format.code)
- return -EPIPE;
-
- /*
- * The field order must match, or the sink field order must be NONE
- * to support interlaced hardware connected to bridges that support
- * progressive formats only.
- */
- if (source_fmt.format.field != sink_fmt.format.field &&
- sink_fmt.format.field != V4L2_FIELD_NONE)
- return -EPIPE;
-
- /*
- * If colorspace is DEFAULT, then assume all the colorimetry is also
- * DEFAULT, return 0 to skip comparing the other colorimetry parameters
- */
- if (source_fmt.format.colorspace == V4L2_COLORSPACE_DEFAULT
- || sink_fmt.format.colorspace == V4L2_COLORSPACE_DEFAULT)
- return 0;
-
- /* Colorspace must match. */
- if (source_fmt.format.colorspace != sink_fmt.format.colorspace)
- return -EPIPE;
-
- /* Colorimetry must match if they are not set to DEFAULT */
- if (source_fmt.format.ycbcr_enc != V4L2_YCBCR_ENC_DEFAULT
- && sink_fmt.format.ycbcr_enc != V4L2_YCBCR_ENC_DEFAULT
- && source_fmt.format.ycbcr_enc != sink_fmt.format.ycbcr_enc)
- return -EPIPE;
-
- if (source_fmt.format.quantization != V4L2_QUANTIZATION_DEFAULT
- && sink_fmt.format.quantization != V4L2_QUANTIZATION_DEFAULT
- && source_fmt.format.quantization != sink_fmt.format.quantization)
- return -EPIPE;
-
- if (source_fmt.format.xfer_func != V4L2_XFER_FUNC_DEFAULT
- && sink_fmt.format.xfer_func != V4L2_XFER_FUNC_DEFAULT
- && source_fmt.format.xfer_func != sink_fmt.format.xfer_func)
- return -EPIPE;
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(vimc_link_validate);
-
-static const struct media_entity_operations vimc_ent_sd_mops = {
- .link_validate = vimc_link_validate,
-};
-
-int vimc_ent_sd_register(struct vimc_ent_device *ved,
- struct v4l2_subdev *sd,
- struct v4l2_device *v4l2_dev,
- const char *const name,
- u32 function,
- u16 num_pads,
- const unsigned long *pads_flag,
- const struct v4l2_subdev_internal_ops *sd_int_ops,
- const struct v4l2_subdev_ops *sd_ops)
-{
- int ret;
-
- /* Allocate the pads. Should be released from the sd_int_op release */
- ved->pads = vimc_pads_init(num_pads, pads_flag);
- if (IS_ERR(ved->pads))
- return PTR_ERR(ved->pads);
-
- /* Fill the vimc_ent_device struct */
- ved->ent = &sd->entity;
-
- /* Initialize the subdev */
- v4l2_subdev_init(sd, sd_ops);
- sd->internal_ops = sd_int_ops;
- sd->entity.function = function;
- sd->entity.ops = &vimc_ent_sd_mops;
- sd->owner = THIS_MODULE;
- strscpy(sd->name, name, sizeof(sd->name));
- v4l2_set_subdevdata(sd, ved);
-
- /* Expose this subdev to user space */
- sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
- if (sd->ctrl_handler)
- sd->flags |= V4L2_SUBDEV_FL_HAS_EVENTS;
-
- /* Initialize the media entity */
- ret = media_entity_pads_init(&sd->entity, num_pads, ved->pads);
- if (ret)
- goto err_clean_pads;
-
- /* Register the subdev with the v4l2 and the media framework */
- ret = v4l2_device_register_subdev(v4l2_dev, sd);
- if (ret) {
- dev_err(v4l2_dev->dev,
- "%s: subdev register failed (err=%d)\n",
- name, ret);
- goto err_clean_m_ent;
- }
-
- return 0;
-
-err_clean_m_ent:
- media_entity_cleanup(&sd->entity);
-err_clean_pads:
- vimc_pads_cleanup(ved->pads);
- return ret;
-}
-EXPORT_SYMBOL_GPL(vimc_ent_sd_register);
-
-void vimc_ent_sd_unregister(struct vimc_ent_device *ved, struct v4l2_subdev *sd)
-{
- media_entity_cleanup(ved->ent);
- v4l2_device_unregister_subdev(sd);
-}
-EXPORT_SYMBOL_GPL(vimc_ent_sd_unregister);
diff --git a/drivers/media/platform/vimc/vimc-common.h b/drivers/media/platform/vimc/vimc-common.h
deleted file mode 100644
index 9c2e0e2..0000000
--- a/drivers/media/platform/vimc/vimc-common.h
+++ /dev/null
@@ -1,217 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/*
- * vimc-common.h Virtual Media Controller Driver
- *
- * Copyright (C) 2015-2017 Helen Koike <helen.fornazier@gmail.com>
- */
-
-#ifndef _VIMC_COMMON_H_
-#define _VIMC_COMMON_H_
-
-#include <linux/slab.h>
-#include <media/media-device.h>
-#include <media/v4l2-device.h>
-
-#define VIMC_PDEV_NAME "vimc"
-
-/* VIMC-specific controls */
-#define VIMC_CID_VIMC_BASE (0x00f00000 | 0xf000)
-#define VIMC_CID_VIMC_CLASS (0x00f00000 | 1)
-#define VIMC_CID_TEST_PATTERN (VIMC_CID_VIMC_BASE + 0)
-
-#define VIMC_FRAME_MAX_WIDTH 4096
-#define VIMC_FRAME_MAX_HEIGHT 2160
-#define VIMC_FRAME_MIN_WIDTH 16
-#define VIMC_FRAME_MIN_HEIGHT 16
-
-#define VIMC_FRAME_INDEX(lin, col, width, bpp) ((lin * width + col) * bpp)
-
-/**
- * struct vimc_colorimetry_clamp - Adjust colorimetry parameters
- *
- * @fmt: the pointer to struct v4l2_pix_format or
- * struct v4l2_mbus_framefmt
- *
- * Entities must check if colorimetry given by the userspace is valid, if not
- * then set them as DEFAULT
- */
-#define vimc_colorimetry_clamp(fmt) \
-do { \
- if ((fmt)->colorspace == V4L2_COLORSPACE_DEFAULT \
- || (fmt)->colorspace > V4L2_COLORSPACE_DCI_P3) { \
- (fmt)->colorspace = V4L2_COLORSPACE_DEFAULT; \
- (fmt)->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; \
- (fmt)->quantization = V4L2_QUANTIZATION_DEFAULT; \
- (fmt)->xfer_func = V4L2_XFER_FUNC_DEFAULT; \
- } \
- if ((fmt)->ycbcr_enc > V4L2_YCBCR_ENC_SMPTE240M) \
- (fmt)->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; \
- if ((fmt)->quantization > V4L2_QUANTIZATION_LIM_RANGE) \
- (fmt)->quantization = V4L2_QUANTIZATION_DEFAULT; \
- if ((fmt)->xfer_func > V4L2_XFER_FUNC_SMPTE2084) \
- (fmt)->xfer_func = V4L2_XFER_FUNC_DEFAULT; \
-} while (0)
-
-/**
- * struct vimc_platform_data - platform data to components
- *
- * @entity_name: The name of the entity to be created
- *
- * Board setup code will often provide additional information using the device's
- * platform_data field to hold additional information.
- * When injecting a new platform_device in the component system the core needs
- * to provide to the corresponding submodules the name of the entity that should
- * be used when registering the subdevice in the Media Controller system.
- */
-struct vimc_platform_data {
- char entity_name[32];
-};
-
-/**
- * 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
- *
- * 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 bpp;
- u32 pixelformat;
- bool bayer;
-};
-
-/**
- * struct vimc_ent_device - core struct that represents a node in the topology
- *
- * @ent: the pointer to struct media_entity for the node
- * @pads: the list of pads of the node
- * @process_frame: callback send a frame to that node
- * @vdev_get_format: callback that returns the current format a pad, used
- * only when is_media_entity_v4l2_video_device(ent) returns
- * true
- *
- * Each node of the topology must create a vimc_ent_device struct. Depending on
- * 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
- * vimc_ent_device struct to be retrieved from the corresponding struct
- * media_entity
- */
-struct vimc_ent_device {
- struct media_entity *ent;
- struct media_pad *pads;
- void * (*process_frame)(struct vimc_ent_device *ved,
- const void *frame);
- void (*vdev_get_format)(struct vimc_ent_device *ved,
- struct v4l2_pix_format *fmt);
-};
-
-/**
- * vimc_pads_init - initialize pads
- *
- * @num_pads: number of pads to initialize
- * @pads_flags: flags to use in each pad
- *
- * Helper functions to allocate/initialize pads
- */
-struct media_pad *vimc_pads_init(u16 num_pads,
- const unsigned long *pads_flag);
-
-/**
- * vimc_pads_cleanup - free pads
- *
- * @pads: pointer to the pads
- *
- * Helper function to free the pads initialized with vimc_pads_init
- */
-static inline void vimc_pads_cleanup(struct media_pad *pads)
-{
- kfree(pads);
-}
-
-/**
- * vimc_pipeline_s_stream - start stream through the pipeline
- *
- * @ent: the pointer to struct media_entity for the node
- * @enable: 1 to start the stream and 0 to stop
- *
- * Helper function to call the s_stream of the subdevices connected
- * in all the sink pads of the entity
- */
-int vimc_pipeline_s_stream(struct media_entity *ent, int enable);
-
-/**
- * vimc_pix_map_by_index - get vimc_pix_map struct by its index
- *
- * @i: index of the vimc_pix_map struct in vimc_pix_map_list
- */
-const struct vimc_pix_map *vimc_pix_map_by_index(unsigned int i);
-
-/**
- * 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
- */
-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
- */
-const struct vimc_pix_map *vimc_pix_map_by_pixelformat(u32 pixelformat);
-
-/**
- * vimc_ent_sd_register - initialize and register a subdev node
- *
- * @ved: the vimc_ent_device struct to be initialize
- * @sd: the v4l2_subdev struct to be initialize and registered
- * @v4l2_dev: the v4l2 device to register the v4l2_subdev
- * @name: name of the sub-device. Please notice that the name must be
- * unique.
- * @function: media entity function defined by MEDIA_ENT_F_* macros
- * @num_pads: number of pads to initialize
- * @pads_flag: flags to use in each pad
- * @sd_int_ops: pointer to &struct v4l2_subdev_internal_ops
- * @sd_ops: pointer to &struct v4l2_subdev_ops.
- *
- * Helper function initialize and register the struct vimc_ent_device and struct
- * v4l2_subdev which represents a subdev node in the topology
- */
-int vimc_ent_sd_register(struct vimc_ent_device *ved,
- struct v4l2_subdev *sd,
- struct v4l2_device *v4l2_dev,
- const char *const name,
- u32 function,
- u16 num_pads,
- const unsigned long *pads_flag,
- const struct v4l2_subdev_internal_ops *sd_int_ops,
- const struct v4l2_subdev_ops *sd_ops);
-
-/**
- * vimc_ent_sd_unregister - cleanup and unregister a subdev node
- *
- * @ved: the vimc_ent_device struct to be cleaned up
- * @sd: the v4l2_subdev struct to be unregistered
- *
- * Helper function cleanup and unregister the struct vimc_ent_device and struct
- * v4l2_subdev which represents a subdev node in the topology
- */
-void vimc_ent_sd_unregister(struct vimc_ent_device *ved,
- struct v4l2_subdev *sd);
-
-/**
- * vimc_link_validate - validates a media link
- *
- * @link: pointer to &struct media_link
- *
- * This function calls validates if a media link is valid for streaming.
- */
-int vimc_link_validate(struct media_link *link);
-
-#endif
diff --git a/drivers/media/platform/vimc/vimc-core.c b/drivers/media/platform/vimc/vimc-core.c
deleted file mode 100644
index 571c55a..0000000
--- a/drivers/media/platform/vimc/vimc-core.c
+++ /dev/null
@@ -1,395 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * vimc-core.c Virtual Media Controller Driver
- *
- * Copyright (C) 2015-2017 Helen Koike <helen.fornazier@gmail.com>
- */
-
-#include <linux/component.h>
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <media/media-device.h>
-#include <media/v4l2-device.h>
-
-#include "vimc-common.h"
-
-#define VIMC_MDEV_MODEL_NAME "VIMC MDEV"
-
-#define VIMC_ENT_LINK(src, srcpad, sink, sinkpad, link_flags) { \
- .src_ent = src, \
- .src_pad = srcpad, \
- .sink_ent = sink, \
- .sink_pad = sinkpad, \
- .flags = link_flags, \
-}
-
-struct vimc_device {
- /* The platform device */
- struct platform_device pdev;
-
- /* The pipeline configuration */
- const struct vimc_pipeline_config *pipe_cfg;
-
- /* The Associated media_device parent */
- struct media_device mdev;
-
- /* Internal v4l2 parent device*/
- struct v4l2_device v4l2_dev;
-
- /* Subdevices */
- struct platform_device **subdevs;
-};
-
-/* Structure which describes individual configuration for each entity */
-struct vimc_ent_config {
- const char *name;
- const char *drv;
-};
-
-/* Structure which describes links between entities */
-struct vimc_ent_link {
- unsigned int src_ent;
- u16 src_pad;
- unsigned int sink_ent;
- u16 sink_pad;
- u32 flags;
-};
-
-/* Structure which describes the whole topology */
-struct vimc_pipeline_config {
- const struct vimc_ent_config *ents;
- size_t num_ents;
- const struct vimc_ent_link *links;
- size_t num_links;
-};
-
-/* --------------------------------------------------------------------------
- * Topology Configuration
- */
-
-static const struct vimc_ent_config ent_config[] = {
- {
- .name = "Sensor A",
- .drv = "vimc-sensor",
- },
- {
- .name = "Sensor B",
- .drv = "vimc-sensor",
- },
- {
- .name = "Debayer A",
- .drv = "vimc-debayer",
- },
- {
- .name = "Debayer B",
- .drv = "vimc-debayer",
- },
- {
- .name = "Raw Capture 0",
- .drv = "vimc-capture",
- },
- {
- .name = "Raw Capture 1",
- .drv = "vimc-capture",
- },
- {
- .name = "RGB/YUV Input",
- /* TODO: change this to vimc-input when it is implemented */
- .drv = "vimc-sensor",
- },
- {
- .name = "Scaler",
- .drv = "vimc-scaler",
- },
- {
- .name = "RGB/YUV Capture",
- .drv = "vimc-capture",
- },
-};
-
-static const struct vimc_ent_link ent_links[] = {
- /* Link: Sensor A (Pad 0)->(Pad 0) Debayer A */
- VIMC_ENT_LINK(0, 0, 2, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
- /* Link: Sensor A (Pad 0)->(Pad 0) Raw Capture 0 */
- VIMC_ENT_LINK(0, 0, 4, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
- /* Link: Sensor B (Pad 0)->(Pad 0) Debayer B */
- VIMC_ENT_LINK(1, 0, 3, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
- /* Link: Sensor B (Pad 0)->(Pad 0) Raw Capture 1 */
- VIMC_ENT_LINK(1, 0, 5, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
- /* Link: Debayer A (Pad 1)->(Pad 0) Scaler */
- VIMC_ENT_LINK(2, 1, 7, 0, MEDIA_LNK_FL_ENABLED),
- /* Link: Debayer B (Pad 1)->(Pad 0) Scaler */
- VIMC_ENT_LINK(3, 1, 7, 0, 0),
- /* Link: RGB/YUV Input (Pad 0)->(Pad 0) Scaler */
- VIMC_ENT_LINK(6, 0, 7, 0, 0),
- /* Link: Scaler (Pad 1)->(Pad 0) RGB/YUV Capture */
- VIMC_ENT_LINK(7, 1, 8, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
-};
-
-static const struct vimc_pipeline_config pipe_cfg = {
- .ents = ent_config,
- .num_ents = ARRAY_SIZE(ent_config),
- .links = ent_links,
- .num_links = ARRAY_SIZE(ent_links)
-};
-
-/* -------------------------------------------------------------------------- */
-
-static int vimc_create_links(struct vimc_device *vimc)
-{
- unsigned int i;
- int ret;
-
- /* Initialize the links between entities */
- for (i = 0; i < vimc->pipe_cfg->num_links; i++) {
- const struct vimc_ent_link *link = &vimc->pipe_cfg->links[i];
- /*
- * TODO: Check another way of retrieving ved struct without
- * relying on platform_get_drvdata
- */
- struct vimc_ent_device *ved_src =
- platform_get_drvdata(vimc->subdevs[link->src_ent]);
- struct vimc_ent_device *ved_sink =
- platform_get_drvdata(vimc->subdevs[link->sink_ent]);
-
- ret = media_create_pad_link(ved_src->ent, link->src_pad,
- ved_sink->ent, link->sink_pad,
- link->flags);
- if (ret)
- return ret;
- }
-
- return 0;
-}
-
-static int vimc_comp_bind(struct device *master)
-{
- struct vimc_device *vimc = container_of(to_platform_device(master),
- struct vimc_device, pdev);
- int ret;
-
- dev_dbg(master, "bind");
-
- /* Register the v4l2 struct */
- ret = v4l2_device_register(vimc->mdev.dev, &vimc->v4l2_dev);
- if (ret) {
- dev_err(vimc->mdev.dev,
- "v4l2 device register failed (err=%d)\n", ret);
- return ret;
- }
-
- /* Bind subdevices */
- ret = component_bind_all(master, &vimc->v4l2_dev);
- if (ret)
- goto err_v4l2_unregister;
-
- /* Initialize links */
- ret = vimc_create_links(vimc);
- if (ret)
- goto err_comp_unbind_all;
-
- /* Register the media device */
- ret = media_device_register(&vimc->mdev);
- if (ret) {
- dev_err(vimc->mdev.dev,
- "media device register failed (err=%d)\n", ret);
- goto err_comp_unbind_all;
- }
-
- /* Expose all subdev's nodes*/
- ret = v4l2_device_register_subdev_nodes(&vimc->v4l2_dev);
- if (ret) {
- dev_err(vimc->mdev.dev,
- "vimc subdev nodes registration failed (err=%d)\n",
- ret);
- goto err_mdev_unregister;
- }
-
- return 0;
-
-err_mdev_unregister:
- media_device_unregister(&vimc->mdev);
- media_device_cleanup(&vimc->mdev);
-err_comp_unbind_all:
- component_unbind_all(master, NULL);
-err_v4l2_unregister:
- v4l2_device_unregister(&vimc->v4l2_dev);
-
- return ret;
-}
-
-static void vimc_comp_unbind(struct device *master)
-{
- struct vimc_device *vimc = container_of(to_platform_device(master),
- struct vimc_device, pdev);
-
- dev_dbg(master, "unbind");
-
- media_device_unregister(&vimc->mdev);
- media_device_cleanup(&vimc->mdev);
- component_unbind_all(master, NULL);
- v4l2_device_unregister(&vimc->v4l2_dev);
-}
-
-static int vimc_comp_compare(struct device *comp, void *data)
-{
- return comp == data;
-}
-
-static struct component_match *vimc_add_subdevs(struct vimc_device *vimc)
-{
- struct component_match *match = NULL;
- struct vimc_platform_data pdata;
- int i;
-
- for (i = 0; i < vimc->pipe_cfg->num_ents; i++) {
- dev_dbg(&vimc->pdev.dev, "new pdev for %s\n",
- vimc->pipe_cfg->ents[i].drv);
-
- strscpy(pdata.entity_name, vimc->pipe_cfg->ents[i].name,
- sizeof(pdata.entity_name));
-
- vimc->subdevs[i] = platform_device_register_data(&vimc->pdev.dev,
- vimc->pipe_cfg->ents[i].drv,
- PLATFORM_DEVID_AUTO,
- &pdata,
- sizeof(pdata));
- if (IS_ERR(vimc->subdevs[i])) {
- match = ERR_CAST(vimc->subdevs[i]);
- while (--i >= 0)
- platform_device_unregister(vimc->subdevs[i]);
-
- return match;
- }
-
- component_match_add(&vimc->pdev.dev, &match, vimc_comp_compare,
- &vimc->subdevs[i]->dev);
- }
-
- return match;
-}
-
-static void vimc_rm_subdevs(struct vimc_device *vimc)
-{
- unsigned int i;
-
- for (i = 0; i < vimc->pipe_cfg->num_ents; i++)
- platform_device_unregister(vimc->subdevs[i]);
-}
-
-static const struct component_master_ops vimc_comp_ops = {
- .bind = vimc_comp_bind,
- .unbind = vimc_comp_unbind,
-};
-
-static int vimc_probe(struct platform_device *pdev)
-{
- struct vimc_device *vimc = container_of(pdev, struct vimc_device, pdev);
- struct component_match *match = NULL;
- int ret;
-
- dev_dbg(&pdev->dev, "probe");
-
- memset(&vimc->mdev, 0, sizeof(vimc->mdev));
-
- /* Create platform_device for each entity in the topology*/
- vimc->subdevs = devm_kcalloc(&vimc->pdev.dev, vimc->pipe_cfg->num_ents,
- sizeof(*vimc->subdevs), GFP_KERNEL);
- if (!vimc->subdevs)
- return -ENOMEM;
-
- match = vimc_add_subdevs(vimc);
- if (IS_ERR(match))
- return PTR_ERR(match);
-
- /* Link the media device within the v4l2_device */
- vimc->v4l2_dev.mdev = &vimc->mdev;
-
- /* Initialize media device */
- strscpy(vimc->mdev.model, VIMC_MDEV_MODEL_NAME,
- sizeof(vimc->mdev.model));
- snprintf(vimc->mdev.bus_info, sizeof(vimc->mdev.bus_info),
- "platform:%s", VIMC_PDEV_NAME);
- vimc->mdev.dev = &pdev->dev;
- media_device_init(&vimc->mdev);
-
- /* Add self to the component system */
- ret = component_master_add_with_match(&pdev->dev, &vimc_comp_ops,
- match);
- if (ret) {
- media_device_cleanup(&vimc->mdev);
- vimc_rm_subdevs(vimc);
- return ret;
- }
-
- return 0;
-}
-
-static int vimc_remove(struct platform_device *pdev)
-{
- struct vimc_device *vimc = container_of(pdev, struct vimc_device, pdev);
-
- dev_dbg(&pdev->dev, "remove");
-
- component_master_del(&pdev->dev, &vimc_comp_ops);
- vimc_rm_subdevs(vimc);
-
- return 0;
-}
-
-static void vimc_dev_release(struct device *dev)
-{
-}
-
-static struct vimc_device vimc_dev = {
- .pipe_cfg = &pipe_cfg,
- .pdev = {
- .name = VIMC_PDEV_NAME,
- .dev.release = vimc_dev_release,
- }
-};
-
-static struct platform_driver vimc_pdrv = {
- .probe = vimc_probe,
- .remove = vimc_remove,
- .driver = {
- .name = VIMC_PDEV_NAME,
- },
-};
-
-static int __init vimc_init(void)
-{
- int ret;
-
- ret = platform_device_register(&vimc_dev.pdev);
- if (ret) {
- dev_err(&vimc_dev.pdev.dev,
- "platform device registration failed (err=%d)\n", ret);
- return ret;
- }
-
- ret = platform_driver_register(&vimc_pdrv);
- if (ret) {
- dev_err(&vimc_dev.pdev.dev,
- "platform driver registration failed (err=%d)\n", ret);
- platform_driver_unregister(&vimc_pdrv);
- return ret;
- }
-
- return 0;
-}
-
-static void __exit vimc_exit(void)
-{
- platform_driver_unregister(&vimc_pdrv);
-
- platform_device_unregister(&vimc_dev.pdev);
-}
-
-module_init(vimc_init);
-module_exit(vimc_exit);
-
-MODULE_DESCRIPTION("Virtual Media Controller Driver (VIMC)");
-MODULE_AUTHOR("Helen Fornazier <helen.fornazier@gmail.com>");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/platform/vimc/vimc-debayer.c b/drivers/media/platform/vimc/vimc-debayer.c
deleted file mode 100644
index baafd9d..0000000
--- a/drivers/media/platform/vimc/vimc-debayer.c
+++ /dev/null
@@ -1,587 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * vimc-debayer.c Virtual Media Controller Driver
- *
- * Copyright (C) 2015-2017 Helen Koike <helen.fornazier@gmail.com>
- */
-
-#include <linux/component.h>
-#include <linux/module.h>
-#include <linux/mod_devicetable.h>
-#include <linux/platform_device.h>
-#include <linux/vmalloc.h>
-#include <linux/v4l2-mediabus.h>
-#include <media/v4l2-subdev.h>
-
-#include "vimc-common.h"
-
-#define VIMC_DEB_DRV_NAME "vimc-debayer"
-
-static unsigned int deb_mean_win_size = 3;
-module_param(deb_mean_win_size, uint, 0000);
-MODULE_PARM_DESC(deb_mean_win_size, " the window size to calculate the mean.\n"
- "NOTE: the window size needs to be an odd number, as the main pixel "
- "stays in the center of the window, otherwise the next odd number "
- "is considered");
-
-#define IS_SINK(pad) (!pad)
-#define IS_SRC(pad) (pad)
-
-enum vimc_deb_rgb_colors {
- VIMC_DEB_RED = 0,
- VIMC_DEB_GREEN = 1,
- VIMC_DEB_BLUE = 2,
-};
-
-struct vimc_deb_pix_map {
- u32 code;
- enum vimc_deb_rgb_colors order[2][2];
-};
-
-struct vimc_deb_device {
- struct vimc_ent_device ved;
- struct v4l2_subdev sd;
- struct device *dev;
- /* The active format */
- struct v4l2_mbus_framefmt sink_fmt;
- u32 src_code;
- void (*set_rgb_src)(struct vimc_deb_device *vdeb, unsigned int lin,
- unsigned int col, unsigned int rgb[3]);
- /* Values calculated when the stream starts */
- u8 *src_frame;
- const struct vimc_deb_pix_map *sink_pix_map;
- unsigned int sink_bpp;
-};
-
-static const struct v4l2_mbus_framefmt sink_fmt_default = {
- .width = 640,
- .height = 480,
- .code = MEDIA_BUS_FMT_SRGGB8_1X8,
- .field = V4L2_FIELD_NONE,
- .colorspace = V4L2_COLORSPACE_DEFAULT,
-};
-
-static const struct vimc_deb_pix_map vimc_deb_pix_map_list[] = {
- {
- .code = MEDIA_BUS_FMT_SBGGR8_1X8,
- .order = { { VIMC_DEB_BLUE, VIMC_DEB_GREEN },
- { VIMC_DEB_GREEN, VIMC_DEB_RED } }
- },
- {
- .code = MEDIA_BUS_FMT_SGBRG8_1X8,
- .order = { { VIMC_DEB_GREEN, VIMC_DEB_BLUE },
- { VIMC_DEB_RED, VIMC_DEB_GREEN } }
- },
- {
- .code = MEDIA_BUS_FMT_SGRBG8_1X8,
- .order = { { VIMC_DEB_GREEN, VIMC_DEB_RED },
- { VIMC_DEB_BLUE, VIMC_DEB_GREEN } }
- },
- {
- .code = MEDIA_BUS_FMT_SRGGB8_1X8,
- .order = { { VIMC_DEB_RED, VIMC_DEB_GREEN },
- { VIMC_DEB_GREEN, VIMC_DEB_BLUE } }
- },
- {
- .code = MEDIA_BUS_FMT_SBGGR10_1X10,
- .order = { { VIMC_DEB_BLUE, VIMC_DEB_GREEN },
- { VIMC_DEB_GREEN, VIMC_DEB_RED } }
- },
- {
- .code = MEDIA_BUS_FMT_SGBRG10_1X10,
- .order = { { VIMC_DEB_GREEN, VIMC_DEB_BLUE },
- { VIMC_DEB_RED, VIMC_DEB_GREEN } }
- },
- {
- .code = MEDIA_BUS_FMT_SGRBG10_1X10,
- .order = { { VIMC_DEB_GREEN, VIMC_DEB_RED },
- { VIMC_DEB_BLUE, VIMC_DEB_GREEN } }
- },
- {
- .code = MEDIA_BUS_FMT_SRGGB10_1X10,
- .order = { { VIMC_DEB_RED, VIMC_DEB_GREEN },
- { VIMC_DEB_GREEN, VIMC_DEB_BLUE } }
- },
- {
- .code = MEDIA_BUS_FMT_SBGGR12_1X12,
- .order = { { VIMC_DEB_BLUE, VIMC_DEB_GREEN },
- { VIMC_DEB_GREEN, VIMC_DEB_RED } }
- },
- {
- .code = MEDIA_BUS_FMT_SGBRG12_1X12,
- .order = { { VIMC_DEB_GREEN, VIMC_DEB_BLUE },
- { VIMC_DEB_RED, VIMC_DEB_GREEN } }
- },
- {
- .code = MEDIA_BUS_FMT_SGRBG12_1X12,
- .order = { { VIMC_DEB_GREEN, VIMC_DEB_RED },
- { VIMC_DEB_BLUE, VIMC_DEB_GREEN } }
- },
- {
- .code = MEDIA_BUS_FMT_SRGGB12_1X12,
- .order = { { VIMC_DEB_RED, VIMC_DEB_GREEN },
- { VIMC_DEB_GREEN, VIMC_DEB_BLUE } }
- },
-};
-
-static const struct vimc_deb_pix_map *vimc_deb_pix_map_by_code(u32 code)
-{
- unsigned int i;
-
- for (i = 0; i < ARRAY_SIZE(vimc_deb_pix_map_list); i++)
- if (vimc_deb_pix_map_list[i].code == code)
- return &vimc_deb_pix_map_list[i];
-
- return NULL;
-}
-
-static int vimc_deb_init_cfg(struct v4l2_subdev *sd,
- struct v4l2_subdev_pad_config *cfg)
-{
- struct vimc_deb_device *vdeb = v4l2_get_subdevdata(sd);
- struct v4l2_mbus_framefmt *mf;
- unsigned int i;
-
- mf = v4l2_subdev_get_try_format(sd, cfg, 0);
- *mf = sink_fmt_default;
-
- for (i = 1; i < sd->entity.num_pads; i++) {
- mf = v4l2_subdev_get_try_format(sd, cfg, i);
- *mf = sink_fmt_default;
- mf->code = vdeb->src_code;
- }
-
- return 0;
-}
-
-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 (IS_SRC(code->pad)) {
- struct vimc_deb_device *vdeb = v4l2_get_subdevdata(sd);
-
- if (code->index)
- return -EINVAL;
-
- code->code = vdeb->src_code;
- } else {
- if (code->index >= ARRAY_SIZE(vimc_deb_pix_map_list))
- return -EINVAL;
-
- code->code = vimc_deb_pix_map_list[code->index].code;
- }
-
- return 0;
-}
-
-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;
-
- if (IS_SINK(fse->pad)) {
- const struct vimc_deb_pix_map *vpix =
- vimc_deb_pix_map_by_code(fse->code);
-
- if (!vpix)
- return -EINVAL;
- } else if (fse->code != vdeb->src_code) {
- return -EINVAL;
- }
-
- fse->min_width = VIMC_FRAME_MIN_WIDTH;
- fse->max_width = VIMC_FRAME_MAX_WIDTH;
- fse->min_height = VIMC_FRAME_MIN_HEIGHT;
- fse->max_height = VIMC_FRAME_MAX_HEIGHT;
-
- return 0;
-}
-
-static int vimc_deb_get_fmt(struct v4l2_subdev *sd,
- struct v4l2_subdev_pad_config *cfg,
- struct v4l2_subdev_format *fmt)
-{
- struct vimc_deb_device *vdeb = v4l2_get_subdevdata(sd);
-
- /* Get the current sink format */
- fmt->format = fmt->which == V4L2_SUBDEV_FORMAT_TRY ?
- *v4l2_subdev_get_try_format(sd, cfg, 0) :
- vdeb->sink_fmt;
-
- /* Set the right code for the source pad */
- if (IS_SRC(fmt->pad))
- fmt->format.code = vdeb->src_code;
-
- return 0;
-}
-
-static void vimc_deb_adjust_sink_fmt(struct v4l2_mbus_framefmt *fmt)
-{
- const struct vimc_deb_pix_map *vpix;
-
- /* Don't accept a code that is not on the debayer table */
- vpix = vimc_deb_pix_map_by_code(fmt->code);
- if (!vpix)
- fmt->code = sink_fmt_default.code;
-
- fmt->width = clamp_t(u32, fmt->width, VIMC_FRAME_MIN_WIDTH,
- VIMC_FRAME_MAX_WIDTH) & ~1;
- fmt->height = clamp_t(u32, fmt->height, VIMC_FRAME_MIN_HEIGHT,
- VIMC_FRAME_MAX_HEIGHT) & ~1;
-
- if (fmt->field == V4L2_FIELD_ANY)
- fmt->field = sink_fmt_default.field;
-
- vimc_colorimetry_clamp(fmt);
-}
-
-static int vimc_deb_set_fmt(struct v4l2_subdev *sd,
- struct v4l2_subdev_pad_config *cfg,
- struct v4l2_subdev_format *fmt)
-{
- struct vimc_deb_device *vdeb = v4l2_get_subdevdata(sd);
- struct v4l2_mbus_framefmt *sink_fmt;
-
- if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
- /* Do not change the format while stream is on */
- if (vdeb->src_frame)
- return -EBUSY;
-
- sink_fmt = &vdeb->sink_fmt;
- } else {
- sink_fmt = v4l2_subdev_get_try_format(sd, cfg, 0);
- }
-
- /*
- * Do not change the format of the source pad,
- * it is propagated from the sink
- */
- if (IS_SRC(fmt->pad)) {
- fmt->format = *sink_fmt;
- /* TODO: Add support for other formats */
- fmt->format.code = vdeb->src_code;
- } else {
- /* Set the new format in the sink pad */
- vimc_deb_adjust_sink_fmt(&fmt->format);
-
- dev_dbg(vdeb->dev, "%s: sink format update: "
- "old:%dx%d (0x%x, %d, %d, %d, %d) "
- "new:%dx%d (0x%x, %d, %d, %d, %d)\n", vdeb->sd.name,
- /* old */
- sink_fmt->width, sink_fmt->height, sink_fmt->code,
- sink_fmt->colorspace, sink_fmt->quantization,
- sink_fmt->xfer_func, sink_fmt->ycbcr_enc,
- /* new */
- fmt->format.width, fmt->format.height, fmt->format.code,
- fmt->format.colorspace, fmt->format.quantization,
- fmt->format.xfer_func, fmt->format.ycbcr_enc);
-
- *sink_fmt = fmt->format;
- }
-
- return 0;
-}
-
-static const struct v4l2_subdev_pad_ops vimc_deb_pad_ops = {
- .init_cfg = vimc_deb_init_cfg,
- .enum_mbus_code = vimc_deb_enum_mbus_code,
- .enum_frame_size = vimc_deb_enum_frame_size,
- .get_fmt = vimc_deb_get_fmt,
- .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])
-{
- unsigned int i, index;
-
- index = VIMC_FRAME_INDEX(lin, col, vdeb->sink_fmt.width, 3);
- for (i = 0; i < 3; i++)
- vdeb->src_frame[index + i] = rgb[i];
-}
-
-static int vimc_deb_s_stream(struct v4l2_subdev *sd, int enable)
-{
- struct vimc_deb_device *vdeb = v4l2_get_subdevdata(sd);
-
- if (enable) {
- const struct vimc_pix_map *vpix;
- unsigned int frame_size;
-
- if (vdeb->src_frame)
- return 0;
-
- /* Calculate the frame size of the source pad */
- vpix = vimc_pix_map_by_code(vdeb->src_code);
- frame_size = vdeb->sink_fmt.width * vdeb->sink_fmt.height *
- vpix->bpp;
-
- /* Save the bytes per pixel of the sink */
- vpix = vimc_pix_map_by_code(vdeb->sink_fmt.code);
- vdeb->sink_bpp = vpix->bpp;
-
- /* Get the corresponding pixel map from the table */
- vdeb->sink_pix_map =
- vimc_deb_pix_map_by_code(vdeb->sink_fmt.code);
-
- /*
- * Allocate the frame buffer. Use vmalloc to be able to
- * allocate a large amount of memory
- */
- vdeb->src_frame = vmalloc(frame_size);
- if (!vdeb->src_frame)
- return -ENOMEM;
-
- } else {
- if (!vdeb->src_frame)
- return 0;
-
- vfree(vdeb->src_frame);
- vdeb->src_frame = NULL;
- }
-
- return 0;
-}
-
-static const struct v4l2_subdev_video_ops vimc_deb_video_ops = {
- .s_stream = vimc_deb_s_stream,
-};
-
-static const struct v4l2_subdev_ops vimc_deb_ops = {
- .pad = &vimc_deb_pad_ops,
- .video = &vimc_deb_video_ops,
-};
-
-static unsigned int vimc_deb_get_val(const u8 *bytes,
- const unsigned int n_bytes)
-{
- unsigned int i;
- unsigned int acc = 0;
-
- for (i = 0; i < n_bytes; i++)
- acc = acc + (bytes[i] << (8 * i));
-
- return acc;
-}
-
-static void vimc_deb_calc_rgb_sink(struct vimc_deb_device *vdeb,
- const u8 *frame,
- const unsigned int lin,
- const unsigned int col,
- unsigned int rgb[3])
-{
- unsigned int i, seek, wlin, wcol;
- unsigned int n_rgb[3] = {0, 0, 0};
-
- for (i = 0; i < 3; i++)
- rgb[i] = 0;
-
- /*
- * Calculate how many we need to subtract to get to the pixel in
- * the top left corner of the mean window (considering the current
- * pixel as the center)
- */
- seek = deb_mean_win_size / 2;
-
- /* Sum the values of the colors in the mean window */
-
- dev_dbg(vdeb->dev,
- "deb: %s: --- Calc pixel %dx%d, window mean %d, seek %d ---\n",
- vdeb->sd.name, lin, col, vdeb->sink_fmt.height, seek);
-
- /*
- * Iterate through all the lines in the mean window, start
- * with zero if the pixel is outside the frame and don't pass
- * the height when the pixel is in the bottom border of the
- * frame
- */
- for (wlin = seek > lin ? 0 : lin - seek;
- wlin < lin + seek + 1 && wlin < vdeb->sink_fmt.height;
- wlin++) {
-
- /*
- * Iterate through all the columns in the mean window, start
- * with zero if the pixel is outside the frame and don't pass
- * the width when the pixel is in the right border of the
- * frame
- */
- for (wcol = seek > col ? 0 : col - seek;
- wcol < col + seek + 1 && wcol < vdeb->sink_fmt.width;
- wcol++) {
- enum vimc_deb_rgb_colors color;
- unsigned int index;
-
- /* Check which color this pixel is */
- color = vdeb->sink_pix_map->order[wlin % 2][wcol % 2];
-
- index = VIMC_FRAME_INDEX(wlin, wcol,
- vdeb->sink_fmt.width,
- vdeb->sink_bpp);
-
- dev_dbg(vdeb->dev,
- "deb: %s: RGB CALC: frame index %d, win pos %dx%d, color %d\n",
- vdeb->sd.name, index, wlin, wcol, color);
-
- /* Get its value */
- rgb[color] = rgb[color] +
- vimc_deb_get_val(&frame[index], vdeb->sink_bpp);
-
- /* Save how many values we already added */
- n_rgb[color]++;
-
- dev_dbg(vdeb->dev, "deb: %s: RGB CALC: val %d, n %d\n",
- vdeb->sd.name, rgb[color], n_rgb[color]);
- }
- }
-
- /* Calculate the mean */
- for (i = 0; i < 3; i++) {
- dev_dbg(vdeb->dev,
- "deb: %s: PRE CALC: %dx%d Color %d, val %d, n %d\n",
- vdeb->sd.name, lin, col, i, rgb[i], n_rgb[i]);
-
- if (n_rgb[i])
- rgb[i] = rgb[i] / n_rgb[i];
-
- dev_dbg(vdeb->dev,
- "deb: %s: FINAL CALC: %dx%d Color %d, val %d\n",
- vdeb->sd.name, lin, col, i, rgb[i]);
- }
-}
-
-static void *vimc_deb_process_frame(struct vimc_ent_device *ved,
- const void *sink_frame)
-{
- struct vimc_deb_device *vdeb = container_of(ved, struct vimc_deb_device,
- ved);
- unsigned int rgb[3];
- unsigned int i, j;
-
- /* If the stream in this node is not active, just return */
- if (!vdeb->src_frame)
- return ERR_PTR(-EINVAL);
-
- for (i = 0; i < vdeb->sink_fmt.height; i++)
- for (j = 0; j < vdeb->sink_fmt.width; j++) {
- vimc_deb_calc_rgb_sink(vdeb, sink_frame, i, j, rgb);
- vdeb->set_rgb_src(vdeb, i, j, rgb);
- }
-
- return vdeb->src_frame;
-
-}
-
-static void vimc_deb_release(struct v4l2_subdev *sd)
-{
- struct vimc_deb_device *vdeb =
- container_of(sd, struct vimc_deb_device, sd);
-
- vimc_pads_cleanup(vdeb->ved.pads);
- kfree(vdeb);
-}
-
-static const struct v4l2_subdev_internal_ops vimc_deb_int_ops = {
- .release = vimc_deb_release,
-};
-
-static void vimc_deb_comp_unbind(struct device *comp, struct device *master,
- void *master_data)
-{
- struct vimc_ent_device *ved = dev_get_drvdata(comp);
- struct vimc_deb_device *vdeb = container_of(ved, struct vimc_deb_device,
- ved);
-
- vimc_ent_sd_unregister(ved, &vdeb->sd);
-}
-
-static int vimc_deb_comp_bind(struct device *comp, struct device *master,
- void *master_data)
-{
- struct v4l2_device *v4l2_dev = master_data;
- struct vimc_platform_data *pdata = comp->platform_data;
- struct vimc_deb_device *vdeb;
- int ret;
-
- /* Allocate the vdeb struct */
- vdeb = kzalloc(sizeof(*vdeb), GFP_KERNEL);
- if (!vdeb)
- return -ENOMEM;
-
- /* Initialize ved and sd */
- ret = vimc_ent_sd_register(&vdeb->ved, &vdeb->sd, v4l2_dev,
- pdata->entity_name,
- MEDIA_ENT_F_PROC_VIDEO_PIXEL_ENC_CONV, 2,
- (const unsigned long[2]) {MEDIA_PAD_FL_SINK,
- MEDIA_PAD_FL_SOURCE},
- &vimc_deb_int_ops, &vimc_deb_ops);
- if (ret) {
- kfree(vdeb);
- return ret;
- }
-
- vdeb->ved.process_frame = vimc_deb_process_frame;
- dev_set_drvdata(comp, &vdeb->ved);
- vdeb->dev = comp;
-
- /* Initialize the frame format */
- vdeb->sink_fmt = sink_fmt_default;
- /*
- * TODO: Add support for more output formats, we only support
- * RGB888 for now
- * NOTE: the src format is always the same as the sink, except
- * for the code
- */
- vdeb->src_code = MEDIA_BUS_FMT_RGB888_1X24;
- vdeb->set_rgb_src = vimc_deb_set_rgb_mbus_fmt_rgb888_1x24;
-
- return 0;
-}
-
-static const struct component_ops vimc_deb_comp_ops = {
- .bind = vimc_deb_comp_bind,
- .unbind = vimc_deb_comp_unbind,
-};
-
-static int vimc_deb_probe(struct platform_device *pdev)
-{
- return component_add(&pdev->dev, &vimc_deb_comp_ops);
-}
-
-static int vimc_deb_remove(struct platform_device *pdev)
-{
- component_del(&pdev->dev, &vimc_deb_comp_ops);
-
- return 0;
-}
-
-static const struct platform_device_id vimc_deb_driver_ids[] = {
- {
- .name = VIMC_DEB_DRV_NAME,
- },
- { }
-};
-
-static struct platform_driver vimc_deb_pdrv = {
- .probe = vimc_deb_probe,
- .remove = vimc_deb_remove,
- .id_table = vimc_deb_driver_ids,
- .driver = {
- .name = VIMC_DEB_DRV_NAME,
- },
-};
-
-module_platform_driver(vimc_deb_pdrv);
-
-MODULE_DEVICE_TABLE(platform, vimc_deb_driver_ids);
-
-MODULE_DESCRIPTION("Virtual Media Controller Driver (VIMC) Debayer");
-MODULE_AUTHOR("Helen Mae Koike Fornazier <helen.fornazier@gmail.com>");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/platform/vimc/vimc-scaler.c b/drivers/media/platform/vimc/vimc-scaler.c
deleted file mode 100644
index c0d9f43..0000000
--- a/drivers/media/platform/vimc/vimc-scaler.c
+++ /dev/null
@@ -1,439 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * vimc-scaler.c Virtual Media Controller Driver
- *
- * Copyright (C) 2015-2017 Helen Koike <helen.fornazier@gmail.com>
- */
-
-#include <linux/component.h>
-#include <linux/module.h>
-#include <linux/mod_devicetable.h>
-#include <linux/platform_device.h>
-#include <linux/vmalloc.h>
-#include <linux/v4l2-mediabus.h>
-#include <media/v4l2-subdev.h>
-
-#include "vimc-common.h"
-
-#define VIMC_SCA_DRV_NAME "vimc-scaler"
-
-static unsigned int sca_mult = 3;
-module_param(sca_mult, uint, 0000);
-MODULE_PARM_DESC(sca_mult, " the image size multiplier");
-
-#define IS_SINK(pad) (!pad)
-#define IS_SRC(pad) (pad)
-#define MAX_ZOOM 8
-
-struct vimc_sca_device {
- struct vimc_ent_device ved;
- struct v4l2_subdev sd;
- struct device *dev;
- /* NOTE: the source fmt is the same as the sink
- * with the width and hight multiplied by mult
- */
- struct v4l2_mbus_framefmt sink_fmt;
- /* Values calculated when the stream starts */
- u8 *src_frame;
- unsigned int src_line_size;
- unsigned int bpp;
-};
-
-static const struct v4l2_mbus_framefmt sink_fmt_default = {
- .width = 640,
- .height = 480,
- .code = MEDIA_BUS_FMT_RGB888_1X24,
- .field = V4L2_FIELD_NONE,
- .colorspace = V4L2_COLORSPACE_DEFAULT,
-};
-
-static int vimc_sca_init_cfg(struct v4l2_subdev *sd,
- struct v4l2_subdev_pad_config *cfg)
-{
- struct v4l2_mbus_framefmt *mf;
- unsigned int i;
-
- mf = v4l2_subdev_get_try_format(sd, cfg, 0);
- *mf = sink_fmt_default;
-
- for (i = 1; i < sd->entity.num_pads; i++) {
- mf = v4l2_subdev_get_try_format(sd, cfg, i);
- *mf = sink_fmt_default;
- mf->width = mf->width * sca_mult;
- mf->height = mf->height * sca_mult;
- }
-
- return 0;
-}
-
-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);
-
- /* We don't support bayer format */
- if (!vpix || vpix->bayer)
- return -EINVAL;
-
- code->code = vpix->code;
-
- return 0;
-}
-
-static int vimc_sca_enum_frame_size(struct v4l2_subdev *sd,
- struct v4l2_subdev_pad_config *cfg,
- struct v4l2_subdev_frame_size_enum *fse)
-{
- const struct vimc_pix_map *vpix;
-
- if (fse->index)
- return -EINVAL;
-
- /* Only accept code in the pix map table in non bayer format */
- vpix = vimc_pix_map_by_code(fse->code);
- if (!vpix || vpix->bayer)
- return -EINVAL;
-
- fse->min_width = VIMC_FRAME_MIN_WIDTH;
- fse->min_height = VIMC_FRAME_MIN_HEIGHT;
-
- if (IS_SINK(fse->pad)) {
- fse->max_width = VIMC_FRAME_MAX_WIDTH;
- fse->max_height = VIMC_FRAME_MAX_HEIGHT;
- } else {
- fse->max_width = VIMC_FRAME_MAX_WIDTH * MAX_ZOOM;
- fse->max_height = VIMC_FRAME_MAX_HEIGHT * MAX_ZOOM;
- }
-
- return 0;
-}
-
-static int vimc_sca_get_fmt(struct v4l2_subdev *sd,
- struct v4l2_subdev_pad_config *cfg,
- struct v4l2_subdev_format *format)
-{
- struct vimc_sca_device *vsca = v4l2_get_subdevdata(sd);
-
- /* Get the current sink format */
- format->format = (format->which == V4L2_SUBDEV_FORMAT_TRY) ?
- *v4l2_subdev_get_try_format(sd, cfg, 0) :
- vsca->sink_fmt;
-
- /* Scale the frame size for the source pad */
- if (IS_SRC(format->pad)) {
- format->format.width = vsca->sink_fmt.width * sca_mult;
- format->format.height = vsca->sink_fmt.height * sca_mult;
- }
-
- return 0;
-}
-
-static void vimc_sca_adjust_sink_fmt(struct v4l2_mbus_framefmt *fmt)
-{
- const struct vimc_pix_map *vpix;
-
- /* Only accept code in the pix map table in non bayer format */
- vpix = vimc_pix_map_by_code(fmt->code);
- if (!vpix || vpix->bayer)
- fmt->code = sink_fmt_default.code;
-
- fmt->width = clamp_t(u32, fmt->width, VIMC_FRAME_MIN_WIDTH,
- VIMC_FRAME_MAX_WIDTH) & ~1;
- fmt->height = clamp_t(u32, fmt->height, VIMC_FRAME_MIN_HEIGHT,
- VIMC_FRAME_MAX_HEIGHT) & ~1;
-
- if (fmt->field == V4L2_FIELD_ANY)
- fmt->field = sink_fmt_default.field;
-
- vimc_colorimetry_clamp(fmt);
-}
-
-static int vimc_sca_set_fmt(struct v4l2_subdev *sd,
- struct v4l2_subdev_pad_config *cfg,
- struct v4l2_subdev_format *fmt)
-{
- struct vimc_sca_device *vsca = v4l2_get_subdevdata(sd);
- struct v4l2_mbus_framefmt *sink_fmt;
-
- if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
- /* Do not change the format while stream is on */
- if (vsca->src_frame)
- return -EBUSY;
-
- sink_fmt = &vsca->sink_fmt;
- } else {
- sink_fmt = v4l2_subdev_get_try_format(sd, cfg, 0);
- }
-
- /*
- * Do not change the format of the source pad,
- * it is propagated from the sink
- */
- if (IS_SRC(fmt->pad)) {
- fmt->format = *sink_fmt;
- fmt->format.width = sink_fmt->width * sca_mult;
- fmt->format.height = sink_fmt->height * sca_mult;
- } else {
- /* Set the new format in the sink pad */
- vimc_sca_adjust_sink_fmt(&fmt->format);
-
- dev_dbg(vsca->dev, "%s: sink format update: "
- "old:%dx%d (0x%x, %d, %d, %d, %d) "
- "new:%dx%d (0x%x, %d, %d, %d, %d)\n", vsca->sd.name,
- /* old */
- sink_fmt->width, sink_fmt->height, sink_fmt->code,
- sink_fmt->colorspace, sink_fmt->quantization,
- sink_fmt->xfer_func, sink_fmt->ycbcr_enc,
- /* new */
- fmt->format.width, fmt->format.height, fmt->format.code,
- fmt->format.colorspace, fmt->format.quantization,
- fmt->format.xfer_func, fmt->format.ycbcr_enc);
-
- *sink_fmt = fmt->format;
- }
-
- return 0;
-}
-
-static const struct v4l2_subdev_pad_ops vimc_sca_pad_ops = {
- .init_cfg = vimc_sca_init_cfg,
- .enum_mbus_code = vimc_sca_enum_mbus_code,
- .enum_frame_size = vimc_sca_enum_frame_size,
- .get_fmt = vimc_sca_get_fmt,
- .set_fmt = vimc_sca_set_fmt,
-};
-
-static int vimc_sca_s_stream(struct v4l2_subdev *sd, int enable)
-{
- struct vimc_sca_device *vsca = v4l2_get_subdevdata(sd);
-
- if (enable) {
- const struct vimc_pix_map *vpix;
- unsigned int frame_size;
-
- if (vsca->src_frame)
- return 0;
-
- /* Save the bytes per pixel of the sink */
- vpix = vimc_pix_map_by_code(vsca->sink_fmt.code);
- vsca->bpp = vpix->bpp;
-
- /* Calculate the width in bytes of the src frame */
- vsca->src_line_size = vsca->sink_fmt.width *
- sca_mult * vsca->bpp;
-
- /* Calculate the frame size of the source pad */
- frame_size = vsca->src_line_size * vsca->sink_fmt.height *
- sca_mult;
-
- /* Allocate the frame buffer. Use vmalloc to be able to
- * allocate a large amount of memory
- */
- vsca->src_frame = vmalloc(frame_size);
- if (!vsca->src_frame)
- return -ENOMEM;
-
- } else {
- if (!vsca->src_frame)
- return 0;
-
- vfree(vsca->src_frame);
- vsca->src_frame = NULL;
- }
-
- return 0;
-}
-
-static const struct v4l2_subdev_video_ops vimc_sca_video_ops = {
- .s_stream = vimc_sca_s_stream,
-};
-
-static const struct v4l2_subdev_ops vimc_sca_ops = {
- .pad = &vimc_sca_pad_ops,
- .video = &vimc_sca_video_ops,
-};
-
-static void vimc_sca_fill_pix(u8 *const ptr,
- const u8 *const pixel,
- const unsigned int bpp)
-{
- unsigned int i;
-
- /* copy the pixel to the pointer */
- for (i = 0; i < bpp; i++)
- ptr[i] = pixel[i];
-}
-
-static void vimc_sca_scale_pix(const struct vimc_sca_device *const vsca,
- const unsigned int lin, const unsigned int col,
- const u8 *const sink_frame)
-{
- unsigned int i, j, index;
- const u8 *pixel;
-
- /* Point to the pixel value in position (lin, col) in the sink frame */
- index = VIMC_FRAME_INDEX(lin, col,
- vsca->sink_fmt.width,
- vsca->bpp);
- pixel = &sink_frame[index];
-
- dev_dbg(vsca->dev,
- "sca: %s: --- scale_pix sink pos %dx%d, index %d ---\n",
- vsca->sd.name, lin, col, index);
-
- /* point to the place we are going to put the first pixel
- * in the scaled src frame
- */
- index = VIMC_FRAME_INDEX(lin * sca_mult, col * sca_mult,
- vsca->sink_fmt.width * sca_mult, vsca->bpp);
-
- dev_dbg(vsca->dev, "sca: %s: scale_pix src pos %dx%d, index %d\n",
- vsca->sd.name, lin * sca_mult, col * sca_mult, index);
-
- /* Repeat this pixel mult times */
- for (i = 0; i < sca_mult; i++) {
- /* Iterate through each beginning of a
- * pixel repetition in a line
- */
- for (j = 0; j < sca_mult * vsca->bpp; j += vsca->bpp) {
- dev_dbg(vsca->dev,
- "sca: %s: sca: scale_pix src pos %d\n",
- vsca->sd.name, index + j);
-
- /* copy the pixel to the position index + j */
- vimc_sca_fill_pix(&vsca->src_frame[index + j],
- pixel, vsca->bpp);
- }
-
- /* move the index to the next line */
- index += vsca->src_line_size;
- }
-}
-
-static void vimc_sca_fill_src_frame(const struct vimc_sca_device *const vsca,
- const u8 *const sink_frame)
-{
- unsigned int i, j;
-
- /* Scale each pixel from the original sink frame */
- /* TODO: implement scale down, only scale up is supported for now */
- for (i = 0; i < vsca->sink_fmt.height; i++)
- for (j = 0; j < vsca->sink_fmt.width; j++)
- vimc_sca_scale_pix(vsca, i, j, sink_frame);
-}
-
-static void *vimc_sca_process_frame(struct vimc_ent_device *ved,
- const void *sink_frame)
-{
- struct vimc_sca_device *vsca = container_of(ved, struct vimc_sca_device,
- ved);
-
- /* If the stream in this node is not active, just return */
- if (!vsca->src_frame)
- return ERR_PTR(-EINVAL);
-
- vimc_sca_fill_src_frame(vsca, sink_frame);
-
- return vsca->src_frame;
-};
-
-static void vimc_sca_release(struct v4l2_subdev *sd)
-{
- struct vimc_sca_device *vsca =
- container_of(sd, struct vimc_sca_device, sd);
-
- vimc_pads_cleanup(vsca->ved.pads);
- kfree(vsca);
-}
-
-static const struct v4l2_subdev_internal_ops vimc_sca_int_ops = {
- .release = vimc_sca_release,
-};
-
-static void vimc_sca_comp_unbind(struct device *comp, struct device *master,
- void *master_data)
-{
- struct vimc_ent_device *ved = dev_get_drvdata(comp);
- struct vimc_sca_device *vsca = container_of(ved, struct vimc_sca_device,
- ved);
-
- vimc_ent_sd_unregister(ved, &vsca->sd);
-}
-
-
-static int vimc_sca_comp_bind(struct device *comp, struct device *master,
- void *master_data)
-{
- struct v4l2_device *v4l2_dev = master_data;
- struct vimc_platform_data *pdata = comp->platform_data;
- struct vimc_sca_device *vsca;
- int ret;
-
- /* Allocate the vsca struct */
- vsca = kzalloc(sizeof(*vsca), GFP_KERNEL);
- if (!vsca)
- return -ENOMEM;
-
- /* Initialize ved and sd */
- ret = vimc_ent_sd_register(&vsca->ved, &vsca->sd, v4l2_dev,
- pdata->entity_name,
- MEDIA_ENT_F_PROC_VIDEO_SCALER, 2,
- (const unsigned long[2]) {MEDIA_PAD_FL_SINK,
- MEDIA_PAD_FL_SOURCE},
- &vimc_sca_int_ops, &vimc_sca_ops);
- if (ret) {
- kfree(vsca);
- return ret;
- }
-
- vsca->ved.process_frame = vimc_sca_process_frame;
- dev_set_drvdata(comp, &vsca->ved);
- vsca->dev = comp;
-
- /* Initialize the frame format */
- vsca->sink_fmt = sink_fmt_default;
-
- return 0;
-}
-
-static const struct component_ops vimc_sca_comp_ops = {
- .bind = vimc_sca_comp_bind,
- .unbind = vimc_sca_comp_unbind,
-};
-
-static int vimc_sca_probe(struct platform_device *pdev)
-{
- return component_add(&pdev->dev, &vimc_sca_comp_ops);
-}
-
-static int vimc_sca_remove(struct platform_device *pdev)
-{
- component_del(&pdev->dev, &vimc_sca_comp_ops);
-
- return 0;
-}
-
-static const struct platform_device_id vimc_sca_driver_ids[] = {
- {
- .name = VIMC_SCA_DRV_NAME,
- },
- { }
-};
-
-static struct platform_driver vimc_sca_pdrv = {
- .probe = vimc_sca_probe,
- .remove = vimc_sca_remove,
- .id_table = vimc_sca_driver_ids,
- .driver = {
- .name = VIMC_SCA_DRV_NAME,
- },
-};
-
-module_platform_driver(vimc_sca_pdrv);
-
-MODULE_DEVICE_TABLE(platform, vimc_sca_driver_ids);
-
-MODULE_DESCRIPTION("Virtual Media Controller Driver (VIMC) Scaler");
-MODULE_AUTHOR("Helen Mae Koike Fornazier <helen.fornazier@gmail.com>");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/platform/vimc/vimc-sensor.c b/drivers/media/platform/vimc/vimc-sensor.c
deleted file mode 100644
index 420573e..0000000
--- a/drivers/media/platform/vimc/vimc-sensor.c
+++ /dev/null
@@ -1,439 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * vimc-sensor.c Virtual Media Controller Driver
- *
- * Copyright (C) 2015-2017 Helen Koike <helen.fornazier@gmail.com>
- */
-
-#include <linux/component.h>
-#include <linux/module.h>
-#include <linux/mod_devicetable.h>
-#include <linux/platform_device.h>
-#include <linux/v4l2-mediabus.h>
-#include <linux/vmalloc.h>
-#include <media/v4l2-ctrls.h>
-#include <media/v4l2-event.h>
-#include <media/v4l2-subdev.h>
-#include <media/tpg/v4l2-tpg.h>
-
-#include "vimc-common.h"
-
-#define VIMC_SEN_DRV_NAME "vimc-sensor"
-
-struct vimc_sen_device {
- struct vimc_ent_device ved;
- struct v4l2_subdev sd;
- struct device *dev;
- struct tpg_data tpg;
- u8 *frame;
- /* The active format */
- struct v4l2_mbus_framefmt mbus_format;
- struct v4l2_ctrl_handler hdl;
-};
-
-static const struct v4l2_mbus_framefmt fmt_default = {
- .width = 640,
- .height = 480,
- .code = MEDIA_BUS_FMT_RGB888_1X24,
- .field = V4L2_FIELD_NONE,
- .colorspace = V4L2_COLORSPACE_DEFAULT,
-};
-
-static int vimc_sen_init_cfg(struct v4l2_subdev *sd,
- struct v4l2_subdev_pad_config *cfg)
-{
- unsigned int i;
-
- for (i = 0; i < sd->entity.num_pads; i++) {
- struct v4l2_mbus_framefmt *mf;
-
- mf = v4l2_subdev_get_try_format(sd, cfg, i);
- *mf = fmt_default;
- }
-
- return 0;
-}
-
-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);
-
- if (!vpix)
- return -EINVAL;
-
- code->code = vpix->code;
-
- return 0;
-}
-
-static int vimc_sen_enum_frame_size(struct v4l2_subdev *sd,
- struct v4l2_subdev_pad_config *cfg,
- struct v4l2_subdev_frame_size_enum *fse)
-{
- const struct vimc_pix_map *vpix;
-
- if (fse->index)
- return -EINVAL;
-
- /* Only accept code in the pix map table */
- vpix = vimc_pix_map_by_code(fse->code);
- if (!vpix)
- return -EINVAL;
-
- fse->min_width = VIMC_FRAME_MIN_WIDTH;
- fse->max_width = VIMC_FRAME_MAX_WIDTH;
- fse->min_height = VIMC_FRAME_MIN_HEIGHT;
- fse->max_height = VIMC_FRAME_MAX_HEIGHT;
-
- return 0;
-}
-
-static int vimc_sen_get_fmt(struct v4l2_subdev *sd,
- struct v4l2_subdev_pad_config *cfg,
- struct v4l2_subdev_format *fmt)
-{
- struct vimc_sen_device *vsen =
- container_of(sd, struct vimc_sen_device, sd);
-
- fmt->format = fmt->which == V4L2_SUBDEV_FORMAT_TRY ?
- *v4l2_subdev_get_try_format(sd, cfg, fmt->pad) :
- vsen->mbus_format;
-
- return 0;
-}
-
-static void vimc_sen_tpg_s_format(struct vimc_sen_device *vsen)
-{
- const struct vimc_pix_map *vpix =
- vimc_pix_map_by_code(vsen->mbus_format.code);
-
- tpg_reset_source(&vsen->tpg, vsen->mbus_format.width,
- vsen->mbus_format.height, vsen->mbus_format.field);
- tpg_s_bytesperline(&vsen->tpg, 0, vsen->mbus_format.width * vpix->bpp);
- tpg_s_buf_height(&vsen->tpg, vsen->mbus_format.height);
- tpg_s_fourcc(&vsen->tpg, vpix->pixelformat);
- /* TODO: add support for V4L2_FIELD_ALTERNATE */
- tpg_s_field(&vsen->tpg, vsen->mbus_format.field, false);
- tpg_s_colorspace(&vsen->tpg, vsen->mbus_format.colorspace);
- tpg_s_ycbcr_enc(&vsen->tpg, vsen->mbus_format.ycbcr_enc);
- tpg_s_quantization(&vsen->tpg, vsen->mbus_format.quantization);
- tpg_s_xfer_func(&vsen->tpg, vsen->mbus_format.xfer_func);
-}
-
-static void vimc_sen_adjust_fmt(struct v4l2_mbus_framefmt *fmt)
-{
- const struct vimc_pix_map *vpix;
-
- /* Only accept code in the pix map table */
- vpix = vimc_pix_map_by_code(fmt->code);
- if (!vpix)
- fmt->code = fmt_default.code;
-
- fmt->width = clamp_t(u32, fmt->width, VIMC_FRAME_MIN_WIDTH,
- VIMC_FRAME_MAX_WIDTH) & ~1;
- fmt->height = clamp_t(u32, fmt->height, VIMC_FRAME_MIN_HEIGHT,
- VIMC_FRAME_MAX_HEIGHT) & ~1;
-
- /* TODO: add support for V4L2_FIELD_ALTERNATE */
- if (fmt->field == V4L2_FIELD_ANY || fmt->field == V4L2_FIELD_ALTERNATE)
- fmt->field = fmt_default.field;
-
- vimc_colorimetry_clamp(fmt);
-}
-
-static int vimc_sen_set_fmt(struct v4l2_subdev *sd,
- struct v4l2_subdev_pad_config *cfg,
- struct v4l2_subdev_format *fmt)
-{
- struct vimc_sen_device *vsen = v4l2_get_subdevdata(sd);
- struct v4l2_mbus_framefmt *mf;
-
- if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
- /* Do not change the format while stream is on */
- if (vsen->frame)
- return -EBUSY;
-
- mf = &vsen->mbus_format;
- } else {
- mf = v4l2_subdev_get_try_format(sd, cfg, fmt->pad);
- }
-
- /* Set the new format */
- vimc_sen_adjust_fmt(&fmt->format);
-
- dev_dbg(vsen->dev, "%s: format update: "
- "old:%dx%d (0x%x, %d, %d, %d, %d) "
- "new:%dx%d (0x%x, %d, %d, %d, %d)\n", vsen->sd.name,
- /* old */
- mf->width, mf->height, mf->code,
- mf->colorspace, mf->quantization,
- mf->xfer_func, mf->ycbcr_enc,
- /* new */
- fmt->format.width, fmt->format.height, fmt->format.code,
- fmt->format.colorspace, fmt->format.quantization,
- fmt->format.xfer_func, fmt->format.ycbcr_enc);
-
- *mf = fmt->format;
-
- return 0;
-}
-
-static const struct v4l2_subdev_pad_ops vimc_sen_pad_ops = {
- .init_cfg = vimc_sen_init_cfg,
- .enum_mbus_code = vimc_sen_enum_mbus_code,
- .enum_frame_size = vimc_sen_enum_frame_size,
- .get_fmt = vimc_sen_get_fmt,
- .set_fmt = vimc_sen_set_fmt,
-};
-
-static void *vimc_sen_process_frame(struct vimc_ent_device *ved,
- const void *sink_frame)
-{
- struct vimc_sen_device *vsen = container_of(ved, struct vimc_sen_device,
- ved);
-
- tpg_fill_plane_buffer(&vsen->tpg, 0, 0, vsen->frame);
- return vsen->frame;
-}
-
-static int vimc_sen_s_stream(struct v4l2_subdev *sd, int enable)
-{
- struct vimc_sen_device *vsen =
- container_of(sd, struct vimc_sen_device, sd);
-
- if (enable) {
- const struct vimc_pix_map *vpix;
- unsigned int frame_size;
-
- /* Calculate the frame size */
- vpix = vimc_pix_map_by_code(vsen->mbus_format.code);
- frame_size = vsen->mbus_format.width * vpix->bpp *
- vsen->mbus_format.height;
-
- /*
- * Allocate the frame buffer. Use vmalloc to be able to
- * allocate a large amount of memory
- */
- vsen->frame = vmalloc(frame_size);
- if (!vsen->frame)
- return -ENOMEM;
-
- /* configure the test pattern generator */
- vimc_sen_tpg_s_format(vsen);
-
- } else {
-
- vfree(vsen->frame);
- vsen->frame = NULL;
- }
-
- return 0;
-}
-
-static const struct v4l2_subdev_core_ops vimc_sen_core_ops = {
- .log_status = v4l2_ctrl_subdev_log_status,
- .subscribe_event = v4l2_ctrl_subdev_subscribe_event,
- .unsubscribe_event = v4l2_event_subdev_unsubscribe,
-};
-
-static const struct v4l2_subdev_video_ops vimc_sen_video_ops = {
- .s_stream = vimc_sen_s_stream,
-};
-
-static const struct v4l2_subdev_ops vimc_sen_ops = {
- .core = &vimc_sen_core_ops,
- .pad = &vimc_sen_pad_ops,
- .video = &vimc_sen_video_ops,
-};
-
-static int vimc_sen_s_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct vimc_sen_device *vsen =
- container_of(ctrl->handler, struct vimc_sen_device, hdl);
-
- switch (ctrl->id) {
- case VIMC_CID_TEST_PATTERN:
- tpg_s_pattern(&vsen->tpg, ctrl->val);
- break;
- case V4L2_CID_HFLIP:
- tpg_s_hflip(&vsen->tpg, ctrl->val);
- break;
- case V4L2_CID_VFLIP:
- tpg_s_vflip(&vsen->tpg, ctrl->val);
- break;
- case V4L2_CID_BRIGHTNESS:
- tpg_s_brightness(&vsen->tpg, ctrl->val);
- break;
- case V4L2_CID_CONTRAST:
- tpg_s_contrast(&vsen->tpg, ctrl->val);
- break;
- case V4L2_CID_HUE:
- tpg_s_hue(&vsen->tpg, ctrl->val);
- break;
- case V4L2_CID_SATURATION:
- tpg_s_saturation(&vsen->tpg, ctrl->val);
- break;
- default:
- return -EINVAL;
- }
- return 0;
-}
-
-static const struct v4l2_ctrl_ops vimc_sen_ctrl_ops = {
- .s_ctrl = vimc_sen_s_ctrl,
-};
-
-static void vimc_sen_release(struct v4l2_subdev *sd)
-{
- struct vimc_sen_device *vsen =
- container_of(sd, struct vimc_sen_device, sd);
-
- v4l2_ctrl_handler_free(&vsen->hdl);
- tpg_free(&vsen->tpg);
- vimc_pads_cleanup(vsen->ved.pads);
- kfree(vsen);
-}
-
-static const struct v4l2_subdev_internal_ops vimc_sen_int_ops = {
- .release = vimc_sen_release,
-};
-
-static void vimc_sen_comp_unbind(struct device *comp, struct device *master,
- void *master_data)
-{
- struct vimc_ent_device *ved = dev_get_drvdata(comp);
- struct vimc_sen_device *vsen =
- container_of(ved, struct vimc_sen_device, ved);
-
- vimc_ent_sd_unregister(ved, &vsen->sd);
-}
-
-/* Image Processing Controls */
-static const struct v4l2_ctrl_config vimc_sen_ctrl_class = {
- .flags = V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_WRITE_ONLY,
- .id = VIMC_CID_VIMC_CLASS,
- .name = "VIMC Controls",
- .type = V4L2_CTRL_TYPE_CTRL_CLASS,
-};
-
-static const struct v4l2_ctrl_config vimc_sen_ctrl_test_pattern = {
- .ops = &vimc_sen_ctrl_ops,
- .id = VIMC_CID_TEST_PATTERN,
- .name = "Test Pattern",
- .type = V4L2_CTRL_TYPE_MENU,
- .max = TPG_PAT_NOISE,
- .qmenu = tpg_pattern_strings,
-};
-
-static int vimc_sen_comp_bind(struct device *comp, struct device *master,
- void *master_data)
-{
- struct v4l2_device *v4l2_dev = master_data;
- struct vimc_platform_data *pdata = comp->platform_data;
- struct vimc_sen_device *vsen;
- int ret;
-
- /* Allocate the vsen struct */
- vsen = kzalloc(sizeof(*vsen), GFP_KERNEL);
- if (!vsen)
- return -ENOMEM;
-
- v4l2_ctrl_handler_init(&vsen->hdl, 4);
-
- v4l2_ctrl_new_custom(&vsen->hdl, &vimc_sen_ctrl_class, NULL);
- v4l2_ctrl_new_custom(&vsen->hdl, &vimc_sen_ctrl_test_pattern, NULL);
- v4l2_ctrl_new_std(&vsen->hdl, &vimc_sen_ctrl_ops,
- V4L2_CID_VFLIP, 0, 1, 1, 0);
- v4l2_ctrl_new_std(&vsen->hdl, &vimc_sen_ctrl_ops,
- V4L2_CID_HFLIP, 0, 1, 1, 0);
- v4l2_ctrl_new_std(&vsen->hdl, &vimc_sen_ctrl_ops,
- V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
- v4l2_ctrl_new_std(&vsen->hdl, &vimc_sen_ctrl_ops,
- V4L2_CID_CONTRAST, 0, 255, 1, 128);
- v4l2_ctrl_new_std(&vsen->hdl, &vimc_sen_ctrl_ops,
- V4L2_CID_HUE, -128, 127, 1, 0);
- v4l2_ctrl_new_std(&vsen->hdl, &vimc_sen_ctrl_ops,
- V4L2_CID_SATURATION, 0, 255, 1, 128);
- vsen->sd.ctrl_handler = &vsen->hdl;
- if (vsen->hdl.error) {
- ret = vsen->hdl.error;
- goto err_free_vsen;
- }
-
- /* Initialize ved and sd */
- ret = vimc_ent_sd_register(&vsen->ved, &vsen->sd, v4l2_dev,
- pdata->entity_name,
- MEDIA_ENT_F_CAM_SENSOR, 1,
- (const unsigned long[1]) {MEDIA_PAD_FL_SOURCE},
- &vimc_sen_int_ops, &vimc_sen_ops);
- if (ret)
- goto err_free_hdl;
-
- vsen->ved.process_frame = vimc_sen_process_frame;
- dev_set_drvdata(comp, &vsen->ved);
- vsen->dev = comp;
-
- /* Initialize the frame format */
- vsen->mbus_format = fmt_default;
-
- /* Initialize the test pattern generator */
- tpg_init(&vsen->tpg, vsen->mbus_format.width,
- vsen->mbus_format.height);
- ret = tpg_alloc(&vsen->tpg, VIMC_FRAME_MAX_WIDTH);
- if (ret)
- goto err_unregister_ent_sd;
-
- return 0;
-
-err_unregister_ent_sd:
- vimc_ent_sd_unregister(&vsen->ved, &vsen->sd);
-err_free_hdl:
- v4l2_ctrl_handler_free(&vsen->hdl);
-err_free_vsen:
- kfree(vsen);
-
- return ret;
-}
-
-static const struct component_ops vimc_sen_comp_ops = {
- .bind = vimc_sen_comp_bind,
- .unbind = vimc_sen_comp_unbind,
-};
-
-static int vimc_sen_probe(struct platform_device *pdev)
-{
- return component_add(&pdev->dev, &vimc_sen_comp_ops);
-}
-
-static int vimc_sen_remove(struct platform_device *pdev)
-{
- component_del(&pdev->dev, &vimc_sen_comp_ops);
-
- return 0;
-}
-
-static const struct platform_device_id vimc_sen_driver_ids[] = {
- {
- .name = VIMC_SEN_DRV_NAME,
- },
- { }
-};
-
-static struct platform_driver vimc_sen_pdrv = {
- .probe = vimc_sen_probe,
- .remove = vimc_sen_remove,
- .id_table = vimc_sen_driver_ids,
- .driver = {
- .name = VIMC_SEN_DRV_NAME,
- },
-};
-
-module_platform_driver(vimc_sen_pdrv);
-
-MODULE_DEVICE_TABLE(platform, vimc_sen_driver_ids);
-
-MODULE_DESCRIPTION("Virtual Media Controller Driver (VIMC) Sensor");
-MODULE_AUTHOR("Helen Mae Koike Fornazier <helen.fornazier@gmail.com>");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/platform/vimc/vimc-streamer.c b/drivers/media/platform/vimc/vimc-streamer.c
deleted file mode 100644
index 048d770..0000000
--- a/drivers/media/platform/vimc/vimc-streamer.c
+++ /dev/null
@@ -1,220 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * vimc-streamer.c Virtual Media Controller Driver
- *
- * Copyright (C) 2018 Lucas A. M. Magalhães <lucmaga@gmail.com>
- *
- */
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/freezer.h>
-#include <linux/kthread.h>
-
-#include "vimc-streamer.h"
-
-/**
- * vimc_get_source_entity - get the entity connected with the first sink pad
- *
- * @ent: reference media_entity
- *
- * Helper function that returns the media entity containing the source pad
- * linked with the first sink pad from the given media entity pad list.
- *
- * Return: The source pad or NULL, if it wasn't found.
- */
-static struct media_entity *vimc_get_source_entity(struct media_entity *ent)
-{
- struct media_pad *pad;
- int i;
-
- for (i = 0; i < ent->num_pads; i++) {
- if (ent->pads[i].flags & MEDIA_PAD_FL_SOURCE)
- continue;
- pad = media_entity_remote_pad(&ent->pads[i]);
- return pad ? pad->entity : NULL;
- }
- return NULL;
-}
-
-/**
- * vimc_streamer_pipeline_terminate - Disable stream in all ved in stream
- *
- * @stream: the pointer to the stream structure with the pipeline to be
- * disabled.
- *
- * Calls s_stream to disable the stream in each entity of the pipeline
- *
- */
-static void vimc_streamer_pipeline_terminate(struct vimc_stream *stream)
-{
- struct vimc_ent_device *ved;
- struct v4l2_subdev *sd;
-
- while (stream->pipe_size) {
- stream->pipe_size--;
- ved = stream->ved_pipeline[stream->pipe_size];
- stream->ved_pipeline[stream->pipe_size] = NULL;
-
- if (!is_media_entity_v4l2_subdev(ved->ent))
- continue;
-
- sd = media_entity_to_v4l2_subdev(ved->ent);
- v4l2_subdev_call(sd, video, s_stream, 0);
- }
-}
-
-/**
- * vimc_streamer_pipeline_init - Initializes the stream structure
- *
- * @stream: the pointer to the stream structure to be initialized
- * @ved: the pointer to the vimc entity initializing the stream
- *
- * Initializes the stream structure. Walks through the entity graph to
- * construct the pipeline used later on the streamer thread.
- * Calls vimc_streamer_s_stream() to enable stream in all entities of
- * the pipeline.
- *
- * Return: 0 if success, error code otherwise.
- */
-static int vimc_streamer_pipeline_init(struct vimc_stream *stream,
- struct vimc_ent_device *ved)
-{
- struct media_entity *entity;
- struct video_device *vdev;
- struct v4l2_subdev *sd;
- int ret = 0;
-
- stream->pipe_size = 0;
- while (stream->pipe_size < VIMC_STREAMER_PIPELINE_MAX_SIZE) {
- if (!ved) {
- vimc_streamer_pipeline_terminate(stream);
- return -EINVAL;
- }
- stream->ved_pipeline[stream->pipe_size++] = ved;
-
- if (is_media_entity_v4l2_subdev(ved->ent)) {
- sd = media_entity_to_v4l2_subdev(ved->ent);
- ret = v4l2_subdev_call(sd, video, s_stream, 1);
- if (ret && ret != -ENOIOCTLCMD) {
- pr_err("subdev_call error %s\n",
- ved->ent->name);
- vimc_streamer_pipeline_terminate(stream);
- return ret;
- }
- }
-
- entity = vimc_get_source_entity(ved->ent);
- /* Check if the end of the pipeline was reached*/
- if (!entity)
- return 0;
-
- /* Get the next device in the pipeline */
- if (is_media_entity_v4l2_subdev(entity)) {
- sd = media_entity_to_v4l2_subdev(entity);
- ved = v4l2_get_subdevdata(sd);
- } else {
- vdev = container_of(entity,
- struct video_device,
- entity);
- ved = video_get_drvdata(vdev);
- }
- }
-
- vimc_streamer_pipeline_terminate(stream);
- return -EINVAL;
-}
-
-/**
- * vimc_streamer_thread - Process frames through the pipeline
- *
- * @data: vimc_stream struct of the current stream
- *
- * From the source to the sink, gets a frame from each subdevice and send to
- * the next one of the pipeline at a fixed framerate.
- *
- * Return:
- * Always zero (created as ``int`` instead of ``void`` to comply with
- * kthread API).
- */
-static int vimc_streamer_thread(void *data)
-{
- struct vimc_stream *stream = data;
- u8 *frame = NULL;
- int i;
-
- set_freezable();
-
- for (;;) {
- try_to_freeze();
- if (kthread_should_stop())
- break;
-
- for (i = stream->pipe_size - 1; i >= 0; i--) {
- frame = stream->ved_pipeline[i]->process_frame(
- stream->ved_pipeline[i], frame);
- if (!frame || IS_ERR(frame))
- break;
- }
- //wait for 60hz
- set_current_state(TASK_UNINTERRUPTIBLE);
- schedule_timeout(HZ / 60);
- }
-
- return 0;
-}
-
-/**
- * vimc_streamer_s_stream - Start/stop the streaming on the media pipeline
- *
- * @stream: the pointer to the stream structure of the current stream
- * @ved: pointer to the vimc entity of the entity of the stream
- * @enable: flag to determine if stream should start/stop
- *
- * When starting, check if there is no ``stream->kthread`` allocated. This
- * should indicate that a stream is already running. Then, it initializes the
- * pipeline, creates and runs a kthread to consume buffers through the pipeline.
- * When stopping, analogously check if there is a stream running, stop the
- * thread and terminates the pipeline.
- *
- * Return: 0 if success, error code otherwise.
- */
-int vimc_streamer_s_stream(struct vimc_stream *stream,
- struct vimc_ent_device *ved,
- int enable)
-{
- int ret;
-
- if (!stream || !ved)
- return -EINVAL;
-
- if (enable) {
- if (stream->kthread)
- return 0;
-
- ret = vimc_streamer_pipeline_init(stream, ved);
- if (ret)
- return ret;
-
- stream->kthread = kthread_run(vimc_streamer_thread, stream,
- "vimc-streamer thread");
-
- if (IS_ERR(stream->kthread))
- return PTR_ERR(stream->kthread);
-
- } else {
- if (!stream->kthread)
- return 0;
-
- ret = kthread_stop(stream->kthread);
- if (ret)
- return ret;
-
- stream->kthread = NULL;
-
- vimc_streamer_pipeline_terminate(stream);
- }
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(vimc_streamer_s_stream);
diff --git a/drivers/media/platform/vimc/vimc-streamer.h b/drivers/media/platform/vimc/vimc-streamer.h
deleted file mode 100644
index fe3c51f..0000000
--- a/drivers/media/platform/vimc/vimc-streamer.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0+ */
-/*
- * vimc-streamer.h Virtual Media Controller Driver
- *
- * Copyright (C) 2018 Lucas A. M. Magalhães <lucmaga@gmail.com>
- *
- */
-
-#ifndef _VIMC_STREAMER_H_
-#define _VIMC_STREAMER_H_
-
-#include <media/media-device.h>
-
-#include "vimc-common.h"
-
-#define VIMC_STREAMER_PIPELINE_MAX_SIZE 16
-
-/**
- * struct vimc_stream - struct that represents a stream in the pipeline
- *
- * @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.
- * @pipe_size: size of @ved_pipeline
- * @kthread: thread that generates the frames of the stream.
- *
- * When the user call stream_on in a video device, struct vimc_stream is
- * used to keep track of all entities and subdevices that generates and
- * process frames for the stream.
- */
-struct vimc_stream {
- struct media_pipeline pipe;
- struct vimc_ent_device *ved_pipeline[VIMC_STREAMER_PIPELINE_MAX_SIZE];
- unsigned int pipe_size;
- struct task_struct *kthread;
-};
-
-int vimc_streamer_s_stream(struct vimc_stream *stream,
- struct vimc_ent_device *ved,
- int enable);
-
-#endif //_VIMC_STREAMER_H_
diff --git a/drivers/media/platform/vivid/Kconfig b/drivers/media/platform/vivid/Kconfig
deleted file mode 100644
index e2ff06e..0000000
--- a/drivers/media/platform/vivid/Kconfig
+++ /dev/null
@@ -1,41 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0-only
-config VIDEO_VIVID
- tristate "Virtual Video Test Driver"
- depends on VIDEO_DEV && VIDEO_V4L2 && !SPARC32 && !SPARC64 && FB
- depends on HAS_DMA
- select FONT_SUPPORT
- select FONT_8x16
- select FB_CFB_FILLRECT
- select FB_CFB_COPYAREA
- select FB_CFB_IMAGEBLIT
- select VIDEOBUF2_VMALLOC
- select VIDEOBUF2_DMA_CONTIG
- select VIDEO_V4L2_TPG
- help
- Enables a virtual video driver. This driver emulates a webcam,
- TV, S-Video and HDMI capture hardware, including VBI support for
- the SDTV inputs. Also video output, VBI output, radio receivers,
- transmitters and software defined radio capture is emulated.
-
- It is highly configurable and is ideal for testing applications.
- Error injection is supported to test rare errors that are hard
- to reproduce in real hardware.
-
- Say Y here if you want to test video apps or debug V4L devices.
- When in doubt, say N.
-
-config VIDEO_VIVID_CEC
- bool "Enable CEC emulation support"
- depends on VIDEO_VIVID
- select CEC_CORE
- help
- When selected the vivid module will emulate the optional
- HDMI CEC feature.
-
-config VIDEO_VIVID_MAX_DEVS
- int "Maximum number of devices"
- depends on VIDEO_VIVID
- default "64"
- help
- This allows you to specify the maximum number of devices supported
- by the vivid driver.
diff --git a/drivers/media/platform/vivid/Makefile b/drivers/media/platform/vivid/Makefile
deleted file mode 100644
index 2f5762e..0000000
--- a/drivers/media/platform/vivid/Makefile
+++ /dev/null
@@ -1,11 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0
-vivid-objs := vivid-core.o vivid-ctrls.o vivid-vid-common.o vivid-vbi-gen.o \
- vivid-vid-cap.o vivid-vid-out.o vivid-kthread-cap.o vivid-kthread-out.o \
- vivid-radio-rx.o vivid-radio-tx.o vivid-radio-common.o \
- vivid-rds-gen.o vivid-sdr-cap.o vivid-vbi-cap.o vivid-vbi-out.o \
- vivid-osd.o
-ifeq ($(CONFIG_VIDEO_VIVID_CEC),y)
- vivid-objs += vivid-cec.o
-endif
-
-obj-$(CONFIG_VIDEO_VIVID) += vivid.o
diff --git a/drivers/media/platform/vivid/vivid-cec.c b/drivers/media/platform/vivid/vivid-cec.c
deleted file mode 100644
index 4d822db..0000000
--- a/drivers/media/platform/vivid/vivid-cec.c
+++ /dev/null
@@ -1,287 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * vivid-cec.c - A Virtual Video Test Driver, cec emulation
- *
- * Copyright 2016 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- */
-
-#include <media/cec.h>
-
-#include "vivid-core.h"
-#include "vivid-cec.h"
-
-#define CEC_TIM_START_BIT_TOTAL 4500
-#define CEC_TIM_START_BIT_LOW 3700
-#define CEC_TIM_START_BIT_HIGH 800
-#define CEC_TIM_DATA_BIT_TOTAL 2400
-#define CEC_TIM_DATA_BIT_0_LOW 1500
-#define CEC_TIM_DATA_BIT_0_HIGH 900
-#define CEC_TIM_DATA_BIT_1_LOW 600
-#define CEC_TIM_DATA_BIT_1_HIGH 1800
-
-void vivid_cec_bus_free_work(struct vivid_dev *dev)
-{
- spin_lock(&dev->cec_slock);
- while (!list_empty(&dev->cec_work_list)) {
- struct vivid_cec_work *cw =
- list_first_entry(&dev->cec_work_list,
- struct vivid_cec_work, list);
-
- spin_unlock(&dev->cec_slock);
- cancel_delayed_work_sync(&cw->work);
- spin_lock(&dev->cec_slock);
- list_del(&cw->list);
- cec_transmit_attempt_done(cw->adap, CEC_TX_STATUS_LOW_DRIVE);
- kfree(cw);
- }
- spin_unlock(&dev->cec_slock);
-}
-
-static bool vivid_cec_find_dest_adap(struct vivid_dev *dev,
- struct cec_adapter *adap, u8 dest)
-{
- unsigned int i;
-
- if (dest >= 0xf)
- return false;
-
- if (adap != dev->cec_rx_adap && dev->cec_rx_adap &&
- dev->cec_rx_adap->is_configured &&
- cec_has_log_addr(dev->cec_rx_adap, dest))
- return true;
-
- for (i = 0; i < MAX_OUTPUTS && dev->cec_tx_adap[i]; i++) {
- if (adap == dev->cec_tx_adap[i])
- continue;
- if (!dev->cec_tx_adap[i]->is_configured)
- continue;
- if (cec_has_log_addr(dev->cec_tx_adap[i], dest))
- return true;
- }
- return false;
-}
-
-static void vivid_cec_pin_adap_events(struct cec_adapter *adap, ktime_t ts,
- const struct cec_msg *msg, bool nacked)
-{
- unsigned int len = nacked ? 1 : msg->len;
- unsigned int i;
- bool bit;
-
- if (adap == NULL)
- return;
-
- /*
- * Suffix ULL on constant 10 makes the expression
- * CEC_TIM_START_BIT_TOTAL + 10ULL * len * CEC_TIM_DATA_BIT_TOTAL
- * to be evaluated using 64-bit unsigned arithmetic (u64), which
- * is what ktime_sub_us expects as second argument.
- */
- ts = ktime_sub_us(ts, CEC_TIM_START_BIT_TOTAL +
- 10ULL * len * CEC_TIM_DATA_BIT_TOTAL);
- cec_queue_pin_cec_event(adap, false, false, ts);
- ts = ktime_add_us(ts, CEC_TIM_START_BIT_LOW);
- cec_queue_pin_cec_event(adap, true, false, ts);
- ts = ktime_add_us(ts, CEC_TIM_START_BIT_HIGH);
-
- for (i = 0; i < 10 * len; i++) {
- switch (i % 10) {
- case 0 ... 7:
- bit = msg->msg[i / 10] & (0x80 >> (i % 10));
- break;
- case 8: /* EOM */
- bit = i / 10 == msg->len - 1;
- break;
- case 9: /* ACK */
- bit = cec_msg_is_broadcast(msg) ^ nacked;
- break;
- }
- cec_queue_pin_cec_event(adap, false, false, ts);
- if (bit)
- ts = ktime_add_us(ts, CEC_TIM_DATA_BIT_1_LOW);
- else
- ts = ktime_add_us(ts, CEC_TIM_DATA_BIT_0_LOW);
- cec_queue_pin_cec_event(adap, true, false, ts);
- if (bit)
- ts = ktime_add_us(ts, CEC_TIM_DATA_BIT_1_HIGH);
- else
- ts = ktime_add_us(ts, CEC_TIM_DATA_BIT_0_HIGH);
- }
-}
-
-static void vivid_cec_pin_events(struct vivid_dev *dev,
- const struct cec_msg *msg, bool nacked)
-{
- ktime_t ts = ktime_get();
- unsigned int i;
-
- vivid_cec_pin_adap_events(dev->cec_rx_adap, ts, msg, nacked);
- for (i = 0; i < MAX_OUTPUTS; i++)
- vivid_cec_pin_adap_events(dev->cec_tx_adap[i], ts, msg, nacked);
-}
-
-static void vivid_cec_xfer_done_worker(struct work_struct *work)
-{
- struct vivid_cec_work *cw =
- container_of(work, struct vivid_cec_work, work.work);
- struct vivid_dev *dev = cw->dev;
- struct cec_adapter *adap = cw->adap;
- u8 dest = cec_msg_destination(&cw->msg);
- bool valid_dest;
- unsigned int i;
-
- valid_dest = cec_msg_is_broadcast(&cw->msg);
- if (!valid_dest)
- valid_dest = vivid_cec_find_dest_adap(dev, adap, dest);
-
- cw->tx_status = valid_dest ? CEC_TX_STATUS_OK : CEC_TX_STATUS_NACK;
- spin_lock(&dev->cec_slock);
- dev->cec_xfer_time_jiffies = 0;
- dev->cec_xfer_start_jiffies = 0;
- list_del(&cw->list);
- spin_unlock(&dev->cec_slock);
- vivid_cec_pin_events(dev, &cw->msg, !valid_dest);
- cec_transmit_attempt_done(cw->adap, cw->tx_status);
-
- /* Broadcast message */
- if (adap != dev->cec_rx_adap)
- cec_received_msg(dev->cec_rx_adap, &cw->msg);
- for (i = 0; i < MAX_OUTPUTS && dev->cec_tx_adap[i]; i++)
- if (adap != dev->cec_tx_adap[i])
- cec_received_msg(dev->cec_tx_adap[i], &cw->msg);
- kfree(cw);
-}
-
-static void vivid_cec_xfer_try_worker(struct work_struct *work)
-{
- struct vivid_cec_work *cw =
- container_of(work, struct vivid_cec_work, work.work);
- struct vivid_dev *dev = cw->dev;
-
- spin_lock(&dev->cec_slock);
- if (dev->cec_xfer_time_jiffies) {
- list_del(&cw->list);
- spin_unlock(&dev->cec_slock);
- cec_transmit_attempt_done(cw->adap, CEC_TX_STATUS_ARB_LOST);
- kfree(cw);
- } else {
- INIT_DELAYED_WORK(&cw->work, vivid_cec_xfer_done_worker);
- dev->cec_xfer_start_jiffies = jiffies;
- dev->cec_xfer_time_jiffies = usecs_to_jiffies(cw->usecs);
- spin_unlock(&dev->cec_slock);
- schedule_delayed_work(&cw->work, dev->cec_xfer_time_jiffies);
- }
-}
-
-static int vivid_cec_adap_enable(struct cec_adapter *adap, bool enable)
-{
- adap->cec_pin_is_high = true;
- return 0;
-}
-
-static int vivid_cec_adap_log_addr(struct cec_adapter *adap, u8 log_addr)
-{
- return 0;
-}
-
-/*
- * One data bit takes 2400 us, each byte needs 10 bits so that's 24000 us
- * per byte.
- */
-#define USECS_PER_BYTE 24000
-
-static int vivid_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,
- u32 signal_free_time, struct cec_msg *msg)
-{
- struct vivid_dev *dev = cec_get_drvdata(adap);
- struct vivid_cec_work *cw = kzalloc(sizeof(*cw), GFP_KERNEL);
- long delta_jiffies = 0;
-
- if (cw == NULL)
- return -ENOMEM;
- cw->dev = dev;
- cw->adap = adap;
- cw->usecs = CEC_FREE_TIME_TO_USEC(signal_free_time) +
- msg->len * USECS_PER_BYTE;
- cw->msg = *msg;
-
- spin_lock(&dev->cec_slock);
- list_add(&cw->list, &dev->cec_work_list);
- if (dev->cec_xfer_time_jiffies == 0) {
- INIT_DELAYED_WORK(&cw->work, vivid_cec_xfer_done_worker);
- dev->cec_xfer_start_jiffies = jiffies;
- dev->cec_xfer_time_jiffies = usecs_to_jiffies(cw->usecs);
- delta_jiffies = dev->cec_xfer_time_jiffies;
- } else {
- INIT_DELAYED_WORK(&cw->work, vivid_cec_xfer_try_worker);
- delta_jiffies = dev->cec_xfer_start_jiffies +
- dev->cec_xfer_time_jiffies - jiffies;
- }
- spin_unlock(&dev->cec_slock);
- schedule_delayed_work(&cw->work, delta_jiffies < 0 ? 0 : delta_jiffies);
- return 0;
-}
-
-static int vivid_received(struct cec_adapter *adap, struct cec_msg *msg)
-{
- struct vivid_dev *dev = cec_get_drvdata(adap);
- struct cec_msg reply;
- u8 dest = cec_msg_destination(msg);
- u8 disp_ctl;
- char osd[14];
-
- if (cec_msg_is_broadcast(msg))
- dest = adap->log_addrs.log_addr[0];
- cec_msg_init(&reply, dest, cec_msg_initiator(msg));
-
- switch (cec_msg_opcode(msg)) {
- case CEC_MSG_SET_OSD_STRING:
- if (!cec_is_sink(adap))
- return -ENOMSG;
- cec_ops_set_osd_string(msg, &disp_ctl, osd);
- switch (disp_ctl) {
- case CEC_OP_DISP_CTL_DEFAULT:
- strscpy(dev->osd, osd, sizeof(dev->osd));
- dev->osd_jiffies = jiffies;
- break;
- case CEC_OP_DISP_CTL_UNTIL_CLEARED:
- strscpy(dev->osd, osd, sizeof(dev->osd));
- dev->osd_jiffies = 0;
- break;
- case CEC_OP_DISP_CTL_CLEAR:
- dev->osd[0] = 0;
- dev->osd_jiffies = 0;
- break;
- default:
- cec_msg_feature_abort(&reply, cec_msg_opcode(msg),
- CEC_OP_ABORT_INVALID_OP);
- cec_transmit_msg(adap, &reply, false);
- break;
- }
- break;
- default:
- return -ENOMSG;
- }
- return 0;
-}
-
-static const struct cec_adap_ops vivid_cec_adap_ops = {
- .adap_enable = vivid_cec_adap_enable,
- .adap_log_addr = vivid_cec_adap_log_addr,
- .adap_transmit = vivid_cec_adap_transmit,
- .received = vivid_received,
-};
-
-struct cec_adapter *vivid_cec_alloc_adap(struct vivid_dev *dev,
- unsigned int idx,
- bool is_source)
-{
- char name[sizeof(dev->vid_out_dev.name) + 2];
- u32 caps = CEC_CAP_DEFAULTS | CEC_CAP_MONITOR_ALL | CEC_CAP_MONITOR_PIN;
-
- snprintf(name, sizeof(name), "%s%d",
- is_source ? dev->vid_out_dev.name : dev->vid_cap_dev.name,
- idx);
- return cec_allocate_adapter(&vivid_cec_adap_ops, dev,
- name, caps, 1);
-}
diff --git a/drivers/media/platform/vivid/vivid-cec.h b/drivers/media/platform/vivid/vivid-cec.h
deleted file mode 100644
index 7524ed4..0000000
--- a/drivers/media/platform/vivid/vivid-cec.h
+++ /dev/null
@@ -1,20 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-/*
- * vivid-cec.h - A Virtual Video Test Driver, cec emulation
- *
- * Copyright 2016 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- */
-
-#ifdef CONFIG_VIDEO_VIVID_CEC
-struct cec_adapter *vivid_cec_alloc_adap(struct vivid_dev *dev,
- unsigned int idx,
- bool is_source);
-void vivid_cec_bus_free_work(struct vivid_dev *dev);
-
-#else
-
-static inline void vivid_cec_bus_free_work(struct vivid_dev *dev)
-{
-}
-
-#endif
diff --git a/drivers/media/platform/vivid/vivid-core.c b/drivers/media/platform/vivid/vivid-core.c
deleted file mode 100644
index cc71aa4..0000000
--- a/drivers/media/platform/vivid/vivid-core.c
+++ /dev/null
@@ -1,1681 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * vivid-core.c - A Virtual Video Test Driver, core initialization
- *
- * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- */
-
-#include <linux/module.h>
-#include <linux/errno.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include <linux/vmalloc.h>
-#include <linux/font.h>
-#include <linux/mutex.h>
-#include <linux/platform_device.h>
-#include <linux/videodev2.h>
-#include <linux/v4l2-dv-timings.h>
-#include <media/videobuf2-vmalloc.h>
-#include <media/videobuf2-dma-contig.h>
-#include <media/v4l2-dv-timings.h>
-#include <media/v4l2-ioctl.h>
-#include <media/v4l2-fh.h>
-#include <media/v4l2-event.h>
-
-#include "vivid-core.h"
-#include "vivid-vid-common.h"
-#include "vivid-vid-cap.h"
-#include "vivid-vid-out.h"
-#include "vivid-radio-common.h"
-#include "vivid-radio-rx.h"
-#include "vivid-radio-tx.h"
-#include "vivid-sdr-cap.h"
-#include "vivid-vbi-cap.h"
-#include "vivid-vbi-out.h"
-#include "vivid-osd.h"
-#include "vivid-cec.h"
-#include "vivid-ctrls.h"
-
-#define VIVID_MODULE_NAME "vivid"
-
-/* The maximum number of vivid devices */
-#define VIVID_MAX_DEVS CONFIG_VIDEO_VIVID_MAX_DEVS
-
-MODULE_DESCRIPTION("Virtual Video Test Driver");
-MODULE_AUTHOR("Hans Verkuil");
-MODULE_LICENSE("GPL");
-
-static unsigned n_devs = 1;
-module_param(n_devs, uint, 0444);
-MODULE_PARM_DESC(n_devs, " number of driver instances to create");
-
-static int vid_cap_nr[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = -1 };
-module_param_array(vid_cap_nr, int, NULL, 0444);
-MODULE_PARM_DESC(vid_cap_nr, " videoX start number, -1 is autodetect");
-
-static int vid_out_nr[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = -1 };
-module_param_array(vid_out_nr, int, NULL, 0444);
-MODULE_PARM_DESC(vid_out_nr, " videoX start number, -1 is autodetect");
-
-static int vbi_cap_nr[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = -1 };
-module_param_array(vbi_cap_nr, int, NULL, 0444);
-MODULE_PARM_DESC(vbi_cap_nr, " vbiX start number, -1 is autodetect");
-
-static int vbi_out_nr[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = -1 };
-module_param_array(vbi_out_nr, int, NULL, 0444);
-MODULE_PARM_DESC(vbi_out_nr, " vbiX start number, -1 is autodetect");
-
-static int sdr_cap_nr[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = -1 };
-module_param_array(sdr_cap_nr, int, NULL, 0444);
-MODULE_PARM_DESC(sdr_cap_nr, " swradioX start number, -1 is autodetect");
-
-static int radio_rx_nr[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = -1 };
-module_param_array(radio_rx_nr, int, NULL, 0444);
-MODULE_PARM_DESC(radio_rx_nr, " radioX start number, -1 is autodetect");
-
-static int radio_tx_nr[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = -1 };
-module_param_array(radio_tx_nr, int, NULL, 0444);
-MODULE_PARM_DESC(radio_tx_nr, " radioX start number, -1 is autodetect");
-
-static int ccs_cap_mode[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = -1 };
-module_param_array(ccs_cap_mode, int, NULL, 0444);
-MODULE_PARM_DESC(ccs_cap_mode, " capture crop/compose/scale mode:\n"
- "\t\t bit 0=crop, 1=compose, 2=scale,\n"
- "\t\t -1=user-controlled (default)");
-
-static int ccs_out_mode[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = -1 };
-module_param_array(ccs_out_mode, int, NULL, 0444);
-MODULE_PARM_DESC(ccs_out_mode, " output crop/compose/scale mode:\n"
- "\t\t bit 0=crop, 1=compose, 2=scale,\n"
- "\t\t -1=user-controlled (default)");
-
-static unsigned multiplanar[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = 1 };
-module_param_array(multiplanar, uint, NULL, 0444);
-MODULE_PARM_DESC(multiplanar, " 1 (default) creates a single planar device, 2 creates a multiplanar device.");
-
-/* Default: video + vbi-cap (raw and sliced) + radio rx + radio tx + sdr + vbi-out + vid-out */
-static unsigned node_types[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = 0x1d3d };
-module_param_array(node_types, uint, NULL, 0444);
-MODULE_PARM_DESC(node_types, " node types, default is 0x1d3d. Bitmask with the following meaning:\n"
- "\t\t bit 0: Video Capture node\n"
- "\t\t bit 2-3: VBI Capture node: 0 = none, 1 = raw vbi, 2 = sliced vbi, 3 = both\n"
- "\t\t bit 4: Radio Receiver node\n"
- "\t\t bit 5: Software Defined Radio Receiver node\n"
- "\t\t bit 8: Video Output node\n"
- "\t\t bit 10-11: VBI Output node: 0 = none, 1 = raw vbi, 2 = sliced vbi, 3 = both\n"
- "\t\t bit 12: Radio Transmitter node\n"
- "\t\t bit 16: Framebuffer for testing overlays");
-
-/* Default: 4 inputs */
-static unsigned num_inputs[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = 4 };
-module_param_array(num_inputs, uint, NULL, 0444);
-MODULE_PARM_DESC(num_inputs, " number of inputs, default is 4");
-
-/* Default: input 0 = WEBCAM, 1 = TV, 2 = SVID, 3 = HDMI */
-static unsigned input_types[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = 0xe4 };
-module_param_array(input_types, uint, NULL, 0444);
-MODULE_PARM_DESC(input_types, " input types, default is 0xe4. Two bits per input,\n"
- "\t\t bits 0-1 == input 0, bits 31-30 == input 15.\n"
- "\t\t Type 0 == webcam, 1 == TV, 2 == S-Video, 3 == HDMI");
-
-/* Default: 2 outputs */
-static unsigned num_outputs[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = 2 };
-module_param_array(num_outputs, uint, NULL, 0444);
-MODULE_PARM_DESC(num_outputs, " number of outputs, default is 2");
-
-/* Default: output 0 = SVID, 1 = HDMI */
-static unsigned output_types[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = 2 };
-module_param_array(output_types, uint, NULL, 0444);
-MODULE_PARM_DESC(output_types, " output types, default is 0x02. One bit per output,\n"
- "\t\t bit 0 == output 0, bit 15 == output 15.\n"
- "\t\t Type 0 == S-Video, 1 == HDMI");
-
-unsigned vivid_debug;
-module_param(vivid_debug, uint, 0644);
-MODULE_PARM_DESC(vivid_debug, " activates debug info");
-
-static bool no_error_inj;
-module_param(no_error_inj, bool, 0444);
-MODULE_PARM_DESC(no_error_inj, " if set disable the error injecting controls");
-
-static unsigned int allocators[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = 0 };
-module_param_array(allocators, uint, NULL, 0444);
-MODULE_PARM_DESC(allocators, " memory allocator selection, default is 0.\n"
- "\t\t 0 == vmalloc\n"
- "\t\t 1 == dma-contig");
-
-static struct vivid_dev *vivid_devs[VIVID_MAX_DEVS];
-
-const struct v4l2_rect vivid_min_rect = {
- 0, 0, MIN_WIDTH, MIN_HEIGHT
-};
-
-const struct v4l2_rect vivid_max_rect = {
- 0, 0, MAX_WIDTH * MAX_ZOOM, MAX_HEIGHT * MAX_ZOOM
-};
-
-static const u8 vivid_hdmi_edid[256] = {
- 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
- 0x31, 0xd8, 0x34, 0x12, 0x00, 0x00, 0x00, 0x00,
- 0x22, 0x1a, 0x01, 0x03, 0x80, 0x60, 0x36, 0x78,
- 0x0f, 0xee, 0x91, 0xa3, 0x54, 0x4c, 0x99, 0x26,
- 0x0f, 0x50, 0x54, 0x2f, 0xcf, 0x00, 0x31, 0x59,
- 0x45, 0x59, 0x81, 0x80, 0x81, 0x40, 0x90, 0x40,
- 0x95, 0x00, 0xa9, 0x40, 0xb3, 0x00, 0x08, 0xe8,
- 0x00, 0x30, 0xf2, 0x70, 0x5a, 0x80, 0xb0, 0x58,
- 0x8a, 0x00, 0xc0, 0x1c, 0x32, 0x00, 0x00, 0x1e,
- 0x00, 0x00, 0x00, 0xfd, 0x00, 0x18, 0x55, 0x18,
- 0x87, 0x3c, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20,
- 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x76,
- 0x69, 0x76, 0x69, 0x64, 0x0a, 0x20, 0x20, 0x20,
- 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0x10,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x7b,
-
- 0x02, 0x03, 0x3f, 0xf1, 0x51, 0x61, 0x60, 0x5f,
- 0x5e, 0x5d, 0x10, 0x1f, 0x04, 0x13, 0x22, 0x21,
- 0x20, 0x05, 0x14, 0x02, 0x11, 0x01, 0x23, 0x09,
- 0x07, 0x07, 0x83, 0x01, 0x00, 0x00, 0x6d, 0x03,
- 0x0c, 0x00, 0x10, 0x00, 0x00, 0x3c, 0x21, 0x00,
- 0x60, 0x01, 0x02, 0x03, 0x67, 0xd8, 0x5d, 0xc4,
- 0x01, 0x78, 0x00, 0x00, 0xe2, 0x00, 0xca, 0xe3,
- 0x05, 0x00, 0x00, 0xe3, 0x06, 0x01, 0x00, 0x4d,
- 0xd0, 0x00, 0xa0, 0xf0, 0x70, 0x3e, 0x80, 0x30,
- 0x20, 0x35, 0x00, 0xc0, 0x1c, 0x32, 0x00, 0x00,
- 0x1e, 0x1a, 0x36, 0x80, 0xa0, 0x70, 0x38, 0x1f,
- 0x40, 0x30, 0x20, 0x35, 0x00, 0xc0, 0x1c, 0x32,
- 0x00, 0x00, 0x1a, 0x1a, 0x1d, 0x00, 0x80, 0x51,
- 0xd0, 0x1c, 0x20, 0x40, 0x80, 0x35, 0x00, 0xc0,
- 0x1c, 0x32, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x82,
-};
-
-static int vidioc_querycap(struct file *file, void *priv,
- struct v4l2_capability *cap)
-{
- struct vivid_dev *dev = video_drvdata(file);
-
- strscpy(cap->driver, "vivid", sizeof(cap->driver));
- strscpy(cap->card, "vivid", sizeof(cap->card));
- snprintf(cap->bus_info, sizeof(cap->bus_info),
- "platform:%s", dev->v4l2_dev.name);
-
- cap->capabilities = dev->vid_cap_caps | dev->vid_out_caps |
- dev->vbi_cap_caps | dev->vbi_out_caps |
- dev->radio_rx_caps | dev->radio_tx_caps |
- dev->sdr_cap_caps | V4L2_CAP_DEVICE_CAPS;
- return 0;
-}
-
-static int vidioc_s_hw_freq_seek(struct file *file, void *fh, const struct v4l2_hw_freq_seek *a)
-{
- struct video_device *vdev = video_devdata(file);
-
- if (vdev->vfl_type == VFL_TYPE_RADIO)
- return vivid_radio_rx_s_hw_freq_seek(file, fh, a);
- return -ENOTTY;
-}
-
-static int vidioc_enum_freq_bands(struct file *file, void *fh, struct v4l2_frequency_band *band)
-{
- struct video_device *vdev = video_devdata(file);
-
- if (vdev->vfl_type == VFL_TYPE_RADIO)
- return vivid_radio_rx_enum_freq_bands(file, fh, band);
- if (vdev->vfl_type == VFL_TYPE_SDR)
- return vivid_sdr_enum_freq_bands(file, fh, band);
- return -ENOTTY;
-}
-
-static int vidioc_g_tuner(struct file *file, void *fh, struct v4l2_tuner *vt)
-{
- struct video_device *vdev = video_devdata(file);
-
- if (vdev->vfl_type == VFL_TYPE_RADIO)
- return vivid_radio_rx_g_tuner(file, fh, vt);
- if (vdev->vfl_type == VFL_TYPE_SDR)
- return vivid_sdr_g_tuner(file, fh, vt);
- return vivid_video_g_tuner(file, fh, vt);
-}
-
-static int vidioc_s_tuner(struct file *file, void *fh, const struct v4l2_tuner *vt)
-{
- struct video_device *vdev = video_devdata(file);
-
- if (vdev->vfl_type == VFL_TYPE_RADIO)
- return vivid_radio_rx_s_tuner(file, fh, vt);
- if (vdev->vfl_type == VFL_TYPE_SDR)
- return vivid_sdr_s_tuner(file, fh, vt);
- return vivid_video_s_tuner(file, fh, vt);
-}
-
-static int vidioc_g_frequency(struct file *file, void *fh, struct v4l2_frequency *vf)
-{
- struct vivid_dev *dev = video_drvdata(file);
- struct video_device *vdev = video_devdata(file);
-
- if (vdev->vfl_type == VFL_TYPE_RADIO)
- return vivid_radio_g_frequency(file,
- vdev->vfl_dir == VFL_DIR_RX ?
- &dev->radio_rx_freq : &dev->radio_tx_freq, vf);
- if (vdev->vfl_type == VFL_TYPE_SDR)
- return vivid_sdr_g_frequency(file, fh, vf);
- return vivid_video_g_frequency(file, fh, vf);
-}
-
-static int vidioc_s_frequency(struct file *file, void *fh, const struct v4l2_frequency *vf)
-{
- struct vivid_dev *dev = video_drvdata(file);
- struct video_device *vdev = video_devdata(file);
-
- if (vdev->vfl_type == VFL_TYPE_RADIO)
- return vivid_radio_s_frequency(file,
- vdev->vfl_dir == VFL_DIR_RX ?
- &dev->radio_rx_freq : &dev->radio_tx_freq, vf);
- if (vdev->vfl_type == VFL_TYPE_SDR)
- return vivid_sdr_s_frequency(file, fh, vf);
- return vivid_video_s_frequency(file, fh, vf);
-}
-
-static int vidioc_overlay(struct file *file, void *fh, unsigned i)
-{
- struct video_device *vdev = video_devdata(file);
-
- if (vdev->vfl_dir == VFL_DIR_RX)
- return vivid_vid_cap_overlay(file, fh, i);
- return vivid_vid_out_overlay(file, fh, i);
-}
-
-static int vidioc_g_fbuf(struct file *file, void *fh, struct v4l2_framebuffer *a)
-{
- struct video_device *vdev = video_devdata(file);
-
- if (vdev->vfl_dir == VFL_DIR_RX)
- return vivid_vid_cap_g_fbuf(file, fh, a);
- return vivid_vid_out_g_fbuf(file, fh, a);
-}
-
-static int vidioc_s_fbuf(struct file *file, void *fh, const struct v4l2_framebuffer *a)
-{
- struct video_device *vdev = video_devdata(file);
-
- if (vdev->vfl_dir == VFL_DIR_RX)
- return vivid_vid_cap_s_fbuf(file, fh, a);
- return vivid_vid_out_s_fbuf(file, fh, a);
-}
-
-static int vidioc_s_std(struct file *file, void *fh, v4l2_std_id id)
-{
- struct video_device *vdev = video_devdata(file);
-
- if (vdev->vfl_dir == VFL_DIR_RX)
- return vivid_vid_cap_s_std(file, fh, id);
- return vivid_vid_out_s_std(file, fh, id);
-}
-
-static int vidioc_s_dv_timings(struct file *file, void *fh, struct v4l2_dv_timings *timings)
-{
- struct video_device *vdev = video_devdata(file);
-
- if (vdev->vfl_dir == VFL_DIR_RX)
- return vivid_vid_cap_s_dv_timings(file, fh, timings);
- return vivid_vid_out_s_dv_timings(file, fh, timings);
-}
-
-static int vidioc_g_pixelaspect(struct file *file, void *fh,
- int type, struct v4l2_fract *f)
-{
- struct video_device *vdev = video_devdata(file);
-
- if (vdev->vfl_dir == VFL_DIR_RX)
- return vivid_vid_cap_g_pixelaspect(file, fh, type, f);
- return vivid_vid_out_g_pixelaspect(file, fh, type, f);
-}
-
-static int vidioc_g_selection(struct file *file, void *fh,
- struct v4l2_selection *sel)
-{
- struct video_device *vdev = video_devdata(file);
-
- if (vdev->vfl_dir == VFL_DIR_RX)
- return vivid_vid_cap_g_selection(file, fh, sel);
- return vivid_vid_out_g_selection(file, fh, sel);
-}
-
-static int vidioc_s_selection(struct file *file, void *fh,
- struct v4l2_selection *sel)
-{
- struct video_device *vdev = video_devdata(file);
-
- if (vdev->vfl_dir == VFL_DIR_RX)
- return vivid_vid_cap_s_selection(file, fh, sel);
- return vivid_vid_out_s_selection(file, fh, sel);
-}
-
-static int vidioc_g_parm(struct file *file, void *fh,
- struct v4l2_streamparm *parm)
-{
- struct video_device *vdev = video_devdata(file);
-
- if (vdev->vfl_dir == VFL_DIR_RX)
- return vivid_vid_cap_g_parm(file, fh, parm);
- return vivid_vid_out_g_parm(file, fh, parm);
-}
-
-static int vidioc_s_parm(struct file *file, void *fh,
- struct v4l2_streamparm *parm)
-{
- struct video_device *vdev = video_devdata(file);
-
- if (vdev->vfl_dir == VFL_DIR_RX)
- return vivid_vid_cap_s_parm(file, fh, parm);
- return -ENOTTY;
-}
-
-static int vidioc_log_status(struct file *file, void *fh)
-{
- struct vivid_dev *dev = video_drvdata(file);
- struct video_device *vdev = video_devdata(file);
-
- v4l2_ctrl_log_status(file, fh);
- if (vdev->vfl_dir == VFL_DIR_RX && vdev->vfl_type == VFL_TYPE_GRABBER)
- tpg_log_status(&dev->tpg);
- return 0;
-}
-
-static ssize_t vivid_radio_read(struct file *file, char __user *buf,
- size_t size, loff_t *offset)
-{
- struct video_device *vdev = video_devdata(file);
-
- if (vdev->vfl_dir == VFL_DIR_TX)
- return -EINVAL;
- return vivid_radio_rx_read(file, buf, size, offset);
-}
-
-static ssize_t vivid_radio_write(struct file *file, const char __user *buf,
- size_t size, loff_t *offset)
-{
- struct video_device *vdev = video_devdata(file);
-
- if (vdev->vfl_dir == VFL_DIR_RX)
- return -EINVAL;
- return vivid_radio_tx_write(file, buf, size, offset);
-}
-
-static __poll_t vivid_radio_poll(struct file *file, struct poll_table_struct *wait)
-{
- struct video_device *vdev = video_devdata(file);
-
- if (vdev->vfl_dir == VFL_DIR_RX)
- return vivid_radio_rx_poll(file, wait);
- return vivid_radio_tx_poll(file, wait);
-}
-
-static bool vivid_is_in_use(struct video_device *vdev)
-{
- unsigned long flags;
- bool res;
-
- spin_lock_irqsave(&vdev->fh_lock, flags);
- res = !list_empty(&vdev->fh_list);
- spin_unlock_irqrestore(&vdev->fh_lock, flags);
- return res;
-}
-
-static bool vivid_is_last_user(struct vivid_dev *dev)
-{
- unsigned uses = vivid_is_in_use(&dev->vid_cap_dev) +
- vivid_is_in_use(&dev->vid_out_dev) +
- vivid_is_in_use(&dev->vbi_cap_dev) +
- vivid_is_in_use(&dev->vbi_out_dev) +
- vivid_is_in_use(&dev->sdr_cap_dev) +
- vivid_is_in_use(&dev->radio_rx_dev) +
- vivid_is_in_use(&dev->radio_tx_dev);
-
- return uses == 1;
-}
-
-static int vivid_fop_release(struct file *file)
-{
- struct vivid_dev *dev = video_drvdata(file);
- struct video_device *vdev = video_devdata(file);
-
- mutex_lock(&dev->mutex);
- if (!no_error_inj && v4l2_fh_is_singular_file(file) &&
- !video_is_registered(vdev) && vivid_is_last_user(dev)) {
- /*
- * I am the last user of this driver, and a disconnect
- * was forced (since this video_device is unregistered),
- * so re-register all video_device's again.
- */
- v4l2_info(&dev->v4l2_dev, "reconnect\n");
- set_bit(V4L2_FL_REGISTERED, &dev->vid_cap_dev.flags);
- set_bit(V4L2_FL_REGISTERED, &dev->vid_out_dev.flags);
- set_bit(V4L2_FL_REGISTERED, &dev->vbi_cap_dev.flags);
- set_bit(V4L2_FL_REGISTERED, &dev->vbi_out_dev.flags);
- set_bit(V4L2_FL_REGISTERED, &dev->sdr_cap_dev.flags);
- set_bit(V4L2_FL_REGISTERED, &dev->radio_rx_dev.flags);
- set_bit(V4L2_FL_REGISTERED, &dev->radio_tx_dev.flags);
- }
- mutex_unlock(&dev->mutex);
- if (file->private_data == dev->overlay_cap_owner)
- dev->overlay_cap_owner = NULL;
- if (file->private_data == dev->radio_rx_rds_owner) {
- dev->radio_rx_rds_last_block = 0;
- dev->radio_rx_rds_owner = NULL;
- }
- if (file->private_data == dev->radio_tx_rds_owner) {
- dev->radio_tx_rds_last_block = 0;
- dev->radio_tx_rds_owner = NULL;
- }
- if (vdev->queue)
- return vb2_fop_release(file);
- return v4l2_fh_release(file);
-}
-
-static const struct v4l2_file_operations vivid_fops = {
- .owner = THIS_MODULE,
- .open = v4l2_fh_open,
- .release = vivid_fop_release,
- .read = vb2_fop_read,
- .write = vb2_fop_write,
- .poll = vb2_fop_poll,
- .unlocked_ioctl = video_ioctl2,
- .mmap = vb2_fop_mmap,
-};
-
-static const struct v4l2_file_operations vivid_radio_fops = {
- .owner = THIS_MODULE,
- .open = v4l2_fh_open,
- .release = vivid_fop_release,
- .read = vivid_radio_read,
- .write = vivid_radio_write,
- .poll = vivid_radio_poll,
- .unlocked_ioctl = video_ioctl2,
-};
-
-static const struct v4l2_ioctl_ops vivid_ioctl_ops = {
- .vidioc_querycap = vidioc_querycap,
-
- .vidioc_enum_fmt_vid_cap = vivid_enum_fmt_vid,
- .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
- .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
- .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
- .vidioc_g_fmt_vid_cap_mplane = vidioc_g_fmt_vid_cap_mplane,
- .vidioc_try_fmt_vid_cap_mplane = vidioc_try_fmt_vid_cap_mplane,
- .vidioc_s_fmt_vid_cap_mplane = vidioc_s_fmt_vid_cap_mplane,
-
- .vidioc_enum_fmt_vid_out = vivid_enum_fmt_vid,
- .vidioc_g_fmt_vid_out = vidioc_g_fmt_vid_out,
- .vidioc_try_fmt_vid_out = vidioc_try_fmt_vid_out,
- .vidioc_s_fmt_vid_out = vidioc_s_fmt_vid_out,
- .vidioc_g_fmt_vid_out_mplane = vidioc_g_fmt_vid_out_mplane,
- .vidioc_try_fmt_vid_out_mplane = vidioc_try_fmt_vid_out_mplane,
- .vidioc_s_fmt_vid_out_mplane = vidioc_s_fmt_vid_out_mplane,
-
- .vidioc_g_selection = vidioc_g_selection,
- .vidioc_s_selection = vidioc_s_selection,
- .vidioc_g_pixelaspect = vidioc_g_pixelaspect,
-
- .vidioc_g_fmt_vbi_cap = vidioc_g_fmt_vbi_cap,
- .vidioc_try_fmt_vbi_cap = vidioc_g_fmt_vbi_cap,
- .vidioc_s_fmt_vbi_cap = vidioc_s_fmt_vbi_cap,
-
- .vidioc_g_fmt_sliced_vbi_cap = vidioc_g_fmt_sliced_vbi_cap,
- .vidioc_try_fmt_sliced_vbi_cap = vidioc_try_fmt_sliced_vbi_cap,
- .vidioc_s_fmt_sliced_vbi_cap = vidioc_s_fmt_sliced_vbi_cap,
- .vidioc_g_sliced_vbi_cap = vidioc_g_sliced_vbi_cap,
-
- .vidioc_g_fmt_vbi_out = vidioc_g_fmt_vbi_out,
- .vidioc_try_fmt_vbi_out = vidioc_g_fmt_vbi_out,
- .vidioc_s_fmt_vbi_out = vidioc_s_fmt_vbi_out,
-
- .vidioc_g_fmt_sliced_vbi_out = vidioc_g_fmt_sliced_vbi_out,
- .vidioc_try_fmt_sliced_vbi_out = vidioc_try_fmt_sliced_vbi_out,
- .vidioc_s_fmt_sliced_vbi_out = vidioc_s_fmt_sliced_vbi_out,
-
- .vidioc_enum_fmt_sdr_cap = vidioc_enum_fmt_sdr_cap,
- .vidioc_g_fmt_sdr_cap = vidioc_g_fmt_sdr_cap,
- .vidioc_try_fmt_sdr_cap = vidioc_try_fmt_sdr_cap,
- .vidioc_s_fmt_sdr_cap = vidioc_s_fmt_sdr_cap,
-
- .vidioc_overlay = vidioc_overlay,
- .vidioc_enum_framesizes = vidioc_enum_framesizes,
- .vidioc_enum_frameintervals = vidioc_enum_frameintervals,
- .vidioc_g_parm = vidioc_g_parm,
- .vidioc_s_parm = vidioc_s_parm,
-
- .vidioc_enum_fmt_vid_overlay = vidioc_enum_fmt_vid_overlay,
- .vidioc_g_fmt_vid_overlay = vidioc_g_fmt_vid_overlay,
- .vidioc_try_fmt_vid_overlay = vidioc_try_fmt_vid_overlay,
- .vidioc_s_fmt_vid_overlay = vidioc_s_fmt_vid_overlay,
- .vidioc_g_fmt_vid_out_overlay = vidioc_g_fmt_vid_out_overlay,
- .vidioc_try_fmt_vid_out_overlay = vidioc_try_fmt_vid_out_overlay,
- .vidioc_s_fmt_vid_out_overlay = vidioc_s_fmt_vid_out_overlay,
- .vidioc_g_fbuf = vidioc_g_fbuf,
- .vidioc_s_fbuf = vidioc_s_fbuf,
-
- .vidioc_reqbufs = vb2_ioctl_reqbufs,
- .vidioc_create_bufs = vb2_ioctl_create_bufs,
- .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
- .vidioc_querybuf = vb2_ioctl_querybuf,
- .vidioc_qbuf = vb2_ioctl_qbuf,
- .vidioc_dqbuf = vb2_ioctl_dqbuf,
- .vidioc_expbuf = vb2_ioctl_expbuf,
- .vidioc_streamon = vb2_ioctl_streamon,
- .vidioc_streamoff = vb2_ioctl_streamoff,
-
- .vidioc_enum_input = vidioc_enum_input,
- .vidioc_g_input = vidioc_g_input,
- .vidioc_s_input = vidioc_s_input,
- .vidioc_s_audio = vidioc_s_audio,
- .vidioc_g_audio = vidioc_g_audio,
- .vidioc_enumaudio = vidioc_enumaudio,
- .vidioc_s_frequency = vidioc_s_frequency,
- .vidioc_g_frequency = vidioc_g_frequency,
- .vidioc_s_tuner = vidioc_s_tuner,
- .vidioc_g_tuner = vidioc_g_tuner,
- .vidioc_s_modulator = vidioc_s_modulator,
- .vidioc_g_modulator = vidioc_g_modulator,
- .vidioc_s_hw_freq_seek = vidioc_s_hw_freq_seek,
- .vidioc_enum_freq_bands = vidioc_enum_freq_bands,
-
- .vidioc_enum_output = vidioc_enum_output,
- .vidioc_g_output = vidioc_g_output,
- .vidioc_s_output = vidioc_s_output,
- .vidioc_s_audout = vidioc_s_audout,
- .vidioc_g_audout = vidioc_g_audout,
- .vidioc_enumaudout = vidioc_enumaudout,
-
- .vidioc_querystd = vidioc_querystd,
- .vidioc_g_std = vidioc_g_std,
- .vidioc_s_std = vidioc_s_std,
- .vidioc_s_dv_timings = vidioc_s_dv_timings,
- .vidioc_g_dv_timings = vidioc_g_dv_timings,
- .vidioc_query_dv_timings = vidioc_query_dv_timings,
- .vidioc_enum_dv_timings = vidioc_enum_dv_timings,
- .vidioc_dv_timings_cap = vidioc_dv_timings_cap,
- .vidioc_g_edid = vidioc_g_edid,
- .vidioc_s_edid = vidioc_s_edid,
-
- .vidioc_log_status = vidioc_log_status,
- .vidioc_subscribe_event = vidioc_subscribe_event,
- .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
-};
-
-/* -----------------------------------------------------------------
- Initialization and module stuff
- ------------------------------------------------------------------*/
-
-static void vivid_dev_release(struct v4l2_device *v4l2_dev)
-{
- struct vivid_dev *dev = container_of(v4l2_dev, struct vivid_dev, v4l2_dev);
-
- vivid_free_controls(dev);
- v4l2_device_unregister(&dev->v4l2_dev);
-#ifdef CONFIG_MEDIA_CONTROLLER
- media_device_cleanup(&dev->mdev);
-#endif
- vfree(dev->scaled_line);
- vfree(dev->blended_line);
- vfree(dev->edid);
- vfree(dev->bitmap_cap);
- vfree(dev->bitmap_out);
- tpg_free(&dev->tpg);
- kfree(dev->query_dv_timings_qmenu);
- kfree(dev->query_dv_timings_qmenu_strings);
- kfree(dev);
-}
-
-#ifdef CONFIG_MEDIA_CONTROLLER
-static int vivid_req_validate(struct media_request *req)
-{
- struct vivid_dev *dev = container_of(req->mdev, struct vivid_dev, mdev);
-
- if (dev->req_validate_error) {
- dev->req_validate_error = false;
- return -EINVAL;
- }
- return vb2_request_validate(req);
-}
-
-static const struct media_device_ops vivid_media_ops = {
- .req_validate = vivid_req_validate,
- .req_queue = vb2_request_queue,
-};
-#endif
-
-static int vivid_create_instance(struct platform_device *pdev, int inst)
-{
- static const struct v4l2_dv_timings def_dv_timings =
- V4L2_DV_BT_CEA_1280X720P60;
- static const struct vb2_mem_ops * const vivid_mem_ops[2] = {
- &vb2_vmalloc_memops,
- &vb2_dma_contig_memops,
- };
- unsigned in_type_counter[4] = { 0, 0, 0, 0 };
- unsigned out_type_counter[4] = { 0, 0, 0, 0 };
- int ccs_cap = ccs_cap_mode[inst];
- int ccs_out = ccs_out_mode[inst];
- bool has_tuner;
- bool has_modulator;
- struct vivid_dev *dev;
- struct video_device *vfd;
- struct vb2_queue *q;
- unsigned node_type = node_types[inst];
- unsigned int allocator = allocators[inst];
- v4l2_std_id tvnorms_cap = 0, tvnorms_out = 0;
- int ret;
- int i;
-#ifdef CONFIG_VIDEO_VIVID_CEC
- unsigned int cec_tx_bus_cnt = 0;
-#endif
-
- /* allocate main vivid state structure */
- dev = kzalloc(sizeof(*dev), GFP_KERNEL);
- if (!dev)
- return -ENOMEM;
-
- dev->inst = inst;
-
-#ifdef CONFIG_MEDIA_CONTROLLER
- dev->v4l2_dev.mdev = &dev->mdev;
-
- /* Initialize media device */
- strscpy(dev->mdev.model, VIVID_MODULE_NAME, sizeof(dev->mdev.model));
- snprintf(dev->mdev.bus_info, sizeof(dev->mdev.bus_info),
- "platform:%s-%03d", VIVID_MODULE_NAME, inst);
- dev->mdev.dev = &pdev->dev;
- media_device_init(&dev->mdev);
- dev->mdev.ops = &vivid_media_ops;
-#endif
-
- /* register v4l2_device */
- snprintf(dev->v4l2_dev.name, sizeof(dev->v4l2_dev.name),
- "%s-%03d", VIVID_MODULE_NAME, inst);
- ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
- if (ret) {
- kfree(dev);
- return ret;
- }
- dev->v4l2_dev.release = vivid_dev_release;
-
- /* start detecting feature set */
-
- /* do we use single- or multi-planar? */
- dev->multiplanar = multiplanar[inst] > 1;
- v4l2_info(&dev->v4l2_dev, "using %splanar format API\n",
- dev->multiplanar ? "multi" : "single ");
-
- /* how many inputs do we have and of what type? */
- dev->num_inputs = num_inputs[inst];
- if (dev->num_inputs < 1)
- dev->num_inputs = 1;
- if (dev->num_inputs >= MAX_INPUTS)
- dev->num_inputs = MAX_INPUTS;
- for (i = 0; i < dev->num_inputs; i++) {
- dev->input_type[i] = (input_types[inst] >> (i * 2)) & 0x3;
- dev->input_name_counter[i] = in_type_counter[dev->input_type[i]]++;
- }
- dev->has_audio_inputs = in_type_counter[TV] && in_type_counter[SVID];
- if (in_type_counter[HDMI] == 16) {
- /* The CEC physical address only allows for max 15 inputs */
- in_type_counter[HDMI]--;
- dev->num_inputs--;
- }
- dev->num_hdmi_inputs = in_type_counter[HDMI];
-
- /* how many outputs do we have and of what type? */
- dev->num_outputs = num_outputs[inst];
- if (dev->num_outputs < 1)
- dev->num_outputs = 1;
- if (dev->num_outputs >= MAX_OUTPUTS)
- dev->num_outputs = MAX_OUTPUTS;
- for (i = 0; i < dev->num_outputs; i++) {
- dev->output_type[i] = ((output_types[inst] >> i) & 1) ? HDMI : SVID;
- dev->output_name_counter[i] = out_type_counter[dev->output_type[i]]++;
- dev->display_present[i] = true;
- }
- dev->has_audio_outputs = out_type_counter[SVID];
- if (out_type_counter[HDMI] == 16) {
- /*
- * The CEC physical address only allows for max 15 inputs,
- * so outputs are also limited to 15 to allow for easy
- * CEC output to input mapping.
- */
- out_type_counter[HDMI]--;
- dev->num_outputs--;
- }
- dev->num_hdmi_outputs = out_type_counter[HDMI];
-
- /* do we create a video capture device? */
- dev->has_vid_cap = node_type & 0x0001;
-
- /* do we create a vbi capture device? */
- if (in_type_counter[TV] || in_type_counter[SVID]) {
- dev->has_raw_vbi_cap = node_type & 0x0004;
- dev->has_sliced_vbi_cap = node_type & 0x0008;
- dev->has_vbi_cap = dev->has_raw_vbi_cap | dev->has_sliced_vbi_cap;
- }
-
- /* do we create a video output device? */
- dev->has_vid_out = node_type & 0x0100;
-
- /* do we create a vbi output device? */
- if (out_type_counter[SVID]) {
- dev->has_raw_vbi_out = node_type & 0x0400;
- dev->has_sliced_vbi_out = node_type & 0x0800;
- dev->has_vbi_out = dev->has_raw_vbi_out | dev->has_sliced_vbi_out;
- }
-
- /* do we create a radio receiver device? */
- dev->has_radio_rx = node_type & 0x0010;
-
- /* do we create a radio transmitter device? */
- dev->has_radio_tx = node_type & 0x1000;
-
- /* do we create a software defined radio capture device? */
- dev->has_sdr_cap = node_type & 0x0020;
-
- /* do we have a tuner? */
- has_tuner = ((dev->has_vid_cap || dev->has_vbi_cap) && in_type_counter[TV]) ||
- dev->has_radio_rx || dev->has_sdr_cap;
-
- /* do we have a modulator? */
- has_modulator = dev->has_radio_tx;
-
- if (dev->has_vid_cap)
- /* do we have a framebuffer for overlay testing? */
- dev->has_fb = node_type & 0x10000;
-
- /* can we do crop/compose/scaling while capturing? */
- if (no_error_inj && ccs_cap == -1)
- ccs_cap = 7;
-
- /* if ccs_cap == -1, then the user can select it using controls */
- if (ccs_cap != -1) {
- dev->has_crop_cap = ccs_cap & 1;
- dev->has_compose_cap = ccs_cap & 2;
- dev->has_scaler_cap = ccs_cap & 4;
- v4l2_info(&dev->v4l2_dev, "Capture Crop: %c Compose: %c Scaler: %c\n",
- dev->has_crop_cap ? 'Y' : 'N',
- dev->has_compose_cap ? 'Y' : 'N',
- dev->has_scaler_cap ? 'Y' : 'N');
- }
-
- /* can we do crop/compose/scaling with video output? */
- if (no_error_inj && ccs_out == -1)
- ccs_out = 7;
-
- /* if ccs_out == -1, then the user can select it using controls */
- if (ccs_out != -1) {
- dev->has_crop_out = ccs_out & 1;
- dev->has_compose_out = ccs_out & 2;
- dev->has_scaler_out = ccs_out & 4;
- v4l2_info(&dev->v4l2_dev, "Output Crop: %c Compose: %c Scaler: %c\n",
- dev->has_crop_out ? 'Y' : 'N',
- dev->has_compose_out ? 'Y' : 'N',
- dev->has_scaler_out ? 'Y' : 'N');
- }
-
- /* end detecting feature set */
-
- if (dev->has_vid_cap) {
- /* set up the capabilities of the video capture device */
- dev->vid_cap_caps = dev->multiplanar ?
- V4L2_CAP_VIDEO_CAPTURE_MPLANE :
- V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OVERLAY;
- dev->vid_cap_caps |= V4L2_CAP_STREAMING | V4L2_CAP_READWRITE;
- if (dev->has_audio_inputs)
- dev->vid_cap_caps |= V4L2_CAP_AUDIO;
- if (in_type_counter[TV])
- dev->vid_cap_caps |= V4L2_CAP_TUNER;
- }
- if (dev->has_vid_out) {
- /* set up the capabilities of the video output device */
- dev->vid_out_caps = dev->multiplanar ?
- V4L2_CAP_VIDEO_OUTPUT_MPLANE :
- V4L2_CAP_VIDEO_OUTPUT;
- if (dev->has_fb)
- dev->vid_out_caps |= V4L2_CAP_VIDEO_OUTPUT_OVERLAY;
- dev->vid_out_caps |= V4L2_CAP_STREAMING | V4L2_CAP_READWRITE;
- if (dev->has_audio_outputs)
- dev->vid_out_caps |= V4L2_CAP_AUDIO;
- }
- if (dev->has_vbi_cap) {
- /* set up the capabilities of the vbi capture device */
- dev->vbi_cap_caps = (dev->has_raw_vbi_cap ? V4L2_CAP_VBI_CAPTURE : 0) |
- (dev->has_sliced_vbi_cap ? V4L2_CAP_SLICED_VBI_CAPTURE : 0);
- dev->vbi_cap_caps |= V4L2_CAP_STREAMING | V4L2_CAP_READWRITE;
- if (dev->has_audio_inputs)
- dev->vbi_cap_caps |= V4L2_CAP_AUDIO;
- if (in_type_counter[TV])
- dev->vbi_cap_caps |= V4L2_CAP_TUNER;
- }
- if (dev->has_vbi_out) {
- /* set up the capabilities of the vbi output device */
- dev->vbi_out_caps = (dev->has_raw_vbi_out ? V4L2_CAP_VBI_OUTPUT : 0) |
- (dev->has_sliced_vbi_out ? V4L2_CAP_SLICED_VBI_OUTPUT : 0);
- dev->vbi_out_caps |= V4L2_CAP_STREAMING | V4L2_CAP_READWRITE;
- if (dev->has_audio_outputs)
- dev->vbi_out_caps |= V4L2_CAP_AUDIO;
- }
- if (dev->has_sdr_cap) {
- /* set up the capabilities of the sdr capture device */
- dev->sdr_cap_caps = V4L2_CAP_SDR_CAPTURE | V4L2_CAP_TUNER;
- dev->sdr_cap_caps |= V4L2_CAP_STREAMING | V4L2_CAP_READWRITE;
- }
- /* set up the capabilities of the radio receiver device */
- if (dev->has_radio_rx)
- dev->radio_rx_caps = V4L2_CAP_RADIO | V4L2_CAP_RDS_CAPTURE |
- V4L2_CAP_HW_FREQ_SEEK | V4L2_CAP_TUNER |
- V4L2_CAP_READWRITE;
- /* set up the capabilities of the radio transmitter device */
- if (dev->has_radio_tx)
- dev->radio_tx_caps = V4L2_CAP_RDS_OUTPUT | V4L2_CAP_MODULATOR |
- V4L2_CAP_READWRITE;
-
- ret = -ENOMEM;
- /* initialize the test pattern generator */
- tpg_init(&dev->tpg, 640, 360);
- if (tpg_alloc(&dev->tpg, MAX_ZOOM * MAX_WIDTH))
- goto free_dev;
- dev->scaled_line = vzalloc(array_size(MAX_WIDTH, MAX_ZOOM));
- if (!dev->scaled_line)
- goto free_dev;
- dev->blended_line = vzalloc(array_size(MAX_WIDTH, MAX_ZOOM));
- if (!dev->blended_line)
- goto free_dev;
-
- /* load the edid */
- dev->edid = vmalloc(256 * 128);
- if (!dev->edid)
- goto free_dev;
-
- while (v4l2_dv_timings_presets[dev->query_dv_timings_size].bt.width)
- dev->query_dv_timings_size++;
-
- /*
- * Create a char pointer array that points to the names of all the
- * preset timings
- */
- dev->query_dv_timings_qmenu = kmalloc_array(dev->query_dv_timings_size,
- sizeof(char *), GFP_KERNEL);
- /*
- * Create a string array containing the names of all the preset
- * timings. Each name is max 31 chars long (+ terminating 0).
- */
- dev->query_dv_timings_qmenu_strings =
- kmalloc_array(dev->query_dv_timings_size, 32, GFP_KERNEL);
-
- if (!dev->query_dv_timings_qmenu ||
- !dev->query_dv_timings_qmenu_strings)
- goto free_dev;
-
- for (i = 0; i < dev->query_dv_timings_size; i++) {
- const struct v4l2_bt_timings *bt = &v4l2_dv_timings_presets[i].bt;
- char *p = dev->query_dv_timings_qmenu_strings + i * 32;
- u32 htot, vtot;
-
- dev->query_dv_timings_qmenu[i] = p;
-
- htot = V4L2_DV_BT_FRAME_WIDTH(bt);
- vtot = V4L2_DV_BT_FRAME_HEIGHT(bt);
- snprintf(p, 32, "%ux%u%s%u",
- bt->width, bt->height, bt->interlaced ? "i" : "p",
- (u32)bt->pixelclock / (htot * vtot));
- }
-
- /* disable invalid ioctls based on the feature set */
- if (!dev->has_audio_inputs) {
- v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_S_AUDIO);
- v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_G_AUDIO);
- v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_ENUMAUDIO);
- v4l2_disable_ioctl(&dev->vbi_cap_dev, VIDIOC_S_AUDIO);
- v4l2_disable_ioctl(&dev->vbi_cap_dev, VIDIOC_G_AUDIO);
- v4l2_disable_ioctl(&dev->vbi_cap_dev, VIDIOC_ENUMAUDIO);
- }
- if (!dev->has_audio_outputs) {
- v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_S_AUDOUT);
- v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_G_AUDOUT);
- v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_ENUMAUDOUT);
- v4l2_disable_ioctl(&dev->vbi_out_dev, VIDIOC_S_AUDOUT);
- v4l2_disable_ioctl(&dev->vbi_out_dev, VIDIOC_G_AUDOUT);
- v4l2_disable_ioctl(&dev->vbi_out_dev, VIDIOC_ENUMAUDOUT);
- }
- if (!in_type_counter[TV] && !in_type_counter[SVID]) {
- v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_S_STD);
- v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_G_STD);
- v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_ENUMSTD);
- v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_QUERYSTD);
- }
- if (!out_type_counter[SVID]) {
- v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_S_STD);
- v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_G_STD);
- v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_ENUMSTD);
- }
- if (!has_tuner && !has_modulator) {
- v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_S_FREQUENCY);
- v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_G_FREQUENCY);
- v4l2_disable_ioctl(&dev->vbi_cap_dev, VIDIOC_S_FREQUENCY);
- v4l2_disable_ioctl(&dev->vbi_cap_dev, VIDIOC_G_FREQUENCY);
- }
- if (!has_tuner) {
- v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_S_TUNER);
- v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_G_TUNER);
- v4l2_disable_ioctl(&dev->vbi_cap_dev, VIDIOC_S_TUNER);
- v4l2_disable_ioctl(&dev->vbi_cap_dev, VIDIOC_G_TUNER);
- }
- if (in_type_counter[HDMI] == 0) {
- v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_S_EDID);
- v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_G_EDID);
- v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_DV_TIMINGS_CAP);
- v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_G_DV_TIMINGS);
- v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_S_DV_TIMINGS);
- v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_ENUM_DV_TIMINGS);
- v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_QUERY_DV_TIMINGS);
- }
- if (out_type_counter[HDMI] == 0) {
- v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_G_EDID);
- v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_DV_TIMINGS_CAP);
- v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_G_DV_TIMINGS);
- v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_S_DV_TIMINGS);
- v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_ENUM_DV_TIMINGS);
- }
- if (!dev->has_fb) {
- v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_G_FBUF);
- v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_S_FBUF);
- v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_OVERLAY);
- }
- v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_S_HW_FREQ_SEEK);
- v4l2_disable_ioctl(&dev->vbi_cap_dev, VIDIOC_S_HW_FREQ_SEEK);
- v4l2_disable_ioctl(&dev->sdr_cap_dev, VIDIOC_S_HW_FREQ_SEEK);
- v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_S_FREQUENCY);
- v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_G_FREQUENCY);
- v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_ENUM_FRAMESIZES);
- v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_ENUM_FRAMEINTERVALS);
- v4l2_disable_ioctl(&dev->vbi_out_dev, VIDIOC_S_FREQUENCY);
- v4l2_disable_ioctl(&dev->vbi_out_dev, VIDIOC_G_FREQUENCY);
-
- /* configure internal data */
- dev->fmt_cap = &vivid_formats[0];
- dev->fmt_out = &vivid_formats[0];
- if (!dev->multiplanar)
- vivid_formats[0].data_offset[0] = 0;
- dev->webcam_size_idx = 1;
- dev->webcam_ival_idx = 3;
- tpg_s_fourcc(&dev->tpg, dev->fmt_cap->fourcc);
- dev->std_out = V4L2_STD_PAL;
- if (dev->input_type[0] == TV || dev->input_type[0] == SVID)
- tvnorms_cap = V4L2_STD_ALL;
- if (dev->output_type[0] == SVID)
- tvnorms_out = V4L2_STD_ALL;
- for (i = 0; i < MAX_INPUTS; i++) {
- dev->dv_timings_cap[i] = def_dv_timings;
- dev->std_cap[i] = V4L2_STD_PAL;
- }
- dev->dv_timings_out = def_dv_timings;
- dev->tv_freq = 2804 /* 175.25 * 16 */;
- dev->tv_audmode = V4L2_TUNER_MODE_STEREO;
- dev->tv_field_cap = V4L2_FIELD_INTERLACED;
- dev->tv_field_out = V4L2_FIELD_INTERLACED;
- dev->radio_rx_freq = 95000 * 16;
- dev->radio_rx_audmode = V4L2_TUNER_MODE_STEREO;
- if (dev->has_radio_tx) {
- dev->radio_tx_freq = 95500 * 16;
- dev->radio_rds_loop = false;
- }
- dev->radio_tx_subchans = V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_RDS;
- dev->sdr_adc_freq = 300000;
- dev->sdr_fm_freq = 50000000;
- dev->sdr_pixelformat = V4L2_SDR_FMT_CU8;
- dev->sdr_buffersize = SDR_CAP_SAMPLES_PER_BUF * 2;
-
- dev->edid_max_blocks = dev->edid_blocks = 2;
- memcpy(dev->edid, vivid_hdmi_edid, sizeof(vivid_hdmi_edid));
- dev->radio_rds_init_time = ktime_get();
-
- /* create all controls */
- ret = vivid_create_controls(dev, ccs_cap == -1, ccs_out == -1, no_error_inj,
- in_type_counter[TV] || in_type_counter[SVID] ||
- out_type_counter[SVID],
- in_type_counter[HDMI] || out_type_counter[HDMI]);
- if (ret)
- goto unreg_dev;
-
- /* enable/disable interface specific controls */
- if (dev->num_outputs && dev->output_type[0] != HDMI)
- v4l2_ctrl_activate(dev->ctrl_display_present, false);
- if (dev->num_inputs && dev->input_type[0] != HDMI) {
- v4l2_ctrl_activate(dev->ctrl_dv_timings_signal_mode, false);
- v4l2_ctrl_activate(dev->ctrl_dv_timings, false);
- } else if (dev->num_inputs && dev->input_type[0] == HDMI) {
- v4l2_ctrl_activate(dev->ctrl_std_signal_mode, false);
- v4l2_ctrl_activate(dev->ctrl_standard, false);
- }
-
- /*
- * update the capture and output formats to do a proper initial
- * configuration.
- */
- vivid_update_format_cap(dev, false);
- vivid_update_format_out(dev);
-
- /* initialize overlay */
- dev->fb_cap.fmt.width = dev->src_rect.width;
- dev->fb_cap.fmt.height = dev->src_rect.height;
- dev->fb_cap.fmt.pixelformat = dev->fmt_cap->fourcc;
- dev->fb_cap.fmt.bytesperline = dev->src_rect.width * tpg_g_twopixelsize(&dev->tpg, 0) / 2;
- dev->fb_cap.fmt.sizeimage = dev->src_rect.height * dev->fb_cap.fmt.bytesperline;
-
- /* initialize locks */
- spin_lock_init(&dev->slock);
- mutex_init(&dev->mutex);
-
- /* init dma queues */
- INIT_LIST_HEAD(&dev->vid_cap_active);
- INIT_LIST_HEAD(&dev->vid_out_active);
- INIT_LIST_HEAD(&dev->vbi_cap_active);
- INIT_LIST_HEAD(&dev->vbi_out_active);
- INIT_LIST_HEAD(&dev->sdr_cap_active);
-
- INIT_LIST_HEAD(&dev->cec_work_list);
- spin_lock_init(&dev->cec_slock);
- /*
- * Same as create_singlethread_workqueue, but now I can use the
- * string formatting of alloc_ordered_workqueue.
- */
- dev->cec_workqueue =
- alloc_ordered_workqueue("vivid-%03d-cec", WQ_MEM_RECLAIM, inst);
- if (!dev->cec_workqueue) {
- ret = -ENOMEM;
- goto unreg_dev;
- }
-
- if (allocator == 1)
- dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
- else if (allocator >= ARRAY_SIZE(vivid_mem_ops))
- allocator = 0;
-
- /* start creating the vb2 queues */
- if (dev->has_vid_cap) {
- snprintf(dev->vid_cap_dev.name, sizeof(dev->vid_cap_dev.name),
- "vivid-%03d-vid-cap", inst);
- /* initialize vid_cap queue */
- q = &dev->vb_vid_cap_q;
- q->type = dev->multiplanar ? V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE :
- V4L2_BUF_TYPE_VIDEO_CAPTURE;
- q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ;
- if (!allocator)
- q->io_modes |= VB2_USERPTR;
- q->drv_priv = dev;
- q->buf_struct_size = sizeof(struct vivid_buffer);
- q->ops = &vivid_vid_cap_qops;
- q->mem_ops = vivid_mem_ops[allocator];
- q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
- q->min_buffers_needed = 2;
- q->lock = &dev->mutex;
- q->dev = dev->v4l2_dev.dev;
- q->supports_requests = true;
-
- ret = vb2_queue_init(q);
- if (ret)
- goto unreg_dev;
- }
-
- if (dev->has_vid_out) {
- snprintf(dev->vid_out_dev.name, sizeof(dev->vid_out_dev.name),
- "vivid-%03d-vid-out", inst);
- /* initialize vid_out queue */
- q = &dev->vb_vid_out_q;
- q->type = dev->multiplanar ? V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE :
- V4L2_BUF_TYPE_VIDEO_OUTPUT;
- q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_WRITE;
- if (!allocator)
- q->io_modes |= VB2_USERPTR;
- q->drv_priv = dev;
- q->buf_struct_size = sizeof(struct vivid_buffer);
- q->ops = &vivid_vid_out_qops;
- q->mem_ops = vivid_mem_ops[allocator];
- q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
- q->min_buffers_needed = 2;
- q->lock = &dev->mutex;
- q->dev = dev->v4l2_dev.dev;
- q->supports_requests = true;
-
- ret = vb2_queue_init(q);
- if (ret)
- goto unreg_dev;
- }
-
- if (dev->has_vbi_cap) {
- /* initialize vbi_cap queue */
- q = &dev->vb_vbi_cap_q;
- q->type = dev->has_raw_vbi_cap ? V4L2_BUF_TYPE_VBI_CAPTURE :
- V4L2_BUF_TYPE_SLICED_VBI_CAPTURE;
- q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ;
- if (!allocator)
- q->io_modes |= VB2_USERPTR;
- q->drv_priv = dev;
- q->buf_struct_size = sizeof(struct vivid_buffer);
- q->ops = &vivid_vbi_cap_qops;
- q->mem_ops = vivid_mem_ops[allocator];
- q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
- q->min_buffers_needed = 2;
- q->lock = &dev->mutex;
- q->dev = dev->v4l2_dev.dev;
- q->supports_requests = true;
-
- ret = vb2_queue_init(q);
- if (ret)
- goto unreg_dev;
- }
-
- if (dev->has_vbi_out) {
- /* initialize vbi_out queue */
- q = &dev->vb_vbi_out_q;
- q->type = dev->has_raw_vbi_out ? V4L2_BUF_TYPE_VBI_OUTPUT :
- V4L2_BUF_TYPE_SLICED_VBI_OUTPUT;
- q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_WRITE;
- if (!allocator)
- q->io_modes |= VB2_USERPTR;
- q->drv_priv = dev;
- q->buf_struct_size = sizeof(struct vivid_buffer);
- q->ops = &vivid_vbi_out_qops;
- q->mem_ops = vivid_mem_ops[allocator];
- q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
- q->min_buffers_needed = 2;
- q->lock = &dev->mutex;
- q->dev = dev->v4l2_dev.dev;
- q->supports_requests = true;
-
- ret = vb2_queue_init(q);
- if (ret)
- goto unreg_dev;
- }
-
- if (dev->has_sdr_cap) {
- /* initialize sdr_cap queue */
- q = &dev->vb_sdr_cap_q;
- q->type = V4L2_BUF_TYPE_SDR_CAPTURE;
- q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ;
- if (!allocator)
- q->io_modes |= VB2_USERPTR;
- q->drv_priv = dev;
- q->buf_struct_size = sizeof(struct vivid_buffer);
- q->ops = &vivid_sdr_cap_qops;
- q->mem_ops = vivid_mem_ops[allocator];
- q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
- q->min_buffers_needed = 8;
- q->lock = &dev->mutex;
- q->dev = dev->v4l2_dev.dev;
- q->supports_requests = true;
-
- ret = vb2_queue_init(q);
- if (ret)
- goto unreg_dev;
- }
-
- if (dev->has_fb) {
- /* Create framebuffer for testing capture/output overlay */
- ret = vivid_fb_init(dev);
- if (ret)
- goto unreg_dev;
- v4l2_info(&dev->v4l2_dev, "Framebuffer device registered as fb%d\n",
- dev->fb_info.node);
- }
-
-#ifdef CONFIG_VIDEO_VIVID_CEC
- if (dev->has_vid_cap && in_type_counter[HDMI]) {
- struct cec_adapter *adap;
-
- adap = vivid_cec_alloc_adap(dev, 0, false);
- ret = PTR_ERR_OR_ZERO(adap);
- if (ret < 0)
- goto unreg_dev;
- dev->cec_rx_adap = adap;
- }
-
- if (dev->has_vid_out) {
- for (i = 0; i < dev->num_outputs; i++) {
- struct cec_adapter *adap;
-
- if (dev->output_type[i] != HDMI)
- continue;
-
- dev->cec_output2bus_map[i] = cec_tx_bus_cnt;
- adap = vivid_cec_alloc_adap(dev, cec_tx_bus_cnt, true);
- ret = PTR_ERR_OR_ZERO(adap);
- if (ret < 0) {
- for (i = 0; i < dev->num_outputs; i++)
- cec_delete_adapter(dev->cec_tx_adap[i]);
- goto unreg_dev;
- }
-
- dev->cec_tx_adap[cec_tx_bus_cnt] = adap;
- cec_tx_bus_cnt++;
- }
- }
-#endif
-
- v4l2_ctrl_handler_setup(&dev->ctrl_hdl_vid_cap);
- v4l2_ctrl_handler_setup(&dev->ctrl_hdl_vid_out);
- v4l2_ctrl_handler_setup(&dev->ctrl_hdl_vbi_cap);
- v4l2_ctrl_handler_setup(&dev->ctrl_hdl_vbi_out);
- v4l2_ctrl_handler_setup(&dev->ctrl_hdl_radio_rx);
- v4l2_ctrl_handler_setup(&dev->ctrl_hdl_radio_tx);
- v4l2_ctrl_handler_setup(&dev->ctrl_hdl_sdr_cap);
-
- /* finally start creating the device nodes */
- if (dev->has_vid_cap) {
- vfd = &dev->vid_cap_dev;
- vfd->fops = &vivid_fops;
- vfd->ioctl_ops = &vivid_ioctl_ops;
- vfd->device_caps = dev->vid_cap_caps;
- vfd->release = video_device_release_empty;
- vfd->v4l2_dev = &dev->v4l2_dev;
- vfd->queue = &dev->vb_vid_cap_q;
- vfd->tvnorms = tvnorms_cap;
-
- /*
- * Provide a mutex to v4l2 core. It will be used to protect
- * all fops and v4l2 ioctls.
- */
- vfd->lock = &dev->mutex;
- video_set_drvdata(vfd, dev);
-
-#ifdef CONFIG_MEDIA_CONTROLLER
- dev->vid_cap_pad.flags = MEDIA_PAD_FL_SINK;
- ret = media_entity_pads_init(&vfd->entity, 1, &dev->vid_cap_pad);
- if (ret)
- goto unreg_dev;
-#endif
-
-#ifdef CONFIG_VIDEO_VIVID_CEC
- if (in_type_counter[HDMI]) {
- ret = cec_register_adapter(dev->cec_rx_adap, &pdev->dev);
- if (ret < 0) {
- cec_delete_adapter(dev->cec_rx_adap);
- dev->cec_rx_adap = NULL;
- goto unreg_dev;
- }
- cec_s_phys_addr(dev->cec_rx_adap, 0, false);
- v4l2_info(&dev->v4l2_dev, "CEC adapter %s registered for HDMI input 0\n",
- dev_name(&dev->cec_rx_adap->devnode.dev));
- }
-#endif
-
- ret = video_register_device(vfd, VFL_TYPE_GRABBER, vid_cap_nr[inst]);
- if (ret < 0)
- goto unreg_dev;
- v4l2_info(&dev->v4l2_dev, "V4L2 capture device registered as %s\n",
- video_device_node_name(vfd));
- }
-
- if (dev->has_vid_out) {
- vfd = &dev->vid_out_dev;
- vfd->vfl_dir = VFL_DIR_TX;
- vfd->fops = &vivid_fops;
- vfd->ioctl_ops = &vivid_ioctl_ops;
- vfd->device_caps = dev->vid_out_caps;
- vfd->release = video_device_release_empty;
- vfd->v4l2_dev = &dev->v4l2_dev;
- vfd->queue = &dev->vb_vid_out_q;
- vfd->tvnorms = tvnorms_out;
-
- /*
- * Provide a mutex to v4l2 core. It will be used to protect
- * all fops and v4l2 ioctls.
- */
- vfd->lock = &dev->mutex;
- video_set_drvdata(vfd, dev);
-
-#ifdef CONFIG_MEDIA_CONTROLLER
- dev->vid_out_pad.flags = MEDIA_PAD_FL_SOURCE;
- ret = media_entity_pads_init(&vfd->entity, 1, &dev->vid_out_pad);
- if (ret)
- goto unreg_dev;
-#endif
-
-#ifdef CONFIG_VIDEO_VIVID_CEC
- for (i = 0; i < cec_tx_bus_cnt; i++) {
- ret = cec_register_adapter(dev->cec_tx_adap[i], &pdev->dev);
- if (ret < 0) {
- for (; i < cec_tx_bus_cnt; i++) {
- cec_delete_adapter(dev->cec_tx_adap[i]);
- dev->cec_tx_adap[i] = NULL;
- }
- goto unreg_dev;
- }
- v4l2_info(&dev->v4l2_dev, "CEC adapter %s registered for HDMI output %d\n",
- dev_name(&dev->cec_tx_adap[i]->devnode.dev), i);
- if (i <= out_type_counter[HDMI])
- cec_s_phys_addr(dev->cec_tx_adap[i], i << 12, false);
- else
- cec_s_phys_addr(dev->cec_tx_adap[i], 0x1000, false);
- }
-#endif
-
- ret = video_register_device(vfd, VFL_TYPE_GRABBER, vid_out_nr[inst]);
- if (ret < 0)
- goto unreg_dev;
- v4l2_info(&dev->v4l2_dev, "V4L2 output device registered as %s\n",
- video_device_node_name(vfd));
- }
-
- if (dev->has_vbi_cap) {
- vfd = &dev->vbi_cap_dev;
- snprintf(vfd->name, sizeof(vfd->name),
- "vivid-%03d-vbi-cap", inst);
- vfd->fops = &vivid_fops;
- vfd->ioctl_ops = &vivid_ioctl_ops;
- vfd->device_caps = dev->vbi_cap_caps;
- vfd->release = video_device_release_empty;
- vfd->v4l2_dev = &dev->v4l2_dev;
- vfd->queue = &dev->vb_vbi_cap_q;
- vfd->lock = &dev->mutex;
- vfd->tvnorms = tvnorms_cap;
- video_set_drvdata(vfd, dev);
-
-#ifdef CONFIG_MEDIA_CONTROLLER
- dev->vbi_cap_pad.flags = MEDIA_PAD_FL_SINK;
- ret = media_entity_pads_init(&vfd->entity, 1, &dev->vbi_cap_pad);
- if (ret)
- goto unreg_dev;
-#endif
-
- ret = video_register_device(vfd, VFL_TYPE_VBI, vbi_cap_nr[inst]);
- if (ret < 0)
- goto unreg_dev;
- v4l2_info(&dev->v4l2_dev, "V4L2 capture device registered as %s, supports %s VBI\n",
- video_device_node_name(vfd),
- (dev->has_raw_vbi_cap && dev->has_sliced_vbi_cap) ?
- "raw and sliced" :
- (dev->has_raw_vbi_cap ? "raw" : "sliced"));
- }
-
- if (dev->has_vbi_out) {
- vfd = &dev->vbi_out_dev;
- snprintf(vfd->name, sizeof(vfd->name),
- "vivid-%03d-vbi-out", inst);
- vfd->vfl_dir = VFL_DIR_TX;
- vfd->fops = &vivid_fops;
- vfd->ioctl_ops = &vivid_ioctl_ops;
- vfd->device_caps = dev->vbi_out_caps;
- vfd->release = video_device_release_empty;
- vfd->v4l2_dev = &dev->v4l2_dev;
- vfd->queue = &dev->vb_vbi_out_q;
- vfd->lock = &dev->mutex;
- vfd->tvnorms = tvnorms_out;
- video_set_drvdata(vfd, dev);
-
-#ifdef CONFIG_MEDIA_CONTROLLER
- dev->vbi_out_pad.flags = MEDIA_PAD_FL_SOURCE;
- ret = media_entity_pads_init(&vfd->entity, 1, &dev->vbi_out_pad);
- if (ret)
- goto unreg_dev;
-#endif
-
- ret = video_register_device(vfd, VFL_TYPE_VBI, vbi_out_nr[inst]);
- if (ret < 0)
- goto unreg_dev;
- v4l2_info(&dev->v4l2_dev, "V4L2 output device registered as %s, supports %s VBI\n",
- video_device_node_name(vfd),
- (dev->has_raw_vbi_out && dev->has_sliced_vbi_out) ?
- "raw and sliced" :
- (dev->has_raw_vbi_out ? "raw" : "sliced"));
- }
-
- if (dev->has_sdr_cap) {
- vfd = &dev->sdr_cap_dev;
- snprintf(vfd->name, sizeof(vfd->name),
- "vivid-%03d-sdr-cap", inst);
- vfd->fops = &vivid_fops;
- vfd->ioctl_ops = &vivid_ioctl_ops;
- vfd->device_caps = dev->sdr_cap_caps;
- vfd->release = video_device_release_empty;
- vfd->v4l2_dev = &dev->v4l2_dev;
- vfd->queue = &dev->vb_sdr_cap_q;
- vfd->lock = &dev->mutex;
- video_set_drvdata(vfd, dev);
-
-#ifdef CONFIG_MEDIA_CONTROLLER
- dev->sdr_cap_pad.flags = MEDIA_PAD_FL_SINK;
- ret = media_entity_pads_init(&vfd->entity, 1, &dev->sdr_cap_pad);
- if (ret)
- goto unreg_dev;
-#endif
-
- ret = video_register_device(vfd, VFL_TYPE_SDR, sdr_cap_nr[inst]);
- if (ret < 0)
- goto unreg_dev;
- v4l2_info(&dev->v4l2_dev, "V4L2 capture device registered as %s\n",
- video_device_node_name(vfd));
- }
-
- if (dev->has_radio_rx) {
- vfd = &dev->radio_rx_dev;
- snprintf(vfd->name, sizeof(vfd->name),
- "vivid-%03d-rad-rx", inst);
- vfd->fops = &vivid_radio_fops;
- vfd->ioctl_ops = &vivid_ioctl_ops;
- vfd->device_caps = dev->radio_rx_caps;
- vfd->release = video_device_release_empty;
- vfd->v4l2_dev = &dev->v4l2_dev;
- vfd->lock = &dev->mutex;
- video_set_drvdata(vfd, dev);
-
- ret = video_register_device(vfd, VFL_TYPE_RADIO, radio_rx_nr[inst]);
- if (ret < 0)
- goto unreg_dev;
- v4l2_info(&dev->v4l2_dev, "V4L2 receiver device registered as %s\n",
- video_device_node_name(vfd));
- }
-
- if (dev->has_radio_tx) {
- vfd = &dev->radio_tx_dev;
- snprintf(vfd->name, sizeof(vfd->name),
- "vivid-%03d-rad-tx", inst);
- vfd->vfl_dir = VFL_DIR_TX;
- vfd->fops = &vivid_radio_fops;
- vfd->ioctl_ops = &vivid_ioctl_ops;
- vfd->device_caps = dev->radio_tx_caps;
- vfd->release = video_device_release_empty;
- vfd->v4l2_dev = &dev->v4l2_dev;
- vfd->lock = &dev->mutex;
- video_set_drvdata(vfd, dev);
-
- ret = video_register_device(vfd, VFL_TYPE_RADIO, radio_tx_nr[inst]);
- if (ret < 0)
- goto unreg_dev;
- v4l2_info(&dev->v4l2_dev, "V4L2 transmitter device registered as %s\n",
- video_device_node_name(vfd));
- }
-
-#ifdef CONFIG_MEDIA_CONTROLLER
- /* Register the media device */
- ret = media_device_register(&dev->mdev);
- if (ret) {
- dev_err(dev->mdev.dev,
- "media device register failed (err=%d)\n", ret);
- goto unreg_dev;
- }
-#endif
-
- /* Now that everything is fine, let's add it to device list */
- vivid_devs[inst] = dev;
-
- return 0;
-
-unreg_dev:
- video_unregister_device(&dev->radio_tx_dev);
- video_unregister_device(&dev->radio_rx_dev);
- video_unregister_device(&dev->sdr_cap_dev);
- video_unregister_device(&dev->vbi_out_dev);
- video_unregister_device(&dev->vbi_cap_dev);
- video_unregister_device(&dev->vid_out_dev);
- video_unregister_device(&dev->vid_cap_dev);
- cec_unregister_adapter(dev->cec_rx_adap);
- for (i = 0; i < MAX_OUTPUTS; i++)
- cec_unregister_adapter(dev->cec_tx_adap[i]);
- if (dev->cec_workqueue) {
- vivid_cec_bus_free_work(dev);
- destroy_workqueue(dev->cec_workqueue);
- }
-free_dev:
- v4l2_device_put(&dev->v4l2_dev);
- return ret;
-}
-
-/* This routine allocates from 1 to n_devs virtual drivers.
-
- The real maximum number of virtual drivers will depend on how many drivers
- will succeed. This is limited to the maximum number of devices that
- videodev supports, which is equal to VIDEO_NUM_DEVICES.
- */
-static int vivid_probe(struct platform_device *pdev)
-{
- const struct font_desc *font = find_font("VGA8x16");
- int ret = 0, i;
-
- if (font == NULL) {
- pr_err("vivid: could not find font\n");
- return -ENODEV;
- }
-
- tpg_set_font(font->data);
-
- n_devs = clamp_t(unsigned, n_devs, 1, VIVID_MAX_DEVS);
-
- for (i = 0; i < n_devs; i++) {
- ret = vivid_create_instance(pdev, i);
- if (ret) {
- /* If some instantiations succeeded, keep driver */
- if (i)
- ret = 0;
- break;
- }
- }
-
- if (ret < 0) {
- pr_err("vivid: error %d while loading driver\n", ret);
- return ret;
- }
-
- /* n_devs will reflect the actual number of allocated devices */
- n_devs = i;
-
- return ret;
-}
-
-static int vivid_remove(struct platform_device *pdev)
-{
- struct vivid_dev *dev;
- unsigned int i, j;
-
- for (i = 0; i < n_devs; i++) {
- dev = vivid_devs[i];
- if (!dev)
- continue;
-
-#ifdef CONFIG_MEDIA_CONTROLLER
- media_device_unregister(&dev->mdev);
-#endif
-
- if (dev->has_vid_cap) {
- v4l2_info(&dev->v4l2_dev, "unregistering %s\n",
- video_device_node_name(&dev->vid_cap_dev));
- video_unregister_device(&dev->vid_cap_dev);
- }
- if (dev->has_vid_out) {
- v4l2_info(&dev->v4l2_dev, "unregistering %s\n",
- video_device_node_name(&dev->vid_out_dev));
- video_unregister_device(&dev->vid_out_dev);
- }
- if (dev->has_vbi_cap) {
- v4l2_info(&dev->v4l2_dev, "unregistering %s\n",
- video_device_node_name(&dev->vbi_cap_dev));
- video_unregister_device(&dev->vbi_cap_dev);
- }
- if (dev->has_vbi_out) {
- v4l2_info(&dev->v4l2_dev, "unregistering %s\n",
- video_device_node_name(&dev->vbi_out_dev));
- video_unregister_device(&dev->vbi_out_dev);
- }
- if (dev->has_sdr_cap) {
- v4l2_info(&dev->v4l2_dev, "unregistering %s\n",
- video_device_node_name(&dev->sdr_cap_dev));
- video_unregister_device(&dev->sdr_cap_dev);
- }
- if (dev->has_radio_rx) {
- v4l2_info(&dev->v4l2_dev, "unregistering %s\n",
- video_device_node_name(&dev->radio_rx_dev));
- video_unregister_device(&dev->radio_rx_dev);
- }
- if (dev->has_radio_tx) {
- v4l2_info(&dev->v4l2_dev, "unregistering %s\n",
- video_device_node_name(&dev->radio_tx_dev));
- video_unregister_device(&dev->radio_tx_dev);
- }
- if (dev->has_fb) {
- v4l2_info(&dev->v4l2_dev, "unregistering fb%d\n",
- dev->fb_info.node);
- unregister_framebuffer(&dev->fb_info);
- vivid_fb_release_buffers(dev);
- }
- cec_unregister_adapter(dev->cec_rx_adap);
- for (j = 0; j < MAX_OUTPUTS; j++)
- cec_unregister_adapter(dev->cec_tx_adap[j]);
- if (dev->cec_workqueue) {
- vivid_cec_bus_free_work(dev);
- destroy_workqueue(dev->cec_workqueue);
- }
- v4l2_device_put(&dev->v4l2_dev);
- vivid_devs[i] = NULL;
- }
- return 0;
-}
-
-static void vivid_pdev_release(struct device *dev)
-{
-}
-
-static struct platform_device vivid_pdev = {
- .name = "vivid",
- .dev.release = vivid_pdev_release,
-};
-
-static struct platform_driver vivid_pdrv = {
- .probe = vivid_probe,
- .remove = vivid_remove,
- .driver = {
- .name = "vivid",
- },
-};
-
-static int __init vivid_init(void)
-{
- int ret;
-
- ret = platform_device_register(&vivid_pdev);
- if (ret)
- return ret;
-
- ret = platform_driver_register(&vivid_pdrv);
- if (ret)
- platform_device_unregister(&vivid_pdev);
-
- return ret;
-}
-
-static void __exit vivid_exit(void)
-{
- platform_driver_unregister(&vivid_pdrv);
- platform_device_unregister(&vivid_pdev);
-}
-
-module_init(vivid_init);
-module_exit(vivid_exit);
diff --git a/drivers/media/platform/vivid/vivid-core.h b/drivers/media/platform/vivid/vivid-core.h
deleted file mode 100644
index 7ebb146..0000000
--- a/drivers/media/platform/vivid/vivid-core.h
+++ /dev/null
@@ -1,567 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-/*
- * vivid-core.h - core datastructures
- *
- * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- */
-
-#ifndef _VIVID_CORE_H_
-#define _VIVID_CORE_H_
-
-#include <linux/fb.h>
-#include <linux/workqueue.h>
-#include <media/cec.h>
-#include <media/videobuf2-v4l2.h>
-#include <media/v4l2-device.h>
-#include <media/v4l2-dev.h>
-#include <media/v4l2-ctrls.h>
-#include <media/tpg/v4l2-tpg.h>
-#include "vivid-rds-gen.h"
-#include "vivid-vbi-gen.h"
-
-#define dprintk(dev, level, fmt, arg...) \
- v4l2_dbg(level, vivid_debug, &dev->v4l2_dev, fmt, ## arg)
-
-/* The maximum number of clip rectangles */
-#define MAX_CLIPS 16
-/* The maximum number of inputs */
-#define MAX_INPUTS 16
-/* The maximum number of outputs */
-#define MAX_OUTPUTS 16
-/* The maximum up or down scaling factor is 4 */
-#define MAX_ZOOM 4
-/* The maximum image width/height are set to 4K DMT */
-#define MAX_WIDTH 4096
-#define MAX_HEIGHT 2160
-/* The minimum image width/height */
-#define MIN_WIDTH 16
-#define MIN_HEIGHT 16
-/* The data_offset of plane 0 for the multiplanar formats */
-#define PLANE0_DATA_OFFSET 128
-
-/* The supported TV frequency range in MHz */
-#define MIN_TV_FREQ (44U * 16U)
-#define MAX_TV_FREQ (958U * 16U)
-
-/* The number of samples returned in every SDR buffer */
-#define SDR_CAP_SAMPLES_PER_BUF 0x4000
-
-/* used by the threads to know when to resync internal counters */
-#define JIFFIES_PER_DAY (3600U * 24U * HZ)
-#define JIFFIES_RESYNC (JIFFIES_PER_DAY * (0xf0000000U / JIFFIES_PER_DAY))
-
-extern const struct v4l2_rect vivid_min_rect;
-extern const struct v4l2_rect vivid_max_rect;
-extern unsigned vivid_debug;
-
-struct vivid_fmt {
- u32 fourcc; /* v4l2 format id */
- enum tgp_color_enc color_enc;
- bool can_do_overlay;
- u8 vdownsampling[TPG_MAX_PLANES];
- u32 alpha_mask;
- u8 planes;
- u8 buffers;
- u32 data_offset[TPG_MAX_PLANES];
- u32 bit_depth[TPG_MAX_PLANES];
-};
-
-extern struct vivid_fmt vivid_formats[];
-
-/* buffer for one video frame */
-struct vivid_buffer {
- /* common v4l buffer stuff -- must be first */
- struct vb2_v4l2_buffer vb;
- struct list_head list;
-};
-
-enum vivid_input {
- WEBCAM,
- TV,
- SVID,
- HDMI,
-};
-
-enum vivid_signal_mode {
- CURRENT_DV_TIMINGS,
- CURRENT_STD = CURRENT_DV_TIMINGS,
- NO_SIGNAL,
- NO_LOCK,
- OUT_OF_RANGE,
- SELECTED_DV_TIMINGS,
- SELECTED_STD = SELECTED_DV_TIMINGS,
- CYCLE_DV_TIMINGS,
- CYCLE_STD = CYCLE_DV_TIMINGS,
- CUSTOM_DV_TIMINGS,
-};
-
-enum vivid_colorspace {
- VIVID_CS_170M,
- VIVID_CS_709,
- VIVID_CS_SRGB,
- VIVID_CS_OPRGB,
- VIVID_CS_2020,
- VIVID_CS_DCI_P3,
- VIVID_CS_240M,
- VIVID_CS_SYS_M,
- VIVID_CS_SYS_BG,
-};
-
-#define VIVID_INVALID_SIGNAL(mode) \
- ((mode) == NO_SIGNAL || (mode) == NO_LOCK || (mode) == OUT_OF_RANGE)
-
-struct vivid_cec_work {
- struct list_head list;
- struct delayed_work work;
- struct cec_adapter *adap;
- struct vivid_dev *dev;
- unsigned int usecs;
- unsigned int timeout_ms;
- u8 tx_status;
- struct cec_msg msg;
-};
-
-struct vivid_dev {
- unsigned inst;
- struct v4l2_device v4l2_dev;
-#ifdef CONFIG_MEDIA_CONTROLLER
- struct media_device mdev;
- struct media_pad vid_cap_pad;
- struct media_pad vid_out_pad;
- struct media_pad vbi_cap_pad;
- struct media_pad vbi_out_pad;
- struct media_pad sdr_cap_pad;
-#endif
- struct v4l2_ctrl_handler ctrl_hdl_user_gen;
- struct v4l2_ctrl_handler ctrl_hdl_user_vid;
- struct v4l2_ctrl_handler ctrl_hdl_user_aud;
- struct v4l2_ctrl_handler ctrl_hdl_streaming;
- struct v4l2_ctrl_handler ctrl_hdl_sdtv_cap;
- struct v4l2_ctrl_handler ctrl_hdl_loop_cap;
- struct v4l2_ctrl_handler ctrl_hdl_fb;
- struct video_device vid_cap_dev;
- struct v4l2_ctrl_handler ctrl_hdl_vid_cap;
- struct video_device vid_out_dev;
- struct v4l2_ctrl_handler ctrl_hdl_vid_out;
- struct video_device vbi_cap_dev;
- struct v4l2_ctrl_handler ctrl_hdl_vbi_cap;
- struct video_device vbi_out_dev;
- struct v4l2_ctrl_handler ctrl_hdl_vbi_out;
- struct video_device radio_rx_dev;
- struct v4l2_ctrl_handler ctrl_hdl_radio_rx;
- struct video_device radio_tx_dev;
- struct v4l2_ctrl_handler ctrl_hdl_radio_tx;
- struct video_device sdr_cap_dev;
- struct v4l2_ctrl_handler ctrl_hdl_sdr_cap;
- spinlock_t slock;
- struct mutex mutex;
-
- /* capabilities */
- u32 vid_cap_caps;
- u32 vid_out_caps;
- u32 vbi_cap_caps;
- u32 vbi_out_caps;
- u32 sdr_cap_caps;
- u32 radio_rx_caps;
- u32 radio_tx_caps;
-
- /* supported features */
- bool multiplanar;
- unsigned num_inputs;
- unsigned int num_hdmi_inputs;
- u8 input_type[MAX_INPUTS];
- u8 input_name_counter[MAX_INPUTS];
- unsigned num_outputs;
- unsigned int num_hdmi_outputs;
- u8 output_type[MAX_OUTPUTS];
- u8 output_name_counter[MAX_OUTPUTS];
- bool has_audio_inputs;
- bool has_audio_outputs;
- bool has_vid_cap;
- bool has_vid_out;
- bool has_vbi_cap;
- bool has_raw_vbi_cap;
- bool has_sliced_vbi_cap;
- bool has_vbi_out;
- bool has_raw_vbi_out;
- bool has_sliced_vbi_out;
- bool has_radio_rx;
- bool has_radio_tx;
- bool has_sdr_cap;
- bool has_fb;
-
- bool can_loop_video;
-
- /* controls */
- struct v4l2_ctrl *brightness;
- struct v4l2_ctrl *contrast;
- struct v4l2_ctrl *saturation;
- struct v4l2_ctrl *hue;
- struct {
- /* autogain/gain cluster */
- struct v4l2_ctrl *autogain;
- struct v4l2_ctrl *gain;
- };
- struct v4l2_ctrl *volume;
- struct v4l2_ctrl *mute;
- struct v4l2_ctrl *alpha;
- struct v4l2_ctrl *button;
- struct v4l2_ctrl *boolean;
- struct v4l2_ctrl *int32;
- struct v4l2_ctrl *int64;
- struct v4l2_ctrl *menu;
- struct v4l2_ctrl *string;
- struct v4l2_ctrl *bitmask;
- struct v4l2_ctrl *int_menu;
- struct v4l2_ctrl *test_pattern;
- struct v4l2_ctrl *colorspace;
- struct v4l2_ctrl *rgb_range_cap;
- struct v4l2_ctrl *real_rgb_range_cap;
- struct {
- /* std_signal_mode/standard cluster */
- struct v4l2_ctrl *ctrl_std_signal_mode;
- struct v4l2_ctrl *ctrl_standard;
- };
- struct {
- /* dv_timings_signal_mode/timings cluster */
- struct v4l2_ctrl *ctrl_dv_timings_signal_mode;
- struct v4l2_ctrl *ctrl_dv_timings;
- };
- struct v4l2_ctrl *ctrl_display_present;
- struct v4l2_ctrl *ctrl_has_crop_cap;
- struct v4l2_ctrl *ctrl_has_compose_cap;
- struct v4l2_ctrl *ctrl_has_scaler_cap;
- struct v4l2_ctrl *ctrl_has_crop_out;
- struct v4l2_ctrl *ctrl_has_compose_out;
- struct v4l2_ctrl *ctrl_has_scaler_out;
- struct v4l2_ctrl *ctrl_tx_mode;
- struct v4l2_ctrl *ctrl_tx_rgb_range;
- struct v4l2_ctrl *ctrl_tx_edid_present;
- struct v4l2_ctrl *ctrl_tx_hotplug;
- struct v4l2_ctrl *ctrl_tx_rxsense;
-
- struct v4l2_ctrl *ctrl_rx_power_present;
-
- struct v4l2_ctrl *radio_tx_rds_pi;
- struct v4l2_ctrl *radio_tx_rds_pty;
- struct v4l2_ctrl *radio_tx_rds_mono_stereo;
- struct v4l2_ctrl *radio_tx_rds_art_head;
- struct v4l2_ctrl *radio_tx_rds_compressed;
- struct v4l2_ctrl *radio_tx_rds_dyn_pty;
- struct v4l2_ctrl *radio_tx_rds_ta;
- struct v4l2_ctrl *radio_tx_rds_tp;
- struct v4l2_ctrl *radio_tx_rds_ms;
- struct v4l2_ctrl *radio_tx_rds_psname;
- struct v4l2_ctrl *radio_tx_rds_radiotext;
-
- struct v4l2_ctrl *radio_rx_rds_pty;
- struct v4l2_ctrl *radio_rx_rds_ta;
- struct v4l2_ctrl *radio_rx_rds_tp;
- struct v4l2_ctrl *radio_rx_rds_ms;
- struct v4l2_ctrl *radio_rx_rds_psname;
- struct v4l2_ctrl *radio_rx_rds_radiotext;
-
- unsigned input_brightness[MAX_INPUTS];
- unsigned osd_mode;
- unsigned button_pressed;
- bool sensor_hflip;
- bool sensor_vflip;
- bool hflip;
- bool vflip;
- bool vbi_cap_interlaced;
- bool loop_video;
- bool reduced_fps;
-
- /* Framebuffer */
- unsigned long video_pbase;
- void *video_vbase;
- u32 video_buffer_size;
- int display_width;
- int display_height;
- int display_byte_stride;
- int bits_per_pixel;
- int bytes_per_pixel;
- struct fb_info fb_info;
- struct fb_var_screeninfo fb_defined;
- struct fb_fix_screeninfo fb_fix;
-
- /* Error injection */
- bool queue_setup_error;
- bool buf_prepare_error;
- bool start_streaming_error;
- bool dqbuf_error;
- bool req_validate_error;
- bool seq_wrap;
- bool time_wrap;
- u64 time_wrap_offset;
- unsigned perc_dropped_buffers;
- enum vivid_signal_mode std_signal_mode[MAX_INPUTS];
- unsigned int query_std_last[MAX_INPUTS];
- v4l2_std_id query_std[MAX_INPUTS];
- enum tpg_video_aspect std_aspect_ratio[MAX_INPUTS];
-
- enum vivid_signal_mode dv_timings_signal_mode[MAX_INPUTS];
- char **query_dv_timings_qmenu;
- char *query_dv_timings_qmenu_strings;
- unsigned query_dv_timings_size;
- unsigned int query_dv_timings_last[MAX_INPUTS];
- unsigned int query_dv_timings[MAX_INPUTS];
- enum tpg_video_aspect dv_timings_aspect_ratio[MAX_INPUTS];
-
- /* Input */
- unsigned input;
- v4l2_std_id std_cap[MAX_INPUTS];
- struct v4l2_dv_timings dv_timings_cap[MAX_INPUTS];
- int dv_timings_cap_sel[MAX_INPUTS];
- u32 service_set_cap;
- struct vivid_vbi_gen_data vbi_gen;
- u8 *edid;
- unsigned edid_blocks;
- unsigned edid_max_blocks;
- unsigned webcam_size_idx;
- unsigned webcam_ival_idx;
- unsigned tv_freq;
- unsigned tv_audmode;
- unsigned tv_field_cap;
- unsigned tv_audio_input;
-
- u32 power_present;
-
- /* Capture Overlay */
- struct v4l2_framebuffer fb_cap;
- struct v4l2_fh *overlay_cap_owner;
- void *fb_vbase_cap;
- int overlay_cap_top, overlay_cap_left;
- enum v4l2_field overlay_cap_field;
- void *bitmap_cap;
- struct v4l2_clip clips_cap[MAX_CLIPS];
- struct v4l2_clip try_clips_cap[MAX_CLIPS];
- unsigned clipcount_cap;
-
- /* Output */
- unsigned output;
- v4l2_std_id std_out;
- struct v4l2_dv_timings dv_timings_out;
- u32 colorspace_out;
- u32 ycbcr_enc_out;
- u32 hsv_enc_out;
- u32 quantization_out;
- u32 xfer_func_out;
- u32 service_set_out;
- unsigned bytesperline_out[TPG_MAX_PLANES];
- unsigned tv_field_out;
- unsigned tv_audio_output;
- bool vbi_out_have_wss;
- u8 vbi_out_wss[2];
- bool vbi_out_have_cc[2];
- u8 vbi_out_cc[2][2];
- bool dvi_d_out;
- u8 *scaled_line;
- u8 *blended_line;
- unsigned cur_scaled_line;
- bool display_present[MAX_OUTPUTS];
-
- /* Output Overlay */
- void *fb_vbase_out;
- bool overlay_out_enabled;
- int overlay_out_top, overlay_out_left;
- void *bitmap_out;
- struct v4l2_clip clips_out[MAX_CLIPS];
- struct v4l2_clip try_clips_out[MAX_CLIPS];
- unsigned clipcount_out;
- unsigned fbuf_out_flags;
- u32 chromakey_out;
- u8 global_alpha_out;
-
- /* video capture */
- struct tpg_data tpg;
- unsigned ms_vid_cap;
- bool must_blank[VIDEO_MAX_FRAME];
-
- const struct vivid_fmt *fmt_cap;
- struct v4l2_fract timeperframe_vid_cap;
- enum v4l2_field field_cap;
- struct v4l2_rect src_rect;
- struct v4l2_rect fmt_cap_rect;
- struct v4l2_rect crop_cap;
- struct v4l2_rect compose_cap;
- struct v4l2_rect crop_bounds_cap;
- struct vb2_queue vb_vid_cap_q;
- struct list_head vid_cap_active;
- struct vb2_queue vb_vbi_cap_q;
- struct list_head vbi_cap_active;
-
- /* thread for generating video capture stream */
- struct task_struct *kthread_vid_cap;
- unsigned long jiffies_vid_cap;
- u64 cap_stream_start;
- u64 cap_frame_period;
- u64 cap_frame_eof_offset;
- u32 cap_seq_offset;
- u32 cap_seq_count;
- bool cap_seq_resync;
- u32 vid_cap_seq_start;
- u32 vid_cap_seq_count;
- bool vid_cap_streaming;
- u32 vbi_cap_seq_start;
- u32 vbi_cap_seq_count;
- bool vbi_cap_streaming;
- bool stream_sliced_vbi_cap;
-
- /* video output */
- const struct vivid_fmt *fmt_out;
- struct v4l2_fract timeperframe_vid_out;
- enum v4l2_field field_out;
- struct v4l2_rect sink_rect;
- struct v4l2_rect fmt_out_rect;
- struct v4l2_rect crop_out;
- struct v4l2_rect compose_out;
- struct v4l2_rect compose_bounds_out;
- struct vb2_queue vb_vid_out_q;
- struct list_head vid_out_active;
- struct vb2_queue vb_vbi_out_q;
- struct list_head vbi_out_active;
-
- /* video loop precalculated rectangles */
-
- /*
- * Intersection between what the output side composes and the capture side
- * crops. I.e., what actually needs to be copied from the output buffer to
- * the capture buffer.
- */
- struct v4l2_rect loop_vid_copy;
- /* The part of the output buffer that (after scaling) corresponds to loop_vid_copy. */
- struct v4l2_rect loop_vid_out;
- /* The part of the capture buffer that (after scaling) corresponds to loop_vid_copy. */
- struct v4l2_rect loop_vid_cap;
- /*
- * The intersection of the framebuffer, the overlay output window and
- * loop_vid_copy. I.e., the part of the framebuffer that actually should be
- * blended with the compose_out rectangle. This uses the framebuffer origin.
- */
- struct v4l2_rect loop_fb_copy;
- /* The same as loop_fb_copy but with compose_out origin. */
- struct v4l2_rect loop_vid_overlay;
- /*
- * The part of the capture buffer that (after scaling) corresponds
- * to loop_vid_overlay.
- */
- struct v4l2_rect loop_vid_overlay_cap;
-
- /* thread for generating video output stream */
- struct task_struct *kthread_vid_out;
- unsigned long jiffies_vid_out;
- u32 out_seq_offset;
- u32 out_seq_count;
- bool out_seq_resync;
- u32 vid_out_seq_start;
- u32 vid_out_seq_count;
- bool vid_out_streaming;
- u32 vbi_out_seq_start;
- u32 vbi_out_seq_count;
- bool vbi_out_streaming;
- bool stream_sliced_vbi_out;
-
- /* SDR capture */
- struct vb2_queue vb_sdr_cap_q;
- struct list_head sdr_cap_active;
- u32 sdr_pixelformat; /* v4l2 format id */
- unsigned sdr_buffersize;
- unsigned sdr_adc_freq;
- unsigned sdr_fm_freq;
- unsigned sdr_fm_deviation;
- int sdr_fixp_src_phase;
- int sdr_fixp_mod_phase;
-
- bool tstamp_src_is_soe;
- bool has_crop_cap;
- bool has_compose_cap;
- bool has_scaler_cap;
- bool has_crop_out;
- bool has_compose_out;
- bool has_scaler_out;
-
- /* thread for generating SDR stream */
- struct task_struct *kthread_sdr_cap;
- unsigned long jiffies_sdr_cap;
- u32 sdr_cap_seq_offset;
- u32 sdr_cap_seq_count;
- bool sdr_cap_seq_resync;
-
- /* RDS generator */
- struct vivid_rds_gen rds_gen;
-
- /* Radio receiver */
- unsigned radio_rx_freq;
- unsigned radio_rx_audmode;
- int radio_rx_sig_qual;
- unsigned radio_rx_hw_seek_mode;
- bool radio_rx_hw_seek_prog_lim;
- bool radio_rx_rds_controls;
- bool radio_rx_rds_enabled;
- unsigned radio_rx_rds_use_alternates;
- unsigned radio_rx_rds_last_block;
- struct v4l2_fh *radio_rx_rds_owner;
-
- /* Radio transmitter */
- unsigned radio_tx_freq;
- unsigned radio_tx_subchans;
- bool radio_tx_rds_controls;
- unsigned radio_tx_rds_last_block;
- struct v4l2_fh *radio_tx_rds_owner;
-
- /* Shared between radio receiver and transmitter */
- bool radio_rds_loop;
- ktime_t radio_rds_init_time;
-
- /* CEC */
- struct cec_adapter *cec_rx_adap;
- struct cec_adapter *cec_tx_adap[MAX_OUTPUTS];
- struct workqueue_struct *cec_workqueue;
- spinlock_t cec_slock;
- struct list_head cec_work_list;
- unsigned int cec_xfer_time_jiffies;
- unsigned long cec_xfer_start_jiffies;
- u8 cec_output2bus_map[MAX_OUTPUTS];
-
- /* CEC OSD String */
- char osd[14];
- unsigned long osd_jiffies;
-};
-
-static inline bool vivid_is_webcam(const struct vivid_dev *dev)
-{
- return dev->input_type[dev->input] == WEBCAM;
-}
-
-static inline bool vivid_is_tv_cap(const struct vivid_dev *dev)
-{
- return dev->input_type[dev->input] == TV;
-}
-
-static inline bool vivid_is_svid_cap(const struct vivid_dev *dev)
-{
- return dev->input_type[dev->input] == SVID;
-}
-
-static inline bool vivid_is_hdmi_cap(const struct vivid_dev *dev)
-{
- return dev->input_type[dev->input] == HDMI;
-}
-
-static inline bool vivid_is_sdtv_cap(const struct vivid_dev *dev)
-{
- return vivid_is_tv_cap(dev) || vivid_is_svid_cap(dev);
-}
-
-static inline bool vivid_is_svid_out(const struct vivid_dev *dev)
-{
- return dev->output_type[dev->output] == SVID;
-}
-
-static inline bool vivid_is_hdmi_out(const struct vivid_dev *dev)
-{
- return dev->output_type[dev->output] == HDMI;
-}
-
-#endif
diff --git a/drivers/media/platform/vivid/vivid-ctrls.c b/drivers/media/platform/vivid/vivid-ctrls.c
deleted file mode 100644
index cb19a9a..0000000
--- a/drivers/media/platform/vivid/vivid-ctrls.c
+++ /dev/null
@@ -1,1839 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * vivid-ctrls.c - control support functions.
- *
- * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- */
-
-#include <linux/errno.h>
-#include <linux/kernel.h>
-#include <linux/videodev2.h>
-#include <media/v4l2-event.h>
-#include <media/v4l2-common.h>
-
-#include "vivid-core.h"
-#include "vivid-vid-cap.h"
-#include "vivid-vid-out.h"
-#include "vivid-vid-common.h"
-#include "vivid-radio-common.h"
-#include "vivid-osd.h"
-#include "vivid-ctrls.h"
-#include "vivid-cec.h"
-
-#define VIVID_CID_CUSTOM_BASE (V4L2_CID_USER_BASE | 0xf000)
-#define VIVID_CID_BUTTON (VIVID_CID_CUSTOM_BASE + 0)
-#define VIVID_CID_BOOLEAN (VIVID_CID_CUSTOM_BASE + 1)
-#define VIVID_CID_INTEGER (VIVID_CID_CUSTOM_BASE + 2)
-#define VIVID_CID_INTEGER64 (VIVID_CID_CUSTOM_BASE + 3)
-#define VIVID_CID_MENU (VIVID_CID_CUSTOM_BASE + 4)
-#define VIVID_CID_STRING (VIVID_CID_CUSTOM_BASE + 5)
-#define VIVID_CID_BITMASK (VIVID_CID_CUSTOM_BASE + 6)
-#define VIVID_CID_INTMENU (VIVID_CID_CUSTOM_BASE + 7)
-#define VIVID_CID_U32_ARRAY (VIVID_CID_CUSTOM_BASE + 8)
-#define VIVID_CID_U16_MATRIX (VIVID_CID_CUSTOM_BASE + 9)
-#define VIVID_CID_U8_4D_ARRAY (VIVID_CID_CUSTOM_BASE + 10)
-
-#define VIVID_CID_VIVID_BASE (0x00f00000 | 0xf000)
-#define VIVID_CID_VIVID_CLASS (0x00f00000 | 1)
-#define VIVID_CID_TEST_PATTERN (VIVID_CID_VIVID_BASE + 0)
-#define VIVID_CID_OSD_TEXT_MODE (VIVID_CID_VIVID_BASE + 1)
-#define VIVID_CID_HOR_MOVEMENT (VIVID_CID_VIVID_BASE + 2)
-#define VIVID_CID_VERT_MOVEMENT (VIVID_CID_VIVID_BASE + 3)
-#define VIVID_CID_SHOW_BORDER (VIVID_CID_VIVID_BASE + 4)
-#define VIVID_CID_SHOW_SQUARE (VIVID_CID_VIVID_BASE + 5)
-#define VIVID_CID_INSERT_SAV (VIVID_CID_VIVID_BASE + 6)
-#define VIVID_CID_INSERT_EAV (VIVID_CID_VIVID_BASE + 7)
-#define VIVID_CID_VBI_CAP_INTERLACED (VIVID_CID_VIVID_BASE + 8)
-
-#define VIVID_CID_HFLIP (VIVID_CID_VIVID_BASE + 20)
-#define VIVID_CID_VFLIP (VIVID_CID_VIVID_BASE + 21)
-#define VIVID_CID_STD_ASPECT_RATIO (VIVID_CID_VIVID_BASE + 22)
-#define VIVID_CID_DV_TIMINGS_ASPECT_RATIO (VIVID_CID_VIVID_BASE + 23)
-#define VIVID_CID_TSTAMP_SRC (VIVID_CID_VIVID_BASE + 24)
-#define VIVID_CID_COLORSPACE (VIVID_CID_VIVID_BASE + 25)
-#define VIVID_CID_XFER_FUNC (VIVID_CID_VIVID_BASE + 26)
-#define VIVID_CID_YCBCR_ENC (VIVID_CID_VIVID_BASE + 27)
-#define VIVID_CID_QUANTIZATION (VIVID_CID_VIVID_BASE + 28)
-#define VIVID_CID_LIMITED_RGB_RANGE (VIVID_CID_VIVID_BASE + 29)
-#define VIVID_CID_ALPHA_MODE (VIVID_CID_VIVID_BASE + 30)
-#define VIVID_CID_HAS_CROP_CAP (VIVID_CID_VIVID_BASE + 31)
-#define VIVID_CID_HAS_COMPOSE_CAP (VIVID_CID_VIVID_BASE + 32)
-#define VIVID_CID_HAS_SCALER_CAP (VIVID_CID_VIVID_BASE + 33)
-#define VIVID_CID_HAS_CROP_OUT (VIVID_CID_VIVID_BASE + 34)
-#define VIVID_CID_HAS_COMPOSE_OUT (VIVID_CID_VIVID_BASE + 35)
-#define VIVID_CID_HAS_SCALER_OUT (VIVID_CID_VIVID_BASE + 36)
-#define VIVID_CID_LOOP_VIDEO (VIVID_CID_VIVID_BASE + 37)
-#define VIVID_CID_SEQ_WRAP (VIVID_CID_VIVID_BASE + 38)
-#define VIVID_CID_TIME_WRAP (VIVID_CID_VIVID_BASE + 39)
-#define VIVID_CID_MAX_EDID_BLOCKS (VIVID_CID_VIVID_BASE + 40)
-#define VIVID_CID_PERCENTAGE_FILL (VIVID_CID_VIVID_BASE + 41)
-#define VIVID_CID_REDUCED_FPS (VIVID_CID_VIVID_BASE + 42)
-#define VIVID_CID_HSV_ENC (VIVID_CID_VIVID_BASE + 43)
-#define VIVID_CID_DISPLAY_PRESENT (VIVID_CID_VIVID_BASE + 44)
-
-#define VIVID_CID_STD_SIGNAL_MODE (VIVID_CID_VIVID_BASE + 60)
-#define VIVID_CID_STANDARD (VIVID_CID_VIVID_BASE + 61)
-#define VIVID_CID_DV_TIMINGS_SIGNAL_MODE (VIVID_CID_VIVID_BASE + 62)
-#define VIVID_CID_DV_TIMINGS (VIVID_CID_VIVID_BASE + 63)
-#define VIVID_CID_PERC_DROPPED (VIVID_CID_VIVID_BASE + 64)
-#define VIVID_CID_DISCONNECT (VIVID_CID_VIVID_BASE + 65)
-#define VIVID_CID_DQBUF_ERROR (VIVID_CID_VIVID_BASE + 66)
-#define VIVID_CID_QUEUE_SETUP_ERROR (VIVID_CID_VIVID_BASE + 67)
-#define VIVID_CID_BUF_PREPARE_ERROR (VIVID_CID_VIVID_BASE + 68)
-#define VIVID_CID_START_STR_ERROR (VIVID_CID_VIVID_BASE + 69)
-#define VIVID_CID_QUEUE_ERROR (VIVID_CID_VIVID_BASE + 70)
-#define VIVID_CID_CLEAR_FB (VIVID_CID_VIVID_BASE + 71)
-#define VIVID_CID_REQ_VALIDATE_ERROR (VIVID_CID_VIVID_BASE + 72)
-
-#define VIVID_CID_RADIO_SEEK_MODE (VIVID_CID_VIVID_BASE + 90)
-#define VIVID_CID_RADIO_SEEK_PROG_LIM (VIVID_CID_VIVID_BASE + 91)
-#define VIVID_CID_RADIO_RX_RDS_RBDS (VIVID_CID_VIVID_BASE + 92)
-#define VIVID_CID_RADIO_RX_RDS_BLOCKIO (VIVID_CID_VIVID_BASE + 93)
-
-#define VIVID_CID_RADIO_TX_RDS_BLOCKIO (VIVID_CID_VIVID_BASE + 94)
-
-#define VIVID_CID_SDR_CAP_FM_DEVIATION (VIVID_CID_VIVID_BASE + 110)
-
-/* General User Controls */
-
-static int vivid_user_gen_s_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct vivid_dev *dev = container_of(ctrl->handler, struct vivid_dev, ctrl_hdl_user_gen);
-
- switch (ctrl->id) {
- case VIVID_CID_DISCONNECT:
- v4l2_info(&dev->v4l2_dev, "disconnect\n");
- clear_bit(V4L2_FL_REGISTERED, &dev->vid_cap_dev.flags);
- clear_bit(V4L2_FL_REGISTERED, &dev->vid_out_dev.flags);
- clear_bit(V4L2_FL_REGISTERED, &dev->vbi_cap_dev.flags);
- clear_bit(V4L2_FL_REGISTERED, &dev->vbi_out_dev.flags);
- clear_bit(V4L2_FL_REGISTERED, &dev->sdr_cap_dev.flags);
- clear_bit(V4L2_FL_REGISTERED, &dev->radio_rx_dev.flags);
- clear_bit(V4L2_FL_REGISTERED, &dev->radio_tx_dev.flags);
- break;
- case VIVID_CID_BUTTON:
- dev->button_pressed = 30;
- break;
- }
- return 0;
-}
-
-static const struct v4l2_ctrl_ops vivid_user_gen_ctrl_ops = {
- .s_ctrl = vivid_user_gen_s_ctrl,
-};
-
-static const struct v4l2_ctrl_config vivid_ctrl_button = {
- .ops = &vivid_user_gen_ctrl_ops,
- .id = VIVID_CID_BUTTON,
- .name = "Button",
- .type = V4L2_CTRL_TYPE_BUTTON,
-};
-
-static const struct v4l2_ctrl_config vivid_ctrl_boolean = {
- .ops = &vivid_user_gen_ctrl_ops,
- .id = VIVID_CID_BOOLEAN,
- .name = "Boolean",
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .min = 0,
- .max = 1,
- .step = 1,
- .def = 1,
-};
-
-static const struct v4l2_ctrl_config vivid_ctrl_int32 = {
- .ops = &vivid_user_gen_ctrl_ops,
- .id = VIVID_CID_INTEGER,
- .name = "Integer 32 Bits",
- .type = V4L2_CTRL_TYPE_INTEGER,
- .min = 0xffffffff80000000ULL,
- .max = 0x7fffffff,
- .step = 1,
-};
-
-static const struct v4l2_ctrl_config vivid_ctrl_int64 = {
- .ops = &vivid_user_gen_ctrl_ops,
- .id = VIVID_CID_INTEGER64,
- .name = "Integer 64 Bits",
- .type = V4L2_CTRL_TYPE_INTEGER64,
- .min = 0x8000000000000000ULL,
- .max = 0x7fffffffffffffffLL,
- .step = 1,
-};
-
-static const struct v4l2_ctrl_config vivid_ctrl_u32_array = {
- .ops = &vivid_user_gen_ctrl_ops,
- .id = VIVID_CID_U32_ARRAY,
- .name = "U32 1 Element Array",
- .type = V4L2_CTRL_TYPE_U32,
- .def = 0x18,
- .min = 0x10,
- .max = 0x20000,
- .step = 1,
- .dims = { 1 },
-};
-
-static const struct v4l2_ctrl_config vivid_ctrl_u16_matrix = {
- .ops = &vivid_user_gen_ctrl_ops,
- .id = VIVID_CID_U16_MATRIX,
- .name = "U16 8x16 Matrix",
- .type = V4L2_CTRL_TYPE_U16,
- .def = 0x18,
- .min = 0x10,
- .max = 0x2000,
- .step = 1,
- .dims = { 8, 16 },
-};
-
-static const struct v4l2_ctrl_config vivid_ctrl_u8_4d_array = {
- .ops = &vivid_user_gen_ctrl_ops,
- .id = VIVID_CID_U8_4D_ARRAY,
- .name = "U8 2x3x4x5 Array",
- .type = V4L2_CTRL_TYPE_U8,
- .def = 0x18,
- .min = 0x10,
- .max = 0x20,
- .step = 1,
- .dims = { 2, 3, 4, 5 },
-};
-
-static const char * const vivid_ctrl_menu_strings[] = {
- "Menu Item 0 (Skipped)",
- "Menu Item 1",
- "Menu Item 2 (Skipped)",
- "Menu Item 3",
- "Menu Item 4",
- "Menu Item 5 (Skipped)",
- NULL,
-};
-
-static const struct v4l2_ctrl_config vivid_ctrl_menu = {
- .ops = &vivid_user_gen_ctrl_ops,
- .id = VIVID_CID_MENU,
- .name = "Menu",
- .type = V4L2_CTRL_TYPE_MENU,
- .min = 1,
- .max = 4,
- .def = 3,
- .menu_skip_mask = 0x04,
- .qmenu = vivid_ctrl_menu_strings,
-};
-
-static const struct v4l2_ctrl_config vivid_ctrl_string = {
- .ops = &vivid_user_gen_ctrl_ops,
- .id = VIVID_CID_STRING,
- .name = "String",
- .type = V4L2_CTRL_TYPE_STRING,
- .min = 2,
- .max = 4,
- .step = 1,
-};
-
-static const struct v4l2_ctrl_config vivid_ctrl_bitmask = {
- .ops = &vivid_user_gen_ctrl_ops,
- .id = VIVID_CID_BITMASK,
- .name = "Bitmask",
- .type = V4L2_CTRL_TYPE_BITMASK,
- .def = 0x80002000,
- .min = 0,
- .max = 0x80402010,
- .step = 0,
-};
-
-static const s64 vivid_ctrl_int_menu_values[] = {
- 1, 1, 2, 3, 5, 8, 13, 21, 42,
-};
-
-static const struct v4l2_ctrl_config vivid_ctrl_int_menu = {
- .ops = &vivid_user_gen_ctrl_ops,
- .id = VIVID_CID_INTMENU,
- .name = "Integer Menu",
- .type = V4L2_CTRL_TYPE_INTEGER_MENU,
- .min = 1,
- .max = 8,
- .def = 4,
- .menu_skip_mask = 0x02,
- .qmenu_int = vivid_ctrl_int_menu_values,
-};
-
-static const struct v4l2_ctrl_config vivid_ctrl_disconnect = {
- .ops = &vivid_user_gen_ctrl_ops,
- .id = VIVID_CID_DISCONNECT,
- .name = "Disconnect",
- .type = V4L2_CTRL_TYPE_BUTTON,
-};
-
-
-/* Framebuffer Controls */
-
-static int vivid_fb_s_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct vivid_dev *dev = container_of(ctrl->handler,
- struct vivid_dev, ctrl_hdl_fb);
-
- switch (ctrl->id) {
- case VIVID_CID_CLEAR_FB:
- vivid_clear_fb(dev);
- break;
- }
- return 0;
-}
-
-static const struct v4l2_ctrl_ops vivid_fb_ctrl_ops = {
- .s_ctrl = vivid_fb_s_ctrl,
-};
-
-static const struct v4l2_ctrl_config vivid_ctrl_clear_fb = {
- .ops = &vivid_fb_ctrl_ops,
- .id = VIVID_CID_CLEAR_FB,
- .name = "Clear Framebuffer",
- .type = V4L2_CTRL_TYPE_BUTTON,
-};
-
-
-/* Video User Controls */
-
-static int vivid_user_vid_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct vivid_dev *dev = container_of(ctrl->handler, struct vivid_dev, ctrl_hdl_user_vid);
-
- switch (ctrl->id) {
- case V4L2_CID_AUTOGAIN:
- dev->gain->val = (jiffies_to_msecs(jiffies) / 1000) & 0xff;
- break;
- }
- return 0;
-}
-
-static int vivid_user_vid_s_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct vivid_dev *dev = container_of(ctrl->handler, struct vivid_dev, ctrl_hdl_user_vid);
-
- switch (ctrl->id) {
- case V4L2_CID_BRIGHTNESS:
- dev->input_brightness[dev->input] = ctrl->val - dev->input * 128;
- tpg_s_brightness(&dev->tpg, dev->input_brightness[dev->input]);
- break;
- case V4L2_CID_CONTRAST:
- tpg_s_contrast(&dev->tpg, ctrl->val);
- break;
- case V4L2_CID_SATURATION:
- tpg_s_saturation(&dev->tpg, ctrl->val);
- break;
- case V4L2_CID_HUE:
- tpg_s_hue(&dev->tpg, ctrl->val);
- break;
- case V4L2_CID_HFLIP:
- dev->hflip = ctrl->val;
- tpg_s_hflip(&dev->tpg, dev->sensor_hflip ^ dev->hflip);
- break;
- case V4L2_CID_VFLIP:
- dev->vflip = ctrl->val;
- tpg_s_vflip(&dev->tpg, dev->sensor_vflip ^ dev->vflip);
- break;
- case V4L2_CID_ALPHA_COMPONENT:
- tpg_s_alpha_component(&dev->tpg, ctrl->val);
- break;
- }
- return 0;
-}
-
-static const struct v4l2_ctrl_ops vivid_user_vid_ctrl_ops = {
- .g_volatile_ctrl = vivid_user_vid_g_volatile_ctrl,
- .s_ctrl = vivid_user_vid_s_ctrl,
-};
-
-
-/* Video Capture Controls */
-
-static int vivid_vid_cap_s_ctrl(struct v4l2_ctrl *ctrl)
-{
- static const u32 colorspaces[] = {
- V4L2_COLORSPACE_SMPTE170M,
- V4L2_COLORSPACE_REC709,
- V4L2_COLORSPACE_SRGB,
- V4L2_COLORSPACE_OPRGB,
- V4L2_COLORSPACE_BT2020,
- V4L2_COLORSPACE_DCI_P3,
- V4L2_COLORSPACE_SMPTE240M,
- V4L2_COLORSPACE_470_SYSTEM_M,
- V4L2_COLORSPACE_470_SYSTEM_BG,
- };
- struct vivid_dev *dev = container_of(ctrl->handler, struct vivid_dev, ctrl_hdl_vid_cap);
- unsigned int i, j;
-
- switch (ctrl->id) {
- case VIVID_CID_TEST_PATTERN:
- vivid_update_quality(dev);
- tpg_s_pattern(&dev->tpg, ctrl->val);
- break;
- case VIVID_CID_COLORSPACE:
- tpg_s_colorspace(&dev->tpg, colorspaces[ctrl->val]);
- vivid_send_source_change(dev, TV);
- vivid_send_source_change(dev, SVID);
- vivid_send_source_change(dev, HDMI);
- vivid_send_source_change(dev, WEBCAM);
- break;
- case VIVID_CID_XFER_FUNC:
- tpg_s_xfer_func(&dev->tpg, ctrl->val);
- vivid_send_source_change(dev, TV);
- vivid_send_source_change(dev, SVID);
- vivid_send_source_change(dev, HDMI);
- vivid_send_source_change(dev, WEBCAM);
- break;
- case VIVID_CID_YCBCR_ENC:
- tpg_s_ycbcr_enc(&dev->tpg, ctrl->val);
- vivid_send_source_change(dev, TV);
- vivid_send_source_change(dev, SVID);
- vivid_send_source_change(dev, HDMI);
- vivid_send_source_change(dev, WEBCAM);
- break;
- case VIVID_CID_HSV_ENC:
- tpg_s_hsv_enc(&dev->tpg, ctrl->val ? V4L2_HSV_ENC_256 :
- V4L2_HSV_ENC_180);
- vivid_send_source_change(dev, TV);
- vivid_send_source_change(dev, SVID);
- vivid_send_source_change(dev, HDMI);
- vivid_send_source_change(dev, WEBCAM);
- break;
- case VIVID_CID_QUANTIZATION:
- tpg_s_quantization(&dev->tpg, ctrl->val);
- vivid_send_source_change(dev, TV);
- vivid_send_source_change(dev, SVID);
- vivid_send_source_change(dev, HDMI);
- vivid_send_source_change(dev, WEBCAM);
- break;
- case V4L2_CID_DV_RX_RGB_RANGE:
- if (!vivid_is_hdmi_cap(dev))
- break;
- tpg_s_rgb_range(&dev->tpg, ctrl->val);
- break;
- case VIVID_CID_LIMITED_RGB_RANGE:
- tpg_s_real_rgb_range(&dev->tpg, ctrl->val ?
- V4L2_DV_RGB_RANGE_LIMITED : V4L2_DV_RGB_RANGE_FULL);
- break;
- case VIVID_CID_ALPHA_MODE:
- tpg_s_alpha_mode(&dev->tpg, ctrl->val);
- break;
- case VIVID_CID_HOR_MOVEMENT:
- tpg_s_mv_hor_mode(&dev->tpg, ctrl->val);
- break;
- case VIVID_CID_VERT_MOVEMENT:
- tpg_s_mv_vert_mode(&dev->tpg, ctrl->val);
- break;
- case VIVID_CID_OSD_TEXT_MODE:
- dev->osd_mode = ctrl->val;
- break;
- case VIVID_CID_PERCENTAGE_FILL:
- tpg_s_perc_fill(&dev->tpg, ctrl->val);
- for (i = 0; i < VIDEO_MAX_FRAME; i++)
- dev->must_blank[i] = ctrl->val < 100;
- break;
- case VIVID_CID_INSERT_SAV:
- tpg_s_insert_sav(&dev->tpg, ctrl->val);
- break;
- case VIVID_CID_INSERT_EAV:
- tpg_s_insert_eav(&dev->tpg, ctrl->val);
- break;
- case VIVID_CID_HFLIP:
- dev->sensor_hflip = ctrl->val;
- tpg_s_hflip(&dev->tpg, dev->sensor_hflip ^ dev->hflip);
- break;
- case VIVID_CID_VFLIP:
- dev->sensor_vflip = ctrl->val;
- tpg_s_vflip(&dev->tpg, dev->sensor_vflip ^ dev->vflip);
- break;
- case VIVID_CID_REDUCED_FPS:
- dev->reduced_fps = ctrl->val;
- vivid_update_format_cap(dev, true);
- break;
- case VIVID_CID_HAS_CROP_CAP:
- dev->has_crop_cap = ctrl->val;
- vivid_update_format_cap(dev, true);
- break;
- case VIVID_CID_HAS_COMPOSE_CAP:
- dev->has_compose_cap = ctrl->val;
- vivid_update_format_cap(dev, true);
- break;
- case VIVID_CID_HAS_SCALER_CAP:
- dev->has_scaler_cap = ctrl->val;
- vivid_update_format_cap(dev, true);
- break;
- case VIVID_CID_SHOW_BORDER:
- tpg_s_show_border(&dev->tpg, ctrl->val);
- break;
- case VIVID_CID_SHOW_SQUARE:
- tpg_s_show_square(&dev->tpg, ctrl->val);
- break;
- case VIVID_CID_STD_ASPECT_RATIO:
- dev->std_aspect_ratio[dev->input] = ctrl->val;
- tpg_s_video_aspect(&dev->tpg, vivid_get_video_aspect(dev));
- break;
- case VIVID_CID_DV_TIMINGS_SIGNAL_MODE:
- dev->dv_timings_signal_mode[dev->input] =
- dev->ctrl_dv_timings_signal_mode->val;
- dev->query_dv_timings[dev->input] = dev->ctrl_dv_timings->val;
-
- dev->power_present = 0;
- for (i = 0, j = 0;
- i < ARRAY_SIZE(dev->dv_timings_signal_mode);
- i++)
- if (dev->input_type[i] == HDMI) {
- if (dev->dv_timings_signal_mode[i] != NO_SIGNAL)
- dev->power_present |= (1 << j);
- j++;
- }
- __v4l2_ctrl_s_ctrl(dev->ctrl_rx_power_present,
- dev->power_present);
-
- v4l2_ctrl_activate(dev->ctrl_dv_timings,
- dev->dv_timings_signal_mode[dev->input] ==
- SELECTED_DV_TIMINGS);
-
- vivid_update_quality(dev);
- vivid_send_source_change(dev, HDMI);
- break;
- case VIVID_CID_DV_TIMINGS_ASPECT_RATIO:
- dev->dv_timings_aspect_ratio[dev->input] = ctrl->val;
- tpg_s_video_aspect(&dev->tpg, vivid_get_video_aspect(dev));
- break;
- case VIVID_CID_TSTAMP_SRC:
- dev->tstamp_src_is_soe = ctrl->val;
- dev->vb_vid_cap_q.timestamp_flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
- if (dev->tstamp_src_is_soe)
- dev->vb_vid_cap_q.timestamp_flags |= V4L2_BUF_FLAG_TSTAMP_SRC_SOE;
- break;
- case VIVID_CID_MAX_EDID_BLOCKS:
- dev->edid_max_blocks = ctrl->val;
- if (dev->edid_blocks > dev->edid_max_blocks)
- dev->edid_blocks = dev->edid_max_blocks;
- break;
- }
- return 0;
-}
-
-static const struct v4l2_ctrl_ops vivid_vid_cap_ctrl_ops = {
- .s_ctrl = vivid_vid_cap_s_ctrl,
-};
-
-static const char * const vivid_ctrl_hor_movement_strings[] = {
- "Move Left Fast",
- "Move Left",
- "Move Left Slow",
- "No Movement",
- "Move Right Slow",
- "Move Right",
- "Move Right Fast",
- NULL,
-};
-
-static const struct v4l2_ctrl_config vivid_ctrl_hor_movement = {
- .ops = &vivid_vid_cap_ctrl_ops,
- .id = VIVID_CID_HOR_MOVEMENT,
- .name = "Horizontal Movement",
- .type = V4L2_CTRL_TYPE_MENU,
- .max = TPG_MOVE_POS_FAST,
- .def = TPG_MOVE_NONE,
- .qmenu = vivid_ctrl_hor_movement_strings,
-};
-
-static const char * const vivid_ctrl_vert_movement_strings[] = {
- "Move Up Fast",
- "Move Up",
- "Move Up Slow",
- "No Movement",
- "Move Down Slow",
- "Move Down",
- "Move Down Fast",
- NULL,
-};
-
-static const struct v4l2_ctrl_config vivid_ctrl_vert_movement = {
- .ops = &vivid_vid_cap_ctrl_ops,
- .id = VIVID_CID_VERT_MOVEMENT,
- .name = "Vertical Movement",
- .type = V4L2_CTRL_TYPE_MENU,
- .max = TPG_MOVE_POS_FAST,
- .def = TPG_MOVE_NONE,
- .qmenu = vivid_ctrl_vert_movement_strings,
-};
-
-static const struct v4l2_ctrl_config vivid_ctrl_show_border = {
- .ops = &vivid_vid_cap_ctrl_ops,
- .id = VIVID_CID_SHOW_BORDER,
- .name = "Show Border",
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .max = 1,
- .step = 1,
-};
-
-static const struct v4l2_ctrl_config vivid_ctrl_show_square = {
- .ops = &vivid_vid_cap_ctrl_ops,
- .id = VIVID_CID_SHOW_SQUARE,
- .name = "Show Square",
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .max = 1,
- .step = 1,
-};
-
-static const char * const vivid_ctrl_osd_mode_strings[] = {
- "All",
- "Counters Only",
- "None",
- NULL,
-};
-
-static const struct v4l2_ctrl_config vivid_ctrl_osd_mode = {
- .ops = &vivid_vid_cap_ctrl_ops,
- .id = VIVID_CID_OSD_TEXT_MODE,
- .name = "OSD Text Mode",
- .type = V4L2_CTRL_TYPE_MENU,
- .max = ARRAY_SIZE(vivid_ctrl_osd_mode_strings) - 2,
- .qmenu = vivid_ctrl_osd_mode_strings,
-};
-
-static const struct v4l2_ctrl_config vivid_ctrl_perc_fill = {
- .ops = &vivid_vid_cap_ctrl_ops,
- .id = VIVID_CID_PERCENTAGE_FILL,
- .name = "Fill Percentage of Frame",
- .type = V4L2_CTRL_TYPE_INTEGER,
- .min = 0,
- .max = 100,
- .def = 100,
- .step = 1,
-};
-
-static const struct v4l2_ctrl_config vivid_ctrl_insert_sav = {
- .ops = &vivid_vid_cap_ctrl_ops,
- .id = VIVID_CID_INSERT_SAV,
- .name = "Insert SAV Code in Image",
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .max = 1,
- .step = 1,
-};
-
-static const struct v4l2_ctrl_config vivid_ctrl_insert_eav = {
- .ops = &vivid_vid_cap_ctrl_ops,
- .id = VIVID_CID_INSERT_EAV,
- .name = "Insert EAV Code in Image",
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .max = 1,
- .step = 1,
-};
-
-static const struct v4l2_ctrl_config vivid_ctrl_hflip = {
- .ops = &vivid_vid_cap_ctrl_ops,
- .id = VIVID_CID_HFLIP,
- .name = "Sensor Flipped Horizontally",
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .max = 1,
- .step = 1,
-};
-
-static const struct v4l2_ctrl_config vivid_ctrl_vflip = {
- .ops = &vivid_vid_cap_ctrl_ops,
- .id = VIVID_CID_VFLIP,
- .name = "Sensor Flipped Vertically",
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .max = 1,
- .step = 1,
-};
-
-static const struct v4l2_ctrl_config vivid_ctrl_reduced_fps = {
- .ops = &vivid_vid_cap_ctrl_ops,
- .id = VIVID_CID_REDUCED_FPS,
- .name = "Reduced Framerate",
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .max = 1,
- .step = 1,
-};
-
-static const struct v4l2_ctrl_config vivid_ctrl_has_crop_cap = {
- .ops = &vivid_vid_cap_ctrl_ops,
- .id = VIVID_CID_HAS_CROP_CAP,
- .name = "Enable Capture Cropping",
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .max = 1,
- .def = 1,
- .step = 1,
-};
-
-static const struct v4l2_ctrl_config vivid_ctrl_has_compose_cap = {
- .ops = &vivid_vid_cap_ctrl_ops,
- .id = VIVID_CID_HAS_COMPOSE_CAP,
- .name = "Enable Capture Composing",
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .max = 1,
- .def = 1,
- .step = 1,
-};
-
-static const struct v4l2_ctrl_config vivid_ctrl_has_scaler_cap = {
- .ops = &vivid_vid_cap_ctrl_ops,
- .id = VIVID_CID_HAS_SCALER_CAP,
- .name = "Enable Capture Scaler",
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .max = 1,
- .def = 1,
- .step = 1,
-};
-
-static const char * const vivid_ctrl_tstamp_src_strings[] = {
- "End of Frame",
- "Start of Exposure",
- NULL,
-};
-
-static const struct v4l2_ctrl_config vivid_ctrl_tstamp_src = {
- .ops = &vivid_vid_cap_ctrl_ops,
- .id = VIVID_CID_TSTAMP_SRC,
- .name = "Timestamp Source",
- .type = V4L2_CTRL_TYPE_MENU,
- .max = ARRAY_SIZE(vivid_ctrl_tstamp_src_strings) - 2,
- .qmenu = vivid_ctrl_tstamp_src_strings,
-};
-
-static const struct v4l2_ctrl_config vivid_ctrl_std_aspect_ratio = {
- .ops = &vivid_vid_cap_ctrl_ops,
- .id = VIVID_CID_STD_ASPECT_RATIO,
- .name = "Standard Aspect Ratio",
- .type = V4L2_CTRL_TYPE_MENU,
- .min = 1,
- .max = 4,
- .def = 1,
- .qmenu = tpg_aspect_strings,
-};
-
-static const char * const vivid_ctrl_dv_timings_signal_mode_strings[] = {
- "Current DV Timings",
- "No Signal",
- "No Lock",
- "Out of Range",
- "Selected DV Timings",
- "Cycle Through All DV Timings",
- "Custom DV Timings",
- NULL,
-};
-
-static const struct v4l2_ctrl_config vivid_ctrl_dv_timings_signal_mode = {
- .ops = &vivid_vid_cap_ctrl_ops,
- .id = VIVID_CID_DV_TIMINGS_SIGNAL_MODE,
- .name = "DV Timings Signal Mode",
- .type = V4L2_CTRL_TYPE_MENU,
- .max = 5,
- .qmenu = vivid_ctrl_dv_timings_signal_mode_strings,
-};
-
-static const struct v4l2_ctrl_config vivid_ctrl_dv_timings_aspect_ratio = {
- .ops = &vivid_vid_cap_ctrl_ops,
- .id = VIVID_CID_DV_TIMINGS_ASPECT_RATIO,
- .name = "DV Timings Aspect Ratio",
- .type = V4L2_CTRL_TYPE_MENU,
- .max = 3,
- .qmenu = tpg_aspect_strings,
-};
-
-static const struct v4l2_ctrl_config vivid_ctrl_max_edid_blocks = {
- .ops = &vivid_vid_cap_ctrl_ops,
- .id = VIVID_CID_MAX_EDID_BLOCKS,
- .name = "Maximum EDID Blocks",
- .type = V4L2_CTRL_TYPE_INTEGER,
- .min = 1,
- .max = 256,
- .def = 2,
- .step = 1,
-};
-
-static const char * const vivid_ctrl_colorspace_strings[] = {
- "SMPTE 170M",
- "Rec. 709",
- "sRGB",
- "opRGB",
- "BT.2020",
- "DCI-P3",
- "SMPTE 240M",
- "470 System M",
- "470 System BG",
- NULL,
-};
-
-static const struct v4l2_ctrl_config vivid_ctrl_colorspace = {
- .ops = &vivid_vid_cap_ctrl_ops,
- .id = VIVID_CID_COLORSPACE,
- .name = "Colorspace",
- .type = V4L2_CTRL_TYPE_MENU,
- .max = ARRAY_SIZE(vivid_ctrl_colorspace_strings) - 2,
- .def = 2,
- .qmenu = vivid_ctrl_colorspace_strings,
-};
-
-static const char * const vivid_ctrl_xfer_func_strings[] = {
- "Default",
- "Rec. 709",
- "sRGB",
- "opRGB",
- "SMPTE 240M",
- "None",
- "DCI-P3",
- "SMPTE 2084",
- NULL,
-};
-
-static const struct v4l2_ctrl_config vivid_ctrl_xfer_func = {
- .ops = &vivid_vid_cap_ctrl_ops,
- .id = VIVID_CID_XFER_FUNC,
- .name = "Transfer Function",
- .type = V4L2_CTRL_TYPE_MENU,
- .max = ARRAY_SIZE(vivid_ctrl_xfer_func_strings) - 2,
- .qmenu = vivid_ctrl_xfer_func_strings,
-};
-
-static const char * const vivid_ctrl_ycbcr_enc_strings[] = {
- "Default",
- "ITU-R 601",
- "Rec. 709",
- "xvYCC 601",
- "xvYCC 709",
- "",
- "BT.2020",
- "BT.2020 Constant Luminance",
- "SMPTE 240M",
- NULL,
-};
-
-static const struct v4l2_ctrl_config vivid_ctrl_ycbcr_enc = {
- .ops = &vivid_vid_cap_ctrl_ops,
- .id = VIVID_CID_YCBCR_ENC,
- .name = "Y'CbCr Encoding",
- .type = V4L2_CTRL_TYPE_MENU,
- .menu_skip_mask = 1 << 5,
- .max = ARRAY_SIZE(vivid_ctrl_ycbcr_enc_strings) - 2,
- .qmenu = vivid_ctrl_ycbcr_enc_strings,
-};
-
-static const char * const vivid_ctrl_hsv_enc_strings[] = {
- "Hue 0-179",
- "Hue 0-256",
- NULL,
-};
-
-static const struct v4l2_ctrl_config vivid_ctrl_hsv_enc = {
- .ops = &vivid_vid_cap_ctrl_ops,
- .id = VIVID_CID_HSV_ENC,
- .name = "HSV Encoding",
- .type = V4L2_CTRL_TYPE_MENU,
- .max = ARRAY_SIZE(vivid_ctrl_hsv_enc_strings) - 2,
- .qmenu = vivid_ctrl_hsv_enc_strings,
-};
-
-static const char * const vivid_ctrl_quantization_strings[] = {
- "Default",
- "Full Range",
- "Limited Range",
- NULL,
-};
-
-static const struct v4l2_ctrl_config vivid_ctrl_quantization = {
- .ops = &vivid_vid_cap_ctrl_ops,
- .id = VIVID_CID_QUANTIZATION,
- .name = "Quantization",
- .type = V4L2_CTRL_TYPE_MENU,
- .max = ARRAY_SIZE(vivid_ctrl_quantization_strings) - 2,
- .qmenu = vivid_ctrl_quantization_strings,
-};
-
-static const struct v4l2_ctrl_config vivid_ctrl_alpha_mode = {
- .ops = &vivid_vid_cap_ctrl_ops,
- .id = VIVID_CID_ALPHA_MODE,
- .name = "Apply Alpha To Red Only",
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .max = 1,
- .step = 1,
-};
-
-static const struct v4l2_ctrl_config vivid_ctrl_limited_rgb_range = {
- .ops = &vivid_vid_cap_ctrl_ops,
- .id = VIVID_CID_LIMITED_RGB_RANGE,
- .name = "Limited RGB Range (16-235)",
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .max = 1,
- .step = 1,
-};
-
-
-/* Video Loop Control */
-
-static int vivid_loop_cap_s_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct vivid_dev *dev = container_of(ctrl->handler, struct vivid_dev, ctrl_hdl_loop_cap);
-
- switch (ctrl->id) {
- case VIVID_CID_LOOP_VIDEO:
- dev->loop_video = ctrl->val;
- vivid_update_quality(dev);
- vivid_send_source_change(dev, SVID);
- vivid_send_source_change(dev, HDMI);
- break;
- }
- return 0;
-}
-
-static const struct v4l2_ctrl_ops vivid_loop_cap_ctrl_ops = {
- .s_ctrl = vivid_loop_cap_s_ctrl,
-};
-
-static const struct v4l2_ctrl_config vivid_ctrl_loop_video = {
- .ops = &vivid_loop_cap_ctrl_ops,
- .id = VIVID_CID_LOOP_VIDEO,
- .name = "Loop Video",
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .max = 1,
- .step = 1,
-};
-
-
-/* VBI Capture Control */
-
-static int vivid_vbi_cap_s_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct vivid_dev *dev = container_of(ctrl->handler, struct vivid_dev, ctrl_hdl_vbi_cap);
-
- switch (ctrl->id) {
- case VIVID_CID_VBI_CAP_INTERLACED:
- dev->vbi_cap_interlaced = ctrl->val;
- break;
- }
- return 0;
-}
-
-static const struct v4l2_ctrl_ops vivid_vbi_cap_ctrl_ops = {
- .s_ctrl = vivid_vbi_cap_s_ctrl,
-};
-
-static const struct v4l2_ctrl_config vivid_ctrl_vbi_cap_interlaced = {
- .ops = &vivid_vbi_cap_ctrl_ops,
- .id = VIVID_CID_VBI_CAP_INTERLACED,
- .name = "Interlaced VBI Format",
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .max = 1,
- .step = 1,
-};
-
-
-/* Video Output Controls */
-
-static int vivid_vid_out_s_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct vivid_dev *dev = container_of(ctrl->handler, struct vivid_dev, ctrl_hdl_vid_out);
- struct v4l2_bt_timings *bt = &dev->dv_timings_out.bt;
- u32 display_present = 0;
- unsigned int i, j, bus_idx;
-
- switch (ctrl->id) {
- case VIVID_CID_HAS_CROP_OUT:
- dev->has_crop_out = ctrl->val;
- vivid_update_format_out(dev);
- break;
- case VIVID_CID_HAS_COMPOSE_OUT:
- dev->has_compose_out = ctrl->val;
- vivid_update_format_out(dev);
- break;
- case VIVID_CID_HAS_SCALER_OUT:
- dev->has_scaler_out = ctrl->val;
- vivid_update_format_out(dev);
- break;
- case V4L2_CID_DV_TX_MODE:
- dev->dvi_d_out = ctrl->val == V4L2_DV_TX_MODE_DVI_D;
- if (!vivid_is_hdmi_out(dev))
- break;
- if (!dev->dvi_d_out && (bt->flags & V4L2_DV_FL_IS_CE_VIDEO)) {
- if (bt->width == 720 && bt->height <= 576)
- dev->colorspace_out = V4L2_COLORSPACE_SMPTE170M;
- else
- dev->colorspace_out = V4L2_COLORSPACE_REC709;
- dev->quantization_out = V4L2_QUANTIZATION_DEFAULT;
- } else {
- dev->colorspace_out = V4L2_COLORSPACE_SRGB;
- dev->quantization_out = dev->dvi_d_out ?
- V4L2_QUANTIZATION_LIM_RANGE :
- V4L2_QUANTIZATION_DEFAULT;
- }
- if (dev->loop_video)
- vivid_send_source_change(dev, HDMI);
- break;
- case VIVID_CID_DISPLAY_PRESENT:
- if (dev->output_type[dev->output] != HDMI)
- break;
-
- dev->display_present[dev->output] = ctrl->val;
- for (i = 0, j = 0; i < dev->num_outputs; i++)
- if (dev->output_type[i] == HDMI)
- display_present |=
- dev->display_present[i] << j++;
-
- __v4l2_ctrl_s_ctrl(dev->ctrl_tx_rxsense, display_present);
-
- if (dev->edid_blocks) {
- __v4l2_ctrl_s_ctrl(dev->ctrl_tx_edid_present,
- display_present);
- __v4l2_ctrl_s_ctrl(dev->ctrl_tx_hotplug,
- display_present);
- }
-
- bus_idx = dev->cec_output2bus_map[dev->output];
- if (!dev->cec_tx_adap[bus_idx])
- break;
-
- if (ctrl->val && dev->edid_blocks)
- cec_s_phys_addr(dev->cec_tx_adap[bus_idx],
- dev->cec_tx_adap[bus_idx]->phys_addr,
- false);
- else
- cec_phys_addr_invalidate(dev->cec_tx_adap[bus_idx]);
-
- break;
- }
- return 0;
-}
-
-static const struct v4l2_ctrl_ops vivid_vid_out_ctrl_ops = {
- .s_ctrl = vivid_vid_out_s_ctrl,
-};
-
-static const struct v4l2_ctrl_config vivid_ctrl_has_crop_out = {
- .ops = &vivid_vid_out_ctrl_ops,
- .id = VIVID_CID_HAS_CROP_OUT,
- .name = "Enable Output Cropping",
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .max = 1,
- .def = 1,
- .step = 1,
-};
-
-static const struct v4l2_ctrl_config vivid_ctrl_has_compose_out = {
- .ops = &vivid_vid_out_ctrl_ops,
- .id = VIVID_CID_HAS_COMPOSE_OUT,
- .name = "Enable Output Composing",
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .max = 1,
- .def = 1,
- .step = 1,
-};
-
-static const struct v4l2_ctrl_config vivid_ctrl_has_scaler_out = {
- .ops = &vivid_vid_out_ctrl_ops,
- .id = VIVID_CID_HAS_SCALER_OUT,
- .name = "Enable Output Scaler",
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .max = 1,
- .def = 1,
- .step = 1,
-};
-
-static const struct v4l2_ctrl_config vivid_ctrl_display_present = {
- .ops = &vivid_vid_out_ctrl_ops,
- .id = VIVID_CID_DISPLAY_PRESENT,
- .name = "Display Present",
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .max = 1,
- .def = 1,
- .step = 1,
-};
-
-/* Streaming Controls */
-
-static int vivid_streaming_s_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct vivid_dev *dev = container_of(ctrl->handler, struct vivid_dev, ctrl_hdl_streaming);
- u64 rem;
-
- switch (ctrl->id) {
- case VIVID_CID_DQBUF_ERROR:
- dev->dqbuf_error = true;
- break;
- case VIVID_CID_PERC_DROPPED:
- dev->perc_dropped_buffers = ctrl->val;
- break;
- case VIVID_CID_QUEUE_SETUP_ERROR:
- dev->queue_setup_error = true;
- break;
- case VIVID_CID_BUF_PREPARE_ERROR:
- dev->buf_prepare_error = true;
- break;
- case VIVID_CID_START_STR_ERROR:
- dev->start_streaming_error = true;
- break;
- case VIVID_CID_REQ_VALIDATE_ERROR:
- dev->req_validate_error = true;
- break;
- case VIVID_CID_QUEUE_ERROR:
- if (vb2_start_streaming_called(&dev->vb_vid_cap_q))
- vb2_queue_error(&dev->vb_vid_cap_q);
- if (vb2_start_streaming_called(&dev->vb_vbi_cap_q))
- vb2_queue_error(&dev->vb_vbi_cap_q);
- if (vb2_start_streaming_called(&dev->vb_vid_out_q))
- vb2_queue_error(&dev->vb_vid_out_q);
- if (vb2_start_streaming_called(&dev->vb_vbi_out_q))
- vb2_queue_error(&dev->vb_vbi_out_q);
- if (vb2_start_streaming_called(&dev->vb_sdr_cap_q))
- vb2_queue_error(&dev->vb_sdr_cap_q);
- break;
- case VIVID_CID_SEQ_WRAP:
- dev->seq_wrap = ctrl->val;
- break;
- case VIVID_CID_TIME_WRAP:
- dev->time_wrap = ctrl->val;
- if (ctrl->val == 0) {
- dev->time_wrap_offset = 0;
- break;
- }
- /*
- * We want to set the time 16 seconds before the 32 bit tv_sec
- * value of struct timeval would wrap around. So first we
- * calculate ktime_get_ns() % ((1 << 32) * NSEC_PER_SEC), and
- * then we set the offset to ((1 << 32) - 16) * NSEC_PER_SEC).
- */
- div64_u64_rem(ktime_get_ns(),
- 0x100000000ULL * NSEC_PER_SEC, &rem);
- dev->time_wrap_offset =
- (0x100000000ULL - 16) * NSEC_PER_SEC - rem;
- break;
- }
- return 0;
-}
-
-static const struct v4l2_ctrl_ops vivid_streaming_ctrl_ops = {
- .s_ctrl = vivid_streaming_s_ctrl,
-};
-
-static const struct v4l2_ctrl_config vivid_ctrl_dqbuf_error = {
- .ops = &vivid_streaming_ctrl_ops,
- .id = VIVID_CID_DQBUF_ERROR,
- .name = "Inject V4L2_BUF_FLAG_ERROR",
- .type = V4L2_CTRL_TYPE_BUTTON,
-};
-
-static const struct v4l2_ctrl_config vivid_ctrl_perc_dropped = {
- .ops = &vivid_streaming_ctrl_ops,
- .id = VIVID_CID_PERC_DROPPED,
- .name = "Percentage of Dropped Buffers",
- .type = V4L2_CTRL_TYPE_INTEGER,
- .min = 0,
- .max = 100,
- .step = 1,
-};
-
-static const struct v4l2_ctrl_config vivid_ctrl_queue_setup_error = {
- .ops = &vivid_streaming_ctrl_ops,
- .id = VIVID_CID_QUEUE_SETUP_ERROR,
- .name = "Inject VIDIOC_REQBUFS Error",
- .type = V4L2_CTRL_TYPE_BUTTON,
-};
-
-static const struct v4l2_ctrl_config vivid_ctrl_buf_prepare_error = {
- .ops = &vivid_streaming_ctrl_ops,
- .id = VIVID_CID_BUF_PREPARE_ERROR,
- .name = "Inject VIDIOC_QBUF Error",
- .type = V4L2_CTRL_TYPE_BUTTON,
-};
-
-static const struct v4l2_ctrl_config vivid_ctrl_start_streaming_error = {
- .ops = &vivid_streaming_ctrl_ops,
- .id = VIVID_CID_START_STR_ERROR,
- .name = "Inject VIDIOC_STREAMON Error",
- .type = V4L2_CTRL_TYPE_BUTTON,
-};
-
-static const struct v4l2_ctrl_config vivid_ctrl_queue_error = {
- .ops = &vivid_streaming_ctrl_ops,
- .id = VIVID_CID_QUEUE_ERROR,
- .name = "Inject Fatal Streaming Error",
- .type = V4L2_CTRL_TYPE_BUTTON,
-};
-
-#ifdef CONFIG_MEDIA_CONTROLLER
-static const struct v4l2_ctrl_config vivid_ctrl_req_validate_error = {
- .ops = &vivid_streaming_ctrl_ops,
- .id = VIVID_CID_REQ_VALIDATE_ERROR,
- .name = "Inject req_validate() Error",
- .type = V4L2_CTRL_TYPE_BUTTON,
-};
-#endif
-
-static const struct v4l2_ctrl_config vivid_ctrl_seq_wrap = {
- .ops = &vivid_streaming_ctrl_ops,
- .id = VIVID_CID_SEQ_WRAP,
- .name = "Wrap Sequence Number",
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .max = 1,
- .step = 1,
-};
-
-static const struct v4l2_ctrl_config vivid_ctrl_time_wrap = {
- .ops = &vivid_streaming_ctrl_ops,
- .id = VIVID_CID_TIME_WRAP,
- .name = "Wrap Timestamp",
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .max = 1,
- .step = 1,
-};
-
-
-/* SDTV Capture Controls */
-
-static int vivid_sdtv_cap_s_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct vivid_dev *dev = container_of(ctrl->handler, struct vivid_dev, ctrl_hdl_sdtv_cap);
-
- switch (ctrl->id) {
- case VIVID_CID_STD_SIGNAL_MODE:
- dev->std_signal_mode[dev->input] =
- dev->ctrl_std_signal_mode->val;
- if (dev->std_signal_mode[dev->input] == SELECTED_STD)
- dev->query_std[dev->input] =
- vivid_standard[dev->ctrl_standard->val];
- v4l2_ctrl_activate(dev->ctrl_standard,
- dev->std_signal_mode[dev->input] ==
- SELECTED_STD);
- vivid_update_quality(dev);
- vivid_send_source_change(dev, TV);
- vivid_send_source_change(dev, SVID);
- break;
- }
- return 0;
-}
-
-static const struct v4l2_ctrl_ops vivid_sdtv_cap_ctrl_ops = {
- .s_ctrl = vivid_sdtv_cap_s_ctrl,
-};
-
-static const char * const vivid_ctrl_std_signal_mode_strings[] = {
- "Current Standard",
- "No Signal",
- "No Lock",
- "",
- "Selected Standard",
- "Cycle Through All Standards",
- NULL,
-};
-
-static const struct v4l2_ctrl_config vivid_ctrl_std_signal_mode = {
- .ops = &vivid_sdtv_cap_ctrl_ops,
- .id = VIVID_CID_STD_SIGNAL_MODE,
- .name = "Standard Signal Mode",
- .type = V4L2_CTRL_TYPE_MENU,
- .max = ARRAY_SIZE(vivid_ctrl_std_signal_mode_strings) - 2,
- .menu_skip_mask = 1 << 3,
- .qmenu = vivid_ctrl_std_signal_mode_strings,
-};
-
-static const struct v4l2_ctrl_config vivid_ctrl_standard = {
- .ops = &vivid_sdtv_cap_ctrl_ops,
- .id = VIVID_CID_STANDARD,
- .name = "Standard",
- .type = V4L2_CTRL_TYPE_MENU,
- .max = 14,
- .qmenu = vivid_ctrl_standard_strings,
-};
-
-
-
-/* Radio Receiver Controls */
-
-static int vivid_radio_rx_s_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct vivid_dev *dev = container_of(ctrl->handler, struct vivid_dev, ctrl_hdl_radio_rx);
-
- switch (ctrl->id) {
- case VIVID_CID_RADIO_SEEK_MODE:
- dev->radio_rx_hw_seek_mode = ctrl->val;
- break;
- case VIVID_CID_RADIO_SEEK_PROG_LIM:
- dev->radio_rx_hw_seek_prog_lim = ctrl->val;
- break;
- case VIVID_CID_RADIO_RX_RDS_RBDS:
- dev->rds_gen.use_rbds = ctrl->val;
- break;
- case VIVID_CID_RADIO_RX_RDS_BLOCKIO:
- dev->radio_rx_rds_controls = ctrl->val;
- dev->radio_rx_caps &= ~V4L2_CAP_READWRITE;
- dev->radio_rx_rds_use_alternates = false;
- if (!dev->radio_rx_rds_controls) {
- dev->radio_rx_caps |= V4L2_CAP_READWRITE;
- __v4l2_ctrl_s_ctrl(dev->radio_rx_rds_pty, 0);
- __v4l2_ctrl_s_ctrl(dev->radio_rx_rds_ta, 0);
- __v4l2_ctrl_s_ctrl(dev->radio_rx_rds_tp, 0);
- __v4l2_ctrl_s_ctrl(dev->radio_rx_rds_ms, 0);
- __v4l2_ctrl_s_ctrl_string(dev->radio_rx_rds_psname, "");
- __v4l2_ctrl_s_ctrl_string(dev->radio_rx_rds_radiotext, "");
- }
- v4l2_ctrl_activate(dev->radio_rx_rds_pty, dev->radio_rx_rds_controls);
- v4l2_ctrl_activate(dev->radio_rx_rds_psname, dev->radio_rx_rds_controls);
- v4l2_ctrl_activate(dev->radio_rx_rds_radiotext, dev->radio_rx_rds_controls);
- v4l2_ctrl_activate(dev->radio_rx_rds_ta, dev->radio_rx_rds_controls);
- v4l2_ctrl_activate(dev->radio_rx_rds_tp, dev->radio_rx_rds_controls);
- v4l2_ctrl_activate(dev->radio_rx_rds_ms, dev->radio_rx_rds_controls);
- dev->radio_rx_dev.device_caps = dev->radio_rx_caps;
- break;
- case V4L2_CID_RDS_RECEPTION:
- dev->radio_rx_rds_enabled = ctrl->val;
- break;
- }
- return 0;
-}
-
-static const struct v4l2_ctrl_ops vivid_radio_rx_ctrl_ops = {
- .s_ctrl = vivid_radio_rx_s_ctrl,
-};
-
-static const char * const vivid_ctrl_radio_rds_mode_strings[] = {
- "Block I/O",
- "Controls",
- NULL,
-};
-
-static const struct v4l2_ctrl_config vivid_ctrl_radio_rx_rds_blockio = {
- .ops = &vivid_radio_rx_ctrl_ops,
- .id = VIVID_CID_RADIO_RX_RDS_BLOCKIO,
- .name = "RDS Rx I/O Mode",
- .type = V4L2_CTRL_TYPE_MENU,
- .qmenu = vivid_ctrl_radio_rds_mode_strings,
- .max = 1,
-};
-
-static const struct v4l2_ctrl_config vivid_ctrl_radio_rx_rds_rbds = {
- .ops = &vivid_radio_rx_ctrl_ops,
- .id = VIVID_CID_RADIO_RX_RDS_RBDS,
- .name = "Generate RBDS Instead of RDS",
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .max = 1,
- .step = 1,
-};
-
-static const char * const vivid_ctrl_radio_hw_seek_mode_strings[] = {
- "Bounded",
- "Wrap Around",
- "Both",
- NULL,
-};
-
-static const struct v4l2_ctrl_config vivid_ctrl_radio_hw_seek_mode = {
- .ops = &vivid_radio_rx_ctrl_ops,
- .id = VIVID_CID_RADIO_SEEK_MODE,
- .name = "Radio HW Seek Mode",
- .type = V4L2_CTRL_TYPE_MENU,
- .max = 2,
- .qmenu = vivid_ctrl_radio_hw_seek_mode_strings,
-};
-
-static const struct v4l2_ctrl_config vivid_ctrl_radio_hw_seek_prog_lim = {
- .ops = &vivid_radio_rx_ctrl_ops,
- .id = VIVID_CID_RADIO_SEEK_PROG_LIM,
- .name = "Radio Programmable HW Seek",
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .max = 1,
- .step = 1,
-};
-
-
-/* Radio Transmitter Controls */
-
-static int vivid_radio_tx_s_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct vivid_dev *dev = container_of(ctrl->handler, struct vivid_dev, ctrl_hdl_radio_tx);
-
- switch (ctrl->id) {
- case VIVID_CID_RADIO_TX_RDS_BLOCKIO:
- dev->radio_tx_rds_controls = ctrl->val;
- dev->radio_tx_caps &= ~V4L2_CAP_READWRITE;
- if (!dev->radio_tx_rds_controls)
- dev->radio_tx_caps |= V4L2_CAP_READWRITE;
- dev->radio_tx_dev.device_caps = dev->radio_tx_caps;
- break;
- case V4L2_CID_RDS_TX_PTY:
- if (dev->radio_rx_rds_controls)
- v4l2_ctrl_s_ctrl(dev->radio_rx_rds_pty, ctrl->val);
- break;
- case V4L2_CID_RDS_TX_PS_NAME:
- if (dev->radio_rx_rds_controls)
- v4l2_ctrl_s_ctrl_string(dev->radio_rx_rds_psname, ctrl->p_new.p_char);
- break;
- case V4L2_CID_RDS_TX_RADIO_TEXT:
- if (dev->radio_rx_rds_controls)
- v4l2_ctrl_s_ctrl_string(dev->radio_rx_rds_radiotext, ctrl->p_new.p_char);
- break;
- case V4L2_CID_RDS_TX_TRAFFIC_ANNOUNCEMENT:
- if (dev->radio_rx_rds_controls)
- v4l2_ctrl_s_ctrl(dev->radio_rx_rds_ta, ctrl->val);
- break;
- case V4L2_CID_RDS_TX_TRAFFIC_PROGRAM:
- if (dev->radio_rx_rds_controls)
- v4l2_ctrl_s_ctrl(dev->radio_rx_rds_tp, ctrl->val);
- break;
- case V4L2_CID_RDS_TX_MUSIC_SPEECH:
- if (dev->radio_rx_rds_controls)
- v4l2_ctrl_s_ctrl(dev->radio_rx_rds_ms, ctrl->val);
- break;
- }
- return 0;
-}
-
-static const struct v4l2_ctrl_ops vivid_radio_tx_ctrl_ops = {
- .s_ctrl = vivid_radio_tx_s_ctrl,
-};
-
-static const struct v4l2_ctrl_config vivid_ctrl_radio_tx_rds_blockio = {
- .ops = &vivid_radio_tx_ctrl_ops,
- .id = VIVID_CID_RADIO_TX_RDS_BLOCKIO,
- .name = "RDS Tx I/O Mode",
- .type = V4L2_CTRL_TYPE_MENU,
- .qmenu = vivid_ctrl_radio_rds_mode_strings,
- .max = 1,
- .def = 1,
-};
-
-
-/* SDR Capture Controls */
-
-static int vivid_sdr_cap_s_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct vivid_dev *dev = container_of(ctrl->handler, struct vivid_dev, ctrl_hdl_sdr_cap);
-
- switch (ctrl->id) {
- case VIVID_CID_SDR_CAP_FM_DEVIATION:
- dev->sdr_fm_deviation = ctrl->val;
- break;
- }
- return 0;
-}
-
-static const struct v4l2_ctrl_ops vivid_sdr_cap_ctrl_ops = {
- .s_ctrl = vivid_sdr_cap_s_ctrl,
-};
-
-static const struct v4l2_ctrl_config vivid_ctrl_sdr_cap_fm_deviation = {
- .ops = &vivid_sdr_cap_ctrl_ops,
- .id = VIVID_CID_SDR_CAP_FM_DEVIATION,
- .name = "FM Deviation",
- .type = V4L2_CTRL_TYPE_INTEGER,
- .min = 100,
- .max = 200000,
- .def = 75000,
- .step = 1,
-};
-
-
-static const struct v4l2_ctrl_config vivid_ctrl_class = {
- .ops = &vivid_user_gen_ctrl_ops,
- .flags = V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_WRITE_ONLY,
- .id = VIVID_CID_VIVID_CLASS,
- .name = "Vivid Controls",
- .type = V4L2_CTRL_TYPE_CTRL_CLASS,
-};
-
-int vivid_create_controls(struct vivid_dev *dev, bool show_ccs_cap,
- bool show_ccs_out, bool no_error_inj,
- bool has_sdtv, bool has_hdmi)
-{
- struct v4l2_ctrl_handler *hdl_user_gen = &dev->ctrl_hdl_user_gen;
- struct v4l2_ctrl_handler *hdl_user_vid = &dev->ctrl_hdl_user_vid;
- struct v4l2_ctrl_handler *hdl_user_aud = &dev->ctrl_hdl_user_aud;
- struct v4l2_ctrl_handler *hdl_streaming = &dev->ctrl_hdl_streaming;
- struct v4l2_ctrl_handler *hdl_sdtv_cap = &dev->ctrl_hdl_sdtv_cap;
- struct v4l2_ctrl_handler *hdl_loop_cap = &dev->ctrl_hdl_loop_cap;
- struct v4l2_ctrl_handler *hdl_fb = &dev->ctrl_hdl_fb;
- struct v4l2_ctrl_handler *hdl_vid_cap = &dev->ctrl_hdl_vid_cap;
- struct v4l2_ctrl_handler *hdl_vid_out = &dev->ctrl_hdl_vid_out;
- struct v4l2_ctrl_handler *hdl_vbi_cap = &dev->ctrl_hdl_vbi_cap;
- struct v4l2_ctrl_handler *hdl_vbi_out = &dev->ctrl_hdl_vbi_out;
- struct v4l2_ctrl_handler *hdl_radio_rx = &dev->ctrl_hdl_radio_rx;
- struct v4l2_ctrl_handler *hdl_radio_tx = &dev->ctrl_hdl_radio_tx;
- struct v4l2_ctrl_handler *hdl_sdr_cap = &dev->ctrl_hdl_sdr_cap;
- struct v4l2_ctrl_config vivid_ctrl_dv_timings = {
- .ops = &vivid_vid_cap_ctrl_ops,
- .id = VIVID_CID_DV_TIMINGS,
- .name = "DV Timings",
- .type = V4L2_CTRL_TYPE_MENU,
- };
- int i;
-
- v4l2_ctrl_handler_init(hdl_user_gen, 10);
- v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_class, NULL);
- v4l2_ctrl_handler_init(hdl_user_vid, 9);
- v4l2_ctrl_new_custom(hdl_user_vid, &vivid_ctrl_class, NULL);
- v4l2_ctrl_handler_init(hdl_user_aud, 2);
- v4l2_ctrl_new_custom(hdl_user_aud, &vivid_ctrl_class, NULL);
- v4l2_ctrl_handler_init(hdl_streaming, 8);
- v4l2_ctrl_new_custom(hdl_streaming, &vivid_ctrl_class, NULL);
- v4l2_ctrl_handler_init(hdl_sdtv_cap, 2);
- v4l2_ctrl_new_custom(hdl_sdtv_cap, &vivid_ctrl_class, NULL);
- v4l2_ctrl_handler_init(hdl_loop_cap, 1);
- v4l2_ctrl_new_custom(hdl_loop_cap, &vivid_ctrl_class, NULL);
- v4l2_ctrl_handler_init(hdl_fb, 1);
- v4l2_ctrl_new_custom(hdl_fb, &vivid_ctrl_class, NULL);
- v4l2_ctrl_handler_init(hdl_vid_cap, 55);
- v4l2_ctrl_new_custom(hdl_vid_cap, &vivid_ctrl_class, NULL);
- v4l2_ctrl_handler_init(hdl_vid_out, 26);
- if (!no_error_inj || dev->has_fb || dev->num_hdmi_outputs)
- v4l2_ctrl_new_custom(hdl_vid_out, &vivid_ctrl_class, NULL);
- v4l2_ctrl_handler_init(hdl_vbi_cap, 21);
- v4l2_ctrl_new_custom(hdl_vbi_cap, &vivid_ctrl_class, NULL);
- v4l2_ctrl_handler_init(hdl_vbi_out, 19);
- if (!no_error_inj)
- v4l2_ctrl_new_custom(hdl_vbi_out, &vivid_ctrl_class, NULL);
- v4l2_ctrl_handler_init(hdl_radio_rx, 17);
- v4l2_ctrl_new_custom(hdl_radio_rx, &vivid_ctrl_class, NULL);
- v4l2_ctrl_handler_init(hdl_radio_tx, 17);
- v4l2_ctrl_new_custom(hdl_radio_tx, &vivid_ctrl_class, NULL);
- v4l2_ctrl_handler_init(hdl_sdr_cap, 19);
- v4l2_ctrl_new_custom(hdl_sdr_cap, &vivid_ctrl_class, NULL);
-
- /* User Controls */
- dev->volume = v4l2_ctrl_new_std(hdl_user_aud, NULL,
- V4L2_CID_AUDIO_VOLUME, 0, 255, 1, 200);
- dev->mute = v4l2_ctrl_new_std(hdl_user_aud, NULL,
- V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0);
- if (dev->has_vid_cap) {
- dev->brightness = v4l2_ctrl_new_std(hdl_user_vid, &vivid_user_vid_ctrl_ops,
- V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
- for (i = 0; i < MAX_INPUTS; i++)
- dev->input_brightness[i] = 128;
- dev->contrast = v4l2_ctrl_new_std(hdl_user_vid, &vivid_user_vid_ctrl_ops,
- V4L2_CID_CONTRAST, 0, 255, 1, 128);
- dev->saturation = v4l2_ctrl_new_std(hdl_user_vid, &vivid_user_vid_ctrl_ops,
- V4L2_CID_SATURATION, 0, 255, 1, 128);
- dev->hue = v4l2_ctrl_new_std(hdl_user_vid, &vivid_user_vid_ctrl_ops,
- V4L2_CID_HUE, -128, 128, 1, 0);
- v4l2_ctrl_new_std(hdl_user_vid, &vivid_user_vid_ctrl_ops,
- V4L2_CID_HFLIP, 0, 1, 1, 0);
- v4l2_ctrl_new_std(hdl_user_vid, &vivid_user_vid_ctrl_ops,
- V4L2_CID_VFLIP, 0, 1, 1, 0);
- dev->autogain = v4l2_ctrl_new_std(hdl_user_vid, &vivid_user_vid_ctrl_ops,
- V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
- dev->gain = v4l2_ctrl_new_std(hdl_user_vid, &vivid_user_vid_ctrl_ops,
- V4L2_CID_GAIN, 0, 255, 1, 100);
- dev->alpha = v4l2_ctrl_new_std(hdl_user_vid, &vivid_user_vid_ctrl_ops,
- V4L2_CID_ALPHA_COMPONENT, 0, 255, 1, 0);
- }
- dev->button = v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_button, NULL);
- dev->int32 = v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_int32, NULL);
- dev->int64 = v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_int64, NULL);
- dev->boolean = v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_boolean, NULL);
- dev->menu = v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_menu, NULL);
- dev->string = v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_string, NULL);
- dev->bitmask = v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_bitmask, NULL);
- dev->int_menu = v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_int_menu, NULL);
- v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_u32_array, NULL);
- v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_u16_matrix, NULL);
- v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_u8_4d_array, NULL);
-
- if (dev->has_vid_cap) {
- /* Image Processing Controls */
- struct v4l2_ctrl_config vivid_ctrl_test_pattern = {
- .ops = &vivid_vid_cap_ctrl_ops,
- .id = VIVID_CID_TEST_PATTERN,
- .name = "Test Pattern",
- .type = V4L2_CTRL_TYPE_MENU,
- .max = TPG_PAT_NOISE,
- .qmenu = tpg_pattern_strings,
- };
-
- dev->test_pattern = v4l2_ctrl_new_custom(hdl_vid_cap,
- &vivid_ctrl_test_pattern, NULL);
- v4l2_ctrl_new_custom(hdl_vid_cap, &vivid_ctrl_perc_fill, NULL);
- v4l2_ctrl_new_custom(hdl_vid_cap, &vivid_ctrl_hor_movement, NULL);
- v4l2_ctrl_new_custom(hdl_vid_cap, &vivid_ctrl_vert_movement, NULL);
- v4l2_ctrl_new_custom(hdl_vid_cap, &vivid_ctrl_osd_mode, NULL);
- v4l2_ctrl_new_custom(hdl_vid_cap, &vivid_ctrl_show_border, NULL);
- v4l2_ctrl_new_custom(hdl_vid_cap, &vivid_ctrl_show_square, NULL);
- v4l2_ctrl_new_custom(hdl_vid_cap, &vivid_ctrl_hflip, NULL);
- v4l2_ctrl_new_custom(hdl_vid_cap, &vivid_ctrl_vflip, NULL);
- v4l2_ctrl_new_custom(hdl_vid_cap, &vivid_ctrl_insert_sav, NULL);
- v4l2_ctrl_new_custom(hdl_vid_cap, &vivid_ctrl_insert_eav, NULL);
- v4l2_ctrl_new_custom(hdl_vid_cap, &vivid_ctrl_reduced_fps, NULL);
- if (show_ccs_cap) {
- dev->ctrl_has_crop_cap = v4l2_ctrl_new_custom(hdl_vid_cap,
- &vivid_ctrl_has_crop_cap, NULL);
- dev->ctrl_has_compose_cap = v4l2_ctrl_new_custom(hdl_vid_cap,
- &vivid_ctrl_has_compose_cap, NULL);
- dev->ctrl_has_scaler_cap = v4l2_ctrl_new_custom(hdl_vid_cap,
- &vivid_ctrl_has_scaler_cap, NULL);
- }
-
- v4l2_ctrl_new_custom(hdl_vid_cap, &vivid_ctrl_tstamp_src, NULL);
- dev->colorspace = v4l2_ctrl_new_custom(hdl_vid_cap,
- &vivid_ctrl_colorspace, NULL);
- v4l2_ctrl_new_custom(hdl_vid_cap, &vivid_ctrl_xfer_func, NULL);
- v4l2_ctrl_new_custom(hdl_vid_cap, &vivid_ctrl_ycbcr_enc, NULL);
- v4l2_ctrl_new_custom(hdl_vid_cap, &vivid_ctrl_hsv_enc, NULL);
- v4l2_ctrl_new_custom(hdl_vid_cap, &vivid_ctrl_quantization, NULL);
- v4l2_ctrl_new_custom(hdl_vid_cap, &vivid_ctrl_alpha_mode, NULL);
- }
-
- if (dev->has_vid_out && show_ccs_out) {
- dev->ctrl_has_crop_out = v4l2_ctrl_new_custom(hdl_vid_out,
- &vivid_ctrl_has_crop_out, NULL);
- dev->ctrl_has_compose_out = v4l2_ctrl_new_custom(hdl_vid_out,
- &vivid_ctrl_has_compose_out, NULL);
- dev->ctrl_has_scaler_out = v4l2_ctrl_new_custom(hdl_vid_out,
- &vivid_ctrl_has_scaler_out, NULL);
- }
-
- /*
- * Testing this driver with v4l2-compliance will trigger the error
- * injection controls, and after that nothing will work as expected.
- * So we have a module option to drop these error injecting controls
- * allowing us to run v4l2_compliance again.
- */
- if (!no_error_inj) {
- v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_disconnect, NULL);
- v4l2_ctrl_new_custom(hdl_streaming, &vivid_ctrl_dqbuf_error, NULL);
- v4l2_ctrl_new_custom(hdl_streaming, &vivid_ctrl_perc_dropped, NULL);
- v4l2_ctrl_new_custom(hdl_streaming, &vivid_ctrl_queue_setup_error, NULL);
- v4l2_ctrl_new_custom(hdl_streaming, &vivid_ctrl_buf_prepare_error, NULL);
- v4l2_ctrl_new_custom(hdl_streaming, &vivid_ctrl_start_streaming_error, NULL);
- v4l2_ctrl_new_custom(hdl_streaming, &vivid_ctrl_queue_error, NULL);
-#ifdef CONFIG_MEDIA_CONTROLLER
- v4l2_ctrl_new_custom(hdl_streaming, &vivid_ctrl_req_validate_error, NULL);
-#endif
- v4l2_ctrl_new_custom(hdl_streaming, &vivid_ctrl_seq_wrap, NULL);
- v4l2_ctrl_new_custom(hdl_streaming, &vivid_ctrl_time_wrap, NULL);
- }
-
- if (has_sdtv && (dev->has_vid_cap || dev->has_vbi_cap)) {
- if (dev->has_vid_cap)
- v4l2_ctrl_new_custom(hdl_vid_cap, &vivid_ctrl_std_aspect_ratio, NULL);
- dev->ctrl_std_signal_mode = v4l2_ctrl_new_custom(hdl_sdtv_cap,
- &vivid_ctrl_std_signal_mode, NULL);
- dev->ctrl_standard = v4l2_ctrl_new_custom(hdl_sdtv_cap,
- &vivid_ctrl_standard, NULL);
- if (dev->ctrl_std_signal_mode)
- v4l2_ctrl_cluster(2, &dev->ctrl_std_signal_mode);
- if (dev->has_raw_vbi_cap)
- v4l2_ctrl_new_custom(hdl_vbi_cap, &vivid_ctrl_vbi_cap_interlaced, NULL);
- }
-
- if (dev->num_hdmi_inputs) {
- s64 hdmi_input_mask = GENMASK(dev->num_hdmi_inputs - 1, 0);
-
- dev->ctrl_dv_timings_signal_mode = v4l2_ctrl_new_custom(hdl_vid_cap,
- &vivid_ctrl_dv_timings_signal_mode, NULL);
-
- vivid_ctrl_dv_timings.max = dev->query_dv_timings_size - 1;
- vivid_ctrl_dv_timings.qmenu =
- (const char * const *)dev->query_dv_timings_qmenu;
- dev->ctrl_dv_timings = v4l2_ctrl_new_custom(hdl_vid_cap,
- &vivid_ctrl_dv_timings, NULL);
- if (dev->ctrl_dv_timings_signal_mode)
- v4l2_ctrl_cluster(2, &dev->ctrl_dv_timings_signal_mode);
-
- v4l2_ctrl_new_custom(hdl_vid_cap, &vivid_ctrl_dv_timings_aspect_ratio, NULL);
- v4l2_ctrl_new_custom(hdl_vid_cap, &vivid_ctrl_max_edid_blocks, NULL);
- dev->real_rgb_range_cap = v4l2_ctrl_new_custom(hdl_vid_cap,
- &vivid_ctrl_limited_rgb_range, NULL);
- dev->rgb_range_cap = v4l2_ctrl_new_std_menu(hdl_vid_cap,
- &vivid_vid_cap_ctrl_ops,
- V4L2_CID_DV_RX_RGB_RANGE, V4L2_DV_RGB_RANGE_FULL,
- 0, V4L2_DV_RGB_RANGE_AUTO);
- dev->ctrl_rx_power_present = v4l2_ctrl_new_std(hdl_vid_cap,
- NULL, V4L2_CID_DV_RX_POWER_PRESENT, 0, hdmi_input_mask,
- 0, hdmi_input_mask);
-
- }
- if (dev->num_hdmi_outputs) {
- s64 hdmi_output_mask = GENMASK(dev->num_hdmi_outputs - 1, 0);
-
- /*
- * We aren't doing anything with this at the moment, but
- * HDMI outputs typically have this controls.
- */
- dev->ctrl_tx_rgb_range = v4l2_ctrl_new_std_menu(hdl_vid_out, NULL,
- V4L2_CID_DV_TX_RGB_RANGE, V4L2_DV_RGB_RANGE_FULL,
- 0, V4L2_DV_RGB_RANGE_AUTO);
- dev->ctrl_tx_mode = v4l2_ctrl_new_std_menu(hdl_vid_out, NULL,
- V4L2_CID_DV_TX_MODE, V4L2_DV_TX_MODE_HDMI,
- 0, V4L2_DV_TX_MODE_HDMI);
- dev->ctrl_display_present = v4l2_ctrl_new_custom(hdl_vid_out,
- &vivid_ctrl_display_present, NULL);
- dev->ctrl_tx_hotplug = v4l2_ctrl_new_std(hdl_vid_out,
- NULL, V4L2_CID_DV_TX_HOTPLUG, 0, hdmi_output_mask,
- 0, hdmi_output_mask);
- dev->ctrl_tx_rxsense = v4l2_ctrl_new_std(hdl_vid_out,
- NULL, V4L2_CID_DV_TX_RXSENSE, 0, hdmi_output_mask,
- 0, hdmi_output_mask);
- dev->ctrl_tx_edid_present = v4l2_ctrl_new_std(hdl_vid_out,
- NULL, V4L2_CID_DV_TX_EDID_PRESENT, 0, hdmi_output_mask,
- 0, hdmi_output_mask);
- }
- if ((dev->has_vid_cap && dev->has_vid_out) ||
- (dev->has_vbi_cap && dev->has_vbi_out))
- v4l2_ctrl_new_custom(hdl_loop_cap, &vivid_ctrl_loop_video, NULL);
-
- if (dev->has_fb)
- v4l2_ctrl_new_custom(hdl_fb, &vivid_ctrl_clear_fb, NULL);
-
- if (dev->has_radio_rx) {
- v4l2_ctrl_new_custom(hdl_radio_rx, &vivid_ctrl_radio_hw_seek_mode, NULL);
- v4l2_ctrl_new_custom(hdl_radio_rx, &vivid_ctrl_radio_hw_seek_prog_lim, NULL);
- v4l2_ctrl_new_custom(hdl_radio_rx, &vivid_ctrl_radio_rx_rds_blockio, NULL);
- v4l2_ctrl_new_custom(hdl_radio_rx, &vivid_ctrl_radio_rx_rds_rbds, NULL);
- v4l2_ctrl_new_std(hdl_radio_rx, &vivid_radio_rx_ctrl_ops,
- V4L2_CID_RDS_RECEPTION, 0, 1, 1, 1);
- dev->radio_rx_rds_pty = v4l2_ctrl_new_std(hdl_radio_rx,
- &vivid_radio_rx_ctrl_ops,
- V4L2_CID_RDS_RX_PTY, 0, 31, 1, 0);
- dev->radio_rx_rds_psname = v4l2_ctrl_new_std(hdl_radio_rx,
- &vivid_radio_rx_ctrl_ops,
- V4L2_CID_RDS_RX_PS_NAME, 0, 8, 8, 0);
- dev->radio_rx_rds_radiotext = v4l2_ctrl_new_std(hdl_radio_rx,
- &vivid_radio_rx_ctrl_ops,
- V4L2_CID_RDS_RX_RADIO_TEXT, 0, 64, 64, 0);
- dev->radio_rx_rds_ta = v4l2_ctrl_new_std(hdl_radio_rx,
- &vivid_radio_rx_ctrl_ops,
- V4L2_CID_RDS_RX_TRAFFIC_ANNOUNCEMENT, 0, 1, 1, 0);
- dev->radio_rx_rds_tp = v4l2_ctrl_new_std(hdl_radio_rx,
- &vivid_radio_rx_ctrl_ops,
- V4L2_CID_RDS_RX_TRAFFIC_PROGRAM, 0, 1, 1, 0);
- dev->radio_rx_rds_ms = v4l2_ctrl_new_std(hdl_radio_rx,
- &vivid_radio_rx_ctrl_ops,
- V4L2_CID_RDS_RX_MUSIC_SPEECH, 0, 1, 1, 1);
- }
- if (dev->has_radio_tx) {
- v4l2_ctrl_new_custom(hdl_radio_tx,
- &vivid_ctrl_radio_tx_rds_blockio, NULL);
- dev->radio_tx_rds_pi = v4l2_ctrl_new_std(hdl_radio_tx,
- &vivid_radio_tx_ctrl_ops,
- V4L2_CID_RDS_TX_PI, 0, 0xffff, 1, 0x8088);
- dev->radio_tx_rds_pty = v4l2_ctrl_new_std(hdl_radio_tx,
- &vivid_radio_tx_ctrl_ops,
- V4L2_CID_RDS_TX_PTY, 0, 31, 1, 3);
- dev->radio_tx_rds_psname = v4l2_ctrl_new_std(hdl_radio_tx,
- &vivid_radio_tx_ctrl_ops,
- V4L2_CID_RDS_TX_PS_NAME, 0, 8, 8, 0);
- if (dev->radio_tx_rds_psname)
- v4l2_ctrl_s_ctrl_string(dev->radio_tx_rds_psname, "VIVID-TX");
- dev->radio_tx_rds_radiotext = v4l2_ctrl_new_std(hdl_radio_tx,
- &vivid_radio_tx_ctrl_ops,
- V4L2_CID_RDS_TX_RADIO_TEXT, 0, 64 * 2, 64, 0);
- if (dev->radio_tx_rds_radiotext)
- v4l2_ctrl_s_ctrl_string(dev->radio_tx_rds_radiotext,
- "This is a VIVID default Radio Text template text, change at will");
- dev->radio_tx_rds_mono_stereo = v4l2_ctrl_new_std(hdl_radio_tx,
- &vivid_radio_tx_ctrl_ops,
- V4L2_CID_RDS_TX_MONO_STEREO, 0, 1, 1, 1);
- dev->radio_tx_rds_art_head = v4l2_ctrl_new_std(hdl_radio_tx,
- &vivid_radio_tx_ctrl_ops,
- V4L2_CID_RDS_TX_ARTIFICIAL_HEAD, 0, 1, 1, 0);
- dev->radio_tx_rds_compressed = v4l2_ctrl_new_std(hdl_radio_tx,
- &vivid_radio_tx_ctrl_ops,
- V4L2_CID_RDS_TX_COMPRESSED, 0, 1, 1, 0);
- dev->radio_tx_rds_dyn_pty = v4l2_ctrl_new_std(hdl_radio_tx,
- &vivid_radio_tx_ctrl_ops,
- V4L2_CID_RDS_TX_DYNAMIC_PTY, 0, 1, 1, 0);
- dev->radio_tx_rds_ta = v4l2_ctrl_new_std(hdl_radio_tx,
- &vivid_radio_tx_ctrl_ops,
- V4L2_CID_RDS_TX_TRAFFIC_ANNOUNCEMENT, 0, 1, 1, 0);
- dev->radio_tx_rds_tp = v4l2_ctrl_new_std(hdl_radio_tx,
- &vivid_radio_tx_ctrl_ops,
- V4L2_CID_RDS_TX_TRAFFIC_PROGRAM, 0, 1, 1, 1);
- dev->radio_tx_rds_ms = v4l2_ctrl_new_std(hdl_radio_tx,
- &vivid_radio_tx_ctrl_ops,
- V4L2_CID_RDS_TX_MUSIC_SPEECH, 0, 1, 1, 1);
- }
- if (dev->has_sdr_cap) {
- v4l2_ctrl_new_custom(hdl_sdr_cap,
- &vivid_ctrl_sdr_cap_fm_deviation, NULL);
- }
- if (hdl_user_gen->error)
- return hdl_user_gen->error;
- if (hdl_user_vid->error)
- return hdl_user_vid->error;
- if (hdl_user_aud->error)
- return hdl_user_aud->error;
- if (hdl_streaming->error)
- return hdl_streaming->error;
- if (hdl_sdr_cap->error)
- return hdl_sdr_cap->error;
- if (hdl_loop_cap->error)
- return hdl_loop_cap->error;
-
- if (dev->autogain)
- v4l2_ctrl_auto_cluster(2, &dev->autogain, 0, true);
-
- if (dev->has_vid_cap) {
- v4l2_ctrl_add_handler(hdl_vid_cap, hdl_user_gen, NULL, false);
- v4l2_ctrl_add_handler(hdl_vid_cap, hdl_user_vid, NULL, false);
- v4l2_ctrl_add_handler(hdl_vid_cap, hdl_user_aud, NULL, false);
- v4l2_ctrl_add_handler(hdl_vid_cap, hdl_streaming, NULL, false);
- v4l2_ctrl_add_handler(hdl_vid_cap, hdl_sdtv_cap, NULL, false);
- v4l2_ctrl_add_handler(hdl_vid_cap, hdl_loop_cap, NULL, false);
- v4l2_ctrl_add_handler(hdl_vid_cap, hdl_fb, NULL, false);
- if (hdl_vid_cap->error)
- return hdl_vid_cap->error;
- dev->vid_cap_dev.ctrl_handler = hdl_vid_cap;
- }
- if (dev->has_vid_out) {
- v4l2_ctrl_add_handler(hdl_vid_out, hdl_user_gen, NULL, false);
- v4l2_ctrl_add_handler(hdl_vid_out, hdl_user_aud, NULL, false);
- v4l2_ctrl_add_handler(hdl_vid_out, hdl_streaming, NULL, false);
- v4l2_ctrl_add_handler(hdl_vid_out, hdl_fb, NULL, false);
- if (hdl_vid_out->error)
- return hdl_vid_out->error;
- dev->vid_out_dev.ctrl_handler = hdl_vid_out;
- }
- if (dev->has_vbi_cap) {
- v4l2_ctrl_add_handler(hdl_vbi_cap, hdl_user_gen, NULL, false);
- v4l2_ctrl_add_handler(hdl_vbi_cap, hdl_streaming, NULL, false);
- v4l2_ctrl_add_handler(hdl_vbi_cap, hdl_sdtv_cap, NULL, false);
- v4l2_ctrl_add_handler(hdl_vbi_cap, hdl_loop_cap, NULL, false);
- if (hdl_vbi_cap->error)
- return hdl_vbi_cap->error;
- dev->vbi_cap_dev.ctrl_handler = hdl_vbi_cap;
- }
- if (dev->has_vbi_out) {
- v4l2_ctrl_add_handler(hdl_vbi_out, hdl_user_gen, NULL, false);
- v4l2_ctrl_add_handler(hdl_vbi_out, hdl_streaming, NULL, false);
- if (hdl_vbi_out->error)
- return hdl_vbi_out->error;
- dev->vbi_out_dev.ctrl_handler = hdl_vbi_out;
- }
- if (dev->has_radio_rx) {
- v4l2_ctrl_add_handler(hdl_radio_rx, hdl_user_gen, NULL, false);
- v4l2_ctrl_add_handler(hdl_radio_rx, hdl_user_aud, NULL, false);
- if (hdl_radio_rx->error)
- return hdl_radio_rx->error;
- dev->radio_rx_dev.ctrl_handler = hdl_radio_rx;
- }
- if (dev->has_radio_tx) {
- v4l2_ctrl_add_handler(hdl_radio_tx, hdl_user_gen, NULL, false);
- v4l2_ctrl_add_handler(hdl_radio_tx, hdl_user_aud, NULL, false);
- if (hdl_radio_tx->error)
- return hdl_radio_tx->error;
- dev->radio_tx_dev.ctrl_handler = hdl_radio_tx;
- }
- if (dev->has_sdr_cap) {
- v4l2_ctrl_add_handler(hdl_sdr_cap, hdl_user_gen, NULL, false);
- v4l2_ctrl_add_handler(hdl_sdr_cap, hdl_streaming, NULL, false);
- if (hdl_sdr_cap->error)
- return hdl_sdr_cap->error;
- dev->sdr_cap_dev.ctrl_handler = hdl_sdr_cap;
- }
- return 0;
-}
-
-void vivid_free_controls(struct vivid_dev *dev)
-{
- v4l2_ctrl_handler_free(&dev->ctrl_hdl_vid_cap);
- v4l2_ctrl_handler_free(&dev->ctrl_hdl_vid_out);
- v4l2_ctrl_handler_free(&dev->ctrl_hdl_vbi_cap);
- v4l2_ctrl_handler_free(&dev->ctrl_hdl_vbi_out);
- v4l2_ctrl_handler_free(&dev->ctrl_hdl_radio_rx);
- v4l2_ctrl_handler_free(&dev->ctrl_hdl_radio_tx);
- v4l2_ctrl_handler_free(&dev->ctrl_hdl_sdr_cap);
- v4l2_ctrl_handler_free(&dev->ctrl_hdl_user_gen);
- v4l2_ctrl_handler_free(&dev->ctrl_hdl_user_vid);
- v4l2_ctrl_handler_free(&dev->ctrl_hdl_user_aud);
- v4l2_ctrl_handler_free(&dev->ctrl_hdl_streaming);
- v4l2_ctrl_handler_free(&dev->ctrl_hdl_sdtv_cap);
- v4l2_ctrl_handler_free(&dev->ctrl_hdl_loop_cap);
- v4l2_ctrl_handler_free(&dev->ctrl_hdl_fb);
-}
diff --git a/drivers/media/platform/vivid/vivid-ctrls.h b/drivers/media/platform/vivid/vivid-ctrls.h
deleted file mode 100644
index 6fad5f5..0000000
--- a/drivers/media/platform/vivid/vivid-ctrls.h
+++ /dev/null
@@ -1,22 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-/*
- * vivid-ctrls.h - control support functions.
- *
- * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- */
-
-#ifndef _VIVID_CTRLS_H_
-#define _VIVID_CTRLS_H_
-
-enum vivid_hw_seek_modes {
- VIVID_HW_SEEK_BOUNDED,
- VIVID_HW_SEEK_WRAP,
- VIVID_HW_SEEK_BOTH,
-};
-
-int vivid_create_controls(struct vivid_dev *dev, bool show_ccs_cap,
- bool show_ccs_out, bool no_error_inj,
- bool has_sdtv, bool has_hdmi);
-void vivid_free_controls(struct vivid_dev *dev);
-
-#endif
diff --git a/drivers/media/platform/vivid/vivid-kthread-cap.c b/drivers/media/platform/vivid/vivid-kthread-cap.c
deleted file mode 100644
index 31f78d6..0000000
--- a/drivers/media/platform/vivid/vivid-kthread-cap.c
+++ /dev/null
@@ -1,965 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * vivid-kthread-cap.h - video/vbi capture thread support functions.
- *
- * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- */
-
-#include <linux/module.h>
-#include <linux/errno.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include <linux/font.h>
-#include <linux/mutex.h>
-#include <linux/videodev2.h>
-#include <linux/kthread.h>
-#include <linux/freezer.h>
-#include <linux/random.h>
-#include <linux/v4l2-dv-timings.h>
-#include <asm/div64.h>
-#include <media/videobuf2-vmalloc.h>
-#include <media/v4l2-dv-timings.h>
-#include <media/v4l2-ioctl.h>
-#include <media/v4l2-fh.h>
-#include <media/v4l2-event.h>
-#include <media/v4l2-rect.h>
-
-#include "vivid-core.h"
-#include "vivid-vid-common.h"
-#include "vivid-vid-cap.h"
-#include "vivid-vid-out.h"
-#include "vivid-radio-common.h"
-#include "vivid-radio-rx.h"
-#include "vivid-radio-tx.h"
-#include "vivid-sdr-cap.h"
-#include "vivid-vbi-cap.h"
-#include "vivid-vbi-out.h"
-#include "vivid-osd.h"
-#include "vivid-ctrls.h"
-#include "vivid-kthread-cap.h"
-
-static inline v4l2_std_id vivid_get_std_cap(const struct vivid_dev *dev)
-{
- if (vivid_is_sdtv_cap(dev))
- return dev->std_cap[dev->input];
- return 0;
-}
-
-static void copy_pix(struct vivid_dev *dev, int win_y, int win_x,
- u16 *cap, const u16 *osd)
-{
- u16 out;
- int left = dev->overlay_out_left;
- int top = dev->overlay_out_top;
- int fb_x = win_x + left;
- int fb_y = win_y + top;
- int i;
-
- out = *cap;
- *cap = *osd;
- if (dev->bitmap_out) {
- const u8 *p = dev->bitmap_out;
- unsigned stride = (dev->compose_out.width + 7) / 8;
-
- win_x -= dev->compose_out.left;
- win_y -= dev->compose_out.top;
- if (!(p[stride * win_y + win_x / 8] & (1 << (win_x & 7))))
- return;
- }
-
- for (i = 0; i < dev->clipcount_out; i++) {
- struct v4l2_rect *r = &dev->clips_out[i].c;
-
- if (fb_y >= r->top && fb_y < r->top + r->height &&
- fb_x >= r->left && fb_x < r->left + r->width)
- return;
- }
- if ((dev->fbuf_out_flags & V4L2_FBUF_FLAG_CHROMAKEY) &&
- *osd != dev->chromakey_out)
- return;
- if ((dev->fbuf_out_flags & V4L2_FBUF_FLAG_SRC_CHROMAKEY) &&
- out == dev->chromakey_out)
- return;
- if (dev->fmt_cap->alpha_mask) {
- if ((dev->fbuf_out_flags & V4L2_FBUF_FLAG_GLOBAL_ALPHA) &&
- dev->global_alpha_out)
- return;
- if ((dev->fbuf_out_flags & V4L2_FBUF_FLAG_LOCAL_ALPHA) &&
- *cap & dev->fmt_cap->alpha_mask)
- return;
- if ((dev->fbuf_out_flags & V4L2_FBUF_FLAG_LOCAL_INV_ALPHA) &&
- !(*cap & dev->fmt_cap->alpha_mask))
- return;
- }
- *cap = out;
-}
-
-static void blend_line(struct vivid_dev *dev, unsigned y_offset, unsigned x_offset,
- u8 *vcapbuf, const u8 *vosdbuf,
- unsigned width, unsigned pixsize)
-{
- unsigned x;
-
- for (x = 0; x < width; x++, vcapbuf += pixsize, vosdbuf += pixsize) {
- copy_pix(dev, y_offset, x_offset + x,
- (u16 *)vcapbuf, (const u16 *)vosdbuf);
- }
-}
-
-static void scale_line(const u8 *src, u8 *dst, unsigned srcw, unsigned dstw, unsigned twopixsize)
-{
- /* Coarse scaling with Bresenham */
- unsigned int_part;
- unsigned fract_part;
- unsigned src_x = 0;
- unsigned error = 0;
- unsigned x;
-
- /*
- * We always combine two pixels to prevent color bleed in the packed
- * yuv case.
- */
- srcw /= 2;
- dstw /= 2;
- int_part = srcw / dstw;
- fract_part = srcw % dstw;
- for (x = 0; x < dstw; x++, dst += twopixsize) {
- memcpy(dst, src + src_x * twopixsize, twopixsize);
- src_x += int_part;
- error += fract_part;
- if (error >= dstw) {
- error -= dstw;
- src_x++;
- }
- }
-}
-
-/*
- * Precalculate the rectangles needed to perform video looping:
- *
- * The nominal pipeline is that the video output buffer is cropped by
- * crop_out, scaled to compose_out, overlaid with the output overlay,
- * cropped on the capture side by crop_cap and scaled again to the video
- * capture buffer using compose_cap.
- *
- * To keep things efficient we calculate the intersection of compose_out
- * and crop_cap (since that's the only part of the video that will
- * actually end up in the capture buffer), determine which part of the
- * video output buffer that is and which part of the video capture buffer
- * so we can scale the video straight from the output buffer to the capture
- * buffer without any intermediate steps.
- *
- * If we need to deal with an output overlay, then there is no choice and
- * that intermediate step still has to be taken. For the output overlay
- * support we calculate the intersection of the framebuffer and the overlay
- * window (which may be partially or wholly outside of the framebuffer
- * itself) and the intersection of that with loop_vid_copy (i.e. the part of
- * the actual looped video that will be overlaid). The result is calculated
- * both in framebuffer coordinates (loop_fb_copy) and compose_out coordinates
- * (loop_vid_overlay). Finally calculate the part of the capture buffer that
- * will receive that overlaid video.
- */
-static void vivid_precalc_copy_rects(struct vivid_dev *dev)
-{
- /* Framebuffer rectangle */
- struct v4l2_rect r_fb = {
- 0, 0, dev->display_width, dev->display_height
- };
- /* Overlay window rectangle in framebuffer coordinates */
- struct v4l2_rect r_overlay = {
- dev->overlay_out_left, dev->overlay_out_top,
- dev->compose_out.width, dev->compose_out.height
- };
-
- v4l2_rect_intersect(&dev->loop_vid_copy, &dev->crop_cap, &dev->compose_out);
-
- dev->loop_vid_out = dev->loop_vid_copy;
- v4l2_rect_scale(&dev->loop_vid_out, &dev->compose_out, &dev->crop_out);
- dev->loop_vid_out.left += dev->crop_out.left;
- dev->loop_vid_out.top += dev->crop_out.top;
-
- dev->loop_vid_cap = dev->loop_vid_copy;
- v4l2_rect_scale(&dev->loop_vid_cap, &dev->crop_cap, &dev->compose_cap);
-
- dprintk(dev, 1,
- "loop_vid_copy: %dx%d@%dx%d loop_vid_out: %dx%d@%dx%d loop_vid_cap: %dx%d@%dx%d\n",
- dev->loop_vid_copy.width, dev->loop_vid_copy.height,
- dev->loop_vid_copy.left, dev->loop_vid_copy.top,
- dev->loop_vid_out.width, dev->loop_vid_out.height,
- dev->loop_vid_out.left, dev->loop_vid_out.top,
- dev->loop_vid_cap.width, dev->loop_vid_cap.height,
- dev->loop_vid_cap.left, dev->loop_vid_cap.top);
-
- v4l2_rect_intersect(&r_overlay, &r_fb, &r_overlay);
-
- /* shift r_overlay to the same origin as compose_out */
- r_overlay.left += dev->compose_out.left - dev->overlay_out_left;
- r_overlay.top += dev->compose_out.top - dev->overlay_out_top;
-
- v4l2_rect_intersect(&dev->loop_vid_overlay, &r_overlay, &dev->loop_vid_copy);
- dev->loop_fb_copy = dev->loop_vid_overlay;
-
- /* shift dev->loop_fb_copy back again to the fb origin */
- dev->loop_fb_copy.left -= dev->compose_out.left - dev->overlay_out_left;
- dev->loop_fb_copy.top -= dev->compose_out.top - dev->overlay_out_top;
-
- dev->loop_vid_overlay_cap = dev->loop_vid_overlay;
- v4l2_rect_scale(&dev->loop_vid_overlay_cap, &dev->crop_cap, &dev->compose_cap);
-
- dprintk(dev, 1,
- "loop_fb_copy: %dx%d@%dx%d loop_vid_overlay: %dx%d@%dx%d loop_vid_overlay_cap: %dx%d@%dx%d\n",
- dev->loop_fb_copy.width, dev->loop_fb_copy.height,
- dev->loop_fb_copy.left, dev->loop_fb_copy.top,
- dev->loop_vid_overlay.width, dev->loop_vid_overlay.height,
- dev->loop_vid_overlay.left, dev->loop_vid_overlay.top,
- dev->loop_vid_overlay_cap.width, dev->loop_vid_overlay_cap.height,
- dev->loop_vid_overlay_cap.left, dev->loop_vid_overlay_cap.top);
-}
-
-static void *plane_vaddr(struct tpg_data *tpg, struct vivid_buffer *buf,
- unsigned p, unsigned bpl[TPG_MAX_PLANES], unsigned h)
-{
- unsigned i;
- void *vbuf;
-
- if (p == 0 || tpg_g_buffers(tpg) > 1)
- return vb2_plane_vaddr(&buf->vb.vb2_buf, p);
- vbuf = vb2_plane_vaddr(&buf->vb.vb2_buf, 0);
- for (i = 0; i < p; i++)
- vbuf += bpl[i] * h / tpg->vdownsampling[i];
- return vbuf;
-}
-
-static noinline_for_stack int vivid_copy_buffer(struct vivid_dev *dev, unsigned p,
- u8 *vcapbuf, struct vivid_buffer *vid_cap_buf)
-{
- bool blank = dev->must_blank[vid_cap_buf->vb.vb2_buf.index];
- struct tpg_data *tpg = &dev->tpg;
- struct vivid_buffer *vid_out_buf = NULL;
- unsigned vdiv = dev->fmt_out->vdownsampling[p];
- unsigned twopixsize = tpg_g_twopixelsize(tpg, p);
- unsigned img_width = tpg_hdiv(tpg, p, dev->compose_cap.width);
- unsigned img_height = dev->compose_cap.height;
- unsigned stride_cap = tpg->bytesperline[p];
- unsigned stride_out = dev->bytesperline_out[p];
- unsigned stride_osd = dev->display_byte_stride;
- unsigned hmax = (img_height * tpg->perc_fill) / 100;
- u8 *voutbuf;
- u8 *vosdbuf = NULL;
- unsigned y;
- bool blend = dev->bitmap_out || dev->clipcount_out || dev->fbuf_out_flags;
- /* Coarse scaling with Bresenham */
- unsigned vid_out_int_part;
- unsigned vid_out_fract_part;
- unsigned vid_out_y = 0;
- unsigned vid_out_error = 0;
- unsigned vid_overlay_int_part = 0;
- unsigned vid_overlay_fract_part = 0;
- unsigned vid_overlay_y = 0;
- unsigned vid_overlay_error = 0;
- unsigned vid_cap_left = tpg_hdiv(tpg, p, dev->loop_vid_cap.left);
- unsigned vid_cap_right;
- bool quick;
-
- vid_out_int_part = dev->loop_vid_out.height / dev->loop_vid_cap.height;
- vid_out_fract_part = dev->loop_vid_out.height % dev->loop_vid_cap.height;
-
- if (!list_empty(&dev->vid_out_active))
- vid_out_buf = list_entry(dev->vid_out_active.next,
- struct vivid_buffer, list);
- if (vid_out_buf == NULL)
- return -ENODATA;
-
- vid_cap_buf->vb.field = vid_out_buf->vb.field;
-
- voutbuf = plane_vaddr(tpg, vid_out_buf, p,
- dev->bytesperline_out, dev->fmt_out_rect.height);
- if (p < dev->fmt_out->buffers)
- voutbuf += vid_out_buf->vb.vb2_buf.planes[p].data_offset;
- voutbuf += tpg_hdiv(tpg, p, dev->loop_vid_out.left) +
- (dev->loop_vid_out.top / vdiv) * stride_out;
- vcapbuf += tpg_hdiv(tpg, p, dev->compose_cap.left) +
- (dev->compose_cap.top / vdiv) * stride_cap;
-
- if (dev->loop_vid_copy.width == 0 || dev->loop_vid_copy.height == 0) {
- /*
- * If there is nothing to copy, then just fill the capture window
- * with black.
- */
- for (y = 0; y < hmax / vdiv; y++, vcapbuf += stride_cap)
- memcpy(vcapbuf, tpg->black_line[p], img_width);
- return 0;
- }
-
- if (dev->overlay_out_enabled &&
- dev->loop_vid_overlay.width && dev->loop_vid_overlay.height) {
- vosdbuf = dev->video_vbase;
- vosdbuf += (dev->loop_fb_copy.left * twopixsize) / 2 +
- dev->loop_fb_copy.top * stride_osd;
- vid_overlay_int_part = dev->loop_vid_overlay.height /
- dev->loop_vid_overlay_cap.height;
- vid_overlay_fract_part = dev->loop_vid_overlay.height %
- dev->loop_vid_overlay_cap.height;
- }
-
- vid_cap_right = tpg_hdiv(tpg, p, dev->loop_vid_cap.left + dev->loop_vid_cap.width);
- /* quick is true if no video scaling is needed */
- quick = dev->loop_vid_out.width == dev->loop_vid_cap.width;
-
- dev->cur_scaled_line = dev->loop_vid_out.height;
- for (y = 0; y < hmax; y += vdiv, vcapbuf += stride_cap) {
- /* osdline is true if this line requires overlay blending */
- bool osdline = vosdbuf && y >= dev->loop_vid_overlay_cap.top &&
- y < dev->loop_vid_overlay_cap.top + dev->loop_vid_overlay_cap.height;
-
- /*
- * If this line of the capture buffer doesn't get any video, then
- * just fill with black.
- */
- if (y < dev->loop_vid_cap.top ||
- y >= dev->loop_vid_cap.top + dev->loop_vid_cap.height) {
- memcpy(vcapbuf, tpg->black_line[p], img_width);
- continue;
- }
-
- /* fill the left border with black */
- if (dev->loop_vid_cap.left)
- memcpy(vcapbuf, tpg->black_line[p], vid_cap_left);
-
- /* fill the right border with black */
- if (vid_cap_right < img_width)
- memcpy(vcapbuf + vid_cap_right, tpg->black_line[p],
- img_width - vid_cap_right);
-
- if (quick && !osdline) {
- memcpy(vcapbuf + vid_cap_left,
- voutbuf + vid_out_y * stride_out,
- tpg_hdiv(tpg, p, dev->loop_vid_cap.width));
- goto update_vid_out_y;
- }
- if (dev->cur_scaled_line == vid_out_y) {
- memcpy(vcapbuf + vid_cap_left, dev->scaled_line,
- tpg_hdiv(tpg, p, dev->loop_vid_cap.width));
- goto update_vid_out_y;
- }
- if (!osdline) {
- scale_line(voutbuf + vid_out_y * stride_out, dev->scaled_line,
- tpg_hdiv(tpg, p, dev->loop_vid_out.width),
- tpg_hdiv(tpg, p, dev->loop_vid_cap.width),
- tpg_g_twopixelsize(tpg, p));
- } else {
- /*
- * Offset in bytes within loop_vid_copy to the start of the
- * loop_vid_overlay rectangle.
- */
- unsigned offset =
- ((dev->loop_vid_overlay.left - dev->loop_vid_copy.left) *
- twopixsize) / 2;
- u8 *osd = vosdbuf + vid_overlay_y * stride_osd;
-
- scale_line(voutbuf + vid_out_y * stride_out, dev->blended_line,
- dev->loop_vid_out.width, dev->loop_vid_copy.width,
- tpg_g_twopixelsize(tpg, p));
- if (blend)
- blend_line(dev, vid_overlay_y + dev->loop_vid_overlay.top,
- dev->loop_vid_overlay.left,
- dev->blended_line + offset, osd,
- dev->loop_vid_overlay.width, twopixsize / 2);
- else
- memcpy(dev->blended_line + offset,
- osd, (dev->loop_vid_overlay.width * twopixsize) / 2);
- scale_line(dev->blended_line, dev->scaled_line,
- dev->loop_vid_copy.width, dev->loop_vid_cap.width,
- tpg_g_twopixelsize(tpg, p));
- }
- dev->cur_scaled_line = vid_out_y;
- memcpy(vcapbuf + vid_cap_left, dev->scaled_line,
- tpg_hdiv(tpg, p, dev->loop_vid_cap.width));
-
-update_vid_out_y:
- if (osdline) {
- vid_overlay_y += vid_overlay_int_part;
- vid_overlay_error += vid_overlay_fract_part;
- if (vid_overlay_error >= dev->loop_vid_overlay_cap.height) {
- vid_overlay_error -= dev->loop_vid_overlay_cap.height;
- vid_overlay_y++;
- }
- }
- vid_out_y += vid_out_int_part;
- vid_out_error += vid_out_fract_part;
- if (vid_out_error >= dev->loop_vid_cap.height / vdiv) {
- vid_out_error -= dev->loop_vid_cap.height / vdiv;
- vid_out_y++;
- }
- }
-
- if (!blank)
- return 0;
- for (; y < img_height; y += vdiv, vcapbuf += stride_cap)
- memcpy(vcapbuf, tpg->contrast_line[p], img_width);
- return 0;
-}
-
-static void vivid_fillbuff(struct vivid_dev *dev, struct vivid_buffer *buf)
-{
- struct tpg_data *tpg = &dev->tpg;
- unsigned factor = V4L2_FIELD_HAS_T_OR_B(dev->field_cap) ? 2 : 1;
- unsigned line_height = 16 / factor;
- bool is_tv = vivid_is_sdtv_cap(dev);
- bool is_60hz = is_tv && (dev->std_cap[dev->input] & V4L2_STD_525_60);
- unsigned p;
- int line = 1;
- u8 *basep[TPG_MAX_PLANES][2];
- unsigned ms;
- char str[100];
- s32 gain;
- bool is_loop = false;
-
- if (dev->loop_video && dev->can_loop_video &&
- ((vivid_is_svid_cap(dev) &&
- !VIVID_INVALID_SIGNAL(dev->std_signal_mode[dev->input])) ||
- (vivid_is_hdmi_cap(dev) &&
- !VIVID_INVALID_SIGNAL(dev->dv_timings_signal_mode[dev->input]))))
- is_loop = true;
-
- buf->vb.sequence = dev->vid_cap_seq_count;
- if (dev->field_cap == V4L2_FIELD_ALTERNATE) {
- /*
- * 60 Hz standards start with the bottom field, 50 Hz standards
- * with the top field. So if the 0-based seq_count is even,
- * then the field is TOP for 50 Hz and BOTTOM for 60 Hz
- * standards.
- */
- buf->vb.field = ((dev->vid_cap_seq_count & 1) ^ is_60hz) ?
- V4L2_FIELD_BOTTOM : V4L2_FIELD_TOP;
- /*
- * The sequence counter counts frames, not fields. So divide
- * by two.
- */
- buf->vb.sequence /= 2;
- } else {
- buf->vb.field = dev->field_cap;
- }
- tpg_s_field(tpg, buf->vb.field,
- dev->field_cap == V4L2_FIELD_ALTERNATE);
- tpg_s_perc_fill_blank(tpg, dev->must_blank[buf->vb.vb2_buf.index]);
-
- vivid_precalc_copy_rects(dev);
-
- for (p = 0; p < tpg_g_planes(tpg); p++) {
- void *vbuf = plane_vaddr(tpg, buf, p,
- tpg->bytesperline, tpg->buf_height);
-
- /*
- * The first plane of a multiplanar format has a non-zero
- * data_offset. This helps testing whether the application
- * correctly supports non-zero data offsets.
- */
- if (p < tpg_g_buffers(tpg) && dev->fmt_cap->data_offset[p]) {
- memset(vbuf, dev->fmt_cap->data_offset[p] & 0xff,
- dev->fmt_cap->data_offset[p]);
- vbuf += dev->fmt_cap->data_offset[p];
- }
- tpg_calc_text_basep(tpg, basep, p, vbuf);
- if (!is_loop || vivid_copy_buffer(dev, p, vbuf, buf))
- tpg_fill_plane_buffer(tpg, vivid_get_std_cap(dev),
- p, vbuf);
- }
- dev->must_blank[buf->vb.vb2_buf.index] = false;
-
- /* Updates stream time, only update at the start of a new frame. */
- if (dev->field_cap != V4L2_FIELD_ALTERNATE ||
- (dev->vid_cap_seq_count & 1) == 0)
- dev->ms_vid_cap =
- jiffies_to_msecs(jiffies - dev->jiffies_vid_cap);
-
- ms = dev->ms_vid_cap;
- if (dev->osd_mode <= 1) {
- snprintf(str, sizeof(str), " %02d:%02d:%02d:%03d %u%s",
- (ms / (60 * 60 * 1000)) % 24,
- (ms / (60 * 1000)) % 60,
- (ms / 1000) % 60,
- ms % 1000,
- buf->vb.sequence,
- (dev->field_cap == V4L2_FIELD_ALTERNATE) ?
- (buf->vb.field == V4L2_FIELD_TOP ?
- " top" : " bottom") : "");
- tpg_gen_text(tpg, basep, line++ * line_height, 16, str);
- }
- if (dev->osd_mode == 0) {
- snprintf(str, sizeof(str), " %dx%d, input %d ",
- dev->src_rect.width, dev->src_rect.height, dev->input);
- tpg_gen_text(tpg, basep, line++ * line_height, 16, str);
-
- gain = v4l2_ctrl_g_ctrl(dev->gain);
- mutex_lock(dev->ctrl_hdl_user_vid.lock);
- snprintf(str, sizeof(str),
- " brightness %3d, contrast %3d, saturation %3d, hue %d ",
- dev->brightness->cur.val,
- dev->contrast->cur.val,
- dev->saturation->cur.val,
- dev->hue->cur.val);
- tpg_gen_text(tpg, basep, line++ * line_height, 16, str);
- snprintf(str, sizeof(str),
- " autogain %d, gain %3d, alpha 0x%02x ",
- dev->autogain->cur.val, gain, dev->alpha->cur.val);
- mutex_unlock(dev->ctrl_hdl_user_vid.lock);
- tpg_gen_text(tpg, basep, line++ * line_height, 16, str);
- mutex_lock(dev->ctrl_hdl_user_aud.lock);
- snprintf(str, sizeof(str),
- " volume %3d, mute %d ",
- dev->volume->cur.val, dev->mute->cur.val);
- mutex_unlock(dev->ctrl_hdl_user_aud.lock);
- tpg_gen_text(tpg, basep, line++ * line_height, 16, str);
- mutex_lock(dev->ctrl_hdl_user_gen.lock);
- snprintf(str, sizeof(str), " int32 %d, int64 %lld, bitmask %08x ",
- dev->int32->cur.val,
- *dev->int64->p_cur.p_s64,
- dev->bitmask->cur.val);
- tpg_gen_text(tpg, basep, line++ * line_height, 16, str);
- snprintf(str, sizeof(str), " boolean %d, menu %s, string \"%s\" ",
- dev->boolean->cur.val,
- dev->menu->qmenu[dev->menu->cur.val],
- dev->string->p_cur.p_char);
- tpg_gen_text(tpg, basep, line++ * line_height, 16, str);
- snprintf(str, sizeof(str), " integer_menu %lld, value %d ",
- dev->int_menu->qmenu_int[dev->int_menu->cur.val],
- dev->int_menu->cur.val);
- mutex_unlock(dev->ctrl_hdl_user_gen.lock);
- tpg_gen_text(tpg, basep, line++ * line_height, 16, str);
- if (dev->button_pressed) {
- dev->button_pressed--;
- snprintf(str, sizeof(str), " button pressed!");
- tpg_gen_text(tpg, basep, line++ * line_height, 16, str);
- }
- if (dev->osd[0]) {
- if (vivid_is_hdmi_cap(dev)) {
- snprintf(str, sizeof(str),
- " OSD \"%s\"", dev->osd);
- tpg_gen_text(tpg, basep, line++ * line_height,
- 16, str);
- }
- if (dev->osd_jiffies &&
- time_is_before_jiffies(dev->osd_jiffies + 5 * HZ)) {
- dev->osd[0] = 0;
- dev->osd_jiffies = 0;
- }
- }
- }
-}
-
-/*
- * Return true if this pixel coordinate is a valid video pixel.
- */
-static bool valid_pix(struct vivid_dev *dev, int win_y, int win_x, int fb_y, int fb_x)
-{
- int i;
-
- if (dev->bitmap_cap) {
- /*
- * Only if the corresponding bit in the bitmap is set can
- * the video pixel be shown. Coordinates are relative to
- * the overlay window set by VIDIOC_S_FMT.
- */
- const u8 *p = dev->bitmap_cap;
- unsigned stride = (dev->compose_cap.width + 7) / 8;
-
- if (!(p[stride * win_y + win_x / 8] & (1 << (win_x & 7))))
- return false;
- }
-
- for (i = 0; i < dev->clipcount_cap; i++) {
- /*
- * Only if the framebuffer coordinate is not in any of the
- * clip rectangles will be video pixel be shown.
- */
- struct v4l2_rect *r = &dev->clips_cap[i].c;
-
- if (fb_y >= r->top && fb_y < r->top + r->height &&
- fb_x >= r->left && fb_x < r->left + r->width)
- return false;
- }
- return true;
-}
-
-/*
- * Draw the image into the overlay buffer.
- * Note that the combination of overlay and multiplanar is not supported.
- */
-static void vivid_overlay(struct vivid_dev *dev, struct vivid_buffer *buf)
-{
- struct tpg_data *tpg = &dev->tpg;
- unsigned pixsize = tpg_g_twopixelsize(tpg, 0) / 2;
- void *vbase = dev->fb_vbase_cap;
- void *vbuf = vb2_plane_vaddr(&buf->vb.vb2_buf, 0);
- unsigned img_width = dev->compose_cap.width;
- unsigned img_height = dev->compose_cap.height;
- unsigned stride = tpg->bytesperline[0];
- /* if quick is true, then valid_pix() doesn't have to be called */
- bool quick = dev->bitmap_cap == NULL && dev->clipcount_cap == 0;
- int x, y, w, out_x = 0;
-
- /*
- * Overlay support is only supported for formats that have a twopixelsize
- * that's >= 2. Warn and bail out if that's not the case.
- */
- if (WARN_ON(pixsize == 0))
- return;
- if ((dev->overlay_cap_field == V4L2_FIELD_TOP ||
- dev->overlay_cap_field == V4L2_FIELD_BOTTOM) &&
- dev->overlay_cap_field != buf->vb.field)
- return;
-
- vbuf += dev->compose_cap.left * pixsize + dev->compose_cap.top * stride;
- x = dev->overlay_cap_left;
- w = img_width;
- if (x < 0) {
- out_x = -x;
- w = w - out_x;
- x = 0;
- } else {
- w = dev->fb_cap.fmt.width - x;
- if (w > img_width)
- w = img_width;
- }
- if (w <= 0)
- return;
- if (dev->overlay_cap_top >= 0)
- vbase += dev->overlay_cap_top * dev->fb_cap.fmt.bytesperline;
- for (y = dev->overlay_cap_top;
- y < dev->overlay_cap_top + (int)img_height;
- y++, vbuf += stride) {
- int px;
-
- if (y < 0 || y > dev->fb_cap.fmt.height)
- continue;
- if (quick) {
- memcpy(vbase + x * pixsize,
- vbuf + out_x * pixsize, w * pixsize);
- vbase += dev->fb_cap.fmt.bytesperline;
- continue;
- }
- for (px = 0; px < w; px++) {
- if (!valid_pix(dev, y - dev->overlay_cap_top,
- px + out_x, y, px + x))
- continue;
- memcpy(vbase + (px + x) * pixsize,
- vbuf + (px + out_x) * pixsize,
- pixsize);
- }
- vbase += dev->fb_cap.fmt.bytesperline;
- }
-}
-
-static void vivid_cap_update_frame_period(struct vivid_dev *dev)
-{
- u64 f_period;
-
- f_period = (u64)dev->timeperframe_vid_cap.numerator * 1000000000;
- if (WARN_ON(dev->timeperframe_vid_cap.denominator == 0))
- dev->timeperframe_vid_cap.denominator = 1;
- do_div(f_period, dev->timeperframe_vid_cap.denominator);
- if (dev->field_cap == V4L2_FIELD_ALTERNATE)
- f_period >>= 1;
- /*
- * If "End of Frame", then offset the exposure time by 0.9
- * of the frame period.
- */
- dev->cap_frame_eof_offset = f_period * 9;
- do_div(dev->cap_frame_eof_offset, 10);
- dev->cap_frame_period = f_period;
-}
-
-static noinline_for_stack void vivid_thread_vid_cap_tick(struct vivid_dev *dev,
- int dropped_bufs)
-{
- struct vivid_buffer *vid_cap_buf = NULL;
- struct vivid_buffer *vbi_cap_buf = NULL;
- u64 f_time = 0;
-
- dprintk(dev, 1, "Video Capture Thread Tick\n");
-
- while (dropped_bufs-- > 1)
- tpg_update_mv_count(&dev->tpg,
- dev->field_cap == V4L2_FIELD_NONE ||
- dev->field_cap == V4L2_FIELD_ALTERNATE);
-
- /* Drop a certain percentage of buffers. */
- if (dev->perc_dropped_buffers &&
- prandom_u32_max(100) < dev->perc_dropped_buffers)
- goto update_mv;
-
- spin_lock(&dev->slock);
- if (!list_empty(&dev->vid_cap_active)) {
- vid_cap_buf = list_entry(dev->vid_cap_active.next, struct vivid_buffer, list);
- list_del(&vid_cap_buf->list);
- }
- if (!list_empty(&dev->vbi_cap_active)) {
- if (dev->field_cap != V4L2_FIELD_ALTERNATE ||
- (dev->vbi_cap_seq_count & 1)) {
- vbi_cap_buf = list_entry(dev->vbi_cap_active.next,
- struct vivid_buffer, list);
- list_del(&vbi_cap_buf->list);
- }
- }
- spin_unlock(&dev->slock);
-
- if (!vid_cap_buf && !vbi_cap_buf)
- goto update_mv;
-
- f_time = dev->cap_frame_period * dev->vid_cap_seq_count +
- dev->cap_stream_start + dev->time_wrap_offset;
- if (!dev->tstamp_src_is_soe)
- f_time += dev->cap_frame_eof_offset;
-
- if (vid_cap_buf) {
- v4l2_ctrl_request_setup(vid_cap_buf->vb.vb2_buf.req_obj.req,
- &dev->ctrl_hdl_vid_cap);
- /* Fill buffer */
- vivid_fillbuff(dev, vid_cap_buf);
- dprintk(dev, 1, "filled buffer %d\n",
- vid_cap_buf->vb.vb2_buf.index);
-
- /* Handle overlay */
- if (dev->overlay_cap_owner && dev->fb_cap.base &&
- dev->fb_cap.fmt.pixelformat == dev->fmt_cap->fourcc)
- vivid_overlay(dev, vid_cap_buf);
-
- v4l2_ctrl_request_complete(vid_cap_buf->vb.vb2_buf.req_obj.req,
- &dev->ctrl_hdl_vid_cap);
- vb2_buffer_done(&vid_cap_buf->vb.vb2_buf, dev->dqbuf_error ?
- VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE);
- dprintk(dev, 2, "vid_cap buffer %d done\n",
- vid_cap_buf->vb.vb2_buf.index);
-
- vid_cap_buf->vb.vb2_buf.timestamp = f_time;
- }
-
- if (vbi_cap_buf) {
- u64 vbi_period;
-
- v4l2_ctrl_request_setup(vbi_cap_buf->vb.vb2_buf.req_obj.req,
- &dev->ctrl_hdl_vbi_cap);
- if (dev->stream_sliced_vbi_cap)
- vivid_sliced_vbi_cap_process(dev, vbi_cap_buf);
- else
- vivid_raw_vbi_cap_process(dev, vbi_cap_buf);
- v4l2_ctrl_request_complete(vbi_cap_buf->vb.vb2_buf.req_obj.req,
- &dev->ctrl_hdl_vbi_cap);
- vb2_buffer_done(&vbi_cap_buf->vb.vb2_buf, dev->dqbuf_error ?
- VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE);
- dprintk(dev, 2, "vbi_cap %d done\n",
- vbi_cap_buf->vb.vb2_buf.index);
-
- /* If capturing a VBI, offset by 0.05 */
- vbi_period = dev->cap_frame_period * 5;
- do_div(vbi_period, 100);
- vbi_cap_buf->vb.vb2_buf.timestamp = f_time + vbi_period;
- }
- dev->dqbuf_error = false;
-
-update_mv:
- /* Update the test pattern movement counters */
- tpg_update_mv_count(&dev->tpg, dev->field_cap == V4L2_FIELD_NONE ||
- dev->field_cap == V4L2_FIELD_ALTERNATE);
-}
-
-static int vivid_thread_vid_cap(void *data)
-{
- struct vivid_dev *dev = data;
- u64 numerators_since_start;
- u64 buffers_since_start;
- u64 next_jiffies_since_start;
- unsigned long jiffies_since_start;
- unsigned long cur_jiffies;
- unsigned wait_jiffies;
- unsigned numerator;
- unsigned denominator;
- int dropped_bufs;
-
- dprintk(dev, 1, "Video Capture Thread Start\n");
-
- set_freezable();
-
- /* Resets frame counters */
- dev->cap_seq_offset = 0;
- dev->cap_seq_count = 0;
- dev->cap_seq_resync = false;
- dev->jiffies_vid_cap = jiffies;
- dev->cap_stream_start = ktime_get_ns();
- vivid_cap_update_frame_period(dev);
-
- for (;;) {
- try_to_freeze();
- if (kthread_should_stop())
- break;
-
- if (!mutex_trylock(&dev->mutex)) {
- schedule_timeout_uninterruptible(1);
- continue;
- }
-
- cur_jiffies = jiffies;
- if (dev->cap_seq_resync) {
- dev->jiffies_vid_cap = cur_jiffies;
- dev->cap_seq_offset = dev->cap_seq_count + 1;
- dev->cap_seq_count = 0;
- dev->cap_stream_start += dev->cap_frame_period *
- dev->cap_seq_offset;
- vivid_cap_update_frame_period(dev);
- dev->cap_seq_resync = false;
- }
- numerator = dev->timeperframe_vid_cap.numerator;
- denominator = dev->timeperframe_vid_cap.denominator;
-
- if (dev->field_cap == V4L2_FIELD_ALTERNATE)
- denominator *= 2;
-
- /* Calculate the number of jiffies since we started streaming */
- jiffies_since_start = cur_jiffies - dev->jiffies_vid_cap;
- /* Get the number of buffers streamed since the start */
- buffers_since_start = (u64)jiffies_since_start * denominator +
- (HZ * numerator) / 2;
- do_div(buffers_since_start, HZ * numerator);
-
- /*
- * After more than 0xf0000000 (rounded down to a multiple of
- * 'jiffies-per-day' to ease jiffies_to_msecs calculation)
- * jiffies have passed since we started streaming reset the
- * counters and keep track of the sequence offset.
- */
- if (jiffies_since_start > JIFFIES_RESYNC) {
- dev->jiffies_vid_cap = cur_jiffies;
- dev->cap_seq_offset = buffers_since_start;
- buffers_since_start = 0;
- }
- dropped_bufs = buffers_since_start + dev->cap_seq_offset - dev->cap_seq_count;
- dev->cap_seq_count = buffers_since_start + dev->cap_seq_offset;
- dev->vid_cap_seq_count = dev->cap_seq_count - dev->vid_cap_seq_start;
- dev->vbi_cap_seq_count = dev->cap_seq_count - dev->vbi_cap_seq_start;
-
- vivid_thread_vid_cap_tick(dev, dropped_bufs);
-
- /*
- * Calculate the number of 'numerators' streamed since we started,
- * including the current buffer.
- */
- numerators_since_start = ++buffers_since_start * numerator;
-
- /* And the number of jiffies since we started */
- jiffies_since_start = jiffies - dev->jiffies_vid_cap;
-
- mutex_unlock(&dev->mutex);
-
- /*
- * Calculate when that next buffer is supposed to start
- * in jiffies since we started streaming.
- */
- next_jiffies_since_start = numerators_since_start * HZ +
- denominator / 2;
- do_div(next_jiffies_since_start, denominator);
- /* If it is in the past, then just schedule asap */
- if (next_jiffies_since_start < jiffies_since_start)
- next_jiffies_since_start = jiffies_since_start;
-
- wait_jiffies = next_jiffies_since_start - jiffies_since_start;
- schedule_timeout_interruptible(wait_jiffies ? wait_jiffies : 1);
- }
- dprintk(dev, 1, "Video Capture Thread End\n");
- return 0;
-}
-
-static void vivid_grab_controls(struct vivid_dev *dev, bool grab)
-{
- v4l2_ctrl_grab(dev->ctrl_has_crop_cap, grab);
- v4l2_ctrl_grab(dev->ctrl_has_compose_cap, grab);
- v4l2_ctrl_grab(dev->ctrl_has_scaler_cap, grab);
-}
-
-int vivid_start_generating_vid_cap(struct vivid_dev *dev, bool *pstreaming)
-{
- dprintk(dev, 1, "%s\n", __func__);
-
- if (dev->kthread_vid_cap) {
- u32 seq_count = dev->cap_seq_count + dev->seq_wrap * 128;
-
- if (pstreaming == &dev->vid_cap_streaming)
- dev->vid_cap_seq_start = seq_count;
- else
- dev->vbi_cap_seq_start = seq_count;
- *pstreaming = true;
- return 0;
- }
-
- /* Resets frame counters */
- tpg_init_mv_count(&dev->tpg);
-
- dev->vid_cap_seq_start = dev->seq_wrap * 128;
- dev->vbi_cap_seq_start = dev->seq_wrap * 128;
-
- dev->kthread_vid_cap = kthread_run(vivid_thread_vid_cap, dev,
- "%s-vid-cap", dev->v4l2_dev.name);
-
- if (IS_ERR(dev->kthread_vid_cap)) {
- int err = PTR_ERR(dev->kthread_vid_cap);
-
- dev->kthread_vid_cap = NULL;
- v4l2_err(&dev->v4l2_dev, "kernel_thread() failed\n");
- return err;
- }
- *pstreaming = true;
- vivid_grab_controls(dev, true);
-
- dprintk(dev, 1, "returning from %s\n", __func__);
- return 0;
-}
-
-void vivid_stop_generating_vid_cap(struct vivid_dev *dev, bool *pstreaming)
-{
- dprintk(dev, 1, "%s\n", __func__);
-
- if (dev->kthread_vid_cap == NULL)
- return;
-
- *pstreaming = false;
- if (pstreaming == &dev->vid_cap_streaming) {
- /* Release all active buffers */
- while (!list_empty(&dev->vid_cap_active)) {
- struct vivid_buffer *buf;
-
- buf = list_entry(dev->vid_cap_active.next,
- struct vivid_buffer, list);
- list_del(&buf->list);
- v4l2_ctrl_request_complete(buf->vb.vb2_buf.req_obj.req,
- &dev->ctrl_hdl_vid_cap);
- vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
- dprintk(dev, 2, "vid_cap buffer %d done\n",
- buf->vb.vb2_buf.index);
- }
- }
-
- if (pstreaming == &dev->vbi_cap_streaming) {
- while (!list_empty(&dev->vbi_cap_active)) {
- struct vivid_buffer *buf;
-
- buf = list_entry(dev->vbi_cap_active.next,
- struct vivid_buffer, list);
- list_del(&buf->list);
- v4l2_ctrl_request_complete(buf->vb.vb2_buf.req_obj.req,
- &dev->ctrl_hdl_vbi_cap);
- vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
- dprintk(dev, 2, "vbi_cap buffer %d done\n",
- buf->vb.vb2_buf.index);
- }
- }
-
- if (dev->vid_cap_streaming || dev->vbi_cap_streaming)
- return;
-
- /* shutdown control thread */
- vivid_grab_controls(dev, false);
- kthread_stop(dev->kthread_vid_cap);
- dev->kthread_vid_cap = NULL;
-}
diff --git a/drivers/media/platform/vivid/vivid-kthread-cap.h b/drivers/media/platform/vivid/vivid-kthread-cap.h
deleted file mode 100644
index 0f43015..0000000
--- a/drivers/media/platform/vivid/vivid-kthread-cap.h
+++ /dev/null
@@ -1,14 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-/*
- * vivid-kthread-cap.h - video/vbi capture thread support functions.
- *
- * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- */
-
-#ifndef _VIVID_KTHREAD_CAP_H_
-#define _VIVID_KTHREAD_CAP_H_
-
-int vivid_start_generating_vid_cap(struct vivid_dev *dev, bool *pstreaming);
-void vivid_stop_generating_vid_cap(struct vivid_dev *dev, bool *pstreaming);
-
-#endif
diff --git a/drivers/media/platform/vivid/vivid-kthread-out.c b/drivers/media/platform/vivid/vivid-kthread-out.c
deleted file mode 100644
index 1e165a6..0000000
--- a/drivers/media/platform/vivid/vivid-kthread-out.c
+++ /dev/null
@@ -1,310 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * vivid-kthread-out.h - video/vbi output thread support functions.
- *
- * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- */
-
-#include <linux/module.h>
-#include <linux/errno.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include <linux/font.h>
-#include <linux/mutex.h>
-#include <linux/videodev2.h>
-#include <linux/kthread.h>
-#include <linux/freezer.h>
-#include <linux/random.h>
-#include <linux/v4l2-dv-timings.h>
-#include <asm/div64.h>
-#include <media/videobuf2-vmalloc.h>
-#include <media/v4l2-dv-timings.h>
-#include <media/v4l2-ioctl.h>
-#include <media/v4l2-fh.h>
-#include <media/v4l2-event.h>
-
-#include "vivid-core.h"
-#include "vivid-vid-common.h"
-#include "vivid-vid-cap.h"
-#include "vivid-vid-out.h"
-#include "vivid-radio-common.h"
-#include "vivid-radio-rx.h"
-#include "vivid-radio-tx.h"
-#include "vivid-sdr-cap.h"
-#include "vivid-vbi-cap.h"
-#include "vivid-vbi-out.h"
-#include "vivid-osd.h"
-#include "vivid-ctrls.h"
-#include "vivid-kthread-out.h"
-
-static void vivid_thread_vid_out_tick(struct vivid_dev *dev)
-{
- struct vivid_buffer *vid_out_buf = NULL;
- struct vivid_buffer *vbi_out_buf = NULL;
-
- dprintk(dev, 1, "Video Output Thread Tick\n");
-
- /* Drop a certain percentage of buffers. */
- if (dev->perc_dropped_buffers &&
- prandom_u32_max(100) < dev->perc_dropped_buffers)
- return;
-
- spin_lock(&dev->slock);
- /*
- * Only dequeue buffer if there is at least one more pending.
- * This makes video loopback possible.
- */
- if (!list_empty(&dev->vid_out_active) &&
- !list_is_singular(&dev->vid_out_active)) {
- vid_out_buf = list_entry(dev->vid_out_active.next,
- struct vivid_buffer, list);
- list_del(&vid_out_buf->list);
- }
- if (!list_empty(&dev->vbi_out_active) &&
- (dev->field_out != V4L2_FIELD_ALTERNATE ||
- (dev->vbi_out_seq_count & 1))) {
- vbi_out_buf = list_entry(dev->vbi_out_active.next,
- struct vivid_buffer, list);
- list_del(&vbi_out_buf->list);
- }
- spin_unlock(&dev->slock);
-
- if (!vid_out_buf && !vbi_out_buf)
- return;
-
- if (vid_out_buf) {
- v4l2_ctrl_request_setup(vid_out_buf->vb.vb2_buf.req_obj.req,
- &dev->ctrl_hdl_vid_out);
- v4l2_ctrl_request_complete(vid_out_buf->vb.vb2_buf.req_obj.req,
- &dev->ctrl_hdl_vid_out);
- vid_out_buf->vb.sequence = dev->vid_out_seq_count;
- if (dev->field_out == V4L2_FIELD_ALTERNATE) {
- /*
- * The sequence counter counts frames, not fields.
- * So divide by two.
- */
- vid_out_buf->vb.sequence /= 2;
- }
- vid_out_buf->vb.vb2_buf.timestamp =
- ktime_get_ns() + dev->time_wrap_offset;
- vb2_buffer_done(&vid_out_buf->vb.vb2_buf, dev->dqbuf_error ?
- VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE);
- dprintk(dev, 2, "vid_out buffer %d done\n",
- vid_out_buf->vb.vb2_buf.index);
- }
-
- if (vbi_out_buf) {
- v4l2_ctrl_request_setup(vbi_out_buf->vb.vb2_buf.req_obj.req,
- &dev->ctrl_hdl_vbi_out);
- v4l2_ctrl_request_complete(vbi_out_buf->vb.vb2_buf.req_obj.req,
- &dev->ctrl_hdl_vbi_out);
- if (dev->stream_sliced_vbi_out)
- vivid_sliced_vbi_out_process(dev, vbi_out_buf);
-
- vbi_out_buf->vb.sequence = dev->vbi_out_seq_count;
- vbi_out_buf->vb.vb2_buf.timestamp =
- ktime_get_ns() + dev->time_wrap_offset;
- vb2_buffer_done(&vbi_out_buf->vb.vb2_buf, dev->dqbuf_error ?
- VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE);
- dprintk(dev, 2, "vbi_out buffer %d done\n",
- vbi_out_buf->vb.vb2_buf.index);
- }
- dev->dqbuf_error = false;
-}
-
-static int vivid_thread_vid_out(void *data)
-{
- struct vivid_dev *dev = data;
- u64 numerators_since_start;
- u64 buffers_since_start;
- u64 next_jiffies_since_start;
- unsigned long jiffies_since_start;
- unsigned long cur_jiffies;
- unsigned wait_jiffies;
- unsigned numerator;
- unsigned denominator;
-
- dprintk(dev, 1, "Video Output Thread Start\n");
-
- set_freezable();
-
- /* Resets frame counters */
- dev->out_seq_offset = 0;
- if (dev->seq_wrap)
- dev->out_seq_count = 0xffffff80U;
- dev->jiffies_vid_out = jiffies;
- dev->vid_out_seq_start = dev->vbi_out_seq_start = 0;
- dev->out_seq_resync = false;
-
- for (;;) {
- try_to_freeze();
- if (kthread_should_stop())
- break;
-
- if (!mutex_trylock(&dev->mutex)) {
- schedule_timeout_uninterruptible(1);
- continue;
- }
-
- cur_jiffies = jiffies;
- if (dev->out_seq_resync) {
- dev->jiffies_vid_out = cur_jiffies;
- dev->out_seq_offset = dev->out_seq_count + 1;
- dev->out_seq_count = 0;
- dev->out_seq_resync = false;
- }
- numerator = dev->timeperframe_vid_out.numerator;
- denominator = dev->timeperframe_vid_out.denominator;
-
- if (dev->field_out == V4L2_FIELD_ALTERNATE)
- denominator *= 2;
-
- /* Calculate the number of jiffies since we started streaming */
- jiffies_since_start = cur_jiffies - dev->jiffies_vid_out;
- /* Get the number of buffers streamed since the start */
- buffers_since_start = (u64)jiffies_since_start * denominator +
- (HZ * numerator) / 2;
- do_div(buffers_since_start, HZ * numerator);
-
- /*
- * After more than 0xf0000000 (rounded down to a multiple of
- * 'jiffies-per-day' to ease jiffies_to_msecs calculation)
- * jiffies have passed since we started streaming reset the
- * counters and keep track of the sequence offset.
- */
- if (jiffies_since_start > JIFFIES_RESYNC) {
- dev->jiffies_vid_out = cur_jiffies;
- dev->out_seq_offset = buffers_since_start;
- buffers_since_start = 0;
- }
- dev->out_seq_count = buffers_since_start + dev->out_seq_offset;
- dev->vid_out_seq_count = dev->out_seq_count - dev->vid_out_seq_start;
- dev->vbi_out_seq_count = dev->out_seq_count - dev->vbi_out_seq_start;
-
- vivid_thread_vid_out_tick(dev);
- mutex_unlock(&dev->mutex);
-
- /*
- * Calculate the number of 'numerators' streamed since we started,
- * not including the current buffer.
- */
- numerators_since_start = buffers_since_start * numerator;
-
- /* And the number of jiffies since we started */
- jiffies_since_start = jiffies - dev->jiffies_vid_out;
-
- /* Increase by the 'numerator' of one buffer */
- numerators_since_start += numerator;
- /*
- * Calculate when that next buffer is supposed to start
- * in jiffies since we started streaming.
- */
- next_jiffies_since_start = numerators_since_start * HZ +
- denominator / 2;
- do_div(next_jiffies_since_start, denominator);
- /* If it is in the past, then just schedule asap */
- if (next_jiffies_since_start < jiffies_since_start)
- next_jiffies_since_start = jiffies_since_start;
-
- wait_jiffies = next_jiffies_since_start - jiffies_since_start;
- schedule_timeout_interruptible(wait_jiffies ? wait_jiffies : 1);
- }
- dprintk(dev, 1, "Video Output Thread End\n");
- return 0;
-}
-
-static void vivid_grab_controls(struct vivid_dev *dev, bool grab)
-{
- v4l2_ctrl_grab(dev->ctrl_has_crop_out, grab);
- v4l2_ctrl_grab(dev->ctrl_has_compose_out, grab);
- v4l2_ctrl_grab(dev->ctrl_has_scaler_out, grab);
- v4l2_ctrl_grab(dev->ctrl_tx_mode, grab);
- v4l2_ctrl_grab(dev->ctrl_tx_rgb_range, grab);
-}
-
-int vivid_start_generating_vid_out(struct vivid_dev *dev, bool *pstreaming)
-{
- dprintk(dev, 1, "%s\n", __func__);
-
- if (dev->kthread_vid_out) {
- u32 seq_count = dev->out_seq_count + dev->seq_wrap * 128;
-
- if (pstreaming == &dev->vid_out_streaming)
- dev->vid_out_seq_start = seq_count;
- else
- dev->vbi_out_seq_start = seq_count;
- *pstreaming = true;
- return 0;
- }
-
- /* Resets frame counters */
- dev->jiffies_vid_out = jiffies;
- dev->vid_out_seq_start = dev->seq_wrap * 128;
- dev->vbi_out_seq_start = dev->seq_wrap * 128;
-
- dev->kthread_vid_out = kthread_run(vivid_thread_vid_out, dev,
- "%s-vid-out", dev->v4l2_dev.name);
-
- if (IS_ERR(dev->kthread_vid_out)) {
- int err = PTR_ERR(dev->kthread_vid_out);
-
- dev->kthread_vid_out = NULL;
- v4l2_err(&dev->v4l2_dev, "kernel_thread() failed\n");
- return err;
- }
- *pstreaming = true;
- vivid_grab_controls(dev, true);
-
- dprintk(dev, 1, "returning from %s\n", __func__);
- return 0;
-}
-
-void vivid_stop_generating_vid_out(struct vivid_dev *dev, bool *pstreaming)
-{
- dprintk(dev, 1, "%s\n", __func__);
-
- if (dev->kthread_vid_out == NULL)
- return;
-
- *pstreaming = false;
- if (pstreaming == &dev->vid_out_streaming) {
- /* Release all active buffers */
- while (!list_empty(&dev->vid_out_active)) {
- struct vivid_buffer *buf;
-
- buf = list_entry(dev->vid_out_active.next,
- struct vivid_buffer, list);
- list_del(&buf->list);
- v4l2_ctrl_request_complete(buf->vb.vb2_buf.req_obj.req,
- &dev->ctrl_hdl_vid_out);
- vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
- dprintk(dev, 2, "vid_out buffer %d done\n",
- buf->vb.vb2_buf.index);
- }
- }
-
- if (pstreaming == &dev->vbi_out_streaming) {
- while (!list_empty(&dev->vbi_out_active)) {
- struct vivid_buffer *buf;
-
- buf = list_entry(dev->vbi_out_active.next,
- struct vivid_buffer, list);
- list_del(&buf->list);
- v4l2_ctrl_request_complete(buf->vb.vb2_buf.req_obj.req,
- &dev->ctrl_hdl_vbi_out);
- vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
- dprintk(dev, 2, "vbi_out buffer %d done\n",
- buf->vb.vb2_buf.index);
- }
- }
-
- if (dev->vid_out_streaming || dev->vbi_out_streaming)
- return;
-
- /* shutdown control thread */
- vivid_grab_controls(dev, false);
- kthread_stop(dev->kthread_vid_out);
- dev->kthread_vid_out = NULL;
-}
diff --git a/drivers/media/platform/vivid/vivid-kthread-out.h b/drivers/media/platform/vivid/vivid-kthread-out.h
deleted file mode 100644
index d5bcf44..0000000
--- a/drivers/media/platform/vivid/vivid-kthread-out.h
+++ /dev/null
@@ -1,14 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-/*
- * vivid-kthread-out.h - video/vbi output thread support functions.
- *
- * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- */
-
-#ifndef _VIVID_KTHREAD_OUT_H_
-#define _VIVID_KTHREAD_OUT_H_
-
-int vivid_start_generating_vid_out(struct vivid_dev *dev, bool *pstreaming);
-void vivid_stop_generating_vid_out(struct vivid_dev *dev, bool *pstreaming);
-
-#endif
diff --git a/drivers/media/platform/vivid/vivid-osd.c b/drivers/media/platform/vivid/vivid-osd.c
deleted file mode 100644
index f2e789b..0000000
--- a/drivers/media/platform/vivid/vivid-osd.c
+++ /dev/null
@@ -1,389 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * vivid-osd.c - osd support for testing overlays.
- *
- * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- */
-
-#include <linux/module.h>
-#include <linux/errno.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include <linux/font.h>
-#include <linux/mutex.h>
-#include <linux/videodev2.h>
-#include <linux/kthread.h>
-#include <linux/freezer.h>
-#include <linux/fb.h>
-#include <media/videobuf2-vmalloc.h>
-#include <media/v4l2-device.h>
-#include <media/v4l2-ioctl.h>
-#include <media/v4l2-ctrls.h>
-#include <media/v4l2-fh.h>
-#include <media/v4l2-event.h>
-#include <media/v4l2-common.h>
-
-#include "vivid-core.h"
-#include "vivid-osd.h"
-
-#define MAX_OSD_WIDTH 720
-#define MAX_OSD_HEIGHT 576
-
-/*
- * Order: white, yellow, cyan, green, magenta, red, blue, black,
- * and same again with the alpha bit set (if any)
- */
-static const u16 rgb555[16] = {
- 0x7fff, 0x7fe0, 0x03ff, 0x03e0, 0x7c1f, 0x7c00, 0x001f, 0x0000,
- 0xffff, 0xffe0, 0x83ff, 0x83e0, 0xfc1f, 0xfc00, 0x801f, 0x8000
-};
-
-static const u16 rgb565[16] = {
- 0xffff, 0xffe0, 0x07ff, 0x07e0, 0xf81f, 0xf800, 0x001f, 0x0000,
- 0xffff, 0xffe0, 0x07ff, 0x07e0, 0xf81f, 0xf800, 0x001f, 0x0000
-};
-
-void vivid_clear_fb(struct vivid_dev *dev)
-{
- void *p = dev->video_vbase;
- const u16 *rgb = rgb555;
- unsigned x, y;
-
- if (dev->fb_defined.green.length == 6)
- rgb = rgb565;
-
- for (y = 0; y < dev->display_height; y++) {
- u16 *d = p;
-
- for (x = 0; x < dev->display_width; x++)
- d[x] = rgb[(y / 16 + x / 16) % 16];
- p += dev->display_byte_stride;
- }
-}
-
-/* --------------------------------------------------------------------- */
-
-static int vivid_fb_ioctl(struct fb_info *info, unsigned cmd, unsigned long arg)
-{
- struct vivid_dev *dev = (struct vivid_dev *)info->par;
-
- switch (cmd) {
- case FBIOGET_VBLANK: {
- struct fb_vblank vblank;
-
- memset(&vblank, 0, sizeof(vblank));
- vblank.flags = FB_VBLANK_HAVE_COUNT | FB_VBLANK_HAVE_VCOUNT |
- FB_VBLANK_HAVE_VSYNC;
- vblank.count = 0;
- vblank.vcount = 0;
- vblank.hcount = 0;
- if (copy_to_user((void __user *)arg, &vblank, sizeof(vblank)))
- return -EFAULT;
- return 0;
- }
-
- default:
- dprintk(dev, 1, "Unknown ioctl %08x\n", cmd);
- return -EINVAL;
- }
- return 0;
-}
-
-/* Framebuffer device handling */
-
-static int vivid_fb_set_var(struct vivid_dev *dev, struct fb_var_screeninfo *var)
-{
- dprintk(dev, 1, "vivid_fb_set_var\n");
-
- if (var->bits_per_pixel != 16) {
- dprintk(dev, 1, "vivid_fb_set_var - Invalid bpp\n");
- return -EINVAL;
- }
- dev->display_byte_stride = var->xres * dev->bytes_per_pixel;
-
- return 0;
-}
-
-static int vivid_fb_get_fix(struct vivid_dev *dev, struct fb_fix_screeninfo *fix)
-{
- dprintk(dev, 1, "vivid_fb_get_fix\n");
- memset(fix, 0, sizeof(struct fb_fix_screeninfo));
- strscpy(fix->id, "vioverlay fb", sizeof(fix->id));
- fix->smem_start = dev->video_pbase;
- fix->smem_len = dev->video_buffer_size;
- fix->type = FB_TYPE_PACKED_PIXELS;
- fix->visual = FB_VISUAL_TRUECOLOR;
- fix->xpanstep = 1;
- fix->ypanstep = 1;
- fix->ywrapstep = 0;
- fix->line_length = dev->display_byte_stride;
- fix->accel = FB_ACCEL_NONE;
- return 0;
-}
-
-/* Check the requested display mode, returning -EINVAL if we can't
- handle it. */
-
-static int _vivid_fb_check_var(struct fb_var_screeninfo *var, struct vivid_dev *dev)
-{
- dprintk(dev, 1, "vivid_fb_check_var\n");
-
- var->bits_per_pixel = 16;
- if (var->green.length == 5) {
- var->red.offset = 10;
- var->red.length = 5;
- var->green.offset = 5;
- var->green.length = 5;
- var->blue.offset = 0;
- var->blue.length = 5;
- var->transp.offset = 15;
- var->transp.length = 1;
- } else {
- var->red.offset = 11;
- var->red.length = 5;
- var->green.offset = 5;
- var->green.length = 6;
- var->blue.offset = 0;
- var->blue.length = 5;
- var->transp.offset = 0;
- var->transp.length = 0;
- }
- var->xoffset = var->yoffset = 0;
- var->left_margin = var->upper_margin = 0;
- var->nonstd = 0;
-
- var->vmode &= ~FB_VMODE_MASK;
- var->vmode |= FB_VMODE_NONINTERLACED;
-
- /* Dummy values */
- var->hsync_len = 24;
- var->vsync_len = 2;
- var->pixclock = 84316;
- var->right_margin = 776;
- var->lower_margin = 591;
- return 0;
-}
-
-static int vivid_fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
-{
- struct vivid_dev *dev = (struct vivid_dev *) info->par;
-
- dprintk(dev, 1, "vivid_fb_check_var\n");
- return _vivid_fb_check_var(var, dev);
-}
-
-static int vivid_fb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
-{
- return 0;
-}
-
-static int vivid_fb_set_par(struct fb_info *info)
-{
- int rc = 0;
- struct vivid_dev *dev = (struct vivid_dev *) info->par;
-
- dprintk(dev, 1, "vivid_fb_set_par\n");
-
- rc = vivid_fb_set_var(dev, &info->var);
- vivid_fb_get_fix(dev, &info->fix);
- return rc;
-}
-
-static int vivid_fb_setcolreg(unsigned regno, unsigned red, unsigned green,
- unsigned blue, unsigned transp,
- struct fb_info *info)
-{
- u32 color, *palette;
-
- if (regno >= info->cmap.len)
- return -EINVAL;
-
- color = ((transp & 0xFF00) << 16) | ((red & 0xFF00) << 8) |
- (green & 0xFF00) | ((blue & 0xFF00) >> 8);
- if (regno >= 16)
- return -EINVAL;
-
- palette = info->pseudo_palette;
- if (info->var.bits_per_pixel == 16) {
- switch (info->var.green.length) {
- case 6:
- color = (red & 0xf800) |
- ((green & 0xfc00) >> 5) |
- ((blue & 0xf800) >> 11);
- break;
- case 5:
- color = ((red & 0xf800) >> 1) |
- ((green & 0xf800) >> 6) |
- ((blue & 0xf800) >> 11) |
- (transp ? 0x8000 : 0);
- break;
- }
- }
- palette[regno] = color;
- return 0;
-}
-
-/* We don't really support blanking. All this does is enable or
- disable the OSD. */
-static int vivid_fb_blank(int blank_mode, struct fb_info *info)
-{
- struct vivid_dev *dev = (struct vivid_dev *)info->par;
-
- dprintk(dev, 1, "Set blanking mode : %d\n", blank_mode);
- switch (blank_mode) {
- case FB_BLANK_UNBLANK:
- break;
- case FB_BLANK_NORMAL:
- case FB_BLANK_HSYNC_SUSPEND:
- case FB_BLANK_VSYNC_SUSPEND:
- case FB_BLANK_POWERDOWN:
- break;
- }
- return 0;
-}
-
-static struct fb_ops vivid_fb_ops = {
- .owner = THIS_MODULE,
- .fb_check_var = vivid_fb_check_var,
- .fb_set_par = vivid_fb_set_par,
- .fb_setcolreg = vivid_fb_setcolreg,
- .fb_fillrect = cfb_fillrect,
- .fb_copyarea = cfb_copyarea,
- .fb_imageblit = cfb_imageblit,
- .fb_cursor = NULL,
- .fb_ioctl = vivid_fb_ioctl,
- .fb_pan_display = vivid_fb_pan_display,
- .fb_blank = vivid_fb_blank,
-};
-
-/* Initialization */
-
-
-/* Setup our initial video mode */
-static int vivid_fb_init_vidmode(struct vivid_dev *dev)
-{
- struct v4l2_rect start_window;
-
- /* Color mode */
-
- dev->bits_per_pixel = 16;
- dev->bytes_per_pixel = dev->bits_per_pixel / 8;
-
- start_window.width = MAX_OSD_WIDTH;
- start_window.left = 0;
-
- dev->display_byte_stride = start_window.width * dev->bytes_per_pixel;
-
- /* Vertical size & position */
-
- start_window.height = MAX_OSD_HEIGHT;
- start_window.top = 0;
-
- dev->display_width = start_window.width;
- dev->display_height = start_window.height;
-
- /* Generate a valid fb_var_screeninfo */
-
- dev->fb_defined.xres = dev->display_width;
- dev->fb_defined.yres = dev->display_height;
- dev->fb_defined.xres_virtual = dev->display_width;
- dev->fb_defined.yres_virtual = dev->display_height;
- dev->fb_defined.bits_per_pixel = dev->bits_per_pixel;
- dev->fb_defined.vmode = FB_VMODE_NONINTERLACED;
- dev->fb_defined.left_margin = start_window.left + 1;
- dev->fb_defined.upper_margin = start_window.top + 1;
- dev->fb_defined.accel_flags = FB_ACCEL_NONE;
- dev->fb_defined.nonstd = 0;
- /* set default to 1:5:5:5 */
- dev->fb_defined.green.length = 5;
-
- /* We've filled in the most data, let the usual mode check
- routine fill in the rest. */
- _vivid_fb_check_var(&dev->fb_defined, dev);
-
- /* Generate valid fb_fix_screeninfo */
-
- vivid_fb_get_fix(dev, &dev->fb_fix);
-
- /* Generate valid fb_info */
-
- dev->fb_info.node = -1;
- dev->fb_info.flags = FBINFO_FLAG_DEFAULT;
- dev->fb_info.fbops = &vivid_fb_ops;
- dev->fb_info.par = dev;
- dev->fb_info.var = dev->fb_defined;
- dev->fb_info.fix = dev->fb_fix;
- dev->fb_info.screen_base = (u8 __iomem *)dev->video_vbase;
- dev->fb_info.fbops = &vivid_fb_ops;
-
- /* Supply some monitor specs. Bogus values will do for now */
- dev->fb_info.monspecs.hfmin = 8000;
- dev->fb_info.monspecs.hfmax = 70000;
- dev->fb_info.monspecs.vfmin = 10;
- dev->fb_info.monspecs.vfmax = 100;
-
- /* Allocate color map */
- if (fb_alloc_cmap(&dev->fb_info.cmap, 256, 1)) {
- pr_err("abort, unable to alloc cmap\n");
- return -ENOMEM;
- }
-
- /* Allocate the pseudo palette */
- dev->fb_info.pseudo_palette = kmalloc_array(16, sizeof(u32), GFP_KERNEL);
-
- return dev->fb_info.pseudo_palette ? 0 : -ENOMEM;
-}
-
-/* Release any memory we've grabbed */
-void vivid_fb_release_buffers(struct vivid_dev *dev)
-{
- if (dev->video_vbase == NULL)
- return;
-
- /* Release cmap */
- if (dev->fb_info.cmap.len)
- fb_dealloc_cmap(&dev->fb_info.cmap);
-
- /* Release pseudo palette */
- kfree(dev->fb_info.pseudo_palette);
- kfree(dev->video_vbase);
-}
-
-/* Initialize the specified card */
-
-int vivid_fb_init(struct vivid_dev *dev)
-{
- int ret;
-
- dev->video_buffer_size = MAX_OSD_HEIGHT * MAX_OSD_WIDTH * 2;
- dev->video_vbase = kzalloc(dev->video_buffer_size, GFP_KERNEL | GFP_DMA32);
- if (dev->video_vbase == NULL)
- return -ENOMEM;
- dev->video_pbase = virt_to_phys(dev->video_vbase);
-
- pr_info("Framebuffer at 0x%lx, mapped to 0x%p, size %dk\n",
- dev->video_pbase, dev->video_vbase,
- dev->video_buffer_size / 1024);
-
- /* Set the startup video mode information */
- ret = vivid_fb_init_vidmode(dev);
- if (ret) {
- vivid_fb_release_buffers(dev);
- return ret;
- }
-
- vivid_clear_fb(dev);
-
- /* Register the framebuffer */
- if (register_framebuffer(&dev->fb_info) < 0) {
- vivid_fb_release_buffers(dev);
- return -EINVAL;
- }
-
- /* Set the card to the requested mode */
- vivid_fb_set_par(&dev->fb_info);
- return 0;
-
-}
diff --git a/drivers/media/platform/vivid/vivid-osd.h b/drivers/media/platform/vivid/vivid-osd.h
deleted file mode 100644
index f9ac1af..0000000
--- a/drivers/media/platform/vivid/vivid-osd.h
+++ /dev/null
@@ -1,15 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-/*
- * vivid-osd.h - output overlay support functions.
- *
- * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- */
-
-#ifndef _VIVID_OSD_H_
-#define _VIVID_OSD_H_
-
-int vivid_fb_init(struct vivid_dev *dev);
-void vivid_fb_release_buffers(struct vivid_dev *dev);
-void vivid_clear_fb(struct vivid_dev *dev);
-
-#endif
diff --git a/drivers/media/platform/vivid/vivid-radio-common.c b/drivers/media/platform/vivid/vivid-radio-common.c
deleted file mode 100644
index 138c7bc..0000000
--- a/drivers/media/platform/vivid/vivid-radio-common.c
+++ /dev/null
@@ -1,177 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * vivid-radio-common.c - common radio rx/tx support functions.
- *
- * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- */
-
-#include <linux/errno.h>
-#include <linux/kernel.h>
-#include <linux/delay.h>
-#include <linux/videodev2.h>
-
-#include "vivid-core.h"
-#include "vivid-ctrls.h"
-#include "vivid-radio-common.h"
-#include "vivid-rds-gen.h"
-
-/*
- * These functions are shared between the vivid receiver and transmitter
- * since both use the same frequency bands.
- */
-
-const struct v4l2_frequency_band vivid_radio_bands[TOT_BANDS] = {
- /* Band FM */
- {
- .type = V4L2_TUNER_RADIO,
- .index = 0,
- .capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO |
- V4L2_TUNER_CAP_FREQ_BANDS,
- .rangelow = FM_FREQ_RANGE_LOW,
- .rangehigh = FM_FREQ_RANGE_HIGH,
- .modulation = V4L2_BAND_MODULATION_FM,
- },
- /* Band AM */
- {
- .type = V4L2_TUNER_RADIO,
- .index = 1,
- .capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_FREQ_BANDS,
- .rangelow = AM_FREQ_RANGE_LOW,
- .rangehigh = AM_FREQ_RANGE_HIGH,
- .modulation = V4L2_BAND_MODULATION_AM,
- },
- /* Band SW */
- {
- .type = V4L2_TUNER_RADIO,
- .index = 2,
- .capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_FREQ_BANDS,
- .rangelow = SW_FREQ_RANGE_LOW,
- .rangehigh = SW_FREQ_RANGE_HIGH,
- .modulation = V4L2_BAND_MODULATION_AM,
- },
-};
-
-/*
- * Initialize the RDS generator. If we can loop, then the RDS generator
- * is set up with the values from the RDS TX controls, otherwise it
- * will fill in standard values using one of two alternates.
- */
-void vivid_radio_rds_init(struct vivid_dev *dev)
-{
- struct vivid_rds_gen *rds = &dev->rds_gen;
- bool alt = dev->radio_rx_rds_use_alternates;
-
- /* Do nothing, blocks will be filled by the transmitter */
- if (dev->radio_rds_loop && !dev->radio_tx_rds_controls)
- return;
-
- if (dev->radio_rds_loop) {
- v4l2_ctrl_lock(dev->radio_tx_rds_pi);
- rds->picode = dev->radio_tx_rds_pi->cur.val;
- rds->pty = dev->radio_tx_rds_pty->cur.val;
- rds->mono_stereo = dev->radio_tx_rds_mono_stereo->cur.val;
- rds->art_head = dev->radio_tx_rds_art_head->cur.val;
- rds->compressed = dev->radio_tx_rds_compressed->cur.val;
- rds->dyn_pty = dev->radio_tx_rds_dyn_pty->cur.val;
- rds->ta = dev->radio_tx_rds_ta->cur.val;
- rds->tp = dev->radio_tx_rds_tp->cur.val;
- rds->ms = dev->radio_tx_rds_ms->cur.val;
- strscpy(rds->psname,
- dev->radio_tx_rds_psname->p_cur.p_char,
- sizeof(rds->psname));
- strscpy(rds->radiotext,
- dev->radio_tx_rds_radiotext->p_cur.p_char + alt * 64,
- sizeof(rds->radiotext));
- v4l2_ctrl_unlock(dev->radio_tx_rds_pi);
- } else {
- vivid_rds_gen_fill(rds, dev->radio_rx_freq, alt);
- }
- if (dev->radio_rx_rds_controls) {
- v4l2_ctrl_s_ctrl(dev->radio_rx_rds_pty, rds->pty);
- v4l2_ctrl_s_ctrl(dev->radio_rx_rds_ta, rds->ta);
- v4l2_ctrl_s_ctrl(dev->radio_rx_rds_tp, rds->tp);
- v4l2_ctrl_s_ctrl(dev->radio_rx_rds_ms, rds->ms);
- v4l2_ctrl_s_ctrl_string(dev->radio_rx_rds_psname, rds->psname);
- v4l2_ctrl_s_ctrl_string(dev->radio_rx_rds_radiotext, rds->radiotext);
- if (!dev->radio_rds_loop)
- dev->radio_rx_rds_use_alternates = !dev->radio_rx_rds_use_alternates;
- }
- vivid_rds_generate(rds);
-}
-
-/*
- * Calculate the emulated signal quality taking into account the frequency
- * the transmitter is using.
- */
-static void vivid_radio_calc_sig_qual(struct vivid_dev *dev)
-{
- int mod = 16000;
- int delta = 800;
- int sig_qual, sig_qual_tx = mod;
-
- /*
- * For SW and FM there is a channel every 1000 kHz, for AM there is one
- * every 100 kHz.
- */
- if (dev->radio_rx_freq <= AM_FREQ_RANGE_HIGH) {
- mod /= 10;
- delta /= 10;
- }
- sig_qual = (dev->radio_rx_freq + delta) % mod - delta;
- if (dev->has_radio_tx)
- sig_qual_tx = dev->radio_rx_freq - dev->radio_tx_freq;
- if (abs(sig_qual_tx) <= abs(sig_qual)) {
- sig_qual = sig_qual_tx;
- /*
- * Zero the internal rds buffer if we are going to loop
- * rds blocks.
- */
- if (!dev->radio_rds_loop && !dev->radio_tx_rds_controls)
- memset(dev->rds_gen.data, 0,
- sizeof(dev->rds_gen.data));
- dev->radio_rds_loop = dev->radio_rx_freq >= FM_FREQ_RANGE_LOW;
- } else {
- dev->radio_rds_loop = false;
- }
- if (dev->radio_rx_freq <= AM_FREQ_RANGE_HIGH)
- sig_qual *= 10;
- dev->radio_rx_sig_qual = sig_qual;
-}
-
-int vivid_radio_g_frequency(struct file *file, const unsigned *pfreq, struct v4l2_frequency *vf)
-{
- if (vf->tuner != 0)
- return -EINVAL;
- vf->frequency = *pfreq;
- return 0;
-}
-
-int vivid_radio_s_frequency(struct file *file, unsigned *pfreq, const struct v4l2_frequency *vf)
-{
- struct vivid_dev *dev = video_drvdata(file);
- unsigned freq;
- unsigned band;
-
- if (vf->tuner != 0)
- return -EINVAL;
-
- if (vf->frequency >= (FM_FREQ_RANGE_LOW + SW_FREQ_RANGE_HIGH) / 2)
- band = BAND_FM;
- else if (vf->frequency <= (AM_FREQ_RANGE_HIGH + SW_FREQ_RANGE_LOW) / 2)
- band = BAND_AM;
- else
- band = BAND_SW;
-
- freq = clamp_t(u32, vf->frequency, vivid_radio_bands[band].rangelow,
- vivid_radio_bands[band].rangehigh);
- *pfreq = freq;
-
- /*
- * For both receiver and transmitter recalculate the signal quality
- * (since that depends on both frequencies) and re-init the rds
- * generator.
- */
- vivid_radio_calc_sig_qual(dev);
- vivid_radio_rds_init(dev);
- return 0;
-}
diff --git a/drivers/media/platform/vivid/vivid-radio-common.h b/drivers/media/platform/vivid/vivid-radio-common.h
deleted file mode 100644
index 30a9900..0000000
--- a/drivers/media/platform/vivid/vivid-radio-common.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-/*
- * vivid-radio-common.h - common radio rx/tx support functions.
- *
- * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- */
-
-#ifndef _VIVID_RADIO_COMMON_H_
-#define _VIVID_RADIO_COMMON_H_
-
-/* The supported radio frequency ranges in kHz */
-#define FM_FREQ_RANGE_LOW (64000U * 16U)
-#define FM_FREQ_RANGE_HIGH (108000U * 16U)
-#define AM_FREQ_RANGE_LOW (520U * 16U)
-#define AM_FREQ_RANGE_HIGH (1710U * 16U)
-#define SW_FREQ_RANGE_LOW (2300U * 16U)
-#define SW_FREQ_RANGE_HIGH (26100U * 16U)
-
-enum { BAND_FM, BAND_AM, BAND_SW, TOT_BANDS };
-
-extern const struct v4l2_frequency_band vivid_radio_bands[TOT_BANDS];
-
-int vivid_radio_g_frequency(struct file *file, const unsigned *freq, struct v4l2_frequency *vf);
-int vivid_radio_s_frequency(struct file *file, unsigned *freq, const struct v4l2_frequency *vf);
-
-void vivid_radio_rds_init(struct vivid_dev *dev);
-
-#endif
diff --git a/drivers/media/platform/vivid/vivid-radio-rx.c b/drivers/media/platform/vivid/vivid-radio-rx.c
deleted file mode 100644
index 232cab5..0000000
--- a/drivers/media/platform/vivid/vivid-radio-rx.c
+++ /dev/null
@@ -1,278 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * vivid-radio-rx.c - radio receiver support functions.
- *
- * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- */
-
-#include <linux/errno.h>
-#include <linux/kernel.h>
-#include <linux/delay.h>
-#include <linux/videodev2.h>
-#include <linux/v4l2-dv-timings.h>
-#include <linux/sched/signal.h>
-
-#include <media/v4l2-common.h>
-#include <media/v4l2-event.h>
-#include <media/v4l2-dv-timings.h>
-
-#include "vivid-core.h"
-#include "vivid-ctrls.h"
-#include "vivid-radio-common.h"
-#include "vivid-rds-gen.h"
-#include "vivid-radio-rx.h"
-
-ssize_t vivid_radio_rx_read(struct file *file, char __user *buf,
- size_t size, loff_t *offset)
-{
- struct vivid_dev *dev = video_drvdata(file);
- struct v4l2_rds_data *data = dev->rds_gen.data;
- bool use_alternates;
- ktime_t timestamp;
- unsigned blk;
- int perc;
- int i;
-
- if (dev->radio_rx_rds_controls)
- return -EINVAL;
- if (size < sizeof(*data))
- return 0;
- size = sizeof(*data) * (size / sizeof(*data));
-
- if (mutex_lock_interruptible(&dev->mutex))
- return -ERESTARTSYS;
- if (dev->radio_rx_rds_owner &&
- file->private_data != dev->radio_rx_rds_owner) {
- mutex_unlock(&dev->mutex);
- return -EBUSY;
- }
- if (dev->radio_rx_rds_owner == NULL) {
- vivid_radio_rds_init(dev);
- dev->radio_rx_rds_owner = file->private_data;
- }
-
-retry:
- timestamp = ktime_sub(ktime_get(), dev->radio_rds_init_time);
- blk = ktime_divns(timestamp, VIVID_RDS_NSEC_PER_BLK);
- use_alternates = (blk % VIVID_RDS_GEN_BLOCKS) & 1;
-
- if (dev->radio_rx_rds_last_block == 0 ||
- dev->radio_rx_rds_use_alternates != use_alternates) {
- dev->radio_rx_rds_use_alternates = use_alternates;
- /* Re-init the RDS generator */
- vivid_radio_rds_init(dev);
- }
- if (blk >= dev->radio_rx_rds_last_block + VIVID_RDS_GEN_BLOCKS)
- dev->radio_rx_rds_last_block = blk - VIVID_RDS_GEN_BLOCKS + 1;
-
- /*
- * No data is available if there hasn't been time to get new data,
- * or if the RDS receiver has been disabled, or if we use the data
- * from the RDS transmitter and that RDS transmitter has been disabled,
- * or if the signal quality is too weak.
- */
- if (blk == dev->radio_rx_rds_last_block || !dev->radio_rx_rds_enabled ||
- (dev->radio_rds_loop && !(dev->radio_tx_subchans & V4L2_TUNER_SUB_RDS)) ||
- abs(dev->radio_rx_sig_qual) > 200) {
- mutex_unlock(&dev->mutex);
- if (file->f_flags & O_NONBLOCK)
- return -EWOULDBLOCK;
- if (msleep_interruptible(20) && signal_pending(current))
- return -EINTR;
- if (mutex_lock_interruptible(&dev->mutex))
- return -ERESTARTSYS;
- goto retry;
- }
-
- /* abs(dev->radio_rx_sig_qual) <= 200, map that to a 0-50% range */
- perc = abs(dev->radio_rx_sig_qual) / 4;
-
- for (i = 0; i < size && blk > dev->radio_rx_rds_last_block;
- dev->radio_rx_rds_last_block++) {
- unsigned data_blk = dev->radio_rx_rds_last_block % VIVID_RDS_GEN_BLOCKS;
- struct v4l2_rds_data rds = data[data_blk];
-
- if (data_blk == 0 && dev->radio_rds_loop)
- vivid_radio_rds_init(dev);
- if (perc && prandom_u32_max(100) < perc) {
- switch (prandom_u32_max(4)) {
- case 0:
- rds.block |= V4L2_RDS_BLOCK_CORRECTED;
- break;
- case 1:
- rds.block |= V4L2_RDS_BLOCK_INVALID;
- break;
- case 2:
- rds.block |= V4L2_RDS_BLOCK_ERROR;
- rds.lsb = prandom_u32_max(256);
- rds.msb = prandom_u32_max(256);
- break;
- case 3: /* Skip block altogether */
- if (i)
- continue;
- /*
- * Must make sure at least one block is
- * returned, otherwise the application
- * might think that end-of-file occurred.
- */
- break;
- }
- }
- if (copy_to_user(buf + i, &rds, sizeof(rds))) {
- i = -EFAULT;
- break;
- }
- i += sizeof(rds);
- }
- mutex_unlock(&dev->mutex);
- return i;
-}
-
-__poll_t vivid_radio_rx_poll(struct file *file, struct poll_table_struct *wait)
-{
- return EPOLLIN | EPOLLRDNORM | v4l2_ctrl_poll(file, wait);
-}
-
-int vivid_radio_rx_enum_freq_bands(struct file *file, void *fh, struct v4l2_frequency_band *band)
-{
- if (band->tuner != 0)
- return -EINVAL;
-
- if (band->index >= TOT_BANDS)
- return -EINVAL;
-
- *band = vivid_radio_bands[band->index];
- return 0;
-}
-
-int vivid_radio_rx_s_hw_freq_seek(struct file *file, void *fh, const struct v4l2_hw_freq_seek *a)
-{
- struct vivid_dev *dev = video_drvdata(file);
- unsigned low, high;
- unsigned freq;
- unsigned spacing;
- unsigned band;
-
- if (a->tuner)
- return -EINVAL;
- if (a->wrap_around && dev->radio_rx_hw_seek_mode == VIVID_HW_SEEK_BOUNDED)
- return -EINVAL;
-
- if (!a->wrap_around && dev->radio_rx_hw_seek_mode == VIVID_HW_SEEK_WRAP)
- return -EINVAL;
- if (!a->rangelow ^ !a->rangehigh)
- return -EINVAL;
-
- if (file->f_flags & O_NONBLOCK)
- return -EWOULDBLOCK;
-
- if (a->rangelow) {
- for (band = 0; band < TOT_BANDS; band++)
- if (a->rangelow >= vivid_radio_bands[band].rangelow &&
- a->rangehigh <= vivid_radio_bands[band].rangehigh)
- break;
- if (band == TOT_BANDS)
- return -EINVAL;
- if (!dev->radio_rx_hw_seek_prog_lim &&
- (a->rangelow != vivid_radio_bands[band].rangelow ||
- a->rangehigh != vivid_radio_bands[band].rangehigh))
- return -EINVAL;
- low = a->rangelow;
- high = a->rangehigh;
- } else {
- for (band = 0; band < TOT_BANDS; band++)
- if (dev->radio_rx_freq >= vivid_radio_bands[band].rangelow &&
- dev->radio_rx_freq <= vivid_radio_bands[band].rangehigh)
- break;
- if (band == TOT_BANDS)
- return -EINVAL;
- low = vivid_radio_bands[band].rangelow;
- high = vivid_radio_bands[band].rangehigh;
- }
- spacing = band == BAND_AM ? 1600 : 16000;
- freq = clamp(dev->radio_rx_freq, low, high);
-
- if (a->seek_upward) {
- freq = spacing * (freq / spacing) + spacing;
- if (freq > high) {
- if (!a->wrap_around)
- return -ENODATA;
- freq = spacing * (low / spacing) + spacing;
- if (freq >= dev->radio_rx_freq)
- return -ENODATA;
- }
- } else {
- freq = spacing * ((freq + spacing - 1) / spacing) - spacing;
- if (freq < low) {
- if (!a->wrap_around)
- return -ENODATA;
- freq = spacing * ((high + spacing - 1) / spacing) - spacing;
- if (freq <= dev->radio_rx_freq)
- return -ENODATA;
- }
- }
- return 0;
-}
-
-int vivid_radio_rx_g_tuner(struct file *file, void *fh, struct v4l2_tuner *vt)
-{
- struct vivid_dev *dev = video_drvdata(file);
- int delta = 800;
- int sig_qual;
-
- if (vt->index > 0)
- return -EINVAL;
-
- strscpy(vt->name, "AM/FM/SW Receiver", sizeof(vt->name));
- vt->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO |
- V4L2_TUNER_CAP_FREQ_BANDS | V4L2_TUNER_CAP_RDS |
- (dev->radio_rx_rds_controls ?
- V4L2_TUNER_CAP_RDS_CONTROLS :
- V4L2_TUNER_CAP_RDS_BLOCK_IO) |
- (dev->radio_rx_hw_seek_prog_lim ?
- V4L2_TUNER_CAP_HWSEEK_PROG_LIM : 0);
- switch (dev->radio_rx_hw_seek_mode) {
- case VIVID_HW_SEEK_BOUNDED:
- vt->capability |= V4L2_TUNER_CAP_HWSEEK_BOUNDED;
- break;
- case VIVID_HW_SEEK_WRAP:
- vt->capability |= V4L2_TUNER_CAP_HWSEEK_WRAP;
- break;
- case VIVID_HW_SEEK_BOTH:
- vt->capability |= V4L2_TUNER_CAP_HWSEEK_WRAP |
- V4L2_TUNER_CAP_HWSEEK_BOUNDED;
- break;
- }
- vt->rangelow = AM_FREQ_RANGE_LOW;
- vt->rangehigh = FM_FREQ_RANGE_HIGH;
- sig_qual = dev->radio_rx_sig_qual;
- vt->signal = abs(sig_qual) > delta ? 0 :
- 0xffff - ((unsigned)abs(sig_qual) * 0xffff) / delta;
- vt->afc = sig_qual > delta ? 0 : sig_qual;
- if (abs(sig_qual) > delta)
- vt->rxsubchans = 0;
- else if (dev->radio_rx_freq < FM_FREQ_RANGE_LOW || vt->signal < 0x8000)
- vt->rxsubchans = V4L2_TUNER_SUB_MONO;
- else if (dev->radio_rds_loop && !(dev->radio_tx_subchans & V4L2_TUNER_SUB_STEREO))
- vt->rxsubchans = V4L2_TUNER_SUB_MONO;
- else
- vt->rxsubchans = V4L2_TUNER_SUB_STEREO;
- if (dev->radio_rx_rds_enabled &&
- (!dev->radio_rds_loop || (dev->radio_tx_subchans & V4L2_TUNER_SUB_RDS)) &&
- dev->radio_rx_freq >= FM_FREQ_RANGE_LOW && vt->signal >= 0xc000)
- vt->rxsubchans |= V4L2_TUNER_SUB_RDS;
- if (dev->radio_rx_rds_controls)
- vivid_radio_rds_init(dev);
- vt->audmode = dev->radio_rx_audmode;
- return 0;
-}
-
-int vivid_radio_rx_s_tuner(struct file *file, void *fh, const struct v4l2_tuner *vt)
-{
- struct vivid_dev *dev = video_drvdata(file);
-
- if (vt->index)
- return -EINVAL;
- dev->radio_rx_audmode = vt->audmode >= V4L2_TUNER_MODE_STEREO;
- return 0;
-}
diff --git a/drivers/media/platform/vivid/vivid-radio-rx.h b/drivers/media/platform/vivid/vivid-radio-rx.h
deleted file mode 100644
index c9c7849..0000000
--- a/drivers/media/platform/vivid/vivid-radio-rx.h
+++ /dev/null
@@ -1,19 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-/*
- * vivid-radio-rx.h - radio receiver support functions.
- *
- * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- */
-
-#ifndef _VIVID_RADIO_RX_H_
-#define _VIVID_RADIO_RX_H_
-
-ssize_t vivid_radio_rx_read(struct file *, char __user *, size_t, loff_t *);
-__poll_t vivid_radio_rx_poll(struct file *file, struct poll_table_struct *wait);
-
-int vivid_radio_rx_enum_freq_bands(struct file *file, void *fh, struct v4l2_frequency_band *band);
-int vivid_radio_rx_s_hw_freq_seek(struct file *file, void *fh, const struct v4l2_hw_freq_seek *a);
-int vivid_radio_rx_g_tuner(struct file *file, void *fh, struct v4l2_tuner *vt);
-int vivid_radio_rx_s_tuner(struct file *file, void *fh, const struct v4l2_tuner *vt);
-
-#endif
diff --git a/drivers/media/platform/vivid/vivid-radio-tx.c b/drivers/media/platform/vivid/vivid-radio-tx.c
deleted file mode 100644
index 049d40b..0000000
--- a/drivers/media/platform/vivid/vivid-radio-tx.c
+++ /dev/null
@@ -1,128 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * vivid-radio-tx.c - radio transmitter support functions.
- *
- * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- */
-
-#include <linux/errno.h>
-#include <linux/kernel.h>
-#include <linux/sched/signal.h>
-#include <linux/delay.h>
-#include <linux/videodev2.h>
-#include <linux/v4l2-dv-timings.h>
-#include <media/v4l2-common.h>
-#include <media/v4l2-event.h>
-#include <media/v4l2-dv-timings.h>
-
-#include "vivid-core.h"
-#include "vivid-ctrls.h"
-#include "vivid-radio-common.h"
-#include "vivid-radio-tx.h"
-
-ssize_t vivid_radio_tx_write(struct file *file, const char __user *buf,
- size_t size, loff_t *offset)
-{
- struct vivid_dev *dev = video_drvdata(file);
- struct v4l2_rds_data *data = dev->rds_gen.data;
- ktime_t timestamp;
- unsigned blk;
- int i;
-
- if (dev->radio_tx_rds_controls)
- return -EINVAL;
-
- if (size < sizeof(*data))
- return -EINVAL;
- size = sizeof(*data) * (size / sizeof(*data));
-
- if (mutex_lock_interruptible(&dev->mutex))
- return -ERESTARTSYS;
- if (dev->radio_tx_rds_owner &&
- file->private_data != dev->radio_tx_rds_owner) {
- mutex_unlock(&dev->mutex);
- return -EBUSY;
- }
- dev->radio_tx_rds_owner = file->private_data;
-
-retry:
- timestamp = ktime_sub(ktime_get(), dev->radio_rds_init_time);
- blk = ktime_divns(timestamp, VIVID_RDS_NSEC_PER_BLK);
- if (blk - VIVID_RDS_GEN_BLOCKS >= dev->radio_tx_rds_last_block)
- dev->radio_tx_rds_last_block = blk - VIVID_RDS_GEN_BLOCKS + 1;
-
- /*
- * No data is available if there hasn't been time to get new data,
- * or if the RDS receiver has been disabled, or if we use the data
- * from the RDS transmitter and that RDS transmitter has been disabled,
- * or if the signal quality is too weak.
- */
- if (blk == dev->radio_tx_rds_last_block ||
- !(dev->radio_tx_subchans & V4L2_TUNER_SUB_RDS)) {
- mutex_unlock(&dev->mutex);
- if (file->f_flags & O_NONBLOCK)
- return -EWOULDBLOCK;
- if (msleep_interruptible(20) && signal_pending(current))
- return -EINTR;
- if (mutex_lock_interruptible(&dev->mutex))
- return -ERESTARTSYS;
- goto retry;
- }
-
- for (i = 0; i < size && blk > dev->radio_tx_rds_last_block;
- dev->radio_tx_rds_last_block++) {
- unsigned data_blk = dev->radio_tx_rds_last_block % VIVID_RDS_GEN_BLOCKS;
- struct v4l2_rds_data rds;
-
- if (copy_from_user(&rds, buf + i, sizeof(rds))) {
- i = -EFAULT;
- break;
- }
- i += sizeof(rds);
- if (!dev->radio_rds_loop)
- continue;
- if ((rds.block & V4L2_RDS_BLOCK_MSK) == V4L2_RDS_BLOCK_INVALID ||
- (rds.block & V4L2_RDS_BLOCK_ERROR))
- continue;
- rds.block &= V4L2_RDS_BLOCK_MSK;
- data[data_blk] = rds;
- }
- mutex_unlock(&dev->mutex);
- return i;
-}
-
-__poll_t vivid_radio_tx_poll(struct file *file, struct poll_table_struct *wait)
-{
- return EPOLLOUT | EPOLLWRNORM | v4l2_ctrl_poll(file, wait);
-}
-
-int vidioc_g_modulator(struct file *file, void *fh, struct v4l2_modulator *a)
-{
- struct vivid_dev *dev = video_drvdata(file);
-
- if (a->index > 0)
- return -EINVAL;
-
- strscpy(a->name, "AM/FM/SW Transmitter", sizeof(a->name));
- a->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO |
- V4L2_TUNER_CAP_FREQ_BANDS | V4L2_TUNER_CAP_RDS |
- (dev->radio_tx_rds_controls ?
- V4L2_TUNER_CAP_RDS_CONTROLS :
- V4L2_TUNER_CAP_RDS_BLOCK_IO);
- a->rangelow = AM_FREQ_RANGE_LOW;
- a->rangehigh = FM_FREQ_RANGE_HIGH;
- a->txsubchans = dev->radio_tx_subchans;
- return 0;
-}
-
-int vidioc_s_modulator(struct file *file, void *fh, const struct v4l2_modulator *a)
-{
- struct vivid_dev *dev = video_drvdata(file);
-
- if (a->index)
- return -EINVAL;
- if (a->txsubchans & ~0x13)
- return -EINVAL;
- dev->radio_tx_subchans = a->txsubchans;
- return 0;
-}
diff --git a/drivers/media/platform/vivid/vivid-radio-tx.h b/drivers/media/platform/vivid/vivid-radio-tx.h
deleted file mode 100644
index c2bf1e7..0000000
--- a/drivers/media/platform/vivid/vivid-radio-tx.h
+++ /dev/null
@@ -1,17 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-/*
- * vivid-radio-tx.h - radio transmitter support functions.
- *
- * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- */
-
-#ifndef _VIVID_RADIO_TX_H_
-#define _VIVID_RADIO_TX_H_
-
-ssize_t vivid_radio_tx_write(struct file *, const char __user *, size_t, loff_t *);
-__poll_t vivid_radio_tx_poll(struct file *file, struct poll_table_struct *wait);
-
-int vidioc_g_modulator(struct file *file, void *fh, struct v4l2_modulator *a);
-int vidioc_s_modulator(struct file *file, void *fh, const struct v4l2_modulator *a);
-
-#endif
diff --git a/drivers/media/platform/vivid/vivid-rds-gen.c b/drivers/media/platform/vivid/vivid-rds-gen.c
deleted file mode 100644
index b5b104e..0000000
--- a/drivers/media/platform/vivid/vivid-rds-gen.c
+++ /dev/null
@@ -1,157 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * vivid-rds-gen.c - rds (radio data system) generator support functions.
- *
- * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- */
-
-#include <linux/kernel.h>
-#include <linux/ktime.h>
-#include <linux/string.h>
-#include <linux/videodev2.h>
-
-#include "vivid-rds-gen.h"
-
-static u8 vivid_get_di(const struct vivid_rds_gen *rds, unsigned grp)
-{
- switch (grp) {
- case 0:
- return (rds->dyn_pty << 2) | (grp & 3);
- case 1:
- return (rds->compressed << 2) | (grp & 3);
- case 2:
- return (rds->art_head << 2) | (grp & 3);
- case 3:
- return (rds->mono_stereo << 2) | (grp & 3);
- }
- return 0;
-}
-
-/*
- * This RDS generator creates 57 RDS groups (one group == four RDS blocks).
- * Groups 0-3, 22-25 and 44-47 (spaced 22 groups apart) are filled with a
- * standard 0B group containing the PI code and PS name.
- *
- * Groups 4-19 and 26-41 use group 2A for the radio text.
- *
- * Group 56 contains the time (group 4A).
- *
- * All remaining groups use a filler group 15B block that just repeats
- * the PI and PTY codes.
- */
-void vivid_rds_generate(struct vivid_rds_gen *rds)
-{
- struct v4l2_rds_data *data = rds->data;
- unsigned grp;
- unsigned idx;
- struct tm tm;
- unsigned date;
- unsigned time;
- int l;
-
- for (grp = 0; grp < VIVID_RDS_GEN_GROUPS; grp++, data += VIVID_RDS_GEN_BLKS_PER_GRP) {
- data[0].lsb = rds->picode & 0xff;
- data[0].msb = rds->picode >> 8;
- data[0].block = V4L2_RDS_BLOCK_A | (V4L2_RDS_BLOCK_A << 3);
- data[1].lsb = rds->pty << 5;
- data[1].msb = (rds->pty >> 3) | (rds->tp << 2);
- data[1].block = V4L2_RDS_BLOCK_B | (V4L2_RDS_BLOCK_B << 3);
- data[3].block = V4L2_RDS_BLOCK_D | (V4L2_RDS_BLOCK_D << 3);
-
- switch (grp) {
- case 0 ... 3:
- case 22 ... 25:
- case 44 ... 47: /* Group 0B */
- idx = (grp % 22) % 4;
- data[1].lsb |= (rds->ta << 4) | (rds->ms << 3);
- data[1].lsb |= vivid_get_di(rds, idx);
- data[1].msb |= 1 << 3;
- data[2].lsb = rds->picode & 0xff;
- data[2].msb = rds->picode >> 8;
- data[2].block = V4L2_RDS_BLOCK_C_ALT | (V4L2_RDS_BLOCK_C_ALT << 3);
- data[3].lsb = rds->psname[2 * idx + 1];
- data[3].msb = rds->psname[2 * idx];
- break;
- case 4 ... 19:
- case 26 ... 41: /* Group 2A */
- idx = ((grp - 4) % 22) % 16;
- data[1].lsb |= idx;
- data[1].msb |= 4 << 3;
- data[2].msb = rds->radiotext[4 * idx];
- data[2].lsb = rds->radiotext[4 * idx + 1];
- data[2].block = V4L2_RDS_BLOCK_C | (V4L2_RDS_BLOCK_C << 3);
- data[3].msb = rds->radiotext[4 * idx + 2];
- data[3].lsb = rds->radiotext[4 * idx + 3];
- break;
- case 56:
- /*
- * Group 4A
- *
- * Uses the algorithm from Annex G of the RDS standard
- * EN 50067:1998 to convert a UTC date to an RDS Modified
- * Julian Day.
- */
- time64_to_tm(ktime_get_real_seconds(), 0, &tm);
- l = tm.tm_mon <= 1;
- date = 14956 + tm.tm_mday + ((tm.tm_year - l) * 1461) / 4 +
- ((tm.tm_mon + 2 + l * 12) * 306001) / 10000;
- time = (tm.tm_hour << 12) |
- (tm.tm_min << 6) |
- (sys_tz.tz_minuteswest >= 0 ? 0x20 : 0) |
- (abs(sys_tz.tz_minuteswest) / 30);
- data[1].lsb &= ~3;
- data[1].lsb |= date >> 15;
- data[1].msb |= 8 << 3;
- data[2].lsb = (date << 1) & 0xfe;
- data[2].lsb |= (time >> 16) & 1;
- data[2].msb = (date >> 7) & 0xff;
- data[2].block = V4L2_RDS_BLOCK_C | (V4L2_RDS_BLOCK_C << 3);
- data[3].lsb = time & 0xff;
- data[3].msb = (time >> 8) & 0xff;
- break;
- default: /* Group 15B */
- data[1].lsb |= (rds->ta << 4) | (rds->ms << 3);
- data[1].lsb |= vivid_get_di(rds, grp % 22);
- data[1].msb |= 0x1f << 3;
- data[2].lsb = rds->picode & 0xff;
- data[2].msb = rds->picode >> 8;
- data[2].block = V4L2_RDS_BLOCK_C_ALT | (V4L2_RDS_BLOCK_C_ALT << 3);
- data[3].lsb = rds->pty << 5;
- data[3].lsb |= (rds->ta << 4) | (rds->ms << 3);
- data[3].lsb |= vivid_get_di(rds, grp % 22);
- data[3].msb |= rds->pty >> 3;
- data[3].msb |= 0x1f << 3;
- break;
- }
- }
-}
-
-void vivid_rds_gen_fill(struct vivid_rds_gen *rds, unsigned freq,
- bool alt)
-{
- /* Alternate PTY between Info and Weather */
- if (rds->use_rbds) {
- rds->picode = 0x2e75; /* 'KLNX' call sign */
- rds->pty = alt ? 29 : 2;
- } else {
- rds->picode = 0x8088;
- rds->pty = alt ? 16 : 3;
- }
- rds->mono_stereo = true;
- rds->art_head = false;
- rds->compressed = false;
- rds->dyn_pty = false;
- rds->tp = true;
- rds->ta = alt;
- rds->ms = true;
- snprintf(rds->psname, sizeof(rds->psname), "%6d.%1d",
- freq / 16, ((freq & 0xf) * 10) / 16);
- if (alt)
- strscpy(rds->radiotext,
- " The Radio Data System can switch between different Radio Texts ",
- sizeof(rds->radiotext));
- else
- strscpy(rds->radiotext,
- "An example of Radio Text as transmitted by the Radio Data System",
- sizeof(rds->radiotext));
-}
diff --git a/drivers/media/platform/vivid/vivid-rds-gen.h b/drivers/media/platform/vivid/vivid-rds-gen.h
deleted file mode 100644
index 35ac574..0000000
--- a/drivers/media/platform/vivid/vivid-rds-gen.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-/*
- * vivid-rds-gen.h - rds (radio data system) generator support functions.
- *
- * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- */
-
-#ifndef _VIVID_RDS_GEN_H_
-#define _VIVID_RDS_GEN_H_
-
-/*
- * It takes almost exactly 5 seconds to transmit 57 RDS groups.
- * Each group has 4 blocks and each block has a payload of 16 bits + a
- * block identification. The driver will generate the contents of these
- * 57 groups only when necessary and it will just be played continuously.
- */
-#define VIVID_RDS_GEN_GROUPS 57
-#define VIVID_RDS_GEN_BLKS_PER_GRP 4
-#define VIVID_RDS_GEN_BLOCKS (VIVID_RDS_GEN_BLKS_PER_GRP * VIVID_RDS_GEN_GROUPS)
-#define VIVID_RDS_NSEC_PER_BLK (u32)(5ull * NSEC_PER_SEC / VIVID_RDS_GEN_BLOCKS)
-
-struct vivid_rds_gen {
- struct v4l2_rds_data data[VIVID_RDS_GEN_BLOCKS];
- bool use_rbds;
- u16 picode;
- u8 pty;
- bool mono_stereo;
- bool art_head;
- bool compressed;
- bool dyn_pty;
- bool ta;
- bool tp;
- bool ms;
- char psname[8 + 1];
- char radiotext[64 + 1];
-};
-
-void vivid_rds_gen_fill(struct vivid_rds_gen *rds, unsigned freq,
- bool use_alternate);
-void vivid_rds_generate(struct vivid_rds_gen *rds);
-
-#endif
diff --git a/drivers/media/platform/vivid/vivid-sdr-cap.c b/drivers/media/platform/vivid/vivid-sdr-cap.c
deleted file mode 100644
index 2b7522e..0000000
--- a/drivers/media/platform/vivid/vivid-sdr-cap.c
+++ /dev/null
@@ -1,570 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * vivid-sdr-cap.c - software defined radio support functions.
- *
- * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- */
-
-#include <linux/errno.h>
-#include <linux/kernel.h>
-#include <linux/delay.h>
-#include <linux/kthread.h>
-#include <linux/freezer.h>
-#include <linux/math64.h>
-#include <linux/videodev2.h>
-#include <linux/v4l2-dv-timings.h>
-#include <media/v4l2-common.h>
-#include <media/v4l2-event.h>
-#include <media/v4l2-dv-timings.h>
-#include <linux/fixp-arith.h>
-
-#include "vivid-core.h"
-#include "vivid-ctrls.h"
-#include "vivid-sdr-cap.h"
-
-/* stream formats */
-struct vivid_format {
- u32 pixelformat;
- u32 buffersize;
-};
-
-/* format descriptions for capture and preview */
-static const struct vivid_format formats[] = {
- {
- .pixelformat = V4L2_SDR_FMT_CU8,
- .buffersize = SDR_CAP_SAMPLES_PER_BUF * 2,
- }, {
- .pixelformat = V4L2_SDR_FMT_CS8,
- .buffersize = SDR_CAP_SAMPLES_PER_BUF * 2,
- },
-};
-
-static const struct v4l2_frequency_band bands_adc[] = {
- {
- .tuner = 0,
- .type = V4L2_TUNER_ADC,
- .index = 0,
- .capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS,
- .rangelow = 300000,
- .rangehigh = 300000,
- },
- {
- .tuner = 0,
- .type = V4L2_TUNER_ADC,
- .index = 1,
- .capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS,
- .rangelow = 900001,
- .rangehigh = 2800000,
- },
- {
- .tuner = 0,
- .type = V4L2_TUNER_ADC,
- .index = 2,
- .capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS,
- .rangelow = 3200000,
- .rangehigh = 3200000,
- },
-};
-
-/* ADC band midpoints */
-#define BAND_ADC_0 ((bands_adc[0].rangehigh + bands_adc[1].rangelow) / 2)
-#define BAND_ADC_1 ((bands_adc[1].rangehigh + bands_adc[2].rangelow) / 2)
-
-static const struct v4l2_frequency_band bands_fm[] = {
- {
- .tuner = 1,
- .type = V4L2_TUNER_RF,
- .index = 0,
- .capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS,
- .rangelow = 50000000,
- .rangehigh = 2000000000,
- },
-};
-
-static void vivid_thread_sdr_cap_tick(struct vivid_dev *dev)
-{
- struct vivid_buffer *sdr_cap_buf = NULL;
-
- dprintk(dev, 1, "SDR Capture Thread Tick\n");
-
- /* Drop a certain percentage of buffers. */
- if (dev->perc_dropped_buffers &&
- prandom_u32_max(100) < dev->perc_dropped_buffers)
- return;
-
- spin_lock(&dev->slock);
- if (!list_empty(&dev->sdr_cap_active)) {
- sdr_cap_buf = list_entry(dev->sdr_cap_active.next,
- struct vivid_buffer, list);
- list_del(&sdr_cap_buf->list);
- }
- spin_unlock(&dev->slock);
-
- if (sdr_cap_buf) {
- sdr_cap_buf->vb.sequence = dev->sdr_cap_seq_count;
- v4l2_ctrl_request_setup(sdr_cap_buf->vb.vb2_buf.req_obj.req,
- &dev->ctrl_hdl_sdr_cap);
- v4l2_ctrl_request_complete(sdr_cap_buf->vb.vb2_buf.req_obj.req,
- &dev->ctrl_hdl_sdr_cap);
- vivid_sdr_cap_process(dev, sdr_cap_buf);
- sdr_cap_buf->vb.vb2_buf.timestamp =
- ktime_get_ns() + dev->time_wrap_offset;
- vb2_buffer_done(&sdr_cap_buf->vb.vb2_buf, dev->dqbuf_error ?
- VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE);
- dev->dqbuf_error = false;
- }
-}
-
-static int vivid_thread_sdr_cap(void *data)
-{
- struct vivid_dev *dev = data;
- u64 samples_since_start;
- u64 buffers_since_start;
- u64 next_jiffies_since_start;
- unsigned long jiffies_since_start;
- unsigned long cur_jiffies;
- unsigned wait_jiffies;
-
- dprintk(dev, 1, "SDR Capture Thread Start\n");
-
- set_freezable();
-
- /* Resets frame counters */
- dev->sdr_cap_seq_offset = 0;
- if (dev->seq_wrap)
- dev->sdr_cap_seq_offset = 0xffffff80U;
- dev->jiffies_sdr_cap = jiffies;
- dev->sdr_cap_seq_resync = false;
-
- for (;;) {
- try_to_freeze();
- if (kthread_should_stop())
- break;
-
- if (!mutex_trylock(&dev->mutex)) {
- schedule_timeout_uninterruptible(1);
- continue;
- }
-
- cur_jiffies = jiffies;
- if (dev->sdr_cap_seq_resync) {
- dev->jiffies_sdr_cap = cur_jiffies;
- dev->sdr_cap_seq_offset = dev->sdr_cap_seq_count + 1;
- dev->sdr_cap_seq_count = 0;
- dev->sdr_cap_seq_resync = false;
- }
- /* Calculate the number of jiffies since we started streaming */
- jiffies_since_start = cur_jiffies - dev->jiffies_sdr_cap;
- /* Get the number of buffers streamed since the start */
- buffers_since_start =
- (u64)jiffies_since_start * dev->sdr_adc_freq +
- (HZ * SDR_CAP_SAMPLES_PER_BUF) / 2;
- do_div(buffers_since_start, HZ * SDR_CAP_SAMPLES_PER_BUF);
-
- /*
- * After more than 0xf0000000 (rounded down to a multiple of
- * 'jiffies-per-day' to ease jiffies_to_msecs calculation)
- * jiffies have passed since we started streaming reset the
- * counters and keep track of the sequence offset.
- */
- if (jiffies_since_start > JIFFIES_RESYNC) {
- dev->jiffies_sdr_cap = cur_jiffies;
- dev->sdr_cap_seq_offset = buffers_since_start;
- buffers_since_start = 0;
- }
- dev->sdr_cap_seq_count =
- buffers_since_start + dev->sdr_cap_seq_offset;
-
- vivid_thread_sdr_cap_tick(dev);
- mutex_unlock(&dev->mutex);
-
- /*
- * Calculate the number of samples streamed since we started,
- * not including the current buffer.
- */
- samples_since_start = buffers_since_start * SDR_CAP_SAMPLES_PER_BUF;
-
- /* And the number of jiffies since we started */
- jiffies_since_start = jiffies - dev->jiffies_sdr_cap;
-
- /* Increase by the number of samples in one buffer */
- samples_since_start += SDR_CAP_SAMPLES_PER_BUF;
- /*
- * Calculate when that next buffer is supposed to start
- * in jiffies since we started streaming.
- */
- next_jiffies_since_start = samples_since_start * HZ +
- dev->sdr_adc_freq / 2;
- do_div(next_jiffies_since_start, dev->sdr_adc_freq);
- /* If it is in the past, then just schedule asap */
- if (next_jiffies_since_start < jiffies_since_start)
- next_jiffies_since_start = jiffies_since_start;
-
- wait_jiffies = next_jiffies_since_start - jiffies_since_start;
- schedule_timeout_interruptible(wait_jiffies ? wait_jiffies : 1);
- }
- dprintk(dev, 1, "SDR Capture Thread End\n");
- return 0;
-}
-
-static int sdr_cap_queue_setup(struct vb2_queue *vq,
- unsigned *nbuffers, unsigned *nplanes,
- unsigned sizes[], struct device *alloc_devs[])
-{
- /* 2 = max 16-bit sample returned */
- sizes[0] = SDR_CAP_SAMPLES_PER_BUF * 2;
- *nplanes = 1;
- return 0;
-}
-
-static int sdr_cap_buf_prepare(struct vb2_buffer *vb)
-{
- struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
- unsigned size = SDR_CAP_SAMPLES_PER_BUF * 2;
-
- dprintk(dev, 1, "%s\n", __func__);
-
- if (dev->buf_prepare_error) {
- /*
- * Error injection: test what happens if buf_prepare() returns
- * an error.
- */
- dev->buf_prepare_error = false;
- return -EINVAL;
- }
- if (vb2_plane_size(vb, 0) < size) {
- dprintk(dev, 1, "%s data will not fit into plane (%lu < %u)\n",
- __func__, vb2_plane_size(vb, 0), size);
- return -EINVAL;
- }
- vb2_set_plane_payload(vb, 0, size);
-
- return 0;
-}
-
-static void sdr_cap_buf_queue(struct vb2_buffer *vb)
-{
- struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
- struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
- struct vivid_buffer *buf = container_of(vbuf, struct vivid_buffer, vb);
-
- dprintk(dev, 1, "%s\n", __func__);
-
- spin_lock(&dev->slock);
- list_add_tail(&buf->list, &dev->sdr_cap_active);
- spin_unlock(&dev->slock);
-}
-
-static int sdr_cap_start_streaming(struct vb2_queue *vq, unsigned count)
-{
- struct vivid_dev *dev = vb2_get_drv_priv(vq);
- int err = 0;
-
- dprintk(dev, 1, "%s\n", __func__);
- dev->sdr_cap_seq_count = 0;
- if (dev->start_streaming_error) {
- dev->start_streaming_error = false;
- err = -EINVAL;
- } else if (dev->kthread_sdr_cap == NULL) {
- dev->kthread_sdr_cap = kthread_run(vivid_thread_sdr_cap, dev,
- "%s-sdr-cap", dev->v4l2_dev.name);
-
- if (IS_ERR(dev->kthread_sdr_cap)) {
- v4l2_err(&dev->v4l2_dev, "kernel_thread() failed\n");
- err = PTR_ERR(dev->kthread_sdr_cap);
- dev->kthread_sdr_cap = NULL;
- }
- }
- if (err) {
- struct vivid_buffer *buf, *tmp;
-
- list_for_each_entry_safe(buf, tmp, &dev->sdr_cap_active, list) {
- list_del(&buf->list);
- vb2_buffer_done(&buf->vb.vb2_buf,
- VB2_BUF_STATE_QUEUED);
- }
- }
- return err;
-}
-
-/* abort streaming and wait for last buffer */
-static void sdr_cap_stop_streaming(struct vb2_queue *vq)
-{
- struct vivid_dev *dev = vb2_get_drv_priv(vq);
-
- if (dev->kthread_sdr_cap == NULL)
- return;
-
- while (!list_empty(&dev->sdr_cap_active)) {
- struct vivid_buffer *buf;
-
- buf = list_entry(dev->sdr_cap_active.next,
- struct vivid_buffer, list);
- list_del(&buf->list);
- v4l2_ctrl_request_complete(buf->vb.vb2_buf.req_obj.req,
- &dev->ctrl_hdl_sdr_cap);
- vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
- }
-
- /* shutdown control thread */
- kthread_stop(dev->kthread_sdr_cap);
- dev->kthread_sdr_cap = NULL;
-}
-
-static void sdr_cap_buf_request_complete(struct vb2_buffer *vb)
-{
- struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
-
- v4l2_ctrl_request_complete(vb->req_obj.req, &dev->ctrl_hdl_sdr_cap);
-}
-
-const struct vb2_ops vivid_sdr_cap_qops = {
- .queue_setup = sdr_cap_queue_setup,
- .buf_prepare = sdr_cap_buf_prepare,
- .buf_queue = sdr_cap_buf_queue,
- .start_streaming = sdr_cap_start_streaming,
- .stop_streaming = sdr_cap_stop_streaming,
- .buf_request_complete = sdr_cap_buf_request_complete,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
-};
-
-int vivid_sdr_enum_freq_bands(struct file *file, void *fh,
- struct v4l2_frequency_band *band)
-{
- switch (band->tuner) {
- case 0:
- if (band->index >= ARRAY_SIZE(bands_adc))
- return -EINVAL;
- *band = bands_adc[band->index];
- return 0;
- case 1:
- if (band->index >= ARRAY_SIZE(bands_fm))
- return -EINVAL;
- *band = bands_fm[band->index];
- return 0;
- default:
- return -EINVAL;
- }
-}
-
-int vivid_sdr_g_frequency(struct file *file, void *fh,
- struct v4l2_frequency *vf)
-{
- struct vivid_dev *dev = video_drvdata(file);
-
- switch (vf->tuner) {
- case 0:
- vf->frequency = dev->sdr_adc_freq;
- vf->type = V4L2_TUNER_ADC;
- return 0;
- case 1:
- vf->frequency = dev->sdr_fm_freq;
- vf->type = V4L2_TUNER_RF;
- return 0;
- default:
- return -EINVAL;
- }
-}
-
-int vivid_sdr_s_frequency(struct file *file, void *fh,
- const struct v4l2_frequency *vf)
-{
- struct vivid_dev *dev = video_drvdata(file);
- unsigned freq = vf->frequency;
- unsigned band;
-
- switch (vf->tuner) {
- case 0:
- if (vf->type != V4L2_TUNER_ADC)
- return -EINVAL;
- if (freq < BAND_ADC_0)
- band = 0;
- else if (freq < BAND_ADC_1)
- band = 1;
- else
- band = 2;
-
- freq = clamp_t(unsigned, freq,
- bands_adc[band].rangelow,
- bands_adc[band].rangehigh);
-
- if (vb2_is_streaming(&dev->vb_sdr_cap_q) &&
- freq != dev->sdr_adc_freq) {
- /* resync the thread's timings */
- dev->sdr_cap_seq_resync = true;
- }
- dev->sdr_adc_freq = freq;
- return 0;
- case 1:
- if (vf->type != V4L2_TUNER_RF)
- return -EINVAL;
- dev->sdr_fm_freq = clamp_t(unsigned, freq,
- bands_fm[0].rangelow,
- bands_fm[0].rangehigh);
- return 0;
- default:
- return -EINVAL;
- }
-}
-
-int vivid_sdr_g_tuner(struct file *file, void *fh, struct v4l2_tuner *vt)
-{
- switch (vt->index) {
- case 0:
- strscpy(vt->name, "ADC", sizeof(vt->name));
- vt->type = V4L2_TUNER_ADC;
- vt->capability =
- V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS;
- vt->rangelow = bands_adc[0].rangelow;
- vt->rangehigh = bands_adc[2].rangehigh;
- return 0;
- case 1:
- strscpy(vt->name, "RF", sizeof(vt->name));
- vt->type = V4L2_TUNER_RF;
- vt->capability =
- V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS;
- vt->rangelow = bands_fm[0].rangelow;
- vt->rangehigh = bands_fm[0].rangehigh;
- return 0;
- default:
- return -EINVAL;
- }
-}
-
-int vivid_sdr_s_tuner(struct file *file, void *fh, const struct v4l2_tuner *vt)
-{
- if (vt->index > 1)
- return -EINVAL;
- return 0;
-}
-
-int vidioc_enum_fmt_sdr_cap(struct file *file, void *fh, struct v4l2_fmtdesc *f)
-{
- if (f->index >= ARRAY_SIZE(formats))
- return -EINVAL;
- f->pixelformat = formats[f->index].pixelformat;
- return 0;
-}
-
-int vidioc_g_fmt_sdr_cap(struct file *file, void *fh, struct v4l2_format *f)
-{
- struct vivid_dev *dev = video_drvdata(file);
-
- f->fmt.sdr.pixelformat = dev->sdr_pixelformat;
- f->fmt.sdr.buffersize = dev->sdr_buffersize;
- memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved));
- return 0;
-}
-
-int vidioc_s_fmt_sdr_cap(struct file *file, void *fh, struct v4l2_format *f)
-{
- struct vivid_dev *dev = video_drvdata(file);
- struct vb2_queue *q = &dev->vb_sdr_cap_q;
- int i;
-
- if (vb2_is_busy(q))
- return -EBUSY;
-
- memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved));
- for (i = 0; i < ARRAY_SIZE(formats); i++) {
- if (formats[i].pixelformat == f->fmt.sdr.pixelformat) {
- dev->sdr_pixelformat = formats[i].pixelformat;
- dev->sdr_buffersize = formats[i].buffersize;
- f->fmt.sdr.buffersize = formats[i].buffersize;
- return 0;
- }
- }
- dev->sdr_pixelformat = formats[0].pixelformat;
- dev->sdr_buffersize = formats[0].buffersize;
- f->fmt.sdr.pixelformat = formats[0].pixelformat;
- f->fmt.sdr.buffersize = formats[0].buffersize;
- return 0;
-}
-
-int vidioc_try_fmt_sdr_cap(struct file *file, void *fh, struct v4l2_format *f)
-{
- int i;
-
- memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved));
- for (i = 0; i < ARRAY_SIZE(formats); i++) {
- if (formats[i].pixelformat == f->fmt.sdr.pixelformat) {
- f->fmt.sdr.buffersize = formats[i].buffersize;
- return 0;
- }
- }
- f->fmt.sdr.pixelformat = formats[0].pixelformat;
- f->fmt.sdr.buffersize = formats[0].buffersize;
- return 0;
-}
-
-#define FIXP_N (15)
-#define FIXP_FRAC (1 << FIXP_N)
-#define FIXP_2PI ((int)(2 * 3.141592653589 * FIXP_FRAC))
-#define M_100000PI (3.14159 * 100000)
-
-void vivid_sdr_cap_process(struct vivid_dev *dev, struct vivid_buffer *buf)
-{
- u8 *vbuf = vb2_plane_vaddr(&buf->vb.vb2_buf, 0);
- unsigned long i;
- unsigned long plane_size = vb2_plane_size(&buf->vb.vb2_buf, 0);
- s64 s64tmp;
- s32 src_phase_step;
- s32 mod_phase_step;
- s32 fixp_i;
- s32 fixp_q;
-
- /* calculate phase step */
- #define BEEP_FREQ 1000 /* 1kHz beep */
- src_phase_step = DIV_ROUND_CLOSEST(FIXP_2PI * BEEP_FREQ,
- dev->sdr_adc_freq);
-
- for (i = 0; i < plane_size; i += 2) {
- mod_phase_step = fixp_cos32_rad(dev->sdr_fixp_src_phase,
- FIXP_2PI) >> (31 - FIXP_N);
-
- dev->sdr_fixp_src_phase += src_phase_step;
- s64tmp = (s64) mod_phase_step * dev->sdr_fm_deviation;
- dev->sdr_fixp_mod_phase += div_s64(s64tmp, M_100000PI);
-
- /*
- * Transfer phase angle to [0, 2xPI] in order to avoid variable
- * overflow and make it suitable for cosine implementation
- * used, which does not support negative angles.
- */
- dev->sdr_fixp_src_phase %= FIXP_2PI;
- dev->sdr_fixp_mod_phase %= FIXP_2PI;
-
- if (dev->sdr_fixp_mod_phase < 0)
- dev->sdr_fixp_mod_phase += FIXP_2PI;
-
- fixp_i = fixp_cos32_rad(dev->sdr_fixp_mod_phase, FIXP_2PI);
- fixp_q = fixp_sin32_rad(dev->sdr_fixp_mod_phase, FIXP_2PI);
-
- /* Normalize fraction values represented with 32 bit precision
- * to fixed point representation with FIXP_N bits */
- fixp_i >>= (31 - FIXP_N);
- fixp_q >>= (31 - FIXP_N);
-
- switch (dev->sdr_pixelformat) {
- case V4L2_SDR_FMT_CU8:
- /* convert 'fixp float' to u8 [0, +255] */
- /* u8 = X * 127.5 + 127.5; X is float [-1.0, +1.0] */
- fixp_i = fixp_i * 1275 + FIXP_FRAC * 1275;
- fixp_q = fixp_q * 1275 + FIXP_FRAC * 1275;
- *vbuf++ = DIV_ROUND_CLOSEST(fixp_i, FIXP_FRAC * 10);
- *vbuf++ = DIV_ROUND_CLOSEST(fixp_q, FIXP_FRAC * 10);
- break;
- case V4L2_SDR_FMT_CS8:
- /* convert 'fixp float' to s8 [-128, +127] */
- /* s8 = X * 127.5 - 0.5; X is float [-1.0, +1.0] */
- fixp_i = fixp_i * 1275 - FIXP_FRAC * 5;
- fixp_q = fixp_q * 1275 - FIXP_FRAC * 5;
- *vbuf++ = DIV_ROUND_CLOSEST(fixp_i, FIXP_FRAC * 10);
- *vbuf++ = DIV_ROUND_CLOSEST(fixp_q, FIXP_FRAC * 10);
- break;
- default:
- break;
- }
- }
-}
diff --git a/drivers/media/platform/vivid/vivid-sdr-cap.h b/drivers/media/platform/vivid/vivid-sdr-cap.h
deleted file mode 100644
index 813c924..0000000
--- a/drivers/media/platform/vivid/vivid-sdr-cap.h
+++ /dev/null
@@ -1,24 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-/*
- * vivid-sdr-cap.h - software defined radio support functions.
- *
- * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- */
-
-#ifndef _VIVID_SDR_CAP_H_
-#define _VIVID_SDR_CAP_H_
-
-int vivid_sdr_enum_freq_bands(struct file *file, void *fh, struct v4l2_frequency_band *band);
-int vivid_sdr_g_frequency(struct file *file, void *fh, struct v4l2_frequency *vf);
-int vivid_sdr_s_frequency(struct file *file, void *fh, const struct v4l2_frequency *vf);
-int vivid_sdr_g_tuner(struct file *file, void *fh, struct v4l2_tuner *vt);
-int vivid_sdr_s_tuner(struct file *file, void *fh, const struct v4l2_tuner *vt);
-int vidioc_enum_fmt_sdr_cap(struct file *file, void *fh, struct v4l2_fmtdesc *f);
-int vidioc_g_fmt_sdr_cap(struct file *file, void *fh, struct v4l2_format *f);
-int vidioc_s_fmt_sdr_cap(struct file *file, void *fh, struct v4l2_format *f);
-int vidioc_try_fmt_sdr_cap(struct file *file, void *fh, struct v4l2_format *f);
-void vivid_sdr_cap_process(struct vivid_dev *dev, struct vivid_buffer *buf);
-
-extern const struct vb2_ops vivid_sdr_cap_qops;
-
-#endif
diff --git a/drivers/media/platform/vivid/vivid-vbi-cap.c b/drivers/media/platform/vivid/vivid-vbi-cap.c
deleted file mode 100644
index 1a9348e..0000000
--- a/drivers/media/platform/vivid/vivid-vbi-cap.c
+++ /dev/null
@@ -1,365 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * vivid-vbi-cap.c - vbi capture support functions.
- *
- * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- */
-
-#include <linux/errno.h>
-#include <linux/kernel.h>
-#include <linux/videodev2.h>
-#include <media/v4l2-common.h>
-
-#include "vivid-core.h"
-#include "vivid-kthread-cap.h"
-#include "vivid-vbi-cap.h"
-#include "vivid-vbi-gen.h"
-
-static void vivid_sliced_vbi_cap_fill(struct vivid_dev *dev, unsigned seqnr)
-{
- struct vivid_vbi_gen_data *vbi_gen = &dev->vbi_gen;
- bool is_60hz = dev->std_cap[dev->input] & V4L2_STD_525_60;
-
- vivid_vbi_gen_sliced(vbi_gen, is_60hz, seqnr);
-
- if (!is_60hz) {
- if (dev->loop_video) {
- if (dev->vbi_out_have_wss) {
- vbi_gen->data[12].data[0] = dev->vbi_out_wss[0];
- vbi_gen->data[12].data[1] = dev->vbi_out_wss[1];
- } else {
- vbi_gen->data[12].id = 0;
- }
- } else {
- switch (tpg_g_video_aspect(&dev->tpg)) {
- case TPG_VIDEO_ASPECT_14X9_CENTRE:
- vbi_gen->data[12].data[0] = 0x01;
- break;
- case TPG_VIDEO_ASPECT_16X9_CENTRE:
- vbi_gen->data[12].data[0] = 0x0b;
- break;
- case TPG_VIDEO_ASPECT_16X9_ANAMORPHIC:
- vbi_gen->data[12].data[0] = 0x07;
- break;
- case TPG_VIDEO_ASPECT_4X3:
- default:
- vbi_gen->data[12].data[0] = 0x08;
- break;
- }
- }
- } else if (dev->loop_video && is_60hz) {
- if (dev->vbi_out_have_cc[0]) {
- vbi_gen->data[0].data[0] = dev->vbi_out_cc[0][0];
- vbi_gen->data[0].data[1] = dev->vbi_out_cc[0][1];
- } else {
- vbi_gen->data[0].id = 0;
- }
- if (dev->vbi_out_have_cc[1]) {
- vbi_gen->data[1].data[0] = dev->vbi_out_cc[1][0];
- vbi_gen->data[1].data[1] = dev->vbi_out_cc[1][1];
- } else {
- vbi_gen->data[1].id = 0;
- }
- }
-}
-
-static void vivid_g_fmt_vbi_cap(struct vivid_dev *dev, struct v4l2_vbi_format *vbi)
-{
- bool is_60hz = dev->std_cap[dev->input] & V4L2_STD_525_60;
-
- vbi->sampling_rate = 27000000;
- vbi->offset = 24;
- vbi->samples_per_line = 1440;
- vbi->sample_format = V4L2_PIX_FMT_GREY;
- vbi->start[0] = is_60hz ? V4L2_VBI_ITU_525_F1_START + 9 : V4L2_VBI_ITU_625_F1_START + 5;
- vbi->start[1] = is_60hz ? V4L2_VBI_ITU_525_F2_START + 9 : V4L2_VBI_ITU_625_F2_START + 5;
- vbi->count[0] = vbi->count[1] = is_60hz ? 12 : 18;
- vbi->flags = dev->vbi_cap_interlaced ? V4L2_VBI_INTERLACED : 0;
- vbi->reserved[0] = 0;
- vbi->reserved[1] = 0;
-}
-
-void vivid_raw_vbi_cap_process(struct vivid_dev *dev, struct vivid_buffer *buf)
-{
- struct v4l2_vbi_format vbi;
- u8 *vbuf = vb2_plane_vaddr(&buf->vb.vb2_buf, 0);
-
- vivid_g_fmt_vbi_cap(dev, &vbi);
- buf->vb.sequence = dev->vbi_cap_seq_count;
- if (dev->field_cap == V4L2_FIELD_ALTERNATE)
- buf->vb.sequence /= 2;
-
- vivid_sliced_vbi_cap_fill(dev, buf->vb.sequence);
-
- memset(vbuf, 0x10, vb2_plane_size(&buf->vb.vb2_buf, 0));
-
- if (!VIVID_INVALID_SIGNAL(dev->std_signal_mode[dev->input]))
- vivid_vbi_gen_raw(&dev->vbi_gen, &vbi, vbuf);
-}
-
-
-void vivid_sliced_vbi_cap_process(struct vivid_dev *dev,
- struct vivid_buffer *buf)
-{
- struct v4l2_sliced_vbi_data *vbuf =
- vb2_plane_vaddr(&buf->vb.vb2_buf, 0);
-
- buf->vb.sequence = dev->vbi_cap_seq_count;
- if (dev->field_cap == V4L2_FIELD_ALTERNATE)
- buf->vb.sequence /= 2;
-
- vivid_sliced_vbi_cap_fill(dev, buf->vb.sequence);
-
- memset(vbuf, 0, vb2_plane_size(&buf->vb.vb2_buf, 0));
- if (!VIVID_INVALID_SIGNAL(dev->std_signal_mode[dev->input])) {
- unsigned i;
-
- for (i = 0; i < 25; i++)
- vbuf[i] = dev->vbi_gen.data[i];
- }
-}
-
-static int vbi_cap_queue_setup(struct vb2_queue *vq,
- unsigned *nbuffers, unsigned *nplanes,
- unsigned sizes[], struct device *alloc_devs[])
-{
- struct vivid_dev *dev = vb2_get_drv_priv(vq);
- bool is_60hz = dev->std_cap[dev->input] & V4L2_STD_525_60;
- unsigned size = vq->type == V4L2_BUF_TYPE_SLICED_VBI_CAPTURE ?
- 36 * sizeof(struct v4l2_sliced_vbi_data) :
- 1440 * 2 * (is_60hz ? 12 : 18);
-
- if (!vivid_is_sdtv_cap(dev))
- return -EINVAL;
-
- sizes[0] = size;
-
- if (vq->num_buffers + *nbuffers < 2)
- *nbuffers = 2 - vq->num_buffers;
-
- *nplanes = 1;
- return 0;
-}
-
-static int vbi_cap_buf_prepare(struct vb2_buffer *vb)
-{
- struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
- bool is_60hz = dev->std_cap[dev->input] & V4L2_STD_525_60;
- unsigned size = vb->vb2_queue->type == V4L2_BUF_TYPE_SLICED_VBI_CAPTURE ?
- 36 * sizeof(struct v4l2_sliced_vbi_data) :
- 1440 * 2 * (is_60hz ? 12 : 18);
-
- dprintk(dev, 1, "%s\n", __func__);
-
- if (dev->buf_prepare_error) {
- /*
- * Error injection: test what happens if buf_prepare() returns
- * an error.
- */
- dev->buf_prepare_error = false;
- return -EINVAL;
- }
- if (vb2_plane_size(vb, 0) < size) {
- dprintk(dev, 1, "%s data will not fit into plane (%lu < %u)\n",
- __func__, vb2_plane_size(vb, 0), size);
- return -EINVAL;
- }
- vb2_set_plane_payload(vb, 0, size);
-
- return 0;
-}
-
-static void vbi_cap_buf_queue(struct vb2_buffer *vb)
-{
- struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
- struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
- struct vivid_buffer *buf = container_of(vbuf, struct vivid_buffer, vb);
-
- dprintk(dev, 1, "%s\n", __func__);
-
- spin_lock(&dev->slock);
- list_add_tail(&buf->list, &dev->vbi_cap_active);
- spin_unlock(&dev->slock);
-}
-
-static int vbi_cap_start_streaming(struct vb2_queue *vq, unsigned count)
-{
- struct vivid_dev *dev = vb2_get_drv_priv(vq);
- int err;
-
- dprintk(dev, 1, "%s\n", __func__);
- dev->vbi_cap_seq_count = 0;
- if (dev->start_streaming_error) {
- dev->start_streaming_error = false;
- err = -EINVAL;
- } else {
- err = vivid_start_generating_vid_cap(dev, &dev->vbi_cap_streaming);
- }
- if (err) {
- struct vivid_buffer *buf, *tmp;
-
- list_for_each_entry_safe(buf, tmp, &dev->vbi_cap_active, list) {
- list_del(&buf->list);
- vb2_buffer_done(&buf->vb.vb2_buf,
- VB2_BUF_STATE_QUEUED);
- }
- }
- return err;
-}
-
-/* abort streaming and wait for last buffer */
-static void vbi_cap_stop_streaming(struct vb2_queue *vq)
-{
- struct vivid_dev *dev = vb2_get_drv_priv(vq);
-
- dprintk(dev, 1, "%s\n", __func__);
- vivid_stop_generating_vid_cap(dev, &dev->vbi_cap_streaming);
-}
-
-static void vbi_cap_buf_request_complete(struct vb2_buffer *vb)
-{
- struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
-
- v4l2_ctrl_request_complete(vb->req_obj.req, &dev->ctrl_hdl_vbi_cap);
-}
-
-const struct vb2_ops vivid_vbi_cap_qops = {
- .queue_setup = vbi_cap_queue_setup,
- .buf_prepare = vbi_cap_buf_prepare,
- .buf_queue = vbi_cap_buf_queue,
- .start_streaming = vbi_cap_start_streaming,
- .stop_streaming = vbi_cap_stop_streaming,
- .buf_request_complete = vbi_cap_buf_request_complete,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
-};
-
-int vidioc_g_fmt_vbi_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct vivid_dev *dev = video_drvdata(file);
- struct v4l2_vbi_format *vbi = &f->fmt.vbi;
-
- if (!vivid_is_sdtv_cap(dev) || !dev->has_raw_vbi_cap)
- return -EINVAL;
-
- vivid_g_fmt_vbi_cap(dev, vbi);
- return 0;
-}
-
-int vidioc_s_fmt_vbi_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct vivid_dev *dev = video_drvdata(file);
- int ret = vidioc_g_fmt_vbi_cap(file, priv, f);
-
- if (ret)
- return ret;
- if (dev->stream_sliced_vbi_cap && vb2_is_busy(&dev->vb_vbi_cap_q))
- return -EBUSY;
- dev->stream_sliced_vbi_cap = false;
- dev->vbi_cap_dev.queue->type = V4L2_BUF_TYPE_VBI_CAPTURE;
- return 0;
-}
-
-void vivid_fill_service_lines(struct v4l2_sliced_vbi_format *vbi, u32 service_set)
-{
- vbi->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36;
- vbi->service_set = service_set;
- memset(vbi->service_lines, 0, sizeof(vbi->service_lines));
- memset(vbi->reserved, 0, sizeof(vbi->reserved));
-
- if (vbi->service_set == 0)
- return;
-
- if (vbi->service_set & V4L2_SLICED_CAPTION_525) {
- vbi->service_lines[0][21] = V4L2_SLICED_CAPTION_525;
- vbi->service_lines[1][21] = V4L2_SLICED_CAPTION_525;
- }
- if (vbi->service_set & V4L2_SLICED_WSS_625) {
- unsigned i;
-
- for (i = 7; i <= 18; i++)
- vbi->service_lines[0][i] =
- vbi->service_lines[1][i] = V4L2_SLICED_TELETEXT_B;
- vbi->service_lines[0][23] = V4L2_SLICED_WSS_625;
- }
-}
-
-int vidioc_g_fmt_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_format *fmt)
-{
- struct vivid_dev *dev = video_drvdata(file);
- struct v4l2_sliced_vbi_format *vbi = &fmt->fmt.sliced;
-
- if (!vivid_is_sdtv_cap(dev) || !dev->has_sliced_vbi_cap)
- return -EINVAL;
-
- vivid_fill_service_lines(vbi, dev->service_set_cap);
- return 0;
-}
-
-int vidioc_try_fmt_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_format *fmt)
-{
- struct vivid_dev *dev = video_drvdata(file);
- struct v4l2_sliced_vbi_format *vbi = &fmt->fmt.sliced;
- bool is_60hz = dev->std_cap[dev->input] & V4L2_STD_525_60;
- u32 service_set = vbi->service_set;
-
- if (!vivid_is_sdtv_cap(dev) || !dev->has_sliced_vbi_cap)
- return -EINVAL;
-
- service_set &= is_60hz ? V4L2_SLICED_CAPTION_525 :
- V4L2_SLICED_WSS_625 | V4L2_SLICED_TELETEXT_B;
- vivid_fill_service_lines(vbi, service_set);
- return 0;
-}
-
-int vidioc_s_fmt_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_format *fmt)
-{
- struct vivid_dev *dev = video_drvdata(file);
- struct v4l2_sliced_vbi_format *vbi = &fmt->fmt.sliced;
- int ret = vidioc_try_fmt_sliced_vbi_cap(file, fh, fmt);
-
- if (ret)
- return ret;
- if (!dev->stream_sliced_vbi_cap && vb2_is_busy(&dev->vb_vbi_cap_q))
- return -EBUSY;
- dev->service_set_cap = vbi->service_set;
- dev->stream_sliced_vbi_cap = true;
- dev->vbi_cap_dev.queue->type = V4L2_BUF_TYPE_SLICED_VBI_CAPTURE;
- return 0;
-}
-
-int vidioc_g_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_sliced_vbi_cap *cap)
-{
- struct vivid_dev *dev = video_drvdata(file);
- struct video_device *vdev = video_devdata(file);
- bool is_60hz;
-
- if (vdev->vfl_dir == VFL_DIR_RX) {
- is_60hz = dev->std_cap[dev->input] & V4L2_STD_525_60;
- if (!vivid_is_sdtv_cap(dev) || !dev->has_sliced_vbi_cap ||
- cap->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE)
- return -EINVAL;
- } else {
- is_60hz = dev->std_out & V4L2_STD_525_60;
- if (!vivid_is_svid_out(dev) || !dev->has_sliced_vbi_out ||
- cap->type != V4L2_BUF_TYPE_SLICED_VBI_OUTPUT)
- return -EINVAL;
- }
-
- cap->service_set = is_60hz ? V4L2_SLICED_CAPTION_525 :
- V4L2_SLICED_WSS_625 | V4L2_SLICED_TELETEXT_B;
- if (is_60hz) {
- cap->service_lines[0][21] = V4L2_SLICED_CAPTION_525;
- cap->service_lines[1][21] = V4L2_SLICED_CAPTION_525;
- } else {
- unsigned i;
-
- for (i = 7; i <= 18; i++)
- cap->service_lines[0][i] =
- cap->service_lines[1][i] = V4L2_SLICED_TELETEXT_B;
- cap->service_lines[0][23] = V4L2_SLICED_WSS_625;
- }
- return 0;
-}
diff --git a/drivers/media/platform/vivid/vivid-vbi-cap.h b/drivers/media/platform/vivid/vivid-vbi-cap.h
deleted file mode 100644
index 91d2de0..0000000
--- a/drivers/media/platform/vivid/vivid-vbi-cap.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-/*
- * vivid-vbi-cap.h - vbi capture support functions.
- *
- * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- */
-
-#ifndef _VIVID_VBI_CAP_H_
-#define _VIVID_VBI_CAP_H_
-
-void vivid_fill_time_of_day_packet(u8 *packet);
-void vivid_raw_vbi_cap_process(struct vivid_dev *dev, struct vivid_buffer *buf);
-void vivid_sliced_vbi_cap_process(struct vivid_dev *dev, struct vivid_buffer *buf);
-void vivid_sliced_vbi_out_process(struct vivid_dev *dev, struct vivid_buffer *buf);
-int vidioc_g_fmt_vbi_cap(struct file *file, void *priv,
- struct v4l2_format *f);
-int vidioc_s_fmt_vbi_cap(struct file *file, void *priv,
- struct v4l2_format *f);
-int vidioc_g_fmt_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_format *fmt);
-int vidioc_try_fmt_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_format *fmt);
-int vidioc_s_fmt_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_format *fmt);
-int vidioc_g_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_sliced_vbi_cap *cap);
-
-void vivid_fill_service_lines(struct v4l2_sliced_vbi_format *vbi, u32 service_set);
-
-extern const struct vb2_ops vivid_vbi_cap_qops;
-
-#endif
diff --git a/drivers/media/platform/vivid/vivid-vbi-gen.c b/drivers/media/platform/vivid/vivid-vbi-gen.c
deleted file mode 100644
index acc9844..0000000
--- a/drivers/media/platform/vivid/vivid-vbi-gen.c
+++ /dev/null
@@ -1,311 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * vivid-vbi-gen.c - vbi generator support functions.
- *
- * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- */
-
-#include <linux/errno.h>
-#include <linux/kernel.h>
-#include <linux/ktime.h>
-#include <linux/string.h>
-#include <linux/videodev2.h>
-
-#include "vivid-vbi-gen.h"
-
-static void wss_insert(u8 *wss, u32 val, unsigned size)
-{
- while (size--)
- *wss++ = (val & (1 << size)) ? 0xc0 : 0x10;
-}
-
-static void vivid_vbi_gen_wss_raw(const struct v4l2_sliced_vbi_data *data,
- u8 *buf, unsigned sampling_rate)
-{
- const unsigned rate = 5000000; /* WSS has a 5 MHz transmission rate */
- u8 wss[29 + 24 + 24 + 24 + 18 + 18] = { 0 };
- const unsigned zero = 0x07;
- const unsigned one = 0x38;
- unsigned bit = 0;
- u16 wss_data;
- int i;
-
- wss_insert(wss + bit, 0x1f1c71c7, 29); bit += 29;
- wss_insert(wss + bit, 0x1e3c1f, 24); bit += 24;
-
- wss_data = (data->data[1] << 8) | data->data[0];
- for (i = 0; i <= 13; i++, bit += 6)
- wss_insert(wss + bit, (wss_data & (1 << i)) ? one : zero, 6);
-
- for (i = 0, bit = 0; bit < sizeof(wss); bit++) {
- unsigned n = ((bit + 1) * sampling_rate) / rate;
-
- while (i < n)
- buf[i++] = wss[bit];
- }
-}
-
-static void vivid_vbi_gen_teletext_raw(const struct v4l2_sliced_vbi_data *data,
- u8 *buf, unsigned sampling_rate)
-{
- const unsigned rate = 6937500 / 10; /* Teletext has a 6.9375 MHz transmission rate */
- u8 teletext[45] = { 0x55, 0x55, 0x27 };
- unsigned bit = 0;
- int i;
-
- memcpy(teletext + 3, data->data, sizeof(teletext) - 3);
- /* prevents 32 bit overflow */
- sampling_rate /= 10;
-
- for (i = 0, bit = 0; bit < sizeof(teletext) * 8; bit++) {
- unsigned n = ((bit + 1) * sampling_rate) / rate;
- u8 val = (teletext[bit / 8] & (1 << (bit & 7))) ? 0xc0 : 0x10;
-
- while (i < n)
- buf[i++] = val;
- }
-}
-
-static void cc_insert(u8 *cc, u8 ch)
-{
- unsigned tot = 0;
- unsigned i;
-
- for (i = 0; i < 7; i++) {
- cc[2 * i] = cc[2 * i + 1] = (ch & (1 << i)) ? 1 : 0;
- tot += cc[2 * i];
- }
- cc[14] = cc[15] = !(tot & 1);
-}
-
-#define CC_PREAMBLE_BITS (14 + 4 + 2)
-
-static void vivid_vbi_gen_cc_raw(const struct v4l2_sliced_vbi_data *data,
- u8 *buf, unsigned sampling_rate)
-{
- const unsigned rate = 1000000; /* CC has a 1 MHz transmission rate */
-
- u8 cc[CC_PREAMBLE_BITS + 2 * 16] = {
- /* Clock run-in: 7 cycles */
- 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
- /* 2 cycles of 0 */
- 0, 0, 0, 0,
- /* Start bit of 1 (each bit is two cycles) */
- 1, 1
- };
- unsigned bit, i;
-
- cc_insert(cc + CC_PREAMBLE_BITS, data->data[0]);
- cc_insert(cc + CC_PREAMBLE_BITS + 16, data->data[1]);
-
- for (i = 0, bit = 0; bit < sizeof(cc); bit++) {
- unsigned n = ((bit + 1) * sampling_rate) / rate;
-
- while (i < n)
- buf[i++] = cc[bit] ? 0xc0 : 0x10;
- }
-}
-
-void vivid_vbi_gen_raw(const struct vivid_vbi_gen_data *vbi,
- const struct v4l2_vbi_format *vbi_fmt, u8 *buf)
-{
- unsigned idx;
-
- for (idx = 0; idx < 25; idx++) {
- const struct v4l2_sliced_vbi_data *data = vbi->data + idx;
- unsigned start_2nd_field;
- unsigned line = data->line;
- u8 *linebuf = buf;
-
- start_2nd_field = (data->id & V4L2_SLICED_VBI_525) ? 263 : 313;
- if (data->field)
- line += start_2nd_field;
- line -= vbi_fmt->start[data->field];
-
- if (vbi_fmt->flags & V4L2_VBI_INTERLACED)
- linebuf += (line * 2 + data->field) *
- vbi_fmt->samples_per_line;
- else
- linebuf += (line + data->field * vbi_fmt->count[0]) *
- vbi_fmt->samples_per_line;
- if (data->id == V4L2_SLICED_CAPTION_525)
- vivid_vbi_gen_cc_raw(data, linebuf, vbi_fmt->sampling_rate);
- else if (data->id == V4L2_SLICED_WSS_625)
- vivid_vbi_gen_wss_raw(data, linebuf, vbi_fmt->sampling_rate);
- else if (data->id == V4L2_SLICED_TELETEXT_B)
- vivid_vbi_gen_teletext_raw(data, linebuf, vbi_fmt->sampling_rate);
- }
-}
-
-static const u8 vivid_cc_sequence1[30] = {
- 0x14, 0x20, /* Resume Caption Loading */
- 'H', 'e',
- 'l', 'l',
- 'o', ' ',
- 'w', 'o',
- 'r', 'l',
- 'd', '!',
- 0x14, 0x2f, /* End of Caption */
-};
-
-static const u8 vivid_cc_sequence2[30] = {
- 0x14, 0x20, /* Resume Caption Loading */
- 'C', 'l',
- 'o', 's',
- 'e', 'd',
- ' ', 'c',
- 'a', 'p',
- 't', 'i',
- 'o', 'n',
- 's', ' ',
- 't', 'e',
- 's', 't',
- 0x14, 0x2f, /* End of Caption */
-};
-
-static u8 calc_parity(u8 val)
-{
- unsigned i;
- unsigned tot = 0;
-
- for (i = 0; i < 7; i++)
- tot += (val & (1 << i)) ? 1 : 0;
- return val | ((tot & 1) ? 0 : 0x80);
-}
-
-static void vivid_vbi_gen_set_time_of_day(u8 *packet)
-{
- struct tm tm;
- u8 checksum, i;
-
- time64_to_tm(ktime_get_real_seconds(), 0, &tm);
- packet[0] = calc_parity(0x07);
- packet[1] = calc_parity(0x01);
- packet[2] = calc_parity(0x40 | tm.tm_min);
- packet[3] = calc_parity(0x40 | tm.tm_hour);
- packet[4] = calc_parity(0x40 | tm.tm_mday);
- if (tm.tm_mday == 1 && tm.tm_mon == 2 &&
- sys_tz.tz_minuteswest > tm.tm_min + tm.tm_hour * 60)
- packet[4] = calc_parity(0x60 | tm.tm_mday);
- packet[5] = calc_parity(0x40 | (1 + tm.tm_mon));
- packet[6] = calc_parity(0x40 | (1 + tm.tm_wday));
- packet[7] = calc_parity(0x40 | ((tm.tm_year - 90) & 0x3f));
- packet[8] = calc_parity(0x0f);
- for (checksum = i = 0; i <= 8; i++)
- checksum += packet[i] & 0x7f;
- packet[9] = calc_parity(0x100 - checksum);
- checksum = 0;
- packet[10] = calc_parity(0x07);
- packet[11] = calc_parity(0x04);
- if (sys_tz.tz_minuteswest >= 0)
- packet[12] = calc_parity(0x40 | ((sys_tz.tz_minuteswest / 60) & 0x1f));
- else
- packet[12] = calc_parity(0x40 | ((24 + sys_tz.tz_minuteswest / 60) & 0x1f));
- packet[13] = calc_parity(0);
- packet[14] = calc_parity(0x0f);
- for (checksum = 0, i = 10; i <= 14; i++)
- checksum += packet[i] & 0x7f;
- packet[15] = calc_parity(0x100 - checksum);
-}
-
-static const u8 hamming[16] = {
- 0x15, 0x02, 0x49, 0x5e, 0x64, 0x73, 0x38, 0x2f,
- 0xd0, 0xc7, 0x8c, 0x9b, 0xa1, 0xb6, 0xfd, 0xea
-};
-
-static void vivid_vbi_gen_teletext(u8 *packet, unsigned line, unsigned frame)
-{
- unsigned offset = 2;
- unsigned i;
-
- packet[0] = hamming[1 + ((line & 1) << 3)];
- packet[1] = hamming[line >> 1];
- memset(packet + 2, 0x20, 40);
- if (line == 0) {
- /* subcode */
- packet[2] = hamming[frame % 10];
- packet[3] = hamming[frame / 10];
- packet[4] = hamming[0];
- packet[5] = hamming[0];
- packet[6] = hamming[0];
- packet[7] = hamming[0];
- packet[8] = hamming[0];
- packet[9] = hamming[1];
- offset = 10;
- }
- packet += offset;
- memcpy(packet, "Page: 100 Row: 10", 17);
- packet[7] = '0' + frame / 10;
- packet[8] = '0' + frame % 10;
- packet[15] = '0' + line / 10;
- packet[16] = '0' + line % 10;
- for (i = 0; i < 42 - offset; i++)
- packet[i] = calc_parity(packet[i]);
-}
-
-void vivid_vbi_gen_sliced(struct vivid_vbi_gen_data *vbi,
- bool is_60hz, unsigned seqnr)
-{
- struct v4l2_sliced_vbi_data *data0 = vbi->data;
- struct v4l2_sliced_vbi_data *data1 = vbi->data + 1;
- unsigned frame = seqnr % 60;
-
- memset(vbi->data, 0, sizeof(vbi->data));
-
- if (!is_60hz) {
- unsigned i;
-
- for (i = 0; i <= 11; i++) {
- data0->id = V4L2_SLICED_TELETEXT_B;
- data0->line = 7 + i;
- vivid_vbi_gen_teletext(data0->data, i, frame);
- data0++;
- }
- data0->id = V4L2_SLICED_WSS_625;
- data0->line = 23;
- /* 4x3 video aspect ratio */
- data0->data[0] = 0x08;
- data0++;
- for (i = 0; i <= 11; i++) {
- data0->id = V4L2_SLICED_TELETEXT_B;
- data0->field = 1;
- data0->line = 7 + i;
- vivid_vbi_gen_teletext(data0->data, 12 + i, frame);
- data0++;
- }
- return;
- }
-
- data0->id = V4L2_SLICED_CAPTION_525;
- data0->line = 21;
- data1->id = V4L2_SLICED_CAPTION_525;
- data1->field = 1;
- data1->line = 21;
-
- if (frame < 15) {
- data0->data[0] = calc_parity(vivid_cc_sequence1[2 * frame]);
- data0->data[1] = calc_parity(vivid_cc_sequence1[2 * frame + 1]);
- } else if (frame >= 30 && frame < 45) {
- frame -= 30;
- data0->data[0] = calc_parity(vivid_cc_sequence2[2 * frame]);
- data0->data[1] = calc_parity(vivid_cc_sequence2[2 * frame + 1]);
- } else {
- data0->data[0] = calc_parity(0);
- data0->data[1] = calc_parity(0);
- }
-
- frame = seqnr % (30 * 60);
- switch (frame) {
- case 0:
- vivid_vbi_gen_set_time_of_day(vbi->time_of_day_packet);
- /* fall through */
- case 1 ... 7:
- data1->data[0] = vbi->time_of_day_packet[frame * 2];
- data1->data[1] = vbi->time_of_day_packet[frame * 2 + 1];
- break;
- default:
- data1->data[0] = calc_parity(0);
- data1->data[1] = calc_parity(0);
- break;
- }
-}
diff --git a/drivers/media/platform/vivid/vivid-vbi-gen.h b/drivers/media/platform/vivid/vivid-vbi-gen.h
deleted file mode 100644
index 2657a7f..0000000
--- a/drivers/media/platform/vivid/vivid-vbi-gen.h
+++ /dev/null
@@ -1,21 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-/*
- * vivid-vbi-gen.h - vbi generator support functions.
- *
- * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- */
-
-#ifndef _VIVID_VBI_GEN_H_
-#define _VIVID_VBI_GEN_H_
-
-struct vivid_vbi_gen_data {
- struct v4l2_sliced_vbi_data data[25];
- u8 time_of_day_packet[16];
-};
-
-void vivid_vbi_gen_sliced(struct vivid_vbi_gen_data *vbi,
- bool is_60hz, unsigned seqnr);
-void vivid_vbi_gen_raw(const struct vivid_vbi_gen_data *vbi,
- const struct v4l2_vbi_format *vbi_fmt, u8 *buf);
-
-#endif
diff --git a/drivers/media/platform/vivid/vivid-vbi-out.c b/drivers/media/platform/vivid/vivid-vbi-out.c
deleted file mode 100644
index cd56476..0000000
--- a/drivers/media/platform/vivid/vivid-vbi-out.c
+++ /dev/null
@@ -1,250 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * vivid-vbi-out.c - vbi output support functions.
- *
- * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- */
-
-#include <linux/errno.h>
-#include <linux/kernel.h>
-#include <linux/videodev2.h>
-#include <media/v4l2-common.h>
-
-#include "vivid-core.h"
-#include "vivid-kthread-out.h"
-#include "vivid-vbi-out.h"
-#include "vivid-vbi-cap.h"
-
-static int vbi_out_queue_setup(struct vb2_queue *vq,
- unsigned *nbuffers, unsigned *nplanes,
- unsigned sizes[], struct device *alloc_devs[])
-{
- struct vivid_dev *dev = vb2_get_drv_priv(vq);
- bool is_60hz = dev->std_out & V4L2_STD_525_60;
- unsigned size = vq->type == V4L2_BUF_TYPE_SLICED_VBI_OUTPUT ?
- 36 * sizeof(struct v4l2_sliced_vbi_data) :
- 1440 * 2 * (is_60hz ? 12 : 18);
-
- if (!vivid_is_svid_out(dev))
- return -EINVAL;
-
- sizes[0] = size;
-
- if (vq->num_buffers + *nbuffers < 2)
- *nbuffers = 2 - vq->num_buffers;
-
- *nplanes = 1;
- return 0;
-}
-
-static int vbi_out_buf_prepare(struct vb2_buffer *vb)
-{
- struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
- bool is_60hz = dev->std_out & V4L2_STD_525_60;
- unsigned size = vb->vb2_queue->type == V4L2_BUF_TYPE_SLICED_VBI_OUTPUT ?
- 36 * sizeof(struct v4l2_sliced_vbi_data) :
- 1440 * 2 * (is_60hz ? 12 : 18);
-
- dprintk(dev, 1, "%s\n", __func__);
-
- if (dev->buf_prepare_error) {
- /*
- * Error injection: test what happens if buf_prepare() returns
- * an error.
- */
- dev->buf_prepare_error = false;
- return -EINVAL;
- }
- if (vb2_plane_size(vb, 0) < size) {
- dprintk(dev, 1, "%s data will not fit into plane (%lu < %u)\n",
- __func__, vb2_plane_size(vb, 0), size);
- return -EINVAL;
- }
- vb2_set_plane_payload(vb, 0, size);
-
- return 0;
-}
-
-static void vbi_out_buf_queue(struct vb2_buffer *vb)
-{
- struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
- struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
- struct vivid_buffer *buf = container_of(vbuf, struct vivid_buffer, vb);
-
- dprintk(dev, 1, "%s\n", __func__);
-
- spin_lock(&dev->slock);
- list_add_tail(&buf->list, &dev->vbi_out_active);
- spin_unlock(&dev->slock);
-}
-
-static int vbi_out_start_streaming(struct vb2_queue *vq, unsigned count)
-{
- struct vivid_dev *dev = vb2_get_drv_priv(vq);
- int err;
-
- dprintk(dev, 1, "%s\n", __func__);
- dev->vbi_out_seq_count = 0;
- if (dev->start_streaming_error) {
- dev->start_streaming_error = false;
- err = -EINVAL;
- } else {
- err = vivid_start_generating_vid_out(dev, &dev->vbi_out_streaming);
- }
- if (err) {
- struct vivid_buffer *buf, *tmp;
-
- list_for_each_entry_safe(buf, tmp, &dev->vbi_out_active, list) {
- list_del(&buf->list);
- vb2_buffer_done(&buf->vb.vb2_buf,
- VB2_BUF_STATE_QUEUED);
- }
- }
- return err;
-}
-
-/* abort streaming and wait for last buffer */
-static void vbi_out_stop_streaming(struct vb2_queue *vq)
-{
- struct vivid_dev *dev = vb2_get_drv_priv(vq);
-
- dprintk(dev, 1, "%s\n", __func__);
- vivid_stop_generating_vid_out(dev, &dev->vbi_out_streaming);
- dev->vbi_out_have_wss = false;
- dev->vbi_out_have_cc[0] = false;
- dev->vbi_out_have_cc[1] = false;
-}
-
-static void vbi_out_buf_request_complete(struct vb2_buffer *vb)
-{
- struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
-
- v4l2_ctrl_request_complete(vb->req_obj.req, &dev->ctrl_hdl_vbi_out);
-}
-
-const struct vb2_ops vivid_vbi_out_qops = {
- .queue_setup = vbi_out_queue_setup,
- .buf_prepare = vbi_out_buf_prepare,
- .buf_queue = vbi_out_buf_queue,
- .start_streaming = vbi_out_start_streaming,
- .stop_streaming = vbi_out_stop_streaming,
- .buf_request_complete = vbi_out_buf_request_complete,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
-};
-
-int vidioc_g_fmt_vbi_out(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct vivid_dev *dev = video_drvdata(file);
- struct v4l2_vbi_format *vbi = &f->fmt.vbi;
- bool is_60hz = dev->std_out & V4L2_STD_525_60;
-
- if (!vivid_is_svid_out(dev) || !dev->has_raw_vbi_out)
- return -EINVAL;
-
- vbi->sampling_rate = 25000000;
- vbi->offset = 24;
- vbi->samples_per_line = 1440;
- vbi->sample_format = V4L2_PIX_FMT_GREY;
- vbi->start[0] = is_60hz ? V4L2_VBI_ITU_525_F1_START + 9 : V4L2_VBI_ITU_625_F1_START + 5;
- vbi->start[1] = is_60hz ? V4L2_VBI_ITU_525_F2_START + 9 : V4L2_VBI_ITU_625_F2_START + 5;
- vbi->count[0] = vbi->count[1] = is_60hz ? 12 : 18;
- vbi->flags = dev->vbi_cap_interlaced ? V4L2_VBI_INTERLACED : 0;
- vbi->reserved[0] = 0;
- vbi->reserved[1] = 0;
- return 0;
-}
-
-int vidioc_s_fmt_vbi_out(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct vivid_dev *dev = video_drvdata(file);
- int ret = vidioc_g_fmt_vbi_out(file, priv, f);
-
- if (ret)
- return ret;
- if (vb2_is_busy(&dev->vb_vbi_out_q))
- return -EBUSY;
- dev->stream_sliced_vbi_out = false;
- dev->vbi_out_dev.queue->type = V4L2_BUF_TYPE_VBI_OUTPUT;
- return 0;
-}
-
-int vidioc_g_fmt_sliced_vbi_out(struct file *file, void *fh, struct v4l2_format *fmt)
-{
- struct vivid_dev *dev = video_drvdata(file);
- struct v4l2_sliced_vbi_format *vbi = &fmt->fmt.sliced;
-
- if (!vivid_is_svid_out(dev) || !dev->has_sliced_vbi_out)
- return -EINVAL;
-
- vivid_fill_service_lines(vbi, dev->service_set_out);
- return 0;
-}
-
-int vidioc_try_fmt_sliced_vbi_out(struct file *file, void *fh, struct v4l2_format *fmt)
-{
- struct vivid_dev *dev = video_drvdata(file);
- struct v4l2_sliced_vbi_format *vbi = &fmt->fmt.sliced;
- bool is_60hz = dev->std_out & V4L2_STD_525_60;
- u32 service_set = vbi->service_set;
-
- if (!vivid_is_svid_out(dev) || !dev->has_sliced_vbi_out)
- return -EINVAL;
-
- service_set &= is_60hz ? V4L2_SLICED_CAPTION_525 :
- V4L2_SLICED_WSS_625 | V4L2_SLICED_TELETEXT_B;
- vivid_fill_service_lines(vbi, service_set);
- return 0;
-}
-
-int vidioc_s_fmt_sliced_vbi_out(struct file *file, void *fh,
- struct v4l2_format *fmt)
-{
- struct vivid_dev *dev = video_drvdata(file);
- struct v4l2_sliced_vbi_format *vbi = &fmt->fmt.sliced;
- int ret = vidioc_try_fmt_sliced_vbi_out(file, fh, fmt);
-
- if (ret)
- return ret;
- if (vb2_is_busy(&dev->vb_vbi_out_q))
- return -EBUSY;
- dev->service_set_out = vbi->service_set;
- dev->stream_sliced_vbi_out = true;
- dev->vbi_out_dev.queue->type = V4L2_BUF_TYPE_SLICED_VBI_OUTPUT;
- return 0;
-}
-
-void vivid_sliced_vbi_out_process(struct vivid_dev *dev,
- struct vivid_buffer *buf)
-{
- struct v4l2_sliced_vbi_data *vbi =
- vb2_plane_vaddr(&buf->vb.vb2_buf, 0);
- unsigned elems =
- vb2_get_plane_payload(&buf->vb.vb2_buf, 0) / sizeof(*vbi);
-
- dev->vbi_out_have_cc[0] = false;
- dev->vbi_out_have_cc[1] = false;
- dev->vbi_out_have_wss = false;
- while (elems--) {
- switch (vbi->id) {
- case V4L2_SLICED_CAPTION_525:
- if ((dev->std_out & V4L2_STD_525_60) && vbi->line == 21) {
- dev->vbi_out_have_cc[!!vbi->field] = true;
- dev->vbi_out_cc[!!vbi->field][0] = vbi->data[0];
- dev->vbi_out_cc[!!vbi->field][1] = vbi->data[1];
- }
- break;
- case V4L2_SLICED_WSS_625:
- if ((dev->std_out & V4L2_STD_625_50) &&
- vbi->field == 0 && vbi->line == 23) {
- dev->vbi_out_have_wss = true;
- dev->vbi_out_wss[0] = vbi->data[0];
- dev->vbi_out_wss[1] = vbi->data[1];
- }
- break;
- }
- vbi++;
- }
-}
diff --git a/drivers/media/platform/vivid/vivid-vbi-out.h b/drivers/media/platform/vivid/vivid-vbi-out.h
deleted file mode 100644
index 7658494..0000000
--- a/drivers/media/platform/vivid/vivid-vbi-out.h
+++ /dev/null
@@ -1,22 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-/*
- * vivid-vbi-out.h - vbi output support functions.
- *
- * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- */
-
-#ifndef _VIVID_VBI_OUT_H_
-#define _VIVID_VBI_OUT_H_
-
-void vivid_sliced_vbi_out_process(struct vivid_dev *dev, struct vivid_buffer *buf);
-int vidioc_g_fmt_vbi_out(struct file *file, void *priv,
- struct v4l2_format *f);
-int vidioc_s_fmt_vbi_out(struct file *file, void *priv,
- struct v4l2_format *f);
-int vidioc_g_fmt_sliced_vbi_out(struct file *file, void *fh, struct v4l2_format *fmt);
-int vidioc_try_fmt_sliced_vbi_out(struct file *file, void *fh, struct v4l2_format *fmt);
-int vidioc_s_fmt_sliced_vbi_out(struct file *file, void *fh, struct v4l2_format *fmt);
-
-extern const struct vb2_ops vivid_vbi_out_qops;
-
-#endif
diff --git a/drivers/media/platform/vivid/vivid-vid-cap.c b/drivers/media/platform/vivid/vivid-vid-cap.c
deleted file mode 100644
index 2d03073..0000000
--- a/drivers/media/platform/vivid/vivid-vid-cap.c
+++ /dev/null
@@ -1,1915 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * vivid-vid-cap.c - video capture support functions.
- *
- * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- */
-
-#include <linux/errno.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/vmalloc.h>
-#include <linux/videodev2.h>
-#include <linux/v4l2-dv-timings.h>
-#include <media/v4l2-common.h>
-#include <media/v4l2-event.h>
-#include <media/v4l2-dv-timings.h>
-#include <media/v4l2-rect.h>
-
-#include "vivid-core.h"
-#include "vivid-vid-common.h"
-#include "vivid-kthread-cap.h"
-#include "vivid-vid-cap.h"
-
-static const struct vivid_fmt formats_ovl[] = {
- {
- .fourcc = V4L2_PIX_FMT_RGB565, /* gggbbbbb rrrrrggg */
- .vdownsampling = { 1 },
- .bit_depth = { 16 },
- .planes = 1,
- .buffers = 1,
- },
- {
- .fourcc = V4L2_PIX_FMT_XRGB555, /* gggbbbbb arrrrrgg */
- .vdownsampling = { 1 },
- .bit_depth = { 16 },
- .planes = 1,
- .buffers = 1,
- },
- {
- .fourcc = V4L2_PIX_FMT_ARGB555, /* gggbbbbb arrrrrgg */
- .vdownsampling = { 1 },
- .bit_depth = { 16 },
- .planes = 1,
- .buffers = 1,
- },
-};
-
-/* The number of discrete webcam framesizes */
-#define VIVID_WEBCAM_SIZES 6
-/* The number of discrete webcam frameintervals */
-#define VIVID_WEBCAM_IVALS (VIVID_WEBCAM_SIZES * 2)
-
-/* Sizes must be in increasing order */
-static const struct v4l2_frmsize_discrete webcam_sizes[VIVID_WEBCAM_SIZES] = {
- { 320, 180 },
- { 640, 360 },
- { 640, 480 },
- { 1280, 720 },
- { 1920, 1080 },
- { 3840, 2160 },
-};
-
-/*
- * Intervals must be in increasing order and there must be twice as many
- * elements in this array as there are in webcam_sizes.
- */
-static const struct v4l2_fract webcam_intervals[VIVID_WEBCAM_IVALS] = {
- { 1, 1 },
- { 1, 2 },
- { 1, 4 },
- { 1, 5 },
- { 1, 10 },
- { 2, 25 },
- { 1, 15 },
- { 1, 25 },
- { 1, 30 },
- { 1, 40 },
- { 1, 50 },
- { 1, 60 },
-};
-
-static int vid_cap_queue_setup(struct vb2_queue *vq,
- unsigned *nbuffers, unsigned *nplanes,
- unsigned sizes[], struct device *alloc_devs[])
-{
- struct vivid_dev *dev = vb2_get_drv_priv(vq);
- unsigned buffers = tpg_g_buffers(&dev->tpg);
- unsigned h = dev->fmt_cap_rect.height;
- unsigned p;
-
- if (dev->field_cap == V4L2_FIELD_ALTERNATE) {
- /*
- * You cannot use read() with FIELD_ALTERNATE since the field
- * information (TOP/BOTTOM) cannot be passed back to the user.
- */
- if (vb2_fileio_is_active(vq))
- return -EINVAL;
- }
-
- if (dev->queue_setup_error) {
- /*
- * Error injection: test what happens if queue_setup() returns
- * an error.
- */
- dev->queue_setup_error = false;
- return -EINVAL;
- }
- if (*nplanes) {
- /*
- * Check if the number of requested planes match
- * the number of buffers in the current format. You can't mix that.
- */
- if (*nplanes != buffers)
- return -EINVAL;
- for (p = 0; p < buffers; p++) {
- if (sizes[p] < tpg_g_line_width(&dev->tpg, p) * h +
- dev->fmt_cap->data_offset[p])
- return -EINVAL;
- }
- } else {
- for (p = 0; p < buffers; p++)
- sizes[p] = (tpg_g_line_width(&dev->tpg, p) * h) /
- dev->fmt_cap->vdownsampling[p] +
- dev->fmt_cap->data_offset[p];
- }
-
- if (vq->num_buffers + *nbuffers < 2)
- *nbuffers = 2 - vq->num_buffers;
-
- *nplanes = buffers;
-
- dprintk(dev, 1, "%s: count=%d\n", __func__, *nbuffers);
- for (p = 0; p < buffers; p++)
- dprintk(dev, 1, "%s: size[%u]=%u\n", __func__, p, sizes[p]);
-
- return 0;
-}
-
-static int vid_cap_buf_prepare(struct vb2_buffer *vb)
-{
- struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
- unsigned long size;
- unsigned buffers = tpg_g_buffers(&dev->tpg);
- unsigned p;
-
- dprintk(dev, 1, "%s\n", __func__);
-
- if (WARN_ON(NULL == dev->fmt_cap))
- return -EINVAL;
-
- if (dev->buf_prepare_error) {
- /*
- * Error injection: test what happens if buf_prepare() returns
- * an error.
- */
- dev->buf_prepare_error = false;
- return -EINVAL;
- }
- for (p = 0; p < buffers; p++) {
- size = (tpg_g_line_width(&dev->tpg, p) *
- dev->fmt_cap_rect.height) /
- dev->fmt_cap->vdownsampling[p] +
- dev->fmt_cap->data_offset[p];
-
- if (vb2_plane_size(vb, p) < size) {
- dprintk(dev, 1, "%s data will not fit into plane %u (%lu < %lu)\n",
- __func__, p, vb2_plane_size(vb, p), size);
- return -EINVAL;
- }
-
- vb2_set_plane_payload(vb, p, size);
- vb->planes[p].data_offset = dev->fmt_cap->data_offset[p];
- }
-
- return 0;
-}
-
-static void vid_cap_buf_finish(struct vb2_buffer *vb)
-{
- struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
- struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
- struct v4l2_timecode *tc = &vbuf->timecode;
- unsigned fps = 25;
- unsigned seq = vbuf->sequence;
-
- if (!vivid_is_sdtv_cap(dev))
- return;
-
- /*
- * Set the timecode. Rarely used, so it is interesting to
- * test this.
- */
- vbuf->flags |= V4L2_BUF_FLAG_TIMECODE;
- if (dev->std_cap[dev->input] & V4L2_STD_525_60)
- fps = 30;
- tc->type = (fps == 30) ? V4L2_TC_TYPE_30FPS : V4L2_TC_TYPE_25FPS;
- tc->flags = 0;
- tc->frames = seq % fps;
- tc->seconds = (seq / fps) % 60;
- tc->minutes = (seq / (60 * fps)) % 60;
- tc->hours = (seq / (60 * 60 * fps)) % 24;
-}
-
-static void vid_cap_buf_queue(struct vb2_buffer *vb)
-{
- struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
- struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
- struct vivid_buffer *buf = container_of(vbuf, struct vivid_buffer, vb);
-
- dprintk(dev, 1, "%s\n", __func__);
-
- spin_lock(&dev->slock);
- list_add_tail(&buf->list, &dev->vid_cap_active);
- spin_unlock(&dev->slock);
-}
-
-static int vid_cap_start_streaming(struct vb2_queue *vq, unsigned count)
-{
- struct vivid_dev *dev = vb2_get_drv_priv(vq);
- unsigned i;
- int err;
-
- if (vb2_is_streaming(&dev->vb_vid_out_q))
- dev->can_loop_video = vivid_vid_can_loop(dev);
-
- dev->vid_cap_seq_count = 0;
- dprintk(dev, 1, "%s\n", __func__);
- for (i = 0; i < VIDEO_MAX_FRAME; i++)
- dev->must_blank[i] = tpg_g_perc_fill(&dev->tpg) < 100;
- if (dev->start_streaming_error) {
- dev->start_streaming_error = false;
- err = -EINVAL;
- } else {
- err = vivid_start_generating_vid_cap(dev, &dev->vid_cap_streaming);
- }
- if (err) {
- struct vivid_buffer *buf, *tmp;
-
- list_for_each_entry_safe(buf, tmp, &dev->vid_cap_active, list) {
- list_del(&buf->list);
- vb2_buffer_done(&buf->vb.vb2_buf,
- VB2_BUF_STATE_QUEUED);
- }
- }
- return err;
-}
-
-/* abort streaming and wait for last buffer */
-static void vid_cap_stop_streaming(struct vb2_queue *vq)
-{
- struct vivid_dev *dev = vb2_get_drv_priv(vq);
-
- dprintk(dev, 1, "%s\n", __func__);
- vivid_stop_generating_vid_cap(dev, &dev->vid_cap_streaming);
- dev->can_loop_video = false;
-}
-
-static void vid_cap_buf_request_complete(struct vb2_buffer *vb)
-{
- struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
-
- v4l2_ctrl_request_complete(vb->req_obj.req, &dev->ctrl_hdl_vid_cap);
-}
-
-const struct vb2_ops vivid_vid_cap_qops = {
- .queue_setup = vid_cap_queue_setup,
- .buf_prepare = vid_cap_buf_prepare,
- .buf_finish = vid_cap_buf_finish,
- .buf_queue = vid_cap_buf_queue,
- .start_streaming = vid_cap_start_streaming,
- .stop_streaming = vid_cap_stop_streaming,
- .buf_request_complete = vid_cap_buf_request_complete,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
-};
-
-/*
- * Determine the 'picture' quality based on the current TV frequency: either
- * COLOR for a good 'signal', GRAY (grayscale picture) for a slightly off
- * signal or NOISE for no signal.
- */
-void vivid_update_quality(struct vivid_dev *dev)
-{
- unsigned freq_modulus;
-
- if (dev->loop_video && (vivid_is_svid_cap(dev) || vivid_is_hdmi_cap(dev))) {
- /*
- * The 'noise' will only be replaced by the actual video
- * if the output video matches the input video settings.
- */
- tpg_s_quality(&dev->tpg, TPG_QUAL_NOISE, 0);
- return;
- }
- if (vivid_is_hdmi_cap(dev) &&
- VIVID_INVALID_SIGNAL(dev->dv_timings_signal_mode[dev->input])) {
- tpg_s_quality(&dev->tpg, TPG_QUAL_NOISE, 0);
- return;
- }
- if (vivid_is_sdtv_cap(dev) &&
- VIVID_INVALID_SIGNAL(dev->std_signal_mode[dev->input])) {
- tpg_s_quality(&dev->tpg, TPG_QUAL_NOISE, 0);
- return;
- }
- if (!vivid_is_tv_cap(dev)) {
- tpg_s_quality(&dev->tpg, TPG_QUAL_COLOR, 0);
- return;
- }
-
- /*
- * There is a fake channel every 6 MHz at 49.25, 55.25, etc.
- * From +/- 0.25 MHz around the channel there is color, and from
- * +/- 1 MHz there is grayscale (chroma is lost).
- * Everywhere else it is just noise.
- */
- freq_modulus = (dev->tv_freq - 676 /* (43.25-1) * 16 */) % (6 * 16);
- if (freq_modulus > 2 * 16) {
- tpg_s_quality(&dev->tpg, TPG_QUAL_NOISE,
- next_pseudo_random32(dev->tv_freq ^ 0x55) & 0x3f);
- return;
- }
- if (freq_modulus < 12 /*0.75 * 16*/ || freq_modulus > 20 /*1.25 * 16*/)
- tpg_s_quality(&dev->tpg, TPG_QUAL_GRAY, 0);
- else
- tpg_s_quality(&dev->tpg, TPG_QUAL_COLOR, 0);
-}
-
-/*
- * Get the current picture quality and the associated afc value.
- */
-static enum tpg_quality vivid_get_quality(struct vivid_dev *dev, s32 *afc)
-{
- unsigned freq_modulus;
-
- if (afc)
- *afc = 0;
- if (tpg_g_quality(&dev->tpg) == TPG_QUAL_COLOR ||
- tpg_g_quality(&dev->tpg) == TPG_QUAL_NOISE)
- return tpg_g_quality(&dev->tpg);
-
- /*
- * There is a fake channel every 6 MHz at 49.25, 55.25, etc.
- * From +/- 0.25 MHz around the channel there is color, and from
- * +/- 1 MHz there is grayscale (chroma is lost).
- * Everywhere else it is just gray.
- */
- freq_modulus = (dev->tv_freq - 676 /* (43.25-1) * 16 */) % (6 * 16);
- if (afc)
- *afc = freq_modulus - 1 * 16;
- return TPG_QUAL_GRAY;
-}
-
-enum tpg_video_aspect vivid_get_video_aspect(const struct vivid_dev *dev)
-{
- if (vivid_is_sdtv_cap(dev))
- return dev->std_aspect_ratio[dev->input];
-
- if (vivid_is_hdmi_cap(dev))
- return dev->dv_timings_aspect_ratio[dev->input];
-
- return TPG_VIDEO_ASPECT_IMAGE;
-}
-
-static enum tpg_pixel_aspect vivid_get_pixel_aspect(const struct vivid_dev *dev)
-{
- if (vivid_is_sdtv_cap(dev))
- return (dev->std_cap[dev->input] & V4L2_STD_525_60) ?
- TPG_PIXEL_ASPECT_NTSC : TPG_PIXEL_ASPECT_PAL;
-
- if (vivid_is_hdmi_cap(dev) &&
- dev->src_rect.width == 720 && dev->src_rect.height <= 576)
- return dev->src_rect.height == 480 ?
- TPG_PIXEL_ASPECT_NTSC : TPG_PIXEL_ASPECT_PAL;
-
- return TPG_PIXEL_ASPECT_SQUARE;
-}
-
-/*
- * Called whenever the format has to be reset which can occur when
- * changing inputs, standard, timings, etc.
- */
-void vivid_update_format_cap(struct vivid_dev *dev, bool keep_controls)
-{
- struct v4l2_bt_timings *bt = &dev->dv_timings_cap[dev->input].bt;
- unsigned size;
- u64 pixelclock;
-
- switch (dev->input_type[dev->input]) {
- case WEBCAM:
- default:
- dev->src_rect.width = webcam_sizes[dev->webcam_size_idx].width;
- dev->src_rect.height = webcam_sizes[dev->webcam_size_idx].height;
- dev->timeperframe_vid_cap = webcam_intervals[dev->webcam_ival_idx];
- dev->field_cap = V4L2_FIELD_NONE;
- tpg_s_rgb_range(&dev->tpg, V4L2_DV_RGB_RANGE_AUTO);
- break;
- case TV:
- case SVID:
- dev->field_cap = dev->tv_field_cap;
- dev->src_rect.width = 720;
- if (dev->std_cap[dev->input] & V4L2_STD_525_60) {
- dev->src_rect.height = 480;
- dev->timeperframe_vid_cap = (struct v4l2_fract) { 1001, 30000 };
- dev->service_set_cap = V4L2_SLICED_CAPTION_525;
- } else {
- dev->src_rect.height = 576;
- dev->timeperframe_vid_cap = (struct v4l2_fract) { 1000, 25000 };
- dev->service_set_cap = V4L2_SLICED_WSS_625 | V4L2_SLICED_TELETEXT_B;
- }
- tpg_s_rgb_range(&dev->tpg, V4L2_DV_RGB_RANGE_AUTO);
- break;
- case HDMI:
- dev->src_rect.width = bt->width;
- dev->src_rect.height = bt->height;
- size = V4L2_DV_BT_FRAME_WIDTH(bt) * V4L2_DV_BT_FRAME_HEIGHT(bt);
- if (dev->reduced_fps && can_reduce_fps(bt)) {
- pixelclock = div_u64(bt->pixelclock * 1000, 1001);
- bt->flags |= V4L2_DV_FL_REDUCED_FPS;
- } else {
- pixelclock = bt->pixelclock;
- bt->flags &= ~V4L2_DV_FL_REDUCED_FPS;
- }
- dev->timeperframe_vid_cap = (struct v4l2_fract) {
- size / 100, (u32)pixelclock / 100
- };
- if (bt->interlaced)
- dev->field_cap = V4L2_FIELD_ALTERNATE;
- else
- dev->field_cap = V4L2_FIELD_NONE;
-
- /*
- * We can be called from within s_ctrl, in that case we can't
- * set/get controls. Luckily we don't need to in that case.
- */
- if (keep_controls || !dev->colorspace)
- break;
- if (bt->flags & V4L2_DV_FL_IS_CE_VIDEO) {
- if (bt->width == 720 && bt->height <= 576)
- v4l2_ctrl_s_ctrl(dev->colorspace, VIVID_CS_170M);
- else
- v4l2_ctrl_s_ctrl(dev->colorspace, VIVID_CS_709);
- v4l2_ctrl_s_ctrl(dev->real_rgb_range_cap, 1);
- } else {
- v4l2_ctrl_s_ctrl(dev->colorspace, VIVID_CS_SRGB);
- v4l2_ctrl_s_ctrl(dev->real_rgb_range_cap, 0);
- }
- tpg_s_rgb_range(&dev->tpg, v4l2_ctrl_g_ctrl(dev->rgb_range_cap));
- break;
- }
- vfree(dev->bitmap_cap);
- dev->bitmap_cap = NULL;
- vivid_update_quality(dev);
- tpg_reset_source(&dev->tpg, dev->src_rect.width, dev->src_rect.height, dev->field_cap);
- dev->crop_cap = dev->src_rect;
- dev->crop_bounds_cap = dev->src_rect;
- dev->compose_cap = dev->crop_cap;
- if (V4L2_FIELD_HAS_T_OR_B(dev->field_cap))
- dev->compose_cap.height /= 2;
- dev->fmt_cap_rect = dev->compose_cap;
- tpg_s_video_aspect(&dev->tpg, vivid_get_video_aspect(dev));
- tpg_s_pixel_aspect(&dev->tpg, vivid_get_pixel_aspect(dev));
- tpg_update_mv_step(&dev->tpg);
-}
-
-/* Map the field to something that is valid for the current input */
-static enum v4l2_field vivid_field_cap(struct vivid_dev *dev, enum v4l2_field field)
-{
- if (vivid_is_sdtv_cap(dev)) {
- switch (field) {
- case V4L2_FIELD_INTERLACED_TB:
- case V4L2_FIELD_INTERLACED_BT:
- case V4L2_FIELD_SEQ_TB:
- case V4L2_FIELD_SEQ_BT:
- case V4L2_FIELD_TOP:
- case V4L2_FIELD_BOTTOM:
- case V4L2_FIELD_ALTERNATE:
- return field;
- case V4L2_FIELD_INTERLACED:
- default:
- return V4L2_FIELD_INTERLACED;
- }
- }
- if (vivid_is_hdmi_cap(dev))
- return dev->dv_timings_cap[dev->input].bt.interlaced ?
- V4L2_FIELD_ALTERNATE : V4L2_FIELD_NONE;
- return V4L2_FIELD_NONE;
-}
-
-static unsigned vivid_colorspace_cap(struct vivid_dev *dev)
-{
- if (!dev->loop_video || vivid_is_webcam(dev) || vivid_is_tv_cap(dev))
- return tpg_g_colorspace(&dev->tpg);
- return dev->colorspace_out;
-}
-
-static unsigned vivid_xfer_func_cap(struct vivid_dev *dev)
-{
- if (!dev->loop_video || vivid_is_webcam(dev) || vivid_is_tv_cap(dev))
- return tpg_g_xfer_func(&dev->tpg);
- return dev->xfer_func_out;
-}
-
-static unsigned vivid_ycbcr_enc_cap(struct vivid_dev *dev)
-{
- if (!dev->loop_video || vivid_is_webcam(dev) || vivid_is_tv_cap(dev))
- return tpg_g_ycbcr_enc(&dev->tpg);
- return dev->ycbcr_enc_out;
-}
-
-static unsigned int vivid_hsv_enc_cap(struct vivid_dev *dev)
-{
- if (!dev->loop_video || vivid_is_webcam(dev) || vivid_is_tv_cap(dev))
- return tpg_g_hsv_enc(&dev->tpg);
- return dev->hsv_enc_out;
-}
-
-static unsigned vivid_quantization_cap(struct vivid_dev *dev)
-{
- if (!dev->loop_video || vivid_is_webcam(dev) || vivid_is_tv_cap(dev))
- return tpg_g_quantization(&dev->tpg);
- return dev->quantization_out;
-}
-
-int vivid_g_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct vivid_dev *dev = video_drvdata(file);
- struct v4l2_pix_format_mplane *mp = &f->fmt.pix_mp;
- unsigned p;
-
- mp->width = dev->fmt_cap_rect.width;
- mp->height = dev->fmt_cap_rect.height;
- mp->field = dev->field_cap;
- mp->pixelformat = dev->fmt_cap->fourcc;
- mp->colorspace = vivid_colorspace_cap(dev);
- mp->xfer_func = vivid_xfer_func_cap(dev);
- if (dev->fmt_cap->color_enc == TGP_COLOR_ENC_HSV)
- mp->hsv_enc = vivid_hsv_enc_cap(dev);
- else
- mp->ycbcr_enc = vivid_ycbcr_enc_cap(dev);
- mp->quantization = vivid_quantization_cap(dev);
- mp->num_planes = dev->fmt_cap->buffers;
- for (p = 0; p < mp->num_planes; p++) {
- mp->plane_fmt[p].bytesperline = tpg_g_bytesperline(&dev->tpg, p);
- mp->plane_fmt[p].sizeimage =
- (tpg_g_line_width(&dev->tpg, p) * mp->height) /
- dev->fmt_cap->vdownsampling[p] +
- dev->fmt_cap->data_offset[p];
- }
- return 0;
-}
-
-int vivid_try_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct v4l2_pix_format_mplane *mp = &f->fmt.pix_mp;
- struct v4l2_plane_pix_format *pfmt = mp->plane_fmt;
- struct vivid_dev *dev = video_drvdata(file);
- const struct vivid_fmt *fmt;
- unsigned bytesperline, max_bpl;
- unsigned factor = 1;
- unsigned w, h;
- unsigned p;
-
- fmt = vivid_get_format(dev, mp->pixelformat);
- if (!fmt) {
- dprintk(dev, 1, "Fourcc format (0x%08x) unknown.\n",
- mp->pixelformat);
- mp->pixelformat = V4L2_PIX_FMT_YUYV;
- fmt = vivid_get_format(dev, mp->pixelformat);
- }
-
- mp->field = vivid_field_cap(dev, mp->field);
- if (vivid_is_webcam(dev)) {
- const struct v4l2_frmsize_discrete *sz =
- v4l2_find_nearest_size(webcam_sizes,
- VIVID_WEBCAM_SIZES, width,
- height, mp->width, mp->height);
-
- w = sz->width;
- h = sz->height;
- } else if (vivid_is_sdtv_cap(dev)) {
- w = 720;
- h = (dev->std_cap[dev->input] & V4L2_STD_525_60) ? 480 : 576;
- } else {
- w = dev->src_rect.width;
- h = dev->src_rect.height;
- }
- if (V4L2_FIELD_HAS_T_OR_B(mp->field))
- factor = 2;
- if (vivid_is_webcam(dev) ||
- (!dev->has_scaler_cap && !dev->has_crop_cap && !dev->has_compose_cap)) {
- mp->width = w;
- mp->height = h / factor;
- } else {
- struct v4l2_rect r = { 0, 0, mp->width, mp->height * factor };
-
- v4l2_rect_set_min_size(&r, &vivid_min_rect);
- v4l2_rect_set_max_size(&r, &vivid_max_rect);
- if (dev->has_scaler_cap && !dev->has_compose_cap) {
- struct v4l2_rect max_r = { 0, 0, MAX_ZOOM * w, MAX_ZOOM * h };
-
- v4l2_rect_set_max_size(&r, &max_r);
- } else if (!dev->has_scaler_cap && dev->has_crop_cap && !dev->has_compose_cap) {
- v4l2_rect_set_max_size(&r, &dev->src_rect);
- } else if (!dev->has_scaler_cap && !dev->has_crop_cap) {
- v4l2_rect_set_min_size(&r, &dev->src_rect);
- }
- mp->width = r.width;
- mp->height = r.height / factor;
- }
-
- /* This driver supports custom bytesperline values */
-
- mp->num_planes = fmt->buffers;
- for (p = 0; p < fmt->buffers; p++) {
- /* Calculate the minimum supported bytesperline value */
- bytesperline = (mp->width * fmt->bit_depth[p]) >> 3;
- /* Calculate the maximum supported bytesperline value */
- max_bpl = (MAX_ZOOM * MAX_WIDTH * fmt->bit_depth[p]) >> 3;
-
- if (pfmt[p].bytesperline > max_bpl)
- pfmt[p].bytesperline = max_bpl;
- if (pfmt[p].bytesperline < bytesperline)
- pfmt[p].bytesperline = bytesperline;
-
- pfmt[p].sizeimage = (pfmt[p].bytesperline * mp->height) /
- fmt->vdownsampling[p] + fmt->data_offset[p];
-
- memset(pfmt[p].reserved, 0, sizeof(pfmt[p].reserved));
- }
- for (p = fmt->buffers; p < fmt->planes; p++)
- pfmt[0].sizeimage += (pfmt[0].bytesperline * mp->height *
- (fmt->bit_depth[p] / fmt->vdownsampling[p])) /
- (fmt->bit_depth[0] / fmt->vdownsampling[0]);
-
- mp->colorspace = vivid_colorspace_cap(dev);
- if (fmt->color_enc == TGP_COLOR_ENC_HSV)
- mp->hsv_enc = vivid_hsv_enc_cap(dev);
- else
- mp->ycbcr_enc = vivid_ycbcr_enc_cap(dev);
- mp->xfer_func = vivid_xfer_func_cap(dev);
- mp->quantization = vivid_quantization_cap(dev);
- memset(mp->reserved, 0, sizeof(mp->reserved));
- return 0;
-}
-
-int vivid_s_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct v4l2_pix_format_mplane *mp = &f->fmt.pix_mp;
- struct vivid_dev *dev = video_drvdata(file);
- struct v4l2_rect *crop = &dev->crop_cap;
- struct v4l2_rect *compose = &dev->compose_cap;
- struct vb2_queue *q = &dev->vb_vid_cap_q;
- int ret = vivid_try_fmt_vid_cap(file, priv, f);
- unsigned factor = 1;
- unsigned p;
- unsigned i;
-
- if (ret < 0)
- return ret;
-
- if (vb2_is_busy(q)) {
- dprintk(dev, 1, "%s device busy\n", __func__);
- return -EBUSY;
- }
-
- if (dev->overlay_cap_owner && dev->fb_cap.fmt.pixelformat != mp->pixelformat) {
- dprintk(dev, 1, "overlay is active, can't change pixelformat\n");
- return -EBUSY;
- }
-
- dev->fmt_cap = vivid_get_format(dev, mp->pixelformat);
- if (V4L2_FIELD_HAS_T_OR_B(mp->field))
- factor = 2;
-
- /* Note: the webcam input doesn't support scaling, cropping or composing */
-
- if (!vivid_is_webcam(dev) &&
- (dev->has_scaler_cap || dev->has_crop_cap || dev->has_compose_cap)) {
- struct v4l2_rect r = { 0, 0, mp->width, mp->height };
-
- if (dev->has_scaler_cap) {
- if (dev->has_compose_cap)
- v4l2_rect_map_inside(compose, &r);
- else
- *compose = r;
- if (dev->has_crop_cap && !dev->has_compose_cap) {
- struct v4l2_rect min_r = {
- 0, 0,
- r.width / MAX_ZOOM,
- factor * r.height / MAX_ZOOM
- };
- struct v4l2_rect max_r = {
- 0, 0,
- r.width * MAX_ZOOM,
- factor * r.height * MAX_ZOOM
- };
-
- v4l2_rect_set_min_size(crop, &min_r);
- v4l2_rect_set_max_size(crop, &max_r);
- v4l2_rect_map_inside(crop, &dev->crop_bounds_cap);
- } else if (dev->has_crop_cap) {
- struct v4l2_rect min_r = {
- 0, 0,
- compose->width / MAX_ZOOM,
- factor * compose->height / MAX_ZOOM
- };
- struct v4l2_rect max_r = {
- 0, 0,
- compose->width * MAX_ZOOM,
- factor * compose->height * MAX_ZOOM
- };
-
- v4l2_rect_set_min_size(crop, &min_r);
- v4l2_rect_set_max_size(crop, &max_r);
- v4l2_rect_map_inside(crop, &dev->crop_bounds_cap);
- }
- } else if (dev->has_crop_cap && !dev->has_compose_cap) {
- r.height *= factor;
- v4l2_rect_set_size_to(crop, &r);
- v4l2_rect_map_inside(crop, &dev->crop_bounds_cap);
- r = *crop;
- r.height /= factor;
- v4l2_rect_set_size_to(compose, &r);
- } else if (!dev->has_crop_cap) {
- v4l2_rect_map_inside(compose, &r);
- } else {
- r.height *= factor;
- v4l2_rect_set_max_size(crop, &r);
- v4l2_rect_map_inside(crop, &dev->crop_bounds_cap);
- compose->top *= factor;
- compose->height *= factor;
- v4l2_rect_set_size_to(compose, crop);
- v4l2_rect_map_inside(compose, &r);
- compose->top /= factor;
- compose->height /= factor;
- }
- } else if (vivid_is_webcam(dev)) {
- /* Guaranteed to be a match */
- for (i = 0; i < ARRAY_SIZE(webcam_sizes); i++)
- if (webcam_sizes[i].width == mp->width &&
- webcam_sizes[i].height == mp->height)
- break;
- dev->webcam_size_idx = i;
- if (dev->webcam_ival_idx >= 2 * (VIVID_WEBCAM_SIZES - i))
- dev->webcam_ival_idx = 2 * (VIVID_WEBCAM_SIZES - i) - 1;
- vivid_update_format_cap(dev, false);
- } else {
- struct v4l2_rect r = { 0, 0, mp->width, mp->height };
-
- v4l2_rect_set_size_to(compose, &r);
- r.height *= factor;
- v4l2_rect_set_size_to(crop, &r);
- }
-
- dev->fmt_cap_rect.width = mp->width;
- dev->fmt_cap_rect.height = mp->height;
- tpg_s_buf_height(&dev->tpg, mp->height);
- tpg_s_fourcc(&dev->tpg, dev->fmt_cap->fourcc);
- for (p = 0; p < tpg_g_buffers(&dev->tpg); p++)
- tpg_s_bytesperline(&dev->tpg, p, mp->plane_fmt[p].bytesperline);
- dev->field_cap = mp->field;
- if (dev->field_cap == V4L2_FIELD_ALTERNATE)
- tpg_s_field(&dev->tpg, V4L2_FIELD_TOP, true);
- else
- tpg_s_field(&dev->tpg, dev->field_cap, false);
- tpg_s_crop_compose(&dev->tpg, &dev->crop_cap, &dev->compose_cap);
- if (vivid_is_sdtv_cap(dev))
- dev->tv_field_cap = mp->field;
- tpg_update_mv_step(&dev->tpg);
- return 0;
-}
-
-int vidioc_g_fmt_vid_cap_mplane(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct vivid_dev *dev = video_drvdata(file);
-
- if (!dev->multiplanar)
- return -ENOTTY;
- return vivid_g_fmt_vid_cap(file, priv, f);
-}
-
-int vidioc_try_fmt_vid_cap_mplane(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct vivid_dev *dev = video_drvdata(file);
-
- if (!dev->multiplanar)
- return -ENOTTY;
- return vivid_try_fmt_vid_cap(file, priv, f);
-}
-
-int vidioc_s_fmt_vid_cap_mplane(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct vivid_dev *dev = video_drvdata(file);
-
- if (!dev->multiplanar)
- return -ENOTTY;
- return vivid_s_fmt_vid_cap(file, priv, f);
-}
-
-int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct vivid_dev *dev = video_drvdata(file);
-
- if (dev->multiplanar)
- return -ENOTTY;
- return fmt_sp2mp_func(file, priv, f, vivid_g_fmt_vid_cap);
-}
-
-int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct vivid_dev *dev = video_drvdata(file);
-
- if (dev->multiplanar)
- return -ENOTTY;
- return fmt_sp2mp_func(file, priv, f, vivid_try_fmt_vid_cap);
-}
-
-int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct vivid_dev *dev = video_drvdata(file);
-
- if (dev->multiplanar)
- return -ENOTTY;
- return fmt_sp2mp_func(file, priv, f, vivid_s_fmt_vid_cap);
-}
-
-int vivid_vid_cap_g_selection(struct file *file, void *priv,
- struct v4l2_selection *sel)
-{
- struct vivid_dev *dev = video_drvdata(file);
-
- if (!dev->has_crop_cap && !dev->has_compose_cap)
- return -ENOTTY;
- if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
- if (vivid_is_webcam(dev))
- return -ENODATA;
-
- sel->r.left = sel->r.top = 0;
- switch (sel->target) {
- case V4L2_SEL_TGT_CROP:
- if (!dev->has_crop_cap)
- return -EINVAL;
- sel->r = dev->crop_cap;
- break;
- case V4L2_SEL_TGT_CROP_DEFAULT:
- case V4L2_SEL_TGT_CROP_BOUNDS:
- if (!dev->has_crop_cap)
- return -EINVAL;
- sel->r = dev->src_rect;
- break;
- case V4L2_SEL_TGT_COMPOSE_BOUNDS:
- if (!dev->has_compose_cap)
- return -EINVAL;
- sel->r = vivid_max_rect;
- break;
- case V4L2_SEL_TGT_COMPOSE:
- if (!dev->has_compose_cap)
- return -EINVAL;
- sel->r = dev->compose_cap;
- break;
- case V4L2_SEL_TGT_COMPOSE_DEFAULT:
- if (!dev->has_compose_cap)
- return -EINVAL;
- sel->r = dev->fmt_cap_rect;
- break;
- default:
- return -EINVAL;
- }
- return 0;
-}
-
-int vivid_vid_cap_s_selection(struct file *file, void *fh, struct v4l2_selection *s)
-{
- struct vivid_dev *dev = video_drvdata(file);
- struct v4l2_rect *crop = &dev->crop_cap;
- struct v4l2_rect *compose = &dev->compose_cap;
- unsigned factor = V4L2_FIELD_HAS_T_OR_B(dev->field_cap) ? 2 : 1;
- int ret;
-
- if (!dev->has_crop_cap && !dev->has_compose_cap)
- return -ENOTTY;
- if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
- if (vivid_is_webcam(dev))
- return -ENODATA;
-
- switch (s->target) {
- case V4L2_SEL_TGT_CROP:
- if (!dev->has_crop_cap)
- return -EINVAL;
- ret = vivid_vid_adjust_sel(s->flags, &s->r);
- if (ret)
- return ret;
- v4l2_rect_set_min_size(&s->r, &vivid_min_rect);
- v4l2_rect_set_max_size(&s->r, &dev->src_rect);
- v4l2_rect_map_inside(&s->r, &dev->crop_bounds_cap);
- s->r.top /= factor;
- s->r.height /= factor;
- if (dev->has_scaler_cap) {
- struct v4l2_rect fmt = dev->fmt_cap_rect;
- struct v4l2_rect max_rect = {
- 0, 0,
- s->r.width * MAX_ZOOM,
- s->r.height * MAX_ZOOM
- };
- struct v4l2_rect min_rect = {
- 0, 0,
- s->r.width / MAX_ZOOM,
- s->r.height / MAX_ZOOM
- };
-
- v4l2_rect_set_min_size(&fmt, &min_rect);
- if (!dev->has_compose_cap)
- v4l2_rect_set_max_size(&fmt, &max_rect);
- if (!v4l2_rect_same_size(&dev->fmt_cap_rect, &fmt) &&
- vb2_is_busy(&dev->vb_vid_cap_q))
- return -EBUSY;
- if (dev->has_compose_cap) {
- v4l2_rect_set_min_size(compose, &min_rect);
- v4l2_rect_set_max_size(compose, &max_rect);
- }
- dev->fmt_cap_rect = fmt;
- tpg_s_buf_height(&dev->tpg, fmt.height);
- } else if (dev->has_compose_cap) {
- struct v4l2_rect fmt = dev->fmt_cap_rect;
-
- v4l2_rect_set_min_size(&fmt, &s->r);
- if (!v4l2_rect_same_size(&dev->fmt_cap_rect, &fmt) &&
- vb2_is_busy(&dev->vb_vid_cap_q))
- return -EBUSY;
- dev->fmt_cap_rect = fmt;
- tpg_s_buf_height(&dev->tpg, fmt.height);
- v4l2_rect_set_size_to(compose, &s->r);
- v4l2_rect_map_inside(compose, &dev->fmt_cap_rect);
- } else {
- if (!v4l2_rect_same_size(&s->r, &dev->fmt_cap_rect) &&
- vb2_is_busy(&dev->vb_vid_cap_q))
- return -EBUSY;
- v4l2_rect_set_size_to(&dev->fmt_cap_rect, &s->r);
- v4l2_rect_set_size_to(compose, &s->r);
- v4l2_rect_map_inside(compose, &dev->fmt_cap_rect);
- tpg_s_buf_height(&dev->tpg, dev->fmt_cap_rect.height);
- }
- s->r.top *= factor;
- s->r.height *= factor;
- *crop = s->r;
- break;
- case V4L2_SEL_TGT_COMPOSE:
- if (!dev->has_compose_cap)
- return -EINVAL;
- ret = vivid_vid_adjust_sel(s->flags, &s->r);
- if (ret)
- return ret;
- v4l2_rect_set_min_size(&s->r, &vivid_min_rect);
- v4l2_rect_set_max_size(&s->r, &dev->fmt_cap_rect);
- if (dev->has_scaler_cap) {
- struct v4l2_rect max_rect = {
- 0, 0,
- dev->src_rect.width * MAX_ZOOM,
- (dev->src_rect.height / factor) * MAX_ZOOM
- };
-
- v4l2_rect_set_max_size(&s->r, &max_rect);
- if (dev->has_crop_cap) {
- struct v4l2_rect min_rect = {
- 0, 0,
- s->r.width / MAX_ZOOM,
- (s->r.height * factor) / MAX_ZOOM
- };
- struct v4l2_rect max_rect = {
- 0, 0,
- s->r.width * MAX_ZOOM,
- (s->r.height * factor) * MAX_ZOOM
- };
-
- v4l2_rect_set_min_size(crop, &min_rect);
- v4l2_rect_set_max_size(crop, &max_rect);
- v4l2_rect_map_inside(crop, &dev->crop_bounds_cap);
- }
- } else if (dev->has_crop_cap) {
- s->r.top *= factor;
- s->r.height *= factor;
- v4l2_rect_set_max_size(&s->r, &dev->src_rect);
- v4l2_rect_set_size_to(crop, &s->r);
- v4l2_rect_map_inside(crop, &dev->crop_bounds_cap);
- s->r.top /= factor;
- s->r.height /= factor;
- } else {
- v4l2_rect_set_size_to(&s->r, &dev->src_rect);
- s->r.height /= factor;
- }
- v4l2_rect_map_inside(&s->r, &dev->fmt_cap_rect);
- if (dev->bitmap_cap && (compose->width != s->r.width ||
- compose->height != s->r.height)) {
- vfree(dev->bitmap_cap);
- dev->bitmap_cap = NULL;
- }
- *compose = s->r;
- break;
- default:
- return -EINVAL;
- }
-
- tpg_s_crop_compose(&dev->tpg, crop, compose);
- return 0;
-}
-
-int vivid_vid_cap_g_pixelaspect(struct file *file, void *priv,
- int type, struct v4l2_fract *f)
-{
- struct vivid_dev *dev = video_drvdata(file);
-
- if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
-
- switch (vivid_get_pixel_aspect(dev)) {
- case TPG_PIXEL_ASPECT_NTSC:
- f->numerator = 11;
- f->denominator = 10;
- break;
- case TPG_PIXEL_ASPECT_PAL:
- f->numerator = 54;
- f->denominator = 59;
- break;
- default:
- break;
- }
- return 0;
-}
-
-int vidioc_enum_fmt_vid_overlay(struct file *file, void *priv,
- struct v4l2_fmtdesc *f)
-{
- struct vivid_dev *dev = video_drvdata(file);
- const struct vivid_fmt *fmt;
-
- if (dev->multiplanar)
- return -ENOTTY;
-
- if (f->index >= ARRAY_SIZE(formats_ovl))
- return -EINVAL;
-
- fmt = &formats_ovl[f->index];
-
- f->pixelformat = fmt->fourcc;
- return 0;
-}
-
-int vidioc_g_fmt_vid_overlay(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct vivid_dev *dev = video_drvdata(file);
- const struct v4l2_rect *compose = &dev->compose_cap;
- struct v4l2_window *win = &f->fmt.win;
- unsigned clipcount = win->clipcount;
-
- if (dev->multiplanar)
- return -ENOTTY;
-
- win->w.top = dev->overlay_cap_top;
- win->w.left = dev->overlay_cap_left;
- win->w.width = compose->width;
- win->w.height = compose->height;
- win->field = dev->overlay_cap_field;
- win->clipcount = dev->clipcount_cap;
- if (clipcount > dev->clipcount_cap)
- clipcount = dev->clipcount_cap;
- if (dev->bitmap_cap == NULL)
- win->bitmap = NULL;
- else if (win->bitmap) {
- if (copy_to_user(win->bitmap, dev->bitmap_cap,
- ((compose->width + 7) / 8) * compose->height))
- return -EFAULT;
- }
- if (clipcount && win->clips) {
- if (copy_to_user(win->clips, dev->clips_cap,
- clipcount * sizeof(dev->clips_cap[0])))
- return -EFAULT;
- }
- return 0;
-}
-
-int vidioc_try_fmt_vid_overlay(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct vivid_dev *dev = video_drvdata(file);
- const struct v4l2_rect *compose = &dev->compose_cap;
- struct v4l2_window *win = &f->fmt.win;
- int i, j;
-
- if (dev->multiplanar)
- return -ENOTTY;
-
- win->w.left = clamp_t(int, win->w.left,
- -dev->fb_cap.fmt.width, dev->fb_cap.fmt.width);
- win->w.top = clamp_t(int, win->w.top,
- -dev->fb_cap.fmt.height, dev->fb_cap.fmt.height);
- win->w.width = compose->width;
- win->w.height = compose->height;
- if (win->field != V4L2_FIELD_BOTTOM && win->field != V4L2_FIELD_TOP)
- win->field = V4L2_FIELD_ANY;
- win->chromakey = 0;
- win->global_alpha = 0;
- if (win->clipcount && !win->clips)
- win->clipcount = 0;
- if (win->clipcount > MAX_CLIPS)
- win->clipcount = MAX_CLIPS;
- if (win->clipcount) {
- if (copy_from_user(dev->try_clips_cap, win->clips,
- win->clipcount * sizeof(dev->clips_cap[0])))
- return -EFAULT;
- for (i = 0; i < win->clipcount; i++) {
- struct v4l2_rect *r = &dev->try_clips_cap[i].c;
-
- r->top = clamp_t(s32, r->top, 0, dev->fb_cap.fmt.height - 1);
- r->height = clamp_t(s32, r->height, 1, dev->fb_cap.fmt.height - r->top);
- r->left = clamp_t(u32, r->left, 0, dev->fb_cap.fmt.width - 1);
- r->width = clamp_t(u32, r->width, 1, dev->fb_cap.fmt.width - r->left);
- }
- /*
- * Yeah, so sue me, it's an O(n^2) algorithm. But n is a small
- * number and it's typically a one-time deal.
- */
- for (i = 0; i < win->clipcount - 1; i++) {
- struct v4l2_rect *r1 = &dev->try_clips_cap[i].c;
-
- for (j = i + 1; j < win->clipcount; j++) {
- struct v4l2_rect *r2 = &dev->try_clips_cap[j].c;
-
- if (v4l2_rect_overlap(r1, r2))
- return -EINVAL;
- }
- }
- if (copy_to_user(win->clips, dev->try_clips_cap,
- win->clipcount * sizeof(dev->clips_cap[0])))
- return -EFAULT;
- }
- return 0;
-}
-
-int vidioc_s_fmt_vid_overlay(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct vivid_dev *dev = video_drvdata(file);
- const struct v4l2_rect *compose = &dev->compose_cap;
- struct v4l2_window *win = &f->fmt.win;
- int ret = vidioc_try_fmt_vid_overlay(file, priv, f);
- unsigned bitmap_size = ((compose->width + 7) / 8) * compose->height;
- unsigned clips_size = win->clipcount * sizeof(dev->clips_cap[0]);
- void *new_bitmap = NULL;
-
- if (ret)
- return ret;
-
- if (win->bitmap) {
- new_bitmap = vzalloc(bitmap_size);
-
- if (new_bitmap == NULL)
- return -ENOMEM;
- if (copy_from_user(new_bitmap, win->bitmap, bitmap_size)) {
- vfree(new_bitmap);
- return -EFAULT;
- }
- }
-
- dev->overlay_cap_top = win->w.top;
- dev->overlay_cap_left = win->w.left;
- dev->overlay_cap_field = win->field;
- vfree(dev->bitmap_cap);
- dev->bitmap_cap = new_bitmap;
- dev->clipcount_cap = win->clipcount;
- if (dev->clipcount_cap)
- memcpy(dev->clips_cap, dev->try_clips_cap, clips_size);
- return 0;
-}
-
-int vivid_vid_cap_overlay(struct file *file, void *fh, unsigned i)
-{
- struct vivid_dev *dev = video_drvdata(file);
-
- if (dev->multiplanar)
- return -ENOTTY;
-
- if (i && dev->fb_vbase_cap == NULL)
- return -EINVAL;
-
- if (i && dev->fb_cap.fmt.pixelformat != dev->fmt_cap->fourcc) {
- dprintk(dev, 1, "mismatch between overlay and video capture pixelformats\n");
- return -EINVAL;
- }
-
- if (dev->overlay_cap_owner && dev->overlay_cap_owner != fh)
- return -EBUSY;
- dev->overlay_cap_owner = i ? fh : NULL;
- return 0;
-}
-
-int vivid_vid_cap_g_fbuf(struct file *file, void *fh,
- struct v4l2_framebuffer *a)
-{
- struct vivid_dev *dev = video_drvdata(file);
-
- if (dev->multiplanar)
- return -ENOTTY;
-
- *a = dev->fb_cap;
- a->capability = V4L2_FBUF_CAP_BITMAP_CLIPPING |
- V4L2_FBUF_CAP_LIST_CLIPPING;
- a->flags = V4L2_FBUF_FLAG_PRIMARY;
- a->fmt.field = V4L2_FIELD_NONE;
- a->fmt.colorspace = V4L2_COLORSPACE_SRGB;
- a->fmt.priv = 0;
- return 0;
-}
-
-int vivid_vid_cap_s_fbuf(struct file *file, void *fh,
- const struct v4l2_framebuffer *a)
-{
- struct vivid_dev *dev = video_drvdata(file);
- const struct vivid_fmt *fmt;
-
- if (dev->multiplanar)
- return -ENOTTY;
-
- if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RAWIO))
- return -EPERM;
-
- if (dev->overlay_cap_owner)
- return -EBUSY;
-
- if (a->base == NULL) {
- dev->fb_cap.base = NULL;
- dev->fb_vbase_cap = NULL;
- return 0;
- }
-
- if (a->fmt.width < 48 || a->fmt.height < 32)
- return -EINVAL;
- fmt = vivid_get_format(dev, a->fmt.pixelformat);
- if (!fmt || !fmt->can_do_overlay)
- return -EINVAL;
- if (a->fmt.bytesperline < (a->fmt.width * fmt->bit_depth[0]) / 8)
- return -EINVAL;
- if (a->fmt.height * a->fmt.bytesperline < a->fmt.sizeimage)
- return -EINVAL;
-
- dev->fb_vbase_cap = phys_to_virt((unsigned long)a->base);
- dev->fb_cap = *a;
- dev->overlay_cap_left = clamp_t(int, dev->overlay_cap_left,
- -dev->fb_cap.fmt.width, dev->fb_cap.fmt.width);
- dev->overlay_cap_top = clamp_t(int, dev->overlay_cap_top,
- -dev->fb_cap.fmt.height, dev->fb_cap.fmt.height);
- return 0;
-}
-
-static const struct v4l2_audio vivid_audio_inputs[] = {
- { 0, "TV", V4L2_AUDCAP_STEREO },
- { 1, "Line-In", V4L2_AUDCAP_STEREO },
-};
-
-int vidioc_enum_input(struct file *file, void *priv,
- struct v4l2_input *inp)
-{
- struct vivid_dev *dev = video_drvdata(file);
-
- if (inp->index >= dev->num_inputs)
- return -EINVAL;
-
- inp->type = V4L2_INPUT_TYPE_CAMERA;
- switch (dev->input_type[inp->index]) {
- case WEBCAM:
- snprintf(inp->name, sizeof(inp->name), "Webcam %u",
- dev->input_name_counter[inp->index]);
- inp->capabilities = 0;
- break;
- case TV:
- snprintf(inp->name, sizeof(inp->name), "TV %u",
- dev->input_name_counter[inp->index]);
- inp->type = V4L2_INPUT_TYPE_TUNER;
- inp->std = V4L2_STD_ALL;
- if (dev->has_audio_inputs)
- inp->audioset = (1 << ARRAY_SIZE(vivid_audio_inputs)) - 1;
- inp->capabilities = V4L2_IN_CAP_STD;
- break;
- case SVID:
- snprintf(inp->name, sizeof(inp->name), "S-Video %u",
- dev->input_name_counter[inp->index]);
- inp->std = V4L2_STD_ALL;
- if (dev->has_audio_inputs)
- inp->audioset = (1 << ARRAY_SIZE(vivid_audio_inputs)) - 1;
- inp->capabilities = V4L2_IN_CAP_STD;
- break;
- case HDMI:
- snprintf(inp->name, sizeof(inp->name), "HDMI %u",
- dev->input_name_counter[inp->index]);
- inp->capabilities = V4L2_IN_CAP_DV_TIMINGS;
- if (dev->edid_blocks == 0 ||
- dev->dv_timings_signal_mode[dev->input] == NO_SIGNAL)
- inp->status |= V4L2_IN_ST_NO_SIGNAL;
- else if (dev->dv_timings_signal_mode[dev->input] == NO_LOCK ||
- dev->dv_timings_signal_mode[dev->input] == OUT_OF_RANGE)
- inp->status |= V4L2_IN_ST_NO_H_LOCK;
- break;
- }
- if (dev->sensor_hflip)
- inp->status |= V4L2_IN_ST_HFLIP;
- if (dev->sensor_vflip)
- inp->status |= V4L2_IN_ST_VFLIP;
- if (dev->input == inp->index && vivid_is_sdtv_cap(dev)) {
- if (dev->std_signal_mode[dev->input] == NO_SIGNAL) {
- inp->status |= V4L2_IN_ST_NO_SIGNAL;
- } else if (dev->std_signal_mode[dev->input] == NO_LOCK) {
- inp->status |= V4L2_IN_ST_NO_H_LOCK;
- } else if (vivid_is_tv_cap(dev)) {
- switch (tpg_g_quality(&dev->tpg)) {
- case TPG_QUAL_GRAY:
- inp->status |= V4L2_IN_ST_COLOR_KILL;
- break;
- case TPG_QUAL_NOISE:
- inp->status |= V4L2_IN_ST_NO_H_LOCK;
- break;
- default:
- break;
- }
- }
- }
- return 0;
-}
-
-int vidioc_g_input(struct file *file, void *priv, unsigned *i)
-{
- struct vivid_dev *dev = video_drvdata(file);
-
- *i = dev->input;
- return 0;
-}
-
-int vidioc_s_input(struct file *file, void *priv, unsigned i)
-{
- struct vivid_dev *dev = video_drvdata(file);
- struct v4l2_bt_timings *bt = &dev->dv_timings_cap[dev->input].bt;
- unsigned brightness;
-
- if (i >= dev->num_inputs)
- return -EINVAL;
-
- if (i == dev->input)
- return 0;
-
- if (vb2_is_busy(&dev->vb_vid_cap_q) || vb2_is_busy(&dev->vb_vbi_cap_q))
- return -EBUSY;
-
- dev->input = i;
- dev->vid_cap_dev.tvnorms = 0;
- if (dev->input_type[i] == TV || dev->input_type[i] == SVID) {
- dev->tv_audio_input = (dev->input_type[i] == TV) ? 0 : 1;
- dev->vid_cap_dev.tvnorms = V4L2_STD_ALL;
- }
- dev->vbi_cap_dev.tvnorms = dev->vid_cap_dev.tvnorms;
- vivid_update_format_cap(dev, false);
-
- if (dev->colorspace) {
- switch (dev->input_type[i]) {
- case WEBCAM:
- v4l2_ctrl_s_ctrl(dev->colorspace, VIVID_CS_SRGB);
- break;
- case TV:
- case SVID:
- v4l2_ctrl_s_ctrl(dev->colorspace, VIVID_CS_170M);
- break;
- case HDMI:
- if (bt->flags & V4L2_DV_FL_IS_CE_VIDEO) {
- if (dev->src_rect.width == 720 && dev->src_rect.height <= 576)
- v4l2_ctrl_s_ctrl(dev->colorspace, VIVID_CS_170M);
- else
- v4l2_ctrl_s_ctrl(dev->colorspace, VIVID_CS_709);
- } else {
- v4l2_ctrl_s_ctrl(dev->colorspace, VIVID_CS_SRGB);
- }
- break;
- }
- }
-
- /*
- * Modify the brightness range depending on the input.
- * This makes it easy to use vivid to test if applications can
- * handle control range modifications and is also how this is
- * typically used in practice as different inputs may be hooked
- * up to different receivers with different control ranges.
- */
- brightness = 128 * i + dev->input_brightness[i];
- v4l2_ctrl_modify_range(dev->brightness,
- 128 * i, 255 + 128 * i, 1, 128 + 128 * i);
- v4l2_ctrl_s_ctrl(dev->brightness, brightness);
-
- /* Restore per-input states. */
- v4l2_ctrl_activate(dev->ctrl_dv_timings_signal_mode,
- vivid_is_hdmi_cap(dev));
- v4l2_ctrl_activate(dev->ctrl_dv_timings, vivid_is_hdmi_cap(dev) &&
- dev->dv_timings_signal_mode[dev->input] ==
- SELECTED_DV_TIMINGS);
- v4l2_ctrl_activate(dev->ctrl_std_signal_mode, vivid_is_sdtv_cap(dev));
- v4l2_ctrl_activate(dev->ctrl_standard, vivid_is_sdtv_cap(dev) &&
- dev->std_signal_mode[dev->input]);
-
- if (vivid_is_hdmi_cap(dev)) {
- v4l2_ctrl_s_ctrl(dev->ctrl_dv_timings_signal_mode,
- dev->dv_timings_signal_mode[dev->input]);
- v4l2_ctrl_s_ctrl(dev->ctrl_dv_timings,
- dev->query_dv_timings[dev->input]);
- } else if (vivid_is_sdtv_cap(dev)) {
- v4l2_ctrl_s_ctrl(dev->ctrl_std_signal_mode,
- dev->std_signal_mode[dev->input]);
- v4l2_ctrl_s_ctrl(dev->ctrl_standard,
- dev->std_signal_mode[dev->input]);
- }
-
- return 0;
-}
-
-int vidioc_enumaudio(struct file *file, void *fh, struct v4l2_audio *vin)
-{
- if (vin->index >= ARRAY_SIZE(vivid_audio_inputs))
- return -EINVAL;
- *vin = vivid_audio_inputs[vin->index];
- return 0;
-}
-
-int vidioc_g_audio(struct file *file, void *fh, struct v4l2_audio *vin)
-{
- struct vivid_dev *dev = video_drvdata(file);
-
- if (!vivid_is_sdtv_cap(dev))
- return -EINVAL;
- *vin = vivid_audio_inputs[dev->tv_audio_input];
- return 0;
-}
-
-int vidioc_s_audio(struct file *file, void *fh, const struct v4l2_audio *vin)
-{
- struct vivid_dev *dev = video_drvdata(file);
-
- if (!vivid_is_sdtv_cap(dev))
- return -EINVAL;
- if (vin->index >= ARRAY_SIZE(vivid_audio_inputs))
- return -EINVAL;
- dev->tv_audio_input = vin->index;
- return 0;
-}
-
-int vivid_video_g_frequency(struct file *file, void *fh, struct v4l2_frequency *vf)
-{
- struct vivid_dev *dev = video_drvdata(file);
-
- if (vf->tuner != 0)
- return -EINVAL;
- vf->frequency = dev->tv_freq;
- return 0;
-}
-
-int vivid_video_s_frequency(struct file *file, void *fh, const struct v4l2_frequency *vf)
-{
- struct vivid_dev *dev = video_drvdata(file);
-
- if (vf->tuner != 0)
- return -EINVAL;
- dev->tv_freq = clamp_t(unsigned, vf->frequency, MIN_TV_FREQ, MAX_TV_FREQ);
- if (vivid_is_tv_cap(dev))
- vivid_update_quality(dev);
- return 0;
-}
-
-int vivid_video_s_tuner(struct file *file, void *fh, const struct v4l2_tuner *vt)
-{
- struct vivid_dev *dev = video_drvdata(file);
-
- if (vt->index != 0)
- return -EINVAL;
- if (vt->audmode > V4L2_TUNER_MODE_LANG1_LANG2)
- return -EINVAL;
- dev->tv_audmode = vt->audmode;
- return 0;
-}
-
-int vivid_video_g_tuner(struct file *file, void *fh, struct v4l2_tuner *vt)
-{
- struct vivid_dev *dev = video_drvdata(file);
- enum tpg_quality qual;
-
- if (vt->index != 0)
- return -EINVAL;
-
- vt->capability = V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_STEREO |
- V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2;
- vt->audmode = dev->tv_audmode;
- vt->rangelow = MIN_TV_FREQ;
- vt->rangehigh = MAX_TV_FREQ;
- qual = vivid_get_quality(dev, &vt->afc);
- if (qual == TPG_QUAL_COLOR)
- vt->signal = 0xffff;
- else if (qual == TPG_QUAL_GRAY)
- vt->signal = 0x8000;
- else
- vt->signal = 0;
- if (qual == TPG_QUAL_NOISE) {
- vt->rxsubchans = 0;
- } else if (qual == TPG_QUAL_GRAY) {
- vt->rxsubchans = V4L2_TUNER_SUB_MONO;
- } else {
- unsigned int channel_nr = dev->tv_freq / (6 * 16);
- unsigned int options =
- (dev->std_cap[dev->input] & V4L2_STD_NTSC_M) ? 4 : 3;
-
- switch (channel_nr % options) {
- case 0:
- vt->rxsubchans = V4L2_TUNER_SUB_MONO;
- break;
- case 1:
- vt->rxsubchans = V4L2_TUNER_SUB_STEREO;
- break;
- case 2:
- if (dev->std_cap[dev->input] & V4L2_STD_NTSC_M)
- vt->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_SAP;
- else
- vt->rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
- break;
- case 3:
- vt->rxsubchans = V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_SAP;
- break;
- }
- }
- strscpy(vt->name, "TV Tuner", sizeof(vt->name));
- return 0;
-}
-
-/* Must remain in sync with the vivid_ctrl_standard_strings array */
-const v4l2_std_id vivid_standard[] = {
- V4L2_STD_NTSC_M,
- V4L2_STD_NTSC_M_JP,
- V4L2_STD_NTSC_M_KR,
- V4L2_STD_NTSC_443,
- V4L2_STD_PAL_BG | V4L2_STD_PAL_H,
- V4L2_STD_PAL_I,
- V4L2_STD_PAL_DK,
- V4L2_STD_PAL_M,
- V4L2_STD_PAL_N,
- V4L2_STD_PAL_Nc,
- V4L2_STD_PAL_60,
- V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H,
- V4L2_STD_SECAM_DK,
- V4L2_STD_SECAM_L,
- V4L2_STD_SECAM_LC,
- V4L2_STD_UNKNOWN
-};
-
-/* Must remain in sync with the vivid_standard array */
-const char * const vivid_ctrl_standard_strings[] = {
- "NTSC-M",
- "NTSC-M-JP",
- "NTSC-M-KR",
- "NTSC-443",
- "PAL-BGH",
- "PAL-I",
- "PAL-DK",
- "PAL-M",
- "PAL-N",
- "PAL-Nc",
- "PAL-60",
- "SECAM-BGH",
- "SECAM-DK",
- "SECAM-L",
- "SECAM-Lc",
- NULL,
-};
-
-int vidioc_querystd(struct file *file, void *priv, v4l2_std_id *id)
-{
- struct vivid_dev *dev = video_drvdata(file);
- unsigned int last = dev->query_std_last[dev->input];
-
- if (!vivid_is_sdtv_cap(dev))
- return -ENODATA;
- if (dev->std_signal_mode[dev->input] == NO_SIGNAL ||
- dev->std_signal_mode[dev->input] == NO_LOCK) {
- *id = V4L2_STD_UNKNOWN;
- return 0;
- }
- if (vivid_is_tv_cap(dev) && tpg_g_quality(&dev->tpg) == TPG_QUAL_NOISE) {
- *id = V4L2_STD_UNKNOWN;
- } else if (dev->std_signal_mode[dev->input] == CURRENT_STD) {
- *id = dev->std_cap[dev->input];
- } else if (dev->std_signal_mode[dev->input] == SELECTED_STD) {
- *id = dev->query_std[dev->input];
- } else {
- *id = vivid_standard[last];
- dev->query_std_last[dev->input] =
- (last + 1) % ARRAY_SIZE(vivid_standard);
- }
-
- return 0;
-}
-
-int vivid_vid_cap_s_std(struct file *file, void *priv, v4l2_std_id id)
-{
- struct vivid_dev *dev = video_drvdata(file);
-
- if (!vivid_is_sdtv_cap(dev))
- return -ENODATA;
- if (dev->std_cap[dev->input] == id)
- return 0;
- if (vb2_is_busy(&dev->vb_vid_cap_q) || vb2_is_busy(&dev->vb_vbi_cap_q))
- return -EBUSY;
- dev->std_cap[dev->input] = id;
- vivid_update_format_cap(dev, false);
- return 0;
-}
-
-static void find_aspect_ratio(u32 width, u32 height,
- u32 *num, u32 *denom)
-{
- if (!(height % 3) && ((height * 4 / 3) == width)) {
- *num = 4;
- *denom = 3;
- } else if (!(height % 9) && ((height * 16 / 9) == width)) {
- *num = 16;
- *denom = 9;
- } else if (!(height % 10) && ((height * 16 / 10) == width)) {
- *num = 16;
- *denom = 10;
- } else if (!(height % 4) && ((height * 5 / 4) == width)) {
- *num = 5;
- *denom = 4;
- } else if (!(height % 9) && ((height * 15 / 9) == width)) {
- *num = 15;
- *denom = 9;
- } else { /* default to 16:9 */
- *num = 16;
- *denom = 9;
- }
-}
-
-static bool valid_cvt_gtf_timings(struct v4l2_dv_timings *timings)
-{
- struct v4l2_bt_timings *bt = &timings->bt;
- u32 total_h_pixel;
- u32 total_v_lines;
- u32 h_freq;
-
- if (!v4l2_valid_dv_timings(timings, &vivid_dv_timings_cap,
- NULL, NULL))
- return false;
-
- total_h_pixel = V4L2_DV_BT_FRAME_WIDTH(bt);
- total_v_lines = V4L2_DV_BT_FRAME_HEIGHT(bt);
-
- h_freq = (u32)bt->pixelclock / total_h_pixel;
-
- if (bt->standards == 0 || (bt->standards & V4L2_DV_BT_STD_CVT)) {
- if (v4l2_detect_cvt(total_v_lines, h_freq, bt->vsync, bt->width,
- bt->polarities, bt->interlaced, timings))
- return true;
- }
-
- if (bt->standards == 0 || (bt->standards & V4L2_DV_BT_STD_GTF)) {
- struct v4l2_fract aspect_ratio;
-
- find_aspect_ratio(bt->width, bt->height,
- &aspect_ratio.numerator,
- &aspect_ratio.denominator);
- if (v4l2_detect_gtf(total_v_lines, h_freq, bt->vsync,
- bt->polarities, bt->interlaced,
- aspect_ratio, timings))
- return true;
- }
- return false;
-}
-
-int vivid_vid_cap_s_dv_timings(struct file *file, void *_fh,
- struct v4l2_dv_timings *timings)
-{
- struct vivid_dev *dev = video_drvdata(file);
-
- if (!vivid_is_hdmi_cap(dev))
- return -ENODATA;
- if (!v4l2_find_dv_timings_cap(timings, &vivid_dv_timings_cap,
- 0, NULL, NULL) &&
- !valid_cvt_gtf_timings(timings))
- return -EINVAL;
-
- if (v4l2_match_dv_timings(timings, &dev->dv_timings_cap[dev->input],
- 0, false))
- return 0;
- if (vb2_is_busy(&dev->vb_vid_cap_q))
- return -EBUSY;
-
- dev->dv_timings_cap[dev->input] = *timings;
- vivid_update_format_cap(dev, false);
- return 0;
-}
-
-int vidioc_query_dv_timings(struct file *file, void *_fh,
- struct v4l2_dv_timings *timings)
-{
- struct vivid_dev *dev = video_drvdata(file);
- unsigned int input = dev->input;
- unsigned int last = dev->query_dv_timings_last[input];
-
- if (!vivid_is_hdmi_cap(dev))
- return -ENODATA;
- if (dev->dv_timings_signal_mode[input] == NO_SIGNAL ||
- dev->edid_blocks == 0)
- return -ENOLINK;
- if (dev->dv_timings_signal_mode[input] == NO_LOCK)
- return -ENOLCK;
- if (dev->dv_timings_signal_mode[input] == OUT_OF_RANGE) {
- timings->bt.pixelclock = vivid_dv_timings_cap.bt.max_pixelclock * 2;
- return -ERANGE;
- }
- if (dev->dv_timings_signal_mode[input] == CURRENT_DV_TIMINGS) {
- *timings = dev->dv_timings_cap[input];
- } else if (dev->dv_timings_signal_mode[input] ==
- SELECTED_DV_TIMINGS) {
- *timings =
- v4l2_dv_timings_presets[dev->query_dv_timings[input]];
- } else {
- *timings =
- v4l2_dv_timings_presets[last];
- dev->query_dv_timings_last[input] =
- (last + 1) % dev->query_dv_timings_size;
- }
- return 0;
-}
-
-int vidioc_s_edid(struct file *file, void *_fh,
- struct v4l2_edid *edid)
-{
- struct vivid_dev *dev = video_drvdata(file);
- u16 phys_addr;
- u32 display_present = 0;
- unsigned int i, j;
- int ret;
-
- memset(edid->reserved, 0, sizeof(edid->reserved));
- if (edid->pad >= dev->num_inputs)
- return -EINVAL;
- if (dev->input_type[edid->pad] != HDMI || edid->start_block)
- return -EINVAL;
- if (edid->blocks == 0) {
- dev->edid_blocks = 0;
- v4l2_ctrl_s_ctrl(dev->ctrl_tx_edid_present, 0);
- v4l2_ctrl_s_ctrl(dev->ctrl_tx_hotplug, 0);
- phys_addr = CEC_PHYS_ADDR_INVALID;
- goto set_phys_addr;
- }
- if (edid->blocks > dev->edid_max_blocks) {
- edid->blocks = dev->edid_max_blocks;
- return -E2BIG;
- }
- phys_addr = cec_get_edid_phys_addr(edid->edid, edid->blocks * 128, NULL);
- ret = v4l2_phys_addr_validate(phys_addr, &phys_addr, NULL);
- if (ret)
- return ret;
-
- if (vb2_is_busy(&dev->vb_vid_cap_q))
- return -EBUSY;
-
- dev->edid_blocks = edid->blocks;
- memcpy(dev->edid, edid->edid, edid->blocks * 128);
-
- for (i = 0, j = 0; i < dev->num_outputs; i++)
- if (dev->output_type[i] == HDMI)
- display_present |=
- dev->display_present[i] << j++;
-
- v4l2_ctrl_s_ctrl(dev->ctrl_tx_edid_present, display_present);
- v4l2_ctrl_s_ctrl(dev->ctrl_tx_hotplug, display_present);
-
-set_phys_addr:
- /* TODO: a proper hotplug detect cycle should be emulated here */
- cec_s_phys_addr(dev->cec_rx_adap, phys_addr, false);
-
- for (i = 0; i < MAX_OUTPUTS && dev->cec_tx_adap[i]; i++)
- cec_s_phys_addr(dev->cec_tx_adap[i],
- dev->display_present[i] ?
- v4l2_phys_addr_for_input(phys_addr, i + 1) :
- CEC_PHYS_ADDR_INVALID,
- false);
- return 0;
-}
-
-int vidioc_enum_framesizes(struct file *file, void *fh,
- struct v4l2_frmsizeenum *fsize)
-{
- struct vivid_dev *dev = video_drvdata(file);
-
- if (!vivid_is_webcam(dev) && !dev->has_scaler_cap)
- return -EINVAL;
- if (vivid_get_format(dev, fsize->pixel_format) == NULL)
- return -EINVAL;
- if (vivid_is_webcam(dev)) {
- if (fsize->index >= ARRAY_SIZE(webcam_sizes))
- return -EINVAL;
- fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
- fsize->discrete = webcam_sizes[fsize->index];
- return 0;
- }
- if (fsize->index)
- return -EINVAL;
- fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
- fsize->stepwise.min_width = MIN_WIDTH;
- fsize->stepwise.max_width = MAX_WIDTH * MAX_ZOOM;
- fsize->stepwise.step_width = 2;
- fsize->stepwise.min_height = MIN_HEIGHT;
- fsize->stepwise.max_height = MAX_HEIGHT * MAX_ZOOM;
- fsize->stepwise.step_height = 2;
- return 0;
-}
-
-/* timeperframe is arbitrary and continuous */
-int vidioc_enum_frameintervals(struct file *file, void *priv,
- struct v4l2_frmivalenum *fival)
-{
- struct vivid_dev *dev = video_drvdata(file);
- const struct vivid_fmt *fmt;
- int i;
-
- fmt = vivid_get_format(dev, fival->pixel_format);
- if (!fmt)
- return -EINVAL;
-
- if (!vivid_is_webcam(dev)) {
- if (fival->index)
- return -EINVAL;
- if (fival->width < MIN_WIDTH || fival->width > MAX_WIDTH * MAX_ZOOM)
- return -EINVAL;
- if (fival->height < MIN_HEIGHT || fival->height > MAX_HEIGHT * MAX_ZOOM)
- return -EINVAL;
- fival->type = V4L2_FRMIVAL_TYPE_DISCRETE;
- fival->discrete = dev->timeperframe_vid_cap;
- return 0;
- }
-
- for (i = 0; i < ARRAY_SIZE(webcam_sizes); i++)
- if (fival->width == webcam_sizes[i].width &&
- fival->height == webcam_sizes[i].height)
- break;
- if (i == ARRAY_SIZE(webcam_sizes))
- return -EINVAL;
- if (fival->index >= 2 * (VIVID_WEBCAM_SIZES - i))
- return -EINVAL;
- fival->type = V4L2_FRMIVAL_TYPE_DISCRETE;
- fival->discrete = webcam_intervals[fival->index];
- return 0;
-}
-
-int vivid_vid_cap_g_parm(struct file *file, void *priv,
- struct v4l2_streamparm *parm)
-{
- struct vivid_dev *dev = video_drvdata(file);
-
- if (parm->type != (dev->multiplanar ?
- V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE :
- V4L2_BUF_TYPE_VIDEO_CAPTURE))
- return -EINVAL;
-
- parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
- parm->parm.capture.timeperframe = dev->timeperframe_vid_cap;
- parm->parm.capture.readbuffers = 1;
- return 0;
-}
-
-int vivid_vid_cap_s_parm(struct file *file, void *priv,
- struct v4l2_streamparm *parm)
-{
- struct vivid_dev *dev = video_drvdata(file);
- unsigned ival_sz = 2 * (VIVID_WEBCAM_SIZES - dev->webcam_size_idx);
- struct v4l2_fract tpf;
- unsigned i;
-
- if (parm->type != (dev->multiplanar ?
- V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE :
- V4L2_BUF_TYPE_VIDEO_CAPTURE))
- return -EINVAL;
- if (!vivid_is_webcam(dev))
- return vivid_vid_cap_g_parm(file, priv, parm);
-
- tpf = parm->parm.capture.timeperframe;
-
- if (tpf.denominator == 0)
- tpf = webcam_intervals[ival_sz - 1];
- for (i = 0; i < ival_sz; i++)
- if (V4L2_FRACT_COMPARE(tpf, >=, webcam_intervals[i]))
- break;
- if (i == ival_sz)
- i = ival_sz - 1;
- dev->webcam_ival_idx = i;
- tpf = webcam_intervals[dev->webcam_ival_idx];
-
- /* resync the thread's timings */
- dev->cap_seq_resync = true;
- dev->timeperframe_vid_cap = tpf;
- parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
- parm->parm.capture.timeperframe = tpf;
- parm->parm.capture.readbuffers = 1;
- return 0;
-}
diff --git a/drivers/media/platform/vivid/vivid-vid-cap.h b/drivers/media/platform/vivid/vivid-vid-cap.h
deleted file mode 100644
index 1e422a5..0000000
--- a/drivers/media/platform/vivid/vivid-vid-cap.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-/*
- * vivid-vid-cap.h - video capture support functions.
- *
- * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- */
-
-#ifndef _VIVID_VID_CAP_H_
-#define _VIVID_VID_CAP_H_
-
-void vivid_update_quality(struct vivid_dev *dev);
-void vivid_update_format_cap(struct vivid_dev *dev, bool keep_controls);
-enum tpg_video_aspect vivid_get_video_aspect(const struct vivid_dev *dev);
-
-extern const v4l2_std_id vivid_standard[];
-extern const char * const vivid_ctrl_standard_strings[];
-
-extern const struct vb2_ops vivid_vid_cap_qops;
-
-int vivid_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f);
-int vivid_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f);
-int vivid_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f);
-int vidioc_g_fmt_vid_cap_mplane(struct file *file, void *priv, struct v4l2_format *f);
-int vidioc_try_fmt_vid_cap_mplane(struct file *file, void *priv, struct v4l2_format *f);
-int vidioc_s_fmt_vid_cap_mplane(struct file *file, void *priv, struct v4l2_format *f);
-int vidioc_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f);
-int vidioc_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f);
-int vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f);
-int vivid_vid_cap_g_selection(struct file *file, void *priv, struct v4l2_selection *sel);
-int vivid_vid_cap_s_selection(struct file *file, void *fh, struct v4l2_selection *s);
-int vivid_vid_cap_g_pixelaspect(struct file *file, void *priv, int type, struct v4l2_fract *f);
-int vidioc_enum_fmt_vid_overlay(struct file *file, void *priv, struct v4l2_fmtdesc *f);
-int vidioc_g_fmt_vid_overlay(struct file *file, void *priv, struct v4l2_format *f);
-int vidioc_try_fmt_vid_overlay(struct file *file, void *priv, struct v4l2_format *f);
-int vidioc_s_fmt_vid_overlay(struct file *file, void *priv, struct v4l2_format *f);
-int vivid_vid_cap_overlay(struct file *file, void *fh, unsigned i);
-int vivid_vid_cap_g_fbuf(struct file *file, void *fh, struct v4l2_framebuffer *a);
-int vivid_vid_cap_s_fbuf(struct file *file, void *fh, const struct v4l2_framebuffer *a);
-int vidioc_enum_input(struct file *file, void *priv, struct v4l2_input *inp);
-int vidioc_g_input(struct file *file, void *priv, unsigned *i);
-int vidioc_s_input(struct file *file, void *priv, unsigned i);
-int vidioc_enumaudio(struct file *file, void *fh, struct v4l2_audio *vin);
-int vidioc_g_audio(struct file *file, void *fh, struct v4l2_audio *vin);
-int vidioc_s_audio(struct file *file, void *fh, const struct v4l2_audio *vin);
-int vivid_video_g_frequency(struct file *file, void *fh, struct v4l2_frequency *vf);
-int vivid_video_s_frequency(struct file *file, void *fh, const struct v4l2_frequency *vf);
-int vivid_video_s_tuner(struct file *file, void *fh, const struct v4l2_tuner *vt);
-int vivid_video_g_tuner(struct file *file, void *fh, struct v4l2_tuner *vt);
-int vidioc_querystd(struct file *file, void *priv, v4l2_std_id *id);
-int vivid_vid_cap_s_std(struct file *file, void *priv, v4l2_std_id id);
-int vivid_vid_cap_s_dv_timings(struct file *file, void *_fh, struct v4l2_dv_timings *timings);
-int vidioc_query_dv_timings(struct file *file, void *_fh, struct v4l2_dv_timings *timings);
-int vidioc_s_edid(struct file *file, void *_fh, struct v4l2_edid *edid);
-int vidioc_enum_framesizes(struct file *file, void *fh, struct v4l2_frmsizeenum *fsize);
-int vidioc_enum_frameintervals(struct file *file, void *priv, struct v4l2_frmivalenum *fival);
-int vivid_vid_cap_g_parm(struct file *file, void *priv, struct v4l2_streamparm *parm);
-int vivid_vid_cap_s_parm(struct file *file, void *priv, struct v4l2_streamparm *parm);
-
-#endif
diff --git a/drivers/media/platform/vivid/vivid-vid-common.c b/drivers/media/platform/vivid/vivid-vid-common.c
deleted file mode 100644
index 8665dfd..0000000
--- a/drivers/media/platform/vivid/vivid-vid-common.c
+++ /dev/null
@@ -1,1035 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * vivid-vid-common.c - common video support functions.
- *
- * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- */
-
-#include <linux/errno.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/videodev2.h>
-#include <linux/v4l2-dv-timings.h>
-#include <media/v4l2-common.h>
-#include <media/v4l2-event.h>
-#include <media/v4l2-dv-timings.h>
-
-#include "vivid-core.h"
-#include "vivid-vid-common.h"
-
-const struct v4l2_dv_timings_cap vivid_dv_timings_cap = {
- .type = V4L2_DV_BT_656_1120,
- /* keep this initialization for compatibility with GCC < 4.4.6 */
- .reserved = { 0 },
- V4L2_INIT_BT_TIMINGS(16, MAX_WIDTH, 16, MAX_HEIGHT, 14000000, 775000000,
- V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT |
- V4L2_DV_BT_STD_CVT | V4L2_DV_BT_STD_GTF,
- V4L2_DV_BT_CAP_PROGRESSIVE | V4L2_DV_BT_CAP_INTERLACED)
-};
-
-/* ------------------------------------------------------------------
- Basic structures
- ------------------------------------------------------------------*/
-
-struct vivid_fmt vivid_formats[] = {
- {
- .fourcc = V4L2_PIX_FMT_YUYV,
- .vdownsampling = { 1 },
- .bit_depth = { 16 },
- .color_enc = TGP_COLOR_ENC_YCBCR,
- .planes = 1,
- .buffers = 1,
- .data_offset = { PLANE0_DATA_OFFSET },
- },
- {
- .fourcc = V4L2_PIX_FMT_UYVY,
- .vdownsampling = { 1 },
- .bit_depth = { 16 },
- .color_enc = TGP_COLOR_ENC_YCBCR,
- .planes = 1,
- .buffers = 1,
- },
- {
- .fourcc = V4L2_PIX_FMT_YVYU,
- .vdownsampling = { 1 },
- .bit_depth = { 16 },
- .color_enc = TGP_COLOR_ENC_YCBCR,
- .planes = 1,
- .buffers = 1,
- },
- {
- .fourcc = V4L2_PIX_FMT_VYUY,
- .vdownsampling = { 1 },
- .bit_depth = { 16 },
- .color_enc = TGP_COLOR_ENC_YCBCR,
- .planes = 1,
- .buffers = 1,
- },
- {
- .fourcc = V4L2_PIX_FMT_YUV422P,
- .vdownsampling = { 1, 1, 1 },
- .bit_depth = { 8, 4, 4 },
- .color_enc = TGP_COLOR_ENC_YCBCR,
- .planes = 3,
- .buffers = 1,
- },
- {
- .fourcc = V4L2_PIX_FMT_YUV420,
- .vdownsampling = { 1, 2, 2 },
- .bit_depth = { 8, 4, 4 },
- .color_enc = TGP_COLOR_ENC_YCBCR,
- .planes = 3,
- .buffers = 1,
- },
- {
- .fourcc = V4L2_PIX_FMT_YVU420,
- .vdownsampling = { 1, 2, 2 },
- .bit_depth = { 8, 4, 4 },
- .color_enc = TGP_COLOR_ENC_YCBCR,
- .planes = 3,
- .buffers = 1,
- },
- {
- .fourcc = V4L2_PIX_FMT_NV12,
- .vdownsampling = { 1, 2 },
- .bit_depth = { 8, 8 },
- .color_enc = TGP_COLOR_ENC_YCBCR,
- .planes = 2,
- .buffers = 1,
- },
- {
- .fourcc = V4L2_PIX_FMT_NV21,
- .vdownsampling = { 1, 2 },
- .bit_depth = { 8, 8 },
- .color_enc = TGP_COLOR_ENC_YCBCR,
- .planes = 2,
- .buffers = 1,
- },
- {
- .fourcc = V4L2_PIX_FMT_NV16,
- .vdownsampling = { 1, 1 },
- .bit_depth = { 8, 8 },
- .color_enc = TGP_COLOR_ENC_YCBCR,
- .planes = 2,
- .buffers = 1,
- },
- {
- .fourcc = V4L2_PIX_FMT_NV61,
- .vdownsampling = { 1, 1 },
- .bit_depth = { 8, 8 },
- .color_enc = TGP_COLOR_ENC_YCBCR,
- .planes = 2,
- .buffers = 1,
- },
- {
- .fourcc = V4L2_PIX_FMT_NV24,
- .vdownsampling = { 1, 1 },
- .bit_depth = { 8, 16 },
- .color_enc = TGP_COLOR_ENC_YCBCR,
- .planes = 2,
- .buffers = 1,
- },
- {
- .fourcc = V4L2_PIX_FMT_NV42,
- .vdownsampling = { 1, 1 },
- .bit_depth = { 8, 16 },
- .color_enc = TGP_COLOR_ENC_YCBCR,
- .planes = 2,
- .buffers = 1,
- },
- {
- .fourcc = V4L2_PIX_FMT_YUV555, /* uuuvvvvv ayyyyyuu */
- .vdownsampling = { 1 },
- .bit_depth = { 16 },
- .planes = 1,
- .buffers = 1,
- .alpha_mask = 0x8000,
- },
- {
- .fourcc = V4L2_PIX_FMT_YUV565, /* uuuvvvvv yyyyyuuu */
- .vdownsampling = { 1 },
- .bit_depth = { 16 },
- .planes = 1,
- .buffers = 1,
- },
- {
- .fourcc = V4L2_PIX_FMT_YUV444, /* uuuuvvvv aaaayyyy */
- .vdownsampling = { 1 },
- .bit_depth = { 16 },
- .planes = 1,
- .buffers = 1,
- .alpha_mask = 0xf000,
- },
- {
- .fourcc = V4L2_PIX_FMT_YUV32, /* ayuv */
- .vdownsampling = { 1 },
- .bit_depth = { 32 },
- .planes = 1,
- .buffers = 1,
- .alpha_mask = 0x000000ff,
- },
- {
- .fourcc = V4L2_PIX_FMT_AYUV32,
- .vdownsampling = { 1 },
- .bit_depth = { 32 },
- .planes = 1,
- .buffers = 1,
- .alpha_mask = 0x000000ff,
- },
- {
- .fourcc = V4L2_PIX_FMT_XYUV32,
- .vdownsampling = { 1 },
- .bit_depth = { 32 },
- .planes = 1,
- .buffers = 1,
- },
- {
- .fourcc = V4L2_PIX_FMT_VUYA32,
- .vdownsampling = { 1 },
- .bit_depth = { 32 },
- .planes = 1,
- .buffers = 1,
- .alpha_mask = 0xff000000,
- },
- {
- .fourcc = V4L2_PIX_FMT_VUYX32,
- .vdownsampling = { 1 },
- .bit_depth = { 32 },
- .planes = 1,
- .buffers = 1,
- },
- {
- .fourcc = V4L2_PIX_FMT_GREY,
- .vdownsampling = { 1 },
- .bit_depth = { 8 },
- .color_enc = TGP_COLOR_ENC_LUMA,
- .planes = 1,
- .buffers = 1,
- },
- {
- .fourcc = V4L2_PIX_FMT_Y10,
- .vdownsampling = { 1 },
- .bit_depth = { 16 },
- .color_enc = TGP_COLOR_ENC_LUMA,
- .planes = 1,
- .buffers = 1,
- },
- {
- .fourcc = V4L2_PIX_FMT_Y12,
- .vdownsampling = { 1 },
- .bit_depth = { 16 },
- .color_enc = TGP_COLOR_ENC_LUMA,
- .planes = 1,
- .buffers = 1,
- },
- {
- .fourcc = V4L2_PIX_FMT_Y16,
- .vdownsampling = { 1 },
- .bit_depth = { 16 },
- .color_enc = TGP_COLOR_ENC_LUMA,
- .planes = 1,
- .buffers = 1,
- },
- {
- .fourcc = V4L2_PIX_FMT_Y16_BE,
- .vdownsampling = { 1 },
- .bit_depth = { 16 },
- .color_enc = TGP_COLOR_ENC_LUMA,
- .planes = 1,
- .buffers = 1,
- },
- {
- .fourcc = V4L2_PIX_FMT_RGB332, /* rrrgggbb */
- .vdownsampling = { 1 },
- .bit_depth = { 8 },
- .planes = 1,
- .buffers = 1,
- },
- {
- .fourcc = V4L2_PIX_FMT_RGB565, /* gggbbbbb rrrrrggg */
- .vdownsampling = { 1 },
- .bit_depth = { 16 },
- .planes = 1,
- .buffers = 1,
- .can_do_overlay = true,
- },
- {
- .fourcc = V4L2_PIX_FMT_RGB565X, /* rrrrrggg gggbbbbb */
- .vdownsampling = { 1 },
- .bit_depth = { 16 },
- .planes = 1,
- .buffers = 1,
- .can_do_overlay = true,
- },
- {
- .fourcc = V4L2_PIX_FMT_RGB444, /* ggggbbbb xxxxrrrr */
- .vdownsampling = { 1 },
- .bit_depth = { 16 },
- .planes = 1,
- .buffers = 1,
- },
- {
- .fourcc = V4L2_PIX_FMT_XRGB444, /* ggggbbbb xxxxrrrr */
- .vdownsampling = { 1 },
- .bit_depth = { 16 },
- .planes = 1,
- .buffers = 1,
- },
- {
- .fourcc = V4L2_PIX_FMT_ARGB444, /* ggggbbbb aaaarrrr */
- .vdownsampling = { 1 },
- .bit_depth = { 16 },
- .planes = 1,
- .buffers = 1,
- .alpha_mask = 0x00f0,
- },
- {
- .fourcc = V4L2_PIX_FMT_RGBX444, /* bbbbxxxx rrrrgggg */
- .vdownsampling = { 1 },
- .bit_depth = { 16 },
- .planes = 1,
- .buffers = 1,
- },
- {
- .fourcc = V4L2_PIX_FMT_RGBA444, /* bbbbaaaa rrrrgggg */
- .vdownsampling = { 1 },
- .bit_depth = { 16 },
- .planes = 1,
- .buffers = 1,
- .alpha_mask = 0x00f0,
- },
- {
- .fourcc = V4L2_PIX_FMT_XBGR444, /* ggggrrrr xxxxbbbb */
- .vdownsampling = { 1 },
- .bit_depth = { 16 },
- .planes = 1,
- .buffers = 1,
- },
- {
- .fourcc = V4L2_PIX_FMT_ABGR444, /* ggggrrrr aaaabbbb */
- .vdownsampling = { 1 },
- .bit_depth = { 16 },
- .planes = 1,
- .buffers = 1,
- .alpha_mask = 0x00f0,
- },
- {
- .fourcc = V4L2_PIX_FMT_BGRX444, /* rrrrxxxx bbbbgggg */
- .vdownsampling = { 1 },
- .bit_depth = { 16 },
- .planes = 1,
- .buffers = 1,
- },
- {
- .fourcc = V4L2_PIX_FMT_BGRA444, /* rrrraaaa bbbbgggg */
- .vdownsampling = { 1 },
- .bit_depth = { 16 },
- .planes = 1,
- .buffers = 1,
- .alpha_mask = 0x00f0,
- },
- {
- .fourcc = V4L2_PIX_FMT_RGB555, /* gggbbbbb xrrrrrgg */
- .vdownsampling = { 1 },
- .bit_depth = { 16 },
- .planes = 1,
- .buffers = 1,
- .can_do_overlay = true,
- },
- {
- .fourcc = V4L2_PIX_FMT_XRGB555, /* gggbbbbb xrrrrrgg */
- .vdownsampling = { 1 },
- .bit_depth = { 16 },
- .planes = 1,
- .buffers = 1,
- .can_do_overlay = true,
- },
- {
- .fourcc = V4L2_PIX_FMT_ARGB555, /* gggbbbbb arrrrrgg */
- .vdownsampling = { 1 },
- .bit_depth = { 16 },
- .planes = 1,
- .buffers = 1,
- .can_do_overlay = true,
- .alpha_mask = 0x8000,
- },
- {
- .fourcc = V4L2_PIX_FMT_RGBX555, /* ggbbbbbx rrrrrggg */
- .vdownsampling = { 1 },
- .bit_depth = { 16 },
- .planes = 1,
- .buffers = 1,
- .can_do_overlay = true,
- },
- {
- .fourcc = V4L2_PIX_FMT_RGBA555, /* ggbbbbba rrrrrggg */
- .vdownsampling = { 1 },
- .bit_depth = { 16 },
- .planes = 1,
- .buffers = 1,
- .can_do_overlay = true,
- .alpha_mask = 0x8000,
- },
- {
- .fourcc = V4L2_PIX_FMT_XBGR555, /* gggrrrrr xbbbbbgg */
- .vdownsampling = { 1 },
- .bit_depth = { 16 },
- .planes = 1,
- .buffers = 1,
- .can_do_overlay = true,
- },
- {
- .fourcc = V4L2_PIX_FMT_ABGR555, /* gggrrrrr abbbbbgg */
- .vdownsampling = { 1 },
- .bit_depth = { 16 },
- .planes = 1,
- .buffers = 1,
- .can_do_overlay = true,
- .alpha_mask = 0x8000,
- },
- {
- .fourcc = V4L2_PIX_FMT_BGRX555, /* ggrrrrrx bbbbbggg */
- .vdownsampling = { 1 },
- .bit_depth = { 16 },
- .planes = 1,
- .buffers = 1,
- .can_do_overlay = true,
- },
- {
- .fourcc = V4L2_PIX_FMT_BGRA555, /* ggrrrrra bbbbbggg */
- .vdownsampling = { 1 },
- .bit_depth = { 16 },
- .planes = 1,
- .buffers = 1,
- .can_do_overlay = true,
- .alpha_mask = 0x8000,
- },
- {
- .fourcc = V4L2_PIX_FMT_RGB555X, /* xrrrrrgg gggbbbbb */
- .vdownsampling = { 1 },
- .bit_depth = { 16 },
- .planes = 1,
- .buffers = 1,
- },
- {
- .fourcc = V4L2_PIX_FMT_XRGB555X, /* xrrrrrgg gggbbbbb */
- .vdownsampling = { 1 },
- .bit_depth = { 16 },
- .planes = 1,
- .buffers = 1,
- },
- {
- .fourcc = V4L2_PIX_FMT_ARGB555X, /* arrrrrgg gggbbbbb */
- .vdownsampling = { 1 },
- .bit_depth = { 16 },
- .planes = 1,
- .buffers = 1,
- .alpha_mask = 0x0080,
- },
- {
- .fourcc = V4L2_PIX_FMT_RGB24, /* rgb */
- .vdownsampling = { 1 },
- .bit_depth = { 24 },
- .planes = 1,
- .buffers = 1,
- },
- {
- .fourcc = V4L2_PIX_FMT_BGR24, /* bgr */
- .vdownsampling = { 1 },
- .bit_depth = { 24 },
- .planes = 1,
- .buffers = 1,
- },
- {
- .fourcc = V4L2_PIX_FMT_BGR666, /* bbbbbbgg ggggrrrr rrxxxxxx */
- .vdownsampling = { 1 },
- .bit_depth = { 32 },
- .planes = 1,
- .buffers = 1,
- },
- {
- .fourcc = V4L2_PIX_FMT_RGB32, /* xrgb */
- .vdownsampling = { 1 },
- .bit_depth = { 32 },
- .planes = 1,
- .buffers = 1,
- },
- {
- .fourcc = V4L2_PIX_FMT_BGR32, /* bgrx */
- .vdownsampling = { 1 },
- .bit_depth = { 32 },
- .planes = 1,
- .buffers = 1,
- },
- {
- .fourcc = V4L2_PIX_FMT_XRGB32, /* xrgb */
- .vdownsampling = { 1 },
- .bit_depth = { 32 },
- .planes = 1,
- .buffers = 1,
- },
- {
- .fourcc = V4L2_PIX_FMT_XBGR32, /* bgrx */
- .vdownsampling = { 1 },
- .bit_depth = { 32 },
- .planes = 1,
- .buffers = 1,
- },
- {
- .fourcc = V4L2_PIX_FMT_ARGB32, /* argb */
- .vdownsampling = { 1 },
- .bit_depth = { 32 },
- .planes = 1,
- .buffers = 1,
- .alpha_mask = 0x000000ff,
- },
- {
- .fourcc = V4L2_PIX_FMT_ABGR32, /* bgra */
- .vdownsampling = { 1 },
- .bit_depth = { 32 },
- .planes = 1,
- .buffers = 1,
- .alpha_mask = 0xff000000,
- },
- {
- .fourcc = V4L2_PIX_FMT_RGBX32, /* rgbx */
- .vdownsampling = { 1 },
- .bit_depth = { 32 },
- .planes = 1,
- .buffers = 1,
- },
- {
- .fourcc = V4L2_PIX_FMT_BGRX32, /* xbgr */
- .vdownsampling = { 1 },
- .bit_depth = { 32 },
- .planes = 1,
- .buffers = 1,
- },
- {
- .fourcc = V4L2_PIX_FMT_RGBA32, /* rgba */
- .vdownsampling = { 1 },
- .bit_depth = { 32 },
- .planes = 1,
- .buffers = 1,
- .alpha_mask = 0x000000ff,
- },
- {
- .fourcc = V4L2_PIX_FMT_BGRA32, /* abgr */
- .vdownsampling = { 1 },
- .bit_depth = { 32 },
- .planes = 1,
- .buffers = 1,
- .alpha_mask = 0xff000000,
- },
- {
- .fourcc = V4L2_PIX_FMT_SBGGR8, /* Bayer BG/GR */
- .vdownsampling = { 1 },
- .bit_depth = { 8 },
- .planes = 1,
- .buffers = 1,
- },
- {
- .fourcc = V4L2_PIX_FMT_SGBRG8, /* Bayer GB/RG */
- .vdownsampling = { 1 },
- .bit_depth = { 8 },
- .planes = 1,
- .buffers = 1,
- },
- {
- .fourcc = V4L2_PIX_FMT_SGRBG8, /* Bayer GR/BG */
- .vdownsampling = { 1 },
- .bit_depth = { 8 },
- .planes = 1,
- .buffers = 1,
- },
- {
- .fourcc = V4L2_PIX_FMT_SRGGB8, /* Bayer RG/GB */
- .vdownsampling = { 1 },
- .bit_depth = { 8 },
- .planes = 1,
- .buffers = 1,
- },
- {
- .fourcc = V4L2_PIX_FMT_SBGGR10, /* Bayer BG/GR */
- .vdownsampling = { 1 },
- .bit_depth = { 16 },
- .planes = 1,
- .buffers = 1,
- },
- {
- .fourcc = V4L2_PIX_FMT_SGBRG10, /* Bayer GB/RG */
- .vdownsampling = { 1 },
- .bit_depth = { 16 },
- .planes = 1,
- .buffers = 1,
- },
- {
- .fourcc = V4L2_PIX_FMT_SGRBG10, /* Bayer GR/BG */
- .vdownsampling = { 1 },
- .bit_depth = { 16 },
- .planes = 1,
- .buffers = 1,
- },
- {
- .fourcc = V4L2_PIX_FMT_SRGGB10, /* Bayer RG/GB */
- .vdownsampling = { 1 },
- .bit_depth = { 16 },
- .planes = 1,
- .buffers = 1,
- },
- {
- .fourcc = V4L2_PIX_FMT_SBGGR12, /* Bayer BG/GR */
- .vdownsampling = { 1 },
- .bit_depth = { 16 },
- .planes = 1,
- .buffers = 1,
- },
- {
- .fourcc = V4L2_PIX_FMT_SGBRG12, /* Bayer GB/RG */
- .vdownsampling = { 1 },
- .bit_depth = { 16 },
- .planes = 1,
- .buffers = 1,
- },
- {
- .fourcc = V4L2_PIX_FMT_SGRBG12, /* Bayer GR/BG */
- .vdownsampling = { 1 },
- .bit_depth = { 16 },
- .planes = 1,
- .buffers = 1,
- },
- {
- .fourcc = V4L2_PIX_FMT_SRGGB12, /* Bayer RG/GB */
- .vdownsampling = { 1 },
- .bit_depth = { 16 },
- .planes = 1,
- .buffers = 1,
- },
- {
- .fourcc = V4L2_PIX_FMT_SBGGR16, /* Bayer BG/GR */
- .vdownsampling = { 1 },
- .bit_depth = { 16 },
- .planes = 1,
- .buffers = 1,
- },
- {
- .fourcc = V4L2_PIX_FMT_SGBRG16, /* Bayer GB/RG */
- .vdownsampling = { 1 },
- .bit_depth = { 16 },
- .planes = 1,
- .buffers = 1,
- },
- {
- .fourcc = V4L2_PIX_FMT_SGRBG16, /* Bayer GR/BG */
- .vdownsampling = { 1 },
- .bit_depth = { 16 },
- .planes = 1,
- .buffers = 1,
- },
- {
- .fourcc = V4L2_PIX_FMT_SRGGB16, /* Bayer RG/GB */
- .vdownsampling = { 1 },
- .bit_depth = { 16 },
- .planes = 1,
- .buffers = 1,
- },
- {
- .fourcc = V4L2_PIX_FMT_HSV24, /* HSV 24bits */
- .color_enc = TGP_COLOR_ENC_HSV,
- .vdownsampling = { 1 },
- .bit_depth = { 24 },
- .planes = 1,
- .buffers = 1,
- },
- {
- .fourcc = V4L2_PIX_FMT_HSV32, /* HSV 32bits */
- .color_enc = TGP_COLOR_ENC_HSV,
- .vdownsampling = { 1 },
- .bit_depth = { 32 },
- .planes = 1,
- .buffers = 1,
- },
-
- /* Multiplanar formats */
-
- {
- .fourcc = V4L2_PIX_FMT_NV16M,
- .vdownsampling = { 1, 1 },
- .bit_depth = { 8, 8 },
- .color_enc = TGP_COLOR_ENC_YCBCR,
- .planes = 2,
- .buffers = 2,
- .data_offset = { PLANE0_DATA_OFFSET, 0 },
- },
- {
- .fourcc = V4L2_PIX_FMT_NV61M,
- .vdownsampling = { 1, 1 },
- .bit_depth = { 8, 8 },
- .color_enc = TGP_COLOR_ENC_YCBCR,
- .planes = 2,
- .buffers = 2,
- .data_offset = { 0, PLANE0_DATA_OFFSET },
- },
- {
- .fourcc = V4L2_PIX_FMT_YUV420M,
- .vdownsampling = { 1, 2, 2 },
- .bit_depth = { 8, 4, 4 },
- .color_enc = TGP_COLOR_ENC_YCBCR,
- .planes = 3,
- .buffers = 3,
- },
- {
- .fourcc = V4L2_PIX_FMT_YVU420M,
- .vdownsampling = { 1, 2, 2 },
- .bit_depth = { 8, 4, 4 },
- .color_enc = TGP_COLOR_ENC_YCBCR,
- .planes = 3,
- .buffers = 3,
- },
- {
- .fourcc = V4L2_PIX_FMT_NV12M,
- .vdownsampling = { 1, 2 },
- .bit_depth = { 8, 8 },
- .color_enc = TGP_COLOR_ENC_YCBCR,
- .planes = 2,
- .buffers = 2,
- },
- {
- .fourcc = V4L2_PIX_FMT_NV21M,
- .vdownsampling = { 1, 2 },
- .bit_depth = { 8, 8 },
- .color_enc = TGP_COLOR_ENC_YCBCR,
- .planes = 2,
- .buffers = 2,
- },
- {
- .fourcc = V4L2_PIX_FMT_YUV422M,
- .vdownsampling = { 1, 1, 1 },
- .bit_depth = { 8, 4, 4 },
- .color_enc = TGP_COLOR_ENC_YCBCR,
- .planes = 3,
- .buffers = 3,
- },
- {
- .fourcc = V4L2_PIX_FMT_YVU422M,
- .vdownsampling = { 1, 1, 1 },
- .bit_depth = { 8, 4, 4 },
- .color_enc = TGP_COLOR_ENC_YCBCR,
- .planes = 3,
- .buffers = 3,
- },
- {
- .fourcc = V4L2_PIX_FMT_YUV444M,
- .vdownsampling = { 1, 1, 1 },
- .bit_depth = { 8, 8, 8 },
- .color_enc = TGP_COLOR_ENC_YCBCR,
- .planes = 3,
- .buffers = 3,
- },
- {
- .fourcc = V4L2_PIX_FMT_YVU444M,
- .vdownsampling = { 1, 1, 1 },
- .bit_depth = { 8, 8, 8 },
- .color_enc = TGP_COLOR_ENC_YCBCR,
- .planes = 3,
- .buffers = 3,
- },
-};
-
-/* There are this many multiplanar formats in the list */
-#define VIVID_MPLANAR_FORMATS 10
-
-const struct vivid_fmt *vivid_get_format(struct vivid_dev *dev, u32 pixelformat)
-{
- const struct vivid_fmt *fmt;
- unsigned k;
-
- for (k = 0; k < ARRAY_SIZE(vivid_formats); k++) {
- fmt = &vivid_formats[k];
- if (fmt->fourcc == pixelformat)
- if (fmt->buffers == 1 || dev->multiplanar)
- return fmt;
- }
-
- return NULL;
-}
-
-bool vivid_vid_can_loop(struct vivid_dev *dev)
-{
- if (dev->src_rect.width != dev->sink_rect.width ||
- dev->src_rect.height != dev->sink_rect.height)
- return false;
- if (dev->fmt_cap->fourcc != dev->fmt_out->fourcc)
- return false;
- if (dev->field_cap != dev->field_out)
- return false;
- /*
- * While this can be supported, it is just too much work
- * to actually implement.
- */
- if (dev->field_cap == V4L2_FIELD_SEQ_TB ||
- dev->field_cap == V4L2_FIELD_SEQ_BT)
- return false;
- if (vivid_is_svid_cap(dev) && vivid_is_svid_out(dev)) {
- if (!(dev->std_cap[dev->input] & V4L2_STD_525_60) !=
- !(dev->std_out & V4L2_STD_525_60))
- return false;
- return true;
- }
- if (vivid_is_hdmi_cap(dev) && vivid_is_hdmi_out(dev))
- return true;
- return false;
-}
-
-void vivid_send_source_change(struct vivid_dev *dev, unsigned type)
-{
- struct v4l2_event ev = {
- .type = V4L2_EVENT_SOURCE_CHANGE,
- .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION,
- };
- unsigned i;
-
- for (i = 0; i < dev->num_inputs; i++) {
- ev.id = i;
- if (dev->input_type[i] == type) {
- if (video_is_registered(&dev->vid_cap_dev) && dev->has_vid_cap)
- v4l2_event_queue(&dev->vid_cap_dev, &ev);
- if (video_is_registered(&dev->vbi_cap_dev) && dev->has_vbi_cap)
- v4l2_event_queue(&dev->vbi_cap_dev, &ev);
- }
- }
-}
-
-/*
- * Conversion function that converts a single-planar format to a
- * single-plane multiplanar format.
- */
-void fmt_sp2mp(const struct v4l2_format *sp_fmt, struct v4l2_format *mp_fmt)
-{
- struct v4l2_pix_format_mplane *mp = &mp_fmt->fmt.pix_mp;
- struct v4l2_plane_pix_format *ppix = &mp->plane_fmt[0];
- const struct v4l2_pix_format *pix = &sp_fmt->fmt.pix;
- bool is_out = sp_fmt->type == V4L2_BUF_TYPE_VIDEO_OUTPUT;
-
- memset(mp->reserved, 0, sizeof(mp->reserved));
- mp_fmt->type = is_out ? V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE :
- V4L2_CAP_VIDEO_CAPTURE_MPLANE;
- mp->width = pix->width;
- mp->height = pix->height;
- mp->pixelformat = pix->pixelformat;
- mp->field = pix->field;
- mp->colorspace = pix->colorspace;
- mp->xfer_func = pix->xfer_func;
- /* Also copies hsv_enc */
- mp->ycbcr_enc = pix->ycbcr_enc;
- mp->quantization = pix->quantization;
- mp->num_planes = 1;
- mp->flags = pix->flags;
- ppix->sizeimage = pix->sizeimage;
- ppix->bytesperline = pix->bytesperline;
- memset(ppix->reserved, 0, sizeof(ppix->reserved));
-}
-
-int fmt_sp2mp_func(struct file *file, void *priv,
- struct v4l2_format *f, fmtfunc func)
-{
- struct v4l2_format fmt;
- struct v4l2_pix_format_mplane *mp = &fmt.fmt.pix_mp;
- struct v4l2_plane_pix_format *ppix = &mp->plane_fmt[0];
- struct v4l2_pix_format *pix = &f->fmt.pix;
- int ret;
-
- /* Converts to a mplane format */
- fmt_sp2mp(f, &fmt);
- /* Passes it to the generic mplane format function */
- ret = func(file, priv, &fmt);
- /* Copies back the mplane data to the single plane format */
- pix->width = mp->width;
- pix->height = mp->height;
- pix->pixelformat = mp->pixelformat;
- pix->field = mp->field;
- pix->colorspace = mp->colorspace;
- pix->xfer_func = mp->xfer_func;
- /* Also copies hsv_enc */
- pix->ycbcr_enc = mp->ycbcr_enc;
- pix->quantization = mp->quantization;
- pix->sizeimage = ppix->sizeimage;
- pix->bytesperline = ppix->bytesperline;
- pix->flags = mp->flags;
- return ret;
-}
-
-int vivid_vid_adjust_sel(unsigned flags, struct v4l2_rect *r)
-{
- unsigned w = r->width;
- unsigned h = r->height;
-
- /* sanitize w and h in case someone passes ~0 as the value */
- w &= 0xffff;
- h &= 0xffff;
- if (!(flags & V4L2_SEL_FLAG_LE)) {
- w++;
- h++;
- if (w < 2)
- w = 2;
- if (h < 2)
- h = 2;
- }
- if (!(flags & V4L2_SEL_FLAG_GE)) {
- if (w > MAX_WIDTH)
- w = MAX_WIDTH;
- if (h > MAX_HEIGHT)
- h = MAX_HEIGHT;
- }
- w = w & ~1;
- h = h & ~1;
- if (w < 2 || h < 2)
- return -ERANGE;
- if (w > MAX_WIDTH || h > MAX_HEIGHT)
- return -ERANGE;
- if (r->top < 0)
- r->top = 0;
- if (r->left < 0)
- r->left = 0;
- /* sanitize left and top in case someone passes ~0 as the value */
- r->left &= 0xfffe;
- r->top &= 0xfffe;
- if (r->left + w > MAX_WIDTH)
- r->left = MAX_WIDTH - w;
- if (r->top + h > MAX_HEIGHT)
- r->top = MAX_HEIGHT - h;
- if ((flags & (V4L2_SEL_FLAG_GE | V4L2_SEL_FLAG_LE)) ==
- (V4L2_SEL_FLAG_GE | V4L2_SEL_FLAG_LE) &&
- (r->width != w || r->height != h))
- return -ERANGE;
- r->width = w;
- r->height = h;
- return 0;
-}
-
-int vivid_enum_fmt_vid(struct file *file, void *priv,
- struct v4l2_fmtdesc *f)
-{
- struct vivid_dev *dev = video_drvdata(file);
- const struct vivid_fmt *fmt;
-
- if (f->index >= ARRAY_SIZE(vivid_formats) -
- (dev->multiplanar ? 0 : VIVID_MPLANAR_FORMATS))
- return -EINVAL;
-
- fmt = &vivid_formats[f->index];
-
- f->pixelformat = fmt->fourcc;
- return 0;
-}
-
-int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *id)
-{
- struct vivid_dev *dev = video_drvdata(file);
- struct video_device *vdev = video_devdata(file);
-
- if (vdev->vfl_dir == VFL_DIR_RX) {
- if (!vivid_is_sdtv_cap(dev))
- return -ENODATA;
- *id = dev->std_cap[dev->input];
- } else {
- if (!vivid_is_svid_out(dev))
- return -ENODATA;
- *id = dev->std_out;
- }
- return 0;
-}
-
-int vidioc_g_dv_timings(struct file *file, void *_fh,
- struct v4l2_dv_timings *timings)
-{
- struct vivid_dev *dev = video_drvdata(file);
- struct video_device *vdev = video_devdata(file);
-
- if (vdev->vfl_dir == VFL_DIR_RX) {
- if (!vivid_is_hdmi_cap(dev))
- return -ENODATA;
- *timings = dev->dv_timings_cap[dev->input];
- } else {
- if (!vivid_is_hdmi_out(dev))
- return -ENODATA;
- *timings = dev->dv_timings_out;
- }
- return 0;
-}
-
-int vidioc_enum_dv_timings(struct file *file, void *_fh,
- struct v4l2_enum_dv_timings *timings)
-{
- struct vivid_dev *dev = video_drvdata(file);
- struct video_device *vdev = video_devdata(file);
-
- if (vdev->vfl_dir == VFL_DIR_RX) {
- if (!vivid_is_hdmi_cap(dev))
- return -ENODATA;
- } else {
- if (!vivid_is_hdmi_out(dev))
- return -ENODATA;
- }
- return v4l2_enum_dv_timings_cap(timings, &vivid_dv_timings_cap,
- NULL, NULL);
-}
-
-int vidioc_dv_timings_cap(struct file *file, void *_fh,
- struct v4l2_dv_timings_cap *cap)
-{
- struct vivid_dev *dev = video_drvdata(file);
- struct video_device *vdev = video_devdata(file);
-
- if (vdev->vfl_dir == VFL_DIR_RX) {
- if (!vivid_is_hdmi_cap(dev))
- return -ENODATA;
- } else {
- if (!vivid_is_hdmi_out(dev))
- return -ENODATA;
- }
- *cap = vivid_dv_timings_cap;
- return 0;
-}
-
-int vidioc_g_edid(struct file *file, void *_fh,
- struct v4l2_edid *edid)
-{
- struct vivid_dev *dev = video_drvdata(file);
- struct video_device *vdev = video_devdata(file);
- struct cec_adapter *adap;
-
- memset(edid->reserved, 0, sizeof(edid->reserved));
- if (vdev->vfl_dir == VFL_DIR_RX) {
- if (edid->pad >= dev->num_inputs)
- return -EINVAL;
- if (dev->input_type[edid->pad] != HDMI)
- return -EINVAL;
- adap = dev->cec_rx_adap;
- } else {
- unsigned int bus_idx;
-
- if (edid->pad >= dev->num_outputs)
- return -EINVAL;
- if (dev->output_type[edid->pad] != HDMI)
- return -EINVAL;
- if (!dev->display_present[edid->pad])
- return -ENODATA;
- bus_idx = dev->cec_output2bus_map[edid->pad];
- adap = dev->cec_tx_adap[bus_idx];
- }
- if (edid->start_block == 0 && edid->blocks == 0) {
- edid->blocks = dev->edid_blocks;
- return 0;
- }
- if (dev->edid_blocks == 0)
- return -ENODATA;
- if (edid->start_block >= dev->edid_blocks)
- return -EINVAL;
- if (edid->blocks > dev->edid_blocks - edid->start_block)
- edid->blocks = dev->edid_blocks - edid->start_block;
- if (adap)
- v4l2_set_edid_phys_addr(dev->edid, dev->edid_blocks * 128, adap->phys_addr);
- memcpy(edid->edid, dev->edid + edid->start_block * 128, edid->blocks * 128);
- return 0;
-}
diff --git a/drivers/media/platform/vivid/vivid-vid-common.h b/drivers/media/platform/vivid/vivid-vid-common.h
deleted file mode 100644
index d908d97..0000000
--- a/drivers/media/platform/vivid/vivid-vid-common.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-/*
- * vivid-vid-common.h - common video support functions.
- *
- * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- */
-
-#ifndef _VIVID_VID_COMMON_H_
-#define _VIVID_VID_COMMON_H_
-
-typedef int (*fmtfunc)(struct file *file, void *priv, struct v4l2_format *f);
-
-/*
- * Conversion function that converts a single-planar format to a
- * single-plane multiplanar format.
- */
-void fmt_sp2mp(const struct v4l2_format *sp_fmt, struct v4l2_format *mp_fmt);
-int fmt_sp2mp_func(struct file *file, void *priv,
- struct v4l2_format *f, fmtfunc func);
-
-extern const struct v4l2_dv_timings_cap vivid_dv_timings_cap;
-
-const struct vivid_fmt *vivid_get_format(struct vivid_dev *dev, u32 pixelformat);
-
-bool vivid_vid_can_loop(struct vivid_dev *dev);
-void vivid_send_source_change(struct vivid_dev *dev, unsigned type);
-
-int vivid_vid_adjust_sel(unsigned flags, struct v4l2_rect *r);
-
-int vivid_enum_fmt_vid(struct file *file, void *priv, struct v4l2_fmtdesc *f);
-int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *id);
-int vidioc_g_dv_timings(struct file *file, void *_fh, struct v4l2_dv_timings *timings);
-int vidioc_enum_dv_timings(struct file *file, void *_fh, struct v4l2_enum_dv_timings *timings);
-int vidioc_dv_timings_cap(struct file *file, void *_fh, struct v4l2_dv_timings_cap *cap);
-int vidioc_g_edid(struct file *file, void *_fh, struct v4l2_edid *edid);
-int vidioc_subscribe_event(struct v4l2_fh *fh, const struct v4l2_event_subscription *sub);
-
-#endif
diff --git a/drivers/media/platform/vivid/vivid-vid-out.c b/drivers/media/platform/vivid/vivid-vid-out.c
deleted file mode 100644
index 54bb3a5..0000000
--- a/drivers/media/platform/vivid/vivid-vid-out.c
+++ /dev/null
@@ -1,1207 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * vivid-vid-out.c - video output support functions.
- *
- * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- */
-
-#include <linux/errno.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/videodev2.h>
-#include <linux/v4l2-dv-timings.h>
-#include <media/v4l2-common.h>
-#include <media/v4l2-event.h>
-#include <media/v4l2-dv-timings.h>
-#include <media/v4l2-rect.h>
-
-#include "vivid-core.h"
-#include "vivid-vid-common.h"
-#include "vivid-kthread-out.h"
-#include "vivid-vid-out.h"
-
-static int vid_out_queue_setup(struct vb2_queue *vq,
- unsigned *nbuffers, unsigned *nplanes,
- unsigned sizes[], struct device *alloc_devs[])
-{
- struct vivid_dev *dev = vb2_get_drv_priv(vq);
- const struct vivid_fmt *vfmt = dev->fmt_out;
- unsigned planes = vfmt->buffers;
- unsigned h = dev->fmt_out_rect.height;
- unsigned int size = dev->bytesperline_out[0] * h + vfmt->data_offset[0];
- unsigned p;
-
- for (p = vfmt->buffers; p < vfmt->planes; p++)
- size += dev->bytesperline_out[p] * h / vfmt->vdownsampling[p] +
- vfmt->data_offset[p];
-
- if (dev->field_out == V4L2_FIELD_ALTERNATE) {
- /*
- * You cannot use write() with FIELD_ALTERNATE since the field
- * information (TOP/BOTTOM) cannot be passed to the kernel.
- */
- if (vb2_fileio_is_active(vq))
- return -EINVAL;
- }
-
- if (dev->queue_setup_error) {
- /*
- * Error injection: test what happens if queue_setup() returns
- * an error.
- */
- dev->queue_setup_error = false;
- return -EINVAL;
- }
-
- if (*nplanes) {
- /*
- * Check if the number of requested planes match
- * the number of planes in the current format. You can't mix that.
- */
- if (*nplanes != planes)
- return -EINVAL;
- if (sizes[0] < size)
- return -EINVAL;
- for (p = 1; p < planes; p++) {
- if (sizes[p] < dev->bytesperline_out[p] * h +
- vfmt->data_offset[p])
- return -EINVAL;
- }
- } else {
- for (p = 0; p < planes; p++)
- sizes[p] = p ? dev->bytesperline_out[p] * h +
- vfmt->data_offset[p] : size;
- }
-
- if (vq->num_buffers + *nbuffers < 2)
- *nbuffers = 2 - vq->num_buffers;
-
- *nplanes = planes;
-
- dprintk(dev, 1, "%s: count=%d\n", __func__, *nbuffers);
- for (p = 0; p < planes; p++)
- dprintk(dev, 1, "%s: size[%u]=%u\n", __func__, p, sizes[p]);
- return 0;
-}
-
-static int vid_out_buf_out_validate(struct vb2_buffer *vb)
-{
- struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
- struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
-
- dprintk(dev, 1, "%s\n", __func__);
-
- if (dev->field_out != V4L2_FIELD_ALTERNATE)
- vbuf->field = dev->field_out;
- else if (vbuf->field != V4L2_FIELD_TOP &&
- vbuf->field != V4L2_FIELD_BOTTOM)
- return -EINVAL;
- return 0;
-}
-
-static int vid_out_buf_prepare(struct vb2_buffer *vb)
-{
- struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
- const struct vivid_fmt *vfmt = dev->fmt_out;
- unsigned int planes = vfmt->buffers;
- unsigned int h = dev->fmt_out_rect.height;
- unsigned int size = dev->bytesperline_out[0] * h;
- unsigned p;
-
- for (p = vfmt->buffers; p < vfmt->planes; p++)
- size += dev->bytesperline_out[p] * h / vfmt->vdownsampling[p];
-
- dprintk(dev, 1, "%s\n", __func__);
-
- if (WARN_ON(NULL == dev->fmt_out))
- return -EINVAL;
-
- if (dev->buf_prepare_error) {
- /*
- * Error injection: test what happens if buf_prepare() returns
- * an error.
- */
- dev->buf_prepare_error = false;
- return -EINVAL;
- }
-
- for (p = 0; p < planes; p++) {
- if (p)
- size = dev->bytesperline_out[p] * h;
- size += vb->planes[p].data_offset;
-
- if (vb2_get_plane_payload(vb, p) < size) {
- dprintk(dev, 1, "%s the payload is too small for plane %u (%lu < %u)\n",
- __func__, p, vb2_get_plane_payload(vb, p), size);
- return -EINVAL;
- }
- }
-
- return 0;
-}
-
-static void vid_out_buf_queue(struct vb2_buffer *vb)
-{
- struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
- struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
- struct vivid_buffer *buf = container_of(vbuf, struct vivid_buffer, vb);
-
- dprintk(dev, 1, "%s\n", __func__);
-
- spin_lock(&dev->slock);
- list_add_tail(&buf->list, &dev->vid_out_active);
- spin_unlock(&dev->slock);
-}
-
-static int vid_out_start_streaming(struct vb2_queue *vq, unsigned count)
-{
- struct vivid_dev *dev = vb2_get_drv_priv(vq);
- int err;
-
- if (vb2_is_streaming(&dev->vb_vid_cap_q))
- dev->can_loop_video = vivid_vid_can_loop(dev);
-
- dev->vid_out_seq_count = 0;
- dprintk(dev, 1, "%s\n", __func__);
- if (dev->start_streaming_error) {
- dev->start_streaming_error = false;
- err = -EINVAL;
- } else {
- err = vivid_start_generating_vid_out(dev, &dev->vid_out_streaming);
- }
- if (err) {
- struct vivid_buffer *buf, *tmp;
-
- list_for_each_entry_safe(buf, tmp, &dev->vid_out_active, list) {
- list_del(&buf->list);
- vb2_buffer_done(&buf->vb.vb2_buf,
- VB2_BUF_STATE_QUEUED);
- }
- }
- return err;
-}
-
-/* abort streaming and wait for last buffer */
-static void vid_out_stop_streaming(struct vb2_queue *vq)
-{
- struct vivid_dev *dev = vb2_get_drv_priv(vq);
-
- dprintk(dev, 1, "%s\n", __func__);
- vivid_stop_generating_vid_out(dev, &dev->vid_out_streaming);
- dev->can_loop_video = false;
-}
-
-static void vid_out_buf_request_complete(struct vb2_buffer *vb)
-{
- struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
-
- v4l2_ctrl_request_complete(vb->req_obj.req, &dev->ctrl_hdl_vid_out);
-}
-
-const struct vb2_ops vivid_vid_out_qops = {
- .queue_setup = vid_out_queue_setup,
- .buf_out_validate = vid_out_buf_out_validate,
- .buf_prepare = vid_out_buf_prepare,
- .buf_queue = vid_out_buf_queue,
- .start_streaming = vid_out_start_streaming,
- .stop_streaming = vid_out_stop_streaming,
- .buf_request_complete = vid_out_buf_request_complete,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
-};
-
-/*
- * Called whenever the format has to be reset which can occur when
- * changing outputs, standard, timings, etc.
- */
-void vivid_update_format_out(struct vivid_dev *dev)
-{
- struct v4l2_bt_timings *bt = &dev->dv_timings_out.bt;
- unsigned size, p;
- u64 pixelclock;
-
- switch (dev->output_type[dev->output]) {
- case SVID:
- default:
- dev->field_out = dev->tv_field_out;
- dev->sink_rect.width = 720;
- if (dev->std_out & V4L2_STD_525_60) {
- dev->sink_rect.height = 480;
- dev->timeperframe_vid_out = (struct v4l2_fract) { 1001, 30000 };
- dev->service_set_out = V4L2_SLICED_CAPTION_525;
- } else {
- dev->sink_rect.height = 576;
- dev->timeperframe_vid_out = (struct v4l2_fract) { 1000, 25000 };
- dev->service_set_out = V4L2_SLICED_WSS_625 | V4L2_SLICED_TELETEXT_B;
- }
- dev->colorspace_out = V4L2_COLORSPACE_SMPTE170M;
- break;
- case HDMI:
- dev->sink_rect.width = bt->width;
- dev->sink_rect.height = bt->height;
- size = V4L2_DV_BT_FRAME_WIDTH(bt) * V4L2_DV_BT_FRAME_HEIGHT(bt);
-
- if (can_reduce_fps(bt) && (bt->flags & V4L2_DV_FL_REDUCED_FPS))
- pixelclock = div_u64(bt->pixelclock * 1000, 1001);
- else
- pixelclock = bt->pixelclock;
-
- dev->timeperframe_vid_out = (struct v4l2_fract) {
- size / 100, (u32)pixelclock / 100
- };
- if (bt->interlaced)
- dev->field_out = V4L2_FIELD_ALTERNATE;
- else
- dev->field_out = V4L2_FIELD_NONE;
- if (!dev->dvi_d_out && (bt->flags & V4L2_DV_FL_IS_CE_VIDEO)) {
- if (bt->width == 720 && bt->height <= 576)
- dev->colorspace_out = V4L2_COLORSPACE_SMPTE170M;
- else
- dev->colorspace_out = V4L2_COLORSPACE_REC709;
- } else {
- dev->colorspace_out = V4L2_COLORSPACE_SRGB;
- }
- break;
- }
- dev->xfer_func_out = V4L2_XFER_FUNC_DEFAULT;
- dev->ycbcr_enc_out = V4L2_YCBCR_ENC_DEFAULT;
- dev->hsv_enc_out = V4L2_HSV_ENC_180;
- dev->quantization_out = V4L2_QUANTIZATION_DEFAULT;
- dev->compose_out = dev->sink_rect;
- dev->compose_bounds_out = dev->sink_rect;
- dev->crop_out = dev->compose_out;
- if (V4L2_FIELD_HAS_T_OR_B(dev->field_out))
- dev->crop_out.height /= 2;
- dev->fmt_out_rect = dev->crop_out;
- for (p = 0; p < dev->fmt_out->planes; p++)
- dev->bytesperline_out[p] =
- (dev->sink_rect.width * dev->fmt_out->bit_depth[p]) / 8;
-}
-
-/* Map the field to something that is valid for the current output */
-static enum v4l2_field vivid_field_out(struct vivid_dev *dev, enum v4l2_field field)
-{
- if (vivid_is_svid_out(dev)) {
- switch (field) {
- case V4L2_FIELD_INTERLACED_TB:
- case V4L2_FIELD_INTERLACED_BT:
- case V4L2_FIELD_SEQ_TB:
- case V4L2_FIELD_SEQ_BT:
- case V4L2_FIELD_ALTERNATE:
- return field;
- case V4L2_FIELD_INTERLACED:
- default:
- return V4L2_FIELD_INTERLACED;
- }
- }
- if (vivid_is_hdmi_out(dev))
- return dev->dv_timings_out.bt.interlaced ? V4L2_FIELD_ALTERNATE :
- V4L2_FIELD_NONE;
- return V4L2_FIELD_NONE;
-}
-
-static enum tpg_pixel_aspect vivid_get_pixel_aspect(const struct vivid_dev *dev)
-{
- if (vivid_is_svid_out(dev))
- return (dev->std_out & V4L2_STD_525_60) ?
- TPG_PIXEL_ASPECT_NTSC : TPG_PIXEL_ASPECT_PAL;
-
- if (vivid_is_hdmi_out(dev) &&
- dev->sink_rect.width == 720 && dev->sink_rect.height <= 576)
- return dev->sink_rect.height == 480 ?
- TPG_PIXEL_ASPECT_NTSC : TPG_PIXEL_ASPECT_PAL;
-
- return TPG_PIXEL_ASPECT_SQUARE;
-}
-
-int vivid_g_fmt_vid_out(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct vivid_dev *dev = video_drvdata(file);
- struct v4l2_pix_format_mplane *mp = &f->fmt.pix_mp;
- const struct vivid_fmt *fmt = dev->fmt_out;
- unsigned p;
-
- mp->width = dev->fmt_out_rect.width;
- mp->height = dev->fmt_out_rect.height;
- mp->field = dev->field_out;
- mp->pixelformat = fmt->fourcc;
- mp->colorspace = dev->colorspace_out;
- mp->xfer_func = dev->xfer_func_out;
- mp->ycbcr_enc = dev->ycbcr_enc_out;
- mp->quantization = dev->quantization_out;
- mp->num_planes = fmt->buffers;
- for (p = 0; p < mp->num_planes; p++) {
- mp->plane_fmt[p].bytesperline = dev->bytesperline_out[p];
- mp->plane_fmt[p].sizeimage =
- mp->plane_fmt[p].bytesperline * mp->height +
- fmt->data_offset[p];
- }
- for (p = fmt->buffers; p < fmt->planes; p++) {
- unsigned stride = dev->bytesperline_out[p];
-
- mp->plane_fmt[0].sizeimage +=
- (stride * mp->height) / fmt->vdownsampling[p];
- }
- return 0;
-}
-
-int vivid_try_fmt_vid_out(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct vivid_dev *dev = video_drvdata(file);
- struct v4l2_bt_timings *bt = &dev->dv_timings_out.bt;
- struct v4l2_pix_format_mplane *mp = &f->fmt.pix_mp;
- struct v4l2_plane_pix_format *pfmt = mp->plane_fmt;
- const struct vivid_fmt *fmt;
- unsigned bytesperline, max_bpl;
- unsigned factor = 1;
- unsigned w, h;
- unsigned p;
-
- fmt = vivid_get_format(dev, mp->pixelformat);
- if (!fmt) {
- dprintk(dev, 1, "Fourcc format (0x%08x) unknown.\n",
- mp->pixelformat);
- mp->pixelformat = V4L2_PIX_FMT_YUYV;
- fmt = vivid_get_format(dev, mp->pixelformat);
- }
-
- mp->field = vivid_field_out(dev, mp->field);
- if (vivid_is_svid_out(dev)) {
- w = 720;
- h = (dev->std_out & V4L2_STD_525_60) ? 480 : 576;
- } else {
- w = dev->sink_rect.width;
- h = dev->sink_rect.height;
- }
- if (V4L2_FIELD_HAS_T_OR_B(mp->field))
- factor = 2;
- if (!dev->has_scaler_out && !dev->has_crop_out && !dev->has_compose_out) {
- mp->width = w;
- mp->height = h / factor;
- } else {
- struct v4l2_rect r = { 0, 0, mp->width, mp->height * factor };
-
- v4l2_rect_set_min_size(&r, &vivid_min_rect);
- v4l2_rect_set_max_size(&r, &vivid_max_rect);
- if (dev->has_scaler_out && !dev->has_crop_out) {
- struct v4l2_rect max_r = { 0, 0, MAX_ZOOM * w, MAX_ZOOM * h };
-
- v4l2_rect_set_max_size(&r, &max_r);
- } else if (!dev->has_scaler_out && dev->has_compose_out && !dev->has_crop_out) {
- v4l2_rect_set_max_size(&r, &dev->sink_rect);
- } else if (!dev->has_scaler_out && !dev->has_compose_out) {
- v4l2_rect_set_min_size(&r, &dev->sink_rect);
- }
- mp->width = r.width;
- mp->height = r.height / factor;
- }
-
- /* This driver supports custom bytesperline values */
-
- mp->num_planes = fmt->buffers;
- for (p = 0; p < fmt->buffers; p++) {
- /* Calculate the minimum supported bytesperline value */
- bytesperline = (mp->width * fmt->bit_depth[p]) >> 3;
- /* Calculate the maximum supported bytesperline value */
- max_bpl = (MAX_ZOOM * MAX_WIDTH * fmt->bit_depth[p]) >> 3;
-
- if (pfmt[p].bytesperline > max_bpl)
- pfmt[p].bytesperline = max_bpl;
- if (pfmt[p].bytesperline < bytesperline)
- pfmt[p].bytesperline = bytesperline;
-
- pfmt[p].sizeimage = (pfmt[p].bytesperline * mp->height) /
- fmt->vdownsampling[p] + fmt->data_offset[p];
-
- memset(pfmt[p].reserved, 0, sizeof(pfmt[p].reserved));
- }
- for (p = fmt->buffers; p < fmt->planes; p++)
- pfmt[0].sizeimage += (pfmt[0].bytesperline * mp->height *
- (fmt->bit_depth[p] / fmt->vdownsampling[p])) /
- (fmt->bit_depth[0] / fmt->vdownsampling[0]);
-
- mp->xfer_func = V4L2_XFER_FUNC_DEFAULT;
- mp->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
- mp->quantization = V4L2_QUANTIZATION_DEFAULT;
- if (vivid_is_svid_out(dev)) {
- mp->colorspace = V4L2_COLORSPACE_SMPTE170M;
- } else if (dev->dvi_d_out || !(bt->flags & V4L2_DV_FL_IS_CE_VIDEO)) {
- mp->colorspace = V4L2_COLORSPACE_SRGB;
- if (dev->dvi_d_out)
- mp->quantization = V4L2_QUANTIZATION_LIM_RANGE;
- } else if (bt->width == 720 && bt->height <= 576) {
- mp->colorspace = V4L2_COLORSPACE_SMPTE170M;
- } else if (mp->colorspace != V4L2_COLORSPACE_SMPTE170M &&
- mp->colorspace != V4L2_COLORSPACE_REC709 &&
- mp->colorspace != V4L2_COLORSPACE_OPRGB &&
- mp->colorspace != V4L2_COLORSPACE_BT2020 &&
- mp->colorspace != V4L2_COLORSPACE_SRGB) {
- mp->colorspace = V4L2_COLORSPACE_REC709;
- }
- memset(mp->reserved, 0, sizeof(mp->reserved));
- return 0;
-}
-
-int vivid_s_fmt_vid_out(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct v4l2_pix_format_mplane *mp = &f->fmt.pix_mp;
- struct vivid_dev *dev = video_drvdata(file);
- struct v4l2_rect *crop = &dev->crop_out;
- struct v4l2_rect *compose = &dev->compose_out;
- struct vb2_queue *q = &dev->vb_vid_out_q;
- int ret = vivid_try_fmt_vid_out(file, priv, f);
- unsigned factor = 1;
- unsigned p;
-
- if (ret < 0)
- return ret;
-
- if (vb2_is_busy(q) &&
- (vivid_is_svid_out(dev) ||
- mp->width != dev->fmt_out_rect.width ||
- mp->height != dev->fmt_out_rect.height ||
- mp->pixelformat != dev->fmt_out->fourcc ||
- mp->field != dev->field_out)) {
- dprintk(dev, 1, "%s device busy\n", __func__);
- return -EBUSY;
- }
-
- /*
- * Allow for changing the colorspace on the fly. Useful for testing
- * purposes, and it is something that HDMI transmitters are able
- * to do.
- */
- if (vb2_is_busy(q))
- goto set_colorspace;
-
- dev->fmt_out = vivid_get_format(dev, mp->pixelformat);
- if (V4L2_FIELD_HAS_T_OR_B(mp->field))
- factor = 2;
-
- if (dev->has_scaler_out || dev->has_crop_out || dev->has_compose_out) {
- struct v4l2_rect r = { 0, 0, mp->width, mp->height };
-
- if (dev->has_scaler_out) {
- if (dev->has_crop_out)
- v4l2_rect_map_inside(crop, &r);
- else
- *crop = r;
- if (dev->has_compose_out && !dev->has_crop_out) {
- struct v4l2_rect min_r = {
- 0, 0,
- r.width / MAX_ZOOM,
- factor * r.height / MAX_ZOOM
- };
- struct v4l2_rect max_r = {
- 0, 0,
- r.width * MAX_ZOOM,
- factor * r.height * MAX_ZOOM
- };
-
- v4l2_rect_set_min_size(compose, &min_r);
- v4l2_rect_set_max_size(compose, &max_r);
- v4l2_rect_map_inside(compose, &dev->compose_bounds_out);
- } else if (dev->has_compose_out) {
- struct v4l2_rect min_r = {
- 0, 0,
- crop->width / MAX_ZOOM,
- factor * crop->height / MAX_ZOOM
- };
- struct v4l2_rect max_r = {
- 0, 0,
- crop->width * MAX_ZOOM,
- factor * crop->height * MAX_ZOOM
- };
-
- v4l2_rect_set_min_size(compose, &min_r);
- v4l2_rect_set_max_size(compose, &max_r);
- v4l2_rect_map_inside(compose, &dev->compose_bounds_out);
- }
- } else if (dev->has_compose_out && !dev->has_crop_out) {
- v4l2_rect_set_size_to(crop, &r);
- r.height *= factor;
- v4l2_rect_set_size_to(compose, &r);
- v4l2_rect_map_inside(compose, &dev->compose_bounds_out);
- } else if (!dev->has_compose_out) {
- v4l2_rect_map_inside(crop, &r);
- r.height /= factor;
- v4l2_rect_set_size_to(compose, &r);
- } else {
- r.height *= factor;
- v4l2_rect_set_max_size(compose, &r);
- v4l2_rect_map_inside(compose, &dev->compose_bounds_out);
- crop->top *= factor;
- crop->height *= factor;
- v4l2_rect_set_size_to(crop, compose);
- v4l2_rect_map_inside(crop, &r);
- crop->top /= factor;
- crop->height /= factor;
- }
- } else {
- struct v4l2_rect r = { 0, 0, mp->width, mp->height };
-
- v4l2_rect_set_size_to(crop, &r);
- r.height /= factor;
- v4l2_rect_set_size_to(compose, &r);
- }
-
- dev->fmt_out_rect.width = mp->width;
- dev->fmt_out_rect.height = mp->height;
- for (p = 0; p < mp->num_planes; p++)
- dev->bytesperline_out[p] = mp->plane_fmt[p].bytesperline;
- for (p = dev->fmt_out->buffers; p < dev->fmt_out->planes; p++)
- dev->bytesperline_out[p] =
- (dev->bytesperline_out[0] * dev->fmt_out->bit_depth[p]) /
- dev->fmt_out->bit_depth[0];
- dev->field_out = mp->field;
- if (vivid_is_svid_out(dev))
- dev->tv_field_out = mp->field;
-
-set_colorspace:
- dev->colorspace_out = mp->colorspace;
- dev->xfer_func_out = mp->xfer_func;
- dev->ycbcr_enc_out = mp->ycbcr_enc;
- dev->quantization_out = mp->quantization;
- if (dev->loop_video) {
- vivid_send_source_change(dev, SVID);
- vivid_send_source_change(dev, HDMI);
- }
- return 0;
-}
-
-int vidioc_g_fmt_vid_out_mplane(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct vivid_dev *dev = video_drvdata(file);
-
- if (!dev->multiplanar)
- return -ENOTTY;
- return vivid_g_fmt_vid_out(file, priv, f);
-}
-
-int vidioc_try_fmt_vid_out_mplane(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct vivid_dev *dev = video_drvdata(file);
-
- if (!dev->multiplanar)
- return -ENOTTY;
- return vivid_try_fmt_vid_out(file, priv, f);
-}
-
-int vidioc_s_fmt_vid_out_mplane(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct vivid_dev *dev = video_drvdata(file);
-
- if (!dev->multiplanar)
- return -ENOTTY;
- return vivid_s_fmt_vid_out(file, priv, f);
-}
-
-int vidioc_g_fmt_vid_out(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct vivid_dev *dev = video_drvdata(file);
-
- if (dev->multiplanar)
- return -ENOTTY;
- return fmt_sp2mp_func(file, priv, f, vivid_g_fmt_vid_out);
-}
-
-int vidioc_try_fmt_vid_out(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct vivid_dev *dev = video_drvdata(file);
-
- if (dev->multiplanar)
- return -ENOTTY;
- return fmt_sp2mp_func(file, priv, f, vivid_try_fmt_vid_out);
-}
-
-int vidioc_s_fmt_vid_out(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct vivid_dev *dev = video_drvdata(file);
-
- if (dev->multiplanar)
- return -ENOTTY;
- return fmt_sp2mp_func(file, priv, f, vivid_s_fmt_vid_out);
-}
-
-int vivid_vid_out_g_selection(struct file *file, void *priv,
- struct v4l2_selection *sel)
-{
- struct vivid_dev *dev = video_drvdata(file);
-
- if (!dev->has_crop_out && !dev->has_compose_out)
- return -ENOTTY;
- if (sel->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
- return -EINVAL;
-
- sel->r.left = sel->r.top = 0;
- switch (sel->target) {
- case V4L2_SEL_TGT_CROP:
- if (!dev->has_crop_out)
- return -EINVAL;
- sel->r = dev->crop_out;
- break;
- case V4L2_SEL_TGT_CROP_DEFAULT:
- if (!dev->has_crop_out)
- return -EINVAL;
- sel->r = dev->fmt_out_rect;
- break;
- case V4L2_SEL_TGT_CROP_BOUNDS:
- if (!dev->has_crop_out)
- return -EINVAL;
- sel->r = vivid_max_rect;
- break;
- case V4L2_SEL_TGT_COMPOSE:
- if (!dev->has_compose_out)
- return -EINVAL;
- sel->r = dev->compose_out;
- break;
- case V4L2_SEL_TGT_COMPOSE_DEFAULT:
- case V4L2_SEL_TGT_COMPOSE_BOUNDS:
- if (!dev->has_compose_out)
- return -EINVAL;
- sel->r = dev->sink_rect;
- break;
- default:
- return -EINVAL;
- }
- return 0;
-}
-
-int vivid_vid_out_s_selection(struct file *file, void *fh, struct v4l2_selection *s)
-{
- struct vivid_dev *dev = video_drvdata(file);
- struct v4l2_rect *crop = &dev->crop_out;
- struct v4l2_rect *compose = &dev->compose_out;
- unsigned factor = V4L2_FIELD_HAS_T_OR_B(dev->field_out) ? 2 : 1;
- int ret;
-
- if (!dev->has_crop_out && !dev->has_compose_out)
- return -ENOTTY;
- if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
- return -EINVAL;
-
- switch (s->target) {
- case V4L2_SEL_TGT_CROP:
- if (!dev->has_crop_out)
- return -EINVAL;
- ret = vivid_vid_adjust_sel(s->flags, &s->r);
- if (ret)
- return ret;
- v4l2_rect_set_min_size(&s->r, &vivid_min_rect);
- v4l2_rect_set_max_size(&s->r, &dev->fmt_out_rect);
- if (dev->has_scaler_out) {
- struct v4l2_rect max_rect = {
- 0, 0,
- dev->sink_rect.width * MAX_ZOOM,
- (dev->sink_rect.height / factor) * MAX_ZOOM
- };
-
- v4l2_rect_set_max_size(&s->r, &max_rect);
- if (dev->has_compose_out) {
- struct v4l2_rect min_rect = {
- 0, 0,
- s->r.width / MAX_ZOOM,
- (s->r.height * factor) / MAX_ZOOM
- };
- struct v4l2_rect max_rect = {
- 0, 0,
- s->r.width * MAX_ZOOM,
- (s->r.height * factor) * MAX_ZOOM
- };
-
- v4l2_rect_set_min_size(compose, &min_rect);
- v4l2_rect_set_max_size(compose, &max_rect);
- v4l2_rect_map_inside(compose, &dev->compose_bounds_out);
- }
- } else if (dev->has_compose_out) {
- s->r.top *= factor;
- s->r.height *= factor;
- v4l2_rect_set_max_size(&s->r, &dev->sink_rect);
- v4l2_rect_set_size_to(compose, &s->r);
- v4l2_rect_map_inside(compose, &dev->compose_bounds_out);
- s->r.top /= factor;
- s->r.height /= factor;
- } else {
- v4l2_rect_set_size_to(&s->r, &dev->sink_rect);
- s->r.height /= factor;
- }
- v4l2_rect_map_inside(&s->r, &dev->fmt_out_rect);
- *crop = s->r;
- break;
- case V4L2_SEL_TGT_COMPOSE:
- if (!dev->has_compose_out)
- return -EINVAL;
- ret = vivid_vid_adjust_sel(s->flags, &s->r);
- if (ret)
- return ret;
- v4l2_rect_set_min_size(&s->r, &vivid_min_rect);
- v4l2_rect_set_max_size(&s->r, &dev->sink_rect);
- v4l2_rect_map_inside(&s->r, &dev->compose_bounds_out);
- s->r.top /= factor;
- s->r.height /= factor;
- if (dev->has_scaler_out) {
- struct v4l2_rect fmt = dev->fmt_out_rect;
- struct v4l2_rect max_rect = {
- 0, 0,
- s->r.width * MAX_ZOOM,
- s->r.height * MAX_ZOOM
- };
- struct v4l2_rect min_rect = {
- 0, 0,
- s->r.width / MAX_ZOOM,
- s->r.height / MAX_ZOOM
- };
-
- v4l2_rect_set_min_size(&fmt, &min_rect);
- if (!dev->has_crop_out)
- v4l2_rect_set_max_size(&fmt, &max_rect);
- if (!v4l2_rect_same_size(&dev->fmt_out_rect, &fmt) &&
- vb2_is_busy(&dev->vb_vid_out_q))
- return -EBUSY;
- if (dev->has_crop_out) {
- v4l2_rect_set_min_size(crop, &min_rect);
- v4l2_rect_set_max_size(crop, &max_rect);
- }
- dev->fmt_out_rect = fmt;
- } else if (dev->has_crop_out) {
- struct v4l2_rect fmt = dev->fmt_out_rect;
-
- v4l2_rect_set_min_size(&fmt, &s->r);
- if (!v4l2_rect_same_size(&dev->fmt_out_rect, &fmt) &&
- vb2_is_busy(&dev->vb_vid_out_q))
- return -EBUSY;
- dev->fmt_out_rect = fmt;
- v4l2_rect_set_size_to(crop, &s->r);
- v4l2_rect_map_inside(crop, &dev->fmt_out_rect);
- } else {
- if (!v4l2_rect_same_size(&s->r, &dev->fmt_out_rect) &&
- vb2_is_busy(&dev->vb_vid_out_q))
- return -EBUSY;
- v4l2_rect_set_size_to(&dev->fmt_out_rect, &s->r);
- v4l2_rect_set_size_to(crop, &s->r);
- crop->height /= factor;
- v4l2_rect_map_inside(crop, &dev->fmt_out_rect);
- }
- s->r.top *= factor;
- s->r.height *= factor;
- if (dev->bitmap_out && (compose->width != s->r.width ||
- compose->height != s->r.height)) {
- vfree(dev->bitmap_out);
- dev->bitmap_out = NULL;
- }
- *compose = s->r;
- break;
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
-int vivid_vid_out_g_pixelaspect(struct file *file, void *priv,
- int type, struct v4l2_fract *f)
-{
- struct vivid_dev *dev = video_drvdata(file);
-
- if (type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
- return -EINVAL;
-
- switch (vivid_get_pixel_aspect(dev)) {
- case TPG_PIXEL_ASPECT_NTSC:
- f->numerator = 11;
- f->denominator = 10;
- break;
- case TPG_PIXEL_ASPECT_PAL:
- f->numerator = 54;
- f->denominator = 59;
- break;
- default:
- break;
- }
- return 0;
-}
-
-int vidioc_g_fmt_vid_out_overlay(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct vivid_dev *dev = video_drvdata(file);
- const struct v4l2_rect *compose = &dev->compose_out;
- struct v4l2_window *win = &f->fmt.win;
- unsigned clipcount = win->clipcount;
-
- if (!dev->has_fb)
- return -EINVAL;
- win->w.top = dev->overlay_out_top;
- win->w.left = dev->overlay_out_left;
- win->w.width = compose->width;
- win->w.height = compose->height;
- win->clipcount = dev->clipcount_out;
- win->field = V4L2_FIELD_ANY;
- win->chromakey = dev->chromakey_out;
- win->global_alpha = dev->global_alpha_out;
- if (clipcount > dev->clipcount_out)
- clipcount = dev->clipcount_out;
- if (dev->bitmap_out == NULL)
- win->bitmap = NULL;
- else if (win->bitmap) {
- if (copy_to_user(win->bitmap, dev->bitmap_out,
- ((dev->compose_out.width + 7) / 8) * dev->compose_out.height))
- return -EFAULT;
- }
- if (clipcount && win->clips) {
- if (copy_to_user(win->clips, dev->clips_out,
- clipcount * sizeof(dev->clips_out[0])))
- return -EFAULT;
- }
- return 0;
-}
-
-int vidioc_try_fmt_vid_out_overlay(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct vivid_dev *dev = video_drvdata(file);
- const struct v4l2_rect *compose = &dev->compose_out;
- struct v4l2_window *win = &f->fmt.win;
- int i, j;
-
- if (!dev->has_fb)
- return -EINVAL;
- win->w.left = clamp_t(int, win->w.left,
- -dev->display_width, dev->display_width);
- win->w.top = clamp_t(int, win->w.top,
- -dev->display_height, dev->display_height);
- win->w.width = compose->width;
- win->w.height = compose->height;
- /*
- * It makes no sense for an OSD to overlay only top or bottom fields,
- * so always set this to ANY.
- */
- win->field = V4L2_FIELD_ANY;
- if (win->clipcount && !win->clips)
- win->clipcount = 0;
- if (win->clipcount > MAX_CLIPS)
- win->clipcount = MAX_CLIPS;
- if (win->clipcount) {
- if (copy_from_user(dev->try_clips_out, win->clips,
- win->clipcount * sizeof(dev->clips_out[0])))
- return -EFAULT;
- for (i = 0; i < win->clipcount; i++) {
- struct v4l2_rect *r = &dev->try_clips_out[i].c;
-
- r->top = clamp_t(s32, r->top, 0, dev->display_height - 1);
- r->height = clamp_t(s32, r->height, 1, dev->display_height - r->top);
- r->left = clamp_t(u32, r->left, 0, dev->display_width - 1);
- r->width = clamp_t(u32, r->width, 1, dev->display_width - r->left);
- }
- /*
- * Yeah, so sue me, it's an O(n^2) algorithm. But n is a small
- * number and it's typically a one-time deal.
- */
- for (i = 0; i < win->clipcount - 1; i++) {
- struct v4l2_rect *r1 = &dev->try_clips_out[i].c;
-
- for (j = i + 1; j < win->clipcount; j++) {
- struct v4l2_rect *r2 = &dev->try_clips_out[j].c;
-
- if (v4l2_rect_overlap(r1, r2))
- return -EINVAL;
- }
- }
- if (copy_to_user(win->clips, dev->try_clips_out,
- win->clipcount * sizeof(dev->clips_out[0])))
- return -EFAULT;
- }
- return 0;
-}
-
-int vidioc_s_fmt_vid_out_overlay(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct vivid_dev *dev = video_drvdata(file);
- const struct v4l2_rect *compose = &dev->compose_out;
- struct v4l2_window *win = &f->fmt.win;
- int ret = vidioc_try_fmt_vid_out_overlay(file, priv, f);
- unsigned bitmap_size = ((compose->width + 7) / 8) * compose->height;
- unsigned clips_size = win->clipcount * sizeof(dev->clips_out[0]);
- void *new_bitmap = NULL;
-
- if (ret)
- return ret;
-
- if (win->bitmap) {
- new_bitmap = vzalloc(bitmap_size);
-
- if (!new_bitmap)
- return -ENOMEM;
- if (copy_from_user(new_bitmap, win->bitmap, bitmap_size)) {
- vfree(new_bitmap);
- return -EFAULT;
- }
- }
-
- dev->overlay_out_top = win->w.top;
- dev->overlay_out_left = win->w.left;
- vfree(dev->bitmap_out);
- dev->bitmap_out = new_bitmap;
- dev->clipcount_out = win->clipcount;
- if (dev->clipcount_out)
- memcpy(dev->clips_out, dev->try_clips_out, clips_size);
- dev->chromakey_out = win->chromakey;
- dev->global_alpha_out = win->global_alpha;
- return ret;
-}
-
-int vivid_vid_out_overlay(struct file *file, void *fh, unsigned i)
-{
- struct vivid_dev *dev = video_drvdata(file);
-
- if (i && !dev->fmt_out->can_do_overlay) {
- dprintk(dev, 1, "unsupported output format for output overlay\n");
- return -EINVAL;
- }
-
- dev->overlay_out_enabled = i;
- return 0;
-}
-
-int vivid_vid_out_g_fbuf(struct file *file, void *fh,
- struct v4l2_framebuffer *a)
-{
- struct vivid_dev *dev = video_drvdata(file);
-
- a->capability = V4L2_FBUF_CAP_EXTERNOVERLAY |
- V4L2_FBUF_CAP_BITMAP_CLIPPING |
- V4L2_FBUF_CAP_LIST_CLIPPING |
- V4L2_FBUF_CAP_CHROMAKEY |
- V4L2_FBUF_CAP_SRC_CHROMAKEY |
- V4L2_FBUF_CAP_GLOBAL_ALPHA |
- V4L2_FBUF_CAP_LOCAL_ALPHA |
- V4L2_FBUF_CAP_LOCAL_INV_ALPHA;
- a->flags = V4L2_FBUF_FLAG_OVERLAY | dev->fbuf_out_flags;
- a->base = (void *)dev->video_pbase;
- a->fmt.width = dev->display_width;
- a->fmt.height = dev->display_height;
- if (dev->fb_defined.green.length == 5)
- a->fmt.pixelformat = V4L2_PIX_FMT_ARGB555;
- else
- a->fmt.pixelformat = V4L2_PIX_FMT_RGB565;
- a->fmt.bytesperline = dev->display_byte_stride;
- a->fmt.sizeimage = a->fmt.height * a->fmt.bytesperline;
- a->fmt.field = V4L2_FIELD_NONE;
- a->fmt.colorspace = V4L2_COLORSPACE_SRGB;
- a->fmt.priv = 0;
- return 0;
-}
-
-int vivid_vid_out_s_fbuf(struct file *file, void *fh,
- const struct v4l2_framebuffer *a)
-{
- struct vivid_dev *dev = video_drvdata(file);
- const unsigned chroma_flags = V4L2_FBUF_FLAG_CHROMAKEY |
- V4L2_FBUF_FLAG_SRC_CHROMAKEY;
- const unsigned alpha_flags = V4L2_FBUF_FLAG_GLOBAL_ALPHA |
- V4L2_FBUF_FLAG_LOCAL_ALPHA |
- V4L2_FBUF_FLAG_LOCAL_INV_ALPHA;
-
-
- if ((a->flags & chroma_flags) == chroma_flags)
- return -EINVAL;
- switch (a->flags & alpha_flags) {
- case 0:
- case V4L2_FBUF_FLAG_GLOBAL_ALPHA:
- case V4L2_FBUF_FLAG_LOCAL_ALPHA:
- case V4L2_FBUF_FLAG_LOCAL_INV_ALPHA:
- break;
- default:
- return -EINVAL;
- }
- dev->fbuf_out_flags &= ~(chroma_flags | alpha_flags);
- dev->fbuf_out_flags |= a->flags & (chroma_flags | alpha_flags);
- return 0;
-}
-
-static const struct v4l2_audioout vivid_audio_outputs[] = {
- { 0, "Line-Out 1" },
- { 1, "Line-Out 2" },
-};
-
-int vidioc_enum_output(struct file *file, void *priv,
- struct v4l2_output *out)
-{
- struct vivid_dev *dev = video_drvdata(file);
-
- if (out->index >= dev->num_outputs)
- return -EINVAL;
-
- out->type = V4L2_OUTPUT_TYPE_ANALOG;
- switch (dev->output_type[out->index]) {
- case SVID:
- snprintf(out->name, sizeof(out->name), "S-Video %u",
- dev->output_name_counter[out->index]);
- out->std = V4L2_STD_ALL;
- if (dev->has_audio_outputs)
- out->audioset = (1 << ARRAY_SIZE(vivid_audio_outputs)) - 1;
- out->capabilities = V4L2_OUT_CAP_STD;
- break;
- case HDMI:
- snprintf(out->name, sizeof(out->name), "HDMI %u",
- dev->output_name_counter[out->index]);
- out->capabilities = V4L2_OUT_CAP_DV_TIMINGS;
- break;
- }
- return 0;
-}
-
-int vidioc_g_output(struct file *file, void *priv, unsigned *o)
-{
- struct vivid_dev *dev = video_drvdata(file);
-
- *o = dev->output;
- return 0;
-}
-
-int vidioc_s_output(struct file *file, void *priv, unsigned o)
-{
- struct vivid_dev *dev = video_drvdata(file);
-
- if (o >= dev->num_outputs)
- return -EINVAL;
-
- if (o == dev->output)
- return 0;
-
- if (vb2_is_busy(&dev->vb_vid_out_q) || vb2_is_busy(&dev->vb_vbi_out_q))
- return -EBUSY;
-
- dev->output = o;
- dev->tv_audio_output = 0;
- if (dev->output_type[o] == SVID)
- dev->vid_out_dev.tvnorms = V4L2_STD_ALL;
- else
- dev->vid_out_dev.tvnorms = 0;
-
- dev->vbi_out_dev.tvnorms = dev->vid_out_dev.tvnorms;
- vivid_update_format_out(dev);
-
- v4l2_ctrl_activate(dev->ctrl_display_present, vivid_is_hdmi_out(dev));
- if (vivid_is_hdmi_out(dev))
- v4l2_ctrl_s_ctrl(dev->ctrl_display_present,
- dev->display_present[dev->output]);
-
- return 0;
-}
-
-int vidioc_enumaudout(struct file *file, void *fh, struct v4l2_audioout *vout)
-{
- if (vout->index >= ARRAY_SIZE(vivid_audio_outputs))
- return -EINVAL;
- *vout = vivid_audio_outputs[vout->index];
- return 0;
-}
-
-int vidioc_g_audout(struct file *file, void *fh, struct v4l2_audioout *vout)
-{
- struct vivid_dev *dev = video_drvdata(file);
-
- if (!vivid_is_svid_out(dev))
- return -EINVAL;
- *vout = vivid_audio_outputs[dev->tv_audio_output];
- return 0;
-}
-
-int vidioc_s_audout(struct file *file, void *fh, const struct v4l2_audioout *vout)
-{
- struct vivid_dev *dev = video_drvdata(file);
-
- if (!vivid_is_svid_out(dev))
- return -EINVAL;
- if (vout->index >= ARRAY_SIZE(vivid_audio_outputs))
- return -EINVAL;
- dev->tv_audio_output = vout->index;
- return 0;
-}
-
-int vivid_vid_out_s_std(struct file *file, void *priv, v4l2_std_id id)
-{
- struct vivid_dev *dev = video_drvdata(file);
-
- if (!vivid_is_svid_out(dev))
- return -ENODATA;
- if (dev->std_out == id)
- return 0;
- if (vb2_is_busy(&dev->vb_vid_out_q) || vb2_is_busy(&dev->vb_vbi_out_q))
- return -EBUSY;
- dev->std_out = id;
- vivid_update_format_out(dev);
- return 0;
-}
-
-static bool valid_cvt_gtf_timings(struct v4l2_dv_timings *timings)
-{
- struct v4l2_bt_timings *bt = &timings->bt;
-
- if ((bt->standards & (V4L2_DV_BT_STD_CVT | V4L2_DV_BT_STD_GTF)) &&
- v4l2_valid_dv_timings(timings, &vivid_dv_timings_cap, NULL, NULL))
- return true;
-
- return false;
-}
-
-int vivid_vid_out_s_dv_timings(struct file *file, void *_fh,
- struct v4l2_dv_timings *timings)
-{
- struct vivid_dev *dev = video_drvdata(file);
- if (!vivid_is_hdmi_out(dev))
- return -ENODATA;
- if (!v4l2_find_dv_timings_cap(timings, &vivid_dv_timings_cap,
- 0, NULL, NULL) &&
- !valid_cvt_gtf_timings(timings))
- return -EINVAL;
- if (v4l2_match_dv_timings(timings, &dev->dv_timings_out, 0, true))
- return 0;
- if (vb2_is_busy(&dev->vb_vid_out_q))
- return -EBUSY;
- dev->dv_timings_out = *timings;
- vivid_update_format_out(dev);
- return 0;
-}
-
-int vivid_vid_out_g_parm(struct file *file, void *priv,
- struct v4l2_streamparm *parm)
-{
- struct vivid_dev *dev = video_drvdata(file);
-
- if (parm->type != (dev->multiplanar ?
- V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE :
- V4L2_BUF_TYPE_VIDEO_OUTPUT))
- return -EINVAL;
-
- parm->parm.output.capability = V4L2_CAP_TIMEPERFRAME;
- parm->parm.output.timeperframe = dev->timeperframe_vid_out;
- parm->parm.output.writebuffers = 1;
-
- return 0;
-}
-
-int vidioc_subscribe_event(struct v4l2_fh *fh,
- const struct v4l2_event_subscription *sub)
-{
- switch (sub->type) {
- case V4L2_EVENT_SOURCE_CHANGE:
- if (fh->vdev->vfl_dir == VFL_DIR_RX)
- return v4l2_src_change_event_subscribe(fh, sub);
- break;
- default:
- return v4l2_ctrl_subscribe_event(fh, sub);
- }
- return -EINVAL;
-}
diff --git a/drivers/media/platform/vivid/vivid-vid-out.h b/drivers/media/platform/vivid/vivid-vid-out.h
deleted file mode 100644
index 8d56314..0000000
--- a/drivers/media/platform/vivid/vivid-vid-out.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-/*
- * vivid-vid-out.h - video output support functions.
- *
- * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- */
-
-#ifndef _VIVID_VID_OUT_H_
-#define _VIVID_VID_OUT_H_
-
-extern const struct vb2_ops vivid_vid_out_qops;
-
-void vivid_update_format_out(struct vivid_dev *dev);
-
-int vivid_g_fmt_vid_out(struct file *file, void *priv, struct v4l2_format *f);
-int vivid_try_fmt_vid_out(struct file *file, void *priv, struct v4l2_format *f);
-int vivid_s_fmt_vid_out(struct file *file, void *priv, struct v4l2_format *f);
-int vidioc_g_fmt_vid_out_mplane(struct file *file, void *priv, struct v4l2_format *f);
-int vidioc_try_fmt_vid_out_mplane(struct file *file, void *priv, struct v4l2_format *f);
-int vidioc_s_fmt_vid_out_mplane(struct file *file, void *priv, struct v4l2_format *f);
-int vidioc_g_fmt_vid_out(struct file *file, void *priv, struct v4l2_format *f);
-int vidioc_try_fmt_vid_out(struct file *file, void *priv, struct v4l2_format *f);
-int vidioc_s_fmt_vid_out(struct file *file, void *priv, struct v4l2_format *f);
-int vivid_vid_out_g_selection(struct file *file, void *priv, struct v4l2_selection *sel);
-int vivid_vid_out_s_selection(struct file *file, void *fh, struct v4l2_selection *s);
-int vivid_vid_out_g_pixelaspect(struct file *file, void *priv, int type, struct v4l2_fract *f);
-int vidioc_enum_fmt_vid_out_overlay(struct file *file, void *priv, struct v4l2_fmtdesc *f);
-int vidioc_g_fmt_vid_out_overlay(struct file *file, void *priv, struct v4l2_format *f);
-int vidioc_try_fmt_vid_out_overlay(struct file *file, void *priv, struct v4l2_format *f);
-int vidioc_s_fmt_vid_out_overlay(struct file *file, void *priv, struct v4l2_format *f);
-int vivid_vid_out_overlay(struct file *file, void *fh, unsigned i);
-int vivid_vid_out_g_fbuf(struct file *file, void *fh, struct v4l2_framebuffer *a);
-int vivid_vid_out_s_fbuf(struct file *file, void *fh, const struct v4l2_framebuffer *a);
-int vidioc_enum_output(struct file *file, void *priv, struct v4l2_output *out);
-int vidioc_g_output(struct file *file, void *priv, unsigned *i);
-int vidioc_s_output(struct file *file, void *priv, unsigned i);
-int vidioc_enumaudout(struct file *file, void *fh, struct v4l2_audioout *vout);
-int vidioc_g_audout(struct file *file, void *fh, struct v4l2_audioout *vout);
-int vidioc_s_audout(struct file *file, void *fh, const struct v4l2_audioout *vout);
-int vivid_vid_out_s_std(struct file *file, void *priv, v4l2_std_id id);
-int vivid_vid_out_s_dv_timings(struct file *file, void *_fh, struct v4l2_dv_timings *timings);
-int vivid_vid_out_g_parm(struct file *file, void *priv, struct v4l2_streamparm *parm);
-
-#endif
diff --git a/drivers/media/platform/vsp1/vsp1_drm.c b/drivers/media/platform/vsp1/vsp1_drm.c
index 1c00688..06f74d4 100644
--- a/drivers/media/platform/vsp1/vsp1_drm.c
+++ b/drivers/media/platform/vsp1/vsp1_drm.c
@@ -912,8 +912,8 @@
* skip cache sync. This will need to be revisited when support for
* non-coherent buffers will be added to the DU driver.
*/
- return dma_map_sg_attrs(vsp1->bus_master, sgt->sgl, sgt->nents,
- DMA_TO_DEVICE, DMA_ATTR_SKIP_CPU_SYNC);
+ return dma_map_sgtable(vsp1->bus_master, sgt, DMA_TO_DEVICE,
+ DMA_ATTR_SKIP_CPU_SYNC);
}
EXPORT_SYMBOL_GPL(vsp1_du_map_sg);
@@ -921,8 +921,8 @@
{
struct vsp1_device *vsp1 = dev_get_drvdata(dev);
- dma_unmap_sg_attrs(vsp1->bus_master, sgt->sgl, sgt->nents,
- DMA_TO_DEVICE, DMA_ATTR_SKIP_CPU_SYNC);
+ dma_unmap_sgtable(vsp1->bus_master, sgt, DMA_TO_DEVICE,
+ DMA_ATTR_SKIP_CPU_SYNC);
}
EXPORT_SYMBOL_GPL(vsp1_du_unmap_sg);
diff --git a/drivers/media/platform/vsp1/vsp1_histo.c b/drivers/media/platform/vsp1/vsp1_histo.c
index 30d751f..a91e142 100644
--- a/drivers/media/platform/vsp1/vsp1_histo.c
+++ b/drivers/media/platform/vsp1/vsp1_histo.c
@@ -551,7 +551,7 @@
histo->video.fops = &histo_v4l2_fops;
snprintf(histo->video.name, sizeof(histo->video.name),
"%s histo", histo->entity.subdev.name);
- histo->video.vfl_type = VFL_TYPE_GRABBER;
+ histo->video.vfl_type = VFL_TYPE_VIDEO;
histo->video.release = video_device_release_empty;
histo->video.ioctl_ops = &histo_v4l2_ioctl_ops;
histo->video.device_caps = V4L2_CAP_META_CAPTURE | V4L2_CAP_STREAMING;
@@ -576,7 +576,7 @@
/* ... and register the video device. */
histo->video.queue = &histo->queue;
- ret = video_register_device(&histo->video, VFL_TYPE_GRABBER, -1);
+ ret = video_register_device(&histo->video, VFL_TYPE_VIDEO, -1);
if (ret < 0) {
dev_err(vsp1->dev, "failed to register video device\n");
goto error;
diff --git a/drivers/media/platform/vsp1/vsp1_regs.h b/drivers/media/platform/vsp1/vsp1_regs.h
index 5c67ff9..fe3130d 100644
--- a/drivers/media/platform/vsp1/vsp1_regs.h
+++ b/drivers/media/platform/vsp1/vsp1_regs.h
@@ -706,7 +706,7 @@
#define VI6_HGT_HUE_AREA_LOWER_SHIFT 16
#define VI6_HGT_HUE_AREA_UPPER_SHIFT 0
#define VI6_HGT_LB_TH 0x3424
-#define VI6_HGT_LBn_H(n) (0x3438 + (n) * 8)
+#define VI6_HGT_LBn_H(n) (0x3428 + (n) * 8)
#define VI6_HGT_LBn_V(n) (0x342c + (n) * 8)
#define VI6_HGT_HISTO(m, n) (0x3450 + (m) * 128 + (n) * 4)
#define VI6_HGT_MAXMIN 0x3750
diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c
index 5e59ed2..044eb57 100644
--- a/drivers/media/platform/vsp1/vsp1_video.c
+++ b/drivers/media/platform/vsp1/vsp1_video.c
@@ -1293,7 +1293,7 @@
video->video.fops = &vsp1_video_fops;
snprintf(video->video.name, sizeof(video->video.name), "%s %s",
rwpf->entity.subdev.name, direction);
- video->video.vfl_type = VFL_TYPE_GRABBER;
+ video->video.vfl_type = VFL_TYPE_VIDEO;
video->video.release = video_device_release_empty;
video->video.ioctl_ops = &vsp1_video_ioctl_ops;
@@ -1316,7 +1316,7 @@
/* ... and register the video device. */
video->video.queue = &video->queue;
- ret = video_register_device(&video->video, VFL_TYPE_GRABBER, -1);
+ ret = video_register_device(&video->video, VFL_TYPE_VIDEO, -1);
if (ret < 0) {
dev_err(video->vsp1->dev, "failed to register video device\n");
goto error;
diff --git a/drivers/media/platform/xilinx/Kconfig b/drivers/media/platform/xilinx/Kconfig
index a2773ad..44587dc 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
@@ -10,6 +12,13 @@
if VIDEO_XILINX
+config VIDEO_XILINX_CSI2RXSS
+ tristate "Xilinx CSI-2 Rx Subsystem"
+ help
+ Driver for Xilinx MIPI CSI-2 Rx Subsystem. This is a V4L sub-device
+ based driver that takes input from CSI-2 Tx source and converts
+ it into an AXI4-Stream.
+
config VIDEO_XILINX_TPG
tristate "Xilinx Video Test Pattern Generator"
depends on VIDEO_XILINX
diff --git a/drivers/media/platform/xilinx/Makefile b/drivers/media/platform/xilinx/Makefile
index 4cdc0b1..6119a34 100644
--- a/drivers/media/platform/xilinx/Makefile
+++ b/drivers/media/platform/xilinx/Makefile
@@ -3,5 +3,6 @@
xilinx-video-objs += xilinx-dma.o xilinx-vip.o xilinx-vipp.o
obj-$(CONFIG_VIDEO_XILINX) += xilinx-video.o
+obj-$(CONFIG_VIDEO_XILINX_CSI2RXSS) += xilinx-csi2rxss.o
obj-$(CONFIG_VIDEO_XILINX_TPG) += xilinx-tpg.o
obj-$(CONFIG_VIDEO_XILINX_VTC) += xilinx-vtc.o
diff --git a/drivers/media/platform/xilinx/xilinx-csi2rxss.c b/drivers/media/platform/xilinx/xilinx-csi2rxss.c
new file mode 100644
index 0000000..fff7dde
--- /dev/null
+++ b/drivers/media/platform/xilinx/xilinx-csi2rxss.c
@@ -0,0 +1,1111 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for Xilinx MIPI CSI-2 Rx Subsystem
+ *
+ * Copyright (C) 2016 - 2020 Xilinx, Inc.
+ *
+ * Contacts: Vishal Sagar <vishal.sagar@xilinx.com>
+ *
+ */
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <linux/v4l2-subdev.h>
+#include <media/media-entity.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-subdev.h>
+#include "xilinx-vip.h"
+
+/* Register register map */
+#define XCSI_CCR_OFFSET 0x00
+#define XCSI_CCR_SOFTRESET BIT(1)
+#define XCSI_CCR_ENABLE BIT(0)
+
+#define XCSI_PCR_OFFSET 0x04
+#define XCSI_PCR_MAXLANES_MASK GENMASK(4, 3)
+#define XCSI_PCR_ACTLANES_MASK GENMASK(1, 0)
+
+#define XCSI_CSR_OFFSET 0x10
+#define XCSI_CSR_PKTCNT GENMASK(31, 16)
+#define XCSI_CSR_SPFIFOFULL BIT(3)
+#define XCSI_CSR_SPFIFONE BIT(2)
+#define XCSI_CSR_SLBF BIT(1)
+#define XCSI_CSR_RIPCD BIT(0)
+
+#define XCSI_GIER_OFFSET 0x20
+#define XCSI_GIER_GIE BIT(0)
+
+#define XCSI_ISR_OFFSET 0x24
+#define XCSI_IER_OFFSET 0x28
+
+#define XCSI_ISR_FR BIT(31)
+#define XCSI_ISR_VCXFE BIT(30)
+#define XCSI_ISR_WCC BIT(22)
+#define XCSI_ISR_ILC BIT(21)
+#define XCSI_ISR_SPFIFOF BIT(20)
+#define XCSI_ISR_SPFIFONE BIT(19)
+#define XCSI_ISR_SLBF BIT(18)
+#define XCSI_ISR_STOP BIT(17)
+#define XCSI_ISR_SOTERR BIT(13)
+#define XCSI_ISR_SOTSYNCERR BIT(12)
+#define XCSI_ISR_ECC2BERR BIT(11)
+#define XCSI_ISR_ECC1BERR BIT(10)
+#define XCSI_ISR_CRCERR BIT(9)
+#define XCSI_ISR_DATAIDERR BIT(8)
+#define XCSI_ISR_VC3FSYNCERR BIT(7)
+#define XCSI_ISR_VC3FLVLERR BIT(6)
+#define XCSI_ISR_VC2FSYNCERR BIT(5)
+#define XCSI_ISR_VC2FLVLERR BIT(4)
+#define XCSI_ISR_VC1FSYNCERR BIT(3)
+#define XCSI_ISR_VC1FLVLERR BIT(2)
+#define XCSI_ISR_VC0FSYNCERR BIT(1)
+#define XCSI_ISR_VC0FLVLERR BIT(0)
+
+#define XCSI_ISR_ALLINTR_MASK (0xc07e3fff)
+
+/*
+ * Removed VCXFE mask as it doesn't exist in IER
+ * Removed STOP state irq as this will keep driver in irq handler only
+ */
+#define XCSI_IER_INTR_MASK (XCSI_ISR_ALLINTR_MASK &\
+ ~(XCSI_ISR_STOP | XCSI_ISR_VCXFE))
+
+#define XCSI_SPKTR_OFFSET 0x30
+#define XCSI_SPKTR_DATA GENMASK(23, 8)
+#define XCSI_SPKTR_VC GENMASK(7, 6)
+#define XCSI_SPKTR_DT GENMASK(5, 0)
+#define XCSI_SPKT_FIFO_DEPTH 31
+
+#define XCSI_VCXR_OFFSET 0x34
+#define XCSI_VCXR_VCERR GENMASK(23, 0)
+#define XCSI_VCXR_FSYNCERR BIT(1)
+#define XCSI_VCXR_FLVLERR BIT(0)
+
+#define XCSI_CLKINFR_OFFSET 0x3C
+#define XCSI_CLKINFR_STOP BIT(1)
+
+#define XCSI_DLXINFR_OFFSET 0x40
+#define XCSI_DLXINFR_STOP BIT(5)
+#define XCSI_DLXINFR_SOTERR BIT(1)
+#define XCSI_DLXINFR_SOTSYNCERR BIT(0)
+#define XCSI_MAXDL_COUNT 0x4
+
+#define XCSI_VCXINF1R_OFFSET 0x60
+#define XCSI_VCXINF1R_LINECOUNT GENMASK(31, 16)
+#define XCSI_VCXINF1R_LINECOUNT_SHIFT 16
+#define XCSI_VCXINF1R_BYTECOUNT GENMASK(15, 0)
+
+#define XCSI_VCXINF2R_OFFSET 0x64
+#define XCSI_VCXINF2R_DT GENMASK(5, 0)
+#define XCSI_MAXVCX_COUNT 16
+
+/*
+ * Sink pad connected to sensor source pad.
+ * Source pad connected to next module like demosaic.
+ */
+#define XCSI_MEDIA_PADS 2
+#define XCSI_DEFAULT_WIDTH 1920
+#define XCSI_DEFAULT_HEIGHT 1080
+
+/* MIPI CSI-2 Data Types from spec */
+#define XCSI_DT_YUV4228B 0x1e
+#define XCSI_DT_YUV42210B 0x1f
+#define XCSI_DT_RGB444 0x20
+#define XCSI_DT_RGB555 0x21
+#define XCSI_DT_RGB565 0x22
+#define XCSI_DT_RGB666 0x23
+#define XCSI_DT_RGB888 0x24
+#define XCSI_DT_RAW6 0x28
+#define XCSI_DT_RAW7 0x29
+#define XCSI_DT_RAW8 0x2a
+#define XCSI_DT_RAW10 0x2b
+#define XCSI_DT_RAW12 0x2c
+#define XCSI_DT_RAW14 0x2d
+#define XCSI_DT_RAW16 0x2e
+#define XCSI_DT_RAW20 0x2f
+
+#define XCSI_VCX_START 4
+#define XCSI_MAX_VC 4
+#define XCSI_MAX_VCX 16
+
+#define XCSI_NEXTREG_OFFSET 4
+
+/* There are 2 events frame sync and frame level error per VC */
+#define XCSI_VCX_NUM_EVENTS ((XCSI_MAX_VCX - XCSI_MAX_VC) * 2)
+
+/**
+ * struct xcsi2rxss_event - Event log structure
+ * @mask: Event mask
+ * @name: Name of the event
+ */
+struct xcsi2rxss_event {
+ u32 mask;
+ const char *name;
+};
+
+static const struct xcsi2rxss_event xcsi2rxss_events[] = {
+ { XCSI_ISR_FR, "Frame Received" },
+ { XCSI_ISR_VCXFE, "VCX Frame Errors" },
+ { XCSI_ISR_WCC, "Word Count Errors" },
+ { XCSI_ISR_ILC, "Invalid Lane Count Error" },
+ { XCSI_ISR_SPFIFOF, "Short Packet FIFO OverFlow Error" },
+ { XCSI_ISR_SPFIFONE, "Short Packet FIFO Not Empty" },
+ { XCSI_ISR_SLBF, "Streamline Buffer Full Error" },
+ { XCSI_ISR_STOP, "Lane Stop State" },
+ { XCSI_ISR_SOTERR, "SOT Error" },
+ { XCSI_ISR_SOTSYNCERR, "SOT Sync Error" },
+ { XCSI_ISR_ECC2BERR, "2 Bit ECC Unrecoverable Error" },
+ { XCSI_ISR_ECC1BERR, "1 Bit ECC Recoverable Error" },
+ { XCSI_ISR_CRCERR, "CRC Error" },
+ { XCSI_ISR_DATAIDERR, "Data Id Error" },
+ { XCSI_ISR_VC3FSYNCERR, "Virtual Channel 3 Frame Sync Error" },
+ { XCSI_ISR_VC3FLVLERR, "Virtual Channel 3 Frame Level Error" },
+ { XCSI_ISR_VC2FSYNCERR, "Virtual Channel 2 Frame Sync Error" },
+ { XCSI_ISR_VC2FLVLERR, "Virtual Channel 2 Frame Level Error" },
+ { XCSI_ISR_VC1FSYNCERR, "Virtual Channel 1 Frame Sync Error" },
+ { XCSI_ISR_VC1FLVLERR, "Virtual Channel 1 Frame Level Error" },
+ { XCSI_ISR_VC0FSYNCERR, "Virtual Channel 0 Frame Sync Error" },
+ { XCSI_ISR_VC0FLVLERR, "Virtual Channel 0 Frame Level Error" }
+};
+
+#define XCSI_NUM_EVENTS ARRAY_SIZE(xcsi2rxss_events)
+
+/*
+ * This table provides a mapping between CSI-2 Data type
+ * and media bus formats
+ */
+static const u32 xcsi2dt_mbus_lut[][2] = {
+ { XCSI_DT_YUV4228B, MEDIA_BUS_FMT_UYVY8_1X16 },
+ { XCSI_DT_YUV42210B, MEDIA_BUS_FMT_UYVY10_1X20 },
+ { XCSI_DT_RGB444, 0 },
+ { XCSI_DT_RGB555, 0 },
+ { XCSI_DT_RGB565, 0 },
+ { XCSI_DT_RGB666, 0 },
+ { XCSI_DT_RGB888, MEDIA_BUS_FMT_RBG888_1X24 },
+ { XCSI_DT_RAW6, 0 },
+ { XCSI_DT_RAW7, 0 },
+ { XCSI_DT_RAW8, MEDIA_BUS_FMT_SRGGB8_1X8 },
+ { XCSI_DT_RAW8, MEDIA_BUS_FMT_SBGGR8_1X8 },
+ { XCSI_DT_RAW8, MEDIA_BUS_FMT_SGBRG8_1X8 },
+ { XCSI_DT_RAW8, MEDIA_BUS_FMT_SGRBG8_1X8 },
+ { XCSI_DT_RAW10, MEDIA_BUS_FMT_SRGGB10_1X10 },
+ { XCSI_DT_RAW10, MEDIA_BUS_FMT_SBGGR10_1X10 },
+ { XCSI_DT_RAW10, MEDIA_BUS_FMT_SGBRG10_1X10 },
+ { XCSI_DT_RAW10, MEDIA_BUS_FMT_SGRBG10_1X10 },
+ { XCSI_DT_RAW12, MEDIA_BUS_FMT_SRGGB12_1X12 },
+ { XCSI_DT_RAW12, MEDIA_BUS_FMT_SBGGR12_1X12 },
+ { XCSI_DT_RAW12, MEDIA_BUS_FMT_SGBRG12_1X12 },
+ { XCSI_DT_RAW12, MEDIA_BUS_FMT_SGRBG12_1X12 },
+ { XCSI_DT_RAW16, MEDIA_BUS_FMT_SRGGB16_1X16 },
+ { XCSI_DT_RAW16, MEDIA_BUS_FMT_SBGGR16_1X16 },
+ { XCSI_DT_RAW16, MEDIA_BUS_FMT_SGBRG16_1X16 },
+ { XCSI_DT_RAW16, MEDIA_BUS_FMT_SGRBG16_1X16 },
+ { XCSI_DT_RAW20, 0 },
+};
+
+/**
+ * struct xcsi2rxss_state - CSI-2 Rx Subsystem device structure
+ * @subdev: The v4l2 subdev structure
+ * @format: Active V4L2 formats on each pad
+ * @default_format: Default V4L2 format
+ * @events: counter for events
+ * @vcx_events: counter for vcx_events
+ * @dev: Platform structure
+ * @rsubdev: Remote subdev connected to sink pad
+ * @rst_gpio: reset to video_aresetn
+ * @clks: array of clocks
+ * @iomem: Base address of subsystem
+ * @max_num_lanes: Maximum number of lanes present
+ * @datatype: Data type filter
+ * @lock: mutex for accessing this structure
+ * @pads: media pads
+ * @streaming: Flag for storing streaming state
+ * @enable_active_lanes: If number of active lanes can be modified
+ * @en_vcx: If more than 4 VC are enabled
+ *
+ * This structure contains the device driver related parameters
+ */
+struct xcsi2rxss_state {
+ struct v4l2_subdev subdev;
+ struct v4l2_mbus_framefmt format;
+ struct v4l2_mbus_framefmt default_format;
+ u32 events[XCSI_NUM_EVENTS];
+ u32 vcx_events[XCSI_VCX_NUM_EVENTS];
+ struct device *dev;
+ struct v4l2_subdev *rsubdev;
+ struct gpio_desc *rst_gpio;
+ struct clk_bulk_data *clks;
+ void __iomem *iomem;
+ u32 max_num_lanes;
+ u32 datatype;
+ /* used to protect access to this struct */
+ struct mutex lock;
+ struct media_pad pads[XCSI_MEDIA_PADS];
+ bool streaming;
+ bool enable_active_lanes;
+ bool en_vcx;
+};
+
+static const struct clk_bulk_data xcsi2rxss_clks[] = {
+ { .id = "lite_aclk" },
+ { .id = "video_aclk" },
+};
+
+static inline struct xcsi2rxss_state *
+to_xcsi2rxssstate(struct v4l2_subdev *subdev)
+{
+ return container_of(subdev, struct xcsi2rxss_state, subdev);
+}
+
+/*
+ * Register related operations
+ */
+static inline u32 xcsi2rxss_read(struct xcsi2rxss_state *xcsi2rxss, u32 addr)
+{
+ return ioread32(xcsi2rxss->iomem + addr);
+}
+
+static inline void xcsi2rxss_write(struct xcsi2rxss_state *xcsi2rxss, u32 addr,
+ u32 value)
+{
+ iowrite32(value, xcsi2rxss->iomem + addr);
+}
+
+static inline void xcsi2rxss_clr(struct xcsi2rxss_state *xcsi2rxss, u32 addr,
+ u32 clr)
+{
+ xcsi2rxss_write(xcsi2rxss, addr,
+ xcsi2rxss_read(xcsi2rxss, addr) & ~clr);
+}
+
+static inline void xcsi2rxss_set(struct xcsi2rxss_state *xcsi2rxss, u32 addr,
+ u32 set)
+{
+ xcsi2rxss_write(xcsi2rxss, addr, xcsi2rxss_read(xcsi2rxss, addr) | set);
+}
+
+/*
+ * This function returns the nth mbus for a data type.
+ * In case of error, mbus code returned is 0.
+ */
+static u32 xcsi2rxss_get_nth_mbus(u32 dt, u32 n)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(xcsi2dt_mbus_lut); i++) {
+ if (xcsi2dt_mbus_lut[i][0] == dt) {
+ if (n-- == 0)
+ return xcsi2dt_mbus_lut[i][1];
+ }
+ }
+
+ return 0;
+}
+
+/* This returns the data type for a media bus format else 0 */
+static u32 xcsi2rxss_get_dt(u32 mbus)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(xcsi2dt_mbus_lut); i++) {
+ if (xcsi2dt_mbus_lut[i][1] == mbus)
+ return xcsi2dt_mbus_lut[i][0];
+ }
+
+ return 0;
+}
+
+/**
+ * xcsi2rxss_soft_reset - Does a soft reset of the MIPI CSI-2 Rx Subsystem
+ * @state: Xilinx CSI-2 Rx Subsystem structure pointer
+ *
+ * Core takes less than 100 video clock cycles to reset.
+ * So a larger timeout value is chosen for margin.
+ *
+ * Return: 0 - on success OR -ETIME if reset times out
+ */
+static int xcsi2rxss_soft_reset(struct xcsi2rxss_state *state)
+{
+ u32 timeout = 1000; /* us */
+
+ xcsi2rxss_set(state, XCSI_CCR_OFFSET, XCSI_CCR_SOFTRESET);
+
+ while (xcsi2rxss_read(state, XCSI_CSR_OFFSET) & XCSI_CSR_RIPCD) {
+ if (timeout == 0) {
+ dev_err(state->dev, "soft reset timed out!\n");
+ return -ETIME;
+ }
+
+ timeout--;
+ udelay(1);
+ }
+
+ xcsi2rxss_clr(state, XCSI_CCR_OFFSET, XCSI_CCR_SOFTRESET);
+ return 0;
+}
+
+static void xcsi2rxss_hard_reset(struct xcsi2rxss_state *state)
+{
+ if (!state->rst_gpio)
+ return;
+
+ /* minimum of 40 dphy_clk_200M cycles */
+ gpiod_set_value_cansleep(state->rst_gpio, 1);
+ usleep_range(1, 2);
+ gpiod_set_value_cansleep(state->rst_gpio, 0);
+}
+
+static void xcsi2rxss_reset_event_counters(struct xcsi2rxss_state *state)
+{
+ unsigned int i;
+
+ for (i = 0; i < XCSI_NUM_EVENTS; i++)
+ state->events[i] = 0;
+
+ for (i = 0; i < XCSI_VCX_NUM_EVENTS; i++)
+ state->vcx_events[i] = 0;
+}
+
+/* Print event counters */
+static void xcsi2rxss_log_counters(struct xcsi2rxss_state *state)
+{
+ struct device *dev = state->dev;
+ unsigned int i;
+
+ for (i = 0; i < XCSI_NUM_EVENTS; i++) {
+ if (state->events[i] > 0) {
+ dev_info(dev, "%s events: %d\n",
+ xcsi2rxss_events[i].name,
+ state->events[i]);
+ }
+ }
+
+ if (state->en_vcx) {
+ for (i = 0; i < XCSI_VCX_NUM_EVENTS; i++) {
+ if (state->vcx_events[i] > 0) {
+ dev_info(dev,
+ "VC %d Frame %s err vcx events: %d\n",
+ (i / 2) + XCSI_VCX_START,
+ i & 1 ? "Sync" : "Level",
+ state->vcx_events[i]);
+ }
+ }
+ }
+}
+
+/**
+ * xcsi2rxss_log_status - Logs the status of the CSI-2 Receiver
+ * @sd: Pointer to V4L2 subdevice structure
+ *
+ * This function prints the current status of Xilinx MIPI CSI-2
+ *
+ * Return: 0 on success
+ */
+static int xcsi2rxss_log_status(struct v4l2_subdev *sd)
+{
+ struct xcsi2rxss_state *xcsi2rxss = to_xcsi2rxssstate(sd);
+ struct device *dev = xcsi2rxss->dev;
+ u32 reg, data;
+ unsigned int i, max_vc;
+
+ mutex_lock(&xcsi2rxss->lock);
+
+ xcsi2rxss_log_counters(xcsi2rxss);
+
+ dev_info(dev, "***** Core Status *****\n");
+ data = xcsi2rxss_read(xcsi2rxss, XCSI_CSR_OFFSET);
+ dev_info(dev, "Short Packet FIFO Full = %s\n",
+ data & XCSI_CSR_SPFIFOFULL ? "true" : "false");
+ dev_info(dev, "Short Packet FIFO Not Empty = %s\n",
+ data & XCSI_CSR_SPFIFONE ? "true" : "false");
+ dev_info(dev, "Stream line buffer full = %s\n",
+ data & XCSI_CSR_SLBF ? "true" : "false");
+ dev_info(dev, "Soft reset/Core disable in progress = %s\n",
+ data & XCSI_CSR_RIPCD ? "true" : "false");
+
+ /* Clk & Lane Info */
+ dev_info(dev, "******** Clock Lane Info *********\n");
+ data = xcsi2rxss_read(xcsi2rxss, XCSI_CLKINFR_OFFSET);
+ dev_info(dev, "Clock Lane in Stop State = %s\n",
+ data & XCSI_CLKINFR_STOP ? "true" : "false");
+
+ dev_info(dev, "******** Data Lane Info *********\n");
+ dev_info(dev, "Lane\tSoT Error\tSoT Sync Error\tStop State\n");
+ reg = XCSI_DLXINFR_OFFSET;
+ for (i = 0; i < XCSI_MAXDL_COUNT; i++) {
+ data = xcsi2rxss_read(xcsi2rxss, reg);
+
+ dev_info(dev, "%d\t%s\t\t%s\t\t%s\n", i,
+ data & XCSI_DLXINFR_SOTERR ? "true" : "false",
+ data & XCSI_DLXINFR_SOTSYNCERR ? "true" : "false",
+ data & XCSI_DLXINFR_STOP ? "true" : "false");
+
+ reg += XCSI_NEXTREG_OFFSET;
+ }
+
+ /* Virtual Channel Image Information */
+ dev_info(dev, "********** Virtual Channel Info ************\n");
+ dev_info(dev, "VC\tLine Count\tByte Count\tData Type\n");
+ if (xcsi2rxss->en_vcx)
+ max_vc = XCSI_MAX_VCX;
+ else
+ max_vc = XCSI_MAX_VC;
+
+ reg = XCSI_VCXINF1R_OFFSET;
+ for (i = 0; i < max_vc; i++) {
+ u32 line_count, byte_count, data_type;
+
+ /* Get line and byte count from VCXINFR1 Register */
+ data = xcsi2rxss_read(xcsi2rxss, reg);
+ byte_count = data & XCSI_VCXINF1R_BYTECOUNT;
+ line_count = data & XCSI_VCXINF1R_LINECOUNT;
+ line_count >>= XCSI_VCXINF1R_LINECOUNT_SHIFT;
+
+ /* Get data type from VCXINFR2 Register */
+ reg += XCSI_NEXTREG_OFFSET;
+ data = xcsi2rxss_read(xcsi2rxss, reg);
+ data_type = data & XCSI_VCXINF2R_DT;
+
+ dev_info(dev, "%d\t%d\t\t%d\t\t0x%x\n", i, line_count,
+ byte_count, data_type);
+
+ /* Move to next pair of VC Info registers */
+ reg += XCSI_NEXTREG_OFFSET;
+ }
+
+ mutex_unlock(&xcsi2rxss->lock);
+
+ return 0;
+}
+
+static struct v4l2_subdev *xcsi2rxss_get_remote_subdev(struct media_pad *local)
+{
+ struct media_pad *remote;
+
+ remote = media_entity_remote_pad(local);
+ if (!remote || !is_media_entity_v4l2_subdev(remote->entity))
+ return NULL;
+
+ return media_entity_to_v4l2_subdev(remote->entity);
+}
+
+static int xcsi2rxss_start_stream(struct xcsi2rxss_state *state)
+{
+ int ret = 0;
+
+ /* enable core */
+ xcsi2rxss_set(state, XCSI_CCR_OFFSET, XCSI_CCR_ENABLE);
+
+ ret = xcsi2rxss_soft_reset(state);
+ if (ret) {
+ state->streaming = false;
+ return ret;
+ }
+
+ /* enable interrupts */
+ xcsi2rxss_clr(state, XCSI_GIER_OFFSET, XCSI_GIER_GIE);
+ xcsi2rxss_write(state, XCSI_IER_OFFSET, XCSI_IER_INTR_MASK);
+ xcsi2rxss_set(state, XCSI_GIER_OFFSET, XCSI_GIER_GIE);
+
+ state->streaming = true;
+
+ state->rsubdev =
+ xcsi2rxss_get_remote_subdev(&state->pads[XVIP_PAD_SINK]);
+
+ ret = v4l2_subdev_call(state->rsubdev, video, s_stream, 1);
+ if (ret) {
+ /* disable interrupts */
+ xcsi2rxss_clr(state, XCSI_IER_OFFSET, XCSI_IER_INTR_MASK);
+ xcsi2rxss_clr(state, XCSI_GIER_OFFSET, XCSI_GIER_GIE);
+
+ /* disable core */
+ xcsi2rxss_clr(state, XCSI_CCR_OFFSET, XCSI_CCR_ENABLE);
+ state->streaming = false;
+ }
+
+ return ret;
+}
+
+static void xcsi2rxss_stop_stream(struct xcsi2rxss_state *state)
+{
+ v4l2_subdev_call(state->rsubdev, video, s_stream, 0);
+
+ /* disable interrupts */
+ xcsi2rxss_clr(state, XCSI_IER_OFFSET, XCSI_IER_INTR_MASK);
+ xcsi2rxss_clr(state, XCSI_GIER_OFFSET, XCSI_GIER_GIE);
+
+ /* disable core */
+ xcsi2rxss_clr(state, XCSI_CCR_OFFSET, XCSI_CCR_ENABLE);
+ state->streaming = false;
+}
+
+/**
+ * xcsi2rxss_irq_handler - Interrupt handler for CSI-2
+ * @irq: IRQ number
+ * @data: Pointer to device state
+ *
+ * In the interrupt handler, a list of event counters are updated for
+ * corresponding interrupts. This is useful to get status / debug.
+ *
+ * Return: IRQ_HANDLED after handling interrupts
+ */
+static irqreturn_t xcsi2rxss_irq_handler(int irq, void *data)
+{
+ struct xcsi2rxss_state *state = (struct xcsi2rxss_state *)data;
+ struct device *dev = state->dev;
+ u32 status;
+
+ status = xcsi2rxss_read(state, XCSI_ISR_OFFSET) & XCSI_ISR_ALLINTR_MASK;
+ xcsi2rxss_write(state, XCSI_ISR_OFFSET, status);
+
+ /* Received a short packet */
+ if (status & XCSI_ISR_SPFIFONE) {
+ u32 count = 0;
+
+ /*
+ * Drain generic short packet FIFO by reading max 31
+ * (fifo depth) short packets from fifo or till fifo is empty.
+ */
+ for (count = 0; count < XCSI_SPKT_FIFO_DEPTH; ++count) {
+ u32 spfifostat, spkt;
+
+ spkt = xcsi2rxss_read(state, XCSI_SPKTR_OFFSET);
+ dev_dbg(dev, "Short packet = 0x%08x\n", spkt);
+ spfifostat = xcsi2rxss_read(state, XCSI_ISR_OFFSET);
+ spfifostat &= XCSI_ISR_SPFIFONE;
+ if (!spfifostat)
+ break;
+ xcsi2rxss_write(state, XCSI_ISR_OFFSET, spfifostat);
+ }
+ }
+
+ /* Short packet FIFO overflow */
+ if (status & XCSI_ISR_SPFIFOF)
+ dev_dbg_ratelimited(dev, "Short packet FIFO overflowed\n");
+
+ /*
+ * Stream line buffer full
+ * This means there is a backpressure from downstream IP
+ */
+ if (status & XCSI_ISR_SLBF) {
+ dev_alert_ratelimited(dev, "Stream Line Buffer Full!\n");
+
+ /* disable interrupts */
+ xcsi2rxss_clr(state, XCSI_IER_OFFSET, XCSI_IER_INTR_MASK);
+ xcsi2rxss_clr(state, XCSI_GIER_OFFSET, XCSI_GIER_GIE);
+
+ /* disable core */
+ xcsi2rxss_clr(state, XCSI_CCR_OFFSET, XCSI_CCR_ENABLE);
+
+ /*
+ * The IP needs to be hard reset before it can be used now.
+ * This will be done in streamoff.
+ */
+
+ /*
+ * TODO: Notify the whole pipeline with v4l2_subdev_notify() to
+ * inform userspace.
+ */
+ }
+
+ /* Increment event counters */
+ if (status & XCSI_ISR_ALLINTR_MASK) {
+ unsigned int i;
+
+ for (i = 0; i < XCSI_NUM_EVENTS; i++) {
+ if (!(status & xcsi2rxss_events[i].mask))
+ continue;
+ state->events[i]++;
+ dev_dbg_ratelimited(dev, "%s: %u\n",
+ xcsi2rxss_events[i].name,
+ state->events[i]);
+ }
+
+ if (status & XCSI_ISR_VCXFE && state->en_vcx) {
+ u32 vcxstatus;
+
+ vcxstatus = xcsi2rxss_read(state, XCSI_VCXR_OFFSET);
+ vcxstatus &= XCSI_VCXR_VCERR;
+ for (i = 0; i < XCSI_VCX_NUM_EVENTS; i++) {
+ if (!(vcxstatus & BIT(i)))
+ continue;
+ state->vcx_events[i]++;
+ }
+ xcsi2rxss_write(state, XCSI_VCXR_OFFSET, vcxstatus);
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * xcsi2rxss_s_stream - It is used to start/stop the streaming.
+ * @sd: V4L2 Sub device
+ * @enable: Flag (True / False)
+ *
+ * This function controls the start or stop of streaming for the
+ * Xilinx MIPI CSI-2 Rx Subsystem.
+ *
+ * Return: 0 on success, errors otherwise
+ */
+static int xcsi2rxss_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct xcsi2rxss_state *xcsi2rxss = to_xcsi2rxssstate(sd);
+ int ret = 0;
+
+ mutex_lock(&xcsi2rxss->lock);
+
+ if (enable == xcsi2rxss->streaming)
+ goto stream_done;
+
+ if (enable) {
+ xcsi2rxss_reset_event_counters(xcsi2rxss);
+ ret = xcsi2rxss_start_stream(xcsi2rxss);
+ } else {
+ xcsi2rxss_stop_stream(xcsi2rxss);
+ xcsi2rxss_hard_reset(xcsi2rxss);
+ }
+
+stream_done:
+ mutex_unlock(&xcsi2rxss->lock);
+ return ret;
+}
+
+static struct v4l2_mbus_framefmt *
+__xcsi2rxss_get_pad_format(struct xcsi2rxss_state *xcsi2rxss,
+ struct v4l2_subdev_pad_config *cfg,
+ unsigned int pad, u32 which)
+{
+ switch (which) {
+ case V4L2_SUBDEV_FORMAT_TRY:
+ return v4l2_subdev_get_try_format(&xcsi2rxss->subdev, cfg, pad);
+ case V4L2_SUBDEV_FORMAT_ACTIVE:
+ return &xcsi2rxss->format;
+ default:
+ return NULL;
+ }
+}
+
+/**
+ * xcsi2rxss_init_cfg - Initialise the pad format config to default
+ * @sd: Pointer to V4L2 Sub device structure
+ * @cfg: Pointer to sub device pad information structure
+ *
+ * This function is used to initialize the pad format with the default
+ * values.
+ *
+ * Return: 0 on success
+ */
+static int xcsi2rxss_init_cfg(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg)
+{
+ struct xcsi2rxss_state *xcsi2rxss = to_xcsi2rxssstate(sd);
+ struct v4l2_mbus_framefmt *format;
+ unsigned int i;
+
+ mutex_lock(&xcsi2rxss->lock);
+ for (i = 0; i < XCSI_MEDIA_PADS; i++) {
+ format = v4l2_subdev_get_try_format(sd, cfg, i);
+ *format = xcsi2rxss->default_format;
+ }
+ mutex_unlock(&xcsi2rxss->lock);
+
+ return 0;
+}
+
+/**
+ * xcsi2rxss_get_format - Get the pad format
+ * @sd: Pointer to V4L2 Sub device structure
+ * @cfg: Pointer to sub device pad information structure
+ * @fmt: Pointer to pad level media bus format
+ *
+ * This function is used to get the pad format information.
+ *
+ * Return: 0 on success
+ */
+static int xcsi2rxss_get_format(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *fmt)
+{
+ struct xcsi2rxss_state *xcsi2rxss = to_xcsi2rxssstate(sd);
+
+ mutex_lock(&xcsi2rxss->lock);
+ fmt->format = *__xcsi2rxss_get_pad_format(xcsi2rxss, cfg, fmt->pad,
+ fmt->which);
+ mutex_unlock(&xcsi2rxss->lock);
+
+ return 0;
+}
+
+/**
+ * xcsi2rxss_set_format - This is used to set the pad format
+ * @sd: Pointer to V4L2 Sub device structure
+ * @cfg: Pointer to sub device pad information structure
+ * @fmt: Pointer to pad level media bus format
+ *
+ * This function is used to set the pad format. Since the pad format is fixed
+ * in hardware, it can't be modified on run time. So when a format set is
+ * requested by application, all parameters except the format type is saved
+ * for the pad and the original pad format is sent back to the application.
+ *
+ * Return: 0 on success
+ */
+static int xcsi2rxss_set_format(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *fmt)
+{
+ struct xcsi2rxss_state *xcsi2rxss = to_xcsi2rxssstate(sd);
+ struct v4l2_mbus_framefmt *__format;
+ u32 dt;
+
+ mutex_lock(&xcsi2rxss->lock);
+
+ /*
+ * Only the format->code parameter matters for CSI as the
+ * CSI format cannot be changed at runtime.
+ * Ensure that format to set is copied to over to CSI pad format
+ */
+ __format = __xcsi2rxss_get_pad_format(xcsi2rxss, cfg,
+ fmt->pad, fmt->which);
+
+ /* only sink pad format can be updated */
+ if (fmt->pad == XVIP_PAD_SOURCE) {
+ fmt->format = *__format;
+ mutex_unlock(&xcsi2rxss->lock);
+ return 0;
+ }
+
+ /*
+ * RAW8 is supported in all datatypes. So if requested media bus format
+ * is of RAW8 type, then allow to be set. In case core is configured to
+ * other RAW, YUV422 8/10 or RGB888, set appropriate media bus format.
+ */
+ dt = xcsi2rxss_get_dt(fmt->format.code);
+ if (dt != xcsi2rxss->datatype && dt != XCSI_DT_RAW8) {
+ dev_dbg(xcsi2rxss->dev, "Unsupported media bus format");
+ /* set the default format for the data type */
+ fmt->format.code = xcsi2rxss_get_nth_mbus(xcsi2rxss->datatype,
+ 0);
+ }
+
+ *__format = fmt->format;
+ mutex_unlock(&xcsi2rxss->lock);
+
+ return 0;
+}
+
+/*
+ * xcsi2rxss_enum_mbus_code - Handle pixel format enumeration
+ * @sd: pointer to v4l2 subdev structure
+ * @cfg: V4L2 subdev pad configuration
+ * @code: pointer to v4l2_subdev_mbus_code_enum structure
+ *
+ * Return: -EINVAL or zero on success
+ */
+static int xcsi2rxss_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ struct xcsi2rxss_state *state = to_xcsi2rxssstate(sd);
+ u32 dt, n;
+ int ret = 0;
+
+ /* RAW8 dt packets are available in all DT configurations */
+ if (code->index < 4) {
+ n = code->index;
+ dt = XCSI_DT_RAW8;
+ } else if (state->datatype != XCSI_DT_RAW8) {
+ n = code->index - 4;
+ dt = state->datatype;
+ } else {
+ return -EINVAL;
+ }
+
+ code->code = xcsi2rxss_get_nth_mbus(dt, n);
+ if (!code->code)
+ ret = -EINVAL;
+
+ return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * Media Operations
+ */
+
+static const struct media_entity_operations xcsi2rxss_media_ops = {
+ .link_validate = v4l2_subdev_link_validate
+};
+
+static const struct v4l2_subdev_core_ops xcsi2rxss_core_ops = {
+ .log_status = xcsi2rxss_log_status,
+};
+
+static const struct v4l2_subdev_video_ops xcsi2rxss_video_ops = {
+ .s_stream = xcsi2rxss_s_stream
+};
+
+static const struct v4l2_subdev_pad_ops xcsi2rxss_pad_ops = {
+ .init_cfg = xcsi2rxss_init_cfg,
+ .get_fmt = xcsi2rxss_get_format,
+ .set_fmt = xcsi2rxss_set_format,
+ .enum_mbus_code = xcsi2rxss_enum_mbus_code,
+ .link_validate = v4l2_subdev_link_validate_default,
+};
+
+static const struct v4l2_subdev_ops xcsi2rxss_ops = {
+ .core = &xcsi2rxss_core_ops,
+ .video = &xcsi2rxss_video_ops,
+ .pad = &xcsi2rxss_pad_ops
+};
+
+static int xcsi2rxss_parse_of(struct xcsi2rxss_state *xcsi2rxss)
+{
+ struct device *dev = xcsi2rxss->dev;
+ struct device_node *node = dev->of_node;
+
+ struct fwnode_handle *ep;
+ struct v4l2_fwnode_endpoint vep = {
+ .bus_type = V4L2_MBUS_CSI2_DPHY
+ };
+ bool en_csi_v20, vfb;
+ int ret;
+
+ en_csi_v20 = of_property_read_bool(node, "xlnx,en-csi-v2-0");
+ if (en_csi_v20)
+ xcsi2rxss->en_vcx = of_property_read_bool(node, "xlnx,en-vcx");
+
+ xcsi2rxss->enable_active_lanes =
+ of_property_read_bool(node, "xlnx,en-active-lanes");
+
+ ret = of_property_read_u32(node, "xlnx,csi-pxl-format",
+ &xcsi2rxss->datatype);
+ if (ret < 0) {
+ dev_err(dev, "missing xlnx,csi-pxl-format property\n");
+ return ret;
+ }
+
+ switch (xcsi2rxss->datatype) {
+ case XCSI_DT_YUV4228B:
+ case XCSI_DT_RGB444:
+ case XCSI_DT_RGB555:
+ case XCSI_DT_RGB565:
+ case XCSI_DT_RGB666:
+ case XCSI_DT_RGB888:
+ case XCSI_DT_RAW6:
+ case XCSI_DT_RAW7:
+ case XCSI_DT_RAW8:
+ case XCSI_DT_RAW10:
+ case XCSI_DT_RAW12:
+ case XCSI_DT_RAW14:
+ break;
+ case XCSI_DT_YUV42210B:
+ case XCSI_DT_RAW16:
+ case XCSI_DT_RAW20:
+ if (!en_csi_v20) {
+ ret = -EINVAL;
+ dev_dbg(dev, "enable csi v2 for this pixel format");
+ }
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ if (ret < 0) {
+ dev_err(dev, "invalid csi-pxl-format property!\n");
+ return ret;
+ }
+
+ vfb = of_property_read_bool(node, "xlnx,vfb");
+ if (!vfb) {
+ dev_err(dev, "operation without VFB is not supported\n");
+ return -EINVAL;
+ }
+
+ ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev),
+ XVIP_PAD_SINK, 0,
+ FWNODE_GRAPH_ENDPOINT_NEXT);
+ if (!ep) {
+ dev_err(dev, "no sink port found");
+ return -EINVAL;
+ }
+
+ ret = v4l2_fwnode_endpoint_parse(ep, &vep);
+ fwnode_handle_put(ep);
+ if (ret) {
+ dev_err(dev, "error parsing sink port");
+ return ret;
+ }
+
+ dev_dbg(dev, "mipi number lanes = %d\n",
+ vep.bus.mipi_csi2.num_data_lanes);
+
+ xcsi2rxss->max_num_lanes = vep.bus.mipi_csi2.num_data_lanes;
+
+ ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev),
+ XVIP_PAD_SOURCE, 0,
+ FWNODE_GRAPH_ENDPOINT_NEXT);
+ if (!ep) {
+ dev_err(dev, "no source port found");
+ return -EINVAL;
+ }
+
+ fwnode_handle_put(ep);
+
+ dev_dbg(dev, "vcx %s, %u data lanes (%s), data type 0x%02x\n",
+ xcsi2rxss->en_vcx ? "enabled" : "disabled",
+ xcsi2rxss->max_num_lanes,
+ xcsi2rxss->enable_active_lanes ? "dynamic" : "static",
+ xcsi2rxss->datatype);
+
+ return 0;
+}
+
+static int xcsi2rxss_probe(struct platform_device *pdev)
+{
+ struct v4l2_subdev *subdev;
+ struct xcsi2rxss_state *xcsi2rxss;
+ int num_clks = ARRAY_SIZE(xcsi2rxss_clks);
+ struct device *dev = &pdev->dev;
+ int irq, ret;
+
+ xcsi2rxss = devm_kzalloc(dev, sizeof(*xcsi2rxss), GFP_KERNEL);
+ if (!xcsi2rxss)
+ return -ENOMEM;
+
+ xcsi2rxss->dev = dev;
+
+ xcsi2rxss->clks = devm_kmemdup(dev, xcsi2rxss_clks,
+ sizeof(xcsi2rxss_clks), GFP_KERNEL);
+ if (!xcsi2rxss->clks)
+ return -ENOMEM;
+
+ /* Reset GPIO */
+ xcsi2rxss->rst_gpio = devm_gpiod_get_optional(dev, "video-reset",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(xcsi2rxss->rst_gpio)) {
+ if (PTR_ERR(xcsi2rxss->rst_gpio) != -EPROBE_DEFER)
+ dev_err(dev, "Video Reset GPIO not setup in DT");
+ return PTR_ERR(xcsi2rxss->rst_gpio);
+ }
+
+ ret = xcsi2rxss_parse_of(xcsi2rxss);
+ if (ret < 0)
+ return ret;
+
+ xcsi2rxss->iomem = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(xcsi2rxss->iomem))
+ return PTR_ERR(xcsi2rxss->iomem);
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ ret = devm_request_threaded_irq(dev, irq, NULL,
+ xcsi2rxss_irq_handler, IRQF_ONESHOT,
+ dev_name(dev), xcsi2rxss);
+ if (ret) {
+ dev_err(dev, "Err = %d Interrupt handler reg failed!\n", ret);
+ return ret;
+ }
+
+ ret = clk_bulk_get(dev, num_clks, xcsi2rxss->clks);
+ if (ret)
+ return ret;
+
+ /* TODO: Enable/disable clocks at stream on/off time. */
+ ret = clk_bulk_prepare_enable(num_clks, xcsi2rxss->clks);
+ if (ret)
+ goto err_clk_put;
+
+ mutex_init(&xcsi2rxss->lock);
+
+ xcsi2rxss_hard_reset(xcsi2rxss);
+ xcsi2rxss_soft_reset(xcsi2rxss);
+
+ /* Initialize V4L2 subdevice and media entity */
+ xcsi2rxss->pads[XVIP_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
+ xcsi2rxss->pads[XVIP_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
+
+ /* Initialize the default format */
+ xcsi2rxss->default_format.code =
+ xcsi2rxss_get_nth_mbus(xcsi2rxss->datatype, 0);
+ xcsi2rxss->default_format.field = V4L2_FIELD_NONE;
+ xcsi2rxss->default_format.colorspace = V4L2_COLORSPACE_SRGB;
+ xcsi2rxss->default_format.width = XCSI_DEFAULT_WIDTH;
+ xcsi2rxss->default_format.height = XCSI_DEFAULT_HEIGHT;
+ xcsi2rxss->format = xcsi2rxss->default_format;
+
+ /* Initialize V4L2 subdevice and media entity */
+ subdev = &xcsi2rxss->subdev;
+ v4l2_subdev_init(subdev, &xcsi2rxss_ops);
+ subdev->dev = dev;
+ strscpy(subdev->name, dev_name(dev), sizeof(subdev->name));
+ subdev->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE;
+ subdev->entity.ops = &xcsi2rxss_media_ops;
+ v4l2_set_subdevdata(subdev, xcsi2rxss);
+
+ ret = media_entity_pads_init(&subdev->entity, XCSI_MEDIA_PADS,
+ xcsi2rxss->pads);
+ if (ret < 0)
+ goto error;
+
+ platform_set_drvdata(pdev, xcsi2rxss);
+
+ ret = v4l2_async_register_subdev(subdev);
+ if (ret < 0) {
+ dev_err(dev, "failed to register subdev\n");
+ goto error;
+ }
+
+ return 0;
+error:
+ media_entity_cleanup(&subdev->entity);
+ mutex_destroy(&xcsi2rxss->lock);
+ clk_bulk_disable_unprepare(num_clks, xcsi2rxss->clks);
+err_clk_put:
+ clk_bulk_put(num_clks, xcsi2rxss->clks);
+ return ret;
+}
+
+static int xcsi2rxss_remove(struct platform_device *pdev)
+{
+ struct xcsi2rxss_state *xcsi2rxss = platform_get_drvdata(pdev);
+ struct v4l2_subdev *subdev = &xcsi2rxss->subdev;
+ int num_clks = ARRAY_SIZE(xcsi2rxss_clks);
+
+ v4l2_async_unregister_subdev(subdev);
+ media_entity_cleanup(&subdev->entity);
+ mutex_destroy(&xcsi2rxss->lock);
+ clk_bulk_disable_unprepare(num_clks, xcsi2rxss->clks);
+ clk_bulk_put(num_clks, xcsi2rxss->clks);
+
+ return 0;
+}
+
+static const struct of_device_id xcsi2rxss_of_id_table[] = {
+ { .compatible = "xlnx,mipi-csi2-rx-subsystem-5.0", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, xcsi2rxss_of_id_table);
+
+static struct platform_driver xcsi2rxss_driver = {
+ .driver = {
+ .name = "xilinx-csi2rxss",
+ .of_match_table = xcsi2rxss_of_id_table,
+ },
+ .probe = xcsi2rxss_probe,
+ .remove = xcsi2rxss_remove,
+};
+
+module_platform_driver(xcsi2rxss_driver);
+
+MODULE_AUTHOR("Vishal Sagar <vsagar@xilinx.com>");
+MODULE_DESCRIPTION("Xilinx MIPI CSI-2 Rx Subsystem Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/xilinx/xilinx-dma.c b/drivers/media/platform/xilinx/xilinx-dma.c
index b211380..2a56201 100644
--- a/drivers/media/platform/xilinx/xilinx-dma.c
+++ b/drivers/media/platform/xilinx/xilinx-dma.c
@@ -685,7 +685,7 @@
xdev->dev->of_node,
type == V4L2_BUF_TYPE_VIDEO_CAPTURE ? "output" : "input",
port);
- dma->video.vfl_type = VFL_TYPE_GRABBER;
+ dma->video.vfl_type = VFL_TYPE_VIDEO;
dma->video.vfl_dir = type == V4L2_BUF_TYPE_VIDEO_CAPTURE
? VFL_DIR_RX : VFL_DIR_TX;
dma->video.release = video_device_release_empty;
@@ -725,16 +725,17 @@
/* ... and the DMA channel. */
snprintf(name, sizeof(name), "port%u", port);
- dma->dma = dma_request_slave_channel(dma->xdev->dev, name);
- if (dma->dma == NULL) {
- dev_err(dma->xdev->dev, "no VDMA channel found\n");
- ret = -ENODEV;
+ dma->dma = dma_request_chan(dma->xdev->dev, name);
+ if (IS_ERR(dma->dma)) {
+ ret = PTR_ERR(dma->dma);
+ if (ret != -EPROBE_DEFER)
+ dev_err(dma->xdev->dev, "no VDMA channel found\n");
goto error;
}
dma->align = 1 << dma->dma->device->copy_align;
- ret = video_register_device(&dma->video, VFL_TYPE_GRABBER, -1);
+ ret = video_register_device(&dma->video, VFL_TYPE_VIDEO, -1);
if (ret < 0) {
dev_err(dma->xdev->dev, "failed to register video device\n");
goto error;
@@ -752,7 +753,7 @@
if (video_is_registered(&dma->video))
video_unregister_device(&dma->video);
- if (dma->dma)
+ if (!IS_ERR_OR_NULL(dma->dma))
dma_release_channel(dma->dma);
media_entity_cleanup(&dma->video.entity);
diff --git a/drivers/media/platform/xilinx/xilinx-dma.h b/drivers/media/platform/xilinx/xilinx-dma.h
index 5aec4d1..2378bda 100644
--- a/drivers/media/platform/xilinx/xilinx-dma.h
+++ b/drivers/media/platform/xilinx/xilinx-dma.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* Xilinx Video DMA
*
diff --git a/drivers/media/platform/xilinx/xilinx-vip.h b/drivers/media/platform/xilinx/xilinx-vip.h
index f71e2b6..a528a32 100644
--- a/drivers/media/platform/xilinx/xilinx-vip.h
+++ b/drivers/media/platform/xilinx/xilinx-vip.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* Xilinx Video IP Core
*
diff --git a/drivers/media/platform/xilinx/xilinx-vipp.h b/drivers/media/platform/xilinx/xilinx-vipp.h
index e65fce9..cc52c18 100644
--- a/drivers/media/platform/xilinx/xilinx-vipp.h
+++ b/drivers/media/platform/xilinx/xilinx-vipp.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* Xilinx Video IP Composite Device
*
diff --git a/drivers/media/platform/xilinx/xilinx-vtc.h b/drivers/media/platform/xilinx/xilinx-vtc.h
index 90cf442..8558459 100644
--- a/drivers/media/platform/xilinx/xilinx-vtc.h
+++ b/drivers/media/platform/xilinx/xilinx-vtc.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* Xilinx Video Timing Controller
*