summaryrefslogtreecommitdiff
path: root/drivers/media
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media')
-rw-r--r--drivers/media/common/Kconfig4
-rw-r--r--drivers/media/common/Makefile3
-rw-r--r--drivers/media/common/saa7146/Kconfig10
-rw-r--r--drivers/media/common/saa7146/Makefile6
-rw-r--r--drivers/media/common/saa7146/saa7146_core.c578
-rw-r--r--drivers/media/common/saa7146/saa7146_fops.c658
-rw-r--r--drivers/media/common/saa7146/saa7146_hlp.c1046
-rw-r--r--drivers/media/common/saa7146/saa7146_i2c.c421
-rw-r--r--drivers/media/common/saa7146/saa7146_vbi.c498
-rw-r--r--drivers/media/common/saa7146/saa7146_video.c1286
-rw-r--r--drivers/media/common/uvc.c183
-rw-r--r--drivers/media/common/videobuf2/videobuf2-core.c30
-rw-r--r--drivers/media/common/videobuf2/videobuf2-dma-contig.c2
-rw-r--r--drivers/media/common/videobuf2/videobuf2-vmalloc.c2
-rw-r--r--drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.c4
-rw-r--r--drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.c14
-rw-r--r--drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.c14
-rw-r--r--drivers/media/dvb-frontends/drx39xyj/drxj.c9
-rw-r--r--drivers/media/dvb-frontends/dvb-pll.c5
-rw-r--r--drivers/media/dvb-frontends/m88ds3103.c6
-rw-r--r--drivers/media/dvb-frontends/mb86a16.c9
-rw-r--r--drivers/media/dvb-frontends/mn88443x.c6
-rw-r--r--drivers/media/dvb-frontends/tc90522.c6
-rw-r--r--drivers/media/i2c/Kconfig40
-rw-r--r--drivers/media/i2c/Makefile3
-rw-r--r--drivers/media/i2c/adv7180.c6
-rw-r--r--drivers/media/i2c/adv7604.c6
-rw-r--r--drivers/media/i2c/ak7375.c38
-rw-r--r--drivers/media/i2c/cs53l32a.c6
-rw-r--r--drivers/media/i2c/imx219.c311
-rw-r--r--drivers/media/i2c/imx290.c1019
-rw-r--r--drivers/media/i2c/imx296.c1172
-rw-r--r--drivers/media/i2c/imx415.c1300
-rw-r--r--drivers/media/i2c/ir-kbd-i2c.c5
-rw-r--r--drivers/media/i2c/max9286.c463
-rw-r--r--drivers/media/i2c/msp3400-driver.c5
-rw-r--r--drivers/media/i2c/mt9p031.c6
-rw-r--r--drivers/media/i2c/mt9v032.c6
-rw-r--r--drivers/media/i2c/ov2740.c4
-rw-r--r--drivers/media/i2c/ov5640.c86
-rw-r--r--drivers/media/i2c/ov5670.c312
-rw-r--r--drivers/media/i2c/ov5675.c198
-rw-r--r--drivers/media/i2c/ov7670.c8
-rw-r--r--drivers/media/i2c/ov772x.c3
-rw-r--r--drivers/media/i2c/ov8858.c2008
-rw-r--r--drivers/media/i2c/ov9282.c9
-rw-r--r--drivers/media/i2c/s5c73m3/s5c73m3-core.c22
-rw-r--r--drivers/media/i2c/s5c73m3/s5c73m3-ctrls.c1
-rw-r--r--drivers/media/i2c/s5c73m3/s5c73m3.h1
-rw-r--r--drivers/media/i2c/saa7115.c6
-rw-r--r--drivers/media/i2c/saa7127.c6
-rw-r--r--drivers/media/i2c/st-vgxy61.c4
-rw-r--r--drivers/media/i2c/tc358746.c9
-rw-r--r--drivers/media/i2c/tda1997x.c6
-rw-r--r--drivers/media/i2c/tvaudio.c5
-rw-r--r--drivers/media/i2c/tvp514x.c6
-rw-r--r--drivers/media/i2c/video-i2c.c6
-rw-r--r--drivers/media/mc/mc-entity.c86
-rw-r--r--drivers/media/pci/Kconfig2
-rw-r--r--drivers/media/pci/Makefile4
-rw-r--r--drivers/media/pci/intel/ipu3/cio2-bridge.c27
-rw-r--r--drivers/media/pci/intel/ipu3/cio2-bridge.h5
-rw-r--r--drivers/media/pci/intel/ipu3/ipu3-cio2-main.c3
-rw-r--r--drivers/media/pci/saa7134/saa7134-core.c2
-rw-r--r--drivers/media/pci/saa7146/Kconfig39
-rw-r--r--drivers/media/pci/saa7146/Makefile6
-rw-r--r--drivers/media/pci/saa7146/hexium_gemini.c425
-rw-r--r--drivers/media/pci/saa7146/hexium_orion.c496
-rw-r--r--drivers/media/pci/saa7146/mxb.c873
-rw-r--r--drivers/media/pci/ttpci/Kconfig86
-rw-r--r--drivers/media/pci/ttpci/Makefile13
-rw-r--r--drivers/media/pci/ttpci/budget-av.c1622
-rw-r--r--drivers/media/pci/ttpci/budget-ci.c1574
-rw-r--r--drivers/media/pci/ttpci/budget-core.c603
-rw-r--r--drivers/media/pci/ttpci/budget.c883
-rw-r--r--drivers/media/pci/ttpci/budget.h129
-rw-r--r--drivers/media/platform/amphion/venc.c18
-rw-r--r--drivers/media/platform/amphion/vpu_color.c6
-rw-r--r--drivers/media/platform/chips-media/imx-vdoa.c6
-rw-r--r--drivers/media/platform/marvell/mmp-driver.c2
-rw-r--r--drivers/media/platform/mediatek/mdp3/Kconfig7
-rw-r--r--drivers/media/platform/mediatek/mdp3/mtk-mdp3-comp.c3
-rw-r--r--drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c7
-rw-r--r--drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_pm.c6
-rw-r--r--drivers/media/platform/mediatek/vcodec/venc/venc_h264_if.c4
-rw-r--r--drivers/media/platform/microchip/microchip-isc-base.c109
-rw-r--r--drivers/media/platform/nxp/dw100/dw100.c2
-rw-r--r--drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c35
-rw-r--r--drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.h4
-rw-r--r--drivers/media/platform/nxp/imx-mipi-csis.c252
-rw-r--r--drivers/media/platform/nxp/imx-pxp.c359
-rw-r--r--drivers/media/platform/nxp/imx7-media-csi.c239
-rw-r--r--drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c3
-rw-r--r--drivers/media/platform/qcom/venus/firmware.c4
-rw-r--r--drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c2
-rw-r--r--drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c2
-rw-r--r--drivers/media/platform/rockchip/rkisp1/rkisp1-capture.c15
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-is.h3
-rw-r--r--drivers/media/platform/samsung/exynos4-is/media-dev.h1
-rw-r--r--drivers/media/platform/samsung/s3c-camif/Kconfig8
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/s5p_mfc.c4
-rw-r--r--drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c1
-rw-r--r--drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c4
-rw-r--r--drivers/media/platform/ti/cal/cal.c4
-rw-r--r--drivers/media/platform/ti/davinci/Kconfig16
-rw-r--r--drivers/media/platform/ti/davinci/Makefile3
-rw-r--r--drivers/media/platform/ti/davinci/vpbe.c840
-rw-r--r--drivers/media/platform/ti/davinci/vpbe_display.c1510
-rw-r--r--drivers/media/platform/ti/davinci/vpbe_osd.c1582
-rw-r--r--drivers/media/platform/ti/davinci/vpbe_osd_regs.h352
-rw-r--r--drivers/media/platform/ti/davinci/vpbe_venc.c676
-rw-r--r--drivers/media/platform/ti/davinci/vpbe_venc_regs.h165
-rw-r--r--drivers/media/platform/ti/davinci/vpif.c2
-rw-r--r--drivers/media/platform/ti/davinci/vpss.c529
-rw-r--r--drivers/media/platform/ti/omap3isp/isp.c9
-rw-r--r--drivers/media/platform/ti/omap3isp/ispvideo.c20
-rw-r--r--drivers/media/platform/verisilicon/hantro_drv.c2
-rw-r--r--drivers/media/platform/verisilicon/hantro_v4l2.c9
-rw-r--r--drivers/media/platform/xilinx/xilinx-dma.c28
-rw-r--r--drivers/media/radio/wl128x/fmdrv_common.c2
-rw-r--r--drivers/media/rc/Kconfig4
-rw-r--r--drivers/media/rc/ene_ir.c3
-rw-r--r--drivers/media/rc/gpio-ir-recv.c18
-rw-r--r--drivers/media/rc/ir-rx51.c6
-rw-r--r--drivers/media/rc/pwm-ir-tx.c2
-rw-r--r--drivers/media/rc/rc-main.c2
-rw-r--r--drivers/media/test-drivers/vidtv/vidtv_psi.c2
-rw-r--r--drivers/media/test-drivers/visl/visl-video.c2
-rw-r--r--drivers/media/tuners/si2157.c6
-rw-r--r--drivers/media/usb/dvb-usb-v2/af9015.c4
-rw-r--r--drivers/media/usb/go7007/go7007-v4l2.c6
-rw-r--r--drivers/media/usb/siano/smsusb.c1
-rw-r--r--drivers/media/usb/uvc/Kconfig1
-rw-r--r--drivers/media/usb/uvc/uvc_ctrl.c342
-rw-r--r--drivers/media/usb/uvc/uvc_driver.c185
-rw-r--r--drivers/media/usb/uvc/uvc_entity.c2
-rw-r--r--drivers/media/usb/uvc/uvc_status.c125
-rw-r--r--drivers/media/usb/uvc/uvc_v4l2.c111
-rw-r--r--drivers/media/usb/uvc/uvc_video.c58
-rw-r--r--drivers/media/usb/uvc/uvcvideo.h39
-rw-r--r--drivers/media/v4l2-core/v4l2-async.c4
-rw-r--r--drivers/media/v4l2-core/v4l2-ctrls-api.c2
-rw-r--r--drivers/media/v4l2-core/v4l2-fwnode.c7
-rw-r--r--drivers/media/v4l2-core/v4l2-h264.c4
-rw-r--r--drivers/media/v4l2-core/v4l2-ioctl.c25
-rw-r--r--drivers/media/v4l2-core/v4l2-jpeg.c4
-rw-r--r--drivers/media/v4l2-core/v4l2-mem2mem.c4
-rw-r--r--drivers/media/v4l2-core/v4l2-subdev-priv.h14
-rw-r--r--drivers/media/v4l2-core/v4l2-subdev.c1063
-rw-r--r--drivers/media/v4l2-core/videobuf-dma-contig.c2
-rw-r--r--drivers/media/v4l2-core/videobuf-dma-sg.c4
-rw-r--r--drivers/media/v4l2-core/videobuf-vmalloc.c2
152 files changed, 20114 insertions, 7559 deletions
diff --git a/drivers/media/common/Kconfig b/drivers/media/common/Kconfig
index 852b7d92fbdd..adcb6655385a 100644
--- a/drivers/media/common/Kconfig
+++ b/drivers/media/common/Kconfig
@@ -14,6 +14,9 @@ config TTPCI_EEPROM
tristate
depends on I2C
+config UVC_COMMON
+ tristate
+
config VIDEO_CX2341X
tristate
@@ -22,6 +25,7 @@ config VIDEO_TVEEPROM
depends on I2C
source "drivers/media/common/b2c2/Kconfig"
+source "drivers/media/common/saa7146/Kconfig"
source "drivers/media/common/siano/Kconfig"
source "drivers/media/common/v4l2-tpg/Kconfig"
source "drivers/media/common/videobuf2/Kconfig"
diff --git a/drivers/media/common/Makefile b/drivers/media/common/Makefile
index d78a0df15478..c5ab905e7c20 100644
--- a/drivers/media/common/Makefile
+++ b/drivers/media/common/Makefile
@@ -1,9 +1,10 @@
# SPDX-License-Identifier: GPL-2.0-only
-obj-y += b2c2/ siano/ v4l2-tpg/ videobuf2/
+obj-y += b2c2/ saa7146/ siano/ v4l2-tpg/ videobuf2/
# Please keep it alphabetically sorted by Kconfig name
# (e. g. LC_ALL=C sort Makefile)
obj-$(CONFIG_CYPRESS_FIRMWARE) += cypress_firmware.o
obj-$(CONFIG_TTPCI_EEPROM) += ttpci-eeprom.o
+obj-$(CONFIG_UVC_COMMON) += uvc.o
obj-$(CONFIG_VIDEO_CX2341X) += cx2341x.o
obj-$(CONFIG_VIDEO_TVEEPROM) += tveeprom.o
diff --git a/drivers/media/common/saa7146/Kconfig b/drivers/media/common/saa7146/Kconfig
new file mode 100644
index 000000000000..a0aa155e5d85
--- /dev/null
+++ b/drivers/media/common/saa7146/Kconfig
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config VIDEO_SAA7146
+ tristate
+ depends on I2C && PCI
+
+config VIDEO_SAA7146_VV
+ tristate
+ depends on VIDEO_DEV
+ select VIDEOBUF_DMA_SG
+ select VIDEO_SAA7146
diff --git a/drivers/media/common/saa7146/Makefile b/drivers/media/common/saa7146/Makefile
new file mode 100644
index 000000000000..2a6337feaec8
--- /dev/null
+++ b/drivers/media/common/saa7146/Makefile
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0-only
+saa7146-objs := saa7146_i2c.o saa7146_core.o
+saa7146_vv-objs := saa7146_fops.o saa7146_video.o saa7146_hlp.o saa7146_vbi.o
+
+obj-$(CONFIG_VIDEO_SAA7146) += saa7146.o
+obj-$(CONFIG_VIDEO_SAA7146_VV) += saa7146_vv.o
diff --git a/drivers/media/common/saa7146/saa7146_core.c b/drivers/media/common/saa7146/saa7146_core.c
new file mode 100644
index 000000000000..e50fa0ff7c5d
--- /dev/null
+++ b/drivers/media/common/saa7146/saa7146_core.c
@@ -0,0 +1,578 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ saa7146.o - driver for generic saa7146-based hardware
+
+ Copyright (C) 1998-2003 Michael Hunold <michael@mihu.de>
+
+*/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <media/drv-intf/saa7146.h>
+#include <linux/module.h>
+
+static int saa7146_num;
+
+unsigned int saa7146_debug;
+
+module_param(saa7146_debug, uint, 0644);
+MODULE_PARM_DESC(saa7146_debug, "debug level (default: 0)");
+
+#if 0
+static void dump_registers(struct saa7146_dev* dev)
+{
+ int i = 0;
+
+ pr_info(" @ %li jiffies:\n", jiffies);
+ for (i = 0; i <= 0x148; i += 4)
+ pr_info("0x%03x: 0x%08x\n", i, saa7146_read(dev, i));
+}
+#endif
+
+/****************************************************************************
+ * gpio and debi helper functions
+ ****************************************************************************/
+
+void saa7146_setgpio(struct saa7146_dev *dev, int port, u32 data)
+{
+ u32 value = 0;
+
+ BUG_ON(port > 3);
+
+ value = saa7146_read(dev, GPIO_CTRL);
+ value &= ~(0xff << (8*port));
+ value |= (data << (8*port));
+ saa7146_write(dev, GPIO_CTRL, value);
+}
+
+/* This DEBI code is based on the saa7146 Stradis driver by Nathan Laredo */
+static inline int saa7146_wait_for_debi_done_sleep(struct saa7146_dev *dev,
+ unsigned long us1, unsigned long us2)
+{
+ unsigned long timeout;
+ int err;
+
+ /* wait for registers to be programmed */
+ timeout = jiffies + usecs_to_jiffies(us1);
+ while (1) {
+ err = time_after(jiffies, timeout);
+ if (saa7146_read(dev, MC2) & 2)
+ break;
+ if (err) {
+ pr_debug("%s: %s timed out while waiting for registers getting programmed\n",
+ dev->name, __func__);
+ return -ETIMEDOUT;
+ }
+ msleep(1);
+ }
+
+ /* wait for transfer to complete */
+ timeout = jiffies + usecs_to_jiffies(us2);
+ while (1) {
+ err = time_after(jiffies, timeout);
+ if (!(saa7146_read(dev, PSR) & SPCI_DEBI_S))
+ break;
+ saa7146_read(dev, MC2);
+ if (err) {
+ DEB_S("%s: %s timed out while waiting for transfer completion\n",
+ dev->name, __func__);
+ return -ETIMEDOUT;
+ }
+ msleep(1);
+ }
+
+ return 0;
+}
+
+static inline int saa7146_wait_for_debi_done_busyloop(struct saa7146_dev *dev,
+ unsigned long us1, unsigned long us2)
+{
+ unsigned long loops;
+
+ /* wait for registers to be programmed */
+ loops = us1;
+ while (1) {
+ if (saa7146_read(dev, MC2) & 2)
+ break;
+ if (!loops--) {
+ pr_err("%s: %s timed out while waiting for registers getting programmed\n",
+ dev->name, __func__);
+ return -ETIMEDOUT;
+ }
+ udelay(1);
+ }
+
+ /* wait for transfer to complete */
+ loops = us2 / 5;
+ while (1) {
+ if (!(saa7146_read(dev, PSR) & SPCI_DEBI_S))
+ break;
+ saa7146_read(dev, MC2);
+ if (!loops--) {
+ DEB_S("%s: %s timed out while waiting for transfer completion\n",
+ dev->name, __func__);
+ return -ETIMEDOUT;
+ }
+ udelay(5);
+ }
+
+ return 0;
+}
+
+int saa7146_wait_for_debi_done(struct saa7146_dev *dev, int nobusyloop)
+{
+ if (nobusyloop)
+ return saa7146_wait_for_debi_done_sleep(dev, 50000, 250000);
+ else
+ return saa7146_wait_for_debi_done_busyloop(dev, 50000, 250000);
+}
+
+/****************************************************************************
+ * general helper functions
+ ****************************************************************************/
+
+/* this is videobuf_vmalloc_to_sg() from videobuf-dma-sg.c
+ make sure virt has been allocated with vmalloc_32(), otherwise the BUG()
+ may be triggered on highmem machines */
+static struct scatterlist* vmalloc_to_sg(unsigned char *virt, int nr_pages)
+{
+ struct scatterlist *sglist;
+ struct page *pg;
+ int i;
+
+ sglist = kmalloc_array(nr_pages, sizeof(struct scatterlist), GFP_KERNEL);
+ if (NULL == sglist)
+ return NULL;
+ sg_init_table(sglist, nr_pages);
+ for (i = 0; i < nr_pages; i++, virt += PAGE_SIZE) {
+ pg = vmalloc_to_page(virt);
+ if (NULL == pg)
+ goto err;
+ BUG_ON(PageHighMem(pg));
+ sg_set_page(&sglist[i], pg, PAGE_SIZE, 0);
+ }
+ return sglist;
+
+ err:
+ kfree(sglist);
+ return NULL;
+}
+
+/********************************************************************************/
+/* common page table functions */
+
+void *saa7146_vmalloc_build_pgtable(struct pci_dev *pci, long length, struct saa7146_pgtable *pt)
+{
+ int pages = (length+PAGE_SIZE-1)/PAGE_SIZE;
+ void *mem = vmalloc_32(length);
+ int slen = 0;
+
+ if (NULL == mem)
+ goto err_null;
+
+ if (!(pt->slist = vmalloc_to_sg(mem, pages)))
+ goto err_free_mem;
+
+ if (saa7146_pgtable_alloc(pci, pt))
+ goto err_free_slist;
+
+ pt->nents = pages;
+ slen = dma_map_sg(&pci->dev, pt->slist, pt->nents, DMA_FROM_DEVICE);
+ if (0 == slen)
+ goto err_free_pgtable;
+
+ if (0 != saa7146_pgtable_build_single(pci, pt, pt->slist, slen))
+ goto err_unmap_sg;
+
+ return mem;
+
+err_unmap_sg:
+ dma_unmap_sg(&pci->dev, pt->slist, pt->nents, DMA_FROM_DEVICE);
+err_free_pgtable:
+ saa7146_pgtable_free(pci, pt);
+err_free_slist:
+ kfree(pt->slist);
+ pt->slist = NULL;
+err_free_mem:
+ vfree(mem);
+err_null:
+ return NULL;
+}
+
+void saa7146_vfree_destroy_pgtable(struct pci_dev *pci, void *mem, struct saa7146_pgtable *pt)
+{
+ dma_unmap_sg(&pci->dev, pt->slist, pt->nents, DMA_FROM_DEVICE);
+ saa7146_pgtable_free(pci, pt);
+ kfree(pt->slist);
+ pt->slist = NULL;
+ vfree(mem);
+}
+
+void saa7146_pgtable_free(struct pci_dev *pci, struct saa7146_pgtable *pt)
+{
+ if (NULL == pt->cpu)
+ return;
+ dma_free_coherent(&pci->dev, pt->size, pt->cpu, pt->dma);
+ pt->cpu = NULL;
+}
+
+int saa7146_pgtable_alloc(struct pci_dev *pci, struct saa7146_pgtable *pt)
+{
+ __le32 *cpu;
+ dma_addr_t dma_addr = 0;
+
+ cpu = dma_alloc_coherent(&pci->dev, PAGE_SIZE, &dma_addr, GFP_KERNEL);
+ if (NULL == cpu) {
+ return -ENOMEM;
+ }
+ pt->size = PAGE_SIZE;
+ pt->cpu = cpu;
+ pt->dma = dma_addr;
+
+ return 0;
+}
+
+int saa7146_pgtable_build_single(struct pci_dev *pci, struct saa7146_pgtable *pt,
+ struct scatterlist *list, int sglen )
+{
+ __le32 *ptr, fill;
+ int nr_pages = 0;
+ int i,p;
+
+ BUG_ON(0 == sglen);
+ BUG_ON(list->offset > PAGE_SIZE);
+
+ /* if we have a user buffer, the first page may not be
+ aligned to a page boundary. */
+ pt->offset = list->offset;
+
+ ptr = pt->cpu;
+ for (i = 0; i < sglen; i++, list++) {
+/*
+ pr_debug("i:%d, adr:0x%08x, len:%d, offset:%d\n",
+ i, sg_dma_address(list), sg_dma_len(list),
+ list->offset);
+*/
+ for (p = 0; p * 4096 < sg_dma_len(list); p++, ptr++) {
+ *ptr = cpu_to_le32(sg_dma_address(list) + p * 4096);
+ nr_pages++;
+ }
+ }
+
+
+ /* safety; fill the page table up with the last valid page */
+ fill = *(ptr-1);
+ for(i=nr_pages;i<1024;i++) {
+ *ptr++ = fill;
+ }
+
+/*
+ ptr = pt->cpu;
+ pr_debug("offset: %d\n", pt->offset);
+ for(i=0;i<5;i++) {
+ pr_debug("ptr1 %d: 0x%08x\n", i, ptr[i]);
+ }
+*/
+ return 0;
+}
+
+/********************************************************************************/
+/* interrupt handler */
+static irqreturn_t interrupt_hw(int irq, void *dev_id)
+{
+ struct saa7146_dev *dev = dev_id;
+ u32 isr;
+ u32 ack_isr;
+
+ /* read out the interrupt status register */
+ ack_isr = isr = saa7146_read(dev, ISR);
+
+ /* is this our interrupt? */
+ if ( 0 == isr ) {
+ /* nope, some other device */
+ return IRQ_NONE;
+ }
+
+ if (dev->ext) {
+ if (dev->ext->irq_mask & isr) {
+ if (dev->ext->irq_func)
+ dev->ext->irq_func(dev, &isr);
+ isr &= ~dev->ext->irq_mask;
+ }
+ }
+ if (0 != (isr & (MASK_27))) {
+ DEB_INT("irq: RPS0 (0x%08x)\n", isr);
+ if (dev->vv_data && dev->vv_callback)
+ dev->vv_callback(dev,isr);
+ isr &= ~MASK_27;
+ }
+ if (0 != (isr & (MASK_28))) {
+ if (dev->vv_data && dev->vv_callback)
+ dev->vv_callback(dev,isr);
+ isr &= ~MASK_28;
+ }
+ if (0 != (isr & (MASK_16|MASK_17))) {
+ SAA7146_IER_DISABLE(dev, MASK_16|MASK_17);
+ /* only wake up if we expect something */
+ if (0 != dev->i2c_op) {
+ dev->i2c_op = 0;
+ wake_up(&dev->i2c_wq);
+ } else {
+ u32 psr = saa7146_read(dev, PSR);
+ u32 ssr = saa7146_read(dev, SSR);
+ pr_warn("%s: unexpected i2c irq: isr %08x psr %08x ssr %08x\n",
+ dev->name, isr, psr, ssr);
+ }
+ isr &= ~(MASK_16|MASK_17);
+ }
+ if( 0 != isr ) {
+ ERR("warning: interrupt enabled, but not handled properly.(0x%08x)\n",
+ isr);
+ ERR("disabling interrupt source(s)!\n");
+ SAA7146_IER_DISABLE(dev,isr);
+ }
+ saa7146_write(dev, ISR, ack_isr);
+ return IRQ_HANDLED;
+}
+
+/*********************************************************************************/
+/* configuration-functions */
+
+static int saa7146_init_one(struct pci_dev *pci, const struct pci_device_id *ent)
+{
+ struct saa7146_pci_extension_data *pci_ext = (struct saa7146_pci_extension_data *)ent->driver_data;
+ struct saa7146_extension *ext = pci_ext->ext;
+ struct saa7146_dev *dev;
+ int err = -ENOMEM;
+
+ /* clear out mem for sure */
+ dev = kzalloc(sizeof(struct saa7146_dev), GFP_KERNEL);
+ if (!dev) {
+ ERR("out of memory\n");
+ goto out;
+ }
+
+ /* create a nice device name */
+ sprintf(dev->name, "saa7146 (%d)", saa7146_num);
+
+ DEB_EE("pci:%p\n", pci);
+
+ err = pci_enable_device(pci);
+ if (err < 0) {
+ ERR("pci_enable_device() failed\n");
+ goto err_free;
+ }
+
+ /* enable bus-mastering */
+ pci_set_master(pci);
+
+ dev->pci = pci;
+
+ /* get chip-revision; this is needed to enable bug-fixes */
+ dev->revision = pci->revision;
+
+ /* remap the memory from virtual to physical address */
+
+ err = pci_request_region(pci, 0, "saa7146");
+ if (err < 0)
+ goto err_disable;
+
+ dev->mem = ioremap(pci_resource_start(pci, 0),
+ pci_resource_len(pci, 0));
+ if (!dev->mem) {
+ ERR("ioremap() failed\n");
+ err = -ENODEV;
+ goto err_release;
+ }
+
+ /* we don't do a master reset here anymore, it screws up
+ some boards that don't have an i2c-eeprom for configuration
+ values */
+/*
+ saa7146_write(dev, MC1, MASK_31);
+*/
+
+ /* disable all irqs */
+ saa7146_write(dev, IER, 0);
+
+ /* shut down all dma transfers and rps tasks */
+ saa7146_write(dev, MC1, 0x30ff0000);
+
+ /* clear out any rps-signals pending */
+ saa7146_write(dev, MC2, 0xf8000000);
+
+ /* request an interrupt for the saa7146 */
+ err = request_irq(pci->irq, interrupt_hw, IRQF_SHARED,
+ dev->name, dev);
+ if (err < 0) {
+ ERR("request_irq() failed\n");
+ goto err_unmap;
+ }
+
+ err = -ENOMEM;
+
+ /* get memory for various stuff */
+ dev->d_rps0.cpu_addr = dma_alloc_coherent(&pci->dev, SAA7146_RPS_MEM,
+ &dev->d_rps0.dma_handle,
+ GFP_KERNEL);
+ if (!dev->d_rps0.cpu_addr)
+ goto err_free_irq;
+
+ dev->d_rps1.cpu_addr = dma_alloc_coherent(&pci->dev, SAA7146_RPS_MEM,
+ &dev->d_rps1.dma_handle,
+ GFP_KERNEL);
+ if (!dev->d_rps1.cpu_addr)
+ goto err_free_rps0;
+
+ dev->d_i2c.cpu_addr = dma_alloc_coherent(&pci->dev, SAA7146_RPS_MEM,
+ &dev->d_i2c.dma_handle, GFP_KERNEL);
+ if (!dev->d_i2c.cpu_addr)
+ goto err_free_rps1;
+
+ /* the rest + print status message */
+
+ pr_info("found saa7146 @ mem %p (revision %d, irq %d) (0x%04x,0x%04x)\n",
+ dev->mem, dev->revision, pci->irq,
+ pci->subsystem_vendor, pci->subsystem_device);
+ dev->ext = ext;
+
+ mutex_init(&dev->v4l2_lock);
+ spin_lock_init(&dev->int_slock);
+ spin_lock_init(&dev->slock);
+
+ mutex_init(&dev->i2c_lock);
+
+ dev->module = THIS_MODULE;
+ init_waitqueue_head(&dev->i2c_wq);
+
+ /* set some sane pci arbitrition values */
+ saa7146_write(dev, PCI_BT_V1, 0x1c00101f);
+
+ /* TODO: use the status code of the callback */
+
+ err = -ENODEV;
+
+ if (ext->probe && ext->probe(dev)) {
+ DEB_D("ext->probe() failed for %p. skipping device.\n", dev);
+ goto err_free_i2c;
+ }
+
+ if (ext->attach(dev, pci_ext)) {
+ DEB_D("ext->attach() failed for %p. skipping device.\n", dev);
+ goto err_free_i2c;
+ }
+ /* V4L extensions will set the pci drvdata to the v4l2_device in the
+ attach() above. So for those cards that do not use V4L we have to
+ set it explicitly. */
+ pci_set_drvdata(pci, &dev->v4l2_dev);
+
+ saa7146_num++;
+
+ err = 0;
+out:
+ return err;
+
+err_free_i2c:
+ dma_free_coherent(&pci->dev, SAA7146_RPS_MEM, dev->d_i2c.cpu_addr,
+ dev->d_i2c.dma_handle);
+err_free_rps1:
+ dma_free_coherent(&pci->dev, SAA7146_RPS_MEM, dev->d_rps1.cpu_addr,
+ dev->d_rps1.dma_handle);
+err_free_rps0:
+ dma_free_coherent(&pci->dev, SAA7146_RPS_MEM, dev->d_rps0.cpu_addr,
+ dev->d_rps0.dma_handle);
+err_free_irq:
+ free_irq(pci->irq, (void *)dev);
+err_unmap:
+ iounmap(dev->mem);
+err_release:
+ pci_release_region(pci, 0);
+err_disable:
+ pci_disable_device(pci);
+err_free:
+ kfree(dev);
+ goto out;
+}
+
+static void saa7146_remove_one(struct pci_dev *pdev)
+{
+ struct v4l2_device *v4l2_dev = pci_get_drvdata(pdev);
+ struct saa7146_dev *dev = to_saa7146_dev(v4l2_dev);
+ struct {
+ void *addr;
+ dma_addr_t dma;
+ } dev_map[] = {
+ { dev->d_i2c.cpu_addr, dev->d_i2c.dma_handle },
+ { dev->d_rps1.cpu_addr, dev->d_rps1.dma_handle },
+ { dev->d_rps0.cpu_addr, dev->d_rps0.dma_handle },
+ { NULL, 0 }
+ }, *p;
+
+ DEB_EE("dev:%p\n", dev);
+
+ dev->ext->detach(dev);
+
+ /* shut down all video dma transfers */
+ saa7146_write(dev, MC1, 0x00ff0000);
+
+ /* disable all irqs, release irq-routine */
+ saa7146_write(dev, IER, 0);
+
+ free_irq(pdev->irq, dev);
+
+ for (p = dev_map; p->addr; p++)
+ dma_free_coherent(&pdev->dev, SAA7146_RPS_MEM, p->addr,
+ p->dma);
+
+ iounmap(dev->mem);
+ pci_release_region(pdev, 0);
+ pci_disable_device(pdev);
+ kfree(dev);
+
+ saa7146_num--;
+}
+
+/*********************************************************************************/
+/* extension handling functions */
+
+int saa7146_register_extension(struct saa7146_extension* ext)
+{
+ DEB_EE("ext:%p\n", ext);
+
+ ext->driver.name = ext->name;
+ ext->driver.id_table = ext->pci_tbl;
+ ext->driver.probe = saa7146_init_one;
+ ext->driver.remove = saa7146_remove_one;
+
+ pr_info("register extension '%s'\n", ext->name);
+ return pci_register_driver(&ext->driver);
+}
+
+int saa7146_unregister_extension(struct saa7146_extension* ext)
+{
+ DEB_EE("ext:%p\n", ext);
+ pr_info("unregister extension '%s'\n", ext->name);
+ pci_unregister_driver(&ext->driver);
+ return 0;
+}
+
+EXPORT_SYMBOL_GPL(saa7146_register_extension);
+EXPORT_SYMBOL_GPL(saa7146_unregister_extension);
+
+/* misc functions used by extension modules */
+EXPORT_SYMBOL_GPL(saa7146_pgtable_alloc);
+EXPORT_SYMBOL_GPL(saa7146_pgtable_free);
+EXPORT_SYMBOL_GPL(saa7146_pgtable_build_single);
+EXPORT_SYMBOL_GPL(saa7146_vmalloc_build_pgtable);
+EXPORT_SYMBOL_GPL(saa7146_vfree_destroy_pgtable);
+EXPORT_SYMBOL_GPL(saa7146_wait_for_debi_done);
+
+EXPORT_SYMBOL_GPL(saa7146_setgpio);
+
+EXPORT_SYMBOL_GPL(saa7146_i2c_adapter_prepare);
+
+EXPORT_SYMBOL_GPL(saa7146_debug);
+
+MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
+MODULE_DESCRIPTION("driver for generic saa7146-based hardware");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/common/saa7146/saa7146_fops.c b/drivers/media/common/saa7146/saa7146_fops.c
new file mode 100644
index 000000000000..e9a15de6126e
--- /dev/null
+++ b/drivers/media/common/saa7146/saa7146_fops.c
@@ -0,0 +1,658 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <media/drv-intf/saa7146_vv.h>
+#include <linux/module.h>
+
+/****************************************************************************/
+/* resource management functions, shamelessly stolen from saa7134 driver */
+
+int saa7146_res_get(struct saa7146_fh *fh, unsigned int bit)
+{
+ struct saa7146_dev *dev = fh->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+
+ if (fh->resources & bit) {
+ DEB_D("already allocated! want: 0x%02x, cur:0x%02x\n",
+ bit, vv->resources);
+ /* have it already allocated */
+ return 1;
+ }
+
+ /* is it free? */
+ if (vv->resources & bit) {
+ DEB_D("locked! vv->resources:0x%02x, we want:0x%02x\n",
+ vv->resources, bit);
+ /* no, someone else uses it */
+ return 0;
+ }
+ /* it's free, grab it */
+ fh->resources |= bit;
+ vv->resources |= bit;
+ DEB_D("res: get 0x%02x, cur:0x%02x\n", bit, vv->resources);
+ return 1;
+}
+
+void saa7146_res_free(struct saa7146_fh *fh, unsigned int bits)
+{
+ struct saa7146_dev *dev = fh->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+
+ BUG_ON((fh->resources & bits) != bits);
+
+ fh->resources &= ~bits;
+ vv->resources &= ~bits;
+ DEB_D("res: put 0x%02x, cur:0x%02x\n", bits, vv->resources);
+}
+
+
+/********************************************************************************/
+/* common dma functions */
+
+void saa7146_dma_free(struct saa7146_dev *dev,struct videobuf_queue *q,
+ struct saa7146_buf *buf)
+{
+ struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);
+ DEB_EE("dev:%p, buf:%p\n", dev, buf);
+
+ videobuf_waiton(q, &buf->vb, 0, 0);
+ videobuf_dma_unmap(q->dev, dma);
+ videobuf_dma_free(dma);
+ buf->vb.state = VIDEOBUF_NEEDS_INIT;
+}
+
+
+/********************************************************************************/
+/* common buffer functions */
+
+int saa7146_buffer_queue(struct saa7146_dev *dev,
+ struct saa7146_dmaqueue *q,
+ struct saa7146_buf *buf)
+{
+ assert_spin_locked(&dev->slock);
+ DEB_EE("dev:%p, dmaq:%p, buf:%p\n", dev, q, buf);
+
+ BUG_ON(!q);
+
+ if (NULL == q->curr) {
+ q->curr = buf;
+ DEB_D("immediately activating buffer %p\n", buf);
+ buf->activate(dev,buf,NULL);
+ } else {
+ list_add_tail(&buf->vb.queue,&q->queue);
+ buf->vb.state = VIDEOBUF_QUEUED;
+ DEB_D("adding buffer %p to queue. (active buffer present)\n",
+ buf);
+ }
+ return 0;
+}
+
+void saa7146_buffer_finish(struct saa7146_dev *dev,
+ struct saa7146_dmaqueue *q,
+ int state)
+{
+ assert_spin_locked(&dev->slock);
+ DEB_EE("dev:%p, dmaq:%p, state:%d\n", dev, q, state);
+ DEB_EE("q->curr:%p\n", q->curr);
+
+ /* finish current buffer */
+ if (NULL == q->curr) {
+ DEB_D("aiii. no current buffer\n");
+ return;
+ }
+
+ q->curr->vb.state = state;
+ q->curr->vb.ts = ktime_get_ns();
+ wake_up(&q->curr->vb.done);
+
+ q->curr = NULL;
+}
+
+void saa7146_buffer_next(struct saa7146_dev *dev,
+ struct saa7146_dmaqueue *q, int vbi)
+{
+ struct saa7146_buf *buf,*next = NULL;
+
+ BUG_ON(!q);
+
+ DEB_INT("dev:%p, dmaq:%p, vbi:%d\n", dev, q, vbi);
+
+ assert_spin_locked(&dev->slock);
+ if (!list_empty(&q->queue)) {
+ /* activate next one from queue */
+ buf = list_entry(q->queue.next,struct saa7146_buf,vb.queue);
+ list_del(&buf->vb.queue);
+ if (!list_empty(&q->queue))
+ next = list_entry(q->queue.next,struct saa7146_buf, vb.queue);
+ q->curr = buf;
+ DEB_INT("next buffer: buf:%p, prev:%p, next:%p\n",
+ buf, q->queue.prev, q->queue.next);
+ buf->activate(dev,buf,next);
+ } else {
+ DEB_INT("no next buffer. stopping.\n");
+ if( 0 != vbi ) {
+ /* turn off video-dma3 */
+ saa7146_write(dev,MC1, MASK_20);
+ } else {
+ /* nothing to do -- just prevent next video-dma1 transfer
+ by lowering the protection address */
+
+ // fixme: fix this for vflip != 0
+
+ saa7146_write(dev, PROT_ADDR1, 0);
+ saa7146_write(dev, MC2, (MASK_02|MASK_18));
+
+ /* write the address of the rps-program */
+ saa7146_write(dev, RPS_ADDR0, dev->d_rps0.dma_handle);
+ /* turn on rps */
+ saa7146_write(dev, MC1, (MASK_12 | MASK_28));
+
+/*
+ printk("vdma%d.base_even: 0x%08x\n", 1,saa7146_read(dev,BASE_EVEN1));
+ printk("vdma%d.base_odd: 0x%08x\n", 1,saa7146_read(dev,BASE_ODD1));
+ printk("vdma%d.prot_addr: 0x%08x\n", 1,saa7146_read(dev,PROT_ADDR1));
+ printk("vdma%d.base_page: 0x%08x\n", 1,saa7146_read(dev,BASE_PAGE1));
+ printk("vdma%d.pitch: 0x%08x\n", 1,saa7146_read(dev,PITCH1));
+ printk("vdma%d.num_line_byte: 0x%08x\n", 1,saa7146_read(dev,NUM_LINE_BYTE1));
+*/
+ }
+ del_timer(&q->timeout);
+ }
+}
+
+void saa7146_buffer_timeout(struct timer_list *t)
+{
+ struct saa7146_dmaqueue *q = from_timer(q, t, timeout);
+ struct saa7146_dev *dev = q->dev;
+ unsigned long flags;
+
+ DEB_EE("dev:%p, dmaq:%p\n", dev, q);
+
+ spin_lock_irqsave(&dev->slock,flags);
+ if (q->curr) {
+ DEB_D("timeout on %p\n", q->curr);
+ saa7146_buffer_finish(dev,q,VIDEOBUF_ERROR);
+ }
+
+ /* we don't restart the transfer here like other drivers do. when
+ a streaming capture is disabled, the timeout function will be
+ called for the current buffer. if we activate the next buffer now,
+ we mess up our capture logic. if a timeout occurs on another buffer,
+ then something is seriously broken before, so no need to buffer the
+ next capture IMHO... */
+/*
+ saa7146_buffer_next(dev,q);
+*/
+ spin_unlock_irqrestore(&dev->slock,flags);
+}
+
+/********************************************************************************/
+/* file operations */
+
+static int fops_open(struct file *file)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct saa7146_dev *dev = video_drvdata(file);
+ struct saa7146_fh *fh = NULL;
+ int result = 0;
+
+ DEB_EE("file:%p, dev:%s\n", file, video_device_node_name(vdev));
+
+ if (mutex_lock_interruptible(vdev->lock))
+ return -ERESTARTSYS;
+
+ DEB_D("using: %p\n", dev);
+
+ /* check if an extension is registered */
+ if( NULL == dev->ext ) {
+ DEB_S("no extension registered for this device\n");
+ result = -ENODEV;
+ goto out;
+ }
+
+ /* allocate per open data */
+ fh = kzalloc(sizeof(*fh),GFP_KERNEL);
+ if (NULL == fh) {
+ DEB_S("cannot allocate memory for per open data\n");
+ result = -ENOMEM;
+ goto out;
+ }
+
+ v4l2_fh_init(&fh->fh, vdev);
+
+ file->private_data = &fh->fh;
+ fh->dev = dev;
+
+ if (vdev->vfl_type == VFL_TYPE_VBI) {
+ DEB_S("initializing vbi...\n");
+ if (dev->ext_vv_data->capabilities & V4L2_CAP_VBI_CAPTURE)
+ result = saa7146_vbi_uops.open(dev,file);
+ if (dev->ext_vv_data->vbi_fops.open)
+ dev->ext_vv_data->vbi_fops.open(file);
+ } else {
+ DEB_S("initializing video...\n");
+ result = saa7146_video_uops.open(dev,file);
+ }
+
+ if (0 != result) {
+ goto out;
+ }
+
+ if( 0 == try_module_get(dev->ext->module)) {
+ result = -EINVAL;
+ goto out;
+ }
+
+ result = 0;
+ v4l2_fh_add(&fh->fh);
+out:
+ if (fh && result != 0) {
+ kfree(fh);
+ file->private_data = NULL;
+ }
+ mutex_unlock(vdev->lock);
+ return result;
+}
+
+static int fops_release(struct file *file)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct saa7146_fh *fh = file->private_data;
+ struct saa7146_dev *dev = fh->dev;
+
+ DEB_EE("file:%p\n", file);
+
+ mutex_lock(vdev->lock);
+
+ if (vdev->vfl_type == VFL_TYPE_VBI) {
+ if (dev->ext_vv_data->capabilities & V4L2_CAP_VBI_CAPTURE)
+ saa7146_vbi_uops.release(dev,file);
+ if (dev->ext_vv_data->vbi_fops.release)
+ dev->ext_vv_data->vbi_fops.release(file);
+ } else {
+ saa7146_video_uops.release(dev,file);
+ }
+
+ v4l2_fh_del(&fh->fh);
+ v4l2_fh_exit(&fh->fh);
+ module_put(dev->ext->module);
+ file->private_data = NULL;
+ kfree(fh);
+
+ mutex_unlock(vdev->lock);
+
+ return 0;
+}
+
+static int fops_mmap(struct file *file, struct vm_area_struct * vma)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct saa7146_fh *fh = file->private_data;
+ struct videobuf_queue *q;
+ int res;
+
+ switch (vdev->vfl_type) {
+ case VFL_TYPE_VIDEO: {
+ DEB_EE("V4L2_BUF_TYPE_VIDEO_CAPTURE: file:%p, vma:%p\n",
+ file, vma);
+ q = &fh->video_q;
+ break;
+ }
+ case VFL_TYPE_VBI: {
+ DEB_EE("V4L2_BUF_TYPE_VBI_CAPTURE: file:%p, vma:%p\n",
+ file, vma);
+ if (fh->dev->ext_vv_data->capabilities & V4L2_CAP_SLICED_VBI_OUTPUT)
+ return -ENODEV;
+ q = &fh->vbi_q;
+ break;
+ }
+ default:
+ BUG();
+ }
+
+ if (mutex_lock_interruptible(vdev->lock))
+ return -ERESTARTSYS;
+ res = videobuf_mmap_mapper(q, vma);
+ mutex_unlock(vdev->lock);
+ return res;
+}
+
+static __poll_t __fops_poll(struct file *file, struct poll_table_struct *wait)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct saa7146_fh *fh = file->private_data;
+ struct videobuf_buffer *buf = NULL;
+ struct videobuf_queue *q;
+ __poll_t res = v4l2_ctrl_poll(file, wait);
+
+ DEB_EE("file:%p, poll:%p\n", file, wait);
+
+ if (vdev->vfl_type == VFL_TYPE_VBI) {
+ if (fh->dev->ext_vv_data->capabilities & V4L2_CAP_SLICED_VBI_OUTPUT)
+ return res | EPOLLOUT | EPOLLWRNORM;
+ if( 0 == fh->vbi_q.streaming )
+ return res | videobuf_poll_stream(file, &fh->vbi_q, wait);
+ q = &fh->vbi_q;
+ } else {
+ DEB_D("using video queue\n");
+ q = &fh->video_q;
+ }
+
+ if (!list_empty(&q->stream))
+ buf = list_entry(q->stream.next, struct videobuf_buffer, stream);
+
+ if (!buf) {
+ DEB_D("buf == NULL!\n");
+ return res | EPOLLERR;
+ }
+
+ poll_wait(file, &buf->done, wait);
+ if (buf->state == VIDEOBUF_DONE || buf->state == VIDEOBUF_ERROR) {
+ DEB_D("poll succeeded!\n");
+ return res | EPOLLIN | EPOLLRDNORM;
+ }
+
+ DEB_D("nothing to poll for, buf->state:%d\n", buf->state);
+ return res;
+}
+
+static __poll_t fops_poll(struct file *file, struct poll_table_struct *wait)
+{
+ struct video_device *vdev = video_devdata(file);
+ __poll_t res;
+
+ mutex_lock(vdev->lock);
+ res = __fops_poll(file, wait);
+ mutex_unlock(vdev->lock);
+ return res;
+}
+
+static ssize_t fops_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct saa7146_fh *fh = file->private_data;
+ int ret;
+
+ switch (vdev->vfl_type) {
+ case VFL_TYPE_VIDEO:
+/*
+ DEB_EE("V4L2_BUF_TYPE_VIDEO_CAPTURE: file:%p, data:%p, count:%lun",
+ file, data, (unsigned long)count);
+*/
+ return saa7146_video_uops.read(file,data,count,ppos);
+ case VFL_TYPE_VBI:
+/*
+ DEB_EE("V4L2_BUF_TYPE_VBI_CAPTURE: file:%p, data:%p, count:%lu\n",
+ file, data, (unsigned long)count);
+*/
+ if (fh->dev->ext_vv_data->capabilities & V4L2_CAP_VBI_CAPTURE) {
+ if (mutex_lock_interruptible(vdev->lock))
+ return -ERESTARTSYS;
+ ret = saa7146_vbi_uops.read(file, data, count, ppos);
+ mutex_unlock(vdev->lock);
+ return ret;
+ }
+ return -EINVAL;
+ default:
+ BUG();
+ }
+}
+
+static ssize_t fops_write(struct file *file, const char __user *data, size_t count, loff_t *ppos)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct saa7146_fh *fh = file->private_data;
+ int ret;
+
+ switch (vdev->vfl_type) {
+ case VFL_TYPE_VIDEO:
+ return -EINVAL;
+ case VFL_TYPE_VBI:
+ if (fh->dev->ext_vv_data->vbi_fops.write) {
+ if (mutex_lock_interruptible(vdev->lock))
+ return -ERESTARTSYS;
+ ret = fh->dev->ext_vv_data->vbi_fops.write(file, data, count, ppos);
+ mutex_unlock(vdev->lock);
+ return ret;
+ }
+ return -EINVAL;
+ default:
+ BUG();
+ }
+}
+
+static const struct v4l2_file_operations video_fops =
+{
+ .owner = THIS_MODULE,
+ .open = fops_open,
+ .release = fops_release,
+ .read = fops_read,
+ .write = fops_write,
+ .poll = fops_poll,
+ .mmap = fops_mmap,
+ .unlocked_ioctl = video_ioctl2,
+};
+
+static void vv_callback(struct saa7146_dev *dev, unsigned long status)
+{
+ u32 isr = status;
+
+ DEB_INT("dev:%p, isr:0x%08x\n", dev, (u32)status);
+
+ if (0 != (isr & (MASK_27))) {
+ DEB_INT("irq: RPS0 (0x%08x)\n", isr);
+ saa7146_video_uops.irq_done(dev,isr);
+ }
+
+ if (0 != (isr & (MASK_28))) {
+ u32 mc2 = saa7146_read(dev, MC2);
+ if( 0 != (mc2 & MASK_15)) {
+ DEB_INT("irq: RPS1 vbi workaround (0x%08x)\n", isr);
+ wake_up(&dev->vv_data->vbi_wq);
+ saa7146_write(dev,MC2, MASK_31);
+ return;
+ }
+ DEB_INT("irq: RPS1 (0x%08x)\n", isr);
+ saa7146_vbi_uops.irq_done(dev,isr);
+ }
+}
+
+static const struct v4l2_ctrl_ops saa7146_ctrl_ops = {
+ .s_ctrl = saa7146_s_ctrl,
+};
+
+int saa7146_vv_init(struct saa7146_dev* dev, struct saa7146_ext_vv *ext_vv)
+{
+ struct v4l2_ctrl_handler *hdl = &dev->ctrl_handler;
+ struct v4l2_pix_format *fmt;
+ struct v4l2_vbi_format *vbi;
+ struct saa7146_vv *vv;
+ int err;
+
+ err = v4l2_device_register(&dev->pci->dev, &dev->v4l2_dev);
+ if (err)
+ return err;
+
+ v4l2_ctrl_handler_init(hdl, 6);
+ v4l2_ctrl_new_std(hdl, &saa7146_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
+ v4l2_ctrl_new_std(hdl, &saa7146_ctrl_ops,
+ V4L2_CID_CONTRAST, 0, 127, 1, 64);
+ v4l2_ctrl_new_std(hdl, &saa7146_ctrl_ops,
+ V4L2_CID_SATURATION, 0, 127, 1, 64);
+ v4l2_ctrl_new_std(hdl, &saa7146_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(hdl, &saa7146_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ if (hdl->error) {
+ err = hdl->error;
+ v4l2_ctrl_handler_free(hdl);
+ v4l2_device_unregister(&dev->v4l2_dev);
+ return err;
+ }
+ dev->v4l2_dev.ctrl_handler = hdl;
+
+ vv = kzalloc(sizeof(struct saa7146_vv), GFP_KERNEL);
+ if (vv == NULL) {
+ ERR("out of memory. aborting.\n");
+ v4l2_ctrl_handler_free(hdl);
+ v4l2_device_unregister(&dev->v4l2_dev);
+ return -ENOMEM;
+ }
+ ext_vv->vid_ops = saa7146_video_ioctl_ops;
+ ext_vv->vbi_ops = saa7146_vbi_ioctl_ops;
+ ext_vv->core_ops = &saa7146_video_ioctl_ops;
+
+ DEB_EE("dev:%p\n", dev);
+
+ /* set default values for video parts of the saa7146 */
+ saa7146_write(dev, BCS_CTRL, 0x80400040);
+
+ /* enable video-port pins */
+ saa7146_write(dev, MC1, (MASK_10 | MASK_26));
+
+ /* save per-device extension data (one extension can
+ handle different devices that might need different
+ configuration data) */
+ dev->ext_vv_data = ext_vv;
+
+ vv->d_clipping.cpu_addr =
+ dma_alloc_coherent(&dev->pci->dev, SAA7146_CLIPPING_MEM,
+ &vv->d_clipping.dma_handle, GFP_KERNEL);
+ if( NULL == vv->d_clipping.cpu_addr ) {
+ ERR("out of memory. aborting.\n");
+ kfree(vv);
+ v4l2_ctrl_handler_free(hdl);
+ v4l2_device_unregister(&dev->v4l2_dev);
+ return -ENOMEM;
+ }
+
+ saa7146_video_uops.init(dev,vv);
+ if (dev->ext_vv_data->capabilities & V4L2_CAP_VBI_CAPTURE)
+ saa7146_vbi_uops.init(dev,vv);
+
+ vv->ov_fb.fmt.width = vv->standard->h_max_out;
+ vv->ov_fb.fmt.height = vv->standard->v_max_out;
+ vv->ov_fb.fmt.pixelformat = V4L2_PIX_FMT_RGB565;
+ vv->ov_fb.fmt.bytesperline = 2 * vv->ov_fb.fmt.width;
+ vv->ov_fb.fmt.sizeimage = vv->ov_fb.fmt.bytesperline * vv->ov_fb.fmt.height;
+ vv->ov_fb.fmt.colorspace = V4L2_COLORSPACE_SRGB;
+
+ fmt = &vv->video_fmt;
+ fmt->width = 384;
+ fmt->height = 288;
+ fmt->pixelformat = V4L2_PIX_FMT_BGR24;
+ fmt->field = V4L2_FIELD_ANY;
+ fmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
+ fmt->bytesperline = 3 * fmt->width;
+ fmt->sizeimage = fmt->bytesperline * fmt->height;
+
+ vbi = &vv->vbi_fmt;
+ vbi->sampling_rate = 27000000;
+ vbi->offset = 248; /* todo */
+ vbi->samples_per_line = 720 * 2;
+ vbi->sample_format = V4L2_PIX_FMT_GREY;
+
+ /* fixme: this only works for PAL */
+ vbi->start[0] = 5;
+ vbi->count[0] = 16;
+ vbi->start[1] = 312;
+ vbi->count[1] = 16;
+
+ timer_setup(&vv->vbi_read_timeout, NULL, 0);
+
+ vv->ov_fb.capability = V4L2_FBUF_CAP_LIST_CLIPPING;
+ vv->ov_fb.flags = V4L2_FBUF_FLAG_PRIMARY;
+ dev->vv_data = vv;
+ dev->vv_callback = &vv_callback;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(saa7146_vv_init);
+
+int saa7146_vv_release(struct saa7146_dev* dev)
+{
+ struct saa7146_vv *vv = dev->vv_data;
+
+ DEB_EE("dev:%p\n", dev);
+
+ v4l2_device_unregister(&dev->v4l2_dev);
+ dma_free_coherent(&dev->pci->dev, SAA7146_CLIPPING_MEM,
+ vv->d_clipping.cpu_addr, vv->d_clipping.dma_handle);
+ v4l2_ctrl_handler_free(&dev->ctrl_handler);
+ kfree(vv);
+ dev->vv_data = NULL;
+ dev->vv_callback = NULL;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(saa7146_vv_release);
+
+int saa7146_register_device(struct video_device *vfd, struct saa7146_dev *dev,
+ char *name, int type)
+{
+ int err;
+ int i;
+
+ DEB_EE("dev:%p, name:'%s', type:%d\n", dev, name, type);
+
+ vfd->fops = &video_fops;
+ if (type == VFL_TYPE_VIDEO)
+ vfd->ioctl_ops = &dev->ext_vv_data->vid_ops;
+ else
+ vfd->ioctl_ops = &dev->ext_vv_data->vbi_ops;
+ vfd->release = video_device_release_empty;
+ vfd->lock = &dev->v4l2_lock;
+ vfd->v4l2_dev = &dev->v4l2_dev;
+ vfd->tvnorms = 0;
+ for (i = 0; i < dev->ext_vv_data->num_stds; i++)
+ vfd->tvnorms |= dev->ext_vv_data->stds[i].id;
+ strscpy(vfd->name, name, sizeof(vfd->name));
+ vfd->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OVERLAY |
+ V4L2_CAP_READWRITE | V4L2_CAP_STREAMING;
+ vfd->device_caps |= dev->ext_vv_data->capabilities;
+ if (type == VFL_TYPE_VIDEO)
+ vfd->device_caps &=
+ ~(V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_OUTPUT);
+ else
+ vfd->device_caps &=
+ ~(V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OVERLAY | V4L2_CAP_AUDIO);
+ video_set_drvdata(vfd, dev);
+
+ err = video_register_device(vfd, type, -1);
+ if (err < 0) {
+ ERR("cannot register v4l2 device. skipping.\n");
+ return err;
+ }
+
+ pr_info("%s: registered device %s [v4l2]\n",
+ dev->name, video_device_node_name(vfd));
+ return 0;
+}
+EXPORT_SYMBOL_GPL(saa7146_register_device);
+
+int saa7146_unregister_device(struct video_device *vfd, struct saa7146_dev *dev)
+{
+ DEB_EE("dev:%p\n", dev);
+
+ video_unregister_device(vfd);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(saa7146_unregister_device);
+
+static int __init saa7146_vv_init_module(void)
+{
+ return 0;
+}
+
+
+static void __exit saa7146_vv_cleanup_module(void)
+{
+}
+
+module_init(saa7146_vv_init_module);
+module_exit(saa7146_vv_cleanup_module);
+
+MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
+MODULE_DESCRIPTION("video4linux driver for saa7146-based hardware");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/common/saa7146/saa7146_hlp.c b/drivers/media/common/saa7146/saa7146_hlp.c
new file mode 100644
index 000000000000..6c9946a402ee
--- /dev/null
+++ b/drivers/media/common/saa7146/saa7146_hlp.c
@@ -0,0 +1,1046 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/export.h>
+#include <media/drv-intf/saa7146_vv.h>
+
+static void calculate_output_format_register(struct saa7146_dev* saa, u32 palette, u32* clip_format)
+{
+ /* clear out the necessary bits */
+ *clip_format &= 0x0000ffff;
+ /* set these bits new */
+ *clip_format |= (( ((palette&0xf00)>>8) << 30) | ((palette&0x00f) << 24) | (((palette&0x0f0)>>4) << 16));
+}
+
+static void calculate_hps_source_and_sync(struct saa7146_dev *dev, int source, int sync, u32* hps_ctrl)
+{
+ *hps_ctrl &= ~(MASK_30 | MASK_31 | MASK_28);
+ *hps_ctrl |= (source << 30) | (sync << 28);
+}
+
+static void calculate_hxo_and_hyo(struct saa7146_vv *vv, u32* hps_h_scale, u32* hps_ctrl)
+{
+ int hyo = 0, hxo = 0;
+
+ hyo = vv->standard->v_offset;
+ hxo = vv->standard->h_offset;
+
+ *hps_h_scale &= ~(MASK_B0 | 0xf00);
+ *hps_h_scale |= (hxo << 0);
+
+ *hps_ctrl &= ~(MASK_W0 | MASK_B2);
+ *hps_ctrl |= (hyo << 12);
+}
+
+/* helper functions for the calculation of the horizontal- and vertical
+ scaling registers, clip-format-register etc ...
+ these functions take pointers to the (most-likely read-out
+ original-values) and manipulate them according to the requested
+ changes.
+*/
+
+/* hps_coeff used for CXY and CXUV; scale 1/1 -> scale 1/64 */
+static struct {
+ u16 hps_coeff;
+ u16 weight_sum;
+} hps_h_coeff_tab [] = {
+ {0x00, 2}, {0x02, 4}, {0x00, 4}, {0x06, 8}, {0x02, 8},
+ {0x08, 8}, {0x00, 8}, {0x1E, 16}, {0x0E, 8}, {0x26, 8},
+ {0x06, 8}, {0x42, 8}, {0x02, 8}, {0x80, 8}, {0x00, 8},
+ {0xFE, 16}, {0xFE, 8}, {0x7E, 8}, {0x7E, 8}, {0x3E, 8},
+ {0x3E, 8}, {0x1E, 8}, {0x1E, 8}, {0x0E, 8}, {0x0E, 8},
+ {0x06, 8}, {0x06, 8}, {0x02, 8}, {0x02, 8}, {0x00, 8},
+ {0x00, 8}, {0xFE, 16}, {0xFE, 8}, {0xFE, 8}, {0xFE, 8},
+ {0xFE, 8}, {0xFE, 8}, {0xFE, 8}, {0xFE, 8}, {0xFE, 8},
+ {0xFE, 8}, {0xFE, 8}, {0xFE, 8}, {0xFE, 8}, {0xFE, 8},
+ {0xFE, 8}, {0xFE, 8}, {0xFE, 8}, {0xFE, 8}, {0x7E, 8},
+ {0x7E, 8}, {0x3E, 8}, {0x3E, 8}, {0x1E, 8}, {0x1E, 8},
+ {0x0E, 8}, {0x0E, 8}, {0x06, 8}, {0x06, 8}, {0x02, 8},
+ {0x02, 8}, {0x00, 8}, {0x00, 8}, {0xFE, 16}
+};
+
+/* table of attenuation values for horizontal scaling */
+static u8 h_attenuation[] = { 1, 2, 4, 8, 2, 4, 8, 16, 0};
+
+/* calculate horizontal scale registers */
+static int calculate_h_scale_registers(struct saa7146_dev *dev,
+ int in_x, int out_x, int flip_lr,
+ u32* hps_ctrl, u32* hps_v_gain, u32* hps_h_prescale, u32* hps_h_scale)
+{
+ /* horizontal prescaler */
+ u32 dcgx = 0, xpsc = 0, xacm = 0, cxy = 0, cxuv = 0;
+ /* horizontal scaler */
+ u32 xim = 0, xp = 0, xsci =0;
+ /* vertical scale & gain */
+ u32 pfuv = 0;
+
+ /* helper variables */
+ u32 h_atten = 0, i = 0;
+
+ if ( 0 == out_x ) {
+ return -EINVAL;
+ }
+
+ /* mask out vanity-bit */
+ *hps_ctrl &= ~MASK_29;
+
+ /* calculate prescale-(xspc)-value: [n .. 1/2) : 1
+ [1/2 .. 1/3) : 2
+ [1/3 .. 1/4) : 3
+ ... */
+ if (in_x > out_x) {
+ xpsc = in_x / out_x;
+ }
+ else {
+ /* zooming */
+ xpsc = 1;
+ }
+
+ /* if flip_lr-bit is set, number of pixels after
+ horizontal prescaling must be < 384 */
+ if ( 0 != flip_lr ) {
+
+ /* set vanity bit */
+ *hps_ctrl |= MASK_29;
+
+ while (in_x / xpsc >= 384 )
+ xpsc++;
+ }
+ /* if zooming is wanted, number of pixels after
+ horizontal prescaling must be < 768 */
+ else {
+ while ( in_x / xpsc >= 768 )
+ xpsc++;
+ }
+
+ /* maximum prescale is 64 (p.69) */
+ if ( xpsc > 64 )
+ xpsc = 64;
+
+ /* keep xacm clear*/
+ xacm = 0;
+
+ /* set horizontal filter parameters (CXY = CXUV) */
+ cxy = hps_h_coeff_tab[( (xpsc - 1) < 63 ? (xpsc - 1) : 63 )].hps_coeff;
+ cxuv = cxy;
+
+ /* calculate and set horizontal fine scale (xsci) */
+
+ /* bypass the horizontal scaler ? */
+ if ( (in_x == out_x) && ( 1 == xpsc ) )
+ xsci = 0x400;
+ else
+ xsci = ( (1024 * in_x) / (out_x * xpsc) ) + xpsc;
+
+ /* set start phase for horizontal fine scale (xp) to 0 */
+ xp = 0;
+
+ /* set xim, if we bypass the horizontal scaler */
+ if ( 0x400 == xsci )
+ xim = 1;
+ else
+ xim = 0;
+
+ /* if the prescaler is bypassed, enable horizontal
+ accumulation mode (xacm) and clear dcgx */
+ if( 1 == xpsc ) {
+ xacm = 1;
+ dcgx = 0;
+ } else {
+ xacm = 0;
+ /* get best match in the table of attenuations
+ for horizontal scaling */
+ h_atten = hps_h_coeff_tab[( (xpsc - 1) < 63 ? (xpsc - 1) : 63 )].weight_sum;
+
+ for (i = 0; h_attenuation[i] != 0; i++) {
+ if (h_attenuation[i] >= h_atten)
+ break;
+ }
+
+ dcgx = i;
+ }
+
+ /* the horizontal scaling increment controls the UV filter
+ to reduce the bandwidth to improve the display quality,
+ so set it ... */
+ if ( xsci == 0x400)
+ pfuv = 0x00;
+ else if ( xsci < 0x600)
+ pfuv = 0x01;
+ else if ( xsci < 0x680)
+ pfuv = 0x11;
+ else if ( xsci < 0x700)
+ pfuv = 0x22;
+ else
+ pfuv = 0x33;
+
+
+ *hps_v_gain &= MASK_W0|MASK_B2;
+ *hps_v_gain |= (pfuv << 24);
+
+ *hps_h_scale &= ~(MASK_W1 | 0xf000);
+ *hps_h_scale |= (xim << 31) | (xp << 24) | (xsci << 12);
+
+ *hps_h_prescale |= (dcgx << 27) | ((xpsc-1) << 18) | (xacm << 17) | (cxy << 8) | (cxuv << 0);
+
+ return 0;
+}
+
+static struct {
+ u16 hps_coeff;
+ u16 weight_sum;
+} hps_v_coeff_tab [] = {
+ {0x0100, 2}, {0x0102, 4}, {0x0300, 4}, {0x0106, 8}, {0x0502, 8},
+ {0x0708, 8}, {0x0F00, 8}, {0x011E, 16}, {0x110E, 16}, {0x1926, 16},
+ {0x3906, 16}, {0x3D42, 16}, {0x7D02, 16}, {0x7F80, 16}, {0xFF00, 16},
+ {0x01FE, 32}, {0x01FE, 32}, {0x817E, 32}, {0x817E, 32}, {0xC13E, 32},
+ {0xC13E, 32}, {0xE11E, 32}, {0xE11E, 32}, {0xF10E, 32}, {0xF10E, 32},
+ {0xF906, 32}, {0xF906, 32}, {0xFD02, 32}, {0xFD02, 32}, {0xFF00, 32},
+ {0xFF00, 32}, {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64},
+ {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64},
+ {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64},
+ {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, {0x817E, 64},
+ {0x817E, 64}, {0xC13E, 64}, {0xC13E, 64}, {0xE11E, 64}, {0xE11E, 64},
+ {0xF10E, 64}, {0xF10E, 64}, {0xF906, 64}, {0xF906, 64}, {0xFD02, 64},
+ {0xFD02, 64}, {0xFF00, 64}, {0xFF00, 64}, {0x01FE, 128}
+};
+
+/* table of attenuation values for vertical scaling */
+static u16 v_attenuation[] = { 2, 4, 8, 16, 32, 64, 128, 256, 0};
+
+/* calculate vertical scale registers */
+static int calculate_v_scale_registers(struct saa7146_dev *dev, enum v4l2_field field,
+ int in_y, int out_y, u32* hps_v_scale, u32* hps_v_gain)
+{
+ int lpi = 0;
+
+ /* vertical scaling */
+ u32 yacm = 0, ysci = 0, yacl = 0, ypo = 0, ype = 0;
+ /* vertical scale & gain */
+ u32 dcgy = 0, cya_cyb = 0;
+
+ /* helper variables */
+ u32 v_atten = 0, i = 0;
+
+ /* error, if vertical zooming */
+ if ( in_y < out_y ) {
+ return -EINVAL;
+ }
+
+ /* linear phase interpolation may be used
+ if scaling is between 1 and 1/2 (both fields used)
+ or scaling is between 1/2 and 1/4 (if only one field is used) */
+
+ if (V4L2_FIELD_HAS_BOTH(field)) {
+ if( 2*out_y >= in_y) {
+ lpi = 1;
+ }
+ } else if (field == V4L2_FIELD_TOP
+ || field == V4L2_FIELD_ALTERNATE
+ || field == V4L2_FIELD_BOTTOM) {
+ if( 4*out_y >= in_y ) {
+ lpi = 1;
+ }
+ out_y *= 2;
+ }
+ if( 0 != lpi ) {
+
+ yacm = 0;
+ yacl = 0;
+ cya_cyb = 0x00ff;
+
+ /* calculate scaling increment */
+ if ( in_y > out_y )
+ ysci = ((1024 * in_y) / (out_y + 1)) - 1024;
+ else
+ ysci = 0;
+
+ dcgy = 0;
+
+ /* calculate ype and ypo */
+ ype = ysci / 16;
+ ypo = ype + (ysci / 64);
+
+ } else {
+ yacm = 1;
+
+ /* calculate scaling increment */
+ ysci = (((10 * 1024 * (in_y - out_y - 1)) / in_y) + 9) / 10;
+
+ /* calculate ype and ypo */
+ ypo = ype = ((ysci + 15) / 16);
+
+ /* the sequence length interval (yacl) has to be set according
+ to the prescale value, e.g. [n .. 1/2) : 0
+ [1/2 .. 1/3) : 1
+ [1/3 .. 1/4) : 2
+ ... */
+ if ( ysci < 512) {
+ yacl = 0;
+ } else {
+ yacl = ( ysci / (1024 - ysci) );
+ }
+
+ /* get filter coefficients for cya, cyb from table hps_v_coeff_tab */
+ cya_cyb = hps_v_coeff_tab[ (yacl < 63 ? yacl : 63 ) ].hps_coeff;
+
+ /* get best match in the table of attenuations for vertical scaling */
+ v_atten = hps_v_coeff_tab[ (yacl < 63 ? yacl : 63 ) ].weight_sum;
+
+ for (i = 0; v_attenuation[i] != 0; i++) {
+ if (v_attenuation[i] >= v_atten)
+ break;
+ }
+
+ dcgy = i;
+ }
+
+ /* ypo and ype swapped in spec ? */
+ *hps_v_scale |= (yacm << 31) | (ysci << 21) | (yacl << 15) | (ypo << 8 ) | (ype << 1);
+
+ *hps_v_gain &= ~(MASK_W0|MASK_B2);
+ *hps_v_gain |= (dcgy << 16) | (cya_cyb << 0);
+
+ return 0;
+}
+
+/* simple bubble-sort algorithm with duplicate elimination */
+static int sort_and_eliminate(u32* values, int* count)
+{
+ int low = 0, high = 0, top = 0;
+ int cur = 0, next = 0;
+
+ /* sanity checks */
+ if( (0 > *count) || (NULL == values) ) {
+ return -EINVAL;
+ }
+
+ /* bubble sort the first @count items of the array @values */
+ for( top = *count; top > 0; top--) {
+ for( low = 0, high = 1; high < top; low++, high++) {
+ if( values[low] > values[high] )
+ swap(values[low], values[high]);
+ }
+ }
+
+ /* remove duplicate items */
+ for( cur = 0, next = 1; next < *count; next++) {
+ if( values[cur] != values[next])
+ values[++cur] = values[next];
+ }
+
+ *count = cur + 1;
+
+ return 0;
+}
+
+static void calculate_clipping_registers_rect(struct saa7146_dev *dev, struct saa7146_fh *fh,
+ struct saa7146_video_dma *vdma2, u32* clip_format, u32* arbtr_ctrl, enum v4l2_field field)
+{
+ struct saa7146_vv *vv = dev->vv_data;
+ __le32 *clipping = vv->d_clipping.cpu_addr;
+
+ int width = vv->ov.win.w.width;
+ int height = vv->ov.win.w.height;
+ int clipcount = vv->ov.nclips;
+
+ u32 line_list[32];
+ u32 pixel_list[32];
+ int numdwords = 0;
+
+ int i = 0, j = 0;
+ int cnt_line = 0, cnt_pixel = 0;
+
+ int x[32], y[32], w[32], h[32];
+
+ /* clear out memory */
+ memset(&line_list[0], 0x00, sizeof(u32)*32);
+ memset(&pixel_list[0], 0x00, sizeof(u32)*32);
+ memset(clipping, 0x00, SAA7146_CLIPPING_MEM);
+
+ /* fill the line and pixel-lists */
+ for(i = 0; i < clipcount; i++) {
+ int l = 0, r = 0, t = 0, b = 0;
+
+ x[i] = vv->ov.clips[i].c.left;
+ y[i] = vv->ov.clips[i].c.top;
+ w[i] = vv->ov.clips[i].c.width;
+ h[i] = vv->ov.clips[i].c.height;
+
+ if( w[i] < 0) {
+ x[i] += w[i]; w[i] = -w[i];
+ }
+ if( h[i] < 0) {
+ y[i] += h[i]; h[i] = -h[i];
+ }
+ if( x[i] < 0) {
+ w[i] += x[i]; x[i] = 0;
+ }
+ if( y[i] < 0) {
+ h[i] += y[i]; y[i] = 0;
+ }
+ if( 0 != vv->vflip ) {
+ y[i] = height - y[i] - h[i];
+ }
+
+ l = x[i];
+ r = x[i]+w[i];
+ t = y[i];
+ b = y[i]+h[i];
+
+ /* insert left/right coordinates */
+ pixel_list[ 2*i ] = min_t(int, l, width);
+ pixel_list[(2*i)+1] = min_t(int, r, width);
+ /* insert top/bottom coordinates */
+ line_list[ 2*i ] = min_t(int, t, height);
+ line_list[(2*i)+1] = min_t(int, b, height);
+ }
+
+ /* sort and eliminate lists */
+ cnt_line = cnt_pixel = 2*clipcount;
+ sort_and_eliminate( &pixel_list[0], &cnt_pixel );
+ sort_and_eliminate( &line_list[0], &cnt_line );
+
+ /* calculate the number of used u32s */
+ numdwords = max_t(int, (cnt_line+1), (cnt_pixel+1))*2;
+ numdwords = max_t(int, 4, numdwords);
+ numdwords = min_t(int, 64, numdwords);
+
+ /* fill up cliptable */
+ for(i = 0; i < cnt_pixel; i++) {
+ clipping[2*i] |= cpu_to_le32(pixel_list[i] << 16);
+ }
+ for(i = 0; i < cnt_line; i++) {
+ clipping[(2*i)+1] |= cpu_to_le32(line_list[i] << 16);
+ }
+
+ /* fill up cliptable with the display infos */
+ for(j = 0; j < clipcount; j++) {
+
+ for(i = 0; i < cnt_pixel; i++) {
+
+ if( x[j] < 0)
+ x[j] = 0;
+
+ if( pixel_list[i] < (x[j] + w[j])) {
+
+ if ( pixel_list[i] >= x[j] ) {
+ clipping[2*i] |= cpu_to_le32(1 << j);
+ }
+ }
+ }
+ for(i = 0; i < cnt_line; i++) {
+
+ if( y[j] < 0)
+ y[j] = 0;
+
+ if( line_list[i] < (y[j] + h[j]) ) {
+
+ if( line_list[i] >= y[j] ) {
+ clipping[(2*i)+1] |= cpu_to_le32(1 << j);
+ }
+ }
+ }
+ }
+
+ /* adjust arbitration control register */
+ *arbtr_ctrl &= 0xffff00ff;
+ *arbtr_ctrl |= 0x00001c00;
+
+ vdma2->base_even = vv->d_clipping.dma_handle;
+ vdma2->base_odd = vv->d_clipping.dma_handle;
+ vdma2->prot_addr = vv->d_clipping.dma_handle+((sizeof(u32))*(numdwords));
+ vdma2->base_page = 0x04;
+ vdma2->pitch = 0x00;
+ vdma2->num_line_byte = (0 << 16 | (sizeof(u32))*(numdwords-1) );
+
+ /* set clipping-mode. this depends on the field(s) used */
+ *clip_format &= 0xfffffff7;
+ if (V4L2_FIELD_HAS_BOTH(field)) {
+ *clip_format |= 0x00000008;
+ } else {
+ *clip_format |= 0x00000000;
+ }
+}
+
+/* disable clipping */
+static void saa7146_disable_clipping(struct saa7146_dev *dev)
+{
+ u32 clip_format = saa7146_read(dev, CLIP_FORMAT_CTRL);
+
+ /* mask out relevant bits (=lower word)*/
+ clip_format &= MASK_W1;
+
+ /* upload clipping-registers*/
+ saa7146_write(dev, CLIP_FORMAT_CTRL,clip_format);
+ saa7146_write(dev, MC2, (MASK_05 | MASK_21));
+
+ /* disable video dma2 */
+ saa7146_write(dev, MC1, MASK_21);
+}
+
+static void saa7146_set_clipping_rect(struct saa7146_fh *fh)
+{
+ struct saa7146_dev *dev = fh->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+ enum v4l2_field field = vv->ov.win.field;
+ struct saa7146_video_dma vdma2;
+ u32 clip_format;
+ u32 arbtr_ctrl;
+
+ /* check clipcount, disable clipping if clipcount == 0*/
+ if (vv->ov.nclips == 0) {
+ saa7146_disable_clipping(dev);
+ return;
+ }
+
+ clip_format = saa7146_read(dev, CLIP_FORMAT_CTRL);
+ arbtr_ctrl = saa7146_read(dev, PCI_BT_V1);
+
+ calculate_clipping_registers_rect(dev, fh, &vdma2, &clip_format, &arbtr_ctrl, field);
+
+ /* set clipping format */
+ clip_format &= 0xffff0008;
+ clip_format |= (SAA7146_CLIPPING_RECT << 4);
+
+ /* prepare video dma2 */
+ saa7146_write(dev, BASE_EVEN2, vdma2.base_even);
+ saa7146_write(dev, BASE_ODD2, vdma2.base_odd);
+ saa7146_write(dev, PROT_ADDR2, vdma2.prot_addr);
+ saa7146_write(dev, BASE_PAGE2, vdma2.base_page);
+ saa7146_write(dev, PITCH2, vdma2.pitch);
+ saa7146_write(dev, NUM_LINE_BYTE2, vdma2.num_line_byte);
+
+ /* prepare the rest */
+ saa7146_write(dev, CLIP_FORMAT_CTRL,clip_format);
+ saa7146_write(dev, PCI_BT_V1, arbtr_ctrl);
+
+ /* upload clip_control-register, clipping-registers, enable video dma2 */
+ saa7146_write(dev, MC2, (MASK_05 | MASK_21 | MASK_03 | MASK_19));
+ saa7146_write(dev, MC1, (MASK_05 | MASK_21));
+}
+
+static void saa7146_set_window(struct saa7146_dev *dev, int width, int height, enum v4l2_field field)
+{
+ struct saa7146_vv *vv = dev->vv_data;
+
+ int source = vv->current_hps_source;
+ int sync = vv->current_hps_sync;
+
+ u32 hps_v_scale = 0, hps_v_gain = 0, hps_ctrl = 0, hps_h_prescale = 0, hps_h_scale = 0;
+
+ /* set vertical scale */
+ hps_v_scale = 0; /* all bits get set by the function-call */
+ hps_v_gain = 0; /* fixme: saa7146_read(dev, HPS_V_GAIN);*/
+ calculate_v_scale_registers(dev, field, vv->standard->v_field*2, height, &hps_v_scale, &hps_v_gain);
+
+ /* set horizontal scale */
+ hps_ctrl = 0;
+ hps_h_prescale = 0; /* all bits get set in the function */
+ hps_h_scale = 0;
+ calculate_h_scale_registers(dev, vv->standard->h_pixels, width, vv->hflip, &hps_ctrl, &hps_v_gain, &hps_h_prescale, &hps_h_scale);
+
+ /* set hyo and hxo */
+ calculate_hxo_and_hyo(vv, &hps_h_scale, &hps_ctrl);
+ calculate_hps_source_and_sync(dev, source, sync, &hps_ctrl);
+
+ /* write out new register contents */
+ saa7146_write(dev, HPS_V_SCALE, hps_v_scale);
+ saa7146_write(dev, HPS_V_GAIN, hps_v_gain);
+ saa7146_write(dev, HPS_CTRL, hps_ctrl);
+ saa7146_write(dev, HPS_H_PRESCALE,hps_h_prescale);
+ saa7146_write(dev, HPS_H_SCALE, hps_h_scale);
+
+ /* upload shadow-ram registers */
+ saa7146_write(dev, MC2, (MASK_05 | MASK_06 | MASK_21 | MASK_22) );
+}
+
+/* calculate the new memory offsets for a desired position */
+static void saa7146_set_position(struct saa7146_dev *dev, int w_x, int w_y, int w_height, enum v4l2_field field, u32 pixelformat)
+{
+ struct saa7146_vv *vv = dev->vv_data;
+ struct saa7146_format *sfmt = saa7146_format_by_fourcc(dev, pixelformat);
+
+ int b_depth = vv->ov_fmt->depth;
+ int b_bpl = vv->ov_fb.fmt.bytesperline;
+ /* The unsigned long cast is to remove a 64-bit compile warning since
+ it looks like a 64-bit address is cast to a 32-bit value, even
+ though the base pointer is really a 32-bit physical address that
+ goes into a 32-bit DMA register.
+ FIXME: might not work on some 64-bit platforms, but see the FIXME
+ in struct v4l2_framebuffer (videodev2.h) for that.
+ */
+ u32 base = (u32)(unsigned long)vv->ov_fb.base;
+
+ struct saa7146_video_dma vdma1;
+
+ /* calculate memory offsets for picture, look if we shall top-down-flip */
+ vdma1.pitch = 2*b_bpl;
+ if ( 0 == vv->vflip ) {
+ vdma1.base_even = base + (w_y * (vdma1.pitch/2)) + (w_x * (b_depth / 8));
+ vdma1.base_odd = vdma1.base_even + (vdma1.pitch / 2);
+ vdma1.prot_addr = vdma1.base_even + (w_height * (vdma1.pitch / 2));
+ }
+ else {
+ vdma1.base_even = base + ((w_y+w_height) * (vdma1.pitch/2)) + (w_x * (b_depth / 8));
+ vdma1.base_odd = vdma1.base_even - (vdma1.pitch / 2);
+ vdma1.prot_addr = vdma1.base_odd - (w_height * (vdma1.pitch / 2));
+ }
+
+ if (V4L2_FIELD_HAS_BOTH(field)) {
+ } else if (field == V4L2_FIELD_ALTERNATE) {
+ /* fixme */
+ vdma1.base_odd = vdma1.prot_addr;
+ vdma1.pitch /= 2;
+ } else if (field == V4L2_FIELD_TOP) {
+ vdma1.base_odd = vdma1.prot_addr;
+ vdma1.pitch /= 2;
+ } else if (field == V4L2_FIELD_BOTTOM) {
+ vdma1.base_odd = vdma1.base_even;
+ vdma1.base_even = vdma1.prot_addr;
+ vdma1.pitch /= 2;
+ }
+
+ if ( 0 != vv->vflip ) {
+ vdma1.pitch *= -1;
+ }
+
+ vdma1.base_page = sfmt->swap;
+ vdma1.num_line_byte = (vv->standard->v_field<<16)+vv->standard->h_pixels;
+
+ saa7146_write_out_dma(dev, 1, &vdma1);
+}
+
+static void saa7146_set_output_format(struct saa7146_dev *dev, unsigned long palette)
+{
+ u32 clip_format = saa7146_read(dev, CLIP_FORMAT_CTRL);
+
+ /* call helper function */
+ calculate_output_format_register(dev,palette,&clip_format);
+
+ /* update the hps registers */
+ saa7146_write(dev, CLIP_FORMAT_CTRL, clip_format);
+ saa7146_write(dev, MC2, (MASK_05 | MASK_21));
+}
+
+/* select input-source */
+void saa7146_set_hps_source_and_sync(struct saa7146_dev *dev, int source, int sync)
+{
+ struct saa7146_vv *vv = dev->vv_data;
+ u32 hps_ctrl = 0;
+
+ /* read old state */
+ hps_ctrl = saa7146_read(dev, HPS_CTRL);
+
+ hps_ctrl &= ~( MASK_31 | MASK_30 | MASK_28 );
+ hps_ctrl |= (source << 30) | (sync << 28);
+
+ /* write back & upload register */
+ saa7146_write(dev, HPS_CTRL, hps_ctrl);
+ saa7146_write(dev, MC2, (MASK_05 | MASK_21));
+
+ vv->current_hps_source = source;
+ vv->current_hps_sync = sync;
+}
+EXPORT_SYMBOL_GPL(saa7146_set_hps_source_and_sync);
+
+int saa7146_enable_overlay(struct saa7146_fh *fh)
+{
+ struct saa7146_dev *dev = fh->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+
+ saa7146_set_window(dev, vv->ov.win.w.width, vv->ov.win.w.height, vv->ov.win.field);
+ saa7146_set_position(dev, vv->ov.win.w.left, vv->ov.win.w.top, vv->ov.win.w.height, vv->ov.win.field, vv->ov_fmt->pixelformat);
+ saa7146_set_output_format(dev, vv->ov_fmt->trans);
+ saa7146_set_clipping_rect(fh);
+
+ /* enable video dma1 */
+ saa7146_write(dev, MC1, (MASK_06 | MASK_22));
+ return 0;
+}
+
+void saa7146_disable_overlay(struct saa7146_fh *fh)
+{
+ struct saa7146_dev *dev = fh->dev;
+
+ /* disable clipping + video dma1 */
+ saa7146_disable_clipping(dev);
+ saa7146_write(dev, MC1, MASK_22);
+}
+
+void saa7146_write_out_dma(struct saa7146_dev* dev, int which, struct saa7146_video_dma* vdma)
+{
+ int where = 0;
+
+ if( which < 1 || which > 3) {
+ return;
+ }
+
+ /* calculate starting address */
+ where = (which-1)*0x18;
+
+ saa7146_write(dev, where, vdma->base_odd);
+ saa7146_write(dev, where+0x04, vdma->base_even);
+ saa7146_write(dev, where+0x08, vdma->prot_addr);
+ saa7146_write(dev, where+0x0c, vdma->pitch);
+ saa7146_write(dev, where+0x10, vdma->base_page);
+ saa7146_write(dev, where+0x14, vdma->num_line_byte);
+
+ /* upload */
+ saa7146_write(dev, MC2, (MASK_02<<(which-1))|(MASK_18<<(which-1)));
+/*
+ printk("vdma%d.base_even: 0x%08x\n", which,vdma->base_even);
+ printk("vdma%d.base_odd: 0x%08x\n", which,vdma->base_odd);
+ printk("vdma%d.prot_addr: 0x%08x\n", which,vdma->prot_addr);
+ printk("vdma%d.base_page: 0x%08x\n", which,vdma->base_page);
+ printk("vdma%d.pitch: 0x%08x\n", which,vdma->pitch);
+ printk("vdma%d.num_line_byte: 0x%08x\n", which,vdma->num_line_byte);
+*/
+}
+
+static int calculate_video_dma_grab_packed(struct saa7146_dev* dev, struct saa7146_buf *buf)
+{
+ struct saa7146_vv *vv = dev->vv_data;
+ struct saa7146_video_dma vdma1;
+
+ struct saa7146_format *sfmt = saa7146_format_by_fourcc(dev,buf->fmt->pixelformat);
+
+ int width = buf->fmt->width;
+ int height = buf->fmt->height;
+ int bytesperline = buf->fmt->bytesperline;
+ enum v4l2_field field = buf->fmt->field;
+
+ int depth = sfmt->depth;
+
+ DEB_CAP("[size=%dx%d,fields=%s]\n",
+ width, height, v4l2_field_names[field]);
+
+ if( bytesperline != 0) {
+ vdma1.pitch = bytesperline*2;
+ } else {
+ vdma1.pitch = (width*depth*2)/8;
+ }
+ vdma1.num_line_byte = ((vv->standard->v_field<<16) + vv->standard->h_pixels);
+ vdma1.base_page = buf->pt[0].dma | ME1 | sfmt->swap;
+
+ if( 0 != vv->vflip ) {
+ vdma1.prot_addr = buf->pt[0].offset;
+ vdma1.base_even = buf->pt[0].offset+(vdma1.pitch/2)*height;
+ vdma1.base_odd = vdma1.base_even - (vdma1.pitch/2);
+ } else {
+ vdma1.base_even = buf->pt[0].offset;
+ vdma1.base_odd = vdma1.base_even + (vdma1.pitch/2);
+ vdma1.prot_addr = buf->pt[0].offset+(vdma1.pitch/2)*height;
+ }
+
+ if (V4L2_FIELD_HAS_BOTH(field)) {
+ } else if (field == V4L2_FIELD_ALTERNATE) {
+ /* fixme */
+ if ( vv->last_field == V4L2_FIELD_TOP ) {
+ vdma1.base_odd = vdma1.prot_addr;
+ vdma1.pitch /= 2;
+ } else if ( vv->last_field == V4L2_FIELD_BOTTOM ) {
+ vdma1.base_odd = vdma1.base_even;
+ vdma1.base_even = vdma1.prot_addr;
+ vdma1.pitch /= 2;
+ }
+ } else if (field == V4L2_FIELD_TOP) {
+ vdma1.base_odd = vdma1.prot_addr;
+ vdma1.pitch /= 2;
+ } else if (field == V4L2_FIELD_BOTTOM) {
+ vdma1.base_odd = vdma1.base_even;
+ vdma1.base_even = vdma1.prot_addr;
+ vdma1.pitch /= 2;
+ }
+
+ if( 0 != vv->vflip ) {
+ vdma1.pitch *= -1;
+ }
+
+ saa7146_write_out_dma(dev, 1, &vdma1);
+ return 0;
+}
+
+static int calc_planar_422(struct saa7146_vv *vv, struct saa7146_buf *buf, struct saa7146_video_dma *vdma2, struct saa7146_video_dma *vdma3)
+{
+ int height = buf->fmt->height;
+ int width = buf->fmt->width;
+
+ vdma2->pitch = width;
+ vdma3->pitch = width;
+
+ /* fixme: look at bytesperline! */
+
+ if( 0 != vv->vflip ) {
+ vdma2->prot_addr = buf->pt[1].offset;
+ vdma2->base_even = ((vdma2->pitch/2)*height)+buf->pt[1].offset;
+ vdma2->base_odd = vdma2->base_even - (vdma2->pitch/2);
+
+ vdma3->prot_addr = buf->pt[2].offset;
+ vdma3->base_even = ((vdma3->pitch/2)*height)+buf->pt[2].offset;
+ vdma3->base_odd = vdma3->base_even - (vdma3->pitch/2);
+ } else {
+ vdma3->base_even = buf->pt[2].offset;
+ vdma3->base_odd = vdma3->base_even + (vdma3->pitch/2);
+ vdma3->prot_addr = (vdma3->pitch/2)*height+buf->pt[2].offset;
+
+ vdma2->base_even = buf->pt[1].offset;
+ vdma2->base_odd = vdma2->base_even + (vdma2->pitch/2);
+ vdma2->prot_addr = (vdma2->pitch/2)*height+buf->pt[1].offset;
+ }
+
+ return 0;
+}
+
+static int calc_planar_420(struct saa7146_vv *vv, struct saa7146_buf *buf, struct saa7146_video_dma *vdma2, struct saa7146_video_dma *vdma3)
+{
+ int height = buf->fmt->height;
+ int width = buf->fmt->width;
+
+ vdma2->pitch = width/2;
+ vdma3->pitch = width/2;
+
+ if( 0 != vv->vflip ) {
+ vdma2->prot_addr = buf->pt[2].offset;
+ vdma2->base_even = ((vdma2->pitch/2)*height)+buf->pt[2].offset;
+ vdma2->base_odd = vdma2->base_even - (vdma2->pitch/2);
+
+ vdma3->prot_addr = buf->pt[1].offset;
+ vdma3->base_even = ((vdma3->pitch/2)*height)+buf->pt[1].offset;
+ vdma3->base_odd = vdma3->base_even - (vdma3->pitch/2);
+
+ } else {
+ vdma3->base_even = buf->pt[2].offset;
+ vdma3->base_odd = vdma3->base_even + (vdma3->pitch);
+ vdma3->prot_addr = (vdma3->pitch/2)*height+buf->pt[2].offset;
+
+ vdma2->base_even = buf->pt[1].offset;
+ vdma2->base_odd = vdma2->base_even + (vdma2->pitch);
+ vdma2->prot_addr = (vdma2->pitch/2)*height+buf->pt[1].offset;
+ }
+ return 0;
+}
+
+static int calculate_video_dma_grab_planar(struct saa7146_dev* dev, struct saa7146_buf *buf)
+{
+ struct saa7146_vv *vv = dev->vv_data;
+ struct saa7146_video_dma vdma1;
+ struct saa7146_video_dma vdma2;
+ struct saa7146_video_dma vdma3;
+
+ struct saa7146_format *sfmt = saa7146_format_by_fourcc(dev,buf->fmt->pixelformat);
+
+ int width = buf->fmt->width;
+ int height = buf->fmt->height;
+ enum v4l2_field field = buf->fmt->field;
+
+ BUG_ON(0 == buf->pt[0].dma);
+ BUG_ON(0 == buf->pt[1].dma);
+ BUG_ON(0 == buf->pt[2].dma);
+
+ DEB_CAP("[size=%dx%d,fields=%s]\n",
+ width, height, v4l2_field_names[field]);
+
+ /* fixme: look at bytesperline! */
+
+ /* fixme: what happens for user space buffers here?. The offsets are
+ most likely wrong, this version here only works for page-aligned
+ buffers, modifications to the pagetable-functions are necessary...*/
+
+ vdma1.pitch = width*2;
+ vdma1.num_line_byte = ((vv->standard->v_field<<16) + vv->standard->h_pixels);
+ vdma1.base_page = buf->pt[0].dma | ME1;
+
+ if( 0 != vv->vflip ) {
+ vdma1.prot_addr = buf->pt[0].offset;
+ vdma1.base_even = ((vdma1.pitch/2)*height)+buf->pt[0].offset;
+ vdma1.base_odd = vdma1.base_even - (vdma1.pitch/2);
+ } else {
+ vdma1.base_even = buf->pt[0].offset;
+ vdma1.base_odd = vdma1.base_even + (vdma1.pitch/2);
+ vdma1.prot_addr = (vdma1.pitch/2)*height+buf->pt[0].offset;
+ }
+
+ vdma2.num_line_byte = 0; /* unused */
+ vdma2.base_page = buf->pt[1].dma | ME1;
+
+ vdma3.num_line_byte = 0; /* unused */
+ vdma3.base_page = buf->pt[2].dma | ME1;
+
+ switch( sfmt->depth ) {
+ case 12: {
+ calc_planar_420(vv,buf,&vdma2,&vdma3);
+ break;
+ }
+ case 16: {
+ calc_planar_422(vv,buf,&vdma2,&vdma3);
+ break;
+ }
+ default: {
+ return -1;
+ }
+ }
+
+ if (V4L2_FIELD_HAS_BOTH(field)) {
+ } else if (field == V4L2_FIELD_ALTERNATE) {
+ /* fixme */
+ vdma1.base_odd = vdma1.prot_addr;
+ vdma1.pitch /= 2;
+ vdma2.base_odd = vdma2.prot_addr;
+ vdma2.pitch /= 2;
+ vdma3.base_odd = vdma3.prot_addr;
+ vdma3.pitch /= 2;
+ } else if (field == V4L2_FIELD_TOP) {
+ vdma1.base_odd = vdma1.prot_addr;
+ vdma1.pitch /= 2;
+ vdma2.base_odd = vdma2.prot_addr;
+ vdma2.pitch /= 2;
+ vdma3.base_odd = vdma3.prot_addr;
+ vdma3.pitch /= 2;
+ } else if (field == V4L2_FIELD_BOTTOM) {
+ vdma1.base_odd = vdma1.base_even;
+ vdma1.base_even = vdma1.prot_addr;
+ vdma1.pitch /= 2;
+ vdma2.base_odd = vdma2.base_even;
+ vdma2.base_even = vdma2.prot_addr;
+ vdma2.pitch /= 2;
+ vdma3.base_odd = vdma3.base_even;
+ vdma3.base_even = vdma3.prot_addr;
+ vdma3.pitch /= 2;
+ }
+
+ if( 0 != vv->vflip ) {
+ vdma1.pitch *= -1;
+ vdma2.pitch *= -1;
+ vdma3.pitch *= -1;
+ }
+
+ saa7146_write_out_dma(dev, 1, &vdma1);
+ if( (sfmt->flags & FORMAT_BYTE_SWAP) != 0 ) {
+ saa7146_write_out_dma(dev, 3, &vdma2);
+ saa7146_write_out_dma(dev, 2, &vdma3);
+ } else {
+ saa7146_write_out_dma(dev, 2, &vdma2);
+ saa7146_write_out_dma(dev, 3, &vdma3);
+ }
+ return 0;
+}
+
+static void program_capture_engine(struct saa7146_dev *dev, int planar)
+{
+ struct saa7146_vv *vv = dev->vv_data;
+ int count = 0;
+
+ unsigned long e_wait = vv->current_hps_sync == SAA7146_HPS_SYNC_PORT_A ? CMD_E_FID_A : CMD_E_FID_B;
+ unsigned long o_wait = vv->current_hps_sync == SAA7146_HPS_SYNC_PORT_A ? CMD_O_FID_A : CMD_O_FID_B;
+
+ /* wait for o_fid_a/b / e_fid_a/b toggle only if rps register 0 is not set*/
+ WRITE_RPS0(CMD_PAUSE | CMD_OAN | CMD_SIG0 | o_wait);
+ WRITE_RPS0(CMD_PAUSE | CMD_OAN | CMD_SIG0 | e_wait);
+
+ /* set rps register 0 */
+ WRITE_RPS0(CMD_WR_REG | (1 << 8) | (MC2/4));
+ WRITE_RPS0(MASK_27 | MASK_11);
+
+ /* turn on video-dma1 */
+ WRITE_RPS0(CMD_WR_REG_MASK | (MC1/4));
+ WRITE_RPS0(MASK_06 | MASK_22); /* => mask */
+ WRITE_RPS0(MASK_06 | MASK_22); /* => values */
+ if( 0 != planar ) {
+ /* turn on video-dma2 */
+ WRITE_RPS0(CMD_WR_REG_MASK | (MC1/4));
+ WRITE_RPS0(MASK_05 | MASK_21); /* => mask */
+ WRITE_RPS0(MASK_05 | MASK_21); /* => values */
+
+ /* turn on video-dma3 */
+ WRITE_RPS0(CMD_WR_REG_MASK | (MC1/4));
+ WRITE_RPS0(MASK_04 | MASK_20); /* => mask */
+ WRITE_RPS0(MASK_04 | MASK_20); /* => values */
+ }
+
+ /* wait for o_fid_a/b / e_fid_a/b toggle */
+ if ( vv->last_field == V4L2_FIELD_INTERLACED ) {
+ WRITE_RPS0(CMD_PAUSE | o_wait);
+ WRITE_RPS0(CMD_PAUSE | e_wait);
+ } else if ( vv->last_field == V4L2_FIELD_TOP ) {
+ WRITE_RPS0(CMD_PAUSE | (vv->current_hps_sync == SAA7146_HPS_SYNC_PORT_A ? MASK_10 : MASK_09));
+ WRITE_RPS0(CMD_PAUSE | o_wait);
+ } else if ( vv->last_field == V4L2_FIELD_BOTTOM ) {
+ WRITE_RPS0(CMD_PAUSE | (vv->current_hps_sync == SAA7146_HPS_SYNC_PORT_A ? MASK_10 : MASK_09));
+ WRITE_RPS0(CMD_PAUSE | e_wait);
+ }
+
+ /* turn off video-dma1 */
+ WRITE_RPS0(CMD_WR_REG_MASK | (MC1/4));
+ WRITE_RPS0(MASK_22 | MASK_06); /* => mask */
+ WRITE_RPS0(MASK_22); /* => values */
+ if( 0 != planar ) {
+ /* turn off video-dma2 */
+ WRITE_RPS0(CMD_WR_REG_MASK | (MC1/4));
+ WRITE_RPS0(MASK_05 | MASK_21); /* => mask */
+ WRITE_RPS0(MASK_21); /* => values */
+
+ /* turn off video-dma3 */
+ WRITE_RPS0(CMD_WR_REG_MASK | (MC1/4));
+ WRITE_RPS0(MASK_04 | MASK_20); /* => mask */
+ WRITE_RPS0(MASK_20); /* => values */
+ }
+
+ /* generate interrupt */
+ WRITE_RPS0(CMD_INTERRUPT);
+
+ /* stop */
+ WRITE_RPS0(CMD_STOP);
+}
+
+void saa7146_set_capture(struct saa7146_dev *dev, struct saa7146_buf *buf, struct saa7146_buf *next)
+{
+ struct saa7146_format *sfmt = saa7146_format_by_fourcc(dev,buf->fmt->pixelformat);
+ struct saa7146_vv *vv = dev->vv_data;
+ u32 vdma1_prot_addr;
+
+ DEB_CAP("buf:%p, next:%p\n", buf, next);
+
+ vdma1_prot_addr = saa7146_read(dev, PROT_ADDR1);
+ if( 0 == vdma1_prot_addr ) {
+ /* clear out beginning of streaming bit (rps register 0)*/
+ DEB_CAP("forcing sync to new frame\n");
+ saa7146_write(dev, MC2, MASK_27 );
+ }
+
+ saa7146_set_window(dev, buf->fmt->width, buf->fmt->height, buf->fmt->field);
+ saa7146_set_output_format(dev, sfmt->trans);
+ saa7146_disable_clipping(dev);
+
+ if ( vv->last_field == V4L2_FIELD_INTERLACED ) {
+ } else if ( vv->last_field == V4L2_FIELD_TOP ) {
+ vv->last_field = V4L2_FIELD_BOTTOM;
+ } else if ( vv->last_field == V4L2_FIELD_BOTTOM ) {
+ vv->last_field = V4L2_FIELD_TOP;
+ }
+
+ if( 0 != IS_PLANAR(sfmt->trans)) {
+ calculate_video_dma_grab_planar(dev, buf);
+ program_capture_engine(dev,1);
+ } else {
+ calculate_video_dma_grab_packed(dev, buf);
+ program_capture_engine(dev,0);
+ }
+
+/*
+ printk("vdma%d.base_even: 0x%08x\n", 1,saa7146_read(dev,BASE_EVEN1));
+ printk("vdma%d.base_odd: 0x%08x\n", 1,saa7146_read(dev,BASE_ODD1));
+ printk("vdma%d.prot_addr: 0x%08x\n", 1,saa7146_read(dev,PROT_ADDR1));
+ printk("vdma%d.base_page: 0x%08x\n", 1,saa7146_read(dev,BASE_PAGE1));
+ printk("vdma%d.pitch: 0x%08x\n", 1,saa7146_read(dev,PITCH1));
+ printk("vdma%d.num_line_byte: 0x%08x\n", 1,saa7146_read(dev,NUM_LINE_BYTE1));
+ printk("vdma%d => vptr : 0x%08x\n", 1,saa7146_read(dev,PCI_VDP1));
+*/
+
+ /* write the address of the rps-program */
+ saa7146_write(dev, RPS_ADDR0, dev->d_rps0.dma_handle);
+
+ /* turn on rps */
+ saa7146_write(dev, MC1, (MASK_12 | MASK_28));
+}
diff --git a/drivers/media/common/saa7146/saa7146_i2c.c b/drivers/media/common/saa7146/saa7146_i2c.c
new file mode 100644
index 000000000000..df9ebe2a168c
--- /dev/null
+++ b/drivers/media/common/saa7146/saa7146_i2c.c
@@ -0,0 +1,421 @@
+// SPDX-License-Identifier: GPL-2.0
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <media/drv-intf/saa7146_vv.h>
+
+static u32 saa7146_i2c_func(struct i2c_adapter *adapter)
+{
+ /* DEB_I2C("'%s'\n", adapter->name); */
+
+ return I2C_FUNC_I2C
+ | I2C_FUNC_SMBUS_QUICK
+ | I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE
+ | I2C_FUNC_SMBUS_READ_BYTE_DATA | I2C_FUNC_SMBUS_WRITE_BYTE_DATA;
+}
+
+/* this function returns the status-register of our i2c-device */
+static inline u32 saa7146_i2c_status(struct saa7146_dev *dev)
+{
+ u32 iicsta = saa7146_read(dev, I2C_STATUS);
+ /* DEB_I2C("status: 0x%08x\n", iicsta); */
+ return iicsta;
+}
+
+/* this function runs through the i2c-messages and prepares the data to be
+ sent through the saa7146. have a look at the specifications p. 122 ff
+ to understand this. it returns the number of u32s to send, or -1
+ in case of an error. */
+static int saa7146_i2c_msg_prepare(const struct i2c_msg *m, int num, __le32 *op)
+{
+ int h1, h2;
+ int i, j, addr;
+ int mem = 0, op_count = 0;
+
+ /* first determine size of needed memory */
+ for(i = 0; i < num; i++) {
+ mem += m[i].len + 1;
+ }
+
+ /* worst case: we need one u32 for three bytes to be send
+ plus one extra byte to address the device */
+ mem = 1 + ((mem-1) / 3);
+
+ /* we assume that op points to a memory of at least
+ * SAA7146_I2C_MEM bytes size. if we exceed this limit...
+ */
+ if ((4 * mem) > SAA7146_I2C_MEM) {
+ /* DEB_I2C("cannot prepare i2c-message\n"); */
+ return -ENOMEM;
+ }
+
+ /* be careful: clear out the i2c-mem first */
+ memset(op,0,sizeof(__le32)*mem);
+
+ /* loop through all messages */
+ for(i = 0; i < num; i++) {
+
+ addr = i2c_8bit_addr_from_msg(&m[i]);
+ h1 = op_count/3; h2 = op_count%3;
+ op[h1] |= cpu_to_le32( (u8)addr << ((3-h2)*8));
+ op[h1] |= cpu_to_le32(SAA7146_I2C_START << ((3-h2)*2));
+ op_count++;
+
+ /* loop through all bytes of message i */
+ for(j = 0; j < m[i].len; j++) {
+ /* insert the data bytes */
+ h1 = op_count/3; h2 = op_count%3;
+ op[h1] |= cpu_to_le32( (u32)((u8)m[i].buf[j]) << ((3-h2)*8));
+ op[h1] |= cpu_to_le32( SAA7146_I2C_CONT << ((3-h2)*2));
+ op_count++;
+ }
+
+ }
+
+ /* have a look at the last byte inserted:
+ if it was: ...CONT change it to ...STOP */
+ h1 = (op_count-1)/3; h2 = (op_count-1)%3;
+ if ( SAA7146_I2C_CONT == (0x3 & (le32_to_cpu(op[h1]) >> ((3-h2)*2))) ) {
+ op[h1] &= ~cpu_to_le32(0x2 << ((3-h2)*2));
+ op[h1] |= cpu_to_le32(SAA7146_I2C_STOP << ((3-h2)*2));
+ }
+
+ /* return the number of u32s to send */
+ return mem;
+}
+
+/* this functions loops through all i2c-messages. normally, it should determine
+ which bytes were read through the adapter and write them back to the corresponding
+ i2c-message. but instead, we simply write back all bytes.
+ fixme: this could be improved. */
+static int saa7146_i2c_msg_cleanup(const struct i2c_msg *m, int num, __le32 *op)
+{
+ int i, j;
+ int op_count = 0;
+
+ /* loop through all messages */
+ for(i = 0; i < num; i++) {
+
+ op_count++;
+
+ /* loop through all bytes of message i */
+ for(j = 0; j < m[i].len; j++) {
+ /* write back all bytes that could have been read */
+ m[i].buf[j] = (le32_to_cpu(op[op_count/3]) >> ((3-(op_count%3))*8));
+ op_count++;
+ }
+ }
+
+ return 0;
+}
+
+/* this functions resets the i2c-device and returns 0 if everything was fine, otherwise -1 */
+static int saa7146_i2c_reset(struct saa7146_dev *dev)
+{
+ /* get current status */
+ u32 status = saa7146_i2c_status(dev);
+
+ /* clear registers for sure */
+ saa7146_write(dev, I2C_STATUS, dev->i2c_bitrate);
+ saa7146_write(dev, I2C_TRANSFER, 0);
+
+ /* check if any operation is still in progress */
+ if ( 0 != ( status & SAA7146_I2C_BUSY) ) {
+
+ /* yes, kill ongoing operation */
+ DEB_I2C("busy_state detected\n");
+
+ /* set "ABORT-OPERATION"-bit (bit 7)*/
+ saa7146_write(dev, I2C_STATUS, (dev->i2c_bitrate | MASK_07));
+ saa7146_write(dev, MC2, (MASK_00 | MASK_16));
+ msleep(SAA7146_I2C_DELAY);
+
+ /* clear all error-bits pending; this is needed because p.123, note 1 */
+ saa7146_write(dev, I2C_STATUS, dev->i2c_bitrate);
+ saa7146_write(dev, MC2, (MASK_00 | MASK_16));
+ msleep(SAA7146_I2C_DELAY);
+ }
+
+ /* check if any error is (still) present. (this can be necessary because p.123, note 1) */
+ status = saa7146_i2c_status(dev);
+
+ if ( dev->i2c_bitrate != status ) {
+
+ DEB_I2C("error_state detected. status:0x%08x\n", status);
+
+ /* Repeat the abort operation. This seems to be necessary
+ after serious protocol errors caused by e.g. the SAA7740 */
+ saa7146_write(dev, I2C_STATUS, (dev->i2c_bitrate | MASK_07));
+ saa7146_write(dev, MC2, (MASK_00 | MASK_16));
+ msleep(SAA7146_I2C_DELAY);
+
+ /* clear all error-bits pending */
+ saa7146_write(dev, I2C_STATUS, dev->i2c_bitrate);
+ saa7146_write(dev, MC2, (MASK_00 | MASK_16));
+ msleep(SAA7146_I2C_DELAY);
+
+ /* the data sheet says it might be necessary to clear the status
+ twice after an abort */
+ saa7146_write(dev, I2C_STATUS, dev->i2c_bitrate);
+ saa7146_write(dev, MC2, (MASK_00 | MASK_16));
+ msleep(SAA7146_I2C_DELAY);
+ }
+
+ /* if any error is still present, a fatal error has occurred ... */
+ status = saa7146_i2c_status(dev);
+ if ( dev->i2c_bitrate != status ) {
+ DEB_I2C("fatal error. status:0x%08x\n", status);
+ return -1;
+ }
+
+ return 0;
+}
+
+/* this functions writes out the data-byte 'dword' to the i2c-device.
+ it returns 0 if ok, -1 if the transfer failed, -2 if the transfer
+ failed badly (e.g. address error) */
+static int saa7146_i2c_writeout(struct saa7146_dev *dev, __le32 *dword, int short_delay)
+{
+ u32 status = 0, mc2 = 0;
+ int trial = 0;
+ unsigned long timeout;
+
+ /* write out i2c-command */
+ DEB_I2C("before: 0x%08x (status: 0x%08x), %d\n",
+ *dword, saa7146_read(dev, I2C_STATUS), dev->i2c_op);
+
+ if( 0 != (SAA7146_USE_I2C_IRQ & dev->ext->flags)) {
+
+ saa7146_write(dev, I2C_STATUS, dev->i2c_bitrate);
+ saa7146_write(dev, I2C_TRANSFER, le32_to_cpu(*dword));
+
+ dev->i2c_op = 1;
+ SAA7146_ISR_CLEAR(dev, MASK_16|MASK_17);
+ SAA7146_IER_ENABLE(dev, MASK_16|MASK_17);
+ saa7146_write(dev, MC2, (MASK_00 | MASK_16));
+
+ timeout = HZ/100 + 1; /* 10ms */
+ timeout = wait_event_interruptible_timeout(dev->i2c_wq, dev->i2c_op == 0, timeout);
+ if (timeout == -ERESTARTSYS || dev->i2c_op) {
+ SAA7146_IER_DISABLE(dev, MASK_16|MASK_17);
+ SAA7146_ISR_CLEAR(dev, MASK_16|MASK_17);
+ if (timeout == -ERESTARTSYS)
+ /* a signal arrived */
+ return -ERESTARTSYS;
+
+ pr_warn("%s %s [irq]: timed out waiting for end of xfer\n",
+ dev->name, __func__);
+ return -EIO;
+ }
+ status = saa7146_read(dev, I2C_STATUS);
+ } else {
+ saa7146_write(dev, I2C_STATUS, dev->i2c_bitrate);
+ saa7146_write(dev, I2C_TRANSFER, le32_to_cpu(*dword));
+ saa7146_write(dev, MC2, (MASK_00 | MASK_16));
+
+ /* do not poll for i2c-status before upload is complete */
+ timeout = jiffies + HZ/100 + 1; /* 10ms */
+ while(1) {
+ mc2 = (saa7146_read(dev, MC2) & 0x1);
+ if( 0 != mc2 ) {
+ break;
+ }
+ if (time_after(jiffies,timeout)) {
+ pr_warn("%s %s: timed out waiting for MC2\n",
+ dev->name, __func__);
+ return -EIO;
+ }
+ }
+ /* wait until we get a transfer done or error */
+ timeout = jiffies + HZ/100 + 1; /* 10ms */
+ /* first read usually delivers bogus results... */
+ saa7146_i2c_status(dev);
+ while(1) {
+ status = saa7146_i2c_status(dev);
+ if ((status & 0x3) != 1)
+ break;
+ if (time_after(jiffies,timeout)) {
+ /* this is normal when probing the bus
+ * (no answer from nonexisistant device...)
+ */
+ pr_warn("%s %s [poll]: timed out waiting for end of xfer\n",
+ dev->name, __func__);
+ return -EIO;
+ }
+ if (++trial < 50 && short_delay)
+ udelay(10);
+ else
+ msleep(1);
+ }
+ }
+
+ /* give a detailed status report */
+ if ( 0 != (status & (SAA7146_I2C_SPERR | SAA7146_I2C_APERR |
+ SAA7146_I2C_DTERR | SAA7146_I2C_DRERR |
+ SAA7146_I2C_AL | SAA7146_I2C_ERR |
+ SAA7146_I2C_BUSY)) ) {
+
+ if ( 0 == (status & SAA7146_I2C_ERR) ||
+ 0 == (status & SAA7146_I2C_BUSY) ) {
+ /* it may take some time until ERR goes high - ignore */
+ DEB_I2C("unexpected i2c status %04x\n", status);
+ }
+ if( 0 != (status & SAA7146_I2C_SPERR) ) {
+ DEB_I2C("error due to invalid start/stop condition\n");
+ }
+ if( 0 != (status & SAA7146_I2C_DTERR) ) {
+ DEB_I2C("error in data transmission\n");
+ }
+ if( 0 != (status & SAA7146_I2C_DRERR) ) {
+ DEB_I2C("error when receiving data\n");
+ }
+ if( 0 != (status & SAA7146_I2C_AL) ) {
+ DEB_I2C("error because arbitration lost\n");
+ }
+
+ /* we handle address-errors here */
+ if( 0 != (status & SAA7146_I2C_APERR) ) {
+ DEB_I2C("error in address phase\n");
+ return -EREMOTEIO;
+ }
+
+ return -EIO;
+ }
+
+ /* read back data, just in case we were reading ... */
+ *dword = cpu_to_le32(saa7146_read(dev, I2C_TRANSFER));
+
+ DEB_I2C("after: 0x%08x\n", *dword);
+ return 0;
+}
+
+static int saa7146_i2c_transfer(struct saa7146_dev *dev, const struct i2c_msg *msgs, int num, int retries)
+{
+ int i = 0, count = 0;
+ __le32 *buffer = dev->d_i2c.cpu_addr;
+ int err = 0;
+ int short_delay = 0;
+
+ if (mutex_lock_interruptible(&dev->i2c_lock))
+ return -ERESTARTSYS;
+
+ for(i=0;i<num;i++) {
+ DEB_I2C("msg:%d/%d\n", i+1, num);
+ }
+
+ /* prepare the message(s), get number of u32s to transfer */
+ count = saa7146_i2c_msg_prepare(msgs, num, buffer);
+ if ( 0 > count ) {
+ err = -EIO;
+ goto out;
+ }
+
+ if ( count > 3 || 0 != (SAA7146_I2C_SHORT_DELAY & dev->ext->flags) )
+ short_delay = 1;
+
+ do {
+ /* reset the i2c-device if necessary */
+ err = saa7146_i2c_reset(dev);
+ if ( 0 > err ) {
+ DEB_I2C("could not reset i2c-device\n");
+ goto out;
+ }
+
+ /* write out the u32s one after another */
+ for(i = 0; i < count; i++) {
+ err = saa7146_i2c_writeout(dev, &buffer[i], short_delay);
+ if ( 0 != err) {
+ /* this one is unsatisfying: some i2c slaves on some
+ dvb cards don't acknowledge correctly, so the saa7146
+ thinks that an address error occurred. in that case, the
+ transaction should be retrying, even if an address error
+ occurred. analog saa7146 based cards extensively rely on
+ i2c address probing, however, and address errors indicate that a
+ device is really *not* there. retrying in that case
+ increases the time the device needs to probe greatly, so
+ it should be avoided. So we bail out in irq mode after an
+ address error and trust the saa7146 address error detection. */
+ if (-EREMOTEIO == err && 0 != (SAA7146_USE_I2C_IRQ & dev->ext->flags))
+ goto out;
+ DEB_I2C("error while sending message(s). starting again\n");
+ break;
+ }
+ }
+ if( 0 == err ) {
+ err = num;
+ break;
+ }
+
+ /* delay a bit before retrying */
+ msleep(10);
+
+ } while (err != num && retries--);
+
+ /* quit if any error occurred */
+ if (err != num)
+ goto out;
+
+ /* if any things had to be read, get the results */
+ if ( 0 != saa7146_i2c_msg_cleanup(msgs, num, buffer)) {
+ DEB_I2C("could not cleanup i2c-message\n");
+ err = -EIO;
+ goto out;
+ }
+
+ /* return the number of delivered messages */
+ DEB_I2C("transmission successful. (msg:%d)\n", err);
+out:
+ /* another bug in revision 0: the i2c-registers get uploaded randomly by other
+ uploads, so we better clear them out before continuing */
+ if( 0 == dev->revision ) {
+ __le32 zero = 0;
+ saa7146_i2c_reset(dev);
+ if( 0 != saa7146_i2c_writeout(dev, &zero, short_delay)) {
+ pr_info("revision 0 error. this should never happen\n");
+ }
+ }
+
+ mutex_unlock(&dev->i2c_lock);
+ return err;
+}
+
+/* utility functions */
+static int saa7146_i2c_xfer(struct i2c_adapter* adapter, struct i2c_msg *msg, int num)
+{
+ struct v4l2_device *v4l2_dev = i2c_get_adapdata(adapter);
+ struct saa7146_dev *dev = to_saa7146_dev(v4l2_dev);
+
+ /* use helper function to transfer data */
+ return saa7146_i2c_transfer(dev, msg, num, adapter->retries);
+}
+
+
+/*****************************************************************************/
+/* i2c-adapter helper functions */
+
+/* exported algorithm data */
+static const struct i2c_algorithm saa7146_algo = {
+ .master_xfer = saa7146_i2c_xfer,
+ .functionality = saa7146_i2c_func,
+};
+
+int saa7146_i2c_adapter_prepare(struct saa7146_dev *dev, struct i2c_adapter *i2c_adapter, u32 bitrate)
+{
+ DEB_EE("bitrate: 0x%08x\n", bitrate);
+
+ /* enable i2c-port pins */
+ saa7146_write(dev, MC1, (MASK_08 | MASK_24));
+
+ dev->i2c_bitrate = bitrate;
+ saa7146_i2c_reset(dev);
+
+ if (i2c_adapter) {
+ i2c_set_adapdata(i2c_adapter, &dev->v4l2_dev);
+ i2c_adapter->dev.parent = &dev->pci->dev;
+ i2c_adapter->algo = &saa7146_algo;
+ i2c_adapter->algo_data = NULL;
+ i2c_adapter->timeout = SAA7146_I2C_TIMEOUT;
+ i2c_adapter->retries = SAA7146_I2C_RETRIES;
+ }
+
+ return 0;
+}
diff --git a/drivers/media/common/saa7146/saa7146_vbi.c b/drivers/media/common/saa7146/saa7146_vbi.c
new file mode 100644
index 000000000000..bd442b984423
--- /dev/null
+++ b/drivers/media/common/saa7146/saa7146_vbi.c
@@ -0,0 +1,498 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <media/drv-intf/saa7146_vv.h>
+
+static int vbi_pixel_to_capture = 720 * 2;
+
+static int vbi_workaround(struct saa7146_dev *dev)
+{
+ struct saa7146_vv *vv = dev->vv_data;
+
+ u32 *cpu;
+ dma_addr_t dma_addr;
+
+ int count = 0;
+ int i;
+
+ DECLARE_WAITQUEUE(wait, current);
+
+ DEB_VBI("dev:%p\n", dev);
+
+ /* once again, a bug in the saa7146: the brs acquisition
+ is buggy and especially the BXO-counter does not work
+ as specified. there is this workaround, but please
+ don't let me explain it. ;-) */
+
+ cpu = dma_alloc_coherent(&dev->pci->dev, 4096, &dma_addr, GFP_KERNEL);
+ if (NULL == cpu)
+ return -ENOMEM;
+
+ /* setup some basic programming, just for the workaround */
+ saa7146_write(dev, BASE_EVEN3, dma_addr);
+ saa7146_write(dev, BASE_ODD3, dma_addr+vbi_pixel_to_capture);
+ saa7146_write(dev, PROT_ADDR3, dma_addr+4096);
+ saa7146_write(dev, PITCH3, vbi_pixel_to_capture);
+ saa7146_write(dev, BASE_PAGE3, 0x0);
+ saa7146_write(dev, NUM_LINE_BYTE3, (2<<16)|((vbi_pixel_to_capture)<<0));
+ saa7146_write(dev, MC2, MASK_04|MASK_20);
+
+ /* load brs-control register */
+ WRITE_RPS1(CMD_WR_REG | (1 << 8) | (BRS_CTRL/4));
+ /* BXO = 1h, BRS to outbound */
+ WRITE_RPS1(0xc000008c);
+ /* wait for vbi_a or vbi_b*/
+ if ( 0 != (SAA7146_USE_PORT_B_FOR_VBI & dev->ext_vv_data->flags)) {
+ DEB_D("...using port b\n");
+ WRITE_RPS1(CMD_PAUSE | CMD_OAN | CMD_SIG1 | CMD_E_FID_B);
+ WRITE_RPS1(CMD_PAUSE | CMD_OAN | CMD_SIG1 | CMD_O_FID_B);
+/*
+ WRITE_RPS1(CMD_PAUSE | MASK_09);
+*/
+ } else {
+ DEB_D("...using port a\n");
+ WRITE_RPS1(CMD_PAUSE | MASK_10);
+ }
+ /* upload brs */
+ WRITE_RPS1(CMD_UPLOAD | MASK_08);
+ /* load brs-control register */
+ WRITE_RPS1(CMD_WR_REG | (1 << 8) | (BRS_CTRL/4));
+ /* BYO = 1, BXO = NQBIL (=1728 for PAL, for NTSC this is 858*2) - NumByte3 (=1440) = 288 */
+ WRITE_RPS1(((1728-(vbi_pixel_to_capture)) << 7) | MASK_19);
+ /* wait for brs_done */
+ WRITE_RPS1(CMD_PAUSE | MASK_08);
+ /* upload brs */
+ WRITE_RPS1(CMD_UPLOAD | MASK_08);
+ /* load video-dma3 NumLines3 and NumBytes3 */
+ WRITE_RPS1(CMD_WR_REG | (1 << 8) | (NUM_LINE_BYTE3/4));
+ /* dev->vbi_count*2 lines, 720 pixel (= 1440 Bytes) */
+ WRITE_RPS1((2 << 16) | (vbi_pixel_to_capture));
+ /* load brs-control register */
+ WRITE_RPS1(CMD_WR_REG | (1 << 8) | (BRS_CTRL/4));
+ /* Set BRS right: note: this is an experimental value for BXO (=> PAL!) */
+ WRITE_RPS1((540 << 7) | (5 << 19)); // 5 == vbi_start
+ /* wait for brs_done */
+ WRITE_RPS1(CMD_PAUSE | MASK_08);
+ /* upload brs and video-dma3*/
+ WRITE_RPS1(CMD_UPLOAD | MASK_08 | MASK_04);
+ /* load mc2 register: enable dma3 */
+ WRITE_RPS1(CMD_WR_REG | (1 << 8) | (MC1/4));
+ WRITE_RPS1(MASK_20 | MASK_04);
+ /* generate interrupt */
+ WRITE_RPS1(CMD_INTERRUPT);
+ /* stop rps1 */
+ WRITE_RPS1(CMD_STOP);
+
+ /* we have to do the workaround twice to be sure that
+ everything is ok */
+ for(i = 0; i < 2; i++) {
+
+ /* indicate to the irq handler that we do the workaround */
+ saa7146_write(dev, MC2, MASK_31|MASK_15);
+
+ saa7146_write(dev, NUM_LINE_BYTE3, (1<<16)|(2<<0));
+ saa7146_write(dev, MC2, MASK_04|MASK_20);
+
+ /* enable rps1 irqs */
+ SAA7146_IER_ENABLE(dev,MASK_28);
+
+ /* prepare to wait to be woken up by the irq-handler */
+ add_wait_queue(&vv->vbi_wq, &wait);
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ /* start rps1 to enable workaround */
+ saa7146_write(dev, RPS_ADDR1, dev->d_rps1.dma_handle);
+ saa7146_write(dev, MC1, (MASK_13 | MASK_29));
+
+ schedule();
+
+ DEB_VBI("brs bug workaround %d/1\n", i);
+
+ remove_wait_queue(&vv->vbi_wq, &wait);
+ __set_current_state(TASK_RUNNING);
+
+ /* disable rps1 irqs */
+ SAA7146_IER_DISABLE(dev,MASK_28);
+
+ /* stop video-dma3 */
+ saa7146_write(dev, MC1, MASK_20);
+
+ if(signal_pending(current)) {
+
+ DEB_VBI("aborted (rps:0x%08x)\n",
+ saa7146_read(dev, RPS_ADDR1));
+
+ /* stop rps1 for sure */
+ saa7146_write(dev, MC1, MASK_29);
+
+ dma_free_coherent(&dev->pci->dev, 4096, cpu, dma_addr);
+ return -EINTR;
+ }
+ }
+
+ dma_free_coherent(&dev->pci->dev, 4096, cpu, dma_addr);
+ return 0;
+}
+
+static void saa7146_set_vbi_capture(struct saa7146_dev *dev, struct saa7146_buf *buf, struct saa7146_buf *next)
+{
+ struct saa7146_vv *vv = dev->vv_data;
+
+ struct saa7146_video_dma vdma3;
+
+ int count = 0;
+ unsigned long e_wait = vv->current_hps_sync == SAA7146_HPS_SYNC_PORT_A ? CMD_E_FID_A : CMD_E_FID_B;
+ unsigned long o_wait = vv->current_hps_sync == SAA7146_HPS_SYNC_PORT_A ? CMD_O_FID_A : CMD_O_FID_B;
+
+/*
+ vdma3.base_even = 0xc8000000+2560*70;
+ vdma3.base_odd = 0xc8000000;
+ vdma3.prot_addr = 0xc8000000+2560*164;
+ vdma3.pitch = 2560;
+ vdma3.base_page = 0;
+ vdma3.num_line_byte = (64<<16)|((vbi_pixel_to_capture)<<0); // set above!
+*/
+ vdma3.base_even = buf->pt[2].offset;
+ vdma3.base_odd = buf->pt[2].offset + 16 * vbi_pixel_to_capture;
+ vdma3.prot_addr = buf->pt[2].offset + 16 * 2 * vbi_pixel_to_capture;
+ vdma3.pitch = vbi_pixel_to_capture;
+ vdma3.base_page = buf->pt[2].dma | ME1;
+ vdma3.num_line_byte = (16 << 16) | vbi_pixel_to_capture;
+
+ saa7146_write_out_dma(dev, 3, &vdma3);
+
+ /* write beginning of rps-program */
+ count = 0;
+
+ /* wait for o_fid_a/b / e_fid_a/b toggle only if bit 1 is not set */
+
+ /* we don't wait here for the first field anymore. this is different from the video
+ capture and might cause that the first buffer is only half filled (with only
+ one field). but since this is some sort of streaming data, this is not that negative.
+ but by doing this, we can use the whole engine from videobuf-dma-sg.c... */
+
+/*
+ WRITE_RPS1(CMD_PAUSE | CMD_OAN | CMD_SIG1 | e_wait);
+ WRITE_RPS1(CMD_PAUSE | CMD_OAN | CMD_SIG1 | o_wait);
+*/
+ /* set bit 1 */
+ WRITE_RPS1(CMD_WR_REG | (1 << 8) | (MC2/4));
+ WRITE_RPS1(MASK_28 | MASK_12);
+
+ /* turn on video-dma3 */
+ WRITE_RPS1(CMD_WR_REG_MASK | (MC1/4));
+ WRITE_RPS1(MASK_04 | MASK_20); /* => mask */
+ WRITE_RPS1(MASK_04 | MASK_20); /* => values */
+
+ /* wait for o_fid_a/b / e_fid_a/b toggle */
+ WRITE_RPS1(CMD_PAUSE | o_wait);
+ WRITE_RPS1(CMD_PAUSE | e_wait);
+
+ /* generate interrupt */
+ WRITE_RPS1(CMD_INTERRUPT);
+
+ /* stop */
+ WRITE_RPS1(CMD_STOP);
+
+ /* enable rps1 irqs */
+ SAA7146_IER_ENABLE(dev, MASK_28);
+
+ /* write the address of the rps-program */
+ saa7146_write(dev, RPS_ADDR1, dev->d_rps1.dma_handle);
+
+ /* turn on rps */
+ saa7146_write(dev, MC1, (MASK_13 | MASK_29));
+}
+
+static int buffer_activate(struct saa7146_dev *dev,
+ struct saa7146_buf *buf,
+ struct saa7146_buf *next)
+{
+ struct saa7146_vv *vv = dev->vv_data;
+ buf->vb.state = VIDEOBUF_ACTIVE;
+
+ DEB_VBI("dev:%p, buf:%p, next:%p\n", dev, buf, next);
+ saa7146_set_vbi_capture(dev,buf,next);
+
+ mod_timer(&vv->vbi_dmaq.timeout, jiffies+BUFFER_TIMEOUT);
+ return 0;
+}
+
+static int buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,enum v4l2_field field)
+{
+ struct file *file = q->priv_data;
+ struct saa7146_fh *fh = file->private_data;
+ struct saa7146_dev *dev = fh->dev;
+ struct saa7146_buf *buf = (struct saa7146_buf *)vb;
+
+ int err = 0;
+ int lines, llength, size;
+
+ lines = 16 * 2 ; /* 2 fields */
+ llength = vbi_pixel_to_capture;
+ size = lines * llength;
+
+ DEB_VBI("vb:%p\n", vb);
+
+ if (0 != buf->vb.baddr && buf->vb.bsize < size) {
+ DEB_VBI("size mismatch\n");
+ return -EINVAL;
+ }
+
+ if (buf->vb.size != size)
+ saa7146_dma_free(dev,q,buf);
+
+ if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
+ struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);
+
+ buf->vb.width = llength;
+ buf->vb.height = lines;
+ buf->vb.size = size;
+ buf->vb.field = field; // FIXME: check this
+
+ saa7146_pgtable_free(dev->pci, &buf->pt[2]);
+ saa7146_pgtable_alloc(dev->pci, &buf->pt[2]);
+
+ err = videobuf_iolock(q,&buf->vb, NULL);
+ if (err)
+ goto oops;
+ err = saa7146_pgtable_build_single(dev->pci, &buf->pt[2],
+ dma->sglist, dma->sglen);
+ if (0 != err)
+ return err;
+ }
+ buf->vb.state = VIDEOBUF_PREPARED;
+ buf->activate = buffer_activate;
+
+ return 0;
+
+ oops:
+ DEB_VBI("error out\n");
+ saa7146_dma_free(dev,q,buf);
+
+ return err;
+}
+
+static int buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size)
+{
+ int llength,lines;
+
+ lines = 16 * 2 ; /* 2 fields */
+ llength = vbi_pixel_to_capture;
+
+ *size = lines * llength;
+ *count = 2;
+
+ DEB_VBI("count:%d, size:%d\n", *count, *size);
+
+ return 0;
+}
+
+static void buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+ struct file *file = q->priv_data;
+ struct saa7146_fh *fh = file->private_data;
+ struct saa7146_dev *dev = fh->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+ struct saa7146_buf *buf = (struct saa7146_buf *)vb;
+
+ DEB_VBI("vb:%p\n", vb);
+ saa7146_buffer_queue(dev, &vv->vbi_dmaq, buf);
+}
+
+static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+ struct file *file = q->priv_data;
+ struct saa7146_fh *fh = file->private_data;
+ struct saa7146_dev *dev = fh->dev;
+ struct saa7146_buf *buf = (struct saa7146_buf *)vb;
+
+ DEB_VBI("vb:%p\n", vb);
+ saa7146_dma_free(dev,q,buf);
+}
+
+static const struct videobuf_queue_ops vbi_qops = {
+ .buf_setup = buffer_setup,
+ .buf_prepare = buffer_prepare,
+ .buf_queue = buffer_queue,
+ .buf_release = buffer_release,
+};
+
+/* ------------------------------------------------------------------ */
+
+static void vbi_stop(struct saa7146_fh *fh, struct file *file)
+{
+ struct saa7146_dev *dev = fh->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+ unsigned long flags;
+ DEB_VBI("dev:%p, fh:%p\n", dev, fh);
+
+ spin_lock_irqsave(&dev->slock,flags);
+
+ /* disable rps1 */
+ saa7146_write(dev, MC1, MASK_29);
+
+ /* disable rps1 irqs */
+ SAA7146_IER_DISABLE(dev, MASK_28);
+
+ /* shut down dma 3 transfers */
+ saa7146_write(dev, MC1, MASK_20);
+
+ if (vv->vbi_dmaq.curr)
+ saa7146_buffer_finish(dev, &vv->vbi_dmaq, VIDEOBUF_DONE);
+
+ videobuf_queue_cancel(&fh->vbi_q);
+
+ vv->vbi_streaming = NULL;
+
+ del_timer(&vv->vbi_dmaq.timeout);
+ del_timer(&vv->vbi_read_timeout);
+
+ spin_unlock_irqrestore(&dev->slock, flags);
+}
+
+static void vbi_read_timeout(struct timer_list *t)
+{
+ struct saa7146_vv *vv = from_timer(vv, t, vbi_read_timeout);
+ struct file *file = vv->vbi_read_timeout_file;
+ struct saa7146_fh *fh = file->private_data;
+ struct saa7146_dev *dev = fh->dev;
+
+ DEB_VBI("dev:%p, fh:%p\n", dev, fh);
+
+ vbi_stop(fh, file);
+}
+
+static void vbi_init(struct saa7146_dev *dev, struct saa7146_vv *vv)
+{
+ DEB_VBI("dev:%p\n", dev);
+
+ INIT_LIST_HEAD(&vv->vbi_dmaq.queue);
+
+ timer_setup(&vv->vbi_dmaq.timeout, saa7146_buffer_timeout, 0);
+ vv->vbi_dmaq.dev = dev;
+
+ init_waitqueue_head(&vv->vbi_wq);
+}
+
+static int vbi_open(struct saa7146_dev *dev, struct file *file)
+{
+ struct saa7146_fh *fh = file->private_data;
+ struct saa7146_vv *vv = fh->dev->vv_data;
+
+ u32 arbtr_ctrl = saa7146_read(dev, PCI_BT_V1);
+ int ret = 0;
+
+ DEB_VBI("dev:%p, fh:%p\n", dev, fh);
+
+ ret = saa7146_res_get(fh, RESOURCE_DMA3_BRS);
+ if (0 == ret) {
+ DEB_S("cannot get vbi RESOURCE_DMA3_BRS resource\n");
+ return -EBUSY;
+ }
+
+ /* adjust arbitrition control for video dma 3 */
+ arbtr_ctrl &= ~0x1f0000;
+ arbtr_ctrl |= 0x1d0000;
+ saa7146_write(dev, PCI_BT_V1, arbtr_ctrl);
+ saa7146_write(dev, MC2, (MASK_04|MASK_20));
+
+ videobuf_queue_sg_init(&fh->vbi_q, &vbi_qops,
+ &dev->pci->dev, &dev->slock,
+ V4L2_BUF_TYPE_VBI_CAPTURE,
+ V4L2_FIELD_SEQ_TB, // FIXME: does this really work?
+ sizeof(struct saa7146_buf),
+ file, &dev->v4l2_lock);
+
+ vv->vbi_read_timeout.function = vbi_read_timeout;
+ vv->vbi_read_timeout_file = file;
+
+ /* initialize the brs */
+ if ( 0 != (SAA7146_USE_PORT_B_FOR_VBI & dev->ext_vv_data->flags)) {
+ saa7146_write(dev, BRS_CTRL, MASK_30|MASK_29 | (7 << 19));
+ } else {
+ saa7146_write(dev, BRS_CTRL, 0x00000001);
+
+ if (0 != (ret = vbi_workaround(dev))) {
+ DEB_VBI("vbi workaround failed!\n");
+ /* return ret;*/
+ }
+ }
+
+ /* upload brs register */
+ saa7146_write(dev, MC2, (MASK_08|MASK_24));
+ return 0;
+}
+
+static void vbi_close(struct saa7146_dev *dev, struct file *file)
+{
+ struct saa7146_fh *fh = file->private_data;
+ struct saa7146_vv *vv = dev->vv_data;
+ DEB_VBI("dev:%p, fh:%p\n", dev, fh);
+
+ if( fh == vv->vbi_streaming ) {
+ vbi_stop(fh, file);
+ }
+ saa7146_res_free(fh, RESOURCE_DMA3_BRS);
+}
+
+static void vbi_irq_done(struct saa7146_dev *dev, unsigned long status)
+{
+ struct saa7146_vv *vv = dev->vv_data;
+ spin_lock(&dev->slock);
+
+ if (vv->vbi_dmaq.curr) {
+ DEB_VBI("dev:%p, curr:%p\n", dev, vv->vbi_dmaq.curr);
+ /* this must be += 2, one count for each field */
+ vv->vbi_fieldcount+=2;
+ vv->vbi_dmaq.curr->vb.field_count = vv->vbi_fieldcount;
+ saa7146_buffer_finish(dev, &vv->vbi_dmaq, VIDEOBUF_DONE);
+ } else {
+ DEB_VBI("dev:%p\n", dev);
+ }
+ saa7146_buffer_next(dev, &vv->vbi_dmaq, 1);
+
+ spin_unlock(&dev->slock);
+}
+
+static ssize_t vbi_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
+{
+ struct saa7146_fh *fh = file->private_data;
+ struct saa7146_dev *dev = fh->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+ ssize_t ret = 0;
+
+ DEB_VBI("dev:%p, fh:%p\n", dev, fh);
+
+ if( NULL == vv->vbi_streaming ) {
+ // fixme: check if dma3 is available
+ // fixme: activate vbi engine here if necessary. (really?)
+ vv->vbi_streaming = fh;
+ }
+
+ if( fh != vv->vbi_streaming ) {
+ DEB_VBI("open %p is already using vbi capture\n",
+ vv->vbi_streaming);
+ return -EBUSY;
+ }
+
+ mod_timer(&vv->vbi_read_timeout, jiffies+BUFFER_TIMEOUT);
+ ret = videobuf_read_stream(&fh->vbi_q, data, count, ppos, 1,
+ file->f_flags & O_NONBLOCK);
+/*
+ printk("BASE_ODD3: 0x%08x\n", saa7146_read(dev, BASE_ODD3));
+ printk("BASE_EVEN3: 0x%08x\n", saa7146_read(dev, BASE_EVEN3));
+ printk("PROT_ADDR3: 0x%08x\n", saa7146_read(dev, PROT_ADDR3));
+ printk("PITCH3: 0x%08x\n", saa7146_read(dev, PITCH3));
+ printk("BASE_PAGE3: 0x%08x\n", saa7146_read(dev, BASE_PAGE3));
+ printk("NUM_LINE_BYTE3: 0x%08x\n", saa7146_read(dev, NUM_LINE_BYTE3));
+ printk("BRS_CTRL: 0x%08x\n", saa7146_read(dev, BRS_CTRL));
+*/
+ return ret;
+}
+
+const struct saa7146_use_ops saa7146_vbi_uops = {
+ .init = vbi_init,
+ .open = vbi_open,
+ .release = vbi_close,
+ .irq_done = vbi_irq_done,
+ .read = vbi_read,
+};
diff --git a/drivers/media/common/saa7146/saa7146_video.c b/drivers/media/common/saa7146/saa7146_video.c
new file mode 100644
index 000000000000..2296765079a4
--- /dev/null
+++ b/drivers/media/common/saa7146/saa7146_video.c
@@ -0,0 +1,1286 @@
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <media/drv-intf/saa7146_vv.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-ctrls.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+
+static int max_memory = 32;
+
+module_param(max_memory, int, 0644);
+MODULE_PARM_DESC(max_memory, "maximum memory usage for capture buffers (default: 32Mb)");
+
+#define IS_CAPTURE_ACTIVE(fh) \
+ (((vv->video_status & STATUS_CAPTURE) != 0) && (vv->video_fh == fh))
+
+#define IS_OVERLAY_ACTIVE(fh) \
+ (((vv->video_status & STATUS_OVERLAY) != 0) && (vv->video_fh == fh))
+
+/* format descriptions for capture and preview */
+static struct saa7146_format formats[] = {
+ {
+ .pixelformat = V4L2_PIX_FMT_RGB332,
+ .trans = RGB08_COMPOSED,
+ .depth = 8,
+ .flags = 0,
+ }, {
+ .pixelformat = V4L2_PIX_FMT_RGB565,
+ .trans = RGB16_COMPOSED,
+ .depth = 16,
+ .flags = 0,
+ }, {
+ .pixelformat = V4L2_PIX_FMT_BGR24,
+ .trans = RGB24_COMPOSED,
+ .depth = 24,
+ .flags = 0,
+ }, {
+ .pixelformat = V4L2_PIX_FMT_BGR32,
+ .trans = RGB32_COMPOSED,
+ .depth = 32,
+ .flags = 0,
+ }, {
+ .pixelformat = V4L2_PIX_FMT_RGB32,
+ .trans = RGB32_COMPOSED,
+ .depth = 32,
+ .flags = 0,
+ .swap = 0x2,
+ }, {
+ .pixelformat = V4L2_PIX_FMT_GREY,
+ .trans = Y8,
+ .depth = 8,
+ .flags = 0,
+ }, {
+ .pixelformat = V4L2_PIX_FMT_YUV422P,
+ .trans = YUV422_DECOMPOSED,
+ .depth = 16,
+ .flags = FORMAT_BYTE_SWAP|FORMAT_IS_PLANAR,
+ }, {
+ .pixelformat = V4L2_PIX_FMT_YVU420,
+ .trans = YUV420_DECOMPOSED,
+ .depth = 12,
+ .flags = FORMAT_BYTE_SWAP|FORMAT_IS_PLANAR,
+ }, {
+ .pixelformat = V4L2_PIX_FMT_YUV420,
+ .trans = YUV420_DECOMPOSED,
+ .depth = 12,
+ .flags = FORMAT_IS_PLANAR,
+ }, {
+ .pixelformat = V4L2_PIX_FMT_UYVY,
+ .trans = YUV422_COMPOSED,
+ .depth = 16,
+ .flags = 0,
+ }
+};
+
+/* unfortunately, the saa7146 contains a bug which prevents it from doing on-the-fly byte swaps.
+ due to this, it's impossible to provide additional *packed* formats, which are simply byte swapped
+ (like V4L2_PIX_FMT_YUYV) ... 8-( */
+
+struct saa7146_format* saa7146_format_by_fourcc(struct saa7146_dev *dev, int fourcc)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(formats); i++) {
+ if (formats[i].pixelformat == fourcc) {
+ return formats+i;
+ }
+ }
+
+ DEB_D("unknown pixelformat:'%4.4s'\n", (char *)&fourcc);
+ return NULL;
+}
+
+static int vidioc_try_fmt_vid_overlay(struct file *file, void *fh, struct v4l2_format *f);
+
+int saa7146_start_preview(struct saa7146_fh *fh)
+{
+ struct saa7146_dev *dev = fh->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+ struct v4l2_format fmt;
+ int ret = 0, err = 0;
+
+ DEB_EE("dev:%p, fh:%p\n", dev, fh);
+
+ /* check if we have overlay information */
+ if (vv->ov.fh == NULL) {
+ DEB_D("no overlay data available. try S_FMT first.\n");
+ return -EAGAIN;
+ }
+
+ /* check if streaming capture is running */
+ if (IS_CAPTURE_ACTIVE(fh) != 0) {
+ DEB_D("streaming capture is active\n");
+ return -EBUSY;
+ }
+
+ /* check if overlay is running */
+ if (IS_OVERLAY_ACTIVE(fh) != 0) {
+ if (vv->video_fh == fh) {
+ DEB_D("overlay is already active\n");
+ return 0;
+ }
+ DEB_D("overlay is already active in another open\n");
+ return -EBUSY;
+ }
+
+ if (0 == saa7146_res_get(fh, RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP)) {
+ DEB_D("cannot get necessary overlay resources\n");
+ return -EBUSY;
+ }
+
+ fmt.fmt.win = vv->ov.win;
+ err = vidioc_try_fmt_vid_overlay(NULL, fh, &fmt);
+ if (0 != err) {
+ saa7146_res_free(vv->video_fh, RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP);
+ return -EBUSY;
+ }
+ vv->ov.win = fmt.fmt.win;
+
+ DEB_D("%dx%d+%d+%d 0x%08x field=%s\n",
+ vv->ov.win.w.width, vv->ov.win.w.height,
+ vv->ov.win.w.left, vv->ov.win.w.top,
+ vv->ov_fmt->pixelformat, v4l2_field_names[vv->ov.win.field]);
+
+ if (0 != (ret = saa7146_enable_overlay(fh))) {
+ DEB_D("enabling overlay failed: %d\n", ret);
+ saa7146_res_free(vv->video_fh, RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP);
+ return ret;
+ }
+
+ vv->video_status = STATUS_OVERLAY;
+ vv->video_fh = fh;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(saa7146_start_preview);
+
+int saa7146_stop_preview(struct saa7146_fh *fh)
+{
+ struct saa7146_dev *dev = fh->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+
+ DEB_EE("dev:%p, fh:%p\n", dev, fh);
+
+ /* check if streaming capture is running */
+ if (IS_CAPTURE_ACTIVE(fh) != 0) {
+ DEB_D("streaming capture is active\n");
+ return -EBUSY;
+ }
+
+ /* check if overlay is running at all */
+ if ((vv->video_status & STATUS_OVERLAY) == 0) {
+ DEB_D("no active overlay\n");
+ return 0;
+ }
+
+ if (vv->video_fh != fh) {
+ DEB_D("overlay is active, but in another open\n");
+ return -EBUSY;
+ }
+
+ vv->video_status = 0;
+ vv->video_fh = NULL;
+
+ saa7146_disable_overlay(fh);
+
+ saa7146_res_free(fh, RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(saa7146_stop_preview);
+
+/********************************************************************************/
+/* common pagetable functions */
+
+static int saa7146_pgtable_build(struct saa7146_dev *dev, struct saa7146_buf *buf)
+{
+ struct pci_dev *pci = dev->pci;
+ struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);
+ struct scatterlist *list = dma->sglist;
+ int length = dma->sglen;
+ struct saa7146_format *sfmt = saa7146_format_by_fourcc(dev,buf->fmt->pixelformat);
+
+ DEB_EE("dev:%p, buf:%p, sg_len:%d\n", dev, buf, length);
+
+ if( 0 != IS_PLANAR(sfmt->trans)) {
+ struct saa7146_pgtable *pt1 = &buf->pt[0];
+ struct saa7146_pgtable *pt2 = &buf->pt[1];
+ struct saa7146_pgtable *pt3 = &buf->pt[2];
+ __le32 *ptr1, *ptr2, *ptr3;
+ __le32 fill;
+
+ int size = buf->fmt->width*buf->fmt->height;
+ int i,p,m1,m2,m3,o1,o2;
+
+ switch( sfmt->depth ) {
+ case 12: {
+ /* create some offsets inside the page table */
+ m1 = ((size+PAGE_SIZE)/PAGE_SIZE)-1;
+ m2 = ((size+(size/4)+PAGE_SIZE)/PAGE_SIZE)-1;
+ m3 = ((size+(size/2)+PAGE_SIZE)/PAGE_SIZE)-1;
+ o1 = size%PAGE_SIZE;
+ o2 = (size+(size/4))%PAGE_SIZE;
+ DEB_CAP("size:%d, m1:%d, m2:%d, m3:%d, o1:%d, o2:%d\n",
+ size, m1, m2, m3, o1, o2);
+ break;
+ }
+ case 16: {
+ /* create some offsets inside the page table */
+ m1 = ((size+PAGE_SIZE)/PAGE_SIZE)-1;
+ m2 = ((size+(size/2)+PAGE_SIZE)/PAGE_SIZE)-1;
+ m3 = ((2*size+PAGE_SIZE)/PAGE_SIZE)-1;
+ o1 = size%PAGE_SIZE;
+ o2 = (size+(size/2))%PAGE_SIZE;
+ DEB_CAP("size:%d, m1:%d, m2:%d, m3:%d, o1:%d, o2:%d\n",
+ size, m1, m2, m3, o1, o2);
+ break;
+ }
+ default: {
+ return -1;
+ }
+ }
+
+ ptr1 = pt1->cpu;
+ ptr2 = pt2->cpu;
+ ptr3 = pt3->cpu;
+
+ /* walk all pages, copy all page addresses to ptr1 */
+ for (i = 0; i < length; i++, list++) {
+ for (p = 0; p * 4096 < sg_dma_len(list); p++, ptr1++)
+ *ptr1 = cpu_to_le32(sg_dma_address(list) - list->offset);
+ }
+/*
+ ptr1 = pt1->cpu;
+ for(j=0;j<40;j++) {
+ printk("ptr1 %d: 0x%08x\n",j,ptr1[j]);
+ }
+*/
+
+ /* if we have a user buffer, the first page may not be
+ aligned to a page boundary. */
+ pt1->offset = dma->sglist->offset;
+ pt2->offset = pt1->offset+o1;
+ pt3->offset = pt1->offset+o2;
+
+ /* create video-dma2 page table */
+ ptr1 = pt1->cpu;
+ for(i = m1; i <= m2 ; i++, ptr2++) {
+ *ptr2 = ptr1[i];
+ }
+ fill = *(ptr2-1);
+ for(;i<1024;i++,ptr2++) {
+ *ptr2 = fill;
+ }
+ /* create video-dma3 page table */
+ ptr1 = pt1->cpu;
+ for(i = m2; i <= m3; i++,ptr3++) {
+ *ptr3 = ptr1[i];
+ }
+ fill = *(ptr3-1);
+ for(;i<1024;i++,ptr3++) {
+ *ptr3 = fill;
+ }
+ /* finally: finish up video-dma1 page table */
+ ptr1 = pt1->cpu+m1;
+ fill = pt1->cpu[m1];
+ for(i=m1;i<1024;i++,ptr1++) {
+ *ptr1 = fill;
+ }
+/*
+ ptr1 = pt1->cpu;
+ ptr2 = pt2->cpu;
+ ptr3 = pt3->cpu;
+ for(j=0;j<40;j++) {
+ printk("ptr1 %d: 0x%08x\n",j,ptr1[j]);
+ }
+ for(j=0;j<40;j++) {
+ printk("ptr2 %d: 0x%08x\n",j,ptr2[j]);
+ }
+ for(j=0;j<40;j++) {
+ printk("ptr3 %d: 0x%08x\n",j,ptr3[j]);
+ }
+*/
+ } else {
+ struct saa7146_pgtable *pt = &buf->pt[0];
+ return saa7146_pgtable_build_single(pci, pt, list, length);
+ }
+
+ return 0;
+}
+
+
+/********************************************************************************/
+/* file operations */
+
+static int video_begin(struct saa7146_fh *fh)
+{
+ struct saa7146_dev *dev = fh->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+ struct saa7146_format *fmt = NULL;
+ unsigned int resource;
+ int ret = 0, err = 0;
+
+ DEB_EE("dev:%p, fh:%p\n", dev, fh);
+
+ if ((vv->video_status & STATUS_CAPTURE) != 0) {
+ if (vv->video_fh == fh) {
+ DEB_S("already capturing\n");
+ return 0;
+ }
+ DEB_S("already capturing in another open\n");
+ return -EBUSY;
+ }
+
+ if ((vv->video_status & STATUS_OVERLAY) != 0) {
+ DEB_S("warning: suspending overlay video for streaming capture\n");
+ vv->ov_suspend = vv->video_fh;
+ err = saa7146_stop_preview(vv->video_fh); /* side effect: video_status is now 0, video_fh is NULL */
+ if (0 != err) {
+ DEB_D("suspending video failed. aborting\n");
+ return err;
+ }
+ }
+
+ fmt = saa7146_format_by_fourcc(dev, vv->video_fmt.pixelformat);
+ /* we need to have a valid format set here */
+ if (!fmt)
+ return -EINVAL;
+
+ if (0 != (fmt->flags & FORMAT_IS_PLANAR)) {
+ resource = RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP|RESOURCE_DMA3_BRS;
+ } else {
+ resource = RESOURCE_DMA1_HPS;
+ }
+
+ ret = saa7146_res_get(fh, resource);
+ if (0 == ret) {
+ DEB_S("cannot get capture resource %d\n", resource);
+ if (vv->ov_suspend != NULL) {
+ saa7146_start_preview(vv->ov_suspend);
+ vv->ov_suspend = NULL;
+ }
+ return -EBUSY;
+ }
+
+ /* clear out beginning of streaming bit (rps register 0)*/
+ saa7146_write(dev, MC2, MASK_27 );
+
+ /* enable rps0 irqs */
+ SAA7146_IER_ENABLE(dev, MASK_27);
+
+ vv->video_fh = fh;
+ vv->video_status = STATUS_CAPTURE;
+
+ return 0;
+}
+
+static int video_end(struct saa7146_fh *fh, struct file *file)
+{
+ struct saa7146_dev *dev = fh->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+ struct saa7146_dmaqueue *q = &vv->video_dmaq;
+ struct saa7146_format *fmt = NULL;
+ unsigned long flags;
+ unsigned int resource;
+ u32 dmas = 0;
+ DEB_EE("dev:%p, fh:%p\n", dev, fh);
+
+ if ((vv->video_status & STATUS_CAPTURE) != STATUS_CAPTURE) {
+ DEB_S("not capturing\n");
+ return 0;
+ }
+
+ if (vv->video_fh != fh) {
+ DEB_S("capturing, but in another open\n");
+ return -EBUSY;
+ }
+
+ fmt = saa7146_format_by_fourcc(dev, vv->video_fmt.pixelformat);
+ /* we need to have a valid format set here */
+ if (!fmt)
+ return -EINVAL;
+
+ if (0 != (fmt->flags & FORMAT_IS_PLANAR)) {
+ resource = RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP|RESOURCE_DMA3_BRS;
+ dmas = MASK_22 | MASK_21 | MASK_20;
+ } else {
+ resource = RESOURCE_DMA1_HPS;
+ dmas = MASK_22;
+ }
+ spin_lock_irqsave(&dev->slock,flags);
+
+ /* disable rps0 */
+ saa7146_write(dev, MC1, MASK_28);
+
+ /* disable rps0 irqs */
+ SAA7146_IER_DISABLE(dev, MASK_27);
+
+ /* shut down all used video dma transfers */
+ saa7146_write(dev, MC1, dmas);
+
+ if (q->curr)
+ saa7146_buffer_finish(dev, q, VIDEOBUF_DONE);
+
+ spin_unlock_irqrestore(&dev->slock, flags);
+
+ vv->video_fh = NULL;
+ vv->video_status = 0;
+
+ saa7146_res_free(fh, resource);
+
+ if (vv->ov_suspend != NULL) {
+ saa7146_start_preview(vv->ov_suspend);
+ vv->ov_suspend = NULL;
+ }
+
+ return 0;
+}
+
+static int vidioc_querycap(struct file *file, void *fh, struct v4l2_capability *cap)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+
+ strscpy((char *)cap->driver, "saa7146 v4l2", sizeof(cap->driver));
+ strscpy((char *)cap->card, dev->ext->name, sizeof(cap->card));
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OVERLAY |
+ V4L2_CAP_READWRITE | V4L2_CAP_STREAMING |
+ V4L2_CAP_DEVICE_CAPS;
+ cap->capabilities |= dev->ext_vv_data->capabilities;
+ return 0;
+}
+
+static int vidioc_g_fbuf(struct file *file, void *fh, struct v4l2_framebuffer *fb)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+
+ *fb = vv->ov_fb;
+ fb->capability = V4L2_FBUF_CAP_LIST_CLIPPING;
+ fb->flags = V4L2_FBUF_FLAG_PRIMARY;
+ return 0;
+}
+
+static int vidioc_s_fbuf(struct file *file, void *fh, const struct v4l2_framebuffer *fb)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+ struct saa7146_format *fmt;
+
+ DEB_EE("VIDIOC_S_FBUF\n");
+
+ if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RAWIO))
+ return -EPERM;
+
+ /* check args */
+ fmt = saa7146_format_by_fourcc(dev, fb->fmt.pixelformat);
+ if (NULL == fmt)
+ return -EINVAL;
+
+ /* planar formats are not allowed for overlay video, clipping and video dma would clash */
+ if (fmt->flags & FORMAT_IS_PLANAR)
+ DEB_S("planar pixelformat '%4.4s' not allowed for overlay\n",
+ (char *)&fmt->pixelformat);
+
+ /* check if overlay is running */
+ if (IS_OVERLAY_ACTIVE(fh) != 0) {
+ if (vv->video_fh != fh) {
+ DEB_D("refusing to change framebuffer information while overlay is active in another open\n");
+ return -EBUSY;
+ }
+ }
+
+ /* ok, accept it */
+ vv->ov_fb = *fb;
+ vv->ov_fmt = fmt;
+
+ if (vv->ov_fb.fmt.bytesperline < vv->ov_fb.fmt.width) {
+ vv->ov_fb.fmt.bytesperline = vv->ov_fb.fmt.width * fmt->depth / 8;
+ DEB_D("setting bytesperline to %d\n", vv->ov_fb.fmt.bytesperline);
+ }
+ return 0;
+}
+
+static int vidioc_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *f)
+{
+ if (f->index >= ARRAY_SIZE(formats))
+ return -EINVAL;
+ f->pixelformat = formats[f->index].pixelformat;
+ return 0;
+}
+
+int saa7146_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct saa7146_dev *dev = container_of(ctrl->handler,
+ struct saa7146_dev, ctrl_handler);
+ struct saa7146_vv *vv = dev->vv_data;
+ u32 val;
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ val = saa7146_read(dev, BCS_CTRL);
+ val &= 0x00ffffff;
+ val |= (ctrl->val << 24);
+ saa7146_write(dev, BCS_CTRL, val);
+ saa7146_write(dev, MC2, MASK_22 | MASK_06);
+ break;
+
+ case V4L2_CID_CONTRAST:
+ val = saa7146_read(dev, BCS_CTRL);
+ val &= 0xff00ffff;
+ val |= (ctrl->val << 16);
+ saa7146_write(dev, BCS_CTRL, val);
+ saa7146_write(dev, MC2, MASK_22 | MASK_06);
+ break;
+
+ case V4L2_CID_SATURATION:
+ val = saa7146_read(dev, BCS_CTRL);
+ val &= 0xffffff00;
+ val |= (ctrl->val << 0);
+ saa7146_write(dev, BCS_CTRL, val);
+ saa7146_write(dev, MC2, MASK_22 | MASK_06);
+ break;
+
+ case V4L2_CID_HFLIP:
+ /* fixme: we can support changing VFLIP and HFLIP here... */
+ if ((vv->video_status & STATUS_CAPTURE))
+ return -EBUSY;
+ vv->hflip = ctrl->val;
+ break;
+
+ case V4L2_CID_VFLIP:
+ if ((vv->video_status & STATUS_CAPTURE))
+ return -EBUSY;
+ vv->vflip = ctrl->val;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ if ((vv->video_status & STATUS_OVERLAY) != 0) { /* CHECK: && (vv->video_fh == fh)) */
+ struct saa7146_fh *fh = vv->video_fh;
+
+ saa7146_stop_preview(fh);
+ saa7146_start_preview(fh);
+ }
+ return 0;
+}
+
+static int vidioc_g_parm(struct file *file, void *fh,
+ struct v4l2_streamparm *parm)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+
+ if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ parm->parm.capture.readbuffers = 1;
+ v4l2_video_std_frame_period(vv->standard->id,
+ &parm->parm.capture.timeperframe);
+ return 0;
+}
+
+static int vidioc_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+
+ f->fmt.pix = vv->video_fmt;
+ return 0;
+}
+
+static int vidioc_g_fmt_vid_overlay(struct file *file, void *fh, struct v4l2_format *f)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+
+ f->fmt.win = vv->ov.win;
+ return 0;
+}
+
+static int vidioc_g_fmt_vbi_cap(struct file *file, void *fh, struct v4l2_format *f)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+
+ f->fmt.vbi = vv->vbi_fmt;
+ return 0;
+}
+
+static int vidioc_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+ struct saa7146_format *fmt;
+ enum v4l2_field field;
+ int maxw, maxh;
+ int calc_bpl;
+
+ DEB_EE("V4L2_BUF_TYPE_VIDEO_CAPTURE: dev:%p, fh:%p\n", dev, fh);
+
+ fmt = saa7146_format_by_fourcc(dev, f->fmt.pix.pixelformat);
+ if (NULL == fmt)
+ return -EINVAL;
+
+ field = f->fmt.pix.field;
+ maxw = vv->standard->h_max_out;
+ maxh = vv->standard->v_max_out;
+
+ if (V4L2_FIELD_ANY == field) {
+ field = (f->fmt.pix.height > maxh / 2)
+ ? V4L2_FIELD_INTERLACED
+ : V4L2_FIELD_BOTTOM;
+ }
+ switch (field) {
+ case V4L2_FIELD_ALTERNATE:
+ vv->last_field = V4L2_FIELD_TOP;
+ maxh = maxh / 2;
+ break;
+ case V4L2_FIELD_TOP:
+ case V4L2_FIELD_BOTTOM:
+ vv->last_field = V4L2_FIELD_INTERLACED;
+ maxh = maxh / 2;
+ break;
+ case V4L2_FIELD_INTERLACED:
+ vv->last_field = V4L2_FIELD_INTERLACED;
+ break;
+ default:
+ DEB_D("no known field mode '%d'\n", field);
+ return -EINVAL;
+ }
+
+ f->fmt.pix.field = field;
+ f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
+ if (f->fmt.pix.width > maxw)
+ f->fmt.pix.width = maxw;
+ if (f->fmt.pix.height > maxh)
+ f->fmt.pix.height = maxh;
+
+ calc_bpl = (f->fmt.pix.width * fmt->depth) / 8;
+
+ if (f->fmt.pix.bytesperline < calc_bpl)
+ f->fmt.pix.bytesperline = calc_bpl;
+
+ if (f->fmt.pix.bytesperline > (2 * PAGE_SIZE * fmt->depth) / 8) /* arbitrary constraint */
+ f->fmt.pix.bytesperline = calc_bpl;
+
+ f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * f->fmt.pix.height;
+ DEB_D("w:%d, h:%d, bytesperline:%d, sizeimage:%d\n",
+ f->fmt.pix.width, f->fmt.pix.height,
+ f->fmt.pix.bytesperline, f->fmt.pix.sizeimage);
+
+ return 0;
+}
+
+
+static int vidioc_try_fmt_vid_overlay(struct file *file, void *fh, struct v4l2_format *f)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+ struct v4l2_window *win = &f->fmt.win;
+ enum v4l2_field field;
+ int maxw, maxh;
+
+ DEB_EE("dev:%p\n", dev);
+
+ if (NULL == vv->ov_fb.base) {
+ DEB_D("no fb base set\n");
+ return -EINVAL;
+ }
+ if (NULL == vv->ov_fmt) {
+ DEB_D("no fb fmt set\n");
+ return -EINVAL;
+ }
+ if (win->w.width < 48 || win->w.height < 32) {
+ DEB_D("min width/height. (%d,%d)\n",
+ win->w.width, win->w.height);
+ return -EINVAL;
+ }
+ if (win->clipcount > 16) {
+ DEB_D("clipcount too big\n");
+ return -EINVAL;
+ }
+
+ field = win->field;
+ maxw = vv->standard->h_max_out;
+ maxh = vv->standard->v_max_out;
+
+ if (V4L2_FIELD_ANY == field) {
+ field = (win->w.height > maxh / 2)
+ ? V4L2_FIELD_INTERLACED
+ : V4L2_FIELD_TOP;
+ }
+ switch (field) {
+ case V4L2_FIELD_TOP:
+ case V4L2_FIELD_BOTTOM:
+ case V4L2_FIELD_ALTERNATE:
+ maxh = maxh / 2;
+ break;
+ case V4L2_FIELD_INTERLACED:
+ break;
+ default:
+ DEB_D("no known field mode '%d'\n", field);
+ return -EINVAL;
+ }
+
+ win->field = field;
+ if (win->w.width > maxw)
+ win->w.width = maxw;
+ if (win->w.height > maxh)
+ win->w.height = maxh;
+
+ return 0;
+}
+
+static int vidioc_s_fmt_vid_cap(struct file *file, void *__fh, struct v4l2_format *f)
+{
+ struct saa7146_fh *fh = __fh;
+ struct saa7146_dev *dev = fh->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+ int err;
+
+ DEB_EE("V4L2_BUF_TYPE_VIDEO_CAPTURE: dev:%p, fh:%p\n", dev, fh);
+ if (IS_CAPTURE_ACTIVE(fh) != 0) {
+ DEB_EE("streaming capture is active\n");
+ return -EBUSY;
+ }
+ err = vidioc_try_fmt_vid_cap(file, fh, f);
+ if (0 != err)
+ return err;
+ vv->video_fmt = f->fmt.pix;
+ DEB_EE("set to pixelformat '%4.4s'\n",
+ (char *)&vv->video_fmt.pixelformat);
+ return 0;
+}
+
+static int vidioc_s_fmt_vid_overlay(struct file *file, void *__fh, struct v4l2_format *f)
+{
+ struct saa7146_fh *fh = __fh;
+ struct saa7146_dev *dev = fh->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+ int err;
+
+ DEB_EE("V4L2_BUF_TYPE_VIDEO_OVERLAY: dev:%p, fh:%p\n", dev, fh);
+ err = vidioc_try_fmt_vid_overlay(file, fh, f);
+ if (0 != err)
+ return err;
+ vv->ov.win = f->fmt.win;
+ vv->ov.nclips = f->fmt.win.clipcount;
+ if (vv->ov.nclips > 16)
+ vv->ov.nclips = 16;
+ memcpy(vv->ov.clips, f->fmt.win.clips,
+ sizeof(struct v4l2_clip) * vv->ov.nclips);
+
+ /* vv->ov.fh is used to indicate that we have valid overlay information, too */
+ vv->ov.fh = fh;
+
+ /* check if our current overlay is active */
+ if (IS_OVERLAY_ACTIVE(fh) != 0) {
+ saa7146_stop_preview(fh);
+ saa7146_start_preview(fh);
+ }
+ return 0;
+}
+
+static int vidioc_g_std(struct file *file, void *fh, v4l2_std_id *norm)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+
+ *norm = vv->standard->id;
+ return 0;
+}
+
+ /* the saa7146 supfhrts (used in conjunction with the saa7111a for example)
+ PAL / NTSC / SECAM. if your hardware does not (or does more)
+ -- override this function in your extension */
+/*
+ case VIDIOC_ENUMSTD:
+ {
+ struct v4l2_standard *e = arg;
+ if (e->index < 0 )
+ return -EINVAL;
+ if( e->index < dev->ext_vv_data->num_stds ) {
+ DEB_EE("VIDIOC_ENUMSTD: index:%d\n", e->index);
+ v4l2_video_std_construct(e, dev->ext_vv_data->stds[e->index].id, dev->ext_vv_data->stds[e->index].name);
+ return 0;
+ }
+ return -EINVAL;
+ }
+ */
+
+static int vidioc_s_std(struct file *file, void *fh, v4l2_std_id id)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+ int found = 0;
+ int err, i;
+
+ DEB_EE("VIDIOC_S_STD\n");
+
+ if ((vv->video_status & STATUS_CAPTURE) == STATUS_CAPTURE) {
+ DEB_D("cannot change video standard while streaming capture is active\n");
+ return -EBUSY;
+ }
+
+ if ((vv->video_status & STATUS_OVERLAY) != 0) {
+ vv->ov_suspend = vv->video_fh;
+ err = saa7146_stop_preview(vv->video_fh); /* side effect: video_status is now 0, video_fh is NULL */
+ if (0 != err) {
+ DEB_D("suspending video failed. aborting\n");
+ return err;
+ }
+ }
+
+ for (i = 0; i < dev->ext_vv_data->num_stds; i++)
+ if (id & dev->ext_vv_data->stds[i].id)
+ break;
+ if (i != dev->ext_vv_data->num_stds) {
+ vv->standard = &dev->ext_vv_data->stds[i];
+ if (NULL != dev->ext_vv_data->std_callback)
+ dev->ext_vv_data->std_callback(dev, vv->standard);
+ found = 1;
+ }
+
+ if (vv->ov_suspend != NULL) {
+ saa7146_start_preview(vv->ov_suspend);
+ vv->ov_suspend = NULL;
+ }
+
+ if (!found) {
+ DEB_EE("VIDIOC_S_STD: standard not found\n");
+ return -EINVAL;
+ }
+
+ DEB_EE("VIDIOC_S_STD: set to standard to '%s'\n", vv->standard->name);
+ return 0;
+}
+
+static int vidioc_overlay(struct file *file, void *fh, unsigned int on)
+{
+ int err;
+
+ DEB_D("VIDIOC_OVERLAY on:%d\n", on);
+ if (on)
+ err = saa7146_start_preview(fh);
+ else
+ err = saa7146_stop_preview(fh);
+ return err;
+}
+
+static int vidioc_reqbufs(struct file *file, void *__fh, struct v4l2_requestbuffers *b)
+{
+ struct saa7146_fh *fh = __fh;
+
+ if (b->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return videobuf_reqbufs(&fh->video_q, b);
+ if (b->type == V4L2_BUF_TYPE_VBI_CAPTURE)
+ return videobuf_reqbufs(&fh->vbi_q, b);
+ return -EINVAL;
+}
+
+static int vidioc_querybuf(struct file *file, void *__fh, struct v4l2_buffer *buf)
+{
+ struct saa7146_fh *fh = __fh;
+
+ if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return videobuf_querybuf(&fh->video_q, buf);
+ if (buf->type == V4L2_BUF_TYPE_VBI_CAPTURE)
+ return videobuf_querybuf(&fh->vbi_q, buf);
+ return -EINVAL;
+}
+
+static int vidioc_qbuf(struct file *file, void *__fh, struct v4l2_buffer *buf)
+{
+ struct saa7146_fh *fh = __fh;
+
+ if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return videobuf_qbuf(&fh->video_q, buf);
+ if (buf->type == V4L2_BUF_TYPE_VBI_CAPTURE)
+ return videobuf_qbuf(&fh->vbi_q, buf);
+ return -EINVAL;
+}
+
+static int vidioc_dqbuf(struct file *file, void *__fh, struct v4l2_buffer *buf)
+{
+ struct saa7146_fh *fh = __fh;
+
+ if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return videobuf_dqbuf(&fh->video_q, buf, file->f_flags & O_NONBLOCK);
+ if (buf->type == V4L2_BUF_TYPE_VBI_CAPTURE)
+ return videobuf_dqbuf(&fh->vbi_q, buf, file->f_flags & O_NONBLOCK);
+ return -EINVAL;
+}
+
+static int vidioc_streamon(struct file *file, void *__fh, enum v4l2_buf_type type)
+{
+ struct saa7146_fh *fh = __fh;
+ int err;
+
+ DEB_D("VIDIOC_STREAMON, type:%d\n", type);
+
+ err = video_begin(fh);
+ if (err)
+ return err;
+ if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return videobuf_streamon(&fh->video_q);
+ if (type == V4L2_BUF_TYPE_VBI_CAPTURE)
+ return videobuf_streamon(&fh->vbi_q);
+ return -EINVAL;
+}
+
+static int vidioc_streamoff(struct file *file, void *__fh, enum v4l2_buf_type type)
+{
+ struct saa7146_fh *fh = __fh;
+ struct saa7146_dev *dev = fh->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+ int err;
+
+ DEB_D("VIDIOC_STREAMOFF, type:%d\n", type);
+
+ /* ugly: we need to copy some checks from video_end(),
+ because videobuf_streamoff() relies on the capture running.
+ check and fix this */
+ if ((vv->video_status & STATUS_CAPTURE) != STATUS_CAPTURE) {
+ DEB_S("not capturing\n");
+ return 0;
+ }
+
+ if (vv->video_fh != fh) {
+ DEB_S("capturing, but in another open\n");
+ return -EBUSY;
+ }
+
+ err = -EINVAL;
+ if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ err = videobuf_streamoff(&fh->video_q);
+ else if (type == V4L2_BUF_TYPE_VBI_CAPTURE)
+ err = videobuf_streamoff(&fh->vbi_q);
+ if (0 != err) {
+ DEB_D("warning: videobuf_streamoff() failed\n");
+ video_end(fh, file);
+ } else {
+ err = video_end(fh, file);
+ }
+ return err;
+}
+
+const struct v4l2_ioctl_ops saa7146_video_ioctl_ops = {
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
+ .vidioc_enum_fmt_vid_overlay = vidioc_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
+ .vidioc_g_fmt_vid_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_overlay = vidioc_overlay,
+ .vidioc_g_fbuf = vidioc_g_fbuf,
+ .vidioc_s_fbuf = vidioc_s_fbuf,
+ .vidioc_reqbufs = vidioc_reqbufs,
+ .vidioc_querybuf = vidioc_querybuf,
+ .vidioc_qbuf = vidioc_qbuf,
+ .vidioc_dqbuf = vidioc_dqbuf,
+ .vidioc_g_std = vidioc_g_std,
+ .vidioc_s_std = vidioc_s_std,
+ .vidioc_streamon = vidioc_streamon,
+ .vidioc_streamoff = vidioc_streamoff,
+ .vidioc_g_parm = vidioc_g_parm,
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+const struct v4l2_ioctl_ops saa7146_vbi_ioctl_ops = {
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_g_fmt_vbi_cap = vidioc_g_fmt_vbi_cap,
+
+ .vidioc_reqbufs = vidioc_reqbufs,
+ .vidioc_querybuf = vidioc_querybuf,
+ .vidioc_qbuf = vidioc_qbuf,
+ .vidioc_dqbuf = vidioc_dqbuf,
+ .vidioc_g_std = vidioc_g_std,
+ .vidioc_s_std = vidioc_s_std,
+ .vidioc_streamon = vidioc_streamon,
+ .vidioc_streamoff = vidioc_streamoff,
+ .vidioc_g_parm = vidioc_g_parm,
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+/*********************************************************************************/
+/* buffer handling functions */
+
+static int buffer_activate (struct saa7146_dev *dev,
+ struct saa7146_buf *buf,
+ struct saa7146_buf *next)
+{
+ struct saa7146_vv *vv = dev->vv_data;
+
+ buf->vb.state = VIDEOBUF_ACTIVE;
+ saa7146_set_capture(dev,buf,next);
+
+ mod_timer(&vv->video_dmaq.timeout, jiffies+BUFFER_TIMEOUT);
+ return 0;
+}
+
+static void release_all_pagetables(struct saa7146_dev *dev, struct saa7146_buf *buf)
+{
+ saa7146_pgtable_free(dev->pci, &buf->pt[0]);
+ saa7146_pgtable_free(dev->pci, &buf->pt[1]);
+ saa7146_pgtable_free(dev->pci, &buf->pt[2]);
+}
+
+static int buffer_prepare(struct videobuf_queue *q,
+ struct videobuf_buffer *vb, enum v4l2_field field)
+{
+ struct file *file = q->priv_data;
+ struct saa7146_fh *fh = file->private_data;
+ struct saa7146_dev *dev = fh->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+ struct saa7146_buf *buf = (struct saa7146_buf *)vb;
+ int size,err = 0;
+
+ DEB_CAP("vbuf:%p\n", vb);
+
+ /* sanity checks */
+ if (vv->video_fmt.width < 48 ||
+ vv->video_fmt.height < 32 ||
+ vv->video_fmt.width > vv->standard->h_max_out ||
+ vv->video_fmt.height > vv->standard->v_max_out) {
+ DEB_D("w (%d) / h (%d) out of bounds\n",
+ vv->video_fmt.width, vv->video_fmt.height);
+ return -EINVAL;
+ }
+
+ size = vv->video_fmt.sizeimage;
+ if (0 != buf->vb.baddr && buf->vb.bsize < size) {
+ DEB_D("size mismatch\n");
+ return -EINVAL;
+ }
+
+ DEB_CAP("buffer_prepare [size=%dx%d,bytes=%d,fields=%s]\n",
+ vv->video_fmt.width, vv->video_fmt.height,
+ size, v4l2_field_names[vv->video_fmt.field]);
+ if (buf->vb.width != vv->video_fmt.width ||
+ buf->vb.bytesperline != vv->video_fmt.bytesperline ||
+ buf->vb.height != vv->video_fmt.height ||
+ buf->vb.size != size ||
+ buf->vb.field != field ||
+ buf->vb.field != vv->video_fmt.field ||
+ buf->fmt != &vv->video_fmt) {
+ saa7146_dma_free(dev,q,buf);
+ }
+
+ if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
+ struct saa7146_format *sfmt;
+
+ buf->vb.bytesperline = vv->video_fmt.bytesperline;
+ buf->vb.width = vv->video_fmt.width;
+ buf->vb.height = vv->video_fmt.height;
+ buf->vb.size = size;
+ buf->vb.field = field;
+ buf->fmt = &vv->video_fmt;
+ buf->vb.field = vv->video_fmt.field;
+
+ sfmt = saa7146_format_by_fourcc(dev,buf->fmt->pixelformat);
+
+ release_all_pagetables(dev, buf);
+ if( 0 != IS_PLANAR(sfmt->trans)) {
+ saa7146_pgtable_alloc(dev->pci, &buf->pt[0]);
+ saa7146_pgtable_alloc(dev->pci, &buf->pt[1]);
+ saa7146_pgtable_alloc(dev->pci, &buf->pt[2]);
+ } else {
+ saa7146_pgtable_alloc(dev->pci, &buf->pt[0]);
+ }
+
+ err = videobuf_iolock(q,&buf->vb, &vv->ov_fb);
+ if (err)
+ goto oops;
+ err = saa7146_pgtable_build(dev,buf);
+ if (err)
+ goto oops;
+ }
+ buf->vb.state = VIDEOBUF_PREPARED;
+ buf->activate = buffer_activate;
+
+ return 0;
+
+ oops:
+ DEB_D("error out\n");
+ saa7146_dma_free(dev,q,buf);
+
+ return err;
+}
+
+static int buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size)
+{
+ struct file *file = q->priv_data;
+ struct saa7146_fh *fh = file->private_data;
+ struct saa7146_vv *vv = fh->dev->vv_data;
+
+ if (0 == *count || *count > MAX_SAA7146_CAPTURE_BUFFERS)
+ *count = MAX_SAA7146_CAPTURE_BUFFERS;
+
+ *size = vv->video_fmt.sizeimage;
+
+ /* check if we exceed the "max_memory" parameter */
+ if( (*count * *size) > (max_memory*1048576) ) {
+ *count = (max_memory*1048576) / *size;
+ }
+
+ DEB_CAP("%d buffers, %d bytes each\n", *count, *size);
+
+ return 0;
+}
+
+static void buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+ struct file *file = q->priv_data;
+ struct saa7146_fh *fh = file->private_data;
+ struct saa7146_dev *dev = fh->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+ struct saa7146_buf *buf = (struct saa7146_buf *)vb;
+
+ DEB_CAP("vbuf:%p\n", vb);
+ saa7146_buffer_queue(fh->dev, &vv->video_dmaq, buf);
+}
+
+static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+ struct file *file = q->priv_data;
+ struct saa7146_fh *fh = file->private_data;
+ struct saa7146_dev *dev = fh->dev;
+ struct saa7146_buf *buf = (struct saa7146_buf *)vb;
+
+ DEB_CAP("vbuf:%p\n", vb);
+
+ saa7146_dma_free(dev,q,buf);
+
+ release_all_pagetables(dev, buf);
+}
+
+static const struct videobuf_queue_ops video_qops = {
+ .buf_setup = buffer_setup,
+ .buf_prepare = buffer_prepare,
+ .buf_queue = buffer_queue,
+ .buf_release = buffer_release,
+};
+
+/********************************************************************************/
+/* file operations */
+
+static void video_init(struct saa7146_dev *dev, struct saa7146_vv *vv)
+{
+ INIT_LIST_HEAD(&vv->video_dmaq.queue);
+
+ timer_setup(&vv->video_dmaq.timeout, saa7146_buffer_timeout, 0);
+ vv->video_dmaq.dev = dev;
+
+ /* set some default values */
+ vv->standard = &dev->ext_vv_data->stds[0];
+
+ /* FIXME: what's this? */
+ vv->current_hps_source = SAA7146_HPS_SOURCE_PORT_A;
+ vv->current_hps_sync = SAA7146_HPS_SYNC_PORT_A;
+}
+
+
+static int video_open(struct saa7146_dev *dev, struct file *file)
+{
+ struct saa7146_fh *fh = file->private_data;
+
+ videobuf_queue_sg_init(&fh->video_q, &video_qops,
+ &dev->pci->dev, &dev->slock,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ V4L2_FIELD_INTERLACED,
+ sizeof(struct saa7146_buf),
+ file, &dev->v4l2_lock);
+
+ return 0;
+}
+
+
+static void video_close(struct saa7146_dev *dev, struct file *file)
+{
+ struct saa7146_fh *fh = file->private_data;
+ struct saa7146_vv *vv = dev->vv_data;
+ struct videobuf_queue *q = &fh->video_q;
+
+ if (IS_CAPTURE_ACTIVE(fh) != 0)
+ video_end(fh, file);
+ else if (IS_OVERLAY_ACTIVE(fh) != 0)
+ saa7146_stop_preview(fh);
+
+ videobuf_stop(q);
+ /* hmm, why is this function declared void? */
+}
+
+
+static void video_irq_done(struct saa7146_dev *dev, unsigned long st)
+{
+ struct saa7146_vv *vv = dev->vv_data;
+ struct saa7146_dmaqueue *q = &vv->video_dmaq;
+
+ spin_lock(&dev->slock);
+ DEB_CAP("called\n");
+
+ /* only finish the buffer if we have one... */
+ if( NULL != q->curr ) {
+ saa7146_buffer_finish(dev,q,VIDEOBUF_DONE);
+ }
+ saa7146_buffer_next(dev,q,0);
+
+ spin_unlock(&dev->slock);
+}
+
+static ssize_t video_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
+{
+ struct saa7146_fh *fh = file->private_data;
+ struct saa7146_dev *dev = fh->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+ ssize_t ret = 0;
+
+ DEB_EE("called\n");
+
+ if ((vv->video_status & STATUS_CAPTURE) != 0) {
+ /* fixme: should we allow read() captures while streaming capture? */
+ if (vv->video_fh == fh) {
+ DEB_S("already capturing\n");
+ return -EBUSY;
+ }
+ DEB_S("already capturing in another open\n");
+ return -EBUSY;
+ }
+
+ ret = video_begin(fh);
+ if( 0 != ret) {
+ goto out;
+ }
+
+ ret = videobuf_read_one(&fh->video_q , data, count, ppos,
+ file->f_flags & O_NONBLOCK);
+ if (ret != 0) {
+ video_end(fh, file);
+ } else {
+ ret = video_end(fh, file);
+ }
+out:
+ /* restart overlay if it was active before */
+ if (vv->ov_suspend != NULL) {
+ saa7146_start_preview(vv->ov_suspend);
+ vv->ov_suspend = NULL;
+ }
+
+ return ret;
+}
+
+const struct saa7146_use_ops saa7146_video_uops = {
+ .init = video_init,
+ .open = video_open,
+ .release = video_close,
+ .irq_done = video_irq_done,
+ .read = video_read,
+};
diff --git a/drivers/media/common/uvc.c b/drivers/media/common/uvc.c
new file mode 100644
index 000000000000..9c0ba7a6c185
--- /dev/null
+++ b/drivers/media/common/uvc.c
@@ -0,0 +1,183 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/usb/uvc.h>
+#include <linux/videodev2.h>
+
+/* ------------------------------------------------------------------------
+ * Video formats
+ */
+
+static const struct uvc_format_desc uvc_fmts[] = {
+ {
+ .guid = UVC_GUID_FORMAT_YUY2,
+ .fcc = V4L2_PIX_FMT_YUYV,
+ },
+ {
+ .guid = UVC_GUID_FORMAT_YUY2_ISIGHT,
+ .fcc = V4L2_PIX_FMT_YUYV,
+ },
+ {
+ .guid = UVC_GUID_FORMAT_NV12,
+ .fcc = V4L2_PIX_FMT_NV12,
+ },
+ {
+ .guid = UVC_GUID_FORMAT_MJPEG,
+ .fcc = V4L2_PIX_FMT_MJPEG,
+ },
+ {
+ .guid = UVC_GUID_FORMAT_YV12,
+ .fcc = V4L2_PIX_FMT_YVU420,
+ },
+ {
+ .guid = UVC_GUID_FORMAT_I420,
+ .fcc = V4L2_PIX_FMT_YUV420,
+ },
+ {
+ .guid = UVC_GUID_FORMAT_M420,
+ .fcc = V4L2_PIX_FMT_M420,
+ },
+ {
+ .guid = UVC_GUID_FORMAT_UYVY,
+ .fcc = V4L2_PIX_FMT_UYVY,
+ },
+ {
+ .guid = UVC_GUID_FORMAT_Y800,
+ .fcc = V4L2_PIX_FMT_GREY,
+ },
+ {
+ .guid = UVC_GUID_FORMAT_Y8,
+ .fcc = V4L2_PIX_FMT_GREY,
+ },
+ {
+ .guid = UVC_GUID_FORMAT_D3DFMT_L8,
+ .fcc = V4L2_PIX_FMT_GREY,
+ },
+ {
+ .guid = UVC_GUID_FORMAT_KSMEDIA_L8_IR,
+ .fcc = V4L2_PIX_FMT_GREY,
+ },
+ {
+ .guid = UVC_GUID_FORMAT_Y10,
+ .fcc = V4L2_PIX_FMT_Y10,
+ },
+ {
+ .guid = UVC_GUID_FORMAT_Y12,
+ .fcc = V4L2_PIX_FMT_Y12,
+ },
+ {
+ .guid = UVC_GUID_FORMAT_Y16,
+ .fcc = V4L2_PIX_FMT_Y16,
+ },
+ {
+ .guid = UVC_GUID_FORMAT_BY8,
+ .fcc = V4L2_PIX_FMT_SBGGR8,
+ },
+ {
+ .guid = UVC_GUID_FORMAT_BA81,
+ .fcc = V4L2_PIX_FMT_SBGGR8,
+ },
+ {
+ .guid = UVC_GUID_FORMAT_GBRG,
+ .fcc = V4L2_PIX_FMT_SGBRG8,
+ },
+ {
+ .guid = UVC_GUID_FORMAT_GRBG,
+ .fcc = V4L2_PIX_FMT_SGRBG8,
+ },
+ {
+ .guid = UVC_GUID_FORMAT_RGGB,
+ .fcc = V4L2_PIX_FMT_SRGGB8,
+ },
+ {
+ .guid = UVC_GUID_FORMAT_RGBP,
+ .fcc = V4L2_PIX_FMT_RGB565,
+ },
+ {
+ .guid = UVC_GUID_FORMAT_BGR3,
+ .fcc = V4L2_PIX_FMT_BGR24,
+ },
+ {
+ .guid = UVC_GUID_FORMAT_BGR4,
+ .fcc = V4L2_PIX_FMT_XBGR32,
+ },
+ {
+ .guid = UVC_GUID_FORMAT_H264,
+ .fcc = V4L2_PIX_FMT_H264,
+ },
+ {
+ .guid = UVC_GUID_FORMAT_H265,
+ .fcc = V4L2_PIX_FMT_HEVC,
+ },
+ {
+ .guid = UVC_GUID_FORMAT_Y8I,
+ .fcc = V4L2_PIX_FMT_Y8I,
+ },
+ {
+ .guid = UVC_GUID_FORMAT_Y12I,
+ .fcc = V4L2_PIX_FMT_Y12I,
+ },
+ {
+ .guid = UVC_GUID_FORMAT_Z16,
+ .fcc = V4L2_PIX_FMT_Z16,
+ },
+ {
+ .guid = UVC_GUID_FORMAT_RW10,
+ .fcc = V4L2_PIX_FMT_SRGGB10P,
+ },
+ {
+ .guid = UVC_GUID_FORMAT_BG16,
+ .fcc = V4L2_PIX_FMT_SBGGR16,
+ },
+ {
+ .guid = UVC_GUID_FORMAT_GB16,
+ .fcc = V4L2_PIX_FMT_SGBRG16,
+ },
+ {
+ .guid = UVC_GUID_FORMAT_RG16,
+ .fcc = V4L2_PIX_FMT_SRGGB16,
+ },
+ {
+ .guid = UVC_GUID_FORMAT_GR16,
+ .fcc = V4L2_PIX_FMT_SGRBG16,
+ },
+ {
+ .guid = UVC_GUID_FORMAT_INVZ,
+ .fcc = V4L2_PIX_FMT_Z16,
+ },
+ {
+ .guid = UVC_GUID_FORMAT_INVI,
+ .fcc = V4L2_PIX_FMT_Y10,
+ },
+ {
+ .guid = UVC_GUID_FORMAT_INZI,
+ .fcc = V4L2_PIX_FMT_INZI,
+ },
+ {
+ .guid = UVC_GUID_FORMAT_CNF4,
+ .fcc = V4L2_PIX_FMT_CNF4,
+ },
+ {
+ .guid = UVC_GUID_FORMAT_HEVC,
+ .fcc = V4L2_PIX_FMT_HEVC,
+ },
+};
+
+const struct uvc_format_desc *uvc_format_by_guid(const u8 guid[16])
+{
+ unsigned int len = ARRAY_SIZE(uvc_fmts);
+ unsigned int i;
+
+ for (i = 0; i < len; ++i) {
+ if (memcmp(guid, uvc_fmts[i].guid, 16) == 0)
+ return &uvc_fmts[i];
+ }
+
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(uvc_format_by_guid);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/common/videobuf2/videobuf2-core.c b/drivers/media/common/videobuf2/videobuf2-core.c
index fc3758a5bc1c..cf6727d9c81f 100644
--- a/drivers/media/common/videobuf2/videobuf2-core.c
+++ b/drivers/media/common/videobuf2/videobuf2-core.c
@@ -502,27 +502,11 @@ static void __vb2_free_mem(struct vb2_queue *q, unsigned int buffers)
* related information, if no buffers are left return the queue to an
* uninitialized state. Might be called even if the queue has already been freed.
*/
-static int __vb2_queue_free(struct vb2_queue *q, unsigned int buffers)
+static void __vb2_queue_free(struct vb2_queue *q, unsigned int buffers)
{
unsigned int buffer;
- /*
- * Sanity check: when preparing a buffer the queue lock is released for
- * a short while (see __buf_prepare for the details), which would allow
- * a race with a reqbufs which can call this function. Removing the
- * buffers from underneath __buf_prepare is obviously a bad idea, so we
- * check if any of the buffers is in the state PREPARING, and if so we
- * just return -EAGAIN.
- */
- for (buffer = q->num_buffers - buffers; buffer < q->num_buffers;
- ++buffer) {
- if (q->bufs[buffer] == NULL)
- continue;
- if (q->bufs[buffer]->state == VB2_BUF_STATE_PREPARING) {
- dprintk(q, 1, "preparing buffers, cannot free\n");
- return -EAGAIN;
- }
- }
+ lockdep_assert_held(&q->mmap_lock);
/* Call driver-provided cleanup function for each buffer, if provided */
for (buffer = q->num_buffers - buffers; buffer < q->num_buffers;
@@ -616,7 +600,6 @@ static int __vb2_queue_free(struct vb2_queue *q, unsigned int buffers)
q->memory = VB2_MEMORY_UNKNOWN;
INIT_LIST_HEAD(&q->queued_list);
}
- return 0;
}
bool vb2_buffer_in_use(struct vb2_queue *q, struct vb2_buffer *vb)
@@ -798,10 +781,8 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory,
* queued without ever calling STREAMON.
*/
__vb2_queue_cancel(q);
- ret = __vb2_queue_free(q, q->num_buffers);
+ __vb2_queue_free(q, q->num_buffers);
mutex_unlock(&q->mmap_lock);
- if (ret)
- return ret;
/*
* In case of REQBUFS(0) return immediately without calling
@@ -2149,8 +2130,6 @@ int vb2_core_streamon(struct vb2_queue *q, unsigned int type)
if (ret)
return ret;
- q->streaming = 1;
-
/*
* Tell driver to start streaming provided sufficient buffers
* are available.
@@ -2161,12 +2140,13 @@ int vb2_core_streamon(struct vb2_queue *q, unsigned int type)
goto unprepare;
}
+ q->streaming = 1;
+
dprintk(q, 3, "successful\n");
return 0;
unprepare:
call_void_qop(q, unprepare_streaming, q);
- q->streaming = 0;
return ret;
}
EXPORT_SYMBOL_GPL(vb2_core_streamon);
diff --git a/drivers/media/common/videobuf2/videobuf2-dma-contig.c b/drivers/media/common/videobuf2/videobuf2-dma-contig.c
index 5f1175f8b349..205d3cac425c 100644
--- a/drivers/media/common/videobuf2/videobuf2-dma-contig.c
+++ b/drivers/media/common/videobuf2/videobuf2-dma-contig.c
@@ -293,7 +293,7 @@ static int vb2_dc_mmap(void *buf_priv, struct vm_area_struct *vma)
return ret;
}
- vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP;
+ vm_flags_set(vma, VM_DONTEXPAND | VM_DONTDUMP);
vma->vm_private_data = &buf->handler;
vma->vm_ops = &vb2_common_vm_ops;
diff --git a/drivers/media/common/videobuf2/videobuf2-vmalloc.c b/drivers/media/common/videobuf2/videobuf2-vmalloc.c
index 959b45beb1f3..a6c6d2fcaaa4 100644
--- a/drivers/media/common/videobuf2/videobuf2-vmalloc.c
+++ b/drivers/media/common/videobuf2/videobuf2-vmalloc.c
@@ -185,7 +185,7 @@ static int vb2_vmalloc_mmap(void *buf_priv, struct vm_area_struct *vma)
/*
* Make sure that vm_areas for 2 buffers won't be merged together
*/
- vma->vm_flags |= VM_DONTEXPAND;
+ vm_flags_set(vma, VM_DONTEXPAND);
/*
* Use common vm_area operations to track buffer refcount.
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.c
index 4cf2d7cfd3f5..0a1f3899d72c 100644
--- a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.c
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.c
@@ -2162,11 +2162,11 @@ int cxd2880_tnrdmd_check_internal_cpu_status(struct cxd2880_tnrdmd
else
*task_completed = 0;
- return ret;
+ return 0;
}
if (cpu_status != 0) {
*task_completed = 0;
- return ret;
+ return 0;
}
ret = cxd2880_tnrdmd_mon_internal_cpu_status_sub(tnr_dmd, &cpu_status);
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.c
index fe3c6f8b1b3e..c7e79da8c432 100644
--- a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.c
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.c
@@ -833,12 +833,12 @@ int cxd2880_tnrdmd_dvbt_check_demod_lock(struct cxd2880_tnrdmd
else
*lock = CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT;
- return ret;
+ return 0;
}
if (sync_stat == 6) {
*lock = CXD2880_TNRDMD_LOCK_RESULT_LOCKED;
- return ret;
+ return 0;
}
ret =
@@ -854,7 +854,7 @@ int cxd2880_tnrdmd_dvbt_check_demod_lock(struct cxd2880_tnrdmd
else
*lock = CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT;
- return ret;
+ return 0;
}
int cxd2880_tnrdmd_dvbt_check_ts_lock(struct cxd2880_tnrdmd
@@ -893,15 +893,15 @@ int cxd2880_tnrdmd_dvbt_check_ts_lock(struct cxd2880_tnrdmd
else
*lock = CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT;
- return ret;
+ return 0;
}
if (ts_lock) {
*lock = CXD2880_TNRDMD_LOCK_RESULT_LOCKED;
- return ret;
+ return 0;
} else if (!unlock_detected) {
*lock = CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT;
- return ret;
+ return 0;
}
ret =
@@ -915,5 +915,5 @@ int cxd2880_tnrdmd_dvbt_check_ts_lock(struct cxd2880_tnrdmd
else
*lock = CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT;
- return ret;
+ return 0;
}
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.c
index dd32004a12d8..a9ab983348c8 100644
--- a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.c
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.c
@@ -1024,12 +1024,12 @@ int cxd2880_tnrdmd_dvbt2_check_demod_lock(struct cxd2880_tnrdmd
else
*lock = CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT;
- return ret;
+ return 0;
}
if (sync_stat == 6) {
*lock = CXD2880_TNRDMD_LOCK_RESULT_LOCKED;
- return ret;
+ return 0;
}
ret =
@@ -1045,7 +1045,7 @@ int cxd2880_tnrdmd_dvbt2_check_demod_lock(struct cxd2880_tnrdmd
else
*lock = CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT;
- return ret;
+ return 0;
}
int cxd2880_tnrdmd_dvbt2_check_ts_lock(struct cxd2880_tnrdmd
@@ -1084,15 +1084,15 @@ int cxd2880_tnrdmd_dvbt2_check_ts_lock(struct cxd2880_tnrdmd
else
*lock = CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT;
- return ret;
+ return 0;
}
if (ts_lock) {
*lock = CXD2880_TNRDMD_LOCK_RESULT_LOCKED;
- return ret;
+ return 0;
} else if (!unlock_detected) {
*lock = CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT;
- return ret;
+ return 0;
}
ret =
@@ -1106,7 +1106,7 @@ int cxd2880_tnrdmd_dvbt2_check_ts_lock(struct cxd2880_tnrdmd
else
*lock = CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT;
- return ret;
+ return 0;
}
int cxd2880_tnrdmd_dvbt2_set_plp_cfg(struct cxd2880_tnrdmd
diff --git a/drivers/media/dvb-frontends/drx39xyj/drxj.c b/drivers/media/dvb-frontends/drx39xyj/drxj.c
index 1dff59ca21a1..6bf6559b127f 100644
--- a/drivers/media/dvb-frontends/drx39xyj/drxj.c
+++ b/drivers/media/dvb-frontends/drx39xyj/drxj.c
@@ -9539,7 +9539,8 @@ ctrl_get_qam_sig_quality(struct drx_demod_instance *demod)
qam_sl_sig_power = DRXJ_QAM_SL_SIG_POWER_QAM256 << 2;
break;
default:
- return -EIO;
+ rc = -EIO;
+ goto rw_error;
}
/* ------------------------------ */
@@ -10916,7 +10917,8 @@ ctrl_set_standard(struct drx_demod_instance *demod, enum drx_standard *standard)
break;
case DRX_STANDARD_AUTO:
default:
- return -EINVAL;
+ rc = -EINVAL;
+ goto rw_error;
}
/*
@@ -11463,7 +11465,8 @@ static int drxj_open(struct drx_demod_instance *demod)
if (DRX_ISPOWERDOWNMODE(demod->my_common_attr->current_power_mode)) {
pr_err("Should powerup before loading the firmware.");
- return -EINVAL;
+ rc = -EINVAL;
+ goto rw_error;
}
rc = drx_ctrl_u_code(demod, &ucode_info, UCODE_UPLOAD);
diff --git a/drivers/media/dvb-frontends/dvb-pll.c b/drivers/media/dvb-frontends/dvb-pll.c
index baf2a378e565..e35e00db7dbb 100644
--- a/drivers/media/dvb-frontends/dvb-pll.c
+++ b/drivers/media/dvb-frontends/dvb-pll.c
@@ -870,8 +870,9 @@ EXPORT_SYMBOL(dvb_pll_attach);
static int
-dvb_pll_probe(struct i2c_client *client, const struct i2c_device_id *id)
+dvb_pll_probe(struct i2c_client *client)
{
+ const struct i2c_device_id *id = i2c_client_get_device_id(client);
struct dvb_pll_config *cfg;
struct dvb_frontend *fe;
unsigned int desc_id;
@@ -941,7 +942,7 @@ static struct i2c_driver dvb_pll_driver = {
.driver = {
.name = "dvb_pll",
},
- .probe = dvb_pll_probe,
+ .probe_new = dvb_pll_probe,
.remove = dvb_pll_remove,
.id_table = dvb_pll_id,
};
diff --git a/drivers/media/dvb-frontends/m88ds3103.c b/drivers/media/dvb-frontends/m88ds3103.c
index 4e844b2ef597..f26508b217ee 100644
--- a/drivers/media/dvb-frontends/m88ds3103.c
+++ b/drivers/media/dvb-frontends/m88ds3103.c
@@ -1760,9 +1760,9 @@ static struct i2c_adapter *m88ds3103_get_i2c_adapter(struct i2c_client *client)
return dev->muxc->adapter[0];
}
-static int m88ds3103_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int m88ds3103_probe(struct i2c_client *client)
{
+ const struct i2c_device_id *id = i2c_client_get_device_id(client);
struct m88ds3103_dev *dev;
struct m88ds3103_platform_data *pdata = client->dev.platform_data;
int ret;
@@ -1941,7 +1941,7 @@ static struct i2c_driver m88ds3103_driver = {
.name = "m88ds3103",
.suppress_bind_attrs = true,
},
- .probe = m88ds3103_probe,
+ .probe_new = m88ds3103_probe,
.remove = m88ds3103_remove,
.id_table = m88ds3103_id_table,
};
diff --git a/drivers/media/dvb-frontends/mb86a16.c b/drivers/media/dvb-frontends/mb86a16.c
index 2505f1e5794e..d3e29937cf4c 100644
--- a/drivers/media/dvb-frontends/mb86a16.c
+++ b/drivers/media/dvb-frontends/mb86a16.c
@@ -1498,6 +1498,7 @@ static int mb86a16_send_diseqc_msg(struct dvb_frontend *fe,
struct dvb_diseqc_master_cmd *cmd)
{
struct mb86a16_state *state = fe->demodulator_priv;
+ int ret = -EREMOTEIO;
int i;
u8 regs;
@@ -1510,8 +1511,10 @@ static int mb86a16_send_diseqc_msg(struct dvb_frontend *fe,
regs = 0x18;
- if (cmd->msg_len > 5 || cmd->msg_len < 4)
- return -EINVAL;
+ if (cmd->msg_len > 5 || cmd->msg_len < 4) {
+ ret = -EINVAL;
+ goto err;
+ }
for (i = 0; i < cmd->msg_len; i++) {
if (mb86a16_write(state, regs, cmd->msg[i]) < 0)
@@ -1532,7 +1535,7 @@ static int mb86a16_send_diseqc_msg(struct dvb_frontend *fe,
err:
dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error");
- return -EREMOTEIO;
+ return ret;
}
static int mb86a16_send_diseqc_burst(struct dvb_frontend *fe,
diff --git a/drivers/media/dvb-frontends/mn88443x.c b/drivers/media/dvb-frontends/mn88443x.c
index 452571b380b7..1f1753f2ab1a 100644
--- a/drivers/media/dvb-frontends/mn88443x.c
+++ b/drivers/media/dvb-frontends/mn88443x.c
@@ -673,9 +673,9 @@ static const struct regmap_config regmap_config = {
.cache_type = REGCACHE_NONE,
};
-static int mn88443x_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int mn88443x_probe(struct i2c_client *client)
{
+ const struct i2c_device_id *id = i2c_client_get_device_id(client);
struct mn88443x_config *conf = client->dev.platform_data;
struct mn88443x_priv *chip;
struct device *dev = &client->dev;
@@ -800,7 +800,7 @@ static struct i2c_driver mn88443x_driver = {
.name = "mn88443x",
.of_match_table = of_match_ptr(mn88443x_of_match),
},
- .probe = mn88443x_probe,
+ .probe_new = mn88443x_probe,
.remove = mn88443x_remove,
.id_table = mn88443x_i2c_id,
};
diff --git a/drivers/media/dvb-frontends/tc90522.c b/drivers/media/dvb-frontends/tc90522.c
index c22d2a2b2a45..77a991bf4713 100644
--- a/drivers/media/dvb-frontends/tc90522.c
+++ b/drivers/media/dvb-frontends/tc90522.c
@@ -779,9 +779,9 @@ static const struct dvb_frontend_ops tc90522_ops_ter = {
};
-static int tc90522_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int tc90522_probe(struct i2c_client *client)
{
+ const struct i2c_device_id *id = i2c_client_get_device_id(client);
struct tc90522_state *state;
struct tc90522_config *cfg;
const struct dvb_frontend_ops *ops;
@@ -840,7 +840,7 @@ static struct i2c_driver tc90522_driver = {
.driver = {
.name = "tc90522",
},
- .probe = tc90522_probe,
+ .probe_new = tc90522_probe,
.remove = tc90522_remove,
.id_table = tc90522_id,
};
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index 833241897d63..c3d5952ca27e 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -162,6 +162,19 @@ config VIDEO_IMX290
To compile this driver as a module, choose M here: the
module will be called imx290.
+config VIDEO_IMX296
+ tristate "Sony IMX296 sensor support"
+ depends on I2C && VIDEO_DEV
+ select MEDIA_CONTROLLER
+ select V4L2_FWNODE
+ select VIDEO_V4L2_SUBDEV_API
+ help
+ This is a Video4Linux2 sensor driver for the Sony
+ IMX296 camera.
+
+ To compile this driver as a module, choose M here: the
+ module will be called imx296.
+
config VIDEO_IMX319
tristate "Sony IMX319 sensor support"
depends on I2C && VIDEO_DEV
@@ -228,6 +241,20 @@ config VIDEO_IMX412
To compile this driver as a module, choose M here: the
module will be called imx412.
+config VIDEO_IMX415
+ tristate "Sony IMX415 sensor support"
+ depends on OF_GPIO
+ depends on I2C && VIDEO_DEV
+ select VIDEO_V4L2_SUBDEV_API
+ select MEDIA_CONTROLLER
+ select V4L2_FWNODE
+ help
+ This is a Video4Linux2 sensor driver for the Sony
+ IMX415 camera.
+
+ To compile this driver as a module, choose M here: the
+ module will be called imx415.
+
config VIDEO_MAX9271_LIB
tristate
@@ -645,6 +672,19 @@ config VIDEO_OV8856
To compile this driver as a module, choose M here: the
module will be called ov8856.
+config VIDEO_OV8858
+ tristate "OmniVision OV8858 sensor support"
+ depends on I2C && PM && VIDEO_DEV
+ select MEDIA_CONTROLLER
+ select VIDEO_V4L2_SUBDEV_API
+ select V4L2_FWNODE
+ help
+ This is a Video4Linux2 sensor driver for OmniVision
+ OV8858 camera sensor.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ov8858.
+
config VIDEO_OV8865
tristate "OmniVision OV8865 sensor support"
depends on I2C && PM && VIDEO_DEV
diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
index 4d6c052bb5a7..4f5e9d9cee85 100644
--- a/drivers/media/i2c/Makefile
+++ b/drivers/media/i2c/Makefile
@@ -43,11 +43,13 @@ obj-$(CONFIG_VIDEO_IMX219) += imx219.o
obj-$(CONFIG_VIDEO_IMX258) += imx258.o
obj-$(CONFIG_VIDEO_IMX274) += imx274.o
obj-$(CONFIG_VIDEO_IMX290) += imx290.o
+obj-$(CONFIG_VIDEO_IMX296) += imx296.o
obj-$(CONFIG_VIDEO_IMX319) += imx319.o
obj-$(CONFIG_VIDEO_IMX334) += imx334.o
obj-$(CONFIG_VIDEO_IMX335) += imx335.o
obj-$(CONFIG_VIDEO_IMX355) += imx355.o
obj-$(CONFIG_VIDEO_IMX412) += imx412.o
+obj-$(CONFIG_VIDEO_IMX415) += imx415.o
obj-$(CONFIG_VIDEO_IR_I2C) += ir-kbd-i2c.o
obj-$(CONFIG_VIDEO_ISL7998X) += isl7998x.o
obj-$(CONFIG_VIDEO_KS0127) += ks0127.o
@@ -96,6 +98,7 @@ obj-$(CONFIG_VIDEO_OV7670) += ov7670.o
obj-$(CONFIG_VIDEO_OV772X) += ov772x.o
obj-$(CONFIG_VIDEO_OV7740) += ov7740.o
obj-$(CONFIG_VIDEO_OV8856) += ov8856.o
+obj-$(CONFIG_VIDEO_OV8858) += ov8858.o
obj-$(CONFIG_VIDEO_OV8865) += ov8865.o
obj-$(CONFIG_VIDEO_OV9282) += ov9282.o
obj-$(CONFIG_VIDEO_OV9640) += ov9640.o
diff --git a/drivers/media/i2c/adv7180.c b/drivers/media/i2c/adv7180.c
index 216fe396973f..a22402b7acff 100644
--- a/drivers/media/i2c/adv7180.c
+++ b/drivers/media/i2c/adv7180.c
@@ -1393,9 +1393,9 @@ out_unlock:
return ret;
}
-static int adv7180_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int adv7180_probe(struct i2c_client *client)
{
+ const struct i2c_device_id *id = i2c_client_get_device_id(client);
struct device_node *np = client->dev.of_node;
struct adv7180_state *state;
struct v4l2_subdev *sd;
@@ -1610,7 +1610,7 @@ static struct i2c_driver adv7180_driver = {
.pm = ADV7180_PM_OPS,
.of_match_table = of_match_ptr(adv7180_of_id),
},
- .probe = adv7180_probe,
+ .probe_new = adv7180_probe,
.remove = adv7180_remove,
.id_table = adv7180_id,
};
diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c
index bda0c547ce44..9d218962d7c8 100644
--- a/drivers/media/i2c/adv7604.c
+++ b/drivers/media/i2c/adv7604.c
@@ -3401,9 +3401,9 @@ static void adv76xx_reset(struct adv76xx_state *state)
}
}
-static int adv76xx_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int adv76xx_probe(struct i2c_client *client)
{
+ const struct i2c_device_id *id = i2c_client_get_device_id(client);
static const struct v4l2_dv_timings cea640x480 =
V4L2_DV_BT_CEA_640X480P59_94;
struct adv76xx_state *state;
@@ -3686,7 +3686,7 @@ static struct i2c_driver adv76xx_driver = {
.name = "adv7604",
.of_match_table = of_match_ptr(adv76xx_of_id),
},
- .probe = adv76xx_probe,
+ .probe_new = adv76xx_probe,
.remove = adv76xx_remove,
.id_table = adv76xx_i2c_id,
};
diff --git a/drivers/media/i2c/ak7375.c b/drivers/media/i2c/ak7375.c
index 1af9f698eecf..e7cec45bc271 100644
--- a/drivers/media/i2c/ak7375.c
+++ b/drivers/media/i2c/ak7375.c
@@ -6,6 +6,7 @@
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
@@ -23,17 +24,29 @@
*/
#define AK7375_CTRL_STEPS 64
#define AK7375_CTRL_DELAY_US 1000
+/*
+ * The vcm may take up 10 ms (tDELAY) to power on and start taking
+ * I2C messages. Based on AK7371 datasheet.
+ */
+#define AK7375_POWER_DELAY_US 10000
#define AK7375_REG_POSITION 0x0
#define AK7375_REG_CONT 0x2
#define AK7375_MODE_ACTIVE 0x0
#define AK7375_MODE_STANDBY 0x40
+static const char * const ak7375_supply_names[] = {
+ "vdd",
+ "vio",
+};
+
/* ak7375 device structure */
struct ak7375_device {
struct v4l2_ctrl_handler ctrls_vcm;
struct v4l2_subdev sd;
struct v4l2_ctrl *focus;
+ struct regulator_bulk_data supplies[ARRAY_SIZE(ak7375_supply_names)];
+
/* active or standby mode */
bool active;
};
@@ -133,12 +146,24 @@ static int ak7375_probe(struct i2c_client *client)
{
struct ak7375_device *ak7375_dev;
int ret;
+ unsigned int i;
ak7375_dev = devm_kzalloc(&client->dev, sizeof(*ak7375_dev),
GFP_KERNEL);
if (!ak7375_dev)
return -ENOMEM;
+ for (i = 0; i < ARRAY_SIZE(ak7375_supply_names); i++)
+ ak7375_dev->supplies[i].supply = ak7375_supply_names[i];
+
+ ret = devm_regulator_bulk_get(&client->dev,
+ ARRAY_SIZE(ak7375_supply_names),
+ ak7375_dev->supplies);
+ if (ret) {
+ dev_err_probe(&client->dev, ret, "Failed to get regulators\n");
+ return ret;
+ }
+
v4l2_i2c_subdev_init(&ak7375_dev->sd, client, &ak7375_ops);
ak7375_dev->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
ak7375_dev->sd.internal_ops = &ak7375_int_ops;
@@ -208,6 +233,11 @@ static int __maybe_unused ak7375_vcm_suspend(struct device *dev)
if (ret)
dev_err(dev, "%s I2C failure: %d\n", __func__, ret);
+ ret = regulator_bulk_disable(ARRAY_SIZE(ak7375_supply_names),
+ ak7375_dev->supplies);
+ if (ret)
+ return ret;
+
ak7375_dev->active = false;
return 0;
@@ -228,6 +258,14 @@ static int __maybe_unused ak7375_vcm_resume(struct device *dev)
if (ak7375_dev->active)
return 0;
+ ret = regulator_bulk_enable(ARRAY_SIZE(ak7375_supply_names),
+ ak7375_dev->supplies);
+ if (ret)
+ return ret;
+
+ /* Wait for vcm to become ready */
+ usleep_range(AK7375_POWER_DELAY_US, AK7375_POWER_DELAY_US + 500);
+
ret = ak7375_i2c_write(ak7375_dev, AK7375_REG_CONT,
AK7375_MODE_ACTIVE, 1);
if (ret) {
diff --git a/drivers/media/i2c/cs53l32a.c b/drivers/media/i2c/cs53l32a.c
index 9461589aea30..670f89de32d4 100644
--- a/drivers/media/i2c/cs53l32a.c
+++ b/drivers/media/i2c/cs53l32a.c
@@ -128,9 +128,9 @@ static const struct v4l2_subdev_ops cs53l32a_ops = {
* concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1'
*/
-static int cs53l32a_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int cs53l32a_probe(struct i2c_client *client)
{
+ const struct i2c_device_id *id = i2c_client_get_device_id(client);
struct cs53l32a_state *state;
struct v4l2_subdev *sd;
int i;
@@ -209,7 +209,7 @@ static struct i2c_driver cs53l32a_driver = {
.driver = {
.name = "cs53l32a",
},
- .probe = cs53l32a_probe,
+ .probe_new = cs53l32a_probe,
.remove = cs53l32a_remove,
.id_table = cs53l32a_id,
};
diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c
index 77bd79a5954e..f9471c9e3a74 100644
--- a/drivers/media/i2c/imx219.c
+++ b/drivers/media/i2c/imx219.c
@@ -42,10 +42,16 @@
/* External clock frequency is 24.0M */
#define IMX219_XCLK_FREQ 24000000
-/* Pixel rate is fixed at 182.4M for all the modes */
+/* Pixel rate is fixed for all the modes */
#define IMX219_PIXEL_RATE 182400000
+#define IMX219_PIXEL_RATE_4LANE 280800000
#define IMX219_DEFAULT_LINK_FREQ 456000000
+#define IMX219_DEFAULT_LINK_FREQ_4LANE 363000000
+
+#define IMX219_REG_CSI_LANE_MODE 0x0114
+#define IMX219_CSI_2_LANE_MODE 0x01
+#define IMX219_CSI_4_LANE_MODE 0x03
/* V_TIMING internal */
#define IMX219_REG_VTS 0x0160
@@ -89,6 +95,12 @@
#define IMX219_REG_ORIENTATION 0x0172
+/* Binning Mode */
+#define IMX219_REG_BINNING_MODE 0x0174
+#define IMX219_BINNING_NONE 0x0000
+#define IMX219_BINNING_2X2 0x0101
+#define IMX219_BINNING_2X2_ANALOG 0x0303
+
/* Test Pattern Control */
#define IMX219_REG_TEST_PATTERN 0x0600
#define IMX219_TEST_PATTERN_DISABLE 0
@@ -143,25 +155,66 @@ struct imx219_mode {
/* Default register values */
struct imx219_reg_list reg_list;
+
+ /* 2x2 binning is used */
+ bool binning;
};
-/*
- * Register sets lifted off the i2C interface from the Raspberry Pi firmware
- * driver.
- * 3280x2464 = mode 2, 1920x1080 = mode 1, 1640x1232 = mode 4, 640x480 = mode 7.
- */
-static const struct imx219_reg mode_3280x2464_regs[] = {
- {0x0100, 0x00},
+static const struct imx219_reg imx219_common_regs[] = {
+ {0x0100, 0x00}, /* Mode Select */
+
+ /* To Access Addresses 3000-5fff, send the following commands */
{0x30eb, 0x0c},
{0x30eb, 0x05},
{0x300a, 0xff},
{0x300b, 0xff},
{0x30eb, 0x05},
{0x30eb, 0x09},
- {0x0114, 0x01},
- {0x0128, 0x00},
- {0x012a, 0x18},
+
+ /* PLL Clock Table */
+ {0x0301, 0x05}, /* VTPXCK_DIV */
+ {0x0303, 0x01}, /* VTSYSCK_DIV */
+ {0x0304, 0x03}, /* PREPLLCK_VT_DIV 0x03 = AUTO set */
+ {0x0305, 0x03}, /* PREPLLCK_OP_DIV 0x03 = AUTO set */
+ {0x0306, 0x00}, /* PLL_VT_MPY */
+ {0x0307, 0x39},
+ {0x030b, 0x01}, /* OP_SYS_CLK_DIV */
+ {0x030c, 0x00}, /* PLL_OP_MPY */
+ {0x030d, 0x72},
+
+ /* Undocumented registers */
+ {0x455e, 0x00},
+ {0x471e, 0x4b},
+ {0x4767, 0x0f},
+ {0x4750, 0x14},
+ {0x4540, 0x00},
+ {0x47b4, 0x14},
+ {0x4713, 0x30},
+ {0x478b, 0x10},
+ {0x478f, 0x10},
+ {0x4793, 0x10},
+ {0x4797, 0x0e},
+ {0x479b, 0x0e},
+
+ /* Frame Bank Register Group "A" */
+ {0x0162, 0x0d}, /* Line_Length_A */
+ {0x0163, 0x78},
+ {0x0170, 0x01}, /* X_ODD_INC_A */
+ {0x0171, 0x01}, /* Y_ODD_INC_A */
+
+ /* Output setup registers */
+ {0x0114, 0x01}, /* CSI 2-Lane Mode */
+ {0x0128, 0x00}, /* DPHY Auto Mode */
+ {0x012a, 0x18}, /* EXCK_Freq */
{0x012b, 0x00},
+};
+
+/*
+ * Register sets lifted off the i2C interface from the Raspberry Pi firmware
+ * driver.
+ * 3280x2464 = mode 2, 1920x1080 = mode 1, 1640x1232 = mode 4, 640x480 = mode 7.
+ */
+static const struct imx219_reg mode_3280x2464_regs[] = {
{0x0164, 0x00},
{0x0165, 0x00},
{0x0166, 0x0c},
@@ -174,53 +227,13 @@ static const struct imx219_reg mode_3280x2464_regs[] = {
{0x016d, 0xd0},
{0x016e, 0x09},
{0x016f, 0xa0},
- {0x0170, 0x01},
- {0x0171, 0x01},
- {0x0174, 0x00},
- {0x0175, 0x00},
- {0x0301, 0x05},
- {0x0303, 0x01},
- {0x0304, 0x03},
- {0x0305, 0x03},
- {0x0306, 0x00},
- {0x0307, 0x39},
- {0x030b, 0x01},
- {0x030c, 0x00},
- {0x030d, 0x72},
{0x0624, 0x0c},
{0x0625, 0xd0},
{0x0626, 0x09},
{0x0627, 0xa0},
- {0x455e, 0x00},
- {0x471e, 0x4b},
- {0x4767, 0x0f},
- {0x4750, 0x14},
- {0x4540, 0x00},
- {0x47b4, 0x14},
- {0x4713, 0x30},
- {0x478b, 0x10},
- {0x478f, 0x10},
- {0x4793, 0x10},
- {0x4797, 0x0e},
- {0x479b, 0x0e},
- {0x0162, 0x0d},
- {0x0163, 0x78},
};
static const struct imx219_reg mode_1920_1080_regs[] = {
- {0x0100, 0x00},
- {0x30eb, 0x05},
- {0x30eb, 0x0c},
- {0x300a, 0xff},
- {0x300b, 0xff},
- {0x30eb, 0x05},
- {0x30eb, 0x09},
- {0x0114, 0x01},
- {0x0128, 0x00},
- {0x012a, 0x18},
- {0x012b, 0x00},
- {0x0162, 0x0d},
- {0x0163, 0x78},
{0x0164, 0x02},
{0x0165, 0xa8},
{0x0166, 0x0a},
@@ -233,49 +246,13 @@ static const struct imx219_reg mode_1920_1080_regs[] = {
{0x016d, 0x80},
{0x016e, 0x04},
{0x016f, 0x38},
- {0x0170, 0x01},
- {0x0171, 0x01},
- {0x0174, 0x00},
- {0x0175, 0x00},
- {0x0301, 0x05},
- {0x0303, 0x01},
- {0x0304, 0x03},
- {0x0305, 0x03},
- {0x0306, 0x00},
- {0x0307, 0x39},
- {0x030b, 0x01},
- {0x030c, 0x00},
- {0x030d, 0x72},
{0x0624, 0x07},
{0x0625, 0x80},
{0x0626, 0x04},
{0x0627, 0x38},
- {0x455e, 0x00},
- {0x471e, 0x4b},
- {0x4767, 0x0f},
- {0x4750, 0x14},
- {0x4540, 0x00},
- {0x47b4, 0x14},
- {0x4713, 0x30},
- {0x478b, 0x10},
- {0x478f, 0x10},
- {0x4793, 0x10},
- {0x4797, 0x0e},
- {0x479b, 0x0e},
};
static const struct imx219_reg mode_1640_1232_regs[] = {
- {0x0100, 0x00},
- {0x30eb, 0x0c},
- {0x30eb, 0x05},
- {0x300a, 0xff},
- {0x300b, 0xff},
- {0x30eb, 0x05},
- {0x30eb, 0x09},
- {0x0114, 0x01},
- {0x0128, 0x00},
- {0x012a, 0x18},
- {0x012b, 0x00},
{0x0164, 0x00},
{0x0165, 0x00},
{0x0166, 0x0c},
@@ -288,53 +265,13 @@ static const struct imx219_reg mode_1640_1232_regs[] = {
{0x016d, 0x68},
{0x016e, 0x04},
{0x016f, 0xd0},
- {0x0170, 0x01},
- {0x0171, 0x01},
- {0x0174, 0x01},
- {0x0175, 0x01},
- {0x0301, 0x05},
- {0x0303, 0x01},
- {0x0304, 0x03},
- {0x0305, 0x03},
- {0x0306, 0x00},
- {0x0307, 0x39},
- {0x030b, 0x01},
- {0x030c, 0x00},
- {0x030d, 0x72},
{0x0624, 0x06},
{0x0625, 0x68},
{0x0626, 0x04},
{0x0627, 0xd0},
- {0x455e, 0x00},
- {0x471e, 0x4b},
- {0x4767, 0x0f},
- {0x4750, 0x14},
- {0x4540, 0x00},
- {0x47b4, 0x14},
- {0x4713, 0x30},
- {0x478b, 0x10},
- {0x478f, 0x10},
- {0x4793, 0x10},
- {0x4797, 0x0e},
- {0x479b, 0x0e},
- {0x0162, 0x0d},
- {0x0163, 0x78},
};
static const struct imx219_reg mode_640_480_regs[] = {
- {0x0100, 0x00},
- {0x30eb, 0x05},
- {0x30eb, 0x0c},
- {0x300a, 0xff},
- {0x300b, 0xff},
- {0x30eb, 0x05},
- {0x30eb, 0x09},
- {0x0114, 0x01},
- {0x0128, 0x00},
- {0x012a, 0x18},
- {0x012b, 0x00},
- {0x0162, 0x0d},
- {0x0163, 0x78},
{0x0164, 0x03},
{0x0165, 0xe8},
{0x0166, 0x08},
@@ -347,35 +284,10 @@ static const struct imx219_reg mode_640_480_regs[] = {
{0x016d, 0x80},
{0x016e, 0x01},
{0x016f, 0xe0},
- {0x0170, 0x01},
- {0x0171, 0x01},
- {0x0174, 0x03},
- {0x0175, 0x03},
- {0x0301, 0x05},
- {0x0303, 0x01},
- {0x0304, 0x03},
- {0x0305, 0x03},
- {0x0306, 0x00},
- {0x0307, 0x39},
- {0x030b, 0x01},
- {0x030c, 0x00},
- {0x030d, 0x72},
{0x0624, 0x06},
{0x0625, 0x68},
{0x0626, 0x04},
{0x0627, 0xd0},
- {0x455e, 0x00},
- {0x471e, 0x4b},
- {0x4767, 0x0f},
- {0x4750, 0x14},
- {0x4540, 0x00},
- {0x47b4, 0x14},
- {0x4713, 0x30},
- {0x478b, 0x10},
- {0x478f, 0x10},
- {0x4793, 0x10},
- {0x4797, 0x0e},
- {0x479b, 0x0e},
};
static const struct imx219_reg raw8_framefmt_regs[] = {
@@ -394,6 +306,10 @@ static const s64 imx219_link_freq_menu[] = {
IMX219_DEFAULT_LINK_FREQ,
};
+static const s64 imx219_link_freq_4lane_menu[] = {
+ IMX219_DEFAULT_LINK_FREQ_4LANE,
+};
+
static const char * const imx219_test_pattern_menu[] = {
"Disabled",
"Color Bars",
@@ -485,6 +401,7 @@ static const struct imx219_mode supported_modes[] = {
.num_of_regs = ARRAY_SIZE(mode_3280x2464_regs),
.regs = mode_3280x2464_regs,
},
+ .binning = false,
},
{
/* 1080P 30fps cropped */
@@ -501,6 +418,7 @@ static const struct imx219_mode supported_modes[] = {
.num_of_regs = ARRAY_SIZE(mode_1920_1080_regs),
.regs = mode_1920_1080_regs,
},
+ .binning = false,
},
{
/* 2x2 binned 30fps mode */
@@ -517,6 +435,7 @@ static const struct imx219_mode supported_modes[] = {
.num_of_regs = ARRAY_SIZE(mode_1640_1232_regs),
.regs = mode_1640_1232_regs,
},
+ .binning = true,
},
{
/* 640x480 30fps mode */
@@ -533,6 +452,7 @@ static const struct imx219_mode supported_modes[] = {
.num_of_regs = ARRAY_SIZE(mode_640_480_regs),
.regs = mode_640_480_regs,
},
+ .binning = true,
},
};
@@ -569,6 +489,9 @@ struct imx219 {
/* Streaming on/off */
bool streaming;
+
+ /* Two or Four lanes */
+ u8 lanes;
};
static inline struct imx219 *to_imx219(struct v4l2_subdev *_sd)
@@ -979,6 +902,35 @@ static int imx219_set_framefmt(struct imx219 *imx219)
return -EINVAL;
}
+static int imx219_set_binning(struct imx219 *imx219)
+{
+ if (!imx219->mode->binning) {
+ return imx219_write_reg(imx219, IMX219_REG_BINNING_MODE,
+ IMX219_REG_VALUE_16BIT,
+ IMX219_BINNING_NONE);
+ }
+
+ switch (imx219->fmt.code) {
+ case MEDIA_BUS_FMT_SRGGB8_1X8:
+ case MEDIA_BUS_FMT_SGRBG8_1X8:
+ case MEDIA_BUS_FMT_SGBRG8_1X8:
+ case MEDIA_BUS_FMT_SBGGR8_1X8:
+ return imx219_write_reg(imx219, IMX219_REG_BINNING_MODE,
+ IMX219_REG_VALUE_16BIT,
+ IMX219_BINNING_2X2_ANALOG);
+
+ case MEDIA_BUS_FMT_SRGGB10_1X10:
+ case MEDIA_BUS_FMT_SGRBG10_1X10:
+ case MEDIA_BUS_FMT_SGBRG10_1X10:
+ case MEDIA_BUS_FMT_SBGGR10_1X10:
+ return imx219_write_reg(imx219, IMX219_REG_BINNING_MODE,
+ IMX219_REG_VALUE_16BIT,
+ IMX219_BINNING_2X2);
+ }
+
+ return -EINVAL;
+}
+
static const struct v4l2_rect *
__imx219_get_pad_crop(struct imx219 *imx219,
struct v4l2_subdev_state *sd_state,
@@ -1031,6 +983,13 @@ static int imx219_get_selection(struct v4l2_subdev *sd,
return -EINVAL;
}
+static int imx219_configure_lanes(struct imx219 *imx219)
+{
+ return imx219_write_reg(imx219, IMX219_REG_CSI_LANE_MODE,
+ IMX219_REG_VALUE_08BIT, (imx219->lanes == 2) ?
+ IMX219_CSI_2_LANE_MODE : IMX219_CSI_4_LANE_MODE);
+};
+
static int imx219_start_streaming(struct imx219 *imx219)
{
struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd);
@@ -1041,6 +1000,20 @@ static int imx219_start_streaming(struct imx219 *imx219)
if (ret < 0)
return ret;
+ /* Send all registers that are common to all modes */
+ ret = imx219_write_regs(imx219, imx219_common_regs, ARRAY_SIZE(imx219_common_regs));
+ if (ret) {
+ dev_err(&client->dev, "%s failed to send mfg header\n", __func__);
+ goto err_rpm_put;
+ }
+
+ /* Configure two or four Lane mode */
+ ret = imx219_configure_lanes(imx219);
+ if (ret) {
+ dev_err(&client->dev, "%s failed to configure lanes\n", __func__);
+ goto err_rpm_put;
+ }
+
/* Apply default values of current mode */
reg_list = &imx219->mode->reg_list;
ret = imx219_write_regs(imx219, reg_list->regs, reg_list->num_of_regs);
@@ -1056,6 +1029,13 @@ static int imx219_start_streaming(struct imx219 *imx219)
goto err_rpm_put;
}
+ ret = imx219_set_binning(imx219);
+ if (ret) {
+ dev_err(&client->dev, "%s failed to set binning: %d\n",
+ __func__, ret);
+ goto err_rpm_put;
+ }
+
/* Apply customized values from user */
ret = __v4l2_ctrl_handler_setup(imx219->sd.ctrl_handler);
if (ret)
@@ -1272,6 +1252,11 @@ static const struct v4l2_subdev_internal_ops imx219_internal_ops = {
.open = imx219_open,
};
+static unsigned long imx219_get_pixel_rate(struct imx219 *imx219)
+{
+ return (imx219->lanes == 2) ? IMX219_PIXEL_RATE : IMX219_PIXEL_RATE_4LANE;
+}
+
/* Initialize control handlers */
static int imx219_init_controls(struct imx219 *imx219)
{
@@ -1293,15 +1278,16 @@ static int imx219_init_controls(struct imx219 *imx219)
/* By default, PIXEL_RATE is read only */
imx219->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops,
V4L2_CID_PIXEL_RATE,
- IMX219_PIXEL_RATE,
- IMX219_PIXEL_RATE, 1,
- IMX219_PIXEL_RATE);
+ imx219_get_pixel_rate(imx219),
+ imx219_get_pixel_rate(imx219), 1,
+ imx219_get_pixel_rate(imx219));
imx219->link_freq =
v4l2_ctrl_new_int_menu(ctrl_hdlr, &imx219_ctrl_ops,
V4L2_CID_LINK_FREQ,
ARRAY_SIZE(imx219_link_freq_menu) - 1, 0,
- imx219_link_freq_menu);
+ (imx219->lanes == 2) ? imx219_link_freq_menu :
+ imx219_link_freq_4lane_menu);
if (imx219->link_freq)
imx219->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
@@ -1396,7 +1382,7 @@ static void imx219_free_controls(struct imx219 *imx219)
mutex_destroy(&imx219->mutex);
}
-static int imx219_check_hwcfg(struct device *dev)
+static int imx219_check_hwcfg(struct device *dev, struct imx219 *imx219)
{
struct fwnode_handle *endpoint;
struct v4l2_fwnode_endpoint ep_cfg = {
@@ -1416,10 +1402,12 @@ static int imx219_check_hwcfg(struct device *dev)
}
/* Check the number of MIPI CSI2 data lanes */
- if (ep_cfg.bus.mipi_csi2.num_data_lanes != 2) {
- dev_err(dev, "only 2 data lanes are currently supported\n");
+ if (ep_cfg.bus.mipi_csi2.num_data_lanes != 2 &&
+ ep_cfg.bus.mipi_csi2.num_data_lanes != 4) {
+ dev_err(dev, "only 2 or 4 data lanes are currently supported\n");
goto error_out;
}
+ imx219->lanes = ep_cfg.bus.mipi_csi2.num_data_lanes;
/* Check the link frequency set in device tree */
if (!ep_cfg.nr_of_link_frequencies) {
@@ -1428,7 +1416,8 @@ static int imx219_check_hwcfg(struct device *dev)
}
if (ep_cfg.nr_of_link_frequencies != 1 ||
- ep_cfg.link_frequencies[0] != IMX219_DEFAULT_LINK_FREQ) {
+ (ep_cfg.link_frequencies[0] != ((imx219->lanes == 2) ?
+ IMX219_DEFAULT_LINK_FREQ : IMX219_DEFAULT_LINK_FREQ_4LANE))) {
dev_err(dev, "Link frequency not supported: %lld\n",
ep_cfg.link_frequencies[0]);
goto error_out;
@@ -1456,7 +1445,7 @@ static int imx219_probe(struct i2c_client *client)
v4l2_i2c_subdev_init(&imx219->sd, client, &imx219_subdev_ops);
/* Check the hardware configuration in device tree */
- if (imx219_check_hwcfg(dev))
+ if (imx219_check_hwcfg(dev, imx219))
return -EINVAL;
/* Get system clock (xclk) */
diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
index 218ded13fd80..49d6c8bdec41 100644
--- a/drivers/media/i2c/imx290.c
+++ b/drivers/media/i2c/imx290.c
@@ -152,13 +152,10 @@
#define IMX290_PIXEL_ARRAY_RECORDING_WIDTH 1920
#define IMX290_PIXEL_ARRAY_RECORDING_HEIGHT 1080
-static const char * const imx290_supply_name[] = {
- "vdda",
- "vddd",
- "vdddo",
-};
+/* Equivalent value for 16bpp */
+#define IMX290_BLACK_LEVEL_DEFAULT 3840
-#define IMX290_NUM_SUPPLIES ARRAY_SIZE(imx290_supply_name)
+#define IMX290_NUM_SUPPLIES 3
struct imx290_regval {
u32 reg;
@@ -180,11 +177,10 @@ struct imx290 {
struct clk *xclk;
struct regmap *regmap;
u8 nlanes;
- u8 bpp;
struct v4l2_subdev sd;
struct media_pad pad;
- struct v4l2_mbus_framefmt current_format;
+
const struct imx290_mode *current_mode;
struct regulator_bulk_data supplies[IMX290_NUM_SUPPLIES];
@@ -195,35 +191,16 @@ struct imx290 {
struct v4l2_ctrl *pixel_rate;
struct v4l2_ctrl *hblank;
struct v4l2_ctrl *vblank;
-
- struct mutex lock;
-};
-
-struct imx290_pixfmt {
- u32 code;
- u8 bpp;
-};
-
-static const struct imx290_pixfmt imx290_formats[] = {
- { MEDIA_BUS_FMT_SRGGB10_1X10, 10 },
- { MEDIA_BUS_FMT_SRGGB12_1X12, 12 },
};
-static const struct regmap_config imx290_regmap_config = {
- .reg_bits = 16,
- .val_bits = 8,
-};
+static inline struct imx290 *to_imx290(struct v4l2_subdev *_sd)
+{
+ return container_of(_sd, struct imx290, sd);
+}
-static const char * const imx290_test_pattern_menu[] = {
- "Disabled",
- "Sequence Pattern 1",
- "Horizontal Color-bar Chart",
- "Vertical Color-bar Chart",
- "Sequence Pattern 2",
- "Gradation Pattern 1",
- "Gradation Pattern 2",
- "000/555h Toggle Pattern",
-};
+/* -----------------------------------------------------------------------------
+ * Modes and formats
+ */
static const struct imx290_regval imx290_global_init_settings[] = {
{ IMX290_CTRL_07, IMX290_WINMODE_1080P },
@@ -338,7 +315,6 @@ static const struct imx290_regval imx290_10bit_settings[] = {
{ IMX290_ADBIT2, IMX290_ADBIT2_10BIT },
{ IMX290_ADBIT3, IMX290_ADBIT3_10BIT },
{ IMX290_CSI_DT_FMT, IMX290_CSI_DT_FMT_RAW10 },
- { IMX290_BLKLEVEL, 60 },
};
static const struct imx290_regval imx290_12bit_settings[] = {
@@ -348,7 +324,6 @@ static const struct imx290_regval imx290_12bit_settings[] = {
{ IMX290_ADBIT2, IMX290_ADBIT2_12BIT },
{ IMX290_ADBIT3, IMX290_ADBIT3_12BIT },
{ IMX290_CSI_DT_FMT, IMX290_CSI_DT_FMT_RAW12 },
- { IMX290_BLKLEVEL, 240 },
};
/* supported link frequencies */
@@ -438,11 +413,45 @@ static inline int imx290_modes_num(const struct imx290 *imx290)
return ARRAY_SIZE(imx290_modes_4lanes);
}
-static inline struct imx290 *to_imx290(struct v4l2_subdev *_sd)
+struct imx290_format_info {
+ u32 code;
+ u8 bpp;
+ const struct imx290_regval *regs;
+ unsigned int num_regs;
+};
+
+static const struct imx290_format_info imx290_formats[] = {
+ {
+ .code = MEDIA_BUS_FMT_SRGGB10_1X10,
+ .bpp = 10,
+ .regs = imx290_10bit_settings,
+ .num_regs = ARRAY_SIZE(imx290_10bit_settings),
+ }, {
+ .code = MEDIA_BUS_FMT_SRGGB12_1X12,
+ .bpp = 12,
+ .regs = imx290_12bit_settings,
+ .num_regs = ARRAY_SIZE(imx290_12bit_settings),
+ }
+};
+
+static const struct imx290_format_info *imx290_format_info(u32 code)
{
- return container_of(_sd, struct imx290, sd);
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(imx290_formats); ++i) {
+ const struct imx290_format_info *info = &imx290_formats[i];
+
+ if (info->code == code)
+ return info;
+ }
+
+ return NULL;
}
+/* -----------------------------------------------------------------------------
+ * Register access
+ */
+
static int __always_unused imx290_read(struct imx290 *imx290, u32 addr, u32 *value)
{
u8 data[3] = { 0, 0, 0 };
@@ -501,28 +510,82 @@ static int imx290_set_register_array(struct imx290 *imx290,
return 0;
}
-/* Stop streaming */
-static int imx290_stop_streaming(struct imx290 *imx290)
+static int imx290_set_data_lanes(struct imx290 *imx290)
{
int ret = 0;
+ u32 frsel;
- imx290_write(imx290, IMX290_STANDBY, 0x01, &ret);
+ switch (imx290->nlanes) {
+ case 2:
+ default:
+ frsel = 0x02;
+ break;
+ case 4:
+ frsel = 0x01;
+ break;
+ }
- msleep(30);
+ imx290_write(imx290, IMX290_PHY_LANE_NUM, imx290->nlanes - 1, &ret);
+ imx290_write(imx290, IMX290_CSI_LANE_MODE, imx290->nlanes - 1, &ret);
+ imx290_write(imx290, IMX290_FR_FDG_SEL, frsel, &ret);
- return imx290_write(imx290, IMX290_XMSTA, 0x01, &ret);
+ return ret;
+}
+
+static int imx290_set_black_level(struct imx290 *imx290,
+ const struct v4l2_mbus_framefmt *format,
+ unsigned int black_level, int *err)
+{
+ unsigned int bpp = imx290_format_info(format->code)->bpp;
+
+ return imx290_write(imx290, IMX290_BLKLEVEL,
+ black_level >> (16 - bpp), err);
+}
+
+static int imx290_setup_format(struct imx290 *imx290,
+ const struct v4l2_mbus_framefmt *format)
+{
+ const struct imx290_format_info *info;
+ int ret;
+
+ info = imx290_format_info(format->code);
+
+ ret = imx290_set_register_array(imx290, info->regs, info->num_regs);
+ if (ret < 0) {
+ dev_err(imx290->dev, "Could not set format registers\n");
+ return ret;
+ }
+
+ return imx290_set_black_level(imx290, format,
+ IMX290_BLACK_LEVEL_DEFAULT, &ret);
}
+/* ----------------------------------------------------------------------------
+ * Controls
+ */
+
static int imx290_set_ctrl(struct v4l2_ctrl *ctrl)
{
struct imx290 *imx290 = container_of(ctrl->handler,
struct imx290, ctrls);
+ const struct v4l2_mbus_framefmt *format;
+ struct v4l2_subdev_state *state;
int ret = 0;
+ /*
+ * Return immediately for controls that don't need to be applied to the
+ * device.
+ */
+ if (ctrl->flags & V4L2_CTRL_FLAG_READ_ONLY)
+ return 0;
+
/* V4L2 controls values will be applied only when power is already up */
if (!pm_runtime_get_if_in_use(imx290->dev))
return 0;
+ state = v4l2_subdev_get_locked_active_state(&imx290->sd);
+ format = v4l2_subdev_get_pad_format(&imx290->sd, state, 0);
+
switch (ctrl->id) {
case V4L2_CID_ANALOGUE_GAIN:
ret = imx290_write(imx290, IMX290_GAIN, ctrl->val, NULL);
@@ -535,7 +598,7 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl)
case V4L2_CID_TEST_PATTERN:
if (ctrl->val) {
- imx290_write(imx290, IMX290_BLKLEVEL, 0, &ret);
+ imx290_set_black_level(imx290, format, 0, &ret);
usleep_range(10000, 11000);
imx290_write(imx290, IMX290_PGCTRL,
(u8)(IMX290_PGCTRL_REGEN |
@@ -544,20 +607,18 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl)
} else {
imx290_write(imx290, IMX290_PGCTRL, 0x00, &ret);
usleep_range(10000, 11000);
- if (imx290->bpp == 10)
- imx290_write(imx290, IMX290_BLKLEVEL, 0x3c,
- &ret);
- else /* 12 bits per pixel */
- imx290_write(imx290, IMX290_BLKLEVEL, 0xf0,
- &ret);
+ imx290_set_black_level(imx290, format,
+ IMX290_BLACK_LEVEL_DEFAULT, &ret);
}
break;
+
default:
ret = -EINVAL;
break;
}
- pm_runtime_put(imx290->dev);
+ pm_runtime_mark_last_busy(imx290->dev);
+ pm_runtime_put_autosuspend(imx290->dev);
return ret;
}
@@ -566,14 +627,217 @@ static const struct v4l2_ctrl_ops imx290_ctrl_ops = {
.s_ctrl = imx290_set_ctrl,
};
-static struct v4l2_mbus_framefmt *
-imx290_get_pad_format(struct imx290 *imx290, struct v4l2_subdev_state *state,
- u32 which)
+static const char * const imx290_test_pattern_menu[] = {
+ "Disabled",
+ "Sequence Pattern 1",
+ "Horizontal Color-bar Chart",
+ "Vertical Color-bar Chart",
+ "Sequence Pattern 2",
+ "Gradation Pattern 1",
+ "Gradation Pattern 2",
+ "000/555h Toggle Pattern",
+};
+
+static void imx290_ctrl_update(struct imx290 *imx290,
+ const struct v4l2_mbus_framefmt *format,
+ const struct imx290_mode *mode)
{
- if (which == V4L2_SUBDEV_FORMAT_ACTIVE)
- return &imx290->current_format;
- else
- return v4l2_subdev_get_try_format(&imx290->sd, state, 0);
+ unsigned int hblank = mode->hmax - mode->width;
+ unsigned int vblank = IMX290_VMAX_DEFAULT - mode->height;
+ s64 link_freq = imx290_link_freqs_ptr(imx290)[mode->link_freq_index];
+ u64 pixel_rate;
+
+ /* pixel rate = link_freq * 2 * nr_of_lanes / bits_per_sample */
+ pixel_rate = link_freq * 2 * imx290->nlanes;
+ do_div(pixel_rate, imx290_format_info(format->code)->bpp);
+
+ __v4l2_ctrl_s_ctrl(imx290->link_freq, mode->link_freq_index);
+ __v4l2_ctrl_s_ctrl_int64(imx290->pixel_rate, pixel_rate);
+
+ __v4l2_ctrl_modify_range(imx290->hblank, hblank, hblank, 1, hblank);
+ __v4l2_ctrl_modify_range(imx290->vblank, vblank, vblank, 1, vblank);
+}
+
+static int imx290_ctrl_init(struct imx290 *imx290)
+{
+ struct v4l2_fwnode_device_properties props;
+ int ret;
+
+ ret = v4l2_fwnode_device_parse(imx290->dev, &props);
+ if (ret < 0)
+ return ret;
+
+ v4l2_ctrl_handler_init(&imx290->ctrls, 9);
+
+ /*
+ * The sensor has an analog gain and a digital gain, both controlled
+ * through a single gain value, expressed in 0.3dB increments. Values
+ * from 0.0dB (0) to 30.0dB (100) apply analog gain only, higher values
+ * up to 72.0dB (240) add further digital gain. Limit the range to
+ * analog gain only, support for digital gain can be added separately
+ * if needed.
+ *
+ * The IMX327 and IMX462 are largely compatible with the IMX290, but
+ * have an analog gain range of 0.0dB to 29.4dB and 42dB of digital
+ * gain. When support for those sensors gets added to the driver, the
+ * gain control should be adjusted accordingly.
+ */
+ v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
+ V4L2_CID_ANALOGUE_GAIN, 0, 100, 1, 0);
+
+ v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
+ V4L2_CID_EXPOSURE, 1, IMX290_VMAX_DEFAULT - 2, 1,
+ IMX290_VMAX_DEFAULT - 2);
+
+ /*
+ * Set the link frequency, pixel rate, horizontal blanking and vertical
+ * blanking to hardcoded values, they will be updated by
+ * imx290_ctrl_update().
+ */
+ imx290->link_freq =
+ v4l2_ctrl_new_int_menu(&imx290->ctrls, &imx290_ctrl_ops,
+ V4L2_CID_LINK_FREQ,
+ imx290_link_freqs_num(imx290) - 1, 0,
+ imx290_link_freqs_ptr(imx290));
+ if (imx290->link_freq)
+ imx290->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+ imx290->pixel_rate = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
+ V4L2_CID_PIXEL_RATE,
+ 1, INT_MAX, 1, 1);
+
+ v4l2_ctrl_new_std_menu_items(&imx290->ctrls, &imx290_ctrl_ops,
+ V4L2_CID_TEST_PATTERN,
+ ARRAY_SIZE(imx290_test_pattern_menu) - 1,
+ 0, 0, imx290_test_pattern_menu);
+
+ imx290->hblank = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
+ V4L2_CID_HBLANK, 1, 1, 1, 1);
+ if (imx290->hblank)
+ imx290->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+ imx290->vblank = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
+ V4L2_CID_VBLANK, 1, 1, 1, 1);
+ if (imx290->vblank)
+ imx290->vblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+ v4l2_ctrl_new_fwnode_properties(&imx290->ctrls, &imx290_ctrl_ops,
+ &props);
+
+ imx290->sd.ctrl_handler = &imx290->ctrls;
+
+ if (imx290->ctrls.error) {
+ ret = imx290->ctrls.error;
+ v4l2_ctrl_handler_free(&imx290->ctrls);
+ return ret;
+ }
+
+ return 0;
+}
+
+/* ----------------------------------------------------------------------------
+ * Subdev operations
+ */
+
+/* Start streaming */
+static int imx290_start_streaming(struct imx290 *imx290,
+ struct v4l2_subdev_state *state)
+{
+ const struct v4l2_mbus_framefmt *format;
+ int ret;
+
+ /* Set init register settings */
+ ret = imx290_set_register_array(imx290, imx290_global_init_settings,
+ ARRAY_SIZE(
+ imx290_global_init_settings));
+ if (ret < 0) {
+ dev_err(imx290->dev, "Could not set init registers\n");
+ return ret;
+ }
+
+ /* Set data lane count */
+ ret = imx290_set_data_lanes(imx290);
+ if (ret < 0) {
+ dev_err(imx290->dev, "Could not set data lanes\n");
+ return ret;
+ }
+
+ /* Apply the register values related to current frame format */
+ format = v4l2_subdev_get_pad_format(&imx290->sd, state, 0);
+ ret = imx290_setup_format(imx290, format);
+ if (ret < 0) {
+ dev_err(imx290->dev, "Could not set frame format\n");
+ return ret;
+ }
+
+ /* Apply default values of current mode */
+ ret = imx290_set_register_array(imx290, imx290->current_mode->data,
+ imx290->current_mode->data_size);
+ if (ret < 0) {
+ dev_err(imx290->dev, "Could not set current mode\n");
+ return ret;
+ }
+
+ ret = imx290_write(imx290, IMX290_HMAX, imx290->current_mode->hmax,
+ NULL);
+ if (ret)
+ return ret;
+
+ /* Apply customized values from user */
+ ret = __v4l2_ctrl_handler_setup(imx290->sd.ctrl_handler);
+ if (ret) {
+ dev_err(imx290->dev, "Could not sync v4l2 controls\n");
+ return ret;
+ }
+
+ imx290_write(imx290, IMX290_STANDBY, 0x00, &ret);
+
+ msleep(30);
+
+ /* Start streaming */
+ return imx290_write(imx290, IMX290_XMSTA, 0x00, &ret);
+}
+
+/* Stop streaming */
+static int imx290_stop_streaming(struct imx290 *imx290)
+{
+ int ret = 0;
+
+ imx290_write(imx290, IMX290_STANDBY, 0x01, &ret);
+
+ msleep(30);
+
+ return imx290_write(imx290, IMX290_XMSTA, 0x01, &ret);
+}
+
+static int imx290_set_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct imx290 *imx290 = to_imx290(sd);
+ struct v4l2_subdev_state *state;
+ int ret = 0;
+
+ state = v4l2_subdev_lock_and_get_active_state(sd);
+
+ if (enable) {
+ ret = pm_runtime_resume_and_get(imx290->dev);
+ if (ret < 0)
+ goto unlock;
+
+ ret = imx290_start_streaming(imx290, state);
+ if (ret) {
+ dev_err(imx290->dev, "Start stream failed\n");
+ pm_runtime_put_sync(imx290->dev);
+ goto unlock;
+ }
+ } else {
+ imx290_stop_streaming(imx290);
+ pm_runtime_mark_last_busy(imx290->dev);
+ pm_runtime_put_autosuspend(imx290->dev);
+ }
+
+unlock:
+ v4l2_subdev_unlock_state(state);
+ return ret;
}
static int imx290_enum_mbus_code(struct v4l2_subdev *sd,
@@ -595,8 +859,7 @@ static int imx290_enum_frame_size(struct v4l2_subdev *sd,
const struct imx290 *imx290 = to_imx290(sd);
const struct imx290_mode *imx290_modes = imx290_modes_ptr(imx290);
- if ((fse->code != imx290_formats[0].code) &&
- (fse->code != imx290_formats[1].code))
+ if (!imx290_format_info(fse->code))
return -EINVAL;
if (fse->index >= imx290_modes_num(imx290))
@@ -610,47 +873,6 @@ static int imx290_enum_frame_size(struct v4l2_subdev *sd,
return 0;
}
-static int imx290_get_fmt(struct v4l2_subdev *sd,
- struct v4l2_subdev_state *sd_state,
- struct v4l2_subdev_format *fmt)
-{
- struct imx290 *imx290 = to_imx290(sd);
- struct v4l2_mbus_framefmt *framefmt;
-
- mutex_lock(&imx290->lock);
-
- framefmt = imx290_get_pad_format(imx290, sd_state, fmt->which);
- fmt->format = *framefmt;
-
- mutex_unlock(&imx290->lock);
-
- return 0;
-}
-
-static inline u8 imx290_get_link_freq_index(struct imx290 *imx290)
-{
- return imx290->current_mode->link_freq_index;
-}
-
-static s64 imx290_get_link_freq(struct imx290 *imx290)
-{
- u8 index = imx290_get_link_freq_index(imx290);
-
- return *(imx290_link_freqs_ptr(imx290) + index);
-}
-
-static u64 imx290_calc_pixel_rate(struct imx290 *imx290)
-{
- s64 link_freq = imx290_get_link_freq(imx290);
- u8 nlanes = imx290->nlanes;
- u64 pixel_rate;
-
- /* pixel rate = link_freq * 2 * nr_of_lanes / bits_per_sample */
- pixel_rate = link_freq * 2 * nlanes;
- do_div(pixel_rate, imx290->bpp);
- return pixel_rate;
-}
-
static int imx290_set_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
@@ -658,9 +880,6 @@ static int imx290_set_fmt(struct v4l2_subdev *sd,
struct imx290 *imx290 = to_imx290(sd);
const struct imx290_mode *mode;
struct v4l2_mbus_framefmt *format;
- unsigned int i;
-
- mutex_lock(&imx290->lock);
mode = v4l2_find_nearest_size(imx290_modes_ptr(imx290),
imx290_modes_num(imx290), width, height,
@@ -669,48 +888,21 @@ static int imx290_set_fmt(struct v4l2_subdev *sd,
fmt->format.width = mode->width;
fmt->format.height = mode->height;
- for (i = 0; i < ARRAY_SIZE(imx290_formats); i++)
- if (imx290_formats[i].code == fmt->format.code)
- break;
-
- if (i >= ARRAY_SIZE(imx290_formats))
- i = 0;
+ if (!imx290_format_info(fmt->format.code))
+ fmt->format.code = imx290_formats[0].code;
- fmt->format.code = imx290_formats[i].code;
fmt->format.field = V4L2_FIELD_NONE;
- format = imx290_get_pad_format(imx290, sd_state, fmt->which);
+ format = v4l2_subdev_get_pad_format(sd, sd_state, 0);
if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
imx290->current_mode = mode;
- imx290->bpp = imx290_formats[i].bpp;
-
- if (imx290->link_freq)
- __v4l2_ctrl_s_ctrl(imx290->link_freq,
- imx290_get_link_freq_index(imx290));
- if (imx290->pixel_rate)
- __v4l2_ctrl_s_ctrl_int64(imx290->pixel_rate,
- imx290_calc_pixel_rate(imx290));
-
- if (imx290->hblank) {
- unsigned int hblank = mode->hmax - mode->width;
-
- __v4l2_ctrl_modify_range(imx290->hblank, hblank, hblank,
- 1, hblank);
- }
-
- if (imx290->vblank) {
- unsigned int vblank = IMX290_VMAX_DEFAULT - mode->height;
- __v4l2_ctrl_modify_range(imx290->vblank, vblank, vblank,
- 1, vblank);
- }
+ imx290_ctrl_update(imx290, &fmt->format, mode);
}
*format = fmt->format;
- mutex_unlock(&imx290->lock);
-
return 0;
}
@@ -718,14 +910,11 @@ static int imx290_get_selection(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
- struct imx290 *imx290 = to_imx290(sd);
struct v4l2_mbus_framefmt *format;
switch (sel->target) {
case V4L2_SEL_TGT_CROP: {
- format = imx290_get_pad_format(imx290, sd_state, sel->which);
-
- mutex_lock(&imx290->lock);
+ format = v4l2_subdev_get_pad_format(sd, sd_state, 0);
sel->r.top = IMX920_PIXEL_ARRAY_MARGIN_TOP
+ (IMX290_PIXEL_ARRAY_RECORDING_HEIGHT - format->height) / 2;
@@ -734,7 +923,6 @@ static int imx290_get_selection(struct v4l2_subdev *sd,
sel->r.width = format->width;
sel->r.height = format->height;
- mutex_unlock(&imx290->lock);
return 0;
}
@@ -763,179 +951,116 @@ static int imx290_get_selection(struct v4l2_subdev *sd,
static int imx290_entity_init_cfg(struct v4l2_subdev *subdev,
struct v4l2_subdev_state *sd_state)
{
- struct v4l2_subdev_format fmt = { 0 };
-
- fmt.which = sd_state ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE;
- fmt.format.width = 1920;
- fmt.format.height = 1080;
+ struct v4l2_subdev_format fmt = {
+ .which = V4L2_SUBDEV_FORMAT_TRY,
+ .format = {
+ .width = 1920,
+ .height = 1080,
+ },
+ };
imx290_set_fmt(subdev, sd_state, &fmt);
return 0;
}
-static int imx290_write_current_format(struct imx290 *imx290)
-{
- int ret;
+static const struct v4l2_subdev_video_ops imx290_video_ops = {
+ .s_stream = imx290_set_stream,
+};
- switch (imx290->current_format.code) {
- case MEDIA_BUS_FMT_SRGGB10_1X10:
- ret = imx290_set_register_array(imx290, imx290_10bit_settings,
- ARRAY_SIZE(
- imx290_10bit_settings));
- if (ret < 0) {
- dev_err(imx290->dev, "Could not set format registers\n");
- return ret;
- }
- break;
- case MEDIA_BUS_FMT_SRGGB12_1X12:
- ret = imx290_set_register_array(imx290, imx290_12bit_settings,
- ARRAY_SIZE(
- imx290_12bit_settings));
- if (ret < 0) {
- dev_err(imx290->dev, "Could not set format registers\n");
- return ret;
- }
- break;
- default:
- dev_err(imx290->dev, "Unknown pixel format\n");
- return -EINVAL;
- }
+static const struct v4l2_subdev_pad_ops imx290_pad_ops = {
+ .init_cfg = imx290_entity_init_cfg,
+ .enum_mbus_code = imx290_enum_mbus_code,
+ .enum_frame_size = imx290_enum_frame_size,
+ .get_fmt = v4l2_subdev_get_fmt,
+ .set_fmt = imx290_set_fmt,
+ .get_selection = imx290_get_selection,
+};
- return 0;
-}
+static const struct v4l2_subdev_ops imx290_subdev_ops = {
+ .video = &imx290_video_ops,
+ .pad = &imx290_pad_ops,
+};
-/* Start streaming */
-static int imx290_start_streaming(struct imx290 *imx290)
+static const struct media_entity_operations imx290_subdev_entity_ops = {
+ .link_validate = v4l2_subdev_link_validate,
+};
+
+static int imx290_subdev_init(struct imx290 *imx290)
{
+ struct i2c_client *client = to_i2c_client(imx290->dev);
+ const struct v4l2_mbus_framefmt *format;
+ struct v4l2_subdev_state *state;
int ret;
- /* Set init register settings */
- ret = imx290_set_register_array(imx290, imx290_global_init_settings,
- ARRAY_SIZE(
- imx290_global_init_settings));
- if (ret < 0) {
- dev_err(imx290->dev, "Could not set init registers\n");
- return ret;
- }
+ imx290->current_mode = &imx290_modes_ptr(imx290)[0];
- /* Apply the register values related to current frame format */
- ret = imx290_write_current_format(imx290);
+ v4l2_i2c_subdev_init(&imx290->sd, client, &imx290_subdev_ops);
+ imx290->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ imx290->sd.dev = imx290->dev;
+ imx290->sd.entity.ops = &imx290_subdev_entity_ops;
+ imx290->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
+
+ imx290->pad.flags = MEDIA_PAD_FL_SOURCE;
+ ret = media_entity_pads_init(&imx290->sd.entity, 1, &imx290->pad);
if (ret < 0) {
- dev_err(imx290->dev, "Could not set frame format\n");
+ dev_err(imx290->dev, "Could not register media entity\n");
return ret;
}
- /* Apply default values of current mode */
- ret = imx290_set_register_array(imx290, imx290->current_mode->data,
- imx290->current_mode->data_size);
+ ret = imx290_ctrl_init(imx290);
if (ret < 0) {
- dev_err(imx290->dev, "Could not set current mode\n");
- return ret;
+ dev_err(imx290->dev, "Control initialization error %d\n", ret);
+ goto err_media;
}
- ret = imx290_write(imx290, IMX290_HMAX, imx290->current_mode->hmax,
- NULL);
- if (ret)
- return ret;
+ imx290->sd.state_lock = imx290->ctrls.lock;
- /* Apply customized values from user */
- ret = v4l2_ctrl_handler_setup(imx290->sd.ctrl_handler);
- if (ret) {
- dev_err(imx290->dev, "Could not sync v4l2 controls\n");
- return ret;
+ ret = v4l2_subdev_init_finalize(&imx290->sd);
+ if (ret < 0) {
+ dev_err(imx290->dev, "subdev initialization error %d\n", ret);
+ goto err_ctrls;
}
- imx290_write(imx290, IMX290_STANDBY, 0x00, &ret);
-
- msleep(30);
-
- /* Start streaming */
- return imx290_write(imx290, IMX290_XMSTA, 0x00, &ret);
-}
-
-static int imx290_set_stream(struct v4l2_subdev *sd, int enable)
-{
- struct imx290 *imx290 = to_imx290(sd);
- int ret = 0;
+ state = v4l2_subdev_lock_and_get_active_state(&imx290->sd);
+ format = v4l2_subdev_get_pad_format(&imx290->sd, state, 0);
+ imx290_ctrl_update(imx290, format, imx290->current_mode);
+ v4l2_subdev_unlock_state(state);
- if (enable) {
- ret = pm_runtime_resume_and_get(imx290->dev);
- if (ret < 0)
- goto unlock_and_return;
-
- ret = imx290_start_streaming(imx290);
- if (ret) {
- dev_err(imx290->dev, "Start stream failed\n");
- pm_runtime_put(imx290->dev);
- goto unlock_and_return;
- }
- } else {
- imx290_stop_streaming(imx290);
- pm_runtime_put(imx290->dev);
- }
-
-unlock_and_return:
+ return 0;
+err_ctrls:
+ v4l2_ctrl_handler_free(&imx290->ctrls);
+err_media:
+ media_entity_cleanup(&imx290->sd.entity);
return ret;
}
-static int imx290_get_regulators(struct device *dev, struct imx290 *imx290)
+static void imx290_subdev_cleanup(struct imx290 *imx290)
{
- unsigned int i;
-
- for (i = 0; i < ARRAY_SIZE(imx290->supplies); i++)
- imx290->supplies[i].supply = imx290_supply_name[i];
-
- return devm_regulator_bulk_get(dev, ARRAY_SIZE(imx290->supplies),
- imx290->supplies);
+ v4l2_subdev_cleanup(&imx290->sd);
+ media_entity_cleanup(&imx290->sd.entity);
+ v4l2_ctrl_handler_free(&imx290->ctrls);
}
-static int imx290_set_data_lanes(struct imx290 *imx290)
-{
- int ret = 0, laneval, frsel;
-
- switch (imx290->nlanes) {
- case 2:
- laneval = 0x01;
- frsel = 0x02;
- break;
- case 4:
- laneval = 0x03;
- frsel = 0x01;
- break;
- default:
- /*
- * We should never hit this since the data lane count is
- * validated in probe itself
- */
- dev_err(imx290->dev, "Lane configuration not supported\n");
- return -EINVAL;
- }
-
- imx290_write(imx290, IMX290_PHY_LANE_NUM, laneval, &ret);
- imx290_write(imx290, IMX290_CSI_LANE_MODE, laneval, &ret);
- imx290_write(imx290, IMX290_FR_FDG_SEL, frsel, &ret);
-
- return ret;
-}
+/* ----------------------------------------------------------------------------
+ * Power management
+ */
-static int imx290_power_on(struct device *dev)
+static int imx290_power_on(struct imx290 *imx290)
{
- struct v4l2_subdev *sd = dev_get_drvdata(dev);
- struct imx290 *imx290 = to_imx290(sd);
int ret;
ret = clk_prepare_enable(imx290->xclk);
if (ret) {
- dev_err(dev, "Failed to enable clock\n");
+ dev_err(imx290->dev, "Failed to enable clock\n");
return ret;
}
ret = regulator_bulk_enable(ARRAY_SIZE(imx290->supplies),
imx290->supplies);
if (ret) {
- dev_err(dev, "Failed to enable regulators\n");
+ dev_err(imx290->dev, "Failed to enable regulators\n");
clk_disable_unprepare(imx290->xclk);
return ret;
}
@@ -944,123 +1069,86 @@ static int imx290_power_on(struct device *dev)
gpiod_set_value_cansleep(imx290->rst_gpio, 0);
usleep_range(30000, 31000);
- /* Set data lane count */
- imx290_set_data_lanes(imx290);
-
return 0;
}
-static int imx290_power_off(struct device *dev)
+static void imx290_power_off(struct imx290 *imx290)
{
- struct v4l2_subdev *sd = dev_get_drvdata(dev);
- struct imx290 *imx290 = to_imx290(sd);
-
clk_disable_unprepare(imx290->xclk);
gpiod_set_value_cansleep(imx290->rst_gpio, 1);
regulator_bulk_disable(ARRAY_SIZE(imx290->supplies), imx290->supplies);
+}
+
+static int imx290_runtime_resume(struct device *dev)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct imx290 *imx290 = to_imx290(sd);
+
+ return imx290_power_on(imx290);
+}
+
+static int imx290_runtime_suspend(struct device *dev)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct imx290 *imx290 = to_imx290(sd);
+
+ imx290_power_off(imx290);
return 0;
}
static const struct dev_pm_ops imx290_pm_ops = {
- SET_RUNTIME_PM_OPS(imx290_power_off, imx290_power_on, NULL)
-};
-
-static const struct v4l2_subdev_video_ops imx290_video_ops = {
- .s_stream = imx290_set_stream,
+ SET_RUNTIME_PM_OPS(imx290_runtime_suspend, imx290_runtime_resume, NULL)
};
-static const struct v4l2_subdev_pad_ops imx290_pad_ops = {
- .init_cfg = imx290_entity_init_cfg,
- .enum_mbus_code = imx290_enum_mbus_code,
- .enum_frame_size = imx290_enum_frame_size,
- .get_fmt = imx290_get_fmt,
- .set_fmt = imx290_set_fmt,
- .get_selection = imx290_get_selection,
-};
+/* ----------------------------------------------------------------------------
+ * Probe & remove
+ */
-static const struct v4l2_subdev_ops imx290_subdev_ops = {
- .video = &imx290_video_ops,
- .pad = &imx290_pad_ops,
+static const struct regmap_config imx290_regmap_config = {
+ .reg_bits = 16,
+ .val_bits = 8,
};
-static const struct media_entity_operations imx290_subdev_entity_ops = {
- .link_validate = v4l2_subdev_link_validate,
+static const char * const imx290_supply_name[IMX290_NUM_SUPPLIES] = {
+ "vdda",
+ "vddd",
+ "vdddo",
};
-static int imx290_ctrl_init(struct imx290 *imx290)
+static int imx290_get_regulators(struct device *dev, struct imx290 *imx290)
{
- struct v4l2_fwnode_device_properties props;
- unsigned int blank;
- int ret;
-
- ret = v4l2_fwnode_device_parse(imx290->dev, &props);
- if (ret < 0)
- return ret;
-
- v4l2_ctrl_handler_init(&imx290->ctrls, 9);
- imx290->ctrls.lock = &imx290->lock;
-
- /*
- * The sensor has an analog gain and a digital gain, both controlled
- * through a single gain value, expressed in 0.3dB increments. Values
- * from 0.0dB (0) to 30.0dB (100) apply analog gain only, higher values
- * up to 72.0dB (240) add further digital gain. Limit the range to
- * analog gain only, support for digital gain can be added separately
- * if needed.
- *
- * The IMX327 and IMX462 are largely compatible with the IMX290, but
- * have an analog gain range of 0.0dB to 29.4dB and 42dB of digital
- * gain. When support for those sensors gets added to the driver, the
- * gain control should be adjusted accordingly.
- */
- v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
- V4L2_CID_ANALOGUE_GAIN, 0, 100, 1, 0);
-
- v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
- V4L2_CID_EXPOSURE, 1, IMX290_VMAX_DEFAULT - 2, 1,
- IMX290_VMAX_DEFAULT - 2);
-
- imx290->link_freq =
- v4l2_ctrl_new_int_menu(&imx290->ctrls, &imx290_ctrl_ops,
- V4L2_CID_LINK_FREQ,
- imx290_link_freqs_num(imx290) - 1, 0,
- imx290_link_freqs_ptr(imx290));
- if (imx290->link_freq)
- imx290->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
-
- imx290->pixel_rate = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
- V4L2_CID_PIXEL_RATE,
- 1, INT_MAX, 1,
- imx290_calc_pixel_rate(imx290));
+ unsigned int i;
- v4l2_ctrl_new_std_menu_items(&imx290->ctrls, &imx290_ctrl_ops,
- V4L2_CID_TEST_PATTERN,
- ARRAY_SIZE(imx290_test_pattern_menu) - 1,
- 0, 0, imx290_test_pattern_menu);
+ for (i = 0; i < ARRAY_SIZE(imx290->supplies); i++)
+ imx290->supplies[i].supply = imx290_supply_name[i];
- blank = imx290->current_mode->hmax - imx290->current_mode->width;
- imx290->hblank = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
- V4L2_CID_HBLANK, blank, blank, 1,
- blank);
- if (imx290->hblank)
- imx290->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+ return devm_regulator_bulk_get(dev, ARRAY_SIZE(imx290->supplies),
+ imx290->supplies);
+}
- blank = IMX290_VMAX_DEFAULT - imx290->current_mode->height;
- imx290->vblank = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
- V4L2_CID_VBLANK, blank, blank, 1,
- blank);
- if (imx290->vblank)
- imx290->vblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+static int imx290_init_clk(struct imx290 *imx290)
+{
+ u32 xclk_freq;
+ int ret;
- v4l2_ctrl_new_fwnode_properties(&imx290->ctrls, &imx290_ctrl_ops,
- &props);
+ ret = fwnode_property_read_u32(dev_fwnode(imx290->dev),
+ "clock-frequency", &xclk_freq);
+ if (ret) {
+ dev_err(imx290->dev, "Could not get xclk frequency\n");
+ return ret;
+ }
- imx290->sd.ctrl_handler = &imx290->ctrls;
+ /* external clock must be 37.125 MHz */
+ if (xclk_freq != 37125000) {
+ dev_err(imx290->dev, "External clock frequency %u is not supported\n",
+ xclk_freq);
+ return -EINVAL;
+ }
- if (imx290->ctrls.error) {
- ret = imx290->ctrls.error;
- v4l2_ctrl_handler_free(&imx290->ctrls);
+ ret = clk_set_rate(imx290->xclk, xclk_freq);
+ if (ret) {
+ dev_err(imx290->dev, "Could not set xclk frequency\n");
return ret;
}
@@ -1089,171 +1177,159 @@ static s64 imx290_check_link_freqs(const struct imx290 *imx290,
return 0;
}
-static int imx290_probe(struct i2c_client *client)
+static int imx290_parse_dt(struct imx290 *imx290)
{
- struct device *dev = &client->dev;
- struct fwnode_handle *endpoint;
/* Only CSI2 is supported for now: */
struct v4l2_fwnode_endpoint ep = {
.bus_type = V4L2_MBUS_CSI2_DPHY
};
- struct imx290 *imx290;
- u32 xclk_freq;
- s64 fq;
+ struct fwnode_handle *endpoint;
int ret;
+ s64 fq;
- imx290 = devm_kzalloc(dev, sizeof(*imx290), GFP_KERNEL);
- if (!imx290)
- return -ENOMEM;
-
- imx290->dev = dev;
- imx290->regmap = devm_regmap_init_i2c(client, &imx290_regmap_config);
- if (IS_ERR(imx290->regmap)) {
- dev_err(dev, "Unable to initialize I2C\n");
- return -ENODEV;
- }
-
- endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL);
+ endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(imx290->dev), NULL);
if (!endpoint) {
- dev_err(dev, "Endpoint node not found\n");
+ dev_err(imx290->dev, "Endpoint node not found\n");
return -EINVAL;
}
ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &ep);
fwnode_handle_put(endpoint);
if (ret == -ENXIO) {
- dev_err(dev, "Unsupported bus type, should be CSI2\n");
- goto free_err;
+ dev_err(imx290->dev, "Unsupported bus type, should be CSI2\n");
+ goto done;
} else if (ret) {
- dev_err(dev, "Parsing endpoint node failed\n");
- goto free_err;
+ dev_err(imx290->dev, "Parsing endpoint node failed\n");
+ goto done;
}
/* Get number of data lanes */
imx290->nlanes = ep.bus.mipi_csi2.num_data_lanes;
if (imx290->nlanes != 2 && imx290->nlanes != 4) {
- dev_err(dev, "Invalid data lanes: %d\n", imx290->nlanes);
+ dev_err(imx290->dev, "Invalid data lanes: %d\n", imx290->nlanes);
ret = -EINVAL;
- goto free_err;
+ goto done;
}
- dev_dbg(dev, "Using %u data lanes\n", imx290->nlanes);
+ dev_dbg(imx290->dev, "Using %u data lanes\n", imx290->nlanes);
if (!ep.nr_of_link_frequencies) {
- dev_err(dev, "link-frequency property not found in DT\n");
+ dev_err(imx290->dev, "link-frequency property not found in DT\n");
ret = -EINVAL;
- goto free_err;
+ goto done;
}
/* Check that link frequences for all the modes are in device tree */
fq = imx290_check_link_freqs(imx290, &ep);
if (fq) {
- dev_err(dev, "Link frequency of %lld is not supported\n", fq);
+ dev_err(imx290->dev, "Link frequency of %lld is not supported\n",
+ fq);
ret = -EINVAL;
- goto free_err;
+ goto done;
}
- /* get system clock (xclk) */
- imx290->xclk = devm_clk_get(dev, "xclk");
- if (IS_ERR(imx290->xclk)) {
- dev_err(dev, "Could not get xclk");
- ret = PTR_ERR(imx290->xclk);
- goto free_err;
- }
+ ret = 0;
- ret = fwnode_property_read_u32(dev_fwnode(dev), "clock-frequency",
- &xclk_freq);
- if (ret) {
- dev_err(dev, "Could not get xclk frequency\n");
- goto free_err;
- }
+done:
+ v4l2_fwnode_endpoint_free(&ep);
+ return ret;
+}
- /* external clock must be 37.125 MHz */
- if (xclk_freq != 37125000) {
- dev_err(dev, "External clock frequency %u is not supported\n",
- xclk_freq);
- ret = -EINVAL;
- goto free_err;
- }
+static int imx290_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct imx290 *imx290;
+ int ret;
- ret = clk_set_rate(imx290->xclk, xclk_freq);
- if (ret) {
- dev_err(dev, "Could not set xclk frequency\n");
- goto free_err;
+ imx290 = devm_kzalloc(dev, sizeof(*imx290), GFP_KERNEL);
+ if (!imx290)
+ return -ENOMEM;
+
+ imx290->dev = dev;
+ imx290->regmap = devm_regmap_init_i2c(client, &imx290_regmap_config);
+ if (IS_ERR(imx290->regmap)) {
+ dev_err(dev, "Unable to initialize I2C\n");
+ return -ENODEV;
}
+ ret = imx290_parse_dt(imx290);
+ if (ret)
+ return ret;
+
+ /* Acquire resources. */
+ imx290->xclk = devm_clk_get(dev, "xclk");
+ if (IS_ERR(imx290->xclk))
+ return dev_err_probe(dev, PTR_ERR(imx290->xclk),
+ "Could not get xclk");
+
ret = imx290_get_regulators(dev, imx290);
- if (ret < 0) {
- dev_err(dev, "Cannot get regulators\n");
- goto free_err;
- }
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Cannot get regulators\n");
imx290->rst_gpio = devm_gpiod_get_optional(dev, "reset",
GPIOD_OUT_HIGH);
- if (IS_ERR(imx290->rst_gpio)) {
- dev_err(dev, "Cannot get reset gpio\n");
- ret = PTR_ERR(imx290->rst_gpio);
- goto free_err;
- }
+ if (IS_ERR(imx290->rst_gpio))
+ return dev_err_probe(dev, PTR_ERR(imx290->rst_gpio),
+ "Cannot get reset gpio\n");
- mutex_init(&imx290->lock);
+ /* Initialize external clock frequency. */
+ ret = imx290_init_clk(imx290);
+ if (ret)
+ return ret;
/*
- * Initialize the frame format. In particular, imx290->current_mode
- * and imx290->bpp are set to defaults: imx290_calc_pixel_rate() call
- * below relies on these fields.
+ * Enable power management. The driver supports runtime PM, but needs to
+ * work when runtime PM is disabled in the kernel. To that end, power
+ * the sensor on manually here.
*/
- imx290_entity_init_cfg(&imx290->sd, NULL);
-
- ret = imx290_ctrl_init(imx290);
+ ret = imx290_power_on(imx290);
if (ret < 0) {
- dev_err(dev, "Control initialization error %d\n", ret);
- goto free_mutex;
+ dev_err(dev, "Could not power on the device\n");
+ return ret;
}
- v4l2_i2c_subdev_init(&imx290->sd, client, &imx290_subdev_ops);
- imx290->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
- imx290->sd.dev = &client->dev;
- imx290->sd.entity.ops = &imx290_subdev_entity_ops;
- imx290->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
+ /*
+ * Enable runtime PM with autosuspend. As the device has been powered
+ * manually, mark it as active, and increase the usage count without
+ * resuming the device.
+ */
+ pm_runtime_set_active(dev);
+ pm_runtime_get_noresume(dev);
+ pm_runtime_enable(dev);
+ pm_runtime_set_autosuspend_delay(dev, 1000);
+ pm_runtime_use_autosuspend(dev);
- imx290->pad.flags = MEDIA_PAD_FL_SOURCE;
- ret = media_entity_pads_init(&imx290->sd.entity, 1, &imx290->pad);
- if (ret < 0) {
- dev_err(dev, "Could not register media entity\n");
- goto free_ctrl;
- }
+ /* Initialize the V4L2 subdev. */
+ ret = imx290_subdev_init(imx290);
+ if (ret)
+ goto err_pm;
+ /*
+ * Finally, register the V4L2 subdev. This must be done after
+ * initializing everything as the subdev can be used immediately after
+ * being registered.
+ */
ret = v4l2_async_register_subdev(&imx290->sd);
if (ret < 0) {
dev_err(dev, "Could not register v4l2 device\n");
- goto free_entity;
- }
-
- /* Power on the device to match runtime PM state below */
- ret = imx290_power_on(dev);
- if (ret < 0) {
- dev_err(dev, "Could not power on the device\n");
- goto free_entity;
+ goto err_subdev;
}
- pm_runtime_set_active(dev);
- pm_runtime_enable(dev);
- pm_runtime_idle(dev);
-
- v4l2_fwnode_endpoint_free(&ep);
+ /*
+ * Decrease the PM usage count. The device will get suspended after the
+ * autosuspend delay, turning the power off.
+ */
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
return 0;
-free_entity:
- media_entity_cleanup(&imx290->sd.entity);
-free_ctrl:
- v4l2_ctrl_handler_free(&imx290->ctrls);
-free_mutex:
- mutex_destroy(&imx290->lock);
-free_err:
- v4l2_fwnode_endpoint_free(&ep);
-
+err_subdev:
+ imx290_subdev_cleanup(imx290);
+err_pm:
+ pm_runtime_disable(dev);
+ pm_runtime_put_noidle(dev);
+ imx290_power_off(imx290);
return ret;
}
@@ -1263,14 +1339,15 @@ static void imx290_remove(struct i2c_client *client)
struct imx290 *imx290 = to_imx290(sd);
v4l2_async_unregister_subdev(sd);
- media_entity_cleanup(&sd->entity);
- v4l2_ctrl_handler_free(sd->ctrl_handler);
-
- mutex_destroy(&imx290->lock);
+ imx290_subdev_cleanup(imx290);
+ /*
+ * Disable runtime PM. In case runtime PM is disabled in the kernel,
+ * make sure to turn power off manually.
+ */
pm_runtime_disable(imx290->dev);
if (!pm_runtime_status_suspended(imx290->dev))
- imx290_power_off(imx290->dev);
+ imx290_power_off(imx290);
pm_runtime_set_suspended(imx290->dev);
}
diff --git a/drivers/media/i2c/imx296.c b/drivers/media/i2c/imx296.c
new file mode 100644
index 000000000000..3c12b6edeac9
--- /dev/null
+++ b/drivers/media/i2c/imx296.c
@@ -0,0 +1,1172 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for IMX296 CMOS Image Sensor from Sony
+ *
+ * Copyright 2019 Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-subdev.h>
+
+#define IMX296_PIXEL_ARRAY_WIDTH 1456
+#define IMX296_PIXEL_ARRAY_HEIGHT 1088
+
+#define IMX296_REG_8BIT(n) ((1 << 16) | (n))
+#define IMX296_REG_16BIT(n) ((2 << 16) | (n))
+#define IMX296_REG_24BIT(n) ((3 << 16) | (n))
+#define IMX296_REG_SIZE_SHIFT 16
+#define IMX296_REG_ADDR_MASK 0xffff
+
+#define IMX296_CTRL00 IMX296_REG_8BIT(0x3000)
+#define IMX296_CTRL00_STANDBY BIT(0)
+#define IMX296_CTRL08 IMX296_REG_8BIT(0x3008)
+#define IMX296_CTRL08_REGHOLD BIT(0)
+#define IMX296_CTRL0A IMX296_REG_8BIT(0x300a)
+#define IMX296_CTRL0A_XMSTA BIT(0)
+#define IMX296_CTRL0B IMX296_REG_8BIT(0x300b)
+#define IMX296_CTRL0B_TRIGEN BIT(0)
+#define IMX296_CTRL0D IMX296_REG_8BIT(0x300d)
+#define IMX296_CTRL0D_WINMODE_ALL (0 << 0)
+#define IMX296_CTRL0D_WINMODE_FD_BINNING (2 << 0)
+#define IMX296_CTRL0D_HADD_ON_BINNING BIT(5)
+#define IMX296_CTRL0D_SAT_CNT BIT(6)
+#define IMX296_CTRL0E IMX296_REG_8BIT(0x300e)
+#define IMX296_CTRL0E_VREVERSE BIT(0)
+#define IMX296_CTRL0E_HREVERSE BIT(1)
+#define IMX296_VMAX IMX296_REG_24BIT(0x3010)
+#define IMX296_HMAX IMX296_REG_16BIT(0x3014)
+#define IMX296_TMDCTRL IMX296_REG_8BIT(0x301d)
+#define IMX296_TMDCTRL_LATCH BIT(0)
+#define IMX296_TMDOUT IMX296_REG_16BIT(0x301e)
+#define IMX296_TMDOUT_MASK 0x3ff
+#define IMX296_WDSEL IMX296_REG_8BIT(0x3021)
+#define IMX296_WDSEL_NORMAL (0 << 0)
+#define IMX296_WDSEL_MULTI_2 (1 << 0)
+#define IMX296_WDSEL_MULTI_4 (3 << 0)
+#define IMX296_BLKLEVELAUTO IMX296_REG_8BIT(0x3022)
+#define IMX296_BLKLEVELAUTO_ON 0x01
+#define IMX296_BLKLEVELAUTO_OFF 0xf0
+#define IMX296_SST IMX296_REG_8BIT(0x3024)
+#define IMX296_SST_EN BIT(0)
+#define IMX296_CTRLTOUT IMX296_REG_8BIT(0x3026)
+#define IMX296_CTRLTOUT_TOUT1SEL_LOW (0 << 0)
+#define IMX296_CTRLTOUT_TOUT1SEL_PULSE (3 << 0)
+#define IMX296_CTRLTOUT_TOUT2SEL_LOW (0 << 2)
+#define IMX296_CTRLTOUT_TOUT2SEL_PULSE (3 << 2)
+#define IMX296_CTRLTRIG IMX296_REG_8BIT(0x3029)
+#define IMX296_CTRLTRIG_TOUT1_SEL_LOW (0 << 0)
+#define IMX296_CTRLTRIG_TOUT1_SEL_PULSE1 (1 << 0)
+#define IMX296_CTRLTRIG_TOUT2_SEL_LOW (0 << 4)
+#define IMX296_CTRLTRIG_TOUT2_SEL_PULSE2 (2 << 4)
+#define IMX296_SYNCSEL IMX296_REG_8BIT(0x3036)
+#define IMX296_SYNCSEL_NORMAL 0xc0
+#define IMX296_SYNCSEL_HIZ 0xf0
+#define IMX296_PULSE1 IMX296_REG_8BIT(0x306d)
+#define IMX296_PULSE1_EN_NOR BIT(0)
+#define IMX296_PULSE1_EN_TRIG BIT(1)
+#define IMX296_PULSE1_POL_HIGH (0 << 2)
+#define IMX296_PULSE1_POL_LOW (1 << 2)
+#define IMX296_PULSE1_UP IMX296_REG_24BIT(0x3070)
+#define IMX296_PULSE1_DN IMX296_REG_24BIT(0x3074)
+#define IMX296_PULSE2 IMX296_REG_8BIT(0x3079)
+#define IMX296_PULSE2_EN_NOR BIT(0)
+#define IMX296_PULSE2_EN_TRIG BIT(1)
+#define IMX296_PULSE2_POL_HIGH (0 << 2)
+#define IMX296_PULSE2_POL_LOW (1 << 2)
+#define IMX296_PULSE2_UP IMX296_REG_24BIT(0x307c)
+#define IMX296_PULSE2_DN IMX296_REG_24BIT(0x3080)
+#define IMX296_INCKSEL(n) IMX296_REG_8BIT(0x3089 + (n))
+#define IMX296_SHS1 IMX296_REG_24BIT(0x308d)
+#define IMX296_SHS2 IMX296_REG_24BIT(0x3090)
+#define IMX296_SHS3 IMX296_REG_24BIT(0x3094)
+#define IMX296_SHS4 IMX296_REG_24BIT(0x3098)
+#define IMX296_VBLANKLP IMX296_REG_8BIT(0x309c)
+#define IMX296_VBLANKLP_NORMAL 0x04
+#define IMX296_VBLANKLP_LOW_POWER 0x2c
+#define IMX296_EXP_CNT IMX296_REG_8BIT(0x30a3)
+#define IMX296_EXP_CNT_RESET BIT(0)
+#define IMX296_EXP_MAX IMX296_REG_16BIT(0x30a6)
+#define IMX296_VINT IMX296_REG_8BIT(0x30aa)
+#define IMX296_VINT_EN BIT(0)
+#define IMX296_LOWLAGTRG IMX296_REG_8BIT(0x30ae)
+#define IMX296_LOWLAGTRG_FAST BIT(0)
+#define IMX296_I2CCTRL IMX296_REG_8BIT(0x30ef)
+#define IMX296_I2CCTRL_I2CACKEN BIT(0)
+
+#define IMX296_SENSOR_INFO IMX296_REG_16BIT(0x3148)
+#define IMX296_SENSOR_INFO_MONO BIT(15)
+#define IMX296_SENSOR_INFO_IMX296LQ 0x4a00
+#define IMX296_SENSOR_INFO_IMX296LL 0xca00
+#define IMX296_S_SHSA IMX296_REG_16BIT(0x31ca)
+#define IMX296_S_SHSB IMX296_REG_16BIT(0x31d2)
+/*
+ * Registers 0x31c8 to 0x31cd, 0x31d0 to 0x31d5, 0x31e2, 0x31e3, 0x31ea and
+ * 0x31eb are related to exposure mode but otherwise not documented.
+ */
+
+#define IMX296_GAINCTRL IMX296_REG_8BIT(0x3200)
+#define IMX296_GAINCTRL_WD_GAIN_MODE_NORMAL 0x01
+#define IMX296_GAINCTRL_WD_GAIN_MODE_MULTI 0x41
+#define IMX296_GAIN IMX296_REG_16BIT(0x3204)
+#define IMX296_GAIN_MIN 0
+#define IMX296_GAIN_MAX 480
+#define IMX296_GAIN1 IMX296_REG_16BIT(0x3208)
+#define IMX296_GAIN2 IMX296_REG_16BIT(0x320c)
+#define IMX296_GAIN3 IMX296_REG_16BIT(0x3210)
+#define IMX296_GAINDLY IMX296_REG_8BIT(0x3212)
+#define IMX296_GAINDLY_NONE 0x08
+#define IMX296_GAINDLY_1FRAME 0x09
+#define IMX296_PGCTRL IMX296_REG_8BIT(0x3238)
+#define IMX296_PGCTRL_REGEN BIT(0)
+#define IMX296_PGCTRL_THRU BIT(1)
+#define IMX296_PGCTRL_CLKEN BIT(2)
+#define IMX296_PGCTRL_MODE(n) ((n) << 3)
+#define IMX296_PGHPOS IMX296_REG_16BIT(0x3239)
+#define IMX296_PGVPOS IMX296_REG_16BIT(0x323c)
+#define IMX296_PGHPSTEP IMX296_REG_8BIT(0x323e)
+#define IMX296_PGVPSTEP IMX296_REG_8BIT(0x323f)
+#define IMX296_PGHPNUM IMX296_REG_8BIT(0x3240)
+#define IMX296_PGVPNUM IMX296_REG_8BIT(0x3241)
+#define IMX296_PGDATA1 IMX296_REG_16BIT(0x3244)
+#define IMX296_PGDATA2 IMX296_REG_16BIT(0x3246)
+#define IMX296_PGHGSTEP IMX296_REG_8BIT(0x3249)
+#define IMX296_BLKLEVEL IMX296_REG_16BIT(0x3254)
+
+#define IMX296_FID0_ROI IMX296_REG_8BIT(0x3300)
+#define IMX296_FID0_ROIH1ON BIT(0)
+#define IMX296_FID0_ROIV1ON BIT(1)
+#define IMX296_FID0_ROIPH1 IMX296_REG_16BIT(0x3310)
+#define IMX296_FID0_ROIPV1 IMX296_REG_16BIT(0x3312)
+#define IMX296_FID0_ROIWH1 IMX296_REG_16BIT(0x3314)
+#define IMX296_FID0_ROIWH1_MIN 80
+#define IMX296_FID0_ROIWV1 IMX296_REG_16BIT(0x3316)
+#define IMX296_FID0_ROIWV1_MIN 4
+
+#define IMX296_CM_HSST_STARTTMG IMX296_REG_16BIT(0x4018)
+#define IMX296_CM_HSST_ENDTMG IMX296_REG_16BIT(0x401a)
+#define IMX296_DA_HSST_STARTTMG IMX296_REG_16BIT(0x404d)
+#define IMX296_DA_HSST_ENDTMG IMX296_REG_16BIT(0x4050)
+#define IMX296_LM_HSST_STARTTMG IMX296_REG_16BIT(0x4094)
+#define IMX296_LM_HSST_ENDTMG IMX296_REG_16BIT(0x4096)
+#define IMX296_SST_SIEASTA1_SET IMX296_REG_8BIT(0x40c9)
+#define IMX296_SST_SIEASTA1PRE_1U IMX296_REG_16BIT(0x40cc)
+#define IMX296_SST_SIEASTA1PRE_1D IMX296_REG_16BIT(0x40ce)
+#define IMX296_SST_SIEASTA1PRE_2U IMX296_REG_16BIT(0x40d0)
+#define IMX296_SST_SIEASTA1PRE_2D IMX296_REG_16BIT(0x40d2)
+#define IMX296_HSST IMX296_REG_8BIT(0x40dc)
+#define IMX296_HSST_EN BIT(2)
+
+#define IMX296_CKREQSEL IMX296_REG_8BIT(0x4101)
+#define IMX296_CKREQSEL_HS BIT(2)
+#define IMX296_GTTABLENUM IMX296_REG_8BIT(0x4114)
+#define IMX296_CTRL418C IMX296_REG_8BIT(0x418c)
+
+struct imx296_clk_params {
+ unsigned int freq;
+ u8 incksel[4];
+ u8 ctrl418c;
+};
+
+static const struct imx296_clk_params imx296_clk_params[] = {
+ { 37125000, { 0x80, 0x0b, 0x80, 0x08 }, 116 },
+ { 54000000, { 0xb0, 0x0f, 0xb0, 0x0c }, 168 },
+ { 74250000, { 0x80, 0x0f, 0x80, 0x0c }, 232 },
+};
+
+static const char * const imx296_supply_names[] = {
+ "dvdd",
+ "ovdd",
+ "avdd",
+};
+
+struct imx296 {
+ struct device *dev;
+ struct clk *clk;
+ struct regulator_bulk_data supplies[ARRAY_SIZE(imx296_supply_names)];
+ struct gpio_desc *reset;
+ struct regmap *regmap;
+
+ const struct imx296_clk_params *clk_params;
+ bool mono;
+
+ bool streaming;
+
+ struct v4l2_subdev subdev;
+ struct media_pad pad;
+
+ struct v4l2_ctrl_handler ctrls;
+ struct v4l2_ctrl *hblank;
+ struct v4l2_ctrl *vblank;
+};
+
+static inline struct imx296 *to_imx296(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct imx296, subdev);
+}
+
+static int imx296_read(struct imx296 *sensor, u32 addr)
+{
+ u8 data[3] = { 0, 0, 0 };
+ int ret;
+
+ ret = regmap_raw_read(sensor->regmap, addr & IMX296_REG_ADDR_MASK, data,
+ (addr >> IMX296_REG_SIZE_SHIFT) & 3);
+ if (ret < 0)
+ return ret;
+
+ return (data[2] << 16) | (data[1] << 8) | data[0];
+}
+
+static int imx296_write(struct imx296 *sensor, u32 addr, u32 value, int *err)
+{
+ u8 data[3] = { value & 0xff, (value >> 8) & 0xff, value >> 16 };
+ int ret;
+
+ if (err && *err)
+ return *err;
+
+ ret = regmap_raw_write(sensor->regmap, addr & IMX296_REG_ADDR_MASK,
+ data, (addr >> IMX296_REG_SIZE_SHIFT) & 3);
+ if (ret < 0) {
+ dev_err(sensor->dev, "%u-bit write to 0x%04x failed: %d\n",
+ ((addr >> IMX296_REG_SIZE_SHIFT) & 3) * 8,
+ addr & IMX296_REG_ADDR_MASK, ret);
+ if (err)
+ *err = ret;
+ }
+
+ return ret;
+}
+
+static int imx296_power_on(struct imx296 *sensor)
+{
+ int ret;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(sensor->supplies),
+ sensor->supplies);
+ if (ret < 0)
+ return ret;
+
+ udelay(1);
+
+ ret = gpiod_direction_output(sensor->reset, 0);
+ if (ret < 0)
+ goto err_supply;
+
+ udelay(1);
+
+ ret = clk_prepare_enable(sensor->clk);
+ if (ret < 0)
+ goto err_reset;
+
+ /*
+ * The documentation doesn't explicitly say how much time is required
+ * after providing a clock and before starting I2C communication. It
+ * mentions a delay of 20µs in 4-wire mode, but tests showed that a
+ * delay of 100µs resulted in I2C communication failures, while 500µs
+ * seems to be enough. Be conservative.
+ */
+ usleep_range(1000, 2000);
+
+ return 0;
+
+err_reset:
+ gpiod_direction_output(sensor->reset, 1);
+err_supply:
+ regulator_bulk_disable(ARRAY_SIZE(sensor->supplies), sensor->supplies);
+ return ret;
+}
+
+static void imx296_power_off(struct imx296 *sensor)
+{
+ clk_disable_unprepare(sensor->clk);
+ gpiod_direction_output(sensor->reset, 1);
+ regulator_bulk_disable(ARRAY_SIZE(sensor->supplies), sensor->supplies);
+}
+
+/* -----------------------------------------------------------------------------
+ * Controls
+ */
+
+static const char * const imx296_test_pattern_menu[] = {
+ "Disabled",
+ "Multiple Pixels",
+ "Sequence 1",
+ "Sequence 2",
+ "Gradient",
+ "Row",
+ "Column",
+ "Cross",
+ "Stripe",
+ "Checks",
+};
+
+static int imx296_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct imx296 *sensor = container_of(ctrl->handler, struct imx296, ctrls);
+ const struct v4l2_mbus_framefmt *format;
+ struct v4l2_subdev_state *state;
+ unsigned int vmax;
+ int ret = 0;
+
+ if (!sensor->streaming)
+ return 0;
+
+ state = v4l2_subdev_get_locked_active_state(&sensor->subdev);
+ format = v4l2_subdev_get_pad_format(&sensor->subdev, state, 0);
+
+ switch (ctrl->id) {
+ case V4L2_CID_EXPOSURE:
+ /* Clamp the exposure value to VMAX. */
+ vmax = format->height + sensor->vblank->cur.val;
+ ctrl->val = min_t(int, ctrl->val, vmax);
+ imx296_write(sensor, IMX296_SHS1, vmax - ctrl->val, &ret);
+ break;
+
+ case V4L2_CID_ANALOGUE_GAIN:
+ imx296_write(sensor, IMX296_GAIN, ctrl->val, &ret);
+ break;
+
+ case V4L2_CID_VBLANK:
+ imx296_write(sensor, IMX296_VMAX, format->height + ctrl->val,
+ &ret);
+ break;
+
+ case V4L2_CID_TEST_PATTERN:
+ if (ctrl->val) {
+ imx296_write(sensor, IMX296_PGHPOS, 8, &ret);
+ imx296_write(sensor, IMX296_PGVPOS, 8, &ret);
+ imx296_write(sensor, IMX296_PGHPSTEP, 8, &ret);
+ imx296_write(sensor, IMX296_PGVPSTEP, 8, &ret);
+ imx296_write(sensor, IMX296_PGHPNUM, 100, &ret);
+ imx296_write(sensor, IMX296_PGVPNUM, 100, &ret);
+ imx296_write(sensor, IMX296_PGDATA1, 0x300, &ret);
+ imx296_write(sensor, IMX296_PGDATA2, 0x100, &ret);
+ imx296_write(sensor, IMX296_PGHGSTEP, 0, &ret);
+ imx296_write(sensor, IMX296_BLKLEVEL, 0, &ret);
+ imx296_write(sensor, IMX296_BLKLEVELAUTO,
+ IMX296_BLKLEVELAUTO_OFF, &ret);
+ imx296_write(sensor, IMX296_PGCTRL,
+ IMX296_PGCTRL_REGEN |
+ IMX296_PGCTRL_CLKEN |
+ IMX296_PGCTRL_MODE(ctrl->val - 1), &ret);
+ } else {
+ imx296_write(sensor, IMX296_PGCTRL,
+ IMX296_PGCTRL_CLKEN, &ret);
+ imx296_write(sensor, IMX296_BLKLEVEL, 0x3c, &ret);
+ imx296_write(sensor, IMX296_BLKLEVELAUTO,
+ IMX296_BLKLEVELAUTO_ON, &ret);
+ }
+ break;
+
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static const struct v4l2_ctrl_ops imx296_ctrl_ops = {
+ .s_ctrl = imx296_s_ctrl,
+};
+
+static int imx296_ctrls_init(struct imx296 *sensor)
+{
+ struct v4l2_fwnode_device_properties props;
+ unsigned int hblank;
+ int ret;
+
+ ret = v4l2_fwnode_device_parse(sensor->dev, &props);
+ if (ret < 0)
+ return ret;
+
+ v4l2_ctrl_handler_init(&sensor->ctrls, 9);
+
+ v4l2_ctrl_new_std(&sensor->ctrls, &imx296_ctrl_ops,
+ V4L2_CID_EXPOSURE, 1, 1048575, 1, 1104);
+ v4l2_ctrl_new_std(&sensor->ctrls, &imx296_ctrl_ops,
+ V4L2_CID_ANALOGUE_GAIN, IMX296_GAIN_MIN,
+ IMX296_GAIN_MAX, 1, IMX296_GAIN_MIN);
+
+ /*
+ * Horizontal blanking is controlled through the HMAX register, which
+ * contains a line length in INCK clock units. The INCK frequency is
+ * fixed to 74.25 MHz. The HMAX value is currently fixed to 1100,
+ * convert it to a number of pixels based on the nominal pixel rate.
+ */
+ hblank = 1100 * 1188000000ULL / 10 / 74250000
+ - IMX296_PIXEL_ARRAY_WIDTH;
+ sensor->hblank = v4l2_ctrl_new_std(&sensor->ctrls, &imx296_ctrl_ops,
+ V4L2_CID_HBLANK, hblank, hblank, 1,
+ hblank);
+ if (sensor->hblank)
+ sensor->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+ sensor->vblank = v4l2_ctrl_new_std(&sensor->ctrls, &imx296_ctrl_ops,
+ V4L2_CID_VBLANK, 30,
+ 1048575 - IMX296_PIXEL_ARRAY_HEIGHT,
+ 1, 30);
+ /*
+ * The sensor calculates the MIPI timings internally to achieve a bit
+ * rate between 1122 and 1198 Mbps. The exact value is unfortunately not
+ * reported, at least according to the documentation. Report a nominal
+ * rate of 1188 Mbps as that is used by the datasheet in multiple
+ * examples.
+ */
+ v4l2_ctrl_new_std(&sensor->ctrls, NULL, V4L2_CID_PIXEL_RATE,
+ 1122000000 / 10, 1198000000 / 10, 1, 1188000000 / 10);
+ v4l2_ctrl_new_std_menu_items(&sensor->ctrls, &imx296_ctrl_ops,
+ V4L2_CID_TEST_PATTERN,
+ ARRAY_SIZE(imx296_test_pattern_menu) - 1,
+ 0, 0, imx296_test_pattern_menu);
+
+ v4l2_ctrl_new_fwnode_properties(&sensor->ctrls, &imx296_ctrl_ops,
+ &props);
+
+ if (sensor->ctrls.error) {
+ dev_err(sensor->dev, "failed to add controls (%d)\n",
+ sensor->ctrls.error);
+ v4l2_ctrl_handler_free(&sensor->ctrls);
+ return sensor->ctrls.error;
+ }
+
+ sensor->subdev.ctrl_handler = &sensor->ctrls;
+
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 Subdev Operations
+ */
+
+/*
+ * This table is extracted from vendor data that is entirely undocumented. The
+ * first register write is required to activate the CSI-2 output. The other
+ * entries may or may not be optional?
+ */
+static const struct {
+ unsigned int reg;
+ unsigned int value;
+} imx296_init_table[] = {
+ { IMX296_REG_8BIT(0x3005), 0xf0 },
+ { IMX296_REG_8BIT(0x309e), 0x04 },
+ { IMX296_REG_8BIT(0x30a0), 0x04 },
+ { IMX296_REG_8BIT(0x30a1), 0x3c },
+ { IMX296_REG_8BIT(0x30a4), 0x5f },
+ { IMX296_REG_8BIT(0x30a8), 0x91 },
+ { IMX296_REG_8BIT(0x30ac), 0x28 },
+ { IMX296_REG_8BIT(0x30af), 0x09 },
+ { IMX296_REG_8BIT(0x30df), 0x00 },
+ { IMX296_REG_8BIT(0x3165), 0x00 },
+ { IMX296_REG_8BIT(0x3169), 0x10 },
+ { IMX296_REG_8BIT(0x316a), 0x02 },
+ { IMX296_REG_8BIT(0x31c8), 0xf3 }, /* Exposure-related */
+ { IMX296_REG_8BIT(0x31d0), 0xf4 }, /* Exposure-related */
+ { IMX296_REG_8BIT(0x321a), 0x00 },
+ { IMX296_REG_8BIT(0x3226), 0x02 },
+ { IMX296_REG_8BIT(0x3256), 0x01 },
+ { IMX296_REG_8BIT(0x3541), 0x72 },
+ { IMX296_REG_8BIT(0x3516), 0x77 },
+ { IMX296_REG_8BIT(0x350b), 0x7f },
+ { IMX296_REG_8BIT(0x3758), 0xa3 },
+ { IMX296_REG_8BIT(0x3759), 0x00 },
+ { IMX296_REG_8BIT(0x375a), 0x85 },
+ { IMX296_REG_8BIT(0x375b), 0x00 },
+ { IMX296_REG_8BIT(0x3832), 0xf5 },
+ { IMX296_REG_8BIT(0x3833), 0x00 },
+ { IMX296_REG_8BIT(0x38a2), 0xf6 },
+ { IMX296_REG_8BIT(0x38a3), 0x00 },
+ { IMX296_REG_8BIT(0x3a00), 0x80 },
+ { IMX296_REG_8BIT(0x3d48), 0xa3 },
+ { IMX296_REG_8BIT(0x3d49), 0x00 },
+ { IMX296_REG_8BIT(0x3d4a), 0x85 },
+ { IMX296_REG_8BIT(0x3d4b), 0x00 },
+ { IMX296_REG_8BIT(0x400e), 0x58 },
+ { IMX296_REG_8BIT(0x4014), 0x1c },
+ { IMX296_REG_8BIT(0x4041), 0x2a },
+ { IMX296_REG_8BIT(0x40a2), 0x06 },
+ { IMX296_REG_8BIT(0x40c1), 0xf6 },
+ { IMX296_REG_8BIT(0x40c7), 0x0f },
+ { IMX296_REG_8BIT(0x40c8), 0x00 },
+ { IMX296_REG_8BIT(0x4174), 0x00 },
+};
+
+static int imx296_setup(struct imx296 *sensor, struct v4l2_subdev_state *state)
+{
+ const struct v4l2_mbus_framefmt *format;
+ const struct v4l2_rect *crop;
+ unsigned int i;
+ int ret = 0;
+
+ format = v4l2_subdev_get_pad_format(&sensor->subdev, state, 0);
+ crop = v4l2_subdev_get_pad_crop(&sensor->subdev, state, 0);
+
+ for (i = 0; i < ARRAY_SIZE(imx296_init_table); ++i)
+ imx296_write(sensor, imx296_init_table[i].reg,
+ imx296_init_table[i].value, &ret);
+
+ if (crop->width != IMX296_PIXEL_ARRAY_WIDTH ||
+ crop->height != IMX296_PIXEL_ARRAY_HEIGHT) {
+ imx296_write(sensor, IMX296_FID0_ROI,
+ IMX296_FID0_ROIH1ON | IMX296_FID0_ROIV1ON, &ret);
+ imx296_write(sensor, IMX296_FID0_ROIPH1, crop->left, &ret);
+ imx296_write(sensor, IMX296_FID0_ROIPV1, crop->top, &ret);
+ imx296_write(sensor, IMX296_FID0_ROIWH1, crop->width, &ret);
+ imx296_write(sensor, IMX296_FID0_ROIWV1, crop->height, &ret);
+ } else {
+ imx296_write(sensor, IMX296_FID0_ROI, 0, &ret);
+ }
+
+ imx296_write(sensor, IMX296_CTRL0D,
+ (crop->width != format->width ?
+ IMX296_CTRL0D_HADD_ON_BINNING : 0) |
+ (crop->height != format->height ?
+ IMX296_CTRL0D_WINMODE_FD_BINNING : 0),
+ &ret);
+
+ /*
+ * HMAX and VMAX configure horizontal and vertical blanking by
+ * specifying the total line time and frame time respectively. The line
+ * time is specified in operational clock units (which appears to be the
+ * output of an internal PLL, fixed at 74.25 MHz regardless of the
+ * exernal clock frequency), while the frame time is specified as a
+ * number of lines.
+ *
+ * In the vertical direction the sensor outputs the following:
+ *
+ * - one line for the FS packet
+ * - two lines of embedded data (DT 0x12)
+ * - six null lines (DT 0x10)
+ * - four lines of vertical effective optical black (DT 0x37)
+ * - 8 to 1088 lines of active image data (RAW10, DT 0x2b)
+ * - one line for the FE packet
+ * - 16 or more lines of vertical blanking
+ */
+ imx296_write(sensor, IMX296_HMAX, 1100, &ret);
+ imx296_write(sensor, IMX296_VMAX,
+ format->height + sensor->vblank->cur.val, &ret);
+
+ for (i = 0; i < ARRAY_SIZE(sensor->clk_params->incksel); ++i)
+ imx296_write(sensor, IMX296_INCKSEL(i),
+ sensor->clk_params->incksel[i], &ret);
+ imx296_write(sensor, IMX296_GTTABLENUM, 0xc5, &ret);
+ imx296_write(sensor, IMX296_CTRL418C, sensor->clk_params->ctrl418c,
+ &ret);
+
+ imx296_write(sensor, IMX296_GAINDLY, IMX296_GAINDLY_NONE, &ret);
+ imx296_write(sensor, IMX296_BLKLEVEL, 0x03c, &ret);
+
+ return ret;
+}
+
+static int imx296_stream_on(struct imx296 *sensor)
+{
+ int ret = 0;
+
+ imx296_write(sensor, IMX296_CTRL00, 0, &ret);
+ usleep_range(2000, 5000);
+ imx296_write(sensor, IMX296_CTRL0A, 0, &ret);
+
+ return ret;
+}
+
+static int imx296_stream_off(struct imx296 *sensor)
+{
+ int ret = 0;
+
+ imx296_write(sensor, IMX296_CTRL0A, IMX296_CTRL0A_XMSTA, &ret);
+ imx296_write(sensor, IMX296_CTRL00, IMX296_CTRL00_STANDBY, &ret);
+
+ return ret;
+}
+
+static int imx296_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct imx296 *sensor = to_imx296(sd);
+ struct v4l2_subdev_state *state;
+ int ret;
+
+ state = v4l2_subdev_lock_and_get_active_state(sd);
+
+ if (!enable) {
+ ret = imx296_stream_off(sensor);
+
+ pm_runtime_mark_last_busy(sensor->dev);
+ pm_runtime_put_autosuspend(sensor->dev);
+
+ sensor->streaming = false;
+
+ goto unlock;
+ }
+
+ ret = pm_runtime_resume_and_get(sensor->dev);
+ if (ret < 0)
+ goto unlock;
+
+ ret = imx296_setup(sensor, state);
+ if (ret < 0)
+ goto err_pm;
+
+ /*
+ * Set streaming to true to ensure __v4l2_ctrl_handler_setup() will set
+ * the controls. The flag is reset to false further down if an error
+ * occurs.
+ */
+ sensor->streaming = true;
+
+ ret = __v4l2_ctrl_handler_setup(&sensor->ctrls);
+ if (ret < 0)
+ goto err_pm;
+
+ ret = imx296_stream_on(sensor);
+ if (ret)
+ goto err_pm;
+
+unlock:
+ v4l2_subdev_unlock_state(state);
+
+ return ret;
+
+err_pm:
+ /*
+ * In case of error, turn the power off synchronously as the device
+ * likely has no other chance to recover.
+ */
+ pm_runtime_put_sync(sensor->dev);
+ sensor->streaming = false;
+
+ goto unlock;
+}
+
+static int imx296_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ struct imx296 *sensor = to_imx296(sd);
+
+ if (code->index != 0)
+ return -EINVAL;
+
+ code->code = sensor->mono ? MEDIA_BUS_FMT_Y10_1X10
+ : MEDIA_BUS_FMT_SBGGR10_1X10;
+
+ return 0;
+}
+
+static int imx296_enum_frame_size(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ const struct v4l2_mbus_framefmt *format;
+
+ format = v4l2_subdev_get_pad_format(sd, state, fse->pad);
+
+ if (fse->index >= 2 || fse->code != format->code)
+ return -EINVAL;
+
+ fse->min_width = IMX296_PIXEL_ARRAY_WIDTH / (fse->index + 1);
+ fse->max_width = fse->min_width;
+ fse->min_height = IMX296_PIXEL_ARRAY_HEIGHT / (fse->index + 1);
+ fse->max_height = fse->min_height;
+
+ return 0;
+}
+
+static int imx296_get_format(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_format *fmt)
+{
+ fmt->format = *v4l2_subdev_get_pad_format(sd, state, fmt->pad);
+
+ return 0;
+}
+
+static int imx296_set_format(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_format *fmt)
+{
+ struct imx296 *sensor = to_imx296(sd);
+ struct v4l2_mbus_framefmt *format;
+ struct v4l2_rect *crop;
+
+ crop = v4l2_subdev_get_pad_crop(sd, state, fmt->pad);
+ format = v4l2_subdev_get_pad_format(sd, state, fmt->pad);
+
+ /*
+ * Binning is only allowed when cropping is disabled according to the
+ * documentation. This should be double-checked.
+ */
+ if (crop->width == IMX296_PIXEL_ARRAY_WIDTH &&
+ crop->height == IMX296_PIXEL_ARRAY_HEIGHT) {
+ unsigned int width;
+ unsigned int height;
+ unsigned int hratio;
+ unsigned int vratio;
+
+ /* Clamp the width and height to avoid dividing by zero. */
+ width = clamp_t(unsigned int, fmt->format.width,
+ crop->width / 2, crop->width);
+ height = clamp_t(unsigned int, fmt->format.height,
+ crop->height / 2, crop->height);
+
+ hratio = DIV_ROUND_CLOSEST(crop->width, width);
+ vratio = DIV_ROUND_CLOSEST(crop->height, height);
+
+ format->width = crop->width / hratio;
+ format->height = crop->height / vratio;
+ } else {
+ format->width = crop->width;
+ format->height = crop->height;
+ }
+
+ format->code = sensor->mono ? MEDIA_BUS_FMT_Y10_1X10
+ : MEDIA_BUS_FMT_SBGGR10_1X10;
+ format->field = V4L2_FIELD_NONE;
+ format->colorspace = V4L2_COLORSPACE_RAW;
+ format->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
+ format->quantization = V4L2_QUANTIZATION_FULL_RANGE;
+ format->xfer_func = V4L2_XFER_FUNC_NONE;
+
+ fmt->format = *format;
+
+ return 0;
+}
+
+static int imx296_get_selection(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_selection *sel)
+{
+ switch (sel->target) {
+ case V4L2_SEL_TGT_CROP:
+ sel->r = *v4l2_subdev_get_pad_crop(sd, state, sel->pad);
+ break;
+
+ case V4L2_SEL_TGT_CROP_DEFAULT:
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ case V4L2_SEL_TGT_NATIVE_SIZE:
+ sel->r.left = 0;
+ sel->r.top = 0;
+ sel->r.width = IMX296_PIXEL_ARRAY_WIDTH;
+ sel->r.height = IMX296_PIXEL_ARRAY_HEIGHT;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int imx296_set_selection(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_selection *sel)
+{
+ struct v4l2_mbus_framefmt *format;
+ struct v4l2_rect *crop;
+ struct v4l2_rect rect;
+
+ if (sel->target != V4L2_SEL_TGT_CROP)
+ return -EINVAL;
+
+ /*
+ * Clamp the crop rectangle boundaries and align them to a multiple of 4
+ * pixels to satisfy hardware requirements.
+ */
+ rect.left = clamp(ALIGN(sel->r.left, 4), 0,
+ IMX296_PIXEL_ARRAY_WIDTH - IMX296_FID0_ROIWH1_MIN);
+ rect.top = clamp(ALIGN(sel->r.top, 4), 0,
+ IMX296_PIXEL_ARRAY_HEIGHT - IMX296_FID0_ROIWV1_MIN);
+ rect.width = clamp_t(unsigned int, ALIGN(sel->r.width, 4),
+ IMX296_FID0_ROIWH1_MIN, IMX296_PIXEL_ARRAY_WIDTH);
+ rect.height = clamp_t(unsigned int, ALIGN(sel->r.height, 4),
+ IMX296_FID0_ROIWV1_MIN, IMX296_PIXEL_ARRAY_HEIGHT);
+
+ rect.width = min_t(unsigned int, rect.width,
+ IMX296_PIXEL_ARRAY_WIDTH - rect.left);
+ rect.height = min_t(unsigned int, rect.height,
+ IMX296_PIXEL_ARRAY_HEIGHT - rect.top);
+
+ crop = v4l2_subdev_get_pad_crop(sd, state, sel->pad);
+
+ if (rect.width != crop->width || rect.height != crop->height) {
+ /*
+ * Reset the output image size if the crop rectangle size has
+ * been modified.
+ */
+ format = v4l2_subdev_get_pad_format(sd, state, sel->pad);
+ format->width = rect.width;
+ format->height = rect.height;
+ }
+
+ *crop = rect;
+ sel->r = rect;
+
+ return 0;
+}
+
+static int imx296_init_cfg(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state)
+{
+ struct v4l2_subdev_selection sel = {
+ .target = V4L2_SEL_TGT_CROP,
+ .r.width = IMX296_PIXEL_ARRAY_WIDTH,
+ .r.height = IMX296_PIXEL_ARRAY_HEIGHT,
+ };
+ struct v4l2_subdev_format format = {
+ .format = {
+ .width = IMX296_PIXEL_ARRAY_WIDTH,
+ .height = IMX296_PIXEL_ARRAY_HEIGHT,
+ },
+ };
+
+ imx296_set_selection(sd, state, &sel);
+ imx296_set_format(sd, state, &format);
+
+ return 0;
+}
+
+static const struct v4l2_subdev_video_ops imx296_subdev_video_ops = {
+ .s_stream = imx296_s_stream,
+};
+
+static const struct v4l2_subdev_pad_ops imx296_subdev_pad_ops = {
+ .enum_mbus_code = imx296_enum_mbus_code,
+ .enum_frame_size = imx296_enum_frame_size,
+ .get_fmt = imx296_get_format,
+ .set_fmt = imx296_set_format,
+ .get_selection = imx296_get_selection,
+ .set_selection = imx296_set_selection,
+ .init_cfg = imx296_init_cfg,
+};
+
+static const struct v4l2_subdev_ops imx296_subdev_ops = {
+ .video = &imx296_subdev_video_ops,
+ .pad = &imx296_subdev_pad_ops,
+};
+
+static int imx296_subdev_init(struct imx296 *sensor)
+{
+ struct i2c_client *client = to_i2c_client(sensor->dev);
+ int ret;
+
+ v4l2_i2c_subdev_init(&sensor->subdev, client, &imx296_subdev_ops);
+
+ ret = imx296_ctrls_init(sensor);
+ if (ret < 0)
+ return ret;
+
+ sensor->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ sensor->pad.flags = MEDIA_PAD_FL_SOURCE;
+ sensor->subdev.entity.function = MEDIA_ENT_F_CAM_SENSOR;
+ ret = media_entity_pads_init(&sensor->subdev.entity, 1, &sensor->pad);
+ if (ret < 0) {
+ v4l2_ctrl_handler_free(&sensor->ctrls);
+ return ret;
+ }
+
+ sensor->subdev.state_lock = sensor->subdev.ctrl_handler->lock;
+
+ v4l2_subdev_init_finalize(&sensor->subdev);
+
+ return ret;
+}
+
+static void imx296_subdev_cleanup(struct imx296 *sensor)
+{
+ media_entity_cleanup(&sensor->subdev.entity);
+ v4l2_ctrl_handler_free(&sensor->ctrls);
+}
+
+/* -----------------------------------------------------------------------------
+ * Power management
+ */
+
+static int __maybe_unused imx296_runtime_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct v4l2_subdev *subdev = i2c_get_clientdata(client);
+ struct imx296 *sensor = to_imx296(subdev);
+
+ return imx296_power_on(sensor);
+}
+
+static int __maybe_unused imx296_runtime_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct v4l2_subdev *subdev = i2c_get_clientdata(client);
+ struct imx296 *sensor = to_imx296(subdev);
+
+ imx296_power_off(sensor);
+
+ return 0;
+}
+
+static const struct dev_pm_ops imx296_pm_ops = {
+ SET_RUNTIME_PM_OPS(imx296_runtime_suspend, imx296_runtime_resume, NULL)
+};
+
+/* -----------------------------------------------------------------------------
+ * Probe & Remove
+ */
+
+static int imx296_read_temperature(struct imx296 *sensor, int *temp)
+{
+ int tmdout;
+ int ret;
+
+ ret = imx296_write(sensor, IMX296_TMDCTRL, IMX296_TMDCTRL_LATCH, NULL);
+ if (ret < 0)
+ return ret;
+
+ tmdout = imx296_read(sensor, IMX296_TMDOUT) & IMX296_TMDOUT_MASK;
+ if (tmdout < 0)
+ return tmdout;
+
+ /* T(°C) = 246.312 - 0.304 * TMDOUT */;
+ *temp = 246312 - 304 * tmdout;
+
+ return imx296_write(sensor, IMX296_TMDCTRL, 0, NULL);
+}
+
+static int imx296_identify_model(struct imx296 *sensor)
+{
+ unsigned int model;
+ int temp = 0;
+ int ret;
+
+ model = (uintptr_t)of_device_get_match_data(sensor->dev);
+ if (model) {
+ dev_dbg(sensor->dev,
+ "sensor model auto-detection disabled, forcing 0x%04x\n",
+ model);
+ sensor->mono = model & IMX296_SENSOR_INFO_MONO;
+ return 0;
+ }
+
+ /*
+ * While most registers can be read when the sensor is in standby, this
+ * is not the case of the sensor info register :-(
+ */
+ ret = imx296_write(sensor, IMX296_CTRL00, 0, NULL);
+ if (ret < 0) {
+ dev_err(sensor->dev,
+ "failed to get sensor out of standby (%d)\n", ret);
+ return ret;
+ }
+
+ ret = imx296_read(sensor, IMX296_SENSOR_INFO);
+ if (ret < 0) {
+ dev_err(sensor->dev, "failed to read sensor information (%d)\n",
+ ret);
+ goto done;
+ }
+
+ model = (ret >> 6) & 0x1ff;
+
+ switch (model) {
+ case 296:
+ sensor->mono = ret & IMX296_SENSOR_INFO_MONO;
+ break;
+ /*
+ * The IMX297 seems to share features with the IMX296, it may be
+ * possible to support it in the same driver.
+ */
+ case 297:
+ default:
+ dev_err(sensor->dev, "invalid device model 0x%04x\n", ret);
+ ret = -ENODEV;
+ goto done;
+ }
+
+ ret = imx296_read_temperature(sensor, &temp);
+ if (ret < 0)
+ goto done;
+
+ dev_info(sensor->dev, "found IMX%u%s (%u.%uC)\n", model,
+ sensor->mono ? "LL" : "LQ", temp / 1000, (temp / 100) % 10);
+
+done:
+ imx296_write(sensor, IMX296_CTRL00, IMX296_CTRL00_STANDBY, NULL);
+ return ret;
+}
+
+static const struct regmap_config imx296_regmap_config = {
+ .reg_bits = 16,
+ .val_bits = 8,
+
+ .wr_table = &(const struct regmap_access_table) {
+ .no_ranges = (const struct regmap_range[]) {
+ {
+ .range_min = IMX296_SENSOR_INFO & 0xffff,
+ .range_max = (IMX296_SENSOR_INFO & 0xffff) + 1,
+ },
+ },
+ .n_no_ranges = 1,
+ },
+};
+
+static int imx296_probe(struct i2c_client *client)
+{
+ struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
+ unsigned long clk_rate;
+ struct imx296 *sensor;
+ unsigned int i;
+ int ret;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
+ dev_warn(&adapter->dev,
+ "I2C-Adapter doesn't support I2C_FUNC_SMBUS_BYTE\n");
+ return -EIO;
+ }
+
+ sensor = devm_kzalloc(&client->dev, sizeof(*sensor), GFP_KERNEL);
+ if (!sensor)
+ return -ENOMEM;
+
+ sensor->dev = &client->dev;
+
+ /* Acquire resources. */
+ for (i = 0; i < ARRAY_SIZE(sensor->supplies); ++i)
+ sensor->supplies[i].supply = imx296_supply_names[i];
+
+ ret = devm_regulator_bulk_get(sensor->dev, ARRAY_SIZE(sensor->supplies),
+ sensor->supplies);
+ if (ret) {
+ dev_err_probe(sensor->dev, ret, "failed to get supplies\n");
+ return ret;
+ }
+
+ sensor->reset = devm_gpiod_get_optional(sensor->dev, "reset",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(sensor->reset))
+ return dev_err_probe(sensor->dev, PTR_ERR(sensor->reset),
+ "failed to get reset GPIO\n");
+
+ sensor->clk = devm_clk_get(sensor->dev, "inck");
+ if (IS_ERR(sensor->clk))
+ return dev_err_probe(sensor->dev, PTR_ERR(sensor->clk),
+ "failed to get clock\n");
+
+ clk_rate = clk_get_rate(sensor->clk);
+ for (i = 0; i < ARRAY_SIZE(imx296_clk_params); ++i) {
+ if (clk_rate == imx296_clk_params[i].freq) {
+ sensor->clk_params = &imx296_clk_params[i];
+ break;
+ }
+ }
+
+ if (!sensor->clk_params) {
+ dev_err(sensor->dev, "unsupported clock rate %lu\n", clk_rate);
+ return -EINVAL;
+ }
+
+ sensor->regmap = devm_regmap_init_i2c(client, &imx296_regmap_config);
+ if (IS_ERR(sensor->regmap))
+ return PTR_ERR(sensor->regmap);
+
+ /*
+ * Enable power management. The driver supports runtime PM, but needs to
+ * work when runtime PM is disabled in the kernel. To that end, power
+ * the sensor on manually here, identify it, and fully initialize it.
+ */
+ ret = imx296_power_on(sensor);
+ if (ret < 0)
+ return ret;
+
+ ret = imx296_identify_model(sensor);
+ if (ret < 0)
+ goto err_power;
+
+ /* Initialize the V4L2 subdev. */
+ ret = imx296_subdev_init(sensor);
+ if (ret < 0)
+ goto err_power;
+
+ /*
+ * Enable runtime PM. As the device has been powered manually, mark it
+ * as active, and increase the usage count without resuming the device.
+ */
+ pm_runtime_set_active(sensor->dev);
+ pm_runtime_get_noresume(sensor->dev);
+ pm_runtime_enable(sensor->dev);
+
+ /* Register the V4L2 subdev. */
+ ret = v4l2_async_register_subdev(&sensor->subdev);
+ if (ret < 0)
+ goto err_pm;
+
+ /*
+ * Finally, enable autosuspend and decrease the usage count. The device
+ * will get suspended after the autosuspend delay, turning the power
+ * off.
+ */
+ pm_runtime_set_autosuspend_delay(sensor->dev, 1000);
+ pm_runtime_use_autosuspend(sensor->dev);
+ pm_runtime_put_autosuspend(sensor->dev);
+
+ return 0;
+
+err_pm:
+ pm_runtime_disable(sensor->dev);
+ pm_runtime_put_noidle(sensor->dev);
+ imx296_subdev_cleanup(sensor);
+err_power:
+ imx296_power_off(sensor);
+ return ret;
+}
+
+static void imx296_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *subdev = i2c_get_clientdata(client);
+ struct imx296 *sensor = to_imx296(subdev);
+
+ v4l2_async_unregister_subdev(subdev);
+
+ imx296_subdev_cleanup(sensor);
+
+ /*
+ * Disable runtime PM. In case runtime PM is disabled in the kernel,
+ * make sure to turn power off manually.
+ */
+ pm_runtime_disable(sensor->dev);
+ if (!pm_runtime_status_suspended(sensor->dev))
+ imx296_power_off(sensor);
+ pm_runtime_set_suspended(sensor->dev);
+}
+
+static const struct of_device_id imx296_of_match[] = {
+ { .compatible = "sony,imx296", .data = NULL },
+ { .compatible = "sony,imx296ll", .data = (void *)IMX296_SENSOR_INFO_IMX296LL },
+ { .compatible = "sony,imx296lq", .data = (void *)IMX296_SENSOR_INFO_IMX296LQ },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, imx296_of_match);
+
+static struct i2c_driver imx296_i2c_driver = {
+ .driver = {
+ .of_match_table = imx296_of_match,
+ .name = "imx296",
+ .pm = &imx296_pm_ops
+ },
+ .probe_new = imx296_probe,
+ .remove = imx296_remove,
+};
+
+module_i2c_driver(imx296_i2c_driver);
+
+MODULE_DESCRIPTION("Sony IMX296 Camera driver");
+MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/i2c/imx415.c b/drivers/media/i2c/imx415.c
new file mode 100644
index 000000000000..d90392df98c7
--- /dev/null
+++ b/drivers/media/i2c/imx415.c
@@ -0,0 +1,1300 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Driver for the Sony IMX415 CMOS Image Sensor.
+ *
+ * Copyright (C) 2023 WolfVision GmbH.
+ */
+
+#include <linux/clk.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-subdev.h>
+
+#define IMX415_PIXEL_ARRAY_TOP 0
+#define IMX415_PIXEL_ARRAY_LEFT 0
+#define IMX415_PIXEL_ARRAY_WIDTH 3864
+#define IMX415_PIXEL_ARRAY_HEIGHT 2192
+#define IMX415_PIXEL_ARRAY_VBLANK 58
+
+#define IMX415_NUM_CLK_PARAM_REGS 11
+
+#define IMX415_REG_8BIT(n) ((1 << 16) | (n))
+#define IMX415_REG_16BIT(n) ((2 << 16) | (n))
+#define IMX415_REG_24BIT(n) ((3 << 16) | (n))
+#define IMX415_REG_SIZE_SHIFT 16
+#define IMX415_REG_ADDR_MASK 0xffff
+
+#define IMX415_MODE IMX415_REG_8BIT(0x3000)
+#define IMX415_MODE_OPERATING (0)
+#define IMX415_MODE_STANDBY BIT(0)
+#define IMX415_REGHOLD IMX415_REG_8BIT(0x3001)
+#define IMX415_REGHOLD_INVALID (0)
+#define IMX415_REGHOLD_VALID BIT(0)
+#define IMX415_XMSTA IMX415_REG_8BIT(0x3002)
+#define IMX415_XMSTA_START (0)
+#define IMX415_XMSTA_STOP BIT(0)
+#define IMX415_BCWAIT_TIME IMX415_REG_16BIT(0x3008)
+#define IMX415_CPWAIT_TIME IMX415_REG_16BIT(0x300A)
+#define IMX415_WINMODE IMX415_REG_8BIT(0x301C)
+#define IMX415_ADDMODE IMX415_REG_8BIT(0x3022)
+#define IMX415_REVERSE IMX415_REG_8BIT(0x3030)
+#define IMX415_HREVERSE_SHIFT (0)
+#define IMX415_VREVERSE_SHIFT BIT(0)
+#define IMX415_ADBIT IMX415_REG_8BIT(0x3031)
+#define IMX415_MDBIT IMX415_REG_8BIT(0x3032)
+#define IMX415_SYS_MODE IMX415_REG_8BIT(0x3033)
+#define IMX415_OUTSEL IMX415_REG_8BIT(0x30C0)
+#define IMX415_DRV IMX415_REG_8BIT(0x30C1)
+#define IMX415_VMAX IMX415_REG_24BIT(0x3024)
+#define IMX415_HMAX IMX415_REG_16BIT(0x3028)
+#define IMX415_SHR0 IMX415_REG_24BIT(0x3050)
+#define IMX415_GAIN_PCG_0 IMX415_REG_16BIT(0x3090)
+#define IMX415_AGAIN_MIN 0
+#define IMX415_AGAIN_MAX 100
+#define IMX415_AGAIN_STEP 1
+#define IMX415_BLKLEVEL IMX415_REG_16BIT(0x30E2)
+#define IMX415_BLKLEVEL_DEFAULT 50
+#define IMX415_TPG_EN_DUOUT IMX415_REG_8BIT(0x30E4)
+#define IMX415_TPG_PATSEL_DUOUT IMX415_REG_8BIT(0x30E6)
+#define IMX415_TPG_COLORWIDTH IMX415_REG_8BIT(0x30E8)
+#define IMX415_TESTCLKEN_MIPI IMX415_REG_8BIT(0x3110)
+#define IMX415_INCKSEL1 IMX415_REG_8BIT(0x3115)
+#define IMX415_INCKSEL2 IMX415_REG_8BIT(0x3116)
+#define IMX415_INCKSEL3 IMX415_REG_16BIT(0x3118)
+#define IMX415_INCKSEL4 IMX415_REG_16BIT(0x311A)
+#define IMX415_INCKSEL5 IMX415_REG_8BIT(0x311E)
+#define IMX415_DIG_CLP_MODE IMX415_REG_8BIT(0x32C8)
+#define IMX415_WRJ_OPEN IMX415_REG_8BIT(0x3390)
+#define IMX415_SENSOR_INFO IMX415_REG_16BIT(0x3F12)
+#define IMX415_SENSOR_INFO_MASK 0xFFF
+#define IMX415_CHIP_ID 0x514
+#define IMX415_LANEMODE IMX415_REG_16BIT(0x4001)
+#define IMX415_LANEMODE_2 1
+#define IMX415_LANEMODE_4 3
+#define IMX415_TXCLKESC_FREQ IMX415_REG_16BIT(0x4004)
+#define IMX415_INCKSEL6 IMX415_REG_8BIT(0x400C)
+#define IMX415_TCLKPOST IMX415_REG_16BIT(0x4018)
+#define IMX415_TCLKPREPARE IMX415_REG_16BIT(0x401A)
+#define IMX415_TCLKTRAIL IMX415_REG_16BIT(0x401C)
+#define IMX415_TCLKZERO IMX415_REG_16BIT(0x401E)
+#define IMX415_THSPREPARE IMX415_REG_16BIT(0x4020)
+#define IMX415_THSZERO IMX415_REG_16BIT(0x4022)
+#define IMX415_THSTRAIL IMX415_REG_16BIT(0x4024)
+#define IMX415_THSEXIT IMX415_REG_16BIT(0x4026)
+#define IMX415_TLPX IMX415_REG_16BIT(0x4028)
+#define IMX415_INCKSEL7 IMX415_REG_8BIT(0x4074)
+
+struct imx415_reg {
+ u32 address;
+ u32 val;
+};
+
+static const char *const imx415_supply_names[] = {
+ "dvdd",
+ "ovdd",
+ "avdd",
+};
+
+/*
+ * The IMX415 data sheet uses lane rates but v4l2 uses link frequency to
+ * describe MIPI CSI-2 speed. This driver uses lane rates wherever possible
+ * and converts them to link frequencies by a factor of two when needed.
+ */
+static const s64 link_freq_menu_items[] = {
+ 594000000 / 2, 720000000 / 2, 891000000 / 2,
+ 1440000000 / 2, 1485000000 / 2,
+};
+
+struct imx415_clk_params {
+ u64 lane_rate;
+ u64 inck;
+ struct imx415_reg regs[IMX415_NUM_CLK_PARAM_REGS];
+};
+
+/* INCK Settings - includes all lane rate and INCK dependent registers */
+static const struct imx415_clk_params imx415_clk_params[] = {
+ {
+ .lane_rate = 594000000,
+ .inck = 27000000,
+ .regs[0] = { IMX415_BCWAIT_TIME, 0x05D },
+ .regs[1] = { IMX415_CPWAIT_TIME, 0x042 },
+ .regs[2] = { IMX415_SYS_MODE, 0x7 },
+ .regs[3] = { IMX415_INCKSEL1, 0x00 },
+ .regs[4] = { IMX415_INCKSEL2, 0x23 },
+ .regs[5] = { IMX415_INCKSEL3, 0x084 },
+ .regs[6] = { IMX415_INCKSEL4, 0x0E7 },
+ .regs[7] = { IMX415_INCKSEL5, 0x23 },
+ .regs[8] = { IMX415_INCKSEL6, 0x0 },
+ .regs[9] = { IMX415_INCKSEL7, 0x1 },
+ .regs[10] = { IMX415_TXCLKESC_FREQ, 0x06C0 },
+ },
+ {
+ .lane_rate = 720000000,
+ .inck = 24000000,
+ .regs[0] = { IMX415_BCWAIT_TIME, 0x054 },
+ .regs[1] = { IMX415_CPWAIT_TIME, 0x03B },
+ .regs[2] = { IMX415_SYS_MODE, 0x9 },
+ .regs[3] = { IMX415_INCKSEL1, 0x00 },
+ .regs[4] = { IMX415_INCKSEL2, 0x23 },
+ .regs[5] = { IMX415_INCKSEL3, 0x0B4 },
+ .regs[6] = { IMX415_INCKSEL4, 0x0FC },
+ .regs[7] = { IMX415_INCKSEL5, 0x23 },
+ .regs[8] = { IMX415_INCKSEL6, 0x0 },
+ .regs[9] = { IMX415_INCKSEL7, 0x1 },
+ .regs[10] = { IMX415_TXCLKESC_FREQ, 0x0600 },
+ },
+ {
+ .lane_rate = 891000000,
+ .inck = 27000000,
+ .regs[0] = { IMX415_BCWAIT_TIME, 0x05D },
+ .regs[1] = { IMX415_CPWAIT_TIME, 0x042 },
+ .regs[2] = { IMX415_SYS_MODE, 0x5 },
+ .regs[3] = { IMX415_INCKSEL1, 0x00 },
+ .regs[4] = { IMX415_INCKSEL2, 0x23 },
+ .regs[5] = { IMX415_INCKSEL3, 0x0C6 },
+ .regs[6] = { IMX415_INCKSEL4, 0x0E7 },
+ .regs[7] = { IMX415_INCKSEL5, 0x23 },
+ .regs[8] = { IMX415_INCKSEL6, 0x0 },
+ .regs[9] = { IMX415_INCKSEL7, 0x1 },
+ .regs[10] = { IMX415_TXCLKESC_FREQ, 0x06C0 },
+ },
+ {
+ .lane_rate = 1440000000,
+ .inck = 24000000,
+ .regs[0] = { IMX415_BCWAIT_TIME, 0x054 },
+ .regs[1] = { IMX415_CPWAIT_TIME, 0x03B },
+ .regs[2] = { IMX415_SYS_MODE, 0x8 },
+ .regs[3] = { IMX415_INCKSEL1, 0x00 },
+ .regs[4] = { IMX415_INCKSEL2, 0x23 },
+ .regs[5] = { IMX415_INCKSEL3, 0x0B4 },
+ .regs[6] = { IMX415_INCKSEL4, 0x0FC },
+ .regs[7] = { IMX415_INCKSEL5, 0x23 },
+ .regs[8] = { IMX415_INCKSEL6, 0x1 },
+ .regs[9] = { IMX415_INCKSEL7, 0x0 },
+ .regs[10] = { IMX415_TXCLKESC_FREQ, 0x0600 },
+ },
+ {
+ .lane_rate = 1485000000,
+ .inck = 27000000,
+ .regs[0] = { IMX415_BCWAIT_TIME, 0x05D },
+ .regs[1] = { IMX415_CPWAIT_TIME, 0x042 },
+ .regs[2] = { IMX415_SYS_MODE, 0x8 },
+ .regs[3] = { IMX415_INCKSEL1, 0x00 },
+ .regs[4] = { IMX415_INCKSEL2, 0x23 },
+ .regs[5] = { IMX415_INCKSEL3, 0x0A5 },
+ .regs[6] = { IMX415_INCKSEL4, 0x0E7 },
+ .regs[7] = { IMX415_INCKSEL5, 0x23 },
+ .regs[8] = { IMX415_INCKSEL6, 0x1 },
+ .regs[9] = { IMX415_INCKSEL7, 0x0 },
+ .regs[10] = { IMX415_TXCLKESC_FREQ, 0x06C0 },
+ },
+};
+
+/* all-pixel 2-lane 720 Mbps 15.74 Hz mode */
+static const struct imx415_reg imx415_mode_2_720[] = {
+ { IMX415_VMAX, 0x08CA },
+ { IMX415_HMAX, 0x07F0 },
+ { IMX415_LANEMODE, IMX415_LANEMODE_2 },
+ { IMX415_TCLKPOST, 0x006F },
+ { IMX415_TCLKPREPARE, 0x002F },
+ { IMX415_TCLKTRAIL, 0x002F },
+ { IMX415_TCLKZERO, 0x00BF },
+ { IMX415_THSPREPARE, 0x002F },
+ { IMX415_THSZERO, 0x0057 },
+ { IMX415_THSTRAIL, 0x002F },
+ { IMX415_THSEXIT, 0x004F },
+ { IMX415_TLPX, 0x0027 },
+};
+
+/* all-pixel 2-lane 1440 Mbps 30.01 Hz mode */
+static const struct imx415_reg imx415_mode_2_1440[] = {
+ { IMX415_VMAX, 0x08CA },
+ { IMX415_HMAX, 0x042A },
+ { IMX415_LANEMODE, IMX415_LANEMODE_2 },
+ { IMX415_TCLKPOST, 0x009F },
+ { IMX415_TCLKPREPARE, 0x0057 },
+ { IMX415_TCLKTRAIL, 0x0057 },
+ { IMX415_TCLKZERO, 0x0187 },
+ { IMX415_THSPREPARE, 0x005F },
+ { IMX415_THSZERO, 0x00A7 },
+ { IMX415_THSTRAIL, 0x005F },
+ { IMX415_THSEXIT, 0x0097 },
+ { IMX415_TLPX, 0x004F },
+};
+
+/* all-pixel 4-lane 891 Mbps 30 Hz mode */
+static const struct imx415_reg imx415_mode_4_891[] = {
+ { IMX415_VMAX, 0x08CA },
+ { IMX415_HMAX, 0x044C },
+ { IMX415_LANEMODE, IMX415_LANEMODE_4 },
+ { IMX415_TCLKPOST, 0x007F },
+ { IMX415_TCLKPREPARE, 0x0037 },
+ { IMX415_TCLKTRAIL, 0x0037 },
+ { IMX415_TCLKZERO, 0x00F7 },
+ { IMX415_THSPREPARE, 0x003F },
+ { IMX415_THSZERO, 0x006F },
+ { IMX415_THSTRAIL, 0x003F },
+ { IMX415_THSEXIT, 0x005F },
+ { IMX415_TLPX, 0x002F },
+};
+
+struct imx415_mode_reg_list {
+ u32 num_of_regs;
+ const struct imx415_reg *regs;
+};
+
+/*
+ * Mode : number of lanes, lane rate and frame rate dependent settings
+ *
+ * pixel_rate and hmax_pix are needed to calculate hblank for the v4l2 ctrl
+ * interface. These values can not be found in the data sheet and should be
+ * treated as virtual values. Use following table when adding new modes.
+ *
+ * lane_rate lanes fps hmax_pix pixel_rate
+ *
+ * 594 2 10.000 4400 99000000
+ * 891 2 15.000 4400 148500000
+ * 720 2 15.748 4064 144000000
+ * 1782 2 30.000 4400 297000000
+ * 2079 2 30.000 4400 297000000
+ * 1440 2 30.019 4510 304615385
+ *
+ * 594 4 20.000 5500 247500000
+ * 594 4 25.000 4400 247500000
+ * 720 4 25.000 4400 247500000
+ * 720 4 30.019 4510 304615385
+ * 891 4 30.000 4400 297000000
+ * 1440 4 30.019 4510 304615385
+ * 1440 4 60.038 4510 609230769
+ * 1485 4 60.000 4400 594000000
+ * 1782 4 60.000 4400 594000000
+ * 2079 4 60.000 4400 594000000
+ * 2376 4 90.164 4392 891000000
+ */
+struct imx415_mode {
+ u64 lane_rate;
+ u32 lanes;
+ u32 hmax_pix;
+ u64 pixel_rate;
+ struct imx415_mode_reg_list reg_list;
+};
+
+/* mode configs */
+static const struct imx415_mode supported_modes[] = {
+ {
+ .lane_rate = 720000000,
+ .lanes = 2,
+ .hmax_pix = 4064,
+ .pixel_rate = 144000000,
+ .reg_list = {
+ .num_of_regs = ARRAY_SIZE(imx415_mode_2_720),
+ .regs = imx415_mode_2_720,
+ },
+ },
+ {
+ .lane_rate = 1440000000,
+ .lanes = 2,
+ .hmax_pix = 4510,
+ .pixel_rate = 304615385,
+ .reg_list = {
+ .num_of_regs = ARRAY_SIZE(imx415_mode_2_1440),
+ .regs = imx415_mode_2_1440,
+ },
+ },
+ {
+ .lane_rate = 891000000,
+ .lanes = 4,
+ .hmax_pix = 4400,
+ .pixel_rate = 297000000,
+ .reg_list = {
+ .num_of_regs = ARRAY_SIZE(imx415_mode_4_891),
+ .regs = imx415_mode_4_891,
+ },
+ },
+};
+
+static const struct regmap_config imx415_regmap_config = {
+ .reg_bits = 16,
+ .val_bits = 8,
+};
+
+static const char *const imx415_test_pattern_menu[] = {
+ "disabled",
+ "solid black",
+ "solid white",
+ "solid dark gray",
+ "solid light gray",
+ "stripes light/dark grey",
+ "stripes dark/light grey",
+ "stripes black/dark grey",
+ "stripes dark grey/black",
+ "stripes black/white",
+ "stripes white/black",
+ "horizontal color bar",
+ "vertical color bar",
+};
+
+struct imx415 {
+ struct device *dev;
+ struct clk *clk;
+ struct regulator_bulk_data supplies[ARRAY_SIZE(imx415_supply_names)];
+ struct gpio_desc *reset;
+ struct regmap *regmap;
+
+ const struct imx415_clk_params *clk_params;
+
+ bool streaming;
+
+ struct v4l2_subdev subdev;
+ struct media_pad pad;
+
+ struct v4l2_ctrl_handler ctrls;
+ struct v4l2_ctrl *vblank;
+ struct v4l2_ctrl *hflip;
+ struct v4l2_ctrl *vflip;
+
+ unsigned int cur_mode;
+ unsigned int num_data_lanes;
+};
+
+/*
+ * This table includes fixed register settings and a bunch of undocumented
+ * registers that have to be set to another value than default.
+ */
+static const struct imx415_reg imx415_init_table[] = {
+ /* use all-pixel readout mode, no flip */
+ { IMX415_WINMODE, 0x00 },
+ { IMX415_ADDMODE, 0x00 },
+ { IMX415_REVERSE, 0x00 },
+ /* use RAW 10-bit mode */
+ { IMX415_ADBIT, 0x00 },
+ { IMX415_MDBIT, 0x00 },
+ /* output VSYNC on XVS and low on XHS */
+ { IMX415_OUTSEL, 0x22 },
+ { IMX415_DRV, 0x00 },
+
+ /* SONY magic registers */
+ { IMX415_REG_8BIT(0x32D4), 0x21 },
+ { IMX415_REG_8BIT(0x32EC), 0xA1 },
+ { IMX415_REG_8BIT(0x3452), 0x7F },
+ { IMX415_REG_8BIT(0x3453), 0x03 },
+ { IMX415_REG_8BIT(0x358A), 0x04 },
+ { IMX415_REG_8BIT(0x35A1), 0x02 },
+ { IMX415_REG_8BIT(0x36BC), 0x0C },
+ { IMX415_REG_8BIT(0x36CC), 0x53 },
+ { IMX415_REG_8BIT(0x36CD), 0x00 },
+ { IMX415_REG_8BIT(0x36CE), 0x3C },
+ { IMX415_REG_8BIT(0x36D0), 0x8C },
+ { IMX415_REG_8BIT(0x36D1), 0x00 },
+ { IMX415_REG_8BIT(0x36D2), 0x71 },
+ { IMX415_REG_8BIT(0x36D4), 0x3C },
+ { IMX415_REG_8BIT(0x36D6), 0x53 },
+ { IMX415_REG_8BIT(0x36D7), 0x00 },
+ { IMX415_REG_8BIT(0x36D8), 0x71 },
+ { IMX415_REG_8BIT(0x36DA), 0x8C },
+ { IMX415_REG_8BIT(0x36DB), 0x00 },
+ { IMX415_REG_8BIT(0x3724), 0x02 },
+ { IMX415_REG_8BIT(0x3726), 0x02 },
+ { IMX415_REG_8BIT(0x3732), 0x02 },
+ { IMX415_REG_8BIT(0x3734), 0x03 },
+ { IMX415_REG_8BIT(0x3736), 0x03 },
+ { IMX415_REG_8BIT(0x3742), 0x03 },
+ { IMX415_REG_8BIT(0x3862), 0xE0 },
+ { IMX415_REG_8BIT(0x38CC), 0x30 },
+ { IMX415_REG_8BIT(0x38CD), 0x2F },
+ { IMX415_REG_8BIT(0x395C), 0x0C },
+ { IMX415_REG_8BIT(0x3A42), 0xD1 },
+ { IMX415_REG_8BIT(0x3A4C), 0x77 },
+ { IMX415_REG_8BIT(0x3AE0), 0x02 },
+ { IMX415_REG_8BIT(0x3AEC), 0x0C },
+ { IMX415_REG_8BIT(0x3B00), 0x2E },
+ { IMX415_REG_8BIT(0x3B06), 0x29 },
+ { IMX415_REG_8BIT(0x3B98), 0x25 },
+ { IMX415_REG_8BIT(0x3B99), 0x21 },
+ { IMX415_REG_8BIT(0x3B9B), 0x13 },
+ { IMX415_REG_8BIT(0x3B9C), 0x13 },
+ { IMX415_REG_8BIT(0x3B9D), 0x13 },
+ { IMX415_REG_8BIT(0x3B9E), 0x13 },
+ { IMX415_REG_8BIT(0x3BA1), 0x00 },
+ { IMX415_REG_8BIT(0x3BA2), 0x06 },
+ { IMX415_REG_8BIT(0x3BA3), 0x0B },
+ { IMX415_REG_8BIT(0x3BA4), 0x10 },
+ { IMX415_REG_8BIT(0x3BA5), 0x14 },
+ { IMX415_REG_8BIT(0x3BA6), 0x18 },
+ { IMX415_REG_8BIT(0x3BA7), 0x1A },
+ { IMX415_REG_8BIT(0x3BA8), 0x1A },
+ { IMX415_REG_8BIT(0x3BA9), 0x1A },
+ { IMX415_REG_8BIT(0x3BAC), 0xED },
+ { IMX415_REG_8BIT(0x3BAD), 0x01 },
+ { IMX415_REG_8BIT(0x3BAE), 0xF6 },
+ { IMX415_REG_8BIT(0x3BAF), 0x02 },
+ { IMX415_REG_8BIT(0x3BB0), 0xA2 },
+ { IMX415_REG_8BIT(0x3BB1), 0x03 },
+ { IMX415_REG_8BIT(0x3BB2), 0xE0 },
+ { IMX415_REG_8BIT(0x3BB3), 0x03 },
+ { IMX415_REG_8BIT(0x3BB4), 0xE0 },
+ { IMX415_REG_8BIT(0x3BB5), 0x03 },
+ { IMX415_REG_8BIT(0x3BB6), 0xE0 },
+ { IMX415_REG_8BIT(0x3BB7), 0x03 },
+ { IMX415_REG_8BIT(0x3BB8), 0xE0 },
+ { IMX415_REG_8BIT(0x3BBA), 0xE0 },
+ { IMX415_REG_8BIT(0x3BBC), 0xDA },
+ { IMX415_REG_8BIT(0x3BBE), 0x88 },
+ { IMX415_REG_8BIT(0x3BC0), 0x44 },
+ { IMX415_REG_8BIT(0x3BC2), 0x7B },
+ { IMX415_REG_8BIT(0x3BC4), 0xA2 },
+ { IMX415_REG_8BIT(0x3BC8), 0xBD },
+ { IMX415_REG_8BIT(0x3BCA), 0xBD },
+};
+
+static inline struct imx415 *to_imx415(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct imx415, subdev);
+}
+
+static int imx415_read(struct imx415 *sensor, u32 addr)
+{
+ u8 data[3] = { 0 };
+ int ret;
+
+ ret = regmap_raw_read(sensor->regmap, addr & IMX415_REG_ADDR_MASK, data,
+ (addr >> IMX415_REG_SIZE_SHIFT) & 3);
+ if (ret < 0)
+ return ret;
+
+ return (data[2] << 16) | (data[1] << 8) | data[0];
+}
+
+static int imx415_write(struct imx415 *sensor, u32 addr, u32 value)
+{
+ u8 data[3] = { value & 0xff, (value >> 8) & 0xff, value >> 16 };
+ int ret;
+
+ ret = regmap_raw_write(sensor->regmap, addr & IMX415_REG_ADDR_MASK,
+ data, (addr >> IMX415_REG_SIZE_SHIFT) & 3);
+ if (ret < 0)
+ dev_err_ratelimited(sensor->dev,
+ "%u-bit write to 0x%04x failed: %d\n",
+ ((addr >> IMX415_REG_SIZE_SHIFT) & 3) * 8,
+ addr & IMX415_REG_ADDR_MASK, ret);
+
+ return 0;
+}
+
+static int imx415_set_testpattern(struct imx415 *sensor, int val)
+{
+ int ret;
+
+ if (val) {
+ ret = imx415_write(sensor, IMX415_BLKLEVEL, 0x00);
+ if (ret)
+ return ret;
+ ret = imx415_write(sensor, IMX415_TPG_EN_DUOUT, 0x01);
+ if (ret)
+ return ret;
+ ret = imx415_write(sensor, IMX415_TPG_PATSEL_DUOUT, val - 1);
+ if (ret)
+ return ret;
+ ret = imx415_write(sensor, IMX415_TPG_COLORWIDTH, 0x01);
+ if (ret)
+ return ret;
+ ret = imx415_write(sensor, IMX415_TESTCLKEN_MIPI, 0x20);
+ if (ret)
+ return ret;
+ ret = imx415_write(sensor, IMX415_DIG_CLP_MODE, 0x00);
+ if (ret)
+ return ret;
+ ret = imx415_write(sensor, IMX415_WRJ_OPEN, 0x00);
+ } else {
+ ret = imx415_write(sensor, IMX415_BLKLEVEL,
+ IMX415_BLKLEVEL_DEFAULT);
+ if (ret)
+ return ret;
+ ret = imx415_write(sensor, IMX415_TPG_EN_DUOUT, 0x00);
+ if (ret)
+ return ret;
+ ret = imx415_write(sensor, IMX415_TESTCLKEN_MIPI, 0x00);
+ if (ret)
+ return ret;
+ ret = imx415_write(sensor, IMX415_DIG_CLP_MODE, 0x01);
+ if (ret)
+ return ret;
+ ret = imx415_write(sensor, IMX415_WRJ_OPEN, 0x01);
+ }
+ return 0;
+}
+
+static int imx415_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct imx415 *sensor = container_of(ctrl->handler, struct imx415,
+ ctrls);
+ const struct v4l2_mbus_framefmt *format;
+ struct v4l2_subdev_state *state;
+ unsigned int vmax;
+ unsigned int flip;
+
+ if (!sensor->streaming)
+ return 0;
+
+ state = v4l2_subdev_get_locked_active_state(&sensor->subdev);
+ format = v4l2_subdev_get_pad_format(&sensor->subdev, state, 0);
+
+ switch (ctrl->id) {
+ case V4L2_CID_EXPOSURE:
+ /* clamp the exposure value to VMAX. */
+ vmax = format->height + sensor->vblank->cur.val;
+ ctrl->val = min_t(int, ctrl->val, vmax);
+ return imx415_write(sensor, IMX415_SHR0, vmax - ctrl->val);
+
+ case V4L2_CID_ANALOGUE_GAIN:
+ /* analogue gain in 0.3 dB step size */
+ return imx415_write(sensor, IMX415_GAIN_PCG_0, ctrl->val);
+
+ case V4L2_CID_HFLIP:
+ case V4L2_CID_VFLIP:
+ flip = (sensor->hflip->val << IMX415_HREVERSE_SHIFT) |
+ (sensor->vflip->val << IMX415_VREVERSE_SHIFT);
+ return imx415_write(sensor, IMX415_REVERSE, flip);
+
+ case V4L2_CID_TEST_PATTERN:
+ return imx415_set_testpattern(sensor, ctrl->val);
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct v4l2_ctrl_ops imx415_ctrl_ops = {
+ .s_ctrl = imx415_s_ctrl,
+};
+
+static int imx415_ctrls_init(struct imx415 *sensor)
+{
+ struct v4l2_fwnode_device_properties props;
+ struct v4l2_ctrl *ctrl;
+ u64 pixel_rate = supported_modes[sensor->cur_mode].pixel_rate;
+ u64 lane_rate = supported_modes[sensor->cur_mode].lane_rate;
+ u32 exposure_max = IMX415_PIXEL_ARRAY_HEIGHT +
+ IMX415_PIXEL_ARRAY_VBLANK - 8;
+ u32 hblank;
+ unsigned int i;
+ int ret;
+
+ ret = v4l2_fwnode_device_parse(sensor->dev, &props);
+ if (ret < 0)
+ return ret;
+
+ v4l2_ctrl_handler_init(&sensor->ctrls, 10);
+
+ for (i = 0; i < ARRAY_SIZE(link_freq_menu_items); ++i) {
+ if (lane_rate == link_freq_menu_items[i] * 2)
+ break;
+ }
+ if (i == ARRAY_SIZE(link_freq_menu_items)) {
+ return dev_err_probe(sensor->dev, -EINVAL,
+ "lane rate %llu not supported\n",
+ lane_rate);
+ }
+
+ ctrl = v4l2_ctrl_new_int_menu(&sensor->ctrls, &imx415_ctrl_ops,
+ V4L2_CID_LINK_FREQ,
+ ARRAY_SIZE(link_freq_menu_items) - 1, i,
+ link_freq_menu_items);
+
+ if (ctrl)
+ ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+ v4l2_ctrl_new_std(&sensor->ctrls, &imx415_ctrl_ops, V4L2_CID_EXPOSURE,
+ 4, exposure_max, 1, exposure_max);
+
+ v4l2_ctrl_new_std(&sensor->ctrls, &imx415_ctrl_ops,
+ V4L2_CID_ANALOGUE_GAIN, IMX415_AGAIN_MIN,
+ IMX415_AGAIN_MAX, IMX415_AGAIN_STEP,
+ IMX415_AGAIN_MIN);
+
+ hblank = supported_modes[sensor->cur_mode].hmax_pix -
+ IMX415_PIXEL_ARRAY_WIDTH;
+ ctrl = v4l2_ctrl_new_std(&sensor->ctrls, &imx415_ctrl_ops,
+ V4L2_CID_HBLANK, hblank, hblank, 1, hblank);
+ if (ctrl)
+ ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+ sensor->vblank = v4l2_ctrl_new_std(&sensor->ctrls, &imx415_ctrl_ops,
+ V4L2_CID_VBLANK,
+ IMX415_PIXEL_ARRAY_VBLANK,
+ IMX415_PIXEL_ARRAY_VBLANK, 1,
+ IMX415_PIXEL_ARRAY_VBLANK);
+ if (sensor->vblank)
+ sensor->vblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+ /*
+ * The pixel rate used here is a virtual value and can be used for
+ * calculating the frame rate together with hblank. It may not
+ * necessarily be the physically correct pixel clock.
+ */
+ v4l2_ctrl_new_std(&sensor->ctrls, NULL, V4L2_CID_PIXEL_RATE, pixel_rate,
+ pixel_rate, 1, pixel_rate);
+
+ sensor->hflip = v4l2_ctrl_new_std(&sensor->ctrls, &imx415_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ sensor->vflip = v4l2_ctrl_new_std(&sensor->ctrls, &imx415_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+
+ v4l2_ctrl_new_std_menu_items(&sensor->ctrls, &imx415_ctrl_ops,
+ V4L2_CID_TEST_PATTERN,
+ ARRAY_SIZE(imx415_test_pattern_menu) - 1,
+ 0, 0, imx415_test_pattern_menu);
+
+ v4l2_ctrl_new_fwnode_properties(&sensor->ctrls, &imx415_ctrl_ops,
+ &props);
+
+ if (sensor->ctrls.error) {
+ dev_err_probe(sensor->dev, sensor->ctrls.error,
+ "failed to add controls\n");
+ v4l2_ctrl_handler_free(&sensor->ctrls);
+ return sensor->ctrls.error;
+ }
+ sensor->subdev.ctrl_handler = &sensor->ctrls;
+
+ return 0;
+}
+
+static int imx415_set_mode(struct imx415 *sensor, int mode)
+{
+ const struct imx415_reg *reg;
+ unsigned int i;
+ int ret = 0;
+
+ if (mode >= ARRAY_SIZE(supported_modes)) {
+ dev_err(sensor->dev, "Mode %d not supported\n", mode);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < supported_modes[mode].reg_list.num_of_regs; ++i) {
+ reg = &supported_modes[mode].reg_list.regs[i];
+ ret = imx415_write(sensor, reg->address, reg->val);
+ if (ret)
+ return ret;
+ }
+
+ for (i = 0; i < IMX415_NUM_CLK_PARAM_REGS; ++i) {
+ reg = &sensor->clk_params->regs[i];
+ ret = imx415_write(sensor, reg->address, reg->val);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int imx415_setup(struct imx415 *sensor, struct v4l2_subdev_state *state)
+{
+ unsigned int i;
+ int ret;
+
+ for (i = 0; i < ARRAY_SIZE(imx415_init_table); ++i) {
+ ret = imx415_write(sensor, imx415_init_table[i].address,
+ imx415_init_table[i].val);
+ if (ret)
+ return ret;
+ }
+
+ return imx415_set_mode(sensor, sensor->cur_mode);
+}
+
+static int imx415_wakeup(struct imx415 *sensor)
+{
+ int ret;
+
+ ret = imx415_write(sensor, IMX415_MODE, IMX415_MODE_OPERATING);
+ if (ret)
+ return ret;
+
+ /*
+ * According to the datasheet we have to wait at least 63 us after
+ * leaving standby mode. But this doesn't work even after 30 ms.
+ * So probably this should be 63 ms and therefore we wait for 80 ms.
+ */
+ msleep(80);
+
+ return 0;
+}
+
+static int imx415_stream_on(struct imx415 *sensor)
+{
+ int ret;
+
+ ret = imx415_wakeup(sensor);
+ if (ret)
+ return ret;
+
+ return imx415_write(sensor, IMX415_XMSTA, IMX415_XMSTA_START);
+}
+
+static int imx415_stream_off(struct imx415 *sensor)
+{
+ int ret;
+
+ ret = imx415_write(sensor, IMX415_XMSTA, IMX415_XMSTA_STOP);
+ if (ret)
+ return ret;
+
+ return imx415_write(sensor, IMX415_MODE, IMX415_MODE_STANDBY);
+}
+
+static int imx415_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct imx415 *sensor = to_imx415(sd);
+ struct v4l2_subdev_state *state;
+ int ret;
+
+ state = v4l2_subdev_lock_and_get_active_state(sd);
+
+ if (!enable) {
+ ret = imx415_stream_off(sensor);
+
+ pm_runtime_mark_last_busy(sensor->dev);
+ pm_runtime_put_autosuspend(sensor->dev);
+
+ sensor->streaming = false;
+
+ goto unlock;
+ }
+
+ ret = pm_runtime_resume_and_get(sensor->dev);
+ if (ret < 0)
+ goto unlock;
+
+ ret = imx415_setup(sensor, state);
+ if (ret)
+ goto err_pm;
+
+ /*
+ * Set streaming to true to ensure __v4l2_ctrl_handler_setup() will set
+ * the controls. The flag is reset to false further down if an error
+ * occurs.
+ */
+ sensor->streaming = true;
+
+ ret = __v4l2_ctrl_handler_setup(&sensor->ctrls);
+ if (ret < 0)
+ goto err_pm;
+
+ ret = imx415_stream_on(sensor);
+ if (ret)
+ goto err_pm;
+
+ ret = 0;
+
+unlock:
+ v4l2_subdev_unlock_state(state);
+
+ return ret;
+
+err_pm:
+ /*
+ * In case of error, turn the power off synchronously as the device
+ * likely has no other chance to recover.
+ */
+ pm_runtime_put_sync(sensor->dev);
+ sensor->streaming = false;
+
+ goto unlock;
+}
+
+static int imx415_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ if (code->index != 0)
+ return -EINVAL;
+
+ code->code = MEDIA_BUS_FMT_SGBRG10_1X10;
+
+ return 0;
+}
+
+static int imx415_enum_frame_size(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ const struct v4l2_mbus_framefmt *format;
+
+ format = v4l2_subdev_get_pad_format(sd, state, fse->pad);
+
+ if (fse->index > 0 || fse->code != format->code)
+ return -EINVAL;
+
+ fse->min_width = IMX415_PIXEL_ARRAY_WIDTH;
+ fse->max_width = fse->min_width;
+ fse->min_height = IMX415_PIXEL_ARRAY_HEIGHT;
+ fse->max_height = fse->min_height;
+ return 0;
+}
+
+static int imx415_get_format(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_format *fmt)
+{
+ fmt->format = *v4l2_subdev_get_pad_format(sd, state, fmt->pad);
+
+ return 0;
+}
+
+static int imx415_set_format(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_format *fmt)
+{
+ struct v4l2_mbus_framefmt *format;
+
+ format = v4l2_subdev_get_pad_format(sd, state, fmt->pad);
+
+ format->width = fmt->format.width;
+ format->height = fmt->format.height;
+ format->code = MEDIA_BUS_FMT_SGBRG10_1X10;
+ format->field = V4L2_FIELD_NONE;
+ format->colorspace = V4L2_COLORSPACE_RAW;
+ format->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
+ format->quantization = V4L2_QUANTIZATION_DEFAULT;
+ format->xfer_func = V4L2_XFER_FUNC_NONE;
+
+ fmt->format = *format;
+ return 0;
+}
+
+static int imx415_get_selection(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_selection *sel)
+{
+ switch (sel->target) {
+ case V4L2_SEL_TGT_CROP:
+ case V4L2_SEL_TGT_CROP_DEFAULT:
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ sel->r.top = IMX415_PIXEL_ARRAY_TOP;
+ sel->r.left = IMX415_PIXEL_ARRAY_LEFT;
+ sel->r.width = IMX415_PIXEL_ARRAY_WIDTH;
+ sel->r.height = IMX415_PIXEL_ARRAY_HEIGHT;
+
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int imx415_init_cfg(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state)
+{
+ struct v4l2_subdev_format format = {
+ .format = {
+ .width = IMX415_PIXEL_ARRAY_WIDTH,
+ .height = IMX415_PIXEL_ARRAY_HEIGHT,
+ },
+ };
+
+ imx415_set_format(sd, state, &format);
+
+ return 0;
+}
+
+static const struct v4l2_subdev_video_ops imx415_subdev_video_ops = {
+ .s_stream = imx415_s_stream,
+};
+
+static const struct v4l2_subdev_pad_ops imx415_subdev_pad_ops = {
+ .enum_mbus_code = imx415_enum_mbus_code,
+ .enum_frame_size = imx415_enum_frame_size,
+ .get_fmt = imx415_get_format,
+ .set_fmt = imx415_set_format,
+ .get_selection = imx415_get_selection,
+ .init_cfg = imx415_init_cfg,
+};
+
+static const struct v4l2_subdev_ops imx415_subdev_ops = {
+ .video = &imx415_subdev_video_ops,
+ .pad = &imx415_subdev_pad_ops,
+};
+
+static int imx415_subdev_init(struct imx415 *sensor)
+{
+ struct i2c_client *client = to_i2c_client(sensor->dev);
+ int ret;
+
+ v4l2_i2c_subdev_init(&sensor->subdev, client, &imx415_subdev_ops);
+
+ ret = imx415_ctrls_init(sensor);
+ if (ret)
+ return ret;
+
+ sensor->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
+ V4L2_SUBDEV_FL_HAS_EVENTS;
+ sensor->pad.flags = MEDIA_PAD_FL_SOURCE;
+ sensor->subdev.entity.function = MEDIA_ENT_F_CAM_SENSOR;
+ ret = media_entity_pads_init(&sensor->subdev.entity, 1, &sensor->pad);
+ if (ret < 0) {
+ v4l2_ctrl_handler_free(&sensor->ctrls);
+ return ret;
+ }
+
+ sensor->subdev.state_lock = sensor->subdev.ctrl_handler->lock;
+ v4l2_subdev_init_finalize(&sensor->subdev);
+
+ return 0;
+}
+
+static void imx415_subdev_cleanup(struct imx415 *sensor)
+{
+ media_entity_cleanup(&sensor->subdev.entity);
+ v4l2_ctrl_handler_free(&sensor->ctrls);
+}
+
+static int imx415_power_on(struct imx415 *sensor)
+{
+ int ret;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(sensor->supplies),
+ sensor->supplies);
+ if (ret < 0)
+ return ret;
+
+ gpiod_set_value_cansleep(sensor->reset, 0);
+
+ udelay(1);
+
+ ret = clk_prepare_enable(sensor->clk);
+ if (ret < 0)
+ goto err_reset;
+
+ /*
+ * Data sheet states that 20 us are required before communication start,
+ * but this doesn't work in all cases. Use 100 us to be on the safe
+ * side.
+ */
+ usleep_range(100, 200);
+
+ return 0;
+
+err_reset:
+ gpiod_set_value_cansleep(sensor->reset, 1);
+ regulator_bulk_disable(ARRAY_SIZE(sensor->supplies), sensor->supplies);
+ return ret;
+}
+
+static void imx415_power_off(struct imx415 *sensor)
+{
+ clk_disable_unprepare(sensor->clk);
+ gpiod_set_value_cansleep(sensor->reset, 1);
+ regulator_bulk_disable(ARRAY_SIZE(sensor->supplies), sensor->supplies);
+}
+
+static int imx415_identify_model(struct imx415 *sensor)
+{
+ int model, ret;
+
+ /*
+ * While most registers can be read when the sensor is in standby, this
+ * is not the case of the sensor info register :-(
+ */
+ ret = imx415_wakeup(sensor);
+ if (ret)
+ return dev_err_probe(sensor->dev, ret,
+ "failed to get sensor out of standby\n");
+
+ ret = imx415_read(sensor, IMX415_SENSOR_INFO);
+ if (ret < 0) {
+ dev_err_probe(sensor->dev, ret,
+ "failed to read sensor information\n");
+ goto done;
+ }
+
+ model = ret & IMX415_SENSOR_INFO_MASK;
+
+ switch (model) {
+ case IMX415_CHIP_ID:
+ dev_info(sensor->dev, "Detected IMX415 image sensor\n");
+ break;
+ default:
+ ret = dev_err_probe(sensor->dev, -ENODEV,
+ "invalid device model 0x%04x\n", model);
+ goto done;
+ }
+
+ ret = 0;
+
+done:
+ imx415_write(sensor, IMX415_MODE, IMX415_MODE_STANDBY);
+ return ret;
+}
+
+static int imx415_check_inck(unsigned long inck, u64 link_frequency)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(imx415_clk_params); ++i) {
+ if ((imx415_clk_params[i].lane_rate == link_frequency * 2) &&
+ imx415_clk_params[i].inck == inck)
+ break;
+ }
+
+ if (i == ARRAY_SIZE(imx415_clk_params))
+ return -EINVAL;
+ else
+ return 0;
+}
+
+static int imx415_parse_hw_config(struct imx415 *sensor)
+{
+ struct v4l2_fwnode_endpoint bus_cfg = {
+ .bus_type = V4L2_MBUS_CSI2_DPHY,
+ };
+ struct fwnode_handle *ep;
+ u64 lane_rate;
+ unsigned long inck;
+ unsigned int i, j;
+ int ret;
+
+ for (i = 0; i < ARRAY_SIZE(sensor->supplies); ++i)
+ sensor->supplies[i].supply = imx415_supply_names[i];
+
+ ret = devm_regulator_bulk_get(sensor->dev, ARRAY_SIZE(sensor->supplies),
+ sensor->supplies);
+ if (ret)
+ return dev_err_probe(sensor->dev, ret,
+ "failed to get supplies\n");
+
+ sensor->reset = devm_gpiod_get_optional(sensor->dev, "reset",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(sensor->reset))
+ return dev_err_probe(sensor->dev, PTR_ERR(sensor->reset),
+ "failed to get reset GPIO\n");
+
+ sensor->clk = devm_clk_get(sensor->dev, "inck");
+ if (IS_ERR(sensor->clk))
+ return dev_err_probe(sensor->dev, PTR_ERR(sensor->clk),
+ "failed to get clock\n");
+
+ ep = fwnode_graph_get_next_endpoint(dev_fwnode(sensor->dev), NULL);
+ if (!ep)
+ return -ENXIO;
+
+ ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg);
+ fwnode_handle_put(ep);
+ if (ret)
+ return ret;
+
+ switch (bus_cfg.bus.mipi_csi2.num_data_lanes) {
+ case 2:
+ case 4:
+ sensor->num_data_lanes = bus_cfg.bus.mipi_csi2.num_data_lanes;
+ break;
+ default:
+ ret = dev_err_probe(sensor->dev, -EINVAL,
+ "invalid number of CSI2 data lanes %d\n",
+ bus_cfg.bus.mipi_csi2.num_data_lanes);
+ goto done_endpoint_free;
+ }
+
+ if (!bus_cfg.nr_of_link_frequencies) {
+ ret = dev_err_probe(sensor->dev, -EINVAL,
+ "no link frequencies defined");
+ goto done_endpoint_free;
+ }
+
+ /*
+ * Check if there exists a sensor mode defined for current INCK,
+ * number of lanes and given lane rates.
+ */
+ inck = clk_get_rate(sensor->clk);
+ for (i = 0; i < bus_cfg.nr_of_link_frequencies; ++i) {
+ if (imx415_check_inck(inck, bus_cfg.link_frequencies[i])) {
+ dev_dbg(sensor->dev,
+ "INCK %lu Hz not supported for this link freq",
+ inck);
+ continue;
+ }
+
+ for (j = 0; j < ARRAY_SIZE(supported_modes); ++j) {
+ if (sensor->num_data_lanes != supported_modes[j].lanes)
+ continue;
+ if (bus_cfg.link_frequencies[i] * 2 !=
+ supported_modes[j].lane_rate)
+ continue;
+ sensor->cur_mode = j;
+ break;
+ }
+ if (j < ARRAY_SIZE(supported_modes))
+ break;
+ }
+ if (i == bus_cfg.nr_of_link_frequencies) {
+ ret = dev_err_probe(sensor->dev, -EINVAL,
+ "no valid sensor mode defined\n");
+ goto done_endpoint_free;
+ }
+
+ lane_rate = supported_modes[sensor->cur_mode].lane_rate;
+ for (i = 0; i < ARRAY_SIZE(imx415_clk_params); ++i) {
+ if (lane_rate == imx415_clk_params[i].lane_rate &&
+ inck == imx415_clk_params[i].inck) {
+ sensor->clk_params = &imx415_clk_params[i];
+ break;
+ }
+ }
+ if (i == ARRAY_SIZE(imx415_clk_params)) {
+ ret = dev_err_probe(sensor->dev, -EINVAL,
+ "Mode %d not supported\n",
+ sensor->cur_mode);
+ goto done_endpoint_free;
+ }
+
+ ret = 0;
+ dev_dbg(sensor->dev, "clock: %lu Hz, lane_rate: %llu bps, lanes: %d\n",
+ inck, lane_rate, sensor->num_data_lanes);
+
+done_endpoint_free:
+ v4l2_fwnode_endpoint_free(&bus_cfg);
+
+ return ret;
+}
+
+static int imx415_probe(struct i2c_client *client)
+{
+ struct imx415 *sensor;
+ int ret;
+
+ sensor = devm_kzalloc(&client->dev, sizeof(*sensor), GFP_KERNEL);
+ if (!sensor)
+ return -ENOMEM;
+
+ sensor->dev = &client->dev;
+
+ ret = imx415_parse_hw_config(sensor);
+ if (ret)
+ return ret;
+
+ sensor->regmap = devm_regmap_init_i2c(client, &imx415_regmap_config);
+ if (IS_ERR(sensor->regmap))
+ return PTR_ERR(sensor->regmap);
+
+ /*
+ * Enable power management. The driver supports runtime PM, but needs to
+ * work when runtime PM is disabled in the kernel. To that end, power
+ * the sensor on manually here, identify it, and fully initialize it.
+ */
+ ret = imx415_power_on(sensor);
+ if (ret)
+ return ret;
+
+ ret = imx415_identify_model(sensor);
+ if (ret)
+ goto err_power;
+
+ ret = imx415_subdev_init(sensor);
+ if (ret)
+ goto err_power;
+
+ /*
+ * Enable runtime PM. As the device has been powered manually, mark it
+ * as active, and increase the usage count without resuming the device.
+ */
+ pm_runtime_set_active(sensor->dev);
+ pm_runtime_get_noresume(sensor->dev);
+ pm_runtime_enable(sensor->dev);
+
+ ret = v4l2_async_register_subdev_sensor(&sensor->subdev);
+ if (ret < 0)
+ goto err_pm;
+
+ /*
+ * Finally, enable autosuspend and decrease the usage count. The device
+ * will get suspended after the autosuspend delay, turning the power
+ * off.
+ */
+ pm_runtime_set_autosuspend_delay(sensor->dev, 1000);
+ pm_runtime_use_autosuspend(sensor->dev);
+ pm_runtime_put_autosuspend(sensor->dev);
+
+ return 0;
+
+err_pm:
+ pm_runtime_disable(sensor->dev);
+ pm_runtime_put_noidle(sensor->dev);
+ imx415_subdev_cleanup(sensor);
+err_power:
+ imx415_power_off(sensor);
+ return ret;
+}
+
+static void imx415_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *subdev = i2c_get_clientdata(client);
+ struct imx415 *sensor = to_imx415(subdev);
+
+ v4l2_async_unregister_subdev(subdev);
+
+ imx415_subdev_cleanup(sensor);
+
+ /*
+ * Disable runtime PM. In case runtime PM is disabled in the kernel,
+ * make sure to turn power off manually.
+ */
+ pm_runtime_disable(sensor->dev);
+ if (!pm_runtime_status_suspended(sensor->dev))
+ imx415_power_off(sensor);
+ pm_runtime_set_suspended(sensor->dev);
+}
+
+static int imx415_runtime_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct v4l2_subdev *subdev = i2c_get_clientdata(client);
+ struct imx415 *sensor = to_imx415(subdev);
+
+ return imx415_power_on(sensor);
+}
+
+static int imx415_runtime_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct v4l2_subdev *subdev = i2c_get_clientdata(client);
+ struct imx415 *sensor = to_imx415(subdev);
+
+ imx415_power_off(sensor);
+
+ return 0;
+}
+
+static DEFINE_RUNTIME_DEV_PM_OPS(imx415_pm_ops, imx415_runtime_suspend,
+ imx415_runtime_resume, NULL);
+
+static const struct of_device_id imx415_of_match[] = {
+ { .compatible = "sony,imx415" },
+ { /* sentinel */ }
+};
+
+MODULE_DEVICE_TABLE(of, imx415_of_match);
+
+static struct i2c_driver imx415_driver = {
+ .probe_new = imx415_probe,
+ .remove = imx415_remove,
+ .driver = {
+ .name = "imx415",
+ .of_match_table = imx415_of_match,
+ .pm = pm_ptr(&imx415_pm_ops),
+ },
+};
+
+module_i2c_driver(imx415_driver);
+
+MODULE_DESCRIPTION("Sony IMX415 image sensor driver");
+MODULE_AUTHOR("Gerald Loacker <gerald.loacker@wolfvision.net>");
+MODULE_AUTHOR("Michael Riesch <michael.riesch@wolfvision.net>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/i2c/ir-kbd-i2c.c b/drivers/media/i2c/ir-kbd-i2c.c
index 25bf1132dbff..51921068931d 100644
--- a/drivers/media/i2c/ir-kbd-i2c.c
+++ b/drivers/media/i2c/ir-kbd-i2c.c
@@ -757,8 +757,9 @@ static int zilog_tx_duty_cycle(struct rc_dev *dev, u32 duty_cycle)
return 0;
}
-static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id)
+static int ir_probe(struct i2c_client *client)
{
+ const struct i2c_device_id *id = i2c_client_get_device_id(client);
char *ir_codes = NULL;
const char *name = NULL;
u64 rc_proto = RC_PROTO_BIT_UNKNOWN;
@@ -987,7 +988,7 @@ static struct i2c_driver ir_kbd_driver = {
.driver = {
.name = "ir-kbd-i2c",
},
- .probe = ir_probe,
+ .probe_new = ir_probe,
.remove = ir_remove,
.id_table = ir_kbd_id,
};
diff --git a/drivers/media/i2c/max9286.c b/drivers/media/i2c/max9286.c
index 9c083cf14231..701038d6d19b 100644
--- a/drivers/media/i2c/max9286.c
+++ b/drivers/media/i2c/max9286.c
@@ -72,7 +72,7 @@
#define MAX9286_DATATYPE_USER_YUV_12BIT (10 << 0)
#define MAX9286_DATATYPE_USER_24BIT (9 << 0)
#define MAX9286_DATATYPE_RAW14 (8 << 0)
-#define MAX9286_DATATYPE_RAW11 (7 << 0)
+#define MAX9286_DATATYPE_RAW12 (7 << 0)
#define MAX9286_DATATYPE_RAW10 (6 << 0)
#define MAX9286_DATATYPE_RAW8 (5 << 0)
#define MAX9286_DATATYPE_YUV422_10BIT (4 << 0)
@@ -81,13 +81,21 @@
#define MAX9286_DATATYPE_RGB565 (1 << 0)
#define MAX9286_DATATYPE_RGB888 (0 << 0)
/* Register 0x15 */
+#define MAX9286_CSI_IMAGE_TYP BIT(7)
#define MAX9286_VC(n) ((n) << 5)
#define MAX9286_VCTYPE BIT(4)
#define MAX9286_CSIOUTEN BIT(3)
-#define MAX9286_0X15_RESV (3 << 0)
+#define MAX9286_SWP_ENDIAN BIT(2)
+#define MAX9286_EN_CCBSYB_CLK_STR BIT(1)
+#define MAX9286_EN_GPI_CCBSYB BIT(0)
/* Register 0x1b */
#define MAX9286_SWITCHIN(n) (1 << ((n) + 4))
#define MAX9286_ENEQ(n) (1 << (n))
+/* Register 0x1c */
+#define MAX9286_HIGHIMM(n) BIT((n) + 4)
+#define MAX9286_I2CSEL BIT(2)
+#define MAX9286_HIBW BIT(1)
+#define MAX9286_BWS BIT(0)
/* Register 0x27 */
#define MAX9286_LOCKED BIT(7)
/* Register 0x31 */
@@ -136,9 +144,20 @@
#define MAX9286_N_PADS 5
#define MAX9286_SRC_PAD 4
+struct max9286_format_info {
+ u32 code;
+ u8 datatype;
+};
+
+struct max9286_i2c_speed {
+ u32 rate;
+ u8 mstbt;
+};
+
struct max9286_source {
struct v4l2_subdev *sd;
struct fwnode_handle *fwnode;
+ struct regulator *regulator;
};
struct max9286_asd {
@@ -168,13 +187,18 @@ struct max9286_priv {
/* The initial reverse control channel amplitude. */
u32 init_rev_chan_mv;
u32 rev_chan_mv;
+ u8 i2c_mstbt;
+ u32 bus_width;
+ bool use_gpio_poc;
u32 gpio_poc[2];
struct v4l2_ctrl_handler ctrls;
- struct v4l2_ctrl *pixelrate;
+ struct v4l2_ctrl *pixelrate_ctrl;
+ unsigned int pixelrate;
struct v4l2_mbus_framefmt fmt[MAX9286_N_SINKS];
+ struct v4l2_fract interval;
/* Protects controls and fmt structures */
struct mutex mutex;
@@ -214,6 +238,45 @@ static inline struct max9286_priv *sd_to_max9286(struct v4l2_subdev *sd)
return container_of(sd, struct max9286_priv, sd);
}
+static const struct max9286_format_info max9286_formats[] = {
+ {
+ .code = MEDIA_BUS_FMT_UYVY8_1X16,
+ .datatype = MAX9286_DATATYPE_YUV422_8BIT,
+ }, {
+ .code = MEDIA_BUS_FMT_VYUY8_1X16,
+ .datatype = MAX9286_DATATYPE_YUV422_8BIT,
+ }, {
+ .code = MEDIA_BUS_FMT_YUYV8_1X16,
+ .datatype = MAX9286_DATATYPE_YUV422_8BIT,
+ }, {
+ .code = MEDIA_BUS_FMT_YVYU8_1X16,
+ .datatype = MAX9286_DATATYPE_YUV422_8BIT,
+ }, {
+ .code = MEDIA_BUS_FMT_SBGGR12_1X12,
+ .datatype = MAX9286_DATATYPE_RAW12,
+ }, {
+ .code = MEDIA_BUS_FMT_SGBRG12_1X12,
+ .datatype = MAX9286_DATATYPE_RAW12,
+ }, {
+ .code = MEDIA_BUS_FMT_SGRBG12_1X12,
+ .datatype = MAX9286_DATATYPE_RAW12,
+ }, {
+ .code = MEDIA_BUS_FMT_SRGGB12_1X12,
+ .datatype = MAX9286_DATATYPE_RAW12,
+ },
+};
+
+static const struct max9286_i2c_speed max9286_i2c_speeds[] = {
+ { .rate = 8470, .mstbt = MAX9286_I2CMSTBT_8KBPS },
+ { .rate = 28300, .mstbt = MAX9286_I2CMSTBT_28KBPS },
+ { .rate = 84700, .mstbt = MAX9286_I2CMSTBT_84KBPS },
+ { .rate = 105000, .mstbt = MAX9286_I2CMSTBT_105KBPS },
+ { .rate = 173000, .mstbt = MAX9286_I2CMSTBT_173KBPS },
+ { .rate = 339000, .mstbt = MAX9286_I2CMSTBT_339KBPS },
+ { .rate = 533000, .mstbt = MAX9286_I2CMSTBT_533KBPS },
+ { .rate = 837000, .mstbt = MAX9286_I2CMSTBT_837KBPS },
+};
+
/* -----------------------------------------------------------------------------
* I2C IO
*/
@@ -334,7 +397,7 @@ error:
static void max9286_configure_i2c(struct max9286_priv *priv, bool localack)
{
u8 config = MAX9286_I2CSLVSH_469NS_234NS | MAX9286_I2CSLVTO_1024US |
- MAX9286_I2CMSTBT_105KBPS;
+ priv->i2c_mstbt;
if (localack)
config |= MAX9286_I2CLOCACK;
@@ -475,6 +538,77 @@ static int max9286_check_config_link(struct max9286_priv *priv,
return 0;
}
+static void max9286_set_video_format(struct max9286_priv *priv,
+ const struct v4l2_mbus_framefmt *format)
+{
+ const struct max9286_format_info *info = NULL;
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(max9286_formats); ++i) {
+ if (max9286_formats[i].code == format->code) {
+ info = &max9286_formats[i];
+ break;
+ }
+ }
+
+ if (WARN_ON(!info))
+ return;
+
+ /*
+ * Video format setup: disable CSI output, set VC according to Link
+ * number, enable I2C clock stretching when CCBSY is low, enable CCBSY
+ * in external GPI-to-GPO mode.
+ */
+ max9286_write(priv, 0x15, MAX9286_VCTYPE | MAX9286_EN_CCBSYB_CLK_STR |
+ MAX9286_EN_GPI_CCBSYB);
+
+ /* Enable CSI-2 Lane D0-D3 only, DBL mode. */
+ max9286_write(priv, 0x12, MAX9286_CSIDBL | MAX9286_DBL |
+ MAX9286_CSILANECNT(priv->csi2_data_lanes) |
+ info->datatype);
+
+ /*
+ * Enable HS/VS encoding, use HS as line valid source, use D14/15 for
+ * HS/VS, invert VS.
+ */
+ max9286_write(priv, 0x0c, MAX9286_HVEN | MAX9286_DESEL |
+ MAX9286_INVVS | MAX9286_HVSRC_D14);
+}
+
+static void max9286_set_fsync_period(struct max9286_priv *priv)
+{
+ u32 fsync;
+
+ if (!priv->interval.numerator || !priv->interval.denominator) {
+ /*
+ * Special case, a null interval enables automatic FRAMESYNC
+ * mode. FRAMESYNC is taken from the slowest link.
+ */
+ max9286_write(priv, 0x01, MAX9286_FSYNCMODE_INT_HIZ |
+ MAX9286_FSYNCMETH_AUTO);
+ return;
+ }
+
+ /*
+ * Manual FRAMESYNC
+ *
+ * The FRAMESYNC generator is configured with a period expressed as a
+ * number of PCLK periods.
+ */
+ fsync = div_u64((u64)priv->pixelrate * priv->interval.numerator,
+ priv->interval.denominator);
+
+ dev_dbg(&priv->client->dev, "fsync period %u (pclk %u)\n", fsync,
+ priv->pixelrate);
+
+ max9286_write(priv, 0x01, MAX9286_FSYNCMODE_INT_OUT |
+ MAX9286_FSYNCMETH_MANUAL);
+
+ max9286_write(priv, 0x06, (fsync >> 0) & 0xff);
+ max9286_write(priv, 0x07, (fsync >> 8) & 0xff);
+ max9286_write(priv, 0x08, (fsync >> 16) & 0xff);
+}
+
/* -----------------------------------------------------------------------------
* V4L2 Subdev
*/
@@ -513,11 +647,13 @@ static int max9286_set_pixelrate(struct max9286_priv *priv)
return -EINVAL;
}
+ priv->pixelrate = pixelrate;
+
/*
* The CSI-2 transmitter pixel rate is the single source rate multiplied
* by the number of available sources.
*/
- return v4l2_ctrl_s_ctrl_int64(priv->pixelrate,
+ return v4l2_ctrl_s_ctrl_int64(priv->pixelrate_ctrl,
pixelrate * priv->nsources);
}
@@ -657,6 +793,17 @@ static int max9286_s_stream(struct v4l2_subdev *sd, int enable)
int ret;
if (enable) {
+ const struct v4l2_mbus_framefmt *format;
+
+ /*
+ * Get the format from the first used sink pad, as all sink
+ * formats must be identical.
+ */
+ format = &priv->fmt[__ffs(priv->bound_sources)];
+
+ max9286_set_video_format(priv, format);
+ max9286_set_fsync_period(priv);
+
/*
* The frame sync between cameras is transmitted across the
* reverse channel as GPIO. We must open all channels while
@@ -698,13 +845,17 @@ static int max9286_s_stream(struct v4l2_subdev *sd, int enable)
}
/*
- * Enable CSI output, VC set according to link number.
- * Bit 7 must be set (chip manual says it's 0 and reserved).
+ * Configure the CSI-2 output to line interleaved mode (W x (N
+ * x H), as opposed to the (N x W) x H mode that outputs the
+ * images stitched side-by-side) and enable it.
*/
- max9286_write(priv, 0x15, 0x80 | MAX9286_VCTYPE |
- MAX9286_CSIOUTEN | MAX9286_0X15_RESV);
+ max9286_write(priv, 0x15, MAX9286_CSI_IMAGE_TYP | MAX9286_VCTYPE |
+ MAX9286_CSIOUTEN | MAX9286_EN_CCBSYB_CLK_STR |
+ MAX9286_EN_GPI_CCBSYB);
} else {
- max9286_write(priv, 0x15, MAX9286_VCTYPE | MAX9286_0X15_RESV);
+ max9286_write(priv, 0x15, MAX9286_VCTYPE |
+ MAX9286_EN_CCBSYB_CLK_STR |
+ MAX9286_EN_GPI_CCBSYB);
/* Stop all cameras. */
for_each_source(priv, source)
@@ -716,6 +867,32 @@ static int max9286_s_stream(struct v4l2_subdev *sd, int enable)
return 0;
}
+static int max9286_g_frame_interval(struct v4l2_subdev *sd,
+ struct v4l2_subdev_frame_interval *interval)
+{
+ struct max9286_priv *priv = sd_to_max9286(sd);
+
+ if (interval->pad != MAX9286_SRC_PAD)
+ return -EINVAL;
+
+ interval->interval = priv->interval;
+
+ return 0;
+}
+
+static int max9286_s_frame_interval(struct v4l2_subdev *sd,
+ struct v4l2_subdev_frame_interval *interval)
+{
+ struct max9286_priv *priv = sd_to_max9286(sd);
+
+ if (interval->pad != MAX9286_SRC_PAD)
+ return -EINVAL;
+
+ priv->interval = interval->interval;
+
+ return 0;
+}
+
static int max9286_enum_mbus_code(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_mbus_code_enum *code)
@@ -749,22 +926,20 @@ static int max9286_set_fmt(struct v4l2_subdev *sd,
{
struct max9286_priv *priv = sd_to_max9286(sd);
struct v4l2_mbus_framefmt *cfg_fmt;
+ unsigned int i;
if (format->pad == MAX9286_SRC_PAD)
return -EINVAL;
- /* Refuse non YUV422 formats as we hardcode DT to 8 bit YUV422 */
- switch (format->format.code) {
- case MEDIA_BUS_FMT_UYVY8_1X16:
- case MEDIA_BUS_FMT_VYUY8_1X16:
- case MEDIA_BUS_FMT_YUYV8_1X16:
- case MEDIA_BUS_FMT_YVYU8_1X16:
- break;
- default:
- format->format.code = MEDIA_BUS_FMT_UYVY8_1X16;
- break;
+ /* Validate the format. */
+ for (i = 0; i < ARRAY_SIZE(max9286_formats); ++i) {
+ if (max9286_formats[i].code == format->format.code)
+ break;
}
+ if (i == ARRAY_SIZE(max9286_formats))
+ format->format.code = max9286_formats[0].code;
+
cfg_fmt = max9286_get_pad_format(priv, sd_state, format->pad,
format->which);
if (!cfg_fmt)
@@ -807,6 +982,8 @@ static int max9286_get_fmt(struct v4l2_subdev *sd,
static const struct v4l2_subdev_video_ops max9286_video_ops = {
.s_stream = max9286_s_stream,
+ .g_frame_interval = max9286_g_frame_interval,
+ .s_frame_interval = max9286_s_frame_interval,
};
static const struct v4l2_subdev_pad_ops max9286_pad_ops = {
@@ -820,16 +997,20 @@ static const struct v4l2_subdev_ops max9286_subdev_ops = {
.pad = &max9286_pad_ops,
};
+static const struct v4l2_mbus_framefmt max9286_default_format = {
+ .width = 1280,
+ .height = 800,
+ .code = MEDIA_BUS_FMT_UYVY8_1X16,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .field = V4L2_FIELD_NONE,
+ .ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT,
+ .quantization = V4L2_QUANTIZATION_DEFAULT,
+ .xfer_func = V4L2_XFER_FUNC_DEFAULT,
+};
+
static void max9286_init_format(struct v4l2_mbus_framefmt *fmt)
{
- fmt->width = 1280;
- fmt->height = 800;
- fmt->code = MEDIA_BUS_FMT_UYVY8_1X16;
- fmt->colorspace = V4L2_COLORSPACE_SRGB;
- fmt->field = V4L2_FIELD_NONE;
- fmt->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
- fmt->quantization = V4L2_QUANTIZATION_DEFAULT;
- fmt->xfer_func = V4L2_XFER_FUNC_DEFAULT;
+ *fmt = max9286_default_format;
}
static int max9286_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
@@ -891,10 +1072,10 @@ static int max9286_v4l2_register(struct max9286_priv *priv)
priv->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
v4l2_ctrl_handler_init(&priv->ctrls, 1);
- priv->pixelrate = v4l2_ctrl_new_std(&priv->ctrls,
- &max9286_ctrl_ops,
- V4L2_CID_PIXEL_RATE,
- 1, INT_MAX, 1, 50000000);
+ priv->pixelrate_ctrl = v4l2_ctrl_new_std(&priv->ctrls,
+ &max9286_ctrl_ops,
+ V4L2_CID_PIXEL_RATE,
+ 1, INT_MAX, 1, 50000000);
priv->sd.ctrl_handler = &priv->ctrls;
ret = priv->ctrls.error;
@@ -932,6 +1113,7 @@ static int max9286_v4l2_register(struct max9286_priv *priv)
err_put_node:
fwnode_handle_put(ep);
err_async:
+ v4l2_ctrl_handler_free(&priv->ctrls);
max9286_v4l2_notifier_unregister(priv);
return ret;
@@ -975,6 +1157,7 @@ static int max9286_setup(struct max9286_priv *priv)
(2 << 6) | (1 << 4) | (0 << 2) | (3 << 0), /* 210x */
(3 << 6) | (2 << 4) | (1 << 2) | (0 << 0), /* 3210 */
};
+ int cfg;
/*
* Set the I2C bus speed.
@@ -993,24 +1176,27 @@ static int max9286_setup(struct max9286_priv *priv)
max9286_write(priv, 0x0b, link_order[priv->route_mask]);
max9286_write(priv, 0x69, (0xf & ~priv->route_mask));
- /*
- * Video format setup:
- * Disable CSI output, VC is set according to Link number.
- */
- max9286_write(priv, 0x15, MAX9286_VCTYPE | MAX9286_0X15_RESV);
+ max9286_set_video_format(priv, &max9286_default_format);
+ max9286_set_fsync_period(priv);
- /* Enable CSI-2 Lane D0-D3 only, DBL mode, YUV422 8-bit. */
- max9286_write(priv, 0x12, MAX9286_CSIDBL | MAX9286_DBL |
- MAX9286_CSILANECNT(priv->csi2_data_lanes) |
- MAX9286_DATATYPE_YUV422_8BIT);
+ cfg = max9286_read(priv, 0x1c);
+ if (cfg < 0)
+ return cfg;
+
+ dev_dbg(&priv->client->dev, "power-up config: %s immunity, %u-bit bus\n",
+ cfg & MAX9286_HIGHIMM(0) ? "high" : "legacy",
+ cfg & MAX9286_BWS ? 32 : cfg & MAX9286_HIBW ? 27 : 24);
- /* Automatic: FRAMESYNC taken from the slowest Link. */
- max9286_write(priv, 0x01, MAX9286_FSYNCMODE_INT_HIZ |
- MAX9286_FSYNCMETH_AUTO);
+ if (priv->bus_width) {
+ cfg &= ~(MAX9286_HIBW | MAX9286_BWS);
- /* Enable HS/VS encoding, use D14/15 for HS/VS, invert VS. */
- max9286_write(priv, 0x0c, MAX9286_HVEN | MAX9286_INVVS |
- MAX9286_HVSRC_D14);
+ if (priv->bus_width == 27)
+ cfg |= MAX9286_HIBW;
+ else if (priv->bus_width == 32)
+ cfg |= MAX9286_BWS;
+
+ max9286_write(priv, 0x1c, cfg);
+ }
/*
* The overlap window seems to provide additional validation by tracking
@@ -1088,9 +1274,6 @@ static int max9286_parse_gpios(struct max9286_priv *priv)
struct device *dev = &priv->client->dev;
int ret;
- /* GPIO values default to high */
- priv->gpio_state = BIT(0) | BIT(1);
-
/*
* Parse the "gpio-poc" vendor property. If the property is not
* specified the camera power is controlled by a regulator.
@@ -1102,18 +1285,7 @@ static int max9286_parse_gpios(struct max9286_priv *priv)
* If gpio lines are not used for the camera power, register
* a gpio controller for consumers.
*/
- ret = max9286_register_gpio(priv);
- if (ret)
- return ret;
-
- priv->regulator = devm_regulator_get(dev, "poc");
- if (IS_ERR(priv->regulator)) {
- return dev_err_probe(dev, PTR_ERR(priv->regulator),
- "Unable to get PoC regulator (%ld)\n",
- PTR_ERR(priv->regulator));
- }
-
- return 0;
+ return max9286_register_gpio(priv);
}
/* If the property is specified make sure it is well formed. */
@@ -1124,21 +1296,75 @@ static int max9286_parse_gpios(struct max9286_priv *priv)
return -EINVAL;
}
+ priv->use_gpio_poc = true;
+ return 0;
+}
+
+static int max9286_poc_power_on(struct max9286_priv *priv)
+{
+ struct max9286_source *source;
+ unsigned int enabled = 0;
+ int ret;
+
+ /* Enable the global regulator if available. */
+ if (priv->regulator)
+ return regulator_enable(priv->regulator);
+
+ if (priv->use_gpio_poc)
+ return max9286_gpio_set(priv, priv->gpio_poc[0],
+ !priv->gpio_poc[1]);
+
+ /* Otherwise use the per-port regulators. */
+ for_each_source(priv, source) {
+ ret = regulator_enable(source->regulator);
+ if (ret < 0)
+ goto error;
+
+ enabled |= BIT(to_index(priv, source));
+ }
+
return 0;
+
+error:
+ for_each_source(priv, source) {
+ if (enabled & BIT(to_index(priv, source)))
+ regulator_disable(source->regulator);
+ }
+
+ return ret;
+}
+
+static int max9286_poc_power_off(struct max9286_priv *priv)
+{
+ struct max9286_source *source;
+ int ret = 0;
+
+ if (priv->regulator)
+ return regulator_disable(priv->regulator);
+
+ if (priv->use_gpio_poc)
+ return max9286_gpio_set(priv, priv->gpio_poc[0],
+ priv->gpio_poc[1]);
+
+ for_each_source(priv, source) {
+ int err;
+
+ err = regulator_disable(source->regulator);
+ if (!ret)
+ ret = err;
+ }
+
+ return ret;
}
static int max9286_poc_enable(struct max9286_priv *priv, bool enable)
{
int ret;
- /* If the regulator is not available, use gpio to control power. */
- if (!priv->regulator)
- ret = max9286_gpio_set(priv, priv->gpio_poc[0],
- enable ^ priv->gpio_poc[1]);
- else if (enable)
- ret = regulator_enable(priv->regulator);
+ if (enable)
+ ret = max9286_poc_power_on(priv);
else
- ret = regulator_disable(priv->regulator);
+ ret = max9286_poc_power_off(priv);
if (ret < 0)
dev_err(&priv->client->dev, "Unable to turn power %s\n",
@@ -1208,6 +1434,8 @@ static int max9286_parse_dt(struct max9286_priv *priv)
struct device_node *node = NULL;
unsigned int i2c_mux_mask = 0;
u32 reverse_channel_microvolt;
+ u32 i2c_clk_freq = 105000;
+ unsigned int i;
/* Balance the of_node_put() performed by of_find_node_by_name(). */
of_node_get(dev->of_node);
@@ -1298,6 +1526,40 @@ static int max9286_parse_dt(struct max9286_priv *priv)
}
of_node_put(node);
+ of_property_read_u32(dev->of_node, "maxim,bus-width", &priv->bus_width);
+ switch (priv->bus_width) {
+ case 0:
+ /*
+ * The property isn't specified in the device tree, the driver
+ * will keep the default value selected by the BWS pin.
+ */
+ case 24:
+ case 27:
+ case 32:
+ break;
+ default:
+ dev_err(dev, "Invalid %s value %u\n", "maxim,bus-width",
+ priv->bus_width);
+ return -EINVAL;
+ }
+
+ of_property_read_u32(dev->of_node, "maxim,i2c-remote-bus-hz",
+ &i2c_clk_freq);
+ for (i = 0; i < ARRAY_SIZE(max9286_i2c_speeds); ++i) {
+ const struct max9286_i2c_speed *speed = &max9286_i2c_speeds[i];
+
+ if (speed->rate == i2c_clk_freq) {
+ priv->i2c_mstbt = speed->mstbt;
+ break;
+ }
+ }
+
+ if (i == ARRAY_SIZE(max9286_i2c_speeds)) {
+ dev_err(dev, "Invalid %s value %u\n", "maxim,i2c-remote-bus-hz",
+ i2c_clk_freq);
+ return -EINVAL;
+ }
+
/*
* Parse the initial value of the reverse channel amplitude from
* the firmware interface and convert it to millivolts.
@@ -1317,6 +1579,44 @@ static int max9286_parse_dt(struct max9286_priv *priv)
return 0;
}
+static int max9286_get_poc_supplies(struct max9286_priv *priv)
+{
+ struct device *dev = &priv->client->dev;
+ struct max9286_source *source;
+ int ret;
+
+ /* Start by getting the global regulator. */
+ priv->regulator = devm_regulator_get_optional(dev, "poc");
+ if (!IS_ERR(priv->regulator))
+ return 0;
+
+ if (PTR_ERR(priv->regulator) != -ENODEV)
+ return dev_err_probe(dev, PTR_ERR(priv->regulator),
+ "Unable to get PoC regulator\n");
+
+ /* If there's no global regulator, get per-port regulators. */
+ dev_dbg(dev,
+ "No global PoC regulator, looking for per-port regulators\n");
+ priv->regulator = NULL;
+
+ for_each_source(priv, source) {
+ unsigned int index = to_index(priv, source);
+ char name[10];
+
+ snprintf(name, sizeof(name), "port%u-poc", index);
+ source->regulator = devm_regulator_get(dev, name);
+ if (IS_ERR(source->regulator)) {
+ ret = PTR_ERR(source->regulator);
+ dev_err_probe(dev, ret,
+ "Unable to get port %u PoC regulator\n",
+ index);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
static int max9286_probe(struct i2c_client *client)
{
struct max9286_priv *priv;
@@ -1330,10 +1630,19 @@ static int max9286_probe(struct i2c_client *client)
priv->client = client;
+ /* GPIO values default to high */
+ priv->gpio_state = BIT(0) | BIT(1);
+
+ ret = max9286_parse_dt(priv);
+ if (ret)
+ goto err_cleanup_dt;
+
priv->gpiod_pwdn = devm_gpiod_get_optional(&client->dev, "enable",
GPIOD_OUT_HIGH);
- if (IS_ERR(priv->gpiod_pwdn))
- return PTR_ERR(priv->gpiod_pwdn);
+ if (IS_ERR(priv->gpiod_pwdn)) {
+ ret = PTR_ERR(priv->gpiod_pwdn);
+ goto err_cleanup_dt;
+ }
gpiod_set_consumer_name(priv->gpiod_pwdn, "max9286-pwdn");
gpiod_set_value_cansleep(priv->gpiod_pwdn, 1);
@@ -1360,9 +1669,11 @@ static int max9286_probe(struct i2c_client *client)
if (ret)
goto err_powerdown;
- ret = max9286_parse_dt(priv);
- if (ret)
- goto err_powerdown;
+ if (!priv->use_gpio_poc) {
+ ret = max9286_get_poc_supplies(priv);
+ if (ret)
+ goto err_cleanup_dt;
+ }
ret = max9286_init(priv);
if (ret < 0)
@@ -1370,10 +1681,10 @@ static int max9286_probe(struct i2c_client *client)
return 0;
-err_cleanup_dt:
- max9286_cleanup_dt(priv);
err_powerdown:
gpiod_set_value_cansleep(priv->gpiod_pwdn, 0);
+err_cleanup_dt:
+ max9286_cleanup_dt(priv);
return ret;
}
diff --git a/drivers/media/i2c/msp3400-driver.c b/drivers/media/i2c/msp3400-driver.c
index 4ce7a15a9884..12032e28b428 100644
--- a/drivers/media/i2c/msp3400-driver.c
+++ b/drivers/media/i2c/msp3400-driver.c
@@ -663,8 +663,9 @@ static const char * const opmode_str[] = {
[OPMODE_AUTOSELECT] = "autodetect and autoselect",
};
-static int msp_probe(struct i2c_client *client, const struct i2c_device_id *id)
+static int msp_probe(struct i2c_client *client)
{
+ const struct i2c_device_id *id = i2c_client_get_device_id(client);
struct msp_state *state;
struct v4l2_subdev *sd;
struct v4l2_ctrl_handler *hdl;
@@ -891,7 +892,7 @@ static struct i2c_driver msp_driver = {
.name = "msp3400",
.pm = &msp3400_pm_ops,
},
- .probe = msp_probe,
+ .probe_new = msp_probe,
.remove = msp_remove,
.id_table = msp_id,
};
diff --git a/drivers/media/i2c/mt9p031.c b/drivers/media/i2c/mt9p031.c
index 4ffc2f6e7db4..9e023a4b9bd1 100644
--- a/drivers/media/i2c/mt9p031.c
+++ b/drivers/media/i2c/mt9p031.c
@@ -1102,9 +1102,9 @@ done:
return pdata;
}
-static int mt9p031_probe(struct i2c_client *client,
- const struct i2c_device_id *did)
+static int mt9p031_probe(struct i2c_client *client)
{
+ const struct i2c_device_id *did = i2c_client_get_device_id(client);
struct mt9p031_platform_data *pdata = mt9p031_get_pdata(client);
struct i2c_adapter *adapter = client->adapter;
struct mt9p031 *mt9p031;
@@ -1248,7 +1248,7 @@ static struct i2c_driver mt9p031_i2c_driver = {
.of_match_table = of_match_ptr(mt9p031_of_match),
.name = "mt9p031",
},
- .probe = mt9p031_probe,
+ .probe_new = mt9p031_probe,
.remove = mt9p031_remove,
.id_table = mt9p031_id,
};
diff --git a/drivers/media/i2c/mt9v032.c b/drivers/media/i2c/mt9v032.c
index bc4388ccc2a8..7cfd4ebdd2e6 100644
--- a/drivers/media/i2c/mt9v032.c
+++ b/drivers/media/i2c/mt9v032.c
@@ -1044,9 +1044,9 @@ done:
return pdata;
}
-static int mt9v032_probe(struct i2c_client *client,
- const struct i2c_device_id *did)
+static int mt9v032_probe(struct i2c_client *client)
{
+ const struct i2c_device_id *did = i2c_client_get_device_id(client);
struct mt9v032_platform_data *pdata = mt9v032_get_pdata(client);
struct mt9v032 *mt9v032;
unsigned int i;
@@ -1296,7 +1296,7 @@ static struct i2c_driver mt9v032_driver = {
.name = "mt9v032",
.of_match_table = of_match_ptr(mt9v032_of_match),
},
- .probe = mt9v032_probe,
+ .probe_new = mt9v032_probe,
.remove = mt9v032_remove,
.id_table = mt9v032_id,
};
diff --git a/drivers/media/i2c/ov2740.c b/drivers/media/i2c/ov2740.c
index f3731f932a94..89d126240c34 100644
--- a/drivers/media/i2c/ov2740.c
+++ b/drivers/media/i2c/ov2740.c
@@ -629,8 +629,10 @@ static int ov2740_init_controls(struct ov2740 *ov2740)
V4L2_CID_TEST_PATTERN,
ARRAY_SIZE(ov2740_test_pattern_menu) - 1,
0, 0, ov2740_test_pattern_menu);
- if (ctrl_hdlr->error)
+ if (ctrl_hdlr->error) {
+ v4l2_ctrl_handler_free(ctrl_hdlr);
return ctrl_hdlr->error;
+ }
ov2740->sd.ctrl_handler = ctrl_hdlr;
diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
index e0f908af581b..1536649b9e90 100644
--- a/drivers/media/i2c/ov5640.c
+++ b/drivers/media/i2c/ov5640.c
@@ -50,6 +50,7 @@
#define OV5640_REG_SYS_CTRL0 0x3008
#define OV5640_REG_SYS_CTRL0_SW_PWDN 0x42
#define OV5640_REG_SYS_CTRL0_SW_PWUP 0x02
+#define OV5640_REG_SYS_CTRL0_SW_RST 0x82
#define OV5640_REG_CHIP_ID 0x300a
#define OV5640_REG_IO_MIPI_CTRL00 0x300e
#define OV5640_REG_PAD_OUTPUT_ENABLE01 0x3017
@@ -520,7 +521,18 @@ static u32 ov5640_code_to_bpp(struct ov5640_dev *sensor, u32 code)
*/
/* YUV422 UYVY VGA@30fps */
-static const struct v4l2_mbus_framefmt ov5640_default_fmt = {
+static const struct v4l2_mbus_framefmt ov5640_csi2_default_fmt = {
+ .code = MEDIA_BUS_FMT_UYVY8_1X16,
+ .width = 640,
+ .height = 480,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(V4L2_COLORSPACE_SRGB),
+ .quantization = V4L2_QUANTIZATION_FULL_RANGE,
+ .xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(V4L2_COLORSPACE_SRGB),
+ .field = V4L2_FIELD_NONE,
+};
+
+static const struct v4l2_mbus_framefmt ov5640_dvp_default_fmt = {
.code = MEDIA_BUS_FMT_UYVY8_2X8,
.width = 640,
.height = 480,
@@ -532,7 +544,7 @@ static const struct v4l2_mbus_framefmt ov5640_default_fmt = {
};
static const struct reg_value ov5640_init_setting[] = {
- {0x3103, 0x11, 0, 0}, {0x3008, 0x82, 0, 5}, {0x3008, 0x42, 0, 0},
+ {0x3103, 0x11, 0, 0},
{0x3103, 0x03, 0, 0}, {0x3630, 0x36, 0, 0},
{0x3631, 0x0e, 0, 0}, {0x3632, 0xe2, 0, 0}, {0x3633, 0x12, 0, 0},
{0x3621, 0xe0, 0, 0}, {0x3704, 0xa0, 0, 0}, {0x3703, 0x5a, 0, 0},
@@ -2424,24 +2436,48 @@ static void ov5640_power(struct ov5640_dev *sensor, bool enable)
gpiod_set_value_cansleep(sensor->pwdn_gpio, enable ? 0 : 1);
}
-static void ov5640_reset(struct ov5640_dev *sensor)
+/*
+ * From section 2.7 power up sequence:
+ * t0 + t1 + t2 >= 5ms Delay from DOVDD stable to PWDN pull down
+ * t3 >= 1ms Delay from PWDN pull down to RESETB pull up
+ * t4 >= 20ms Delay from RESETB pull up to SCCB (i2c) stable
+ *
+ * Some modules don't expose RESETB/PWDN pins directly, instead providing a
+ * "PWUP" GPIO which is wired through appropriate delays and inverters to the
+ * pins.
+ *
+ * In such cases, this gpio should be mapped to pwdn_gpio in the driver, and we
+ * should still toggle the pwdn_gpio below with the appropriate delays, while
+ * the calls to reset_gpio will be ignored.
+ */
+static void ov5640_powerup_sequence(struct ov5640_dev *sensor)
{
- if (!sensor->reset_gpio)
- return;
-
- gpiod_set_value_cansleep(sensor->reset_gpio, 0);
+ if (sensor->pwdn_gpio) {
+ gpiod_set_value_cansleep(sensor->reset_gpio, 0);
- /* camera power cycle */
- ov5640_power(sensor, false);
- usleep_range(5000, 10000);
- ov5640_power(sensor, true);
- usleep_range(5000, 10000);
+ /* camera power cycle */
+ ov5640_power(sensor, false);
+ usleep_range(5000, 10000);
+ ov5640_power(sensor, true);
+ usleep_range(5000, 10000);
- gpiod_set_value_cansleep(sensor->reset_gpio, 1);
- usleep_range(1000, 2000);
+ gpiod_set_value_cansleep(sensor->reset_gpio, 1);
+ usleep_range(1000, 2000);
- gpiod_set_value_cansleep(sensor->reset_gpio, 0);
+ gpiod_set_value_cansleep(sensor->reset_gpio, 0);
+ } else {
+ /* software reset */
+ ov5640_write_reg(sensor, OV5640_REG_SYS_CTRL0,
+ OV5640_REG_SYS_CTRL0_SW_RST);
+ }
usleep_range(20000, 25000);
+
+ /*
+ * software standby: allows registers programming;
+ * exit at restore_mode() for CSI, s_stream(1) for DVP
+ */
+ ov5640_write_reg(sensor, OV5640_REG_SYS_CTRL0,
+ OV5640_REG_SYS_CTRL0_SW_PWDN);
}
static int ov5640_set_power_on(struct ov5640_dev *sensor)
@@ -2464,8 +2500,7 @@ static int ov5640_set_power_on(struct ov5640_dev *sensor)
goto xclk_off;
}
- ov5640_reset(sensor);
- ov5640_power(sensor, true);
+ ov5640_powerup_sequence(sensor);
ret = ov5640_init_slave_id(sensor);
if (ret)
@@ -3316,6 +3351,7 @@ static int ov5640_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
break;
}
+ pm_runtime_mark_last_busy(&sensor->i2c_client->dev);
pm_runtime_put_autosuspend(&sensor->i2c_client->dev);
return 0;
@@ -3391,6 +3427,7 @@ static int ov5640_s_ctrl(struct v4l2_ctrl *ctrl)
break;
}
+ pm_runtime_mark_last_busy(&sensor->i2c_client->dev);
pm_runtime_put_autosuspend(&sensor->i2c_client->dev);
return ret;
@@ -3458,7 +3495,7 @@ static int ov5640_init_controls(struct ov5640_dev *sensor)
/* Auto/manual gain */
ctrls->auto_gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_AUTOGAIN,
0, 1, 1, 1);
- ctrls->gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_GAIN,
+ ctrls->gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_ANALOGUE_GAIN,
0, 1023, 1, 0);
ctrls->saturation = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_SATURATION,
@@ -3710,8 +3747,10 @@ static int ov5640_s_stream(struct v4l2_subdev *sd, int enable)
out:
mutex_unlock(&sensor->lock);
- if (!enable || ret)
+ if (!enable || ret) {
+ pm_runtime_mark_last_busy(&sensor->i2c_client->dev);
pm_runtime_put_autosuspend(&sensor->i2c_client->dev);
+ }
return ret;
}
@@ -3719,11 +3758,13 @@ out:
static int ov5640_init_cfg(struct v4l2_subdev *sd,
struct v4l2_subdev_state *state)
{
+ struct ov5640_dev *sensor = to_ov5640_dev(sd);
struct v4l2_mbus_framefmt *fmt =
v4l2_subdev_get_try_format(sd, state, 0);
struct v4l2_rect *crop = v4l2_subdev_get_try_crop(sd, state, 0);
- *fmt = ov5640_default_fmt;
+ *fmt = ov5640_is_csi2(sensor) ? ov5640_csi2_default_fmt :
+ ov5640_dvp_default_fmt;
crop->left = OV5640_PIXEL_ARRAY_LEFT;
crop->top = OV5640_PIXEL_ARRAY_TOP;
@@ -3812,7 +3853,6 @@ static int ov5640_probe(struct i2c_client *client)
* default init sequence initialize sensor to
* YUV422 UYVY VGA@30fps
*/
- sensor->fmt = ov5640_default_fmt;
sensor->frame_interval.numerator = 1;
sensor->frame_interval.denominator = ov5640_framerates[OV5640_30_FPS];
sensor->current_fr = OV5640_30_FPS;
@@ -3845,6 +3885,9 @@ static int ov5640_probe(struct i2c_client *client)
return -EINVAL;
}
+ sensor->fmt = ov5640_is_csi2(sensor) ? ov5640_csi2_default_fmt :
+ ov5640_dvp_default_fmt;
+
/* get system clock (xclk) */
sensor->xclk = devm_clk_get(dev, "xclk");
if (IS_ERR(sensor->xclk)) {
@@ -3912,6 +3955,7 @@ static int ov5640_probe(struct i2c_client *client)
pm_runtime_set_autosuspend_delay(dev, 1000);
pm_runtime_use_autosuspend(dev);
+ pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev);
return 0;
diff --git a/drivers/media/i2c/ov5670.c b/drivers/media/i2c/ov5670.c
index bc9fc3bc90c2..f79d908f4531 100644
--- a/drivers/media/i2c/ov5670.c
+++ b/drivers/media/i2c/ov5670.c
@@ -1,15 +1,24 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2017 Intel Corporation.
+#include <asm/unaligned.h>
#include <linux/acpi.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
+#include <linux/mod_devicetable.h>
#include <linux/module.h>
+#include <linux/of.h>
#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
#include <media/v4l2-event.h>
#include <media/v4l2-fwnode.h>
+#define OV5670_XVCLK_FREQ 19200000
+
#define OV5670_REG_CHIP_ID 0x300a
#define OV5670_CHIP_ID 0x005670
@@ -65,6 +74,10 @@
#define OV5670_REG_VALUE_16BIT 2
#define OV5670_REG_VALUE_24BIT 3
+/* Pixel Array */
+#define OV5670_NATIVE_WIDTH 2624
+#define OV5670_NATIVE_HEIGHT 1980
+
/* Initial number of frames to skip to avoid possible garbage */
#define OV5670_NUM_OF_SKIP_FRAMES 2
@@ -83,6 +96,14 @@ struct ov5670_link_freq_config {
const struct ov5670_reg_list reg_list;
};
+static const char * const ov5670_supply_names[] = {
+ "avdd", /* Analog power */
+ "dvdd", /* Digital power */
+ "dovdd", /* Digital output power */
+};
+
+#define OV5670_NUM_SUPPLIES ARRAY_SIZE(ov5670_supply_names)
+
struct ov5670_mode {
/* Frame width in pixels */
u32 width;
@@ -99,10 +120,25 @@ struct ov5670_mode {
/* Link frequency needed for this resolution */
u32 link_freq_index;
+ /* Analog crop rectangle */
+ const struct v4l2_rect *analog_crop;
+
/* Sensor register settings for this resolution */
const struct ov5670_reg_list reg_list;
};
+/*
+ * All the modes supported by the driver are obtained by subsampling the
+ * full pixel array. The below values are reflected in registers from
+ * 0x3800-0x3807 in the modes register-value tables.
+ */
+static const struct v4l2_rect ov5670_analog_crop = {
+ .left = 12,
+ .top = 4,
+ .width = 2600,
+ .height = 1952,
+};
+
static const struct ov5670_reg mipi_data_rate_840mbps[] = {
{0x0300, 0x04},
{0x0301, 0x00},
@@ -1750,66 +1786,73 @@ static const struct ov5670_mode supported_modes[] = {
.height = 1944,
.vts_def = OV5670_VTS_30FPS,
.vts_min = OV5670_VTS_30FPS,
+ .link_freq_index = OV5670_LINK_FREQ_422MHZ_INDEX,
+ .analog_crop = &ov5670_analog_crop,
.reg_list = {
.num_of_regs = ARRAY_SIZE(mode_2592x1944_regs),
.regs = mode_2592x1944_regs,
},
- .link_freq_index = OV5670_LINK_FREQ_422MHZ_INDEX,
},
{
.width = 1296,
.height = 972,
.vts_def = OV5670_VTS_30FPS,
.vts_min = 996,
+ .link_freq_index = OV5670_LINK_FREQ_422MHZ_INDEX,
+ .analog_crop = &ov5670_analog_crop,
.reg_list = {
.num_of_regs = ARRAY_SIZE(mode_1296x972_regs),
.regs = mode_1296x972_regs,
},
- .link_freq_index = OV5670_LINK_FREQ_422MHZ_INDEX,
},
{
.width = 648,
.height = 486,
.vts_def = OV5670_VTS_30FPS,
.vts_min = 516,
+ .link_freq_index = OV5670_LINK_FREQ_422MHZ_INDEX,
+ .analog_crop = &ov5670_analog_crop,
.reg_list = {
.num_of_regs = ARRAY_SIZE(mode_648x486_regs),
.regs = mode_648x486_regs,
},
- .link_freq_index = OV5670_LINK_FREQ_422MHZ_INDEX,
},
{
.width = 2560,
.height = 1440,
.vts_def = OV5670_VTS_30FPS,
.vts_min = OV5670_VTS_30FPS,
+ .link_freq_index = OV5670_LINK_FREQ_422MHZ_INDEX,
+ .analog_crop = &ov5670_analog_crop,
.reg_list = {
.num_of_regs = ARRAY_SIZE(mode_2560x1440_regs),
.regs = mode_2560x1440_regs,
},
- .link_freq_index = OV5670_LINK_FREQ_422MHZ_INDEX,
},
{
.width = 1280,
.height = 720,
.vts_def = OV5670_VTS_30FPS,
.vts_min = 1020,
+
+ .link_freq_index = OV5670_LINK_FREQ_422MHZ_INDEX,
+ .analog_crop = &ov5670_analog_crop,
.reg_list = {
.num_of_regs = ARRAY_SIZE(mode_1280x720_regs),
.regs = mode_1280x720_regs,
},
- .link_freq_index = OV5670_LINK_FREQ_422MHZ_INDEX,
},
{
.width = 640,
.height = 360,
.vts_def = OV5670_VTS_30FPS,
.vts_min = 510,
+ .link_freq_index = OV5670_LINK_FREQ_422MHZ_INDEX,
+ .analog_crop = &ov5670_analog_crop,
.reg_list = {
.num_of_regs = ARRAY_SIZE(mode_640x360_regs),
.regs = mode_640x360_regs,
},
- .link_freq_index = OV5670_LINK_FREQ_422MHZ_INDEX,
}
};
@@ -1828,6 +1871,16 @@ struct ov5670 {
/* Current mode */
const struct ov5670_mode *cur_mode;
+ /* xvclk input clock */
+ struct clk *xvclk;
+
+ /* Regulators */
+ struct regulator_bulk_data supplies[OV5670_NUM_SUPPLIES];
+
+ /* Power-down and reset gpios. */
+ struct gpio_desc *pwdn_gpio; /* PWDNB pin. */
+ struct gpio_desc *reset_gpio; /* XSHUTDOWN pin. */
+
/* To serialize asynchronus callbacks */
struct mutex mutex;
@@ -1935,27 +1988,6 @@ static int ov5670_write_reg_list(struct ov5670 *ov5670,
return ov5670_write_regs(ov5670, r_list->regs, r_list->num_of_regs);
}
-/* Open sub-device */
-static int ov5670_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
-{
- struct ov5670 *ov5670 = to_ov5670(sd);
- struct v4l2_mbus_framefmt *try_fmt =
- v4l2_subdev_get_try_format(sd, fh->state, 0);
-
- mutex_lock(&ov5670->mutex);
-
- /* Initialize try_fmt */
- try_fmt->width = ov5670->cur_mode->width;
- try_fmt->height = ov5670->cur_mode->height;
- try_fmt->code = MEDIA_BUS_FMT_SGRBG10_1X10;
- try_fmt->field = V4L2_FIELD_NONE;
-
- /* No crop or compose */
- mutex_unlock(&ov5670->mutex);
-
- return 0;
-}
-
static int ov5670_update_digital_gain(struct ov5670 *ov5670, u32 d_gain)
{
int ret;
@@ -2006,7 +2038,7 @@ static int ov5670_set_ctrl(struct v4l2_ctrl *ctrl)
struct ov5670, ctrl_handler);
struct i2c_client *client = v4l2_get_subdevdata(&ov5670->sd);
s64 max;
- int ret = 0;
+ int ret;
/* Propagate change of current control to all related controls */
switch (ctrl->id) {
@@ -2045,7 +2077,13 @@ static int ov5670_set_ctrl(struct v4l2_ctrl *ctrl)
case V4L2_CID_TEST_PATTERN:
ret = ov5670_enable_test_pattern(ov5670, ctrl->val);
break;
+ case V4L2_CID_HBLANK:
+ case V4L2_CID_LINK_FREQ:
+ case V4L2_CID_PIXEL_RATE:
+ ret = 0;
+ break;
default:
+ ret = -EINVAL;
dev_info(&client->dev, "%s Unhandled id:0x%x, val:0x%x\n",
__func__, ctrl->id, ctrl->val);
break;
@@ -2155,6 +2193,28 @@ error:
return ret;
}
+static int ov5670_init_cfg(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state)
+{
+ struct v4l2_mbus_framefmt *fmt =
+ v4l2_subdev_get_try_format(sd, state, 0);
+ const struct ov5670_mode *default_mode = &supported_modes[0];
+ struct v4l2_rect *crop = v4l2_subdev_get_try_crop(sd, state, 0);
+
+ fmt->width = default_mode->width;
+ fmt->height = default_mode->height;
+ fmt->code = MEDIA_BUS_FMT_SGRBG10_1X10;
+ fmt->field = V4L2_FIELD_NONE;
+ fmt->colorspace = V4L2_COLORSPACE_SRGB;
+ fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(V4L2_COLORSPACE_SRGB);
+ fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
+ fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(V4L2_COLORSPACE_SRGB);
+
+ *crop = *default_mode->analog_crop;
+
+ return 0;
+}
+
static int ov5670_enum_mbus_code(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_mbus_code_enum *code)
@@ -2404,6 +2464,49 @@ unlock_and_return:
return ret;
}
+static int __maybe_unused ov5670_runtime_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct ov5670 *ov5670 = to_ov5670(sd);
+ unsigned long delay_us;
+ int ret;
+
+ ret = clk_prepare_enable(ov5670->xvclk);
+ if (ret)
+ return ret;
+
+ ret = regulator_bulk_enable(OV5670_NUM_SUPPLIES, ov5670->supplies);
+ if (ret) {
+ clk_disable_unprepare(ov5670->xvclk);
+ return ret;
+ }
+
+ gpiod_set_value_cansleep(ov5670->pwdn_gpio, 0);
+ gpiod_set_value_cansleep(ov5670->reset_gpio, 0);
+
+ /* 8192 * 2 clock pulses before the first SCCB transaction. */
+ delay_us = DIV_ROUND_UP(8192 * 2 * 1000,
+ DIV_ROUND_UP(OV5670_XVCLK_FREQ, 1000));
+ fsleep(delay_us);
+
+ return 0;
+}
+
+static int __maybe_unused ov5670_runtime_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct ov5670 *ov5670 = to_ov5670(sd);
+
+ gpiod_set_value_cansleep(ov5670->reset_gpio, 1);
+ gpiod_set_value_cansleep(ov5670->pwdn_gpio, 1);
+ regulator_bulk_disable(OV5670_NUM_SUPPLIES, ov5670->supplies);
+ clk_disable_unprepare(ov5670->xvclk);
+
+ return 0;
+}
+
static int __maybe_unused ov5670_suspend(struct device *dev)
{
struct v4l2_subdev *sd = dev_get_drvdata(dev);
@@ -2438,15 +2541,64 @@ static const struct v4l2_subdev_core_ops ov5670_core_ops = {
.unsubscribe_event = v4l2_event_subdev_unsubscribe,
};
+static const struct v4l2_rect *
+__ov5670_get_pad_crop(struct ov5670 *sensor, struct v4l2_subdev_state *state,
+ unsigned int pad, enum v4l2_subdev_format_whence which)
+{
+ const struct ov5670_mode *mode = sensor->cur_mode;
+
+ switch (which) {
+ case V4L2_SUBDEV_FORMAT_TRY:
+ return v4l2_subdev_get_try_crop(&sensor->sd, state, pad);
+ case V4L2_SUBDEV_FORMAT_ACTIVE:
+ return mode->analog_crop;
+ }
+
+ return NULL;
+}
+
+static int ov5670_get_selection(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_selection *sel)
+{
+ struct ov5670 *sensor = to_ov5670(subdev);
+
+ switch (sel->target) {
+ case V4L2_SEL_TGT_CROP:
+ mutex_lock(&sensor->mutex);
+ sel->r = *__ov5670_get_pad_crop(sensor, state, sel->pad,
+ sel->which);
+ mutex_unlock(&sensor->mutex);
+ break;
+ case V4L2_SEL_TGT_NATIVE_SIZE:
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ sel->r.top = 0;
+ sel->r.left = 0;
+ sel->r.width = OV5670_NATIVE_WIDTH;
+ sel->r.height = OV5670_NATIVE_HEIGHT;
+ break;
+ case V4L2_SEL_TGT_CROP_DEFAULT:
+ sel->r = ov5670_analog_crop;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static const struct v4l2_subdev_video_ops ov5670_video_ops = {
.s_stream = ov5670_set_stream,
};
static const struct v4l2_subdev_pad_ops ov5670_pad_ops = {
+ .init_cfg = ov5670_init_cfg,
.enum_mbus_code = ov5670_enum_mbus_code,
.get_fmt = ov5670_get_pad_format,
.set_fmt = ov5670_set_pad_format,
.enum_frame_size = ov5670_enum_frame_size,
+ .get_selection = ov5670_get_selection,
+ .set_selection = ov5670_get_selection,
};
static const struct v4l2_subdev_sensor_ops ov5670_sensor_ops = {
@@ -2464,9 +2616,34 @@ static const struct media_entity_operations ov5670_subdev_entity_ops = {
.link_validate = v4l2_subdev_link_validate,
};
-static const struct v4l2_subdev_internal_ops ov5670_internal_ops = {
- .open = ov5670_open,
-};
+static int ov5670_regulators_probe(struct ov5670 *ov5670)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&ov5670->sd);
+ unsigned int i;
+
+ for (i = 0; i < OV5670_NUM_SUPPLIES; i++)
+ ov5670->supplies[i].supply = ov5670_supply_names[i];
+
+ return devm_regulator_bulk_get(&client->dev, OV5670_NUM_SUPPLIES,
+ ov5670->supplies);
+}
+
+static int ov5670_gpio_probe(struct ov5670 *ov5670)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&ov5670->sd);
+
+ ov5670->pwdn_gpio = devm_gpiod_get_optional(&client->dev, "powerdown",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(ov5670->pwdn_gpio))
+ return PTR_ERR(ov5670->pwdn_gpio);
+
+ ov5670->reset_gpio = devm_gpiod_get_optional(&client->dev, "reset",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(ov5670->reset_gpio))
+ return PTR_ERR(ov5670->reset_gpio);
+
+ return 0;
+}
static int ov5670_probe(struct i2c_client *client)
{
@@ -2476,10 +2653,6 @@ static int ov5670_probe(struct i2c_client *client)
bool full_power;
int ret;
- device_property_read_u32(&client->dev, "clock-frequency", &input_clk);
- if (input_clk != 19200000)
- return -EINVAL;
-
ov5670 = devm_kzalloc(&client->dev, sizeof(*ov5670), GFP_KERNEL);
if (!ov5670) {
ret = -ENOMEM;
@@ -2487,16 +2660,50 @@ static int ov5670_probe(struct i2c_client *client)
goto error_print;
}
+ ov5670->xvclk = devm_clk_get(&client->dev, NULL);
+ if (!IS_ERR_OR_NULL(ov5670->xvclk))
+ input_clk = clk_get_rate(ov5670->xvclk);
+ else if (PTR_ERR(ov5670->xvclk) == -ENOENT)
+ device_property_read_u32(&client->dev, "clock-frequency",
+ &input_clk);
+ else
+ return dev_err_probe(&client->dev, PTR_ERR(ov5670->xvclk),
+ "error getting clock\n");
+
+ if (input_clk != OV5670_XVCLK_FREQ) {
+ dev_err(&client->dev,
+ "Unsupported clock frequency %u\n", input_clk);
+ return -EINVAL;
+ }
+
/* Initialize subdev */
v4l2_i2c_subdev_init(&ov5670->sd, client, &ov5670_subdev_ops);
+ ret = ov5670_regulators_probe(ov5670);
+ if (ret) {
+ err_msg = "Regulators probe failed";
+ goto error_print;
+ }
+
+ ret = ov5670_gpio_probe(ov5670);
+ if (ret) {
+ err_msg = "GPIO probe failed";
+ goto error_print;
+ }
+
full_power = acpi_dev_state_d0(&client->dev);
if (full_power) {
+ ret = ov5670_runtime_resume(&client->dev);
+ if (ret) {
+ err_msg = "Power up failed";
+ goto error_print;
+ }
+
/* Check module identity */
ret = ov5670_identify_module(ov5670);
if (ret) {
err_msg = "ov5670_identify_module() error";
- goto error_print;
+ goto error_power_off;
}
}
@@ -2511,7 +2718,6 @@ static int ov5670_probe(struct i2c_client *client)
goto error_mutex_destroy;
}
- ov5670->sd.internal_ops = &ov5670_internal_ops;
ov5670->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
V4L2_SUBDEV_FL_HAS_EVENTS;
ov5670->sd.entity.ops = &ov5670_subdev_entity_ops;
@@ -2525,24 +2731,27 @@ static int ov5670_probe(struct i2c_client *client)
goto error_handler_free;
}
- /* Async register for subdev */
- ret = v4l2_async_register_subdev_sensor(&ov5670->sd);
- if (ret < 0) {
- err_msg = "v4l2_async_register_subdev() error";
- goto error_entity_cleanup;
- }
-
ov5670->streaming = false;
/* Set the device's state to active if it's in D0 state. */
if (full_power)
pm_runtime_set_active(&client->dev);
pm_runtime_enable(&client->dev);
+
+ /* Async register for subdev */
+ ret = v4l2_async_register_subdev_sensor(&ov5670->sd);
+ if (ret < 0) {
+ err_msg = "v4l2_async_register_subdev() error";
+ goto error_pm_disable;
+ }
+
pm_runtime_idle(&client->dev);
return 0;
-error_entity_cleanup:
+error_pm_disable:
+ pm_runtime_disable(&client->dev);
+
media_entity_cleanup(&ov5670->sd.entity);
error_handler_free:
@@ -2551,6 +2760,10 @@ error_handler_free:
error_mutex_destroy:
mutex_destroy(&ov5670->mutex);
+error_power_off:
+ if (full_power)
+ ov5670_runtime_suspend(&client->dev);
+
error_print:
dev_err(&client->dev, "%s: %s %d\n", __func__, err_msg, ret);
@@ -2568,10 +2781,12 @@ static void ov5670_remove(struct i2c_client *client)
mutex_destroy(&ov5670->mutex);
pm_runtime_disable(&client->dev);
+ ov5670_runtime_suspend(&client->dev);
}
static const struct dev_pm_ops ov5670_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(ov5670_suspend, ov5670_resume)
+ SET_RUNTIME_PM_OPS(ov5670_runtime_suspend, ov5670_runtime_resume, NULL)
};
#ifdef CONFIG_ACPI
@@ -2583,11 +2798,18 @@ static const struct acpi_device_id ov5670_acpi_ids[] = {
MODULE_DEVICE_TABLE(acpi, ov5670_acpi_ids);
#endif
+static const struct of_device_id ov5670_of_ids[] = {
+ { .compatible = "ovti,ov5670" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, ov5670_of_ids);
+
static struct i2c_driver ov5670_i2c_driver = {
.driver = {
.name = "ov5670",
.pm = &ov5670_pm_ops,
.acpi_match_table = ACPI_PTR(ov5670_acpi_ids),
+ .of_match_table = ov5670_of_ids,
},
.probe_new = ov5670_probe,
.remove = ov5670_remove,
diff --git a/drivers/media/i2c/ov5675.c b/drivers/media/i2c/ov5675.c
index 94dc8cb7a7c0..d55180b3b7aa 100644
--- a/drivers/media/i2c/ov5675.c
+++ b/drivers/media/i2c/ov5675.c
@@ -3,10 +3,14 @@
#include <asm/unaligned.h>
#include <linux/acpi.h>
+#include <linux/clk.h>
#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
+#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
#include <media/v4l2-fwnode.h>
@@ -17,7 +21,7 @@
#define OV5675_LINK_FREQ_450MHZ 450000000ULL
#define OV5675_SCLK 90000000LL
-#define OV5675_MCLK 19200000
+#define OV5675_XVCLK_19_2 19200000
#define OV5675_DATA_LANES 2
#define OV5675_RGB_DEPTH 10
@@ -76,6 +80,14 @@
#define to_ov5675(_sd) container_of(_sd, struct ov5675, sd)
+static const char * const ov5675_supply_names[] = {
+ "avdd", /* Analog power */
+ "dovdd", /* Digital I/O power */
+ "dvdd", /* Digital core power */
+};
+
+#define OV5675_NUM_SUPPLIES ARRAY_SIZE(ov5675_supply_names)
+
enum {
OV5675_LINK_FREQ_900MBPS,
};
@@ -484,6 +496,9 @@ struct ov5675 {
struct v4l2_subdev sd;
struct media_pad pad;
struct v4l2_ctrl_handler ctrl_handler;
+ struct clk *xvclk;
+ struct gpio_desc *reset_gpio;
+ struct regulator_bulk_data supplies[OV5675_NUM_SUPPLIES];
/* V4L2 Controls */
struct v4l2_ctrl *link_freq;
@@ -764,12 +779,14 @@ static const struct v4l2_ctrl_ops ov5675_ctrl_ops = {
static int ov5675_init_controls(struct ov5675 *ov5675)
{
+ struct i2c_client *client = v4l2_get_subdevdata(&ov5675->sd);
+ struct v4l2_fwnode_device_properties props;
struct v4l2_ctrl_handler *ctrl_hdlr;
s64 exposure_max, h_blank;
int ret;
ctrl_hdlr = &ov5675->ctrl_handler;
- ret = v4l2_ctrl_handler_init(ctrl_hdlr, 8);
+ ret = v4l2_ctrl_handler_init(ctrl_hdlr, 10);
if (ret)
return ret;
@@ -820,12 +837,28 @@ static int ov5675_init_controls(struct ov5675 *ov5675)
v4l2_ctrl_new_std(ctrl_hdlr, &ov5675_ctrl_ops,
V4L2_CID_VFLIP, 0, 1, 1, 0);
- if (ctrl_hdlr->error)
+ if (ctrl_hdlr->error) {
+ v4l2_ctrl_handler_free(ctrl_hdlr);
return ctrl_hdlr->error;
+ }
+
+ ret = v4l2_fwnode_device_parse(&client->dev, &props);
+ if (ret)
+ goto error;
+
+ ret = v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, &ov5675_ctrl_ops,
+ &props);
+ if (ret)
+ goto error;
ov5675->sd.ctrl_handler = ctrl_hdlr;
return 0;
+
+error:
+ v4l2_ctrl_handler_free(ctrl_hdlr);
+
+ return ret;
}
static void ov5675_update_pad_format(const struct ov5675_mode *mode,
@@ -944,6 +977,56 @@ static int ov5675_set_stream(struct v4l2_subdev *sd, int enable)
return ret;
}
+static int ov5675_power_off(struct device *dev)
+{
+ /* 512 xvclk cycles after the last SCCB transation or MIPI frame end */
+ u32 delay_us = DIV_ROUND_UP(512, OV5675_XVCLK_19_2 / 1000 / 1000);
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct ov5675 *ov5675 = to_ov5675(sd);
+
+ usleep_range(delay_us, delay_us * 2);
+
+ clk_disable_unprepare(ov5675->xvclk);
+ gpiod_set_value_cansleep(ov5675->reset_gpio, 1);
+ regulator_bulk_disable(OV5675_NUM_SUPPLIES, ov5675->supplies);
+
+ return 0;
+}
+
+static int ov5675_power_on(struct device *dev)
+{
+ u32 delay_us = DIV_ROUND_UP(8192, OV5675_XVCLK_19_2 / 1000 / 1000);
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct ov5675 *ov5675 = to_ov5675(sd);
+ int ret;
+
+ ret = clk_prepare_enable(ov5675->xvclk);
+ if (ret < 0) {
+ dev_err(dev, "failed to enable xvclk: %d\n", ret);
+ return ret;
+ }
+
+ gpiod_set_value_cansleep(ov5675->reset_gpio, 1);
+
+ ret = regulator_bulk_enable(OV5675_NUM_SUPPLIES, ov5675->supplies);
+ if (ret) {
+ clk_disable_unprepare(ov5675->xvclk);
+ return ret;
+ }
+
+ /* Reset pulse should be at least 2ms and reset gpio released only once
+ * regulators are stable.
+ */
+ usleep_range(2000, 2200);
+
+ gpiod_set_value_cansleep(ov5675->reset_gpio, 0);
+
+ /* 8192 xvclk cycles prior to the first SCCB transation */
+ usleep_range(delay_us, delay_us * 2);
+
+ return 0;
+}
+
static int __maybe_unused ov5675_suspend(struct device *dev)
{
struct v4l2_subdev *sd = dev_get_drvdata(dev);
@@ -1040,6 +1123,31 @@ static int ov5675_get_format(struct v4l2_subdev *sd,
return 0;
}
+static int ov5675_get_selection(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_selection *sel)
+{
+ if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE)
+ return -EINVAL;
+
+ switch (sel->target) {
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ sel->r.top = 0;
+ sel->r.left = 0;
+ sel->r.width = 2624;
+ sel->r.height = 2000;
+ return 0;
+ case V4L2_SEL_TGT_CROP:
+ case V4L2_SEL_TGT_CROP_DEFAULT:
+ sel->r.top = 16;
+ sel->r.left = 16;
+ sel->r.width = 2592;
+ sel->r.height = 1944;
+ return 0;
+ }
+ return -EINVAL;
+}
+
static int ov5675_enum_mbus_code(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_mbus_code_enum *code)
@@ -1089,6 +1197,7 @@ static const struct v4l2_subdev_video_ops ov5675_video_ops = {
static const struct v4l2_subdev_pad_ops ov5675_pad_ops = {
.set_fmt = ov5675_set_format,
.get_fmt = ov5675_get_format,
+ .get_selection = ov5675_get_selection,
.enum_mbus_code = ov5675_enum_mbus_code,
.enum_frame_size = ov5675_enum_frame_size,
};
@@ -1106,32 +1215,60 @@ static const struct v4l2_subdev_internal_ops ov5675_internal_ops = {
.open = ov5675_open,
};
-static int ov5675_check_hwcfg(struct device *dev)
+static int ov5675_get_hwcfg(struct ov5675 *ov5675, struct device *dev)
{
struct fwnode_handle *ep;
struct fwnode_handle *fwnode = dev_fwnode(dev);
struct v4l2_fwnode_endpoint bus_cfg = {
.bus_type = V4L2_MBUS_CSI2_DPHY
};
- u32 mclk;
+ u32 xvclk_rate;
int ret;
unsigned int i, j;
if (!fwnode)
return -ENXIO;
- ret = fwnode_property_read_u32(fwnode, "clock-frequency", &mclk);
+ ov5675->xvclk = devm_clk_get_optional(dev, NULL);
+ if (IS_ERR(ov5675->xvclk))
+ return dev_err_probe(dev, PTR_ERR(ov5675->xvclk),
+ "failed to get xvclk: %ld\n",
+ PTR_ERR(ov5675->xvclk));
- if (ret) {
- dev_err(dev, "can't get clock frequency");
- return ret;
+ if (ov5675->xvclk) {
+ xvclk_rate = clk_get_rate(ov5675->xvclk);
+ } else {
+ ret = fwnode_property_read_u32(fwnode, "clock-frequency",
+ &xvclk_rate);
+
+ if (ret) {
+ dev_err(dev, "can't get clock frequency");
+ return ret;
+ }
}
- if (mclk != OV5675_MCLK) {
- dev_err(dev, "external clock %d is not supported", mclk);
+ if (xvclk_rate != OV5675_XVCLK_19_2) {
+ dev_err(dev, "external clock rate %u is unsupported",
+ xvclk_rate);
return -EINVAL;
}
+ ov5675->reset_gpio = devm_gpiod_get_optional(dev, "reset",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(ov5675->reset_gpio)) {
+ ret = PTR_ERR(ov5675->reset_gpio);
+ dev_err(dev, "failed to get reset-gpios: %d\n", ret);
+ return ret;
+ }
+
+ for (i = 0; i < OV5675_NUM_SUPPLIES; i++)
+ ov5675->supplies[i].supply = ov5675_supply_names[i];
+
+ ret = devm_regulator_bulk_get(dev, OV5675_NUM_SUPPLIES,
+ ov5675->supplies);
+ if (ret)
+ return ret;
+
ep = fwnode_graph_get_next_endpoint(fwnode, NULL);
if (!ep)
return -ENXIO;
@@ -1185,6 +1322,10 @@ static void ov5675_remove(struct i2c_client *client)
v4l2_ctrl_handler_free(sd->ctrl_handler);
pm_runtime_disable(&client->dev);
mutex_destroy(&ov5675->mutex);
+
+ if (!pm_runtime_status_suspended(&client->dev))
+ ov5675_power_off(&client->dev);
+ pm_runtime_set_suspended(&client->dev);
}
static int ov5675_probe(struct i2c_client *client)
@@ -1193,25 +1334,31 @@ static int ov5675_probe(struct i2c_client *client)
bool full_power;
int ret;
- ret = ov5675_check_hwcfg(&client->dev);
+ ov5675 = devm_kzalloc(&client->dev, sizeof(*ov5675), GFP_KERNEL);
+ if (!ov5675)
+ return -ENOMEM;
+
+ ret = ov5675_get_hwcfg(ov5675, &client->dev);
if (ret) {
- dev_err(&client->dev, "failed to check HW configuration: %d",
+ dev_err(&client->dev, "failed to get HW configuration: %d",
ret);
return ret;
}
- ov5675 = devm_kzalloc(&client->dev, sizeof(*ov5675), GFP_KERNEL);
- if (!ov5675)
- return -ENOMEM;
-
v4l2_i2c_subdev_init(&ov5675->sd, client, &ov5675_subdev_ops);
+ ret = ov5675_power_on(&client->dev);
+ if (ret) {
+ dev_err(&client->dev, "failed to power on: %d\n", ret);
+ return ret;
+ }
+
full_power = acpi_dev_state_d0(&client->dev);
if (full_power) {
ret = ov5675_identify_module(ov5675);
if (ret) {
dev_err(&client->dev, "failed to find sensor: %d", ret);
- return ret;
+ goto probe_power_off;
}
}
@@ -1241,11 +1388,6 @@ static int ov5675_probe(struct i2c_client *client)
goto probe_error_media_entity_cleanup;
}
- /*
- * Device is already turned on by i2c-core with ACPI domain PM.
- * Enable runtime PM and turn off the device.
- */
-
/* Set the device's state to active if it's in D0 state. */
if (full_power)
pm_runtime_set_active(&client->dev);
@@ -1260,12 +1402,15 @@ probe_error_media_entity_cleanup:
probe_error_v4l2_ctrl_handler_free:
v4l2_ctrl_handler_free(ov5675->sd.ctrl_handler);
mutex_destroy(&ov5675->mutex);
+probe_power_off:
+ ov5675_power_off(&client->dev);
return ret;
}
static const struct dev_pm_ops ov5675_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(ov5675_suspend, ov5675_resume)
+ SET_RUNTIME_PM_OPS(ov5675_power_off, ov5675_power_on, NULL)
};
#ifdef CONFIG_ACPI
@@ -1277,11 +1422,18 @@ static const struct acpi_device_id ov5675_acpi_ids[] = {
MODULE_DEVICE_TABLE(acpi, ov5675_acpi_ids);
#endif
+static const struct of_device_id ov5675_of_match[] = {
+ { .compatible = "ovti,ov5675", },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, ov5675_of_match);
+
static struct i2c_driver ov5675_i2c_driver = {
.driver = {
.name = "ov5675",
.pm = &ov5675_pm_ops,
.acpi_match_table = ACPI_PTR(ov5675_acpi_ids),
+ .of_match_table = ov5675_of_match,
},
.probe_new = ov5675_probe,
.remove = ov5675_remove,
diff --git a/drivers/media/i2c/ov7670.c b/drivers/media/i2c/ov7670.c
index 11d3bef65d43..b1bb0833571e 100644
--- a/drivers/media/i2c/ov7670.c
+++ b/drivers/media/i2c/ov7670.c
@@ -1840,16 +1840,16 @@ static int ov7670_parse_dt(struct device *dev,
if (bus_cfg.bus_type != V4L2_MBUS_PARALLEL) {
dev_err(dev, "Unsupported media bus type\n");
- return ret;
+ return -EINVAL;
}
info->mbus_config = bus_cfg.bus.parallel.flags;
return 0;
}
-static int ov7670_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int ov7670_probe(struct i2c_client *client)
{
+ const struct i2c_device_id *id = i2c_client_get_device_id(client);
struct v4l2_fract tpf;
struct v4l2_subdev *sd;
struct ov7670_info *info;
@@ -2038,7 +2038,7 @@ static struct i2c_driver ov7670_driver = {
.name = "ov7670",
.of_match_table = of_match_ptr(ov7670_of_match),
},
- .probe = ov7670_probe,
+ .probe_new = ov7670_probe,
.remove = ov7670_remove,
.id_table = ov7670_id,
};
diff --git a/drivers/media/i2c/ov772x.c b/drivers/media/i2c/ov772x.c
index 4189e3fc3d53..a238e63425f8 100644
--- a/drivers/media/i2c/ov772x.c
+++ b/drivers/media/i2c/ov772x.c
@@ -1462,7 +1462,7 @@ static int ov772x_probe(struct i2c_client *client)
priv->subdev.ctrl_handler = &priv->hdl;
if (priv->hdl.error) {
ret = priv->hdl.error;
- goto error_mutex_destroy;
+ goto error_ctrl_free;
}
priv->clk = clk_get(&client->dev, NULL);
@@ -1515,7 +1515,6 @@ error_clk_put:
clk_put(priv->clk);
error_ctrl_free:
v4l2_ctrl_handler_free(&priv->hdl);
-error_mutex_destroy:
mutex_destroy(&priv->lock);
return ret;
diff --git a/drivers/media/i2c/ov8858.c b/drivers/media/i2c/ov8858.c
new file mode 100644
index 000000000000..9ca8a17bfbb9
--- /dev/null
+++ b/drivers/media/i2c/ov8858.c
@@ -0,0 +1,2008 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Jacopo Mondi <jacopo.mondi@ideasonboard.com>
+ * Copyright (C) 2022 Nicholas Roth <nicholas@rothemail.net>
+ * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd.
+ */
+
+#include <asm/unaligned.h>
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/pm_runtime.h>
+#include <linux/property.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+
+#include <media/media-entity.h>
+#include <media/v4l2-async.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-mediabus.h>
+#include <media/v4l2-subdev.h>
+
+#define OV8858_LINK_FREQ 360000000U
+#define OV8858_XVCLK_FREQ 24000000
+
+#define OV8858_REG_SIZE_SHIFT 16
+#define OV8858_REG_ADDR_MASK 0xffff
+#define OV8858_REG_8BIT(n) ((1U << OV8858_REG_SIZE_SHIFT) | (n))
+#define OV8858_REG_16BIT(n) ((2U << OV8858_REG_SIZE_SHIFT) | (n))
+#define OV8858_REG_24BIT(n) ((3U << OV8858_REG_SIZE_SHIFT) | (n))
+
+#define OV8858_REG_SC_CTRL0100 OV8858_REG_8BIT(0x0100)
+#define OV8858_MODE_SW_STANDBY 0x0
+#define OV8858_MODE_STREAMING 0x1
+
+#define OV8858_REG_CHIP_ID OV8858_REG_24BIT(0x300a)
+#define OV8858_CHIP_ID 0x008858
+
+#define OV8858_REG_SUB_ID OV8858_REG_8BIT(0x302a)
+#define OV8858_R1A 0xb0
+#define OV8858_R2A 0xb2
+
+#define OV8858_REG_LONG_EXPO OV8858_REG_24BIT(0x3500)
+#define OV8858_EXPOSURE_MIN 4
+#define OV8858_EXPOSURE_STEP 1
+#define OV8858_EXPOSURE_MARGIN 4
+
+#define OV8858_REG_LONG_GAIN OV8858_REG_16BIT(0x3508)
+#define OV8858_LONG_GAIN_MIN 0x0
+#define OV8858_LONG_GAIN_MAX 0x7ff
+#define OV8858_LONG_GAIN_STEP 1
+#define OV8858_LONG_GAIN_DEFAULT 0x80
+
+#define OV8858_REG_LONG_DIGIGAIN OV8858_REG_16BIT(0x350a)
+#define OV8858_LONG_DIGIGAIN_H_MASK 0x3fc0
+#define OV8858_LONG_DIGIGAIN_L_MASK 0x3f
+#define OV8858_LONG_DIGIGAIN_H_SHIFT 2
+#define OV8858_LONG_DIGIGAIN_MIN 0x0
+#define OV8858_LONG_DIGIGAIN_MAX 0x3fff
+#define OV8858_LONG_DIGIGAIN_STEP 1
+#define OV8858_LONG_DIGIGAIN_DEFAULT 0x200
+
+#define OV8858_REG_VTS OV8858_REG_16BIT(0x380e)
+#define OV8858_VTS_MAX 0x7fff
+
+#define OV8858_REG_TEST_PATTERN OV8858_REG_8BIT(0x5e00)
+#define OV8858_TEST_PATTERN_ENABLE 0x80
+#define OV8858_TEST_PATTERN_DISABLE 0x0
+
+#define REG_NULL 0xffff
+
+static const char * const ov8858_supply_names[] = {
+ "avdd", /* Analog power */
+ "dovdd", /* Digital I/O power */
+ "dvdd", /* Digital core power */
+};
+
+struct regval {
+ u16 addr;
+ u8 val;
+};
+
+struct regval_modes {
+ const struct regval *mode_2lanes;
+ const struct regval *mode_4lanes;
+};
+
+struct ov8858_mode {
+ u32 width;
+ u32 height;
+ u32 hts_def;
+ u32 vts_def;
+ u32 exp_def;
+ const struct regval_modes reg_modes;
+};
+
+struct ov8858 {
+ struct clk *xvclk;
+ struct gpio_desc *reset_gpio;
+ struct gpio_desc *pwdn_gpio;
+ struct regulator_bulk_data supplies[ARRAY_SIZE(ov8858_supply_names)];
+
+ struct v4l2_subdev subdev;
+ struct media_pad pad;
+
+ struct v4l2_ctrl_handler ctrl_handler;
+ struct v4l2_ctrl *exposure;
+ struct v4l2_ctrl *hblank;
+ struct v4l2_ctrl *vblank;
+
+ const struct regval *global_regs;
+
+ unsigned int num_lanes;
+};
+
+static inline struct ov8858 *sd_to_ov8858(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct ov8858, subdev);
+}
+
+static const struct regval ov8858_global_regs_r1a[] = {
+ {0x0100, 0x00},
+ {0x0100, 0x00},
+ {0x0100, 0x00},
+ {0x0100, 0x00},
+ {0x0302, 0x1e},
+ {0x0303, 0x00},
+ {0x0304, 0x03},
+ {0x030e, 0x00},
+ {0x030f, 0x09},
+ {0x0312, 0x01},
+ {0x031e, 0x0c},
+ {0x3600, 0x00},
+ {0x3601, 0x00},
+ {0x3602, 0x00},
+ {0x3603, 0x00},
+ {0x3604, 0x22},
+ {0x3605, 0x30},
+ {0x3606, 0x00},
+ {0x3607, 0x20},
+ {0x3608, 0x11},
+ {0x3609, 0x28},
+ {0x360a, 0x00},
+ {0x360b, 0x06},
+ {0x360c, 0xdc},
+ {0x360d, 0x40},
+ {0x360e, 0x0c},
+ {0x360f, 0x20},
+ {0x3610, 0x07},
+ {0x3611, 0x20},
+ {0x3612, 0x88},
+ {0x3613, 0x80},
+ {0x3614, 0x58},
+ {0x3615, 0x00},
+ {0x3616, 0x4a},
+ {0x3617, 0xb0},
+ {0x3618, 0x56},
+ {0x3619, 0x70},
+ {0x361a, 0x99},
+ {0x361b, 0x00},
+ {0x361c, 0x07},
+ {0x361d, 0x00},
+ {0x361e, 0x00},
+ {0x361f, 0x00},
+ {0x3638, 0xff},
+ {0x3633, 0x0c},
+ {0x3634, 0x0c},
+ {0x3635, 0x0c},
+ {0x3636, 0x0c},
+ {0x3645, 0x13},
+ {0x3646, 0x83},
+ {0x364a, 0x07},
+ {0x3015, 0x01},
+ {0x3018, 0x32},
+ {0x3020, 0x93},
+ {0x3022, 0x01},
+ {0x3031, 0x0a},
+ {0x3034, 0x00},
+ {0x3106, 0x01},
+ {0x3305, 0xf1},
+ {0x3308, 0x00},
+ {0x3309, 0x28},
+ {0x330a, 0x00},
+ {0x330b, 0x20},
+ {0x330c, 0x00},
+ {0x330d, 0x00},
+ {0x330e, 0x00},
+ {0x330f, 0x40},
+ {0x3307, 0x04},
+ {0x3500, 0x00},
+ {0x3501, 0x4d},
+ {0x3502, 0x40},
+ {0x3503, 0x00},
+ {0x3505, 0x80},
+ {0x3508, 0x04},
+ {0x3509, 0x00},
+ {0x350c, 0x00},
+ {0x350d, 0x80},
+ {0x3510, 0x00},
+ {0x3511, 0x02},
+ {0x3512, 0x00},
+ {0x3700, 0x18},
+ {0x3701, 0x0c},
+ {0x3702, 0x28},
+ {0x3703, 0x19},
+ {0x3704, 0x14},
+ {0x3705, 0x00},
+ {0x3706, 0x35},
+ {0x3707, 0x04},
+ {0x3708, 0x24},
+ {0x3709, 0x33},
+ {0x370a, 0x00},
+ {0x370b, 0xb5},
+ {0x370c, 0x04},
+ {0x3718, 0x12},
+ {0x3719, 0x31},
+ {0x3712, 0x42},
+ {0x3714, 0x24},
+ {0x371e, 0x19},
+ {0x371f, 0x40},
+ {0x3720, 0x05},
+ {0x3721, 0x05},
+ {0x3724, 0x06},
+ {0x3725, 0x01},
+ {0x3726, 0x06},
+ {0x3728, 0x05},
+ {0x3729, 0x02},
+ {0x372a, 0x03},
+ {0x372b, 0x53},
+ {0x372c, 0xa3},
+ {0x372d, 0x53},
+ {0x372e, 0x06},
+ {0x372f, 0x10},
+ {0x3730, 0x01},
+ {0x3731, 0x06},
+ {0x3732, 0x14},
+ {0x3733, 0x10},
+ {0x3734, 0x40},
+ {0x3736, 0x20},
+ {0x373a, 0x05},
+ {0x373b, 0x06},
+ {0x373c, 0x0a},
+ {0x373e, 0x03},
+ {0x3755, 0x10},
+ {0x3758, 0x00},
+ {0x3759, 0x4c},
+ {0x375a, 0x06},
+ {0x375b, 0x13},
+ {0x375c, 0x20},
+ {0x375d, 0x02},
+ {0x375e, 0x00},
+ {0x375f, 0x14},
+ {0x3768, 0x22},
+ {0x3769, 0x44},
+ {0x376a, 0x44},
+ {0x3761, 0x00},
+ {0x3762, 0x00},
+ {0x3763, 0x00},
+ {0x3766, 0xff},
+ {0x376b, 0x00},
+ {0x3772, 0x23},
+ {0x3773, 0x02},
+ {0x3774, 0x16},
+ {0x3775, 0x12},
+ {0x3776, 0x04},
+ {0x3777, 0x00},
+ {0x3778, 0x1b},
+ {0x37a0, 0x44},
+ {0x37a1, 0x3d},
+ {0x37a2, 0x3d},
+ {0x37a3, 0x00},
+ {0x37a4, 0x00},
+ {0x37a5, 0x00},
+ {0x37a6, 0x00},
+ {0x37a7, 0x44},
+ {0x37a8, 0x4c},
+ {0x37a9, 0x4c},
+ {0x3760, 0x00},
+ {0x376f, 0x01},
+ {0x37aa, 0x44},
+ {0x37ab, 0x2e},
+ {0x37ac, 0x2e},
+ {0x37ad, 0x33},
+ {0x37ae, 0x0d},
+ {0x37af, 0x0d},
+ {0x37b0, 0x00},
+ {0x37b1, 0x00},
+ {0x37b2, 0x00},
+ {0x37b3, 0x42},
+ {0x37b4, 0x42},
+ {0x37b5, 0x33},
+ {0x37b6, 0x00},
+ {0x37b7, 0x00},
+ {0x37b8, 0x00},
+ {0x37b9, 0xff},
+ {0x3800, 0x00},
+ {0x3801, 0x0c},
+ {0x3802, 0x00},
+ {0x3803, 0x0c},
+ {0x3804, 0x0c},
+ {0x3805, 0xd3},
+ {0x3806, 0x09},
+ {0x3807, 0xa3},
+ {0x3808, 0x06},
+ {0x3809, 0x60},
+ {0x380a, 0x04},
+ {0x380b, 0xc8},
+ {0x380c, 0x07},
+ {0x380d, 0x88},
+ {0x380e, 0x04},
+ {0x380f, 0xdc},
+ {0x3810, 0x00},
+ {0x3811, 0x04},
+ {0x3813, 0x02},
+ {0x3814, 0x03},
+ {0x3815, 0x01},
+ {0x3820, 0x00},
+ {0x3821, 0x67},
+ {0x382a, 0x03},
+ {0x382b, 0x01},
+ {0x3830, 0x08},
+ {0x3836, 0x02},
+ {0x3837, 0x18},
+ {0x3841, 0xff},
+ {0x3846, 0x48},
+ {0x3d85, 0x14},
+ {0x3f08, 0x08},
+ {0x3f0a, 0x80},
+ {0x4000, 0xf1},
+ {0x4001, 0x10},
+ {0x4005, 0x10},
+ {0x4002, 0x27},
+ {0x4009, 0x81},
+ {0x400b, 0x0c},
+ {0x401b, 0x00},
+ {0x401d, 0x00},
+ {0x4020, 0x00},
+ {0x4021, 0x04},
+ {0x4022, 0x04},
+ {0x4023, 0xb9},
+ {0x4024, 0x05},
+ {0x4025, 0x2a},
+ {0x4026, 0x05},
+ {0x4027, 0x2b},
+ {0x4028, 0x00},
+ {0x4029, 0x02},
+ {0x402a, 0x04},
+ {0x402b, 0x04},
+ {0x402c, 0x02},
+ {0x402d, 0x02},
+ {0x402e, 0x08},
+ {0x402f, 0x02},
+ {0x401f, 0x00},
+ {0x4034, 0x3f},
+ {0x403d, 0x04},
+ {0x4300, 0xff},
+ {0x4301, 0x00},
+ {0x4302, 0x0f},
+ {0x4316, 0x00},
+ {0x4500, 0x38},
+ {0x4503, 0x18},
+ {0x4600, 0x00},
+ {0x4601, 0xcb},
+ {0x481f, 0x32},
+ {0x4837, 0x16},
+ {0x4850, 0x10},
+ {0x4851, 0x32},
+ {0x4b00, 0x2a},
+ {0x4b0d, 0x00},
+ {0x4d00, 0x04},
+ {0x4d01, 0x18},
+ {0x4d02, 0xc3},
+ {0x4d03, 0xff},
+ {0x4d04, 0xff},
+ {0x4d05, 0xff},
+ {0x5000, 0x7e},
+ {0x5001, 0x01},
+ {0x5002, 0x08},
+ {0x5003, 0x20},
+ {0x5046, 0x12},
+ {0x5901, 0x00},
+ {0x5e00, 0x00},
+ {0x5e01, 0x41},
+ {0x382d, 0x7f},
+ {0x4825, 0x3a},
+ {0x4826, 0x40},
+ {0x4808, 0x25},
+ {REG_NULL, 0x00},
+};
+
+static const struct regval ov8858_global_regs_r2a_2lane[] = {
+ /*
+ * MIPI=720Mbps, SysClk=144Mhz,Dac Clock=360Mhz.
+ * v00_01_00 (05/29/2014) : initial setting
+ * AM19 : 3617 <- 0xC0
+ * AM20 : change FWC_6K_EN to be default 0x3618=0x5a
+ */
+ {0x0103, 0x01}, /* software reset */
+ {0x0100, 0x00}, /* software standby */
+ {0x0302, 0x1e}, /* pll1_multi */
+ {0x0303, 0x00}, /* pll1_divm */
+ {0x0304, 0x03}, /* pll1_div_mipi */
+ {0x030e, 0x02}, /* pll2_rdiv */
+ {0x030f, 0x04}, /* pll2_divsp */
+ {0x0312, 0x03}, /* pll2_pre_div0, pll2_r_divdac */
+ {0x031e, 0x0c}, /* pll1_no_lat */
+ {0x3600, 0x00},
+ {0x3601, 0x00},
+ {0x3602, 0x00},
+ {0x3603, 0x00},
+ {0x3604, 0x22},
+ {0x3605, 0x20},
+ {0x3606, 0x00},
+ {0x3607, 0x20},
+ {0x3608, 0x11},
+ {0x3609, 0x28},
+ {0x360a, 0x00},
+ {0x360b, 0x05},
+ {0x360c, 0xd4},
+ {0x360d, 0x40},
+ {0x360e, 0x0c},
+ {0x360f, 0x20},
+ {0x3610, 0x07},
+ {0x3611, 0x20},
+ {0x3612, 0x88},
+ {0x3613, 0x80},
+ {0x3614, 0x58},
+ {0x3615, 0x00},
+ {0x3616, 0x4a},
+ {0x3617, 0x90},
+ {0x3618, 0x5a},
+ {0x3619, 0x70},
+ {0x361a, 0x99},
+ {0x361b, 0x0a},
+ {0x361c, 0x07},
+ {0x361d, 0x00},
+ {0x361e, 0x00},
+ {0x361f, 0x00},
+ {0x3638, 0xff},
+ {0x3633, 0x0f},
+ {0x3634, 0x0f},
+ {0x3635, 0x0f},
+ {0x3636, 0x12},
+ {0x3645, 0x13},
+ {0x3646, 0x83},
+ {0x364a, 0x07},
+ {0x3015, 0x00},
+ {0x3018, 0x32}, /* MIPI 2 lane */
+ {0x3020, 0x93}, /* Clock switch output normal, pclk_div =/1 */
+ {0x3022, 0x01}, /* pd_mipi enable when rst_sync */
+ {0x3031, 0x0a}, /* MIPI 10-bit mode */
+ {0x3034, 0x00},
+ {0x3106, 0x01}, /* sclk_div, sclk_pre_div */
+ {0x3305, 0xf1},
+ {0x3308, 0x00},
+ {0x3309, 0x28},
+ {0x330a, 0x00},
+ {0x330b, 0x20},
+ {0x330c, 0x00},
+ {0x330d, 0x00},
+ {0x330e, 0x00},
+ {0x330f, 0x40},
+ {0x3307, 0x04},
+ {0x3500, 0x00}, /* exposure H */
+ {0x3501, 0x4d}, /* exposure M */
+ {0x3502, 0x40}, /* exposure L */
+ {0x3503, 0x80}, /* gain delay ?, exposure delay 1 frame, real gain */
+ {0x3505, 0x80}, /* gain option */
+ {0x3508, 0x02}, /* gain H */
+ {0x3509, 0x00}, /* gain L */
+ {0x350c, 0x00}, /* short gain H */
+ {0x350d, 0x80}, /* short gain L */
+ {0x3510, 0x00}, /* short exposure H */
+ {0x3511, 0x02}, /* short exposure M */
+ {0x3512, 0x00}, /* short exposure L */
+ {0x3700, 0x18},
+ {0x3701, 0x0c},
+ {0x3702, 0x28},
+ {0x3703, 0x19},
+ {0x3704, 0x14},
+ {0x3705, 0x00},
+ {0x3706, 0x82},
+ {0x3707, 0x04},
+ {0x3708, 0x24},
+ {0x3709, 0x33},
+ {0x370a, 0x01},
+ {0x370b, 0x82},
+ {0x370c, 0x04},
+ {0x3718, 0x12},
+ {0x3719, 0x31},
+ {0x3712, 0x42},
+ {0x3714, 0x24},
+ {0x371e, 0x19},
+ {0x371f, 0x40},
+ {0x3720, 0x05},
+ {0x3721, 0x05},
+ {0x3724, 0x06},
+ {0x3725, 0x01},
+ {0x3726, 0x06},
+ {0x3728, 0x05},
+ {0x3729, 0x02},
+ {0x372a, 0x03},
+ {0x372b, 0x53},
+ {0x372c, 0xa3},
+ {0x372d, 0x53},
+ {0x372e, 0x06},
+ {0x372f, 0x10},
+ {0x3730, 0x01},
+ {0x3731, 0x06},
+ {0x3732, 0x14},
+ {0x3733, 0x10},
+ {0x3734, 0x40},
+ {0x3736, 0x20},
+ {0x373a, 0x05},
+ {0x373b, 0x06},
+ {0x373c, 0x0a},
+ {0x373e, 0x03},
+ {0x3750, 0x0a},
+ {0x3751, 0x0e},
+ {0x3755, 0x10},
+ {0x3758, 0x00},
+ {0x3759, 0x4c},
+ {0x375a, 0x06},
+ {0x375b, 0x13},
+ {0x375c, 0x20},
+ {0x375d, 0x02},
+ {0x375e, 0x00},
+ {0x375f, 0x14},
+ {0x3768, 0x22},
+ {0x3769, 0x44},
+ {0x376a, 0x44},
+ {0x3761, 0x00},
+ {0x3762, 0x00},
+ {0x3763, 0x00},
+ {0x3766, 0xff},
+ {0x376b, 0x00},
+ {0x3772, 0x23},
+ {0x3773, 0x02},
+ {0x3774, 0x16},
+ {0x3775, 0x12},
+ {0x3776, 0x04},
+ {0x3777, 0x00},
+ {0x3778, 0x17},
+ {0x37a0, 0x44},
+ {0x37a1, 0x3d},
+ {0x37a2, 0x3d},
+ {0x37a3, 0x00},
+ {0x37a4, 0x00},
+ {0x37a5, 0x00},
+ {0x37a6, 0x00},
+ {0x37a7, 0x44},
+ {0x37a8, 0x4c},
+ {0x37a9, 0x4c},
+ {0x3760, 0x00},
+ {0x376f, 0x01},
+ {0x37aa, 0x44},
+ {0x37ab, 0x2e},
+ {0x37ac, 0x2e},
+ {0x37ad, 0x33},
+ {0x37ae, 0x0d},
+ {0x37af, 0x0d},
+ {0x37b0, 0x00},
+ {0x37b1, 0x00},
+ {0x37b2, 0x00},
+ {0x37b3, 0x42},
+ {0x37b4, 0x42},
+ {0x37b5, 0x31},
+ {0x37b6, 0x00},
+ {0x37b7, 0x00},
+ {0x37b8, 0x00},
+ {0x37b9, 0xff},
+ {0x3800, 0x00}, /* x start H */
+ {0x3801, 0x0c}, /* x start L */
+ {0x3802, 0x00}, /* y start H */
+ {0x3803, 0x0c}, /* y start L */
+ {0x3804, 0x0c}, /* x end H */
+ {0x3805, 0xd3}, /* x end L */
+ {0x3806, 0x09}, /* y end H */
+ {0x3807, 0xa3}, /* y end L */
+ {0x3808, 0x06}, /* x output size H */
+ {0x3809, 0x60}, /* x output size L */
+ {0x380a, 0x04}, /* y output size H */
+ {0x380b, 0xc8}, /* y output size L */
+ {0x380c, 0x07}, /* HTS H */
+ {0x380d, 0x88}, /* HTS L */
+ {0x380e, 0x04}, /* VTS H */
+ {0x380f, 0xdc}, /* VTS L */
+ {0x3810, 0x00}, /* ISP x win H */
+ {0x3811, 0x04}, /* ISP x win L */
+ {0x3813, 0x02}, /* ISP y win L */
+ {0x3814, 0x03}, /* x odd inc */
+ {0x3815, 0x01}, /* x even inc */
+ {0x3820, 0x00}, /* vflip off */
+ {0x3821, 0x67}, /* mirror on, bin on */
+ {0x382a, 0x03}, /* y odd inc */
+ {0x382b, 0x01}, /* y even inc */
+ {0x3830, 0x08},
+ {0x3836, 0x02},
+ {0x3837, 0x18},
+ {0x3841, 0xff}, /* window auto size enable */
+ {0x3846, 0x48},
+ {0x3d85, 0x16}, /* OTP power up load data enable with BIST */
+ {0x3d8c, 0x73}, /* OTP setting start High */
+ {0x3d8d, 0xde}, /* OTP setting start Low */
+ {0x3f08, 0x08},
+ {0x3f0a, 0x00},
+ {0x4000, 0xf1}, /* out_range_trig, format_chg_trig */
+ {0x4001, 0x10}, /* total 128 black column */
+ {0x4005, 0x10}, /* BLC target L */
+ {0x4002, 0x27}, /* value used to limit BLC offset */
+ {0x4009, 0x81}, /* final BLC offset limitation enable */
+ {0x400b, 0x0c}, /* DCBLC on, DCBLC manual mode on */
+ {0x401b, 0x00}, /* zero line R coefficient */
+ {0x401d, 0x00}, /* zoro line T coefficient */
+ {0x4020, 0x00}, /* Anchor left start H */
+ {0x4021, 0x04}, /* Anchor left start L */
+ {0x4022, 0x06}, /* Anchor left end H */
+ {0x4023, 0x00}, /* Anchor left end L */
+ {0x4024, 0x0f}, /* Anchor right start H */
+ {0x4025, 0x2a}, /* Anchor right start L */
+ {0x4026, 0x0f}, /* Anchor right end H */
+ {0x4027, 0x2b}, /* Anchor right end L */
+ {0x4028, 0x00}, /* top zero line start */
+ {0x4029, 0x02}, /* top zero line number */
+ {0x402a, 0x04}, /* top black line start */
+ {0x402b, 0x04}, /* top black line number */
+ {0x402c, 0x00}, /* bottom zero line start */
+ {0x402d, 0x02}, /* bottom zoro line number */
+ {0x402e, 0x04}, /* bottom black line start */
+ {0x402f, 0x04}, /* bottom black line number */
+ {0x401f, 0x00}, /* interpolation x/y disable, Anchor one disable */
+ {0x4034, 0x3f},
+ {0x403d, 0x04}, /* md_precision_en */
+ {0x4300, 0xff}, /* clip max H */
+ {0x4301, 0x00}, /* clip min H */
+ {0x4302, 0x0f}, /* clip min L, clip max L */
+ {0x4316, 0x00},
+ {0x4500, 0x58},
+ {0x4503, 0x18},
+ {0x4600, 0x00},
+ {0x4601, 0xcb},
+ {0x481f, 0x32}, /* clk prepare min */
+ {0x4837, 0x16}, /* global timing */
+ {0x4850, 0x10}, /* lane 1 = 1, lane 0 = 0 */
+ {0x4851, 0x32}, /* lane 3 = 3, lane 2 = 2 */
+ {0x4b00, 0x2a},
+ {0x4b0d, 0x00},
+ {0x4d00, 0x04}, /* temperature sensor */
+ {0x4d01, 0x18},
+ {0x4d02, 0xc3},
+ {0x4d03, 0xff},
+ {0x4d04, 0xff},
+ {0x4d05, 0xff}, /* temperature sensor */
+ {0x5000, 0xfe}, /* lenc on, slave/master AWB gain/statistics enable */
+ {0x5001, 0x01}, /* BLC on */
+ {0x5002, 0x08}, /* H scale off, WBMATCH off, OTP_DPC */
+ {0x5003, 0x20}, /* DPC_DBC buffer control enable, WB */
+ {0x501e, 0x93}, /* enable digital gain */
+ {0x5046, 0x12},
+ {0x5780, 0x3e}, /* DPC */
+ {0x5781, 0x0f},
+ {0x5782, 0x44},
+ {0x5783, 0x02},
+ {0x5784, 0x01},
+ {0x5785, 0x00},
+ {0x5786, 0x00},
+ {0x5787, 0x04},
+ {0x5788, 0x02},
+ {0x5789, 0x0f},
+ {0x578a, 0xfd},
+ {0x578b, 0xf5},
+ {0x578c, 0xf5},
+ {0x578d, 0x03},
+ {0x578e, 0x08},
+ {0x578f, 0x0c},
+ {0x5790, 0x08},
+ {0x5791, 0x04},
+ {0x5792, 0x00},
+ {0x5793, 0x52},
+ {0x5794, 0xa3}, /* DPC */
+ {0x5871, 0x0d}, /* Lenc */
+ {0x5870, 0x18},
+ {0x586e, 0x10},
+ {0x586f, 0x08},
+ {0x58f7, 0x01},
+ {0x58f8, 0x3d}, /* Lenc */
+ {0x5901, 0x00}, /* H skip off, V skip off */
+ {0x5b00, 0x02}, /* OTP DPC start address */
+ {0x5b01, 0x10}, /* OTP DPC start address */
+ {0x5b02, 0x03}, /* OTP DPC end address */
+ {0x5b03, 0xcf}, /* OTP DPC end address */
+ {0x5b05, 0x6c}, /* recover method = 2b11, */
+ {0x5e00, 0x00}, /* use 0x3ff to test pattern off */
+ {0x5e01, 0x41}, /* window cut enable */
+ {0x382d, 0x7f},
+ {0x4825, 0x3a}, /* lpx_p_min */
+ {0x4826, 0x40}, /* hs_prepare_min */
+ {0x4808, 0x25}, /* wake up delay in 1/1024 s */
+ {0x3763, 0x18},
+ {0x3768, 0xcc},
+ {0x470b, 0x28},
+ {0x4202, 0x00},
+ {0x400d, 0x10}, /* BLC offset trigger L */
+ {0x4040, 0x04}, /* BLC gain th2 */
+ {0x403e, 0x04}, /* BLC gain th1 */
+ {0x4041, 0xc6}, /* BLC */
+ {0x3007, 0x80},
+ {0x400a, 0x01},
+ {REG_NULL, 0x00},
+};
+
+/*
+ * Xclk 24Mhz
+ * max_framerate 30fps
+ * mipi_datarate per lane 720Mbps
+ */
+static const struct regval ov8858_1632x1224_regs_2lane[] = {
+ /*
+ * MIPI=720Mbps, SysClk=144Mhz,Dac Clock=360Mhz.
+ * v00_01_00 (05/29/2014) : initial setting
+ * AM19 : 3617 <- 0xC0
+ * AM20 : change FWC_6K_EN to be default 0x3618=0x5a
+ */
+ {0x0100, 0x00},
+ {0x3501, 0x4d}, /* exposure M */
+ {0x3502, 0x40}, /* exposure L */
+ {0x3778, 0x17},
+ {0x3808, 0x06}, /* x output size H */
+ {0x3809, 0x60}, /* x output size L */
+ {0x380a, 0x04}, /* y output size H */
+ {0x380b, 0xc8}, /* y output size L */
+ {0x380c, 0x07}, /* HTS H */
+ {0x380d, 0x88}, /* HTS L */
+ {0x380e, 0x04}, /* VTS H */
+ {0x380f, 0xdc}, /* VTS L */
+ {0x3814, 0x03}, /* x odd inc */
+ {0x3821, 0x67}, /* mirror on, bin on */
+ {0x382a, 0x03}, /* y odd inc */
+ {0x3830, 0x08},
+ {0x3836, 0x02},
+ {0x3f0a, 0x00},
+ {0x4001, 0x10}, /* total 128 black column */
+ {0x4022, 0x06}, /* Anchor left end H */
+ {0x4023, 0x00}, /* Anchor left end L */
+ {0x4025, 0x2a}, /* Anchor right start L */
+ {0x4027, 0x2b}, /* Anchor right end L */
+ {0x402b, 0x04}, /* top black line number */
+ {0x402f, 0x04}, /* bottom black line number */
+ {0x4500, 0x58},
+ {0x4600, 0x00},
+ {0x4601, 0xcb},
+ {0x382d, 0x7f},
+ {0x0100, 0x01},
+ {REG_NULL, 0x00},
+};
+
+/*
+ * Xclk 24Mhz
+ * max_framerate 15fps
+ * mipi_datarate per lane 720Mbps
+ */
+static const struct regval ov8858_3264x2448_regs_2lane[] = {
+ {0x0100, 0x00},
+ {0x3501, 0x9a}, /* exposure M */
+ {0x3502, 0x20}, /* exposure L */
+ {0x3778, 0x1a},
+ {0x3808, 0x0c}, /* x output size H */
+ {0x3809, 0xc0}, /* x output size L */
+ {0x380a, 0x09}, /* y output size H */
+ {0x380b, 0x90}, /* y output size L */
+ {0x380c, 0x07}, /* HTS H */
+ {0x380d, 0x94}, /* HTS L */
+ {0x380e, 0x09}, /* VTS H */
+ {0x380f, 0xaa}, /* VTS L */
+ {0x3814, 0x01}, /* x odd inc */
+ {0x3821, 0x46}, /* mirror on, bin off */
+ {0x382a, 0x01}, /* y odd inc */
+ {0x3830, 0x06},
+ {0x3836, 0x01},
+ {0x3f0a, 0x00},
+ {0x4001, 0x00}, /* total 256 black column */
+ {0x4022, 0x0c}, /* Anchor left end H */
+ {0x4023, 0x60}, /* Anchor left end L */
+ {0x4025, 0x36}, /* Anchor right start L */
+ {0x4027, 0x37}, /* Anchor right end L */
+ {0x402b, 0x08}, /* top black line number */
+ {0x402f, 0x08}, /* bottom black line number */
+ {0x4500, 0x58},
+ {0x4600, 0x01},
+ {0x4601, 0x97},
+ {0x382d, 0xff},
+ {REG_NULL, 0x00},
+};
+
+static const struct regval ov8858_global_regs_r2a_4lane[] = {
+ /*
+ * MIPI=720Mbps, SysClk=144Mhz,Dac Clock=360Mhz.
+ * v00_01_00 (05/29/2014) : initial setting
+ * AM19 : 3617 <- 0xC0
+ * AM20 : change FWC_6K_EN to be default 0x3618=0x5a
+ */
+ {0x0103, 0x01}, /* software reset for OVTATool only */
+ {0x0103, 0x01}, /* software reset */
+ {0x0100, 0x00}, /* software standby */
+ {0x0302, 0x1e}, /* pll1_multi */
+ {0x0303, 0x00}, /* pll1_divm */
+ {0x0304, 0x03}, /* pll1_div_mipi */
+ {0x030e, 0x00}, /* pll2_rdiv */
+ {0x030f, 0x04}, /* pll2_divsp */
+ {0x0312, 0x01}, /* pll2_pre_div0, pll2_r_divdac */
+ {0x031e, 0x0c}, /* pll1_no_lat */
+ {0x3600, 0x00},
+ {0x3601, 0x00},
+ {0x3602, 0x00},
+ {0x3603, 0x00},
+ {0x3604, 0x22},
+ {0x3605, 0x20},
+ {0x3606, 0x00},
+ {0x3607, 0x20},
+ {0x3608, 0x11},
+ {0x3609, 0x28},
+ {0x360a, 0x00},
+ {0x360b, 0x05},
+ {0x360c, 0xd4},
+ {0x360d, 0x40},
+ {0x360e, 0x0c},
+ {0x360f, 0x20},
+ {0x3610, 0x07},
+ {0x3611, 0x20},
+ {0x3612, 0x88},
+ {0x3613, 0x80},
+ {0x3614, 0x58},
+ {0x3615, 0x00},
+ {0x3616, 0x4a},
+ {0x3617, 0x90},
+ {0x3618, 0x5a},
+ {0x3619, 0x70},
+ {0x361a, 0x99},
+ {0x361b, 0x0a},
+ {0x361c, 0x07},
+ {0x361d, 0x00},
+ {0x361e, 0x00},
+ {0x361f, 0x00},
+ {0x3638, 0xff},
+ {0x3633, 0x0f},
+ {0x3634, 0x0f},
+ {0x3635, 0x0f},
+ {0x3636, 0x12},
+ {0x3645, 0x13},
+ {0x3646, 0x83},
+ {0x364a, 0x07},
+ {0x3015, 0x01},
+ {0x3018, 0x72}, /* MIPI 4 lane */
+ {0x3020, 0x93}, /* Clock switch output normal, pclk_div =/1 */
+ {0x3022, 0x01}, /* pd_mipi enable when rst_sync */
+ {0x3031, 0x0a}, /* MIPI 10-bit mode */
+ {0x3034, 0x00},
+ {0x3106, 0x01}, /* sclk_div, sclk_pre_div */
+ {0x3305, 0xf1},
+ {0x3308, 0x00},
+ {0x3309, 0x28},
+ {0x330a, 0x00},
+ {0x330b, 0x20},
+ {0x330c, 0x00},
+ {0x330d, 0x00},
+ {0x330e, 0x00},
+ {0x330f, 0x40},
+ {0x3307, 0x04},
+ {0x3500, 0x00}, /* exposure H */
+ {0x3501, 0x4d}, /* exposure M */
+ {0x3502, 0x40}, /* exposure L */
+ {0x3503, 0x80}, /* gain delay ?, exposure delay 1 frame, real gain */
+ {0x3505, 0x80}, /* gain option */
+ {0x3508, 0x02}, /* gain H */
+ {0x3509, 0x00}, /* gain L */
+ {0x350c, 0x00}, /* short gain H */
+ {0x350d, 0x80}, /* short gain L */
+ {0x3510, 0x00}, /* short exposure H */
+ {0x3511, 0x02}, /* short exposure M */
+ {0x3512, 0x00}, /* short exposure L */
+ {0x3700, 0x30},
+ {0x3701, 0x18},
+ {0x3702, 0x50},
+ {0x3703, 0x32},
+ {0x3704, 0x28},
+ {0x3705, 0x00},
+ {0x3706, 0x82},
+ {0x3707, 0x08},
+ {0x3708, 0x48},
+ {0x3709, 0x66},
+ {0x370a, 0x01},
+ {0x370b, 0x82},
+ {0x370c, 0x07},
+ {0x3718, 0x14},
+ {0x3719, 0x31},
+ {0x3712, 0x44},
+ {0x3714, 0x24},
+ {0x371e, 0x31},
+ {0x371f, 0x7f},
+ {0x3720, 0x0a},
+ {0x3721, 0x0a},
+ {0x3724, 0x0c},
+ {0x3725, 0x02},
+ {0x3726, 0x0c},
+ {0x3728, 0x0a},
+ {0x3729, 0x03},
+ {0x372a, 0x06},
+ {0x372b, 0xa6},
+ {0x372c, 0xa6},
+ {0x372d, 0xa6},
+ {0x372e, 0x0c},
+ {0x372f, 0x20},
+ {0x3730, 0x02},
+ {0x3731, 0x0c},
+ {0x3732, 0x28},
+ {0x3733, 0x10},
+ {0x3734, 0x40},
+ {0x3736, 0x30},
+ {0x373a, 0x0a},
+ {0x373b, 0x0b},
+ {0x373c, 0x14},
+ {0x373e, 0x06},
+ {0x3750, 0x0a},
+ {0x3751, 0x0e},
+ {0x3755, 0x10},
+ {0x3758, 0x00},
+ {0x3759, 0x4c},
+ {0x375a, 0x0c},
+ {0x375b, 0x26},
+ {0x375c, 0x20},
+ {0x375d, 0x04},
+ {0x375e, 0x00},
+ {0x375f, 0x28},
+ {0x3768, 0x22},
+ {0x3769, 0x44},
+ {0x376a, 0x44},
+ {0x3761, 0x00},
+ {0x3762, 0x00},
+ {0x3763, 0x00},
+ {0x3766, 0xff},
+ {0x376b, 0x00},
+ {0x3772, 0x46},
+ {0x3773, 0x04},
+ {0x3774, 0x2c},
+ {0x3775, 0x13},
+ {0x3776, 0x08},
+ {0x3777, 0x00},
+ {0x3778, 0x17},
+ {0x37a0, 0x88},
+ {0x37a1, 0x7a},
+ {0x37a2, 0x7a},
+ {0x37a3, 0x00},
+ {0x37a4, 0x00},
+ {0x37a5, 0x00},
+ {0x37a6, 0x00},
+ {0x37a7, 0x88},
+ {0x37a8, 0x98},
+ {0x37a9, 0x98},
+ {0x3760, 0x00},
+ {0x376f, 0x01},
+ {0x37aa, 0x88},
+ {0x37ab, 0x5c},
+ {0x37ac, 0x5c},
+ {0x37ad, 0x55},
+ {0x37ae, 0x19},
+ {0x37af, 0x19},
+ {0x37b0, 0x00},
+ {0x37b1, 0x00},
+ {0x37b2, 0x00},
+ {0x37b3, 0x84},
+ {0x37b4, 0x84},
+ {0x37b5, 0x60},
+ {0x37b6, 0x00},
+ {0x37b7, 0x00},
+ {0x37b8, 0x00},
+ {0x37b9, 0xff},
+ {0x3800, 0x00}, /* x start H */
+ {0x3801, 0x0c}, /* x start L */
+ {0x3802, 0x00}, /* y start H */
+ {0x3803, 0x0c}, /* y start L */
+ {0x3804, 0x0c}, /* x end H */
+ {0x3805, 0xd3}, /* x end L */
+ {0x3806, 0x09}, /* y end H */
+ {0x3807, 0xa3}, /* y end L */
+ {0x3808, 0x06}, /* x output size H */
+ {0x3809, 0x60}, /* x output size L */
+ {0x380a, 0x04}, /* y output size H */
+ {0x380b, 0xc8}, /* y output size L */
+ {0x380c, 0x07}, /* HTS H */
+ {0x380d, 0x88}, /* HTS L */
+ {0x380e, 0x04}, /* VTS H */
+ {0x380f, 0xdc}, /* VTS L */
+ {0x3810, 0x00}, /* ISP x win H */
+ {0x3811, 0x04}, /* ISP x win L */
+ {0x3813, 0x02}, /* ISP y win L */
+ {0x3814, 0x03}, /* x odd inc */
+ {0x3815, 0x01}, /* x even inc */
+ {0x3820, 0x00}, /* vflip off */
+ {0x3821, 0x67}, /* mirror on, bin o */
+ {0x382a, 0x03}, /* y odd inc */
+ {0x382b, 0x01}, /* y even inc */
+ {0x3830, 0x08},
+ {0x3836, 0x02},
+ {0x3837, 0x18},
+ {0x3841, 0xff}, /* window auto size enable */
+ {0x3846, 0x48},
+ {0x3d85, 0x16}, /* OTP power up load data/setting enable */
+ {0x3d8c, 0x73}, /* OTP setting start High */
+ {0x3d8d, 0xde}, /* OTP setting start Low */
+ {0x3f08, 0x10},
+ {0x3f0a, 0x00},
+ {0x4000, 0xf1}, /* out_range/format_chg/gain/exp_chg trig enable */
+ {0x4001, 0x10}, /* total 128 black column */
+ {0x4005, 0x10}, /* BLC target L */
+ {0x4002, 0x27}, /* value used to limit BLC offset */
+ {0x4009, 0x81}, /* final BLC offset limitation enable */
+ {0x400b, 0x0c}, /* DCBLC on, DCBLC manual mode on */
+ {0x401b, 0x00}, /* zero line R coefficient */
+ {0x401d, 0x00}, /* zoro line T coefficient */
+ {0x4020, 0x00}, /* Anchor left start H */
+ {0x4021, 0x04}, /* Anchor left start L */
+ {0x4022, 0x06}, /* Anchor left end H */
+ {0x4023, 0x00}, /* Anchor left end L */
+ {0x4024, 0x0f}, /* Anchor right start H */
+ {0x4025, 0x2a}, /* Anchor right start L */
+ {0x4026, 0x0f}, /* Anchor right end H */
+ {0x4027, 0x2b}, /* Anchor right end L */
+ {0x4028, 0x00}, /* top zero line start */
+ {0x4029, 0x02}, /* top zero line number */
+ {0x402a, 0x04}, /* top black line start */
+ {0x402b, 0x04}, /* top black line number */
+ {0x402c, 0x00}, /* bottom zero line start */
+ {0x402d, 0x02}, /* bottom zoro line number */
+ {0x402e, 0x04}, /* bottom black line start */
+ {0x402f, 0x04}, /* bottom black line number */
+ {0x401f, 0x00}, /* interpolation x/y disable, Anchor one disable */
+ {0x4034, 0x3f},
+ {0x403d, 0x04}, /* md_precision_en */
+ {0x4300, 0xff}, /* clip max H */
+ {0x4301, 0x00}, /* clip min H */
+ {0x4302, 0x0f}, /* clip min L, clip max L */
+ {0x4316, 0x00},
+ {0x4500, 0x58},
+ {0x4503, 0x18},
+ {0x4600, 0x00},
+ {0x4601, 0xcb},
+ {0x481f, 0x32}, /* clk prepare min */
+ {0x4837, 0x16}, /* global timing */
+ {0x4850, 0x10}, /* lane 1 = 1, lane 0 = 0 */
+ {0x4851, 0x32}, /* lane 3 = 3, lane 2 = 2 */
+ {0x4b00, 0x2a},
+ {0x4b0d, 0x00},
+ {0x4d00, 0x04}, /* temperature sensor */
+ {0x4d01, 0x18},
+ {0x4d02, 0xc3},
+ {0x4d03, 0xff},
+ {0x4d04, 0xff},
+ {0x4d05, 0xff}, /* temperature sensor */
+ {0x5000, 0xfe}, /* lenc on, slave/master AWB gain/statistics enable */
+ {0x5001, 0x01}, /* BLC on */
+ {0x5002, 0x08}, /* WBMATCH sensor's gain, H scale/WBMATCH/OTP_DPC off */
+ {0x5003, 0x20}, /* DPC_DBC buffer control enable, WB */
+ {0x501e, 0x93}, /* enable digital gain */
+ {0x5046, 0x12},
+ {0x5780, 0x3e}, /* DPC */
+ {0x5781, 0x0f},
+ {0x5782, 0x44},
+ {0x5783, 0x02},
+ {0x5784, 0x01},
+ {0x5785, 0x00},
+ {0x5786, 0x00},
+ {0x5787, 0x04},
+ {0x5788, 0x02},
+ {0x5789, 0x0f},
+ {0x578a, 0xfd},
+ {0x578b, 0xf5},
+ {0x578c, 0xf5},
+ {0x578d, 0x03},
+ {0x578e, 0x08},
+ {0x578f, 0x0c},
+ {0x5790, 0x08},
+ {0x5791, 0x04},
+ {0x5792, 0x00},
+ {0x5793, 0x52},
+ {0x5794, 0xa3}, /* DPC */
+ {0x5871, 0x0d}, /* Lenc */
+ {0x5870, 0x18},
+ {0x586e, 0x10},
+ {0x586f, 0x08},
+ {0x58f7, 0x01},
+ {0x58f8, 0x3d}, /* Lenc */
+ {0x5901, 0x00}, /* H skip off, V skip off */
+ {0x5b00, 0x02}, /* OTP DPC start address */
+ {0x5b01, 0x10}, /* OTP DPC start address */
+ {0x5b02, 0x03}, /* OTP DPC end address */
+ {0x5b03, 0xcf}, /* OTP DPC end address */
+ {0x5b05, 0x6c}, /* recover method = 2b11 */
+ {0x5e00, 0x00}, /* use 0x3ff to test pattern off */
+ {0x5e01, 0x41}, /* window cut enable */
+ {0x382d, 0x7f},
+ {0x4825, 0x3a}, /* lpx_p_min */
+ {0x4826, 0x40}, /* hs_prepare_min */
+ {0x4808, 0x25}, /* wake up delay in 1/1024 s */
+ {0x3763, 0x18},
+ {0x3768, 0xcc},
+ {0x470b, 0x28},
+ {0x4202, 0x00},
+ {0x400d, 0x10}, /* BLC offset trigger L */
+ {0x4040, 0x04}, /* BLC gain th2 */
+ {0x403e, 0x04}, /* BLC gain th1 */
+ {0x4041, 0xc6}, /* BLC */
+ {0x3007, 0x80},
+ {0x400a, 0x01},
+ {REG_NULL, 0x00},
+};
+
+/*
+ * Xclk 24Mhz
+ * max_framerate 60fps
+ * mipi_datarate per lane 720Mbps
+ */
+static const struct regval ov8858_1632x1224_regs_4lane[] = {
+ {0x0100, 0x00},
+ {0x3501, 0x4d}, /* exposure M */
+ {0x3502, 0x40}, /* exposure L */
+ {0x3808, 0x06}, /* x output size H */
+ {0x3809, 0x60}, /* x output size L */
+ {0x380a, 0x04}, /* y output size H */
+ {0x380b, 0xc8}, /* y output size L */
+ {0x380c, 0x07}, /* HTS H */
+ {0x380d, 0x88}, /* HTS L */
+ {0x380e, 0x04}, /* VTS H */
+ {0x380f, 0xdc}, /* VTS L */
+ {0x3814, 0x03}, /* x odd inc */
+ {0x3821, 0x67}, /* mirror on, bin on */
+ {0x382a, 0x03}, /* y odd inc */
+ {0x3830, 0x08},
+ {0x3836, 0x02},
+ {0x3f0a, 0x00},
+ {0x4001, 0x10}, /* total 128 black column */
+ {0x4022, 0x06}, /* Anchor left end H */
+ {0x4023, 0x00}, /* Anchor left end L */
+ {0x4025, 0x2a}, /* Anchor right start L */
+ {0x4027, 0x2b}, /* Anchor right end L */
+ {0x402b, 0x04}, /* top black line number */
+ {0x402f, 0x04}, /* bottom black line number */
+ {0x4500, 0x58},
+ {0x4600, 0x00},
+ {0x4601, 0xcb},
+ {0x382d, 0x7f},
+ {0x0100, 0x01},
+ {REG_NULL, 0x00},
+};
+
+/*
+ * Xclk 24Mhz
+ * max_framerate 30fps
+ * mipi_datarate per lane 720Mbps
+ */
+static const struct regval ov8858_3264x2448_regs_4lane[] = {
+ {0x0100, 0x00},
+ {0x3501, 0x9a}, /* exposure M */
+ {0x3502, 0x20}, /* exposure L */
+ {0x3808, 0x0c}, /* x output size H */
+ {0x3809, 0xc0}, /* x output size L */
+ {0x380a, 0x09}, /* y output size H */
+ {0x380b, 0x90}, /* y output size L */
+ {0x380c, 0x07}, /* HTS H */
+ {0x380d, 0x94}, /* HTS L */
+ {0x380e, 0x09}, /* VTS H */
+ {0x380f, 0xaa}, /* VTS L */
+ {0x3814, 0x01}, /* x odd inc */
+ {0x3821, 0x46}, /* mirror on, bin off */
+ {0x382a, 0x01}, /* y odd inc */
+ {0x3830, 0x06},
+ {0x3836, 0x01},
+ {0x3f0a, 0x00},
+ {0x4001, 0x00}, /* total 256 black column */
+ {0x4022, 0x0c}, /* Anchor left end H */
+ {0x4023, 0x60}, /* Anchor left end L */
+ {0x4025, 0x36}, /* Anchor right start L */
+ {0x4027, 0x37}, /* Anchor right end L */
+ {0x402b, 0x08}, /* top black line number */
+ {0x402f, 0x08}, /* interpolation x/y disable, Anchor one disable */
+ {0x4500, 0x58},
+ {0x4600, 0x01},
+ {0x4601, 0x97},
+ {0x382d, 0xff},
+ {REG_NULL, 0x00},
+};
+
+static const struct ov8858_mode ov8858_modes[] = {
+ {
+ .width = 3264,
+ .height = 2448,
+ .exp_def = 2464,
+ .hts_def = 1940 * 2,
+ .vts_def = 2472,
+ .reg_modes = {
+ .mode_2lanes = ov8858_3264x2448_regs_2lane,
+ .mode_4lanes = ov8858_3264x2448_regs_4lane,
+ },
+ },
+ {
+ .width = 1632,
+ .height = 1224,
+ .exp_def = 1232,
+ .hts_def = 1928 * 2,
+ .vts_def = 1244,
+ .reg_modes = {
+ .mode_2lanes = ov8858_1632x1224_regs_2lane,
+ .mode_4lanes = ov8858_1632x1224_regs_4lane,
+ },
+ },
+};
+
+static const s64 link_freq_menu_items[] = {
+ OV8858_LINK_FREQ
+};
+
+static const char * const ov8858_test_pattern_menu[] = {
+ "Disabled",
+ "Vertical Color Bar Type 1",
+ "Vertical Color Bar Type 2",
+ "Vertical Color Bar Type 3",
+ "Vertical Color Bar Type 4"
+};
+
+/* ----------------------------------------------------------------------------
+ * HW access
+ */
+
+static int ov8858_write(struct ov8858 *ov8858, u32 reg, u32 val, int *err)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&ov8858->subdev);
+ unsigned int len = (reg >> OV8858_REG_SIZE_SHIFT) & 3;
+ u16 addr = reg & OV8858_REG_ADDR_MASK;
+ u8 buf[6];
+ int ret;
+
+ if (err && *err)
+ return *err;
+
+ put_unaligned_be16(addr, buf);
+ put_unaligned_be32(val << (8 * (4 - len)), buf + 2);
+
+ ret = i2c_master_send(client, buf, len + 2);
+ if (ret != len + 2) {
+ ret = ret < 0 ? ret : -EIO;
+ if (err)
+ *err = ret;
+
+ dev_err(&client->dev,
+ "Failed to write reg %u: %d\n", addr, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ov8858_write_array(struct ov8858 *ov8858, const struct regval *regs)
+{
+ unsigned int i;
+ int ret = 0;
+
+ for (i = 0; ret == 0 && regs[i].addr != REG_NULL; ++i) {
+ ov8858_write(ov8858, OV8858_REG_8BIT(regs[i].addr),
+ regs[i].val, &ret);
+ }
+
+ return ret;
+}
+
+static int ov8858_read(struct ov8858 *ov8858, u32 reg, u32 *val)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&ov8858->subdev);
+ __be16 reg_addr_be = cpu_to_be16(reg & OV8858_REG_ADDR_MASK);
+ unsigned int len = (reg >> OV8858_REG_SIZE_SHIFT) & 3;
+ struct i2c_msg msgs[2];
+ __be32 data_be = 0;
+ u8 *data_be_p;
+ int ret;
+
+ data_be_p = (u8 *)&data_be;
+
+ /* Write register address */
+ msgs[0].addr = client->addr;
+ msgs[0].flags = 0;
+ msgs[0].len = 2;
+ msgs[0].buf = (u8 *)&reg_addr_be;
+
+ /* Read data from register */
+ msgs[1].addr = client->addr;
+ msgs[1].flags = I2C_M_RD;
+ msgs[1].len = len;
+ msgs[1].buf = &data_be_p[4 - len];
+
+ ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+ if (ret != ARRAY_SIZE(msgs)) {
+ ret = ret < 0 ? ret : -EIO;
+ dev_err(&client->dev,
+ "Failed to read reg %u: %d\n", reg, ret);
+ return ret;
+ }
+
+ *val = be32_to_cpu(data_be);
+
+ return 0;
+}
+
+/* ----------------------------------------------------------------------------
+ * Streaming
+ */
+
+static int ov8858_start_stream(struct ov8858 *ov8858,
+ struct v4l2_subdev_state *state)
+{
+ struct v4l2_mbus_framefmt *format;
+ const struct ov8858_mode *mode;
+ const struct regval *reg_list;
+ int ret;
+
+ ret = ov8858_write_array(ov8858, ov8858->global_regs);
+ if (ret)
+ return ret;
+
+ format = v4l2_subdev_get_pad_format(&ov8858->subdev, state, 0);
+ mode = v4l2_find_nearest_size(ov8858_modes, ARRAY_SIZE(ov8858_modes),
+ width, height, format->width,
+ format->height);
+
+ reg_list = ov8858->num_lanes == 4
+ ? mode->reg_modes.mode_4lanes
+ : mode->reg_modes.mode_2lanes;
+
+ ret = ov8858_write_array(ov8858, reg_list);
+ if (ret)
+ return ret;
+
+ /* 200 usec max to let PLL stabilize. */
+ fsleep(200);
+
+ ret = __v4l2_ctrl_handler_setup(&ov8858->ctrl_handler);
+ if (ret)
+ return ret;
+
+ ret = ov8858_write(ov8858, OV8858_REG_SC_CTRL0100,
+ OV8858_MODE_STREAMING, NULL);
+ if (ret)
+ return ret;
+
+ /* t5 (fixed) = 10msec before entering streaming state */
+ fsleep(10000);
+
+ return 0;
+}
+
+static int ov8858_stop_stream(struct ov8858 *ov8858)
+{
+ return ov8858_write(ov8858, OV8858_REG_SC_CTRL0100,
+ OV8858_MODE_SW_STANDBY, NULL);
+}
+
+static int ov8858_s_stream(struct v4l2_subdev *sd, int on)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct ov8858 *ov8858 = sd_to_ov8858(sd);
+ struct v4l2_subdev_state *state;
+ int ret = 0;
+
+ state = v4l2_subdev_lock_and_get_active_state(sd);
+
+ if (on) {
+ ret = pm_runtime_resume_and_get(&client->dev);
+ if (ret < 0)
+ goto unlock_and_return;
+
+ ret = ov8858_start_stream(ov8858, state);
+ if (ret) {
+ dev_err(&client->dev, "Failed to start streaming\n");
+ pm_runtime_put_sync(&client->dev);
+ goto unlock_and_return;
+ }
+ } else {
+ ov8858_stop_stream(ov8858);
+ pm_runtime_mark_last_busy(&client->dev);
+ pm_runtime_put_autosuspend(&client->dev);
+ }
+
+unlock_and_return:
+ v4l2_subdev_unlock_state(state);
+
+ return ret;
+}
+
+static const struct v4l2_subdev_video_ops ov8858_video_ops = {
+ .s_stream = ov8858_s_stream,
+};
+
+/* ----------------------------------------------------------------------------
+ * Pad ops
+ */
+
+static int ov8858_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_format *fmt)
+{
+ struct ov8858 *ov8858 = sd_to_ov8858(sd);
+ const struct ov8858_mode *mode;
+ s64 h_blank, vblank_def;
+
+ mode = v4l2_find_nearest_size(ov8858_modes, ARRAY_SIZE(ov8858_modes),
+ width, height, fmt->format.width,
+ fmt->format.height);
+
+ fmt->format.code = MEDIA_BUS_FMT_SBGGR10_1X10;
+ fmt->format.width = mode->width;
+ fmt->format.height = mode->height;
+ fmt->format.field = V4L2_FIELD_NONE;
+
+ /* Store the format in the current subdev state. */
+ *v4l2_subdev_get_pad_format(sd, state, 0) = fmt->format;
+
+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY)
+ return 0;
+
+ /* Adjust control limits when a new mode is applied. */
+ h_blank = mode->hts_def - mode->width;
+ __v4l2_ctrl_modify_range(ov8858->hblank, h_blank, h_blank, 1,
+ h_blank);
+
+ vblank_def = mode->vts_def - mode->height;
+ __v4l2_ctrl_modify_range(ov8858->vblank, vblank_def,
+ OV8858_VTS_MAX - mode->height, 1,
+ vblank_def);
+
+ return 0;
+}
+
+static int ov8858_enum_frame_sizes(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ if (fse->index >= ARRAY_SIZE(ov8858_modes))
+ return -EINVAL;
+
+ if (fse->code != MEDIA_BUS_FMT_SBGGR10_1X10)
+ return -EINVAL;
+
+ fse->min_width = ov8858_modes[fse->index].width;
+ fse->max_width = ov8858_modes[fse->index].width;
+ fse->max_height = ov8858_modes[fse->index].height;
+ fse->min_height = ov8858_modes[fse->index].height;
+
+ return 0;
+}
+
+static int ov8858_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ if (code->index != 0)
+ return -EINVAL;
+
+ code->code = MEDIA_BUS_FMT_SBGGR10_1X10;
+
+ return 0;
+}
+
+static int ov8858_init_cfg(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state)
+{
+ const struct ov8858_mode *def_mode = &ov8858_modes[0];
+ struct v4l2_subdev_format fmt = {
+ .which = V4L2_SUBDEV_FORMAT_TRY,
+ .format = {
+ .width = def_mode->width,
+ .height = def_mode->height,
+ },
+ };
+
+ ov8858_set_fmt(sd, sd_state, &fmt);
+
+ return 0;
+}
+
+static const struct v4l2_subdev_pad_ops ov8858_pad_ops = {
+ .init_cfg = ov8858_init_cfg,
+ .enum_mbus_code = ov8858_enum_mbus_code,
+ .enum_frame_size = ov8858_enum_frame_sizes,
+ .get_fmt = v4l2_subdev_get_fmt,
+ .set_fmt = ov8858_set_fmt,
+};
+
+static const struct v4l2_subdev_core_ops ov8858_core_ops = {
+ .subscribe_event = v4l2_ctrl_subdev_subscribe_event,
+ .unsubscribe_event = v4l2_event_subdev_unsubscribe,
+};
+
+static const struct v4l2_subdev_ops ov8858_subdev_ops = {
+ .core = &ov8858_core_ops,
+ .video = &ov8858_video_ops,
+ .pad = &ov8858_pad_ops,
+};
+
+/* ----------------------------------------------------------------------------
+ * Controls handling
+ */
+
+static int ov8858_enable_test_pattern(struct ov8858 *ov8858, u32 pattern)
+{
+ u32 val;
+
+ if (pattern)
+ val = (pattern - 1) | OV8858_TEST_PATTERN_ENABLE;
+ else
+ val = OV8858_TEST_PATTERN_DISABLE;
+
+ return ov8858_write(ov8858, OV8858_REG_TEST_PATTERN, val, NULL);
+}
+
+static int ov8858_set_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct ov8858 *ov8858 = container_of(ctrl->handler,
+ struct ov8858, ctrl_handler);
+
+ struct i2c_client *client = v4l2_get_subdevdata(&ov8858->subdev);
+ struct v4l2_mbus_framefmt *format;
+ struct v4l2_subdev_state *state;
+ u16 digi_gain;
+ s64 max_exp;
+ int ret;
+
+ /*
+ * The control handler and the subdev state use the same mutex and the
+ * mutex is guaranteed to be locked:
+ * - by the core when s_ctrl is called int the VIDIOC_S_CTRL call path
+ * - by the driver when s_ctrl is called in the s_stream(1) call path
+ */
+ state = v4l2_subdev_get_locked_active_state(&ov8858->subdev);
+ format = v4l2_subdev_get_pad_format(&ov8858->subdev, state, 0);
+
+ /* Propagate change of current control to all related controls */
+ switch (ctrl->id) {
+ case V4L2_CID_VBLANK:
+ /* Update max exposure while meeting expected vblanking */
+ max_exp = format->height + ctrl->val - OV8858_EXPOSURE_MARGIN;
+ __v4l2_ctrl_modify_range(ov8858->exposure,
+ ov8858->exposure->minimum, max_exp,
+ ov8858->exposure->step,
+ ov8858->exposure->default_value);
+ break;
+ }
+
+ if (!pm_runtime_get_if_in_use(&client->dev))
+ return 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_EXPOSURE:
+ /* 4 least significant bits of exposure are fractional part */
+ ret = ov8858_write(ov8858, OV8858_REG_LONG_EXPO,
+ ctrl->val << 4, NULL);
+ break;
+ case V4L2_CID_ANALOGUE_GAIN:
+ ret = ov8858_write(ov8858, OV8858_REG_LONG_GAIN,
+ ctrl->val, NULL);
+ break;
+ case V4L2_CID_DIGITAL_GAIN:
+ /*
+ * Digital gain is assembled as:
+ * 0x350a[7:0] = dgain[13:6]
+ * 0x350b[5:0] = dgain[5:0]
+ * Reassemble the control value to write it in one go.
+ */
+ digi_gain = (ctrl->val & OV8858_LONG_DIGIGAIN_L_MASK)
+ | ((ctrl->val & OV8858_LONG_DIGIGAIN_H_MASK) <<
+ OV8858_LONG_DIGIGAIN_H_SHIFT);
+ ret = ov8858_write(ov8858, OV8858_REG_LONG_DIGIGAIN,
+ digi_gain, NULL);
+ break;
+ case V4L2_CID_VBLANK:
+ ret = ov8858_write(ov8858, OV8858_REG_VTS,
+ ctrl->val + format->height, NULL);
+ break;
+ case V4L2_CID_TEST_PATTERN:
+ ret = ov8858_enable_test_pattern(ov8858, ctrl->val);
+ break;
+ default:
+ ret = -EINVAL;
+ dev_warn(&client->dev, "%s Unhandled id: 0x%x\n",
+ __func__, ctrl->id);
+ break;
+ }
+
+ pm_runtime_put(&client->dev);
+
+ return ret;
+}
+
+static const struct v4l2_ctrl_ops ov8858_ctrl_ops = {
+ .s_ctrl = ov8858_set_ctrl,
+};
+
+/* ----------------------------------------------------------------------------
+ * Power Management
+ */
+
+static int ov8858_power_on(struct ov8858 *ov8858)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&ov8858->subdev);
+ struct device *dev = &client->dev;
+ unsigned long delay_us;
+ int ret;
+
+ if (clk_get_rate(ov8858->xvclk) != OV8858_XVCLK_FREQ)
+ dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n");
+
+ ret = clk_prepare_enable(ov8858->xvclk);
+ if (ret < 0) {
+ dev_err(dev, "Failed to enable xvclk\n");
+ return ret;
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(ov8858_supply_names),
+ ov8858->supplies);
+ if (ret < 0) {
+ dev_err(dev, "Failed to enable regulators\n");
+ goto disable_clk;
+ }
+
+ /*
+ * The chip manual only suggests 8192 cycles prior to first SCCB
+ * transaction, but a double sleep between the release of gpios
+ * helps with sporadic failures observed at probe time.
+ */
+ delay_us = DIV_ROUND_UP(8192, OV8858_XVCLK_FREQ / 1000 / 1000);
+
+ gpiod_set_value_cansleep(ov8858->reset_gpio, 0);
+ fsleep(delay_us);
+ gpiod_set_value_cansleep(ov8858->pwdn_gpio, 0);
+ fsleep(delay_us);
+
+ return 0;
+
+disable_clk:
+ clk_disable_unprepare(ov8858->xvclk);
+
+ return ret;
+}
+
+static void ov8858_power_off(struct ov8858 *ov8858)
+{
+ gpiod_set_value_cansleep(ov8858->pwdn_gpio, 1);
+ clk_disable_unprepare(ov8858->xvclk);
+ gpiod_set_value_cansleep(ov8858->reset_gpio, 1);
+
+ regulator_bulk_disable(ARRAY_SIZE(ov8858_supply_names),
+ ov8858->supplies);
+}
+
+static int ov8858_runtime_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct ov8858 *ov8858 = sd_to_ov8858(sd);
+
+ return ov8858_power_on(ov8858);
+}
+
+static int ov8858_runtime_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct ov8858 *ov8858 = sd_to_ov8858(sd);
+
+ ov8858_power_off(ov8858);
+
+ return 0;
+}
+
+static const struct dev_pm_ops ov8858_pm_ops = {
+ SET_RUNTIME_PM_OPS(ov8858_runtime_suspend,
+ ov8858_runtime_resume, NULL)
+};
+
+/* ----------------------------------------------------------------------------
+ * Probe and initialization
+ */
+
+static int ov8858_init_ctrls(struct ov8858 *ov8858)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&ov8858->subdev);
+ struct v4l2_ctrl_handler *handler = &ov8858->ctrl_handler;
+ const struct ov8858_mode *mode = &ov8858_modes[0];
+ struct v4l2_fwnode_device_properties props;
+ s64 exposure_max, vblank_def;
+ unsigned int pixel_rate;
+ struct v4l2_ctrl *ctrl;
+ u32 h_blank;
+ int ret;
+
+ ret = v4l2_ctrl_handler_init(handler, 10);
+ if (ret)
+ return ret;
+
+ ctrl = v4l2_ctrl_new_int_menu(handler, NULL, V4L2_CID_LINK_FREQ,
+ 0, 0, link_freq_menu_items);
+ if (ctrl)
+ ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+ /* pixel rate = link frequency * 2 * lanes / bpp */
+ pixel_rate = OV8858_LINK_FREQ * 2 * ov8858->num_lanes / 10;
+ v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE,
+ 0, pixel_rate, 1, pixel_rate);
+
+ h_blank = mode->hts_def - mode->width;
+ ov8858->hblank = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK,
+ h_blank, h_blank, 1, h_blank);
+ if (ov8858->hblank)
+ ov8858->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+ vblank_def = mode->vts_def - mode->height;
+ ov8858->vblank = v4l2_ctrl_new_std(handler, &ov8858_ctrl_ops,
+ V4L2_CID_VBLANK, vblank_def,
+ OV8858_VTS_MAX - mode->height,
+ 1, vblank_def);
+
+ exposure_max = mode->vts_def - OV8858_EXPOSURE_MARGIN;
+ ov8858->exposure = v4l2_ctrl_new_std(handler, &ov8858_ctrl_ops,
+ V4L2_CID_EXPOSURE,
+ OV8858_EXPOSURE_MIN,
+ exposure_max, OV8858_EXPOSURE_STEP,
+ mode->exp_def);
+
+ v4l2_ctrl_new_std(handler, &ov8858_ctrl_ops, V4L2_CID_ANALOGUE_GAIN,
+ OV8858_LONG_GAIN_MIN, OV8858_LONG_GAIN_MAX,
+ OV8858_LONG_GAIN_STEP, OV8858_LONG_GAIN_DEFAULT);
+
+ v4l2_ctrl_new_std(handler, &ov8858_ctrl_ops, V4L2_CID_DIGITAL_GAIN,
+ OV8858_LONG_DIGIGAIN_MIN, OV8858_LONG_DIGIGAIN_MAX,
+ OV8858_LONG_DIGIGAIN_STEP,
+ OV8858_LONG_DIGIGAIN_DEFAULT);
+
+ v4l2_ctrl_new_std_menu_items(handler, &ov8858_ctrl_ops,
+ V4L2_CID_TEST_PATTERN,
+ ARRAY_SIZE(ov8858_test_pattern_menu) - 1,
+ 0, 0, ov8858_test_pattern_menu);
+
+ if (handler->error) {
+ ret = handler->error;
+ goto err_free_handler;
+ }
+
+ ret = v4l2_fwnode_device_parse(&client->dev, &props);
+ if (ret)
+ goto err_free_handler;
+
+ ret = v4l2_ctrl_new_fwnode_properties(handler, &ov8858_ctrl_ops,
+ &props);
+ if (ret)
+ goto err_free_handler;
+
+ ov8858->subdev.ctrl_handler = handler;
+
+ return 0;
+
+err_free_handler:
+ dev_err(&client->dev, "Failed to init controls: %d\n", ret);
+ v4l2_ctrl_handler_free(handler);
+
+ return ret;
+}
+
+static int ov8858_check_sensor_id(struct ov8858 *ov8858)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&ov8858->subdev);
+ u32 id = 0;
+ int ret;
+
+ ret = ov8858_read(ov8858, OV8858_REG_CHIP_ID, &id);
+ if (ret)
+ return ret;
+
+ if (id != OV8858_CHIP_ID) {
+ dev_err(&client->dev, "Unexpected sensor id 0x%x\n", id);
+ return -ENODEV;
+ }
+
+ ret = ov8858_read(ov8858, OV8858_REG_SUB_ID, &id);
+ if (ret)
+ return ret;
+
+ dev_info(&client->dev, "Detected OV8858 sensor, revision 0x%x\n", id);
+
+ if (id == OV8858_R2A) {
+ /* R2A supports 2 and 4 lanes modes. */
+ ov8858->global_regs = ov8858->num_lanes == 4
+ ? ov8858_global_regs_r2a_4lane
+ : ov8858_global_regs_r2a_2lane;
+ } else if (ov8858->num_lanes == 2) {
+ /*
+ * R1A only supports 2 lanes mode and it's only partially
+ * supported.
+ */
+ ov8858->global_regs = ov8858_global_regs_r1a;
+ dev_warn(&client->dev, "R1A may not work well!\n");
+ } else {
+ dev_err(&client->dev,
+ "Unsupported number of data lanes for R1A revision.\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int ov8858_configure_regulators(struct ov8858 *ov8858)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&ov8858->subdev);
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(ov8858_supply_names); i++)
+ ov8858->supplies[i].supply = ov8858_supply_names[i];
+
+ return devm_regulator_bulk_get(&client->dev,
+ ARRAY_SIZE(ov8858_supply_names),
+ ov8858->supplies);
+}
+
+static int ov8858_parse_of(struct ov8858 *ov8858)
+{
+ struct v4l2_fwnode_endpoint vep = { .bus_type = V4L2_MBUS_CSI2_DPHY };
+ struct i2c_client *client = v4l2_get_subdevdata(&ov8858->subdev);
+ struct device *dev = &client->dev;
+ struct fwnode_handle *endpoint;
+ int ret;
+
+ endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL);
+ if (!endpoint) {
+ dev_err(dev, "Failed to get endpoint\n");
+ return -EINVAL;
+ }
+
+ ret = v4l2_fwnode_endpoint_parse(endpoint, &vep);
+ if (ret) {
+ dev_err(dev, "Failed to parse endpoint: %d\n", ret);
+ fwnode_handle_put(endpoint);
+ return ret;
+ }
+
+ ov8858->num_lanes = vep.bus.mipi_csi2.num_data_lanes;
+ switch (ov8858->num_lanes) {
+ case 4:
+ case 2:
+ break;
+ default:
+ dev_err(dev, "Unsupported number of data lanes %u\n",
+ ov8858->num_lanes);
+ fwnode_handle_put(endpoint);
+ return -EINVAL;
+ }
+
+ ov8858->subdev.fwnode = endpoint;
+
+ return 0;
+}
+
+static int ov8858_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct v4l2_subdev *sd;
+ struct ov8858 *ov8858;
+ int ret;
+
+ ov8858 = devm_kzalloc(dev, sizeof(*ov8858), GFP_KERNEL);
+ if (!ov8858)
+ return -ENOMEM;
+
+ ov8858->xvclk = devm_clk_get(dev, "xvclk");
+ if (IS_ERR(ov8858->xvclk))
+ return dev_err_probe(dev, PTR_ERR(ov8858->xvclk),
+ "Failed to get xvclk\n");
+
+ ov8858->reset_gpio = devm_gpiod_get_optional(dev, "reset",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(ov8858->reset_gpio))
+ return dev_err_probe(dev, PTR_ERR(ov8858->reset_gpio),
+ "Failed to get reset gpio\n");
+
+ ov8858->pwdn_gpio = devm_gpiod_get_optional(dev, "powerdown",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(ov8858->pwdn_gpio))
+ return dev_err_probe(dev, PTR_ERR(ov8858->pwdn_gpio),
+ "Failed to get powerdown gpio\n");
+
+ v4l2_i2c_subdev_init(&ov8858->subdev, client, &ov8858_subdev_ops);
+
+ ret = ov8858_configure_regulators(ov8858);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to get regulators\n");
+
+ ret = ov8858_parse_of(ov8858);
+ if (ret)
+ return ret;
+
+ ret = ov8858_init_ctrls(ov8858);
+ if (ret)
+ goto err_put_fwnode;
+
+ sd = &ov8858->subdev;
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
+ ov8858->pad.flags = MEDIA_PAD_FL_SOURCE;
+ sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;
+ ret = media_entity_pads_init(&sd->entity, 1, &ov8858->pad);
+ if (ret < 0)
+ goto err_free_handler;
+
+ sd->state_lock = ov8858->ctrl_handler.lock;
+ ret = v4l2_subdev_init_finalize(sd);
+ if (ret < 0) {
+ dev_err(&client->dev, "Subdev initialization error %d\n", ret);
+ goto err_clean_entity;
+ }
+
+ ret = ov8858_power_on(ov8858);
+ if (ret)
+ goto err_clean_entity;
+
+ pm_runtime_set_active(dev);
+ pm_runtime_get_noresume(dev);
+ pm_runtime_enable(dev);
+
+ ret = ov8858_check_sensor_id(ov8858);
+ if (ret)
+ goto err_power_off;
+
+ pm_runtime_set_autosuspend_delay(dev, 1000);
+ pm_runtime_use_autosuspend(dev);
+
+ ret = v4l2_async_register_subdev_sensor(sd);
+ if (ret) {
+ dev_err(dev, "v4l2 async register subdev failed\n");
+ goto err_power_off;
+ }
+
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
+
+ return 0;
+
+err_power_off:
+ pm_runtime_disable(dev);
+ pm_runtime_put_noidle(dev);
+ ov8858_power_off(ov8858);
+err_clean_entity:
+ media_entity_cleanup(&sd->entity);
+err_free_handler:
+ v4l2_ctrl_handler_free(&ov8858->ctrl_handler);
+err_put_fwnode:
+ fwnode_handle_put(ov8858->subdev.fwnode);
+
+ return ret;
+}
+
+static void ov8858_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct ov8858 *ov8858 = sd_to_ov8858(sd);
+
+ v4l2_async_unregister_subdev(sd);
+ media_entity_cleanup(&sd->entity);
+ v4l2_ctrl_handler_free(&ov8858->ctrl_handler);
+ fwnode_handle_put(ov8858->subdev.fwnode);
+
+ pm_runtime_disable(&client->dev);
+ if (!pm_runtime_status_suspended(&client->dev))
+ ov8858_power_off(ov8858);
+ pm_runtime_set_suspended(&client->dev);
+}
+
+static const struct of_device_id ov8858_of_match[] = {
+ { .compatible = "ovti,ov8858" },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, ov8858_of_match);
+
+static struct i2c_driver ov8858_i2c_driver = {
+ .driver = {
+ .name = "ov8858",
+ .pm = &ov8858_pm_ops,
+ .of_match_table = ov8858_of_match,
+ },
+ .probe_new = &ov8858_probe,
+ .remove = &ov8858_remove,
+};
+
+module_i2c_driver(ov8858_i2c_driver);
+
+MODULE_DESCRIPTION("OmniVision OV8858 sensor driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/i2c/ov9282.c b/drivers/media/i2c/ov9282.c
index 37a55d53af56..7f46cac38aab 100644
--- a/drivers/media/i2c/ov9282.c
+++ b/drivers/media/i2c/ov9282.c
@@ -148,7 +148,6 @@ struct ov9282_mode {
/**
* struct ov9282 - ov9282 sensor device structure
* @dev: Pointer to generic device
- * @client: Pointer to i2c client
* @sd: V4L2 sub-device
* @pad: Media pad. Only one pad supported
* @reset_gpio: Sensor reset gpio
@@ -170,7 +169,6 @@ struct ov9282_mode {
*/
struct ov9282 {
struct device *dev;
- struct i2c_client *client;
struct v4l2_subdev sd;
struct media_pad pad;
struct gpio_desc *reset_gpio;
@@ -1144,10 +1142,9 @@ static int ov9282_parse_hw_config(struct ov9282 *ov9282)
}
ret = ov9282_configure_regulators(ov9282);
- if (ret) {
- dev_err(ov9282->dev, "Failed to get power regulators\n");
- return ret;
- }
+ if (ret)
+ return dev_err_probe(ov9282->dev, ret,
+ "Failed to get power regulators\n");
rate = clk_get_rate(ov9282->inclk);
if (rate != OV9282_INCLK_RATE) {
diff --git a/drivers/media/i2c/s5c73m3/s5c73m3-core.c b/drivers/media/i2c/s5c73m3/s5c73m3-core.c
index 59b03b0860d5..7938a3327d3e 100644
--- a/drivers/media/i2c/s5c73m3/s5c73m3-core.c
+++ b/drivers/media/i2c/s5c73m3/s5c73m3-core.c
@@ -26,7 +26,6 @@
#include <media/v4l2-device.h>
#include <media/v4l2-subdev.h>
#include <media/v4l2-mediabus.h>
-#include <media/i2c/s5c73m3.h>
#include <media/v4l2-fwnode.h>
#include "s5c73m3.h"
@@ -436,7 +435,7 @@ static int __s5c73m3_s_stream(struct s5c73m3 *state, struct v4l2_subdev *sd,
state->streaming = !!on;
if (!on)
- return ret;
+ return 0;
if (state->apply_fiv) {
ret = s5c73m3_set_frame_rate(state);
@@ -1522,25 +1521,16 @@ static const struct v4l2_subdev_ops oif_subdev_ops = {
.video = &s5c73m3_oif_video_ops,
};
-static int s5c73m3_get_platform_data(struct s5c73m3 *state)
+static int s5c73m3_get_dt_data(struct s5c73m3 *state)
{
- struct i2c_client *c = state->i2c_client;
- struct device *dev = &c->dev;
- const struct s5c73m3_platform_data *pdata = dev->platform_data;
+ struct device *dev = &state->i2c_client->dev;
struct device_node *node = dev->of_node;
struct device_node *node_ep;
struct v4l2_fwnode_endpoint ep = { .bus_type = 0 };
int ret;
- if (!node) {
- if (!pdata) {
- dev_err(dev, "Platform data not specified\n");
- return -EINVAL;
- }
-
- state->mclk_frequency = pdata->mclk_frequency;
- return 0;
- }
+ if (!node)
+ return -EINVAL;
state->clock = devm_clk_get(dev, S5C73M3_CLK_NAME);
if (IS_ERR(state->clock))
@@ -1603,7 +1593,7 @@ static int s5c73m3_probe(struct i2c_client *client)
return -ENOMEM;
state->i2c_client = client;
- ret = s5c73m3_get_platform_data(state);
+ ret = s5c73m3_get_dt_data(state);
if (ret < 0)
return ret;
diff --git a/drivers/media/i2c/s5c73m3/s5c73m3-ctrls.c b/drivers/media/i2c/s5c73m3/s5c73m3-ctrls.c
index e3543ae384ed..1c8103670fa2 100644
--- a/drivers/media/i2c/s5c73m3/s5c73m3-ctrls.c
+++ b/drivers/media/i2c/s5c73m3/s5c73m3-ctrls.c
@@ -23,7 +23,6 @@
#include <media/v4l2-device.h>
#include <media/v4l2-subdev.h>
#include <media/v4l2-mediabus.h>
-#include <media/i2c/s5c73m3.h>
#include "s5c73m3.h"
diff --git a/drivers/media/i2c/s5c73m3/s5c73m3.h b/drivers/media/i2c/s5c73m3/s5c73m3.h
index 1fc7df41c5ee..627e80cf5b72 100644
--- a/drivers/media/i2c/s5c73m3/s5c73m3.h
+++ b/drivers/media/i2c/s5c73m3/s5c73m3.h
@@ -16,7 +16,6 @@
#include <media/v4l2-common.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-subdev.h>
-#include <media/i2c/s5c73m3.h>
#define DRIVER_NAME "S5C73M3"
diff --git a/drivers/media/i2c/saa7115.c b/drivers/media/i2c/saa7115.c
index 86e70a980218..efeda3956f81 100644
--- a/drivers/media/i2c/saa7115.c
+++ b/drivers/media/i2c/saa7115.c
@@ -1804,9 +1804,9 @@ static int saa711x_detect_chip(struct i2c_client *client,
return -ENODEV;
}
-static int saa711x_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int saa711x_probe(struct i2c_client *client)
{
+ const struct i2c_device_id *id = i2c_client_get_device_id(client);
struct saa711x_state *state;
struct v4l2_subdev *sd;
struct v4l2_ctrl_handler *hdl;
@@ -1951,7 +1951,7 @@ static struct i2c_driver saa711x_driver = {
.driver = {
.name = "saa7115",
},
- .probe = saa711x_probe,
+ .probe_new = saa711x_probe,
.remove = saa711x_remove,
.id_table = saa711x_id,
};
diff --git a/drivers/media/i2c/saa7127.c b/drivers/media/i2c/saa7127.c
index 78c9388c2ea1..f98f3a1c38a9 100644
--- a/drivers/media/i2c/saa7127.c
+++ b/drivers/media/i2c/saa7127.c
@@ -708,9 +708,9 @@ static const struct v4l2_subdev_ops saa7127_ops = {
/* ----------------------------------------------------------------------- */
-static int saa7127_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int saa7127_probe(struct i2c_client *client)
{
+ const struct i2c_device_id *id = i2c_client_get_device_id(client);
struct saa7127_state *state;
struct v4l2_subdev *sd;
struct v4l2_sliced_vbi_data vbi = { 0, 0, 0, 0 }; /* set to disabled */
@@ -810,7 +810,7 @@ static struct i2c_driver saa7127_driver = {
.driver = {
.name = "saa7127",
},
- .probe = saa7127_probe,
+ .probe_new = saa7127_probe,
.remove = saa7127_remove,
.id_table = saa7127_id,
};
diff --git a/drivers/media/i2c/st-vgxy61.c b/drivers/media/i2c/st-vgxy61.c
index 826baf4e064d..5dcabee6677d 100644
--- a/drivers/media/i2c/st-vgxy61.c
+++ b/drivers/media/i2c/st-vgxy61.c
@@ -5,7 +5,6 @@
* Copyright (C) 2022 STMicroelectronics SA
*/
-#include <asm-generic/unaligned.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
@@ -15,6 +14,9 @@
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
#include <linux/units.h>
+
+#include <asm/unaligned.h>
+
#include <media/mipi-csi2.h>
#include <media/v4l2-async.h>
#include <media/v4l2-ctrls.h>
diff --git a/drivers/media/i2c/tc358746.c b/drivers/media/i2c/tc358746.c
index d1f552bd81d4..4063754a6732 100644
--- a/drivers/media/i2c/tc358746.c
+++ b/drivers/media/i2c/tc358746.c
@@ -406,7 +406,7 @@ tc358746_apply_pll_config(struct tc358746 *tc358746)
val = PLL_FRS(ilog2(post)) | RESETB | PLL_EN;
mask = PLL_FRS_MASK | RESETB | PLL_EN;
- tc358746_update_bits(tc358746, PLLCTL1_REG, mask, val);
+ err = tc358746_update_bits(tc358746, PLLCTL1_REG, mask, val);
if (err)
return err;
@@ -988,6 +988,8 @@ static int __maybe_unused
tc358746_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
{
struct tc358746 *tc358746 = to_tc358746(sd);
+ u32 val;
+ int err;
/* 32-bit registers starting from CLW_DPHYCONTTX */
reg->size = reg->reg < CLW_DPHYCONTTX_REG ? 2 : 4;
@@ -995,12 +997,13 @@ tc358746_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
if (!pm_runtime_get_if_in_use(sd->dev))
return 0;
- tc358746_read(tc358746, reg->reg, (u32 *)&reg->val);
+ err = tc358746_read(tc358746, reg->reg, &val);
+ reg->val = val;
pm_runtime_mark_last_busy(sd->dev);
pm_runtime_put_sync_autosuspend(sd->dev);
- return 0;
+ return err;
}
static int __maybe_unused
diff --git a/drivers/media/i2c/tda1997x.c b/drivers/media/i2c/tda1997x.c
index 83931826cf6f..27f6393dc327 100644
--- a/drivers/media/i2c/tda1997x.c
+++ b/drivers/media/i2c/tda1997x.c
@@ -2519,9 +2519,9 @@ static struct snd_soc_component_driver tda1997x_codec_driver = {
.endianness = 1,
};
-static int tda1997x_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int tda1997x_probe(struct i2c_client *client)
{
+ const struct i2c_device_id *id = i2c_client_get_device_id(client);
struct tda1997x_state *state;
struct tda1997x_platform_data *pdata;
struct v4l2_subdev *sd;
@@ -2834,7 +2834,7 @@ static struct i2c_driver tda1997x_i2c_driver = {
.name = "tda1997x",
.of_match_table = of_match_ptr(tda1997x_of_id),
},
- .probe = tda1997x_probe,
+ .probe_new = tda1997x_probe,
.remove = tda1997x_remove,
.id_table = tda1997x_i2c_id,
};
diff --git a/drivers/media/i2c/tvaudio.c b/drivers/media/i2c/tvaudio.c
index 9f1ed078b661..a54c76d9e23b 100644
--- a/drivers/media/i2c/tvaudio.c
+++ b/drivers/media/i2c/tvaudio.c
@@ -1934,8 +1934,9 @@ static const struct v4l2_subdev_ops tvaudio_ops = {
/* i2c registration */
-static int tvaudio_probe(struct i2c_client *client, const struct i2c_device_id *id)
+static int tvaudio_probe(struct i2c_client *client)
{
+ const struct i2c_device_id *id = i2c_client_get_device_id(client);
struct CHIPSTATE *chip;
struct CHIPDESC *desc;
struct v4l2_subdev *sd;
@@ -2094,7 +2095,7 @@ static struct i2c_driver tvaudio_driver = {
.driver = {
.name = "tvaudio",
},
- .probe = tvaudio_probe,
+ .probe_new = tvaudio_probe,
.remove = tvaudio_remove,
.id_table = tvaudio_id,
};
diff --git a/drivers/media/i2c/tvp514x.c b/drivers/media/i2c/tvp514x.c
index a746d96875f9..f294cae72b01 100644
--- a/drivers/media/i2c/tvp514x.c
+++ b/drivers/media/i2c/tvp514x.c
@@ -1017,14 +1017,14 @@ done:
/**
* tvp514x_probe() - decoder driver i2c probe handler
* @client: i2c driver client device structure
- * @id: i2c driver id table
*
* Register decoder as an i2c client device and V4L2
* device.
*/
static int
-tvp514x_probe(struct i2c_client *client, const struct i2c_device_id *id)
+tvp514x_probe(struct i2c_client *client)
{
+ const struct i2c_device_id *id = i2c_client_get_device_id(client);
struct tvp514x_platform_data *pdata = tvp514x_get_pdata(client);
struct tvp514x_decoder *decoder;
struct v4l2_subdev *sd;
@@ -1208,7 +1208,7 @@ static struct i2c_driver tvp514x_driver = {
.of_match_table = of_match_ptr(tvp514x_of_match),
.name = TVP514X_MODULE_NAME,
},
- .probe = tvp514x_probe,
+ .probe_new = tvp514x_probe,
.remove = tvp514x_remove,
.id_table = tvp514x_id,
};
diff --git a/drivers/media/i2c/video-i2c.c b/drivers/media/i2c/video-i2c.c
index f15ef2d13059..dddf9827b314 100644
--- a/drivers/media/i2c/video-i2c.c
+++ b/drivers/media/i2c/video-i2c.c
@@ -757,9 +757,9 @@ static void video_i2c_release(struct video_device *vdev)
kfree(data);
}
-static int video_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int video_i2c_probe(struct i2c_client *client)
{
+ const struct i2c_device_id *id = i2c_client_get_device_id(client);
struct video_i2c_data *data;
struct v4l2_device *v4l2_dev;
struct vb2_queue *queue;
@@ -959,7 +959,7 @@ static struct i2c_driver video_i2c_driver = {
.of_match_table = video_i2c_of_match,
.pm = &video_i2c_pm_ops,
},
- .probe = video_i2c_probe,
+ .probe_new = video_i2c_probe,
.remove = video_i2c_remove,
.id_table = video_i2c_id_table,
};
diff --git a/drivers/media/mc/mc-entity.c b/drivers/media/mc/mc-entity.c
index b8bcbc734eaf..e7216a985ba6 100644
--- a/drivers/media/mc/mc-entity.c
+++ b/drivers/media/mc/mc-entity.c
@@ -226,7 +226,13 @@ EXPORT_SYMBOL_GPL(media_entity_pads_init);
* Graph traversal
*/
-/*
+/**
+ * media_entity_has_pad_interdep - Check interdependency between two pads
+ *
+ * @entity: The entity
+ * @pad0: The first pad index
+ * @pad1: The second pad index
+ *
* This function checks the interdependency inside the entity between @pad0
* and @pad1. If two pads are interdependent they are part of the same pipeline
* and enabling one of the pads means that the other pad will become "locked"
@@ -236,6 +242,13 @@ EXPORT_SYMBOL_GPL(media_entity_pads_init);
* to check the dependency inside the entity between @pad0 and @pad1. If the
* has_pad_interdep operation is not implemented, all pads of the entity are
* considered to be interdependent.
+ *
+ * One of @pad0 and @pad1 must be a sink pad and the other one a source pad.
+ * The function returns false if both pads are sinks or sources.
+ *
+ * The caller must hold entity->graph_obj.mdev->mutex.
+ *
+ * Return: true if the pads are connected internally and false otherwise.
*/
static bool media_entity_has_pad_interdep(struct media_entity *entity,
unsigned int pad0, unsigned int pad1)
@@ -295,7 +308,7 @@ static struct media_entity *stack_pop(struct media_graph *graph)
*
* Reserve resources for graph walk in media device's current
* state. The memory must be released using
- * media_graph_walk_free().
+ * media_graph_walk_cleanup().
*
* Returns error on failure, zero on success.
*/
@@ -703,7 +716,7 @@ done:
__must_check int __media_pipeline_start(struct media_pad *pad,
struct media_pipeline *pipe)
{
- struct media_device *mdev = pad->entity->graph_obj.mdev;
+ struct media_device *mdev = pad->graph_obj.mdev;
struct media_pipeline_pad *err_ppad;
struct media_pipeline_pad *ppad;
int ret;
@@ -711,8 +724,8 @@ __must_check int __media_pipeline_start(struct media_pad *pad,
lockdep_assert_held(&mdev->graph_mutex);
/*
- * If the entity is already part of a pipeline, that pipeline must
- * be the same as the pipe given to media_pipeline_start().
+ * If the pad is already part of a pipeline, that pipeline must be the
+ * same as the pipe given to media_pipeline_start().
*/
if (WARN_ON(pad->pipe && pad->pipe != pipe))
return -EINVAL;
@@ -851,7 +864,7 @@ EXPORT_SYMBOL_GPL(__media_pipeline_start);
__must_check int media_pipeline_start(struct media_pad *pad,
struct media_pipeline *pipe)
{
- struct media_device *mdev = pad->entity->graph_obj.mdev;
+ struct media_device *mdev = pad->graph_obj.mdev;
int ret;
mutex_lock(&mdev->graph_mutex);
@@ -888,7 +901,7 @@ EXPORT_SYMBOL_GPL(__media_pipeline_stop);
void media_pipeline_stop(struct media_pad *pad)
{
- struct media_device *mdev = pad->entity->graph_obj.mdev;
+ struct media_device *mdev = pad->graph_obj.mdev;
mutex_lock(&mdev->graph_mutex);
__media_pipeline_stop(pad);
@@ -898,7 +911,7 @@ EXPORT_SYMBOL_GPL(media_pipeline_stop);
__must_check int media_pipeline_alloc_start(struct media_pad *pad)
{
- struct media_device *mdev = pad->entity->graph_obj.mdev;
+ struct media_device *mdev = pad->graph_obj.mdev;
struct media_pipeline *new_pipe = NULL;
struct media_pipeline *pipe;
int ret;
@@ -906,7 +919,7 @@ __must_check int media_pipeline_alloc_start(struct media_pad *pad)
mutex_lock(&mdev->graph_mutex);
/*
- * Is the entity already part of a pipeline? If not, we need to allocate
+ * Is the pad already part of a pipeline? If not, we need to allocate
* a pipe.
*/
pipe = media_pad_pipeline(pad);
@@ -932,6 +945,61 @@ out:
}
EXPORT_SYMBOL_GPL(media_pipeline_alloc_start);
+struct media_pad *
+__media_pipeline_pad_iter_next(struct media_pipeline *pipe,
+ struct media_pipeline_pad_iter *iter,
+ struct media_pad *pad)
+{
+ if (!pad)
+ iter->cursor = pipe->pads.next;
+
+ if (iter->cursor == &pipe->pads)
+ return NULL;
+
+ pad = list_entry(iter->cursor, struct media_pipeline_pad, list)->pad;
+ iter->cursor = iter->cursor->next;
+
+ return pad;
+}
+EXPORT_SYMBOL_GPL(__media_pipeline_pad_iter_next);
+
+int media_pipeline_entity_iter_init(struct media_pipeline *pipe,
+ struct media_pipeline_entity_iter *iter)
+{
+ return media_entity_enum_init(&iter->ent_enum, pipe->mdev);
+}
+EXPORT_SYMBOL_GPL(media_pipeline_entity_iter_init);
+
+void media_pipeline_entity_iter_cleanup(struct media_pipeline_entity_iter *iter)
+{
+ media_entity_enum_cleanup(&iter->ent_enum);
+}
+EXPORT_SYMBOL_GPL(media_pipeline_entity_iter_cleanup);
+
+struct media_entity *
+__media_pipeline_entity_iter_next(struct media_pipeline *pipe,
+ struct media_pipeline_entity_iter *iter,
+ struct media_entity *entity)
+{
+ if (!entity)
+ iter->cursor = pipe->pads.next;
+
+ while (iter->cursor != &pipe->pads) {
+ struct media_pipeline_pad *ppad;
+ struct media_entity *entity;
+
+ ppad = list_entry(iter->cursor, struct media_pipeline_pad, list);
+ entity = ppad->pad->entity;
+ iter->cursor = iter->cursor->next;
+
+ if (!media_entity_enum_test_and_set(&iter->ent_enum, entity))
+ return entity;
+ }
+
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(__media_pipeline_entity_iter_next);
+
/* -----------------------------------------------------------------------------
* Links management
*/
diff --git a/drivers/media/pci/Kconfig b/drivers/media/pci/Kconfig
index dff0b450f387..480194543d05 100644
--- a/drivers/media/pci/Kconfig
+++ b/drivers/media/pci/Kconfig
@@ -27,6 +27,7 @@ if MEDIA_ANALOG_TV_SUPPORT
source "drivers/media/pci/dt3155/Kconfig"
source "drivers/media/pci/ivtv/Kconfig"
+source "drivers/media/pci/saa7146/Kconfig"
endif
@@ -57,6 +58,7 @@ source "drivers/media/pci/pluto2/Kconfig"
source "drivers/media/pci/pt1/Kconfig"
source "drivers/media/pci/pt3/Kconfig"
source "drivers/media/pci/smipcie/Kconfig"
+source "drivers/media/pci/ttpci/Kconfig"
endif
diff --git a/drivers/media/pci/Makefile b/drivers/media/pci/Makefile
index 8f887a8a7f17..8bed619b7130 100644
--- a/drivers/media/pci/Makefile
+++ b/drivers/media/pci/Makefile
@@ -5,7 +5,8 @@
# Please keep it alphabetically sorted by directory
# (e. g. LC_ALL=C sort Makefile)
-obj-y += b2c2/ \
+obj-y += ttpci/ \
+ b2c2/ \
pluto2/ \
dm1105/ \
pt1/ \
@@ -13,6 +14,7 @@ obj-y += b2c2/ \
mantis/ \
ngene/ \
ddbridge/ \
+ saa7146/ \
smipcie/ \
netup_unidvb/ \
intel/
diff --git a/drivers/media/pci/intel/ipu3/cio2-bridge.c b/drivers/media/pci/intel/ipu3/cio2-bridge.c
index df6c94da2f6a..dfefe0d8aa95 100644
--- a/drivers/media/pci/intel/ipu3/cio2-bridge.c
+++ b/drivers/media/pci/intel/ipu3/cio2-bridge.c
@@ -195,6 +195,19 @@ static void cio2_bridge_init_swnode_names(struct cio2_sensor *sensor)
SWNODE_GRAPH_ENDPOINT_NAME_FMT, 0); /* And endpoint 0 */
}
+static void cio2_bridge_init_swnode_group(struct cio2_sensor *sensor)
+{
+ struct software_node *nodes = sensor->swnodes;
+
+ sensor->group[SWNODE_SENSOR_HID] = &nodes[SWNODE_SENSOR_HID];
+ sensor->group[SWNODE_SENSOR_PORT] = &nodes[SWNODE_SENSOR_PORT];
+ sensor->group[SWNODE_SENSOR_ENDPOINT] = &nodes[SWNODE_SENSOR_ENDPOINT];
+ sensor->group[SWNODE_CIO2_PORT] = &nodes[SWNODE_CIO2_PORT];
+ sensor->group[SWNODE_CIO2_ENDPOINT] = &nodes[SWNODE_CIO2_ENDPOINT];
+ if (sensor->ssdb.vcmtype)
+ sensor->group[SWNODE_VCM] = &nodes[SWNODE_VCM];
+}
+
static void cio2_bridge_create_connection_swnodes(struct cio2_bridge *bridge,
struct cio2_sensor *sensor)
{
@@ -219,6 +232,8 @@ static void cio2_bridge_create_connection_swnodes(struct cio2_bridge *bridge,
if (sensor->ssdb.vcmtype)
nodes[SWNODE_VCM] =
NODE_VCM(cio2_vcm_types[sensor->ssdb.vcmtype - 1]);
+
+ cio2_bridge_init_swnode_group(sensor);
}
static void cio2_bridge_instantiate_vcm_i2c_client(struct cio2_sensor *sensor)
@@ -252,7 +267,7 @@ static void cio2_bridge_unregister_sensors(struct cio2_bridge *bridge)
for (i = 0; i < bridge->n_sensors; i++) {
sensor = &bridge->sensors[i];
- software_node_unregister_nodes(sensor->swnodes);
+ software_node_unregister_node_group(sensor->group);
ACPI_FREE(sensor->pld);
acpi_dev_put(sensor->adev);
i2c_unregister_device(sensor->vcm_i2c_client);
@@ -263,7 +278,7 @@ static int cio2_bridge_connect_sensor(const struct cio2_sensor_config *cfg,
struct cio2_bridge *bridge,
struct pci_dev *cio2)
{
- struct fwnode_handle *fwnode;
+ struct fwnode_handle *fwnode, *primary;
struct cio2_sensor *sensor;
struct acpi_device *adev;
acpi_status status;
@@ -310,7 +325,7 @@ static int cio2_bridge_connect_sensor(const struct cio2_sensor_config *cfg,
cio2_bridge_create_fwnode_properties(sensor, bridge, cfg);
cio2_bridge_create_connection_swnodes(bridge, sensor);
- ret = software_node_register_nodes(sensor->swnodes);
+ ret = software_node_register_node_group(sensor->group);
if (ret)
goto err_free_pld;
@@ -322,7 +337,9 @@ static int cio2_bridge_connect_sensor(const struct cio2_sensor_config *cfg,
}
sensor->adev = acpi_dev_get(adev);
- adev->fwnode.secondary = fwnode;
+
+ primary = acpi_fwnode_handle(adev);
+ primary->secondary = fwnode;
cio2_bridge_instantiate_vcm_i2c_client(sensor);
@@ -335,7 +352,7 @@ static int cio2_bridge_connect_sensor(const struct cio2_sensor_config *cfg,
return 0;
err_free_swnodes:
- software_node_unregister_nodes(sensor->swnodes);
+ software_node_unregister_node_group(sensor->group);
err_free_pld:
ACPI_FREE(sensor->pld);
err_put_adev:
diff --git a/drivers/media/pci/intel/ipu3/cio2-bridge.h b/drivers/media/pci/intel/ipu3/cio2-bridge.h
index 4418cbd08208..b93b749c65bd 100644
--- a/drivers/media/pci/intel/ipu3/cio2-bridge.h
+++ b/drivers/media/pci/intel/ipu3/cio2-bridge.h
@@ -117,8 +117,9 @@ struct cio2_sensor {
struct acpi_device *adev;
struct i2c_client *vcm_i2c_client;
- /* SWNODE_COUNT + 1 for terminating empty node */
- struct software_node swnodes[SWNODE_COUNT + 1];
+ /* SWNODE_COUNT + 1 for terminating NULL */
+ const struct software_node *group[SWNODE_COUNT + 1];
+ struct software_node swnodes[SWNODE_COUNT];
struct cio2_node_names node_names;
struct cio2_sensor_ssdb ssdb;
diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c b/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c
index 390bd5ea3472..3b76a9d0383a 100644
--- a/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c
+++ b/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c
@@ -1843,6 +1843,9 @@ static void cio2_pci_remove(struct pci_dev *pci_dev)
v4l2_device_unregister(&cio2->v4l2_dev);
media_device_cleanup(&cio2->media_dev);
mutex_destroy(&cio2->lock);
+
+ pm_runtime_forbid(&pci_dev->dev);
+ pm_runtime_get_noresume(&pci_dev->dev);
}
static int __maybe_unused cio2_runtime_suspend(struct device *dev)
diff --git a/drivers/media/pci/saa7134/saa7134-core.c b/drivers/media/pci/saa7134/saa7134-core.c
index 96328b0af164..cf2871306987 100644
--- a/drivers/media/pci/saa7134/saa7134-core.c
+++ b/drivers/media/pci/saa7134/saa7134-core.c
@@ -978,7 +978,7 @@ static void saa7134_unregister_video(struct saa7134_dev *dev)
}
if (dev->radio_dev) {
if (video_is_registered(dev->radio_dev))
- vb2_video_unregister_device(dev->radio_dev);
+ video_unregister_device(dev->radio_dev);
else
video_device_release(dev->radio_dev);
dev->radio_dev = NULL;
diff --git a/drivers/media/pci/saa7146/Kconfig b/drivers/media/pci/saa7146/Kconfig
new file mode 100644
index 000000000000..3bbb68a0ed7b
--- /dev/null
+++ b/drivers/media/pci/saa7146/Kconfig
@@ -0,0 +1,39 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config VIDEO_HEXIUM_GEMINI
+ tristate "Hexium Gemini frame grabber"
+ depends on PCI && VIDEO_DEV && I2C
+ select VIDEO_SAA7146_VV
+ help
+ This is a video4linux driver for the Hexium Gemini frame
+ grabber card by Hexium. Please note that the Gemini Dual
+ card is *not* fully supported.
+
+ To compile this driver as a module, choose M here: the
+ module will be called hexium_gemini.
+
+config VIDEO_HEXIUM_ORION
+ tristate "Hexium HV-PCI6 and Orion frame grabber"
+ depends on PCI && VIDEO_DEV && I2C
+ select VIDEO_SAA7146_VV
+ help
+ This is a video4linux driver for the Hexium HV-PCI6 and
+ Orion frame grabber cards by Hexium.
+
+ To compile this driver as a module, choose M here: the
+ module will be called hexium_orion.
+
+config VIDEO_MXB
+ tristate "Siemens-Nixdorf 'Multimedia eXtension Board'"
+ depends on PCI && VIDEO_DEV && I2C
+ select VIDEO_SAA7146_VV
+ select VIDEO_TUNER
+ select VIDEO_SAA711X if MEDIA_SUBDRV_AUTOSELECT
+ select VIDEO_TDA9840 if MEDIA_SUBDRV_AUTOSELECT
+ select VIDEO_TEA6415C if MEDIA_SUBDRV_AUTOSELECT
+ select VIDEO_TEA6420 if MEDIA_SUBDRV_AUTOSELECT
+ help
+ This is a video4linux driver for the 'Multimedia eXtension Board'
+ TV card by Siemens-Nixdorf.
+
+ To compile this driver as a module, choose M here: the
+ module will be called mxb.
diff --git a/drivers/media/pci/saa7146/Makefile b/drivers/media/pci/saa7146/Makefile
new file mode 100644
index 000000000000..37c9336f83d5
--- /dev/null
+++ b/drivers/media/pci/saa7146/Makefile
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_VIDEO_MXB) += mxb.o
+obj-$(CONFIG_VIDEO_HEXIUM_ORION) += hexium_orion.o
+obj-$(CONFIG_VIDEO_HEXIUM_GEMINI) += hexium_gemini.o
+
+ccflags-y += -I$(srctree)/drivers/media/i2c
diff --git a/drivers/media/pci/saa7146/hexium_gemini.c b/drivers/media/pci/saa7146/hexium_gemini.c
new file mode 100644
index 000000000000..3947701cd6c7
--- /dev/null
+++ b/drivers/media/pci/saa7146/hexium_gemini.c
@@ -0,0 +1,425 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ hexium_gemini.c - v4l2 driver for Hexium Gemini frame grabber cards
+
+ Visit http://www.mihu.de/linux/saa7146/ and follow the link
+ to "hexium" for further details about this card.
+
+ Copyright (C) 2003 Michael Hunold <michael@mihu.de>
+
+*/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define DEBUG_VARIABLE debug
+
+#include <media/drv-intf/saa7146_vv.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+
+static int debug;
+module_param(debug, int, 0);
+MODULE_PARM_DESC(debug, "debug verbosity");
+
+/* global variables */
+static int hexium_num;
+
+#define HEXIUM_GEMINI 4
+#define HEXIUM_GEMINI_DUAL 5
+
+#define HEXIUM_INPUTS 9
+static struct v4l2_input hexium_inputs[HEXIUM_INPUTS] = {
+ { 0, "CVBS 1", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD },
+ { 1, "CVBS 2", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD },
+ { 2, "CVBS 3", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD },
+ { 3, "CVBS 4", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD },
+ { 4, "CVBS 5", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD },
+ { 5, "CVBS 6", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD },
+ { 6, "Y/C 1", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD },
+ { 7, "Y/C 2", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD },
+ { 8, "Y/C 3", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD },
+};
+
+#define HEXIUM_AUDIOS 0
+
+struct hexium_data
+{
+ s8 adr;
+ u8 byte;
+};
+
+#define HEXIUM_GEMINI_V_1_0 1
+#define HEXIUM_GEMINI_DUAL_V_1_0 2
+
+struct hexium
+{
+ int type;
+
+ struct video_device video_dev;
+ struct i2c_adapter i2c_adapter;
+
+ int cur_input; /* current input */
+ v4l2_std_id cur_std; /* current standard */
+};
+
+/* Samsung KS0127B decoder default registers */
+static u8 hexium_ks0127b[0x100]={
+/*00*/ 0x00,0x52,0x30,0x40,0x01,0x0C,0x2A,0x10,
+/*08*/ 0x00,0x00,0x00,0x60,0x00,0x00,0x0F,0x06,
+/*10*/ 0x00,0x00,0xE4,0xC0,0x00,0x00,0x00,0x00,
+/*18*/ 0x14,0x9B,0xFE,0xFF,0xFC,0xFF,0x03,0x22,
+/*20*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*28*/ 0x00,0x00,0x00,0x00,0x00,0x2C,0x9B,0x00,
+/*30*/ 0x00,0x00,0x10,0x80,0x80,0x10,0x80,0x80,
+/*38*/ 0x01,0x04,0x00,0x00,0x00,0x29,0xC0,0x00,
+/*40*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*48*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*50*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*58*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*60*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*68*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*70*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*78*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*80*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*88*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*90*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*98*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*A0*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*A8*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*B0*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*B8*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*C0*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*C8*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*D0*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*D8*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*E0*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*E8*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*F0*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*F8*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
+};
+
+static struct hexium_data hexium_pal[] = {
+ { 0x01, 0x52 }, { 0x12, 0x64 }, { 0x2D, 0x2C }, { 0x2E, 0x9B }, { -1 , 0xFF }
+};
+
+static struct hexium_data hexium_ntsc[] = {
+ { 0x01, 0x53 }, { 0x12, 0x04 }, { 0x2D, 0x23 }, { 0x2E, 0x81 }, { -1 , 0xFF }
+};
+
+static struct hexium_data hexium_secam[] = {
+ { 0x01, 0x52 }, { 0x12, 0x64 }, { 0x2D, 0x2C }, { 0x2E, 0x9B }, { -1 , 0xFF }
+};
+
+static struct hexium_data hexium_input_select[] = {
+ { 0x02, 0x60 },
+ { 0x02, 0x64 },
+ { 0x02, 0x61 },
+ { 0x02, 0x65 },
+ { 0x02, 0x62 },
+ { 0x02, 0x66 },
+ { 0x02, 0x68 },
+ { 0x02, 0x69 },
+ { 0x02, 0x6A },
+};
+
+/* fixme: h_offset = 0 for Hexium Gemini *Dual*, which
+ are currently *not* supported*/
+static struct saa7146_standard hexium_standards[] = {
+ {
+ .name = "PAL", .id = V4L2_STD_PAL,
+ .v_offset = 28, .v_field = 288,
+ .h_offset = 1, .h_pixels = 680,
+ .v_max_out = 576, .h_max_out = 768,
+ }, {
+ .name = "NTSC", .id = V4L2_STD_NTSC,
+ .v_offset = 28, .v_field = 240,
+ .h_offset = 1, .h_pixels = 640,
+ .v_max_out = 480, .h_max_out = 640,
+ }, {
+ .name = "SECAM", .id = V4L2_STD_SECAM,
+ .v_offset = 28, .v_field = 288,
+ .h_offset = 1, .h_pixels = 720,
+ .v_max_out = 576, .h_max_out = 768,
+ }
+};
+
+/* bring hardware to a sane state. this has to be done, just in case someone
+ wants to capture from this device before it has been properly initialized.
+ the capture engine would badly fail, because no valid signal arrives on the
+ saa7146, thus leading to timeouts and stuff. */
+static int hexium_init_done(struct saa7146_dev *dev)
+{
+ struct hexium *hexium = (struct hexium *) dev->ext_priv;
+ union i2c_smbus_data data;
+ int i = 0;
+
+ DEB_D("hexium_init_done called\n");
+
+ /* initialize the helper ics to useful values */
+ for (i = 0; i < sizeof(hexium_ks0127b); i++) {
+ data.byte = hexium_ks0127b[i];
+ if (0 != i2c_smbus_xfer(&hexium->i2c_adapter, 0x6c, 0, I2C_SMBUS_WRITE, i, I2C_SMBUS_BYTE_DATA, &data)) {
+ pr_err("hexium_init_done() failed for address 0x%02x\n",
+ i);
+ }
+ }
+
+ return 0;
+}
+
+static int hexium_set_input(struct hexium *hexium, int input)
+{
+ union i2c_smbus_data data;
+
+ DEB_D("\n");
+
+ data.byte = hexium_input_select[input].byte;
+ if (0 != i2c_smbus_xfer(&hexium->i2c_adapter, 0x6c, 0, I2C_SMBUS_WRITE, hexium_input_select[input].adr, I2C_SMBUS_BYTE_DATA, &data)) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static int hexium_set_standard(struct hexium *hexium, struct hexium_data *vdec)
+{
+ union i2c_smbus_data data;
+ int i = 0;
+
+ DEB_D("\n");
+
+ while (vdec[i].adr != -1) {
+ data.byte = vdec[i].byte;
+ if (0 != i2c_smbus_xfer(&hexium->i2c_adapter, 0x6c, 0, I2C_SMBUS_WRITE, vdec[i].adr, I2C_SMBUS_BYTE_DATA, &data)) {
+ pr_err("hexium_init_done: hexium_set_standard() failed for address 0x%02x\n",
+ i);
+ return -1;
+ }
+ i++;
+ }
+ return 0;
+}
+
+static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i)
+{
+ DEB_EE("VIDIOC_ENUMINPUT %d\n", i->index);
+
+ if (i->index >= HEXIUM_INPUTS)
+ return -EINVAL;
+
+ memcpy(i, &hexium_inputs[i->index], sizeof(struct v4l2_input));
+
+ DEB_D("v4l2_ioctl: VIDIOC_ENUMINPUT %d\n", i->index);
+ return 0;
+}
+
+static int vidioc_g_input(struct file *file, void *fh, unsigned int *input)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct hexium *hexium = (struct hexium *) dev->ext_priv;
+
+ *input = hexium->cur_input;
+
+ DEB_D("VIDIOC_G_INPUT: %d\n", *input);
+ return 0;
+}
+
+static int vidioc_s_input(struct file *file, void *fh, unsigned int input)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct hexium *hexium = (struct hexium *) dev->ext_priv;
+
+ DEB_EE("VIDIOC_S_INPUT %d\n", input);
+
+ if (input >= HEXIUM_INPUTS)
+ return -EINVAL;
+
+ hexium->cur_input = input;
+ hexium_set_input(hexium, input);
+ return 0;
+}
+
+static struct saa7146_ext_vv vv_data;
+
+/* this function only gets called when the probing was successful */
+static int hexium_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data *info)
+{
+ struct hexium *hexium;
+ int ret;
+
+ DEB_EE("\n");
+
+ hexium = kzalloc(sizeof(*hexium), GFP_KERNEL);
+ if (!hexium)
+ return -ENOMEM;
+
+ dev->ext_priv = hexium;
+
+ /* enable i2c-port pins */
+ saa7146_write(dev, MC1, (MASK_08 | MASK_24 | MASK_10 | MASK_26));
+
+ strscpy(hexium->i2c_adapter.name, "hexium gemini",
+ sizeof(hexium->i2c_adapter.name));
+ saa7146_i2c_adapter_prepare(dev, &hexium->i2c_adapter, SAA7146_I2C_BUS_BIT_RATE_480);
+ if (i2c_add_adapter(&hexium->i2c_adapter) < 0) {
+ DEB_S("cannot register i2c-device. skipping.\n");
+ kfree(hexium);
+ return -EFAULT;
+ }
+
+ /* set HWControl GPIO number 2 */
+ saa7146_setgpio(dev, 2, SAA7146_GPIO_OUTHI);
+
+ saa7146_write(dev, DD1_INIT, 0x07000700);
+ saa7146_write(dev, DD1_STREAM_B, 0x00000000);
+ saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26));
+
+ /* the rest */
+ hexium->cur_input = 0;
+ hexium_init_done(dev);
+
+ hexium_set_standard(hexium, hexium_pal);
+ hexium->cur_std = V4L2_STD_PAL;
+
+ hexium_set_input(hexium, 0);
+ hexium->cur_input = 0;
+
+ ret = saa7146_vv_init(dev, &vv_data);
+ if (ret) {
+ i2c_del_adapter(&hexium->i2c_adapter);
+ kfree(hexium);
+ return ret;
+ }
+
+ vv_data.vid_ops.vidioc_enum_input = vidioc_enum_input;
+ vv_data.vid_ops.vidioc_g_input = vidioc_g_input;
+ vv_data.vid_ops.vidioc_s_input = vidioc_s_input;
+ ret = saa7146_register_device(&hexium->video_dev, dev, "hexium gemini", VFL_TYPE_VIDEO);
+ if (ret < 0) {
+ pr_err("cannot register capture v4l2 device. skipping.\n");
+ saa7146_vv_release(dev);
+ i2c_del_adapter(&hexium->i2c_adapter);
+ kfree(hexium);
+ return ret;
+ }
+
+ pr_info("found 'hexium gemini' frame grabber-%d\n", hexium_num);
+ hexium_num++;
+
+ return 0;
+}
+
+static int hexium_detach(struct saa7146_dev *dev)
+{
+ struct hexium *hexium = (struct hexium *) dev->ext_priv;
+
+ DEB_EE("dev:%p\n", dev);
+
+ saa7146_unregister_device(&hexium->video_dev, dev);
+ saa7146_vv_release(dev);
+
+ hexium_num--;
+
+ i2c_del_adapter(&hexium->i2c_adapter);
+ kfree(hexium);
+ return 0;
+}
+
+static int std_callback(struct saa7146_dev *dev, struct saa7146_standard *std)
+{
+ struct hexium *hexium = (struct hexium *) dev->ext_priv;
+
+ if (V4L2_STD_PAL == std->id) {
+ hexium_set_standard(hexium, hexium_pal);
+ hexium->cur_std = V4L2_STD_PAL;
+ return 0;
+ } else if (V4L2_STD_NTSC == std->id) {
+ hexium_set_standard(hexium, hexium_ntsc);
+ hexium->cur_std = V4L2_STD_NTSC;
+ return 0;
+ } else if (V4L2_STD_SECAM == std->id) {
+ hexium_set_standard(hexium, hexium_secam);
+ hexium->cur_std = V4L2_STD_SECAM;
+ return 0;
+ }
+
+ return -1;
+}
+
+static struct saa7146_extension hexium_extension;
+
+static struct saa7146_pci_extension_data hexium_gemini_4bnc = {
+ .ext_priv = "Hexium Gemini (4 BNC)",
+ .ext = &hexium_extension,
+};
+
+static struct saa7146_pci_extension_data hexium_gemini_dual_4bnc = {
+ .ext_priv = "Hexium Gemini Dual (4 BNC)",
+ .ext = &hexium_extension,
+};
+
+static const struct pci_device_id pci_tbl[] = {
+ {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7146,
+ .subvendor = 0x17c8,
+ .subdevice = 0x2401,
+ .driver_data = (unsigned long) &hexium_gemini_4bnc,
+ },
+ {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7146,
+ .subvendor = 0x17c8,
+ .subdevice = 0x2402,
+ .driver_data = (unsigned long) &hexium_gemini_dual_4bnc,
+ },
+ {
+ .vendor = 0,
+ }
+};
+
+MODULE_DEVICE_TABLE(pci, pci_tbl);
+
+static struct saa7146_ext_vv vv_data = {
+ .inputs = HEXIUM_INPUTS,
+ .capabilities = 0,
+ .stds = &hexium_standards[0],
+ .num_stds = ARRAY_SIZE(hexium_standards),
+ .std_callback = &std_callback,
+};
+
+static struct saa7146_extension hexium_extension = {
+ .name = "hexium gemini",
+ .flags = SAA7146_USE_I2C_IRQ,
+
+ .pci_tbl = &pci_tbl[0],
+ .module = THIS_MODULE,
+
+ .attach = hexium_attach,
+ .detach = hexium_detach,
+
+ .irq_mask = 0,
+ .irq_func = NULL,
+};
+
+static int __init hexium_init_module(void)
+{
+ if (0 != saa7146_register_extension(&hexium_extension)) {
+ DEB_S("failed to register extension\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static void __exit hexium_cleanup_module(void)
+{
+ saa7146_unregister_extension(&hexium_extension);
+}
+
+module_init(hexium_init_module);
+module_exit(hexium_cleanup_module);
+
+MODULE_DESCRIPTION("video4linux-2 driver for Hexium Gemini frame grabber cards");
+MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/pci/saa7146/hexium_orion.c b/drivers/media/pci/saa7146/hexium_orion.c
new file mode 100644
index 000000000000..2eb4bee16b71
--- /dev/null
+++ b/drivers/media/pci/saa7146/hexium_orion.c
@@ -0,0 +1,496 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ hexium_orion.c - v4l2 driver for the Hexium Orion frame grabber cards
+
+ Visit http://www.mihu.de/linux/saa7146/ and follow the link
+ to "hexium" for further details about this card.
+
+ Copyright (C) 2003 Michael Hunold <michael@mihu.de>
+
+*/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define DEBUG_VARIABLE debug
+
+#include <media/drv-intf/saa7146_vv.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+
+static int debug;
+module_param(debug, int, 0);
+MODULE_PARM_DESC(debug, "debug verbosity");
+
+/* global variables */
+static int hexium_num;
+
+#define HEXIUM_HV_PCI6_ORION 1
+#define HEXIUM_ORION_1SVHS_3BNC 2
+#define HEXIUM_ORION_4BNC 3
+
+#define HEXIUM_INPUTS 9
+static struct v4l2_input hexium_inputs[HEXIUM_INPUTS] = {
+ { 0, "CVBS 1", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD },
+ { 1, "CVBS 2", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD },
+ { 2, "CVBS 3", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD },
+ { 3, "CVBS 4", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD },
+ { 4, "CVBS 5", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD },
+ { 5, "CVBS 6", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD },
+ { 6, "Y/C 1", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD },
+ { 7, "Y/C 2", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD },
+ { 8, "Y/C 3", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD },
+};
+
+#define HEXIUM_AUDIOS 0
+
+struct hexium_data
+{
+ s8 adr;
+ u8 byte;
+};
+
+struct hexium
+{
+ int type;
+ struct video_device video_dev;
+ struct i2c_adapter i2c_adapter;
+
+ int cur_input; /* current input */
+};
+
+/* Philips SAA7110 decoder default registers */
+static u8 hexium_saa7110[53]={
+/*00*/ 0x4C,0x3C,0x0D,0xEF,0xBD,0xF0,0x00,0x00,
+/*08*/ 0xF8,0xF8,0x60,0x60,0x40,0x86,0x18,0x90,
+/*10*/ 0x00,0x2C,0x40,0x46,0x42,0x1A,0xFF,0xDA,
+/*18*/ 0xF0,0x8B,0x00,0x00,0x00,0x00,0x00,0x00,
+/*20*/ 0xD9,0x17,0x40,0x41,0x80,0x41,0x80,0x4F,
+/*28*/ 0xFE,0x01,0x0F,0x0F,0x03,0x01,0x81,0x03,
+/*30*/ 0x44,0x75,0x01,0x8C,0x03
+};
+
+static struct {
+ struct hexium_data data[8];
+} hexium_input_select[] = {
+{
+ { /* cvbs 1 */
+ { 0x06, 0x00 },
+ { 0x20, 0xD9 },
+ { 0x21, 0x17 }, // 0x16,
+ { 0x22, 0x40 },
+ { 0x2C, 0x03 },
+ { 0x30, 0x44 },
+ { 0x31, 0x75 }, // ??
+ { 0x21, 0x16 }, // 0x03,
+ }
+}, {
+ { /* cvbs 2 */
+ { 0x06, 0x00 },
+ { 0x20, 0x78 },
+ { 0x21, 0x07 }, // 0x03,
+ { 0x22, 0xD2 },
+ { 0x2C, 0x83 },
+ { 0x30, 0x60 },
+ { 0x31, 0xB5 }, // ?
+ { 0x21, 0x03 },
+ }
+}, {
+ { /* cvbs 3 */
+ { 0x06, 0x00 },
+ { 0x20, 0xBA },
+ { 0x21, 0x07 }, // 0x05,
+ { 0x22, 0x91 },
+ { 0x2C, 0x03 },
+ { 0x30, 0x60 },
+ { 0x31, 0xB5 }, // ??
+ { 0x21, 0x05 }, // 0x03,
+ }
+}, {
+ { /* cvbs 4 */
+ { 0x06, 0x00 },
+ { 0x20, 0xD8 },
+ { 0x21, 0x17 }, // 0x16,
+ { 0x22, 0x40 },
+ { 0x2C, 0x03 },
+ { 0x30, 0x44 },
+ { 0x31, 0x75 }, // ??
+ { 0x21, 0x16 }, // 0x03,
+ }
+}, {
+ { /* cvbs 5 */
+ { 0x06, 0x00 },
+ { 0x20, 0xB8 },
+ { 0x21, 0x07 }, // 0x05,
+ { 0x22, 0x91 },
+ { 0x2C, 0x03 },
+ { 0x30, 0x60 },
+ { 0x31, 0xB5 }, // ??
+ { 0x21, 0x05 }, // 0x03,
+ }
+}, {
+ { /* cvbs 6 */
+ { 0x06, 0x00 },
+ { 0x20, 0x7C },
+ { 0x21, 0x07 }, // 0x03
+ { 0x22, 0xD2 },
+ { 0x2C, 0x83 },
+ { 0x30, 0x60 },
+ { 0x31, 0xB5 }, // ??
+ { 0x21, 0x03 },
+ }
+}, {
+ { /* y/c 1 */
+ { 0x06, 0x80 },
+ { 0x20, 0x59 },
+ { 0x21, 0x17 },
+ { 0x22, 0x42 },
+ { 0x2C, 0xA3 },
+ { 0x30, 0x44 },
+ { 0x31, 0x75 },
+ { 0x21, 0x12 },
+ }
+}, {
+ { /* y/c 2 */
+ { 0x06, 0x80 },
+ { 0x20, 0x9A },
+ { 0x21, 0x17 },
+ { 0x22, 0xB1 },
+ { 0x2C, 0x13 },
+ { 0x30, 0x60 },
+ { 0x31, 0xB5 },
+ { 0x21, 0x14 },
+ }
+}, {
+ { /* y/c 3 */
+ { 0x06, 0x80 },
+ { 0x20, 0x3C },
+ { 0x21, 0x27 },
+ { 0x22, 0xC1 },
+ { 0x2C, 0x23 },
+ { 0x30, 0x44 },
+ { 0x31, 0x75 },
+ { 0x21, 0x21 },
+ }
+}
+};
+
+static struct saa7146_standard hexium_standards[] = {
+ {
+ .name = "PAL", .id = V4L2_STD_PAL,
+ .v_offset = 16, .v_field = 288,
+ .h_offset = 1, .h_pixels = 680,
+ .v_max_out = 576, .h_max_out = 768,
+ }, {
+ .name = "NTSC", .id = V4L2_STD_NTSC,
+ .v_offset = 16, .v_field = 240,
+ .h_offset = 1, .h_pixels = 640,
+ .v_max_out = 480, .h_max_out = 640,
+ }, {
+ .name = "SECAM", .id = V4L2_STD_SECAM,
+ .v_offset = 16, .v_field = 288,
+ .h_offset = 1, .h_pixels = 720,
+ .v_max_out = 576, .h_max_out = 768,
+ }
+};
+
+/* this is only called for old HV-PCI6/Orion cards
+ without eeprom */
+static int hexium_probe(struct saa7146_dev *dev)
+{
+ struct hexium *hexium = NULL;
+ union i2c_smbus_data data;
+ int err = 0;
+
+ DEB_EE("\n");
+
+ /* there are no hexium orion cards with revision 0 saa7146s */
+ if (0 == dev->revision) {
+ return -EFAULT;
+ }
+
+ hexium = kzalloc(sizeof(*hexium), GFP_KERNEL);
+ if (!hexium)
+ return -ENOMEM;
+
+ /* enable i2c-port pins */
+ saa7146_write(dev, MC1, (MASK_08 | MASK_24 | MASK_10 | MASK_26));
+
+ saa7146_write(dev, DD1_INIT, 0x01000100);
+ saa7146_write(dev, DD1_STREAM_B, 0x00000000);
+ saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26));
+
+ strscpy(hexium->i2c_adapter.name, "hexium orion",
+ sizeof(hexium->i2c_adapter.name));
+ saa7146_i2c_adapter_prepare(dev, &hexium->i2c_adapter, SAA7146_I2C_BUS_BIT_RATE_480);
+ if (i2c_add_adapter(&hexium->i2c_adapter) < 0) {
+ DEB_S("cannot register i2c-device. skipping.\n");
+ kfree(hexium);
+ return -EFAULT;
+ }
+
+ /* set SAA7110 control GPIO 0 */
+ saa7146_setgpio(dev, 0, SAA7146_GPIO_OUTHI);
+ /* set HWControl GPIO number 2 */
+ saa7146_setgpio(dev, 2, SAA7146_GPIO_OUTHI);
+
+ mdelay(10);
+
+ /* detect newer Hexium Orion cards by subsystem ids */
+ if (0x17c8 == dev->pci->subsystem_vendor && 0x0101 == dev->pci->subsystem_device) {
+ pr_info("device is a Hexium Orion w/ 1 SVHS + 3 BNC inputs\n");
+ /* we store the pointer in our private data field */
+ dev->ext_priv = hexium;
+ hexium->type = HEXIUM_ORION_1SVHS_3BNC;
+ return 0;
+ }
+
+ if (0x17c8 == dev->pci->subsystem_vendor && 0x2101 == dev->pci->subsystem_device) {
+ pr_info("device is a Hexium Orion w/ 4 BNC inputs\n");
+ /* we store the pointer in our private data field */
+ dev->ext_priv = hexium;
+ hexium->type = HEXIUM_ORION_4BNC;
+ return 0;
+ }
+
+ /* check if this is an old hexium Orion card by looking at
+ a saa7110 at address 0x4e */
+ err = i2c_smbus_xfer(&hexium->i2c_adapter, 0x4e, 0, I2C_SMBUS_READ,
+ 0x00, I2C_SMBUS_BYTE_DATA, &data);
+ if (err == 0) {
+ pr_info("device is a Hexium HV-PCI6/Orion (old)\n");
+ /* we store the pointer in our private data field */
+ dev->ext_priv = hexium;
+ hexium->type = HEXIUM_HV_PCI6_ORION;
+ return 0;
+ }
+
+ i2c_del_adapter(&hexium->i2c_adapter);
+ kfree(hexium);
+ return -EFAULT;
+}
+
+/* bring hardware to a sane state. this has to be done, just in case someone
+ wants to capture from this device before it has been properly initialized.
+ the capture engine would badly fail, because no valid signal arrives on the
+ saa7146, thus leading to timeouts and stuff. */
+static int hexium_init_done(struct saa7146_dev *dev)
+{
+ struct hexium *hexium = (struct hexium *) dev->ext_priv;
+ union i2c_smbus_data data;
+ int i = 0;
+
+ DEB_D("hexium_init_done called\n");
+
+ /* initialize the helper ics to useful values */
+ for (i = 0; i < sizeof(hexium_saa7110); i++) {
+ data.byte = hexium_saa7110[i];
+ if (0 != i2c_smbus_xfer(&hexium->i2c_adapter, 0x4e, 0, I2C_SMBUS_WRITE, i, I2C_SMBUS_BYTE_DATA, &data)) {
+ pr_err("failed for address 0x%02x\n", i);
+ }
+ }
+
+ return 0;
+}
+
+static int hexium_set_input(struct hexium *hexium, int input)
+{
+ union i2c_smbus_data data;
+ int i = 0;
+
+ DEB_D("\n");
+
+ for (i = 0; i < 8; i++) {
+ int adr = hexium_input_select[input].data[i].adr;
+ data.byte = hexium_input_select[input].data[i].byte;
+ if (0 != i2c_smbus_xfer(&hexium->i2c_adapter, 0x4e, 0, I2C_SMBUS_WRITE, adr, I2C_SMBUS_BYTE_DATA, &data)) {
+ return -1;
+ }
+ pr_debug("%d: 0x%02x => 0x%02x\n", input, adr, data.byte);
+ }
+
+ return 0;
+}
+
+static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i)
+{
+ DEB_EE("VIDIOC_ENUMINPUT %d\n", i->index);
+
+ if (i->index >= HEXIUM_INPUTS)
+ return -EINVAL;
+
+ memcpy(i, &hexium_inputs[i->index], sizeof(struct v4l2_input));
+
+ DEB_D("v4l2_ioctl: VIDIOC_ENUMINPUT %d\n", i->index);
+ return 0;
+}
+
+static int vidioc_g_input(struct file *file, void *fh, unsigned int *input)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct hexium *hexium = (struct hexium *) dev->ext_priv;
+
+ *input = hexium->cur_input;
+
+ DEB_D("VIDIOC_G_INPUT: %d\n", *input);
+ return 0;
+}
+
+static int vidioc_s_input(struct file *file, void *fh, unsigned int input)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct hexium *hexium = (struct hexium *) dev->ext_priv;
+
+ if (input >= HEXIUM_INPUTS)
+ return -EINVAL;
+
+ hexium->cur_input = input;
+ hexium_set_input(hexium, input);
+
+ return 0;
+}
+
+static struct saa7146_ext_vv vv_data;
+
+/* this function only gets called when the probing was successful */
+static int hexium_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data *info)
+{
+ struct hexium *hexium = (struct hexium *) dev->ext_priv;
+ int ret;
+
+ DEB_EE("\n");
+
+ ret = saa7146_vv_init(dev, &vv_data);
+ if (ret) {
+ pr_err("Error in saa7146_vv_init()\n");
+ return ret;
+ }
+
+ vv_data.vid_ops.vidioc_enum_input = vidioc_enum_input;
+ vv_data.vid_ops.vidioc_g_input = vidioc_g_input;
+ vv_data.vid_ops.vidioc_s_input = vidioc_s_input;
+ if (0 != saa7146_register_device(&hexium->video_dev, dev, "hexium orion", VFL_TYPE_VIDEO)) {
+ pr_err("cannot register capture v4l2 device. skipping.\n");
+ return -1;
+ }
+
+ pr_err("found 'hexium orion' frame grabber-%d\n", hexium_num);
+ hexium_num++;
+
+ /* the rest */
+ hexium->cur_input = 0;
+ hexium_init_done(dev);
+
+ return 0;
+}
+
+static int hexium_detach(struct saa7146_dev *dev)
+{
+ struct hexium *hexium = (struct hexium *) dev->ext_priv;
+
+ DEB_EE("dev:%p\n", dev);
+
+ saa7146_unregister_device(&hexium->video_dev, dev);
+ saa7146_vv_release(dev);
+
+ hexium_num--;
+
+ i2c_del_adapter(&hexium->i2c_adapter);
+ kfree(hexium);
+ return 0;
+}
+
+static int std_callback(struct saa7146_dev *dev, struct saa7146_standard *std)
+{
+ return 0;
+}
+
+static struct saa7146_extension extension;
+
+static struct saa7146_pci_extension_data hexium_hv_pci6 = {
+ .ext_priv = "Hexium HV-PCI6 / Orion",
+ .ext = &extension,
+};
+
+static struct saa7146_pci_extension_data hexium_orion_1svhs_3bnc = {
+ .ext_priv = "Hexium HV-PCI6 / Orion (1 SVHS/3 BNC)",
+ .ext = &extension,
+};
+
+static struct saa7146_pci_extension_data hexium_orion_4bnc = {
+ .ext_priv = "Hexium HV-PCI6 / Orion (4 BNC)",
+ .ext = &extension,
+};
+
+static const struct pci_device_id pci_tbl[] = {
+ {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7146,
+ .subvendor = 0x0000,
+ .subdevice = 0x0000,
+ .driver_data = (unsigned long) &hexium_hv_pci6,
+ },
+ {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7146,
+ .subvendor = 0x17c8,
+ .subdevice = 0x0101,
+ .driver_data = (unsigned long) &hexium_orion_1svhs_3bnc,
+ },
+ {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7146,
+ .subvendor = 0x17c8,
+ .subdevice = 0x2101,
+ .driver_data = (unsigned long) &hexium_orion_4bnc,
+ },
+ {
+ .vendor = 0,
+ }
+};
+
+MODULE_DEVICE_TABLE(pci, pci_tbl);
+
+static struct saa7146_ext_vv vv_data = {
+ .inputs = HEXIUM_INPUTS,
+ .capabilities = 0,
+ .stds = &hexium_standards[0],
+ .num_stds = ARRAY_SIZE(hexium_standards),
+ .std_callback = &std_callback,
+};
+
+static struct saa7146_extension extension = {
+ .name = "hexium HV-PCI6 Orion",
+ .flags = 0, // SAA7146_USE_I2C_IRQ,
+
+ .pci_tbl = &pci_tbl[0],
+ .module = THIS_MODULE,
+
+ .probe = hexium_probe,
+ .attach = hexium_attach,
+ .detach = hexium_detach,
+
+ .irq_mask = 0,
+ .irq_func = NULL,
+};
+
+static int __init hexium_init_module(void)
+{
+ if (0 != saa7146_register_extension(&extension)) {
+ DEB_S("failed to register extension\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static void __exit hexium_cleanup_module(void)
+{
+ saa7146_unregister_extension(&extension);
+}
+
+module_init(hexium_init_module);
+module_exit(hexium_cleanup_module);
+
+MODULE_DESCRIPTION("video4linux-2 driver for Hexium Orion frame grabber cards");
+MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/pci/saa7146/mxb.c b/drivers/media/pci/saa7146/mxb.c
new file mode 100644
index 000000000000..7ded8f5b05cb
--- /dev/null
+++ b/drivers/media/pci/saa7146/mxb.c
@@ -0,0 +1,873 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ mxb - v4l2 driver for the Multimedia eXtension Board
+
+ Copyright (C) 1998-2006 Michael Hunold <michael@mihu.de>
+
+ Visit http://www.themm.net/~mihu/linux/saa7146/mxb.html
+ for further details about this card.
+
+*/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define DEBUG_VARIABLE debug
+
+#include <media/drv-intf/saa7146_vv.h>
+#include <media/tuner.h>
+#include <media/v4l2-common.h>
+#include <media/i2c/saa7115.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+
+#include "tea6415c.h"
+#include "tea6420.h"
+
+#define MXB_AUDIOS 6
+
+#define I2C_SAA7111A 0x24
+#define I2C_TDA9840 0x42
+#define I2C_TEA6415C 0x43
+#define I2C_TEA6420_1 0x4c
+#define I2C_TEA6420_2 0x4d
+#define I2C_TUNER 0x60
+
+#define MXB_BOARD_CAN_DO_VBI(dev) (dev->revision != 0)
+
+/* global variable */
+static int mxb_num;
+
+/* initial frequence the tuner will be tuned to.
+ in verden (lower saxony, germany) 4148 is a
+ channel called "phoenix" */
+static int freq = 4148;
+module_param(freq, int, 0644);
+MODULE_PARM_DESC(freq, "initial frequency the tuner will be tuned to while setup");
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off device debugging (default:off).");
+
+#define MXB_INPUTS 4
+enum { TUNER, AUX1, AUX3, AUX3_YC };
+
+static struct v4l2_input mxb_inputs[MXB_INPUTS] = {
+ { TUNER, "Tuner", V4L2_INPUT_TYPE_TUNER, 0x3f, 0,
+ V4L2_STD_PAL_BG | V4L2_STD_PAL_I, 0, V4L2_IN_CAP_STD },
+ { AUX1, "AUX1", V4L2_INPUT_TYPE_CAMERA, 0x3f, 0,
+ V4L2_STD_ALL, 0, V4L2_IN_CAP_STD },
+ { AUX3, "AUX3 Composite", V4L2_INPUT_TYPE_CAMERA, 0x3f, 0,
+ V4L2_STD_ALL, 0, V4L2_IN_CAP_STD },
+ { AUX3_YC, "AUX3 S-Video", V4L2_INPUT_TYPE_CAMERA, 0x3f, 0,
+ V4L2_STD_ALL, 0, V4L2_IN_CAP_STD },
+};
+
+/* this array holds the information, which port of the saa7146 each
+ input actually uses. the mxb uses port 0 for every input */
+static struct {
+ int hps_source;
+ int hps_sync;
+} input_port_selection[MXB_INPUTS] = {
+ { SAA7146_HPS_SOURCE_PORT_A, SAA7146_HPS_SYNC_PORT_A },
+ { SAA7146_HPS_SOURCE_PORT_A, SAA7146_HPS_SYNC_PORT_A },
+ { SAA7146_HPS_SOURCE_PORT_A, SAA7146_HPS_SYNC_PORT_A },
+ { SAA7146_HPS_SOURCE_PORT_A, SAA7146_HPS_SYNC_PORT_A },
+};
+
+/* this array holds the information of the audio source (mxb_audios),
+ which has to be switched corresponding to the video source (mxb_channels) */
+static int video_audio_connect[MXB_INPUTS] =
+ { 0, 1, 3, 3 };
+
+struct mxb_routing {
+ u32 input;
+ u32 output;
+};
+
+/* these are the available audio sources, which can switched
+ to the line- and cd-output individually */
+static struct v4l2_audio mxb_audios[MXB_AUDIOS] = {
+ {
+ .index = 0,
+ .name = "Tuner",
+ .capability = V4L2_AUDCAP_STEREO,
+ } , {
+ .index = 1,
+ .name = "AUX1",
+ .capability = V4L2_AUDCAP_STEREO,
+ } , {
+ .index = 2,
+ .name = "AUX2",
+ .capability = V4L2_AUDCAP_STEREO,
+ } , {
+ .index = 3,
+ .name = "AUX3",
+ .capability = V4L2_AUDCAP_STEREO,
+ } , {
+ .index = 4,
+ .name = "Radio (X9)",
+ .capability = V4L2_AUDCAP_STEREO,
+ } , {
+ .index = 5,
+ .name = "CD-ROM (X10)",
+ .capability = V4L2_AUDCAP_STEREO,
+ }
+};
+
+/* These are the necessary input-output-pins for bringing one audio source
+ (see above) to the CD-output. Note that gain is set to 0 in this table. */
+static struct mxb_routing TEA6420_cd[MXB_AUDIOS + 1][2] = {
+ { { 1, 1 }, { 1, 1 } }, /* Tuner */
+ { { 5, 1 }, { 6, 1 } }, /* AUX 1 */
+ { { 4, 1 }, { 6, 1 } }, /* AUX 2 */
+ { { 3, 1 }, { 6, 1 } }, /* AUX 3 */
+ { { 1, 1 }, { 3, 1 } }, /* Radio */
+ { { 1, 1 }, { 2, 1 } }, /* CD-Rom */
+ { { 6, 1 }, { 6, 1 } } /* Mute */
+};
+
+/* These are the necessary input-output-pins for bringing one audio source
+ (see above) to the line-output. Note that gain is set to 0 in this table. */
+static struct mxb_routing TEA6420_line[MXB_AUDIOS + 1][2] = {
+ { { 2, 3 }, { 1, 2 } },
+ { { 5, 3 }, { 6, 2 } },
+ { { 4, 3 }, { 6, 2 } },
+ { { 3, 3 }, { 6, 2 } },
+ { { 2, 3 }, { 3, 2 } },
+ { { 2, 3 }, { 2, 2 } },
+ { { 6, 3 }, { 6, 2 } } /* Mute */
+};
+
+struct mxb
+{
+ struct video_device video_dev;
+ struct video_device vbi_dev;
+
+ struct i2c_adapter i2c_adapter;
+
+ struct v4l2_subdev *saa7111a;
+ struct v4l2_subdev *tda9840;
+ struct v4l2_subdev *tea6415c;
+ struct v4l2_subdev *tuner;
+ struct v4l2_subdev *tea6420_1;
+ struct v4l2_subdev *tea6420_2;
+
+ int cur_mode; /* current audio mode (mono, stereo, ...) */
+ int cur_input; /* current input */
+ int cur_audinput; /* current audio input */
+ int cur_mute; /* current mute status */
+ struct v4l2_frequency cur_freq; /* current frequency the tuner is tuned to */
+};
+
+#define saa7111a_call(mxb, o, f, args...) \
+ v4l2_subdev_call(mxb->saa7111a, o, f, ##args)
+#define tda9840_call(mxb, o, f, args...) \
+ v4l2_subdev_call(mxb->tda9840, o, f, ##args)
+#define tea6415c_call(mxb, o, f, args...) \
+ v4l2_subdev_call(mxb->tea6415c, o, f, ##args)
+#define tuner_call(mxb, o, f, args...) \
+ v4l2_subdev_call(mxb->tuner, o, f, ##args)
+#define call_all(dev, o, f, args...) \
+ v4l2_device_call_until_err(&dev->v4l2_dev, 0, o, f, ##args)
+
+static void mxb_update_audmode(struct mxb *mxb)
+{
+ struct v4l2_tuner t = {
+ .audmode = mxb->cur_mode,
+ };
+
+ tda9840_call(mxb, tuner, s_tuner, &t);
+}
+
+static inline void tea6420_route(struct mxb *mxb, int idx)
+{
+ v4l2_subdev_call(mxb->tea6420_1, audio, s_routing,
+ TEA6420_cd[idx][0].input, TEA6420_cd[idx][0].output, 0);
+ v4l2_subdev_call(mxb->tea6420_2, audio, s_routing,
+ TEA6420_cd[idx][1].input, TEA6420_cd[idx][1].output, 0);
+ v4l2_subdev_call(mxb->tea6420_1, audio, s_routing,
+ TEA6420_line[idx][0].input, TEA6420_line[idx][0].output, 0);
+ v4l2_subdev_call(mxb->tea6420_2, audio, s_routing,
+ TEA6420_line[idx][1].input, TEA6420_line[idx][1].output, 0);
+}
+
+static struct saa7146_extension extension;
+
+static int mxb_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct saa7146_dev *dev = container_of(ctrl->handler,
+ struct saa7146_dev, ctrl_handler);
+ struct mxb *mxb = dev->ext_priv;
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ mxb->cur_mute = ctrl->val;
+ /* switch the audio-source */
+ tea6420_route(mxb, ctrl->val ? 6 :
+ video_audio_connect[mxb->cur_input]);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static const struct v4l2_ctrl_ops mxb_ctrl_ops = {
+ .s_ctrl = mxb_s_ctrl,
+};
+
+static int mxb_probe(struct saa7146_dev *dev)
+{
+ struct v4l2_ctrl_handler *hdl = &dev->ctrl_handler;
+ struct mxb *mxb = NULL;
+
+ v4l2_ctrl_new_std(hdl, &mxb_ctrl_ops,
+ V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1);
+ if (hdl->error)
+ return hdl->error;
+ mxb = kzalloc(sizeof(struct mxb), GFP_KERNEL);
+ if (mxb == NULL) {
+ DEB_D("not enough kernel memory\n");
+ return -ENOMEM;
+ }
+
+
+ snprintf(mxb->i2c_adapter.name, sizeof(mxb->i2c_adapter.name), "mxb%d", mxb_num);
+
+ saa7146_i2c_adapter_prepare(dev, &mxb->i2c_adapter, SAA7146_I2C_BUS_BIT_RATE_480);
+ if (i2c_add_adapter(&mxb->i2c_adapter) < 0) {
+ DEB_S("cannot register i2c-device. skipping.\n");
+ kfree(mxb);
+ return -EFAULT;
+ }
+
+ mxb->saa7111a = v4l2_i2c_new_subdev(&dev->v4l2_dev, &mxb->i2c_adapter,
+ "saa7111", I2C_SAA7111A, NULL);
+ mxb->tea6420_1 = v4l2_i2c_new_subdev(&dev->v4l2_dev, &mxb->i2c_adapter,
+ "tea6420", I2C_TEA6420_1, NULL);
+ mxb->tea6420_2 = v4l2_i2c_new_subdev(&dev->v4l2_dev, &mxb->i2c_adapter,
+ "tea6420", I2C_TEA6420_2, NULL);
+ mxb->tea6415c = v4l2_i2c_new_subdev(&dev->v4l2_dev, &mxb->i2c_adapter,
+ "tea6415c", I2C_TEA6415C, NULL);
+ mxb->tda9840 = v4l2_i2c_new_subdev(&dev->v4l2_dev, &mxb->i2c_adapter,
+ "tda9840", I2C_TDA9840, NULL);
+ mxb->tuner = v4l2_i2c_new_subdev(&dev->v4l2_dev, &mxb->i2c_adapter,
+ "tuner", I2C_TUNER, NULL);
+
+ /* check if all devices are present */
+ if (!mxb->tea6420_1 || !mxb->tea6420_2 || !mxb->tea6415c ||
+ !mxb->tda9840 || !mxb->saa7111a || !mxb->tuner) {
+ pr_err("did not find all i2c devices. aborting\n");
+ i2c_del_adapter(&mxb->i2c_adapter);
+ kfree(mxb);
+ return -ENODEV;
+ }
+
+ /* all devices are present, probe was successful */
+
+ /* we store the pointer in our private data field */
+ dev->ext_priv = mxb;
+
+ v4l2_ctrl_handler_setup(hdl);
+
+ return 0;
+}
+
+/* some init data for the saa7740, the so-called 'sound arena module'.
+ there are no specs available, so we simply use some init values */
+static struct {
+ int length;
+ char data[9];
+} mxb_saa7740_init[] = {
+ { 3, { 0x80, 0x00, 0x00 } },{ 3, { 0x80, 0x89, 0x00 } },
+ { 3, { 0x80, 0xb0, 0x0a } },{ 3, { 0x00, 0x00, 0x00 } },
+ { 3, { 0x49, 0x00, 0x00 } },{ 3, { 0x4a, 0x00, 0x00 } },
+ { 3, { 0x4b, 0x00, 0x00 } },{ 3, { 0x4c, 0x00, 0x00 } },
+ { 3, { 0x4d, 0x00, 0x00 } },{ 3, { 0x4e, 0x00, 0x00 } },
+ { 3, { 0x4f, 0x00, 0x00 } },{ 3, { 0x50, 0x00, 0x00 } },
+ { 3, { 0x51, 0x00, 0x00 } },{ 3, { 0x52, 0x00, 0x00 } },
+ { 3, { 0x53, 0x00, 0x00 } },{ 3, { 0x54, 0x00, 0x00 } },
+ { 3, { 0x55, 0x00, 0x00 } },{ 3, { 0x56, 0x00, 0x00 } },
+ { 3, { 0x57, 0x00, 0x00 } },{ 3, { 0x58, 0x00, 0x00 } },
+ { 3, { 0x59, 0x00, 0x00 } },{ 3, { 0x5a, 0x00, 0x00 } },
+ { 3, { 0x5b, 0x00, 0x00 } },{ 3, { 0x5c, 0x00, 0x00 } },
+ { 3, { 0x5d, 0x00, 0x00 } },{ 3, { 0x5e, 0x00, 0x00 } },
+ { 3, { 0x5f, 0x00, 0x00 } },{ 3, { 0x60, 0x00, 0x00 } },
+ { 3, { 0x61, 0x00, 0x00 } },{ 3, { 0x62, 0x00, 0x00 } },
+ { 3, { 0x63, 0x00, 0x00 } },{ 3, { 0x64, 0x00, 0x00 } },
+ { 3, { 0x65, 0x00, 0x00 } },{ 3, { 0x66, 0x00, 0x00 } },
+ { 3, { 0x67, 0x00, 0x00 } },{ 3, { 0x68, 0x00, 0x00 } },
+ { 3, { 0x69, 0x00, 0x00 } },{ 3, { 0x6a, 0x00, 0x00 } },
+ { 3, { 0x6b, 0x00, 0x00 } },{ 3, { 0x6c, 0x00, 0x00 } },
+ { 3, { 0x6d, 0x00, 0x00 } },{ 3, { 0x6e, 0x00, 0x00 } },
+ { 3, { 0x6f, 0x00, 0x00 } },{ 3, { 0x70, 0x00, 0x00 } },
+ { 3, { 0x71, 0x00, 0x00 } },{ 3, { 0x72, 0x00, 0x00 } },
+ { 3, { 0x73, 0x00, 0x00 } },{ 3, { 0x74, 0x00, 0x00 } },
+ { 3, { 0x75, 0x00, 0x00 } },{ 3, { 0x76, 0x00, 0x00 } },
+ { 3, { 0x77, 0x00, 0x00 } },{ 3, { 0x41, 0x00, 0x42 } },
+ { 3, { 0x42, 0x10, 0x42 } },{ 3, { 0x43, 0x20, 0x42 } },
+ { 3, { 0x44, 0x30, 0x42 } },{ 3, { 0x45, 0x00, 0x01 } },
+ { 3, { 0x46, 0x00, 0x01 } },{ 3, { 0x47, 0x00, 0x01 } },
+ { 3, { 0x48, 0x00, 0x01 } },
+ { 9, { 0x01, 0x03, 0xc5, 0x5c, 0x7a, 0x85, 0x01, 0x00, 0x54 } },
+ { 9, { 0x21, 0x03, 0xc5, 0x5c, 0x7a, 0x85, 0x01, 0x00, 0x54 } },
+ { 9, { 0x09, 0x0b, 0xb4, 0x6b, 0x74, 0x85, 0x95, 0x00, 0x34 } },
+ { 9, { 0x29, 0x0b, 0xb4, 0x6b, 0x74, 0x85, 0x95, 0x00, 0x34 } },
+ { 9, { 0x11, 0x17, 0x43, 0x62, 0x68, 0x89, 0xd1, 0xff, 0xb0 } },
+ { 9, { 0x31, 0x17, 0x43, 0x62, 0x68, 0x89, 0xd1, 0xff, 0xb0 } },
+ { 9, { 0x19, 0x20, 0x62, 0x51, 0x5a, 0x95, 0x19, 0x01, 0x50 } },
+ { 9, { 0x39, 0x20, 0x62, 0x51, 0x5a, 0x95, 0x19, 0x01, 0x50 } },
+ { 9, { 0x05, 0x3e, 0xd2, 0x69, 0x4e, 0x9a, 0x51, 0x00, 0xf0 } },
+ { 9, { 0x25, 0x3e, 0xd2, 0x69, 0x4e, 0x9a, 0x51, 0x00, 0xf0 } },
+ { 9, { 0x0d, 0x3d, 0xa1, 0x40, 0x7d, 0x9f, 0x29, 0xfe, 0x14 } },
+ { 9, { 0x2d, 0x3d, 0xa1, 0x40, 0x7d, 0x9f, 0x29, 0xfe, 0x14 } },
+ { 9, { 0x15, 0x73, 0xa1, 0x50, 0x5d, 0xa6, 0xf5, 0xfe, 0x38 } },
+ { 9, { 0x35, 0x73, 0xa1, 0x50, 0x5d, 0xa6, 0xf5, 0xfe, 0x38 } },
+ { 9, { 0x1d, 0xed, 0xd0, 0x68, 0x29, 0xb4, 0xe1, 0x00, 0xb8 } },
+ { 9, { 0x3d, 0xed, 0xd0, 0x68, 0x29, 0xb4, 0xe1, 0x00, 0xb8 } },
+ { 3, { 0x80, 0xb3, 0x0a } },
+ {-1, { 0 } }
+};
+
+/* bring hardware to a sane state. this has to be done, just in case someone
+ wants to capture from this device before it has been properly initialized.
+ the capture engine would badly fail, because no valid signal arrives on the
+ saa7146, thus leading to timeouts and stuff. */
+static int mxb_init_done(struct saa7146_dev* dev)
+{
+ struct mxb* mxb = (struct mxb*)dev->ext_priv;
+ struct i2c_msg msg;
+ struct tuner_setup tun_setup;
+ v4l2_std_id std = V4L2_STD_PAL_BG;
+
+ int i, err = 0;
+
+ /* mute audio on tea6420s */
+ tea6420_route(mxb, 6);
+
+ /* select video mode in saa7111a */
+ saa7111a_call(mxb, video, s_std, std);
+
+ /* select tuner-output on saa7111a */
+ saa7111a_call(mxb, video, s_routing, SAA7115_COMPOSITE0,
+ SAA7111_FMT_CCIR, 0);
+
+ /* select a tuner type */
+ tun_setup.mode_mask = T_ANALOG_TV;
+ tun_setup.addr = ADDR_UNSET;
+ tun_setup.type = TUNER_PHILIPS_PAL;
+ tuner_call(mxb, tuner, s_type_addr, &tun_setup);
+ /* tune in some frequency on tuner */
+ mxb->cur_freq.tuner = 0;
+ mxb->cur_freq.type = V4L2_TUNER_ANALOG_TV;
+ mxb->cur_freq.frequency = freq;
+ tuner_call(mxb, tuner, s_frequency, &mxb->cur_freq);
+
+ /* set a default video standard */
+ /* These two gpio calls set the GPIO pins that control the tda9820 */
+ saa7146_write(dev, GPIO_CTRL, 0x00404050);
+ saa7111a_call(mxb, core, s_gpio, 1);
+ saa7111a_call(mxb, video, s_std, std);
+ tuner_call(mxb, video, s_std, std);
+
+ /* switch to tuner-channel on tea6415c */
+ tea6415c_call(mxb, video, s_routing, 3, 17, 0);
+
+ /* select tuner-output on multicable on tea6415c */
+ tea6415c_call(mxb, video, s_routing, 3, 13, 0);
+
+ /* the rest for mxb */
+ mxb->cur_input = 0;
+ mxb->cur_audinput = video_audio_connect[mxb->cur_input];
+ mxb->cur_mute = 1;
+
+ mxb->cur_mode = V4L2_TUNER_MODE_STEREO;
+ mxb_update_audmode(mxb);
+
+ /* check if the saa7740 (aka 'sound arena module') is present
+ on the mxb. if so, we must initialize it. due to lack of
+ information about the saa7740, the values were reverse
+ engineered. */
+ msg.addr = 0x1b;
+ msg.flags = 0;
+ msg.len = mxb_saa7740_init[0].length;
+ msg.buf = &mxb_saa7740_init[0].data[0];
+
+ err = i2c_transfer(&mxb->i2c_adapter, &msg, 1);
+ if (err == 1) {
+ /* the sound arena module is a pos, that's probably the reason
+ philips refuses to hand out a datasheet for the saa7740...
+ it seems to screw up the i2c bus, so we disable fast irq
+ based i2c transactions here and rely on the slow and safe
+ polling method ... */
+ extension.flags &= ~SAA7146_USE_I2C_IRQ;
+ for (i = 1; ; i++) {
+ if (-1 == mxb_saa7740_init[i].length)
+ break;
+
+ msg.len = mxb_saa7740_init[i].length;
+ msg.buf = &mxb_saa7740_init[i].data[0];
+ err = i2c_transfer(&mxb->i2c_adapter, &msg, 1);
+ if (err != 1) {
+ DEB_D("failed to initialize 'sound arena module'\n");
+ goto err;
+ }
+ }
+ pr_info("'sound arena module' detected\n");
+ }
+err:
+ /* the rest for saa7146: you should definitely set some basic values
+ for the input-port handling of the saa7146. */
+
+ /* ext->saa has been filled by the core driver */
+
+ /* some stuff is done via variables */
+ saa7146_set_hps_source_and_sync(dev, input_port_selection[mxb->cur_input].hps_source,
+ input_port_selection[mxb->cur_input].hps_sync);
+
+ /* some stuff is done via direct write to the registers */
+
+ /* this is ugly, but because of the fact that this is completely
+ hardware dependend, it should be done directly... */
+ saa7146_write(dev, DD1_STREAM_B, 0x00000000);
+ saa7146_write(dev, DD1_INIT, 0x02000200);
+ saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26));
+
+ return 0;
+}
+
+/* interrupt-handler. this gets called when irq_mask is != 0.
+ it must clear the interrupt-bits in irq_mask it has handled */
+/*
+void mxb_irq_bh(struct saa7146_dev* dev, u32* irq_mask)
+{
+ struct mxb* mxb = (struct mxb*)dev->ext_priv;
+}
+*/
+
+static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i)
+{
+ DEB_EE("VIDIOC_ENUMINPUT %d\n", i->index);
+ if (i->index >= MXB_INPUTS)
+ return -EINVAL;
+ memcpy(i, &mxb_inputs[i->index], sizeof(struct v4l2_input));
+ return 0;
+}
+
+static int vidioc_g_input(struct file *file, void *fh, unsigned int *i)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct mxb *mxb = (struct mxb *)dev->ext_priv;
+ *i = mxb->cur_input;
+
+ DEB_EE("VIDIOC_G_INPUT %d\n", *i);
+ return 0;
+}
+
+static int vidioc_s_input(struct file *file, void *fh, unsigned int input)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct mxb *mxb = (struct mxb *)dev->ext_priv;
+ int err = 0;
+ int i = 0;
+
+ DEB_EE("VIDIOC_S_INPUT %d\n", input);
+
+ if (input >= MXB_INPUTS)
+ return -EINVAL;
+
+ mxb->cur_input = input;
+
+ saa7146_set_hps_source_and_sync(dev, input_port_selection[input].hps_source,
+ input_port_selection[input].hps_sync);
+
+ /* prepare switching of tea6415c and saa7111a;
+ have a look at the 'background'-file for further information */
+ switch (input) {
+ case TUNER:
+ i = SAA7115_COMPOSITE0;
+
+ err = tea6415c_call(mxb, video, s_routing, 3, 17, 0);
+
+ /* connect tuner-output always to multicable */
+ if (!err)
+ err = tea6415c_call(mxb, video, s_routing, 3, 13, 0);
+ break;
+ case AUX3_YC:
+ /* nothing to be done here. aux3_yc is
+ directly connected to the saa711a */
+ i = SAA7115_SVIDEO1;
+ break;
+ case AUX3:
+ /* nothing to be done here. aux3 is
+ directly connected to the saa711a */
+ i = SAA7115_COMPOSITE1;
+ break;
+ case AUX1:
+ i = SAA7115_COMPOSITE0;
+ err = tea6415c_call(mxb, video, s_routing, 1, 17, 0);
+ break;
+ }
+
+ if (err)
+ return err;
+
+ /* switch video in saa7111a */
+ if (saa7111a_call(mxb, video, s_routing, i, SAA7111_FMT_CCIR, 0))
+ pr_err("VIDIOC_S_INPUT: could not address saa7111a\n");
+
+ mxb->cur_audinput = video_audio_connect[input];
+ /* switch the audio-source only if necessary */
+ if (0 == mxb->cur_mute)
+ tea6420_route(mxb, mxb->cur_audinput);
+ if (mxb->cur_audinput == 0)
+ mxb_update_audmode(mxb);
+
+ return 0;
+}
+
+static int vidioc_g_tuner(struct file *file, void *fh, struct v4l2_tuner *t)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct mxb *mxb = (struct mxb *)dev->ext_priv;
+
+ if (t->index) {
+ DEB_D("VIDIOC_G_TUNER: channel %d does not have a tuner attached\n",
+ t->index);
+ return -EINVAL;
+ }
+
+ DEB_EE("VIDIOC_G_TUNER: %d\n", t->index);
+
+ memset(t, 0, sizeof(*t));
+ strscpy(t->name, "TV Tuner", sizeof(t->name));
+ t->type = V4L2_TUNER_ANALOG_TV;
+ t->capability = V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_STEREO |
+ V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP;
+ t->audmode = mxb->cur_mode;
+ return call_all(dev, tuner, g_tuner, t);
+}
+
+static int vidioc_s_tuner(struct file *file, void *fh, const struct v4l2_tuner *t)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct mxb *mxb = (struct mxb *)dev->ext_priv;
+
+ if (t->index) {
+ DEB_D("VIDIOC_S_TUNER: channel %d does not have a tuner attached\n",
+ t->index);
+ return -EINVAL;
+ }
+
+ mxb->cur_mode = t->audmode;
+ return call_all(dev, tuner, s_tuner, t);
+}
+
+static int vidioc_querystd(struct file *file, void *fh, v4l2_std_id *norm)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+
+ return call_all(dev, video, querystd, norm);
+}
+
+static int vidioc_g_frequency(struct file *file, void *fh, struct v4l2_frequency *f)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct mxb *mxb = (struct mxb *)dev->ext_priv;
+
+ if (f->tuner)
+ return -EINVAL;
+ *f = mxb->cur_freq;
+
+ DEB_EE("VIDIOC_G_FREQ: freq:0x%08x\n", mxb->cur_freq.frequency);
+ return 0;
+}
+
+static int vidioc_s_frequency(struct file *file, void *fh, const struct v4l2_frequency *f)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct mxb *mxb = (struct mxb *)dev->ext_priv;
+ struct saa7146_vv *vv = dev->vv_data;
+
+ if (f->tuner)
+ return -EINVAL;
+
+ if (V4L2_TUNER_ANALOG_TV != f->type)
+ return -EINVAL;
+
+ DEB_EE("VIDIOC_S_FREQUENCY: freq:0x%08x\n", mxb->cur_freq.frequency);
+
+ /* tune in desired frequency */
+ tuner_call(mxb, tuner, s_frequency, f);
+ /* let the tuner subdev clamp the frequency to the tuner range */
+ mxb->cur_freq = *f;
+ tuner_call(mxb, tuner, g_frequency, &mxb->cur_freq);
+ if (mxb->cur_audinput == 0)
+ mxb_update_audmode(mxb);
+
+ if (mxb->cur_input)
+ return 0;
+
+ /* hack: changing the frequency should invalidate the vbi-counter (=> alevt) */
+ spin_lock(&dev->slock);
+ vv->vbi_fieldcount = 0;
+ spin_unlock(&dev->slock);
+
+ return 0;
+}
+
+static int vidioc_enumaudio(struct file *file, void *fh, struct v4l2_audio *a)
+{
+ if (a->index >= MXB_AUDIOS)
+ return -EINVAL;
+ *a = mxb_audios[a->index];
+ return 0;
+}
+
+static int vidioc_g_audio(struct file *file, void *fh, struct v4l2_audio *a)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct mxb *mxb = (struct mxb *)dev->ext_priv;
+
+ DEB_EE("VIDIOC_G_AUDIO\n");
+ *a = mxb_audios[mxb->cur_audinput];
+ return 0;
+}
+
+static int vidioc_s_audio(struct file *file, void *fh, const struct v4l2_audio *a)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct mxb *mxb = (struct mxb *)dev->ext_priv;
+
+ DEB_D("VIDIOC_S_AUDIO %d\n", a->index);
+ if (a->index >= 32 ||
+ !(mxb_inputs[mxb->cur_input].audioset & (1 << a->index)))
+ return -EINVAL;
+
+ if (mxb->cur_audinput != a->index) {
+ mxb->cur_audinput = a->index;
+ tea6420_route(mxb, a->index);
+ if (mxb->cur_audinput == 0)
+ mxb_update_audmode(mxb);
+ }
+ return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int vidioc_g_register(struct file *file, void *fh, struct v4l2_dbg_register *reg)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+
+ if (reg->reg > pci_resource_len(dev->pci, 0) - 4)
+ return -EINVAL;
+ reg->val = saa7146_read(dev, reg->reg);
+ reg->size = 4;
+ return 0;
+}
+
+static int vidioc_s_register(struct file *file, void *fh, const struct v4l2_dbg_register *reg)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+
+ if (reg->reg > pci_resource_len(dev->pci, 0) - 4)
+ return -EINVAL;
+ saa7146_write(dev, reg->reg, reg->val);
+ return 0;
+}
+#endif
+
+static struct saa7146_ext_vv vv_data;
+
+/* this function only gets called when the probing was successful */
+static int mxb_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data *info)
+{
+ struct mxb *mxb;
+ int ret;
+
+ DEB_EE("dev:%p\n", dev);
+
+ ret = saa7146_vv_init(dev, &vv_data);
+ if (ret) {
+ ERR("Error in saa7146_vv_init()");
+ return ret;
+ }
+
+ if (mxb_probe(dev)) {
+ saa7146_vv_release(dev);
+ return -1;
+ }
+ mxb = (struct mxb *)dev->ext_priv;
+
+ vv_data.vid_ops.vidioc_enum_input = vidioc_enum_input;
+ vv_data.vid_ops.vidioc_g_input = vidioc_g_input;
+ vv_data.vid_ops.vidioc_s_input = vidioc_s_input;
+ vv_data.vid_ops.vidioc_querystd = vidioc_querystd;
+ vv_data.vid_ops.vidioc_g_tuner = vidioc_g_tuner;
+ vv_data.vid_ops.vidioc_s_tuner = vidioc_s_tuner;
+ vv_data.vid_ops.vidioc_g_frequency = vidioc_g_frequency;
+ vv_data.vid_ops.vidioc_s_frequency = vidioc_s_frequency;
+ vv_data.vid_ops.vidioc_enumaudio = vidioc_enumaudio;
+ vv_data.vid_ops.vidioc_g_audio = vidioc_g_audio;
+ vv_data.vid_ops.vidioc_s_audio = vidioc_s_audio;
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ vv_data.vid_ops.vidioc_g_register = vidioc_g_register;
+ vv_data.vid_ops.vidioc_s_register = vidioc_s_register;
+#endif
+ if (saa7146_register_device(&mxb->video_dev, dev, "mxb", VFL_TYPE_VIDEO)) {
+ ERR("cannot register capture v4l2 device. skipping.\n");
+ saa7146_vv_release(dev);
+ return -1;
+ }
+
+ /* initialization stuff (vbi) (only for revision > 0 and for extensions which want it)*/
+ if (MXB_BOARD_CAN_DO_VBI(dev)) {
+ if (saa7146_register_device(&mxb->vbi_dev, dev, "mxb", VFL_TYPE_VBI)) {
+ ERR("cannot register vbi v4l2 device. skipping.\n");
+ }
+ }
+
+ pr_info("found Multimedia eXtension Board #%d\n", mxb_num);
+
+ mxb_num++;
+ mxb_init_done(dev);
+ return 0;
+}
+
+static int mxb_detach(struct saa7146_dev *dev)
+{
+ struct mxb *mxb = (struct mxb *)dev->ext_priv;
+
+ DEB_EE("dev:%p\n", dev);
+
+ /* mute audio on tea6420s */
+ tea6420_route(mxb, 6);
+
+ saa7146_unregister_device(&mxb->video_dev,dev);
+ if (MXB_BOARD_CAN_DO_VBI(dev))
+ saa7146_unregister_device(&mxb->vbi_dev, dev);
+ saa7146_vv_release(dev);
+
+ mxb_num--;
+
+ i2c_del_adapter(&mxb->i2c_adapter);
+ kfree(mxb);
+
+ return 0;
+}
+
+static int std_callback(struct saa7146_dev *dev, struct saa7146_standard *standard)
+{
+ struct mxb *mxb = (struct mxb *)dev->ext_priv;
+
+ if (V4L2_STD_PAL_I == standard->id) {
+ v4l2_std_id std = V4L2_STD_PAL_I;
+
+ DEB_D("VIDIOC_S_STD: setting mxb for PAL_I\n");
+ /* These two gpio calls set the GPIO pins that control the tda9820 */
+ saa7146_write(dev, GPIO_CTRL, 0x00404050);
+ saa7111a_call(mxb, core, s_gpio, 0);
+ saa7111a_call(mxb, video, s_std, std);
+ if (mxb->cur_input == 0)
+ tuner_call(mxb, video, s_std, std);
+ } else {
+ v4l2_std_id std = V4L2_STD_PAL_BG;
+
+ if (mxb->cur_input)
+ std = standard->id;
+ DEB_D("VIDIOC_S_STD: setting mxb for PAL/NTSC/SECAM\n");
+ /* These two gpio calls set the GPIO pins that control the tda9820 */
+ saa7146_write(dev, GPIO_CTRL, 0x00404050);
+ saa7111a_call(mxb, core, s_gpio, 1);
+ saa7111a_call(mxb, video, s_std, std);
+ if (mxb->cur_input == 0)
+ tuner_call(mxb, video, s_std, std);
+ }
+ return 0;
+}
+
+static struct saa7146_standard standard[] = {
+ {
+ .name = "PAL-BG", .id = V4L2_STD_PAL_BG,
+ .v_offset = 0x17, .v_field = 288,
+ .h_offset = 0x14, .h_pixels = 680,
+ .v_max_out = 576, .h_max_out = 768,
+ }, {
+ .name = "PAL-I", .id = V4L2_STD_PAL_I,
+ .v_offset = 0x17, .v_field = 288,
+ .h_offset = 0x14, .h_pixels = 680,
+ .v_max_out = 576, .h_max_out = 768,
+ }, {
+ .name = "NTSC", .id = V4L2_STD_NTSC,
+ .v_offset = 0x16, .v_field = 240,
+ .h_offset = 0x06, .h_pixels = 708,
+ .v_max_out = 480, .h_max_out = 640,
+ }, {
+ .name = "SECAM", .id = V4L2_STD_SECAM,
+ .v_offset = 0x14, .v_field = 288,
+ .h_offset = 0x14, .h_pixels = 720,
+ .v_max_out = 576, .h_max_out = 768,
+ }
+};
+
+static struct saa7146_pci_extension_data mxb = {
+ .ext_priv = "Multimedia eXtension Board",
+ .ext = &extension,
+};
+
+static const struct pci_device_id pci_tbl[] = {
+ {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7146,
+ .subvendor = 0x0000,
+ .subdevice = 0x0000,
+ .driver_data = (unsigned long)&mxb,
+ }, {
+ .vendor = 0,
+ }
+};
+
+MODULE_DEVICE_TABLE(pci, pci_tbl);
+
+static struct saa7146_ext_vv vv_data = {
+ .inputs = MXB_INPUTS,
+ .capabilities = V4L2_CAP_TUNER | V4L2_CAP_VBI_CAPTURE | V4L2_CAP_AUDIO,
+ .stds = &standard[0],
+ .num_stds = ARRAY_SIZE(standard),
+ .std_callback = &std_callback,
+};
+
+static struct saa7146_extension extension = {
+ .name = "Multimedia eXtension Board",
+ .flags = SAA7146_USE_I2C_IRQ,
+
+ .pci_tbl = &pci_tbl[0],
+ .module = THIS_MODULE,
+
+ .attach = mxb_attach,
+ .detach = mxb_detach,
+
+ .irq_mask = 0,
+ .irq_func = NULL,
+};
+
+static int __init mxb_init_module(void)
+{
+ if (saa7146_register_extension(&extension)) {
+ DEB_S("failed to register extension\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static void __exit mxb_cleanup_module(void)
+{
+ saa7146_unregister_extension(&extension);
+}
+
+module_init(mxb_init_module);
+module_exit(mxb_cleanup_module);
+
+MODULE_DESCRIPTION("video4linux-2 driver for the Siemens-Nixdorf 'Multimedia eXtension board'");
+MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/pci/ttpci/Kconfig b/drivers/media/pci/ttpci/Kconfig
new file mode 100644
index 000000000000..65a6832a6b96
--- /dev/null
+++ b/drivers/media/pci/ttpci/Kconfig
@@ -0,0 +1,86 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config DVB_BUDGET_CORE
+ tristate "SAA7146 DVB cards (aka Budget, Nova-PCI)"
+ depends on DVB_CORE && PCI && I2C
+ select VIDEO_SAA7146
+ select TTPCI_EEPROM
+ help
+ Support for simple SAA7146 based DVB cards
+ (so called Budget- or Nova-PCI cards) without onboard
+ MPEG2 decoder.
+
+config DVB_BUDGET
+ tristate "Budget cards"
+ depends on DVB_BUDGET_CORE && I2C
+ select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_VES1X93 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_VES1820 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_L64781 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_TDA8083 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_S5H1420 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_TDA10086 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_TDA826X if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_LNBP21 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_TDA1004X if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_ISL6423 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_STV090x if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_STV6110x if MEDIA_SUBDRV_AUTOSELECT
+ help
+ Support for simple SAA7146 based DVB cards (so called Budget-
+ or Nova-PCI cards) without onboard MPEG2 decoder, and without
+ analog inputs or an onboard Common Interface connector.
+
+ Say Y if you own such a card and want to use it.
+
+ To compile this driver as a module, choose M here: the
+ module will be called budget.
+
+config DVB_BUDGET_CI
+ tristate "Budget cards with onboard CI connector"
+ depends on DVB_BUDGET_CORE && I2C
+ select DVB_STV0297 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_TDA1004X if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_STB0899 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_STB6100 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_LNBP21 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_STV0288 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_STB6000 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_TDA10023 if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_TDA827X if MEDIA_SUBDRV_AUTOSELECT
+ depends on RC_CORE
+ help
+ Support for simple SAA7146 based DVB cards
+ (so called Budget- or Nova-PCI cards) without onboard
+ MPEG2 decoder, but with onboard Common Interface connector.
+
+ Note: The Common Interface is not yet supported by this driver
+ due to lack of information from the vendor.
+
+ Say Y if you own such a card and want to use it.
+
+ To compile this driver as a module, choose M here: the
+ module will be called budget-ci.
+
+config DVB_BUDGET_AV
+ tristate "Budget cards with analog video inputs"
+ depends on DVB_BUDGET_CORE && I2C
+ select VIDEO_SAA7146_VV
+ depends on VIDEO_DEV # dependencies of VIDEO_SAA7146_VV
+ select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_TDA1004X if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_TDA10021 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_TDA10023 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_STB0899 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_TDA8261 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_TUA6100 if MEDIA_SUBDRV_AUTOSELECT
+ help
+ Support for simple SAA7146 based DVB cards
+ (so called Budget- or Nova-PCI cards) without onboard
+ MPEG2 decoder, but with one or more analog video inputs.
+
+ Say Y if you own such a card and want to use it.
+
+ To compile this driver as a module, choose M here: the
+ module will be called budget-av.
diff --git a/drivers/media/pci/ttpci/Makefile b/drivers/media/pci/ttpci/Makefile
new file mode 100644
index 000000000000..b0708f6e40cc
--- /dev/null
+++ b/drivers/media/pci/ttpci/Makefile
@@ -0,0 +1,13 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for the kernel SAA7146 FULL TS DVB device driver
+#
+
+obj-$(CONFIG_DVB_BUDGET_CORE) += budget-core.o
+obj-$(CONFIG_DVB_BUDGET) += budget.o
+obj-$(CONFIG_DVB_BUDGET_AV) += budget-av.o
+obj-$(CONFIG_DVB_BUDGET_CI) += budget-ci.o
+
+ccflags-y += -I $(srctree)/drivers/media/dvb-frontends/
+ccflags-y += -I $(srctree)/drivers/media/tuners
+ccflags-y += -I $(srctree)/drivers/media/common
diff --git a/drivers/media/pci/ttpci/budget-av.c b/drivers/media/pci/ttpci/budget-av.c
new file mode 100644
index 000000000000..3cb83005cf09
--- /dev/null
+++ b/drivers/media/pci/ttpci/budget-av.c
@@ -0,0 +1,1622 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * budget-av.c: driver for the SAA7146 based Budget DVB cards
+ * with analog video in
+ *
+ * Compiled from various sources by Michael Hunold <michael@mihu.de>
+ *
+ * CI interface support (c) 2004 Olivier Gournet <ogournet@anevia.com> &
+ * Andrew de Quincey <adq_dvb@lidskialf.net>
+ *
+ * Copyright (C) 2002 Ralph Metzler <rjkm@metzlerbros.de>
+ *
+ * Copyright (C) 1999-2002 Ralph Metzler
+ * & Marcus Metzler for convergence integrated media GmbH
+ *
+ * the project's page is at https://linuxtv.org
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include "budget.h"
+#include "stv0299.h"
+#include "stb0899_drv.h"
+#include "stb0899_reg.h"
+#include "stb0899_cfg.h"
+#include "tda8261.h"
+#include "tda8261_cfg.h"
+#include "tda1002x.h"
+#include "tda1004x.h"
+#include "tua6100.h"
+#include "dvb-pll.h"
+#include <media/drv-intf/saa7146_vv.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/spinlock.h>
+
+#include <media/dvb_ca_en50221.h>
+
+#define DEBICICAM 0x02420000
+
+#define SLOTSTATUS_NONE 1
+#define SLOTSTATUS_PRESENT 2
+#define SLOTSTATUS_RESET 4
+#define SLOTSTATUS_READY 8
+#define SLOTSTATUS_OCCUPIED (SLOTSTATUS_PRESENT|SLOTSTATUS_RESET|SLOTSTATUS_READY)
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+struct budget_av {
+ struct budget budget;
+ struct video_device vd;
+ int cur_input;
+ int has_saa7113;
+ struct tasklet_struct ciintf_irq_tasklet;
+ int slot_status;
+ struct dvb_ca_en50221 ca;
+ u8 reinitialise_demod:1;
+};
+
+static int ciintf_slot_shutdown(struct dvb_ca_en50221 *ca, int slot);
+
+
+/* GPIO Connections:
+ * 0 - Vcc/Reset (Reset is controlled by capacitor). Resets the frontend *AS WELL*!
+ * 1 - CI memory select 0=>IO memory, 1=>Attribute Memory
+ * 2 - CI Card Enable (Active Low)
+ * 3 - CI Card Detect
+ */
+
+/****************************************************************************
+ * INITIALIZATION
+ ****************************************************************************/
+
+static u8 i2c_readreg(struct i2c_adapter *i2c, u8 id, u8 reg)
+{
+ u8 mm1[] = { 0x00 };
+ u8 mm2[] = { 0x00 };
+ struct i2c_msg msgs[2];
+
+ msgs[0].flags = 0;
+ msgs[1].flags = I2C_M_RD;
+ msgs[0].addr = msgs[1].addr = id / 2;
+ mm1[0] = reg;
+ msgs[0].len = 1;
+ msgs[1].len = 1;
+ msgs[0].buf = mm1;
+ msgs[1].buf = mm2;
+
+ i2c_transfer(i2c, msgs, 2);
+
+ return mm2[0];
+}
+
+static int i2c_readregs(struct i2c_adapter *i2c, u8 id, u8 reg, u8 * buf, u8 len)
+{
+ u8 mm1[] = { reg };
+ struct i2c_msg msgs[2] = {
+ {.addr = id / 2,.flags = 0,.buf = mm1,.len = 1},
+ {.addr = id / 2,.flags = I2C_M_RD,.buf = buf,.len = len}
+ };
+
+ if (i2c_transfer(i2c, msgs, 2) != 2)
+ return -EIO;
+
+ return 0;
+}
+
+static int i2c_writereg(struct i2c_adapter *i2c, u8 id, u8 reg, u8 val)
+{
+ u8 msg[2] = { reg, val };
+ struct i2c_msg msgs;
+
+ msgs.flags = 0;
+ msgs.addr = id / 2;
+ msgs.len = 2;
+ msgs.buf = msg;
+ return i2c_transfer(i2c, &msgs, 1);
+}
+
+static int ciintf_read_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int address)
+{
+ struct budget_av *budget_av = (struct budget_av *) ca->data;
+ int result;
+
+ if (slot != 0)
+ return -EINVAL;
+
+ saa7146_setgpio(budget_av->budget.dev, 1, SAA7146_GPIO_OUTHI);
+ udelay(1);
+
+ result = ttpci_budget_debiread(&budget_av->budget, DEBICICAM, address & 0xfff, 1, 0, 1);
+ if (result == -ETIMEDOUT) {
+ ciintf_slot_shutdown(ca, slot);
+ pr_info("cam ejected 1\n");
+ }
+ return result;
+}
+
+static int ciintf_write_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int address, u8 value)
+{
+ struct budget_av *budget_av = (struct budget_av *) ca->data;
+ int result;
+
+ if (slot != 0)
+ return -EINVAL;
+
+ saa7146_setgpio(budget_av->budget.dev, 1, SAA7146_GPIO_OUTHI);
+ udelay(1);
+
+ result = ttpci_budget_debiwrite(&budget_av->budget, DEBICICAM, address & 0xfff, 1, value, 0, 1);
+ if (result == -ETIMEDOUT) {
+ ciintf_slot_shutdown(ca, slot);
+ pr_info("cam ejected 2\n");
+ }
+ return result;
+}
+
+static int ciintf_read_cam_control(struct dvb_ca_en50221 *ca, int slot, u8 address)
+{
+ struct budget_av *budget_av = (struct budget_av *) ca->data;
+ int result;
+
+ if (slot != 0)
+ return -EINVAL;
+
+ saa7146_setgpio(budget_av->budget.dev, 1, SAA7146_GPIO_OUTLO);
+ udelay(1);
+
+ result = ttpci_budget_debiread(&budget_av->budget, DEBICICAM, address & 3, 1, 0, 0);
+ if (result == -ETIMEDOUT) {
+ ciintf_slot_shutdown(ca, slot);
+ pr_info("cam ejected 3\n");
+ return -ETIMEDOUT;
+ }
+ return result;
+}
+
+static int ciintf_write_cam_control(struct dvb_ca_en50221 *ca, int slot, u8 address, u8 value)
+{
+ struct budget_av *budget_av = (struct budget_av *) ca->data;
+ int result;
+
+ if (slot != 0)
+ return -EINVAL;
+
+ saa7146_setgpio(budget_av->budget.dev, 1, SAA7146_GPIO_OUTLO);
+ udelay(1);
+
+ result = ttpci_budget_debiwrite(&budget_av->budget, DEBICICAM, address & 3, 1, value, 0, 0);
+ if (result == -ETIMEDOUT) {
+ ciintf_slot_shutdown(ca, slot);
+ pr_info("cam ejected 5\n");
+ }
+ return result;
+}
+
+static int ciintf_slot_reset(struct dvb_ca_en50221 *ca, int slot)
+{
+ struct budget_av *budget_av = (struct budget_av *) ca->data;
+ struct saa7146_dev *saa = budget_av->budget.dev;
+
+ if (slot != 0)
+ return -EINVAL;
+
+ dprintk(1, "ciintf_slot_reset\n");
+ budget_av->slot_status = SLOTSTATUS_RESET;
+
+ saa7146_setgpio(saa, 2, SAA7146_GPIO_OUTHI); /* disable card */
+
+ saa7146_setgpio(saa, 0, SAA7146_GPIO_OUTHI); /* Vcc off */
+ msleep(2);
+ saa7146_setgpio(saa, 0, SAA7146_GPIO_OUTLO); /* Vcc on */
+ msleep(20); /* 20 ms Vcc settling time */
+
+ saa7146_setgpio(saa, 2, SAA7146_GPIO_OUTLO); /* enable card */
+ ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTB);
+ msleep(20);
+
+ /* reinitialise the frontend if necessary */
+ if (budget_av->reinitialise_demod)
+ dvb_frontend_reinitialise(budget_av->budget.dvb_frontend);
+
+ return 0;
+}
+
+static int ciintf_slot_shutdown(struct dvb_ca_en50221 *ca, int slot)
+{
+ struct budget_av *budget_av = (struct budget_av *) ca->data;
+ struct saa7146_dev *saa = budget_av->budget.dev;
+
+ if (slot != 0)
+ return -EINVAL;
+
+ dprintk(1, "ciintf_slot_shutdown\n");
+
+ ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTB);
+ budget_av->slot_status = SLOTSTATUS_NONE;
+
+ return 0;
+}
+
+static int ciintf_slot_ts_enable(struct dvb_ca_en50221 *ca, int slot)
+{
+ struct budget_av *budget_av = (struct budget_av *) ca->data;
+ struct saa7146_dev *saa = budget_av->budget.dev;
+
+ if (slot != 0)
+ return -EINVAL;
+
+ dprintk(1, "ciintf_slot_ts_enable: %d\n", budget_av->slot_status);
+
+ ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTA);
+
+ return 0;
+}
+
+static int ciintf_poll_slot_status(struct dvb_ca_en50221 *ca, int slot, int open)
+{
+ struct budget_av *budget_av = (struct budget_av *) ca->data;
+ struct saa7146_dev *saa = budget_av->budget.dev;
+ int result;
+
+ if (slot != 0)
+ return -EINVAL;
+
+ /* test the card detect line - needs to be done carefully
+ * since it never goes high for some CAMs on this interface (e.g. topuptv) */
+ if (budget_av->slot_status == SLOTSTATUS_NONE) {
+ saa7146_setgpio(saa, 3, SAA7146_GPIO_INPUT);
+ udelay(1);
+ if (saa7146_read(saa, PSR) & MASK_06) {
+ if (budget_av->slot_status == SLOTSTATUS_NONE) {
+ budget_av->slot_status = SLOTSTATUS_PRESENT;
+ pr_info("cam inserted A\n");
+ }
+ }
+ saa7146_setgpio(saa, 3, SAA7146_GPIO_OUTLO);
+ }
+
+ /* We also try and read from IO memory to work round the above detection bug. If
+ * there is no CAM, we will get a timeout. Only done if there is no cam
+ * present, since this test actually breaks some cams :(
+ *
+ * if the CI interface is not open, we also do the above test since we
+ * don't care if the cam has problems - we'll be resetting it on open() anyway */
+ if ((budget_av->slot_status == SLOTSTATUS_NONE) || (!open)) {
+ saa7146_setgpio(budget_av->budget.dev, 1, SAA7146_GPIO_OUTLO);
+ result = ttpci_budget_debiread(&budget_av->budget, DEBICICAM, 0, 1, 0, 1);
+ if ((result >= 0) && (budget_av->slot_status == SLOTSTATUS_NONE)) {
+ budget_av->slot_status = SLOTSTATUS_PRESENT;
+ pr_info("cam inserted B\n");
+ } else if (result < 0) {
+ if (budget_av->slot_status != SLOTSTATUS_NONE) {
+ ciintf_slot_shutdown(ca, slot);
+ pr_info("cam ejected 5\n");
+ return 0;
+ }
+ }
+ }
+
+ /* read from attribute memory in reset/ready state to know when the CAM is ready */
+ if (budget_av->slot_status == SLOTSTATUS_RESET) {
+ result = ciintf_read_attribute_mem(ca, slot, 0);
+ if (result == 0x1d) {
+ budget_av->slot_status = SLOTSTATUS_READY;
+ }
+ }
+
+ /* work out correct return code */
+ if (budget_av->slot_status != SLOTSTATUS_NONE) {
+ if (budget_av->slot_status & SLOTSTATUS_READY) {
+ return DVB_CA_EN50221_POLL_CAM_PRESENT | DVB_CA_EN50221_POLL_CAM_READY;
+ }
+ return DVB_CA_EN50221_POLL_CAM_PRESENT;
+ }
+ return 0;
+}
+
+static int ciintf_init(struct budget_av *budget_av)
+{
+ struct saa7146_dev *saa = budget_av->budget.dev;
+ int result;
+
+ memset(&budget_av->ca, 0, sizeof(struct dvb_ca_en50221));
+
+ saa7146_setgpio(saa, 0, SAA7146_GPIO_OUTLO);
+ saa7146_setgpio(saa, 1, SAA7146_GPIO_OUTLO);
+ saa7146_setgpio(saa, 2, SAA7146_GPIO_OUTLO);
+ saa7146_setgpio(saa, 3, SAA7146_GPIO_OUTLO);
+
+ /* Enable DEBI pins */
+ saa7146_write(saa, MC1, MASK_27 | MASK_11);
+
+ /* register CI interface */
+ budget_av->ca.owner = THIS_MODULE;
+ budget_av->ca.read_attribute_mem = ciintf_read_attribute_mem;
+ budget_av->ca.write_attribute_mem = ciintf_write_attribute_mem;
+ budget_av->ca.read_cam_control = ciintf_read_cam_control;
+ budget_av->ca.write_cam_control = ciintf_write_cam_control;
+ budget_av->ca.slot_reset = ciintf_slot_reset;
+ budget_av->ca.slot_shutdown = ciintf_slot_shutdown;
+ budget_av->ca.slot_ts_enable = ciintf_slot_ts_enable;
+ budget_av->ca.poll_slot_status = ciintf_poll_slot_status;
+ budget_av->ca.data = budget_av;
+ budget_av->budget.ci_present = 1;
+ budget_av->slot_status = SLOTSTATUS_NONE;
+
+ if ((result = dvb_ca_en50221_init(&budget_av->budget.dvb_adapter,
+ &budget_av->ca, 0, 1)) != 0) {
+ pr_err("ci initialisation failed\n");
+ goto error;
+ }
+
+ pr_info("ci interface initialised\n");
+ return 0;
+
+error:
+ saa7146_write(saa, MC1, MASK_27);
+ return result;
+}
+
+static void ciintf_deinit(struct budget_av *budget_av)
+{
+ struct saa7146_dev *saa = budget_av->budget.dev;
+
+ saa7146_setgpio(saa, 0, SAA7146_GPIO_INPUT);
+ saa7146_setgpio(saa, 1, SAA7146_GPIO_INPUT);
+ saa7146_setgpio(saa, 2, SAA7146_GPIO_INPUT);
+ saa7146_setgpio(saa, 3, SAA7146_GPIO_INPUT);
+
+ /* release the CA device */
+ dvb_ca_en50221_release(&budget_av->ca);
+
+ /* disable DEBI pins */
+ saa7146_write(saa, MC1, MASK_27);
+}
+
+
+static const u8 saa7113_tab[] = {
+ 0x01, 0x08,
+ 0x02, 0xc0,
+ 0x03, 0x33,
+ 0x04, 0x00,
+ 0x05, 0x00,
+ 0x06, 0xeb,
+ 0x07, 0xe0,
+ 0x08, 0x28,
+ 0x09, 0x00,
+ 0x0a, 0x80,
+ 0x0b, 0x47,
+ 0x0c, 0x40,
+ 0x0d, 0x00,
+ 0x0e, 0x01,
+ 0x0f, 0x44,
+
+ 0x10, 0x08,
+ 0x11, 0x0c,
+ 0x12, 0x7b,
+ 0x13, 0x00,
+ 0x15, 0x00, 0x16, 0x00, 0x17, 0x00,
+
+ 0x57, 0xff,
+ 0x40, 0x82, 0x58, 0x00, 0x59, 0x54, 0x5a, 0x07,
+ 0x5b, 0x83, 0x5e, 0x00,
+ 0xff
+};
+
+static int saa7113_init(struct budget_av *budget_av)
+{
+ struct budget *budget = &budget_av->budget;
+ struct saa7146_dev *saa = budget->dev;
+ const u8 *data = saa7113_tab;
+
+ saa7146_setgpio(saa, 0, SAA7146_GPIO_OUTHI);
+ msleep(200);
+
+ if (i2c_writereg(&budget->i2c_adap, 0x4a, 0x01, 0x08) != 1) {
+ dprintk(1, "saa7113 not found on KNC card\n");
+ return -ENODEV;
+ }
+
+ dprintk(1, "saa7113 detected and initializing\n");
+
+ while (*data != 0xff) {
+ i2c_writereg(&budget->i2c_adap, 0x4a, *data, *(data + 1));
+ data += 2;
+ }
+
+ dprintk(1, "saa7113 status=%02x\n", i2c_readreg(&budget->i2c_adap, 0x4a, 0x1f));
+
+ return 0;
+}
+
+static int saa7113_setinput(struct budget_av *budget_av, int input)
+{
+ struct budget *budget = &budget_av->budget;
+
+ if (1 != budget_av->has_saa7113)
+ return -ENODEV;
+
+ if (input == 1) {
+ i2c_writereg(&budget->i2c_adap, 0x4a, 0x02, 0xc7);
+ i2c_writereg(&budget->i2c_adap, 0x4a, 0x09, 0x80);
+ } else if (input == 0) {
+ i2c_writereg(&budget->i2c_adap, 0x4a, 0x02, 0xc0);
+ i2c_writereg(&budget->i2c_adap, 0x4a, 0x09, 0x00);
+ } else
+ return -EINVAL;
+
+ budget_av->cur_input = input;
+ return 0;
+}
+
+
+static int philips_su1278_ty_ci_set_symbol_rate(struct dvb_frontend *fe, u32 srate, u32 ratio)
+{
+ u8 aclk = 0;
+ u8 bclk = 0;
+ u8 m1;
+
+ aclk = 0xb5;
+ if (srate < 2000000)
+ bclk = 0x86;
+ else if (srate < 5000000)
+ bclk = 0x89;
+ else if (srate < 15000000)
+ bclk = 0x8f;
+ else if (srate < 45000000)
+ bclk = 0x95;
+
+ m1 = 0x14;
+ if (srate < 4000000)
+ m1 = 0x10;
+
+ stv0299_writereg(fe, 0x13, aclk);
+ stv0299_writereg(fe, 0x14, bclk);
+ stv0299_writereg(fe, 0x1f, (ratio >> 16) & 0xff);
+ stv0299_writereg(fe, 0x20, (ratio >> 8) & 0xff);
+ stv0299_writereg(fe, 0x21, (ratio) & 0xf0);
+ stv0299_writereg(fe, 0x0f, 0x80 | m1);
+
+ return 0;
+}
+
+static int philips_su1278_ty_ci_tuner_set_params(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ u32 div;
+ u8 buf[4];
+ struct budget *budget = (struct budget *) fe->dvb->priv;
+ struct i2c_msg msg = {.addr = 0x61,.flags = 0,.buf = buf,.len = sizeof(buf) };
+
+ if ((c->frequency < 950000) || (c->frequency > 2150000))
+ return -EINVAL;
+
+ div = (c->frequency + (125 - 1)) / 125; /* round correctly */
+ buf[0] = (div >> 8) & 0x7f;
+ buf[1] = div & 0xff;
+ buf[2] = 0x80 | ((div & 0x18000) >> 10) | 4;
+ buf[3] = 0x20;
+
+ if (c->symbol_rate < 4000000)
+ buf[3] |= 1;
+
+ if (c->frequency < 1250000)
+ buf[3] |= 0;
+ else if (c->frequency < 1550000)
+ buf[3] |= 0x40;
+ else if (c->frequency < 2050000)
+ buf[3] |= 0x80;
+ else if (c->frequency < 2150000)
+ buf[3] |= 0xC0;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ if (i2c_transfer(&budget->i2c_adap, &msg, 1) != 1)
+ return -EIO;
+ return 0;
+}
+
+static u8 typhoon_cinergy1200s_inittab[] = {
+ 0x01, 0x15,
+ 0x02, 0x30,
+ 0x03, 0x00,
+ 0x04, 0x7d, /* F22FR = 0x7d, F22 = f_VCO / 128 / 0x7d = 22 kHz */
+ 0x05, 0x35, /* I2CT = 0, SCLT = 1, SDAT = 1 */
+ 0x06, 0x40, /* DAC not used, set to high impendance mode */
+ 0x07, 0x00, /* DAC LSB */
+ 0x08, 0x40, /* DiSEqC off */
+ 0x09, 0x00, /* FIFO */
+ 0x0c, 0x51, /* OP1 ctl = Normal, OP1 val = 1 (LNB Power ON) */
+ 0x0d, 0x82, /* DC offset compensation = ON, beta_agc1 = 2 */
+ 0x0e, 0x23, /* alpha_tmg = 2, beta_tmg = 3 */
+ 0x10, 0x3f, // AGC2 0x3d
+ 0x11, 0x84,
+ 0x12, 0xb9,
+ 0x15, 0xc9, // lock detector threshold
+ 0x16, 0x00,
+ 0x17, 0x00,
+ 0x18, 0x00,
+ 0x19, 0x00,
+ 0x1a, 0x00,
+ 0x1f, 0x50,
+ 0x20, 0x00,
+ 0x21, 0x00,
+ 0x22, 0x00,
+ 0x23, 0x00,
+ 0x28, 0x00, // out imp: normal out type: parallel FEC mode:0
+ 0x29, 0x1e, // 1/2 threshold
+ 0x2a, 0x14, // 2/3 threshold
+ 0x2b, 0x0f, // 3/4 threshold
+ 0x2c, 0x09, // 5/6 threshold
+ 0x2d, 0x05, // 7/8 threshold
+ 0x2e, 0x01,
+ 0x31, 0x1f, // test all FECs
+ 0x32, 0x19, // viterbi and synchro search
+ 0x33, 0xfc, // rs control
+ 0x34, 0x93, // error control
+ 0x0f, 0x92,
+ 0xff, 0xff
+};
+
+static const struct stv0299_config typhoon_config = {
+ .demod_address = 0x68,
+ .inittab = typhoon_cinergy1200s_inittab,
+ .mclk = 88000000UL,
+ .invert = 0,
+ .skip_reinit = 0,
+ .lock_output = STV0299_LOCKOUTPUT_1,
+ .volt13_op0_op1 = STV0299_VOLT13_OP0,
+ .min_delay_ms = 100,
+ .set_symbol_rate = philips_su1278_ty_ci_set_symbol_rate,
+};
+
+
+static const struct stv0299_config cinergy_1200s_config = {
+ .demod_address = 0x68,
+ .inittab = typhoon_cinergy1200s_inittab,
+ .mclk = 88000000UL,
+ .invert = 0,
+ .skip_reinit = 0,
+ .lock_output = STV0299_LOCKOUTPUT_0,
+ .volt13_op0_op1 = STV0299_VOLT13_OP0,
+ .min_delay_ms = 100,
+ .set_symbol_rate = philips_su1278_ty_ci_set_symbol_rate,
+};
+
+static const struct stv0299_config cinergy_1200s_1894_0010_config = {
+ .demod_address = 0x68,
+ .inittab = typhoon_cinergy1200s_inittab,
+ .mclk = 88000000UL,
+ .invert = 1,
+ .skip_reinit = 0,
+ .lock_output = STV0299_LOCKOUTPUT_1,
+ .volt13_op0_op1 = STV0299_VOLT13_OP0,
+ .min_delay_ms = 100,
+ .set_symbol_rate = philips_su1278_ty_ci_set_symbol_rate,
+};
+
+static int philips_cu1216_tuner_set_params(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ struct budget *budget = (struct budget *) fe->dvb->priv;
+ u8 buf[6];
+ struct i2c_msg msg = {.addr = 0x60,.flags = 0,.buf = buf,.len = sizeof(buf) };
+ int i;
+
+#define CU1216_IF 36125000
+#define TUNER_MUL 62500
+
+ u32 div = (c->frequency + CU1216_IF + TUNER_MUL / 2) / TUNER_MUL;
+
+ buf[0] = (div >> 8) & 0x7f;
+ buf[1] = div & 0xff;
+ buf[2] = 0xce;
+ buf[3] = (c->frequency < 150000000 ? 0x01 :
+ c->frequency < 445000000 ? 0x02 : 0x04);
+ buf[4] = 0xde;
+ buf[5] = 0x20;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ if (i2c_transfer(&budget->i2c_adap, &msg, 1) != 1)
+ return -EIO;
+
+ /* wait for the pll lock */
+ msg.flags = I2C_M_RD;
+ msg.len = 1;
+ for (i = 0; i < 20; i++) {
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ if (i2c_transfer(&budget->i2c_adap, &msg, 1) == 1 && (buf[0] & 0x40))
+ break;
+ msleep(10);
+ }
+
+ /* switch the charge pump to the lower current */
+ msg.flags = 0;
+ msg.len = 2;
+ msg.buf = &buf[2];
+ buf[2] &= ~0x40;
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ if (i2c_transfer(&budget->i2c_adap, &msg, 1) != 1)
+ return -EIO;
+
+ return 0;
+}
+
+static struct tda1002x_config philips_cu1216_config = {
+ .demod_address = 0x0c,
+ .invert = 1,
+};
+
+static struct tda1002x_config philips_cu1216_config_altaddress = {
+ .demod_address = 0x0d,
+ .invert = 0,
+};
+
+static struct tda10023_config philips_cu1216_tda10023_config = {
+ .demod_address = 0x0c,
+ .invert = 1,
+};
+
+static int philips_tu1216_tuner_init(struct dvb_frontend *fe)
+{
+ struct budget *budget = (struct budget *) fe->dvb->priv;
+ static u8 tu1216_init[] = { 0x0b, 0xf5, 0x85, 0xab };
+ struct i2c_msg tuner_msg = {.addr = 0x60,.flags = 0,.buf = tu1216_init,.len = sizeof(tu1216_init) };
+
+ // setup PLL configuration
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ if (i2c_transfer(&budget->i2c_adap, &tuner_msg, 1) != 1)
+ return -EIO;
+ msleep(1);
+
+ return 0;
+}
+
+static int philips_tu1216_tuner_set_params(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ struct budget *budget = (struct budget *) fe->dvb->priv;
+ u8 tuner_buf[4];
+ struct i2c_msg tuner_msg = {.addr = 0x60,.flags = 0,.buf = tuner_buf,.len =
+ sizeof(tuner_buf) };
+ int tuner_frequency = 0;
+ u8 band, cp, filter;
+
+ // determine charge pump
+ tuner_frequency = c->frequency + 36166000;
+ if (tuner_frequency < 87000000)
+ return -EINVAL;
+ else if (tuner_frequency < 130000000)
+ cp = 3;
+ else if (tuner_frequency < 160000000)
+ cp = 5;
+ else if (tuner_frequency < 200000000)
+ cp = 6;
+ else if (tuner_frequency < 290000000)
+ cp = 3;
+ else if (tuner_frequency < 420000000)
+ cp = 5;
+ else if (tuner_frequency < 480000000)
+ cp = 6;
+ else if (tuner_frequency < 620000000)
+ cp = 3;
+ else if (tuner_frequency < 830000000)
+ cp = 5;
+ else if (tuner_frequency < 895000000)
+ cp = 7;
+ else
+ return -EINVAL;
+
+ // determine band
+ if (c->frequency < 49000000)
+ return -EINVAL;
+ else if (c->frequency < 161000000)
+ band = 1;
+ else if (c->frequency < 444000000)
+ band = 2;
+ else if (c->frequency < 861000000)
+ band = 4;
+ else
+ return -EINVAL;
+
+ // setup PLL filter
+ switch (c->bandwidth_hz) {
+ case 6000000:
+ filter = 0;
+ break;
+
+ case 7000000:
+ filter = 0;
+ break;
+
+ case 8000000:
+ filter = 1;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ // calculate divisor
+ // ((36166000+((1000000/6)/2)) + Finput)/(1000000/6)
+ tuner_frequency = (((c->frequency / 1000) * 6) + 217496) / 1000;
+
+ // setup tuner buffer
+ tuner_buf[0] = (tuner_frequency >> 8) & 0x7f;
+ tuner_buf[1] = tuner_frequency & 0xff;
+ tuner_buf[2] = 0xca;
+ tuner_buf[3] = (cp << 5) | (filter << 3) | band;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ if (i2c_transfer(&budget->i2c_adap, &tuner_msg, 1) != 1)
+ return -EIO;
+
+ msleep(1);
+ return 0;
+}
+
+static int philips_tu1216_request_firmware(struct dvb_frontend *fe,
+ const struct firmware **fw, char *name)
+{
+ struct budget *budget = (struct budget *) fe->dvb->priv;
+
+ return request_firmware(fw, name, &budget->dev->pci->dev);
+}
+
+static struct tda1004x_config philips_tu1216_config = {
+
+ .demod_address = 0x8,
+ .invert = 1,
+ .invert_oclk = 1,
+ .xtal_freq = TDA10046_XTAL_4M,
+ .agc_config = TDA10046_AGC_DEFAULT,
+ .if_freq = TDA10046_FREQ_3617,
+ .request_firmware = philips_tu1216_request_firmware,
+};
+
+static u8 philips_sd1878_inittab[] = {
+ 0x01, 0x15,
+ 0x02, 0x30,
+ 0x03, 0x00,
+ 0x04, 0x7d,
+ 0x05, 0x35,
+ 0x06, 0x40,
+ 0x07, 0x00,
+ 0x08, 0x43,
+ 0x09, 0x02,
+ 0x0C, 0x51,
+ 0x0D, 0x82,
+ 0x0E, 0x23,
+ 0x10, 0x3f,
+ 0x11, 0x84,
+ 0x12, 0xb9,
+ 0x15, 0xc9,
+ 0x16, 0x19,
+ 0x17, 0x8c,
+ 0x18, 0x59,
+ 0x19, 0xf8,
+ 0x1a, 0xfe,
+ 0x1c, 0x7f,
+ 0x1d, 0x00,
+ 0x1e, 0x00,
+ 0x1f, 0x50,
+ 0x20, 0x00,
+ 0x21, 0x00,
+ 0x22, 0x00,
+ 0x23, 0x00,
+ 0x28, 0x00,
+ 0x29, 0x28,
+ 0x2a, 0x14,
+ 0x2b, 0x0f,
+ 0x2c, 0x09,
+ 0x2d, 0x09,
+ 0x31, 0x1f,
+ 0x32, 0x19,
+ 0x33, 0xfc,
+ 0x34, 0x93,
+ 0xff, 0xff
+};
+
+static int philips_sd1878_ci_set_symbol_rate(struct dvb_frontend *fe,
+ u32 srate, u32 ratio)
+{
+ u8 aclk = 0;
+ u8 bclk = 0;
+ u8 m1;
+
+ aclk = 0xb5;
+ if (srate < 2000000)
+ bclk = 0x86;
+ else if (srate < 5000000)
+ bclk = 0x89;
+ else if (srate < 15000000)
+ bclk = 0x8f;
+ else if (srate < 45000000)
+ bclk = 0x95;
+
+ m1 = 0x14;
+ if (srate < 4000000)
+ m1 = 0x10;
+
+ stv0299_writereg(fe, 0x0e, 0x23);
+ stv0299_writereg(fe, 0x0f, 0x94);
+ stv0299_writereg(fe, 0x10, 0x39);
+ stv0299_writereg(fe, 0x13, aclk);
+ stv0299_writereg(fe, 0x14, bclk);
+ stv0299_writereg(fe, 0x15, 0xc9);
+ stv0299_writereg(fe, 0x1f, (ratio >> 16) & 0xff);
+ stv0299_writereg(fe, 0x20, (ratio >> 8) & 0xff);
+ stv0299_writereg(fe, 0x21, (ratio) & 0xf0);
+ stv0299_writereg(fe, 0x0f, 0x80 | m1);
+
+ return 0;
+}
+
+static const struct stv0299_config philips_sd1878_config = {
+ .demod_address = 0x68,
+ .inittab = philips_sd1878_inittab,
+ .mclk = 88000000UL,
+ .invert = 0,
+ .skip_reinit = 0,
+ .lock_output = STV0299_LOCKOUTPUT_1,
+ .volt13_op0_op1 = STV0299_VOLT13_OP0,
+ .min_delay_ms = 100,
+ .set_symbol_rate = philips_sd1878_ci_set_symbol_rate,
+};
+
+/* KNC1 DVB-S (STB0899) Inittab */
+static const struct stb0899_s1_reg knc1_stb0899_s1_init_1[] = {
+
+ { STB0899_DEV_ID , 0x81 },
+ { STB0899_DISCNTRL1 , 0x32 },
+ { STB0899_DISCNTRL2 , 0x80 },
+ { STB0899_DISRX_ST0 , 0x04 },
+ { STB0899_DISRX_ST1 , 0x00 },
+ { STB0899_DISPARITY , 0x00 },
+ { STB0899_DISSTATUS , 0x20 },
+ { STB0899_DISF22 , 0x8c },
+ { STB0899_DISF22RX , 0x9a },
+ { STB0899_SYSREG , 0x0b },
+ { STB0899_ACRPRESC , 0x11 },
+ { STB0899_ACRDIV1 , 0x0a },
+ { STB0899_ACRDIV2 , 0x05 },
+ { STB0899_DACR1 , 0x00 },
+ { STB0899_DACR2 , 0x00 },
+ { STB0899_OUTCFG , 0x00 },
+ { STB0899_MODECFG , 0x00 },
+ { STB0899_IRQSTATUS_3 , 0x30 },
+ { STB0899_IRQSTATUS_2 , 0x00 },
+ { STB0899_IRQSTATUS_1 , 0x00 },
+ { STB0899_IRQSTATUS_0 , 0x00 },
+ { STB0899_IRQMSK_3 , 0xf3 },
+ { STB0899_IRQMSK_2 , 0xfc },
+ { STB0899_IRQMSK_1 , 0xff },
+ { STB0899_IRQMSK_0 , 0xff },
+ { STB0899_IRQCFG , 0x00 },
+ { STB0899_I2CCFG , 0x88 },
+ { STB0899_I2CRPT , 0x58 }, /* Repeater=8, Stop=disabled */
+ { STB0899_IOPVALUE5 , 0x00 },
+ { STB0899_IOPVALUE4 , 0x20 },
+ { STB0899_IOPVALUE3 , 0xc9 },
+ { STB0899_IOPVALUE2 , 0x90 },
+ { STB0899_IOPVALUE1 , 0x40 },
+ { STB0899_IOPVALUE0 , 0x00 },
+ { STB0899_GPIO00CFG , 0x82 },
+ { STB0899_GPIO01CFG , 0x82 },
+ { STB0899_GPIO02CFG , 0x82 },
+ { STB0899_GPIO03CFG , 0x82 },
+ { STB0899_GPIO04CFG , 0x82 },
+ { STB0899_GPIO05CFG , 0x82 },
+ { STB0899_GPIO06CFG , 0x82 },
+ { STB0899_GPIO07CFG , 0x82 },
+ { STB0899_GPIO08CFG , 0x82 },
+ { STB0899_GPIO09CFG , 0x82 },
+ { STB0899_GPIO10CFG , 0x82 },
+ { STB0899_GPIO11CFG , 0x82 },
+ { STB0899_GPIO12CFG , 0x82 },
+ { STB0899_GPIO13CFG , 0x82 },
+ { STB0899_GPIO14CFG , 0x82 },
+ { STB0899_GPIO15CFG , 0x82 },
+ { STB0899_GPIO16CFG , 0x82 },
+ { STB0899_GPIO17CFG , 0x82 },
+ { STB0899_GPIO18CFG , 0x82 },
+ { STB0899_GPIO19CFG , 0x82 },
+ { STB0899_GPIO20CFG , 0x82 },
+ { STB0899_SDATCFG , 0xb8 },
+ { STB0899_SCLTCFG , 0xba },
+ { STB0899_AGCRFCFG , 0x08 }, /* 0x1c */
+ { STB0899_GPIO22 , 0x82 }, /* AGCBB2CFG */
+ { STB0899_GPIO21 , 0x91 }, /* AGCBB1CFG */
+ { STB0899_DIRCLKCFG , 0x82 },
+ { STB0899_CLKOUT27CFG , 0x7e },
+ { STB0899_STDBYCFG , 0x82 },
+ { STB0899_CS0CFG , 0x82 },
+ { STB0899_CS1CFG , 0x82 },
+ { STB0899_DISEQCOCFG , 0x20 },
+ { STB0899_GPIO32CFG , 0x82 },
+ { STB0899_GPIO33CFG , 0x82 },
+ { STB0899_GPIO34CFG , 0x82 },
+ { STB0899_GPIO35CFG , 0x82 },
+ { STB0899_GPIO36CFG , 0x82 },
+ { STB0899_GPIO37CFG , 0x82 },
+ { STB0899_GPIO38CFG , 0x82 },
+ { STB0899_GPIO39CFG , 0x82 },
+ { STB0899_NCOARSE , 0x15 }, /* 0x15 = 27 Mhz Clock, F/3 = 198MHz, F/6 = 99MHz */
+ { STB0899_SYNTCTRL , 0x02 }, /* 0x00 = CLK from CLKI, 0x02 = CLK from XTALI */
+ { STB0899_FILTCTRL , 0x00 },
+ { STB0899_SYSCTRL , 0x00 },
+ { STB0899_STOPCLK1 , 0x20 },
+ { STB0899_STOPCLK2 , 0x00 },
+ { STB0899_INTBUFSTATUS , 0x00 },
+ { STB0899_INTBUFCTRL , 0x0a },
+ { 0xffff , 0xff },
+};
+
+static const struct stb0899_s1_reg knc1_stb0899_s1_init_3[] = {
+ { STB0899_DEMOD , 0x00 },
+ { STB0899_RCOMPC , 0xc9 },
+ { STB0899_AGC1CN , 0x41 },
+ { STB0899_AGC1REF , 0x08 },
+ { STB0899_RTC , 0x7a },
+ { STB0899_TMGCFG , 0x4e },
+ { STB0899_AGC2REF , 0x33 },
+ { STB0899_TLSR , 0x84 },
+ { STB0899_CFD , 0xee },
+ { STB0899_ACLC , 0x87 },
+ { STB0899_BCLC , 0x94 },
+ { STB0899_EQON , 0x41 },
+ { STB0899_LDT , 0xdd },
+ { STB0899_LDT2 , 0xc9 },
+ { STB0899_EQUALREF , 0xb4 },
+ { STB0899_TMGRAMP , 0x10 },
+ { STB0899_TMGTHD , 0x30 },
+ { STB0899_IDCCOMP , 0xfb },
+ { STB0899_QDCCOMP , 0x03 },
+ { STB0899_POWERI , 0x3b },
+ { STB0899_POWERQ , 0x3d },
+ { STB0899_RCOMP , 0x81 },
+ { STB0899_AGCIQIN , 0x80 },
+ { STB0899_AGC2I1 , 0x04 },
+ { STB0899_AGC2I2 , 0xf5 },
+ { STB0899_TLIR , 0x25 },
+ { STB0899_RTF , 0x80 },
+ { STB0899_DSTATUS , 0x00 },
+ { STB0899_LDI , 0xca },
+ { STB0899_CFRM , 0xf1 },
+ { STB0899_CFRL , 0xf3 },
+ { STB0899_NIRM , 0x2a },
+ { STB0899_NIRL , 0x05 },
+ { STB0899_ISYMB , 0x17 },
+ { STB0899_QSYMB , 0xfa },
+ { STB0899_SFRH , 0x2f },
+ { STB0899_SFRM , 0x68 },
+ { STB0899_SFRL , 0x40 },
+ { STB0899_SFRUPH , 0x2f },
+ { STB0899_SFRUPM , 0x68 },
+ { STB0899_SFRUPL , 0x40 },
+ { STB0899_EQUAI1 , 0xfd },
+ { STB0899_EQUAQ1 , 0x04 },
+ { STB0899_EQUAI2 , 0x0f },
+ { STB0899_EQUAQ2 , 0xff },
+ { STB0899_EQUAI3 , 0xdf },
+ { STB0899_EQUAQ3 , 0xfa },
+ { STB0899_EQUAI4 , 0x37 },
+ { STB0899_EQUAQ4 , 0x0d },
+ { STB0899_EQUAI5 , 0xbd },
+ { STB0899_EQUAQ5 , 0xf7 },
+ { STB0899_DSTATUS2 , 0x00 },
+ { STB0899_VSTATUS , 0x00 },
+ { STB0899_VERROR , 0xff },
+ { STB0899_IQSWAP , 0x2a },
+ { STB0899_ECNT1M , 0x00 },
+ { STB0899_ECNT1L , 0x00 },
+ { STB0899_ECNT2M , 0x00 },
+ { STB0899_ECNT2L , 0x00 },
+ { STB0899_ECNT3M , 0x00 },
+ { STB0899_ECNT3L , 0x00 },
+ { STB0899_FECAUTO1 , 0x06 },
+ { STB0899_FECM , 0x01 },
+ { STB0899_VTH12 , 0xf0 },
+ { STB0899_VTH23 , 0xa0 },
+ { STB0899_VTH34 , 0x78 },
+ { STB0899_VTH56 , 0x4e },
+ { STB0899_VTH67 , 0x48 },
+ { STB0899_VTH78 , 0x38 },
+ { STB0899_PRVIT , 0xff },
+ { STB0899_VITSYNC , 0x19 },
+ { STB0899_RSULC , 0xb1 }, /* DVB = 0xb1, DSS = 0xa1 */
+ { STB0899_TSULC , 0x42 },
+ { STB0899_RSLLC , 0x40 },
+ { STB0899_TSLPL , 0x12 },
+ { STB0899_TSCFGH , 0x0c },
+ { STB0899_TSCFGM , 0x00 },
+ { STB0899_TSCFGL , 0x0c },
+ { STB0899_TSOUT , 0x4d }, /* 0x0d for CAM */
+ { STB0899_RSSYNCDEL , 0x00 },
+ { STB0899_TSINHDELH , 0x02 },
+ { STB0899_TSINHDELM , 0x00 },
+ { STB0899_TSINHDELL , 0x00 },
+ { STB0899_TSLLSTKM , 0x00 },
+ { STB0899_TSLLSTKL , 0x00 },
+ { STB0899_TSULSTKM , 0x00 },
+ { STB0899_TSULSTKL , 0xab },
+ { STB0899_PCKLENUL , 0x00 },
+ { STB0899_PCKLENLL , 0xcc },
+ { STB0899_RSPCKLEN , 0xcc },
+ { STB0899_TSSTATUS , 0x80 },
+ { STB0899_ERRCTRL1 , 0xb6 },
+ { STB0899_ERRCTRL2 , 0x96 },
+ { STB0899_ERRCTRL3 , 0x89 },
+ { STB0899_DMONMSK1 , 0x27 },
+ { STB0899_DMONMSK0 , 0x03 },
+ { STB0899_DEMAPVIT , 0x5c },
+ { STB0899_PLPARM , 0x1f },
+ { STB0899_PDELCTRL , 0x48 },
+ { STB0899_PDELCTRL2 , 0x00 },
+ { STB0899_BBHCTRL1 , 0x00 },
+ { STB0899_BBHCTRL2 , 0x00 },
+ { STB0899_HYSTTHRESH , 0x77 },
+ { STB0899_MATCSTM , 0x00 },
+ { STB0899_MATCSTL , 0x00 },
+ { STB0899_UPLCSTM , 0x00 },
+ { STB0899_UPLCSTL , 0x00 },
+ { STB0899_DFLCSTM , 0x00 },
+ { STB0899_DFLCSTL , 0x00 },
+ { STB0899_SYNCCST , 0x00 },
+ { STB0899_SYNCDCSTM , 0x00 },
+ { STB0899_SYNCDCSTL , 0x00 },
+ { STB0899_ISI_ENTRY , 0x00 },
+ { STB0899_ISI_BIT_EN , 0x00 },
+ { STB0899_MATSTRM , 0x00 },
+ { STB0899_MATSTRL , 0x00 },
+ { STB0899_UPLSTRM , 0x00 },
+ { STB0899_UPLSTRL , 0x00 },
+ { STB0899_DFLSTRM , 0x00 },
+ { STB0899_DFLSTRL , 0x00 },
+ { STB0899_SYNCSTR , 0x00 },
+ { STB0899_SYNCDSTRM , 0x00 },
+ { STB0899_SYNCDSTRL , 0x00 },
+ { STB0899_CFGPDELSTATUS1 , 0x10 },
+ { STB0899_CFGPDELSTATUS2 , 0x00 },
+ { STB0899_BBFERRORM , 0x00 },
+ { STB0899_BBFERRORL , 0x00 },
+ { STB0899_UPKTERRORM , 0x00 },
+ { STB0899_UPKTERRORL , 0x00 },
+ { 0xffff , 0xff },
+};
+
+/* STB0899 demodulator config for the KNC1 and clones */
+static struct stb0899_config knc1_dvbs2_config = {
+ .init_dev = knc1_stb0899_s1_init_1,
+ .init_s2_demod = stb0899_s2_init_2,
+ .init_s1_demod = knc1_stb0899_s1_init_3,
+ .init_s2_fec = stb0899_s2_init_4,
+ .init_tst = stb0899_s1_init_5,
+
+ .postproc = NULL,
+
+ .demod_address = 0x68,
+// .ts_output_mode = STB0899_OUT_PARALLEL, /* types = SERIAL/PARALLEL */
+ .block_sync_mode = STB0899_SYNC_FORCED, /* DSS, SYNC_FORCED/UNSYNCED */
+// .ts_pfbit_toggle = STB0899_MPEG_NORMAL, /* DirecTV, MPEG toggling seq */
+
+ .xtal_freq = 27000000,
+ .inversion = IQ_SWAP_OFF,
+
+ .lo_clk = 76500000,
+ .hi_clk = 90000000,
+
+ .esno_ave = STB0899_DVBS2_ESNO_AVE,
+ .esno_quant = STB0899_DVBS2_ESNO_QUANT,
+ .avframes_coarse = STB0899_DVBS2_AVFRAMES_COARSE,
+ .avframes_fine = STB0899_DVBS2_AVFRAMES_FINE,
+ .miss_threshold = STB0899_DVBS2_MISS_THRESHOLD,
+ .uwp_threshold_acq = STB0899_DVBS2_UWP_THRESHOLD_ACQ,
+ .uwp_threshold_track = STB0899_DVBS2_UWP_THRESHOLD_TRACK,
+ .uwp_threshold_sof = STB0899_DVBS2_UWP_THRESHOLD_SOF,
+ .sof_search_timeout = STB0899_DVBS2_SOF_SEARCH_TIMEOUT,
+
+ .btr_nco_bits = STB0899_DVBS2_BTR_NCO_BITS,
+ .btr_gain_shift_offset = STB0899_DVBS2_BTR_GAIN_SHIFT_OFFSET,
+ .crl_nco_bits = STB0899_DVBS2_CRL_NCO_BITS,
+ .ldpc_max_iter = STB0899_DVBS2_LDPC_MAX_ITER,
+
+ .tuner_get_frequency = tda8261_get_frequency,
+ .tuner_set_frequency = tda8261_set_frequency,
+ .tuner_set_bandwidth = NULL,
+ .tuner_get_bandwidth = tda8261_get_bandwidth,
+ .tuner_set_rfsiggain = NULL
+};
+
+/*
+ * SD1878/SHA tuner config
+ * 1F, Single I/P, Horizontal mount, High Sensitivity
+ */
+static const struct tda8261_config sd1878c_config = {
+// .name = "SD1878/SHA",
+ .addr = 0x60,
+ .step_size = TDA8261_STEP_1000 /* kHz */
+};
+
+static u8 read_pwm(struct budget_av *budget_av)
+{
+ u8 b = 0xff;
+ u8 pwm;
+ struct i2c_msg msg[] = { {.addr = 0x50,.flags = 0,.buf = &b,.len = 1},
+ {.addr = 0x50,.flags = I2C_M_RD,.buf = &pwm,.len = 1}
+ };
+
+ if ((i2c_transfer(&budget_av->budget.i2c_adap, msg, 2) != 2)
+ || (pwm == 0xff))
+ pwm = 0x48;
+
+ return pwm;
+}
+
+#define SUBID_DVBS_KNC1 0x0010
+#define SUBID_DVBS_KNC1_PLUS 0x0011
+#define SUBID_DVBS_TYPHOON 0x4f56
+#define SUBID_DVBS_CINERGY1200 0x1154
+#define SUBID_DVBS_CYNERGY1200N 0x1155
+#define SUBID_DVBS_TV_STAR 0x0014
+#define SUBID_DVBS_TV_STAR_PLUS_X4 0x0015
+#define SUBID_DVBS_TV_STAR_CI 0x0016
+#define SUBID_DVBS2_KNC1 0x0018
+#define SUBID_DVBS2_KNC1_OEM 0x0019
+#define SUBID_DVBS_EASYWATCH_1 0x001a
+#define SUBID_DVBS_EASYWATCH_2 0x001b
+#define SUBID_DVBS2_EASYWATCH 0x001d
+#define SUBID_DVBS_EASYWATCH 0x001e
+
+#define SUBID_DVBC_EASYWATCH 0x002a
+#define SUBID_DVBC_EASYWATCH_MK3 0x002c
+#define SUBID_DVBC_KNC1 0x0020
+#define SUBID_DVBC_KNC1_PLUS 0x0021
+#define SUBID_DVBC_KNC1_MK3 0x0022
+#define SUBID_DVBC_KNC1_TDA10024 0x0028
+#define SUBID_DVBC_KNC1_PLUS_MK3 0x0023
+#define SUBID_DVBC_CINERGY1200 0x1156
+#define SUBID_DVBC_CINERGY1200_MK3 0x1176
+
+#define SUBID_DVBT_EASYWATCH 0x003a
+#define SUBID_DVBT_KNC1_PLUS 0x0031
+#define SUBID_DVBT_KNC1 0x0030
+#define SUBID_DVBT_CINERGY1200 0x1157
+
+static void frontend_init(struct budget_av *budget_av)
+{
+ struct saa7146_dev * saa = budget_av->budget.dev;
+ struct dvb_frontend * fe = NULL;
+
+ /* Enable / PowerON Frontend */
+ saa7146_setgpio(saa, 0, SAA7146_GPIO_OUTLO);
+
+ /* Wait for PowerON */
+ msleep(100);
+
+ /* additional setup necessary for the PLUS cards */
+ switch (saa->pci->subsystem_device) {
+ case SUBID_DVBS_KNC1_PLUS:
+ case SUBID_DVBC_KNC1_PLUS:
+ case SUBID_DVBT_KNC1_PLUS:
+ case SUBID_DVBC_EASYWATCH:
+ case SUBID_DVBC_KNC1_PLUS_MK3:
+ case SUBID_DVBS2_KNC1:
+ case SUBID_DVBS2_KNC1_OEM:
+ case SUBID_DVBS2_EASYWATCH:
+ saa7146_setgpio(saa, 3, SAA7146_GPIO_OUTHI);
+ break;
+ }
+
+ switch (saa->pci->subsystem_device) {
+
+ case SUBID_DVBS_KNC1:
+ /*
+ * maybe that setting is needed for other dvb-s cards as well,
+ * but so far it has been only confirmed for this type
+ */
+ budget_av->reinitialise_demod = 1;
+ fallthrough;
+ case SUBID_DVBS_KNC1_PLUS:
+ case SUBID_DVBS_EASYWATCH_1:
+ if (saa->pci->subsystem_vendor == 0x1894) {
+ fe = dvb_attach(stv0299_attach, &cinergy_1200s_1894_0010_config,
+ &budget_av->budget.i2c_adap);
+ if (fe) {
+ dvb_attach(tua6100_attach, fe, 0x60, &budget_av->budget.i2c_adap);
+ }
+ } else {
+ fe = dvb_attach(stv0299_attach, &typhoon_config,
+ &budget_av->budget.i2c_adap);
+ if (fe) {
+ fe->ops.tuner_ops.set_params = philips_su1278_ty_ci_tuner_set_params;
+ }
+ }
+ break;
+
+ case SUBID_DVBS_TV_STAR:
+ case SUBID_DVBS_TV_STAR_PLUS_X4:
+ case SUBID_DVBS_TV_STAR_CI:
+ case SUBID_DVBS_CYNERGY1200N:
+ case SUBID_DVBS_EASYWATCH:
+ case SUBID_DVBS_EASYWATCH_2:
+ fe = dvb_attach(stv0299_attach, &philips_sd1878_config,
+ &budget_av->budget.i2c_adap);
+ if (fe) {
+ dvb_attach(dvb_pll_attach, fe, 0x60,
+ &budget_av->budget.i2c_adap,
+ DVB_PLL_PHILIPS_SD1878_TDA8261);
+ }
+ break;
+
+ case SUBID_DVBS_TYPHOON:
+ fe = dvb_attach(stv0299_attach, &typhoon_config,
+ &budget_av->budget.i2c_adap);
+ if (fe) {
+ fe->ops.tuner_ops.set_params = philips_su1278_ty_ci_tuner_set_params;
+ }
+ break;
+ case SUBID_DVBS2_KNC1:
+ case SUBID_DVBS2_KNC1_OEM:
+ case SUBID_DVBS2_EASYWATCH:
+ budget_av->reinitialise_demod = 1;
+ if ((fe = dvb_attach(stb0899_attach, &knc1_dvbs2_config, &budget_av->budget.i2c_adap)))
+ dvb_attach(tda8261_attach, fe, &sd1878c_config, &budget_av->budget.i2c_adap);
+
+ break;
+ case SUBID_DVBS_CINERGY1200:
+ fe = dvb_attach(stv0299_attach, &cinergy_1200s_config,
+ &budget_av->budget.i2c_adap);
+ if (fe) {
+ fe->ops.tuner_ops.set_params = philips_su1278_ty_ci_tuner_set_params;
+ }
+ break;
+
+ case SUBID_DVBC_KNC1:
+ case SUBID_DVBC_KNC1_PLUS:
+ case SUBID_DVBC_CINERGY1200:
+ case SUBID_DVBC_EASYWATCH:
+ budget_av->reinitialise_demod = 1;
+ budget_av->budget.dev->i2c_bitrate = SAA7146_I2C_BUS_BIT_RATE_240;
+ fe = dvb_attach(tda10021_attach, &philips_cu1216_config,
+ &budget_av->budget.i2c_adap,
+ read_pwm(budget_av));
+ if (fe == NULL)
+ fe = dvb_attach(tda10021_attach, &philips_cu1216_config_altaddress,
+ &budget_av->budget.i2c_adap,
+ read_pwm(budget_av));
+ if (fe) {
+ fe->ops.tuner_ops.set_params = philips_cu1216_tuner_set_params;
+ }
+ break;
+
+ case SUBID_DVBC_EASYWATCH_MK3:
+ case SUBID_DVBC_CINERGY1200_MK3:
+ case SUBID_DVBC_KNC1_MK3:
+ case SUBID_DVBC_KNC1_TDA10024:
+ case SUBID_DVBC_KNC1_PLUS_MK3:
+ budget_av->reinitialise_demod = 1;
+ budget_av->budget.dev->i2c_bitrate = SAA7146_I2C_BUS_BIT_RATE_240;
+ fe = dvb_attach(tda10023_attach,
+ &philips_cu1216_tda10023_config,
+ &budget_av->budget.i2c_adap,
+ read_pwm(budget_av));
+ if (fe) {
+ fe->ops.tuner_ops.set_params = philips_cu1216_tuner_set_params;
+ }
+ break;
+
+ case SUBID_DVBT_EASYWATCH:
+ case SUBID_DVBT_KNC1:
+ case SUBID_DVBT_KNC1_PLUS:
+ case SUBID_DVBT_CINERGY1200:
+ budget_av->reinitialise_demod = 1;
+ fe = dvb_attach(tda10046_attach, &philips_tu1216_config,
+ &budget_av->budget.i2c_adap);
+ if (fe) {
+ fe->ops.tuner_ops.init = philips_tu1216_tuner_init;
+ fe->ops.tuner_ops.set_params = philips_tu1216_tuner_set_params;
+ }
+ break;
+ }
+
+ if (fe == NULL) {
+ pr_err("A frontend driver was not found for device [%04x:%04x] subsystem [%04x:%04x]\n",
+ saa->pci->vendor,
+ saa->pci->device,
+ saa->pci->subsystem_vendor,
+ saa->pci->subsystem_device);
+ return;
+ }
+
+ budget_av->budget.dvb_frontend = fe;
+
+ if (dvb_register_frontend(&budget_av->budget.dvb_adapter,
+ budget_av->budget.dvb_frontend)) {
+ pr_err("Frontend registration failed!\n");
+ dvb_frontend_detach(budget_av->budget.dvb_frontend);
+ budget_av->budget.dvb_frontend = NULL;
+ }
+}
+
+
+static void budget_av_irq(struct saa7146_dev *dev, u32 * isr)
+{
+ struct budget_av *budget_av = (struct budget_av *) dev->ext_priv;
+
+ dprintk(8, "dev: %p, budget_av: %p\n", dev, budget_av);
+
+ if (*isr & MASK_10)
+ ttpci_budget_irq10_handler(dev, isr);
+}
+
+static int budget_av_detach(struct saa7146_dev *dev)
+{
+ struct budget_av *budget_av = (struct budget_av *) dev->ext_priv;
+ int err;
+
+ dprintk(2, "dev: %p\n", dev);
+
+ if (1 == budget_av->has_saa7113) {
+ saa7146_setgpio(dev, 0, SAA7146_GPIO_OUTLO);
+
+ msleep(200);
+
+ saa7146_unregister_device(&budget_av->vd, dev);
+
+ saa7146_vv_release(dev);
+ }
+
+ if (budget_av->budget.ci_present)
+ ciintf_deinit(budget_av);
+
+ if (budget_av->budget.dvb_frontend != NULL) {
+ dvb_unregister_frontend(budget_av->budget.dvb_frontend);
+ dvb_frontend_detach(budget_av->budget.dvb_frontend);
+ }
+ err = ttpci_budget_deinit(&budget_av->budget);
+
+ kfree(budget_av);
+
+ return err;
+}
+
+#define KNC1_INPUTS 2
+static struct v4l2_input knc1_inputs[KNC1_INPUTS] = {
+ { 0, "Composite", V4L2_INPUT_TYPE_TUNER, 1, 0,
+ V4L2_STD_PAL_BG | V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD },
+ { 1, "S-Video", V4L2_INPUT_TYPE_CAMERA, 2, 0,
+ V4L2_STD_PAL_BG | V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD },
+};
+
+static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i)
+{
+ dprintk(1, "VIDIOC_ENUMINPUT %d\n", i->index);
+ if (i->index >= KNC1_INPUTS)
+ return -EINVAL;
+ memcpy(i, &knc1_inputs[i->index], sizeof(struct v4l2_input));
+ return 0;
+}
+
+static int vidioc_g_input(struct file *file, void *fh, unsigned int *i)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct budget_av *budget_av = (struct budget_av *)dev->ext_priv;
+
+ *i = budget_av->cur_input;
+
+ dprintk(1, "VIDIOC_G_INPUT %d\n", *i);
+ return 0;
+}
+
+static int vidioc_s_input(struct file *file, void *fh, unsigned int input)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct budget_av *budget_av = (struct budget_av *)dev->ext_priv;
+
+ dprintk(1, "VIDIOC_S_INPUT %d\n", input);
+ return saa7113_setinput(budget_av, input);
+}
+
+static struct saa7146_ext_vv vv_data;
+
+static int budget_av_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data *info)
+{
+ struct budget_av *budget_av;
+ u8 *mac;
+ int err;
+
+ dprintk(2, "dev: %p\n", dev);
+
+ if (!(budget_av = kzalloc(sizeof(struct budget_av), GFP_KERNEL)))
+ return -ENOMEM;
+
+ budget_av->has_saa7113 = 0;
+ budget_av->budget.ci_present = 0;
+
+ dev->ext_priv = budget_av;
+
+ err = ttpci_budget_init(&budget_av->budget, dev, info, THIS_MODULE,
+ adapter_nr);
+ if (err) {
+ kfree(budget_av);
+ return err;
+ }
+
+ /* knc1 initialization */
+ saa7146_write(dev, DD1_STREAM_B, 0x04000000);
+ saa7146_write(dev, DD1_INIT, 0x07000600);
+ saa7146_write(dev, MC2, MASK_09 | MASK_25 | MASK_10 | MASK_26);
+
+ if (saa7113_init(budget_av) == 0) {
+ budget_av->has_saa7113 = 1;
+ err = saa7146_vv_init(dev, &vv_data);
+ if (err != 0) {
+ /* fixme: proper cleanup here */
+ ERR("cannot init vv subsystem\n");
+ return err;
+ }
+ vv_data.vid_ops.vidioc_enum_input = vidioc_enum_input;
+ vv_data.vid_ops.vidioc_g_input = vidioc_g_input;
+ vv_data.vid_ops.vidioc_s_input = vidioc_s_input;
+
+ if ((err = saa7146_register_device(&budget_av->vd, dev, "knc1", VFL_TYPE_VIDEO))) {
+ /* fixme: proper cleanup here */
+ ERR("cannot register capture v4l2 device\n");
+ saa7146_vv_release(dev);
+ return err;
+ }
+
+ /* beware: this modifies dev->vv ... */
+ saa7146_set_hps_source_and_sync(dev, SAA7146_HPS_SOURCE_PORT_A,
+ SAA7146_HPS_SYNC_PORT_A);
+
+ saa7113_setinput(budget_av, 0);
+ }
+
+ /* fixme: find some sane values here... */
+ saa7146_write(dev, PCI_BT_V1, 0x1c00101f);
+
+ mac = budget_av->budget.dvb_adapter.proposed_mac;
+ if (i2c_readregs(&budget_av->budget.i2c_adap, 0xa0, 0x30, mac, 6)) {
+ pr_err("KNC1-%d: Could not read MAC from KNC1 card\n",
+ budget_av->budget.dvb_adapter.num);
+ eth_zero_addr(mac);
+ } else {
+ pr_info("KNC1-%d: MAC addr = %pM\n",
+ budget_av->budget.dvb_adapter.num, mac);
+ }
+
+ budget_av->budget.dvb_adapter.priv = budget_av;
+ frontend_init(budget_av);
+ ciintf_init(budget_av);
+
+ ttpci_budget_init_hooks(&budget_av->budget);
+
+ return 0;
+}
+
+static struct saa7146_standard standard[] = {
+ {.name = "PAL",.id = V4L2_STD_PAL,
+ .v_offset = 0x17,.v_field = 288,
+ .h_offset = 0x14,.h_pixels = 680,
+ .v_max_out = 576,.h_max_out = 768 },
+
+ {.name = "NTSC",.id = V4L2_STD_NTSC,
+ .v_offset = 0x16,.v_field = 240,
+ .h_offset = 0x06,.h_pixels = 708,
+ .v_max_out = 480,.h_max_out = 640, },
+};
+
+static struct saa7146_ext_vv vv_data = {
+ .inputs = 2,
+ .capabilities = 0, // perhaps later: V4L2_CAP_VBI_CAPTURE, but that need tweaking with the saa7113
+ .flags = 0,
+ .stds = &standard[0],
+ .num_stds = ARRAY_SIZE(standard),
+};
+
+static struct saa7146_extension budget_extension;
+
+MAKE_BUDGET_INFO(knc1s, "KNC1 DVB-S", BUDGET_KNC1S);
+MAKE_BUDGET_INFO(knc1s2,"KNC1 DVB-S2", BUDGET_KNC1S2);
+MAKE_BUDGET_INFO(sates2,"Satelco EasyWatch DVB-S2", BUDGET_KNC1S2);
+MAKE_BUDGET_INFO(knc1c, "KNC1 DVB-C", BUDGET_KNC1C);
+MAKE_BUDGET_INFO(knc1t, "KNC1 DVB-T", BUDGET_KNC1T);
+MAKE_BUDGET_INFO(kncxs, "KNC TV STAR DVB-S", BUDGET_TVSTAR);
+MAKE_BUDGET_INFO(satewpls, "Satelco EasyWatch DVB-S light", BUDGET_TVSTAR);
+MAKE_BUDGET_INFO(satewpls1, "Satelco EasyWatch DVB-S light", BUDGET_KNC1S);
+MAKE_BUDGET_INFO(satewps, "Satelco EasyWatch DVB-S", BUDGET_KNC1S);
+MAKE_BUDGET_INFO(satewplc, "Satelco EasyWatch DVB-C", BUDGET_KNC1CP);
+MAKE_BUDGET_INFO(satewcmk3, "Satelco EasyWatch DVB-C MK3", BUDGET_KNC1C_MK3);
+MAKE_BUDGET_INFO(satewt, "Satelco EasyWatch DVB-T", BUDGET_KNC1T);
+MAKE_BUDGET_INFO(knc1sp, "KNC1 DVB-S Plus", BUDGET_KNC1SP);
+MAKE_BUDGET_INFO(knc1spx4, "KNC1 DVB-S Plus X4", BUDGET_KNC1SP);
+MAKE_BUDGET_INFO(knc1cp, "KNC1 DVB-C Plus", BUDGET_KNC1CP);
+MAKE_BUDGET_INFO(knc1cmk3, "KNC1 DVB-C MK3", BUDGET_KNC1C_MK3);
+MAKE_BUDGET_INFO(knc1ctda10024, "KNC1 DVB-C TDA10024", BUDGET_KNC1C_TDA10024);
+MAKE_BUDGET_INFO(knc1cpmk3, "KNC1 DVB-C Plus MK3", BUDGET_KNC1CP_MK3);
+MAKE_BUDGET_INFO(knc1tp, "KNC1 DVB-T Plus", BUDGET_KNC1TP);
+MAKE_BUDGET_INFO(cin1200s, "TerraTec Cinergy 1200 DVB-S", BUDGET_CIN1200S);
+MAKE_BUDGET_INFO(cin1200sn, "TerraTec Cinergy 1200 DVB-S", BUDGET_CIN1200S);
+MAKE_BUDGET_INFO(cin1200c, "Terratec Cinergy 1200 DVB-C", BUDGET_CIN1200C);
+MAKE_BUDGET_INFO(cin1200cmk3, "Terratec Cinergy 1200 DVB-C MK3", BUDGET_CIN1200C_MK3);
+MAKE_BUDGET_INFO(cin1200t, "Terratec Cinergy 1200 DVB-T", BUDGET_CIN1200T);
+
+static const struct pci_device_id pci_tbl[] = {
+ MAKE_EXTENSION_PCI(knc1s, 0x1131, 0x4f56),
+ MAKE_EXTENSION_PCI(knc1s, 0x1131, 0x0010),
+ MAKE_EXTENSION_PCI(knc1s, 0x1894, 0x0010),
+ MAKE_EXTENSION_PCI(knc1sp, 0x1131, 0x0011),
+ MAKE_EXTENSION_PCI(knc1sp, 0x1894, 0x0011),
+ MAKE_EXTENSION_PCI(kncxs, 0x1894, 0x0014),
+ MAKE_EXTENSION_PCI(knc1spx4, 0x1894, 0x0015),
+ MAKE_EXTENSION_PCI(kncxs, 0x1894, 0x0016),
+ MAKE_EXTENSION_PCI(knc1s2, 0x1894, 0x0018),
+ MAKE_EXTENSION_PCI(knc1s2, 0x1894, 0x0019),
+ MAKE_EXTENSION_PCI(sates2, 0x1894, 0x001d),
+ MAKE_EXTENSION_PCI(satewpls, 0x1894, 0x001e),
+ MAKE_EXTENSION_PCI(satewpls1, 0x1894, 0x001a),
+ MAKE_EXTENSION_PCI(satewps, 0x1894, 0x001b),
+ MAKE_EXTENSION_PCI(satewplc, 0x1894, 0x002a),
+ MAKE_EXTENSION_PCI(satewcmk3, 0x1894, 0x002c),
+ MAKE_EXTENSION_PCI(satewt, 0x1894, 0x003a),
+ MAKE_EXTENSION_PCI(knc1c, 0x1894, 0x0020),
+ MAKE_EXTENSION_PCI(knc1cp, 0x1894, 0x0021),
+ MAKE_EXTENSION_PCI(knc1cmk3, 0x1894, 0x0022),
+ MAKE_EXTENSION_PCI(knc1ctda10024, 0x1894, 0x0028),
+ MAKE_EXTENSION_PCI(knc1cpmk3, 0x1894, 0x0023),
+ MAKE_EXTENSION_PCI(knc1t, 0x1894, 0x0030),
+ MAKE_EXTENSION_PCI(knc1tp, 0x1894, 0x0031),
+ MAKE_EXTENSION_PCI(cin1200s, 0x153b, 0x1154),
+ MAKE_EXTENSION_PCI(cin1200sn, 0x153b, 0x1155),
+ MAKE_EXTENSION_PCI(cin1200c, 0x153b, 0x1156),
+ MAKE_EXTENSION_PCI(cin1200cmk3, 0x153b, 0x1176),
+ MAKE_EXTENSION_PCI(cin1200t, 0x153b, 0x1157),
+ {
+ .vendor = 0,
+ }
+};
+
+MODULE_DEVICE_TABLE(pci, pci_tbl);
+
+static struct saa7146_extension budget_extension = {
+ .name = "budget_av",
+ .flags = SAA7146_USE_I2C_IRQ,
+
+ .pci_tbl = pci_tbl,
+
+ .module = THIS_MODULE,
+ .attach = budget_av_attach,
+ .detach = budget_av_detach,
+
+ .irq_mask = MASK_10,
+ .irq_func = budget_av_irq,
+};
+
+static int __init budget_av_init(void)
+{
+ return saa7146_register_extension(&budget_extension);
+}
+
+static void __exit budget_av_exit(void)
+{
+ saa7146_unregister_extension(&budget_extension);
+}
+
+module_init(budget_av_init);
+module_exit(budget_av_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Ralph Metzler, Marcus Metzler, Michael Hunold, others");
+MODULE_DESCRIPTION("driver for the SAA7146 based so-called budget PCI DVB w/ analog input and CI-module (e.g. the KNC cards)");
diff --git a/drivers/media/pci/ttpci/budget-ci.c b/drivers/media/pci/ttpci/budget-ci.c
new file mode 100644
index 000000000000..d59d18647371
--- /dev/null
+++ b/drivers/media/pci/ttpci/budget-ci.c
@@ -0,0 +1,1574 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * budget-ci.c: driver for the SAA7146 based Budget DVB cards
+ *
+ * Compiled from various sources by Michael Hunold <michael@mihu.de>
+ *
+ * msp430 IR support contributed by Jack Thomasson <jkt@Helius.COM>
+ * partially based on the Siemens DVB driver by Ralph+Marcus Metzler
+ *
+ * CI interface support (c) 2004 Andrew de Quincey <adq_dvb@lidskialf.net>
+ *
+ * the project's page is at https://linuxtv.org
+ */
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <media/rc-core.h>
+
+#include "budget.h"
+
+#include <media/dvb_ca_en50221.h>
+#include "stv0299.h"
+#include "stv0297.h"
+#include "tda1004x.h"
+#include "stb0899_drv.h"
+#include "stb0899_reg.h"
+#include "stb0899_cfg.h"
+#include "stb6100.h"
+#include "stb6100_cfg.h"
+#include "lnbp21.h"
+#include "bsbe1.h"
+#include "bsru6.h"
+#include "tda1002x.h"
+#include "tda827x.h"
+#include "bsbe1-d01a.h"
+
+#define MODULE_NAME "budget_ci"
+
+/*
+ * Regarding DEBIADDR_IR:
+ * Some CI modules hang if random addresses are read.
+ * Using address 0x4000 for the IR read means that we
+ * use the same address as for CI version, which should
+ * be a safe default.
+ */
+#define DEBIADDR_IR 0x4000
+#define DEBIADDR_CICONTROL 0x0000
+#define DEBIADDR_CIVERSION 0x4000
+#define DEBIADDR_IO 0x1000
+#define DEBIADDR_ATTR 0x3000
+
+#define CICONTROL_RESET 0x01
+#define CICONTROL_ENABLETS 0x02
+#define CICONTROL_CAMDETECT 0x08
+
+#define DEBICICTL 0x00420000
+#define DEBICICAM 0x02420000
+
+#define SLOTSTATUS_NONE 1
+#define SLOTSTATUS_PRESENT 2
+#define SLOTSTATUS_RESET 4
+#define SLOTSTATUS_READY 8
+#define SLOTSTATUS_OCCUPIED (SLOTSTATUS_PRESENT|SLOTSTATUS_RESET|SLOTSTATUS_READY)
+
+/* RC5 device wildcard */
+#define IR_DEVICE_ANY 255
+
+static int rc5_device = -1;
+module_param(rc5_device, int, 0644);
+MODULE_PARM_DESC(rc5_device, "only IR commands to given RC5 device (device = 0 - 31, any device = 255, default: autodetect)");
+
+static int ir_debug;
+module_param(ir_debug, int, 0644);
+MODULE_PARM_DESC(ir_debug, "enable debugging information for IR decoding");
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+struct budget_ci_ir {
+ struct rc_dev *dev;
+ struct tasklet_struct msp430_irq_tasklet;
+ char name[72]; /* 40 + 32 for (struct saa7146_dev).name */
+ char phys[32];
+ int rc5_device;
+ u32 ir_key;
+ bool have_command;
+ bool full_rc5; /* Outputs a full RC5 code */
+};
+
+struct budget_ci {
+ struct budget budget;
+ struct tasklet_struct ciintf_irq_tasklet;
+ int slot_status;
+ int ci_irq;
+ struct dvb_ca_en50221 ca;
+ struct budget_ci_ir ir;
+ u8 tuner_pll_address; /* used for philips_tdm1316l configs */
+};
+
+static void msp430_ir_interrupt(struct tasklet_struct *t)
+{
+ struct budget_ci_ir *ir = from_tasklet(ir, t, msp430_irq_tasklet);
+ struct budget_ci *budget_ci = container_of(ir, typeof(*budget_ci), ir);
+ struct rc_dev *dev = budget_ci->ir.dev;
+ u32 command = ttpci_budget_debiread(&budget_ci->budget, DEBINOSWAP, DEBIADDR_IR, 2, 1, 0) >> 8;
+
+ /*
+ * The msp430 chip can generate two different bytes, command and device
+ *
+ * type1: X1CCCCCC, C = command bits (0 - 63)
+ * type2: X0TDDDDD, D = device bits (0 - 31), T = RC5 toggle bit
+ *
+ * Each signal from the remote control can generate one or more command
+ * bytes and one or more device bytes. For the repeated bytes, the
+ * highest bit (X) is set. The first command byte is always generated
+ * before the first device byte. Other than that, no specific order
+ * seems to apply. To make life interesting, bytes can also be lost.
+ *
+ * Only when we have a command and device byte, a keypress is
+ * generated.
+ */
+
+ if (ir_debug)
+ printk("budget_ci: received byte 0x%02x\n", command);
+
+ /* Remove repeat bit, we use every command */
+ command = command & 0x7f;
+
+ /* Is this a RC5 command byte? */
+ if (command & 0x40) {
+ budget_ci->ir.have_command = true;
+ budget_ci->ir.ir_key = command & 0x3f;
+ return;
+ }
+
+ /* It's a RC5 device byte */
+ if (!budget_ci->ir.have_command)
+ return;
+ budget_ci->ir.have_command = false;
+
+ if (budget_ci->ir.rc5_device != IR_DEVICE_ANY &&
+ budget_ci->ir.rc5_device != (command & 0x1f))
+ return;
+
+ if (budget_ci->ir.full_rc5) {
+ rc_keydown(dev, RC_PROTO_RC5,
+ RC_SCANCODE_RC5(budget_ci->ir.rc5_device, budget_ci->ir.ir_key),
+ !!(command & 0x20));
+ return;
+ }
+
+ /* FIXME: We should generate complete scancodes for all devices */
+ rc_keydown(dev, RC_PROTO_UNKNOWN, budget_ci->ir.ir_key,
+ !!(command & 0x20));
+}
+
+static int msp430_ir_init(struct budget_ci *budget_ci)
+{
+ struct saa7146_dev *saa = budget_ci->budget.dev;
+ struct rc_dev *dev;
+ int error;
+
+ dev = rc_allocate_device(RC_DRIVER_SCANCODE);
+ if (!dev) {
+ printk(KERN_ERR "budget_ci: IR interface initialisation failed\n");
+ return -ENOMEM;
+ }
+
+ snprintf(budget_ci->ir.name, sizeof(budget_ci->ir.name),
+ "Budget-CI dvb ir receiver %s", saa->name);
+ snprintf(budget_ci->ir.phys, sizeof(budget_ci->ir.phys),
+ "pci-%s/ir0", pci_name(saa->pci));
+
+ dev->driver_name = MODULE_NAME;
+ dev->device_name = budget_ci->ir.name;
+ dev->input_phys = budget_ci->ir.phys;
+ dev->input_id.bustype = BUS_PCI;
+ dev->input_id.version = 1;
+ if (saa->pci->subsystem_vendor) {
+ dev->input_id.vendor = saa->pci->subsystem_vendor;
+ dev->input_id.product = saa->pci->subsystem_device;
+ } else {
+ dev->input_id.vendor = saa->pci->vendor;
+ dev->input_id.product = saa->pci->device;
+ }
+ dev->dev.parent = &saa->pci->dev;
+
+ if (rc5_device < 0)
+ budget_ci->ir.rc5_device = IR_DEVICE_ANY;
+ else
+ budget_ci->ir.rc5_device = rc5_device;
+
+ /* Select keymap and address */
+ switch (budget_ci->budget.dev->pci->subsystem_device) {
+ case 0x100c:
+ case 0x100f:
+ case 0x1011:
+ case 0x1012:
+ /* The hauppauge keymap is a superset of these remotes */
+ dev->map_name = RC_MAP_HAUPPAUGE;
+ budget_ci->ir.full_rc5 = true;
+
+ if (rc5_device < 0)
+ budget_ci->ir.rc5_device = 0x1f;
+ break;
+ case 0x1010:
+ case 0x1017:
+ case 0x1019:
+ case 0x101a:
+ case 0x101b:
+ /* for the Technotrend 1500 bundled remote */
+ dev->map_name = RC_MAP_TT_1500;
+ break;
+ default:
+ /* unknown remote */
+ dev->map_name = RC_MAP_BUDGET_CI_OLD;
+ break;
+ }
+ if (!budget_ci->ir.full_rc5)
+ dev->scancode_mask = 0xff;
+
+ error = rc_register_device(dev);
+ if (error) {
+ printk(KERN_ERR "budget_ci: could not init driver for IR device (code %d)\n", error);
+ rc_free_device(dev);
+ return error;
+ }
+
+ budget_ci->ir.dev = dev;
+
+ tasklet_setup(&budget_ci->ir.msp430_irq_tasklet, msp430_ir_interrupt);
+
+ SAA7146_IER_ENABLE(saa, MASK_06);
+ saa7146_setgpio(saa, 3, SAA7146_GPIO_IRQHI);
+
+ return 0;
+}
+
+static void msp430_ir_deinit(struct budget_ci *budget_ci)
+{
+ struct saa7146_dev *saa = budget_ci->budget.dev;
+
+ SAA7146_IER_DISABLE(saa, MASK_06);
+ saa7146_setgpio(saa, 3, SAA7146_GPIO_INPUT);
+ tasklet_kill(&budget_ci->ir.msp430_irq_tasklet);
+
+ rc_unregister_device(budget_ci->ir.dev);
+}
+
+static int ciintf_read_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int address)
+{
+ struct budget_ci *budget_ci = (struct budget_ci *) ca->data;
+
+ if (slot != 0)
+ return -EINVAL;
+
+ return ttpci_budget_debiread(&budget_ci->budget, DEBICICAM,
+ DEBIADDR_ATTR | (address & 0xfff), 1, 1, 0);
+}
+
+static int ciintf_write_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int address, u8 value)
+{
+ struct budget_ci *budget_ci = (struct budget_ci *) ca->data;
+
+ if (slot != 0)
+ return -EINVAL;
+
+ return ttpci_budget_debiwrite(&budget_ci->budget, DEBICICAM,
+ DEBIADDR_ATTR | (address & 0xfff), 1, value, 1, 0);
+}
+
+static int ciintf_read_cam_control(struct dvb_ca_en50221 *ca, int slot, u8 address)
+{
+ struct budget_ci *budget_ci = (struct budget_ci *) ca->data;
+
+ if (slot != 0)
+ return -EINVAL;
+
+ return ttpci_budget_debiread(&budget_ci->budget, DEBICICAM,
+ DEBIADDR_IO | (address & 3), 1, 1, 0);
+}
+
+static int ciintf_write_cam_control(struct dvb_ca_en50221 *ca, int slot, u8 address, u8 value)
+{
+ struct budget_ci *budget_ci = (struct budget_ci *) ca->data;
+
+ if (slot != 0)
+ return -EINVAL;
+
+ return ttpci_budget_debiwrite(&budget_ci->budget, DEBICICAM,
+ DEBIADDR_IO | (address & 3), 1, value, 1, 0);
+}
+
+static int ciintf_slot_reset(struct dvb_ca_en50221 *ca, int slot)
+{
+ struct budget_ci *budget_ci = (struct budget_ci *) ca->data;
+ struct saa7146_dev *saa = budget_ci->budget.dev;
+
+ if (slot != 0)
+ return -EINVAL;
+
+ if (budget_ci->ci_irq) {
+ // trigger on RISING edge during reset so we know when READY is re-asserted
+ saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQHI);
+ }
+ budget_ci->slot_status = SLOTSTATUS_RESET;
+ ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, 0, 1, 0);
+ msleep(1);
+ ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1,
+ CICONTROL_RESET, 1, 0);
+
+ saa7146_setgpio(saa, 1, SAA7146_GPIO_OUTHI);
+ ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTB);
+ return 0;
+}
+
+static int ciintf_slot_shutdown(struct dvb_ca_en50221 *ca, int slot)
+{
+ struct budget_ci *budget_ci = (struct budget_ci *) ca->data;
+ struct saa7146_dev *saa = budget_ci->budget.dev;
+
+ if (slot != 0)
+ return -EINVAL;
+
+ saa7146_setgpio(saa, 1, SAA7146_GPIO_OUTHI);
+ ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTB);
+ return 0;
+}
+
+static int ciintf_slot_ts_enable(struct dvb_ca_en50221 *ca, int slot)
+{
+ struct budget_ci *budget_ci = (struct budget_ci *) ca->data;
+ struct saa7146_dev *saa = budget_ci->budget.dev;
+ int tmp;
+
+ if (slot != 0)
+ return -EINVAL;
+
+ saa7146_setgpio(saa, 1, SAA7146_GPIO_OUTLO);
+
+ tmp = ttpci_budget_debiread(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, 1, 0);
+ ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1,
+ tmp | CICONTROL_ENABLETS, 1, 0);
+
+ ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTA);
+ return 0;
+}
+
+static void ciintf_interrupt(struct tasklet_struct *t)
+{
+ struct budget_ci *budget_ci = from_tasklet(budget_ci, t,
+ ciintf_irq_tasklet);
+ struct saa7146_dev *saa = budget_ci->budget.dev;
+ unsigned int flags;
+
+ // ensure we don't get spurious IRQs during initialisation
+ if (!budget_ci->budget.ci_present)
+ return;
+
+ // read the CAM status
+ flags = ttpci_budget_debiread(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, 1, 0);
+ if (flags & CICONTROL_CAMDETECT) {
+
+ // GPIO should be set to trigger on falling edge if a CAM is present
+ saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQLO);
+
+ if (budget_ci->slot_status & SLOTSTATUS_NONE) {
+ // CAM insertion IRQ
+ budget_ci->slot_status = SLOTSTATUS_PRESENT;
+ dvb_ca_en50221_camchange_irq(&budget_ci->ca, 0,
+ DVB_CA_EN50221_CAMCHANGE_INSERTED);
+
+ } else if (budget_ci->slot_status & SLOTSTATUS_RESET) {
+ // CAM ready (reset completed)
+ budget_ci->slot_status = SLOTSTATUS_READY;
+ dvb_ca_en50221_camready_irq(&budget_ci->ca, 0);
+
+ } else if (budget_ci->slot_status & SLOTSTATUS_READY) {
+ // FR/DA IRQ
+ dvb_ca_en50221_frda_irq(&budget_ci->ca, 0);
+ }
+ } else {
+
+ // trigger on rising edge if a CAM is not present - when a CAM is inserted, we
+ // only want to get the IRQ when it sets READY. If we trigger on the falling edge,
+ // the CAM might not actually be ready yet.
+ saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQHI);
+
+ // generate a CAM removal IRQ if we haven't already
+ if (budget_ci->slot_status & SLOTSTATUS_OCCUPIED) {
+ // CAM removal IRQ
+ budget_ci->slot_status = SLOTSTATUS_NONE;
+ dvb_ca_en50221_camchange_irq(&budget_ci->ca, 0,
+ DVB_CA_EN50221_CAMCHANGE_REMOVED);
+ }
+ }
+}
+
+static int ciintf_poll_slot_status(struct dvb_ca_en50221 *ca, int slot, int open)
+{
+ struct budget_ci *budget_ci = (struct budget_ci *) ca->data;
+ unsigned int flags;
+
+ // ensure we don't get spurious IRQs during initialisation
+ if (!budget_ci->budget.ci_present)
+ return -EINVAL;
+
+ // read the CAM status
+ flags = ttpci_budget_debiread(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, 1, 0);
+ if (flags & CICONTROL_CAMDETECT) {
+ // mark it as present if it wasn't before
+ if (budget_ci->slot_status & SLOTSTATUS_NONE) {
+ budget_ci->slot_status = SLOTSTATUS_PRESENT;
+ }
+
+ // during a RESET, we check if we can read from IO memory to see when CAM is ready
+ if (budget_ci->slot_status & SLOTSTATUS_RESET) {
+ if (ciintf_read_attribute_mem(ca, slot, 0) == 0x1d) {
+ budget_ci->slot_status = SLOTSTATUS_READY;
+ }
+ }
+ } else {
+ budget_ci->slot_status = SLOTSTATUS_NONE;
+ }
+
+ if (budget_ci->slot_status != SLOTSTATUS_NONE) {
+ if (budget_ci->slot_status & SLOTSTATUS_READY) {
+ return DVB_CA_EN50221_POLL_CAM_PRESENT | DVB_CA_EN50221_POLL_CAM_READY;
+ }
+ return DVB_CA_EN50221_POLL_CAM_PRESENT;
+ }
+
+ return 0;
+}
+
+static int ciintf_init(struct budget_ci *budget_ci)
+{
+ struct saa7146_dev *saa = budget_ci->budget.dev;
+ int flags;
+ int result;
+ int ci_version;
+ int ca_flags;
+
+ memset(&budget_ci->ca, 0, sizeof(struct dvb_ca_en50221));
+
+ // enable DEBI pins
+ saa7146_write(saa, MC1, MASK_27 | MASK_11);
+
+ // test if it is there
+ ci_version = ttpci_budget_debiread(&budget_ci->budget, DEBICICTL, DEBIADDR_CIVERSION, 1, 1, 0);
+ if ((ci_version & 0xa0) != 0xa0) {
+ result = -ENODEV;
+ goto error;
+ }
+
+ // determine whether a CAM is present or not
+ flags = ttpci_budget_debiread(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, 1, 0);
+ budget_ci->slot_status = SLOTSTATUS_NONE;
+ if (flags & CICONTROL_CAMDETECT)
+ budget_ci->slot_status = SLOTSTATUS_PRESENT;
+
+ // version 0xa2 of the CI firmware doesn't generate interrupts
+ if (ci_version == 0xa2) {
+ ca_flags = 0;
+ budget_ci->ci_irq = 0;
+ } else {
+ ca_flags = DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE |
+ DVB_CA_EN50221_FLAG_IRQ_FR |
+ DVB_CA_EN50221_FLAG_IRQ_DA;
+ budget_ci->ci_irq = 1;
+ }
+
+ // register CI interface
+ budget_ci->ca.owner = THIS_MODULE;
+ budget_ci->ca.read_attribute_mem = ciintf_read_attribute_mem;
+ budget_ci->ca.write_attribute_mem = ciintf_write_attribute_mem;
+ budget_ci->ca.read_cam_control = ciintf_read_cam_control;
+ budget_ci->ca.write_cam_control = ciintf_write_cam_control;
+ budget_ci->ca.slot_reset = ciintf_slot_reset;
+ budget_ci->ca.slot_shutdown = ciintf_slot_shutdown;
+ budget_ci->ca.slot_ts_enable = ciintf_slot_ts_enable;
+ budget_ci->ca.poll_slot_status = ciintf_poll_slot_status;
+ budget_ci->ca.data = budget_ci;
+ if ((result = dvb_ca_en50221_init(&budget_ci->budget.dvb_adapter,
+ &budget_ci->ca,
+ ca_flags, 1)) != 0) {
+ printk("budget_ci: CI interface detected, but initialisation failed.\n");
+ goto error;
+ }
+
+ // Setup CI slot IRQ
+ if (budget_ci->ci_irq) {
+ tasklet_setup(&budget_ci->ciintf_irq_tasklet, ciintf_interrupt);
+ if (budget_ci->slot_status != SLOTSTATUS_NONE) {
+ saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQLO);
+ } else {
+ saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQHI);
+ }
+ SAA7146_IER_ENABLE(saa, MASK_03);
+ }
+
+ // enable interface
+ ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1,
+ CICONTROL_RESET, 1, 0);
+
+ // success!
+ printk("budget_ci: CI interface initialised\n");
+ budget_ci->budget.ci_present = 1;
+
+ // forge a fake CI IRQ so the CAM state is setup correctly
+ if (budget_ci->ci_irq) {
+ flags = DVB_CA_EN50221_CAMCHANGE_REMOVED;
+ if (budget_ci->slot_status != SLOTSTATUS_NONE)
+ flags = DVB_CA_EN50221_CAMCHANGE_INSERTED;
+ dvb_ca_en50221_camchange_irq(&budget_ci->ca, 0, flags);
+ }
+
+ return 0;
+
+error:
+ saa7146_write(saa, MC1, MASK_27);
+ return result;
+}
+
+static void ciintf_deinit(struct budget_ci *budget_ci)
+{
+ struct saa7146_dev *saa = budget_ci->budget.dev;
+
+ // disable CI interrupts
+ if (budget_ci->ci_irq) {
+ SAA7146_IER_DISABLE(saa, MASK_03);
+ saa7146_setgpio(saa, 0, SAA7146_GPIO_INPUT);
+ tasklet_kill(&budget_ci->ciintf_irq_tasklet);
+ }
+
+ // reset interface
+ ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, 0, 1, 0);
+ msleep(1);
+ ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1,
+ CICONTROL_RESET, 1, 0);
+
+ // disable TS data stream to CI interface
+ saa7146_setgpio(saa, 1, SAA7146_GPIO_INPUT);
+
+ // release the CA device
+ dvb_ca_en50221_release(&budget_ci->ca);
+
+ // disable DEBI pins
+ saa7146_write(saa, MC1, MASK_27);
+}
+
+static void budget_ci_irq(struct saa7146_dev *dev, u32 * isr)
+{
+ struct budget_ci *budget_ci = (struct budget_ci *) dev->ext_priv;
+
+ dprintk(8, "dev: %p, budget_ci: %p\n", dev, budget_ci);
+
+ if (*isr & MASK_06)
+ tasklet_schedule(&budget_ci->ir.msp430_irq_tasklet);
+
+ if (*isr & MASK_10)
+ ttpci_budget_irq10_handler(dev, isr);
+
+ if ((*isr & MASK_03) && (budget_ci->budget.ci_present) && (budget_ci->ci_irq))
+ tasklet_schedule(&budget_ci->ciintf_irq_tasklet);
+}
+
+static u8 philips_su1278_tt_inittab[] = {
+ 0x01, 0x0f,
+ 0x02, 0x30,
+ 0x03, 0x00,
+ 0x04, 0x5b,
+ 0x05, 0x85,
+ 0x06, 0x02,
+ 0x07, 0x00,
+ 0x08, 0x02,
+ 0x09, 0x00,
+ 0x0C, 0x01,
+ 0x0D, 0x81,
+ 0x0E, 0x44,
+ 0x0f, 0x14,
+ 0x10, 0x3c,
+ 0x11, 0x84,
+ 0x12, 0xda,
+ 0x13, 0x97,
+ 0x14, 0x95,
+ 0x15, 0xc9,
+ 0x16, 0x19,
+ 0x17, 0x8c,
+ 0x18, 0x59,
+ 0x19, 0xf8,
+ 0x1a, 0xfe,
+ 0x1c, 0x7f,
+ 0x1d, 0x00,
+ 0x1e, 0x00,
+ 0x1f, 0x50,
+ 0x20, 0x00,
+ 0x21, 0x00,
+ 0x22, 0x00,
+ 0x23, 0x00,
+ 0x28, 0x00,
+ 0x29, 0x28,
+ 0x2a, 0x14,
+ 0x2b, 0x0f,
+ 0x2c, 0x09,
+ 0x2d, 0x09,
+ 0x31, 0x1f,
+ 0x32, 0x19,
+ 0x33, 0xfc,
+ 0x34, 0x93,
+ 0xff, 0xff
+};
+
+static int philips_su1278_tt_set_symbol_rate(struct dvb_frontend *fe, u32 srate, u32 ratio)
+{
+ stv0299_writereg(fe, 0x0e, 0x44);
+ if (srate >= 10000000) {
+ stv0299_writereg(fe, 0x13, 0x97);
+ stv0299_writereg(fe, 0x14, 0x95);
+ stv0299_writereg(fe, 0x15, 0xc9);
+ stv0299_writereg(fe, 0x17, 0x8c);
+ stv0299_writereg(fe, 0x1a, 0xfe);
+ stv0299_writereg(fe, 0x1c, 0x7f);
+ stv0299_writereg(fe, 0x2d, 0x09);
+ } else {
+ stv0299_writereg(fe, 0x13, 0x99);
+ stv0299_writereg(fe, 0x14, 0x8d);
+ stv0299_writereg(fe, 0x15, 0xce);
+ stv0299_writereg(fe, 0x17, 0x43);
+ stv0299_writereg(fe, 0x1a, 0x1d);
+ stv0299_writereg(fe, 0x1c, 0x12);
+ stv0299_writereg(fe, 0x2d, 0x05);
+ }
+ stv0299_writereg(fe, 0x0e, 0x23);
+ stv0299_writereg(fe, 0x0f, 0x94);
+ stv0299_writereg(fe, 0x10, 0x39);
+ stv0299_writereg(fe, 0x15, 0xc9);
+
+ stv0299_writereg(fe, 0x1f, (ratio >> 16) & 0xff);
+ stv0299_writereg(fe, 0x20, (ratio >> 8) & 0xff);
+ stv0299_writereg(fe, 0x21, (ratio) & 0xf0);
+
+ return 0;
+}
+
+static int philips_su1278_tt_tuner_set_params(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+ struct budget_ci *budget_ci = (struct budget_ci *) fe->dvb->priv;
+ u32 div;
+ u8 buf[4];
+ struct i2c_msg msg = {.addr = 0x60,.flags = 0,.buf = buf,.len = sizeof(buf) };
+
+ if ((p->frequency < 950000) || (p->frequency > 2150000))
+ return -EINVAL;
+
+ div = (p->frequency + (500 - 1)) / 500; /* round correctly */
+ buf[0] = (div >> 8) & 0x7f;
+ buf[1] = div & 0xff;
+ buf[2] = 0x80 | ((div & 0x18000) >> 10) | 2;
+ buf[3] = 0x20;
+
+ if (p->symbol_rate < 4000000)
+ buf[3] |= 1;
+
+ if (p->frequency < 1250000)
+ buf[3] |= 0;
+ else if (p->frequency < 1550000)
+ buf[3] |= 0x40;
+ else if (p->frequency < 2050000)
+ buf[3] |= 0x80;
+ else if (p->frequency < 2150000)
+ buf[3] |= 0xC0;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ if (i2c_transfer(&budget_ci->budget.i2c_adap, &msg, 1) != 1)
+ return -EIO;
+ return 0;
+}
+
+static const struct stv0299_config philips_su1278_tt_config = {
+
+ .demod_address = 0x68,
+ .inittab = philips_su1278_tt_inittab,
+ .mclk = 64000000UL,
+ .invert = 0,
+ .skip_reinit = 1,
+ .lock_output = STV0299_LOCKOUTPUT_1,
+ .volt13_op0_op1 = STV0299_VOLT13_OP1,
+ .min_delay_ms = 50,
+ .set_symbol_rate = philips_su1278_tt_set_symbol_rate,
+};
+
+
+
+static int philips_tdm1316l_tuner_init(struct dvb_frontend *fe)
+{
+ struct budget_ci *budget_ci = (struct budget_ci *) fe->dvb->priv;
+ static u8 td1316_init[] = { 0x0b, 0xf5, 0x85, 0xab };
+ static u8 disable_mc44BC374c[] = { 0x1d, 0x74, 0xa0, 0x68 };
+ struct i2c_msg tuner_msg = {.addr = budget_ci->tuner_pll_address,.flags = 0,.buf = td1316_init,.len =
+ sizeof(td1316_init) };
+
+ // setup PLL configuration
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ if (i2c_transfer(&budget_ci->budget.i2c_adap, &tuner_msg, 1) != 1)
+ return -EIO;
+ msleep(1);
+
+ // disable the mc44BC374c (do not check for errors)
+ tuner_msg.addr = 0x65;
+ tuner_msg.buf = disable_mc44BC374c;
+ tuner_msg.len = sizeof(disable_mc44BC374c);
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ if (i2c_transfer(&budget_ci->budget.i2c_adap, &tuner_msg, 1) != 1) {
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ i2c_transfer(&budget_ci->budget.i2c_adap, &tuner_msg, 1);
+ }
+
+ return 0;
+}
+
+static int philips_tdm1316l_tuner_set_params(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+ struct budget_ci *budget_ci = (struct budget_ci *) fe->dvb->priv;
+ u8 tuner_buf[4];
+ struct i2c_msg tuner_msg = {.addr = budget_ci->tuner_pll_address,.flags = 0,.buf = tuner_buf,.len = sizeof(tuner_buf) };
+ int tuner_frequency = 0;
+ u8 band, cp, filter;
+
+ // determine charge pump
+ tuner_frequency = p->frequency + 36130000;
+ if (tuner_frequency < 87000000)
+ return -EINVAL;
+ else if (tuner_frequency < 130000000)
+ cp = 3;
+ else if (tuner_frequency < 160000000)
+ cp = 5;
+ else if (tuner_frequency < 200000000)
+ cp = 6;
+ else if (tuner_frequency < 290000000)
+ cp = 3;
+ else if (tuner_frequency < 420000000)
+ cp = 5;
+ else if (tuner_frequency < 480000000)
+ cp = 6;
+ else if (tuner_frequency < 620000000)
+ cp = 3;
+ else if (tuner_frequency < 830000000)
+ cp = 5;
+ else if (tuner_frequency < 895000000)
+ cp = 7;
+ else
+ return -EINVAL;
+
+ // determine band
+ if (p->frequency < 49000000)
+ return -EINVAL;
+ else if (p->frequency < 159000000)
+ band = 1;
+ else if (p->frequency < 444000000)
+ band = 2;
+ else if (p->frequency < 861000000)
+ band = 4;
+ else
+ return -EINVAL;
+
+ // setup PLL filter and TDA9889
+ switch (p->bandwidth_hz) {
+ case 6000000:
+ tda1004x_writereg(fe, 0x0C, 0x14);
+ filter = 0;
+ break;
+
+ case 7000000:
+ tda1004x_writereg(fe, 0x0C, 0x80);
+ filter = 0;
+ break;
+
+ case 8000000:
+ tda1004x_writereg(fe, 0x0C, 0x14);
+ filter = 1;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ // calculate divisor
+ // ((36130000+((1000000/6)/2)) + Finput)/(1000000/6)
+ tuner_frequency = (((p->frequency / 1000) * 6) + 217280) / 1000;
+
+ // setup tuner buffer
+ tuner_buf[0] = tuner_frequency >> 8;
+ tuner_buf[1] = tuner_frequency & 0xff;
+ tuner_buf[2] = 0xca;
+ tuner_buf[3] = (cp << 5) | (filter << 3) | band;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ if (i2c_transfer(&budget_ci->budget.i2c_adap, &tuner_msg, 1) != 1)
+ return -EIO;
+
+ msleep(1);
+ return 0;
+}
+
+static int philips_tdm1316l_request_firmware(struct dvb_frontend *fe,
+ const struct firmware **fw, char *name)
+{
+ struct budget_ci *budget_ci = (struct budget_ci *) fe->dvb->priv;
+
+ return request_firmware(fw, name, &budget_ci->budget.dev->pci->dev);
+}
+
+static struct tda1004x_config philips_tdm1316l_config = {
+
+ .demod_address = 0x8,
+ .invert = 0,
+ .invert_oclk = 0,
+ .xtal_freq = TDA10046_XTAL_4M,
+ .agc_config = TDA10046_AGC_DEFAULT,
+ .if_freq = TDA10046_FREQ_3617,
+ .request_firmware = philips_tdm1316l_request_firmware,
+};
+
+static struct tda1004x_config philips_tdm1316l_config_invert = {
+
+ .demod_address = 0x8,
+ .invert = 1,
+ .invert_oclk = 0,
+ .xtal_freq = TDA10046_XTAL_4M,
+ .agc_config = TDA10046_AGC_DEFAULT,
+ .if_freq = TDA10046_FREQ_3617,
+ .request_firmware = philips_tdm1316l_request_firmware,
+};
+
+static int dvbc_philips_tdm1316l_tuner_set_params(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+ struct budget_ci *budget_ci = (struct budget_ci *) fe->dvb->priv;
+ u8 tuner_buf[5];
+ struct i2c_msg tuner_msg = {.addr = budget_ci->tuner_pll_address,
+ .flags = 0,
+ .buf = tuner_buf,
+ .len = sizeof(tuner_buf) };
+ int tuner_frequency = 0;
+ u8 band, cp, filter;
+
+ // determine charge pump
+ tuner_frequency = p->frequency + 36125000;
+ if (tuner_frequency < 87000000)
+ return -EINVAL;
+ else if (tuner_frequency < 130000000) {
+ cp = 3;
+ band = 1;
+ } else if (tuner_frequency < 160000000) {
+ cp = 5;
+ band = 1;
+ } else if (tuner_frequency < 200000000) {
+ cp = 6;
+ band = 1;
+ } else if (tuner_frequency < 290000000) {
+ cp = 3;
+ band = 2;
+ } else if (tuner_frequency < 420000000) {
+ cp = 5;
+ band = 2;
+ } else if (tuner_frequency < 480000000) {
+ cp = 6;
+ band = 2;
+ } else if (tuner_frequency < 620000000) {
+ cp = 3;
+ band = 4;
+ } else if (tuner_frequency < 830000000) {
+ cp = 5;
+ band = 4;
+ } else if (tuner_frequency < 895000000) {
+ cp = 7;
+ band = 4;
+ } else
+ return -EINVAL;
+
+ // assume PLL filter should always be 8MHz for the moment.
+ filter = 1;
+
+ // calculate divisor
+ tuner_frequency = (p->frequency + 36125000 + (62500/2)) / 62500;
+
+ // setup tuner buffer
+ tuner_buf[0] = tuner_frequency >> 8;
+ tuner_buf[1] = tuner_frequency & 0xff;
+ tuner_buf[2] = 0xc8;
+ tuner_buf[3] = (cp << 5) | (filter << 3) | band;
+ tuner_buf[4] = 0x80;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ if (i2c_transfer(&budget_ci->budget.i2c_adap, &tuner_msg, 1) != 1)
+ return -EIO;
+
+ msleep(50);
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ if (i2c_transfer(&budget_ci->budget.i2c_adap, &tuner_msg, 1) != 1)
+ return -EIO;
+
+ msleep(1);
+
+ return 0;
+}
+
+static u8 dvbc_philips_tdm1316l_inittab[] = {
+ 0x80, 0x01,
+ 0x80, 0x00,
+ 0x81, 0x01,
+ 0x81, 0x00,
+ 0x00, 0x09,
+ 0x01, 0x69,
+ 0x03, 0x00,
+ 0x04, 0x00,
+ 0x07, 0x00,
+ 0x08, 0x00,
+ 0x20, 0x00,
+ 0x21, 0x40,
+ 0x22, 0x00,
+ 0x23, 0x00,
+ 0x24, 0x40,
+ 0x25, 0x88,
+ 0x30, 0xff,
+ 0x31, 0x00,
+ 0x32, 0xff,
+ 0x33, 0x00,
+ 0x34, 0x50,
+ 0x35, 0x7f,
+ 0x36, 0x00,
+ 0x37, 0x20,
+ 0x38, 0x00,
+ 0x40, 0x1c,
+ 0x41, 0xff,
+ 0x42, 0x29,
+ 0x43, 0x20,
+ 0x44, 0xff,
+ 0x45, 0x00,
+ 0x46, 0x00,
+ 0x49, 0x04,
+ 0x4a, 0x00,
+ 0x4b, 0x7b,
+ 0x52, 0x30,
+ 0x55, 0xae,
+ 0x56, 0x47,
+ 0x57, 0xe1,
+ 0x58, 0x3a,
+ 0x5a, 0x1e,
+ 0x5b, 0x34,
+ 0x60, 0x00,
+ 0x63, 0x00,
+ 0x64, 0x00,
+ 0x65, 0x00,
+ 0x66, 0x00,
+ 0x67, 0x00,
+ 0x68, 0x00,
+ 0x69, 0x00,
+ 0x6a, 0x02,
+ 0x6b, 0x00,
+ 0x70, 0xff,
+ 0x71, 0x00,
+ 0x72, 0x00,
+ 0x73, 0x00,
+ 0x74, 0x0c,
+ 0x80, 0x00,
+ 0x81, 0x00,
+ 0x82, 0x00,
+ 0x83, 0x00,
+ 0x84, 0x04,
+ 0x85, 0x80,
+ 0x86, 0x24,
+ 0x87, 0x78,
+ 0x88, 0x10,
+ 0x89, 0x00,
+ 0x90, 0x01,
+ 0x91, 0x01,
+ 0xa0, 0x04,
+ 0xa1, 0x00,
+ 0xa2, 0x00,
+ 0xb0, 0x91,
+ 0xb1, 0x0b,
+ 0xc0, 0x53,
+ 0xc1, 0x70,
+ 0xc2, 0x12,
+ 0xd0, 0x00,
+ 0xd1, 0x00,
+ 0xd2, 0x00,
+ 0xd3, 0x00,
+ 0xd4, 0x00,
+ 0xd5, 0x00,
+ 0xde, 0x00,
+ 0xdf, 0x00,
+ 0x61, 0x38,
+ 0x62, 0x0a,
+ 0x53, 0x13,
+ 0x59, 0x08,
+ 0xff, 0xff,
+};
+
+static struct stv0297_config dvbc_philips_tdm1316l_config = {
+ .demod_address = 0x1c,
+ .inittab = dvbc_philips_tdm1316l_inittab,
+ .invert = 0,
+ .stop_during_read = 1,
+};
+
+static struct tda10023_config tda10023_config = {
+ .demod_address = 0xc,
+ .invert = 0,
+ .xtal = 16000000,
+ .pll_m = 11,
+ .pll_p = 3,
+ .pll_n = 1,
+ .deltaf = 0xa511,
+};
+
+static struct tda827x_config tda827x_config = {
+ .config = 0,
+};
+
+/* TT S2-3200 DVB-S (STB0899) Inittab */
+static const struct stb0899_s1_reg tt3200_stb0899_s1_init_1[] = {
+
+ { STB0899_DEV_ID , 0x81 },
+ { STB0899_DISCNTRL1 , 0x32 },
+ { STB0899_DISCNTRL2 , 0x80 },
+ { STB0899_DISRX_ST0 , 0x04 },
+ { STB0899_DISRX_ST1 , 0x00 },
+ { STB0899_DISPARITY , 0x00 },
+ { STB0899_DISSTATUS , 0x20 },
+ { STB0899_DISF22 , 0x8c },
+ { STB0899_DISF22RX , 0x9a },
+ { STB0899_SYSREG , 0x0b },
+ { STB0899_ACRPRESC , 0x11 },
+ { STB0899_ACRDIV1 , 0x0a },
+ { STB0899_ACRDIV2 , 0x05 },
+ { STB0899_DACR1 , 0x00 },
+ { STB0899_DACR2 , 0x00 },
+ { STB0899_OUTCFG , 0x00 },
+ { STB0899_MODECFG , 0x00 },
+ { STB0899_IRQSTATUS_3 , 0x30 },
+ { STB0899_IRQSTATUS_2 , 0x00 },
+ { STB0899_IRQSTATUS_1 , 0x00 },
+ { STB0899_IRQSTATUS_0 , 0x00 },
+ { STB0899_IRQMSK_3 , 0xf3 },
+ { STB0899_IRQMSK_2 , 0xfc },
+ { STB0899_IRQMSK_1 , 0xff },
+ { STB0899_IRQMSK_0 , 0xff },
+ { STB0899_IRQCFG , 0x00 },
+ { STB0899_I2CCFG , 0x88 },
+ { STB0899_I2CRPT , 0x48 }, /* 12k Pullup, Repeater=16, Stop=disabled */
+ { STB0899_IOPVALUE5 , 0x00 },
+ { STB0899_IOPVALUE4 , 0x20 },
+ { STB0899_IOPVALUE3 , 0xc9 },
+ { STB0899_IOPVALUE2 , 0x90 },
+ { STB0899_IOPVALUE1 , 0x40 },
+ { STB0899_IOPVALUE0 , 0x00 },
+ { STB0899_GPIO00CFG , 0x82 },
+ { STB0899_GPIO01CFG , 0x82 },
+ { STB0899_GPIO02CFG , 0x82 },
+ { STB0899_GPIO03CFG , 0x82 },
+ { STB0899_GPIO04CFG , 0x82 },
+ { STB0899_GPIO05CFG , 0x82 },
+ { STB0899_GPIO06CFG , 0x82 },
+ { STB0899_GPIO07CFG , 0x82 },
+ { STB0899_GPIO08CFG , 0x82 },
+ { STB0899_GPIO09CFG , 0x82 },
+ { STB0899_GPIO10CFG , 0x82 },
+ { STB0899_GPIO11CFG , 0x82 },
+ { STB0899_GPIO12CFG , 0x82 },
+ { STB0899_GPIO13CFG , 0x82 },
+ { STB0899_GPIO14CFG , 0x82 },
+ { STB0899_GPIO15CFG , 0x82 },
+ { STB0899_GPIO16CFG , 0x82 },
+ { STB0899_GPIO17CFG , 0x82 },
+ { STB0899_GPIO18CFG , 0x82 },
+ { STB0899_GPIO19CFG , 0x82 },
+ { STB0899_GPIO20CFG , 0x82 },
+ { STB0899_SDATCFG , 0xb8 },
+ { STB0899_SCLTCFG , 0xba },
+ { STB0899_AGCRFCFG , 0x1c }, /* 0x11 */
+ { STB0899_GPIO22 , 0x82 }, /* AGCBB2CFG */
+ { STB0899_GPIO21 , 0x91 }, /* AGCBB1CFG */
+ { STB0899_DIRCLKCFG , 0x82 },
+ { STB0899_CLKOUT27CFG , 0x7e },
+ { STB0899_STDBYCFG , 0x82 },
+ { STB0899_CS0CFG , 0x82 },
+ { STB0899_CS1CFG , 0x82 },
+ { STB0899_DISEQCOCFG , 0x20 },
+ { STB0899_GPIO32CFG , 0x82 },
+ { STB0899_GPIO33CFG , 0x82 },
+ { STB0899_GPIO34CFG , 0x82 },
+ { STB0899_GPIO35CFG , 0x82 },
+ { STB0899_GPIO36CFG , 0x82 },
+ { STB0899_GPIO37CFG , 0x82 },
+ { STB0899_GPIO38CFG , 0x82 },
+ { STB0899_GPIO39CFG , 0x82 },
+ { STB0899_NCOARSE , 0x15 }, /* 0x15 = 27 Mhz Clock, F/3 = 198MHz, F/6 = 99MHz */
+ { STB0899_SYNTCTRL , 0x02 }, /* 0x00 = CLK from CLKI, 0x02 = CLK from XTALI */
+ { STB0899_FILTCTRL , 0x00 },
+ { STB0899_SYSCTRL , 0x00 },
+ { STB0899_STOPCLK1 , 0x20 },
+ { STB0899_STOPCLK2 , 0x00 },
+ { STB0899_INTBUFSTATUS , 0x00 },
+ { STB0899_INTBUFCTRL , 0x0a },
+ { 0xffff , 0xff },
+};
+
+static const struct stb0899_s1_reg tt3200_stb0899_s1_init_3[] = {
+ { STB0899_DEMOD , 0x00 },
+ { STB0899_RCOMPC , 0xc9 },
+ { STB0899_AGC1CN , 0x41 },
+ { STB0899_AGC1REF , 0x10 },
+ { STB0899_RTC , 0x7a },
+ { STB0899_TMGCFG , 0x4e },
+ { STB0899_AGC2REF , 0x34 },
+ { STB0899_TLSR , 0x84 },
+ { STB0899_CFD , 0xc7 },
+ { STB0899_ACLC , 0x87 },
+ { STB0899_BCLC , 0x94 },
+ { STB0899_EQON , 0x41 },
+ { STB0899_LDT , 0xdd },
+ { STB0899_LDT2 , 0xc9 },
+ { STB0899_EQUALREF , 0xb4 },
+ { STB0899_TMGRAMP , 0x10 },
+ { STB0899_TMGTHD , 0x30 },
+ { STB0899_IDCCOMP , 0xfb },
+ { STB0899_QDCCOMP , 0x03 },
+ { STB0899_POWERI , 0x3b },
+ { STB0899_POWERQ , 0x3d },
+ { STB0899_RCOMP , 0x81 },
+ { STB0899_AGCIQIN , 0x80 },
+ { STB0899_AGC2I1 , 0x04 },
+ { STB0899_AGC2I2 , 0xf5 },
+ { STB0899_TLIR , 0x25 },
+ { STB0899_RTF , 0x80 },
+ { STB0899_DSTATUS , 0x00 },
+ { STB0899_LDI , 0xca },
+ { STB0899_CFRM , 0xf1 },
+ { STB0899_CFRL , 0xf3 },
+ { STB0899_NIRM , 0x2a },
+ { STB0899_NIRL , 0x05 },
+ { STB0899_ISYMB , 0x17 },
+ { STB0899_QSYMB , 0xfa },
+ { STB0899_SFRH , 0x2f },
+ { STB0899_SFRM , 0x68 },
+ { STB0899_SFRL , 0x40 },
+ { STB0899_SFRUPH , 0x2f },
+ { STB0899_SFRUPM , 0x68 },
+ { STB0899_SFRUPL , 0x40 },
+ { STB0899_EQUAI1 , 0xfd },
+ { STB0899_EQUAQ1 , 0x04 },
+ { STB0899_EQUAI2 , 0x0f },
+ { STB0899_EQUAQ2 , 0xff },
+ { STB0899_EQUAI3 , 0xdf },
+ { STB0899_EQUAQ3 , 0xfa },
+ { STB0899_EQUAI4 , 0x37 },
+ { STB0899_EQUAQ4 , 0x0d },
+ { STB0899_EQUAI5 , 0xbd },
+ { STB0899_EQUAQ5 , 0xf7 },
+ { STB0899_DSTATUS2 , 0x00 },
+ { STB0899_VSTATUS , 0x00 },
+ { STB0899_VERROR , 0xff },
+ { STB0899_IQSWAP , 0x2a },
+ { STB0899_ECNT1M , 0x00 },
+ { STB0899_ECNT1L , 0x00 },
+ { STB0899_ECNT2M , 0x00 },
+ { STB0899_ECNT2L , 0x00 },
+ { STB0899_ECNT3M , 0x00 },
+ { STB0899_ECNT3L , 0x00 },
+ { STB0899_FECAUTO1 , 0x06 },
+ { STB0899_FECM , 0x01 },
+ { STB0899_VTH12 , 0xf0 },
+ { STB0899_VTH23 , 0xa0 },
+ { STB0899_VTH34 , 0x78 },
+ { STB0899_VTH56 , 0x4e },
+ { STB0899_VTH67 , 0x48 },
+ { STB0899_VTH78 , 0x38 },
+ { STB0899_PRVIT , 0xff },
+ { STB0899_VITSYNC , 0x19 },
+ { STB0899_RSULC , 0xb1 }, /* DVB = 0xb1, DSS = 0xa1 */
+ { STB0899_TSULC , 0x42 },
+ { STB0899_RSLLC , 0x40 },
+ { STB0899_TSLPL , 0x12 },
+ { STB0899_TSCFGH , 0x0c },
+ { STB0899_TSCFGM , 0x00 },
+ { STB0899_TSCFGL , 0x0c },
+ { STB0899_TSOUT , 0x4d }, /* 0x0d for CAM */
+ { STB0899_RSSYNCDEL , 0x00 },
+ { STB0899_TSINHDELH , 0x02 },
+ { STB0899_TSINHDELM , 0x00 },
+ { STB0899_TSINHDELL , 0x00 },
+ { STB0899_TSLLSTKM , 0x00 },
+ { STB0899_TSLLSTKL , 0x00 },
+ { STB0899_TSULSTKM , 0x00 },
+ { STB0899_TSULSTKL , 0xab },
+ { STB0899_PCKLENUL , 0x00 },
+ { STB0899_PCKLENLL , 0xcc },
+ { STB0899_RSPCKLEN , 0xcc },
+ { STB0899_TSSTATUS , 0x80 },
+ { STB0899_ERRCTRL1 , 0xb6 },
+ { STB0899_ERRCTRL2 , 0x96 },
+ { STB0899_ERRCTRL3 , 0x89 },
+ { STB0899_DMONMSK1 , 0x27 },
+ { STB0899_DMONMSK0 , 0x03 },
+ { STB0899_DEMAPVIT , 0x5c },
+ { STB0899_PLPARM , 0x1f },
+ { STB0899_PDELCTRL , 0x48 },
+ { STB0899_PDELCTRL2 , 0x00 },
+ { STB0899_BBHCTRL1 , 0x00 },
+ { STB0899_BBHCTRL2 , 0x00 },
+ { STB0899_HYSTTHRESH , 0x77 },
+ { STB0899_MATCSTM , 0x00 },
+ { STB0899_MATCSTL , 0x00 },
+ { STB0899_UPLCSTM , 0x00 },
+ { STB0899_UPLCSTL , 0x00 },
+ { STB0899_DFLCSTM , 0x00 },
+ { STB0899_DFLCSTL , 0x00 },
+ { STB0899_SYNCCST , 0x00 },
+ { STB0899_SYNCDCSTM , 0x00 },
+ { STB0899_SYNCDCSTL , 0x00 },
+ { STB0899_ISI_ENTRY , 0x00 },
+ { STB0899_ISI_BIT_EN , 0x00 },
+ { STB0899_MATSTRM , 0x00 },
+ { STB0899_MATSTRL , 0x00 },
+ { STB0899_UPLSTRM , 0x00 },
+ { STB0899_UPLSTRL , 0x00 },
+ { STB0899_DFLSTRM , 0x00 },
+ { STB0899_DFLSTRL , 0x00 },
+ { STB0899_SYNCSTR , 0x00 },
+ { STB0899_SYNCDSTRM , 0x00 },
+ { STB0899_SYNCDSTRL , 0x00 },
+ { STB0899_CFGPDELSTATUS1 , 0x10 },
+ { STB0899_CFGPDELSTATUS2 , 0x00 },
+ { STB0899_BBFERRORM , 0x00 },
+ { STB0899_BBFERRORL , 0x00 },
+ { STB0899_UPKTERRORM , 0x00 },
+ { STB0899_UPKTERRORL , 0x00 },
+ { 0xffff , 0xff },
+};
+
+static struct stb0899_config tt3200_config = {
+ .init_dev = tt3200_stb0899_s1_init_1,
+ .init_s2_demod = stb0899_s2_init_2,
+ .init_s1_demod = tt3200_stb0899_s1_init_3,
+ .init_s2_fec = stb0899_s2_init_4,
+ .init_tst = stb0899_s1_init_5,
+
+ .postproc = NULL,
+
+ .demod_address = 0x68,
+
+ .xtal_freq = 27000000,
+ .inversion = IQ_SWAP_ON,
+
+ .lo_clk = 76500000,
+ .hi_clk = 99000000,
+
+ .esno_ave = STB0899_DVBS2_ESNO_AVE,
+ .esno_quant = STB0899_DVBS2_ESNO_QUANT,
+ .avframes_coarse = STB0899_DVBS2_AVFRAMES_COARSE,
+ .avframes_fine = STB0899_DVBS2_AVFRAMES_FINE,
+ .miss_threshold = STB0899_DVBS2_MISS_THRESHOLD,
+ .uwp_threshold_acq = STB0899_DVBS2_UWP_THRESHOLD_ACQ,
+ .uwp_threshold_track = STB0899_DVBS2_UWP_THRESHOLD_TRACK,
+ .uwp_threshold_sof = STB0899_DVBS2_UWP_THRESHOLD_SOF,
+ .sof_search_timeout = STB0899_DVBS2_SOF_SEARCH_TIMEOUT,
+
+ .btr_nco_bits = STB0899_DVBS2_BTR_NCO_BITS,
+ .btr_gain_shift_offset = STB0899_DVBS2_BTR_GAIN_SHIFT_OFFSET,
+ .crl_nco_bits = STB0899_DVBS2_CRL_NCO_BITS,
+ .ldpc_max_iter = STB0899_DVBS2_LDPC_MAX_ITER,
+
+ .tuner_get_frequency = stb6100_get_frequency,
+ .tuner_set_frequency = stb6100_set_frequency,
+ .tuner_set_bandwidth = stb6100_set_bandwidth,
+ .tuner_get_bandwidth = stb6100_get_bandwidth,
+ .tuner_set_rfsiggain = NULL
+};
+
+static struct stb6100_config tt3200_stb6100_config = {
+ .tuner_address = 0x60,
+ .refclock = 27000000,
+};
+
+static void frontend_init(struct budget_ci *budget_ci)
+{
+ switch (budget_ci->budget.dev->pci->subsystem_device) {
+ case 0x100c: // Hauppauge/TT Nova-CI budget (stv0299/ALPS BSRU6(tsa5059))
+ budget_ci->budget.dvb_frontend =
+ dvb_attach(stv0299_attach, &alps_bsru6_config, &budget_ci->budget.i2c_adap);
+ if (budget_ci->budget.dvb_frontend) {
+ budget_ci->budget.dvb_frontend->ops.tuner_ops.set_params = alps_bsru6_tuner_set_params;
+ budget_ci->budget.dvb_frontend->tuner_priv = &budget_ci->budget.i2c_adap;
+ break;
+ }
+ break;
+
+ case 0x100f: // Hauppauge/TT Nova-CI budget (stv0299b/Philips su1278(tsa5059))
+ budget_ci->budget.dvb_frontend =
+ dvb_attach(stv0299_attach, &philips_su1278_tt_config, &budget_ci->budget.i2c_adap);
+ if (budget_ci->budget.dvb_frontend) {
+ budget_ci->budget.dvb_frontend->ops.tuner_ops.set_params = philips_su1278_tt_tuner_set_params;
+ break;
+ }
+ break;
+
+ case 0x1010: // TT DVB-C CI budget (stv0297/Philips tdm1316l(tda6651tt))
+ budget_ci->tuner_pll_address = 0x61;
+ budget_ci->budget.dvb_frontend =
+ dvb_attach(stv0297_attach, &dvbc_philips_tdm1316l_config, &budget_ci->budget.i2c_adap);
+ if (budget_ci->budget.dvb_frontend) {
+ budget_ci->budget.dvb_frontend->ops.tuner_ops.set_params = dvbc_philips_tdm1316l_tuner_set_params;
+ break;
+ }
+ break;
+
+ case 0x1011: // Hauppauge/TT Nova-T budget (tda10045/Philips tdm1316l(tda6651tt) + TDA9889)
+ budget_ci->tuner_pll_address = 0x63;
+ budget_ci->budget.dvb_frontend =
+ dvb_attach(tda10045_attach, &philips_tdm1316l_config, &budget_ci->budget.i2c_adap);
+ if (budget_ci->budget.dvb_frontend) {
+ budget_ci->budget.dvb_frontend->ops.tuner_ops.init = philips_tdm1316l_tuner_init;
+ budget_ci->budget.dvb_frontend->ops.tuner_ops.set_params = philips_tdm1316l_tuner_set_params;
+ break;
+ }
+ break;
+
+ case 0x1012: // TT DVB-T CI budget (tda10046/Philips tdm1316l(tda6651tt))
+ budget_ci->tuner_pll_address = 0x60;
+ budget_ci->budget.dvb_frontend =
+ dvb_attach(tda10046_attach, &philips_tdm1316l_config_invert, &budget_ci->budget.i2c_adap);
+ if (budget_ci->budget.dvb_frontend) {
+ budget_ci->budget.dvb_frontend->ops.tuner_ops.init = philips_tdm1316l_tuner_init;
+ budget_ci->budget.dvb_frontend->ops.tuner_ops.set_params = philips_tdm1316l_tuner_set_params;
+ break;
+ }
+ break;
+
+ case 0x1017: // TT S-1500 PCI
+ budget_ci->budget.dvb_frontend = dvb_attach(stv0299_attach, &alps_bsbe1_config, &budget_ci->budget.i2c_adap);
+ if (budget_ci->budget.dvb_frontend) {
+ budget_ci->budget.dvb_frontend->ops.tuner_ops.set_params = alps_bsbe1_tuner_set_params;
+ budget_ci->budget.dvb_frontend->tuner_priv = &budget_ci->budget.i2c_adap;
+
+ budget_ci->budget.dvb_frontend->ops.dishnetwork_send_legacy_command = NULL;
+ if (dvb_attach(lnbp21_attach, budget_ci->budget.dvb_frontend, &budget_ci->budget.i2c_adap, LNBP21_LLC, 0) == NULL) {
+ printk("%s: No LNBP21 found!\n", __func__);
+ dvb_frontend_detach(budget_ci->budget.dvb_frontend);
+ budget_ci->budget.dvb_frontend = NULL;
+ }
+ }
+ break;
+
+ case 0x101a: /* TT Budget-C-1501 (philips tda10023/philips tda8274A) */
+ budget_ci->budget.dvb_frontend = dvb_attach(tda10023_attach, &tda10023_config, &budget_ci->budget.i2c_adap, 0x48);
+ if (budget_ci->budget.dvb_frontend) {
+ if (dvb_attach(tda827x_attach, budget_ci->budget.dvb_frontend, 0x61, &budget_ci->budget.i2c_adap, &tda827x_config) == NULL) {
+ printk(KERN_ERR "%s: No tda827x found!\n", __func__);
+ dvb_frontend_detach(budget_ci->budget.dvb_frontend);
+ budget_ci->budget.dvb_frontend = NULL;
+ }
+ }
+ break;
+
+ case 0x101b: /* TT S-1500B (BSBE1-D01A - STV0288/STB6000/LNBP21) */
+ budget_ci->budget.dvb_frontend = dvb_attach(stv0288_attach, &stv0288_bsbe1_d01a_config, &budget_ci->budget.i2c_adap);
+ if (budget_ci->budget.dvb_frontend) {
+ if (dvb_attach(stb6000_attach, budget_ci->budget.dvb_frontend, 0x63, &budget_ci->budget.i2c_adap)) {
+ if (!dvb_attach(lnbp21_attach, budget_ci->budget.dvb_frontend, &budget_ci->budget.i2c_adap, 0, 0)) {
+ printk(KERN_ERR "%s: No LNBP21 found!\n", __func__);
+ dvb_frontend_detach(budget_ci->budget.dvb_frontend);
+ budget_ci->budget.dvb_frontend = NULL;
+ }
+ } else {
+ printk(KERN_ERR "%s: No STB6000 found!\n", __func__);
+ dvb_frontend_detach(budget_ci->budget.dvb_frontend);
+ budget_ci->budget.dvb_frontend = NULL;
+ }
+ }
+ break;
+
+ case 0x1019: // TT S2-3200 PCI
+ /*
+ * NOTE! on some STB0899 versions, the internal PLL takes a longer time
+ * to settle, aka LOCK. On the older revisions of the chip, we don't see
+ * this, as a result on the newer chips the entire clock tree, will not
+ * be stable after a freshly POWER 'ed up situation.
+ * In this case, we should RESET the STB0899 (Active LOW) and wait for
+ * PLL stabilization.
+ *
+ * On the TT S2 3200 and clones, the STB0899 demodulator's RESETB is
+ * connected to the SAA7146 GPIO, GPIO2, Pin 142
+ */
+ /* Reset Demodulator */
+ saa7146_setgpio(budget_ci->budget.dev, 2, SAA7146_GPIO_OUTLO);
+ /* Wait for everything to die */
+ msleep(50);
+ /* Pull it up out of Reset state */
+ saa7146_setgpio(budget_ci->budget.dev, 2, SAA7146_GPIO_OUTHI);
+ /* Wait for PLL to stabilize */
+ msleep(250);
+ /*
+ * PLL state should be stable now. Ideally, we should check
+ * for PLL LOCK status. But well, never mind!
+ */
+ budget_ci->budget.dvb_frontend = dvb_attach(stb0899_attach, &tt3200_config, &budget_ci->budget.i2c_adap);
+ if (budget_ci->budget.dvb_frontend) {
+ if (dvb_attach(stb6100_attach, budget_ci->budget.dvb_frontend, &tt3200_stb6100_config, &budget_ci->budget.i2c_adap)) {
+ if (!dvb_attach(lnbp21_attach, budget_ci->budget.dvb_frontend, &budget_ci->budget.i2c_adap, 0, 0)) {
+ printk("%s: No LNBP21 found!\n", __func__);
+ dvb_frontend_detach(budget_ci->budget.dvb_frontend);
+ budget_ci->budget.dvb_frontend = NULL;
+ }
+ } else {
+ dvb_frontend_detach(budget_ci->budget.dvb_frontend);
+ budget_ci->budget.dvb_frontend = NULL;
+ }
+ }
+ break;
+
+ }
+
+ if (budget_ci->budget.dvb_frontend == NULL) {
+ printk("budget-ci: A frontend driver was not found for device [%04x:%04x] subsystem [%04x:%04x]\n",
+ budget_ci->budget.dev->pci->vendor,
+ budget_ci->budget.dev->pci->device,
+ budget_ci->budget.dev->pci->subsystem_vendor,
+ budget_ci->budget.dev->pci->subsystem_device);
+ } else {
+ if (dvb_register_frontend
+ (&budget_ci->budget.dvb_adapter, budget_ci->budget.dvb_frontend)) {
+ printk("budget-ci: Frontend registration failed!\n");
+ dvb_frontend_detach(budget_ci->budget.dvb_frontend);
+ budget_ci->budget.dvb_frontend = NULL;
+ }
+ }
+}
+
+static int budget_ci_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data *info)
+{
+ struct budget_ci *budget_ci;
+ int err;
+
+ budget_ci = kzalloc(sizeof(struct budget_ci), GFP_KERNEL);
+ if (!budget_ci) {
+ err = -ENOMEM;
+ goto out1;
+ }
+
+ dprintk(2, "budget_ci: %p\n", budget_ci);
+
+ dev->ext_priv = budget_ci;
+
+ err = ttpci_budget_init(&budget_ci->budget, dev, info, THIS_MODULE,
+ adapter_nr);
+ if (err)
+ goto out2;
+
+ err = msp430_ir_init(budget_ci);
+ if (err)
+ goto out3;
+
+ ciintf_init(budget_ci);
+
+ budget_ci->budget.dvb_adapter.priv = budget_ci;
+ frontend_init(budget_ci);
+
+ ttpci_budget_init_hooks(&budget_ci->budget);
+
+ return 0;
+
+out3:
+ ttpci_budget_deinit(&budget_ci->budget);
+out2:
+ kfree(budget_ci);
+out1:
+ return err;
+}
+
+static int budget_ci_detach(struct saa7146_dev *dev)
+{
+ struct budget_ci *budget_ci = (struct budget_ci *) dev->ext_priv;
+ struct saa7146_dev *saa = budget_ci->budget.dev;
+ int err;
+
+ if (budget_ci->budget.ci_present)
+ ciintf_deinit(budget_ci);
+ msp430_ir_deinit(budget_ci);
+ if (budget_ci->budget.dvb_frontend) {
+ dvb_unregister_frontend(budget_ci->budget.dvb_frontend);
+ dvb_frontend_detach(budget_ci->budget.dvb_frontend);
+ }
+ err = ttpci_budget_deinit(&budget_ci->budget);
+
+ // disable frontend and CI interface
+ saa7146_setgpio(saa, 2, SAA7146_GPIO_INPUT);
+
+ kfree(budget_ci);
+
+ return err;
+}
+
+static struct saa7146_extension budget_extension;
+
+MAKE_BUDGET_INFO(ttbs2, "TT-Budget/S-1500 PCI", BUDGET_TT);
+MAKE_BUDGET_INFO(ttbci, "TT-Budget/WinTV-NOVA-CI PCI", BUDGET_TT_HW_DISEQC);
+MAKE_BUDGET_INFO(ttbt2, "TT-Budget/WinTV-NOVA-T PCI", BUDGET_TT);
+MAKE_BUDGET_INFO(ttbtci, "TT-Budget-T-CI PCI", BUDGET_TT);
+MAKE_BUDGET_INFO(ttbcci, "TT-Budget-C-CI PCI", BUDGET_TT);
+MAKE_BUDGET_INFO(ttc1501, "TT-Budget C-1501 PCI", BUDGET_TT);
+MAKE_BUDGET_INFO(tt3200, "TT-Budget S2-3200 PCI", BUDGET_TT);
+MAKE_BUDGET_INFO(ttbs1500b, "TT-Budget S-1500B PCI", BUDGET_TT);
+
+static const struct pci_device_id pci_tbl[] = {
+ MAKE_EXTENSION_PCI(ttbci, 0x13c2, 0x100c),
+ MAKE_EXTENSION_PCI(ttbci, 0x13c2, 0x100f),
+ MAKE_EXTENSION_PCI(ttbcci, 0x13c2, 0x1010),
+ MAKE_EXTENSION_PCI(ttbt2, 0x13c2, 0x1011),
+ MAKE_EXTENSION_PCI(ttbtci, 0x13c2, 0x1012),
+ MAKE_EXTENSION_PCI(ttbs2, 0x13c2, 0x1017),
+ MAKE_EXTENSION_PCI(ttc1501, 0x13c2, 0x101a),
+ MAKE_EXTENSION_PCI(tt3200, 0x13c2, 0x1019),
+ MAKE_EXTENSION_PCI(ttbs1500b, 0x13c2, 0x101b),
+ {
+ .vendor = 0,
+ }
+};
+
+MODULE_DEVICE_TABLE(pci, pci_tbl);
+
+static struct saa7146_extension budget_extension = {
+ .name = "budget_ci dvb",
+ .flags = SAA7146_USE_I2C_IRQ,
+
+ .module = THIS_MODULE,
+ .pci_tbl = &pci_tbl[0],
+ .attach = budget_ci_attach,
+ .detach = budget_ci_detach,
+
+ .irq_mask = MASK_03 | MASK_06 | MASK_10,
+ .irq_func = budget_ci_irq,
+};
+
+static int __init budget_ci_init(void)
+{
+ return saa7146_register_extension(&budget_extension);
+}
+
+static void __exit budget_ci_exit(void)
+{
+ saa7146_unregister_extension(&budget_extension);
+}
+
+module_init(budget_ci_init);
+module_exit(budget_ci_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Michael Hunold, Jack Thomasson, Andrew de Quincey, others");
+MODULE_DESCRIPTION("driver for the SAA7146 based so-called budget PCI DVB cards w/ CI-module produced by Siemens, Technotrend, Hauppauge");
diff --git a/drivers/media/pci/ttpci/budget-core.c b/drivers/media/pci/ttpci/budget-core.c
new file mode 100644
index 000000000000..5d5796f24469
--- /dev/null
+++ b/drivers/media/pci/ttpci/budget-core.c
@@ -0,0 +1,603 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * budget-core.c: driver for the SAA7146 based Budget DVB cards
+ *
+ * Compiled from various sources by Michael Hunold <michael@mihu.de>
+ *
+ * Copyright (C) 2002 Ralph Metzler <rjkm@metzlerbros.de>
+ *
+ * Copyright (C) 1999-2002 Ralph Metzler
+ * & Marcus Metzler for convergence integrated media GmbH
+ *
+ * 26feb2004 Support for FS Activy Card (Grundig tuner) by
+ * Michael Dreher <michael@5dot1.de>,
+ * Oliver Endriss <o.endriss@gmx.de>,
+ * Andreas 'randy' Weinberger
+ *
+ * the project's page is at https://linuxtv.org
+ */
+
+
+#include "budget.h"
+#include "ttpci-eeprom.h"
+
+#define TS_WIDTH (2 * TS_SIZE)
+#define TS_WIDTH_ACTIVY TS_SIZE
+#define TS_WIDTH_DVBC TS_SIZE
+#define TS_HEIGHT_MASK 0xf00
+#define TS_HEIGHT_MASK_ACTIVY 0xc00
+#define TS_HEIGHT_MASK_DVBC 0xe00
+#define TS_MIN_BUFSIZE_K 188
+#define TS_MAX_BUFSIZE_K 1410
+#define TS_MAX_BUFSIZE_K_ACTIVY 564
+#define TS_MAX_BUFSIZE_K_DVBC 1316
+#define BUFFER_WARNING_WAIT (30*HZ)
+
+int budget_debug;
+static int dma_buffer_size = TS_MIN_BUFSIZE_K;
+module_param_named(debug, budget_debug, int, 0644);
+module_param_named(bufsize, dma_buffer_size, int, 0444);
+MODULE_PARM_DESC(debug, "Turn on/off budget debugging (default:off).");
+MODULE_PARM_DESC(bufsize, "DMA buffer size in KB, default: 188, min: 188, max: 1410 (Activy: 564)");
+
+/****************************************************************************
+ * TT budget / WinTV Nova
+ ****************************************************************************/
+
+static int stop_ts_capture(struct budget *budget)
+{
+ dprintk(2, "budget: %p\n", budget);
+
+ saa7146_write(budget->dev, MC1, MASK_20); // DMA3 off
+ SAA7146_IER_DISABLE(budget->dev, MASK_10);
+ return 0;
+}
+
+static int start_ts_capture(struct budget *budget)
+{
+ struct saa7146_dev *dev = budget->dev;
+
+ dprintk(2, "budget: %p\n", budget);
+
+ if (!budget->feeding || !budget->fe_synced)
+ return 0;
+
+ saa7146_write(dev, MC1, MASK_20); // DMA3 off
+
+ memset(budget->grabbing, 0x00, budget->buffer_size);
+
+ saa7146_write(dev, PCI_BT_V1, 0x001c0000 | (saa7146_read(dev, PCI_BT_V1) & ~0x001f0000));
+
+ budget->ttbp = 0;
+
+ /*
+ * Signal path on the Activy:
+ *
+ * tuner -> SAA7146 port A -> SAA7146 BRS -> SAA7146 DMA3 -> memory
+ *
+ * Since the tuner feeds 204 bytes packets into the SAA7146,
+ * DMA3 is configured to strip the trailing 16 FEC bytes:
+ * Pitch: 188, NumBytes3: 188, NumLines3: 1024
+ */
+
+ switch(budget->card->type) {
+ case BUDGET_FS_ACTIVY:
+ saa7146_write(dev, DD1_INIT, 0x04000000);
+ saa7146_write(dev, MC2, (MASK_09 | MASK_25));
+ saa7146_write(dev, BRS_CTRL, 0x00000000);
+ break;
+ case BUDGET_PATCH:
+ saa7146_write(dev, DD1_INIT, 0x00000200);
+ saa7146_write(dev, MC2, (MASK_10 | MASK_26));
+ saa7146_write(dev, BRS_CTRL, 0x60000000);
+ break;
+ case BUDGET_CIN1200C_MK3:
+ case BUDGET_KNC1C_MK3:
+ case BUDGET_KNC1C_TDA10024:
+ case BUDGET_KNC1CP_MK3:
+ if (budget->video_port == BUDGET_VIDEO_PORTA) {
+ saa7146_write(dev, DD1_INIT, 0x06000200);
+ saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26));
+ saa7146_write(dev, BRS_CTRL, 0x00000000);
+ } else {
+ saa7146_write(dev, DD1_INIT, 0x00000600);
+ saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26));
+ saa7146_write(dev, BRS_CTRL, 0x60000000);
+ }
+ break;
+ default:
+ if (budget->video_port == BUDGET_VIDEO_PORTA) {
+ saa7146_write(dev, DD1_INIT, 0x06000200);
+ saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26));
+ saa7146_write(dev, BRS_CTRL, 0x00000000);
+ } else {
+ saa7146_write(dev, DD1_INIT, 0x02000600);
+ saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26));
+ saa7146_write(dev, BRS_CTRL, 0x60000000);
+ }
+ }
+
+ saa7146_write(dev, MC2, (MASK_08 | MASK_24));
+ mdelay(10);
+
+ saa7146_write(dev, BASE_ODD3, 0);
+ if (budget->buffer_size > budget->buffer_height * budget->buffer_width) {
+ // using odd/even buffers
+ saa7146_write(dev, BASE_EVEN3, budget->buffer_height * budget->buffer_width);
+ } else {
+ // using a single buffer
+ saa7146_write(dev, BASE_EVEN3, 0);
+ }
+ saa7146_write(dev, PROT_ADDR3, budget->buffer_size);
+ saa7146_write(dev, BASE_PAGE3, budget->pt.dma | ME1 | 0x90);
+
+ saa7146_write(dev, PITCH3, budget->buffer_width);
+ saa7146_write(dev, NUM_LINE_BYTE3,
+ (budget->buffer_height << 16) | budget->buffer_width);
+
+ saa7146_write(dev, MC2, (MASK_04 | MASK_20));
+
+ SAA7146_ISR_CLEAR(budget->dev, MASK_10); /* VPE */
+ SAA7146_IER_ENABLE(budget->dev, MASK_10); /* VPE */
+ saa7146_write(dev, MC1, (MASK_04 | MASK_20)); /* DMA3 on */
+
+ return 0;
+}
+
+static int budget_read_fe_status(struct dvb_frontend *fe,
+ enum fe_status *status)
+{
+ struct budget *budget = (struct budget *) fe->dvb->priv;
+ int synced;
+ int ret;
+
+ if (budget->read_fe_status)
+ ret = budget->read_fe_status(fe, status);
+ else
+ ret = -EINVAL;
+
+ if (!ret) {
+ synced = (*status & FE_HAS_LOCK);
+ if (synced != budget->fe_synced) {
+ budget->fe_synced = synced;
+ spin_lock(&budget->feedlock);
+ if (synced)
+ start_ts_capture(budget);
+ else
+ stop_ts_capture(budget);
+ spin_unlock(&budget->feedlock);
+ }
+ }
+ return ret;
+}
+
+static void vpeirq(struct tasklet_struct *t)
+{
+ struct budget *budget = from_tasklet(budget, t, vpe_tasklet);
+ u8 *mem = (u8 *) (budget->grabbing);
+ u32 olddma = budget->ttbp;
+ u32 newdma = saa7146_read(budget->dev, PCI_VDP3);
+ u32 count;
+
+ /* Ensure streamed PCI data is synced to CPU */
+ dma_sync_sg_for_cpu(&budget->dev->pci->dev, budget->pt.slist,
+ budget->pt.nents, DMA_FROM_DEVICE);
+
+ /* nearest lower position divisible by 188 */
+ newdma -= newdma % 188;
+
+ if (newdma >= budget->buffer_size)
+ return;
+
+ budget->ttbp = newdma;
+
+ if (budget->feeding == 0 || newdma == olddma)
+ return;
+
+ if (newdma > olddma) { /* no wraparound, dump olddma..newdma */
+ count = newdma - olddma;
+ dvb_dmx_swfilter_packets(&budget->demux, mem + olddma, count / 188);
+ } else { /* wraparound, dump olddma..buflen and 0..newdma */
+ count = budget->buffer_size - olddma;
+ dvb_dmx_swfilter_packets(&budget->demux, mem + olddma, count / 188);
+ count += newdma;
+ dvb_dmx_swfilter_packets(&budget->demux, mem, newdma / 188);
+ }
+
+ if (count > budget->buffer_warning_threshold)
+ budget->buffer_warnings++;
+
+ if (budget->buffer_warnings && time_after(jiffies, budget->buffer_warning_time)) {
+ printk("%s %s: used %d times >80%% of buffer (%u bytes now)\n",
+ budget->dev->name, __func__, budget->buffer_warnings, count);
+ budget->buffer_warning_time = jiffies + BUFFER_WARNING_WAIT;
+ budget->buffer_warnings = 0;
+ }
+}
+
+
+static int ttpci_budget_debiread_nolock(struct budget *budget, u32 config,
+ int addr, int count, int nobusyloop)
+{
+ struct saa7146_dev *saa = budget->dev;
+ int result;
+
+ result = saa7146_wait_for_debi_done(saa, nobusyloop);
+ if (result < 0)
+ return result;
+
+ saa7146_write(saa, DEBI_COMMAND, (count << 17) | 0x10000 | (addr & 0xffff));
+ saa7146_write(saa, DEBI_CONFIG, config);
+ saa7146_write(saa, DEBI_PAGE, 0);
+ saa7146_write(saa, MC2, (2 << 16) | 2);
+
+ result = saa7146_wait_for_debi_done(saa, nobusyloop);
+ if (result < 0)
+ return result;
+
+ result = saa7146_read(saa, DEBI_AD);
+ result &= (0xffffffffUL >> ((4 - count) * 8));
+ return result;
+}
+
+int ttpci_budget_debiread(struct budget *budget, u32 config, int addr, int count,
+ int uselocks, int nobusyloop)
+{
+ if (count > 4 || count <= 0)
+ return 0;
+
+ if (uselocks) {
+ unsigned long flags;
+ int result;
+
+ spin_lock_irqsave(&budget->debilock, flags);
+ result = ttpci_budget_debiread_nolock(budget, config, addr,
+ count, nobusyloop);
+ spin_unlock_irqrestore(&budget->debilock, flags);
+ return result;
+ }
+ return ttpci_budget_debiread_nolock(budget, config, addr,
+ count, nobusyloop);
+}
+
+static int ttpci_budget_debiwrite_nolock(struct budget *budget, u32 config,
+ int addr, int count, u32 value, int nobusyloop)
+{
+ struct saa7146_dev *saa = budget->dev;
+ int result;
+
+ result = saa7146_wait_for_debi_done(saa, nobusyloop);
+ if (result < 0)
+ return result;
+
+ saa7146_write(saa, DEBI_COMMAND, (count << 17) | 0x00000 | (addr & 0xffff));
+ saa7146_write(saa, DEBI_CONFIG, config);
+ saa7146_write(saa, DEBI_PAGE, 0);
+ saa7146_write(saa, DEBI_AD, value);
+ saa7146_write(saa, MC2, (2 << 16) | 2);
+
+ result = saa7146_wait_for_debi_done(saa, nobusyloop);
+ return result < 0 ? result : 0;
+}
+
+int ttpci_budget_debiwrite(struct budget *budget, u32 config, int addr,
+ int count, u32 value, int uselocks, int nobusyloop)
+{
+ if (count > 4 || count <= 0)
+ return 0;
+
+ if (uselocks) {
+ unsigned long flags;
+ int result;
+
+ spin_lock_irqsave(&budget->debilock, flags);
+ result = ttpci_budget_debiwrite_nolock(budget, config, addr,
+ count, value, nobusyloop);
+ spin_unlock_irqrestore(&budget->debilock, flags);
+ return result;
+ }
+ return ttpci_budget_debiwrite_nolock(budget, config, addr,
+ count, value, nobusyloop);
+}
+
+
+/****************************************************************************
+ * DVB API SECTION
+ ****************************************************************************/
+
+static int budget_start_feed(struct dvb_demux_feed *feed)
+{
+ struct dvb_demux *demux = feed->demux;
+ struct budget *budget = (struct budget *) demux->priv;
+ int status = 0;
+
+ dprintk(2, "budget: %p\n", budget);
+
+ if (!demux->dmx.frontend)
+ return -EINVAL;
+
+ spin_lock(&budget->feedlock);
+ feed->pusi_seen = false; /* have a clean section start */
+ if (budget->feeding++ == 0)
+ status = start_ts_capture(budget);
+ spin_unlock(&budget->feedlock);
+ return status;
+}
+
+static int budget_stop_feed(struct dvb_demux_feed *feed)
+{
+ struct dvb_demux *demux = feed->demux;
+ struct budget *budget = (struct budget *) demux->priv;
+ int status = 0;
+
+ dprintk(2, "budget: %p\n", budget);
+
+ spin_lock(&budget->feedlock);
+ if (--budget->feeding == 0)
+ status = stop_ts_capture(budget);
+ spin_unlock(&budget->feedlock);
+ return status;
+}
+
+static int budget_register(struct budget *budget)
+{
+ struct dvb_demux *dvbdemux = &budget->demux;
+ int ret;
+
+ dprintk(2, "budget: %p\n", budget);
+
+ dvbdemux->priv = (void *) budget;
+
+ dvbdemux->filternum = 256;
+ dvbdemux->feednum = 256;
+ dvbdemux->start_feed = budget_start_feed;
+ dvbdemux->stop_feed = budget_stop_feed;
+ dvbdemux->write_to_decoder = NULL;
+
+ dvbdemux->dmx.capabilities = (DMX_TS_FILTERING | DMX_SECTION_FILTERING |
+ DMX_MEMORY_BASED_FILTERING);
+
+ dvb_dmx_init(&budget->demux);
+
+ budget->dmxdev.filternum = 256;
+ budget->dmxdev.demux = &dvbdemux->dmx;
+ budget->dmxdev.capabilities = 0;
+
+ dvb_dmxdev_init(&budget->dmxdev, &budget->dvb_adapter);
+
+ budget->hw_frontend.source = DMX_FRONTEND_0;
+
+ ret = dvbdemux->dmx.add_frontend(&dvbdemux->dmx, &budget->hw_frontend);
+
+ if (ret < 0)
+ goto err_release_dmx;
+
+ budget->mem_frontend.source = DMX_MEMORY_FE;
+ ret = dvbdemux->dmx.add_frontend(&dvbdemux->dmx, &budget->mem_frontend);
+ if (ret < 0)
+ goto err_release_dmx;
+
+ ret = dvbdemux->dmx.connect_frontend(&dvbdemux->dmx, &budget->hw_frontend);
+ if (ret < 0)
+ goto err_release_dmx;
+
+ dvb_net_init(&budget->dvb_adapter, &budget->dvb_net, &dvbdemux->dmx);
+
+ return 0;
+
+err_release_dmx:
+ dvb_dmxdev_release(&budget->dmxdev);
+ dvb_dmx_release(&budget->demux);
+ return ret;
+}
+
+static void budget_unregister(struct budget *budget)
+{
+ struct dvb_demux *dvbdemux = &budget->demux;
+
+ dprintk(2, "budget: %p\n", budget);
+
+ dvb_net_release(&budget->dvb_net);
+
+ dvbdemux->dmx.close(&dvbdemux->dmx);
+ dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, &budget->hw_frontend);
+ dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, &budget->mem_frontend);
+
+ dvb_dmxdev_release(&budget->dmxdev);
+ dvb_dmx_release(&budget->demux);
+}
+
+int ttpci_budget_init(struct budget *budget, struct saa7146_dev *dev,
+ struct saa7146_pci_extension_data *info,
+ struct module *owner, short *adapter_nums)
+{
+ int ret = 0;
+ struct budget_info *bi = info->ext_priv;
+ int max_bufsize;
+ int height_mask;
+
+ memset(budget, 0, sizeof(struct budget));
+
+ dprintk(2, "dev: %p, budget: %p\n", dev, budget);
+
+ budget->card = bi;
+ budget->dev = (struct saa7146_dev *) dev;
+
+ switch(budget->card->type) {
+ case BUDGET_FS_ACTIVY:
+ budget->buffer_width = TS_WIDTH_ACTIVY;
+ max_bufsize = TS_MAX_BUFSIZE_K_ACTIVY;
+ height_mask = TS_HEIGHT_MASK_ACTIVY;
+ break;
+
+ case BUDGET_KNC1C:
+ case BUDGET_KNC1CP:
+ case BUDGET_CIN1200C:
+ case BUDGET_KNC1C_MK3:
+ case BUDGET_KNC1C_TDA10024:
+ case BUDGET_KNC1CP_MK3:
+ case BUDGET_CIN1200C_MK3:
+ budget->buffer_width = TS_WIDTH_DVBC;
+ max_bufsize = TS_MAX_BUFSIZE_K_DVBC;
+ height_mask = TS_HEIGHT_MASK_DVBC;
+ break;
+
+ default:
+ budget->buffer_width = TS_WIDTH;
+ max_bufsize = TS_MAX_BUFSIZE_K;
+ height_mask = TS_HEIGHT_MASK;
+ }
+
+ if (dma_buffer_size < TS_MIN_BUFSIZE_K)
+ dma_buffer_size = TS_MIN_BUFSIZE_K;
+ else if (dma_buffer_size > max_bufsize)
+ dma_buffer_size = max_bufsize;
+
+ budget->buffer_height = dma_buffer_size * 1024 / budget->buffer_width;
+ if (budget->buffer_height > 0xfff) {
+ budget->buffer_height /= 2;
+ budget->buffer_height &= height_mask;
+ budget->buffer_size = 2 * budget->buffer_height * budget->buffer_width;
+ } else {
+ budget->buffer_height &= height_mask;
+ budget->buffer_size = budget->buffer_height * budget->buffer_width;
+ }
+ budget->buffer_warning_threshold = budget->buffer_size * 80/100;
+ budget->buffer_warnings = 0;
+ budget->buffer_warning_time = jiffies;
+
+ dprintk(2, "%s: buffer type = %s, width = %d, height = %d\n",
+ budget->dev->name,
+ budget->buffer_size > budget->buffer_width * budget->buffer_height ? "odd/even" : "single",
+ budget->buffer_width, budget->buffer_height);
+ printk("%s: dma buffer size %u\n", budget->dev->name, budget->buffer_size);
+
+ ret = dvb_register_adapter(&budget->dvb_adapter, budget->card->name,
+ owner, &budget->dev->pci->dev, adapter_nums);
+ if (ret < 0)
+ return ret;
+
+ /* set dd1 stream a & b */
+ saa7146_write(dev, DD1_STREAM_B, 0x00000000);
+ saa7146_write(dev, MC2, (MASK_09 | MASK_25));
+ saa7146_write(dev, MC2, (MASK_10 | MASK_26));
+ saa7146_write(dev, DD1_INIT, 0x02000000);
+ saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26));
+
+ if (bi->type != BUDGET_FS_ACTIVY)
+ budget->video_port = BUDGET_VIDEO_PORTB;
+ else
+ budget->video_port = BUDGET_VIDEO_PORTA;
+ spin_lock_init(&budget->feedlock);
+ spin_lock_init(&budget->debilock);
+
+ /* the Siemens DVB needs this if you want to have the i2c chips
+ get recognized before the main driver is loaded */
+ if (bi->type != BUDGET_FS_ACTIVY)
+ saa7146_write(dev, GPIO_CTRL, 0x500000); /* GPIO 3 = 1 */
+
+ strscpy(budget->i2c_adap.name, budget->card->name,
+ sizeof(budget->i2c_adap.name));
+
+ saa7146_i2c_adapter_prepare(dev, &budget->i2c_adap, SAA7146_I2C_BUS_BIT_RATE_120);
+ strscpy(budget->i2c_adap.name, budget->card->name,
+ sizeof(budget->i2c_adap.name));
+
+ if (i2c_add_adapter(&budget->i2c_adap) < 0) {
+ ret = -ENOMEM;
+ goto err_dvb_unregister;
+ }
+
+ ttpci_eeprom_parse_mac(&budget->i2c_adap, budget->dvb_adapter.proposed_mac);
+
+ budget->grabbing = saa7146_vmalloc_build_pgtable(dev->pci, budget->buffer_size, &budget->pt);
+ if (NULL == budget->grabbing) {
+ ret = -ENOMEM;
+ goto err_del_i2c;
+ }
+
+ saa7146_write(dev, PCI_BT_V1, 0x001c0000);
+ /* upload all */
+ saa7146_write(dev, GPIO_CTRL, 0x000000);
+
+ tasklet_setup(&budget->vpe_tasklet, vpeirq);
+
+ /* frontend power on */
+ if (bi->type != BUDGET_FS_ACTIVY)
+ saa7146_setgpio(dev, 2, SAA7146_GPIO_OUTHI);
+
+ if ((ret = budget_register(budget)) == 0)
+ return 0; /* Everything OK */
+
+ /* An error occurred, cleanup resources */
+ saa7146_vfree_destroy_pgtable(dev->pci, budget->grabbing, &budget->pt);
+
+err_del_i2c:
+ i2c_del_adapter(&budget->i2c_adap);
+
+err_dvb_unregister:
+ dvb_unregister_adapter(&budget->dvb_adapter);
+
+ return ret;
+}
+
+void ttpci_budget_init_hooks(struct budget *budget)
+{
+ if (budget->dvb_frontend && !budget->read_fe_status) {
+ budget->read_fe_status = budget->dvb_frontend->ops.read_status;
+ budget->dvb_frontend->ops.read_status = budget_read_fe_status;
+ }
+}
+
+int ttpci_budget_deinit(struct budget *budget)
+{
+ struct saa7146_dev *dev = budget->dev;
+
+ dprintk(2, "budget: %p\n", budget);
+
+ budget_unregister(budget);
+
+ tasklet_kill(&budget->vpe_tasklet);
+
+ saa7146_vfree_destroy_pgtable(dev->pci, budget->grabbing, &budget->pt);
+
+ i2c_del_adapter(&budget->i2c_adap);
+
+ dvb_unregister_adapter(&budget->dvb_adapter);
+
+ return 0;
+}
+
+void ttpci_budget_irq10_handler(struct saa7146_dev *dev, u32 * isr)
+{
+ struct budget *budget = (struct budget *) dev->ext_priv;
+
+ dprintk(8, "dev: %p, budget: %p\n", dev, budget);
+
+ if (*isr & MASK_10)
+ tasklet_schedule(&budget->vpe_tasklet);
+}
+
+void ttpci_budget_set_video_port(struct saa7146_dev *dev, int video_port)
+{
+ struct budget *budget = (struct budget *) dev->ext_priv;
+
+ spin_lock(&budget->feedlock);
+ budget->video_port = video_port;
+ if (budget->feeding) {
+ stop_ts_capture(budget);
+ start_ts_capture(budget);
+ }
+ spin_unlock(&budget->feedlock);
+}
+
+EXPORT_SYMBOL_GPL(ttpci_budget_debiread);
+EXPORT_SYMBOL_GPL(ttpci_budget_debiwrite);
+EXPORT_SYMBOL_GPL(ttpci_budget_init);
+EXPORT_SYMBOL_GPL(ttpci_budget_init_hooks);
+EXPORT_SYMBOL_GPL(ttpci_budget_deinit);
+EXPORT_SYMBOL_GPL(ttpci_budget_irq10_handler);
+EXPORT_SYMBOL_GPL(ttpci_budget_set_video_port);
+EXPORT_SYMBOL_GPL(budget_debug);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/pci/ttpci/budget.c b/drivers/media/pci/ttpci/budget.c
new file mode 100644
index 000000000000..a88711a3ac7f
--- /dev/null
+++ b/drivers/media/pci/ttpci/budget.c
@@ -0,0 +1,883 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * budget.c: driver for the SAA7146 based Budget DVB cards
+ *
+ * Compiled from various sources by Michael Hunold <michael@mihu.de>
+ *
+ * Copyright (C) 2002 Ralph Metzler <rjkm@metzlerbros.de>
+ *
+ * Copyright (C) 1999-2002 Ralph Metzler
+ * & Marcus Metzler for convergence integrated media GmbH
+ *
+ * 26feb2004 Support for FS Activy Card (Grundig tuner) by
+ * Michael Dreher <michael@5dot1.de>,
+ * Oliver Endriss <o.endriss@gmx.de> and
+ * Andreas 'randy' Weinberger
+ *
+ * the project's page is at https://linuxtv.org
+ */
+
+#include "budget.h"
+#include "stv0299.h"
+#include "ves1x93.h"
+#include "ves1820.h"
+#include "l64781.h"
+#include "tda8083.h"
+#include "s5h1420.h"
+#include "tda10086.h"
+#include "tda826x.h"
+#include "lnbp21.h"
+#include "bsru6.h"
+#include "bsbe1.h"
+#include "tdhd1.h"
+#include "stv6110x.h"
+#include "stv090x.h"
+#include "isl6423.h"
+#include "lnbh24.h"
+
+
+static int diseqc_method;
+module_param(diseqc_method, int, 0444);
+MODULE_PARM_DESC(diseqc_method, "Select DiSEqC method for subsystem id 13c2:1003, 0: default, 1: more reliable (for newer revisions only)");
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+static void Set22K (struct budget *budget, int state)
+{
+ struct saa7146_dev *dev=budget->dev;
+ dprintk(2, "budget: %p\n", budget);
+ saa7146_setgpio(dev, 3, (state ? SAA7146_GPIO_OUTHI : SAA7146_GPIO_OUTLO));
+}
+
+/* Diseqc functions only for TT Budget card */
+/* taken from the Skyvision DVB driver by
+ Ralph Metzler <rjkm@metzlerbros.de> */
+
+static void DiseqcSendBit (struct budget *budget, int data)
+{
+ struct saa7146_dev *dev=budget->dev;
+ dprintk(2, "budget: %p\n", budget);
+
+ saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTHI);
+ udelay(data ? 500 : 1000);
+ saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO);
+ udelay(data ? 1000 : 500);
+}
+
+static void DiseqcSendByte (struct budget *budget, int data)
+{
+ int i, par=1, d;
+
+ dprintk(2, "budget: %p\n", budget);
+
+ for (i=7; i>=0; i--) {
+ d = (data>>i)&1;
+ par ^= d;
+ DiseqcSendBit(budget, d);
+ }
+
+ DiseqcSendBit(budget, par);
+}
+
+static int SendDiSEqCMsg (struct budget *budget, int len, u8 *msg, unsigned long burst)
+{
+ struct saa7146_dev *dev=budget->dev;
+ int i;
+
+ dprintk(2, "budget: %p\n", budget);
+
+ saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO);
+ mdelay(16);
+
+ for (i=0; i<len; i++)
+ DiseqcSendByte(budget, msg[i]);
+
+ mdelay(16);
+
+ if (burst!=-1) {
+ if (burst)
+ DiseqcSendByte(budget, 0xff);
+ else {
+ saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTHI);
+ mdelay(12);
+ udelay(500);
+ saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO);
+ }
+ msleep(20);
+ }
+
+ return 0;
+}
+
+/*
+ * Routines for the Fujitsu Siemens Activy budget card
+ * 22 kHz tone and DiSEqC are handled by the frontend.
+ * Voltage must be set here.
+ * GPIO 1: LNBP EN, GPIO 2: LNBP VSEL
+ */
+static int SetVoltage_Activy(struct budget *budget,
+ enum fe_sec_voltage voltage)
+{
+ struct saa7146_dev *dev=budget->dev;
+
+ dprintk(2, "budget: %p\n", budget);
+
+ switch (voltage) {
+ case SEC_VOLTAGE_13:
+ saa7146_setgpio(dev, 1, SAA7146_GPIO_OUTHI);
+ saa7146_setgpio(dev, 2, SAA7146_GPIO_OUTLO);
+ break;
+ case SEC_VOLTAGE_18:
+ saa7146_setgpio(dev, 1, SAA7146_GPIO_OUTHI);
+ saa7146_setgpio(dev, 2, SAA7146_GPIO_OUTHI);
+ break;
+ case SEC_VOLTAGE_OFF:
+ saa7146_setgpio(dev, 1, SAA7146_GPIO_OUTLO);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int siemens_budget_set_voltage(struct dvb_frontend *fe,
+ enum fe_sec_voltage voltage)
+{
+ struct budget* budget = (struct budget*) fe->dvb->priv;
+
+ return SetVoltage_Activy (budget, voltage);
+}
+
+static int budget_set_tone(struct dvb_frontend *fe,
+ enum fe_sec_tone_mode tone)
+{
+ struct budget* budget = (struct budget*) fe->dvb->priv;
+
+ switch (tone) {
+ case SEC_TONE_ON:
+ Set22K (budget, 1);
+ break;
+
+ case SEC_TONE_OFF:
+ Set22K (budget, 0);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int budget_diseqc_send_master_cmd(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd* cmd)
+{
+ struct budget* budget = (struct budget*) fe->dvb->priv;
+
+ SendDiSEqCMsg (budget, cmd->msg_len, cmd->msg, 0);
+
+ return 0;
+}
+
+static int budget_diseqc_send_burst(struct dvb_frontend *fe,
+ enum fe_sec_mini_cmd minicmd)
+{
+ struct budget* budget = (struct budget*) fe->dvb->priv;
+
+ SendDiSEqCMsg (budget, 0, NULL, minicmd);
+
+ return 0;
+}
+
+static int alps_bsrv2_tuner_set_params(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ struct budget* budget = (struct budget*) fe->dvb->priv;
+ u8 pwr = 0;
+ u8 buf[4];
+ struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = buf, .len = sizeof(buf) };
+ u32 div = (c->frequency + 479500) / 125;
+
+ if (c->frequency > 2000000)
+ pwr = 3;
+ else if (c->frequency > 1800000)
+ pwr = 2;
+ else if (c->frequency > 1600000)
+ pwr = 1;
+ else if (c->frequency > 1200000)
+ pwr = 0;
+ else if (c->frequency >= 1100000)
+ pwr = 1;
+ else pwr = 2;
+
+ buf[0] = (div >> 8) & 0x7f;
+ buf[1] = div & 0xff;
+ buf[2] = ((div & 0x18000) >> 10) | 0x95;
+ buf[3] = (pwr << 6) | 0x30;
+
+ // NOTE: since we're using a prescaler of 2, we set the
+ // divisor frequency to 62.5kHz and divide by 125 above
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO;
+ return 0;
+}
+
+static struct ves1x93_config alps_bsrv2_config =
+{
+ .demod_address = 0x08,
+ .xin = 90100000UL,
+ .invert_pwm = 0,
+};
+
+static int alps_tdbe2_tuner_set_params(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ struct budget* budget = (struct budget*) fe->dvb->priv;
+ u32 div;
+ u8 data[4];
+ struct i2c_msg msg = { .addr = 0x62, .flags = 0, .buf = data, .len = sizeof(data) };
+
+ div = (c->frequency + 35937500 + 31250) / 62500;
+
+ data[0] = (div >> 8) & 0x7f;
+ data[1] = div & 0xff;
+ data[2] = 0x85 | ((div >> 10) & 0x60);
+ data[3] = (c->frequency < 174000000 ? 0x88 : c->frequency < 470000000 ? 0x84 : 0x81);
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO;
+ return 0;
+}
+
+static struct ves1820_config alps_tdbe2_config = {
+ .demod_address = 0x09,
+ .xin = 57840000UL,
+ .invert = 1,
+ .selagc = VES1820_SELAGC_SIGNAMPERR,
+};
+
+static int grundig_29504_401_tuner_set_params(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ struct budget *budget = fe->dvb->priv;
+ u8 *tuner_addr = fe->tuner_priv;
+ u32 div;
+ u8 cfg, cpump, band_select;
+ u8 data[4];
+ struct i2c_msg msg = { .flags = 0, .buf = data, .len = sizeof(data) };
+
+ if (tuner_addr)
+ msg.addr = *tuner_addr;
+ else
+ msg.addr = 0x61;
+
+ div = (36125000 + c->frequency) / 166666;
+
+ cfg = 0x88;
+
+ if (c->frequency < 175000000)
+ cpump = 2;
+ else if (c->frequency < 390000000)
+ cpump = 1;
+ else if (c->frequency < 470000000)
+ cpump = 2;
+ else if (c->frequency < 750000000)
+ cpump = 1;
+ else
+ cpump = 3;
+
+ if (c->frequency < 175000000)
+ band_select = 0x0e;
+ else if (c->frequency < 470000000)
+ band_select = 0x05;
+ else
+ band_select = 0x03;
+
+ data[0] = (div >> 8) & 0x7f;
+ data[1] = div & 0xff;
+ data[2] = ((div >> 10) & 0x60) | cfg;
+ data[3] = (cpump << 6) | band_select;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO;
+ return 0;
+}
+
+static struct l64781_config grundig_29504_401_config = {
+ .demod_address = 0x55,
+};
+
+static struct l64781_config grundig_29504_401_config_activy = {
+ .demod_address = 0x54,
+};
+
+static u8 tuner_address_grundig_29504_401_activy = 0x60;
+
+static int grundig_29504_451_tuner_set_params(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ struct budget* budget = (struct budget*) fe->dvb->priv;
+ u32 div;
+ u8 data[4];
+ struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = sizeof(data) };
+
+ div = c->frequency / 125;
+ data[0] = (div >> 8) & 0x7f;
+ data[1] = div & 0xff;
+ data[2] = 0x8e;
+ data[3] = 0x00;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO;
+ return 0;
+}
+
+static struct tda8083_config grundig_29504_451_config = {
+ .demod_address = 0x68,
+};
+
+static int s5h1420_tuner_set_params(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ struct budget* budget = (struct budget*) fe->dvb->priv;
+ u32 div;
+ u8 data[4];
+ struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = sizeof(data) };
+
+ div = c->frequency / 1000;
+ data[0] = (div >> 8) & 0x7f;
+ data[1] = div & 0xff;
+ data[2] = 0xc2;
+
+ if (div < 1450)
+ data[3] = 0x00;
+ else if (div < 1850)
+ data[3] = 0x40;
+ else if (div < 2000)
+ data[3] = 0x80;
+ else
+ data[3] = 0xc0;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO;
+
+ return 0;
+}
+
+static struct s5h1420_config s5h1420_config = {
+ .demod_address = 0x53,
+ .invert = 1,
+ .cdclk_polarity = 1,
+};
+
+static struct tda10086_config tda10086_config = {
+ .demod_address = 0x0e,
+ .invert = 0,
+ .diseqc_tone = 1,
+ .xtal_freq = TDA10086_XTAL_16M,
+};
+
+static const struct stv0299_config alps_bsru6_config_activy = {
+ .demod_address = 0x68,
+ .inittab = alps_bsru6_inittab,
+ .mclk = 88000000UL,
+ .invert = 1,
+ .op0_off = 1,
+ .min_delay_ms = 100,
+ .set_symbol_rate = alps_bsru6_set_symbol_rate,
+};
+
+static const struct stv0299_config alps_bsbe1_config_activy = {
+ .demod_address = 0x68,
+ .inittab = alps_bsbe1_inittab,
+ .mclk = 88000000UL,
+ .invert = 1,
+ .op0_off = 1,
+ .min_delay_ms = 100,
+ .set_symbol_rate = alps_bsbe1_set_symbol_rate,
+};
+
+static int alps_tdhd1_204_request_firmware(struct dvb_frontend *fe, const struct firmware **fw, char *name)
+{
+ struct budget *budget = (struct budget *)fe->dvb->priv;
+
+ return request_firmware(fw, name, &budget->dev->pci->dev);
+}
+
+
+static int i2c_readreg(struct i2c_adapter *i2c, u8 adr, u8 reg)
+{
+ u8 val;
+ struct i2c_msg msg[] = {
+ { .addr = adr, .flags = 0, .buf = &reg, .len = 1 },
+ { .addr = adr, .flags = I2C_M_RD, .buf = &val, .len = 1 }
+ };
+
+ return (i2c_transfer(i2c, msg, 2) != 2) ? -EIO : val;
+}
+
+static u8 read_pwm(struct budget* budget)
+{
+ u8 b = 0xff;
+ u8 pwm;
+ struct i2c_msg msg[] = { { .addr = 0x50,.flags = 0,.buf = &b,.len = 1 },
+ { .addr = 0x50,.flags = I2C_M_RD,.buf = &pwm,.len = 1} };
+
+ if ((i2c_transfer(&budget->i2c_adap, msg, 2) != 2) || (pwm == 0xff))
+ pwm = 0x48;
+
+ return pwm;
+}
+
+static struct stv090x_config tt1600_stv090x_config = {
+ .device = STV0903,
+ .demod_mode = STV090x_SINGLE,
+ .clk_mode = STV090x_CLK_EXT,
+
+ .xtal = 13500000,
+ .address = 0x68,
+
+ .ts1_mode = STV090x_TSMODE_DVBCI,
+ .ts2_mode = STV090x_TSMODE_SERIAL_CONTINUOUS,
+
+ .repeater_level = STV090x_RPTLEVEL_16,
+
+ .tuner_init = NULL,
+ .tuner_sleep = NULL,
+ .tuner_set_mode = NULL,
+ .tuner_set_frequency = NULL,
+ .tuner_get_frequency = NULL,
+ .tuner_set_bandwidth = NULL,
+ .tuner_get_bandwidth = NULL,
+ .tuner_set_bbgain = NULL,
+ .tuner_get_bbgain = NULL,
+ .tuner_set_refclk = NULL,
+ .tuner_get_status = NULL,
+};
+
+static struct stv6110x_config tt1600_stv6110x_config = {
+ .addr = 0x60,
+ .refclk = 27000000,
+ .clk_div = 2,
+};
+
+static struct isl6423_config tt1600_isl6423_config = {
+ .current_max = SEC_CURRENT_515m,
+ .curlim = SEC_CURRENT_LIM_ON,
+ .mod_extern = 1,
+ .addr = 0x08,
+};
+
+static void frontend_init(struct budget *budget)
+{
+ (void)alps_bsbe1_config; /* avoid warning */
+
+ switch(budget->dev->pci->subsystem_device) {
+ case 0x1003: // Hauppauge/TT Nova budget (stv0299/ALPS BSRU6(tsa5059) OR ves1893/ALPS BSRV2(sp5659))
+ case 0x1013:
+ // try the ALPS BSRV2 first of all
+ budget->dvb_frontend = dvb_attach(ves1x93_attach, &alps_bsrv2_config, &budget->i2c_adap);
+ if (budget->dvb_frontend) {
+ budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsrv2_tuner_set_params;
+ budget->dvb_frontend->ops.diseqc_send_master_cmd = budget_diseqc_send_master_cmd;
+ budget->dvb_frontend->ops.diseqc_send_burst = budget_diseqc_send_burst;
+ budget->dvb_frontend->ops.set_tone = budget_set_tone;
+ break;
+ }
+
+ // try the ALPS BSRU6 now
+ budget->dvb_frontend = dvb_attach(stv0299_attach, &alps_bsru6_config, &budget->i2c_adap);
+ if (budget->dvb_frontend) {
+ budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsru6_tuner_set_params;
+ budget->dvb_frontend->tuner_priv = &budget->i2c_adap;
+ if (budget->dev->pci->subsystem_device == 0x1003 && diseqc_method == 0) {
+ budget->dvb_frontend->ops.diseqc_send_master_cmd = budget_diseqc_send_master_cmd;
+ budget->dvb_frontend->ops.diseqc_send_burst = budget_diseqc_send_burst;
+ budget->dvb_frontend->ops.set_tone = budget_set_tone;
+ }
+ break;
+ }
+ break;
+
+ case 0x1004: // Hauppauge/TT DVB-C budget (ves1820/ALPS TDBE2(sp5659))
+
+ budget->dvb_frontend = dvb_attach(ves1820_attach, &alps_tdbe2_config, &budget->i2c_adap, read_pwm(budget));
+ if (budget->dvb_frontend) {
+ budget->dvb_frontend->ops.tuner_ops.set_params = alps_tdbe2_tuner_set_params;
+ break;
+ }
+ break;
+
+ case 0x1005: // Hauppauge/TT Nova-T budget (L64781/Grundig 29504-401(tsa5060))
+
+ budget->dvb_frontend = dvb_attach(l64781_attach, &grundig_29504_401_config, &budget->i2c_adap);
+ if (budget->dvb_frontend) {
+ budget->dvb_frontend->ops.tuner_ops.set_params = grundig_29504_401_tuner_set_params;
+ budget->dvb_frontend->tuner_priv = NULL;
+ break;
+ }
+ break;
+
+ case 0x4f52: /* Cards based on Philips Semi Sylt PCI ref. design */
+ budget->dvb_frontend = dvb_attach(stv0299_attach, &alps_bsru6_config, &budget->i2c_adap);
+ if (budget->dvb_frontend) {
+ printk(KERN_INFO "budget: tuner ALPS BSRU6 in Philips Semi. Sylt detected\n");
+ budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsru6_tuner_set_params;
+ budget->dvb_frontend->tuner_priv = &budget->i2c_adap;
+ break;
+ }
+ break;
+
+ case 0x4f60: /* Fujitsu Siemens Activy Budget-S PCI rev AL (stv0299/tsa5059) */
+ {
+ int subtype = i2c_readreg(&budget->i2c_adap, 0x50, 0x67);
+
+ if (subtype < 0)
+ break;
+ /* fixme: find a better way to identify the card */
+ if (subtype < 0x36) {
+ /* assume ALPS BSRU6 */
+ budget->dvb_frontend = dvb_attach(stv0299_attach, &alps_bsru6_config_activy, &budget->i2c_adap);
+ if (budget->dvb_frontend) {
+ printk(KERN_INFO "budget: tuner ALPS BSRU6 detected\n");
+ budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsru6_tuner_set_params;
+ budget->dvb_frontend->tuner_priv = &budget->i2c_adap;
+ budget->dvb_frontend->ops.set_voltage = siemens_budget_set_voltage;
+ budget->dvb_frontend->ops.dishnetwork_send_legacy_command = NULL;
+ break;
+ }
+ } else {
+ /* assume ALPS BSBE1 */
+ /* reset tuner */
+ saa7146_setgpio(budget->dev, 3, SAA7146_GPIO_OUTLO);
+ msleep(50);
+ saa7146_setgpio(budget->dev, 3, SAA7146_GPIO_OUTHI);
+ msleep(250);
+ budget->dvb_frontend = dvb_attach(stv0299_attach, &alps_bsbe1_config_activy, &budget->i2c_adap);
+ if (budget->dvb_frontend) {
+ printk(KERN_INFO "budget: tuner ALPS BSBE1 detected\n");
+ budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsbe1_tuner_set_params;
+ budget->dvb_frontend->tuner_priv = &budget->i2c_adap;
+ budget->dvb_frontend->ops.set_voltage = siemens_budget_set_voltage;
+ budget->dvb_frontend->ops.dishnetwork_send_legacy_command = NULL;
+ break;
+ }
+ }
+ break;
+ }
+
+ case 0x4f61: // Fujitsu Siemens Activy Budget-S PCI rev GR (tda8083/Grundig 29504-451(tsa5522))
+ budget->dvb_frontend = dvb_attach(tda8083_attach, &grundig_29504_451_config, &budget->i2c_adap);
+ if (budget->dvb_frontend) {
+ budget->dvb_frontend->ops.tuner_ops.set_params = grundig_29504_451_tuner_set_params;
+ budget->dvb_frontend->ops.set_voltage = siemens_budget_set_voltage;
+ budget->dvb_frontend->ops.dishnetwork_send_legacy_command = NULL;
+ }
+ break;
+
+ case 0x5f60: /* Fujitsu Siemens Activy Budget-T PCI rev AL (tda10046/ALPS TDHD1-204A) */
+ budget->dvb_frontend = dvb_attach(tda10046_attach, &alps_tdhd1_204a_config, &budget->i2c_adap);
+ if (budget->dvb_frontend) {
+ budget->dvb_frontend->ops.tuner_ops.set_params = alps_tdhd1_204a_tuner_set_params;
+ budget->dvb_frontend->tuner_priv = &budget->i2c_adap;
+ }
+ break;
+
+ case 0x5f61: /* Fujitsu Siemens Activy Budget-T PCI rev GR (L64781/Grundig 29504-401(tsa5060)) */
+ budget->dvb_frontend = dvb_attach(l64781_attach, &grundig_29504_401_config_activy, &budget->i2c_adap);
+ if (budget->dvb_frontend) {
+ budget->dvb_frontend->tuner_priv = &tuner_address_grundig_29504_401_activy;
+ budget->dvb_frontend->ops.tuner_ops.set_params = grundig_29504_401_tuner_set_params;
+ }
+ break;
+
+ case 0x1016: // Hauppauge/TT Nova-S SE (samsung s5h1420/????(tda8260))
+ {
+ struct dvb_frontend *fe;
+
+ fe = dvb_attach(s5h1420_attach, &s5h1420_config, &budget->i2c_adap);
+ if (fe) {
+ fe->ops.tuner_ops.set_params = s5h1420_tuner_set_params;
+ budget->dvb_frontend = fe;
+ if (dvb_attach(lnbp21_attach, fe, &budget->i2c_adap,
+ 0, 0) == NULL) {
+ printk("%s: No LNBP21 found!\n", __func__);
+ goto error_out;
+ }
+ break;
+ }
+ }
+ fallthrough;
+ case 0x1018: // TT Budget-S-1401 (philips tda10086/philips tda8262)
+ {
+ struct dvb_frontend *fe;
+
+ // gpio2 is connected to CLB - reset it + leave it high
+ saa7146_setgpio(budget->dev, 2, SAA7146_GPIO_OUTLO);
+ msleep(1);
+ saa7146_setgpio(budget->dev, 2, SAA7146_GPIO_OUTHI);
+ msleep(1);
+
+ fe = dvb_attach(tda10086_attach, &tda10086_config, &budget->i2c_adap);
+ if (fe) {
+ budget->dvb_frontend = fe;
+ if (dvb_attach(tda826x_attach, fe, 0x60,
+ &budget->i2c_adap, 0) == NULL)
+ printk("%s: No tda826x found!\n", __func__);
+ if (dvb_attach(lnbp21_attach, fe,
+ &budget->i2c_adap, 0, 0) == NULL) {
+ printk("%s: No LNBP21 found!\n", __func__);
+ goto error_out;
+ }
+ break;
+ }
+ }
+ fallthrough;
+
+ case 0x101c: { /* TT S2-1600 */
+ const struct stv6110x_devctl *ctl;
+ saa7146_setgpio(budget->dev, 2, SAA7146_GPIO_OUTLO);
+ msleep(50);
+ saa7146_setgpio(budget->dev, 2, SAA7146_GPIO_OUTHI);
+ msleep(250);
+
+ budget->dvb_frontend = dvb_attach(stv090x_attach,
+ &tt1600_stv090x_config,
+ &budget->i2c_adap,
+ STV090x_DEMODULATOR_0);
+
+ if (budget->dvb_frontend) {
+
+ ctl = dvb_attach(stv6110x_attach,
+ budget->dvb_frontend,
+ &tt1600_stv6110x_config,
+ &budget->i2c_adap);
+
+ if (ctl) {
+ tt1600_stv090x_config.tuner_init = ctl->tuner_init;
+ tt1600_stv090x_config.tuner_sleep = ctl->tuner_sleep;
+ tt1600_stv090x_config.tuner_set_mode = ctl->tuner_set_mode;
+ tt1600_stv090x_config.tuner_set_frequency = ctl->tuner_set_frequency;
+ tt1600_stv090x_config.tuner_get_frequency = ctl->tuner_get_frequency;
+ tt1600_stv090x_config.tuner_set_bandwidth = ctl->tuner_set_bandwidth;
+ tt1600_stv090x_config.tuner_get_bandwidth = ctl->tuner_get_bandwidth;
+ tt1600_stv090x_config.tuner_set_bbgain = ctl->tuner_set_bbgain;
+ tt1600_stv090x_config.tuner_get_bbgain = ctl->tuner_get_bbgain;
+ tt1600_stv090x_config.tuner_set_refclk = ctl->tuner_set_refclk;
+ tt1600_stv090x_config.tuner_get_status = ctl->tuner_get_status;
+
+ /* call the init function once to initialize
+ tuner's clock output divider and demod's
+ master clock */
+ if (budget->dvb_frontend->ops.init)
+ budget->dvb_frontend->ops.init(budget->dvb_frontend);
+
+ if (dvb_attach(isl6423_attach,
+ budget->dvb_frontend,
+ &budget->i2c_adap,
+ &tt1600_isl6423_config) == NULL) {
+ printk(KERN_ERR "%s: No Intersil ISL6423 found!\n", __func__);
+ goto error_out;
+ }
+ } else {
+ printk(KERN_ERR "%s: No STV6110(A) Silicon Tuner found!\n", __func__);
+ goto error_out;
+ }
+ }
+ }
+ break;
+
+ case 0x1020: { /* Omicom S2 */
+ const struct stv6110x_devctl *ctl;
+ saa7146_setgpio(budget->dev, 2, SAA7146_GPIO_OUTLO);
+ msleep(50);
+ saa7146_setgpio(budget->dev, 2, SAA7146_GPIO_OUTHI);
+ msleep(250);
+
+ budget->dvb_frontend = dvb_attach(stv090x_attach,
+ &tt1600_stv090x_config,
+ &budget->i2c_adap,
+ STV090x_DEMODULATOR_0);
+
+ if (budget->dvb_frontend) {
+ printk(KERN_INFO "budget: Omicom S2 detected\n");
+
+ ctl = dvb_attach(stv6110x_attach,
+ budget->dvb_frontend,
+ &tt1600_stv6110x_config,
+ &budget->i2c_adap);
+
+ if (ctl) {
+ tt1600_stv090x_config.tuner_init = ctl->tuner_init;
+ tt1600_stv090x_config.tuner_sleep = ctl->tuner_sleep;
+ tt1600_stv090x_config.tuner_set_mode = ctl->tuner_set_mode;
+ tt1600_stv090x_config.tuner_set_frequency = ctl->tuner_set_frequency;
+ tt1600_stv090x_config.tuner_get_frequency = ctl->tuner_get_frequency;
+ tt1600_stv090x_config.tuner_set_bandwidth = ctl->tuner_set_bandwidth;
+ tt1600_stv090x_config.tuner_get_bandwidth = ctl->tuner_get_bandwidth;
+ tt1600_stv090x_config.tuner_set_bbgain = ctl->tuner_set_bbgain;
+ tt1600_stv090x_config.tuner_get_bbgain = ctl->tuner_get_bbgain;
+ tt1600_stv090x_config.tuner_set_refclk = ctl->tuner_set_refclk;
+ tt1600_stv090x_config.tuner_get_status = ctl->tuner_get_status;
+
+ /* call the init function once to initialize
+ tuner's clock output divider and demod's
+ master clock */
+ if (budget->dvb_frontend->ops.init)
+ budget->dvb_frontend->ops.init(budget->dvb_frontend);
+
+ if (dvb_attach(lnbh24_attach,
+ budget->dvb_frontend,
+ &budget->i2c_adap,
+ LNBH24_PCL | LNBH24_TTX,
+ LNBH24_TEN, 0x14>>1) == NULL) {
+ printk(KERN_ERR
+ "No LNBH24 found!\n");
+ goto error_out;
+ }
+ } else {
+ printk(KERN_ERR "%s: No STV6110(A) Silicon Tuner found!\n", __func__);
+ goto error_out;
+ }
+ }
+ }
+ break;
+ }
+
+ if (budget->dvb_frontend == NULL) {
+ printk("budget: A frontend driver was not found for device [%04x:%04x] subsystem [%04x:%04x]\n",
+ budget->dev->pci->vendor,
+ budget->dev->pci->device,
+ budget->dev->pci->subsystem_vendor,
+ budget->dev->pci->subsystem_device);
+ } else {
+ if (dvb_register_frontend(&budget->dvb_adapter, budget->dvb_frontend))
+ goto error_out;
+ }
+ return;
+
+error_out:
+ printk("budget: Frontend registration failed!\n");
+ dvb_frontend_detach(budget->dvb_frontend);
+ budget->dvb_frontend = NULL;
+ return;
+}
+
+static int budget_attach (struct saa7146_dev* dev, struct saa7146_pci_extension_data *info)
+{
+ struct budget *budget = NULL;
+ int err;
+
+ budget = kmalloc(sizeof(struct budget), GFP_KERNEL);
+ if( NULL == budget ) {
+ return -ENOMEM;
+ }
+
+ dprintk(2, "dev:%p, info:%p, budget:%p\n", dev, info, budget);
+
+ dev->ext_priv = budget;
+
+ err = ttpci_budget_init(budget, dev, info, THIS_MODULE, adapter_nr);
+ if (err) {
+ printk("==> failed\n");
+ kfree (budget);
+ return err;
+ }
+
+ budget->dvb_adapter.priv = budget;
+ frontend_init(budget);
+
+ ttpci_budget_init_hooks(budget);
+
+ return 0;
+}
+
+static int budget_detach (struct saa7146_dev* dev)
+{
+ struct budget *budget = (struct budget*) dev->ext_priv;
+ int err;
+
+ if (budget->dvb_frontend) {
+ dvb_unregister_frontend(budget->dvb_frontend);
+ dvb_frontend_detach(budget->dvb_frontend);
+ }
+
+ err = ttpci_budget_deinit (budget);
+
+ kfree (budget);
+ dev->ext_priv = NULL;
+
+ return err;
+}
+
+static struct saa7146_extension budget_extension;
+
+MAKE_BUDGET_INFO(ttbs, "TT-Budget/WinTV-NOVA-S PCI", BUDGET_TT);
+MAKE_BUDGET_INFO(ttbc, "TT-Budget/WinTV-NOVA-C PCI", BUDGET_TT);
+MAKE_BUDGET_INFO(ttbt, "TT-Budget/WinTV-NOVA-T PCI", BUDGET_TT);
+MAKE_BUDGET_INFO(satel, "SATELCO Multimedia PCI", BUDGET_TT_HW_DISEQC);
+MAKE_BUDGET_INFO(ttbs1401, "TT-Budget-S-1401 PCI", BUDGET_TT);
+MAKE_BUDGET_INFO(tt1600, "TT-Budget S2-1600 PCI", BUDGET_TT);
+MAKE_BUDGET_INFO(fsacs0, "Fujitsu Siemens Activy Budget-S PCI (rev GR/grundig frontend)", BUDGET_FS_ACTIVY);
+MAKE_BUDGET_INFO(fsacs1, "Fujitsu Siemens Activy Budget-S PCI (rev AL/alps frontend)", BUDGET_FS_ACTIVY);
+MAKE_BUDGET_INFO(fsact, "Fujitsu Siemens Activy Budget-T PCI (rev GR/Grundig frontend)", BUDGET_FS_ACTIVY);
+MAKE_BUDGET_INFO(fsact1, "Fujitsu Siemens Activy Budget-T PCI (rev AL/ALPS TDHD1-204A)", BUDGET_FS_ACTIVY);
+MAKE_BUDGET_INFO(omicom, "Omicom S2 PCI", BUDGET_TT);
+MAKE_BUDGET_INFO(sylt, "Philips Semi Sylt PCI", BUDGET_TT_HW_DISEQC);
+
+static const struct pci_device_id pci_tbl[] = {
+ MAKE_EXTENSION_PCI(ttbs, 0x13c2, 0x1003),
+ MAKE_EXTENSION_PCI(ttbc, 0x13c2, 0x1004),
+ MAKE_EXTENSION_PCI(ttbt, 0x13c2, 0x1005),
+ MAKE_EXTENSION_PCI(satel, 0x13c2, 0x1013),
+ MAKE_EXTENSION_PCI(ttbs, 0x13c2, 0x1016),
+ MAKE_EXTENSION_PCI(ttbs1401, 0x13c2, 0x1018),
+ MAKE_EXTENSION_PCI(tt1600, 0x13c2, 0x101c),
+ MAKE_EXTENSION_PCI(fsacs1,0x1131, 0x4f60),
+ MAKE_EXTENSION_PCI(fsacs0,0x1131, 0x4f61),
+ MAKE_EXTENSION_PCI(fsact1, 0x1131, 0x5f60),
+ MAKE_EXTENSION_PCI(fsact, 0x1131, 0x5f61),
+ MAKE_EXTENSION_PCI(omicom, 0x14c4, 0x1020),
+ MAKE_EXTENSION_PCI(sylt, 0x1131, 0x4f52),
+ {
+ .vendor = 0,
+ }
+};
+
+MODULE_DEVICE_TABLE(pci, pci_tbl);
+
+static struct saa7146_extension budget_extension = {
+ .name = "budget dvb",
+ .flags = SAA7146_USE_I2C_IRQ,
+
+ .module = THIS_MODULE,
+ .pci_tbl = pci_tbl,
+ .attach = budget_attach,
+ .detach = budget_detach,
+
+ .irq_mask = MASK_10,
+ .irq_func = ttpci_budget_irq10_handler,
+};
+
+static int __init budget_init(void)
+{
+ return saa7146_register_extension(&budget_extension);
+}
+
+static void __exit budget_exit(void)
+{
+ saa7146_unregister_extension(&budget_extension);
+}
+
+module_init(budget_init);
+module_exit(budget_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Ralph Metzler, Marcus Metzler, Michael Hunold, others");
+MODULE_DESCRIPTION("driver for the SAA7146 based so-called budget PCI DVB cards by Siemens, Technotrend, Hauppauge");
diff --git a/drivers/media/pci/ttpci/budget.h b/drivers/media/pci/ttpci/budget.h
new file mode 100644
index 000000000000..bd87432e6cde
--- /dev/null
+++ b/drivers/media/pci/ttpci/budget.h
@@ -0,0 +1,129 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef __BUDGET_DVB__
+#define __BUDGET_DVB__
+
+#include <media/dvb_frontend.h>
+#include <media/dvbdev.h>
+#include <media/demux.h>
+#include <media/dvb_demux.h>
+#include <media/dmxdev.h>
+#include <media/dvb_net.h>
+
+#include <linux/module.h>
+#include <linux/mutex.h>
+
+#include <media/drv-intf/saa7146.h>
+
+extern int budget_debug;
+
+#ifdef dprintk
+#undef dprintk
+#endif
+
+#define dprintk(level, fmt, arg...) do { \
+ if (level & budget_debug) \
+ printk(KERN_DEBUG KBUILD_MODNAME ": %s(): " fmt, \
+ __func__, ##arg); \
+} while (0)
+
+#define TS_SIZE 188
+
+struct budget_info {
+ char *name;
+ int type;
+};
+
+/* place to store all the necessary device information */
+struct budget {
+
+ /* devices */
+ struct dvb_device dvb_dev;
+ struct dvb_net dvb_net;
+
+ struct saa7146_dev *dev;
+
+ struct i2c_adapter i2c_adap;
+ struct budget_info *card;
+
+ unsigned char *grabbing;
+ struct saa7146_pgtable pt;
+
+ struct tasklet_struct fidb_tasklet;
+ struct tasklet_struct vpe_tasklet;
+
+ struct dmxdev dmxdev;
+ struct dvb_demux demux;
+
+ struct dmx_frontend hw_frontend;
+ struct dmx_frontend mem_frontend;
+
+ int ci_present;
+ int video_port;
+
+ u32 buffer_width;
+ u32 buffer_height;
+ u32 buffer_size;
+ u32 buffer_warning_threshold;
+ u32 buffer_warnings;
+ unsigned long buffer_warning_time;
+
+ u32 ttbp;
+ int feeding;
+
+ spinlock_t feedlock;
+
+ spinlock_t debilock;
+
+ struct dvb_adapter dvb_adapter;
+ struct dvb_frontend *dvb_frontend;
+ int (*read_fe_status)(struct dvb_frontend *fe, enum fe_status *status);
+ int fe_synced;
+
+ void *priv;
+};
+
+#define MAKE_BUDGET_INFO(x_var,x_name,x_type) \
+static struct budget_info x_var ## _info = { \
+ .name=x_name, \
+ .type=x_type }; \
+static struct saa7146_pci_extension_data x_var = { \
+ .ext_priv = &x_var ## _info, \
+ .ext = &budget_extension };
+
+#define BUDGET_TT 0
+#define BUDGET_TT_HW_DISEQC 1
+#define BUDGET_PATCH 3
+#define BUDGET_FS_ACTIVY 4
+#define BUDGET_CIN1200S 5
+#define BUDGET_CIN1200C 6
+#define BUDGET_CIN1200T 7
+#define BUDGET_KNC1S 8
+#define BUDGET_KNC1C 9
+#define BUDGET_KNC1T 10
+#define BUDGET_KNC1SP 11
+#define BUDGET_KNC1CP 12
+#define BUDGET_KNC1TP 13
+#define BUDGET_TVSTAR 14
+#define BUDGET_CIN1200C_MK3 15
+#define BUDGET_KNC1C_MK3 16
+#define BUDGET_KNC1CP_MK3 17
+#define BUDGET_KNC1S2 18
+#define BUDGET_KNC1C_TDA10024 19
+
+#define BUDGET_VIDEO_PORTA 0
+#define BUDGET_VIDEO_PORTB 1
+
+extern int ttpci_budget_init(struct budget *budget, struct saa7146_dev *dev,
+ struct saa7146_pci_extension_data *info,
+ struct module *owner, short *adapter_nums);
+extern void ttpci_budget_init_hooks(struct budget *budget);
+extern int ttpci_budget_deinit(struct budget *budget);
+extern void ttpci_budget_irq10_handler(struct saa7146_dev *dev, u32 * isr);
+extern void ttpci_budget_set_video_port(struct saa7146_dev *dev, int video_port);
+extern int ttpci_budget_debiread(struct budget *budget, u32 config, int addr, int count,
+ int uselocks, int nobusyloop);
+extern int ttpci_budget_debiwrite(struct budget *budget, u32 config, int addr, int count, u32 value,
+ int uselocks, int nobusyloop);
+
+#endif
diff --git a/drivers/media/platform/amphion/venc.c b/drivers/media/platform/amphion/venc.c
index 3cbe8ce637e5..e6e8fe45fc7c 100644
--- a/drivers/media/platform/amphion/venc.c
+++ b/drivers/media/platform/amphion/venc.c
@@ -250,19 +250,10 @@ static int venc_s_fmt(struct file *file, void *fh, struct v4l2_format *f)
}
if (V4L2_TYPE_IS_OUTPUT(f->type)) {
- if (!vpu_color_check_primaries(pix_mp->colorspace)) {
- venc->params.color.primaries = pix_mp->colorspace;
- vpu_color_get_default(venc->params.color.primaries,
- &venc->params.color.transfer,
- &venc->params.color.matrix,
- &venc->params.color.full_range);
- }
- if (!vpu_color_check_transfers(pix_mp->xfer_func))
- venc->params.color.transfer = pix_mp->xfer_func;
- if (!vpu_color_check_matrix(pix_mp->ycbcr_enc))
- venc->params.color.matrix = pix_mp->ycbcr_enc;
- if (!vpu_color_check_full_range(pix_mp->quantization))
- venc->params.color.full_range = pix_mp->quantization;
+ venc->params.color.primaries = pix_mp->colorspace;
+ venc->params.color.transfer = pix_mp->xfer_func;
+ venc->params.color.matrix = pix_mp->ycbcr_enc;
+ venc->params.color.full_range = pix_mp->quantization;
}
pix_mp->colorspace = venc->params.color.primaries;
@@ -1281,7 +1272,6 @@ static void venc_init(struct file *file)
f.fmt.pix_mp.width = 1280;
f.fmt.pix_mp.height = 720;
f.fmt.pix_mp.field = V4L2_FIELD_NONE;
- f.fmt.pix_mp.colorspace = V4L2_COLORSPACE_REC709;
venc_s_fmt(file, &inst->fh, &f);
memset(&f, 0, sizeof(f));
diff --git a/drivers/media/platform/amphion/vpu_color.c b/drivers/media/platform/amphion/vpu_color.c
index 80b9a53fd1c1..4ae435cbc5cd 100644
--- a/drivers/media/platform/amphion/vpu_color.c
+++ b/drivers/media/platform/amphion/vpu_color.c
@@ -17,7 +17,7 @@
#include "vpu_helpers.h"
static const u8 colorprimaries[] = {
- 0,
+ V4L2_COLORSPACE_LAST,
V4L2_COLORSPACE_REC709, /*Rec. ITU-R BT.709-6*/
0,
0,
@@ -31,7 +31,7 @@ static const u8 colorprimaries[] = {
};
static const u8 colortransfers[] = {
- 0,
+ V4L2_XFER_FUNC_LAST,
V4L2_XFER_FUNC_709, /*Rec. ITU-R BT.709-6*/
0,
0,
@@ -53,7 +53,7 @@ static const u8 colortransfers[] = {
};
static const u8 colormatrixcoefs[] = {
- 0,
+ V4L2_YCBCR_ENC_LAST,
V4L2_YCBCR_ENC_709, /*Rec. ITU-R BT.709-6*/
0,
0,
diff --git a/drivers/media/platform/chips-media/imx-vdoa.c b/drivers/media/platform/chips-media/imx-vdoa.c
index c70871bae193..c3561fcecb98 100644
--- a/drivers/media/platform/chips-media/imx-vdoa.c
+++ b/drivers/media/platform/chips-media/imx-vdoa.c
@@ -324,11 +324,6 @@ static int vdoa_probe(struct platform_device *pdev)
return 0;
}
-static int vdoa_remove(struct platform_device *pdev)
-{
- return 0;
-}
-
static const struct of_device_id vdoa_dt_ids[] = {
{ .compatible = "fsl,imx6q-vdoa" },
{}
@@ -337,7 +332,6 @@ MODULE_DEVICE_TABLE(of, vdoa_dt_ids);
static struct platform_driver vdoa_driver = {
.probe = vdoa_probe,
- .remove = vdoa_remove,
.driver = {
.name = VDOA_NAME,
.of_match_table = vdoa_dt_ids,
diff --git a/drivers/media/platform/marvell/mmp-driver.c b/drivers/media/platform/marvell/mmp-driver.c
index df16899ab1cb..ef22bf8f276c 100644
--- a/drivers/media/platform/marvell/mmp-driver.c
+++ b/drivers/media/platform/marvell/mmp-driver.c
@@ -254,7 +254,7 @@ static int mmpcam_probe(struct platform_device *pdev)
*/
ret = mccic_register(mcam);
if (ret)
- return ret;
+ goto out;
/*
* Add OF clock provider.
diff --git a/drivers/media/platform/mediatek/mdp3/Kconfig b/drivers/media/platform/mediatek/mdp3/Kconfig
index 846e759a8f6a..602329c44750 100644
--- a/drivers/media/platform/mediatek/mdp3/Kconfig
+++ b/drivers/media/platform/mediatek/mdp3/Kconfig
@@ -3,14 +3,13 @@ config VIDEO_MEDIATEK_MDP3
tristate "MediaTek MDP v3 driver"
depends on MTK_IOMMU || COMPILE_TEST
depends on VIDEO_DEV
- depends on ARCH_MEDIATEK || COMPILE_TEST
depends on HAS_DMA
depends on REMOTEPROC
+ depends on MTK_MMSYS
+ depends on MTK_CMDQ
+ depends on MTK_SCP
select VIDEOBUF2_DMA_CONTIG
select V4L2_MEM2MEM_DEV
- select MTK_MMSYS
- select MTK_CMDQ
- select MTK_SCP
default n
help
It is a v4l2 driver and present in MediaTek MT8183 SoC.
diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-comp.c b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-comp.c
index 7bc05f42a23c..091a68685590 100644
--- a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-comp.c
+++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-comp.c
@@ -1002,7 +1002,8 @@ int mdp_comp_config(struct mdp_dev *mdp)
if (!pdev) {
dev_warn(dev, "can't find platform device of node:%s\n",
node->name);
- return -ENODEV;
+ ret = -ENODEV;
+ goto err_init_comps;
}
comp->comp_dev = &pdev->dev;
diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c
index 2d1f6ae9f080..97edcd9d1c81 100644
--- a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c
+++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c
@@ -207,8 +207,8 @@ static int mdp_probe(struct platform_device *pdev)
}
for (i = 0; i < MDP_PIPE_MAX; i++) {
mdp->mdp_mutex[i] = mtk_mutex_get(&mm_pdev->dev);
- if (!mdp->mdp_mutex[i]) {
- ret = -ENODEV;
+ if (IS_ERR(mdp->mdp_mutex[i])) {
+ ret = PTR_ERR(mdp->mdp_mutex[i]);
goto err_free_mutex;
}
}
@@ -289,7 +289,8 @@ err_deinit_comp:
mdp_comp_destroy(mdp);
err_free_mutex:
for (i = 0; i < MDP_PIPE_MAX; i++)
- mtk_mutex_put(mdp->mdp_mutex[i]);
+ if (!IS_ERR_OR_NULL(mdp->mdp_mutex[i]))
+ mtk_mutex_put(mdp->mdp_mutex[i]);
err_destroy_device:
kfree(mdp);
err_return:
diff --git a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_pm.c b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_pm.c
index 4305e4eb9900..777d445999e9 100644
--- a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_pm.c
+++ b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_pm.c
@@ -72,9 +72,9 @@ static void mtk_vcodec_dec_pw_off(struct mtk_vcodec_pm *pm)
{
int ret;
- ret = pm_runtime_put_sync(pm->dev);
- if (ret)
- mtk_v4l2_err("pm_runtime_put_sync fail %d", ret);
+ ret = pm_runtime_put(pm->dev);
+ if (ret && ret != -EAGAIN)
+ mtk_v4l2_err("pm_runtime_put fail %d", ret);
}
static void mtk_vcodec_dec_clock_on(struct mtk_vcodec_pm *pm)
diff --git a/drivers/media/platform/mediatek/vcodec/venc/venc_h264_if.c b/drivers/media/platform/mediatek/vcodec/venc/venc_h264_if.c
index 13c4f860fa69..60fd165c0d94 100644
--- a/drivers/media/platform/mediatek/vcodec/venc/venc_h264_if.c
+++ b/drivers/media/platform/mediatek/vcodec/venc/venc_h264_if.c
@@ -565,7 +565,7 @@ static int h264_encode_frame(struct venc_h264_inst *inst,
*bs_size);
++inst->frm_cnt;
++inst->skip_frm_cnt;
- return ret;
+ return 0;
}
irq_status = h264_enc_wait_venc_done(inst);
@@ -580,7 +580,7 @@ static int h264_encode_frame(struct venc_h264_inst *inst,
mtk_vcodec_debug(inst, "frm %d bs_size %d key_frm %d <-",
inst->frm_cnt, *bs_size, inst->vpu_inst.is_key_frm);
- return ret;
+ return 0;
}
static void h264_encode_filler(struct venc_h264_inst *inst, void *buf,
diff --git a/drivers/media/platform/microchip/microchip-isc-base.c b/drivers/media/platform/microchip/microchip-isc-base.c
index e2994d48f10c..71758ee8474b 100644
--- a/drivers/media/platform/microchip/microchip-isc-base.c
+++ b/drivers/media/platform/microchip/microchip-isc-base.c
@@ -32,10 +32,6 @@
#include "microchip-isc-regs.h"
#include "microchip-isc.h"
-static unsigned int debug;
-module_param(debug, int, 0644);
-MODULE_PARM_DESC(debug, "debug level (0-2)");
-
#define ISC_IS_FORMAT_RAW(mbus_code) \
(((mbus_code) & 0xf000) == 0x3000)
@@ -114,8 +110,8 @@ static int isc_buffer_prepare(struct vb2_buffer *vb)
unsigned long size = isc->fmt.fmt.pix.sizeimage;
if (vb2_plane_size(vb, 0) < size) {
- v4l2_err(&isc->v4l2_dev, "buffer too small (%lu < %lu)\n",
- vb2_plane_size(vb, 0), size);
+ dev_err(isc->dev, "buffer too small (%lu < %lu)\n",
+ vb2_plane_size(vb, 0), size);
return -EINVAL;
}
@@ -346,15 +342,14 @@ static int isc_start_streaming(struct vb2_queue *vq, unsigned int count)
/* Enable stream on the sub device */
ret = v4l2_subdev_call(isc->current_subdev->sd, video, s_stream, 1);
if (ret && ret != -ENOIOCTLCMD) {
- v4l2_err(&isc->v4l2_dev, "stream on failed in subdev %d\n",
- ret);
+ dev_err(isc->dev, "stream on failed in subdev %d\n", ret);
goto err_start_stream;
}
ret = pm_runtime_resume_and_get(isc->dev);
if (ret < 0) {
- v4l2_err(&isc->v4l2_dev, "RPM resume failed in subdev %d\n",
- ret);
+ dev_err(isc->dev, "RPM resume failed in subdev %d\n",
+ ret);
goto err_pm_get;
}
@@ -423,8 +418,7 @@ static void isc_stop_streaming(struct vb2_queue *vq)
/* Wait until the end of the current frame */
if (isc->cur_frm && !wait_for_completion_timeout(&isc->comp, 5 * HZ))
- v4l2_err(&isc->v4l2_dev,
- "Timeout waiting for end of the capture\n");
+ dev_err(isc->dev, "Timeout waiting for end of the capture\n");
mutex_unlock(&isc->awb_mutex);
@@ -436,7 +430,7 @@ static void isc_stop_streaming(struct vb2_queue *vq)
/* Disable stream on the sub device */
ret = v4l2_subdev_call(isc->current_subdev->sd, video, s_stream, 0);
if (ret && ret != -ENOIOCTLCMD)
- v4l2_err(&isc->v4l2_dev, "stream off failed in subdev\n");
+ dev_err(isc->dev, "stream off failed in subdev\n");
/* Release all active buffers */
spin_lock_irqsave(&isc->dma_queue_lock, flags);
@@ -620,28 +614,28 @@ static int isc_try_validate_formats(struct isc_device *isc)
break;
default:
/* any other different formats are not supported */
- v4l2_err(&isc->v4l2_dev, "Requested unsupported format.\n");
+ dev_err(isc->dev, "Requested unsupported format.\n");
ret = -EINVAL;
}
- v4l2_dbg(1, debug, &isc->v4l2_dev,
- "Format validation, requested rgb=%u, yuv=%u, grey=%u, bayer=%u\n",
- rgb, yuv, grey, bayer);
+ dev_dbg(isc->dev,
+ "Format validation, requested rgb=%u, yuv=%u, grey=%u, bayer=%u\n",
+ rgb, yuv, grey, bayer);
if (bayer &&
!ISC_IS_FORMAT_RAW(isc->try_config.sd_format->mbus_code)) {
- v4l2_err(&isc->v4l2_dev, "Cannot output RAW if we do not receive RAW.\n");
+ dev_err(isc->dev, "Cannot output RAW if we do not receive RAW.\n");
return -EINVAL;
}
if (grey && !ISC_IS_FORMAT_RAW(isc->try_config.sd_format->mbus_code) &&
!ISC_IS_FORMAT_GREY(isc->try_config.sd_format->mbus_code)) {
- v4l2_err(&isc->v4l2_dev, "Cannot output GREY if we do not receive RAW/GREY.\n");
+ dev_err(isc->dev, "Cannot output GREY if we do not receive RAW/GREY.\n");
return -EINVAL;
}
if ((rgb || bayer || yuv) &&
ISC_IS_FORMAT_GREY(isc->try_config.sd_format->mbus_code)) {
- v4l2_err(&isc->v4l2_dev, "Cannot convert GREY to another format.\n");
+ dev_err(isc->dev, "Cannot convert GREY to another format.\n");
return -EINVAL;
}
@@ -936,9 +930,9 @@ static int isc_set_fmt(struct isc_device *isc, struct v4l2_format *f)
isc->config = isc->try_config;
isc->fmt = isc->try_fmt;
- v4l2_dbg(1, debug, &isc->v4l2_dev, "ISC set_fmt to %.4s @%dx%d\n",
- (char *)&f->fmt.pix.pixelformat,
- f->fmt.pix.width, f->fmt.pix.height);
+ dev_dbg(isc->dev, "ISC set_fmt to %.4s @%dx%d\n",
+ (char *)&f->fmt.pix.pixelformat,
+ f->fmt.pix.width, f->fmt.pix.height);
return 0;
}
@@ -973,9 +967,9 @@ static int isc_validate(struct isc_device *isc)
/* Check if the format is not supported */
if (!sd_fmt) {
- v4l2_err(&isc->v4l2_dev,
- "Current subdevice is streaming a media bus code that is not supported 0x%x\n",
- format.format.code);
+ dev_err(isc->dev,
+ "Current subdevice is streaming a media bus code that is not supported 0x%x\n",
+ format.format.code);
return -EPIPE;
}
@@ -993,16 +987,16 @@ static int isc_validate(struct isc_device *isc)
/* Check if the frame size is the same. Otherwise we may overflow */
if (pixfmt->height != format.format.height ||
pixfmt->width != format.format.width) {
- v4l2_err(&isc->v4l2_dev,
- "ISC not configured with the proper frame size: %dx%d\n",
- format.format.width, format.format.height);
+ dev_err(isc->dev,
+ "ISC not configured with the proper frame size: %dx%d\n",
+ format.format.width, format.format.height);
return -EPIPE;
}
- v4l2_dbg(1, debug, &isc->v4l2_dev,
- "Identified subdev using format %.4s with %dx%d %d bpp\n",
- (char *)&sd_fmt->fourcc, pixfmt->width, pixfmt->height,
- isc->try_config.bpp);
+ dev_dbg(isc->dev,
+ "Identified subdev using format %.4s with %dx%d %d bpp\n",
+ (char *)&sd_fmt->fourcc, pixfmt->width, pixfmt->height,
+ isc->try_config.bpp);
/* Reset and restart AWB if the subdevice changed the format */
if (isc->try_config.sd_format && isc->config.sd_format &&
@@ -1027,7 +1021,7 @@ static int isc_validate(struct isc_device *isc)
isc->config = isc->try_config;
- v4l2_dbg(1, debug, &isc->v4l2_dev, "New ISC configuration in place\n");
+ dev_dbg(isc->dev, "New ISC configuration in place\n");
return 0;
}
@@ -1294,9 +1288,8 @@ static void isc_hist_count(struct isc_device *isc, u32 *min, u32 *max)
if (!*min)
*min = 1;
- v4l2_dbg(1, debug, &isc->v4l2_dev,
- "isc wb: hist_id %u, hist_count %u",
- ctrls->hist_id, *hist_count);
+ dev_dbg(isc->dev, "isc wb: hist_id %u, hist_count %u",
+ ctrls->hist_id, *hist_count);
}
static void isc_wb_update(struct isc_ctrls *ctrls)
@@ -1318,8 +1311,7 @@ static void isc_wb_update(struct isc_ctrls *ctrls)
(u64)hist_count[ISC_HIS_CFG_MODE_GB];
avg >>= 1;
- v4l2_dbg(1, debug, &isc->v4l2_dev,
- "isc wb: green components average %llu\n", avg);
+ dev_dbg(isc->dev, "isc wb: green components average %llu\n", avg);
/* Green histogram is null, nothing to do */
if (!avg)
@@ -1373,9 +1365,9 @@ static void isc_wb_update(struct isc_ctrls *ctrls)
else
gw_gain[c] = 1 << 9;
- v4l2_dbg(1, debug, &isc->v4l2_dev,
- "isc wb: component %d, s_gain %u, gw_gain %u\n",
- c, s_gain[c], gw_gain[c]);
+ dev_dbg(isc->dev,
+ "isc wb: component %d, s_gain %u, gw_gain %u\n",
+ c, s_gain[c], gw_gain[c]);
/* multiply both gains and adjust for decimals */
ctrls->gain[c] = s_gain[c] * gw_gain[c];
ctrls->gain[c] >>= 9;
@@ -1383,9 +1375,8 @@ static void isc_wb_update(struct isc_ctrls *ctrls)
/* make sure we are not out of range */
ctrls->gain[c] = clamp_val(ctrls->gain[c], 0, GENMASK(12, 0));
- v4l2_dbg(1, debug, &isc->v4l2_dev,
- "isc wb: component %d, final gain %u\n",
- c, ctrls->gain[c]);
+ dev_dbg(isc->dev, "isc wb: component %d, final gain %u\n",
+ c, ctrls->gain[c]);
}
}
@@ -1406,8 +1397,8 @@ static void isc_awb_work(struct work_struct *w)
isc_hist_count(isc, &min, &max);
- v4l2_dbg(1, debug, &isc->v4l2_dev,
- "isc wb mode %d: hist min %u , max %u\n", hist_id, min, max);
+ dev_dbg(isc->dev,
+ "isc wb mode %d: hist min %u , max %u\n", hist_id, min, max);
ctrls->hist_minmax[hist_id][HIST_MIN_INDEX] = min;
ctrls->hist_minmax[hist_id][HIST_MAX_INDEX] = max;
@@ -1446,8 +1437,8 @@ static void isc_awb_work(struct work_struct *w)
* we are basically done.
*/
if (ctrls->awb == ISC_WB_ONETIME) {
- v4l2_info(&isc->v4l2_dev,
- "Completed one time white-balance adjustment.\n");
+ dev_info(isc->dev,
+ "Completed one time white-balance adjustment.\n");
/* update the v4l2 controls values */
isc_update_v4l2_ctrls(isc);
ctrls->awb = ISC_WB_NONE;
@@ -1580,8 +1571,7 @@ static int isc_s_awb_ctrl(struct v4l2_ctrl *ctrl)
V4L2_CTRL_FLAG_INACTIVE)) {
ctrls->awb = ISC_WB_ONETIME;
isc_set_histogram(isc, true);
- v4l2_dbg(1, debug, &isc->v4l2_dev,
- "One time white-balance started.\n");
+ dev_dbg(isc->dev, "One time white-balance started.\n");
}
return 0;
}
@@ -1730,7 +1720,7 @@ static int isc_async_bound(struct v4l2_async_notifier *notifier,
int pad;
if (video_is_registered(&isc->video_dev)) {
- v4l2_err(&isc->v4l2_dev, "only supports one sub-device.\n");
+ dev_err(isc->dev, "only supports one sub-device.\n");
return -EBUSY;
}
@@ -1739,8 +1729,7 @@ static int isc_async_bound(struct v4l2_async_notifier *notifier,
pad = media_entity_get_fwnode_pad(&subdev->entity, asd->match.fwnode,
MEDIA_PAD_FL_SOURCE);
if (pad < 0) {
- v4l2_err(&isc->v4l2_dev, "failed to find pad for %s\n",
- subdev->name);
+ dev_err(isc->dev, "failed to find pad for %s\n", subdev->name);
return pad;
}
@@ -1813,7 +1802,7 @@ static int isc_async_complete(struct v4l2_async_notifier *notifier)
ret = v4l2_device_register_subdev_nodes(&isc->v4l2_dev);
if (ret < 0) {
- v4l2_err(&isc->v4l2_dev, "Failed to register subdev nodes\n");
+ dev_err(isc->dev, "Failed to register subdev nodes\n");
return ret;
}
@@ -1838,8 +1827,7 @@ static int isc_async_complete(struct v4l2_async_notifier *notifier)
ret = vb2_queue_init(q);
if (ret < 0) {
- v4l2_err(&isc->v4l2_dev,
- "vb2_queue_init() failed: %d\n", ret);
+ dev_err(isc->dev, "vb2_queue_init() failed: %d\n", ret);
goto isc_async_complete_err;
}
@@ -1850,13 +1838,13 @@ static int isc_async_complete(struct v4l2_async_notifier *notifier)
ret = isc_set_default_fmt(isc);
if (ret) {
- v4l2_err(&isc->v4l2_dev, "Could not set default format\n");
+ dev_err(isc->dev, "Could not set default format\n");
goto isc_async_complete_err;
}
ret = isc_ctrl_init(isc);
if (ret) {
- v4l2_err(&isc->v4l2_dev, "Init isc ctrols failed: %d\n", ret);
+ dev_err(isc->dev, "Init isc ctrols failed: %d\n", ret);
goto isc_async_complete_err;
}
@@ -1876,8 +1864,7 @@ static int isc_async_complete(struct v4l2_async_notifier *notifier)
ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
if (ret < 0) {
- v4l2_err(&isc->v4l2_dev,
- "video_register_device failed: %d\n", ret);
+ dev_err(isc->dev, "video_register_device failed: %d\n", ret);
goto isc_async_complete_err;
}
diff --git a/drivers/media/platform/nxp/dw100/dw100.c b/drivers/media/platform/nxp/dw100/dw100.c
index f6d48c36f386..189d60cd5ed1 100644
--- a/drivers/media/platform/nxp/dw100/dw100.c
+++ b/drivers/media/platform/nxp/dw100/dw100.c
@@ -1571,7 +1571,7 @@ static int dw100_probe(struct platform_device *pdev)
dev_name(&pdev->dev), dw_dev);
if (ret < 0) {
dev_err(&pdev->dev, "Failed to request irq: %d\n", ret);
- return ret;
+ goto err_pm;
}
ret = v4l2_device_register(&pdev->dev, &dw_dev->v4l2_dev);
diff --git a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c
index 6cd015a35f7c..f085f14d676a 100644
--- a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c
+++ b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c
@@ -2472,19 +2472,12 @@ static int mxc_jpeg_probe(struct platform_device *pdev)
jpeg->mode = mode;
/* Get clocks */
- jpeg->clk_ipg = devm_clk_get(dev, "ipg");
- if (IS_ERR(jpeg->clk_ipg)) {
- dev_err(dev, "failed to get clock: ipg\n");
- ret = PTR_ERR(jpeg->clk_ipg);
- goto err_clk;
- }
-
- jpeg->clk_per = devm_clk_get(dev, "per");
- if (IS_ERR(jpeg->clk_per)) {
- dev_err(dev, "failed to get clock: per\n");
- ret = PTR_ERR(jpeg->clk_per);
+ ret = devm_clk_bulk_get_all(&pdev->dev, &jpeg->clks);
+ if (ret < 0) {
+ dev_err(dev, "failed to get clock\n");
goto err_clk;
}
+ jpeg->num_clks = ret;
ret = mxc_jpeg_attach_pm_domains(jpeg);
if (ret < 0) {
@@ -2581,32 +2574,20 @@ static int mxc_jpeg_runtime_resume(struct device *dev)
struct mxc_jpeg_dev *jpeg = dev_get_drvdata(dev);
int ret;
- ret = clk_prepare_enable(jpeg->clk_ipg);
- if (ret < 0) {
- dev_err(dev, "failed to enable clock: ipg\n");
- goto err_ipg;
- }
-
- ret = clk_prepare_enable(jpeg->clk_per);
+ ret = clk_bulk_prepare_enable(jpeg->num_clks, jpeg->clks);
if (ret < 0) {
- dev_err(dev, "failed to enable clock: per\n");
- goto err_per;
+ dev_err(dev, "failed to enable clock\n");
+ return ret;
}
return 0;
-
-err_per:
- clk_disable_unprepare(jpeg->clk_ipg);
-err_ipg:
- return ret;
}
static int mxc_jpeg_runtime_suspend(struct device *dev)
{
struct mxc_jpeg_dev *jpeg = dev_get_drvdata(dev);
- clk_disable_unprepare(jpeg->clk_ipg);
- clk_disable_unprepare(jpeg->clk_per);
+ clk_bulk_disable_unprepare(jpeg->num_clks, jpeg->clks);
return 0;
}
diff --git a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.h b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.h
index 8fa8c0aec5a2..87157db78082 100644
--- a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.h
+++ b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.h
@@ -120,8 +120,8 @@ struct mxc_jpeg_dev {
spinlock_t hw_lock; /* hardware access lock */
unsigned int mode;
struct mutex lock; /* v4l2 ioctls serialization */
- struct clk *clk_ipg;
- struct clk *clk_per;
+ struct clk_bulk_data *clks;
+ int num_clks;
struct platform_device *pdev;
struct device *dev;
void __iomem *base_reg;
diff --git a/drivers/media/platform/nxp/imx-mipi-csis.c b/drivers/media/platform/nxp/imx-mipi-csis.c
index 905072871ed2..be2768a47995 100644
--- a/drivers/media/platform/nxp/imx-mipi-csis.c
+++ b/drivers/media/platform/nxp/imx-mipi-csis.c
@@ -327,10 +327,6 @@ struct mipi_csis_device {
u32 hs_settle;
u32 clk_settle;
- struct mutex lock; /* Protect csis_fmt and format_mbus */
- const struct csis_pix_format *csis_fmt;
- struct v4l2_mbus_framefmt format_mbus[CSIS_PADS_NUM];
-
spinlock_t slock; /* Protect events */
struct mipi_csis_event events[MIPI_CSIS_NUM_EVENTS];
struct dentry *debugfs_root;
@@ -559,10 +555,10 @@ static void mipi_csis_system_enable(struct mipi_csis_device *csis, int on)
mipi_csis_write(csis, MIPI_CSIS_DPHY_CMN_CTRL, val);
}
-/* Called with the csis.lock mutex held */
-static void __mipi_csis_set_format(struct mipi_csis_device *csis)
+static void __mipi_csis_set_format(struct mipi_csis_device *csis,
+ const struct v4l2_mbus_framefmt *format,
+ const struct csis_pix_format *csis_fmt)
{
- struct v4l2_mbus_framefmt *mf = &csis->format_mbus[CSIS_PAD_SINK];
u32 val;
/* Color format */
@@ -583,25 +579,26 @@ static void __mipi_csis_set_format(struct mipi_csis_device *csis)
*
* TODO: Verify which other formats require DUAL (or QUAD) modes.
*/
- if (csis->csis_fmt->data_type == MIPI_CSI2_DATA_TYPE_YUV422_8)
+ if (csis_fmt->data_type == MIPI_CSI2_DATA_TYPE_YUV422_8)
val |= MIPI_CSIS_ISPCFG_PIXEL_MODE_DUAL;
- val |= MIPI_CSIS_ISPCFG_FMT(csis->csis_fmt->data_type);
+ val |= MIPI_CSIS_ISPCFG_FMT(csis_fmt->data_type);
mipi_csis_write(csis, MIPI_CSIS_ISP_CONFIG_CH(0), val);
/* Pixel resolution */
- val = mf->width | (mf->height << 16);
+ val = format->width | (format->height << 16);
mipi_csis_write(csis, MIPI_CSIS_ISP_RESOL_CH(0), val);
}
-static int mipi_csis_calculate_params(struct mipi_csis_device *csis)
+static int mipi_csis_calculate_params(struct mipi_csis_device *csis,
+ const struct csis_pix_format *csis_fmt)
{
s64 link_freq;
u32 lane_rate;
/* Calculate the line rate from the pixel rate. */
link_freq = v4l2_get_link_freq(csis->src_sd->ctrl_handler,
- csis->csis_fmt->width,
+ csis_fmt->width,
csis->bus.num_data_lanes * 2);
if (link_freq < 0) {
dev_err(csis->dev, "Unable to obtain link frequency: %d\n",
@@ -643,7 +640,9 @@ static int mipi_csis_calculate_params(struct mipi_csis_device *csis)
return 0;
}
-static void mipi_csis_set_params(struct mipi_csis_device *csis)
+static void mipi_csis_set_params(struct mipi_csis_device *csis,
+ const struct v4l2_mbus_framefmt *format,
+ const struct csis_pix_format *csis_fmt)
{
int lanes = csis->bus.num_data_lanes;
u32 val;
@@ -655,7 +654,7 @@ static void mipi_csis_set_params(struct mipi_csis_device *csis)
val |= MIPI_CSIS_CMN_CTRL_INTER_MODE;
mipi_csis_write(csis, MIPI_CSIS_CMN_CTRL, val);
- __mipi_csis_set_format(csis);
+ __mipi_csis_set_format(csis, format, csis_fmt);
mipi_csis_write(csis, MIPI_CSIS_DPHY_CMN_CTRL,
MIPI_CSIS_DPHY_CMN_CTRL_HSSETTLE(csis->hs_settle) |
@@ -728,10 +727,12 @@ static int mipi_csis_clk_get(struct mipi_csis_device *csis)
return ret;
}
-static void mipi_csis_start_stream(struct mipi_csis_device *csis)
+static void mipi_csis_start_stream(struct mipi_csis_device *csis,
+ const struct v4l2_mbus_framefmt *format,
+ const struct csis_pix_format *csis_fmt)
{
mipi_csis_sw_reset(csis);
- mipi_csis_set_params(csis);
+ mipi_csis_set_params(csis, format, csis_fmt);
mipi_csis_system_enable(csis, true);
mipi_csis_enable_interrupts(csis, true);
}
@@ -935,120 +936,63 @@ static struct mipi_csis_device *sd_to_mipi_csis_device(struct v4l2_subdev *sdev)
static int mipi_csis_s_stream(struct v4l2_subdev *sd, int enable)
{
struct mipi_csis_device *csis = sd_to_mipi_csis_device(sd);
+ const struct v4l2_mbus_framefmt *format;
+ const struct csis_pix_format *csis_fmt;
+ struct v4l2_subdev_state *state;
int ret;
if (!enable) {
- mutex_lock(&csis->lock);
-
v4l2_subdev_call(csis->src_sd, video, s_stream, 0);
mipi_csis_stop_stream(csis);
if (csis->debug.enable)
mipi_csis_log_counters(csis, true);
- mutex_unlock(&csis->lock);
-
pm_runtime_put(csis->dev);
return 0;
}
- ret = mipi_csis_calculate_params(csis);
+ state = v4l2_subdev_lock_and_get_active_state(sd);
+
+ format = v4l2_subdev_get_pad_format(sd, state, CSIS_PAD_SINK);
+ csis_fmt = find_csis_format(format->code);
+
+ ret = mipi_csis_calculate_params(csis, csis_fmt);
if (ret < 0)
- return ret;
+ goto err_unlock;
mipi_csis_clear_counters(csis);
ret = pm_runtime_resume_and_get(csis->dev);
if (ret < 0)
- return ret;
+ goto err_unlock;
- mutex_lock(&csis->lock);
+ mipi_csis_start_stream(csis, format, csis_fmt);
- mipi_csis_start_stream(csis);
ret = v4l2_subdev_call(csis->src_sd, video, s_stream, 1);
if (ret < 0)
- goto error;
+ goto err_stop;
mipi_csis_log_counters(csis, true);
- mutex_unlock(&csis->lock);
+ v4l2_subdev_unlock_state(state);
return 0;
-error:
+err_stop:
mipi_csis_stop_stream(csis);
- mutex_unlock(&csis->lock);
pm_runtime_put(csis->dev);
+err_unlock:
+ v4l2_subdev_unlock_state(state);
return ret;
}
-static struct v4l2_mbus_framefmt *
-mipi_csis_get_format(struct mipi_csis_device *csis,
- struct v4l2_subdev_state *sd_state,
- enum v4l2_subdev_format_whence which,
- unsigned int pad)
-{
- if (which == V4L2_SUBDEV_FORMAT_TRY)
- return v4l2_subdev_get_try_format(&csis->sd, sd_state, pad);
-
- return &csis->format_mbus[pad];
-}
-
-static int mipi_csis_init_cfg(struct v4l2_subdev *sd,
- struct v4l2_subdev_state *sd_state)
-{
- struct mipi_csis_device *csis = sd_to_mipi_csis_device(sd);
- struct v4l2_mbus_framefmt *fmt_sink;
- struct v4l2_mbus_framefmt *fmt_source;
- enum v4l2_subdev_format_whence which;
-
- which = sd_state ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE;
- fmt_sink = mipi_csis_get_format(csis, sd_state, which, CSIS_PAD_SINK);
-
- fmt_sink->code = MEDIA_BUS_FMT_UYVY8_1X16;
- fmt_sink->width = MIPI_CSIS_DEF_PIX_WIDTH;
- fmt_sink->height = MIPI_CSIS_DEF_PIX_HEIGHT;
- fmt_sink->field = V4L2_FIELD_NONE;
-
- fmt_sink->colorspace = V4L2_COLORSPACE_SMPTE170M;
- fmt_sink->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt_sink->colorspace);
- fmt_sink->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt_sink->colorspace);
- fmt_sink->quantization =
- V4L2_MAP_QUANTIZATION_DEFAULT(false, fmt_sink->colorspace,
- fmt_sink->ycbcr_enc);
-
- fmt_source = mipi_csis_get_format(csis, sd_state, which,
- CSIS_PAD_SOURCE);
- *fmt_source = *fmt_sink;
-
- return 0;
-}
-
-static int mipi_csis_get_fmt(struct v4l2_subdev *sd,
- struct v4l2_subdev_state *sd_state,
- struct v4l2_subdev_format *sdformat)
-{
- struct mipi_csis_device *csis = sd_to_mipi_csis_device(sd);
- struct v4l2_mbus_framefmt *fmt;
-
- fmt = mipi_csis_get_format(csis, sd_state, sdformat->which,
- sdformat->pad);
-
- mutex_lock(&csis->lock);
- sdformat->format = *fmt;
- mutex_unlock(&csis->lock);
-
- return 0;
-}
-
static int mipi_csis_enum_mbus_code(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_mbus_code_enum *code)
{
- struct mipi_csis_device *csis = sd_to_mipi_csis_device(sd);
-
/*
* The CSIS can't transcode in any way, the source format is identical
* to the sink format.
@@ -1059,8 +1003,7 @@ static int mipi_csis_enum_mbus_code(struct v4l2_subdev *sd,
if (code->index > 0)
return -EINVAL;
- fmt = mipi_csis_get_format(csis, sd_state, code->which,
- code->pad);
+ fmt = v4l2_subdev_get_pad_format(sd, sd_state, code->pad);
code->code = fmt->code;
return 0;
}
@@ -1080,7 +1023,6 @@ static int mipi_csis_set_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *sdformat)
{
- struct mipi_csis_device *csis = sd_to_mipi_csis_device(sd);
struct csis_pix_format const *csis_fmt;
struct v4l2_mbus_framefmt *fmt;
unsigned int align;
@@ -1090,7 +1032,7 @@ static int mipi_csis_set_fmt(struct v4l2_subdev *sd,
* modified.
*/
if (sdformat->pad == CSIS_PAD_SOURCE)
- return mipi_csis_get_fmt(sd, sd_state, sdformat);
+ return v4l2_subdev_get_fmt(sd, sd_state, sdformat);
if (sdformat->pad != CSIS_PAD_SINK)
return -EINVAL;
@@ -1128,14 +1070,12 @@ static int mipi_csis_set_fmt(struct v4l2_subdev *sd,
&sdformat->format.height, 1,
CSIS_MAX_PIX_HEIGHT, 0, 0);
- fmt = mipi_csis_get_format(csis, sd_state, sdformat->which,
- sdformat->pad);
-
- mutex_lock(&csis->lock);
+ fmt = v4l2_subdev_get_pad_format(sd, sd_state, sdformat->pad);
fmt->code = csis_fmt->code;
fmt->width = sdformat->format.width;
fmt->height = sdformat->format.height;
+ fmt->field = V4L2_FIELD_NONE;
fmt->colorspace = sdformat->format.colorspace;
fmt->quantization = sdformat->format.quantization;
fmt->xfer_func = sdformat->format.xfer_func;
@@ -1144,48 +1084,68 @@ static int mipi_csis_set_fmt(struct v4l2_subdev *sd,
sdformat->format = *fmt;
/* Propagate the format from sink to source. */
- fmt = mipi_csis_get_format(csis, sd_state, sdformat->which,
- CSIS_PAD_SOURCE);
+ fmt = v4l2_subdev_get_pad_format(sd, sd_state, CSIS_PAD_SOURCE);
*fmt = sdformat->format;
/* The format on the source pad might change due to unpacking. */
fmt->code = csis_fmt->output;
- /* Store the CSIS format descriptor for active formats. */
- if (sdformat->which == V4L2_SUBDEV_FORMAT_ACTIVE)
- csis->csis_fmt = csis_fmt;
-
- mutex_unlock(&csis->lock);
-
return 0;
}
static int mipi_csis_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad,
struct v4l2_mbus_frame_desc *fd)
{
- struct mipi_csis_device *csis = sd_to_mipi_csis_device(sd);
struct v4l2_mbus_frame_desc_entry *entry = &fd->entry[0];
+ const struct csis_pix_format *csis_fmt;
+ const struct v4l2_mbus_framefmt *fmt;
+ struct v4l2_subdev_state *state;
if (pad != CSIS_PAD_SOURCE)
return -EINVAL;
+ state = v4l2_subdev_lock_and_get_active_state(sd);
+ fmt = v4l2_subdev_get_pad_format(sd, state, CSIS_PAD_SOURCE);
+ csis_fmt = find_csis_format(fmt->code);
+ v4l2_subdev_unlock_state(state);
+
+ if (!csis_fmt)
+ return -EPIPE;
+
fd->type = V4L2_MBUS_FRAME_DESC_TYPE_PARALLEL;
fd->num_entries = 1;
memset(entry, 0, sizeof(*entry));
- mutex_lock(&csis->lock);
-
entry->flags = 0;
- entry->pixelcode = csis->csis_fmt->code;
+ entry->pixelcode = csis_fmt->code;
entry->bus.csi2.vc = 0;
- entry->bus.csi2.dt = csis->csis_fmt->data_type;
-
- mutex_unlock(&csis->lock);
+ entry->bus.csi2.dt = csis_fmt->data_type;
return 0;
}
+static int mipi_csis_init_cfg(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state)
+{
+ struct v4l2_subdev_format fmt = {
+ .pad = CSIS_PAD_SINK,
+ };
+
+ fmt.format.code = mipi_csis_formats[0].code;
+ fmt.format.width = MIPI_CSIS_DEF_PIX_WIDTH;
+ fmt.format.height = MIPI_CSIS_DEF_PIX_HEIGHT;
+
+ fmt.format.colorspace = V4L2_COLORSPACE_SMPTE170M;
+ fmt.format.xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt.format.colorspace);
+ fmt.format.ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt.format.colorspace);
+ fmt.format.quantization =
+ V4L2_MAP_QUANTIZATION_DEFAULT(false, fmt.format.colorspace,
+ fmt.format.ycbcr_enc);
+
+ return mipi_csis_set_fmt(sd, sd_state, &fmt);
+}
+
static int mipi_csis_log_status(struct v4l2_subdev *sd)
{
struct mipi_csis_device *csis = sd_to_mipi_csis_device(sd);
@@ -1208,7 +1168,7 @@ static const struct v4l2_subdev_video_ops mipi_csis_video_ops = {
static const struct v4l2_subdev_pad_ops mipi_csis_pad_ops = {
.init_cfg = mipi_csis_init_cfg,
.enum_mbus_code = mipi_csis_enum_mbus_code,
- .get_fmt = mipi_csis_get_fmt,
+ .get_fmt = v4l2_subdev_get_fmt,
.set_fmt = mipi_csis_set_fmt,
.get_frame_desc = mipi_csis_get_frame_desc,
};
@@ -1348,40 +1308,34 @@ static int __maybe_unused mipi_csis_runtime_suspend(struct device *dev)
{
struct v4l2_subdev *sd = dev_get_drvdata(dev);
struct mipi_csis_device *csis = sd_to_mipi_csis_device(sd);
- int ret = 0;
-
- mutex_lock(&csis->lock);
+ int ret;
ret = mipi_csis_phy_disable(csis);
if (ret)
- goto unlock;
+ return -EAGAIN;
mipi_csis_clk_disable(csis);
-unlock:
- mutex_unlock(&csis->lock);
-
- return ret ? -EAGAIN : 0;
+ return 0;
}
static int __maybe_unused mipi_csis_runtime_resume(struct device *dev)
{
struct v4l2_subdev *sd = dev_get_drvdata(dev);
struct mipi_csis_device *csis = sd_to_mipi_csis_device(sd);
- int ret = 0;
-
- mutex_lock(&csis->lock);
+ int ret;
ret = mipi_csis_phy_enable(csis);
if (ret)
- goto unlock;
-
- mipi_csis_clk_enable(csis);
+ return -EAGAIN;
-unlock:
- mutex_unlock(&csis->lock);
+ ret = mipi_csis_clk_enable(csis);
+ if (ret) {
+ mipi_csis_phy_disable(csis);
+ return ret;
+ }
- return ret ? -EAGAIN : 0;
+ return 0;
}
static const struct dev_pm_ops mipi_csis_pm_ops = {
@@ -1396,6 +1350,7 @@ static const struct dev_pm_ops mipi_csis_pm_ops = {
static int mipi_csis_subdev_init(struct mipi_csis_device *csis)
{
struct v4l2_subdev *sd = &csis->sd;
+ int ret;
v4l2_subdev_init(sd, &mipi_csis_subdev_ops);
sd->owner = THIS_MODULE;
@@ -1417,15 +1372,21 @@ static int mipi_csis_subdev_init(struct mipi_csis_device *csis)
return -ENOENT;
}
- csis->csis_fmt = &mipi_csis_formats[0];
- mipi_csis_init_cfg(sd, NULL);
-
csis->pads[CSIS_PAD_SINK].flags = MEDIA_PAD_FL_SINK
| MEDIA_PAD_FL_MUST_CONNECT;
csis->pads[CSIS_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE
| MEDIA_PAD_FL_MUST_CONNECT;
- return media_entity_pads_init(&sd->entity, CSIS_PADS_NUM,
- csis->pads);
+ ret = media_entity_pads_init(&sd->entity, CSIS_PADS_NUM, csis->pads);
+ if (ret)
+ return ret;
+
+ ret = v4l2_subdev_init_finalize(sd);
+ if (ret) {
+ media_entity_cleanup(&sd->entity);
+ return ret;
+ }
+
+ return 0;
}
static int mipi_csis_parse_dt(struct mipi_csis_device *csis)
@@ -1450,7 +1411,6 @@ static int mipi_csis_probe(struct platform_device *pdev)
if (!csis)
return -ENOMEM;
- mutex_init(&csis->lock);
spin_lock_init(&csis->slock);
csis->dev = dev;
@@ -1496,20 +1456,20 @@ static int mipi_csis_probe(struct platform_device *pdev)
dev_name(dev), csis);
if (ret) {
dev_err(dev, "Interrupt request failed\n");
- goto disable_clock;
+ goto err_disable_clock;
}
/* Initialize and register the subdev. */
ret = mipi_csis_subdev_init(csis);
if (ret < 0)
- goto disable_clock;
+ goto err_disable_clock;
platform_set_drvdata(pdev, &csis->sd);
ret = mipi_csis_async_register(csis);
if (ret < 0) {
dev_err(dev, "async register failed: %d\n", ret);
- goto cleanup;
+ goto err_cleanup;
}
/* Initialize debugfs. */
@@ -1520,7 +1480,7 @@ static int mipi_csis_probe(struct platform_device *pdev)
if (!pm_runtime_enabled(dev)) {
ret = mipi_csis_runtime_resume(dev);
if (ret < 0)
- goto unregister_all;
+ goto err_unregister_all;
}
dev_info(dev, "lanes: %d, freq: %u\n",
@@ -1528,17 +1488,17 @@ static int mipi_csis_probe(struct platform_device *pdev)
return 0;
-unregister_all:
+err_unregister_all:
mipi_csis_debugfs_exit(csis);
-cleanup:
+err_cleanup:
+ v4l2_subdev_cleanup(&csis->sd);
media_entity_cleanup(&csis->sd.entity);
v4l2_async_nf_unregister(&csis->notifier);
v4l2_async_nf_cleanup(&csis->notifier);
v4l2_async_unregister_subdev(&csis->sd);
-disable_clock:
+err_disable_clock:
mipi_csis_clk_disable(csis);
fwnode_handle_put(csis->sd.fwnode);
- mutex_destroy(&csis->lock);
return ret;
}
@@ -1556,9 +1516,9 @@ static int mipi_csis_remove(struct platform_device *pdev)
pm_runtime_disable(&pdev->dev);
mipi_csis_runtime_suspend(&pdev->dev);
mipi_csis_clk_disable(csis);
+ v4l2_subdev_cleanup(&csis->sd);
media_entity_cleanup(&csis->sd.entity);
fwnode_handle_put(csis->sd.fwnode);
- mutex_destroy(&csis->lock);
pm_runtime_set_suspended(&pdev->dev);
return 0;
diff --git a/drivers/media/platform/nxp/imx-pxp.c b/drivers/media/platform/nxp/imx-pxp.c
index 689ae5e6ac62..fde3c36e5e1d 100644
--- a/drivers/media/platform/nxp/imx-pxp.c
+++ b/drivers/media/platform/nxp/imx-pxp.c
@@ -10,6 +10,7 @@
* Pawel Osciak, <pawel@osciak.com>
* Marek Szyprowski, <m.szyprowski@samsung.com>
*/
+#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
@@ -18,15 +19,18 @@
#include <linux/iopoll.h>
#include <linux/module.h>
#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
#include <linux/sched.h>
#include <linux/slab.h>
-#include <linux/platform_device.h>
-#include <media/v4l2-mem2mem.h>
-#include <media/v4l2-device.h>
-#include <media/v4l2-ioctl.h>
+#include <media/media-device.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-dma-contig.h>
#include "imx-pxp.h"
@@ -52,6 +56,11 @@ MODULE_PARM_DESC(debug, "activates debug info");
#define MEM2MEM_HFLIP (1 << 0)
#define MEM2MEM_VFLIP (1 << 1)
+#define PXP_VERSION_MAJOR(version) \
+ FIELD_GET(BM_PXP_VERSION_MAJOR, version)
+#define PXP_VERSION_MINOR(version) \
+ FIELD_GET(BM_PXP_VERSION_MINOR, version)
+
#define dprintk(dev, fmt, arg...) \
v4l2_dbg(1, debug, &dev->v4l2_dev, "%s: " fmt, __func__, ## arg)
@@ -168,14 +177,21 @@ enum {
V4L2_M2M_DST = 1,
};
-static struct pxp_fmt *find_format(struct v4l2_format *f)
+static const struct regmap_config pxp_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = HW_PXP_VERSION,
+};
+
+static struct pxp_fmt *find_format(unsigned int pixelformat)
{
struct pxp_fmt *fmt;
unsigned int k;
for (k = 0; k < NUM_FORMATS; k++) {
fmt = &formats[k];
- if (fmt->fourcc == f->fmt.pix.pixelformat)
+ if (fmt->fourcc == pixelformat)
break;
}
@@ -185,12 +201,23 @@ static struct pxp_fmt *find_format(struct v4l2_format *f)
return &formats[k];
}
+struct pxp_ctx;
+
+struct pxp_pdata {
+ u32 (*data_path_ctrl0)(struct pxp_ctx *ctx);
+};
+
struct pxp_dev {
struct v4l2_device v4l2_dev;
struct video_device vfd;
+#ifdef CONFIG_MEDIA_CONTROLLER
+ struct media_device mdev;
+#endif
struct clk *clk;
- void __iomem *mmio;
+ struct regmap *regmap;
+
+ const struct pxp_pdata *pdata;
atomic_t num_inst;
struct mutex dev_mutex;
@@ -234,6 +261,20 @@ static struct pxp_q_data *get_q_data(struct pxp_ctx *ctx,
return &ctx->q_data[V4L2_M2M_DST];
}
+static inline u32 pxp_read(struct pxp_dev *dev, u32 reg)
+{
+ u32 value;
+
+ regmap_read(dev->regmap, reg, &value);
+
+ return value;
+}
+
+static inline void pxp_write(struct pxp_dev *dev, u32 reg, u32 value)
+{
+ regmap_write(dev->regmap, reg, value);
+}
+
static u32 pxp_v4l2_pix_fmt_to_ps_format(u32 v4l2_pix_fmt)
{
switch (v4l2_pix_fmt) {
@@ -486,11 +527,11 @@ static void pxp_setup_csc(struct pxp_ctx *ctx)
csc1_coef = csc1_coef_smpte240m_lim;
}
- writel(csc1_coef[0], dev->mmio + HW_PXP_CSC1_COEF0);
- writel(csc1_coef[1], dev->mmio + HW_PXP_CSC1_COEF1);
- writel(csc1_coef[2], dev->mmio + HW_PXP_CSC1_COEF2);
+ pxp_write(dev, HW_PXP_CSC1_COEF0, csc1_coef[0]);
+ pxp_write(dev, HW_PXP_CSC1_COEF1, csc1_coef[1]);
+ pxp_write(dev, HW_PXP_CSC1_COEF2, csc1_coef[2]);
} else {
- writel(BM_PXP_CSC1_COEF0_BYPASS, dev->mmio + HW_PXP_CSC1_COEF0);
+ pxp_write(dev, HW_PXP_CSC1_COEF0, BM_PXP_CSC1_COEF0_BYPASS);
}
if (!pxp_v4l2_pix_fmt_is_yuv(ctx->q_data[V4L2_M2M_SRC].fmt->fourcc) &&
@@ -706,18 +747,95 @@ static void pxp_setup_csc(struct pxp_ctx *ctx)
BP_PXP_CSC2_CTRL_CSC_MODE;
}
- writel(csc2_ctrl, dev->mmio + HW_PXP_CSC2_CTRL);
- writel(csc2_coef[0], dev->mmio + HW_PXP_CSC2_COEF0);
- writel(csc2_coef[1], dev->mmio + HW_PXP_CSC2_COEF1);
- writel(csc2_coef[2], dev->mmio + HW_PXP_CSC2_COEF2);
- writel(csc2_coef[3], dev->mmio + HW_PXP_CSC2_COEF3);
- writel(csc2_coef[4], dev->mmio + HW_PXP_CSC2_COEF4);
- writel(csc2_coef[5], dev->mmio + HW_PXP_CSC2_COEF5);
+ pxp_write(dev, HW_PXP_CSC2_CTRL, csc2_ctrl);
+ pxp_write(dev, HW_PXP_CSC2_COEF0, csc2_coef[0]);
+ pxp_write(dev, HW_PXP_CSC2_COEF1, csc2_coef[1]);
+ pxp_write(dev, HW_PXP_CSC2_COEF2, csc2_coef[2]);
+ pxp_write(dev, HW_PXP_CSC2_COEF3, csc2_coef[3]);
+ pxp_write(dev, HW_PXP_CSC2_COEF4, csc2_coef[4]);
+ pxp_write(dev, HW_PXP_CSC2_COEF5, csc2_coef[5]);
} else {
- writel(BM_PXP_CSC2_CTRL_BYPASS, dev->mmio + HW_PXP_CSC2_CTRL);
+ pxp_write(dev, HW_PXP_CSC2_CTRL, BM_PXP_CSC2_CTRL_BYPASS);
}
}
+static u32 pxp_imx6ull_data_path_ctrl0(struct pxp_ctx *ctx)
+{
+ u32 ctrl0;
+
+ ctrl0 = 0;
+ ctrl0 |= BF_PXP_DATA_PATH_CTRL0_MUX15_SEL(3);
+ /* Bypass Dithering x3CH */
+ ctrl0 |= BF_PXP_DATA_PATH_CTRL0_MUX14_SEL(1);
+ ctrl0 |= BF_PXP_DATA_PATH_CTRL0_MUX13_SEL(3);
+ /* Select Rotation */
+ ctrl0 |= BF_PXP_DATA_PATH_CTRL0_MUX12_SEL(0);
+ /* Bypass LUT */
+ ctrl0 |= BF_PXP_DATA_PATH_CTRL0_MUX11_SEL(1);
+ ctrl0 |= BF_PXP_DATA_PATH_CTRL0_MUX10_SEL(3);
+ ctrl0 |= BF_PXP_DATA_PATH_CTRL0_MUX9_SEL(3);
+ /* Select CSC 2 */
+ ctrl0 |= BF_PXP_DATA_PATH_CTRL0_MUX8_SEL(0);
+ ctrl0 |= BF_PXP_DATA_PATH_CTRL0_MUX7_SEL(3);
+ ctrl0 |= BF_PXP_DATA_PATH_CTRL0_MUX6_SEL(3);
+ ctrl0 |= BF_PXP_DATA_PATH_CTRL0_MUX5_SEL(3);
+ ctrl0 |= BF_PXP_DATA_PATH_CTRL0_MUX4_SEL(3);
+ /* Bypass Rotation 2 */
+ ctrl0 |= BF_PXP_DATA_PATH_CTRL0_MUX3_SEL(0);
+ ctrl0 |= BF_PXP_DATA_PATH_CTRL0_MUX2_SEL(3);
+ ctrl0 |= BF_PXP_DATA_PATH_CTRL0_MUX1_SEL(3);
+ ctrl0 |= BF_PXP_DATA_PATH_CTRL0_MUX0_SEL(3);
+
+ return ctrl0;
+}
+
+static u32 pxp_imx7d_data_path_ctrl0(struct pxp_ctx *ctx)
+{
+ u32 ctrl0;
+
+ ctrl0 = 0;
+ ctrl0 |= BF_PXP_DATA_PATH_CTRL0_MUX15_SEL(3);
+ /* Select Rotation 0 */
+ ctrl0 |= BF_PXP_DATA_PATH_CTRL0_MUX14_SEL(0);
+ ctrl0 |= BF_PXP_DATA_PATH_CTRL0_MUX13_SEL(3);
+ /* Select MUX11 for Rotation 0 */
+ ctrl0 |= BF_PXP_DATA_PATH_CTRL0_MUX12_SEL(1);
+ /* Bypass LUT */
+ ctrl0 |= BF_PXP_DATA_PATH_CTRL0_MUX11_SEL(1);
+ ctrl0 |= BF_PXP_DATA_PATH_CTRL0_MUX10_SEL(3);
+ ctrl0 |= BF_PXP_DATA_PATH_CTRL0_MUX9_SEL(3);
+ /* Select CSC 2 */
+ ctrl0 |= BF_PXP_DATA_PATH_CTRL0_MUX8_SEL(0);
+ ctrl0 |= BF_PXP_DATA_PATH_CTRL0_MUX7_SEL(3);
+ /* Select Composite Alpha Blending/Color Key 0 for CSC 2 */
+ ctrl0 |= BF_PXP_DATA_PATH_CTRL0_MUX6_SEL(1);
+ ctrl0 |= BF_PXP_DATA_PATH_CTRL0_MUX5_SEL(3);
+ ctrl0 |= BF_PXP_DATA_PATH_CTRL0_MUX4_SEL(3);
+ /* Bypass Rotation 1 */
+ ctrl0 |= BF_PXP_DATA_PATH_CTRL0_MUX3_SEL(0);
+ ctrl0 |= BF_PXP_DATA_PATH_CTRL0_MUX2_SEL(3);
+ ctrl0 |= BF_PXP_DATA_PATH_CTRL0_MUX1_SEL(3);
+ ctrl0 |= BF_PXP_DATA_PATH_CTRL0_MUX0_SEL(3);
+
+ return ctrl0;
+}
+
+static void pxp_set_data_path(struct pxp_ctx *ctx)
+{
+ struct pxp_dev *dev = ctx->dev;
+ u32 ctrl0;
+ u32 ctrl1;
+
+ ctrl0 = dev->pdata->data_path_ctrl0(ctx);
+
+ ctrl1 = 0;
+ ctrl1 |= BF_PXP_DATA_PATH_CTRL1_MUX17_SEL(3);
+ ctrl1 |= BF_PXP_DATA_PATH_CTRL1_MUX16_SEL(3);
+
+ pxp_write(dev, HW_PXP_DATA_PATH_CTRL0, ctrl0);
+ pxp_write(dev, HW_PXP_DATA_PATH_CTRL1, ctrl1);
+}
+
static int pxp_start(struct pxp_ctx *ctx, struct vb2_v4l2_buffer *in_vb,
struct vb2_v4l2_buffer *out_vb)
{
@@ -871,67 +989,48 @@ static int pxp_start(struct pxp_ctx *ctx, struct vb2_v4l2_buffer *in_vb,
BF_PXP_PS_SCALE_XSCALE(xscale);
ps_offset = BF_PXP_PS_OFFSET_YOFFSET(0) | BF_PXP_PS_OFFSET_XOFFSET(0);
- writel(ctrl, dev->mmio + HW_PXP_CTRL);
+ pxp_write(dev, HW_PXP_CTRL, ctrl);
/* skip STAT */
- writel(out_ctrl, dev->mmio + HW_PXP_OUT_CTRL);
- writel(out_buf, dev->mmio + HW_PXP_OUT_BUF);
- writel(out_buf2, dev->mmio + HW_PXP_OUT_BUF2);
- writel(out_pitch, dev->mmio + HW_PXP_OUT_PITCH);
- writel(out_lrc, dev->mmio + HW_PXP_OUT_LRC);
- writel(out_ps_ulc, dev->mmio + HW_PXP_OUT_PS_ULC);
- writel(out_ps_lrc, dev->mmio + HW_PXP_OUT_PS_LRC);
- writel(as_ulc, dev->mmio + HW_PXP_OUT_AS_ULC);
- writel(as_lrc, dev->mmio + HW_PXP_OUT_AS_LRC);
- writel(ps_ctrl, dev->mmio + HW_PXP_PS_CTRL);
- writel(ps_buf, dev->mmio + HW_PXP_PS_BUF);
- writel(ps_ubuf, dev->mmio + HW_PXP_PS_UBUF);
- writel(ps_vbuf, dev->mmio + HW_PXP_PS_VBUF);
- writel(ps_pitch, dev->mmio + HW_PXP_PS_PITCH);
- writel(0x00ffffff, dev->mmio + HW_PXP_PS_BACKGROUND_0);
- writel(ps_scale, dev->mmio + HW_PXP_PS_SCALE);
- writel(ps_offset, dev->mmio + HW_PXP_PS_OFFSET);
+ pxp_write(dev, HW_PXP_OUT_CTRL, out_ctrl);
+ pxp_write(dev, HW_PXP_OUT_BUF, out_buf);
+ pxp_write(dev, HW_PXP_OUT_BUF2, out_buf2);
+ pxp_write(dev, HW_PXP_OUT_PITCH, out_pitch);
+ pxp_write(dev, HW_PXP_OUT_LRC, out_lrc);
+ pxp_write(dev, HW_PXP_OUT_PS_ULC, out_ps_ulc);
+ pxp_write(dev, HW_PXP_OUT_PS_LRC, out_ps_lrc);
+ pxp_write(dev, HW_PXP_OUT_AS_ULC, as_ulc);
+ pxp_write(dev, HW_PXP_OUT_AS_LRC, as_lrc);
+ pxp_write(dev, HW_PXP_PS_CTRL, ps_ctrl);
+ pxp_write(dev, HW_PXP_PS_BUF, ps_buf);
+ pxp_write(dev, HW_PXP_PS_UBUF, ps_ubuf);
+ pxp_write(dev, HW_PXP_PS_VBUF, ps_vbuf);
+ pxp_write(dev, HW_PXP_PS_PITCH, ps_pitch);
+ pxp_write(dev, HW_PXP_PS_BACKGROUND_0, 0x00ffffff);
+ pxp_write(dev, HW_PXP_PS_SCALE, ps_scale);
+ pxp_write(dev, HW_PXP_PS_OFFSET, ps_offset);
/* disable processed surface color keying */
- writel(0x00ffffff, dev->mmio + HW_PXP_PS_CLRKEYLOW_0);
- writel(0x00000000, dev->mmio + HW_PXP_PS_CLRKEYHIGH_0);
+ pxp_write(dev, HW_PXP_PS_CLRKEYLOW_0, 0x00ffffff);
+ pxp_write(dev, HW_PXP_PS_CLRKEYHIGH_0, 0x00000000);
/* disable alpha surface color keying */
- writel(0x00ffffff, dev->mmio + HW_PXP_AS_CLRKEYLOW_0);
- writel(0x00000000, dev->mmio + HW_PXP_AS_CLRKEYHIGH_0);
+ pxp_write(dev, HW_PXP_AS_CLRKEYLOW_0, 0x00ffffff);
+ pxp_write(dev, HW_PXP_AS_CLRKEYHIGH_0, 0x00000000);
/* setup CSC */
pxp_setup_csc(ctx);
/* bypass LUT */
- writel(BM_PXP_LUT_CTRL_BYPASS, dev->mmio + HW_PXP_LUT_CTRL);
-
- writel(BF_PXP_DATA_PATH_CTRL0_MUX15_SEL(0)|
- BF_PXP_DATA_PATH_CTRL0_MUX14_SEL(1)|
- BF_PXP_DATA_PATH_CTRL0_MUX13_SEL(0)|
- BF_PXP_DATA_PATH_CTRL0_MUX12_SEL(0)|
- BF_PXP_DATA_PATH_CTRL0_MUX11_SEL(0)|
- BF_PXP_DATA_PATH_CTRL0_MUX10_SEL(0)|
- BF_PXP_DATA_PATH_CTRL0_MUX9_SEL(1)|
- BF_PXP_DATA_PATH_CTRL0_MUX8_SEL(0)|
- BF_PXP_DATA_PATH_CTRL0_MUX7_SEL(0)|
- BF_PXP_DATA_PATH_CTRL0_MUX6_SEL(0)|
- BF_PXP_DATA_PATH_CTRL0_MUX5_SEL(0)|
- BF_PXP_DATA_PATH_CTRL0_MUX4_SEL(0)|
- BF_PXP_DATA_PATH_CTRL0_MUX3_SEL(0)|
- BF_PXP_DATA_PATH_CTRL0_MUX2_SEL(0)|
- BF_PXP_DATA_PATH_CTRL0_MUX1_SEL(0)|
- BF_PXP_DATA_PATH_CTRL0_MUX0_SEL(0),
- dev->mmio + HW_PXP_DATA_PATH_CTRL0);
- writel(BF_PXP_DATA_PATH_CTRL1_MUX17_SEL(1) |
- BF_PXP_DATA_PATH_CTRL1_MUX16_SEL(1),
- dev->mmio + HW_PXP_DATA_PATH_CTRL1);
-
- writel(0xffff, dev->mmio + HW_PXP_IRQ_MASK);
+ pxp_write(dev, HW_PXP_LUT_CTRL, BM_PXP_LUT_CTRL_BYPASS);
+
+ pxp_set_data_path(ctx);
+
+ pxp_write(dev, HW_PXP_IRQ_MASK, 0xffff);
/* ungate, enable PS/AS/OUT and PXP operation */
- writel(BM_PXP_CTRL_IRQ_ENABLE, dev->mmio + HW_PXP_CTRL_SET);
- writel(BM_PXP_CTRL_ENABLE | BM_PXP_CTRL_ENABLE_CSC2 |
- BM_PXP_CTRL_ENABLE_LUT | BM_PXP_CTRL_ENABLE_ROTATE0 |
- BM_PXP_CTRL_ENABLE_PS_AS_OUT, dev->mmio + HW_PXP_CTRL_SET);
+ pxp_write(dev, HW_PXP_CTRL_SET, BM_PXP_CTRL_IRQ_ENABLE);
+ pxp_write(dev, HW_PXP_CTRL_SET,
+ BM_PXP_CTRL_ENABLE | BM_PXP_CTRL_ENABLE_CSC2 |
+ BM_PXP_CTRL_ENABLE_ROTATE0 | BM_PXP_CTRL_ENABLE_PS_AS_OUT);
return 0;
}
@@ -1004,23 +1103,23 @@ static irqreturn_t pxp_irq_handler(int irq, void *dev_id)
struct pxp_dev *dev = dev_id;
u32 stat;
- stat = readl(dev->mmio + HW_PXP_STAT);
+ stat = pxp_read(dev, HW_PXP_STAT);
if (stat & BM_PXP_STAT_IRQ0) {
/* we expect x = 0, y = height, irq0 = 1 */
if (stat & ~(BM_PXP_STAT_BLOCKX | BM_PXP_STAT_BLOCKY |
BM_PXP_STAT_IRQ0))
dprintk(dev, "%s: stat = 0x%08x\n", __func__, stat);
- writel(BM_PXP_STAT_IRQ0, dev->mmio + HW_PXP_STAT_CLR);
+ pxp_write(dev, HW_PXP_STAT_CLR, BM_PXP_STAT_IRQ0);
pxp_job_finish(dev);
} else {
- u32 irq = readl(dev->mmio + HW_PXP_IRQ);
+ u32 irq = pxp_read(dev, HW_PXP_IRQ);
dprintk(dev, "%s: stat = 0x%08x\n", __func__, stat);
dprintk(dev, "%s: irq = 0x%08x\n", __func__, irq);
- writel(irq, dev->mmio + HW_PXP_IRQ_CLR);
+ pxp_write(dev, HW_PXP_IRQ_CLR, irq);
}
return IRQ_HANDLED;
@@ -1034,8 +1133,6 @@ static int pxp_querycap(struct file *file, void *priv,
{
strscpy(cap->driver, MEM2MEM_NAME, sizeof(cap->driver));
strscpy(cap->card, MEM2MEM_NAME, sizeof(cap->card));
- snprintf(cap->bus_info, sizeof(cap->bus_info),
- "platform:%s", MEM2MEM_NAME);
return 0;
}
@@ -1181,10 +1278,10 @@ static int pxp_try_fmt_vid_cap(struct file *file, void *priv,
struct pxp_fmt *fmt;
struct pxp_ctx *ctx = file2ctx(file);
- fmt = find_format(f);
+ fmt = find_format(f->fmt.pix.pixelformat);
if (!fmt) {
f->fmt.pix.pixelformat = formats[0].fourcc;
- fmt = find_format(f);
+ fmt = find_format(f->fmt.pix.pixelformat);
}
if (!(fmt->types & MEM2MEM_CAPTURE)) {
v4l2_err(&ctx->dev->v4l2_dev,
@@ -1209,10 +1306,10 @@ static int pxp_try_fmt_vid_out(struct file *file, void *priv,
struct pxp_fmt *fmt;
struct pxp_ctx *ctx = file2ctx(file);
- fmt = find_format(f);
+ fmt = find_format(f->fmt.pix.pixelformat);
if (!fmt) {
f->fmt.pix.pixelformat = formats[0].fourcc;
- fmt = find_format(f);
+ fmt = find_format(f->fmt.pix.pixelformat);
}
if (!(fmt->types & MEM2MEM_OUTPUT)) {
v4l2_err(&ctx->dev->v4l2_dev,
@@ -1245,7 +1342,7 @@ static int pxp_s_fmt(struct pxp_ctx *ctx, struct v4l2_format *f)
return -EBUSY;
}
- q_data->fmt = find_format(f);
+ q_data->fmt = find_format(f->fmt.pix.pixelformat);
q_data->width = f->fmt.pix.width;
q_data->height = f->fmt.pix.height;
q_data->bytesperline = f->fmt.pix.bytesperline;
@@ -1304,6 +1401,26 @@ static int pxp_s_fmt_vid_out(struct file *file, void *priv,
return 0;
}
+static int pxp_enum_framesizes(struct file *file, void *fh,
+ struct v4l2_frmsizeenum *fsize)
+{
+ if (fsize->index > 0)
+ return -EINVAL;
+
+ if (!find_format(fsize->pixel_format))
+ return -EINVAL;
+
+ fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
+ fsize->stepwise.min_width = MIN_W;
+ fsize->stepwise.max_width = MAX_W;
+ fsize->stepwise.step_width = 1 << ALIGN_W;
+ fsize->stepwise.min_height = MIN_H;
+ fsize->stepwise.max_height = MAX_H;
+ fsize->stepwise.step_height = 1 << ALIGN_H;
+
+ return 0;
+}
+
static u8 pxp_degrees_to_rot_mode(u32 degrees)
{
switch (degrees) {
@@ -1372,6 +1489,8 @@ static const struct v4l2_ioctl_ops pxp_ioctl_ops = {
.vidioc_try_fmt_vid_out = pxp_try_fmt_vid_out,
.vidioc_s_fmt_vid_out = pxp_s_fmt_vid_out,
+ .vidioc_enum_framesizes = pxp_enum_framesizes,
+
.vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs,
.vidioc_querybuf = v4l2_m2m_ioctl_querybuf,
.vidioc_qbuf = v4l2_m2m_ioctl_qbuf,
@@ -1644,18 +1763,18 @@ static int pxp_soft_reset(struct pxp_dev *dev)
int ret;
u32 val;
- writel(BM_PXP_CTRL_SFTRST, dev->mmio + HW_PXP_CTRL_CLR);
- writel(BM_PXP_CTRL_CLKGATE, dev->mmio + HW_PXP_CTRL_CLR);
+ pxp_write(dev, HW_PXP_CTRL_CLR, BM_PXP_CTRL_SFTRST);
+ pxp_write(dev, HW_PXP_CTRL_CLR, BM_PXP_CTRL_CLKGATE);
- writel(BM_PXP_CTRL_SFTRST, dev->mmio + HW_PXP_CTRL_SET);
+ pxp_write(dev, HW_PXP_CTRL_SET, BM_PXP_CTRL_SFTRST);
- ret = readl_poll_timeout(dev->mmio + HW_PXP_CTRL, val,
- val & BM_PXP_CTRL_CLKGATE, 0, 100);
+ ret = regmap_read_poll_timeout(dev->regmap, HW_PXP_CTRL, val,
+ val & BM_PXP_CTRL_CLKGATE, 0, 100);
if (ret < 0)
return ret;
- writel(BM_PXP_CTRL_SFTRST, dev->mmio + HW_PXP_CTRL_CLR);
- writel(BM_PXP_CTRL_CLKGATE, dev->mmio + HW_PXP_CTRL_CLR);
+ pxp_write(dev, HW_PXP_CTRL_CLR, BM_PXP_CTRL_SFTRST);
+ pxp_write(dev, HW_PXP_CTRL_CLR, BM_PXP_CTRL_CLKGATE);
return 0;
}
@@ -1664,13 +1783,17 @@ static int pxp_probe(struct platform_device *pdev)
{
struct pxp_dev *dev;
struct video_device *vfd;
+ u32 hw_version;
int irq;
int ret;
+ void __iomem *mmio;
dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
if (!dev)
return -ENOMEM;
+ dev->pdata = of_device_get_match_data(&pdev->dev);
+
dev->clk = devm_clk_get(&pdev->dev, "axi");
if (IS_ERR(dev->clk)) {
ret = PTR_ERR(dev->clk);
@@ -1678,9 +1801,11 @@ static int pxp_probe(struct platform_device *pdev)
return ret;
}
- dev->mmio = devm_platform_ioremap_resource(pdev, 0);
- if (IS_ERR(dev->mmio))
- return PTR_ERR(dev->mmio);
+ mmio = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(mmio))
+ return PTR_ERR(mmio);
+ dev->regmap = devm_regmap_init_mmio(&pdev->dev, mmio,
+ &pxp_regmap_config);
irq = platform_get_irq(pdev, 0);
if (irq < 0)
@@ -1688,8 +1813,8 @@ static int pxp_probe(struct platform_device *pdev)
spin_lock_init(&dev->irqlock);
- ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, pxp_irq_handler,
- IRQF_ONESHOT, dev_name(&pdev->dev), dev);
+ ret = devm_request_irq(&pdev->dev, irq, pxp_irq_handler, 0,
+ dev_name(&pdev->dev), dev);
if (ret < 0) {
dev_err(&pdev->dev, "Failed to request irq: %d\n", ret);
return ret;
@@ -1705,6 +1830,10 @@ static int pxp_probe(struct platform_device *pdev)
goto err_clk;
}
+ hw_version = pxp_read(dev, HW_PXP_VERSION);
+ dev_dbg(&pdev->dev, "PXP Version %u.%u\n",
+ PXP_VERSION_MAJOR(hw_version), PXP_VERSION_MINOR(hw_version));
+
ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
if (ret)
goto err_clk;
@@ -1737,8 +1866,34 @@ static int pxp_probe(struct platform_device *pdev)
goto err_m2m;
}
+#ifdef CONFIG_MEDIA_CONTROLLER
+ dev->mdev.dev = &pdev->dev;
+ strscpy(dev->mdev.model, MEM2MEM_NAME, sizeof(dev->mdev.model));
+ media_device_init(&dev->mdev);
+ dev->v4l2_dev.mdev = &dev->mdev;
+
+ ret = v4l2_m2m_register_media_controller(dev->m2m_dev, vfd,
+ MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to initialize media device\n");
+ goto err_vfd;
+ }
+
+ ret = media_device_register(&dev->mdev);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to register media device\n");
+ goto err_m2m_mc;
+ }
+#endif
+
return 0;
+#ifdef CONFIG_MEDIA_CONTROLLER
+err_m2m_mc:
+ v4l2_m2m_unregister_media_controller(dev->m2m_dev);
+err_vfd:
+ video_unregister_device(vfd);
+#endif
err_m2m:
v4l2_m2m_release(dev->m2m_dev);
err_v4l2:
@@ -1753,12 +1908,17 @@ static int pxp_remove(struct platform_device *pdev)
{
struct pxp_dev *dev = platform_get_drvdata(pdev);
- writel(BM_PXP_CTRL_CLKGATE, dev->mmio + HW_PXP_CTRL_SET);
- writel(BM_PXP_CTRL_SFTRST, dev->mmio + HW_PXP_CTRL_SET);
+ pxp_write(dev, HW_PXP_CTRL_SET, BM_PXP_CTRL_CLKGATE);
+ pxp_write(dev, HW_PXP_CTRL_SET, BM_PXP_CTRL_SFTRST);
clk_disable_unprepare(dev->clk);
v4l2_info(&dev->v4l2_dev, "Removing " MEM2MEM_NAME);
+
+#ifdef CONFIG_MEDIA_CONTROLLER
+ media_device_unregister(&dev->mdev);
+ v4l2_m2m_unregister_media_controller(dev->m2m_dev);
+#endif
video_unregister_device(&dev->vfd);
v4l2_m2m_release(dev->m2m_dev);
v4l2_device_unregister(&dev->v4l2_dev);
@@ -1766,8 +1926,17 @@ static int pxp_remove(struct platform_device *pdev)
return 0;
}
+static const struct pxp_pdata pxp_imx6ull_pdata = {
+ .data_path_ctrl0 = pxp_imx6ull_data_path_ctrl0,
+};
+
+static const struct pxp_pdata pxp_imx7d_pdata = {
+ .data_path_ctrl0 = pxp_imx7d_data_path_ctrl0,
+};
+
static const struct of_device_id pxp_dt_ids[] = {
- { .compatible = "fsl,imx6ull-pxp", .data = NULL },
+ { .compatible = "fsl,imx6ull-pxp", .data = &pxp_imx6ull_pdata },
+ { .compatible = "fsl,imx7d-pxp", .data = &pxp_imx7d_pdata },
{ },
};
MODULE_DEVICE_TABLE(of, pxp_dt_ids);
diff --git a/drivers/media/platform/nxp/imx7-media-csi.c b/drivers/media/platform/nxp/imx7-media-csi.c
index 886374d3a6ff..c22bf5c827e7 100644
--- a/drivers/media/platform/nxp/imx7-media-csi.c
+++ b/drivers/media/platform/nxp/imx7-media-csi.c
@@ -211,7 +211,6 @@ struct imx7_csi {
int irq;
struct clk *mclk;
- struct mutex lock; /* Protects is_streaming, format_mbus, cc */
spinlock_t irqlock; /* Protects last_eof */
/* Media and V4L2 device */
@@ -227,9 +226,6 @@ struct imx7_csi {
struct v4l2_subdev sd;
struct media_pad pad[IMX7_CSI_PADS_NUM];
- struct v4l2_mbus_framefmt format_mbus[IMX7_CSI_PADS_NUM];
- const struct imx7_csi_pixfmt *cc[IMX7_CSI_PADS_NUM];
-
/* Video device */
struct video_device *vdev; /* Video device */
struct media_pad vdev_pad; /* Video device pad */
@@ -510,7 +506,8 @@ static void imx7_csi_dma_stop(struct imx7_csi *csi)
imx7_csi_hw_disable_irq(csi);
}
-static void imx7_csi_configure(struct imx7_csi *csi)
+static void imx7_csi_configure(struct imx7_csi *csi,
+ struct v4l2_subdev_state *sd_state)
{
struct v4l2_pix_format *out_pix = &csi->vdev_fmt;
int width = out_pix->width;
@@ -541,12 +538,17 @@ static void imx7_csi_configure(struct imx7_csi *csi)
out_pix->pixelformat == V4L2_PIX_FMT_YUYV)
width *= 2;
} else {
+ const struct v4l2_mbus_framefmt *sink_fmt;
+
+ sink_fmt = v4l2_subdev_get_pad_format(&csi->sd, sd_state,
+ IMX7_CSI_PAD_SINK);
+
cr1 = BIT_SOF_POL | BIT_REDGE | BIT_HSYNC_POL | BIT_FCC
| BIT_MCLKDIV(1) | BIT_MCLKEN;
cr18 |= BIT_DATA_FROM_MIPI;
- switch (csi->format_mbus[IMX7_CSI_PAD_SINK].code) {
+ switch (sink_fmt->code) {
case MEDIA_BUS_FMT_Y8_1X8:
case MEDIA_BUS_FMT_SBGGR8_1X8:
case MEDIA_BUS_FMT_SGBRG8_1X8:
@@ -627,7 +629,8 @@ static void imx7_csi_configure(struct imx7_csi *csi)
imx7_csi_reg_write(csi, stride, CSI_CSIFBUF_PARA);
}
-static int imx7_csi_init(struct imx7_csi *csi)
+static int imx7_csi_init(struct imx7_csi *csi,
+ struct v4l2_subdev_state *sd_state)
{
int ret;
@@ -635,11 +638,13 @@ static int imx7_csi_init(struct imx7_csi *csi)
if (ret < 0)
return ret;
- imx7_csi_configure(csi);
+ imx7_csi_configure(csi, sd_state);
ret = imx7_csi_dma_setup(csi);
- if (ret < 0)
+ if (ret < 0) {
+ clk_disable_unprepare(csi->mclk);
return ret;
+ }
return 0;
}
@@ -1411,14 +1416,15 @@ static void imx7_csi_video_buf_queue(struct vb2_buffer *vb)
static int imx7_csi_video_validate_fmt(struct imx7_csi *csi)
{
- struct v4l2_subdev_format fmt_src;
+ struct v4l2_subdev_format fmt_src = {
+ .pad = IMX7_CSI_PAD_SRC,
+ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
+ };
const struct imx7_csi_pixfmt *cc;
int ret;
/* Retrieve the media bus format on the source subdev. */
- fmt_src.pad = IMX7_CSI_PAD_SRC;
- fmt_src.which = V4L2_SUBDEV_FORMAT_ACTIVE;
- ret = v4l2_subdev_call(&csi->sd, pad, get_fmt, NULL, &fmt_src);
+ ret = v4l2_subdev_call_state_active(&csi->sd, pad, get_fmt, &fmt_src);
if (ret)
return ret;
@@ -1599,17 +1605,15 @@ static struct imx7_csi_vb2_buffer *imx7_csi_video_next_buf(struct imx7_csi *csi)
static int imx7_csi_video_init_format(struct imx7_csi *csi)
{
- struct v4l2_subdev_format fmt_src = {
- .pad = IMX7_CSI_PAD_SRC,
- .which = V4L2_SUBDEV_FORMAT_ACTIVE,
- };
- fmt_src.format.code = IMX7_CSI_DEF_MBUS_CODE;
- fmt_src.format.width = IMX7_CSI_DEF_PIX_WIDTH;
- fmt_src.format.height = IMX7_CSI_DEF_PIX_HEIGHT;
+ struct v4l2_mbus_framefmt format = { };
+
+ format.code = IMX7_CSI_DEF_MBUS_CODE;
+ format.width = IMX7_CSI_DEF_PIX_WIDTH;
+ format.height = IMX7_CSI_DEF_PIX_HEIGHT;
- imx7_csi_mbus_fmt_to_pix_fmt(&csi->vdev_fmt, &fmt_src.format, NULL);
- csi->vdev_compose.width = fmt_src.format.width;
- csi->vdev_compose.height = fmt_src.format.height;
+ imx7_csi_mbus_fmt_to_pix_fmt(&csi->vdev_fmt, &format, NULL);
+ csi->vdev_compose.width = format.width;
+ csi->vdev_compose.height = format.height;
csi->vdev_cc = imx7_csi_find_pixel_format(csi->vdev_fmt.pixelformat);
@@ -1728,20 +1732,13 @@ static int imx7_csi_video_init(struct imx7_csi *csi)
static int imx7_csi_s_stream(struct v4l2_subdev *sd, int enable)
{
struct imx7_csi *csi = v4l2_get_subdevdata(sd);
+ struct v4l2_subdev_state *sd_state;
int ret = 0;
- mutex_lock(&csi->lock);
-
- if (!csi->src_sd) {
- ret = -EPIPE;
- goto out_unlock;
- }
-
- if (csi->is_streaming == !!enable)
- goto out_unlock;
+ sd_state = v4l2_subdev_lock_and_get_active_state(sd);
if (enable) {
- ret = imx7_csi_init(csi);
+ ret = imx7_csi_init(csi, sd_state);
if (ret < 0)
goto out_unlock;
@@ -1763,29 +1760,14 @@ static int imx7_csi_s_stream(struct v4l2_subdev *sd, int enable)
csi->is_streaming = !!enable;
out_unlock:
- mutex_unlock(&csi->lock);
+ v4l2_subdev_unlock_state(sd_state);
return ret;
}
-static struct v4l2_mbus_framefmt *
-imx7_csi_get_format(struct imx7_csi *csi,
- struct v4l2_subdev_state *sd_state,
- unsigned int pad,
- enum v4l2_subdev_format_whence which)
-{
- if (which == V4L2_SUBDEV_FORMAT_TRY)
- return v4l2_subdev_get_try_format(&csi->sd, sd_state, pad);
-
- return &csi->format_mbus[pad];
-}
-
static int imx7_csi_init_cfg(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state)
{
- const enum v4l2_subdev_format_whence which =
- sd_state ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE;
- struct imx7_csi *csi = v4l2_get_subdevdata(sd);
const struct imx7_csi_pixfmt *cc;
int i;
@@ -1793,7 +1775,7 @@ static int imx7_csi_init_cfg(struct v4l2_subdev *sd,
for (i = 0; i < IMX7_CSI_PADS_NUM; i++) {
struct v4l2_mbus_framefmt *mf =
- imx7_csi_get_format(csi, sd_state, i, which);
+ v4l2_subdev_get_pad_format(sd, sd_state, i);
mf->code = IMX7_CSI_DEF_MBUS_CODE;
mf->width = IMX7_CSI_DEF_PIX_WIDTH;
@@ -1805,8 +1787,6 @@ static int imx7_csi_init_cfg(struct v4l2_subdev *sd,
mf->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(mf->colorspace);
mf->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(!cc->yuv,
mf->colorspace, mf->ycbcr_enc);
-
- csi->cc[i] = cc;
}
return 0;
@@ -1816,59 +1796,30 @@ static int imx7_csi_enum_mbus_code(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_mbus_code_enum *code)
{
- struct imx7_csi *csi = v4l2_get_subdevdata(sd);
struct v4l2_mbus_framefmt *in_fmt;
int ret = 0;
- mutex_lock(&csi->lock);
-
- in_fmt = imx7_csi_get_format(csi, sd_state, IMX7_CSI_PAD_SINK,
- code->which);
+ in_fmt = v4l2_subdev_get_pad_format(sd, sd_state, IMX7_CSI_PAD_SINK);
switch (code->pad) {
case IMX7_CSI_PAD_SINK:
ret = imx7_csi_enum_mbus_formats(&code->code, code->index);
break;
+
case IMX7_CSI_PAD_SRC:
if (code->index != 0) {
ret = -EINVAL;
- goto out_unlock;
+ break;
}
code->code = in_fmt->code;
break;
- default:
- ret = -EINVAL;
- }
-
-out_unlock:
- mutex_unlock(&csi->lock);
-
- return ret;
-}
-static int imx7_csi_get_fmt(struct v4l2_subdev *sd,
- struct v4l2_subdev_state *sd_state,
- struct v4l2_subdev_format *sdformat)
-{
- struct imx7_csi *csi = v4l2_get_subdevdata(sd);
- struct v4l2_mbus_framefmt *fmt;
- int ret = 0;
-
- mutex_lock(&csi->lock);
-
- fmt = imx7_csi_get_format(csi, sd_state, sdformat->pad,
- sdformat->which);
- if (!fmt) {
+ default:
ret = -EINVAL;
- goto out_unlock;
+ break;
}
- sdformat->format = *fmt;
-
-out_unlock:
- mutex_unlock(&csi->lock);
-
return ret;
}
@@ -1918,19 +1869,16 @@ static void imx7_csi_try_colorimetry(struct v4l2_mbus_framefmt *tryfmt)
tryfmt->ycbcr_enc);
}
-static int imx7_csi_try_fmt(struct imx7_csi *csi,
- struct v4l2_subdev_state *sd_state,
- struct v4l2_subdev_format *sdformat,
- const struct imx7_csi_pixfmt **cc)
+static void imx7_csi_try_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_format *sdformat,
+ const struct imx7_csi_pixfmt **cc)
{
const struct imx7_csi_pixfmt *in_cc;
struct v4l2_mbus_framefmt *in_fmt;
u32 code;
- in_fmt = imx7_csi_get_format(csi, sd_state, IMX7_CSI_PAD_SINK,
- sdformat->which);
- if (!in_fmt)
- return -EINVAL;
+ in_fmt = v4l2_subdev_get_pad_format(sd, sd_state, IMX7_CSI_PAD_SINK);
switch (sdformat->pad) {
case IMX7_CSI_PAD_SRC:
@@ -1947,6 +1895,7 @@ static int imx7_csi_try_fmt(struct imx7_csi *csi,
sdformat->format.quantization = in_fmt->quantization;
sdformat->format.ycbcr_enc = in_fmt->ycbcr_enc;
break;
+
case IMX7_CSI_PAD_SINK:
*cc = imx7_csi_find_mbus_format(sdformat->format.code);
if (!*cc) {
@@ -1958,13 +1907,9 @@ static int imx7_csi_try_fmt(struct imx7_csi *csi,
if (sdformat->format.field != V4L2_FIELD_INTERLACED)
sdformat->format.field = V4L2_FIELD_NONE;
break;
- default:
- return -EINVAL;
}
imx7_csi_try_colorimetry(&sdformat->format);
-
- return 0;
}
static int imx7_csi_set_fmt(struct v4l2_subdev *sd,
@@ -1977,28 +1922,13 @@ static int imx7_csi_set_fmt(struct v4l2_subdev *sd,
const struct imx7_csi_pixfmt *cc;
struct v4l2_mbus_framefmt *fmt;
struct v4l2_subdev_format format;
- int ret = 0;
-
- if (sdformat->pad >= IMX7_CSI_PADS_NUM)
- return -EINVAL;
-
- mutex_lock(&csi->lock);
- if (csi->is_streaming) {
- ret = -EBUSY;
- goto out_unlock;
- }
+ if (csi->is_streaming)
+ return -EBUSY;
- ret = imx7_csi_try_fmt(csi, sd_state, sdformat, &cc);
- if (ret < 0)
- goto out_unlock;
+ imx7_csi_try_fmt(sd, sd_state, sdformat, &cc);
- fmt = imx7_csi_get_format(csi, sd_state, sdformat->pad,
- sdformat->which);
- if (!fmt) {
- ret = -EINVAL;
- goto out_unlock;
- }
+ fmt = v4l2_subdev_get_pad_format(sd, sd_state, sdformat->pad);
*fmt = sdformat->format;
@@ -2007,25 +1937,14 @@ static int imx7_csi_set_fmt(struct v4l2_subdev *sd,
format.pad = IMX7_CSI_PAD_SRC;
format.which = sdformat->which;
format.format = sdformat->format;
- if (imx7_csi_try_fmt(csi, sd_state, &format, &outcc)) {
- ret = -EINVAL;
- goto out_unlock;
- }
- outfmt = imx7_csi_get_format(csi, sd_state, IMX7_CSI_PAD_SRC,
- sdformat->which);
- *outfmt = format.format;
+ imx7_csi_try_fmt(sd, sd_state, &format, &outcc);
- if (sdformat->which == V4L2_SUBDEV_FORMAT_ACTIVE)
- csi->cc[IMX7_CSI_PAD_SRC] = outcc;
+ outfmt = v4l2_subdev_get_pad_format(sd, sd_state,
+ IMX7_CSI_PAD_SRC);
+ *outfmt = format.format;
}
- if (sdformat->which == V4L2_SUBDEV_FORMAT_ACTIVE)
- csi->cc[sdformat->pad] = cc;
-
-out_unlock:
- mutex_unlock(&csi->lock);
-
- return ret;
+ return 0;
}
static int imx7_csi_pad_link_validate(struct v4l2_subdev *sd,
@@ -2038,9 +1957,6 @@ static int imx7_csi_pad_link_validate(struct v4l2_subdev *sd,
unsigned int i;
int ret;
- if (!csi->src_sd)
- return -EPIPE;
-
/*
* Validate the source link, and record whether the source uses the
* parallel input or the CSI-2 receiver.
@@ -2128,7 +2044,7 @@ static const struct v4l2_subdev_video_ops imx7_csi_video_ops = {
static const struct v4l2_subdev_pad_ops imx7_csi_pad_ops = {
.init_cfg = imx7_csi_init_cfg,
.enum_mbus_code = imx7_csi_enum_mbus_code,
- .get_fmt = imx7_csi_get_fmt,
+ .get_fmt = v4l2_subdev_get_fmt,
.set_fmt = imx7_csi_set_fmt,
.link_validate = imx7_csi_pad_link_validate,
};
@@ -2201,7 +2117,7 @@ static int imx7_csi_async_register(struct imx7_csi *csi)
ret = PTR_ERR(asd);
/* OK if asd already exists */
if (ret != -EEXIST)
- return ret;
+ goto error;
}
}
@@ -2209,15 +2125,20 @@ static int imx7_csi_async_register(struct imx7_csi *csi)
ret = v4l2_async_nf_register(&csi->v4l2_dev, &csi->notifier);
if (ret)
- return ret;
+ goto error;
return 0;
+
+error:
+ v4l2_async_nf_cleanup(&csi->notifier);
+ return ret;
}
static void imx7_csi_media_cleanup(struct imx7_csi *csi)
{
v4l2_device_unregister(&csi->v4l2_dev);
media_device_unregister(&csi->mdev);
+ v4l2_subdev_cleanup(&csi->sd);
media_device_cleanup(&csi->mdev);
}
@@ -2285,6 +2206,10 @@ static int imx7_csi_media_init(struct imx7_csi *csi)
if (ret)
goto error;
+ ret = v4l2_subdev_init_finalize(&csi->sd);
+ if (ret)
+ goto error;
+
ret = v4l2_device_register_subdev(&csi->v4l2_dev, &csi->sd);
if (ret)
goto error;
@@ -2310,27 +2235,22 @@ static int imx7_csi_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, csi);
spin_lock_init(&csi->irqlock);
- mutex_init(&csi->lock);
/* Acquire resources and install interrupt handler. */
csi->mclk = devm_clk_get(&pdev->dev, "mclk");
if (IS_ERR(csi->mclk)) {
ret = PTR_ERR(csi->mclk);
dev_err(dev, "Failed to get mclk: %d", ret);
- goto destroy_mutex;
+ return ret;
}
csi->irq = platform_get_irq(pdev, 0);
- if (csi->irq < 0) {
- ret = csi->irq;
- goto destroy_mutex;
- }
+ if (csi->irq < 0)
+ return csi->irq;
csi->regbase = devm_platform_ioremap_resource(pdev, 0);
- if (IS_ERR(csi->regbase)) {
- ret = PTR_ERR(csi->regbase);
- goto destroy_mutex;
- }
+ if (IS_ERR(csi->regbase))
+ return PTR_ERR(csi->regbase);
csi->model = (enum imx_csi_model)(uintptr_t)of_device_get_match_data(&pdev->dev);
@@ -2338,34 +2258,23 @@ static int imx7_csi_probe(struct platform_device *pdev)
(void *)csi);
if (ret < 0) {
dev_err(dev, "Request CSI IRQ failed.\n");
- goto destroy_mutex;
+ return ret;
}
/* Initialize all the media device infrastructure. */
ret = imx7_csi_media_init(csi);
if (ret)
- goto destroy_mutex;
-
- /* Set the default mbus formats. */
- ret = imx7_csi_init_cfg(&csi->sd, NULL);
- if (ret)
- goto media_cleanup;
+ return ret;
ret = imx7_csi_async_register(csi);
if (ret)
- goto subdev_notifier_cleanup;
+ goto err_media_cleanup;
return 0;
-subdev_notifier_cleanup:
- v4l2_async_nf_unregister(&csi->notifier);
- v4l2_async_nf_cleanup(&csi->notifier);
-media_cleanup:
+err_media_cleanup:
imx7_csi_media_cleanup(csi);
-destroy_mutex:
- mutex_destroy(&csi->lock);
-
return ret;
}
@@ -2379,8 +2288,6 @@ static int imx7_csi_remove(struct platform_device *pdev)
v4l2_async_nf_cleanup(&csi->notifier);
v4l2_async_unregister_subdev(&csi->sd);
- mutex_destroy(&csi->lock);
-
return 0;
}
diff --git a/drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c b/drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c
index 451a4c9b3d30..04baa80494c6 100644
--- a/drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c
+++ b/drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c
@@ -429,7 +429,8 @@ static void csiphy_gen2_config_lanes(struct csiphy_device *csiphy,
array_size = ARRAY_SIZE(lane_regs_sm8250[0]);
break;
default:
- unreachable();
+ WARN(1, "unknown cspi version\n");
+ return;
}
for (l = 0; l < 5; l++) {
diff --git a/drivers/media/platform/qcom/venus/firmware.c b/drivers/media/platform/qcom/venus/firmware.c
index 142d4c74017c..61ff20a7e935 100644
--- a/drivers/media/platform/qcom/venus/firmware.c
+++ b/drivers/media/platform/qcom/venus/firmware.c
@@ -12,7 +12,7 @@
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/of_device.h>
-#include <linux/qcom_scm.h>
+#include <linux/firmware/qcom/qcom_scm.h>
#include <linux/sizes.h>
#include <linux/soc/qcom/mdt_loader.h>
@@ -158,7 +158,7 @@ static int venus_boot_no_tz(struct venus_core *core, phys_addr_t mem_phys,
core->fw.mapped_mem_size = mem_size;
ret = iommu_map(iommu, VENUS_FW_START_ADDR, mem_phys, mem_size,
- IOMMU_READ | IOMMU_WRITE | IOMMU_PRIV);
+ IOMMU_READ | IOMMU_WRITE | IOMMU_PRIV, GFP_KERNEL);
if (ret) {
dev_err(dev, "could not map video firmware region\n");
return ret;
diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c
index 33e08efa3039..384fb54e219a 100644
--- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c
+++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c
@@ -406,7 +406,7 @@ static void rzg2l_csi2_mipi_link_disable(struct rzg2l_csi2 *csi2)
if (!(rzg2l_csi2_read(csi2, CSI2nRTST) & CSI2nRTST_VSRSTS))
break;
usleep_range(100, 200);
- };
+ }
if (!timeout)
dev_err(csi2->dev, "Clearing CSI2nRTST.VSRSTS timed out\n");
diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c
index 91b57c7c2e56..e6eedd65b71d 100644
--- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c
+++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c
@@ -404,7 +404,7 @@ void rzg2l_cru_stop_image_processing(struct rzg2l_cru_dev *cru)
break;
usleep_range(10, 20);
- };
+ }
/* Notify that AXI bus can not stop here */
if (!retries)
diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-capture.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-capture.c
index d4540684ea9a..d1d1fdce03e3 100644
--- a/drivers/media/platform/rockchip/rkisp1/rkisp1-capture.c
+++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-capture.c
@@ -1131,10 +1131,12 @@ static void rkisp1_try_fmt(const struct rkisp1_capture *cap,
const struct rkisp1_capture_config *config = cap->config;
const struct rkisp1_capture_fmt_cfg *fmt;
const struct v4l2_format_info *info;
- const unsigned int max_widths[] = { RKISP1_RSZ_MP_SRC_MAX_WIDTH,
- RKISP1_RSZ_SP_SRC_MAX_WIDTH };
- const unsigned int max_heights[] = { RKISP1_RSZ_MP_SRC_MAX_HEIGHT,
- RKISP1_RSZ_SP_SRC_MAX_HEIGHT};
+ static const unsigned int max_widths[] = {
+ RKISP1_RSZ_MP_SRC_MAX_WIDTH, RKISP1_RSZ_SP_SRC_MAX_WIDTH
+ };
+ static const unsigned int max_heights[] = {
+ RKISP1_RSZ_MP_SRC_MAX_HEIGHT, RKISP1_RSZ_SP_SRC_MAX_HEIGHT
+ };
fmt = rkisp1_find_fmt_cfg(cap, pixm->pixelformat);
if (!fmt) {
@@ -1336,8 +1338,9 @@ void rkisp1_capture_devs_unregister(struct rkisp1_device *rkisp1)
static int rkisp1_register_capture(struct rkisp1_capture *cap)
{
- const char * const dev_names[] = {RKISP1_MP_DEV_NAME,
- RKISP1_SP_DEV_NAME};
+ static const char * const dev_names[] = {
+ RKISP1_MP_DEV_NAME, RKISP1_SP_DEV_NAME
+ };
struct v4l2_device *v4l2_dev = &cap->rkisp1->v4l2_dev;
struct video_device *vdev = &cap->vnode.vdev;
struct rkisp1_vdev_node *node;
diff --git a/drivers/media/platform/samsung/exynos4-is/fimc-is.h b/drivers/media/platform/samsung/exynos4-is/fimc-is.h
index 06586e455b1d..c126b779aafc 100644
--- a/drivers/media/platform/samsung/exynos4-is/fimc-is.h
+++ b/drivers/media/platform/samsung/exynos4-is/fimc-is.h
@@ -14,7 +14,6 @@
#include <linux/clk.h>
#include <linux/device.h>
#include <linux/kernel.h>
-#include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h>
#include <linux/sizes.h>
#include <linux/spinlock.h>
@@ -231,7 +230,6 @@ struct chain_config {
/**
* struct fimc_is - fimc-is data structure
* @pdev: pointer to FIMC-IS platform device
- * @pctrl: pointer to pinctrl structure for this device
* @v4l2_dev: pointer to the top level v4l2_device
* @fw: data structure describing the FIMC-IS firmware binary
* @memory: memory region assigned for the FIMC-IS (firmware)
@@ -262,7 +260,6 @@ struct chain_config {
*/
struct fimc_is {
struct platform_device *pdev;
- struct pinctrl *pctrl;
struct v4l2_device *v4l2_dev;
struct fimc_is_firmware fw;
diff --git a/drivers/media/platform/samsung/exynos4-is/media-dev.h b/drivers/media/platform/samsung/exynos4-is/media-dev.h
index 62ad5d7e035a..079105d88bab 100644
--- a/drivers/media/platform/samsung/exynos4-is/media-dev.h
+++ b/drivers/media/platform/samsung/exynos4-is/media-dev.h
@@ -11,7 +11,6 @@
#include <linux/platform_device.h>
#include <linux/mutex.h>
#include <linux/of.h>
-#include <linux/pinctrl/consumer.h>
#include <media/media-device.h>
#include <media/media-entity.h>
#include <media/v4l2-device.h>
diff --git a/drivers/media/platform/samsung/s3c-camif/Kconfig b/drivers/media/platform/samsung/s3c-camif/Kconfig
index 8cb8d1ac3edc..f359f6382fff 100644
--- a/drivers/media/platform/samsung/s3c-camif/Kconfig
+++ b/drivers/media/platform/samsung/s3c-camif/Kconfig
@@ -1,15 +1,15 @@
# SPDX-License-Identifier: GPL-2.0-only
config VIDEO_S3C_CAMIF
- tristate "Samsung S3C24XX/S3C64XX SoC Camera Interface driver"
+ tristate "Samsung 3C64XX SoC Camera Interface driver"
depends on V4L_PLATFORM_DRIVERS
depends on VIDEO_DEV && I2C && PM
- depends on ARCH_S3C64XX || PLAT_S3C24XX || COMPILE_TEST
+ depends on ARCH_S3C64XX || COMPILE_TEST
select MEDIA_CONTROLLER
select VIDEO_V4L2_SUBDEV_API
select VIDEOBUF2_DMA_CONTIG
help
- This is a v4l2 driver for s3c24xx and s3c64xx SoC series camera
- host interface (CAMIF).
+ This is a v4l2 driver for s3c64xx SoC series camera host interface
+ (CAMIF).
To compile this driver as a module, choose M here: the module
will be called s3c-camif.
diff --git a/drivers/media/platform/samsung/s5p-mfc/s5p_mfc.c b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc.c
index f3e4cdac1ef3..9d2cce124a34 100644
--- a/drivers/media/platform/samsung/s5p-mfc/s5p_mfc.c
+++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc.c
@@ -1021,8 +1021,8 @@ static __poll_t s5p_mfc_poll(struct file *file,
* means either in driver already or waiting for driver to claim it
* and start processing.
*/
- if ((!src_q->streaming || list_empty(&src_q->queued_list))
- && (!dst_q->streaming || list_empty(&dst_q->queued_list))) {
+ if ((!vb2_is_streaming(src_q) || list_empty(&src_q->queued_list)) &&
+ (!vb2_is_streaming(dst_q) || list_empty(&dst_q->queued_list))) {
rc = EPOLLERR;
goto end;
}
diff --git a/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c b/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c
index 18e6c65f4737..86c5235a0c7a 100644
--- a/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c
+++ b/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c
@@ -264,6 +264,7 @@ static int sun4i_csi_remove(struct platform_device *pdev)
{
struct sun4i_csi *csi = platform_get_drvdata(pdev);
+ pm_runtime_disable(&pdev->dev);
v4l2_async_nf_unregister(&csi->notifier);
v4l2_async_nf_cleanup(&csi->notifier);
vb2_video_unregister_device(&csi->vdev);
diff --git a/drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c b/drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c
index a3e826a755fc..95b5633b7914 100644
--- a/drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c
+++ b/drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c
@@ -245,7 +245,7 @@ static int sun4i_csi_start_streaming(struct vb2_queue *vq, unsigned int count)
* We need a scratch buffer in case where we'll not have any
* more buffer queued so that we don't error out. One of those
* cases is when you end up at the last frame to capture, you
- * don't havea any buffer queued any more, and yet it doesn't
+ * don't have any buffer queued any more, and yet it doesn't
* really matter since you'll never reach the next buffer.
*
* Since we support the multi-planar API, we need to have a
@@ -311,7 +311,7 @@ static int sun4i_csi_start_streaming(struct vb2_queue *vq, unsigned int count)
writel(CSI_BUF_CTRL_DBE, csi->regs + CSI_BUF_CTRL_REG);
/* Clear the pending interrupts */
- writel(CSI_INT_FRM_DONE, csi->regs + 0x34);
+ writel(CSI_INT_FRM_DONE, csi->regs + CSI_INT_STA_REG);
/* Enable frame done interrupt */
writel(CSI_INT_FRM_DONE, csi->regs + CSI_INT_EN_REG);
diff --git a/drivers/media/platform/ti/cal/cal.c b/drivers/media/platform/ti/cal/cal.c
index 56b61c0583cf..1236215ec70e 100644
--- a/drivers/media/platform/ti/cal/cal.c
+++ b/drivers/media/platform/ti/cal/cal.c
@@ -1050,8 +1050,10 @@ static struct cal_ctx *cal_ctx_create(struct cal_dev *cal, int inst)
ctx->cport = inst;
ret = cal_ctx_v4l2_init(ctx);
- if (ret)
+ if (ret) {
+ kfree(ctx);
return NULL;
+ }
return ctx;
}
diff --git a/drivers/media/platform/ti/davinci/Kconfig b/drivers/media/platform/ti/davinci/Kconfig
index 96d4bed7fe9e..542a602e66be 100644
--- a/drivers/media/platform/ti/davinci/Kconfig
+++ b/drivers/media/platform/ti/davinci/Kconfig
@@ -31,19 +31,3 @@ config VIDEO_DAVINCI_VPIF_CAPTURE
To compile this driver as a module, choose M here. There will
be two modules called vpif.ko and vpif_capture.ko
-
-config VIDEO_DAVINCI_VPBE_DISPLAY
- tristate "TI DaVinci VPBE V4L2-Display driver"
- depends on V4L_PLATFORM_DRIVERS
- depends on VIDEO_DEV
- depends on ARCH_DAVINCI || COMPILE_TEST
- depends on I2C
- select VIDEOBUF2_DMA_CONTIG
- help
- Enables Davinci VPBE module used for display devices.
- This module is used for display on TI DM644x/DM365/DM355
- based display devices.
-
- To compile this driver as a module, choose M here. There will
- be five modules created called vpss.ko, vpbe.ko, vpbe_osd.ko,
- vpbe_venc.ko and vpbe_display.ko
diff --git a/drivers/media/platform/ti/davinci/Makefile b/drivers/media/platform/ti/davinci/Makefile
index b20a91653162..512f03369bae 100644
--- a/drivers/media/platform/ti/davinci/Makefile
+++ b/drivers/media/platform/ti/davinci/Makefile
@@ -7,6 +7,3 @@
obj-$(CONFIG_VIDEO_DAVINCI_VPIF_DISPLAY) += vpif.o vpif_display.o
#VPIF Capture driver
obj-$(CONFIG_VIDEO_DAVINCI_VPIF_CAPTURE) += vpif.o vpif_capture.o
-
-obj-$(CONFIG_VIDEO_DAVINCI_VPBE_DISPLAY) += vpss.o vpbe.o vpbe_osd.o \
- vpbe_venc.o vpbe_display.o
diff --git a/drivers/media/platform/ti/davinci/vpbe.c b/drivers/media/platform/ti/davinci/vpbe.c
deleted file mode 100644
index 509ecc84624e..000000000000
--- a/drivers/media/platform/ti/davinci/vpbe.c
+++ /dev/null
@@ -1,840 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * Copyright (C) 2010 Texas Instruments Inc
- */
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/errno.h>
-#include <linux/fs.h>
-#include <linux/string.h>
-#include <linux/wait.h>
-#include <linux/time.h>
-#include <linux/platform_device.h>
-#include <linux/io.h>
-#include <linux/slab.h>
-#include <linux/clk.h>
-#include <linux/err.h>
-
-#include <media/v4l2-device.h>
-#include <media/davinci/vpbe_types.h>
-#include <media/davinci/vpbe.h>
-#include <media/davinci/vpss.h>
-#include <media/davinci/vpbe_venc.h>
-
-#define VPBE_DEFAULT_OUTPUT "Composite"
-#define VPBE_DEFAULT_MODE "ntsc"
-
-static char *def_output = VPBE_DEFAULT_OUTPUT;
-static char *def_mode = VPBE_DEFAULT_MODE;
-static int debug;
-
-module_param(def_output, charp, S_IRUGO);
-module_param(def_mode, charp, S_IRUGO);
-module_param(debug, int, 0644);
-
-MODULE_PARM_DESC(def_output, "vpbe output name (default:Composite)");
-MODULE_PARM_DESC(def_mode, "vpbe output mode name (default:ntsc");
-MODULE_PARM_DESC(debug, "Debug level 0-1");
-
-MODULE_DESCRIPTION("TI DMXXX VPBE Display controller");
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Texas Instruments");
-
-/**
- * vpbe_current_encoder_info - Get config info for current encoder
- * @vpbe_dev: vpbe device ptr
- *
- * Return ptr to current encoder config info
- */
-static struct encoder_config_info*
-vpbe_current_encoder_info(struct vpbe_device *vpbe_dev)
-{
- struct vpbe_config *cfg = vpbe_dev->cfg;
- int index = vpbe_dev->current_sd_index;
-
- return ((index == 0) ? &cfg->venc :
- &cfg->ext_encoders[index-1]);
-}
-
-/**
- * vpbe_find_encoder_sd_index - Given a name find encoder sd index
- *
- * @cfg: ptr to vpbe cfg
- * @index: index used by application
- *
- * Return sd index of the encoder
- */
-static int vpbe_find_encoder_sd_index(struct vpbe_config *cfg,
- int index)
-{
- char *encoder_name = cfg->outputs[index].subdev_name;
- int i;
-
- /* Venc is always first */
- if (!strcmp(encoder_name, cfg->venc.module_name))
- return 0;
-
- for (i = 0; i < cfg->num_ext_encoders; i++) {
- if (!strcmp(encoder_name,
- cfg->ext_encoders[i].module_name))
- return i+1;
- }
-
- return -EINVAL;
-}
-
-/**
- * vpbe_enum_outputs - enumerate outputs
- * @vpbe_dev: vpbe device ptr
- * @output: ptr to v4l2_output structure
- *
- * Enumerates the outputs available at the vpbe display
- * returns the status, -EINVAL if end of output list
- */
-static int vpbe_enum_outputs(struct vpbe_device *vpbe_dev,
- struct v4l2_output *output)
-{
- struct vpbe_config *cfg = vpbe_dev->cfg;
- unsigned int temp_index = output->index;
-
- if (temp_index >= cfg->num_outputs)
- return -EINVAL;
-
- *output = cfg->outputs[temp_index].output;
- output->index = temp_index;
-
- return 0;
-}
-
-static int vpbe_get_mode_info(struct vpbe_device *vpbe_dev, char *mode,
- int output_index)
-{
- struct vpbe_config *cfg = vpbe_dev->cfg;
- struct vpbe_enc_mode_info var;
- int curr_output = output_index;
- int i;
-
- if (!mode)
- return -EINVAL;
-
- for (i = 0; i < cfg->outputs[curr_output].num_modes; i++) {
- var = cfg->outputs[curr_output].modes[i];
- if (!strcmp(mode, var.name)) {
- vpbe_dev->current_timings = var;
- return 0;
- }
- }
-
- return -EINVAL;
-}
-
-static int vpbe_get_current_mode_info(struct vpbe_device *vpbe_dev,
- struct vpbe_enc_mode_info *mode_info)
-{
- if (!mode_info)
- return -EINVAL;
-
- *mode_info = vpbe_dev->current_timings;
-
- return 0;
-}
-
-/* Get std by std id */
-static int vpbe_get_std_info(struct vpbe_device *vpbe_dev,
- v4l2_std_id std_id)
-{
- struct vpbe_config *cfg = vpbe_dev->cfg;
- struct vpbe_enc_mode_info var;
- int curr_output = vpbe_dev->current_out_index;
- int i;
-
- for (i = 0; i < vpbe_dev->cfg->outputs[curr_output].num_modes; i++) {
- var = cfg->outputs[curr_output].modes[i];
- if ((var.timings_type & VPBE_ENC_STD) &&
- (var.std_id & std_id)) {
- vpbe_dev->current_timings = var;
- return 0;
- }
- }
-
- return -EINVAL;
-}
-
-static int vpbe_get_std_info_by_name(struct vpbe_device *vpbe_dev,
- char *std_name)
-{
- struct vpbe_config *cfg = vpbe_dev->cfg;
- struct vpbe_enc_mode_info var;
- int curr_output = vpbe_dev->current_out_index;
- int i;
-
- for (i = 0; i < vpbe_dev->cfg->outputs[curr_output].num_modes; i++) {
- var = cfg->outputs[curr_output].modes[i];
- if (!strcmp(var.name, std_name)) {
- vpbe_dev->current_timings = var;
- return 0;
- }
- }
-
- return -EINVAL;
-}
-
-/**
- * vpbe_set_output - Set output
- * @vpbe_dev: vpbe device ptr
- * @index: index of output
- *
- * Set vpbe output to the output specified by the index
- */
-static int vpbe_set_output(struct vpbe_device *vpbe_dev, int index)
-{
- struct encoder_config_info *curr_enc_info =
- vpbe_current_encoder_info(vpbe_dev);
- struct vpbe_config *cfg = vpbe_dev->cfg;
- struct venc_platform_data *venc_device = vpbe_dev->venc_device;
- int enc_out_index;
- int sd_index;
- int ret;
-
- if (index >= cfg->num_outputs)
- return -EINVAL;
-
- mutex_lock(&vpbe_dev->lock);
-
- sd_index = vpbe_dev->current_sd_index;
- enc_out_index = cfg->outputs[index].output.index;
- /*
- * Currently we switch the encoder based on output selected
- * by the application. If media controller is implemented later
- * there is will be an API added to setup_link between venc
- * and external encoder. So in that case below comparison always
- * match and encoder will not be switched. But if application
- * chose not to use media controller, then this provides current
- * way of switching encoder at the venc output.
- */
- if (strcmp(curr_enc_info->module_name,
- cfg->outputs[index].subdev_name)) {
- /* Need to switch the encoder at the output */
- sd_index = vpbe_find_encoder_sd_index(cfg, index);
- if (sd_index < 0) {
- ret = -EINVAL;
- goto unlock;
- }
-
- ret = venc_device->setup_if_config(cfg->outputs[index].if_params);
- if (ret)
- goto unlock;
- }
-
- /* Set output at the encoder */
- ret = v4l2_subdev_call(vpbe_dev->encoders[sd_index], video,
- s_routing, 0, enc_out_index, 0);
- if (ret)
- goto unlock;
-
- /*
- * It is assumed that venc or external encoder will set a default
- * mode in the sub device. For external encoder or LCD pannel output,
- * we also need to set up the lcd port for the required mode. So setup
- * the lcd port for the default mode that is configured in the board
- * arch/arm/mach-davinci/board-dm355-evm.setup file for the external
- * encoder.
- */
- ret = vpbe_get_mode_info(vpbe_dev,
- cfg->outputs[index].default_mode, index);
- if (!ret) {
- struct osd_state *osd_device = vpbe_dev->osd_device;
-
- osd_device->ops.set_left_margin(osd_device,
- vpbe_dev->current_timings.left_margin);
- osd_device->ops.set_top_margin(osd_device,
- vpbe_dev->current_timings.upper_margin);
- vpbe_dev->current_sd_index = sd_index;
- vpbe_dev->current_out_index = index;
- }
-unlock:
- mutex_unlock(&vpbe_dev->lock);
- return ret;
-}
-
-static int vpbe_set_default_output(struct vpbe_device *vpbe_dev)
-{
- struct vpbe_config *cfg = vpbe_dev->cfg;
- int i;
-
- for (i = 0; i < cfg->num_outputs; i++) {
- if (!strcmp(def_output,
- cfg->outputs[i].output.name)) {
- int ret = vpbe_set_output(vpbe_dev, i);
-
- if (!ret)
- vpbe_dev->current_out_index = i;
- return ret;
- }
- }
- return 0;
-}
-
-/**
- * vpbe_get_output - Get output
- * @vpbe_dev: vpbe device ptr
- *
- * return current vpbe output to the index
- */
-static unsigned int vpbe_get_output(struct vpbe_device *vpbe_dev)
-{
- return vpbe_dev->current_out_index;
-}
-
-/*
- * vpbe_s_dv_timings - Set the given preset timings in the encoder
- *
- * Sets the timings if supported by the current encoder. Return the status.
- * 0 - success & -EINVAL on error
- */
-static int vpbe_s_dv_timings(struct vpbe_device *vpbe_dev,
- struct v4l2_dv_timings *dv_timings)
-{
- struct vpbe_config *cfg = vpbe_dev->cfg;
- int out_index = vpbe_dev->current_out_index;
- struct vpbe_output *output = &cfg->outputs[out_index];
- int sd_index = vpbe_dev->current_sd_index;
- int ret, i;
-
-
- if (!(cfg->outputs[out_index].output.capabilities &
- V4L2_OUT_CAP_DV_TIMINGS))
- return -ENODATA;
-
- for (i = 0; i < output->num_modes; i++) {
- if (output->modes[i].timings_type == VPBE_ENC_DV_TIMINGS &&
- !memcmp(&output->modes[i].dv_timings,
- dv_timings, sizeof(*dv_timings)))
- break;
- }
- if (i >= output->num_modes)
- return -EINVAL;
- vpbe_dev->current_timings = output->modes[i];
- mutex_lock(&vpbe_dev->lock);
-
- ret = v4l2_subdev_call(vpbe_dev->encoders[sd_index], video,
- s_dv_timings, dv_timings);
- if (!ret && vpbe_dev->amp) {
- /* Call amplifier subdevice */
- ret = v4l2_subdev_call(vpbe_dev->amp, video,
- s_dv_timings, dv_timings);
- }
- /* set the lcd controller output for the given mode */
- if (!ret) {
- struct osd_state *osd_device = vpbe_dev->osd_device;
-
- osd_device->ops.set_left_margin(osd_device,
- vpbe_dev->current_timings.left_margin);
- osd_device->ops.set_top_margin(osd_device,
- vpbe_dev->current_timings.upper_margin);
- }
- mutex_unlock(&vpbe_dev->lock);
-
- return ret;
-}
-
-/*
- * vpbe_g_dv_timings - Get the timings in the current encoder
- *
- * Get the timings in the current encoder. Return the status. 0 - success
- * -EINVAL on error
- */
-static int vpbe_g_dv_timings(struct vpbe_device *vpbe_dev,
- struct v4l2_dv_timings *dv_timings)
-{
- struct vpbe_config *cfg = vpbe_dev->cfg;
- int out_index = vpbe_dev->current_out_index;
-
- if (!(cfg->outputs[out_index].output.capabilities &
- V4L2_OUT_CAP_DV_TIMINGS))
- return -ENODATA;
-
- if (vpbe_dev->current_timings.timings_type &
- VPBE_ENC_DV_TIMINGS) {
- *dv_timings = vpbe_dev->current_timings.dv_timings;
- return 0;
- }
-
- return -EINVAL;
-}
-
-/*
- * vpbe_enum_dv_timings - Enumerate the dv timings in the current encoder
- *
- * Get the timings in the current encoder. Return the status. 0 - success
- * -EINVAL on error
- */
-static int vpbe_enum_dv_timings(struct vpbe_device *vpbe_dev,
- struct v4l2_enum_dv_timings *timings)
-{
- struct vpbe_config *cfg = vpbe_dev->cfg;
- int out_index = vpbe_dev->current_out_index;
- struct vpbe_output *output = &cfg->outputs[out_index];
- int j = 0;
- int i;
-
- if (!(output->output.capabilities & V4L2_OUT_CAP_DV_TIMINGS))
- return -ENODATA;
-
- for (i = 0; i < output->num_modes; i++) {
- if (output->modes[i].timings_type == VPBE_ENC_DV_TIMINGS) {
- if (j == timings->index)
- break;
- j++;
- }
- }
-
- if (i == output->num_modes)
- return -EINVAL;
- timings->timings = output->modes[i].dv_timings;
- return 0;
-}
-
-/*
- * vpbe_s_std - Set the given standard in the encoder
- *
- * Sets the standard if supported by the current encoder. Return the status.
- * 0 - success & -EINVAL on error
- */
-static int vpbe_s_std(struct vpbe_device *vpbe_dev, v4l2_std_id std_id)
-{
- struct vpbe_config *cfg = vpbe_dev->cfg;
- int out_index = vpbe_dev->current_out_index;
- int sd_index = vpbe_dev->current_sd_index;
- int ret;
-
- if (!(cfg->outputs[out_index].output.capabilities &
- V4L2_OUT_CAP_STD))
- return -ENODATA;
-
- ret = vpbe_get_std_info(vpbe_dev, std_id);
- if (ret)
- return ret;
-
- mutex_lock(&vpbe_dev->lock);
-
- ret = v4l2_subdev_call(vpbe_dev->encoders[sd_index], video,
- s_std_output, std_id);
- /* set the lcd controller output for the given mode */
- if (!ret) {
- struct osd_state *osd_device = vpbe_dev->osd_device;
-
- osd_device->ops.set_left_margin(osd_device,
- vpbe_dev->current_timings.left_margin);
- osd_device->ops.set_top_margin(osd_device,
- vpbe_dev->current_timings.upper_margin);
- }
- mutex_unlock(&vpbe_dev->lock);
-
- return ret;
-}
-
-/*
- * vpbe_g_std - Get the standard in the current encoder
- *
- * Get the standard in the current encoder. Return the status. 0 - success
- * -EINVAL on error
- */
-static int vpbe_g_std(struct vpbe_device *vpbe_dev, v4l2_std_id *std_id)
-{
- struct vpbe_enc_mode_info *cur_timings = &vpbe_dev->current_timings;
- struct vpbe_config *cfg = vpbe_dev->cfg;
- int out_index = vpbe_dev->current_out_index;
-
- if (!(cfg->outputs[out_index].output.capabilities & V4L2_OUT_CAP_STD))
- return -ENODATA;
-
- if (cur_timings->timings_type & VPBE_ENC_STD) {
- *std_id = cur_timings->std_id;
- return 0;
- }
-
- return -EINVAL;
-}
-
-/*
- * vpbe_set_mode - Set mode in the current encoder using mode info
- *
- * Use the mode string to decide what timings to set in the encoder
- * This is typically useful when fbset command is used to change the current
- * timings by specifying a string to indicate the timings.
- */
-static int vpbe_set_mode(struct vpbe_device *vpbe_dev,
- struct vpbe_enc_mode_info *mode_info)
-{
- struct vpbe_enc_mode_info *preset_mode = NULL;
- struct vpbe_config *cfg = vpbe_dev->cfg;
- struct v4l2_dv_timings dv_timings;
- struct osd_state *osd_device;
- int out_index = vpbe_dev->current_out_index;
- int i;
-
- if (!mode_info || !mode_info->name)
- return -EINVAL;
-
- for (i = 0; i < cfg->outputs[out_index].num_modes; i++) {
- if (!strcmp(mode_info->name,
- cfg->outputs[out_index].modes[i].name)) {
- preset_mode = &cfg->outputs[out_index].modes[i];
- /*
- * it may be one of the 3 timings type. Check and
- * invoke right API
- */
- if (preset_mode->timings_type & VPBE_ENC_STD)
- return vpbe_s_std(vpbe_dev,
- preset_mode->std_id);
- if (preset_mode->timings_type &
- VPBE_ENC_DV_TIMINGS) {
- dv_timings =
- preset_mode->dv_timings;
- return vpbe_s_dv_timings(vpbe_dev, &dv_timings);
- }
- }
- }
-
- /* Only custom timing should reach here */
- if (!preset_mode)
- return -EINVAL;
-
- mutex_lock(&vpbe_dev->lock);
-
- osd_device = vpbe_dev->osd_device;
- vpbe_dev->current_timings = *preset_mode;
- osd_device->ops.set_left_margin(osd_device,
- vpbe_dev->current_timings.left_margin);
- osd_device->ops.set_top_margin(osd_device,
- vpbe_dev->current_timings.upper_margin);
-
- mutex_unlock(&vpbe_dev->lock);
- return 0;
-}
-
-static int vpbe_set_default_mode(struct vpbe_device *vpbe_dev)
-{
- int ret;
-
- ret = vpbe_get_std_info_by_name(vpbe_dev, def_mode);
- if (ret)
- return ret;
-
- /* set the default mode in the encoder */
- return vpbe_set_mode(vpbe_dev, &vpbe_dev->current_timings);
-}
-
-static int platform_device_get(struct device *dev, void *data)
-{
- struct platform_device *pdev = to_platform_device(dev);
- struct vpbe_device *vpbe_dev = data;
-
- if (strstr(pdev->name, "vpbe-osd"))
- vpbe_dev->osd_device = platform_get_drvdata(pdev);
- if (strstr(pdev->name, "vpbe-venc"))
- vpbe_dev->venc_device = dev_get_platdata(&pdev->dev);
-
- return 0;
-}
-
-/**
- * vpbe_initialize() - Initialize the vpbe display controller
- * @dev: Master and slave device ptr
- * @vpbe_dev: vpbe device ptr
- *
- * Master frame buffer device drivers calls this to initialize vpbe
- * display controller. This will then registers v4l2 device and the sub
- * devices and sets a current encoder sub device for display. v4l2 display
- * device driver is the master and frame buffer display device driver is
- * the slave. Frame buffer display driver checks the initialized during
- * probe and exit if not initialized. Returns status.
- */
-static int vpbe_initialize(struct device *dev, struct vpbe_device *vpbe_dev)
-{
- struct encoder_config_info *enc_info;
- struct amp_config_info *amp_info;
- struct v4l2_subdev **enc_subdev;
- struct osd_state *osd_device;
- struct i2c_adapter *i2c_adap;
- int num_encoders;
- int ret = 0;
- int err;
- int i;
-
- /*
- * v4l2 abd FBDev frame buffer devices will get the vpbe_dev pointer
- * from the platform device by iteration of platform drivers and
- * matching with device name
- */
- if (!vpbe_dev || !dev) {
- printk(KERN_ERR "Null device pointers.\n");
- return -ENODEV;
- }
-
- if (vpbe_dev->initialized)
- return 0;
-
- mutex_lock(&vpbe_dev->lock);
-
- if (strcmp(vpbe_dev->cfg->module_name, "dm644x-vpbe-display") != 0) {
- /* We have dac clock available for platform */
- vpbe_dev->dac_clk = clk_get(vpbe_dev->pdev, "vpss_dac");
- if (IS_ERR(vpbe_dev->dac_clk)) {
- ret = PTR_ERR(vpbe_dev->dac_clk);
- goto fail_mutex_unlock;
- }
- if (clk_prepare_enable(vpbe_dev->dac_clk)) {
- ret = -ENODEV;
- clk_put(vpbe_dev->dac_clk);
- goto fail_mutex_unlock;
- }
- }
-
- /* first enable vpss clocks */
- vpss_enable_clock(VPSS_VPBE_CLOCK, 1);
-
- /* First register a v4l2 device */
- ret = v4l2_device_register(dev, &vpbe_dev->v4l2_dev);
- if (ret) {
- v4l2_err(dev->driver,
- "Unable to register v4l2 device.\n");
- goto fail_clk_put;
- }
- v4l2_info(&vpbe_dev->v4l2_dev, "vpbe v4l2 device registered\n");
-
- err = bus_for_each_dev(&platform_bus_type, NULL, vpbe_dev,
- platform_device_get);
- if (err < 0) {
- ret = err;
- goto fail_dev_unregister;
- }
-
- vpbe_dev->venc = venc_sub_dev_init(&vpbe_dev->v4l2_dev,
- vpbe_dev->cfg->venc.module_name);
- /* register venc sub device */
- if (!vpbe_dev->venc) {
- v4l2_err(&vpbe_dev->v4l2_dev,
- "vpbe unable to init venc sub device\n");
- ret = -ENODEV;
- goto fail_dev_unregister;
- }
- /* initialize osd device */
- osd_device = vpbe_dev->osd_device;
- if (osd_device->ops.initialize) {
- err = osd_device->ops.initialize(osd_device);
- if (err) {
- v4l2_err(&vpbe_dev->v4l2_dev,
- "unable to initialize the OSD device");
- ret = -ENOMEM;
- goto fail_dev_unregister;
- }
- }
-
- /*
- * Register any external encoders that are configured. At index 0 we
- * store venc sd index.
- */
- num_encoders = vpbe_dev->cfg->num_ext_encoders + 1;
- vpbe_dev->encoders = kmalloc_array(num_encoders,
- sizeof(*vpbe_dev->encoders),
- GFP_KERNEL);
- if (!vpbe_dev->encoders) {
- ret = -ENOMEM;
- goto fail_dev_unregister;
- }
-
- i2c_adap = i2c_get_adapter(vpbe_dev->cfg->i2c_adapter_id);
- for (i = 0; i < (vpbe_dev->cfg->num_ext_encoders + 1); i++) {
- if (i == 0) {
- /* venc is at index 0 */
- enc_subdev = &vpbe_dev->encoders[i];
- *enc_subdev = vpbe_dev->venc;
- continue;
- }
- enc_info = &vpbe_dev->cfg->ext_encoders[i];
- if (enc_info->is_i2c) {
- enc_subdev = &vpbe_dev->encoders[i];
- *enc_subdev = v4l2_i2c_new_subdev_board(
- &vpbe_dev->v4l2_dev, i2c_adap,
- &enc_info->board_info, NULL);
- if (*enc_subdev)
- v4l2_info(&vpbe_dev->v4l2_dev,
- "v4l2 sub device %s registered\n",
- enc_info->module_name);
- else {
- v4l2_err(&vpbe_dev->v4l2_dev, "encoder %s failed to register",
- enc_info->module_name);
- ret = -ENODEV;
- goto fail_kfree_encoders;
- }
- } else
- v4l2_warn(&vpbe_dev->v4l2_dev, "non-i2c encoders currently not supported");
- }
- /* Add amplifier subdevice for dm365 */
- if ((strcmp(vpbe_dev->cfg->module_name, "dm365-vpbe-display") == 0) &&
- vpbe_dev->cfg->amp) {
- amp_info = vpbe_dev->cfg->amp;
- if (amp_info->is_i2c) {
- vpbe_dev->amp = v4l2_i2c_new_subdev_board(
- &vpbe_dev->v4l2_dev, i2c_adap,
- &amp_info->board_info, NULL);
- if (!vpbe_dev->amp) {
- v4l2_err(&vpbe_dev->v4l2_dev,
- "amplifier %s failed to register",
- amp_info->module_name);
- ret = -ENODEV;
- goto fail_kfree_encoders;
- }
- v4l2_info(&vpbe_dev->v4l2_dev,
- "v4l2 sub device %s registered\n",
- amp_info->module_name);
- } else {
- vpbe_dev->amp = NULL;
- v4l2_warn(&vpbe_dev->v4l2_dev, "non-i2c amplifiers currently not supported");
- }
- } else {
- vpbe_dev->amp = NULL;
- }
-
- /* set the current encoder and output to that of venc by default */
- vpbe_dev->current_sd_index = 0;
- vpbe_dev->current_out_index = 0;
-
- mutex_unlock(&vpbe_dev->lock);
-
- printk(KERN_NOTICE "Setting default output to %s\n", def_output);
- ret = vpbe_set_default_output(vpbe_dev);
- if (ret) {
- v4l2_err(&vpbe_dev->v4l2_dev, "Failed to set default output %s",
- def_output);
- goto fail_kfree_amp;
- }
-
- printk(KERN_NOTICE "Setting default mode to %s\n", def_mode);
- ret = vpbe_set_default_mode(vpbe_dev);
- if (ret) {
- v4l2_err(&vpbe_dev->v4l2_dev, "Failed to set default mode %s",
- def_mode);
- goto fail_kfree_amp;
- }
- vpbe_dev->initialized = 1;
- /* TBD handling of bootargs for default output and mode */
- return 0;
-
-fail_kfree_amp:
- mutex_lock(&vpbe_dev->lock);
- kfree(vpbe_dev->amp);
-fail_kfree_encoders:
- kfree(vpbe_dev->encoders);
-fail_dev_unregister:
- v4l2_device_unregister(&vpbe_dev->v4l2_dev);
-fail_clk_put:
- if (strcmp(vpbe_dev->cfg->module_name, "dm644x-vpbe-display") != 0) {
- clk_disable_unprepare(vpbe_dev->dac_clk);
- clk_put(vpbe_dev->dac_clk);
- }
-fail_mutex_unlock:
- mutex_unlock(&vpbe_dev->lock);
- return ret;
-}
-
-/**
- * vpbe_deinitialize() - de-initialize the vpbe display controller
- * @dev: Master and slave device ptr
- * @vpbe_dev: vpbe device ptr
- *
- * vpbe_master and slave frame buffer devices calls this to de-initialize
- * the display controller. It is called when master and slave device
- * driver modules are removed and no longer requires the display controller.
- */
-static void vpbe_deinitialize(struct device *dev, struct vpbe_device *vpbe_dev)
-{
- v4l2_device_unregister(&vpbe_dev->v4l2_dev);
- if (strcmp(vpbe_dev->cfg->module_name, "dm644x-vpbe-display") != 0) {
- clk_disable_unprepare(vpbe_dev->dac_clk);
- clk_put(vpbe_dev->dac_clk);
- }
-
- kfree(vpbe_dev->amp);
- kfree(vpbe_dev->encoders);
- vpbe_dev->initialized = 0;
- /* disable vpss clocks */
- vpss_enable_clock(VPSS_VPBE_CLOCK, 0);
-}
-
-static const struct vpbe_device_ops vpbe_dev_ops = {
- .enum_outputs = vpbe_enum_outputs,
- .set_output = vpbe_set_output,
- .get_output = vpbe_get_output,
- .s_dv_timings = vpbe_s_dv_timings,
- .g_dv_timings = vpbe_g_dv_timings,
- .enum_dv_timings = vpbe_enum_dv_timings,
- .s_std = vpbe_s_std,
- .g_std = vpbe_g_std,
- .initialize = vpbe_initialize,
- .deinitialize = vpbe_deinitialize,
- .get_mode_info = vpbe_get_current_mode_info,
- .set_mode = vpbe_set_mode,
-};
-
-static int vpbe_probe(struct platform_device *pdev)
-{
- struct vpbe_device *vpbe_dev;
- struct vpbe_config *cfg;
-
- if (!pdev->dev.platform_data) {
- v4l2_err(pdev->dev.driver, "No platform data\n");
- return -ENODEV;
- }
- cfg = pdev->dev.platform_data;
-
- if (!cfg->module_name[0] ||
- !cfg->osd.module_name[0] ||
- !cfg->venc.module_name[0]) {
- v4l2_err(pdev->dev.driver, "vpbe display module names not defined\n");
- return -EINVAL;
- }
-
- vpbe_dev = kzalloc(sizeof(*vpbe_dev), GFP_KERNEL);
- if (!vpbe_dev)
- return -ENOMEM;
-
- vpbe_dev->cfg = cfg;
- vpbe_dev->ops = vpbe_dev_ops;
- vpbe_dev->pdev = &pdev->dev;
-
- if (cfg->outputs->num_modes > 0)
- vpbe_dev->current_timings = vpbe_dev->cfg->outputs[0].modes[0];
- else {
- kfree(vpbe_dev);
- return -ENODEV;
- }
-
- /* set the driver data in platform device */
- platform_set_drvdata(pdev, vpbe_dev);
- mutex_init(&vpbe_dev->lock);
-
- return 0;
-}
-
-static int vpbe_remove(struct platform_device *device)
-{
- struct vpbe_device *vpbe_dev = platform_get_drvdata(device);
-
- kfree(vpbe_dev);
-
- return 0;
-}
-
-static struct platform_driver vpbe_driver = {
- .driver = {
- .name = "vpbe_controller",
- },
- .probe = vpbe_probe,
- .remove = vpbe_remove,
-};
-
-module_platform_driver(vpbe_driver);
diff --git a/drivers/media/platform/ti/davinci/vpbe_display.c b/drivers/media/platform/ti/davinci/vpbe_display.c
deleted file mode 100644
index 9ea70817538e..000000000000
--- a/drivers/media/platform/ti/davinci/vpbe_display.c
+++ /dev/null
@@ -1,1510 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * Copyright (C) 2010 Texas Instruments Incorporated - https://www.ti.com/
- */
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/errno.h>
-#include <linux/interrupt.h>
-#include <linux/string.h>
-#include <linux/wait.h>
-#include <linux/time.h>
-#include <linux/platform_device.h>
-#include <linux/irq.h>
-#include <linux/mm.h>
-#include <linux/mutex.h>
-#include <linux/videodev2.h>
-#include <linux/slab.h>
-
-
-#include <media/v4l2-dev.h>
-#include <media/v4l2-common.h>
-#include <media/v4l2-ioctl.h>
-#include <media/v4l2-device.h>
-#include <media/davinci/vpbe_display.h>
-#include <media/davinci/vpbe_types.h>
-#include <media/davinci/vpbe.h>
-#include <media/davinci/vpbe_venc.h>
-#include <media/davinci/vpbe_osd.h>
-#include "vpbe_venc_regs.h"
-
-#define VPBE_DISPLAY_DRIVER "vpbe-v4l2"
-
-static int debug;
-
-#define VPBE_DEFAULT_NUM_BUFS 3
-
-module_param(debug, int, 0644);
-
-static int vpbe_set_osd_display_params(struct vpbe_display *disp_dev,
- struct vpbe_layer *layer);
-
-static int venc_is_second_field(struct vpbe_display *disp_dev)
-{
- struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev;
- int ret, val;
-
- ret = v4l2_subdev_call(vpbe_dev->venc,
- core,
- command,
- VENC_GET_FLD,
- &val);
- if (ret < 0) {
- v4l2_err(&vpbe_dev->v4l2_dev,
- "Error in getting Field ID 0\n");
- return 1;
- }
- return val;
-}
-
-static void vpbe_isr_even_field(struct vpbe_display *disp_obj,
- struct vpbe_layer *layer)
-{
- if (layer->cur_frm == layer->next_frm)
- return;
-
- layer->cur_frm->vb.vb2_buf.timestamp = ktime_get_ns();
- vb2_buffer_done(&layer->cur_frm->vb.vb2_buf, VB2_BUF_STATE_DONE);
- /* Make cur_frm pointing to next_frm */
- layer->cur_frm = layer->next_frm;
-}
-
-static void vpbe_isr_odd_field(struct vpbe_display *disp_obj,
- struct vpbe_layer *layer)
-{
- struct osd_state *osd_device = disp_obj->osd_device;
- unsigned long addr;
-
- spin_lock(&disp_obj->dma_queue_lock);
- if (list_empty(&layer->dma_queue) ||
- (layer->cur_frm != layer->next_frm)) {
- spin_unlock(&disp_obj->dma_queue_lock);
- return;
- }
- /*
- * one field is displayed configure
- * the next frame if it is available
- * otherwise hold on current frame
- * Get next from the buffer queue
- */
- layer->next_frm = list_entry(layer->dma_queue.next,
- struct vpbe_disp_buffer, list);
- /* Remove that from the buffer queue */
- list_del(&layer->next_frm->list);
- spin_unlock(&disp_obj->dma_queue_lock);
- /* Mark state of the frame to active */
- layer->next_frm->vb.vb2_buf.state = VB2_BUF_STATE_ACTIVE;
- addr = vb2_dma_contig_plane_dma_addr(&layer->next_frm->vb.vb2_buf, 0);
- osd_device->ops.start_layer(osd_device,
- layer->layer_info.id,
- addr,
- disp_obj->cbcr_ofst);
-}
-
-/* interrupt service routine */
-static irqreturn_t venc_isr(int irq, void *arg)
-{
- struct vpbe_display *disp_dev = (struct vpbe_display *)arg;
- struct vpbe_layer *layer;
- static unsigned last_event;
- unsigned event = 0;
- int fid;
- int i;
-
- if (!arg || !disp_dev->dev[0])
- return IRQ_HANDLED;
-
- if (venc_is_second_field(disp_dev))
- event |= VENC_SECOND_FIELD;
- else
- event |= VENC_FIRST_FIELD;
-
- if (event == (last_event & ~VENC_END_OF_FRAME)) {
- /*
- * If the display is non-interlaced, then we need to flag the
- * end-of-frame event at every interrupt regardless of the
- * value of the FIDST bit. We can conclude that the display is
- * non-interlaced if the value of the FIDST bit is unchanged
- * from the previous interrupt.
- */
- event |= VENC_END_OF_FRAME;
- } else if (event == VENC_SECOND_FIELD) {
- /* end-of-frame for interlaced display */
- event |= VENC_END_OF_FRAME;
- }
- last_event = event;
-
- for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) {
- layer = disp_dev->dev[i];
-
- if (!vb2_start_streaming_called(&layer->buffer_queue))
- continue;
-
- if (layer->layer_first_int) {
- layer->layer_first_int = 0;
- continue;
- }
- /* Check the field format */
- if ((V4L2_FIELD_NONE == layer->pix_fmt.field) &&
- (event & VENC_END_OF_FRAME)) {
- /* Progressive mode */
-
- vpbe_isr_even_field(disp_dev, layer);
- vpbe_isr_odd_field(disp_dev, layer);
- } else {
- /* Interlaced mode */
-
- layer->field_id ^= 1;
- if (event & VENC_FIRST_FIELD)
- fid = 0;
- else
- fid = 1;
-
- /*
- * If field id does not match with store
- * field id
- */
- if (fid != layer->field_id) {
- /* Make them in sync */
- layer->field_id = fid;
- continue;
- }
- /*
- * device field id and local field id are
- * in sync. If this is even field
- */
- if (0 == fid)
- vpbe_isr_even_field(disp_dev, layer);
- else /* odd field */
- vpbe_isr_odd_field(disp_dev, layer);
- }
- }
-
- return IRQ_HANDLED;
-}
-
-/*
- * vpbe_buffer_prepare()
- * This is the callback function called from vb2_qbuf() function
- * the buffer is prepared and user space virtual address is converted into
- * physical address
- */
-static int vpbe_buffer_prepare(struct vb2_buffer *vb)
-{
- struct vb2_queue *q = vb->vb2_queue;
- struct vpbe_layer *layer = vb2_get_drv_priv(q);
- struct vpbe_device *vpbe_dev = layer->disp_dev->vpbe_dev;
- unsigned long addr;
-
- v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
- "vpbe_buffer_prepare\n");
-
- vb2_set_plane_payload(vb, 0, layer->pix_fmt.sizeimage);
- if (vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0))
- return -EINVAL;
-
- addr = vb2_dma_contig_plane_dma_addr(vb, 0);
- if (!IS_ALIGNED(addr, 8)) {
- v4l2_err(&vpbe_dev->v4l2_dev,
- "buffer_prepare:offset is not aligned to 32 bytes\n");
- return -EINVAL;
- }
- return 0;
-}
-
-/*
- * vpbe_buffer_setup()
- * This function allocates memory for the buffers
- */
-static int
-vpbe_buffer_queue_setup(struct vb2_queue *vq,
- unsigned int *nbuffers, unsigned int *nplanes,
- unsigned int sizes[], struct device *alloc_devs[])
-
-{
- /* Get the file handle object and layer object */
- struct vpbe_layer *layer = vb2_get_drv_priv(vq);
- struct vpbe_device *vpbe_dev = layer->disp_dev->vpbe_dev;
-
- v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_buffer_setup\n");
-
- /* Store number of buffers allocated in numbuffer member */
- if (vq->num_buffers + *nbuffers < VPBE_DEFAULT_NUM_BUFS)
- *nbuffers = VPBE_DEFAULT_NUM_BUFS - vq->num_buffers;
-
- if (*nplanes)
- return sizes[0] < layer->pix_fmt.sizeimage ? -EINVAL : 0;
-
- *nplanes = 1;
- sizes[0] = layer->pix_fmt.sizeimage;
-
- return 0;
-}
-
-/*
- * vpbe_buffer_queue()
- * This function adds the buffer to DMA queue
- */
-static void vpbe_buffer_queue(struct vb2_buffer *vb)
-{
- struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
- /* Get the file handle object and layer object */
- struct vpbe_disp_buffer *buf = container_of(vbuf,
- struct vpbe_disp_buffer, vb);
- struct vpbe_layer *layer = vb2_get_drv_priv(vb->vb2_queue);
- struct vpbe_display *disp = layer->disp_dev;
- struct vpbe_device *vpbe_dev = layer->disp_dev->vpbe_dev;
- unsigned long flags;
-
- v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
- "vpbe_buffer_queue\n");
-
- /* add the buffer to the DMA queue */
- spin_lock_irqsave(&disp->dma_queue_lock, flags);
- list_add_tail(&buf->list, &layer->dma_queue);
- spin_unlock_irqrestore(&disp->dma_queue_lock, flags);
-}
-
-static int vpbe_start_streaming(struct vb2_queue *vq, unsigned int count)
-{
- struct vpbe_layer *layer = vb2_get_drv_priv(vq);
- struct osd_state *osd_device = layer->disp_dev->osd_device;
- int ret;
-
- osd_device->ops.disable_layer(osd_device, layer->layer_info.id);
-
- /* Get the next frame from the buffer queue */
- layer->next_frm = layer->cur_frm = list_entry(layer->dma_queue.next,
- struct vpbe_disp_buffer, list);
- /* Remove buffer from the buffer queue */
- list_del(&layer->cur_frm->list);
- /* Mark state of the current frame to active */
- layer->cur_frm->vb.vb2_buf.state = VB2_BUF_STATE_ACTIVE;
- /* Initialize field_id and started member */
- layer->field_id = 0;
-
- /* Set parameters in OSD and VENC */
- ret = vpbe_set_osd_display_params(layer->disp_dev, layer);
- if (ret < 0) {
- struct vpbe_disp_buffer *buf, *tmp;
-
- vb2_buffer_done(&layer->cur_frm->vb.vb2_buf,
- VB2_BUF_STATE_QUEUED);
- list_for_each_entry_safe(buf, tmp, &layer->dma_queue, list) {
- list_del(&buf->list);
- vb2_buffer_done(&buf->vb.vb2_buf,
- VB2_BUF_STATE_QUEUED);
- }
-
- return ret;
- }
-
- /*
- * if request format is yuv420 semiplanar, need to
- * enable both video windows
- */
- layer->layer_first_int = 1;
-
- return ret;
-}
-
-static void vpbe_stop_streaming(struct vb2_queue *vq)
-{
- struct vpbe_layer *layer = vb2_get_drv_priv(vq);
- struct osd_state *osd_device = layer->disp_dev->osd_device;
- struct vpbe_display *disp = layer->disp_dev;
- unsigned long flags;
-
- if (!vb2_is_streaming(vq))
- return;
-
- osd_device->ops.disable_layer(osd_device, layer->layer_info.id);
-
- /* release all active buffers */
- spin_lock_irqsave(&disp->dma_queue_lock, flags);
- if (layer->cur_frm == layer->next_frm) {
- vb2_buffer_done(&layer->cur_frm->vb.vb2_buf,
- VB2_BUF_STATE_ERROR);
- } else {
- if (layer->cur_frm)
- vb2_buffer_done(&layer->cur_frm->vb.vb2_buf,
- VB2_BUF_STATE_ERROR);
- if (layer->next_frm)
- vb2_buffer_done(&layer->next_frm->vb.vb2_buf,
- VB2_BUF_STATE_ERROR);
- }
-
- while (!list_empty(&layer->dma_queue)) {
- layer->next_frm = list_entry(layer->dma_queue.next,
- struct vpbe_disp_buffer, list);
- list_del(&layer->next_frm->list);
- vb2_buffer_done(&layer->next_frm->vb.vb2_buf,
- VB2_BUF_STATE_ERROR);
- }
- spin_unlock_irqrestore(&disp->dma_queue_lock, flags);
-}
-
-static const struct vb2_ops video_qops = {
- .queue_setup = vpbe_buffer_queue_setup,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
- .buf_prepare = vpbe_buffer_prepare,
- .start_streaming = vpbe_start_streaming,
- .stop_streaming = vpbe_stop_streaming,
- .buf_queue = vpbe_buffer_queue,
-};
-
-static
-struct vpbe_layer*
-_vpbe_display_get_other_win_layer(struct vpbe_display *disp_dev,
- struct vpbe_layer *layer)
-{
- enum vpbe_display_device_id thiswin, otherwin;
- thiswin = layer->device_id;
-
- otherwin = (thiswin == VPBE_DISPLAY_DEVICE_0) ?
- VPBE_DISPLAY_DEVICE_1 : VPBE_DISPLAY_DEVICE_0;
- return disp_dev->dev[otherwin];
-}
-
-static int vpbe_set_osd_display_params(struct vpbe_display *disp_dev,
- struct vpbe_layer *layer)
-{
- struct osd_layer_config *cfg = &layer->layer_info.config;
- struct osd_state *osd_device = disp_dev->osd_device;
- struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev;
- unsigned long addr;
- int ret;
-
- addr = vb2_dma_contig_plane_dma_addr(&layer->cur_frm->vb.vb2_buf, 0);
- /* Set address in the display registers */
- osd_device->ops.start_layer(osd_device,
- layer->layer_info.id,
- addr,
- disp_dev->cbcr_ofst);
-
- ret = osd_device->ops.enable_layer(osd_device,
- layer->layer_info.id, 0);
- if (ret < 0) {
- v4l2_err(&vpbe_dev->v4l2_dev,
- "Error in enabling osd window layer 0\n");
- return -1;
- }
-
- /* Enable the window */
- layer->layer_info.enable = 1;
- if (cfg->pixfmt == PIXFMT_NV12) {
- struct vpbe_layer *otherlayer =
- _vpbe_display_get_other_win_layer(disp_dev, layer);
-
- ret = osd_device->ops.enable_layer(osd_device,
- otherlayer->layer_info.id, 1);
- if (ret < 0) {
- v4l2_err(&vpbe_dev->v4l2_dev,
- "Error in enabling osd window layer 1\n");
- return -1;
- }
- otherlayer->layer_info.enable = 1;
- }
- return 0;
-}
-
-static void
-vpbe_disp_calculate_scale_factor(struct vpbe_display *disp_dev,
- struct vpbe_layer *layer,
- int expected_xsize, int expected_ysize)
-{
- struct display_layer_info *layer_info = &layer->layer_info;
- struct v4l2_pix_format *pixfmt = &layer->pix_fmt;
- struct osd_layer_config *cfg = &layer->layer_info.config;
- struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev;
- int calculated_xsize;
- int h_exp = 0;
- int v_exp = 0;
- int h_scale;
- int v_scale;
-
- v4l2_std_id standard_id = vpbe_dev->current_timings.std_id;
-
- /*
- * Application initially set the image format. Current display
- * size is obtained from the vpbe display controller. expected_xsize
- * and expected_ysize are set through S_SELECTION ioctl. Based on this,
- * driver will calculate the scale factors for vertical and
- * horizontal direction so that the image is displayed scaled
- * and expanded. Application uses expansion to display the image
- * in a square pixel. Otherwise it is displayed using displays
- * pixel aspect ratio.It is expected that application chooses
- * the crop coordinates for cropped or scaled display. if crop
- * size is less than the image size, it is displayed cropped or
- * it is displayed scaled and/or expanded.
- *
- * to begin with, set the crop window same as expected. Later we
- * will override with scaled window size
- */
-
- cfg->xsize = pixfmt->width;
- cfg->ysize = pixfmt->height;
- layer_info->h_zoom = ZOOM_X1; /* no horizontal zoom */
- layer_info->v_zoom = ZOOM_X1; /* no horizontal zoom */
- layer_info->h_exp = H_EXP_OFF; /* no horizontal zoom */
- layer_info->v_exp = V_EXP_OFF; /* no horizontal zoom */
-
- if (pixfmt->width < expected_xsize) {
- h_scale = vpbe_dev->current_timings.xres / pixfmt->width;
- if (h_scale < 2)
- h_scale = 1;
- else if (h_scale >= 4)
- h_scale = 4;
- else
- h_scale = 2;
- cfg->xsize *= h_scale;
- if (cfg->xsize < expected_xsize) {
- if ((standard_id & V4L2_STD_525_60) ||
- (standard_id & V4L2_STD_625_50)) {
- calculated_xsize = (cfg->xsize *
- VPBE_DISPLAY_H_EXP_RATIO_N) /
- VPBE_DISPLAY_H_EXP_RATIO_D;
- if (calculated_xsize <= expected_xsize) {
- h_exp = 1;
- cfg->xsize = calculated_xsize;
- }
- }
- }
- if (h_scale == 2)
- layer_info->h_zoom = ZOOM_X2;
- else if (h_scale == 4)
- layer_info->h_zoom = ZOOM_X4;
- if (h_exp)
- layer_info->h_exp = H_EXP_9_OVER_8;
- } else {
- /* no scaling, only cropping. Set display area to crop area */
- cfg->xsize = expected_xsize;
- }
-
- if (pixfmt->height < expected_ysize) {
- v_scale = expected_ysize / pixfmt->height;
- if (v_scale < 2)
- v_scale = 1;
- else if (v_scale >= 4)
- v_scale = 4;
- else
- v_scale = 2;
- cfg->ysize *= v_scale;
- if (cfg->ysize < expected_ysize) {
- if ((standard_id & V4L2_STD_625_50)) {
- calculated_xsize = (cfg->ysize *
- VPBE_DISPLAY_V_EXP_RATIO_N) /
- VPBE_DISPLAY_V_EXP_RATIO_D;
- if (calculated_xsize <= expected_ysize) {
- v_exp = 1;
- cfg->ysize = calculated_xsize;
- }
- }
- }
- if (v_scale == 2)
- layer_info->v_zoom = ZOOM_X2;
- else if (v_scale == 4)
- layer_info->v_zoom = ZOOM_X4;
- if (v_exp)
- layer_info->v_exp = V_EXP_6_OVER_5;
- } else {
- /* no scaling, only cropping. Set display area to crop area */
- cfg->ysize = expected_ysize;
- }
- v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
- "crop display xsize = %d, ysize = %d\n",
- cfg->xsize, cfg->ysize);
-}
-
-static void vpbe_disp_adj_position(struct vpbe_display *disp_dev,
- struct vpbe_layer *layer,
- int top, int left)
-{
- struct osd_layer_config *cfg = &layer->layer_info.config;
- struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev;
-
- cfg->xpos = min((unsigned int)left,
- vpbe_dev->current_timings.xres - cfg->xsize);
- cfg->ypos = min((unsigned int)top,
- vpbe_dev->current_timings.yres - cfg->ysize);
-
- v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
- "new xpos = %d, ypos = %d\n",
- cfg->xpos, cfg->ypos);
-}
-
-static void vpbe_disp_check_window_params(struct vpbe_display *disp_dev,
- struct v4l2_rect *c)
-{
- struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev;
-
- if ((c->width == 0) ||
- ((c->width + c->left) > vpbe_dev->current_timings.xres))
- c->width = vpbe_dev->current_timings.xres - c->left;
-
- if ((c->height == 0) || ((c->height + c->top) >
- vpbe_dev->current_timings.yres))
- c->height = vpbe_dev->current_timings.yres - c->top;
-
- /* window height must be even for interlaced display */
- if (vpbe_dev->current_timings.interlaced)
- c->height &= (~0x01);
-
-}
-
-/*
- * vpbe_try_format()
- * If user application provides width and height, and have bytesperline set
- * to zero, driver calculates bytesperline and sizeimage based on hardware
- * limits.
- */
-static int vpbe_try_format(struct vpbe_display *disp_dev,
- struct v4l2_pix_format *pixfmt, int check)
-{
- struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev;
- int min_height = 1;
- int min_width = 32;
- int max_height;
- int max_width;
- int bpp;
-
- if ((pixfmt->pixelformat != V4L2_PIX_FMT_UYVY) &&
- (pixfmt->pixelformat != V4L2_PIX_FMT_NV12))
- /* choose default as V4L2_PIX_FMT_UYVY */
- pixfmt->pixelformat = V4L2_PIX_FMT_UYVY;
-
- /* Check the field format */
- if ((pixfmt->field != V4L2_FIELD_INTERLACED) &&
- (pixfmt->field != V4L2_FIELD_NONE)) {
- if (vpbe_dev->current_timings.interlaced)
- pixfmt->field = V4L2_FIELD_INTERLACED;
- else
- pixfmt->field = V4L2_FIELD_NONE;
- }
-
- if (pixfmt->field == V4L2_FIELD_INTERLACED)
- min_height = 2;
-
- if (pixfmt->pixelformat == V4L2_PIX_FMT_NV12)
- bpp = 1;
- else
- bpp = 2;
-
- max_width = vpbe_dev->current_timings.xres;
- max_height = vpbe_dev->current_timings.yres;
-
- min_width /= bpp;
-
- if (!pixfmt->width || (pixfmt->width < min_width) ||
- (pixfmt->width > max_width)) {
- pixfmt->width = vpbe_dev->current_timings.xres;
- }
-
- if (!pixfmt->height || (pixfmt->height < min_height) ||
- (pixfmt->height > max_height)) {
- pixfmt->height = vpbe_dev->current_timings.yres;
- }
-
- if (pixfmt->bytesperline < (pixfmt->width * bpp))
- pixfmt->bytesperline = pixfmt->width * bpp;
-
- /* Make the bytesperline 32 byte aligned */
- pixfmt->bytesperline = ((pixfmt->width * bpp + 31) & ~31);
-
- if (pixfmt->pixelformat == V4L2_PIX_FMT_NV12)
- pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height +
- (pixfmt->bytesperline * pixfmt->height >> 1);
- else
- pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height;
-
- return 0;
-}
-
-static int vpbe_display_querycap(struct file *file, void *priv,
- struct v4l2_capability *cap)
-{
- struct vpbe_layer *layer = video_drvdata(file);
- struct vpbe_device *vpbe_dev = layer->disp_dev->vpbe_dev;
-
- snprintf(cap->driver, sizeof(cap->driver), "%s",
- dev_name(vpbe_dev->pdev));
- strscpy(cap->card, vpbe_dev->cfg->module_name, sizeof(cap->card));
-
- return 0;
-}
-
-static int vpbe_display_s_selection(struct file *file, void *priv,
- struct v4l2_selection *sel)
-{
- struct vpbe_layer *layer = video_drvdata(file);
- struct vpbe_display *disp_dev = layer->disp_dev;
- struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev;
- struct osd_layer_config *cfg = &layer->layer_info.config;
- struct osd_state *osd_device = disp_dev->osd_device;
- struct v4l2_rect rect = sel->r;
- int ret;
-
- v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
- "VIDIOC_S_SELECTION, layer id = %d\n", layer->device_id);
-
- if (sel->type != V4L2_BUF_TYPE_VIDEO_OUTPUT ||
- sel->target != V4L2_SEL_TGT_CROP)
- return -EINVAL;
-
- if (rect.top < 0)
- rect.top = 0;
- if (rect.left < 0)
- rect.left = 0;
-
- vpbe_disp_check_window_params(disp_dev, &rect);
-
- osd_device->ops.get_layer_config(osd_device,
- layer->layer_info.id, cfg);
-
- vpbe_disp_calculate_scale_factor(disp_dev, layer,
- rect.width,
- rect.height);
- vpbe_disp_adj_position(disp_dev, layer, rect.top,
- rect.left);
- ret = osd_device->ops.set_layer_config(osd_device,
- layer->layer_info.id, cfg);
- if (ret < 0) {
- v4l2_err(&vpbe_dev->v4l2_dev,
- "Error in set layer config:\n");
- return -EINVAL;
- }
-
- /* apply zooming and h or v expansion */
- osd_device->ops.set_zoom(osd_device,
- layer->layer_info.id,
- layer->layer_info.h_zoom,
- layer->layer_info.v_zoom);
- ret = osd_device->ops.set_vid_expansion(osd_device,
- layer->layer_info.h_exp,
- layer->layer_info.v_exp);
- if (ret < 0) {
- v4l2_err(&vpbe_dev->v4l2_dev,
- "Error in set vid expansion:\n");
- return -EINVAL;
- }
-
- if ((layer->layer_info.h_zoom != ZOOM_X1) ||
- (layer->layer_info.v_zoom != ZOOM_X1) ||
- (layer->layer_info.h_exp != H_EXP_OFF) ||
- (layer->layer_info.v_exp != V_EXP_OFF))
- /* Enable expansion filter */
- osd_device->ops.set_interpolation_filter(osd_device, 1);
- else
- osd_device->ops.set_interpolation_filter(osd_device, 0);
-
- sel->r = rect;
- return 0;
-}
-
-static int vpbe_display_g_selection(struct file *file, void *priv,
- struct v4l2_selection *sel)
-{
- struct vpbe_layer *layer = video_drvdata(file);
- struct osd_layer_config *cfg = &layer->layer_info.config;
- struct vpbe_device *vpbe_dev = layer->disp_dev->vpbe_dev;
- struct osd_state *osd_device = layer->disp_dev->osd_device;
- struct v4l2_rect *rect = &sel->r;
-
- v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
- "VIDIOC_G_SELECTION, layer id = %d\n",
- layer->device_id);
-
- if (sel->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
- return -EINVAL;
-
- switch (sel->target) {
- case V4L2_SEL_TGT_CROP:
- osd_device->ops.get_layer_config(osd_device,
- layer->layer_info.id, cfg);
- rect->top = cfg->ypos;
- rect->left = cfg->xpos;
- rect->width = cfg->xsize;
- rect->height = cfg->ysize;
- break;
- case V4L2_SEL_TGT_CROP_DEFAULT:
- case V4L2_SEL_TGT_CROP_BOUNDS:
- rect->left = 0;
- rect->top = 0;
- rect->width = vpbe_dev->current_timings.xres;
- rect->height = vpbe_dev->current_timings.yres;
- break;
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int vpbe_display_g_pixelaspect(struct file *file, void *priv,
- int type, struct v4l2_fract *f)
-{
- struct vpbe_layer *layer = video_drvdata(file);
- struct vpbe_device *vpbe_dev = layer->disp_dev->vpbe_dev;
-
- v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_CROPCAP ioctl\n");
-
- if (type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
- return -EINVAL;
-
- *f = vpbe_dev->current_timings.aspect;
- return 0;
-}
-
-static int vpbe_display_g_fmt(struct file *file, void *priv,
- struct v4l2_format *fmt)
-{
- struct vpbe_layer *layer = video_drvdata(file);
- struct vpbe_device *vpbe_dev = layer->disp_dev->vpbe_dev;
-
- v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
- "VIDIOC_G_FMT, layer id = %d\n",
- layer->device_id);
-
- /* If buffer type is video output */
- if (V4L2_BUF_TYPE_VIDEO_OUTPUT != fmt->type) {
- v4l2_err(&vpbe_dev->v4l2_dev, "invalid type\n");
- return -EINVAL;
- }
- /* Fill in the information about format */
- fmt->fmt.pix = layer->pix_fmt;
-
- return 0;
-}
-
-static int vpbe_display_enum_fmt(struct file *file, void *priv,
- struct v4l2_fmtdesc *fmt)
-{
- struct vpbe_layer *layer = video_drvdata(file);
- struct vpbe_device *vpbe_dev = layer->disp_dev->vpbe_dev;
-
- v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
- "VIDIOC_ENUM_FMT, layer id = %d\n",
- layer->device_id);
- if (fmt->index > 1) {
- v4l2_err(&vpbe_dev->v4l2_dev, "Invalid format index\n");
- return -EINVAL;
- }
-
- /* Fill in the information about format */
- if (fmt->index == 0)
- fmt->pixelformat = V4L2_PIX_FMT_UYVY;
- else
- fmt->pixelformat = V4L2_PIX_FMT_NV12;
-
- return 0;
-}
-
-static int vpbe_display_s_fmt(struct file *file, void *priv,
- struct v4l2_format *fmt)
-{
- struct vpbe_layer *layer = video_drvdata(file);
- struct vpbe_display *disp_dev = layer->disp_dev;
- struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev;
- struct osd_layer_config *cfg = &layer->layer_info.config;
- struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;
- struct osd_state *osd_device = disp_dev->osd_device;
- int ret;
-
- v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
- "VIDIOC_S_FMT, layer id = %d\n",
- layer->device_id);
-
- if (vb2_is_busy(&layer->buffer_queue))
- return -EBUSY;
-
- if (V4L2_BUF_TYPE_VIDEO_OUTPUT != fmt->type) {
- v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "invalid type\n");
- return -EINVAL;
- }
- /* Check for valid pixel format */
- ret = vpbe_try_format(disp_dev, pixfmt, 1);
- if (ret)
- return ret;
-
- /* YUV420 is requested, check availability of the
- other video window */
-
- layer->pix_fmt = *pixfmt;
- if (pixfmt->pixelformat == V4L2_PIX_FMT_NV12) {
- struct vpbe_layer *otherlayer;
-
- otherlayer = _vpbe_display_get_other_win_layer(disp_dev, layer);
- /* if other layer is available, only
- * claim it, do not configure it
- */
- ret = osd_device->ops.request_layer(osd_device,
- otherlayer->layer_info.id);
- if (ret < 0) {
- v4l2_err(&vpbe_dev->v4l2_dev,
- "Display Manager failed to allocate layer\n");
- return -EBUSY;
- }
- }
-
- /* Get osd layer config */
- osd_device->ops.get_layer_config(osd_device,
- layer->layer_info.id, cfg);
- /* Store the pixel format in the layer object */
- cfg->xsize = pixfmt->width;
- cfg->ysize = pixfmt->height;
- cfg->line_length = pixfmt->bytesperline;
- cfg->ypos = 0;
- cfg->xpos = 0;
- cfg->interlaced = vpbe_dev->current_timings.interlaced;
-
- if (V4L2_PIX_FMT_UYVY == pixfmt->pixelformat)
- cfg->pixfmt = PIXFMT_YCBCRI;
-
- /* Change of the default pixel format for both video windows */
- if (V4L2_PIX_FMT_NV12 == pixfmt->pixelformat) {
- struct vpbe_layer *otherlayer;
- cfg->pixfmt = PIXFMT_NV12;
- otherlayer = _vpbe_display_get_other_win_layer(disp_dev,
- layer);
- otherlayer->layer_info.config.pixfmt = PIXFMT_NV12;
- }
-
- /* Set the layer config in the osd window */
- ret = osd_device->ops.set_layer_config(osd_device,
- layer->layer_info.id, cfg);
- if (ret < 0) {
- v4l2_err(&vpbe_dev->v4l2_dev,
- "Error in S_FMT params:\n");
- return -EINVAL;
- }
-
- /* Readback and fill the local copy of current pix format */
- osd_device->ops.get_layer_config(osd_device,
- layer->layer_info.id, cfg);
-
- return 0;
-}
-
-static int vpbe_display_try_fmt(struct file *file, void *priv,
- struct v4l2_format *fmt)
-{
- struct vpbe_layer *layer = video_drvdata(file);
- struct vpbe_display *disp_dev = layer->disp_dev;
- struct vpbe_device *vpbe_dev = layer->disp_dev->vpbe_dev;
- struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;
-
- v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_TRY_FMT\n");
-
- if (V4L2_BUF_TYPE_VIDEO_OUTPUT != fmt->type) {
- v4l2_err(&vpbe_dev->v4l2_dev, "invalid type\n");
- return -EINVAL;
- }
-
- /* Check for valid field format */
- return vpbe_try_format(disp_dev, pixfmt, 0);
-
-}
-
-/*
- * vpbe_display_s_std - Set the given standard in the encoder
- *
- * Sets the standard if supported by the current encoder. Return the status.
- * 0 - success & -EINVAL on error
- */
-static int vpbe_display_s_std(struct file *file, void *priv,
- v4l2_std_id std_id)
-{
- struct vpbe_layer *layer = video_drvdata(file);
- struct vpbe_device *vpbe_dev = layer->disp_dev->vpbe_dev;
- int ret;
-
- v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_S_STD\n");
-
- if (vb2_is_busy(&layer->buffer_queue))
- return -EBUSY;
-
- if (vpbe_dev->ops.s_std) {
- ret = vpbe_dev->ops.s_std(vpbe_dev, std_id);
- if (ret) {
- v4l2_err(&vpbe_dev->v4l2_dev,
- "Failed to set standard for sub devices\n");
- return -EINVAL;
- }
- } else {
- return -EINVAL;
- }
-
- return 0;
-}
-
-/*
- * vpbe_display_g_std - Get the standard in the current encoder
- *
- * Get the standard in the current encoder. Return the status. 0 - success
- * -EINVAL on error
- */
-static int vpbe_display_g_std(struct file *file, void *priv,
- v4l2_std_id *std_id)
-{
- struct vpbe_layer *layer = video_drvdata(file);
- struct vpbe_device *vpbe_dev = layer->disp_dev->vpbe_dev;
-
- v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_G_STD\n");
-
- /* Get the standard from the current encoder */
- if (vpbe_dev->current_timings.timings_type & VPBE_ENC_STD) {
- *std_id = vpbe_dev->current_timings.std_id;
- return 0;
- }
-
- return -EINVAL;
-}
-
-/*
- * vpbe_display_enum_output - enumerate outputs
- *
- * Enumerates the outputs available at the vpbe display
- * returns the status, -EINVAL if end of output list
- */
-static int vpbe_display_enum_output(struct file *file, void *priv,
- struct v4l2_output *output)
-{
- struct vpbe_layer *layer = video_drvdata(file);
- struct vpbe_device *vpbe_dev = layer->disp_dev->vpbe_dev;
- int ret;
-
- v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_ENUM_OUTPUT\n");
-
- /* Enumerate outputs */
- if (!vpbe_dev->ops.enum_outputs)
- return -EINVAL;
-
- ret = vpbe_dev->ops.enum_outputs(vpbe_dev, output);
- if (ret) {
- v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
- "Failed to enumerate outputs\n");
- return -EINVAL;
- }
-
- return 0;
-}
-
-/*
- * vpbe_display_s_output - Set output to
- * the output specified by the index
- */
-static int vpbe_display_s_output(struct file *file, void *priv,
- unsigned int i)
-{
- struct vpbe_layer *layer = video_drvdata(file);
- struct vpbe_device *vpbe_dev = layer->disp_dev->vpbe_dev;
- int ret;
-
- v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_S_OUTPUT\n");
-
- if (vb2_is_busy(&layer->buffer_queue))
- return -EBUSY;
-
- if (!vpbe_dev->ops.set_output)
- return -EINVAL;
-
- ret = vpbe_dev->ops.set_output(vpbe_dev, i);
- if (ret) {
- v4l2_err(&vpbe_dev->v4l2_dev,
- "Failed to set output for sub devices\n");
- return -EINVAL;
- }
-
- return 0;
-}
-
-/*
- * vpbe_display_g_output - Get output from subdevice
- * for a given by the index
- */
-static int vpbe_display_g_output(struct file *file, void *priv,
- unsigned int *i)
-{
- struct vpbe_layer *layer = video_drvdata(file);
- struct vpbe_device *vpbe_dev = layer->disp_dev->vpbe_dev;
-
- v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_G_OUTPUT\n");
- /* Get the standard from the current encoder */
- *i = vpbe_dev->current_out_index;
-
- return 0;
-}
-
-/*
- * vpbe_display_enum_dv_timings - Enumerate the dv timings
- *
- * enum the timings in the current encoder. Return the status. 0 - success
- * -EINVAL on error
- */
-static int
-vpbe_display_enum_dv_timings(struct file *file, void *priv,
- struct v4l2_enum_dv_timings *timings)
-{
- struct vpbe_layer *layer = video_drvdata(file);
- struct vpbe_device *vpbe_dev = layer->disp_dev->vpbe_dev;
- int ret;
-
- v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_ENUM_DV_TIMINGS\n");
-
- /* Enumerate outputs */
- if (!vpbe_dev->ops.enum_dv_timings)
- return -EINVAL;
-
- ret = vpbe_dev->ops.enum_dv_timings(vpbe_dev, timings);
- if (ret) {
- v4l2_err(&vpbe_dev->v4l2_dev,
- "Failed to enumerate dv timings info\n");
- return -EINVAL;
- }
-
- return 0;
-}
-
-/*
- * vpbe_display_s_dv_timings - Set the dv timings
- *
- * Set the timings in the current encoder. Return the status. 0 - success
- * -EINVAL on error
- */
-static int
-vpbe_display_s_dv_timings(struct file *file, void *priv,
- struct v4l2_dv_timings *timings)
-{
- struct vpbe_layer *layer = video_drvdata(file);
- struct vpbe_device *vpbe_dev = layer->disp_dev->vpbe_dev;
- int ret;
-
- v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_S_DV_TIMINGS\n");
-
- if (vb2_is_busy(&layer->buffer_queue))
- return -EBUSY;
-
- /* Set the given standard in the encoder */
- if (!vpbe_dev->ops.s_dv_timings)
- return -EINVAL;
-
- ret = vpbe_dev->ops.s_dv_timings(vpbe_dev, timings);
- if (ret) {
- v4l2_err(&vpbe_dev->v4l2_dev,
- "Failed to set the dv timings info\n");
- return -EINVAL;
- }
-
- return 0;
-}
-
-/*
- * vpbe_display_g_dv_timings - Set the dv timings
- *
- * Get the timings in the current encoder. Return the status. 0 - success
- * -EINVAL on error
- */
-static int
-vpbe_display_g_dv_timings(struct file *file, void *priv,
- struct v4l2_dv_timings *dv_timings)
-{
- struct vpbe_layer *layer = video_drvdata(file);
- struct vpbe_device *vpbe_dev = layer->disp_dev->vpbe_dev;
-
- v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_G_DV_TIMINGS\n");
-
- /* Get the given standard in the encoder */
-
- if (vpbe_dev->current_timings.timings_type &
- VPBE_ENC_DV_TIMINGS) {
- *dv_timings = vpbe_dev->current_timings.dv_timings;
- } else {
- return -EINVAL;
- }
-
- return 0;
-}
-
-/*
- * vpbe_display_open()
- * It creates object of file handle structure and stores it in private_data
- * member of filepointer
- */
-static int vpbe_display_open(struct file *file)
-{
- struct vpbe_layer *layer = video_drvdata(file);
- struct vpbe_display *disp_dev = layer->disp_dev;
- struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev;
- struct osd_state *osd_device = disp_dev->osd_device;
- int err;
-
- /* creating context for file descriptor */
- err = v4l2_fh_open(file);
- if (err) {
- v4l2_err(&vpbe_dev->v4l2_dev, "v4l2_fh_open failed\n");
- return err;
- }
-
- /* leaving if layer is already initialized */
- if (!v4l2_fh_is_singular_file(file))
- return err;
-
- if (!layer->usrs) {
- if (mutex_lock_interruptible(&layer->opslock))
- return -ERESTARTSYS;
- /* First claim the layer for this device */
- err = osd_device->ops.request_layer(osd_device,
- layer->layer_info.id);
- mutex_unlock(&layer->opslock);
- if (err < 0) {
- /* Couldn't get layer */
- v4l2_err(&vpbe_dev->v4l2_dev,
- "Display Manager failed to allocate layer\n");
- v4l2_fh_release(file);
- return -EINVAL;
- }
- }
- /* Increment layer usrs counter */
- layer->usrs++;
- v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
- "vpbe display device opened successfully\n");
- return 0;
-}
-
-/*
- * vpbe_display_release()
- * This function deletes buffer queue, frees the buffers and the davinci
- * display file * handle
- */
-static int vpbe_display_release(struct file *file)
-{
- struct vpbe_layer *layer = video_drvdata(file);
- struct osd_layer_config *cfg = &layer->layer_info.config;
- struct vpbe_display *disp_dev = layer->disp_dev;
- struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev;
- struct osd_state *osd_device = disp_dev->osd_device;
-
- v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_display_release\n");
-
- mutex_lock(&layer->opslock);
-
- osd_device->ops.disable_layer(osd_device,
- layer->layer_info.id);
- /* Decrement layer usrs counter */
- layer->usrs--;
- /* If this file handle has initialize encoder device, reset it */
- if (!layer->usrs) {
- if (cfg->pixfmt == PIXFMT_NV12) {
- struct vpbe_layer *otherlayer;
- otherlayer =
- _vpbe_display_get_other_win_layer(disp_dev, layer);
- osd_device->ops.disable_layer(osd_device,
- otherlayer->layer_info.id);
- osd_device->ops.release_layer(osd_device,
- otherlayer->layer_info.id);
- }
- osd_device->ops.disable_layer(osd_device,
- layer->layer_info.id);
- osd_device->ops.release_layer(osd_device,
- layer->layer_info.id);
- }
-
- _vb2_fop_release(file, NULL);
- mutex_unlock(&layer->opslock);
-
- disp_dev->cbcr_ofst = 0;
-
- return 0;
-}
-
-/* vpbe capture ioctl operations */
-static const struct v4l2_ioctl_ops vpbe_ioctl_ops = {
- .vidioc_querycap = vpbe_display_querycap,
- .vidioc_g_fmt_vid_out = vpbe_display_g_fmt,
- .vidioc_enum_fmt_vid_out = vpbe_display_enum_fmt,
- .vidioc_s_fmt_vid_out = vpbe_display_s_fmt,
- .vidioc_try_fmt_vid_out = vpbe_display_try_fmt,
-
- .vidioc_reqbufs = vb2_ioctl_reqbufs,
- .vidioc_create_bufs = vb2_ioctl_create_bufs,
- .vidioc_querybuf = vb2_ioctl_querybuf,
- .vidioc_qbuf = vb2_ioctl_qbuf,
- .vidioc_dqbuf = vb2_ioctl_dqbuf,
- .vidioc_streamon = vb2_ioctl_streamon,
- .vidioc_streamoff = vb2_ioctl_streamoff,
- .vidioc_expbuf = vb2_ioctl_expbuf,
-
- .vidioc_g_pixelaspect = vpbe_display_g_pixelaspect,
- .vidioc_g_selection = vpbe_display_g_selection,
- .vidioc_s_selection = vpbe_display_s_selection,
-
- .vidioc_s_std = vpbe_display_s_std,
- .vidioc_g_std = vpbe_display_g_std,
-
- .vidioc_enum_output = vpbe_display_enum_output,
- .vidioc_s_output = vpbe_display_s_output,
- .vidioc_g_output = vpbe_display_g_output,
-
- .vidioc_s_dv_timings = vpbe_display_s_dv_timings,
- .vidioc_g_dv_timings = vpbe_display_g_dv_timings,
- .vidioc_enum_dv_timings = vpbe_display_enum_dv_timings,
-};
-
-static const struct v4l2_file_operations vpbe_fops = {
- .owner = THIS_MODULE,
- .open = vpbe_display_open,
- .release = vpbe_display_release,
- .unlocked_ioctl = video_ioctl2,
- .mmap = vb2_fop_mmap,
- .poll = vb2_fop_poll,
-};
-
-static int vpbe_device_get(struct device *dev, void *data)
-{
- struct platform_device *pdev = to_platform_device(dev);
- struct vpbe_display *vpbe_disp = data;
-
- if (strcmp("vpbe_controller", pdev->name) == 0)
- vpbe_disp->vpbe_dev = platform_get_drvdata(pdev);
-
- if (strstr(pdev->name, "vpbe-osd"))
- vpbe_disp->osd_device = platform_get_drvdata(pdev);
-
- return 0;
-}
-
-static int init_vpbe_layer(int i, struct vpbe_display *disp_dev,
- struct platform_device *pdev)
-{
- struct vpbe_layer *vpbe_display_layer = NULL;
- struct video_device *vbd = NULL;
-
- /* Allocate memory for four plane display objects */
- disp_dev->dev[i] = kzalloc(sizeof(*disp_dev->dev[i]), GFP_KERNEL);
- if (!disp_dev->dev[i])
- return -ENOMEM;
-
- spin_lock_init(&disp_dev->dev[i]->irqlock);
- mutex_init(&disp_dev->dev[i]->opslock);
-
- /* Get the pointer to the layer object */
- vpbe_display_layer = disp_dev->dev[i];
- vbd = &vpbe_display_layer->video_dev;
- /* Initialize field of video device */
- vbd->release = video_device_release_empty;
- vbd->fops = &vpbe_fops;
- vbd->ioctl_ops = &vpbe_ioctl_ops;
- vbd->minor = -1;
- vbd->v4l2_dev = &disp_dev->vpbe_dev->v4l2_dev;
- vbd->lock = &vpbe_display_layer->opslock;
- vbd->vfl_dir = VFL_DIR_TX;
- vbd->device_caps = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING;
-
- if (disp_dev->vpbe_dev->current_timings.timings_type &
- VPBE_ENC_STD)
- vbd->tvnorms = (V4L2_STD_525_60 | V4L2_STD_625_50);
-
- snprintf(vbd->name, sizeof(vbd->name),
- "DaVinci_VPBE Display_DRIVER_V%d.%d.%d",
- (VPBE_DISPLAY_VERSION_CODE >> 16) & 0xff,
- (VPBE_DISPLAY_VERSION_CODE >> 8) & 0xff,
- (VPBE_DISPLAY_VERSION_CODE) & 0xff);
-
- vpbe_display_layer->device_id = i;
-
- vpbe_display_layer->layer_info.id =
- ((i == VPBE_DISPLAY_DEVICE_0) ? WIN_VID0 : WIN_VID1);
-
-
- return 0;
-}
-
-static int register_device(struct vpbe_layer *vpbe_display_layer,
- struct vpbe_display *disp_dev,
- struct platform_device *pdev)
-{
- int err;
-
- v4l2_info(&disp_dev->vpbe_dev->v4l2_dev,
- "Trying to register VPBE display device.\n");
- v4l2_info(&disp_dev->vpbe_dev->v4l2_dev,
- "layer=%p,layer->video_dev=%p\n",
- vpbe_display_layer,
- &vpbe_display_layer->video_dev);
-
- vpbe_display_layer->video_dev.queue = &vpbe_display_layer->buffer_queue;
- err = video_register_device(&vpbe_display_layer->video_dev,
- VFL_TYPE_VIDEO,
- -1);
- if (err)
- return -ENODEV;
-
- vpbe_display_layer->disp_dev = disp_dev;
- /* set the driver data in platform device */
- platform_set_drvdata(pdev, disp_dev);
- video_set_drvdata(&vpbe_display_layer->video_dev,
- vpbe_display_layer);
-
- return 0;
-}
-
-
-
-/*
- * vpbe_display_probe()
- * This function creates device entries by register itself to the V4L2 driver
- * and initializes fields of each layer objects
- */
-static int vpbe_display_probe(struct platform_device *pdev)
-{
- struct vpbe_display *disp_dev;
- struct v4l2_device *v4l2_dev;
- struct resource *res = NULL;
- struct vb2_queue *q;
- int k;
- int i;
- int err;
- int irq;
-
- printk(KERN_DEBUG "vpbe_display_probe\n");
- /* Allocate memory for vpbe_display */
- disp_dev = devm_kzalloc(&pdev->dev, sizeof(*disp_dev), GFP_KERNEL);
- if (!disp_dev)
- return -ENOMEM;
-
- spin_lock_init(&disp_dev->dma_queue_lock);
- /*
- * Scan all the platform devices to find the vpbe
- * controller device and get the vpbe_dev object
- */
- err = bus_for_each_dev(&platform_bus_type, NULL, disp_dev,
- vpbe_device_get);
- if (err < 0)
- return err;
-
- v4l2_dev = &disp_dev->vpbe_dev->v4l2_dev;
- /* Initialize the vpbe display controller */
- if (disp_dev->vpbe_dev->ops.initialize) {
- err = disp_dev->vpbe_dev->ops.initialize(&pdev->dev,
- disp_dev->vpbe_dev);
- if (err) {
- v4l2_err(v4l2_dev, "Error initing vpbe\n");
- err = -ENOMEM;
- goto probe_out;
- }
- }
-
- for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) {
- if (init_vpbe_layer(i, disp_dev, pdev)) {
- err = -ENODEV;
- goto probe_out;
- }
- }
-
- res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (!res) {
- v4l2_err(v4l2_dev, "Unable to get VENC interrupt resource\n");
- err = -ENODEV;
- goto probe_out;
- }
-
- irq = res->start;
- err = devm_request_irq(&pdev->dev, irq, venc_isr, 0,
- VPBE_DISPLAY_DRIVER, disp_dev);
- if (err) {
- v4l2_err(v4l2_dev, "VPBE IRQ request failed\n");
- goto probe_out;
- }
-
- for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) {
- /* initialize vb2 queue */
- q = &disp_dev->dev[i]->buffer_queue;
- memset(q, 0, sizeof(*q));
- q->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
- q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
- q->drv_priv = disp_dev->dev[i];
- q->ops = &video_qops;
- q->mem_ops = &vb2_dma_contig_memops;
- q->buf_struct_size = sizeof(struct vpbe_disp_buffer);
- q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
- q->min_buffers_needed = 1;
- q->lock = &disp_dev->dev[i]->opslock;
- q->dev = disp_dev->vpbe_dev->pdev;
- err = vb2_queue_init(q);
- if (err) {
- v4l2_err(v4l2_dev, "vb2_queue_init() failed\n");
- goto probe_out;
- }
-
- INIT_LIST_HEAD(&disp_dev->dev[i]->dma_queue);
-
- if (register_device(disp_dev->dev[i], disp_dev, pdev)) {
- err = -ENODEV;
- goto probe_out;
- }
- }
-
- v4l2_dbg(1, debug, v4l2_dev,
- "Successfully completed the probing of vpbe v4l2 device\n");
-
- return 0;
-
-probe_out:
- for (k = 0; k < VPBE_DISPLAY_MAX_DEVICES; k++) {
- /* Unregister video device */
- if (disp_dev->dev[k]) {
- video_unregister_device(&disp_dev->dev[k]->video_dev);
- kfree(disp_dev->dev[k]);
- }
- }
- return err;
-}
-
-/*
- * vpbe_display_remove()
- * It un-register hardware layer from V4L2 driver
- */
-static int vpbe_display_remove(struct platform_device *pdev)
-{
- struct vpbe_layer *vpbe_display_layer;
- struct vpbe_display *disp_dev = platform_get_drvdata(pdev);
- struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev;
- int i;
-
- v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_display_remove\n");
-
- /* deinitialize the vpbe display controller */
- if (vpbe_dev->ops.deinitialize)
- vpbe_dev->ops.deinitialize(&pdev->dev, vpbe_dev);
- /* un-register device */
- for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) {
- /* Get the pointer to the layer object */
- vpbe_display_layer = disp_dev->dev[i];
- /* Unregister video device */
- video_unregister_device(&vpbe_display_layer->video_dev);
-
- }
- for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) {
- kfree(disp_dev->dev[i]);
- disp_dev->dev[i] = NULL;
- }
-
- return 0;
-}
-
-static struct platform_driver vpbe_display_driver = {
- .driver = {
- .name = VPBE_DISPLAY_DRIVER,
- .bus = &platform_bus_type,
- },
- .probe = vpbe_display_probe,
- .remove = vpbe_display_remove,
-};
-
-module_platform_driver(vpbe_display_driver);
-
-MODULE_DESCRIPTION("TI DM644x/DM355/DM365 VPBE Display controller");
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Texas Instruments");
diff --git a/drivers/media/platform/ti/davinci/vpbe_osd.c b/drivers/media/platform/ti/davinci/vpbe_osd.c
deleted file mode 100644
index 32f7ef547c82..000000000000
--- a/drivers/media/platform/ti/davinci/vpbe_osd.c
+++ /dev/null
@@ -1,1582 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * Copyright (C) 2007-2010 Texas Instruments Inc
- * Copyright (C) 2007 MontaVista Software, Inc.
- *
- * Andy Lowe (alowe@mvista.com), MontaVista Software
- * - Initial version
- * Murali Karicheri (mkaricheri@gmail.com), Texas Instruments Ltd.
- * - ported to sub device interface
- */
-#include <linux/module.h>
-#include <linux/mod_devicetable.h>
-#include <linux/kernel.h>
-#include <linux/interrupt.h>
-#include <linux/platform_device.h>
-#include <linux/clk.h>
-#include <linux/slab.h>
-
-#include <media/davinci/vpss.h>
-#include <media/v4l2-device.h>
-#include <media/davinci/vpbe_types.h>
-#include <media/davinci/vpbe_osd.h>
-
-#include <linux/io.h>
-#include "vpbe_osd_regs.h"
-
-#define MODULE_NAME "davinci-vpbe-osd"
-
-static const struct platform_device_id vpbe_osd_devtype[] = {
- {
- .name = DM644X_VPBE_OSD_SUBDEV_NAME,
- .driver_data = VPBE_VERSION_1,
- }, {
- .name = DM365_VPBE_OSD_SUBDEV_NAME,
- .driver_data = VPBE_VERSION_2,
- }, {
- .name = DM355_VPBE_OSD_SUBDEV_NAME,
- .driver_data = VPBE_VERSION_3,
- },
- {
- /* sentinel */
- }
-};
-
-MODULE_DEVICE_TABLE(platform, vpbe_osd_devtype);
-
-/* register access routines */
-static inline u32 __always_unused osd_read(struct osd_state *sd, u32 offset)
-{
- struct osd_state *osd = sd;
-
- return readl(osd->osd_base + offset);
-}
-
-static inline u32 osd_write(struct osd_state *sd, u32 val, u32 offset)
-{
- struct osd_state *osd = sd;
-
- writel(val, osd->osd_base + offset);
-
- return val;
-}
-
-static inline u32 osd_set(struct osd_state *sd, u32 mask, u32 offset)
-{
- struct osd_state *osd = sd;
-
- void __iomem *addr = osd->osd_base + offset;
- u32 val = readl(addr) | mask;
-
- writel(val, addr);
-
- return val;
-}
-
-static inline u32 osd_clear(struct osd_state *sd, u32 mask, u32 offset)
-{
- struct osd_state *osd = sd;
-
- void __iomem *addr = osd->osd_base + offset;
- u32 val = readl(addr) & ~mask;
-
- writel(val, addr);
-
- return val;
-}
-
-static inline u32 osd_modify(struct osd_state *sd, u32 mask, u32 val,
- u32 offset)
-{
- struct osd_state *osd = sd;
-
- void __iomem *addr = osd->osd_base + offset;
- u32 new_val = (readl(addr) & ~mask) | (val & mask);
-
- writel(new_val, addr);
-
- return new_val;
-}
-
-/* define some macros for layer and pixfmt classification */
-#define is_osd_win(layer) (((layer) == WIN_OSD0) || ((layer) == WIN_OSD1))
-#define is_vid_win(layer) (((layer) == WIN_VID0) || ((layer) == WIN_VID1))
-#define is_rgb_pixfmt(pixfmt) \
- (((pixfmt) == PIXFMT_RGB565) || ((pixfmt) == PIXFMT_RGB888))
-#define is_yc_pixfmt(pixfmt) \
- (((pixfmt) == PIXFMT_YCBCRI) || ((pixfmt) == PIXFMT_YCRCBI) || \
- ((pixfmt) == PIXFMT_NV12))
-#define MAX_WIN_SIZE OSD_VIDWIN0XP_V0X
-#define MAX_LINE_LENGTH (OSD_VIDWIN0OFST_V0LO << 5)
-
-/**
- * _osd_dm6446_vid0_pingpong() - field inversion fix for DM6446
- * @sd: ptr to struct osd_state
- * @field_inversion: inversion flag
- * @fb_base_phys: frame buffer address
- * @lconfig: ptr to layer config
- *
- * This routine implements a workaround for the field signal inversion silicon
- * erratum described in Advisory 1.3.8 for the DM6446. The fb_base_phys and
- * lconfig parameters apply to the vid0 window. This routine should be called
- * whenever the vid0 layer configuration or start address is modified, or when
- * the OSD field inversion setting is modified.
- * Returns: 1 if the ping-pong buffers need to be toggled in the vsync isr, or
- * 0 otherwise
- */
-static int _osd_dm6446_vid0_pingpong(struct osd_state *sd,
- int field_inversion,
- unsigned long fb_base_phys,
- const struct osd_layer_config *lconfig)
-{
- struct osd_platform_data *pdata;
-
- pdata = (struct osd_platform_data *)sd->dev->platform_data;
- if (pdata != NULL && pdata->field_inv_wa_enable) {
-
- if (!field_inversion || !lconfig->interlaced) {
- osd_write(sd, fb_base_phys & ~0x1F, OSD_VIDWIN0ADR);
- osd_write(sd, fb_base_phys & ~0x1F, OSD_PPVWIN0ADR);
- osd_modify(sd, OSD_MISCCTL_PPSW | OSD_MISCCTL_PPRV, 0,
- OSD_MISCCTL);
- return 0;
- } else {
- unsigned miscctl = OSD_MISCCTL_PPRV;
-
- osd_write(sd,
- (fb_base_phys & ~0x1F) - lconfig->line_length,
- OSD_VIDWIN0ADR);
- osd_write(sd,
- (fb_base_phys & ~0x1F) + lconfig->line_length,
- OSD_PPVWIN0ADR);
- osd_modify(sd,
- OSD_MISCCTL_PPSW | OSD_MISCCTL_PPRV, miscctl,
- OSD_MISCCTL);
-
- return 1;
- }
- }
-
- return 0;
-}
-
-static void _osd_set_field_inversion(struct osd_state *sd, int enable)
-{
- unsigned fsinv = 0;
-
- if (enable)
- fsinv = OSD_MODE_FSINV;
-
- osd_modify(sd, OSD_MODE_FSINV, fsinv, OSD_MODE);
-}
-
-static void _osd_set_blink_attribute(struct osd_state *sd, int enable,
- enum osd_blink_interval blink)
-{
- u32 osdatrmd = 0;
-
- if (enable) {
- osdatrmd |= OSD_OSDATRMD_BLNK;
- osdatrmd |= blink << OSD_OSDATRMD_BLNKINT_SHIFT;
- }
- /* caller must ensure that OSD1 is configured in attribute mode */
- osd_modify(sd, OSD_OSDATRMD_BLNKINT | OSD_OSDATRMD_BLNK, osdatrmd,
- OSD_OSDATRMD);
-}
-
-static void _osd_set_rom_clut(struct osd_state *sd,
- enum osd_rom_clut rom_clut)
-{
- if (rom_clut == ROM_CLUT0)
- osd_clear(sd, OSD_MISCCTL_RSEL, OSD_MISCCTL);
- else
- osd_set(sd, OSD_MISCCTL_RSEL, OSD_MISCCTL);
-}
-
-static void _osd_set_palette_map(struct osd_state *sd,
- enum osd_win_layer osdwin,
- unsigned char pixel_value,
- unsigned char clut_index,
- enum osd_pix_format pixfmt)
-{
- static const int map_2bpp[] = { 0, 5, 10, 15 };
- static const int map_1bpp[] = { 0, 15 };
- int bmp_offset;
- int bmp_shift;
- int bmp_mask;
- int bmp_reg;
-
- switch (pixfmt) {
- case PIXFMT_1BPP:
- bmp_reg = map_1bpp[pixel_value & 0x1];
- break;
- case PIXFMT_2BPP:
- bmp_reg = map_2bpp[pixel_value & 0x3];
- break;
- case PIXFMT_4BPP:
- bmp_reg = pixel_value & 0xf;
- break;
- default:
- return;
- }
-
- switch (osdwin) {
- case OSDWIN_OSD0:
- bmp_offset = OSD_W0BMP01 + (bmp_reg >> 1) * sizeof(u32);
- break;
- case OSDWIN_OSD1:
- bmp_offset = OSD_W1BMP01 + (bmp_reg >> 1) * sizeof(u32);
- break;
- default:
- return;
- }
-
- if (bmp_reg & 1) {
- bmp_shift = 8;
- bmp_mask = 0xff << 8;
- } else {
- bmp_shift = 0;
- bmp_mask = 0xff;
- }
-
- osd_modify(sd, bmp_mask, clut_index << bmp_shift, bmp_offset);
-}
-
-static void _osd_set_rec601_attenuation(struct osd_state *sd,
- enum osd_win_layer osdwin, int enable)
-{
- switch (osdwin) {
- case OSDWIN_OSD0:
- osd_modify(sd, OSD_OSDWIN0MD_ATN0E,
- enable ? OSD_OSDWIN0MD_ATN0E : 0,
- OSD_OSDWIN0MD);
- if (sd->vpbe_type == VPBE_VERSION_1)
- osd_modify(sd, OSD_OSDWIN0MD_ATN0E,
- enable ? OSD_OSDWIN0MD_ATN0E : 0,
- OSD_OSDWIN0MD);
- else if ((sd->vpbe_type == VPBE_VERSION_3) ||
- (sd->vpbe_type == VPBE_VERSION_2))
- osd_modify(sd, OSD_EXTMODE_ATNOSD0EN,
- enable ? OSD_EXTMODE_ATNOSD0EN : 0,
- OSD_EXTMODE);
- break;
- case OSDWIN_OSD1:
- osd_modify(sd, OSD_OSDWIN1MD_ATN1E,
- enable ? OSD_OSDWIN1MD_ATN1E : 0,
- OSD_OSDWIN1MD);
- if (sd->vpbe_type == VPBE_VERSION_1)
- osd_modify(sd, OSD_OSDWIN1MD_ATN1E,
- enable ? OSD_OSDWIN1MD_ATN1E : 0,
- OSD_OSDWIN1MD);
- else if ((sd->vpbe_type == VPBE_VERSION_3) ||
- (sd->vpbe_type == VPBE_VERSION_2))
- osd_modify(sd, OSD_EXTMODE_ATNOSD1EN,
- enable ? OSD_EXTMODE_ATNOSD1EN : 0,
- OSD_EXTMODE);
- break;
- }
-}
-
-static void _osd_set_blending_factor(struct osd_state *sd,
- enum osd_win_layer osdwin,
- enum osd_blending_factor blend)
-{
- switch (osdwin) {
- case OSDWIN_OSD0:
- osd_modify(sd, OSD_OSDWIN0MD_BLND0,
- blend << OSD_OSDWIN0MD_BLND0_SHIFT, OSD_OSDWIN0MD);
- break;
- case OSDWIN_OSD1:
- osd_modify(sd, OSD_OSDWIN1MD_BLND1,
- blend << OSD_OSDWIN1MD_BLND1_SHIFT, OSD_OSDWIN1MD);
- break;
- }
-}
-
-static void _osd_enable_rgb888_pixblend(struct osd_state *sd,
- enum osd_win_layer osdwin)
-{
-
- osd_modify(sd, OSD_MISCCTL_BLDSEL, 0, OSD_MISCCTL);
- switch (osdwin) {
- case OSDWIN_OSD0:
- osd_modify(sd, OSD_EXTMODE_OSD0BLDCHR,
- OSD_EXTMODE_OSD0BLDCHR, OSD_EXTMODE);
- break;
- case OSDWIN_OSD1:
- osd_modify(sd, OSD_EXTMODE_OSD1BLDCHR,
- OSD_EXTMODE_OSD1BLDCHR, OSD_EXTMODE);
- break;
- }
-}
-
-static void _osd_enable_color_key(struct osd_state *sd,
- enum osd_win_layer osdwin,
- unsigned colorkey,
- enum osd_pix_format pixfmt)
-{
- switch (pixfmt) {
- case PIXFMT_1BPP:
- case PIXFMT_2BPP:
- case PIXFMT_4BPP:
- case PIXFMT_8BPP:
- if (sd->vpbe_type == VPBE_VERSION_3) {
- switch (osdwin) {
- case OSDWIN_OSD0:
- osd_modify(sd, OSD_TRANSPBMPIDX_BMP0,
- colorkey <<
- OSD_TRANSPBMPIDX_BMP0_SHIFT,
- OSD_TRANSPBMPIDX);
- break;
- case OSDWIN_OSD1:
- osd_modify(sd, OSD_TRANSPBMPIDX_BMP1,
- colorkey <<
- OSD_TRANSPBMPIDX_BMP1_SHIFT,
- OSD_TRANSPBMPIDX);
- break;
- }
- }
- break;
- case PIXFMT_RGB565:
- if (sd->vpbe_type == VPBE_VERSION_1)
- osd_write(sd, colorkey & OSD_TRANSPVAL_RGBTRANS,
- OSD_TRANSPVAL);
- else if (sd->vpbe_type == VPBE_VERSION_3)
- osd_write(sd, colorkey & OSD_TRANSPVALL_RGBL,
- OSD_TRANSPVALL);
- break;
- case PIXFMT_YCBCRI:
- case PIXFMT_YCRCBI:
- if (sd->vpbe_type == VPBE_VERSION_3)
- osd_modify(sd, OSD_TRANSPVALU_Y, colorkey,
- OSD_TRANSPVALU);
- break;
- case PIXFMT_RGB888:
- if (sd->vpbe_type == VPBE_VERSION_3) {
- osd_write(sd, colorkey & OSD_TRANSPVALL_RGBL,
- OSD_TRANSPVALL);
- osd_modify(sd, OSD_TRANSPVALU_RGBU, colorkey >> 16,
- OSD_TRANSPVALU);
- }
- break;
- default:
- break;
- }
-
- switch (osdwin) {
- case OSDWIN_OSD0:
- osd_set(sd, OSD_OSDWIN0MD_TE0, OSD_OSDWIN0MD);
- break;
- case OSDWIN_OSD1:
- osd_set(sd, OSD_OSDWIN1MD_TE1, OSD_OSDWIN1MD);
- break;
- }
-}
-
-static void _osd_disable_color_key(struct osd_state *sd,
- enum osd_win_layer osdwin)
-{
- switch (osdwin) {
- case OSDWIN_OSD0:
- osd_clear(sd, OSD_OSDWIN0MD_TE0, OSD_OSDWIN0MD);
- break;
- case OSDWIN_OSD1:
- osd_clear(sd, OSD_OSDWIN1MD_TE1, OSD_OSDWIN1MD);
- break;
- }
-}
-
-static void _osd_set_osd_clut(struct osd_state *sd,
- enum osd_win_layer osdwin,
- enum osd_clut clut)
-{
- u32 winmd = 0;
-
- switch (osdwin) {
- case OSDWIN_OSD0:
- if (clut == RAM_CLUT)
- winmd |= OSD_OSDWIN0MD_CLUTS0;
- osd_modify(sd, OSD_OSDWIN0MD_CLUTS0, winmd, OSD_OSDWIN0MD);
- break;
- case OSDWIN_OSD1:
- if (clut == RAM_CLUT)
- winmd |= OSD_OSDWIN1MD_CLUTS1;
- osd_modify(sd, OSD_OSDWIN1MD_CLUTS1, winmd, OSD_OSDWIN1MD);
- break;
- }
-}
-
-static void _osd_set_zoom(struct osd_state *sd, enum osd_layer layer,
- enum osd_zoom_factor h_zoom,
- enum osd_zoom_factor v_zoom)
-{
- u32 winmd = 0;
-
- switch (layer) {
- case WIN_OSD0:
- winmd |= (h_zoom << OSD_OSDWIN0MD_OHZ0_SHIFT);
- winmd |= (v_zoom << OSD_OSDWIN0MD_OVZ0_SHIFT);
- osd_modify(sd, OSD_OSDWIN0MD_OHZ0 | OSD_OSDWIN0MD_OVZ0, winmd,
- OSD_OSDWIN0MD);
- break;
- case WIN_VID0:
- winmd |= (h_zoom << OSD_VIDWINMD_VHZ0_SHIFT);
- winmd |= (v_zoom << OSD_VIDWINMD_VVZ0_SHIFT);
- osd_modify(sd, OSD_VIDWINMD_VHZ0 | OSD_VIDWINMD_VVZ0, winmd,
- OSD_VIDWINMD);
- break;
- case WIN_OSD1:
- winmd |= (h_zoom << OSD_OSDWIN1MD_OHZ1_SHIFT);
- winmd |= (v_zoom << OSD_OSDWIN1MD_OVZ1_SHIFT);
- osd_modify(sd, OSD_OSDWIN1MD_OHZ1 | OSD_OSDWIN1MD_OVZ1, winmd,
- OSD_OSDWIN1MD);
- break;
- case WIN_VID1:
- winmd |= (h_zoom << OSD_VIDWINMD_VHZ1_SHIFT);
- winmd |= (v_zoom << OSD_VIDWINMD_VVZ1_SHIFT);
- osd_modify(sd, OSD_VIDWINMD_VHZ1 | OSD_VIDWINMD_VVZ1, winmd,
- OSD_VIDWINMD);
- break;
- }
-}
-
-static void _osd_disable_layer(struct osd_state *sd, enum osd_layer layer)
-{
- switch (layer) {
- case WIN_OSD0:
- osd_clear(sd, OSD_OSDWIN0MD_OACT0, OSD_OSDWIN0MD);
- break;
- case WIN_VID0:
- osd_clear(sd, OSD_VIDWINMD_ACT0, OSD_VIDWINMD);
- break;
- case WIN_OSD1:
- /* disable attribute mode as well as disabling the window */
- osd_clear(sd, OSD_OSDWIN1MD_OASW | OSD_OSDWIN1MD_OACT1,
- OSD_OSDWIN1MD);
- break;
- case WIN_VID1:
- osd_clear(sd, OSD_VIDWINMD_ACT1, OSD_VIDWINMD);
- break;
- }
-}
-
-static void osd_disable_layer(struct osd_state *sd, enum osd_layer layer)
-{
- struct osd_state *osd = sd;
- struct osd_window_state *win = &osd->win[layer];
- unsigned long flags;
-
- spin_lock_irqsave(&osd->lock, flags);
-
- if (!win->is_enabled) {
- spin_unlock_irqrestore(&osd->lock, flags);
- return;
- }
- win->is_enabled = 0;
-
- _osd_disable_layer(sd, layer);
-
- spin_unlock_irqrestore(&osd->lock, flags);
-}
-
-static void _osd_enable_attribute_mode(struct osd_state *sd)
-{
- /* enable attribute mode for OSD1 */
- osd_set(sd, OSD_OSDWIN1MD_OASW, OSD_OSDWIN1MD);
-}
-
-static void _osd_enable_layer(struct osd_state *sd, enum osd_layer layer)
-{
- switch (layer) {
- case WIN_OSD0:
- osd_set(sd, OSD_OSDWIN0MD_OACT0, OSD_OSDWIN0MD);
- break;
- case WIN_VID0:
- osd_set(sd, OSD_VIDWINMD_ACT0, OSD_VIDWINMD);
- break;
- case WIN_OSD1:
- /* enable OSD1 and disable attribute mode */
- osd_modify(sd, OSD_OSDWIN1MD_OASW | OSD_OSDWIN1MD_OACT1,
- OSD_OSDWIN1MD_OACT1, OSD_OSDWIN1MD);
- break;
- case WIN_VID1:
- osd_set(sd, OSD_VIDWINMD_ACT1, OSD_VIDWINMD);
- break;
- }
-}
-
-static int osd_enable_layer(struct osd_state *sd, enum osd_layer layer,
- int otherwin)
-{
- struct osd_state *osd = sd;
- struct osd_window_state *win = &osd->win[layer];
- struct osd_layer_config *cfg = &win->lconfig;
- unsigned long flags;
-
- spin_lock_irqsave(&osd->lock, flags);
-
- /*
- * use otherwin flag to know this is the other vid window
- * in YUV420 mode, if is, skip this check
- */
- if (!otherwin && (!win->is_allocated ||
- !win->fb_base_phys ||
- !cfg->line_length ||
- !cfg->xsize ||
- !cfg->ysize)) {
- spin_unlock_irqrestore(&osd->lock, flags);
- return -1;
- }
-
- if (win->is_enabled) {
- spin_unlock_irqrestore(&osd->lock, flags);
- return 0;
- }
- win->is_enabled = 1;
-
- if (cfg->pixfmt != PIXFMT_OSD_ATTR)
- _osd_enable_layer(sd, layer);
- else {
- _osd_enable_attribute_mode(sd);
- _osd_set_blink_attribute(sd, osd->is_blinking, osd->blink);
- }
-
- spin_unlock_irqrestore(&osd->lock, flags);
-
- return 0;
-}
-
-#define OSD_SRC_ADDR_HIGH4 0x7800000
-#define OSD_SRC_ADDR_HIGH7 0x7F0000
-#define OSD_SRCADD_OFSET_SFT 23
-#define OSD_SRCADD_ADD_SFT 16
-#define OSD_WINADL_MASK 0xFFFF
-#define OSD_WINOFST_MASK 0x1000
-#define VPBE_REG_BASE 0x80000000
-
-static void _osd_start_layer(struct osd_state *sd, enum osd_layer layer,
- unsigned long fb_base_phys,
- unsigned long cbcr_ofst)
-{
-
- if (sd->vpbe_type == VPBE_VERSION_1) {
- switch (layer) {
- case WIN_OSD0:
- osd_write(sd, fb_base_phys & ~0x1F, OSD_OSDWIN0ADR);
- break;
- case WIN_VID0:
- osd_write(sd, fb_base_phys & ~0x1F, OSD_VIDWIN0ADR);
- break;
- case WIN_OSD1:
- osd_write(sd, fb_base_phys & ~0x1F, OSD_OSDWIN1ADR);
- break;
- case WIN_VID1:
- osd_write(sd, fb_base_phys & ~0x1F, OSD_VIDWIN1ADR);
- break;
- }
- } else if (sd->vpbe_type == VPBE_VERSION_3) {
- unsigned long fb_offset_32 =
- (fb_base_phys - VPBE_REG_BASE) >> 5;
-
- switch (layer) {
- case WIN_OSD0:
- osd_modify(sd, OSD_OSDWINADH_O0AH,
- fb_offset_32 >> (OSD_SRCADD_ADD_SFT -
- OSD_OSDWINADH_O0AH_SHIFT),
- OSD_OSDWINADH);
- osd_write(sd, fb_offset_32 & OSD_OSDWIN0ADL_O0AL,
- OSD_OSDWIN0ADL);
- break;
- case WIN_VID0:
- osd_modify(sd, OSD_VIDWINADH_V0AH,
- fb_offset_32 >> (OSD_SRCADD_ADD_SFT -
- OSD_VIDWINADH_V0AH_SHIFT),
- OSD_VIDWINADH);
- osd_write(sd, fb_offset_32 & OSD_VIDWIN0ADL_V0AL,
- OSD_VIDWIN0ADL);
- break;
- case WIN_OSD1:
- osd_modify(sd, OSD_OSDWINADH_O1AH,
- fb_offset_32 >> (OSD_SRCADD_ADD_SFT -
- OSD_OSDWINADH_O1AH_SHIFT),
- OSD_OSDWINADH);
- osd_write(sd, fb_offset_32 & OSD_OSDWIN1ADL_O1AL,
- OSD_OSDWIN1ADL);
- break;
- case WIN_VID1:
- osd_modify(sd, OSD_VIDWINADH_V1AH,
- fb_offset_32 >> (OSD_SRCADD_ADD_SFT -
- OSD_VIDWINADH_V1AH_SHIFT),
- OSD_VIDWINADH);
- osd_write(sd, fb_offset_32 & OSD_VIDWIN1ADL_V1AL,
- OSD_VIDWIN1ADL);
- break;
- }
- } else if (sd->vpbe_type == VPBE_VERSION_2) {
- struct osd_window_state *win = &sd->win[layer];
- unsigned long fb_offset_32, cbcr_offset_32;
-
- fb_offset_32 = fb_base_phys - VPBE_REG_BASE;
- if (cbcr_ofst)
- cbcr_offset_32 = cbcr_ofst;
- else
- cbcr_offset_32 = win->lconfig.line_length *
- win->lconfig.ysize;
- cbcr_offset_32 += fb_offset_32;
- fb_offset_32 = fb_offset_32 >> 5;
- cbcr_offset_32 = cbcr_offset_32 >> 5;
- /*
- * DM365: start address is 27-bit long address b26 - b23 are
- * in offset register b12 - b9, and * bit 26 has to be '1'
- */
- if (win->lconfig.pixfmt == PIXFMT_NV12) {
- switch (layer) {
- case WIN_VID0:
- case WIN_VID1:
- /* Y is in VID0 */
- osd_modify(sd, OSD_VIDWIN0OFST_V0AH,
- ((fb_offset_32 & OSD_SRC_ADDR_HIGH4) >>
- (OSD_SRCADD_OFSET_SFT -
- OSD_WINOFST_AH_SHIFT)) |
- OSD_WINOFST_MASK, OSD_VIDWIN0OFST);
- osd_modify(sd, OSD_VIDWINADH_V0AH,
- (fb_offset_32 & OSD_SRC_ADDR_HIGH7) >>
- (OSD_SRCADD_ADD_SFT -
- OSD_VIDWINADH_V0AH_SHIFT),
- OSD_VIDWINADH);
- osd_write(sd, fb_offset_32 & OSD_WINADL_MASK,
- OSD_VIDWIN0ADL);
- /* CbCr is in VID1 */
- osd_modify(sd, OSD_VIDWIN1OFST_V1AH,
- ((cbcr_offset_32 &
- OSD_SRC_ADDR_HIGH4) >>
- (OSD_SRCADD_OFSET_SFT -
- OSD_WINOFST_AH_SHIFT)) |
- OSD_WINOFST_MASK, OSD_VIDWIN1OFST);
- osd_modify(sd, OSD_VIDWINADH_V1AH,
- (cbcr_offset_32 &
- OSD_SRC_ADDR_HIGH7) >>
- (OSD_SRCADD_ADD_SFT -
- OSD_VIDWINADH_V1AH_SHIFT),
- OSD_VIDWINADH);
- osd_write(sd, cbcr_offset_32 & OSD_WINADL_MASK,
- OSD_VIDWIN1ADL);
- break;
- default:
- break;
- }
- }
-
- switch (layer) {
- case WIN_OSD0:
- osd_modify(sd, OSD_OSDWIN0OFST_O0AH,
- ((fb_offset_32 & OSD_SRC_ADDR_HIGH4) >>
- (OSD_SRCADD_OFSET_SFT -
- OSD_WINOFST_AH_SHIFT)) | OSD_WINOFST_MASK,
- OSD_OSDWIN0OFST);
- osd_modify(sd, OSD_OSDWINADH_O0AH,
- (fb_offset_32 & OSD_SRC_ADDR_HIGH7) >>
- (OSD_SRCADD_ADD_SFT -
- OSD_OSDWINADH_O0AH_SHIFT), OSD_OSDWINADH);
- osd_write(sd, fb_offset_32 & OSD_WINADL_MASK,
- OSD_OSDWIN0ADL);
- break;
- case WIN_VID0:
- if (win->lconfig.pixfmt != PIXFMT_NV12) {
- osd_modify(sd, OSD_VIDWIN0OFST_V0AH,
- ((fb_offset_32 & OSD_SRC_ADDR_HIGH4) >>
- (OSD_SRCADD_OFSET_SFT -
- OSD_WINOFST_AH_SHIFT)) |
- OSD_WINOFST_MASK, OSD_VIDWIN0OFST);
- osd_modify(sd, OSD_VIDWINADH_V0AH,
- (fb_offset_32 & OSD_SRC_ADDR_HIGH7) >>
- (OSD_SRCADD_ADD_SFT -
- OSD_VIDWINADH_V0AH_SHIFT),
- OSD_VIDWINADH);
- osd_write(sd, fb_offset_32 & OSD_WINADL_MASK,
- OSD_VIDWIN0ADL);
- }
- break;
- case WIN_OSD1:
- osd_modify(sd, OSD_OSDWIN1OFST_O1AH,
- ((fb_offset_32 & OSD_SRC_ADDR_HIGH4) >>
- (OSD_SRCADD_OFSET_SFT -
- OSD_WINOFST_AH_SHIFT)) | OSD_WINOFST_MASK,
- OSD_OSDWIN1OFST);
- osd_modify(sd, OSD_OSDWINADH_O1AH,
- (fb_offset_32 & OSD_SRC_ADDR_HIGH7) >>
- (OSD_SRCADD_ADD_SFT -
- OSD_OSDWINADH_O1AH_SHIFT),
- OSD_OSDWINADH);
- osd_write(sd, fb_offset_32 & OSD_WINADL_MASK,
- OSD_OSDWIN1ADL);
- break;
- case WIN_VID1:
- if (win->lconfig.pixfmt != PIXFMT_NV12) {
- osd_modify(sd, OSD_VIDWIN1OFST_V1AH,
- ((fb_offset_32 & OSD_SRC_ADDR_HIGH4) >>
- (OSD_SRCADD_OFSET_SFT -
- OSD_WINOFST_AH_SHIFT)) |
- OSD_WINOFST_MASK, OSD_VIDWIN1OFST);
- osd_modify(sd, OSD_VIDWINADH_V1AH,
- (fb_offset_32 & OSD_SRC_ADDR_HIGH7) >>
- (OSD_SRCADD_ADD_SFT -
- OSD_VIDWINADH_V1AH_SHIFT),
- OSD_VIDWINADH);
- osd_write(sd, fb_offset_32 & OSD_WINADL_MASK,
- OSD_VIDWIN1ADL);
- }
- break;
- }
- }
-}
-
-static void osd_start_layer(struct osd_state *sd, enum osd_layer layer,
- unsigned long fb_base_phys,
- unsigned long cbcr_ofst)
-{
- struct osd_state *osd = sd;
- struct osd_window_state *win = &osd->win[layer];
- struct osd_layer_config *cfg = &win->lconfig;
- unsigned long flags;
-
- spin_lock_irqsave(&osd->lock, flags);
-
- win->fb_base_phys = fb_base_phys & ~0x1F;
- _osd_start_layer(sd, layer, fb_base_phys, cbcr_ofst);
-
- if (layer == WIN_VID0) {
- osd->pingpong =
- _osd_dm6446_vid0_pingpong(sd, osd->field_inversion,
- win->fb_base_phys,
- cfg);
- }
-
- spin_unlock_irqrestore(&osd->lock, flags);
-}
-
-static void osd_get_layer_config(struct osd_state *sd, enum osd_layer layer,
- struct osd_layer_config *lconfig)
-{
- struct osd_state *osd = sd;
- struct osd_window_state *win = &osd->win[layer];
- unsigned long flags;
-
- spin_lock_irqsave(&osd->lock, flags);
-
- *lconfig = win->lconfig;
-
- spin_unlock_irqrestore(&osd->lock, flags);
-}
-
-/**
- * try_layer_config() - Try a specific configuration for the layer
- * @sd: ptr to struct osd_state
- * @layer: layer to configure
- * @lconfig: layer configuration to try
- *
- * If the requested lconfig is completely rejected and the value of lconfig on
- * exit is the current lconfig, then try_layer_config() returns 1. Otherwise,
- * try_layer_config() returns 0. A return value of 0 does not necessarily mean
- * that the value of lconfig on exit is identical to the value of lconfig on
- * entry, but merely that it represents a change from the current lconfig.
- */
-static int try_layer_config(struct osd_state *sd, enum osd_layer layer,
- struct osd_layer_config *lconfig)
-{
- struct osd_state *osd = sd;
- struct osd_window_state *win = &osd->win[layer];
- int bad_config = 0;
-
- /* verify that the pixel format is compatible with the layer */
- switch (lconfig->pixfmt) {
- case PIXFMT_1BPP:
- case PIXFMT_2BPP:
- case PIXFMT_4BPP:
- case PIXFMT_8BPP:
- case PIXFMT_RGB565:
- if (osd->vpbe_type == VPBE_VERSION_1)
- bad_config = !is_vid_win(layer);
- break;
- case PIXFMT_YCBCRI:
- case PIXFMT_YCRCBI:
- bad_config = !is_vid_win(layer);
- break;
- case PIXFMT_RGB888:
- if (osd->vpbe_type == VPBE_VERSION_1)
- bad_config = !is_vid_win(layer);
- else if ((osd->vpbe_type == VPBE_VERSION_3) ||
- (osd->vpbe_type == VPBE_VERSION_2))
- bad_config = !is_osd_win(layer);
- break;
- case PIXFMT_NV12:
- if (osd->vpbe_type != VPBE_VERSION_2)
- bad_config = 1;
- else
- bad_config = is_osd_win(layer);
- break;
- case PIXFMT_OSD_ATTR:
- bad_config = (layer != WIN_OSD1);
- break;
- default:
- bad_config = 1;
- break;
- }
- if (bad_config) {
- /*
- * The requested pixel format is incompatible with the layer,
- * so keep the current layer configuration.
- */
- *lconfig = win->lconfig;
- return bad_config;
- }
-
- /* DM6446: */
- /* only one OSD window at a time can use RGB pixel formats */
- if ((osd->vpbe_type == VPBE_VERSION_1) &&
- is_osd_win(layer) && is_rgb_pixfmt(lconfig->pixfmt)) {
- enum osd_pix_format pixfmt;
-
- if (layer == WIN_OSD0)
- pixfmt = osd->win[WIN_OSD1].lconfig.pixfmt;
- else
- pixfmt = osd->win[WIN_OSD0].lconfig.pixfmt;
-
- if (is_rgb_pixfmt(pixfmt)) {
- /*
- * The other OSD window is already configured for an
- * RGB, so keep the current layer configuration.
- */
- *lconfig = win->lconfig;
- return 1;
- }
- }
-
- /* DM6446: only one video window at a time can use RGB888 */
- if ((osd->vpbe_type == VPBE_VERSION_1) && is_vid_win(layer) &&
- lconfig->pixfmt == PIXFMT_RGB888) {
- enum osd_pix_format pixfmt;
-
- if (layer == WIN_VID0)
- pixfmt = osd->win[WIN_VID1].lconfig.pixfmt;
- else
- pixfmt = osd->win[WIN_VID0].lconfig.pixfmt;
-
- if (pixfmt == PIXFMT_RGB888) {
- /*
- * The other video window is already configured for
- * RGB888, so keep the current layer configuration.
- */
- *lconfig = win->lconfig;
- return 1;
- }
- }
-
- /* window dimensions must be non-zero */
- if (!lconfig->line_length || !lconfig->xsize || !lconfig->ysize) {
- *lconfig = win->lconfig;
- return 1;
- }
-
- /* round line_length up to a multiple of 32 */
- lconfig->line_length = ((lconfig->line_length + 31) / 32) * 32;
- lconfig->line_length =
- min(lconfig->line_length, (unsigned)MAX_LINE_LENGTH);
- lconfig->xsize = min(lconfig->xsize, (unsigned)MAX_WIN_SIZE);
- lconfig->ysize = min(lconfig->ysize, (unsigned)MAX_WIN_SIZE);
- lconfig->xpos = min(lconfig->xpos, (unsigned)MAX_WIN_SIZE);
- lconfig->ypos = min(lconfig->ypos, (unsigned)MAX_WIN_SIZE);
- lconfig->interlaced = (lconfig->interlaced != 0);
- if (lconfig->interlaced) {
- /* ysize and ypos must be even for interlaced displays */
- lconfig->ysize &= ~1;
- lconfig->ypos &= ~1;
- }
-
- return 0;
-}
-
-static void _osd_disable_vid_rgb888(struct osd_state *sd)
-{
- /*
- * The DM6446 supports RGB888 pixel format in a single video window.
- * This routine disables RGB888 pixel format for both video windows.
- * The caller must ensure that neither video window is currently
- * configured for RGB888 pixel format.
- */
- if (sd->vpbe_type == VPBE_VERSION_1)
- osd_clear(sd, OSD_MISCCTL_RGBEN, OSD_MISCCTL);
-}
-
-static void _osd_enable_vid_rgb888(struct osd_state *sd,
- enum osd_layer layer)
-{
- /*
- * The DM6446 supports RGB888 pixel format in a single video window.
- * This routine enables RGB888 pixel format for the specified video
- * window. The caller must ensure that the other video window is not
- * currently configured for RGB888 pixel format, as this routine will
- * disable RGB888 pixel format for the other window.
- */
- if (sd->vpbe_type == VPBE_VERSION_1) {
- if (layer == WIN_VID0)
- osd_modify(sd, OSD_MISCCTL_RGBEN | OSD_MISCCTL_RGBWIN,
- OSD_MISCCTL_RGBEN, OSD_MISCCTL);
- else if (layer == WIN_VID1)
- osd_modify(sd, OSD_MISCCTL_RGBEN | OSD_MISCCTL_RGBWIN,
- OSD_MISCCTL_RGBEN | OSD_MISCCTL_RGBWIN,
- OSD_MISCCTL);
- }
-}
-
-static void _osd_set_cbcr_order(struct osd_state *sd,
- enum osd_pix_format pixfmt)
-{
- /*
- * The caller must ensure that all windows using YC pixfmt use the same
- * Cb/Cr order.
- */
- if (pixfmt == PIXFMT_YCBCRI)
- osd_clear(sd, OSD_MODE_CS, OSD_MODE);
- else if (pixfmt == PIXFMT_YCRCBI)
- osd_set(sd, OSD_MODE_CS, OSD_MODE);
-}
-
-static void _osd_set_layer_config(struct osd_state *sd, enum osd_layer layer,
- const struct osd_layer_config *lconfig)
-{
- u32 winmd = 0, winmd_mask = 0, bmw = 0;
-
- _osd_set_cbcr_order(sd, lconfig->pixfmt);
-
- switch (layer) {
- case WIN_OSD0:
- if (sd->vpbe_type == VPBE_VERSION_1) {
- winmd_mask |= OSD_OSDWIN0MD_RGB0E;
- if (lconfig->pixfmt == PIXFMT_RGB565)
- winmd |= OSD_OSDWIN0MD_RGB0E;
- } else if ((sd->vpbe_type == VPBE_VERSION_3) ||
- (sd->vpbe_type == VPBE_VERSION_2)) {
- winmd_mask |= OSD_OSDWIN0MD_BMP0MD;
- switch (lconfig->pixfmt) {
- case PIXFMT_RGB565:
- winmd |= (1 <<
- OSD_OSDWIN0MD_BMP0MD_SHIFT);
- break;
- case PIXFMT_RGB888:
- winmd |= (2 << OSD_OSDWIN0MD_BMP0MD_SHIFT);
- _osd_enable_rgb888_pixblend(sd, OSDWIN_OSD0);
- break;
- case PIXFMT_YCBCRI:
- case PIXFMT_YCRCBI:
- winmd |= (3 << OSD_OSDWIN0MD_BMP0MD_SHIFT);
- break;
- default:
- break;
- }
- }
-
- winmd_mask |= OSD_OSDWIN0MD_BMW0 | OSD_OSDWIN0MD_OFF0;
-
- switch (lconfig->pixfmt) {
- case PIXFMT_1BPP:
- bmw = 0;
- break;
- case PIXFMT_2BPP:
- bmw = 1;
- break;
- case PIXFMT_4BPP:
- bmw = 2;
- break;
- case PIXFMT_8BPP:
- bmw = 3;
- break;
- default:
- break;
- }
- winmd |= (bmw << OSD_OSDWIN0MD_BMW0_SHIFT);
-
- if (lconfig->interlaced)
- winmd |= OSD_OSDWIN0MD_OFF0;
-
- osd_modify(sd, winmd_mask, winmd, OSD_OSDWIN0MD);
- osd_write(sd, lconfig->line_length >> 5, OSD_OSDWIN0OFST);
- osd_write(sd, lconfig->xpos, OSD_OSDWIN0XP);
- osd_write(sd, lconfig->xsize, OSD_OSDWIN0XL);
- if (lconfig->interlaced) {
- osd_write(sd, lconfig->ypos >> 1, OSD_OSDWIN0YP);
- osd_write(sd, lconfig->ysize >> 1, OSD_OSDWIN0YL);
- } else {
- osd_write(sd, lconfig->ypos, OSD_OSDWIN0YP);
- osd_write(sd, lconfig->ysize, OSD_OSDWIN0YL);
- }
- break;
- case WIN_VID0:
- winmd_mask |= OSD_VIDWINMD_VFF0;
- if (lconfig->interlaced)
- winmd |= OSD_VIDWINMD_VFF0;
-
- osd_modify(sd, winmd_mask, winmd, OSD_VIDWINMD);
- osd_write(sd, lconfig->line_length >> 5, OSD_VIDWIN0OFST);
- osd_write(sd, lconfig->xpos, OSD_VIDWIN0XP);
- osd_write(sd, lconfig->xsize, OSD_VIDWIN0XL);
- /*
- * For YUV420P format the register contents are
- * duplicated in both VID registers
- */
- if ((sd->vpbe_type == VPBE_VERSION_2) &&
- (lconfig->pixfmt == PIXFMT_NV12)) {
- /* other window also */
- if (lconfig->interlaced) {
- winmd_mask |= OSD_VIDWINMD_VFF1;
- winmd |= OSD_VIDWINMD_VFF1;
- osd_modify(sd, winmd_mask, winmd,
- OSD_VIDWINMD);
- }
-
- osd_modify(sd, OSD_MISCCTL_S420D,
- OSD_MISCCTL_S420D, OSD_MISCCTL);
- osd_write(sd, lconfig->line_length >> 5,
- OSD_VIDWIN1OFST);
- osd_write(sd, lconfig->xpos, OSD_VIDWIN1XP);
- osd_write(sd, lconfig->xsize, OSD_VIDWIN1XL);
- /*
- * if NV21 pixfmt and line length not 32B
- * aligned (e.g. NTSC), Need to set window
- * X pixel size to be 32B aligned as well
- */
- if (lconfig->xsize % 32) {
- osd_write(sd,
- ((lconfig->xsize + 31) & ~31),
- OSD_VIDWIN1XL);
- osd_write(sd,
- ((lconfig->xsize + 31) & ~31),
- OSD_VIDWIN0XL);
- }
- } else if ((sd->vpbe_type == VPBE_VERSION_2) &&
- (lconfig->pixfmt != PIXFMT_NV12)) {
- osd_modify(sd, OSD_MISCCTL_S420D, ~OSD_MISCCTL_S420D,
- OSD_MISCCTL);
- }
-
- if (lconfig->interlaced) {
- osd_write(sd, lconfig->ypos >> 1, OSD_VIDWIN0YP);
- osd_write(sd, lconfig->ysize >> 1, OSD_VIDWIN0YL);
- if ((sd->vpbe_type == VPBE_VERSION_2) &&
- lconfig->pixfmt == PIXFMT_NV12) {
- osd_write(sd, lconfig->ypos >> 1,
- OSD_VIDWIN1YP);
- osd_write(sd, lconfig->ysize >> 1,
- OSD_VIDWIN1YL);
- }
- } else {
- osd_write(sd, lconfig->ypos, OSD_VIDWIN0YP);
- osd_write(sd, lconfig->ysize, OSD_VIDWIN0YL);
- if ((sd->vpbe_type == VPBE_VERSION_2) &&
- lconfig->pixfmt == PIXFMT_NV12) {
- osd_write(sd, lconfig->ypos, OSD_VIDWIN1YP);
- osd_write(sd, lconfig->ysize, OSD_VIDWIN1YL);
- }
- }
- break;
- case WIN_OSD1:
- /*
- * The caller must ensure that OSD1 is disabled prior to
- * switching from a normal mode to attribute mode or from
- * attribute mode to a normal mode.
- */
- if (lconfig->pixfmt == PIXFMT_OSD_ATTR) {
- if (sd->vpbe_type == VPBE_VERSION_1) {
- winmd_mask |= OSD_OSDWIN1MD_ATN1E |
- OSD_OSDWIN1MD_RGB1E | OSD_OSDWIN1MD_CLUTS1 |
- OSD_OSDWIN1MD_BLND1 | OSD_OSDWIN1MD_TE1;
- } else {
- winmd_mask |= OSD_OSDWIN1MD_BMP1MD |
- OSD_OSDWIN1MD_CLUTS1 | OSD_OSDWIN1MD_BLND1 |
- OSD_OSDWIN1MD_TE1;
- }
- } else {
- if (sd->vpbe_type == VPBE_VERSION_1) {
- winmd_mask |= OSD_OSDWIN1MD_RGB1E;
- if (lconfig->pixfmt == PIXFMT_RGB565)
- winmd |= OSD_OSDWIN1MD_RGB1E;
- } else if ((sd->vpbe_type == VPBE_VERSION_3)
- || (sd->vpbe_type == VPBE_VERSION_2)) {
- winmd_mask |= OSD_OSDWIN1MD_BMP1MD;
- switch (lconfig->pixfmt) {
- case PIXFMT_RGB565:
- winmd |=
- (1 << OSD_OSDWIN1MD_BMP1MD_SHIFT);
- break;
- case PIXFMT_RGB888:
- winmd |=
- (2 << OSD_OSDWIN1MD_BMP1MD_SHIFT);
- _osd_enable_rgb888_pixblend(sd,
- OSDWIN_OSD1);
- break;
- case PIXFMT_YCBCRI:
- case PIXFMT_YCRCBI:
- winmd |=
- (3 << OSD_OSDWIN1MD_BMP1MD_SHIFT);
- break;
- default:
- break;
- }
- }
-
- winmd_mask |= OSD_OSDWIN1MD_BMW1;
- switch (lconfig->pixfmt) {
- case PIXFMT_1BPP:
- bmw = 0;
- break;
- case PIXFMT_2BPP:
- bmw = 1;
- break;
- case PIXFMT_4BPP:
- bmw = 2;
- break;
- case PIXFMT_8BPP:
- bmw = 3;
- break;
- default:
- break;
- }
- winmd |= (bmw << OSD_OSDWIN1MD_BMW1_SHIFT);
- }
-
- winmd_mask |= OSD_OSDWIN1MD_OFF1;
- if (lconfig->interlaced)
- winmd |= OSD_OSDWIN1MD_OFF1;
-
- osd_modify(sd, winmd_mask, winmd, OSD_OSDWIN1MD);
- osd_write(sd, lconfig->line_length >> 5, OSD_OSDWIN1OFST);
- osd_write(sd, lconfig->xpos, OSD_OSDWIN1XP);
- osd_write(sd, lconfig->xsize, OSD_OSDWIN1XL);
- if (lconfig->interlaced) {
- osd_write(sd, lconfig->ypos >> 1, OSD_OSDWIN1YP);
- osd_write(sd, lconfig->ysize >> 1, OSD_OSDWIN1YL);
- } else {
- osd_write(sd, lconfig->ypos, OSD_OSDWIN1YP);
- osd_write(sd, lconfig->ysize, OSD_OSDWIN1YL);
- }
- break;
- case WIN_VID1:
- winmd_mask |= OSD_VIDWINMD_VFF1;
- if (lconfig->interlaced)
- winmd |= OSD_VIDWINMD_VFF1;
-
- osd_modify(sd, winmd_mask, winmd, OSD_VIDWINMD);
- osd_write(sd, lconfig->line_length >> 5, OSD_VIDWIN1OFST);
- osd_write(sd, lconfig->xpos, OSD_VIDWIN1XP);
- osd_write(sd, lconfig->xsize, OSD_VIDWIN1XL);
- /*
- * For YUV420P format the register contents are
- * duplicated in both VID registers
- */
- if (sd->vpbe_type == VPBE_VERSION_2) {
- if (lconfig->pixfmt == PIXFMT_NV12) {
- /* other window also */
- if (lconfig->interlaced) {
- winmd_mask |= OSD_VIDWINMD_VFF0;
- winmd |= OSD_VIDWINMD_VFF0;
- osd_modify(sd, winmd_mask, winmd,
- OSD_VIDWINMD);
- }
- osd_modify(sd, OSD_MISCCTL_S420D,
- OSD_MISCCTL_S420D, OSD_MISCCTL);
- osd_write(sd, lconfig->line_length >> 5,
- OSD_VIDWIN0OFST);
- osd_write(sd, lconfig->xpos, OSD_VIDWIN0XP);
- osd_write(sd, lconfig->xsize, OSD_VIDWIN0XL);
- } else {
- osd_modify(sd, OSD_MISCCTL_S420D,
- ~OSD_MISCCTL_S420D, OSD_MISCCTL);
- }
- }
-
- if (lconfig->interlaced) {
- osd_write(sd, lconfig->ypos >> 1, OSD_VIDWIN1YP);
- osd_write(sd, lconfig->ysize >> 1, OSD_VIDWIN1YL);
- if ((sd->vpbe_type == VPBE_VERSION_2) &&
- lconfig->pixfmt == PIXFMT_NV12) {
- osd_write(sd, lconfig->ypos >> 1,
- OSD_VIDWIN0YP);
- osd_write(sd, lconfig->ysize >> 1,
- OSD_VIDWIN0YL);
- }
- } else {
- osd_write(sd, lconfig->ypos, OSD_VIDWIN1YP);
- osd_write(sd, lconfig->ysize, OSD_VIDWIN1YL);
- if ((sd->vpbe_type == VPBE_VERSION_2) &&
- lconfig->pixfmt == PIXFMT_NV12) {
- osd_write(sd, lconfig->ypos, OSD_VIDWIN0YP);
- osd_write(sd, lconfig->ysize, OSD_VIDWIN0YL);
- }
- }
- break;
- }
-}
-
-static int osd_set_layer_config(struct osd_state *sd, enum osd_layer layer,
- struct osd_layer_config *lconfig)
-{
- struct osd_state *osd = sd;
- struct osd_window_state *win = &osd->win[layer];
- struct osd_layer_config *cfg = &win->lconfig;
- unsigned long flags;
- int reject_config;
-
- spin_lock_irqsave(&osd->lock, flags);
-
- reject_config = try_layer_config(sd, layer, lconfig);
- if (reject_config) {
- spin_unlock_irqrestore(&osd->lock, flags);
- return reject_config;
- }
-
- /* update the current Cb/Cr order */
- if (is_yc_pixfmt(lconfig->pixfmt))
- osd->yc_pixfmt = lconfig->pixfmt;
-
- /*
- * If we are switching OSD1 from normal mode to attribute mode or from
- * attribute mode to normal mode, then we must disable the window.
- */
- if (layer == WIN_OSD1) {
- if (((lconfig->pixfmt == PIXFMT_OSD_ATTR) &&
- (cfg->pixfmt != PIXFMT_OSD_ATTR)) ||
- ((lconfig->pixfmt != PIXFMT_OSD_ATTR) &&
- (cfg->pixfmt == PIXFMT_OSD_ATTR))) {
- win->is_enabled = 0;
- _osd_disable_layer(sd, layer);
- }
- }
-
- _osd_set_layer_config(sd, layer, lconfig);
-
- if (layer == WIN_OSD1) {
- struct osd_osdwin_state *osdwin_state =
- &osd->osdwin[OSDWIN_OSD1];
-
- if ((lconfig->pixfmt != PIXFMT_OSD_ATTR) &&
- (cfg->pixfmt == PIXFMT_OSD_ATTR)) {
- /*
- * We just switched OSD1 from attribute mode to normal
- * mode, so we must initialize the CLUT select, the
- * blend factor, transparency colorkey enable, and
- * attenuation enable (DM6446 only) bits in the
- * OSDWIN1MD register.
- */
- _osd_set_osd_clut(sd, OSDWIN_OSD1,
- osdwin_state->clut);
- _osd_set_blending_factor(sd, OSDWIN_OSD1,
- osdwin_state->blend);
- if (osdwin_state->colorkey_blending) {
- _osd_enable_color_key(sd, OSDWIN_OSD1,
- osdwin_state->
- colorkey,
- lconfig->pixfmt);
- } else
- _osd_disable_color_key(sd, OSDWIN_OSD1);
- _osd_set_rec601_attenuation(sd, OSDWIN_OSD1,
- osdwin_state->
- rec601_attenuation);
- } else if ((lconfig->pixfmt == PIXFMT_OSD_ATTR) &&
- (cfg->pixfmt != PIXFMT_OSD_ATTR)) {
- /*
- * We just switched OSD1 from normal mode to attribute
- * mode, so we must initialize the blink enable and
- * blink interval bits in the OSDATRMD register.
- */
- _osd_set_blink_attribute(sd, osd->is_blinking,
- osd->blink);
- }
- }
-
- /*
- * If we just switched to a 1-, 2-, or 4-bits-per-pixel bitmap format
- * then configure a default palette map.
- */
- if ((lconfig->pixfmt != cfg->pixfmt) &&
- ((lconfig->pixfmt == PIXFMT_1BPP) ||
- (lconfig->pixfmt == PIXFMT_2BPP) ||
- (lconfig->pixfmt == PIXFMT_4BPP))) {
- enum osd_win_layer osdwin =
- ((layer == WIN_OSD0) ? OSDWIN_OSD0 : OSDWIN_OSD1);
- struct osd_osdwin_state *osdwin_state =
- &osd->osdwin[osdwin];
- unsigned char clut_index;
- unsigned char clut_entries = 0;
-
- switch (lconfig->pixfmt) {
- case PIXFMT_1BPP:
- clut_entries = 2;
- break;
- case PIXFMT_2BPP:
- clut_entries = 4;
- break;
- case PIXFMT_4BPP:
- clut_entries = 16;
- break;
- default:
- break;
- }
- /*
- * The default palette map maps the pixel value to the clut
- * index, i.e. pixel value 0 maps to clut entry 0, pixel value
- * 1 maps to clut entry 1, etc.
- */
- for (clut_index = 0; clut_index < 16; clut_index++) {
- osdwin_state->palette_map[clut_index] = clut_index;
- if (clut_index < clut_entries) {
- _osd_set_palette_map(sd, osdwin, clut_index,
- clut_index,
- lconfig->pixfmt);
- }
- }
- }
-
- *cfg = *lconfig;
- /* DM6446: configure the RGB888 enable and window selection */
- if (osd->win[WIN_VID0].lconfig.pixfmt == PIXFMT_RGB888)
- _osd_enable_vid_rgb888(sd, WIN_VID0);
- else if (osd->win[WIN_VID1].lconfig.pixfmt == PIXFMT_RGB888)
- _osd_enable_vid_rgb888(sd, WIN_VID1);
- else
- _osd_disable_vid_rgb888(sd);
-
- if (layer == WIN_VID0) {
- osd->pingpong =
- _osd_dm6446_vid0_pingpong(sd, osd->field_inversion,
- win->fb_base_phys,
- cfg);
- }
-
- spin_unlock_irqrestore(&osd->lock, flags);
-
- return 0;
-}
-
-static void osd_init_layer(struct osd_state *sd, enum osd_layer layer)
-{
- struct osd_state *osd = sd;
- struct osd_window_state *win = &osd->win[layer];
- enum osd_win_layer osdwin;
- struct osd_osdwin_state *osdwin_state;
- struct osd_layer_config *cfg = &win->lconfig;
- unsigned long flags;
-
- spin_lock_irqsave(&osd->lock, flags);
-
- win->is_enabled = 0;
- _osd_disable_layer(sd, layer);
-
- win->h_zoom = ZOOM_X1;
- win->v_zoom = ZOOM_X1;
- _osd_set_zoom(sd, layer, win->h_zoom, win->v_zoom);
-
- win->fb_base_phys = 0;
- _osd_start_layer(sd, layer, win->fb_base_phys, 0);
-
- cfg->line_length = 0;
- cfg->xsize = 0;
- cfg->ysize = 0;
- cfg->xpos = 0;
- cfg->ypos = 0;
- cfg->interlaced = 0;
- switch (layer) {
- case WIN_OSD0:
- case WIN_OSD1:
- osdwin = (layer == WIN_OSD0) ? OSDWIN_OSD0 : OSDWIN_OSD1;
- osdwin_state = &osd->osdwin[osdwin];
- /*
- * Other code relies on the fact that OSD windows default to a
- * bitmap pixel format when they are deallocated, so don't
- * change this default pixel format.
- */
- cfg->pixfmt = PIXFMT_8BPP;
- _osd_set_layer_config(sd, layer, cfg);
- osdwin_state->clut = RAM_CLUT;
- _osd_set_osd_clut(sd, osdwin, osdwin_state->clut);
- osdwin_state->colorkey_blending = 0;
- _osd_disable_color_key(sd, osdwin);
- osdwin_state->blend = OSD_8_VID_0;
- _osd_set_blending_factor(sd, osdwin, osdwin_state->blend);
- osdwin_state->rec601_attenuation = 0;
- _osd_set_rec601_attenuation(sd, osdwin,
- osdwin_state->
- rec601_attenuation);
- if (osdwin == OSDWIN_OSD1) {
- osd->is_blinking = 0;
- osd->blink = BLINK_X1;
- }
- break;
- case WIN_VID0:
- case WIN_VID1:
- cfg->pixfmt = osd->yc_pixfmt;
- _osd_set_layer_config(sd, layer, cfg);
- break;
- }
-
- spin_unlock_irqrestore(&osd->lock, flags);
-}
-
-static void osd_release_layer(struct osd_state *sd, enum osd_layer layer)
-{
- struct osd_state *osd = sd;
- struct osd_window_state *win = &osd->win[layer];
- unsigned long flags;
-
- spin_lock_irqsave(&osd->lock, flags);
-
- if (!win->is_allocated) {
- spin_unlock_irqrestore(&osd->lock, flags);
- return;
- }
-
- spin_unlock_irqrestore(&osd->lock, flags);
- osd_init_layer(sd, layer);
- spin_lock_irqsave(&osd->lock, flags);
-
- win->is_allocated = 0;
-
- spin_unlock_irqrestore(&osd->lock, flags);
-}
-
-static int osd_request_layer(struct osd_state *sd, enum osd_layer layer)
-{
- struct osd_state *osd = sd;
- struct osd_window_state *win = &osd->win[layer];
- unsigned long flags;
-
- spin_lock_irqsave(&osd->lock, flags);
-
- if (win->is_allocated) {
- spin_unlock_irqrestore(&osd->lock, flags);
- return -1;
- }
- win->is_allocated = 1;
-
- spin_unlock_irqrestore(&osd->lock, flags);
-
- return 0;
-}
-
-static void _osd_init(struct osd_state *sd)
-{
- osd_write(sd, 0, OSD_MODE);
- osd_write(sd, 0, OSD_VIDWINMD);
- osd_write(sd, 0, OSD_OSDWIN0MD);
- osd_write(sd, 0, OSD_OSDWIN1MD);
- osd_write(sd, 0, OSD_RECTCUR);
- osd_write(sd, 0, OSD_MISCCTL);
- if (sd->vpbe_type == VPBE_VERSION_3) {
- osd_write(sd, 0, OSD_VBNDRY);
- osd_write(sd, 0, OSD_EXTMODE);
- osd_write(sd, OSD_MISCCTL_DMANG, OSD_MISCCTL);
- }
-}
-
-static void osd_set_left_margin(struct osd_state *sd, u32 val)
-{
- osd_write(sd, val, OSD_BASEPX);
-}
-
-static void osd_set_top_margin(struct osd_state *sd, u32 val)
-{
- osd_write(sd, val, OSD_BASEPY);
-}
-
-static int osd_initialize(struct osd_state *osd)
-{
- if (osd == NULL)
- return -ENODEV;
- _osd_init(osd);
-
- /* set default Cb/Cr order */
- osd->yc_pixfmt = PIXFMT_YCBCRI;
-
- if (osd->vpbe_type == VPBE_VERSION_3) {
- /*
- * ROM CLUT1 on the DM355 is similar (identical?) to ROM CLUT0
- * on the DM6446, so make ROM_CLUT1 the default on the DM355.
- */
- osd->rom_clut = ROM_CLUT1;
- }
-
- _osd_set_field_inversion(osd, osd->field_inversion);
- _osd_set_rom_clut(osd, osd->rom_clut);
-
- osd_init_layer(osd, WIN_OSD0);
- osd_init_layer(osd, WIN_VID0);
- osd_init_layer(osd, WIN_OSD1);
- osd_init_layer(osd, WIN_VID1);
-
- return 0;
-}
-
-static const struct vpbe_osd_ops osd_ops = {
- .initialize = osd_initialize,
- .request_layer = osd_request_layer,
- .release_layer = osd_release_layer,
- .enable_layer = osd_enable_layer,
- .disable_layer = osd_disable_layer,
- .set_layer_config = osd_set_layer_config,
- .get_layer_config = osd_get_layer_config,
- .start_layer = osd_start_layer,
- .set_left_margin = osd_set_left_margin,
- .set_top_margin = osd_set_top_margin,
-};
-
-static int osd_probe(struct platform_device *pdev)
-{
- const struct platform_device_id *pdev_id;
- struct osd_state *osd;
- struct resource *res;
-
- pdev_id = platform_get_device_id(pdev);
- if (!pdev_id)
- return -EINVAL;
-
- osd = devm_kzalloc(&pdev->dev, sizeof(struct osd_state), GFP_KERNEL);
- if (osd == NULL)
- return -ENOMEM;
-
-
- osd->dev = &pdev->dev;
- osd->vpbe_type = pdev_id->driver_data;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- osd->osd_base = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(osd->osd_base))
- return PTR_ERR(osd->osd_base);
-
- osd->osd_base_phys = res->start;
- osd->osd_size = resource_size(res);
- spin_lock_init(&osd->lock);
- osd->ops = osd_ops;
- platform_set_drvdata(pdev, osd);
- dev_notice(osd->dev, "OSD sub device probe success\n");
-
- return 0;
-}
-
-static int osd_remove(struct platform_device *pdev)
-{
- return 0;
-}
-
-static struct platform_driver osd_driver = {
- .probe = osd_probe,
- .remove = osd_remove,
- .driver = {
- .name = MODULE_NAME,
- },
- .id_table = vpbe_osd_devtype
-};
-
-module_platform_driver(osd_driver);
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("DaVinci OSD Manager Driver");
-MODULE_AUTHOR("Texas Instruments");
diff --git a/drivers/media/platform/ti/davinci/vpbe_osd_regs.h b/drivers/media/platform/ti/davinci/vpbe_osd_regs.h
deleted file mode 100644
index cecd5991d4c5..000000000000
--- a/drivers/media/platform/ti/davinci/vpbe_osd_regs.h
+++ /dev/null
@@ -1,352 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-/*
- * Copyright (C) 2006-2010 Texas Instruments Inc
- */
-#ifndef _VPBE_OSD_REGS_H
-#define _VPBE_OSD_REGS_H
-
-/* VPBE Global Registers */
-#define VPBE_PID 0x0
-#define VPBE_PCR 0x4
-
-/* VPSS CLock Registers */
-#define VPSSCLK_PID 0x00
-#define VPSSCLK_CLKCTRL 0x04
-
-/* VPSS Buffer Logic Registers */
-#define VPSSBL_PID 0x00
-#define VPSSBL_PCR 0x04
-#define VPSSBL_BCR 0x08
-#define VPSSBL_INTSTAT 0x0C
-#define VPSSBL_INTSEL 0x10
-#define VPSSBL_EVTSEL 0x14
-#define VPSSBL_MEMCTRL 0x18
-#define VPSSBL_CCDCMUX 0x1C
-
-/* DM365 ISP5 system configuration */
-#define ISP5_PID 0x0
-#define ISP5_PCCR 0x4
-#define ISP5_BCR 0x8
-#define ISP5_INTSTAT 0xC
-#define ISP5_INTSEL1 0x10
-#define ISP5_INTSEL2 0x14
-#define ISP5_INTSEL3 0x18
-#define ISP5_EVTSEL 0x1c
-#define ISP5_CCDCMUX 0x20
-
-/* VPBE On-Screen Display Subsystem Registers (OSD) */
-#define OSD_MODE 0x00
-#define OSD_VIDWINMD 0x04
-#define OSD_OSDWIN0MD 0x08
-#define OSD_OSDWIN1MD 0x0C
-#define OSD_OSDATRMD 0x0C
-#define OSD_RECTCUR 0x10
-#define OSD_VIDWIN0OFST 0x18
-#define OSD_VIDWIN1OFST 0x1C
-#define OSD_OSDWIN0OFST 0x20
-#define OSD_OSDWIN1OFST 0x24
-#define OSD_VIDWINADH 0x28
-#define OSD_VIDWIN0ADL 0x2C
-#define OSD_VIDWIN0ADR 0x2C
-#define OSD_VIDWIN1ADL 0x30
-#define OSD_VIDWIN1ADR 0x30
-#define OSD_OSDWINADH 0x34
-#define OSD_OSDWIN0ADL 0x38
-#define OSD_OSDWIN0ADR 0x38
-#define OSD_OSDWIN1ADL 0x3C
-#define OSD_OSDWIN1ADR 0x3C
-#define OSD_BASEPX 0x40
-#define OSD_BASEPY 0x44
-#define OSD_VIDWIN0XP 0x48
-#define OSD_VIDWIN0YP 0x4C
-#define OSD_VIDWIN0XL 0x50
-#define OSD_VIDWIN0YL 0x54
-#define OSD_VIDWIN1XP 0x58
-#define OSD_VIDWIN1YP 0x5C
-#define OSD_VIDWIN1XL 0x60
-#define OSD_VIDWIN1YL 0x64
-#define OSD_OSDWIN0XP 0x68
-#define OSD_OSDWIN0YP 0x6C
-#define OSD_OSDWIN0XL 0x70
-#define OSD_OSDWIN0YL 0x74
-#define OSD_OSDWIN1XP 0x78
-#define OSD_OSDWIN1YP 0x7C
-#define OSD_OSDWIN1XL 0x80
-#define OSD_OSDWIN1YL 0x84
-#define OSD_CURXP 0x88
-#define OSD_CURYP 0x8C
-#define OSD_CURXL 0x90
-#define OSD_CURYL 0x94
-#define OSD_W0BMP01 0xA0
-#define OSD_W0BMP23 0xA4
-#define OSD_W0BMP45 0xA8
-#define OSD_W0BMP67 0xAC
-#define OSD_W0BMP89 0xB0
-#define OSD_W0BMPAB 0xB4
-#define OSD_W0BMPCD 0xB8
-#define OSD_W0BMPEF 0xBC
-#define OSD_W1BMP01 0xC0
-#define OSD_W1BMP23 0xC4
-#define OSD_W1BMP45 0xC8
-#define OSD_W1BMP67 0xCC
-#define OSD_W1BMP89 0xD0
-#define OSD_W1BMPAB 0xD4
-#define OSD_W1BMPCD 0xD8
-#define OSD_W1BMPEF 0xDC
-#define OSD_VBNDRY 0xE0
-#define OSD_EXTMODE 0xE4
-#define OSD_MISCCTL 0xE8
-#define OSD_CLUTRAMYCB 0xEC
-#define OSD_CLUTRAMCR 0xF0
-#define OSD_TRANSPVAL 0xF4
-#define OSD_TRANSPVALL 0xF4
-#define OSD_TRANSPVALU 0xF8
-#define OSD_TRANSPBMPIDX 0xFC
-#define OSD_PPVWIN0ADR 0xFC
-
-/* bit definitions */
-#define VPBE_PCR_VENC_DIV (1 << 1)
-#define VPBE_PCR_CLK_OFF (1 << 0)
-
-#define VPSSBL_INTSTAT_HSSIINT (1 << 14)
-#define VPSSBL_INTSTAT_CFALDINT (1 << 13)
-#define VPSSBL_INTSTAT_IPIPE_INT5 (1 << 12)
-#define VPSSBL_INTSTAT_IPIPE_INT4 (1 << 11)
-#define VPSSBL_INTSTAT_IPIPE_INT3 (1 << 10)
-#define VPSSBL_INTSTAT_IPIPE_INT2 (1 << 9)
-#define VPSSBL_INTSTAT_IPIPE_INT1 (1 << 8)
-#define VPSSBL_INTSTAT_IPIPE_INT0 (1 << 7)
-#define VPSSBL_INTSTAT_IPIPEIFINT (1 << 6)
-#define VPSSBL_INTSTAT_OSDINT (1 << 5)
-#define VPSSBL_INTSTAT_VENCINT (1 << 4)
-#define VPSSBL_INTSTAT_H3AINT (1 << 3)
-#define VPSSBL_INTSTAT_CCDC_VDINT2 (1 << 2)
-#define VPSSBL_INTSTAT_CCDC_VDINT1 (1 << 1)
-#define VPSSBL_INTSTAT_CCDC_VDINT0 (1 << 0)
-
-/* DM365 ISP5 bit definitions */
-#define ISP5_INTSTAT_VENCINT (1 << 21)
-#define ISP5_INTSTAT_OSDINT (1 << 20)
-
-/* VMOD TVTYP options for HDMD=0 */
-#define SDTV_NTSC 0
-#define SDTV_PAL 1
-/* VMOD TVTYP options for HDMD=1 */
-#define HDTV_525P 0
-#define HDTV_625P 1
-#define HDTV_1080I 2
-#define HDTV_720P 3
-
-#define OSD_MODE_CS (1 << 15)
-#define OSD_MODE_OVRSZ (1 << 14)
-#define OSD_MODE_OHRSZ (1 << 13)
-#define OSD_MODE_EF (1 << 12)
-#define OSD_MODE_VVRSZ (1 << 11)
-#define OSD_MODE_VHRSZ (1 << 10)
-#define OSD_MODE_FSINV (1 << 9)
-#define OSD_MODE_BCLUT (1 << 8)
-#define OSD_MODE_CABG_SHIFT 0
-#define OSD_MODE_CABG (0xff << 0)
-
-#define OSD_VIDWINMD_VFINV (1 << 15)
-#define OSD_VIDWINMD_V1EFC (1 << 14)
-#define OSD_VIDWINMD_VHZ1_SHIFT 12
-#define OSD_VIDWINMD_VHZ1 (3 << 12)
-#define OSD_VIDWINMD_VVZ1_SHIFT 10
-#define OSD_VIDWINMD_VVZ1 (3 << 10)
-#define OSD_VIDWINMD_VFF1 (1 << 9)
-#define OSD_VIDWINMD_ACT1 (1 << 8)
-#define OSD_VIDWINMD_V0EFC (1 << 6)
-#define OSD_VIDWINMD_VHZ0_SHIFT 4
-#define OSD_VIDWINMD_VHZ0 (3 << 4)
-#define OSD_VIDWINMD_VVZ0_SHIFT 2
-#define OSD_VIDWINMD_VVZ0 (3 << 2)
-#define OSD_VIDWINMD_VFF0 (1 << 1)
-#define OSD_VIDWINMD_ACT0 (1 << 0)
-
-#define OSD_OSDWIN0MD_ATN0E (1 << 14)
-#define OSD_OSDWIN0MD_RGB0E (1 << 13)
-#define OSD_OSDWIN0MD_BMP0MD_SHIFT 13
-#define OSD_OSDWIN0MD_BMP0MD (3 << 13)
-#define OSD_OSDWIN0MD_CLUTS0 (1 << 12)
-#define OSD_OSDWIN0MD_OHZ0_SHIFT 10
-#define OSD_OSDWIN0MD_OHZ0 (3 << 10)
-#define OSD_OSDWIN0MD_OVZ0_SHIFT 8
-#define OSD_OSDWIN0MD_OVZ0 (3 << 8)
-#define OSD_OSDWIN0MD_BMW0_SHIFT 6
-#define OSD_OSDWIN0MD_BMW0 (3 << 6)
-#define OSD_OSDWIN0MD_BLND0_SHIFT 3
-#define OSD_OSDWIN0MD_BLND0 (7 << 3)
-#define OSD_OSDWIN0MD_TE0 (1 << 2)
-#define OSD_OSDWIN0MD_OFF0 (1 << 1)
-#define OSD_OSDWIN0MD_OACT0 (1 << 0)
-
-#define OSD_OSDWIN1MD_OASW (1 << 15)
-#define OSD_OSDWIN1MD_ATN1E (1 << 14)
-#define OSD_OSDWIN1MD_RGB1E (1 << 13)
-#define OSD_OSDWIN1MD_BMP1MD_SHIFT 13
-#define OSD_OSDWIN1MD_BMP1MD (3 << 13)
-#define OSD_OSDWIN1MD_CLUTS1 (1 << 12)
-#define OSD_OSDWIN1MD_OHZ1_SHIFT 10
-#define OSD_OSDWIN1MD_OHZ1 (3 << 10)
-#define OSD_OSDWIN1MD_OVZ1_SHIFT 8
-#define OSD_OSDWIN1MD_OVZ1 (3 << 8)
-#define OSD_OSDWIN1MD_BMW1_SHIFT 6
-#define OSD_OSDWIN1MD_BMW1 (3 << 6)
-#define OSD_OSDWIN1MD_BLND1_SHIFT 3
-#define OSD_OSDWIN1MD_BLND1 (7 << 3)
-#define OSD_OSDWIN1MD_TE1 (1 << 2)
-#define OSD_OSDWIN1MD_OFF1 (1 << 1)
-#define OSD_OSDWIN1MD_OACT1 (1 << 0)
-
-#define OSD_OSDATRMD_OASW (1 << 15)
-#define OSD_OSDATRMD_OHZA_SHIFT 10
-#define OSD_OSDATRMD_OHZA (3 << 10)
-#define OSD_OSDATRMD_OVZA_SHIFT 8
-#define OSD_OSDATRMD_OVZA (3 << 8)
-#define OSD_OSDATRMD_BLNKINT_SHIFT 6
-#define OSD_OSDATRMD_BLNKINT (3 << 6)
-#define OSD_OSDATRMD_OFFA (1 << 1)
-#define OSD_OSDATRMD_BLNK (1 << 0)
-
-#define OSD_RECTCUR_RCAD_SHIFT 8
-#define OSD_RECTCUR_RCAD (0xff << 8)
-#define OSD_RECTCUR_CLUTSR (1 << 7)
-#define OSD_RECTCUR_RCHW_SHIFT 4
-#define OSD_RECTCUR_RCHW (7 << 4)
-#define OSD_RECTCUR_RCVW_SHIFT 1
-#define OSD_RECTCUR_RCVW (7 << 1)
-#define OSD_RECTCUR_RCACT (1 << 0)
-
-#define OSD_VIDWIN0OFST_V0LO (0x1ff << 0)
-
-#define OSD_VIDWIN1OFST_V1LO (0x1ff << 0)
-
-#define OSD_OSDWIN0OFST_O0LO (0x1ff << 0)
-
-#define OSD_OSDWIN1OFST_O1LO (0x1ff << 0)
-
-#define OSD_WINOFST_AH_SHIFT 9
-
-#define OSD_VIDWIN0OFST_V0AH (0xf << 9)
-#define OSD_VIDWIN1OFST_V1AH (0xf << 9)
-#define OSD_OSDWIN0OFST_O0AH (0xf << 9)
-#define OSD_OSDWIN1OFST_O1AH (0xf << 9)
-
-#define OSD_VIDWINADH_V1AH_SHIFT 8
-#define OSD_VIDWINADH_V1AH (0x7f << 8)
-#define OSD_VIDWINADH_V0AH_SHIFT 0
-#define OSD_VIDWINADH_V0AH (0x7f << 0)
-
-#define OSD_VIDWIN0ADL_V0AL (0xffff << 0)
-
-#define OSD_VIDWIN1ADL_V1AL (0xffff << 0)
-
-#define OSD_OSDWINADH_O1AH_SHIFT 8
-#define OSD_OSDWINADH_O1AH (0x7f << 8)
-#define OSD_OSDWINADH_O0AH_SHIFT 0
-#define OSD_OSDWINADH_O0AH (0x7f << 0)
-
-#define OSD_OSDWIN0ADL_O0AL (0xffff << 0)
-
-#define OSD_OSDWIN1ADL_O1AL (0xffff << 0)
-
-#define OSD_BASEPX_BPX (0x3ff << 0)
-
-#define OSD_BASEPY_BPY (0x1ff << 0)
-
-#define OSD_VIDWIN0XP_V0X (0x7ff << 0)
-
-#define OSD_VIDWIN0YP_V0Y (0x7ff << 0)
-
-#define OSD_VIDWIN0XL_V0W (0x7ff << 0)
-
-#define OSD_VIDWIN0YL_V0H (0x7ff << 0)
-
-#define OSD_VIDWIN1XP_V1X (0x7ff << 0)
-
-#define OSD_VIDWIN1YP_V1Y (0x7ff << 0)
-
-#define OSD_VIDWIN1XL_V1W (0x7ff << 0)
-
-#define OSD_VIDWIN1YL_V1H (0x7ff << 0)
-
-#define OSD_OSDWIN0XP_W0X (0x7ff << 0)
-
-#define OSD_OSDWIN0YP_W0Y (0x7ff << 0)
-
-#define OSD_OSDWIN0XL_W0W (0x7ff << 0)
-
-#define OSD_OSDWIN0YL_W0H (0x7ff << 0)
-
-#define OSD_OSDWIN1XP_W1X (0x7ff << 0)
-
-#define OSD_OSDWIN1YP_W1Y (0x7ff << 0)
-
-#define OSD_OSDWIN1XL_W1W (0x7ff << 0)
-
-#define OSD_OSDWIN1YL_W1H (0x7ff << 0)
-
-#define OSD_CURXP_RCSX (0x7ff << 0)
-
-#define OSD_CURYP_RCSY (0x7ff << 0)
-
-#define OSD_CURXL_RCSW (0x7ff << 0)
-
-#define OSD_CURYL_RCSH (0x7ff << 0)
-
-#define OSD_EXTMODE_EXPMDSEL (1 << 15)
-#define OSD_EXTMODE_SCRNHEXP_SHIFT 13
-#define OSD_EXTMODE_SCRNHEXP (3 << 13)
-#define OSD_EXTMODE_SCRNVEXP (1 << 12)
-#define OSD_EXTMODE_OSD1BLDCHR (1 << 11)
-#define OSD_EXTMODE_OSD0BLDCHR (1 << 10)
-#define OSD_EXTMODE_ATNOSD1EN (1 << 9)
-#define OSD_EXTMODE_ATNOSD0EN (1 << 8)
-#define OSD_EXTMODE_OSDHRSZ15 (1 << 7)
-#define OSD_EXTMODE_VIDHRSZ15 (1 << 6)
-#define OSD_EXTMODE_ZMFILV1HEN (1 << 5)
-#define OSD_EXTMODE_ZMFILV1VEN (1 << 4)
-#define OSD_EXTMODE_ZMFILV0HEN (1 << 3)
-#define OSD_EXTMODE_ZMFILV0VEN (1 << 2)
-#define OSD_EXTMODE_EXPFILHEN (1 << 1)
-#define OSD_EXTMODE_EXPFILVEN (1 << 0)
-
-#define OSD_MISCCTL_BLDSEL (1 << 15)
-#define OSD_MISCCTL_S420D (1 << 14)
-#define OSD_MISCCTL_BMAPT (1 << 13)
-#define OSD_MISCCTL_DM365M (1 << 12)
-#define OSD_MISCCTL_RGBEN (1 << 7)
-#define OSD_MISCCTL_RGBWIN (1 << 6)
-#define OSD_MISCCTL_DMANG (1 << 6)
-#define OSD_MISCCTL_TMON (1 << 5)
-#define OSD_MISCCTL_RSEL (1 << 4)
-#define OSD_MISCCTL_CPBSY (1 << 3)
-#define OSD_MISCCTL_PPSW (1 << 2)
-#define OSD_MISCCTL_PPRV (1 << 1)
-
-#define OSD_CLUTRAMYCB_Y_SHIFT 8
-#define OSD_CLUTRAMYCB_Y (0xff << 8)
-#define OSD_CLUTRAMYCB_CB_SHIFT 0
-#define OSD_CLUTRAMYCB_CB (0xff << 0)
-
-#define OSD_CLUTRAMCR_CR_SHIFT 8
-#define OSD_CLUTRAMCR_CR (0xff << 8)
-#define OSD_CLUTRAMCR_CADDR_SHIFT 0
-#define OSD_CLUTRAMCR_CADDR (0xff << 0)
-
-#define OSD_TRANSPVAL_RGBTRANS (0xffff << 0)
-
-#define OSD_TRANSPVALL_RGBL (0xffff << 0)
-
-#define OSD_TRANSPVALU_Y_SHIFT 8
-#define OSD_TRANSPVALU_Y (0xff << 8)
-#define OSD_TRANSPVALU_RGBU_SHIFT 0
-#define OSD_TRANSPVALU_RGBU (0xff << 0)
-
-#define OSD_TRANSPBMPIDX_BMP1_SHIFT 8
-#define OSD_TRANSPBMPIDX_BMP1 (0xff << 8)
-#define OSD_TRANSPBMPIDX_BMP0_SHIFT 0
-#define OSD_TRANSPBMPIDX_BMP0 0xff
-
-#endif /* _DAVINCI_VPBE_H_ */
diff --git a/drivers/media/platform/ti/davinci/vpbe_venc.c b/drivers/media/platform/ti/davinci/vpbe_venc.c
deleted file mode 100644
index 4c8e31de12b1..000000000000
--- a/drivers/media/platform/ti/davinci/vpbe_venc.c
+++ /dev/null
@@ -1,676 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * Copyright (C) 2010 Texas Instruments Inc
- */
-#include <linux/module.h>
-#include <linux/mod_devicetable.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/ctype.h>
-#include <linux/delay.h>
-#include <linux/device.h>
-#include <linux/interrupt.h>
-#include <linux/platform_device.h>
-#include <linux/videodev2.h>
-#include <linux/slab.h>
-
-#include <linux/platform_data/i2c-davinci.h>
-
-#include <linux/io.h>
-
-#include <media/davinci/vpbe_types.h>
-#include <media/davinci/vpbe_venc.h>
-#include <media/davinci/vpss.h>
-#include <media/v4l2-device.h>
-
-#include "vpbe_venc_regs.h"
-
-#define MODULE_NAME "davinci-vpbe-venc"
-
-static const struct platform_device_id vpbe_venc_devtype[] = {
- {
- .name = DM644X_VPBE_VENC_SUBDEV_NAME,
- .driver_data = VPBE_VERSION_1,
- }, {
- .name = DM365_VPBE_VENC_SUBDEV_NAME,
- .driver_data = VPBE_VERSION_2,
- }, {
- .name = DM355_VPBE_VENC_SUBDEV_NAME,
- .driver_data = VPBE_VERSION_3,
- },
- {
- /* sentinel */
- }
-};
-
-MODULE_DEVICE_TABLE(platform, vpbe_venc_devtype);
-
-static int debug = 2;
-module_param(debug, int, 0644);
-MODULE_PARM_DESC(debug, "Debug level 0-2");
-
-struct venc_state {
- struct v4l2_subdev sd;
- struct venc_callback *callback;
- struct venc_platform_data *pdata;
- struct device *pdev;
- u32 output;
- v4l2_std_id std;
- spinlock_t lock;
- void __iomem *venc_base;
- void __iomem *vdaccfg_reg;
- enum vpbe_version venc_type;
-};
-
-static inline struct venc_state *to_state(struct v4l2_subdev *sd)
-{
- return container_of(sd, struct venc_state, sd);
-}
-
-static inline u32 venc_read(struct v4l2_subdev *sd, u32 offset)
-{
- struct venc_state *venc = to_state(sd);
-
- return readl(venc->venc_base + offset);
-}
-
-static inline u32 venc_write(struct v4l2_subdev *sd, u32 offset, u32 val)
-{
- struct venc_state *venc = to_state(sd);
-
- writel(val, (venc->venc_base + offset));
-
- return val;
-}
-
-static inline u32 venc_modify(struct v4l2_subdev *sd, u32 offset,
- u32 val, u32 mask)
-{
- u32 new_val = (venc_read(sd, offset) & ~mask) | (val & mask);
-
- venc_write(sd, offset, new_val);
-
- return new_val;
-}
-
-static inline u32 vdaccfg_write(struct v4l2_subdev *sd, u32 val)
-{
- struct venc_state *venc = to_state(sd);
-
- writel(val, venc->vdaccfg_reg);
-
- val = readl(venc->vdaccfg_reg);
-
- return val;
-}
-
-#define VDAC_COMPONENT 0x543
-#define VDAC_S_VIDEO 0x210
-/* This function sets the dac of the VPBE for various outputs
- */
-static int venc_set_dac(struct v4l2_subdev *sd, u32 out_index)
-{
- switch (out_index) {
- case 0:
- v4l2_dbg(debug, 1, sd, "Setting output to Composite\n");
- venc_write(sd, VENC_DACSEL, 0);
- break;
- case 1:
- v4l2_dbg(debug, 1, sd, "Setting output to Component\n");
- venc_write(sd, VENC_DACSEL, VDAC_COMPONENT);
- break;
- case 2:
- v4l2_dbg(debug, 1, sd, "Setting output to S-video\n");
- venc_write(sd, VENC_DACSEL, VDAC_S_VIDEO);
- break;
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
-static void venc_enabledigitaloutput(struct v4l2_subdev *sd, int benable)
-{
- struct venc_state *venc = to_state(sd);
-
- v4l2_dbg(debug, 2, sd, "venc_enabledigitaloutput\n");
-
- if (benable) {
- venc_write(sd, VENC_VMOD, 0);
- venc_write(sd, VENC_CVBS, 0);
- venc_write(sd, VENC_LCDOUT, 0);
- venc_write(sd, VENC_HSPLS, 0);
- venc_write(sd, VENC_HSTART, 0);
- venc_write(sd, VENC_HVALID, 0);
- venc_write(sd, VENC_HINT, 0);
- venc_write(sd, VENC_VSPLS, 0);
- venc_write(sd, VENC_VSTART, 0);
- venc_write(sd, VENC_VVALID, 0);
- venc_write(sd, VENC_VINT, 0);
- venc_write(sd, VENC_YCCCTL, 0);
- venc_write(sd, VENC_DACSEL, 0);
-
- } else {
- venc_write(sd, VENC_VMOD, 0);
- /* disable VCLK output pin enable */
- venc_write(sd, VENC_VIDCTL, 0x141);
-
- /* Disable output sync pins */
- venc_write(sd, VENC_SYNCCTL, 0);
-
- /* Disable DCLOCK */
- venc_write(sd, VENC_DCLKCTL, 0);
- venc_write(sd, VENC_DRGBX1, 0x0000057C);
-
- /* Disable LCD output control (accepting default polarity) */
- venc_write(sd, VENC_LCDOUT, 0);
- if (venc->venc_type != VPBE_VERSION_3)
- venc_write(sd, VENC_CMPNT, 0x100);
- venc_write(sd, VENC_HSPLS, 0);
- venc_write(sd, VENC_HINT, 0);
- venc_write(sd, VENC_HSTART, 0);
- venc_write(sd, VENC_HVALID, 0);
-
- venc_write(sd, VENC_VSPLS, 0);
- venc_write(sd, VENC_VINT, 0);
- venc_write(sd, VENC_VSTART, 0);
- venc_write(sd, VENC_VVALID, 0);
-
- venc_write(sd, VENC_HSDLY, 0);
- venc_write(sd, VENC_VSDLY, 0);
-
- venc_write(sd, VENC_YCCCTL, 0);
- venc_write(sd, VENC_VSTARTA, 0);
-
- /* Set OSD clock and OSD Sync Adavance registers */
- venc_write(sd, VENC_OSDCLK0, 1);
- venc_write(sd, VENC_OSDCLK1, 2);
- }
-}
-
-static void
-venc_enable_vpss_clock(int venc_type,
- enum vpbe_enc_timings_type type,
- unsigned int pclock)
-{
- if (venc_type == VPBE_VERSION_1)
- return;
-
- if (venc_type == VPBE_VERSION_2 && (type == VPBE_ENC_STD || (type ==
- VPBE_ENC_DV_TIMINGS && pclock <= 27000000))) {
- vpss_enable_clock(VPSS_VENC_CLOCK_SEL, 1);
- vpss_enable_clock(VPSS_VPBE_CLOCK, 1);
- return;
- }
-
- if (venc_type == VPBE_VERSION_3 && type == VPBE_ENC_STD)
- vpss_enable_clock(VPSS_VENC_CLOCK_SEL, 0);
-}
-
-#define VDAC_CONFIG_SD_V3 0x0E21A6B6
-#define VDAC_CONFIG_SD_V2 0x081141CF
-/*
- * setting NTSC mode
- */
-static int venc_set_ntsc(struct v4l2_subdev *sd)
-{
- struct venc_state *venc = to_state(sd);
- struct venc_platform_data *pdata = venc->pdata;
-
- v4l2_dbg(debug, 2, sd, "venc_set_ntsc\n");
-
- /* Setup clock at VPSS & VENC for SD */
- vpss_enable_clock(VPSS_VENC_CLOCK_SEL, 1);
- if (pdata->setup_clock(VPBE_ENC_STD, V4L2_STD_525_60) < 0)
- return -EINVAL;
-
- venc_enable_vpss_clock(venc->venc_type, VPBE_ENC_STD, V4L2_STD_525_60);
- venc_enabledigitaloutput(sd, 0);
-
- if (venc->venc_type == VPBE_VERSION_3) {
- venc_write(sd, VENC_CLKCTL, 0x01);
- venc_write(sd, VENC_VIDCTL, 0);
- vdaccfg_write(sd, VDAC_CONFIG_SD_V3);
- } else if (venc->venc_type == VPBE_VERSION_2) {
- venc_write(sd, VENC_CLKCTL, 0x01);
- venc_write(sd, VENC_VIDCTL, 0);
- vdaccfg_write(sd, VDAC_CONFIG_SD_V2);
- } else {
- /* to set VENC CLK DIV to 1 - final clock is 54 MHz */
- venc_modify(sd, VENC_VIDCTL, 0, 1 << 1);
- /* Set REC656 Mode */
- venc_write(sd, VENC_YCCCTL, 0x1);
- venc_modify(sd, VENC_VDPRO, 0, VENC_VDPRO_DAFRQ);
- venc_modify(sd, VENC_VDPRO, 0, VENC_VDPRO_DAUPS);
- }
-
- venc_write(sd, VENC_VMOD, 0);
- venc_modify(sd, VENC_VMOD, (1 << VENC_VMOD_VIE_SHIFT),
- VENC_VMOD_VIE);
- venc_modify(sd, VENC_VMOD, (0 << VENC_VMOD_VMD), VENC_VMOD_VMD);
- venc_modify(sd, VENC_VMOD, (0 << VENC_VMOD_TVTYP_SHIFT),
- VENC_VMOD_TVTYP);
- venc_write(sd, VENC_DACTST, 0x0);
- venc_modify(sd, VENC_VMOD, VENC_VMOD_VENC, VENC_VMOD_VENC);
-
- return 0;
-}
-
-/*
- * setting PAL mode
- */
-static int venc_set_pal(struct v4l2_subdev *sd)
-{
- struct venc_state *venc = to_state(sd);
-
- v4l2_dbg(debug, 2, sd, "venc_set_pal\n");
-
- /* Setup clock at VPSS & VENC for SD */
- vpss_enable_clock(VPSS_VENC_CLOCK_SEL, 1);
- if (venc->pdata->setup_clock(VPBE_ENC_STD, V4L2_STD_625_50) < 0)
- return -EINVAL;
-
- venc_enable_vpss_clock(venc->venc_type, VPBE_ENC_STD, V4L2_STD_625_50);
- venc_enabledigitaloutput(sd, 0);
-
- if (venc->venc_type == VPBE_VERSION_3) {
- venc_write(sd, VENC_CLKCTL, 0x1);
- venc_write(sd, VENC_VIDCTL, 0);
- vdaccfg_write(sd, VDAC_CONFIG_SD_V3);
- } else if (venc->venc_type == VPBE_VERSION_2) {
- venc_write(sd, VENC_CLKCTL, 0x1);
- venc_write(sd, VENC_VIDCTL, 0);
- vdaccfg_write(sd, VDAC_CONFIG_SD_V2);
- } else {
- /* to set VENC CLK DIV to 1 - final clock is 54 MHz */
- venc_modify(sd, VENC_VIDCTL, 0, 1 << 1);
- /* Set REC656 Mode */
- venc_write(sd, VENC_YCCCTL, 0x1);
- }
-
- venc_modify(sd, VENC_SYNCCTL, 1 << VENC_SYNCCTL_OVD_SHIFT,
- VENC_SYNCCTL_OVD);
- venc_write(sd, VENC_VMOD, 0);
- venc_modify(sd, VENC_VMOD,
- (1 << VENC_VMOD_VIE_SHIFT),
- VENC_VMOD_VIE);
- venc_modify(sd, VENC_VMOD,
- (0 << VENC_VMOD_VMD), VENC_VMOD_VMD);
- venc_modify(sd, VENC_VMOD,
- (1 << VENC_VMOD_TVTYP_SHIFT),
- VENC_VMOD_TVTYP);
- venc_write(sd, VENC_DACTST, 0x0);
- venc_modify(sd, VENC_VMOD, VENC_VMOD_VENC, VENC_VMOD_VENC);
-
- return 0;
-}
-
-#define VDAC_CONFIG_HD_V2 0x081141EF
-/*
- * venc_set_480p59_94
- *
- * This function configures the video encoder to EDTV(525p) component setting.
- */
-static int venc_set_480p59_94(struct v4l2_subdev *sd)
-{
- struct venc_state *venc = to_state(sd);
- struct venc_platform_data *pdata = venc->pdata;
-
- v4l2_dbg(debug, 2, sd, "venc_set_480p59_94\n");
- if (venc->venc_type != VPBE_VERSION_1 &&
- venc->venc_type != VPBE_VERSION_2)
- return -EINVAL;
-
- /* Setup clock at VPSS & VENC for SD */
- if (pdata->setup_clock(VPBE_ENC_DV_TIMINGS, 27000000) < 0)
- return -EINVAL;
-
- venc_enable_vpss_clock(venc->venc_type, VPBE_ENC_DV_TIMINGS, 27000000);
- venc_enabledigitaloutput(sd, 0);
-
- if (venc->venc_type == VPBE_VERSION_2)
- vdaccfg_write(sd, VDAC_CONFIG_HD_V2);
- venc_write(sd, VENC_OSDCLK0, 0);
- venc_write(sd, VENC_OSDCLK1, 1);
-
- if (venc->venc_type == VPBE_VERSION_1) {
- venc_modify(sd, VENC_VDPRO, VENC_VDPRO_DAFRQ,
- VENC_VDPRO_DAFRQ);
- venc_modify(sd, VENC_VDPRO, VENC_VDPRO_DAUPS,
- VENC_VDPRO_DAUPS);
- }
-
- venc_write(sd, VENC_VMOD, 0);
- venc_modify(sd, VENC_VMOD, (1 << VENC_VMOD_VIE_SHIFT),
- VENC_VMOD_VIE);
- venc_modify(sd, VENC_VMOD, VENC_VMOD_HDMD, VENC_VMOD_HDMD);
- venc_modify(sd, VENC_VMOD, (HDTV_525P << VENC_VMOD_TVTYP_SHIFT),
- VENC_VMOD_TVTYP);
- venc_modify(sd, VENC_VMOD, VENC_VMOD_VDMD_YCBCR8 <<
- VENC_VMOD_VDMD_SHIFT, VENC_VMOD_VDMD);
-
- venc_modify(sd, VENC_VMOD, VENC_VMOD_VENC, VENC_VMOD_VENC);
-
- return 0;
-}
-
-/*
- * venc_set_625p
- *
- * This function configures the video encoder to HDTV(625p) component setting
- */
-static int venc_set_576p50(struct v4l2_subdev *sd)
-{
- struct venc_state *venc = to_state(sd);
- struct venc_platform_data *pdata = venc->pdata;
-
- v4l2_dbg(debug, 2, sd, "venc_set_576p50\n");
-
- if (venc->venc_type != VPBE_VERSION_1 &&
- venc->venc_type != VPBE_VERSION_2)
- return -EINVAL;
- /* Setup clock at VPSS & VENC for SD */
- if (pdata->setup_clock(VPBE_ENC_DV_TIMINGS, 27000000) < 0)
- return -EINVAL;
-
- venc_enable_vpss_clock(venc->venc_type, VPBE_ENC_DV_TIMINGS, 27000000);
- venc_enabledigitaloutput(sd, 0);
-
- if (venc->venc_type == VPBE_VERSION_2)
- vdaccfg_write(sd, VDAC_CONFIG_HD_V2);
-
- venc_write(sd, VENC_OSDCLK0, 0);
- venc_write(sd, VENC_OSDCLK1, 1);
-
- if (venc->venc_type == VPBE_VERSION_1) {
- venc_modify(sd, VENC_VDPRO, VENC_VDPRO_DAFRQ,
- VENC_VDPRO_DAFRQ);
- venc_modify(sd, VENC_VDPRO, VENC_VDPRO_DAUPS,
- VENC_VDPRO_DAUPS);
- }
-
- venc_write(sd, VENC_VMOD, 0);
- venc_modify(sd, VENC_VMOD, (1 << VENC_VMOD_VIE_SHIFT),
- VENC_VMOD_VIE);
- venc_modify(sd, VENC_VMOD, VENC_VMOD_HDMD, VENC_VMOD_HDMD);
- venc_modify(sd, VENC_VMOD, (HDTV_625P << VENC_VMOD_TVTYP_SHIFT),
- VENC_VMOD_TVTYP);
-
- venc_modify(sd, VENC_VMOD, VENC_VMOD_VDMD_YCBCR8 <<
- VENC_VMOD_VDMD_SHIFT, VENC_VMOD_VDMD);
- venc_modify(sd, VENC_VMOD, VENC_VMOD_VENC, VENC_VMOD_VENC);
-
- return 0;
-}
-
-/*
- * venc_set_720p60_internal - Setup 720p60 in venc for dm365 only
- */
-static int venc_set_720p60_internal(struct v4l2_subdev *sd)
-{
- struct venc_state *venc = to_state(sd);
- struct venc_platform_data *pdata = venc->pdata;
-
- if (pdata->setup_clock(VPBE_ENC_DV_TIMINGS, 74250000) < 0)
- return -EINVAL;
-
- venc_enable_vpss_clock(venc->venc_type, VPBE_ENC_DV_TIMINGS, 74250000);
- venc_enabledigitaloutput(sd, 0);
-
- venc_write(sd, VENC_OSDCLK0, 0);
- venc_write(sd, VENC_OSDCLK1, 1);
-
- venc_write(sd, VENC_VMOD, 0);
- /* DM365 component HD mode */
- venc_modify(sd, VENC_VMOD, (1 << VENC_VMOD_VIE_SHIFT),
- VENC_VMOD_VIE);
- venc_modify(sd, VENC_VMOD, VENC_VMOD_HDMD, VENC_VMOD_HDMD);
- venc_modify(sd, VENC_VMOD, (HDTV_720P << VENC_VMOD_TVTYP_SHIFT),
- VENC_VMOD_TVTYP);
- venc_modify(sd, VENC_VMOD, VENC_VMOD_VENC, VENC_VMOD_VENC);
- venc_write(sd, VENC_XHINTVL, 0);
- return 0;
-}
-
-/*
- * venc_set_1080i30_internal - Setup 1080i30 in venc for dm365 only
- */
-static int venc_set_1080i30_internal(struct v4l2_subdev *sd)
-{
- struct venc_state *venc = to_state(sd);
- struct venc_platform_data *pdata = venc->pdata;
-
- if (pdata->setup_clock(VPBE_ENC_DV_TIMINGS, 74250000) < 0)
- return -EINVAL;
-
- venc_enable_vpss_clock(venc->venc_type, VPBE_ENC_DV_TIMINGS, 74250000);
- venc_enabledigitaloutput(sd, 0);
-
- venc_write(sd, VENC_OSDCLK0, 0);
- venc_write(sd, VENC_OSDCLK1, 1);
-
-
- venc_write(sd, VENC_VMOD, 0);
- /* DM365 component HD mode */
- venc_modify(sd, VENC_VMOD, (1 << VENC_VMOD_VIE_SHIFT),
- VENC_VMOD_VIE);
- venc_modify(sd, VENC_VMOD, VENC_VMOD_HDMD, VENC_VMOD_HDMD);
- venc_modify(sd, VENC_VMOD, (HDTV_1080I << VENC_VMOD_TVTYP_SHIFT),
- VENC_VMOD_TVTYP);
- venc_modify(sd, VENC_VMOD, VENC_VMOD_VENC, VENC_VMOD_VENC);
- venc_write(sd, VENC_XHINTVL, 0);
- return 0;
-}
-
-static int venc_s_std_output(struct v4l2_subdev *sd, v4l2_std_id norm)
-{
- v4l2_dbg(debug, 1, sd, "venc_s_std_output\n");
-
- if (norm & V4L2_STD_525_60)
- return venc_set_ntsc(sd);
- else if (norm & V4L2_STD_625_50)
- return venc_set_pal(sd);
-
- return -EINVAL;
-}
-
-static int venc_s_dv_timings(struct v4l2_subdev *sd,
- struct v4l2_dv_timings *dv_timings)
-{
- struct venc_state *venc = to_state(sd);
- u32 height = dv_timings->bt.height;
- int ret;
-
- v4l2_dbg(debug, 1, sd, "venc_s_dv_timings\n");
-
- if (height == 576)
- return venc_set_576p50(sd);
- else if (height == 480)
- return venc_set_480p59_94(sd);
- else if ((height == 720) &&
- (venc->venc_type == VPBE_VERSION_2)) {
- /* TBD setup internal 720p mode here */
- ret = venc_set_720p60_internal(sd);
- /* for DM365 VPBE, there is DAC inside */
- vdaccfg_write(sd, VDAC_CONFIG_HD_V2);
- return ret;
- } else if ((height == 1080) &&
- (venc->venc_type == VPBE_VERSION_2)) {
- /* TBD setup internal 1080i mode here */
- ret = venc_set_1080i30_internal(sd);
- /* for DM365 VPBE, there is DAC inside */
- vdaccfg_write(sd, VDAC_CONFIG_HD_V2);
- return ret;
- }
- return -EINVAL;
-}
-
-static int venc_s_routing(struct v4l2_subdev *sd, u32 input, u32 output,
- u32 config)
-{
- struct venc_state *venc = to_state(sd);
- int ret;
-
- v4l2_dbg(debug, 1, sd, "venc_s_routing\n");
-
- ret = venc_set_dac(sd, output);
- if (!ret)
- venc->output = output;
-
- return ret;
-}
-
-static long venc_command(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
-{
- u32 val;
-
- switch (cmd) {
- case VENC_GET_FLD:
- val = venc_read(sd, VENC_VSTAT);
- *((int *)arg) = ((val & VENC_VSTAT_FIDST) ==
- VENC_VSTAT_FIDST);
- break;
- default:
- v4l2_err(sd, "Wrong IOCTL cmd\n");
- break;
- }
-
- return 0;
-}
-
-static const struct v4l2_subdev_core_ops venc_core_ops = {
- .command = venc_command,
-};
-
-static const struct v4l2_subdev_video_ops venc_video_ops = {
- .s_routing = venc_s_routing,
- .s_std_output = venc_s_std_output,
- .s_dv_timings = venc_s_dv_timings,
-};
-
-static const struct v4l2_subdev_ops venc_ops = {
- .core = &venc_core_ops,
- .video = &venc_video_ops,
-};
-
-static int venc_initialize(struct v4l2_subdev *sd)
-{
- struct venc_state *venc = to_state(sd);
- int ret;
-
- /* Set default to output to composite and std to NTSC */
- venc->output = 0;
- venc->std = V4L2_STD_525_60;
-
- ret = venc_s_routing(sd, 0, venc->output, 0);
- if (ret < 0) {
- v4l2_err(sd, "Error setting output during init\n");
- return -EINVAL;
- }
-
- ret = venc_s_std_output(sd, venc->std);
- if (ret < 0) {
- v4l2_err(sd, "Error setting std during init\n");
- return -EINVAL;
- }
-
- return ret;
-}
-
-static int venc_device_get(struct device *dev, void *data)
-{
- struct platform_device *pdev = to_platform_device(dev);
- struct venc_state **venc = data;
-
- if (strstr(pdev->name, "vpbe-venc") != NULL)
- *venc = platform_get_drvdata(pdev);
-
- return 0;
-}
-
-struct v4l2_subdev *venc_sub_dev_init(struct v4l2_device *v4l2_dev,
- const char *venc_name)
-{
- struct venc_state *venc = NULL;
-
- bus_for_each_dev(&platform_bus_type, NULL, &venc,
- venc_device_get);
- if (venc == NULL)
- return NULL;
-
- v4l2_subdev_init(&venc->sd, &venc_ops);
-
- strscpy(venc->sd.name, venc_name, sizeof(venc->sd.name));
- if (v4l2_device_register_subdev(v4l2_dev, &venc->sd) < 0) {
- v4l2_err(v4l2_dev,
- "vpbe unable to register venc sub device\n");
- return NULL;
- }
- if (venc_initialize(&venc->sd)) {
- v4l2_err(v4l2_dev,
- "vpbe venc initialization failed\n");
- return NULL;
- }
-
- return &venc->sd;
-}
-EXPORT_SYMBOL(venc_sub_dev_init);
-
-static int venc_probe(struct platform_device *pdev)
-{
- const struct platform_device_id *pdev_id;
- struct venc_state *venc;
-
- if (!pdev->dev.platform_data) {
- dev_err(&pdev->dev, "No platform data for VENC sub device");
- return -EINVAL;
- }
-
- pdev_id = platform_get_device_id(pdev);
- if (!pdev_id)
- return -EINVAL;
-
- venc = devm_kzalloc(&pdev->dev, sizeof(struct venc_state), GFP_KERNEL);
- if (venc == NULL)
- return -ENOMEM;
-
- venc->venc_type = pdev_id->driver_data;
- venc->pdev = &pdev->dev;
- venc->pdata = pdev->dev.platform_data;
-
- venc->venc_base = devm_platform_ioremap_resource(pdev, 0);
- if (IS_ERR(venc->venc_base))
- return PTR_ERR(venc->venc_base);
-
- if (venc->venc_type != VPBE_VERSION_1) {
- venc->vdaccfg_reg = devm_platform_ioremap_resource(pdev, 1);
- if (IS_ERR(venc->vdaccfg_reg))
- return PTR_ERR(venc->vdaccfg_reg);
- }
- spin_lock_init(&venc->lock);
- platform_set_drvdata(pdev, venc);
- dev_notice(venc->pdev, "VENC sub device probe success\n");
-
- return 0;
-}
-
-static int venc_remove(struct platform_device *pdev)
-{
- return 0;
-}
-
-static struct platform_driver venc_driver = {
- .probe = venc_probe,
- .remove = venc_remove,
- .driver = {
- .name = MODULE_NAME,
- },
- .id_table = vpbe_venc_devtype
-};
-
-module_platform_driver(venc_driver);
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("VPBE VENC Driver");
-MODULE_AUTHOR("Texas Instruments");
diff --git a/drivers/media/platform/ti/davinci/vpbe_venc_regs.h b/drivers/media/platform/ti/davinci/vpbe_venc_regs.h
deleted file mode 100644
index 29d8fc3af662..000000000000
--- a/drivers/media/platform/ti/davinci/vpbe_venc_regs.h
+++ /dev/null
@@ -1,165 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-/*
- * Copyright (C) 2006-2010 Texas Instruments Inc
- */
-#ifndef _VPBE_VENC_REGS_H
-#define _VPBE_VENC_REGS_H
-
-/* VPBE Video Encoder / Digital LCD Subsystem Registers (VENC) */
-#define VENC_VMOD 0x00
-#define VENC_VIDCTL 0x04
-#define VENC_VDPRO 0x08
-#define VENC_SYNCCTL 0x0C
-#define VENC_HSPLS 0x10
-#define VENC_VSPLS 0x14
-#define VENC_HINT 0x18
-#define VENC_HSTART 0x1C
-#define VENC_HVALID 0x20
-#define VENC_VINT 0x24
-#define VENC_VSTART 0x28
-#define VENC_VVALID 0x2C
-#define VENC_HSDLY 0x30
-#define VENC_VSDLY 0x34
-#define VENC_YCCCTL 0x38
-#define VENC_RGBCTL 0x3C
-#define VENC_RGBCLP 0x40
-#define VENC_LINECTL 0x44
-#define VENC_CULLLINE 0x48
-#define VENC_LCDOUT 0x4C
-#define VENC_BRTS 0x50
-#define VENC_BRTW 0x54
-#define VENC_ACCTL 0x58
-#define VENC_PWMP 0x5C
-#define VENC_PWMW 0x60
-#define VENC_DCLKCTL 0x64
-#define VENC_DCLKPTN0 0x68
-#define VENC_DCLKPTN1 0x6C
-#define VENC_DCLKPTN2 0x70
-#define VENC_DCLKPTN3 0x74
-#define VENC_DCLKPTN0A 0x78
-#define VENC_DCLKPTN1A 0x7C
-#define VENC_DCLKPTN2A 0x80
-#define VENC_DCLKPTN3A 0x84
-#define VENC_DCLKHS 0x88
-#define VENC_DCLKHSA 0x8C
-#define VENC_DCLKHR 0x90
-#define VENC_DCLKVS 0x94
-#define VENC_DCLKVR 0x98
-#define VENC_CAPCTL 0x9C
-#define VENC_CAPDO 0xA0
-#define VENC_CAPDE 0xA4
-#define VENC_ATR0 0xA8
-#define VENC_ATR1 0xAC
-#define VENC_ATR2 0xB0
-#define VENC_VSTAT 0xB8
-#define VENC_RAMADR 0xBC
-#define VENC_RAMPORT 0xC0
-#define VENC_DACTST 0xC4
-#define VENC_YCOLVL 0xC8
-#define VENC_SCPROG 0xCC
-#define VENC_CVBS 0xDC
-#define VENC_CMPNT 0xE0
-#define VENC_ETMG0 0xE4
-#define VENC_ETMG1 0xE8
-#define VENC_ETMG2 0xEC
-#define VENC_ETMG3 0xF0
-#define VENC_DACSEL 0xF4
-#define VENC_ARGBX0 0x100
-#define VENC_ARGBX1 0x104
-#define VENC_ARGBX2 0x108
-#define VENC_ARGBX3 0x10C
-#define VENC_ARGBX4 0x110
-#define VENC_DRGBX0 0x114
-#define VENC_DRGBX1 0x118
-#define VENC_DRGBX2 0x11C
-#define VENC_DRGBX3 0x120
-#define VENC_DRGBX4 0x124
-#define VENC_VSTARTA 0x128
-#define VENC_OSDCLK0 0x12C
-#define VENC_OSDCLK1 0x130
-#define VENC_HVLDCL0 0x134
-#define VENC_HVLDCL1 0x138
-#define VENC_OSDHADV 0x13C
-#define VENC_CLKCTL 0x140
-#define VENC_GAMCTL 0x144
-#define VENC_XHINTVL 0x174
-
-/* bit definitions */
-#define VPBE_PCR_VENC_DIV (1 << 1)
-#define VPBE_PCR_CLK_OFF (1 << 0)
-
-#define VENC_VMOD_VDMD_SHIFT 12
-#define VENC_VMOD_VDMD_YCBCR16 0
-#define VENC_VMOD_VDMD_YCBCR8 1
-#define VENC_VMOD_VDMD_RGB666 2
-#define VENC_VMOD_VDMD_RGB8 3
-#define VENC_VMOD_VDMD_EPSON 4
-#define VENC_VMOD_VDMD_CASIO 5
-#define VENC_VMOD_VDMD_UDISPQVGA 6
-#define VENC_VMOD_VDMD_STNLCD 7
-#define VENC_VMOD_VIE_SHIFT 1
-#define VENC_VMOD_VDMD (7 << 12)
-#define VENC_VMOD_ITLCL (1 << 11)
-#define VENC_VMOD_ITLC (1 << 10)
-#define VENC_VMOD_NSIT (1 << 9)
-#define VENC_VMOD_HDMD (1 << 8)
-#define VENC_VMOD_TVTYP_SHIFT 6
-#define VENC_VMOD_TVTYP (3 << 6)
-#define VENC_VMOD_SLAVE (1 << 5)
-#define VENC_VMOD_VMD (1 << 4)
-#define VENC_VMOD_BLNK (1 << 3)
-#define VENC_VMOD_VIE (1 << 1)
-#define VENC_VMOD_VENC (1 << 0)
-
-/* VMOD TVTYP options for HDMD=0 */
-#define SDTV_NTSC 0
-#define SDTV_PAL 1
-/* VMOD TVTYP options for HDMD=1 */
-#define HDTV_525P 0
-#define HDTV_625P 1
-#define HDTV_1080I 2
-#define HDTV_720P 3
-
-#define VENC_VIDCTL_VCLKP (1 << 14)
-#define VENC_VIDCTL_VCLKE_SHIFT 13
-#define VENC_VIDCTL_VCLKE (1 << 13)
-#define VENC_VIDCTL_VCLKZ_SHIFT 12
-#define VENC_VIDCTL_VCLKZ (1 << 12)
-#define VENC_VIDCTL_SYDIR_SHIFT 8
-#define VENC_VIDCTL_SYDIR (1 << 8)
-#define VENC_VIDCTL_DOMD_SHIFT 4
-#define VENC_VIDCTL_DOMD (3 << 4)
-#define VENC_VIDCTL_YCDIR_SHIFT 0
-#define VENC_VIDCTL_YCDIR (1 << 0)
-
-#define VENC_VDPRO_ATYCC_SHIFT 5
-#define VENC_VDPRO_ATYCC (1 << 5)
-#define VENC_VDPRO_ATCOM_SHIFT 4
-#define VENC_VDPRO_ATCOM (1 << 4)
-#define VENC_VDPRO_DAFRQ (1 << 3)
-#define VENC_VDPRO_DAUPS (1 << 2)
-#define VENC_VDPRO_CUPS (1 << 1)
-#define VENC_VDPRO_YUPS (1 << 0)
-
-#define VENC_SYNCCTL_VPL_SHIFT 3
-#define VENC_SYNCCTL_VPL (1 << 3)
-#define VENC_SYNCCTL_HPL_SHIFT 2
-#define VENC_SYNCCTL_HPL (1 << 2)
-#define VENC_SYNCCTL_SYEV_SHIFT 1
-#define VENC_SYNCCTL_SYEV (1 << 1)
-#define VENC_SYNCCTL_SYEH_SHIFT 0
-#define VENC_SYNCCTL_SYEH (1 << 0)
-#define VENC_SYNCCTL_OVD_SHIFT 14
-#define VENC_SYNCCTL_OVD (1 << 14)
-
-#define VENC_DCLKCTL_DCKEC_SHIFT 11
-#define VENC_DCLKCTL_DCKEC (1 << 11)
-#define VENC_DCLKCTL_DCKPW_SHIFT 0
-#define VENC_DCLKCTL_DCKPW (0x3f << 0)
-
-#define VENC_VSTAT_FIDST (1 << 4)
-
-#define VENC_CMPNT_MRGB_SHIFT 14
-#define VENC_CMPNT_MRGB (1 << 14)
-
-#endif /* _VPBE_VENC_REGS_H */
diff --git a/drivers/media/platform/ti/davinci/vpif.c b/drivers/media/platform/ti/davinci/vpif.c
index da27da4c165a..832489822706 100644
--- a/drivers/media/platform/ti/davinci/vpif.c
+++ b/drivers/media/platform/ti/davinci/vpif.c
@@ -480,7 +480,7 @@ static int vpif_probe(struct platform_device *pdev)
ret = irq;
goto err_put_rpm;
}
- res_irq = (struct resource)DEFINE_RES_IRQ_NAMED(irq, of_node_full_name(pdev->dev.of_node));
+ res_irq = DEFINE_RES_IRQ_NAMED(irq, of_node_full_name(pdev->dev.of_node));
res_irq.flags |= irq_get_trigger_type(irq);
pdev_capture = kzalloc(sizeof(*pdev_capture), GFP_KERNEL);
diff --git a/drivers/media/platform/ti/davinci/vpss.c b/drivers/media/platform/ti/davinci/vpss.c
deleted file mode 100644
index d15b991ab17c..000000000000
--- a/drivers/media/platform/ti/davinci/vpss.c
+++ /dev/null
@@ -1,529 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * Copyright (C) 2009 Texas Instruments.
- *
- * common vpss system module platform driver for all video drivers.
- */
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/io.h>
-#include <linux/pm_runtime.h>
-#include <linux/err.h>
-
-#include <media/davinci/vpss.h>
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("VPSS Driver");
-MODULE_AUTHOR("Texas Instruments");
-
-/* DM644x defines */
-#define DM644X_SBL_PCR_VPSS (4)
-
-#define DM355_VPSSBL_INTSEL 0x10
-#define DM355_VPSSBL_EVTSEL 0x14
-/* vpss BL register offsets */
-#define DM355_VPSSBL_CCDCMUX 0x1c
-/* vpss CLK register offsets */
-#define DM355_VPSSCLK_CLKCTRL 0x04
-/* masks and shifts */
-#define VPSS_HSSISEL_SHIFT 4
-/*
- * VDINT0 - vpss_int0, VDINT1 - vpss_int1, H3A - vpss_int4,
- * IPIPE_INT1_SDR - vpss_int5
- */
-#define DM355_VPSSBL_INTSEL_DEFAULT 0xff83ff10
-/* VENCINT - vpss_int8 */
-#define DM355_VPSSBL_EVTSEL_DEFAULT 0x4
-
-#define DM365_ISP5_PCCR 0x04
-#define DM365_ISP5_PCCR_BL_CLK_ENABLE BIT(0)
-#define DM365_ISP5_PCCR_ISIF_CLK_ENABLE BIT(1)
-#define DM365_ISP5_PCCR_H3A_CLK_ENABLE BIT(2)
-#define DM365_ISP5_PCCR_RSZ_CLK_ENABLE BIT(3)
-#define DM365_ISP5_PCCR_IPIPE_CLK_ENABLE BIT(4)
-#define DM365_ISP5_PCCR_IPIPEIF_CLK_ENABLE BIT(5)
-#define DM365_ISP5_PCCR_RSV BIT(6)
-
-#define DM365_ISP5_BCR 0x08
-#define DM365_ISP5_BCR_ISIF_OUT_ENABLE BIT(1)
-
-#define DM365_ISP5_INTSEL1 0x10
-#define DM365_ISP5_INTSEL2 0x14
-#define DM365_ISP5_INTSEL3 0x18
-#define DM365_ISP5_CCDCMUX 0x20
-#define DM365_ISP5_PG_FRAME_SIZE 0x28
-#define DM365_VPBE_CLK_CTRL 0x00
-
-#define VPSS_CLK_CTRL 0x01c40044
-#define VPSS_CLK_CTRL_VENCCLKEN BIT(3)
-#define VPSS_CLK_CTRL_DACCLKEN BIT(4)
-
-/*
- * vpss interrupts. VDINT0 - vpss_int0, VDINT1 - vpss_int1,
- * AF - vpss_int3
- */
-#define DM365_ISP5_INTSEL1_DEFAULT 0x0b1f0100
-/* AEW - vpss_int6, RSZ_INT_DMA - vpss_int5 */
-#define DM365_ISP5_INTSEL2_DEFAULT 0x1f0a0f1f
-/* VENC - vpss_int8 */
-#define DM365_ISP5_INTSEL3_DEFAULT 0x00000015
-
-/* masks and shifts for DM365*/
-#define DM365_CCDC_PG_VD_POL_SHIFT 0
-#define DM365_CCDC_PG_HD_POL_SHIFT 1
-
-#define CCD_SRC_SEL_MASK (BIT_MASK(5) | BIT_MASK(4))
-#define CCD_SRC_SEL_SHIFT 4
-
-/* Different SoC platforms supported by this driver */
-enum vpss_platform_type {
- DM644X,
- DM355,
- DM365,
-};
-
-/*
- * vpss operations. Depends on platform. Not all functions are available
- * on all platforms. The api, first check if a function is available before
- * invoking it. In the probe, the function ptrs are initialized based on
- * vpss name. vpss name can be "dm355_vpss", "dm644x_vpss" etc.
- */
-struct vpss_hw_ops {
- /* enable clock */
- int (*enable_clock)(enum vpss_clock_sel clock_sel, int en);
- /* select input to ccdc */
- void (*select_ccdc_source)(enum vpss_ccdc_source_sel src_sel);
- /* clear wbl overflow bit */
- int (*clear_wbl_overflow)(enum vpss_wbl_sel wbl_sel);
- /* set sync polarity */
- void (*set_sync_pol)(struct vpss_sync_pol);
- /* set the PG_FRAME_SIZE register*/
- void (*set_pg_frame_size)(struct vpss_pg_frame_size);
- /* check and clear interrupt if occurred */
- int (*dma_complete_interrupt)(void);
-};
-
-/* vpss configuration */
-struct vpss_oper_config {
- __iomem void *vpss_regs_base0;
- __iomem void *vpss_regs_base1;
- __iomem void *vpss_regs_base2;
- enum vpss_platform_type platform;
- spinlock_t vpss_lock;
- struct vpss_hw_ops hw_ops;
-};
-
-static struct vpss_oper_config oper_cfg;
-
-/* register access routines */
-static inline u32 bl_regr(u32 offset)
-{
- return __raw_readl(oper_cfg.vpss_regs_base0 + offset);
-}
-
-static inline void bl_regw(u32 val, u32 offset)
-{
- __raw_writel(val, oper_cfg.vpss_regs_base0 + offset);
-}
-
-static inline u32 vpss_regr(u32 offset)
-{
- return __raw_readl(oper_cfg.vpss_regs_base1 + offset);
-}
-
-static inline void vpss_regw(u32 val, u32 offset)
-{
- __raw_writel(val, oper_cfg.vpss_regs_base1 + offset);
-}
-
-/* For DM365 only */
-static inline u32 isp5_read(u32 offset)
-{
- return __raw_readl(oper_cfg.vpss_regs_base0 + offset);
-}
-
-/* For DM365 only */
-static inline void isp5_write(u32 val, u32 offset)
-{
- __raw_writel(val, oper_cfg.vpss_regs_base0 + offset);
-}
-
-static void dm365_select_ccdc_source(enum vpss_ccdc_source_sel src_sel)
-{
- u32 temp = isp5_read(DM365_ISP5_CCDCMUX) & ~CCD_SRC_SEL_MASK;
-
- /* if we are using pattern generator, enable it */
- if (src_sel == VPSS_PGLPBK || src_sel == VPSS_CCDCPG)
- temp |= 0x08;
-
- temp |= (src_sel << CCD_SRC_SEL_SHIFT);
- isp5_write(temp, DM365_ISP5_CCDCMUX);
-}
-
-static void dm355_select_ccdc_source(enum vpss_ccdc_source_sel src_sel)
-{
- bl_regw(src_sel << VPSS_HSSISEL_SHIFT, DM355_VPSSBL_CCDCMUX);
-}
-
-int vpss_dma_complete_interrupt(void)
-{
- if (!oper_cfg.hw_ops.dma_complete_interrupt)
- return 2;
- return oper_cfg.hw_ops.dma_complete_interrupt();
-}
-EXPORT_SYMBOL(vpss_dma_complete_interrupt);
-
-int vpss_select_ccdc_source(enum vpss_ccdc_source_sel src_sel)
-{
- if (!oper_cfg.hw_ops.select_ccdc_source)
- return -EINVAL;
-
- oper_cfg.hw_ops.select_ccdc_source(src_sel);
- return 0;
-}
-EXPORT_SYMBOL(vpss_select_ccdc_source);
-
-static int dm644x_clear_wbl_overflow(enum vpss_wbl_sel wbl_sel)
-{
- u32 mask = 1, val;
-
- if (wbl_sel < VPSS_PCR_AEW_WBL_0 ||
- wbl_sel > VPSS_PCR_CCDC_WBL_O)
- return -EINVAL;
-
- /* writing a 0 clear the overflow */
- mask = ~(mask << wbl_sel);
- val = bl_regr(DM644X_SBL_PCR_VPSS) & mask;
- bl_regw(val, DM644X_SBL_PCR_VPSS);
- return 0;
-}
-
-void vpss_set_sync_pol(struct vpss_sync_pol sync)
-{
- if (!oper_cfg.hw_ops.set_sync_pol)
- return;
-
- oper_cfg.hw_ops.set_sync_pol(sync);
-}
-EXPORT_SYMBOL(vpss_set_sync_pol);
-
-int vpss_clear_wbl_overflow(enum vpss_wbl_sel wbl_sel)
-{
- if (!oper_cfg.hw_ops.clear_wbl_overflow)
- return -EINVAL;
-
- return oper_cfg.hw_ops.clear_wbl_overflow(wbl_sel);
-}
-EXPORT_SYMBOL(vpss_clear_wbl_overflow);
-
-/*
- * dm355_enable_clock - Enable VPSS Clock
- * @clock_sel: Clock to be enabled/disabled
- * @en: enable/disable flag
- *
- * This is called to enable or disable a vpss clock
- */
-static int dm355_enable_clock(enum vpss_clock_sel clock_sel, int en)
-{
- unsigned long flags;
- u32 utemp, mask = 0x1, shift = 0;
-
- switch (clock_sel) {
- case VPSS_VPBE_CLOCK:
- /* nothing since lsb */
- break;
- case VPSS_VENC_CLOCK_SEL:
- shift = 2;
- break;
- case VPSS_CFALD_CLOCK:
- shift = 3;
- break;
- case VPSS_H3A_CLOCK:
- shift = 4;
- break;
- case VPSS_IPIPE_CLOCK:
- shift = 5;
- break;
- case VPSS_CCDC_CLOCK:
- shift = 6;
- break;
- default:
- printk(KERN_ERR "dm355_enable_clock: Invalid selector: %d\n",
- clock_sel);
- return -EINVAL;
- }
-
- spin_lock_irqsave(&oper_cfg.vpss_lock, flags);
- utemp = vpss_regr(DM355_VPSSCLK_CLKCTRL);
- if (!en)
- utemp &= ~(mask << shift);
- else
- utemp |= (mask << shift);
-
- vpss_regw(utemp, DM355_VPSSCLK_CLKCTRL);
- spin_unlock_irqrestore(&oper_cfg.vpss_lock, flags);
- return 0;
-}
-
-static int dm365_enable_clock(enum vpss_clock_sel clock_sel, int en)
-{
- unsigned long flags;
- u32 utemp, mask = 0x1, shift = 0, offset = DM365_ISP5_PCCR;
- u32 (*read)(u32 offset) = isp5_read;
- void(*write)(u32 val, u32 offset) = isp5_write;
-
- switch (clock_sel) {
- case VPSS_BL_CLOCK:
- break;
- case VPSS_CCDC_CLOCK:
- shift = 1;
- break;
- case VPSS_H3A_CLOCK:
- shift = 2;
- break;
- case VPSS_RSZ_CLOCK:
- shift = 3;
- break;
- case VPSS_IPIPE_CLOCK:
- shift = 4;
- break;
- case VPSS_IPIPEIF_CLOCK:
- shift = 5;
- break;
- case VPSS_PCLK_INTERNAL:
- shift = 6;
- break;
- case VPSS_PSYNC_CLOCK_SEL:
- shift = 7;
- break;
- case VPSS_VPBE_CLOCK:
- read = vpss_regr;
- write = vpss_regw;
- offset = DM365_VPBE_CLK_CTRL;
- break;
- case VPSS_VENC_CLOCK_SEL:
- shift = 2;
- read = vpss_regr;
- write = vpss_regw;
- offset = DM365_VPBE_CLK_CTRL;
- break;
- case VPSS_LDC_CLOCK:
- shift = 3;
- read = vpss_regr;
- write = vpss_regw;
- offset = DM365_VPBE_CLK_CTRL;
- break;
- case VPSS_FDIF_CLOCK:
- shift = 4;
- read = vpss_regr;
- write = vpss_regw;
- offset = DM365_VPBE_CLK_CTRL;
- break;
- case VPSS_OSD_CLOCK_SEL:
- shift = 6;
- read = vpss_regr;
- write = vpss_regw;
- offset = DM365_VPBE_CLK_CTRL;
- break;
- case VPSS_LDC_CLOCK_SEL:
- shift = 7;
- read = vpss_regr;
- write = vpss_regw;
- offset = DM365_VPBE_CLK_CTRL;
- break;
- default:
- printk(KERN_ERR "dm365_enable_clock: Invalid selector: %d\n",
- clock_sel);
- return -1;
- }
-
- spin_lock_irqsave(&oper_cfg.vpss_lock, flags);
- utemp = read(offset);
- if (!en) {
- mask = ~mask;
- utemp &= (mask << shift);
- } else
- utemp |= (mask << shift);
-
- write(utemp, offset);
- spin_unlock_irqrestore(&oper_cfg.vpss_lock, flags);
-
- return 0;
-}
-
-int vpss_enable_clock(enum vpss_clock_sel clock_sel, int en)
-{
- if (!oper_cfg.hw_ops.enable_clock)
- return -EINVAL;
-
- return oper_cfg.hw_ops.enable_clock(clock_sel, en);
-}
-EXPORT_SYMBOL(vpss_enable_clock);
-
-void dm365_vpss_set_sync_pol(struct vpss_sync_pol sync)
-{
- int val = 0;
- val = isp5_read(DM365_ISP5_CCDCMUX);
-
- val |= (sync.ccdpg_hdpol << DM365_CCDC_PG_HD_POL_SHIFT);
- val |= (sync.ccdpg_vdpol << DM365_CCDC_PG_VD_POL_SHIFT);
-
- isp5_write(val, DM365_ISP5_CCDCMUX);
-}
-EXPORT_SYMBOL(dm365_vpss_set_sync_pol);
-
-void vpss_set_pg_frame_size(struct vpss_pg_frame_size frame_size)
-{
- if (!oper_cfg.hw_ops.set_pg_frame_size)
- return;
-
- oper_cfg.hw_ops.set_pg_frame_size(frame_size);
-}
-EXPORT_SYMBOL(vpss_set_pg_frame_size);
-
-void dm365_vpss_set_pg_frame_size(struct vpss_pg_frame_size frame_size)
-{
- int current_reg = ((frame_size.hlpfr >> 1) - 1) << 16;
-
- current_reg |= (frame_size.pplen - 1);
- isp5_write(current_reg, DM365_ISP5_PG_FRAME_SIZE);
-}
-EXPORT_SYMBOL(dm365_vpss_set_pg_frame_size);
-
-static int vpss_probe(struct platform_device *pdev)
-{
- char *platform_name;
-
- if (!pdev->dev.platform_data) {
- dev_err(&pdev->dev, "no platform data\n");
- return -ENOENT;
- }
-
- platform_name = pdev->dev.platform_data;
- if (!strcmp(platform_name, "dm355_vpss"))
- oper_cfg.platform = DM355;
- else if (!strcmp(platform_name, "dm365_vpss"))
- oper_cfg.platform = DM365;
- else if (!strcmp(platform_name, "dm644x_vpss"))
- oper_cfg.platform = DM644X;
- else {
- dev_err(&pdev->dev, "vpss driver not supported on this platform\n");
- return -ENODEV;
- }
-
- dev_info(&pdev->dev, "%s vpss probed\n", platform_name);
- oper_cfg.vpss_regs_base0 = devm_platform_ioremap_resource(pdev, 0);
- if (IS_ERR(oper_cfg.vpss_regs_base0))
- return PTR_ERR(oper_cfg.vpss_regs_base0);
-
- if (oper_cfg.platform == DM355 || oper_cfg.platform == DM365) {
- oper_cfg.vpss_regs_base1 = devm_platform_ioremap_resource(pdev, 1);
- if (IS_ERR(oper_cfg.vpss_regs_base1))
- return PTR_ERR(oper_cfg.vpss_regs_base1);
- }
-
- if (oper_cfg.platform == DM355) {
- oper_cfg.hw_ops.enable_clock = dm355_enable_clock;
- oper_cfg.hw_ops.select_ccdc_source = dm355_select_ccdc_source;
- /* Setup vpss interrupts */
- bl_regw(DM355_VPSSBL_INTSEL_DEFAULT, DM355_VPSSBL_INTSEL);
- bl_regw(DM355_VPSSBL_EVTSEL_DEFAULT, DM355_VPSSBL_EVTSEL);
- } else if (oper_cfg.platform == DM365) {
- oper_cfg.hw_ops.enable_clock = dm365_enable_clock;
- oper_cfg.hw_ops.select_ccdc_source = dm365_select_ccdc_source;
- /* Setup vpss interrupts */
- isp5_write((isp5_read(DM365_ISP5_PCCR) |
- DM365_ISP5_PCCR_BL_CLK_ENABLE |
- DM365_ISP5_PCCR_ISIF_CLK_ENABLE |
- DM365_ISP5_PCCR_H3A_CLK_ENABLE |
- DM365_ISP5_PCCR_RSZ_CLK_ENABLE |
- DM365_ISP5_PCCR_IPIPE_CLK_ENABLE |
- DM365_ISP5_PCCR_IPIPEIF_CLK_ENABLE |
- DM365_ISP5_PCCR_RSV), DM365_ISP5_PCCR);
- isp5_write((isp5_read(DM365_ISP5_BCR) |
- DM365_ISP5_BCR_ISIF_OUT_ENABLE), DM365_ISP5_BCR);
- isp5_write(DM365_ISP5_INTSEL1_DEFAULT, DM365_ISP5_INTSEL1);
- isp5_write(DM365_ISP5_INTSEL2_DEFAULT, DM365_ISP5_INTSEL2);
- isp5_write(DM365_ISP5_INTSEL3_DEFAULT, DM365_ISP5_INTSEL3);
- } else
- oper_cfg.hw_ops.clear_wbl_overflow = dm644x_clear_wbl_overflow;
-
- pm_runtime_enable(&pdev->dev);
-
- pm_runtime_get(&pdev->dev);
-
- spin_lock_init(&oper_cfg.vpss_lock);
- dev_info(&pdev->dev, "%s vpss probe success\n", platform_name);
-
- return 0;
-}
-
-static int vpss_remove(struct platform_device *pdev)
-{
- pm_runtime_disable(&pdev->dev);
- return 0;
-}
-
-static int vpss_suspend(struct device *dev)
-{
- pm_runtime_put(dev);
- return 0;
-}
-
-static int vpss_resume(struct device *dev)
-{
- pm_runtime_get(dev);
- return 0;
-}
-
-static const struct dev_pm_ops vpss_pm_ops = {
- .suspend = vpss_suspend,
- .resume = vpss_resume,
-};
-
-static struct platform_driver vpss_driver = {
- .driver = {
- .name = "vpss",
- .pm = &vpss_pm_ops,
- },
- .remove = vpss_remove,
- .probe = vpss_probe,
-};
-
-static void vpss_exit(void)
-{
- platform_driver_unregister(&vpss_driver);
- iounmap(oper_cfg.vpss_regs_base2);
- release_mem_region(VPSS_CLK_CTRL, 4);
-}
-
-static int __init vpss_init(void)
-{
- int ret;
-
- if (!request_mem_region(VPSS_CLK_CTRL, 4, "vpss_clock_control"))
- return -EBUSY;
-
- oper_cfg.vpss_regs_base2 = ioremap(VPSS_CLK_CTRL, 4);
- if (unlikely(!oper_cfg.vpss_regs_base2)) {
- ret = -ENOMEM;
- goto err_ioremap;
- }
-
- writel(VPSS_CLK_CTRL_VENCCLKEN |
- VPSS_CLK_CTRL_DACCLKEN, oper_cfg.vpss_regs_base2);
-
- ret = platform_driver_register(&vpss_driver);
- if (ret)
- goto err_pd_register;
-
- return 0;
-
-err_pd_register:
- iounmap(oper_cfg.vpss_regs_base2);
-err_ioremap:
- release_mem_region(VPSS_CLK_CTRL, 4);
- return ret;
-}
-subsys_initcall(vpss_init);
-module_exit(vpss_exit);
diff --git a/drivers/media/platform/ti/omap3isp/isp.c b/drivers/media/platform/ti/omap3isp/isp.c
index 1d40bb59ff81..e7327e38482d 100644
--- a/drivers/media/platform/ti/omap3isp/isp.c
+++ b/drivers/media/platform/ti/omap3isp/isp.c
@@ -2307,7 +2307,16 @@ static int isp_probe(struct platform_device *pdev)
/* Regulators */
isp->isp_csiphy1.vdd = devm_regulator_get(&pdev->dev, "vdd-csiphy1");
+ if (IS_ERR(isp->isp_csiphy1.vdd)) {
+ ret = PTR_ERR(isp->isp_csiphy1.vdd);
+ goto error;
+ }
+
isp->isp_csiphy2.vdd = devm_regulator_get(&pdev->dev, "vdd-csiphy2");
+ if (IS_ERR(isp->isp_csiphy2.vdd)) {
+ ret = PTR_ERR(isp->isp_csiphy2.vdd);
+ goto error;
+ }
/* Clocks
*
diff --git a/drivers/media/platform/ti/omap3isp/ispvideo.c b/drivers/media/platform/ti/omap3isp/ispvideo.c
index 3e5348c63773..ddc7d08d4f96 100644
--- a/drivers/media/platform/ti/omap3isp/ispvideo.c
+++ b/drivers/media/platform/ti/omap3isp/ispvideo.c
@@ -221,22 +221,16 @@ isp_video_remote_subdev(struct isp_video *video, u32 *pad)
static int isp_video_get_graph_data(struct isp_video *video,
struct isp_pipeline *pipe)
{
- struct media_graph graph;
- struct media_entity *entity = &video->video.entity;
- struct media_device *mdev = entity->graph_obj.mdev;
+ struct media_pipeline_entity_iter iter;
+ struct media_entity *entity;
struct isp_video *far_end = NULL;
int ret;
- mutex_lock(&mdev->graph_mutex);
- ret = media_graph_walk_init(&graph, mdev);
- if (ret) {
- mutex_unlock(&mdev->graph_mutex);
+ ret = media_pipeline_entity_iter_init(&pipe->pipe, &iter);
+ if (ret)
return ret;
- }
- media_graph_walk_start(&graph, entity);
-
- while ((entity = media_graph_walk_next(&graph))) {
+ media_pipeline_for_each_entity(&pipe->pipe, &iter, entity) {
struct isp_video *__video;
media_entity_enum_set(&pipe->ent_enum, entity);
@@ -255,9 +249,7 @@ static int isp_video_get_graph_data(struct isp_video *video,
far_end = __video;
}
- mutex_unlock(&mdev->graph_mutex);
-
- media_graph_walk_cleanup(&graph);
+ media_pipeline_entity_iter_cleanup(&iter);
if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
pipe->input = far_end;
diff --git a/drivers/media/platform/verisilicon/hantro_drv.c b/drivers/media/platform/verisilicon/hantro_drv.c
index 8cb4a68c9119..b0aeedae7b65 100644
--- a/drivers/media/platform/verisilicon/hantro_drv.c
+++ b/drivers/media/platform/verisilicon/hantro_drv.c
@@ -1050,8 +1050,6 @@ static int hantro_probe(struct platform_device *pdev)
vpu->mdev.dev = vpu->dev;
strscpy(vpu->mdev.model, DRIVER_NAME, sizeof(vpu->mdev.model));
- strscpy(vpu->mdev.bus_info, "platform: " DRIVER_NAME,
- sizeof(vpu->mdev.bus_info));
media_device_init(&vpu->mdev);
vpu->mdev.ops = &hantro_m2m_media_ops;
vpu->v4l2_dev.mdev = &vpu->mdev;
diff --git a/drivers/media/platform/verisilicon/hantro_v4l2.c b/drivers/media/platform/verisilicon/hantro_v4l2.c
index 2c7a805289e7..c0d427956210 100644
--- a/drivers/media/platform/verisilicon/hantro_v4l2.c
+++ b/drivers/media/platform/verisilicon/hantro_v4l2.c
@@ -142,8 +142,6 @@ static int vidioc_querycap(struct file *file, void *priv,
strscpy(cap->driver, vpu->dev->driver->name, sizeof(cap->driver));
strscpy(cap->card, vdev->name, sizeof(cap->card));
- snprintf(cap->bus_info, sizeof(cap->bus_info), "platform: %s",
- vpu->dev->driver->name);
return 0;
}
@@ -161,8 +159,11 @@ static int vidioc_enum_framesizes(struct file *file, void *priv,
}
/* For non-coded formats check if postprocessing scaling is possible */
- if (fmt->codec_mode == HANTRO_MODE_NONE && hantro_needs_postproc(ctx, fmt)) {
- return hanto_postproc_enum_framesizes(ctx, fsize);
+ if (fmt->codec_mode == HANTRO_MODE_NONE) {
+ if (hantro_needs_postproc(ctx, fmt))
+ return hanto_postproc_enum_framesizes(ctx, fsize);
+ else
+ return -ENOTTY;
} else if (fsize->index != 0) {
vpu_debug(0, "invalid frame size index (expected 0, got %d)\n",
fsize->index);
diff --git a/drivers/media/platform/xilinx/xilinx-dma.c b/drivers/media/platform/xilinx/xilinx-dma.c
index 0a7fd8642a65..fee02c8c85fd 100644
--- a/drivers/media/platform/xilinx/xilinx-dma.c
+++ b/drivers/media/platform/xilinx/xilinx-dma.c
@@ -173,31 +173,19 @@ done:
static int xvip_pipeline_validate(struct xvip_pipeline *pipe,
struct xvip_dma *start)
{
- struct media_graph graph;
- struct media_entity *entity = &start->video.entity;
- struct media_device *mdev = entity->graph_obj.mdev;
+ struct media_pipeline_pad_iter iter;
unsigned int num_inputs = 0;
unsigned int num_outputs = 0;
- int ret;
-
- mutex_lock(&mdev->graph_mutex);
-
- /* Walk the graph to locate the video nodes. */
- ret = media_graph_walk_init(&graph, mdev);
- if (ret) {
- mutex_unlock(&mdev->graph_mutex);
- return ret;
- }
-
- media_graph_walk_start(&graph, entity);
+ struct media_pad *pad;
- while ((entity = media_graph_walk_next(&graph))) {
+ /* Locate the video nodes in the pipeline. */
+ media_pipeline_for_each_pad(&pipe->pipe, &iter, pad) {
struct xvip_dma *dma;
- if (entity->function != MEDIA_ENT_F_IO_V4L)
+ if (pad->entity->function != MEDIA_ENT_F_IO_V4L)
continue;
- dma = to_xvip_dma(media_entity_to_video_device(entity));
+ dma = to_xvip_dma(media_entity_to_video_device(pad->entity));
if (dma->pad.flags & MEDIA_PAD_FL_SINK) {
pipe->output = dma;
@@ -207,10 +195,6 @@ static int xvip_pipeline_validate(struct xvip_pipeline *pipe,
}
}
- mutex_unlock(&mdev->graph_mutex);
-
- media_graph_walk_cleanup(&graph);
-
/* We need exactly one output and zero or one input. */
if (num_outputs != 1 || num_inputs > 1)
return -EPIPE;
diff --git a/drivers/media/radio/wl128x/fmdrv_common.c b/drivers/media/radio/wl128x/fmdrv_common.c
index 8a316de70e6c..cbd49dff6d74 100644
--- a/drivers/media/radio/wl128x/fmdrv_common.c
+++ b/drivers/media/radio/wl128x/fmdrv_common.c
@@ -1442,7 +1442,7 @@ static long fm_st_receive(void *arg, struct sk_buff *skb)
{
struct fmdev *fmdev;
- fmdev = (struct fmdev *)arg;
+ fmdev = arg;
if (skb == NULL) {
fmerr("Invalid SKB received from ST\n");
diff --git a/drivers/media/rc/Kconfig b/drivers/media/rc/Kconfig
index f560fc38895f..ac4172feb6f9 100644
--- a/drivers/media/rc/Kconfig
+++ b/drivers/media/rc/Kconfig
@@ -314,7 +314,7 @@ config IR_PWM_TX
tristate "PWM IR transmitter"
depends on LIRC
depends on PWM
- depends on OF || COMPILE_TEST
+ depends on OF
help
Say Y if you want to use a PWM based IR transmitter. This is
more power efficient than the bit banging gpio driver.
@@ -361,7 +361,7 @@ config IR_SERIAL_TRANSMITTER
config IR_SPI
tristate "SPI connected IR LED"
depends on SPI && LIRC
- depends on OF || COMPILE_TEST
+ depends on OF
help
Say Y if you want to use an IR LED connected through SPI bus.
diff --git a/drivers/media/rc/ene_ir.c b/drivers/media/rc/ene_ir.c
index e09270916fbc..11ee21a7db8f 100644
--- a/drivers/media/rc/ene_ir.c
+++ b/drivers/media/rc/ene_ir.c
@@ -1106,6 +1106,8 @@ static void ene_remove(struct pnp_dev *pnp_dev)
struct ene_device *dev = pnp_get_drvdata(pnp_dev);
unsigned long flags;
+ rc_unregister_device(dev->rdev);
+ del_timer_sync(&dev->tx_sim_timer);
spin_lock_irqsave(&dev->hw_lock, flags);
ene_rx_disable(dev);
ene_rx_restore_hw_buffer(dev);
@@ -1113,7 +1115,6 @@ static void ene_remove(struct pnp_dev *pnp_dev)
free_irq(dev->irq, dev);
release_region(dev->hw_io, ENE_IO_SIZE);
- rc_unregister_device(dev->rdev);
kfree(dev);
}
diff --git a/drivers/media/rc/gpio-ir-recv.c b/drivers/media/rc/gpio-ir-recv.c
index 8f1fff7af6c9..8dbe780dae4e 100644
--- a/drivers/media/rc/gpio-ir-recv.c
+++ b/drivers/media/rc/gpio-ir-recv.c
@@ -126,6 +126,23 @@ static int gpio_ir_recv_probe(struct platform_device *pdev)
"gpio-ir-recv-irq", gpio_dev);
}
+static int gpio_ir_recv_remove(struct platform_device *pdev)
+{
+ struct gpio_rc_dev *gpio_dev = platform_get_drvdata(pdev);
+ struct device *pmdev = gpio_dev->pmdev;
+
+ if (pmdev) {
+ pm_runtime_get_sync(pmdev);
+ cpu_latency_qos_remove_request(&gpio_dev->qos);
+
+ pm_runtime_disable(pmdev);
+ pm_runtime_put_noidle(pmdev);
+ pm_runtime_set_suspended(pmdev);
+ }
+
+ return 0;
+}
+
#ifdef CONFIG_PM
static int gpio_ir_recv_suspend(struct device *dev)
{
@@ -185,6 +202,7 @@ MODULE_DEVICE_TABLE(of, gpio_ir_recv_of_match);
static struct platform_driver gpio_ir_recv_driver = {
.probe = gpio_ir_recv_probe,
+ .remove = gpio_ir_recv_remove,
.driver = {
.name = KBUILD_MODNAME,
.of_match_table = of_match_ptr(gpio_ir_recv_of_match),
diff --git a/drivers/media/rc/ir-rx51.c b/drivers/media/rc/ir-rx51.c
index 85080c3d2053..adbbe639a261 100644
--- a/drivers/media/rc/ir-rx51.c
+++ b/drivers/media/rc/ir-rx51.c
@@ -261,11 +261,6 @@ static int ir_rx51_probe(struct platform_device *dev)
return devm_rc_register_device(&dev->dev, ir_rx51.rcdev);
}
-static int ir_rx51_remove(struct platform_device *dev)
-{
- return 0;
-}
-
static const struct of_device_id ir_rx51_match[] = {
{
.compatible = "nokia,n900-ir",
@@ -276,7 +271,6 @@ MODULE_DEVICE_TABLE(of, ir_rx51_match);
static struct platform_driver ir_rx51_platform_driver = {
.probe = ir_rx51_probe,
- .remove = ir_rx51_remove,
.suspend = ir_rx51_suspend,
.resume = ir_rx51_resume,
.driver = {
diff --git a/drivers/media/rc/pwm-ir-tx.c b/drivers/media/rc/pwm-ir-tx.c
index 105a9c24f1e3..7732054c4621 100644
--- a/drivers/media/rc/pwm-ir-tx.c
+++ b/drivers/media/rc/pwm-ir-tx.c
@@ -120,7 +120,7 @@ static struct platform_driver pwm_ir_driver = {
.probe = pwm_ir_probe,
.driver = {
.name = DRIVER_NAME,
- .of_match_table = of_match_ptr(pwm_ir_of_match),
+ .of_match_table = pwm_ir_of_match,
},
};
module_platform_driver(pwm_ir_driver);
diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c
index 527d9324742b..6bdad6341844 100644
--- a/drivers/media/rc/rc-main.c
+++ b/drivers/media/rc/rc-main.c
@@ -1614,7 +1614,7 @@ static void rc_dev_release(struct device *device)
kfree(dev);
}
-static int rc_dev_uevent(struct device *device, struct kobj_uevent_env *env)
+static int rc_dev_uevent(const struct device *device, struct kobj_uevent_env *env)
{
struct rc_dev *dev = to_rc_dev(device);
int ret = 0;
diff --git a/drivers/media/test-drivers/vidtv/vidtv_psi.c b/drivers/media/test-drivers/vidtv/vidtv_psi.c
index a5875380ef40..ce0b7a6e92dc 100644
--- a/drivers/media/test-drivers/vidtv/vidtv_psi.c
+++ b/drivers/media/test-drivers/vidtv/vidtv_psi.c
@@ -1940,7 +1940,7 @@ u32 vidtv_psi_eit_write_into(struct vidtv_psi_eit_write_args *args)
struct vidtv_psi_table_eit_event
*vidtv_psi_eit_event_init(struct vidtv_psi_table_eit_event *head, u16 event_id)
{
- const u8 DURATION[] = {0x23, 0x59, 0x59}; /* BCD encoded */
+ static const u8 DURATION[] = {0x23, 0x59, 0x59}; /* BCD encoded */
struct vidtv_psi_table_eit_event *e;
struct timespec64 ts;
struct tm time;
diff --git a/drivers/media/test-drivers/visl/visl-video.c b/drivers/media/test-drivers/visl/visl-video.c
index b08664dfbe5f..7cac6a6456eb 100644
--- a/drivers/media/test-drivers/visl/visl-video.c
+++ b/drivers/media/test-drivers/visl/visl-video.c
@@ -687,7 +687,7 @@ static void visl_buf_request_complete(struct vb2_buffer *vb)
v4l2_ctrl_request_complete(vb->req_obj.req, &ctx->hdl);
}
-const struct vb2_ops visl_qops = {
+static const struct vb2_ops visl_qops = {
.queue_setup = visl_queue_setup,
.buf_out_validate = visl_buf_out_validate,
.buf_prepare = visl_buf_prepare,
diff --git a/drivers/media/tuners/si2157.c b/drivers/media/tuners/si2157.c
index 476b32c04c20..3fa3dcda917a 100644
--- a/drivers/media/tuners/si2157.c
+++ b/drivers/media/tuners/si2157.c
@@ -875,9 +875,9 @@ err:
dev_dbg(&client->dev, "failed=%d\n", ret);
}
-static int si2157_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int si2157_probe(struct i2c_client *client)
{
+ const struct i2c_device_id *id = i2c_client_get_device_id(client);
struct si2157_config *cfg = client->dev.platform_data;
struct dvb_frontend *fe = cfg->fe;
struct si2157_dev *dev;
@@ -990,7 +990,7 @@ static struct i2c_driver si2157_driver = {
.name = "si2157",
.suppress_bind_attrs = true,
},
- .probe = si2157_probe,
+ .probe_new = si2157_probe,
.remove = si2157_remove,
.id_table = si2157_id_table,
};
diff --git a/drivers/media/usb/dvb-usb-v2/af9015.c b/drivers/media/usb/dvb-usb-v2/af9015.c
index d33514acc2b5..4014f7d07330 100644
--- a/drivers/media/usb/dvb-usb-v2/af9015.c
+++ b/drivers/media/usb/dvb-usb-v2/af9015.c
@@ -1165,7 +1165,7 @@ static int af9015_rc_query(struct dvb_usb_device *d)
/* If any of these are non-zero, assume invalid data */
if (buf[1] || buf[2] || buf[3]) {
dev_dbg(&intf->dev, "invalid data\n");
- return ret;
+ return 0;
}
/* Check for repeat of previous code */
@@ -1174,7 +1174,7 @@ static int af9015_rc_query(struct dvb_usb_device *d)
dev_dbg(&intf->dev, "key repeated\n");
rc_repeat(d->rc_dev);
state->rc_repeat = buf[6];
- return ret;
+ return 0;
}
/* Only process key if canary killed */
diff --git a/drivers/media/usb/go7007/go7007-v4l2.c b/drivers/media/usb/go7007/go7007-v4l2.c
index b2edc4deaca3..13256565b034 100644
--- a/drivers/media/usb/go7007/go7007-v4l2.c
+++ b/drivers/media/usb/go7007/go7007-v4l2.c
@@ -404,16 +404,13 @@ static int go7007_start_streaming(struct vb2_queue *q, unsigned int count)
go->next_seq = 0;
go->active_buf = NULL;
go->modet_event_status = 0;
- q->streaming = 1;
if (go7007_start_encoder(go) < 0)
ret = -EIO;
else
ret = 0;
mutex_unlock(&go->hw_lock);
- if (ret) {
- q->streaming = 0;
+ if (ret)
return ret;
- }
call_all(&go->v4l2_dev, video, s_stream, 1);
v4l2_ctrl_grab(go->mpeg_video_gop_size, true);
v4l2_ctrl_grab(go->mpeg_video_gop_closure, true);
@@ -430,7 +427,6 @@ static void go7007_stop_streaming(struct vb2_queue *q)
struct go7007 *go = vb2_get_drv_priv(q);
unsigned long flags;
- q->streaming = 0;
go7007_stream_stop(go);
mutex_lock(&go->hw_lock);
go7007_reset_encoder(go);
diff --git a/drivers/media/usb/siano/smsusb.c b/drivers/media/usb/siano/smsusb.c
index fe9c7b3a950e..6f443c542c6d 100644
--- a/drivers/media/usb/siano/smsusb.c
+++ b/drivers/media/usb/siano/smsusb.c
@@ -179,6 +179,7 @@ static void smsusb_stop_streaming(struct smsusb_device_t *dev)
for (i = 0; i < MAX_URBS; i++) {
usb_kill_urb(&dev->surbs[i].urb);
+ cancel_work_sync(&dev->surbs[i].wq);
if (dev->surbs[i].cb) {
smscore_putbuffer(dev->coredev, dev->surbs[i].cb);
diff --git a/drivers/media/usb/uvc/Kconfig b/drivers/media/usb/uvc/Kconfig
index ca51ee8e45f3..579532272fd6 100644
--- a/drivers/media/usb/uvc/Kconfig
+++ b/drivers/media/usb/uvc/Kconfig
@@ -3,6 +3,7 @@ config USB_VIDEO_CLASS
tristate "USB Video Class (UVC)"
depends on VIDEO_DEV
select VIDEOBUF2_VMALLOC
+ select UVC_COMMON
help
Support for the USB Video Class (UVC). Currently only video
input devices, such as webcams, are supported.
diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
index c95a2229f4fa..5e9d3da862dd 100644
--- a/drivers/media/usb/uvc/uvc_ctrl.c
+++ b/drivers/media/usb/uvc/uvc_ctrl.c
@@ -6,19 +6,21 @@
* Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*/
+#include <asm/barrier.h>
+#include <linux/bitops.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/usb.h>
+#include <linux/usb/uvc.h>
#include <linux/videodev2.h>
#include <linux/vmalloc.h>
#include <linux/wait.h>
#include <linux/workqueue.h>
#include <linux/atomic.h>
#include <media/v4l2-ctrls.h>
-#include <media/v4l2-uvc.h>
#include "uvcvideo.h"
@@ -363,19 +365,45 @@ static const u32 uvc_control_classes[] = {
V4L2_CID_USER_CLASS,
};
-static const struct uvc_menu_info power_line_frequency_controls[] = {
- { 0, "Disabled" },
- { 1, "50 Hz" },
- { 2, "60 Hz" },
- { 3, "Auto" },
-};
+static const int exposure_auto_mapping[] = { 2, 1, 4, 8 };
-static const struct uvc_menu_info exposure_auto_controls[] = {
- { 2, "Auto Mode" },
- { 1, "Manual Mode" },
- { 4, "Shutter Priority Mode" },
- { 8, "Aperture Priority Mode" },
-};
+/*
+ * This function translates the V4L2 menu index @idx, as exposed to userspace as
+ * the V4L2 control value, to the corresponding UVC control value used by the
+ * device. The custom menu_mapping in the control @mapping is used when
+ * available, otherwise the function assumes that the V4L2 and UVC values are
+ * identical.
+ *
+ * For controls of type UVC_CTRL_DATA_TYPE_BITMASK, the UVC control value is
+ * expressed as a bitmask and is thus guaranteed to have a single bit set.
+ *
+ * The function returns -EINVAL if the V4L2 menu index @idx isn't valid for the
+ * control, which includes all controls whose type isn't UVC_CTRL_DATA_TYPE_ENUM
+ * or UVC_CTRL_DATA_TYPE_BITMASK.
+ */
+static int uvc_mapping_get_menu_value(const struct uvc_control_mapping *mapping,
+ u32 idx)
+{
+ if (!test_bit(idx, &mapping->menu_mask))
+ return -EINVAL;
+
+ if (mapping->menu_mapping)
+ return mapping->menu_mapping[idx];
+
+ return idx;
+}
+
+static const char *
+uvc_mapping_get_menu_name(const struct uvc_control_mapping *mapping, u32 idx)
+{
+ if (!test_bit(idx, &mapping->menu_mask))
+ return NULL;
+
+ if (mapping->menu_names)
+ return mapping->menu_names[idx];
+
+ return v4l2_ctrl_get_menu(mapping->id)[idx];
+}
static s32 uvc_ctrl_get_zoom(struct uvc_control_mapping *mapping,
u8 query, const u8 *data)
@@ -524,8 +552,9 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
.offset = 0,
.v4l2_type = V4L2_CTRL_TYPE_MENU,
.data_type = UVC_CTRL_DATA_TYPE_BITMASK,
- .menu_info = exposure_auto_controls,
- .menu_count = ARRAY_SIZE(exposure_auto_controls),
+ .menu_mapping = exposure_auto_mapping,
+ .menu_mask = GENMASK(V4L2_EXPOSURE_APERTURE_PRIORITY,
+ V4L2_EXPOSURE_AUTO),
.slave_ids = { V4L2_CID_EXPOSURE_ABSOLUTE, },
},
{
@@ -721,32 +750,50 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
},
};
-static const struct uvc_control_mapping uvc_ctrl_mappings_uvc11[] = {
- {
- .id = V4L2_CID_POWER_LINE_FREQUENCY,
- .entity = UVC_GUID_UVC_PROCESSING,
- .selector = UVC_PU_POWER_LINE_FREQUENCY_CONTROL,
- .size = 2,
- .offset = 0,
- .v4l2_type = V4L2_CTRL_TYPE_MENU,
- .data_type = UVC_CTRL_DATA_TYPE_ENUM,
- .menu_info = power_line_frequency_controls,
- .menu_count = ARRAY_SIZE(power_line_frequency_controls) - 1,
- },
+const struct uvc_control_mapping uvc_ctrl_power_line_mapping_limited = {
+ .id = V4L2_CID_POWER_LINE_FREQUENCY,
+ .entity = UVC_GUID_UVC_PROCESSING,
+ .selector = UVC_PU_POWER_LINE_FREQUENCY_CONTROL,
+ .size = 2,
+ .offset = 0,
+ .v4l2_type = V4L2_CTRL_TYPE_MENU,
+ .data_type = UVC_CTRL_DATA_TYPE_ENUM,
+ .menu_mask = GENMASK(V4L2_CID_POWER_LINE_FREQUENCY_60HZ,
+ V4L2_CID_POWER_LINE_FREQUENCY_50HZ),
};
-static const struct uvc_control_mapping uvc_ctrl_mappings_uvc15[] = {
- {
- .id = V4L2_CID_POWER_LINE_FREQUENCY,
- .entity = UVC_GUID_UVC_PROCESSING,
- .selector = UVC_PU_POWER_LINE_FREQUENCY_CONTROL,
- .size = 2,
- .offset = 0,
- .v4l2_type = V4L2_CTRL_TYPE_MENU,
- .data_type = UVC_CTRL_DATA_TYPE_ENUM,
- .menu_info = power_line_frequency_controls,
- .menu_count = ARRAY_SIZE(power_line_frequency_controls),
- },
+const struct uvc_control_mapping uvc_ctrl_power_line_mapping_uvc11 = {
+ .id = V4L2_CID_POWER_LINE_FREQUENCY,
+ .entity = UVC_GUID_UVC_PROCESSING,
+ .selector = UVC_PU_POWER_LINE_FREQUENCY_CONTROL,
+ .size = 2,
+ .offset = 0,
+ .v4l2_type = V4L2_CTRL_TYPE_MENU,
+ .data_type = UVC_CTRL_DATA_TYPE_ENUM,
+ .menu_mask = GENMASK(V4L2_CID_POWER_LINE_FREQUENCY_60HZ,
+ V4L2_CID_POWER_LINE_FREQUENCY_DISABLED),
+};
+
+static const struct uvc_control_mapping *uvc_ctrl_mappings_uvc11[] = {
+ &uvc_ctrl_power_line_mapping_uvc11,
+ NULL, /* Sentinel */
+};
+
+static const struct uvc_control_mapping uvc_ctrl_power_line_mapping_uvc15 = {
+ .id = V4L2_CID_POWER_LINE_FREQUENCY,
+ .entity = UVC_GUID_UVC_PROCESSING,
+ .selector = UVC_PU_POWER_LINE_FREQUENCY_CONTROL,
+ .size = 2,
+ .offset = 0,
+ .v4l2_type = V4L2_CTRL_TYPE_MENU,
+ .data_type = UVC_CTRL_DATA_TYPE_ENUM,
+ .menu_mask = GENMASK(V4L2_CID_POWER_LINE_FREQUENCY_AUTO,
+ V4L2_CID_POWER_LINE_FREQUENCY_DISABLED),
+};
+
+static const struct uvc_control_mapping *uvc_ctrl_mappings_uvc15[] = {
+ &uvc_ctrl_power_line_mapping_uvc15,
+ NULL, /* Sentinel */
};
/* ------------------------------------------------------------------------
@@ -972,11 +1019,17 @@ static s32 __uvc_ctrl_get_value(struct uvc_control_mapping *mapping,
s32 value = mapping->get(mapping, UVC_GET_CUR, data);
if (mapping->v4l2_type == V4L2_CTRL_TYPE_MENU) {
- const struct uvc_menu_info *menu = mapping->menu_info;
unsigned int i;
- for (i = 0; i < mapping->menu_count; ++i, ++menu) {
- if (menu->value == value) {
+ for (i = 0; BIT(i) <= mapping->menu_mask; ++i) {
+ u32 menu_value;
+
+ if (!test_bit(i, &mapping->menu_mask))
+ continue;
+
+ menu_value = uvc_mapping_get_menu_value(mapping, i);
+
+ if (menu_value == value) {
value = i;
break;
}
@@ -1085,11 +1138,28 @@ static int uvc_query_v4l2_class(struct uvc_video_chain *chain, u32 req_id,
return 0;
}
+/*
+ * Check if control @v4l2_id can be accessed by the given control @ioctl
+ * (VIDIOC_G_EXT_CTRLS, VIDIOC_TRY_EXT_CTRLS or VIDIOC_S_EXT_CTRLS).
+ *
+ * For set operations on slave controls, check if the master's value is set to
+ * manual, either in the others controls set in the same ioctl call, or from
+ * the master's current value. This catches VIDIOC_S_EXT_CTRLS calls that set
+ * both the master and slave control, such as for instance setting
+ * auto_exposure=1, exposure_time_absolute=251.
+ */
int uvc_ctrl_is_accessible(struct uvc_video_chain *chain, u32 v4l2_id,
- bool read)
+ const struct v4l2_ext_controls *ctrls,
+ unsigned long ioctl)
{
+ struct uvc_control_mapping *master_map = NULL;
+ struct uvc_control *master_ctrl = NULL;
struct uvc_control_mapping *mapping;
struct uvc_control *ctrl;
+ bool read = ioctl == VIDIOC_G_EXT_CTRLS;
+ s32 val;
+ int ret;
+ int i;
if (__uvc_query_v4l2_class(chain, v4l2_id, 0) >= 0)
return -EACCES;
@@ -1104,6 +1174,29 @@ int uvc_ctrl_is_accessible(struct uvc_video_chain *chain, u32 v4l2_id,
if (!(ctrl->info.flags & UVC_CTRL_FLAG_SET_CUR) && !read)
return -EACCES;
+ if (ioctl != VIDIOC_S_EXT_CTRLS || !mapping->master_id)
+ return 0;
+
+ /*
+ * Iterate backwards in cases where the master control is accessed
+ * multiple times in the same ioctl. We want the last value.
+ */
+ for (i = ctrls->count - 1; i >= 0; i--) {
+ if (ctrls->controls[i].id == mapping->master_id)
+ return ctrls->controls[i].value ==
+ mapping->master_manual ? 0 : -EACCES;
+ }
+
+ __uvc_find_control(ctrl->entity, mapping->master_id, &master_map,
+ &master_ctrl, 0);
+
+ if (!master_ctrl || !(master_ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR))
+ return 0;
+
+ ret = __uvc_ctrl_get(chain, master_ctrl, master_map, &val);
+ if (ret >= 0 && val != mapping->master_manual)
+ return -EACCES;
+
return 0;
}
@@ -1121,6 +1214,25 @@ static const char *uvc_map_get_name(const struct uvc_control_mapping *map)
return "Unknown Control";
}
+static u32 uvc_get_ctrl_bitmap(struct uvc_control *ctrl,
+ struct uvc_control_mapping *mapping)
+{
+ /*
+ * Some controls, like CT_AE_MODE_CONTROL, use GET_RES to represent
+ * the number of bits supported. Those controls do not list GET_MAX
+ * as supported.
+ */
+ if (ctrl->info.flags & UVC_CTRL_FLAG_GET_RES)
+ return mapping->get(mapping, UVC_GET_RES,
+ uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES));
+
+ if (ctrl->info.flags & UVC_CTRL_FLAG_GET_MAX)
+ return mapping->get(mapping, UVC_GET_MAX,
+ uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MAX));
+
+ return ~0;
+}
+
static int __uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
struct uvc_control *ctrl,
struct uvc_control_mapping *mapping,
@@ -1128,7 +1240,6 @@ static int __uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
{
struct uvc_control_mapping *master_map = NULL;
struct uvc_control *master_ctrl = NULL;
- const struct uvc_menu_info *menu;
unsigned int i;
memset(v4l2_ctrl, 0, sizeof(*v4l2_ctrl));
@@ -1169,13 +1280,19 @@ static int __uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
switch (mapping->v4l2_type) {
case V4L2_CTRL_TYPE_MENU:
- v4l2_ctrl->minimum = 0;
- v4l2_ctrl->maximum = mapping->menu_count - 1;
+ v4l2_ctrl->minimum = ffs(mapping->menu_mask) - 1;
+ v4l2_ctrl->maximum = fls(mapping->menu_mask) - 1;
v4l2_ctrl->step = 1;
- menu = mapping->menu_info;
- for (i = 0; i < mapping->menu_count; ++i, ++menu) {
- if (menu->value == v4l2_ctrl->default_value) {
+ for (i = 0; BIT(i) <= mapping->menu_mask; ++i) {
+ u32 menu_value;
+
+ if (!test_bit(i, &mapping->menu_mask))
+ continue;
+
+ menu_value = uvc_mapping_get_menu_value(mapping, i);
+
+ if (menu_value == v4l2_ctrl->default_value) {
v4l2_ctrl->default_value = i;
break;
}
@@ -1195,6 +1312,12 @@ static int __uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
v4l2_ctrl->step = 0;
return 0;
+ case V4L2_CTRL_TYPE_BITMASK:
+ v4l2_ctrl->minimum = 0;
+ v4l2_ctrl->maximum = uvc_get_ctrl_bitmap(ctrl, mapping);
+ v4l2_ctrl->step = 0;
+ return 0;
+
default:
break;
}
@@ -1268,11 +1391,11 @@ done:
int uvc_query_v4l2_menu(struct uvc_video_chain *chain,
struct v4l2_querymenu *query_menu)
{
- const struct uvc_menu_info *menu_info;
struct uvc_control_mapping *mapping;
struct uvc_control *ctrl;
u32 index = query_menu->index;
u32 id = query_menu->id;
+ const char *name;
int ret;
memset(query_menu, 0, sizeof(*query_menu));
@@ -1289,16 +1412,13 @@ int uvc_query_v4l2_menu(struct uvc_video_chain *chain,
goto done;
}
- if (query_menu->index >= mapping->menu_count) {
+ if (!test_bit(query_menu->index, &mapping->menu_mask)) {
ret = -EINVAL;
goto done;
}
- menu_info = &mapping->menu_info[query_menu->index];
-
- if (mapping->data_type == UVC_CTRL_DATA_TYPE_BITMASK &&
- (ctrl->info.flags & UVC_CTRL_FLAG_GET_RES)) {
- s32 bitmap;
+ if (mapping->data_type == UVC_CTRL_DATA_TYPE_BITMASK) {
+ int mask;
if (!ctrl->cached) {
ret = uvc_ctrl_populate_cache(chain, ctrl);
@@ -1306,15 +1426,25 @@ int uvc_query_v4l2_menu(struct uvc_video_chain *chain,
goto done;
}
- bitmap = mapping->get(mapping, UVC_GET_RES,
- uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES));
- if (!(bitmap & menu_info->value)) {
+ mask = uvc_mapping_get_menu_value(mapping, query_menu->index);
+ if (mask < 0) {
+ ret = mask;
+ goto done;
+ }
+
+ if (!(uvc_get_ctrl_bitmap(ctrl, mapping) & mask)) {
ret = -EINVAL;
goto done;
}
}
- strscpy(query_menu->name, menu_info->name, sizeof(query_menu->name));
+ name = uvc_mapping_get_menu_name(mapping, query_menu->index);
+ if (!name) {
+ ret = -EINVAL;
+ goto done;
+ }
+
+ strscpy(query_menu->name, name, sizeof(query_menu->name));
done:
mutex_unlock(&chain->ctrl_mutex);
@@ -1442,6 +1572,10 @@ static void uvc_ctrl_status_event_work(struct work_struct *work)
uvc_ctrl_status_event(w->chain, w->ctrl, w->data);
+ /* The barrier is needed to synchronize with uvc_status_stop(). */
+ if (smp_load_acquire(&dev->flush_status))
+ return;
+
/* Resubmit the URB. */
w->urb->interval = dev->int_ep->desc.bInterval;
ret = usb_submit_urb(w->urb, GFP_KERNEL);
@@ -1791,31 +1925,44 @@ int uvc_ctrl_set(struct uvc_fh *handle,
value = xctrl->value;
break;
+ case V4L2_CTRL_TYPE_BITMASK:
+ if (!ctrl->cached) {
+ ret = uvc_ctrl_populate_cache(chain, ctrl);
+ if (ret < 0)
+ return ret;
+ }
+
+ xctrl->value &= uvc_get_ctrl_bitmap(ctrl, mapping);
+ value = xctrl->value;
+ break;
+
case V4L2_CTRL_TYPE_BOOLEAN:
xctrl->value = clamp(xctrl->value, 0, 1);
value = xctrl->value;
break;
case V4L2_CTRL_TYPE_MENU:
- if (xctrl->value < 0 || xctrl->value >= mapping->menu_count)
+ if (xctrl->value < (ffs(mapping->menu_mask) - 1) ||
+ xctrl->value > (fls(mapping->menu_mask) - 1))
return -ERANGE;
- value = mapping->menu_info[xctrl->value].value;
+
+ if (!test_bit(xctrl->value, &mapping->menu_mask))
+ return -EINVAL;
+
+ value = uvc_mapping_get_menu_value(mapping, xctrl->value);
/*
* Valid menu indices are reported by the GET_RES request for
* UVC controls that support it.
*/
- if (mapping->data_type == UVC_CTRL_DATA_TYPE_BITMASK &&
- (ctrl->info.flags & UVC_CTRL_FLAG_GET_RES)) {
+ if (mapping->data_type == UVC_CTRL_DATA_TYPE_BITMASK) {
if (!ctrl->cached) {
ret = uvc_ctrl_populate_cache(chain, ctrl);
if (ret < 0)
return ret;
}
- step = mapping->get(mapping, UVC_GET_RES,
- uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES));
- if (!(step & value))
+ if (!(uvc_get_ctrl_bitmap(ctrl, mapping) & value))
return -EINVAL;
}
@@ -2218,31 +2365,42 @@ static int __uvc_ctrl_add_mapping(struct uvc_video_chain *chain,
unsigned int i;
/*
- * Most mappings come from static kernel data and need to be duplicated.
+ * Most mappings come from static kernel data, and need to be duplicated.
* Mappings that come from userspace will be unnecessarily duplicated,
* this could be optimized.
*/
map = kmemdup(mapping, sizeof(*mapping), GFP_KERNEL);
- if (map == NULL)
+ if (!map)
return -ENOMEM;
+ map->name = NULL;
+ map->menu_names = NULL;
+ map->menu_mapping = NULL;
+
/* For UVCIOC_CTRL_MAP custom control */
if (mapping->name) {
map->name = kstrdup(mapping->name, GFP_KERNEL);
- if (!map->name) {
- kfree(map);
- return -ENOMEM;
- }
+ if (!map->name)
+ goto err_nomem;
}
INIT_LIST_HEAD(&map->ev_subs);
- size = sizeof(*mapping->menu_info) * mapping->menu_count;
- map->menu_info = kmemdup(mapping->menu_info, size, GFP_KERNEL);
- if (map->menu_info == NULL) {
- kfree(map->name);
- kfree(map);
- return -ENOMEM;
+ if (mapping->menu_mapping && mapping->menu_mask) {
+ size = sizeof(mapping->menu_mapping[0])
+ * fls(mapping->menu_mask);
+ map->menu_mapping = kmemdup(mapping->menu_mapping, size,
+ GFP_KERNEL);
+ if (!map->menu_mapping)
+ goto err_nomem;
+ }
+ if (mapping->menu_names && mapping->menu_mask) {
+ size = sizeof(mapping->menu_names[0])
+ * fls(mapping->menu_mask);
+ map->menu_names = kmemdup(mapping->menu_names, size,
+ GFP_KERNEL);
+ if (!map->menu_names)
+ goto err_nomem;
}
if (map->get == NULL)
@@ -2264,6 +2422,13 @@ static int __uvc_ctrl_add_mapping(struct uvc_video_chain *chain,
ctrl->info.selector);
return 0;
+
+err_nomem:
+ kfree(map->menu_names);
+ kfree(map->menu_mapping);
+ kfree(map->name);
+ kfree(map);
+ return -ENOMEM;
}
int uvc_ctrl_add_mapping(struct uvc_video_chain *chain,
@@ -2421,8 +2586,7 @@ static void uvc_ctrl_prune_entity(struct uvc_device *dev,
static void uvc_ctrl_init_ctrl(struct uvc_video_chain *chain,
struct uvc_control *ctrl)
{
- const struct uvc_control_mapping *mappings;
- unsigned int num_mappings;
+ const struct uvc_control_mapping **mappings;
unsigned int i;
/*
@@ -2489,16 +2653,11 @@ static void uvc_ctrl_init_ctrl(struct uvc_video_chain *chain,
}
/* Finally process version-specific mappings. */
- if (chain->dev->uvc_version < 0x0150) {
- mappings = uvc_ctrl_mappings_uvc11;
- num_mappings = ARRAY_SIZE(uvc_ctrl_mappings_uvc11);
- } else {
- mappings = uvc_ctrl_mappings_uvc15;
- num_mappings = ARRAY_SIZE(uvc_ctrl_mappings_uvc15);
- }
+ mappings = chain->dev->uvc_version < 0x0150
+ ? uvc_ctrl_mappings_uvc11 : uvc_ctrl_mappings_uvc15;
- for (i = 0; i < num_mappings; ++i) {
- const struct uvc_control_mapping *mapping = &mappings[i];
+ for (i = 0; mappings[i]; ++i) {
+ const struct uvc_control_mapping *mapping = mappings[i];
if (uvc_entity_match_guid(ctrl->entity, mapping->entity) &&
ctrl->info.selector == mapping->selector)
@@ -2591,7 +2750,8 @@ static void uvc_ctrl_cleanup_mappings(struct uvc_device *dev,
list_for_each_entry_safe(mapping, nm, &ctrl->info.mappings, list) {
list_del(&mapping->list);
- kfree(mapping->menu_info);
+ kfree(mapping->menu_names);
+ kfree(mapping->menu_mapping);
kfree(mapping->name);
kfree(mapping);
}
diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c
index e4bcb5011360..7aefa76a42b3 100644
--- a/drivers/media/usb/uvc/uvc_driver.c
+++ b/drivers/media/usb/uvc/uvc_driver.c
@@ -7,12 +7,14 @@
*/
#include <linux/atomic.h>
+#include <linux/bits.h>
#include <linux/gpio/consumer.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/usb.h>
+#include <linux/usb/uvc.h>
#include <linux/videodev2.h>
#include <linux/vmalloc.h>
#include <linux/wait.h>
@@ -20,7 +22,6 @@
#include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h>
-#include <media/v4l2-uvc.h>
#include "uvcvideo.h"
@@ -224,7 +225,7 @@ static int uvc_parse_format(struct uvc_device *dev,
{
struct usb_interface *intf = streaming->intf;
struct usb_host_interface *alts = intf->cur_altsetting;
- struct uvc_format_desc *fmtdesc;
+ const struct uvc_format_desc *fmtdesc;
struct uvc_frame *frame;
const unsigned char *start = buffer;
unsigned int width_multiplier = 1;
@@ -251,14 +252,10 @@ static int uvc_parse_format(struct uvc_device *dev,
fmtdesc = uvc_format_by_guid(&buffer[5]);
if (fmtdesc != NULL) {
- strscpy(format->name, fmtdesc->name,
- sizeof(format->name));
format->fcc = fmtdesc->fcc;
} else {
dev_info(&streaming->intf->dev,
"Unknown video format %pUl\n", &buffer[5]);
- snprintf(format->name, sizeof(format->name), "%pUl\n",
- &buffer[5]);
format->fcc = 0;
}
@@ -270,8 +267,6 @@ static int uvc_parse_format(struct uvc_device *dev,
*/
if (dev->quirks & UVC_QUIRK_FORCE_Y8) {
if (format->fcc == V4L2_PIX_FMT_YUYV) {
- strscpy(format->name, "Greyscale 8-bit (Y8 )",
- sizeof(format->name));
format->fcc = V4L2_PIX_FMT_GREY;
format->bpp = 8;
width_multiplier = 2;
@@ -312,7 +307,6 @@ static int uvc_parse_format(struct uvc_device *dev,
return -EINVAL;
}
- strscpy(format->name, "MJPEG", sizeof(format->name));
format->fcc = V4L2_PIX_FMT_MJPEG;
format->flags = UVC_FMT_FLAG_COMPRESSED;
format->bpp = 0;
@@ -328,17 +322,7 @@ static int uvc_parse_format(struct uvc_device *dev,
return -EINVAL;
}
- switch (buffer[8] & 0x7f) {
- case 0:
- strscpy(format->name, "SD-DV", sizeof(format->name));
- break;
- case 1:
- strscpy(format->name, "SDL-DV", sizeof(format->name));
- break;
- case 2:
- strscpy(format->name, "HD-DV", sizeof(format->name));
- break;
- default:
+ if ((buffer[8] & 0x7f) > 2) {
uvc_dbg(dev, DESCR,
"device %d videostreaming interface %d: unknown DV format %u\n",
dev->udev->devnum,
@@ -346,9 +330,6 @@ static int uvc_parse_format(struct uvc_device *dev,
return -EINVAL;
}
- strlcat(format->name, buffer[8] & (1 << 7) ? " 60Hz" : " 50Hz",
- sizeof(format->name));
-
format->fcc = V4L2_PIX_FMT_DV;
format->flags = UVC_FMT_FLAG_COMPRESSED | UVC_FMT_FLAG_STREAM;
format->bpp = 0;
@@ -375,7 +356,7 @@ static int uvc_parse_format(struct uvc_device *dev,
return -EINVAL;
}
- uvc_dbg(dev, DESCR, "Found format %s\n", format->name);
+ uvc_dbg(dev, DESCR, "Found format %p4cc", &format->fcc);
buflen -= buffer[0];
buffer += buffer[0];
@@ -732,6 +713,7 @@ static int uvc_parse_streaming(struct uvc_device *dev,
/* Parse the alternate settings to find the maximum bandwidth. */
for (i = 0; i < intf->num_altsetting; ++i) {
struct usb_host_endpoint *ep;
+
alts = &intf->altsetting[i];
ep = uvc_find_endpoint(alts,
streaming->header.bEndpointAddress);
@@ -813,6 +795,27 @@ static struct uvc_entity *uvc_alloc_entity(u16 type, u16 id,
return entity;
}
+static void uvc_entity_set_name(struct uvc_device *dev, struct uvc_entity *entity,
+ const char *type_name, u8 string_id)
+{
+ int ret;
+
+ /*
+ * First attempt to read the entity name from the device. If the entity
+ * has no associated string, or if reading the string fails (most
+ * likely due to a buggy firmware), fall back to default names based on
+ * the entity type.
+ */
+ if (string_id) {
+ ret = usb_string(dev->udev, string_id, entity->name,
+ sizeof(entity->name));
+ if (!ret)
+ return;
+ }
+
+ sprintf(entity->name, "%s %u", type_name, entity->id);
+}
+
/* Parse vendor-specific extensions. */
static int uvc_parse_vendor_control(struct uvc_device *dev,
const unsigned char *buffer, int buflen)
@@ -879,11 +882,7 @@ static int uvc_parse_vendor_control(struct uvc_device *dev,
+ n;
memcpy(unit->extension.bmControls, &buffer[23+p], 2*n);
- if (buffer[24+p+2*n] != 0)
- usb_string(udev, buffer[24+p+2*n], unit->name,
- sizeof(unit->name));
- else
- sprintf(unit->name, "Extension %u", buffer[3]);
+ uvc_entity_set_name(dev, unit, "Extension", buffer[24+p+2*n]);
list_add_tail(&unit->list, &dev->entities);
handled = 1;
@@ -901,6 +900,7 @@ static int uvc_parse_standard_control(struct uvc_device *dev,
struct usb_interface *intf;
struct usb_host_interface *alts = dev->intf->cur_altsetting;
unsigned int i, n, p, len;
+ const char *type_name;
u16 type;
switch (buffer[2]) {
@@ -1006,15 +1006,14 @@ static int uvc_parse_standard_control(struct uvc_device *dev,
memcpy(term->media.bmTransportModes, &buffer[10+n], p);
}
- if (buffer[7] != 0)
- usb_string(udev, buffer[7], term->name,
- sizeof(term->name));
- else if (UVC_ENTITY_TYPE(term) == UVC_ITT_CAMERA)
- sprintf(term->name, "Camera %u", buffer[3]);
+ if (UVC_ENTITY_TYPE(term) == UVC_ITT_CAMERA)
+ type_name = "Camera";
else if (UVC_ENTITY_TYPE(term) == UVC_ITT_MEDIA_TRANSPORT_INPUT)
- sprintf(term->name, "Media %u", buffer[3]);
+ type_name = "Media";
else
- sprintf(term->name, "Input %u", buffer[3]);
+ type_name = "Input";
+
+ uvc_entity_set_name(dev, term, type_name, buffer[7]);
list_add_tail(&term->list, &dev->entities);
break;
@@ -1047,11 +1046,7 @@ static int uvc_parse_standard_control(struct uvc_device *dev,
memcpy(term->baSourceID, &buffer[7], 1);
- if (buffer[8] != 0)
- usb_string(udev, buffer[8], term->name,
- sizeof(term->name));
- else
- sprintf(term->name, "Output %u", buffer[3]);
+ uvc_entity_set_name(dev, term, "Output", buffer[8]);
list_add_tail(&term->list, &dev->entities);
break;
@@ -1072,11 +1067,7 @@ static int uvc_parse_standard_control(struct uvc_device *dev,
memcpy(unit->baSourceID, &buffer[5], p);
- if (buffer[5+p] != 0)
- usb_string(udev, buffer[5+p], unit->name,
- sizeof(unit->name));
- else
- sprintf(unit->name, "Selector %u", buffer[3]);
+ uvc_entity_set_name(dev, unit, "Selector", buffer[5+p]);
list_add_tail(&unit->list, &dev->entities);
break;
@@ -1105,11 +1096,7 @@ static int uvc_parse_standard_control(struct uvc_device *dev,
if (dev->uvc_version >= 0x0110)
unit->processing.bmVideoStandards = buffer[9+n];
- if (buffer[8+n] != 0)
- usb_string(udev, buffer[8+n], unit->name,
- sizeof(unit->name));
- else
- sprintf(unit->name, "Processing %u", buffer[3]);
+ uvc_entity_set_name(dev, unit, "Processing", buffer[8+n]);
list_add_tail(&unit->list, &dev->entities);
break;
@@ -1136,11 +1123,7 @@ static int uvc_parse_standard_control(struct uvc_device *dev,
unit->extension.bmControls = (u8 *)unit + sizeof(*unit);
memcpy(unit->extension.bmControls, &buffer[23+p], n);
- if (buffer[23+p+n] != 0)
- usb_string(udev, buffer[23+p+n], unit->name,
- sizeof(unit->name));
- else
- sprintf(unit->name, "Extension %u", buffer[3]);
+ uvc_entity_set_name(dev, unit, "Extension", buffer[23+p+n]);
list_add_tail(&unit->list, &dev->entities);
break;
@@ -1173,7 +1156,8 @@ static int uvc_parse_control(struct uvc_device *dev)
buffer[1] != USB_DT_CS_INTERFACE)
goto next_descriptor;
- if ((ret = uvc_parse_standard_control(dev, buffer, buflen)) < 0)
+ ret = uvc_parse_standard_control(dev, buffer, buflen);
+ if (ret < 0)
return ret;
next_descriptor:
@@ -1856,12 +1840,14 @@ static void uvc_delete(struct kref *kref)
list_for_each_safe(p, n, &dev->chains) {
struct uvc_video_chain *chain;
+
chain = list_entry(p, struct uvc_video_chain, list);
kfree(chain);
}
list_for_each_safe(p, n, &dev->entities) {
struct uvc_entity *entity;
+
entity = list_entry(p, struct uvc_entity, list);
#ifdef CONFIG_MEDIA_CONTROLLER
uvc_mc_cleanup_entity(entity);
@@ -1871,6 +1857,7 @@ static void uvc_delete(struct kref *kref)
list_for_each_safe(p, n, &dev->streams) {
struct uvc_streaming *streaming;
+
streaming = list_entry(p, struct uvc_streaming, list);
usb_driver_release_interface(&uvc_driver.driver,
streaming->intf);
@@ -2206,7 +2193,8 @@ static int uvc_probe(struct usb_interface *intf,
usb_set_intfdata(intf, dev);
/* Initialize the interrupt URB. */
- if ((ret = uvc_status_init(dev)) < 0) {
+ ret = uvc_status_init(dev);
+ if (ret < 0) {
dev_info(&dev->udev->dev,
"Unable to initialize the status endpoint (%d), status interrupt will not be supported.\n",
ret);
@@ -2353,40 +2341,23 @@ static int uvc_clock_param_set(const char *val, const struct kernel_param *kp)
}
module_param_call(clock, uvc_clock_param_set, uvc_clock_param_get,
- &uvc_clock_param, S_IRUGO|S_IWUSR);
+ &uvc_clock_param, 0644);
MODULE_PARM_DESC(clock, "Video buffers timestamp clock");
-module_param_named(hwtimestamps, uvc_hw_timestamps_param, uint, S_IRUGO|S_IWUSR);
+module_param_named(hwtimestamps, uvc_hw_timestamps_param, uint, 0644);
MODULE_PARM_DESC(hwtimestamps, "Use hardware timestamps");
-module_param_named(nodrop, uvc_no_drop_param, uint, S_IRUGO|S_IWUSR);
+module_param_named(nodrop, uvc_no_drop_param, uint, 0644);
MODULE_PARM_DESC(nodrop, "Don't drop incomplete frames");
-module_param_named(quirks, uvc_quirks_param, uint, S_IRUGO|S_IWUSR);
+module_param_named(quirks, uvc_quirks_param, uint, 0644);
MODULE_PARM_DESC(quirks, "Forced device quirks");
-module_param_named(trace, uvc_dbg_param, uint, S_IRUGO|S_IWUSR);
+module_param_named(trace, uvc_dbg_param, uint, 0644);
MODULE_PARM_DESC(trace, "Trace level bitmask");
-module_param_named(timeout, uvc_timeout_param, uint, S_IRUGO|S_IWUSR);
+module_param_named(timeout, uvc_timeout_param, uint, 0644);
MODULE_PARM_DESC(timeout, "Streaming control requests timeout");
/* ------------------------------------------------------------------------
* Driver initialization and cleanup
*/
-static const struct uvc_menu_info power_line_frequency_controls_limited[] = {
- { 1, "50 Hz" },
- { 2, "60 Hz" },
-};
-
-static const struct uvc_control_mapping uvc_ctrl_power_line_mapping_limited = {
- .id = V4L2_CID_POWER_LINE_FREQUENCY,
- .entity = UVC_GUID_UVC_PROCESSING,
- .selector = UVC_PU_POWER_LINE_FREQUENCY_CONTROL,
- .size = 2,
- .offset = 0,
- .v4l2_type = V4L2_CTRL_TYPE_MENU,
- .data_type = UVC_CTRL_DATA_TYPE_ENUM,
- .menu_info = power_line_frequency_controls_limited,
- .menu_count = ARRAY_SIZE(power_line_frequency_controls_limited),
-};
-
static const struct uvc_device_info uvc_ctrl_power_line_limited = {
.mappings = (const struct uvc_control_mapping *[]) {
&uvc_ctrl_power_line_mapping_limited,
@@ -2394,6 +2365,13 @@ static const struct uvc_device_info uvc_ctrl_power_line_limited = {
},
};
+static const struct uvc_device_info uvc_ctrl_power_line_uvc11 = {
+ .mappings = (const struct uvc_control_mapping *[]) {
+ &uvc_ctrl_power_line_mapping_uvc11,
+ NULL, /* Sentinel */
+ },
+};
+
static const struct uvc_device_info uvc_quirk_probe_minmax = {
.quirks = UVC_QUIRK_PROBE_MINMAX,
};
@@ -2496,6 +2474,24 @@ static const struct usb_device_id uvc_ids[] = {
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0,
.driver_info = (kernel_ulong_t)&uvc_quirk_probe_minmax },
+ /* Logitech, Webcam C910 */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x046d,
+ .idProduct = 0x0821,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_WAKE_AUTOSUSPEND)},
+ /* Logitech, Webcam B910 */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x046d,
+ .idProduct = 0x0823,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_WAKE_AUTOSUSPEND)},
/* Logitech Quickcam Fusion */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
@@ -2973,6 +2969,15 @@ static const struct usb_device_id uvc_ids[] = {
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0,
.driver_info = UVC_INFO_QUIRK(UVC_QUIRK_FORCE_BPP) },
+ /* Lenovo Integrated Camera */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x30c9,
+ .idProduct = 0x0093,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = UVC_PC_PROTOCOL_15,
+ .driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_uvc11 },
/* Sonix Technology USB 2.0 Camera */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
@@ -2991,6 +2996,24 @@ static const struct usb_device_id uvc_ids[] = {
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0,
.driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_limited },
+ /* Acer EasyCamera */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x5986,
+ .idProduct = 0x1180,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_limited },
+ /* Acer EasyCamera */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x5986,
+ .idProduct = 0x1180,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_limited },
/* Intel RealSense D4M */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
diff --git a/drivers/media/usb/uvc/uvc_entity.c b/drivers/media/usb/uvc/uvc_entity.c
index 7c4d2f93d351..cc68dd24eb42 100644
--- a/drivers/media/usb/uvc/uvc_entity.c
+++ b/drivers/media/usb/uvc/uvc_entity.c
@@ -37,7 +37,7 @@ static int uvc_mc_create_links(struct uvc_video_chain *chain,
continue;
remote = uvc_entity_by_id(chain->dev, entity->baSourceID[i]);
- if (remote == NULL)
+ if (remote == NULL || remote->num_pads == 0)
return -EINVAL;
source = (UVC_ENTITY_TYPE(remote) == UVC_TT_STREAMING)
diff --git a/drivers/media/usb/uvc/uvc_status.c b/drivers/media/usb/uvc/uvc_status.c
index 7518ffce22ed..a78a88c710e2 100644
--- a/drivers/media/usb/uvc/uvc_status.c
+++ b/drivers/media/usb/uvc/uvc_status.c
@@ -6,6 +6,7 @@
* Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*/
+#include <asm/barrier.h>
#include <linux/kernel.h>
#include <linux/input.h>
#include <linux/slab.h>
@@ -18,11 +19,34 @@
* Input device
*/
#ifdef CONFIG_USB_VIDEO_CLASS_INPUT_EVDEV
+
+static bool uvc_input_has_button(struct uvc_device *dev)
+{
+ struct uvc_streaming *stream;
+
+ /*
+ * The device has button events if both bTriggerSupport and
+ * bTriggerUsage are one. Otherwise the camera button does not
+ * exist or is handled automatically by the camera without host
+ * driver or client application intervention.
+ */
+ list_for_each_entry(stream, &dev->streams, list) {
+ if (stream->header.bTriggerSupport == 1 &&
+ stream->header.bTriggerUsage == 1)
+ return true;
+ }
+
+ return false;
+}
+
static int uvc_input_init(struct uvc_device *dev)
{
struct input_dev *input;
int ret;
+ if (!uvc_input_has_button(dev))
+ return 0;
+
input = input_allocate_device();
if (input == NULL)
return -ENOMEM;
@@ -73,38 +97,23 @@ static void uvc_input_report_key(struct uvc_device *dev, unsigned int code,
/* --------------------------------------------------------------------------
* Status interrupt endpoint
*/
-struct uvc_streaming_status {
- u8 bStatusType;
- u8 bOriginator;
- u8 bEvent;
- u8 bValue[];
-} __packed;
-
-struct uvc_control_status {
- u8 bStatusType;
- u8 bOriginator;
- u8 bEvent;
- u8 bSelector;
- u8 bAttribute;
- u8 bValue[];
-} __packed;
-
static void uvc_event_streaming(struct uvc_device *dev,
- struct uvc_streaming_status *status, int len)
+ struct uvc_status *status, int len)
{
- if (len < 3) {
+ if (len <= offsetof(struct uvc_status, bEvent)) {
uvc_dbg(dev, STATUS,
"Invalid streaming status event received\n");
return;
}
if (status->bEvent == 0) {
- if (len < 4)
+ if (len <= offsetof(struct uvc_status, streaming))
return;
+
uvc_dbg(dev, STATUS, "Button (intf %u) %s len %d\n",
status->bOriginator,
- status->bValue[0] ? "pressed" : "released", len);
- uvc_input_report_key(dev, KEY_CAMERA, status->bValue[0]);
+ status->streaming.button ? "pressed" : "released", len);
+ uvc_input_report_key(dev, KEY_CAMERA, status->streaming.button);
} else {
uvc_dbg(dev, STATUS, "Stream %u error event %02x len %d\n",
status->bOriginator, status->bEvent, len);
@@ -131,7 +140,7 @@ static struct uvc_control *uvc_event_entity_find_ctrl(struct uvc_entity *entity,
}
static struct uvc_control *uvc_event_find_ctrl(struct uvc_device *dev,
- const struct uvc_control_status *status,
+ const struct uvc_status *status,
struct uvc_video_chain **chain)
{
list_for_each_entry((*chain), &dev->chains, list) {
@@ -143,7 +152,7 @@ static struct uvc_control *uvc_event_find_ctrl(struct uvc_device *dev,
continue;
ctrl = uvc_event_entity_find_ctrl(entity,
- status->bSelector);
+ status->control.bSelector);
if (ctrl)
return ctrl;
}
@@ -153,7 +162,7 @@ static struct uvc_control *uvc_event_find_ctrl(struct uvc_device *dev,
}
static bool uvc_event_control(struct urb *urb,
- const struct uvc_control_status *status, int len)
+ const struct uvc_status *status, int len)
{
static const char *attrs[] = { "value", "info", "failure", "min", "max" };
struct uvc_device *dev = urb->context;
@@ -161,24 +170,24 @@ static bool uvc_event_control(struct urb *urb,
struct uvc_control *ctrl;
if (len < 6 || status->bEvent != 0 ||
- status->bAttribute >= ARRAY_SIZE(attrs)) {
+ status->control.bAttribute >= ARRAY_SIZE(attrs)) {
uvc_dbg(dev, STATUS, "Invalid control status event received\n");
return false;
}
uvc_dbg(dev, STATUS, "Control %u/%u %s change len %d\n",
- status->bOriginator, status->bSelector,
- attrs[status->bAttribute], len);
+ status->bOriginator, status->control.bSelector,
+ attrs[status->control.bAttribute], len);
/* Find the control. */
ctrl = uvc_event_find_ctrl(dev, status, &chain);
if (!ctrl)
return false;
- switch (status->bAttribute) {
+ switch (status->control.bAttribute) {
case UVC_CTRL_VALUE_CHANGE:
return uvc_ctrl_status_event_async(urb, chain, ctrl,
- status->bValue);
+ status->control.bValue);
case UVC_CTRL_INFO_CHANGE:
case UVC_CTRL_FAILURE_CHANGE:
@@ -214,28 +223,22 @@ static void uvc_status_complete(struct urb *urb)
len = urb->actual_length;
if (len > 0) {
- switch (dev->status[0] & 0x0f) {
+ switch (dev->status->bStatusType & 0x0f) {
case UVC_STATUS_TYPE_CONTROL: {
- struct uvc_control_status *status =
- (struct uvc_control_status *)dev->status;
-
- if (uvc_event_control(urb, status, len))
+ if (uvc_event_control(urb, dev->status, len))
/* The URB will be resubmitted in work context. */
return;
break;
}
case UVC_STATUS_TYPE_STREAMING: {
- struct uvc_streaming_status *status =
- (struct uvc_streaming_status *)dev->status;
-
- uvc_event_streaming(dev, status, len);
+ uvc_event_streaming(dev, dev->status, len);
break;
}
default:
uvc_dbg(dev, STATUS, "Unknown status event type %u\n",
- dev->status[0]);
+ dev->status->bStatusType);
break;
}
}
@@ -259,12 +262,12 @@ int uvc_status_init(struct uvc_device *dev)
uvc_input_init(dev);
- dev->status = kzalloc(UVC_MAX_STATUS_SIZE, GFP_KERNEL);
- if (dev->status == NULL)
+ dev->status = kzalloc(sizeof(*dev->status), GFP_KERNEL);
+ if (!dev->status)
return -ENOMEM;
dev->int_urb = usb_alloc_urb(0, GFP_KERNEL);
- if (dev->int_urb == NULL) {
+ if (!dev->int_urb) {
kfree(dev->status);
return -ENOMEM;
}
@@ -281,7 +284,7 @@ int uvc_status_init(struct uvc_device *dev)
interval = fls(interval) - 1;
usb_fill_int_urb(dev->int_urb, dev->udev, pipe,
- dev->status, UVC_MAX_STATUS_SIZE, uvc_status_complete,
+ dev->status, sizeof(*dev->status), uvc_status_complete,
dev, interval);
return 0;
@@ -309,5 +312,41 @@ int uvc_status_start(struct uvc_device *dev, gfp_t flags)
void uvc_status_stop(struct uvc_device *dev)
{
+ struct uvc_ctrl_work *w = &dev->async_ctrl;
+
+ /*
+ * Prevent the asynchronous control handler from requeing the URB. The
+ * barrier is needed so the flush_status change is visible to other
+ * CPUs running the asynchronous handler before usb_kill_urb() is
+ * called below.
+ */
+ smp_store_release(&dev->flush_status, true);
+
+ /*
+ * Cancel any pending asynchronous work. If any status event was queued,
+ * process it synchronously.
+ */
+ if (cancel_work_sync(&w->work))
+ uvc_ctrl_status_event(w->chain, w->ctrl, w->data);
+
+ /* Kill the urb. */
usb_kill_urb(dev->int_urb);
+
+ /*
+ * The URB completion handler may have queued asynchronous work. This
+ * won't resubmit the URB as flush_status is set, but it needs to be
+ * cancelled before returning or it could then race with a future
+ * uvc_status_start() call.
+ */
+ if (cancel_work_sync(&w->work))
+ uvc_ctrl_status_event(w->chain, w->ctrl, w->data);
+
+ /*
+ * From this point, there are no events on the queue and the status URB
+ * is dead. No events will be queued until uvc_status_start() is called.
+ * The barrier is needed to make sure that flush_status is visible to
+ * uvc_ctrl_status_event_work() when uvc_status_start() will be called
+ * again.
+ */
+ smp_store_release(&dev->flush_status, false);
}
diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c
index f4d4c33b6dfb..35453f81c1d9 100644
--- a/drivers/media/usb/uvc/uvc_v4l2.c
+++ b/drivers/media/usb/uvc/uvc_v4l2.c
@@ -6,6 +6,7 @@
* Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*/
+#include <linux/bits.h>
#include <linux/compat.h>
#include <linux/kernel.h>
#include <linux/list.h>
@@ -25,14 +26,84 @@
#include "uvcvideo.h"
+static int uvc_control_add_xu_mapping(struct uvc_video_chain *chain,
+ struct uvc_control_mapping *map,
+ const struct uvc_xu_control_mapping *xmap)
+{
+ unsigned int i;
+ size_t size;
+ int ret;
+
+ /*
+ * Prevent excessive memory consumption, as well as integer
+ * overflows.
+ */
+ if (xmap->menu_count == 0 ||
+ xmap->menu_count > UVC_MAX_CONTROL_MENU_ENTRIES)
+ return -EINVAL;
+
+ map->menu_names = NULL;
+ map->menu_mapping = NULL;
+
+ map->menu_mask = BIT_MASK(xmap->menu_count);
+
+ size = xmap->menu_count * sizeof(*map->menu_mapping);
+ map->menu_mapping = kzalloc(size, GFP_KERNEL);
+ if (!map->menu_mapping) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ for (i = 0; i < xmap->menu_count ; i++) {
+ if (copy_from_user((u32 *)&map->menu_mapping[i],
+ &xmap->menu_info[i].value,
+ sizeof(map->menu_mapping[i]))) {
+ ret = -EACCES;
+ goto done;
+ }
+ }
+
+ /*
+ * Always use the standard naming if available, otherwise copy the
+ * names supplied by userspace.
+ */
+ if (!v4l2_ctrl_get_menu(map->id)) {
+ size = xmap->menu_count * sizeof(map->menu_names[0]);
+ map->menu_names = kzalloc(size, GFP_KERNEL);
+ if (!map->menu_names) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ for (i = 0; i < xmap->menu_count ; i++) {
+ /* sizeof(names[i]) - 1: to take care of \0 */
+ if (copy_from_user((char *)map->menu_names[i],
+ xmap->menu_info[i].name,
+ sizeof(map->menu_names[i]) - 1)) {
+ ret = -EACCES;
+ goto done;
+ }
+ }
+ }
+
+ ret = uvc_ctrl_add_mapping(chain, map);
+
+done:
+ kfree(map->menu_names);
+ map->menu_names = NULL;
+ kfree(map->menu_mapping);
+ map->menu_mapping = NULL;
+
+ return ret;
+}
+
/* ------------------------------------------------------------------------
* UVC ioctls
*/
-static int uvc_ioctl_ctrl_map(struct uvc_video_chain *chain,
- struct uvc_xu_control_mapping *xmap)
+static int uvc_ioctl_xu_ctrl_map(struct uvc_video_chain *chain,
+ struct uvc_xu_control_mapping *xmap)
{
struct uvc_control_mapping *map;
- unsigned int size;
int ret;
map = kzalloc(sizeof(*map), GFP_KERNEL);
@@ -60,39 +131,20 @@ static int uvc_ioctl_ctrl_map(struct uvc_video_chain *chain,
case V4L2_CTRL_TYPE_INTEGER:
case V4L2_CTRL_TYPE_BOOLEAN:
case V4L2_CTRL_TYPE_BUTTON:
+ ret = uvc_ctrl_add_mapping(chain, map);
break;
case V4L2_CTRL_TYPE_MENU:
- /*
- * Prevent excessive memory consumption, as well as integer
- * overflows.
- */
- if (xmap->menu_count == 0 ||
- xmap->menu_count > UVC_MAX_CONTROL_MENU_ENTRIES) {
- ret = -EINVAL;
- goto free_map;
- }
-
- size = xmap->menu_count * sizeof(*map->menu_info);
- map->menu_info = memdup_user(xmap->menu_info, size);
- if (IS_ERR(map->menu_info)) {
- ret = PTR_ERR(map->menu_info);
- goto free_map;
- }
-
- map->menu_count = xmap->menu_count;
+ ret = uvc_control_add_xu_mapping(chain, map, xmap);
break;
default:
uvc_dbg(chain->dev, CONTROL,
"Unsupported V4L2 control type %u\n", xmap->v4l2_type);
ret = -ENOTTY;
- goto free_map;
+ break;
}
- ret = uvc_ctrl_add_mapping(chain, map);
-
- kfree(map->menu_info);
free_map:
kfree(map);
@@ -660,8 +712,6 @@ static int uvc_ioctl_enum_fmt(struct uvc_streaming *stream,
fmt->flags = 0;
if (format->flags & UVC_FMT_FLAG_COMPRESSED)
fmt->flags |= V4L2_FMT_FLAG_COMPRESSED;
- strscpy(fmt->description, format->name, sizeof(fmt->description));
- fmt->description[sizeof(fmt->description) - 1] = 0;
fmt->pixelformat = format->fcc;
return 0;
}
@@ -1020,8 +1070,7 @@ static int uvc_ctrl_check_access(struct uvc_video_chain *chain,
int ret = 0;
for (i = 0; i < ctrls->count; ++ctrl, ++i) {
- ret = uvc_ctrl_is_accessible(chain, ctrl->id,
- ioctl == VIDIOC_G_EXT_CTRLS);
+ ret = uvc_ctrl_is_accessible(chain, ctrl->id, ctrls, ioctl);
if (ret)
break;
}
@@ -1316,7 +1365,7 @@ static long uvc_ioctl_default(struct file *file, void *fh, bool valid_prio,
switch (cmd) {
/* Dynamic controls. */
case UVCIOC_CTRL_MAP:
- return uvc_ioctl_ctrl_map(chain, arg);
+ return uvc_ioctl_xu_ctrl_map(chain, arg);
case UVCIOC_CTRL_QUERY:
return uvc_xu_ctrl_query(chain, arg);
@@ -1429,7 +1478,7 @@ static long uvc_v4l2_compat_ioctl32(struct file *file,
ret = uvc_v4l2_get_xu_mapping(&karg.xmap, up);
if (ret)
return ret;
- ret = uvc_ioctl_ctrl_map(handle->chain, &karg.xmap);
+ ret = uvc_ioctl_xu_ctrl_map(handle->chain, &karg.xmap);
if (ret)
return ret;
ret = uvc_v4l2_put_xu_mapping(&karg.xmap, up);
diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c
index d2eb9066e4dc..d4b023d4de7c 100644
--- a/drivers/media/usb/uvc/uvc_video.c
+++ b/drivers/media/usb/uvc/uvc_video.c
@@ -79,13 +79,14 @@ int uvc_query_ctrl(struct uvc_device *dev, u8 query, u8 unit,
if (likely(ret == size))
return 0;
- dev_err(&dev->udev->dev,
- "Failed to query (%s) UVC control %u on unit %u: %d (exp. %u).\n",
- uvc_query_name(query), cs, unit, ret, size);
-
- if (ret != -EPIPE)
- return ret;
+ if (ret != -EPIPE) {
+ dev_err(&dev->udev->dev,
+ "Failed to query (%s) UVC control %u on unit %u: %d (exp. %u).\n",
+ uvc_query_name(query), cs, unit, ret, size);
+ return ret < 0 ? ret : -EPIPE;
+ }
+ /* Reuse data[0] to request the error code. */
tmp = *(u8 *)data;
ret = __uvc_query_ctrl(dev, UVC_GET_CUR, 0, intfnum,
@@ -107,7 +108,7 @@ int uvc_query_ctrl(struct uvc_device *dev, u8 query, u8 unit,
case 1: /* Not ready */
return -EBUSY;
case 2: /* Wrong state */
- return -EILSEQ;
+ return -EACCES;
case 3: /* Power */
return -EREMOTE;
case 4: /* Out of range */
@@ -129,12 +130,13 @@ int uvc_query_ctrl(struct uvc_device *dev, u8 query, u8 unit,
return -EPIPE;
}
+static const struct usb_device_id elgato_cam_link_4k = {
+ USB_DEVICE(0x0fd9, 0x0066)
+};
+
static void uvc_fixup_video_ctrl(struct uvc_streaming *stream,
struct uvc_streaming_control *ctrl)
{
- static const struct usb_device_id elgato_cam_link_4k = {
- USB_DEVICE(0x0fd9, 0x0066)
- };
struct uvc_format *format = NULL;
struct uvc_frame *frame = NULL;
unsigned int i;
@@ -297,7 +299,7 @@ static int uvc_get_video_ctrl(struct uvc_streaming *stream,
dev_err(&stream->intf->dev,
"Failed to query (%u) UVC %s control : %d (exp. %u).\n",
query, probe ? "probe" : "commit", ret, size);
- ret = -EIO;
+ ret = (ret == -EPROTO) ? -EPROTO : -EIO;
goto out;
}
@@ -516,7 +518,9 @@ uvc_video_clock_decode(struct uvc_streaming *stream, struct uvc_buffer *buf,
/*
* To limit the amount of data, drop SCRs with an SOF identical to the
- * previous one.
+ * previous one. This filtering is also needed to support UVC 1.5, where
+ * all the data packets of the same frame contains the same SOF. In that
+ * case only the first one will match the host_sof.
*/
dev_sof = get_unaligned_le16(&data[header_size - 2]);
if (dev_sof == stream->clock.last_sof)
@@ -1352,7 +1356,9 @@ static void uvc_video_decode_meta(struct uvc_streaming *stream,
if (has_scr)
memcpy(stream->clock.last_scr, scr, 6);
- memcpy(&meta->length, mem, length);
+ meta->length = mem[0];
+ meta->flags = mem[1];
+ memcpy(meta->buf, &mem[2], length - 2);
meta_buf->bytesused += length + sizeof(meta->ns) + sizeof(meta->sof);
uvc_dbg(stream->dev, FRAME,
@@ -1965,6 +1971,17 @@ static int uvc_video_start_transfer(struct uvc_streaming *stream,
"Selecting alternate setting %u (%u B/frame bandwidth)\n",
altsetting, best_psize);
+ /*
+ * Some devices, namely the Logitech C910 and B910, are unable
+ * to recover from a USB autosuspend, unless the alternate
+ * setting of the streaming interface is toggled.
+ */
+ if (stream->dev->quirks & UVC_QUIRK_WAKE_AUTOSUSPEND) {
+ usb_set_interface(stream->dev->udev, intfnum,
+ altsetting);
+ usb_set_interface(stream->dev->udev, intfnum, 0);
+ }
+
ret = usb_set_interface(stream->dev->udev, intfnum, altsetting);
if (ret < 0)
return ret;
@@ -2121,6 +2138,21 @@ int uvc_video_init(struct uvc_streaming *stream)
* request on the probe control, as required by the UVC specification.
*/
ret = uvc_get_video_ctrl(stream, probe, 1, UVC_GET_CUR);
+
+ /*
+ * Elgato Cam Link 4k can be in a stalled state if the resolution of
+ * the external source has changed while the firmware initializes.
+ * Once in this state, the device is useless until it receives a
+ * USB reset. It has even been observed that the stalled state will
+ * continue even after unplugging the device.
+ */
+ if (ret == -EPROTO &&
+ usb_match_one_id(stream->dev->intf, &elgato_cam_link_4k)) {
+ dev_err(&stream->intf->dev, "Elgato Cam Link 4K firmware crash detected\n");
+ dev_err(&stream->intf->dev, "Resetting the device, unplug and replug to recover\n");
+ usb_reset_device(stream->dev->udev);
+ }
+
if (ret < 0)
return ret;
diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
index df93db259312..9a596c8d894a 100644
--- a/drivers/media/usb/uvc/uvcvideo.h
+++ b/drivers/media/usb/uvc/uvcvideo.h
@@ -51,8 +51,6 @@
#define UVC_URBS 5
/* Maximum number of packets per URB. */
#define UVC_MAX_PACKETS 32
-/* Maximum status buffer size in bytes of interrupt URB. */
-#define UVC_MAX_STATUS_SIZE 16
#define UVC_CTRL_CONTROL_TIMEOUT 5000
#define UVC_CTRL_STREAMING_TIMEOUT 5000
@@ -74,6 +72,7 @@
#define UVC_QUIRK_RESTORE_CTRLS_ON_INIT 0x00000400
#define UVC_QUIRK_FORCE_Y8 0x00000800
#define UVC_QUIRK_FORCE_BPP 0x00001000
+#define UVC_QUIRK_WAKE_AUTOSUSPEND 0x00002000
/* Format flags */
#define UVC_FMT_FLAG_COMPRESSED 0x00000001
@@ -116,8 +115,9 @@ struct uvc_control_mapping {
enum v4l2_ctrl_type v4l2_type;
u32 data_type;
- const struct uvc_menu_info *menu_info;
- u32 menu_count;
+ const u32 *menu_mapping;
+ const char (*menu_names)[UVC_MENU_NAME_LEN];
+ unsigned long menu_mask;
u32 master_id;
s32 master_manual;
@@ -264,8 +264,6 @@ struct uvc_format {
u32 fcc;
u32 flags;
- char name[32];
-
unsigned int nframes;
struct uvc_frame *frame;
};
@@ -527,6 +525,26 @@ struct uvc_device_info {
const struct uvc_control_mapping **mappings;
};
+struct uvc_status_streaming {
+ u8 button;
+} __packed;
+
+struct uvc_status_control {
+ u8 bSelector;
+ u8 bAttribute;
+ u8 bValue[11];
+} __packed;
+
+struct uvc_status {
+ u8 bStatusType;
+ u8 bOriginator;
+ u8 bEvent;
+ union {
+ struct uvc_status_control control;
+ struct uvc_status_streaming streaming;
+ };
+} __packed;
+
struct uvc_device {
struct usb_device *udev;
struct usb_interface *intf;
@@ -559,7 +577,9 @@ struct uvc_device {
/* Status Interrupt Endpoint */
struct usb_host_endpoint *int_ep;
struct urb *int_urb;
- u8 *status;
+ struct uvc_status *status;
+ bool flush_status;
+
struct input_dev *input;
char input_phys[64];
@@ -728,6 +748,8 @@ int uvc_status_start(struct uvc_device *dev, gfp_t flags);
void uvc_status_stop(struct uvc_device *dev);
/* Controls */
+extern const struct uvc_control_mapping uvc_ctrl_power_line_mapping_limited;
+extern const struct uvc_control_mapping uvc_ctrl_power_line_mapping_uvc11;
extern const struct v4l2_subscribed_event_ops uvc_ctrl_sub_ev_ops;
int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
@@ -761,7 +783,8 @@ static inline int uvc_ctrl_rollback(struct uvc_fh *handle)
int uvc_ctrl_get(struct uvc_video_chain *chain, struct v4l2_ext_control *xctrl);
int uvc_ctrl_set(struct uvc_fh *handle, struct v4l2_ext_control *xctrl);
int uvc_ctrl_is_accessible(struct uvc_video_chain *chain, u32 v4l2_id,
- bool read);
+ const struct v4l2_ext_controls *ctrls,
+ unsigned long ioctl);
int uvc_xu_ctrl_query(struct uvc_video_chain *chain,
struct uvc_xu_control_query *xqry);
diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c
index 2f1b718a9189..d7e9ffc7aa23 100644
--- a/drivers/media/v4l2-core/v4l2-async.c
+++ b/drivers/media/v4l2-core/v4l2-async.c
@@ -24,6 +24,8 @@
#include <media/v4l2-fwnode.h>
#include <media/v4l2-subdev.h>
+#include "v4l2-subdev-priv.h"
+
static int v4l2_async_nf_call_bound(struct v4l2_async_notifier *n,
struct v4l2_subdev *subdev,
struct v4l2_async_subdev *asd)
@@ -822,6 +824,8 @@ void v4l2_async_unregister_subdev(struct v4l2_subdev *sd)
if (!sd->async_list.next)
return;
+ v4l2_subdev_put_privacy_led(sd);
+
mutex_lock(&list_lock);
__v4l2_async_nf_unregister(sd->subdev_notifier);
diff --git a/drivers/media/v4l2-core/v4l2-ctrls-api.c b/drivers/media/v4l2-core/v4l2-ctrls-api.c
index 3d3b6dc24ca6..002ea6588edf 100644
--- a/drivers/media/v4l2-core/v4l2-ctrls-api.c
+++ b/drivers/media/v4l2-core/v4l2-ctrls-api.c
@@ -150,8 +150,8 @@ static int user_to_new(struct v4l2_ext_control *c, struct v4l2_ctrl *ctrl)
* then return an error.
*/
if (strlen(ctrl->p_new.p_char) == ctrl->maximum && last)
- ctrl->is_new = 1;
return -ERANGE;
+ ctrl->is_new = 1;
}
return ret;
default:
diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c b/drivers/media/v4l2-core/v4l2-fwnode.c
index 3d9533c1b202..049c2f2001ea 100644
--- a/drivers/media/v4l2-core/v4l2-fwnode.c
+++ b/drivers/media/v4l2-core/v4l2-fwnode.c
@@ -28,6 +28,8 @@
#include <media/v4l2-fwnode.h>
#include <media/v4l2-subdev.h>
+#include "v4l2-subdev-priv.h"
+
static const struct v4l2_fwnode_bus_conv {
enum v4l2_fwnode_bus_type fwnode_bus_type;
enum v4l2_mbus_type mbus_type;
@@ -1302,6 +1304,10 @@ int v4l2_async_register_subdev_sensor(struct v4l2_subdev *sd)
v4l2_async_nf_init(notifier);
+ ret = v4l2_subdev_get_privacy_led(sd);
+ if (ret < 0)
+ goto out_cleanup;
+
ret = v4l2_async_nf_parse_fwnode_sensor(sd->dev, notifier);
if (ret < 0)
goto out_cleanup;
@@ -1322,6 +1328,7 @@ out_unregister:
v4l2_async_nf_unregister(notifier);
out_cleanup:
+ v4l2_subdev_put_privacy_led(sd);
v4l2_async_nf_cleanup(notifier);
kfree(notifier);
diff --git a/drivers/media/v4l2-core/v4l2-h264.c b/drivers/media/v4l2-core/v4l2-h264.c
index 72bd64f65198..c00197d095e7 100644
--- a/drivers/media/v4l2-core/v4l2-h264.c
+++ b/drivers/media/v4l2-core/v4l2-h264.c
@@ -305,6 +305,8 @@ static const char *format_ref_list_p(const struct v4l2_h264_reflist_builder *bui
int n = 0, i;
*out_str = kmalloc(tmp_str_size, GFP_KERNEL);
+ if (!(*out_str))
+ return NULL;
n += snprintf(*out_str + n, tmp_str_size - n, "|");
@@ -343,6 +345,8 @@ static const char *format_ref_list_b(const struct v4l2_h264_reflist_builder *bui
int n = 0, i;
*out_str = kmalloc(tmp_str_size, GFP_KERNEL);
+ if (!(*out_str))
+ return NULL;
n += snprintf(*out_str + n, tmp_str_size - n, "|");
diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
index 2a0139c72d29..87f163a89c80 100644
--- a/drivers/media/v4l2-core/v4l2-ioctl.c
+++ b/drivers/media/v4l2-core/v4l2-ioctl.c
@@ -16,6 +16,7 @@
#include <linux/kernel.h>
#include <linux/version.h>
+#include <linux/v4l2-subdev.h>
#include <linux/videodev2.h>
#include <media/media-device.h> /* for media_set_bus_info() */
@@ -3154,6 +3155,21 @@ static int check_array_args(unsigned int cmd, void *parg, size_t *array_size,
ret = 1;
break;
}
+
+ case VIDIOC_SUBDEV_G_ROUTING:
+ case VIDIOC_SUBDEV_S_ROUTING: {
+ struct v4l2_subdev_routing *routing = parg;
+
+ if (routing->num_routes > 256)
+ return -E2BIG;
+
+ *user_ptr = u64_to_user_ptr(routing->routes);
+ *kernel_ptr = (void **)&routing->routes;
+ *array_size = sizeof(struct v4l2_subdev_route)
+ * routing->num_routes;
+ ret = 1;
+ break;
+ }
}
return ret;
@@ -3400,8 +3416,15 @@ video_usercopy(struct file *file, unsigned int orig_cmd, unsigned long arg,
/*
* Some ioctls can return an error, but still have valid
* results that must be returned.
+ *
+ * FIXME: subdev IOCTLS are partially handled here and partially in
+ * v4l2-subdev.c and the 'always_copy' flag can only be set for IOCTLS
+ * defined here as part of the 'v4l2_ioctls' array. As
+ * VIDIOC_SUBDEV_G_ROUTING needs to return results to applications even
+ * in case of failure, but it is not defined here as part of the
+ * 'v4l2_ioctls' array, insert an ad-hoc check to address that.
*/
- if (err < 0 && !always_copy)
+ if (err < 0 && !always_copy && cmd != VIDIOC_SUBDEV_G_ROUTING)
goto out;
if (has_array_args) {
diff --git a/drivers/media/v4l2-core/v4l2-jpeg.c b/drivers/media/v4l2-core/v4l2-jpeg.c
index c2513b775f6a..94435a7b6816 100644
--- a/drivers/media/v4l2-core/v4l2-jpeg.c
+++ b/drivers/media/v4l2-core/v4l2-jpeg.c
@@ -460,7 +460,7 @@ static int jpeg_parse_app14_data(struct jpeg_stream *stream,
/* Check for "Adobe\0" in Ap1..6 */
if (stream->curr + 6 > stream->end ||
strncmp(stream->curr, "Adobe\0", 6))
- return -EINVAL;
+ return jpeg_skip(stream, lp - 2);
/* get to Ap12 */
ret = jpeg_skip(stream, 11);
@@ -474,7 +474,7 @@ static int jpeg_parse_app14_data(struct jpeg_stream *stream,
*tf = ret;
/* skip the rest of the segment, this ensures at least it is complete */
- skip = lp - 2 - 11;
+ skip = lp - 2 - 11 - 1;
return jpeg_skip(stream, skip);
}
diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c
index be7fde1ed3ea..0cc30397fbad 100644
--- a/drivers/media/v4l2-core/v4l2-mem2mem.c
+++ b/drivers/media/v4l2-core/v4l2-mem2mem.c
@@ -922,9 +922,9 @@ static __poll_t v4l2_m2m_poll_for_data(struct file *file,
* means either in driver already or waiting for driver to claim it
* and start processing.
*/
- if ((!src_q->streaming || src_q->error ||
+ if ((!vb2_is_streaming(src_q) || src_q->error ||
list_empty(&src_q->queued_list)) &&
- (!dst_q->streaming || dst_q->error ||
+ (!vb2_is_streaming(dst_q) || dst_q->error ||
(list_empty(&dst_q->queued_list) && !dst_q->last_buffer_dequeued)))
return EPOLLERR;
diff --git a/drivers/media/v4l2-core/v4l2-subdev-priv.h b/drivers/media/v4l2-core/v4l2-subdev-priv.h
new file mode 100644
index 000000000000..52391d6d8ab7
--- /dev/null
+++ b/drivers/media/v4l2-core/v4l2-subdev-priv.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * V4L2 sub-device pivate header.
+ *
+ * Copyright (C) 2023 Hans de Goede <hdegoede@redhat.com>
+ */
+
+#ifndef _V4L2_SUBDEV_PRIV_H_
+#define _V4L2_SUBDEV_PRIV_H_
+
+int v4l2_subdev_get_privacy_led(struct v4l2_subdev *sd);
+void v4l2_subdev_put_privacy_led(struct v4l2_subdev *sd);
+
+#endif
diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c
index 4988a25bd8f4..b10045c02f43 100644
--- a/drivers/media/v4l2-core/v4l2-subdev.c
+++ b/drivers/media/v4l2-core/v4l2-subdev.c
@@ -8,20 +8,43 @@
* Sakari Ailus <sakari.ailus@iki.fi>
*/
+#include <linux/export.h>
#include <linux/ioctl.h>
+#include <linux/leds.h>
#include <linux/mm.h>
#include <linux/module.h>
+#include <linux/overflow.h>
#include <linux/slab.h>
#include <linux/types.h>
-#include <linux/videodev2.h>
-#include <linux/export.h>
#include <linux/version.h>
+#include <linux/videodev2.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
-#include <media/v4l2-ioctl.h>
-#include <media/v4l2-fh.h>
#include <media/v4l2-event.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-ioctl.h>
+
+#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API)
+/*
+ * The Streams API is an experimental feature. To use the Streams API, set
+ * 'v4l2_subdev_enable_streams_api' to 1 below.
+ */
+
+static bool v4l2_subdev_enable_streams_api;
+#endif
+
+/*
+ * Maximum stream ID is 63 for now, as we use u64 bitmask to represent a set
+ * of streams.
+ *
+ * Note that V4L2_FRAME_DESC_ENTRY_MAX is related: V4L2_FRAME_DESC_ENTRY_MAX
+ * restricts the total number of streams in a pad, although the stream ID is
+ * not restricted.
+ */
+#define V4L2_SUBDEV_MAX_STREAM_ID 63
+
+#include "v4l2-subdev-priv.h"
#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API)
static int subdev_fh_init(struct v4l2_subdev_fh *fh, struct v4l2_subdev *sd)
@@ -148,8 +171,22 @@ static inline int check_pad(struct v4l2_subdev *sd, u32 pad)
return 0;
}
-static int check_state_pads(u32 which, struct v4l2_subdev_state *state)
+static int check_state(struct v4l2_subdev *sd, struct v4l2_subdev_state *state,
+ u32 which, u32 pad, u32 stream)
{
+ if (sd->flags & V4L2_SUBDEV_FL_STREAMS) {
+#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API)
+ if (!v4l2_subdev_state_get_stream_format(state, pad, stream))
+ return -EINVAL;
+ return 0;
+#else
+ return -EINVAL;
+#endif
+ }
+
+ if (stream != 0)
+ return -EINVAL;
+
if (which == V4L2_SUBDEV_FORMAT_TRY && (!state || !state->pads))
return -EINVAL;
@@ -163,8 +200,11 @@ static inline int check_format(struct v4l2_subdev *sd,
if (!format)
return -EINVAL;
+ if (!(sd->flags & V4L2_SUBDEV_FL_STREAMS))
+ format->stream = 0;
+
return check_which(format->which) ? : check_pad(sd, format->pad) ? :
- check_state_pads(format->which, state);
+ check_state(sd, state, format->which, format->pad, format->stream);
}
static int call_get_fmt(struct v4l2_subdev *sd,
@@ -190,8 +230,11 @@ static int call_enum_mbus_code(struct v4l2_subdev *sd,
if (!code)
return -EINVAL;
+ if (!(sd->flags & V4L2_SUBDEV_FL_STREAMS))
+ code->stream = 0;
+
return check_which(code->which) ? : check_pad(sd, code->pad) ? :
- check_state_pads(code->which, state) ? :
+ check_state(sd, state, code->which, code->pad, code->stream) ? :
sd->ops->pad->enum_mbus_code(sd, state, code);
}
@@ -202,8 +245,11 @@ static int call_enum_frame_size(struct v4l2_subdev *sd,
if (!fse)
return -EINVAL;
+ if (!(sd->flags & V4L2_SUBDEV_FL_STREAMS))
+ fse->stream = 0;
+
return check_which(fse->which) ? : check_pad(sd, fse->pad) ? :
- check_state_pads(fse->which, state) ? :
+ check_state(sd, state, fse->which, fse->pad, fse->stream) ? :
sd->ops->pad->enum_frame_size(sd, state, fse);
}
@@ -237,8 +283,11 @@ static int call_enum_frame_interval(struct v4l2_subdev *sd,
if (!fie)
return -EINVAL;
+ if (!(sd->flags & V4L2_SUBDEV_FL_STREAMS))
+ fie->stream = 0;
+
return check_which(fie->which) ? : check_pad(sd, fie->pad) ? :
- check_state_pads(fie->which, state) ? :
+ check_state(sd, state, fie->which, fie->pad, fie->stream) ? :
sd->ops->pad->enum_frame_interval(sd, state, fie);
}
@@ -249,8 +298,11 @@ static inline int check_selection(struct v4l2_subdev *sd,
if (!sel)
return -EINVAL;
+ if (!(sd->flags & V4L2_SUBDEV_FL_STREAMS))
+ sel->stream = 0;
+
return check_which(sel->which) ? : check_pad(sd, sel->pad) ? :
- check_state_pads(sel->which, state);
+ check_state(sd, state, sel->which, sel->pad, sel->stream);
}
static int call_get_selection(struct v4l2_subdev *sd,
@@ -322,6 +374,15 @@ static int call_s_stream(struct v4l2_subdev *sd, int enable)
{
int ret;
+#if IS_REACHABLE(CONFIG_LEDS_CLASS)
+ if (!IS_ERR_OR_NULL(sd->privacy_led)) {
+ if (enable)
+ led_set_brightness(sd->privacy_led,
+ sd->privacy_led->max_brightness);
+ else
+ led_set_brightness(sd->privacy_led, 0);
+ }
+#endif
ret = sd->ops->video->s_stream(sd, enable);
if (!enable && ret < 0) {
@@ -432,6 +493,10 @@ subdev_ioctl_get_state(struct v4l2_subdev *sd, struct v4l2_subdev_fh *subdev_fh,
case VIDIOC_SUBDEV_S_SELECTION:
which = ((struct v4l2_subdev_selection *)arg)->which;
break;
+ case VIDIOC_SUBDEV_G_ROUTING:
+ case VIDIOC_SUBDEV_S_ROUTING:
+ which = ((struct v4l2_subdev_routing *)arg)->which;
+ break;
}
return which == V4L2_SUBDEV_FORMAT_TRY ?
@@ -446,6 +511,7 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg,
struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev);
struct v4l2_fh *vfh = file->private_data;
bool ro_subdev = test_bit(V4L2_FL_SUBDEV_RO_DEVNODE, &vdev->flags);
+ bool streams_subdev = sd->flags & V4L2_SUBDEV_FL_STREAMS;
int rval;
switch (cmd) {
@@ -454,7 +520,9 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg,
memset(cap->reserved, 0, sizeof(cap->reserved));
cap->version = LINUX_VERSION_CODE;
- cap->capabilities = ro_subdev ? V4L2_SUBDEV_CAP_RO_SUBDEV : 0;
+ cap->capabilities =
+ (ro_subdev ? V4L2_SUBDEV_CAP_RO_SUBDEV : 0) |
+ (streams_subdev ? V4L2_SUBDEV_CAP_STREAMS : 0);
return 0;
}
@@ -745,6 +813,81 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg,
case VIDIOC_SUBDEV_QUERYSTD:
return v4l2_subdev_call(sd, video, querystd, arg);
+ case VIDIOC_SUBDEV_G_ROUTING: {
+ struct v4l2_subdev_routing *routing = arg;
+ struct v4l2_subdev_krouting *krouting;
+
+ if (!v4l2_subdev_enable_streams_api)
+ return -ENOIOCTLCMD;
+
+ if (!(sd->flags & V4L2_SUBDEV_FL_STREAMS))
+ return -ENOIOCTLCMD;
+
+ memset(routing->reserved, 0, sizeof(routing->reserved));
+
+ krouting = &state->routing;
+
+ if (routing->num_routes < krouting->num_routes) {
+ routing->num_routes = krouting->num_routes;
+ return -ENOSPC;
+ }
+
+ memcpy((struct v4l2_subdev_route *)(uintptr_t)routing->routes,
+ krouting->routes,
+ krouting->num_routes * sizeof(*krouting->routes));
+ routing->num_routes = krouting->num_routes;
+
+ return 0;
+ }
+
+ case VIDIOC_SUBDEV_S_ROUTING: {
+ struct v4l2_subdev_routing *routing = arg;
+ struct v4l2_subdev_route *routes =
+ (struct v4l2_subdev_route *)(uintptr_t)routing->routes;
+ struct v4l2_subdev_krouting krouting = {};
+ unsigned int i;
+
+ if (!v4l2_subdev_enable_streams_api)
+ return -ENOIOCTLCMD;
+
+ if (!(sd->flags & V4L2_SUBDEV_FL_STREAMS))
+ return -ENOIOCTLCMD;
+
+ if (routing->which != V4L2_SUBDEV_FORMAT_TRY && ro_subdev)
+ return -EPERM;
+
+ memset(routing->reserved, 0, sizeof(routing->reserved));
+
+ for (i = 0; i < routing->num_routes; ++i) {
+ const struct v4l2_subdev_route *route = &routes[i];
+ const struct media_pad *pads = sd->entity.pads;
+
+ if (route->sink_stream > V4L2_SUBDEV_MAX_STREAM_ID ||
+ route->source_stream > V4L2_SUBDEV_MAX_STREAM_ID)
+ return -EINVAL;
+
+ if (route->sink_pad >= sd->entity.num_pads)
+ return -EINVAL;
+
+ if (!(pads[route->sink_pad].flags &
+ MEDIA_PAD_FL_SINK))
+ return -EINVAL;
+
+ if (route->source_pad >= sd->entity.num_pads)
+ return -EINVAL;
+
+ if (!(pads[route->source_pad].flags &
+ MEDIA_PAD_FL_SOURCE))
+ return -EINVAL;
+ }
+
+ krouting.num_routes = routing->num_routes;
+ krouting.routes = routes;
+
+ return v4l2_subdev_call(sd, pad, set_routing, state,
+ routing->which, &krouting);
+ }
+
default:
return v4l2_subdev_call(sd, core, ioctl, cmd, arg);
}
@@ -925,7 +1068,7 @@ int v4l2_subdev_link_validate_default(struct v4l2_subdev *sd,
EXPORT_SYMBOL_GPL(v4l2_subdev_link_validate_default);
static int
-v4l2_subdev_link_validate_get_format(struct media_pad *pad,
+v4l2_subdev_link_validate_get_format(struct media_pad *pad, u32 stream,
struct v4l2_subdev_format *fmt)
{
if (is_media_entity_v4l2_subdev(pad->entity)) {
@@ -934,7 +1077,11 @@ v4l2_subdev_link_validate_get_format(struct media_pad *pad,
fmt->which = V4L2_SUBDEV_FORMAT_ACTIVE;
fmt->pad = pad->index;
- return v4l2_subdev_call_state_active(sd, pad, get_fmt, fmt);
+ fmt->stream = stream;
+
+ return v4l2_subdev_call(sd, pad, get_fmt,
+ v4l2_subdev_get_locked_active_state(sd),
+ fmt);
}
WARN(pad->entity->function != MEDIA_ENT_F_IO_V4L,
@@ -944,34 +1091,206 @@ v4l2_subdev_link_validate_get_format(struct media_pad *pad,
return -EINVAL;
}
+#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API)
+
+static void __v4l2_link_validate_get_streams(struct media_pad *pad,
+ u64 *streams_mask)
+{
+ struct v4l2_subdev_route *route;
+ struct v4l2_subdev_state *state;
+ struct v4l2_subdev *subdev;
+
+ subdev = media_entity_to_v4l2_subdev(pad->entity);
+
+ *streams_mask = 0;
+
+ state = v4l2_subdev_get_locked_active_state(subdev);
+ if (WARN_ON(!state))
+ return;
+
+ for_each_active_route(&state->routing, route) {
+ u32 route_pad;
+ u32 route_stream;
+
+ if (pad->flags & MEDIA_PAD_FL_SOURCE) {
+ route_pad = route->source_pad;
+ route_stream = route->source_stream;
+ } else {
+ route_pad = route->sink_pad;
+ route_stream = route->sink_stream;
+ }
+
+ if (route_pad != pad->index)
+ continue;
+
+ *streams_mask |= BIT_ULL(route_stream);
+ }
+}
+
+#endif /* CONFIG_VIDEO_V4L2_SUBDEV_API */
+
+static void v4l2_link_validate_get_streams(struct media_pad *pad,
+ u64 *streams_mask)
+{
+ struct v4l2_subdev *subdev = media_entity_to_v4l2_subdev(pad->entity);
+
+ if (!(subdev->flags & V4L2_SUBDEV_FL_STREAMS)) {
+ /* Non-streams subdevs have an implicit stream 0 */
+ *streams_mask = BIT_ULL(0);
+ return;
+ }
+
+#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API)
+ __v4l2_link_validate_get_streams(pad, streams_mask);
+#else
+ /* This shouldn't happen */
+ *streams_mask = 0;
+#endif
+}
+
+static int v4l2_subdev_link_validate_locked(struct media_link *link)
+{
+ struct v4l2_subdev *sink_subdev =
+ media_entity_to_v4l2_subdev(link->sink->entity);
+ struct device *dev = sink_subdev->entity.graph_obj.mdev->dev;
+ u64 source_streams_mask;
+ u64 sink_streams_mask;
+ u64 dangling_sink_streams;
+ u32 stream;
+ int ret;
+
+ dev_dbg(dev, "validating link \"%s\":%u -> \"%s\":%u\n",
+ link->source->entity->name, link->source->index,
+ link->sink->entity->name, link->sink->index);
+
+ v4l2_link_validate_get_streams(link->source, &source_streams_mask);
+ v4l2_link_validate_get_streams(link->sink, &sink_streams_mask);
+
+ /*
+ * It is ok to have more source streams than sink streams as extra
+ * source streams can just be ignored by the receiver, but having extra
+ * sink streams is an error as streams must have a source.
+ */
+ dangling_sink_streams = (source_streams_mask ^ sink_streams_mask) &
+ sink_streams_mask;
+ if (dangling_sink_streams) {
+ dev_err(dev, "Dangling sink streams: mask %#llx\n",
+ dangling_sink_streams);
+ return -EINVAL;
+ }
+
+ /* Validate source and sink stream formats */
+
+ for (stream = 0; stream < sizeof(sink_streams_mask) * 8; ++stream) {
+ struct v4l2_subdev_format sink_fmt, source_fmt;
+
+ if (!(sink_streams_mask & BIT_ULL(stream)))
+ continue;
+
+ dev_dbg(dev, "validating stream \"%s\":%u:%u -> \"%s\":%u:%u\n",
+ link->source->entity->name, link->source->index, stream,
+ link->sink->entity->name, link->sink->index, stream);
+
+ ret = v4l2_subdev_link_validate_get_format(link->source, stream,
+ &source_fmt);
+ if (ret < 0) {
+ dev_dbg(dev,
+ "Failed to get format for \"%s\":%u:%u (but that's ok)\n",
+ link->source->entity->name, link->source->index,
+ stream);
+ continue;
+ }
+
+ ret = v4l2_subdev_link_validate_get_format(link->sink, stream,
+ &sink_fmt);
+ if (ret < 0) {
+ dev_dbg(dev,
+ "Failed to get format for \"%s\":%u:%u (but that's ok)\n",
+ link->sink->entity->name, link->sink->index,
+ stream);
+ continue;
+ }
+
+ /* TODO: add stream number to link_validate() */
+ ret = v4l2_subdev_call(sink_subdev, pad, link_validate, link,
+ &source_fmt, &sink_fmt);
+ if (!ret)
+ continue;
+
+ if (ret != -ENOIOCTLCMD)
+ return ret;
+
+ ret = v4l2_subdev_link_validate_default(sink_subdev, link,
+ &source_fmt, &sink_fmt);
+
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
int v4l2_subdev_link_validate(struct media_link *link)
{
- struct v4l2_subdev *sink;
- struct v4l2_subdev_format sink_fmt, source_fmt;
- int rval;
+ struct v4l2_subdev *source_sd, *sink_sd;
+ struct v4l2_subdev_state *source_state, *sink_state;
+ int ret;
- rval = v4l2_subdev_link_validate_get_format(
- link->source, &source_fmt);
- if (rval < 0)
- return 0;
+ sink_sd = media_entity_to_v4l2_subdev(link->sink->entity);
+ source_sd = media_entity_to_v4l2_subdev(link->source->entity);
- rval = v4l2_subdev_link_validate_get_format(
- link->sink, &sink_fmt);
- if (rval < 0)
- return 0;
+ sink_state = v4l2_subdev_get_unlocked_active_state(sink_sd);
+ source_state = v4l2_subdev_get_unlocked_active_state(source_sd);
- sink = media_entity_to_v4l2_subdev(link->sink->entity);
+ if (sink_state)
+ v4l2_subdev_lock_state(sink_state);
- rval = v4l2_subdev_call(sink, pad, link_validate, link,
- &source_fmt, &sink_fmt);
- if (rval != -ENOIOCTLCMD)
- return rval;
+ if (source_state)
+ v4l2_subdev_lock_state(source_state);
+
+ ret = v4l2_subdev_link_validate_locked(link);
+
+ if (sink_state)
+ v4l2_subdev_unlock_state(sink_state);
- return v4l2_subdev_link_validate_default(
- sink, link, &source_fmt, &sink_fmt);
+ if (source_state)
+ v4l2_subdev_unlock_state(source_state);
+
+ return ret;
}
EXPORT_SYMBOL_GPL(v4l2_subdev_link_validate);
+bool v4l2_subdev_has_pad_interdep(struct media_entity *entity,
+ unsigned int pad0, unsigned int pad1)
+{
+ struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
+ struct v4l2_subdev_krouting *routing;
+ struct v4l2_subdev_state *state;
+ unsigned int i;
+
+ state = v4l2_subdev_lock_and_get_active_state(sd);
+
+ routing = &state->routing;
+
+ for (i = 0; i < routing->num_routes; ++i) {
+ struct v4l2_subdev_route *route = &routing->routes[i];
+
+ if (!(route->flags & V4L2_SUBDEV_ROUTE_FL_ACTIVE))
+ continue;
+
+ if ((route->sink_pad == pad0 && route->source_pad == pad1) ||
+ (route->source_pad == pad0 && route->sink_pad == pad1)) {
+ v4l2_subdev_unlock_state(state);
+ return true;
+ }
+ }
+
+ v4l2_subdev_unlock_state(state);
+
+ return false;
+}
+EXPORT_SYMBOL_GPL(v4l2_subdev_has_pad_interdep);
+
struct v4l2_subdev_state *
__v4l2_subdev_state_alloc(struct v4l2_subdev *sd, const char *lock_name,
struct lock_class_key *lock_key)
@@ -989,7 +1308,8 @@ __v4l2_subdev_state_alloc(struct v4l2_subdev *sd, const char *lock_name,
else
state->lock = &state->_lock;
- if (sd->entity.num_pads) {
+ /* Drivers that support streams do not need the legacy pad config */
+ if (!(sd->flags & V4L2_SUBDEV_FL_STREAMS) && sd->entity.num_pads) {
state->pads = kvcalloc(sd->entity.num_pads,
sizeof(*state->pads), GFP_KERNEL);
if (!state->pads) {
@@ -1028,6 +1348,8 @@ void __v4l2_subdev_state_free(struct v4l2_subdev_state *state)
mutex_destroy(&state->_lock);
+ kfree(state->routing.routes);
+ kvfree(state->stream_configs.configs);
kvfree(state->pads);
kfree(state);
}
@@ -1057,15 +1379,68 @@ EXPORT_SYMBOL_GPL(v4l2_subdev_cleanup);
#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API)
+static int
+v4l2_subdev_init_stream_configs(struct v4l2_subdev_stream_configs *stream_configs,
+ const struct v4l2_subdev_krouting *routing)
+{
+ struct v4l2_subdev_stream_configs new_configs = { 0 };
+ struct v4l2_subdev_route *route;
+ u32 idx;
+
+ /* Count number of formats needed */
+ for_each_active_route(routing, route) {
+ /*
+ * Each route needs a format on both ends of the route.
+ */
+ new_configs.num_configs += 2;
+ }
+
+ if (new_configs.num_configs) {
+ new_configs.configs = kvcalloc(new_configs.num_configs,
+ sizeof(*new_configs.configs),
+ GFP_KERNEL);
+
+ if (!new_configs.configs)
+ return -ENOMEM;
+ }
+
+ /*
+ * Fill in the 'pad' and stream' value for each item in the array from
+ * the routing table
+ */
+ idx = 0;
+
+ for_each_active_route(routing, route) {
+ new_configs.configs[idx].pad = route->sink_pad;
+ new_configs.configs[idx].stream = route->sink_stream;
+
+ idx++;
+
+ new_configs.configs[idx].pad = route->source_pad;
+ new_configs.configs[idx].stream = route->source_stream;
+
+ idx++;
+ }
+
+ kvfree(stream_configs->configs);
+ *stream_configs = new_configs;
+
+ return 0;
+}
+
int v4l2_subdev_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *state,
struct v4l2_subdev_format *format)
{
struct v4l2_mbus_framefmt *fmt;
- if (format->pad >= sd->entity.num_pads)
- return -EINVAL;
+ if (sd->flags & V4L2_SUBDEV_FL_STREAMS)
+ fmt = v4l2_subdev_state_get_stream_format(state, format->pad,
+ format->stream);
+ else if (format->pad < sd->entity.num_pads && format->stream == 0)
+ fmt = v4l2_subdev_get_pad_format(sd, state, format->pad);
+ else
+ fmt = NULL;
- fmt = v4l2_subdev_get_pad_format(sd, state, format->pad);
if (!fmt)
return -EINVAL;
@@ -1075,6 +1450,590 @@ int v4l2_subdev_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *state,
}
EXPORT_SYMBOL_GPL(v4l2_subdev_get_fmt);
+int v4l2_subdev_set_routing(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ const struct v4l2_subdev_krouting *routing)
+{
+ struct v4l2_subdev_krouting *dst = &state->routing;
+ const struct v4l2_subdev_krouting *src = routing;
+ struct v4l2_subdev_krouting new_routing = { 0 };
+ size_t bytes;
+ int r;
+
+ if (unlikely(check_mul_overflow((size_t)src->num_routes,
+ sizeof(*src->routes), &bytes)))
+ return -EOVERFLOW;
+
+ lockdep_assert_held(state->lock);
+
+ if (src->num_routes > 0) {
+ new_routing.routes = kmemdup(src->routes, bytes, GFP_KERNEL);
+ if (!new_routing.routes)
+ return -ENOMEM;
+ }
+
+ new_routing.num_routes = src->num_routes;
+
+ r = v4l2_subdev_init_stream_configs(&state->stream_configs,
+ &new_routing);
+ if (r) {
+ kfree(new_routing.routes);
+ return r;
+ }
+
+ kfree(dst->routes);
+ *dst = new_routing;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(v4l2_subdev_set_routing);
+
+struct v4l2_subdev_route *
+__v4l2_subdev_next_active_route(const struct v4l2_subdev_krouting *routing,
+ struct v4l2_subdev_route *route)
+{
+ if (route)
+ ++route;
+ else
+ route = &routing->routes[0];
+
+ for (; route < routing->routes + routing->num_routes; ++route) {
+ if (!(route->flags & V4L2_SUBDEV_ROUTE_FL_ACTIVE))
+ continue;
+
+ return route;
+ }
+
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(__v4l2_subdev_next_active_route);
+
+int v4l2_subdev_set_routing_with_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_krouting *routing,
+ const struct v4l2_mbus_framefmt *fmt)
+{
+ struct v4l2_subdev_stream_configs *stream_configs;
+ unsigned int i;
+ int ret;
+
+ ret = v4l2_subdev_set_routing(sd, state, routing);
+ if (ret)
+ return ret;
+
+ stream_configs = &state->stream_configs;
+
+ for (i = 0; i < stream_configs->num_configs; ++i)
+ stream_configs->configs[i].fmt = *fmt;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(v4l2_subdev_set_routing_with_fmt);
+
+struct v4l2_mbus_framefmt *
+v4l2_subdev_state_get_stream_format(struct v4l2_subdev_state *state,
+ unsigned int pad, u32 stream)
+{
+ struct v4l2_subdev_stream_configs *stream_configs;
+ unsigned int i;
+
+ lockdep_assert_held(state->lock);
+
+ stream_configs = &state->stream_configs;
+
+ for (i = 0; i < stream_configs->num_configs; ++i) {
+ if (stream_configs->configs[i].pad == pad &&
+ stream_configs->configs[i].stream == stream)
+ return &stream_configs->configs[i].fmt;
+ }
+
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(v4l2_subdev_state_get_stream_format);
+
+struct v4l2_rect *
+v4l2_subdev_state_get_stream_crop(struct v4l2_subdev_state *state,
+ unsigned int pad, u32 stream)
+{
+ struct v4l2_subdev_stream_configs *stream_configs;
+ unsigned int i;
+
+ lockdep_assert_held(state->lock);
+
+ stream_configs = &state->stream_configs;
+
+ for (i = 0; i < stream_configs->num_configs; ++i) {
+ if (stream_configs->configs[i].pad == pad &&
+ stream_configs->configs[i].stream == stream)
+ return &stream_configs->configs[i].crop;
+ }
+
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(v4l2_subdev_state_get_stream_crop);
+
+struct v4l2_rect *
+v4l2_subdev_state_get_stream_compose(struct v4l2_subdev_state *state,
+ unsigned int pad, u32 stream)
+{
+ struct v4l2_subdev_stream_configs *stream_configs;
+ unsigned int i;
+
+ lockdep_assert_held(state->lock);
+
+ stream_configs = &state->stream_configs;
+
+ for (i = 0; i < stream_configs->num_configs; ++i) {
+ if (stream_configs->configs[i].pad == pad &&
+ stream_configs->configs[i].stream == stream)
+ return &stream_configs->configs[i].compose;
+ }
+
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(v4l2_subdev_state_get_stream_compose);
+
+int v4l2_subdev_routing_find_opposite_end(const struct v4l2_subdev_krouting *routing,
+ u32 pad, u32 stream, u32 *other_pad,
+ u32 *other_stream)
+{
+ unsigned int i;
+
+ for (i = 0; i < routing->num_routes; ++i) {
+ struct v4l2_subdev_route *route = &routing->routes[i];
+
+ if (route->source_pad == pad &&
+ route->source_stream == stream) {
+ if (other_pad)
+ *other_pad = route->sink_pad;
+ if (other_stream)
+ *other_stream = route->sink_stream;
+ return 0;
+ }
+
+ if (route->sink_pad == pad && route->sink_stream == stream) {
+ if (other_pad)
+ *other_pad = route->source_pad;
+ if (other_stream)
+ *other_stream = route->source_stream;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(v4l2_subdev_routing_find_opposite_end);
+
+struct v4l2_mbus_framefmt *
+v4l2_subdev_state_get_opposite_stream_format(struct v4l2_subdev_state *state,
+ u32 pad, u32 stream)
+{
+ u32 other_pad, other_stream;
+ int ret;
+
+ ret = v4l2_subdev_routing_find_opposite_end(&state->routing,
+ pad, stream,
+ &other_pad, &other_stream);
+ if (ret)
+ return NULL;
+
+ return v4l2_subdev_state_get_stream_format(state, other_pad,
+ other_stream);
+}
+EXPORT_SYMBOL_GPL(v4l2_subdev_state_get_opposite_stream_format);
+
+u64 v4l2_subdev_state_xlate_streams(const struct v4l2_subdev_state *state,
+ u32 pad0, u32 pad1, u64 *streams)
+{
+ const struct v4l2_subdev_krouting *routing = &state->routing;
+ struct v4l2_subdev_route *route;
+ u64 streams0 = 0;
+ u64 streams1 = 0;
+
+ for_each_active_route(routing, route) {
+ if (route->sink_pad == pad0 && route->source_pad == pad1 &&
+ (*streams & BIT_ULL(route->sink_stream))) {
+ streams0 |= BIT_ULL(route->sink_stream);
+ streams1 |= BIT_ULL(route->source_stream);
+ }
+ if (route->source_pad == pad0 && route->sink_pad == pad1 &&
+ (*streams & BIT_ULL(route->source_stream))) {
+ streams0 |= BIT_ULL(route->source_stream);
+ streams1 |= BIT_ULL(route->sink_stream);
+ }
+ }
+
+ *streams = streams0;
+ return streams1;
+}
+EXPORT_SYMBOL_GPL(v4l2_subdev_state_xlate_streams);
+
+int v4l2_subdev_routing_validate(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_krouting *routing,
+ enum v4l2_subdev_routing_restriction disallow)
+{
+ u32 *remote_pads = NULL;
+ unsigned int i, j;
+ int ret = -EINVAL;
+
+ if (disallow & V4L2_SUBDEV_ROUTING_NO_STREAM_MIX) {
+ remote_pads = kcalloc(sd->entity.num_pads, sizeof(*remote_pads),
+ GFP_KERNEL);
+ if (!remote_pads)
+ return -ENOMEM;
+
+ for (i = 0; i < sd->entity.num_pads; ++i)
+ remote_pads[i] = U32_MAX;
+ }
+
+ for (i = 0; i < routing->num_routes; ++i) {
+ const struct v4l2_subdev_route *route = &routing->routes[i];
+
+ /* Validate the sink and source pad numbers. */
+ if (route->sink_pad >= sd->entity.num_pads ||
+ !(sd->entity.pads[route->sink_pad].flags & MEDIA_PAD_FL_SINK)) {
+ dev_dbg(sd->dev, "route %u sink (%u) is not a sink pad\n",
+ i, route->sink_pad);
+ goto out;
+ }
+
+ if (route->source_pad >= sd->entity.num_pads ||
+ !(sd->entity.pads[route->source_pad].flags & MEDIA_PAD_FL_SOURCE)) {
+ dev_dbg(sd->dev, "route %u source (%u) is not a source pad\n",
+ i, route->source_pad);
+ goto out;
+ }
+
+ /*
+ * V4L2_SUBDEV_ROUTING_NO_STREAM_MIX: Streams on the same pad
+ * may not be routed to streams on different pads.
+ */
+ if (disallow & V4L2_SUBDEV_ROUTING_NO_STREAM_MIX) {
+ if (remote_pads[route->sink_pad] != U32_MAX &&
+ remote_pads[route->sink_pad] != route->source_pad) {
+ dev_dbg(sd->dev,
+ "route %u attempts to mix %s streams\n",
+ i, "sink");
+ goto out;
+ }
+
+ if (remote_pads[route->source_pad] != U32_MAX &&
+ remote_pads[route->source_pad] != route->sink_pad) {
+ dev_dbg(sd->dev,
+ "route %u attempts to mix %s streams\n",
+ i, "source");
+ goto out;
+ }
+
+ remote_pads[route->sink_pad] = route->source_pad;
+ remote_pads[route->source_pad] = route->sink_pad;
+ }
+
+ for (j = i + 1; j < routing->num_routes; ++j) {
+ const struct v4l2_subdev_route *r = &routing->routes[j];
+
+ /*
+ * V4L2_SUBDEV_ROUTING_NO_1_TO_N: No two routes can
+ * originate from the same (sink) stream.
+ */
+ if ((disallow & V4L2_SUBDEV_ROUTING_NO_1_TO_N) &&
+ route->sink_pad == r->sink_pad &&
+ route->sink_stream == r->sink_stream) {
+ dev_dbg(sd->dev,
+ "routes %u and %u originate from same sink (%u/%u)\n",
+ i, j, route->sink_pad,
+ route->sink_stream);
+ goto out;
+ }
+
+ /*
+ * V4L2_SUBDEV_ROUTING_NO_N_TO_1: No two routes can end
+ * at the same (source) stream.
+ */
+ if ((disallow & V4L2_SUBDEV_ROUTING_NO_N_TO_1) &&
+ route->source_pad == r->source_pad &&
+ route->source_stream == r->source_stream) {
+ dev_dbg(sd->dev,
+ "routes %u and %u end at same source (%u/%u)\n",
+ i, j, route->source_pad,
+ route->source_stream);
+ goto out;
+ }
+ }
+ }
+
+ ret = 0;
+
+out:
+ kfree(remote_pads);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(v4l2_subdev_routing_validate);
+
+static int v4l2_subdev_enable_streams_fallback(struct v4l2_subdev *sd, u32 pad,
+ u64 streams_mask)
+{
+ struct device *dev = sd->entity.graph_obj.mdev->dev;
+ unsigned int i;
+ int ret;
+
+ /*
+ * The subdev doesn't implement pad-based stream enable, fall back
+ * on the .s_stream() operation. This can only be done for subdevs that
+ * have a single source pad, as sd->enabled_streams is global to the
+ * subdev.
+ */
+ if (!(sd->entity.pads[pad].flags & MEDIA_PAD_FL_SOURCE))
+ return -EOPNOTSUPP;
+
+ for (i = 0; i < sd->entity.num_pads; ++i) {
+ if (i != pad && sd->entity.pads[i].flags & MEDIA_PAD_FL_SOURCE)
+ return -EOPNOTSUPP;
+ }
+
+ if (sd->enabled_streams & streams_mask) {
+ dev_dbg(dev, "set of streams %#llx already enabled on %s:%u\n",
+ streams_mask, sd->entity.name, pad);
+ return -EALREADY;
+ }
+
+ /* Start streaming when the first streams are enabled. */
+ if (!sd->enabled_streams) {
+ ret = v4l2_subdev_call(sd, video, s_stream, 1);
+ if (ret)
+ return ret;
+ }
+
+ sd->enabled_streams |= streams_mask;
+
+ return 0;
+}
+
+int v4l2_subdev_enable_streams(struct v4l2_subdev *sd, u32 pad,
+ u64 streams_mask)
+{
+ struct device *dev = sd->entity.graph_obj.mdev->dev;
+ struct v4l2_subdev_state *state;
+ u64 found_streams = 0;
+ unsigned int i;
+ int ret;
+
+ /* A few basic sanity checks first. */
+ if (pad >= sd->entity.num_pads)
+ return -EINVAL;
+
+ if (!streams_mask)
+ return 0;
+
+ /* Fallback on .s_stream() if .enable_streams() isn't available. */
+ if (!sd->ops->pad || !sd->ops->pad->enable_streams)
+ return v4l2_subdev_enable_streams_fallback(sd, pad,
+ streams_mask);
+
+ state = v4l2_subdev_lock_and_get_active_state(sd);
+
+ /*
+ * Verify that the requested streams exist and that they are not
+ * already enabled.
+ */
+ for (i = 0; i < state->stream_configs.num_configs; ++i) {
+ struct v4l2_subdev_stream_config *cfg =
+ &state->stream_configs.configs[i];
+
+ if (cfg->pad != pad || !(streams_mask & BIT_ULL(cfg->stream)))
+ continue;
+
+ found_streams |= BIT_ULL(cfg->stream);
+
+ if (cfg->enabled) {
+ dev_dbg(dev, "stream %u already enabled on %s:%u\n",
+ cfg->stream, sd->entity.name, pad);
+ ret = -EALREADY;
+ goto done;
+ }
+ }
+
+ if (found_streams != streams_mask) {
+ dev_dbg(dev, "streams 0x%llx not found on %s:%u\n",
+ streams_mask & ~found_streams, sd->entity.name, pad);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ /* Call the .enable_streams() operation. */
+ ret = v4l2_subdev_call(sd, pad, enable_streams, state, pad,
+ streams_mask);
+ if (ret)
+ goto done;
+
+ /* Mark the streams as enabled. */
+ for (i = 0; i < state->stream_configs.num_configs; ++i) {
+ struct v4l2_subdev_stream_config *cfg =
+ &state->stream_configs.configs[i];
+
+ if (cfg->pad == pad && (streams_mask & BIT_ULL(cfg->stream)))
+ cfg->enabled = true;
+ }
+
+done:
+ v4l2_subdev_unlock_state(state);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(v4l2_subdev_enable_streams);
+
+static int v4l2_subdev_disable_streams_fallback(struct v4l2_subdev *sd, u32 pad,
+ u64 streams_mask)
+{
+ struct device *dev = sd->entity.graph_obj.mdev->dev;
+ unsigned int i;
+ int ret;
+
+ /*
+ * If the subdev doesn't implement pad-based stream enable, fall back
+ * on the .s_stream() operation. This can only be done for subdevs that
+ * have a single source pad, as sd->enabled_streams is global to the
+ * subdev.
+ */
+ if (!(sd->entity.pads[pad].flags & MEDIA_PAD_FL_SOURCE))
+ return -EOPNOTSUPP;
+
+ for (i = 0; i < sd->entity.num_pads; ++i) {
+ if (i != pad && sd->entity.pads[i].flags & MEDIA_PAD_FL_SOURCE)
+ return -EOPNOTSUPP;
+ }
+
+ if ((sd->enabled_streams & streams_mask) != streams_mask) {
+ dev_dbg(dev, "set of streams %#llx already disabled on %s:%u\n",
+ streams_mask, sd->entity.name, pad);
+ return -EALREADY;
+ }
+
+ /* Stop streaming when the last streams are disabled. */
+ if (!(sd->enabled_streams & ~streams_mask)) {
+ ret = v4l2_subdev_call(sd, video, s_stream, 0);
+ if (ret)
+ return ret;
+ }
+
+ sd->enabled_streams &= ~streams_mask;
+
+ return 0;
+}
+
+int v4l2_subdev_disable_streams(struct v4l2_subdev *sd, u32 pad,
+ u64 streams_mask)
+{
+ struct device *dev = sd->entity.graph_obj.mdev->dev;
+ struct v4l2_subdev_state *state;
+ u64 found_streams = 0;
+ unsigned int i;
+ int ret;
+
+ /* A few basic sanity checks first. */
+ if (pad >= sd->entity.num_pads)
+ return -EINVAL;
+
+ if (!streams_mask)
+ return 0;
+
+ /* Fallback on .s_stream() if .disable_streams() isn't available. */
+ if (!sd->ops->pad || !sd->ops->pad->disable_streams)
+ return v4l2_subdev_disable_streams_fallback(sd, pad,
+ streams_mask);
+
+ state = v4l2_subdev_lock_and_get_active_state(sd);
+
+ /*
+ * Verify that the requested streams exist and that they are not
+ * already disabled.
+ */
+ for (i = 0; i < state->stream_configs.num_configs; ++i) {
+ struct v4l2_subdev_stream_config *cfg =
+ &state->stream_configs.configs[i];
+
+ if (cfg->pad != pad || !(streams_mask & BIT_ULL(cfg->stream)))
+ continue;
+
+ found_streams |= BIT_ULL(cfg->stream);
+
+ if (!cfg->enabled) {
+ dev_dbg(dev, "stream %u already disabled on %s:%u\n",
+ cfg->stream, sd->entity.name, pad);
+ ret = -EALREADY;
+ goto done;
+ }
+ }
+
+ if (found_streams != streams_mask) {
+ dev_dbg(dev, "streams 0x%llx not found on %s:%u\n",
+ streams_mask & ~found_streams, sd->entity.name, pad);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ /* Call the .disable_streams() operation. */
+ ret = v4l2_subdev_call(sd, pad, disable_streams, state, pad,
+ streams_mask);
+ if (ret)
+ goto done;
+
+ /* Mark the streams as disabled. */
+ for (i = 0; i < state->stream_configs.num_configs; ++i) {
+ struct v4l2_subdev_stream_config *cfg =
+ &state->stream_configs.configs[i];
+
+ if (cfg->pad == pad && (streams_mask & BIT_ULL(cfg->stream)))
+ cfg->enabled = false;
+ }
+
+done:
+ v4l2_subdev_unlock_state(state);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(v4l2_subdev_disable_streams);
+
+int v4l2_subdev_s_stream_helper(struct v4l2_subdev *sd, int enable)
+{
+ struct v4l2_subdev_state *state;
+ struct v4l2_subdev_route *route;
+ struct media_pad *pad;
+ u64 source_mask = 0;
+ int pad_index = -1;
+
+ /*
+ * Find the source pad. This helper is meant for subdevs that have a
+ * single source pad, so failures shouldn't happen, but catch them
+ * loudly nonetheless as they indicate a driver bug.
+ */
+ media_entity_for_each_pad(&sd->entity, pad) {
+ if (pad->flags & MEDIA_PAD_FL_SOURCE) {
+ pad_index = pad->index;
+ break;
+ }
+ }
+
+ if (WARN_ON(pad_index == -1))
+ return -EINVAL;
+
+ /*
+ * As there's a single source pad, just collect all the source streams.
+ */
+ state = v4l2_subdev_lock_and_get_active_state(sd);
+
+ for_each_active_route(&state->routing, route)
+ source_mask |= BIT_ULL(route->source_stream);
+
+ v4l2_subdev_unlock_state(state);
+
+ if (enable)
+ return v4l2_subdev_enable_streams(sd, pad_index, source_mask);
+ else
+ return v4l2_subdev_disable_streams(sd, pad_index, source_mask);
+}
+EXPORT_SYMBOL_GPL(v4l2_subdev_s_stream_helper);
+
#endif /* CONFIG_VIDEO_V4L2_SUBDEV_API */
#endif /* CONFIG_MEDIA_CONTROLLER */
@@ -1090,6 +2049,7 @@ void v4l2_subdev_init(struct v4l2_subdev *sd, const struct v4l2_subdev_ops *ops)
sd->grp_id = 0;
sd->dev_priv = NULL;
sd->host_priv = NULL;
+ sd->privacy_led = NULL;
#if defined(CONFIG_MEDIA_CONTROLLER)
sd->entity.name = sd->name;
sd->entity.obj_type = MEDIA_ENTITY_TYPE_V4L2_SUBDEV;
@@ -1105,3 +2065,36 @@ void v4l2_subdev_notify_event(struct v4l2_subdev *sd,
v4l2_subdev_notify(sd, V4L2_DEVICE_NOTIFY_EVENT, (void *)ev);
}
EXPORT_SYMBOL_GPL(v4l2_subdev_notify_event);
+
+int v4l2_subdev_get_privacy_led(struct v4l2_subdev *sd)
+{
+#if IS_REACHABLE(CONFIG_LEDS_CLASS)
+ sd->privacy_led = led_get(sd->dev, "privacy-led");
+ if (IS_ERR(sd->privacy_led) && PTR_ERR(sd->privacy_led) != -ENOENT)
+ return dev_err_probe(sd->dev, PTR_ERR(sd->privacy_led),
+ "getting privacy LED\n");
+
+ if (!IS_ERR_OR_NULL(sd->privacy_led)) {
+ mutex_lock(&sd->privacy_led->led_access);
+ led_sysfs_disable(sd->privacy_led);
+ led_trigger_remove(sd->privacy_led);
+ led_set_brightness(sd->privacy_led, 0);
+ mutex_unlock(&sd->privacy_led->led_access);
+ }
+#endif
+ return 0;
+}
+EXPORT_SYMBOL_GPL(v4l2_subdev_get_privacy_led);
+
+void v4l2_subdev_put_privacy_led(struct v4l2_subdev *sd)
+{
+#if IS_REACHABLE(CONFIG_LEDS_CLASS)
+ if (!IS_ERR_OR_NULL(sd->privacy_led)) {
+ mutex_lock(&sd->privacy_led->led_access);
+ led_sysfs_enable(sd->privacy_led);
+ mutex_unlock(&sd->privacy_led->led_access);
+ led_put(sd->privacy_led);
+ }
+#endif
+}
+EXPORT_SYMBOL_GPL(v4l2_subdev_put_privacy_led);
diff --git a/drivers/media/v4l2-core/videobuf-dma-contig.c b/drivers/media/v4l2-core/videobuf-dma-contig.c
index f2c439359557..4c2ec7a0d804 100644
--- a/drivers/media/v4l2-core/videobuf-dma-contig.c
+++ b/drivers/media/v4l2-core/videobuf-dma-contig.c
@@ -314,7 +314,7 @@ static int __videobuf_mmap_mapper(struct videobuf_queue *q,
}
vma->vm_ops = &videobuf_vm_ops;
- vma->vm_flags |= VM_DONTEXPAND;
+ vm_flags_set(vma, VM_DONTEXPAND);
vma->vm_private_data = map;
dev_dbg(q->dev, "mmap %p: q=%p %08lx-%08lx (%lx) pgoff %08lx buf %d\n",
diff --git a/drivers/media/v4l2-core/videobuf-dma-sg.c b/drivers/media/v4l2-core/videobuf-dma-sg.c
index 234e9f647c96..53001532e8e3 100644
--- a/drivers/media/v4l2-core/videobuf-dma-sg.c
+++ b/drivers/media/v4l2-core/videobuf-dma-sg.c
@@ -630,8 +630,8 @@ static int __videobuf_mmap_mapper(struct videobuf_queue *q,
map->count = 1;
map->q = q;
vma->vm_ops = &videobuf_vm_ops;
- vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP;
- vma->vm_flags &= ~VM_IO; /* using shared anonymous pages */
+ /* using shared anonymous pages */
+ vm_flags_mod(vma, VM_DONTEXPAND | VM_DONTDUMP, VM_IO);
vma->vm_private_data = map;
dprintk(1, "mmap %p: q=%p %08lx-%08lx pgoff %08lx bufs %d-%d\n",
map, q, vma->vm_start, vma->vm_end, vma->vm_pgoff, first, last);
diff --git a/drivers/media/v4l2-core/videobuf-vmalloc.c b/drivers/media/v4l2-core/videobuf-vmalloc.c
index 9b2443720ab0..85c7090606d6 100644
--- a/drivers/media/v4l2-core/videobuf-vmalloc.c
+++ b/drivers/media/v4l2-core/videobuf-vmalloc.c
@@ -247,7 +247,7 @@ static int __videobuf_mmap_mapper(struct videobuf_queue *q,
}
vma->vm_ops = &videobuf_vm_ops;
- vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP;
+ vm_flags_set(vma, VM_DONTEXPAND | VM_DONTDUMP);
vma->vm_private_data = map;
dprintk(1, "mmap %p: q=%p %08lx-%08lx (%lx) pgoff %08lx buf %d\n",