From 4b32216adb010a364f23a055c45e06e839b089f9 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 24 Mar 2020 10:39:43 +0100 Subject: media: split test drivers from platform directory When the first test device was added (vivi.c), there were just one file. I was too lazy on that time to create a separate directory just for it, so I kept it together with platform. Now, we have vivid, vicodec, vim2m and vimc. Also, a new virtual driver has been prepared to support DVB API. So, it is time to solve this mess, by placing test stuff on a separate directory. It should be noticed that we also have some skeleton drivers (for V4L and for DVB). For now, we'll keep them separate, as they're not really test drivers, but instead, just examples. The DVB frontend ones will likely be part of a new DVB test driver. By that time, it should make sense to move them here as well. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/Kconfig | 14 + drivers/media/platform/Kconfig | 23 - drivers/media/platform/Makefile | 5 - drivers/media/platform/vicodec/Kconfig | 13 - drivers/media/platform/vicodec/Makefile | 4 - drivers/media/platform/vicodec/codec-fwht.c | 958 --------- drivers/media/platform/vicodec/codec-fwht.h | 150 -- drivers/media/platform/vicodec/codec-v4l2-fwht.c | 367 ---- drivers/media/platform/vicodec/codec-v4l2-fwht.h | 64 - drivers/media/platform/vicodec/vicodec-core.c | 2238 -------------------- drivers/media/platform/vim2m.c | 1441 ------------- drivers/media/platform/vimc/Kconfig | 15 - drivers/media/platform/vimc/Makefile | 6 - drivers/media/platform/vimc/vimc-capture.c | 480 ----- drivers/media/platform/vimc/vimc-common.c | 369 ---- drivers/media/platform/vimc/vimc-common.h | 224 -- drivers/media/platform/vimc/vimc-core.c | 381 ---- drivers/media/platform/vimc/vimc-debayer.c | 581 ----- drivers/media/platform/vimc/vimc-scaler.c | 511 ----- drivers/media/platform/vimc/vimc-sensor.c | 376 ---- drivers/media/platform/vimc/vimc-streamer.c | 238 --- drivers/media/platform/vimc/vimc-streamer.h | 44 - drivers/media/platform/vivid/Kconfig | 41 - drivers/media/platform/vivid/Makefile | 12 - drivers/media/platform/vivid/vivid-cec.c | 286 --- drivers/media/platform/vivid/vivid-cec.h | 20 - drivers/media/platform/vivid/vivid-core.c | 2006 ------------------ drivers/media/platform/vivid/vivid-core.h | 612 ------ drivers/media/platform/vivid/vivid-ctrls.c | 1939 ----------------- drivers/media/platform/vivid/vivid-ctrls.h | 22 - drivers/media/platform/vivid/vivid-kthread-cap.c | 1007 --------- drivers/media/platform/vivid/vivid-kthread-cap.h | 14 - drivers/media/platform/vivid/vivid-kthread-out.c | 353 --- drivers/media/platform/vivid/vivid-kthread-out.h | 14 - drivers/media/platform/vivid/vivid-kthread-touch.c | 181 -- drivers/media/platform/vivid/vivid-kthread-touch.h | 13 - drivers/media/platform/vivid/vivid-meta-cap.c | 201 -- drivers/media/platform/vivid/vivid-meta-cap.h | 29 - drivers/media/platform/vivid/vivid-meta-out.c | 174 -- drivers/media/platform/vivid/vivid-meta-out.h | 25 - drivers/media/platform/vivid/vivid-osd.c | 388 ---- drivers/media/platform/vivid/vivid-osd.h | 15 - drivers/media/platform/vivid/vivid-radio-common.c | 177 -- drivers/media/platform/vivid/vivid-radio-common.h | 28 - drivers/media/platform/vivid/vivid-radio-rx.c | 278 --- drivers/media/platform/vivid/vivid-radio-rx.h | 19 - drivers/media/platform/vivid/vivid-radio-tx.c | 128 -- drivers/media/platform/vivid/vivid-radio-tx.h | 17 - drivers/media/platform/vivid/vivid-rds-gen.c | 157 -- drivers/media/platform/vivid/vivid-rds-gen.h | 42 - drivers/media/platform/vivid/vivid-sdr-cap.c | 570 ----- drivers/media/platform/vivid/vivid-sdr-cap.h | 24 - drivers/media/platform/vivid/vivid-touch-cap.c | 341 --- drivers/media/platform/vivid/vivid-touch-cap.h | 39 - drivers/media/platform/vivid/vivid-vbi-cap.c | 365 ---- drivers/media/platform/vivid/vivid-vbi-cap.h | 28 - drivers/media/platform/vivid/vivid-vbi-gen.c | 311 --- drivers/media/platform/vivid/vivid-vbi-gen.h | 21 - drivers/media/platform/vivid/vivid-vbi-out.c | 250 --- drivers/media/platform/vivid/vivid-vbi-out.h | 22 - drivers/media/platform/vivid/vivid-vid-cap.c | 1918 ----------------- drivers/media/platform/vivid/vivid-vid-cap.h | 59 - drivers/media/platform/vivid/vivid-vid-common.c | 1035 --------- drivers/media/platform/vivid/vivid-vid-common.h | 38 - drivers/media/platform/vivid/vivid-vid-out.c | 1210 ----------- drivers/media/platform/vivid/vivid-vid-out.h | 44 - drivers/media/test_drivers/Kconfig | 28 + drivers/media/test_drivers/Makefile | 9 + drivers/media/test_drivers/vicodec/Kconfig | 13 + drivers/media/test_drivers/vicodec/Makefile | 4 + drivers/media/test_drivers/vicodec/codec-fwht.c | 958 +++++++++ drivers/media/test_drivers/vicodec/codec-fwht.h | 150 ++ .../media/test_drivers/vicodec/codec-v4l2-fwht.c | 367 ++++ .../media/test_drivers/vicodec/codec-v4l2-fwht.h | 64 + drivers/media/test_drivers/vicodec/vicodec-core.c | 2238 ++++++++++++++++++++ drivers/media/test_drivers/vim2m.c | 1441 +++++++++++++ drivers/media/test_drivers/vimc/Kconfig | 15 + drivers/media/test_drivers/vimc/Makefile | 6 + drivers/media/test_drivers/vimc/vimc-capture.c | 480 +++++ drivers/media/test_drivers/vimc/vimc-common.c | 369 ++++ drivers/media/test_drivers/vimc/vimc-common.h | 224 ++ drivers/media/test_drivers/vimc/vimc-core.c | 381 ++++ drivers/media/test_drivers/vimc/vimc-debayer.c | 581 +++++ drivers/media/test_drivers/vimc/vimc-scaler.c | 511 +++++ drivers/media/test_drivers/vimc/vimc-sensor.c | 376 ++++ drivers/media/test_drivers/vimc/vimc-streamer.c | 238 +++ drivers/media/test_drivers/vimc/vimc-streamer.h | 44 + drivers/media/test_drivers/vivid/Kconfig | 41 + drivers/media/test_drivers/vivid/Makefile | 12 + drivers/media/test_drivers/vivid/vivid-cec.c | 286 +++ drivers/media/test_drivers/vivid/vivid-cec.h | 20 + drivers/media/test_drivers/vivid/vivid-core.c | 2006 ++++++++++++++++++ drivers/media/test_drivers/vivid/vivid-core.h | 612 ++++++ drivers/media/test_drivers/vivid/vivid-ctrls.c | 1939 +++++++++++++++++ drivers/media/test_drivers/vivid/vivid-ctrls.h | 22 + .../media/test_drivers/vivid/vivid-kthread-cap.c | 1007 +++++++++ .../media/test_drivers/vivid/vivid-kthread-cap.h | 14 + .../media/test_drivers/vivid/vivid-kthread-out.c | 353 +++ .../media/test_drivers/vivid/vivid-kthread-out.h | 14 + .../media/test_drivers/vivid/vivid-kthread-touch.c | 181 ++ .../media/test_drivers/vivid/vivid-kthread-touch.h | 13 + drivers/media/test_drivers/vivid/vivid-meta-cap.c | 201 ++ drivers/media/test_drivers/vivid/vivid-meta-cap.h | 29 + drivers/media/test_drivers/vivid/vivid-meta-out.c | 174 ++ drivers/media/test_drivers/vivid/vivid-meta-out.h | 25 + drivers/media/test_drivers/vivid/vivid-osd.c | 388 ++++ drivers/media/test_drivers/vivid/vivid-osd.h | 15 + .../media/test_drivers/vivid/vivid-radio-common.c | 177 ++ .../media/test_drivers/vivid/vivid-radio-common.h | 28 + drivers/media/test_drivers/vivid/vivid-radio-rx.c | 278 +++ drivers/media/test_drivers/vivid/vivid-radio-rx.h | 19 + drivers/media/test_drivers/vivid/vivid-radio-tx.c | 128 ++ drivers/media/test_drivers/vivid/vivid-radio-tx.h | 17 + drivers/media/test_drivers/vivid/vivid-rds-gen.c | 157 ++ drivers/media/test_drivers/vivid/vivid-rds-gen.h | 42 + drivers/media/test_drivers/vivid/vivid-sdr-cap.c | 570 +++++ drivers/media/test_drivers/vivid/vivid-sdr-cap.h | 24 + drivers/media/test_drivers/vivid/vivid-touch-cap.c | 341 +++ drivers/media/test_drivers/vivid/vivid-touch-cap.h | 39 + drivers/media/test_drivers/vivid/vivid-vbi-cap.c | 365 ++++ drivers/media/test_drivers/vivid/vivid-vbi-cap.h | 28 + drivers/media/test_drivers/vivid/vivid-vbi-gen.c | 311 +++ drivers/media/test_drivers/vivid/vivid-vbi-gen.h | 21 + drivers/media/test_drivers/vivid/vivid-vbi-out.c | 250 +++ drivers/media/test_drivers/vivid/vivid-vbi-out.h | 22 + drivers/media/test_drivers/vivid/vivid-vid-cap.c | 1918 +++++++++++++++++ drivers/media/test_drivers/vivid/vivid-vid-cap.h | 59 + .../media/test_drivers/vivid/vivid-vid-common.c | 1035 +++++++++ .../media/test_drivers/vivid/vivid-vid-common.h | 38 + drivers/media/test_drivers/vivid/vivid-vid-out.c | 1210 +++++++++++ drivers/media/test_drivers/vivid/vivid-vid-out.h | 44 + 131 files changed, 22984 insertions(+), 22961 deletions(-) delete mode 100644 drivers/media/platform/vicodec/Kconfig delete mode 100644 drivers/media/platform/vicodec/Makefile delete mode 100644 drivers/media/platform/vicodec/codec-fwht.c delete mode 100644 drivers/media/platform/vicodec/codec-fwht.h delete mode 100644 drivers/media/platform/vicodec/codec-v4l2-fwht.c delete mode 100644 drivers/media/platform/vicodec/codec-v4l2-fwht.h delete mode 100644 drivers/media/platform/vicodec/vicodec-core.c delete mode 100644 drivers/media/platform/vim2m.c delete mode 100644 drivers/media/platform/vimc/Kconfig delete mode 100644 drivers/media/platform/vimc/Makefile delete mode 100644 drivers/media/platform/vimc/vimc-capture.c delete mode 100644 drivers/media/platform/vimc/vimc-common.c delete mode 100644 drivers/media/platform/vimc/vimc-common.h delete mode 100644 drivers/media/platform/vimc/vimc-core.c delete mode 100644 drivers/media/platform/vimc/vimc-debayer.c delete mode 100644 drivers/media/platform/vimc/vimc-scaler.c delete mode 100644 drivers/media/platform/vimc/vimc-sensor.c delete mode 100644 drivers/media/platform/vimc/vimc-streamer.c delete mode 100644 drivers/media/platform/vimc/vimc-streamer.h delete mode 100644 drivers/media/platform/vivid/Kconfig delete mode 100644 drivers/media/platform/vivid/Makefile delete mode 100644 drivers/media/platform/vivid/vivid-cec.c delete mode 100644 drivers/media/platform/vivid/vivid-cec.h delete mode 100644 drivers/media/platform/vivid/vivid-core.c delete mode 100644 drivers/media/platform/vivid/vivid-core.h delete mode 100644 drivers/media/platform/vivid/vivid-ctrls.c delete mode 100644 drivers/media/platform/vivid/vivid-ctrls.h delete mode 100644 drivers/media/platform/vivid/vivid-kthread-cap.c delete mode 100644 drivers/media/platform/vivid/vivid-kthread-cap.h delete mode 100644 drivers/media/platform/vivid/vivid-kthread-out.c delete mode 100644 drivers/media/platform/vivid/vivid-kthread-out.h delete mode 100644 drivers/media/platform/vivid/vivid-kthread-touch.c delete mode 100644 drivers/media/platform/vivid/vivid-kthread-touch.h delete mode 100644 drivers/media/platform/vivid/vivid-meta-cap.c delete mode 100644 drivers/media/platform/vivid/vivid-meta-cap.h delete mode 100644 drivers/media/platform/vivid/vivid-meta-out.c delete mode 100644 drivers/media/platform/vivid/vivid-meta-out.h delete mode 100644 drivers/media/platform/vivid/vivid-osd.c delete mode 100644 drivers/media/platform/vivid/vivid-osd.h delete mode 100644 drivers/media/platform/vivid/vivid-radio-common.c delete mode 100644 drivers/media/platform/vivid/vivid-radio-common.h delete mode 100644 drivers/media/platform/vivid/vivid-radio-rx.c delete mode 100644 drivers/media/platform/vivid/vivid-radio-rx.h delete mode 100644 drivers/media/platform/vivid/vivid-radio-tx.c delete mode 100644 drivers/media/platform/vivid/vivid-radio-tx.h delete mode 100644 drivers/media/platform/vivid/vivid-rds-gen.c delete mode 100644 drivers/media/platform/vivid/vivid-rds-gen.h delete mode 100644 drivers/media/platform/vivid/vivid-sdr-cap.c delete mode 100644 drivers/media/platform/vivid/vivid-sdr-cap.h delete mode 100644 drivers/media/platform/vivid/vivid-touch-cap.c delete mode 100644 drivers/media/platform/vivid/vivid-touch-cap.h delete mode 100644 drivers/media/platform/vivid/vivid-vbi-cap.c delete mode 100644 drivers/media/platform/vivid/vivid-vbi-cap.h delete mode 100644 drivers/media/platform/vivid/vivid-vbi-gen.c delete mode 100644 drivers/media/platform/vivid/vivid-vbi-gen.h delete mode 100644 drivers/media/platform/vivid/vivid-vbi-out.c delete mode 100644 drivers/media/platform/vivid/vivid-vbi-out.h delete mode 100644 drivers/media/platform/vivid/vivid-vid-cap.c delete mode 100644 drivers/media/platform/vivid/vivid-vid-cap.h delete mode 100644 drivers/media/platform/vivid/vivid-vid-common.c delete mode 100644 drivers/media/platform/vivid/vivid-vid-common.h delete mode 100644 drivers/media/platform/vivid/vivid-vid-out.c delete mode 100644 drivers/media/platform/vivid/vivid-vid-out.h create mode 100644 drivers/media/test_drivers/Kconfig create mode 100644 drivers/media/test_drivers/Makefile create mode 100644 drivers/media/test_drivers/vicodec/Kconfig create mode 100644 drivers/media/test_drivers/vicodec/Makefile create mode 100644 drivers/media/test_drivers/vicodec/codec-fwht.c create mode 100644 drivers/media/test_drivers/vicodec/codec-fwht.h create mode 100644 drivers/media/test_drivers/vicodec/codec-v4l2-fwht.c create mode 100644 drivers/media/test_drivers/vicodec/codec-v4l2-fwht.h create mode 100644 drivers/media/test_drivers/vicodec/vicodec-core.c create mode 100644 drivers/media/test_drivers/vim2m.c create mode 100644 drivers/media/test_drivers/vimc/Kconfig create mode 100644 drivers/media/test_drivers/vimc/Makefile create mode 100644 drivers/media/test_drivers/vimc/vimc-capture.c create mode 100644 drivers/media/test_drivers/vimc/vimc-common.c create mode 100644 drivers/media/test_drivers/vimc/vimc-common.h create mode 100644 drivers/media/test_drivers/vimc/vimc-core.c create mode 100644 drivers/media/test_drivers/vimc/vimc-debayer.c create mode 100644 drivers/media/test_drivers/vimc/vimc-scaler.c create mode 100644 drivers/media/test_drivers/vimc/vimc-sensor.c create mode 100644 drivers/media/test_drivers/vimc/vimc-streamer.c create mode 100644 drivers/media/test_drivers/vimc/vimc-streamer.h create mode 100644 drivers/media/test_drivers/vivid/Kconfig create mode 100644 drivers/media/test_drivers/vivid/Makefile create mode 100644 drivers/media/test_drivers/vivid/vivid-cec.c create mode 100644 drivers/media/test_drivers/vivid/vivid-cec.h create mode 100644 drivers/media/test_drivers/vivid/vivid-core.c create mode 100644 drivers/media/test_drivers/vivid/vivid-core.h create mode 100644 drivers/media/test_drivers/vivid/vivid-ctrls.c create mode 100644 drivers/media/test_drivers/vivid/vivid-ctrls.h create mode 100644 drivers/media/test_drivers/vivid/vivid-kthread-cap.c create mode 100644 drivers/media/test_drivers/vivid/vivid-kthread-cap.h create mode 100644 drivers/media/test_drivers/vivid/vivid-kthread-out.c create mode 100644 drivers/media/test_drivers/vivid/vivid-kthread-out.h create mode 100644 drivers/media/test_drivers/vivid/vivid-kthread-touch.c create mode 100644 drivers/media/test_drivers/vivid/vivid-kthread-touch.h create mode 100644 drivers/media/test_drivers/vivid/vivid-meta-cap.c create mode 100644 drivers/media/test_drivers/vivid/vivid-meta-cap.h create mode 100644 drivers/media/test_drivers/vivid/vivid-meta-out.c create mode 100644 drivers/media/test_drivers/vivid/vivid-meta-out.h create mode 100644 drivers/media/test_drivers/vivid/vivid-osd.c create mode 100644 drivers/media/test_drivers/vivid/vivid-osd.h create mode 100644 drivers/media/test_drivers/vivid/vivid-radio-common.c create mode 100644 drivers/media/test_drivers/vivid/vivid-radio-common.h create mode 100644 drivers/media/test_drivers/vivid/vivid-radio-rx.c create mode 100644 drivers/media/test_drivers/vivid/vivid-radio-rx.h create mode 100644 drivers/media/test_drivers/vivid/vivid-radio-tx.c create mode 100644 drivers/media/test_drivers/vivid/vivid-radio-tx.h create mode 100644 drivers/media/test_drivers/vivid/vivid-rds-gen.c create mode 100644 drivers/media/test_drivers/vivid/vivid-rds-gen.h create mode 100644 drivers/media/test_drivers/vivid/vivid-sdr-cap.c create mode 100644 drivers/media/test_drivers/vivid/vivid-sdr-cap.h create mode 100644 drivers/media/test_drivers/vivid/vivid-touch-cap.c create mode 100644 drivers/media/test_drivers/vivid/vivid-touch-cap.h create mode 100644 drivers/media/test_drivers/vivid/vivid-vbi-cap.c create mode 100644 drivers/media/test_drivers/vivid/vivid-vbi-cap.h create mode 100644 drivers/media/test_drivers/vivid/vivid-vbi-gen.c create mode 100644 drivers/media/test_drivers/vivid/vivid-vbi-gen.h create mode 100644 drivers/media/test_drivers/vivid/vivid-vbi-out.c create mode 100644 drivers/media/test_drivers/vivid/vivid-vbi-out.h create mode 100644 drivers/media/test_drivers/vivid/vivid-vid-cap.c create mode 100644 drivers/media/test_drivers/vivid/vivid-vid-cap.h create mode 100644 drivers/media/test_drivers/vivid/vivid-vid-common.c create mode 100644 drivers/media/test_drivers/vivid/vivid-vid-common.h create mode 100644 drivers/media/test_drivers/vivid/vivid-vid-out.c create mode 100644 drivers/media/test_drivers/vivid/vivid-vid-out.h (limited to 'drivers/media') diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig index 72d4f3e0b081..b35c980dcf56 100644 --- a/drivers/media/Kconfig +++ b/drivers/media/Kconfig @@ -101,6 +101,19 @@ config MEDIA_PLATFORM_SUPPORT Say Y when you want to be able so see such devices. +config MEDIA_TEST_SUPPORT + bool "Test drivers support" + help + Those drivers should not be used on production Kernels, but + can be useful on debug ones. It enables several dummy drivers + that simulate a real hardware. Very useful to test userspace + applications and to validate if the subsystem core is doesn't + have regressions. + + Say Y if you want to use some virtual test driver. + + In case of doubts, say N. + source "drivers/media/cec/Kconfig" source "drivers/media/mc/Kconfig" @@ -180,6 +193,7 @@ source "drivers/media/radio/Kconfig" if MEDIA_PLATFORM_SUPPORT source "drivers/media/platform/Kconfig" +source "drivers/media/test_drivers/Kconfig" source "drivers/media/mmc/Kconfig" endif diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index c4178420d2c5..80028337bf00 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -529,29 +529,6 @@ config VIDEO_TI_SC config VIDEO_TI_CSC tristate -menuconfig V4L_TEST_DRIVERS - bool "Media test drivers" - depends on MEDIA_CAMERA_SUPPORT - -if V4L_TEST_DRIVERS - -source "drivers/media/platform/vimc/Kconfig" - -source "drivers/media/platform/vivid/Kconfig" - -config VIDEO_VIM2M - tristate "Virtual Memory-to-Memory Driver" - depends on VIDEO_DEV && VIDEO_V4L2 - select VIDEOBUF2_VMALLOC - select V4L2_MEM2MEM_DEV - help - This is a virtual test device for the memory-to-memory driver - framework. - -source "drivers/media/platform/vicodec/Kconfig" - -endif #V4L_TEST_DRIVERS - menuconfig DVB_PLATFORM_DRIVERS bool "DVB platform devices" depends on MEDIA_DIGITAL_TV_SUPPORT diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile index d13db96e3015..a0194ef1211f 100644 --- a/drivers/media/platform/Makefile +++ b/drivers/media/platform/Makefile @@ -14,11 +14,6 @@ obj-$(CONFIG_VIDEO_PXA27x) += pxa_camera.o obj-$(CONFIG_VIDEO_VIU) += fsl-viu.o -obj-$(CONFIG_VIDEO_VIMC) += vimc/ -obj-$(CONFIG_VIDEO_VIVID) += vivid/ -obj-$(CONFIG_VIDEO_VIM2M) += vim2m.o -obj-$(CONFIG_VIDEO_VICODEC) += vicodec/ - obj-y += ti-vpe/ obj-$(CONFIG_VIDEO_MX2_EMMAPRP) += mx2_emmaprp.o diff --git a/drivers/media/platform/vicodec/Kconfig b/drivers/media/platform/vicodec/Kconfig deleted file mode 100644 index 89456665cb16..000000000000 --- 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 01bf7e9308a6..000000000000 --- 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 31faf319e508..000000000000 --- 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 -#include -#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 b6fec2b1cbca..000000000000 --- 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 -#include -#include - -/* - * 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 b6e39fbd8ad5..000000000000 --- 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 -#include -#include -#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 1a0d2a9f931a..000000000000 --- 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 30ced1c21387..000000000000 --- a/drivers/media/platform/vicodec/vicodec-core.c +++ /dev/null @@ -1,2238 +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 -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include "codec-v4l2-fwht.h" - -MODULE_DESCRIPTION("Virtual codec device"); -MODULE_AUTHOR("Hans Verkuil "); -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; - spinlock_t *lock; - - struct v4l2_ctrl_handler hdl; - - /* 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 && - v4l2_m2m_is_last_draining_src_buf(ctx->fh.m2m_ctx, src_buf)) { - dst_buf->flags |= V4L2_BUF_FLAG_LAST; - v4l2_event_queue_fh(&ctx->fh, &vicodec_eos_event); - v4l2_m2m_mark_stopped(ctx->fh.m2m_ctx); - } - 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->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 (v4l2_m2m_is_last_draining_src_buf(ctx->fh.m2m_ctx, - 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 (v4l2_m2m_is_last_draining_src_buf(ctx->fh.m2m_ctx, - 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_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; - - ret = v4l2_m2m_ioctl_encoder_cmd(file, fh, ec); - if (ret < 0) - return ret; - - if (ec->cmd == V4L2_ENC_CMD_STOP && - v4l2_m2m_has_stopped(ctx->fh.m2m_ctx)) - v4l2_event_queue_fh(&ctx->fh, &vicodec_eos_event); - - if (ec->cmd == V4L2_ENC_CMD_START && - v4l2_m2m_has_stopped(ctx->fh.m2m_ctx)) - vb2_clear_last_buffer_dequeued(&ctx->fh.m2m_ctx->cap_q_ctx.q); - - return 0; -} - -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; - - ret = v4l2_m2m_ioctl_decoder_cmd(file, fh, dc); - if (ret < 0) - return ret; - - if (dc->cmd == V4L2_DEC_CMD_STOP && - v4l2_m2m_has_stopped(ctx->fh.m2m_ctx)) - v4l2_event_queue_fh(&ctx->fh, &vicodec_eos_event); - - if (dc->cmd == V4L2_DEC_CMD_START && - v4l2_m2m_has_stopped(ctx->fh.m2m_ctx)) - vb2_clear_last_buffer_dequeued(&ctx->fh.m2m_ctx->cap_q_ctx.q); - - return 0; -} - -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 (!V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type) && - vb2_is_streaming(vb->vb2_queue) && - v4l2_m2m_dst_buf_is_last(ctx->fh.m2m_ctx)) { - unsigned int i; - - for (i = 0; i < vb->num_planes; i++) - vb->planes[i].bytesused = 0; - - vbuf->field = V4L2_FIELD_NONE; - vbuf->sequence = - get_q_data(ctx, vb->vb2_queue->type)->sequence++; - - v4l2_m2m_last_buffer_done(ctx->fh.m2m_ctx, vbuf); - 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; - - v4l2_m2m_update_start_streaming_state(ctx->fh.m2m_ctx, q); - - 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); - - v4l2_m2m_update_stop_streaming_state(ctx->fh.m2m_ctx, q); - - if (V4L2_TYPE_IS_OUTPUT(q->type) && - v4l2_m2m_has_stopped(ctx->fh.m2m_ctx)) - v4l2_event_queue_fh(&ctx->fh, &vicodec_eos_event); - - 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); - 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_VIDEO, 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); - - if (register_instance(dev, &dev->stateful_enc, - "stateful-encoder", true)) - goto unreg_dev; - - if (register_instance(dev, &dev->stateful_dec, - "stateful-decoder", false)) - goto unreg_sf_enc; - - if (register_instance(dev, &dev->stateless_dec, - "stateless-decoder", false)) - 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/vim2m.c b/drivers/media/platform/vim2m.c deleted file mode 100644 index ac6717fbb764..000000000000 --- 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, - * Marek Szyprowski, - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the - * License, or (at your option) any later version - */ -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -MODULE_DESCRIPTION("Virtual device for mem2mem framework testing"); -MODULE_AUTHOR("Pawel Osciak, "); -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_VIDEO, 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 bd221d3e1a4a..000000000000 --- 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 a53b2b532e9f..000000000000 --- a/drivers/media/platform/vimc/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -vimc-y := vimc-core.o vimc-common.o vimc-streamer.o vimc-capture.o \ - vimc-debayer.o vimc-scaler.o vimc-sensor.o - -obj-$(CONFIG_VIDEO_VIMC) += vimc.o - diff --git a/drivers/media/platform/vimc/vimc-capture.c b/drivers/media/platform/vimc/vimc-capture.c deleted file mode 100644 index 23e740c1c5c0..000000000000 --- a/drivers/media/platform/vimc/vimc-capture.c +++ /dev/null @@ -1,480 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * vimc-capture.c Virtual Media Controller Driver - * - * Copyright (C) 2015-2017 Helen Koike - */ - -#include -#include -#include - -#include "vimc-common.h" -#include "vimc-streamer.h" - -struct vimc_cap_device { - struct vimc_ent_device ved; - struct video_device vdev; - 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; - struct media_pad pad; -}; - -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->ved.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->ved.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_vdev_link_validate, -}; - -void vimc_cap_release(struct vimc_ent_device *ved) -{ - struct vimc_cap_device *vcap = - container_of(ved, struct vimc_cap_device, ved); - - media_entity_cleanup(vcap->ved.ent); - kfree(vcap); -} - -void vimc_cap_unregister(struct vimc_ent_device *ved) -{ - struct vimc_cap_device *vcap = - container_of(ved, struct vimc_cap_device, ved); - - vb2_queue_release(&vcap->queue); - 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; -} - -struct vimc_ent_device *vimc_cap_add(struct vimc_device *vimc, - const char *vcfg_name) -{ - struct v4l2_device *v4l2_dev = &vimc->v4l2_dev; - const struct vimc_pix_map *vpix; - 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 NULL; - - /* Initialize the media entity */ - vcap->vdev.entity.name = vcfg_name; - vcap->vdev.entity.function = MEDIA_ENT_F_IO_V4L; - vcap->pad.flags = MEDIA_PAD_FL_SINK; - ret = media_entity_pads_init(&vcap->vdev.entity, - 1, &vcap->pad); - if (ret) - goto err_free_vcap; - - /* 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(vimc->mdev.dev, "%s: vb2 queue init failed (err=%d)\n", - vcfg_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; - vcap->ved.dev = vimc->mdev.dev; - - /* 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 = video_device_release_empty; - 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, vcfg_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_VIDEO, -1); - if (ret) { - dev_err(vimc->mdev.dev, "%s: video register failed (err=%d)\n", - vcap->vdev.name, ret); - goto err_release_queue; - } - - return &vcap->ved; - -err_release_queue: - vb2_queue_release(q); -err_clean_m_ent: - media_entity_cleanup(&vcap->vdev.entity); -err_free_vcap: - kfree(vcap); - - return NULL; -} diff --git a/drivers/media/platform/vimc/vimc-common.c b/drivers/media/platform/vimc/vimc-common.c deleted file mode 100644 index c95c17c048f2..000000000000 --- a/drivers/media/platform/vimc/vimc-common.c +++ /dev/null @@ -1,369 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * vimc-common.c Virtual Media Controller Driver - * - * Copyright (C) 2015-2017 Helen Koike - */ - -#include -#include - -#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, - }, -}; - -bool vimc_is_source(struct media_entity *ent) -{ - unsigned int i; - - for (i = 0; i < ent->num_pads; i++) - if (ent->pads[i].flags & MEDIA_PAD_FL_SINK) - return false; - return 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]; -} - -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; -} - -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; -} - -static int vimc_get_pix_format(struct media_pad *pad, - struct v4l2_pix_format *fmt) -{ - if (is_media_entity_v4l2_subdev(pad->entity)) { - struct v4l2_subdev *sd = - media_entity_to_v4l2_subdev(pad->entity); - struct v4l2_subdev_format sd_fmt; - const struct vimc_pix_map *pix_map; - int ret; - - sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; - sd_fmt.pad = pad->index; - - ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &sd_fmt); - if (ret) - return ret; - - v4l2_fill_pix_format(fmt, &sd_fmt.format); - pix_map = vimc_pix_map_by_code(sd_fmt.format.code); - fmt->pixelformat = pix_map->pixelformat; - } 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); - - if (!ved->vdev_get_format) - return -ENOIOCTLCMD; - - ved->vdev_get_format(ved, fmt); - } else { - return -EINVAL; - } - - return 0; -} - -int vimc_vdev_link_validate(struct media_link *link) -{ - struct v4l2_pix_format source_fmt, sink_fmt; - int ret; - - ret = vimc_get_pix_format(link->source, &source_fmt); - if (ret) - return ret; - - ret = vimc_get_pix_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.width, source_fmt.height, - source_fmt.pixelformat, source_fmt.colorspace, - source_fmt.quantization, source_fmt.xfer_func, - source_fmt.ycbcr_enc, - /* sink */ - link->sink->entity->name, - sink_fmt.width, sink_fmt.height, - sink_fmt.pixelformat, sink_fmt.colorspace, - sink_fmt.quantization, sink_fmt.xfer_func, - sink_fmt.ycbcr_enc); - - /* The width, height and pixelformat must match. */ - if (source_fmt.width != sink_fmt.width || - source_fmt.height != sink_fmt.height || - source_fmt.pixelformat != sink_fmt.pixelformat) - 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.field != sink_fmt.field && - sink_fmt.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.colorspace == V4L2_COLORSPACE_DEFAULT || - sink_fmt.colorspace == V4L2_COLORSPACE_DEFAULT) - return 0; - - /* Colorspace must match. */ - if (source_fmt.colorspace != sink_fmt.colorspace) - return -EPIPE; - - /* Colorimetry must match if they are not set to DEFAULT */ - if (source_fmt.ycbcr_enc != V4L2_YCBCR_ENC_DEFAULT && - sink_fmt.ycbcr_enc != V4L2_YCBCR_ENC_DEFAULT && - source_fmt.ycbcr_enc != sink_fmt.ycbcr_enc) - return -EPIPE; - - if (source_fmt.quantization != V4L2_QUANTIZATION_DEFAULT && - sink_fmt.quantization != V4L2_QUANTIZATION_DEFAULT && - source_fmt.quantization != sink_fmt.quantization) - return -EPIPE; - - if (source_fmt.xfer_func != V4L2_XFER_FUNC_DEFAULT && - sink_fmt.xfer_func != V4L2_XFER_FUNC_DEFAULT && - source_fmt.xfer_func != sink_fmt.xfer_func) - return -EPIPE; - - return 0; -} - -static const struct media_entity_operations vimc_ent_sd_mops = { - .link_validate = v4l2_subdev_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, - struct media_pad *pads, - const struct v4l2_subdev_ops *sd_ops) -{ - int ret; - - /* Fill the vimc_ent_device struct */ - ved->ent = &sd->entity; - - /* Initialize the subdev */ - v4l2_subdev_init(sd, sd_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, pads); - if (ret) - return ret; - - /* 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); - return ret; -} diff --git a/drivers/media/platform/vimc/vimc-common.h b/drivers/media/platform/vimc/vimc-common.h deleted file mode 100644 index 616d5a6b0754..000000000000 --- a/drivers/media/platform/vimc/vimc-common.h +++ /dev/null @@ -1,224 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * vimc-common.h Virtual Media Controller Driver - * - * Copyright (C) 2015-2017 Helen Koike - */ - -#ifndef _VIMC_COMMON_H_ -#define _VIMC_COMMON_H_ - -#include -#include -#include -#include - -#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_CID_MEAN_WIN_SIZE (VIMC_CID_VIMC_BASE + 1) - -#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) - -/* Source and sink pad checks */ -#define VIMC_IS_SRC(pad) (pad) -#define VIMC_IS_SINK(pad) (!(pad)) - -/** - * 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_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 an entity in the - * topology - * - * @dev: a pointer of the device struct of the driver - * @ent: the pointer to struct media_entity for 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 device *dev; - struct media_entity *ent; - 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); -}; - -/** - * struct vimc_device - main device for vimc driver - * - * @pipe_cfg pointer to the vimc pipeline configuration structure - * @ent_devs array of vimc_ent_device pointers - * @mdev the associated media_device parent - * @v4l2_dev Internal v4l2 parent device - */ -struct vimc_device { - const struct vimc_pipeline_config *pipe_cfg; - struct vimc_ent_device **ent_devs; - struct media_device mdev; - struct v4l2_device v4l2_dev; -}; - -/** - * struct vimc_ent_config Structure which describes individual - * configuration for each entity - * - * @name entity name - * @ved pointer to vimc_ent_device (a node in the - * topology) - * @add initializes and registers - * vim entity - called from vimc-core - * @unregister unregisters vimc entity - called from vimc-core - * @release releases vimc entity - called from the v4l2_dev - * release callback - */ -struct vimc_ent_config { - const char *name; - struct vimc_ent_device *(*add)(struct vimc_device *vimc, - const char *vcfg_name); - void (*unregister)(struct vimc_ent_device *ved); - void (*release)(struct vimc_ent_device *ved); -}; - -/** - * vimc_is_source - returns true if the entity has only source pads - * - * @ent: pointer to &struct media_entity - * - */ -bool vimc_is_source(struct media_entity *ent); - -/* prototypes for vimc_ent_config hooks */ -struct vimc_ent_device *vimc_cap_add(struct vimc_device *vimc, - const char *vcfg_name); -void vimc_cap_unregister(struct vimc_ent_device *ved); -void vimc_cap_release(struct vimc_ent_device *ved); - -struct vimc_ent_device *vimc_deb_add(struct vimc_device *vimc, - const char *vcfg_name); -void vimc_deb_release(struct vimc_ent_device *ved); - -struct vimc_ent_device *vimc_sca_add(struct vimc_device *vimc, - const char *vcfg_name); -void vimc_sca_release(struct vimc_ent_device *ved); - -struct vimc_ent_device *vimc_sen_add(struct vimc_device *vimc, - const char *vcfg_name); -void vimc_sen_release(struct vimc_ent_device *ved); - -/** - * 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: the array of pads of the entity, the caller should set the - flags of the pads - * @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, - struct media_pad *pads, - const struct v4l2_subdev_ops *sd_ops); - -/** - * vimc_vdev_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_vdev_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 339126e565dc..000000000000 --- a/drivers/media/platform/vimc/vimc-core.c +++ /dev/null @@ -1,381 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * vimc-core.c Virtual Media Controller Driver - * - * Copyright (C) 2015-2017 Helen Koike - */ - -#include -#include -#include -#include -#include - -#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, \ -} - -/* 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 struct vimc_ent_config ent_config[] = { - { - .name = "Sensor A", - .add = vimc_sen_add, - .release = vimc_sen_release, - }, - { - .name = "Sensor B", - .add = vimc_sen_add, - .release = vimc_sen_release, - }, - { - .name = "Debayer A", - .add = vimc_deb_add, - .release = vimc_deb_release, - }, - { - .name = "Debayer B", - .add = vimc_deb_add, - .release = vimc_deb_release, - }, - { - .name = "Raw Capture 0", - .add = vimc_cap_add, - .unregister = vimc_cap_unregister, - .release = vimc_cap_release, - }, - { - .name = "Raw Capture 1", - .add = vimc_cap_add, - .unregister = vimc_cap_unregister, - .release = vimc_cap_release, - }, - { - /* TODO: change this to vimc-input when it is implemented */ - .name = "RGB/YUV Input", - .add = vimc_sen_add, - .release = vimc_sen_release, - }, - { - .name = "Scaler", - .add = vimc_sca_add, - .release = vimc_sca_release, - }, - { - .name = "RGB/YUV Capture", - .add = vimc_cap_add, - .unregister = vimc_cap_unregister, - .release = vimc_cap_release, - }, -}; - -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 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 void vimc_rm_links(struct vimc_device *vimc) -{ - unsigned int i; - - for (i = 0; i < vimc->pipe_cfg->num_ents; i++) - media_entity_remove_links(vimc->ent_devs[i]->ent); -} - -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]; - - struct vimc_ent_device *ved_src = - vimc->ent_devs[link->src_ent]; - struct vimc_ent_device *ved_sink = - vimc->ent_devs[link->sink_ent]; - - ret = media_create_pad_link(ved_src->ent, link->src_pad, - ved_sink->ent, link->sink_pad, - link->flags); - if (ret) - goto err_rm_links; - } - - return 0; - -err_rm_links: - vimc_rm_links(vimc); - return ret; -} - -static int vimc_add_subdevs(struct vimc_device *vimc) -{ - unsigned int i; - - for (i = 0; i < vimc->pipe_cfg->num_ents; i++) { - dev_dbg(vimc->mdev.dev, "new entity for %s\n", - vimc->pipe_cfg->ents[i].name); - vimc->ent_devs[i] = vimc->pipe_cfg->ents[i].add(vimc, - vimc->pipe_cfg->ents[i].name); - if (!vimc->ent_devs[i]) { - dev_err(vimc->mdev.dev, "add new entity for %s\n", - vimc->pipe_cfg->ents[i].name); - return -EINVAL; - } - } - return 0; -} - -static void vimc_release_subdevs(struct vimc_device *vimc) -{ - unsigned int i; - - for (i = 0; i < vimc->pipe_cfg->num_ents; i++) - if (vimc->ent_devs[i]) - vimc->pipe_cfg->ents[i].release(vimc->ent_devs[i]); -} - -static void vimc_unregister_subdevs(struct vimc_device *vimc) -{ - unsigned int i; - - for (i = 0; i < vimc->pipe_cfg->num_ents; i++) - if (vimc->ent_devs[i] && vimc->pipe_cfg->ents[i].unregister) - vimc->pipe_cfg->ents[i].unregister(vimc->ent_devs[i]); -} - -static void vimc_v4l2_dev_release(struct v4l2_device *v4l2_dev) -{ - struct vimc_device *vimc = - container_of(v4l2_dev, struct vimc_device, v4l2_dev); - - vimc_release_subdevs(vimc); - media_device_cleanup(&vimc->mdev); - kfree(vimc->ent_devs); - kfree(vimc); -} - -static int vimc_register_devices(struct vimc_device *vimc) -{ - int ret; - - /* 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; - } - /* allocate ent_devs */ - vimc->ent_devs = kcalloc(vimc->pipe_cfg->num_ents, - sizeof(*vimc->ent_devs), GFP_KERNEL); - if (!vimc->ent_devs) { - ret = -ENOMEM; - goto err_v4l2_unregister; - } - - /* Invoke entity config hooks to initialize and register subdevs */ - ret = vimc_add_subdevs(vimc); - if (ret) - /* remove sundevs that got added */ - goto err_rm_subdevs; - - /* Initialize links */ - ret = vimc_create_links(vimc); - if (ret) - goto err_rm_subdevs; - - /* 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_rm_subdevs; - } - - /* 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); -err_rm_subdevs: - vimc_unregister_subdevs(vimc); - vimc_release_subdevs(vimc); - kfree(vimc->ent_devs); -err_v4l2_unregister: - v4l2_device_unregister(&vimc->v4l2_dev); - - return ret; -} - -static void vimc_unregister(struct vimc_device *vimc) -{ - vimc_unregister_subdevs(vimc); - media_device_unregister(&vimc->mdev); - v4l2_device_unregister(&vimc->v4l2_dev); -} - -static int vimc_probe(struct platform_device *pdev) -{ - struct vimc_device *vimc; - int ret; - - dev_dbg(&pdev->dev, "probe"); - - vimc = kzalloc(sizeof(*vimc), GFP_KERNEL); - if (!vimc) - return -ENOMEM; - - vimc->pipe_cfg = &pipe_cfg; - - /* 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); - - ret = vimc_register_devices(vimc); - if (ret) { - media_device_cleanup(&vimc->mdev); - kfree(vimc); - return ret; - } - /* - * the release cb is set only after successful registration. - * if the registration fails, we release directly from probe - */ - - vimc->v4l2_dev.release = vimc_v4l2_dev_release; - platform_set_drvdata(pdev, vimc); - return 0; -} - -static int vimc_remove(struct platform_device *pdev) -{ - struct vimc_device *vimc = platform_get_drvdata(pdev); - - dev_dbg(&pdev->dev, "remove"); - - vimc_unregister(vimc); - v4l2_device_put(&vimc->v4l2_dev); - - return 0; -} - -static void vimc_dev_release(struct device *dev) -{ -} - -static struct platform_device vimc_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_pdev); - if (ret) { - dev_err(&vimc_pdev.dev, - "platform device registration failed (err=%d)\n", ret); - return ret; - } - - ret = platform_driver_register(&vimc_pdrv); - if (ret) { - dev_err(&vimc_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_pdev); -} - -module_init(vimc_init); -module_exit(vimc_exit); - -MODULE_DESCRIPTION("Virtual Media Controller Driver (VIMC)"); -MODULE_AUTHOR("Helen Fornazier "); -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 baf6bf9f65b5..000000000000 --- a/drivers/media/platform/vimc/vimc-debayer.c +++ /dev/null @@ -1,581 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * vimc-debayer.c Virtual Media Controller Driver - * - * Copyright (C) 2015-2017 Helen Koike - */ - -#include -#include -#include -#include -#include -#include -#include - -#include "vimc-common.h" - -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; - /* 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; - unsigned int mean_win_size; - struct v4l2_ctrl_handler hdl; - struct media_pad pads[2]; -}; - -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 (VIMC_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 (VIMC_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 (VIMC_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 (VIMC_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->ved.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_core_ops vimc_deb_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_deb_video_ops = { - .s_stream = vimc_deb_s_stream, -}; - -static const struct v4l2_subdev_ops vimc_deb_ops = { - .core = &vimc_deb_core_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 = vdeb->mean_win_size / 2; - - /* Sum the values of the colors in the mean window */ - - dev_dbg(vdeb->ved.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->ved.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->ved.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->ved.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->ved.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 int vimc_deb_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct vimc_deb_device *vdeb = - container_of(ctrl->handler, struct vimc_deb_device, hdl); - - switch (ctrl->id) { - case VIMC_CID_MEAN_WIN_SIZE: - vdeb->mean_win_size = ctrl->val; - break; - default: - return -EINVAL; - } - return 0; -} - -static const struct v4l2_ctrl_ops vimc_deb_ctrl_ops = { - .s_ctrl = vimc_deb_s_ctrl, -}; - -void vimc_deb_release(struct vimc_ent_device *ved) -{ - struct vimc_deb_device *vdeb = - container_of(ved, struct vimc_deb_device, ved); - - v4l2_ctrl_handler_free(&vdeb->hdl); - media_entity_cleanup(vdeb->ved.ent); - kfree(vdeb); -} - -static const struct v4l2_ctrl_config vimc_deb_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_deb_ctrl_mean_win_size = { - .ops = &vimc_deb_ctrl_ops, - .id = VIMC_CID_MEAN_WIN_SIZE, - .name = "Debayer Mean Window Size", - .type = V4L2_CTRL_TYPE_INTEGER, - .min = 1, - .max = 25, - .step = 2, - .def = 3, -}; - -struct vimc_ent_device *vimc_deb_add(struct vimc_device *vimc, - const char *vcfg_name) -{ - struct v4l2_device *v4l2_dev = &vimc->v4l2_dev; - struct vimc_deb_device *vdeb; - int ret; - - /* Allocate the vdeb struct */ - vdeb = kzalloc(sizeof(*vdeb), GFP_KERNEL); - if (!vdeb) - return NULL; - - /* Create controls: */ - v4l2_ctrl_handler_init(&vdeb->hdl, 2); - v4l2_ctrl_new_custom(&vdeb->hdl, &vimc_deb_ctrl_class, NULL); - v4l2_ctrl_new_custom(&vdeb->hdl, &vimc_deb_ctrl_mean_win_size, NULL); - vdeb->sd.ctrl_handler = &vdeb->hdl; - if (vdeb->hdl.error) { - ret = vdeb->hdl.error; - goto err_free_vdeb; - } - - /* Initialize ved and sd */ - vdeb->pads[0].flags = MEDIA_PAD_FL_SINK; - vdeb->pads[1].flags = MEDIA_PAD_FL_SOURCE; - - ret = vimc_ent_sd_register(&vdeb->ved, &vdeb->sd, v4l2_dev, - vcfg_name, - MEDIA_ENT_F_PROC_VIDEO_PIXEL_ENC_CONV, 2, - vdeb->pads, &vimc_deb_ops); - if (ret) - goto err_free_hdl; - - vdeb->ved.process_frame = vimc_deb_process_frame; - vdeb->ved.dev = vimc->mdev.dev; - vdeb->mean_win_size = vimc_deb_ctrl_mean_win_size.def; - - /* 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 &vdeb->ved; - -err_free_hdl: - v4l2_ctrl_handler_free(&vdeb->hdl); -err_free_vdeb: - kfree(vdeb); - - return NULL; -} diff --git a/drivers/media/platform/vimc/vimc-scaler.c b/drivers/media/platform/vimc/vimc-scaler.c deleted file mode 100644 index 7521439747c5..000000000000 --- a/drivers/media/platform/vimc/vimc-scaler.c +++ /dev/null @@ -1,511 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * vimc-scaler.c Virtual Media Controller Driver - * - * Copyright (C) 2015-2017 Helen Koike - */ - -#include -#include -#include -#include -#include - -#include "vimc-common.h" - -static unsigned int sca_mult = 3; -module_param(sca_mult, uint, 0000); -MODULE_PARM_DESC(sca_mult, " the image size multiplier"); - -#define MAX_ZOOM 8 - -#define VIMC_SCA_FMT_WIDTH_DEFAULT 640 -#define VIMC_SCA_FMT_HEIGHT_DEFAULT 480 - -struct vimc_sca_device { - struct vimc_ent_device ved; - struct v4l2_subdev sd; - /* NOTE: the source fmt is the same as the sink - * with the width and hight multiplied by mult - */ - struct v4l2_mbus_framefmt sink_fmt; - struct v4l2_rect crop_rect; - /* Values calculated when the stream starts */ - u8 *src_frame; - unsigned int src_line_size; - unsigned int bpp; - struct media_pad pads[2]; -}; - -static const struct v4l2_mbus_framefmt sink_fmt_default = { - .width = VIMC_SCA_FMT_WIDTH_DEFAULT, - .height = VIMC_SCA_FMT_HEIGHT_DEFAULT, - .code = MEDIA_BUS_FMT_RGB888_1X24, - .field = V4L2_FIELD_NONE, - .colorspace = V4L2_COLORSPACE_DEFAULT, -}; - -static const struct v4l2_rect crop_rect_default = { - .width = VIMC_SCA_FMT_WIDTH_DEFAULT, - .height = VIMC_SCA_FMT_HEIGHT_DEFAULT, - .top = 0, - .left = 0, -}; - -static const struct v4l2_rect crop_rect_min = { - .width = VIMC_FRAME_MIN_WIDTH, - .height = VIMC_FRAME_MIN_HEIGHT, - .top = 0, - .left = 0, -}; - -static struct v4l2_rect -vimc_sca_get_crop_bound_sink(const struct v4l2_mbus_framefmt *sink_fmt) -{ - /* Get the crop bounds to clamp the crop rectangle correctly */ - struct v4l2_rect r = { - .left = 0, - .top = 0, - .width = sink_fmt->width, - .height = sink_fmt->height, - }; - return r; -} - -static void vimc_sca_adjust_sink_crop(struct v4l2_rect *r, - const struct v4l2_mbus_framefmt *sink_fmt) -{ - const struct v4l2_rect sink_rect = - vimc_sca_get_crop_bound_sink(sink_fmt); - - /* Disallow rectangles smaller than the minimal one. */ - v4l2_rect_set_min_size(r, &crop_rect_min); - v4l2_rect_map_inside(r, &sink_rect); -} - -static int vimc_sca_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg) -{ - struct v4l2_mbus_framefmt *mf; - struct v4l2_rect *r; - unsigned int i; - - mf = v4l2_subdev_get_try_format(sd, cfg, 0); - *mf = sink_fmt_default; - - r = v4l2_subdev_get_try_crop(sd, cfg, 0); - *r = crop_rect_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 (VIMC_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); - struct v4l2_rect *crop_rect; - - /* Get the current sink format */ - if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - format->format = *v4l2_subdev_get_try_format(sd, cfg, 0); - crop_rect = v4l2_subdev_get_try_crop(sd, cfg, 0); - } else { - format->format = vsca->sink_fmt; - crop_rect = &vsca->crop_rect; - } - - /* Scale the frame size for the source pad */ - if (VIMC_IS_SRC(format->pad)) { - format->format.width = crop_rect->width * sca_mult; - format->format.height = crop_rect->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; - struct v4l2_rect *crop_rect; - - 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; - crop_rect = &vsca->crop_rect; - } else { - sink_fmt = v4l2_subdev_get_try_format(sd, cfg, 0); - crop_rect = v4l2_subdev_get_try_crop(sd, cfg, 0); - } - - /* - * Do not change the format of the source pad, - * it is propagated from the sink - */ - if (VIMC_IS_SRC(fmt->pad)) { - fmt->format = *sink_fmt; - fmt->format.width = crop_rect->width * sca_mult; - fmt->format.height = crop_rect->height * sca_mult; - } else { - /* Set the new format in the sink pad */ - vimc_sca_adjust_sink_fmt(&fmt->format); - - dev_dbg(vsca->ved.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; - - /* Do the crop, but respect the current bounds */ - vimc_sca_adjust_sink_crop(crop_rect, sink_fmt); - } - - return 0; -} - -static int vimc_sca_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_selection *sel) -{ - struct vimc_sca_device *vsca = v4l2_get_subdevdata(sd); - struct v4l2_mbus_framefmt *sink_fmt; - struct v4l2_rect *crop_rect; - - if (VIMC_IS_SRC(sel->pad)) - return -EINVAL; - - if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) { - sink_fmt = &vsca->sink_fmt; - crop_rect = &vsca->crop_rect; - } else { - sink_fmt = v4l2_subdev_get_try_format(sd, cfg, 0); - crop_rect = v4l2_subdev_get_try_crop(sd, cfg, 0); - } - - switch (sel->target) { - case V4L2_SEL_TGT_CROP: - sel->r = *crop_rect; - break; - case V4L2_SEL_TGT_CROP_BOUNDS: - sel->r = vimc_sca_get_crop_bound_sink(sink_fmt); - break; - default: - return -EINVAL; - } - - return 0; -} - -static int vimc_sca_set_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_selection *sel) -{ - struct vimc_sca_device *vsca = v4l2_get_subdevdata(sd); - struct v4l2_mbus_framefmt *sink_fmt; - struct v4l2_rect *crop_rect; - - if (VIMC_IS_SRC(sel->pad)) - return -EINVAL; - - if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) { - /* Do not change the format while stream is on */ - if (vsca->src_frame) - return -EBUSY; - - crop_rect = &vsca->crop_rect; - sink_fmt = &vsca->sink_fmt; - } else { - crop_rect = v4l2_subdev_get_try_crop(sd, cfg, 0); - sink_fmt = v4l2_subdev_get_try_format(sd, cfg, 0); - } - - switch (sel->target) { - case V4L2_SEL_TGT_CROP: - /* Do the crop, but respect the current bounds */ - vimc_sca_adjust_sink_crop(&sel->r, sink_fmt); - *crop_rect = sel->r; - break; - default: - return -EINVAL; - } - - 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, - .get_selection = vimc_sca_get_selection, - .set_selection = vimc_sca_set_selection, -}; - -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->crop_rect.width * - sca_mult * vsca->bpp; - - /* Calculate the frame size of the source pad */ - frame_size = vsca->src_line_size * vsca->crop_rect.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, - unsigned int lin, unsigned int col, - const u8 *const sink_frame) -{ - const struct v4l2_rect crop_rect = vsca->crop_rect; - 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->ved.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 - */ - lin -= crop_rect.top; - col -= crop_rect.left; - index = VIMC_FRAME_INDEX(lin * sca_mult, col * sca_mult, - crop_rect.width * sca_mult, vsca->bpp); - - dev_dbg(vsca->ved.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->ved.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) -{ - const struct v4l2_rect r = vsca->crop_rect; - 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 = r.top; i < r.top + r.height; i++) - for (j = r.left; j < r.left + r.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; -}; - -void vimc_sca_release(struct vimc_ent_device *ved) -{ - struct vimc_sca_device *vsca = - container_of(ved, struct vimc_sca_device, ved); - - media_entity_cleanup(vsca->ved.ent); - kfree(vsca); -} - -struct vimc_ent_device *vimc_sca_add(struct vimc_device *vimc, - const char *vcfg_name) -{ - struct v4l2_device *v4l2_dev = &vimc->v4l2_dev; - struct vimc_sca_device *vsca; - int ret; - - /* Allocate the vsca struct */ - vsca = kzalloc(sizeof(*vsca), GFP_KERNEL); - if (!vsca) - return NULL; - - /* Initialize ved and sd */ - vsca->pads[0].flags = MEDIA_PAD_FL_SINK; - vsca->pads[1].flags = MEDIA_PAD_FL_SOURCE; - - ret = vimc_ent_sd_register(&vsca->ved, &vsca->sd, v4l2_dev, - vcfg_name, - MEDIA_ENT_F_PROC_VIDEO_SCALER, 2, - vsca->pads, &vimc_sca_ops); - if (ret) { - kfree(vsca); - return NULL; - } - - vsca->ved.process_frame = vimc_sca_process_frame; - vsca->ved.dev = vimc->mdev.dev; - - /* Initialize the frame format */ - vsca->sink_fmt = sink_fmt_default; - - /* Initialize the crop selection */ - vsca->crop_rect = crop_rect_default; - - return &vsca->ved; -} diff --git a/drivers/media/platform/vimc/vimc-sensor.c b/drivers/media/platform/vimc/vimc-sensor.c deleted file mode 100644 index 92daee58209e..000000000000 --- a/drivers/media/platform/vimc/vimc-sensor.c +++ /dev/null @@ -1,376 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * vimc-sensor.c Virtual Media Controller Driver - * - * Copyright (C) 2015-2017 Helen Koike - */ - -#include -#include -#include -#include -#include -#include - -#include "vimc-common.h" - -struct vimc_sen_device { - struct vimc_ent_device ved; - struct v4l2_subdev sd; - struct tpg_data tpg; - u8 *frame; - /* The active format */ - struct v4l2_mbus_framefmt mbus_format; - struct v4l2_ctrl_handler hdl; - struct media_pad pad; -}; - -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->ved.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, -}; - -void vimc_sen_release(struct vimc_ent_device *ved) -{ - struct vimc_sen_device *vsen = - container_of(ved, struct vimc_sen_device, ved); - - v4l2_ctrl_handler_free(&vsen->hdl); - tpg_free(&vsen->tpg); - media_entity_cleanup(vsen->ved.ent); - kfree(vsen); -} - -/* 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, -}; - -struct vimc_ent_device *vimc_sen_add(struct vimc_device *vimc, - const char *vcfg_name) -{ - struct v4l2_device *v4l2_dev = &vimc->v4l2_dev; - struct vimc_sen_device *vsen; - int ret; - - /* Allocate the vsen struct */ - vsen = kzalloc(sizeof(*vsen), GFP_KERNEL); - if (!vsen) - return NULL; - - 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 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_free_hdl; - - /* Initialize ved and sd */ - vsen->pad.flags = MEDIA_PAD_FL_SOURCE; - ret = vimc_ent_sd_register(&vsen->ved, &vsen->sd, v4l2_dev, - vcfg_name, - MEDIA_ENT_F_CAM_SENSOR, 1, &vsen->pad, - &vimc_sen_ops); - if (ret) - goto err_free_tpg; - - vsen->ved.process_frame = vimc_sen_process_frame; - vsen->ved.dev = vimc->mdev.dev; - - /* Initialize the frame format */ - vsen->mbus_format = fmt_default; - - return &vsen->ved; - -err_free_tpg: - tpg_free(&vsen->tpg); -err_free_hdl: - v4l2_ctrl_handler_free(&vsen->hdl); -err_free_vsen: - kfree(vsen); - - return NULL; -} diff --git a/drivers/media/platform/vimc/vimc-streamer.c b/drivers/media/platform/vimc/vimc-streamer.c deleted file mode 100644 index 65feb3c596db..000000000000 --- a/drivers/media/platform/vimc/vimc-streamer.c +++ /dev/null @@ -1,238 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * vimc-streamer.c Virtual Media Controller Driver - * - * Copyright (C) 2018 Lucas A. M. Magalhães - * - */ - -#include -#include -#include - -#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) { - dev_err(ved->dev, "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) { - /* the first entity of the pipe should be source only */ - if (!vimc_is_source(ved->ent)) { - dev_err(ved->dev, - "first entity in the pipe '%s' is not a source\n", - ved->ent->name); - vimc_streamer_pipeline_terminate(stream); - return -EPIPE; - } - 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)) { - ret = PTR_ERR(stream->kthread); - dev_err(ved->dev, "kthread_run failed with %d\n", ret); - vimc_streamer_pipeline_terminate(stream); - stream->kthread = NULL; - return ret; - } - - } else { - if (!stream->kthread) - return 0; - - ret = kthread_stop(stream->kthread); - /* - * kthread_stop returns -EINTR in cases when streamon was - * immediately followed by streamoff, and the thread didn't had - * a chance to run. Ignore errors to stop the stream in the - * pipeline. - */ - if (ret) - dev_dbg(ved->dev, "kthread_stop returned '%d'\n", ret); - - stream->kthread = NULL; - - vimc_streamer_pipeline_terminate(stream); - } - - return 0; -} diff --git a/drivers/media/platform/vimc/vimc-streamer.h b/drivers/media/platform/vimc/vimc-streamer.h deleted file mode 100644 index fe3c51f15fad..000000000000 --- 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 - * - */ - -#ifndef _VIMC_STREAMER_H_ -#define _VIMC_STREAMER_H_ - -#include - -#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 e2ff06edfa93..000000000000 --- 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 b12ad0152a3e..000000000000 --- a/drivers/media/platform/vivid/Makefile +++ /dev/null @@ -1,12 +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 vivid-meta-cap.o vivid-meta-out.o \ - vivid-kthread-touch.o vivid-touch-cap.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 4d2413e87730..000000000000 --- a/drivers/media/platform/vivid/vivid-cec.c +++ /dev/null @@ -1,286 +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 - -#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) -{ - u32 caps = CEC_CAP_DEFAULTS | CEC_CAP_MONITOR_ALL | CEC_CAP_MONITOR_PIN; - char name[32]; - - snprintf(name, sizeof(name), "vivid-%03d-vid-%s%d", - dev->inst, is_source ? "out" : "cap", 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 7524ed48a914..000000000000 --- 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 6c740e3e6999..000000000000 --- a/drivers/media/platform/vivid/vivid-core.c +++ /dev/null @@ -1,2006 +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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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" -#include "vivid-meta-cap.h" -#include "vivid-meta-out.h" -#include "vivid-touch-cap.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 meta_cap_nr[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = -1 }; -module_param_array(meta_cap_nr, int, NULL, 0444); -MODULE_PARM_DESC(meta_cap_nr, " videoX start number, -1 is autodetect"); - -static int meta_out_nr[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = -1 }; -module_param_array(meta_out_nr, int, NULL, 0444); -MODULE_PARM_DESC(meta_out_nr, " videoX start number, -1 is autodetect"); - -static int touch_cap_nr[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = -1 }; -module_param_array(touch_cap_nr, int, NULL, 0444); -MODULE_PARM_DESC(touch_cap_nr, " v4l-touchX 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 + meta-cap - */ -static unsigned int node_types[VIVID_MAX_DEVS] = { - [0 ... (VIVID_MAX_DEVS - 1)] = 0xe1d3d -}; -module_param_array(node_types, uint, NULL, 0444); -MODULE_PARM_DESC(node_types, " node types, default is 0xe1d3d. 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\n" - "\t\t bit 17: Metadata Capture node\n" - "\t\t bit 18: Metadata Output node\n" - "\t\t bit 19: Touch Capture node\n"); - -/* 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, 0xf0, 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, 0xea, 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, 0x63, -}; - -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 | dev->meta_cap_caps | - dev->meta_out_caps | dev->touch_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_type == VFL_TYPE_TOUCH) - return vivid_g_parm_tch(file, fh, parm); - 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_VIDEO) - 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 int vivid_enum_input(struct file *file, void *priv, - struct v4l2_input *inp) -{ - struct video_device *vdev = video_devdata(file); - - if (vdev->vfl_type == VFL_TYPE_TOUCH) - return vivid_enum_input_tch(file, priv, inp); - return vidioc_enum_input(file, priv, inp); -} - -static int vivid_g_input(struct file *file, void *priv, unsigned int *i) -{ - struct video_device *vdev = video_devdata(file); - - if (vdev->vfl_type == VFL_TYPE_TOUCH) - return vivid_g_input_tch(file, priv, i); - return vidioc_g_input(file, priv, i); -} - -static int vivid_s_input(struct file *file, void *priv, unsigned int i) -{ - struct video_device *vdev = video_devdata(file); - - if (vdev->vfl_type == VFL_TYPE_TOUCH) - return vivid_s_input_tch(file, priv, i); - return vidioc_s_input(file, priv, i); -} - -static int vivid_enum_fmt_cap(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - struct video_device *vdev = video_devdata(file); - - if (vdev->vfl_type == VFL_TYPE_TOUCH) - return vivid_enum_fmt_tch(file, priv, f); - return vivid_enum_fmt_vid(file, priv, f); -} - -static int vivid_g_fmt_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct video_device *vdev = video_devdata(file); - - if (vdev->vfl_type == VFL_TYPE_TOUCH) - return vivid_g_fmt_tch(file, priv, f); - return vidioc_g_fmt_vid_cap(file, priv, f); -} - -static int vivid_try_fmt_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct video_device *vdev = video_devdata(file); - - if (vdev->vfl_type == VFL_TYPE_TOUCH) - return vivid_g_fmt_tch(file, priv, f); - return vidioc_try_fmt_vid_cap(file, priv, f); -} - -static int vivid_s_fmt_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct video_device *vdev = video_devdata(file); - - if (vdev->vfl_type == VFL_TYPE_TOUCH) - return vivid_g_fmt_tch(file, priv, f); - return vidioc_s_fmt_vid_cap(file, priv, f); -} - -static int vivid_g_fmt_cap_mplane(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct video_device *vdev = video_devdata(file); - - if (vdev->vfl_type == VFL_TYPE_TOUCH) - return vivid_g_fmt_tch_mplane(file, priv, f); - return vidioc_g_fmt_vid_cap_mplane(file, priv, f); -} - -static int vivid_try_fmt_cap_mplane(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct video_device *vdev = video_devdata(file); - - if (vdev->vfl_type == VFL_TYPE_TOUCH) - return vivid_g_fmt_tch_mplane(file, priv, f); - return vidioc_try_fmt_vid_cap_mplane(file, priv, f); -} - -static int vivid_s_fmt_cap_mplane(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct video_device *vdev = video_devdata(file); - - if (vdev->vfl_type == VFL_TYPE_TOUCH) - return vivid_g_fmt_tch_mplane(file, priv, f); - return vidioc_s_fmt_vid_cap_mplane(file, priv, f); -} - -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) + - vivid_is_in_use(&dev->meta_cap_dev) + - vivid_is_in_use(&dev->meta_out_dev) + - vivid_is_in_use(&dev->touch_cap_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); - set_bit(V4L2_FL_REGISTERED, &dev->meta_cap_dev.flags); - set_bit(V4L2_FL_REGISTERED, &dev->meta_out_dev.flags); - set_bit(V4L2_FL_REGISTERED, &dev->touch_cap_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_cap, - .vidioc_g_fmt_vid_cap = vivid_g_fmt_cap, - .vidioc_try_fmt_vid_cap = vivid_try_fmt_cap, - .vidioc_s_fmt_vid_cap = vivid_s_fmt_cap, - .vidioc_g_fmt_vid_cap_mplane = vivid_g_fmt_cap_mplane, - .vidioc_try_fmt_vid_cap_mplane = vivid_try_fmt_cap_mplane, - .vidioc_s_fmt_vid_cap_mplane = vivid_s_fmt_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 = vivid_enum_input, - .vidioc_g_input = vivid_g_input, - .vidioc_s_input = vivid_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, - - .vidioc_enum_fmt_meta_cap = vidioc_enum_fmt_meta_cap, - .vidioc_g_fmt_meta_cap = vidioc_g_fmt_meta_cap, - .vidioc_s_fmt_meta_cap = vidioc_g_fmt_meta_cap, - .vidioc_try_fmt_meta_cap = vidioc_g_fmt_meta_cap, - - .vidioc_enum_fmt_meta_out = vidioc_enum_fmt_meta_out, - .vidioc_g_fmt_meta_out = vidioc_g_fmt_meta_out, - .vidioc_s_fmt_meta_out = vidioc_g_fmt_meta_out, - .vidioc_try_fmt_meta_out = vidioc_g_fmt_meta_out, -}; - -/* ----------------------------------------------------------------- - 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_queue(struct vivid_dev *dev, - struct vb2_queue *q, - u32 buf_type, - unsigned int min_buffers_needed, - const struct vb2_ops *ops) -{ - if (buf_type == V4L2_BUF_TYPE_VIDEO_CAPTURE && dev->multiplanar) - buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; - else if (buf_type == V4L2_BUF_TYPE_VIDEO_OUTPUT && dev->multiplanar) - buf_type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; - else if (buf_type == V4L2_BUF_TYPE_VBI_CAPTURE && !dev->has_raw_vbi_cap) - buf_type = V4L2_BUF_TYPE_SLICED_VBI_CAPTURE; - else if (buf_type == V4L2_BUF_TYPE_VBI_OUTPUT && !dev->has_raw_vbi_out) - buf_type = V4L2_BUF_TYPE_SLICED_VBI_OUTPUT; - - q->type = buf_type; - q->io_modes = VB2_MMAP | VB2_DMABUF; - q->io_modes |= V4L2_TYPE_IS_OUTPUT(buf_type) ? VB2_WRITE : VB2_READ; - if (allocators[dev->inst] != 1) - q->io_modes |= VB2_USERPTR; - q->drv_priv = dev; - q->buf_struct_size = sizeof(struct vivid_buffer); - q->ops = ops; - q->mem_ops = allocators[dev->inst] == 1 ? &vb2_dma_contig_memops : - &vb2_vmalloc_memops; - q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - q->min_buffers_needed = min_buffers_needed; - q->lock = &dev->mutex; - q->dev = dev->v4l2_dev.dev; - q->supports_requests = true; - - return vb2_queue_init(q); -} - -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; - 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; - unsigned node_type = node_types[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 meta capture device */ - dev->has_meta_cap = node_type & 0x20000; - - /* sanity checks */ - if ((in_type_counter[WEBCAM] || in_type_counter[HDMI]) && - !dev->has_vid_cap && !dev->has_meta_cap) { - v4l2_warn(&dev->v4l2_dev, - "Webcam or HDMI input without video or metadata nodes\n"); - kfree(dev); - return -EINVAL; - } - if ((in_type_counter[TV] || in_type_counter[SVID]) && - !dev->has_vid_cap && !dev->has_vbi_cap && !dev->has_meta_cap) { - v4l2_warn(&dev->v4l2_dev, - "TV or S-Video input without video, VBI or metadata nodes\n"); - kfree(dev); - return -EINVAL; - } - - /* 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 metadata output device */ - dev->has_meta_out = node_type & 0x40000; - - /* sanity checks */ - if (out_type_counter[SVID] && - !dev->has_vid_out && !dev->has_vbi_out && !dev->has_meta_out) { - v4l2_warn(&dev->v4l2_dev, - "S-Video output without video, VBI or metadata nodes\n"); - kfree(dev); - return -EINVAL; - } - if (out_type_counter[HDMI] && !dev->has_vid_out && !dev->has_meta_out) { - v4l2_warn(&dev->v4l2_dev, - "HDMI output without video or metadata nodes\n"); - kfree(dev); - return -EINVAL; - } - - /* 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 TV tuner? */ - dev->has_tv_tuner = in_type_counter[TV]; - - /* 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'); - } - - /* do we create a touch capture device */ - dev->has_touch_cap = node_type & 0x80000; - - /* 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 (dev->has_tv_tuner) - 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 (dev->has_tv_tuner) - 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; - - /* set up the capabilities of meta capture device */ - if (dev->has_meta_cap) { - dev->meta_cap_caps = V4L2_CAP_META_CAPTURE | - V4L2_CAP_STREAMING | V4L2_CAP_READWRITE; - if (dev->has_audio_inputs) - dev->meta_cap_caps |= V4L2_CAP_AUDIO; - if (dev->has_tv_tuner) - dev->meta_cap_caps |= V4L2_CAP_TUNER; - } - /* set up the capabilities of meta output device */ - if (dev->has_meta_out) { - dev->meta_out_caps = V4L2_CAP_META_OUTPUT | - V4L2_CAP_STREAMING | V4L2_CAP_READWRITE; - if (dev->has_audio_outputs) - dev->meta_out_caps |= V4L2_CAP_AUDIO; - } - /* set up the capabilities of the touch capture device */ - if (dev->has_touch_cap) { - dev->touch_cap_caps = V4L2_CAP_TOUCH | V4L2_CAP_STREAMING | - V4L2_CAP_READWRITE; - dev->touch_cap_caps |= dev->multiplanar ? - V4L2_CAP_VIDEO_CAPTURE_MPLANE : V4L2_CAP_VIDEO_CAPTURE; - } - - 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); - v4l2_disable_ioctl(&dev->meta_cap_dev, VIDIOC_S_AUDIO); - v4l2_disable_ioctl(&dev->meta_cap_dev, VIDIOC_G_AUDIO); - v4l2_disable_ioctl(&dev->meta_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); - v4l2_disable_ioctl(&dev->meta_out_dev, VIDIOC_S_AUDOUT); - v4l2_disable_ioctl(&dev->meta_out_dev, VIDIOC_G_AUDOUT); - v4l2_disable_ioctl(&dev->meta_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); - v4l2_disable_ioctl(&dev->meta_cap_dev, VIDIOC_S_FREQUENCY); - v4l2_disable_ioctl(&dev->meta_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); - v4l2_disable_ioctl(&dev->meta_cap_dev, VIDIOC_S_TUNER); - v4l2_disable_ioctl(&dev->meta_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->meta_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); - v4l2_disable_ioctl(&dev->meta_out_dev, VIDIOC_S_FREQUENCY); - v4l2_disable_ioctl(&dev->meta_out_dev, VIDIOC_G_FREQUENCY); - v4l2_disable_ioctl(&dev->touch_cap_dev, VIDIOC_S_PARM); - v4l2_disable_ioctl(&dev->touch_cap_dev, VIDIOC_ENUM_FRAMESIZES); - v4l2_disable_ioctl(&dev->touch_cap_dev, VIDIOC_ENUM_FRAMEINTERVALS); - - /* 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; - - /* update touch configuration */ - dev->timeperframe_tch_cap.numerator = 1; - dev->timeperframe_tch_cap.denominator = 10; - vivid_set_touch(dev, 0); - - /* 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->meta_cap_active); - INIT_LIST_HEAD(&dev->meta_out_active); - INIT_LIST_HEAD(&dev->touch_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 (allocators[inst] == 1) - dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); - - /* start creating the vb2 queues */ - if (dev->has_vid_cap) { - /* initialize vid_cap queue */ - ret = vivid_create_queue(dev, &dev->vb_vid_cap_q, - V4L2_BUF_TYPE_VIDEO_CAPTURE, 2, - &vivid_vid_cap_qops); - if (ret) - goto unreg_dev; - } - - if (dev->has_vid_out) { - /* initialize vid_out queue */ - ret = vivid_create_queue(dev, &dev->vb_vid_out_q, - V4L2_BUF_TYPE_VIDEO_OUTPUT, 2, - &vivid_vid_out_qops); - if (ret) - goto unreg_dev; - } - - if (dev->has_vbi_cap) { - /* initialize vbi_cap queue */ - ret = vivid_create_queue(dev, &dev->vb_vbi_cap_q, - V4L2_BUF_TYPE_VBI_CAPTURE, 2, - &vivid_vbi_cap_qops); - if (ret) - goto unreg_dev; - } - - if (dev->has_vbi_out) { - /* initialize vbi_out queue */ - ret = vivid_create_queue(dev, &dev->vb_vbi_out_q, - V4L2_BUF_TYPE_VBI_OUTPUT, 2, - &vivid_vbi_out_qops); - if (ret) - goto unreg_dev; - } - - if (dev->has_sdr_cap) { - /* initialize sdr_cap queue */ - ret = vivid_create_queue(dev, &dev->vb_sdr_cap_q, - V4L2_BUF_TYPE_SDR_CAPTURE, 8, - &vivid_sdr_cap_qops); - if (ret) - goto unreg_dev; - } - - if (dev->has_meta_cap) { - /* initialize meta_cap queue */ - ret = vivid_create_queue(dev, &dev->vb_meta_cap_q, - V4L2_BUF_TYPE_META_CAPTURE, 2, - &vivid_meta_cap_qops); - if (ret) - goto unreg_dev; - } - - if (dev->has_meta_out) { - /* initialize meta_out queue */ - ret = vivid_create_queue(dev, &dev->vb_meta_out_q, - V4L2_BUF_TYPE_META_OUTPUT, 1, - &vivid_meta_out_qops); - if (ret) - goto unreg_dev; - } - - if (dev->has_touch_cap) { - /* initialize touch_cap queue */ - ret = vivid_create_queue(dev, &dev->vb_touch_cap_q, - V4L2_BUF_TYPE_VIDEO_CAPTURE, 1, - &vivid_touch_cap_qops); - 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); - v4l2_ctrl_handler_setup(&dev->ctrl_hdl_meta_cap); - v4l2_ctrl_handler_setup(&dev->ctrl_hdl_meta_out); - v4l2_ctrl_handler_setup(&dev->ctrl_hdl_touch_cap); - - /* finally start creating the device nodes */ - if (dev->has_vid_cap) { - vfd = &dev->vid_cap_dev; - snprintf(vfd->name, sizeof(vfd->name), - "vivid-%03d-vid-cap", inst); - 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_VIDEO, 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; - snprintf(vfd->name, sizeof(vfd->name), - "vivid-%03d-vid-out", inst); - vfd->vfl_dir = VFL_DIR_TX; - vfd->fops = &vivid_fops; - vfd->ioctl_ops = &vivid_ioctl_ops; - vfd->device_caps = dev->vid_out_caps; - vfd->release = video_device_release_empty; - vfd->v4l2_dev = &dev->v4l2_dev; - vfd->queue = &dev->vb_vid_out_q; - 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 + 1) << 12, false); - else - cec_s_phys_addr(dev->cec_tx_adap[i], 0x1000, false); - } -#endif - - ret = video_register_device(vfd, VFL_TYPE_VIDEO, 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)); - } - - if (dev->has_meta_cap) { - vfd = &dev->meta_cap_dev; - snprintf(vfd->name, sizeof(vfd->name), - "vivid-%03d-meta-cap", inst); - vfd->fops = &vivid_fops; - vfd->ioctl_ops = &vivid_ioctl_ops; - vfd->device_caps = dev->meta_cap_caps; - vfd->release = video_device_release_empty; - vfd->v4l2_dev = &dev->v4l2_dev; - vfd->queue = &dev->vb_meta_cap_q; - vfd->lock = &dev->mutex; - vfd->tvnorms = tvnorms_cap; - video_set_drvdata(vfd, dev); -#ifdef CONFIG_MEDIA_CONTROLLER - dev->meta_cap_pad.flags = MEDIA_PAD_FL_SINK; - ret = media_entity_pads_init(&vfd->entity, 1, - &dev->meta_cap_pad); - if (ret) - goto unreg_dev; -#endif - ret = video_register_device(vfd, VFL_TYPE_VIDEO, - meta_cap_nr[inst]); - if (ret < 0) - goto unreg_dev; - v4l2_info(&dev->v4l2_dev, - "V4L2 metadata capture device registered as %s\n", - video_device_node_name(vfd)); - } - - if (dev->has_meta_out) { - vfd = &dev->meta_out_dev; - snprintf(vfd->name, sizeof(vfd->name), - "vivid-%03d-meta-out", inst); - vfd->vfl_dir = VFL_DIR_TX; - vfd->fops = &vivid_fops; - vfd->ioctl_ops = &vivid_ioctl_ops; - vfd->device_caps = dev->meta_out_caps; - vfd->release = video_device_release_empty; - vfd->v4l2_dev = &dev->v4l2_dev; - vfd->queue = &dev->vb_meta_out_q; - vfd->lock = &dev->mutex; - vfd->tvnorms = tvnorms_out; - video_set_drvdata(vfd, dev); -#ifdef CONFIG_MEDIA_CONTROLLER - dev->meta_out_pad.flags = MEDIA_PAD_FL_SOURCE; - ret = media_entity_pads_init(&vfd->entity, 1, - &dev->meta_out_pad); - if (ret) - goto unreg_dev; -#endif - ret = video_register_device(vfd, VFL_TYPE_VIDEO, - meta_out_nr[inst]); - if (ret < 0) - goto unreg_dev; - v4l2_info(&dev->v4l2_dev, - "V4L2 metadata output device registered as %s\n", - video_device_node_name(vfd)); - } - - if (dev->has_touch_cap) { - vfd = &dev->touch_cap_dev; - snprintf(vfd->name, sizeof(vfd->name), - "vivid-%03d-touch-cap", inst); - vfd->fops = &vivid_fops; - vfd->ioctl_ops = &vivid_ioctl_ops; - vfd->device_caps = dev->touch_cap_caps; - vfd->release = video_device_release_empty; - vfd->v4l2_dev = &dev->v4l2_dev; - vfd->queue = &dev->vb_touch_cap_q; - vfd->tvnorms = tvnorms_cap; - vfd->lock = &dev->mutex; - video_set_drvdata(vfd, dev); -#ifdef CONFIG_MEDIA_CONTROLLER - dev->touch_cap_pad.flags = MEDIA_PAD_FL_SINK; - ret = media_entity_pads_init(&vfd->entity, 1, - &dev->touch_cap_pad); - if (ret) - goto unreg_dev; -#endif - ret = video_register_device(vfd, VFL_TYPE_TOUCH, - touch_cap_nr[inst]); - if (ret < 0) - goto unreg_dev; - v4l2_info(&dev->v4l2_dev, - "V4L2 touch capture 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->touch_cap_dev); - video_unregister_device(&dev->meta_out_dev); - video_unregister_device(&dev->meta_cap_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); - } - if (dev->has_meta_cap) { - v4l2_info(&dev->v4l2_dev, "unregistering %s\n", - video_device_node_name(&dev->meta_cap_dev)); - video_unregister_device(&dev->meta_cap_dev); - } - if (dev->has_meta_out) { - v4l2_info(&dev->v4l2_dev, "unregistering %s\n", - video_device_node_name(&dev->meta_out_dev)); - video_unregister_device(&dev->meta_out_dev); - } - if (dev->has_touch_cap) { - v4l2_info(&dev->v4l2_dev, "unregistering %s\n", - video_device_node_name(&dev->touch_cap_dev)); - video_unregister_device(&dev->touch_cap_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 99e69b8f770f..000000000000 --- a/drivers/media/platform/vivid/vivid-core.h +++ /dev/null @@ -1,612 +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 -#include -#include -#include -#include -#include -#include -#include -#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; - struct media_pad meta_cap_pad; - struct media_pad meta_out_pad; - struct media_pad touch_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; - struct video_device meta_cap_dev; - struct v4l2_ctrl_handler ctrl_hdl_meta_cap; - struct video_device meta_out_dev; - struct v4l2_ctrl_handler ctrl_hdl_meta_out; - struct video_device touch_cap_dev; - struct v4l2_ctrl_handler ctrl_hdl_touch_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; - u32 meta_cap_caps; - u32 meta_out_caps; - u32 touch_cap_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 has_meta_cap; - bool has_meta_out; - bool has_tv_tuner; - bool has_touch_cap; - - 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; - struct vb2_queue vb_meta_cap_q; - struct list_head meta_cap_active; - struct vb2_queue vb_touch_cap_q; - struct list_head touch_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; - u32 meta_cap_seq_start; - u32 meta_cap_seq_count; - bool meta_cap_streaming; - - /* Touch capture */ - struct task_struct *kthread_touch_cap; - unsigned long jiffies_touch_cap; - u64 touch_cap_stream_start; - u32 touch_cap_seq_offset; - bool touch_cap_seq_resync; - u32 touch_cap_seq_start; - u32 touch_cap_seq_count; - bool touch_cap_streaming; - struct v4l2_fract timeperframe_tch_cap; - struct v4l2_pix_format tch_format; - int tch_pat_random; - - /* 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; - struct vb2_queue vb_meta_out_q; - struct list_head meta_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; - u32 meta_out_seq_start; - u32 meta_out_seq_count; - bool meta_out_streaming; - - /* 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; - - bool meta_pts; - bool meta_scr; -}; - -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 334130568dcb..000000000000 --- a/drivers/media/platform/vivid/vivid-ctrls.c +++ /dev/null @@ -1,1939 +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 -#include -#include -#include -#include - -#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_AREA (VIVID_CID_CUSTOM_BASE + 11) - -#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) - -#define VIVID_CID_META_CAP_GENERATE_PTS (VIVID_CID_VIVID_BASE + 111) -#define VIVID_CID_META_CAP_GENERATE_SCR (VIVID_CID_VIVID_BASE + 112) - -/* 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); - clear_bit(V4L2_FL_REGISTERED, &dev->meta_cap_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, -}; - -static const struct v4l2_area area = { - .width = 1000, - .height = 2000, -}; - -static const struct v4l2_ctrl_config vivid_ctrl_area = { - .ops = &vivid_user_gen_ctrl_ops, - .id = VIVID_CID_AREA, - .name = "Area", - .type = V4L2_CTRL_TYPE_AREA, - .p_def.p_const = &area, -}; - -/* 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, -}; - -/* Metadata Capture Control */ - -static int vivid_meta_cap_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct vivid_dev *dev = container_of(ctrl->handler, struct vivid_dev, - ctrl_hdl_meta_cap); - - switch (ctrl->id) { - case VIVID_CID_META_CAP_GENERATE_PTS: - dev->meta_pts = ctrl->val; - break; - case VIVID_CID_META_CAP_GENERATE_SCR: - dev->meta_scr = ctrl->val; - break; - } - return 0; -} - -static const struct v4l2_ctrl_ops vivid_meta_cap_ctrl_ops = { - .s_ctrl = vivid_meta_cap_s_ctrl, -}; - -static const struct v4l2_ctrl_config vivid_ctrl_meta_has_pts = { - .ops = &vivid_meta_cap_ctrl_ops, - .id = VIVID_CID_META_CAP_GENERATE_PTS, - .name = "Generate PTS", - .type = V4L2_CTRL_TYPE_BOOLEAN, - .max = 1, - .def = 1, - .step = 1, -}; - -static const struct v4l2_ctrl_config vivid_ctrl_meta_has_src_clk = { - .ops = &vivid_meta_cap_ctrl_ops, - .id = VIVID_CID_META_CAP_GENERATE_SCR, - .name = "Generate SCR", - .type = V4L2_CTRL_TYPE_BOOLEAN, - .max = 1, - .def = 1, - .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_handler *hdl_meta_cap = &dev->ctrl_hdl_meta_cap; - struct v4l2_ctrl_handler *hdl_meta_out = &dev->ctrl_hdl_meta_out; - struct v4l2_ctrl_handler *hdl_tch_cap = &dev->ctrl_hdl_touch_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); - v4l2_ctrl_handler_init(hdl_meta_cap, 2); - v4l2_ctrl_new_custom(hdl_meta_cap, &vivid_ctrl_class, NULL); - v4l2_ctrl_handler_init(hdl_meta_out, 2); - v4l2_ctrl_new_custom(hdl_meta_out, &vivid_ctrl_class, NULL); - v4l2_ctrl_handler_init(hdl_tch_cap, 2); - v4l2_ctrl_new_custom(hdl_tch_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_area, 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 (dev->has_meta_cap) { - v4l2_ctrl_new_custom(hdl_meta_cap, - &vivid_ctrl_meta_has_pts, NULL); - v4l2_ctrl_new_custom(hdl_meta_cap, - &vivid_ctrl_meta_has_src_clk, 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; - } - if (dev->has_meta_cap) { - v4l2_ctrl_add_handler(hdl_meta_cap, hdl_user_gen, NULL, false); - v4l2_ctrl_add_handler(hdl_meta_cap, hdl_streaming, NULL, false); - if (hdl_meta_cap->error) - return hdl_meta_cap->error; - dev->meta_cap_dev.ctrl_handler = hdl_meta_cap; - } - if (dev->has_meta_out) { - v4l2_ctrl_add_handler(hdl_meta_out, hdl_user_gen, NULL, false); - v4l2_ctrl_add_handler(hdl_meta_out, hdl_streaming, NULL, false); - if (hdl_meta_out->error) - return hdl_meta_out->error; - dev->meta_out_dev.ctrl_handler = hdl_meta_out; - } - if (dev->has_touch_cap) { - v4l2_ctrl_add_handler(hdl_tch_cap, hdl_user_gen, NULL, false); - v4l2_ctrl_add_handler(hdl_tch_cap, hdl_streaming, NULL, false); - if (hdl_tch_cap->error) - return hdl_tch_cap->error; - dev->touch_cap_dev.ctrl_handler = hdl_tch_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); - v4l2_ctrl_handler_free(&dev->ctrl_hdl_meta_cap); - v4l2_ctrl_handler_free(&dev->ctrl_hdl_meta_out); - v4l2_ctrl_handler_free(&dev->ctrl_hdl_touch_cap); -} diff --git a/drivers/media/platform/vivid/vivid-ctrls.h b/drivers/media/platform/vivid/vivid-ctrls.h deleted file mode 100644 index 6fad5f5d0054..000000000000 --- 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 01a9d671b947..000000000000 --- a/drivers/media/platform/vivid/vivid-kthread-cap.c +++ /dev/null @@ -1,1007 +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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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" -#include "vivid-meta-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; - struct vivid_buffer *meta_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); - } - } - if (!list_empty(&dev->meta_cap_active)) { - meta_cap_buf = list_entry(dev->meta_cap_active.next, - struct vivid_buffer, list); - list_del(&meta_cap_buf->list); - } - - spin_unlock(&dev->slock); - - if (!vid_cap_buf && !vbi_cap_buf && !meta_cap_buf) - goto update_mv; - - f_time = dev->cap_frame_period * dev->vid_cap_seq_count + - dev->cap_stream_start + dev->time_wrap_offset; - - 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 (!dev->tstamp_src_is_soe) - vid_cap_buf->vb.vb2_buf.timestamp += dev->cap_frame_eof_offset; - } - - 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 + dev->cap_frame_eof_offset + vbi_period; - } - - if (meta_cap_buf) { - v4l2_ctrl_request_setup(meta_cap_buf->vb.vb2_buf.req_obj.req, - &dev->ctrl_hdl_meta_cap); - vivid_meta_cap_fillbuff(dev, meta_cap_buf, f_time); - v4l2_ctrl_request_complete(meta_cap_buf->vb.vb2_buf.req_obj.req, - &dev->ctrl_hdl_meta_cap); - vb2_buffer_done(&meta_cap_buf->vb.vb2_buf, dev->dqbuf_error ? - VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE); - dprintk(dev, 2, "meta_cap %d done\n", - meta_cap_buf->vb.vb2_buf.index); - meta_cap_buf->vb.vb2_buf.timestamp = f_time + dev->cap_frame_eof_offset; - } - - 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; - dev->meta_cap_seq_count = dev->cap_seq_count - dev->meta_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 if (pstreaming == &dev->vbi_cap_streaming) - dev->vbi_cap_seq_start = seq_count; - else - dev->meta_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->meta_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 (pstreaming == &dev->meta_cap_streaming) { - while (!list_empty(&dev->meta_cap_active)) { - struct vivid_buffer *buf; - - buf = list_entry(dev->meta_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_meta_cap); - vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); - dprintk(dev, 2, "meta_cap buffer %d done\n", - buf->vb.vb2_buf.index); - } - } - - if (dev->vid_cap_streaming || dev->vbi_cap_streaming || - dev->meta_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 0f43015306d6..000000000000 --- 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 6780687978f9..000000000000 --- a/drivers/media/platform/vivid/vivid-kthread-out.c +++ /dev/null @@ -1,353 +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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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" -#include "vivid-meta-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; - struct vivid_buffer *meta_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); - } - if (!list_empty(&dev->meta_out_active)) { - meta_out_buf = list_entry(dev->meta_out_active.next, - struct vivid_buffer, list); - list_del(&meta_out_buf->list); - } - spin_unlock(&dev->slock); - - if (!vid_out_buf && !vbi_out_buf && !meta_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); - } - if (meta_out_buf) { - v4l2_ctrl_request_setup(meta_out_buf->vb.vb2_buf.req_obj.req, - &dev->ctrl_hdl_meta_out); - v4l2_ctrl_request_complete(meta_out_buf->vb.vb2_buf.req_obj.req, - &dev->ctrl_hdl_meta_out); - vivid_meta_out_process(dev, meta_out_buf); - meta_out_buf->vb.sequence = dev->meta_out_seq_count; - meta_out_buf->vb.vb2_buf.timestamp = - ktime_get_ns() + dev->time_wrap_offset; - vb2_buffer_done(&meta_out_buf->vb.vb2_buf, dev->dqbuf_error ? - VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE); - dprintk(dev, 2, "meta_out buffer %d done\n", - meta_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->meta_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; - dev->meta_out_seq_count = dev->out_seq_count - dev->meta_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 if (pstreaming == &dev->vbi_out_streaming) - dev->vbi_out_seq_start = seq_count; - else - dev->meta_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->meta_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 (pstreaming == &dev->meta_out_streaming) { - while (!list_empty(&dev->meta_out_active)) { - struct vivid_buffer *buf; - - buf = list_entry(dev->meta_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_meta_out); - vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); - dprintk(dev, 2, "meta_out buffer %d done\n", - buf->vb.vb2_buf.index); - } - } - - if (dev->vid_out_streaming || dev->vbi_out_streaming || - dev->meta_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 d5bcf44bbaca..000000000000 --- 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-kthread-touch.c b/drivers/media/platform/vivid/vivid-kthread-touch.c deleted file mode 100644 index 674507b5ccb5..000000000000 --- a/drivers/media/platform/vivid/vivid-kthread-touch.c +++ /dev/null @@ -1,181 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * vivid-kthread-touch.c - touch capture thread support functions. - * - */ - -#include -#include "vivid-core.h" -#include "vivid-kthread-touch.h" -#include "vivid-touch-cap.h" - -static noinline_for_stack void vivid_thread_tch_cap_tick(struct vivid_dev *dev, - int dropped_bufs) -{ - struct vivid_buffer *tch_cap_buf = NULL; - - spin_lock(&dev->slock); - if (!list_empty(&dev->touch_cap_active)) { - tch_cap_buf = list_entry(dev->touch_cap_active.next, - struct vivid_buffer, list); - list_del(&tch_cap_buf->list); - } - - spin_unlock(&dev->slock); - - if (tch_cap_buf) { - v4l2_ctrl_request_setup(tch_cap_buf->vb.vb2_buf.req_obj.req, - &dev->ctrl_hdl_touch_cap); - - vivid_fillbuff_tch(dev, tch_cap_buf); - v4l2_ctrl_request_complete(tch_cap_buf->vb.vb2_buf.req_obj.req, - &dev->ctrl_hdl_touch_cap); - vb2_buffer_done(&tch_cap_buf->vb.vb2_buf, dev->dqbuf_error ? - VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE); - dprintk(dev, 2, "touch_cap buffer %d done\n", - tch_cap_buf->vb.vb2_buf.index); - - tch_cap_buf->vb.vb2_buf.timestamp = ktime_get_ns() + dev->time_wrap_offset; - } - dev->dqbuf_error = false; -} - -static int vivid_thread_touch_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 int wait_jiffies; - unsigned int numerator; - unsigned int denominator; - int dropped_bufs; - - dprintk(dev, 1, "Touch Capture Thread Start\n"); - - set_freezable(); - - /* Resets frame counters */ - dev->touch_cap_seq_offset = 0; - dev->touch_cap_seq_count = 0; - dev->touch_cap_seq_resync = false; - dev->jiffies_touch_cap = jiffies; - - for (;;) { - try_to_freeze(); - if (kthread_should_stop()) - break; - - if (!mutex_trylock(&dev->mutex)) { - schedule_timeout_uninterruptible(1); - continue; - } - cur_jiffies = jiffies; - if (dev->touch_cap_seq_resync) { - dev->jiffies_touch_cap = cur_jiffies; - dev->touch_cap_seq_offset = dev->touch_cap_seq_count + 1; - dev->touch_cap_seq_count = 0; - dev->cap_seq_resync = false; - } - denominator = dev->timeperframe_tch_cap.denominator; - numerator = dev->timeperframe_tch_cap.numerator; - - /* Calculate the number of jiffies since we started streaming */ - jiffies_since_start = cur_jiffies - dev->jiffies_touch_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_touch_cap = cur_jiffies; - dev->cap_seq_offset = buffers_since_start; - buffers_since_start = 0; - } - dropped_bufs = buffers_since_start + dev->touch_cap_seq_offset - dev->touch_cap_seq_count; - dev->touch_cap_seq_count = buffers_since_start + dev->touch_cap_seq_offset; - - vivid_thread_tch_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_touch_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, "Touch Capture Thread End\n"); - return 0; -} - -int vivid_start_generating_touch_cap(struct vivid_dev *dev) -{ - if (dev->kthread_touch_cap) { - dev->touch_cap_streaming = true; - return 0; - } - - dev->kthread_touch_cap = kthread_run(vivid_thread_touch_cap, dev, - "%s-tch-cap", dev->v4l2_dev.name); - - if (IS_ERR(dev->kthread_touch_cap)) { - int err = PTR_ERR(dev->kthread_touch_cap); - - dev->kthread_touch_cap = NULL; - v4l2_err(&dev->v4l2_dev, "kernel_thread() failed\n"); - return err; - } - dev->touch_cap_streaming = true; - dprintk(dev, 1, "returning from %s\n", __func__); - return 0; -} - -void vivid_stop_generating_touch_cap(struct vivid_dev *dev) -{ - if (!dev->kthread_touch_cap) - return; - - dev->touch_cap_streaming = false; - - while (!list_empty(&dev->touch_cap_active)) { - struct vivid_buffer *buf; - - buf = list_entry(dev->touch_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_touch_cap); - vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); - dprintk(dev, 2, "touch_cap buffer %d done\n", - buf->vb.vb2_buf.index); - } - - kthread_stop(dev->kthread_touch_cap); - dev->kthread_touch_cap = NULL; -} diff --git a/drivers/media/platform/vivid/vivid-kthread-touch.h b/drivers/media/platform/vivid/vivid-kthread-touch.h deleted file mode 100644 index ecf79b46209e..000000000000 --- a/drivers/media/platform/vivid/vivid-kthread-touch.h +++ /dev/null @@ -1,13 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * vivid-kthread-cap.h - video/vbi capture thread support functions. - * - */ - -#ifndef _VIVID_KTHREAD_CAP_H_ -#define _VIVID_KTHREAD_CAP_H_ - -int vivid_start_generating_touch_cap(struct vivid_dev *dev); -void vivid_stop_generating_touch_cap(struct vivid_dev *dev); - -#endif diff --git a/drivers/media/platform/vivid/vivid-meta-cap.c b/drivers/media/platform/vivid/vivid-meta-cap.c deleted file mode 100644 index 780f96860a6d..000000000000 --- a/drivers/media/platform/vivid/vivid-meta-cap.c +++ /dev/null @@ -1,201 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * vivid-meta-cap.c - meta capture support functions. - */ - -#include -#include -#include -#include -#include - -#include "vivid-core.h" -#include "vivid-kthread-cap.h" -#include "vivid-meta-cap.h" - -static int meta_cap_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, - unsigned int *nplanes, unsigned int sizes[], - struct device *alloc_devs[]) -{ - struct vivid_dev *dev = vb2_get_drv_priv(vq); - unsigned int size = sizeof(struct vivid_uvc_meta_buf); - - if (!vivid_is_webcam(dev)) - return -EINVAL; - - if (*nplanes) { - if (sizes[0] < size) - return -EINVAL; - } else { - sizes[0] = size; - } - - if (vq->num_buffers + *nbuffers < 2) - *nbuffers = 2 - vq->num_buffers; - - *nplanes = 1; - return 0; -} - -static int meta_cap_buf_prepare(struct vb2_buffer *vb) -{ - struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue); - unsigned int size = sizeof(struct vivid_uvc_meta_buf); - - 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 meta_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->meta_cap_active); - spin_unlock(&dev->slock); -} - -static int meta_cap_start_streaming(struct vb2_queue *vq, unsigned int count) -{ - struct vivid_dev *dev = vb2_get_drv_priv(vq); - int err; - - dprintk(dev, 1, "%s\n", __func__); - dev->meta_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->meta_cap_streaming); - } - if (err) { - struct vivid_buffer *buf, *tmp; - - list_for_each_entry_safe(buf, tmp, - &dev->meta_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 meta_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->meta_cap_streaming); -} - -static void meta_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_meta_cap); -} - -const struct vb2_ops vivid_meta_cap_qops = { - .queue_setup = meta_cap_queue_setup, - .buf_prepare = meta_cap_buf_prepare, - .buf_queue = meta_cap_buf_queue, - .start_streaming = meta_cap_start_streaming, - .stop_streaming = meta_cap_stop_streaming, - .buf_request_complete = meta_cap_buf_request_complete, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, -}; - -int vidioc_enum_fmt_meta_cap(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - struct vivid_dev *dev = video_drvdata(file); - - if (!vivid_is_webcam(dev)) - return -EINVAL; - - if (f->index > 0) - return -EINVAL; - - f->type = V4L2_BUF_TYPE_META_CAPTURE; - f->pixelformat = V4L2_META_FMT_UVC; - return 0; -} - -int vidioc_g_fmt_meta_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct vivid_dev *dev = video_drvdata(file); - struct v4l2_meta_format *meta = &f->fmt.meta; - - if (!vivid_is_webcam(dev) || !dev->has_meta_cap) - return -EINVAL; - - meta->dataformat = V4L2_META_FMT_UVC; - meta->buffersize = sizeof(struct vivid_uvc_meta_buf); - return 0; -} - -void vivid_meta_cap_fillbuff(struct vivid_dev *dev, - struct vivid_buffer *buf, u64 soe) -{ - struct vivid_uvc_meta_buf *meta = vb2_plane_vaddr(&buf->vb.vb2_buf, 0); - int buf_off = 0; - - buf->vb.sequence = dev->meta_cap_seq_count; - if (dev->field_cap == V4L2_FIELD_ALTERNATE) - buf->vb.sequence /= 2; - memset(meta, 1, vb2_plane_size(&buf->vb.vb2_buf, 0)); - - meta->ns = ktime_get_ns(); - meta->sof = buf->vb.sequence * 30; - meta->length = sizeof(*meta) - offsetof(struct vivid_uvc_meta_buf, length); - meta->flags = UVC_STREAM_EOH | UVC_STREAM_EOF; - - if ((buf->vb.sequence % 2) == 0) - meta->flags |= UVC_STREAM_FID; - - dprintk(dev, 2, "%s ns:%llu sof:%4d len:%u flags: 0x%02x", - __func__, meta->ns, meta->sof, meta->length, meta->flags); - if (dev->meta_pts) { - meta->flags |= UVC_STREAM_PTS; - meta->buf[0] = div_u64(soe, VIVID_META_CLOCK_UNIT); - buf_off = 4; - dprintk(dev, 2, " pts: %u\n", *(__u32 *)(meta->buf)); - } - - if (dev->meta_scr) { - meta->flags |= UVC_STREAM_SCR; - meta->buf[buf_off] = div_u64((soe + dev->cap_frame_eof_offset), - VIVID_META_CLOCK_UNIT); - - meta->buf[buf_off + 4] = (buf->vb.sequence * 30) % 1000; - dprintk(dev, 2, " stc: %u, sof counter: %u\n", - *(__u32 *)(meta->buf + buf_off), - *(__u16 *)(meta->buf + buf_off + 4)); - } - dprintk(dev, 2, "\n"); -} diff --git a/drivers/media/platform/vivid/vivid-meta-cap.h b/drivers/media/platform/vivid/vivid-meta-cap.h deleted file mode 100644 index 4670d00d1576..000000000000 --- a/drivers/media/platform/vivid/vivid-meta-cap.h +++ /dev/null @@ -1,29 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * vivid-meta-cap.h - meta capture support functions. - */ -#ifndef _VIVID_META_CAP_H_ -#define _VIVID_META_CAP_H_ - -#define VIVID_META_CLOCK_UNIT 10 /* 100 MHz */ - -struct vivid_uvc_meta_buf { - __u64 ns; - __u16 sof; - __u8 length; - __u8 flags; - __u8 buf[10]; /* PTS(4)+STC(4)+SOF(2) */ -} __packed; - -void vivid_meta_cap_fillbuff(struct vivid_dev *dev, - struct vivid_buffer *buf, u64 soe); - -int vidioc_enum_fmt_meta_cap(struct file *file, void *priv, - struct v4l2_fmtdesc *f); - -int vidioc_g_fmt_meta_cap(struct file *file, void *priv, - struct v4l2_format *f); - -extern const struct vb2_ops vivid_meta_cap_qops; - -#endif diff --git a/drivers/media/platform/vivid/vivid-meta-out.c b/drivers/media/platform/vivid/vivid-meta-out.c deleted file mode 100644 index ff8a039aba72..000000000000 --- a/drivers/media/platform/vivid/vivid-meta-out.c +++ /dev/null @@ -1,174 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * vivid-meta-out.c - meta output support functions. - */ - -#include -#include -#include -#include -#include - -#include "vivid-core.h" -#include "vivid-kthread-out.h" -#include "vivid-meta-out.h" - -static int meta_out_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, - unsigned int *nplanes, unsigned int sizes[], - struct device *alloc_devs[]) -{ - struct vivid_dev *dev = vb2_get_drv_priv(vq); - unsigned int size = sizeof(struct vivid_meta_out_buf); - - if (!vivid_is_webcam(dev)) - return -EINVAL; - - if (*nplanes) { - if (sizes[0] < size) - return -EINVAL; - } else { - sizes[0] = size; - } - - if (vq->num_buffers + *nbuffers < 2) - *nbuffers = 2 - vq->num_buffers; - - *nplanes = 1; - return 0; -} - -static int meta_out_buf_prepare(struct vb2_buffer *vb) -{ - struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue); - unsigned int size = sizeof(struct vivid_meta_out_buf); - - 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 meta_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->meta_out_active); - spin_unlock(&dev->slock); -} - -static int meta_out_start_streaming(struct vb2_queue *vq, unsigned int count) -{ - struct vivid_dev *dev = vb2_get_drv_priv(vq); - int err; - - dprintk(dev, 1, "%s\n", __func__); - dev->meta_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->meta_out_streaming); - } - if (err) { - struct vivid_buffer *buf, *tmp; - - list_for_each_entry_safe(buf, tmp, - &dev->meta_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 meta_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->meta_out_streaming); -} - -static void meta_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_meta_out); -} - -const struct vb2_ops vivid_meta_out_qops = { - .queue_setup = meta_out_queue_setup, - .buf_prepare = meta_out_buf_prepare, - .buf_queue = meta_out_buf_queue, - .start_streaming = meta_out_start_streaming, - .stop_streaming = meta_out_stop_streaming, - .buf_request_complete = meta_out_buf_request_complete, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, -}; - -int vidioc_enum_fmt_meta_out(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - struct vivid_dev *dev = video_drvdata(file); - - if (!vivid_is_webcam(dev)) - return -EINVAL; - - if (f->index > 0) - return -EINVAL; - - f->type = V4L2_BUF_TYPE_META_OUTPUT; - f->pixelformat = V4L2_META_FMT_VIVID; - return 0; -} - -int vidioc_g_fmt_meta_out(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct vivid_dev *dev = video_drvdata(file); - struct v4l2_meta_format *meta = &f->fmt.meta; - - if (!vivid_is_webcam(dev) || !dev->has_meta_out) - return -EINVAL; - - meta->dataformat = V4L2_META_FMT_VIVID; - meta->buffersize = sizeof(struct vivid_meta_out_buf); - return 0; -} - -void vivid_meta_out_process(struct vivid_dev *dev, - struct vivid_buffer *buf) -{ - struct vivid_meta_out_buf *meta = vb2_plane_vaddr(&buf->vb.vb2_buf, 0); - - tpg_s_brightness(&dev->tpg, meta->brightness); - tpg_s_contrast(&dev->tpg, meta->contrast); - tpg_s_saturation(&dev->tpg, meta->saturation); - tpg_s_hue(&dev->tpg, meta->hue); - dprintk(dev, 2, " %s brightness %u contrast %u saturation %u hue %d\n", - __func__, meta->brightness, meta->contrast, - meta->saturation, meta->hue); -} diff --git a/drivers/media/platform/vivid/vivid-meta-out.h b/drivers/media/platform/vivid/vivid-meta-out.h deleted file mode 100644 index 0c639b7c2842..000000000000 --- a/drivers/media/platform/vivid/vivid-meta-out.h +++ /dev/null @@ -1,25 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * vivid-meta-out.h - meta output support functions. - */ -#ifndef _VIVID_META_OUT_H_ -#define _VIVID_META_OUT_H_ - -struct vivid_meta_out_buf { - u16 brightness; - u16 contrast; - u16 saturation; - s16 hue; -}; - -void vivid_meta_out_process(struct vivid_dev *dev, struct vivid_buffer *buf); -int vidioc_enum_fmt_meta_out(struct file *file, void *priv, - struct v4l2_fmtdesc *f); -int vidioc_g_fmt_meta_out(struct file *file, void *priv, - struct v4l2_format *f); -int vidioc_s_fmt_meta_out(struct file *file, void *priv, - struct v4l2_format *f); - -extern const struct vb2_ops vivid_meta_out_qops; - -#endif diff --git a/drivers/media/platform/vivid/vivid-osd.c b/drivers/media/platform/vivid/vivid-osd.c deleted file mode 100644 index fbaec8acc161..000000000000 --- a/drivers/media/platform/vivid/vivid-osd.c +++ /dev/null @@ -1,388 +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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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 const 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.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 f9ac1af25dd3..000000000000 --- 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 138c7bce68b1..000000000000 --- 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 -#include -#include -#include - -#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 30a9900e5b2b..000000000000 --- 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 232cab508f48..000000000000 --- 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 -#include -#include -#include -#include -#include - -#include -#include -#include - -#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 c9c7849f6f99..000000000000 --- 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 049d40b948bb..000000000000 --- 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 -#include -#include -#include -#include -#include -#include -#include -#include - -#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 c2bf1e7e634a..000000000000 --- 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 b5b104ee64c9..000000000000 --- 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 -#include -#include -#include - -#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 35ac5742302b..000000000000 --- 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 2b7522e16efc..000000000000 --- 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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 813c9248e5a7..000000000000 --- 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-touch-cap.c b/drivers/media/platform/vivid/vivid-touch-cap.c deleted file mode 100644 index ebb00b128030..000000000000 --- a/drivers/media/platform/vivid/vivid-touch-cap.c +++ /dev/null @@ -1,341 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * vivid-touch-cap.c - touch support functions. - */ - -#include "vivid-core.h" -#include "vivid-kthread-touch.h" -#include "vivid-vid-common.h" -#include "vivid-touch-cap.h" - -static int touch_cap_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, - unsigned int *nplanes, unsigned int sizes[], - struct device *alloc_devs[]) -{ - struct vivid_dev *dev = vb2_get_drv_priv(vq); - struct v4l2_pix_format *f = &dev->tch_format; - unsigned int size = f->sizeimage; - - if (*nplanes) { - if (sizes[0] < size) - return -EINVAL; - } else { - sizes[0] = size; - } - - if (vq->num_buffers + *nbuffers < 2) - *nbuffers = 2 - vq->num_buffers; - - *nplanes = 1; - return 0; -} - -static int touch_cap_buf_prepare(struct vb2_buffer *vb) -{ - struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue); - struct v4l2_pix_format *f = &dev->tch_format; - unsigned int size = f->sizeimage; - - 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 touch_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); - - vbuf->field = V4L2_FIELD_NONE; - spin_lock(&dev->slock); - list_add_tail(&buf->list, &dev->touch_cap_active); - spin_unlock(&dev->slock); -} - -static int touch_cap_start_streaming(struct vb2_queue *vq, unsigned int count) -{ - struct vivid_dev *dev = vb2_get_drv_priv(vq); - int err; - - dev->touch_cap_seq_count = 0; - if (dev->start_streaming_error) { - dev->start_streaming_error = false; - err = -EINVAL; - } else { - err = vivid_start_generating_touch_cap(dev); - } - if (err) { - struct vivid_buffer *buf, *tmp; - - list_for_each_entry_safe(buf, tmp, - &dev->touch_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 touch_cap_stop_streaming(struct vb2_queue *vq) -{ - struct vivid_dev *dev = vb2_get_drv_priv(vq); - - vivid_stop_generating_touch_cap(dev); -} - -static void touch_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_touch_cap); -} - -const struct vb2_ops vivid_touch_cap_qops = { - .queue_setup = touch_cap_queue_setup, - .buf_prepare = touch_cap_buf_prepare, - .buf_queue = touch_cap_buf_queue, - .start_streaming = touch_cap_start_streaming, - .stop_streaming = touch_cap_stop_streaming, - .buf_request_complete = touch_cap_buf_request_complete, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, -}; - -int vivid_enum_fmt_tch(struct file *file, void *priv, struct v4l2_fmtdesc *f) -{ - if (f->index) - return -EINVAL; - - f->pixelformat = V4L2_TCH_FMT_DELTA_TD16; - return 0; -} - -int vivid_g_fmt_tch(struct file *file, void *priv, struct v4l2_format *f) -{ - struct vivid_dev *dev = video_drvdata(file); - - if (dev->multiplanar) - return -ENOTTY; - f->fmt.pix = dev->tch_format; - return 0; -} - -int vivid_g_fmt_tch_mplane(struct file *file, void *priv, struct v4l2_format *f) -{ - struct vivid_dev *dev = video_drvdata(file); - struct v4l2_format sp_fmt; - - if (!dev->multiplanar) - return -ENOTTY; - sp_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - sp_fmt.fmt.pix = dev->tch_format; - fmt_sp2mp(&sp_fmt, f); - return 0; -} - -int vivid_g_parm_tch(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_tch_cap; - parm->parm.capture.readbuffers = 1; - return 0; -} - -int vivid_enum_input_tch(struct file *file, void *priv, struct v4l2_input *inp) -{ - if (inp->index) - return -EINVAL; - - inp->type = V4L2_INPUT_TYPE_TOUCH; - strscpy(inp->name, "Vivid Touch", sizeof(inp->name)); - inp->capabilities = 0; - return 0; -} - -int vivid_g_input_tch(struct file *file, void *priv, unsigned int *i) -{ - *i = 0; - return 0; -} - -int vivid_set_touch(struct vivid_dev *dev, unsigned int i) -{ - struct v4l2_pix_format *f = &dev->tch_format; - - if (i) - return -EINVAL; - - f->pixelformat = V4L2_TCH_FMT_DELTA_TD16; - f->width = VIVID_TCH_WIDTH; - f->height = VIVID_TCH_HEIGHT; - f->field = V4L2_FIELD_NONE; - f->colorspace = V4L2_COLORSPACE_RAW; - f->bytesperline = f->width * sizeof(s16); - f->sizeimage = f->width * f->height * sizeof(s16); - return 0; -} - -int vivid_s_input_tch(struct file *file, void *priv, unsigned int i) -{ - return vivid_set_touch(video_drvdata(file), i); -} - -static void vivid_fill_buff_noise(__s16 *tch_buf, int size) -{ - int i; - - /* Fill 10% of the values within range -3 and 3, zero the others */ - for (i = 0; i < size; i++) { - unsigned int rand = get_random_int(); - - if (rand % 10) - tch_buf[i] = 0; - else - tch_buf[i] = (rand / 10) % 7 - 3; - } -} - -static inline int get_random_pressure(void) -{ - return get_random_int() % VIVID_PRESSURE_LIMIT; -} - -static void vivid_tch_buf_set(struct v4l2_pix_format *f, - __s16 *tch_buf, - int index) -{ - unsigned int x = index % f->width; - unsigned int y = index / f->width; - unsigned int offset = VIVID_MIN_PRESSURE; - - tch_buf[index] = offset + get_random_pressure(); - offset /= 2; - if (x) - tch_buf[index - 1] = offset + get_random_pressure(); - if (x < f->width - 1) - tch_buf[index + 1] = offset + get_random_pressure(); - if (y) - tch_buf[index - f->width] = offset + get_random_pressure(); - if (y < f->height - 1) - tch_buf[index + f->width] = offset + get_random_pressure(); - offset /= 2; - if (x && y) - tch_buf[index - 1 - f->width] = offset + get_random_pressure(); - if (x < f->width - 1 && y) - tch_buf[index + 1 - f->width] = offset + get_random_pressure(); - if (x && y < f->height - 1) - tch_buf[index - 1 + f->width] = offset + get_random_pressure(); - if (x < f->width - 1 && y < f->height - 1) - tch_buf[index + 1 + f->width] = offset + get_random_pressure(); -} - -void vivid_fillbuff_tch(struct vivid_dev *dev, struct vivid_buffer *buf) -{ - struct v4l2_pix_format *f = &dev->tch_format; - int size = f->width * f->height; - int x, y, xstart, ystart, offset_x, offset_y; - unsigned int test_pattern, test_pat_idx, rand; - - __s16 *tch_buf = vb2_plane_vaddr(&buf->vb.vb2_buf, 0); - - buf->vb.sequence = dev->touch_cap_seq_count; - test_pattern = (buf->vb.sequence / TCH_SEQ_COUNT) % TEST_CASE_MAX; - test_pat_idx = buf->vb.sequence % TCH_SEQ_COUNT; - - vivid_fill_buff_noise(tch_buf, size); - - if (test_pat_idx >= TCH_PATTERN_COUNT) - return; - - if (test_pat_idx == 0) - dev->tch_pat_random = get_random_int(); - rand = dev->tch_pat_random; - - switch (test_pattern) { - case SINGLE_TAP: - if (test_pat_idx == 2) - vivid_tch_buf_set(f, tch_buf, rand % size); - break; - case DOUBLE_TAP: - if (test_pat_idx == 2 || test_pat_idx == 4) - vivid_tch_buf_set(f, tch_buf, rand % size); - break; - case TRIPLE_TAP: - if (test_pat_idx == 2 || test_pat_idx == 4 || test_pat_idx == 6) - vivid_tch_buf_set(f, tch_buf, rand % size); - break; - case MOVE_LEFT_TO_RIGHT: - vivid_tch_buf_set(f, tch_buf, - (rand % f->height) * f->width + - test_pat_idx * - (f->width / TCH_PATTERN_COUNT)); - break; - case ZOOM_IN: - x = f->width / 2; - y = f->height / 2; - offset_x = ((TCH_PATTERN_COUNT - 1 - test_pat_idx) * x) / - TCH_PATTERN_COUNT; - offset_y = ((TCH_PATTERN_COUNT - 1 - test_pat_idx) * y) / - TCH_PATTERN_COUNT; - vivid_tch_buf_set(f, tch_buf, - (x - offset_x) + f->width * (y - offset_y)); - vivid_tch_buf_set(f, tch_buf, - (x + offset_x) + f->width * (y + offset_y)); - break; - case ZOOM_OUT: - x = f->width / 2; - y = f->height / 2; - offset_x = (test_pat_idx * x) / TCH_PATTERN_COUNT; - offset_y = (test_pat_idx * y) / TCH_PATTERN_COUNT; - vivid_tch_buf_set(f, tch_buf, - (x - offset_x) + f->width * (y - offset_y)); - vivid_tch_buf_set(f, tch_buf, - (x + offset_x) + f->width * (y + offset_y)); - break; - case PALM_PRESS: - for (x = 0; x < f->width; x++) - for (y = f->height / 2; y < f->height; y++) - tch_buf[x + f->width * y] = VIVID_MIN_PRESSURE + - get_random_pressure(); - break; - case MULTIPLE_PRESS: - /* 16 pressure points */ - for (y = 0; y < 4; y++) { - for (x = 0; x < 4; x++) { - ystart = (y * f->height) / 4 + f->height / 8; - xstart = (x * f->width) / 4 + f->width / 8; - vivid_tch_buf_set(f, tch_buf, - ystart * f->width + xstart); - } - } - break; - } -#ifdef __BIG_ENDIAN__ - for (x = 0; x < size; x++) - tch_buf[x] = (__force s16)__cpu_to_le16((u16)tch_buf[x]); -#endif -} diff --git a/drivers/media/platform/vivid/vivid-touch-cap.h b/drivers/media/platform/vivid/vivid-touch-cap.h deleted file mode 100644 index 07e514046ae8..000000000000 --- a/drivers/media/platform/vivid/vivid-touch-cap.h +++ /dev/null @@ -1,39 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * vivid-touch-cap.h - touch support functions. - */ -#ifndef _VIVID_TOUCH_CAP_H_ -#define _VIVID_TOUCH_CAP_H_ - -#define VIVID_TCH_HEIGHT 12 -#define VIVID_TCH_WIDTH 21 -#define VIVID_MIN_PRESSURE 180 -#define VIVID_PRESSURE_LIMIT 40 -#define TCH_SEQ_COUNT 16 -#define TCH_PATTERN_COUNT 12 - -enum vivid_tch_test { - SINGLE_TAP, - DOUBLE_TAP, - TRIPLE_TAP, - MOVE_LEFT_TO_RIGHT, - ZOOM_IN, - ZOOM_OUT, - PALM_PRESS, - MULTIPLE_PRESS, - TEST_CASE_MAX -}; - -extern const struct vb2_ops vivid_touch_cap_qops; - -int vivid_enum_fmt_tch(struct file *file, void *priv, struct v4l2_fmtdesc *f); -int vivid_g_fmt_tch(struct file *file, void *priv, struct v4l2_format *f); -int vivid_g_fmt_tch_mplane(struct file *file, void *priv, struct v4l2_format *f); -int vivid_enum_input_tch(struct file *file, void *priv, struct v4l2_input *inp); -int vivid_g_input_tch(struct file *file, void *priv, unsigned int *i); -int vivid_s_input_tch(struct file *file, void *priv, unsigned int i); -void vivid_fillbuff_tch(struct vivid_dev *dev, struct vivid_buffer *buf); -int vivid_set_touch(struct vivid_dev *dev, unsigned int i); -int vivid_g_parm_tch(struct file *file, void *priv, - struct v4l2_streamparm *parm); -#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 1a9348eea781..000000000000 --- 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 -#include -#include -#include - -#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 91d2de01381c..000000000000 --- 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 acc98445a1fa..000000000000 --- 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 -#include -#include -#include -#include - -#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 2657a7f5571c..000000000000 --- 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 cd56476902a2..000000000000 --- 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 -#include -#include -#include - -#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 76584940cdaf..000000000000 --- 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 e94beef008c8..000000000000 --- a/drivers/media/platform/vivid/vivid-vid-cap.c +++ /dev/null @@ -1,1918 +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 -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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) || - vb2_is_busy(&dev->vb_meta_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; - dev->meta_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 1e422a59eeab..000000000000 --- 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 76b0be670ebb..000000000000 --- 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 -#include -#include -#include -#include -#include -#include -#include - -#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_BUF_TYPE_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 d908d9725283..000000000000 --- 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 ee3446e3217c..000000000000 --- a/drivers/media/platform/vivid/vivid-vid-out.c +++ /dev/null @@ -1,1210 +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 -#include -#include -#include -#include -#include -#include -#include -#include - -#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) || - vb2_is_busy(&dev->vb_meta_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; - dev->meta_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 8d56314f4ea1..000000000000 --- 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/test_drivers/Kconfig b/drivers/media/test_drivers/Kconfig new file mode 100644 index 000000000000..258a4d36c0d3 --- /dev/null +++ b/drivers/media/test_drivers/Kconfig @@ -0,0 +1,28 @@ +# SPDX-License-Identifier: GPL-2.0-only + +if MEDIA_TEST_SUPPORT + +menuconfig V4L_TEST_DRIVERS + bool "V4L test drivers" + depends on MEDIA_CAMERA_SUPPORT + +if V4L_TEST_DRIVERS + +source "drivers/media/test_drivers/vimc/Kconfig" + +source "drivers/media/test_drivers/vivid/Kconfig" + +config VIDEO_VIM2M + tristate "Virtual Memory-to-Memory Driver" + depends on VIDEO_DEV && VIDEO_V4L2 + select VIDEOBUF2_VMALLOC + select V4L2_MEM2MEM_DEV + help + This is a virtual test device for the memory-to-memory driver + framework. + +source "drivers/media/test_drivers/vicodec/Kconfig" + +endif #V4L_TEST_DRIVERS + +endif #MEDIA_TEST_SUPPORT diff --git a/drivers/media/test_drivers/Makefile b/drivers/media/test_drivers/Makefile new file mode 100644 index 000000000000..74410d3a9f2d --- /dev/null +++ b/drivers/media/test_drivers/Makefile @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for the test drivers. +# + +obj-$(CONFIG_VIDEO_VIMC) += vimc/ +obj-$(CONFIG_VIDEO_VIVID) += vivid/ +obj-$(CONFIG_VIDEO_VIM2M) += vim2m.o +obj-$(CONFIG_VIDEO_VICODEC) += vicodec/ diff --git a/drivers/media/test_drivers/vicodec/Kconfig b/drivers/media/test_drivers/vicodec/Kconfig new file mode 100644 index 000000000000..89456665cb16 --- /dev/null +++ b/drivers/media/test_drivers/vicodec/Kconfig @@ -0,0 +1,13 @@ +# 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/test_drivers/vicodec/Makefile b/drivers/media/test_drivers/vicodec/Makefile new file mode 100644 index 000000000000..01bf7e9308a6 --- /dev/null +++ b/drivers/media/test_drivers/vicodec/Makefile @@ -0,0 +1,4 @@ +# 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/test_drivers/vicodec/codec-fwht.c b/drivers/media/test_drivers/vicodec/codec-fwht.c new file mode 100644 index 000000000000..31faf319e508 --- /dev/null +++ b/drivers/media/test_drivers/vicodec/codec-fwht.c @@ -0,0 +1,958 @@ +// 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 +#include +#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/test_drivers/vicodec/codec-fwht.h b/drivers/media/test_drivers/vicodec/codec-fwht.h new file mode 100644 index 000000000000..b6fec2b1cbca --- /dev/null +++ b/drivers/media/test_drivers/vicodec/codec-fwht.h @@ -0,0 +1,150 @@ +/* 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 +#include +#include + +/* + * 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/test_drivers/vicodec/codec-v4l2-fwht.c b/drivers/media/test_drivers/vicodec/codec-v4l2-fwht.c new file mode 100644 index 000000000000..b6e39fbd8ad5 --- /dev/null +++ b/drivers/media/test_drivers/vicodec/codec-v4l2-fwht.c @@ -0,0 +1,367 @@ +// 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 +#include +#include +#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/test_drivers/vicodec/codec-v4l2-fwht.h b/drivers/media/test_drivers/vicodec/codec-v4l2-fwht.h new file mode 100644 index 000000000000..1a0d2a9f931a --- /dev/null +++ b/drivers/media/test_drivers/vicodec/codec-v4l2-fwht.h @@ -0,0 +1,64 @@ +/* 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/test_drivers/vicodec/vicodec-core.c b/drivers/media/test_drivers/vicodec/vicodec-core.c new file mode 100644 index 000000000000..30ced1c21387 --- /dev/null +++ b/drivers/media/test_drivers/vicodec/vicodec-core.c @@ -0,0 +1,2238 @@ +// 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 +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "codec-v4l2-fwht.h" + +MODULE_DESCRIPTION("Virtual codec device"); +MODULE_AUTHOR("Hans Verkuil "); +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; + spinlock_t *lock; + + struct v4l2_ctrl_handler hdl; + + /* 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 && + v4l2_m2m_is_last_draining_src_buf(ctx->fh.m2m_ctx, src_buf)) { + dst_buf->flags |= V4L2_BUF_FLAG_LAST; + v4l2_event_queue_fh(&ctx->fh, &vicodec_eos_event); + v4l2_m2m_mark_stopped(ctx->fh.m2m_ctx); + } + 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->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 (v4l2_m2m_is_last_draining_src_buf(ctx->fh.m2m_ctx, + 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 (v4l2_m2m_is_last_draining_src_buf(ctx->fh.m2m_ctx, + 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_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; + + ret = v4l2_m2m_ioctl_encoder_cmd(file, fh, ec); + if (ret < 0) + return ret; + + if (ec->cmd == V4L2_ENC_CMD_STOP && + v4l2_m2m_has_stopped(ctx->fh.m2m_ctx)) + v4l2_event_queue_fh(&ctx->fh, &vicodec_eos_event); + + if (ec->cmd == V4L2_ENC_CMD_START && + v4l2_m2m_has_stopped(ctx->fh.m2m_ctx)) + vb2_clear_last_buffer_dequeued(&ctx->fh.m2m_ctx->cap_q_ctx.q); + + return 0; +} + +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; + + ret = v4l2_m2m_ioctl_decoder_cmd(file, fh, dc); + if (ret < 0) + return ret; + + if (dc->cmd == V4L2_DEC_CMD_STOP && + v4l2_m2m_has_stopped(ctx->fh.m2m_ctx)) + v4l2_event_queue_fh(&ctx->fh, &vicodec_eos_event); + + if (dc->cmd == V4L2_DEC_CMD_START && + v4l2_m2m_has_stopped(ctx->fh.m2m_ctx)) + vb2_clear_last_buffer_dequeued(&ctx->fh.m2m_ctx->cap_q_ctx.q); + + return 0; +} + +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 (!V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type) && + vb2_is_streaming(vb->vb2_queue) && + v4l2_m2m_dst_buf_is_last(ctx->fh.m2m_ctx)) { + unsigned int i; + + for (i = 0; i < vb->num_planes; i++) + vb->planes[i].bytesused = 0; + + vbuf->field = V4L2_FIELD_NONE; + vbuf->sequence = + get_q_data(ctx, vb->vb2_queue->type)->sequence++; + + v4l2_m2m_last_buffer_done(ctx->fh.m2m_ctx, vbuf); + 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; + + v4l2_m2m_update_start_streaming_state(ctx->fh.m2m_ctx, q); + + 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); + + v4l2_m2m_update_stop_streaming_state(ctx->fh.m2m_ctx, q); + + if (V4L2_TYPE_IS_OUTPUT(q->type) && + v4l2_m2m_has_stopped(ctx->fh.m2m_ctx)) + v4l2_event_queue_fh(&ctx->fh, &vicodec_eos_event); + + 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); + 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_VIDEO, 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); + + if (register_instance(dev, &dev->stateful_enc, + "stateful-encoder", true)) + goto unreg_dev; + + if (register_instance(dev, &dev->stateful_dec, + "stateful-decoder", false)) + goto unreg_sf_enc; + + if (register_instance(dev, &dev->stateless_dec, + "stateless-decoder", false)) + 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/test_drivers/vim2m.c b/drivers/media/test_drivers/vim2m.c new file mode 100644 index 000000000000..ac6717fbb764 --- /dev/null +++ b/drivers/media/test_drivers/vim2m.c @@ -0,0 +1,1441 @@ +// 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, + * Marek Szyprowski, + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the + * License, or (at your option) any later version + */ +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +MODULE_DESCRIPTION("Virtual device for mem2mem framework testing"); +MODULE_AUTHOR("Pawel Osciak, "); +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_VIDEO, 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/test_drivers/vimc/Kconfig b/drivers/media/test_drivers/vimc/Kconfig new file mode 100644 index 000000000000..bd221d3e1a4a --- /dev/null +++ b/drivers/media/test_drivers/vimc/Kconfig @@ -0,0 +1,15 @@ +# 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/test_drivers/vimc/Makefile b/drivers/media/test_drivers/vimc/Makefile new file mode 100644 index 000000000000..a53b2b532e9f --- /dev/null +++ b/drivers/media/test_drivers/vimc/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0 +vimc-y := vimc-core.o vimc-common.o vimc-streamer.o vimc-capture.o \ + vimc-debayer.o vimc-scaler.o vimc-sensor.o + +obj-$(CONFIG_VIDEO_VIMC) += vimc.o + diff --git a/drivers/media/test_drivers/vimc/vimc-capture.c b/drivers/media/test_drivers/vimc/vimc-capture.c new file mode 100644 index 000000000000..23e740c1c5c0 --- /dev/null +++ b/drivers/media/test_drivers/vimc/vimc-capture.c @@ -0,0 +1,480 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * vimc-capture.c Virtual Media Controller Driver + * + * Copyright (C) 2015-2017 Helen Koike + */ + +#include +#include +#include + +#include "vimc-common.h" +#include "vimc-streamer.h" + +struct vimc_cap_device { + struct vimc_ent_device ved; + struct video_device vdev; + 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; + struct media_pad pad; +}; + +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->ved.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->ved.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_vdev_link_validate, +}; + +void vimc_cap_release(struct vimc_ent_device *ved) +{ + struct vimc_cap_device *vcap = + container_of(ved, struct vimc_cap_device, ved); + + media_entity_cleanup(vcap->ved.ent); + kfree(vcap); +} + +void vimc_cap_unregister(struct vimc_ent_device *ved) +{ + struct vimc_cap_device *vcap = + container_of(ved, struct vimc_cap_device, ved); + + vb2_queue_release(&vcap->queue); + 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; +} + +struct vimc_ent_device *vimc_cap_add(struct vimc_device *vimc, + const char *vcfg_name) +{ + struct v4l2_device *v4l2_dev = &vimc->v4l2_dev; + const struct vimc_pix_map *vpix; + 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 NULL; + + /* Initialize the media entity */ + vcap->vdev.entity.name = vcfg_name; + vcap->vdev.entity.function = MEDIA_ENT_F_IO_V4L; + vcap->pad.flags = MEDIA_PAD_FL_SINK; + ret = media_entity_pads_init(&vcap->vdev.entity, + 1, &vcap->pad); + if (ret) + goto err_free_vcap; + + /* 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(vimc->mdev.dev, "%s: vb2 queue init failed (err=%d)\n", + vcfg_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; + vcap->ved.dev = vimc->mdev.dev; + + /* 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 = video_device_release_empty; + 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, vcfg_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_VIDEO, -1); + if (ret) { + dev_err(vimc->mdev.dev, "%s: video register failed (err=%d)\n", + vcap->vdev.name, ret); + goto err_release_queue; + } + + return &vcap->ved; + +err_release_queue: + vb2_queue_release(q); +err_clean_m_ent: + media_entity_cleanup(&vcap->vdev.entity); +err_free_vcap: + kfree(vcap); + + return NULL; +} diff --git a/drivers/media/test_drivers/vimc/vimc-common.c b/drivers/media/test_drivers/vimc/vimc-common.c new file mode 100644 index 000000000000..c95c17c048f2 --- /dev/null +++ b/drivers/media/test_drivers/vimc/vimc-common.c @@ -0,0 +1,369 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * vimc-common.c Virtual Media Controller Driver + * + * Copyright (C) 2015-2017 Helen Koike + */ + +#include +#include + +#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, + }, +}; + +bool vimc_is_source(struct media_entity *ent) +{ + unsigned int i; + + for (i = 0; i < ent->num_pads; i++) + if (ent->pads[i].flags & MEDIA_PAD_FL_SINK) + return false; + return 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]; +} + +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; +} + +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; +} + +static int vimc_get_pix_format(struct media_pad *pad, + struct v4l2_pix_format *fmt) +{ + if (is_media_entity_v4l2_subdev(pad->entity)) { + struct v4l2_subdev *sd = + media_entity_to_v4l2_subdev(pad->entity); + struct v4l2_subdev_format sd_fmt; + const struct vimc_pix_map *pix_map; + int ret; + + sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + sd_fmt.pad = pad->index; + + ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &sd_fmt); + if (ret) + return ret; + + v4l2_fill_pix_format(fmt, &sd_fmt.format); + pix_map = vimc_pix_map_by_code(sd_fmt.format.code); + fmt->pixelformat = pix_map->pixelformat; + } 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); + + if (!ved->vdev_get_format) + return -ENOIOCTLCMD; + + ved->vdev_get_format(ved, fmt); + } else { + return -EINVAL; + } + + return 0; +} + +int vimc_vdev_link_validate(struct media_link *link) +{ + struct v4l2_pix_format source_fmt, sink_fmt; + int ret; + + ret = vimc_get_pix_format(link->source, &source_fmt); + if (ret) + return ret; + + ret = vimc_get_pix_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.width, source_fmt.height, + source_fmt.pixelformat, source_fmt.colorspace, + source_fmt.quantization, source_fmt.xfer_func, + source_fmt.ycbcr_enc, + /* sink */ + link->sink->entity->name, + sink_fmt.width, sink_fmt.height, + sink_fmt.pixelformat, sink_fmt.colorspace, + sink_fmt.quantization, sink_fmt.xfer_func, + sink_fmt.ycbcr_enc); + + /* The width, height and pixelformat must match. */ + if (source_fmt.width != sink_fmt.width || + source_fmt.height != sink_fmt.height || + source_fmt.pixelformat != sink_fmt.pixelformat) + 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.field != sink_fmt.field && + sink_fmt.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.colorspace == V4L2_COLORSPACE_DEFAULT || + sink_fmt.colorspace == V4L2_COLORSPACE_DEFAULT) + return 0; + + /* Colorspace must match. */ + if (source_fmt.colorspace != sink_fmt.colorspace) + return -EPIPE; + + /* Colorimetry must match if they are not set to DEFAULT */ + if (source_fmt.ycbcr_enc != V4L2_YCBCR_ENC_DEFAULT && + sink_fmt.ycbcr_enc != V4L2_YCBCR_ENC_DEFAULT && + source_fmt.ycbcr_enc != sink_fmt.ycbcr_enc) + return -EPIPE; + + if (source_fmt.quantization != V4L2_QUANTIZATION_DEFAULT && + sink_fmt.quantization != V4L2_QUANTIZATION_DEFAULT && + source_fmt.quantization != sink_fmt.quantization) + return -EPIPE; + + if (source_fmt.xfer_func != V4L2_XFER_FUNC_DEFAULT && + sink_fmt.xfer_func != V4L2_XFER_FUNC_DEFAULT && + source_fmt.xfer_func != sink_fmt.xfer_func) + return -EPIPE; + + return 0; +} + +static const struct media_entity_operations vimc_ent_sd_mops = { + .link_validate = v4l2_subdev_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, + struct media_pad *pads, + const struct v4l2_subdev_ops *sd_ops) +{ + int ret; + + /* Fill the vimc_ent_device struct */ + ved->ent = &sd->entity; + + /* Initialize the subdev */ + v4l2_subdev_init(sd, sd_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, pads); + if (ret) + return ret; + + /* 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); + return ret; +} diff --git a/drivers/media/test_drivers/vimc/vimc-common.h b/drivers/media/test_drivers/vimc/vimc-common.h new file mode 100644 index 000000000000..616d5a6b0754 --- /dev/null +++ b/drivers/media/test_drivers/vimc/vimc-common.h @@ -0,0 +1,224 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * vimc-common.h Virtual Media Controller Driver + * + * Copyright (C) 2015-2017 Helen Koike + */ + +#ifndef _VIMC_COMMON_H_ +#define _VIMC_COMMON_H_ + +#include +#include +#include +#include + +#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_CID_MEAN_WIN_SIZE (VIMC_CID_VIMC_BASE + 1) + +#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) + +/* Source and sink pad checks */ +#define VIMC_IS_SRC(pad) (pad) +#define VIMC_IS_SINK(pad) (!(pad)) + +/** + * 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_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 an entity in the + * topology + * + * @dev: a pointer of the device struct of the driver + * @ent: the pointer to struct media_entity for 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 device *dev; + struct media_entity *ent; + 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); +}; + +/** + * struct vimc_device - main device for vimc driver + * + * @pipe_cfg pointer to the vimc pipeline configuration structure + * @ent_devs array of vimc_ent_device pointers + * @mdev the associated media_device parent + * @v4l2_dev Internal v4l2 parent device + */ +struct vimc_device { + const struct vimc_pipeline_config *pipe_cfg; + struct vimc_ent_device **ent_devs; + struct media_device mdev; + struct v4l2_device v4l2_dev; +}; + +/** + * struct vimc_ent_config Structure which describes individual + * configuration for each entity + * + * @name entity name + * @ved pointer to vimc_ent_device (a node in the + * topology) + * @add initializes and registers + * vim entity - called from vimc-core + * @unregister unregisters vimc entity - called from vimc-core + * @release releases vimc entity - called from the v4l2_dev + * release callback + */ +struct vimc_ent_config { + const char *name; + struct vimc_ent_device *(*add)(struct vimc_device *vimc, + const char *vcfg_name); + void (*unregister)(struct vimc_ent_device *ved); + void (*release)(struct vimc_ent_device *ved); +}; + +/** + * vimc_is_source - returns true if the entity has only source pads + * + * @ent: pointer to &struct media_entity + * + */ +bool vimc_is_source(struct media_entity *ent); + +/* prototypes for vimc_ent_config hooks */ +struct vimc_ent_device *vimc_cap_add(struct vimc_device *vimc, + const char *vcfg_name); +void vimc_cap_unregister(struct vimc_ent_device *ved); +void vimc_cap_release(struct vimc_ent_device *ved); + +struct vimc_ent_device *vimc_deb_add(struct vimc_device *vimc, + const char *vcfg_name); +void vimc_deb_release(struct vimc_ent_device *ved); + +struct vimc_ent_device *vimc_sca_add(struct vimc_device *vimc, + const char *vcfg_name); +void vimc_sca_release(struct vimc_ent_device *ved); + +struct vimc_ent_device *vimc_sen_add(struct vimc_device *vimc, + const char *vcfg_name); +void vimc_sen_release(struct vimc_ent_device *ved); + +/** + * 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: the array of pads of the entity, the caller should set the + flags of the pads + * @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, + struct media_pad *pads, + const struct v4l2_subdev_ops *sd_ops); + +/** + * vimc_vdev_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_vdev_link_validate(struct media_link *link); + +#endif diff --git a/drivers/media/test_drivers/vimc/vimc-core.c b/drivers/media/test_drivers/vimc/vimc-core.c new file mode 100644 index 000000000000..339126e565dc --- /dev/null +++ b/drivers/media/test_drivers/vimc/vimc-core.c @@ -0,0 +1,381 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * vimc-core.c Virtual Media Controller Driver + * + * Copyright (C) 2015-2017 Helen Koike + */ + +#include +#include +#include +#include +#include + +#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, \ +} + +/* 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 struct vimc_ent_config ent_config[] = { + { + .name = "Sensor A", + .add = vimc_sen_add, + .release = vimc_sen_release, + }, + { + .name = "Sensor B", + .add = vimc_sen_add, + .release = vimc_sen_release, + }, + { + .name = "Debayer A", + .add = vimc_deb_add, + .release = vimc_deb_release, + }, + { + .name = "Debayer B", + .add = vimc_deb_add, + .release = vimc_deb_release, + }, + { + .name = "Raw Capture 0", + .add = vimc_cap_add, + .unregister = vimc_cap_unregister, + .release = vimc_cap_release, + }, + { + .name = "Raw Capture 1", + .add = vimc_cap_add, + .unregister = vimc_cap_unregister, + .release = vimc_cap_release, + }, + { + /* TODO: change this to vimc-input when it is implemented */ + .name = "RGB/YUV Input", + .add = vimc_sen_add, + .release = vimc_sen_release, + }, + { + .name = "Scaler", + .add = vimc_sca_add, + .release = vimc_sca_release, + }, + { + .name = "RGB/YUV Capture", + .add = vimc_cap_add, + .unregister = vimc_cap_unregister, + .release = vimc_cap_release, + }, +}; + +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 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 void vimc_rm_links(struct vimc_device *vimc) +{ + unsigned int i; + + for (i = 0; i < vimc->pipe_cfg->num_ents; i++) + media_entity_remove_links(vimc->ent_devs[i]->ent); +} + +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]; + + struct vimc_ent_device *ved_src = + vimc->ent_devs[link->src_ent]; + struct vimc_ent_device *ved_sink = + vimc->ent_devs[link->sink_ent]; + + ret = media_create_pad_link(ved_src->ent, link->src_pad, + ved_sink->ent, link->sink_pad, + link->flags); + if (ret) + goto err_rm_links; + } + + return 0; + +err_rm_links: + vimc_rm_links(vimc); + return ret; +} + +static int vimc_add_subdevs(struct vimc_device *vimc) +{ + unsigned int i; + + for (i = 0; i < vimc->pipe_cfg->num_ents; i++) { + dev_dbg(vimc->mdev.dev, "new entity for %s\n", + vimc->pipe_cfg->ents[i].name); + vimc->ent_devs[i] = vimc->pipe_cfg->ents[i].add(vimc, + vimc->pipe_cfg->ents[i].name); + if (!vimc->ent_devs[i]) { + dev_err(vimc->mdev.dev, "add new entity for %s\n", + vimc->pipe_cfg->ents[i].name); + return -EINVAL; + } + } + return 0; +} + +static void vimc_release_subdevs(struct vimc_device *vimc) +{ + unsigned int i; + + for (i = 0; i < vimc->pipe_cfg->num_ents; i++) + if (vimc->ent_devs[i]) + vimc->pipe_cfg->ents[i].release(vimc->ent_devs[i]); +} + +static void vimc_unregister_subdevs(struct vimc_device *vimc) +{ + unsigned int i; + + for (i = 0; i < vimc->pipe_cfg->num_ents; i++) + if (vimc->ent_devs[i] && vimc->pipe_cfg->ents[i].unregister) + vimc->pipe_cfg->ents[i].unregister(vimc->ent_devs[i]); +} + +static void vimc_v4l2_dev_release(struct v4l2_device *v4l2_dev) +{ + struct vimc_device *vimc = + container_of(v4l2_dev, struct vimc_device, v4l2_dev); + + vimc_release_subdevs(vimc); + media_device_cleanup(&vimc->mdev); + kfree(vimc->ent_devs); + kfree(vimc); +} + +static int vimc_register_devices(struct vimc_device *vimc) +{ + int ret; + + /* 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; + } + /* allocate ent_devs */ + vimc->ent_devs = kcalloc(vimc->pipe_cfg->num_ents, + sizeof(*vimc->ent_devs), GFP_KERNEL); + if (!vimc->ent_devs) { + ret = -ENOMEM; + goto err_v4l2_unregister; + } + + /* Invoke entity config hooks to initialize and register subdevs */ + ret = vimc_add_subdevs(vimc); + if (ret) + /* remove sundevs that got added */ + goto err_rm_subdevs; + + /* Initialize links */ + ret = vimc_create_links(vimc); + if (ret) + goto err_rm_subdevs; + + /* 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_rm_subdevs; + } + + /* 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); +err_rm_subdevs: + vimc_unregister_subdevs(vimc); + vimc_release_subdevs(vimc); + kfree(vimc->ent_devs); +err_v4l2_unregister: + v4l2_device_unregister(&vimc->v4l2_dev); + + return ret; +} + +static void vimc_unregister(struct vimc_device *vimc) +{ + vimc_unregister_subdevs(vimc); + media_device_unregister(&vimc->mdev); + v4l2_device_unregister(&vimc->v4l2_dev); +} + +static int vimc_probe(struct platform_device *pdev) +{ + struct vimc_device *vimc; + int ret; + + dev_dbg(&pdev->dev, "probe"); + + vimc = kzalloc(sizeof(*vimc), GFP_KERNEL); + if (!vimc) + return -ENOMEM; + + vimc->pipe_cfg = &pipe_cfg; + + /* 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); + + ret = vimc_register_devices(vimc); + if (ret) { + media_device_cleanup(&vimc->mdev); + kfree(vimc); + return ret; + } + /* + * the release cb is set only after successful registration. + * if the registration fails, we release directly from probe + */ + + vimc->v4l2_dev.release = vimc_v4l2_dev_release; + platform_set_drvdata(pdev, vimc); + return 0; +} + +static int vimc_remove(struct platform_device *pdev) +{ + struct vimc_device *vimc = platform_get_drvdata(pdev); + + dev_dbg(&pdev->dev, "remove"); + + vimc_unregister(vimc); + v4l2_device_put(&vimc->v4l2_dev); + + return 0; +} + +static void vimc_dev_release(struct device *dev) +{ +} + +static struct platform_device vimc_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_pdev); + if (ret) { + dev_err(&vimc_pdev.dev, + "platform device registration failed (err=%d)\n", ret); + return ret; + } + + ret = platform_driver_register(&vimc_pdrv); + if (ret) { + dev_err(&vimc_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_pdev); +} + +module_init(vimc_init); +module_exit(vimc_exit); + +MODULE_DESCRIPTION("Virtual Media Controller Driver (VIMC)"); +MODULE_AUTHOR("Helen Fornazier "); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/test_drivers/vimc/vimc-debayer.c b/drivers/media/test_drivers/vimc/vimc-debayer.c new file mode 100644 index 000000000000..baf6bf9f65b5 --- /dev/null +++ b/drivers/media/test_drivers/vimc/vimc-debayer.c @@ -0,0 +1,581 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * vimc-debayer.c Virtual Media Controller Driver + * + * Copyright (C) 2015-2017 Helen Koike + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "vimc-common.h" + +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; + /* 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; + unsigned int mean_win_size; + struct v4l2_ctrl_handler hdl; + struct media_pad pads[2]; +}; + +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 (VIMC_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 (VIMC_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 (VIMC_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 (VIMC_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->ved.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_core_ops vimc_deb_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_deb_video_ops = { + .s_stream = vimc_deb_s_stream, +}; + +static const struct v4l2_subdev_ops vimc_deb_ops = { + .core = &vimc_deb_core_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 = vdeb->mean_win_size / 2; + + /* Sum the values of the colors in the mean window */ + + dev_dbg(vdeb->ved.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->ved.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->ved.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->ved.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->ved.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 int vimc_deb_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct vimc_deb_device *vdeb = + container_of(ctrl->handler, struct vimc_deb_device, hdl); + + switch (ctrl->id) { + case VIMC_CID_MEAN_WIN_SIZE: + vdeb->mean_win_size = ctrl->val; + break; + default: + return -EINVAL; + } + return 0; +} + +static const struct v4l2_ctrl_ops vimc_deb_ctrl_ops = { + .s_ctrl = vimc_deb_s_ctrl, +}; + +void vimc_deb_release(struct vimc_ent_device *ved) +{ + struct vimc_deb_device *vdeb = + container_of(ved, struct vimc_deb_device, ved); + + v4l2_ctrl_handler_free(&vdeb->hdl); + media_entity_cleanup(vdeb->ved.ent); + kfree(vdeb); +} + +static const struct v4l2_ctrl_config vimc_deb_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_deb_ctrl_mean_win_size = { + .ops = &vimc_deb_ctrl_ops, + .id = VIMC_CID_MEAN_WIN_SIZE, + .name = "Debayer Mean Window Size", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 1, + .max = 25, + .step = 2, + .def = 3, +}; + +struct vimc_ent_device *vimc_deb_add(struct vimc_device *vimc, + const char *vcfg_name) +{ + struct v4l2_device *v4l2_dev = &vimc->v4l2_dev; + struct vimc_deb_device *vdeb; + int ret; + + /* Allocate the vdeb struct */ + vdeb = kzalloc(sizeof(*vdeb), GFP_KERNEL); + if (!vdeb) + return NULL; + + /* Create controls: */ + v4l2_ctrl_handler_init(&vdeb->hdl, 2); + v4l2_ctrl_new_custom(&vdeb->hdl, &vimc_deb_ctrl_class, NULL); + v4l2_ctrl_new_custom(&vdeb->hdl, &vimc_deb_ctrl_mean_win_size, NULL); + vdeb->sd.ctrl_handler = &vdeb->hdl; + if (vdeb->hdl.error) { + ret = vdeb->hdl.error; + goto err_free_vdeb; + } + + /* Initialize ved and sd */ + vdeb->pads[0].flags = MEDIA_PAD_FL_SINK; + vdeb->pads[1].flags = MEDIA_PAD_FL_SOURCE; + + ret = vimc_ent_sd_register(&vdeb->ved, &vdeb->sd, v4l2_dev, + vcfg_name, + MEDIA_ENT_F_PROC_VIDEO_PIXEL_ENC_CONV, 2, + vdeb->pads, &vimc_deb_ops); + if (ret) + goto err_free_hdl; + + vdeb->ved.process_frame = vimc_deb_process_frame; + vdeb->ved.dev = vimc->mdev.dev; + vdeb->mean_win_size = vimc_deb_ctrl_mean_win_size.def; + + /* 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 &vdeb->ved; + +err_free_hdl: + v4l2_ctrl_handler_free(&vdeb->hdl); +err_free_vdeb: + kfree(vdeb); + + return NULL; +} diff --git a/drivers/media/test_drivers/vimc/vimc-scaler.c b/drivers/media/test_drivers/vimc/vimc-scaler.c new file mode 100644 index 000000000000..7521439747c5 --- /dev/null +++ b/drivers/media/test_drivers/vimc/vimc-scaler.c @@ -0,0 +1,511 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * vimc-scaler.c Virtual Media Controller Driver + * + * Copyright (C) 2015-2017 Helen Koike + */ + +#include +#include +#include +#include +#include + +#include "vimc-common.h" + +static unsigned int sca_mult = 3; +module_param(sca_mult, uint, 0000); +MODULE_PARM_DESC(sca_mult, " the image size multiplier"); + +#define MAX_ZOOM 8 + +#define VIMC_SCA_FMT_WIDTH_DEFAULT 640 +#define VIMC_SCA_FMT_HEIGHT_DEFAULT 480 + +struct vimc_sca_device { + struct vimc_ent_device ved; + struct v4l2_subdev sd; + /* NOTE: the source fmt is the same as the sink + * with the width and hight multiplied by mult + */ + struct v4l2_mbus_framefmt sink_fmt; + struct v4l2_rect crop_rect; + /* Values calculated when the stream starts */ + u8 *src_frame; + unsigned int src_line_size; + unsigned int bpp; + struct media_pad pads[2]; +}; + +static const struct v4l2_mbus_framefmt sink_fmt_default = { + .width = VIMC_SCA_FMT_WIDTH_DEFAULT, + .height = VIMC_SCA_FMT_HEIGHT_DEFAULT, + .code = MEDIA_BUS_FMT_RGB888_1X24, + .field = V4L2_FIELD_NONE, + .colorspace = V4L2_COLORSPACE_DEFAULT, +}; + +static const struct v4l2_rect crop_rect_default = { + .width = VIMC_SCA_FMT_WIDTH_DEFAULT, + .height = VIMC_SCA_FMT_HEIGHT_DEFAULT, + .top = 0, + .left = 0, +}; + +static const struct v4l2_rect crop_rect_min = { + .width = VIMC_FRAME_MIN_WIDTH, + .height = VIMC_FRAME_MIN_HEIGHT, + .top = 0, + .left = 0, +}; + +static struct v4l2_rect +vimc_sca_get_crop_bound_sink(const struct v4l2_mbus_framefmt *sink_fmt) +{ + /* Get the crop bounds to clamp the crop rectangle correctly */ + struct v4l2_rect r = { + .left = 0, + .top = 0, + .width = sink_fmt->width, + .height = sink_fmt->height, + }; + return r; +} + +static void vimc_sca_adjust_sink_crop(struct v4l2_rect *r, + const struct v4l2_mbus_framefmt *sink_fmt) +{ + const struct v4l2_rect sink_rect = + vimc_sca_get_crop_bound_sink(sink_fmt); + + /* Disallow rectangles smaller than the minimal one. */ + v4l2_rect_set_min_size(r, &crop_rect_min); + v4l2_rect_map_inside(r, &sink_rect); +} + +static int vimc_sca_init_cfg(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg) +{ + struct v4l2_mbus_framefmt *mf; + struct v4l2_rect *r; + unsigned int i; + + mf = v4l2_subdev_get_try_format(sd, cfg, 0); + *mf = sink_fmt_default; + + r = v4l2_subdev_get_try_crop(sd, cfg, 0); + *r = crop_rect_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 (VIMC_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); + struct v4l2_rect *crop_rect; + + /* Get the current sink format */ + if (format->which == V4L2_SUBDEV_FORMAT_TRY) { + format->format = *v4l2_subdev_get_try_format(sd, cfg, 0); + crop_rect = v4l2_subdev_get_try_crop(sd, cfg, 0); + } else { + format->format = vsca->sink_fmt; + crop_rect = &vsca->crop_rect; + } + + /* Scale the frame size for the source pad */ + if (VIMC_IS_SRC(format->pad)) { + format->format.width = crop_rect->width * sca_mult; + format->format.height = crop_rect->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; + struct v4l2_rect *crop_rect; + + 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; + crop_rect = &vsca->crop_rect; + } else { + sink_fmt = v4l2_subdev_get_try_format(sd, cfg, 0); + crop_rect = v4l2_subdev_get_try_crop(sd, cfg, 0); + } + + /* + * Do not change the format of the source pad, + * it is propagated from the sink + */ + if (VIMC_IS_SRC(fmt->pad)) { + fmt->format = *sink_fmt; + fmt->format.width = crop_rect->width * sca_mult; + fmt->format.height = crop_rect->height * sca_mult; + } else { + /* Set the new format in the sink pad */ + vimc_sca_adjust_sink_fmt(&fmt->format); + + dev_dbg(vsca->ved.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; + + /* Do the crop, but respect the current bounds */ + vimc_sca_adjust_sink_crop(crop_rect, sink_fmt); + } + + return 0; +} + +static int vimc_sca_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) +{ + struct vimc_sca_device *vsca = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *sink_fmt; + struct v4l2_rect *crop_rect; + + if (VIMC_IS_SRC(sel->pad)) + return -EINVAL; + + if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + sink_fmt = &vsca->sink_fmt; + crop_rect = &vsca->crop_rect; + } else { + sink_fmt = v4l2_subdev_get_try_format(sd, cfg, 0); + crop_rect = v4l2_subdev_get_try_crop(sd, cfg, 0); + } + + switch (sel->target) { + case V4L2_SEL_TGT_CROP: + sel->r = *crop_rect; + break; + case V4L2_SEL_TGT_CROP_BOUNDS: + sel->r = vimc_sca_get_crop_bound_sink(sink_fmt); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int vimc_sca_set_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) +{ + struct vimc_sca_device *vsca = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *sink_fmt; + struct v4l2_rect *crop_rect; + + if (VIMC_IS_SRC(sel->pad)) + return -EINVAL; + + if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + /* Do not change the format while stream is on */ + if (vsca->src_frame) + return -EBUSY; + + crop_rect = &vsca->crop_rect; + sink_fmt = &vsca->sink_fmt; + } else { + crop_rect = v4l2_subdev_get_try_crop(sd, cfg, 0); + sink_fmt = v4l2_subdev_get_try_format(sd, cfg, 0); + } + + switch (sel->target) { + case V4L2_SEL_TGT_CROP: + /* Do the crop, but respect the current bounds */ + vimc_sca_adjust_sink_crop(&sel->r, sink_fmt); + *crop_rect = sel->r; + break; + default: + return -EINVAL; + } + + 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, + .get_selection = vimc_sca_get_selection, + .set_selection = vimc_sca_set_selection, +}; + +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->crop_rect.width * + sca_mult * vsca->bpp; + + /* Calculate the frame size of the source pad */ + frame_size = vsca->src_line_size * vsca->crop_rect.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, + unsigned int lin, unsigned int col, + const u8 *const sink_frame) +{ + const struct v4l2_rect crop_rect = vsca->crop_rect; + 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->ved.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 + */ + lin -= crop_rect.top; + col -= crop_rect.left; + index = VIMC_FRAME_INDEX(lin * sca_mult, col * sca_mult, + crop_rect.width * sca_mult, vsca->bpp); + + dev_dbg(vsca->ved.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->ved.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) +{ + const struct v4l2_rect r = vsca->crop_rect; + 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 = r.top; i < r.top + r.height; i++) + for (j = r.left; j < r.left + r.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; +}; + +void vimc_sca_release(struct vimc_ent_device *ved) +{ + struct vimc_sca_device *vsca = + container_of(ved, struct vimc_sca_device, ved); + + media_entity_cleanup(vsca->ved.ent); + kfree(vsca); +} + +struct vimc_ent_device *vimc_sca_add(struct vimc_device *vimc, + const char *vcfg_name) +{ + struct v4l2_device *v4l2_dev = &vimc->v4l2_dev; + struct vimc_sca_device *vsca; + int ret; + + /* Allocate the vsca struct */ + vsca = kzalloc(sizeof(*vsca), GFP_KERNEL); + if (!vsca) + return NULL; + + /* Initialize ved and sd */ + vsca->pads[0].flags = MEDIA_PAD_FL_SINK; + vsca->pads[1].flags = MEDIA_PAD_FL_SOURCE; + + ret = vimc_ent_sd_register(&vsca->ved, &vsca->sd, v4l2_dev, + vcfg_name, + MEDIA_ENT_F_PROC_VIDEO_SCALER, 2, + vsca->pads, &vimc_sca_ops); + if (ret) { + kfree(vsca); + return NULL; + } + + vsca->ved.process_frame = vimc_sca_process_frame; + vsca->ved.dev = vimc->mdev.dev; + + /* Initialize the frame format */ + vsca->sink_fmt = sink_fmt_default; + + /* Initialize the crop selection */ + vsca->crop_rect = crop_rect_default; + + return &vsca->ved; +} diff --git a/drivers/media/test_drivers/vimc/vimc-sensor.c b/drivers/media/test_drivers/vimc/vimc-sensor.c new file mode 100644 index 000000000000..92daee58209e --- /dev/null +++ b/drivers/media/test_drivers/vimc/vimc-sensor.c @@ -0,0 +1,376 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * vimc-sensor.c Virtual Media Controller Driver + * + * Copyright (C) 2015-2017 Helen Koike + */ + +#include +#include +#include +#include +#include +#include + +#include "vimc-common.h" + +struct vimc_sen_device { + struct vimc_ent_device ved; + struct v4l2_subdev sd; + struct tpg_data tpg; + u8 *frame; + /* The active format */ + struct v4l2_mbus_framefmt mbus_format; + struct v4l2_ctrl_handler hdl; + struct media_pad pad; +}; + +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->ved.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, +}; + +void vimc_sen_release(struct vimc_ent_device *ved) +{ + struct vimc_sen_device *vsen = + container_of(ved, struct vimc_sen_device, ved); + + v4l2_ctrl_handler_free(&vsen->hdl); + tpg_free(&vsen->tpg); + media_entity_cleanup(vsen->ved.ent); + kfree(vsen); +} + +/* 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, +}; + +struct vimc_ent_device *vimc_sen_add(struct vimc_device *vimc, + const char *vcfg_name) +{ + struct v4l2_device *v4l2_dev = &vimc->v4l2_dev; + struct vimc_sen_device *vsen; + int ret; + + /* Allocate the vsen struct */ + vsen = kzalloc(sizeof(*vsen), GFP_KERNEL); + if (!vsen) + return NULL; + + 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 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_free_hdl; + + /* Initialize ved and sd */ + vsen->pad.flags = MEDIA_PAD_FL_SOURCE; + ret = vimc_ent_sd_register(&vsen->ved, &vsen->sd, v4l2_dev, + vcfg_name, + MEDIA_ENT_F_CAM_SENSOR, 1, &vsen->pad, + &vimc_sen_ops); + if (ret) + goto err_free_tpg; + + vsen->ved.process_frame = vimc_sen_process_frame; + vsen->ved.dev = vimc->mdev.dev; + + /* Initialize the frame format */ + vsen->mbus_format = fmt_default; + + return &vsen->ved; + +err_free_tpg: + tpg_free(&vsen->tpg); +err_free_hdl: + v4l2_ctrl_handler_free(&vsen->hdl); +err_free_vsen: + kfree(vsen); + + return NULL; +} diff --git a/drivers/media/test_drivers/vimc/vimc-streamer.c b/drivers/media/test_drivers/vimc/vimc-streamer.c new file mode 100644 index 000000000000..65feb3c596db --- /dev/null +++ b/drivers/media/test_drivers/vimc/vimc-streamer.c @@ -0,0 +1,238 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * vimc-streamer.c Virtual Media Controller Driver + * + * Copyright (C) 2018 Lucas A. M. Magalhães + * + */ + +#include +#include +#include + +#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) { + dev_err(ved->dev, "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) { + /* the first entity of the pipe should be source only */ + if (!vimc_is_source(ved->ent)) { + dev_err(ved->dev, + "first entity in the pipe '%s' is not a source\n", + ved->ent->name); + vimc_streamer_pipeline_terminate(stream); + return -EPIPE; + } + 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)) { + ret = PTR_ERR(stream->kthread); + dev_err(ved->dev, "kthread_run failed with %d\n", ret); + vimc_streamer_pipeline_terminate(stream); + stream->kthread = NULL; + return ret; + } + + } else { + if (!stream->kthread) + return 0; + + ret = kthread_stop(stream->kthread); + /* + * kthread_stop returns -EINTR in cases when streamon was + * immediately followed by streamoff, and the thread didn't had + * a chance to run. Ignore errors to stop the stream in the + * pipeline. + */ + if (ret) + dev_dbg(ved->dev, "kthread_stop returned '%d'\n", ret); + + stream->kthread = NULL; + + vimc_streamer_pipeline_terminate(stream); + } + + return 0; +} diff --git a/drivers/media/test_drivers/vimc/vimc-streamer.h b/drivers/media/test_drivers/vimc/vimc-streamer.h new file mode 100644 index 000000000000..fe3c51f15fad --- /dev/null +++ b/drivers/media/test_drivers/vimc/vimc-streamer.h @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * vimc-streamer.h Virtual Media Controller Driver + * + * Copyright (C) 2018 Lucas A. M. Magalhães + * + */ + +#ifndef _VIMC_STREAMER_H_ +#define _VIMC_STREAMER_H_ + +#include + +#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/test_drivers/vivid/Kconfig b/drivers/media/test_drivers/vivid/Kconfig new file mode 100644 index 000000000000..e2ff06edfa93 --- /dev/null +++ b/drivers/media/test_drivers/vivid/Kconfig @@ -0,0 +1,41 @@ +# 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/test_drivers/vivid/Makefile b/drivers/media/test_drivers/vivid/Makefile new file mode 100644 index 000000000000..b12ad0152a3e --- /dev/null +++ b/drivers/media/test_drivers/vivid/Makefile @@ -0,0 +1,12 @@ +# 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 vivid-meta-cap.o vivid-meta-out.o \ + vivid-kthread-touch.o vivid-touch-cap.o +ifeq ($(CONFIG_VIDEO_VIVID_CEC),y) + vivid-objs += vivid-cec.o +endif + +obj-$(CONFIG_VIDEO_VIVID) += vivid.o diff --git a/drivers/media/test_drivers/vivid/vivid-cec.c b/drivers/media/test_drivers/vivid/vivid-cec.c new file mode 100644 index 000000000000..4d2413e87730 --- /dev/null +++ b/drivers/media/test_drivers/vivid/vivid-cec.c @@ -0,0 +1,286 @@ +// 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 + +#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) +{ + u32 caps = CEC_CAP_DEFAULTS | CEC_CAP_MONITOR_ALL | CEC_CAP_MONITOR_PIN; + char name[32]; + + snprintf(name, sizeof(name), "vivid-%03d-vid-%s%d", + dev->inst, is_source ? "out" : "cap", idx); + return cec_allocate_adapter(&vivid_cec_adap_ops, dev, + name, caps, 1); +} diff --git a/drivers/media/test_drivers/vivid/vivid-cec.h b/drivers/media/test_drivers/vivid/vivid-cec.h new file mode 100644 index 000000000000..7524ed48a914 --- /dev/null +++ b/drivers/media/test_drivers/vivid/vivid-cec.h @@ -0,0 +1,20 @@ +/* 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/test_drivers/vivid/vivid-core.c b/drivers/media/test_drivers/vivid/vivid-core.c new file mode 100644 index 000000000000..6c740e3e6999 --- /dev/null +++ b/drivers/media/test_drivers/vivid/vivid-core.c @@ -0,0 +1,2006 @@ +// 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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" +#include "vivid-meta-cap.h" +#include "vivid-meta-out.h" +#include "vivid-touch-cap.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 meta_cap_nr[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = -1 }; +module_param_array(meta_cap_nr, int, NULL, 0444); +MODULE_PARM_DESC(meta_cap_nr, " videoX start number, -1 is autodetect"); + +static int meta_out_nr[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = -1 }; +module_param_array(meta_out_nr, int, NULL, 0444); +MODULE_PARM_DESC(meta_out_nr, " videoX start number, -1 is autodetect"); + +static int touch_cap_nr[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = -1 }; +module_param_array(touch_cap_nr, int, NULL, 0444); +MODULE_PARM_DESC(touch_cap_nr, " v4l-touchX 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 + meta-cap + */ +static unsigned int node_types[VIVID_MAX_DEVS] = { + [0 ... (VIVID_MAX_DEVS - 1)] = 0xe1d3d +}; +module_param_array(node_types, uint, NULL, 0444); +MODULE_PARM_DESC(node_types, " node types, default is 0xe1d3d. 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\n" + "\t\t bit 17: Metadata Capture node\n" + "\t\t bit 18: Metadata Output node\n" + "\t\t bit 19: Touch Capture node\n"); + +/* 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, 0xf0, 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, 0xea, 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, 0x63, +}; + +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 | dev->meta_cap_caps | + dev->meta_out_caps | dev->touch_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_type == VFL_TYPE_TOUCH) + return vivid_g_parm_tch(file, fh, parm); + 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_VIDEO) + 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 int vivid_enum_input(struct file *file, void *priv, + struct v4l2_input *inp) +{ + struct video_device *vdev = video_devdata(file); + + if (vdev->vfl_type == VFL_TYPE_TOUCH) + return vivid_enum_input_tch(file, priv, inp); + return vidioc_enum_input(file, priv, inp); +} + +static int vivid_g_input(struct file *file, void *priv, unsigned int *i) +{ + struct video_device *vdev = video_devdata(file); + + if (vdev->vfl_type == VFL_TYPE_TOUCH) + return vivid_g_input_tch(file, priv, i); + return vidioc_g_input(file, priv, i); +} + +static int vivid_s_input(struct file *file, void *priv, unsigned int i) +{ + struct video_device *vdev = video_devdata(file); + + if (vdev->vfl_type == VFL_TYPE_TOUCH) + return vivid_s_input_tch(file, priv, i); + return vidioc_s_input(file, priv, i); +} + +static int vivid_enum_fmt_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + struct video_device *vdev = video_devdata(file); + + if (vdev->vfl_type == VFL_TYPE_TOUCH) + return vivid_enum_fmt_tch(file, priv, f); + return vivid_enum_fmt_vid(file, priv, f); +} + +static int vivid_g_fmt_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct video_device *vdev = video_devdata(file); + + if (vdev->vfl_type == VFL_TYPE_TOUCH) + return vivid_g_fmt_tch(file, priv, f); + return vidioc_g_fmt_vid_cap(file, priv, f); +} + +static int vivid_try_fmt_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct video_device *vdev = video_devdata(file); + + if (vdev->vfl_type == VFL_TYPE_TOUCH) + return vivid_g_fmt_tch(file, priv, f); + return vidioc_try_fmt_vid_cap(file, priv, f); +} + +static int vivid_s_fmt_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct video_device *vdev = video_devdata(file); + + if (vdev->vfl_type == VFL_TYPE_TOUCH) + return vivid_g_fmt_tch(file, priv, f); + return vidioc_s_fmt_vid_cap(file, priv, f); +} + +static int vivid_g_fmt_cap_mplane(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct video_device *vdev = video_devdata(file); + + if (vdev->vfl_type == VFL_TYPE_TOUCH) + return vivid_g_fmt_tch_mplane(file, priv, f); + return vidioc_g_fmt_vid_cap_mplane(file, priv, f); +} + +static int vivid_try_fmt_cap_mplane(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct video_device *vdev = video_devdata(file); + + if (vdev->vfl_type == VFL_TYPE_TOUCH) + return vivid_g_fmt_tch_mplane(file, priv, f); + return vidioc_try_fmt_vid_cap_mplane(file, priv, f); +} + +static int vivid_s_fmt_cap_mplane(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct video_device *vdev = video_devdata(file); + + if (vdev->vfl_type == VFL_TYPE_TOUCH) + return vivid_g_fmt_tch_mplane(file, priv, f); + return vidioc_s_fmt_vid_cap_mplane(file, priv, f); +} + +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) + + vivid_is_in_use(&dev->meta_cap_dev) + + vivid_is_in_use(&dev->meta_out_dev) + + vivid_is_in_use(&dev->touch_cap_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); + set_bit(V4L2_FL_REGISTERED, &dev->meta_cap_dev.flags); + set_bit(V4L2_FL_REGISTERED, &dev->meta_out_dev.flags); + set_bit(V4L2_FL_REGISTERED, &dev->touch_cap_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_cap, + .vidioc_g_fmt_vid_cap = vivid_g_fmt_cap, + .vidioc_try_fmt_vid_cap = vivid_try_fmt_cap, + .vidioc_s_fmt_vid_cap = vivid_s_fmt_cap, + .vidioc_g_fmt_vid_cap_mplane = vivid_g_fmt_cap_mplane, + .vidioc_try_fmt_vid_cap_mplane = vivid_try_fmt_cap_mplane, + .vidioc_s_fmt_vid_cap_mplane = vivid_s_fmt_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 = vivid_enum_input, + .vidioc_g_input = vivid_g_input, + .vidioc_s_input = vivid_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, + + .vidioc_enum_fmt_meta_cap = vidioc_enum_fmt_meta_cap, + .vidioc_g_fmt_meta_cap = vidioc_g_fmt_meta_cap, + .vidioc_s_fmt_meta_cap = vidioc_g_fmt_meta_cap, + .vidioc_try_fmt_meta_cap = vidioc_g_fmt_meta_cap, + + .vidioc_enum_fmt_meta_out = vidioc_enum_fmt_meta_out, + .vidioc_g_fmt_meta_out = vidioc_g_fmt_meta_out, + .vidioc_s_fmt_meta_out = vidioc_g_fmt_meta_out, + .vidioc_try_fmt_meta_out = vidioc_g_fmt_meta_out, +}; + +/* ----------------------------------------------------------------- + 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_queue(struct vivid_dev *dev, + struct vb2_queue *q, + u32 buf_type, + unsigned int min_buffers_needed, + const struct vb2_ops *ops) +{ + if (buf_type == V4L2_BUF_TYPE_VIDEO_CAPTURE && dev->multiplanar) + buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + else if (buf_type == V4L2_BUF_TYPE_VIDEO_OUTPUT && dev->multiplanar) + buf_type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + else if (buf_type == V4L2_BUF_TYPE_VBI_CAPTURE && !dev->has_raw_vbi_cap) + buf_type = V4L2_BUF_TYPE_SLICED_VBI_CAPTURE; + else if (buf_type == V4L2_BUF_TYPE_VBI_OUTPUT && !dev->has_raw_vbi_out) + buf_type = V4L2_BUF_TYPE_SLICED_VBI_OUTPUT; + + q->type = buf_type; + q->io_modes = VB2_MMAP | VB2_DMABUF; + q->io_modes |= V4L2_TYPE_IS_OUTPUT(buf_type) ? VB2_WRITE : VB2_READ; + if (allocators[dev->inst] != 1) + q->io_modes |= VB2_USERPTR; + q->drv_priv = dev; + q->buf_struct_size = sizeof(struct vivid_buffer); + q->ops = ops; + q->mem_ops = allocators[dev->inst] == 1 ? &vb2_dma_contig_memops : + &vb2_vmalloc_memops; + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->min_buffers_needed = min_buffers_needed; + q->lock = &dev->mutex; + q->dev = dev->v4l2_dev.dev; + q->supports_requests = true; + + return vb2_queue_init(q); +} + +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; + 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; + unsigned node_type = node_types[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 meta capture device */ + dev->has_meta_cap = node_type & 0x20000; + + /* sanity checks */ + if ((in_type_counter[WEBCAM] || in_type_counter[HDMI]) && + !dev->has_vid_cap && !dev->has_meta_cap) { + v4l2_warn(&dev->v4l2_dev, + "Webcam or HDMI input without video or metadata nodes\n"); + kfree(dev); + return -EINVAL; + } + if ((in_type_counter[TV] || in_type_counter[SVID]) && + !dev->has_vid_cap && !dev->has_vbi_cap && !dev->has_meta_cap) { + v4l2_warn(&dev->v4l2_dev, + "TV or S-Video input without video, VBI or metadata nodes\n"); + kfree(dev); + return -EINVAL; + } + + /* 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 metadata output device */ + dev->has_meta_out = node_type & 0x40000; + + /* sanity checks */ + if (out_type_counter[SVID] && + !dev->has_vid_out && !dev->has_vbi_out && !dev->has_meta_out) { + v4l2_warn(&dev->v4l2_dev, + "S-Video output without video, VBI or metadata nodes\n"); + kfree(dev); + return -EINVAL; + } + if (out_type_counter[HDMI] && !dev->has_vid_out && !dev->has_meta_out) { + v4l2_warn(&dev->v4l2_dev, + "HDMI output without video or metadata nodes\n"); + kfree(dev); + return -EINVAL; + } + + /* 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 TV tuner? */ + dev->has_tv_tuner = in_type_counter[TV]; + + /* 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'); + } + + /* do we create a touch capture device */ + dev->has_touch_cap = node_type & 0x80000; + + /* 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 (dev->has_tv_tuner) + 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 (dev->has_tv_tuner) + 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; + + /* set up the capabilities of meta capture device */ + if (dev->has_meta_cap) { + dev->meta_cap_caps = V4L2_CAP_META_CAPTURE | + V4L2_CAP_STREAMING | V4L2_CAP_READWRITE; + if (dev->has_audio_inputs) + dev->meta_cap_caps |= V4L2_CAP_AUDIO; + if (dev->has_tv_tuner) + dev->meta_cap_caps |= V4L2_CAP_TUNER; + } + /* set up the capabilities of meta output device */ + if (dev->has_meta_out) { + dev->meta_out_caps = V4L2_CAP_META_OUTPUT | + V4L2_CAP_STREAMING | V4L2_CAP_READWRITE; + if (dev->has_audio_outputs) + dev->meta_out_caps |= V4L2_CAP_AUDIO; + } + /* set up the capabilities of the touch capture device */ + if (dev->has_touch_cap) { + dev->touch_cap_caps = V4L2_CAP_TOUCH | V4L2_CAP_STREAMING | + V4L2_CAP_READWRITE; + dev->touch_cap_caps |= dev->multiplanar ? + V4L2_CAP_VIDEO_CAPTURE_MPLANE : V4L2_CAP_VIDEO_CAPTURE; + } + + 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); + v4l2_disable_ioctl(&dev->meta_cap_dev, VIDIOC_S_AUDIO); + v4l2_disable_ioctl(&dev->meta_cap_dev, VIDIOC_G_AUDIO); + v4l2_disable_ioctl(&dev->meta_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); + v4l2_disable_ioctl(&dev->meta_out_dev, VIDIOC_S_AUDOUT); + v4l2_disable_ioctl(&dev->meta_out_dev, VIDIOC_G_AUDOUT); + v4l2_disable_ioctl(&dev->meta_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); + v4l2_disable_ioctl(&dev->meta_cap_dev, VIDIOC_S_FREQUENCY); + v4l2_disable_ioctl(&dev->meta_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); + v4l2_disable_ioctl(&dev->meta_cap_dev, VIDIOC_S_TUNER); + v4l2_disable_ioctl(&dev->meta_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->meta_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); + v4l2_disable_ioctl(&dev->meta_out_dev, VIDIOC_S_FREQUENCY); + v4l2_disable_ioctl(&dev->meta_out_dev, VIDIOC_G_FREQUENCY); + v4l2_disable_ioctl(&dev->touch_cap_dev, VIDIOC_S_PARM); + v4l2_disable_ioctl(&dev->touch_cap_dev, VIDIOC_ENUM_FRAMESIZES); + v4l2_disable_ioctl(&dev->touch_cap_dev, VIDIOC_ENUM_FRAMEINTERVALS); + + /* 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; + + /* update touch configuration */ + dev->timeperframe_tch_cap.numerator = 1; + dev->timeperframe_tch_cap.denominator = 10; + vivid_set_touch(dev, 0); + + /* 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->meta_cap_active); + INIT_LIST_HEAD(&dev->meta_out_active); + INIT_LIST_HEAD(&dev->touch_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 (allocators[inst] == 1) + dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); + + /* start creating the vb2 queues */ + if (dev->has_vid_cap) { + /* initialize vid_cap queue */ + ret = vivid_create_queue(dev, &dev->vb_vid_cap_q, + V4L2_BUF_TYPE_VIDEO_CAPTURE, 2, + &vivid_vid_cap_qops); + if (ret) + goto unreg_dev; + } + + if (dev->has_vid_out) { + /* initialize vid_out queue */ + ret = vivid_create_queue(dev, &dev->vb_vid_out_q, + V4L2_BUF_TYPE_VIDEO_OUTPUT, 2, + &vivid_vid_out_qops); + if (ret) + goto unreg_dev; + } + + if (dev->has_vbi_cap) { + /* initialize vbi_cap queue */ + ret = vivid_create_queue(dev, &dev->vb_vbi_cap_q, + V4L2_BUF_TYPE_VBI_CAPTURE, 2, + &vivid_vbi_cap_qops); + if (ret) + goto unreg_dev; + } + + if (dev->has_vbi_out) { + /* initialize vbi_out queue */ + ret = vivid_create_queue(dev, &dev->vb_vbi_out_q, + V4L2_BUF_TYPE_VBI_OUTPUT, 2, + &vivid_vbi_out_qops); + if (ret) + goto unreg_dev; + } + + if (dev->has_sdr_cap) { + /* initialize sdr_cap queue */ + ret = vivid_create_queue(dev, &dev->vb_sdr_cap_q, + V4L2_BUF_TYPE_SDR_CAPTURE, 8, + &vivid_sdr_cap_qops); + if (ret) + goto unreg_dev; + } + + if (dev->has_meta_cap) { + /* initialize meta_cap queue */ + ret = vivid_create_queue(dev, &dev->vb_meta_cap_q, + V4L2_BUF_TYPE_META_CAPTURE, 2, + &vivid_meta_cap_qops); + if (ret) + goto unreg_dev; + } + + if (dev->has_meta_out) { + /* initialize meta_out queue */ + ret = vivid_create_queue(dev, &dev->vb_meta_out_q, + V4L2_BUF_TYPE_META_OUTPUT, 1, + &vivid_meta_out_qops); + if (ret) + goto unreg_dev; + } + + if (dev->has_touch_cap) { + /* initialize touch_cap queue */ + ret = vivid_create_queue(dev, &dev->vb_touch_cap_q, + V4L2_BUF_TYPE_VIDEO_CAPTURE, 1, + &vivid_touch_cap_qops); + 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); + v4l2_ctrl_handler_setup(&dev->ctrl_hdl_meta_cap); + v4l2_ctrl_handler_setup(&dev->ctrl_hdl_meta_out); + v4l2_ctrl_handler_setup(&dev->ctrl_hdl_touch_cap); + + /* finally start creating the device nodes */ + if (dev->has_vid_cap) { + vfd = &dev->vid_cap_dev; + snprintf(vfd->name, sizeof(vfd->name), + "vivid-%03d-vid-cap", inst); + 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_VIDEO, 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; + snprintf(vfd->name, sizeof(vfd->name), + "vivid-%03d-vid-out", inst); + vfd->vfl_dir = VFL_DIR_TX; + vfd->fops = &vivid_fops; + vfd->ioctl_ops = &vivid_ioctl_ops; + vfd->device_caps = dev->vid_out_caps; + vfd->release = video_device_release_empty; + vfd->v4l2_dev = &dev->v4l2_dev; + vfd->queue = &dev->vb_vid_out_q; + 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 + 1) << 12, false); + else + cec_s_phys_addr(dev->cec_tx_adap[i], 0x1000, false); + } +#endif + + ret = video_register_device(vfd, VFL_TYPE_VIDEO, 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)); + } + + if (dev->has_meta_cap) { + vfd = &dev->meta_cap_dev; + snprintf(vfd->name, sizeof(vfd->name), + "vivid-%03d-meta-cap", inst); + vfd->fops = &vivid_fops; + vfd->ioctl_ops = &vivid_ioctl_ops; + vfd->device_caps = dev->meta_cap_caps; + vfd->release = video_device_release_empty; + vfd->v4l2_dev = &dev->v4l2_dev; + vfd->queue = &dev->vb_meta_cap_q; + vfd->lock = &dev->mutex; + vfd->tvnorms = tvnorms_cap; + video_set_drvdata(vfd, dev); +#ifdef CONFIG_MEDIA_CONTROLLER + dev->meta_cap_pad.flags = MEDIA_PAD_FL_SINK; + ret = media_entity_pads_init(&vfd->entity, 1, + &dev->meta_cap_pad); + if (ret) + goto unreg_dev; +#endif + ret = video_register_device(vfd, VFL_TYPE_VIDEO, + meta_cap_nr[inst]); + if (ret < 0) + goto unreg_dev; + v4l2_info(&dev->v4l2_dev, + "V4L2 metadata capture device registered as %s\n", + video_device_node_name(vfd)); + } + + if (dev->has_meta_out) { + vfd = &dev->meta_out_dev; + snprintf(vfd->name, sizeof(vfd->name), + "vivid-%03d-meta-out", inst); + vfd->vfl_dir = VFL_DIR_TX; + vfd->fops = &vivid_fops; + vfd->ioctl_ops = &vivid_ioctl_ops; + vfd->device_caps = dev->meta_out_caps; + vfd->release = video_device_release_empty; + vfd->v4l2_dev = &dev->v4l2_dev; + vfd->queue = &dev->vb_meta_out_q; + vfd->lock = &dev->mutex; + vfd->tvnorms = tvnorms_out; + video_set_drvdata(vfd, dev); +#ifdef CONFIG_MEDIA_CONTROLLER + dev->meta_out_pad.flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_pads_init(&vfd->entity, 1, + &dev->meta_out_pad); + if (ret) + goto unreg_dev; +#endif + ret = video_register_device(vfd, VFL_TYPE_VIDEO, + meta_out_nr[inst]); + if (ret < 0) + goto unreg_dev; + v4l2_info(&dev->v4l2_dev, + "V4L2 metadata output device registered as %s\n", + video_device_node_name(vfd)); + } + + if (dev->has_touch_cap) { + vfd = &dev->touch_cap_dev; + snprintf(vfd->name, sizeof(vfd->name), + "vivid-%03d-touch-cap", inst); + vfd->fops = &vivid_fops; + vfd->ioctl_ops = &vivid_ioctl_ops; + vfd->device_caps = dev->touch_cap_caps; + vfd->release = video_device_release_empty; + vfd->v4l2_dev = &dev->v4l2_dev; + vfd->queue = &dev->vb_touch_cap_q; + vfd->tvnorms = tvnorms_cap; + vfd->lock = &dev->mutex; + video_set_drvdata(vfd, dev); +#ifdef CONFIG_MEDIA_CONTROLLER + dev->touch_cap_pad.flags = MEDIA_PAD_FL_SINK; + ret = media_entity_pads_init(&vfd->entity, 1, + &dev->touch_cap_pad); + if (ret) + goto unreg_dev; +#endif + ret = video_register_device(vfd, VFL_TYPE_TOUCH, + touch_cap_nr[inst]); + if (ret < 0) + goto unreg_dev; + v4l2_info(&dev->v4l2_dev, + "V4L2 touch capture 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->touch_cap_dev); + video_unregister_device(&dev->meta_out_dev); + video_unregister_device(&dev->meta_cap_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); + } + if (dev->has_meta_cap) { + v4l2_info(&dev->v4l2_dev, "unregistering %s\n", + video_device_node_name(&dev->meta_cap_dev)); + video_unregister_device(&dev->meta_cap_dev); + } + if (dev->has_meta_out) { + v4l2_info(&dev->v4l2_dev, "unregistering %s\n", + video_device_node_name(&dev->meta_out_dev)); + video_unregister_device(&dev->meta_out_dev); + } + if (dev->has_touch_cap) { + v4l2_info(&dev->v4l2_dev, "unregistering %s\n", + video_device_node_name(&dev->touch_cap_dev)); + video_unregister_device(&dev->touch_cap_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/test_drivers/vivid/vivid-core.h b/drivers/media/test_drivers/vivid/vivid-core.h new file mode 100644 index 000000000000..99e69b8f770f --- /dev/null +++ b/drivers/media/test_drivers/vivid/vivid-core.h @@ -0,0 +1,612 @@ +/* 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 +#include +#include +#include +#include +#include +#include +#include +#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; + struct media_pad meta_cap_pad; + struct media_pad meta_out_pad; + struct media_pad touch_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; + struct video_device meta_cap_dev; + struct v4l2_ctrl_handler ctrl_hdl_meta_cap; + struct video_device meta_out_dev; + struct v4l2_ctrl_handler ctrl_hdl_meta_out; + struct video_device touch_cap_dev; + struct v4l2_ctrl_handler ctrl_hdl_touch_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; + u32 meta_cap_caps; + u32 meta_out_caps; + u32 touch_cap_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 has_meta_cap; + bool has_meta_out; + bool has_tv_tuner; + bool has_touch_cap; + + 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; + struct vb2_queue vb_meta_cap_q; + struct list_head meta_cap_active; + struct vb2_queue vb_touch_cap_q; + struct list_head touch_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; + u32 meta_cap_seq_start; + u32 meta_cap_seq_count; + bool meta_cap_streaming; + + /* Touch capture */ + struct task_struct *kthread_touch_cap; + unsigned long jiffies_touch_cap; + u64 touch_cap_stream_start; + u32 touch_cap_seq_offset; + bool touch_cap_seq_resync; + u32 touch_cap_seq_start; + u32 touch_cap_seq_count; + bool touch_cap_streaming; + struct v4l2_fract timeperframe_tch_cap; + struct v4l2_pix_format tch_format; + int tch_pat_random; + + /* 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; + struct vb2_queue vb_meta_out_q; + struct list_head meta_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; + u32 meta_out_seq_start; + u32 meta_out_seq_count; + bool meta_out_streaming; + + /* 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; + + bool meta_pts; + bool meta_scr; +}; + +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/test_drivers/vivid/vivid-ctrls.c b/drivers/media/test_drivers/vivid/vivid-ctrls.c new file mode 100644 index 000000000000..334130568dcb --- /dev/null +++ b/drivers/media/test_drivers/vivid/vivid-ctrls.c @@ -0,0 +1,1939 @@ +// 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 +#include +#include +#include +#include + +#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_AREA (VIVID_CID_CUSTOM_BASE + 11) + +#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) + +#define VIVID_CID_META_CAP_GENERATE_PTS (VIVID_CID_VIVID_BASE + 111) +#define VIVID_CID_META_CAP_GENERATE_SCR (VIVID_CID_VIVID_BASE + 112) + +/* 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); + clear_bit(V4L2_FL_REGISTERED, &dev->meta_cap_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, +}; + +static const struct v4l2_area area = { + .width = 1000, + .height = 2000, +}; + +static const struct v4l2_ctrl_config vivid_ctrl_area = { + .ops = &vivid_user_gen_ctrl_ops, + .id = VIVID_CID_AREA, + .name = "Area", + .type = V4L2_CTRL_TYPE_AREA, + .p_def.p_const = &area, +}; + +/* 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, +}; + +/* Metadata Capture Control */ + +static int vivid_meta_cap_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct vivid_dev *dev = container_of(ctrl->handler, struct vivid_dev, + ctrl_hdl_meta_cap); + + switch (ctrl->id) { + case VIVID_CID_META_CAP_GENERATE_PTS: + dev->meta_pts = ctrl->val; + break; + case VIVID_CID_META_CAP_GENERATE_SCR: + dev->meta_scr = ctrl->val; + break; + } + return 0; +} + +static const struct v4l2_ctrl_ops vivid_meta_cap_ctrl_ops = { + .s_ctrl = vivid_meta_cap_s_ctrl, +}; + +static const struct v4l2_ctrl_config vivid_ctrl_meta_has_pts = { + .ops = &vivid_meta_cap_ctrl_ops, + .id = VIVID_CID_META_CAP_GENERATE_PTS, + .name = "Generate PTS", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .max = 1, + .def = 1, + .step = 1, +}; + +static const struct v4l2_ctrl_config vivid_ctrl_meta_has_src_clk = { + .ops = &vivid_meta_cap_ctrl_ops, + .id = VIVID_CID_META_CAP_GENERATE_SCR, + .name = "Generate SCR", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .max = 1, + .def = 1, + .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_handler *hdl_meta_cap = &dev->ctrl_hdl_meta_cap; + struct v4l2_ctrl_handler *hdl_meta_out = &dev->ctrl_hdl_meta_out; + struct v4l2_ctrl_handler *hdl_tch_cap = &dev->ctrl_hdl_touch_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); + v4l2_ctrl_handler_init(hdl_meta_cap, 2); + v4l2_ctrl_new_custom(hdl_meta_cap, &vivid_ctrl_class, NULL); + v4l2_ctrl_handler_init(hdl_meta_out, 2); + v4l2_ctrl_new_custom(hdl_meta_out, &vivid_ctrl_class, NULL); + v4l2_ctrl_handler_init(hdl_tch_cap, 2); + v4l2_ctrl_new_custom(hdl_tch_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_area, 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 (dev->has_meta_cap) { + v4l2_ctrl_new_custom(hdl_meta_cap, + &vivid_ctrl_meta_has_pts, NULL); + v4l2_ctrl_new_custom(hdl_meta_cap, + &vivid_ctrl_meta_has_src_clk, 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; + } + if (dev->has_meta_cap) { + v4l2_ctrl_add_handler(hdl_meta_cap, hdl_user_gen, NULL, false); + v4l2_ctrl_add_handler(hdl_meta_cap, hdl_streaming, NULL, false); + if (hdl_meta_cap->error) + return hdl_meta_cap->error; + dev->meta_cap_dev.ctrl_handler = hdl_meta_cap; + } + if (dev->has_meta_out) { + v4l2_ctrl_add_handler(hdl_meta_out, hdl_user_gen, NULL, false); + v4l2_ctrl_add_handler(hdl_meta_out, hdl_streaming, NULL, false); + if (hdl_meta_out->error) + return hdl_meta_out->error; + dev->meta_out_dev.ctrl_handler = hdl_meta_out; + } + if (dev->has_touch_cap) { + v4l2_ctrl_add_handler(hdl_tch_cap, hdl_user_gen, NULL, false); + v4l2_ctrl_add_handler(hdl_tch_cap, hdl_streaming, NULL, false); + if (hdl_tch_cap->error) + return hdl_tch_cap->error; + dev->touch_cap_dev.ctrl_handler = hdl_tch_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); + v4l2_ctrl_handler_free(&dev->ctrl_hdl_meta_cap); + v4l2_ctrl_handler_free(&dev->ctrl_hdl_meta_out); + v4l2_ctrl_handler_free(&dev->ctrl_hdl_touch_cap); +} diff --git a/drivers/media/test_drivers/vivid/vivid-ctrls.h b/drivers/media/test_drivers/vivid/vivid-ctrls.h new file mode 100644 index 000000000000..6fad5f5d0054 --- /dev/null +++ b/drivers/media/test_drivers/vivid/vivid-ctrls.h @@ -0,0 +1,22 @@ +/* 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/test_drivers/vivid/vivid-kthread-cap.c b/drivers/media/test_drivers/vivid/vivid-kthread-cap.c new file mode 100644 index 000000000000..01a9d671b947 --- /dev/null +++ b/drivers/media/test_drivers/vivid/vivid-kthread-cap.c @@ -0,0 +1,1007 @@ +// 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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" +#include "vivid-meta-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; + struct vivid_buffer *meta_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); + } + } + if (!list_empty(&dev->meta_cap_active)) { + meta_cap_buf = list_entry(dev->meta_cap_active.next, + struct vivid_buffer, list); + list_del(&meta_cap_buf->list); + } + + spin_unlock(&dev->slock); + + if (!vid_cap_buf && !vbi_cap_buf && !meta_cap_buf) + goto update_mv; + + f_time = dev->cap_frame_period * dev->vid_cap_seq_count + + dev->cap_stream_start + dev->time_wrap_offset; + + 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 (!dev->tstamp_src_is_soe) + vid_cap_buf->vb.vb2_buf.timestamp += dev->cap_frame_eof_offset; + } + + 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 + dev->cap_frame_eof_offset + vbi_period; + } + + if (meta_cap_buf) { + v4l2_ctrl_request_setup(meta_cap_buf->vb.vb2_buf.req_obj.req, + &dev->ctrl_hdl_meta_cap); + vivid_meta_cap_fillbuff(dev, meta_cap_buf, f_time); + v4l2_ctrl_request_complete(meta_cap_buf->vb.vb2_buf.req_obj.req, + &dev->ctrl_hdl_meta_cap); + vb2_buffer_done(&meta_cap_buf->vb.vb2_buf, dev->dqbuf_error ? + VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE); + dprintk(dev, 2, "meta_cap %d done\n", + meta_cap_buf->vb.vb2_buf.index); + meta_cap_buf->vb.vb2_buf.timestamp = f_time + dev->cap_frame_eof_offset; + } + + 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; + dev->meta_cap_seq_count = dev->cap_seq_count - dev->meta_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 if (pstreaming == &dev->vbi_cap_streaming) + dev->vbi_cap_seq_start = seq_count; + else + dev->meta_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->meta_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 (pstreaming == &dev->meta_cap_streaming) { + while (!list_empty(&dev->meta_cap_active)) { + struct vivid_buffer *buf; + + buf = list_entry(dev->meta_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_meta_cap); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); + dprintk(dev, 2, "meta_cap buffer %d done\n", + buf->vb.vb2_buf.index); + } + } + + if (dev->vid_cap_streaming || dev->vbi_cap_streaming || + dev->meta_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/test_drivers/vivid/vivid-kthread-cap.h b/drivers/media/test_drivers/vivid/vivid-kthread-cap.h new file mode 100644 index 000000000000..0f43015306d6 --- /dev/null +++ b/drivers/media/test_drivers/vivid/vivid-kthread-cap.h @@ -0,0 +1,14 @@ +/* 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/test_drivers/vivid/vivid-kthread-out.c b/drivers/media/test_drivers/vivid/vivid-kthread-out.c new file mode 100644 index 000000000000..6780687978f9 --- /dev/null +++ b/drivers/media/test_drivers/vivid/vivid-kthread-out.c @@ -0,0 +1,353 @@ +// 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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" +#include "vivid-meta-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; + struct vivid_buffer *meta_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); + } + if (!list_empty(&dev->meta_out_active)) { + meta_out_buf = list_entry(dev->meta_out_active.next, + struct vivid_buffer, list); + list_del(&meta_out_buf->list); + } + spin_unlock(&dev->slock); + + if (!vid_out_buf && !vbi_out_buf && !meta_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); + } + if (meta_out_buf) { + v4l2_ctrl_request_setup(meta_out_buf->vb.vb2_buf.req_obj.req, + &dev->ctrl_hdl_meta_out); + v4l2_ctrl_request_complete(meta_out_buf->vb.vb2_buf.req_obj.req, + &dev->ctrl_hdl_meta_out); + vivid_meta_out_process(dev, meta_out_buf); + meta_out_buf->vb.sequence = dev->meta_out_seq_count; + meta_out_buf->vb.vb2_buf.timestamp = + ktime_get_ns() + dev->time_wrap_offset; + vb2_buffer_done(&meta_out_buf->vb.vb2_buf, dev->dqbuf_error ? + VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE); + dprintk(dev, 2, "meta_out buffer %d done\n", + meta_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->meta_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; + dev->meta_out_seq_count = dev->out_seq_count - dev->meta_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 if (pstreaming == &dev->vbi_out_streaming) + dev->vbi_out_seq_start = seq_count; + else + dev->meta_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->meta_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 (pstreaming == &dev->meta_out_streaming) { + while (!list_empty(&dev->meta_out_active)) { + struct vivid_buffer *buf; + + buf = list_entry(dev->meta_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_meta_out); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); + dprintk(dev, 2, "meta_out buffer %d done\n", + buf->vb.vb2_buf.index); + } + } + + if (dev->vid_out_streaming || dev->vbi_out_streaming || + dev->meta_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/test_drivers/vivid/vivid-kthread-out.h b/drivers/media/test_drivers/vivid/vivid-kthread-out.h new file mode 100644 index 000000000000..d5bcf44bbaca --- /dev/null +++ b/drivers/media/test_drivers/vivid/vivid-kthread-out.h @@ -0,0 +1,14 @@ +/* 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/test_drivers/vivid/vivid-kthread-touch.c b/drivers/media/test_drivers/vivid/vivid-kthread-touch.c new file mode 100644 index 000000000000..674507b5ccb5 --- /dev/null +++ b/drivers/media/test_drivers/vivid/vivid-kthread-touch.c @@ -0,0 +1,181 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * vivid-kthread-touch.c - touch capture thread support functions. + * + */ + +#include +#include "vivid-core.h" +#include "vivid-kthread-touch.h" +#include "vivid-touch-cap.h" + +static noinline_for_stack void vivid_thread_tch_cap_tick(struct vivid_dev *dev, + int dropped_bufs) +{ + struct vivid_buffer *tch_cap_buf = NULL; + + spin_lock(&dev->slock); + if (!list_empty(&dev->touch_cap_active)) { + tch_cap_buf = list_entry(dev->touch_cap_active.next, + struct vivid_buffer, list); + list_del(&tch_cap_buf->list); + } + + spin_unlock(&dev->slock); + + if (tch_cap_buf) { + v4l2_ctrl_request_setup(tch_cap_buf->vb.vb2_buf.req_obj.req, + &dev->ctrl_hdl_touch_cap); + + vivid_fillbuff_tch(dev, tch_cap_buf); + v4l2_ctrl_request_complete(tch_cap_buf->vb.vb2_buf.req_obj.req, + &dev->ctrl_hdl_touch_cap); + vb2_buffer_done(&tch_cap_buf->vb.vb2_buf, dev->dqbuf_error ? + VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE); + dprintk(dev, 2, "touch_cap buffer %d done\n", + tch_cap_buf->vb.vb2_buf.index); + + tch_cap_buf->vb.vb2_buf.timestamp = ktime_get_ns() + dev->time_wrap_offset; + } + dev->dqbuf_error = false; +} + +static int vivid_thread_touch_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 int wait_jiffies; + unsigned int numerator; + unsigned int denominator; + int dropped_bufs; + + dprintk(dev, 1, "Touch Capture Thread Start\n"); + + set_freezable(); + + /* Resets frame counters */ + dev->touch_cap_seq_offset = 0; + dev->touch_cap_seq_count = 0; + dev->touch_cap_seq_resync = false; + dev->jiffies_touch_cap = jiffies; + + for (;;) { + try_to_freeze(); + if (kthread_should_stop()) + break; + + if (!mutex_trylock(&dev->mutex)) { + schedule_timeout_uninterruptible(1); + continue; + } + cur_jiffies = jiffies; + if (dev->touch_cap_seq_resync) { + dev->jiffies_touch_cap = cur_jiffies; + dev->touch_cap_seq_offset = dev->touch_cap_seq_count + 1; + dev->touch_cap_seq_count = 0; + dev->cap_seq_resync = false; + } + denominator = dev->timeperframe_tch_cap.denominator; + numerator = dev->timeperframe_tch_cap.numerator; + + /* Calculate the number of jiffies since we started streaming */ + jiffies_since_start = cur_jiffies - dev->jiffies_touch_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_touch_cap = cur_jiffies; + dev->cap_seq_offset = buffers_since_start; + buffers_since_start = 0; + } + dropped_bufs = buffers_since_start + dev->touch_cap_seq_offset - dev->touch_cap_seq_count; + dev->touch_cap_seq_count = buffers_since_start + dev->touch_cap_seq_offset; + + vivid_thread_tch_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_touch_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, "Touch Capture Thread End\n"); + return 0; +} + +int vivid_start_generating_touch_cap(struct vivid_dev *dev) +{ + if (dev->kthread_touch_cap) { + dev->touch_cap_streaming = true; + return 0; + } + + dev->kthread_touch_cap = kthread_run(vivid_thread_touch_cap, dev, + "%s-tch-cap", dev->v4l2_dev.name); + + if (IS_ERR(dev->kthread_touch_cap)) { + int err = PTR_ERR(dev->kthread_touch_cap); + + dev->kthread_touch_cap = NULL; + v4l2_err(&dev->v4l2_dev, "kernel_thread() failed\n"); + return err; + } + dev->touch_cap_streaming = true; + dprintk(dev, 1, "returning from %s\n", __func__); + return 0; +} + +void vivid_stop_generating_touch_cap(struct vivid_dev *dev) +{ + if (!dev->kthread_touch_cap) + return; + + dev->touch_cap_streaming = false; + + while (!list_empty(&dev->touch_cap_active)) { + struct vivid_buffer *buf; + + buf = list_entry(dev->touch_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_touch_cap); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); + dprintk(dev, 2, "touch_cap buffer %d done\n", + buf->vb.vb2_buf.index); + } + + kthread_stop(dev->kthread_touch_cap); + dev->kthread_touch_cap = NULL; +} diff --git a/drivers/media/test_drivers/vivid/vivid-kthread-touch.h b/drivers/media/test_drivers/vivid/vivid-kthread-touch.h new file mode 100644 index 000000000000..ecf79b46209e --- /dev/null +++ b/drivers/media/test_drivers/vivid/vivid-kthread-touch.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * vivid-kthread-cap.h - video/vbi capture thread support functions. + * + */ + +#ifndef _VIVID_KTHREAD_CAP_H_ +#define _VIVID_KTHREAD_CAP_H_ + +int vivid_start_generating_touch_cap(struct vivid_dev *dev); +void vivid_stop_generating_touch_cap(struct vivid_dev *dev); + +#endif diff --git a/drivers/media/test_drivers/vivid/vivid-meta-cap.c b/drivers/media/test_drivers/vivid/vivid-meta-cap.c new file mode 100644 index 000000000000..780f96860a6d --- /dev/null +++ b/drivers/media/test_drivers/vivid/vivid-meta-cap.c @@ -0,0 +1,201 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * vivid-meta-cap.c - meta capture support functions. + */ + +#include +#include +#include +#include +#include + +#include "vivid-core.h" +#include "vivid-kthread-cap.h" +#include "vivid-meta-cap.h" + +static int meta_cap_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, + unsigned int *nplanes, unsigned int sizes[], + struct device *alloc_devs[]) +{ + struct vivid_dev *dev = vb2_get_drv_priv(vq); + unsigned int size = sizeof(struct vivid_uvc_meta_buf); + + if (!vivid_is_webcam(dev)) + return -EINVAL; + + if (*nplanes) { + if (sizes[0] < size) + return -EINVAL; + } else { + sizes[0] = size; + } + + if (vq->num_buffers + *nbuffers < 2) + *nbuffers = 2 - vq->num_buffers; + + *nplanes = 1; + return 0; +} + +static int meta_cap_buf_prepare(struct vb2_buffer *vb) +{ + struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue); + unsigned int size = sizeof(struct vivid_uvc_meta_buf); + + 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 meta_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->meta_cap_active); + spin_unlock(&dev->slock); +} + +static int meta_cap_start_streaming(struct vb2_queue *vq, unsigned int count) +{ + struct vivid_dev *dev = vb2_get_drv_priv(vq); + int err; + + dprintk(dev, 1, "%s\n", __func__); + dev->meta_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->meta_cap_streaming); + } + if (err) { + struct vivid_buffer *buf, *tmp; + + list_for_each_entry_safe(buf, tmp, + &dev->meta_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 meta_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->meta_cap_streaming); +} + +static void meta_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_meta_cap); +} + +const struct vb2_ops vivid_meta_cap_qops = { + .queue_setup = meta_cap_queue_setup, + .buf_prepare = meta_cap_buf_prepare, + .buf_queue = meta_cap_buf_queue, + .start_streaming = meta_cap_start_streaming, + .stop_streaming = meta_cap_stop_streaming, + .buf_request_complete = meta_cap_buf_request_complete, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +int vidioc_enum_fmt_meta_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + struct vivid_dev *dev = video_drvdata(file); + + if (!vivid_is_webcam(dev)) + return -EINVAL; + + if (f->index > 0) + return -EINVAL; + + f->type = V4L2_BUF_TYPE_META_CAPTURE; + f->pixelformat = V4L2_META_FMT_UVC; + return 0; +} + +int vidioc_g_fmt_meta_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct vivid_dev *dev = video_drvdata(file); + struct v4l2_meta_format *meta = &f->fmt.meta; + + if (!vivid_is_webcam(dev) || !dev->has_meta_cap) + return -EINVAL; + + meta->dataformat = V4L2_META_FMT_UVC; + meta->buffersize = sizeof(struct vivid_uvc_meta_buf); + return 0; +} + +void vivid_meta_cap_fillbuff(struct vivid_dev *dev, + struct vivid_buffer *buf, u64 soe) +{ + struct vivid_uvc_meta_buf *meta = vb2_plane_vaddr(&buf->vb.vb2_buf, 0); + int buf_off = 0; + + buf->vb.sequence = dev->meta_cap_seq_count; + if (dev->field_cap == V4L2_FIELD_ALTERNATE) + buf->vb.sequence /= 2; + memset(meta, 1, vb2_plane_size(&buf->vb.vb2_buf, 0)); + + meta->ns = ktime_get_ns(); + meta->sof = buf->vb.sequence * 30; + meta->length = sizeof(*meta) - offsetof(struct vivid_uvc_meta_buf, length); + meta->flags = UVC_STREAM_EOH | UVC_STREAM_EOF; + + if ((buf->vb.sequence % 2) == 0) + meta->flags |= UVC_STREAM_FID; + + dprintk(dev, 2, "%s ns:%llu sof:%4d len:%u flags: 0x%02x", + __func__, meta->ns, meta->sof, meta->length, meta->flags); + if (dev->meta_pts) { + meta->flags |= UVC_STREAM_PTS; + meta->buf[0] = div_u64(soe, VIVID_META_CLOCK_UNIT); + buf_off = 4; + dprintk(dev, 2, " pts: %u\n", *(__u32 *)(meta->buf)); + } + + if (dev->meta_scr) { + meta->flags |= UVC_STREAM_SCR; + meta->buf[buf_off] = div_u64((soe + dev->cap_frame_eof_offset), + VIVID_META_CLOCK_UNIT); + + meta->buf[buf_off + 4] = (buf->vb.sequence * 30) % 1000; + dprintk(dev, 2, " stc: %u, sof counter: %u\n", + *(__u32 *)(meta->buf + buf_off), + *(__u16 *)(meta->buf + buf_off + 4)); + } + dprintk(dev, 2, "\n"); +} diff --git a/drivers/media/test_drivers/vivid/vivid-meta-cap.h b/drivers/media/test_drivers/vivid/vivid-meta-cap.h new file mode 100644 index 000000000000..4670d00d1576 --- /dev/null +++ b/drivers/media/test_drivers/vivid/vivid-meta-cap.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * vivid-meta-cap.h - meta capture support functions. + */ +#ifndef _VIVID_META_CAP_H_ +#define _VIVID_META_CAP_H_ + +#define VIVID_META_CLOCK_UNIT 10 /* 100 MHz */ + +struct vivid_uvc_meta_buf { + __u64 ns; + __u16 sof; + __u8 length; + __u8 flags; + __u8 buf[10]; /* PTS(4)+STC(4)+SOF(2) */ +} __packed; + +void vivid_meta_cap_fillbuff(struct vivid_dev *dev, + struct vivid_buffer *buf, u64 soe); + +int vidioc_enum_fmt_meta_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f); + +int vidioc_g_fmt_meta_cap(struct file *file, void *priv, + struct v4l2_format *f); + +extern const struct vb2_ops vivid_meta_cap_qops; + +#endif diff --git a/drivers/media/test_drivers/vivid/vivid-meta-out.c b/drivers/media/test_drivers/vivid/vivid-meta-out.c new file mode 100644 index 000000000000..ff8a039aba72 --- /dev/null +++ b/drivers/media/test_drivers/vivid/vivid-meta-out.c @@ -0,0 +1,174 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * vivid-meta-out.c - meta output support functions. + */ + +#include +#include +#include +#include +#include + +#include "vivid-core.h" +#include "vivid-kthread-out.h" +#include "vivid-meta-out.h" + +static int meta_out_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, + unsigned int *nplanes, unsigned int sizes[], + struct device *alloc_devs[]) +{ + struct vivid_dev *dev = vb2_get_drv_priv(vq); + unsigned int size = sizeof(struct vivid_meta_out_buf); + + if (!vivid_is_webcam(dev)) + return -EINVAL; + + if (*nplanes) { + if (sizes[0] < size) + return -EINVAL; + } else { + sizes[0] = size; + } + + if (vq->num_buffers + *nbuffers < 2) + *nbuffers = 2 - vq->num_buffers; + + *nplanes = 1; + return 0; +} + +static int meta_out_buf_prepare(struct vb2_buffer *vb) +{ + struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue); + unsigned int size = sizeof(struct vivid_meta_out_buf); + + 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 meta_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->meta_out_active); + spin_unlock(&dev->slock); +} + +static int meta_out_start_streaming(struct vb2_queue *vq, unsigned int count) +{ + struct vivid_dev *dev = vb2_get_drv_priv(vq); + int err; + + dprintk(dev, 1, "%s\n", __func__); + dev->meta_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->meta_out_streaming); + } + if (err) { + struct vivid_buffer *buf, *tmp; + + list_for_each_entry_safe(buf, tmp, + &dev->meta_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 meta_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->meta_out_streaming); +} + +static void meta_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_meta_out); +} + +const struct vb2_ops vivid_meta_out_qops = { + .queue_setup = meta_out_queue_setup, + .buf_prepare = meta_out_buf_prepare, + .buf_queue = meta_out_buf_queue, + .start_streaming = meta_out_start_streaming, + .stop_streaming = meta_out_stop_streaming, + .buf_request_complete = meta_out_buf_request_complete, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +int vidioc_enum_fmt_meta_out(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + struct vivid_dev *dev = video_drvdata(file); + + if (!vivid_is_webcam(dev)) + return -EINVAL; + + if (f->index > 0) + return -EINVAL; + + f->type = V4L2_BUF_TYPE_META_OUTPUT; + f->pixelformat = V4L2_META_FMT_VIVID; + return 0; +} + +int vidioc_g_fmt_meta_out(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct vivid_dev *dev = video_drvdata(file); + struct v4l2_meta_format *meta = &f->fmt.meta; + + if (!vivid_is_webcam(dev) || !dev->has_meta_out) + return -EINVAL; + + meta->dataformat = V4L2_META_FMT_VIVID; + meta->buffersize = sizeof(struct vivid_meta_out_buf); + return 0; +} + +void vivid_meta_out_process(struct vivid_dev *dev, + struct vivid_buffer *buf) +{ + struct vivid_meta_out_buf *meta = vb2_plane_vaddr(&buf->vb.vb2_buf, 0); + + tpg_s_brightness(&dev->tpg, meta->brightness); + tpg_s_contrast(&dev->tpg, meta->contrast); + tpg_s_saturation(&dev->tpg, meta->saturation); + tpg_s_hue(&dev->tpg, meta->hue); + dprintk(dev, 2, " %s brightness %u contrast %u saturation %u hue %d\n", + __func__, meta->brightness, meta->contrast, + meta->saturation, meta->hue); +} diff --git a/drivers/media/test_drivers/vivid/vivid-meta-out.h b/drivers/media/test_drivers/vivid/vivid-meta-out.h new file mode 100644 index 000000000000..0c639b7c2842 --- /dev/null +++ b/drivers/media/test_drivers/vivid/vivid-meta-out.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * vivid-meta-out.h - meta output support functions. + */ +#ifndef _VIVID_META_OUT_H_ +#define _VIVID_META_OUT_H_ + +struct vivid_meta_out_buf { + u16 brightness; + u16 contrast; + u16 saturation; + s16 hue; +}; + +void vivid_meta_out_process(struct vivid_dev *dev, struct vivid_buffer *buf); +int vidioc_enum_fmt_meta_out(struct file *file, void *priv, + struct v4l2_fmtdesc *f); +int vidioc_g_fmt_meta_out(struct file *file, void *priv, + struct v4l2_format *f); +int vidioc_s_fmt_meta_out(struct file *file, void *priv, + struct v4l2_format *f); + +extern const struct vb2_ops vivid_meta_out_qops; + +#endif diff --git a/drivers/media/test_drivers/vivid/vivid-osd.c b/drivers/media/test_drivers/vivid/vivid-osd.c new file mode 100644 index 000000000000..fbaec8acc161 --- /dev/null +++ b/drivers/media/test_drivers/vivid/vivid-osd.c @@ -0,0 +1,388 @@ +// 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 const 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.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/test_drivers/vivid/vivid-osd.h b/drivers/media/test_drivers/vivid/vivid-osd.h new file mode 100644 index 000000000000..f9ac1af25dd3 --- /dev/null +++ b/drivers/media/test_drivers/vivid/vivid-osd.h @@ -0,0 +1,15 @@ +/* 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/test_drivers/vivid/vivid-radio-common.c b/drivers/media/test_drivers/vivid/vivid-radio-common.c new file mode 100644 index 000000000000..138c7bce68b1 --- /dev/null +++ b/drivers/media/test_drivers/vivid/vivid-radio-common.c @@ -0,0 +1,177 @@ +// 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 +#include +#include +#include + +#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/test_drivers/vivid/vivid-radio-common.h b/drivers/media/test_drivers/vivid/vivid-radio-common.h new file mode 100644 index 000000000000..30a9900e5b2b --- /dev/null +++ b/drivers/media/test_drivers/vivid/vivid-radio-common.h @@ -0,0 +1,28 @@ +/* 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/test_drivers/vivid/vivid-radio-rx.c b/drivers/media/test_drivers/vivid/vivid-radio-rx.c new file mode 100644 index 000000000000..232cab508f48 --- /dev/null +++ b/drivers/media/test_drivers/vivid/vivid-radio-rx.c @@ -0,0 +1,278 @@ +// 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 +#include +#include +#include +#include +#include + +#include +#include +#include + +#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/test_drivers/vivid/vivid-radio-rx.h b/drivers/media/test_drivers/vivid/vivid-radio-rx.h new file mode 100644 index 000000000000..c9c7849f6f99 --- /dev/null +++ b/drivers/media/test_drivers/vivid/vivid-radio-rx.h @@ -0,0 +1,19 @@ +/* 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/test_drivers/vivid/vivid-radio-tx.c b/drivers/media/test_drivers/vivid/vivid-radio-tx.c new file mode 100644 index 000000000000..049d40b948bb --- /dev/null +++ b/drivers/media/test_drivers/vivid/vivid-radio-tx.c @@ -0,0 +1,128 @@ +// 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#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/test_drivers/vivid/vivid-radio-tx.h b/drivers/media/test_drivers/vivid/vivid-radio-tx.h new file mode 100644 index 000000000000..c2bf1e7e634a --- /dev/null +++ b/drivers/media/test_drivers/vivid/vivid-radio-tx.h @@ -0,0 +1,17 @@ +/* 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/test_drivers/vivid/vivid-rds-gen.c b/drivers/media/test_drivers/vivid/vivid-rds-gen.c new file mode 100644 index 000000000000..b5b104ee64c9 --- /dev/null +++ b/drivers/media/test_drivers/vivid/vivid-rds-gen.c @@ -0,0 +1,157 @@ +// 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 +#include +#include +#include + +#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/test_drivers/vivid/vivid-rds-gen.h b/drivers/media/test_drivers/vivid/vivid-rds-gen.h new file mode 100644 index 000000000000..35ac5742302b --- /dev/null +++ b/drivers/media/test_drivers/vivid/vivid-rds-gen.h @@ -0,0 +1,42 @@ +/* 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/test_drivers/vivid/vivid-sdr-cap.c b/drivers/media/test_drivers/vivid/vivid-sdr-cap.c new file mode 100644 index 000000000000..2b7522e16efc --- /dev/null +++ b/drivers/media/test_drivers/vivid/vivid-sdr-cap.c @@ -0,0 +1,570 @@ +// 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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/test_drivers/vivid/vivid-sdr-cap.h b/drivers/media/test_drivers/vivid/vivid-sdr-cap.h new file mode 100644 index 000000000000..813c9248e5a7 --- /dev/null +++ b/drivers/media/test_drivers/vivid/vivid-sdr-cap.h @@ -0,0 +1,24 @@ +/* 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/test_drivers/vivid/vivid-touch-cap.c b/drivers/media/test_drivers/vivid/vivid-touch-cap.c new file mode 100644 index 000000000000..ebb00b128030 --- /dev/null +++ b/drivers/media/test_drivers/vivid/vivid-touch-cap.c @@ -0,0 +1,341 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * vivid-touch-cap.c - touch support functions. + */ + +#include "vivid-core.h" +#include "vivid-kthread-touch.h" +#include "vivid-vid-common.h" +#include "vivid-touch-cap.h" + +static int touch_cap_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, + unsigned int *nplanes, unsigned int sizes[], + struct device *alloc_devs[]) +{ + struct vivid_dev *dev = vb2_get_drv_priv(vq); + struct v4l2_pix_format *f = &dev->tch_format; + unsigned int size = f->sizeimage; + + if (*nplanes) { + if (sizes[0] < size) + return -EINVAL; + } else { + sizes[0] = size; + } + + if (vq->num_buffers + *nbuffers < 2) + *nbuffers = 2 - vq->num_buffers; + + *nplanes = 1; + return 0; +} + +static int touch_cap_buf_prepare(struct vb2_buffer *vb) +{ + struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue); + struct v4l2_pix_format *f = &dev->tch_format; + unsigned int size = f->sizeimage; + + 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 touch_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); + + vbuf->field = V4L2_FIELD_NONE; + spin_lock(&dev->slock); + list_add_tail(&buf->list, &dev->touch_cap_active); + spin_unlock(&dev->slock); +} + +static int touch_cap_start_streaming(struct vb2_queue *vq, unsigned int count) +{ + struct vivid_dev *dev = vb2_get_drv_priv(vq); + int err; + + dev->touch_cap_seq_count = 0; + if (dev->start_streaming_error) { + dev->start_streaming_error = false; + err = -EINVAL; + } else { + err = vivid_start_generating_touch_cap(dev); + } + if (err) { + struct vivid_buffer *buf, *tmp; + + list_for_each_entry_safe(buf, tmp, + &dev->touch_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 touch_cap_stop_streaming(struct vb2_queue *vq) +{ + struct vivid_dev *dev = vb2_get_drv_priv(vq); + + vivid_stop_generating_touch_cap(dev); +} + +static void touch_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_touch_cap); +} + +const struct vb2_ops vivid_touch_cap_qops = { + .queue_setup = touch_cap_queue_setup, + .buf_prepare = touch_cap_buf_prepare, + .buf_queue = touch_cap_buf_queue, + .start_streaming = touch_cap_start_streaming, + .stop_streaming = touch_cap_stop_streaming, + .buf_request_complete = touch_cap_buf_request_complete, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +int vivid_enum_fmt_tch(struct file *file, void *priv, struct v4l2_fmtdesc *f) +{ + if (f->index) + return -EINVAL; + + f->pixelformat = V4L2_TCH_FMT_DELTA_TD16; + return 0; +} + +int vivid_g_fmt_tch(struct file *file, void *priv, struct v4l2_format *f) +{ + struct vivid_dev *dev = video_drvdata(file); + + if (dev->multiplanar) + return -ENOTTY; + f->fmt.pix = dev->tch_format; + return 0; +} + +int vivid_g_fmt_tch_mplane(struct file *file, void *priv, struct v4l2_format *f) +{ + struct vivid_dev *dev = video_drvdata(file); + struct v4l2_format sp_fmt; + + if (!dev->multiplanar) + return -ENOTTY; + sp_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + sp_fmt.fmt.pix = dev->tch_format; + fmt_sp2mp(&sp_fmt, f); + return 0; +} + +int vivid_g_parm_tch(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_tch_cap; + parm->parm.capture.readbuffers = 1; + return 0; +} + +int vivid_enum_input_tch(struct file *file, void *priv, struct v4l2_input *inp) +{ + if (inp->index) + return -EINVAL; + + inp->type = V4L2_INPUT_TYPE_TOUCH; + strscpy(inp->name, "Vivid Touch", sizeof(inp->name)); + inp->capabilities = 0; + return 0; +} + +int vivid_g_input_tch(struct file *file, void *priv, unsigned int *i) +{ + *i = 0; + return 0; +} + +int vivid_set_touch(struct vivid_dev *dev, unsigned int i) +{ + struct v4l2_pix_format *f = &dev->tch_format; + + if (i) + return -EINVAL; + + f->pixelformat = V4L2_TCH_FMT_DELTA_TD16; + f->width = VIVID_TCH_WIDTH; + f->height = VIVID_TCH_HEIGHT; + f->field = V4L2_FIELD_NONE; + f->colorspace = V4L2_COLORSPACE_RAW; + f->bytesperline = f->width * sizeof(s16); + f->sizeimage = f->width * f->height * sizeof(s16); + return 0; +} + +int vivid_s_input_tch(struct file *file, void *priv, unsigned int i) +{ + return vivid_set_touch(video_drvdata(file), i); +} + +static void vivid_fill_buff_noise(__s16 *tch_buf, int size) +{ + int i; + + /* Fill 10% of the values within range -3 and 3, zero the others */ + for (i = 0; i < size; i++) { + unsigned int rand = get_random_int(); + + if (rand % 10) + tch_buf[i] = 0; + else + tch_buf[i] = (rand / 10) % 7 - 3; + } +} + +static inline int get_random_pressure(void) +{ + return get_random_int() % VIVID_PRESSURE_LIMIT; +} + +static void vivid_tch_buf_set(struct v4l2_pix_format *f, + __s16 *tch_buf, + int index) +{ + unsigned int x = index % f->width; + unsigned int y = index / f->width; + unsigned int offset = VIVID_MIN_PRESSURE; + + tch_buf[index] = offset + get_random_pressure(); + offset /= 2; + if (x) + tch_buf[index - 1] = offset + get_random_pressure(); + if (x < f->width - 1) + tch_buf[index + 1] = offset + get_random_pressure(); + if (y) + tch_buf[index - f->width] = offset + get_random_pressure(); + if (y < f->height - 1) + tch_buf[index + f->width] = offset + get_random_pressure(); + offset /= 2; + if (x && y) + tch_buf[index - 1 - f->width] = offset + get_random_pressure(); + if (x < f->width - 1 && y) + tch_buf[index + 1 - f->width] = offset + get_random_pressure(); + if (x && y < f->height - 1) + tch_buf[index - 1 + f->width] = offset + get_random_pressure(); + if (x < f->width - 1 && y < f->height - 1) + tch_buf[index + 1 + f->width] = offset + get_random_pressure(); +} + +void vivid_fillbuff_tch(struct vivid_dev *dev, struct vivid_buffer *buf) +{ + struct v4l2_pix_format *f = &dev->tch_format; + int size = f->width * f->height; + int x, y, xstart, ystart, offset_x, offset_y; + unsigned int test_pattern, test_pat_idx, rand; + + __s16 *tch_buf = vb2_plane_vaddr(&buf->vb.vb2_buf, 0); + + buf->vb.sequence = dev->touch_cap_seq_count; + test_pattern = (buf->vb.sequence / TCH_SEQ_COUNT) % TEST_CASE_MAX; + test_pat_idx = buf->vb.sequence % TCH_SEQ_COUNT; + + vivid_fill_buff_noise(tch_buf, size); + + if (test_pat_idx >= TCH_PATTERN_COUNT) + return; + + if (test_pat_idx == 0) + dev->tch_pat_random = get_random_int(); + rand = dev->tch_pat_random; + + switch (test_pattern) { + case SINGLE_TAP: + if (test_pat_idx == 2) + vivid_tch_buf_set(f, tch_buf, rand % size); + break; + case DOUBLE_TAP: + if (test_pat_idx == 2 || test_pat_idx == 4) + vivid_tch_buf_set(f, tch_buf, rand % size); + break; + case TRIPLE_TAP: + if (test_pat_idx == 2 || test_pat_idx == 4 || test_pat_idx == 6) + vivid_tch_buf_set(f, tch_buf, rand % size); + break; + case MOVE_LEFT_TO_RIGHT: + vivid_tch_buf_set(f, tch_buf, + (rand % f->height) * f->width + + test_pat_idx * + (f->width / TCH_PATTERN_COUNT)); + break; + case ZOOM_IN: + x = f->width / 2; + y = f->height / 2; + offset_x = ((TCH_PATTERN_COUNT - 1 - test_pat_idx) * x) / + TCH_PATTERN_COUNT; + offset_y = ((TCH_PATTERN_COUNT - 1 - test_pat_idx) * y) / + TCH_PATTERN_COUNT; + vivid_tch_buf_set(f, tch_buf, + (x - offset_x) + f->width * (y - offset_y)); + vivid_tch_buf_set(f, tch_buf, + (x + offset_x) + f->width * (y + offset_y)); + break; + case ZOOM_OUT: + x = f->width / 2; + y = f->height / 2; + offset_x = (test_pat_idx * x) / TCH_PATTERN_COUNT; + offset_y = (test_pat_idx * y) / TCH_PATTERN_COUNT; + vivid_tch_buf_set(f, tch_buf, + (x - offset_x) + f->width * (y - offset_y)); + vivid_tch_buf_set(f, tch_buf, + (x + offset_x) + f->width * (y + offset_y)); + break; + case PALM_PRESS: + for (x = 0; x < f->width; x++) + for (y = f->height / 2; y < f->height; y++) + tch_buf[x + f->width * y] = VIVID_MIN_PRESSURE + + get_random_pressure(); + break; + case MULTIPLE_PRESS: + /* 16 pressure points */ + for (y = 0; y < 4; y++) { + for (x = 0; x < 4; x++) { + ystart = (y * f->height) / 4 + f->height / 8; + xstart = (x * f->width) / 4 + f->width / 8; + vivid_tch_buf_set(f, tch_buf, + ystart * f->width + xstart); + } + } + break; + } +#ifdef __BIG_ENDIAN__ + for (x = 0; x < size; x++) + tch_buf[x] = (__force s16)__cpu_to_le16((u16)tch_buf[x]); +#endif +} diff --git a/drivers/media/test_drivers/vivid/vivid-touch-cap.h b/drivers/media/test_drivers/vivid/vivid-touch-cap.h new file mode 100644 index 000000000000..07e514046ae8 --- /dev/null +++ b/drivers/media/test_drivers/vivid/vivid-touch-cap.h @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * vivid-touch-cap.h - touch support functions. + */ +#ifndef _VIVID_TOUCH_CAP_H_ +#define _VIVID_TOUCH_CAP_H_ + +#define VIVID_TCH_HEIGHT 12 +#define VIVID_TCH_WIDTH 21 +#define VIVID_MIN_PRESSURE 180 +#define VIVID_PRESSURE_LIMIT 40 +#define TCH_SEQ_COUNT 16 +#define TCH_PATTERN_COUNT 12 + +enum vivid_tch_test { + SINGLE_TAP, + DOUBLE_TAP, + TRIPLE_TAP, + MOVE_LEFT_TO_RIGHT, + ZOOM_IN, + ZOOM_OUT, + PALM_PRESS, + MULTIPLE_PRESS, + TEST_CASE_MAX +}; + +extern const struct vb2_ops vivid_touch_cap_qops; + +int vivid_enum_fmt_tch(struct file *file, void *priv, struct v4l2_fmtdesc *f); +int vivid_g_fmt_tch(struct file *file, void *priv, struct v4l2_format *f); +int vivid_g_fmt_tch_mplane(struct file *file, void *priv, struct v4l2_format *f); +int vivid_enum_input_tch(struct file *file, void *priv, struct v4l2_input *inp); +int vivid_g_input_tch(struct file *file, void *priv, unsigned int *i); +int vivid_s_input_tch(struct file *file, void *priv, unsigned int i); +void vivid_fillbuff_tch(struct vivid_dev *dev, struct vivid_buffer *buf); +int vivid_set_touch(struct vivid_dev *dev, unsigned int i); +int vivid_g_parm_tch(struct file *file, void *priv, + struct v4l2_streamparm *parm); +#endif diff --git a/drivers/media/test_drivers/vivid/vivid-vbi-cap.c b/drivers/media/test_drivers/vivid/vivid-vbi-cap.c new file mode 100644 index 000000000000..1a9348eea781 --- /dev/null +++ b/drivers/media/test_drivers/vivid/vivid-vbi-cap.c @@ -0,0 +1,365 @@ +// 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 +#include +#include +#include + +#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/test_drivers/vivid/vivid-vbi-cap.h b/drivers/media/test_drivers/vivid/vivid-vbi-cap.h new file mode 100644 index 000000000000..91d2de01381c --- /dev/null +++ b/drivers/media/test_drivers/vivid/vivid-vbi-cap.h @@ -0,0 +1,28 @@ +/* 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/test_drivers/vivid/vivid-vbi-gen.c b/drivers/media/test_drivers/vivid/vivid-vbi-gen.c new file mode 100644 index 000000000000..acc98445a1fa --- /dev/null +++ b/drivers/media/test_drivers/vivid/vivid-vbi-gen.c @@ -0,0 +1,311 @@ +// 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 +#include +#include +#include +#include + +#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/test_drivers/vivid/vivid-vbi-gen.h b/drivers/media/test_drivers/vivid/vivid-vbi-gen.h new file mode 100644 index 000000000000..2657a7f5571c --- /dev/null +++ b/drivers/media/test_drivers/vivid/vivid-vbi-gen.h @@ -0,0 +1,21 @@ +/* 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/test_drivers/vivid/vivid-vbi-out.c b/drivers/media/test_drivers/vivid/vivid-vbi-out.c new file mode 100644 index 000000000000..cd56476902a2 --- /dev/null +++ b/drivers/media/test_drivers/vivid/vivid-vbi-out.c @@ -0,0 +1,250 @@ +// 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 +#include +#include +#include + +#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/test_drivers/vivid/vivid-vbi-out.h b/drivers/media/test_drivers/vivid/vivid-vbi-out.h new file mode 100644 index 000000000000..76584940cdaf --- /dev/null +++ b/drivers/media/test_drivers/vivid/vivid-vbi-out.h @@ -0,0 +1,22 @@ +/* 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/test_drivers/vivid/vivid-vid-cap.c b/drivers/media/test_drivers/vivid/vivid-vid-cap.c new file mode 100644 index 000000000000..e94beef008c8 --- /dev/null +++ b/drivers/media/test_drivers/vivid/vivid-vid-cap.c @@ -0,0 +1,1918 @@ +// 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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) || + vb2_is_busy(&dev->vb_meta_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; + dev->meta_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/test_drivers/vivid/vivid-vid-cap.h b/drivers/media/test_drivers/vivid/vivid-vid-cap.h new file mode 100644 index 000000000000..1e422a59eeab --- /dev/null +++ b/drivers/media/test_drivers/vivid/vivid-vid-cap.h @@ -0,0 +1,59 @@ +/* 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/test_drivers/vivid/vivid-vid-common.c b/drivers/media/test_drivers/vivid/vivid-vid-common.c new file mode 100644 index 000000000000..76b0be670ebb --- /dev/null +++ b/drivers/media/test_drivers/vivid/vivid-vid-common.c @@ -0,0 +1,1035 @@ +// 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 +#include +#include +#include +#include +#include +#include +#include + +#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_BUF_TYPE_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/test_drivers/vivid/vivid-vid-common.h b/drivers/media/test_drivers/vivid/vivid-vid-common.h new file mode 100644 index 000000000000..d908d9725283 --- /dev/null +++ b/drivers/media/test_drivers/vivid/vivid-vid-common.h @@ -0,0 +1,38 @@ +/* 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/test_drivers/vivid/vivid-vid-out.c b/drivers/media/test_drivers/vivid/vivid-vid-out.c new file mode 100644 index 000000000000..ee3446e3217c --- /dev/null +++ b/drivers/media/test_drivers/vivid/vivid-vid-out.c @@ -0,0 +1,1210 @@ +// 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#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) || + vb2_is_busy(&dev->vb_meta_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; + dev->meta_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/test_drivers/vivid/vivid-vid-out.h b/drivers/media/test_drivers/vivid/vivid-vid-out.h new file mode 100644 index 000000000000..8d56314f4ea1 --- /dev/null +++ b/drivers/media/test_drivers/vivid/vivid-vid-out.h @@ -0,0 +1,44 @@ +/* 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 -- cgit v1.2.3