aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/platform
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/platform')
-rw-r--r--drivers/media/platform/Kconfig54
-rw-r--r--drivers/media/platform/Makefile8
-rw-r--r--drivers/media/platform/blackfin/Kconfig1
-rw-r--r--drivers/media/platform/coda.c3933
-rw-r--r--drivers/media/platform/coda/Makefile3
-rw-r--r--drivers/media/platform/coda/coda-bit.c1861
-rw-r--r--drivers/media/platform/coda/coda-common.c2052
-rw-r--r--drivers/media/platform/coda/coda-h264.c37
-rw-r--r--drivers/media/platform/coda/coda.h287
-rw-r--r--drivers/media/platform/coda/coda_regs.h (renamed from drivers/media/platform/coda.h)0
-rw-r--r--drivers/media/platform/davinci/Kconfig18
-rw-r--r--drivers/media/platform/davinci/dm355_ccdc.c2
-rw-r--r--drivers/media/platform/davinci/dm644x_ccdc.c14
-rw-r--r--drivers/media/platform/davinci/vpfe_capture.c16
-rw-r--r--drivers/media/platform/davinci/vpif.c1
-rw-r--r--drivers/media/platform/davinci/vpif_capture.c13
-rw-r--r--drivers/media/platform/davinci/vpif_display.c22
-rw-r--r--drivers/media/platform/exynos-gsc/gsc-core.c6
-rw-r--r--drivers/media/platform/exynos-gsc/gsc-m2m.c3
-rw-r--r--drivers/media/platform/exynos-gsc/gsc-regs.c8
-rw-r--r--drivers/media/platform/exynos4-is/Kconfig5
-rw-r--r--drivers/media/platform/exynos4-is/fimc-is-errno.c4
-rw-r--r--drivers/media/platform/exynos4-is/fimc-is-errno.h4
-rw-r--r--drivers/media/platform/exynos4-is/fimc-is-param.c2
-rw-r--r--drivers/media/platform/exynos4-is/fimc-is.c10
-rw-r--r--drivers/media/platform/exynos4-is/fimc-isp-video.c9
-rw-r--r--drivers/media/platform/exynos4-is/media-dev.c4
-rw-r--r--drivers/media/platform/exynos4-is/mipi-csis.c3
-rw-r--r--drivers/media/platform/marvell-ccic/Kconfig2
-rw-r--r--drivers/media/platform/marvell-ccic/mcam-core.c2
-rw-r--r--drivers/media/platform/mx2_emmaprp.c2
-rw-r--r--drivers/media/platform/omap/Kconfig2
-rw-r--r--drivers/media/platform/omap/omap_vout.c16
-rw-r--r--drivers/media/platform/omap/omap_vout_vrfb.c10
-rw-r--r--drivers/media/platform/omap/omap_vout_vrfb.h18
-rw-r--r--drivers/media/platform/omap3isp/cfa_coef_table.h10
-rw-r--r--drivers/media/platform/omap3isp/gamma_table.h10
-rw-r--r--drivers/media/platform/omap3isp/isp.c20
-rw-r--r--drivers/media/platform/omap3isp/isp.h10
-rw-r--r--drivers/media/platform/omap3isp/ispccdc.c424
-rw-r--r--drivers/media/platform/omap3isp/ispccdc.h21
-rw-r--r--drivers/media/platform/omap3isp/ispccp2.c10
-rw-r--r--drivers/media/platform/omap3isp/ispccp2.h10
-rw-r--r--drivers/media/platform/omap3isp/ispcsi2.c10
-rw-r--r--drivers/media/platform/omap3isp/ispcsi2.h10
-rw-r--r--drivers/media/platform/omap3isp/ispcsiphy.c10
-rw-r--r--drivers/media/platform/omap3isp/ispcsiphy.h10
-rw-r--r--drivers/media/platform/omap3isp/isph3a.h10
-rw-r--r--drivers/media/platform/omap3isp/isph3a_aewb.c10
-rw-r--r--drivers/media/platform/omap3isp/isph3a_af.c10
-rw-r--r--drivers/media/platform/omap3isp/isphist.c10
-rw-r--r--drivers/media/platform/omap3isp/isphist.h10
-rw-r--r--drivers/media/platform/omap3isp/isppreview.c10
-rw-r--r--drivers/media/platform/omap3isp/isppreview.h10
-rw-r--r--drivers/media/platform/omap3isp/ispreg.h20
-rw-r--r--drivers/media/platform/omap3isp/ispresizer.c80
-rw-r--r--drivers/media/platform/omap3isp/ispresizer.h13
-rw-r--r--drivers/media/platform/omap3isp/ispstat.c10
-rw-r--r--drivers/media/platform/omap3isp/ispstat.h10
-rw-r--r--drivers/media/platform/omap3isp/ispvideo.c59
-rw-r--r--drivers/media/platform/omap3isp/ispvideo.h12
-rw-r--r--drivers/media/platform/omap3isp/luma_enhance_table.h10
-rw-r--r--drivers/media/platform/omap3isp/noise_filter_table.h10
-rw-r--r--drivers/media/platform/s3c-camif/camif-capture.c4
-rw-r--r--drivers/media/platform/s3c-camif/camif-regs.c4
-rw-r--r--drivers/media/platform/s5p-g2d/g2d.c7
-rw-r--r--drivers/media/platform/s5p-jpeg/jpeg-core.c2
-rw-r--r--drivers/media/platform/s5p-jpeg/jpeg-hw-exynos3250.c2
-rw-r--r--drivers/media/platform/s5p-jpeg/jpeg-hw-exynos4.c11
-rw-r--r--drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.c6
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc.c83
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v5.c1
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v6.c1
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_common.h6
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c27
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_debug.h6
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_dec.c54
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_enc.c67
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_opr.c4
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_opr.h488
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c31
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c491
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_pm.c2
-rw-r--r--drivers/media/platform/s5p-tv/Kconfig4
-rw-r--r--drivers/media/platform/s5p-tv/hdmi_drv.c2
-rw-r--r--drivers/media/platform/s5p-tv/sdo_drv.c2
-rw-r--r--drivers/media/platform/s5p-tv/sii9234_drv.c2
-rw-r--r--drivers/media/platform/sh_veu.c4
-rw-r--r--drivers/media/platform/soc_camera/Kconfig16
-rw-r--r--drivers/media/platform/soc_camera/atmel-isi.c13
-rw-r--r--drivers/media/platform/soc_camera/mx2_camera.c5
-rw-r--r--drivers/media/platform/soc_camera/pxa_camera.c2
-rw-r--r--drivers/media/platform/soc_camera/rcar_vin.c4
-rw-r--r--drivers/media/platform/soc_camera/soc_camera.c21
-rw-r--r--drivers/media/platform/ti-vpe/vpdma.c4
-rw-r--r--drivers/media/platform/ti-vpe/vpe.c20
-rw-r--r--drivers/media/platform/via-camera.c13
-rw-r--r--drivers/media/platform/vivi.c1542
-rw-r--r--drivers/media/platform/vivid/Kconfig19
-rw-r--r--drivers/media/platform/vivid/Makefile6
-rw-r--r--drivers/media/platform/vivid/vivid-core.c1390
-rw-r--r--drivers/media/platform/vivid/vivid-core.h520
-rw-r--r--drivers/media/platform/vivid/vivid-ctrls.c1502
-rw-r--r--drivers/media/platform/vivid/vivid-ctrls.h34
-rw-r--r--drivers/media/platform/vivid/vivid-kthread-cap.c886
-rw-r--r--drivers/media/platform/vivid/vivid-kthread-cap.h26
-rw-r--r--drivers/media/platform/vivid/vivid-kthread-out.c305
-rw-r--r--drivers/media/platform/vivid/vivid-kthread-out.h26
-rw-r--r--drivers/media/platform/vivid/vivid-osd.c400
-rw-r--r--drivers/media/platform/vivid/vivid-osd.h27
-rw-r--r--drivers/media/platform/vivid/vivid-radio-common.c189
-rw-r--r--drivers/media/platform/vivid/vivid-radio-common.h40
-rw-r--r--drivers/media/platform/vivid/vivid-radio-rx.c287
-rw-r--r--drivers/media/platform/vivid/vivid-radio-rx.h31
-rw-r--r--drivers/media/platform/vivid/vivid-radio-tx.c141
-rw-r--r--drivers/media/platform/vivid/vivid-radio-tx.h29
-rw-r--r--drivers/media/platform/vivid/vivid-rds-gen.c166
-rw-r--r--drivers/media/platform/vivid/vivid-rds-gen.h53
-rw-r--r--drivers/media/platform/vivid/vivid-sdr-cap.c499
-rw-r--r--drivers/media/platform/vivid/vivid-sdr-cap.h34
-rw-r--r--drivers/media/platform/vivid/vivid-tpg-colors.c310
-rw-r--r--drivers/media/platform/vivid/vivid-tpg-colors.h64
-rw-r--r--drivers/media/platform/vivid/vivid-tpg.c1439
-rw-r--r--drivers/media/platform/vivid/vivid-tpg.h439
-rw-r--r--drivers/media/platform/vivid/vivid-vbi-cap.c371
-rw-r--r--drivers/media/platform/vivid/vivid-vbi-cap.h40
-rw-r--r--drivers/media/platform/vivid/vivid-vbi-gen.c323
-rw-r--r--drivers/media/platform/vivid/vivid-vbi-gen.h33
-rw-r--r--drivers/media/platform/vivid/vivid-vbi-out.c248
-rw-r--r--drivers/media/platform/vivid/vivid-vbi-out.h34
-rw-r--r--drivers/media/platform/vivid/vivid-vid-cap.c1730
-rw-r--r--drivers/media/platform/vivid/vivid-vid-cap.h71
-rw-r--r--drivers/media/platform/vivid/vivid-vid-common.c571
-rw-r--r--drivers/media/platform/vivid/vivid-vid-common.h61
-rw-r--r--drivers/media/platform/vivid/vivid-vid-out.c1146
-rw-r--r--drivers/media/platform/vivid/vivid-vid-out.h56
136 files changed, 19015 insertions, 6763 deletions
diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
index 6d86646d9743..bee9074ebc13 100644
--- a/drivers/media/platform/Kconfig
+++ b/drivers/media/platform/Kconfig
@@ -56,7 +56,8 @@ config VIDEO_VIU
config VIDEO_TIMBERDALE
tristate "Support for timberdale Video In/LogiWIN"
- depends on MFD_TIMBERDALE && VIDEO_V4L2 && I2C && DMADEVICES
+ depends on VIDEO_V4L2 && I2C && DMADEVICES
+ depends on MFD_TIMBERDALE || COMPILE_TEST
select DMA_ENGINE
select TIMB_DMA
select VIDEO_ADV7180
@@ -74,7 +75,8 @@ config VIDEO_VINO
config VIDEO_M32R_AR
tristate "AR devices"
- depends on M32R && VIDEO_V4L2
+ depends on VIDEO_V4L2
+ depends on M32R || COMPILE_TEST
---help---
This is a video4linux driver for the Renesas AR (Artificial Retina)
camera module.
@@ -94,6 +96,7 @@ config VIDEO_M32R_AR_M64278
config VIDEO_OMAP3
tristate "OMAP 3 Camera support"
depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API && ARCH_OMAP3
+ depends on HAS_DMA
select ARM_DMA_USE_IOMMU
select OMAP_IOMMU
select VIDEOBUF2_DMA_CONTIG
@@ -109,7 +112,9 @@ config VIDEO_OMAP3_DEBUG
config VIDEO_S3C_CAMIF
tristate "Samsung S3C24XX/S3C64XX SoC Camera Interface driver"
depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API
- depends on (ARCH_S3C64XX || PLAT_S3C24XX) && PM_RUNTIME
+ depends on PM_RUNTIME
+ depends on ARCH_S3C64XX || PLAT_S3C24XX || COMPILE_TEST
+ depends on HAS_DMA
select VIDEOBUF2_DMA_CONTIG
---help---
This is a v4l2 driver for s3c24xx and s3c64xx SoC series camera
@@ -140,6 +145,7 @@ if V4L_MEM2MEM_DRIVERS
config VIDEO_CODA
tristate "Chips&Media Coda multi-standard codec IP"
depends on VIDEO_DEV && VIDEO_V4L2 && ARCH_MXC
+ depends on HAS_DMA
select SRAM
select VIDEOBUF2_DMA_CONTIG
select V4L2_MEM2MEM_DEV
@@ -151,6 +157,7 @@ config VIDEO_CODA
config VIDEO_MEM2MEM_DEINTERLACE
tristate "Deinterlace support"
depends on VIDEO_DEV && VIDEO_V4L2 && DMA_ENGINE
+ depends on HAS_DMA
select VIDEOBUF2_DMA_CONTIG
select V4L2_MEM2MEM_DEV
help
@@ -158,7 +165,9 @@ config VIDEO_MEM2MEM_DEINTERLACE
config VIDEO_SAMSUNG_S5P_G2D
tristate "Samsung S5P and EXYNOS4 G2D 2d graphics accelerator driver"
- depends on VIDEO_DEV && VIDEO_V4L2 && (PLAT_S5P || ARCH_EXYNOS)
+ depends on VIDEO_DEV && VIDEO_V4L2
+ depends on PLAT_S5P || ARCH_EXYNOS || COMPILE_TEST
+ depends on HAS_DMA
select VIDEOBUF2_DMA_CONTIG
select V4L2_MEM2MEM_DEV
default n
@@ -168,7 +177,9 @@ config VIDEO_SAMSUNG_S5P_G2D
config VIDEO_SAMSUNG_S5P_JPEG
tristate "Samsung S5P/Exynos3250/Exynos4 JPEG codec driver"
- depends on VIDEO_DEV && VIDEO_V4L2 && (PLAT_S5P || ARCH_EXYNOS)
+ depends on VIDEO_DEV && VIDEO_V4L2
+ depends on PLAT_S5P || ARCH_EXYNOS || COMPILE_TEST
+ depends on HAS_DMA
select VIDEOBUF2_DMA_CONTIG
select V4L2_MEM2MEM_DEV
---help---
@@ -177,7 +188,9 @@ config VIDEO_SAMSUNG_S5P_JPEG
config VIDEO_SAMSUNG_S5P_MFC
tristate "Samsung S5P MFC Video Codec"
- depends on VIDEO_DEV && VIDEO_V4L2 && (PLAT_S5P || ARCH_EXYNOS)
+ depends on VIDEO_DEV && VIDEO_V4L2
+ depends on PLAT_S5P || ARCH_EXYNOS || COMPILE_TEST
+ depends on HAS_DMA
select VIDEOBUF2_DMA_CONTIG
default n
help
@@ -185,7 +198,9 @@ config VIDEO_SAMSUNG_S5P_MFC
config VIDEO_MX2_EMMAPRP
tristate "MX2 eMMa-PrP support"
- depends on VIDEO_DEV && VIDEO_V4L2 && SOC_IMX27
+ depends on VIDEO_DEV && VIDEO_V4L2
+ depends on SOC_IMX27 || COMPILE_TEST
+ depends on HAS_DMA
select VIDEOBUF2_DMA_CONTIG
select V4L2_MEM2MEM_DEV
help
@@ -195,7 +210,9 @@ config VIDEO_MX2_EMMAPRP
config VIDEO_SAMSUNG_EXYNOS_GSC
tristate "Samsung Exynos G-Scaler driver"
- depends on VIDEO_DEV && VIDEO_V4L2 && ARCH_EXYNOS5
+ depends on VIDEO_DEV && VIDEO_V4L2
+ depends on ARCH_EXYNOS5 || COMPILE_TEST
+ depends on HAS_DMA
select VIDEOBUF2_DMA_CONTIG
select V4L2_MEM2MEM_DEV
help
@@ -204,6 +221,7 @@ config VIDEO_SAMSUNG_EXYNOS_GSC
config VIDEO_SH_VEU
tristate "SuperH VEU mem2mem video processing driver"
depends on VIDEO_DEV && VIDEO_V4L2 && HAS_DMA
+ depends on HAS_DMA
select VIDEOBUF2_DMA_CONTIG
select V4L2_MEM2MEM_DEV
help
@@ -213,6 +231,7 @@ config VIDEO_SH_VEU
config VIDEO_RENESAS_VSP1
tristate "Renesas VSP1 Video Processing Engine"
depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && HAS_DMA
+ depends on ARCH_SHMOBILE || COMPILE_TEST
select VIDEOBUF2_DMA_CONTIG
---help---
This is a V4L2 driver for the Renesas VSP1 video processing engine.
@@ -222,7 +241,9 @@ config VIDEO_RENESAS_VSP1
config VIDEO_TI_VPE
tristate "TI VPE (Video Processing Engine) driver"
- depends on VIDEO_DEV && VIDEO_V4L2 && SOC_DRA7XX
+ depends on VIDEO_DEV && VIDEO_V4L2
+ depends on SOC_DRA7XX || COMPILE_TEST
+ depends on HAS_DMA
select VIDEOBUF2_DMA_CONTIG
select V4L2_MEM2MEM_DEV
default n
@@ -243,19 +264,8 @@ menuconfig V4L_TEST_DRIVERS
depends on MEDIA_CAMERA_SUPPORT
if V4L_TEST_DRIVERS
-config VIDEO_VIVI
- tristate "Virtual Video Driver"
- depends on VIDEO_DEV && VIDEO_V4L2 && !SPARC32 && !SPARC64
- select FONT_SUPPORT
- select FONT_8x16
- select VIDEOBUF2_VMALLOC
- default n
- ---help---
- Enables a virtual video driver. This device shows a color bar
- and a timestamp, as a real device would generate by using V4L2
- api.
- Say Y here if you want to test video apps or debug V4L devices.
- In doubt, say N.
+
+source "drivers/media/platform/vivid/Kconfig"
config VIDEO_MEM2MEM_TESTDEV
tristate "Virtual test device for mem2mem framework"
diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
index e5269da91906..579046bc276f 100644
--- a/drivers/media/platform/Makefile
+++ b/drivers/media/platform/Makefile
@@ -15,14 +15,14 @@ obj-$(CONFIG_VIDEO_MMP_CAMERA) += marvell-ccic/
obj-$(CONFIG_VIDEO_OMAP3) += omap3isp/
obj-$(CONFIG_VIDEO_VIU) += fsl-viu.o
-obj-$(CONFIG_VIDEO_VIVI) += vivi.o
+obj-$(CONFIG_VIDEO_VIVID) += vivid/
obj-$(CONFIG_VIDEO_MEM2MEM_TESTDEV) += mem2mem_testdev.o
obj-$(CONFIG_VIDEO_TI_VPE) += ti-vpe/
obj-$(CONFIG_VIDEO_MX2_EMMAPRP) += mx2_emmaprp.o
-obj-$(CONFIG_VIDEO_CODA) += coda.o
+obj-$(CONFIG_VIDEO_CODA) += coda/
obj-$(CONFIG_VIDEO_SH_VEU) += sh_veu.o
@@ -47,8 +47,6 @@ obj-$(CONFIG_SOC_CAMERA) += soc_camera/
obj-$(CONFIG_VIDEO_RENESAS_VSP1) += vsp1/
-obj-y += davinci/
-
-obj-$(CONFIG_ARCH_OMAP) += omap/
+obj-y += omap/
ccflags-y += -I$(srctree)/drivers/media/i2c
diff --git a/drivers/media/platform/blackfin/Kconfig b/drivers/media/platform/blackfin/Kconfig
index cc239972fa2c..68fa90151b8f 100644
--- a/drivers/media/platform/blackfin/Kconfig
+++ b/drivers/media/platform/blackfin/Kconfig
@@ -1,6 +1,7 @@
config VIDEO_BLACKFIN_CAPTURE
tristate "Blackfin Video Capture Driver"
depends on VIDEO_V4L2 && BLACKFIN && I2C
+ depends on HAS_DMA
select VIDEOBUF2_DMA_CONTIG
help
V4L2 bridge driver for Blackfin video capture device.
diff --git a/drivers/media/platform/coda.c b/drivers/media/platform/coda.c
deleted file mode 100644
index 3a6d1d2b429e..000000000000
--- a/drivers/media/platform/coda.c
+++ /dev/null
@@ -1,3933 +0,0 @@
-/*
- * Coda multi-standard codec IP
- *
- * Copyright (C) 2012 Vista Silicon S.L.
- * Javier Martin, <javier.martin@vista-silicon.com>
- * Xavier Duret
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- */
-
-#include <linux/clk.h>
-#include <linux/debugfs.h>
-#include <linux/delay.h>
-#include <linux/firmware.h>
-#include <linux/genalloc.h>
-#include <linux/interrupt.h>
-#include <linux/io.h>
-#include <linux/irq.h>
-#include <linux/kfifo.h>
-#include <linux/module.h>
-#include <linux/of_device.h>
-#include <linux/platform_device.h>
-#include <linux/pm_runtime.h>
-#include <linux/slab.h>
-#include <linux/videodev2.h>
-#include <linux/of.h>
-#include <linux/platform_data/coda.h>
-#include <linux/reset.h>
-
-#include <media/v4l2-ctrls.h>
-#include <media/v4l2-device.h>
-#include <media/v4l2-event.h>
-#include <media/v4l2-ioctl.h>
-#include <media/v4l2-mem2mem.h>
-#include <media/videobuf2-core.h>
-#include <media/videobuf2-dma-contig.h>
-
-#include "coda.h"
-
-#define CODA_NAME "coda"
-
-#define CODADX6_MAX_INSTANCES 4
-
-#define CODA_PARA_BUF_SIZE (10 * 1024)
-#define CODA_ISRAM_SIZE (2048 * 2)
-
-#define CODA7_PS_BUF_SIZE 0x28000
-#define CODA9_PS_SAVE_SIZE (512 * 1024)
-
-#define CODA_MAX_FRAMEBUFFERS 8
-
-#define CODA_MAX_FRAME_SIZE 0x100000
-#define FMO_SLICE_SAVE_BUF_SIZE (32)
-#define CODA_DEFAULT_GAMMA 4096
-#define CODA9_DEFAULT_GAMMA 24576 /* 0.75 * 32768 */
-
-#define MIN_W 176
-#define MIN_H 144
-
-#define S_ALIGN 1 /* multiple of 2 */
-#define W_ALIGN 1 /* multiple of 2 */
-#define H_ALIGN 1 /* multiple of 2 */
-
-#define fh_to_ctx(__fh) container_of(__fh, struct coda_ctx, fh)
-
-static int coda_debug;
-module_param(coda_debug, int, 0644);
-MODULE_PARM_DESC(coda_debug, "Debug level (0-1)");
-
-enum {
- V4L2_M2M_SRC = 0,
- V4L2_M2M_DST = 1,
-};
-
-enum coda_inst_type {
- CODA_INST_ENCODER,
- CODA_INST_DECODER,
-};
-
-enum coda_product {
- CODA_DX6 = 0xf001,
- CODA_7541 = 0xf012,
- CODA_960 = 0xf020,
-};
-
-struct coda_fmt {
- char *name;
- u32 fourcc;
-};
-
-struct coda_codec {
- u32 mode;
- u32 src_fourcc;
- u32 dst_fourcc;
- u32 max_w;
- u32 max_h;
-};
-
-struct coda_devtype {
- char *firmware;
- enum coda_product product;
- struct coda_codec *codecs;
- unsigned int num_codecs;
- size_t workbuf_size;
- size_t tempbuf_size;
- size_t iram_size;
-};
-
-/* Per-queue, driver-specific private data */
-struct coda_q_data {
- unsigned int width;
- unsigned int height;
- unsigned int bytesperline;
- unsigned int sizeimage;
- unsigned int fourcc;
- struct v4l2_rect rect;
-};
-
-struct coda_aux_buf {
- void *vaddr;
- dma_addr_t paddr;
- u32 size;
- struct debugfs_blob_wrapper blob;
- struct dentry *dentry;
-};
-
-struct coda_dev {
- struct v4l2_device v4l2_dev;
- struct video_device vfd;
- struct platform_device *plat_dev;
- const struct coda_devtype *devtype;
-
- void __iomem *regs_base;
- struct clk *clk_per;
- struct clk *clk_ahb;
- struct reset_control *rstc;
-
- struct coda_aux_buf codebuf;
- struct coda_aux_buf tempbuf;
- struct coda_aux_buf workbuf;
- struct gen_pool *iram_pool;
- struct coda_aux_buf iram;
-
- spinlock_t irqlock;
- struct mutex dev_mutex;
- struct mutex coda_mutex;
- struct workqueue_struct *workqueue;
- struct v4l2_m2m_dev *m2m_dev;
- struct vb2_alloc_ctx *alloc_ctx;
- struct list_head instances;
- unsigned long instance_mask;
- struct dentry *debugfs_root;
-};
-
-struct coda_params {
- u8 rot_mode;
- u8 h264_intra_qp;
- u8 h264_inter_qp;
- u8 h264_min_qp;
- u8 h264_max_qp;
- u8 h264_deblk_enabled;
- u8 h264_deblk_alpha;
- u8 h264_deblk_beta;
- u8 mpeg4_intra_qp;
- u8 mpeg4_inter_qp;
- u8 gop_size;
- int intra_refresh;
- int codec_mode;
- int codec_mode_aux;
- enum v4l2_mpeg_video_multi_slice_mode slice_mode;
- u32 framerate;
- u16 bitrate;
- u32 slice_max_bits;
- u32 slice_max_mb;
-};
-
-struct coda_iram_info {
- u32 axi_sram_use;
- phys_addr_t buf_bit_use;
- phys_addr_t buf_ip_ac_dc_use;
- phys_addr_t buf_dbk_y_use;
- phys_addr_t buf_dbk_c_use;
- phys_addr_t buf_ovl_use;
- phys_addr_t buf_btp_use;
- phys_addr_t search_ram_paddr;
- int search_ram_size;
- int remaining;
- phys_addr_t next_paddr;
-};
-
-struct gdi_tiled_map {
- int xy2ca_map[16];
- int xy2ba_map[16];
- int xy2ra_map[16];
- int rbc2axi_map[32];
- int xy2rbc_config;
- int map_type;
-#define GDI_LINEAR_FRAME_MAP 0
-};
-
-struct coda_timestamp {
- struct list_head list;
- u32 sequence;
- struct v4l2_timecode timecode;
- struct timeval timestamp;
-};
-
-struct coda_ctx {
- struct coda_dev *dev;
- struct mutex buffer_mutex;
- struct list_head list;
- struct work_struct pic_run_work;
- struct work_struct seq_end_work;
- struct completion completion;
- int aborting;
- int initialized;
- int streamon_out;
- int streamon_cap;
- u32 isequence;
- u32 qsequence;
- u32 osequence;
- u32 sequence_offset;
- struct coda_q_data q_data[2];
- enum coda_inst_type inst_type;
- struct coda_codec *codec;
- enum v4l2_colorspace colorspace;
- struct coda_params params;
- struct v4l2_ctrl_handler ctrls;
- struct v4l2_fh fh;
- int gopcounter;
- int runcounter;
- char vpu_header[3][64];
- int vpu_header_size[3];
- struct kfifo bitstream_fifo;
- struct mutex bitstream_mutex;
- struct coda_aux_buf bitstream;
- bool hold;
- struct coda_aux_buf parabuf;
- struct coda_aux_buf psbuf;
- struct coda_aux_buf slicebuf;
- struct coda_aux_buf internal_frames[CODA_MAX_FRAMEBUFFERS];
- u32 frame_types[CODA_MAX_FRAMEBUFFERS];
- struct coda_timestamp frame_timestamps[CODA_MAX_FRAMEBUFFERS];
- u32 frame_errors[CODA_MAX_FRAMEBUFFERS];
- struct list_head timestamp_list;
- struct coda_aux_buf workbuf;
- int num_internal_frames;
- int idx;
- int reg_idx;
- struct coda_iram_info iram_info;
- struct gdi_tiled_map tiled_map;
- u32 bit_stream_param;
- u32 frm_dis_flg;
- u32 frame_mem_ctrl;
- int display_idx;
- struct dentry *debugfs_entry;
-};
-
-static const u8 coda_filler_nal[14] = { 0x00, 0x00, 0x00, 0x01, 0x0c, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80 };
-static const u8 coda_filler_size[8] = { 0, 7, 14, 13, 12, 11, 10, 9 };
-
-static inline void coda_write(struct coda_dev *dev, u32 data, u32 reg)
-{
- v4l2_dbg(1, coda_debug, &dev->v4l2_dev,
- "%s: data=0x%x, reg=0x%x\n", __func__, data, reg);
- writel(data, dev->regs_base + reg);
-}
-
-static inline unsigned int coda_read(struct coda_dev *dev, u32 reg)
-{
- u32 data;
- data = readl(dev->regs_base + reg);
- v4l2_dbg(1, coda_debug, &dev->v4l2_dev,
- "%s: data=0x%x, reg=0x%x\n", __func__, data, reg);
- return data;
-}
-
-static inline unsigned long coda_isbusy(struct coda_dev *dev)
-{
- return coda_read(dev, CODA_REG_BIT_BUSY);
-}
-
-static inline int coda_is_initialized(struct coda_dev *dev)
-{
- return (coda_read(dev, CODA_REG_BIT_CUR_PC) != 0);
-}
-
-static int coda_wait_timeout(struct coda_dev *dev)
-{
- unsigned long timeout = jiffies + msecs_to_jiffies(1000);
-
- while (coda_isbusy(dev)) {
- if (time_after(jiffies, timeout))
- return -ETIMEDOUT;
- }
- return 0;
-}
-
-static void coda_command_async(struct coda_ctx *ctx, int cmd)
-{
- struct coda_dev *dev = ctx->dev;
-
- if (dev->devtype->product == CODA_960 ||
- dev->devtype->product == CODA_7541) {
- /* Restore context related registers to CODA */
- coda_write(dev, ctx->bit_stream_param,
- CODA_REG_BIT_BIT_STREAM_PARAM);
- coda_write(dev, ctx->frm_dis_flg,
- CODA_REG_BIT_FRM_DIS_FLG(ctx->reg_idx));
- coda_write(dev, ctx->frame_mem_ctrl,
- CODA_REG_BIT_FRAME_MEM_CTRL);
- coda_write(dev, ctx->workbuf.paddr, CODA_REG_BIT_WORK_BUF_ADDR);
- }
-
- if (dev->devtype->product == CODA_960) {
- coda_write(dev, 1, CODA9_GDI_WPROT_ERR_CLR);
- coda_write(dev, 0, CODA9_GDI_WPROT_RGN_EN);
- }
-
- coda_write(dev, CODA_REG_BIT_BUSY_FLAG, CODA_REG_BIT_BUSY);
-
- coda_write(dev, ctx->idx, CODA_REG_BIT_RUN_INDEX);
- coda_write(dev, ctx->params.codec_mode, CODA_REG_BIT_RUN_COD_STD);
- coda_write(dev, ctx->params.codec_mode_aux, CODA7_REG_BIT_RUN_AUX_STD);
-
- coda_write(dev, cmd, CODA_REG_BIT_RUN_COMMAND);
-}
-
-static int coda_command_sync(struct coda_ctx *ctx, int cmd)
-{
- struct coda_dev *dev = ctx->dev;
-
- coda_command_async(ctx, cmd);
- return coda_wait_timeout(dev);
-}
-
-static int coda_hw_reset(struct coda_ctx *ctx)
-{
- struct coda_dev *dev = ctx->dev;
- unsigned long timeout;
- unsigned int idx;
- int ret;
-
- if (!dev->rstc)
- return -ENOENT;
-
- idx = coda_read(dev, CODA_REG_BIT_RUN_INDEX);
-
- timeout = jiffies + msecs_to_jiffies(100);
- coda_write(dev, 0x11, CODA9_GDI_BUS_CTRL);
- while (coda_read(dev, CODA9_GDI_BUS_STATUS) != 0x77) {
- if (time_after(jiffies, timeout))
- return -ETIME;
- cpu_relax();
- }
-
- ret = reset_control_reset(dev->rstc);
- if (ret < 0)
- return ret;
-
- coda_write(dev, 0x00, CODA9_GDI_BUS_CTRL);
- coda_write(dev, CODA_REG_BIT_BUSY_FLAG, CODA_REG_BIT_BUSY);
- coda_write(dev, CODA_REG_RUN_ENABLE, CODA_REG_BIT_CODE_RUN);
- ret = coda_wait_timeout(dev);
- coda_write(dev, idx, CODA_REG_BIT_RUN_INDEX);
-
- return ret;
-}
-
-static struct coda_q_data *get_q_data(struct coda_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;
- }
-}
-
-/*
- * Array of all formats supported by any version of Coda:
- */
-static struct coda_fmt coda_formats[] = {
- {
- .name = "YUV 4:2:0 Planar, YCbCr",
- .fourcc = V4L2_PIX_FMT_YUV420,
- },
- {
- .name = "YUV 4:2:0 Planar, YCrCb",
- .fourcc = V4L2_PIX_FMT_YVU420,
- },
- {
- .name = "H264 Encoded Stream",
- .fourcc = V4L2_PIX_FMT_H264,
- },
- {
- .name = "MPEG4 Encoded Stream",
- .fourcc = V4L2_PIX_FMT_MPEG4,
- },
-};
-
-#define CODA_CODEC(mode, src_fourcc, dst_fourcc, max_w, max_h) \
- { mode, src_fourcc, dst_fourcc, max_w, max_h }
-
-/*
- * Arrays of codecs supported by each given version of Coda:
- * i.MX27 -> codadx6
- * i.MX5x -> coda7
- * i.MX6 -> coda960
- * Use V4L2_PIX_FMT_YUV420 as placeholder for all supported YUV 4:2:0 variants
- */
-static struct coda_codec codadx6_codecs[] = {
- CODA_CODEC(CODADX6_MODE_ENCODE_H264, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_H264, 720, 576),
- CODA_CODEC(CODADX6_MODE_ENCODE_MP4, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_MPEG4, 720, 576),
-};
-
-static struct coda_codec coda7_codecs[] = {
- CODA_CODEC(CODA7_MODE_ENCODE_H264, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_H264, 1280, 720),
- CODA_CODEC(CODA7_MODE_ENCODE_MP4, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_MPEG4, 1280, 720),
- CODA_CODEC(CODA7_MODE_DECODE_H264, V4L2_PIX_FMT_H264, V4L2_PIX_FMT_YUV420, 1920, 1080),
- CODA_CODEC(CODA7_MODE_DECODE_MP4, V4L2_PIX_FMT_MPEG4, V4L2_PIX_FMT_YUV420, 1920, 1080),
-};
-
-static struct coda_codec coda9_codecs[] = {
- CODA_CODEC(CODA9_MODE_ENCODE_H264, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_H264, 1920, 1080),
- CODA_CODEC(CODA9_MODE_ENCODE_MP4, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_MPEG4, 1920, 1080),
- CODA_CODEC(CODA9_MODE_DECODE_H264, V4L2_PIX_FMT_H264, V4L2_PIX_FMT_YUV420, 1920, 1080),
- CODA_CODEC(CODA9_MODE_DECODE_MP4, V4L2_PIX_FMT_MPEG4, V4L2_PIX_FMT_YUV420, 1920, 1080),
-};
-
-static bool coda_format_is_yuv(u32 fourcc)
-{
- switch (fourcc) {
- case V4L2_PIX_FMT_YUV420:
- case V4L2_PIX_FMT_YVU420:
- return true;
- default:
- return false;
- }
-}
-
-/*
- * Normalize all supported YUV 4:2:0 formats to the value used in the codec
- * tables.
- */
-static u32 coda_format_normalize_yuv(u32 fourcc)
-{
- return coda_format_is_yuv(fourcc) ? V4L2_PIX_FMT_YUV420 : fourcc;
-}
-
-static struct coda_codec *coda_find_codec(struct coda_dev *dev, int src_fourcc,
- int dst_fourcc)
-{
- struct coda_codec *codecs = dev->devtype->codecs;
- int num_codecs = dev->devtype->num_codecs;
- int k;
-
- src_fourcc = coda_format_normalize_yuv(src_fourcc);
- dst_fourcc = coda_format_normalize_yuv(dst_fourcc);
- if (src_fourcc == dst_fourcc)
- return NULL;
-
- for (k = 0; k < num_codecs; k++) {
- if (codecs[k].src_fourcc == src_fourcc &&
- codecs[k].dst_fourcc == dst_fourcc)
- break;
- }
-
- if (k == num_codecs)
- return NULL;
-
- return &codecs[k];
-}
-
-static void coda_get_max_dimensions(struct coda_dev *dev,
- struct coda_codec *codec,
- int *max_w, int *max_h)
-{
- struct coda_codec *codecs = dev->devtype->codecs;
- int num_codecs = dev->devtype->num_codecs;
- unsigned int w, h;
- int k;
-
- if (codec) {
- w = codec->max_w;
- h = codec->max_h;
- } else {
- for (k = 0, w = 0, h = 0; k < num_codecs; k++) {
- w = max(w, codecs[k].max_w);
- h = max(h, codecs[k].max_h);
- }
- }
-
- if (max_w)
- *max_w = w;
- if (max_h)
- *max_h = h;
-}
-
-static char *coda_product_name(int product)
-{
- static char buf[9];
-
- switch (product) {
- case CODA_DX6:
- return "CodaDx6";
- case CODA_7541:
- return "CODA7541";
- case CODA_960:
- return "CODA960";
- default:
- snprintf(buf, sizeof(buf), "(0x%04x)", product);
- return buf;
- }
-}
-
-/*
- * V4L2 ioctl() operations.
- */
-static int coda_querycap(struct file *file, void *priv,
- struct v4l2_capability *cap)
-{
- struct coda_ctx *ctx = fh_to_ctx(priv);
-
- strlcpy(cap->driver, CODA_NAME, sizeof(cap->driver));
- strlcpy(cap->card, coda_product_name(ctx->dev->devtype->product),
- sizeof(cap->card));
- strlcpy(cap->bus_info, "platform:" CODA_NAME, sizeof(cap->bus_info));
- /*
- * This is only a mem-to-mem video device. The capture and output
- * device capability flags are left only for backward compatibility
- * and are scheduled for removal.
- */
- cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT |
- V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING;
- cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
-
- return 0;
-}
-
-static int enum_fmt(void *priv, struct v4l2_fmtdesc *f,
- enum v4l2_buf_type type, int src_fourcc)
-{
- struct coda_ctx *ctx = fh_to_ctx(priv);
- struct coda_codec *codecs = ctx->dev->devtype->codecs;
- struct coda_fmt *formats = coda_formats;
- struct coda_fmt *fmt;
- int num_codecs = ctx->dev->devtype->num_codecs;
- int num_formats = ARRAY_SIZE(coda_formats);
- int i, k, num = 0;
-
- for (i = 0; i < num_formats; i++) {
- /* Both uncompressed formats are always supported */
- if (coda_format_is_yuv(formats[i].fourcc) &&
- !coda_format_is_yuv(src_fourcc)) {
- if (num == f->index)
- break;
- ++num;
- continue;
- }
- /* Compressed formats may be supported, check the codec list */
- for (k = 0; k < num_codecs; k++) {
- /* if src_fourcc is set, only consider matching codecs */
- if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE &&
- formats[i].fourcc == codecs[k].dst_fourcc &&
- (!src_fourcc || src_fourcc == codecs[k].src_fourcc))
- break;
- if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT &&
- formats[i].fourcc == codecs[k].src_fourcc)
- break;
- }
- if (k < num_codecs) {
- if (num == f->index)
- break;
- ++num;
- }
- }
-
- if (i < num_formats) {
- fmt = &formats[i];
- strlcpy(f->description, fmt->name, sizeof(f->description));
- f->pixelformat = fmt->fourcc;
- if (!coda_format_is_yuv(fmt->fourcc))
- f->flags |= V4L2_FMT_FLAG_COMPRESSED;
- return 0;
- }
-
- /* Format not found */
- return -EINVAL;
-}
-
-static int coda_enum_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_fmtdesc *f)
-{
- struct coda_ctx *ctx = fh_to_ctx(priv);
- struct vb2_queue *src_vq;
- struct coda_q_data *q_data_src;
-
- /* If the source format is already fixed, only list matching formats */
- src_vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
- if (vb2_is_streaming(src_vq)) {
- q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
-
- return enum_fmt(priv, f, V4L2_BUF_TYPE_VIDEO_CAPTURE,
- q_data_src->fourcc);
- }
-
- return enum_fmt(priv, f, V4L2_BUF_TYPE_VIDEO_CAPTURE, 0);
-}
-
-static int coda_enum_fmt_vid_out(struct file *file, void *priv,
- struct v4l2_fmtdesc *f)
-{
- return enum_fmt(priv, f, V4L2_BUF_TYPE_VIDEO_OUTPUT, 0);
-}
-
-static int coda_g_fmt(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct coda_q_data *q_data;
- struct coda_ctx *ctx = fh_to_ctx(priv);
-
- q_data = get_q_data(ctx, f->type);
- if (!q_data)
- return -EINVAL;
-
- f->fmt.pix.field = V4L2_FIELD_NONE;
- f->fmt.pix.pixelformat = q_data->fourcc;
- f->fmt.pix.width = q_data->width;
- f->fmt.pix.height = q_data->height;
- f->fmt.pix.bytesperline = q_data->bytesperline;
-
- f->fmt.pix.sizeimage = q_data->sizeimage;
- f->fmt.pix.colorspace = ctx->colorspace;
-
- return 0;
-}
-
-static int coda_try_fmt(struct coda_ctx *ctx, struct coda_codec *codec,
- struct v4l2_format *f)
-{
- struct coda_dev *dev = ctx->dev;
- struct coda_q_data *q_data;
- unsigned int max_w, max_h;
- enum v4l2_field field;
-
- field = f->fmt.pix.field;
- if (field == V4L2_FIELD_ANY)
- field = V4L2_FIELD_NONE;
- else if (V4L2_FIELD_NONE != field)
- return -EINVAL;
-
- /* V4L2 specification suggests the driver corrects the format struct
- * if any of the dimensions is unsupported */
- f->fmt.pix.field = field;
-
- coda_get_max_dimensions(dev, codec, &max_w, &max_h);
- v4l_bound_align_image(&f->fmt.pix.width, MIN_W, max_w, W_ALIGN,
- &f->fmt.pix.height, MIN_H, max_h, H_ALIGN,
- S_ALIGN);
-
- switch (f->fmt.pix.pixelformat) {
- case V4L2_PIX_FMT_YUV420:
- case V4L2_PIX_FMT_YVU420:
- case V4L2_PIX_FMT_H264:
- case V4L2_PIX_FMT_MPEG4:
- case V4L2_PIX_FMT_JPEG:
- break;
- default:
- q_data = get_q_data(ctx, f->type);
- if (!q_data)
- return -EINVAL;
- f->fmt.pix.pixelformat = q_data->fourcc;
- }
-
- switch (f->fmt.pix.pixelformat) {
- case V4L2_PIX_FMT_YUV420:
- case V4L2_PIX_FMT_YVU420:
- /* Frame stride must be multiple of 8, but 16 for h.264 */
- f->fmt.pix.bytesperline = round_up(f->fmt.pix.width, 16);
- f->fmt.pix.sizeimage = f->fmt.pix.bytesperline *
- f->fmt.pix.height * 3 / 2;
- break;
- case V4L2_PIX_FMT_H264:
- case V4L2_PIX_FMT_MPEG4:
- case V4L2_PIX_FMT_JPEG:
- f->fmt.pix.bytesperline = 0;
- f->fmt.pix.sizeimage = CODA_MAX_FRAME_SIZE;
- break;
- default:
- BUG();
- }
-
- return 0;
-}
-
-static int coda_try_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct coda_ctx *ctx = fh_to_ctx(priv);
- struct coda_codec *codec;
- struct vb2_queue *src_vq;
- int ret;
-
- /*
- * If the source format is already fixed, try to find a codec that
- * converts to the given destination format
- */
- src_vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
- if (vb2_is_streaming(src_vq)) {
- struct coda_q_data *q_data_src;
-
- q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
- codec = coda_find_codec(ctx->dev, q_data_src->fourcc,
- f->fmt.pix.pixelformat);
- if (!codec)
- return -EINVAL;
- } else {
- /* Otherwise determine codec by encoded format, if possible */
- codec = coda_find_codec(ctx->dev, V4L2_PIX_FMT_YUV420,
- f->fmt.pix.pixelformat);
- }
-
- f->fmt.pix.colorspace = ctx->colorspace;
-
- ret = coda_try_fmt(ctx, codec, f);
- if (ret < 0)
- return ret;
-
- /* The h.264 decoder only returns complete 16x16 macroblocks */
- if (codec && codec->src_fourcc == V4L2_PIX_FMT_H264) {
- f->fmt.pix.width = f->fmt.pix.width;
- f->fmt.pix.height = round_up(f->fmt.pix.height, 16);
- f->fmt.pix.bytesperline = round_up(f->fmt.pix.width, 16);
- f->fmt.pix.sizeimage = f->fmt.pix.bytesperline *
- f->fmt.pix.height * 3 / 2;
- }
-
- return 0;
-}
-
-static int coda_try_fmt_vid_out(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct coda_ctx *ctx = fh_to_ctx(priv);
- struct coda_codec *codec;
-
- /* Determine codec by encoded format, returns NULL if raw or invalid */
- codec = coda_find_codec(ctx->dev, f->fmt.pix.pixelformat,
- V4L2_PIX_FMT_YUV420);
-
- if (!f->fmt.pix.colorspace)
- f->fmt.pix.colorspace = V4L2_COLORSPACE_REC709;
-
- return coda_try_fmt(ctx, codec, f);
-}
-
-static int coda_s_fmt(struct coda_ctx *ctx, struct v4l2_format *f)
-{
- struct coda_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->fourcc = f->fmt.pix.pixelformat;
- q_data->width = f->fmt.pix.width;
- q_data->height = f->fmt.pix.height;
- q_data->bytesperline = f->fmt.pix.bytesperline;
- q_data->sizeimage = f->fmt.pix.sizeimage;
- q_data->rect.left = 0;
- q_data->rect.top = 0;
- q_data->rect.width = f->fmt.pix.width;
- q_data->rect.height = f->fmt.pix.height;
-
- v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
- "Setting format for type %d, wxh: %dx%d, fmt: %d\n",
- f->type, q_data->width, q_data->height, q_data->fourcc);
-
- return 0;
-}
-
-static int coda_s_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct coda_ctx *ctx = fh_to_ctx(priv);
- int ret;
-
- ret = coda_try_fmt_vid_cap(file, priv, f);
- if (ret)
- return ret;
-
- return coda_s_fmt(ctx, f);
-}
-
-static int coda_s_fmt_vid_out(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct coda_ctx *ctx = fh_to_ctx(priv);
- int ret;
-
- ret = coda_try_fmt_vid_out(file, priv, f);
- if (ret)
- return ret;
-
- ret = coda_s_fmt(ctx, f);
- if (ret)
- ctx->colorspace = f->fmt.pix.colorspace;
-
- return ret;
-}
-
-static int coda_qbuf(struct file *file, void *priv,
- struct v4l2_buffer *buf)
-{
- struct coda_ctx *ctx = fh_to_ctx(priv);
-
- return v4l2_m2m_qbuf(file, ctx->fh.m2m_ctx, buf);
-}
-
-static bool coda_buf_is_end_of_stream(struct coda_ctx *ctx,
- struct v4l2_buffer *buf)
-{
- struct vb2_queue *src_vq;
-
- src_vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
-
- return ((ctx->bit_stream_param & CODA_BIT_STREAM_END_FLAG) &&
- (buf->sequence == (ctx->qsequence - 1)));
-}
-
-static int coda_dqbuf(struct file *file, void *priv,
- struct v4l2_buffer *buf)
-{
- struct coda_ctx *ctx = fh_to_ctx(priv);
- int ret;
-
- ret = v4l2_m2m_dqbuf(file, ctx->fh.m2m_ctx, buf);
-
- /* If this is the last capture buffer, emit an end-of-stream event */
- if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE &&
- coda_buf_is_end_of_stream(ctx, buf)) {
- const struct v4l2_event eos_event = {
- .type = V4L2_EVENT_EOS
- };
-
- v4l2_event_queue_fh(&ctx->fh, &eos_event);
- }
-
- return ret;
-}
-
-static int coda_g_selection(struct file *file, void *fh,
- struct v4l2_selection *s)
-{
- struct coda_ctx *ctx = fh_to_ctx(fh);
- struct coda_q_data *q_data;
- struct v4l2_rect r, *rsel;
-
- q_data = get_q_data(ctx, s->type);
- if (!q_data)
- return -EINVAL;
-
- r.left = 0;
- r.top = 0;
- r.width = q_data->width;
- r.height = q_data->height;
- rsel = &q_data->rect;
-
- switch (s->target) {
- case V4L2_SEL_TGT_CROP_DEFAULT:
- case V4L2_SEL_TGT_CROP_BOUNDS:
- rsel = &r;
- /* fallthrough */
- case V4L2_SEL_TGT_CROP:
- if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
- return -EINVAL;
- break;
- case V4L2_SEL_TGT_COMPOSE_BOUNDS:
- case V4L2_SEL_TGT_COMPOSE_PADDED:
- rsel = &r;
- /* fallthrough */
- case V4L2_SEL_TGT_COMPOSE:
- case V4L2_SEL_TGT_COMPOSE_DEFAULT:
- if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
- break;
- default:
- return -EINVAL;
- }
-
- s->r = *rsel;
-
- return 0;
-}
-
-static int coda_try_decoder_cmd(struct file *file, void *fh,
- struct v4l2_decoder_cmd *dc)
-{
- if (dc->cmd != V4L2_DEC_CMD_STOP)
- return -EINVAL;
-
- if (dc->flags & V4L2_DEC_CMD_STOP_TO_BLACK)
- return -EINVAL;
-
- if (!(dc->flags & V4L2_DEC_CMD_STOP_IMMEDIATELY) && (dc->stop.pts != 0))
- return -EINVAL;
-
- return 0;
-}
-
-static int coda_decoder_cmd(struct file *file, void *fh,
- struct v4l2_decoder_cmd *dc)
-{
- struct coda_ctx *ctx = fh_to_ctx(fh);
- struct coda_dev *dev = ctx->dev;
- int ret;
-
- ret = coda_try_decoder_cmd(file, fh, dc);
- if (ret < 0)
- return ret;
-
- /* Ignore decoder stop command silently in encoder context */
- if (ctx->inst_type != CODA_INST_DECODER)
- return 0;
-
- /* Set the strem-end flag on this context */
- ctx->bit_stream_param |= CODA_BIT_STREAM_END_FLAG;
-
- if ((dev->devtype->product == CODA_960) &&
- coda_isbusy(dev) &&
- (ctx->idx == coda_read(dev, CODA_REG_BIT_RUN_INDEX))) {
- /* If this context is currently running, update the hardware flag */
- coda_write(dev, ctx->bit_stream_param, CODA_REG_BIT_BIT_STREAM_PARAM);
- }
- ctx->hold = false;
- v4l2_m2m_try_schedule(ctx->fh.m2m_ctx);
-
- return 0;
-}
-
-static int coda_subscribe_event(struct v4l2_fh *fh,
- const struct v4l2_event_subscription *sub)
-{
- switch (sub->type) {
- case V4L2_EVENT_EOS:
- return v4l2_event_subscribe(fh, sub, 0, NULL);
- default:
- return v4l2_ctrl_subscribe_event(fh, sub);
- }
-}
-
-static const struct v4l2_ioctl_ops coda_ioctl_ops = {
- .vidioc_querycap = coda_querycap,
-
- .vidioc_enum_fmt_vid_cap = coda_enum_fmt_vid_cap,
- .vidioc_g_fmt_vid_cap = coda_g_fmt,
- .vidioc_try_fmt_vid_cap = coda_try_fmt_vid_cap,
- .vidioc_s_fmt_vid_cap = coda_s_fmt_vid_cap,
-
- .vidioc_enum_fmt_vid_out = coda_enum_fmt_vid_out,
- .vidioc_g_fmt_vid_out = coda_g_fmt,
- .vidioc_try_fmt_vid_out = coda_try_fmt_vid_out,
- .vidioc_s_fmt_vid_out = coda_s_fmt_vid_out,
-
- .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs,
- .vidioc_querybuf = v4l2_m2m_ioctl_querybuf,
-
- .vidioc_qbuf = coda_qbuf,
- .vidioc_expbuf = v4l2_m2m_ioctl_expbuf,
- .vidioc_dqbuf = coda_dqbuf,
- .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs,
-
- .vidioc_streamon = v4l2_m2m_ioctl_streamon,
- .vidioc_streamoff = v4l2_m2m_ioctl_streamoff,
-
- .vidioc_g_selection = coda_g_selection,
-
- .vidioc_try_decoder_cmd = coda_try_decoder_cmd,
- .vidioc_decoder_cmd = coda_decoder_cmd,
-
- .vidioc_subscribe_event = coda_subscribe_event,
- .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
-};
-
-static int coda_start_decoding(struct coda_ctx *ctx);
-
-static inline int coda_get_bitstream_payload(struct coda_ctx *ctx)
-{
- return kfifo_len(&ctx->bitstream_fifo);
-}
-
-static void coda_kfifo_sync_from_device(struct coda_ctx *ctx)
-{
- struct __kfifo *kfifo = &ctx->bitstream_fifo.kfifo;
- struct coda_dev *dev = ctx->dev;
- u32 rd_ptr;
-
- rd_ptr = coda_read(dev, CODA_REG_BIT_RD_PTR(ctx->reg_idx));
- kfifo->out = (kfifo->in & ~kfifo->mask) |
- (rd_ptr - ctx->bitstream.paddr);
- if (kfifo->out > kfifo->in)
- kfifo->out -= kfifo->mask + 1;
-}
-
-static void coda_kfifo_sync_to_device_full(struct coda_ctx *ctx)
-{
- struct __kfifo *kfifo = &ctx->bitstream_fifo.kfifo;
- struct coda_dev *dev = ctx->dev;
- u32 rd_ptr, wr_ptr;
-
- rd_ptr = ctx->bitstream.paddr + (kfifo->out & kfifo->mask);
- coda_write(dev, rd_ptr, CODA_REG_BIT_RD_PTR(ctx->reg_idx));
- wr_ptr = ctx->bitstream.paddr + (kfifo->in & kfifo->mask);
- coda_write(dev, wr_ptr, CODA_REG_BIT_WR_PTR(ctx->reg_idx));
-}
-
-static void coda_kfifo_sync_to_device_write(struct coda_ctx *ctx)
-{
- struct __kfifo *kfifo = &ctx->bitstream_fifo.kfifo;
- struct coda_dev *dev = ctx->dev;
- u32 wr_ptr;
-
- wr_ptr = ctx->bitstream.paddr + (kfifo->in & kfifo->mask);
- coda_write(dev, wr_ptr, CODA_REG_BIT_WR_PTR(ctx->reg_idx));
-}
-
-static int coda_bitstream_queue(struct coda_ctx *ctx, struct vb2_buffer *src_buf)
-{
- u32 src_size = vb2_get_plane_payload(src_buf, 0);
- u32 n;
-
- n = kfifo_in(&ctx->bitstream_fifo, vb2_plane_vaddr(src_buf, 0), src_size);
- if (n < src_size)
- return -ENOSPC;
-
- dma_sync_single_for_device(&ctx->dev->plat_dev->dev, ctx->bitstream.paddr,
- ctx->bitstream.size, DMA_TO_DEVICE);
-
- src_buf->v4l2_buf.sequence = ctx->qsequence++;
-
- return 0;
-}
-
-static bool coda_bitstream_try_queue(struct coda_ctx *ctx,
- struct vb2_buffer *src_buf)
-{
- int ret;
-
- if (coda_get_bitstream_payload(ctx) +
- vb2_get_plane_payload(src_buf, 0) + 512 >= ctx->bitstream.size)
- return false;
-
- if (vb2_plane_vaddr(src_buf, 0) == NULL) {
- v4l2_err(&ctx->dev->v4l2_dev, "trying to queue empty buffer\n");
- return true;
- }
-
- ret = coda_bitstream_queue(ctx, src_buf);
- if (ret < 0) {
- v4l2_err(&ctx->dev->v4l2_dev, "bitstream buffer overflow\n");
- return false;
- }
- /* Sync read pointer to device */
- if (ctx == v4l2_m2m_get_curr_priv(ctx->dev->m2m_dev))
- coda_kfifo_sync_to_device_write(ctx);
-
- ctx->hold = false;
-
- return true;
-}
-
-static void coda_fill_bitstream(struct coda_ctx *ctx)
-{
- struct vb2_buffer *src_buf;
- struct coda_timestamp *ts;
-
- while (v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx) > 0) {
- src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
-
- if (coda_bitstream_try_queue(ctx, src_buf)) {
- /*
- * Source buffer is queued in the bitstream ringbuffer;
- * queue the timestamp and mark source buffer as done
- */
- src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
-
- ts = kmalloc(sizeof(*ts), GFP_KERNEL);
- if (ts) {
- ts->sequence = src_buf->v4l2_buf.sequence;
- ts->timecode = src_buf->v4l2_buf.timecode;
- ts->timestamp = src_buf->v4l2_buf.timestamp;
- list_add_tail(&ts->list, &ctx->timestamp_list);
- }
-
- v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE);
- } else {
- break;
- }
- }
-}
-
-static void coda_set_gdi_regs(struct coda_ctx *ctx)
-{
- struct gdi_tiled_map *tiled_map = &ctx->tiled_map;
- struct coda_dev *dev = ctx->dev;
- int i;
-
- for (i = 0; i < 16; i++)
- coda_write(dev, tiled_map->xy2ca_map[i],
- CODA9_GDI_XY2_CAS_0 + 4 * i);
- for (i = 0; i < 4; i++)
- coda_write(dev, tiled_map->xy2ba_map[i],
- CODA9_GDI_XY2_BA_0 + 4 * i);
- for (i = 0; i < 16; i++)
- coda_write(dev, tiled_map->xy2ra_map[i],
- CODA9_GDI_XY2_RAS_0 + 4 * i);
- coda_write(dev, tiled_map->xy2rbc_config, CODA9_GDI_XY2_RBC_CONFIG);
- for (i = 0; i < 32; i++)
- coda_write(dev, tiled_map->rbc2axi_map[i],
- CODA9_GDI_RBC2_AXI_0 + 4 * i);
-}
-
-/*
- * Mem-to-mem operations.
- */
-static int coda_prepare_decode(struct coda_ctx *ctx)
-{
- struct vb2_buffer *dst_buf;
- struct coda_dev *dev = ctx->dev;
- struct coda_q_data *q_data_dst;
- u32 stridey, height;
- u32 picture_y, picture_cb, picture_cr;
-
- dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
- q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
-
- if (ctx->params.rot_mode & CODA_ROT_90) {
- stridey = q_data_dst->height;
- height = q_data_dst->width;
- } else {
- stridey = q_data_dst->width;
- height = q_data_dst->height;
- }
-
- /* Try to copy source buffer contents into the bitstream ringbuffer */
- mutex_lock(&ctx->bitstream_mutex);
- coda_fill_bitstream(ctx);
- mutex_unlock(&ctx->bitstream_mutex);
-
- if (coda_get_bitstream_payload(ctx) < 512 &&
- (!(ctx->bit_stream_param & CODA_BIT_STREAM_END_FLAG))) {
- v4l2_dbg(1, coda_debug, &dev->v4l2_dev,
- "bitstream payload: %d, skipping\n",
- coda_get_bitstream_payload(ctx));
- v4l2_m2m_job_finish(ctx->dev->m2m_dev, ctx->fh.m2m_ctx);
- return -EAGAIN;
- }
-
- /* Run coda_start_decoding (again) if not yet initialized */
- if (!ctx->initialized) {
- int ret = coda_start_decoding(ctx);
- if (ret < 0) {
- v4l2_err(&dev->v4l2_dev, "failed to start decoding\n");
- v4l2_m2m_job_finish(ctx->dev->m2m_dev, ctx->fh.m2m_ctx);
- return -EAGAIN;
- } else {
- ctx->initialized = 1;
- }
- }
-
- if (dev->devtype->product == CODA_960)
- coda_set_gdi_regs(ctx);
-
- /* Set rotator output */
- picture_y = vb2_dma_contig_plane_dma_addr(dst_buf, 0);
- if (q_data_dst->fourcc == V4L2_PIX_FMT_YVU420) {
- /* Switch Cr and Cb for YVU420 format */
- picture_cr = picture_y + stridey * height;
- picture_cb = picture_cr + stridey / 2 * height / 2;
- } else {
- picture_cb = picture_y + stridey * height;
- picture_cr = picture_cb + stridey / 2 * height / 2;
- }
-
- if (dev->devtype->product == CODA_960) {
- /*
- * The CODA960 seems to have an internal list of buffers with
- * 64 entries that includes the registered frame buffers as
- * well as the rotator buffer output.
- * ROT_INDEX needs to be < 0x40, but > ctx->num_internal_frames.
- */
- coda_write(dev, CODA_MAX_FRAMEBUFFERS + dst_buf->v4l2_buf.index,
- CODA9_CMD_DEC_PIC_ROT_INDEX);
- coda_write(dev, picture_y, CODA9_CMD_DEC_PIC_ROT_ADDR_Y);
- coda_write(dev, picture_cb, CODA9_CMD_DEC_PIC_ROT_ADDR_CB);
- coda_write(dev, picture_cr, CODA9_CMD_DEC_PIC_ROT_ADDR_CR);
- coda_write(dev, stridey, CODA9_CMD_DEC_PIC_ROT_STRIDE);
- } else {
- coda_write(dev, picture_y, CODA_CMD_DEC_PIC_ROT_ADDR_Y);
- coda_write(dev, picture_cb, CODA_CMD_DEC_PIC_ROT_ADDR_CB);
- coda_write(dev, picture_cr, CODA_CMD_DEC_PIC_ROT_ADDR_CR);
- coda_write(dev, stridey, CODA_CMD_DEC_PIC_ROT_STRIDE);
- }
- coda_write(dev, CODA_ROT_MIR_ENABLE | ctx->params.rot_mode,
- CODA_CMD_DEC_PIC_ROT_MODE);
-
- switch (dev->devtype->product) {
- case CODA_DX6:
- /* TBD */
- case CODA_7541:
- coda_write(dev, CODA_PRE_SCAN_EN, CODA_CMD_DEC_PIC_OPTION);
- break;
- case CODA_960:
- coda_write(dev, (1 << 10), CODA_CMD_DEC_PIC_OPTION); /* 'hardcode to use interrupt disable mode'? */
- break;
- }
-
- coda_write(dev, 0, CODA_CMD_DEC_PIC_SKIP_NUM);
-
- coda_write(dev, 0, CODA_CMD_DEC_PIC_BB_START);
- coda_write(dev, 0, CODA_CMD_DEC_PIC_START_BYTE);
-
- return 0;
-}
-
-static void coda_prepare_encode(struct coda_ctx *ctx)
-{
- struct coda_q_data *q_data_src, *q_data_dst;
- struct vb2_buffer *src_buf, *dst_buf;
- struct coda_dev *dev = ctx->dev;
- int force_ipicture;
- int quant_param = 0;
- u32 picture_y, picture_cb, picture_cr;
- u32 pic_stream_buffer_addr, pic_stream_buffer_size;
- u32 dst_fourcc;
-
- src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
- dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
- q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
- q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
- dst_fourcc = q_data_dst->fourcc;
-
- src_buf->v4l2_buf.sequence = ctx->osequence;
- dst_buf->v4l2_buf.sequence = ctx->osequence;
- ctx->osequence++;
-
- /*
- * Workaround coda firmware BUG that only marks the first
- * frame as IDR. This is a problem for some decoders that can't
- * recover when a frame is lost.
- */
- if (src_buf->v4l2_buf.sequence % ctx->params.gop_size) {
- src_buf->v4l2_buf.flags |= V4L2_BUF_FLAG_PFRAME;
- src_buf->v4l2_buf.flags &= ~V4L2_BUF_FLAG_KEYFRAME;
- } else {
- src_buf->v4l2_buf.flags |= V4L2_BUF_FLAG_KEYFRAME;
- src_buf->v4l2_buf.flags &= ~V4L2_BUF_FLAG_PFRAME;
- }
-
- if (dev->devtype->product == CODA_960)
- coda_set_gdi_regs(ctx);
-
- /*
- * Copy headers at the beginning of the first frame for H.264 only.
- * In MPEG4 they are already copied by the coda.
- */
- if (src_buf->v4l2_buf.sequence == 0) {
- pic_stream_buffer_addr =
- vb2_dma_contig_plane_dma_addr(dst_buf, 0) +
- ctx->vpu_header_size[0] +
- ctx->vpu_header_size[1] +
- ctx->vpu_header_size[2];
- pic_stream_buffer_size = CODA_MAX_FRAME_SIZE -
- ctx->vpu_header_size[0] -
- ctx->vpu_header_size[1] -
- ctx->vpu_header_size[2];
- memcpy(vb2_plane_vaddr(dst_buf, 0),
- &ctx->vpu_header[0][0], ctx->vpu_header_size[0]);
- memcpy(vb2_plane_vaddr(dst_buf, 0) + ctx->vpu_header_size[0],
- &ctx->vpu_header[1][0], ctx->vpu_header_size[1]);
- memcpy(vb2_plane_vaddr(dst_buf, 0) + ctx->vpu_header_size[0] +
- ctx->vpu_header_size[1], &ctx->vpu_header[2][0],
- ctx->vpu_header_size[2]);
- } else {
- pic_stream_buffer_addr =
- vb2_dma_contig_plane_dma_addr(dst_buf, 0);
- pic_stream_buffer_size = CODA_MAX_FRAME_SIZE;
- }
-
- if (src_buf->v4l2_buf.flags & V4L2_BUF_FLAG_KEYFRAME) {
- force_ipicture = 1;
- switch (dst_fourcc) {
- case V4L2_PIX_FMT_H264:
- quant_param = ctx->params.h264_intra_qp;
- break;
- case V4L2_PIX_FMT_MPEG4:
- quant_param = ctx->params.mpeg4_intra_qp;
- break;
- default:
- v4l2_warn(&ctx->dev->v4l2_dev,
- "cannot set intra qp, fmt not supported\n");
- break;
- }
- } else {
- force_ipicture = 0;
- switch (dst_fourcc) {
- case V4L2_PIX_FMT_H264:
- quant_param = ctx->params.h264_inter_qp;
- break;
- case V4L2_PIX_FMT_MPEG4:
- quant_param = ctx->params.mpeg4_inter_qp;
- break;
- default:
- v4l2_warn(&ctx->dev->v4l2_dev,
- "cannot set inter qp, fmt not supported\n");
- break;
- }
- }
-
- /* submit */
- coda_write(dev, CODA_ROT_MIR_ENABLE | ctx->params.rot_mode, CODA_CMD_ENC_PIC_ROT_MODE);
- coda_write(dev, quant_param, CODA_CMD_ENC_PIC_QS);
-
-
- picture_y = vb2_dma_contig_plane_dma_addr(src_buf, 0);
- switch (q_data_src->fourcc) {
- case V4L2_PIX_FMT_YVU420:
- /* Switch Cb and Cr for YVU420 format */
- picture_cr = picture_y + q_data_src->bytesperline *
- q_data_src->height;
- picture_cb = picture_cr + q_data_src->bytesperline / 2 *
- q_data_src->height / 2;
- break;
- case V4L2_PIX_FMT_YUV420:
- default:
- picture_cb = picture_y + q_data_src->bytesperline *
- q_data_src->height;
- picture_cr = picture_cb + q_data_src->bytesperline / 2 *
- q_data_src->height / 2;
- break;
- }
-
- if (dev->devtype->product == CODA_960) {
- coda_write(dev, 4/*FIXME: 0*/, CODA9_CMD_ENC_PIC_SRC_INDEX);
- coda_write(dev, q_data_src->width, CODA9_CMD_ENC_PIC_SRC_STRIDE);
- coda_write(dev, 0, CODA9_CMD_ENC_PIC_SUB_FRAME_SYNC);
-
- coda_write(dev, picture_y, CODA9_CMD_ENC_PIC_SRC_ADDR_Y);
- coda_write(dev, picture_cb, CODA9_CMD_ENC_PIC_SRC_ADDR_CB);
- coda_write(dev, picture_cr, CODA9_CMD_ENC_PIC_SRC_ADDR_CR);
- } else {
- coda_write(dev, picture_y, CODA_CMD_ENC_PIC_SRC_ADDR_Y);
- coda_write(dev, picture_cb, CODA_CMD_ENC_PIC_SRC_ADDR_CB);
- coda_write(dev, picture_cr, CODA_CMD_ENC_PIC_SRC_ADDR_CR);
- }
- coda_write(dev, force_ipicture << 1 & 0x2,
- CODA_CMD_ENC_PIC_OPTION);
-
- coda_write(dev, pic_stream_buffer_addr, CODA_CMD_ENC_PIC_BB_START);
- coda_write(dev, pic_stream_buffer_size / 1024,
- CODA_CMD_ENC_PIC_BB_SIZE);
-
- if (!ctx->streamon_out) {
- /* After streamoff on the output side, set the stream end flag */
- ctx->bit_stream_param |= CODA_BIT_STREAM_END_FLAG;
- coda_write(dev, ctx->bit_stream_param, CODA_REG_BIT_BIT_STREAM_PARAM);
- }
-}
-
-static void coda_device_run(void *m2m_priv)
-{
- struct coda_ctx *ctx = m2m_priv;
- struct coda_dev *dev = ctx->dev;
-
- queue_work(dev->workqueue, &ctx->pic_run_work);
-}
-
-static void coda_free_framebuffers(struct coda_ctx *ctx);
-static void coda_free_context_buffers(struct coda_ctx *ctx);
-
-static void coda_seq_end_work(struct work_struct *work)
-{
- struct coda_ctx *ctx = container_of(work, struct coda_ctx, seq_end_work);
- struct coda_dev *dev = ctx->dev;
-
- mutex_lock(&ctx->buffer_mutex);
- mutex_lock(&dev->coda_mutex);
-
- v4l2_dbg(1, coda_debug, &dev->v4l2_dev,
- "%d: %s: sent command 'SEQ_END' to coda\n", ctx->idx, __func__);
- if (coda_command_sync(ctx, CODA_COMMAND_SEQ_END)) {
- v4l2_err(&dev->v4l2_dev,
- "CODA_COMMAND_SEQ_END failed\n");
- }
-
- kfifo_init(&ctx->bitstream_fifo,
- ctx->bitstream.vaddr, ctx->bitstream.size);
-
- coda_free_framebuffers(ctx);
- coda_free_context_buffers(ctx);
-
- mutex_unlock(&dev->coda_mutex);
- mutex_unlock(&ctx->buffer_mutex);
-}
-
-static void coda_finish_decode(struct coda_ctx *ctx);
-static void coda_finish_encode(struct coda_ctx *ctx);
-
-static void coda_pic_run_work(struct work_struct *work)
-{
- struct coda_ctx *ctx = container_of(work, struct coda_ctx, pic_run_work);
- struct coda_dev *dev = ctx->dev;
- int ret;
-
- mutex_lock(&ctx->buffer_mutex);
- mutex_lock(&dev->coda_mutex);
-
- if (ctx->inst_type == CODA_INST_DECODER) {
- ret = coda_prepare_decode(ctx);
- if (ret < 0) {
- mutex_unlock(&dev->coda_mutex);
- mutex_unlock(&ctx->buffer_mutex);
- /* job_finish scheduled by prepare_decode */
- return;
- }
- } else {
- coda_prepare_encode(ctx);
- }
-
- if (dev->devtype->product != CODA_DX6)
- coda_write(dev, ctx->iram_info.axi_sram_use,
- CODA7_REG_BIT_AXI_SRAM_USE);
-
- if (ctx->inst_type == CODA_INST_DECODER)
- coda_kfifo_sync_to_device_full(ctx);
- coda_command_async(ctx, CODA_COMMAND_PIC_RUN);
-
- if (!wait_for_completion_timeout(&ctx->completion, msecs_to_jiffies(1000))) {
- dev_err(&dev->plat_dev->dev, "CODA PIC_RUN timeout\n");
-
- ctx->hold = true;
-
- coda_hw_reset(ctx);
- } else if (!ctx->aborting) {
- if (ctx->inst_type == CODA_INST_DECODER)
- coda_finish_decode(ctx);
- else
- coda_finish_encode(ctx);
- }
-
- if (ctx->aborting || (!ctx->streamon_cap && !ctx->streamon_out))
- queue_work(dev->workqueue, &ctx->seq_end_work);
-
- mutex_unlock(&dev->coda_mutex);
- mutex_unlock(&ctx->buffer_mutex);
-
- v4l2_m2m_job_finish(ctx->dev->m2m_dev, ctx->fh.m2m_ctx);
-}
-
-static int coda_job_ready(void *m2m_priv)
-{
- struct coda_ctx *ctx = m2m_priv;
-
- /*
- * For both 'P' and 'key' frame cases 1 picture
- * and 1 frame are needed. In the decoder case,
- * the compressed frame can be in the bitstream.
- */
- if (!v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx) &&
- ctx->inst_type != CODA_INST_DECODER) {
- v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
- "not ready: not enough video buffers.\n");
- return 0;
- }
-
- if (!v4l2_m2m_num_dst_bufs_ready(ctx->fh.m2m_ctx)) {
- v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
- "not ready: not enough video capture buffers.\n");
- return 0;
- }
-
- if (ctx->hold ||
- ((ctx->inst_type == CODA_INST_DECODER) &&
- (coda_get_bitstream_payload(ctx) < 512) &&
- !(ctx->bit_stream_param & CODA_BIT_STREAM_END_FLAG))) {
- v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
- "%d: not ready: not enough bitstream data.\n",
- ctx->idx);
- return 0;
- }
-
- if (ctx->aborting) {
- v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
- "not ready: aborting\n");
- return 0;
- }
-
- v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
- "job ready\n");
- return 1;
-}
-
-static void coda_job_abort(void *priv)
-{
- struct coda_ctx *ctx = priv;
-
- ctx->aborting = 1;
-
- v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
- "Aborting task\n");
-}
-
-static void coda_lock(void *m2m_priv)
-{
- struct coda_ctx *ctx = m2m_priv;
- struct coda_dev *pcdev = ctx->dev;
- mutex_lock(&pcdev->dev_mutex);
-}
-
-static void coda_unlock(void *m2m_priv)
-{
- struct coda_ctx *ctx = m2m_priv;
- struct coda_dev *pcdev = ctx->dev;
- mutex_unlock(&pcdev->dev_mutex);
-}
-
-static struct v4l2_m2m_ops coda_m2m_ops = {
- .device_run = coda_device_run,
- .job_ready = coda_job_ready,
- .job_abort = coda_job_abort,
- .lock = coda_lock,
- .unlock = coda_unlock,
-};
-
-static void coda_set_tiled_map_type(struct coda_ctx *ctx, int tiled_map_type)
-{
- struct gdi_tiled_map *tiled_map = &ctx->tiled_map;
- int luma_map, chro_map, i;
-
- memset(tiled_map, 0, sizeof(*tiled_map));
-
- luma_map = 64;
- chro_map = 64;
- tiled_map->map_type = tiled_map_type;
- for (i = 0; i < 16; i++)
- tiled_map->xy2ca_map[i] = luma_map << 8 | chro_map;
- for (i = 0; i < 4; i++)
- tiled_map->xy2ba_map[i] = luma_map << 8 | chro_map;
- for (i = 0; i < 16; i++)
- tiled_map->xy2ra_map[i] = luma_map << 8 | chro_map;
-
- if (tiled_map_type == GDI_LINEAR_FRAME_MAP) {
- tiled_map->xy2rbc_config = 0;
- } else {
- dev_err(&ctx->dev->plat_dev->dev, "invalid map type: %d\n",
- tiled_map_type);
- return;
- }
-}
-
-static void set_default_params(struct coda_ctx *ctx)
-{
- int max_w;
- int max_h;
-
- ctx->codec = &ctx->dev->devtype->codecs[0];
- max_w = ctx->codec->max_w;
- max_h = ctx->codec->max_h;
-
- ctx->params.codec_mode = CODA_MODE_INVALID;
- ctx->colorspace = V4L2_COLORSPACE_REC709;
- ctx->params.framerate = 30;
- ctx->aborting = 0;
-
- /* Default formats for output and input queues */
- ctx->q_data[V4L2_M2M_SRC].fourcc = ctx->codec->src_fourcc;
- ctx->q_data[V4L2_M2M_DST].fourcc = ctx->codec->dst_fourcc;
- ctx->q_data[V4L2_M2M_SRC].width = max_w;
- ctx->q_data[V4L2_M2M_SRC].height = max_h;
- ctx->q_data[V4L2_M2M_SRC].bytesperline = max_w;
- ctx->q_data[V4L2_M2M_SRC].sizeimage = (max_w * max_h * 3) / 2;
- ctx->q_data[V4L2_M2M_DST].width = max_w;
- ctx->q_data[V4L2_M2M_DST].height = max_h;
- ctx->q_data[V4L2_M2M_DST].bytesperline = 0;
- ctx->q_data[V4L2_M2M_DST].sizeimage = CODA_MAX_FRAME_SIZE;
- ctx->q_data[V4L2_M2M_SRC].rect.width = max_w;
- ctx->q_data[V4L2_M2M_SRC].rect.height = max_h;
- ctx->q_data[V4L2_M2M_DST].rect.width = max_w;
- ctx->q_data[V4L2_M2M_DST].rect.height = max_h;
-
- if (ctx->dev->devtype->product == CODA_960)
- coda_set_tiled_map_type(ctx, GDI_LINEAR_FRAME_MAP);
-}
-
-/*
- * Queue operations
- */
-static int coda_queue_setup(struct vb2_queue *vq,
- const struct v4l2_format *fmt,
- unsigned int *nbuffers, unsigned int *nplanes,
- unsigned int sizes[], void *alloc_ctxs[])
-{
- struct coda_ctx *ctx = vb2_get_drv_priv(vq);
- struct coda_q_data *q_data;
- unsigned int size;
-
- q_data = get_q_data(ctx, vq->type);
- size = q_data->sizeimage;
-
- *nplanes = 1;
- sizes[0] = size;
-
- alloc_ctxs[0] = ctx->dev->alloc_ctx;
-
- v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
- "get %d buffer(s) of size %d each.\n", *nbuffers, size);
-
- return 0;
-}
-
-static int coda_buf_prepare(struct vb2_buffer *vb)
-{
- struct coda_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
- struct coda_q_data *q_data;
-
- q_data = get_q_data(ctx, vb->vb2_queue->type);
-
- if (vb2_plane_size(vb, 0) < q_data->sizeimage) {
- v4l2_warn(&ctx->dev->v4l2_dev,
- "%s data will not fit into plane (%lu < %lu)\n",
- __func__, vb2_plane_size(vb, 0),
- (long)q_data->sizeimage);
- return -EINVAL;
- }
-
- return 0;
-}
-
-static void coda_buf_queue(struct vb2_buffer *vb)
-{
- struct coda_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
- struct coda_dev *dev = ctx->dev;
- struct coda_q_data *q_data;
-
- q_data = get_q_data(ctx, vb->vb2_queue->type);
-
- /*
- * In the decoder case, immediately try to copy the buffer into the
- * bitstream ringbuffer and mark it as ready to be dequeued.
- */
- if (q_data->fourcc == V4L2_PIX_FMT_H264 &&
- vb->vb2_queue->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
- /*
- * For backwards compatibility, queuing an empty buffer marks
- * the stream end
- */
- if (vb2_get_plane_payload(vb, 0) == 0) {
- ctx->bit_stream_param |= CODA_BIT_STREAM_END_FLAG;
- if ((dev->devtype->product == CODA_960) &&
- coda_isbusy(dev) &&
- (ctx->idx == coda_read(dev, CODA_REG_BIT_RUN_INDEX))) {
- /* if this decoder instance is running, set the stream end flag */
- coda_write(dev, ctx->bit_stream_param, CODA_REG_BIT_BIT_STREAM_PARAM);
- }
- }
- mutex_lock(&ctx->bitstream_mutex);
- v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vb);
- coda_fill_bitstream(ctx);
- mutex_unlock(&ctx->bitstream_mutex);
- } else {
- v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vb);
- }
-}
-
-static void coda_parabuf_write(struct coda_ctx *ctx, int index, u32 value)
-{
- struct coda_dev *dev = ctx->dev;
- u32 *p = ctx->parabuf.vaddr;
-
- if (dev->devtype->product == CODA_DX6)
- p[index] = value;
- else
- p[index ^ 1] = value;
-}
-
-static int coda_alloc_aux_buf(struct coda_dev *dev,
- struct coda_aux_buf *buf, size_t size,
- const char *name, struct dentry *parent)
-{
- buf->vaddr = dma_alloc_coherent(&dev->plat_dev->dev, size, &buf->paddr,
- GFP_KERNEL);
- if (!buf->vaddr)
- return -ENOMEM;
-
- buf->size = size;
-
- if (name && parent) {
- buf->blob.data = buf->vaddr;
- buf->blob.size = size;
- buf->dentry = debugfs_create_blob(name, 0644, parent, &buf->blob);
- if (!buf->dentry)
- dev_warn(&dev->plat_dev->dev,
- "failed to create debugfs entry %s\n", name);
- }
-
- return 0;
-}
-
-static inline int coda_alloc_context_buf(struct coda_ctx *ctx,
- struct coda_aux_buf *buf, size_t size,
- const char *name)
-{
- return coda_alloc_aux_buf(ctx->dev, buf, size, name, ctx->debugfs_entry);
-}
-
-static void coda_free_aux_buf(struct coda_dev *dev,
- struct coda_aux_buf *buf)
-{
- if (buf->vaddr) {
- dma_free_coherent(&dev->plat_dev->dev, buf->size,
- buf->vaddr, buf->paddr);
- buf->vaddr = NULL;
- buf->size = 0;
- }
- debugfs_remove(buf->dentry);
-}
-
-static void coda_free_framebuffers(struct coda_ctx *ctx)
-{
- int i;
-
- for (i = 0; i < CODA_MAX_FRAMEBUFFERS; i++)
- coda_free_aux_buf(ctx->dev, &ctx->internal_frames[i]);
-}
-
-static int coda_alloc_framebuffers(struct coda_ctx *ctx, struct coda_q_data *q_data, u32 fourcc)
-{
- struct coda_dev *dev = ctx->dev;
- int width, height;
- dma_addr_t paddr;
- int ysize;
- int ret;
- int i;
-
- if (ctx->codec && (ctx->codec->src_fourcc == V4L2_PIX_FMT_H264 ||
- ctx->codec->dst_fourcc == V4L2_PIX_FMT_H264)) {
- width = round_up(q_data->width, 16);
- height = round_up(q_data->height, 16);
- } else {
- width = round_up(q_data->width, 8);
- height = q_data->height;
- }
- ysize = width * height;
-
- /* Allocate frame buffers */
- for (i = 0; i < ctx->num_internal_frames; i++) {
- size_t size;
- char *name;
-
- size = ysize + ysize / 2;
- if (ctx->codec->src_fourcc == V4L2_PIX_FMT_H264 &&
- dev->devtype->product != CODA_DX6)
- size += ysize / 4;
- name = kasprintf(GFP_KERNEL, "fb%d", i);
- ret = coda_alloc_context_buf(ctx, &ctx->internal_frames[i],
- size, name);
- kfree(name);
- if (ret < 0) {
- coda_free_framebuffers(ctx);
- return ret;
- }
- }
-
- /* Register frame buffers in the parameter buffer */
- for (i = 0; i < ctx->num_internal_frames; i++) {
- paddr = ctx->internal_frames[i].paddr;
- coda_parabuf_write(ctx, i * 3 + 0, paddr); /* Y */
- coda_parabuf_write(ctx, i * 3 + 1, paddr + ysize); /* Cb */
- coda_parabuf_write(ctx, i * 3 + 2, paddr + ysize + ysize/4); /* Cr */
-
- /* mvcol buffer for h.264 */
- if (ctx->codec->src_fourcc == V4L2_PIX_FMT_H264 &&
- dev->devtype->product != CODA_DX6)
- coda_parabuf_write(ctx, 96 + i,
- ctx->internal_frames[i].paddr +
- ysize + ysize/4 + ysize/4);
- }
-
- /* mvcol buffer for mpeg4 */
- if ((dev->devtype->product != CODA_DX6) &&
- (ctx->codec->src_fourcc == V4L2_PIX_FMT_MPEG4))
- coda_parabuf_write(ctx, 97, ctx->internal_frames[i].paddr +
- ysize + ysize/4 + ysize/4);
-
- return 0;
-}
-
-static int coda_h264_padding(int size, char *p)
-{
- int nal_size;
- int diff;
-
- diff = size - (size & ~0x7);
- if (diff == 0)
- return 0;
-
- nal_size = coda_filler_size[diff];
- memcpy(p, coda_filler_nal, nal_size);
-
- /* Add rbsp stop bit and trailing at the end */
- *(p + nal_size - 1) = 0x80;
-
- return nal_size;
-}
-
-static phys_addr_t coda_iram_alloc(struct coda_iram_info *iram, size_t size)
-{
- phys_addr_t ret;
-
- size = round_up(size, 1024);
- if (size > iram->remaining)
- return 0;
- iram->remaining -= size;
-
- ret = iram->next_paddr;
- iram->next_paddr += size;
-
- return ret;
-}
-
-static void coda_setup_iram(struct coda_ctx *ctx)
-{
- struct coda_iram_info *iram_info = &ctx->iram_info;
- struct coda_dev *dev = ctx->dev;
- int mb_width;
- int dbk_bits;
- int bit_bits;
- int ip_bits;
-
- memset(iram_info, 0, sizeof(*iram_info));
- iram_info->next_paddr = dev->iram.paddr;
- iram_info->remaining = dev->iram.size;
-
- switch (dev->devtype->product) {
- case CODA_7541:
- dbk_bits = CODA7_USE_HOST_DBK_ENABLE | CODA7_USE_DBK_ENABLE;
- bit_bits = CODA7_USE_HOST_BIT_ENABLE | CODA7_USE_BIT_ENABLE;
- ip_bits = CODA7_USE_HOST_IP_ENABLE | CODA7_USE_IP_ENABLE;
- break;
- case CODA_960:
- dbk_bits = CODA9_USE_HOST_DBK_ENABLE | CODA9_USE_DBK_ENABLE;
- bit_bits = CODA9_USE_HOST_BIT_ENABLE | CODA7_USE_BIT_ENABLE;
- ip_bits = CODA9_USE_HOST_IP_ENABLE | CODA7_USE_IP_ENABLE;
- break;
- default: /* CODA_DX6 */
- return;
- }
-
- if (ctx->inst_type == CODA_INST_ENCODER) {
- struct coda_q_data *q_data_src;
-
- q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
- mb_width = DIV_ROUND_UP(q_data_src->width, 16);
-
- /* Prioritize in case IRAM is too small for everything */
- if (dev->devtype->product == CODA_7541) {
- iram_info->search_ram_size = round_up(mb_width * 16 *
- 36 + 2048, 1024);
- iram_info->search_ram_paddr = coda_iram_alloc(iram_info,
- iram_info->search_ram_size);
- if (!iram_info->search_ram_paddr) {
- pr_err("IRAM is smaller than the search ram size\n");
- goto out;
- }
- iram_info->axi_sram_use |= CODA7_USE_HOST_ME_ENABLE |
- CODA7_USE_ME_ENABLE;
- }
-
- /* Only H.264BP and H.263P3 are considered */
- iram_info->buf_dbk_y_use = coda_iram_alloc(iram_info, 64 * mb_width);
- iram_info->buf_dbk_c_use = coda_iram_alloc(iram_info, 64 * mb_width);
- if (!iram_info->buf_dbk_c_use)
- goto out;
- iram_info->axi_sram_use |= dbk_bits;
-
- iram_info->buf_bit_use = coda_iram_alloc(iram_info, 128 * mb_width);
- if (!iram_info->buf_bit_use)
- goto out;
- iram_info->axi_sram_use |= bit_bits;
-
- iram_info->buf_ip_ac_dc_use = coda_iram_alloc(iram_info, 128 * mb_width);
- if (!iram_info->buf_ip_ac_dc_use)
- goto out;
- iram_info->axi_sram_use |= ip_bits;
-
- /* OVL and BTP disabled for encoder */
- } else if (ctx->inst_type == CODA_INST_DECODER) {
- struct coda_q_data *q_data_dst;
-
- q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
- mb_width = DIV_ROUND_UP(q_data_dst->width, 16);
-
- iram_info->buf_dbk_y_use = coda_iram_alloc(iram_info, 128 * mb_width);
- iram_info->buf_dbk_c_use = coda_iram_alloc(iram_info, 128 * mb_width);
- if (!iram_info->buf_dbk_c_use)
- goto out;
- iram_info->axi_sram_use |= dbk_bits;
-
- iram_info->buf_bit_use = coda_iram_alloc(iram_info, 128 * mb_width);
- if (!iram_info->buf_bit_use)
- goto out;
- iram_info->axi_sram_use |= bit_bits;
-
- iram_info->buf_ip_ac_dc_use = coda_iram_alloc(iram_info, 128 * mb_width);
- if (!iram_info->buf_ip_ac_dc_use)
- goto out;
- iram_info->axi_sram_use |= ip_bits;
-
- /* OVL and BTP unused as there is no VC1 support yet */
- }
-
-out:
- if (!(iram_info->axi_sram_use & CODA7_USE_HOST_IP_ENABLE))
- v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
- "IRAM smaller than needed\n");
-
- if (dev->devtype->product == CODA_7541) {
- /* TODO - Enabling these causes picture errors on CODA7541 */
- if (ctx->inst_type == CODA_INST_DECODER) {
- /* fw 1.4.50 */
- iram_info->axi_sram_use &= ~(CODA7_USE_HOST_IP_ENABLE |
- CODA7_USE_IP_ENABLE);
- } else {
- /* fw 13.4.29 */
- iram_info->axi_sram_use &= ~(CODA7_USE_HOST_IP_ENABLE |
- CODA7_USE_HOST_DBK_ENABLE |
- CODA7_USE_IP_ENABLE |
- CODA7_USE_DBK_ENABLE);
- }
- }
-}
-
-static void coda_free_context_buffers(struct coda_ctx *ctx)
-{
- struct coda_dev *dev = ctx->dev;
-
- coda_free_aux_buf(dev, &ctx->slicebuf);
- coda_free_aux_buf(dev, &ctx->psbuf);
- if (dev->devtype->product != CODA_DX6)
- coda_free_aux_buf(dev, &ctx->workbuf);
-}
-
-static int coda_alloc_context_buffers(struct coda_ctx *ctx,
- struct coda_q_data *q_data)
-{
- struct coda_dev *dev = ctx->dev;
- size_t size;
- int ret;
-
- if (dev->devtype->product == CODA_DX6)
- return 0;
-
- if (ctx->psbuf.vaddr) {
- v4l2_err(&dev->v4l2_dev, "psmembuf still allocated\n");
- return -EBUSY;
- }
- if (ctx->slicebuf.vaddr) {
- v4l2_err(&dev->v4l2_dev, "slicebuf still allocated\n");
- return -EBUSY;
- }
- if (ctx->workbuf.vaddr) {
- v4l2_err(&dev->v4l2_dev, "context buffer still allocated\n");
- ret = -EBUSY;
- return -ENOMEM;
- }
-
- if (q_data->fourcc == V4L2_PIX_FMT_H264) {
- /* worst case slice size */
- size = (DIV_ROUND_UP(q_data->width, 16) *
- DIV_ROUND_UP(q_data->height, 16)) * 3200 / 8 + 512;
- ret = coda_alloc_context_buf(ctx, &ctx->slicebuf, size, "slicebuf");
- if (ret < 0) {
- v4l2_err(&dev->v4l2_dev, "failed to allocate %d byte slice buffer",
- ctx->slicebuf.size);
- return ret;
- }
- }
-
- if (dev->devtype->product == CODA_7541) {
- ret = coda_alloc_context_buf(ctx, &ctx->psbuf, CODA7_PS_BUF_SIZE, "psbuf");
- if (ret < 0) {
- v4l2_err(&dev->v4l2_dev, "failed to allocate psmem buffer");
- goto err;
- }
- }
-
- size = dev->devtype->workbuf_size;
- if (dev->devtype->product == CODA_960 &&
- q_data->fourcc == V4L2_PIX_FMT_H264)
- size += CODA9_PS_SAVE_SIZE;
- ret = coda_alloc_context_buf(ctx, &ctx->workbuf, size, "workbuf");
- if (ret < 0) {
- v4l2_err(&dev->v4l2_dev, "failed to allocate %d byte context buffer",
- ctx->workbuf.size);
- goto err;
- }
-
- return 0;
-
-err:
- coda_free_context_buffers(ctx);
- return ret;
-}
-
-static int coda_start_decoding(struct coda_ctx *ctx)
-{
- struct coda_q_data *q_data_src, *q_data_dst;
- u32 bitstream_buf, bitstream_size;
- struct coda_dev *dev = ctx->dev;
- int width, height;
- u32 src_fourcc;
- u32 val;
- int ret;
-
- /* Start decoding */
- q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
- q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
- bitstream_buf = ctx->bitstream.paddr;
- bitstream_size = ctx->bitstream.size;
- src_fourcc = q_data_src->fourcc;
-
- coda_write(dev, ctx->parabuf.paddr, CODA_REG_BIT_PARA_BUF_ADDR);
-
- /* Update coda bitstream read and write pointers from kfifo */
- coda_kfifo_sync_to_device_full(ctx);
-
- ctx->display_idx = -1;
- ctx->frm_dis_flg = 0;
- coda_write(dev, 0, CODA_REG_BIT_FRM_DIS_FLG(ctx->reg_idx));
-
- coda_write(dev, CODA_BIT_DEC_SEQ_INIT_ESCAPE,
- CODA_REG_BIT_BIT_STREAM_PARAM);
-
- coda_write(dev, bitstream_buf, CODA_CMD_DEC_SEQ_BB_START);
- coda_write(dev, bitstream_size / 1024, CODA_CMD_DEC_SEQ_BB_SIZE);
- val = 0;
- if ((dev->devtype->product == CODA_7541) ||
- (dev->devtype->product == CODA_960))
- val |= CODA_REORDER_ENABLE;
- coda_write(dev, val, CODA_CMD_DEC_SEQ_OPTION);
-
- ctx->params.codec_mode = ctx->codec->mode;
- if (dev->devtype->product == CODA_960 &&
- src_fourcc == V4L2_PIX_FMT_MPEG4)
- ctx->params.codec_mode_aux = CODA_MP4_AUX_MPEG4;
- else
- ctx->params.codec_mode_aux = 0;
- if (src_fourcc == V4L2_PIX_FMT_H264) {
- if (dev->devtype->product == CODA_7541) {
- coda_write(dev, ctx->psbuf.paddr,
- CODA_CMD_DEC_SEQ_PS_BB_START);
- coda_write(dev, (CODA7_PS_BUF_SIZE / 1024),
- CODA_CMD_DEC_SEQ_PS_BB_SIZE);
- }
- if (dev->devtype->product == CODA_960) {
- coda_write(dev, 0, CODA_CMD_DEC_SEQ_X264_MV_EN);
- coda_write(dev, 512, CODA_CMD_DEC_SEQ_SPP_CHUNK_SIZE);
- }
- }
- if (dev->devtype->product != CODA_960) {
- coda_write(dev, 0, CODA_CMD_DEC_SEQ_SRC_SIZE);
- }
-
- if (coda_command_sync(ctx, CODA_COMMAND_SEQ_INIT)) {
- v4l2_err(&dev->v4l2_dev, "CODA_COMMAND_SEQ_INIT timeout\n");
- coda_write(dev, 0, CODA_REG_BIT_BIT_STREAM_PARAM);
- return -ETIMEDOUT;
- }
-
- /* Update kfifo out pointer from coda bitstream read pointer */
- coda_kfifo_sync_from_device(ctx);
-
- coda_write(dev, 0, CODA_REG_BIT_BIT_STREAM_PARAM);
-
- if (coda_read(dev, CODA_RET_DEC_SEQ_SUCCESS) == 0) {
- v4l2_err(&dev->v4l2_dev,
- "CODA_COMMAND_SEQ_INIT failed, error code = %d\n",
- coda_read(dev, CODA_RET_DEC_SEQ_ERR_REASON));
- return -EAGAIN;
- }
-
- val = coda_read(dev, CODA_RET_DEC_SEQ_SRC_SIZE);
- if (dev->devtype->product == CODA_DX6) {
- width = (val >> CODADX6_PICWIDTH_OFFSET) & CODADX6_PICWIDTH_MASK;
- height = val & CODADX6_PICHEIGHT_MASK;
- } else {
- width = (val >> CODA7_PICWIDTH_OFFSET) & CODA7_PICWIDTH_MASK;
- height = val & CODA7_PICHEIGHT_MASK;
- }
-
- if (width > q_data_dst->width || height > q_data_dst->height) {
- v4l2_err(&dev->v4l2_dev, "stream is %dx%d, not %dx%d\n",
- width, height, q_data_dst->width, q_data_dst->height);
- return -EINVAL;
- }
-
- width = round_up(width, 16);
- height = round_up(height, 16);
-
- v4l2_dbg(1, coda_debug, &dev->v4l2_dev, "%s instance %d now: %dx%d\n",
- __func__, ctx->idx, width, height);
-
- ctx->num_internal_frames = coda_read(dev, CODA_RET_DEC_SEQ_FRAME_NEED);
- if (ctx->num_internal_frames > CODA_MAX_FRAMEBUFFERS) {
- v4l2_err(&dev->v4l2_dev,
- "not enough framebuffers to decode (%d < %d)\n",
- CODA_MAX_FRAMEBUFFERS, ctx->num_internal_frames);
- return -EINVAL;
- }
-
- if (src_fourcc == V4L2_PIX_FMT_H264) {
- u32 left_right;
- u32 top_bottom;
-
- left_right = coda_read(dev, CODA_RET_DEC_SEQ_CROP_LEFT_RIGHT);
- top_bottom = coda_read(dev, CODA_RET_DEC_SEQ_CROP_TOP_BOTTOM);
-
- q_data_dst->rect.left = (left_right >> 10) & 0x3ff;
- q_data_dst->rect.top = (top_bottom >> 10) & 0x3ff;
- q_data_dst->rect.width = width - q_data_dst->rect.left -
- (left_right & 0x3ff);
- q_data_dst->rect.height = height - q_data_dst->rect.top -
- (top_bottom & 0x3ff);
- }
-
- ret = coda_alloc_framebuffers(ctx, q_data_dst, src_fourcc);
- if (ret < 0)
- return ret;
-
- /* Tell the decoder how many frame buffers we allocated. */
- coda_write(dev, ctx->num_internal_frames, CODA_CMD_SET_FRAME_BUF_NUM);
- coda_write(dev, width, CODA_CMD_SET_FRAME_BUF_STRIDE);
-
- if (dev->devtype->product != CODA_DX6) {
- /* Set secondary AXI IRAM */
- coda_setup_iram(ctx);
-
- coda_write(dev, ctx->iram_info.buf_bit_use,
- CODA7_CMD_SET_FRAME_AXI_BIT_ADDR);
- coda_write(dev, ctx->iram_info.buf_ip_ac_dc_use,
- CODA7_CMD_SET_FRAME_AXI_IPACDC_ADDR);
- coda_write(dev, ctx->iram_info.buf_dbk_y_use,
- CODA7_CMD_SET_FRAME_AXI_DBKY_ADDR);
- coda_write(dev, ctx->iram_info.buf_dbk_c_use,
- CODA7_CMD_SET_FRAME_AXI_DBKC_ADDR);
- coda_write(dev, ctx->iram_info.buf_ovl_use,
- CODA7_CMD_SET_FRAME_AXI_OVL_ADDR);
- if (dev->devtype->product == CODA_960)
- coda_write(dev, ctx->iram_info.buf_btp_use,
- CODA9_CMD_SET_FRAME_AXI_BTP_ADDR);
- }
-
- if (dev->devtype->product == CODA_960) {
- coda_write(dev, -1, CODA9_CMD_SET_FRAME_DELAY);
-
- coda_write(dev, 0x20262024, CODA9_CMD_SET_FRAME_CACHE_SIZE);
- coda_write(dev, 2 << CODA9_CACHE_PAGEMERGE_OFFSET |
- 32 << CODA9_CACHE_LUMA_BUFFER_SIZE_OFFSET |
- 8 << CODA9_CACHE_CB_BUFFER_SIZE_OFFSET |
- 8 << CODA9_CACHE_CR_BUFFER_SIZE_OFFSET,
- CODA9_CMD_SET_FRAME_CACHE_CONFIG);
- }
-
- if (src_fourcc == V4L2_PIX_FMT_H264) {
- coda_write(dev, ctx->slicebuf.paddr,
- CODA_CMD_SET_FRAME_SLICE_BB_START);
- coda_write(dev, ctx->slicebuf.size / 1024,
- CODA_CMD_SET_FRAME_SLICE_BB_SIZE);
- }
-
- if (dev->devtype->product == CODA_7541) {
- int max_mb_x = 1920 / 16;
- int max_mb_y = 1088 / 16;
- int max_mb_num = max_mb_x * max_mb_y;
-
- coda_write(dev, max_mb_num << 16 | max_mb_x << 8 | max_mb_y,
- CODA7_CMD_SET_FRAME_MAX_DEC_SIZE);
- } else if (dev->devtype->product == CODA_960) {
- int max_mb_x = 1920 / 16;
- int max_mb_y = 1088 / 16;
- int max_mb_num = max_mb_x * max_mb_y;
-
- coda_write(dev, max_mb_num << 16 | max_mb_x << 8 | max_mb_y,
- CODA9_CMD_SET_FRAME_MAX_DEC_SIZE);
- }
-
- if (coda_command_sync(ctx, CODA_COMMAND_SET_FRAME_BUF)) {
- v4l2_err(&ctx->dev->v4l2_dev,
- "CODA_COMMAND_SET_FRAME_BUF timeout\n");
- return -ETIMEDOUT;
- }
-
- return 0;
-}
-
-static int coda_encode_header(struct coda_ctx *ctx, struct vb2_buffer *buf,
- int header_code, u8 *header, int *size)
-{
- struct coda_dev *dev = ctx->dev;
- size_t bufsize;
- int ret;
- int i;
-
- if (dev->devtype->product == CODA_960)
- memset(vb2_plane_vaddr(buf, 0), 0, 64);
-
- coda_write(dev, vb2_dma_contig_plane_dma_addr(buf, 0),
- CODA_CMD_ENC_HEADER_BB_START);
- bufsize = vb2_plane_size(buf, 0);
- if (dev->devtype->product == CODA_960)
- bufsize /= 1024;
- coda_write(dev, bufsize, CODA_CMD_ENC_HEADER_BB_SIZE);
- coda_write(dev, header_code, CODA_CMD_ENC_HEADER_CODE);
- ret = coda_command_sync(ctx, CODA_COMMAND_ENCODE_HEADER);
- if (ret < 0) {
- v4l2_err(&dev->v4l2_dev, "CODA_COMMAND_ENCODE_HEADER timeout\n");
- return ret;
- }
-
- if (dev->devtype->product == CODA_960) {
- for (i = 63; i > 0; i--)
- if (((char *)vb2_plane_vaddr(buf, 0))[i] != 0)
- break;
- *size = i + 1;
- } else {
- *size = coda_read(dev, CODA_REG_BIT_WR_PTR(ctx->reg_idx)) -
- coda_read(dev, CODA_CMD_ENC_HEADER_BB_START);
- }
- memcpy(header, vb2_plane_vaddr(buf, 0), *size);
-
- return 0;
-}
-
-static int coda_start_encoding(struct coda_ctx *ctx);
-
-static int coda_start_streaming(struct vb2_queue *q, unsigned int count)
-{
- struct coda_ctx *ctx = vb2_get_drv_priv(q);
- struct v4l2_device *v4l2_dev = &ctx->dev->v4l2_dev;
- struct coda_dev *dev = ctx->dev;
- struct coda_q_data *q_data_src, *q_data_dst;
- u32 dst_fourcc;
- int ret = 0;
-
- q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
- if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
- if (q_data_src->fourcc == V4L2_PIX_FMT_H264) {
- if (coda_get_bitstream_payload(ctx) < 512)
- return -EINVAL;
- } else {
- if (count < 1)
- return -EINVAL;
- }
-
- ctx->streamon_out = 1;
-
- if (coda_format_is_yuv(q_data_src->fourcc))
- ctx->inst_type = CODA_INST_ENCODER;
- else
- ctx->inst_type = CODA_INST_DECODER;
- } else {
- if (count < 1)
- return -EINVAL;
-
- ctx->streamon_cap = 1;
- }
-
- /* Don't start the coda unless both queues are on */
- if (!(ctx->streamon_out & ctx->streamon_cap))
- return 0;
-
- /* Allow decoder device_run with no new buffers queued */
- if (ctx->inst_type == CODA_INST_DECODER)
- v4l2_m2m_set_src_buffered(ctx->fh.m2m_ctx, true);
-
- ctx->gopcounter = ctx->params.gop_size - 1;
- q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
- dst_fourcc = q_data_dst->fourcc;
-
- ctx->codec = coda_find_codec(ctx->dev, q_data_src->fourcc,
- q_data_dst->fourcc);
- if (!ctx->codec) {
- v4l2_err(v4l2_dev, "couldn't tell instance type.\n");
- return -EINVAL;
- }
-
- /* Allocate per-instance buffers */
- ret = coda_alloc_context_buffers(ctx, q_data_src);
- if (ret < 0)
- return ret;
-
- if (ctx->inst_type == CODA_INST_DECODER) {
- mutex_lock(&dev->coda_mutex);
- ret = coda_start_decoding(ctx);
- mutex_unlock(&dev->coda_mutex);
- if (ret == -EAGAIN)
- return 0;
- else if (ret < 0)
- return ret;
- } else {
- ret = coda_start_encoding(ctx);
- }
-
- ctx->initialized = 1;
- return ret;
-}
-
-static int coda_start_encoding(struct coda_ctx *ctx)
-{
- struct coda_dev *dev = ctx->dev;
- struct v4l2_device *v4l2_dev = &dev->v4l2_dev;
- struct coda_q_data *q_data_src, *q_data_dst;
- u32 bitstream_buf, bitstream_size;
- struct vb2_buffer *buf;
- int gamma, ret, value;
- u32 dst_fourcc;
-
- q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
- q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
- dst_fourcc = q_data_dst->fourcc;
-
- buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
- bitstream_buf = vb2_dma_contig_plane_dma_addr(buf, 0);
- bitstream_size = q_data_dst->sizeimage;
-
- if (!coda_is_initialized(dev)) {
- v4l2_err(v4l2_dev, "coda is not initialized.\n");
- return -EFAULT;
- }
-
- mutex_lock(&dev->coda_mutex);
-
- coda_write(dev, ctx->parabuf.paddr, CODA_REG_BIT_PARA_BUF_ADDR);
- coda_write(dev, bitstream_buf, CODA_REG_BIT_RD_PTR(ctx->reg_idx));
- coda_write(dev, bitstream_buf, CODA_REG_BIT_WR_PTR(ctx->reg_idx));
- switch (dev->devtype->product) {
- case CODA_DX6:
- coda_write(dev, CODADX6_STREAM_BUF_DYNALLOC_EN |
- CODADX6_STREAM_BUF_PIC_RESET, CODA_REG_BIT_STREAM_CTRL);
- break;
- case CODA_960:
- coda_write(dev, 0, CODA9_GDI_WPROT_RGN_EN);
- /* fallthrough */
- case CODA_7541:
- coda_write(dev, CODA7_STREAM_BUF_DYNALLOC_EN |
- CODA7_STREAM_BUF_PIC_RESET, CODA_REG_BIT_STREAM_CTRL);
- break;
- }
-
- value = coda_read(dev, CODA_REG_BIT_FRAME_MEM_CTRL);
- value &= ~(1 << 2 | 0x7 << 9);
- ctx->frame_mem_ctrl = value;
- coda_write(dev, value, CODA_REG_BIT_FRAME_MEM_CTRL);
-
- if (dev->devtype->product == CODA_DX6) {
- /* Configure the coda */
- coda_write(dev, dev->iram.paddr, CODADX6_REG_BIT_SEARCH_RAM_BASE_ADDR);
- }
-
- /* Could set rotation here if needed */
- switch (dev->devtype->product) {
- case CODA_DX6:
- value = (q_data_src->width & CODADX6_PICWIDTH_MASK) << CODADX6_PICWIDTH_OFFSET;
- value |= (q_data_src->height & CODADX6_PICHEIGHT_MASK) << CODA_PICHEIGHT_OFFSET;
- break;
- case CODA_7541:
- if (dst_fourcc == V4L2_PIX_FMT_H264) {
- value = (round_up(q_data_src->width, 16) &
- CODA7_PICWIDTH_MASK) << CODA7_PICWIDTH_OFFSET;
- value |= (round_up(q_data_src->height, 16) &
- CODA7_PICHEIGHT_MASK) << CODA_PICHEIGHT_OFFSET;
- break;
- }
- /* fallthrough */
- case CODA_960:
- value = (q_data_src->width & CODA7_PICWIDTH_MASK) << CODA7_PICWIDTH_OFFSET;
- value |= (q_data_src->height & CODA7_PICHEIGHT_MASK) << CODA_PICHEIGHT_OFFSET;
- }
- coda_write(dev, value, CODA_CMD_ENC_SEQ_SRC_SIZE);
- coda_write(dev, ctx->params.framerate,
- CODA_CMD_ENC_SEQ_SRC_F_RATE);
-
- ctx->params.codec_mode = ctx->codec->mode;
- switch (dst_fourcc) {
- case V4L2_PIX_FMT_MPEG4:
- if (dev->devtype->product == CODA_960)
- coda_write(dev, CODA9_STD_MPEG4, CODA_CMD_ENC_SEQ_COD_STD);
- else
- coda_write(dev, CODA_STD_MPEG4, CODA_CMD_ENC_SEQ_COD_STD);
- coda_write(dev, 0, CODA_CMD_ENC_SEQ_MP4_PARA);
- break;
- case V4L2_PIX_FMT_H264:
- if (dev->devtype->product == CODA_960)
- coda_write(dev, CODA9_STD_H264, CODA_CMD_ENC_SEQ_COD_STD);
- else
- coda_write(dev, CODA_STD_H264, CODA_CMD_ENC_SEQ_COD_STD);
- if (ctx->params.h264_deblk_enabled) {
- value = ((ctx->params.h264_deblk_alpha &
- CODA_264PARAM_DEBLKFILTEROFFSETALPHA_MASK) <<
- CODA_264PARAM_DEBLKFILTEROFFSETALPHA_OFFSET) |
- ((ctx->params.h264_deblk_beta &
- CODA_264PARAM_DEBLKFILTEROFFSETBETA_MASK) <<
- CODA_264PARAM_DEBLKFILTEROFFSETBETA_OFFSET);
- } else {
- value = 1 << CODA_264PARAM_DISABLEDEBLK_OFFSET;
- }
- coda_write(dev, value, CODA_CMD_ENC_SEQ_264_PARA);
- break;
- default:
- v4l2_err(v4l2_dev,
- "dst format (0x%08x) invalid.\n", dst_fourcc);
- ret = -EINVAL;
- goto out;
- }
-
- switch (ctx->params.slice_mode) {
- case V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE:
- value = 0;
- break;
- case V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_MB:
- value = (ctx->params.slice_max_mb & CODA_SLICING_SIZE_MASK) << CODA_SLICING_SIZE_OFFSET;
- value |= (1 & CODA_SLICING_UNIT_MASK) << CODA_SLICING_UNIT_OFFSET;
- value |= 1 & CODA_SLICING_MODE_MASK;
- break;
- case V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES:
- value = (ctx->params.slice_max_bits & CODA_SLICING_SIZE_MASK) << CODA_SLICING_SIZE_OFFSET;
- value |= (0 & CODA_SLICING_UNIT_MASK) << CODA_SLICING_UNIT_OFFSET;
- value |= 1 & CODA_SLICING_MODE_MASK;
- break;
- }
- coda_write(dev, value, CODA_CMD_ENC_SEQ_SLICE_MODE);
- value = ctx->params.gop_size & CODA_GOP_SIZE_MASK;
- coda_write(dev, value, CODA_CMD_ENC_SEQ_GOP_SIZE);
-
- if (ctx->params.bitrate) {
- /* Rate control enabled */
- value = (ctx->params.bitrate & CODA_RATECONTROL_BITRATE_MASK) << CODA_RATECONTROL_BITRATE_OFFSET;
- value |= 1 & CODA_RATECONTROL_ENABLE_MASK;
- if (dev->devtype->product == CODA_960)
- value |= BIT(31); /* disable autoskip */
- } else {
- value = 0;
- }
- coda_write(dev, value, CODA_CMD_ENC_SEQ_RC_PARA);
-
- coda_write(dev, 0, CODA_CMD_ENC_SEQ_RC_BUF_SIZE);
- coda_write(dev, ctx->params.intra_refresh,
- CODA_CMD_ENC_SEQ_INTRA_REFRESH);
-
- coda_write(dev, bitstream_buf, CODA_CMD_ENC_SEQ_BB_START);
- coda_write(dev, bitstream_size / 1024, CODA_CMD_ENC_SEQ_BB_SIZE);
-
-
- value = 0;
- if (dev->devtype->product == CODA_960)
- gamma = CODA9_DEFAULT_GAMMA;
- else
- gamma = CODA_DEFAULT_GAMMA;
- if (gamma > 0) {
- coda_write(dev, (gamma & CODA_GAMMA_MASK) << CODA_GAMMA_OFFSET,
- CODA_CMD_ENC_SEQ_RC_GAMMA);
- }
-
- if (ctx->params.h264_min_qp || ctx->params.h264_max_qp) {
- coda_write(dev,
- ctx->params.h264_min_qp << CODA_QPMIN_OFFSET |
- ctx->params.h264_max_qp << CODA_QPMAX_OFFSET,
- CODA_CMD_ENC_SEQ_RC_QP_MIN_MAX);
- }
- if (dev->devtype->product == CODA_960) {
- if (ctx->params.h264_max_qp)
- value |= 1 << CODA9_OPTION_RCQPMAX_OFFSET;
- if (CODA_DEFAULT_GAMMA > 0)
- value |= 1 << CODA9_OPTION_GAMMA_OFFSET;
- } else {
- if (CODA_DEFAULT_GAMMA > 0) {
- if (dev->devtype->product == CODA_DX6)
- value |= 1 << CODADX6_OPTION_GAMMA_OFFSET;
- else
- value |= 1 << CODA7_OPTION_GAMMA_OFFSET;
- }
- if (ctx->params.h264_min_qp)
- value |= 1 << CODA7_OPTION_RCQPMIN_OFFSET;
- if (ctx->params.h264_max_qp)
- value |= 1 << CODA7_OPTION_RCQPMAX_OFFSET;
- }
- coda_write(dev, value, CODA_CMD_ENC_SEQ_OPTION);
-
- coda_write(dev, 0, CODA_CMD_ENC_SEQ_RC_INTERVAL_MODE);
-
- coda_setup_iram(ctx);
-
- if (dst_fourcc == V4L2_PIX_FMT_H264) {
- switch (dev->devtype->product) {
- case CODA_DX6:
- value = FMO_SLICE_SAVE_BUF_SIZE << 7;
- coda_write(dev, value, CODADX6_CMD_ENC_SEQ_FMO);
- break;
- case CODA_7541:
- coda_write(dev, ctx->iram_info.search_ram_paddr,
- CODA7_CMD_ENC_SEQ_SEARCH_BASE);
- coda_write(dev, ctx->iram_info.search_ram_size,
- CODA7_CMD_ENC_SEQ_SEARCH_SIZE);
- break;
- case CODA_960:
- coda_write(dev, 0, CODA9_CMD_ENC_SEQ_ME_OPTION);
- coda_write(dev, 0, CODA9_CMD_ENC_SEQ_INTRA_WEIGHT);
- }
- }
-
- ret = coda_command_sync(ctx, CODA_COMMAND_SEQ_INIT);
- if (ret < 0) {
- v4l2_err(v4l2_dev, "CODA_COMMAND_SEQ_INIT timeout\n");
- goto out;
- }
-
- if (coda_read(dev, CODA_RET_ENC_SEQ_SUCCESS) == 0) {
- v4l2_err(v4l2_dev, "CODA_COMMAND_SEQ_INIT failed\n");
- ret = -EFAULT;
- goto out;
- }
-
- if (dev->devtype->product == CODA_960)
- ctx->num_internal_frames = 4;
- else
- ctx->num_internal_frames = 2;
- ret = coda_alloc_framebuffers(ctx, q_data_src, dst_fourcc);
- if (ret < 0) {
- v4l2_err(v4l2_dev, "failed to allocate framebuffers\n");
- goto out;
- }
-
- coda_write(dev, ctx->num_internal_frames, CODA_CMD_SET_FRAME_BUF_NUM);
- coda_write(dev, q_data_src->bytesperline,
- CODA_CMD_SET_FRAME_BUF_STRIDE);
- if (dev->devtype->product == CODA_7541) {
- coda_write(dev, q_data_src->bytesperline,
- CODA7_CMD_SET_FRAME_SOURCE_BUF_STRIDE);
- }
- if (dev->devtype->product != CODA_DX6) {
- coda_write(dev, ctx->iram_info.buf_bit_use,
- CODA7_CMD_SET_FRAME_AXI_BIT_ADDR);
- coda_write(dev, ctx->iram_info.buf_ip_ac_dc_use,
- CODA7_CMD_SET_FRAME_AXI_IPACDC_ADDR);
- coda_write(dev, ctx->iram_info.buf_dbk_y_use,
- CODA7_CMD_SET_FRAME_AXI_DBKY_ADDR);
- coda_write(dev, ctx->iram_info.buf_dbk_c_use,
- CODA7_CMD_SET_FRAME_AXI_DBKC_ADDR);
- coda_write(dev, ctx->iram_info.buf_ovl_use,
- CODA7_CMD_SET_FRAME_AXI_OVL_ADDR);
- if (dev->devtype->product == CODA_960) {
- coda_write(dev, ctx->iram_info.buf_btp_use,
- CODA9_CMD_SET_FRAME_AXI_BTP_ADDR);
-
- /* FIXME */
- coda_write(dev, ctx->internal_frames[2].paddr, CODA9_CMD_SET_FRAME_SUBSAMP_A);
- coda_write(dev, ctx->internal_frames[3].paddr, CODA9_CMD_SET_FRAME_SUBSAMP_B);
- }
- }
-
- ret = coda_command_sync(ctx, CODA_COMMAND_SET_FRAME_BUF);
- if (ret < 0) {
- v4l2_err(v4l2_dev, "CODA_COMMAND_SET_FRAME_BUF timeout\n");
- goto out;
- }
-
- /* Save stream headers */
- buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
- switch (dst_fourcc) {
- case V4L2_PIX_FMT_H264:
- /*
- * Get SPS in the first frame and copy it to an
- * intermediate buffer.
- */
- ret = coda_encode_header(ctx, buf, CODA_HEADER_H264_SPS,
- &ctx->vpu_header[0][0],
- &ctx->vpu_header_size[0]);
- if (ret < 0)
- goto out;
-
- /*
- * Get PPS in the first frame and copy it to an
- * intermediate buffer.
- */
- ret = coda_encode_header(ctx, buf, CODA_HEADER_H264_PPS,
- &ctx->vpu_header[1][0],
- &ctx->vpu_header_size[1]);
- if (ret < 0)
- goto out;
-
- /*
- * Length of H.264 headers is variable and thus it might not be
- * aligned for the coda to append the encoded frame. In that is
- * the case a filler NAL must be added to header 2.
- */
- ctx->vpu_header_size[2] = coda_h264_padding(
- (ctx->vpu_header_size[0] +
- ctx->vpu_header_size[1]),
- ctx->vpu_header[2]);
- break;
- case V4L2_PIX_FMT_MPEG4:
- /*
- * Get VOS in the first frame and copy it to an
- * intermediate buffer
- */
- ret = coda_encode_header(ctx, buf, CODA_HEADER_MP4V_VOS,
- &ctx->vpu_header[0][0],
- &ctx->vpu_header_size[0]);
- if (ret < 0)
- goto out;
-
- ret = coda_encode_header(ctx, buf, CODA_HEADER_MP4V_VIS,
- &ctx->vpu_header[1][0],
- &ctx->vpu_header_size[1]);
- if (ret < 0)
- goto out;
-
- ret = coda_encode_header(ctx, buf, CODA_HEADER_MP4V_VOL,
- &ctx->vpu_header[2][0],
- &ctx->vpu_header_size[2]);
- if (ret < 0)
- goto out;
- break;
- default:
- /* No more formats need to save headers at the moment */
- break;
- }
-
-out:
- mutex_unlock(&dev->coda_mutex);
- return ret;
-}
-
-static void coda_stop_streaming(struct vb2_queue *q)
-{
- struct coda_ctx *ctx = vb2_get_drv_priv(q);
- struct coda_dev *dev = ctx->dev;
-
- if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
- v4l2_dbg(1, coda_debug, &dev->v4l2_dev,
- "%s: output\n", __func__);
- ctx->streamon_out = 0;
-
- if (ctx->inst_type == CODA_INST_DECODER &&
- coda_isbusy(dev) && ctx->idx == coda_read(dev, CODA_REG_BIT_RUN_INDEX)) {
- /* if this decoder instance is running, set the stream end flag */
- if (dev->devtype->product == CODA_960) {
- u32 val = coda_read(dev, CODA_REG_BIT_BIT_STREAM_PARAM);
-
- val |= CODA_BIT_STREAM_END_FLAG;
- coda_write(dev, val, CODA_REG_BIT_BIT_STREAM_PARAM);
- ctx->bit_stream_param = val;
- }
- }
- ctx->bit_stream_param |= CODA_BIT_STREAM_END_FLAG;
-
- ctx->isequence = 0;
- } else {
- v4l2_dbg(1, coda_debug, &dev->v4l2_dev,
- "%s: capture\n", __func__);
- ctx->streamon_cap = 0;
-
- ctx->osequence = 0;
- ctx->sequence_offset = 0;
- }
-
- if (!ctx->streamon_out && !ctx->streamon_cap) {
- struct coda_timestamp *ts;
-
- while (!list_empty(&ctx->timestamp_list)) {
- ts = list_first_entry(&ctx->timestamp_list,
- struct coda_timestamp, list);
- list_del(&ts->list);
- kfree(ts);
- }
- kfifo_init(&ctx->bitstream_fifo,
- ctx->bitstream.vaddr, ctx->bitstream.size);
- ctx->runcounter = 0;
- }
-}
-
-static struct vb2_ops coda_qops = {
- .queue_setup = coda_queue_setup,
- .buf_prepare = coda_buf_prepare,
- .buf_queue = coda_buf_queue,
- .start_streaming = coda_start_streaming,
- .stop_streaming = coda_stop_streaming,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
-};
-
-static int coda_s_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct coda_ctx *ctx =
- container_of(ctrl->handler, struct coda_ctx, ctrls);
-
- v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
- "s_ctrl: id = %d, val = %d\n", ctrl->id, ctrl->val);
-
- switch (ctrl->id) {
- case V4L2_CID_HFLIP:
- if (ctrl->val)
- ctx->params.rot_mode |= CODA_MIR_HOR;
- else
- ctx->params.rot_mode &= ~CODA_MIR_HOR;
- break;
- case V4L2_CID_VFLIP:
- if (ctrl->val)
- ctx->params.rot_mode |= CODA_MIR_VER;
- else
- ctx->params.rot_mode &= ~CODA_MIR_VER;
- break;
- case V4L2_CID_MPEG_VIDEO_BITRATE:
- ctx->params.bitrate = ctrl->val / 1000;
- break;
- case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
- ctx->params.gop_size = ctrl->val;
- break;
- case V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP:
- ctx->params.h264_intra_qp = ctrl->val;
- break;
- case V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP:
- ctx->params.h264_inter_qp = ctrl->val;
- break;
- case V4L2_CID_MPEG_VIDEO_H264_MIN_QP:
- ctx->params.h264_min_qp = ctrl->val;
- break;
- case V4L2_CID_MPEG_VIDEO_H264_MAX_QP:
- ctx->params.h264_max_qp = ctrl->val;
- break;
- case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA:
- ctx->params.h264_deblk_alpha = ctrl->val;
- break;
- case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA:
- ctx->params.h264_deblk_beta = ctrl->val;
- break;
- case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE:
- ctx->params.h264_deblk_enabled = (ctrl->val ==
- V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_ENABLED);
- break;
- case V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP:
- ctx->params.mpeg4_intra_qp = ctrl->val;
- break;
- case V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP:
- ctx->params.mpeg4_inter_qp = ctrl->val;
- break;
- case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE:
- ctx->params.slice_mode = ctrl->val;
- break;
- case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB:
- ctx->params.slice_max_mb = ctrl->val;
- break;
- case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES:
- ctx->params.slice_max_bits = ctrl->val * 8;
- break;
- case V4L2_CID_MPEG_VIDEO_HEADER_MODE:
- break;
- case V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB:
- ctx->params.intra_refresh = ctrl->val;
- break;
- default:
- v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
- "Invalid control, id=%d, val=%d\n",
- ctrl->id, ctrl->val);
- return -EINVAL;
- }
-
- return 0;
-}
-
-static struct v4l2_ctrl_ops coda_ctrl_ops = {
- .s_ctrl = coda_s_ctrl,
-};
-
-static int coda_ctrls_setup(struct coda_ctx *ctx)
-{
- v4l2_ctrl_handler_init(&ctx->ctrls, 9);
-
- v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops,
- V4L2_CID_HFLIP, 0, 1, 1, 0);
- v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops,
- V4L2_CID_VFLIP, 0, 1, 1, 0);
- v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops,
- V4L2_CID_MPEG_VIDEO_BITRATE, 0, 32767000, 1, 0);
- v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops,
- V4L2_CID_MPEG_VIDEO_GOP_SIZE, 1, 60, 1, 16);
- v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops,
- V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP, 0, 51, 1, 25);
- v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops,
- V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP, 0, 51, 1, 25);
- if (ctx->dev->devtype->product != CODA_960) {
- v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops,
- V4L2_CID_MPEG_VIDEO_H264_MIN_QP, 0, 51, 1, 12);
- }
- v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops,
- V4L2_CID_MPEG_VIDEO_H264_MAX_QP, 0, 51, 1, 51);
- v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops,
- V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA, 0, 15, 1, 0);
- v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops,
- V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA, 0, 15, 1, 0);
- v4l2_ctrl_new_std_menu(&ctx->ctrls, &coda_ctrl_ops,
- V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE,
- V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED, 0x0,
- V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_ENABLED);
- v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops,
- V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP, 1, 31, 1, 2);
- v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops,
- V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP, 1, 31, 1, 2);
- v4l2_ctrl_new_std_menu(&ctx->ctrls, &coda_ctrl_ops,
- V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE,
- V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES, 0x0,
- V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE);
- v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops,
- V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB, 1, 0x3fffffff, 1, 1);
- v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops,
- V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES, 1, 0x3fffffff, 1, 500);
- v4l2_ctrl_new_std_menu(&ctx->ctrls, &coda_ctrl_ops,
- V4L2_CID_MPEG_VIDEO_HEADER_MODE,
- V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME,
- (1 << V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE),
- V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME);
- v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops,
- V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB, 0, 1920 * 1088 / 256, 1, 0);
-
- if (ctx->ctrls.error) {
- v4l2_err(&ctx->dev->v4l2_dev, "control initialization error (%d)",
- ctx->ctrls.error);
- return -EINVAL;
- }
-
- return v4l2_ctrl_handler_setup(&ctx->ctrls);
-}
-
-static int coda_queue_init(void *priv, struct vb2_queue *src_vq,
- struct vb2_queue *dst_vq)
-{
- struct coda_ctx *ctx = priv;
- int ret;
-
- src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
- src_vq->io_modes = VB2_DMABUF | VB2_MMAP | VB2_USERPTR;
- src_vq->drv_priv = ctx;
- src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
- src_vq->ops = &coda_qops;
- src_vq->mem_ops = &vb2_dma_contig_memops;
- src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
- src_vq->lock = &ctx->dev->dev_mutex;
-
- ret = vb2_queue_init(src_vq);
- if (ret)
- return ret;
-
- dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- dst_vq->io_modes = VB2_DMABUF | VB2_MMAP | VB2_USERPTR;
- dst_vq->drv_priv = ctx;
- dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
- dst_vq->ops = &coda_qops;
- dst_vq->mem_ops = &vb2_dma_contig_memops;
- dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
- dst_vq->lock = &ctx->dev->dev_mutex;
-
- return vb2_queue_init(dst_vq);
-}
-
-static int coda_next_free_instance(struct coda_dev *dev)
-{
- int idx = ffz(dev->instance_mask);
-
- if ((idx < 0) ||
- (dev->devtype->product == CODA_DX6 && idx > CODADX6_MAX_INSTANCES))
- return -EBUSY;
-
- return idx;
-}
-
-static int coda_open(struct file *file)
-{
- struct coda_dev *dev = video_drvdata(file);
- struct coda_ctx *ctx = NULL;
- char *name;
- int ret;
- int idx;
-
- ctx = kzalloc(sizeof *ctx, GFP_KERNEL);
- if (!ctx)
- return -ENOMEM;
-
- idx = coda_next_free_instance(dev);
- if (idx < 0) {
- ret = idx;
- goto err_coda_max;
- }
- set_bit(idx, &dev->instance_mask);
-
- name = kasprintf(GFP_KERNEL, "context%d", idx);
- ctx->debugfs_entry = debugfs_create_dir(name, dev->debugfs_root);
- kfree(name);
-
- init_completion(&ctx->completion);
- INIT_WORK(&ctx->pic_run_work, coda_pic_run_work);
- INIT_WORK(&ctx->seq_end_work, coda_seq_end_work);
- v4l2_fh_init(&ctx->fh, video_devdata(file));
- file->private_data = &ctx->fh;
- v4l2_fh_add(&ctx->fh);
- ctx->dev = dev;
- ctx->idx = idx;
- switch (dev->devtype->product) {
- case CODA_7541:
- case CODA_960:
- ctx->reg_idx = 0;
- break;
- default:
- ctx->reg_idx = idx;
- }
-
- /* Power up and upload firmware if necessary */
- ret = pm_runtime_get_sync(&dev->plat_dev->dev);
- if (ret < 0) {
- v4l2_err(&dev->v4l2_dev, "failed to power up: %d\n", ret);
- goto err_pm_get;
- }
-
- ret = clk_prepare_enable(dev->clk_per);
- if (ret)
- goto err_clk_per;
-
- ret = clk_prepare_enable(dev->clk_ahb);
- if (ret)
- goto err_clk_ahb;
-
- set_default_params(ctx);
- ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx,
- &coda_queue_init);
- if (IS_ERR(ctx->fh.m2m_ctx)) {
- ret = PTR_ERR(ctx->fh.m2m_ctx);
-
- v4l2_err(&dev->v4l2_dev, "%s return error (%d)\n",
- __func__, ret);
- goto err_ctx_init;
- }
-
- ret = coda_ctrls_setup(ctx);
- if (ret) {
- v4l2_err(&dev->v4l2_dev, "failed to setup coda controls\n");
- goto err_ctrls_setup;
- }
-
- ctx->fh.ctrl_handler = &ctx->ctrls;
-
- ret = coda_alloc_context_buf(ctx, &ctx->parabuf, CODA_PARA_BUF_SIZE,
- "parabuf");
- if (ret < 0) {
- v4l2_err(&dev->v4l2_dev, "failed to allocate parabuf");
- goto err_dma_alloc;
- }
-
- ctx->bitstream.size = CODA_MAX_FRAME_SIZE;
- ctx->bitstream.vaddr = dma_alloc_writecombine(&dev->plat_dev->dev,
- ctx->bitstream.size, &ctx->bitstream.paddr, GFP_KERNEL);
- if (!ctx->bitstream.vaddr) {
- v4l2_err(&dev->v4l2_dev, "failed to allocate bitstream ringbuffer");
- ret = -ENOMEM;
- goto err_dma_writecombine;
- }
- kfifo_init(&ctx->bitstream_fifo,
- ctx->bitstream.vaddr, ctx->bitstream.size);
- mutex_init(&ctx->bitstream_mutex);
- mutex_init(&ctx->buffer_mutex);
- INIT_LIST_HEAD(&ctx->timestamp_list);
-
- coda_lock(ctx);
- list_add(&ctx->list, &dev->instances);
- coda_unlock(ctx);
-
- v4l2_dbg(1, coda_debug, &dev->v4l2_dev, "Created instance %d (%p)\n",
- ctx->idx, ctx);
-
- return 0;
-
-err_dma_writecombine:
- coda_free_context_buffers(ctx);
- if (ctx->dev->devtype->product == CODA_DX6)
- coda_free_aux_buf(dev, &ctx->workbuf);
- coda_free_aux_buf(dev, &ctx->parabuf);
-err_dma_alloc:
- v4l2_ctrl_handler_free(&ctx->ctrls);
-err_ctrls_setup:
- v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
-err_ctx_init:
- clk_disable_unprepare(dev->clk_ahb);
-err_clk_ahb:
- clk_disable_unprepare(dev->clk_per);
-err_clk_per:
- pm_runtime_put_sync(&dev->plat_dev->dev);
-err_pm_get:
- v4l2_fh_del(&ctx->fh);
- v4l2_fh_exit(&ctx->fh);
- clear_bit(ctx->idx, &dev->instance_mask);
-err_coda_max:
- kfree(ctx);
- return ret;
-}
-
-static int coda_release(struct file *file)
-{
- struct coda_dev *dev = video_drvdata(file);
- struct coda_ctx *ctx = fh_to_ctx(file->private_data);
-
- v4l2_dbg(1, coda_debug, &dev->v4l2_dev, "Releasing instance %p\n",
- ctx);
-
- debugfs_remove_recursive(ctx->debugfs_entry);
-
- /* If this instance is running, call .job_abort and wait for it to end */
- v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
-
- /* In case the instance was not running, we still need to call SEQ_END */
- if (ctx->initialized) {
- queue_work(dev->workqueue, &ctx->seq_end_work);
- flush_work(&ctx->seq_end_work);
- }
-
- coda_free_framebuffers(ctx);
-
- coda_lock(ctx);
- list_del(&ctx->list);
- coda_unlock(ctx);
-
- dma_free_writecombine(&dev->plat_dev->dev, ctx->bitstream.size,
- ctx->bitstream.vaddr, ctx->bitstream.paddr);
- coda_free_context_buffers(ctx);
- if (ctx->dev->devtype->product == CODA_DX6)
- coda_free_aux_buf(dev, &ctx->workbuf);
-
- coda_free_aux_buf(dev, &ctx->parabuf);
- v4l2_ctrl_handler_free(&ctx->ctrls);
- clk_disable_unprepare(dev->clk_ahb);
- clk_disable_unprepare(dev->clk_per);
- pm_runtime_put_sync(&dev->plat_dev->dev);
- v4l2_fh_del(&ctx->fh);
- v4l2_fh_exit(&ctx->fh);
- clear_bit(ctx->idx, &dev->instance_mask);
- kfree(ctx);
-
- return 0;
-}
-
-static const struct v4l2_file_operations coda_fops = {
- .owner = THIS_MODULE,
- .open = coda_open,
- .release = coda_release,
- .poll = v4l2_m2m_fop_poll,
- .unlocked_ioctl = video_ioctl2,
- .mmap = v4l2_m2m_fop_mmap,
-};
-
-static void coda_finish_decode(struct coda_ctx *ctx)
-{
- struct coda_dev *dev = ctx->dev;
- struct coda_q_data *q_data_src;
- struct coda_q_data *q_data_dst;
- struct vb2_buffer *dst_buf;
- struct coda_timestamp *ts;
- int width, height;
- int decoded_idx;
- int display_idx;
- u32 src_fourcc;
- int success;
- u32 err_mb;
- u32 val;
-
- dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
-
- /* Update kfifo out pointer from coda bitstream read pointer */
- coda_kfifo_sync_from_device(ctx);
-
- /*
- * in stream-end mode, the read pointer can overshoot the write pointer
- * by up to 512 bytes
- */
- if (ctx->bit_stream_param & CODA_BIT_STREAM_END_FLAG) {
- if (coda_get_bitstream_payload(ctx) >= 0x100000 - 512)
- kfifo_init(&ctx->bitstream_fifo,
- ctx->bitstream.vaddr, ctx->bitstream.size);
- }
-
- q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
- src_fourcc = q_data_src->fourcc;
-
- val = coda_read(dev, CODA_RET_DEC_PIC_SUCCESS);
- if (val != 1)
- pr_err("DEC_PIC_SUCCESS = %d\n", val);
-
- success = val & 0x1;
- if (!success)
- v4l2_err(&dev->v4l2_dev, "decode failed\n");
-
- if (src_fourcc == V4L2_PIX_FMT_H264) {
- if (val & (1 << 3))
- v4l2_err(&dev->v4l2_dev,
- "insufficient PS buffer space (%d bytes)\n",
- ctx->psbuf.size);
- if (val & (1 << 2))
- v4l2_err(&dev->v4l2_dev,
- "insufficient slice buffer space (%d bytes)\n",
- ctx->slicebuf.size);
- }
-
- val = coda_read(dev, CODA_RET_DEC_PIC_SIZE);
- width = (val >> 16) & 0xffff;
- height = val & 0xffff;
-
- q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
-
- /* frame crop information */
- if (src_fourcc == V4L2_PIX_FMT_H264) {
- u32 left_right;
- u32 top_bottom;
-
- left_right = coda_read(dev, CODA_RET_DEC_PIC_CROP_LEFT_RIGHT);
- top_bottom = coda_read(dev, CODA_RET_DEC_PIC_CROP_TOP_BOTTOM);
-
- if (left_right == 0xffffffff && top_bottom == 0xffffffff) {
- /* Keep current crop information */
- } else {
- struct v4l2_rect *rect = &q_data_dst->rect;
-
- rect->left = left_right >> 16 & 0xffff;
- rect->top = top_bottom >> 16 & 0xffff;
- rect->width = width - rect->left -
- (left_right & 0xffff);
- rect->height = height - rect->top -
- (top_bottom & 0xffff);
- }
- } else {
- /* no cropping */
- }
-
- err_mb = coda_read(dev, CODA_RET_DEC_PIC_ERR_MB);
- if (err_mb > 0)
- v4l2_err(&dev->v4l2_dev,
- "errors in %d macroblocks\n", err_mb);
-
- if (dev->devtype->product == CODA_7541) {
- val = coda_read(dev, CODA_RET_DEC_PIC_OPTION);
- if (val == 0) {
- /* not enough bitstream data */
- v4l2_dbg(1, coda_debug, &dev->v4l2_dev,
- "prescan failed: %d\n", val);
- ctx->hold = true;
- return;
- }
- }
-
- ctx->frm_dis_flg = coda_read(dev, CODA_REG_BIT_FRM_DIS_FLG(ctx->reg_idx));
-
- /*
- * The previous display frame was copied out by the rotator,
- * now it can be overwritten again
- */
- if (ctx->display_idx >= 0 &&
- ctx->display_idx < ctx->num_internal_frames) {
- ctx->frm_dis_flg &= ~(1 << ctx->display_idx);
- coda_write(dev, ctx->frm_dis_flg,
- CODA_REG_BIT_FRM_DIS_FLG(ctx->reg_idx));
- }
-
- /*
- * The index of the last decoded frame, not necessarily in
- * display order, and the index of the next display frame.
- * The latter could have been decoded in a previous run.
- */
- decoded_idx = coda_read(dev, CODA_RET_DEC_PIC_CUR_IDX);
- display_idx = coda_read(dev, CODA_RET_DEC_PIC_FRAME_IDX);
-
- if (decoded_idx == -1) {
- /* no frame was decoded, but we might have a display frame */
- if (display_idx >= 0 && display_idx < ctx->num_internal_frames)
- ctx->sequence_offset++;
- else if (ctx->display_idx < 0)
- ctx->hold = true;
- } else if (decoded_idx == -2) {
- /* no frame was decoded, we still return the remaining buffers */
- } else if (decoded_idx < 0 || decoded_idx >= ctx->num_internal_frames) {
- v4l2_err(&dev->v4l2_dev,
- "decoded frame index out of range: %d\n", decoded_idx);
- } else {
- ts = list_first_entry(&ctx->timestamp_list,
- struct coda_timestamp, list);
- list_del(&ts->list);
- val = coda_read(dev, CODA_RET_DEC_PIC_FRAME_NUM) - 1;
- val -= ctx->sequence_offset;
- if (val != (ts->sequence & 0xffff)) {
- v4l2_err(&dev->v4l2_dev,
- "sequence number mismatch (%d(%d) != %d)\n",
- val, ctx->sequence_offset, ts->sequence);
- }
- ctx->frame_timestamps[decoded_idx] = *ts;
- kfree(ts);
-
- val = coda_read(dev, CODA_RET_DEC_PIC_TYPE) & 0x7;
- if (val == 0)
- ctx->frame_types[decoded_idx] = V4L2_BUF_FLAG_KEYFRAME;
- else if (val == 1)
- ctx->frame_types[decoded_idx] = V4L2_BUF_FLAG_PFRAME;
- else
- ctx->frame_types[decoded_idx] = V4L2_BUF_FLAG_BFRAME;
-
- ctx->frame_errors[decoded_idx] = err_mb;
- }
-
- if (display_idx == -1) {
- /*
- * no more frames to be decoded, but there could still
- * be rotator output to dequeue
- */
- ctx->hold = true;
- } else if (display_idx == -3) {
- /* possibly prescan failure */
- } else if (display_idx < 0 || display_idx >= ctx->num_internal_frames) {
- v4l2_err(&dev->v4l2_dev,
- "presentation frame index out of range: %d\n",
- display_idx);
- }
-
- /* If a frame was copied out, return it */
- if (ctx->display_idx >= 0 &&
- ctx->display_idx < ctx->num_internal_frames) {
- dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
- dst_buf->v4l2_buf.sequence = ctx->osequence++;
-
- dst_buf->v4l2_buf.flags &= ~(V4L2_BUF_FLAG_KEYFRAME |
- V4L2_BUF_FLAG_PFRAME |
- V4L2_BUF_FLAG_BFRAME);
- dst_buf->v4l2_buf.flags |= ctx->frame_types[ctx->display_idx];
- ts = &ctx->frame_timestamps[ctx->display_idx];
- dst_buf->v4l2_buf.timecode = ts->timecode;
- dst_buf->v4l2_buf.timestamp = ts->timestamp;
-
- vb2_set_plane_payload(dst_buf, 0, width * height * 3 / 2);
-
- v4l2_m2m_buf_done(dst_buf, ctx->frame_errors[display_idx] ?
- VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE);
-
- v4l2_dbg(1, coda_debug, &dev->v4l2_dev,
- "job finished: decoding frame (%d) (%s)\n",
- dst_buf->v4l2_buf.sequence,
- (dst_buf->v4l2_buf.flags & V4L2_BUF_FLAG_KEYFRAME) ?
- "KEYFRAME" : "PFRAME");
- } else {
- v4l2_dbg(1, coda_debug, &dev->v4l2_dev,
- "job finished: no frame decoded\n");
- }
-
- /* The rotator will copy the current display frame next time */
- ctx->display_idx = display_idx;
-}
-
-static void coda_finish_encode(struct coda_ctx *ctx)
-{
- struct vb2_buffer *src_buf, *dst_buf;
- struct coda_dev *dev = ctx->dev;
- u32 wr_ptr, start_ptr;
-
- src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
- dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
-
- /* Get results from the coda */
- start_ptr = coda_read(dev, CODA_CMD_ENC_PIC_BB_START);
- wr_ptr = coda_read(dev, CODA_REG_BIT_WR_PTR(ctx->reg_idx));
-
- /* Calculate bytesused field */
- if (dst_buf->v4l2_buf.sequence == 0) {
- vb2_set_plane_payload(dst_buf, 0, wr_ptr - start_ptr +
- ctx->vpu_header_size[0] +
- ctx->vpu_header_size[1] +
- ctx->vpu_header_size[2]);
- } else {
- vb2_set_plane_payload(dst_buf, 0, wr_ptr - start_ptr);
- }
-
- v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, "frame size = %u\n",
- wr_ptr - start_ptr);
-
- coda_read(dev, CODA_RET_ENC_PIC_SLICE_NUM);
- coda_read(dev, CODA_RET_ENC_PIC_FLAG);
-
- if (coda_read(dev, CODA_RET_ENC_PIC_TYPE) == 0) {
- dst_buf->v4l2_buf.flags |= V4L2_BUF_FLAG_KEYFRAME;
- dst_buf->v4l2_buf.flags &= ~V4L2_BUF_FLAG_PFRAME;
- } else {
- dst_buf->v4l2_buf.flags |= V4L2_BUF_FLAG_PFRAME;
- dst_buf->v4l2_buf.flags &= ~V4L2_BUF_FLAG_KEYFRAME;
- }
-
- dst_buf->v4l2_buf.timestamp = src_buf->v4l2_buf.timestamp;
- dst_buf->v4l2_buf.flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
- dst_buf->v4l2_buf.flags |=
- src_buf->v4l2_buf.flags & V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
- dst_buf->v4l2_buf.timecode = src_buf->v4l2_buf.timecode;
-
- v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE);
-
- dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
- v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_DONE);
-
- ctx->gopcounter--;
- if (ctx->gopcounter < 0)
- ctx->gopcounter = ctx->params.gop_size - 1;
-
- v4l2_dbg(1, coda_debug, &dev->v4l2_dev,
- "job finished: encoding frame (%d) (%s)\n",
- dst_buf->v4l2_buf.sequence,
- (dst_buf->v4l2_buf.flags & V4L2_BUF_FLAG_KEYFRAME) ?
- "KEYFRAME" : "PFRAME");
-}
-
-static irqreturn_t coda_irq_handler(int irq, void *data)
-{
- struct coda_dev *dev = data;
- struct coda_ctx *ctx;
-
- /* read status register to attend the IRQ */
- coda_read(dev, CODA_REG_BIT_INT_STATUS);
- coda_write(dev, CODA_REG_BIT_INT_CLEAR_SET,
- CODA_REG_BIT_INT_CLEAR);
-
- ctx = v4l2_m2m_get_curr_priv(dev->m2m_dev);
- if (ctx == NULL) {
- v4l2_err(&dev->v4l2_dev, "Instance released before the end of transaction\n");
- mutex_unlock(&dev->coda_mutex);
- return IRQ_HANDLED;
- }
-
- if (ctx->aborting) {
- v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
- "task has been aborted\n");
- }
-
- if (coda_isbusy(ctx->dev)) {
- v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
- "coda is still busy!!!!\n");
- return IRQ_NONE;
- }
-
- complete(&ctx->completion);
-
- return IRQ_HANDLED;
-}
-
-static u32 coda_supported_firmwares[] = {
- CODA_FIRMWARE_VERNUM(CODA_DX6, 2, 2, 5),
- CODA_FIRMWARE_VERNUM(CODA_7541, 1, 4, 50),
- CODA_FIRMWARE_VERNUM(CODA_960, 2, 1, 5),
-};
-
-static bool coda_firmware_supported(u32 vernum)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(coda_supported_firmwares); i++)
- if (vernum == coda_supported_firmwares[i])
- return true;
- return false;
-}
-
-static int coda_hw_init(struct coda_dev *dev)
-{
- u32 data;
- u16 *p;
- int i, ret;
-
- ret = clk_prepare_enable(dev->clk_per);
- if (ret)
- goto err_clk_per;
-
- ret = clk_prepare_enable(dev->clk_ahb);
- if (ret)
- goto err_clk_ahb;
-
- if (dev->rstc)
- reset_control_reset(dev->rstc);
-
- /*
- * Copy the first CODA_ISRAM_SIZE in the internal SRAM.
- * The 16-bit chars in the code buffer are in memory access
- * order, re-sort them to CODA order for register download.
- * Data in this SRAM survives a reboot.
- */
- p = (u16 *)dev->codebuf.vaddr;
- if (dev->devtype->product == CODA_DX6) {
- for (i = 0; i < (CODA_ISRAM_SIZE / 2); i++) {
- data = CODA_DOWN_ADDRESS_SET(i) |
- CODA_DOWN_DATA_SET(p[i ^ 1]);
- coda_write(dev, data, CODA_REG_BIT_CODE_DOWN);
- }
- } else {
- for (i = 0; i < (CODA_ISRAM_SIZE / 2); i++) {
- data = CODA_DOWN_ADDRESS_SET(i) |
- CODA_DOWN_DATA_SET(p[round_down(i, 4) +
- 3 - (i % 4)]);
- coda_write(dev, data, CODA_REG_BIT_CODE_DOWN);
- }
- }
-
- /* Clear registers */
- for (i = 0; i < 64; i++)
- coda_write(dev, 0, CODA_REG_BIT_CODE_BUF_ADDR + i * 4);
-
- /* Tell the BIT where to find everything it needs */
- if (dev->devtype->product == CODA_960 ||
- dev->devtype->product == CODA_7541) {
- coda_write(dev, dev->tempbuf.paddr,
- CODA_REG_BIT_TEMP_BUF_ADDR);
- coda_write(dev, 0, CODA_REG_BIT_BIT_STREAM_PARAM);
- } else {
- coda_write(dev, dev->workbuf.paddr,
- CODA_REG_BIT_WORK_BUF_ADDR);
- }
- coda_write(dev, dev->codebuf.paddr,
- CODA_REG_BIT_CODE_BUF_ADDR);
- coda_write(dev, 0, CODA_REG_BIT_CODE_RUN);
-
- /* Set default values */
- switch (dev->devtype->product) {
- case CODA_DX6:
- coda_write(dev, CODADX6_STREAM_BUF_PIC_FLUSH, CODA_REG_BIT_STREAM_CTRL);
- break;
- default:
- coda_write(dev, CODA7_STREAM_BUF_PIC_FLUSH, CODA_REG_BIT_STREAM_CTRL);
- }
- if (dev->devtype->product == CODA_960)
- coda_write(dev, 1 << 12, CODA_REG_BIT_FRAME_MEM_CTRL);
- else
- coda_write(dev, 0, CODA_REG_BIT_FRAME_MEM_CTRL);
-
- if (dev->devtype->product != CODA_DX6)
- coda_write(dev, 0, CODA7_REG_BIT_AXI_SRAM_USE);
-
- coda_write(dev, CODA_INT_INTERRUPT_ENABLE,
- CODA_REG_BIT_INT_ENABLE);
-
- /* Reset VPU and start processor */
- data = coda_read(dev, CODA_REG_BIT_CODE_RESET);
- data |= CODA_REG_RESET_ENABLE;
- coda_write(dev, data, CODA_REG_BIT_CODE_RESET);
- udelay(10);
- data &= ~CODA_REG_RESET_ENABLE;
- coda_write(dev, data, CODA_REG_BIT_CODE_RESET);
- coda_write(dev, CODA_REG_RUN_ENABLE, CODA_REG_BIT_CODE_RUN);
-
- clk_disable_unprepare(dev->clk_ahb);
- clk_disable_unprepare(dev->clk_per);
-
- return 0;
-
-err_clk_ahb:
- clk_disable_unprepare(dev->clk_per);
-err_clk_per:
- return ret;
-}
-
-static int coda_check_firmware(struct coda_dev *dev)
-{
- u16 product, major, minor, release;
- u32 data;
- int ret;
-
- ret = clk_prepare_enable(dev->clk_per);
- if (ret)
- goto err_clk_per;
-
- ret = clk_prepare_enable(dev->clk_ahb);
- if (ret)
- goto err_clk_ahb;
-
- coda_write(dev, 0, CODA_CMD_FIRMWARE_VERNUM);
- coda_write(dev, CODA_REG_BIT_BUSY_FLAG, CODA_REG_BIT_BUSY);
- coda_write(dev, 0, CODA_REG_BIT_RUN_INDEX);
- coda_write(dev, 0, CODA_REG_BIT_RUN_COD_STD);
- coda_write(dev, CODA_COMMAND_FIRMWARE_GET, CODA_REG_BIT_RUN_COMMAND);
- if (coda_wait_timeout(dev)) {
- v4l2_err(&dev->v4l2_dev, "firmware get command error\n");
- ret = -EIO;
- goto err_run_cmd;
- }
-
- if (dev->devtype->product == CODA_960) {
- data = coda_read(dev, CODA9_CMD_FIRMWARE_CODE_REV);
- v4l2_info(&dev->v4l2_dev, "Firmware code revision: %d\n",
- data);
- }
-
- /* Check we are compatible with the loaded firmware */
- data = coda_read(dev, CODA_CMD_FIRMWARE_VERNUM);
- product = CODA_FIRMWARE_PRODUCT(data);
- major = CODA_FIRMWARE_MAJOR(data);
- minor = CODA_FIRMWARE_MINOR(data);
- release = CODA_FIRMWARE_RELEASE(data);
-
- clk_disable_unprepare(dev->clk_per);
- clk_disable_unprepare(dev->clk_ahb);
-
- if (product != dev->devtype->product) {
- v4l2_err(&dev->v4l2_dev, "Wrong firmware. Hw: %s, Fw: %s,"
- " Version: %u.%u.%u\n",
- coda_product_name(dev->devtype->product),
- coda_product_name(product), major, minor, release);
- return -EINVAL;
- }
-
- v4l2_info(&dev->v4l2_dev, "Initialized %s.\n",
- coda_product_name(product));
-
- if (coda_firmware_supported(data)) {
- v4l2_info(&dev->v4l2_dev, "Firmware version: %u.%u.%u\n",
- major, minor, release);
- } else {
- v4l2_warn(&dev->v4l2_dev, "Unsupported firmware version: "
- "%u.%u.%u\n", major, minor, release);
- }
-
- return 0;
-
-err_run_cmd:
- clk_disable_unprepare(dev->clk_ahb);
-err_clk_ahb:
- clk_disable_unprepare(dev->clk_per);
-err_clk_per:
- return ret;
-}
-
-static void coda_fw_callback(const struct firmware *fw, void *context)
-{
- struct coda_dev *dev = context;
- struct platform_device *pdev = dev->plat_dev;
- int ret;
-
- if (!fw) {
- v4l2_err(&dev->v4l2_dev, "firmware request failed\n");
- return;
- }
-
- /* allocate auxiliary per-device code buffer for the BIT processor */
- ret = coda_alloc_aux_buf(dev, &dev->codebuf, fw->size, "codebuf",
- dev->debugfs_root);
- if (ret < 0) {
- dev_err(&pdev->dev, "failed to allocate code buffer\n");
- return;
- }
-
- /* Copy the whole firmware image to the code buffer */
- memcpy(dev->codebuf.vaddr, fw->data, fw->size);
- release_firmware(fw);
-
- if (pm_runtime_enabled(&pdev->dev) && pdev->dev.pm_domain) {
- /*
- * Enabling power temporarily will cause coda_hw_init to be
- * called via coda_runtime_resume by the pm domain.
- */
- ret = pm_runtime_get_sync(&dev->plat_dev->dev);
- if (ret < 0) {
- v4l2_err(&dev->v4l2_dev, "failed to power on: %d\n",
- ret);
- return;
- }
-
- ret = coda_check_firmware(dev);
- if (ret < 0)
- return;
-
- pm_runtime_put_sync(&dev->plat_dev->dev);
- } else {
- /*
- * If runtime pm is disabled or pm_domain is not set,
- * initialize once manually.
- */
- ret = coda_hw_init(dev);
- if (ret < 0) {
- v4l2_err(&dev->v4l2_dev, "HW initialization failed\n");
- return;
- }
-
- ret = coda_check_firmware(dev);
- if (ret < 0)
- return;
- }
-
- dev->vfd.fops = &coda_fops,
- dev->vfd.ioctl_ops = &coda_ioctl_ops;
- dev->vfd.release = video_device_release_empty,
- dev->vfd.lock = &dev->dev_mutex;
- dev->vfd.v4l2_dev = &dev->v4l2_dev;
- dev->vfd.vfl_dir = VFL_DIR_M2M;
- snprintf(dev->vfd.name, sizeof(dev->vfd.name), "%s", CODA_NAME);
- video_set_drvdata(&dev->vfd, dev);
-
- dev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
- if (IS_ERR(dev->alloc_ctx)) {
- v4l2_err(&dev->v4l2_dev, "Failed to alloc vb2 context\n");
- return;
- }
-
- dev->m2m_dev = v4l2_m2m_init(&coda_m2m_ops);
- if (IS_ERR(dev->m2m_dev)) {
- v4l2_err(&dev->v4l2_dev, "Failed to init mem2mem device\n");
- goto rel_ctx;
- }
-
- ret = video_register_device(&dev->vfd, VFL_TYPE_GRABBER, 0);
- if (ret) {
- v4l2_err(&dev->v4l2_dev, "Failed to register video device\n");
- goto rel_m2m;
- }
- v4l2_info(&dev->v4l2_dev, "codec registered as /dev/video%d\n",
- dev->vfd.num);
-
- return;
-
-rel_m2m:
- v4l2_m2m_release(dev->m2m_dev);
-rel_ctx:
- vb2_dma_contig_cleanup_ctx(dev->alloc_ctx);
-}
-
-static int coda_firmware_request(struct coda_dev *dev)
-{
- char *fw = dev->devtype->firmware;
-
- dev_dbg(&dev->plat_dev->dev, "requesting firmware '%s' for %s\n", fw,
- coda_product_name(dev->devtype->product));
-
- return request_firmware_nowait(THIS_MODULE, true,
- fw, &dev->plat_dev->dev, GFP_KERNEL, dev, coda_fw_callback);
-}
-
-enum coda_platform {
- CODA_IMX27,
- CODA_IMX53,
- CODA_IMX6Q,
- CODA_IMX6DL,
-};
-
-static const struct coda_devtype coda_devdata[] = {
- [CODA_IMX27] = {
- .firmware = "v4l-codadx6-imx27.bin",
- .product = CODA_DX6,
- .codecs = codadx6_codecs,
- .num_codecs = ARRAY_SIZE(codadx6_codecs),
- .workbuf_size = 288 * 1024 + FMO_SLICE_SAVE_BUF_SIZE * 8 * 1024,
- .iram_size = 0xb000,
- },
- [CODA_IMX53] = {
- .firmware = "v4l-coda7541-imx53.bin",
- .product = CODA_7541,
- .codecs = coda7_codecs,
- .num_codecs = ARRAY_SIZE(coda7_codecs),
- .workbuf_size = 128 * 1024,
- .tempbuf_size = 304 * 1024,
- .iram_size = 0x14000,
- },
- [CODA_IMX6Q] = {
- .firmware = "v4l-coda960-imx6q.bin",
- .product = CODA_960,
- .codecs = coda9_codecs,
- .num_codecs = ARRAY_SIZE(coda9_codecs),
- .workbuf_size = 80 * 1024,
- .tempbuf_size = 204 * 1024,
- .iram_size = 0x21000,
- },
- [CODA_IMX6DL] = {
- .firmware = "v4l-coda960-imx6dl.bin",
- .product = CODA_960,
- .codecs = coda9_codecs,
- .num_codecs = ARRAY_SIZE(coda9_codecs),
- .workbuf_size = 80 * 1024,
- .tempbuf_size = 204 * 1024,
- .iram_size = 0x20000,
- },
-};
-
-static struct platform_device_id coda_platform_ids[] = {
- { .name = "coda-imx27", .driver_data = CODA_IMX27 },
- { .name = "coda-imx53", .driver_data = CODA_IMX53 },
- { /* sentinel */ }
-};
-MODULE_DEVICE_TABLE(platform, coda_platform_ids);
-
-#ifdef CONFIG_OF
-static const struct of_device_id coda_dt_ids[] = {
- { .compatible = "fsl,imx27-vpu", .data = &coda_devdata[CODA_IMX27] },
- { .compatible = "fsl,imx53-vpu", .data = &coda_devdata[CODA_IMX53] },
- { .compatible = "fsl,imx6q-vpu", .data = &coda_devdata[CODA_IMX6Q] },
- { .compatible = "fsl,imx6dl-vpu", .data = &coda_devdata[CODA_IMX6DL] },
- { /* sentinel */ }
-};
-MODULE_DEVICE_TABLE(of, coda_dt_ids);
-#endif
-
-static int coda_probe(struct platform_device *pdev)
-{
- const struct of_device_id *of_id =
- of_match_device(of_match_ptr(coda_dt_ids), &pdev->dev);
- const struct platform_device_id *pdev_id;
- struct coda_platform_data *pdata = pdev->dev.platform_data;
- struct device_node *np = pdev->dev.of_node;
- struct gen_pool *pool;
- struct coda_dev *dev;
- struct resource *res;
- int ret, irq;
-
- dev = devm_kzalloc(&pdev->dev, sizeof *dev, GFP_KERNEL);
- if (!dev) {
- dev_err(&pdev->dev, "Not enough memory for %s\n",
- CODA_NAME);
- return -ENOMEM;
- }
-
- spin_lock_init(&dev->irqlock);
- INIT_LIST_HEAD(&dev->instances);
-
- dev->plat_dev = pdev;
- dev->clk_per = devm_clk_get(&pdev->dev, "per");
- if (IS_ERR(dev->clk_per)) {
- dev_err(&pdev->dev, "Could not get per clock\n");
- return PTR_ERR(dev->clk_per);
- }
-
- dev->clk_ahb = devm_clk_get(&pdev->dev, "ahb");
- if (IS_ERR(dev->clk_ahb)) {
- dev_err(&pdev->dev, "Could not get ahb clock\n");
- return PTR_ERR(dev->clk_ahb);
- }
-
- /* Get memory for physical registers */
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- dev->regs_base = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(dev->regs_base))
- return PTR_ERR(dev->regs_base);
-
- /* IRQ */
- irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- dev_err(&pdev->dev, "failed to get irq resource\n");
- return irq;
- }
-
- ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, coda_irq_handler,
- IRQF_ONESHOT, dev_name(&pdev->dev), dev);
- if (ret < 0) {
- dev_err(&pdev->dev, "failed to request irq: %d\n", ret);
- return ret;
- }
-
- dev->rstc = devm_reset_control_get_optional(&pdev->dev, NULL);
- if (IS_ERR(dev->rstc)) {
- ret = PTR_ERR(dev->rstc);
- if (ret == -ENOENT || ret == -ENOSYS) {
- dev->rstc = NULL;
- } else {
- dev_err(&pdev->dev, "failed get reset control: %d\n", ret);
- return ret;
- }
- }
-
- /* Get IRAM pool from device tree or platform data */
- pool = of_get_named_gen_pool(np, "iram", 0);
- if (!pool && pdata)
- pool = dev_get_gen_pool(pdata->iram_dev);
- if (!pool) {
- dev_err(&pdev->dev, "iram pool not available\n");
- return -ENOMEM;
- }
- dev->iram_pool = pool;
-
- ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
- if (ret)
- return ret;
-
- mutex_init(&dev->dev_mutex);
- mutex_init(&dev->coda_mutex);
-
- pdev_id = of_id ? of_id->data : platform_get_device_id(pdev);
-
- if (of_id) {
- dev->devtype = of_id->data;
- } else if (pdev_id) {
- dev->devtype = &coda_devdata[pdev_id->driver_data];
- } else {
- v4l2_device_unregister(&dev->v4l2_dev);
- return -EINVAL;
- }
-
- dev->debugfs_root = debugfs_create_dir("coda", NULL);
- if (!dev->debugfs_root)
- dev_warn(&pdev->dev, "failed to create debugfs root\n");
-
- /* allocate auxiliary per-device buffers for the BIT processor */
- if (dev->devtype->product == CODA_DX6) {
- ret = coda_alloc_aux_buf(dev, &dev->workbuf,
- dev->devtype->workbuf_size, "workbuf",
- dev->debugfs_root);
- if (ret < 0) {
- dev_err(&pdev->dev, "failed to allocate work buffer\n");
- v4l2_device_unregister(&dev->v4l2_dev);
- return ret;
- }
- }
-
- if (dev->devtype->tempbuf_size) {
- ret = coda_alloc_aux_buf(dev, &dev->tempbuf,
- dev->devtype->tempbuf_size, "tempbuf",
- dev->debugfs_root);
- if (ret < 0) {
- dev_err(&pdev->dev, "failed to allocate temp buffer\n");
- v4l2_device_unregister(&dev->v4l2_dev);
- return ret;
- }
- }
-
- dev->iram.size = dev->devtype->iram_size;
- dev->iram.vaddr = gen_pool_dma_alloc(dev->iram_pool, dev->iram.size,
- &dev->iram.paddr);
- if (!dev->iram.vaddr) {
- dev_err(&pdev->dev, "unable to alloc iram\n");
- return -ENOMEM;
- }
-
- dev->iram.blob.data = dev->iram.vaddr;
- dev->iram.blob.size = dev->iram.size;
- dev->iram.dentry = debugfs_create_blob("iram", 0644, dev->debugfs_root,
- &dev->iram.blob);
-
- dev->workqueue = alloc_workqueue("coda", WQ_UNBOUND | WQ_MEM_RECLAIM, 1);
- if (!dev->workqueue) {
- dev_err(&pdev->dev, "unable to alloc workqueue\n");
- return -ENOMEM;
- }
-
- platform_set_drvdata(pdev, dev);
-
- pm_runtime_enable(&pdev->dev);
-
- return coda_firmware_request(dev);
-}
-
-static int coda_remove(struct platform_device *pdev)
-{
- struct coda_dev *dev = platform_get_drvdata(pdev);
-
- video_unregister_device(&dev->vfd);
- if (dev->m2m_dev)
- v4l2_m2m_release(dev->m2m_dev);
- pm_runtime_disable(&pdev->dev);
- if (dev->alloc_ctx)
- vb2_dma_contig_cleanup_ctx(dev->alloc_ctx);
- v4l2_device_unregister(&dev->v4l2_dev);
- destroy_workqueue(dev->workqueue);
- if (dev->iram.vaddr)
- gen_pool_free(dev->iram_pool, (unsigned long)dev->iram.vaddr,
- dev->iram.size);
- coda_free_aux_buf(dev, &dev->codebuf);
- coda_free_aux_buf(dev, &dev->tempbuf);
- coda_free_aux_buf(dev, &dev->workbuf);
- debugfs_remove_recursive(dev->debugfs_root);
- return 0;
-}
-
-#ifdef CONFIG_PM_RUNTIME
-static int coda_runtime_resume(struct device *dev)
-{
- struct coda_dev *cdev = dev_get_drvdata(dev);
- int ret = 0;
-
- if (dev->pm_domain) {
- ret = coda_hw_init(cdev);
- if (ret)
- v4l2_err(&cdev->v4l2_dev, "HW initialization failed\n");
- }
-
- return ret;
-}
-#endif
-
-static const struct dev_pm_ops coda_pm_ops = {
- SET_RUNTIME_PM_OPS(NULL, coda_runtime_resume, NULL)
-};
-
-static struct platform_driver coda_driver = {
- .probe = coda_probe,
- .remove = coda_remove,
- .driver = {
- .name = CODA_NAME,
- .owner = THIS_MODULE,
- .of_match_table = of_match_ptr(coda_dt_ids),
- .pm = &coda_pm_ops,
- },
- .id_table = coda_platform_ids,
-};
-
-module_platform_driver(coda_driver);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Javier Martin <javier.martin@vista-silicon.com>");
-MODULE_DESCRIPTION("Coda multi-standard codec V4L2 driver");
diff --git a/drivers/media/platform/coda/Makefile b/drivers/media/platform/coda/Makefile
new file mode 100644
index 000000000000..3543291e6273
--- /dev/null
+++ b/drivers/media/platform/coda/Makefile
@@ -0,0 +1,3 @@
+coda-objs := coda-common.o coda-bit.o coda-h264.o
+
+obj-$(CONFIG_VIDEO_CODA) += coda.o
diff --git a/drivers/media/platform/coda/coda-bit.c b/drivers/media/platform/coda/coda-bit.c
new file mode 100644
index 000000000000..9b8ea8bbeb4e
--- /dev/null
+++ b/drivers/media/platform/coda/coda-bit.c
@@ -0,0 +1,1861 @@
+/*
+ * Coda multi-standard codec IP - BIT processor functions
+ *
+ * Copyright (C) 2012 Vista Silicon S.L.
+ * Javier Martin, <javier.martin@vista-silicon.com>
+ * Xavier Duret
+ * Copyright (C) 2012-2014 Philipp Zabel, Pengutronix
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/clk.h>
+#include <linux/irqreturn.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+
+#include <media/v4l2-common.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-mem2mem.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-dma-contig.h>
+#include <media/videobuf2-vmalloc.h>
+
+#include "coda.h"
+
+#define CODA7_PS_BUF_SIZE 0x28000
+#define CODA9_PS_SAVE_SIZE (512 * 1024)
+
+#define CODA_DEFAULT_GAMMA 4096
+#define CODA9_DEFAULT_GAMMA 24576 /* 0.75 * 32768 */
+
+static inline int coda_is_initialized(struct coda_dev *dev)
+{
+ return coda_read(dev, CODA_REG_BIT_CUR_PC) != 0;
+}
+
+static inline unsigned long coda_isbusy(struct coda_dev *dev)
+{
+ return coda_read(dev, CODA_REG_BIT_BUSY);
+}
+
+static int coda_wait_timeout(struct coda_dev *dev)
+{
+ unsigned long timeout = jiffies + msecs_to_jiffies(1000);
+
+ while (coda_isbusy(dev)) {
+ if (time_after(jiffies, timeout))
+ return -ETIMEDOUT;
+ }
+ return 0;
+}
+
+static void coda_command_async(struct coda_ctx *ctx, int cmd)
+{
+ struct coda_dev *dev = ctx->dev;
+
+ if (dev->devtype->product == CODA_960 ||
+ dev->devtype->product == CODA_7541) {
+ /* Restore context related registers to CODA */
+ coda_write(dev, ctx->bit_stream_param,
+ CODA_REG_BIT_BIT_STREAM_PARAM);
+ coda_write(dev, ctx->frm_dis_flg,
+ CODA_REG_BIT_FRM_DIS_FLG(ctx->reg_idx));
+ coda_write(dev, ctx->frame_mem_ctrl,
+ CODA_REG_BIT_FRAME_MEM_CTRL);
+ coda_write(dev, ctx->workbuf.paddr, CODA_REG_BIT_WORK_BUF_ADDR);
+ }
+
+ if (dev->devtype->product == CODA_960) {
+ coda_write(dev, 1, CODA9_GDI_WPROT_ERR_CLR);
+ coda_write(dev, 0, CODA9_GDI_WPROT_RGN_EN);
+ }
+
+ coda_write(dev, CODA_REG_BIT_BUSY_FLAG, CODA_REG_BIT_BUSY);
+
+ coda_write(dev, ctx->idx, CODA_REG_BIT_RUN_INDEX);
+ coda_write(dev, ctx->params.codec_mode, CODA_REG_BIT_RUN_COD_STD);
+ coda_write(dev, ctx->params.codec_mode_aux, CODA7_REG_BIT_RUN_AUX_STD);
+
+ coda_write(dev, cmd, CODA_REG_BIT_RUN_COMMAND);
+}
+
+static int coda_command_sync(struct coda_ctx *ctx, int cmd)
+{
+ struct coda_dev *dev = ctx->dev;
+
+ coda_command_async(ctx, cmd);
+ return coda_wait_timeout(dev);
+}
+
+int coda_hw_reset(struct coda_ctx *ctx)
+{
+ struct coda_dev *dev = ctx->dev;
+ unsigned long timeout;
+ unsigned int idx;
+ int ret;
+
+ if (!dev->rstc)
+ return -ENOENT;
+
+ idx = coda_read(dev, CODA_REG_BIT_RUN_INDEX);
+
+ if (dev->devtype->product == CODA_960) {
+ timeout = jiffies + msecs_to_jiffies(100);
+ coda_write(dev, 0x11, CODA9_GDI_BUS_CTRL);
+ while (coda_read(dev, CODA9_GDI_BUS_STATUS) != 0x77) {
+ if (time_after(jiffies, timeout))
+ return -ETIME;
+ cpu_relax();
+ }
+ }
+
+ ret = reset_control_reset(dev->rstc);
+ if (ret < 0)
+ return ret;
+
+ if (dev->devtype->product == CODA_960)
+ coda_write(dev, 0x00, CODA9_GDI_BUS_CTRL);
+ coda_write(dev, CODA_REG_BIT_BUSY_FLAG, CODA_REG_BIT_BUSY);
+ coda_write(dev, CODA_REG_RUN_ENABLE, CODA_REG_BIT_CODE_RUN);
+ ret = coda_wait_timeout(dev);
+ coda_write(dev, idx, CODA_REG_BIT_RUN_INDEX);
+
+ return ret;
+}
+
+static void coda_kfifo_sync_from_device(struct coda_ctx *ctx)
+{
+ struct __kfifo *kfifo = &ctx->bitstream_fifo.kfifo;
+ struct coda_dev *dev = ctx->dev;
+ u32 rd_ptr;
+
+ rd_ptr = coda_read(dev, CODA_REG_BIT_RD_PTR(ctx->reg_idx));
+ kfifo->out = (kfifo->in & ~kfifo->mask) |
+ (rd_ptr - ctx->bitstream.paddr);
+ if (kfifo->out > kfifo->in)
+ kfifo->out -= kfifo->mask + 1;
+}
+
+static void coda_kfifo_sync_to_device_full(struct coda_ctx *ctx)
+{
+ struct __kfifo *kfifo = &ctx->bitstream_fifo.kfifo;
+ struct coda_dev *dev = ctx->dev;
+ u32 rd_ptr, wr_ptr;
+
+ rd_ptr = ctx->bitstream.paddr + (kfifo->out & kfifo->mask);
+ coda_write(dev, rd_ptr, CODA_REG_BIT_RD_PTR(ctx->reg_idx));
+ wr_ptr = ctx->bitstream.paddr + (kfifo->in & kfifo->mask);
+ coda_write(dev, wr_ptr, CODA_REG_BIT_WR_PTR(ctx->reg_idx));
+}
+
+static void coda_kfifo_sync_to_device_write(struct coda_ctx *ctx)
+{
+ struct __kfifo *kfifo = &ctx->bitstream_fifo.kfifo;
+ struct coda_dev *dev = ctx->dev;
+ u32 wr_ptr;
+
+ wr_ptr = ctx->bitstream.paddr + (kfifo->in & kfifo->mask);
+ coda_write(dev, wr_ptr, CODA_REG_BIT_WR_PTR(ctx->reg_idx));
+}
+
+static int coda_bitstream_queue(struct coda_ctx *ctx,
+ struct vb2_buffer *src_buf)
+{
+ u32 src_size = vb2_get_plane_payload(src_buf, 0);
+ u32 n;
+
+ n = kfifo_in(&ctx->bitstream_fifo, vb2_plane_vaddr(src_buf, 0),
+ src_size);
+ if (n < src_size)
+ return -ENOSPC;
+
+ dma_sync_single_for_device(&ctx->dev->plat_dev->dev,
+ ctx->bitstream.paddr, ctx->bitstream.size,
+ DMA_TO_DEVICE);
+
+ src_buf->v4l2_buf.sequence = ctx->qsequence++;
+
+ return 0;
+}
+
+static bool coda_bitstream_try_queue(struct coda_ctx *ctx,
+ struct vb2_buffer *src_buf)
+{
+ int ret;
+
+ if (coda_get_bitstream_payload(ctx) +
+ vb2_get_plane_payload(src_buf, 0) + 512 >= ctx->bitstream.size)
+ return false;
+
+ if (vb2_plane_vaddr(src_buf, 0) == NULL) {
+ v4l2_err(&ctx->dev->v4l2_dev, "trying to queue empty buffer\n");
+ return true;
+ }
+
+ ret = coda_bitstream_queue(ctx, src_buf);
+ if (ret < 0) {
+ v4l2_err(&ctx->dev->v4l2_dev, "bitstream buffer overflow\n");
+ return false;
+ }
+ /* Sync read pointer to device */
+ if (ctx == v4l2_m2m_get_curr_priv(ctx->dev->m2m_dev))
+ coda_kfifo_sync_to_device_write(ctx);
+
+ ctx->hold = false;
+
+ return true;
+}
+
+void coda_fill_bitstream(struct coda_ctx *ctx)
+{
+ struct vb2_buffer *src_buf;
+ struct coda_timestamp *ts;
+
+ while (v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx) > 0) {
+ src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
+
+ if (coda_bitstream_try_queue(ctx, src_buf)) {
+ /*
+ * Source buffer is queued in the bitstream ringbuffer;
+ * queue the timestamp and mark source buffer as done
+ */
+ src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
+
+ ts = kmalloc(sizeof(*ts), GFP_KERNEL);
+ if (ts) {
+ ts->sequence = src_buf->v4l2_buf.sequence;
+ ts->timecode = src_buf->v4l2_buf.timecode;
+ ts->timestamp = src_buf->v4l2_buf.timestamp;
+ list_add_tail(&ts->list, &ctx->timestamp_list);
+ }
+
+ v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE);
+ } else {
+ break;
+ }
+ }
+}
+
+void coda_bit_stream_end_flag(struct coda_ctx *ctx)
+{
+ struct coda_dev *dev = ctx->dev;
+
+ ctx->bit_stream_param |= CODA_BIT_STREAM_END_FLAG;
+
+ /* If this context is currently running, update the hardware flag */
+ if ((dev->devtype->product == CODA_960) &&
+ coda_isbusy(dev) &&
+ (ctx->idx == coda_read(dev, CODA_REG_BIT_RUN_INDEX))) {
+ coda_write(dev, ctx->bit_stream_param,
+ CODA_REG_BIT_BIT_STREAM_PARAM);
+ }
+}
+
+static void coda_parabuf_write(struct coda_ctx *ctx, int index, u32 value)
+{
+ struct coda_dev *dev = ctx->dev;
+ u32 *p = ctx->parabuf.vaddr;
+
+ if (dev->devtype->product == CODA_DX6)
+ p[index] = value;
+ else
+ p[index ^ 1] = value;
+}
+
+static void coda_free_framebuffers(struct coda_ctx *ctx)
+{
+ int i;
+
+ for (i = 0; i < CODA_MAX_FRAMEBUFFERS; i++)
+ coda_free_aux_buf(ctx->dev, &ctx->internal_frames[i]);
+}
+
+static int coda_alloc_framebuffers(struct coda_ctx *ctx,
+ struct coda_q_data *q_data, u32 fourcc)
+{
+ struct coda_dev *dev = ctx->dev;
+ int width, height;
+ dma_addr_t paddr;
+ int ysize;
+ int ret;
+ int i;
+
+ if (ctx->codec && (ctx->codec->src_fourcc == V4L2_PIX_FMT_H264 ||
+ ctx->codec->dst_fourcc == V4L2_PIX_FMT_H264)) {
+ width = round_up(q_data->width, 16);
+ height = round_up(q_data->height, 16);
+ } else {
+ width = round_up(q_data->width, 8);
+ height = q_data->height;
+ }
+ ysize = width * height;
+
+ /* Allocate frame buffers */
+ for (i = 0; i < ctx->num_internal_frames; i++) {
+ size_t size;
+ char *name;
+
+ size = ysize + ysize / 2;
+ if (ctx->codec->src_fourcc == V4L2_PIX_FMT_H264 &&
+ dev->devtype->product != CODA_DX6)
+ size += ysize / 4;
+ name = kasprintf(GFP_KERNEL, "fb%d", i);
+ ret = coda_alloc_context_buf(ctx, &ctx->internal_frames[i],
+ size, name);
+ kfree(name);
+ if (ret < 0) {
+ coda_free_framebuffers(ctx);
+ return ret;
+ }
+ }
+
+ /* Register frame buffers in the parameter buffer */
+ for (i = 0; i < ctx->num_internal_frames; i++) {
+ paddr = ctx->internal_frames[i].paddr;
+ /* Start addresses of Y, Cb, Cr planes */
+ coda_parabuf_write(ctx, i * 3 + 0, paddr);
+ coda_parabuf_write(ctx, i * 3 + 1, paddr + ysize);
+ coda_parabuf_write(ctx, i * 3 + 2, paddr + ysize + ysize / 4);
+
+ /* mvcol buffer for h.264 */
+ if (ctx->codec->src_fourcc == V4L2_PIX_FMT_H264 &&
+ dev->devtype->product != CODA_DX6)
+ coda_parabuf_write(ctx, 96 + i,
+ ctx->internal_frames[i].paddr +
+ ysize + ysize/4 + ysize/4);
+ }
+
+ /* mvcol buffer for mpeg4 */
+ if ((dev->devtype->product != CODA_DX6) &&
+ (ctx->codec->src_fourcc == V4L2_PIX_FMT_MPEG4))
+ coda_parabuf_write(ctx, 97, ctx->internal_frames[i].paddr +
+ ysize + ysize/4 + ysize/4);
+
+ return 0;
+}
+
+static void coda_free_context_buffers(struct coda_ctx *ctx)
+{
+ struct coda_dev *dev = ctx->dev;
+
+ coda_free_aux_buf(dev, &ctx->slicebuf);
+ coda_free_aux_buf(dev, &ctx->psbuf);
+ if (dev->devtype->product != CODA_DX6)
+ coda_free_aux_buf(dev, &ctx->workbuf);
+}
+
+static int coda_alloc_context_buffers(struct coda_ctx *ctx,
+ struct coda_q_data *q_data)
+{
+ struct coda_dev *dev = ctx->dev;
+ size_t size;
+ int ret;
+
+ if (dev->devtype->product == CODA_DX6)
+ return 0;
+
+ if (ctx->psbuf.vaddr) {
+ v4l2_err(&dev->v4l2_dev, "psmembuf still allocated\n");
+ return -EBUSY;
+ }
+ if (ctx->slicebuf.vaddr) {
+ v4l2_err(&dev->v4l2_dev, "slicebuf still allocated\n");
+ return -EBUSY;
+ }
+ if (ctx->workbuf.vaddr) {
+ v4l2_err(&dev->v4l2_dev, "context buffer still allocated\n");
+ ret = -EBUSY;
+ return -ENOMEM;
+ }
+
+ if (q_data->fourcc == V4L2_PIX_FMT_H264) {
+ /* worst case slice size */
+ size = (DIV_ROUND_UP(q_data->width, 16) *
+ DIV_ROUND_UP(q_data->height, 16)) * 3200 / 8 + 512;
+ ret = coda_alloc_context_buf(ctx, &ctx->slicebuf, size,
+ "slicebuf");
+ if (ret < 0) {
+ v4l2_err(&dev->v4l2_dev,
+ "failed to allocate %d byte slice buffer",
+ ctx->slicebuf.size);
+ return ret;
+ }
+ }
+
+ if (dev->devtype->product == CODA_7541) {
+ ret = coda_alloc_context_buf(ctx, &ctx->psbuf,
+ CODA7_PS_BUF_SIZE, "psbuf");
+ if (ret < 0) {
+ v4l2_err(&dev->v4l2_dev,
+ "failed to allocate psmem buffer");
+ goto err;
+ }
+ }
+
+ size = dev->devtype->workbuf_size;
+ if (dev->devtype->product == CODA_960 &&
+ q_data->fourcc == V4L2_PIX_FMT_H264)
+ size += CODA9_PS_SAVE_SIZE;
+ ret = coda_alloc_context_buf(ctx, &ctx->workbuf, size, "workbuf");
+ if (ret < 0) {
+ v4l2_err(&dev->v4l2_dev,
+ "failed to allocate %d byte context buffer",
+ ctx->workbuf.size);
+ goto err;
+ }
+
+ return 0;
+
+err:
+ coda_free_context_buffers(ctx);
+ return ret;
+}
+
+static int coda_encode_header(struct coda_ctx *ctx, struct vb2_buffer *buf,
+ int header_code, u8 *header, int *size)
+{
+ struct coda_dev *dev = ctx->dev;
+ size_t bufsize;
+ int ret;
+ int i;
+
+ if (dev->devtype->product == CODA_960)
+ memset(vb2_plane_vaddr(buf, 0), 0, 64);
+
+ coda_write(dev, vb2_dma_contig_plane_dma_addr(buf, 0),
+ CODA_CMD_ENC_HEADER_BB_START);
+ bufsize = vb2_plane_size(buf, 0);
+ if (dev->devtype->product == CODA_960)
+ bufsize /= 1024;
+ coda_write(dev, bufsize, CODA_CMD_ENC_HEADER_BB_SIZE);
+ coda_write(dev, header_code, CODA_CMD_ENC_HEADER_CODE);
+ ret = coda_command_sync(ctx, CODA_COMMAND_ENCODE_HEADER);
+ if (ret < 0) {
+ v4l2_err(&dev->v4l2_dev, "CODA_COMMAND_ENCODE_HEADER timeout\n");
+ return ret;
+ }
+
+ if (dev->devtype->product == CODA_960) {
+ for (i = 63; i > 0; i--)
+ if (((char *)vb2_plane_vaddr(buf, 0))[i] != 0)
+ break;
+ *size = i + 1;
+ } else {
+ *size = coda_read(dev, CODA_REG_BIT_WR_PTR(ctx->reg_idx)) -
+ coda_read(dev, CODA_CMD_ENC_HEADER_BB_START);
+ }
+ memcpy(header, vb2_plane_vaddr(buf, 0), *size);
+
+ return 0;
+}
+
+static phys_addr_t coda_iram_alloc(struct coda_iram_info *iram, size_t size)
+{
+ phys_addr_t ret;
+
+ size = round_up(size, 1024);
+ if (size > iram->remaining)
+ return 0;
+ iram->remaining -= size;
+
+ ret = iram->next_paddr;
+ iram->next_paddr += size;
+
+ return ret;
+}
+
+static void coda_setup_iram(struct coda_ctx *ctx)
+{
+ struct coda_iram_info *iram_info = &ctx->iram_info;
+ struct coda_dev *dev = ctx->dev;
+ int w64, w128;
+ int mb_width;
+ int dbk_bits;
+ int bit_bits;
+ int ip_bits;
+
+ memset(iram_info, 0, sizeof(*iram_info));
+ iram_info->next_paddr = dev->iram.paddr;
+ iram_info->remaining = dev->iram.size;
+
+ if (!dev->iram.vaddr)
+ return;
+
+ switch (dev->devtype->product) {
+ case CODA_7541:
+ dbk_bits = CODA7_USE_HOST_DBK_ENABLE | CODA7_USE_DBK_ENABLE;
+ bit_bits = CODA7_USE_HOST_BIT_ENABLE | CODA7_USE_BIT_ENABLE;
+ ip_bits = CODA7_USE_HOST_IP_ENABLE | CODA7_USE_IP_ENABLE;
+ break;
+ case CODA_960:
+ dbk_bits = CODA9_USE_HOST_DBK_ENABLE | CODA9_USE_DBK_ENABLE;
+ bit_bits = CODA9_USE_HOST_BIT_ENABLE | CODA7_USE_BIT_ENABLE;
+ ip_bits = CODA9_USE_HOST_IP_ENABLE | CODA7_USE_IP_ENABLE;
+ break;
+ default: /* CODA_DX6 */
+ return;
+ }
+
+ if (ctx->inst_type == CODA_INST_ENCODER) {
+ struct coda_q_data *q_data_src;
+
+ q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
+ mb_width = DIV_ROUND_UP(q_data_src->width, 16);
+ w128 = mb_width * 128;
+ w64 = mb_width * 64;
+
+ /* Prioritize in case IRAM is too small for everything */
+ if (dev->devtype->product == CODA_7541) {
+ iram_info->search_ram_size = round_up(mb_width * 16 *
+ 36 + 2048, 1024);
+ iram_info->search_ram_paddr = coda_iram_alloc(iram_info,
+ iram_info->search_ram_size);
+ if (!iram_info->search_ram_paddr) {
+ pr_err("IRAM is smaller than the search ram size\n");
+ goto out;
+ }
+ iram_info->axi_sram_use |= CODA7_USE_HOST_ME_ENABLE |
+ CODA7_USE_ME_ENABLE;
+ }
+
+ /* Only H.264BP and H.263P3 are considered */
+ iram_info->buf_dbk_y_use = coda_iram_alloc(iram_info, w64);
+ iram_info->buf_dbk_c_use = coda_iram_alloc(iram_info, w64);
+ if (!iram_info->buf_dbk_c_use)
+ goto out;
+ iram_info->axi_sram_use |= dbk_bits;
+
+ iram_info->buf_bit_use = coda_iram_alloc(iram_info, w128);
+ if (!iram_info->buf_bit_use)
+ goto out;
+ iram_info->axi_sram_use |= bit_bits;
+
+ iram_info->buf_ip_ac_dc_use = coda_iram_alloc(iram_info, w128);
+ if (!iram_info->buf_ip_ac_dc_use)
+ goto out;
+ iram_info->axi_sram_use |= ip_bits;
+
+ /* OVL and BTP disabled for encoder */
+ } else if (ctx->inst_type == CODA_INST_DECODER) {
+ struct coda_q_data *q_data_dst;
+
+ q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+ mb_width = DIV_ROUND_UP(q_data_dst->width, 16);
+ w128 = mb_width * 128;
+
+ iram_info->buf_dbk_y_use = coda_iram_alloc(iram_info, w128);
+ iram_info->buf_dbk_c_use = coda_iram_alloc(iram_info, w128);
+ if (!iram_info->buf_dbk_c_use)
+ goto out;
+ iram_info->axi_sram_use |= dbk_bits;
+
+ iram_info->buf_bit_use = coda_iram_alloc(iram_info, w128);
+ if (!iram_info->buf_bit_use)
+ goto out;
+ iram_info->axi_sram_use |= bit_bits;
+
+ iram_info->buf_ip_ac_dc_use = coda_iram_alloc(iram_info, w128);
+ if (!iram_info->buf_ip_ac_dc_use)
+ goto out;
+ iram_info->axi_sram_use |= ip_bits;
+
+ /* OVL and BTP unused as there is no VC1 support yet */
+ }
+
+out:
+ if (!(iram_info->axi_sram_use & CODA7_USE_HOST_IP_ENABLE))
+ v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
+ "IRAM smaller than needed\n");
+
+ if (dev->devtype->product == CODA_7541) {
+ /* TODO - Enabling these causes picture errors on CODA7541 */
+ if (ctx->inst_type == CODA_INST_DECODER) {
+ /* fw 1.4.50 */
+ iram_info->axi_sram_use &= ~(CODA7_USE_HOST_IP_ENABLE |
+ CODA7_USE_IP_ENABLE);
+ } else {
+ /* fw 13.4.29 */
+ iram_info->axi_sram_use &= ~(CODA7_USE_HOST_IP_ENABLE |
+ CODA7_USE_HOST_DBK_ENABLE |
+ CODA7_USE_IP_ENABLE |
+ CODA7_USE_DBK_ENABLE);
+ }
+ }
+}
+
+static u32 coda_supported_firmwares[] = {
+ CODA_FIRMWARE_VERNUM(CODA_DX6, 2, 2, 5),
+ CODA_FIRMWARE_VERNUM(CODA_7541, 1, 4, 50),
+ CODA_FIRMWARE_VERNUM(CODA_960, 2, 1, 5),
+};
+
+static bool coda_firmware_supported(u32 vernum)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(coda_supported_firmwares); i++)
+ if (vernum == coda_supported_firmwares[i])
+ return true;
+ return false;
+}
+
+int coda_check_firmware(struct coda_dev *dev)
+{
+ u16 product, major, minor, release;
+ u32 data;
+ int ret;
+
+ ret = clk_prepare_enable(dev->clk_per);
+ if (ret)
+ goto err_clk_per;
+
+ ret = clk_prepare_enable(dev->clk_ahb);
+ if (ret)
+ goto err_clk_ahb;
+
+ coda_write(dev, 0, CODA_CMD_FIRMWARE_VERNUM);
+ coda_write(dev, CODA_REG_BIT_BUSY_FLAG, CODA_REG_BIT_BUSY);
+ coda_write(dev, 0, CODA_REG_BIT_RUN_INDEX);
+ coda_write(dev, 0, CODA_REG_BIT_RUN_COD_STD);
+ coda_write(dev, CODA_COMMAND_FIRMWARE_GET, CODA_REG_BIT_RUN_COMMAND);
+ if (coda_wait_timeout(dev)) {
+ v4l2_err(&dev->v4l2_dev, "firmware get command error\n");
+ ret = -EIO;
+ goto err_run_cmd;
+ }
+
+ if (dev->devtype->product == CODA_960) {
+ data = coda_read(dev, CODA9_CMD_FIRMWARE_CODE_REV);
+ v4l2_info(&dev->v4l2_dev, "Firmware code revision: %d\n",
+ data);
+ }
+
+ /* Check we are compatible with the loaded firmware */
+ data = coda_read(dev, CODA_CMD_FIRMWARE_VERNUM);
+ product = CODA_FIRMWARE_PRODUCT(data);
+ major = CODA_FIRMWARE_MAJOR(data);
+ minor = CODA_FIRMWARE_MINOR(data);
+ release = CODA_FIRMWARE_RELEASE(data);
+
+ clk_disable_unprepare(dev->clk_per);
+ clk_disable_unprepare(dev->clk_ahb);
+
+ if (product != dev->devtype->product) {
+ v4l2_err(&dev->v4l2_dev,
+ "Wrong firmware. Hw: %s, Fw: %s, Version: %u.%u.%u\n",
+ coda_product_name(dev->devtype->product),
+ coda_product_name(product), major, minor, release);
+ return -EINVAL;
+ }
+
+ v4l2_info(&dev->v4l2_dev, "Initialized %s.\n",
+ coda_product_name(product));
+
+ if (coda_firmware_supported(data)) {
+ v4l2_info(&dev->v4l2_dev, "Firmware version: %u.%u.%u\n",
+ major, minor, release);
+ } else {
+ v4l2_warn(&dev->v4l2_dev,
+ "Unsupported firmware version: %u.%u.%u\n",
+ major, minor, release);
+ }
+
+ return 0;
+
+err_run_cmd:
+ clk_disable_unprepare(dev->clk_ahb);
+err_clk_ahb:
+ clk_disable_unprepare(dev->clk_per);
+err_clk_per:
+ return ret;
+}
+
+/*
+ * Encoder context operations
+ */
+
+static int coda_start_encoding(struct coda_ctx *ctx)
+{
+ struct coda_dev *dev = ctx->dev;
+ struct v4l2_device *v4l2_dev = &dev->v4l2_dev;
+ struct coda_q_data *q_data_src, *q_data_dst;
+ u32 bitstream_buf, bitstream_size;
+ struct vb2_buffer *buf;
+ int gamma, ret, value;
+ u32 dst_fourcc;
+
+ q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
+ q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+ dst_fourcc = q_data_dst->fourcc;
+
+ /* Allocate per-instance buffers */
+ ret = coda_alloc_context_buffers(ctx, q_data_src);
+ if (ret < 0)
+ return ret;
+
+ buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
+ bitstream_buf = vb2_dma_contig_plane_dma_addr(buf, 0);
+ bitstream_size = q_data_dst->sizeimage;
+
+ if (!coda_is_initialized(dev)) {
+ v4l2_err(v4l2_dev, "coda is not initialized.\n");
+ return -EFAULT;
+ }
+
+ mutex_lock(&dev->coda_mutex);
+
+ coda_write(dev, ctx->parabuf.paddr, CODA_REG_BIT_PARA_BUF_ADDR);
+ coda_write(dev, bitstream_buf, CODA_REG_BIT_RD_PTR(ctx->reg_idx));
+ coda_write(dev, bitstream_buf, CODA_REG_BIT_WR_PTR(ctx->reg_idx));
+ switch (dev->devtype->product) {
+ case CODA_DX6:
+ coda_write(dev, CODADX6_STREAM_BUF_DYNALLOC_EN |
+ CODADX6_STREAM_BUF_PIC_RESET, CODA_REG_BIT_STREAM_CTRL);
+ break;
+ case CODA_960:
+ coda_write(dev, 0, CODA9_GDI_WPROT_RGN_EN);
+ /* fallthrough */
+ case CODA_7541:
+ coda_write(dev, CODA7_STREAM_BUF_DYNALLOC_EN |
+ CODA7_STREAM_BUF_PIC_RESET, CODA_REG_BIT_STREAM_CTRL);
+ break;
+ }
+
+ value = coda_read(dev, CODA_REG_BIT_FRAME_MEM_CTRL);
+ value &= ~(1 << 2 | 0x7 << 9);
+ ctx->frame_mem_ctrl = value;
+ coda_write(dev, value, CODA_REG_BIT_FRAME_MEM_CTRL);
+
+ if (dev->devtype->product == CODA_DX6) {
+ /* Configure the coda */
+ coda_write(dev, dev->iram.paddr,
+ CODADX6_REG_BIT_SEARCH_RAM_BASE_ADDR);
+ }
+
+ /* Could set rotation here if needed */
+ switch (dev->devtype->product) {
+ case CODA_DX6:
+ value = (q_data_src->width & CODADX6_PICWIDTH_MASK)
+ << CODADX6_PICWIDTH_OFFSET;
+ value |= (q_data_src->height & CODADX6_PICHEIGHT_MASK)
+ << CODA_PICHEIGHT_OFFSET;
+ break;
+ case CODA_7541:
+ if (dst_fourcc == V4L2_PIX_FMT_H264) {
+ value = (round_up(q_data_src->width, 16) &
+ CODA7_PICWIDTH_MASK) << CODA7_PICWIDTH_OFFSET;
+ value |= (round_up(q_data_src->height, 16) &
+ CODA7_PICHEIGHT_MASK) << CODA_PICHEIGHT_OFFSET;
+ break;
+ }
+ /* fallthrough */
+ case CODA_960:
+ value = (q_data_src->width & CODA7_PICWIDTH_MASK)
+ << CODA7_PICWIDTH_OFFSET;
+ value |= (q_data_src->height & CODA7_PICHEIGHT_MASK)
+ << CODA_PICHEIGHT_OFFSET;
+ }
+ coda_write(dev, value, CODA_CMD_ENC_SEQ_SRC_SIZE);
+ coda_write(dev, ctx->params.framerate,
+ CODA_CMD_ENC_SEQ_SRC_F_RATE);
+
+ ctx->params.codec_mode = ctx->codec->mode;
+ switch (dst_fourcc) {
+ case V4L2_PIX_FMT_MPEG4:
+ if (dev->devtype->product == CODA_960)
+ coda_write(dev, CODA9_STD_MPEG4,
+ CODA_CMD_ENC_SEQ_COD_STD);
+ else
+ coda_write(dev, CODA_STD_MPEG4,
+ CODA_CMD_ENC_SEQ_COD_STD);
+ coda_write(dev, 0, CODA_CMD_ENC_SEQ_MP4_PARA);
+ break;
+ case V4L2_PIX_FMT_H264:
+ if (dev->devtype->product == CODA_960)
+ coda_write(dev, CODA9_STD_H264,
+ CODA_CMD_ENC_SEQ_COD_STD);
+ else
+ coda_write(dev, CODA_STD_H264,
+ CODA_CMD_ENC_SEQ_COD_STD);
+ if (ctx->params.h264_deblk_enabled) {
+ value = ((ctx->params.h264_deblk_alpha &
+ CODA_264PARAM_DEBLKFILTEROFFSETALPHA_MASK) <<
+ CODA_264PARAM_DEBLKFILTEROFFSETALPHA_OFFSET) |
+ ((ctx->params.h264_deblk_beta &
+ CODA_264PARAM_DEBLKFILTEROFFSETBETA_MASK) <<
+ CODA_264PARAM_DEBLKFILTEROFFSETBETA_OFFSET);
+ } else {
+ value = 1 << CODA_264PARAM_DISABLEDEBLK_OFFSET;
+ }
+ coda_write(dev, value, CODA_CMD_ENC_SEQ_264_PARA);
+ break;
+ default:
+ v4l2_err(v4l2_dev,
+ "dst format (0x%08x) invalid.\n", dst_fourcc);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ switch (ctx->params.slice_mode) {
+ case V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE:
+ value = 0;
+ break;
+ case V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_MB:
+ value = (ctx->params.slice_max_mb & CODA_SLICING_SIZE_MASK)
+ << CODA_SLICING_SIZE_OFFSET;
+ value |= (1 & CODA_SLICING_UNIT_MASK)
+ << CODA_SLICING_UNIT_OFFSET;
+ value |= 1 & CODA_SLICING_MODE_MASK;
+ break;
+ case V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES:
+ value = (ctx->params.slice_max_bits & CODA_SLICING_SIZE_MASK)
+ << CODA_SLICING_SIZE_OFFSET;
+ value |= (0 & CODA_SLICING_UNIT_MASK)
+ << CODA_SLICING_UNIT_OFFSET;
+ value |= 1 & CODA_SLICING_MODE_MASK;
+ break;
+ }
+ coda_write(dev, value, CODA_CMD_ENC_SEQ_SLICE_MODE);
+ value = ctx->params.gop_size & CODA_GOP_SIZE_MASK;
+ coda_write(dev, value, CODA_CMD_ENC_SEQ_GOP_SIZE);
+
+ if (ctx->params.bitrate) {
+ /* Rate control enabled */
+ value = (ctx->params.bitrate & CODA_RATECONTROL_BITRATE_MASK)
+ << CODA_RATECONTROL_BITRATE_OFFSET;
+ value |= 1 & CODA_RATECONTROL_ENABLE_MASK;
+ if (dev->devtype->product == CODA_960)
+ value |= BIT(31); /* disable autoskip */
+ } else {
+ value = 0;
+ }
+ coda_write(dev, value, CODA_CMD_ENC_SEQ_RC_PARA);
+
+ coda_write(dev, 0, CODA_CMD_ENC_SEQ_RC_BUF_SIZE);
+ coda_write(dev, ctx->params.intra_refresh,
+ CODA_CMD_ENC_SEQ_INTRA_REFRESH);
+
+ coda_write(dev, bitstream_buf, CODA_CMD_ENC_SEQ_BB_START);
+ coda_write(dev, bitstream_size / 1024, CODA_CMD_ENC_SEQ_BB_SIZE);
+
+
+ value = 0;
+ if (dev->devtype->product == CODA_960)
+ gamma = CODA9_DEFAULT_GAMMA;
+ else
+ gamma = CODA_DEFAULT_GAMMA;
+ if (gamma > 0) {
+ coda_write(dev, (gamma & CODA_GAMMA_MASK) << CODA_GAMMA_OFFSET,
+ CODA_CMD_ENC_SEQ_RC_GAMMA);
+ }
+
+ if (ctx->params.h264_min_qp || ctx->params.h264_max_qp) {
+ coda_write(dev,
+ ctx->params.h264_min_qp << CODA_QPMIN_OFFSET |
+ ctx->params.h264_max_qp << CODA_QPMAX_OFFSET,
+ CODA_CMD_ENC_SEQ_RC_QP_MIN_MAX);
+ }
+ if (dev->devtype->product == CODA_960) {
+ if (ctx->params.h264_max_qp)
+ value |= 1 << CODA9_OPTION_RCQPMAX_OFFSET;
+ if (CODA_DEFAULT_GAMMA > 0)
+ value |= 1 << CODA9_OPTION_GAMMA_OFFSET;
+ } else {
+ if (CODA_DEFAULT_GAMMA > 0) {
+ if (dev->devtype->product == CODA_DX6)
+ value |= 1 << CODADX6_OPTION_GAMMA_OFFSET;
+ else
+ value |= 1 << CODA7_OPTION_GAMMA_OFFSET;
+ }
+ if (ctx->params.h264_min_qp)
+ value |= 1 << CODA7_OPTION_RCQPMIN_OFFSET;
+ if (ctx->params.h264_max_qp)
+ value |= 1 << CODA7_OPTION_RCQPMAX_OFFSET;
+ }
+ coda_write(dev, value, CODA_CMD_ENC_SEQ_OPTION);
+
+ coda_write(dev, 0, CODA_CMD_ENC_SEQ_RC_INTERVAL_MODE);
+
+ coda_setup_iram(ctx);
+
+ if (dst_fourcc == V4L2_PIX_FMT_H264) {
+ switch (dev->devtype->product) {
+ case CODA_DX6:
+ value = FMO_SLICE_SAVE_BUF_SIZE << 7;
+ coda_write(dev, value, CODADX6_CMD_ENC_SEQ_FMO);
+ break;
+ case CODA_7541:
+ coda_write(dev, ctx->iram_info.search_ram_paddr,
+ CODA7_CMD_ENC_SEQ_SEARCH_BASE);
+ coda_write(dev, ctx->iram_info.search_ram_size,
+ CODA7_CMD_ENC_SEQ_SEARCH_SIZE);
+ break;
+ case CODA_960:
+ coda_write(dev, 0, CODA9_CMD_ENC_SEQ_ME_OPTION);
+ coda_write(dev, 0, CODA9_CMD_ENC_SEQ_INTRA_WEIGHT);
+ }
+ }
+
+ ret = coda_command_sync(ctx, CODA_COMMAND_SEQ_INIT);
+ if (ret < 0) {
+ v4l2_err(v4l2_dev, "CODA_COMMAND_SEQ_INIT timeout\n");
+ goto out;
+ }
+
+ if (coda_read(dev, CODA_RET_ENC_SEQ_SUCCESS) == 0) {
+ v4l2_err(v4l2_dev, "CODA_COMMAND_SEQ_INIT failed\n");
+ ret = -EFAULT;
+ goto out;
+ }
+
+ if (dev->devtype->product == CODA_960)
+ ctx->num_internal_frames = 4;
+ else
+ ctx->num_internal_frames = 2;
+ ret = coda_alloc_framebuffers(ctx, q_data_src, dst_fourcc);
+ if (ret < 0) {
+ v4l2_err(v4l2_dev, "failed to allocate framebuffers\n");
+ goto out;
+ }
+
+ coda_write(dev, ctx->num_internal_frames, CODA_CMD_SET_FRAME_BUF_NUM);
+ coda_write(dev, q_data_src->bytesperline,
+ CODA_CMD_SET_FRAME_BUF_STRIDE);
+ if (dev->devtype->product == CODA_7541) {
+ coda_write(dev, q_data_src->bytesperline,
+ CODA7_CMD_SET_FRAME_SOURCE_BUF_STRIDE);
+ }
+ if (dev->devtype->product != CODA_DX6) {
+ coda_write(dev, ctx->iram_info.buf_bit_use,
+ CODA7_CMD_SET_FRAME_AXI_BIT_ADDR);
+ coda_write(dev, ctx->iram_info.buf_ip_ac_dc_use,
+ CODA7_CMD_SET_FRAME_AXI_IPACDC_ADDR);
+ coda_write(dev, ctx->iram_info.buf_dbk_y_use,
+ CODA7_CMD_SET_FRAME_AXI_DBKY_ADDR);
+ coda_write(dev, ctx->iram_info.buf_dbk_c_use,
+ CODA7_CMD_SET_FRAME_AXI_DBKC_ADDR);
+ coda_write(dev, ctx->iram_info.buf_ovl_use,
+ CODA7_CMD_SET_FRAME_AXI_OVL_ADDR);
+ if (dev->devtype->product == CODA_960) {
+ coda_write(dev, ctx->iram_info.buf_btp_use,
+ CODA9_CMD_SET_FRAME_AXI_BTP_ADDR);
+
+ /* FIXME */
+ coda_write(dev, ctx->internal_frames[2].paddr,
+ CODA9_CMD_SET_FRAME_SUBSAMP_A);
+ coda_write(dev, ctx->internal_frames[3].paddr,
+ CODA9_CMD_SET_FRAME_SUBSAMP_B);
+ }
+ }
+
+ ret = coda_command_sync(ctx, CODA_COMMAND_SET_FRAME_BUF);
+ if (ret < 0) {
+ v4l2_err(v4l2_dev, "CODA_COMMAND_SET_FRAME_BUF timeout\n");
+ goto out;
+ }
+
+ /* Save stream headers */
+ buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
+ switch (dst_fourcc) {
+ case V4L2_PIX_FMT_H264:
+ /*
+ * Get SPS in the first frame and copy it to an
+ * intermediate buffer.
+ */
+ ret = coda_encode_header(ctx, buf, CODA_HEADER_H264_SPS,
+ &ctx->vpu_header[0][0],
+ &ctx->vpu_header_size[0]);
+ if (ret < 0)
+ goto out;
+
+ /*
+ * Get PPS in the first frame and copy it to an
+ * intermediate buffer.
+ */
+ ret = coda_encode_header(ctx, buf, CODA_HEADER_H264_PPS,
+ &ctx->vpu_header[1][0],
+ &ctx->vpu_header_size[1]);
+ if (ret < 0)
+ goto out;
+
+ /*
+ * Length of H.264 headers is variable and thus it might not be
+ * aligned for the coda to append the encoded frame. In that is
+ * the case a filler NAL must be added to header 2.
+ */
+ ctx->vpu_header_size[2] = coda_h264_padding(
+ (ctx->vpu_header_size[0] +
+ ctx->vpu_header_size[1]),
+ ctx->vpu_header[2]);
+ break;
+ case V4L2_PIX_FMT_MPEG4:
+ /*
+ * Get VOS in the first frame and copy it to an
+ * intermediate buffer
+ */
+ ret = coda_encode_header(ctx, buf, CODA_HEADER_MP4V_VOS,
+ &ctx->vpu_header[0][0],
+ &ctx->vpu_header_size[0]);
+ if (ret < 0)
+ goto out;
+
+ ret = coda_encode_header(ctx, buf, CODA_HEADER_MP4V_VIS,
+ &ctx->vpu_header[1][0],
+ &ctx->vpu_header_size[1]);
+ if (ret < 0)
+ goto out;
+
+ ret = coda_encode_header(ctx, buf, CODA_HEADER_MP4V_VOL,
+ &ctx->vpu_header[2][0],
+ &ctx->vpu_header_size[2]);
+ if (ret < 0)
+ goto out;
+ break;
+ default:
+ /* No more formats need to save headers at the moment */
+ break;
+ }
+
+out:
+ mutex_unlock(&dev->coda_mutex);
+ return ret;
+}
+
+static int coda_prepare_encode(struct coda_ctx *ctx)
+{
+ struct coda_q_data *q_data_src, *q_data_dst;
+ struct vb2_buffer *src_buf, *dst_buf;
+ struct coda_dev *dev = ctx->dev;
+ int force_ipicture;
+ int quant_param = 0;
+ u32 picture_y, picture_cb, picture_cr;
+ u32 pic_stream_buffer_addr, pic_stream_buffer_size;
+ u32 dst_fourcc;
+
+ src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
+ dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
+ q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
+ q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+ dst_fourcc = q_data_dst->fourcc;
+
+ src_buf->v4l2_buf.sequence = ctx->osequence;
+ dst_buf->v4l2_buf.sequence = ctx->osequence;
+ ctx->osequence++;
+
+ /*
+ * Workaround coda firmware BUG that only marks the first
+ * frame as IDR. This is a problem for some decoders that can't
+ * recover when a frame is lost.
+ */
+ if (src_buf->v4l2_buf.sequence % ctx->params.gop_size) {
+ src_buf->v4l2_buf.flags |= V4L2_BUF_FLAG_PFRAME;
+ src_buf->v4l2_buf.flags &= ~V4L2_BUF_FLAG_KEYFRAME;
+ } else {
+ src_buf->v4l2_buf.flags |= V4L2_BUF_FLAG_KEYFRAME;
+ src_buf->v4l2_buf.flags &= ~V4L2_BUF_FLAG_PFRAME;
+ }
+
+ if (dev->devtype->product == CODA_960)
+ coda_set_gdi_regs(ctx);
+
+ /*
+ * Copy headers at the beginning of the first frame for H.264 only.
+ * In MPEG4 they are already copied by the coda.
+ */
+ if (src_buf->v4l2_buf.sequence == 0) {
+ pic_stream_buffer_addr =
+ vb2_dma_contig_plane_dma_addr(dst_buf, 0) +
+ ctx->vpu_header_size[0] +
+ ctx->vpu_header_size[1] +
+ ctx->vpu_header_size[2];
+ pic_stream_buffer_size = CODA_MAX_FRAME_SIZE -
+ ctx->vpu_header_size[0] -
+ ctx->vpu_header_size[1] -
+ ctx->vpu_header_size[2];
+ memcpy(vb2_plane_vaddr(dst_buf, 0),
+ &ctx->vpu_header[0][0], ctx->vpu_header_size[0]);
+ memcpy(vb2_plane_vaddr(dst_buf, 0) + ctx->vpu_header_size[0],
+ &ctx->vpu_header[1][0], ctx->vpu_header_size[1]);
+ memcpy(vb2_plane_vaddr(dst_buf, 0) + ctx->vpu_header_size[0] +
+ ctx->vpu_header_size[1], &ctx->vpu_header[2][0],
+ ctx->vpu_header_size[2]);
+ } else {
+ pic_stream_buffer_addr =
+ vb2_dma_contig_plane_dma_addr(dst_buf, 0);
+ pic_stream_buffer_size = CODA_MAX_FRAME_SIZE;
+ }
+
+ if (src_buf->v4l2_buf.flags & V4L2_BUF_FLAG_KEYFRAME) {
+ force_ipicture = 1;
+ switch (dst_fourcc) {
+ case V4L2_PIX_FMT_H264:
+ quant_param = ctx->params.h264_intra_qp;
+ break;
+ case V4L2_PIX_FMT_MPEG4:
+ quant_param = ctx->params.mpeg4_intra_qp;
+ break;
+ default:
+ v4l2_warn(&ctx->dev->v4l2_dev,
+ "cannot set intra qp, fmt not supported\n");
+ break;
+ }
+ } else {
+ force_ipicture = 0;
+ switch (dst_fourcc) {
+ case V4L2_PIX_FMT_H264:
+ quant_param = ctx->params.h264_inter_qp;
+ break;
+ case V4L2_PIX_FMT_MPEG4:
+ quant_param = ctx->params.mpeg4_inter_qp;
+ break;
+ default:
+ v4l2_warn(&ctx->dev->v4l2_dev,
+ "cannot set inter qp, fmt not supported\n");
+ break;
+ }
+ }
+
+ /* submit */
+ coda_write(dev, CODA_ROT_MIR_ENABLE | ctx->params.rot_mode,
+ CODA_CMD_ENC_PIC_ROT_MODE);
+ coda_write(dev, quant_param, CODA_CMD_ENC_PIC_QS);
+
+
+ picture_y = vb2_dma_contig_plane_dma_addr(src_buf, 0);
+ switch (q_data_src->fourcc) {
+ case V4L2_PIX_FMT_YVU420:
+ /* Switch Cb and Cr for YVU420 format */
+ picture_cr = picture_y + q_data_src->bytesperline *
+ q_data_src->height;
+ picture_cb = picture_cr + q_data_src->bytesperline / 2 *
+ q_data_src->height / 2;
+ break;
+ case V4L2_PIX_FMT_YUV420:
+ default:
+ picture_cb = picture_y + q_data_src->bytesperline *
+ q_data_src->height;
+ picture_cr = picture_cb + q_data_src->bytesperline / 2 *
+ q_data_src->height / 2;
+ break;
+ }
+
+ if (dev->devtype->product == CODA_960) {
+ coda_write(dev, 4/*FIXME: 0*/, CODA9_CMD_ENC_PIC_SRC_INDEX);
+ coda_write(dev, q_data_src->width, CODA9_CMD_ENC_PIC_SRC_STRIDE);
+ coda_write(dev, 0, CODA9_CMD_ENC_PIC_SUB_FRAME_SYNC);
+
+ coda_write(dev, picture_y, CODA9_CMD_ENC_PIC_SRC_ADDR_Y);
+ coda_write(dev, picture_cb, CODA9_CMD_ENC_PIC_SRC_ADDR_CB);
+ coda_write(dev, picture_cr, CODA9_CMD_ENC_PIC_SRC_ADDR_CR);
+ } else {
+ coda_write(dev, picture_y, CODA_CMD_ENC_PIC_SRC_ADDR_Y);
+ coda_write(dev, picture_cb, CODA_CMD_ENC_PIC_SRC_ADDR_CB);
+ coda_write(dev, picture_cr, CODA_CMD_ENC_PIC_SRC_ADDR_CR);
+ }
+ coda_write(dev, force_ipicture << 1 & 0x2,
+ CODA_CMD_ENC_PIC_OPTION);
+
+ coda_write(dev, pic_stream_buffer_addr, CODA_CMD_ENC_PIC_BB_START);
+ coda_write(dev, pic_stream_buffer_size / 1024,
+ CODA_CMD_ENC_PIC_BB_SIZE);
+
+ if (!ctx->streamon_out) {
+ /* After streamoff on the output side, set stream end flag */
+ ctx->bit_stream_param |= CODA_BIT_STREAM_END_FLAG;
+ coda_write(dev, ctx->bit_stream_param,
+ CODA_REG_BIT_BIT_STREAM_PARAM);
+ }
+
+ if (dev->devtype->product != CODA_DX6)
+ coda_write(dev, ctx->iram_info.axi_sram_use,
+ CODA7_REG_BIT_AXI_SRAM_USE);
+
+ coda_command_async(ctx, CODA_COMMAND_PIC_RUN);
+
+ return 0;
+}
+
+static void coda_finish_encode(struct coda_ctx *ctx)
+{
+ struct vb2_buffer *src_buf, *dst_buf;
+ struct coda_dev *dev = ctx->dev;
+ u32 wr_ptr, start_ptr;
+
+ src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
+ dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
+
+ /* Get results from the coda */
+ start_ptr = coda_read(dev, CODA_CMD_ENC_PIC_BB_START);
+ wr_ptr = coda_read(dev, CODA_REG_BIT_WR_PTR(ctx->reg_idx));
+
+ /* Calculate bytesused field */
+ if (dst_buf->v4l2_buf.sequence == 0) {
+ vb2_set_plane_payload(dst_buf, 0, wr_ptr - start_ptr +
+ ctx->vpu_header_size[0] +
+ ctx->vpu_header_size[1] +
+ ctx->vpu_header_size[2]);
+ } else {
+ vb2_set_plane_payload(dst_buf, 0, wr_ptr - start_ptr);
+ }
+
+ v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, "frame size = %u\n",
+ wr_ptr - start_ptr);
+
+ coda_read(dev, CODA_RET_ENC_PIC_SLICE_NUM);
+ coda_read(dev, CODA_RET_ENC_PIC_FLAG);
+
+ if (coda_read(dev, CODA_RET_ENC_PIC_TYPE) == 0) {
+ dst_buf->v4l2_buf.flags |= V4L2_BUF_FLAG_KEYFRAME;
+ dst_buf->v4l2_buf.flags &= ~V4L2_BUF_FLAG_PFRAME;
+ } else {
+ dst_buf->v4l2_buf.flags |= V4L2_BUF_FLAG_PFRAME;
+ dst_buf->v4l2_buf.flags &= ~V4L2_BUF_FLAG_KEYFRAME;
+ }
+
+ dst_buf->v4l2_buf.timestamp = src_buf->v4l2_buf.timestamp;
+ dst_buf->v4l2_buf.flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
+ dst_buf->v4l2_buf.flags |=
+ src_buf->v4l2_buf.flags & V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
+ dst_buf->v4l2_buf.timecode = src_buf->v4l2_buf.timecode;
+
+ v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE);
+
+ dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
+ v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_DONE);
+
+ ctx->gopcounter--;
+ if (ctx->gopcounter < 0)
+ ctx->gopcounter = ctx->params.gop_size - 1;
+
+ v4l2_dbg(1, coda_debug, &dev->v4l2_dev,
+ "job finished: encoding frame (%d) (%s)\n",
+ dst_buf->v4l2_buf.sequence,
+ (dst_buf->v4l2_buf.flags & V4L2_BUF_FLAG_KEYFRAME) ?
+ "KEYFRAME" : "PFRAME");
+}
+
+static void coda_seq_end_work(struct work_struct *work)
+{
+ struct coda_ctx *ctx = container_of(work, struct coda_ctx, seq_end_work);
+ struct coda_dev *dev = ctx->dev;
+
+ mutex_lock(&ctx->buffer_mutex);
+ mutex_lock(&dev->coda_mutex);
+
+ v4l2_dbg(1, coda_debug, &dev->v4l2_dev,
+ "%d: %s: sent command 'SEQ_END' to coda\n", ctx->idx,
+ __func__);
+ if (coda_command_sync(ctx, CODA_COMMAND_SEQ_END)) {
+ v4l2_err(&dev->v4l2_dev,
+ "CODA_COMMAND_SEQ_END failed\n");
+ }
+
+ kfifo_init(&ctx->bitstream_fifo,
+ ctx->bitstream.vaddr, ctx->bitstream.size);
+
+ coda_free_framebuffers(ctx);
+ coda_free_context_buffers(ctx);
+
+ mutex_unlock(&dev->coda_mutex);
+ mutex_unlock(&ctx->buffer_mutex);
+}
+
+static void coda_bit_release(struct coda_ctx *ctx)
+{
+ coda_free_framebuffers(ctx);
+ coda_free_context_buffers(ctx);
+}
+
+const struct coda_context_ops coda_bit_encode_ops = {
+ .queue_init = coda_encoder_queue_init,
+ .start_streaming = coda_start_encoding,
+ .prepare_run = coda_prepare_encode,
+ .finish_run = coda_finish_encode,
+ .seq_end_work = coda_seq_end_work,
+ .release = coda_bit_release,
+};
+
+/*
+ * Decoder context operations
+ */
+
+static int __coda_start_decoding(struct coda_ctx *ctx)
+{
+ struct coda_q_data *q_data_src, *q_data_dst;
+ u32 bitstream_buf, bitstream_size;
+ struct coda_dev *dev = ctx->dev;
+ int width, height;
+ u32 src_fourcc;
+ u32 val;
+ int ret;
+
+ /* Start decoding */
+ q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
+ q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+ bitstream_buf = ctx->bitstream.paddr;
+ bitstream_size = ctx->bitstream.size;
+ src_fourcc = q_data_src->fourcc;
+
+ /* Allocate per-instance buffers */
+ ret = coda_alloc_context_buffers(ctx, q_data_src);
+ if (ret < 0)
+ return ret;
+
+ coda_write(dev, ctx->parabuf.paddr, CODA_REG_BIT_PARA_BUF_ADDR);
+
+ /* Update coda bitstream read and write pointers from kfifo */
+ coda_kfifo_sync_to_device_full(ctx);
+
+ ctx->display_idx = -1;
+ ctx->frm_dis_flg = 0;
+ coda_write(dev, 0, CODA_REG_BIT_FRM_DIS_FLG(ctx->reg_idx));
+
+ coda_write(dev, CODA_BIT_DEC_SEQ_INIT_ESCAPE,
+ CODA_REG_BIT_BIT_STREAM_PARAM);
+
+ coda_write(dev, bitstream_buf, CODA_CMD_DEC_SEQ_BB_START);
+ coda_write(dev, bitstream_size / 1024, CODA_CMD_DEC_SEQ_BB_SIZE);
+ val = 0;
+ if ((dev->devtype->product == CODA_7541) ||
+ (dev->devtype->product == CODA_960))
+ val |= CODA_REORDER_ENABLE;
+ coda_write(dev, val, CODA_CMD_DEC_SEQ_OPTION);
+
+ ctx->params.codec_mode = ctx->codec->mode;
+ if (dev->devtype->product == CODA_960 &&
+ src_fourcc == V4L2_PIX_FMT_MPEG4)
+ ctx->params.codec_mode_aux = CODA_MP4_AUX_MPEG4;
+ else
+ ctx->params.codec_mode_aux = 0;
+ if (src_fourcc == V4L2_PIX_FMT_H264) {
+ if (dev->devtype->product == CODA_7541) {
+ coda_write(dev, ctx->psbuf.paddr,
+ CODA_CMD_DEC_SEQ_PS_BB_START);
+ coda_write(dev, (CODA7_PS_BUF_SIZE / 1024),
+ CODA_CMD_DEC_SEQ_PS_BB_SIZE);
+ }
+ if (dev->devtype->product == CODA_960) {
+ coda_write(dev, 0, CODA_CMD_DEC_SEQ_X264_MV_EN);
+ coda_write(dev, 512, CODA_CMD_DEC_SEQ_SPP_CHUNK_SIZE);
+ }
+ }
+ if (dev->devtype->product != CODA_960)
+ coda_write(dev, 0, CODA_CMD_DEC_SEQ_SRC_SIZE);
+
+ if (coda_command_sync(ctx, CODA_COMMAND_SEQ_INIT)) {
+ v4l2_err(&dev->v4l2_dev, "CODA_COMMAND_SEQ_INIT timeout\n");
+ coda_write(dev, 0, CODA_REG_BIT_BIT_STREAM_PARAM);
+ return -ETIMEDOUT;
+ }
+
+ /* Update kfifo out pointer from coda bitstream read pointer */
+ coda_kfifo_sync_from_device(ctx);
+
+ coda_write(dev, 0, CODA_REG_BIT_BIT_STREAM_PARAM);
+
+ if (coda_read(dev, CODA_RET_DEC_SEQ_SUCCESS) == 0) {
+ v4l2_err(&dev->v4l2_dev,
+ "CODA_COMMAND_SEQ_INIT failed, error code = %d\n",
+ coda_read(dev, CODA_RET_DEC_SEQ_ERR_REASON));
+ return -EAGAIN;
+ }
+
+ val = coda_read(dev, CODA_RET_DEC_SEQ_SRC_SIZE);
+ if (dev->devtype->product == CODA_DX6) {
+ width = (val >> CODADX6_PICWIDTH_OFFSET) & CODADX6_PICWIDTH_MASK;
+ height = val & CODADX6_PICHEIGHT_MASK;
+ } else {
+ width = (val >> CODA7_PICWIDTH_OFFSET) & CODA7_PICWIDTH_MASK;
+ height = val & CODA7_PICHEIGHT_MASK;
+ }
+
+ if (width > q_data_dst->width || height > q_data_dst->height) {
+ v4l2_err(&dev->v4l2_dev, "stream is %dx%d, not %dx%d\n",
+ width, height, q_data_dst->width, q_data_dst->height);
+ return -EINVAL;
+ }
+
+ width = round_up(width, 16);
+ height = round_up(height, 16);
+
+ v4l2_dbg(1, coda_debug, &dev->v4l2_dev, "%s instance %d now: %dx%d\n",
+ __func__, ctx->idx, width, height);
+
+ ctx->num_internal_frames = coda_read(dev, CODA_RET_DEC_SEQ_FRAME_NEED);
+ if (ctx->num_internal_frames > CODA_MAX_FRAMEBUFFERS) {
+ v4l2_err(&dev->v4l2_dev,
+ "not enough framebuffers to decode (%d < %d)\n",
+ CODA_MAX_FRAMEBUFFERS, ctx->num_internal_frames);
+ return -EINVAL;
+ }
+
+ if (src_fourcc == V4L2_PIX_FMT_H264) {
+ u32 left_right;
+ u32 top_bottom;
+
+ left_right = coda_read(dev, CODA_RET_DEC_SEQ_CROP_LEFT_RIGHT);
+ top_bottom = coda_read(dev, CODA_RET_DEC_SEQ_CROP_TOP_BOTTOM);
+
+ q_data_dst->rect.left = (left_right >> 10) & 0x3ff;
+ q_data_dst->rect.top = (top_bottom >> 10) & 0x3ff;
+ q_data_dst->rect.width = width - q_data_dst->rect.left -
+ (left_right & 0x3ff);
+ q_data_dst->rect.height = height - q_data_dst->rect.top -
+ (top_bottom & 0x3ff);
+ }
+
+ ret = coda_alloc_framebuffers(ctx, q_data_dst, src_fourcc);
+ if (ret < 0) {
+ v4l2_err(&dev->v4l2_dev, "failed to allocate framebuffers\n");
+ return ret;
+ }
+
+ /* Tell the decoder how many frame buffers we allocated. */
+ coda_write(dev, ctx->num_internal_frames, CODA_CMD_SET_FRAME_BUF_NUM);
+ coda_write(dev, width, CODA_CMD_SET_FRAME_BUF_STRIDE);
+
+ if (dev->devtype->product != CODA_DX6) {
+ /* Set secondary AXI IRAM */
+ coda_setup_iram(ctx);
+
+ coda_write(dev, ctx->iram_info.buf_bit_use,
+ CODA7_CMD_SET_FRAME_AXI_BIT_ADDR);
+ coda_write(dev, ctx->iram_info.buf_ip_ac_dc_use,
+ CODA7_CMD_SET_FRAME_AXI_IPACDC_ADDR);
+ coda_write(dev, ctx->iram_info.buf_dbk_y_use,
+ CODA7_CMD_SET_FRAME_AXI_DBKY_ADDR);
+ coda_write(dev, ctx->iram_info.buf_dbk_c_use,
+ CODA7_CMD_SET_FRAME_AXI_DBKC_ADDR);
+ coda_write(dev, ctx->iram_info.buf_ovl_use,
+ CODA7_CMD_SET_FRAME_AXI_OVL_ADDR);
+ if (dev->devtype->product == CODA_960)
+ coda_write(dev, ctx->iram_info.buf_btp_use,
+ CODA9_CMD_SET_FRAME_AXI_BTP_ADDR);
+ }
+
+ if (dev->devtype->product == CODA_960) {
+ coda_write(dev, -1, CODA9_CMD_SET_FRAME_DELAY);
+
+ coda_write(dev, 0x20262024, CODA9_CMD_SET_FRAME_CACHE_SIZE);
+ coda_write(dev, 2 << CODA9_CACHE_PAGEMERGE_OFFSET |
+ 32 << CODA9_CACHE_LUMA_BUFFER_SIZE_OFFSET |
+ 8 << CODA9_CACHE_CB_BUFFER_SIZE_OFFSET |
+ 8 << CODA9_CACHE_CR_BUFFER_SIZE_OFFSET,
+ CODA9_CMD_SET_FRAME_CACHE_CONFIG);
+ }
+
+ if (src_fourcc == V4L2_PIX_FMT_H264) {
+ coda_write(dev, ctx->slicebuf.paddr,
+ CODA_CMD_SET_FRAME_SLICE_BB_START);
+ coda_write(dev, ctx->slicebuf.size / 1024,
+ CODA_CMD_SET_FRAME_SLICE_BB_SIZE);
+ }
+
+ if (dev->devtype->product == CODA_7541) {
+ int max_mb_x = 1920 / 16;
+ int max_mb_y = 1088 / 16;
+ int max_mb_num = max_mb_x * max_mb_y;
+
+ coda_write(dev, max_mb_num << 16 | max_mb_x << 8 | max_mb_y,
+ CODA7_CMD_SET_FRAME_MAX_DEC_SIZE);
+ } else if (dev->devtype->product == CODA_960) {
+ int max_mb_x = 1920 / 16;
+ int max_mb_y = 1088 / 16;
+ int max_mb_num = max_mb_x * max_mb_y;
+
+ coda_write(dev, max_mb_num << 16 | max_mb_x << 8 | max_mb_y,
+ CODA9_CMD_SET_FRAME_MAX_DEC_SIZE);
+ }
+
+ if (coda_command_sync(ctx, CODA_COMMAND_SET_FRAME_BUF)) {
+ v4l2_err(&ctx->dev->v4l2_dev,
+ "CODA_COMMAND_SET_FRAME_BUF timeout\n");
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static int coda_start_decoding(struct coda_ctx *ctx)
+{
+ struct coda_dev *dev = ctx->dev;
+ int ret;
+
+ mutex_lock(&dev->coda_mutex);
+ ret = __coda_start_decoding(ctx);
+ mutex_unlock(&dev->coda_mutex);
+
+ return ret;
+}
+
+static int coda_prepare_decode(struct coda_ctx *ctx)
+{
+ struct vb2_buffer *dst_buf;
+ struct coda_dev *dev = ctx->dev;
+ struct coda_q_data *q_data_dst;
+ u32 stridey, height;
+ u32 picture_y, picture_cb, picture_cr;
+
+ dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
+ q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+
+ if (ctx->params.rot_mode & CODA_ROT_90) {
+ stridey = q_data_dst->height;
+ height = q_data_dst->width;
+ } else {
+ stridey = q_data_dst->width;
+ height = q_data_dst->height;
+ }
+
+ /* Try to copy source buffer contents into the bitstream ringbuffer */
+ mutex_lock(&ctx->bitstream_mutex);
+ coda_fill_bitstream(ctx);
+ mutex_unlock(&ctx->bitstream_mutex);
+
+ if (coda_get_bitstream_payload(ctx) < 512 &&
+ (!(ctx->bit_stream_param & CODA_BIT_STREAM_END_FLAG))) {
+ v4l2_dbg(1, coda_debug, &dev->v4l2_dev,
+ "bitstream payload: %d, skipping\n",
+ coda_get_bitstream_payload(ctx));
+ v4l2_m2m_job_finish(ctx->dev->m2m_dev, ctx->fh.m2m_ctx);
+ return -EAGAIN;
+ }
+
+ /* Run coda_start_decoding (again) if not yet initialized */
+ if (!ctx->initialized) {
+ int ret = __coda_start_decoding(ctx);
+
+ if (ret < 0) {
+ v4l2_err(&dev->v4l2_dev, "failed to start decoding\n");
+ v4l2_m2m_job_finish(ctx->dev->m2m_dev, ctx->fh.m2m_ctx);
+ return -EAGAIN;
+ } else {
+ ctx->initialized = 1;
+ }
+ }
+
+ if (dev->devtype->product == CODA_960)
+ coda_set_gdi_regs(ctx);
+
+ /* Set rotator output */
+ picture_y = vb2_dma_contig_plane_dma_addr(dst_buf, 0);
+ if (q_data_dst->fourcc == V4L2_PIX_FMT_YVU420) {
+ /* Switch Cr and Cb for YVU420 format */
+ picture_cr = picture_y + stridey * height;
+ picture_cb = picture_cr + stridey / 2 * height / 2;
+ } else {
+ picture_cb = picture_y + stridey * height;
+ picture_cr = picture_cb + stridey / 2 * height / 2;
+ }
+
+ if (dev->devtype->product == CODA_960) {
+ /*
+ * The CODA960 seems to have an internal list of buffers with
+ * 64 entries that includes the registered frame buffers as
+ * well as the rotator buffer output.
+ * ROT_INDEX needs to be < 0x40, but > ctx->num_internal_frames.
+ */
+ coda_write(dev, CODA_MAX_FRAMEBUFFERS + dst_buf->v4l2_buf.index,
+ CODA9_CMD_DEC_PIC_ROT_INDEX);
+ coda_write(dev, picture_y, CODA9_CMD_DEC_PIC_ROT_ADDR_Y);
+ coda_write(dev, picture_cb, CODA9_CMD_DEC_PIC_ROT_ADDR_CB);
+ coda_write(dev, picture_cr, CODA9_CMD_DEC_PIC_ROT_ADDR_CR);
+ coda_write(dev, stridey, CODA9_CMD_DEC_PIC_ROT_STRIDE);
+ } else {
+ coda_write(dev, picture_y, CODA_CMD_DEC_PIC_ROT_ADDR_Y);
+ coda_write(dev, picture_cb, CODA_CMD_DEC_PIC_ROT_ADDR_CB);
+ coda_write(dev, picture_cr, CODA_CMD_DEC_PIC_ROT_ADDR_CR);
+ coda_write(dev, stridey, CODA_CMD_DEC_PIC_ROT_STRIDE);
+ }
+ coda_write(dev, CODA_ROT_MIR_ENABLE | ctx->params.rot_mode,
+ CODA_CMD_DEC_PIC_ROT_MODE);
+
+ switch (dev->devtype->product) {
+ case CODA_DX6:
+ /* TBD */
+ case CODA_7541:
+ coda_write(dev, CODA_PRE_SCAN_EN, CODA_CMD_DEC_PIC_OPTION);
+ break;
+ case CODA_960:
+ /* 'hardcode to use interrupt disable mode'? */
+ coda_write(dev, (1 << 10), CODA_CMD_DEC_PIC_OPTION);
+ break;
+ }
+
+ coda_write(dev, 0, CODA_CMD_DEC_PIC_SKIP_NUM);
+
+ coda_write(dev, 0, CODA_CMD_DEC_PIC_BB_START);
+ coda_write(dev, 0, CODA_CMD_DEC_PIC_START_BYTE);
+
+ if (dev->devtype->product != CODA_DX6)
+ coda_write(dev, ctx->iram_info.axi_sram_use,
+ CODA7_REG_BIT_AXI_SRAM_USE);
+
+ coda_kfifo_sync_to_device_full(ctx);
+
+ coda_command_async(ctx, CODA_COMMAND_PIC_RUN);
+
+ return 0;
+}
+
+static void coda_finish_decode(struct coda_ctx *ctx)
+{
+ struct coda_dev *dev = ctx->dev;
+ struct coda_q_data *q_data_src;
+ struct coda_q_data *q_data_dst;
+ struct vb2_buffer *dst_buf;
+ struct coda_timestamp *ts;
+ int width, height;
+ int decoded_idx;
+ int display_idx;
+ u32 src_fourcc;
+ int success;
+ u32 err_mb;
+ u32 val;
+
+ /* Update kfifo out pointer from coda bitstream read pointer */
+ coda_kfifo_sync_from_device(ctx);
+
+ /*
+ * in stream-end mode, the read pointer can overshoot the write pointer
+ * by up to 512 bytes
+ */
+ if (ctx->bit_stream_param & CODA_BIT_STREAM_END_FLAG) {
+ if (coda_get_bitstream_payload(ctx) >= CODA_MAX_FRAME_SIZE - 512)
+ kfifo_init(&ctx->bitstream_fifo,
+ ctx->bitstream.vaddr, ctx->bitstream.size);
+ }
+
+ q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
+ src_fourcc = q_data_src->fourcc;
+
+ val = coda_read(dev, CODA_RET_DEC_PIC_SUCCESS);
+ if (val != 1)
+ pr_err("DEC_PIC_SUCCESS = %d\n", val);
+
+ success = val & 0x1;
+ if (!success)
+ v4l2_err(&dev->v4l2_dev, "decode failed\n");
+
+ if (src_fourcc == V4L2_PIX_FMT_H264) {
+ if (val & (1 << 3))
+ v4l2_err(&dev->v4l2_dev,
+ "insufficient PS buffer space (%d bytes)\n",
+ ctx->psbuf.size);
+ if (val & (1 << 2))
+ v4l2_err(&dev->v4l2_dev,
+ "insufficient slice buffer space (%d bytes)\n",
+ ctx->slicebuf.size);
+ }
+
+ val = coda_read(dev, CODA_RET_DEC_PIC_SIZE);
+ width = (val >> 16) & 0xffff;
+ height = val & 0xffff;
+
+ q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+
+ /* frame crop information */
+ if (src_fourcc == V4L2_PIX_FMT_H264) {
+ u32 left_right;
+ u32 top_bottom;
+
+ left_right = coda_read(dev, CODA_RET_DEC_PIC_CROP_LEFT_RIGHT);
+ top_bottom = coda_read(dev, CODA_RET_DEC_PIC_CROP_TOP_BOTTOM);
+
+ if (left_right == 0xffffffff && top_bottom == 0xffffffff) {
+ /* Keep current crop information */
+ } else {
+ struct v4l2_rect *rect = &q_data_dst->rect;
+
+ rect->left = left_right >> 16 & 0xffff;
+ rect->top = top_bottom >> 16 & 0xffff;
+ rect->width = width - rect->left -
+ (left_right & 0xffff);
+ rect->height = height - rect->top -
+ (top_bottom & 0xffff);
+ }
+ } else {
+ /* no cropping */
+ }
+
+ err_mb = coda_read(dev, CODA_RET_DEC_PIC_ERR_MB);
+ if (err_mb > 0)
+ v4l2_err(&dev->v4l2_dev,
+ "errors in %d macroblocks\n", err_mb);
+
+ if (dev->devtype->product == CODA_7541) {
+ val = coda_read(dev, CODA_RET_DEC_PIC_OPTION);
+ if (val == 0) {
+ /* not enough bitstream data */
+ v4l2_dbg(1, coda_debug, &dev->v4l2_dev,
+ "prescan failed: %d\n", val);
+ ctx->hold = true;
+ return;
+ }
+ }
+
+ ctx->frm_dis_flg = coda_read(dev,
+ CODA_REG_BIT_FRM_DIS_FLG(ctx->reg_idx));
+
+ /*
+ * The previous display frame was copied out by the rotator,
+ * now it can be overwritten again
+ */
+ if (ctx->display_idx >= 0 &&
+ ctx->display_idx < ctx->num_internal_frames) {
+ ctx->frm_dis_flg &= ~(1 << ctx->display_idx);
+ coda_write(dev, ctx->frm_dis_flg,
+ CODA_REG_BIT_FRM_DIS_FLG(ctx->reg_idx));
+ }
+
+ /*
+ * The index of the last decoded frame, not necessarily in
+ * display order, and the index of the next display frame.
+ * The latter could have been decoded in a previous run.
+ */
+ decoded_idx = coda_read(dev, CODA_RET_DEC_PIC_CUR_IDX);
+ display_idx = coda_read(dev, CODA_RET_DEC_PIC_FRAME_IDX);
+
+ if (decoded_idx == -1) {
+ /* no frame was decoded, but we might have a display frame */
+ if (display_idx >= 0 && display_idx < ctx->num_internal_frames)
+ ctx->sequence_offset++;
+ else if (ctx->display_idx < 0)
+ ctx->hold = true;
+ } else if (decoded_idx == -2) {
+ /* no frame was decoded, we still return remaining buffers */
+ } else if (decoded_idx < 0 || decoded_idx >= ctx->num_internal_frames) {
+ v4l2_err(&dev->v4l2_dev,
+ "decoded frame index out of range: %d\n", decoded_idx);
+ } else {
+ val = coda_read(dev, CODA_RET_DEC_PIC_FRAME_NUM) - 1;
+ val -= ctx->sequence_offset;
+ mutex_lock(&ctx->bitstream_mutex);
+ if (!list_empty(&ctx->timestamp_list)) {
+ ts = list_first_entry(&ctx->timestamp_list,
+ struct coda_timestamp, list);
+ list_del(&ts->list);
+ if (val != (ts->sequence & 0xffff)) {
+ v4l2_err(&dev->v4l2_dev,
+ "sequence number mismatch (%d(%d) != %d)\n",
+ val, ctx->sequence_offset,
+ ts->sequence);
+ }
+ ctx->frame_timestamps[decoded_idx] = *ts;
+ kfree(ts);
+ } else {
+ v4l2_err(&dev->v4l2_dev, "empty timestamp list!\n");
+ memset(&ctx->frame_timestamps[decoded_idx], 0,
+ sizeof(struct coda_timestamp));
+ ctx->frame_timestamps[decoded_idx].sequence = val;
+ }
+ mutex_unlock(&ctx->bitstream_mutex);
+
+ val = coda_read(dev, CODA_RET_DEC_PIC_TYPE) & 0x7;
+ if (val == 0)
+ ctx->frame_types[decoded_idx] = V4L2_BUF_FLAG_KEYFRAME;
+ else if (val == 1)
+ ctx->frame_types[decoded_idx] = V4L2_BUF_FLAG_PFRAME;
+ else
+ ctx->frame_types[decoded_idx] = V4L2_BUF_FLAG_BFRAME;
+
+ ctx->frame_errors[decoded_idx] = err_mb;
+ }
+
+ if (display_idx == -1) {
+ /*
+ * no more frames to be decoded, but there could still
+ * be rotator output to dequeue
+ */
+ ctx->hold = true;
+ } else if (display_idx == -3) {
+ /* possibly prescan failure */
+ } else if (display_idx < 0 || display_idx >= ctx->num_internal_frames) {
+ v4l2_err(&dev->v4l2_dev,
+ "presentation frame index out of range: %d\n",
+ display_idx);
+ }
+
+ /* If a frame was copied out, return it */
+ if (ctx->display_idx >= 0 &&
+ ctx->display_idx < ctx->num_internal_frames) {
+ dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
+ dst_buf->v4l2_buf.sequence = ctx->osequence++;
+
+ dst_buf->v4l2_buf.flags &= ~(V4L2_BUF_FLAG_KEYFRAME |
+ V4L2_BUF_FLAG_PFRAME |
+ V4L2_BUF_FLAG_BFRAME);
+ dst_buf->v4l2_buf.flags |= ctx->frame_types[ctx->display_idx];
+ ts = &ctx->frame_timestamps[ctx->display_idx];
+ dst_buf->v4l2_buf.timecode = ts->timecode;
+ dst_buf->v4l2_buf.timestamp = ts->timestamp;
+
+ vb2_set_plane_payload(dst_buf, 0, width * height * 3 / 2);
+
+ v4l2_m2m_buf_done(dst_buf, ctx->frame_errors[display_idx] ?
+ VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE);
+
+ v4l2_dbg(1, coda_debug, &dev->v4l2_dev,
+ "job finished: decoding frame (%d) (%s)\n",
+ dst_buf->v4l2_buf.sequence,
+ (dst_buf->v4l2_buf.flags & V4L2_BUF_FLAG_KEYFRAME) ?
+ "KEYFRAME" : "PFRAME");
+ } else {
+ v4l2_dbg(1, coda_debug, &dev->v4l2_dev,
+ "job finished: no frame decoded\n");
+ }
+
+ /* The rotator will copy the current display frame next time */
+ ctx->display_idx = display_idx;
+}
+
+const struct coda_context_ops coda_bit_decode_ops = {
+ .queue_init = coda_decoder_queue_init,
+ .start_streaming = coda_start_decoding,
+ .prepare_run = coda_prepare_decode,
+ .finish_run = coda_finish_decode,
+ .seq_end_work = coda_seq_end_work,
+ .release = coda_bit_release,
+};
+
+irqreturn_t coda_irq_handler(int irq, void *data)
+{
+ struct coda_dev *dev = data;
+ struct coda_ctx *ctx;
+
+ /* read status register to attend the IRQ */
+ coda_read(dev, CODA_REG_BIT_INT_STATUS);
+ coda_write(dev, CODA_REG_BIT_INT_CLEAR_SET,
+ CODA_REG_BIT_INT_CLEAR);
+
+ ctx = v4l2_m2m_get_curr_priv(dev->m2m_dev);
+ if (ctx == NULL) {
+ v4l2_err(&dev->v4l2_dev,
+ "Instance released before the end of transaction\n");
+ mutex_unlock(&dev->coda_mutex);
+ return IRQ_HANDLED;
+ }
+
+ if (ctx->aborting) {
+ v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
+ "task has been aborted\n");
+ }
+
+ if (coda_isbusy(ctx->dev)) {
+ v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
+ "coda is still busy!!!!\n");
+ return IRQ_NONE;
+ }
+
+ complete(&ctx->completion);
+
+ return IRQ_HANDLED;
+}
diff --git a/drivers/media/platform/coda/coda-common.c b/drivers/media/platform/coda/coda-common.c
new file mode 100644
index 000000000000..ced47609f5ef
--- /dev/null
+++ b/drivers/media/platform/coda/coda-common.c
@@ -0,0 +1,2052 @@
+/*
+ * Coda multi-standard codec IP
+ *
+ * Copyright (C) 2012 Vista Silicon S.L.
+ * Javier Martin, <javier.martin@vista-silicon.com>
+ * Xavier Duret
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/clk.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/firmware.h>
+#include <linux/genalloc.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/kfifo.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+#include <linux/of.h>
+#include <linux/platform_data/coda.h>
+#include <linux/reset.h>
+
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-mem2mem.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include "coda.h"
+
+#define CODA_NAME "coda"
+
+#define CODADX6_MAX_INSTANCES 4
+
+#define CODA_PARA_BUF_SIZE (10 * 1024)
+#define CODA_ISRAM_SIZE (2048 * 2)
+
+#define MIN_W 176
+#define MIN_H 144
+
+#define S_ALIGN 1 /* multiple of 2 */
+#define W_ALIGN 1 /* multiple of 2 */
+#define H_ALIGN 1 /* multiple of 2 */
+
+#define fh_to_ctx(__fh) container_of(__fh, struct coda_ctx, fh)
+
+int coda_debug;
+module_param(coda_debug, int, 0644);
+MODULE_PARM_DESC(coda_debug, "Debug level (0-2)");
+
+struct coda_fmt {
+ char *name;
+ u32 fourcc;
+};
+
+void coda_write(struct coda_dev *dev, u32 data, u32 reg)
+{
+ v4l2_dbg(2, coda_debug, &dev->v4l2_dev,
+ "%s: data=0x%x, reg=0x%x\n", __func__, data, reg);
+ writel(data, dev->regs_base + reg);
+}
+
+unsigned int coda_read(struct coda_dev *dev, u32 reg)
+{
+ u32 data;
+
+ data = readl(dev->regs_base + reg);
+ v4l2_dbg(2, coda_debug, &dev->v4l2_dev,
+ "%s: data=0x%x, reg=0x%x\n", __func__, data, reg);
+ return data;
+}
+
+/*
+ * Array of all formats supported by any version of Coda:
+ */
+static const struct coda_fmt coda_formats[] = {
+ {
+ .name = "YUV 4:2:0 Planar, YCbCr",
+ .fourcc = V4L2_PIX_FMT_YUV420,
+ },
+ {
+ .name = "YUV 4:2:0 Planar, YCrCb",
+ .fourcc = V4L2_PIX_FMT_YVU420,
+ },
+ {
+ .name = "H264 Encoded Stream",
+ .fourcc = V4L2_PIX_FMT_H264,
+ },
+ {
+ .name = "MPEG4 Encoded Stream",
+ .fourcc = V4L2_PIX_FMT_MPEG4,
+ },
+};
+
+#define CODA_CODEC(mode, src_fourcc, dst_fourcc, max_w, max_h) \
+ { mode, src_fourcc, dst_fourcc, max_w, max_h }
+
+/*
+ * Arrays of codecs supported by each given version of Coda:
+ * i.MX27 -> codadx6
+ * i.MX5x -> coda7
+ * i.MX6 -> coda960
+ * Use V4L2_PIX_FMT_YUV420 as placeholder for all supported YUV 4:2:0 variants
+ */
+static const struct coda_codec codadx6_codecs[] = {
+ CODA_CODEC(CODADX6_MODE_ENCODE_H264, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_H264, 720, 576),
+ CODA_CODEC(CODADX6_MODE_ENCODE_MP4, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_MPEG4, 720, 576),
+};
+
+static const struct coda_codec coda7_codecs[] = {
+ CODA_CODEC(CODA7_MODE_ENCODE_H264, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_H264, 1280, 720),
+ CODA_CODEC(CODA7_MODE_ENCODE_MP4, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_MPEG4, 1280, 720),
+ CODA_CODEC(CODA7_MODE_DECODE_H264, V4L2_PIX_FMT_H264, V4L2_PIX_FMT_YUV420, 1920, 1088),
+ CODA_CODEC(CODA7_MODE_DECODE_MP4, V4L2_PIX_FMT_MPEG4, V4L2_PIX_FMT_YUV420, 1920, 1088),
+};
+
+static const struct coda_codec coda9_codecs[] = {
+ CODA_CODEC(CODA9_MODE_ENCODE_H264, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_H264, 1920, 1088),
+ CODA_CODEC(CODA9_MODE_ENCODE_MP4, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_MPEG4, 1920, 1088),
+ CODA_CODEC(CODA9_MODE_DECODE_H264, V4L2_PIX_FMT_H264, V4L2_PIX_FMT_YUV420, 1920, 1088),
+ CODA_CODEC(CODA9_MODE_DECODE_MP4, V4L2_PIX_FMT_MPEG4, V4L2_PIX_FMT_YUV420, 1920, 1088),
+};
+
+static bool coda_format_is_yuv(u32 fourcc)
+{
+ switch (fourcc) {
+ case V4L2_PIX_FMT_YUV420:
+ case V4L2_PIX_FMT_YVU420:
+ return true;
+ default:
+ return false;
+ }
+}
+
+/*
+ * Normalize all supported YUV 4:2:0 formats to the value used in the codec
+ * tables.
+ */
+static u32 coda_format_normalize_yuv(u32 fourcc)
+{
+ return coda_format_is_yuv(fourcc) ? V4L2_PIX_FMT_YUV420 : fourcc;
+}
+
+static const struct coda_codec *coda_find_codec(struct coda_dev *dev,
+ int src_fourcc, int dst_fourcc)
+{
+ const struct coda_codec *codecs = dev->devtype->codecs;
+ int num_codecs = dev->devtype->num_codecs;
+ int k;
+
+ src_fourcc = coda_format_normalize_yuv(src_fourcc);
+ dst_fourcc = coda_format_normalize_yuv(dst_fourcc);
+ if (src_fourcc == dst_fourcc)
+ return NULL;
+
+ for (k = 0; k < num_codecs; k++) {
+ if (codecs[k].src_fourcc == src_fourcc &&
+ codecs[k].dst_fourcc == dst_fourcc)
+ break;
+ }
+
+ if (k == num_codecs)
+ return NULL;
+
+ return &codecs[k];
+}
+
+static void coda_get_max_dimensions(struct coda_dev *dev,
+ const struct coda_codec *codec,
+ int *max_w, int *max_h)
+{
+ const struct coda_codec *codecs = dev->devtype->codecs;
+ int num_codecs = dev->devtype->num_codecs;
+ unsigned int w, h;
+ int k;
+
+ if (codec) {
+ w = codec->max_w;
+ h = codec->max_h;
+ } else {
+ for (k = 0, w = 0, h = 0; k < num_codecs; k++) {
+ w = max(w, codecs[k].max_w);
+ h = max(h, codecs[k].max_h);
+ }
+ }
+
+ if (max_w)
+ *max_w = w;
+ if (max_h)
+ *max_h = h;
+}
+
+const char *coda_product_name(int product)
+{
+ static char buf[9];
+
+ switch (product) {
+ case CODA_DX6:
+ return "CodaDx6";
+ case CODA_7541:
+ return "CODA7541";
+ case CODA_960:
+ return "CODA960";
+ default:
+ snprintf(buf, sizeof(buf), "(0x%04x)", product);
+ return buf;
+ }
+}
+
+/*
+ * V4L2 ioctl() operations.
+ */
+static int coda_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct coda_ctx *ctx = fh_to_ctx(priv);
+
+ strlcpy(cap->driver, CODA_NAME, sizeof(cap->driver));
+ strlcpy(cap->card, coda_product_name(ctx->dev->devtype->product),
+ sizeof(cap->card));
+ strlcpy(cap->bus_info, "platform:" CODA_NAME, sizeof(cap->bus_info));
+ cap->device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING;
+ cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
+
+ return 0;
+}
+
+static int coda_enum_fmt(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ struct coda_ctx *ctx = fh_to_ctx(priv);
+ const struct coda_codec *codecs = ctx->dev->devtype->codecs;
+ const struct coda_fmt *formats = coda_formats;
+ const struct coda_fmt *fmt;
+ int num_codecs = ctx->dev->devtype->num_codecs;
+ int num_formats = ARRAY_SIZE(coda_formats);
+ int i, k, num = 0;
+ bool yuv;
+
+ if (ctx->inst_type == CODA_INST_ENCODER)
+ yuv = (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT);
+ else
+ yuv = (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE);
+
+ for (i = 0; i < num_formats; i++) {
+ /* Skip either raw or compressed formats */
+ if (yuv != coda_format_is_yuv(formats[i].fourcc))
+ continue;
+ /* All uncompressed formats are always supported */
+ if (yuv) {
+ if (num == f->index)
+ break;
+ ++num;
+ continue;
+ }
+ /* Compressed formats may be supported, check the codec list */
+ for (k = 0; k < num_codecs; k++) {
+ if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+ formats[i].fourcc == codecs[k].dst_fourcc)
+ break;
+ if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT &&
+ formats[i].fourcc == codecs[k].src_fourcc)
+ break;
+ }
+ if (k < num_codecs) {
+ if (num == f->index)
+ break;
+ ++num;
+ }
+ }
+
+ if (i < num_formats) {
+ fmt = &formats[i];
+ strlcpy(f->description, fmt->name, sizeof(f->description));
+ f->pixelformat = fmt->fourcc;
+ if (!yuv)
+ f->flags |= V4L2_FMT_FLAG_COMPRESSED;
+ return 0;
+ }
+
+ /* Format not found */
+ return -EINVAL;
+}
+
+static int coda_g_fmt(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct coda_q_data *q_data;
+ struct coda_ctx *ctx = fh_to_ctx(priv);
+
+ q_data = get_q_data(ctx, f->type);
+ if (!q_data)
+ return -EINVAL;
+
+ f->fmt.pix.field = V4L2_FIELD_NONE;
+ f->fmt.pix.pixelformat = q_data->fourcc;
+ f->fmt.pix.width = q_data->width;
+ f->fmt.pix.height = q_data->height;
+ f->fmt.pix.bytesperline = q_data->bytesperline;
+
+ f->fmt.pix.sizeimage = q_data->sizeimage;
+ f->fmt.pix.colorspace = ctx->colorspace;
+
+ return 0;
+}
+
+static int coda_try_fmt(struct coda_ctx *ctx, const struct coda_codec *codec,
+ struct v4l2_format *f)
+{
+ struct coda_dev *dev = ctx->dev;
+ struct coda_q_data *q_data;
+ unsigned int max_w, max_h;
+ enum v4l2_field field;
+
+ field = f->fmt.pix.field;
+ if (field == V4L2_FIELD_ANY)
+ field = V4L2_FIELD_NONE;
+ else if (V4L2_FIELD_NONE != field)
+ return -EINVAL;
+
+ /* V4L2 specification suggests the driver corrects the format struct
+ * if any of the dimensions is unsupported */
+ f->fmt.pix.field = field;
+
+ coda_get_max_dimensions(dev, codec, &max_w, &max_h);
+ v4l_bound_align_image(&f->fmt.pix.width, MIN_W, max_w, W_ALIGN,
+ &f->fmt.pix.height, MIN_H, max_h, H_ALIGN,
+ S_ALIGN);
+
+ switch (f->fmt.pix.pixelformat) {
+ case V4L2_PIX_FMT_YUV420:
+ case V4L2_PIX_FMT_YVU420:
+ case V4L2_PIX_FMT_H264:
+ case V4L2_PIX_FMT_MPEG4:
+ case V4L2_PIX_FMT_JPEG:
+ break;
+ default:
+ q_data = get_q_data(ctx, f->type);
+ if (!q_data)
+ return -EINVAL;
+ f->fmt.pix.pixelformat = q_data->fourcc;
+ }
+
+ switch (f->fmt.pix.pixelformat) {
+ case V4L2_PIX_FMT_YUV420:
+ case V4L2_PIX_FMT_YVU420:
+ /* Frame stride must be multiple of 8, but 16 for h.264 */
+ f->fmt.pix.bytesperline = round_up(f->fmt.pix.width, 16);
+ f->fmt.pix.sizeimage = f->fmt.pix.bytesperline *
+ f->fmt.pix.height * 3 / 2;
+ break;
+ case V4L2_PIX_FMT_H264:
+ case V4L2_PIX_FMT_MPEG4:
+ case V4L2_PIX_FMT_JPEG:
+ f->fmt.pix.bytesperline = 0;
+ f->fmt.pix.sizeimage = CODA_MAX_FRAME_SIZE;
+ break;
+ default:
+ BUG();
+ }
+
+ return 0;
+}
+
+static int coda_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct coda_ctx *ctx = fh_to_ctx(priv);
+ const struct coda_codec *codec = NULL;
+ struct vb2_queue *src_vq;
+ int ret;
+
+ /*
+ * If the source format is already fixed, try to find a codec that
+ * converts to the given destination format
+ */
+ src_vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
+ if (vb2_is_streaming(src_vq)) {
+ struct coda_q_data *q_data_src;
+
+ q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
+ codec = coda_find_codec(ctx->dev, q_data_src->fourcc,
+ f->fmt.pix.pixelformat);
+ if (!codec)
+ return -EINVAL;
+
+ f->fmt.pix.width = q_data_src->width;
+ f->fmt.pix.height = q_data_src->height;
+ } else {
+ /* Otherwise determine codec by encoded format, if possible */
+ codec = coda_find_codec(ctx->dev, V4L2_PIX_FMT_YUV420,
+ f->fmt.pix.pixelformat);
+ }
+
+ f->fmt.pix.colorspace = ctx->colorspace;
+
+ ret = coda_try_fmt(ctx, codec, f);
+ if (ret < 0)
+ return ret;
+
+ /* The h.264 decoder only returns complete 16x16 macroblocks */
+ if (codec && codec->src_fourcc == V4L2_PIX_FMT_H264) {
+ f->fmt.pix.width = f->fmt.pix.width;
+ f->fmt.pix.height = round_up(f->fmt.pix.height, 16);
+ f->fmt.pix.bytesperline = round_up(f->fmt.pix.width, 16);
+ f->fmt.pix.sizeimage = f->fmt.pix.bytesperline *
+ f->fmt.pix.height * 3 / 2;
+ }
+
+ return 0;
+}
+
+static int coda_try_fmt_vid_out(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct coda_ctx *ctx = fh_to_ctx(priv);
+ const struct coda_codec *codec = NULL;
+
+ /* Determine codec by encoded format, returns NULL if raw or invalid */
+ if (ctx->inst_type == CODA_INST_DECODER) {
+ codec = coda_find_codec(ctx->dev, f->fmt.pix.pixelformat,
+ V4L2_PIX_FMT_YUV420);
+ if (!codec)
+ codec = coda_find_codec(ctx->dev, V4L2_PIX_FMT_H264,
+ V4L2_PIX_FMT_YUV420);
+ if (!codec)
+ return -EINVAL;
+ }
+
+ if (!f->fmt.pix.colorspace)
+ f->fmt.pix.colorspace = V4L2_COLORSPACE_REC709;
+
+ return coda_try_fmt(ctx, codec, f);
+}
+
+static int coda_s_fmt(struct coda_ctx *ctx, struct v4l2_format *f)
+{
+ struct coda_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->fourcc = f->fmt.pix.pixelformat;
+ q_data->width = f->fmt.pix.width;
+ q_data->height = f->fmt.pix.height;
+ q_data->bytesperline = f->fmt.pix.bytesperline;
+ q_data->sizeimage = f->fmt.pix.sizeimage;
+ q_data->rect.left = 0;
+ q_data->rect.top = 0;
+ q_data->rect.width = f->fmt.pix.width;
+ q_data->rect.height = f->fmt.pix.height;
+
+ v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
+ "Setting format for type %d, wxh: %dx%d, fmt: %d\n",
+ f->type, q_data->width, q_data->height, q_data->fourcc);
+
+ return 0;
+}
+
+static int coda_s_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct coda_ctx *ctx = fh_to_ctx(priv);
+ int ret;
+
+ ret = coda_try_fmt_vid_cap(file, priv, f);
+ if (ret)
+ return ret;
+
+ return coda_s_fmt(ctx, f);
+}
+
+static int coda_s_fmt_vid_out(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct coda_ctx *ctx = fh_to_ctx(priv);
+ struct v4l2_format f_cap;
+ int ret;
+
+ ret = coda_try_fmt_vid_out(file, priv, f);
+ if (ret)
+ return ret;
+
+ ret = coda_s_fmt(ctx, f);
+ if (ret)
+ return ret;
+
+ ctx->colorspace = f->fmt.pix.colorspace;
+
+ f_cap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ coda_g_fmt(file, priv, &f_cap);
+ f_cap.fmt.pix.width = f->fmt.pix.width;
+ f_cap.fmt.pix.height = f->fmt.pix.height;
+
+ ret = coda_try_fmt_vid_cap(file, priv, &f_cap);
+ if (ret)
+ return ret;
+
+ return coda_s_fmt(ctx, &f_cap);
+}
+
+static int coda_qbuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct coda_ctx *ctx = fh_to_ctx(priv);
+
+ return v4l2_m2m_qbuf(file, ctx->fh.m2m_ctx, buf);
+}
+
+static bool coda_buf_is_end_of_stream(struct coda_ctx *ctx,
+ struct v4l2_buffer *buf)
+{
+ struct vb2_queue *src_vq;
+
+ src_vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
+
+ return ((ctx->bit_stream_param & CODA_BIT_STREAM_END_FLAG) &&
+ (buf->sequence == (ctx->qsequence - 1)));
+}
+
+static int coda_dqbuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct coda_ctx *ctx = fh_to_ctx(priv);
+ int ret;
+
+ ret = v4l2_m2m_dqbuf(file, ctx->fh.m2m_ctx, buf);
+
+ /* If this is the last capture buffer, emit an end-of-stream event */
+ if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+ coda_buf_is_end_of_stream(ctx, buf)) {
+ const struct v4l2_event eos_event = {
+ .type = V4L2_EVENT_EOS
+ };
+
+ v4l2_event_queue_fh(&ctx->fh, &eos_event);
+ }
+
+ return ret;
+}
+
+static int coda_g_selection(struct file *file, void *fh,
+ struct v4l2_selection *s)
+{
+ struct coda_ctx *ctx = fh_to_ctx(fh);
+ struct coda_q_data *q_data;
+ struct v4l2_rect r, *rsel;
+
+ q_data = get_q_data(ctx, s->type);
+ if (!q_data)
+ return -EINVAL;
+
+ r.left = 0;
+ r.top = 0;
+ r.width = q_data->width;
+ r.height = q_data->height;
+ rsel = &q_data->rect;
+
+ switch (s->target) {
+ case V4L2_SEL_TGT_CROP_DEFAULT:
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ rsel = &r;
+ /* fallthrough */
+ case V4L2_SEL_TGT_CROP:
+ if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
+ return -EINVAL;
+ break;
+ case V4L2_SEL_TGT_COMPOSE_BOUNDS:
+ case V4L2_SEL_TGT_COMPOSE_PADDED:
+ rsel = &r;
+ /* fallthrough */
+ case V4L2_SEL_TGT_COMPOSE:
+ case V4L2_SEL_TGT_COMPOSE_DEFAULT:
+ if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ s->r = *rsel;
+
+ return 0;
+}
+
+static int coda_try_decoder_cmd(struct file *file, void *fh,
+ struct v4l2_decoder_cmd *dc)
+{
+ if (dc->cmd != V4L2_DEC_CMD_STOP)
+ return -EINVAL;
+
+ if (dc->flags & V4L2_DEC_CMD_STOP_TO_BLACK)
+ return -EINVAL;
+
+ if (!(dc->flags & V4L2_DEC_CMD_STOP_IMMEDIATELY) && (dc->stop.pts != 0))
+ return -EINVAL;
+
+ return 0;
+}
+
+static int coda_decoder_cmd(struct file *file, void *fh,
+ struct v4l2_decoder_cmd *dc)
+{
+ struct coda_ctx *ctx = fh_to_ctx(fh);
+ int ret;
+
+ ret = coda_try_decoder_cmd(file, fh, dc);
+ if (ret < 0)
+ return ret;
+
+ /* Ignore decoder stop command silently in encoder context */
+ if (ctx->inst_type != CODA_INST_DECODER)
+ return 0;
+
+ /* Set the stream-end flag on this context */
+ coda_bit_stream_end_flag(ctx);
+ ctx->hold = false;
+ v4l2_m2m_try_schedule(ctx->fh.m2m_ctx);
+
+ return 0;
+}
+
+static int coda_subscribe_event(struct v4l2_fh *fh,
+ const struct v4l2_event_subscription *sub)
+{
+ switch (sub->type) {
+ case V4L2_EVENT_EOS:
+ return v4l2_event_subscribe(fh, sub, 0, NULL);
+ default:
+ return v4l2_ctrl_subscribe_event(fh, sub);
+ }
+}
+
+static const struct v4l2_ioctl_ops coda_ioctl_ops = {
+ .vidioc_querycap = coda_querycap,
+
+ .vidioc_enum_fmt_vid_cap = coda_enum_fmt,
+ .vidioc_g_fmt_vid_cap = coda_g_fmt,
+ .vidioc_try_fmt_vid_cap = coda_try_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = coda_s_fmt_vid_cap,
+
+ .vidioc_enum_fmt_vid_out = coda_enum_fmt,
+ .vidioc_g_fmt_vid_out = coda_g_fmt,
+ .vidioc_try_fmt_vid_out = coda_try_fmt_vid_out,
+ .vidioc_s_fmt_vid_out = coda_s_fmt_vid_out,
+
+ .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs,
+ .vidioc_querybuf = v4l2_m2m_ioctl_querybuf,
+
+ .vidioc_qbuf = coda_qbuf,
+ .vidioc_expbuf = v4l2_m2m_ioctl_expbuf,
+ .vidioc_dqbuf = coda_dqbuf,
+ .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs,
+
+ .vidioc_streamon = v4l2_m2m_ioctl_streamon,
+ .vidioc_streamoff = v4l2_m2m_ioctl_streamoff,
+
+ .vidioc_g_selection = coda_g_selection,
+
+ .vidioc_try_decoder_cmd = coda_try_decoder_cmd,
+ .vidioc_decoder_cmd = coda_decoder_cmd,
+
+ .vidioc_subscribe_event = coda_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+void coda_set_gdi_regs(struct coda_ctx *ctx)
+{
+ struct gdi_tiled_map *tiled_map = &ctx->tiled_map;
+ struct coda_dev *dev = ctx->dev;
+ int i;
+
+ for (i = 0; i < 16; i++)
+ coda_write(dev, tiled_map->xy2ca_map[i],
+ CODA9_GDI_XY2_CAS_0 + 4 * i);
+ for (i = 0; i < 4; i++)
+ coda_write(dev, tiled_map->xy2ba_map[i],
+ CODA9_GDI_XY2_BA_0 + 4 * i);
+ for (i = 0; i < 16; i++)
+ coda_write(dev, tiled_map->xy2ra_map[i],
+ CODA9_GDI_XY2_RAS_0 + 4 * i);
+ coda_write(dev, tiled_map->xy2rbc_config, CODA9_GDI_XY2_RBC_CONFIG);
+ for (i = 0; i < 32; i++)
+ coda_write(dev, tiled_map->rbc2axi_map[i],
+ CODA9_GDI_RBC2_AXI_0 + 4 * i);
+}
+
+/*
+ * Mem-to-mem operations.
+ */
+
+static void coda_device_run(void *m2m_priv)
+{
+ struct coda_ctx *ctx = m2m_priv;
+ struct coda_dev *dev = ctx->dev;
+
+ queue_work(dev->workqueue, &ctx->pic_run_work);
+}
+
+static void coda_pic_run_work(struct work_struct *work)
+{
+ struct coda_ctx *ctx = container_of(work, struct coda_ctx, pic_run_work);
+ struct coda_dev *dev = ctx->dev;
+ int ret;
+
+ mutex_lock(&ctx->buffer_mutex);
+ mutex_lock(&dev->coda_mutex);
+
+ ret = ctx->ops->prepare_run(ctx);
+ if (ret < 0 && ctx->inst_type == CODA_INST_DECODER) {
+ mutex_unlock(&dev->coda_mutex);
+ mutex_unlock(&ctx->buffer_mutex);
+ /* job_finish scheduled by prepare_decode */
+ return;
+ }
+
+ if (!wait_for_completion_timeout(&ctx->completion,
+ msecs_to_jiffies(1000))) {
+ dev_err(&dev->plat_dev->dev, "CODA PIC_RUN timeout\n");
+
+ ctx->hold = true;
+
+ coda_hw_reset(ctx);
+ } else if (!ctx->aborting) {
+ ctx->ops->finish_run(ctx);
+ }
+
+ if (ctx->aborting || (!ctx->streamon_cap && !ctx->streamon_out))
+ queue_work(dev->workqueue, &ctx->seq_end_work);
+
+ mutex_unlock(&dev->coda_mutex);
+ mutex_unlock(&ctx->buffer_mutex);
+
+ v4l2_m2m_job_finish(ctx->dev->m2m_dev, ctx->fh.m2m_ctx);
+}
+
+static int coda_job_ready(void *m2m_priv)
+{
+ struct coda_ctx *ctx = m2m_priv;
+
+ /*
+ * For both 'P' and 'key' frame cases 1 picture
+ * and 1 frame are needed. In the decoder case,
+ * the compressed frame can be in the bitstream.
+ */
+ if (!v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx) &&
+ ctx->inst_type != CODA_INST_DECODER) {
+ v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
+ "not ready: not enough video buffers.\n");
+ return 0;
+ }
+
+ if (!v4l2_m2m_num_dst_bufs_ready(ctx->fh.m2m_ctx)) {
+ v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
+ "not ready: not enough video capture buffers.\n");
+ return 0;
+ }
+
+ if (ctx->hold ||
+ ((ctx->inst_type == CODA_INST_DECODER) &&
+ (coda_get_bitstream_payload(ctx) < 512) &&
+ !(ctx->bit_stream_param & CODA_BIT_STREAM_END_FLAG))) {
+ v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
+ "%d: not ready: not enough bitstream data.\n",
+ ctx->idx);
+ return 0;
+ }
+
+ if (ctx->aborting) {
+ v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
+ "not ready: aborting\n");
+ return 0;
+ }
+
+ v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
+ "job ready\n");
+ return 1;
+}
+
+static void coda_job_abort(void *priv)
+{
+ struct coda_ctx *ctx = priv;
+
+ ctx->aborting = 1;
+
+ v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
+ "Aborting task\n");
+}
+
+static void coda_lock(void *m2m_priv)
+{
+ struct coda_ctx *ctx = m2m_priv;
+ struct coda_dev *pcdev = ctx->dev;
+
+ mutex_lock(&pcdev->dev_mutex);
+}
+
+static void coda_unlock(void *m2m_priv)
+{
+ struct coda_ctx *ctx = m2m_priv;
+ struct coda_dev *pcdev = ctx->dev;
+
+ mutex_unlock(&pcdev->dev_mutex);
+}
+
+static const struct v4l2_m2m_ops coda_m2m_ops = {
+ .device_run = coda_device_run,
+ .job_ready = coda_job_ready,
+ .job_abort = coda_job_abort,
+ .lock = coda_lock,
+ .unlock = coda_unlock,
+};
+
+static void coda_set_tiled_map_type(struct coda_ctx *ctx, int tiled_map_type)
+{
+ struct gdi_tiled_map *tiled_map = &ctx->tiled_map;
+ int luma_map, chro_map, i;
+
+ memset(tiled_map, 0, sizeof(*tiled_map));
+
+ luma_map = 64;
+ chro_map = 64;
+ tiled_map->map_type = tiled_map_type;
+ for (i = 0; i < 16; i++)
+ tiled_map->xy2ca_map[i] = luma_map << 8 | chro_map;
+ for (i = 0; i < 4; i++)
+ tiled_map->xy2ba_map[i] = luma_map << 8 | chro_map;
+ for (i = 0; i < 16; i++)
+ tiled_map->xy2ra_map[i] = luma_map << 8 | chro_map;
+
+ if (tiled_map_type == GDI_LINEAR_FRAME_MAP) {
+ tiled_map->xy2rbc_config = 0;
+ } else {
+ dev_err(&ctx->dev->plat_dev->dev, "invalid map type: %d\n",
+ tiled_map_type);
+ return;
+ }
+}
+
+static void set_default_params(struct coda_ctx *ctx)
+{
+ u32 src_fourcc, dst_fourcc;
+ int max_w;
+ int max_h;
+
+ if (ctx->inst_type == CODA_INST_ENCODER) {
+ src_fourcc = V4L2_PIX_FMT_YUV420;
+ dst_fourcc = V4L2_PIX_FMT_H264;
+ } else {
+ src_fourcc = V4L2_PIX_FMT_H264;
+ dst_fourcc = V4L2_PIX_FMT_YUV420;
+ }
+ ctx->codec = coda_find_codec(ctx->dev, src_fourcc, dst_fourcc);
+ max_w = ctx->codec->max_w;
+ max_h = ctx->codec->max_h;
+
+ ctx->params.codec_mode = ctx->codec->mode;
+ ctx->colorspace = V4L2_COLORSPACE_REC709;
+ ctx->params.framerate = 30;
+ ctx->aborting = 0;
+
+ /* Default formats for output and input queues */
+ ctx->q_data[V4L2_M2M_SRC].fourcc = ctx->codec->src_fourcc;
+ ctx->q_data[V4L2_M2M_DST].fourcc = ctx->codec->dst_fourcc;
+ ctx->q_data[V4L2_M2M_SRC].width = max_w;
+ ctx->q_data[V4L2_M2M_SRC].height = max_h;
+ ctx->q_data[V4L2_M2M_DST].width = max_w;
+ ctx->q_data[V4L2_M2M_DST].height = max_h;
+ if (ctx->codec->src_fourcc == V4L2_PIX_FMT_YUV420) {
+ ctx->q_data[V4L2_M2M_SRC].bytesperline = max_w;
+ ctx->q_data[V4L2_M2M_SRC].sizeimage = (max_w * max_h * 3) / 2;
+ ctx->q_data[V4L2_M2M_DST].bytesperline = 0;
+ ctx->q_data[V4L2_M2M_DST].sizeimage = CODA_MAX_FRAME_SIZE;
+ } else {
+ ctx->q_data[V4L2_M2M_SRC].bytesperline = 0;
+ ctx->q_data[V4L2_M2M_SRC].sizeimage = CODA_MAX_FRAME_SIZE;
+ ctx->q_data[V4L2_M2M_DST].bytesperline = max_w;
+ ctx->q_data[V4L2_M2M_DST].sizeimage = (max_w * max_h * 3) / 2;
+ }
+ ctx->q_data[V4L2_M2M_SRC].rect.width = max_w;
+ ctx->q_data[V4L2_M2M_SRC].rect.height = max_h;
+ ctx->q_data[V4L2_M2M_DST].rect.width = max_w;
+ ctx->q_data[V4L2_M2M_DST].rect.height = max_h;
+
+ if (ctx->dev->devtype->product == CODA_960)
+ coda_set_tiled_map_type(ctx, GDI_LINEAR_FRAME_MAP);
+}
+
+/*
+ * Queue operations
+ */
+static int coda_queue_setup(struct vb2_queue *vq,
+ const struct v4l2_format *fmt,
+ unsigned int *nbuffers, unsigned int *nplanes,
+ unsigned int sizes[], void *alloc_ctxs[])
+{
+ struct coda_ctx *ctx = vb2_get_drv_priv(vq);
+ struct coda_q_data *q_data;
+ unsigned int size;
+
+ q_data = get_q_data(ctx, vq->type);
+ size = q_data->sizeimage;
+
+ *nplanes = 1;
+ sizes[0] = size;
+
+ alloc_ctxs[0] = ctx->dev->alloc_ctx;
+
+ v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
+ "get %d buffer(s) of size %d each.\n", *nbuffers, size);
+
+ return 0;
+}
+
+static int coda_buf_prepare(struct vb2_buffer *vb)
+{
+ struct coda_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+ struct coda_q_data *q_data;
+
+ q_data = get_q_data(ctx, vb->vb2_queue->type);
+
+ if (vb2_plane_size(vb, 0) < q_data->sizeimage) {
+ v4l2_warn(&ctx->dev->v4l2_dev,
+ "%s data will not fit into plane (%lu < %lu)\n",
+ __func__, vb2_plane_size(vb, 0),
+ (long)q_data->sizeimage);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void coda_buf_queue(struct vb2_buffer *vb)
+{
+ struct coda_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+ struct coda_q_data *q_data;
+
+ q_data = get_q_data(ctx, vb->vb2_queue->type);
+
+ /*
+ * In the decoder case, immediately try to copy the buffer into the
+ * bitstream ringbuffer and mark it as ready to be dequeued.
+ */
+ if (q_data->fourcc == V4L2_PIX_FMT_H264 &&
+ vb->vb2_queue->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ /*
+ * For backwards compatibility, queuing an empty buffer marks
+ * the stream end
+ */
+ if (vb2_get_plane_payload(vb, 0) == 0)
+ coda_bit_stream_end_flag(ctx);
+ mutex_lock(&ctx->bitstream_mutex);
+ v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vb);
+ if (vb2_is_streaming(vb->vb2_queue))
+ coda_fill_bitstream(ctx);
+ mutex_unlock(&ctx->bitstream_mutex);
+ } else {
+ v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vb);
+ }
+}
+
+int coda_alloc_aux_buf(struct coda_dev *dev, struct coda_aux_buf *buf,
+ size_t size, const char *name, struct dentry *parent)
+{
+ buf->vaddr = dma_alloc_coherent(&dev->plat_dev->dev, size, &buf->paddr,
+ GFP_KERNEL);
+ if (!buf->vaddr) {
+ v4l2_err(&dev->v4l2_dev,
+ "Failed to allocate %s buffer of size %u\n",
+ name, size);
+ return -ENOMEM;
+ }
+
+ buf->size = size;
+
+ if (name && parent) {
+ buf->blob.data = buf->vaddr;
+ buf->blob.size = size;
+ buf->dentry = debugfs_create_blob(name, 0644, parent,
+ &buf->blob);
+ if (!buf->dentry)
+ dev_warn(&dev->plat_dev->dev,
+ "failed to create debugfs entry %s\n", name);
+ }
+
+ return 0;
+}
+
+void coda_free_aux_buf(struct coda_dev *dev,
+ struct coda_aux_buf *buf)
+{
+ if (buf->vaddr) {
+ dma_free_coherent(&dev->plat_dev->dev, buf->size,
+ buf->vaddr, buf->paddr);
+ buf->vaddr = NULL;
+ buf->size = 0;
+ }
+ debugfs_remove(buf->dentry);
+}
+
+static int coda_start_streaming(struct vb2_queue *q, unsigned int count)
+{
+ struct coda_ctx *ctx = vb2_get_drv_priv(q);
+ struct v4l2_device *v4l2_dev = &ctx->dev->v4l2_dev;
+ struct coda_q_data *q_data_src, *q_data_dst;
+ struct vb2_buffer *buf;
+ u32 dst_fourcc;
+ int ret = 0;
+
+ q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
+ if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ if (q_data_src->fourcc == V4L2_PIX_FMT_H264) {
+ /* copy the buffers that where queued before streamon */
+ mutex_lock(&ctx->bitstream_mutex);
+ coda_fill_bitstream(ctx);
+ mutex_unlock(&ctx->bitstream_mutex);
+
+ if (coda_get_bitstream_payload(ctx) < 512) {
+ ret = -EINVAL;
+ goto err;
+ }
+ } else {
+ if (count < 1) {
+ ret = -EINVAL;
+ goto err;
+ }
+ }
+
+ ctx->streamon_out = 1;
+ } else {
+ if (count < 1) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ ctx->streamon_cap = 1;
+ }
+
+ /* Don't start the coda unless both queues are on */
+ if (!(ctx->streamon_out & ctx->streamon_cap))
+ return 0;
+
+ /* Allow decoder device_run with no new buffers queued */
+ if (ctx->inst_type == CODA_INST_DECODER)
+ v4l2_m2m_set_src_buffered(ctx->fh.m2m_ctx, true);
+
+ ctx->gopcounter = ctx->params.gop_size - 1;
+ q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+ dst_fourcc = q_data_dst->fourcc;
+
+ ctx->codec = coda_find_codec(ctx->dev, q_data_src->fourcc,
+ q_data_dst->fourcc);
+ if (!ctx->codec) {
+ v4l2_err(v4l2_dev, "couldn't tell instance type.\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ ret = ctx->ops->start_streaming(ctx);
+ if (ctx->inst_type == CODA_INST_DECODER) {
+ if (ret == -EAGAIN)
+ return 0;
+ else if (ret < 0)
+ goto err;
+ }
+
+ ctx->initialized = 1;
+ return ret;
+
+err:
+ if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ while ((buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx)))
+ v4l2_m2m_buf_done(buf, VB2_BUF_STATE_DEQUEUED);
+ } else {
+ while ((buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx)))
+ v4l2_m2m_buf_done(buf, VB2_BUF_STATE_DEQUEUED);
+ }
+ return ret;
+}
+
+static void coda_stop_streaming(struct vb2_queue *q)
+{
+ struct coda_ctx *ctx = vb2_get_drv_priv(q);
+ struct coda_dev *dev = ctx->dev;
+ struct vb2_buffer *buf;
+
+ if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ v4l2_dbg(1, coda_debug, &dev->v4l2_dev,
+ "%s: output\n", __func__);
+ ctx->streamon_out = 0;
+
+ coda_bit_stream_end_flag(ctx);
+
+ ctx->isequence = 0;
+
+ while ((buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx)))
+ v4l2_m2m_buf_done(buf, VB2_BUF_STATE_ERROR);
+ } else {
+ v4l2_dbg(1, coda_debug, &dev->v4l2_dev,
+ "%s: capture\n", __func__);
+ ctx->streamon_cap = 0;
+
+ ctx->osequence = 0;
+ ctx->sequence_offset = 0;
+
+ while ((buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx)))
+ v4l2_m2m_buf_done(buf, VB2_BUF_STATE_ERROR);
+ }
+
+ if (!ctx->streamon_out && !ctx->streamon_cap) {
+ struct coda_timestamp *ts;
+
+ mutex_lock(&ctx->bitstream_mutex);
+ while (!list_empty(&ctx->timestamp_list)) {
+ ts = list_first_entry(&ctx->timestamp_list,
+ struct coda_timestamp, list);
+ list_del(&ts->list);
+ kfree(ts);
+ }
+ mutex_unlock(&ctx->bitstream_mutex);
+ kfifo_init(&ctx->bitstream_fifo,
+ ctx->bitstream.vaddr, ctx->bitstream.size);
+ ctx->runcounter = 0;
+ }
+}
+
+static const struct vb2_ops coda_qops = {
+ .queue_setup = coda_queue_setup,
+ .buf_prepare = coda_buf_prepare,
+ .buf_queue = coda_buf_queue,
+ .start_streaming = coda_start_streaming,
+ .stop_streaming = coda_stop_streaming,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
+};
+
+static int coda_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct coda_ctx *ctx =
+ container_of(ctrl->handler, struct coda_ctx, ctrls);
+
+ v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
+ "s_ctrl: id = %d, val = %d\n", ctrl->id, ctrl->val);
+
+ switch (ctrl->id) {
+ case V4L2_CID_HFLIP:
+ if (ctrl->val)
+ ctx->params.rot_mode |= CODA_MIR_HOR;
+ else
+ ctx->params.rot_mode &= ~CODA_MIR_HOR;
+ break;
+ case V4L2_CID_VFLIP:
+ if (ctrl->val)
+ ctx->params.rot_mode |= CODA_MIR_VER;
+ else
+ ctx->params.rot_mode &= ~CODA_MIR_VER;
+ break;
+ case V4L2_CID_MPEG_VIDEO_BITRATE:
+ ctx->params.bitrate = ctrl->val / 1000;
+ break;
+ case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
+ ctx->params.gop_size = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP:
+ ctx->params.h264_intra_qp = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP:
+ ctx->params.h264_inter_qp = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_MIN_QP:
+ ctx->params.h264_min_qp = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_MAX_QP:
+ ctx->params.h264_max_qp = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA:
+ ctx->params.h264_deblk_alpha = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA:
+ ctx->params.h264_deblk_beta = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE:
+ ctx->params.h264_deblk_enabled = (ctrl->val ==
+ V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_ENABLED);
+ break;
+ case V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP:
+ ctx->params.mpeg4_intra_qp = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP:
+ ctx->params.mpeg4_inter_qp = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE:
+ ctx->params.slice_mode = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB:
+ ctx->params.slice_max_mb = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES:
+ ctx->params.slice_max_bits = ctrl->val * 8;
+ break;
+ case V4L2_CID_MPEG_VIDEO_HEADER_MODE:
+ break;
+ case V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB:
+ ctx->params.intra_refresh = ctrl->val;
+ break;
+ default:
+ v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
+ "Invalid control, id=%d, val=%d\n",
+ ctrl->id, ctrl->val);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct v4l2_ctrl_ops coda_ctrl_ops = {
+ .s_ctrl = coda_s_ctrl,
+};
+
+static int coda_ctrls_setup(struct coda_ctx *ctx)
+{
+ v4l2_ctrl_handler_init(&ctx->ctrls, 9);
+
+ v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_BITRATE, 0, 32767000, 1, 0);
+ v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_GOP_SIZE, 1, 60, 1, 16);
+ v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP, 0, 51, 1, 25);
+ v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP, 0, 51, 1, 25);
+ if (ctx->dev->devtype->product != CODA_960) {
+ v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_H264_MIN_QP, 0, 51, 1, 12);
+ }
+ v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_H264_MAX_QP, 0, 51, 1, 51);
+ v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA, 0, 15, 1, 0);
+ v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA, 0, 15, 1, 0);
+ v4l2_ctrl_new_std_menu(&ctx->ctrls, &coda_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE,
+ V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED, 0x0,
+ V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_ENABLED);
+ v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP, 1, 31, 1, 2);
+ v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP, 1, 31, 1, 2);
+ v4l2_ctrl_new_std_menu(&ctx->ctrls, &coda_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE,
+ V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES, 0x0,
+ V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE);
+ v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB, 1, 0x3fffffff, 1, 1);
+ v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES, 1, 0x3fffffff, 1,
+ 500);
+ v4l2_ctrl_new_std_menu(&ctx->ctrls, &coda_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_HEADER_MODE,
+ V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME,
+ (1 << V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE),
+ V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME);
+ v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB, 0,
+ 1920 * 1088 / 256, 1, 0);
+
+ if (ctx->ctrls.error) {
+ v4l2_err(&ctx->dev->v4l2_dev,
+ "control initialization error (%d)",
+ ctx->ctrls.error);
+ return -EINVAL;
+ }
+
+ return v4l2_ctrl_handler_setup(&ctx->ctrls);
+}
+
+static int coda_queue_init(struct coda_ctx *ctx, struct vb2_queue *vq)
+{
+ vq->drv_priv = ctx;
+ vq->ops = &coda_qops;
+ vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
+ vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ vq->lock = &ctx->dev->dev_mutex;
+
+ return vb2_queue_init(vq);
+}
+
+int coda_encoder_queue_init(void *priv, struct vb2_queue *src_vq,
+ struct vb2_queue *dst_vq)
+{
+ int ret;
+
+ src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ src_vq->io_modes = VB2_DMABUF | VB2_MMAP;
+ src_vq->mem_ops = &vb2_dma_contig_memops;
+
+ ret = coda_queue_init(priv, src_vq);
+ if (ret)
+ return ret;
+
+ dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ dst_vq->io_modes = VB2_DMABUF | VB2_MMAP;
+ dst_vq->mem_ops = &vb2_dma_contig_memops;
+
+ return coda_queue_init(priv, dst_vq);
+}
+
+int coda_decoder_queue_init(void *priv, struct vb2_queue *src_vq,
+ struct vb2_queue *dst_vq)
+{
+ int ret;
+
+ src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ src_vq->io_modes = VB2_DMABUF | VB2_MMAP;
+ src_vq->mem_ops = &vb2_dma_contig_memops;
+
+ ret = coda_queue_init(priv, src_vq);
+ if (ret)
+ return ret;
+
+ dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ dst_vq->io_modes = VB2_DMABUF | VB2_MMAP;
+ dst_vq->mem_ops = &vb2_dma_contig_memops;
+
+ return coda_queue_init(priv, dst_vq);
+}
+
+static int coda_next_free_instance(struct coda_dev *dev)
+{
+ int idx = ffz(dev->instance_mask);
+
+ if ((idx < 0) ||
+ (dev->devtype->product == CODA_DX6 && idx > CODADX6_MAX_INSTANCES))
+ return -EBUSY;
+
+ return idx;
+}
+
+static int coda_open(struct file *file, enum coda_inst_type inst_type,
+ const struct coda_context_ops *ctx_ops)
+{
+ struct coda_dev *dev = video_drvdata(file);
+ struct coda_ctx *ctx = NULL;
+ char *name;
+ int ret;
+ int idx;
+
+ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ idx = coda_next_free_instance(dev);
+ if (idx < 0) {
+ ret = idx;
+ goto err_coda_max;
+ }
+ set_bit(idx, &dev->instance_mask);
+
+ name = kasprintf(GFP_KERNEL, "context%d", idx);
+ ctx->debugfs_entry = debugfs_create_dir(name, dev->debugfs_root);
+ kfree(name);
+
+ ctx->inst_type = inst_type;
+ ctx->ops = ctx_ops;
+ init_completion(&ctx->completion);
+ INIT_WORK(&ctx->pic_run_work, coda_pic_run_work);
+ INIT_WORK(&ctx->seq_end_work, ctx->ops->seq_end_work);
+ v4l2_fh_init(&ctx->fh, video_devdata(file));
+ file->private_data = &ctx->fh;
+ v4l2_fh_add(&ctx->fh);
+ ctx->dev = dev;
+ ctx->idx = idx;
+ switch (dev->devtype->product) {
+ case CODA_7541:
+ case CODA_960:
+ ctx->reg_idx = 0;
+ break;
+ default:
+ ctx->reg_idx = idx;
+ }
+
+ /* Power up and upload firmware if necessary */
+ ret = pm_runtime_get_sync(&dev->plat_dev->dev);
+ if (ret < 0) {
+ v4l2_err(&dev->v4l2_dev, "failed to power up: %d\n", ret);
+ goto err_pm_get;
+ }
+
+ ret = clk_prepare_enable(dev->clk_per);
+ if (ret)
+ goto err_clk_per;
+
+ ret = clk_prepare_enable(dev->clk_ahb);
+ if (ret)
+ goto err_clk_ahb;
+
+ set_default_params(ctx);
+ ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx,
+ ctx->ops->queue_init);
+ if (IS_ERR(ctx->fh.m2m_ctx)) {
+ ret = PTR_ERR(ctx->fh.m2m_ctx);
+
+ v4l2_err(&dev->v4l2_dev, "%s return error (%d)\n",
+ __func__, ret);
+ goto err_ctx_init;
+ }
+
+ ret = coda_ctrls_setup(ctx);
+ if (ret) {
+ v4l2_err(&dev->v4l2_dev, "failed to setup coda controls\n");
+ goto err_ctrls_setup;
+ }
+
+ ctx->fh.ctrl_handler = &ctx->ctrls;
+
+ ret = coda_alloc_context_buf(ctx, &ctx->parabuf, CODA_PARA_BUF_SIZE,
+ "parabuf");
+ if (ret < 0) {
+ v4l2_err(&dev->v4l2_dev, "failed to allocate parabuf");
+ goto err_dma_alloc;
+ }
+
+ ctx->bitstream.size = CODA_MAX_FRAME_SIZE;
+ ctx->bitstream.vaddr = dma_alloc_writecombine(&dev->plat_dev->dev,
+ ctx->bitstream.size, &ctx->bitstream.paddr, GFP_KERNEL);
+ if (!ctx->bitstream.vaddr) {
+ v4l2_err(&dev->v4l2_dev,
+ "failed to allocate bitstream ringbuffer");
+ ret = -ENOMEM;
+ goto err_dma_writecombine;
+ }
+ kfifo_init(&ctx->bitstream_fifo,
+ ctx->bitstream.vaddr, ctx->bitstream.size);
+ mutex_init(&ctx->bitstream_mutex);
+ mutex_init(&ctx->buffer_mutex);
+ INIT_LIST_HEAD(&ctx->timestamp_list);
+
+ coda_lock(ctx);
+ list_add(&ctx->list, &dev->instances);
+ coda_unlock(ctx);
+
+ v4l2_dbg(1, coda_debug, &dev->v4l2_dev, "Created instance %d (%p)\n",
+ ctx->idx, ctx);
+
+ return 0;
+
+err_dma_writecombine:
+ if (ctx->dev->devtype->product == CODA_DX6)
+ coda_free_aux_buf(dev, &ctx->workbuf);
+ coda_free_aux_buf(dev, &ctx->parabuf);
+err_dma_alloc:
+ v4l2_ctrl_handler_free(&ctx->ctrls);
+err_ctrls_setup:
+ v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
+err_ctx_init:
+ clk_disable_unprepare(dev->clk_ahb);
+err_clk_ahb:
+ clk_disable_unprepare(dev->clk_per);
+err_clk_per:
+ pm_runtime_put_sync(&dev->plat_dev->dev);
+err_pm_get:
+ v4l2_fh_del(&ctx->fh);
+ v4l2_fh_exit(&ctx->fh);
+ clear_bit(ctx->idx, &dev->instance_mask);
+err_coda_max:
+ kfree(ctx);
+ return ret;
+}
+
+static int coda_encoder_open(struct file *file)
+{
+ return coda_open(file, CODA_INST_ENCODER, &coda_bit_encode_ops);
+}
+
+static int coda_decoder_open(struct file *file)
+{
+ return coda_open(file, CODA_INST_DECODER, &coda_bit_decode_ops);
+}
+
+static int coda_release(struct file *file)
+{
+ struct coda_dev *dev = video_drvdata(file);
+ struct coda_ctx *ctx = fh_to_ctx(file->private_data);
+
+ v4l2_dbg(1, coda_debug, &dev->v4l2_dev, "Releasing instance %p\n",
+ ctx);
+
+ debugfs_remove_recursive(ctx->debugfs_entry);
+
+ /* If this instance is running, call .job_abort and wait for it to end */
+ v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
+
+ /* In case the instance was not running, we still need to call SEQ_END */
+ if (ctx->initialized) {
+ queue_work(dev->workqueue, &ctx->seq_end_work);
+ flush_work(&ctx->seq_end_work);
+ }
+
+ coda_lock(ctx);
+ list_del(&ctx->list);
+ coda_unlock(ctx);
+
+ dma_free_writecombine(&dev->plat_dev->dev, ctx->bitstream.size,
+ ctx->bitstream.vaddr, ctx->bitstream.paddr);
+ if (ctx->dev->devtype->product == CODA_DX6)
+ coda_free_aux_buf(dev, &ctx->workbuf);
+
+ coda_free_aux_buf(dev, &ctx->parabuf);
+ v4l2_ctrl_handler_free(&ctx->ctrls);
+ clk_disable_unprepare(dev->clk_ahb);
+ clk_disable_unprepare(dev->clk_per);
+ pm_runtime_put_sync(&dev->plat_dev->dev);
+ v4l2_fh_del(&ctx->fh);
+ v4l2_fh_exit(&ctx->fh);
+ clear_bit(ctx->idx, &dev->instance_mask);
+ if (ctx->ops->release)
+ ctx->ops->release(ctx);
+ kfree(ctx);
+
+ return 0;
+}
+
+static const struct v4l2_file_operations coda_encoder_fops = {
+ .owner = THIS_MODULE,
+ .open = coda_encoder_open,
+ .release = coda_release,
+ .poll = v4l2_m2m_fop_poll,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = v4l2_m2m_fop_mmap,
+};
+
+static const struct v4l2_file_operations coda_decoder_fops = {
+ .owner = THIS_MODULE,
+ .open = coda_decoder_open,
+ .release = coda_release,
+ .poll = v4l2_m2m_fop_poll,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = v4l2_m2m_fop_mmap,
+};
+
+static int coda_hw_init(struct coda_dev *dev)
+{
+ u32 data;
+ u16 *p;
+ int i, ret;
+
+ ret = clk_prepare_enable(dev->clk_per);
+ if (ret)
+ goto err_clk_per;
+
+ ret = clk_prepare_enable(dev->clk_ahb);
+ if (ret)
+ goto err_clk_ahb;
+
+ if (dev->rstc)
+ reset_control_reset(dev->rstc);
+
+ /*
+ * Copy the first CODA_ISRAM_SIZE in the internal SRAM.
+ * The 16-bit chars in the code buffer are in memory access
+ * order, re-sort them to CODA order for register download.
+ * Data in this SRAM survives a reboot.
+ */
+ p = (u16 *)dev->codebuf.vaddr;
+ if (dev->devtype->product == CODA_DX6) {
+ for (i = 0; i < (CODA_ISRAM_SIZE / 2); i++) {
+ data = CODA_DOWN_ADDRESS_SET(i) |
+ CODA_DOWN_DATA_SET(p[i ^ 1]);
+ coda_write(dev, data, CODA_REG_BIT_CODE_DOWN);
+ }
+ } else {
+ for (i = 0; i < (CODA_ISRAM_SIZE / 2); i++) {
+ data = CODA_DOWN_ADDRESS_SET(i) |
+ CODA_DOWN_DATA_SET(p[round_down(i, 4) +
+ 3 - (i % 4)]);
+ coda_write(dev, data, CODA_REG_BIT_CODE_DOWN);
+ }
+ }
+
+ /* Clear registers */
+ for (i = 0; i < 64; i++)
+ coda_write(dev, 0, CODA_REG_BIT_CODE_BUF_ADDR + i * 4);
+
+ /* Tell the BIT where to find everything it needs */
+ if (dev->devtype->product == CODA_960 ||
+ dev->devtype->product == CODA_7541) {
+ coda_write(dev, dev->tempbuf.paddr,
+ CODA_REG_BIT_TEMP_BUF_ADDR);
+ coda_write(dev, 0, CODA_REG_BIT_BIT_STREAM_PARAM);
+ } else {
+ coda_write(dev, dev->workbuf.paddr,
+ CODA_REG_BIT_WORK_BUF_ADDR);
+ }
+ coda_write(dev, dev->codebuf.paddr,
+ CODA_REG_BIT_CODE_BUF_ADDR);
+ coda_write(dev, 0, CODA_REG_BIT_CODE_RUN);
+
+ /* Set default values */
+ switch (dev->devtype->product) {
+ case CODA_DX6:
+ coda_write(dev, CODADX6_STREAM_BUF_PIC_FLUSH,
+ CODA_REG_BIT_STREAM_CTRL);
+ break;
+ default:
+ coda_write(dev, CODA7_STREAM_BUF_PIC_FLUSH,
+ CODA_REG_BIT_STREAM_CTRL);
+ }
+ if (dev->devtype->product == CODA_960)
+ coda_write(dev, 1 << 12, CODA_REG_BIT_FRAME_MEM_CTRL);
+ else
+ coda_write(dev, 0, CODA_REG_BIT_FRAME_MEM_CTRL);
+
+ if (dev->devtype->product != CODA_DX6)
+ coda_write(dev, 0, CODA7_REG_BIT_AXI_SRAM_USE);
+
+ coda_write(dev, CODA_INT_INTERRUPT_ENABLE,
+ CODA_REG_BIT_INT_ENABLE);
+
+ /* Reset VPU and start processor */
+ data = coda_read(dev, CODA_REG_BIT_CODE_RESET);
+ data |= CODA_REG_RESET_ENABLE;
+ coda_write(dev, data, CODA_REG_BIT_CODE_RESET);
+ udelay(10);
+ data &= ~CODA_REG_RESET_ENABLE;
+ coda_write(dev, data, CODA_REG_BIT_CODE_RESET);
+ coda_write(dev, CODA_REG_RUN_ENABLE, CODA_REG_BIT_CODE_RUN);
+
+ clk_disable_unprepare(dev->clk_ahb);
+ clk_disable_unprepare(dev->clk_per);
+
+ return 0;
+
+err_clk_ahb:
+ clk_disable_unprepare(dev->clk_per);
+err_clk_per:
+ return ret;
+}
+
+static int coda_register_device(struct coda_dev *dev, struct video_device *vfd)
+{
+ vfd->release = video_device_release_empty,
+ vfd->lock = &dev->dev_mutex;
+ vfd->v4l2_dev = &dev->v4l2_dev;
+ vfd->vfl_dir = VFL_DIR_M2M;
+ video_set_drvdata(vfd, dev);
+
+ /* Not applicable, use the selection API instead */
+ v4l2_disable_ioctl(vfd, VIDIOC_CROPCAP);
+ v4l2_disable_ioctl(vfd, VIDIOC_G_CROP);
+ v4l2_disable_ioctl(vfd, VIDIOC_S_CROP);
+
+ return video_register_device(vfd, VFL_TYPE_GRABBER, 0);
+}
+
+static void coda_fw_callback(const struct firmware *fw, void *context)
+{
+ struct coda_dev *dev = context;
+ struct platform_device *pdev = dev->plat_dev;
+ int ret;
+
+ if (!fw) {
+ v4l2_err(&dev->v4l2_dev, "firmware request failed\n");
+ goto put_pm;
+ }
+
+ /* allocate auxiliary per-device code buffer for the BIT processor */
+ ret = coda_alloc_aux_buf(dev, &dev->codebuf, fw->size, "codebuf",
+ dev->debugfs_root);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to allocate code buffer\n");
+ goto put_pm;
+ }
+
+ /* Copy the whole firmware image to the code buffer */
+ memcpy(dev->codebuf.vaddr, fw->data, fw->size);
+ release_firmware(fw);
+
+ ret = coda_hw_init(dev);
+ if (ret < 0) {
+ v4l2_err(&dev->v4l2_dev, "HW initialization failed\n");
+ goto put_pm;
+ }
+
+ ret = coda_check_firmware(dev);
+ if (ret < 0)
+ goto put_pm;
+
+ dev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
+ if (IS_ERR(dev->alloc_ctx)) {
+ v4l2_err(&dev->v4l2_dev, "Failed to alloc vb2 context\n");
+ goto put_pm;
+ }
+
+ dev->m2m_dev = v4l2_m2m_init(&coda_m2m_ops);
+ if (IS_ERR(dev->m2m_dev)) {
+ v4l2_err(&dev->v4l2_dev, "Failed to init mem2mem device\n");
+ goto rel_ctx;
+ }
+
+ dev->vfd[0].fops = &coda_encoder_fops,
+ dev->vfd[0].ioctl_ops = &coda_ioctl_ops;
+ snprintf(dev->vfd[0].name, sizeof(dev->vfd[0].name), "coda-encoder");
+ ret = coda_register_device(dev, &dev->vfd[0]);
+ if (ret) {
+ v4l2_err(&dev->v4l2_dev,
+ "Failed to register encoder video device\n");
+ goto rel_m2m;
+ }
+
+ dev->vfd[1].fops = &coda_decoder_fops,
+ dev->vfd[1].ioctl_ops = &coda_ioctl_ops;
+ snprintf(dev->vfd[1].name, sizeof(dev->vfd[1].name), "coda-decoder");
+ ret = coda_register_device(dev, &dev->vfd[1]);
+ if (ret) {
+ v4l2_err(&dev->v4l2_dev,
+ "Failed to register decoder video device\n");
+ goto rel_m2m;
+ }
+
+ v4l2_info(&dev->v4l2_dev, "codec registered as /dev/video[%d-%d]\n",
+ dev->vfd[0].num, dev->vfd[1].num);
+
+ pm_runtime_put_sync(&pdev->dev);
+ return;
+
+rel_m2m:
+ v4l2_m2m_release(dev->m2m_dev);
+rel_ctx:
+ vb2_dma_contig_cleanup_ctx(dev->alloc_ctx);
+put_pm:
+ pm_runtime_put_sync(&pdev->dev);
+}
+
+static int coda_firmware_request(struct coda_dev *dev)
+{
+ char *fw = dev->devtype->firmware;
+
+ dev_dbg(&dev->plat_dev->dev, "requesting firmware '%s' for %s\n", fw,
+ coda_product_name(dev->devtype->product));
+
+ return request_firmware_nowait(THIS_MODULE, true,
+ fw, &dev->plat_dev->dev, GFP_KERNEL, dev, coda_fw_callback);
+}
+
+enum coda_platform {
+ CODA_IMX27,
+ CODA_IMX53,
+ CODA_IMX6Q,
+ CODA_IMX6DL,
+};
+
+static const struct coda_devtype coda_devdata[] = {
+ [CODA_IMX27] = {
+ .firmware = "v4l-codadx6-imx27.bin",
+ .product = CODA_DX6,
+ .codecs = codadx6_codecs,
+ .num_codecs = ARRAY_SIZE(codadx6_codecs),
+ .workbuf_size = 288 * 1024 + FMO_SLICE_SAVE_BUF_SIZE * 8 * 1024,
+ .iram_size = 0xb000,
+ },
+ [CODA_IMX53] = {
+ .firmware = "v4l-coda7541-imx53.bin",
+ .product = CODA_7541,
+ .codecs = coda7_codecs,
+ .num_codecs = ARRAY_SIZE(coda7_codecs),
+ .workbuf_size = 128 * 1024,
+ .tempbuf_size = 304 * 1024,
+ .iram_size = 0x14000,
+ },
+ [CODA_IMX6Q] = {
+ .firmware = "v4l-coda960-imx6q.bin",
+ .product = CODA_960,
+ .codecs = coda9_codecs,
+ .num_codecs = ARRAY_SIZE(coda9_codecs),
+ .workbuf_size = 80 * 1024,
+ .tempbuf_size = 204 * 1024,
+ .iram_size = 0x21000,
+ },
+ [CODA_IMX6DL] = {
+ .firmware = "v4l-coda960-imx6dl.bin",
+ .product = CODA_960,
+ .codecs = coda9_codecs,
+ .num_codecs = ARRAY_SIZE(coda9_codecs),
+ .workbuf_size = 80 * 1024,
+ .tempbuf_size = 204 * 1024,
+ .iram_size = 0x20000,
+ },
+};
+
+static struct platform_device_id coda_platform_ids[] = {
+ { .name = "coda-imx27", .driver_data = CODA_IMX27 },
+ { .name = "coda-imx53", .driver_data = CODA_IMX53 },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(platform, coda_platform_ids);
+
+#ifdef CONFIG_OF
+static const struct of_device_id coda_dt_ids[] = {
+ { .compatible = "fsl,imx27-vpu", .data = &coda_devdata[CODA_IMX27] },
+ { .compatible = "fsl,imx53-vpu", .data = &coda_devdata[CODA_IMX53] },
+ { .compatible = "fsl,imx6q-vpu", .data = &coda_devdata[CODA_IMX6Q] },
+ { .compatible = "fsl,imx6dl-vpu", .data = &coda_devdata[CODA_IMX6DL] },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, coda_dt_ids);
+#endif
+
+static int coda_probe(struct platform_device *pdev)
+{
+ const struct of_device_id *of_id =
+ of_match_device(of_match_ptr(coda_dt_ids), &pdev->dev);
+ const struct platform_device_id *pdev_id;
+ struct coda_platform_data *pdata = pdev->dev.platform_data;
+ struct device_node *np = pdev->dev.of_node;
+ struct gen_pool *pool;
+ struct coda_dev *dev;
+ struct resource *res;
+ int ret, irq;
+
+ dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
+ if (!dev) {
+ dev_err(&pdev->dev, "Not enough memory for %s\n",
+ CODA_NAME);
+ return -ENOMEM;
+ }
+
+ spin_lock_init(&dev->irqlock);
+ INIT_LIST_HEAD(&dev->instances);
+
+ dev->plat_dev = pdev;
+ dev->clk_per = devm_clk_get(&pdev->dev, "per");
+ if (IS_ERR(dev->clk_per)) {
+ dev_err(&pdev->dev, "Could not get per clock\n");
+ return PTR_ERR(dev->clk_per);
+ }
+
+ dev->clk_ahb = devm_clk_get(&pdev->dev, "ahb");
+ if (IS_ERR(dev->clk_ahb)) {
+ dev_err(&pdev->dev, "Could not get ahb clock\n");
+ return PTR_ERR(dev->clk_ahb);
+ }
+
+ /* Get memory for physical registers */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ dev->regs_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(dev->regs_base))
+ return PTR_ERR(dev->regs_base);
+
+ /* IRQ */
+ irq = platform_get_irq_byname(pdev, "bit");
+ if (irq < 0)
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "failed to get irq resource\n");
+ return irq;
+ }
+
+ ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, coda_irq_handler,
+ IRQF_ONESHOT, dev_name(&pdev->dev), dev);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to request irq: %d\n", ret);
+ return ret;
+ }
+
+ dev->rstc = devm_reset_control_get_optional(&pdev->dev, NULL);
+ if (IS_ERR(dev->rstc)) {
+ ret = PTR_ERR(dev->rstc);
+ if (ret == -ENOENT || ret == -ENOSYS) {
+ dev->rstc = NULL;
+ } else {
+ dev_err(&pdev->dev, "failed get reset control: %d\n",
+ ret);
+ return ret;
+ }
+ }
+
+ /* Get IRAM pool from device tree or platform data */
+ pool = of_get_named_gen_pool(np, "iram", 0);
+ if (!pool && pdata)
+ pool = dev_get_gen_pool(pdata->iram_dev);
+ if (!pool) {
+ dev_err(&pdev->dev, "iram pool not available\n");
+ return -ENOMEM;
+ }
+ dev->iram_pool = pool;
+
+ ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
+ if (ret)
+ return ret;
+
+ mutex_init(&dev->dev_mutex);
+ mutex_init(&dev->coda_mutex);
+
+ pdev_id = of_id ? of_id->data : platform_get_device_id(pdev);
+
+ if (of_id) {
+ dev->devtype = of_id->data;
+ } else if (pdev_id) {
+ dev->devtype = &coda_devdata[pdev_id->driver_data];
+ } else {
+ v4l2_device_unregister(&dev->v4l2_dev);
+ return -EINVAL;
+ }
+
+ dev->debugfs_root = debugfs_create_dir("coda", NULL);
+ if (!dev->debugfs_root)
+ dev_warn(&pdev->dev, "failed to create debugfs root\n");
+
+ /* allocate auxiliary per-device buffers for the BIT processor */
+ if (dev->devtype->product == CODA_DX6) {
+ ret = coda_alloc_aux_buf(dev, &dev->workbuf,
+ dev->devtype->workbuf_size, "workbuf",
+ dev->debugfs_root);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to allocate work buffer\n");
+ v4l2_device_unregister(&dev->v4l2_dev);
+ return ret;
+ }
+ }
+
+ if (dev->devtype->tempbuf_size) {
+ ret = coda_alloc_aux_buf(dev, &dev->tempbuf,
+ dev->devtype->tempbuf_size, "tempbuf",
+ dev->debugfs_root);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to allocate temp buffer\n");
+ v4l2_device_unregister(&dev->v4l2_dev);
+ return ret;
+ }
+ }
+
+ dev->iram.size = dev->devtype->iram_size;
+ dev->iram.vaddr = gen_pool_dma_alloc(dev->iram_pool, dev->iram.size,
+ &dev->iram.paddr);
+ if (!dev->iram.vaddr) {
+ dev_warn(&pdev->dev, "unable to alloc iram\n");
+ } else {
+ dev->iram.blob.data = dev->iram.vaddr;
+ dev->iram.blob.size = dev->iram.size;
+ dev->iram.dentry = debugfs_create_blob("iram", 0644,
+ dev->debugfs_root,
+ &dev->iram.blob);
+ }
+
+ dev->workqueue = alloc_workqueue("coda", WQ_UNBOUND | WQ_MEM_RECLAIM, 1);
+ if (!dev->workqueue) {
+ dev_err(&pdev->dev, "unable to alloc workqueue\n");
+ return -ENOMEM;
+ }
+
+ platform_set_drvdata(pdev, dev);
+
+ /*
+ * Start activated so we can directly call coda_hw_init in
+ * coda_fw_callback regardless of whether CONFIG_PM_RUNTIME is
+ * enabled or whether the device is associated with a PM domain.
+ */
+ pm_runtime_get_noresume(&pdev->dev);
+ pm_runtime_set_active(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+
+ return coda_firmware_request(dev);
+}
+
+static int coda_remove(struct platform_device *pdev)
+{
+ struct coda_dev *dev = platform_get_drvdata(pdev);
+
+ video_unregister_device(&dev->vfd[0]);
+ video_unregister_device(&dev->vfd[1]);
+ if (dev->m2m_dev)
+ v4l2_m2m_release(dev->m2m_dev);
+ pm_runtime_disable(&pdev->dev);
+ if (dev->alloc_ctx)
+ vb2_dma_contig_cleanup_ctx(dev->alloc_ctx);
+ v4l2_device_unregister(&dev->v4l2_dev);
+ destroy_workqueue(dev->workqueue);
+ if (dev->iram.vaddr)
+ gen_pool_free(dev->iram_pool, (unsigned long)dev->iram.vaddr,
+ dev->iram.size);
+ coda_free_aux_buf(dev, &dev->codebuf);
+ coda_free_aux_buf(dev, &dev->tempbuf);
+ coda_free_aux_buf(dev, &dev->workbuf);
+ debugfs_remove_recursive(dev->debugfs_root);
+ return 0;
+}
+
+#ifdef CONFIG_PM_RUNTIME
+static int coda_runtime_resume(struct device *dev)
+{
+ struct coda_dev *cdev = dev_get_drvdata(dev);
+ int ret = 0;
+
+ if (dev->pm_domain && cdev->codebuf.vaddr) {
+ ret = coda_hw_init(cdev);
+ if (ret)
+ v4l2_err(&cdev->v4l2_dev, "HW initialization failed\n");
+ }
+
+ return ret;
+}
+#endif
+
+static const struct dev_pm_ops coda_pm_ops = {
+ SET_RUNTIME_PM_OPS(NULL, coda_runtime_resume, NULL)
+};
+
+static struct platform_driver coda_driver = {
+ .probe = coda_probe,
+ .remove = coda_remove,
+ .driver = {
+ .name = CODA_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(coda_dt_ids),
+ .pm = &coda_pm_ops,
+ },
+ .id_table = coda_platform_ids,
+};
+
+module_platform_driver(coda_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Javier Martin <javier.martin@vista-silicon.com>");
+MODULE_DESCRIPTION("Coda multi-standard codec V4L2 driver");
diff --git a/drivers/media/platform/coda/coda-h264.c b/drivers/media/platform/coda/coda-h264.c
new file mode 100644
index 000000000000..456773af1f1d
--- /dev/null
+++ b/drivers/media/platform/coda/coda-h264.c
@@ -0,0 +1,37 @@
+/*
+ * Coda multi-standard codec IP - H.264 helper functions
+ *
+ * Copyright (C) 2012 Vista Silicon S.L.
+ * Javier Martin, <javier.martin@vista-silicon.com>
+ * Xavier Duret
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+
+static const u8 coda_filler_nal[14] = { 0x00, 0x00, 0x00, 0x01, 0x0c, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80 };
+static const u8 coda_filler_size[8] = { 0, 7, 14, 13, 12, 11, 10, 9 };
+
+int coda_h264_padding(int size, char *p)
+{
+ int nal_size;
+ int diff;
+
+ diff = size - (size & ~0x7);
+ if (diff == 0)
+ return 0;
+
+ nal_size = coda_filler_size[diff];
+ memcpy(p, coda_filler_nal, nal_size);
+
+ /* Add rbsp stop bit and trailing at the end */
+ *(p + nal_size - 1) = 0x80;
+
+ return nal_size;
+}
diff --git a/drivers/media/platform/coda/coda.h b/drivers/media/platform/coda/coda.h
new file mode 100644
index 000000000000..bbc18c0dacd9
--- /dev/null
+++ b/drivers/media/platform/coda/coda.h
@@ -0,0 +1,287 @@
+/*
+ * Coda multi-standard codec IP
+ *
+ * Copyright (C) 2012 Vista Silicon S.L.
+ * Javier Martin, <javier.martin@vista-silicon.com>
+ * Xavier Duret
+ * Copyright (C) 2012-2014 Philipp Zabel, Pengutronix
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/debugfs.h>
+#include <linux/irqreturn.h>
+#include <linux/mutex.h>
+#include <linux/kfifo.h>
+#include <linux/videodev2.h>
+
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-fh.h>
+#include <media/videobuf2-core.h>
+
+#include "coda_regs.h"
+
+#define CODA_MAX_FRAMEBUFFERS 8
+#define CODA_MAX_FRAME_SIZE 0x100000
+#define FMO_SLICE_SAVE_BUF_SIZE (32)
+
+enum {
+ V4L2_M2M_SRC = 0,
+ V4L2_M2M_DST = 1,
+};
+
+enum coda_inst_type {
+ CODA_INST_ENCODER,
+ CODA_INST_DECODER,
+};
+
+enum coda_product {
+ CODA_DX6 = 0xf001,
+ CODA_7541 = 0xf012,
+ CODA_960 = 0xf020,
+};
+
+struct coda_devtype {
+ char *firmware;
+ enum coda_product product;
+ const struct coda_codec *codecs;
+ unsigned int num_codecs;
+ size_t workbuf_size;
+ size_t tempbuf_size;
+ size_t iram_size;
+};
+
+struct coda_aux_buf {
+ void *vaddr;
+ dma_addr_t paddr;
+ u32 size;
+ struct debugfs_blob_wrapper blob;
+ struct dentry *dentry;
+};
+
+struct coda_dev {
+ struct v4l2_device v4l2_dev;
+ struct video_device vfd[2];
+ struct platform_device *plat_dev;
+ const struct coda_devtype *devtype;
+
+ void __iomem *regs_base;
+ struct clk *clk_per;
+ struct clk *clk_ahb;
+ struct reset_control *rstc;
+
+ struct coda_aux_buf codebuf;
+ struct coda_aux_buf tempbuf;
+ struct coda_aux_buf workbuf;
+ struct gen_pool *iram_pool;
+ struct coda_aux_buf iram;
+
+ spinlock_t irqlock;
+ struct mutex dev_mutex;
+ struct mutex coda_mutex;
+ struct workqueue_struct *workqueue;
+ struct v4l2_m2m_dev *m2m_dev;
+ struct vb2_alloc_ctx *alloc_ctx;
+ struct list_head instances;
+ unsigned long instance_mask;
+ struct dentry *debugfs_root;
+};
+
+struct coda_codec {
+ u32 mode;
+ u32 src_fourcc;
+ u32 dst_fourcc;
+ u32 max_w;
+ u32 max_h;
+};
+
+struct coda_huff_tab;
+
+struct coda_params {
+ u8 rot_mode;
+ u8 h264_intra_qp;
+ u8 h264_inter_qp;
+ u8 h264_min_qp;
+ u8 h264_max_qp;
+ u8 h264_deblk_enabled;
+ u8 h264_deblk_alpha;
+ u8 h264_deblk_beta;
+ u8 mpeg4_intra_qp;
+ u8 mpeg4_inter_qp;
+ u8 gop_size;
+ int intra_refresh;
+ int codec_mode;
+ int codec_mode_aux;
+ enum v4l2_mpeg_video_multi_slice_mode slice_mode;
+ u32 framerate;
+ u16 bitrate;
+ u32 slice_max_bits;
+ u32 slice_max_mb;
+};
+
+struct coda_timestamp {
+ struct list_head list;
+ u32 sequence;
+ struct v4l2_timecode timecode;
+ struct timeval timestamp;
+};
+
+/* Per-queue, driver-specific private data */
+struct coda_q_data {
+ unsigned int width;
+ unsigned int height;
+ unsigned int bytesperline;
+ unsigned int sizeimage;
+ unsigned int fourcc;
+ struct v4l2_rect rect;
+};
+
+struct coda_iram_info {
+ u32 axi_sram_use;
+ phys_addr_t buf_bit_use;
+ phys_addr_t buf_ip_ac_dc_use;
+ phys_addr_t buf_dbk_y_use;
+ phys_addr_t buf_dbk_c_use;
+ phys_addr_t buf_ovl_use;
+ phys_addr_t buf_btp_use;
+ phys_addr_t search_ram_paddr;
+ int search_ram_size;
+ int remaining;
+ phys_addr_t next_paddr;
+};
+
+struct gdi_tiled_map {
+ int xy2ca_map[16];
+ int xy2ba_map[16];
+ int xy2ra_map[16];
+ int rbc2axi_map[32];
+ int xy2rbc_config;
+ int map_type;
+#define GDI_LINEAR_FRAME_MAP 0
+};
+
+struct coda_ctx;
+
+struct coda_context_ops {
+ int (*queue_init)(void *priv, struct vb2_queue *src_vq,
+ struct vb2_queue *dst_vq);
+ int (*start_streaming)(struct coda_ctx *ctx);
+ int (*prepare_run)(struct coda_ctx *ctx);
+ void (*finish_run)(struct coda_ctx *ctx);
+ void (*seq_end_work)(struct work_struct *work);
+ void (*release)(struct coda_ctx *ctx);
+};
+
+struct coda_ctx {
+ struct coda_dev *dev;
+ struct mutex buffer_mutex;
+ struct list_head list;
+ struct work_struct pic_run_work;
+ struct work_struct seq_end_work;
+ struct completion completion;
+ const struct coda_context_ops *ops;
+ int aborting;
+ int initialized;
+ int streamon_out;
+ int streamon_cap;
+ u32 isequence;
+ u32 qsequence;
+ u32 osequence;
+ u32 sequence_offset;
+ struct coda_q_data q_data[2];
+ enum coda_inst_type inst_type;
+ const struct coda_codec *codec;
+ enum v4l2_colorspace colorspace;
+ struct coda_params params;
+ struct v4l2_ctrl_handler ctrls;
+ struct v4l2_fh fh;
+ int gopcounter;
+ int runcounter;
+ char vpu_header[3][64];
+ int vpu_header_size[3];
+ struct kfifo bitstream_fifo;
+ struct mutex bitstream_mutex;
+ struct coda_aux_buf bitstream;
+ bool hold;
+ struct coda_aux_buf parabuf;
+ struct coda_aux_buf psbuf;
+ struct coda_aux_buf slicebuf;
+ struct coda_aux_buf internal_frames[CODA_MAX_FRAMEBUFFERS];
+ u32 frame_types[CODA_MAX_FRAMEBUFFERS];
+ struct coda_timestamp frame_timestamps[CODA_MAX_FRAMEBUFFERS];
+ u32 frame_errors[CODA_MAX_FRAMEBUFFERS];
+ struct list_head timestamp_list;
+ struct coda_aux_buf workbuf;
+ int num_internal_frames;
+ int idx;
+ int reg_idx;
+ struct coda_iram_info iram_info;
+ struct gdi_tiled_map tiled_map;
+ u32 bit_stream_param;
+ u32 frm_dis_flg;
+ u32 frame_mem_ctrl;
+ int display_idx;
+ struct dentry *debugfs_entry;
+};
+
+extern int coda_debug;
+
+void coda_write(struct coda_dev *dev, u32 data, u32 reg);
+unsigned int coda_read(struct coda_dev *dev, u32 reg);
+
+int coda_alloc_aux_buf(struct coda_dev *dev, struct coda_aux_buf *buf,
+ size_t size, const char *name, struct dentry *parent);
+void coda_free_aux_buf(struct coda_dev *dev, struct coda_aux_buf *buf);
+
+static inline int coda_alloc_context_buf(struct coda_ctx *ctx,
+ struct coda_aux_buf *buf, size_t size,
+ const char *name)
+{
+ return coda_alloc_aux_buf(ctx->dev, buf, size, name, ctx->debugfs_entry);
+}
+
+int coda_encoder_queue_init(void *priv, struct vb2_queue *src_vq,
+ struct vb2_queue *dst_vq);
+int coda_decoder_queue_init(void *priv, struct vb2_queue *src_vq,
+ struct vb2_queue *dst_vq);
+
+int coda_hw_reset(struct coda_ctx *ctx);
+
+void coda_fill_bitstream(struct coda_ctx *ctx);
+
+void coda_set_gdi_regs(struct coda_ctx *ctx);
+
+static inline struct coda_q_data *get_q_data(struct coda_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;
+ }
+}
+
+const char *coda_product_name(int product);
+
+int coda_check_firmware(struct coda_dev *dev);
+
+static inline int coda_get_bitstream_payload(struct coda_ctx *ctx)
+{
+ return kfifo_len(&ctx->bitstream_fifo);
+}
+
+void coda_bit_stream_end_flag(struct coda_ctx *ctx);
+
+int coda_h264_padding(int size, char *p);
+
+extern const struct coda_context_ops coda_bit_encode_ops;
+extern const struct coda_context_ops coda_bit_decode_ops;
+
+irqreturn_t coda_irq_handler(int irq, void *data);
diff --git a/drivers/media/platform/coda.h b/drivers/media/platform/coda/coda_regs.h
index c791275e307b..c791275e307b 100644
--- a/drivers/media/platform/coda.h
+++ b/drivers/media/platform/coda/coda_regs.h
diff --git a/drivers/media/platform/davinci/Kconfig b/drivers/media/platform/davinci/Kconfig
index afb3aec1320e..d9e1ddb586b1 100644
--- a/drivers/media/platform/davinci/Kconfig
+++ b/drivers/media/platform/davinci/Kconfig
@@ -1,6 +1,8 @@
config VIDEO_DAVINCI_VPIF_DISPLAY
tristate "TI DaVinci VPIF V4L2-Display driver"
- depends on VIDEO_DEV && ARCH_DAVINCI
+ depends on VIDEO_DEV
+ depends on ARCH_DAVINCI || COMPILE_TEST
+ depends on HAS_DMA
select VIDEOBUF2_DMA_CONTIG
select VIDEO_ADV7343 if MEDIA_SUBDRV_AUTOSELECT
select VIDEO_THS7303 if MEDIA_SUBDRV_AUTOSELECT
@@ -14,7 +16,9 @@ config VIDEO_DAVINCI_VPIF_DISPLAY
config VIDEO_DAVINCI_VPIF_CAPTURE
tristate "TI DaVinci VPIF video capture driver"
- depends on VIDEO_DEV && ARCH_DAVINCI
+ depends on VIDEO_DEV
+ depends on ARCH_DAVINCI || COMPILE_TEST
+ depends on HAS_DMA
select VIDEOBUF2_DMA_CONTIG
help
Enables Davinci VPIF module used for capture devices.
@@ -26,7 +30,9 @@ config VIDEO_DAVINCI_VPIF_CAPTURE
config VIDEO_DM6446_CCDC
tristate "TI DM6446 CCDC video capture driver"
- depends on VIDEO_V4L2 && (ARCH_DAVINCI || ARCH_OMAP3)
+ depends on VIDEO_V4L2
+ depends on ARCH_DAVINCI || COMPILE_TEST
+ depends on HAS_DMA
select VIDEOBUF_DMA_CONTIG
help
Enables DaVinci CCD hw module. DaVinci CCDC hw interfaces
@@ -40,7 +46,9 @@ config VIDEO_DM6446_CCDC
config VIDEO_DM355_CCDC
tristate "TI DM355 CCDC video capture driver"
- depends on VIDEO_V4L2 && ARCH_DAVINCI
+ depends on VIDEO_V4L2
+ depends on ARCH_DAVINCI || COMPILE_TEST
+ depends on HAS_DMA
select VIDEOBUF_DMA_CONTIG
help
Enables DM355 CCD hw module. DM355 CCDC hw interfaces
@@ -55,6 +63,7 @@ config VIDEO_DM355_CCDC
config VIDEO_DM365_ISIF
tristate "TI DM365 ISIF video capture driver"
depends on VIDEO_V4L2 && ARCH_DAVINCI
+ depends on HAS_DMA
select VIDEOBUF_DMA_CONTIG
help
Enables ISIF hw module. This is the hardware module for
@@ -67,6 +76,7 @@ config VIDEO_DM365_ISIF
config VIDEO_DAVINCI_VPBE_DISPLAY
tristate "TI DaVinci VPBE V4L2-Display driver"
depends on ARCH_DAVINCI
+ depends on HAS_DMA
select VIDEOBUF2_DMA_CONTIG
help
Enables Davinci VPBE module used for display devices.
diff --git a/drivers/media/platform/davinci/dm355_ccdc.c b/drivers/media/platform/davinci/dm355_ccdc.c
index 05f8fb7f7b70..3f44deb5b7a7 100644
--- a/drivers/media/platform/davinci/dm355_ccdc.c
+++ b/drivers/media/platform/davinci/dm355_ccdc.c
@@ -460,7 +460,7 @@ static void ccdc_config_black_compense(struct ccdc_black_compensation *bcomp)
* ccdc_write_dfc_entry()
* write an entry in the dfc table.
*/
-int ccdc_write_dfc_entry(int index, struct ccdc_vertical_dft *dfc)
+static int ccdc_write_dfc_entry(int index, struct ccdc_vertical_dft *dfc)
{
/* TODO This is to be re-visited and adjusted */
#define DFC_WRITE_WAIT_COUNT 1000
diff --git a/drivers/media/platform/davinci/dm644x_ccdc.c b/drivers/media/platform/davinci/dm644x_ccdc.c
index 07e98df3d867..62a0ebb01056 100644
--- a/drivers/media/platform/davinci/dm644x_ccdc.c
+++ b/drivers/media/platform/davinci/dm644x_ccdc.c
@@ -130,9 +130,9 @@ static void ccdc_enable_vport(int flag)
* This function will configure the window size
* to be capture in CCDC reg
*/
-void ccdc_setwin(struct v4l2_rect *image_win,
- enum ccdc_frmfmt frm_fmt,
- int ppc)
+static void ccdc_setwin(struct v4l2_rect *image_win,
+ enum ccdc_frmfmt frm_fmt,
+ int ppc)
{
int horz_start, horz_nr_pixels;
int vert_start, vert_nr_lines;
@@ -291,7 +291,7 @@ static int ccdc_update_raw_params(struct ccdc_config_params_raw *raw_params)
dev_dbg(ccdc_cfg.dev, "\n copy_from_user failed");
return -EFAULT;
}
- config_params->fault_pxl.fpc_table_addr = (unsigned int)fpc_physaddr;
+ config_params->fault_pxl.fpc_table_addr = (unsigned long)fpc_physaddr;
return 0;
}
@@ -370,7 +370,7 @@ static int ccdc_set_params(void __user *params)
* ccdc_config_ycbcr()
* This function will configure CCDC for YCbCr video capture
*/
-void ccdc_config_ycbcr(void)
+static void ccdc_config_ycbcr(void)
{
struct ccdc_params_ycbcr *params = &ccdc_cfg.ycbcr;
u32 syn_mode;
@@ -506,7 +506,7 @@ static void ccdc_config_fpc(struct ccdc_fault_pixel *fpc)
/* Configure Fault pixel if needed */
regw(fpc->fpc_table_addr, CCDC_FPC_ADDR);
- dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to FPC_ADDR...\n",
+ dev_dbg(ccdc_cfg.dev, "\nWriting 0x%lx to FPC_ADDR...\n",
(fpc->fpc_table_addr));
/* Write the FPC params with FPC disable */
val = fpc->fp_num & CCDC_FPC_FPC_NUM_MASK;
@@ -523,7 +523,7 @@ static void ccdc_config_fpc(struct ccdc_fault_pixel *fpc)
* ccdc_config_raw()
* This function will configure CCDC for Raw capture mode
*/
-void ccdc_config_raw(void)
+static void ccdc_config_raw(void)
{
struct ccdc_params_raw *params = &ccdc_cfg.bayer;
struct ccdc_config_params_raw *config_params =
diff --git a/drivers/media/platform/davinci/vpfe_capture.c b/drivers/media/platform/davinci/vpfe_capture.c
index ea7661a27479..de55f47a77db 100644
--- a/drivers/media/platform/davinci/vpfe_capture.c
+++ b/drivers/media/platform/davinci/vpfe_capture.c
@@ -125,7 +125,7 @@ static DEFINE_MUTEX(ccdc_lock);
/* ccdc configuration */
static struct ccdc_config *ccdc_cfg;
-const struct vpfe_standard vpfe_standards[] = {
+static const struct vpfe_standard vpfe_standards[] = {
{V4L2_STD_525_60, 720, 480, {11, 10}, 1},
{V4L2_STD_625_50, 720, 576, {54, 59}, 1},
};
@@ -442,11 +442,10 @@ static int vpfe_config_image_format(struct vpfe_device *vpfe_dev,
return ret;
/* Update the values of sizeimage and bytesperline */
- if (!ret) {
- pix->bytesperline = ccdc_dev->hw_ops.get_line_length();
- pix->sizeimage = pix->bytesperline * pix->height;
- }
- return ret;
+ pix->bytesperline = ccdc_dev->hw_ops.get_line_length();
+ pix->sizeimage = pix->bytesperline * pix->height;
+
+ return 0;
}
static int vpfe_initialize_device(struct vpfe_device *vpfe_dev)
@@ -943,12 +942,11 @@ static int vpfe_g_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *fmt)
{
struct vpfe_device *vpfe_dev = video_drvdata(file);
- int ret = 0;
v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_g_fmt_vid_cap\n");
/* Fill in the information about format */
*fmt = vpfe_dev->fmt;
- return ret;
+ return 0;
}
static int vpfe_enum_fmt_vid_cap(struct file *file, void *priv,
@@ -1914,7 +1912,7 @@ static int vpfe_probe(struct platform_device *pdev)
v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev,
"trying to register vpfe device.\n");
v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev,
- "video_dev=%x\n", (int)&vpfe_dev->video_dev);
+ "video_dev=%p\n", &vpfe_dev->video_dev);
vpfe_dev->fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ret = video_register_device(vpfe_dev->video_dev,
VFL_TYPE_GRABBER, -1);
diff --git a/drivers/media/platform/davinci/vpif.c b/drivers/media/platform/davinci/vpif.c
index cd08e5248387..3dad5bd7fe0a 100644
--- a/drivers/media/platform/davinci/vpif.c
+++ b/drivers/media/platform/davinci/vpif.c
@@ -38,6 +38,7 @@ MODULE_LICENSE("GPL");
#define VPIF_CH3_MAX_MODES 2
spinlock_t vpif_lock;
+EXPORT_SYMBOL_GPL(vpif_lock);
void __iomem *vpif_base;
EXPORT_SYMBOL_GPL(vpif_base);
diff --git a/drivers/media/platform/davinci/vpif_capture.c b/drivers/media/platform/davinci/vpif_capture.c
index b054b7eec53d..3ccb26ff43c8 100644
--- a/drivers/media/platform/davinci/vpif_capture.c
+++ b/drivers/media/platform/davinci/vpif_capture.c
@@ -213,8 +213,6 @@ static int vpif_start_streaming(struct vb2_queue *vq, unsigned int count)
/* Remove buffer from the buffer queue */
list_del(&common->cur_frm->list);
spin_unlock_irqrestore(&common->irqlock, flags);
- /* Mark state of the current frame to active */
- common->cur_frm->vb.state = VB2_BUF_STATE_ACTIVE;
addr = vb2_dma_contig_plane_dma_addr(&common->cur_frm->vb, 0);
@@ -350,7 +348,6 @@ static void vpif_schedule_next_buffer(struct common_obj *common)
/* Remove that buffer from the buffer queue */
list_del(&common->next_frm->list);
spin_unlock(&common->irqlock);
- common->next_frm->vb.state = VB2_BUF_STATE_ACTIVE;
addr = vb2_dma_contig_plane_dma_addr(&common->next_frm->vb, 0);
/* Set top and bottom field addresses in VPIF registers */
@@ -373,7 +370,6 @@ static irqreturn_t vpif_channel_isr(int irq, void *dev_id)
struct vpif_device *dev = &vpif_obj;
struct common_obj *common;
struct channel_obj *ch;
- enum v4l2_field field;
int channel_id = 0;
int fid = -1, i;
@@ -383,8 +379,6 @@ static irqreturn_t vpif_channel_isr(int irq, void *dev_id)
ch = dev->dev[channel_id];
- field = ch->common[VPIF_VIDEO_INDEX].fmt.fmt.pix.field;
-
for (i = 0; i < VPIF_NUMBER_OF_OBJECTS; i++) {
common = &ch->common[i];
/* skip If streaming is not started in this channel */
@@ -533,7 +527,7 @@ static int vpif_update_std_info(struct channel_obj *ch)
*/
static void vpif_calculate_offsets(struct channel_obj *ch)
{
- unsigned int hpitch, vpitch, sizeimage;
+ unsigned int hpitch, sizeimage;
struct video_obj *vid_ch = &(ch->video);
struct vpif_params *vpifparams = &ch->vpifparams;
struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
@@ -552,7 +546,6 @@ static void vpif_calculate_offsets(struct channel_obj *ch)
sizeimage = common->fmt.fmt.pix.sizeimage;
hpitch = common->fmt.fmt.pix.bytesperline;
- vpitch = sizeimage / (hpitch * 2);
if ((V4L2_FIELD_NONE == vid_ch->buf_field) ||
(V4L2_FIELD_INTERLACED == vid_ch->buf_field)) {
@@ -1603,7 +1596,7 @@ static int vpif_suspend(struct device *dev)
ch = vpif_obj.dev[i];
common = &ch->common[VPIF_VIDEO_INDEX];
- if (!vb2_is_streaming(&common->buffer_queue))
+ if (!vb2_start_streaming_called(&common->buffer_queue))
continue;
mutex_lock(&common->lock);
@@ -1637,7 +1630,7 @@ static int vpif_resume(struct device *dev)
ch = vpif_obj.dev[i];
common = &ch->common[VPIF_VIDEO_INDEX];
- if (!vb2_is_streaming(&common->buffer_queue))
+ if (!vb2_start_streaming_called(&common->buffer_queue))
continue;
mutex_lock(&common->lock);
diff --git a/drivers/media/platform/davinci/vpif_display.c b/drivers/media/platform/davinci/vpif_display.c
index a03ec7381cfe..8d6ced56253c 100644
--- a/drivers/media/platform/davinci/vpif_display.c
+++ b/drivers/media/platform/davinci/vpif_display.c
@@ -196,8 +196,6 @@ static int vpif_start_streaming(struct vb2_queue *vq, unsigned int count)
list_del(&common->cur_frm->list);
spin_unlock_irqrestore(&common->irqlock, flags);
- /* Mark state of the current frame to active */
- common->cur_frm->vb.state = VB2_BUF_STATE_ACTIVE;
addr = vb2_dma_contig_plane_dma_addr(&common->cur_frm->vb, 0);
common->set_addr((addr + common->ytop_off),
@@ -306,8 +304,6 @@ static void process_progressive_mode(struct common_obj *common)
/* Remove that buffer from the buffer queue */
list_del(&common->next_frm->list);
spin_unlock(&common->irqlock);
- /* Mark status of the buffer as active */
- common->next_frm->vb.state = VB2_BUF_STATE_ACTIVE;
/* Set top and bottom field addrs in VPIF registers */
addr = vb2_dma_contig_plane_dma_addr(&common->next_frm->vb, 0);
@@ -360,7 +356,6 @@ static irqreturn_t vpif_channel_isr(int irq, void *dev_id)
struct vpif_device *dev = &vpif_obj;
struct channel_obj *ch;
struct common_obj *common;
- enum v4l2_field field;
int fid = -1, i;
int channel_id = 0;
@@ -369,7 +364,6 @@ static irqreturn_t vpif_channel_isr(int irq, void *dev_id)
return IRQ_NONE;
ch = dev->dev[channel_id];
- field = ch->common[VPIF_VIDEO_INDEX].fmt.fmt.pix.field;
for (i = 0; i < VPIF_NUMOBJECTS; i++) {
common = &ch->common[i];
/* If streaming is started in this channel */
@@ -502,7 +496,7 @@ static void vpif_calculate_offsets(struct channel_obj *ch)
struct vpif_params *vpifparams = &ch->vpifparams;
enum v4l2_field field = common->fmt.fmt.pix.field;
struct video_obj *vid_ch = &ch->video;
- unsigned int hpitch, vpitch, sizeimage;
+ unsigned int hpitch, sizeimage;
if (V4L2_FIELD_ANY == common->fmt.fmt.pix.field) {
if (ch->vpifparams.std_info.frm_fmt)
@@ -516,7 +510,6 @@ static void vpif_calculate_offsets(struct channel_obj *ch)
sizeimage = common->fmt.fmt.pix.sizeimage;
hpitch = common->fmt.fmt.pix.bytesperline;
- vpitch = sizeimage / (hpitch * 2);
if ((V4L2_FIELD_NONE == vid_ch->buf_field) ||
(V4L2_FIELD_INTERLACED == vid_ch->buf_field)) {
common->ytop_off = 0;
@@ -813,17 +806,14 @@ static int vpif_set_output(struct vpif_display_config *vpif_cfg,
{
struct vpif_display_chan_config *chan_cfg =
&vpif_cfg->chan_config[ch->channel_id];
- struct vpif_subdev_info *subdev_info = NULL;
struct v4l2_subdev *sd = NULL;
u32 input = 0, output = 0;
int sd_index;
int ret;
sd_index = vpif_output_to_subdev(vpif_cfg, chan_cfg, index);
- if (sd_index >= 0) {
+ if (sd_index >= 0)
sd = vpif_obj.sd[sd_index];
- subdev_info = &vpif_cfg->subdevinfo[sd_index];
- }
if (sd) {
input = chan_cfg->outputs[index].input_route;
@@ -1210,8 +1200,8 @@ static int vpif_probe_complete(void)
INIT_LIST_HEAD(&common->dma_queue);
/* register video device */
- vpif_dbg(1, debug, "channel=%x,channel->video_dev=%x\n",
- (int)ch, (int)&ch->video_dev);
+ vpif_dbg(1, debug, "channel=%p,channel->video_dev=%p\n",
+ ch, &ch->video_dev);
/* Initialize the video_device structure */
vdev = ch->video_dev;
@@ -1410,7 +1400,7 @@ static int vpif_suspend(struct device *dev)
ch = vpif_obj.dev[i];
common = &ch->common[VPIF_VIDEO_INDEX];
- if (!vb2_is_streaming(&common->buffer_queue))
+ if (!vb2_start_streaming_called(&common->buffer_queue))
continue;
mutex_lock(&common->lock);
@@ -1442,7 +1432,7 @@ static int vpif_resume(struct device *dev)
ch = vpif_obj.dev[i];
common = &ch->common[VPIF_VIDEO_INDEX];
- if (!vb2_is_streaming(&common->buffer_queue))
+ if (!vb2_start_streaming_called(&common->buffer_queue))
continue;
mutex_lock(&common->lock);
diff --git a/drivers/media/platform/exynos-gsc/gsc-core.c b/drivers/media/platform/exynos-gsc/gsc-core.c
index 9d0cc04d7ab7..b4c9f1d08968 100644
--- a/drivers/media/platform/exynos-gsc/gsc-core.c
+++ b/drivers/media/platform/exynos-gsc/gsc-core.c
@@ -852,8 +852,8 @@ int gsc_prepare_addr(struct gsc_ctx *ctx, struct vb2_buffer *vb,
(frame->fmt->pixelformat == V4L2_PIX_FMT_YVU420M))
swap(addr->cb, addr->cr);
- pr_debug("ADDR: y= 0x%X cb= 0x%X cr= 0x%X ret= %d",
- addr->y, addr->cb, addr->cr, ret);
+ pr_debug("ADDR: y= %pad cb= %pad cr= %pad ret= %d",
+ &addr->y, &addr->cb, &addr->cr, ret);
return ret;
}
@@ -1086,7 +1086,7 @@ static int gsc_probe(struct platform_device *pdev)
else
gsc->id = pdev->id;
- if (gsc->id < 0 || gsc->id >= drv_data->num_entities) {
+ if (gsc->id >= drv_data->num_entities) {
dev_err(dev, "Invalid platform device id: %d\n", gsc->id);
return -EINVAL;
}
diff --git a/drivers/media/platform/exynos-gsc/gsc-m2m.c b/drivers/media/platform/exynos-gsc/gsc-m2m.c
index e434f1f03d7b..74e1de637e8f 100644
--- a/drivers/media/platform/exynos-gsc/gsc-m2m.c
+++ b/drivers/media/platform/exynos-gsc/gsc-m2m.c
@@ -362,7 +362,6 @@ static int gsc_m2m_reqbufs(struct file *file, void *fh,
{
struct gsc_ctx *ctx = fh_to_ctx(fh);
struct gsc_dev *gsc = ctx->gsc_dev;
- struct gsc_frame *frame;
u32 max_cnt;
max_cnt = (reqbufs->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) ?
@@ -376,8 +375,6 @@ static int gsc_m2m_reqbufs(struct file *file, void *fh,
gsc_ctx_state_lock_clear(GSC_DST_FMT, ctx);
}
- frame = ctx_get_frame(ctx, reqbufs->type);
-
return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs);
}
diff --git a/drivers/media/platform/exynos-gsc/gsc-regs.c b/drivers/media/platform/exynos-gsc/gsc-regs.c
index e22d147a6940..ce12a1100511 100644
--- a/drivers/media/platform/exynos-gsc/gsc-regs.c
+++ b/drivers/media/platform/exynos-gsc/gsc-regs.c
@@ -90,8 +90,8 @@ void gsc_hw_set_output_buf_masking(struct gsc_dev *dev, u32 shift,
void gsc_hw_set_input_addr(struct gsc_dev *dev, struct gsc_addr *addr,
int index)
{
- pr_debug("src_buf[%d]: 0x%X, cb: 0x%X, cr: 0x%X", index,
- addr->y, addr->cb, addr->cr);
+ pr_debug("src_buf[%d]: %pad, cb: %pad, cr: %pad", index,
+ &addr->y, &addr->cb, &addr->cr);
writel(addr->y, dev->regs + GSC_IN_BASE_ADDR_Y(index));
writel(addr->cb, dev->regs + GSC_IN_BASE_ADDR_CB(index));
writel(addr->cr, dev->regs + GSC_IN_BASE_ADDR_CR(index));
@@ -101,8 +101,8 @@ void gsc_hw_set_input_addr(struct gsc_dev *dev, struct gsc_addr *addr,
void gsc_hw_set_output_addr(struct gsc_dev *dev,
struct gsc_addr *addr, int index)
{
- pr_debug("dst_buf[%d]: 0x%X, cb: 0x%X, cr: 0x%X",
- index, addr->y, addr->cb, addr->cr);
+ pr_debug("dst_buf[%d]: %pad, cb: %pad, cr: %pad",
+ index, &addr->y, &addr->cb, &addr->cr);
writel(addr->y, dev->regs + GSC_OUT_BASE_ADDR_Y(index));
writel(addr->cb, dev->regs + GSC_OUT_BASE_ADDR_CB(index));
writel(addr->cr, dev->regs + GSC_OUT_BASE_ADDR_CR(index));
diff --git a/drivers/media/platform/exynos4-is/Kconfig b/drivers/media/platform/exynos4-is/Kconfig
index 5dcaa0a80540..77c951237744 100644
--- a/drivers/media/platform/exynos4-is/Kconfig
+++ b/drivers/media/platform/exynos4-is/Kconfig
@@ -2,7 +2,7 @@
config VIDEO_SAMSUNG_EXYNOS4_IS
bool "Samsung S5P/EXYNOS4 SoC series Camera Subsystem driver"
depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
- depends on (PLAT_S5P || ARCH_EXYNOS)
+ depends on (PLAT_S5P || ARCH_EXYNOS || COMPILE_TEST)
depends on OF && COMMON_CLK
help
Say Y here to enable camera host interface devices for
@@ -16,6 +16,7 @@ config VIDEO_EXYNOS4_IS_COMMON
config VIDEO_S5P_FIMC
tristate "S5P/EXYNOS4 FIMC/CAMIF camera interface driver"
depends on I2C
+ depends on HAS_DMA
select VIDEOBUF2_DMA_CONTIG
select V4L2_MEM2MEM_DEV
select MFD_SYSCON
@@ -43,6 +44,7 @@ if SOC_EXYNOS4212 || SOC_EXYNOS4412 || SOC_EXYNOS5250
config VIDEO_EXYNOS_FIMC_LITE
tristate "EXYNOS FIMC-LITE camera interface driver"
depends on I2C
+ depends on HAS_DMA
select VIDEOBUF2_DMA_CONTIG
select VIDEO_EXYNOS4_IS_COMMON
help
@@ -55,6 +57,7 @@ endif
config VIDEO_EXYNOS4_FIMC_IS
tristate "EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver"
+ depends on HAS_DMA
select VIDEOBUF2_DMA_CONTIG
depends on OF
select FW_LOADER
diff --git a/drivers/media/platform/exynos4-is/fimc-is-errno.c b/drivers/media/platform/exynos4-is/fimc-is-errno.c
index e8519e151c1a..e050e63fe358 100644
--- a/drivers/media/platform/exynos4-is/fimc-is-errno.c
+++ b/drivers/media/platform/exynos4-is/fimc-is-errno.c
@@ -15,7 +15,7 @@
#include "fimc-is-errno.h"
-const char * const fimc_is_param_strerr(unsigned int error)
+const char *fimc_is_param_strerr(unsigned int error)
{
switch (error) {
case ERROR_COMMON_CMD:
@@ -146,7 +146,7 @@ const char * const fimc_is_param_strerr(unsigned int error)
}
}
-const char * const fimc_is_strerr(unsigned int error)
+const char *fimc_is_strerr(unsigned int error)
{
error &= ~IS_ERROR_TIME_OUT_FLAG;
diff --git a/drivers/media/platform/exynos4-is/fimc-is-errno.h b/drivers/media/platform/exynos4-is/fimc-is-errno.h
index 3de6f6da6f87..ef981e74513a 100644
--- a/drivers/media/platform/exynos4-is/fimc-is-errno.h
+++ b/drivers/media/platform/exynos4-is/fimc-is-errno.h
@@ -242,7 +242,7 @@ enum fimc_is_error {
ERROR_SCALER_FLIP = 521,
};
-const char * const fimc_is_strerr(unsigned int error);
-const char * const fimc_is_param_strerr(unsigned int error);
+const char *fimc_is_strerr(unsigned int error);
+const char *fimc_is_param_strerr(unsigned int error);
#endif /* FIMC_IS_ERR_H_ */
diff --git a/drivers/media/platform/exynos4-is/fimc-is-param.c b/drivers/media/platform/exynos4-is/fimc-is-param.c
index bf1465d1bf6d..72b9b436c5c0 100644
--- a/drivers/media/platform/exynos4-is/fimc-is-param.c
+++ b/drivers/media/platform/exynos4-is/fimc-is-param.c
@@ -667,7 +667,6 @@ void __is_set_fd_config_orientation_val(struct fimc_is *is, u32 val)
void fimc_is_set_initial_params(struct fimc_is *is)
{
struct global_param *global;
- struct sensor_param *sensor;
struct isp_param *isp;
struct drc_param *drc;
struct fd_param *fd;
@@ -676,7 +675,6 @@ void fimc_is_set_initial_params(struct fimc_is *is)
index = is->config_index;
global = &is->config[index].global;
- sensor = &is->config[index].sensor;
isp = &is->config[index].isp;
drc = &is->config[index].drc;
fd = &is->config[index].fd;
diff --git a/drivers/media/platform/exynos4-is/fimc-is.c b/drivers/media/platform/exynos4-is/fimc-is.c
index 5476dce3ad29..22162b2567da 100644
--- a/drivers/media/platform/exynos4-is/fimc-is.c
+++ b/drivers/media/platform/exynos4-is/fimc-is.c
@@ -388,7 +388,7 @@ static void fimc_is_load_firmware(const struct firmware *fw, void *context)
mutex_lock(&is->lock);
if (fw->size < FIMC_IS_FW_SIZE_MIN || fw->size > FIMC_IS_FW_SIZE_MAX) {
- dev_err(dev, "wrong firmware size: %d\n", fw->size);
+ dev_err(dev, "wrong firmware size: %zu\n", fw->size);
goto done;
}
@@ -416,7 +416,7 @@ static void fimc_is_load_firmware(const struct firmware *fw, void *context)
dev_info(dev, "loaded firmware: %s, rev. %s\n",
is->fw.info, is->fw.version);
- dev_dbg(dev, "FW size: %d, paddr: %#x\n", fw->size, is->memory.paddr);
+ dev_dbg(dev, "FW size: %zu, paddr: %pad\n", fw->size, &is->memory.paddr);
is->is_shared_region->chip_id = 0xe4412;
is->is_shared_region->chip_rev_no = 1;
@@ -693,9 +693,9 @@ int fimc_is_hw_initialize(struct fimc_is *is)
return -EIO;
}
- pr_debug("shared region: %#x, parameter region: %#x\n",
- is->memory.paddr + FIMC_IS_SHARED_REGION_OFFSET,
- is->is_dma_p_region);
+ pr_debug("shared region: %pad, parameter region: %pad\n",
+ &is->memory.paddr + FIMC_IS_SHARED_REGION_OFFSET,
+ &is->is_dma_p_region);
is->setfile.sub_index = 0;
diff --git a/drivers/media/platform/exynos4-is/fimc-isp-video.c b/drivers/media/platform/exynos4-is/fimc-isp-video.c
index 93f9cf2ebcd6..76b6b4d14616 100644
--- a/drivers/media/platform/exynos4-is/fimc-isp-video.c
+++ b/drivers/media/platform/exynos4-is/fimc-isp-video.c
@@ -219,9 +219,9 @@ static void isp_video_capture_buffer_queue(struct vb2_buffer *vb)
ivb->dma_addr[i];
isp_dbg(2, &video->ve.vdev,
- "dma_buf %d (%d/%d/%d) addr: %#x\n",
- buf_index, ivb->index, i, vb->v4l2_buf.index,
- ivb->dma_addr[i]);
+ "dma_buf %pad (%d/%d/%d) addr: %pad\n",
+ &buf_index, ivb->index, i, vb->v4l2_buf.index,
+ &ivb->dma_addr[i]);
}
if (++video->buf_count < video->reqbufs_count)
@@ -313,7 +313,6 @@ static int isp_video_release(struct file *file)
struct fimc_is_video *ivc = &isp->video_capture;
struct media_entity *entity = &ivc->ve.vdev.entity;
struct media_device *mdev = entity->parent;
- int ret = 0;
mutex_lock(&isp->video_lock);
@@ -335,7 +334,7 @@ static int isp_video_release(struct file *file)
pm_runtime_put(&isp->pdev->dev);
mutex_unlock(&isp->video_lock);
- return ret;
+ return 0;
}
static const struct v4l2_file_operations isp_video_fops = {
diff --git a/drivers/media/platform/exynos4-is/media-dev.c b/drivers/media/platform/exynos4-is/media-dev.c
index 344718df5c62..54c49d5e7690 100644
--- a/drivers/media/platform/exynos4-is/media-dev.c
+++ b/drivers/media/platform/exynos4-is/media-dev.c
@@ -1098,8 +1098,10 @@ static int fimc_md_link_notify(struct media_link *link, unsigned int flags,
if (notification == MEDIA_DEV_NOTIFY_PRE_LINK_CH) {
if (!(flags & MEDIA_LNK_FL_ENABLED))
ret = __fimc_md_modify_pipelines(sink, false);
+#if 0
else
- ; /* TODO: Link state change validation */
+ /* TODO: Link state change validation */
+#endif
/* After link activation */
} else if (notification == MEDIA_DEV_NOTIFY_POST_LINK_CH &&
(link->flags & MEDIA_LNK_FL_ENABLED)) {
diff --git a/drivers/media/platform/exynos4-is/mipi-csis.c b/drivers/media/platform/exynos4-is/mipi-csis.c
index ae54ef5f535d..db6fd14d1936 100644
--- a/drivers/media/platform/exynos4-is/mipi-csis.c
+++ b/drivers/media/platform/exynos4-is/mipi-csis.c
@@ -25,6 +25,7 @@
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
+#include <linux/sizes.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/videodev2.h>
@@ -752,7 +753,7 @@ static int s5pcsis_parse_dt(struct platform_device *pdev,
v4l2_of_parse_endpoint(node, &endpoint);
state->index = endpoint.base.port - FIMC_INPUT_MIPI_CSI2_0;
- if (state->index < 0 || state->index >= CSIS_MAX_ENTITIES)
+ if (state->index >= CSIS_MAX_ENTITIES)
return -ENXIO;
/* Get MIPI CSI-2 bus configration from the endpoint node. */
diff --git a/drivers/media/platform/marvell-ccic/Kconfig b/drivers/media/platform/marvell-ccic/Kconfig
index bf739e3b3398..6265d36adceb 100644
--- a/drivers/media/platform/marvell-ccic/Kconfig
+++ b/drivers/media/platform/marvell-ccic/Kconfig
@@ -1,6 +1,7 @@
config VIDEO_CAFE_CCIC
tristate "Marvell 88ALP01 (Cafe) CMOS Camera Controller support"
depends on PCI && I2C && VIDEO_V4L2
+ depends on HAS_DMA
select VIDEO_OV7670
select VIDEOBUF2_VMALLOC
select VIDEOBUF2_DMA_CONTIG
@@ -12,6 +13,7 @@ config VIDEO_CAFE_CCIC
config VIDEO_MMP_CAMERA
tristate "Marvell Armada 610 integrated camera controller support"
depends on ARCH_MMP && I2C && VIDEO_V4L2
+ depends on HAS_DMA
select VIDEO_OV7670
select I2C_GPIO
select VIDEOBUF2_DMA_SG
diff --git a/drivers/media/platform/marvell-ccic/mcam-core.c b/drivers/media/platform/marvell-ccic/mcam-core.c
index be4b51212106..7a86c77bffa0 100644
--- a/drivers/media/platform/marvell-ccic/mcam-core.c
+++ b/drivers/media/platform/marvell-ccic/mcam-core.c
@@ -67,7 +67,7 @@ MODULE_PARM_DESC(dma_buf_size,
"parameters require larger buffers, an attempt to reallocate "
"will be made.");
#else /* MCAM_MODE_VMALLOC */
-static const bool alloc_bufs_at_read = 0;
+static const bool alloc_bufs_at_read;
static const int n_dma_bufs = 3; /* Used by S/G_PARM */
#endif /* MCAM_MODE_VMALLOC */
diff --git a/drivers/media/platform/mx2_emmaprp.c b/drivers/media/platform/mx2_emmaprp.c
index fa8f7cabe364..4971ff21f82b 100644
--- a/drivers/media/platform/mx2_emmaprp.c
+++ b/drivers/media/platform/mx2_emmaprp.c
@@ -27,7 +27,7 @@
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
#include <media/videobuf2-dma-contig.h>
-#include <asm/sizes.h>
+#include <linux/sizes.h>
#define EMMAPRP_MODULE_NAME "mem2mem-emmaprp"
diff --git a/drivers/media/platform/omap/Kconfig b/drivers/media/platform/omap/Kconfig
index 37ad446b35b3..05de442d24e4 100644
--- a/drivers/media/platform/omap/Kconfig
+++ b/drivers/media/platform/omap/Kconfig
@@ -3,7 +3,7 @@ config VIDEO_OMAP2_VOUT_VRFB
config VIDEO_OMAP2_VOUT
tristate "OMAP2/OMAP3 V4L2-Display driver"
- depends on ARCH_OMAP2 || ARCH_OMAP3
+ depends on ARCH_OMAP2 || ARCH_OMAP3 || (COMPILE_TEST && HAS_MMU)
select VIDEOBUF_GEN
select VIDEOBUF_DMA_CONTIG
select OMAP2_DSS if HAS_IOMEM && ARCH_OMAP2PLUS
diff --git a/drivers/media/platform/omap/omap_vout.c b/drivers/media/platform/omap/omap_vout.c
index 2d177fa58471..64ab6fb06b9c 100644
--- a/drivers/media/platform/omap/omap_vout.c
+++ b/drivers/media/platform/omap/omap_vout.c
@@ -369,7 +369,7 @@ static int omapvid_setup_overlay(struct omap_vout_device *vout,
{
int ret = 0;
struct omap_overlay_info info;
- int cropheight, cropwidth, pixheight, pixwidth;
+ int cropheight, cropwidth, pixwidth;
if ((ovl->caps & OMAP_DSS_OVL_CAP_SCALE) == 0 &&
(outw != vout->pix.width || outh != vout->pix.height)) {
@@ -389,12 +389,10 @@ static int omapvid_setup_overlay(struct omap_vout_device *vout,
if (is_rotation_90_or_270(vout)) {
cropheight = vout->crop.width;
cropwidth = vout->crop.height;
- pixheight = vout->pix.width;
pixwidth = vout->pix.height;
} else {
cropheight = vout->crop.height;
cropwidth = vout->crop.width;
- pixheight = vout->pix.height;
pixwidth = vout->pix.width;
}
@@ -991,7 +989,7 @@ static int omap_vout_release(struct file *file)
mask = DISPC_IRQ_VSYNC | DISPC_IRQ_EVSYNC_EVEN |
DISPC_IRQ_EVSYNC_ODD | DISPC_IRQ_VSYNC2;
omap_dispc_unregister_isr(omap_vout_isr, vout, mask);
- vout->streaming = 0;
+ vout->streaming = false;
videobuf_streamoff(q);
videobuf_queue_cancel(q);
@@ -1451,12 +1449,10 @@ static int vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *a)
}
case V4L2_CID_VFLIP:
{
- struct omap_overlay *ovl;
struct omapvideo_info *ovid;
unsigned int mirror = a->value;
ovid = &vout->vid_info;
- ovl = ovid->overlays[0];
mutex_lock(&vout->lock);
if (mirror && ovid->rotation_type == VOUT_ROT_NONE) {
@@ -1489,7 +1485,7 @@ static int vidioc_reqbufs(struct file *file, void *fh,
struct omap_vout_device *vout = fh;
struct videobuf_queue *q = &vout->vbq;
- if ((req->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) || (req->count < 0))
+ if (req->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
return -EINVAL;
/* if memory is not mmp or userptr
return error */
@@ -1648,7 +1644,7 @@ static int vidioc_streamon(struct file *file, void *fh, enum v4l2_buf_type i)
vout->field_id = 0;
/* set flag here. Next QBUF will start DMA */
- vout->streaming = 1;
+ vout->streaming = true;
vout->first_int = 1;
@@ -1708,7 +1704,7 @@ static int vidioc_streamoff(struct file *file, void *fh, enum v4l2_buf_type i)
if (!vout->streaming)
return -EINVAL;
- vout->streaming = 0;
+ vout->streaming = false;
mask = DISPC_IRQ_VSYNC | DISPC_IRQ_EVSYNC_EVEN | DISPC_IRQ_EVSYNC_ODD
| DISPC_IRQ_VSYNC2;
@@ -1916,7 +1912,7 @@ static int __init omap_vout_setup_video_data(struct omap_vout_device *vout)
control[0].id = V4L2_CID_ROTATE;
control[0].value = 0;
vout->rotation = 0;
- vout->mirror = 0;
+ vout->mirror = false;
vout->control[2].id = V4L2_CID_HFLIP;
vout->control[2].value = 0;
if (vout->vid_info.rotation_type == VOUT_ROT_VRFB)
diff --git a/drivers/media/platform/omap/omap_vout_vrfb.c b/drivers/media/platform/omap/omap_vout_vrfb.c
index 62e7e5783ce8..aa39306afc73 100644
--- a/drivers/media/platform/omap/omap_vout_vrfb.c
+++ b/drivers/media/platform/omap/omap_vout_vrfb.c
@@ -148,7 +148,7 @@ int omap_vout_setup_vrfb_bufs(struct platform_device *pdev, int vid_num,
ret = -ENOMEM;
goto release_vrfb_ctx;
}
- vout->vrfb_static_allocation = 1;
+ vout->vrfb_static_allocation = true;
}
return 0;
@@ -336,7 +336,7 @@ void omap_vout_calculate_vrfb_offset(struct omap_vout_device *vout)
offset = vout->vrfb_context[0].yoffset *
vout->vrfb_context[0].bytespp;
temp_ps = ps / vr_ps;
- if (mirroring == 0) {
+ if (!mirroring) {
*cropped_offset = offset + line_length *
temp_ps * cleft + crop->top * temp_ps;
} else {
@@ -350,7 +350,7 @@ void omap_vout_calculate_vrfb_offset(struct omap_vout_device *vout)
vout->vrfb_context[0].bytespp) +
(vout->vrfb_context[0].xoffset *
vout->vrfb_context[0].bytespp));
- if (mirroring == 0) {
+ if (!mirroring) {
*cropped_offset = offset + (line_length * ps * ctop) +
(cleft / vr_ps) * ps;
@@ -364,7 +364,7 @@ void omap_vout_calculate_vrfb_offset(struct omap_vout_device *vout)
offset = MAX_PIXELS_PER_LINE * vout->vrfb_context[0].xoffset *
vout->vrfb_context[0].bytespp;
temp_ps = ps / vr_ps;
- if (mirroring == 0) {
+ if (!mirroring) {
*cropped_offset = offset + line_length *
temp_ps * crop->left + ctop * ps;
} else {
@@ -375,7 +375,7 @@ void omap_vout_calculate_vrfb_offset(struct omap_vout_device *vout)
}
break;
case dss_rotation_0_degree:
- if (mirroring == 0) {
+ if (!mirroring) {
*cropped_offset = (line_length * ps) *
crop->top + (crop->left / vr_ps) * ps;
} else {
diff --git a/drivers/media/platform/omap/omap_vout_vrfb.h b/drivers/media/platform/omap/omap_vout_vrfb.h
index ffde741e0590..4c2314839b48 100644
--- a/drivers/media/platform/omap/omap_vout_vrfb.h
+++ b/drivers/media/platform/omap/omap_vout_vrfb.h
@@ -23,18 +23,18 @@ int omap_vout_prepare_vrfb(struct omap_vout_device *vout,
struct videobuf_buffer *vb);
void omap_vout_calculate_vrfb_offset(struct omap_vout_device *vout);
#else
-void omap_vout_free_vrfb_buffers(struct omap_vout_device *vout) { }
-int omap_vout_setup_vrfb_bufs(struct platform_device *pdev, int vid_num,
+static inline void omap_vout_free_vrfb_buffers(struct omap_vout_device *vout) { };
+static inline int omap_vout_setup_vrfb_bufs(struct platform_device *pdev, int vid_num,
u32 static_vrfb_allocation)
- { return 0; }
-void omap_vout_release_vrfb(struct omap_vout_device *vout) { }
-int omap_vout_vrfb_buffer_setup(struct omap_vout_device *vout,
+ { return 0; };
+static inline void omap_vout_release_vrfb(struct omap_vout_device *vout) { };
+static inline int omap_vout_vrfb_buffer_setup(struct omap_vout_device *vout,
unsigned int *count, unsigned int startindex)
- { return 0; }
-int omap_vout_prepare_vrfb(struct omap_vout_device *vout,
+ { return 0; };
+static inline int omap_vout_prepare_vrfb(struct omap_vout_device *vout,
struct videobuf_buffer *vb)
- { return 0; }
-void omap_vout_calculate_vrfb_offset(struct omap_vout_device *vout) { }
+ { return 0; };
+static inline void omap_vout_calculate_vrfb_offset(struct omap_vout_device *vout) { };
#endif
#endif
diff --git a/drivers/media/platform/omap3isp/cfa_coef_table.h b/drivers/media/platform/omap3isp/cfa_coef_table.h
index c84df0706f3e..e75b0eb2519b 100644
--- a/drivers/media/platform/omap3isp/cfa_coef_table.h
+++ b/drivers/media/platform/omap3isp/cfa_coef_table.h
@@ -11,16 +11,6 @@
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
*/
{ 244, 0, 247, 0, 12, 27, 36, 247, 250, 0, 27, 0, 4, 250, 12, 244,
diff --git a/drivers/media/platform/omap3isp/gamma_table.h b/drivers/media/platform/omap3isp/gamma_table.h
index 78deebf7d965..3b507078016d 100644
--- a/drivers/media/platform/omap3isp/gamma_table.h
+++ b/drivers/media/platform/omap3isp/gamma_table.h
@@ -12,16 +12,6 @@
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
*/
0, 0, 1, 2, 3, 3, 4, 5, 6, 8, 10, 12, 14, 16, 18, 20,
diff --git a/drivers/media/platform/omap3isp/isp.c b/drivers/media/platform/omap3isp/isp.c
index 2c7aa6720569..72265e58ca60 100644
--- a/drivers/media/platform/omap3isp/isp.c
+++ b/drivers/media/platform/omap3isp/isp.c
@@ -40,16 +40,6 @@
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
*/
#include <asm/cacheflush.h>
@@ -999,16 +989,14 @@ static int isp_pipeline_disable(struct isp_pipeline *pipe)
video, s_stream, 0);
}
- v4l2_subdev_call(subdev, video, s_stream, 0);
+ ret = v4l2_subdev_call(subdev, video, s_stream, 0);
if (subdev == &isp->isp_res.subdev)
- ret = isp_pipeline_wait(isp, isp_pipeline_wait_resizer);
+ ret |= isp_pipeline_wait(isp, isp_pipeline_wait_resizer);
else if (subdev == &isp->isp_prev.subdev)
- ret = isp_pipeline_wait(isp, isp_pipeline_wait_preview);
+ ret |= isp_pipeline_wait(isp, isp_pipeline_wait_preview);
else if (subdev == &isp->isp_ccdc.subdev)
- ret = isp_pipeline_wait(isp, isp_pipeline_wait_ccdc);
- else
- ret = 0;
+ ret |= isp_pipeline_wait(isp, isp_pipeline_wait_ccdc);
/* Handle stop failures. An entity that fails to stop can
* usually just be restarted. Flag the stop failure nonetheless
diff --git a/drivers/media/platform/omap3isp/isp.h b/drivers/media/platform/omap3isp/isp.h
index 2c314eea1252..cfdfc8714b6b 100644
--- a/drivers/media/platform/omap3isp/isp.h
+++ b/drivers/media/platform/omap3isp/isp.h
@@ -12,16 +12,6 @@
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
*/
#ifndef OMAP3_ISP_CORE_H
diff --git a/drivers/media/platform/omap3isp/ispccdc.c b/drivers/media/platform/omap3isp/ispccdc.c
index 9f727d20f06d..81a9dc053d58 100644
--- a/drivers/media/platform/omap3isp/ispccdc.c
+++ b/drivers/media/platform/omap3isp/ispccdc.c
@@ -12,16 +12,6 @@
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
*/
#include <linux/module.h>
@@ -491,14 +481,13 @@ done:
static inline int ccdc_lsc_is_configured(struct isp_ccdc_device *ccdc)
{
unsigned long flags;
+ int ret;
spin_lock_irqsave(&ccdc->lsc.req_lock, flags);
- if (ccdc->lsc.active) {
- spin_unlock_irqrestore(&ccdc->lsc.req_lock, flags);
- return 1;
- }
+ ret = ccdc->lsc.active != NULL;
spin_unlock_irqrestore(&ccdc->lsc.req_lock, flags);
- return 0;
+
+ return ret;
}
static int ccdc_lsc_enable(struct isp_ccdc_device *ccdc)
@@ -818,29 +807,48 @@ static void ccdc_config_vp(struct isp_ccdc_device *ccdc)
struct isp_pipeline *pipe = to_isp_pipeline(&ccdc->subdev.entity);
struct isp_device *isp = to_isp_device(ccdc);
const struct isp_format_info *info;
+ struct v4l2_mbus_framefmt *format;
unsigned long l3_ick = pipe->l3_ick;
unsigned int max_div = isp->revision == ISP_REVISION_15_0 ? 64 : 8;
unsigned int div = 0;
- u32 fmtcfg_vp;
+ u32 fmtcfg = ISPCCDC_FMTCFG_VPEN;
+
+ format = &ccdc->formats[CCDC_PAD_SOURCE_VP];
+
+ if (!format->code) {
+ /* Disable the video port when the input format isn't supported.
+ * This is indicated by a pixel code set to 0.
+ */
+ isp_reg_writel(isp, 0, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMTCFG);
+ return;
+ }
+
+ isp_reg_writel(isp, (0 << ISPCCDC_FMT_HORZ_FMTSPH_SHIFT) |
+ (format->width << ISPCCDC_FMT_HORZ_FMTLNH_SHIFT),
+ OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMT_HORZ);
+ isp_reg_writel(isp, (0 << ISPCCDC_FMT_VERT_FMTSLV_SHIFT) |
+ ((format->height + 1) << ISPCCDC_FMT_VERT_FMTLNV_SHIFT),
+ OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMT_VERT);
- fmtcfg_vp = isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMTCFG)
- & ~(ISPCCDC_FMTCFG_VPIN_MASK | ISPCCDC_FMTCFG_VPIF_FRQ_MASK);
+ isp_reg_writel(isp, (format->width << ISPCCDC_VP_OUT_HORZ_NUM_SHIFT) |
+ (format->height << ISPCCDC_VP_OUT_VERT_NUM_SHIFT),
+ OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VP_OUT);
info = omap3isp_video_format_info(ccdc->formats[CCDC_PAD_SINK].code);
switch (info->width) {
case 8:
case 10:
- fmtcfg_vp |= ISPCCDC_FMTCFG_VPIN_9_0;
+ fmtcfg |= ISPCCDC_FMTCFG_VPIN_9_0;
break;
case 11:
- fmtcfg_vp |= ISPCCDC_FMTCFG_VPIN_10_1;
+ fmtcfg |= ISPCCDC_FMTCFG_VPIN_10_1;
break;
case 12:
- fmtcfg_vp |= ISPCCDC_FMTCFG_VPIN_11_2;
+ fmtcfg |= ISPCCDC_FMTCFG_VPIN_11_2;
break;
case 13:
- fmtcfg_vp |= ISPCCDC_FMTCFG_VPIN_12_3;
+ fmtcfg |= ISPCCDC_FMTCFG_VPIN_12_3;
break;
}
@@ -850,75 +858,59 @@ static void ccdc_config_vp(struct isp_ccdc_device *ccdc)
div = l3_ick / pipe->external_rate;
div = clamp(div, 2U, max_div);
- fmtcfg_vp |= (div - 2) << ISPCCDC_FMTCFG_VPIF_FRQ_SHIFT;
+ fmtcfg |= (div - 2) << ISPCCDC_FMTCFG_VPIF_FRQ_SHIFT;
- isp_reg_writel(isp, fmtcfg_vp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMTCFG);
-}
-
-/*
- * ccdc_enable_vp - Enable Video Port.
- * @ccdc: Pointer to ISP CCDC device.
- * @enable: 0 Disables VP, 1 Enables VP
- *
- * This is needed for outputting image to Preview, H3A and HIST ISP submodules.
- */
-static void ccdc_enable_vp(struct isp_ccdc_device *ccdc, u8 enable)
-{
- struct isp_device *isp = to_isp_device(ccdc);
-
- isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMTCFG,
- ISPCCDC_FMTCFG_VPEN, enable ? ISPCCDC_FMTCFG_VPEN : 0);
+ isp_reg_writel(isp, fmtcfg, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMTCFG);
}
/*
* ccdc_config_outlineoffset - Configure memory saving output line offset
* @ccdc: Pointer to ISP CCDC device.
- * @offset: Address offset to start a new line. Must be twice the
- * Output width and aligned on 32 byte boundary
- * @oddeven: Specifies the odd/even line pattern to be chosen to store the
- * output.
- * @numlines: Set the value 0-3 for +1-4lines, 4-7 for -1-4lines.
+ * @bpl: Number of bytes per line when stored in memory.
+ * @field: Field order when storing interlaced formats in memory.
+ *
+ * Configure the offsets for the line output control:
+ *
+ * - The horizontal line offset is defined as the number of bytes between the
+ * start of two consecutive lines in memory. Set it to the given bytes per
+ * line value.
+ *
+ * - The field offset value is defined as the number of lines to offset the
+ * start of the field identified by FID = 1. Set it to one.
*
- * - Configures the output line offset when stored in memory
- * - Sets the odd/even line pattern to store the output
- * (EVENEVEN (1), ODDEVEN (2), EVENODD (3), ODDODD (4))
- * - Configures the number of even and odd line fields in case of rearranging
- * the lines.
+ * - The line offset values are defined as the number of lines (as defined by
+ * the horizontal line offset) between the start of two consecutive lines for
+ * all combinations of odd/even lines in odd/even fields. When interleaving
+ * fields set them all to two lines, and to one line otherwise.
*/
static void ccdc_config_outlineoffset(struct isp_ccdc_device *ccdc,
- u32 offset, u8 oddeven, u8 numlines)
+ unsigned int bpl,
+ enum v4l2_field field)
{
struct isp_device *isp = to_isp_device(ccdc);
+ u32 sdofst = 0;
- isp_reg_writel(isp, offset & 0xffff,
- OMAP3_ISP_IOMEM_CCDC, ISPCCDC_HSIZE_OFF);
+ isp_reg_writel(isp, bpl & 0xffff, OMAP3_ISP_IOMEM_CCDC,
+ ISPCCDC_HSIZE_OFF);
- isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST,
- ISPCCDC_SDOFST_FINV);
-
- isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST,
- ISPCCDC_SDOFST_FOFST_4L);
-
- switch (oddeven) {
- case EVENEVEN:
- isp_reg_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST,
- (numlines & 0x7) << ISPCCDC_SDOFST_LOFST0_SHIFT);
- break;
- case ODDEVEN:
- isp_reg_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST,
- (numlines & 0x7) << ISPCCDC_SDOFST_LOFST1_SHIFT);
- break;
- case EVENODD:
- isp_reg_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST,
- (numlines & 0x7) << ISPCCDC_SDOFST_LOFST2_SHIFT);
- break;
- case ODDODD:
- isp_reg_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST,
- (numlines & 0x7) << ISPCCDC_SDOFST_LOFST3_SHIFT);
+ switch (field) {
+ case V4L2_FIELD_INTERLACED_TB:
+ case V4L2_FIELD_INTERLACED_BT:
+ /* When interleaving fields in memory offset field one by one
+ * line and set the line offset to two lines.
+ */
+ sdofst |= (1 << ISPCCDC_SDOFST_LOFST0_SHIFT)
+ | (1 << ISPCCDC_SDOFST_LOFST1_SHIFT)
+ | (1 << ISPCCDC_SDOFST_LOFST2_SHIFT)
+ | (1 << ISPCCDC_SDOFST_LOFST3_SHIFT);
break;
+
default:
+ /* In all other cases set the line offsets to one line. */
break;
}
+
+ isp_reg_writel(isp, sdofst, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST);
}
/*
@@ -981,10 +973,16 @@ static void ccdc_config_sync_if(struct isp_ccdc_device *ccdc,
if (format->code == V4L2_MBUS_FMT_YUYV8_2X8 ||
format->code == V4L2_MBUS_FMT_UYVY8_2X8) {
- /* The bridge is enabled for YUV8 formats. Configure the input
- * mode accordingly.
+ /* According to the OMAP3 TRM the input mode only affects SYNC
+ * mode, enabling BT.656 mode should take precedence. However,
+ * in practice setting the input mode to YCbCr data on 8 bits
+ * seems to be required in BT.656 mode. In SYNC mode set it to
+ * YCbCr on 16 bits as the bridge is enabled in that case.
*/
- syn_mode |= ISPCCDC_SYN_MODE_INPMOD_YCBCR16;
+ if (ccdc->bt656)
+ syn_mode |= ISPCCDC_SYN_MODE_INPMOD_YCBCR8;
+ else
+ syn_mode |= ISPCCDC_SYN_MODE_INPMOD_YCBCR16;
}
switch (data_size) {
@@ -1008,9 +1006,15 @@ static void ccdc_config_sync_if(struct isp_ccdc_device *ccdc,
if (pdata && pdata->hs_pol)
syn_mode |= ISPCCDC_SYN_MODE_HDPOL;
- if (pdata && pdata->vs_pol)
+ /* The polarity of the vertical sync signal output by the BT.656
+ * decoder is not documented and seems to be active low.
+ */
+ if ((pdata && pdata->vs_pol) || ccdc->bt656)
syn_mode |= ISPCCDC_SYN_MODE_VDPOL;
+ if (pdata && pdata->fld_pol)
+ syn_mode |= ISPCCDC_SYN_MODE_FLDPOL;
+
isp_reg_writel(isp, syn_mode, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SYN_MODE);
/* The CCDC_CFG.Y8POS bit is used in YCbCr8 input mode only. The
@@ -1023,8 +1027,16 @@ static void ccdc_config_sync_if(struct isp_ccdc_device *ccdc,
isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CFG,
ISPCCDC_CFG_Y8POS);
- isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_REC656IF,
- ISPCCDC_REC656IF_R656ON);
+ /* Enable or disable BT.656 mode, including error correction for the
+ * synchronization codes.
+ */
+ if (ccdc->bt656)
+ isp_reg_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_REC656IF,
+ ISPCCDC_REC656IF_R656ON | ISPCCDC_REC656IF_ECCFVH);
+ else
+ isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_REC656IF,
+ ISPCCDC_REC656IF_R656ON | ISPCCDC_REC656IF_ECCFVH);
+
}
/* CCDC formats descriptions */
@@ -1115,17 +1127,33 @@ static void ccdc_configure(struct isp_ccdc_device *ccdc)
unsigned long flags;
unsigned int bridge;
unsigned int shift;
+ unsigned int nph;
+ unsigned int sph;
u32 syn_mode;
u32 ccdc_pattern;
+ ccdc->bt656 = false;
+ ccdc->fields = 0;
+
pad = media_entity_remote_pad(&ccdc->pads[CCDC_PAD_SINK]);
sensor = media_entity_to_v4l2_subdev(pad->entity);
- if (ccdc->input == CCDC_INPUT_PARALLEL)
+ if (ccdc->input == CCDC_INPUT_PARALLEL) {
+ struct v4l2_mbus_config cfg;
+ int ret;
+
+ ret = v4l2_subdev_call(sensor, video, g_mbus_config, &cfg);
+ if (!ret)
+ ccdc->bt656 = cfg.type == V4L2_MBUS_BT656;
+
pdata = &((struct isp_v4l2_subdevs_group *)sensor->host_priv)
->bus.parallel;
+ }
+
+ /* CCDC_PAD_SINK */
+ format = &ccdc->formats[CCDC_PAD_SINK];
/* Compute the lane shifter shift value and enable the bridge when the
- * input format is YUV.
+ * input format is a non-BT.656 YUV variant.
*/
fmt_src.pad = pad->index;
fmt_src.which = V4L2_SUBDEV_FORMAT_ACTIVE;
@@ -1134,12 +1162,13 @@ static void ccdc_configure(struct isp_ccdc_device *ccdc)
depth_in = fmt_info->width;
}
- fmt_info = omap3isp_video_format_info
- (isp->isp_ccdc.formats[CCDC_PAD_SINK].code);
+ fmt_info = omap3isp_video_format_info(format->code);
depth_out = fmt_info->width;
shift = depth_in - depth_out;
- if (fmt_info->code == V4L2_MBUS_FMT_YUYV8_2X8)
+ if (ccdc->bt656)
+ bridge = ISPCTRL_PAR_BRIDGE_DISABLE;
+ else if (fmt_info->code == V4L2_MBUS_FMT_YUYV8_2X8)
bridge = ISPCTRL_PAR_BRIDGE_LENDIAN;
else if (fmt_info->code == V4L2_MBUS_FMT_UYVY8_2X8)
bridge = ISPCTRL_PAR_BRIDGE_BENDIAN;
@@ -1148,6 +1177,7 @@ static void ccdc_configure(struct isp_ccdc_device *ccdc)
omap3isp_configure_bridge(isp, ccdc->input, pdata, shift, bridge);
+ /* Configure the sync interface. */
ccdc_config_sync_if(ccdc, pdata, depth_out);
syn_mode = isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SYN_MODE);
@@ -1167,9 +1197,6 @@ static void ccdc_configure(struct isp_ccdc_device *ccdc)
else
syn_mode &= ~ISPCCDC_SYN_MODE_SDR2RSZ;
- /* CCDC_PAD_SINK */
- format = &ccdc->formats[CCDC_PAD_SINK];
-
/* Mosaic filter */
switch (format->code) {
case V4L2_MBUS_FMT_SRGGB10_1X10:
@@ -1202,16 +1229,40 @@ static void ccdc_configure(struct isp_ccdc_device *ccdc)
format = &ccdc->formats[CCDC_PAD_SOURCE_OF];
crop = &ccdc->crop;
- isp_reg_writel(isp, (crop->left << ISPCCDC_HORZ_INFO_SPH_SHIFT) |
- ((crop->width - 1) << ISPCCDC_HORZ_INFO_NPH_SHIFT),
+ /* The horizontal coordinates are expressed in pixel clock cycles. We
+ * need two cycles per pixel in BT.656 mode, and one cycle per pixel in
+ * SYNC mode regardless of the format as the bridge is enabled for YUV
+ * formats in that case.
+ */
+ if (ccdc->bt656) {
+ sph = crop->left * 2;
+ nph = crop->width * 2 - 1;
+ } else {
+ sph = crop->left;
+ nph = crop->width - 1;
+ }
+
+ isp_reg_writel(isp, (sph << ISPCCDC_HORZ_INFO_SPH_SHIFT) |
+ (nph << ISPCCDC_HORZ_INFO_NPH_SHIFT),
OMAP3_ISP_IOMEM_CCDC, ISPCCDC_HORZ_INFO);
- isp_reg_writel(isp, crop->top << ISPCCDC_VERT_START_SLV0_SHIFT,
+ isp_reg_writel(isp, (crop->top << ISPCCDC_VERT_START_SLV0_SHIFT) |
+ (crop->top << ISPCCDC_VERT_START_SLV1_SHIFT),
OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VERT_START);
isp_reg_writel(isp, (crop->height - 1)
<< ISPCCDC_VERT_LINES_NLV_SHIFT,
OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VERT_LINES);
- ccdc_config_outlineoffset(ccdc, ccdc->video_out.bpl_value, 0, 0);
+ ccdc_config_outlineoffset(ccdc, ccdc->video_out.bpl_value,
+ format->field);
+
+ /* When interleaving fields enable processing of the field input signal.
+ * This will cause the line output control module to apply the field
+ * offset to field 1.
+ */
+ if (ccdc->formats[CCDC_PAD_SINK].field == V4L2_FIELD_ALTERNATE &&
+ (format->field == V4L2_FIELD_INTERLACED_TB ||
+ format->field == V4L2_FIELD_INTERLACED_BT))
+ syn_mode |= ISPCCDC_SYN_MODE_FLDMODE;
/* The CCDC outputs data in UYVY order by default. Swap bytes to get
* YUYV.
@@ -1223,8 +1274,11 @@ static void ccdc_configure(struct isp_ccdc_device *ccdc)
isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CFG,
ISPCCDC_CFG_BSWD);
- /* Use PACK8 mode for 1byte per pixel formats. */
- if (omap3isp_video_format_info(format->code)->width <= 8)
+ /* Use PACK8 mode for 1byte per pixel formats. Check for BT.656 mode
+ * explicitly as the driver reports 1X16 instead of 2X8 at the OF pad
+ * for simplicity.
+ */
+ if (omap3isp_video_format_info(format->code)->width <= 8 || ccdc->bt656)
syn_mode |= ISPCCDC_SYN_MODE_PACK8;
else
syn_mode &= ~ISPCCDC_SYN_MODE_PACK8;
@@ -1232,18 +1286,7 @@ static void ccdc_configure(struct isp_ccdc_device *ccdc)
isp_reg_writel(isp, syn_mode, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SYN_MODE);
/* CCDC_PAD_SOURCE_VP */
- format = &ccdc->formats[CCDC_PAD_SOURCE_VP];
-
- isp_reg_writel(isp, (0 << ISPCCDC_FMT_HORZ_FMTSPH_SHIFT) |
- (format->width << ISPCCDC_FMT_HORZ_FMTLNH_SHIFT),
- OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMT_HORZ);
- isp_reg_writel(isp, (0 << ISPCCDC_FMT_VERT_FMTSLV_SHIFT) |
- ((format->height + 1) << ISPCCDC_FMT_VERT_FMTLNV_SHIFT),
- OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMT_VERT);
-
- isp_reg_writel(isp, (format->width << ISPCCDC_VP_OUT_HORZ_NUM_SHIFT) |
- (format->height << ISPCCDC_VP_OUT_VERT_NUM_SHIFT),
- OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VP_OUT);
+ ccdc_config_vp(ccdc);
/* Lens shading correction. */
spin_lock_irqsave(&ccdc->lsc.req_lock, flags);
@@ -1277,6 +1320,8 @@ static void __ccdc_enable(struct isp_ccdc_device *ccdc, int enable)
isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_PCR,
ISPCCDC_PCR_EN, enable ? ISPCCDC_PCR_EN : 0);
+
+ ccdc->running = enable;
}
static int ccdc_disable(struct isp_ccdc_device *ccdc)
@@ -1287,6 +1332,8 @@ static int ccdc_disable(struct isp_ccdc_device *ccdc)
spin_lock_irqsave(&ccdc->lock, flags);
if (ccdc->state == ISP_PIPELINE_STREAM_CONTINUOUS)
ccdc->stopping = CCDC_STOP_REQUEST;
+ if (!ccdc->running)
+ ccdc->stopping = CCDC_STOP_FINISHED;
spin_unlock_irqrestore(&ccdc->lock, flags);
ret = wait_event_timeout(ccdc->wait,
@@ -1369,14 +1416,14 @@ static int ccdc_sbl_wait_idle(struct isp_ccdc_device *ccdc,
return -EBUSY;
}
-/* __ccdc_handle_stopping - Handle CCDC and/or LSC stopping sequence
+/* ccdc_handle_stopping - Handle CCDC and/or LSC stopping sequence
* @ccdc: Pointer to ISP CCDC device.
* @event: Pointing which event trigger handler
*
* Return 1 when the event and stopping request combination is satisfied,
* zero otherwise.
*/
-static int __ccdc_handle_stopping(struct isp_ccdc_device *ccdc, u32 event)
+static int ccdc_handle_stopping(struct isp_ccdc_device *ccdc, u32 event)
{
int rval = 0;
@@ -1458,7 +1505,7 @@ static void ccdc_lsc_isr(struct isp_ccdc_device *ccdc, u32 events)
if (ccdc->lsc.state == LSC_STATE_STOPPING)
ccdc->lsc.state = LSC_STATE_STOPPED;
- if (__ccdc_handle_stopping(ccdc, CCDC_EVENT_LSC_DONE))
+ if (ccdc_handle_stopping(ccdc, CCDC_EVENT_LSC_DONE))
goto done;
if (ccdc->lsc.state != LSC_STATE_RECONFIG)
@@ -1486,12 +1533,59 @@ done:
spin_unlock_irqrestore(&ccdc->lsc.req_lock, flags);
}
+/*
+ * Check whether the CCDC has captured all fields necessary to complete the
+ * buffer.
+ */
+static bool ccdc_has_all_fields(struct isp_ccdc_device *ccdc)
+{
+ struct isp_pipeline *pipe = to_isp_pipeline(&ccdc->subdev.entity);
+ struct isp_device *isp = to_isp_device(ccdc);
+ enum v4l2_field of_field = ccdc->formats[CCDC_PAD_SOURCE_OF].field;
+ enum v4l2_field field;
+
+ /* When the input is progressive fields don't matter. */
+ if (of_field == V4L2_FIELD_NONE)
+ return true;
+
+ /* Read the current field identifier. */
+ field = isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SYN_MODE)
+ & ISPCCDC_SYN_MODE_FLDSTAT
+ ? V4L2_FIELD_BOTTOM : V4L2_FIELD_TOP;
+
+ /* When capturing fields in alternate order just store the current field
+ * identifier in the pipeline.
+ */
+ if (of_field == V4L2_FIELD_ALTERNATE) {
+ pipe->field = field;
+ return true;
+ }
+
+ /* The format is interlaced. Make sure we've captured both fields. */
+ ccdc->fields |= field == V4L2_FIELD_BOTTOM
+ ? CCDC_FIELD_BOTTOM : CCDC_FIELD_TOP;
+
+ if (ccdc->fields != CCDC_FIELD_BOTH)
+ return false;
+
+ /* Verify that the field just captured corresponds to the last field
+ * needed based on the desired field order.
+ */
+ if ((of_field == V4L2_FIELD_INTERLACED_TB && field == V4L2_FIELD_TOP) ||
+ (of_field == V4L2_FIELD_INTERLACED_BT && field == V4L2_FIELD_BOTTOM))
+ return false;
+
+ /* The buffer can be completed, reset the fields for the next buffer. */
+ ccdc->fields = 0;
+
+ return true;
+}
+
static int ccdc_isr_buffer(struct isp_ccdc_device *ccdc)
{
struct isp_pipeline *pipe = to_isp_pipeline(&ccdc->subdev.entity);
struct isp_device *isp = to_isp_device(ccdc);
struct isp_buffer *buffer;
- int restart = 0;
/* The CCDC generates VD0 interrupts even when disabled (the datasheet
* doesn't explicitly state if that's supposed to happen or not, so it
@@ -1500,30 +1594,31 @@ static int ccdc_isr_buffer(struct isp_ccdc_device *ccdc)
* would thus not be enough, we need to handle the situation explicitly.
*/
if (list_empty(&ccdc->video_out.dmaqueue))
- goto done;
+ return 0;
/* We're in continuous mode, and memory writes were disabled due to a
* buffer underrun. Reenable them now that we have a buffer. The buffer
* address has been set in ccdc_video_queue.
*/
if (ccdc->state == ISP_PIPELINE_STREAM_CONTINUOUS && ccdc->underrun) {
- restart = 1;
ccdc->underrun = 0;
- goto done;
+ return 1;
}
+ /* Wait for the CCDC to become idle. */
if (ccdc_sbl_wait_idle(ccdc, 1000)) {
dev_info(isp->dev, "CCDC won't become idle!\n");
isp->crashed |= 1U << ccdc->subdev.entity.id;
omap3isp_pipeline_cancel_stream(pipe);
- goto done;
+ return 0;
}
+ if (!ccdc_has_all_fields(ccdc))
+ return 1;
+
buffer = omap3isp_video_buffer_next(&ccdc->video_out);
- if (buffer != NULL) {
+ if (buffer != NULL)
ccdc_set_outaddr(ccdc, buffer->dma);
- restart = 1;
- }
pipe->state |= ISP_PIPELINE_IDLE_OUTPUT;
@@ -1532,8 +1627,7 @@ static int ccdc_isr_buffer(struct isp_ccdc_device *ccdc)
omap3isp_pipeline_set_stream(pipe,
ISP_PIPELINE_STREAM_SINGLESHOT);
-done:
- return restart;
+ return buffer != NULL;
}
/*
@@ -1547,11 +1641,38 @@ static void ccdc_vd0_isr(struct isp_ccdc_device *ccdc)
unsigned long flags;
int restart = 0;
+ /* In BT.656 mode the CCDC doesn't generate an HS/VS interrupt. We thus
+ * need to increment the frame counter here.
+ */
+ if (ccdc->bt656) {
+ struct isp_pipeline *pipe =
+ to_isp_pipeline(&ccdc->subdev.entity);
+
+ atomic_inc(&pipe->frame_number);
+ }
+
+ /* Emulate a VD1 interrupt for BT.656 mode, as we can't stop the CCDC in
+ * the VD1 interrupt handler in that mode without risking a CCDC stall
+ * if a short frame is received.
+ */
+ if (ccdc->bt656) {
+ spin_lock_irqsave(&ccdc->lock, flags);
+ if (ccdc->state == ISP_PIPELINE_STREAM_CONTINUOUS &&
+ ccdc->output & CCDC_OUTPUT_MEMORY) {
+ if (ccdc->lsc.state != LSC_STATE_STOPPED)
+ __ccdc_lsc_enable(ccdc, 0);
+ __ccdc_enable(ccdc, 0);
+ }
+ ccdc_handle_stopping(ccdc, CCDC_EVENT_VD1);
+ spin_unlock_irqrestore(&ccdc->lock, flags);
+ }
+
if (ccdc->output & CCDC_OUTPUT_MEMORY)
restart = ccdc_isr_buffer(ccdc);
spin_lock_irqsave(&ccdc->lock, flags);
- if (__ccdc_handle_stopping(ccdc, CCDC_EVENT_VD0)) {
+
+ if (ccdc_handle_stopping(ccdc, CCDC_EVENT_VD0)) {
spin_unlock_irqrestore(&ccdc->lock, flags);
return;
}
@@ -1572,6 +1693,18 @@ static void ccdc_vd1_isr(struct isp_ccdc_device *ccdc)
{
unsigned long flags;
+ /* In BT.656 mode the synchronization signals are generated by the CCDC
+ * from the embedded sync codes. The VD0 and VD1 interrupts are thus
+ * only triggered when the CCDC is enabled, unlike external sync mode
+ * where the line counter runs even when the CCDC is stopped. We can't
+ * disable the CCDC at VD1 time, as no VD0 interrupt would be generated
+ * for a short frame, which would result in the CCDC being stopped and
+ * no VD interrupt generated anymore. The CCDC is stopped from the VD0
+ * interrupt handler instead for BT.656.
+ */
+ if (ccdc->bt656)
+ return;
+
spin_lock_irqsave(&ccdc->lsc.req_lock, flags);
/*
@@ -1601,7 +1734,7 @@ static void ccdc_vd1_isr(struct isp_ccdc_device *ccdc)
break;
}
- if (__ccdc_handle_stopping(ccdc, CCDC_EVENT_VD1))
+ if (ccdc_handle_stopping(ccdc, CCDC_EVENT_VD1))
goto done;
if (ccdc->lsc.request == NULL)
@@ -1656,6 +1789,8 @@ int omap3isp_ccdc_isr(struct isp_ccdc_device *ccdc, u32 events)
static int ccdc_video_queue(struct isp_video *video, struct isp_buffer *buffer)
{
struct isp_ccdc_device *ccdc = &video->isp->isp_ccdc;
+ unsigned long flags;
+ bool restart = false;
if (!(ccdc->output & CCDC_OUTPUT_MEMORY))
return -ENODEV;
@@ -1664,9 +1799,20 @@ static int ccdc_video_queue(struct isp_video *video, struct isp_buffer *buffer)
/* We now have a buffer queued on the output, restart the pipeline
* on the next CCDC interrupt if running in continuous mode (or when
- * starting the stream).
+ * starting the stream) in external sync mode, or immediately in BT.656
+ * sync mode as no CCDC interrupt is generated when the CCDC is stopped
+ * in that case.
*/
- ccdc->underrun = 1;
+ spin_lock_irqsave(&ccdc->lock, flags);
+ if (ccdc->state == ISP_PIPELINE_STREAM_CONTINUOUS && !ccdc->running &&
+ ccdc->bt656)
+ restart = true;
+ else
+ ccdc->underrun = 1;
+ spin_unlock_irqrestore(&ccdc->lock, flags);
+
+ if (restart)
+ ccdc_enable(ccdc);
return 0;
}
@@ -1753,11 +1899,6 @@ static int ccdc_set_stream(struct v4l2_subdev *sd, int enable)
ccdc_configure(ccdc);
- /* TODO: Don't configure the video port if all of its output
- * links are inactive.
- */
- ccdc_config_vp(ccdc);
- ccdc_enable_vp(ccdc, 1);
ccdc_print_status(ccdc);
}
@@ -1830,6 +1971,7 @@ ccdc_try_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh,
unsigned int width = fmt->width;
unsigned int height = fmt->height;
struct v4l2_rect *crop;
+ enum v4l2_field field;
unsigned int i;
switch (pad) {
@@ -1846,14 +1988,24 @@ ccdc_try_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh,
/* Clamp the input size. */
fmt->width = clamp_t(u32, width, 32, 4096);
fmt->height = clamp_t(u32, height, 32, 4096);
+
+ /* Default to progressive field order. */
+ if (fmt->field == V4L2_FIELD_ANY)
+ fmt->field = V4L2_FIELD_NONE;
+
break;
case CCDC_PAD_SOURCE_OF:
pixelcode = fmt->code;
+ field = fmt->field;
*fmt = *__ccdc_get_format(ccdc, fh, CCDC_PAD_SINK, which);
- /* YUV formats are converted from 2X8 to 1X16 by the bridge and
- * can be byte-swapped.
+ /* In SYNC mode the bridge converts YUV formats from 2X8 to
+ * 1X16. In BT.656 no such conversion occurs. As we don't know
+ * at this point whether the source will use SYNC or BT.656 mode
+ * let's pretend the conversion always occurs. The CCDC will be
+ * configured to pack bytes in BT.656, hiding the inaccuracy.
+ * In all cases bytes can be swapped.
*/
if (fmt->code == V4L2_MBUS_FMT_YUYV8_2X8 ||
fmt->code == V4L2_MBUS_FMT_UYVY8_2X8) {
@@ -1874,6 +2026,17 @@ ccdc_try_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh,
crop = __ccdc_get_crop(ccdc, fh, which);
fmt->width = crop->width;
fmt->height = crop->height;
+
+ /* When input format is interlaced with alternating fields the
+ * CCDC can interleave the fields.
+ */
+ if (fmt->field == V4L2_FIELD_ALTERNATE &&
+ (field == V4L2_FIELD_INTERLACED_TB ||
+ field == V4L2_FIELD_INTERLACED_BT)) {
+ fmt->field = field;
+ fmt->height *= 2;
+ }
+
break;
case CCDC_PAD_SOURCE_VP:
@@ -1901,7 +2064,6 @@ ccdc_try_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh,
* stored on 2 bytes.
*/
fmt->colorspace = V4L2_COLORSPACE_SRGB;
- fmt->field = V4L2_FIELD_NONE;
}
/*
diff --git a/drivers/media/platform/omap3isp/ispccdc.h b/drivers/media/platform/omap3isp/ispccdc.h
index f65061602c71..3440a7097940 100644
--- a/drivers/media/platform/omap3isp/ispccdc.h
+++ b/drivers/media/platform/omap3isp/ispccdc.h
@@ -12,16 +12,6 @@
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
*/
#ifndef OMAP3_ISP_CCDC_H
@@ -103,6 +93,10 @@ struct ispccdc_lsc {
#define CCDC_PAD_SOURCE_VP 2
#define CCDC_PADS_NUM 3
+#define CCDC_FIELD_TOP 1
+#define CCDC_FIELD_BOTTOM 2
+#define CCDC_FIELD_BOTH 3
+
/*
* struct isp_ccdc_device - Structure for the CCDC module to store its own
* information
@@ -123,11 +117,14 @@ struct ispccdc_lsc {
* @lsc: Lens shading compensation configuration
* @update: Bitmask of controls to update during the next interrupt
* @shadow_update: Controls update in progress by userspace
+ * @bt656: Whether the input interface uses BT.656 synchronization
+ * @fields: The fields (CCDC_FIELD_*) stored in the current buffer
* @underrun: A buffer underrun occurred and a new buffer has been queued
* @state: Streaming state
* @lock: Serializes shadow_update with interrupt handler
* @wait: Wait queue used to stop the module
* @stopping: Stopping state
+ * @running: Is the CCDC hardware running
* @ioctl_lock: Serializes ioctl calls and LSC requests freeing
*/
struct isp_ccdc_device {
@@ -151,11 +148,15 @@ struct isp_ccdc_device {
unsigned int update;
unsigned int shadow_update;
+ bool bt656;
+ unsigned int fields;
+
unsigned int underrun:1;
enum isp_pipeline_stream_state state;
spinlock_t lock;
wait_queue_head_t wait;
unsigned int stopping;
+ bool running;
struct mutex ioctl_lock;
};
diff --git a/drivers/media/platform/omap3isp/ispccp2.c b/drivers/media/platform/omap3isp/ispccp2.c
index f3801db9095c..9cb49b3c04bd 100644
--- a/drivers/media/platform/omap3isp/ispccp2.c
+++ b/drivers/media/platform/omap3isp/ispccp2.c
@@ -12,16 +12,6 @@
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
*/
#include <linux/delay.h>
diff --git a/drivers/media/platform/omap3isp/ispccp2.h b/drivers/media/platform/omap3isp/ispccp2.h
index 76d65f4576ef..4662bffa79e3 100644
--- a/drivers/media/platform/omap3isp/ispccp2.h
+++ b/drivers/media/platform/omap3isp/ispccp2.h
@@ -12,16 +12,6 @@
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
*/
#ifndef OMAP3_ISP_CCP2_H
diff --git a/drivers/media/platform/omap3isp/ispcsi2.c b/drivers/media/platform/omap3isp/ispcsi2.c
index 5a2e47e58b84..6530b255f103 100644
--- a/drivers/media/platform/omap3isp/ispcsi2.c
+++ b/drivers/media/platform/omap3isp/ispcsi2.c
@@ -12,16 +12,6 @@
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
*/
#include <linux/delay.h>
#include <media/v4l2-common.h>
diff --git a/drivers/media/platform/omap3isp/ispcsi2.h b/drivers/media/platform/omap3isp/ispcsi2.h
index c57729b7e86e..453ed62fe394 100644
--- a/drivers/media/platform/omap3isp/ispcsi2.h
+++ b/drivers/media/platform/omap3isp/ispcsi2.h
@@ -12,16 +12,6 @@
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
*/
#ifndef OMAP3_ISP_CSI2_H
diff --git a/drivers/media/platform/omap3isp/ispcsiphy.c b/drivers/media/platform/omap3isp/ispcsiphy.c
index c09de32f986a..e033f2237a72 100644
--- a/drivers/media/platform/omap3isp/ispcsiphy.c
+++ b/drivers/media/platform/omap3isp/ispcsiphy.c
@@ -12,16 +12,6 @@
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
*/
#include <linux/delay.h>
diff --git a/drivers/media/platform/omap3isp/ispcsiphy.h b/drivers/media/platform/omap3isp/ispcsiphy.h
index 14551fd77697..e17c88beab92 100644
--- a/drivers/media/platform/omap3isp/ispcsiphy.h
+++ b/drivers/media/platform/omap3isp/ispcsiphy.h
@@ -12,16 +12,6 @@
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
*/
#ifndef OMAP3_ISP_CSI_PHY_H
diff --git a/drivers/media/platform/omap3isp/isph3a.h b/drivers/media/platform/omap3isp/isph3a.h
index fb09fd4ca755..e5b28d0f3b0f 100644
--- a/drivers/media/platform/omap3isp/isph3a.h
+++ b/drivers/media/platform/omap3isp/isph3a.h
@@ -13,16 +13,6 @@
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
*/
#ifndef OMAP3_ISP_H3A_H
diff --git a/drivers/media/platform/omap3isp/isph3a_aewb.c b/drivers/media/platform/omap3isp/isph3a_aewb.c
index d6811ce263eb..b208c5417146 100644
--- a/drivers/media/platform/omap3isp/isph3a_aewb.c
+++ b/drivers/media/platform/omap3isp/isph3a_aewb.c
@@ -13,16 +13,6 @@
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
*/
#include <linux/slab.h>
diff --git a/drivers/media/platform/omap3isp/isph3a_af.c b/drivers/media/platform/omap3isp/isph3a_af.c
index 6fc960cd30f5..8a83e195f3e3 100644
--- a/drivers/media/platform/omap3isp/isph3a_af.c
+++ b/drivers/media/platform/omap3isp/isph3a_af.c
@@ -13,16 +13,6 @@
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
*/
/* Linux specific include files */
diff --git a/drivers/media/platform/omap3isp/isphist.c b/drivers/media/platform/omap3isp/isphist.c
index 06a5f8164eaa..ce822c34c843 100644
--- a/drivers/media/platform/omap3isp/isphist.c
+++ b/drivers/media/platform/omap3isp/isphist.c
@@ -13,16 +13,6 @@
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
*/
#include <linux/delay.h>
diff --git a/drivers/media/platform/omap3isp/isphist.h b/drivers/media/platform/omap3isp/isphist.h
index 0b2a38ec94c4..3b5415517dcd 100644
--- a/drivers/media/platform/omap3isp/isphist.h
+++ b/drivers/media/platform/omap3isp/isphist.h
@@ -13,16 +13,6 @@
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
*/
#ifndef OMAP3_ISP_HIST_H
diff --git a/drivers/media/platform/omap3isp/isppreview.c b/drivers/media/platform/omap3isp/isppreview.c
index 720809b07e75..605f57ef0a49 100644
--- a/drivers/media/platform/omap3isp/isppreview.c
+++ b/drivers/media/platform/omap3isp/isppreview.c
@@ -12,16 +12,6 @@
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
*/
#include <linux/device.h>
diff --git a/drivers/media/platform/omap3isp/isppreview.h b/drivers/media/platform/omap3isp/isppreview.h
index f66923407f8c..16fdc03a3d43 100644
--- a/drivers/media/platform/omap3isp/isppreview.h
+++ b/drivers/media/platform/omap3isp/isppreview.h
@@ -12,16 +12,6 @@
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
*/
#ifndef OMAP3_ISP_PREVIEW_H
diff --git a/drivers/media/platform/omap3isp/ispreg.h b/drivers/media/platform/omap3isp/ispreg.h
index b7d90e6fb01d..b5ea8da0b904 100644
--- a/drivers/media/platform/omap3isp/ispreg.h
+++ b/drivers/media/platform/omap3isp/ispreg.h
@@ -12,16 +12,6 @@
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
*/
#ifndef OMAP3_ISP_REG_H
@@ -740,17 +730,13 @@
#define ISPCCDC_HSIZE_OFF_SHIFT 0
-#define ISPCCDC_SDOFST_FINV (1 << 14)
-#define ISPCCDC_SDOFST_FOFST_1L 0
-#define ISPCCDC_SDOFST_FOFST_4L (3 << 12)
+#define ISPCCDC_SDOFST_FIINV (1 << 14)
+#define ISPCCDC_SDOFST_FOFST_SHIFT 12
+#define ISPCCDC_SDOFST_FOFST_MASK (3 << 12)
#define ISPCCDC_SDOFST_LOFST3_SHIFT 0
#define ISPCCDC_SDOFST_LOFST2_SHIFT 3
#define ISPCCDC_SDOFST_LOFST1_SHIFT 6
#define ISPCCDC_SDOFST_LOFST0_SHIFT 9
-#define EVENEVEN 1
-#define ODDEVEN 2
-#define EVENODD 3
-#define ODDODD 4
#define ISPCCDC_CLAMP_OBGAIN_SHIFT 0
#define ISPCCDC_CLAMP_OBST_SHIFT 10
diff --git a/drivers/media/platform/omap3isp/ispresizer.c b/drivers/media/platform/omap3isp/ispresizer.c
index 6f077c2377db..05d1ace57451 100644
--- a/drivers/media/platform/omap3isp/ispresizer.c
+++ b/drivers/media/platform/omap3isp/ispresizer.c
@@ -12,16 +12,6 @@
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
*/
#include <linux/device.h>
@@ -239,7 +229,7 @@ static void resizer_set_phase(struct isp_res_device *res, u32 h_phase,
u32 v_phase)
{
struct isp_device *isp = to_isp_device(res);
- u32 rgval = 0;
+ u32 rgval;
rgval = isp_reg_readl(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT) &
~(ISPRSZ_CNT_HSTPH_MASK | ISPRSZ_CNT_VSTPH_MASK);
@@ -275,7 +265,7 @@ static void resizer_set_luma(struct isp_res_device *res,
struct resizer_luma_yenh *luma)
{
struct isp_device *isp = to_isp_device(res);
- u32 rgval = 0;
+ u32 rgval;
rgval = (luma->algo << ISPRSZ_YENH_ALGO_SHIFT)
& ISPRSZ_YENH_ALGO_MASK;
@@ -322,7 +312,7 @@ static void resizer_set_ratio(struct isp_res_device *res,
{
struct isp_device *isp = to_isp_device(res);
const u16 *h_filter, *v_filter;
- u32 rgval = 0;
+ u32 rgval;
rgval = isp_reg_readl(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT) &
~(ISPRSZ_CNT_HRSZ_MASK | ISPRSZ_CNT_VRSZ_MASK);
@@ -365,9 +355,8 @@ static void resizer_set_output_size(struct isp_res_device *res,
u32 width, u32 height)
{
struct isp_device *isp = to_isp_device(res);
- u32 rgval = 0;
+ u32 rgval;
- dev_dbg(isp->dev, "Output size[w/h]: %dx%d\n", width, height);
rgval = (width << ISPRSZ_OUT_SIZE_HORZ_SHIFT)
& ISPRSZ_OUT_SIZE_HORZ_MASK;
rgval |= (height << ISPRSZ_OUT_SIZE_VERT_SHIFT)
@@ -409,7 +398,7 @@ static void resizer_set_output_offset(struct isp_res_device *res, u32 offset)
static void resizer_set_start(struct isp_res_device *res, u32 left, u32 top)
{
struct isp_device *isp = to_isp_device(res);
- u32 rgval = 0;
+ u32 rgval;
rgval = (left << ISPRSZ_IN_START_HORZ_ST_SHIFT)
& ISPRSZ_IN_START_HORZ_ST_MASK;
@@ -429,9 +418,7 @@ static void resizer_set_input_size(struct isp_res_device *res,
u32 width, u32 height)
{
struct isp_device *isp = to_isp_device(res);
- u32 rgval = 0;
-
- dev_dbg(isp->dev, "Input size[w/h]: %dx%d\n", width, height);
+ u32 rgval;
rgval = (width << ISPRSZ_IN_SIZE_HORZ_SHIFT)
& ISPRSZ_IN_SIZE_HORZ_MASK;
@@ -1075,10 +1062,13 @@ static void resizer_isr_buffer(struct isp_res_device *res)
void omap3isp_resizer_isr(struct isp_res_device *res)
{
struct v4l2_mbus_framefmt *informat, *outformat;
+ unsigned long flags;
if (omap3isp_module_sync_is_stopping(&res->wait, &res->stopping))
return;
+ spin_lock_irqsave(&res->lock, flags);
+
if (res->applycrop) {
outformat = __resizer_get_format(res, NULL, RESZ_PAD_SOURCE,
V4L2_SUBDEV_FORMAT_ACTIVE);
@@ -1088,6 +1078,8 @@ void omap3isp_resizer_isr(struct isp_res_device *res)
res->applycrop = 0;
}
+ spin_unlock_irqrestore(&res->lock, flags);
+
resizer_isr_buffer(res);
}
@@ -1290,8 +1282,10 @@ static int resizer_set_selection(struct v4l2_subdev *sd,
{
struct isp_res_device *res = v4l2_get_subdevdata(sd);
struct isp_device *isp = to_isp_device(res);
- struct v4l2_mbus_framefmt *format_sink, *format_source;
+ const struct v4l2_mbus_framefmt *format_sink;
+ struct v4l2_mbus_framefmt format_source;
struct resizer_ratio ratio;
+ unsigned long flags;
if (sel->target != V4L2_SEL_TGT_CROP ||
sel->pad != RESZ_PAD_SINK)
@@ -1299,16 +1293,14 @@ static int resizer_set_selection(struct v4l2_subdev *sd,
format_sink = __resizer_get_format(res, fh, RESZ_PAD_SINK,
sel->which);
- format_source = __resizer_get_format(res, fh, RESZ_PAD_SOURCE,
- sel->which);
-
- dev_dbg(isp->dev, "%s: L=%d,T=%d,W=%d,H=%d,which=%d\n", __func__,
- sel->r.left, sel->r.top, sel->r.width, sel->r.height,
- sel->which);
+ format_source = *__resizer_get_format(res, fh, RESZ_PAD_SOURCE,
+ sel->which);
- dev_dbg(isp->dev, "%s: input=%dx%d, output=%dx%d\n", __func__,
+ dev_dbg(isp->dev, "%s(%s): req %ux%u -> (%d,%d)/%ux%u -> %ux%u\n",
+ __func__, sel->which == V4L2_SUBDEV_FORMAT_TRY ? "try" : "act",
format_sink->width, format_sink->height,
- format_source->width, format_source->height);
+ sel->r.left, sel->r.top, sel->r.width, sel->r.height,
+ format_source.width, format_source.height);
/* Clamp the crop rectangle to the bounds, and then mangle it further to
* fulfill the TRM equations. Store the clamped but otherwise unmangled
@@ -1318,23 +1310,39 @@ static int resizer_set_selection(struct v4l2_subdev *sd,
* smaller input crop rectangle every time the output size is set if we
* stored the mangled rectangle.
*/
- resizer_try_crop(format_sink, format_source, &sel->r);
+ resizer_try_crop(format_sink, &format_source, &sel->r);
*__resizer_get_crop(res, fh, sel->which) = sel->r;
- resizer_calc_ratios(res, &sel->r, format_source, &ratio);
+ resizer_calc_ratios(res, &sel->r, &format_source, &ratio);
- if (sel->which == V4L2_SUBDEV_FORMAT_TRY)
+ dev_dbg(isp->dev, "%s(%s): got %ux%u -> (%d,%d)/%ux%u -> %ux%u\n",
+ __func__, sel->which == V4L2_SUBDEV_FORMAT_TRY ? "try" : "act",
+ format_sink->width, format_sink->height,
+ sel->r.left, sel->r.top, sel->r.width, sel->r.height,
+ format_source.width, format_source.height);
+
+ if (sel->which == V4L2_SUBDEV_FORMAT_TRY) {
+ *__resizer_get_format(res, fh, RESZ_PAD_SOURCE, sel->which) =
+ format_source;
return 0;
+ }
+
+ /* Update the source format, resizing ratios and crop rectangle. If
+ * streaming is on the IRQ handler will reprogram the resizer after the
+ * current frame. We thus we need to protect against race conditions.
+ */
+ spin_lock_irqsave(&res->lock, flags);
+
+ *__resizer_get_format(res, fh, RESZ_PAD_SOURCE, sel->which) =
+ format_source;
res->ratio = ratio;
res->crop.active = sel->r;
- /*
- * set_selection can be called while streaming is on. In this case the
- * crop values will be set in the next IRQ.
- */
if (res->state != ISP_PIPELINE_STREAM_STOPPED)
res->applycrop = 1;
+ spin_unlock_irqrestore(&res->lock, flags);
+
return 0;
}
@@ -1781,6 +1789,8 @@ int omap3isp_resizer_init(struct isp_device *isp)
init_waitqueue_head(&res->wait);
atomic_set(&res->stopping, 0);
+ spin_lock_init(&res->lock);
+
return resizer_init_entities(res);
}
diff --git a/drivers/media/platform/omap3isp/ispresizer.h b/drivers/media/platform/omap3isp/ispresizer.h
index 9b01e9047c15..5414542912e2 100644
--- a/drivers/media/platform/omap3isp/ispresizer.h
+++ b/drivers/media/platform/omap3isp/ispresizer.h
@@ -12,21 +12,12 @@
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
*/
#ifndef OMAP3_ISP_RESIZER_H
#define OMAP3_ISP_RESIZER_H
+#include <linux/spinlock.h>
#include <linux/types.h>
/*
@@ -96,6 +87,7 @@ enum resizer_input_entity {
/*
* struct isp_res_device - OMAP3 ISP resizer module
+ * @lock: Protects formats and crop rectangles between set_selection and IRQ
* @crop.request: Crop rectangle requested by the user
* @crop.active: Active crop rectangle (based on hardware requirements)
*/
@@ -116,6 +108,7 @@ struct isp_res_device {
enum isp_pipeline_stream_state state;
wait_queue_head_t wait;
atomic_t stopping;
+ spinlock_t lock;
struct {
struct v4l2_rect request;
diff --git a/drivers/media/platform/omap3isp/ispstat.c b/drivers/media/platform/omap3isp/ispstat.c
index e6cbc1eaf4ca..a94e8340508f 100644
--- a/drivers/media/platform/omap3isp/ispstat.c
+++ b/drivers/media/platform/omap3isp/ispstat.c
@@ -13,16 +13,6 @@
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
*/
#include <linux/dma-mapping.h>
diff --git a/drivers/media/platform/omap3isp/ispstat.h b/drivers/media/platform/omap3isp/ispstat.h
index 58d6ac7cb664..b32b29677e2c 100644
--- a/drivers/media/platform/omap3isp/ispstat.h
+++ b/drivers/media/platform/omap3isp/ispstat.h
@@ -13,16 +13,6 @@
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
*/
#ifndef OMAP3_ISP_STAT_H
diff --git a/drivers/media/platform/omap3isp/ispvideo.c b/drivers/media/platform/omap3isp/ispvideo.c
index e36bac26476c..bc38c88c7bd9 100644
--- a/drivers/media/platform/omap3isp/ispvideo.c
+++ b/drivers/media/platform/omap3isp/ispvideo.c
@@ -11,16 +11,6 @@
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
*/
#include <asm/cacheflush.h>
@@ -319,10 +309,11 @@ isp_video_check_format(struct isp_video *video, struct isp_video_fh *vfh)
vfh->format.fmt.pix.height != format.fmt.pix.height ||
vfh->format.fmt.pix.width != format.fmt.pix.width ||
vfh->format.fmt.pix.bytesperline != format.fmt.pix.bytesperline ||
- vfh->format.fmt.pix.sizeimage != format.fmt.pix.sizeimage)
+ vfh->format.fmt.pix.sizeimage != format.fmt.pix.sizeimage ||
+ vfh->format.fmt.pix.field != format.fmt.pix.field)
return -EINVAL;
- return ret;
+ return 0;
}
/* -----------------------------------------------------------------------------
@@ -491,6 +482,11 @@ struct isp_buffer *omap3isp_video_buffer_next(struct isp_video *video)
else
buf->vb.v4l2_buf.sequence = atomic_read(&pipe->frame_number);
+ if (pipe->field != V4L2_FIELD_NONE)
+ buf->vb.v4l2_buf.sequence /= 2;
+
+ buf->vb.v4l2_buf.field = pipe->field;
+
/* Report pipeline errors to userspace on the capture device side. */
if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && pipe->error) {
state = VB2_BUF_STATE_ERROR;
@@ -641,7 +637,40 @@ isp_video_set_format(struct file *file, void *fh, struct v4l2_format *format)
if (format->type != video->type)
return -EINVAL;
- mutex_lock(&video->mutex);
+ /* Replace unsupported field orders with sane defaults. */
+ switch (format->fmt.pix.field) {
+ case V4L2_FIELD_NONE:
+ /* Progressive is supported everywhere. */
+ break;
+ case V4L2_FIELD_ALTERNATE:
+ /* ALTERNATE is not supported on output nodes. */
+ if (video->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
+ format->fmt.pix.field = V4L2_FIELD_NONE;
+ break;
+ case V4L2_FIELD_INTERLACED:
+ /* The ISP has no concept of video standard, select the
+ * top-bottom order when the unqualified interlaced order is
+ * requested.
+ */
+ format->fmt.pix.field = V4L2_FIELD_INTERLACED_TB;
+ /* Fall-through */
+ case V4L2_FIELD_INTERLACED_TB:
+ case V4L2_FIELD_INTERLACED_BT:
+ /* Interlaced orders are only supported at the CCDC output. */
+ if (video != &video->isp->isp_ccdc.video_out)
+ format->fmt.pix.field = V4L2_FIELD_NONE;
+ break;
+ case V4L2_FIELD_TOP:
+ case V4L2_FIELD_BOTTOM:
+ case V4L2_FIELD_SEQ_TB:
+ case V4L2_FIELD_SEQ_BT:
+ default:
+ /* All other field orders are currently unsupported, default to
+ * progressive.
+ */
+ format->fmt.pix.field = V4L2_FIELD_NONE;
+ break;
+ }
/* Fill the bytesperline and sizeimage fields by converting to media bus
* format and back to pixel format.
@@ -649,9 +678,10 @@ isp_video_set_format(struct file *file, void *fh, struct v4l2_format *format)
isp_video_pix_to_mbus(&format->fmt.pix, &fmt);
isp_video_mbus_to_pix(video, &fmt, &format->fmt.pix);
+ mutex_lock(&video->mutex);
vfh->format = *format;
-
mutex_unlock(&video->mutex);
+
return 0;
}
@@ -1039,6 +1069,7 @@ isp_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type)
video->queue = &vfh->queue;
INIT_LIST_HEAD(&video->dmaqueue);
atomic_set(&pipe->frame_number, -1);
+ pipe->field = vfh->format.fmt.pix.field;
mutex_lock(&video->queue_lock);
ret = vb2_streamon(&vfh->queue, type);
diff --git a/drivers/media/platform/omap3isp/ispvideo.h b/drivers/media/platform/omap3isp/ispvideo.h
index 7d2e82122ecd..0b7efedc3da9 100644
--- a/drivers/media/platform/omap3isp/ispvideo.h
+++ b/drivers/media/platform/omap3isp/ispvideo.h
@@ -11,16 +11,6 @@
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
*/
#ifndef OMAP3_ISP_VIDEO_H
@@ -88,6 +78,7 @@ enum isp_pipeline_state {
/*
* struct isp_pipeline - An ISP hardware pipeline
+ * @field: The field being processed by the pipeline
* @error: A hardware error occurred during capture
* @entities: Bitmask of entities in the pipeline (indexed by entity ID)
*/
@@ -101,6 +92,7 @@ struct isp_pipeline {
u32 entities;
unsigned long l3_ick;
unsigned int max_rate;
+ enum v4l2_field field;
atomic_t frame_number;
bool do_propagation; /* of frame number */
bool error;
diff --git a/drivers/media/platform/omap3isp/luma_enhance_table.h b/drivers/media/platform/omap3isp/luma_enhance_table.h
index 098b45e2280f..81c5b1566469 100644
--- a/drivers/media/platform/omap3isp/luma_enhance_table.h
+++ b/drivers/media/platform/omap3isp/luma_enhance_table.h
@@ -12,16 +12,6 @@
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
*/
1047552, 1047552, 1047552, 1047552, 1047552, 1047552, 1047552, 1047552,
diff --git a/drivers/media/platform/omap3isp/noise_filter_table.h b/drivers/media/platform/omap3isp/noise_filter_table.h
index d50451a4a242..5073f9847937 100644
--- a/drivers/media/platform/omap3isp/noise_filter_table.h
+++ b/drivers/media/platform/omap3isp/noise_filter_table.h
@@ -12,16 +12,6 @@
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
*/
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
diff --git a/drivers/media/platform/s3c-camif/camif-capture.c b/drivers/media/platform/s3c-camif/camif-capture.c
index f33641384e15..4f81b4c9d113 100644
--- a/drivers/media/platform/s3c-camif/camif-capture.c
+++ b/drivers/media/platform/s3c-camif/camif-capture.c
@@ -280,8 +280,8 @@ static int camif_prepare_addr(struct camif_vp *vp, struct vb2_buffer *vb,
return -EINVAL;
}
- pr_debug("DMA address: y: %#x cb: %#x cr: %#x\n",
- paddr->y, paddr->cb, paddr->cr);
+ pr_debug("DMA address: y: %pad cb: %pad cr: %pad\n",
+ &paddr->y, &paddr->cb, &paddr->cr);
return 0;
}
diff --git a/drivers/media/platform/s3c-camif/camif-regs.c b/drivers/media/platform/s3c-camif/camif-regs.c
index ebf5b184cce4..6e0c9988a191 100644
--- a/drivers/media/platform/s3c-camif/camif-regs.c
+++ b/drivers/media/platform/s3c-camif/camif-regs.c
@@ -214,8 +214,8 @@ void camif_hw_set_output_addr(struct camif_vp *vp,
paddr->cr);
}
- pr_debug("dst_buf[%d]: %#X, cb: %#X, cr: %#X\n",
- i, paddr->y, paddr->cb, paddr->cr);
+ pr_debug("dst_buf[%d]: %pad, cb: %pad, cr: %pad\n",
+ i, &paddr->y, &paddr->cb, &paddr->cr);
}
static void camif_hw_set_out_dma_size(struct camif_vp *vp)
diff --git a/drivers/media/platform/s5p-g2d/g2d.c b/drivers/media/platform/s5p-g2d/g2d.c
index 357af1ebaeda..d79e214ce8ce 100644
--- a/drivers/media/platform/s5p-g2d/g2d.c
+++ b/drivers/media/platform/s5p-g2d/g2d.c
@@ -490,14 +490,13 @@ static void job_abort(void *prv)
{
struct g2d_ctx *ctx = prv;
struct g2d_dev *dev = ctx->dev;
- int ret;
if (dev->curr == NULL) /* No job currently running */
return;
- ret = wait_event_timeout(dev->irq_queue,
- dev->curr == NULL,
- msecs_to_jiffies(G2D_TIMEOUT));
+ wait_event_timeout(dev->irq_queue,
+ dev->curr == NULL,
+ msecs_to_jiffies(G2D_TIMEOUT));
}
static void device_run(void *prv)
diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.c b/drivers/media/platform/s5p-jpeg/jpeg-core.c
index e66acbc2a82d..e525a7c8d885 100644
--- a/drivers/media/platform/s5p-jpeg/jpeg-core.c
+++ b/drivers/media/platform/s5p-jpeg/jpeg-core.c
@@ -729,7 +729,7 @@ static inline void exynos4_jpeg_set_qtbl_chr(void __iomem *regs, int quality)
ARRAY_SIZE(qtbl_chrominance[quality]));
}
-void exynos4_jpeg_set_huff_tbl(void __iomem *base)
+static void exynos4_jpeg_set_huff_tbl(void __iomem *base)
{
exynos4_jpeg_set_tbl(base, hdctbl0, EXYNOS4_HUFF_TBL_HDCLL,
ARRAY_SIZE(hdctbl0));
diff --git a/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos3250.c b/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos3250.c
index d26e1f846553..e8c2cad93962 100644
--- a/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos3250.c
+++ b/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos3250.c
@@ -233,6 +233,7 @@ void exynos3250_jpeg_set_x(void __iomem *regs, unsigned int x)
writel(reg, regs + EXYNOS3250_JPGX);
}
+#if 0 /* Currently unused */
unsigned int exynos3250_jpeg_get_y(void __iomem *regs)
{
return readl(regs + EXYNOS3250_JPGY);
@@ -242,6 +243,7 @@ unsigned int exynos3250_jpeg_get_x(void __iomem *regs)
{
return readl(regs + EXYNOS3250_JPGX);
}
+#endif
void exynos3250_jpeg_interrupts_enable(void __iomem *regs)
{
diff --git a/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos4.c b/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos4.c
index da8d6a1a984f..ab6d6f43c96f 100644
--- a/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos4.c
+++ b/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos4.c
@@ -23,7 +23,7 @@ void exynos4_jpeg_sw_reset(void __iomem *base)
reg = readl(base + EXYNOS4_JPEG_CNTL_REG);
writel(reg & ~EXYNOS4_SOFT_RESET_HI, base + EXYNOS4_JPEG_CNTL_REG);
- ndelay(100000);
+ udelay(100);
writel(reg | EXYNOS4_SOFT_RESET_HI, base + EXYNOS4_JPEG_CNTL_REG);
}
@@ -151,9 +151,6 @@ void exynos4_jpeg_set_enc_out_fmt(void __iomem *base, unsigned int out_fmt)
void exynos4_jpeg_set_interrupt(void __iomem *base)
{
- unsigned int reg;
-
- reg = readl(base + EXYNOS4_INT_EN_REG) & ~EXYNOS4_INT_EN_MASK;
writel(EXYNOS4_INT_EN_ALL, base + EXYNOS4_INT_EN_REG);
}
@@ -185,7 +182,7 @@ void exynos4_jpeg_set_huf_table_enable(void __iomem *base, int value)
writel(reg | EXYNOS4_HUF_TBL_EN,
base + EXYNOS4_JPEG_CNTL_REG);
else
- writel(reg | ~EXYNOS4_HUF_TBL_EN,
+ writel(reg & ~EXYNOS4_HUF_TBL_EN,
base + EXYNOS4_JPEG_CNTL_REG);
}
@@ -196,9 +193,9 @@ void exynos4_jpeg_set_sys_int_enable(void __iomem *base, int value)
reg = readl(base + EXYNOS4_JPEG_CNTL_REG) & ~(EXYNOS4_SYS_INT_EN);
if (value == 1)
- writel(EXYNOS4_SYS_INT_EN, base + EXYNOS4_JPEG_CNTL_REG);
+ writel(reg | EXYNOS4_SYS_INT_EN, base + EXYNOS4_JPEG_CNTL_REG);
else
- writel(~EXYNOS4_SYS_INT_EN, base + EXYNOS4_JPEG_CNTL_REG);
+ writel(reg & ~EXYNOS4_SYS_INT_EN, base + EXYNOS4_JPEG_CNTL_REG);
}
void exynos4_jpeg_set_stream_buf_address(void __iomem *base,
diff --git a/drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.c b/drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.c
index 52407d790726..e3b8e67e005f 100644
--- a/drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.c
+++ b/drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.c
@@ -324,11 +324,9 @@ int s5p_jpeg_stream_stat_ok(void __iomem *regs)
void s5p_jpeg_clear_int(void __iomem *regs)
{
- unsigned long reg;
-
- reg = readl(regs + S5P_JPGINTST);
+ readl(regs + S5P_JPGINTST);
writel(S5P_INT_RELEASE, regs + S5P_JPGCOM);
- reg = readl(regs + S5P_JPGOPR);
+ readl(regs + S5P_JPGOPR);
}
unsigned int s5p_jpeg_compressed_size(void __iomem *regs)
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c
index d35b0418ab37..165bc86c5962 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c
@@ -37,8 +37,8 @@
#define S5P_MFC_DEC_NAME "s5p-mfc-dec"
#define S5P_MFC_ENC_NAME "s5p-mfc-enc"
-int debug;
-module_param(debug, int, S_IRUGO | S_IWUSR);
+int mfc_debug_level;
+module_param_named(debug, mfc_debug_level, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(debug, "Debug level - higher value produces more verbose messages");
/* Helper functions for interrupt processing */
@@ -150,10 +150,10 @@ static void s5p_mfc_watchdog_worker(struct work_struct *work)
if (!ctx)
continue;
ctx->state = MFCINST_ERROR;
- s5p_mfc_hw_call(dev->mfc_ops, cleanup_queue, &ctx->dst_queue,
- &ctx->vq_dst);
- s5p_mfc_hw_call(dev->mfc_ops, cleanup_queue, &ctx->src_queue,
- &ctx->vq_src);
+ s5p_mfc_hw_call_void(dev->mfc_ops, cleanup_queue,
+ &ctx->dst_queue, &ctx->vq_dst);
+ s5p_mfc_hw_call_void(dev->mfc_ops, cleanup_queue,
+ &ctx->src_queue, &ctx->vq_src);
clear_work_bit(ctx);
wake_up_ctx(ctx, S5P_MFC_R2H_CMD_ERR_RET, 0);
}
@@ -264,7 +264,12 @@ static void s5p_mfc_handle_frame_new(struct s5p_mfc_ctx *ctx, unsigned int err)
unsigned int frame_type;
dspl_y_addr = s5p_mfc_hw_call(dev->mfc_ops, get_dspl_y_adr, dev);
- frame_type = s5p_mfc_hw_call(dev->mfc_ops, get_disp_frame_type, ctx);
+ if (IS_MFCV6_PLUS(dev))
+ frame_type = s5p_mfc_hw_call(dev->mfc_ops,
+ get_disp_frame_type, ctx);
+ else
+ frame_type = s5p_mfc_hw_call(dev->mfc_ops,
+ get_dec_frame_type, dev);
/* If frame is same as previous then skip and do not dequeue */
if (frame_type == S5P_FIMV_DECODE_FRAME_SKIPPED) {
@@ -327,12 +332,12 @@ static void s5p_mfc_handle_frame(struct s5p_mfc_ctx *ctx,
if (res_change == S5P_FIMV_RES_INCREASE ||
res_change == S5P_FIMV_RES_DECREASE) {
ctx->state = MFCINST_RES_CHANGE_INIT;
- s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev);
+ s5p_mfc_hw_call_void(dev->mfc_ops, clear_int_flags, dev);
wake_up_ctx(ctx, reason, err);
if (test_and_clear_bit(0, &dev->hw_lock) == 0)
BUG();
s5p_mfc_clock_off();
- s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
+ s5p_mfc_hw_call_void(dev->mfc_ops, try_run, dev);
return;
}
if (ctx->dpb_flush_flag)
@@ -400,7 +405,7 @@ leave_handle_frame:
if ((ctx->src_queue_cnt == 0 && ctx->state != MFCINST_FINISHING)
|| ctx->dst_queue_cnt < ctx->pb_count)
clear_work_bit(ctx);
- s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev);
+ s5p_mfc_hw_call_void(dev->mfc_ops, clear_int_flags, dev);
wake_up_ctx(ctx, reason, err);
if (test_and_clear_bit(0, &dev->hw_lock) == 0)
BUG();
@@ -409,7 +414,7 @@ leave_handle_frame:
if (test_bit(0, &dev->enter_suspend))
wake_up_dev(dev, reason, err);
else
- s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
+ s5p_mfc_hw_call_void(dev->mfc_ops, try_run, dev);
}
/* Error handling for interrupt */
@@ -435,10 +440,10 @@ static void s5p_mfc_handle_error(struct s5p_mfc_dev *dev,
ctx->state = MFCINST_ERROR;
/* Mark all dst buffers as having an error */
spin_lock_irqsave(&dev->irqlock, flags);
- s5p_mfc_hw_call(dev->mfc_ops, cleanup_queue,
+ s5p_mfc_hw_call_void(dev->mfc_ops, cleanup_queue,
&ctx->dst_queue, &ctx->vq_dst);
/* Mark all src buffers as having an error */
- s5p_mfc_hw_call(dev->mfc_ops, cleanup_queue,
+ s5p_mfc_hw_call_void(dev->mfc_ops, cleanup_queue,
&ctx->src_queue, &ctx->vq_src);
spin_unlock_irqrestore(&dev->irqlock, flags);
wake_up_ctx(ctx, reason, err);
@@ -452,7 +457,7 @@ static void s5p_mfc_handle_error(struct s5p_mfc_dev *dev,
}
if (test_and_clear_bit(0, &dev->hw_lock) == 0)
BUG();
- s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev);
+ s5p_mfc_hw_call_void(dev->mfc_ops, clear_int_flags, dev);
s5p_mfc_clock_off();
wake_up_dev(dev, reason, err);
return;
@@ -476,7 +481,7 @@ static void s5p_mfc_handle_seq_done(struct s5p_mfc_ctx *ctx,
ctx->img_height = s5p_mfc_hw_call(dev->mfc_ops, get_img_height,
dev);
- s5p_mfc_hw_call(dev->mfc_ops, dec_calc_dpb_size, ctx);
+ s5p_mfc_hw_call_void(dev->mfc_ops, dec_calc_dpb_size, ctx);
ctx->pb_count = s5p_mfc_hw_call(dev->mfc_ops, get_dpb_count,
dev);
@@ -503,12 +508,12 @@ static void s5p_mfc_handle_seq_done(struct s5p_mfc_ctx *ctx,
ctx->head_processed = 1;
}
}
- s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev);
+ s5p_mfc_hw_call_void(dev->mfc_ops, clear_int_flags, dev);
clear_work_bit(ctx);
if (test_and_clear_bit(0, &dev->hw_lock) == 0)
BUG();
s5p_mfc_clock_off();
- s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
+ s5p_mfc_hw_call_void(dev->mfc_ops, try_run, dev);
wake_up_ctx(ctx, reason, err);
}
@@ -523,7 +528,7 @@ static void s5p_mfc_handle_init_buffers(struct s5p_mfc_ctx *ctx,
if (ctx == NULL)
return;
dev = ctx->dev;
- s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev);
+ s5p_mfc_hw_call_void(dev->mfc_ops, clear_int_flags, dev);
ctx->int_type = reason;
ctx->int_err = err;
ctx->int_cond = 1;
@@ -550,7 +555,7 @@ static void s5p_mfc_handle_init_buffers(struct s5p_mfc_ctx *ctx,
s5p_mfc_clock_off();
wake_up(&ctx->queue);
- s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
+ s5p_mfc_hw_call_void(dev->mfc_ops, try_run, dev);
} else {
if (test_and_clear_bit(0, &dev->hw_lock) == 0)
BUG();
@@ -591,7 +596,7 @@ static void s5p_mfc_handle_stream_complete(struct s5p_mfc_ctx *ctx,
s5p_mfc_clock_off();
wake_up(&ctx->queue);
- s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
+ s5p_mfc_hw_call_void(dev->mfc_ops, try_run, dev);
}
/* Interrupt processing */
@@ -628,12 +633,12 @@ static irqreturn_t s5p_mfc_irq(int irq, void *priv)
if (ctx->c_ops->post_frame_start) {
if (ctx->c_ops->post_frame_start(ctx))
mfc_err("post_frame_start() failed\n");
- s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev);
+ s5p_mfc_hw_call_void(dev->mfc_ops, clear_int_flags, dev);
wake_up_ctx(ctx, reason, err);
if (test_and_clear_bit(0, &dev->hw_lock) == 0)
BUG();
s5p_mfc_clock_off();
- s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
+ s5p_mfc_hw_call_void(dev->mfc_ops, try_run, dev);
} else {
s5p_mfc_handle_frame(ctx, reason, err);
}
@@ -663,7 +668,7 @@ static irqreturn_t s5p_mfc_irq(int irq, void *priv)
case S5P_MFC_R2H_CMD_WAKEUP_RET:
if (ctx)
clear_work_bit(ctx);
- s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev);
+ s5p_mfc_hw_call_void(dev->mfc_ops, clear_int_flags, dev);
wake_up_dev(dev, reason, err);
clear_bit(0, &dev->hw_lock);
clear_bit(0, &dev->enter_suspend);
@@ -685,12 +690,12 @@ static irqreturn_t s5p_mfc_irq(int irq, void *priv)
default:
mfc_debug(2, "Unknown int reason\n");
- s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev);
+ s5p_mfc_hw_call_void(dev->mfc_ops, clear_int_flags, dev);
}
mfc_debug_leave();
return IRQ_HANDLED;
irq_cleanup_hw:
- s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev);
+ s5p_mfc_hw_call_void(dev->mfc_ops, clear_int_flags, dev);
ctx->int_type = reason;
ctx->int_err = err;
ctx->int_cond = 1;
@@ -699,7 +704,7 @@ irq_cleanup_hw:
s5p_mfc_clock_off();
- s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
+ s5p_mfc_hw_call_void(dev->mfc_ops, try_run, dev);
mfc_debug(2, "Exit via irq_cleanup_hw\n");
return IRQ_HANDLED;
}
@@ -1311,11 +1316,9 @@ static int s5p_mfc_runtime_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct s5p_mfc_dev *m_dev = platform_get_drvdata(pdev);
- int pre_power;
if (!m_dev->alloc_ctx)
return 0;
- pre_power = atomic_read(&m_dev->pm.power);
atomic_set(&m_dev->pm.power, 1);
return 0;
}
@@ -1328,20 +1331,20 @@ static const struct dev_pm_ops s5p_mfc_pm_ops = {
NULL)
};
-struct s5p_mfc_buf_size_v5 mfc_buf_size_v5 = {
+static struct s5p_mfc_buf_size_v5 mfc_buf_size_v5 = {
.h264_ctx = MFC_H264_CTX_BUF_SIZE,
.non_h264_ctx = MFC_CTX_BUF_SIZE,
.dsc = DESC_BUF_SIZE,
.shm = SHARED_BUF_SIZE,
};
-struct s5p_mfc_buf_size buf_size_v5 = {
+static struct s5p_mfc_buf_size buf_size_v5 = {
.fw = MAX_FW_SIZE,
.cpb = MAX_CPB_SIZE,
.priv = &mfc_buf_size_v5,
};
-struct s5p_mfc_buf_align mfc_buf_align_v5 = {
+static struct s5p_mfc_buf_align mfc_buf_align_v5 = {
.base = MFC_BASE_ALIGN_ORDER,
};
@@ -1354,7 +1357,7 @@ static struct s5p_mfc_variant mfc_drvdata_v5 = {
.fw_name[0] = "s5p-mfc.fw",
};
-struct s5p_mfc_buf_size_v6 mfc_buf_size_v6 = {
+static struct s5p_mfc_buf_size_v6 mfc_buf_size_v6 = {
.dev_ctx = MFC_CTX_BUF_SIZE_V6,
.h264_dec_ctx = MFC_H264_DEC_CTX_BUF_SIZE_V6,
.other_dec_ctx = MFC_OTHER_DEC_CTX_BUF_SIZE_V6,
@@ -1362,13 +1365,13 @@ struct s5p_mfc_buf_size_v6 mfc_buf_size_v6 = {
.other_enc_ctx = MFC_OTHER_ENC_CTX_BUF_SIZE_V6,
};
-struct s5p_mfc_buf_size buf_size_v6 = {
+static struct s5p_mfc_buf_size buf_size_v6 = {
.fw = MAX_FW_SIZE_V6,
.cpb = MAX_CPB_SIZE_V6,
.priv = &mfc_buf_size_v6,
};
-struct s5p_mfc_buf_align mfc_buf_align_v6 = {
+static struct s5p_mfc_buf_align mfc_buf_align_v6 = {
.base = 0,
};
@@ -1386,7 +1389,7 @@ static struct s5p_mfc_variant mfc_drvdata_v6 = {
.fw_name[1] = "s5p-mfc-v6-v2.fw",
};
-struct s5p_mfc_buf_size_v6 mfc_buf_size_v7 = {
+static struct s5p_mfc_buf_size_v6 mfc_buf_size_v7 = {
.dev_ctx = MFC_CTX_BUF_SIZE_V7,
.h264_dec_ctx = MFC_H264_DEC_CTX_BUF_SIZE_V7,
.other_dec_ctx = MFC_OTHER_DEC_CTX_BUF_SIZE_V7,
@@ -1394,13 +1397,13 @@ struct s5p_mfc_buf_size_v6 mfc_buf_size_v7 = {
.other_enc_ctx = MFC_OTHER_ENC_CTX_BUF_SIZE_V7,
};
-struct s5p_mfc_buf_size buf_size_v7 = {
+static struct s5p_mfc_buf_size buf_size_v7 = {
.fw = MAX_FW_SIZE_V7,
.cpb = MAX_CPB_SIZE_V7,
.priv = &mfc_buf_size_v7,
};
-struct s5p_mfc_buf_align mfc_buf_align_v7 = {
+static struct s5p_mfc_buf_align mfc_buf_align_v7 = {
.base = 0,
};
@@ -1413,7 +1416,7 @@ static struct s5p_mfc_variant mfc_drvdata_v7 = {
.fw_name[0] = "s5p-mfc-v7.fw",
};
-struct s5p_mfc_buf_size_v6 mfc_buf_size_v8 = {
+static struct s5p_mfc_buf_size_v6 mfc_buf_size_v8 = {
.dev_ctx = MFC_CTX_BUF_SIZE_V8,
.h264_dec_ctx = MFC_H264_DEC_CTX_BUF_SIZE_V8,
.other_dec_ctx = MFC_OTHER_DEC_CTX_BUF_SIZE_V8,
@@ -1421,13 +1424,13 @@ struct s5p_mfc_buf_size_v6 mfc_buf_size_v8 = {
.other_enc_ctx = MFC_OTHER_ENC_CTX_BUF_SIZE_V8,
};
-struct s5p_mfc_buf_size buf_size_v8 = {
+static struct s5p_mfc_buf_size buf_size_v8 = {
.fw = MAX_FW_SIZE_V8,
.cpb = MAX_CPB_SIZE_V8,
.priv = &mfc_buf_size_v8,
};
-struct s5p_mfc_buf_align mfc_buf_align_v8 = {
+static struct s5p_mfc_buf_align mfc_buf_align_v8 = {
.base = 0,
};
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v5.c b/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v5.c
index 9a6efd6c1329..8c4739ca16d6 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v5.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v5.c
@@ -14,6 +14,7 @@
#include "s5p_mfc_cmd.h"
#include "s5p_mfc_common.h"
#include "s5p_mfc_debug.h"
+#include "s5p_mfc_cmd_v5.h"
/* This function is used to send a command to the MFC */
static int s5p_mfc_cmd_host2risc_v5(struct s5p_mfc_dev *dev, int cmd,
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v6.c b/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v6.c
index ec1a5947ed7d..f17609669b96 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v6.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v6.c
@@ -16,6 +16,7 @@
#include "s5p_mfc_debug.h"
#include "s5p_mfc_intr.h"
#include "s5p_mfc_opr.h"
+#include "s5p_mfc_cmd_v6.h"
static int s5p_mfc_cmd_host2risc_v6(struct s5p_mfc_dev *dev, int cmd,
struct s5p_mfc_cmd_args *args)
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_common.h b/drivers/media/platform/s5p-mfc/s5p_mfc_common.h
index 01816ffb384b..3e41ca1293ed 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_common.h
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_common.h
@@ -698,6 +698,12 @@ struct mfc_control {
#define s5p_mfc_hw_call(f, op, args...) \
((f && f->op) ? f->op(args) : -ENODEV)
+#define s5p_mfc_hw_call_void(f, op, args...) \
+do { \
+ if (f && f->op) \
+ f->op(args); \
+} while (0)
+
#define fh_to_ctx(__fh) container_of(__fh, struct s5p_mfc_ctx, fh)
#define ctrl_to_ctx(__ctrl) \
container_of((__ctrl)->handler, struct s5p_mfc_ctx, ctrl_handler)
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c b/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c
index ca9f78922832..0c885a8a0e9f 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c
@@ -21,6 +21,7 @@
#include "s5p_mfc_intr.h"
#include "s5p_mfc_opr.h"
#include "s5p_mfc_pm.h"
+#include "s5p_mfc_ctrl.h"
/* Allocate memory for firmware */
int s5p_mfc_alloc_firmware(struct s5p_mfc_dev *dev)
@@ -188,12 +189,12 @@ static inline void s5p_mfc_init_memctrl(struct s5p_mfc_dev *dev)
{
if (IS_MFCV6_PLUS(dev)) {
mfc_write(dev, dev->bank1, S5P_FIMV_RISC_BASE_ADDRESS_V6);
- mfc_debug(2, "Base Address : %08x\n", dev->bank1);
+ mfc_debug(2, "Base Address : %pad\n", &dev->bank1);
} else {
mfc_write(dev, dev->bank1, S5P_FIMV_MC_DRAMBASE_ADR_A);
mfc_write(dev, dev->bank2, S5P_FIMV_MC_DRAMBASE_ADR_B);
- mfc_debug(2, "Bank1: %08x, Bank2: %08x\n",
- dev->bank1, dev->bank2);
+ mfc_debug(2, "Bank1: %pad, Bank2: %pad\n",
+ &dev->bank1, &dev->bank2);
}
}
@@ -257,9 +258,9 @@ int s5p_mfc_init_hw(struct s5p_mfc_dev *dev)
s5p_mfc_clock_off();
return ret;
}
- mfc_debug(2, "Ok, now will write a command to init the system\n");
+ mfc_debug(2, "Ok, now will wait for completion of hardware init\n");
if (s5p_mfc_wait_for_done_dev(dev, S5P_MFC_R2H_CMD_SYS_INIT_RET)) {
- mfc_err("Failed to load firmware\n");
+ mfc_err("Failed to init hardware\n");
s5p_mfc_reset(dev);
s5p_mfc_clock_off();
return -EIO;
@@ -293,7 +294,7 @@ void s5p_mfc_deinit_hw(struct s5p_mfc_dev *dev)
s5p_mfc_clock_on();
s5p_mfc_reset(dev);
- s5p_mfc_hw_call(dev->mfc_ops, release_dev_context_buffer, dev);
+ s5p_mfc_hw_call_void(dev->mfc_ops, release_dev_context_buffer, dev);
s5p_mfc_clock_off();
}
@@ -396,7 +397,7 @@ int s5p_mfc_open_mfc_inst(struct s5p_mfc_dev *dev, struct s5p_mfc_ctx *ctx)
set_work_bit_irqsave(ctx);
s5p_mfc_clean_ctx_int_flags(ctx);
- s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
+ s5p_mfc_hw_call_void(dev->mfc_ops, try_run, dev);
if (s5p_mfc_wait_for_done_ctx(ctx,
S5P_MFC_R2H_CMD_OPEN_INSTANCE_RET, 0)) {
/* Error or timeout */
@@ -410,9 +411,9 @@ int s5p_mfc_open_mfc_inst(struct s5p_mfc_dev *dev, struct s5p_mfc_ctx *ctx)
err_free_desc_buf:
if (ctx->type == MFCINST_DECODER)
- s5p_mfc_hw_call(dev->mfc_ops, release_dec_desc_buffer, ctx);
+ s5p_mfc_hw_call_void(dev->mfc_ops, release_dec_desc_buffer, ctx);
err_free_inst_buf:
- s5p_mfc_hw_call(dev->mfc_ops, release_instance_buffer, ctx);
+ s5p_mfc_hw_call_void(dev->mfc_ops, release_instance_buffer, ctx);
err:
return ret;
}
@@ -422,17 +423,17 @@ void s5p_mfc_close_mfc_inst(struct s5p_mfc_dev *dev, struct s5p_mfc_ctx *ctx)
ctx->state = MFCINST_RETURN_INST;
set_work_bit_irqsave(ctx);
s5p_mfc_clean_ctx_int_flags(ctx);
- s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
+ s5p_mfc_hw_call_void(dev->mfc_ops, try_run, dev);
/* Wait until instance is returned or timeout occurred */
if (s5p_mfc_wait_for_done_ctx(ctx,
S5P_MFC_R2H_CMD_CLOSE_INSTANCE_RET, 0))
mfc_err("Err returning instance\n");
/* Free resources */
- s5p_mfc_hw_call(dev->mfc_ops, release_codec_buffers, ctx);
- s5p_mfc_hw_call(dev->mfc_ops, release_instance_buffer, ctx);
+ s5p_mfc_hw_call_void(dev->mfc_ops, release_codec_buffers, ctx);
+ s5p_mfc_hw_call_void(dev->mfc_ops, release_instance_buffer, ctx);
if (ctx->type == MFCINST_DECODER)
- s5p_mfc_hw_call(dev->mfc_ops, release_dec_desc_buffer, ctx);
+ s5p_mfc_hw_call_void(dev->mfc_ops, release_dec_desc_buffer, ctx);
ctx->inst_no = MFC_NO_INSTANCE_SET;
ctx->state = MFCINST_FREE;
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_debug.h b/drivers/media/platform/s5p-mfc/s5p_mfc_debug.h
index 8e608f5aa0d7..5936923c631c 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_debug.h
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_debug.h
@@ -1,5 +1,5 @@
/*
- * drivers/media/platform/samsung/mfc5/s5p_mfc_debug.h
+ * drivers/media/platform/s5p-mfc/s5p_mfc_debug.h
*
* Header file for Samsung MFC (Multi Function Codec - FIMV) driver
* This file contains debug macros
@@ -18,11 +18,11 @@
#define DEBUG
#ifdef DEBUG
-extern int debug;
+extern int mfc_debug_level;
#define mfc_debug(level, fmt, args...) \
do { \
- if (debug >= level) \
+ if (mfc_debug_level >= level) \
printk(KERN_DEBUG "%s:%d: " fmt, \
__func__, __LINE__, ##args); \
} while (0)
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c
index 9103258b7df3..a98fe023deaf 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c
@@ -283,17 +283,13 @@ static int vidioc_querycap(struct file *file, void *priv,
/* Enumerate format */
static int vidioc_enum_fmt(struct file *file, struct v4l2_fmtdesc *f,
- bool mplane, bool out)
+ bool out)
{
struct s5p_mfc_dev *dev = video_drvdata(file);
struct s5p_mfc_fmt *fmt;
int i, j = 0;
for (i = 0; i < ARRAY_SIZE(formats); ++i) {
- if (mplane && formats[i].num_planes == 1)
- continue;
- else if (!mplane && formats[i].num_planes > 1)
- continue;
if (out && formats[i].type != MFC_FMT_DEC)
continue;
else if (!out && formats[i].type != MFC_FMT_RAW)
@@ -313,28 +309,16 @@ static int vidioc_enum_fmt(struct file *file, struct v4l2_fmtdesc *f,
return 0;
}
-static int vidioc_enum_fmt_vid_cap(struct file *file, void *pirv,
- struct v4l2_fmtdesc *f)
-{
- return vidioc_enum_fmt(file, f, false, false);
-}
-
static int vidioc_enum_fmt_vid_cap_mplane(struct file *file, void *pirv,
struct v4l2_fmtdesc *f)
{
- return vidioc_enum_fmt(file, f, true, false);
-}
-
-static int vidioc_enum_fmt_vid_out(struct file *file, void *priv,
- struct v4l2_fmtdesc *f)
-{
- return vidioc_enum_fmt(file, f, false, true);
+ return vidioc_enum_fmt(file, f, false);
}
static int vidioc_enum_fmt_vid_out_mplane(struct file *file, void *priv,
struct v4l2_fmtdesc *f)
{
- return vidioc_enum_fmt(file, f, true, true);
+ return vidioc_enum_fmt(file, f, true);
}
/* Get format */
@@ -543,7 +527,7 @@ static int reqbufs_capture(struct s5p_mfc_dev *dev, struct s5p_mfc_ctx *ctx,
ret = vb2_reqbufs(&ctx->vq_dst, reqbufs);
if (ret)
goto out;
- s5p_mfc_hw_call(dev->mfc_ops, release_codec_buffers, ctx);
+ s5p_mfc_hw_call_void(dev->mfc_ops, release_codec_buffers, ctx);
ctx->dst_bufs_cnt = 0;
} else if (ctx->capture_state == QUEUE_FREE) {
WARN_ON(ctx->dst_bufs_cnt != 0);
@@ -571,7 +555,7 @@ static int reqbufs_capture(struct s5p_mfc_dev *dev, struct s5p_mfc_ctx *ctx,
if (s5p_mfc_ctx_ready(ctx))
set_work_bit_irqsave(ctx);
- s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
+ s5p_mfc_hw_call_void(dev->mfc_ops, try_run, dev);
s5p_mfc_wait_for_done_ctx(ctx, S5P_MFC_R2H_CMD_INIT_BUFFERS_RET,
0);
} else {
@@ -823,8 +807,8 @@ static int vidioc_g_crop(struct file *file, void *priv,
return 0;
}
-int vidioc_decoder_cmd(struct file *file, void *priv,
- struct v4l2_decoder_cmd *cmd)
+static int vidioc_decoder_cmd(struct file *file, void *priv,
+ struct v4l2_decoder_cmd *cmd)
{
struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
struct s5p_mfc_dev *dev = ctx->dev;
@@ -846,7 +830,7 @@ int vidioc_decoder_cmd(struct file *file, void *priv,
if (s5p_mfc_ctx_ready(ctx))
set_work_bit_irqsave(ctx);
spin_unlock_irqrestore(&dev->irqlock, flags);
- s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
+ s5p_mfc_hw_call_void(dev->mfc_ops, try_run, dev);
} else {
mfc_err("EOS: marking last buffer of stream");
buf = list_entry(ctx->src_queue.prev,
@@ -881,9 +865,7 @@ static int vidioc_subscribe_event(struct v4l2_fh *fh,
/* v4l2_ioctl_ops */
static const struct v4l2_ioctl_ops s5p_mfc_dec_ioctl_ops = {
.vidioc_querycap = vidioc_querycap,
- .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
.vidioc_enum_fmt_vid_cap_mplane = vidioc_enum_fmt_vid_cap_mplane,
- .vidioc_enum_fmt_vid_out = vidioc_enum_fmt_vid_out,
.vidioc_enum_fmt_vid_out_mplane = vidioc_enum_fmt_vid_out_mplane,
.vidioc_g_fmt_vid_cap_mplane = vidioc_g_fmt,
.vidioc_g_fmt_vid_out_mplane = vidioc_g_fmt,
@@ -990,7 +972,7 @@ static int s5p_mfc_buf_init(struct vb2_buffer *vb)
if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
if (ctx->capture_state == QUEUE_BUFS_MMAPED)
return 0;
- for (i = 0; i <= ctx->src_fmt->num_planes ; i++) {
+ for (i = 0; i < ctx->dst_fmt->num_planes; i++) {
if (IS_ERR_OR_NULL(ERR_PTR(
vb2_dma_contig_plane_dma_addr(vb, i)))) {
mfc_err("Plane mem not allocated\n");
@@ -1044,7 +1026,7 @@ static int s5p_mfc_start_streaming(struct vb2_queue *q, unsigned int count)
/* If context is ready then dev = work->data;schedule it to run */
if (s5p_mfc_ctx_ready(ctx))
set_work_bit_irqsave(ctx);
- s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
+ s5p_mfc_hw_call_void(dev->mfc_ops, try_run, dev);
return 0;
}
@@ -1065,8 +1047,8 @@ static void s5p_mfc_stop_streaming(struct vb2_queue *q)
}
if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
spin_lock_irqsave(&dev->irqlock, flags);
- s5p_mfc_hw_call(dev->mfc_ops, cleanup_queue, &ctx->dst_queue,
- &ctx->vq_dst);
+ s5p_mfc_hw_call_void(dev->mfc_ops, cleanup_queue,
+ &ctx->dst_queue, &ctx->vq_dst);
INIT_LIST_HEAD(&ctx->dst_queue);
ctx->dst_queue_cnt = 0;
ctx->dpb_flush_flag = 1;
@@ -1076,7 +1058,7 @@ static void s5p_mfc_stop_streaming(struct vb2_queue *q)
ctx->state = MFCINST_FLUSH;
set_work_bit_irqsave(ctx);
s5p_mfc_clean_ctx_int_flags(ctx);
- s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
+ s5p_mfc_hw_call_void(dev->mfc_ops, try_run, dev);
if (s5p_mfc_wait_for_done_ctx(ctx,
S5P_MFC_R2H_CMD_DPB_FLUSH_RET, 0))
mfc_err("Err flushing buffers\n");
@@ -1084,8 +1066,8 @@ static void s5p_mfc_stop_streaming(struct vb2_queue *q)
}
if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
spin_lock_irqsave(&dev->irqlock, flags);
- s5p_mfc_hw_call(dev->mfc_ops, cleanup_queue, &ctx->src_queue,
- &ctx->vq_src);
+ s5p_mfc_hw_call_void(dev->mfc_ops, cleanup_queue,
+ &ctx->src_queue, &ctx->vq_src);
INIT_LIST_HEAD(&ctx->src_queue);
ctx->src_queue_cnt = 0;
spin_unlock_irqrestore(&dev->irqlock, flags);
@@ -1124,7 +1106,7 @@ static void s5p_mfc_buf_queue(struct vb2_buffer *vb)
}
if (s5p_mfc_ctx_ready(ctx))
set_work_bit_irqsave(ctx);
- s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
+ s5p_mfc_hw_call_void(dev->mfc_ops, try_run, dev);
}
static struct vb2_ops s5p_mfc_dec_qops = {
@@ -1220,7 +1202,7 @@ void s5p_mfc_dec_init(struct s5p_mfc_ctx *ctx)
else
f.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_NV12MT;
ctx->dst_fmt = find_format(&f, MFC_FMT_RAW);
- mfc_debug(2, "Default src_fmt is %x, dest_fmt is %x\n",
- (unsigned int)ctx->src_fmt, (unsigned int)ctx->dst_fmt);
+ mfc_debug(2, "Default src_fmt is %p, dest_fmt is %p\n",
+ ctx->src_fmt, ctx->dst_fmt);
}
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c
index d26b2484ca10..a904a1c7bb21 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c
@@ -739,14 +739,11 @@ static int s5p_mfc_ctx_ready(struct s5p_mfc_ctx *ctx)
static void cleanup_ref_queue(struct s5p_mfc_ctx *ctx)
{
struct s5p_mfc_buf *mb_entry;
- unsigned long mb_y_addr, mb_c_addr;
/* move buffers in ref queue to src queue */
while (!list_empty(&ctx->ref_queue)) {
mb_entry = list_entry((&ctx->ref_queue)->next,
struct s5p_mfc_buf, list);
- mb_y_addr = vb2_dma_contig_plane_dma_addr(mb_entry->b, 0);
- mb_c_addr = vb2_dma_contig_plane_dma_addr(mb_entry->b, 1);
list_del(&mb_entry->list);
ctx->ref_queue_cnt--;
list_add_tail(&mb_entry->list, &ctx->src_queue);
@@ -770,7 +767,7 @@ static int enc_pre_seq_start(struct s5p_mfc_ctx *ctx)
dst_mb = list_entry(ctx->dst_queue.next, struct s5p_mfc_buf, list);
dst_addr = vb2_dma_contig_plane_dma_addr(dst_mb->b, 0);
dst_size = vb2_plane_size(dst_mb->b, 0);
- s5p_mfc_hw_call(dev->mfc_ops, set_enc_stream_buffer, ctx, dst_addr,
+ s5p_mfc_hw_call_void(dev->mfc_ops, set_enc_stream_buffer, ctx, dst_addr,
dst_size);
spin_unlock_irqrestore(&dev->irqlock, flags);
return 0;
@@ -803,7 +800,7 @@ static int enc_post_seq_start(struct s5p_mfc_ctx *ctx)
ctx->state = MFCINST_RUNNING;
if (s5p_mfc_ctx_ready(ctx))
set_work_bit_irqsave(ctx);
- s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
+ s5p_mfc_hw_call_void(dev->mfc_ops, try_run, dev);
} else {
enc_pb_count = s5p_mfc_hw_call(dev->mfc_ops,
get_enc_dpb_count, dev);
@@ -828,15 +825,15 @@ static int enc_pre_frame_start(struct s5p_mfc_ctx *ctx)
src_mb = list_entry(ctx->src_queue.next, struct s5p_mfc_buf, list);
src_y_addr = vb2_dma_contig_plane_dma_addr(src_mb->b, 0);
src_c_addr = vb2_dma_contig_plane_dma_addr(src_mb->b, 1);
- s5p_mfc_hw_call(dev->mfc_ops, set_enc_frame_buffer, ctx, src_y_addr,
- src_c_addr);
+ s5p_mfc_hw_call_void(dev->mfc_ops, set_enc_frame_buffer, ctx,
+ src_y_addr, src_c_addr);
spin_unlock_irqrestore(&dev->irqlock, flags);
spin_lock_irqsave(&dev->irqlock, flags);
dst_mb = list_entry(ctx->dst_queue.next, struct s5p_mfc_buf, list);
dst_addr = vb2_dma_contig_plane_dma_addr(dst_mb->b, 0);
dst_size = vb2_plane_size(dst_mb->b, 0);
- s5p_mfc_hw_call(dev->mfc_ops, set_enc_stream_buffer, ctx, dst_addr,
+ s5p_mfc_hw_call_void(dev->mfc_ops, set_enc_stream_buffer, ctx, dst_addr,
dst_size);
spin_unlock_irqrestore(&dev->irqlock, flags);
@@ -861,7 +858,7 @@ static int enc_post_frame_start(struct s5p_mfc_ctx *ctx)
mfc_read(dev, S5P_FIMV_ENC_SI_PIC_CNT));
spin_lock_irqsave(&dev->irqlock, flags);
if (slice_type >= 0) {
- s5p_mfc_hw_call(dev->mfc_ops, get_enc_frame_buffer, ctx,
+ s5p_mfc_hw_call_void(dev->mfc_ops, get_enc_frame_buffer, ctx,
&enc_y_addr, &enc_c_addr);
list_for_each_entry(mb_entry, &ctx->src_queue, list) {
mb_y_addr = vb2_dma_contig_plane_dma_addr(mb_entry->b, 0);
@@ -954,17 +951,13 @@ static int vidioc_querycap(struct file *file, void *priv,
}
static int vidioc_enum_fmt(struct file *file, struct v4l2_fmtdesc *f,
- bool mplane, bool out)
+ bool out)
{
struct s5p_mfc_dev *dev = video_drvdata(file);
struct s5p_mfc_fmt *fmt;
int i, j = 0;
for (i = 0; i < ARRAY_SIZE(formats); ++i) {
- if (mplane && formats[i].num_planes == 1)
- continue;
- else if (!mplane && formats[i].num_planes > 1)
- continue;
if (out && formats[i].type != MFC_FMT_RAW)
continue;
else if (!out && formats[i].type != MFC_FMT_ENC)
@@ -984,28 +977,16 @@ static int vidioc_enum_fmt(struct file *file, struct v4l2_fmtdesc *f,
return -EINVAL;
}
-static int vidioc_enum_fmt_vid_cap(struct file *file, void *pirv,
- struct v4l2_fmtdesc *f)
-{
- return vidioc_enum_fmt(file, f, false, false);
-}
-
static int vidioc_enum_fmt_vid_cap_mplane(struct file *file, void *pirv,
struct v4l2_fmtdesc *f)
{
- return vidioc_enum_fmt(file, f, true, false);
-}
-
-static int vidioc_enum_fmt_vid_out(struct file *file, void *prov,
- struct v4l2_fmtdesc *f)
-{
- return vidioc_enum_fmt(file, f, false, true);
+ return vidioc_enum_fmt(file, f, false);
}
static int vidioc_enum_fmt_vid_out_mplane(struct file *file, void *prov,
struct v4l2_fmtdesc *f)
{
- return vidioc_enum_fmt(file, f, true, true);
+ return vidioc_enum_fmt(file, f, true);
}
static int vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format *f)
@@ -1127,7 +1108,7 @@ static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f)
pix_fmt_mp->width, pix_fmt_mp->height,
ctx->img_width, ctx->img_height);
- s5p_mfc_hw_call(dev->mfc_ops, enc_calc_src_size, ctx);
+ s5p_mfc_hw_call_void(dev->mfc_ops, enc_calc_src_size, ctx);
pix_fmt_mp->plane_fmt[0].sizeimage = ctx->luma_size;
pix_fmt_mp->plane_fmt[0].bytesperline = ctx->buf_width;
pix_fmt_mp->plane_fmt[1].sizeimage = ctx->chroma_size;
@@ -1681,8 +1662,8 @@ static int vidioc_g_parm(struct file *file, void *priv,
return 0;
}
-int vidioc_encoder_cmd(struct file *file, void *priv,
- struct v4l2_encoder_cmd *cmd)
+static int vidioc_encoder_cmd(struct file *file, void *priv,
+ struct v4l2_encoder_cmd *cmd)
{
struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
struct s5p_mfc_dev *dev = ctx->dev;
@@ -1704,7 +1685,7 @@ int vidioc_encoder_cmd(struct file *file, void *priv,
if (s5p_mfc_ctx_ready(ctx))
set_work_bit_irqsave(ctx);
spin_unlock_irqrestore(&dev->irqlock, flags);
- s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
+ s5p_mfc_hw_call_void(dev->mfc_ops, try_run, dev);
} else {
mfc_debug(2, "EOS: marking last buffer of stream\n");
buf = list_entry(ctx->src_queue.prev,
@@ -1736,9 +1717,7 @@ static int vidioc_subscribe_event(struct v4l2_fh *fh,
static const struct v4l2_ioctl_ops s5p_mfc_enc_ioctl_ops = {
.vidioc_querycap = vidioc_querycap,
- .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
.vidioc_enum_fmt_vid_cap_mplane = vidioc_enum_fmt_vid_cap_mplane,
- .vidioc_enum_fmt_vid_out = vidioc_enum_fmt_vid_out,
.vidioc_enum_fmt_vid_out_mplane = vidioc_enum_fmt_vid_out_mplane,
.vidioc_g_fmt_vid_cap_mplane = vidioc_g_fmt,
.vidioc_g_fmt_vid_out_mplane = vidioc_g_fmt,
@@ -1771,13 +1750,13 @@ static int check_vb_with_fmt(struct s5p_mfc_fmt *fmt, struct vb2_buffer *vb)
return -EINVAL;
}
for (i = 0; i < fmt->num_planes; i++) {
- if (!vb2_dma_contig_plane_dma_addr(vb, i)) {
+ dma_addr_t dma = vb2_dma_contig_plane_dma_addr(vb, i);
+ if (!dma) {
mfc_err("failed to get plane cookie\n");
return -EINVAL;
}
- mfc_debug(2, "index: %d, plane[%d] cookie: 0x%08zx\n",
- vb->v4l2_buf.index, i,
- vb2_dma_contig_plane_dma_addr(vb, i));
+ mfc_debug(2, "index: %d, plane[%d] cookie: %pad\n",
+ vb->v4l2_buf.index, i, &dma);
}
return 0;
}
@@ -1897,7 +1876,7 @@ static int s5p_mfc_buf_prepare(struct vb2_buffer *vb)
ret = check_vb_with_fmt(ctx->dst_fmt, vb);
if (ret < 0)
return ret;
- mfc_debug(2, "plane size: %ld, dst size: %d\n",
+ mfc_debug(2, "plane size: %ld, dst size: %zu\n",
vb2_plane_size(vb, 0), ctx->enc_dst_buf_size);
if (vb2_plane_size(vb, 0) < ctx->enc_dst_buf_size) {
mfc_err("plane size is too small for capture\n");
@@ -1948,7 +1927,7 @@ static int s5p_mfc_start_streaming(struct vb2_queue *q, unsigned int count)
/* If context is ready then dev = work->data;schedule it to run */
if (s5p_mfc_ctx_ready(ctx))
set_work_bit_irqsave(ctx);
- s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
+ s5p_mfc_hw_call_void(dev->mfc_ops, try_run, dev);
return 0;
}
@@ -1969,14 +1948,14 @@ static void s5p_mfc_stop_streaming(struct vb2_queue *q)
ctx->state = MFCINST_FINISHED;
spin_lock_irqsave(&dev->irqlock, flags);
if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
- s5p_mfc_hw_call(dev->mfc_ops, cleanup_queue, &ctx->dst_queue,
- &ctx->vq_dst);
+ s5p_mfc_hw_call_void(dev->mfc_ops, cleanup_queue,
+ &ctx->dst_queue, &ctx->vq_dst);
INIT_LIST_HEAD(&ctx->dst_queue);
ctx->dst_queue_cnt = 0;
}
if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
cleanup_ref_queue(ctx);
- s5p_mfc_hw_call(dev->mfc_ops, cleanup_queue, &ctx->src_queue,
+ s5p_mfc_hw_call_void(dev->mfc_ops, cleanup_queue, &ctx->src_queue,
&ctx->vq_src);
INIT_LIST_HEAD(&ctx->src_queue);
ctx->src_queue_cnt = 0;
@@ -2017,7 +1996,7 @@ static void s5p_mfc_buf_queue(struct vb2_buffer *vb)
}
if (s5p_mfc_ctx_ready(ctx))
set_work_bit_irqsave(ctx);
- s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
+ s5p_mfc_hw_call_void(dev->mfc_ops, try_run, dev);
}
static struct vb2_ops s5p_mfc_enc_qops = {
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr.c b/drivers/media/platform/s5p-mfc/s5p_mfc_opr.c
index c9a227428e6a..00a1d8b2a8c2 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr.c
@@ -41,7 +41,7 @@ int s5p_mfc_alloc_priv_buf(struct device *dev,
struct s5p_mfc_priv_buf *b)
{
- mfc_debug(3, "Allocating priv: %d\n", b->size);
+ mfc_debug(3, "Allocating priv: %zu\n", b->size);
b->virt = dma_alloc_coherent(dev, b->size, &b->dma, GFP_KERNEL);
@@ -50,7 +50,7 @@ int s5p_mfc_alloc_priv_buf(struct device *dev,
return -ENOMEM;
}
- mfc_debug(3, "Allocated addr %p %08x\n", b->virt, b->dma);
+ mfc_debug(3, "Allocated addr %p %pad\n", b->virt, &b->dma);
return 0;
}
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr.h b/drivers/media/platform/s5p-mfc/s5p_mfc_opr.h
index 7a7ad32ee608..de2b8c69daa5 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr.h
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr.h
@@ -20,254 +20,254 @@
struct s5p_mfc_regs {
/* codec common registers */
- void *risc_on;
- void *risc2host_int;
- void *host2risc_int;
- void *risc_base_address;
- void *mfc_reset;
- void *host2risc_command;
- void *risc2host_command;
- void *mfc_bus_reset_ctrl;
- void *firmware_version;
- void *instance_id;
- void *codec_type;
- void *context_mem_addr;
- void *context_mem_size;
- void *pixel_format;
- void *metadata_enable;
- void *mfc_version;
- void *dbg_info_enable;
- void *dbg_buffer_addr;
- void *dbg_buffer_size;
- void *hed_control;
- void *mfc_timeout_value;
- void *hed_shared_mem_addr;
- void *dis_shared_mem_addr;/* only v7 */
- void *ret_instance_id;
- void *error_code;
- void *dbg_buffer_output_size;
- void *metadata_status;
- void *metadata_addr_mb_info;
- void *metadata_size_mb_info;
- void *dbg_info_stage_counter;
+ volatile void __iomem *risc_on;
+ volatile void __iomem *risc2host_int;
+ volatile void __iomem *host2risc_int;
+ volatile void __iomem *risc_base_address;
+ volatile void __iomem *mfc_reset;
+ volatile void __iomem *host2risc_command;
+ volatile void __iomem *risc2host_command;
+ volatile void __iomem *mfc_bus_reset_ctrl;
+ volatile void __iomem *firmware_version;
+ volatile void __iomem *instance_id;
+ volatile void __iomem *codec_type;
+ volatile void __iomem *context_mem_addr;
+ volatile void __iomem *context_mem_size;
+ volatile void __iomem *pixel_format;
+ volatile void __iomem *metadata_enable;
+ volatile void __iomem *mfc_version;
+ volatile void __iomem *dbg_info_enable;
+ volatile void __iomem *dbg_buffer_addr;
+ volatile void __iomem *dbg_buffer_size;
+ volatile void __iomem *hed_control;
+ volatile void __iomem *mfc_timeout_value;
+ volatile void __iomem *hed_shared_mem_addr;
+ volatile void __iomem *dis_shared_mem_addr;/* only v7 */
+ volatile void __iomem *ret_instance_id;
+ volatile void __iomem *error_code;
+ volatile void __iomem *dbg_buffer_output_size;
+ volatile void __iomem *metadata_status;
+ volatile void __iomem *metadata_addr_mb_info;
+ volatile void __iomem *metadata_size_mb_info;
+ volatile void __iomem *dbg_info_stage_counter;
/* decoder registers */
- void *d_crc_ctrl;
- void *d_dec_options;
- void *d_display_delay;
- void *d_set_frame_width;
- void *d_set_frame_height;
- void *d_sei_enable;
- void *d_min_num_dpb;
- void *d_min_first_plane_dpb_size;
- void *d_min_second_plane_dpb_size;
- void *d_min_third_plane_dpb_size;/* only v8 */
- void *d_min_num_mv;
- void *d_mvc_num_views;
- void *d_min_num_dis;/* only v7 */
- void *d_min_first_dis_size;/* only v7 */
- void *d_min_second_dis_size;/* only v7 */
- void *d_min_third_dis_size;/* only v7 */
- void *d_post_filter_luma_dpb0;/* v7 and v8 */
- void *d_post_filter_luma_dpb1;/* v7 and v8 */
- void *d_post_filter_luma_dpb2;/* only v7 */
- void *d_post_filter_chroma_dpb0;/* v7 and v8 */
- void *d_post_filter_chroma_dpb1;/* v7 and v8 */
- void *d_post_filter_chroma_dpb2;/* only v7 */
- void *d_num_dpb;
- void *d_num_mv;
- void *d_init_buffer_options;
- void *d_first_plane_dpb_stride_size;/* only v8 */
- void *d_second_plane_dpb_stride_size;/* only v8 */
- void *d_third_plane_dpb_stride_size;/* only v8 */
- void *d_first_plane_dpb_size;
- void *d_second_plane_dpb_size;
- void *d_third_plane_dpb_size;/* only v8 */
- void *d_mv_buffer_size;
- void *d_first_plane_dpb;
- void *d_second_plane_dpb;
- void *d_third_plane_dpb;
- void *d_mv_buffer;
- void *d_scratch_buffer_addr;
- void *d_scratch_buffer_size;
- void *d_metadata_buffer_addr;
- void *d_metadata_buffer_size;
- void *d_nal_start_options;/* v7 and v8 */
- void *d_cpb_buffer_addr;
- void *d_cpb_buffer_size;
- void *d_available_dpb_flag_upper;
- void *d_available_dpb_flag_lower;
- void *d_cpb_buffer_offset;
- void *d_slice_if_enable;
- void *d_picture_tag;
- void *d_stream_data_size;
- void *d_dynamic_dpb_flag_upper;/* v7 and v8 */
- void *d_dynamic_dpb_flag_lower;/* v7 and v8 */
- void *d_display_frame_width;
- void *d_display_frame_height;
- void *d_display_status;
- void *d_display_first_plane_addr;
- void *d_display_second_plane_addr;
- void *d_display_third_plane_addr;/* only v8 */
- void *d_display_frame_type;
- void *d_display_crop_info1;
- void *d_display_crop_info2;
- void *d_display_picture_profile;
- void *d_display_luma_crc;/* v7 and v8 */
- void *d_display_chroma0_crc;/* v7 and v8 */
- void *d_display_chroma1_crc;/* only v8 */
- void *d_display_luma_crc_top;/* only v6 */
- void *d_display_chroma_crc_top;/* only v6 */
- void *d_display_luma_crc_bot;/* only v6 */
- void *d_display_chroma_crc_bot;/* only v6 */
- void *d_display_aspect_ratio;
- void *d_display_extended_ar;
- void *d_decoded_frame_width;
- void *d_decoded_frame_height;
- void *d_decoded_status;
- void *d_decoded_first_plane_addr;
- void *d_decoded_second_plane_addr;
- void *d_decoded_third_plane_addr;/* only v8 */
- void *d_decoded_frame_type;
- void *d_decoded_crop_info1;
- void *d_decoded_crop_info2;
- void *d_decoded_picture_profile;
- void *d_decoded_nal_size;
- void *d_decoded_luma_crc;
- void *d_decoded_chroma0_crc;
- void *d_decoded_chroma1_crc;/* only v8 */
- void *d_ret_picture_tag_top;
- void *d_ret_picture_tag_bot;
- void *d_ret_picture_time_top;
- void *d_ret_picture_time_bot;
- void *d_chroma_format;
- void *d_vc1_info;/* v7 and v8 */
- void *d_mpeg4_info;
- void *d_h264_info;
- void *d_metadata_addr_concealed_mb;
- void *d_metadata_size_concealed_mb;
- void *d_metadata_addr_vc1_param;
- void *d_metadata_size_vc1_param;
- void *d_metadata_addr_sei_nal;
- void *d_metadata_size_sei_nal;
- void *d_metadata_addr_vui;
- void *d_metadata_size_vui;
- void *d_metadata_addr_mvcvui;/* v7 and v8 */
- void *d_metadata_size_mvcvui;/* v7 and v8 */
- void *d_mvc_view_id;
- void *d_frame_pack_sei_avail;
- void *d_frame_pack_arrgment_id;
- void *d_frame_pack_sei_info;
- void *d_frame_pack_grid_pos;
- void *d_display_recovery_sei_info;/* v7 and v8 */
- void *d_decoded_recovery_sei_info;/* v7 and v8 */
- void *d_display_first_addr;/* only v7 */
- void *d_display_second_addr;/* only v7 */
- void *d_display_third_addr;/* only v7 */
- void *d_decoded_first_addr;/* only v7 */
- void *d_decoded_second_addr;/* only v7 */
- void *d_decoded_third_addr;/* only v7 */
- void *d_used_dpb_flag_upper;/* v7 and v8 */
- void *d_used_dpb_flag_lower;/* v7 and v8 */
+ volatile void __iomem *d_crc_ctrl;
+ volatile void __iomem *d_dec_options;
+ volatile void __iomem *d_display_delay;
+ volatile void __iomem *d_set_frame_width;
+ volatile void __iomem *d_set_frame_height;
+ volatile void __iomem *d_sei_enable;
+ volatile void __iomem *d_min_num_dpb;
+ volatile void __iomem *d_min_first_plane_dpb_size;
+ volatile void __iomem *d_min_second_plane_dpb_size;
+ volatile void __iomem *d_min_third_plane_dpb_size;/* only v8 */
+ volatile void __iomem *d_min_num_mv;
+ volatile void __iomem *d_mvc_num_views;
+ volatile void __iomem *d_min_num_dis;/* only v7 */
+ volatile void __iomem *d_min_first_dis_size;/* only v7 */
+ volatile void __iomem *d_min_second_dis_size;/* only v7 */
+ volatile void __iomem *d_min_third_dis_size;/* only v7 */
+ volatile void __iomem *d_post_filter_luma_dpb0;/* v7 and v8 */
+ volatile void __iomem *d_post_filter_luma_dpb1;/* v7 and v8 */
+ volatile void __iomem *d_post_filter_luma_dpb2;/* only v7 */
+ volatile void __iomem *d_post_filter_chroma_dpb0;/* v7 and v8 */
+ volatile void __iomem *d_post_filter_chroma_dpb1;/* v7 and v8 */
+ volatile void __iomem *d_post_filter_chroma_dpb2;/* only v7 */
+ volatile void __iomem *d_num_dpb;
+ volatile void __iomem *d_num_mv;
+ volatile void __iomem *d_init_buffer_options;
+ volatile void __iomem *d_first_plane_dpb_stride_size;/* only v8 */
+ volatile void __iomem *d_second_plane_dpb_stride_size;/* only v8 */
+ volatile void __iomem *d_third_plane_dpb_stride_size;/* only v8 */
+ volatile void __iomem *d_first_plane_dpb_size;
+ volatile void __iomem *d_second_plane_dpb_size;
+ volatile void __iomem *d_third_plane_dpb_size;/* only v8 */
+ volatile void __iomem *d_mv_buffer_size;
+ volatile void __iomem *d_first_plane_dpb;
+ volatile void __iomem *d_second_plane_dpb;
+ volatile void __iomem *d_third_plane_dpb;
+ volatile void __iomem *d_mv_buffer;
+ volatile void __iomem *d_scratch_buffer_addr;
+ volatile void __iomem *d_scratch_buffer_size;
+ volatile void __iomem *d_metadata_buffer_addr;
+ volatile void __iomem *d_metadata_buffer_size;
+ volatile void __iomem *d_nal_start_options;/* v7 and v8 */
+ volatile void __iomem *d_cpb_buffer_addr;
+ volatile void __iomem *d_cpb_buffer_size;
+ volatile void __iomem *d_available_dpb_flag_upper;
+ volatile void __iomem *d_available_dpb_flag_lower;
+ volatile void __iomem *d_cpb_buffer_offset;
+ volatile void __iomem *d_slice_if_enable;
+ volatile void __iomem *d_picture_tag;
+ volatile void __iomem *d_stream_data_size;
+ volatile void __iomem *d_dynamic_dpb_flag_upper;/* v7 and v8 */
+ volatile void __iomem *d_dynamic_dpb_flag_lower;/* v7 and v8 */
+ volatile void __iomem *d_display_frame_width;
+ volatile void __iomem *d_display_frame_height;
+ volatile void __iomem *d_display_status;
+ volatile void __iomem *d_display_first_plane_addr;
+ volatile void __iomem *d_display_second_plane_addr;
+ volatile void __iomem *d_display_third_plane_addr;/* only v8 */
+ volatile void __iomem *d_display_frame_type;
+ volatile void __iomem *d_display_crop_info1;
+ volatile void __iomem *d_display_crop_info2;
+ volatile void __iomem *d_display_picture_profile;
+ volatile void __iomem *d_display_luma_crc;/* v7 and v8 */
+ volatile void __iomem *d_display_chroma0_crc;/* v7 and v8 */
+ volatile void __iomem *d_display_chroma1_crc;/* only v8 */
+ volatile void __iomem *d_display_luma_crc_top;/* only v6 */
+ volatile void __iomem *d_display_chroma_crc_top;/* only v6 */
+ volatile void __iomem *d_display_luma_crc_bot;/* only v6 */
+ volatile void __iomem *d_display_chroma_crc_bot;/* only v6 */
+ volatile void __iomem *d_display_aspect_ratio;
+ volatile void __iomem *d_display_extended_ar;
+ volatile void __iomem *d_decoded_frame_width;
+ volatile void __iomem *d_decoded_frame_height;
+ volatile void __iomem *d_decoded_status;
+ volatile void __iomem *d_decoded_first_plane_addr;
+ volatile void __iomem *d_decoded_second_plane_addr;
+ volatile void __iomem *d_decoded_third_plane_addr;/* only v8 */
+ volatile void __iomem *d_decoded_frame_type;
+ volatile void __iomem *d_decoded_crop_info1;
+ volatile void __iomem *d_decoded_crop_info2;
+ volatile void __iomem *d_decoded_picture_profile;
+ volatile void __iomem *d_decoded_nal_size;
+ volatile void __iomem *d_decoded_luma_crc;
+ volatile void __iomem *d_decoded_chroma0_crc;
+ volatile void __iomem *d_decoded_chroma1_crc;/* only v8 */
+ volatile void __iomem *d_ret_picture_tag_top;
+ volatile void __iomem *d_ret_picture_tag_bot;
+ volatile void __iomem *d_ret_picture_time_top;
+ volatile void __iomem *d_ret_picture_time_bot;
+ volatile void __iomem *d_chroma_format;
+ volatile void __iomem *d_vc1_info;/* v7 and v8 */
+ volatile void __iomem *d_mpeg4_info;
+ volatile void __iomem *d_h264_info;
+ volatile void __iomem *d_metadata_addr_concealed_mb;
+ volatile void __iomem *d_metadata_size_concealed_mb;
+ volatile void __iomem *d_metadata_addr_vc1_param;
+ volatile void __iomem *d_metadata_size_vc1_param;
+ volatile void __iomem *d_metadata_addr_sei_nal;
+ volatile void __iomem *d_metadata_size_sei_nal;
+ volatile void __iomem *d_metadata_addr_vui;
+ volatile void __iomem *d_metadata_size_vui;
+ volatile void __iomem *d_metadata_addr_mvcvui;/* v7 and v8 */
+ volatile void __iomem *d_metadata_size_mvcvui;/* v7 and v8 */
+ volatile void __iomem *d_mvc_view_id;
+ volatile void __iomem *d_frame_pack_sei_avail;
+ volatile void __iomem *d_frame_pack_arrgment_id;
+ volatile void __iomem *d_frame_pack_sei_info;
+ volatile void __iomem *d_frame_pack_grid_pos;
+ volatile void __iomem *d_display_recovery_sei_info;/* v7 and v8 */
+ volatile void __iomem *d_decoded_recovery_sei_info;/* v7 and v8 */
+ volatile void __iomem *d_display_first_addr;/* only v7 */
+ volatile void __iomem *d_display_second_addr;/* only v7 */
+ volatile void __iomem *d_display_third_addr;/* only v7 */
+ volatile void __iomem *d_decoded_first_addr;/* only v7 */
+ volatile void __iomem *d_decoded_second_addr;/* only v7 */
+ volatile void __iomem *d_decoded_third_addr;/* only v7 */
+ volatile void __iomem *d_used_dpb_flag_upper;/* v7 and v8 */
+ volatile void __iomem *d_used_dpb_flag_lower;/* v7 and v8 */
/* encoder registers */
- void *e_frame_width;
- void *e_frame_height;
- void *e_cropped_frame_width;
- void *e_cropped_frame_height;
- void *e_frame_crop_offset;
- void *e_enc_options;
- void *e_picture_profile;
- void *e_vbv_buffer_size;
- void *e_vbv_init_delay;
- void *e_fixed_picture_qp;
- void *e_rc_config;
- void *e_rc_qp_bound;
- void *e_rc_qp_bound_pb;/* v7 and v8 */
- void *e_rc_mode;
- void *e_mb_rc_config;
- void *e_padding_ctrl;
- void *e_air_threshold;
- void *e_mv_hor_range;
- void *e_mv_ver_range;
- void *e_num_dpb;
- void *e_luma_dpb;
- void *e_chroma_dpb;
- void *e_me_buffer;
- void *e_scratch_buffer_addr;
- void *e_scratch_buffer_size;
- void *e_tmv_buffer0;
- void *e_tmv_buffer1;
- void *e_ir_buffer_addr;/* v7 and v8 */
- void *e_source_first_plane_addr;
- void *e_source_second_plane_addr;
- void *e_source_third_plane_addr;/* v7 and v8 */
- void *e_source_first_plane_stride;/* v7 and v8 */
- void *e_source_second_plane_stride;/* v7 and v8 */
- void *e_source_third_plane_stride;/* v7 and v8 */
- void *e_stream_buffer_addr;
- void *e_stream_buffer_size;
- void *e_roi_buffer_addr;
- void *e_param_change;
- void *e_ir_size;
- void *e_gop_config;
- void *e_mslice_mode;
- void *e_mslice_size_mb;
- void *e_mslice_size_bits;
- void *e_frame_insertion;
- void *e_rc_frame_rate;
- void *e_rc_bit_rate;
- void *e_rc_roi_ctrl;
- void *e_picture_tag;
- void *e_bit_count_enable;
- void *e_max_bit_count;
- void *e_min_bit_count;
- void *e_metadata_buffer_addr;
- void *e_metadata_buffer_size;
- void *e_encoded_source_first_plane_addr;
- void *e_encoded_source_second_plane_addr;
- void *e_encoded_source_third_plane_addr;/* v7 and v8 */
- void *e_stream_size;
- void *e_slice_type;
- void *e_picture_count;
- void *e_ret_picture_tag;
- void *e_stream_buffer_write_pointer; /* only v6 */
- void *e_recon_luma_dpb_addr;
- void *e_recon_chroma_dpb_addr;
- void *e_metadata_addr_enc_slice;
- void *e_metadata_size_enc_slice;
- void *e_mpeg4_options;
- void *e_mpeg4_hec_period;
- void *e_aspect_ratio;
- void *e_extended_sar;
- void *e_h264_options;
- void *e_h264_options_2;/* v7 and v8 */
- void *e_h264_lf_alpha_offset;
- void *e_h264_lf_beta_offset;
- void *e_h264_i_period;
- void *e_h264_fmo_slice_grp_map_type;
- void *e_h264_fmo_num_slice_grp_minus1;
- void *e_h264_fmo_slice_grp_change_dir;
- void *e_h264_fmo_slice_grp_change_rate_minus1;
- void *e_h264_fmo_run_length_minus1_0;
- void *e_h264_aso_slice_order_0;
- void *e_h264_chroma_qp_offset;
- void *e_h264_num_t_layer;
- void *e_h264_hierarchical_qp_layer0;
- void *e_h264_frame_packing_sei_info;
- void *e_h264_nal_control;/* v7 and v8 */
- void *e_mvc_frame_qp_view1;
- void *e_mvc_rc_bit_rate_view1;
- void *e_mvc_rc_qbound_view1;
- void *e_mvc_rc_mode_view1;
- void *e_mvc_inter_view_prediction_on;
- void *e_vp8_options;/* v7 and v8 */
- void *e_vp8_filter_options;/* v7 and v8 */
- void *e_vp8_golden_frame_option;/* v7 and v8 */
- void *e_vp8_num_t_layer;/* v7 and v8 */
- void *e_vp8_hierarchical_qp_layer0;/* v7 and v8 */
- void *e_vp8_hierarchical_qp_layer1;/* v7 and v8 */
- void *e_vp8_hierarchical_qp_layer2;/* v7 and v8 */
+ volatile void __iomem *e_frame_width;
+ volatile void __iomem *e_frame_height;
+ volatile void __iomem *e_cropped_frame_width;
+ volatile void __iomem *e_cropped_frame_height;
+ volatile void __iomem *e_frame_crop_offset;
+ volatile void __iomem *e_enc_options;
+ volatile void __iomem *e_picture_profile;
+ volatile void __iomem *e_vbv_buffer_size;
+ volatile void __iomem *e_vbv_init_delay;
+ volatile void __iomem *e_fixed_picture_qp;
+ volatile void __iomem *e_rc_config;
+ volatile void __iomem *e_rc_qp_bound;
+ volatile void __iomem *e_rc_qp_bound_pb;/* v7 and v8 */
+ volatile void __iomem *e_rc_mode;
+ volatile void __iomem *e_mb_rc_config;
+ volatile void __iomem *e_padding_ctrl;
+ volatile void __iomem *e_air_threshold;
+ volatile void __iomem *e_mv_hor_range;
+ volatile void __iomem *e_mv_ver_range;
+ volatile void __iomem *e_num_dpb;
+ volatile void __iomem *e_luma_dpb;
+ volatile void __iomem *e_chroma_dpb;
+ volatile void __iomem *e_me_buffer;
+ volatile void __iomem *e_scratch_buffer_addr;
+ volatile void __iomem *e_scratch_buffer_size;
+ volatile void __iomem *e_tmv_buffer0;
+ volatile void __iomem *e_tmv_buffer1;
+ volatile void __iomem *e_ir_buffer_addr;/* v7 and v8 */
+ volatile void __iomem *e_source_first_plane_addr;
+ volatile void __iomem *e_source_second_plane_addr;
+ volatile void __iomem *e_source_third_plane_addr;/* v7 and v8 */
+ volatile void __iomem *e_source_first_plane_stride;/* v7 and v8 */
+ volatile void __iomem *e_source_second_plane_stride;/* v7 and v8 */
+ volatile void __iomem *e_source_third_plane_stride;/* v7 and v8 */
+ volatile void __iomem *e_stream_buffer_addr;
+ volatile void __iomem *e_stream_buffer_size;
+ volatile void __iomem *e_roi_buffer_addr;
+ volatile void __iomem *e_param_change;
+ volatile void __iomem *e_ir_size;
+ volatile void __iomem *e_gop_config;
+ volatile void __iomem *e_mslice_mode;
+ volatile void __iomem *e_mslice_size_mb;
+ volatile void __iomem *e_mslice_size_bits;
+ volatile void __iomem *e_frame_insertion;
+ volatile void __iomem *e_rc_frame_rate;
+ volatile void __iomem *e_rc_bit_rate;
+ volatile void __iomem *e_rc_roi_ctrl;
+ volatile void __iomem *e_picture_tag;
+ volatile void __iomem *e_bit_count_enable;
+ volatile void __iomem *e_max_bit_count;
+ volatile void __iomem *e_min_bit_count;
+ volatile void __iomem *e_metadata_buffer_addr;
+ volatile void __iomem *e_metadata_buffer_size;
+ volatile void __iomem *e_encoded_source_first_plane_addr;
+ volatile void __iomem *e_encoded_source_second_plane_addr;
+ volatile void __iomem *e_encoded_source_third_plane_addr;/* v7 and v8 */
+ volatile void __iomem *e_stream_size;
+ volatile void __iomem *e_slice_type;
+ volatile void __iomem *e_picture_count;
+ volatile void __iomem *e_ret_picture_tag;
+ volatile void __iomem *e_stream_buffer_write_pointer; /* only v6 */
+ volatile void __iomem *e_recon_luma_dpb_addr;
+ volatile void __iomem *e_recon_chroma_dpb_addr;
+ volatile void __iomem *e_metadata_addr_enc_slice;
+ volatile void __iomem *e_metadata_size_enc_slice;
+ volatile void __iomem *e_mpeg4_options;
+ volatile void __iomem *e_mpeg4_hec_period;
+ volatile void __iomem *e_aspect_ratio;
+ volatile void __iomem *e_extended_sar;
+ volatile void __iomem *e_h264_options;
+ volatile void __iomem *e_h264_options_2;/* v7 and v8 */
+ volatile void __iomem *e_h264_lf_alpha_offset;
+ volatile void __iomem *e_h264_lf_beta_offset;
+ volatile void __iomem *e_h264_i_period;
+ volatile void __iomem *e_h264_fmo_slice_grp_map_type;
+ volatile void __iomem *e_h264_fmo_num_slice_grp_minus1;
+ volatile void __iomem *e_h264_fmo_slice_grp_change_dir;
+ volatile void __iomem *e_h264_fmo_slice_grp_change_rate_minus1;
+ volatile void __iomem *e_h264_fmo_run_length_minus1_0;
+ volatile void __iomem *e_h264_aso_slice_order_0;
+ volatile void __iomem *e_h264_chroma_qp_offset;
+ volatile void __iomem *e_h264_num_t_layer;
+ volatile void __iomem *e_h264_hierarchical_qp_layer0;
+ volatile void __iomem *e_h264_frame_packing_sei_info;
+ volatile void __iomem *e_h264_nal_control;/* v7 and v8 */
+ volatile void __iomem *e_mvc_frame_qp_view1;
+ volatile void __iomem *e_mvc_rc_bit_rate_view1;
+ volatile void __iomem *e_mvc_rc_qbound_view1;
+ volatile void __iomem *e_mvc_rc_mode_view1;
+ volatile void __iomem *e_mvc_inter_view_prediction_on;
+ volatile void __iomem *e_vp8_options;/* v7 and v8 */
+ volatile void __iomem *e_vp8_filter_options;/* v7 and v8 */
+ volatile void __iomem *e_vp8_golden_frame_option;/* v7 and v8 */
+ volatile void __iomem *e_vp8_num_t_layer;/* v7 and v8 */
+ volatile void __iomem *e_vp8_hierarchical_qp_layer0;/* v7 and v8 */
+ volatile void __iomem *e_vp8_hierarchical_qp_layer1;/* v7 and v8 */
+ volatile void __iomem *e_vp8_hierarchical_qp_layer2;/* v7 and v8 */
};
struct s5p_mfc_hw_ops {
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c
index 58ec7bb26ebc..7cf07963187d 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c
@@ -228,6 +228,7 @@ static int s5p_mfc_alloc_instance_buffer_v5(struct s5p_mfc_ctx *ctx)
ret = s5p_mfc_alloc_priv_buf(dev->mem_dev_l, &ctx->shm);
if (ret) {
mfc_err("Failed to allocate shared memory buffer\n");
+ s5p_mfc_release_priv_buf(dev->mem_dev_l, &ctx->ctx);
return ret;
}
@@ -262,7 +263,7 @@ static void s5p_mfc_release_dev_context_buffer_v5(struct s5p_mfc_dev *dev)
static void s5p_mfc_write_info_v5(struct s5p_mfc_ctx *ctx, unsigned int data,
unsigned int ofs)
{
- writel(data, (ctx->shm.virt + ofs));
+ writel(data, (volatile void __iomem *)(ctx->shm.virt + ofs));
wmb();
}
@@ -270,7 +271,7 @@ static unsigned int s5p_mfc_read_info_v5(struct s5p_mfc_ctx *ctx,
unsigned int ofs)
{
rmb();
- return readl(ctx->shm.virt + ofs);
+ return readl((volatile void __iomem *)(ctx->shm.virt + ofs));
}
static void s5p_mfc_dec_calc_dpb_size_v5(struct s5p_mfc_ctx *ctx)
@@ -377,7 +378,7 @@ static int s5p_mfc_set_dec_stream_buffer_v5(struct s5p_mfc_ctx *ctx,
/* Set decoding frame buffer */
static int s5p_mfc_set_dec_frame_buffer_v5(struct s5p_mfc_ctx *ctx)
{
- unsigned int frame_size, i;
+ unsigned int frame_size_lu, i;
unsigned int frame_size_ch, frame_size_mv;
struct s5p_mfc_dev *dev = ctx->dev;
unsigned int dpb;
@@ -465,23 +466,23 @@ static int s5p_mfc_set_dec_frame_buffer_v5(struct s5p_mfc_ctx *ctx)
ctx->codec_mode);
return -EINVAL;
}
- frame_size = ctx->luma_size;
+ frame_size_lu = ctx->luma_size;
frame_size_ch = ctx->chroma_size;
frame_size_mv = ctx->mv_size;
- mfc_debug(2, "Frm size: %d ch: %d mv: %d\n", frame_size, frame_size_ch,
+ mfc_debug(2, "Frm size: %d ch: %d mv: %d\n", frame_size_lu, frame_size_ch,
frame_size_mv);
for (i = 0; i < ctx->total_dpb_count; i++) {
/* Bank2 */
- mfc_debug(2, "Luma %d: %x\n", i,
+ mfc_debug(2, "Luma %d: %zx\n", i,
ctx->dst_bufs[i].cookie.raw.luma);
mfc_write(dev, OFFSETB(ctx->dst_bufs[i].cookie.raw.luma),
S5P_FIMV_DEC_LUMA_ADR + i * 4);
- mfc_debug(2, "\tChroma %d: %x\n", i,
+ mfc_debug(2, "\tChroma %d: %zx\n", i,
ctx->dst_bufs[i].cookie.raw.chroma);
mfc_write(dev, OFFSETA(ctx->dst_bufs[i].cookie.raw.chroma),
S5P_FIMV_DEC_CHROMA_ADR + i * 4);
if (ctx->codec_mode == S5P_MFC_CODEC_H264_DEC) {
- mfc_debug(2, "\tBuf2: %x, size: %d\n",
+ mfc_debug(2, "\tBuf2: %zx, size: %d\n",
buf_addr2, buf_size2);
mfc_write(dev, OFFSETB(buf_addr2),
S5P_FIMV_H264_MV_ADR + i * 4);
@@ -489,14 +490,14 @@ static int s5p_mfc_set_dec_frame_buffer_v5(struct s5p_mfc_ctx *ctx)
buf_size2 -= frame_size_mv;
}
}
- mfc_debug(2, "Buf1: %u, buf_size1: %d\n", buf_addr1, buf_size1);
+ mfc_debug(2, "Buf1: %zu, buf_size1: %d\n", buf_addr1, buf_size1);
mfc_debug(2, "Buf 1/2 size after: %d/%d (frames %d)\n",
buf_size1, buf_size2, ctx->total_dpb_count);
if (buf_size1 < 0 || buf_size2 < 0) {
mfc_debug(2, "Not enough memory has been allocated\n");
return -ENOMEM;
}
- s5p_mfc_write_info_v5(ctx, frame_size, ALLOC_LUMA_DPB_SIZE);
+ s5p_mfc_write_info_v5(ctx, frame_size_lu, ALLOC_LUMA_DPB_SIZE);
s5p_mfc_write_info_v5(ctx, frame_size_ch, ALLOC_CHROMA_DPB_SIZE);
if (ctx->codec_mode == S5P_MFC_CODEC_H264_DEC)
s5p_mfc_write_info_v5(ctx, frame_size_mv, ALLOC_MV_SIZE);
@@ -566,7 +567,7 @@ static int s5p_mfc_set_enc_ref_buffer_v5(struct s5p_mfc_ctx *ctx)
enc_ref_c_size = ALIGN(guard_width * guard_height,
S5P_FIMV_NV12MT_SALIGN);
}
- mfc_debug(2, "buf_size1: %d, buf_size2: %d\n", buf_size1, buf_size2);
+ mfc_debug(2, "buf_size1: %zu, buf_size2: %zu\n", buf_size1, buf_size2);
switch (ctx->codec_mode) {
case S5P_MFC_CODEC_H264_ENC:
for (i = 0; i < 2; i++) {
@@ -605,7 +606,7 @@ static int s5p_mfc_set_enc_ref_buffer_v5(struct s5p_mfc_ctx *ctx)
S5P_FIMV_H264_NBOR_INFO_ADR);
buf_addr1 += S5P_FIMV_ENC_NBORINFO_SIZE;
buf_size1 -= S5P_FIMV_ENC_NBORINFO_SIZE;
- mfc_debug(2, "buf_size1: %d, buf_size2: %d\n",
+ mfc_debug(2, "buf_size1: %zu, buf_size2: %zu\n",
buf_size1, buf_size2);
break;
case S5P_MFC_CODEC_MPEG4_ENC:
@@ -636,7 +637,7 @@ static int s5p_mfc_set_enc_ref_buffer_v5(struct s5p_mfc_ctx *ctx)
S5P_FIMV_MPEG4_ACDC_COEF_ADR);
buf_addr1 += S5P_FIMV_ENC_ACDCCOEF_SIZE;
buf_size1 -= S5P_FIMV_ENC_ACDCCOEF_SIZE;
- mfc_debug(2, "buf_size1: %d, buf_size2: %d\n",
+ mfc_debug(2, "buf_size1: %zu, buf_size2: %zu\n",
buf_size1, buf_size2);
break;
case S5P_MFC_CODEC_H263_ENC:
@@ -662,7 +663,7 @@ static int s5p_mfc_set_enc_ref_buffer_v5(struct s5p_mfc_ctx *ctx)
mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_H263_ACDC_COEF_ADR);
buf_addr1 += S5P_FIMV_ENC_ACDCCOEF_SIZE;
buf_size1 -= S5P_FIMV_ENC_ACDCCOEF_SIZE;
- mfc_debug(2, "buf_size1: %d, buf_size2: %d\n",
+ mfc_debug(2, "buf_size1: %zu, buf_size2: %zu\n",
buf_size1, buf_size2);
break;
default:
@@ -1186,7 +1187,6 @@ static int s5p_mfc_run_dec_frame(struct s5p_mfc_ctx *ctx, int last_frame)
struct s5p_mfc_dev *dev = ctx->dev;
struct s5p_mfc_buf *temp_vb;
unsigned long flags;
- unsigned int index;
if (ctx->state == MFCINST_FINISHING) {
last_frame = MFC_DEC_LAST_FRAME;
@@ -1211,7 +1211,6 @@ static int s5p_mfc_run_dec_frame(struct s5p_mfc_ctx *ctx, int last_frame)
vb2_dma_contig_plane_dma_addr(temp_vb->b, 0),
ctx->consumed_stream, temp_vb->b->v4l2_planes[0].bytesused);
spin_unlock_irqrestore(&dev->irqlock, flags);
- index = temp_vb->b->v4l2_buf.index;
dev->curr_ctx = ctx->num;
s5p_mfc_clean_ctx_int_flags(ctx);
if (temp_vb->b->v4l2_planes[0].bytesused == 0) {
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c
index c1c12f8d8f68..8798b14bacce 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c
@@ -43,11 +43,6 @@
} while (0)
#endif /* S5P_MFC_DEBUG_REGWRITE */
-#define READL(reg) \
- (WARN_ON_ONCE(!(reg)) ? 0 : readl(reg))
-#define WRITEL(data, reg) \
- (WARN_ON_ONCE(!(reg)) ? 0 : writel((data), (reg)))
-
#define IS_MFCV6_V2(dev) (!IS_MFCV7_PLUS(dev) && dev->fw_ver == MFC_FW_V2)
/* Allocate temporary buffers for decoding */
@@ -105,7 +100,7 @@ static int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx)
mb_width, mb_height),
S5P_FIMV_ME_BUFFER_ALIGN_V6);
- mfc_debug(2, "recon luma size: %d chroma size: %d\n",
+ mfc_debug(2, "recon luma size: %zu chroma size: %zu\n",
ctx->luma_dpb_size, ctx->chroma_dpb_size);
} else {
return -EINVAL;
@@ -416,10 +411,10 @@ static int s5p_mfc_set_dec_stream_buffer_v6(struct s5p_mfc_ctx *ctx,
mfc_debug(2, "inst_no: %d, buf_addr: 0x%08x,\n"
"buf_size: 0x%08x (%d)\n",
ctx->inst_no, buf_addr, strm_size, strm_size);
- WRITEL(strm_size, mfc_regs->d_stream_data_size);
- WRITEL(buf_addr, mfc_regs->d_cpb_buffer_addr);
- WRITEL(buf_size->cpb, mfc_regs->d_cpb_buffer_size);
- WRITEL(start_num_byte, mfc_regs->d_cpb_buffer_offset);
+ writel(strm_size, mfc_regs->d_stream_data_size);
+ writel(buf_addr, mfc_regs->d_cpb_buffer_addr);
+ writel(buf_size->cpb, mfc_regs->d_cpb_buffer_size);
+ writel(start_num_byte, mfc_regs->d_cpb_buffer_offset);
mfc_debug_leave();
return 0;
@@ -443,17 +438,17 @@ static int s5p_mfc_set_dec_frame_buffer_v6(struct s5p_mfc_ctx *ctx)
mfc_debug(2, "Total DPB COUNT: %d\n", ctx->total_dpb_count);
mfc_debug(2, "Setting display delay to %d\n", ctx->display_delay);
- WRITEL(ctx->total_dpb_count, mfc_regs->d_num_dpb);
- WRITEL(ctx->luma_size, mfc_regs->d_first_plane_dpb_size);
- WRITEL(ctx->chroma_size, mfc_regs->d_second_plane_dpb_size);
+ writel(ctx->total_dpb_count, mfc_regs->d_num_dpb);
+ writel(ctx->luma_size, mfc_regs->d_first_plane_dpb_size);
+ writel(ctx->chroma_size, mfc_regs->d_second_plane_dpb_size);
- WRITEL(buf_addr1, mfc_regs->d_scratch_buffer_addr);
- WRITEL(ctx->scratch_buf_size, mfc_regs->d_scratch_buffer_size);
+ writel(buf_addr1, mfc_regs->d_scratch_buffer_addr);
+ writel(ctx->scratch_buf_size, mfc_regs->d_scratch_buffer_size);
if (IS_MFCV8(dev)) {
- WRITEL(ctx->img_width,
+ writel(ctx->img_width,
mfc_regs->d_first_plane_dpb_stride_size);
- WRITEL(ctx->img_width,
+ writel(ctx->img_width,
mfc_regs->d_second_plane_dpb_stride_size);
}
@@ -462,8 +457,8 @@ static int s5p_mfc_set_dec_frame_buffer_v6(struct s5p_mfc_ctx *ctx)
if (ctx->codec_mode == S5P_FIMV_CODEC_H264_DEC ||
ctx->codec_mode == S5P_FIMV_CODEC_H264_MVC_DEC){
- WRITEL(ctx->mv_size, mfc_regs->d_mv_buffer_size);
- WRITEL(ctx->mv_count, mfc_regs->d_num_mv);
+ writel(ctx->mv_size, mfc_regs->d_mv_buffer_size);
+ writel(ctx->mv_count, mfc_regs->d_num_mv);
}
frame_size = ctx->luma_size;
@@ -474,13 +469,13 @@ static int s5p_mfc_set_dec_frame_buffer_v6(struct s5p_mfc_ctx *ctx)
for (i = 0; i < ctx->total_dpb_count; i++) {
/* Bank2 */
- mfc_debug(2, "Luma %d: %x\n", i,
+ mfc_debug(2, "Luma %d: %zx\n", i,
ctx->dst_bufs[i].cookie.raw.luma);
- WRITEL(ctx->dst_bufs[i].cookie.raw.luma,
+ writel(ctx->dst_bufs[i].cookie.raw.luma,
mfc_regs->d_first_plane_dpb + i * 4);
- mfc_debug(2, "\tChroma %d: %x\n", i,
+ mfc_debug(2, "\tChroma %d: %zx\n", i,
ctx->dst_bufs[i].cookie.raw.chroma);
- WRITEL(ctx->dst_bufs[i].cookie.raw.chroma,
+ writel(ctx->dst_bufs[i].cookie.raw.chroma,
mfc_regs->d_second_plane_dpb + i * 4);
}
if (ctx->codec_mode == S5P_MFC_CODEC_H264_DEC ||
@@ -492,23 +487,23 @@ static int s5p_mfc_set_dec_frame_buffer_v6(struct s5p_mfc_ctx *ctx)
align_gap = buf_addr1 - align_gap;
buf_size1 -= align_gap;
- mfc_debug(2, "\tBuf1: %x, size: %d\n",
+ mfc_debug(2, "\tBuf1: %zx, size: %d\n",
buf_addr1, buf_size1);
- WRITEL(buf_addr1, mfc_regs->d_mv_buffer + i * 4);
+ writel(buf_addr1, mfc_regs->d_mv_buffer + i * 4);
buf_addr1 += frame_size_mv;
buf_size1 -= frame_size_mv;
}
}
- mfc_debug(2, "Buf1: %u, buf_size1: %d (frames %d)\n",
+ mfc_debug(2, "Buf1: %zu, buf_size1: %d (frames %d)\n",
buf_addr1, buf_size1, ctx->total_dpb_count);
if (buf_size1 < 0) {
mfc_debug(2, "Not enough memory has been allocated.\n");
return -ENOMEM;
}
- WRITEL(ctx->inst_no, mfc_regs->instance_id);
- s5p_mfc_hw_call(dev->mfc_cmds, cmd_host2risc, dev,
+ writel(ctx->inst_no, mfc_regs->instance_id);
+ s5p_mfc_hw_call_void(dev->mfc_cmds, cmd_host2risc, dev,
S5P_FIMV_CH_INIT_BUFS_V6, NULL);
mfc_debug(2, "After setting buffers.\n");
@@ -522,8 +517,8 @@ static int s5p_mfc_set_enc_stream_buffer_v6(struct s5p_mfc_ctx *ctx,
struct s5p_mfc_dev *dev = ctx->dev;
const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs;
- WRITEL(addr, mfc_regs->e_stream_buffer_addr); /* 16B align */
- WRITEL(size, mfc_regs->e_stream_buffer_size);
+ writel(addr, mfc_regs->e_stream_buffer_addr); /* 16B align */
+ writel(size, mfc_regs->e_stream_buffer_size);
mfc_debug(2, "stream buf addr: 0x%08lx, size: 0x%d\n",
addr, size);
@@ -537,8 +532,8 @@ static void s5p_mfc_set_enc_frame_buffer_v6(struct s5p_mfc_ctx *ctx,
struct s5p_mfc_dev *dev = ctx->dev;
const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs;
- WRITEL(y_addr, mfc_regs->e_source_first_plane_addr);
- WRITEL(c_addr, mfc_regs->e_source_second_plane_addr);
+ writel(y_addr, mfc_regs->e_source_first_plane_addr);
+ writel(c_addr, mfc_regs->e_source_second_plane_addr);
mfc_debug(2, "enc src y buf addr: 0x%08lx\n", y_addr);
mfc_debug(2, "enc src c buf addr: 0x%08lx\n", c_addr);
@@ -551,11 +546,11 @@ static void s5p_mfc_get_enc_frame_buffer_v6(struct s5p_mfc_ctx *ctx,
const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs;
unsigned long enc_recon_y_addr, enc_recon_c_addr;
- *y_addr = READL(mfc_regs->e_encoded_source_first_plane_addr);
- *c_addr = READL(mfc_regs->e_encoded_source_second_plane_addr);
+ *y_addr = readl(mfc_regs->e_encoded_source_first_plane_addr);
+ *c_addr = readl(mfc_regs->e_encoded_source_second_plane_addr);
- enc_recon_y_addr = READL(mfc_regs->e_recon_luma_dpb_addr);
- enc_recon_c_addr = READL(mfc_regs->e_recon_chroma_dpb_addr);
+ enc_recon_y_addr = readl(mfc_regs->e_recon_luma_dpb_addr);
+ enc_recon_c_addr = readl(mfc_regs->e_recon_chroma_dpb_addr);
mfc_debug(2, "recon y addr: 0x%08lx\n", enc_recon_y_addr);
mfc_debug(2, "recon c addr: 0x%08lx\n", enc_recon_c_addr);
@@ -577,36 +572,36 @@ static int s5p_mfc_set_enc_ref_buffer_v6(struct s5p_mfc_ctx *ctx)
mfc_debug(2, "Buf1: %p (%d)\n", (void *)buf_addr1, buf_size1);
for (i = 0; i < ctx->pb_count; i++) {
- WRITEL(buf_addr1, mfc_regs->e_luma_dpb + (4 * i));
+ writel(buf_addr1, mfc_regs->e_luma_dpb + (4 * i));
buf_addr1 += ctx->luma_dpb_size;
- WRITEL(buf_addr1, mfc_regs->e_chroma_dpb + (4 * i));
+ writel(buf_addr1, mfc_regs->e_chroma_dpb + (4 * i));
buf_addr1 += ctx->chroma_dpb_size;
- WRITEL(buf_addr1, mfc_regs->e_me_buffer + (4 * i));
+ writel(buf_addr1, mfc_regs->e_me_buffer + (4 * i));
buf_addr1 += ctx->me_buffer_size;
buf_size1 -= (ctx->luma_dpb_size + ctx->chroma_dpb_size +
ctx->me_buffer_size);
}
- WRITEL(buf_addr1, mfc_regs->e_scratch_buffer_addr);
- WRITEL(ctx->scratch_buf_size, mfc_regs->e_scratch_buffer_size);
+ writel(buf_addr1, mfc_regs->e_scratch_buffer_addr);
+ writel(ctx->scratch_buf_size, mfc_regs->e_scratch_buffer_size);
buf_addr1 += ctx->scratch_buf_size;
buf_size1 -= ctx->scratch_buf_size;
- WRITEL(buf_addr1, mfc_regs->e_tmv_buffer0);
+ writel(buf_addr1, mfc_regs->e_tmv_buffer0);
buf_addr1 += ctx->tmv_buffer_size >> 1;
- WRITEL(buf_addr1, mfc_regs->e_tmv_buffer1);
+ writel(buf_addr1, mfc_regs->e_tmv_buffer1);
buf_addr1 += ctx->tmv_buffer_size >> 1;
buf_size1 -= ctx->tmv_buffer_size;
- mfc_debug(2, "Buf1: %u, buf_size1: %d (ref frames %d)\n",
+ mfc_debug(2, "Buf1: %zu, buf_size1: %d (ref frames %d)\n",
buf_addr1, buf_size1, ctx->pb_count);
if (buf_size1 < 0) {
mfc_debug(2, "Not enough memory has been allocated.\n");
return -ENOMEM;
}
- WRITEL(ctx->inst_no, mfc_regs->instance_id);
- s5p_mfc_hw_call(dev->mfc_cmds, cmd_host2risc, dev,
+ writel(ctx->inst_no, mfc_regs->instance_id);
+ s5p_mfc_hw_call_void(dev->mfc_cmds, cmd_host2risc, dev,
S5P_FIMV_CH_INIT_BUFS_V6, NULL);
mfc_debug_leave();
@@ -621,15 +616,15 @@ static int s5p_mfc_set_slice_mode(struct s5p_mfc_ctx *ctx)
/* multi-slice control */
/* multi-slice MB number or bit size */
- WRITEL(ctx->slice_mode, mfc_regs->e_mslice_mode);
+ writel(ctx->slice_mode, mfc_regs->e_mslice_mode);
if (ctx->slice_mode == V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_MB) {
- WRITEL(ctx->slice_size.mb, mfc_regs->e_mslice_size_mb);
+ writel(ctx->slice_size.mb, mfc_regs->e_mslice_size_mb);
} else if (ctx->slice_mode ==
V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES) {
- WRITEL(ctx->slice_size.bits, mfc_regs->e_mslice_size_bits);
+ writel(ctx->slice_size.bits, mfc_regs->e_mslice_size_bits);
} else {
- WRITEL(0x0, mfc_regs->e_mslice_size_mb);
- WRITEL(0x0, mfc_regs->e_mslice_size_bits);
+ writel(0x0, mfc_regs->e_mslice_size_mb);
+ writel(0x0, mfc_regs->e_mslice_size_bits);
}
return 0;
@@ -645,21 +640,21 @@ static int s5p_mfc_set_enc_params(struct s5p_mfc_ctx *ctx)
mfc_debug_enter();
/* width */
- WRITEL(ctx->img_width, mfc_regs->e_frame_width); /* 16 align */
+ writel(ctx->img_width, mfc_regs->e_frame_width); /* 16 align */
/* height */
- WRITEL(ctx->img_height, mfc_regs->e_frame_height); /* 16 align */
+ writel(ctx->img_height, mfc_regs->e_frame_height); /* 16 align */
/* cropped width */
- WRITEL(ctx->img_width, mfc_regs->e_cropped_frame_width);
+ writel(ctx->img_width, mfc_regs->e_cropped_frame_width);
/* cropped height */
- WRITEL(ctx->img_height, mfc_regs->e_cropped_frame_height);
+ writel(ctx->img_height, mfc_regs->e_cropped_frame_height);
/* cropped offset */
- WRITEL(0x0, mfc_regs->e_frame_crop_offset);
+ writel(0x0, mfc_regs->e_frame_crop_offset);
/* pictype : IDR period */
reg = 0;
reg |= p->gop_size & 0xFFFF;
- WRITEL(reg, mfc_regs->e_gop_config);
+ writel(reg, mfc_regs->e_gop_config);
/* multi-slice control */
/* multi-slice MB number or bit size */
@@ -667,65 +662,65 @@ static int s5p_mfc_set_enc_params(struct s5p_mfc_ctx *ctx)
reg = 0;
if (p->slice_mode == V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_MB) {
reg |= (0x1 << 3);
- WRITEL(reg, mfc_regs->e_enc_options);
+ writel(reg, mfc_regs->e_enc_options);
ctx->slice_size.mb = p->slice_mb;
} else if (p->slice_mode == V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES) {
reg |= (0x1 << 3);
- WRITEL(reg, mfc_regs->e_enc_options);
+ writel(reg, mfc_regs->e_enc_options);
ctx->slice_size.bits = p->slice_bit;
} else {
reg &= ~(0x1 << 3);
- WRITEL(reg, mfc_regs->e_enc_options);
+ writel(reg, mfc_regs->e_enc_options);
}
s5p_mfc_set_slice_mode(ctx);
/* cyclic intra refresh */
- WRITEL(p->intra_refresh_mb, mfc_regs->e_ir_size);
- reg = READL(mfc_regs->e_enc_options);
+ writel(p->intra_refresh_mb, mfc_regs->e_ir_size);
+ reg = readl(mfc_regs->e_enc_options);
if (p->intra_refresh_mb == 0)
reg &= ~(0x1 << 4);
else
reg |= (0x1 << 4);
- WRITEL(reg, mfc_regs->e_enc_options);
+ writel(reg, mfc_regs->e_enc_options);
/* 'NON_REFERENCE_STORE_ENABLE' for debugging */
- reg = READL(mfc_regs->e_enc_options);
+ reg = readl(mfc_regs->e_enc_options);
reg &= ~(0x1 << 9);
- WRITEL(reg, mfc_regs->e_enc_options);
+ writel(reg, mfc_regs->e_enc_options);
/* memory structure cur. frame */
if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_NV12M) {
/* 0: Linear, 1: 2D tiled*/
- reg = READL(mfc_regs->e_enc_options);
+ reg = readl(mfc_regs->e_enc_options);
reg &= ~(0x1 << 7);
- WRITEL(reg, mfc_regs->e_enc_options);
+ writel(reg, mfc_regs->e_enc_options);
/* 0: NV12(CbCr), 1: NV21(CrCb) */
- WRITEL(0x0, mfc_regs->pixel_format);
+ writel(0x0, mfc_regs->pixel_format);
} else if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_NV21M) {
/* 0: Linear, 1: 2D tiled*/
- reg = READL(mfc_regs->e_enc_options);
+ reg = readl(mfc_regs->e_enc_options);
reg &= ~(0x1 << 7);
- WRITEL(reg, mfc_regs->e_enc_options);
+ writel(reg, mfc_regs->e_enc_options);
/* 0: NV12(CbCr), 1: NV21(CrCb) */
- WRITEL(0x1, mfc_regs->pixel_format);
+ writel(0x1, mfc_regs->pixel_format);
} else if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_NV12MT_16X16) {
/* 0: Linear, 1: 2D tiled*/
- reg = READL(mfc_regs->e_enc_options);
+ reg = readl(mfc_regs->e_enc_options);
reg |= (0x1 << 7);
- WRITEL(reg, mfc_regs->e_enc_options);
+ writel(reg, mfc_regs->e_enc_options);
/* 0: NV12(CbCr), 1: NV21(CrCb) */
- WRITEL(0x0, mfc_regs->pixel_format);
+ writel(0x0, mfc_regs->pixel_format);
}
/* memory structure recon. frame */
/* 0: Linear, 1: 2D tiled */
- reg = READL(mfc_regs->e_enc_options);
+ reg = readl(mfc_regs->e_enc_options);
reg |= (0x1 << 8);
- WRITEL(reg, mfc_regs->e_enc_options);
+ writel(reg, mfc_regs->e_enc_options);
/* padding control & value */
- WRITEL(0x0, mfc_regs->e_padding_ctrl);
+ writel(0x0, mfc_regs->e_padding_ctrl);
if (p->pad) {
reg = 0;
/** enable */
@@ -736,64 +731,64 @@ static int s5p_mfc_set_enc_params(struct s5p_mfc_ctx *ctx)
reg |= ((p->pad_cb & 0xFF) << 8);
/** y value */
reg |= p->pad_luma & 0xFF;
- WRITEL(reg, mfc_regs->e_padding_ctrl);
+ writel(reg, mfc_regs->e_padding_ctrl);
}
/* rate control config. */
reg = 0;
/* frame-level rate control */
reg |= ((p->rc_frame & 0x1) << 9);
- WRITEL(reg, mfc_regs->e_rc_config);
+ writel(reg, mfc_regs->e_rc_config);
/* bit rate */
if (p->rc_frame)
- WRITEL(p->rc_bitrate,
+ writel(p->rc_bitrate,
mfc_regs->e_rc_bit_rate);
else
- WRITEL(1, mfc_regs->e_rc_bit_rate);
+ writel(1, mfc_regs->e_rc_bit_rate);
/* reaction coefficient */
if (p->rc_frame) {
if (p->rc_reaction_coeff < TIGHT_CBR_MAX) /* tight CBR */
- WRITEL(1, mfc_regs->e_rc_mode);
+ writel(1, mfc_regs->e_rc_mode);
else /* loose CBR */
- WRITEL(2, mfc_regs->e_rc_mode);
+ writel(2, mfc_regs->e_rc_mode);
}
/* seq header ctrl */
- reg = READL(mfc_regs->e_enc_options);
+ reg = readl(mfc_regs->e_enc_options);
reg &= ~(0x1 << 2);
reg |= ((p->seq_hdr_mode & 0x1) << 2);
/* frame skip mode */
reg &= ~(0x3);
reg |= (p->frame_skip_mode & 0x3);
- WRITEL(reg, mfc_regs->e_enc_options);
+ writel(reg, mfc_regs->e_enc_options);
/* 'DROP_CONTROL_ENABLE', disable */
- reg = READL(mfc_regs->e_rc_config);
+ reg = readl(mfc_regs->e_rc_config);
reg &= ~(0x1 << 10);
- WRITEL(reg, mfc_regs->e_rc_config);
+ writel(reg, mfc_regs->e_rc_config);
/* setting for MV range [16, 256] */
reg = (p->mv_h_range & S5P_FIMV_E_MV_RANGE_V6_MASK);
- WRITEL(reg, mfc_regs->e_mv_hor_range);
+ writel(reg, mfc_regs->e_mv_hor_range);
reg = (p->mv_v_range & S5P_FIMV_E_MV_RANGE_V6_MASK);
- WRITEL(reg, mfc_regs->e_mv_ver_range);
+ writel(reg, mfc_regs->e_mv_ver_range);
- WRITEL(0x0, mfc_regs->e_frame_insertion);
- WRITEL(0x0, mfc_regs->e_roi_buffer_addr);
- WRITEL(0x0, mfc_regs->e_param_change);
- WRITEL(0x0, mfc_regs->e_rc_roi_ctrl);
- WRITEL(0x0, mfc_regs->e_picture_tag);
+ writel(0x0, mfc_regs->e_frame_insertion);
+ writel(0x0, mfc_regs->e_roi_buffer_addr);
+ writel(0x0, mfc_regs->e_param_change);
+ writel(0x0, mfc_regs->e_rc_roi_ctrl);
+ writel(0x0, mfc_regs->e_picture_tag);
- WRITEL(0x0, mfc_regs->e_bit_count_enable);
- WRITEL(0x0, mfc_regs->e_max_bit_count);
- WRITEL(0x0, mfc_regs->e_min_bit_count);
+ writel(0x0, mfc_regs->e_bit_count_enable);
+ writel(0x0, mfc_regs->e_max_bit_count);
+ writel(0x0, mfc_regs->e_min_bit_count);
- WRITEL(0x0, mfc_regs->e_metadata_buffer_addr);
- WRITEL(0x0, mfc_regs->e_metadata_buffer_size);
+ writel(0x0, mfc_regs->e_metadata_buffer_addr);
+ writel(0x0, mfc_regs->e_metadata_buffer_size);
mfc_debug_leave();
@@ -814,10 +809,10 @@ static int s5p_mfc_set_enc_params_h264(struct s5p_mfc_ctx *ctx)
s5p_mfc_set_enc_params(ctx);
/* pictype : number of B */
- reg = READL(mfc_regs->e_gop_config);
+ reg = readl(mfc_regs->e_gop_config);
reg &= ~(0x3 << 16);
reg |= ((p->num_b_frame & 0x3) << 16);
- WRITEL(reg, mfc_regs->e_gop_config);
+ writel(reg, mfc_regs->e_gop_config);
/* profile & level */
reg = 0;
@@ -825,19 +820,19 @@ static int s5p_mfc_set_enc_params_h264(struct s5p_mfc_ctx *ctx)
reg |= ((p_h264->level & 0xFF) << 8);
/** profile - 0 ~ 3 */
reg |= p_h264->profile & 0x3F;
- WRITEL(reg, mfc_regs->e_picture_profile);
+ writel(reg, mfc_regs->e_picture_profile);
/* rate control config. */
- reg = READL(mfc_regs->e_rc_config);
+ reg = readl(mfc_regs->e_rc_config);
/** macroblock level rate control */
reg &= ~(0x1 << 8);
reg |= ((p->rc_mb & 0x1) << 8);
- WRITEL(reg, mfc_regs->e_rc_config);
+ writel(reg, mfc_regs->e_rc_config);
/** frame QP */
reg &= ~(0x3F);
reg |= p_h264->rc_frame_qp & 0x3F;
- WRITEL(reg, mfc_regs->e_rc_config);
+ writel(reg, mfc_regs->e_rc_config);
/* max & min value of QP */
reg = 0;
@@ -845,16 +840,16 @@ static int s5p_mfc_set_enc_params_h264(struct s5p_mfc_ctx *ctx)
reg |= ((p_h264->rc_max_qp & 0x3F) << 8);
/** min QP */
reg |= p_h264->rc_min_qp & 0x3F;
- WRITEL(reg, mfc_regs->e_rc_qp_bound);
+ writel(reg, mfc_regs->e_rc_qp_bound);
/* other QPs */
- WRITEL(0x0, mfc_regs->e_fixed_picture_qp);
+ writel(0x0, mfc_regs->e_fixed_picture_qp);
if (!p->rc_frame && !p->rc_mb) {
reg = 0;
reg |= ((p_h264->rc_b_frame_qp & 0x3F) << 16);
reg |= ((p_h264->rc_p_frame_qp & 0x3F) << 8);
reg |= p_h264->rc_frame_qp & 0x3F;
- WRITEL(reg, mfc_regs->e_fixed_picture_qp);
+ writel(reg, mfc_regs->e_fixed_picture_qp);
}
/* frame rate */
@@ -862,38 +857,38 @@ static int s5p_mfc_set_enc_params_h264(struct s5p_mfc_ctx *ctx)
reg = 0;
reg |= ((p->rc_framerate_num & 0xFFFF) << 16);
reg |= p->rc_framerate_denom & 0xFFFF;
- WRITEL(reg, mfc_regs->e_rc_frame_rate);
+ writel(reg, mfc_regs->e_rc_frame_rate);
}
/* vbv buffer size */
if (p->frame_skip_mode ==
V4L2_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE_BUF_LIMIT) {
- WRITEL(p_h264->cpb_size & 0xFFFF,
+ writel(p_h264->cpb_size & 0xFFFF,
mfc_regs->e_vbv_buffer_size);
if (p->rc_frame)
- WRITEL(p->vbv_delay, mfc_regs->e_vbv_init_delay);
+ writel(p->vbv_delay, mfc_regs->e_vbv_init_delay);
}
/* interlace */
reg = 0;
reg |= ((p_h264->interlace & 0x1) << 3);
- WRITEL(reg, mfc_regs->e_h264_options);
+ writel(reg, mfc_regs->e_h264_options);
/* height */
if (p_h264->interlace) {
- WRITEL(ctx->img_height >> 1,
+ writel(ctx->img_height >> 1,
mfc_regs->e_frame_height); /* 32 align */
/* cropped height */
- WRITEL(ctx->img_height >> 1,
+ writel(ctx->img_height >> 1,
mfc_regs->e_cropped_frame_height);
}
/* loop filter ctrl */
- reg = READL(mfc_regs->e_h264_options);
+ reg = readl(mfc_regs->e_h264_options);
reg &= ~(0x3 << 1);
reg |= ((p_h264->loop_filter_mode & 0x3) << 1);
- WRITEL(reg, mfc_regs->e_h264_options);
+ writel(reg, mfc_regs->e_h264_options);
/* loopfilter alpha offset */
if (p_h264->loop_filter_alpha < 0) {
@@ -903,7 +898,7 @@ static int s5p_mfc_set_enc_params_h264(struct s5p_mfc_ctx *ctx)
reg = 0x00;
reg |= (p_h264->loop_filter_alpha & 0xF);
}
- WRITEL(reg, mfc_regs->e_h264_lf_alpha_offset);
+ writel(reg, mfc_regs->e_h264_lf_alpha_offset);
/* loopfilter beta offset */
if (p_h264->loop_filter_beta < 0) {
@@ -913,28 +908,28 @@ static int s5p_mfc_set_enc_params_h264(struct s5p_mfc_ctx *ctx)
reg = 0x00;
reg |= (p_h264->loop_filter_beta & 0xF);
}
- WRITEL(reg, mfc_regs->e_h264_lf_beta_offset);
+ writel(reg, mfc_regs->e_h264_lf_beta_offset);
/* entropy coding mode */
- reg = READL(mfc_regs->e_h264_options);
+ reg = readl(mfc_regs->e_h264_options);
reg &= ~(0x1);
reg |= p_h264->entropy_mode & 0x1;
- WRITEL(reg, mfc_regs->e_h264_options);
+ writel(reg, mfc_regs->e_h264_options);
/* number of ref. picture */
- reg = READL(mfc_regs->e_h264_options);
+ reg = readl(mfc_regs->e_h264_options);
reg &= ~(0x1 << 7);
reg |= (((p_h264->num_ref_pic_4p - 1) & 0x1) << 7);
- WRITEL(reg, mfc_regs->e_h264_options);
+ writel(reg, mfc_regs->e_h264_options);
/* 8x8 transform enable */
- reg = READL(mfc_regs->e_h264_options);
+ reg = readl(mfc_regs->e_h264_options);
reg &= ~(0x3 << 12);
reg |= ((p_h264->_8x8_transform & 0x3) << 12);
- WRITEL(reg, mfc_regs->e_h264_options);
+ writel(reg, mfc_regs->e_h264_options);
/* macroblock adaptive scaling features */
- WRITEL(0x0, mfc_regs->e_mb_rc_config);
+ writel(0x0, mfc_regs->e_mb_rc_config);
if (p->rc_mb) {
reg = 0;
/** dark region */
@@ -945,95 +940,95 @@ static int s5p_mfc_set_enc_params_h264(struct s5p_mfc_ctx *ctx)
reg |= ((p_h264->rc_mb_static & 0x1) << 1);
/** high activity region */
reg |= p_h264->rc_mb_activity & 0x1;
- WRITEL(reg, mfc_regs->e_mb_rc_config);
+ writel(reg, mfc_regs->e_mb_rc_config);
}
/* aspect ratio VUI */
- READL(mfc_regs->e_h264_options);
+ readl(mfc_regs->e_h264_options);
reg &= ~(0x1 << 5);
reg |= ((p_h264->vui_sar & 0x1) << 5);
- WRITEL(reg, mfc_regs->e_h264_options);
+ writel(reg, mfc_regs->e_h264_options);
- WRITEL(0x0, mfc_regs->e_aspect_ratio);
- WRITEL(0x0, mfc_regs->e_extended_sar);
+ writel(0x0, mfc_regs->e_aspect_ratio);
+ writel(0x0, mfc_regs->e_extended_sar);
if (p_h264->vui_sar) {
/* aspect ration IDC */
reg = 0;
reg |= p_h264->vui_sar_idc & 0xFF;
- WRITEL(reg, mfc_regs->e_aspect_ratio);
+ writel(reg, mfc_regs->e_aspect_ratio);
if (p_h264->vui_sar_idc == 0xFF) {
/* extended SAR */
reg = 0;
reg |= (p_h264->vui_ext_sar_width & 0xFFFF) << 16;
reg |= p_h264->vui_ext_sar_height & 0xFFFF;
- WRITEL(reg, mfc_regs->e_extended_sar);
+ writel(reg, mfc_regs->e_extended_sar);
}
}
/* intra picture period for H.264 open GOP */
/* control */
- READL(mfc_regs->e_h264_options);
+ readl(mfc_regs->e_h264_options);
reg &= ~(0x1 << 4);
reg |= ((p_h264->open_gop & 0x1) << 4);
- WRITEL(reg, mfc_regs->e_h264_options);
+ writel(reg, mfc_regs->e_h264_options);
/* value */
- WRITEL(0x0, mfc_regs->e_h264_i_period);
+ writel(0x0, mfc_regs->e_h264_i_period);
if (p_h264->open_gop) {
reg = 0;
reg |= p_h264->open_gop_size & 0xFFFF;
- WRITEL(reg, mfc_regs->e_h264_i_period);
+ writel(reg, mfc_regs->e_h264_i_period);
}
/* 'WEIGHTED_BI_PREDICTION' for B is disable */
- READL(mfc_regs->e_h264_options);
+ readl(mfc_regs->e_h264_options);
reg &= ~(0x3 << 9);
- WRITEL(reg, mfc_regs->e_h264_options);
+ writel(reg, mfc_regs->e_h264_options);
/* 'CONSTRAINED_INTRA_PRED_ENABLE' is disable */
- READL(mfc_regs->e_h264_options);
+ readl(mfc_regs->e_h264_options);
reg &= ~(0x1 << 14);
- WRITEL(reg, mfc_regs->e_h264_options);
+ writel(reg, mfc_regs->e_h264_options);
/* ASO */
- READL(mfc_regs->e_h264_options);
+ readl(mfc_regs->e_h264_options);
reg &= ~(0x1 << 6);
reg |= ((p_h264->aso & 0x1) << 6);
- WRITEL(reg, mfc_regs->e_h264_options);
+ writel(reg, mfc_regs->e_h264_options);
/* hier qp enable */
- READL(mfc_regs->e_h264_options);
+ readl(mfc_regs->e_h264_options);
reg &= ~(0x1 << 8);
reg |= ((p_h264->open_gop & 0x1) << 8);
- WRITEL(reg, mfc_regs->e_h264_options);
+ writel(reg, mfc_regs->e_h264_options);
reg = 0;
if (p_h264->hier_qp && p_h264->hier_qp_layer) {
reg |= (p_h264->hier_qp_type & 0x1) << 0x3;
reg |= p_h264->hier_qp_layer & 0x7;
- WRITEL(reg, mfc_regs->e_h264_num_t_layer);
+ writel(reg, mfc_regs->e_h264_num_t_layer);
/* QP value for each layer */
for (i = 0; i < p_h264->hier_qp_layer &&
i < ARRAY_SIZE(p_h264->hier_qp_layer_qp); i++) {
- WRITEL(p_h264->hier_qp_layer_qp[i],
+ writel(p_h264->hier_qp_layer_qp[i],
mfc_regs->e_h264_hierarchical_qp_layer0
+ i * 4);
}
}
/* number of coding layer should be zero when hierarchical is disable */
- WRITEL(reg, mfc_regs->e_h264_num_t_layer);
+ writel(reg, mfc_regs->e_h264_num_t_layer);
/* frame packing SEI generation */
- READL(mfc_regs->e_h264_options);
+ readl(mfc_regs->e_h264_options);
reg &= ~(0x1 << 25);
reg |= ((p_h264->sei_frame_packing & 0x1) << 25);
- WRITEL(reg, mfc_regs->e_h264_options);
+ writel(reg, mfc_regs->e_h264_options);
if (p_h264->sei_frame_packing) {
reg = 0;
/** current frame0 flag */
reg |= ((p_h264->sei_fp_curr_frame_0 & 0x1) << 2);
/** arrangement type */
reg |= p_h264->sei_fp_arrangement_type & 0x3;
- WRITEL(reg, mfc_regs->e_h264_frame_packing_sei_info);
+ writel(reg, mfc_regs->e_h264_frame_packing_sei_info);
}
if (p_h264->fmo) {
@@ -1042,7 +1037,7 @@ static int s5p_mfc_set_enc_params_h264(struct s5p_mfc_ctx *ctx)
if (p_h264->fmo_slice_grp > 4)
p_h264->fmo_slice_grp = 4;
for (i = 0; i < (p_h264->fmo_slice_grp & 0xF); i++)
- WRITEL(p_h264->fmo_run_len[i] - 1,
+ writel(p_h264->fmo_run_len[i] - 1,
mfc_regs->e_h264_fmo_run_length_minus1_0
+ i * 4);
break;
@@ -1054,10 +1049,10 @@ static int s5p_mfc_set_enc_params_h264(struct s5p_mfc_ctx *ctx)
case V4L2_MPEG_VIDEO_H264_FMO_MAP_TYPE_WIPE_SCAN:
if (p_h264->fmo_slice_grp > 2)
p_h264->fmo_slice_grp = 2;
- WRITEL(p_h264->fmo_chg_dir & 0x1,
+ writel(p_h264->fmo_chg_dir & 0x1,
mfc_regs->e_h264_fmo_slice_grp_change_dir);
/* the valid range is 0 ~ number of macroblocks -1 */
- WRITEL(p_h264->fmo_chg_rate,
+ writel(p_h264->fmo_chg_rate,
mfc_regs->e_h264_fmo_slice_grp_change_rate_minus1);
break;
default:
@@ -1068,12 +1063,12 @@ static int s5p_mfc_set_enc_params_h264(struct s5p_mfc_ctx *ctx)
break;
}
- WRITEL(p_h264->fmo_map_type,
+ writel(p_h264->fmo_map_type,
mfc_regs->e_h264_fmo_slice_grp_map_type);
- WRITEL(p_h264->fmo_slice_grp - 1,
+ writel(p_h264->fmo_slice_grp - 1,
mfc_regs->e_h264_fmo_num_slice_grp_minus1);
} else {
- WRITEL(0, mfc_regs->e_h264_fmo_num_slice_grp_minus1);
+ writel(0, mfc_regs->e_h264_fmo_num_slice_grp_minus1);
}
mfc_debug_leave();
@@ -1094,10 +1089,10 @@ static int s5p_mfc_set_enc_params_mpeg4(struct s5p_mfc_ctx *ctx)
s5p_mfc_set_enc_params(ctx);
/* pictype : number of B */
- reg = READL(mfc_regs->e_gop_config);
+ reg = readl(mfc_regs->e_gop_config);
reg &= ~(0x3 << 16);
reg |= ((p->num_b_frame & 0x3) << 16);
- WRITEL(reg, mfc_regs->e_gop_config);
+ writel(reg, mfc_regs->e_gop_config);
/* profile & level */
reg = 0;
@@ -1105,19 +1100,19 @@ static int s5p_mfc_set_enc_params_mpeg4(struct s5p_mfc_ctx *ctx)
reg |= ((p_mpeg4->level & 0xFF) << 8);
/** profile - 0 ~ 1 */
reg |= p_mpeg4->profile & 0x3F;
- WRITEL(reg, mfc_regs->e_picture_profile);
+ writel(reg, mfc_regs->e_picture_profile);
/* rate control config. */
- reg = READL(mfc_regs->e_rc_config);
+ reg = readl(mfc_regs->e_rc_config);
/** macroblock level rate control */
reg &= ~(0x1 << 8);
reg |= ((p->rc_mb & 0x1) << 8);
- WRITEL(reg, mfc_regs->e_rc_config);
+ writel(reg, mfc_regs->e_rc_config);
/** frame QP */
reg &= ~(0x3F);
reg |= p_mpeg4->rc_frame_qp & 0x3F;
- WRITEL(reg, mfc_regs->e_rc_config);
+ writel(reg, mfc_regs->e_rc_config);
/* max & min value of QP */
reg = 0;
@@ -1125,16 +1120,16 @@ static int s5p_mfc_set_enc_params_mpeg4(struct s5p_mfc_ctx *ctx)
reg |= ((p_mpeg4->rc_max_qp & 0x3F) << 8);
/** min QP */
reg |= p_mpeg4->rc_min_qp & 0x3F;
- WRITEL(reg, mfc_regs->e_rc_qp_bound);
+ writel(reg, mfc_regs->e_rc_qp_bound);
/* other QPs */
- WRITEL(0x0, mfc_regs->e_fixed_picture_qp);
+ writel(0x0, mfc_regs->e_fixed_picture_qp);
if (!p->rc_frame && !p->rc_mb) {
reg = 0;
reg |= ((p_mpeg4->rc_b_frame_qp & 0x3F) << 16);
reg |= ((p_mpeg4->rc_p_frame_qp & 0x3F) << 8);
reg |= p_mpeg4->rc_frame_qp & 0x3F;
- WRITEL(reg, mfc_regs->e_fixed_picture_qp);
+ writel(reg, mfc_regs->e_fixed_picture_qp);
}
/* frame rate */
@@ -1142,21 +1137,21 @@ static int s5p_mfc_set_enc_params_mpeg4(struct s5p_mfc_ctx *ctx)
reg = 0;
reg |= ((p->rc_framerate_num & 0xFFFF) << 16);
reg |= p->rc_framerate_denom & 0xFFFF;
- WRITEL(reg, mfc_regs->e_rc_frame_rate);
+ writel(reg, mfc_regs->e_rc_frame_rate);
}
/* vbv buffer size */
if (p->frame_skip_mode ==
V4L2_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE_BUF_LIMIT) {
- WRITEL(p->vbv_size & 0xFFFF, mfc_regs->e_vbv_buffer_size);
+ writel(p->vbv_size & 0xFFFF, mfc_regs->e_vbv_buffer_size);
if (p->rc_frame)
- WRITEL(p->vbv_delay, mfc_regs->e_vbv_init_delay);
+ writel(p->vbv_delay, mfc_regs->e_vbv_init_delay);
}
/* Disable HEC */
- WRITEL(0x0, mfc_regs->e_mpeg4_options);
- WRITEL(0x0, mfc_regs->e_mpeg4_hec_period);
+ writel(0x0, mfc_regs->e_mpeg4_options);
+ writel(0x0, mfc_regs->e_mpeg4_hec_period);
mfc_debug_leave();
@@ -1179,19 +1174,19 @@ static int s5p_mfc_set_enc_params_h263(struct s5p_mfc_ctx *ctx)
reg = 0;
/** profile */
reg |= (0x1 << 4);
- WRITEL(reg, mfc_regs->e_picture_profile);
+ writel(reg, mfc_regs->e_picture_profile);
/* rate control config. */
- reg = READL(mfc_regs->e_rc_config);
+ reg = readl(mfc_regs->e_rc_config);
/** macroblock level rate control */
reg &= ~(0x1 << 8);
reg |= ((p->rc_mb & 0x1) << 8);
- WRITEL(reg, mfc_regs->e_rc_config);
+ writel(reg, mfc_regs->e_rc_config);
/** frame QP */
reg &= ~(0x3F);
reg |= p_h263->rc_frame_qp & 0x3F;
- WRITEL(reg, mfc_regs->e_rc_config);
+ writel(reg, mfc_regs->e_rc_config);
/* max & min value of QP */
reg = 0;
@@ -1199,16 +1194,16 @@ static int s5p_mfc_set_enc_params_h263(struct s5p_mfc_ctx *ctx)
reg |= ((p_h263->rc_max_qp & 0x3F) << 8);
/** min QP */
reg |= p_h263->rc_min_qp & 0x3F;
- WRITEL(reg, mfc_regs->e_rc_qp_bound);
+ writel(reg, mfc_regs->e_rc_qp_bound);
/* other QPs */
- WRITEL(0x0, mfc_regs->e_fixed_picture_qp);
+ writel(0x0, mfc_regs->e_fixed_picture_qp);
if (!p->rc_frame && !p->rc_mb) {
reg = 0;
reg |= ((p_h263->rc_b_frame_qp & 0x3F) << 16);
reg |= ((p_h263->rc_p_frame_qp & 0x3F) << 8);
reg |= p_h263->rc_frame_qp & 0x3F;
- WRITEL(reg, mfc_regs->e_fixed_picture_qp);
+ writel(reg, mfc_regs->e_fixed_picture_qp);
}
/* frame rate */
@@ -1216,16 +1211,16 @@ static int s5p_mfc_set_enc_params_h263(struct s5p_mfc_ctx *ctx)
reg = 0;
reg |= ((p->rc_framerate_num & 0xFFFF) << 16);
reg |= p->rc_framerate_denom & 0xFFFF;
- WRITEL(reg, mfc_regs->e_rc_frame_rate);
+ writel(reg, mfc_regs->e_rc_frame_rate);
}
/* vbv buffer size */
if (p->frame_skip_mode ==
V4L2_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE_BUF_LIMIT) {
- WRITEL(p->vbv_size & 0xFFFF, mfc_regs->e_vbv_buffer_size);
+ writel(p->vbv_size & 0xFFFF, mfc_regs->e_vbv_buffer_size);
if (p->rc_frame)
- WRITEL(p->vbv_delay, mfc_regs->e_vbv_init_delay);
+ writel(p->vbv_delay, mfc_regs->e_vbv_init_delay);
}
mfc_debug_leave();
@@ -1247,57 +1242,57 @@ static int s5p_mfc_set_enc_params_vp8(struct s5p_mfc_ctx *ctx)
s5p_mfc_set_enc_params(ctx);
/* pictype : number of B */
- reg = READL(mfc_regs->e_gop_config);
+ reg = readl(mfc_regs->e_gop_config);
reg &= ~(0x3 << 16);
reg |= ((p->num_b_frame & 0x3) << 16);
- WRITEL(reg, mfc_regs->e_gop_config);
+ writel(reg, mfc_regs->e_gop_config);
/* profile - 0 ~ 3 */
reg = p_vp8->profile & 0x3;
- WRITEL(reg, mfc_regs->e_picture_profile);
+ writel(reg, mfc_regs->e_picture_profile);
/* rate control config. */
- reg = READL(mfc_regs->e_rc_config);
+ reg = readl(mfc_regs->e_rc_config);
/** macroblock level rate control */
reg &= ~(0x1 << 8);
reg |= ((p->rc_mb & 0x1) << 8);
- WRITEL(reg, mfc_regs->e_rc_config);
+ writel(reg, mfc_regs->e_rc_config);
/* frame rate */
if (p->rc_frame && p->rc_framerate_num && p->rc_framerate_denom) {
reg = 0;
reg |= ((p->rc_framerate_num & 0xFFFF) << 16);
reg |= p->rc_framerate_denom & 0xFFFF;
- WRITEL(reg, mfc_regs->e_rc_frame_rate);
+ writel(reg, mfc_regs->e_rc_frame_rate);
}
/* frame QP */
reg &= ~(0x7F);
reg |= p_vp8->rc_frame_qp & 0x7F;
- WRITEL(reg, mfc_regs->e_rc_config);
+ writel(reg, mfc_regs->e_rc_config);
/* other QPs */
- WRITEL(0x0, mfc_regs->e_fixed_picture_qp);
+ writel(0x0, mfc_regs->e_fixed_picture_qp);
if (!p->rc_frame && !p->rc_mb) {
reg = 0;
reg |= ((p_vp8->rc_p_frame_qp & 0x7F) << 8);
reg |= p_vp8->rc_frame_qp & 0x7F;
- WRITEL(reg, mfc_regs->e_fixed_picture_qp);
+ writel(reg, mfc_regs->e_fixed_picture_qp);
}
/* max QP */
reg = ((p_vp8->rc_max_qp & 0x7F) << 8);
/* min QP */
reg |= p_vp8->rc_min_qp & 0x7F;
- WRITEL(reg, mfc_regs->e_rc_qp_bound);
+ writel(reg, mfc_regs->e_rc_qp_bound);
/* vbv buffer size */
if (p->frame_skip_mode ==
V4L2_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE_BUF_LIMIT) {
- WRITEL(p->vbv_size & 0xFFFF, mfc_regs->e_vbv_buffer_size);
+ writel(p->vbv_size & 0xFFFF, mfc_regs->e_vbv_buffer_size);
if (p->rc_frame)
- WRITEL(p->vbv_delay, mfc_regs->e_vbv_init_delay);
+ writel(p->vbv_delay, mfc_regs->e_vbv_init_delay);
}
/* VP8 specific params */
@@ -1319,7 +1314,7 @@ static int s5p_mfc_set_enc_params_vp8(struct s5p_mfc_ctx *ctx)
}
reg |= (val & 0xF) << 3;
reg |= (p_vp8->num_ref & 0x2);
- WRITEL(reg, mfc_regs->e_vp8_options);
+ writel(reg, mfc_regs->e_vp8_options);
mfc_debug_leave();
@@ -1338,9 +1333,9 @@ static int s5p_mfc_init_decode_v6(struct s5p_mfc_ctx *ctx)
mfc_debug(2, "InstNo: %d/%d\n", ctx->inst_no,
S5P_FIMV_CH_SEQ_HEADER_V6);
mfc_debug(2, "BUFs: %08x %08x %08x\n",
- READL(mfc_regs->d_cpb_buffer_addr),
- READL(mfc_regs->d_cpb_buffer_addr),
- READL(mfc_regs->d_cpb_buffer_addr));
+ readl(mfc_regs->d_cpb_buffer_addr),
+ readl(mfc_regs->d_cpb_buffer_addr),
+ readl(mfc_regs->d_cpb_buffer_addr));
/* FMO_ASO_CTRL - 0: Enable, 1: Disable */
reg |= (fmo_aso_ctrl << S5P_FIMV_D_OPT_FMO_ASO_CTRL_MASK_V6);
@@ -1351,11 +1346,11 @@ static int s5p_mfc_init_decode_v6(struct s5p_mfc_ctx *ctx)
* set to negative value. */
if (ctx->display_delay >= 0) {
reg |= (0x1 << S5P_FIMV_D_OPT_DDELAY_EN_SHIFT_V6);
- WRITEL(ctx->display_delay, mfc_regs->d_display_delay);
+ writel(ctx->display_delay, mfc_regs->d_display_delay);
}
if (IS_MFCV7_PLUS(dev) || IS_MFCV6_V2(dev)) {
- WRITEL(reg, mfc_regs->d_dec_options);
+ writel(reg, mfc_regs->d_dec_options);
reg = 0;
}
@@ -1370,22 +1365,22 @@ static int s5p_mfc_init_decode_v6(struct s5p_mfc_ctx *ctx)
reg |= (0x1 << S5P_FIMV_D_OPT_TILE_MODE_SHIFT_V6);
if (IS_MFCV7_PLUS(dev) || IS_MFCV6_V2(dev))
- WRITEL(reg, mfc_regs->d_init_buffer_options);
+ writel(reg, mfc_regs->d_init_buffer_options);
else
- WRITEL(reg, mfc_regs->d_dec_options);
+ writel(reg, mfc_regs->d_dec_options);
/* 0: NV12(CbCr), 1: NV21(CrCb) */
if (ctx->dst_fmt->fourcc == V4L2_PIX_FMT_NV21M)
- WRITEL(0x1, mfc_regs->pixel_format);
+ writel(0x1, mfc_regs->pixel_format);
else
- WRITEL(0x0, mfc_regs->pixel_format);
+ writel(0x0, mfc_regs->pixel_format);
/* sei parse */
- WRITEL(ctx->sei_fp_parse & 0x1, mfc_regs->d_sei_enable);
+ writel(ctx->sei_fp_parse & 0x1, mfc_regs->d_sei_enable);
- WRITEL(ctx->inst_no, mfc_regs->instance_id);
- s5p_mfc_hw_call(dev->mfc_cmds, cmd_host2risc, dev,
+ writel(ctx->inst_no, mfc_regs->instance_id);
+ s5p_mfc_hw_call_void(dev->mfc_cmds, cmd_host2risc, dev,
S5P_FIMV_CH_SEQ_HEADER_V6, NULL);
mfc_debug_leave();
@@ -1400,8 +1395,8 @@ static inline void s5p_mfc_set_flush(struct s5p_mfc_ctx *ctx, int flush)
if (flush) {
dev->curr_ctx = ctx->num;
s5p_mfc_clean_ctx_int_flags(ctx);
- WRITEL(ctx->inst_no, mfc_regs->instance_id);
- s5p_mfc_hw_call(dev->mfc_cmds, cmd_host2risc, dev,
+ writel(ctx->inst_no, mfc_regs->instance_id);
+ s5p_mfc_hw_call_void(dev->mfc_cmds, cmd_host2risc, dev,
S5P_FIMV_H2R_CMD_FLUSH_V6, NULL);
}
}
@@ -1413,19 +1408,19 @@ static int s5p_mfc_decode_one_frame_v6(struct s5p_mfc_ctx *ctx,
struct s5p_mfc_dev *dev = ctx->dev;
const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs;
- WRITEL(ctx->dec_dst_flag, mfc_regs->d_available_dpb_flag_lower);
- WRITEL(ctx->slice_interface & 0x1, mfc_regs->d_slice_if_enable);
+ writel(ctx->dec_dst_flag, mfc_regs->d_available_dpb_flag_lower);
+ writel(ctx->slice_interface & 0x1, mfc_regs->d_slice_if_enable);
- WRITEL(ctx->inst_no, mfc_regs->instance_id);
+ writel(ctx->inst_no, mfc_regs->instance_id);
/* Issue different commands to instance basing on whether it
* is the last frame or not. */
switch (last_frame) {
case 0:
- s5p_mfc_hw_call(dev->mfc_cmds, cmd_host2risc, dev,
+ s5p_mfc_hw_call_void(dev->mfc_cmds, cmd_host2risc, dev,
S5P_FIMV_CH_FRAME_START_V6, NULL);
break;
case 1:
- s5p_mfc_hw_call(dev->mfc_cmds, cmd_host2risc, dev,
+ s5p_mfc_hw_call_void(dev->mfc_cmds, cmd_host2risc, dev,
S5P_FIMV_CH_LAST_FRAME_V6, NULL);
break;
default:
@@ -1458,12 +1453,12 @@ static int s5p_mfc_init_encode_v6(struct s5p_mfc_ctx *ctx)
/* Set stride lengths for v7 & above */
if (IS_MFCV7_PLUS(dev)) {
- WRITEL(ctx->img_width, mfc_regs->e_source_first_plane_stride);
- WRITEL(ctx->img_width, mfc_regs->e_source_second_plane_stride);
+ writel(ctx->img_width, mfc_regs->e_source_first_plane_stride);
+ writel(ctx->img_width, mfc_regs->e_source_second_plane_stride);
}
- WRITEL(ctx->inst_no, mfc_regs->instance_id);
- s5p_mfc_hw_call(dev->mfc_cmds, cmd_host2risc, dev,
+ writel(ctx->inst_no, mfc_regs->instance_id);
+ s5p_mfc_hw_call_void(dev->mfc_cmds, cmd_host2risc, dev,
S5P_FIMV_CH_SEQ_HEADER_V6, NULL);
return 0;
@@ -1479,7 +1474,7 @@ static int s5p_mfc_h264_set_aso_slice_order_v6(struct s5p_mfc_ctx *ctx)
if (p_h264->aso) {
for (i = 0; i < ARRAY_SIZE(p_h264->aso_slice_order); i++) {
- WRITEL(p_h264->aso_slice_order[i],
+ writel(p_h264->aso_slice_order[i],
mfc_regs->e_h264_aso_slice_order_0 + i * 4);
}
}
@@ -1501,8 +1496,8 @@ static int s5p_mfc_encode_one_frame_v6(struct s5p_mfc_ctx *ctx)
s5p_mfc_set_slice_mode(ctx);
- WRITEL(ctx->inst_no, mfc_regs->instance_id);
- s5p_mfc_hw_call(dev->mfc_cmds, cmd_host2risc, dev,
+ writel(ctx->inst_no, mfc_regs->instance_id);
+ s5p_mfc_hw_call_void(dev->mfc_cmds, cmd_host2risc, dev,
S5P_FIMV_CH_FRAME_START_V6, NULL);
mfc_debug(2, "--\n");
@@ -1877,15 +1872,15 @@ static void s5p_mfc_cleanup_queue_v6(struct list_head *lh, struct vb2_queue *vq)
static void s5p_mfc_clear_int_flags_v6(struct s5p_mfc_dev *dev)
{
const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs;
- WRITEL(0, mfc_regs->risc2host_command);
- WRITEL(0, mfc_regs->risc2host_int);
+ writel(0, mfc_regs->risc2host_command);
+ writel(0, mfc_regs->risc2host_int);
}
static void s5p_mfc_write_info_v6(struct s5p_mfc_ctx *ctx, unsigned int data,
unsigned int ofs)
{
s5p_mfc_clock_on();
- WRITEL(data, (void *)ofs);
+ writel(data, (volatile void __iomem *)((unsigned long)ofs));
s5p_mfc_clock_off();
}
@@ -1895,7 +1890,7 @@ s5p_mfc_read_info_v6(struct s5p_mfc_ctx *ctx, unsigned int ofs)
int ret;
s5p_mfc_clock_on();
- ret = READL((void *)ofs);
+ ret = readl((volatile void __iomem *)((unsigned long)ofs));
s5p_mfc_clock_off();
return ret;
@@ -1903,51 +1898,51 @@ s5p_mfc_read_info_v6(struct s5p_mfc_ctx *ctx, unsigned int ofs)
static int s5p_mfc_get_dspl_y_adr_v6(struct s5p_mfc_dev *dev)
{
- return READL(dev->mfc_regs->d_display_first_plane_addr);
+ return readl(dev->mfc_regs->d_display_first_plane_addr);
}
static int s5p_mfc_get_dec_y_adr_v6(struct s5p_mfc_dev *dev)
{
- return READL(dev->mfc_regs->d_decoded_first_plane_addr);
+ return readl(dev->mfc_regs->d_decoded_first_plane_addr);
}
static int s5p_mfc_get_dspl_status_v6(struct s5p_mfc_dev *dev)
{
- return READL(dev->mfc_regs->d_display_status);
+ return readl(dev->mfc_regs->d_display_status);
}
static int s5p_mfc_get_dec_status_v6(struct s5p_mfc_dev *dev)
{
- return READL(dev->mfc_regs->d_decoded_status);
+ return readl(dev->mfc_regs->d_decoded_status);
}
static int s5p_mfc_get_dec_frame_type_v6(struct s5p_mfc_dev *dev)
{
- return READL(dev->mfc_regs->d_decoded_frame_type) &
+ return readl(dev->mfc_regs->d_decoded_frame_type) &
S5P_FIMV_DECODE_FRAME_MASK_V6;
}
static int s5p_mfc_get_disp_frame_type_v6(struct s5p_mfc_ctx *ctx)
{
struct s5p_mfc_dev *dev = ctx->dev;
- return READL(dev->mfc_regs->d_display_frame_type) &
+ return readl(dev->mfc_regs->d_display_frame_type) &
S5P_FIMV_DECODE_FRAME_MASK_V6;
}
static int s5p_mfc_get_consumed_stream_v6(struct s5p_mfc_dev *dev)
{
- return READL(dev->mfc_regs->d_decoded_nal_size);
+ return readl(dev->mfc_regs->d_decoded_nal_size);
}
static int s5p_mfc_get_int_reason_v6(struct s5p_mfc_dev *dev)
{
- return READL(dev->mfc_regs->risc2host_command) &
+ return readl(dev->mfc_regs->risc2host_command) &
S5P_FIMV_RISC2HOST_CMD_MASK;
}
static int s5p_mfc_get_int_err_v6(struct s5p_mfc_dev *dev)
{
- return READL(dev->mfc_regs->error_code);
+ return readl(dev->mfc_regs->error_code);
}
static int s5p_mfc_err_dec_v6(unsigned int err)
@@ -1962,87 +1957,87 @@ static int s5p_mfc_err_dspl_v6(unsigned int err)
static int s5p_mfc_get_img_width_v6(struct s5p_mfc_dev *dev)
{
- return READL(dev->mfc_regs->d_display_frame_width);
+ return readl(dev->mfc_regs->d_display_frame_width);
}
static int s5p_mfc_get_img_height_v6(struct s5p_mfc_dev *dev)
{
- return READL(dev->mfc_regs->d_display_frame_height);
+ return readl(dev->mfc_regs->d_display_frame_height);
}
static int s5p_mfc_get_dpb_count_v6(struct s5p_mfc_dev *dev)
{
- return READL(dev->mfc_regs->d_min_num_dpb);
+ return readl(dev->mfc_regs->d_min_num_dpb);
}
static int s5p_mfc_get_mv_count_v6(struct s5p_mfc_dev *dev)
{
- return READL(dev->mfc_regs->d_min_num_mv);
+ return readl(dev->mfc_regs->d_min_num_mv);
}
static int s5p_mfc_get_inst_no_v6(struct s5p_mfc_dev *dev)
{
- return READL(dev->mfc_regs->ret_instance_id);
+ return readl(dev->mfc_regs->ret_instance_id);
}
static int s5p_mfc_get_enc_dpb_count_v6(struct s5p_mfc_dev *dev)
{
- return READL(dev->mfc_regs->e_num_dpb);
+ return readl(dev->mfc_regs->e_num_dpb);
}
static int s5p_mfc_get_enc_strm_size_v6(struct s5p_mfc_dev *dev)
{
- return READL(dev->mfc_regs->e_stream_size);
+ return readl(dev->mfc_regs->e_stream_size);
}
static int s5p_mfc_get_enc_slice_type_v6(struct s5p_mfc_dev *dev)
{
- return READL(dev->mfc_regs->e_slice_type);
+ return readl(dev->mfc_regs->e_slice_type);
}
static int s5p_mfc_get_enc_pic_count_v6(struct s5p_mfc_dev *dev)
{
- return READL(dev->mfc_regs->e_picture_count);
+ return readl(dev->mfc_regs->e_picture_count);
}
static int s5p_mfc_get_sei_avail_status_v6(struct s5p_mfc_ctx *ctx)
{
struct s5p_mfc_dev *dev = ctx->dev;
- return READL(dev->mfc_regs->d_frame_pack_sei_avail);
+ return readl(dev->mfc_regs->d_frame_pack_sei_avail);
}
static int s5p_mfc_get_mvc_num_views_v6(struct s5p_mfc_dev *dev)
{
- return READL(dev->mfc_regs->d_mvc_num_views);
+ return readl(dev->mfc_regs->d_mvc_num_views);
}
static int s5p_mfc_get_mvc_view_id_v6(struct s5p_mfc_dev *dev)
{
- return READL(dev->mfc_regs->d_mvc_view_id);
+ return readl(dev->mfc_regs->d_mvc_view_id);
}
static unsigned int s5p_mfc_get_pic_type_top_v6(struct s5p_mfc_ctx *ctx)
{
return s5p_mfc_read_info_v6(ctx,
- (unsigned int) ctx->dev->mfc_regs->d_ret_picture_tag_top);
+ (__force unsigned long) ctx->dev->mfc_regs->d_ret_picture_tag_top);
}
static unsigned int s5p_mfc_get_pic_type_bot_v6(struct s5p_mfc_ctx *ctx)
{
return s5p_mfc_read_info_v6(ctx,
- (unsigned int) ctx->dev->mfc_regs->d_ret_picture_tag_bot);
+ (__force unsigned long) ctx->dev->mfc_regs->d_ret_picture_tag_bot);
}
static unsigned int s5p_mfc_get_crop_info_h_v6(struct s5p_mfc_ctx *ctx)
{
return s5p_mfc_read_info_v6(ctx,
- (unsigned int) ctx->dev->mfc_regs->d_display_crop_info1);
+ (__force unsigned long) ctx->dev->mfc_regs->d_display_crop_info1);
}
static unsigned int s5p_mfc_get_crop_info_v_v6(struct s5p_mfc_ctx *ctx)
{
return s5p_mfc_read_info_v6(ctx,
- (unsigned int) ctx->dev->mfc_regs->d_display_crop_info2);
+ (__force unsigned long) ctx->dev->mfc_regs->d_display_crop_info2);
}
static struct s5p_mfc_regs mfc_regs;
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_pm.c b/drivers/media/platform/s5p-mfc/s5p_mfc_pm.c
index b6a8be97a96c..826c48945bf5 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_pm.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_pm.c
@@ -21,7 +21,7 @@
#include "s5p_mfc_pm.h"
#define MFC_GATE_CLK_NAME "mfc"
-#define MFC_SCLK_NAME "sclk-mfc"
+#define MFC_SCLK_NAME "sclk_mfc"
#define MFC_SCLK_RATE (200 * 1000000)
#define CLK_DEBUG
diff --git a/drivers/media/platform/s5p-tv/Kconfig b/drivers/media/platform/s5p-tv/Kconfig
index 369a4c191e18..a9d56f8936b4 100644
--- a/drivers/media/platform/s5p-tv/Kconfig
+++ b/drivers/media/platform/s5p-tv/Kconfig
@@ -8,7 +8,8 @@
config VIDEO_SAMSUNG_S5P_TV
bool "Samsung TV driver for S5P platform"
- depends on (PLAT_S5P || ARCH_EXYNOS) && PM_RUNTIME
+ depends on PM_RUNTIME
+ depends on PLAT_S5P || ARCH_EXYNOS || COMPILE_TEST
default n
---help---
Say Y here to enable selecting the TV output devices for
@@ -70,6 +71,7 @@ config VIDEO_SAMSUNG_S5P_MIXER
tristate "Samsung Mixer and Video Processor Driver"
depends on VIDEO_DEV && VIDEO_V4L2
depends on VIDEO_SAMSUNG_S5P_TV
+ depends on HAS_DMA
select VIDEOBUF2_DMA_CONTIG
help
Say Y here if you want support for the Mixer in Samsung S5P SoCs.
diff --git a/drivers/media/platform/s5p-tv/hdmi_drv.c b/drivers/media/platform/s5p-tv/hdmi_drv.c
index 754740f4b671..37c8bd694c5f 100644
--- a/drivers/media/platform/s5p-tv/hdmi_drv.c
+++ b/drivers/media/platform/s5p-tv/hdmi_drv.c
@@ -615,7 +615,7 @@ static int hdmi_s_power(struct v4l2_subdev *sd, int on)
else
ret = pm_runtime_put_sync(hdev->dev);
/* only values < 0 indicate errors */
- return IS_ERR_VALUE(ret) ? ret : 0;
+ return ret < 0 ? ret : 0;
}
static int hdmi_s_dv_timings(struct v4l2_subdev *sd,
diff --git a/drivers/media/platform/s5p-tv/sdo_drv.c b/drivers/media/platform/s5p-tv/sdo_drv.c
index 5a7c3796f22e..72cf892dd008 100644
--- a/drivers/media/platform/s5p-tv/sdo_drv.c
+++ b/drivers/media/platform/s5p-tv/sdo_drv.c
@@ -190,7 +190,7 @@ static int sdo_s_power(struct v4l2_subdev *sd, int on)
ret = pm_runtime_put_sync(dev);
/* only values < 0 indicate errors */
- return IS_ERR_VALUE(ret) ? ret : 0;
+ return ret < 0 ? ret : 0;
}
static int sdo_streamon(struct sdo_device *sdev)
diff --git a/drivers/media/platform/s5p-tv/sii9234_drv.c b/drivers/media/platform/s5p-tv/sii9234_drv.c
index 3dd762e5b67e..db8c17bb4aaa 100644
--- a/drivers/media/platform/s5p-tv/sii9234_drv.c
+++ b/drivers/media/platform/s5p-tv/sii9234_drv.c
@@ -289,7 +289,7 @@ static int sii9234_s_power(struct v4l2_subdev *sd, int on)
else
ret = pm_runtime_put(&ctx->client->dev);
/* only values < 0 indicate errors */
- return IS_ERR_VALUE(ret) ? ret : 0;
+ return ret < 0 ? ret : 0;
}
static int sii9234_s_stream(struct v4l2_subdev *sd, int enable)
diff --git a/drivers/media/platform/sh_veu.c b/drivers/media/platform/sh_veu.c
index 8dc279d4d561..be3b3bc71a0f 100644
--- a/drivers/media/platform/sh_veu.c
+++ b/drivers/media/platform/sh_veu.c
@@ -26,6 +26,7 @@
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-mem2mem.h>
+#include <media/v4l2-image-sizes.h>
#include <media/videobuf2-dma-contig.h>
#define VEU_STR 0x00 /* start register */
@@ -135,9 +136,6 @@ enum sh_veu_fmt_idx {
SH_VEU_FMT_RGB24,
};
-#define VGA_WIDTH 640
-#define VGA_HEIGHT 480
-
#define DEFAULT_IN_WIDTH VGA_WIDTH
#define DEFAULT_IN_HEIGHT VGA_HEIGHT
#define DEFAULT_IN_FMTIDX SH_VEU_FMT_NV12
diff --git a/drivers/media/platform/soc_camera/Kconfig b/drivers/media/platform/soc_camera/Kconfig
index 6540847f4e1d..f2776cd415ca 100644
--- a/drivers/media/platform/soc_camera/Kconfig
+++ b/drivers/media/platform/soc_camera/Kconfig
@@ -20,6 +20,8 @@ config SOC_CAMERA_PLATFORM
config VIDEO_MX3
tristate "i.MX3x Camera Sensor Interface driver"
depends on VIDEO_DEV && MX3_IPU && SOC_CAMERA
+ depends on MX3_IPU || COMPILE_TEST
+ depends on HAS_DMA
select VIDEOBUF2_DMA_CONTIG
---help---
This is a v4l2 driver for the i.MX3x Camera Sensor Interface
@@ -35,6 +37,7 @@ config VIDEO_RCAR_VIN
tristate "R-Car Video Input (VIN) support"
depends on VIDEO_DEV && SOC_CAMERA
depends on ARCH_SHMOBILE || COMPILE_TEST
+ depends on HAS_DMA
select VIDEOBUF2_DMA_CONTIG
select SOC_CAMERA_SCALE_CROP
---help---
@@ -51,6 +54,7 @@ config VIDEO_SH_MOBILE_CEU
tristate "SuperH Mobile CEU Interface driver"
depends on VIDEO_DEV && SOC_CAMERA && HAS_DMA && HAVE_CLK
depends on ARCH_SHMOBILE || SUPERH || COMPILE_TEST
+ depends on HAS_DMA
select VIDEOBUF2_DMA_CONTIG
select SOC_CAMERA_SCALE_CROP
---help---
@@ -58,7 +62,9 @@ config VIDEO_SH_MOBILE_CEU
config VIDEO_OMAP1
tristate "OMAP1 Camera Interface driver"
- depends on VIDEO_DEV && ARCH_OMAP1 && SOC_CAMERA
+ depends on VIDEO_DEV && SOC_CAMERA
+ depends on ARCH_OMAP1
+ depends on HAS_DMA
select VIDEOBUF_DMA_CONTIG
select VIDEOBUF_DMA_SG
---help---
@@ -66,14 +72,18 @@ config VIDEO_OMAP1
config VIDEO_MX2
tristate "i.MX27 Camera Sensor Interface driver"
- depends on VIDEO_DEV && SOC_CAMERA && SOC_IMX27
+ depends on VIDEO_DEV && SOC_CAMERA
+ depends on SOC_IMX27 || COMPILE_TEST
+ depends on HAS_DMA
select VIDEOBUF2_DMA_CONTIG
---help---
This is a v4l2 driver for the i.MX27 Camera Sensor Interface
config VIDEO_ATMEL_ISI
tristate "ATMEL Image Sensor Interface (ISI) support"
- depends on VIDEO_DEV && SOC_CAMERA && ARCH_AT91
+ depends on VIDEO_DEV && SOC_CAMERA
+ depends on ARCH_AT91 || COMPILE_TEST
+ depends on HAS_DMA
select VIDEOBUF2_DMA_CONTIG
---help---
This module makes the ATMEL Image Sensor Interface available
diff --git a/drivers/media/platform/soc_camera/atmel-isi.c b/drivers/media/platform/soc_camera/atmel-isi.c
index 3408b045b3f1..c5291b001057 100644
--- a/drivers/media/platform/soc_camera/atmel-isi.c
+++ b/drivers/media/platform/soc_camera/atmel-isi.c
@@ -54,7 +54,7 @@ static void set_dma_ctrl(struct fbd *fb_desc, u32 ctrl)
struct isi_dma_desc {
struct list_head list;
struct fbd *p_fbd;
- u32 fbd_phys;
+ dma_addr_t fbd_phys;
};
/* Frame buffer data */
@@ -75,7 +75,7 @@ struct atmel_isi {
/* Allocate descriptors for dma buffer use */
struct fbd *p_fb_descriptors;
- u32 fb_descriptors_phys;
+ dma_addr_t fb_descriptors_phys;
struct list_head dma_desc_head;
struct isi_dma_desc dma_desc[MAX_BUFFER_NUM];
@@ -169,7 +169,7 @@ static irqreturn_t atmel_isi_handle_streaming(struct atmel_isi *isi)
isi->active = list_entry(isi->video_buffer_list.next,
struct frame_buffer, list);
isi_writel(isi, ISI_DMA_C_DSCR,
- isi->active->p_dma_desc->fbd_phys);
+ (u32)isi->active->p_dma_desc->fbd_phys);
isi_writel(isi, ISI_DMA_C_CTRL,
ISI_DMA_CTRL_FETCH | ISI_DMA_CTRL_DONE);
isi_writel(isi, ISI_DMA_CHER, ISI_DMA_CHSR_C_CH);
@@ -346,7 +346,7 @@ static void start_dma(struct atmel_isi *isi, struct frame_buffer *buffer)
return;
}
- isi_writel(isi, ISI_DMA_C_DSCR, buffer->p_dma_desc->fbd_phys);
+ isi_writel(isi, ISI_DMA_C_DSCR, (u32)buffer->p_dma_desc->fbd_phys);
isi_writel(isi, ISI_DMA_C_CTRL, ISI_DMA_CTRL_FETCH | ISI_DMA_CTRL_DONE);
isi_writel(isi, ISI_DMA_CHER, ISI_DMA_CHSR_C_CH);
@@ -384,7 +384,6 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count)
struct soc_camera_device *icd = soc_camera_from_vb2q(vq);
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct atmel_isi *isi = ici->priv;
- u32 sr = 0;
int ret;
/* Reset ISI */
@@ -394,11 +393,11 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count)
return ret;
}
/* Disable all interrupts */
- isi_writel(isi, ISI_INTDIS, ~0UL);
+ isi_writel(isi, ISI_INTDIS, (u32)~0UL);
spin_lock_irq(&isi->lock);
/* Clear any pending interrupt */
- sr = isi_readl(isi, ISI_STATUS);
+ isi_readl(isi, ISI_STATUS);
if (count)
start_dma(isi, isi->active);
diff --git a/drivers/media/platform/soc_camera/mx2_camera.c b/drivers/media/platform/soc_camera/mx2_camera.c
index b40bc2e5ba47..2347612a4cc1 100644
--- a/drivers/media/platform/soc_camera/mx2_camera.c
+++ b/drivers/media/platform/soc_camera/mx2_camera.c
@@ -809,10 +809,9 @@ static int mx2_camera_init_videobuf(struct vb2_queue *q,
static int mx27_camera_emma_prp_reset(struct mx2_camera_dev *pcdev)
{
- u32 cntl;
int count = 0;
- cntl = readl(pcdev->base_emma + PRP_CNTL);
+ readl(pcdev->base_emma + PRP_CNTL);
writel(PRP_CNTL_SWRST, pcdev->base_emma + PRP_CNTL);
while (count++ < 100) {
if (!(readl(pcdev->base_emma + PRP_CNTL) & PRP_CNTL_SWRST))
@@ -1003,7 +1002,7 @@ static int mx2_emmaprp_resize(struct mx2_camera_dev *pcdev,
struct v4l2_mbus_framefmt *mf_in,
struct v4l2_pix_format *pix_out, bool apply)
{
- int num, den;
+ unsigned int num, den;
unsigned long m;
int i, dir;
diff --git a/drivers/media/platform/soc_camera/pxa_camera.c b/drivers/media/platform/soc_camera/pxa_camera.c
index 64dc80ccd6f9..66178fc9f9eb 100644
--- a/drivers/media/platform/soc_camera/pxa_camera.c
+++ b/drivers/media/platform/soc_camera/pxa_camera.c
@@ -1694,7 +1694,7 @@ static int pxa_camera_pdata_from_dt(struct device *dev,
break;
default:
break;
- };
+ }
if (ep.bus.parallel.flags & V4L2_MBUS_MASTER)
pcdev->platform_flags |= PXA_CAMERA_MASTER;
diff --git a/drivers/media/platform/soc_camera/rcar_vin.c b/drivers/media/platform/soc_camera/rcar_vin.c
index 85d579f65f52..20defcb8b31b 100644
--- a/drivers/media/platform/soc_camera/rcar_vin.c
+++ b/drivers/media/platform/soc_camera/rcar_vin.c
@@ -981,7 +981,7 @@ static int rcar_vin_get_formats(struct soc_camera_device *icd, unsigned int idx,
if (shift == 3) {
dev_err(dev,
- "Failed to configure the client below %ux%x\n",
+ "Failed to configure the client below %ux%u\n",
mf.width, mf.height);
return -EIO;
}
@@ -1502,7 +1502,7 @@ static int rcar_vin_probe(struct platform_device *pdev)
} else {
priv->ici.nr = of_alias_get_id(pdev->dev.of_node, "vin");
priv->chip = (enum chip_id)match->data;
- };
+ }
spin_lock_init(&priv->lock);
INIT_LIST_HEAD(&priv->capture);
diff --git a/drivers/media/platform/soc_camera/soc_camera.c b/drivers/media/platform/soc_camera/soc_camera.c
index f4308fed5431..8e61b976da19 100644
--- a/drivers/media/platform/soc_camera/soc_camera.c
+++ b/drivers/media/platform/soc_camera/soc_camera.c
@@ -437,6 +437,22 @@ static int soc_camera_prepare_buf(struct file *file, void *priv,
return vb2_prepare_buf(&icd->vb2_vidq, b);
}
+static int soc_camera_expbuf(struct file *file, void *priv,
+ struct v4l2_exportbuffer *p)
+{
+ struct soc_camera_device *icd = file->private_data;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+
+ if (icd->streamer != file)
+ return -EBUSY;
+
+ /* videobuf2 only */
+ if (ici->ops->init_videobuf)
+ return -EINVAL;
+ else
+ return vb2_expbuf(&icd->vb2_vidq, p);
+}
+
/* Always entered with .host_lock held */
static int soc_camera_init_user_formats(struct soc_camera_device *icd)
{
@@ -1347,13 +1363,11 @@ static int soc_camera_i2c_init(struct soc_camera_device *icd,
return -ENODEV;
}
- ssdd = kzalloc(sizeof(*ssdd), GFP_KERNEL);
+ ssdd = kmemdup(&sdesc->subdev_desc, sizeof(*ssdd), GFP_KERNEL);
if (!ssdd) {
ret = -ENOMEM;
goto ealloc;
}
-
- memcpy(ssdd, &sdesc->subdev_desc, sizeof(*ssdd));
/*
* In synchronous case we request regulators ourselves in
* soc_camera_pdrv_probe(), make sure the subdevice driver doesn't try
@@ -2085,6 +2099,7 @@ static const struct v4l2_ioctl_ops soc_camera_ioctl_ops = {
.vidioc_dqbuf = soc_camera_dqbuf,
.vidioc_create_bufs = soc_camera_create_bufs,
.vidioc_prepare_buf = soc_camera_prepare_buf,
+ .vidioc_expbuf = soc_camera_expbuf,
.vidioc_streamon = soc_camera_streamon,
.vidioc_streamoff = soc_camera_streamoff,
.vidioc_cropcap = soc_camera_cropcap,
diff --git a/drivers/media/platform/ti-vpe/vpdma.c b/drivers/media/platform/ti-vpe/vpdma.c
index a51a01359805..3e2e3a33e6ed 100644
--- a/drivers/media/platform/ti-vpe/vpdma.c
+++ b/drivers/media/platform/ti-vpe/vpdma.c
@@ -329,7 +329,7 @@ int vpdma_alloc_desc_buf(struct vpdma_buf *buf, size_t size)
if (!buf->addr)
return -ENOMEM;
- WARN_ON((u32) buf->addr & VPDMA_DESC_ALIGN);
+ WARN_ON(((unsigned long)buf->addr & VPDMA_DESC_ALIGN) != 0);
return 0;
}
@@ -584,7 +584,7 @@ static void dump_dtd(struct vpdma_dtd *dtd)
pr_debug("word1: line_length = %d, xfer_height = %d\n",
dtd_get_line_length(dtd), dtd_get_xfer_height(dtd));
- pr_debug("word2: start_addr = 0x%08x\n", dtd->start_addr);
+ pr_debug("word2: start_addr = %pad\n", &dtd->start_addr);
pr_debug("word3: pkt_type = %d, mode = %d, dir = %d, chan = %d, "
"pri = %d, next_chan = %d\n", dtd_get_pkt_type(dtd),
diff --git a/drivers/media/platform/ti-vpe/vpe.c b/drivers/media/platform/ti-vpe/vpe.c
index 972f43f69206..9a081c291159 100644
--- a/drivers/media/platform/ti-vpe/vpe.c
+++ b/drivers/media/platform/ti-vpe/vpe.c
@@ -31,6 +31,7 @@
#include <linux/slab.h>
#include <linux/videodev2.h>
#include <linux/log2.h>
+#include <linux/sizes.h>
#include <media/v4l2-common.h>
#include <media/v4l2-ctrls.h>
@@ -138,12 +139,12 @@ struct vpe_dei_regs {
* default expert DEI register values, unlikely to be modified.
*/
static const struct vpe_dei_regs dei_regs = {
- 0x020C0804u,
- 0x0118100Fu,
- 0x08040200u,
- 0x1010100Cu,
- 0x10101010u,
- 0x10101010u,
+ .mdt_spacial_freq_thr_reg = 0x020C0804u,
+ .edi_config_reg = 0x0118100Fu,
+ .edi_lut_reg0 = 0x08040200u,
+ .edi_lut_reg1 = 0x1010100Cu,
+ .edi_lut_reg2 = 0x10101010u,
+ .edi_lut_reg3 = 0x10101010u,
};
/*
@@ -834,10 +835,10 @@ static int set_srcdst_params(struct vpe_ctx *ctx)
VPDMA_STRIDE_ALIGN);
mv_buf_size = bytes_per_line * s_q_data->height;
- ctx->deinterlacing = 1;
+ ctx->deinterlacing = true;
src_h <<= 1;
} else {
- ctx->deinterlacing = 0;
+ ctx->deinterlacing = false;
mv_buf_size = 0;
}
@@ -2343,8 +2344,7 @@ v4l2_dev_unreg:
static int vpe_remove(struct platform_device *pdev)
{
- struct vpe_dev *dev =
- (struct vpe_dev *) platform_get_drvdata(pdev);
+ struct vpe_dev *dev = platform_get_drvdata(pdev);
v4l2_info(&dev->v4l2_dev, "Removing " VPE_MODULE_NAME);
diff --git a/drivers/media/platform/via-camera.c b/drivers/media/platform/via-camera.c
index b4f9d03636e3..ae6870cb8339 100644
--- a/drivers/media/platform/via-camera.c
+++ b/drivers/media/platform/via-camera.c
@@ -18,6 +18,7 @@
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-ctrls.h>
+#include <media/v4l2-image-sizes.h>
#include <media/ov7670.h>
#include <media/videobuf-dma-sg.h>
#include <linux/delay.h>
@@ -49,14 +50,6 @@ MODULE_PARM_DESC(override_serial,
"to force-enable the camera.");
/*
- * Basic window sizes.
- */
-#define VGA_WIDTH 640
-#define VGA_HEIGHT 480
-#define QCIF_WIDTH 176
-#define QCIF_HEIGHT 144
-
-/*
* The structure describing our camera.
*/
enum viacam_opstate { S_IDLE = 0, S_RUNNING = 1 };
@@ -89,7 +82,7 @@ struct via_camera {
* live in frame buffer memory, so we don't call them "DMA".
*/
unsigned int cb_offsets[3]; /* offsets into fb mem */
- u8 *cb_addrs[3]; /* Kernel-space addresses */
+ u8 __iomem *cb_addrs[3]; /* Kernel-space addresses */
int n_cap_bufs; /* How many are we using? */
int next_buf;
struct videobuf_queue vb_queue;
@@ -1283,7 +1276,7 @@ static bool viacam_serial_is_enabled(void)
VIACAM_SERIAL_CREG, &cbyte);
if ((cbyte & VIACAM_SERIAL_BIT) == 0)
return false; /* Not enabled */
- if (override_serial == 0) {
+ if (!override_serial) {
printk(KERN_NOTICE "Via camera: serial port is enabled, " \
"refusing to load.\n");
printk(KERN_NOTICE "Specify override_serial=1 to force " \
diff --git a/drivers/media/platform/vivi.c b/drivers/media/platform/vivi.c
deleted file mode 100644
index 80333714ffa7..000000000000
--- a/drivers/media/platform/vivi.c
+++ /dev/null
@@ -1,1542 +0,0 @@
-/*
- * Virtual Video driver - This code emulates a real video device with v4l2 api
- *
- * Copyright (c) 2006 by:
- * Mauro Carvalho Chehab <mchehab--a.t--infradead.org>
- * Ted Walther <ted--a.t--enumera.com>
- * John Sokol <sokol--a.t--videotechnology.com>
- * http://v4l.videotechnology.com/
- *
- * Conversion to videobuf2 by Pawel Osciak & Marek Szyprowski
- * Copyright (c) 2010 Samsung Electronics
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the BSD Licence, GNU General Public License
- * as published by the Free Software Foundation; either version 2 of the
- * License, or (at your option) any later version
- */
-#include <linux/module.h>
-#include <linux/errno.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include <linux/font.h>
-#include <linux/mutex.h>
-#include <linux/videodev2.h>
-#include <linux/kthread.h>
-#include <linux/freezer.h>
-#include <media/videobuf2-vmalloc.h>
-#include <media/v4l2-device.h>
-#include <media/v4l2-ioctl.h>
-#include <media/v4l2-ctrls.h>
-#include <media/v4l2-fh.h>
-#include <media/v4l2-event.h>
-#include <media/v4l2-common.h>
-
-#define VIVI_MODULE_NAME "vivi"
-
-/* Maximum allowed frame rate
- *
- * Vivi will allow setting timeperframe in [1/FPS_MAX - FPS_MAX/1] range.
- *
- * Ideally FPS_MAX should be infinity, i.e. practically UINT_MAX, but that
- * might hit application errors when they manipulate these values.
- *
- * Besides, for tpf < 1ms image-generation logic should be changed, to avoid
- * producing frames with equal content.
- */
-#define FPS_MAX 1000
-
-#define MAX_WIDTH 1920
-#define MAX_HEIGHT 1200
-
-#define VIVI_VERSION "0.8.1"
-
-MODULE_DESCRIPTION("Video Technology Magazine Virtual Video Capture Board");
-MODULE_AUTHOR("Mauro Carvalho Chehab, Ted Walther and John Sokol");
-MODULE_LICENSE("Dual BSD/GPL");
-MODULE_VERSION(VIVI_VERSION);
-
-static unsigned video_nr = -1;
-module_param(video_nr, uint, 0644);
-MODULE_PARM_DESC(video_nr, "videoX start number, -1 is autodetect");
-
-static unsigned n_devs = 1;
-module_param(n_devs, uint, 0644);
-MODULE_PARM_DESC(n_devs, "number of video devices to create");
-
-static unsigned debug;
-module_param(debug, uint, 0644);
-MODULE_PARM_DESC(debug, "activates debug info");
-
-/* Global font descriptor */
-static const u8 *font8x16;
-
-/* timeperframe: min/max and default */
-static const struct v4l2_fract
- tpf_min = {.numerator = 1, .denominator = FPS_MAX},
- tpf_max = {.numerator = FPS_MAX, .denominator = 1},
- tpf_default = {.numerator = 1001, .denominator = 30000}; /* NTSC */
-
-#define dprintk(dev, level, fmt, arg...) \
- v4l2_dbg(level, debug, &dev->v4l2_dev, fmt, ## arg)
-
-/* ------------------------------------------------------------------
- Basic structures
- ------------------------------------------------------------------*/
-
-struct vivi_fmt {
- const char *name;
- u32 fourcc; /* v4l2 format id */
- u8 depth;
- bool is_yuv;
-};
-
-static const struct vivi_fmt formats[] = {
- {
- .name = "4:2:2, packed, YUYV",
- .fourcc = V4L2_PIX_FMT_YUYV,
- .depth = 16,
- .is_yuv = true,
- },
- {
- .name = "4:2:2, packed, UYVY",
- .fourcc = V4L2_PIX_FMT_UYVY,
- .depth = 16,
- .is_yuv = true,
- },
- {
- .name = "4:2:2, packed, YVYU",
- .fourcc = V4L2_PIX_FMT_YVYU,
- .depth = 16,
- .is_yuv = true,
- },
- {
- .name = "4:2:2, packed, VYUY",
- .fourcc = V4L2_PIX_FMT_VYUY,
- .depth = 16,
- .is_yuv = true,
- },
- {
- .name = "RGB565 (LE)",
- .fourcc = V4L2_PIX_FMT_RGB565, /* gggbbbbb rrrrrggg */
- .depth = 16,
- },
- {
- .name = "RGB565 (BE)",
- .fourcc = V4L2_PIX_FMT_RGB565X, /* rrrrrggg gggbbbbb */
- .depth = 16,
- },
- {
- .name = "RGB555 (LE)",
- .fourcc = V4L2_PIX_FMT_RGB555, /* gggbbbbb arrrrrgg */
- .depth = 16,
- },
- {
- .name = "RGB555 (BE)",
- .fourcc = V4L2_PIX_FMT_RGB555X, /* arrrrrgg gggbbbbb */
- .depth = 16,
- },
- {
- .name = "RGB24 (LE)",
- .fourcc = V4L2_PIX_FMT_RGB24, /* rgb */
- .depth = 24,
- },
- {
- .name = "RGB24 (BE)",
- .fourcc = V4L2_PIX_FMT_BGR24, /* bgr */
- .depth = 24,
- },
- {
- .name = "RGB32 (LE)",
- .fourcc = V4L2_PIX_FMT_RGB32, /* argb */
- .depth = 32,
- },
- {
- .name = "RGB32 (BE)",
- .fourcc = V4L2_PIX_FMT_BGR32, /* bgra */
- .depth = 32,
- },
-};
-
-static const struct vivi_fmt *__get_format(u32 pixelformat)
-{
- const struct vivi_fmt *fmt;
- unsigned int k;
-
- for (k = 0; k < ARRAY_SIZE(formats); k++) {
- fmt = &formats[k];
- if (fmt->fourcc == pixelformat)
- break;
- }
-
- if (k == ARRAY_SIZE(formats))
- return NULL;
-
- return &formats[k];
-}
-
-static const struct vivi_fmt *get_format(struct v4l2_format *f)
-{
- return __get_format(f->fmt.pix.pixelformat);
-}
-
-/* buffer for one video frame */
-struct vivi_buffer {
- /* common v4l buffer stuff -- must be first */
- struct vb2_buffer vb;
- struct list_head list;
-};
-
-struct vivi_dmaqueue {
- struct list_head active;
-
- /* thread for generating video stream*/
- struct task_struct *kthread;
- wait_queue_head_t wq;
- /* Counters to control fps rate */
- int frame;
- int ini_jiffies;
-};
-
-static LIST_HEAD(vivi_devlist);
-
-struct vivi_dev {
- struct list_head vivi_devlist;
- struct v4l2_device v4l2_dev;
- struct v4l2_ctrl_handler ctrl_handler;
- struct video_device vdev;
-
- /* 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 *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;
-
- spinlock_t slock;
- struct mutex mutex;
-
- struct vivi_dmaqueue vidq;
-
- /* Several counters */
- unsigned ms;
- unsigned long jiffies;
- unsigned button_pressed;
-
- int mv_count; /* Controls bars movement */
-
- /* Input Number */
- int input;
-
- /* video capture */
- const struct vivi_fmt *fmt;
- struct v4l2_fract timeperframe;
- unsigned int width, height;
- struct vb2_queue vb_vidq;
- unsigned int seq_count;
-
- u8 bars[9][3];
- u8 line[MAX_WIDTH * 8] __attribute__((__aligned__(4)));
- unsigned int pixelsize;
- u8 alpha_component;
- u32 textfg, textbg;
-};
-
-/* ------------------------------------------------------------------
- DMA and thread functions
- ------------------------------------------------------------------*/
-
-/* Bars and Colors should match positions */
-
-enum colors {
- WHITE,
- AMBER,
- CYAN,
- GREEN,
- MAGENTA,
- RED,
- BLUE,
- BLACK,
- TEXT_BLACK,
-};
-
-/* R G B */
-#define COLOR_WHITE {204, 204, 204}
-#define COLOR_AMBER {208, 208, 0}
-#define COLOR_CYAN { 0, 206, 206}
-#define COLOR_GREEN { 0, 239, 0}
-#define COLOR_MAGENTA {239, 0, 239}
-#define COLOR_RED {205, 0, 0}
-#define COLOR_BLUE { 0, 0, 255}
-#define COLOR_BLACK { 0, 0, 0}
-
-struct bar_std {
- u8 bar[9][3];
-};
-
-/* Maximum number of bars are 10 - otherwise, the input print code
- should be modified */
-static const struct bar_std bars[] = {
- { /* Standard ITU-R color bar sequence */
- { COLOR_WHITE, COLOR_AMBER, COLOR_CYAN, COLOR_GREEN,
- COLOR_MAGENTA, COLOR_RED, COLOR_BLUE, COLOR_BLACK, COLOR_BLACK }
- }, {
- { COLOR_WHITE, COLOR_AMBER, COLOR_BLACK, COLOR_WHITE,
- COLOR_AMBER, COLOR_BLACK, COLOR_WHITE, COLOR_AMBER, COLOR_BLACK }
- }, {
- { COLOR_WHITE, COLOR_CYAN, COLOR_BLACK, COLOR_WHITE,
- COLOR_CYAN, COLOR_BLACK, COLOR_WHITE, COLOR_CYAN, COLOR_BLACK }
- }, {
- { COLOR_WHITE, COLOR_GREEN, COLOR_BLACK, COLOR_WHITE,
- COLOR_GREEN, COLOR_BLACK, COLOR_WHITE, COLOR_GREEN, COLOR_BLACK }
- },
-};
-
-#define NUM_INPUTS ARRAY_SIZE(bars)
-
-#define TO_Y(r, g, b) \
- (((16829 * r + 33039 * g + 6416 * b + 32768) >> 16) + 16)
-/* RGB to V(Cr) Color transform */
-#define TO_V(r, g, b) \
- (((28784 * r - 24103 * g - 4681 * b + 32768) >> 16) + 128)
-/* RGB to U(Cb) Color transform */
-#define TO_U(r, g, b) \
- (((-9714 * r - 19070 * g + 28784 * b + 32768) >> 16) + 128)
-
-/* precalculate color bar values to speed up rendering */
-static void precalculate_bars(struct vivi_dev *dev)
-{
- u8 r, g, b;
- int k, is_yuv;
-
- for (k = 0; k < 9; k++) {
- r = bars[dev->input].bar[k][0];
- g = bars[dev->input].bar[k][1];
- b = bars[dev->input].bar[k][2];
- is_yuv = dev->fmt->is_yuv;
-
- switch (dev->fmt->fourcc) {
- case V4L2_PIX_FMT_RGB565:
- case V4L2_PIX_FMT_RGB565X:
- r >>= 3;
- g >>= 2;
- b >>= 3;
- break;
- case V4L2_PIX_FMT_RGB555:
- case V4L2_PIX_FMT_RGB555X:
- r >>= 3;
- g >>= 3;
- b >>= 3;
- break;
- case V4L2_PIX_FMT_YUYV:
- case V4L2_PIX_FMT_UYVY:
- case V4L2_PIX_FMT_YVYU:
- case V4L2_PIX_FMT_VYUY:
- case V4L2_PIX_FMT_RGB24:
- case V4L2_PIX_FMT_BGR24:
- case V4L2_PIX_FMT_RGB32:
- case V4L2_PIX_FMT_BGR32:
- break;
- }
-
- if (is_yuv) {
- dev->bars[k][0] = TO_Y(r, g, b); /* Luma */
- dev->bars[k][1] = TO_U(r, g, b); /* Cb */
- dev->bars[k][2] = TO_V(r, g, b); /* Cr */
- } else {
- dev->bars[k][0] = r;
- dev->bars[k][1] = g;
- dev->bars[k][2] = b;
- }
- }
-}
-
-/* 'odd' is true for pixels 1, 3, 5, etc. and false for pixels 0, 2, 4, etc. */
-static void gen_twopix(struct vivi_dev *dev, u8 *buf, int colorpos, bool odd)
-{
- u8 r_y, g_u, b_v;
- u8 alpha = dev->alpha_component;
- int color;
- u8 *p;
-
- r_y = dev->bars[colorpos][0]; /* R or precalculated Y */
- g_u = dev->bars[colorpos][1]; /* G or precalculated U */
- b_v = dev->bars[colorpos][2]; /* B or precalculated V */
-
- for (color = 0; color < dev->pixelsize; color++) {
- p = buf + color;
-
- switch (dev->fmt->fourcc) {
- case V4L2_PIX_FMT_YUYV:
- switch (color) {
- case 0:
- *p = r_y;
- break;
- case 1:
- *p = odd ? b_v : g_u;
- break;
- }
- break;
- case V4L2_PIX_FMT_UYVY:
- switch (color) {
- case 0:
- *p = odd ? b_v : g_u;
- break;
- case 1:
- *p = r_y;
- break;
- }
- break;
- case V4L2_PIX_FMT_YVYU:
- switch (color) {
- case 0:
- *p = r_y;
- break;
- case 1:
- *p = odd ? g_u : b_v;
- break;
- }
- break;
- case V4L2_PIX_FMT_VYUY:
- switch (color) {
- case 0:
- *p = odd ? g_u : b_v;
- break;
- case 1:
- *p = r_y;
- break;
- }
- break;
- case V4L2_PIX_FMT_RGB565:
- switch (color) {
- case 0:
- *p = (g_u << 5) | b_v;
- break;
- case 1:
- *p = (r_y << 3) | (g_u >> 3);
- break;
- }
- break;
- case V4L2_PIX_FMT_RGB565X:
- switch (color) {
- case 0:
- *p = (r_y << 3) | (g_u >> 3);
- break;
- case 1:
- *p = (g_u << 5) | b_v;
- break;
- }
- break;
- case V4L2_PIX_FMT_RGB555:
- switch (color) {
- case 0:
- *p = (g_u << 5) | b_v;
- break;
- case 1:
- *p = (alpha & 0x80) | (r_y << 2) | (g_u >> 3);
- break;
- }
- break;
- case V4L2_PIX_FMT_RGB555X:
- switch (color) {
- case 0:
- *p = (alpha & 0x80) | (r_y << 2) | (g_u >> 3);
- break;
- case 1:
- *p = (g_u << 5) | b_v;
- break;
- }
- break;
- case V4L2_PIX_FMT_RGB24:
- switch (color) {
- case 0:
- *p = r_y;
- break;
- case 1:
- *p = g_u;
- break;
- case 2:
- *p = b_v;
- break;
- }
- break;
- case V4L2_PIX_FMT_BGR24:
- switch (color) {
- case 0:
- *p = b_v;
- break;
- case 1:
- *p = g_u;
- break;
- case 2:
- *p = r_y;
- break;
- }
- break;
- case V4L2_PIX_FMT_RGB32:
- switch (color) {
- case 0:
- *p = alpha;
- break;
- case 1:
- *p = r_y;
- break;
- case 2:
- *p = g_u;
- break;
- case 3:
- *p = b_v;
- break;
- }
- break;
- case V4L2_PIX_FMT_BGR32:
- switch (color) {
- case 0:
- *p = b_v;
- break;
- case 1:
- *p = g_u;
- break;
- case 2:
- *p = r_y;
- break;
- case 3:
- *p = alpha;
- break;
- }
- break;
- }
- }
-}
-
-static void precalculate_line(struct vivi_dev *dev)
-{
- unsigned pixsize = dev->pixelsize;
- unsigned pixsize2 = 2*pixsize;
- int colorpos;
- u8 *pos;
-
- for (colorpos = 0; colorpos < 16; ++colorpos) {
- u8 pix[8];
- int wstart = colorpos * dev->width / 8;
- int wend = (colorpos+1) * dev->width / 8;
- int w;
-
- gen_twopix(dev, &pix[0], colorpos % 8, 0);
- gen_twopix(dev, &pix[pixsize], colorpos % 8, 1);
-
- for (w = wstart/2*2, pos = dev->line + w*pixsize; w < wend; w += 2, pos += pixsize2)
- memcpy(pos, pix, pixsize2);
- }
-}
-
-/* need this to do rgb24 rendering */
-typedef struct { u16 __; u8 _; } __attribute__((packed)) x24;
-
-static void gen_text(struct vivi_dev *dev, char *basep,
- int y, int x, char *text)
-{
- int line;
- unsigned int width = dev->width;
-
- /* Checks if it is possible to show string */
- if (y + 16 >= dev->height || x + strlen(text) * 8 >= width)
- return;
-
- /* Print stream time */
-#define PRINTSTR(PIXTYPE) do { \
- PIXTYPE fg; \
- PIXTYPE bg; \
- memcpy(&fg, &dev->textfg, sizeof(PIXTYPE)); \
- memcpy(&bg, &dev->textbg, sizeof(PIXTYPE)); \
- \
- for (line = 0; line < 16; line++) { \
- PIXTYPE *pos = (PIXTYPE *)( basep + ((y + line) * width + x) * sizeof(PIXTYPE) ); \
- u8 *s; \
- \
- for (s = text; *s; s++) { \
- u8 chr = font8x16[*s * 16 + line]; \
- \
- pos[0] = (chr & (0x01 << 7) ? fg : bg); \
- pos[1] = (chr & (0x01 << 6) ? fg : bg); \
- pos[2] = (chr & (0x01 << 5) ? fg : bg); \
- pos[3] = (chr & (0x01 << 4) ? fg : bg); \
- pos[4] = (chr & (0x01 << 3) ? fg : bg); \
- pos[5] = (chr & (0x01 << 2) ? fg : bg); \
- pos[6] = (chr & (0x01 << 1) ? fg : bg); \
- pos[7] = (chr & (0x01 << 0) ? fg : bg); \
- \
- pos += 8; \
- } \
- } \
-} while (0)
-
- switch (dev->pixelsize) {
- case 2:
- PRINTSTR(u16); break;
- case 4:
- PRINTSTR(u32); break;
- case 3:
- PRINTSTR(x24); break;
- }
-}
-
-static void vivi_fillbuff(struct vivi_dev *dev, struct vivi_buffer *buf)
-{
- int stride = dev->width * dev->pixelsize;
- int hmax = dev->height;
- void *vbuf = vb2_plane_vaddr(&buf->vb, 0);
- unsigned ms;
- char str[100];
- int h, line = 1;
- u8 *linestart;
- s32 gain;
-
- if (!vbuf)
- return;
-
- linestart = dev->line + (dev->mv_count % dev->width) * dev->pixelsize;
-
- for (h = 0; h < hmax; h++)
- memcpy(vbuf + h * stride, linestart, stride);
-
- /* Updates stream time */
-
- gen_twopix(dev, (u8 *)&dev->textbg, TEXT_BLACK, /*odd=*/ 0);
- gen_twopix(dev, (u8 *)&dev->textfg, WHITE, /*odd=*/ 0);
-
- dev->ms += jiffies_to_msecs(jiffies - dev->jiffies);
- dev->jiffies = jiffies;
- ms = dev->ms;
- snprintf(str, sizeof(str), " %02d:%02d:%02d:%03d ",
- (ms / (60 * 60 * 1000)) % 24,
- (ms / (60 * 1000)) % 60,
- (ms / 1000) % 60,
- ms % 1000);
- gen_text(dev, vbuf, line++ * 16, 16, str);
- snprintf(str, sizeof(str), " %dx%d, input %d ",
- dev->width, dev->height, dev->input);
- gen_text(dev, vbuf, line++ * 16, 16, str);
-
- gain = v4l2_ctrl_g_ctrl(dev->gain);
- mutex_lock(dev->ctrl_handler.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);
- gen_text(dev, vbuf, line++ * 16, 16, str);
- snprintf(str, sizeof(str), " autogain %d, gain %3d, volume %3d, alpha 0x%02x ",
- dev->autogain->cur.val, gain, dev->volume->cur.val,
- dev->alpha->cur.val);
- gen_text(dev, vbuf, line++ * 16, 16, str);
- snprintf(str, sizeof(str), " int32 %d, int64 %lld, bitmask %08x ",
- dev->int32->cur.val,
- *dev->int64->p_cur.p_s64,
- dev->bitmask->cur.val);
- gen_text(dev, vbuf, line++ * 16, 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);
- gen_text(dev, vbuf, line++ * 16, 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);
- gen_text(dev, vbuf, line++ * 16, 16, str);
- mutex_unlock(dev->ctrl_handler.lock);
- if (dev->button_pressed) {
- dev->button_pressed--;
- snprintf(str, sizeof(str), " button pressed!");
- gen_text(dev, vbuf, line++ * 16, 16, str);
- }
-
- dev->mv_count += 2;
-
- buf->vb.v4l2_buf.field = V4L2_FIELD_INTERLACED;
- buf->vb.v4l2_buf.sequence = dev->seq_count++;
- v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp);
-}
-
-static void vivi_thread_tick(struct vivi_dev *dev)
-{
- struct vivi_dmaqueue *dma_q = &dev->vidq;
- struct vivi_buffer *buf;
- unsigned long flags = 0;
-
- dprintk(dev, 1, "Thread tick\n");
-
- spin_lock_irqsave(&dev->slock, flags);
- if (list_empty(&dma_q->active)) {
- dprintk(dev, 1, "No active queue to serve\n");
- spin_unlock_irqrestore(&dev->slock, flags);
- return;
- }
-
- buf = list_entry(dma_q->active.next, struct vivi_buffer, list);
- list_del(&buf->list);
- spin_unlock_irqrestore(&dev->slock, flags);
-
- v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp);
-
- /* Fill buffer */
- vivi_fillbuff(dev, buf);
- dprintk(dev, 1, "filled buffer %p\n", buf);
-
- vb2_buffer_done(&buf->vb, VB2_BUF_STATE_DONE);
- dprintk(dev, 2, "[%p/%d] done\n", buf, buf->vb.v4l2_buf.index);
-}
-
-#define frames_to_ms(dev, frames) \
- ((frames * dev->timeperframe.numerator * 1000) / dev->timeperframe.denominator)
-
-static void vivi_sleep(struct vivi_dev *dev)
-{
- struct vivi_dmaqueue *dma_q = &dev->vidq;
- int timeout;
- DECLARE_WAITQUEUE(wait, current);
-
- dprintk(dev, 1, "%s dma_q=0x%08lx\n", __func__,
- (unsigned long)dma_q);
-
- add_wait_queue(&dma_q->wq, &wait);
- if (kthread_should_stop())
- goto stop_task;
-
- /* Calculate time to wake up */
- timeout = msecs_to_jiffies(frames_to_ms(dev, 1));
-
- vivi_thread_tick(dev);
-
- schedule_timeout_interruptible(timeout);
-
-stop_task:
- remove_wait_queue(&dma_q->wq, &wait);
- try_to_freeze();
-}
-
-static int vivi_thread(void *data)
-{
- struct vivi_dev *dev = data;
-
- dprintk(dev, 1, "thread started\n");
-
- set_freezable();
-
- for (;;) {
- vivi_sleep(dev);
-
- if (kthread_should_stop())
- break;
- }
- dprintk(dev, 1, "thread: exit\n");
- return 0;
-}
-
-static int vivi_start_generating(struct vivi_dev *dev)
-{
- struct vivi_dmaqueue *dma_q = &dev->vidq;
-
- dprintk(dev, 1, "%s\n", __func__);
-
- /* Resets frame counters */
- dev->ms = 0;
- dev->mv_count = 0;
- dev->jiffies = jiffies;
-
- dma_q->frame = 0;
- dma_q->ini_jiffies = jiffies;
- dma_q->kthread = kthread_run(vivi_thread, dev, "%s",
- dev->v4l2_dev.name);
-
- if (IS_ERR(dma_q->kthread)) {
- v4l2_err(&dev->v4l2_dev, "kernel_thread() failed\n");
- return PTR_ERR(dma_q->kthread);
- }
- /* Wakes thread */
- wake_up_interruptible(&dma_q->wq);
-
- dprintk(dev, 1, "returning from %s\n", __func__);
- return 0;
-}
-
-static void vivi_stop_generating(struct vivi_dev *dev)
-{
- struct vivi_dmaqueue *dma_q = &dev->vidq;
-
- dprintk(dev, 1, "%s\n", __func__);
-
- /* shutdown control thread */
- if (dma_q->kthread) {
- kthread_stop(dma_q->kthread);
- dma_q->kthread = NULL;
- }
-
- /*
- * Typical driver might need to wait here until dma engine stops.
- * In this case we can abort imiedetly, so it's just a noop.
- */
-
- /* Release all active buffers */
- while (!list_empty(&dma_q->active)) {
- struct vivi_buffer *buf;
- buf = list_entry(dma_q->active.next, struct vivi_buffer, list);
- list_del(&buf->list);
- vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
- dprintk(dev, 2, "[%p/%d] done\n", buf, buf->vb.v4l2_buf.index);
- }
-}
-/* ------------------------------------------------------------------
- Videobuf operations
- ------------------------------------------------------------------*/
-static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
- unsigned int *nbuffers, unsigned int *nplanes,
- unsigned int sizes[], void *alloc_ctxs[])
-{
- struct vivi_dev *dev = vb2_get_drv_priv(vq);
- unsigned long size;
-
- size = dev->width * dev->height * dev->pixelsize;
- if (fmt) {
- if (fmt->fmt.pix.sizeimage < size)
- return -EINVAL;
- size = fmt->fmt.pix.sizeimage;
- /* check against insane over 8K resolution buffers */
- if (size > 7680 * 4320 * dev->pixelsize)
- return -EINVAL;
- }
-
- *nplanes = 1;
-
- sizes[0] = size;
-
- /*
- * videobuf2-vmalloc allocator is context-less so no need to set
- * alloc_ctxs array.
- */
-
- dprintk(dev, 1, "%s, count=%d, size=%ld\n", __func__,
- *nbuffers, size);
-
- return 0;
-}
-
-static int buffer_prepare(struct vb2_buffer *vb)
-{
- struct vivi_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
- struct vivi_buffer *buf = container_of(vb, struct vivi_buffer, vb);
- unsigned long size;
-
- dprintk(dev, 1, "%s, field=%d\n", __func__, vb->v4l2_buf.field);
-
- BUG_ON(NULL == dev->fmt);
-
- /*
- * Theses properties only change when queue is idle, see s_fmt.
- * The below checks should not be performed here, on each
- * buffer_prepare (i.e. on each qbuf). Most of the code in this function
- * should thus be moved to buffer_init and s_fmt.
- */
- if (dev->width < 48 || dev->width > MAX_WIDTH ||
- dev->height < 32 || dev->height > MAX_HEIGHT)
- return -EINVAL;
-
- size = dev->width * dev->height * dev->pixelsize;
- if (vb2_plane_size(vb, 0) < size) {
- dprintk(dev, 1, "%s data will not fit into plane (%lu < %lu)\n",
- __func__, vb2_plane_size(vb, 0), size);
- return -EINVAL;
- }
-
- vb2_set_plane_payload(&buf->vb, 0, size);
-
- precalculate_bars(dev);
- precalculate_line(dev);
-
- return 0;
-}
-
-static void buffer_queue(struct vb2_buffer *vb)
-{
- struct vivi_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
- struct vivi_buffer *buf = container_of(vb, struct vivi_buffer, vb);
- struct vivi_dmaqueue *vidq = &dev->vidq;
- unsigned long flags = 0;
-
- dprintk(dev, 1, "%s\n", __func__);
-
- spin_lock_irqsave(&dev->slock, flags);
- list_add_tail(&buf->list, &vidq->active);
- spin_unlock_irqrestore(&dev->slock, flags);
-}
-
-static int start_streaming(struct vb2_queue *vq, unsigned int count)
-{
- struct vivi_dev *dev = vb2_get_drv_priv(vq);
- int err;
-
- dprintk(dev, 1, "%s\n", __func__);
- dev->seq_count = 0;
- err = vivi_start_generating(dev);
- if (err) {
- struct vivi_buffer *buf, *tmp;
-
- list_for_each_entry_safe(buf, tmp, &dev->vidq.active, list) {
- list_del(&buf->list);
- vb2_buffer_done(&buf->vb, VB2_BUF_STATE_QUEUED);
- }
- }
- return err;
-}
-
-/* abort streaming and wait for last buffer */
-static void stop_streaming(struct vb2_queue *vq)
-{
- struct vivi_dev *dev = vb2_get_drv_priv(vq);
- dprintk(dev, 1, "%s\n", __func__);
- vivi_stop_generating(dev);
-}
-
-static void vivi_lock(struct vb2_queue *vq)
-{
- struct vivi_dev *dev = vb2_get_drv_priv(vq);
- mutex_lock(&dev->mutex);
-}
-
-static void vivi_unlock(struct vb2_queue *vq)
-{
- struct vivi_dev *dev = vb2_get_drv_priv(vq);
- mutex_unlock(&dev->mutex);
-}
-
-
-static const struct vb2_ops vivi_video_qops = {
- .queue_setup = queue_setup,
- .buf_prepare = buffer_prepare,
- .buf_queue = buffer_queue,
- .start_streaming = start_streaming,
- .stop_streaming = stop_streaming,
- .wait_prepare = vivi_unlock,
- .wait_finish = vivi_lock,
-};
-
-/* ------------------------------------------------------------------
- IOCTL vidioc handling
- ------------------------------------------------------------------*/
-static int vidioc_querycap(struct file *file, void *priv,
- struct v4l2_capability *cap)
-{
- struct vivi_dev *dev = video_drvdata(file);
-
- strcpy(cap->driver, "vivi");
- strcpy(cap->card, "vivi");
- snprintf(cap->bus_info, sizeof(cap->bus_info),
- "platform:%s", dev->v4l2_dev.name);
- cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING |
- V4L2_CAP_READWRITE;
- cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
- return 0;
-}
-
-static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_fmtdesc *f)
-{
- const struct vivi_fmt *fmt;
-
- if (f->index >= ARRAY_SIZE(formats))
- return -EINVAL;
-
- fmt = &formats[f->index];
-
- strlcpy(f->description, fmt->name, sizeof(f->description));
- f->pixelformat = fmt->fourcc;
- return 0;
-}
-
-static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct vivi_dev *dev = video_drvdata(file);
-
- f->fmt.pix.width = dev->width;
- f->fmt.pix.height = dev->height;
- f->fmt.pix.field = V4L2_FIELD_INTERLACED;
- f->fmt.pix.pixelformat = dev->fmt->fourcc;
- f->fmt.pix.bytesperline =
- (f->fmt.pix.width * dev->fmt->depth) >> 3;
- f->fmt.pix.sizeimage =
- f->fmt.pix.height * f->fmt.pix.bytesperline;
- if (dev->fmt->is_yuv)
- f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
- else
- f->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB;
- return 0;
-}
-
-static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct vivi_dev *dev = video_drvdata(file);
- const struct vivi_fmt *fmt;
-
- fmt = get_format(f);
- if (!fmt) {
- dprintk(dev, 1, "Fourcc format (0x%08x) unknown.\n",
- f->fmt.pix.pixelformat);
- f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
- fmt = get_format(f);
- }
-
- f->fmt.pix.field = V4L2_FIELD_INTERLACED;
- v4l_bound_align_image(&f->fmt.pix.width, 48, MAX_WIDTH, 2,
- &f->fmt.pix.height, 32, MAX_HEIGHT, 0, 0);
- f->fmt.pix.bytesperline =
- (f->fmt.pix.width * fmt->depth) >> 3;
- f->fmt.pix.sizeimage =
- f->fmt.pix.height * f->fmt.pix.bytesperline;
- if (fmt->is_yuv)
- f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
- else
- f->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB;
- return 0;
-}
-
-static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct vivi_dev *dev = video_drvdata(file);
- struct vb2_queue *q = &dev->vb_vidq;
-
- int ret = vidioc_try_fmt_vid_cap(file, priv, f);
- if (ret < 0)
- return ret;
-
- if (vb2_is_busy(q)) {
- dprintk(dev, 1, "%s device busy\n", __func__);
- return -EBUSY;
- }
-
- dev->fmt = get_format(f);
- dev->pixelsize = dev->fmt->depth / 8;
- dev->width = f->fmt.pix.width;
- dev->height = f->fmt.pix.height;
-
- return 0;
-}
-
-static int vidioc_enum_framesizes(struct file *file, void *fh,
- struct v4l2_frmsizeenum *fsize)
-{
- static const struct v4l2_frmsize_stepwise sizes = {
- 48, MAX_WIDTH, 4,
- 32, MAX_HEIGHT, 1
- };
- int i;
-
- if (fsize->index)
- return -EINVAL;
- for (i = 0; i < ARRAY_SIZE(formats); i++)
- if (formats[i].fourcc == fsize->pixel_format)
- break;
- if (i == ARRAY_SIZE(formats))
- return -EINVAL;
- fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
- fsize->stepwise = sizes;
- return 0;
-}
-
-/* only one input in this sample driver */
-static int vidioc_enum_input(struct file *file, void *priv,
- struct v4l2_input *inp)
-{
- if (inp->index >= NUM_INPUTS)
- return -EINVAL;
-
- inp->type = V4L2_INPUT_TYPE_CAMERA;
- sprintf(inp->name, "Camera %u", inp->index);
- return 0;
-}
-
-static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
-{
- struct vivi_dev *dev = video_drvdata(file);
-
- *i = dev->input;
- return 0;
-}
-
-static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
-{
- struct vivi_dev *dev = video_drvdata(file);
-
- if (i >= NUM_INPUTS)
- return -EINVAL;
-
- if (i == dev->input)
- return 0;
-
- dev->input = i;
- /*
- * Modify the brightness range depending on the input.
- * This makes it easy to use vivi 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.
- */
- v4l2_ctrl_modify_range(dev->brightness,
- 128 * i, 255 + 128 * i, 1, 127 + 128 * i);
- precalculate_bars(dev);
- precalculate_line(dev);
- return 0;
-}
-
-/* timeperframe is arbitrary and continuous */
-static int vidioc_enum_frameintervals(struct file *file, void *priv,
- struct v4l2_frmivalenum *fival)
-{
- const struct vivi_fmt *fmt;
-
- if (fival->index)
- return -EINVAL;
-
- fmt = __get_format(fival->pixel_format);
- if (!fmt)
- return -EINVAL;
-
- /* check for valid width/height */
- if (fival->width < 48 || fival->width > MAX_WIDTH || (fival->width & 3))
- return -EINVAL;
- if (fival->height < 32 || fival->height > MAX_HEIGHT)
- return -EINVAL;
-
- fival->type = V4L2_FRMIVAL_TYPE_CONTINUOUS;
-
- /* fill in stepwise (step=1.0 is required by V4L2 spec) */
- fival->stepwise.min = tpf_min;
- fival->stepwise.max = tpf_max;
- fival->stepwise.step = (struct v4l2_fract) {1, 1};
-
- return 0;
-}
-
-static int vidioc_g_parm(struct file *file, void *priv,
- struct v4l2_streamparm *parm)
-{
- struct vivi_dev *dev = video_drvdata(file);
-
- if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
-
- parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
- parm->parm.capture.timeperframe = dev->timeperframe;
- parm->parm.capture.readbuffers = 1;
- return 0;
-}
-
-#define FRACT_CMP(a, OP, b) \
- ((u64)(a).numerator * (b).denominator OP (u64)(b).numerator * (a).denominator)
-
-static int vidioc_s_parm(struct file *file, void *priv,
- struct v4l2_streamparm *parm)
-{
- struct vivi_dev *dev = video_drvdata(file);
- struct v4l2_fract tpf;
-
- if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
-
- tpf = parm->parm.capture.timeperframe;
-
- /* tpf: {*, 0} resets timing; clip to [min, max]*/
- tpf = tpf.denominator ? tpf : tpf_default;
- tpf = FRACT_CMP(tpf, <, tpf_min) ? tpf_min : tpf;
- tpf = FRACT_CMP(tpf, >, tpf_max) ? tpf_max : tpf;
-
- dev->timeperframe = tpf;
- parm->parm.capture.timeperframe = tpf;
- parm->parm.capture.readbuffers = 1;
- return 0;
-}
-
-/* --- controls ---------------------------------------------- */
-
-static int vivi_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct vivi_dev *dev = container_of(ctrl->handler, struct vivi_dev, ctrl_handler);
-
- if (ctrl == dev->autogain)
- dev->gain->val = jiffies & 0xff;
- return 0;
-}
-
-static int vivi_s_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct vivi_dev *dev = container_of(ctrl->handler, struct vivi_dev, ctrl_handler);
-
- switch (ctrl->id) {
- case V4L2_CID_ALPHA_COMPONENT:
- dev->alpha_component = ctrl->val;
- break;
- default:
- if (ctrl == dev->button)
- dev->button_pressed = 30;
- break;
- }
- return 0;
-}
-
-/* ------------------------------------------------------------------
- File operations for the device
- ------------------------------------------------------------------*/
-
-static const struct v4l2_ctrl_ops vivi_ctrl_ops = {
- .g_volatile_ctrl = vivi_g_volatile_ctrl,
- .s_ctrl = vivi_s_ctrl,
-};
-
-#define VIVI_CID_CUSTOM_BASE (V4L2_CID_USER_BASE | 0xf000)
-
-static const struct v4l2_ctrl_config vivi_ctrl_button = {
- .ops = &vivi_ctrl_ops,
- .id = VIVI_CID_CUSTOM_BASE + 0,
- .name = "Button",
- .type = V4L2_CTRL_TYPE_BUTTON,
-};
-
-static const struct v4l2_ctrl_config vivi_ctrl_boolean = {
- .ops = &vivi_ctrl_ops,
- .id = VIVI_CID_CUSTOM_BASE + 1,
- .name = "Boolean",
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .min = 0,
- .max = 1,
- .step = 1,
- .def = 1,
-};
-
-static const struct v4l2_ctrl_config vivi_ctrl_int32 = {
- .ops = &vivi_ctrl_ops,
- .id = VIVI_CID_CUSTOM_BASE + 2,
- .name = "Integer 32 Bits",
- .type = V4L2_CTRL_TYPE_INTEGER,
- .min = -0x80000000LL,
- .max = 0x7fffffff,
- .step = 1,
-};
-
-static const struct v4l2_ctrl_config vivi_ctrl_int64 = {
- .ops = &vivi_ctrl_ops,
- .id = VIVI_CID_CUSTOM_BASE + 3,
- .name = "Integer 64 Bits",
- .type = V4L2_CTRL_TYPE_INTEGER64,
- .min = LLONG_MIN,
- .max = LLONG_MAX,
- .step = 1,
-};
-
-static const char * const vivi_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 vivi_ctrl_menu = {
- .ops = &vivi_ctrl_ops,
- .id = VIVI_CID_CUSTOM_BASE + 4,
- .name = "Menu",
- .type = V4L2_CTRL_TYPE_MENU,
- .min = 1,
- .max = 4,
- .def = 3,
- .menu_skip_mask = 0x04,
- .qmenu = vivi_ctrl_menu_strings,
-};
-
-static const struct v4l2_ctrl_config vivi_ctrl_string = {
- .ops = &vivi_ctrl_ops,
- .id = VIVI_CID_CUSTOM_BASE + 5,
- .name = "String",
- .type = V4L2_CTRL_TYPE_STRING,
- .min = 2,
- .max = 4,
- .step = 1,
-};
-
-static const struct v4l2_ctrl_config vivi_ctrl_bitmask = {
- .ops = &vivi_ctrl_ops,
- .id = VIVI_CID_CUSTOM_BASE + 6,
- .name = "Bitmask",
- .type = V4L2_CTRL_TYPE_BITMASK,
- .def = 0x80002000,
- .min = 0,
- .max = 0x80402010,
- .step = 0,
-};
-
-static const s64 vivi_ctrl_int_menu_values[] = {
- 1, 1, 2, 3, 5, 8, 13, 21, 42,
-};
-
-static const struct v4l2_ctrl_config vivi_ctrl_int_menu = {
- .ops = &vivi_ctrl_ops,
- .id = VIVI_CID_CUSTOM_BASE + 7,
- .name = "Integer menu",
- .type = V4L2_CTRL_TYPE_INTEGER_MENU,
- .min = 1,
- .max = 8,
- .def = 4,
- .menu_skip_mask = 0x02,
- .qmenu_int = vivi_ctrl_int_menu_values,
-};
-
-static const struct v4l2_file_operations vivi_fops = {
- .owner = THIS_MODULE,
- .open = v4l2_fh_open,
- .release = vb2_fop_release,
- .read = vb2_fop_read,
- .poll = vb2_fop_poll,
- .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */
- .mmap = vb2_fop_mmap,
-};
-
-static const struct v4l2_ioctl_ops vivi_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_enum_framesizes = vidioc_enum_framesizes,
- .vidioc_reqbufs = vb2_ioctl_reqbufs,
- .vidioc_create_bufs = vb2_ioctl_create_bufs,
- .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
- .vidioc_querybuf = vb2_ioctl_querybuf,
- .vidioc_qbuf = vb2_ioctl_qbuf,
- .vidioc_dqbuf = vb2_ioctl_dqbuf,
- .vidioc_enum_input = vidioc_enum_input,
- .vidioc_g_input = vidioc_g_input,
- .vidioc_s_input = vidioc_s_input,
- .vidioc_enum_frameintervals = vidioc_enum_frameintervals,
- .vidioc_g_parm = vidioc_g_parm,
- .vidioc_s_parm = vidioc_s_parm,
- .vidioc_streamon = vb2_ioctl_streamon,
- .vidioc_streamoff = vb2_ioctl_streamoff,
- .vidioc_log_status = v4l2_ctrl_log_status,
- .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
- .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
-};
-
-static const struct video_device vivi_template = {
- .name = "vivi",
- .fops = &vivi_fops,
- .ioctl_ops = &vivi_ioctl_ops,
- .release = video_device_release_empty,
-};
-
-/* -----------------------------------------------------------------
- Initialization and module stuff
- ------------------------------------------------------------------*/
-
-static int vivi_release(void)
-{
- struct vivi_dev *dev;
- struct list_head *list;
-
- while (!list_empty(&vivi_devlist)) {
- list = vivi_devlist.next;
- list_del(list);
- dev = list_entry(list, struct vivi_dev, vivi_devlist);
-
- v4l2_info(&dev->v4l2_dev, "unregistering %s\n",
- video_device_node_name(&dev->vdev));
- video_unregister_device(&dev->vdev);
- v4l2_device_unregister(&dev->v4l2_dev);
- v4l2_ctrl_handler_free(&dev->ctrl_handler);
- kfree(dev);
- }
-
- return 0;
-}
-
-static int __init vivi_create_instance(int inst)
-{
- struct vivi_dev *dev;
- struct video_device *vfd;
- struct v4l2_ctrl_handler *hdl;
- struct vb2_queue *q;
- int ret;
-
- dev = kzalloc(sizeof(*dev), GFP_KERNEL);
- if (!dev)
- return -ENOMEM;
-
- snprintf(dev->v4l2_dev.name, sizeof(dev->v4l2_dev.name),
- "%s-%03d", VIVI_MODULE_NAME, inst);
- ret = v4l2_device_register(NULL, &dev->v4l2_dev);
- if (ret)
- goto free_dev;
-
- dev->fmt = &formats[0];
- dev->timeperframe = tpf_default;
- dev->width = 640;
- dev->height = 480;
- dev->pixelsize = dev->fmt->depth / 8;
- hdl = &dev->ctrl_handler;
- v4l2_ctrl_handler_init(hdl, 11);
- dev->volume = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
- V4L2_CID_AUDIO_VOLUME, 0, 255, 1, 200);
- dev->brightness = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
- V4L2_CID_BRIGHTNESS, 0, 255, 1, 127);
- dev->contrast = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
- V4L2_CID_CONTRAST, 0, 255, 1, 16);
- dev->saturation = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
- V4L2_CID_SATURATION, 0, 255, 1, 127);
- dev->hue = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
- V4L2_CID_HUE, -128, 127, 1, 0);
- dev->autogain = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
- V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
- dev->gain = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
- V4L2_CID_GAIN, 0, 255, 1, 100);
- dev->alpha = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
- V4L2_CID_ALPHA_COMPONENT, 0, 255, 1, 0);
- dev->button = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_button, NULL);
- dev->int32 = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_int32, NULL);
- dev->int64 = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_int64, NULL);
- dev->boolean = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_boolean, NULL);
- dev->menu = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_menu, NULL);
- dev->string = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_string, NULL);
- dev->bitmask = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_bitmask, NULL);
- dev->int_menu = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_int_menu, NULL);
- if (hdl->error) {
- ret = hdl->error;
- goto unreg_dev;
- }
- v4l2_ctrl_auto_cluster(2, &dev->autogain, 0, true);
- dev->v4l2_dev.ctrl_handler = hdl;
-
- /* initialize locks */
- spin_lock_init(&dev->slock);
-
- /* initialize queue */
- q = &dev->vb_vidq;
- q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ;
- q->drv_priv = dev;
- q->buf_struct_size = sizeof(struct vivi_buffer);
- q->ops = &vivi_video_qops;
- q->mem_ops = &vb2_vmalloc_memops;
- q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
-
- ret = vb2_queue_init(q);
- if (ret)
- goto unreg_dev;
-
- mutex_init(&dev->mutex);
-
- /* init video dma queues */
- INIT_LIST_HEAD(&dev->vidq.active);
- init_waitqueue_head(&dev->vidq.wq);
-
- vfd = &dev->vdev;
- *vfd = vivi_template;
- vfd->debug = debug;
- vfd->v4l2_dev = &dev->v4l2_dev;
- vfd->queue = q;
-
- /*
- * 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);
-
- ret = video_register_device(vfd, VFL_TYPE_GRABBER, video_nr);
- if (ret < 0)
- goto unreg_dev;
-
- /* Now that everything is fine, let's add it to device list */
- list_add_tail(&dev->vivi_devlist, &vivi_devlist);
-
- v4l2_info(&dev->v4l2_dev, "V4L2 device registered as %s\n",
- video_device_node_name(vfd));
- return 0;
-
-unreg_dev:
- v4l2_ctrl_handler_free(hdl);
- v4l2_device_unregister(&dev->v4l2_dev);
-free_dev:
- kfree(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 __init vivi_init(void)
-{
- const struct font_desc *font = find_font("VGA8x16");
- int ret = 0, i;
-
- if (font == NULL) {
- printk(KERN_ERR "vivi: could not find font\n");
- return -ENODEV;
- }
- font8x16 = font->data;
-
- if (n_devs <= 0)
- n_devs = 1;
-
- for (i = 0; i < n_devs; i++) {
- ret = vivi_create_instance(i);
- if (ret) {
- /* If some instantiations succeeded, keep driver */
- if (i)
- ret = 0;
- break;
- }
- }
-
- if (ret < 0) {
- printk(KERN_ERR "vivi: error %d while loading driver\n", ret);
- return ret;
- }
-
- printk(KERN_INFO "Video Technology Magazine Virtual Video "
- "Capture Board ver %s successfully loaded.\n",
- VIVI_VERSION);
-
- /* n_devs will reflect the actual number of allocated devices */
- n_devs = i;
-
- return ret;
-}
-
-static void __exit vivi_exit(void)
-{
- vivi_release();
-}
-
-module_init(vivi_init);
-module_exit(vivi_exit);
diff --git a/drivers/media/platform/vivid/Kconfig b/drivers/media/platform/vivid/Kconfig
new file mode 100644
index 000000000000..d71139a2ae00
--- /dev/null
+++ b/drivers/media/platform/vivid/Kconfig
@@ -0,0 +1,19 @@
+config VIDEO_VIVID
+ tristate "Virtual Video Test Driver"
+ depends on VIDEO_DEV && VIDEO_V4L2 && !SPARC32 && !SPARC64
+ select FONT_SUPPORT
+ select FONT_8x16
+ select VIDEOBUF2_VMALLOC
+ default n
+ ---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.
diff --git a/drivers/media/platform/vivid/Makefile b/drivers/media/platform/vivid/Makefile
new file mode 100644
index 000000000000..756fc12851df
--- /dev/null
+++ b/drivers/media/platform/vivid/Makefile
@@ -0,0 +1,6 @@
+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-tpg.o vivid-tpg-colors.o
+obj-$(CONFIG_VIDEO_VIVID) += vivid.o
diff --git a/drivers/media/platform/vivid/vivid-core.c b/drivers/media/platform/vivid/vivid-core.c
new file mode 100644
index 000000000000..2c61a62ab48b
--- /dev/null
+++ b/drivers/media/platform/vivid/vivid-core.c
@@ -0,0 +1,1390 @@
+/*
+ * vivid-core.c - A Virtual Video Test Driver, core initialization
+ *
+ * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/font.h>
+#include <linux/mutex.h>
+#include <linux/videodev2.h>
+#include <linux/v4l2-dv-timings.h>
+#include <media/videobuf2-vmalloc.h>
+#include <media/v4l2-dv-timings.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-event.h>
+
+#include "vivid-core.h"
+#include "vivid-vid-common.h"
+#include "vivid-vid-cap.h"
+#include "vivid-vid-out.h"
+#include "vivid-radio-common.h"
+#include "vivid-radio-rx.h"
+#include "vivid-radio-tx.h"
+#include "vivid-sdr-cap.h"
+#include "vivid-vbi-cap.h"
+#include "vivid-vbi-out.h"
+#include "vivid-osd.h"
+#include "vivid-ctrls.h"
+
+#define VIVID_MODULE_NAME "vivid"
+
+/* The maximum number of vivid devices */
+#define VIVID_MAX_DEVS 64
+
+MODULE_DESCRIPTION("Virtual Video Test Driver");
+MODULE_AUTHOR("Hans Verkuil");
+MODULE_LICENSE("GPL");
+
+static unsigned n_devs = 1;
+module_param(n_devs, uint, 0444);
+MODULE_PARM_DESC(n_devs, " number of driver instances to create");
+
+static int vid_cap_nr[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = -1 };
+module_param_array(vid_cap_nr, int, NULL, 0444);
+MODULE_PARM_DESC(vid_cap_nr, " videoX start number, -1 is autodetect");
+
+static int vid_out_nr[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = -1 };
+module_param_array(vid_out_nr, int, NULL, 0444);
+MODULE_PARM_DESC(vid_out_nr, " videoX start number, -1 is autodetect");
+
+static int vbi_cap_nr[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = -1 };
+module_param_array(vbi_cap_nr, int, NULL, 0444);
+MODULE_PARM_DESC(vbi_cap_nr, " vbiX start number, -1 is autodetect");
+
+static int vbi_out_nr[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = -1 };
+module_param_array(vbi_out_nr, int, NULL, 0444);
+MODULE_PARM_DESC(vbi_out_nr, " vbiX start number, -1 is autodetect");
+
+static int sdr_cap_nr[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = -1 };
+module_param_array(sdr_cap_nr, int, NULL, 0444);
+MODULE_PARM_DESC(sdr_cap_nr, " swradioX start number, -1 is autodetect");
+
+static int radio_rx_nr[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = -1 };
+module_param_array(radio_rx_nr, int, NULL, 0444);
+MODULE_PARM_DESC(radio_rx_nr, " radioX start number, -1 is autodetect");
+
+static int radio_tx_nr[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = -1 };
+module_param_array(radio_tx_nr, int, NULL, 0444);
+MODULE_PARM_DESC(radio_tx_nr, " radioX start number, -1 is autodetect");
+
+static int ccs_cap_mode[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = -1 };
+module_param_array(ccs_cap_mode, int, NULL, 0444);
+MODULE_PARM_DESC(ccs_cap_mode, " capture crop/compose/scale mode:\n"
+ "\t\t bit 0=crop, 1=compose, 2=scale,\n"
+ "\t\t -1=user-controlled (default)");
+
+static int ccs_out_mode[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = -1 };
+module_param_array(ccs_out_mode, int, NULL, 0444);
+MODULE_PARM_DESC(ccs_out_mode, " output crop/compose/scale mode:\n"
+ "\t\t bit 0=crop, 1=compose, 2=scale,\n"
+ "\t\t -1=user-controlled (default)");
+
+static unsigned multiplanar[VIVID_MAX_DEVS];
+module_param_array(multiplanar, uint, NULL, 0444);
+MODULE_PARM_DESC(multiplanar, " 0 (default) is alternating single and multiplanar devices,\n"
+ "\t\t 1 is single planar devices,\n"
+ "\t\t 2 is multiplanar devices");
+
+/* Default: video + vbi-cap (raw and sliced) + radio rx + radio tx + sdr + vbi-out + vid-out */
+static unsigned node_types[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = 0x1d3d };
+module_param_array(node_types, uint, NULL, 0444);
+MODULE_PARM_DESC(node_types, " node types, default is 0x1d3d. Bitmask with the following meaning:\n"
+ "\t\t bit 0: Video Capture node\n"
+ "\t\t bit 2-3: VBI Capture node: 0 = none, 1 = raw vbi, 2 = sliced vbi, 3 = both\n"
+ "\t\t bit 4: Radio Receiver node\n"
+ "\t\t bit 5: Software Defined Radio Receiver node\n"
+ "\t\t bit 8: Video Output node\n"
+ "\t\t bit 10-11: VBI Output node: 0 = none, 1 = raw vbi, 2 = sliced vbi, 3 = both\n"
+ "\t\t bit 12: Radio Transmitter node\n"
+ "\t\t bit 16: Framebuffer for testing overlays");
+
+/* Default: 4 inputs */
+static unsigned num_inputs[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = 4 };
+module_param_array(num_inputs, uint, NULL, 0444);
+MODULE_PARM_DESC(num_inputs, " number of inputs, default is 4");
+
+/* Default: input 0 = WEBCAM, 1 = TV, 2 = SVID, 3 = HDMI */
+static unsigned input_types[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = 0xe4 };
+module_param_array(input_types, uint, NULL, 0444);
+MODULE_PARM_DESC(input_types, " input types, default is 0xe4. Two bits per input,\n"
+ "\t\t bits 0-1 == input 0, bits 31-30 == input 15.\n"
+ "\t\t Type 0 == webcam, 1 == TV, 2 == S-Video, 3 == HDMI");
+
+/* Default: 2 outputs */
+static unsigned num_outputs[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = 2 };
+module_param_array(num_outputs, uint, NULL, 0444);
+MODULE_PARM_DESC(num_outputs, " number of outputs, default is 2");
+
+/* Default: output 0 = SVID, 1 = HDMI */
+static unsigned output_types[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = 2 };
+module_param_array(output_types, uint, NULL, 0444);
+MODULE_PARM_DESC(output_types, " output types, default is 0x02. One bit per output,\n"
+ "\t\t bit 0 == output 0, bit 15 == output 15.\n"
+ "\t\t Type 0 == S-Video, 1 == HDMI");
+
+unsigned vivid_debug;
+module_param(vivid_debug, uint, 0644);
+MODULE_PARM_DESC(vivid_debug, " activates debug info");
+
+static bool no_error_inj;
+module_param(no_error_inj, bool, 0444);
+MODULE_PARM_DESC(no_error_inj, " if set disable the error injecting controls");
+
+static 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,
+ 0x63, 0x3a, 0xaa, 0x55, 0x00, 0x00, 0x00, 0x00,
+ 0x0a, 0x18, 0x01, 0x03, 0x80, 0x10, 0x09, 0x78,
+ 0x0e, 0x00, 0xb2, 0xa0, 0x57, 0x49, 0x9b, 0x26,
+ 0x10, 0x48, 0x4f, 0x2f, 0xcf, 0x00, 0x31, 0x59,
+ 0x45, 0x59, 0x81, 0x80, 0x81, 0x40, 0x90, 0x40,
+ 0x95, 0x00, 0xa9, 0x40, 0xb3, 0x00, 0x02, 0x3a,
+ 0x80, 0x18, 0x71, 0x38, 0x2d, 0x40, 0x58, 0x2c,
+ 0x46, 0x00, 0x10, 0x09, 0x00, 0x00, 0x00, 0x1e,
+ 0x00, 0x00, 0x00, 0xfd, 0x00, 0x18, 0x55, 0x18,
+ 0x5e, 0x11, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc, 0x00, 'v',
+ '4', 'l', '2', '-', 'h', 'd', 'm', 'i',
+ 0x0a, 0x0a, 0x0a, 0x0a, 0x00, 0x00, 0x00, 0x10,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xf0,
+
+ 0x02, 0x03, 0x1a, 0xc0, 0x48, 0xa2, 0x10, 0x04,
+ 0x02, 0x01, 0x21, 0x14, 0x13, 0x23, 0x09, 0x07,
+ 0x07, 0x65, 0x03, 0x0c, 0x00, 0x10, 0x00, 0xe2,
+ 0x00, 0x2a, 0x01, 0x1d, 0x00, 0x80, 0x51, 0xd0,
+ 0x1c, 0x20, 0x40, 0x80, 0x35, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x1e, 0x8c, 0x0a, 0xd0, 0x8a,
+ 0x20, 0xe0, 0x2d, 0x10, 0x10, 0x3e, 0x96, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd7
+};
+
+void vivid_lock(struct vb2_queue *vq)
+{
+ struct vivid_dev *dev = vb2_get_drv_priv(vq);
+
+ mutex_lock(&dev->mutex);
+}
+
+void vivid_unlock(struct vb2_queue *vq)
+{
+ struct vivid_dev *dev = vb2_get_drv_priv(vq);
+
+ mutex_unlock(&dev->mutex);
+}
+
+static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct vivid_dev *dev = video_drvdata(file);
+ struct video_device *vdev = video_devdata(file);
+
+ strcpy(cap->driver, "vivid");
+ strcpy(cap->card, "vivid");
+ snprintf(cap->bus_info, sizeof(cap->bus_info),
+ "platform:%s", dev->v4l2_dev.name);
+
+ if (vdev->vfl_type == VFL_TYPE_GRABBER && vdev->vfl_dir == VFL_DIR_RX)
+ cap->device_caps = dev->vid_cap_caps;
+ if (vdev->vfl_type == VFL_TYPE_GRABBER && vdev->vfl_dir == VFL_DIR_TX)
+ cap->device_caps = dev->vid_out_caps;
+ else if (vdev->vfl_type == VFL_TYPE_VBI && vdev->vfl_dir == VFL_DIR_RX)
+ cap->device_caps = dev->vbi_cap_caps;
+ else if (vdev->vfl_type == VFL_TYPE_VBI && vdev->vfl_dir == VFL_DIR_TX)
+ cap->device_caps = dev->vbi_out_caps;
+ else if (vdev->vfl_type == VFL_TYPE_SDR)
+ cap->device_caps = dev->sdr_cap_caps;
+ else if (vdev->vfl_type == VFL_TYPE_RADIO && vdev->vfl_dir == VFL_DIR_RX)
+ cap->device_caps = dev->radio_rx_caps;
+ else if (vdev->vfl_type == VFL_TYPE_RADIO && vdev->vfl_dir == VFL_DIR_TX)
+ cap->device_caps = dev->radio_tx_caps;
+ cap->capabilities = dev->vid_cap_caps | dev->vid_out_caps |
+ dev->vbi_cap_caps | dev->vbi_out_caps |
+ dev->radio_rx_caps | dev->radio_tx_caps |
+ dev->sdr_cap_caps | V4L2_CAP_DEVICE_CAPS;
+ return 0;
+}
+
+static int vidioc_s_hw_freq_seek(struct file *file, void *fh, const struct v4l2_hw_freq_seek *a)
+{
+ struct video_device *vdev = video_devdata(file);
+
+ if (vdev->vfl_type == VFL_TYPE_RADIO)
+ return vivid_radio_rx_s_hw_freq_seek(file, fh, a);
+ return -ENOTTY;
+}
+
+static int vidioc_enum_freq_bands(struct file *file, void *fh, struct v4l2_frequency_band *band)
+{
+ struct video_device *vdev = video_devdata(file);
+
+ if (vdev->vfl_type == VFL_TYPE_RADIO)
+ return vivid_radio_rx_enum_freq_bands(file, fh, band);
+ if (vdev->vfl_type == VFL_TYPE_SDR)
+ return vivid_sdr_enum_freq_bands(file, fh, band);
+ return -ENOTTY;
+}
+
+static int vidioc_g_tuner(struct file *file, void *fh, struct v4l2_tuner *vt)
+{
+ struct video_device *vdev = video_devdata(file);
+
+ if (vdev->vfl_type == VFL_TYPE_RADIO)
+ return vivid_radio_rx_g_tuner(file, fh, vt);
+ if (vdev->vfl_type == VFL_TYPE_SDR)
+ return vivid_sdr_g_tuner(file, fh, vt);
+ return vivid_video_g_tuner(file, fh, vt);
+}
+
+static int vidioc_s_tuner(struct file *file, void *fh, const struct v4l2_tuner *vt)
+{
+ struct video_device *vdev = video_devdata(file);
+
+ if (vdev->vfl_type == VFL_TYPE_RADIO)
+ return vivid_radio_rx_s_tuner(file, fh, vt);
+ if (vdev->vfl_type == VFL_TYPE_SDR)
+ return vivid_sdr_s_tuner(file, fh, vt);
+ return vivid_video_s_tuner(file, fh, vt);
+}
+
+static int vidioc_g_frequency(struct file *file, void *fh, struct v4l2_frequency *vf)
+{
+ struct vivid_dev *dev = video_drvdata(file);
+ struct video_device *vdev = video_devdata(file);
+
+ if (vdev->vfl_type == VFL_TYPE_RADIO)
+ return vivid_radio_g_frequency(file,
+ vdev->vfl_dir == VFL_DIR_RX ?
+ &dev->radio_rx_freq : &dev->radio_tx_freq, vf);
+ if (vdev->vfl_type == VFL_TYPE_SDR)
+ return vivid_sdr_g_frequency(file, fh, vf);
+ return vivid_video_g_frequency(file, fh, vf);
+}
+
+static int vidioc_s_frequency(struct file *file, void *fh, const struct v4l2_frequency *vf)
+{
+ struct vivid_dev *dev = video_drvdata(file);
+ struct video_device *vdev = video_devdata(file);
+
+ if (vdev->vfl_type == VFL_TYPE_RADIO)
+ return vivid_radio_s_frequency(file,
+ vdev->vfl_dir == VFL_DIR_RX ?
+ &dev->radio_rx_freq : &dev->radio_tx_freq, vf);
+ if (vdev->vfl_type == VFL_TYPE_SDR)
+ return vivid_sdr_s_frequency(file, fh, vf);
+ return vivid_video_s_frequency(file, fh, vf);
+}
+
+static int vidioc_overlay(struct file *file, void *fh, unsigned i)
+{
+ struct video_device *vdev = video_devdata(file);
+
+ if (vdev->vfl_dir == VFL_DIR_RX)
+ return vivid_vid_cap_overlay(file, fh, i);
+ return vivid_vid_out_overlay(file, fh, i);
+}
+
+static int vidioc_g_fbuf(struct file *file, void *fh, struct v4l2_framebuffer *a)
+{
+ struct video_device *vdev = video_devdata(file);
+
+ if (vdev->vfl_dir == VFL_DIR_RX)
+ return vivid_vid_cap_g_fbuf(file, fh, a);
+ return vivid_vid_out_g_fbuf(file, fh, a);
+}
+
+static int vidioc_s_fbuf(struct file *file, void *fh, const struct v4l2_framebuffer *a)
+{
+ struct video_device *vdev = video_devdata(file);
+
+ if (vdev->vfl_dir == VFL_DIR_RX)
+ return vivid_vid_cap_s_fbuf(file, fh, a);
+ return vivid_vid_out_s_fbuf(file, fh, a);
+}
+
+static int vidioc_s_std(struct file *file, void *fh, v4l2_std_id id)
+{
+ struct video_device *vdev = video_devdata(file);
+
+ if (vdev->vfl_dir == VFL_DIR_RX)
+ return vivid_vid_cap_s_std(file, fh, id);
+ return vivid_vid_out_s_std(file, fh, id);
+}
+
+static int vidioc_s_dv_timings(struct file *file, void *fh, struct v4l2_dv_timings *timings)
+{
+ struct video_device *vdev = video_devdata(file);
+
+ if (vdev->vfl_dir == VFL_DIR_RX)
+ return vivid_vid_cap_s_dv_timings(file, fh, timings);
+ return vivid_vid_out_s_dv_timings(file, fh, timings);
+}
+
+static int vidioc_cropcap(struct file *file, void *fh, struct v4l2_cropcap *cc)
+{
+ struct video_device *vdev = video_devdata(file);
+
+ if (vdev->vfl_dir == VFL_DIR_RX)
+ return vivid_vid_cap_cropcap(file, fh, cc);
+ return vivid_vid_out_cropcap(file, fh, cc);
+}
+
+static int vidioc_g_selection(struct file *file, void *fh,
+ struct v4l2_selection *sel)
+{
+ struct video_device *vdev = video_devdata(file);
+
+ if (vdev->vfl_dir == VFL_DIR_RX)
+ return vivid_vid_cap_g_selection(file, fh, sel);
+ return vivid_vid_out_g_selection(file, fh, sel);
+}
+
+static int vidioc_s_selection(struct file *file, void *fh,
+ struct v4l2_selection *sel)
+{
+ struct video_device *vdev = video_devdata(file);
+
+ if (vdev->vfl_dir == VFL_DIR_RX)
+ return vivid_vid_cap_s_selection(file, fh, sel);
+ return vivid_vid_out_s_selection(file, fh, sel);
+}
+
+static int vidioc_g_parm(struct file *file, void *fh,
+ struct v4l2_streamparm *parm)
+{
+ struct video_device *vdev = video_devdata(file);
+
+ if (vdev->vfl_dir == VFL_DIR_RX)
+ return vivid_vid_cap_g_parm(file, fh, parm);
+ return vivid_vid_out_g_parm(file, fh, parm);
+}
+
+static int vidioc_s_parm(struct file *file, void *fh,
+ struct v4l2_streamparm *parm)
+{
+ struct video_device *vdev = video_devdata(file);
+
+ if (vdev->vfl_dir == VFL_DIR_RX)
+ return vivid_vid_cap_s_parm(file, fh, parm);
+ return vivid_vid_out_g_parm(file, fh, parm);
+}
+
+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 unsigned int vivid_radio_poll(struct file *file, struct poll_table_struct *wait)
+{
+ struct video_device *vdev = video_devdata(file);
+
+ if (vdev->vfl_dir == VFL_DIR_RX)
+ return vivid_radio_rx_poll(file, wait);
+ return vivid_radio_tx_poll(file, wait);
+}
+
+static bool vivid_is_in_use(struct video_device *vdev)
+{
+ unsigned long flags;
+ bool res;
+
+ spin_lock_irqsave(&vdev->fh_lock, flags);
+ res = !list_empty(&vdev->fh_list);
+ spin_unlock_irqrestore(&vdev->fh_lock, flags);
+ return res;
+}
+
+static bool vivid_is_last_user(struct vivid_dev *dev)
+{
+ unsigned uses = vivid_is_in_use(&dev->vid_cap_dev) +
+ vivid_is_in_use(&dev->vid_out_dev) +
+ vivid_is_in_use(&dev->vbi_cap_dev) +
+ vivid_is_in_use(&dev->vbi_out_dev) +
+ vivid_is_in_use(&dev->sdr_cap_dev) +
+ vivid_is_in_use(&dev->radio_rx_dev) +
+ vivid_is_in_use(&dev->radio_tx_dev);
+
+ return uses == 1;
+}
+
+static int vivid_fop_release(struct file *file)
+{
+ struct vivid_dev *dev = video_drvdata(file);
+ struct video_device *vdev = video_devdata(file);
+
+ mutex_lock(&dev->mutex);
+ if (!no_error_inj && v4l2_fh_is_singular_file(file) &&
+ !video_is_registered(vdev) && vivid_is_last_user(dev)) {
+ /*
+ * I am the last user of this driver, and a disconnect
+ * was forced (since this video_device is unregistered),
+ * so re-register all video_device's again.
+ */
+ v4l2_info(&dev->v4l2_dev, "reconnect\n");
+ set_bit(V4L2_FL_REGISTERED, &dev->vid_cap_dev.flags);
+ set_bit(V4L2_FL_REGISTERED, &dev->vid_out_dev.flags);
+ set_bit(V4L2_FL_REGISTERED, &dev->vbi_cap_dev.flags);
+ set_bit(V4L2_FL_REGISTERED, &dev->vbi_out_dev.flags);
+ set_bit(V4L2_FL_REGISTERED, &dev->sdr_cap_dev.flags);
+ set_bit(V4L2_FL_REGISTERED, &dev->radio_rx_dev.flags);
+ set_bit(V4L2_FL_REGISTERED, &dev->radio_tx_dev.flags);
+ }
+ mutex_unlock(&dev->mutex);
+ if (file->private_data == dev->overlay_cap_owner)
+ dev->overlay_cap_owner = NULL;
+ if (file->private_data == dev->radio_rx_rds_owner) {
+ dev->radio_rx_rds_last_block = 0;
+ dev->radio_rx_rds_owner = NULL;
+ }
+ if (file->private_data == dev->radio_tx_rds_owner) {
+ dev->radio_tx_rds_last_block = 0;
+ dev->radio_tx_rds_owner = NULL;
+ }
+ if (vdev->queue)
+ return vb2_fop_release(file);
+ return v4l2_fh_release(file);
+}
+
+static const struct v4l2_file_operations vivid_fops = {
+ .owner = THIS_MODULE,
+ .open = v4l2_fh_open,
+ .release = vivid_fop_release,
+ .read = vb2_fop_read,
+ .write = vb2_fop_write,
+ .poll = vb2_fop_poll,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = vb2_fop_mmap,
+};
+
+static const struct v4l2_file_operations vivid_radio_fops = {
+ .owner = THIS_MODULE,
+ .open = v4l2_fh_open,
+ .release = vivid_fop_release,
+ .read = vivid_radio_read,
+ .write = vivid_radio_write,
+ .poll = vivid_radio_poll,
+ .unlocked_ioctl = video_ioctl2,
+};
+
+static const struct v4l2_ioctl_ops vivid_ioctl_ops = {
+ .vidioc_querycap = vidioc_querycap,
+
+ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid,
+ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
+ .vidioc_enum_fmt_vid_cap_mplane = vidioc_enum_fmt_vid_mplane,
+ .vidioc_g_fmt_vid_cap_mplane = vidioc_g_fmt_vid_cap_mplane,
+ .vidioc_try_fmt_vid_cap_mplane = vidioc_try_fmt_vid_cap_mplane,
+ .vidioc_s_fmt_vid_cap_mplane = vidioc_s_fmt_vid_cap_mplane,
+
+ .vidioc_enum_fmt_vid_out = vidioc_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_enum_fmt_vid_out_mplane = vidioc_enum_fmt_vid_mplane,
+ .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_cropcap = vidioc_cropcap,
+
+ .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_g_fmt_sdr_cap,
+ .vidioc_s_fmt_sdr_cap = vidioc_g_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,
+/* Not yet .vidioc_expbuf = vb2_ioctl_expbuf,*/
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
+
+ .vidioc_enum_input = vidioc_enum_input,
+ .vidioc_g_input = vidioc_g_input,
+ .vidioc_s_input = vidioc_s_input,
+ .vidioc_s_audio = vidioc_s_audio,
+ .vidioc_g_audio = vidioc_g_audio,
+ .vidioc_enumaudio = vidioc_enumaudio,
+ .vidioc_s_frequency = vidioc_s_frequency,
+ .vidioc_g_frequency = vidioc_g_frequency,
+ .vidioc_s_tuner = vidioc_s_tuner,
+ .vidioc_g_tuner = vidioc_g_tuner,
+ .vidioc_s_modulator = vidioc_s_modulator,
+ .vidioc_g_modulator = vidioc_g_modulator,
+ .vidioc_s_hw_freq_seek = vidioc_s_hw_freq_seek,
+ .vidioc_enum_freq_bands = vidioc_enum_freq_bands,
+
+ .vidioc_enum_output = vidioc_enum_output,
+ .vidioc_g_output = vidioc_g_output,
+ .vidioc_s_output = vidioc_s_output,
+ .vidioc_s_audout = vidioc_s_audout,
+ .vidioc_g_audout = vidioc_g_audout,
+ .vidioc_enumaudout = vidioc_enumaudout,
+
+ .vidioc_querystd = vidioc_querystd,
+ .vidioc_g_std = vidioc_g_std,
+ .vidioc_s_std = vidioc_s_std,
+ .vidioc_s_dv_timings = vidioc_s_dv_timings,
+ .vidioc_g_dv_timings = vidioc_g_dv_timings,
+ .vidioc_query_dv_timings = vidioc_query_dv_timings,
+ .vidioc_enum_dv_timings = vidioc_enum_dv_timings,
+ .vidioc_dv_timings_cap = vidioc_dv_timings_cap,
+ .vidioc_g_edid = vidioc_g_edid,
+ .vidioc_s_edid = vidioc_s_edid,
+
+ .vidioc_log_status = v4l2_ctrl_log_status,
+ .vidioc_subscribe_event = vidioc_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+/* -----------------------------------------------------------------
+ Initialization and module stuff
+ ------------------------------------------------------------------*/
+
+static int __init vivid_create_instance(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;
+ struct vb2_queue *q;
+ unsigned node_type = node_types[inst];
+ v4l2_std_id tvnorms_cap = 0, tvnorms_out = 0;
+ int ret;
+ int i;
+
+ /* allocate main vivid state structure */
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+
+ dev->inst = inst;
+
+ /* register v4l2_device */
+ snprintf(dev->v4l2_dev.name, sizeof(dev->v4l2_dev.name),
+ "%s-%03d", VIVID_MODULE_NAME, inst);
+ ret = v4l2_device_register(NULL, &dev->v4l2_dev);
+ if (ret)
+ goto free_dev;
+
+ /* start detecting feature set */
+
+ /* do we use single- or multi-planar? */
+ if (multiplanar[inst] == 0)
+ dev->multiplanar = inst & 1;
+ else
+ 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];
+
+ /* 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->has_audio_outputs = out_type_counter[SVID];
+
+ /* do we create a video capture device? */
+ dev->has_vid_cap = node_type & 0x0001;
+
+ /* do we create a vbi capture device? */
+ if (in_type_counter[TV] || in_type_counter[SVID]) {
+ dev->has_raw_vbi_cap = node_type & 0x0004;
+ dev->has_sliced_vbi_cap = node_type & 0x0008;
+ dev->has_vbi_cap = dev->has_raw_vbi_cap | dev->has_sliced_vbi_cap;
+ }
+
+ /* do we create a video output device? */
+ dev->has_vid_out = node_type & 0x0100;
+
+ /* do we create a vbi output device? */
+ if (out_type_counter[SVID]) {
+ dev->has_raw_vbi_out = node_type & 0x0400;
+ dev->has_sliced_vbi_out = node_type & 0x0800;
+ dev->has_vbi_out = dev->has_raw_vbi_out | dev->has_sliced_vbi_out;
+ }
+
+ /* do we create a radio receiver device? */
+ dev->has_radio_rx = node_type & 0x0010;
+
+ /* do we create a radio transmitter device? */
+ dev->has_radio_tx = node_type & 0x1000;
+
+ /* do we create a software defined radio capture device? */
+ dev->has_sdr_cap = node_type & 0x0020;
+
+ /* do we have a tuner? */
+ has_tuner = ((dev->has_vid_cap || dev->has_vbi_cap) && in_type_counter[TV]) ||
+ dev->has_radio_rx || dev->has_sdr_cap;
+
+ /* do we have a modulator? */
+ has_modulator = dev->has_radio_tx;
+
+ if (dev->has_vid_cap)
+ /* do we have a framebuffer for overlay testing? */
+ dev->has_fb = node_type & 0x10000;
+
+ /* can we do crop/compose/scaling while capturing? */
+ if (no_error_inj && ccs_cap == -1)
+ ccs_cap = 7;
+
+ /* if ccs_cap == -1, then the use 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 use can select it using controls */
+ if (ccs_out != -1) {
+ dev->has_crop_out = ccs_out & 1;
+ dev->has_compose_out = ccs_out & 2;
+ dev->has_scaler_out = ccs_out & 4;
+ v4l2_info(&dev->v4l2_dev, "Output Crop: %c Compose: %c Scaler: %c\n",
+ dev->has_crop_out ? 'Y' : 'N',
+ dev->has_compose_out ? 'Y' : 'N',
+ dev->has_scaler_out ? 'Y' : 'N');
+ }
+
+ /* end detecting feature set */
+
+ if (dev->has_vid_cap) {
+ /* set up the capabilities of the video capture device */
+ dev->vid_cap_caps = dev->multiplanar ?
+ V4L2_CAP_VIDEO_CAPTURE_MPLANE :
+ V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OVERLAY;
+ dev->vid_cap_caps |= V4L2_CAP_STREAMING | V4L2_CAP_READWRITE;
+ if (dev->has_audio_inputs)
+ dev->vid_cap_caps |= V4L2_CAP_AUDIO;
+ if (in_type_counter[TV])
+ dev->vid_cap_caps |= V4L2_CAP_TUNER;
+ }
+ if (dev->has_vid_out) {
+ /* set up the capabilities of the video output device */
+ dev->vid_out_caps = dev->multiplanar ?
+ V4L2_CAP_VIDEO_OUTPUT_MPLANE :
+ V4L2_CAP_VIDEO_OUTPUT;
+ if (dev->has_fb)
+ dev->vid_out_caps |= V4L2_CAP_VIDEO_OUTPUT_OVERLAY;
+ dev->vid_out_caps |= V4L2_CAP_STREAMING | V4L2_CAP_READWRITE;
+ if (dev->has_audio_outputs)
+ dev->vid_out_caps |= V4L2_CAP_AUDIO;
+ }
+ if (dev->has_vbi_cap) {
+ /* set up the capabilities of the vbi capture device */
+ dev->vbi_cap_caps = (dev->has_raw_vbi_cap ? V4L2_CAP_VBI_CAPTURE : 0) |
+ (dev->has_sliced_vbi_cap ? V4L2_CAP_SLICED_VBI_CAPTURE : 0);
+ dev->vbi_cap_caps |= V4L2_CAP_STREAMING | V4L2_CAP_READWRITE;
+ if (dev->has_audio_inputs)
+ dev->vbi_cap_caps |= V4L2_CAP_AUDIO;
+ if (in_type_counter[TV])
+ dev->vbi_cap_caps |= V4L2_CAP_TUNER;
+ }
+ if (dev->has_vbi_out) {
+ /* set up the capabilities of the vbi output device */
+ dev->vbi_out_caps = (dev->has_raw_vbi_out ? V4L2_CAP_VBI_OUTPUT : 0) |
+ (dev->has_sliced_vbi_out ? V4L2_CAP_SLICED_VBI_OUTPUT : 0);
+ dev->vbi_out_caps |= V4L2_CAP_STREAMING | V4L2_CAP_READWRITE;
+ if (dev->has_audio_outputs)
+ dev->vbi_out_caps |= V4L2_CAP_AUDIO;
+ }
+ if (dev->has_sdr_cap) {
+ /* set up the capabilities of the sdr capture device */
+ dev->sdr_cap_caps = V4L2_CAP_SDR_CAPTURE | V4L2_CAP_TUNER;
+ dev->sdr_cap_caps |= V4L2_CAP_STREAMING | V4L2_CAP_READWRITE;
+ }
+ /* set up the capabilities of the radio receiver device */
+ if (dev->has_radio_rx)
+ dev->radio_rx_caps = V4L2_CAP_RADIO | V4L2_CAP_RDS_CAPTURE |
+ V4L2_CAP_HW_FREQ_SEEK | V4L2_CAP_TUNER |
+ V4L2_CAP_READWRITE;
+ /* set up the capabilities of the radio transmitter device */
+ if (dev->has_radio_tx)
+ dev->radio_tx_caps = V4L2_CAP_RDS_OUTPUT | V4L2_CAP_MODULATOR |
+ V4L2_CAP_READWRITE;
+
+ /* 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(MAX_ZOOM * MAX_WIDTH);
+ if (!dev->scaled_line)
+ goto free_dev;
+ dev->blended_line = vzalloc(MAX_ZOOM * MAX_WIDTH);
+ if (!dev->blended_line)
+ goto free_dev;
+
+ /* load the edid */
+ dev->edid = vmalloc(256 * 128);
+ if (!dev->edid)
+ goto free_dev;
+
+ /* create a string array containing the names of all the preset timings */
+ while (v4l2_dv_timings_presets[dev->query_dv_timings_size].bt.width)
+ dev->query_dv_timings_size++;
+ dev->query_dv_timings_qmenu = kmalloc(dev->query_dv_timings_size *
+ (sizeof(void *) + 32), GFP_KERNEL);
+ if (dev->query_dv_timings_qmenu == NULL)
+ 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 = (char *)&dev->query_dv_timings_qmenu[dev->query_dv_timings_size];
+ u32 htot, vtot;
+
+ p += i * 32;
+ dev->query_dv_timings_qmenu[i] = p;
+
+ htot = V4L2_DV_BT_FRAME_WIDTH(bt);
+ vtot = V4L2_DV_BT_FRAME_HEIGHT(bt);
+ snprintf(p, 32, "%ux%u%s%u",
+ bt->width, bt->height, bt->interlaced ? "i" : "p",
+ (u32)bt->pixelclock / (htot * vtot));
+ }
+
+ /* disable invalid ioctls based on the feature set */
+ if (!dev->has_audio_inputs) {
+ v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_S_AUDIO);
+ v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_G_AUDIO);
+ v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_ENUMAUDIO);
+ v4l2_disable_ioctl(&dev->vbi_cap_dev, VIDIOC_S_AUDIO);
+ v4l2_disable_ioctl(&dev->vbi_cap_dev, VIDIOC_G_AUDIO);
+ v4l2_disable_ioctl(&dev->vbi_cap_dev, VIDIOC_ENUMAUDIO);
+ }
+ if (!dev->has_audio_outputs) {
+ v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_S_AUDOUT);
+ v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_G_AUDOUT);
+ v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_ENUMAUDOUT);
+ v4l2_disable_ioctl(&dev->vbi_out_dev, VIDIOC_S_AUDOUT);
+ v4l2_disable_ioctl(&dev->vbi_out_dev, VIDIOC_G_AUDOUT);
+ v4l2_disable_ioctl(&dev->vbi_out_dev, VIDIOC_ENUMAUDOUT);
+ }
+ if (!in_type_counter[TV] && !in_type_counter[SVID]) {
+ v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_S_STD);
+ v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_G_STD);
+ v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_ENUMSTD);
+ v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_QUERYSTD);
+ }
+ if (!out_type_counter[SVID]) {
+ v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_S_STD);
+ v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_G_STD);
+ v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_ENUMSTD);
+ }
+ if (!has_tuner && !has_modulator) {
+ v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_S_FREQUENCY);
+ v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_G_FREQUENCY);
+ v4l2_disable_ioctl(&dev->vbi_cap_dev, VIDIOC_S_FREQUENCY);
+ v4l2_disable_ioctl(&dev->vbi_cap_dev, VIDIOC_G_FREQUENCY);
+ }
+ if (!has_tuner) {
+ v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_S_TUNER);
+ v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_G_TUNER);
+ v4l2_disable_ioctl(&dev->vbi_cap_dev, VIDIOC_S_TUNER);
+ v4l2_disable_ioctl(&dev->vbi_cap_dev, VIDIOC_G_TUNER);
+ }
+ if (in_type_counter[HDMI] == 0) {
+ v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_S_EDID);
+ v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_G_EDID);
+ v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_DV_TIMINGS_CAP);
+ v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_G_DV_TIMINGS);
+ v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_S_DV_TIMINGS);
+ v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_ENUM_DV_TIMINGS);
+ v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_QUERY_DV_TIMINGS);
+ }
+ if (out_type_counter[HDMI] == 0) {
+ v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_G_EDID);
+ v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_DV_TIMINGS_CAP);
+ v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_G_DV_TIMINGS);
+ v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_S_DV_TIMINGS);
+ v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_ENUM_DV_TIMINGS);
+ }
+ if (!dev->has_fb) {
+ v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_G_FBUF);
+ v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_S_FBUF);
+ v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_OVERLAY);
+ }
+ v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_S_HW_FREQ_SEEK);
+ v4l2_disable_ioctl(&dev->vbi_cap_dev, VIDIOC_S_HW_FREQ_SEEK);
+ v4l2_disable_ioctl(&dev->sdr_cap_dev, VIDIOC_S_HW_FREQ_SEEK);
+ v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_S_FREQUENCY);
+ v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_G_FREQUENCY);
+ v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_ENUM_FRAMESIZES);
+ v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_ENUM_FRAMEINTERVALS);
+ v4l2_disable_ioctl(&dev->vbi_out_dev, VIDIOC_S_FREQUENCY);
+ v4l2_disable_ioctl(&dev->vbi_out_dev, VIDIOC_G_FREQUENCY);
+
+ /* configure internal data */
+ dev->fmt_cap = &vivid_formats[0];
+ dev->fmt_out = &vivid_formats[0];
+ if (!dev->multiplanar)
+ vivid_formats[0].data_offset[0] = 0;
+ dev->webcam_size_idx = 1;
+ dev->webcam_ival_idx = 3;
+ tpg_s_fourcc(&dev->tpg, dev->fmt_cap->fourcc);
+ dev->std_cap = V4L2_STD_PAL;
+ 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;
+ dev->dv_timings_cap = def_dv_timings;
+ 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->edid_max_blocks = dev->edid_blocks = 2;
+ memcpy(dev->edid, vivid_hdmi_edid, sizeof(vivid_hdmi_edid));
+ ktime_get_ts(&dev->radio_rds_init_ts);
+
+ /* 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;
+
+ /*
+ * update the capture and output formats to do a proper initial
+ * configuration.
+ */
+ vivid_update_format_cap(dev, false);
+ vivid_update_format_out(dev);
+
+ 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);
+
+ /* initialize overlay */
+ dev->fb_cap.fmt.width = dev->src_rect.width;
+ dev->fb_cap.fmt.height = dev->src_rect.height;
+ dev->fb_cap.fmt.pixelformat = dev->fmt_cap->fourcc;
+ dev->fb_cap.fmt.bytesperline = dev->src_rect.width * tpg_g_twopixelsize(&dev->tpg, 0) / 2;
+ dev->fb_cap.fmt.sizeimage = dev->src_rect.height * dev->fb_cap.fmt.bytesperline;
+
+ /* initialize locks */
+ spin_lock_init(&dev->slock);
+ mutex_init(&dev->mutex);
+
+ /* init dma queues */
+ INIT_LIST_HEAD(&dev->vid_cap_active);
+ INIT_LIST_HEAD(&dev->vid_out_active);
+ INIT_LIST_HEAD(&dev->vbi_cap_active);
+ INIT_LIST_HEAD(&dev->vbi_out_active);
+ INIT_LIST_HEAD(&dev->sdr_cap_active);
+
+ /* start creating the vb2 queues */
+ if (dev->has_vid_cap) {
+ /* initialize vid_cap queue */
+ q = &dev->vb_vid_cap_q;
+ q->type = dev->multiplanar ? V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE :
+ V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ;
+ q->drv_priv = dev;
+ q->buf_struct_size = sizeof(struct vivid_buffer);
+ q->ops = &vivid_vid_cap_qops;
+ q->mem_ops = &vb2_vmalloc_memops;
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ q->min_buffers_needed = 2;
+
+ ret = vb2_queue_init(q);
+ if (ret)
+ goto unreg_dev;
+ }
+
+ if (dev->has_vid_out) {
+ /* initialize vid_out queue */
+ q = &dev->vb_vid_out_q;
+ q->type = dev->multiplanar ? V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE :
+ V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_WRITE;
+ q->drv_priv = dev;
+ q->buf_struct_size = sizeof(struct vivid_buffer);
+ q->ops = &vivid_vid_out_qops;
+ q->mem_ops = &vb2_vmalloc_memops;
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ q->min_buffers_needed = 2;
+
+ ret = vb2_queue_init(q);
+ if (ret)
+ goto unreg_dev;
+ }
+
+ if (dev->has_vbi_cap) {
+ /* initialize vbi_cap queue */
+ q = &dev->vb_vbi_cap_q;
+ q->type = dev->has_raw_vbi_cap ? V4L2_BUF_TYPE_VBI_CAPTURE :
+ V4L2_BUF_TYPE_SLICED_VBI_CAPTURE;
+ q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ;
+ q->drv_priv = dev;
+ q->buf_struct_size = sizeof(struct vivid_buffer);
+ q->ops = &vivid_vbi_cap_qops;
+ q->mem_ops = &vb2_vmalloc_memops;
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ q->min_buffers_needed = 2;
+
+ ret = vb2_queue_init(q);
+ if (ret)
+ goto unreg_dev;
+ }
+
+ if (dev->has_vbi_out) {
+ /* initialize vbi_out queue */
+ q = &dev->vb_vbi_out_q;
+ q->type = dev->has_raw_vbi_out ? V4L2_BUF_TYPE_VBI_OUTPUT :
+ V4L2_BUF_TYPE_SLICED_VBI_OUTPUT;
+ q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_WRITE;
+ q->drv_priv = dev;
+ q->buf_struct_size = sizeof(struct vivid_buffer);
+ q->ops = &vivid_vbi_out_qops;
+ q->mem_ops = &vb2_vmalloc_memops;
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ q->min_buffers_needed = 2;
+
+ ret = vb2_queue_init(q);
+ if (ret)
+ goto unreg_dev;
+ }
+
+ if (dev->has_sdr_cap) {
+ /* initialize sdr_cap queue */
+ q = &dev->vb_sdr_cap_q;
+ q->type = V4L2_BUF_TYPE_SDR_CAPTURE;
+ q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ;
+ q->drv_priv = dev;
+ q->buf_struct_size = sizeof(struct vivid_buffer);
+ q->ops = &vivid_sdr_cap_qops;
+ q->mem_ops = &vb2_vmalloc_memops;
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ q->min_buffers_needed = 8;
+
+ ret = vb2_queue_init(q);
+ if (ret)
+ goto unreg_dev;
+ }
+
+ if (dev->has_fb) {
+ /* Create framebuffer for testing capture/output overlay */
+ ret = vivid_fb_init(dev);
+ if (ret)
+ goto unreg_dev;
+ v4l2_info(&dev->v4l2_dev, "Framebuffer device registered as fb%d\n",
+ dev->fb_info.node);
+ }
+
+ /* finally start creating the device nodes */
+ if (dev->has_vid_cap) {
+ vfd = &dev->vid_cap_dev;
+ strlcpy(vfd->name, "vivid-vid-cap", sizeof(vfd->name));
+ vfd->fops = &vivid_fops;
+ vfd->ioctl_ops = &vivid_ioctl_ops;
+ 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);
+
+ ret = video_register_device(vfd, VFL_TYPE_GRABBER, vid_cap_nr[inst]);
+ if (ret < 0)
+ goto unreg_dev;
+ v4l2_info(&dev->v4l2_dev, "V4L2 capture device registered as %s\n",
+ video_device_node_name(vfd));
+ }
+
+ if (dev->has_vid_out) {
+ vfd = &dev->vid_out_dev;
+ strlcpy(vfd->name, "vivid-vid-out", sizeof(vfd->name));
+ vfd->vfl_dir = VFL_DIR_TX;
+ vfd->fops = &vivid_fops;
+ vfd->ioctl_ops = &vivid_ioctl_ops;
+ 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);
+
+ ret = video_register_device(vfd, VFL_TYPE_GRABBER, vid_out_nr[inst]);
+ if (ret < 0)
+ goto unreg_dev;
+ v4l2_info(&dev->v4l2_dev, "V4L2 output device registered as %s\n",
+ video_device_node_name(vfd));
+ }
+
+ if (dev->has_vbi_cap) {
+ vfd = &dev->vbi_cap_dev;
+ strlcpy(vfd->name, "vivid-vbi-cap", sizeof(vfd->name));
+ vfd->fops = &vivid_fops;
+ vfd->ioctl_ops = &vivid_ioctl_ops;
+ 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);
+
+ 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;
+ strlcpy(vfd->name, "vivid-vbi-out", sizeof(vfd->name));
+ vfd->vfl_dir = VFL_DIR_TX;
+ vfd->fops = &vivid_fops;
+ vfd->ioctl_ops = &vivid_ioctl_ops;
+ 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);
+
+ 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;
+ strlcpy(vfd->name, "vivid-sdr-cap", sizeof(vfd->name));
+ vfd->fops = &vivid_fops;
+ vfd->ioctl_ops = &vivid_ioctl_ops;
+ 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);
+
+ 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;
+ strlcpy(vfd->name, "vivid-rad-rx", sizeof(vfd->name));
+ vfd->fops = &vivid_radio_fops;
+ vfd->ioctl_ops = &vivid_ioctl_ops;
+ 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;
+ strlcpy(vfd->name, "vivid-rad-tx", sizeof(vfd->name));
+ vfd->vfl_dir = VFL_DIR_TX;
+ vfd->fops = &vivid_radio_fops;
+ vfd->ioctl_ops = &vivid_ioctl_ops;
+ 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));
+ }
+
+ /* Now that everything is fine, let's add it to device list */
+ vivid_devs[inst] = dev;
+
+ return 0;
+
+unreg_dev:
+ video_unregister_device(&dev->radio_tx_dev);
+ video_unregister_device(&dev->radio_rx_dev);
+ video_unregister_device(&dev->sdr_cap_dev);
+ video_unregister_device(&dev->vbi_out_dev);
+ video_unregister_device(&dev->vbi_cap_dev);
+ video_unregister_device(&dev->vid_out_dev);
+ video_unregister_device(&dev->vid_cap_dev);
+ vivid_free_controls(dev);
+ v4l2_device_unregister(&dev->v4l2_dev);
+free_dev:
+ vfree(dev->scaled_line);
+ vfree(dev->blended_line);
+ vfree(dev->edid);
+ tpg_free(&dev->tpg);
+ kfree(dev->query_dv_timings_qmenu);
+ kfree(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 __init vivid_init(void)
+{
+ 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(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 void __exit vivid_exit(void)
+{
+ struct vivid_dev *dev;
+ unsigned i;
+
+ for (i = 0; vivid_devs[i]; i++) {
+ dev = vivid_devs[i];
+
+ 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);
+ }
+ v4l2_device_unregister(&dev->v4l2_dev);
+ vivid_free_controls(dev);
+ 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);
+ vivid_devs[i] = NULL;
+ }
+}
+
+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
new file mode 100644
index 000000000000..811c286491a5
--- /dev/null
+++ b/drivers/media/platform/vivid/vivid-core.h
@@ -0,0 +1,520 @@
+/*
+ * vivid-core.h - core datastructures
+ *
+ * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef _VIVID_CORE_H_
+#define _VIVID_CORE_H_
+
+#include <linux/fb.h>
+#include <media/videobuf2-core.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-ctrls.h>
+#include "vivid-tpg.h"
+#include "vivid-rds-gen.h"
+#include "vivid-vbi-gen.h"
+
+#define dprintk(dev, level, fmt, arg...) \
+ v4l2_dbg(level, vivid_debug, &dev->v4l2_dev, fmt, ## arg)
+
+/* Maximum allowed frame rate
+ *
+ * vivid will allow setting timeperframe in [1/FPS_MAX - FPS_MAX/1] range.
+ *
+ * Ideally FPS_MAX should be infinity, i.e. practically UINT_MAX, but that
+ * might hit application errors when they manipulate these values.
+ *
+ * Besides, for tpf < 10ms image-generation logic should be changed, to avoid
+ * producing frames with equal content.
+ */
+#define FPS_MAX 100
+
+/* 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 {
+ const char *name;
+ u32 fourcc; /* v4l2 format id */
+ u8 depth;
+ bool is_yuv;
+ bool can_do_overlay;
+ u32 alpha_mask;
+ u8 planes;
+ u32 data_offset[2];
+};
+
+extern struct vivid_fmt vivid_formats[];
+
+/* buffer for one video frame */
+struct vivid_buffer {
+ /* common v4l buffer stuff -- must be first */
+ struct vb2_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,
+};
+
+#define VIVID_INVALID_SIGNAL(mode) \
+ ((mode) == NO_SIGNAL || (mode) == NO_LOCK || (mode) == OUT_OF_RANGE)
+
+struct vivid_dev {
+ unsigned inst;
+ struct v4l2_device v4l2_dev;
+ 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_out;
+ struct video_device vid_cap_dev;
+ struct v4l2_ctrl_handler ctrl_hdl_vid_cap;
+ struct video_device vid_out_dev;
+ struct v4l2_ctrl_handler ctrl_hdl_vid_out;
+ struct video_device vbi_cap_dev;
+ struct v4l2_ctrl_handler ctrl_hdl_vbi_cap;
+ struct video_device vbi_out_dev;
+ struct v4l2_ctrl_handler ctrl_hdl_vbi_out;
+ struct video_device radio_rx_dev;
+ struct v4l2_ctrl_handler ctrl_hdl_radio_rx;
+ struct video_device radio_tx_dev;
+ struct v4l2_ctrl_handler ctrl_hdl_radio_tx;
+ struct video_device sdr_cap_dev;
+ struct v4l2_ctrl_handler ctrl_hdl_sdr_cap;
+ spinlock_t slock;
+ struct mutex mutex;
+
+ /* capabilities */
+ u32 vid_cap_caps;
+ u32 vid_out_caps;
+ u32 vbi_cap_caps;
+ u32 vbi_out_caps;
+ u32 sdr_cap_caps;
+ u32 radio_rx_caps;
+ u32 radio_tx_caps;
+
+ /* supported features */
+ bool multiplanar;
+ unsigned num_inputs;
+ u8 input_type[MAX_INPUTS];
+ u8 input_name_counter[MAX_INPUTS];
+ unsigned num_outputs;
+ u8 output_type[MAX_OUTPUTS];
+ u8 output_name_counter[MAX_OUTPUTS];
+ bool has_audio_inputs;
+ bool has_audio_outputs;
+ bool has_vid_cap;
+ bool has_vid_out;
+ bool has_vbi_cap;
+ bool has_raw_vbi_cap;
+ bool has_sliced_vbi_cap;
+ bool has_vbi_out;
+ bool has_raw_vbi_out;
+ bool has_sliced_vbi_out;
+ bool has_radio_rx;
+ bool has_radio_tx;
+ bool has_sdr_cap;
+ bool has_fb;
+
+ bool can_loop_video;
+
+ /* controls */
+ struct v4l2_ctrl *brightness;
+ struct v4l2_ctrl *contrast;
+ struct v4l2_ctrl *saturation;
+ struct v4l2_ctrl *hue;
+ struct {
+ /* autogain/gain cluster */
+ struct v4l2_ctrl *autogain;
+ struct v4l2_ctrl *gain;
+ };
+ struct v4l2_ctrl *volume;
+ struct v4l2_ctrl *mute;
+ struct v4l2_ctrl *alpha;
+ struct v4l2_ctrl *button;
+ struct v4l2_ctrl *boolean;
+ struct v4l2_ctrl *int32;
+ struct v4l2_ctrl *int64;
+ struct v4l2_ctrl *menu;
+ struct v4l2_ctrl *string;
+ struct v4l2_ctrl *bitmask;
+ struct v4l2_ctrl *int_menu;
+ struct v4l2_ctrl *test_pattern;
+ struct v4l2_ctrl *colorspace;
+ struct v4l2_ctrl *rgb_range_cap;
+ struct v4l2_ctrl *real_rgb_range_cap;
+ struct {
+ /* std_signal_mode/standard cluster */
+ struct v4l2_ctrl *ctrl_std_signal_mode;
+ struct v4l2_ctrl *ctrl_standard;
+ };
+ struct {
+ /* dv_timings_signal_mode/timings cluster */
+ struct v4l2_ctrl *ctrl_dv_timings_signal_mode;
+ struct v4l2_ctrl *ctrl_dv_timings;
+ };
+ struct v4l2_ctrl *ctrl_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 *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;
+
+ /* 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 seq_wrap;
+ bool time_wrap;
+ __kernel_time_t time_wrap_offset;
+ unsigned perc_dropped_buffers;
+ enum vivid_signal_mode std_signal_mode;
+ unsigned query_std_last;
+ v4l2_std_id query_std;
+ enum tpg_video_aspect std_aspect_ratio;
+
+ enum vivid_signal_mode dv_timings_signal_mode;
+ char **query_dv_timings_qmenu;
+ unsigned query_dv_timings_size;
+ unsigned query_dv_timings_last;
+ unsigned query_dv_timings;
+ enum tpg_video_aspect dv_timings_aspect_ratio;
+
+ /* Input */
+ unsigned input;
+ v4l2_std_id std_cap;
+ struct v4l2_dv_timings dv_timings_cap;
+ 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;
+
+ /* 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 service_set_out;
+ u32 bytesperline_out[2];
+ 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;
+
+ /* Output Overlay */
+ void *fb_vbase_out;
+ bool overlay_out_enabled;
+ int overlay_out_top, overlay_out_left;
+ void *bitmap_out;
+ struct v4l2_clip clips_out[MAX_CLIPS];
+ struct v4l2_clip try_clips_out[MAX_CLIPS];
+ unsigned clipcount_out;
+ unsigned fbuf_out_flags;
+ u32 chromakey_out;
+ u8 global_alpha_out;
+
+ /* video capture */
+ struct tpg_data tpg;
+ unsigned ms_vid_cap;
+ bool must_blank[VIDEO_MAX_FRAME];
+
+ const struct vivid_fmt *fmt_cap;
+ struct v4l2_fract timeperframe_vid_cap;
+ enum v4l2_field field_cap;
+ struct v4l2_rect src_rect;
+ struct v4l2_rect fmt_cap_rect;
+ struct v4l2_rect crop_cap;
+ struct v4l2_rect compose_cap;
+ struct v4l2_rect crop_bounds_cap;
+ struct vb2_queue vb_vid_cap_q;
+ struct list_head vid_cap_active;
+ struct vb2_queue vb_vbi_cap_q;
+ struct list_head vbi_cap_active;
+
+ /* thread for generating video capture stream */
+ struct task_struct *kthread_vid_cap;
+ unsigned long jiffies_vid_cap;
+ u32 cap_seq_offset;
+ u32 cap_seq_count;
+ bool cap_seq_resync;
+ u32 vid_cap_seq_start;
+ u32 vid_cap_seq_count;
+ bool vid_cap_streaming;
+ u32 vbi_cap_seq_start;
+ u32 vbi_cap_seq_count;
+ bool vbi_cap_streaming;
+ bool stream_sliced_vbi_cap;
+
+ /* video output */
+ const struct vivid_fmt *fmt_out;
+ struct v4l2_fract timeperframe_vid_out;
+ enum v4l2_field field_out;
+ struct v4l2_rect sink_rect;
+ struct v4l2_rect fmt_out_rect;
+ struct v4l2_rect crop_out;
+ struct v4l2_rect compose_out;
+ struct v4l2_rect compose_bounds_out;
+ struct vb2_queue vb_vid_out_q;
+ struct list_head vid_out_active;
+ struct vb2_queue vb_vbi_out_q;
+ struct list_head vbi_out_active;
+
+ /* video loop precalculated rectangles */
+
+ /*
+ * Intersection between what the output side composes and the capture side
+ * crops. I.e., what actually needs to be copied from the output buffer to
+ * the capture buffer.
+ */
+ struct v4l2_rect loop_vid_copy;
+ /* The part of the output buffer that (after scaling) corresponds to loop_vid_copy. */
+ struct v4l2_rect loop_vid_out;
+ /* The part of the capture buffer that (after scaling) corresponds to loop_vid_copy. */
+ struct v4l2_rect loop_vid_cap;
+ /*
+ * The intersection of the framebuffer, the overlay output window and
+ * loop_vid_copy. I.e., the part of the framebuffer that actually should be
+ * blended with the compose_out rectangle. This uses the framebuffer origin.
+ */
+ struct v4l2_rect loop_fb_copy;
+ /* The same as loop_fb_copy but with compose_out origin. */
+ struct v4l2_rect loop_vid_overlay;
+ /*
+ * The part of the capture buffer that (after scaling) corresponds
+ * to loop_vid_overlay.
+ */
+ struct v4l2_rect loop_vid_overlay_cap;
+
+ /* thread for generating video output stream */
+ struct task_struct *kthread_vid_out;
+ unsigned long jiffies_vid_out;
+ u32 out_seq_offset;
+ u32 out_seq_count;
+ bool out_seq_resync;
+ u32 vid_out_seq_start;
+ u32 vid_out_seq_count;
+ bool vid_out_streaming;
+ u32 vbi_out_seq_start;
+ u32 vbi_out_seq_count;
+ bool vbi_out_streaming;
+ bool stream_sliced_vbi_out;
+
+ /* SDR capture */
+ struct vb2_queue vb_sdr_cap_q;
+ struct list_head sdr_cap_active;
+ unsigned sdr_adc_freq;
+ unsigned sdr_fm_freq;
+ 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;
+ struct timespec radio_rds_init_ts;
+};
+
+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;
+}
+
+void vivid_lock(struct vb2_queue *vq);
+void vivid_unlock(struct vb2_queue *vq);
+
+#endif
diff --git a/drivers/media/platform/vivid/vivid-ctrls.c b/drivers/media/platform/vivid/vivid-ctrls.c
new file mode 100644
index 000000000000..d5cbf0038f24
--- /dev/null
+++ b/drivers/media/platform/vivid/vivid-ctrls.c
@@ -0,0 +1,1502 @@
+/*
+ * vivid-ctrls.c - control support functions.
+ *
+ * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-common.h>
+
+#include "vivid-core.h"
+#include "vivid-vid-cap.h"
+#include "vivid-vid-out.h"
+#include "vivid-vid-common.h"
+#include "vivid-radio-common.h"
+#include "vivid-osd.h"
+#include "vivid-ctrls.h"
+
+#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_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_LIMITED_RGB_RANGE (VIVID_CID_VIVID_BASE + 26)
+#define VIVID_CID_ALPHA_MODE (VIVID_CID_VIVID_BASE + 27)
+#define VIVID_CID_HAS_CROP_CAP (VIVID_CID_VIVID_BASE + 28)
+#define VIVID_CID_HAS_COMPOSE_CAP (VIVID_CID_VIVID_BASE + 29)
+#define VIVID_CID_HAS_SCALER_CAP (VIVID_CID_VIVID_BASE + 30)
+#define VIVID_CID_HAS_CROP_OUT (VIVID_CID_VIVID_BASE + 31)
+#define VIVID_CID_HAS_COMPOSE_OUT (VIVID_CID_VIVID_BASE + 32)
+#define VIVID_CID_HAS_SCALER_OUT (VIVID_CID_VIVID_BASE + 33)
+#define VIVID_CID_LOOP_VIDEO (VIVID_CID_VIVID_BASE + 34)
+#define VIVID_CID_SEQ_WRAP (VIVID_CID_VIVID_BASE + 35)
+#define VIVID_CID_TIME_WRAP (VIVID_CID_VIVID_BASE + 36)
+#define VIVID_CID_MAX_EDID_BLOCKS (VIVID_CID_VIVID_BASE + 37)
+#define VIVID_CID_PERCENTAGE_FILL (VIVID_CID_VIVID_BASE + 38)
+
+#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_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)
+
+
+/* General User Controls */
+
+static int vivid_user_gen_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct vivid_dev *dev = container_of(ctrl->handler, struct vivid_dev, ctrl_hdl_user_gen);
+
+ switch (ctrl->id) {
+ case VIVID_CID_DISCONNECT:
+ v4l2_info(&dev->v4l2_dev, "disconnect\n");
+ clear_bit(V4L2_FL_REGISTERED, &dev->vid_cap_dev.flags);
+ clear_bit(V4L2_FL_REGISTERED, &dev->vid_out_dev.flags);
+ clear_bit(V4L2_FL_REGISTERED, &dev->vbi_cap_dev.flags);
+ clear_bit(V4L2_FL_REGISTERED, &dev->vbi_out_dev.flags);
+ clear_bit(V4L2_FL_REGISTERED, &dev->sdr_cap_dev.flags);
+ clear_bit(V4L2_FL_REGISTERED, &dev->radio_rx_dev.flags);
+ clear_bit(V4L2_FL_REGISTERED, &dev->radio_tx_dev.flags);
+ break;
+ case VIVID_CID_CLEAR_FB:
+ vivid_clear_fb(dev);
+ 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 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_ctrl_config vivid_ctrl_clear_fb = {
+ .ops = &vivid_user_gen_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 = dev->jiffies_vid_cap & 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)
+{
+ struct vivid_dev *dev = container_of(ctrl->handler, struct vivid_dev, ctrl_hdl_vid_cap);
+ unsigned i;
+
+ 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, 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_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 = 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->ctrl_dv_timings_signal_mode->val;
+ if (dev->dv_timings_signal_mode == SELECTED_DV_TIMINGS)
+ dev->query_dv_timings = dev->ctrl_dv_timings->val;
+ v4l2_ctrl_activate(dev->ctrl_dv_timings,
+ dev->dv_timings_signal_mode == 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 = 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 = 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_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 = 1,
+ .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",
+ "SMPTE 240M",
+ "REC 709",
+ "", /* Skip Bt878 entry */
+ "470 System M",
+ "470 System BG",
+ "", /* Skip JPEG entry */
+ "sRGB",
+ 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,
+ .min = 1,
+ .max = 8,
+ .menu_skip_mask = (1 << 4) | (1 << 7),
+ .def = 8,
+ .qmenu = vivid_ctrl_colorspace_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,
+};
+
+
+/* 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;
+
+ 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->standards & V4L2_DV_BT_STD_CEA861)) {
+ 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;
+ }
+ if (dev->loop_video)
+ vivid_send_source_change(dev, HDMI);
+ 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,
+};
+
+
+/* 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);
+ struct timeval tv;
+
+ 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_QUEUE_ERROR:
+ if (dev->vb_vid_cap_q.start_streaming_called)
+ vb2_queue_error(&dev->vb_vid_cap_q);
+ if (dev->vb_vbi_cap_q.start_streaming_called)
+ vb2_queue_error(&dev->vb_vbi_cap_q);
+ if (dev->vb_vid_out_q.start_streaming_called)
+ vb2_queue_error(&dev->vb_vid_out_q);
+ if (dev->vb_vbi_out_q.start_streaming_called)
+ vb2_queue_error(&dev->vb_vbi_out_q);
+ if (dev->vb_sdr_cap_q.start_streaming_called)
+ 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;
+ }
+ v4l2_get_timestamp(&tv);
+ dev->time_wrap_offset = -tv.tv_sec - 16;
+ 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,
+};
+
+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->ctrl_std_signal_mode->val;
+ if (dev->std_signal_mode == SELECTED_STD)
+ dev->query_std = vivid_standard[dev->ctrl_standard->val];
+ v4l2_ctrl_activate(dev->ctrl_standard, dev->std_signal_mode == 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 = 5,
+ .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);
+ 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;
+ 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,
+};
+
+
+
+/* Video Loop Control */
+
+static int vivid_loop_out_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct vivid_dev *dev = container_of(ctrl->handler, struct vivid_dev, ctrl_hdl_loop_out);
+
+ 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_out_ctrl_ops = {
+ .s_ctrl = vivid_loop_out_s_ctrl,
+};
+
+static const struct v4l2_ctrl_config vivid_ctrl_loop_video = {
+ .ops = &vivid_loop_out_ctrl_ops,
+ .id = VIVID_CID_LOOP_VIDEO,
+ .name = "Loop Video",
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .max = 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_out = &dev->ctrl_hdl_loop_out;
+ struct v4l2_ctrl_handler *hdl_vid_cap = &dev->ctrl_hdl_vid_cap;
+ struct v4l2_ctrl_handler *hdl_vid_out = &dev->ctrl_hdl_vid_out;
+ struct v4l2_ctrl_handler *hdl_vbi_cap = &dev->ctrl_hdl_vbi_cap;
+ struct v4l2_ctrl_handler *hdl_vbi_out = &dev->ctrl_hdl_vbi_out;
+ struct v4l2_ctrl_handler *hdl_radio_rx = &dev->ctrl_hdl_radio_rx;
+ struct v4l2_ctrl_handler *hdl_radio_tx = &dev->ctrl_hdl_radio_tx;
+ struct v4l2_ctrl_handler *hdl_sdr_cap = &dev->ctrl_hdl_sdr_cap;
+ struct v4l2_ctrl_config vivid_ctrl_dv_timings = {
+ .ops = &vivid_vid_cap_ctrl_ops,
+ .id = VIVID_CID_DV_TIMINGS,
+ .name = "DV Timings",
+ .type = V4L2_CTRL_TYPE_MENU,
+ };
+ int i;
+
+ v4l2_ctrl_handler_init(hdl_user_gen, 10);
+ v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_class, NULL);
+ v4l2_ctrl_handler_init(hdl_user_vid, 9);
+ v4l2_ctrl_new_custom(hdl_user_vid, &vivid_ctrl_class, NULL);
+ v4l2_ctrl_handler_init(hdl_user_aud, 2);
+ v4l2_ctrl_new_custom(hdl_user_aud, &vivid_ctrl_class, NULL);
+ v4l2_ctrl_handler_init(hdl_streaming, 8);
+ v4l2_ctrl_new_custom(hdl_streaming, &vivid_ctrl_class, NULL);
+ v4l2_ctrl_handler_init(hdl_sdtv_cap, 2);
+ v4l2_ctrl_new_custom(hdl_sdtv_cap, &vivid_ctrl_class, NULL);
+ v4l2_ctrl_handler_init(hdl_loop_out, 1);
+ v4l2_ctrl_new_custom(hdl_loop_out, &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);
+ 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);
+ 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, 18);
+ v4l2_ctrl_new_custom(hdl_sdr_cap, &vivid_ctrl_class, NULL);
+
+ /* User Controls */
+ dev->volume = v4l2_ctrl_new_std(hdl_user_aud, NULL,
+ V4L2_CID_AUDIO_VOLUME, 0, 255, 1, 200);
+ dev->mute = v4l2_ctrl_new_std(hdl_user_aud, NULL,
+ V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0);
+ if (dev->has_vid_cap) {
+ dev->brightness = v4l2_ctrl_new_std(hdl_user_vid, &vivid_user_vid_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
+ for (i = 0; i < MAX_INPUTS; i++)
+ dev->input_brightness[i] = 128;
+ dev->contrast = v4l2_ctrl_new_std(hdl_user_vid, &vivid_user_vid_ctrl_ops,
+ V4L2_CID_CONTRAST, 0, 255, 1, 128);
+ dev->saturation = v4l2_ctrl_new_std(hdl_user_vid, &vivid_user_vid_ctrl_ops,
+ V4L2_CID_SATURATION, 0, 255, 1, 128);
+ dev->hue = v4l2_ctrl_new_std(hdl_user_vid, &vivid_user_vid_ctrl_ops,
+ V4L2_CID_HUE, -128, 128, 1, 0);
+ v4l2_ctrl_new_std(hdl_user_vid, &vivid_user_vid_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(hdl_user_vid, &vivid_user_vid_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+ dev->autogain = v4l2_ctrl_new_std(hdl_user_vid, &vivid_user_vid_ctrl_ops,
+ V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
+ dev->gain = v4l2_ctrl_new_std(hdl_user_vid, &vivid_user_vid_ctrl_ops,
+ V4L2_CID_GAIN, 0, 255, 1, 100);
+ dev->alpha = v4l2_ctrl_new_std(hdl_user_vid, &vivid_user_vid_ctrl_ops,
+ V4L2_CID_ALPHA_COMPONENT, 0, 255, 1, 0);
+ }
+ dev->button = v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_button, NULL);
+ dev->int32 = v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_int32, NULL);
+ dev->int64 = v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_int64, NULL);
+ dev->boolean = v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_boolean, NULL);
+ dev->menu = v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_menu, NULL);
+ dev->string = v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_string, NULL);
+ dev->bitmask = v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_bitmask, NULL);
+ dev->int_menu = v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_int_menu, NULL);
+
+ 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);
+ 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_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);
+ 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 (has_hdmi && dev->has_vid_cap) {
+ 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);
+ }
+ if (has_hdmi && dev->has_vid_out) {
+ /*
+ * 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);
+ }
+ if ((dev->has_vid_cap && dev->has_vid_out) ||
+ (dev->has_vbi_cap && dev->has_vbi_out))
+ v4l2_ctrl_new_custom(hdl_loop_out, &vivid_ctrl_loop_video, NULL);
+
+ if (dev->has_fb)
+ v4l2_ctrl_new_custom(hdl_user_gen, &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 (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_out->error)
+ return hdl_loop_out->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);
+ v4l2_ctrl_add_handler(hdl_vid_cap, hdl_user_vid, NULL);
+ v4l2_ctrl_add_handler(hdl_vid_cap, hdl_user_aud, NULL);
+ v4l2_ctrl_add_handler(hdl_vid_cap, hdl_streaming, NULL);
+ v4l2_ctrl_add_handler(hdl_vid_cap, hdl_sdtv_cap, NULL);
+ 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);
+ v4l2_ctrl_add_handler(hdl_vid_out, hdl_user_aud, NULL);
+ v4l2_ctrl_add_handler(hdl_vid_out, hdl_streaming, NULL);
+ v4l2_ctrl_add_handler(hdl_vid_out, hdl_loop_out, NULL);
+ 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);
+ v4l2_ctrl_add_handler(hdl_vbi_cap, hdl_streaming, NULL);
+ v4l2_ctrl_add_handler(hdl_vbi_cap, hdl_sdtv_cap, NULL);
+ 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);
+ v4l2_ctrl_add_handler(hdl_vbi_out, hdl_streaming, NULL);
+ v4l2_ctrl_add_handler(hdl_vbi_out, hdl_loop_out, NULL);
+ 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);
+ v4l2_ctrl_add_handler(hdl_radio_rx, hdl_user_aud, NULL);
+ 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);
+ v4l2_ctrl_add_handler(hdl_radio_tx, hdl_user_aud, NULL);
+ 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);
+ v4l2_ctrl_add_handler(hdl_sdr_cap, hdl_streaming, NULL);
+ if (hdl_sdr_cap->error)
+ return hdl_sdr_cap->error;
+ dev->sdr_cap_dev.ctrl_handler = hdl_sdr_cap;
+ }
+ return 0;
+}
+
+void vivid_free_controls(struct vivid_dev *dev)
+{
+ v4l2_ctrl_handler_free(&dev->ctrl_hdl_vid_cap);
+ v4l2_ctrl_handler_free(&dev->ctrl_hdl_vid_out);
+ v4l2_ctrl_handler_free(&dev->ctrl_hdl_vbi_cap);
+ v4l2_ctrl_handler_free(&dev->ctrl_hdl_vbi_out);
+ v4l2_ctrl_handler_free(&dev->ctrl_hdl_radio_rx);
+ v4l2_ctrl_handler_free(&dev->ctrl_hdl_radio_tx);
+ v4l2_ctrl_handler_free(&dev->ctrl_hdl_sdr_cap);
+ v4l2_ctrl_handler_free(&dev->ctrl_hdl_user_gen);
+ v4l2_ctrl_handler_free(&dev->ctrl_hdl_user_vid);
+ v4l2_ctrl_handler_free(&dev->ctrl_hdl_user_aud);
+ v4l2_ctrl_handler_free(&dev->ctrl_hdl_streaming);
+ v4l2_ctrl_handler_free(&dev->ctrl_hdl_sdtv_cap);
+ v4l2_ctrl_handler_free(&dev->ctrl_hdl_loop_out);
+}
diff --git a/drivers/media/platform/vivid/vivid-ctrls.h b/drivers/media/platform/vivid/vivid-ctrls.h
new file mode 100644
index 000000000000..9bcca9d56359
--- /dev/null
+++ b/drivers/media/platform/vivid/vivid-ctrls.h
@@ -0,0 +1,34 @@
+/*
+ * vivid-ctrls.h - control support functions.
+ *
+ * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef _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
new file mode 100644
index 000000000000..39a67cfae120
--- /dev/null
+++ b/drivers/media/platform/vivid/vivid-kthread-cap.c
@@ -0,0 +1,886 @@
+/*
+ * vivid-kthread-cap.h - video/vbi capture thread support functions.
+ *
+ * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/font.h>
+#include <linux/mutex.h>
+#include <linux/videodev2.h>
+#include <linux/kthread.h>
+#include <linux/freezer.h>
+#include <linux/random.h>
+#include <linux/v4l2-dv-timings.h>
+#include <asm/div64.h>
+#include <media/videobuf2-vmalloc.h>
+#include <media/v4l2-dv-timings.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-event.h>
+
+#include "vivid-core.h"
+#include "vivid-vid-common.h"
+#include "vivid-vid-cap.h"
+#include "vivid-vid-out.h"
+#include "vivid-radio-common.h"
+#include "vivid-radio-rx.h"
+#include "vivid-radio-tx.h"
+#include "vivid-sdr-cap.h"
+#include "vivid-vbi-cap.h"
+#include "vivid-vbi-out.h"
+#include "vivid-osd.h"
+#include "vivid-ctrls.h"
+#include "vivid-kthread-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;
+ 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
+ };
+
+ dev->loop_vid_copy = rect_intersect(&dev->crop_cap, &dev->compose_out);
+
+ dev->loop_vid_out = dev->loop_vid_copy;
+ 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;
+ 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);
+
+ r_overlay = rect_intersect(&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;
+
+ dev->loop_vid_overlay = rect_intersect(&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;
+ 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 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.v4l2_buf.index];
+ struct tpg_data *tpg = &dev->tpg;
+ struct vivid_buffer *vid_out_buf = NULL;
+ unsigned pixsize = tpg_g_twopixelsize(tpg, p) / 2;
+ unsigned img_width = 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_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.v4l2_buf.field = vid_out_buf->vb.v4l2_buf.field;
+
+ voutbuf = vb2_plane_vaddr(&vid_out_buf->vb, p) +
+ vid_out_buf->vb.v4l2_planes[p].data_offset;
+ voutbuf += dev->loop_vid_out.left * pixsize + dev->loop_vid_out.top * stride_out;
+ vcapbuf += dev->compose_cap.left * pixsize + dev->compose_cap.top * 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; y++, vcapbuf += stride_cap)
+ memcpy(vcapbuf, tpg->black_line[p], img_width * pixsize);
+ 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 * pixsize +
+ 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 = 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++, 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 * pixsize);
+ continue;
+ }
+
+ /* fill the left border with black */
+ if (dev->loop_vid_cap.left)
+ memcpy(vcapbuf, tpg->black_line[p], dev->loop_vid_cap.left * pixsize);
+
+ /* fill the right border with black */
+ if (vid_cap_right < img_width)
+ memcpy(vcapbuf + vid_cap_right * pixsize,
+ tpg->black_line[p], (img_width - vid_cap_right) * pixsize);
+
+ if (quick && !osdline) {
+ memcpy(vcapbuf + dev->loop_vid_cap.left * pixsize,
+ voutbuf + vid_out_y * stride_out,
+ dev->loop_vid_cap.width * pixsize);
+ goto update_vid_out_y;
+ }
+ if (dev->cur_scaled_line == vid_out_y) {
+ memcpy(vcapbuf + dev->loop_vid_cap.left * pixsize,
+ dev->scaled_line,
+ dev->loop_vid_cap.width * pixsize);
+ goto update_vid_out_y;
+ }
+ if (!osdline) {
+ scale_line(voutbuf + vid_out_y * stride_out, dev->scaled_line,
+ dev->loop_vid_out.width, 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) * pixsize;
+ 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, pixsize);
+ else
+ memcpy(dev->blended_line + offset,
+ osd, dev->loop_vid_overlay.width * pixsize);
+ 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 + dev->loop_vid_cap.left * pixsize,
+ dev->scaled_line,
+ dev->loop_vid_cap.width * pixsize);
+
+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) {
+ vid_out_error -= dev->loop_vid_cap.height;
+ vid_out_y++;
+ }
+ }
+
+ if (!blank)
+ return 0;
+ for (; y < img_height; y++, vcapbuf += stride_cap)
+ memcpy(vcapbuf, tpg->contrast_line[p], img_width * pixsize);
+ return 0;
+}
+
+static void vivid_fillbuff(struct vivid_dev *dev, struct vivid_buffer *buf)
+{
+ 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 & 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)) ||
+ (vivid_is_hdmi_cap(dev) && !VIVID_INVALID_SIGNAL(dev->dv_timings_signal_mode))))
+ is_loop = true;
+
+ buf->vb.v4l2_buf.sequence = dev->vid_cap_seq_count;
+ /*
+ * Take the timestamp now if the timestamp source is set to
+ * "Start of Exposure".
+ */
+ if (dev->tstamp_src_is_soe)
+ v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp);
+ 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.v4l2_buf.field = ((dev->vid_cap_seq_count & 1) ^ is_60hz) ?
+ V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM;
+ /*
+ * The sequence counter counts frames, not fields. So divide
+ * by two.
+ */
+ buf->vb.v4l2_buf.sequence /= 2;
+ } else {
+ buf->vb.v4l2_buf.field = dev->field_cap;
+ }
+ tpg_s_field(&dev->tpg, buf->vb.v4l2_buf.field);
+ tpg_s_perc_fill_blank(&dev->tpg, dev->must_blank[buf->vb.v4l2_buf.index]);
+
+ vivid_precalc_copy_rects(dev);
+
+ for (p = 0; p < tpg_g_planes(&dev->tpg); p++) {
+ void *vbuf = vb2_plane_vaddr(&buf->vb, p);
+
+ /*
+ * 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 (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(&dev->tpg, basep, p, vbuf);
+ if (!is_loop || vivid_copy_buffer(dev, p, vbuf, buf))
+ tpg_fillbuffer(&dev->tpg, vivid_get_std_cap(dev), p, vbuf);
+ }
+ dev->must_blank[buf->vb.v4l2_buf.index] = false;
+
+ /* Updates stream time, only update at the start of a new frame. */
+ if (dev->field_cap != V4L2_FIELD_ALTERNATE || (buf->vb.v4l2_buf.sequence & 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.v4l2_buf.sequence,
+ (dev->field_cap == V4L2_FIELD_ALTERNATE) ?
+ (buf->vb.v4l2_buf.field == V4L2_FIELD_TOP ?
+ " top" : " bottom") : "");
+ tpg_gen_text(&dev->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(&dev->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(&dev->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(&dev->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(&dev->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(&dev->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(&dev->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(&dev->tpg, basep, line++ * line_height, 16, str);
+ if (dev->button_pressed) {
+ dev->button_pressed--;
+ snprintf(str, sizeof(str), " button pressed!");
+ tpg_gen_text(&dev->tpg, basep, line++ * line_height, 16, str);
+ }
+ }
+
+ /*
+ * If "End of Frame" is specified at the timestamp source, then take
+ * the timestamp now.
+ */
+ if (!dev->tstamp_src_is_soe)
+ v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp);
+ buf->vb.v4l2_buf.timestamp.tv_sec += dev->time_wrap_offset;
+}
+
+/*
+ * 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, 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;
+
+ if ((dev->overlay_cap_field == V4L2_FIELD_TOP ||
+ dev->overlay_cap_field == V4L2_FIELD_BOTTOM) &&
+ dev->overlay_cap_field != buf->vb.v4l2_buf.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_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;
+
+ dprintk(dev, 1, "Video Capture Thread Tick\n");
+
+ while (dropped_bufs-- > 1)
+ tpg_update_mv_count(&dev->tpg,
+ dev->field_cap == V4L2_FIELD_NONE ||
+ dev->field_cap == V4L2_FIELD_ALTERNATE);
+
+ /* Drop a certain percentage of buffers. */
+ if (dev->perc_dropped_buffers &&
+ prandom_u32_max(100) < dev->perc_dropped_buffers)
+ goto update_mv;
+
+ spin_lock(&dev->slock);
+ if (!list_empty(&dev->vid_cap_active)) {
+ vid_cap_buf = list_entry(dev->vid_cap_active.next, struct vivid_buffer, list);
+ list_del(&vid_cap_buf->list);
+ }
+ if (!list_empty(&dev->vbi_cap_active)) {
+ if (dev->field_cap != V4L2_FIELD_ALTERNATE ||
+ (dev->vbi_cap_seq_count & 1)) {
+ vbi_cap_buf = list_entry(dev->vbi_cap_active.next,
+ struct vivid_buffer, list);
+ list_del(&vbi_cap_buf->list);
+ }
+ }
+ spin_unlock(&dev->slock);
+
+ if (!vid_cap_buf && !vbi_cap_buf)
+ goto update_mv;
+
+ if (vid_cap_buf) {
+ /* Fill buffer */
+ vivid_fillbuff(dev, vid_cap_buf);
+ dprintk(dev, 1, "filled buffer %d\n",
+ vid_cap_buf->vb.v4l2_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);
+
+ vb2_buffer_done(&vid_cap_buf->vb, dev->dqbuf_error ?
+ VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE);
+ dprintk(dev, 2, "vid_cap buffer %d done\n",
+ vid_cap_buf->vb.v4l2_buf.index);
+ }
+
+ if (vbi_cap_buf) {
+ 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);
+ vb2_buffer_done(&vbi_cap_buf->vb, dev->dqbuf_error ?
+ VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE);
+ dprintk(dev, 2, "vbi_cap %d done\n",
+ vbi_cap_buf->vb.v4l2_buf.index);
+ }
+ 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;
+
+ for (;;) {
+ try_to_freeze();
+ if (kthread_should_stop())
+ break;
+
+ mutex_lock(&dev->mutex);
+ 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_seq_resync = false;
+ }
+ numerator = dev->timeperframe_vid_cap.numerator;
+ denominator = dev->timeperframe_vid_cap.denominator;
+
+ if (dev->field_cap == V4L2_FIELD_ALTERNATE)
+ denominator *= 2;
+
+ /* Calculate the number of jiffies since we started streaming */
+ jiffies_since_start = cur_jiffies - dev->jiffies_vid_cap;
+ /* Get the number of buffers streamed since the start */
+ buffers_since_start = (u64)jiffies_since_start * denominator +
+ (HZ * numerator) / 2;
+ do_div(buffers_since_start, HZ * numerator);
+
+ /*
+ * After more than 0xf0000000 (rounded down to a multiple of
+ * 'jiffies-per-day' to ease jiffies_to_msecs calculation)
+ * jiffies have passed since we started streaming reset the
+ * counters and keep track of the sequence offset.
+ */
+ if (jiffies_since_start > JIFFIES_RESYNC) {
+ dev->jiffies_vid_cap = cur_jiffies;
+ dev->cap_seq_offset = buffers_since_start;
+ buffers_since_start = 0;
+ }
+ dropped_bufs = buffers_since_start + dev->cap_seq_offset - dev->cap_seq_count;
+ dev->cap_seq_count = buffers_since_start + dev->cap_seq_offset;
+ dev->vid_cap_seq_count = dev->cap_seq_count - dev->vid_cap_seq_start;
+ dev->vbi_cap_seq_count = dev->cap_seq_count - dev->vbi_cap_seq_start;
+
+ vivid_thread_vid_cap_tick(dev, dropped_bufs);
+
+ /*
+ * Calculate the number of 'numerators' streamed since we started,
+ * including the current buffer.
+ */
+ numerators_since_start = ++buffers_since_start * numerator;
+
+ /* And the number of jiffies since we started */
+ jiffies_since_start = jiffies - dev->jiffies_vid_cap;
+
+ mutex_unlock(&dev->mutex);
+
+ /*
+ * Calculate when that next buffer is supposed to start
+ * in jiffies since we started streaming.
+ */
+ next_jiffies_since_start = numerators_since_start * HZ +
+ denominator / 2;
+ do_div(next_jiffies_since_start, denominator);
+ /* If it is in the past, then just schedule asap */
+ if (next_jiffies_since_start < jiffies_since_start)
+ next_jiffies_since_start = jiffies_since_start;
+
+ wait_jiffies = next_jiffies_since_start - jiffies_since_start;
+ schedule_timeout_interruptible(wait_jiffies ? wait_jiffies : 1);
+ }
+ dprintk(dev, 1, "Video Capture Thread End\n");
+ return 0;
+}
+
+static void vivid_grab_controls(struct vivid_dev *dev, bool grab)
+{
+ v4l2_ctrl_grab(dev->ctrl_has_crop_cap, grab);
+ v4l2_ctrl_grab(dev->ctrl_has_compose_cap, grab);
+ v4l2_ctrl_grab(dev->ctrl_has_scaler_cap, grab);
+}
+
+int vivid_start_generating_vid_cap(struct vivid_dev *dev, bool *pstreaming)
+{
+ dprintk(dev, 1, "%s\n", __func__);
+
+ if (dev->kthread_vid_cap) {
+ u32 seq_count = dev->cap_seq_count + dev->seq_wrap * 128;
+
+ if (pstreaming == &dev->vid_cap_streaming)
+ dev->vid_cap_seq_start = seq_count;
+ else
+ dev->vbi_cap_seq_start = seq_count;
+ *pstreaming = true;
+ return 0;
+ }
+
+ /* Resets frame counters */
+ tpg_init_mv_count(&dev->tpg);
+
+ dev->vid_cap_seq_start = dev->seq_wrap * 128;
+ dev->vbi_cap_seq_start = dev->seq_wrap * 128;
+
+ dev->kthread_vid_cap = kthread_run(vivid_thread_vid_cap, dev,
+ "%s-vid-cap", dev->v4l2_dev.name);
+
+ if (IS_ERR(dev->kthread_vid_cap)) {
+ v4l2_err(&dev->v4l2_dev, "kernel_thread() failed\n");
+ return PTR_ERR(dev->kthread_vid_cap);
+ }
+ *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);
+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+ dprintk(dev, 2, "vid_cap buffer %d done\n",
+ buf->vb.v4l2_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);
+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+ dprintk(dev, 2, "vbi_cap buffer %d done\n",
+ buf->vb.v4l2_buf.index);
+ }
+ }
+
+ if (dev->vid_cap_streaming || dev->vbi_cap_streaming)
+ return;
+
+ /* shutdown control thread */
+ vivid_grab_controls(dev, false);
+ mutex_unlock(&dev->mutex);
+ kthread_stop(dev->kthread_vid_cap);
+ dev->kthread_vid_cap = NULL;
+ mutex_lock(&dev->mutex);
+}
diff --git a/drivers/media/platform/vivid/vivid-kthread-cap.h b/drivers/media/platform/vivid/vivid-kthread-cap.h
new file mode 100644
index 000000000000..5b92fc9a0d04
--- /dev/null
+++ b/drivers/media/platform/vivid/vivid-kthread-cap.h
@@ -0,0 +1,26 @@
+/*
+ * vivid-kthread-cap.h - video/vbi capture thread support functions.
+ *
+ * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef _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
new file mode 100644
index 000000000000..d9f36ccd7efb
--- /dev/null
+++ b/drivers/media/platform/vivid/vivid-kthread-out.c
@@ -0,0 +1,305 @@
+/*
+ * vivid-kthread-out.h - video/vbi output thread support functions.
+ *
+ * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/font.h>
+#include <linux/mutex.h>
+#include <linux/videodev2.h>
+#include <linux/kthread.h>
+#include <linux/freezer.h>
+#include <linux/random.h>
+#include <linux/v4l2-dv-timings.h>
+#include <asm/div64.h>
+#include <media/videobuf2-vmalloc.h>
+#include <media/v4l2-dv-timings.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-event.h>
+
+#include "vivid-core.h"
+#include "vivid-vid-common.h"
+#include "vivid-vid-cap.h"
+#include "vivid-vid-out.h"
+#include "vivid-radio-common.h"
+#include "vivid-radio-rx.h"
+#include "vivid-radio-tx.h"
+#include "vivid-sdr-cap.h"
+#include "vivid-vbi-cap.h"
+#include "vivid-vbi-out.h"
+#include "vivid-osd.h"
+#include "vivid-ctrls.h"
+#include "vivid-kthread-out.h"
+
+static void vivid_thread_vid_out_tick(struct vivid_dev *dev)
+{
+ struct vivid_buffer *vid_out_buf = NULL;
+ struct vivid_buffer *vbi_out_buf = NULL;
+
+ dprintk(dev, 1, "Video Output Thread Tick\n");
+
+ /* Drop a certain percentage of buffers. */
+ if (dev->perc_dropped_buffers &&
+ prandom_u32_max(100) < dev->perc_dropped_buffers)
+ return;
+
+ spin_lock(&dev->slock);
+ /*
+ * Only dequeue buffer if there is at least one more pending.
+ * This makes video loopback possible.
+ */
+ if (!list_empty(&dev->vid_out_active) &&
+ !list_is_singular(&dev->vid_out_active)) {
+ vid_out_buf = list_entry(dev->vid_out_active.next,
+ struct vivid_buffer, list);
+ list_del(&vid_out_buf->list);
+ }
+ if (!list_empty(&dev->vbi_out_active) &&
+ (dev->field_out != V4L2_FIELD_ALTERNATE ||
+ (dev->vbi_out_seq_count & 1))) {
+ vbi_out_buf = list_entry(dev->vbi_out_active.next,
+ struct vivid_buffer, list);
+ list_del(&vbi_out_buf->list);
+ }
+ spin_unlock(&dev->slock);
+
+ if (!vid_out_buf && !vbi_out_buf)
+ return;
+
+ if (vid_out_buf) {
+ vid_out_buf->vb.v4l2_buf.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.v4l2_buf.sequence /= 2;
+ }
+ v4l2_get_timestamp(&vid_out_buf->vb.v4l2_buf.timestamp);
+ vid_out_buf->vb.v4l2_buf.timestamp.tv_sec += dev->time_wrap_offset;
+ vb2_buffer_done(&vid_out_buf->vb, dev->dqbuf_error ?
+ VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE);
+ dprintk(dev, 2, "vid_out buffer %d done\n",
+ vid_out_buf->vb.v4l2_buf.index);
+ }
+
+ if (vbi_out_buf) {
+ if (dev->stream_sliced_vbi_out)
+ vivid_sliced_vbi_out_process(dev, vbi_out_buf);
+
+ vbi_out_buf->vb.v4l2_buf.sequence = dev->vbi_out_seq_count;
+ v4l2_get_timestamp(&vbi_out_buf->vb.v4l2_buf.timestamp);
+ vbi_out_buf->vb.v4l2_buf.timestamp.tv_sec += dev->time_wrap_offset;
+ vb2_buffer_done(&vbi_out_buf->vb, dev->dqbuf_error ?
+ VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE);
+ dprintk(dev, 2, "vbi_out buffer %d done\n",
+ vbi_out_buf->vb.v4l2_buf.index);
+ }
+ dev->dqbuf_error = false;
+}
+
+static int vivid_thread_vid_out(void *data)
+{
+ struct vivid_dev *dev = data;
+ u64 numerators_since_start;
+ u64 buffers_since_start;
+ u64 next_jiffies_since_start;
+ unsigned long jiffies_since_start;
+ unsigned long cur_jiffies;
+ unsigned wait_jiffies;
+ unsigned numerator;
+ unsigned denominator;
+
+ dprintk(dev, 1, "Video Output Thread Start\n");
+
+ set_freezable();
+
+ /* Resets frame counters */
+ dev->out_seq_offset = 0;
+ if (dev->seq_wrap)
+ dev->out_seq_count = 0xffffff80U;
+ dev->jiffies_vid_out = jiffies;
+ dev->vid_out_seq_start = dev->vbi_out_seq_start = 0;
+ dev->out_seq_resync = false;
+
+ for (;;) {
+ try_to_freeze();
+ if (kthread_should_stop())
+ break;
+
+ mutex_lock(&dev->mutex);
+ cur_jiffies = jiffies;
+ if (dev->out_seq_resync) {
+ dev->jiffies_vid_out = cur_jiffies;
+ dev->out_seq_offset = dev->out_seq_count + 1;
+ dev->out_seq_count = 0;
+ dev->out_seq_resync = false;
+ }
+ numerator = dev->timeperframe_vid_out.numerator;
+ denominator = dev->timeperframe_vid_out.denominator;
+
+ if (dev->field_out == V4L2_FIELD_ALTERNATE)
+ denominator *= 2;
+
+ /* Calculate the number of jiffies since we started streaming */
+ jiffies_since_start = cur_jiffies - dev->jiffies_vid_out;
+ /* Get the number of buffers streamed since the start */
+ buffers_since_start = (u64)jiffies_since_start * denominator +
+ (HZ * numerator) / 2;
+ do_div(buffers_since_start, HZ * numerator);
+
+ /*
+ * After more than 0xf0000000 (rounded down to a multiple of
+ * 'jiffies-per-day' to ease jiffies_to_msecs calculation)
+ * jiffies have passed since we started streaming reset the
+ * counters and keep track of the sequence offset.
+ */
+ if (jiffies_since_start > JIFFIES_RESYNC) {
+ dev->jiffies_vid_out = cur_jiffies;
+ dev->out_seq_offset = buffers_since_start;
+ buffers_since_start = 0;
+ }
+ dev->out_seq_count = buffers_since_start + dev->out_seq_offset;
+ dev->vid_out_seq_count = dev->out_seq_count - dev->vid_out_seq_start;
+ dev->vbi_out_seq_count = dev->out_seq_count - dev->vbi_out_seq_start;
+
+ vivid_thread_vid_out_tick(dev);
+ mutex_unlock(&dev->mutex);
+
+ /*
+ * Calculate the number of 'numerators' streamed since we started,
+ * not including the current buffer.
+ */
+ numerators_since_start = buffers_since_start * numerator;
+
+ /* And the number of jiffies since we started */
+ jiffies_since_start = jiffies - dev->jiffies_vid_out;
+
+ /* Increase by the 'numerator' of one buffer */
+ numerators_since_start += numerator;
+ /*
+ * Calculate when that next buffer is supposed to start
+ * in jiffies since we started streaming.
+ */
+ next_jiffies_since_start = numerators_since_start * HZ +
+ denominator / 2;
+ do_div(next_jiffies_since_start, denominator);
+ /* If it is in the past, then just schedule asap */
+ if (next_jiffies_since_start < jiffies_since_start)
+ next_jiffies_since_start = jiffies_since_start;
+
+ wait_jiffies = next_jiffies_since_start - jiffies_since_start;
+ schedule_timeout_interruptible(wait_jiffies ? wait_jiffies : 1);
+ }
+ dprintk(dev, 1, "Video Output Thread End\n");
+ return 0;
+}
+
+static void vivid_grab_controls(struct vivid_dev *dev, bool grab)
+{
+ v4l2_ctrl_grab(dev->ctrl_has_crop_out, grab);
+ v4l2_ctrl_grab(dev->ctrl_has_compose_out, grab);
+ v4l2_ctrl_grab(dev->ctrl_has_scaler_out, grab);
+ v4l2_ctrl_grab(dev->ctrl_tx_mode, grab);
+ v4l2_ctrl_grab(dev->ctrl_tx_rgb_range, grab);
+}
+
+int vivid_start_generating_vid_out(struct vivid_dev *dev, bool *pstreaming)
+{
+ dprintk(dev, 1, "%s\n", __func__);
+
+ if (dev->kthread_vid_out) {
+ u32 seq_count = dev->out_seq_count + dev->seq_wrap * 128;
+
+ if (pstreaming == &dev->vid_out_streaming)
+ dev->vid_out_seq_start = seq_count;
+ else
+ dev->vbi_out_seq_start = seq_count;
+ *pstreaming = true;
+ return 0;
+ }
+
+ /* Resets frame counters */
+ dev->jiffies_vid_out = jiffies;
+ dev->vid_out_seq_start = dev->seq_wrap * 128;
+ dev->vbi_out_seq_start = dev->seq_wrap * 128;
+
+ dev->kthread_vid_out = kthread_run(vivid_thread_vid_out, dev,
+ "%s-vid-out", dev->v4l2_dev.name);
+
+ if (IS_ERR(dev->kthread_vid_out)) {
+ v4l2_err(&dev->v4l2_dev, "kernel_thread() failed\n");
+ return PTR_ERR(dev->kthread_vid_out);
+ }
+ *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);
+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+ dprintk(dev, 2, "vid_out buffer %d done\n",
+ buf->vb.v4l2_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);
+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+ dprintk(dev, 2, "vbi_out buffer %d done\n",
+ buf->vb.v4l2_buf.index);
+ }
+ }
+
+ if (dev->vid_out_streaming || dev->vbi_out_streaming)
+ return;
+
+ /* shutdown control thread */
+ vivid_grab_controls(dev, false);
+ mutex_unlock(&dev->mutex);
+ kthread_stop(dev->kthread_vid_out);
+ dev->kthread_vid_out = NULL;
+ mutex_lock(&dev->mutex);
+}
diff --git a/drivers/media/platform/vivid/vivid-kthread-out.h b/drivers/media/platform/vivid/vivid-kthread-out.h
new file mode 100644
index 000000000000..2bf04a17b05d
--- /dev/null
+++ b/drivers/media/platform/vivid/vivid-kthread-out.h
@@ -0,0 +1,26 @@
+/*
+ * vivid-kthread-out.h - video/vbi output thread support functions.
+ *
+ * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef _VIVID_KTHREAD_OUT_H_
+#define _VIVID_KTHREAD_OUT_H_
+
+int vivid_start_generating_vid_out(struct vivid_dev *dev, bool *pstreaming);
+void vivid_stop_generating_vid_out(struct vivid_dev *dev, bool *pstreaming);
+
+#endif
diff --git a/drivers/media/platform/vivid/vivid-osd.c b/drivers/media/platform/vivid/vivid-osd.c
new file mode 100644
index 000000000000..084d346fb4c4
--- /dev/null
+++ b/drivers/media/platform/vivid/vivid-osd.c
@@ -0,0 +1,400 @@
+/*
+ * vivid-osd.c - osd support for testing overlays.
+ *
+ * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/font.h>
+#include <linux/mutex.h>
+#include <linux/videodev2.h>
+#include <linux/kthread.h>
+#include <linux/freezer.h>
+#include <linux/fb.h>
+#include <media/videobuf2-vmalloc.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-common.h>
+
+#include "vivid-core.h"
+#include "vivid-osd.h"
+
+#define MAX_OSD_WIDTH 720
+#define MAX_OSD_HEIGHT 576
+
+/*
+ * Order: white, yellow, cyan, green, magenta, red, blue, black,
+ * and same again with the alpha bit set (if any)
+ */
+static const u16 rgb555[16] = {
+ 0x7fff, 0x7fe0, 0x03ff, 0x03e0, 0x7c1f, 0x7c00, 0x001f, 0x0000,
+ 0xffff, 0xffe0, 0x83ff, 0x83e0, 0xfc1f, 0xfc00, 0x801f, 0x8000
+};
+
+static const u16 rgb565[16] = {
+ 0xffff, 0xffe0, 0x07ff, 0x07e0, 0xf81f, 0xf800, 0x001f, 0x0000,
+ 0xffff, 0xffe0, 0x07ff, 0x07e0, 0xf81f, 0xf800, 0x001f, 0x0000
+};
+
+void vivid_clear_fb(struct vivid_dev *dev)
+{
+ void *p = dev->video_vbase;
+ const u16 *rgb = rgb555;
+ unsigned x, y;
+
+ if (dev->fb_defined.green.length == 6)
+ rgb = rgb565;
+
+ for (y = 0; y < dev->display_height; y++) {
+ u16 *d = p;
+
+ for (x = 0; x < dev->display_width; x++)
+ d[x] = rgb[(y / 16 + x / 16) % 16];
+ p += dev->display_byte_stride;
+ }
+}
+
+/* --------------------------------------------------------------------- */
+
+static int vivid_fb_ioctl(struct fb_info *info, unsigned cmd, unsigned long arg)
+{
+ struct vivid_dev *dev = (struct vivid_dev *)info->par;
+
+ switch (cmd) {
+ case FBIOGET_VBLANK: {
+ struct fb_vblank vblank;
+
+ 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));
+ strlcpy(fix->id, "vioverlay fb", sizeof(fix->id));
+ fix->smem_start = dev->video_pbase;
+ fix->smem_len = dev->video_buffer_size;
+ fix->type = FB_TYPE_PACKED_PIXELS;
+ fix->visual = FB_VISUAL_TRUECOLOR;
+ fix->xpanstep = 1;
+ fix->ypanstep = 1;
+ fix->ywrapstep = 0;
+ fix->line_length = dev->display_byte_stride;
+ fix->accel = FB_ACCEL_NONE;
+ return 0;
+}
+
+/* Check the requested display mode, returning -EINVAL if we can't
+ handle it. */
+
+static int _vivid_fb_check_var(struct fb_var_screeninfo *var, struct vivid_dev *dev)
+{
+ dprintk(dev, 1, "vivid_fb_check_var\n");
+
+ var->bits_per_pixel = 16;
+ if (var->green.length == 5) {
+ var->red.offset = 10;
+ var->red.length = 5;
+ var->green.offset = 5;
+ var->green.length = 5;
+ var->blue.offset = 0;
+ var->blue.length = 5;
+ var->transp.offset = 15;
+ var->transp.length = 1;
+ } else {
+ var->red.offset = 11;
+ var->red.length = 5;
+ var->green.offset = 5;
+ var->green.length = 6;
+ var->blue.offset = 0;
+ var->blue.length = 5;
+ var->transp.offset = 0;
+ var->transp.length = 0;
+ }
+ var->xoffset = var->yoffset = 0;
+ var->left_margin = var->upper_margin = 0;
+ var->nonstd = 0;
+
+ var->vmode &= ~FB_VMODE_MASK;
+ var->vmode = FB_VMODE_NONINTERLACED;
+
+ /* Dummy values */
+ var->hsync_len = 24;
+ var->vsync_len = 2;
+ var->pixclock = 84316;
+ var->right_margin = 776;
+ var->lower_margin = 591;
+ return 0;
+}
+
+static int vivid_fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
+{
+ struct vivid_dev *dev = (struct vivid_dev *) info->par;
+
+ dprintk(dev, 1, "vivid_fb_check_var\n");
+ return _vivid_fb_check_var(var, dev);
+}
+
+static int vivid_fb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
+{
+ return 0;
+}
+
+static int vivid_fb_set_par(struct fb_info *info)
+{
+ int rc = 0;
+ struct vivid_dev *dev = (struct vivid_dev *) info->par;
+
+ dprintk(dev, 1, "vivid_fb_set_par\n");
+
+ rc = vivid_fb_set_var(dev, &info->var);
+ vivid_fb_get_fix(dev, &info->fix);
+ return rc;
+}
+
+static int vivid_fb_setcolreg(unsigned regno, unsigned red, unsigned green,
+ unsigned blue, unsigned transp,
+ struct fb_info *info)
+{
+ u32 color, *palette;
+
+ if (regno >= info->cmap.len)
+ return -EINVAL;
+
+ color = ((transp & 0xFF00) << 16) | ((red & 0xFF00) << 8) |
+ (green & 0xFF00) | ((blue & 0xFF00) >> 8);
+ if (regno >= 16)
+ return -EINVAL;
+
+ palette = info->pseudo_palette;
+ if (info->var.bits_per_pixel == 16) {
+ switch (info->var.green.length) {
+ case 6:
+ color = (red & 0xf800) |
+ ((green & 0xfc00) >> 5) |
+ ((blue & 0xf800) >> 11);
+ break;
+ case 5:
+ color = ((red & 0xf800) >> 1) |
+ ((green & 0xf800) >> 6) |
+ ((blue & 0xf800) >> 11) |
+ (transp ? 0x8000 : 0);
+ break;
+ }
+ }
+ palette[regno] = color;
+ return 0;
+}
+
+/* We don't really support blanking. All this does is enable or
+ disable the OSD. */
+static int vivid_fb_blank(int blank_mode, struct fb_info *info)
+{
+ struct vivid_dev *dev = (struct vivid_dev *)info->par;
+
+ dprintk(dev, 1, "Set blanking mode : %d\n", blank_mode);
+ switch (blank_mode) {
+ case FB_BLANK_UNBLANK:
+ break;
+ case FB_BLANK_NORMAL:
+ case FB_BLANK_HSYNC_SUSPEND:
+ case FB_BLANK_VSYNC_SUSPEND:
+ case FB_BLANK_POWERDOWN:
+ break;
+ }
+ return 0;
+}
+
+static struct fb_ops vivid_fb_ops = {
+ .owner = THIS_MODULE,
+ .fb_check_var = vivid_fb_check_var,
+ .fb_set_par = vivid_fb_set_par,
+ .fb_setcolreg = vivid_fb_setcolreg,
+ .fb_fillrect = cfb_fillrect,
+ .fb_copyarea = cfb_copyarea,
+ .fb_imageblit = cfb_imageblit,
+ .fb_cursor = NULL,
+ .fb_ioctl = vivid_fb_ioctl,
+ .fb_pan_display = vivid_fb_pan_display,
+ .fb_blank = vivid_fb_blank,
+};
+
+/* Initialization */
+
+
+/* Setup our initial video mode */
+static int vivid_fb_init_vidmode(struct vivid_dev *dev)
+{
+ struct v4l2_rect start_window;
+
+ /* Color mode */
+
+ dev->bits_per_pixel = 16;
+ dev->bytes_per_pixel = dev->bits_per_pixel / 8;
+
+ start_window.width = MAX_OSD_WIDTH;
+ start_window.left = 0;
+
+ dev->display_byte_stride = start_window.width * dev->bytes_per_pixel;
+
+ /* Vertical size & position */
+
+ start_window.height = MAX_OSD_HEIGHT;
+ start_window.top = 0;
+
+ dev->display_width = start_window.width;
+ dev->display_height = start_window.height;
+
+ /* Generate a valid fb_var_screeninfo */
+
+ dev->fb_defined.xres = dev->display_width;
+ dev->fb_defined.yres = dev->display_height;
+ dev->fb_defined.xres_virtual = dev->display_width;
+ dev->fb_defined.yres_virtual = dev->display_height;
+ dev->fb_defined.bits_per_pixel = dev->bits_per_pixel;
+ dev->fb_defined.vmode = FB_VMODE_NONINTERLACED;
+ dev->fb_defined.left_margin = start_window.left + 1;
+ dev->fb_defined.upper_margin = start_window.top + 1;
+ dev->fb_defined.accel_flags = FB_ACCEL_NONE;
+ dev->fb_defined.nonstd = 0;
+ /* set default to 1:5:5:5 */
+ dev->fb_defined.green.length = 5;
+
+ /* We've filled in the most data, let the usual mode check
+ routine fill in the rest. */
+ _vivid_fb_check_var(&dev->fb_defined, dev);
+
+ /* Generate valid fb_fix_screeninfo */
+
+ vivid_fb_get_fix(dev, &dev->fb_fix);
+
+ /* Generate valid fb_info */
+
+ dev->fb_info.node = -1;
+ dev->fb_info.flags = FBINFO_FLAG_DEFAULT;
+ dev->fb_info.fbops = &vivid_fb_ops;
+ dev->fb_info.par = dev;
+ dev->fb_info.var = dev->fb_defined;
+ dev->fb_info.fix = dev->fb_fix;
+ dev->fb_info.screen_base = (u8 __iomem *)dev->video_vbase;
+ dev->fb_info.fbops = &vivid_fb_ops;
+
+ /* Supply some monitor specs. Bogus values will do for now */
+ dev->fb_info.monspecs.hfmin = 8000;
+ dev->fb_info.monspecs.hfmax = 70000;
+ dev->fb_info.monspecs.vfmin = 10;
+ dev->fb_info.monspecs.vfmax = 100;
+
+ /* Allocate color map */
+ if (fb_alloc_cmap(&dev->fb_info.cmap, 256, 1)) {
+ pr_err("abort, unable to alloc cmap\n");
+ return -ENOMEM;
+ }
+
+ /* Allocate the pseudo palette */
+ dev->fb_info.pseudo_palette = kmalloc_array(16, sizeof(u32), GFP_KERNEL);
+
+ return dev->fb_info.pseudo_palette ? 0 : -ENOMEM;
+}
+
+/* Release any memory we've grabbed */
+void vivid_fb_release_buffers(struct vivid_dev *dev)
+{
+ if (dev->video_vbase == NULL)
+ return;
+
+ /* Release cmap */
+ if (dev->fb_info.cmap.len)
+ fb_dealloc_cmap(&dev->fb_info.cmap);
+
+ /* Release pseudo palette */
+ kfree(dev->fb_info.pseudo_palette);
+ kfree((void *)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
new file mode 100644
index 000000000000..57c9daa5940a
--- /dev/null
+++ b/drivers/media/platform/vivid/vivid-osd.h
@@ -0,0 +1,27 @@
+/*
+ * vivid-osd.h - output overlay support functions.
+ *
+ * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef _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
new file mode 100644
index 000000000000..78c1e920670a
--- /dev/null
+++ b/drivers/media/platform/vivid/vivid-radio-common.c
@@ -0,0 +1,189 @@
+/*
+ * vivid-radio-common.c - common radio rx/tx support functions.
+ *
+ * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/videodev2.h>
+
+#include "vivid-core.h"
+#include "vivid-ctrls.h"
+#include "vivid-radio-common.h"
+#include "vivid-rds-gen.h"
+
+/*
+ * These functions are shared between the vivid receiver and transmitter
+ * since both use the same frequency bands.
+ */
+
+const struct v4l2_frequency_band vivid_radio_bands[TOT_BANDS] = {
+ /* Band FM */
+ {
+ .type = V4L2_TUNER_RADIO,
+ .index = 0,
+ .capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO |
+ V4L2_TUNER_CAP_FREQ_BANDS,
+ .rangelow = FM_FREQ_RANGE_LOW,
+ .rangehigh = FM_FREQ_RANGE_HIGH,
+ .modulation = V4L2_BAND_MODULATION_FM,
+ },
+ /* Band AM */
+ {
+ .type = V4L2_TUNER_RADIO,
+ .index = 1,
+ .capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_FREQ_BANDS,
+ .rangelow = AM_FREQ_RANGE_LOW,
+ .rangehigh = AM_FREQ_RANGE_HIGH,
+ .modulation = V4L2_BAND_MODULATION_AM,
+ },
+ /* Band SW */
+ {
+ .type = V4L2_TUNER_RADIO,
+ .index = 2,
+ .capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_FREQ_BANDS,
+ .rangelow = SW_FREQ_RANGE_LOW,
+ .rangehigh = SW_FREQ_RANGE_HIGH,
+ .modulation = V4L2_BAND_MODULATION_AM,
+ },
+};
+
+/*
+ * Initialize the RDS generator. If we can loop, then the RDS generator
+ * is set up with the values from the RDS TX controls, otherwise it
+ * will fill in standard values using one of two alternates.
+ */
+void vivid_radio_rds_init(struct vivid_dev *dev)
+{
+ struct vivid_rds_gen *rds = &dev->rds_gen;
+ bool alt = dev->radio_rx_rds_use_alternates;
+
+ /* Do nothing, blocks will be filled by the transmitter */
+ if (dev->radio_rds_loop && !dev->radio_tx_rds_controls)
+ return;
+
+ if (dev->radio_rds_loop) {
+ v4l2_ctrl_lock(dev->radio_tx_rds_pi);
+ rds->picode = dev->radio_tx_rds_pi->cur.val;
+ rds->pty = dev->radio_tx_rds_pty->cur.val;
+ rds->mono_stereo = dev->radio_tx_rds_mono_stereo->cur.val;
+ rds->art_head = dev->radio_tx_rds_art_head->cur.val;
+ rds->compressed = dev->radio_tx_rds_compressed->cur.val;
+ rds->dyn_pty = dev->radio_tx_rds_dyn_pty->cur.val;
+ rds->ta = dev->radio_tx_rds_ta->cur.val;
+ rds->tp = dev->radio_tx_rds_tp->cur.val;
+ rds->ms = dev->radio_tx_rds_ms->cur.val;
+ strlcpy(rds->psname,
+ dev->radio_tx_rds_psname->p_cur.p_char,
+ sizeof(rds->psname));
+ strlcpy(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
new file mode 100644
index 000000000000..92fe589141b7
--- /dev/null
+++ b/drivers/media/platform/vivid/vivid-radio-common.h
@@ -0,0 +1,40 @@
+/*
+ * vivid-radio-common.h - common radio rx/tx support functions.
+ *
+ * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef _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
new file mode 100644
index 000000000000..c7651a506668
--- /dev/null
+++ b/drivers/media/platform/vivid/vivid-radio-rx.c
@@ -0,0 +1,287 @@
+/*
+ * vivid-radio-rx.c - radio receiver support functions.
+ *
+ * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/videodev2.h>
+#include <linux/v4l2-dv-timings.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-dv-timings.h>
+
+#include "vivid-core.h"
+#include "vivid-ctrls.h"
+#include "vivid-radio-common.h"
+#include "vivid-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 timespec ts;
+ struct v4l2_rds_data *data = dev->rds_gen.data;
+ bool use_alternates;
+ 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:
+ ktime_get_ts(&ts);
+ use_alternates = ts.tv_sec % 10 >= 5;
+ 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);
+ }
+ ts = timespec_sub(ts, dev->radio_rds_init_ts);
+ blk = ts.tv_sec * 100 + ts.tv_nsec / 10000000;
+ blk = (blk * VIVID_RDS_GEN_BLOCKS) / 500;
+ 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;
+}
+
+unsigned int vivid_radio_rx_poll(struct file *file, struct poll_table_struct *wait)
+{
+ return POLLIN | POLLRDNORM | 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;
+ 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;
+
+ strlcpy(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 - (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
new file mode 100644
index 000000000000..1077d8f061eb
--- /dev/null
+++ b/drivers/media/platform/vivid/vivid-radio-rx.h
@@ -0,0 +1,31 @@
+/*
+ * vivid-radio-rx.h - radio receiver support functions.
+ *
+ * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef _VIVID_RADIO_RX_H_
+#define _VIVID_RADIO_RX_H_
+
+ssize_t vivid_radio_rx_read(struct file *, char __user *, size_t, loff_t *);
+unsigned int 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
new file mode 100644
index 000000000000..8c59d4f53200
--- /dev/null
+++ b/drivers/media/platform/vivid/vivid-radio-tx.c
@@ -0,0 +1,141 @@
+/*
+ * vivid-radio-tx.c - radio transmitter support functions.
+ *
+ * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/videodev2.h>
+#include <linux/v4l2-dv-timings.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-dv-timings.h>
+
+#include "vivid-core.h"
+#include "vivid-ctrls.h"
+#include "vivid-radio-common.h"
+#include "vivid-radio-tx.h"
+
+ssize_t vivid_radio_tx_write(struct file *file, const char __user *buf,
+ size_t size, loff_t *offset)
+{
+ struct vivid_dev *dev = video_drvdata(file);
+ struct v4l2_rds_data *data = dev->rds_gen.data;
+ struct timespec ts;
+ 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:
+ ktime_get_ts(&ts);
+ ts = timespec_sub(ts, dev->radio_rds_init_ts);
+ blk = ts.tv_sec * 100 + ts.tv_nsec / 10000000;
+ blk = (blk * VIVID_RDS_GEN_BLOCKS) / 500;
+ 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;
+}
+
+unsigned int vivid_radio_tx_poll(struct file *file, struct poll_table_struct *wait)
+{
+ return POLLOUT | POLLWRNORM | 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;
+
+ strlcpy(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
new file mode 100644
index 000000000000..7f8ff7547119
--- /dev/null
+++ b/drivers/media/platform/vivid/vivid-radio-tx.h
@@ -0,0 +1,29 @@
+/*
+ * vivid-radio-tx.h - radio transmitter support functions.
+ *
+ * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef _VIVID_RADIO_TX_H_
+#define _VIVID_RADIO_TX_H_
+
+ssize_t vivid_radio_tx_write(struct file *, const char __user *, size_t, loff_t *);
+unsigned int 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
new file mode 100644
index 000000000000..c382343fdb66
--- /dev/null
+++ b/drivers/media/platform/vivid/vivid-rds-gen.c
@@ -0,0 +1,166 @@
+/*
+ * vivid-rds-gen.c - rds (radio data system) generator support functions.
+ *
+ * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/kernel.h>
+#include <linux/ktime.h>
+#include <linux/string.h>
+#include <linux/videodev2.h>
+
+#include "vivid-rds-gen.h"
+
+static u8 vivid_get_di(const struct vivid_rds_gen *rds, unsigned grp)
+{
+ switch (grp) {
+ case 0:
+ return (rds->dyn_pty << 2) | (grp & 3);
+ case 1:
+ return (rds->compressed << 2) | (grp & 3);
+ case 2:
+ return (rds->art_head << 2) | (grp & 3);
+ case 3:
+ return (rds->mono_stereo << 2) | (grp & 3);
+ }
+ return 0;
+}
+
+/*
+ * This RDS generator creates 57 RDS groups (one group == four RDS blocks).
+ * Groups 0-3, 22-25 and 44-47 (spaced 22 groups apart) are filled with a
+ * standard 0B group containing the PI code and PS name.
+ *
+ * Groups 4-19 and 26-41 use group 2A for the radio text.
+ *
+ * Group 56 contains the time (group 4A).
+ *
+ * All remaining groups use a filler group 15B block that just repeats
+ * the PI and PTY codes.
+ */
+void vivid_rds_generate(struct vivid_rds_gen *rds)
+{
+ struct v4l2_rds_data *data = rds->data;
+ unsigned grp;
+ 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 */
+ data[1].lsb |= (rds->ta << 4) | (rds->ms << 3);
+ data[1].lsb |= vivid_get_di(rds, grp % 22);
+ 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 * (grp % 22) + 1];
+ data[3].msb = rds->psname[2 * (grp % 22)];
+ break;
+ case 4 ... 19:
+ case 26 ... 41: /* Group 2A */
+ data[1].lsb |= (grp - 4) % 22;
+ data[1].msb |= 4 << 3;
+ data[2].msb = rds->radiotext[4 * ((grp - 4) % 22)];
+ data[2].lsb = rds->radiotext[4 * ((grp - 4) % 22) + 1];
+ data[2].block = V4L2_RDS_BLOCK_C | (V4L2_RDS_BLOCK_C << 3);
+ data[3].msb = rds->radiotext[4 * ((grp - 4) % 22) + 2];
+ data[3].lsb = rds->radiotext[4 * ((grp - 4) % 22) + 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.
+ */
+ time_to_tm(get_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)
+ strlcpy(rds->radiotext,
+ " The Radio Data System can switch between different Radio Texts ",
+ sizeof(rds->radiotext));
+ else
+ strlcpy(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
new file mode 100644
index 000000000000..eff4bf552ed3
--- /dev/null
+++ b/drivers/media/platform/vivid/vivid-rds-gen.h
@@ -0,0 +1,53 @@
+/*
+ * vivid-rds-gen.h - rds (radio data system) generator support functions.
+ *
+ * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef _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)
+
+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
new file mode 100644
index 000000000000..8c5d661cfc49
--- /dev/null
+++ b/drivers/media/platform/vivid/vivid-sdr-cap.c
@@ -0,0 +1,499 @@
+/*
+ * vivid-sdr-cap.c - software defined radio support functions.
+ *
+ * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/kthread.h>
+#include <linux/freezer.h>
+#include <linux/videodev2.h>
+#include <linux/v4l2-dv-timings.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-dv-timings.h>
+
+#include "vivid-core.h"
+#include "vivid-ctrls.h"
+#include "vivid-sdr-cap.h"
+
+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.v4l2_buf.sequence = dev->sdr_cap_seq_count;
+ vivid_sdr_cap_process(dev, sdr_cap_buf);
+ v4l2_get_timestamp(&sdr_cap_buf->vb.v4l2_buf.timestamp);
+ sdr_cap_buf->vb.v4l2_buf.timestamp.tv_sec += dev->time_wrap_offset;
+ vb2_buffer_done(&sdr_cap_buf->vb, 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;
+
+ mutex_lock(&dev->mutex);
+ 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, const struct v4l2_format *fmt,
+ unsigned *nbuffers, unsigned *nplanes,
+ unsigned sizes[], void *alloc_ctxs[])
+{
+ /* 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 vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
+ struct vivid_buffer *buf = container_of(vb, 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_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);
+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+ }
+
+ /* shutdown control thread */
+ mutex_unlock(&dev->mutex);
+ kthread_stop(dev->kthread_sdr_cap);
+ dev->kthread_sdr_cap = NULL;
+ mutex_lock(&dev->mutex);
+}
+
+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,
+ .wait_prepare = vivid_unlock,
+ .wait_finish = vivid_lock,
+};
+
+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:
+ strlcpy(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:
+ strlcpy(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)
+ return -EINVAL;
+ f->pixelformat = V4L2_SDR_FMT_CU8;
+ strlcpy(f->description, "IQ U8", sizeof(f->description));
+ return 0;
+}
+
+int vidioc_g_fmt_sdr_cap(struct file *file, void *fh, struct v4l2_format *f)
+{
+ f->fmt.sdr.pixelformat = V4L2_SDR_FMT_CU8;
+ f->fmt.sdr.buffersize = SDR_CAP_SAMPLES_PER_BUF * 2;
+ memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved));
+ return 0;
+}
+
+#define FIXP_FRAC (1 << 15)
+#define FIXP_PI ((int)(FIXP_FRAC * 3.141592653589))
+
+/* cos() from cx88 driver: cx88-dsp.c */
+static s32 fixp_cos(unsigned int x)
+{
+ u32 t2, t4, t6, t8;
+ u16 period = x / FIXP_PI;
+
+ if (period % 2)
+ return -fixp_cos(x - FIXP_PI);
+ x = x % FIXP_PI;
+ if (x > FIXP_PI/2)
+ return -fixp_cos(FIXP_PI/2 - (x % (FIXP_PI/2)));
+ /* Now x is between 0 and FIXP_PI/2.
+ * To calculate cos(x) we use it's Taylor polinom. */
+ t2 = x*x/FIXP_FRAC/2;
+ t4 = t2*x/FIXP_FRAC*x/FIXP_FRAC/3/4;
+ t6 = t4*x/FIXP_FRAC*x/FIXP_FRAC/5/6;
+ t8 = t6*x/FIXP_FRAC*x/FIXP_FRAC/7/8;
+ return FIXP_FRAC-t2+t4-t6+t8;
+}
+
+static inline s32 fixp_sin(unsigned int x)
+{
+ return -fixp_cos(x + (FIXP_PI / 2));
+}
+
+void vivid_sdr_cap_process(struct vivid_dev *dev, struct vivid_buffer *buf)
+{
+ u8 *vbuf = vb2_plane_vaddr(&buf->vb, 0);
+ unsigned long i;
+ unsigned long plane_size = vb2_plane_size(&buf->vb, 0);
+ int fixp_src_phase_step, fixp_i, fixp_q;
+
+ /*
+ * TODO: Generated beep tone goes very crackly when sample rate is
+ * increased to ~1Msps or more. That is because of huge rounding error
+ * of phase angle caused by used cosine implementation.
+ */
+
+ /* calculate phase step */
+ #define BEEP_FREQ 1000 /* 1kHz beep */
+ fixp_src_phase_step = DIV_ROUND_CLOSEST(2 * FIXP_PI * BEEP_FREQ,
+ dev->sdr_adc_freq);
+
+ for (i = 0; i < plane_size; i += 2) {
+ dev->sdr_fixp_mod_phase += fixp_cos(dev->sdr_fixp_src_phase);
+ dev->sdr_fixp_src_phase += fixp_src_phase_step;
+
+ /*
+ * Transfer phases to [0 / 2xPI] in order to avoid variable
+ * overflow and make it suitable for cosine implementation
+ * used, which does not support negative angles.
+ */
+ while (dev->sdr_fixp_mod_phase < (0 * FIXP_PI))
+ dev->sdr_fixp_mod_phase += (2 * FIXP_PI);
+ while (dev->sdr_fixp_mod_phase > (2 * FIXP_PI))
+ dev->sdr_fixp_mod_phase -= (2 * FIXP_PI);
+
+ while (dev->sdr_fixp_src_phase > (2 * FIXP_PI))
+ dev->sdr_fixp_src_phase -= (2 * FIXP_PI);
+
+ fixp_i = fixp_cos(dev->sdr_fixp_mod_phase);
+ fixp_q = fixp_sin(dev->sdr_fixp_mod_phase);
+
+ /* convert 'fixp float' to u8 */
+ /* u8 = X * 127.5f + 127.5f; where 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);
+ }
+}
diff --git a/drivers/media/platform/vivid/vivid-sdr-cap.h b/drivers/media/platform/vivid/vivid-sdr-cap.h
new file mode 100644
index 000000000000..79c1890de972
--- /dev/null
+++ b/drivers/media/platform/vivid/vivid-sdr-cap.h
@@ -0,0 +1,34 @@
+/*
+ * vivid-sdr-cap.h - software defined radio support functions.
+ *
+ * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef _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);
+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-tpg-colors.c b/drivers/media/platform/vivid/vivid-tpg-colors.c
new file mode 100644
index 000000000000..2adddc0ca662
--- /dev/null
+++ b/drivers/media/platform/vivid/vivid-tpg-colors.c
@@ -0,0 +1,310 @@
+/*
+ * vivid-color.c - A table that converts colors to various colorspaces
+ *
+ * The test pattern generator uses the tpg_colors for its test patterns.
+ * For testing colorspaces the first 8 colors of that table need to be
+ * converted to their equivalent in the target colorspace.
+ *
+ * The tpg_csc_colors[] table is the result of that conversion and since
+ * it is precalculated the colorspace conversion is just a simple table
+ * lookup.
+ *
+ * This source also contains the code used to generate the tpg_csc_colors
+ * table. Run the following command to compile it:
+ *
+ * gcc vivid-colors.c -DCOMPILE_APP -o gen-colors -lm
+ *
+ * and run the utility.
+ *
+ * Note that the converted colors are in the range 0x000-0xff0 (so times 16)
+ * in order to preserve precision.
+ *
+ * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/videodev2.h>
+
+#include "vivid-tpg-colors.h"
+
+/* sRGB colors with range [0-255] */
+const struct color tpg_colors[TPG_COLOR_MAX] = {
+ /*
+ * Colors to test colorspace conversion: converting these colors
+ * to other colorspaces will never lead to out-of-gamut colors.
+ */
+ { 191, 191, 191 }, /* TPG_COLOR_CSC_WHITE */
+ { 191, 191, 50 }, /* TPG_COLOR_CSC_YELLOW */
+ { 50, 191, 191 }, /* TPG_COLOR_CSC_CYAN */
+ { 50, 191, 50 }, /* TPG_COLOR_CSC_GREEN */
+ { 191, 50, 191 }, /* TPG_COLOR_CSC_MAGENTA */
+ { 191, 50, 50 }, /* TPG_COLOR_CSC_RED */
+ { 50, 50, 191 }, /* TPG_COLOR_CSC_BLUE */
+ { 50, 50, 50 }, /* TPG_COLOR_CSC_BLACK */
+
+ /* 75% colors */
+ { 191, 191, 0 }, /* TPG_COLOR_75_YELLOW */
+ { 0, 191, 191 }, /* TPG_COLOR_75_CYAN */
+ { 0, 191, 0 }, /* TPG_COLOR_75_GREEN */
+ { 191, 0, 191 }, /* TPG_COLOR_75_MAGENTA */
+ { 191, 0, 0 }, /* TPG_COLOR_75_RED */
+ { 0, 0, 191 }, /* TPG_COLOR_75_BLUE */
+
+ /* 100% colors */
+ { 255, 255, 255 }, /* TPG_COLOR_100_WHITE */
+ { 255, 255, 0 }, /* TPG_COLOR_100_YELLOW */
+ { 0, 255, 255 }, /* TPG_COLOR_100_CYAN */
+ { 0, 255, 0 }, /* TPG_COLOR_100_GREEN */
+ { 255, 0, 255 }, /* TPG_COLOR_100_MAGENTA */
+ { 255, 0, 0 }, /* TPG_COLOR_100_RED */
+ { 0, 0, 255 }, /* TPG_COLOR_100_BLUE */
+ { 0, 0, 0 }, /* TPG_COLOR_100_BLACK */
+
+ { 0, 0, 0 }, /* TPG_COLOR_RANDOM placeholder */
+};
+
+#ifndef COMPILE_APP
+
+/* Generated table */
+const struct color16 tpg_csc_colors[V4L2_COLORSPACE_SRGB + 1][TPG_COLOR_CSC_BLACK + 1] = {
+ [V4L2_COLORSPACE_SMPTE170M][0] = { 2953, 2939, 2939 },
+ [V4L2_COLORSPACE_SMPTE170M][1] = { 2954, 2963, 585 },
+ [V4L2_COLORSPACE_SMPTE170M][2] = { 84, 2967, 2937 },
+ [V4L2_COLORSPACE_SMPTE170M][3] = { 93, 2990, 575 },
+ [V4L2_COLORSPACE_SMPTE170M][4] = { 3030, 259, 2933 },
+ [V4L2_COLORSPACE_SMPTE170M][5] = { 3031, 406, 557 },
+ [V4L2_COLORSPACE_SMPTE170M][6] = { 544, 428, 2931 },
+ [V4L2_COLORSPACE_SMPTE170M][7] = { 551, 547, 547 },
+ [V4L2_COLORSPACE_SMPTE240M][0] = { 2926, 2926, 2926 },
+ [V4L2_COLORSPACE_SMPTE240M][1] = { 2926, 2926, 857 },
+ [V4L2_COLORSPACE_SMPTE240M][2] = { 1594, 2901, 2901 },
+ [V4L2_COLORSPACE_SMPTE240M][3] = { 1594, 2901, 774 },
+ [V4L2_COLORSPACE_SMPTE240M][4] = { 2484, 618, 2858 },
+ [V4L2_COLORSPACE_SMPTE240M][5] = { 2484, 618, 617 },
+ [V4L2_COLORSPACE_SMPTE240M][6] = { 507, 507, 2832 },
+ [V4L2_COLORSPACE_SMPTE240M][7] = { 507, 507, 507 },
+ [V4L2_COLORSPACE_REC709][0] = { 2939, 2939, 2939 },
+ [V4L2_COLORSPACE_REC709][1] = { 2939, 2939, 547 },
+ [V4L2_COLORSPACE_REC709][2] = { 547, 2939, 2939 },
+ [V4L2_COLORSPACE_REC709][3] = { 547, 2939, 547 },
+ [V4L2_COLORSPACE_REC709][4] = { 2939, 547, 2939 },
+ [V4L2_COLORSPACE_REC709][5] = { 2939, 547, 547 },
+ [V4L2_COLORSPACE_REC709][6] = { 547, 547, 2939 },
+ [V4L2_COLORSPACE_REC709][7] = { 547, 547, 547 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][0] = { 2894, 2988, 2808 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][1] = { 2847, 3070, 843 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][2] = { 1656, 2962, 2783 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][3] = { 1572, 3045, 763 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][4] = { 2477, 229, 2743 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][5] = { 2422, 672, 614 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][6] = { 725, 63, 2718 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][7] = { 534, 561, 509 },
+ [V4L2_COLORSPACE_470_SYSTEM_BG][0] = { 2939, 2939, 2939 },
+ [V4L2_COLORSPACE_470_SYSTEM_BG][1] = { 2939, 2939, 621 },
+ [V4L2_COLORSPACE_470_SYSTEM_BG][2] = { 786, 2939, 2939 },
+ [V4L2_COLORSPACE_470_SYSTEM_BG][3] = { 786, 2939, 621 },
+ [V4L2_COLORSPACE_470_SYSTEM_BG][4] = { 2879, 547, 2923 },
+ [V4L2_COLORSPACE_470_SYSTEM_BG][5] = { 2879, 547, 547 },
+ [V4L2_COLORSPACE_470_SYSTEM_BG][6] = { 547, 547, 2923 },
+ [V4L2_COLORSPACE_470_SYSTEM_BG][7] = { 547, 547, 547 },
+ [V4L2_COLORSPACE_SRGB][0] = { 3056, 3056, 3056 },
+ [V4L2_COLORSPACE_SRGB][1] = { 3056, 3056, 800 },
+ [V4L2_COLORSPACE_SRGB][2] = { 800, 3056, 3056 },
+ [V4L2_COLORSPACE_SRGB][3] = { 800, 3056, 800 },
+ [V4L2_COLORSPACE_SRGB][4] = { 3056, 800, 3056 },
+ [V4L2_COLORSPACE_SRGB][5] = { 3056, 800, 800 },
+ [V4L2_COLORSPACE_SRGB][6] = { 800, 800, 3056 },
+ [V4L2_COLORSPACE_SRGB][7] = { 800, 800, 800 },
+};
+
+#else
+
+/* This code generates the table above */
+
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+static const double rec709_to_ntsc1953[3][3] = {
+ { 0.6698, 0.2678, 0.0323 },
+ { 0.0185, 1.0742, -0.0603 },
+ { 0.0162, 0.0432, 0.8551 }
+};
+
+static const double rec709_to_ebu[3][3] = {
+ { 0.9578, 0.0422, 0 },
+ { 0 , 1 , 0 },
+ { 0 , 0.0118, 0.9882 }
+};
+
+static const double rec709_to_170m[3][3] = {
+ { 1.0654, -0.0554, -0.0010 },
+ { -0.0196, 1.0364, -0.0167 },
+ { 0.0016, 0.0044, 0.9940 }
+};
+
+static const double rec709_to_240m[3][3] = {
+ { 0.7151, 0.2849, 0 },
+ { 0.0179, 0.9821, 0 },
+ { 0.0177, 0.0472, 0.9350 }
+};
+
+
+static void mult_matrix(double *r, double *g, double *b, const double m[3][3])
+{
+ double ir, ig, ib;
+
+ ir = m[0][0] * (*r) + m[0][1] * (*g) + m[0][2] * (*b);
+ ig = m[1][0] * (*r) + m[1][1] * (*g) + m[1][2] * (*b);
+ ib = m[2][0] * (*r) + m[2][1] * (*g) + m[2][2] * (*b);
+ *r = ir;
+ *g = ig;
+ *b = ib;
+}
+
+static double transfer_srgb_to_rgb(double v)
+{
+ return (v <= 0.03928) ? v / 12.92 : pow((v + 0.055) / 1.055, 2.4);
+}
+
+static double transfer_rgb_to_smpte240m(double v)
+{
+ return (v <= 0.0228) ? v * 4.0 : 1.1115 * pow(v, 0.45) - 0.1115;
+}
+
+static double transfer_rgb_to_rec709(double v)
+{
+ return (v < 0.018) ? v * 4.5 : 1.099 * pow(v, 0.45) - 0.099;
+}
+
+static double transfer_srgb_to_rec709(double v)
+{
+ return transfer_rgb_to_rec709(transfer_srgb_to_rgb(v));
+}
+
+static void csc(enum v4l2_colorspace colorspace, double *r, double *g, double *b)
+{
+ /* Convert the primaries of Rec. 709 Linear RGB */
+ switch (colorspace) {
+ case V4L2_COLORSPACE_SMPTE240M:
+ *r = transfer_srgb_to_rgb(*r);
+ *g = transfer_srgb_to_rgb(*g);
+ *b = transfer_srgb_to_rgb(*b);
+ mult_matrix(r, g, b, rec709_to_240m);
+ break;
+ case V4L2_COLORSPACE_SMPTE170M:
+ *r = transfer_srgb_to_rgb(*r);
+ *g = transfer_srgb_to_rgb(*g);
+ *b = transfer_srgb_to_rgb(*b);
+ mult_matrix(r, g, b, rec709_to_170m);
+ break;
+ case V4L2_COLORSPACE_470_SYSTEM_BG:
+ *r = transfer_srgb_to_rgb(*r);
+ *g = transfer_srgb_to_rgb(*g);
+ *b = transfer_srgb_to_rgb(*b);
+ mult_matrix(r, g, b, rec709_to_ebu);
+ break;
+ case V4L2_COLORSPACE_470_SYSTEM_M:
+ *r = transfer_srgb_to_rgb(*r);
+ *g = transfer_srgb_to_rgb(*g);
+ *b = transfer_srgb_to_rgb(*b);
+ mult_matrix(r, g, b, rec709_to_ntsc1953);
+ break;
+ case V4L2_COLORSPACE_SRGB:
+ case V4L2_COLORSPACE_REC709:
+ default:
+ break;
+ }
+
+ *r = ((*r) < 0) ? 0 : (((*r) > 1) ? 1 : (*r));
+ *g = ((*g) < 0) ? 0 : (((*g) > 1) ? 1 : (*g));
+ *b = ((*b) < 0) ? 0 : (((*b) > 1) ? 1 : (*b));
+
+ /* Encode to gamma corrected colorspace */
+ switch (colorspace) {
+ case V4L2_COLORSPACE_SMPTE240M:
+ *r = transfer_rgb_to_smpte240m(*r);
+ *g = transfer_rgb_to_smpte240m(*g);
+ *b = transfer_rgb_to_smpte240m(*b);
+ break;
+ case V4L2_COLORSPACE_SMPTE170M:
+ case V4L2_COLORSPACE_470_SYSTEM_M:
+ case V4L2_COLORSPACE_470_SYSTEM_BG:
+ *r = transfer_rgb_to_rec709(*r);
+ *g = transfer_rgb_to_rec709(*g);
+ *b = transfer_rgb_to_rec709(*b);
+ break;
+ case V4L2_COLORSPACE_SRGB:
+ break;
+ case V4L2_COLORSPACE_REC709:
+ default:
+ *r = transfer_srgb_to_rec709(*r);
+ *g = transfer_srgb_to_rec709(*g);
+ *b = transfer_srgb_to_rec709(*b);
+ break;
+ }
+}
+
+int main(int argc, char **argv)
+{
+ static const unsigned colorspaces[] = {
+ 0,
+ V4L2_COLORSPACE_SMPTE170M,
+ V4L2_COLORSPACE_SMPTE240M,
+ V4L2_COLORSPACE_REC709,
+ 0,
+ V4L2_COLORSPACE_470_SYSTEM_M,
+ V4L2_COLORSPACE_470_SYSTEM_BG,
+ 0,
+ V4L2_COLORSPACE_SRGB,
+ };
+ static const char * const colorspace_names[] = {
+ "",
+ "V4L2_COLORSPACE_SMPTE170M",
+ "V4L2_COLORSPACE_SMPTE240M",
+ "V4L2_COLORSPACE_REC709",
+ "",
+ "V4L2_COLORSPACE_470_SYSTEM_M",
+ "V4L2_COLORSPACE_470_SYSTEM_BG",
+ "",
+ "V4L2_COLORSPACE_SRGB",
+ };
+ int i;
+ int c;
+
+ printf("/* Generated table */\n");
+ printf("const struct color16 tpg_csc_colors[V4L2_COLORSPACE_SRGB + 1][TPG_COLOR_CSC_BLACK + 1] = {\n");
+ for (c = 0; c <= V4L2_COLORSPACE_SRGB; c++) {
+ for (i = 0; i <= TPG_COLOR_CSC_BLACK; i++) {
+ double r, g, b;
+
+ if (colorspaces[c] == 0)
+ continue;
+
+ r = tpg_colors[i].r / 255.0;
+ g = tpg_colors[i].g / 255.0;
+ b = tpg_colors[i].b / 255.0;
+
+ csc(c, &r, &g, &b);
+
+ printf("\t[%s][%d] = { %d, %d, %d },\n", colorspace_names[c], i,
+ (int)(r * 4080), (int)(g * 4080), (int)(b * 4080));
+ }
+ }
+ printf("};\n\n");
+ return 0;
+}
+
+#endif
diff --git a/drivers/media/platform/vivid/vivid-tpg-colors.h b/drivers/media/platform/vivid/vivid-tpg-colors.h
new file mode 100644
index 000000000000..a2678fbec256
--- /dev/null
+++ b/drivers/media/platform/vivid/vivid-tpg-colors.h
@@ -0,0 +1,64 @@
+/*
+ * vivid-color.h - Color definitions for the test pattern generator
+ *
+ * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef _VIVID_COLORS_H_
+#define _VIVID_COLORS_H_
+
+struct color {
+ unsigned char r, g, b;
+};
+
+struct color16 {
+ int r, g, b;
+};
+
+enum tpg_color {
+ TPG_COLOR_CSC_WHITE,
+ TPG_COLOR_CSC_YELLOW,
+ TPG_COLOR_CSC_CYAN,
+ TPG_COLOR_CSC_GREEN,
+ TPG_COLOR_CSC_MAGENTA,
+ TPG_COLOR_CSC_RED,
+ TPG_COLOR_CSC_BLUE,
+ TPG_COLOR_CSC_BLACK,
+ TPG_COLOR_75_YELLOW,
+ TPG_COLOR_75_CYAN,
+ TPG_COLOR_75_GREEN,
+ TPG_COLOR_75_MAGENTA,
+ TPG_COLOR_75_RED,
+ TPG_COLOR_75_BLUE,
+ TPG_COLOR_100_WHITE,
+ TPG_COLOR_100_YELLOW,
+ TPG_COLOR_100_CYAN,
+ TPG_COLOR_100_GREEN,
+ TPG_COLOR_100_MAGENTA,
+ TPG_COLOR_100_RED,
+ TPG_COLOR_100_BLUE,
+ TPG_COLOR_100_BLACK,
+ TPG_COLOR_TEXTFG,
+ TPG_COLOR_TEXTBG,
+ TPG_COLOR_RANDOM,
+ TPG_COLOR_RAMP,
+ TPG_COLOR_MAX = TPG_COLOR_RAMP + 256
+};
+
+extern const struct color tpg_colors[TPG_COLOR_MAX];
+extern const struct color16 tpg_csc_colors[V4L2_COLORSPACE_SRGB + 1][TPG_COLOR_CSC_BLACK + 1];
+
+#endif
diff --git a/drivers/media/platform/vivid/vivid-tpg.c b/drivers/media/platform/vivid/vivid-tpg.c
new file mode 100644
index 000000000000..0c6fa53fa646
--- /dev/null
+++ b/drivers/media/platform/vivid/vivid-tpg.c
@@ -0,0 +1,1439 @@
+/*
+ * vivid-tpg.c - Test Pattern Generator
+ *
+ * Note: gen_twopix and tpg_gen_text are based on code from vivi.c. See the
+ * vivi.c source for the copyright information of those functions.
+ *
+ * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "vivid-tpg.h"
+
+/* Must remain in sync with enum tpg_pattern */
+const char * const tpg_pattern_strings[] = {
+ "75% Colorbar",
+ "100% Colorbar",
+ "CSC Colorbar",
+ "Horizontal 100% Colorbar",
+ "100% Color Squares",
+ "100% Black",
+ "100% White",
+ "100% Red",
+ "100% Green",
+ "100% Blue",
+ "16x16 Checkers",
+ "1x1 Checkers",
+ "Alternating Hor Lines",
+ "Alternating Vert Lines",
+ "One Pixel Wide Cross",
+ "Two Pixels Wide Cross",
+ "Ten Pixels Wide Cross",
+ "Gray Ramp",
+ "Noise",
+ NULL
+};
+
+/* Must remain in sync with enum tpg_aspect */
+const char * const tpg_aspect_strings[] = {
+ "Source Width x Height",
+ "4x3",
+ "14x9",
+ "16x9",
+ "16x9 Anamorphic",
+ NULL
+};
+
+/*
+ * Sine table: sin[0] = 127 * sin(-180 degrees)
+ * sin[128] = 127 * sin(0 degrees)
+ * sin[256] = 127 * sin(180 degrees)
+ */
+static const s8 sin[257] = {
+ 0, -4, -7, -11, -13, -18, -20, -22, -26, -29, -33, -35, -37, -41, -43, -48,
+ -50, -52, -56, -58, -62, -63, -65, -69, -71, -75, -76, -78, -82, -83, -87, -88,
+ -90, -93, -94, -97, -99, -101, -103, -104, -107, -108, -110, -111, -112, -114, -115, -117,
+ -118, -119, -120, -121, -122, -123, -123, -124, -125, -125, -126, -126, -127, -127, -127, -127,
+ -127, -127, -127, -127, -126, -126, -125, -125, -124, -124, -123, -122, -121, -120, -119, -118,
+ -117, -116, -114, -113, -111, -110, -109, -107, -105, -103, -101, -100, -97, -96, -93, -91,
+ -90, -87, -85, -82, -80, -76, -75, -73, -69, -67, -63, -62, -60, -56, -54, -50,
+ -48, -46, -41, -39, -35, -33, -31, -26, -24, -20, -18, -15, -11, -9, -4, -2,
+ 0, 2, 4, 9, 11, 15, 18, 20, 24, 26, 31, 33, 35, 39, 41, 46,
+ 48, 50, 54, 56, 60, 62, 64, 67, 69, 73, 75, 76, 80, 82, 85, 87,
+ 90, 91, 93, 96, 97, 100, 101, 103, 105, 107, 109, 110, 111, 113, 114, 116,
+ 117, 118, 119, 120, 121, 122, 123, 124, 124, 125, 125, 126, 126, 127, 127, 127,
+ 127, 127, 127, 127, 127, 126, 126, 125, 125, 124, 123, 123, 122, 121, 120, 119,
+ 118, 117, 115, 114, 112, 111, 110, 108, 107, 104, 103, 101, 99, 97, 94, 93,
+ 90, 88, 87, 83, 82, 78, 76, 75, 71, 69, 65, 64, 62, 58, 56, 52,
+ 50, 48, 43, 41, 37, 35, 33, 29, 26, 22, 20, 18, 13, 11, 7, 4,
+ 0,
+};
+
+#define cos(idx) sin[((idx) + 64) % sizeof(sin)]
+
+/* Global font descriptor */
+static const u8 *font8x16;
+
+void tpg_set_font(const u8 *f)
+{
+ font8x16 = f;
+}
+
+void tpg_init(struct tpg_data *tpg, unsigned w, unsigned h)
+{
+ memset(tpg, 0, sizeof(*tpg));
+ tpg->scaled_width = tpg->src_width = w;
+ tpg->src_height = tpg->buf_height = h;
+ tpg->crop.width = tpg->compose.width = w;
+ tpg->crop.height = tpg->compose.height = h;
+ tpg->recalc_colors = true;
+ tpg->recalc_square_border = true;
+ tpg->brightness = 128;
+ tpg->contrast = 128;
+ tpg->saturation = 128;
+ tpg->hue = 0;
+ tpg->mv_hor_mode = TPG_MOVE_NONE;
+ tpg->mv_vert_mode = TPG_MOVE_NONE;
+ tpg->field = V4L2_FIELD_NONE;
+ tpg_s_fourcc(tpg, V4L2_PIX_FMT_RGB24);
+ tpg->colorspace = V4L2_COLORSPACE_SRGB;
+ tpg->perc_fill = 100;
+}
+
+int tpg_alloc(struct tpg_data *tpg, unsigned max_w)
+{
+ unsigned pat;
+ unsigned plane;
+
+ tpg->max_line_width = max_w;
+ for (pat = 0; pat < TPG_MAX_PAT_LINES; pat++) {
+ for (plane = 0; plane < TPG_MAX_PLANES; plane++) {
+ unsigned pixelsz = plane ? 1 : 4;
+
+ tpg->lines[pat][plane] = vzalloc(max_w * 2 * pixelsz);
+ if (!tpg->lines[pat][plane])
+ return -ENOMEM;
+ }
+ }
+ for (plane = 0; plane < TPG_MAX_PLANES; plane++) {
+ unsigned pixelsz = plane ? 1 : 4;
+
+ tpg->contrast_line[plane] = vzalloc(max_w * pixelsz);
+ if (!tpg->contrast_line[plane])
+ return -ENOMEM;
+ tpg->black_line[plane] = vzalloc(max_w * pixelsz);
+ if (!tpg->black_line[plane])
+ return -ENOMEM;
+ tpg->random_line[plane] = vzalloc(max_w * pixelsz);
+ if (!tpg->random_line[plane])
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+void tpg_free(struct tpg_data *tpg)
+{
+ unsigned pat;
+ unsigned plane;
+
+ for (pat = 0; pat < TPG_MAX_PAT_LINES; pat++)
+ for (plane = 0; plane < TPG_MAX_PLANES; plane++) {
+ vfree(tpg->lines[pat][plane]);
+ tpg->lines[pat][plane] = NULL;
+ }
+ for (plane = 0; plane < TPG_MAX_PLANES; plane++) {
+ vfree(tpg->contrast_line[plane]);
+ vfree(tpg->black_line[plane]);
+ vfree(tpg->random_line[plane]);
+ tpg->contrast_line[plane] = NULL;
+ tpg->black_line[plane] = NULL;
+ tpg->random_line[plane] = NULL;
+ }
+}
+
+bool tpg_s_fourcc(struct tpg_data *tpg, u32 fourcc)
+{
+ tpg->fourcc = fourcc;
+ tpg->planes = 1;
+ tpg->recalc_colors = true;
+ switch (fourcc) {
+ case V4L2_PIX_FMT_RGB565:
+ case V4L2_PIX_FMT_RGB565X:
+ case V4L2_PIX_FMT_RGB555:
+ case V4L2_PIX_FMT_XRGB555:
+ case V4L2_PIX_FMT_ARGB555:
+ case V4L2_PIX_FMT_RGB555X:
+ case V4L2_PIX_FMT_RGB24:
+ case V4L2_PIX_FMT_BGR24:
+ case V4L2_PIX_FMT_RGB32:
+ case V4L2_PIX_FMT_BGR32:
+ case V4L2_PIX_FMT_XRGB32:
+ case V4L2_PIX_FMT_XBGR32:
+ case V4L2_PIX_FMT_ARGB32:
+ case V4L2_PIX_FMT_ABGR32:
+ tpg->is_yuv = false;
+ break;
+ case V4L2_PIX_FMT_NV16M:
+ case V4L2_PIX_FMT_NV61M:
+ tpg->planes = 2;
+ /* fall-through */
+ case V4L2_PIX_FMT_YUYV:
+ case V4L2_PIX_FMT_UYVY:
+ case V4L2_PIX_FMT_YVYU:
+ case V4L2_PIX_FMT_VYUY:
+ tpg->is_yuv = true;
+ break;
+ default:
+ return false;
+ }
+
+ switch (fourcc) {
+ case V4L2_PIX_FMT_RGB565:
+ case V4L2_PIX_FMT_RGB565X:
+ case V4L2_PIX_FMT_RGB555:
+ case V4L2_PIX_FMT_XRGB555:
+ case V4L2_PIX_FMT_ARGB555:
+ case V4L2_PIX_FMT_RGB555X:
+ case V4L2_PIX_FMT_YUYV:
+ case V4L2_PIX_FMT_UYVY:
+ case V4L2_PIX_FMT_YVYU:
+ case V4L2_PIX_FMT_VYUY:
+ tpg->twopixelsize[0] = 2 * 2;
+ break;
+ case V4L2_PIX_FMT_RGB24:
+ case V4L2_PIX_FMT_BGR24:
+ tpg->twopixelsize[0] = 2 * 3;
+ break;
+ case V4L2_PIX_FMT_RGB32:
+ case V4L2_PIX_FMT_BGR32:
+ case V4L2_PIX_FMT_XRGB32:
+ case V4L2_PIX_FMT_XBGR32:
+ case V4L2_PIX_FMT_ARGB32:
+ case V4L2_PIX_FMT_ABGR32:
+ tpg->twopixelsize[0] = 2 * 4;
+ break;
+ case V4L2_PIX_FMT_NV16M:
+ case V4L2_PIX_FMT_NV61M:
+ tpg->twopixelsize[0] = 2;
+ tpg->twopixelsize[1] = 2;
+ break;
+ }
+ return true;
+}
+
+void tpg_s_crop_compose(struct tpg_data *tpg, const struct v4l2_rect *crop,
+ const struct v4l2_rect *compose)
+{
+ tpg->crop = *crop;
+ tpg->compose = *compose;
+ tpg->scaled_width = (tpg->src_width * tpg->compose.width +
+ tpg->crop.width - 1) / tpg->crop.width;
+ tpg->scaled_width &= ~1;
+ if (tpg->scaled_width > tpg->max_line_width)
+ tpg->scaled_width = tpg->max_line_width;
+ if (tpg->scaled_width < 2)
+ tpg->scaled_width = 2;
+ tpg->recalc_lines = true;
+}
+
+void tpg_reset_source(struct tpg_data *tpg, unsigned width, unsigned height,
+ u32 field)
+{
+ unsigned p;
+
+ tpg->src_width = width;
+ tpg->src_height = height;
+ tpg->field = field;
+ tpg->buf_height = height;
+ if (V4L2_FIELD_HAS_T_OR_B(field))
+ tpg->buf_height /= 2;
+ tpg->scaled_width = width;
+ tpg->crop.top = tpg->crop.left = 0;
+ tpg->crop.width = width;
+ tpg->crop.height = height;
+ tpg->compose.top = tpg->compose.left = 0;
+ tpg->compose.width = width;
+ tpg->compose.height = tpg->buf_height;
+ for (p = 0; p < tpg->planes; p++)
+ tpg->bytesperline[p] = width * tpg->twopixelsize[p] / 2;
+ tpg->recalc_square_border = true;
+}
+
+static enum tpg_color tpg_get_textbg_color(struct tpg_data *tpg)
+{
+ switch (tpg->pattern) {
+ case TPG_PAT_BLACK:
+ return TPG_COLOR_100_WHITE;
+ case TPG_PAT_CSC_COLORBAR:
+ return TPG_COLOR_CSC_BLACK;
+ default:
+ return TPG_COLOR_100_BLACK;
+ }
+}
+
+static enum tpg_color tpg_get_textfg_color(struct tpg_data *tpg)
+{
+ switch (tpg->pattern) {
+ case TPG_PAT_75_COLORBAR:
+ case TPG_PAT_CSC_COLORBAR:
+ return TPG_COLOR_CSC_WHITE;
+ case TPG_PAT_BLACK:
+ return TPG_COLOR_100_BLACK;
+ default:
+ return TPG_COLOR_100_WHITE;
+ }
+}
+
+static u16 color_to_y(struct tpg_data *tpg, int r, int g, int b)
+{
+ switch (tpg->colorspace) {
+ case V4L2_COLORSPACE_SMPTE170M:
+ case V4L2_COLORSPACE_470_SYSTEM_M:
+ case V4L2_COLORSPACE_470_SYSTEM_BG:
+ return ((16829 * r + 33039 * g + 6416 * b + 16 * 32768) >> 16) + (16 << 4);
+ case V4L2_COLORSPACE_SMPTE240M:
+ return ((11932 * r + 39455 * g + 4897 * b + 16 * 32768) >> 16) + (16 << 4);
+ case V4L2_COLORSPACE_REC709:
+ case V4L2_COLORSPACE_SRGB:
+ default:
+ return ((11966 * r + 40254 * g + 4064 * b + 16 * 32768) >> 16) + (16 << 4);
+ }
+}
+
+static u16 color_to_cb(struct tpg_data *tpg, int r, int g, int b)
+{
+ switch (tpg->colorspace) {
+ case V4L2_COLORSPACE_SMPTE170M:
+ case V4L2_COLORSPACE_470_SYSTEM_M:
+ case V4L2_COLORSPACE_470_SYSTEM_BG:
+ return ((-9714 * r - 19070 * g + 28784 * b + 16 * 32768) >> 16) + (128 << 4);
+ case V4L2_COLORSPACE_SMPTE240M:
+ return ((-6684 * r - 22100 * g + 28784 * b + 16 * 32768) >> 16) + (128 << 4);
+ case V4L2_COLORSPACE_REC709:
+ case V4L2_COLORSPACE_SRGB:
+ default:
+ return ((-6596 * r - 22189 * g + 28784 * b + 16 * 32768) >> 16) + (128 << 4);
+ }
+}
+
+static u16 color_to_cr(struct tpg_data *tpg, int r, int g, int b)
+{
+ switch (tpg->colorspace) {
+ case V4L2_COLORSPACE_SMPTE170M:
+ case V4L2_COLORSPACE_470_SYSTEM_M:
+ case V4L2_COLORSPACE_470_SYSTEM_BG:
+ return ((28784 * r - 24103 * g - 4681 * b + 16 * 32768) >> 16) + (128 << 4);
+ case V4L2_COLORSPACE_SMPTE240M:
+ return ((28784 * r - 25606 * g - 3178 * b + 16 * 32768) >> 16) + (128 << 4);
+ case V4L2_COLORSPACE_REC709:
+ case V4L2_COLORSPACE_SRGB:
+ default:
+ return ((28784 * r - 26145 * g - 2639 * b + 16 * 32768) >> 16) + (128 << 4);
+ }
+}
+
+static u16 ycbcr_to_r(struct tpg_data *tpg, int y, int cb, int cr)
+{
+ int r;
+
+ y -= 16 << 4;
+ cb -= 128 << 4;
+ cr -= 128 << 4;
+ switch (tpg->colorspace) {
+ case V4L2_COLORSPACE_SMPTE170M:
+ case V4L2_COLORSPACE_470_SYSTEM_M:
+ case V4L2_COLORSPACE_470_SYSTEM_BG:
+ r = 4769 * y + 6537 * cr;
+ break;
+ case V4L2_COLORSPACE_SMPTE240M:
+ r = 4769 * y + 7376 * cr;
+ break;
+ case V4L2_COLORSPACE_REC709:
+ case V4L2_COLORSPACE_SRGB:
+ default:
+ r = 4769 * y + 7343 * cr;
+ break;
+ }
+ return clamp(r >> 12, 0, 0xff0);
+}
+
+static u16 ycbcr_to_g(struct tpg_data *tpg, int y, int cb, int cr)
+{
+ int g;
+
+ y -= 16 << 4;
+ cb -= 128 << 4;
+ cr -= 128 << 4;
+ switch (tpg->colorspace) {
+ case V4L2_COLORSPACE_SMPTE170M:
+ case V4L2_COLORSPACE_470_SYSTEM_M:
+ case V4L2_COLORSPACE_470_SYSTEM_BG:
+ g = 4769 * y - 1605 * cb - 3330 * cr;
+ break;
+ case V4L2_COLORSPACE_SMPTE240M:
+ g = 4769 * y - 1055 * cb - 2341 * cr;
+ break;
+ case V4L2_COLORSPACE_REC709:
+ case V4L2_COLORSPACE_SRGB:
+ default:
+ g = 4769 * y - 873 * cb - 2183 * cr;
+ break;
+ }
+ return clamp(g >> 12, 0, 0xff0);
+}
+
+static u16 ycbcr_to_b(struct tpg_data *tpg, int y, int cb, int cr)
+{
+ int b;
+
+ y -= 16 << 4;
+ cb -= 128 << 4;
+ cr -= 128 << 4;
+ switch (tpg->colorspace) {
+ case V4L2_COLORSPACE_SMPTE170M:
+ case V4L2_COLORSPACE_470_SYSTEM_M:
+ case V4L2_COLORSPACE_470_SYSTEM_BG:
+ b = 4769 * y + 7343 * cb;
+ break;
+ case V4L2_COLORSPACE_SMPTE240M:
+ b = 4769 * y + 8552 * cb;
+ break;
+ case V4L2_COLORSPACE_REC709:
+ case V4L2_COLORSPACE_SRGB:
+ default:
+ b = 4769 * y + 8652 * cb;
+ break;
+ }
+ return clamp(b >> 12, 0, 0xff0);
+}
+
+/* precalculate color bar values to speed up rendering */
+static void precalculate_color(struct tpg_data *tpg, int k)
+{
+ int col = k;
+ int r = tpg_colors[col].r;
+ int g = tpg_colors[col].g;
+ int b = tpg_colors[col].b;
+
+ if (k == TPG_COLOR_TEXTBG) {
+ col = tpg_get_textbg_color(tpg);
+
+ r = tpg_colors[col].r;
+ g = tpg_colors[col].g;
+ b = tpg_colors[col].b;
+ } else if (k == TPG_COLOR_TEXTFG) {
+ col = tpg_get_textfg_color(tpg);
+
+ r = tpg_colors[col].r;
+ g = tpg_colors[col].g;
+ b = tpg_colors[col].b;
+ } else if (tpg->pattern == TPG_PAT_NOISE) {
+ r = g = b = prandom_u32_max(256);
+ } else if (k == TPG_COLOR_RANDOM) {
+ r = g = b = tpg->qual_offset + prandom_u32_max(196);
+ } else if (k >= TPG_COLOR_RAMP) {
+ r = g = b = k - TPG_COLOR_RAMP;
+ }
+
+ if (tpg->pattern == TPG_PAT_CSC_COLORBAR && col <= TPG_COLOR_CSC_BLACK) {
+ r = tpg_csc_colors[tpg->colorspace][col].r;
+ g = tpg_csc_colors[tpg->colorspace][col].g;
+ b = tpg_csc_colors[tpg->colorspace][col].b;
+ } else {
+ r <<= 4;
+ g <<= 4;
+ b <<= 4;
+ }
+ if (tpg->qual == TPG_QUAL_GRAY)
+ r = g = b = color_to_y(tpg, r, g, b);
+
+ /*
+ * The assumption is that the RGB output is always full range,
+ * so only if the rgb_range overrides the 'real' rgb range do
+ * we need to convert the RGB values.
+ *
+ * Currently there is no way of signalling to userspace if you
+ * are actually giving it limited range RGB (or full range
+ * YUV for that matter).
+ *
+ * Remember that r, g and b are still in the 0 - 0xff0 range.
+ */
+ if (tpg->real_rgb_range == V4L2_DV_RGB_RANGE_LIMITED &&
+ tpg->rgb_range == V4L2_DV_RGB_RANGE_FULL) {
+ /*
+ * Convert from full range (which is what r, g and b are)
+ * to limited range (which is the 'real' RGB range), which
+ * is then interpreted as full range.
+ */
+ r = (r * 219) / 255 + (16 << 4);
+ g = (g * 219) / 255 + (16 << 4);
+ b = (b * 219) / 255 + (16 << 4);
+ } else if (tpg->real_rgb_range != V4L2_DV_RGB_RANGE_LIMITED &&
+ tpg->rgb_range == V4L2_DV_RGB_RANGE_LIMITED) {
+ /*
+ * Clamp r, g and b to the limited range and convert to full
+ * range since that's what we deliver.
+ */
+ r = clamp(r, 16 << 4, 235 << 4);
+ g = clamp(g, 16 << 4, 235 << 4);
+ b = clamp(b, 16 << 4, 235 << 4);
+ r = (r - (16 << 4)) * 255 / 219;
+ g = (g - (16 << 4)) * 255 / 219;
+ b = (b - (16 << 4)) * 255 / 219;
+ }
+
+ if (tpg->brightness != 128 || tpg->contrast != 128 ||
+ tpg->saturation != 128 || tpg->hue) {
+ /* Implement these operations */
+
+ /* First convert to YCbCr */
+ int y = color_to_y(tpg, r, g, b); /* Luma */
+ int cb = color_to_cb(tpg, r, g, b); /* Cb */
+ int cr = color_to_cr(tpg, r, g, b); /* Cr */
+ int tmp_cb, tmp_cr;
+
+ y = (16 << 4) + ((y - (16 << 4)) * tpg->contrast) / 128;
+ y += (tpg->brightness << 4) - (128 << 4);
+
+ cb -= 128 << 4;
+ cr -= 128 << 4;
+ tmp_cb = (cb * cos(128 + tpg->hue)) / 127 + (cr * sin[128 + tpg->hue]) / 127;
+ tmp_cr = (cr * cos(128 + tpg->hue)) / 127 - (cb * sin[128 + tpg->hue]) / 127;
+
+ cb = (128 << 4) + (tmp_cb * tpg->contrast * tpg->saturation) / (128 * 128);
+ cr = (128 << 4) + (tmp_cr * tpg->contrast * tpg->saturation) / (128 * 128);
+ if (tpg->is_yuv) {
+ tpg->colors[k][0] = clamp(y >> 4, 1, 254);
+ tpg->colors[k][1] = clamp(cb >> 4, 1, 254);
+ tpg->colors[k][2] = clamp(cr >> 4, 1, 254);
+ return;
+ }
+ r = ycbcr_to_r(tpg, y, cb, cr);
+ g = ycbcr_to_g(tpg, y, cb, cr);
+ b = ycbcr_to_b(tpg, y, cb, cr);
+ }
+
+ if (tpg->is_yuv) {
+ /* Convert to YCbCr */
+ u16 y = color_to_y(tpg, r, g, b); /* Luma */
+ u16 cb = color_to_cb(tpg, r, g, b); /* Cb */
+ u16 cr = color_to_cr(tpg, r, g, b); /* Cr */
+
+ tpg->colors[k][0] = clamp(y >> 4, 1, 254);
+ tpg->colors[k][1] = clamp(cb >> 4, 1, 254);
+ tpg->colors[k][2] = clamp(cr >> 4, 1, 254);
+ } else {
+ switch (tpg->fourcc) {
+ case V4L2_PIX_FMT_RGB565:
+ case V4L2_PIX_FMT_RGB565X:
+ r >>= 7;
+ g >>= 6;
+ b >>= 7;
+ break;
+ case V4L2_PIX_FMT_RGB555:
+ case V4L2_PIX_FMT_XRGB555:
+ case V4L2_PIX_FMT_ARGB555:
+ case V4L2_PIX_FMT_RGB555X:
+ r >>= 7;
+ g >>= 7;
+ b >>= 7;
+ break;
+ default:
+ r >>= 4;
+ g >>= 4;
+ b >>= 4;
+ break;
+ }
+
+ tpg->colors[k][0] = r;
+ tpg->colors[k][1] = g;
+ tpg->colors[k][2] = b;
+ }
+}
+
+static void tpg_precalculate_colors(struct tpg_data *tpg)
+{
+ int k;
+
+ for (k = 0; k < TPG_COLOR_MAX; k++)
+ precalculate_color(tpg, k);
+}
+
+/* 'odd' is true for pixels 1, 3, 5, etc. and false for pixels 0, 2, 4, etc. */
+static void gen_twopix(struct tpg_data *tpg,
+ u8 buf[TPG_MAX_PLANES][8], int color, bool odd)
+{
+ unsigned offset = odd * tpg->twopixelsize[0] / 2;
+ u8 alpha = tpg->alpha_component;
+ u8 r_y, g_u, b_v;
+
+ if (tpg->alpha_red_only && color != TPG_COLOR_CSC_RED &&
+ color != TPG_COLOR_100_RED &&
+ color != TPG_COLOR_75_RED)
+ alpha = 0;
+ if (color == TPG_COLOR_RANDOM)
+ precalculate_color(tpg, color);
+ r_y = tpg->colors[color][0]; /* R or precalculated Y */
+ g_u = tpg->colors[color][1]; /* G or precalculated U */
+ b_v = tpg->colors[color][2]; /* B or precalculated V */
+
+ switch (tpg->fourcc) {
+ case V4L2_PIX_FMT_NV16M:
+ buf[0][offset] = r_y;
+ buf[1][offset] = odd ? b_v : g_u;
+ break;
+ case V4L2_PIX_FMT_NV61M:
+ buf[0][offset] = r_y;
+ buf[1][offset] = odd ? g_u : b_v;
+ break;
+
+ case V4L2_PIX_FMT_YUYV:
+ buf[0][offset] = r_y;
+ buf[0][offset + 1] = odd ? b_v : g_u;
+ break;
+ case V4L2_PIX_FMT_UYVY:
+ buf[0][offset] = odd ? b_v : g_u;
+ buf[0][offset + 1] = r_y;
+ break;
+ case V4L2_PIX_FMT_YVYU:
+ buf[0][offset] = r_y;
+ buf[0][offset + 1] = odd ? g_u : b_v;
+ break;
+ case V4L2_PIX_FMT_VYUY:
+ buf[0][offset] = odd ? g_u : b_v;
+ buf[0][offset + 1] = r_y;
+ break;
+ case V4L2_PIX_FMT_RGB565:
+ buf[0][offset] = (g_u << 5) | b_v;
+ buf[0][offset + 1] = (r_y << 3) | (g_u >> 3);
+ break;
+ case V4L2_PIX_FMT_RGB565X:
+ buf[0][offset] = (r_y << 3) | (g_u >> 3);
+ buf[0][offset + 1] = (g_u << 5) | b_v;
+ break;
+ case V4L2_PIX_FMT_RGB555:
+ case V4L2_PIX_FMT_XRGB555:
+ alpha = 0;
+ /* fall through */
+ case V4L2_PIX_FMT_ARGB555:
+ buf[0][offset] = (g_u << 5) | b_v;
+ buf[0][offset + 1] = (alpha & 0x80) | (r_y << 2) | (g_u >> 3);
+ break;
+ case V4L2_PIX_FMT_RGB555X:
+ buf[0][offset] = (alpha & 0x80) | (r_y << 2) | (g_u >> 3);
+ buf[0][offset + 1] = (g_u << 5) | b_v;
+ break;
+ case V4L2_PIX_FMT_RGB24:
+ buf[0][offset] = r_y;
+ buf[0][offset + 1] = g_u;
+ buf[0][offset + 2] = b_v;
+ break;
+ case V4L2_PIX_FMT_BGR24:
+ buf[0][offset] = b_v;
+ buf[0][offset + 1] = g_u;
+ buf[0][offset + 2] = r_y;
+ break;
+ case V4L2_PIX_FMT_RGB32:
+ case V4L2_PIX_FMT_XRGB32:
+ alpha = 0;
+ /* fall through */
+ case V4L2_PIX_FMT_ARGB32:
+ buf[0][offset] = alpha;
+ buf[0][offset + 1] = r_y;
+ buf[0][offset + 2] = g_u;
+ buf[0][offset + 3] = b_v;
+ break;
+ case V4L2_PIX_FMT_BGR32:
+ case V4L2_PIX_FMT_XBGR32:
+ alpha = 0;
+ /* fall through */
+ case V4L2_PIX_FMT_ABGR32:
+ buf[0][offset] = b_v;
+ buf[0][offset + 1] = g_u;
+ buf[0][offset + 2] = r_y;
+ buf[0][offset + 3] = alpha;
+ break;
+ }
+}
+
+/* Return how many pattern lines are used by the current pattern. */
+static unsigned tpg_get_pat_lines(struct tpg_data *tpg)
+{
+ switch (tpg->pattern) {
+ case TPG_PAT_CHECKERS_16X16:
+ case TPG_PAT_CHECKERS_1X1:
+ case TPG_PAT_ALTERNATING_HLINES:
+ case TPG_PAT_CROSS_1_PIXEL:
+ case TPG_PAT_CROSS_2_PIXELS:
+ case TPG_PAT_CROSS_10_PIXELS:
+ return 2;
+ case TPG_PAT_100_COLORSQUARES:
+ case TPG_PAT_100_HCOLORBAR:
+ return 8;
+ default:
+ return 1;
+ }
+}
+
+/* Which pattern line should be used for the given frame line. */
+static unsigned tpg_get_pat_line(struct tpg_data *tpg, unsigned line)
+{
+ switch (tpg->pattern) {
+ case TPG_PAT_CHECKERS_16X16:
+ return (line >> 4) & 1;
+ case TPG_PAT_CHECKERS_1X1:
+ case TPG_PAT_ALTERNATING_HLINES:
+ return line & 1;
+ case TPG_PAT_100_COLORSQUARES:
+ case TPG_PAT_100_HCOLORBAR:
+ return (line * 8) / tpg->src_height;
+ case TPG_PAT_CROSS_1_PIXEL:
+ return line == tpg->src_height / 2;
+ case TPG_PAT_CROSS_2_PIXELS:
+ return (line + 1) / 2 == tpg->src_height / 4;
+ case TPG_PAT_CROSS_10_PIXELS:
+ return (line + 10) / 20 == tpg->src_height / 40;
+ default:
+ return 0;
+ }
+}
+
+/*
+ * Which color should be used for the given pattern line and X coordinate.
+ * Note: x is in the range 0 to 2 * tpg->src_width.
+ */
+static enum tpg_color tpg_get_color(struct tpg_data *tpg, unsigned pat_line, unsigned x)
+{
+ /* Maximum number of bars are TPG_COLOR_MAX - otherwise, the input print code
+ should be modified */
+ static const enum tpg_color bars[3][8] = {
+ /* Standard ITU-R 75% color bar sequence */
+ { TPG_COLOR_CSC_WHITE, TPG_COLOR_75_YELLOW,
+ TPG_COLOR_75_CYAN, TPG_COLOR_75_GREEN,
+ TPG_COLOR_75_MAGENTA, TPG_COLOR_75_RED,
+ TPG_COLOR_75_BLUE, TPG_COLOR_100_BLACK, },
+ /* Standard ITU-R 100% color bar sequence */
+ { TPG_COLOR_100_WHITE, TPG_COLOR_100_YELLOW,
+ TPG_COLOR_100_CYAN, TPG_COLOR_100_GREEN,
+ TPG_COLOR_100_MAGENTA, TPG_COLOR_100_RED,
+ TPG_COLOR_100_BLUE, TPG_COLOR_100_BLACK, },
+ /* Color bar sequence suitable to test CSC */
+ { TPG_COLOR_CSC_WHITE, TPG_COLOR_CSC_YELLOW,
+ TPG_COLOR_CSC_CYAN, TPG_COLOR_CSC_GREEN,
+ TPG_COLOR_CSC_MAGENTA, TPG_COLOR_CSC_RED,
+ TPG_COLOR_CSC_BLUE, TPG_COLOR_CSC_BLACK, },
+ };
+
+ switch (tpg->pattern) {
+ case TPG_PAT_75_COLORBAR:
+ case TPG_PAT_100_COLORBAR:
+ case TPG_PAT_CSC_COLORBAR:
+ return bars[tpg->pattern][((x * 8) / tpg->src_width) % 8];
+ case TPG_PAT_100_COLORSQUARES:
+ return bars[1][(pat_line + (x * 8) / tpg->src_width) % 8];
+ case TPG_PAT_100_HCOLORBAR:
+ return bars[1][pat_line];
+ case TPG_PAT_BLACK:
+ return TPG_COLOR_100_BLACK;
+ case TPG_PAT_WHITE:
+ return TPG_COLOR_100_WHITE;
+ case TPG_PAT_RED:
+ return TPG_COLOR_100_RED;
+ case TPG_PAT_GREEN:
+ return TPG_COLOR_100_GREEN;
+ case TPG_PAT_BLUE:
+ return TPG_COLOR_100_BLUE;
+ case TPG_PAT_CHECKERS_16X16:
+ return (((x >> 4) & 1) ^ (pat_line & 1)) ?
+ TPG_COLOR_100_BLACK : TPG_COLOR_100_WHITE;
+ case TPG_PAT_CHECKERS_1X1:
+ return ((x & 1) ^ (pat_line & 1)) ?
+ TPG_COLOR_100_WHITE : TPG_COLOR_100_BLACK;
+ case TPG_PAT_ALTERNATING_HLINES:
+ return pat_line ? TPG_COLOR_100_WHITE : TPG_COLOR_100_BLACK;
+ case TPG_PAT_ALTERNATING_VLINES:
+ return (x & 1) ? TPG_COLOR_100_WHITE : TPG_COLOR_100_BLACK;
+ case TPG_PAT_CROSS_1_PIXEL:
+ if (pat_line || (x % tpg->src_width) == tpg->src_width / 2)
+ return TPG_COLOR_100_BLACK;
+ return TPG_COLOR_100_WHITE;
+ case TPG_PAT_CROSS_2_PIXELS:
+ if (pat_line || ((x % tpg->src_width) + 1) / 2 == tpg->src_width / 4)
+ return TPG_COLOR_100_BLACK;
+ return TPG_COLOR_100_WHITE;
+ case TPG_PAT_CROSS_10_PIXELS:
+ if (pat_line || ((x % tpg->src_width) + 10) / 20 == tpg->src_width / 40)
+ return TPG_COLOR_100_BLACK;
+ return TPG_COLOR_100_WHITE;
+ case TPG_PAT_GRAY_RAMP:
+ return TPG_COLOR_RAMP + ((x % tpg->src_width) * 256) / tpg->src_width;
+ default:
+ return TPG_COLOR_100_RED;
+ }
+}
+
+/*
+ * Given the pixel aspect ratio and video aspect ratio calculate the
+ * coordinates of a centered square and the coordinates of the border of
+ * the active video area. The coordinates are relative to the source
+ * frame rectangle.
+ */
+static void tpg_calculate_square_border(struct tpg_data *tpg)
+{
+ unsigned w = tpg->src_width;
+ unsigned h = tpg->src_height;
+ unsigned sq_w, sq_h;
+
+ sq_w = (w * 2 / 5) & ~1;
+ if (((w - sq_w) / 2) & 1)
+ sq_w += 2;
+ sq_h = sq_w;
+ tpg->square.width = sq_w;
+ if (tpg->vid_aspect == TPG_VIDEO_ASPECT_16X9_ANAMORPHIC) {
+ unsigned ana_sq_w = (sq_w / 4) * 3;
+
+ if (((w - ana_sq_w) / 2) & 1)
+ ana_sq_w += 2;
+ tpg->square.width = ana_sq_w;
+ }
+ tpg->square.left = (w - tpg->square.width) / 2;
+ if (tpg->pix_aspect == TPG_PIXEL_ASPECT_NTSC)
+ sq_h = sq_w * 10 / 11;
+ else if (tpg->pix_aspect == TPG_PIXEL_ASPECT_PAL)
+ sq_h = sq_w * 59 / 54;
+ tpg->square.height = sq_h;
+ tpg->square.top = (h - sq_h) / 2;
+ tpg->border.left = 0;
+ tpg->border.width = w;
+ tpg->border.top = 0;
+ tpg->border.height = h;
+ switch (tpg->vid_aspect) {
+ case TPG_VIDEO_ASPECT_4X3:
+ if (tpg->pix_aspect)
+ return;
+ if (3 * w >= 4 * h) {
+ tpg->border.width = ((4 * h) / 3) & ~1;
+ if (((w - tpg->border.width) / 2) & ~1)
+ tpg->border.width -= 2;
+ tpg->border.left = (w - tpg->border.width) / 2;
+ break;
+ }
+ tpg->border.height = ((3 * w) / 4) & ~1;
+ tpg->border.top = (h - tpg->border.height) / 2;
+ break;
+ case TPG_VIDEO_ASPECT_14X9_CENTRE:
+ if (tpg->pix_aspect) {
+ tpg->border.height = tpg->pix_aspect == TPG_PIXEL_ASPECT_NTSC ? 420 : 506;
+ tpg->border.top = (h - tpg->border.height) / 2;
+ break;
+ }
+ if (9 * w >= 14 * h) {
+ tpg->border.width = ((14 * h) / 9) & ~1;
+ if (((w - tpg->border.width) / 2) & ~1)
+ tpg->border.width -= 2;
+ tpg->border.left = (w - tpg->border.width) / 2;
+ break;
+ }
+ tpg->border.height = ((9 * w) / 14) & ~1;
+ tpg->border.top = (h - tpg->border.height) / 2;
+ break;
+ case TPG_VIDEO_ASPECT_16X9_CENTRE:
+ if (tpg->pix_aspect) {
+ tpg->border.height = tpg->pix_aspect == TPG_PIXEL_ASPECT_NTSC ? 368 : 442;
+ tpg->border.top = (h - tpg->border.height) / 2;
+ break;
+ }
+ if (9 * w >= 16 * h) {
+ tpg->border.width = ((16 * h) / 9) & ~1;
+ if (((w - tpg->border.width) / 2) & ~1)
+ tpg->border.width -= 2;
+ tpg->border.left = (w - tpg->border.width) / 2;
+ break;
+ }
+ tpg->border.height = ((9 * w) / 16) & ~1;
+ tpg->border.top = (h - tpg->border.height) / 2;
+ break;
+ default:
+ break;
+ }
+}
+
+static void tpg_precalculate_line(struct tpg_data *tpg)
+{
+ enum tpg_color contrast;
+ unsigned pat;
+ unsigned p;
+ unsigned x;
+
+ switch (tpg->pattern) {
+ case TPG_PAT_GREEN:
+ contrast = TPG_COLOR_100_RED;
+ break;
+ case TPG_PAT_CSC_COLORBAR:
+ contrast = TPG_COLOR_CSC_GREEN;
+ break;
+ default:
+ contrast = TPG_COLOR_100_GREEN;
+ break;
+ }
+
+ for (pat = 0; pat < tpg_get_pat_lines(tpg); pat++) {
+ /* Coarse scaling with Bresenham */
+ unsigned int_part = tpg->src_width / tpg->scaled_width;
+ unsigned fract_part = tpg->src_width % tpg->scaled_width;
+ unsigned src_x = 0;
+ unsigned error = 0;
+
+ for (x = 0; x < tpg->scaled_width * 2; x += 2) {
+ unsigned real_x = src_x;
+ enum tpg_color color1, color2;
+ u8 pix[TPG_MAX_PLANES][8];
+
+ real_x = tpg->hflip ? tpg->src_width * 2 - real_x - 2 : real_x;
+ color1 = tpg_get_color(tpg, pat, real_x);
+
+ src_x += int_part;
+ error += fract_part;
+ if (error >= tpg->scaled_width) {
+ error -= tpg->scaled_width;
+ src_x++;
+ }
+
+ real_x = src_x;
+ real_x = tpg->hflip ? tpg->src_width * 2 - real_x - 2 : real_x;
+ color2 = tpg_get_color(tpg, pat, real_x);
+
+ src_x += int_part;
+ error += fract_part;
+ if (error >= tpg->scaled_width) {
+ error -= tpg->scaled_width;
+ src_x++;
+ }
+
+ gen_twopix(tpg, pix, tpg->hflip ? color2 : color1, 0);
+ gen_twopix(tpg, pix, tpg->hflip ? color1 : color2, 1);
+ for (p = 0; p < tpg->planes; p++) {
+ unsigned twopixsize = tpg->twopixelsize[p];
+ u8 *pos = tpg->lines[pat][p] + x * twopixsize / 2;
+
+ memcpy(pos, pix[p], twopixsize);
+ }
+ }
+ }
+ for (x = 0; x < tpg->scaled_width; x += 2) {
+ u8 pix[TPG_MAX_PLANES][8];
+
+ gen_twopix(tpg, pix, contrast, 0);
+ gen_twopix(tpg, pix, contrast, 1);
+ for (p = 0; p < tpg->planes; p++) {
+ unsigned twopixsize = tpg->twopixelsize[p];
+ u8 *pos = tpg->contrast_line[p] + x * twopixsize / 2;
+
+ memcpy(pos, pix[p], twopixsize);
+ }
+ }
+ for (x = 0; x < tpg->scaled_width; x += 2) {
+ u8 pix[TPG_MAX_PLANES][8];
+
+ gen_twopix(tpg, pix, TPG_COLOR_100_BLACK, 0);
+ gen_twopix(tpg, pix, TPG_COLOR_100_BLACK, 1);
+ for (p = 0; p < tpg->planes; p++) {
+ unsigned twopixsize = tpg->twopixelsize[p];
+ u8 *pos = tpg->black_line[p] + x * twopixsize / 2;
+
+ memcpy(pos, pix[p], twopixsize);
+ }
+ }
+ for (x = 0; x < tpg->scaled_width * 2; x += 2) {
+ u8 pix[TPG_MAX_PLANES][8];
+
+ gen_twopix(tpg, pix, TPG_COLOR_RANDOM, 0);
+ gen_twopix(tpg, pix, TPG_COLOR_RANDOM, 1);
+ for (p = 0; p < tpg->planes; p++) {
+ unsigned twopixsize = tpg->twopixelsize[p];
+ u8 *pos = tpg->random_line[p] + x * twopixsize / 2;
+
+ memcpy(pos, pix[p], twopixsize);
+ }
+ }
+ gen_twopix(tpg, tpg->textbg, TPG_COLOR_TEXTBG, 0);
+ gen_twopix(tpg, tpg->textbg, TPG_COLOR_TEXTBG, 1);
+ gen_twopix(tpg, tpg->textfg, TPG_COLOR_TEXTFG, 0);
+ gen_twopix(tpg, tpg->textfg, TPG_COLOR_TEXTFG, 1);
+}
+
+/* need this to do rgb24 rendering */
+typedef struct { u16 __; u8 _; } __packed x24;
+
+void tpg_gen_text(struct tpg_data *tpg, u8 *basep[TPG_MAX_PLANES][2],
+ int y, int x, char *text)
+{
+ int line;
+ unsigned step = V4L2_FIELD_HAS_T_OR_B(tpg->field) ? 2 : 1;
+ unsigned div = step;
+ unsigned first = 0;
+ unsigned len = strlen(text);
+ unsigned p;
+
+ if (font8x16 == NULL || basep == NULL)
+ return;
+
+ /* Checks if it is possible to show string */
+ if (y + 16 >= tpg->compose.height || x + 8 >= tpg->compose.width)
+ return;
+
+ if (len > (tpg->compose.width - x) / 8)
+ len = (tpg->compose.width - x) / 8;
+ if (tpg->vflip)
+ y = tpg->compose.height - y - 16;
+ if (tpg->hflip)
+ x = tpg->compose.width - x - 8;
+ y += tpg->compose.top;
+ x += tpg->compose.left;
+ if (tpg->field == V4L2_FIELD_BOTTOM)
+ first = 1;
+ else if (tpg->field == V4L2_FIELD_SEQ_TB || tpg->field == V4L2_FIELD_SEQ_BT)
+ div = 2;
+
+ for (p = 0; p < tpg->planes; p++) {
+ /* Print stream time */
+#define PRINTSTR(PIXTYPE) do { \
+ PIXTYPE fg; \
+ PIXTYPE bg; \
+ memcpy(&fg, tpg->textfg[p], sizeof(PIXTYPE)); \
+ memcpy(&bg, tpg->textbg[p], sizeof(PIXTYPE)); \
+ \
+ for (line = first; line < 16; line += step) { \
+ int l = tpg->vflip ? 15 - line : line; \
+ PIXTYPE *pos = (PIXTYPE *)(basep[p][line & 1] + \
+ ((y * step + l) / div) * tpg->bytesperline[p] + \
+ x * sizeof(PIXTYPE)); \
+ unsigned s; \
+ \
+ for (s = 0; s < len; s++) { \
+ u8 chr = font8x16[text[s] * 16 + line]; \
+ \
+ if (tpg->hflip) { \
+ pos[7] = (chr & (0x01 << 7) ? fg : bg); \
+ pos[6] = (chr & (0x01 << 6) ? fg : bg); \
+ pos[5] = (chr & (0x01 << 5) ? fg : bg); \
+ pos[4] = (chr & (0x01 << 4) ? fg : bg); \
+ pos[3] = (chr & (0x01 << 3) ? fg : bg); \
+ pos[2] = (chr & (0x01 << 2) ? fg : bg); \
+ pos[1] = (chr & (0x01 << 1) ? fg : bg); \
+ pos[0] = (chr & (0x01 << 0) ? fg : bg); \
+ } else { \
+ pos[0] = (chr & (0x01 << 7) ? fg : bg); \
+ pos[1] = (chr & (0x01 << 6) ? fg : bg); \
+ pos[2] = (chr & (0x01 << 5) ? fg : bg); \
+ pos[3] = (chr & (0x01 << 4) ? fg : bg); \
+ pos[4] = (chr & (0x01 << 3) ? fg : bg); \
+ pos[5] = (chr & (0x01 << 2) ? fg : bg); \
+ pos[6] = (chr & (0x01 << 1) ? fg : bg); \
+ pos[7] = (chr & (0x01 << 0) ? fg : bg); \
+ } \
+ \
+ pos += tpg->hflip ? -8 : 8; \
+ } \
+ } \
+} while (0)
+
+ switch (tpg->twopixelsize[p]) {
+ case 2:
+ PRINTSTR(u8); break;
+ case 4:
+ PRINTSTR(u16); break;
+ case 6:
+ PRINTSTR(x24); break;
+ case 8:
+ PRINTSTR(u32); break;
+ }
+ }
+}
+
+void tpg_update_mv_step(struct tpg_data *tpg)
+{
+ int factor = tpg->mv_hor_mode > TPG_MOVE_NONE ? -1 : 1;
+
+ if (tpg->hflip)
+ factor = -factor;
+ switch (tpg->mv_hor_mode) {
+ case TPG_MOVE_NEG_FAST:
+ case TPG_MOVE_POS_FAST:
+ tpg->mv_hor_step = ((tpg->src_width + 319) / 320) * 4;
+ break;
+ case TPG_MOVE_NEG:
+ case TPG_MOVE_POS:
+ tpg->mv_hor_step = ((tpg->src_width + 639) / 640) * 4;
+ break;
+ case TPG_MOVE_NEG_SLOW:
+ case TPG_MOVE_POS_SLOW:
+ tpg->mv_hor_step = 2;
+ break;
+ case TPG_MOVE_NONE:
+ tpg->mv_hor_step = 0;
+ break;
+ }
+ if (factor < 0)
+ tpg->mv_hor_step = tpg->src_width - tpg->mv_hor_step;
+
+ factor = tpg->mv_vert_mode > TPG_MOVE_NONE ? -1 : 1;
+ switch (tpg->mv_vert_mode) {
+ case TPG_MOVE_NEG_FAST:
+ case TPG_MOVE_POS_FAST:
+ tpg->mv_vert_step = ((tpg->src_width + 319) / 320) * 4;
+ break;
+ case TPG_MOVE_NEG:
+ case TPG_MOVE_POS:
+ tpg->mv_vert_step = ((tpg->src_width + 639) / 640) * 4;
+ break;
+ case TPG_MOVE_NEG_SLOW:
+ case TPG_MOVE_POS_SLOW:
+ tpg->mv_vert_step = 1;
+ break;
+ case TPG_MOVE_NONE:
+ tpg->mv_vert_step = 0;
+ break;
+ }
+ if (factor < 0)
+ tpg->mv_vert_step = tpg->src_height - tpg->mv_vert_step;
+}
+
+/* Map the line number relative to the crop rectangle to a frame line number */
+static unsigned tpg_calc_frameline(struct tpg_data *tpg, unsigned src_y,
+ unsigned field)
+{
+ switch (field) {
+ case V4L2_FIELD_TOP:
+ return tpg->crop.top + src_y * 2;
+ case V4L2_FIELD_BOTTOM:
+ return tpg->crop.top + src_y * 2 + 1;
+ default:
+ return src_y + tpg->crop.top;
+ }
+}
+
+/*
+ * Map the line number relative to the compose rectangle to a destination
+ * buffer line number.
+ */
+static unsigned tpg_calc_buffer_line(struct tpg_data *tpg, unsigned y,
+ unsigned field)
+{
+ y += tpg->compose.top;
+ switch (field) {
+ case V4L2_FIELD_SEQ_TB:
+ if (y & 1)
+ return tpg->buf_height / 2 + y / 2;
+ return y / 2;
+ case V4L2_FIELD_SEQ_BT:
+ if (y & 1)
+ return y / 2;
+ return tpg->buf_height / 2 + y / 2;
+ default:
+ return y;
+ }
+}
+
+static void tpg_recalc(struct tpg_data *tpg)
+{
+ if (tpg->recalc_colors) {
+ tpg->recalc_colors = false;
+ tpg->recalc_lines = true;
+ tpg_precalculate_colors(tpg);
+ }
+ if (tpg->recalc_square_border) {
+ tpg->recalc_square_border = false;
+ tpg_calculate_square_border(tpg);
+ }
+ if (tpg->recalc_lines) {
+ tpg->recalc_lines = false;
+ tpg_precalculate_line(tpg);
+ }
+}
+
+void tpg_calc_text_basep(struct tpg_data *tpg,
+ u8 *basep[TPG_MAX_PLANES][2], unsigned p, u8 *vbuf)
+{
+ unsigned stride = tpg->bytesperline[p];
+
+ tpg_recalc(tpg);
+
+ basep[p][0] = vbuf;
+ basep[p][1] = vbuf;
+ if (tpg->field == V4L2_FIELD_SEQ_TB)
+ basep[p][1] += tpg->buf_height * stride / 2;
+ else if (tpg->field == V4L2_FIELD_SEQ_BT)
+ basep[p][0] += tpg->buf_height * stride / 2;
+}
+
+void tpg_fillbuffer(struct tpg_data *tpg, v4l2_std_id std, unsigned p, u8 *vbuf)
+{
+ bool is_tv = std;
+ bool is_60hz = is_tv && (std & V4L2_STD_525_60);
+ unsigned mv_hor_old = tpg->mv_hor_count % tpg->src_width;
+ unsigned mv_hor_new = (tpg->mv_hor_count + tpg->mv_hor_step) % tpg->src_width;
+ unsigned mv_vert_old = tpg->mv_vert_count % tpg->src_height;
+ unsigned mv_vert_new = (tpg->mv_vert_count + tpg->mv_vert_step) % tpg->src_height;
+ unsigned wss_width;
+ unsigned f;
+ int hmax = (tpg->compose.height * tpg->perc_fill) / 100;
+ int h;
+ unsigned twopixsize = tpg->twopixelsize[p];
+ unsigned img_width = tpg->compose.width * twopixsize / 2;
+ unsigned line_offset;
+ unsigned left_pillar_width = 0;
+ unsigned right_pillar_start = img_width;
+ unsigned stride = tpg->bytesperline[p];
+ unsigned factor = V4L2_FIELD_HAS_T_OR_B(tpg->field) ? 2 : 1;
+ u8 *orig_vbuf = vbuf;
+
+ /* Coarse scaling with Bresenham */
+ unsigned int_part = (tpg->crop.height / factor) / tpg->compose.height;
+ unsigned fract_part = (tpg->crop.height / factor) % tpg->compose.height;
+ unsigned src_y = 0;
+ unsigned error = 0;
+
+ tpg_recalc(tpg);
+
+ mv_hor_old = (mv_hor_old * tpg->scaled_width / tpg->src_width) & ~1;
+ mv_hor_new = (mv_hor_new * tpg->scaled_width / tpg->src_width) & ~1;
+ wss_width = tpg->crop.left < tpg->src_width / 2 ?
+ tpg->src_width / 2 - tpg->crop.left : 0;
+ if (wss_width > tpg->crop.width)
+ wss_width = tpg->crop.width;
+ wss_width = wss_width * tpg->scaled_width / tpg->src_width;
+
+ vbuf += tpg->compose.left * twopixsize / 2;
+ line_offset = tpg->crop.left * tpg->scaled_width / tpg->src_width;
+ line_offset = (line_offset & ~1) * twopixsize / 2;
+ if (tpg->crop.left < tpg->border.left) {
+ left_pillar_width = tpg->border.left - tpg->crop.left;
+ if (left_pillar_width > tpg->crop.width)
+ left_pillar_width = tpg->crop.width;
+ left_pillar_width = (left_pillar_width * tpg->scaled_width) / tpg->src_width;
+ left_pillar_width = (left_pillar_width & ~1) * twopixsize / 2;
+ }
+ if (tpg->crop.left + tpg->crop.width > tpg->border.left + tpg->border.width) {
+ right_pillar_start = tpg->border.left + tpg->border.width - tpg->crop.left;
+ right_pillar_start = (right_pillar_start * tpg->scaled_width) / tpg->src_width;
+ right_pillar_start = (right_pillar_start & ~1) * twopixsize / 2;
+ if (right_pillar_start > img_width)
+ right_pillar_start = img_width;
+ }
+
+ f = tpg->field == (is_60hz ? V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM);
+
+ for (h = 0; h < tpg->compose.height; h++) {
+ bool even;
+ bool fill_blank = false;
+ unsigned frame_line;
+ unsigned buf_line;
+ unsigned pat_line_old;
+ unsigned pat_line_new;
+ u8 *linestart_older;
+ u8 *linestart_newer;
+ u8 *linestart_top;
+ u8 *linestart_bottom;
+
+ frame_line = tpg_calc_frameline(tpg, src_y, tpg->field);
+ even = !(frame_line & 1);
+ buf_line = tpg_calc_buffer_line(tpg, h, tpg->field);
+ src_y += int_part;
+ error += fract_part;
+ if (error >= tpg->compose.height) {
+ error -= tpg->compose.height;
+ src_y++;
+ }
+
+ if (h >= hmax) {
+ if (hmax == tpg->compose.height)
+ continue;
+ if (!tpg->perc_fill_blank)
+ continue;
+ fill_blank = true;
+ }
+
+ if (tpg->vflip)
+ frame_line = tpg->src_height - frame_line - 1;
+
+ if (fill_blank) {
+ linestart_older = tpg->contrast_line[p];
+ linestart_newer = tpg->contrast_line[p];
+ } else if (tpg->qual != TPG_QUAL_NOISE &&
+ (frame_line < tpg->border.top ||
+ frame_line >= tpg->border.top + tpg->border.height)) {
+ linestart_older = tpg->black_line[p];
+ linestart_newer = tpg->black_line[p];
+ } else if (tpg->pattern == TPG_PAT_NOISE || tpg->qual == TPG_QUAL_NOISE) {
+ linestart_older = tpg->random_line[p] +
+ twopixsize * prandom_u32_max(tpg->src_width / 2);
+ linestart_newer = tpg->random_line[p] +
+ twopixsize * prandom_u32_max(tpg->src_width / 2);
+ } else {
+ pat_line_old = tpg_get_pat_line(tpg,
+ (frame_line + mv_vert_old) % tpg->src_height);
+ pat_line_new = tpg_get_pat_line(tpg,
+ (frame_line + mv_vert_new) % tpg->src_height);
+ linestart_older = tpg->lines[pat_line_old][p] +
+ mv_hor_old * twopixsize / 2;
+ linestart_newer = tpg->lines[pat_line_new][p] +
+ mv_hor_new * twopixsize / 2;
+ linestart_older += line_offset;
+ linestart_newer += line_offset;
+ }
+ if (is_60hz) {
+ linestart_top = linestart_newer;
+ linestart_bottom = linestart_older;
+ } else {
+ linestart_top = linestart_older;
+ linestart_bottom = linestart_newer;
+ }
+
+ switch (tpg->field) {
+ case V4L2_FIELD_INTERLACED:
+ case V4L2_FIELD_INTERLACED_TB:
+ case V4L2_FIELD_SEQ_TB:
+ case V4L2_FIELD_SEQ_BT:
+ if (even)
+ memcpy(vbuf + buf_line * stride, linestart_top, img_width);
+ else
+ memcpy(vbuf + buf_line * stride, linestart_bottom, img_width);
+ break;
+ case V4L2_FIELD_INTERLACED_BT:
+ if (even)
+ memcpy(vbuf + buf_line * stride, linestart_bottom, img_width);
+ else
+ memcpy(vbuf + buf_line * stride, linestart_top, img_width);
+ break;
+ case V4L2_FIELD_TOP:
+ memcpy(vbuf + buf_line * stride, linestart_top, img_width);
+ break;
+ case V4L2_FIELD_BOTTOM:
+ memcpy(vbuf + buf_line * stride, linestart_bottom, img_width);
+ break;
+ case V4L2_FIELD_NONE:
+ default:
+ memcpy(vbuf + buf_line * stride, linestart_older, img_width);
+ break;
+ }
+
+ if (is_tv && !is_60hz && frame_line == 0 && wss_width) {
+ /*
+ * Replace the first half of the top line of a 50 Hz frame
+ * with random data to simulate a WSS signal.
+ */
+ u8 *wss = tpg->random_line[p] +
+ twopixsize * prandom_u32_max(tpg->src_width / 2);
+
+ memcpy(vbuf + buf_line * stride, wss, wss_width * twopixsize / 2);
+ }
+ }
+
+ vbuf = orig_vbuf;
+ vbuf += tpg->compose.left * twopixsize / 2;
+ src_y = 0;
+ error = 0;
+ for (h = 0; h < tpg->compose.height; h++) {
+ unsigned frame_line = tpg_calc_frameline(tpg, src_y, tpg->field);
+ unsigned buf_line = tpg_calc_buffer_line(tpg, h, tpg->field);
+ const struct v4l2_rect *sq = &tpg->square;
+ const struct v4l2_rect *b = &tpg->border;
+ const struct v4l2_rect *c = &tpg->crop;
+
+ src_y += int_part;
+ error += fract_part;
+ if (error >= tpg->compose.height) {
+ error -= tpg->compose.height;
+ src_y++;
+ }
+
+ if (tpg->show_border && frame_line >= b->top &&
+ frame_line < b->top + b->height) {
+ unsigned bottom = b->top + b->height - 1;
+ unsigned left = left_pillar_width;
+ unsigned right = right_pillar_start;
+
+ if (frame_line == b->top || frame_line == b->top + 1 ||
+ frame_line == bottom || frame_line == bottom - 1) {
+ memcpy(vbuf + buf_line * stride + left, tpg->contrast_line[p],
+ right - left);
+ } else {
+ if (b->left >= c->left &&
+ b->left < c->left + c->width)
+ memcpy(vbuf + buf_line * stride + left,
+ tpg->contrast_line[p], twopixsize);
+ if (b->left + b->width > c->left &&
+ b->left + b->width <= c->left + c->width)
+ memcpy(vbuf + buf_line * stride + right - twopixsize,
+ tpg->contrast_line[p], twopixsize);
+ }
+ }
+ if (tpg->qual != TPG_QUAL_NOISE && frame_line >= b->top &&
+ frame_line < b->top + b->height) {
+ memcpy(vbuf + buf_line * stride, tpg->black_line[p], left_pillar_width);
+ memcpy(vbuf + buf_line * stride + right_pillar_start, tpg->black_line[p],
+ img_width - right_pillar_start);
+ }
+ if (tpg->show_square && frame_line >= sq->top &&
+ frame_line < sq->top + sq->height &&
+ sq->left < c->left + c->width &&
+ sq->left + sq->width >= c->left) {
+ unsigned left = sq->left;
+ unsigned width = sq->width;
+
+ if (c->left > left) {
+ width -= c->left - left;
+ left = c->left;
+ }
+ if (c->left + c->width < left + width)
+ width -= left + width - c->left - c->width;
+ left -= c->left;
+ left = (left * tpg->scaled_width) / tpg->src_width;
+ left = (left & ~1) * twopixsize / 2;
+ width = (width * tpg->scaled_width) / tpg->src_width;
+ width = (width & ~1) * twopixsize / 2;
+ memcpy(vbuf + buf_line * stride + left, tpg->contrast_line[p], width);
+ }
+ if (tpg->insert_sav) {
+ unsigned offset = (tpg->compose.width / 6) * twopixsize;
+ u8 *p = vbuf + buf_line * stride + offset;
+ unsigned vact = 0, hact = 0;
+
+ p[0] = 0xff;
+ p[1] = 0;
+ p[2] = 0;
+ p[3] = 0x80 | (f << 6) | (vact << 5) | (hact << 4) |
+ ((hact ^ vact) << 3) |
+ ((hact ^ f) << 2) |
+ ((f ^ vact) << 1) |
+ (hact ^ vact ^ f);
+ }
+ if (tpg->insert_eav) {
+ unsigned offset = (tpg->compose.width / 6) * 2 * twopixsize;
+ u8 *p = vbuf + buf_line * stride + offset;
+ unsigned vact = 0, hact = 1;
+
+ p[0] = 0xff;
+ p[1] = 0;
+ p[2] = 0;
+ p[3] = 0x80 | (f << 6) | (vact << 5) | (hact << 4) |
+ ((hact ^ vact) << 3) |
+ ((hact ^ f) << 2) |
+ ((f ^ vact) << 1) |
+ (hact ^ vact ^ f);
+ }
+ }
+}
diff --git a/drivers/media/platform/vivid/vivid-tpg.h b/drivers/media/platform/vivid/vivid-tpg.h
new file mode 100644
index 000000000000..8ef3e52ba3be
--- /dev/null
+++ b/drivers/media/platform/vivid/vivid-tpg.h
@@ -0,0 +1,439 @@
+/*
+ * vivid-tpg.h - Test Pattern Generator
+ *
+ * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef _VIVID_TPG_H_
+#define _VIVID_TPG_H_
+
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/random.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/videodev2.h>
+
+#include "vivid-tpg-colors.h"
+
+enum tpg_pattern {
+ TPG_PAT_75_COLORBAR,
+ TPG_PAT_100_COLORBAR,
+ TPG_PAT_CSC_COLORBAR,
+ TPG_PAT_100_HCOLORBAR,
+ TPG_PAT_100_COLORSQUARES,
+ TPG_PAT_BLACK,
+ TPG_PAT_WHITE,
+ TPG_PAT_RED,
+ TPG_PAT_GREEN,
+ TPG_PAT_BLUE,
+ TPG_PAT_CHECKERS_16X16,
+ TPG_PAT_CHECKERS_1X1,
+ TPG_PAT_ALTERNATING_HLINES,
+ TPG_PAT_ALTERNATING_VLINES,
+ TPG_PAT_CROSS_1_PIXEL,
+ TPG_PAT_CROSS_2_PIXELS,
+ TPG_PAT_CROSS_10_PIXELS,
+ TPG_PAT_GRAY_RAMP,
+
+ /* Must be the last pattern */
+ TPG_PAT_NOISE,
+};
+
+extern const char * const tpg_pattern_strings[];
+
+enum tpg_quality {
+ TPG_QUAL_COLOR,
+ TPG_QUAL_GRAY,
+ TPG_QUAL_NOISE
+};
+
+enum tpg_video_aspect {
+ TPG_VIDEO_ASPECT_IMAGE,
+ TPG_VIDEO_ASPECT_4X3,
+ TPG_VIDEO_ASPECT_14X9_CENTRE,
+ TPG_VIDEO_ASPECT_16X9_CENTRE,
+ TPG_VIDEO_ASPECT_16X9_ANAMORPHIC,
+};
+
+enum tpg_pixel_aspect {
+ TPG_PIXEL_ASPECT_SQUARE,
+ TPG_PIXEL_ASPECT_NTSC,
+ TPG_PIXEL_ASPECT_PAL,
+};
+
+enum tpg_move_mode {
+ TPG_MOVE_NEG_FAST,
+ TPG_MOVE_NEG,
+ TPG_MOVE_NEG_SLOW,
+ TPG_MOVE_NONE,
+ TPG_MOVE_POS_SLOW,
+ TPG_MOVE_POS,
+ TPG_MOVE_POS_FAST,
+};
+
+extern const char * const tpg_aspect_strings[];
+
+#define TPG_MAX_PLANES 2
+#define TPG_MAX_PAT_LINES 8
+
+struct tpg_data {
+ /* Source frame size */
+ unsigned src_width, src_height;
+ /* Buffer height */
+ unsigned buf_height;
+ /* Scaled output frame size */
+ unsigned scaled_width;
+ u32 field;
+ /* crop coordinates are frame-based */
+ struct v4l2_rect crop;
+ /* compose coordinates are format-based */
+ struct v4l2_rect compose;
+ /* border and square coordinates are frame-based */
+ struct v4l2_rect border;
+ struct v4l2_rect square;
+
+ /* Color-related fields */
+ enum tpg_quality qual;
+ unsigned qual_offset;
+ u8 alpha_component;
+ bool alpha_red_only;
+ u8 brightness;
+ u8 contrast;
+ u8 saturation;
+ s16 hue;
+ u32 fourcc;
+ bool is_yuv;
+ u32 colorspace;
+ enum tpg_video_aspect vid_aspect;
+ enum tpg_pixel_aspect pix_aspect;
+ unsigned rgb_range;
+ unsigned real_rgb_range;
+ unsigned planes;
+ /* Used to store the colors in native format, either RGB or YUV */
+ u8 colors[TPG_COLOR_MAX][3];
+ u8 textfg[TPG_MAX_PLANES][8], textbg[TPG_MAX_PLANES][8];
+ /* size in bytes for two pixels in each plane */
+ unsigned twopixelsize[TPG_MAX_PLANES];
+ unsigned bytesperline[TPG_MAX_PLANES];
+
+ /* Configuration */
+ enum tpg_pattern pattern;
+ bool hflip;
+ bool vflip;
+ unsigned perc_fill;
+ bool perc_fill_blank;
+ bool show_border;
+ bool show_square;
+ bool insert_sav;
+ bool insert_eav;
+
+ /* Test pattern movement */
+ enum tpg_move_mode mv_hor_mode;
+ int mv_hor_count;
+ int mv_hor_step;
+ enum tpg_move_mode mv_vert_mode;
+ int mv_vert_count;
+ int mv_vert_step;
+
+ bool recalc_colors;
+ bool recalc_lines;
+ bool recalc_square_border;
+
+ /* Used to store TPG_MAX_PAT_LINES lines, each with up to two planes */
+ unsigned max_line_width;
+ u8 *lines[TPG_MAX_PAT_LINES][TPG_MAX_PLANES];
+ u8 *random_line[TPG_MAX_PLANES];
+ u8 *contrast_line[TPG_MAX_PLANES];
+ u8 *black_line[TPG_MAX_PLANES];
+};
+
+void tpg_init(struct tpg_data *tpg, unsigned w, unsigned h);
+int tpg_alloc(struct tpg_data *tpg, unsigned max_w);
+void tpg_free(struct tpg_data *tpg);
+void tpg_reset_source(struct tpg_data *tpg, unsigned width, unsigned height,
+ u32 field);
+
+void tpg_set_font(const u8 *f);
+void tpg_gen_text(struct tpg_data *tpg,
+ u8 *basep[TPG_MAX_PLANES][2], int y, int x, char *text);
+void tpg_calc_text_basep(struct tpg_data *tpg,
+ u8 *basep[TPG_MAX_PLANES][2], unsigned p, u8 *vbuf);
+void tpg_fillbuffer(struct tpg_data *tpg, v4l2_std_id std, unsigned p, u8 *vbuf);
+bool tpg_s_fourcc(struct tpg_data *tpg, u32 fourcc);
+void tpg_s_crop_compose(struct tpg_data *tpg, const struct v4l2_rect *crop,
+ const struct v4l2_rect *compose);
+
+static inline void tpg_s_pattern(struct tpg_data *tpg, enum tpg_pattern pattern)
+{
+ if (tpg->pattern == pattern)
+ return;
+ tpg->pattern = pattern;
+ tpg->recalc_colors = true;
+}
+
+static inline void tpg_s_quality(struct tpg_data *tpg,
+ enum tpg_quality qual, unsigned qual_offset)
+{
+ if (tpg->qual == qual && tpg->qual_offset == qual_offset)
+ return;
+ tpg->qual = qual;
+ tpg->qual_offset = qual_offset;
+ tpg->recalc_colors = true;
+}
+
+static inline enum tpg_quality tpg_g_quality(const struct tpg_data *tpg)
+{
+ return tpg->qual;
+}
+
+static inline void tpg_s_alpha_component(struct tpg_data *tpg,
+ u8 alpha_component)
+{
+ if (tpg->alpha_component == alpha_component)
+ return;
+ tpg->alpha_component = alpha_component;
+ tpg->recalc_colors = true;
+}
+
+static inline void tpg_s_alpha_mode(struct tpg_data *tpg,
+ bool red_only)
+{
+ if (tpg->alpha_red_only == red_only)
+ return;
+ tpg->alpha_red_only = red_only;
+ tpg->recalc_colors = true;
+}
+
+static inline void tpg_s_brightness(struct tpg_data *tpg,
+ u8 brightness)
+{
+ if (tpg->brightness == brightness)
+ return;
+ tpg->brightness = brightness;
+ tpg->recalc_colors = true;
+}
+
+static inline void tpg_s_contrast(struct tpg_data *tpg,
+ u8 contrast)
+{
+ if (tpg->contrast == contrast)
+ return;
+ tpg->contrast = contrast;
+ tpg->recalc_colors = true;
+}
+
+static inline void tpg_s_saturation(struct tpg_data *tpg,
+ u8 saturation)
+{
+ if (tpg->saturation == saturation)
+ return;
+ tpg->saturation = saturation;
+ tpg->recalc_colors = true;
+}
+
+static inline void tpg_s_hue(struct tpg_data *tpg,
+ s16 hue)
+{
+ if (tpg->hue == hue)
+ return;
+ tpg->hue = hue;
+ tpg->recalc_colors = true;
+}
+
+static inline void tpg_s_rgb_range(struct tpg_data *tpg,
+ unsigned rgb_range)
+{
+ if (tpg->rgb_range == rgb_range)
+ return;
+ tpg->rgb_range = rgb_range;
+ tpg->recalc_colors = true;
+}
+
+static inline void tpg_s_real_rgb_range(struct tpg_data *tpg,
+ unsigned rgb_range)
+{
+ if (tpg->real_rgb_range == rgb_range)
+ return;
+ tpg->real_rgb_range = rgb_range;
+ tpg->recalc_colors = true;
+}
+
+static inline void tpg_s_colorspace(struct tpg_data *tpg, u32 colorspace)
+{
+ if (tpg->colorspace == colorspace)
+ return;
+ tpg->colorspace = colorspace;
+ tpg->recalc_colors = true;
+}
+
+static inline u32 tpg_g_colorspace(const struct tpg_data *tpg)
+{
+ return tpg->colorspace;
+}
+
+static inline unsigned tpg_g_planes(const struct tpg_data *tpg)
+{
+ return tpg->planes;
+}
+
+static inline unsigned tpg_g_twopixelsize(const struct tpg_data *tpg, unsigned plane)
+{
+ return tpg->twopixelsize[plane];
+}
+
+static inline unsigned tpg_g_bytesperline(const struct tpg_data *tpg, unsigned plane)
+{
+ return tpg->bytesperline[plane];
+}
+
+static inline void tpg_s_bytesperline(struct tpg_data *tpg, unsigned plane, unsigned bpl)
+{
+ tpg->bytesperline[plane] = bpl;
+}
+
+static inline void tpg_s_buf_height(struct tpg_data *tpg, unsigned h)
+{
+ tpg->buf_height = h;
+}
+
+static inline void tpg_s_field(struct tpg_data *tpg, unsigned field)
+{
+ tpg->field = field;
+}
+
+static inline void tpg_s_perc_fill(struct tpg_data *tpg,
+ unsigned perc_fill)
+{
+ tpg->perc_fill = perc_fill;
+}
+
+static inline unsigned tpg_g_perc_fill(const struct tpg_data *tpg)
+{
+ return tpg->perc_fill;
+}
+
+static inline void tpg_s_perc_fill_blank(struct tpg_data *tpg,
+ bool perc_fill_blank)
+{
+ tpg->perc_fill_blank = perc_fill_blank;
+}
+
+static inline void tpg_s_video_aspect(struct tpg_data *tpg,
+ enum tpg_video_aspect vid_aspect)
+{
+ if (tpg->vid_aspect == vid_aspect)
+ return;
+ tpg->vid_aspect = vid_aspect;
+ tpg->recalc_square_border = true;
+}
+
+static inline enum tpg_video_aspect tpg_g_video_aspect(const struct tpg_data *tpg)
+{
+ return tpg->vid_aspect;
+}
+
+static inline void tpg_s_pixel_aspect(struct tpg_data *tpg,
+ enum tpg_pixel_aspect pix_aspect)
+{
+ if (tpg->pix_aspect == pix_aspect)
+ return;
+ tpg->pix_aspect = pix_aspect;
+ tpg->recalc_square_border = true;
+}
+
+static inline void tpg_s_show_border(struct tpg_data *tpg,
+ bool show_border)
+{
+ tpg->show_border = show_border;
+}
+
+static inline void tpg_s_show_square(struct tpg_data *tpg,
+ bool show_square)
+{
+ tpg->show_square = show_square;
+}
+
+static inline void tpg_s_insert_sav(struct tpg_data *tpg, bool insert_sav)
+{
+ tpg->insert_sav = insert_sav;
+}
+
+static inline void tpg_s_insert_eav(struct tpg_data *tpg, bool insert_eav)
+{
+ tpg->insert_eav = insert_eav;
+}
+
+void tpg_update_mv_step(struct tpg_data *tpg);
+
+static inline void tpg_s_mv_hor_mode(struct tpg_data *tpg,
+ enum tpg_move_mode mv_hor_mode)
+{
+ tpg->mv_hor_mode = mv_hor_mode;
+ tpg_update_mv_step(tpg);
+}
+
+static inline void tpg_s_mv_vert_mode(struct tpg_data *tpg,
+ enum tpg_move_mode mv_vert_mode)
+{
+ tpg->mv_vert_mode = mv_vert_mode;
+ tpg_update_mv_step(tpg);
+}
+
+static inline void tpg_init_mv_count(struct tpg_data *tpg)
+{
+ tpg->mv_hor_count = tpg->mv_vert_count = 0;
+}
+
+static inline void tpg_update_mv_count(struct tpg_data *tpg, bool frame_is_field)
+{
+ tpg->mv_hor_count += tpg->mv_hor_step * (frame_is_field ? 1 : 2);
+ tpg->mv_vert_count += tpg->mv_vert_step * (frame_is_field ? 1 : 2);
+}
+
+static inline void tpg_s_hflip(struct tpg_data *tpg, bool hflip)
+{
+ if (tpg->hflip == hflip)
+ return;
+ tpg->hflip = hflip;
+ tpg_update_mv_step(tpg);
+ tpg->recalc_lines = true;
+}
+
+static inline bool tpg_g_hflip(const struct tpg_data *tpg)
+{
+ return tpg->hflip;
+}
+
+static inline void tpg_s_vflip(struct tpg_data *tpg, bool vflip)
+{
+ tpg->vflip = vflip;
+}
+
+static inline bool tpg_g_vflip(const struct tpg_data *tpg)
+{
+ return tpg->vflip;
+}
+
+static inline bool tpg_pattern_is_static(const struct tpg_data *tpg)
+{
+ return tpg->pattern != TPG_PAT_NOISE &&
+ tpg->mv_hor_mode == TPG_MOVE_NONE &&
+ tpg->mv_vert_mode == TPG_MOVE_NONE;
+}
+
+#endif
diff --git a/drivers/media/platform/vivid/vivid-vbi-cap.c b/drivers/media/platform/vivid/vivid-vbi-cap.c
new file mode 100644
index 000000000000..2166d0bf6fe2
--- /dev/null
+++ b/drivers/media/platform/vivid/vivid-vbi-cap.c
@@ -0,0 +1,371 @@
+/*
+ * vivid-vbi-cap.c - vbi capture support functions.
+ *
+ * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-common.h>
+
+#include "vivid-core.h"
+#include "vivid-kthread-cap.h"
+#include "vivid-vbi-cap.h"
+#include "vivid-vbi-gen.h"
+
+static void vivid_sliced_vbi_cap_fill(struct vivid_dev *dev, unsigned seqnr)
+{
+ struct vivid_vbi_gen_data *vbi_gen = &dev->vbi_gen;
+ bool is_60hz = dev->std_cap & 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 & 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, 0);
+
+ vivid_g_fmt_vbi_cap(dev, &vbi);
+ buf->vb.v4l2_buf.sequence = dev->vbi_cap_seq_count;
+ if (dev->field_cap == V4L2_FIELD_ALTERNATE)
+ buf->vb.v4l2_buf.sequence /= 2;
+
+ vivid_sliced_vbi_cap_fill(dev, buf->vb.v4l2_buf.sequence);
+
+ memset(vbuf, 0x10, vb2_plane_size(&buf->vb, 0));
+
+ if (!VIVID_INVALID_SIGNAL(dev->std_signal_mode))
+ vivid_vbi_gen_raw(&dev->vbi_gen, &vbi, vbuf);
+
+ v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp);
+ buf->vb.v4l2_buf.timestamp.tv_sec += dev->time_wrap_offset;
+}
+
+
+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, 0);
+
+ buf->vb.v4l2_buf.sequence = dev->vbi_cap_seq_count;
+ if (dev->field_cap == V4L2_FIELD_ALTERNATE)
+ buf->vb.v4l2_buf.sequence /= 2;
+
+ vivid_sliced_vbi_cap_fill(dev, buf->vb.v4l2_buf.sequence);
+
+ memset(vbuf, 0, vb2_plane_size(&buf->vb, 0));
+ if (!VIVID_INVALID_SIGNAL(dev->std_signal_mode)) {
+ unsigned i;
+
+ for (i = 0; i < 25; i++)
+ vbuf[i] = dev->vbi_gen.data[i];
+ }
+
+ v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp);
+ buf->vb.v4l2_buf.timestamp.tv_sec += dev->time_wrap_offset;
+}
+
+static int vbi_cap_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
+ unsigned *nbuffers, unsigned *nplanes,
+ unsigned sizes[], void *alloc_ctxs[])
+{
+ struct vivid_dev *dev = vb2_get_drv_priv(vq);
+ bool is_60hz = dev->std_cap & 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 & 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 vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
+ struct vivid_buffer *buf = container_of(vb, 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_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);
+}
+
+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,
+ .wait_prepare = vivid_unlock,
+ .wait_finish = vivid_lock,
+};
+
+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 & 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 & 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
new file mode 100644
index 000000000000..2d8ea0bac743
--- /dev/null
+++ b/drivers/media/platform/vivid/vivid-vbi-cap.h
@@ -0,0 +1,40 @@
+/*
+ * vivid-vbi-cap.h - vbi capture support functions.
+ *
+ * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef _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
new file mode 100644
index 000000000000..a2159de83d0b
--- /dev/null
+++ b/drivers/media/platform/vivid/vivid-vbi-gen.c
@@ -0,0 +1,323 @@
+/*
+ * vivid-vbi-gen.c - vbi generator support functions.
+ *
+ * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/ktime.h>
+#include <linux/string.h>
+#include <linux/videodev2.h>
+
+#include "vivid-vbi-gen.h"
+
+static void wss_insert(u8 *wss, u32 val, unsigned size)
+{
+ while (size--)
+ *wss++ = (val & (1 << size)) ? 0xc0 : 0x10;
+}
+
+static void vivid_vbi_gen_wss_raw(const struct v4l2_sliced_vbi_data *data,
+ u8 *buf, unsigned sampling_rate)
+{
+ const unsigned rate = 5000000; /* WSS has a 5 MHz transmission rate */
+ u8 wss[29 + 24 + 24 + 24 + 18 + 18] = { 0 };
+ const unsigned zero = 0x07;
+ const unsigned one = 0x38;
+ unsigned bit = 0;
+ u16 wss_data;
+ int i;
+
+ wss_insert(wss + bit, 0x1f1c71c7, 29); bit += 29;
+ wss_insert(wss + bit, 0x1e3c1f, 24); bit += 24;
+
+ wss_data = (data->data[1] << 8) | data->data[0];
+ for (i = 0; i <= 13; i++, bit += 6)
+ wss_insert(wss + bit, (wss_data & (1 << i)) ? one : zero, 6);
+
+ for (i = 0, bit = 0; bit < sizeof(wss); bit++) {
+ unsigned n = ((bit + 1) * sampling_rate) / rate;
+
+ while (i < n)
+ buf[i++] = wss[bit];
+ }
+}
+
+static void vivid_vbi_gen_teletext_raw(const struct v4l2_sliced_vbi_data *data,
+ u8 *buf, unsigned sampling_rate)
+{
+ const unsigned rate = 6937500 / 10; /* Teletext has a 6.9375 MHz transmission rate */
+ u8 teletext[45] = { 0x55, 0x55, 0x27 };
+ unsigned bit = 0;
+ int i;
+
+ memcpy(teletext + 3, data->data, sizeof(teletext) - 3);
+ /* prevents 32 bit overflow */
+ sampling_rate /= 10;
+
+ for (i = 0, bit = 0; bit < sizeof(teletext) * 8; bit++) {
+ unsigned n = ((bit + 1) * sampling_rate) / rate;
+ u8 val = (teletext[bit / 8] & (1 << (bit & 7))) ? 0xc0 : 0x10;
+
+ while (i < n)
+ buf[i++] = val;
+ }
+}
+
+static void cc_insert(u8 *cc, u8 ch)
+{
+ unsigned tot = 0;
+ unsigned i;
+
+ for (i = 0; i < 7; i++) {
+ cc[2 * i] = cc[2 * i + 1] = (ch & (1 << i)) ? 1 : 0;
+ tot += cc[2 * i];
+ }
+ cc[14] = cc[15] = !(tot & 1);
+}
+
+#define CC_PREAMBLE_BITS (14 + 4 + 2)
+
+static void vivid_vbi_gen_cc_raw(const struct v4l2_sliced_vbi_data *data,
+ u8 *buf, unsigned sampling_rate)
+{
+ const unsigned rate = 1000000; /* CC has a 1 MHz transmission rate */
+
+ u8 cc[CC_PREAMBLE_BITS + 2 * 16] = {
+ /* Clock run-in: 7 cycles */
+ 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+ /* 2 cycles of 0 */
+ 0, 0, 0, 0,
+ /* Start bit of 1 (each bit is two cycles) */
+ 1, 1
+ };
+ unsigned bit, i;
+
+ cc_insert(cc + CC_PREAMBLE_BITS, data->data[0]);
+ cc_insert(cc + CC_PREAMBLE_BITS + 16, data->data[1]);
+
+ for (i = 0, bit = 0; bit < sizeof(cc); bit++) {
+ unsigned n = ((bit + 1) * sampling_rate) / rate;
+
+ while (i < n)
+ buf[i++] = cc[bit] ? 0xc0 : 0x10;
+ }
+}
+
+void vivid_vbi_gen_raw(const struct vivid_vbi_gen_data *vbi,
+ const struct v4l2_vbi_format *vbi_fmt, u8 *buf)
+{
+ unsigned idx;
+
+ for (idx = 0; idx < 25; idx++) {
+ const struct v4l2_sliced_vbi_data *data = vbi->data + idx;
+ unsigned start_2nd_field;
+ unsigned line = data->line;
+ u8 *linebuf = buf;
+
+ start_2nd_field = (data->id & V4L2_SLICED_VBI_525) ? 263 : 313;
+ if (data->field)
+ line += start_2nd_field;
+ line -= vbi_fmt->start[data->field];
+
+ if (vbi_fmt->flags & V4L2_VBI_INTERLACED)
+ linebuf += (line * 2 + data->field) *
+ vbi_fmt->samples_per_line;
+ else
+ linebuf += (line + data->field * vbi_fmt->count[0]) *
+ vbi_fmt->samples_per_line;
+ if (data->id == V4L2_SLICED_CAPTION_525)
+ vivid_vbi_gen_cc_raw(data, linebuf, vbi_fmt->sampling_rate);
+ else if (data->id == V4L2_SLICED_WSS_625)
+ vivid_vbi_gen_wss_raw(data, linebuf, vbi_fmt->sampling_rate);
+ else if (data->id == V4L2_SLICED_TELETEXT_B)
+ vivid_vbi_gen_teletext_raw(data, linebuf, vbi_fmt->sampling_rate);
+ }
+}
+
+static const u8 vivid_cc_sequence1[30] = {
+ 0x14, 0x20, /* Resume Caption Loading */
+ 'H', 'e',
+ 'l', 'l',
+ 'o', ' ',
+ 'w', 'o',
+ 'r', 'l',
+ 'd', '!',
+ 0x14, 0x2f, /* End of Caption */
+};
+
+static const u8 vivid_cc_sequence2[30] = {
+ 0x14, 0x20, /* Resume Caption Loading */
+ 'C', 'l',
+ 'o', 's',
+ 'e', 'd',
+ ' ', 'c',
+ 'a', 'p',
+ 't', 'i',
+ 'o', 'n',
+ 's', ' ',
+ 't', 'e',
+ 's', 't',
+ 0x14, 0x2f, /* End of Caption */
+};
+
+static u8 calc_parity(u8 val)
+{
+ unsigned i;
+ unsigned tot = 0;
+
+ for (i = 0; i < 7; i++)
+ tot += (val & (1 << i)) ? 1 : 0;
+ return val | ((tot & 1) ? 0 : 0x80);
+}
+
+static void vivid_vbi_gen_set_time_of_day(u8 *packet)
+{
+ struct tm tm;
+ u8 checksum, i;
+
+ time_to_tm(get_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
new file mode 100644
index 000000000000..8444abe905ea
--- /dev/null
+++ b/drivers/media/platform/vivid/vivid-vbi-gen.h
@@ -0,0 +1,33 @@
+/*
+ * vivid-vbi-gen.h - vbi generator support functions.
+ *
+ * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef _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
new file mode 100644
index 000000000000..9d00a07ecdcd
--- /dev/null
+++ b/drivers/media/platform/vivid/vivid-vbi-out.c
@@ -0,0 +1,248 @@
+/*
+ * vivid-vbi-out.c - vbi output support functions.
+ *
+ * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-common.h>
+
+#include "vivid-core.h"
+#include "vivid-kthread-out.h"
+#include "vivid-vbi-out.h"
+#include "vivid-vbi-cap.h"
+
+static int vbi_out_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
+ unsigned *nbuffers, unsigned *nplanes,
+ unsigned sizes[], void *alloc_ctxs[])
+{
+ 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 vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
+ struct vivid_buffer *buf = container_of(vb, 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_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;
+}
+
+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,
+ .wait_prepare = vivid_unlock,
+ .wait_finish = vivid_lock,
+};
+
+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, 0);
+ unsigned elems = vb2_get_plane_payload(&buf->vb, 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
new file mode 100644
index 000000000000..6555ba9d2860
--- /dev/null
+++ b/drivers/media/platform/vivid/vivid-vbi-out.h
@@ -0,0 +1,34 @@
+/*
+ * vivid-vbi-out.h - vbi output support functions.
+ *
+ * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef _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
new file mode 100644
index 000000000000..331c54429b40
--- /dev/null
+++ b/drivers/media/platform/vivid/vivid-vid-cap.c
@@ -0,0 +1,1730 @@
+/*
+ * vivid-vid-cap.c - video capture support functions.
+ *
+ * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/vmalloc.h>
+#include <linux/videodev2.h>
+#include <linux/v4l2-dv-timings.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-dv-timings.h>
+
+#include "vivid-core.h"
+#include "vivid-vid-common.h"
+#include "vivid-kthread-cap.h"
+#include "vivid-vid-cap.h"
+
+/* timeperframe: min/max and default */
+static const struct v4l2_fract
+ tpf_min = {.numerator = 1, .denominator = FPS_MAX},
+ tpf_max = {.numerator = FPS_MAX, .denominator = 1},
+ tpf_default = {.numerator = 1, .denominator = 30};
+
+static const struct vivid_fmt formats_ovl[] = {
+ {
+ .name = "RGB565 (LE)",
+ .fourcc = V4L2_PIX_FMT_RGB565, /* gggbbbbb rrrrrggg */
+ .depth = 16,
+ .planes = 1,
+ },
+ {
+ .name = "XRGB555 (LE)",
+ .fourcc = V4L2_PIX_FMT_XRGB555, /* gggbbbbb arrrrrgg */
+ .depth = 16,
+ .planes = 1,
+ },
+ {
+ .name = "ARGB555 (LE)",
+ .fourcc = V4L2_PIX_FMT_ARGB555, /* gggbbbbb arrrrrgg */
+ .depth = 16,
+ .planes = 1,
+ },
+};
+
+/* The number of discrete webcam framesizes */
+#define VIVID_WEBCAM_SIZES 3
+/* 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 },
+ { 1280, 720 },
+};
+
+/*
+ * 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, 10 },
+ { 1, 15 },
+ { 1, 25 },
+ { 1, 30 },
+ { 1, 50 },
+ { 1, 60 },
+};
+
+static const struct v4l2_discrete_probe webcam_probe = {
+ webcam_sizes,
+ VIVID_WEBCAM_SIZES
+};
+
+static int vid_cap_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
+ unsigned *nbuffers, unsigned *nplanes,
+ unsigned sizes[], void *alloc_ctxs[])
+{
+ struct vivid_dev *dev = vb2_get_drv_priv(vq);
+ unsigned planes = tpg_g_planes(&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 (fmt) {
+ const struct v4l2_pix_format_mplane *mp;
+ struct v4l2_format mp_fmt;
+ const struct vivid_fmt *vfmt;
+
+ if (!V4L2_TYPE_IS_MULTIPLANAR(fmt->type)) {
+ fmt_sp2mp(fmt, &mp_fmt);
+ fmt = &mp_fmt;
+ }
+ mp = &fmt->fmt.pix_mp;
+ /*
+ * Check if the number of planes in the specified format match
+ * the number of planes in the current format. You can't mix that.
+ */
+ if (mp->num_planes != planes)
+ return -EINVAL;
+ vfmt = vivid_get_format(dev, mp->pixelformat);
+ for (p = 0; p < planes; p++) {
+ sizes[p] = mp->plane_fmt[p].sizeimage;
+ if (sizes[0] < tpg_g_bytesperline(&dev->tpg, 0) * h +
+ vfmt->data_offset[p])
+ return -EINVAL;
+ }
+ } else {
+ for (p = 0; p < planes; p++)
+ sizes[p] = tpg_g_bytesperline(&dev->tpg, p) * h +
+ dev->fmt_cap->data_offset[p];
+ }
+
+ if (vq->num_buffers + *nbuffers < 2)
+ *nbuffers = 2 - vq->num_buffers;
+
+ *nplanes = planes;
+
+ /*
+ * videobuf2-vmalloc allocator is context-less so no need to set
+ * alloc_ctxs array.
+ */
+
+ if (planes == 2)
+ dprintk(dev, 1, "%s, count=%d, sizes=%u, %u\n", __func__,
+ *nbuffers, sizes[0], sizes[1]);
+ else
+ dprintk(dev, 1, "%s, count=%d, size=%u\n", __func__,
+ *nbuffers, sizes[0]);
+
+ 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 planes = tpg_g_planes(&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 < planes; p++) {
+ size = tpg_g_bytesperline(&dev->tpg, p) * dev->fmt_cap_rect.height +
+ dev->fmt_cap->data_offset[p];
+
+ if (vb2_plane_size(vb, 0) < size) {
+ dprintk(dev, 1, "%s data will not fit into plane %u (%lu < %lu)\n",
+ __func__, p, vb2_plane_size(vb, 0), size);
+ return -EINVAL;
+ }
+
+ vb2_set_plane_payload(vb, p, size);
+ vb->v4l2_planes[p].data_offset = dev->fmt_cap->data_offset[p];
+ }
+
+ return 0;
+}
+
+static void vid_cap_buf_finish(struct vb2_buffer *vb)
+{
+ struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
+ struct v4l2_timecode *tc = &vb->v4l2_buf.timecode;
+ unsigned fps = 25;
+ unsigned seq = vb->v4l2_buf.sequence;
+
+ if (!vivid_is_sdtv_cap(dev))
+ return;
+
+ /*
+ * Set the timecode. Rarely used, so it is interesting to
+ * test this.
+ */
+ vb->v4l2_buf.flags |= V4L2_BUF_FLAG_TIMECODE;
+ if (dev->std_cap & 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 vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
+ struct vivid_buffer *buf = container_of(vb, 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);
+
+ if (dev->kthread_vid_cap)
+ return 0;
+
+ 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_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;
+}
+
+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,
+ .wait_prepare = vivid_unlock,
+ .wait_finish = vivid_lock,
+};
+
+/*
+ * 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)) {
+ tpg_s_quality(&dev->tpg, TPG_QUAL_NOISE, 0);
+ return;
+ }
+ if (vivid_is_sdtv_cap(dev) && VIVID_INVALID_SIGNAL(dev->std_signal_mode)) {
+ 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;
+
+ if (vivid_is_hdmi_cap(dev))
+ return dev->dv_timings_aspect_ratio;
+
+ 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 & 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.bt;
+ unsigned size;
+
+ 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 & 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);
+ dev->timeperframe_vid_cap = (struct v4l2_fract) {
+ size / 100, (u32)bt->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->standards & V4L2_DV_BT_STD_CEA861) {
+ if (bt->width == 720 && bt->height <= 576)
+ v4l2_ctrl_s_ctrl(dev->colorspace, V4L2_COLORSPACE_SMPTE170M);
+ else
+ v4l2_ctrl_s_ctrl(dev->colorspace, V4L2_COLORSPACE_REC709);
+ v4l2_ctrl_s_ctrl(dev->real_rgb_range_cap, 1);
+ } else {
+ v4l2_ctrl_s_ctrl(dev->colorspace, V4L2_COLORSPACE_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;
+ }
+ 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.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;
+}
+
+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->num_planes = dev->fmt_cap->planes;
+ for (p = 0; p < mp->num_planes; p++) {
+ mp->plane_fmt[p].bytesperline = tpg_g_bytesperline(&dev->tpg, p);
+ mp->plane_fmt[p].sizeimage =
+ mp->plane_fmt[p].bytesperline * mp->height +
+ 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_format(&webcam_probe, mp->width, mp->height);
+
+ w = sz->width;
+ h = sz->height;
+ } else if (vivid_is_sdtv_cap(dev)) {
+ w = 720;
+ h = (dev->std_cap & 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 };
+
+ rect_set_min_size(&r, &vivid_min_rect);
+ 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 };
+
+ rect_set_max_size(&r, &max_r);
+ } else if (!dev->has_scaler_cap && dev->has_crop_cap && !dev->has_compose_cap) {
+ rect_set_max_size(&r, &dev->src_rect);
+ } else if (!dev->has_scaler_cap && !dev->has_crop_cap) {
+ rect_set_min_size(&r, &dev->src_rect);
+ }
+ mp->width = r.width;
+ mp->height = r.height / factor;
+ }
+
+ /* This driver supports custom bytesperline values */
+
+ /* Calculate the minimum supported bytesperline value */
+ bytesperline = (mp->width * fmt->depth) >> 3;
+ /* Calculate the maximum supported bytesperline value */
+ max_bpl = (MAX_ZOOM * MAX_WIDTH * fmt->depth) >> 3;
+ mp->num_planes = fmt->planes;
+ for (p = 0; p < mp->num_planes; p++) {
+ 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->data_offset[p];
+ memset(pfmt[p].reserved, 0, sizeof(pfmt[p].reserved));
+ }
+ mp->colorspace = vivid_colorspace_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 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)
+ 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
+ };
+
+ rect_set_min_size(crop, &min_r);
+ rect_set_max_size(crop, &max_r);
+ 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
+ };
+
+ rect_set_min_size(crop, &min_r);
+ rect_set_max_size(crop, &max_r);
+ rect_map_inside(crop, &dev->crop_bounds_cap);
+ }
+ } else if (dev->has_crop_cap && !dev->has_compose_cap) {
+ r.height *= factor;
+ rect_set_size_to(crop, &r);
+ rect_map_inside(crop, &dev->crop_bounds_cap);
+ r = *crop;
+ r.height /= factor;
+ rect_set_size_to(compose, &r);
+ } else if (!dev->has_crop_cap) {
+ rect_map_inside(compose, &r);
+ } else {
+ r.height *= factor;
+ rect_set_max_size(crop, &r);
+ rect_map_inside(crop, &dev->crop_bounds_cap);
+ compose->top *= factor;
+ compose->height *= factor;
+ rect_set_size_to(compose, crop);
+ 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 * (3 - i))
+ dev->webcam_ival_idx = 2 * (3 - i) - 1;
+ vivid_update_format_cap(dev, false);
+ } else {
+ struct v4l2_rect r = { 0, 0, mp->width, mp->height };
+
+ rect_set_size_to(compose, &r);
+ r.height *= factor;
+ 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_bytesperline(&dev->tpg, 0, mp->plane_fmt[0].bytesperline);
+ if (tpg_g_planes(&dev->tpg) > 1)
+ tpg_s_bytesperline(&dev->tpg, 1, mp->plane_fmt[1].bytesperline);
+ dev->field_cap = mp->field;
+ tpg_s_field(&dev->tpg, dev->field_cap);
+ tpg_s_crop_compose(&dev->tpg, &dev->crop_cap, &dev->compose_cap);
+ tpg_s_fourcc(&dev->tpg, dev->fmt_cap->fourcc);
+ 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 -EINVAL;
+
+ 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 -EINVAL;
+
+ 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;
+ rect_set_min_size(&s->r, &vivid_min_rect);
+ rect_set_max_size(&s->r, &dev->src_rect);
+ 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
+ };
+
+ rect_set_min_size(&fmt, &min_rect);
+ if (!dev->has_compose_cap)
+ rect_set_max_size(&fmt, &max_rect);
+ if (!rect_same_size(&dev->fmt_cap_rect, &fmt) &&
+ vb2_is_busy(&dev->vb_vid_cap_q))
+ return -EBUSY;
+ if (dev->has_compose_cap) {
+ rect_set_min_size(compose, &min_rect);
+ 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;
+
+ rect_set_min_size(&fmt, &s->r);
+ if (!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);
+ rect_set_size_to(compose, &s->r);
+ rect_map_inside(compose, &dev->fmt_cap_rect);
+ } else {
+ if (!rect_same_size(&s->r, &dev->fmt_cap_rect) &&
+ vb2_is_busy(&dev->vb_vid_cap_q))
+ return -EBUSY;
+ rect_set_size_to(&dev->fmt_cap_rect, &s->r);
+ rect_set_size_to(compose, &s->r);
+ 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;
+ rect_set_min_size(&s->r, &vivid_min_rect);
+ 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
+ };
+
+ 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
+ };
+
+ rect_set_min_size(crop, &min_rect);
+ rect_set_max_size(crop, &max_rect);
+ rect_map_inside(crop, &dev->crop_bounds_cap);
+ }
+ } else if (dev->has_crop_cap) {
+ s->r.top *= factor;
+ s->r.height *= factor;
+ rect_set_max_size(&s->r, &dev->src_rect);
+ rect_set_size_to(crop, &s->r);
+ rect_map_inside(crop, &dev->crop_bounds_cap);
+ s->r.top /= factor;
+ s->r.height /= factor;
+ } else {
+ rect_set_size_to(&s->r, &dev->src_rect);
+ s->r.height /= factor;
+ }
+ rect_map_inside(&s->r, &dev->fmt_cap_rect);
+ if (dev->bitmap_cap && (compose->width != s->r.width ||
+ compose->height != s->r.height)) {
+ kfree(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_cropcap(struct file *file, void *priv,
+ struct v4l2_cropcap *cap)
+{
+ struct vivid_dev *dev = video_drvdata(file);
+
+ if (cap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ switch (vivid_get_pixel_aspect(dev)) {
+ case TPG_PIXEL_ASPECT_NTSC:
+ cap->pixelaspect.numerator = 11;
+ cap->pixelaspect.denominator = 10;
+ break;
+ case TPG_PIXEL_ASPECT_PAL:
+ cap->pixelaspect.numerator = 54;
+ cap->pixelaspect.denominator = 59;
+ break;
+ case TPG_PIXEL_ASPECT_SQUARE:
+ cap->pixelaspect.numerator = 1;
+ cap->pixelaspect.denominator = 1;
+ break;
+ }
+ return 0;
+}
+
+int vidioc_enum_fmt_vid_overlay(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ const struct vivid_fmt *fmt;
+
+ if (f->index >= ARRAY_SIZE(formats_ovl))
+ return -EINVAL;
+
+ fmt = &formats_ovl[f->index];
+
+ strlcpy(f->description, fmt->name, sizeof(f->description));
+ 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;
+
+ 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;
+
+ 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 (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 (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);
+
+ *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 (!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->depth) / 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 == NO_SIGNAL)
+ inp->status |= V4L2_IN_ST_NO_SIGNAL;
+ else if (dev->dv_timings_signal_mode == NO_LOCK ||
+ dev->dv_timings_signal_mode == 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 == NO_SIGNAL) {
+ inp->status |= V4L2_IN_ST_NO_SIGNAL;
+ } else if (dev->std_signal_mode == 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.bt;
+ unsigned brightness;
+
+ if (i >= dev->num_inputs)
+ return -EINVAL;
+
+ if (i == dev->input)
+ return 0;
+
+ if (vb2_is_busy(&dev->vb_vid_cap_q) || vb2_is_busy(&dev->vb_vbi_cap_q))
+ return -EBUSY;
+
+ dev->input = i;
+ dev->vid_cap_dev.tvnorms = 0;
+ if (dev->input_type[i] == TV || dev->input_type[i] == SVID) {
+ dev->tv_audio_input = (dev->input_type[i] == TV) ? 0 : 1;
+ dev->vid_cap_dev.tvnorms = V4L2_STD_ALL;
+ }
+ dev->vbi_cap_dev.tvnorms = dev->vid_cap_dev.tvnorms;
+ vivid_update_format_cap(dev, false);
+
+ if (dev->colorspace) {
+ switch (dev->input_type[i]) {
+ case WEBCAM:
+ v4l2_ctrl_s_ctrl(dev->colorspace, V4L2_COLORSPACE_SRGB);
+ break;
+ case TV:
+ case SVID:
+ v4l2_ctrl_s_ctrl(dev->colorspace, V4L2_COLORSPACE_SMPTE170M);
+ break;
+ case HDMI:
+ if (bt->standards & V4L2_DV_BT_STD_CEA861) {
+ if (dev->src_rect.width == 720 && dev->src_rect.height <= 576)
+ v4l2_ctrl_s_ctrl(dev->colorspace, V4L2_COLORSPACE_SMPTE170M);
+ else
+ v4l2_ctrl_s_ctrl(dev->colorspace, V4L2_COLORSPACE_REC709);
+ } else {
+ v4l2_ctrl_s_ctrl(dev->colorspace, V4L2_COLORSPACE_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);
+ 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 channel_nr = dev->tv_freq / (6 * 16);
+ unsigned options = (dev->std_cap & 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 & 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;
+ }
+ }
+ strlcpy(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);
+
+ if (!vivid_is_sdtv_cap(dev))
+ return -ENODATA;
+ if (dev->std_signal_mode == NO_SIGNAL ||
+ dev->std_signal_mode == 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 == CURRENT_STD) {
+ *id = dev->std_cap;
+ } else if (dev->std_signal_mode == SELECTED_STD) {
+ *id = dev->query_std;
+ } else {
+ *id = vivid_standard[dev->query_std_last];
+ dev->query_std_last = (dev->query_std_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 == 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 = id;
+ vivid_update_format_cap(dev, false);
+ return 0;
+}
+
+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 (vb2_is_busy(&dev->vb_vid_cap_q))
+ return -EBUSY;
+ if (!v4l2_find_dv_timings_cap(timings, &vivid_dv_timings_cap,
+ 0, NULL, NULL))
+ return -EINVAL;
+ if (v4l2_match_dv_timings(timings, &dev->dv_timings_cap, 0))
+ return 0;
+ dev->dv_timings_cap = *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);
+
+ if (!vivid_is_hdmi_cap(dev))
+ return -ENODATA;
+ if (dev->dv_timings_signal_mode == NO_SIGNAL ||
+ dev->edid_blocks == 0)
+ return -ENOLINK;
+ if (dev->dv_timings_signal_mode == NO_LOCK)
+ return -ENOLCK;
+ if (dev->dv_timings_signal_mode == OUT_OF_RANGE) {
+ timings->bt.pixelclock = vivid_dv_timings_cap.bt.max_pixelclock * 2;
+ return -ERANGE;
+ }
+ if (dev->dv_timings_signal_mode == CURRENT_DV_TIMINGS) {
+ *timings = dev->dv_timings_cap;
+ } else if (dev->dv_timings_signal_mode == SELECTED_DV_TIMINGS) {
+ *timings = v4l2_dv_timings_presets[dev->query_dv_timings];
+ } else {
+ *timings = v4l2_dv_timings_presets[dev->query_dv_timings_last];
+ dev->query_dv_timings_last = (dev->query_dv_timings_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);
+
+ 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;
+ return 0;
+ }
+ if (edid->blocks > dev->edid_max_blocks) {
+ edid->blocks = dev->edid_max_blocks;
+ return -E2BIG;
+ }
+ dev->edid_blocks = edid->blocks;
+ memcpy(dev->edid, edid->edid, edid->blocks * 128);
+ 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)) {
+ static const struct v4l2_fract step = { 1, 1 };
+
+ 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_CONTINUOUS;
+ fival->stepwise.min = tpf_min;
+ fival->stepwise.max = tpf_max;
+ fival->stepwise.step = step;
+ 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 * (3 - 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;
+}
+
+#define FRACT_CMP(a, OP, b) \
+ ((u64)(a).numerator * (b).denominator OP (u64)(b).numerator * (a).denominator)
+
+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 * (3 - 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 (FRACT_CMP(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];
+ tpf = FRACT_CMP(tpf, <, tpf_min) ? tpf_min : tpf;
+ tpf = FRACT_CMP(tpf, >, tpf_max) ? tpf_max : tpf;
+
+ /* resync the thread's timings */
+ dev->cap_seq_resync = true;
+ dev->timeperframe_vid_cap = tpf;
+ 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
new file mode 100644
index 000000000000..94079815dbc2
--- /dev/null
+++ b/drivers/media/platform/vivid/vivid-vid-cap.h
@@ -0,0 +1,71 @@
+/*
+ * vivid-vid-cap.h - video capture support functions.
+ *
+ * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef _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_cropcap(struct file *file, void *priv, struct v4l2_cropcap *cap);
+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
new file mode 100644
index 000000000000..16cd6d2d2ed6
--- /dev/null
+++ b/drivers/media/platform/vivid/vivid-vid-common.c
@@ -0,0 +1,571 @@
+/*
+ * vivid-vid-common.c - common video support functions.
+ *
+ * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/videodev2.h>
+#include <linux/v4l2-dv-timings.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-dv-timings.h>
+
+#include "vivid-core.h"
+#include "vivid-vid-common.h"
+
+const struct v4l2_dv_timings_cap vivid_dv_timings_cap = {
+ .type = V4L2_DV_BT_656_1120,
+ /* keep this initialization for compatibility with GCC < 4.4.6 */
+ .reserved = { 0 },
+ V4L2_INIT_BT_TIMINGS(0, MAX_WIDTH, 0, MAX_HEIGHT, 25000000, 600000000,
+ V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT,
+ V4L2_DV_BT_CAP_PROGRESSIVE | V4L2_DV_BT_CAP_INTERLACED)
+};
+
+/* ------------------------------------------------------------------
+ Basic structures
+ ------------------------------------------------------------------*/
+
+struct vivid_fmt vivid_formats[] = {
+ {
+ .name = "4:2:2, packed, YUYV",
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .depth = 16,
+ .is_yuv = true,
+ .planes = 1,
+ .data_offset = { PLANE0_DATA_OFFSET, 0 },
+ },
+ {
+ .name = "4:2:2, packed, UYVY",
+ .fourcc = V4L2_PIX_FMT_UYVY,
+ .depth = 16,
+ .is_yuv = true,
+ .planes = 1,
+ },
+ {
+ .name = "4:2:2, packed, YVYU",
+ .fourcc = V4L2_PIX_FMT_YVYU,
+ .depth = 16,
+ .is_yuv = true,
+ .planes = 1,
+ },
+ {
+ .name = "4:2:2, packed, VYUY",
+ .fourcc = V4L2_PIX_FMT_VYUY,
+ .depth = 16,
+ .is_yuv = true,
+ .planes = 1,
+ },
+ {
+ .name = "RGB565 (LE)",
+ .fourcc = V4L2_PIX_FMT_RGB565, /* gggbbbbb rrrrrggg */
+ .depth = 16,
+ .planes = 1,
+ .can_do_overlay = true,
+ },
+ {
+ .name = "RGB565 (BE)",
+ .fourcc = V4L2_PIX_FMT_RGB565X, /* rrrrrggg gggbbbbb */
+ .depth = 16,
+ .planes = 1,
+ .can_do_overlay = true,
+ },
+ {
+ .name = "RGB555 (LE)",
+ .fourcc = V4L2_PIX_FMT_RGB555, /* gggbbbbb arrrrrgg */
+ .depth = 16,
+ .planes = 1,
+ .can_do_overlay = true,
+ },
+ {
+ .name = "XRGB555 (LE)",
+ .fourcc = V4L2_PIX_FMT_XRGB555, /* gggbbbbb arrrrrgg */
+ .depth = 16,
+ .planes = 1,
+ .can_do_overlay = true,
+ },
+ {
+ .name = "ARGB555 (LE)",
+ .fourcc = V4L2_PIX_FMT_ARGB555, /* gggbbbbb arrrrrgg */
+ .depth = 16,
+ .planes = 1,
+ .can_do_overlay = true,
+ .alpha_mask = 0x8000,
+ },
+ {
+ .name = "RGB555 (BE)",
+ .fourcc = V4L2_PIX_FMT_RGB555X, /* arrrrrgg gggbbbbb */
+ .depth = 16,
+ .planes = 1,
+ .can_do_overlay = true,
+ },
+ {
+ .name = "RGB24 (LE)",
+ .fourcc = V4L2_PIX_FMT_RGB24, /* rgb */
+ .depth = 24,
+ .planes = 1,
+ },
+ {
+ .name = "RGB24 (BE)",
+ .fourcc = V4L2_PIX_FMT_BGR24, /* bgr */
+ .depth = 24,
+ .planes = 1,
+ },
+ {
+ .name = "RGB32 (LE)",
+ .fourcc = V4L2_PIX_FMT_RGB32, /* argb */
+ .depth = 32,
+ .planes = 1,
+ },
+ {
+ .name = "RGB32 (BE)",
+ .fourcc = V4L2_PIX_FMT_BGR32, /* bgra */
+ .depth = 32,
+ .planes = 1,
+ },
+ {
+ .name = "XRGB32 (LE)",
+ .fourcc = V4L2_PIX_FMT_XRGB32, /* argb */
+ .depth = 32,
+ .planes = 1,
+ },
+ {
+ .name = "XRGB32 (BE)",
+ .fourcc = V4L2_PIX_FMT_XBGR32, /* bgra */
+ .depth = 32,
+ .planes = 1,
+ },
+ {
+ .name = "ARGB32 (LE)",
+ .fourcc = V4L2_PIX_FMT_ARGB32, /* argb */
+ .depth = 32,
+ .planes = 1,
+ .alpha_mask = 0x000000ff,
+ },
+ {
+ .name = "ARGB32 (BE)",
+ .fourcc = V4L2_PIX_FMT_ABGR32, /* bgra */
+ .depth = 32,
+ .planes = 1,
+ .alpha_mask = 0xff000000,
+ },
+ {
+ .name = "4:2:2, planar, YUV",
+ .fourcc = V4L2_PIX_FMT_NV16M,
+ .depth = 8,
+ .is_yuv = true,
+ .planes = 2,
+ .data_offset = { PLANE0_DATA_OFFSET, 0 },
+ },
+ {
+ .name = "4:2:2, planar, YVU",
+ .fourcc = V4L2_PIX_FMT_NV61M,
+ .depth = 8,
+ .is_yuv = true,
+ .planes = 2,
+ .data_offset = { 0, PLANE0_DATA_OFFSET },
+ },
+};
+
+/* There are 2 multiplanar formats in the list */
+#define VIVID_MPLANAR_FORMATS 2
+
+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->planes == 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;
+ if (vivid_is_svid_cap(dev) && vivid_is_svid_out(dev)) {
+ if (!(dev->std_cap & V4L2_STD_525_60) !=
+ !(dev->std_out & V4L2_STD_525_60))
+ return false;
+ return true;
+ }
+ if (vivid_is_hdmi_cap(dev) && vivid_is_hdmi_out(dev))
+ return true;
+ return false;
+}
+
+void vivid_send_source_change(struct vivid_dev *dev, unsigned type)
+{
+ struct v4l2_event ev = {
+ .type = V4L2_EVENT_SOURCE_CHANGE,
+ .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION,
+ };
+ unsigned i;
+
+ for (i = 0; i < dev->num_inputs; i++) {
+ ev.id = i;
+ if (dev->input_type[i] == type) {
+ if (video_is_registered(&dev->vid_cap_dev) && dev->has_vid_cap)
+ v4l2_event_queue(&dev->vid_cap_dev, &ev);
+ if (video_is_registered(&dev->vbi_cap_dev) && dev->has_vbi_cap)
+ v4l2_event_queue(&dev->vbi_cap_dev, &ev);
+ }
+ }
+}
+
+/*
+ * Conversion function that converts a single-planar format to a
+ * single-plane multiplanar format.
+ */
+void fmt_sp2mp(const struct v4l2_format *sp_fmt, struct v4l2_format *mp_fmt)
+{
+ struct v4l2_pix_format_mplane *mp = &mp_fmt->fmt.pix_mp;
+ struct v4l2_plane_pix_format *ppix = &mp->plane_fmt[0];
+ const struct v4l2_pix_format *pix = &sp_fmt->fmt.pix;
+ bool is_out = sp_fmt->type == V4L2_BUF_TYPE_VIDEO_OUTPUT;
+
+ memset(mp->reserved, 0, sizeof(mp->reserved));
+ mp_fmt->type = is_out ? V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE :
+ V4L2_CAP_VIDEO_CAPTURE_MPLANE;
+ mp->width = pix->width;
+ mp->height = pix->height;
+ mp->pixelformat = pix->pixelformat;
+ mp->field = pix->field;
+ mp->colorspace = pix->colorspace;
+ mp->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->sizeimage = ppix->sizeimage;
+ pix->bytesperline = ppix->bytesperline;
+ pix->flags = mp->flags;
+ return ret;
+}
+
+/* v4l2_rect helper function: copy the width/height values */
+void rect_set_size_to(struct v4l2_rect *r, const struct v4l2_rect *size)
+{
+ r->width = size->width;
+ r->height = size->height;
+}
+
+/* v4l2_rect helper function: width and height of r should be >= min_size */
+void rect_set_min_size(struct v4l2_rect *r, const struct v4l2_rect *min_size)
+{
+ if (r->width < min_size->width)
+ r->width = min_size->width;
+ if (r->height < min_size->height)
+ r->height = min_size->height;
+}
+
+/* v4l2_rect helper function: width and height of r should be <= max_size */
+void rect_set_max_size(struct v4l2_rect *r, const struct v4l2_rect *max_size)
+{
+ if (r->width > max_size->width)
+ r->width = max_size->width;
+ if (r->height > max_size->height)
+ r->height = max_size->height;
+}
+
+/* v4l2_rect helper function: r should be inside boundary */
+void rect_map_inside(struct v4l2_rect *r, const struct v4l2_rect *boundary)
+{
+ rect_set_max_size(r, boundary);
+ if (r->left < boundary->left)
+ r->left = boundary->left;
+ if (r->top < boundary->top)
+ r->top = boundary->top;
+ if (r->left + r->width > boundary->width)
+ r->left = boundary->width - r->width;
+ if (r->top + r->height > boundary->height)
+ r->top = boundary->height - r->height;
+}
+
+/* v4l2_rect helper function: return true if r1 has the same size as r2 */
+bool rect_same_size(const struct v4l2_rect *r1, const struct v4l2_rect *r2)
+{
+ return r1->width == r2->width && r1->height == r2->height;
+}
+
+/* v4l2_rect helper function: calculate the intersection of two rects */
+struct v4l2_rect rect_intersect(const struct v4l2_rect *a, const struct v4l2_rect *b)
+{
+ struct v4l2_rect r;
+ int right, bottom;
+
+ r.top = max(a->top, b->top);
+ r.left = max(a->left, b->left);
+ bottom = min(a->top + a->height, b->top + b->height);
+ right = min(a->left + a->width, b->left + b->width);
+ r.height = max(0, bottom - r.top);
+ r.width = max(0, right - r.left);
+ return r;
+}
+
+/*
+ * v4l2_rect helper function: scale rect r by to->width / from->width and
+ * to->height / from->height.
+ */
+void rect_scale(struct v4l2_rect *r, const struct v4l2_rect *from,
+ const struct v4l2_rect *to)
+{
+ if (from->width == 0 || from->height == 0) {
+ r->left = r->top = r->width = r->height = 0;
+ return;
+ }
+ r->left = (((r->left - from->left) * to->width) / from->width) & ~1;
+ r->width = ((r->width * to->width) / from->width) & ~1;
+ r->top = ((r->top - from->top) * to->height) / from->height;
+ r->height = (r->height * to->height) / from->height;
+}
+
+bool rect_overlap(const struct v4l2_rect *r1, const struct v4l2_rect *r2)
+{
+ /*
+ * IF the left side of r1 is to the right of the right side of r2 OR
+ * the left side of r2 is to the right of the right side of r1 THEN
+ * they do not overlap.
+ */
+ if (r1->left >= r2->left + r2->width ||
+ r2->left >= r1->left + r1->width)
+ return false;
+ /*
+ * IF the top side of r1 is below the bottom of r2 OR
+ * the top side of r2 is below the bottom of r1 THEN
+ * they do not overlap.
+ */
+ if (r1->top >= r2->top + r2->height ||
+ r2->top >= r1->top + r1->height)
+ return false;
+ return true;
+}
+int vivid_vid_adjust_sel(unsigned flags, struct v4l2_rect *r)
+{
+ unsigned w = r->width;
+ unsigned h = r->height;
+
+ 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;
+ r->left &= ~1;
+ r->top &= ~1;
+ 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];
+
+ strlcpy(f->description, fmt->name, sizeof(f->description));
+ f->pixelformat = fmt->fourcc;
+ return 0;
+}
+
+int vidioc_enum_fmt_vid_mplane(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ struct vivid_dev *dev = video_drvdata(file);
+
+ if (!dev->multiplanar)
+ return -ENOTTY;
+ return vivid_enum_fmt_vid(file, priv, f);
+}
+
+int vidioc_enum_fmt_vid(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ struct vivid_dev *dev = video_drvdata(file);
+
+ if (dev->multiplanar)
+ return -ENOTTY;
+ return vivid_enum_fmt_vid(file, priv, f);
+}
+
+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;
+ } 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;
+ } 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);
+
+ 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;
+ } else {
+ if (edid->pad >= dev->num_outputs)
+ return -EINVAL;
+ if (dev->output_type[edid->pad] != HDMI)
+ return -EINVAL;
+ }
+ 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->start_block + edid->blocks > dev->edid_blocks)
+ edid->blocks = dev->edid_blocks - edid->start_block;
+ memcpy(edid->edid, dev->edid, 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
new file mode 100644
index 000000000000..3ec4fa85c9b9
--- /dev/null
+++ b/drivers/media/platform/vivid/vivid-vid-common.h
@@ -0,0 +1,61 @@
+/*
+ * vivid-vid-common.h - common video support functions.
+ *
+ * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef _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);
+
+bool rect_overlap(const struct v4l2_rect *r1, const struct v4l2_rect *r2);
+void rect_set_size_to(struct v4l2_rect *r, const struct v4l2_rect *size);
+void rect_set_min_size(struct v4l2_rect *r, const struct v4l2_rect *min_size);
+void rect_set_max_size(struct v4l2_rect *r, const struct v4l2_rect *max_size);
+void rect_map_inside(struct v4l2_rect *r, const struct v4l2_rect *boundary);
+bool rect_same_size(const struct v4l2_rect *r1, const struct v4l2_rect *r2);
+struct v4l2_rect rect_intersect(const struct v4l2_rect *a, const struct v4l2_rect *b);
+void rect_scale(struct v4l2_rect *r, const struct v4l2_rect *from,
+ const struct v4l2_rect *to);
+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_enum_fmt_vid_mplane(struct file *file, void *priv, struct v4l2_fmtdesc *f);
+int vidioc_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
new file mode 100644
index 000000000000..69c2dbd2d165
--- /dev/null
+++ b/drivers/media/platform/vivid/vivid-vid-out.c
@@ -0,0 +1,1146 @@
+/*
+ * vivid-vid-out.c - video output support functions.
+ *
+ * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/videodev2.h>
+#include <linux/v4l2-dv-timings.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-dv-timings.h>
+
+#include "vivid-core.h"
+#include "vivid-vid-common.h"
+#include "vivid-kthread-out.h"
+#include "vivid-vid-out.h"
+
+static int vid_out_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
+ unsigned *nbuffers, unsigned *nplanes,
+ unsigned sizes[], void *alloc_ctxs[])
+{
+ struct vivid_dev *dev = vb2_get_drv_priv(vq);
+ unsigned planes = dev->fmt_out->planes;
+ unsigned h = dev->fmt_out_rect.height;
+ unsigned size = dev->bytesperline_out[0] * h;
+
+ 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 (fmt) {
+ const struct v4l2_pix_format_mplane *mp;
+ struct v4l2_format mp_fmt;
+
+ if (!V4L2_TYPE_IS_MULTIPLANAR(fmt->type)) {
+ fmt_sp2mp(fmt, &mp_fmt);
+ fmt = &mp_fmt;
+ }
+ mp = &fmt->fmt.pix_mp;
+ /*
+ * Check if the number of planes in the specified format match
+ * the number of planes in the current format. You can't mix that.
+ */
+ if (mp->num_planes != planes)
+ return -EINVAL;
+ sizes[0] = mp->plane_fmt[0].sizeimage;
+ if (planes == 2) {
+ sizes[1] = mp->plane_fmt[1].sizeimage;
+ if (sizes[0] < dev->bytesperline_out[0] * h ||
+ sizes[1] < dev->bytesperline_out[1] * h)
+ return -EINVAL;
+ } else if (sizes[0] < size) {
+ return -EINVAL;
+ }
+ } else {
+ if (planes == 2) {
+ sizes[0] = dev->bytesperline_out[0] * h;
+ sizes[1] = dev->bytesperline_out[1] * h;
+ } else {
+ sizes[0] = size;
+ }
+ }
+
+ if (vq->num_buffers + *nbuffers < 2)
+ *nbuffers = 2 - vq->num_buffers;
+
+ *nplanes = planes;
+
+ /*
+ * videobuf2-vmalloc allocator is context-less so no need to set
+ * alloc_ctxs array.
+ */
+
+ if (planes == 2)
+ dprintk(dev, 1, "%s, count=%d, sizes=%u, %u\n", __func__,
+ *nbuffers, sizes[0], sizes[1]);
+ else
+ dprintk(dev, 1, "%s, count=%d, size=%u\n", __func__,
+ *nbuffers, sizes[0]);
+ return 0;
+}
+
+static int vid_out_buf_prepare(struct vb2_buffer *vb)
+{
+ struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
+ unsigned long size;
+ unsigned planes = dev->fmt_out->planes;
+ unsigned 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;
+ }
+
+ if (dev->field_out != V4L2_FIELD_ALTERNATE)
+ vb->v4l2_buf.field = dev->field_out;
+ else if (vb->v4l2_buf.field != V4L2_FIELD_TOP &&
+ vb->v4l2_buf.field != V4L2_FIELD_BOTTOM)
+ return -EINVAL;
+
+ for (p = 0; p < planes; p++) {
+ size = dev->bytesperline_out[p] * dev->fmt_out_rect.height +
+ vb->v4l2_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 < %lu)\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 vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
+ struct vivid_buffer *buf = container_of(vb, 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);
+
+ if (dev->kthread_vid_out)
+ return 0;
+
+ 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_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;
+}
+
+const struct vb2_ops vivid_vid_out_qops = {
+ .queue_setup = vid_out_queue_setup,
+ .buf_prepare = vid_out_buf_prepare,
+ .buf_queue = vid_out_buf_queue,
+ .start_streaming = vid_out_start_streaming,
+ .stop_streaming = vid_out_stop_streaming,
+ .wait_prepare = vivid_unlock,
+ .wait_finish = vivid_lock,
+};
+
+/*
+ * 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;
+
+ 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);
+ dev->timeperframe_vid_out = (struct v4l2_fract) {
+ size / 100, (u32)bt->pixelclock / 100
+ };
+ if (bt->interlaced)
+ dev->field_out = V4L2_FIELD_ALTERNATE;
+ else
+ dev->field_out = V4L2_FIELD_NONE;
+ if (!dev->dvi_d_out && (bt->standards & V4L2_DV_BT_STD_CEA861)) {
+ 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->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;
+ dev->bytesperline_out[0] = (dev->sink_rect.width * dev->fmt_out->depth) / 8;
+ if (dev->fmt_out->planes == 2)
+ dev->bytesperline_out[1] = (dev->sink_rect.width * dev->fmt_out->depth) / 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;
+ unsigned p;
+
+ mp->width = dev->fmt_out_rect.width;
+ mp->height = dev->fmt_out_rect.height;
+ mp->field = dev->field_out;
+ mp->pixelformat = dev->fmt_out->fourcc;
+ mp->colorspace = dev->colorspace_out;
+ mp->num_planes = dev->fmt_out->planes;
+ 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;
+ }
+ 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 };
+
+ rect_set_min_size(&r, &vivid_min_rect);
+ 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 };
+
+ rect_set_max_size(&r, &max_r);
+ } else if (!dev->has_scaler_out && dev->has_compose_out && !dev->has_crop_out) {
+ rect_set_max_size(&r, &dev->sink_rect);
+ } else if (!dev->has_scaler_out && !dev->has_compose_out) {
+ rect_set_min_size(&r, &dev->sink_rect);
+ }
+ mp->width = r.width;
+ mp->height = r.height / factor;
+ }
+
+ /* This driver supports custom bytesperline values */
+
+ /* Calculate the minimum supported bytesperline value */
+ bytesperline = (mp->width * fmt->depth) >> 3;
+ /* Calculate the maximum supported bytesperline value */
+ max_bpl = (MAX_ZOOM * MAX_WIDTH * fmt->depth) >> 3;
+ mp->num_planes = fmt->planes;
+ for (p = 0; p < mp->num_planes; p++) {
+ 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;
+ memset(pfmt[p].reserved, 0, sizeof(pfmt[p].reserved));
+ }
+ if (vivid_is_svid_out(dev))
+ mp->colorspace = V4L2_COLORSPACE_SMPTE170M;
+ else if (dev->dvi_d_out || !(bt->standards & V4L2_DV_BT_STD_CEA861))
+ mp->colorspace = V4L2_COLORSPACE_SRGB;
+ 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_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;
+
+ 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)
+ 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
+ };
+
+ rect_set_min_size(compose, &min_r);
+ rect_set_max_size(compose, &max_r);
+ 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
+ };
+
+ rect_set_min_size(compose, &min_r);
+ rect_set_max_size(compose, &max_r);
+ rect_map_inside(compose, &dev->compose_bounds_out);
+ }
+ } else if (dev->has_compose_out && !dev->has_crop_out) {
+ rect_set_size_to(crop, &r);
+ r.height *= factor;
+ rect_set_size_to(compose, &r);
+ rect_map_inside(compose, &dev->compose_bounds_out);
+ } else if (!dev->has_compose_out) {
+ rect_map_inside(crop, &r);
+ r.height /= factor;
+ rect_set_size_to(compose, &r);
+ } else {
+ r.height *= factor;
+ rect_set_max_size(compose, &r);
+ rect_map_inside(compose, &dev->compose_bounds_out);
+ crop->top *= factor;
+ crop->height *= factor;
+ rect_set_size_to(crop, compose);
+ rect_map_inside(crop, &r);
+ crop->top /= factor;
+ crop->height /= factor;
+ }
+ } else {
+ struct v4l2_rect r = { 0, 0, mp->width, mp->height };
+
+ rect_set_size_to(crop, &r);
+ r.height /= factor;
+ rect_set_size_to(compose, &r);
+ }
+
+ dev->fmt_out_rect.width = mp->width;
+ dev->fmt_out_rect.height = mp->height;
+ dev->bytesperline_out[0] = mp->plane_fmt[0].bytesperline;
+ if (mp->num_planes > 1)
+ dev->bytesperline_out[1] = mp->plane_fmt[1].bytesperline;
+ dev->field_out = mp->field;
+ if (vivid_is_svid_out(dev))
+ dev->tv_field_out = mp->field;
+
+set_colorspace:
+ dev->colorspace_out = mp->colorspace;
+ 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_compose_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;
+ rect_set_min_size(&s->r, &vivid_min_rect);
+ 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
+ };
+
+ 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
+ };
+
+ rect_set_min_size(compose, &min_rect);
+ rect_set_max_size(compose, &max_rect);
+ rect_map_inside(compose, &dev->compose_bounds_out);
+ }
+ } else if (dev->has_compose_out) {
+ s->r.top *= factor;
+ s->r.height *= factor;
+ rect_set_max_size(&s->r, &dev->sink_rect);
+ rect_set_size_to(compose, &s->r);
+ rect_map_inside(compose, &dev->compose_bounds_out);
+ s->r.top /= factor;
+ s->r.height /= factor;
+ } else {
+ rect_set_size_to(&s->r, &dev->sink_rect);
+ s->r.height /= factor;
+ }
+ 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;
+ rect_set_min_size(&s->r, &vivid_min_rect);
+ rect_set_max_size(&s->r, &dev->sink_rect);
+ 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
+ };
+
+ rect_set_min_size(&fmt, &min_rect);
+ if (!dev->has_crop_out)
+ rect_set_max_size(&fmt, &max_rect);
+ if (!rect_same_size(&dev->fmt_out_rect, &fmt) &&
+ vb2_is_busy(&dev->vb_vid_out_q))
+ return -EBUSY;
+ if (dev->has_crop_out) {
+ rect_set_min_size(crop, &min_rect);
+ 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;
+
+ rect_set_min_size(&fmt, &s->r);
+ if (!rect_same_size(&dev->fmt_out_rect, &fmt) &&
+ vb2_is_busy(&dev->vb_vid_out_q))
+ return -EBUSY;
+ dev->fmt_out_rect = fmt;
+ rect_set_size_to(crop, &s->r);
+ rect_map_inside(crop, &dev->fmt_out_rect);
+ } else {
+ if (!rect_same_size(&s->r, &dev->fmt_out_rect) &&
+ vb2_is_busy(&dev->vb_vid_out_q))
+ return -EBUSY;
+ rect_set_size_to(&dev->fmt_out_rect, &s->r);
+ rect_set_size_to(crop, &s->r);
+ crop->height /= factor;
+ 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)) {
+ kfree(dev->bitmap_out);
+ dev->bitmap_out = NULL;
+ }
+ *compose = s->r;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int vivid_vid_out_cropcap(struct file *file, void *priv,
+ struct v4l2_cropcap *cap)
+{
+ struct vivid_dev *dev = video_drvdata(file);
+
+ if (cap->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
+ return -EINVAL;
+
+ switch (vivid_get_pixel_aspect(dev)) {
+ case TPG_PIXEL_ASPECT_NTSC:
+ cap->pixelaspect.numerator = 11;
+ cap->pixelaspect.denominator = 10;
+ break;
+ case TPG_PIXEL_ASPECT_PAL:
+ cap->pixelaspect.numerator = 54;
+ cap->pixelaspect.denominator = 59;
+ break;
+ case TPG_PIXEL_ASPECT_SQUARE:
+ cap->pixelaspect.numerator = 1;
+ cap->pixelaspect.denominator = 1;
+ 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 (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 = memdup_user(win->bitmap, bitmap_size);
+
+ if (IS_ERR(new_bitmap))
+ return PTR_ERR(new_bitmap);
+ }
+
+ dev->overlay_out_top = win->w.top;
+ dev->overlay_out_left = win->w.left;
+ kfree(dev->bitmap_out);
+ dev->bitmap_out = new_bitmap;
+ dev->clipcount_out = win->clipcount;
+ if (dev->clipcount_out)
+ memcpy(dev->clips_out, dev->try_clips_out, clips_size);
+ dev->chromakey_out = win->chromakey;
+ dev->global_alpha_out = win->global_alpha;
+ return ret;
+}
+
+int vivid_vid_out_overlay(struct file *file, void *fh, unsigned i)
+{
+ struct vivid_dev *dev = video_drvdata(file);
+
+ if (i && !dev->fmt_out->can_do_overlay) {
+ dprintk(dev, 1, "unsupported output format for output overlay\n");
+ return -EINVAL;
+ }
+
+ dev->overlay_out_enabled = i;
+ return 0;
+}
+
+int vivid_vid_out_g_fbuf(struct file *file, void *fh,
+ struct v4l2_framebuffer *a)
+{
+ struct vivid_dev *dev = video_drvdata(file);
+
+ a->capability = V4L2_FBUF_CAP_EXTERNOVERLAY |
+ V4L2_FBUF_CAP_BITMAP_CLIPPING |
+ V4L2_FBUF_CAP_LIST_CLIPPING |
+ V4L2_FBUF_CAP_CHROMAKEY |
+ V4L2_FBUF_CAP_SRC_CHROMAKEY |
+ V4L2_FBUF_CAP_GLOBAL_ALPHA |
+ V4L2_FBUF_CAP_LOCAL_ALPHA |
+ V4L2_FBUF_CAP_LOCAL_INV_ALPHA;
+ a->flags = V4L2_FBUF_FLAG_OVERLAY | dev->fbuf_out_flags;
+ a->base = (void *)dev->video_pbase;
+ a->fmt.width = dev->display_width;
+ a->fmt.height = dev->display_height;
+ if (dev->fb_defined.green.length == 5)
+ a->fmt.pixelformat = V4L2_PIX_FMT_ARGB555;
+ else
+ a->fmt.pixelformat = V4L2_PIX_FMT_RGB565;
+ a->fmt.bytesperline = dev->display_byte_stride;
+ a->fmt.sizeimage = a->fmt.height * a->fmt.bytesperline;
+ a->fmt.field = V4L2_FIELD_NONE;
+ a->fmt.colorspace = V4L2_COLORSPACE_SRGB;
+ a->fmt.priv = 0;
+ return 0;
+}
+
+int vivid_vid_out_s_fbuf(struct file *file, void *fh,
+ const struct v4l2_framebuffer *a)
+{
+ struct vivid_dev *dev = video_drvdata(file);
+ const unsigned chroma_flags = V4L2_FBUF_FLAG_CHROMAKEY |
+ V4L2_FBUF_FLAG_SRC_CHROMAKEY;
+ const unsigned alpha_flags = V4L2_FBUF_FLAG_GLOBAL_ALPHA |
+ V4L2_FBUF_FLAG_LOCAL_ALPHA |
+ V4L2_FBUF_FLAG_LOCAL_INV_ALPHA;
+
+
+ if ((a->flags & chroma_flags) == chroma_flags)
+ return -EINVAL;
+ switch (a->flags & alpha_flags) {
+ case 0:
+ case V4L2_FBUF_FLAG_GLOBAL_ALPHA:
+ case V4L2_FBUF_FLAG_LOCAL_ALPHA:
+ case V4L2_FBUF_FLAG_LOCAL_INV_ALPHA:
+ break;
+ default:
+ return -EINVAL;
+ }
+ dev->fbuf_out_flags &= ~(chroma_flags | alpha_flags);
+ dev->fbuf_out_flags = a->flags & (chroma_flags | alpha_flags);
+ return 0;
+}
+
+static const struct v4l2_audioout vivid_audio_outputs[] = {
+ { 0, "Line-Out 1" },
+ { 1, "Line-Out 2" },
+};
+
+int vidioc_enum_output(struct file *file, void *priv,
+ struct v4l2_output *out)
+{
+ struct vivid_dev *dev = video_drvdata(file);
+
+ if (out->index >= dev->num_outputs)
+ return -EINVAL;
+
+ out->type = V4L2_OUTPUT_TYPE_ANALOG;
+ switch (dev->output_type[out->index]) {
+ case SVID:
+ snprintf(out->name, sizeof(out->name), "S-Video %u",
+ dev->output_name_counter[out->index]);
+ out->std = V4L2_STD_ALL;
+ if (dev->has_audio_outputs)
+ out->audioset = (1 << ARRAY_SIZE(vivid_audio_outputs)) - 1;
+ out->capabilities = V4L2_OUT_CAP_STD;
+ break;
+ case HDMI:
+ snprintf(out->name, sizeof(out->name), "HDMI %u",
+ dev->output_name_counter[out->index]);
+ out->capabilities = V4L2_OUT_CAP_DV_TIMINGS;
+ break;
+ }
+ return 0;
+}
+
+int vidioc_g_output(struct file *file, void *priv, unsigned *o)
+{
+ struct vivid_dev *dev = video_drvdata(file);
+
+ *o = dev->output;
+ return 0;
+}
+
+int vidioc_s_output(struct file *file, void *priv, unsigned o)
+{
+ struct vivid_dev *dev = video_drvdata(file);
+
+ if (o >= dev->num_outputs)
+ return -EINVAL;
+
+ if (o == dev->output)
+ return 0;
+
+ if (vb2_is_busy(&dev->vb_vid_out_q) || vb2_is_busy(&dev->vb_vbi_out_q))
+ return -EBUSY;
+
+ dev->output = o;
+ dev->tv_audio_output = 0;
+ if (dev->output_type[o] == SVID)
+ dev->vid_out_dev.tvnorms = V4L2_STD_ALL;
+ else
+ dev->vid_out_dev.tvnorms = 0;
+
+ dev->vbi_out_dev.tvnorms = dev->vid_out_dev.tvnorms;
+ vivid_update_format_out(dev);
+ 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;
+}
+
+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 (vb2_is_busy(&dev->vb_vid_out_q))
+ return -EBUSY;
+ if (!v4l2_find_dv_timings_cap(timings, &vivid_dv_timings_cap,
+ 0, NULL, NULL))
+ return -EINVAL;
+ if (v4l2_match_dv_timings(timings, &dev->dv_timings_out, 0))
+ return 0;
+ 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_CTRL:
+ return v4l2_ctrl_subscribe_event(fh, sub);
+ case V4L2_EVENT_SOURCE_CHANGE:
+ if (fh->vdev->vfl_dir == VFL_DIR_RX)
+ return v4l2_src_change_event_subscribe(fh, sub);
+ break;
+ default:
+ break;
+ }
+ return -EINVAL;
+}
diff --git a/drivers/media/platform/vivid/vivid-vid-out.h b/drivers/media/platform/vivid/vivid-vid-out.h
new file mode 100644
index 000000000000..dfa84db184ed
--- /dev/null
+++ b/drivers/media/platform/vivid/vivid-vid-out.h
@@ -0,0 +1,56 @@
+/*
+ * vivid-vid-out.h - video output support functions.
+ *
+ * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef _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_cropcap(struct file *file, void *fh, struct v4l2_cropcap *cap);
+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