* [PATCH 0/12] TW686x driver
@ 2016-01-28 8:29 Krzysztof Hałasa
2016-01-28 8:53 ` [PATCH 1/12] Add support for TW686[4589]-based frame grabbers Krzysztof Hałasa
` (15 more replies)
0 siblings, 16 replies; 19+ messages in thread
From: Krzysztof Hałasa @ 2016-01-28 8:29 UTC (permalink / raw)
To: Hans Verkuil; +Cc: linux-media, Ezequiel Garcia
Hi,
I'm posting a driver for TW686[4589]-based PCIe cards. The first patch
has been posted and reviewed by Ezequiel back in July 2015, the
subsequent patches are changes made in response to the review and/or are
required by the more recent kernel versions.
This driver lacks CMA-based frame mode DMA operation, I'll add it a bit
later. Also:
- I haven't converted the kthread to a workqueue - the driver is
modeled after other code and it can be done later, if needed
- I have skipped suggested PCI ID changes and the 704 vs 720 pixels/line
question - this may need further consideration.
Please merge.
The following changes since Linux 4.4 are available in the git
repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/chris/linux.git techwell-4.4
for you to fetch changes up to 8e495778acd4602c472cefa460a1afb41c4b8f25:
[MEDIA] TW686x: return VB2_BUF_STATE_ERROR frames on timeout/errors (2016-01-27 14:47:41 +0100)
----------------------------------------------------------------
Krzysztof Hałasa (12):
[MEDIA] Add support for TW686[4589]-based frame grabbers
[MEDIA] TW686x: Trivial changes suggested by Ezequiel Garcia
[MEDIA] TW686x: Switch to devm_*()
[MEDIA] TW686x: Fix s_std() / g_std() / g_parm() pointer to self
[MEDIA] TW686x: Fix handling of TV standard values
[MEDIA] TW686x: Fix try_fmt() color space
[MEDIA] TW686x: Add enum_input() / g_input() / s_input()
[MEDIA] TW686x: do not use pci_dma_supported()
[MEDIA] TW686x: switch to vb2_v4l2_buffer
[MEDIA] TW686x: handle non-NULL format in queue_setup()
[MEDIA] TW686x: Track frame sequence numbers
[MEDIA] TW686x: return VB2_BUF_STATE_ERROR frames on timeout/errors
drivers/media/pci/Kconfig | 1 +
drivers/media/pci/Makefile | 1 +
drivers/media/pci/tw686x/Kconfig | 16 ++
drivers/media/pci/tw686x/Makefile | 3 +
drivers/media/pci/tw686x/tw686x-core.c | 140 +++++++++++++
drivers/media/pci/tw686x/tw686x-regs.h | 103 +++++++++
drivers/media/pci/tw686x/tw686x-video.c | 815 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
drivers/media/pci/tw686x/tw686x.h | 118 +++++++++++
8 files changed, 1197 insertions(+)
create mode 100644 drivers/media/pci/tw686x/Kconfig
create mode 100644 drivers/media/pci/tw686x/Makefile
create mode 100644 drivers/media/pci/tw686x/tw686x-core.c
create mode 100644 drivers/media/pci/tw686x/tw686x-regs.h
create mode 100644 drivers/media/pci/tw686x/tw686x-video.c
create mode 100644 drivers/media/pci/tw686x/tw686x.h
Thanks.
--
Krzysztof Halasa
Industrial Research Institute for Automation and Measurements PIAP
Al. Jerozolimskie 202, 02-486 Warsaw, Poland
^ permalink raw reply [flat|nested] 19+ messages in thread
* [PATCH 1/12] Add support for TW686[4589]-based frame grabbers
2016-01-28 8:29 [PATCH 0/12] TW686x driver Krzysztof Hałasa
@ 2016-01-28 8:53 ` Krzysztof Hałasa
2016-01-28 8:57 ` [PATCH 2/12] TW686x: Trivial changes suggested by Ezequiel Garcia Krzysztof Hałasa
` (14 subsequent siblings)
15 siblings, 0 replies; 19+ messages in thread
From: Krzysztof Hałasa @ 2016-01-28 8:53 UTC (permalink / raw)
To: Hans Verkuil; +Cc: linux-media, Ezequiel Garcia
Signed-off-by: Krzysztof Hałasa <khalasa@piap.pl>
diff --git a/drivers/media/pci/Kconfig b/drivers/media/pci/Kconfig
index 48a611b..4f6467f 100644
--- a/drivers/media/pci/Kconfig
+++ b/drivers/media/pci/Kconfig
@@ -14,6 +14,7 @@ source "drivers/media/pci/meye/Kconfig"
source "drivers/media/pci/solo6x10/Kconfig"
source "drivers/media/pci/sta2x11/Kconfig"
source "drivers/media/pci/tw68/Kconfig"
+source "drivers/media/pci/tw686x/Kconfig"
source "drivers/media/pci/zoran/Kconfig"
endif
diff --git a/drivers/media/pci/Makefile b/drivers/media/pci/Makefile
index 5f8aacb..2e54c36 100644
--- a/drivers/media/pci/Makefile
+++ b/drivers/media/pci/Makefile
@@ -25,6 +25,7 @@ obj-$(CONFIG_VIDEO_BT848) += bt8xx/
obj-$(CONFIG_VIDEO_SAA7134) += saa7134/
obj-$(CONFIG_VIDEO_SAA7164) += saa7164/
obj-$(CONFIG_VIDEO_TW68) += tw68/
+obj-$(CONFIG_VIDEO_TW686X) += tw686x/
obj-$(CONFIG_VIDEO_DT3155) += dt3155/
obj-$(CONFIG_VIDEO_MEYE) += meye/
obj-$(CONFIG_STA2X11_VIP) += sta2x11/
diff --git a/drivers/media/pci/tw686x/Kconfig b/drivers/media/pci/tw686x/Kconfig
new file mode 100644
index 0000000..8792a68
--- /dev/null
+++ b/drivers/media/pci/tw686x/Kconfig
@@ -0,0 +1,16 @@
+config VIDEO_TW686X
+ tristate "Intersil/Techwell TW686x Video For Linux"
+ depends on VIDEO_DEV && PCI && VIDEO_V4L2
+ select VIDEOBUF2_DMA_SG
+ help
+ Support for Intersil/Techwell TW686x-based frame grabber cards.
+
+ Currently supported chips:
+ - TW6864 (4 video channels),
+ - TW6865 (4 video channels, not tested, second generation chip),
+ - TW6868 (8 video channels but only 4 first channels using
+ built-in video decoder are supported, not tested),
+ - TW6869 (8 video channels, second generation chip).
+
+ To compile this driver as a module, choose M here: the module
+ will be named tw686x.
diff --git a/drivers/media/pci/tw686x/Makefile b/drivers/media/pci/tw686x/Makefile
new file mode 100644
index 0000000..083b806
--- /dev/null
+++ b/drivers/media/pci/tw686x/Makefile
@@ -0,0 +1,3 @@
+tw686x-objs := tw686x-core.o tw686x-video.o
+
+obj-$(CONFIG_VIDEO_TW686X) += tw686x.o
diff --git a/drivers/media/pci/tw686x/tw686x-core.c b/drivers/media/pci/tw686x/tw686x-core.c
new file mode 100644
index 0000000..aa873c5
--- /dev/null
+++ b/drivers/media/pci/tw686x/tw686x-core.c
@@ -0,0 +1,176 @@
+/*
+ Copyright (C) 2015 Industrial Research Institute for Automation
+ and Measurements PIAP
+
+ Written by Krzysztof Hałasa.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of version 2 of the GNU General Public License
+ as published by the Free Software Foundation.
+*/
+
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include "tw686x.h"
+#include "tw686x-regs.h"
+
+static irqreturn_t tw686x_irq(int irq, void *dev_id)
+{
+ struct tw686x_dev *dev = (struct tw686x_dev *)dev_id;
+ u32 int_status = reg_read(dev, INT_STATUS); /* cleared on read */
+ unsigned long flags;
+ unsigned handled = 0;
+
+ if (int_status) {
+ spin_lock_irqsave(&dev->irq_lock, flags);
+ dev->dma_requests |= int_status;
+ spin_unlock_irqrestore(&dev->irq_lock, flags);
+
+ if (int_status & 0xFF0000FF)
+ handled = tw686x_video_irq(dev);
+ }
+
+ return IRQ_RETVAL(handled);
+}
+
+static int tw686x_probe(struct pci_dev *pci_dev,
+ const struct pci_device_id *pci_id)
+{
+ struct tw686x_dev *dev;
+ int err;
+
+ dev = kzalloc(sizeof(*dev) + (pci_id->driver_data & TYPE_MAX_CHANNELS) *
+ sizeof(dev->video_channels[0]), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+
+ sprintf(dev->name, "TW%04X", pci_dev->device);
+ dev->type = pci_id->driver_data;
+
+ pr_info("%s: PCI %s, IRQ %d, MMIO 0x%lx\n", dev->name,
+ pci_name(pci_dev), pci_dev->irq,
+ (unsigned long)pci_resource_start(pci_dev, 0));
+
+ dev->pci_dev = pci_dev;
+ if (pci_enable_device(pci_dev)) {
+ err = -EIO;
+ goto free_dev;
+ }
+
+ pci_set_master(pci_dev);
+
+ if (!pci_dma_supported(pci_dev, DMA_BIT_MASK(32))) {
+ pr_err("%s: 32-bit PCI DMA not supported\n", dev->name);
+ err = -EIO;
+ goto disable;
+ }
+
+ if (!request_mem_region(pci_resource_start(pci_dev, 0),
+ pci_resource_len(pci_dev, 0), dev->name)) {
+ pr_err("%s: Unable to get MMIO region\n", dev->name);
+ err = -EBUSY;
+ goto disable;
+ }
+
+ dev->mmio = ioremap_nocache(pci_resource_start(pci_dev, 0),
+ pci_resource_len(pci_dev, 0));
+ if (!dev->mmio) {
+ pr_err("%s: Unable to remap MMIO region\n", dev->name);
+ err = -EIO;
+ goto free_region;
+ }
+
+ reg_write(dev, SYS_SOFT_RST, 0x0F); /* Reset all subsystems */
+ mdelay(1);
+
+ reg_write(dev, SRST[0], 0x3F);
+ if (max_channels(dev) > 4)
+ reg_write(dev, SRST[1], 0x3F);
+ reg_write(dev, DMA_CMD, 0);
+ reg_write(dev, DMA_CHANNEL_ENABLE, 0);
+ reg_write(dev, DMA_CHANNEL_TIMEOUT, 0x3EFF0FF0);
+ reg_write(dev, DMA_TIMER_INTERVAL, 0x38000);
+ reg_write(dev, DMA_CONFIG, 0xFFFFFF04);
+
+ spin_lock_init(&dev->irq_lock);
+
+ err = request_irq(pci_dev->irq, tw686x_irq, IRQF_SHARED, dev->name,
+ dev);
+ if (err < 0) {
+ pr_err("%s: Unable to get IRQ\n", dev->name);
+ goto iounmap;
+ }
+
+ err = tw686x_video_init(dev);
+ if (err)
+ goto free_irq;
+
+ pci_set_drvdata(pci_dev, dev);
+ return 0;
+
+free_irq:
+ free_irq(pci_dev->irq, dev);
+iounmap:
+ iounmap(dev->mmio);
+free_region:
+ release_mem_region(pci_resource_start(pci_dev, 0),
+ pci_resource_len(pci_dev, 0));
+disable:
+ pci_disable_device(pci_dev);
+free_dev:
+ kfree(dev);
+ return err;
+}
+
+static void tw686x_remove(struct pci_dev *pci_dev)
+{
+ struct tw686x_dev *dev = pci_get_drvdata(pci_dev);
+
+ tw686x_video_free(dev);
+
+ free_irq(pci_dev->irq, dev);
+ iounmap(dev->mmio);
+ release_mem_region(pci_resource_start(pci_dev, 0),
+ pci_resource_len(pci_dev, 0));
+ pci_disable_device(pci_dev);
+ kfree(dev);
+}
+
+/* driver_data is number of A/V channels */
+static const struct pci_device_id tw686x_pci_tbl[] = {
+ {PCI_DEVICE(0x1797, 0x6864), .driver_data = 4},
+ /* not tested */
+ {PCI_DEVICE(0x1797, 0x6865), .driver_data = 4 | TYPE_SECOND_GEN},
+ /* TW6868 supports 8 A/V channels with an external TW2865 chip -
+ not supported by the driver */
+ {PCI_DEVICE(0x1797, 0x6868), .driver_data = 4}, /* not tested */
+ {PCI_DEVICE(0x1797, 0x6869), .driver_data = 8 | TYPE_SECOND_GEN},
+ {}
+};
+
+static struct pci_driver tw686x_pci_driver = {
+ .name = "TW686x",
+ .id_table = tw686x_pci_tbl,
+ .probe = tw686x_probe,
+ .remove = tw686x_remove,
+};
+
+static int tw686x_init(void)
+{
+ return pci_register_driver(&tw686x_pci_driver);
+}
+
+static void tw686x_exit(void)
+{
+ pci_unregister_driver(&tw686x_pci_driver);
+}
+
+MODULE_DESCRIPTION("Driver for video frame grabber cards based on Intersil/Techwell TW686[4589]");
+MODULE_AUTHOR("Krzysztof Halasa");
+MODULE_LICENSE("GPL v2");
+MODULE_DEVICE_TABLE(pci, tw686x_pci_tbl);
+module_init(tw686x_init);
+module_exit(tw686x_exit);
diff --git a/drivers/media/pci/tw686x/tw686x-regs.h b/drivers/media/pci/tw686x/tw686x-regs.h
new file mode 100644
index 0000000..f9ac413
--- /dev/null
+++ b/drivers/media/pci/tw686x/tw686x-regs.h
@@ -0,0 +1,103 @@
+/* DMA controller registers */
+#define REG8_1(a0) ((const u16[8]){a0, a0 + 1, a0 + 2, a0 + 3, \
+ a0 + 4, a0 + 5, a0 + 6, a0 + 7})
+#define REG8_2(a0) ((const u16[8]){a0, a0 + 2, a0 + 4, a0 + 6, \
+ a0 + 8, a0 + 0xA, a0 + 0xC, a0 + 0xE})
+#define REG8_8(a0) ((const u16[8]){a0, a0 + 8, a0 + 0x10, a0 + 0x18, \
+ a0 + 0x20, a0 + 0x28, a0 + 0x30, a0 + 0x38})
+#define INT_STATUS 0x00
+#define PB_STATUS 0x01
+#define DMA_CMD 0x02
+#define VIDEO_FIFO_STATUS 0x03
+#define VIDEO_CHANNEL_ID 0x04
+#define VIDEO_PARSER_STATUS 0x05
+#define SYS_SOFT_RST 0x06
+#define DMA_PAGE_TABLE0_ADDR ((const u16[8]){0x08, 0xD0, 0xD2, 0xD4, \
+ 0xD6, 0xD8, 0xDA, 0xDC})
+#define DMA_PAGE_TABLE1_ADDR ((const u16[8]){0x09, 0xD1, 0xD3, 0xD5, \
+ 0xD7, 0xD9, 0xDB, 0xDD})
+#define DMA_CHANNEL_ENABLE 0x0a
+#define DMA_CONFIG 0x0b
+#define DMA_TIMER_INTERVAL 0x0c
+#define DMA_CHANNEL_TIMEOUT 0x0d
+#define VDMA_CHANNEL_CONFIG REG8_1(0x10)
+#define ADMA_P_ADDR REG8_2(0x18)
+#define ADMA_B_ADDR REG8_2(0x19)
+#define DMA10_P_ADDR 0x28 /* ??? */
+#define DMA10_B_ADDR 0x29
+#define VIDEO_CONTROL1 0x2A
+#define VIDEO_CONTROL2 0x2B
+#define AUDIO_CONTROL1 0x2C
+#define AUDIO_CONTROL2 0x2D
+#define PHASE_REF 0x2E
+#define GPIO_REG 0x2F
+#define INTL_HBAR_CTRL REG8_1(0x30)
+#define AUDIO_CONTROL3 0x38
+#define VIDEO_FIELD_CTRL REG8_1(0x39)
+#define HSCALER_CTRL REG8_1(0x42)
+#define VIDEO_SIZE REG8_1(0x4A)
+#define VIDEO_SIZE_F2 REG8_1(0x52)
+#define MD_CONF REG8_1(0x60)
+#define MD_INIT REG8_1(0x68)
+#define MD_MAP0 REG8_1(0x70)
+#define VDMA_P_ADDR REG8_8(0x80) /* not used in DMA SG mode */
+#define VDMA_WHP REG8_8(0x81)
+#define VDMA_B_ADDR REG8_8(0x82)
+#define VDMA_F2_P_ADDR REG8_8(0x84)
+#define VDMA_F2_WHP REG8_8(0x85)
+#define VDMA_F2_B_ADDR REG8_8(0x86)
+#define EP_REG_ADDR 0xFE
+#define EP_REG_DATA 0xFF
+
+/* Video decoder registers */
+#define VDREG8(a0) ((const u16[8]){ \
+ a0 + 0x000, a0 + 0x010, a0 + 0x020, a0 + 0x030, \
+ a0 + 0x100, a0 + 0x110, a0 + 0x120, a0 + 0x130})
+#define VIDSTAT VDREG8(0x100)
+#define BRIGHT VDREG8(0x101)
+#define CONTRAST VDREG8(0x102)
+#define SHARPNESS VDREG8(0x103)
+#define SAT_U VDREG8(0x104)
+#define SAT_V VDREG8(0x105)
+#define HUE VDREG8(0x106)
+#define CROP_HI VDREG8(0x107)
+#define VDELAY_LO VDREG8(0x108)
+#define VACTIVE_LO VDREG8(0x109)
+#define HDELAY_LO VDREG8(0x10A)
+#define HACTIVE_LO VDREG8(0x10B)
+#define MVSN VDREG8(0x10C)
+#define STATUS2 VDREG8(0x10C)
+#define SDT VDREG8(0x10E)
+#define SDT_EN VDREG8(0x10F)
+
+#define VSCALE_LO VDREG8(0x144)
+#define SCALE_HI VDREG8(0x145)
+#define HSCALE_LO VDREG8(0x146)
+#define F2CROP_HI VDREG8(0x147)
+#define F2VDELAY_LO VDREG8(0x148)
+#define F2VACTIVE_LO VDREG8(0x149)
+#define F2HDELAY_LO VDREG8(0x14A)
+#define F2HACTIVE_LO VDREG8(0x14B)
+#define F2VSCALE_LO VDREG8(0x14C)
+#define F2SCALE_HI VDREG8(0x14D)
+#define F2HSCALE_LO VDREG8(0x14E)
+#define F2CNT VDREG8(0x14F)
+
+#define VDREG2(a0) ((const u16[2]){a0, a0 + 0x100})
+#define SRST VDREG2(0x180)
+#define ACNTL VDREG2(0x181)
+#define ACNTL2 VDREG2(0x182)
+#define CNTRL1 VDREG2(0x183)
+#define CKHY VDREG2(0x184)
+#define SHCOR VDREG2(0x185)
+#define CORING VDREG2(0x186)
+#define CLMPG VDREG2(0x187)
+#define IAGC VDREG2(0x188)
+#define VCTRL1 VDREG2(0x18F)
+#define MISC1 VDREG2(0x194)
+#define LOOP VDREG2(0x195)
+#define MISC2 VDREG2(0x196)
+
+#define CLMD VDREG2(0x197)
+#define AIGAIN ((const u16[8]){0x1D0, 0x1D1, 0x1D2, 0x1D3, \
+ 0x2D0, 0x2D1, 0x2D2, 0x2D3})
diff --git a/drivers/media/pci/tw686x/tw686x-video.c b/drivers/media/pci/tw686x/tw686x-video.c
new file mode 100644
index 0000000..bf4f12e
--- /dev/null
+++ b/drivers/media/pci/tw686x/tw686x-video.c
@@ -0,0 +1,776 @@
+/*
+ Copyright (C) 2015 Industrial Research Institute for Automation
+ and Measurements PIAP
+
+ Written by Krzysztof Hałasa.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of version 2 of the GNU General Public License
+ as published by the Free Software Foundation.
+*/
+
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-event.h>
+#include "tw686x.h"
+#include "tw686x-regs.h"
+
+#define MAX_SG_ENTRY_SIZE (/* 8192 - 128 */ 4096)
+#define MAX_SG_DESC_COUNT 256 /* PAL 704x576 needs up to 198 4-KB pages */
+
+static const struct tw686x_format formats[] = {
+ {
+ .name = "4:2:2 packed, UYVY", /* aka Y422 */
+ .fourcc = V4L2_PIX_FMT_UYVY,
+ .mode = 0,
+ .depth = 16,
+ }, {
+#if 0
+ .name = "4:2:0 packed, YUV",
+ .mode = 1, /* non-standard */
+ .depth = 12,
+ }, {
+ .name = "4:1:1 packed, YUV",
+ .mode = 2, /* non-standard */
+ .depth = 12,
+ }, {
+#endif
+ .name = "4:1:1 packed, YUV",
+ .fourcc = V4L2_PIX_FMT_Y41P,
+ .mode = 3,
+ .depth = 12,
+ }, {
+ .name = "15 bpp RGB",
+ .fourcc = V4L2_PIX_FMT_RGB555,
+ .mode = 4,
+ .depth = 16,
+ }, {
+ .name = "16 bpp RGB",
+ .fourcc = V4L2_PIX_FMT_RGB565,
+ .mode = 5,
+ .depth = 16,
+ }, {
+ .name = "4:2:2 packed, YUYV",
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .mode = 6,
+ .depth = 16,
+ }
+ /* mode 7 is "reserved" */
+};
+
+static const v4l2_std_id video_standards[7] = {
+ V4L2_STD_NTSC,
+ V4L2_STD_PAL,
+ V4L2_STD_SECAM,
+ V4L2_STD_NTSC_443,
+ V4L2_STD_PAL_M,
+ V4L2_STD_PAL_N,
+ V4L2_STD_PAL_60,
+};
+
+static const struct tw686x_format *format_by_fourcc(unsigned fourcc)
+{
+ unsigned cnt;
+
+ for (cnt = 0; cnt < ARRAY_SIZE(formats); cnt++)
+ if (formats[cnt].fourcc == fourcc)
+ return &formats[cnt];
+ return NULL;
+}
+
+/* video queue operations */
+
+static int tw686x_queue_setup(struct vb2_queue *vq,
+ const struct v4l2_format *v4l_fmt,
+ unsigned int *nbuffers, unsigned int *nplanes,
+ unsigned int sizes[], void *alloc_ctxs[])
+{
+ struct tw686x_video_channel *vc = vb2_get_drv_priv(vq);
+
+ sizes[0] = vc->width * vc->height * vc->format->depth / 8;
+ alloc_ctxs[0] = vc->alloc_ctx;
+ *nplanes = 1; /* packed formats only */
+ if (*nbuffers < 2)
+ *nbuffers = 2;
+
+ return 0;
+}
+
+static void tw686x_buf_queue(struct vb2_buffer *vb)
+{
+ struct tw686x_video_channel *vc = vb2_get_drv_priv(vb->vb2_queue);
+ struct tw686x_vb2_buf *buf;
+
+ buf = container_of(vb, struct tw686x_vb2_buf, vb);
+
+ spin_lock(&vc->qlock);
+ list_add_tail(&buf->list, &vc->vidq_queued);
+ spin_unlock(&vc->qlock);
+}
+
+static void setup_descs(struct tw686x_video_channel *vc, unsigned n)
+{
+loop:
+ while (!list_empty(&vc->vidq_queued)) {
+ struct vdma_desc *descs = vc->sg_descs[n];
+ struct tw686x_vb2_buf *buf;
+ struct sg_table *vbuf;
+ struct scatterlist *sg;
+ unsigned buf_len, count = 0;
+ int i;
+
+ buf = list_first_entry(&vc->vidq_queued, struct tw686x_vb2_buf,
+ list);
+ list_del(&buf->list);
+
+ buf_len = vc->width * vc->height * vc->format->depth / 8;
+ if (vb2_plane_size(&buf->vb, 0) < buf_len) {
+ pr_err("Video buffer size too small\n");
+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+ goto loop; /* try another */
+ }
+
+ vbuf = vb2_dma_sg_plane_desc(&buf->vb, 0);
+ for_each_sg(vbuf->sgl, sg, vbuf->nents, i) {
+ dma_addr_t phys = sg_dma_address(sg);
+ unsigned len = sg_dma_len(sg);
+
+ while (len && buf_len) {
+ unsigned entry_len = min_t(unsigned, len,
+ MAX_SG_ENTRY_SIZE);
+ entry_len = min(entry_len, buf_len);
+ if (count == MAX_SG_DESC_COUNT) {
+ pr_err("Video buffer size too fragmented\n");
+ vb2_buffer_done(&buf->vb,
+ VB2_BUF_STATE_ERROR);
+ goto loop;
+ }
+ descs[count].phys = cpu_to_le32(phys);
+ descs[count++].flags_length =
+ cpu_to_le32(0x40000000 /* available */ |
+ entry_len);
+ phys += entry_len;
+ len -= entry_len;
+ buf_len -= entry_len;
+ }
+ if (!buf_len)
+ break;
+ }
+
+ /* clear the remaining entries */
+ while (count < MAX_SG_DESC_COUNT) {
+ descs[count].phys = 0;
+ descs[count++].flags_length = 0; /* unavailable */
+ }
+
+ buf->vb.state = VB2_BUF_STATE_ACTIVE;
+ vc->curr_bufs[n] = buf;
+ return;
+ }
+ vc->curr_bufs[n] = NULL;
+}
+
+/* On TW6864 and TW6868, all channels share the pair of video DMA SG tables,
+ with 10-bit start_idx and end_idx determining start and end of frame buffer
+ for particular channel.
+ TW6868 with all its 8 channels would be problematic (only 127 SG entries per
+ channel) but we support only 4 channels on this chip anyway (the first
+ 4 channels are driven with internal video decoder, the other 4 would require
+ an external TW286x part).
+
+ On TW6865 and TW6869, each channel has its own DMA SG table, with indexes
+ starting with 0. Both chips have complete sets of internal video decoders
+ (respectively 4 or 8-channel).
+
+ All chips have separate SG tables for two video frames. */
+
+static void setup_dma_cfg(struct tw686x_video_channel *vc)
+{
+ unsigned field_width = 704;
+ unsigned field_height = (vc->video_standard & V4L2_STD_625_50) ?
+ 288 : 240;
+ unsigned start_idx = is_second_gen(vc->dev) ? 0 :
+ vc->ch * MAX_SG_DESC_COUNT;
+ unsigned end_idx = start_idx + MAX_SG_DESC_COUNT - 1;
+ u32 dma_cfg = (0 << 30) /* input selection */ |
+ (1 << 29) /* field2 dropped (if any) */ |
+ ((vc->height < 300) << 28) /* field dropping */ |
+ (1 << 27) /* master */ |
+ (0 << 25) /* master channel (for slave only) */ |
+ (0 << 24) /* (no) vertical (line) decimation */ |
+ ((vc->width < 400) << 23) /* horizontal decimation */ |
+ (vc->format->mode << 20) /* output video format */ |
+ (end_idx << 10) /* DMA end index */ |
+ start_idx /* DMA start index */;
+ u32 reg;
+
+ reg_write(vc->dev, VDMA_CHANNEL_CONFIG[vc->ch], dma_cfg);
+ reg_write(vc->dev, VIDEO_SIZE[vc->ch], (1 << 31) | (field_height << 16)
+ | field_width);
+ reg = reg_read(vc->dev, VIDEO_CONTROL1);
+ reg_write(vc->dev, VIDEO_CONTROL1, reg);
+}
+
+static int tw686x_start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+ struct tw686x_video_channel *vc = vb2_get_drv_priv(vq);
+ struct tw686x_dev *dev = vc->dev;
+ u32 dma_ch_mask;
+ unsigned n;
+
+ setup_dma_cfg(vc);
+
+ /* queue video buffers if available */
+ spin_lock(&vc->qlock);
+ for (n = 0; n < 2; n++)
+ setup_descs(vc, n);
+ spin_unlock(&vc->qlock);
+
+ dev->video_active |= 1 << vc->ch;
+ dma_ch_mask = reg_read(dev, DMA_CHANNEL_ENABLE) | (1 << vc->ch);
+ reg_write(dev, DMA_CHANNEL_ENABLE, dma_ch_mask);
+ reg_write(dev, DMA_CMD, (1 << 31) | dma_ch_mask);
+ return 0;
+}
+
+static void tw686x_stop_streaming(struct vb2_queue *vq)
+{
+ struct tw686x_video_channel *vc = vb2_get_drv_priv(vq);
+ struct tw686x_dev *dev = vc->dev;
+ u32 dma_ch_mask = reg_read(dev, DMA_CHANNEL_ENABLE);
+ u32 dma_cmd = reg_read(dev, DMA_CMD);
+ unsigned n;
+
+ dma_ch_mask &= ~(1 << vc->ch);
+ reg_write(dev, DMA_CHANNEL_ENABLE, dma_ch_mask);
+
+ dev->video_active &= ~(1 << vc->ch);
+
+ dma_cmd &= ~(1 << vc->ch);
+ reg_write(dev, DMA_CMD, dma_cmd);
+
+ if (!dev->video_active) {
+ reg_write(dev, DMA_CMD, 0);
+ reg_write(dev, DMA_CHANNEL_ENABLE, 0);
+ }
+
+ spin_lock(&vc->qlock);
+ while (!list_empty(&vc->vidq_queued)) {
+ struct tw686x_vb2_buf *buf;
+
+ buf = list_entry(vc->vidq_queued.next, struct tw686x_vb2_buf,
+ list);
+ list_del(&buf->list);
+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+ }
+
+ for (n = 0; n < 2; n++)
+ if (vc->curr_bufs[n])
+ vb2_buffer_done(&vc->curr_bufs[n]->vb,
+ VB2_BUF_STATE_ERROR);
+
+ spin_unlock(&vc->qlock);
+}
+
+static struct vb2_ops tw686x_video_qops = {
+ .queue_setup = tw686x_queue_setup,
+ .buf_queue = tw686x_buf_queue,
+ .start_streaming = tw686x_start_streaming,
+ .stop_streaming = tw686x_stop_streaming,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
+};
+
+static int tw686x_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct tw686x_video_channel *vc;
+ struct tw686x_dev *dev;
+ unsigned ch;
+
+ vc = container_of(ctrl->handler, struct tw686x_video_channel,
+ ctrl_handler);
+ dev = vc->dev;
+ ch = vc->ch;
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ reg_write(dev, BRIGHT[ch], ctrl->val & 0xFF);
+ return 0;
+
+ case V4L2_CID_CONTRAST:
+ reg_write(dev, CONTRAST[ch], ctrl->val);
+ return 0;
+
+ case V4L2_CID_SATURATION:
+ reg_write(dev, SAT_U[ch], ctrl->val);
+ reg_write(dev, SAT_V[ch], ctrl->val);
+ return 0;
+
+ case V4L2_CID_HUE:
+ reg_write(dev, HUE[ch], ctrl->val & 0xFF);
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static const struct v4l2_ctrl_ops ctrl_ops = {
+ .s_ctrl = tw686x_s_ctrl,
+};
+
+static int tw686x_g_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct tw686x_video_channel *vc = video_drvdata(file);
+
+ f->fmt.pix.width = vc->width;
+ f->fmt.pix.height = vc->height;
+ f->fmt.pix.field = vc->field;
+ f->fmt.pix.pixelformat = vc->format->fourcc;
+ f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
+ f->fmt.pix.bytesperline = f->fmt.pix.width * vc->format->depth / 8;
+ f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
+ return 0;
+}
+
+static int tw686x_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct tw686x_video_channel *vc = video_drvdata(file);
+ const struct tw686x_format *format;
+ unsigned width, height, height_div = 1;
+
+ format = format_by_fourcc(f->fmt.pix.pixelformat);
+ if (!format) {
+ format = &formats[0];
+ f->fmt.pix.pixelformat = format->fourcc;
+ }
+
+ width = 704;
+ if (f->fmt.pix.width < width * 3 / 4 /* halfway */)
+ width /= 2;
+
+ height = (vc->video_standard & V4L2_STD_625_50) ? 576 : 480;
+ if (f->fmt.pix.height < height * 3 / 4 /* halfway */)
+ height_div = 2;
+
+ switch (f->fmt.pix.field) {
+ case V4L2_FIELD_TOP:
+ case V4L2_FIELD_BOTTOM:
+ height_div = 2;
+ break;
+ case V4L2_FIELD_SEQ_BT:
+ if (height_div > 1)
+ f->fmt.pix.field = V4L2_FIELD_BOTTOM;
+ break;
+ default:
+ if (height_div > 1)
+ f->fmt.pix.field = V4L2_FIELD_TOP;
+ else
+ f->fmt.pix.field = V4L2_FIELD_SEQ_TB;
+ }
+ height /= height_div;
+
+ f->fmt.pix.width = width;
+ f->fmt.pix.height = height;
+ f->fmt.pix.bytesperline = f->fmt.pix.width * format->depth / 8;
+ f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
+
+ return 0;
+}
+
+static int tw686x_s_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct tw686x_video_channel *vc = video_drvdata(file);
+ int err;
+
+ err = tw686x_try_fmt_vid_cap(file, priv, f);
+ if (err)
+ return err;
+
+ vc->format = format_by_fourcc(f->fmt.pix.pixelformat);
+ vc->field = f->fmt.pix.field;
+ vc->width = f->fmt.pix.width;
+ vc->height = f->fmt.pix.height;
+ return 0;
+}
+
+static int tw686x_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct tw686x_video_channel *vc = video_drvdata(file);
+ struct tw686x_dev *dev = vc->dev;
+
+ strcpy(cap->driver, "TW686x");
+ strcpy(cap->card, dev->name);
+ sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci_dev));
+ cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+ cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
+ return 0;
+}
+
+static int tw686x_s_std(struct file *file, void *priv, v4l2_std_id id)
+{
+ struct tw686x_video_channel *vc = priv;
+ unsigned std, count = 0;
+ u32 sdt, std_mask = 0;
+
+ for (std = 0; std > ARRAY_SIZE(video_standards); std++)
+ if (id & video_standards[std]) {
+ sdt = std;
+ std_mask |= 1 << std;
+ count++;
+ }
+
+ if (count != 1)
+ return -EINVAL; /* must request exactly one standard */
+
+ reg_write(vc->dev, SDT[vc->ch], sdt);
+ vc->video_standard = video_standards[sdt];
+ return 0;
+}
+
+static int tw686x_g_std(struct file *file, void *priv, v4l2_std_id *id)
+{
+ struct tw686x_video_channel *vc = priv;
+
+ *id = vc->video_standard;
+ return 0;
+}
+
+static int tw686x_enum_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ if (f->index >= ARRAY_SIZE(formats))
+ return -EINVAL;
+
+ strlcpy(f->description, formats[f->index].name, sizeof(f->description));
+ f->pixelformat = formats[f->index].fourcc;
+ return 0;
+}
+
+static int tw686x_g_parm(struct file *file, void *priv,
+ struct v4l2_streamparm *sp)
+{
+ struct tw686x_video_channel *vc = priv;
+
+ if (sp->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ memset(&sp->parm.capture, 0, sizeof(sp->parm.capture));
+ sp->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
+ v4l2_video_std_frame_period(vc->video_standard,
+ &sp->parm.capture.timeperframe);
+
+ return 0;
+}
+
+const struct v4l2_file_operations tw686x_video_fops = {
+ .owner = THIS_MODULE,
+ .open = v4l2_fh_open,
+ .unlocked_ioctl = video_ioctl2,
+ .release = vb2_fop_release,
+ .poll = vb2_fop_poll,
+ .read = vb2_fop_read,
+ .mmap = vb2_fop_mmap,
+};
+
+const struct v4l2_ioctl_ops tw686x_video_ioctl_ops = {
+ .vidioc_querycap = tw686x_querycap,
+ .vidioc_enum_fmt_vid_cap = tw686x_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = tw686x_g_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = tw686x_s_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = tw686x_try_fmt_vid_cap,
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_create_bufs = vb2_ioctl_create_bufs,
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
+ .vidioc_g_std = tw686x_g_std,
+ .vidioc_s_std = tw686x_s_std,
+ .vidioc_g_parm = tw686x_g_parm,
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+static int video_thread(void *arg)
+{
+ struct tw686x_dev *dev = arg;
+ DECLARE_WAITQUEUE(wait, current);
+
+ set_freezable();
+ add_wait_queue(&dev->video_thread_wait, &wait);
+
+ while (1) {
+ long timeout = schedule_timeout_interruptible(HZ);
+ unsigned ch;
+
+ if (timeout == -ERESTARTSYS || kthread_should_stop())
+ break;
+
+ for (ch = 0; ch < max_channels(dev); ch++) {
+ struct tw686x_video_channel *vc;
+ unsigned long flags;
+ u32 request, n;
+
+ vc = &dev->video_channels[ch];
+ if (!(dev->video_active & (1 << ch)))
+ continue;
+
+ spin_lock_irq(&dev->irq_lock);
+ request = dev->dma_requests & (0x01000001 << ch);
+ if (request)
+ dev->dma_requests &= ~request;
+ spin_unlock_irq(&dev->irq_lock);
+
+ if (!request)
+ continue;
+
+ request >>= ch;
+
+ /* handle channel events */
+ if ((request & 0x01000000) |
+ (reg_read(dev, VIDEO_FIFO_STATUS) & 0x01010001) |
+ (reg_read(dev, VIDEO_PARSER_STATUS) & 0x00000101)) {
+ /* DMA Errors - reset channel */
+ u32 reg;
+
+ spin_lock_irqsave(&dev->irq_lock, flags);
+ reg = reg_read(dev, DMA_CMD);
+ /* Reset DMA channel */
+ reg_write(dev, DMA_CMD, reg & ~(1 << ch));
+ reg_write(dev, DMA_CMD, reg);
+ spin_unlock_irqrestore(&dev->irq_lock, flags);
+ } else {
+ /* handle video stream */
+ mutex_lock(&vc->vb_mutex);
+ spin_lock(&vc->qlock);
+ n = !!(reg_read(dev, PB_STATUS) & (1 << ch));
+ if (vc->curr_bufs[n]) {
+ struct vb2_buffer *vb;
+
+ vb = &vc->curr_bufs[n]->vb;
+ v4l2_get_timestamp(&vb->v4l2_buf.timestamp);
+ vb->v4l2_buf.field = vc->field;
+ vb2_set_plane_payload(vb, 0, vc->width * vc->height * vc->format->depth / 8);
+ vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
+ }
+ setup_descs(vc, n);
+ spin_unlock(&vc->qlock);
+ mutex_unlock(&vc->vb_mutex);
+ }
+ }
+ try_to_freeze();
+ }
+
+ remove_wait_queue(&dev->video_thread_wait, &wait);
+ return 0;
+}
+
+int tw686x_video_irq(struct tw686x_dev *dev)
+{
+ unsigned long flags, handled = 0;
+ u32 requests;
+
+ spin_lock_irqsave(&dev->irq_lock, flags);
+ requests = dev->dma_requests;
+ spin_unlock_irqrestore(&dev->irq_lock, flags);
+
+ if (dev->dma_requests & dev->video_active) {
+ wake_up_interruptible_all(&dev->video_thread_wait);
+ handled = 1;
+ }
+ return handled;
+}
+
+void tw686x_video_free(struct tw686x_dev *dev)
+{
+ unsigned ch, n;
+
+ if (dev->video_thread)
+ kthread_stop(dev->video_thread);
+
+ for (ch = 0; ch < max_channels(dev); ch++) {
+ struct tw686x_video_channel *vc = &dev->video_channels[ch];
+
+ v4l2_ctrl_handler_free(&vc->ctrl_handler);
+ if (vc->device)
+ video_unregister_device(vc->device);
+ vb2_dma_sg_cleanup_ctx(vc->alloc_ctx);
+ for (n = 0; n < 2; n++) {
+ struct dma_desc *descs = &vc->sg_tables[n];
+
+ if (descs->virt)
+ pci_free_consistent(dev->pci_dev, descs->size,
+ descs->virt, descs->phys);
+ }
+ }
+
+ v4l2_device_unregister(&dev->v4l2_dev);
+}
+
+#define SG_TABLE_SIZE (MAX_SG_DESC_COUNT * sizeof(struct vdma_desc))
+
+int tw686x_video_init(struct tw686x_dev *dev)
+{
+ unsigned ch, n;
+ int err;
+
+ init_waitqueue_head(&dev->video_thread_wait);
+
+ err = v4l2_device_register(&dev->pci_dev->dev, &dev->v4l2_dev);
+ if (err)
+ return err;
+
+ reg_write(dev, PHASE_REF, 0x00001518); /* Scatter-gather DMA mode */
+
+ /* setup required SG table sizes */
+ for (n = 0; n < 2; n++)
+ if (is_second_gen(dev)) {
+ /* TW 6865, TW6869 - each channel needs a pair of
+ descriptor tables */
+ for (ch = 0; ch < max_channels(dev); ch++)
+ dev->video_channels[ch].sg_tables[n].size =
+ SG_TABLE_SIZE;
+
+ } else
+ /* TW 6864, TW6868 - we need to allocate a pair of
+ descriptor tables, common for all channels.
+ Each table will be bigger than 4 KB. */
+ dev->video_channels[0].sg_tables[n].size =
+ max_channels(dev) * SG_TABLE_SIZE;
+
+ /* allocate SG tables and initialize video channels */
+ for (ch = 0; ch < max_channels(dev); ch++) {
+ struct tw686x_video_channel *vc = &dev->video_channels[ch];
+ struct video_device *vdev;
+
+ mutex_init(&vc->vb_mutex);
+ spin_lock_init(&vc->qlock);
+ INIT_LIST_HEAD(&vc->vidq_queued);
+
+ vc->dev = dev;
+ vc->ch = ch;
+
+ /* default settings */
+ vc->format = &formats[0];
+ vc->video_standard = V4L2_STD_PAL;
+ vc->field = V4L2_FIELD_SEQ_TB;
+ vc->width = 704;
+ vc->height = 576;
+
+ for (n = 0; n < 2; n++) {
+ void *cpu;
+
+ if (vc->sg_tables[n].size) {
+ unsigned reg = n ? DMA_PAGE_TABLE1_ADDR[ch] :
+ DMA_PAGE_TABLE0_ADDR[ch];
+
+ cpu = pci_alloc_consistent(dev->pci_dev,
+ vc->sg_tables[n].size,
+ &vc->sg_tables[n].phys);
+ if (!cpu) {
+ pr_err("Error allocating video DMA scatter-gather tables\n");
+ err = -ENOMEM;
+ goto error;
+ }
+ vc->sg_tables[n].virt = cpu;
+ reg_write(dev, reg, vc->sg_tables[n].phys);
+ } else
+ cpu = dev->video_channels[0].sg_tables[n].virt +
+ ch * SG_TABLE_SIZE;
+
+ vc->sg_descs[n] = cpu;
+ }
+
+ reg_write(dev, VCTRL1[0], 0x24);
+ reg_write(dev, LOOP[0], 0xA5);
+ if (max_channels(dev) > 4) {
+ reg_write(dev, VCTRL1[1], 0x24);
+ reg_write(dev, LOOP[1], 0xA5);
+ }
+ reg_write(dev, VIDEO_FIELD_CTRL[ch], 0);
+ reg_write(dev, VDELAY_LO[ch], 0x14);
+
+ vdev = video_device_alloc();
+ if (!vdev) {
+ pr_warn("Unable to allocate video device\n");
+ err = -ENOMEM;
+ goto error;
+ }
+
+ vc->alloc_ctx = vb2_dma_sg_init_ctx(&dev->pci_dev->dev);
+ if (IS_ERR(vc->alloc_ctx)) {
+ pr_warn("Unable to initialize DMA scatter-gather context\n");
+ err = PTR_ERR(vc->alloc_ctx);
+ goto error;
+ }
+
+ vc->vidq.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ vc->vidq.io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
+ vc->vidq.drv_priv = vc;
+ vc->vidq.buf_struct_size = sizeof(struct tw686x_vb2_buf);
+ vc->vidq.ops = &tw686x_video_qops;
+ vc->vidq.mem_ops = &vb2_dma_sg_memops;
+ vc->vidq.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ vc->vidq.min_buffers_needed = 2;
+ vc->vidq.lock = &vc->vb_mutex;
+
+ err = vb2_queue_init(&vc->vidq);
+ if (err)
+ goto error;
+
+ strcpy(vdev->name, "TW686x-video");
+ snprintf(vdev->name, sizeof(vdev->name), "%s video", dev->name);
+ vdev->fops = &tw686x_video_fops;
+ vdev->ioctl_ops = &tw686x_video_ioctl_ops;
+ vdev->release = video_device_release;
+ vdev->v4l2_dev = &dev->v4l2_dev;
+ vdev->queue = &vc->vidq;
+ vdev->tvnorms = V4L2_STD_PAL | V4L2_STD_NTSC | V4L2_STD_SECAM |
+ V4L2_STD_PAL_60;
+ vdev->minor = -1;
+ vdev->lock = &vc->vb_mutex;
+
+ dev->video_channels[ch].device = vdev;
+ video_set_drvdata(vdev, vc);
+ err = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
+ if (err < 0)
+ goto error;
+
+ v4l2_ctrl_handler_init(&vc->ctrl_handler,
+ 4 /* number of controls */);
+ vdev->ctrl_handler = &vc->ctrl_handler;
+ v4l2_ctrl_new_std(&vc->ctrl_handler, &ctrl_ops,
+ V4L2_CID_BRIGHTNESS, -128, 127, 1, 0);
+ v4l2_ctrl_new_std(&vc->ctrl_handler, &ctrl_ops,
+ V4L2_CID_CONTRAST, 0, 255, 1, 64);
+ v4l2_ctrl_new_std(&vc->ctrl_handler, &ctrl_ops,
+ V4L2_CID_SATURATION, 0, 255, 1, 128);
+ v4l2_ctrl_new_std(&vc->ctrl_handler, &ctrl_ops, V4L2_CID_HUE,
+ -124, 127, 1, 0);
+ err = vc->ctrl_handler.error;
+ if (err)
+ goto error;
+
+ v4l2_ctrl_handler_setup(&vc->ctrl_handler);
+ }
+
+ dev->video_thread = kthread_run(video_thread, dev, "tw686x_video");
+ if (IS_ERR(dev->video_thread)) {
+ err = PTR_ERR(dev->video_thread);
+ goto error;
+ }
+
+ return 0;
+
+error:
+ tw686x_video_free(dev);
+ return err;
+}
diff --git a/drivers/media/pci/tw686x/tw686x.h b/drivers/media/pci/tw686x/tw686x.h
new file mode 100644
index 0000000..6a147d2
--- /dev/null
+++ b/drivers/media/pci/tw686x/tw686x.h
@@ -0,0 +1,117 @@
+/*
+ Copyright (C) 2015 Industrial Research Institute for Automation
+ and Measurements PIAP
+
+ Written by Krzysztof Hałasa.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of version 2 of the GNU General Public License
+ as published by the Free Software Foundation.
+*/
+
+#include <linux/delay.h>
+#include <linux/freezer.h>
+#include <linux/interrupt.h>
+#include <linux/kthread.h>
+#include <linux/mutex.h>
+#include <linux/pci.h>
+#include <media/videobuf2-dma-sg.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+
+#define TYPE_MAX_CHANNELS 0x0F
+#define TYPE_SECOND_GEN 0x10
+
+struct tw686x_format {
+ char *name;
+ unsigned fourcc;
+ unsigned depth;
+ unsigned mode;
+};
+
+struct dma_desc {
+ dma_addr_t phys;
+ void *virt;
+ unsigned size;
+};
+
+struct vdma_desc {
+ __le32 flags_length; /* 3 MSBits for flags, 13 LSBits for length */
+ __le32 phys;
+};
+
+struct tw686x_vb2_buf {
+ struct vb2_buffer vb;
+ struct list_head list;
+};
+
+struct tw686x_video_channel {
+ struct tw686x_dev *dev;
+
+ struct vb2_queue vidq;
+ struct list_head vidq_queued;
+ struct video_device *device;
+ struct dma_desc sg_tables[2];
+ struct tw686x_vb2_buf *curr_bufs[2];
+ void *alloc_ctx;
+ struct vdma_desc *sg_descs[2];
+
+ struct v4l2_ctrl_handler ctrl_handler;
+ const struct tw686x_format *format;
+ struct mutex vb_mutex;
+ spinlock_t qlock;
+ v4l2_std_id video_standard;
+ unsigned width, height;
+ enum v4l2_field field; /* supported TOP, BOTTOM, SEQ_TB and SEQ_BT */
+ unsigned ch;
+};
+
+/* global device status */
+struct tw686x_dev {
+ spinlock_t irq_lock;
+
+ struct v4l2_device v4l2_dev;
+ struct snd_card *card; /* sound card */
+
+ unsigned video_active; /* active video channel mask */
+
+ char name[32];
+ unsigned type;
+ struct pci_dev *pci_dev;
+ __u32 __iomem *mmio;
+
+ struct task_struct *video_thread;
+ wait_queue_head_t video_thread_wait;
+ u32 dma_requests;
+
+ struct tw686x_video_channel video_channels[0];
+};
+
+static inline uint32_t reg_read(struct tw686x_dev *dev, unsigned reg)
+{
+ return readl(dev->mmio + reg);
+}
+
+static inline void reg_write(struct tw686x_dev *dev, unsigned reg,
+ uint32_t value)
+{
+ writel(value, dev->mmio + reg);
+}
+
+static inline unsigned max_channels(struct tw686x_dev *dev)
+{
+ return dev->type & TYPE_MAX_CHANNELS; /* 4 or 8 channels */
+}
+
+static inline unsigned is_second_gen(struct tw686x_dev *dev)
+{
+ /* each channel has its own DMA SG table */
+ return dev->type & TYPE_SECOND_GEN;
+}
+
+int tw686x_video_irq(struct tw686x_dev *dev);
+int tw686x_video_init(struct tw686x_dev *dev);
+void tw686x_video_free(struct tw686x_dev *dev);
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH 2/12] TW686x: Trivial changes suggested by Ezequiel Garcia
2016-01-28 8:29 [PATCH 0/12] TW686x driver Krzysztof Hałasa
2016-01-28 8:53 ` [PATCH 1/12] Add support for TW686[4589]-based frame grabbers Krzysztof Hałasa
@ 2016-01-28 8:57 ` Krzysztof Hałasa
2016-01-28 9:01 ` [PATCH 3/12] TW686x: Switch to devm_*() Krzysztof Hałasa
` (13 subsequent siblings)
15 siblings, 0 replies; 19+ messages in thread
From: Krzysztof Hałasa @ 2016-01-28 8:57 UTC (permalink / raw)
To: Hans Verkuil; +Cc: linux-media, Ezequiel Garcia
Signed-off-by: Krzysztof Hałasa <khalasa@piap.pl>
diff --git a/drivers/media/pci/tw686x/tw686x-core.c b/drivers/media/pci/tw686x/tw686x-core.c
index aa873c5..f22f485 100644
--- a/drivers/media/pci/tw686x/tw686x-core.c
+++ b/drivers/media/pci/tw686x/tw686x-core.c
@@ -1,13 +1,13 @@
/*
- Copyright (C) 2015 Industrial Research Institute for Automation
- and Measurements PIAP
-
- Written by Krzysztof Hałasa.
-
- This program is free software; you can redistribute it and/or modify it
- under the terms of version 2 of the GNU General Public License
- as published by the Free Software Foundation.
-*/
+ * Copyright (C) 2015 Industrial Research Institute for Automation
+ * and Measurements PIAP
+ *
+ * Written by Krzysztof Hałasa.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
+ */
#include <linux/init.h>
#include <linux/interrupt.h>
@@ -68,15 +68,13 @@ static int tw686x_probe(struct pci_dev *pci_dev,
goto disable;
}
- if (!request_mem_region(pci_resource_start(pci_dev, 0),
- pci_resource_len(pci_dev, 0), dev->name)) {
+ err = pci_request_regions(pci_dev, dev->name);
+ if (err < 0) {
pr_err("%s: Unable to get MMIO region\n", dev->name);
- err = -EBUSY;
goto disable;
}
- dev->mmio = ioremap_nocache(pci_resource_start(pci_dev, 0),
- pci_resource_len(pci_dev, 0));
+ dev->mmio = pci_ioremap_bar(pci_dev, 0);
if (!dev->mmio) {
pr_err("%s: Unable to remap MMIO region\n", dev->name);
err = -EIO;
@@ -158,19 +156,8 @@ static struct pci_driver tw686x_pci_driver = {
.remove = tw686x_remove,
};
-static int tw686x_init(void)
-{
- return pci_register_driver(&tw686x_pci_driver);
-}
-
-static void tw686x_exit(void)
-{
- pci_unregister_driver(&tw686x_pci_driver);
-}
-
MODULE_DESCRIPTION("Driver for video frame grabber cards based on Intersil/Techwell TW686[4589]");
MODULE_AUTHOR("Krzysztof Halasa");
MODULE_LICENSE("GPL v2");
MODULE_DEVICE_TABLE(pci, tw686x_pci_tbl);
-module_init(tw686x_init);
-module_exit(tw686x_exit);
+module_pci_driver(tw686x_pci_driver);
diff --git a/drivers/media/pci/tw686x/tw686x-regs.h b/drivers/media/pci/tw686x/tw686x-regs.h
index f9ac413..33b492b 100644
--- a/drivers/media/pci/tw686x/tw686x-regs.h
+++ b/drivers/media/pci/tw686x/tw686x-regs.h
@@ -16,10 +16,10 @@
0xD6, 0xD8, 0xDA, 0xDC})
#define DMA_PAGE_TABLE1_ADDR ((const u16[8]){0x09, 0xD1, 0xD3, 0xD5, \
0xD7, 0xD9, 0xDB, 0xDD})
-#define DMA_CHANNEL_ENABLE 0x0a
-#define DMA_CONFIG 0x0b
-#define DMA_TIMER_INTERVAL 0x0c
-#define DMA_CHANNEL_TIMEOUT 0x0d
+#define DMA_CHANNEL_ENABLE 0x0A
+#define DMA_CONFIG 0x0B
+#define DMA_TIMER_INTERVAL 0x0C
+#define DMA_CHANNEL_TIMEOUT 0x0D
#define VDMA_CHANNEL_CONFIG REG8_1(0x10)
#define ADMA_P_ADDR REG8_2(0x18)
#define ADMA_B_ADDR REG8_2(0x19)
diff --git a/drivers/media/pci/tw686x/tw686x-video.c b/drivers/media/pci/tw686x/tw686x-video.c
index bf4f12e..5a1b9ab 100644
--- a/drivers/media/pci/tw686x/tw686x-video.c
+++ b/drivers/media/pci/tw686x/tw686x-video.c
@@ -1,13 +1,13 @@
/*
- Copyright (C) 2015 Industrial Research Institute for Automation
- and Measurements PIAP
-
- Written by Krzysztof Hałasa.
-
- This program is free software; you can redistribute it and/or modify it
- under the terms of version 2 of the GNU General Public License
- as published by the Free Software Foundation.
-*/
+ * Copyright (C) 2015 Industrial Research Institute for Automation
+ * and Measurements PIAP
+ *
+ * Written by Krzysztof Hałasa.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
+ */
#include <linux/init.h>
#include <linux/list.h>
diff --git a/drivers/media/pci/tw686x/tw686x.h b/drivers/media/pci/tw686x/tw686x.h
index 6a147d2..8b9d313 100644
--- a/drivers/media/pci/tw686x/tw686x.h
+++ b/drivers/media/pci/tw686x/tw686x.h
@@ -1,13 +1,13 @@
/*
- Copyright (C) 2015 Industrial Research Institute for Automation
- and Measurements PIAP
-
- Written by Krzysztof Hałasa.
-
- This program is free software; you can redistribute it and/or modify it
- under the terms of version 2 of the GNU General Public License
- as published by the Free Software Foundation.
-*/
+ * Copyright (C) 2015 Industrial Research Institute for Automation
+ * and Measurements PIAP
+ *
+ * Written by Krzysztof Hałasa.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
+ */
#include <linux/delay.h>
#include <linux/freezer.h>
^ permalink raw reply related [flat|nested] 19+ messages in thread
* Re: [PATCH 3/12] TW686x: Switch to devm_*()
2016-01-28 8:29 [PATCH 0/12] TW686x driver Krzysztof Hałasa
2016-01-28 8:53 ` [PATCH 1/12] Add support for TW686[4589]-based frame grabbers Krzysztof Hałasa
2016-01-28 8:57 ` [PATCH 2/12] TW686x: Trivial changes suggested by Ezequiel Garcia Krzysztof Hałasa
@ 2016-01-28 9:01 ` Krzysztof Hałasa
2016-01-28 9:01 ` [PATCH 4/12] TW686x: Fix s_std() / g_std() / g_parm() pointer to self Krzysztof Hałasa
` (12 subsequent siblings)
15 siblings, 0 replies; 19+ messages in thread
From: Krzysztof Hałasa @ 2016-01-28 9:01 UTC (permalink / raw)
To: Hans Verkuil; +Cc: linux-media, Ezequiel Garcia
Signed-off-by: Krzysztof Hałasa <khalasa@piap.pl>
diff --git a/drivers/media/pci/tw686x/tw686x-core.c b/drivers/media/pci/tw686x/tw686x-core.c
index f22f485..e2dec02 100644
--- a/drivers/media/pci/tw686x/tw686x-core.c
+++ b/drivers/media/pci/tw686x/tw686x-core.c
@@ -42,8 +42,9 @@ static int tw686x_probe(struct pci_dev *pci_dev,
struct tw686x_dev *dev;
int err;
- dev = kzalloc(sizeof(*dev) + (pci_id->driver_data & TYPE_MAX_CHANNELS) *
- sizeof(dev->video_channels[0]), GFP_KERNEL);
+ dev = devm_kzalloc(&pci_dev->dev,
+ sizeof(*dev) + (pci_id->driver_data & TYPE_MAX_CHANNELS) *
+ sizeof(dev->video_channels[0]), GFP_KERNEL);
if (!dev)
return -ENOMEM;
@@ -55,30 +56,26 @@ static int tw686x_probe(struct pci_dev *pci_dev,
(unsigned long)pci_resource_start(pci_dev, 0));
dev->pci_dev = pci_dev;
- if (pci_enable_device(pci_dev)) {
- err = -EIO;
- goto free_dev;
- }
+ if (pcim_enable_device(pci_dev))
+ return -EIO;
pci_set_master(pci_dev);
if (!pci_dma_supported(pci_dev, DMA_BIT_MASK(32))) {
pr_err("%s: 32-bit PCI DMA not supported\n", dev->name);
- err = -EIO;
- goto disable;
+ return -EIO;
}
err = pci_request_regions(pci_dev, dev->name);
if (err < 0) {
pr_err("%s: Unable to get MMIO region\n", dev->name);
- goto disable;
+ return err;
}
dev->mmio = pci_ioremap_bar(pci_dev, 0);
if (!dev->mmio) {
pr_err("%s: Unable to remap MMIO region\n", dev->name);
- err = -EIO;
- goto free_region;
+ return -EIO;
}
reg_write(dev, SYS_SOFT_RST, 0x0F); /* Reset all subsystems */
@@ -95,32 +92,19 @@ static int tw686x_probe(struct pci_dev *pci_dev,
spin_lock_init(&dev->irq_lock);
- err = request_irq(pci_dev->irq, tw686x_irq, IRQF_SHARED, dev->name,
- dev);
+ err = devm_request_irq(&pci_dev->dev, pci_dev->irq, tw686x_irq,
+ IRQF_SHARED, dev->name, dev);
if (err < 0) {
pr_err("%s: Unable to get IRQ\n", dev->name);
- goto iounmap;
+ return err;
}
err = tw686x_video_init(dev);
if (err)
- goto free_irq;
+ return err;
pci_set_drvdata(pci_dev, dev);
return 0;
-
-free_irq:
- free_irq(pci_dev->irq, dev);
-iounmap:
- iounmap(dev->mmio);
-free_region:
- release_mem_region(pci_resource_start(pci_dev, 0),
- pci_resource_len(pci_dev, 0));
-disable:
- pci_disable_device(pci_dev);
-free_dev:
- kfree(dev);
- return err;
}
static void tw686x_remove(struct pci_dev *pci_dev)
@@ -128,13 +112,6 @@ static void tw686x_remove(struct pci_dev *pci_dev)
struct tw686x_dev *dev = pci_get_drvdata(pci_dev);
tw686x_video_free(dev);
-
- free_irq(pci_dev->irq, dev);
- iounmap(dev->mmio);
- release_mem_region(pci_resource_start(pci_dev, 0),
- pci_resource_len(pci_dev, 0));
- pci_disable_device(pci_dev);
- kfree(dev);
}
/* driver_data is number of A/V channels */
^ permalink raw reply related [flat|nested] 19+ messages in thread
* Re: [PATCH 4/12] TW686x: Fix s_std() / g_std() / g_parm() pointer to self
2016-01-28 8:29 [PATCH 0/12] TW686x driver Krzysztof Hałasa
` (2 preceding siblings ...)
2016-01-28 9:01 ` [PATCH 3/12] TW686x: Switch to devm_*() Krzysztof Hałasa
@ 2016-01-28 9:01 ` Krzysztof Hałasa
2016-01-28 9:03 ` [PATCH 5/12] TW686x: Fix handling of TV standard values Krzysztof Hałasa
` (11 subsequent siblings)
15 siblings, 0 replies; 19+ messages in thread
From: Krzysztof Hałasa @ 2016-01-28 9:01 UTC (permalink / raw)
To: Hans Verkuil; +Cc: linux-media, Ezequiel Garcia
Signed-off-by: Krzysztof Hałasa <khalasa@piap.pl>
diff --git a/drivers/media/pci/tw686x/tw686x-video.c b/drivers/media/pci/tw686x/tw686x-video.c
index 5a1b9ab..78f4f55 100644
--- a/drivers/media/pci/tw686x/tw686x-video.c
+++ b/drivers/media/pci/tw686x/tw686x-video.c
@@ -416,7 +416,7 @@ static int tw686x_querycap(struct file *file, void *priv,
static int tw686x_s_std(struct file *file, void *priv, v4l2_std_id id)
{
- struct tw686x_video_channel *vc = priv;
+ struct tw686x_video_channel *vc = video_drvdata(file);
unsigned std, count = 0;
u32 sdt, std_mask = 0;
@@ -437,7 +437,7 @@ static int tw686x_s_std(struct file *file, void *priv, v4l2_std_id id)
static int tw686x_g_std(struct file *file, void *priv, v4l2_std_id *id)
{
- struct tw686x_video_channel *vc = priv;
+ struct tw686x_video_channel *vc = video_drvdata(file);
*id = vc->video_standard;
return 0;
@@ -457,7 +457,7 @@ static int tw686x_enum_fmt_vid_cap(struct file *file, void *priv,
static int tw686x_g_parm(struct file *file, void *priv,
struct v4l2_streamparm *sp)
{
- struct tw686x_video_channel *vc = priv;
+ struct tw686x_video_channel *vc = video_drvdata(file);
if (sp->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH 5/12] TW686x: Fix handling of TV standard values
2016-01-28 8:29 [PATCH 0/12] TW686x driver Krzysztof Hałasa
` (3 preceding siblings ...)
2016-01-28 9:01 ` [PATCH 4/12] TW686x: Fix s_std() / g_std() / g_parm() pointer to self Krzysztof Hałasa
@ 2016-01-28 9:03 ` Krzysztof Hałasa
2016-01-28 9:04 ` [PATCH 6/12] TW686x: Fix try_fmt() color space Krzysztof Hałasa
` (10 subsequent siblings)
15 siblings, 0 replies; 19+ messages in thread
From: Krzysztof Hałasa @ 2016-01-28 9:03 UTC (permalink / raw)
To: Hans Verkuil; +Cc: linux-media, Ezequiel Garcia
Signed-off-by: Krzysztof Hałasa <khalasa@piap.pl>
diff --git a/drivers/media/pci/tw686x/tw686x-video.c b/drivers/media/pci/tw686x/tw686x-video.c
index 78f4f55..c5d8f28 100644
--- a/drivers/media/pci/tw686x/tw686x-video.c
+++ b/drivers/media/pci/tw686x/tw686x-video.c
@@ -417,18 +417,14 @@ static int tw686x_querycap(struct file *file, void *priv,
static int tw686x_s_std(struct file *file, void *priv, v4l2_std_id id)
{
struct tw686x_video_channel *vc = video_drvdata(file);
- unsigned std, count = 0;
- u32 sdt, std_mask = 0;
-
- for (std = 0; std > ARRAY_SIZE(video_standards); std++)
- if (id & video_standards[std]) {
- sdt = std;
- std_mask |= 1 << std;
- count++;
- }
+ unsigned cnt;
+ u32 sdt = 0; /* default */
- if (count != 1)
- return -EINVAL; /* must request exactly one standard */
+ for (cnt = 0; cnt < ARRAY_SIZE(video_standards); cnt++)
+ if (id & video_standards[cnt]) {
+ sdt = cnt;
+ break;
+ }
reg_write(vc->dev, SDT[vc->ch], sdt);
vc->video_standard = video_standards[sdt];
@@ -658,12 +654,13 @@ int tw686x_video_init(struct tw686x_dev *dev)
vc->dev = dev;
vc->ch = ch;
- /* default settings */
+ /* default settings: NTSC */
vc->format = &formats[0];
- vc->video_standard = V4L2_STD_PAL;
- vc->field = V4L2_FIELD_SEQ_TB;
+ vc->video_standard = V4L2_STD_NTSC;
+ reg_write(vc->dev, SDT[vc->ch], 0);
+ vc->field = V4L2_FIELD_SEQ_BT;
vc->width = 704;
- vc->height = 576;
+ vc->height = 480;
for (n = 0; n < 2; n++) {
void *cpu;
@@ -733,8 +730,7 @@ int tw686x_video_init(struct tw686x_dev *dev)
vdev->release = video_device_release;
vdev->v4l2_dev = &dev->v4l2_dev;
vdev->queue = &vc->vidq;
- vdev->tvnorms = V4L2_STD_PAL | V4L2_STD_NTSC | V4L2_STD_SECAM |
- V4L2_STD_PAL_60;
+ vdev->tvnorms = V4L2_STD_ALL;
vdev->minor = -1;
vdev->lock = &vc->vb_mutex;
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH 6/12] TW686x: Fix try_fmt() color space
2016-01-28 8:29 [PATCH 0/12] TW686x driver Krzysztof Hałasa
` (4 preceding siblings ...)
2016-01-28 9:03 ` [PATCH 5/12] TW686x: Fix handling of TV standard values Krzysztof Hałasa
@ 2016-01-28 9:04 ` Krzysztof Hałasa
2016-01-28 9:05 ` [PATCH 7/12] TW686x: Add enum_input() / g_input() / s_input() Krzysztof Hałasa
` (9 subsequent siblings)
15 siblings, 0 replies; 19+ messages in thread
From: Krzysztof Hałasa @ 2016-01-28 9:04 UTC (permalink / raw)
To: Hans Verkuil; +Cc: linux-media, Ezequiel Garcia
Signed-off-by: Krzysztof Hałasa <khalasa@piap.pl>
diff --git a/drivers/media/pci/tw686x/tw686x-video.c b/drivers/media/pci/tw686x/tw686x-video.c
index c5d8f28..c781b3c 100644
--- a/drivers/media/pci/tw686x/tw686x-video.c
+++ b/drivers/media/pci/tw686x/tw686x-video.c
@@ -379,6 +379,7 @@ static int tw686x_try_fmt_vid_cap(struct file *file, void *priv,
f->fmt.pix.height = height;
f->fmt.pix.bytesperline = f->fmt.pix.width * format->depth / 8;
f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
+ f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
return 0;
}
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH 7/12] TW686x: Add enum_input() / g_input() / s_input()
2016-01-28 8:29 [PATCH 0/12] TW686x driver Krzysztof Hałasa
` (5 preceding siblings ...)
2016-01-28 9:04 ` [PATCH 6/12] TW686x: Fix try_fmt() color space Krzysztof Hałasa
@ 2016-01-28 9:05 ` Krzysztof Hałasa
2016-01-28 9:06 ` [PATCH 8/12] TW686x: do not use pci_dma_supported() Krzysztof Hałasa
` (8 subsequent siblings)
15 siblings, 0 replies; 19+ messages in thread
From: Krzysztof Hałasa @ 2016-01-28 9:05 UTC (permalink / raw)
To: Hans Verkuil; +Cc: linux-media, Ezequiel Garcia
Signed-off-by: Krzysztof Hałasa <khalasa@piap.pl>
diff --git a/drivers/media/pci/tw686x/tw686x-video.c b/drivers/media/pci/tw686x/tw686x-video.c
index c781b3c..21efa30 100644
--- a/drivers/media/pci/tw686x/tw686x-video.c
+++ b/drivers/media/pci/tw686x/tw686x-video.c
@@ -466,6 +466,34 @@ static int tw686x_g_parm(struct file *file, void *priv,
return 0;
}
+static int tw686x_enum_input(struct file *file, void *priv,
+ struct v4l2_input *inp)
+{
+ /* the chip has internal multiplexer, support can be added
+ if the actual hw uses it */
+ if (inp->index)
+ return -EINVAL;
+
+ snprintf(inp->name, sizeof(inp->name), "Composite");
+ inp->type = V4L2_INPUT_TYPE_CAMERA;
+ inp->std = V4L2_STD_ALL;
+ inp->capabilities = V4L2_IN_CAP_STD;
+ return 0;
+}
+
+static int tw686x_g_input(struct file *file, void *priv, unsigned *v)
+{
+ *v = 0;
+ return 0;
+}
+
+static int tw686x_s_input(struct file *file, void *priv, unsigned v)
+{
+ if (v)
+ return -EINVAL;
+ return 0;
+}
+
const struct v4l2_file_operations tw686x_video_fops = {
.owner = THIS_MODULE,
.open = v4l2_fh_open,
@@ -492,6 +520,9 @@ const struct v4l2_ioctl_ops tw686x_video_ioctl_ops = {
.vidioc_g_std = tw686x_g_std,
.vidioc_s_std = tw686x_s_std,
.vidioc_g_parm = tw686x_g_parm,
+ .vidioc_enum_input = tw686x_enum_input,
+ .vidioc_g_input = tw686x_g_input,
+ .vidioc_s_input = tw686x_s_input,
.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
};
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH 8/12] TW686x: do not use pci_dma_supported()
2016-01-28 8:29 [PATCH 0/12] TW686x driver Krzysztof Hałasa
` (6 preceding siblings ...)
2016-01-28 9:05 ` [PATCH 7/12] TW686x: Add enum_input() / g_input() / s_input() Krzysztof Hałasa
@ 2016-01-28 9:06 ` Krzysztof Hałasa
2016-01-28 9:08 ` [PATCH 9/12] TW686x: switch to vb2_v4l2_buffer Krzysztof Hałasa
` (7 subsequent siblings)
15 siblings, 0 replies; 19+ messages in thread
From: Krzysztof Hałasa @ 2016-01-28 9:06 UTC (permalink / raw)
To: Hans Verkuil; +Cc: linux-media, Ezequiel Garcia
Signed-off-by: Krzysztof Hałasa <khalasa@piap.pl>
diff --git a/drivers/media/pci/tw686x/tw686x-core.c b/drivers/media/pci/tw686x/tw686x-core.c
index e2dec02..a0e1d2d 100644
--- a/drivers/media/pci/tw686x/tw686x-core.c
+++ b/drivers/media/pci/tw686x/tw686x-core.c
@@ -61,7 +61,7 @@ static int tw686x_probe(struct pci_dev *pci_dev,
pci_set_master(pci_dev);
- if (!pci_dma_supported(pci_dev, DMA_BIT_MASK(32))) {
+ if (pci_set_dma_mask(pci_dev, DMA_BIT_MASK(32))) {
pr_err("%s: 32-bit PCI DMA not supported\n", dev->name);
return -EIO;
}
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH 9/12] TW686x: switch to vb2_v4l2_buffer
2016-01-28 8:29 [PATCH 0/12] TW686x driver Krzysztof Hałasa
` (7 preceding siblings ...)
2016-01-28 9:06 ` [PATCH 8/12] TW686x: do not use pci_dma_supported() Krzysztof Hałasa
@ 2016-01-28 9:08 ` Krzysztof Hałasa
2016-01-28 9:09 ` [PATCH 10/12] TW686x: handle non-NULL format in queue_setup() Krzysztof Hałasa
` (6 subsequent siblings)
15 siblings, 0 replies; 19+ messages in thread
From: Krzysztof Hałasa @ 2016-01-28 9:08 UTC (permalink / raw)
To: Hans Verkuil; +Cc: linux-media, Ezequiel Garcia
Signed-off-by: Krzysztof Hałasa <khalasa@piap.pl>
diff --git a/drivers/media/pci/tw686x/tw686x-video.c b/drivers/media/pci/tw686x/tw686x-video.c
index 21efa30..12cc108 100644
--- a/drivers/media/pci/tw686x/tw686x-video.c
+++ b/drivers/media/pci/tw686x/tw686x-video.c
@@ -84,8 +84,7 @@ static const struct tw686x_format *format_by_fourcc(unsigned fourcc)
/* video queue operations */
-static int tw686x_queue_setup(struct vb2_queue *vq,
- const struct v4l2_format *v4l_fmt,
+static int tw686x_queue_setup(struct vb2_queue *vq, const void *arg,
unsigned int *nbuffers, unsigned int *nplanes,
unsigned int sizes[], void *alloc_ctxs[])
{
@@ -103,9 +102,10 @@ static int tw686x_queue_setup(struct vb2_queue *vq,
static void tw686x_buf_queue(struct vb2_buffer *vb)
{
struct tw686x_video_channel *vc = vb2_get_drv_priv(vb->vb2_queue);
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
struct tw686x_vb2_buf *buf;
- buf = container_of(vb, struct tw686x_vb2_buf, vb);
+ buf = container_of(vbuf, struct tw686x_vb2_buf, vb);
spin_lock(&vc->qlock);
list_add_tail(&buf->list, &vc->vidq_queued);
@@ -128,13 +128,13 @@ loop:
list_del(&buf->list);
buf_len = vc->width * vc->height * vc->format->depth / 8;
- if (vb2_plane_size(&buf->vb, 0) < buf_len) {
+ if (vb2_plane_size(&buf->vb.vb2_buf, 0) < buf_len) {
pr_err("Video buffer size too small\n");
- vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+ vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
goto loop; /* try another */
}
- vbuf = vb2_dma_sg_plane_desc(&buf->vb, 0);
+ vbuf = vb2_dma_sg_plane_desc(&buf->vb.vb2_buf, 0);
for_each_sg(vbuf->sgl, sg, vbuf->nents, i) {
dma_addr_t phys = sg_dma_address(sg);
unsigned len = sg_dma_len(sg);
@@ -145,7 +145,7 @@ loop:
entry_len = min(entry_len, buf_len);
if (count == MAX_SG_DESC_COUNT) {
pr_err("Video buffer size too fragmented\n");
- vb2_buffer_done(&buf->vb,
+ vb2_buffer_done(&buf->vb.vb2_buf,
VB2_BUF_STATE_ERROR);
goto loop;
}
@@ -167,7 +167,7 @@ loop:
descs[count++].flags_length = 0; /* unavailable */
}
- buf->vb.state = VB2_BUF_STATE_ACTIVE;
+ buf->vb.vb2_buf.state = VB2_BUF_STATE_ACTIVE;
vc->curr_bufs[n] = buf;
return;
}
@@ -265,12 +265,12 @@ static void tw686x_stop_streaming(struct vb2_queue *vq)
buf = list_entry(vc->vidq_queued.next, struct tw686x_vb2_buf,
list);
list_del(&buf->list);
- vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+ vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
}
for (n = 0; n < 2; n++)
if (vc->curr_bufs[n])
- vb2_buffer_done(&vc->curr_bufs[n]->vb,
+ vb2_buffer_done(&vc->curr_bufs[n]->vb.vb2_buf,
VB2_BUF_STATE_ERROR);
spin_unlock(&vc->qlock);
@@ -581,13 +581,13 @@ static int video_thread(void *arg)
spin_lock(&vc->qlock);
n = !!(reg_read(dev, PB_STATUS) & (1 << ch));
if (vc->curr_bufs[n]) {
- struct vb2_buffer *vb;
+ struct vb2_v4l2_buffer *vb;
vb = &vc->curr_bufs[n]->vb;
- v4l2_get_timestamp(&vb->v4l2_buf.timestamp);
- vb->v4l2_buf.field = vc->field;
- vb2_set_plane_payload(vb, 0, vc->width * vc->height * vc->format->depth / 8);
- vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
+ v4l2_get_timestamp(&vb->timestamp);
+ vb->field = vc->field;
+ vb2_set_plane_payload(&vb->vb2_buf, 0, vc->width * vc->height * vc->format->depth / 8);
+ vb2_buffer_done(&vb->vb2_buf, VB2_BUF_STATE_DONE);
}
setup_descs(vc, n);
spin_unlock(&vc->qlock);
diff --git a/drivers/media/pci/tw686x/tw686x.h b/drivers/media/pci/tw686x/tw686x.h
index 8b9d313..a7f1d18 100644
--- a/drivers/media/pci/tw686x/tw686x.h
+++ b/drivers/media/pci/tw686x/tw686x.h
@@ -44,7 +44,7 @@ struct vdma_desc {
};
struct tw686x_vb2_buf {
- struct vb2_buffer vb;
+ struct vb2_v4l2_buffer vb;
struct list_head list;
};
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH 10/12] TW686x: handle non-NULL format in queue_setup()
2016-01-28 8:29 [PATCH 0/12] TW686x driver Krzysztof Hałasa
` (8 preceding siblings ...)
2016-01-28 9:08 ` [PATCH 9/12] TW686x: switch to vb2_v4l2_buffer Krzysztof Hałasa
@ 2016-01-28 9:09 ` Krzysztof Hałasa
2016-01-28 9:10 ` [PATCH 11/12] TW686x: Track frame sequence numbers Krzysztof Hałasa
` (5 subsequent siblings)
15 siblings, 0 replies; 19+ messages in thread
From: Krzysztof Hałasa @ 2016-01-28 9:09 UTC (permalink / raw)
To: Hans Verkuil; +Cc: linux-media, Ezequiel Garcia
Signed-off-by: Krzysztof Hałasa <khalasa@piap.pl>
diff --git a/drivers/media/pci/tw686x/tw686x-video.c b/drivers/media/pci/tw686x/tw686x-video.c
index 12cc108..cfc15e7 100644
--- a/drivers/media/pci/tw686x/tw686x-video.c
+++ b/drivers/media/pci/tw686x/tw686x-video.c
@@ -82,6 +82,50 @@ static const struct tw686x_format *format_by_fourcc(unsigned fourcc)
return NULL;
}
+static void tw686x_get_format(struct tw686x_video_channel *vc,
+ struct v4l2_format *f)
+{
+ const struct tw686x_format *format;
+ unsigned width, height, height_div = 1;
+
+ format = format_by_fourcc(f->fmt.pix.pixelformat);
+ if (!format) {
+ format = &formats[0];
+ f->fmt.pix.pixelformat = format->fourcc;
+ }
+
+ width = 704;
+ if (f->fmt.pix.width < width * 3 / 4 /* halfway */)
+ width /= 2;
+
+ height = (vc->video_standard & V4L2_STD_625_50) ? 576 : 480;
+ if (f->fmt.pix.height < height * 3 / 4 /* halfway */)
+ height_div = 2;
+
+ switch (f->fmt.pix.field) {
+ case V4L2_FIELD_TOP:
+ case V4L2_FIELD_BOTTOM:
+ height_div = 2;
+ break;
+ case V4L2_FIELD_SEQ_BT:
+ if (height_div > 1)
+ f->fmt.pix.field = V4L2_FIELD_BOTTOM;
+ break;
+ default:
+ if (height_div > 1)
+ f->fmt.pix.field = V4L2_FIELD_TOP;
+ else
+ f->fmt.pix.field = V4L2_FIELD_SEQ_TB;
+ }
+ height /= height_div;
+
+ f->fmt.pix.width = width;
+ f->fmt.pix.height = height;
+ f->fmt.pix.bytesperline = f->fmt.pix.width * format->depth / 8;
+ f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
+ f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
+}
+
/* video queue operations */
static int tw686x_queue_setup(struct vb2_queue *vq, const void *arg,
@@ -90,12 +134,17 @@ static int tw686x_queue_setup(struct vb2_queue *vq, const void *arg,
{
struct tw686x_video_channel *vc = vb2_get_drv_priv(vq);
- sizes[0] = vc->width * vc->height * vc->format->depth / 8;
+ if (arg) {
+ struct v4l2_format fmt = *(const struct v4l2_format*)arg;
+ tw686x_get_format(vc, &fmt);
+ sizes[0] = fmt.fmt.pix.sizeimage;
+ } else
+ sizes[0] = vc->width * vc->height * vc->format->depth / 8;
+
alloc_ctxs[0] = vc->alloc_ctx;
*nplanes = 1; /* packed formats only */
if (*nbuffers < 2)
*nbuffers = 2;
-
return 0;
}
@@ -340,47 +389,7 @@ static int tw686x_g_fmt_vid_cap(struct file *file, void *priv,
static int tw686x_try_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
- struct tw686x_video_channel *vc = video_drvdata(file);
- const struct tw686x_format *format;
- unsigned width, height, height_div = 1;
-
- format = format_by_fourcc(f->fmt.pix.pixelformat);
- if (!format) {
- format = &formats[0];
- f->fmt.pix.pixelformat = format->fourcc;
- }
-
- width = 704;
- if (f->fmt.pix.width < width * 3 / 4 /* halfway */)
- width /= 2;
-
- height = (vc->video_standard & V4L2_STD_625_50) ? 576 : 480;
- if (f->fmt.pix.height < height * 3 / 4 /* halfway */)
- height_div = 2;
-
- switch (f->fmt.pix.field) {
- case V4L2_FIELD_TOP:
- case V4L2_FIELD_BOTTOM:
- height_div = 2;
- break;
- case V4L2_FIELD_SEQ_BT:
- if (height_div > 1)
- f->fmt.pix.field = V4L2_FIELD_BOTTOM;
- break;
- default:
- if (height_div > 1)
- f->fmt.pix.field = V4L2_FIELD_TOP;
- else
- f->fmt.pix.field = V4L2_FIELD_SEQ_TB;
- }
- height /= height_div;
-
- f->fmt.pix.width = width;
- f->fmt.pix.height = height;
- f->fmt.pix.bytesperline = f->fmt.pix.width * format->depth / 8;
- f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
- f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
-
+ tw686x_get_format(video_drvdata(file), f);
return 0;
}
@@ -388,12 +397,8 @@ static int tw686x_s_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
struct tw686x_video_channel *vc = video_drvdata(file);
- int err;
-
- err = tw686x_try_fmt_vid_cap(file, priv, f);
- if (err)
- return err;
+ tw686x_get_format(vc, f);
vc->format = format_by_fourcc(f->fmt.pix.pixelformat);
vc->field = f->fmt.pix.field;
vc->width = f->fmt.pix.width;
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH 11/12] TW686x: Track frame sequence numbers
2016-01-28 8:29 [PATCH 0/12] TW686x driver Krzysztof Hałasa
` (9 preceding siblings ...)
2016-01-28 9:09 ` [PATCH 10/12] TW686x: handle non-NULL format in queue_setup() Krzysztof Hałasa
@ 2016-01-28 9:10 ` Krzysztof Hałasa
2016-01-28 9:11 ` [PATCH 12/12] TW686x: return VB2_BUF_STATE_ERROR frames on timeout/errors Krzysztof Hałasa
` (4 subsequent siblings)
15 siblings, 0 replies; 19+ messages in thread
From: Krzysztof Hałasa @ 2016-01-28 9:10 UTC (permalink / raw)
To: Hans Verkuil; +Cc: linux-media, Ezequiel Garcia
Signed-off-by: Krzysztof Hałasa <khalasa@piap.pl>
diff --git a/drivers/media/pci/tw686x/tw686x-video.c b/drivers/media/pci/tw686x/tw686x-video.c
index cfc15e7..d09a4b0 100644
--- a/drivers/media/pci/tw686x/tw686x-video.c
+++ b/drivers/media/pci/tw686x/tw686x-video.c
@@ -280,6 +280,7 @@ static int tw686x_start_streaming(struct vb2_queue *vq, unsigned int count)
spin_unlock(&vc->qlock);
dev->video_active |= 1 << vc->ch;
+ vc->seq = 0;
dma_ch_mask = reg_read(dev, DMA_CHANNEL_ENABLE) | (1 << vc->ch);
reg_write(dev, DMA_CHANNEL_ENABLE, dma_ch_mask);
reg_write(dev, DMA_CMD, (1 << 31) | dma_ch_mask);
@@ -591,6 +592,10 @@ static int video_thread(void *arg)
vb = &vc->curr_bufs[n]->vb;
v4l2_get_timestamp(&vb->timestamp);
vb->field = vc->field;
+ if (V4L2_FIELD_HAS_BOTH(vc->field))
+ vb->sequence = vc->seq++;
+ else
+ vb->sequence = (vc->seq++) / 2;
vb2_set_plane_payload(&vb->vb2_buf, 0, vc->width * vc->height * vc->format->depth / 8);
vb2_buffer_done(&vb->vb2_buf, VB2_BUF_STATE_DONE);
}
diff --git a/drivers/media/pci/tw686x/tw686x.h b/drivers/media/pci/tw686x/tw686x.h
index a7f1d18..540b0ad 100644
--- a/drivers/media/pci/tw686x/tw686x.h
+++ b/drivers/media/pci/tw686x/tw686x.h
@@ -66,6 +66,7 @@ struct tw686x_video_channel {
v4l2_std_id video_standard;
unsigned width, height;
enum v4l2_field field; /* supported TOP, BOTTOM, SEQ_TB and SEQ_BT */
+ unsigned seq; /* video field or frame counter */
unsigned ch;
};
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH 12/12] TW686x: return VB2_BUF_STATE_ERROR frames on timeout/errors
2016-01-28 8:29 [PATCH 0/12] TW686x driver Krzysztof Hałasa
` (10 preceding siblings ...)
2016-01-28 9:10 ` [PATCH 11/12] TW686x: Track frame sequence numbers Krzysztof Hałasa
@ 2016-01-28 9:11 ` Krzysztof Hałasa
2016-01-28 9:13 ` [PATCH 0/12] TW686x driver Krzysztof Hałasa
` (3 subsequent siblings)
15 siblings, 0 replies; 19+ messages in thread
From: Krzysztof Hałasa @ 2016-01-28 9:11 UTC (permalink / raw)
To: Hans Verkuil; +Cc: linux-media, Ezequiel Garcia
Signed-off-by: Krzysztof Hałasa <khalasa@piap.pl>
diff --git a/drivers/media/pci/tw686x/tw686x-video.c b/drivers/media/pci/tw686x/tw686x-video.c
index d09a4b0..bb77c1b 100644
--- a/drivers/media/pci/tw686x/tw686x-video.c
+++ b/drivers/media/pci/tw686x/tw686x-video.c
@@ -551,7 +551,7 @@ static int video_thread(void *arg)
for (ch = 0; ch < max_channels(dev); ch++) {
struct tw686x_video_channel *vc;
unsigned long flags;
- u32 request, n;
+ u32 request, n, stat = VB2_BUF_STATE_DONE;
vc = &dev->video_channels[ch];
if (!(dev->video_active & (1 << ch)))
@@ -581,28 +581,29 @@ static int video_thread(void *arg)
reg_write(dev, DMA_CMD, reg & ~(1 << ch));
reg_write(dev, DMA_CMD, reg);
spin_unlock_irqrestore(&dev->irq_lock, flags);
- } else {
- /* handle video stream */
- mutex_lock(&vc->vb_mutex);
- spin_lock(&vc->qlock);
- n = !!(reg_read(dev, PB_STATUS) & (1 << ch));
- if (vc->curr_bufs[n]) {
- struct vb2_v4l2_buffer *vb;
-
- vb = &vc->curr_bufs[n]->vb;
- v4l2_get_timestamp(&vb->timestamp);
- vb->field = vc->field;
- if (V4L2_FIELD_HAS_BOTH(vc->field))
- vb->sequence = vc->seq++;
- else
- vb->sequence = (vc->seq++) / 2;
- vb2_set_plane_payload(&vb->vb2_buf, 0, vc->width * vc->height * vc->format->depth / 8);
- vb2_buffer_done(&vb->vb2_buf, VB2_BUF_STATE_DONE);
- }
- setup_descs(vc, n);
- spin_unlock(&vc->qlock);
- mutex_unlock(&vc->vb_mutex);
+ stat = VB2_BUF_STATE_ERROR;
+ }
+
+ /* handle video stream */
+ mutex_lock(&vc->vb_mutex);
+ spin_lock(&vc->qlock);
+ n = !!(reg_read(dev, PB_STATUS) & (1 << ch));
+ if (vc->curr_bufs[n]) {
+ struct vb2_v4l2_buffer *vb;
+
+ vb = &vc->curr_bufs[n]->vb;
+ v4l2_get_timestamp(&vb->timestamp);
+ vb->field = vc->field;
+ if (V4L2_FIELD_HAS_BOTH(vc->field))
+ vb->sequence = vc->seq++;
+ else
+ vb->sequence = (vc->seq++) / 2;
+ vb2_set_plane_payload(&vb->vb2_buf, 0, vc->width * vc->height * vc->format->depth / 8);
+ vb2_buffer_done(&vb->vb2_buf, stat);
}
+ setup_descs(vc, n);
+ spin_unlock(&vc->qlock);
+ mutex_unlock(&vc->vb_mutex);
}
try_to_freeze();
}
^ permalink raw reply related [flat|nested] 19+ messages in thread
* Re: [PATCH 0/12] TW686x driver
2016-01-28 8:29 [PATCH 0/12] TW686x driver Krzysztof Hałasa
` (11 preceding siblings ...)
2016-01-28 9:11 ` [PATCH 12/12] TW686x: return VB2_BUF_STATE_ERROR frames on timeout/errors Krzysztof Hałasa
@ 2016-01-28 9:13 ` Krzysztof Hałasa
2016-01-28 9:18 ` [PATCH 3/12] TW686x: Switch to devm_*() Krzysztof Hałasa
` (2 subsequent siblings)
15 siblings, 0 replies; 19+ messages in thread
From: Krzysztof Hałasa @ 2016-01-28 9:13 UTC (permalink / raw)
To: Hans Verkuil; +Cc: linux-media, Ezequiel Garcia
BTW I had to rewrite the tree on git.kernel.org due to missing SOBs
in a part of the commits. It's now fixed.
--
Krzysztof Halasa
Industrial Research Institute for Automation and Measurements PIAP
Al. Jerozolimskie 202, 02-486 Warsaw, Poland
^ permalink raw reply [flat|nested] 19+ messages in thread
* [PATCH 3/12] TW686x: Switch to devm_*()
2016-01-28 8:29 [PATCH 0/12] TW686x driver Krzysztof Hałasa
` (12 preceding siblings ...)
2016-01-28 9:13 ` [PATCH 0/12] TW686x driver Krzysztof Hałasa
@ 2016-01-28 9:18 ` Krzysztof Hałasa
2016-01-28 9:19 ` [PATCH 4/12] TW686x: Fix s_std() / g_std() / g_parm() pointer to self Krzysztof Hałasa
2016-02-08 10:49 ` [PATCH 0/12] TW686x driver Hans Verkuil
15 siblings, 0 replies; 19+ messages in thread
From: Krzysztof Hałasa @ 2016-01-28 9:18 UTC (permalink / raw)
To: Hans Verkuil; +Cc: linux-media, Ezequiel Garcia
Signed-off-by: Krzysztof Hałasa <khalasa@piap.pl>
diff --git a/drivers/media/pci/tw686x/tw686x-core.c b/drivers/media/pci/tw686x/tw686x-core.c
index f22f485..e2dec02 100644
--- a/drivers/media/pci/tw686x/tw686x-core.c
+++ b/drivers/media/pci/tw686x/tw686x-core.c
@@ -42,8 +42,9 @@ static int tw686x_probe(struct pci_dev *pci_dev,
struct tw686x_dev *dev;
int err;
- dev = kzalloc(sizeof(*dev) + (pci_id->driver_data & TYPE_MAX_CHANNELS) *
- sizeof(dev->video_channels[0]), GFP_KERNEL);
+ dev = devm_kzalloc(&pci_dev->dev,
+ sizeof(*dev) + (pci_id->driver_data & TYPE_MAX_CHANNELS) *
+ sizeof(dev->video_channels[0]), GFP_KERNEL);
if (!dev)
return -ENOMEM;
@@ -55,30 +56,26 @@ static int tw686x_probe(struct pci_dev *pci_dev,
(unsigned long)pci_resource_start(pci_dev, 0));
dev->pci_dev = pci_dev;
- if (pci_enable_device(pci_dev)) {
- err = -EIO;
- goto free_dev;
- }
+ if (pcim_enable_device(pci_dev))
+ return -EIO;
pci_set_master(pci_dev);
if (!pci_dma_supported(pci_dev, DMA_BIT_MASK(32))) {
pr_err("%s: 32-bit PCI DMA not supported\n", dev->name);
- err = -EIO;
- goto disable;
+ return -EIO;
}
err = pci_request_regions(pci_dev, dev->name);
if (err < 0) {
pr_err("%s: Unable to get MMIO region\n", dev->name);
- goto disable;
+ return err;
}
dev->mmio = pci_ioremap_bar(pci_dev, 0);
if (!dev->mmio) {
pr_err("%s: Unable to remap MMIO region\n", dev->name);
- err = -EIO;
- goto free_region;
+ return -EIO;
}
reg_write(dev, SYS_SOFT_RST, 0x0F); /* Reset all subsystems */
@@ -95,32 +92,19 @@ static int tw686x_probe(struct pci_dev *pci_dev,
spin_lock_init(&dev->irq_lock);
- err = request_irq(pci_dev->irq, tw686x_irq, IRQF_SHARED, dev->name,
- dev);
+ err = devm_request_irq(&pci_dev->dev, pci_dev->irq, tw686x_irq,
+ IRQF_SHARED, dev->name, dev);
if (err < 0) {
pr_err("%s: Unable to get IRQ\n", dev->name);
- goto iounmap;
+ return err;
}
err = tw686x_video_init(dev);
if (err)
- goto free_irq;
+ return err;
pci_set_drvdata(pci_dev, dev);
return 0;
-
-free_irq:
- free_irq(pci_dev->irq, dev);
-iounmap:
- iounmap(dev->mmio);
-free_region:
- release_mem_region(pci_resource_start(pci_dev, 0),
- pci_resource_len(pci_dev, 0));
-disable:
- pci_disable_device(pci_dev);
-free_dev:
- kfree(dev);
- return err;
}
static void tw686x_remove(struct pci_dev *pci_dev)
@@ -128,13 +112,6 @@ static void tw686x_remove(struct pci_dev *pci_dev)
struct tw686x_dev *dev = pci_get_drvdata(pci_dev);
tw686x_video_free(dev);
-
- free_irq(pci_dev->irq, dev);
- iounmap(dev->mmio);
- release_mem_region(pci_resource_start(pci_dev, 0),
- pci_resource_len(pci_dev, 0));
- pci_disable_device(pci_dev);
- kfree(dev);
}
/* driver_data is number of A/V channels */
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH 4/12] TW686x: Fix s_std() / g_std() / g_parm() pointer to self
2016-01-28 8:29 [PATCH 0/12] TW686x driver Krzysztof Hałasa
` (13 preceding siblings ...)
2016-01-28 9:18 ` [PATCH 3/12] TW686x: Switch to devm_*() Krzysztof Hałasa
@ 2016-01-28 9:19 ` Krzysztof Hałasa
2016-02-08 10:49 ` [PATCH 0/12] TW686x driver Hans Verkuil
15 siblings, 0 replies; 19+ messages in thread
From: Krzysztof Hałasa @ 2016-01-28 9:19 UTC (permalink / raw)
To: Hans Verkuil; +Cc: linux-media, Ezequiel Garcia
Signed-off-by: Krzysztof Hałasa <khalasa@piap.pl>
diff --git a/drivers/media/pci/tw686x/tw686x-video.c b/drivers/media/pci/tw686x/tw686x-video.c
index 5a1b9ab..78f4f55 100644
--- a/drivers/media/pci/tw686x/tw686x-video.c
+++ b/drivers/media/pci/tw686x/tw686x-video.c
@@ -416,7 +416,7 @@ static int tw686x_querycap(struct file *file, void *priv,
static int tw686x_s_std(struct file *file, void *priv, v4l2_std_id id)
{
- struct tw686x_video_channel *vc = priv;
+ struct tw686x_video_channel *vc = video_drvdata(file);
unsigned std, count = 0;
u32 sdt, std_mask = 0;
@@ -437,7 +437,7 @@ static int tw686x_s_std(struct file *file, void *priv, v4l2_std_id id)
static int tw686x_g_std(struct file *file, void *priv, v4l2_std_id *id)
{
- struct tw686x_video_channel *vc = priv;
+ struct tw686x_video_channel *vc = video_drvdata(file);
*id = vc->video_standard;
return 0;
@@ -457,7 +457,7 @@ static int tw686x_enum_fmt_vid_cap(struct file *file, void *priv,
static int tw686x_g_parm(struct file *file, void *priv,
struct v4l2_streamparm *sp)
{
- struct tw686x_video_channel *vc = priv;
+ struct tw686x_video_channel *vc = video_drvdata(file);
if (sp->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
^ permalink raw reply related [flat|nested] 19+ messages in thread
* Re: [PATCH 0/12] TW686x driver
2016-01-28 8:29 [PATCH 0/12] TW686x driver Krzysztof Hałasa
` (14 preceding siblings ...)
2016-01-28 9:19 ` [PATCH 4/12] TW686x: Fix s_std() / g_std() / g_parm() pointer to self Krzysztof Hałasa
@ 2016-02-08 10:49 ` Hans Verkuil
2016-02-15 14:25 ` Krzysztof Hałasa
2016-02-16 9:09 ` Krzysztof Hałasa
15 siblings, 2 replies; 19+ messages in thread
From: Hans Verkuil @ 2016-02-08 10:49 UTC (permalink / raw)
To: Krzysztof Hałasa; +Cc: linux-media, Ezequiel Garcia
On 01/28/2016 09:29 AM, Krzysztof Hałasa wrote:
> Hi,
>
> I'm posting a driver for TW686[4589]-based PCIe cards. The first patch
> has been posted and reviewed by Ezequiel back in July 2015, the
> subsequent patches are changes made in response to the review and/or are
> required by the more recent kernel versions.
>
> This driver lacks CMA-based frame mode DMA operation, I'll add it a bit
> later. Also:
> - I haven't converted the kthread to a workqueue - the driver is
> modeled after other code and it can be done later, if needed
> - I have skipped suggested PCI ID changes and the 704 vs 720 pixels/line
> question - this may need further consideration.
>
> Please merge.
Please repost as a single patch. Also make sure it is based on the latest
media_tree master branch.
Your current patch series breaks bisectability (basically, after patch 1 it
won't compile since it's not using vb2_v4l2_buffer yet).
Also, for new drivers we generally don't care about the history, we prefer a
single patch. That makes it easier to review as well.
I'll take a good look at the code once I have a v2.
Now, I am not planning to merge that, but I will compare it to what Ezequiel has
and use that comparison as a starting point for further discussions.
As I mentioned before, my preference is to merge a driver that supports both
frame and field modes (or whatever they are called).
Regards,
Hans
>
> The following changes since Linux 4.4 are available in the git
> repository at:
>
> git://git.kernel.org/pub/scm/linux/kernel/git/chris/linux.git techwell-4.4
>
> for you to fetch changes up to 8e495778acd4602c472cefa460a1afb41c4b8f25:
>
> [MEDIA] TW686x: return VB2_BUF_STATE_ERROR frames on timeout/errors (2016-01-27 14:47:41 +0100)
>
> ----------------------------------------------------------------
> Krzysztof Hałasa (12):
> [MEDIA] Add support for TW686[4589]-based frame grabbers
> [MEDIA] TW686x: Trivial changes suggested by Ezequiel Garcia
> [MEDIA] TW686x: Switch to devm_*()
> [MEDIA] TW686x: Fix s_std() / g_std() / g_parm() pointer to self
> [MEDIA] TW686x: Fix handling of TV standard values
> [MEDIA] TW686x: Fix try_fmt() color space
> [MEDIA] TW686x: Add enum_input() / g_input() / s_input()
> [MEDIA] TW686x: do not use pci_dma_supported()
> [MEDIA] TW686x: switch to vb2_v4l2_buffer
> [MEDIA] TW686x: handle non-NULL format in queue_setup()
> [MEDIA] TW686x: Track frame sequence numbers
> [MEDIA] TW686x: return VB2_BUF_STATE_ERROR frames on timeout/errors
>
> drivers/media/pci/Kconfig | 1 +
> drivers/media/pci/Makefile | 1 +
> drivers/media/pci/tw686x/Kconfig | 16 ++
> drivers/media/pci/tw686x/Makefile | 3 +
> drivers/media/pci/tw686x/tw686x-core.c | 140 +++++++++++++
> drivers/media/pci/tw686x/tw686x-regs.h | 103 +++++++++
> drivers/media/pci/tw686x/tw686x-video.c | 815 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> drivers/media/pci/tw686x/tw686x.h | 118 +++++++++++
> 8 files changed, 1197 insertions(+)
> create mode 100644 drivers/media/pci/tw686x/Kconfig
> create mode 100644 drivers/media/pci/tw686x/Makefile
> create mode 100644 drivers/media/pci/tw686x/tw686x-core.c
> create mode 100644 drivers/media/pci/tw686x/tw686x-regs.h
> create mode 100644 drivers/media/pci/tw686x/tw686x-video.c
> create mode 100644 drivers/media/pci/tw686x/tw686x.h
>
> Thanks.
>
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH 0/12] TW686x driver
2016-02-08 10:49 ` [PATCH 0/12] TW686x driver Hans Verkuil
@ 2016-02-15 14:25 ` Krzysztof Hałasa
2016-02-16 9:09 ` Krzysztof Hałasa
1 sibling, 0 replies; 19+ messages in thread
From: Krzysztof Hałasa @ 2016-02-15 14:25 UTC (permalink / raw)
To: Hans Verkuil; +Cc: linux-media, Ezequiel Garcia
Hans Verkuil <hverkuil@xs4all.nl> writes:
> Please repost as a single patch. Also make sure it is based on the latest
> media_tree master branch.
>
> Your current patch series breaks bisectability (basically, after patch 1 it
> won't compile since it's not using vb2_v4l2_buffer yet).
>
> Also, for new drivers we generally don't care about the history, we prefer a
> single patch. That makes it easier to review as well.
No problem. I posted incremental patches to help with (incremental)
review.
--
Krzysztof Halasa
Industrial Research Institute for Automation and Measurements PIAP
Al. Jerozolimskie 202, 02-486 Warsaw, Poland
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH 0/12] TW686x driver
2016-02-08 10:49 ` [PATCH 0/12] TW686x driver Hans Verkuil
2016-02-15 14:25 ` Krzysztof Hałasa
@ 2016-02-16 9:09 ` Krzysztof Hałasa
1 sibling, 0 replies; 19+ messages in thread
From: Krzysztof Hałasa @ 2016-02-16 9:09 UTC (permalink / raw)
To: Hans Verkuil; +Cc: linux-media, Ezequiel Garcia
Hans Verkuil <hverkuil@xs4all.nl> writes:
> Now, I am not planning to merge that, but I will compare it to what Ezequiel has
> and use that comparison as a starting point for further discussions.
I'm not opposed to Ezequiel's changes in general - I only want him to
present them as changes, and not as a "rewritten driver". Unless they
are considered a rewritten driver, of course.
> As I mentioned before, my preference is to merge a driver that supports both
> frame and field modes (or whatever they are called).
Well, I don't know about his, but my version presently only support
frame mode (V4L2_FIELD_SEQ_*). It specifically doesn't support
INTERLACED frame mode (SG DMA hw limitation) and it doesn't support
field mode, except for FIELD_TOP and FIELD_BOTTOM (only specific fields)
which I use to get a lower resolution image.
I also plan to use an INTERLACED frame mode with a DMA (non-SG to CMA,
it turned out it works well on my systems), but I would rather like to
have the driver in the tree before submitting further (non-essential)
patches.
Field modes are of course possible, though I've never seen any interest
for them, so I haven't bothered. Can add, it's a simple thing and it
works fine (apart from increased QBUF/DQBUF rate).
There is also a specific YUV420 mode (with a custom encoding) which
I'd like to add (unfortunately unavailable in V4L2_FIELD_SEQ_*, but
usable in INTERLACED and FIELD_*). This mode requires two separate line
lengths - odd lines contain YUV data and are twice as long as even lines
which only contain Y.
How do I set up such a mode (fmt.pix.bytesperline)?
This mode is useful on low-performance machines, e.g. feeding data to
H.264 encoders.
--
Krzysztof Halasa
Industrial Research Institute for Automation and Measurements PIAP
Al. Jerozolimskie 202, 02-486 Warsaw, Poland
^ permalink raw reply [flat|nested] 19+ messages in thread
end of thread, other threads:[~2016-02-16 9:09 UTC | newest]
Thread overview: 19+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-01-28 8:29 [PATCH 0/12] TW686x driver Krzysztof Hałasa
2016-01-28 8:53 ` [PATCH 1/12] Add support for TW686[4589]-based frame grabbers Krzysztof Hałasa
2016-01-28 8:57 ` [PATCH 2/12] TW686x: Trivial changes suggested by Ezequiel Garcia Krzysztof Hałasa
2016-01-28 9:01 ` [PATCH 3/12] TW686x: Switch to devm_*() Krzysztof Hałasa
2016-01-28 9:01 ` [PATCH 4/12] TW686x: Fix s_std() / g_std() / g_parm() pointer to self Krzysztof Hałasa
2016-01-28 9:03 ` [PATCH 5/12] TW686x: Fix handling of TV standard values Krzysztof Hałasa
2016-01-28 9:04 ` [PATCH 6/12] TW686x: Fix try_fmt() color space Krzysztof Hałasa
2016-01-28 9:05 ` [PATCH 7/12] TW686x: Add enum_input() / g_input() / s_input() Krzysztof Hałasa
2016-01-28 9:06 ` [PATCH 8/12] TW686x: do not use pci_dma_supported() Krzysztof Hałasa
2016-01-28 9:08 ` [PATCH 9/12] TW686x: switch to vb2_v4l2_buffer Krzysztof Hałasa
2016-01-28 9:09 ` [PATCH 10/12] TW686x: handle non-NULL format in queue_setup() Krzysztof Hałasa
2016-01-28 9:10 ` [PATCH 11/12] TW686x: Track frame sequence numbers Krzysztof Hałasa
2016-01-28 9:11 ` [PATCH 12/12] TW686x: return VB2_BUF_STATE_ERROR frames on timeout/errors Krzysztof Hałasa
2016-01-28 9:13 ` [PATCH 0/12] TW686x driver Krzysztof Hałasa
2016-01-28 9:18 ` [PATCH 3/12] TW686x: Switch to devm_*() Krzysztof Hałasa
2016-01-28 9:19 ` [PATCH 4/12] TW686x: Fix s_std() / g_std() / g_parm() pointer to self Krzysztof Hałasa
2016-02-08 10:49 ` [PATCH 0/12] TW686x driver Hans Verkuil
2016-02-15 14:25 ` Krzysztof Hałasa
2016-02-16 9:09 ` Krzysztof Hałasa
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.