From dd8245f445f5e751b38126140b6ba1723f06c60b Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 18 May 2017 05:40:18 -0300 Subject: [media] atomisp: don't treat warnings as errors Several atomisp files use: ccflags-y += -Werror As, on media, our usual procedure is to use W=1, and atomisp has *a lot* of warnings with such flag enabled,like: ./drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_common/host/system_local.h:62:26: warning: 'DDR_BASE' defined but not used [-Wunused-const-variable=] At the end, it causes our build to fail, impacting our workflow. So, remove this crap. If one wants to force -Werror, he can still build with it enabled by passing a parameter to make. Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/atomisp/i2c/Makefile | 2 -- drivers/staging/media/atomisp/i2c/imx/Makefile | 2 -- drivers/staging/media/atomisp/i2c/ov5693/Makefile | 2 -- drivers/staging/media/atomisp/pci/atomisp2/Makefile | 2 +- 4 files changed, 1 insertion(+), 7 deletions(-) diff --git a/drivers/staging/media/atomisp/i2c/Makefile b/drivers/staging/media/atomisp/i2c/Makefile index 8ea01904c0ea..466517c7c8e6 100644 --- a/drivers/staging/media/atomisp/i2c/Makefile +++ b/drivers/staging/media/atomisp/i2c/Makefile @@ -19,5 +19,3 @@ obj-$(CONFIG_VIDEO_AP1302) += ap1302.o obj-$(CONFIG_VIDEO_LM3554) += lm3554.o -ccflags-y += -Werror - diff --git a/drivers/staging/media/atomisp/i2c/imx/Makefile b/drivers/staging/media/atomisp/i2c/imx/Makefile index 1d7f7ab94cac..6b13a3a66e49 100644 --- a/drivers/staging/media/atomisp/i2c/imx/Makefile +++ b/drivers/staging/media/atomisp/i2c/imx/Makefile @@ -4,5 +4,3 @@ imx1x5-objs := imx.o drv201.o ad5816g.o dw9714.o dw9719.o dw9718.o vcm.o otp.o o ov8858_driver-objs := ../ov8858.o dw9718.o vcm.o obj-$(CONFIG_VIDEO_OV8858) += ov8858_driver.o - -ccflags-y += -Werror diff --git a/drivers/staging/media/atomisp/i2c/ov5693/Makefile b/drivers/staging/media/atomisp/i2c/ov5693/Makefile index fceb9e9b881b..c9c0e1245858 100644 --- a/drivers/staging/media/atomisp/i2c/ov5693/Makefile +++ b/drivers/staging/media/atomisp/i2c/ov5693/Makefile @@ -1,3 +1 @@ obj-$(CONFIG_VIDEO_OV5693) += ov5693.o - -ccflags-y += -Werror diff --git a/drivers/staging/media/atomisp/pci/atomisp2/Makefile b/drivers/staging/media/atomisp/pci/atomisp2/Makefile index 3fa7c1c1479f..f126a89a08e9 100644 --- a/drivers/staging/media/atomisp/pci/atomisp2/Makefile +++ b/drivers/staging/media/atomisp/pci/atomisp2/Makefile @@ -351,5 +351,5 @@ DEFINES := -DHRT_HW -DHRT_ISP_CSS_CUSTOM_HOST -DHRT_USE_VIR_ADDRS -D__HOST__ DEFINES += -DATOMISP_POSTFIX=\"css2400b0_v21\" -DISP2400B0 DEFINES += -DSYSTEM_hive_isp_css_2400_system -DISP2400 -ccflags-y += $(INCLUDES) $(DEFINES) -fno-common -Werror +ccflags-y += $(INCLUDES) $(DEFINES) -fno-common -- cgit v1.2.3-58-ga151 From 592ddc9f7db36c778d3bf9ffdfd93d8d5d548e48 Mon Sep 17 00:00:00 2001 From: Sean Young Date: Tue, 16 May 2017 04:56:14 -0300 Subject: [media] sir_ir: infinite loop in interrupt handler Since this driver does no detection of hardware, it might be used with a non-sir port. Escape out if we are spinning. Reported-by: kbuild test robot Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/sir_ir.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/media/rc/sir_ir.c b/drivers/media/rc/sir_ir.c index e12ec50bf0bf..90a5f8fd5eea 100644 --- a/drivers/media/rc/sir_ir.c +++ b/drivers/media/rc/sir_ir.c @@ -183,9 +183,15 @@ static irqreturn_t sir_interrupt(int irq, void *dev_id) static unsigned long delt; unsigned long deltintr; unsigned long flags; + int counter = 0; int iir, lsr; while ((iir = inb(io + UART_IIR) & UART_IIR_ID)) { + if (++counter > 256) { + dev_err(&sir_ir_dev->dev, "Trapped in interrupt"); + break; + } + switch (iir & UART_IIR_ID) { /* FIXME toto treba preriedit */ case UART_IIR_MSI: (void)inb(io + UART_MSR); -- cgit v1.2.3-58-ga151 From b2aceb739b5af6a8abc5ea6ab9e6a0409a3b5b1d Mon Sep 17 00:00:00 2001 From: David Härdeman Date: Thu, 27 Apr 2017 17:33:58 -0300 Subject: [media] rc-core: fix input repeat handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The call to input_register_device() needs to take place before the repeat parameters are set or the input subsystem repeat handling will be disabled (as was already noted in the comments in that function). Cc: stable # v4.11 Signed-off-by: David Härdeman Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/rc-main.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c index 6ec73357fa47..802e559cc30e 100644 --- a/drivers/media/rc/rc-main.c +++ b/drivers/media/rc/rc-main.c @@ -1703,6 +1703,16 @@ static int rc_setup_rx_device(struct rc_dev *dev) if (dev->close) dev->input_dev->close = ir_close; + dev->input_dev->dev.parent = &dev->dev; + memcpy(&dev->input_dev->id, &dev->input_id, sizeof(dev->input_id)); + dev->input_dev->phys = dev->input_phys; + dev->input_dev->name = dev->input_name; + + /* rc_open will be called here */ + rc = input_register_device(dev->input_dev); + if (rc) + goto out_table; + /* * Default delay of 250ms is too short for some protocols, especially * since the timeout is currently set to 250ms. Increase it to 500ms, @@ -1718,16 +1728,6 @@ static int rc_setup_rx_device(struct rc_dev *dev) */ dev->input_dev->rep[REP_PERIOD] = 125; - dev->input_dev->dev.parent = &dev->dev; - memcpy(&dev->input_dev->id, &dev->input_id, sizeof(dev->input_id)); - dev->input_dev->phys = dev->input_phys; - dev->input_dev->name = dev->input_name; - - /* rc_open will be called here */ - rc = input_register_device(dev->input_dev); - if (rc) - goto out_table; - return 0; out_table: -- cgit v1.2.3-58-ga151 From 0f7c4063f8cd78b1a1e4858be39d3144cf7315dc Mon Sep 17 00:00:00 2001 From: David Härdeman Date: Mon, 1 May 2017 10:32:34 -0300 Subject: [media] ir-lirc-codec: let lirc_dev handle the lirc_buffer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ir_lirc_register() currently creates its own lirc_buffer before passing the lirc_driver to lirc_register_driver(). When a module is later unloaded, ir_lirc_unregister() gets called which performs a call to lirc_unregister_driver() and then free():s the lirc_buffer. The problem is that: a) there can still be a userspace app holding an open lirc fd when lirc_unregister_driver() returns; and b) the lirc_buffer contains "wait_queue_head_t wait_poll" which is potentially used as long as any userspace app is still around. The result is an oops which can be triggered quite easily by a userspace app monitoring its lirc fd using epoll() and not closing the fd promptly on device removal. The minimalistic fix is to let lirc_dev create the lirc_buffer since lirc_dev will then also free the buffer once it believes it is safe to do so. Signed-off-by: David Härdeman Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/ir-lirc-codec.c | 25 +++++++------------------ drivers/media/rc/lirc_dev.c | 13 ++++++++++++- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/drivers/media/rc/ir-lirc-codec.c b/drivers/media/rc/ir-lirc-codec.c index de85f1d7ce43..8f0669c9894c 100644 --- a/drivers/media/rc/ir-lirc-codec.c +++ b/drivers/media/rc/ir-lirc-codec.c @@ -354,7 +354,6 @@ static const struct file_operations lirc_fops = { static int ir_lirc_register(struct rc_dev *dev) { struct lirc_driver *drv; - struct lirc_buffer *rbuf; int rc = -ENOMEM; unsigned long features = 0; @@ -362,19 +361,12 @@ static int ir_lirc_register(struct rc_dev *dev) if (!drv) return rc; - rbuf = kzalloc(sizeof(struct lirc_buffer), GFP_KERNEL); - if (!rbuf) - goto rbuf_alloc_failed; - - rc = lirc_buffer_init(rbuf, sizeof(int), LIRCBUF_SIZE); - if (rc) - goto rbuf_init_failed; - if (dev->driver_type != RC_DRIVER_IR_RAW_TX) { features |= LIRC_CAN_REC_MODE2; if (dev->rx_resolution) features |= LIRC_CAN_GET_REC_RESOLUTION; } + if (dev->tx_ir) { features |= LIRC_CAN_SEND_PULSE; if (dev->s_tx_mask) @@ -403,10 +395,12 @@ static int ir_lirc_register(struct rc_dev *dev) drv->minor = -1; drv->features = features; drv->data = &dev->raw->lirc; - drv->rbuf = rbuf; + drv->rbuf = NULL; drv->set_use_inc = &ir_lirc_open; drv->set_use_dec = &ir_lirc_close; drv->code_length = sizeof(struct ir_raw_event) * 8; + drv->chunk_size = sizeof(int); + drv->buffer_size = LIRCBUF_SIZE; drv->fops = &lirc_fops; drv->dev = &dev->dev; drv->rdev = dev; @@ -415,19 +409,15 @@ static int ir_lirc_register(struct rc_dev *dev) drv->minor = lirc_register_driver(drv); if (drv->minor < 0) { rc = -ENODEV; - goto lirc_register_failed; + goto out; } dev->raw->lirc.drv = drv; dev->raw->lirc.dev = dev; return 0; -lirc_register_failed: -rbuf_init_failed: - kfree(rbuf); -rbuf_alloc_failed: +out: kfree(drv); - return rc; } @@ -436,9 +426,8 @@ static int ir_lirc_unregister(struct rc_dev *dev) struct lirc_codec *lirc = &dev->raw->lirc; lirc_unregister_driver(lirc->drv->minor); - lirc_buffer_free(lirc->drv->rbuf); - kfree(lirc->drv->rbuf); kfree(lirc->drv); + lirc->drv = NULL; return 0; } diff --git a/drivers/media/rc/lirc_dev.c b/drivers/media/rc/lirc_dev.c index 8d60c9f00df9..42704552b005 100644 --- a/drivers/media/rc/lirc_dev.c +++ b/drivers/media/rc/lirc_dev.c @@ -52,6 +52,7 @@ struct irctl { struct mutex irctl_lock; struct lirc_buffer *buf; + bool buf_internal; unsigned int chunk_size; struct device dev; @@ -83,7 +84,7 @@ static void lirc_release(struct device *ld) put_device(ir->dev.parent); - if (ir->buf != ir->d.rbuf) { + if (ir->buf_internal) { lirc_buffer_free(ir->buf); kfree(ir->buf); } @@ -198,6 +199,7 @@ static int lirc_allocate_buffer(struct irctl *ir) if (d->rbuf) { ir->buf = d->rbuf; + ir->buf_internal = false; } else { ir->buf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL); if (!ir->buf) { @@ -208,8 +210,11 @@ static int lirc_allocate_buffer(struct irctl *ir) err = lirc_buffer_init(ir->buf, chunk_size, buffer_size); if (err) { kfree(ir->buf); + ir->buf = NULL; goto out; } + + ir->buf_internal = true; } ir->chunk_size = ir->buf->chunk_size; @@ -362,6 +367,12 @@ int lirc_register_driver(struct lirc_driver *d) err = lirc_allocate_buffer(irctls[minor]); if (err) lirc_unregister_driver(minor); + else + /* + * This is kind of a hack but ir-lirc-codec needs + * access to the buffer that lirc_dev allocated. + */ + d->rbuf = irctls[minor]->buf; } return err ? err : minor; -- cgit v1.2.3-58-ga151 From 5c621744b4270976f2dba71c49d92883afaf1604 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 19 Apr 2017 14:15:32 -0300 Subject: [media] rainshadow-cec: use strlcat instead of strncat gcc warns about an obviously incorrect use of strncat(): drivers/media/usb/rainshadow-cec/rainshadow-cec.c: In function 'rain_cec_adap_transmit': drivers/media/usb/rainshadow-cec/rainshadow-cec.c:299:4: error: specified bound 48 equals the size of the destination [-Werror=stringop-overflow=] It seems that strlcat was intended here, and using that makes the code correct. Fixes: 0f314f6c2e77 ("[media] rainshadow-cec: new RainShadow Tech HDMI CEC driver") Signed-off-by: Arnd Bergmann Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/rainshadow-cec/rainshadow-cec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/usb/rainshadow-cec/rainshadow-cec.c b/drivers/media/usb/rainshadow-cec/rainshadow-cec.c index 541ca543f71f..dc1f64f904cd 100644 --- a/drivers/media/usb/rainshadow-cec/rainshadow-cec.c +++ b/drivers/media/usb/rainshadow-cec/rainshadow-cec.c @@ -296,7 +296,7 @@ static int rain_cec_adap_transmit(struct cec_adapter *adap, u8 attempts, cec_msg_destination(msg), msg->msg[1]); for (i = 2; i < msg->len; i++) { snprintf(hex, sizeof(hex), "%02x", msg->msg[i]); - strncat(cmd, hex, sizeof(cmd)); + strlcat(cmd, hex, sizeof(cmd)); } } mutex_lock(&rain->write_lock); -- cgit v1.2.3-58-ga151 From ea6a69defd3311014d8a0ea89245410593c8d00c Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 21 Apr 2017 07:51:21 -0300 Subject: [media] rainshadow-cec: avoid -Wmaybe-uninitialized warning The barrier implied by spin_unlock() in rain_irq_work_handler makes it hard for gcc to figure out the state of the variables, leading to a false-positive warning: drivers/media/usb/rainshadow-cec/rainshadow-cec.c: In function 'rain_irq_work_handler': drivers/media/usb/rainshadow-cec/rainshadow-cec.c:171:31: error: 'data' may be used uninitialized in this function [-Werror=maybe-uninitialized] Slightly rearranging the code makes it easier for the compiler to see that the code is correct, and gets rid of the warning. Fixes: 0f314f6c2e77 ("[media] rainshadow-cec: new RainShadow Tech HDMI CEC driver") Signed-off-by: Arnd Bergmann Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/rainshadow-cec/rainshadow-cec.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/usb/rainshadow-cec/rainshadow-cec.c b/drivers/media/usb/rainshadow-cec/rainshadow-cec.c index dc1f64f904cd..9ddd6a99f066 100644 --- a/drivers/media/usb/rainshadow-cec/rainshadow-cec.c +++ b/drivers/media/usb/rainshadow-cec/rainshadow-cec.c @@ -123,11 +123,12 @@ static void rain_irq_work_handler(struct work_struct *work) char data; spin_lock_irqsave(&rain->buf_lock, flags); - exit_loop = rain->buf_len == 0; if (rain->buf_len) { data = rain->buf[rain->buf_rd_idx]; rain->buf_len--; rain->buf_rd_idx = (rain->buf_rd_idx + 1) & 0xff; + } else { + exit_loop = true; } spin_unlock_irqrestore(&rain->buf_lock, flags); -- cgit v1.2.3-58-ga151 From 71e22ed1e22ff4bd83ed9743083cc8381ada1702 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 21 Apr 2017 07:52:17 -0300 Subject: [media] cec: improve MEDIA_CEC_RC dependencies Changing the IS_REACHABLE() into a plain #ifdef broke the case of CONFIG_MEDIA_RC=m && CONFIG_MEDIA_CEC=y: drivers/media/cec/cec-core.o: In function `cec_unregister_adapter': cec-core.c:(.text.cec_unregister_adapter+0x18): undefined reference to `rc_unregister_device' drivers/media/cec/cec-core.o: In function `cec_delete_adapter': cec-core.c:(.text.cec_delete_adapter+0x54): undefined reference to `rc_free_device' drivers/media/cec/cec-core.o: In function `cec_register_adapter': cec-core.c:(.text.cec_register_adapter+0x94): undefined reference to `rc_register_device' cec-core.c:(.text.cec_register_adapter+0xa4): undefined reference to `rc_free_device' cec-core.c:(.text.cec_register_adapter+0x110): undefined reference to `rc_unregister_device' drivers/media/cec/cec-core.o: In function `cec_allocate_adapter': cec-core.c:(.text.cec_allocate_adapter+0x234): undefined reference to `rc_allocate_device' drivers/media/cec/cec-adap.o: In function `cec_received_msg': cec-adap.c:(.text.cec_received_msg+0x734): undefined reference to `rc_keydown' cec-adap.c:(.text.cec_received_msg+0x768): undefined reference to `rc_keyup' This adds an additional dependency to explicitly forbid this combination. Fixes: 5f2c467c54f5 ("[media] cec: add MEDIA_CEC_RC config option") Signed-off-by: Arnd Bergmann Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/cec/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/cec/Kconfig b/drivers/media/cec/Kconfig index f944d93e3167..488fb908244d 100644 --- a/drivers/media/cec/Kconfig +++ b/drivers/media/cec/Kconfig @@ -9,6 +9,7 @@ config MEDIA_CEC_NOTIFIER config MEDIA_CEC_RC bool "HDMI CEC RC integration" depends on CEC_CORE && RC_CORE + depends on CEC_CORE=m || RC_CORE=y ---help--- Pass on CEC remote control messages to the RC framework. -- cgit v1.2.3-58-ga151 From e3b4d10cc057522353c4a02f2f90dca6a52e006f Mon Sep 17 00:00:00 2001 From: Petr Cvek Date: Mon, 24 Apr 2017 22:51:58 -0300 Subject: [media] pxa_camera: fix module remove codepath for v4l2 clock The conversion from soc_camera omitted a correct handling of the clock gating for a sensor. When the pxa_camera driver module was removed it tried to unregister clk, but this caused a similar warning: WARNING: CPU: 0 PID: 6740 at drivers/media/v4l2-core/v4l2-clk.c:278 v4l2_clk_unregister(): Refusing to unregister ref-counted 0-0030 clock! The clock was at time still refcounted by the sensor driver. Before the removing of the pxa_camera the clock must be dropped by the sensor driver. This should be triggered by v4l2_async_notifier_unregister() call which removes sensor driver module too, calls unbind() function and then tries to probe sensor driver again. Inside unbind() we can safely unregister the v4l2 clock as the sensor driver got removed. The original v4l2_clk_unregister() should be put inside test as the clock can be already unregistered from unbind(). If there was not any bound sensor the clock is still present. The codepath is practically a copy from the old soc_camera. The bug was tested with a pxa_camera+ov9640 combination during the conversion of the ov9640 from the soc_camera. Signed-off-by: Petr Cvek Tested-by: Robert Jarzmik Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/pxa_camera.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/pxa_camera.c b/drivers/media/platform/pxa_camera.c index 929006f65cc7..6615f80fe059 100644 --- a/drivers/media/platform/pxa_camera.c +++ b/drivers/media/platform/pxa_camera.c @@ -2177,6 +2177,12 @@ static void pxa_camera_sensor_unbind(struct v4l2_async_notifier *notifier, pxa_dma_stop_channels(pcdev); pxa_camera_destroy_formats(pcdev); + + if (pcdev->mclk_clk) { + v4l2_clk_unregister(pcdev->mclk_clk); + pcdev->mclk_clk = NULL; + } + video_unregister_device(&pcdev->vdev); pcdev->sensor = NULL; @@ -2501,7 +2507,13 @@ static int pxa_camera_remove(struct platform_device *pdev) dma_release_channel(pcdev->dma_chans[1]); dma_release_channel(pcdev->dma_chans[2]); - v4l2_clk_unregister(pcdev->mclk_clk); + v4l2_async_notifier_unregister(&pcdev->notifier); + + if (pcdev->mclk_clk) { + v4l2_clk_unregister(pcdev->mclk_clk); + pcdev->mclk_clk = NULL; + } + v4l2_device_unregister(&pcdev->v4l2_dev); dev_info(&pdev->dev, "PXA Camera driver unloaded\n"); -- cgit v1.2.3-58-ga151 From be321a82bf8ee2e59734dd0d1fa1183a55a81d5d Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Tue, 25 Apr 2017 00:12:46 -0300 Subject: [media] rainshadow-cec: Fix missing spin_lock_init() The driver allocates the spinlock but not initialize it. Use spin_lock_init() on it to initialize it correctly. This is detected by Coccinelle semantic patch. Fixes: 0f314f6c2e77 ("[media] rainshadow-cec: new RainShadow Tech HDMI CEC driver") Signed-off-by: Wei Yongjun Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/rainshadow-cec/rainshadow-cec.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/usb/rainshadow-cec/rainshadow-cec.c b/drivers/media/usb/rainshadow-cec/rainshadow-cec.c index 9ddd6a99f066..8d3ca2c8b20f 100644 --- a/drivers/media/usb/rainshadow-cec/rainshadow-cec.c +++ b/drivers/media/usb/rainshadow-cec/rainshadow-cec.c @@ -337,6 +337,7 @@ static int rain_connect(struct serio *serio, struct serio_driver *drv) serio_set_drvdata(serio, rain); INIT_WORK(&rain->work, rain_irq_work_handler); mutex_init(&rain->write_lock); + spin_lock_init(&rain->buf_lock); err = serio_open(serio, drv); if (err) -- cgit v1.2.3-58-ga151 From df0e1fb999ee4dc2cb911dd9256f421ac089f0e9 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Tue, 25 Apr 2017 11:38:18 -0300 Subject: [media] s5p-cec: remove unused including Remove including that is not needed. Signed-off-by: Wei Yongjun Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-cec/s5p_cec.h | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/media/platform/s5p-cec/s5p_cec.h b/drivers/media/platform/s5p-cec/s5p_cec.h index 7015845c1caa..8bcd8dc1aeb9 100644 --- a/drivers/media/platform/s5p-cec/s5p_cec.h +++ b/drivers/media/platform/s5p-cec/s5p_cec.h @@ -22,7 +22,6 @@ #include #include #include -#include #include #include -- cgit v1.2.3-58-ga151 From ef6ff8f47263b1a98b7c3a8d7ee30c1d5b0afdfa Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Fri, 28 Apr 2017 01:51:40 -0300 Subject: [media] vb2: Fix an off by one error in 'vb2_plane_vaddr' We should ensure that 'plane_no' is '< vb->num_planes' as done in 'vb2_plane_cookie' just a few lines below. Fixes: e23ccc0ad925 ("[media] v4l: add videobuf2 Video for Linux 2 driver framework") Cc: stable@vger.kernel.org Signed-off-by: Christophe JAILLET Reviewed-by: Sakari Ailus Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/videobuf2-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c index 94afbbf92807..c0175ea7e7ad 100644 --- a/drivers/media/v4l2-core/videobuf2-core.c +++ b/drivers/media/v4l2-core/videobuf2-core.c @@ -868,7 +868,7 @@ EXPORT_SYMBOL_GPL(vb2_core_create_bufs); void *vb2_plane_vaddr(struct vb2_buffer *vb, unsigned int plane_no) { - if (plane_no > vb->num_planes || !vb->planes[plane_no].mem_priv) + if (plane_no >= vb->num_planes || !vb->planes[plane_no].mem_priv) return NULL; return call_ptr_memop(vb, vaddr, vb->planes[plane_no].mem_priv); -- cgit v1.2.3-58-ga151 From 79eddc992ea45239d93930601f6c773dea51ac84 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 12 May 2017 16:39:21 -0300 Subject: [media] cec-notifier.h: handle unreachable CONFIG_CEC_CORE Fix a link error in this specific combination of config options: CONFIG_MEDIA_CEC_SUPPORT=y CONFIG_CEC_CORE=m CONFIG_MEDIA_CEC_NOTIFIER=y CONFIG_VIDEO_STI_HDMI_CEC=m CONFIG_DRM_STI=y drivers/gpu/drm/sti/sti_hdmi.o: In function `sti_hdmi_remove': sti_hdmi.c:(.text.sti_hdmi_remove+0x10): undefined reference to `cec_notifier_set_phys_addr' sti_hdmi.c:(.text.sti_hdmi_remove+0x34): undefined reference to `cec_notifier_put' drivers/gpu/drm/sti/sti_hdmi.o: In function `sti_hdmi_connector_get_modes': sti_hdmi.c:(.text.sti_hdmi_connector_get_modes+0x4a): undefined reference to `cec_notifier_set_phys_addr_from_edid' drivers/gpu/drm/sti/sti_hdmi.o: In function `sti_hdmi_probe': sti_hdmi.c:(.text.sti_hdmi_probe+0x204): undefined reference to `cec_notifier_get' drivers/gpu/drm/sti/sti_hdmi.o: In function `sti_hdmi_connector_detect': sti_hdmi.c:(.text.sti_hdmi_connector_detect+0x36): undefined reference to `cec_notifier_set_phys_addr' drivers/gpu/drm/sti/sti_hdmi.o: In function `sti_hdmi_disable': sti_hdmi.c:(.text.sti_hdmi_disable+0xc0): undefined reference to `cec_notifier_set_phys_addr' The version below seems to work, though I don't particularly like the IS_REACHABLE() addition since that can be confusing to users. Signed-off-by: Arnd Bergmann Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- include/media/cec-notifier.h | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/include/media/cec-notifier.h b/include/media/cec-notifier.h index eb50ce54b759..71d7ced2c09e 100644 --- a/include/media/cec-notifier.h +++ b/include/media/cec-notifier.h @@ -29,7 +29,7 @@ struct edid; struct cec_adapter; struct cec_notifier; -#ifdef CONFIG_MEDIA_CEC_NOTIFIER +#if IS_REACHABLE(CONFIG_CEC_CORE) && IS_ENABLED(CONFIG_MEDIA_CEC_NOTIFIER) /** * cec_notifier_get - find or create a new cec_notifier for the given device. @@ -106,6 +106,16 @@ static inline void cec_notifier_set_phys_addr_from_edid(struct cec_notifier *n, { } +static inline void cec_notifier_register(struct cec_notifier *n, + struct cec_adapter *adap, + void (*callback)(struct cec_adapter *adap, u16 pa)) +{ +} + +static inline void cec_notifier_unregister(struct cec_notifier *n) +{ +} + #endif #endif -- cgit v1.2.3-58-ga151 From f2c61f98e0b5f8b53b8fb860e5dcdd661bde7d0b Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Thu, 4 May 2017 12:20:17 -0300 Subject: [media] tc358743: fix register i2c_rd/wr function fix The below mentioned fix contains a small but severe bug, fix it to make the driver work again. Fixes: 3538aa6ecfb2 ("[media] tc358743: fix register i2c_rd/wr functions") Cc: Hans Verkuil Cc: Mauro Carvalho Chehab Signed-off-by: Philipp Zabel Acked-by: Arnd Bergmann Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/tc358743.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/i2c/tc358743.c b/drivers/media/i2c/tc358743.c index acef4eca269f..3251cba89e8f 100644 --- a/drivers/media/i2c/tc358743.c +++ b/drivers/media/i2c/tc358743.c @@ -223,7 +223,7 @@ static void i2c_wr8(struct v4l2_subdev *sd, u16 reg, u8 val) static void i2c_wr8_and_or(struct v4l2_subdev *sd, u16 reg, u8 mask, u8 val) { - i2c_wrreg(sd, reg, (i2c_rdreg(sd, reg, 2) & mask) | val, 2); + i2c_wrreg(sd, reg, (i2c_rdreg(sd, reg, 1) & mask) | val, 1); } static u16 i2c_rd16(struct v4l2_subdev *sd, u16 reg) -- cgit v1.2.3-58-ga151 From 158aeefcb82f875ac603d66596e33dbdfcf8a400 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Thu, 18 May 2017 10:50:10 -0300 Subject: [media] atomisp: Add __printf validation and fix fallout __printf validation adds format and argument validation. Fix the various broken format/argument mismatches. Signed-off-by: Joe Perches Signed-off-by: Greg Kroah-Hartman Signed-off-by: Mauro Carvalho Chehab --- .../isp/kernels/sdis/sdis_1.0/ia_css_sdis.host.c | 6 +++--- .../isp/kernels/sdis/sdis_2/ia_css_sdis2.host.c | 2 +- .../css2400/isp/kernels/tnr/tnr_1.0/ia_css_tnr.host.c | 2 +- .../css2400/runtime/debug/interface/ia_css_debug.h | 1 + .../atomisp2/css2400/runtime/debug/src/ia_css_debug.c | 6 +++--- .../media/atomisp/pci/atomisp2/css2400/sh_css.c | 19 ++++++++++--------- .../media/atomisp/pci/atomisp2/css2400/sh_css_mipi.c | 2 +- .../atomisp/pci/atomisp2/css2400/sh_css_params.c | 10 +++++----- 8 files changed, 25 insertions(+), 23 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/sdis/sdis_1.0/ia_css_sdis.host.c b/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/sdis/sdis_1.0/ia_css_sdis.host.c index 0daab1176865..9478c12abe89 100644 --- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/sdis/sdis_1.0/ia_css_sdis.host.c +++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/sdis/sdis_1.0/ia_css_sdis.host.c @@ -265,9 +265,9 @@ ia_css_translate_dvs_statistics( assert(isp_stats->hor_proj != NULL); assert(isp_stats->ver_proj != NULL); - IA_CSS_ENTER("hproj=%p, vproj=%p, haddr=%x, vaddr=%x", - host_stats->hor_proj, host_stats->ver_proj, - isp_stats->hor_proj, isp_stats->ver_proj); + IA_CSS_ENTER("hproj=%p, vproj=%p, haddr=%p, vaddr=%p", + host_stats->hor_proj, host_stats->ver_proj, + isp_stats->hor_proj, isp_stats->ver_proj); hor_num_isp = host_stats->grid.aligned_height; ver_num_isp = host_stats->grid.aligned_width; diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/sdis/sdis_2/ia_css_sdis2.host.c b/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/sdis/sdis_2/ia_css_sdis2.host.c index 5a0c103e9eb7..9bccb6473154 100644 --- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/sdis/sdis_2/ia_css_sdis2.host.c +++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/sdis/sdis_2/ia_css_sdis2.host.c @@ -213,7 +213,7 @@ ia_css_translate_dvs2_statistics( "hor_coefs.even_real=%p, hor_coefs.even_imag=%p, " "ver_coefs.odd_real=%p, ver_coefs.odd_imag=%p, " "ver_coefs.even_real=%p, ver_coefs.even_imag=%p, " - "haddr=%x, vaddr=%x", + "haddr=%p, vaddr=%p", host_stats->hor_prod.odd_real, host_stats->hor_prod.odd_imag, host_stats->hor_prod.even_real, host_stats->hor_prod.even_imag, host_stats->ver_prod.odd_real, host_stats->ver_prod.odd_imag, diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/tnr/tnr_1.0/ia_css_tnr.host.c b/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/tnr/tnr_1.0/ia_css_tnr.host.c index 804c19ab4485..222a7bd7f176 100644 --- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/tnr/tnr_1.0/ia_css_tnr.host.c +++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/tnr/tnr_1.0/ia_css_tnr.host.c @@ -55,7 +55,7 @@ ia_css_tnr_dump( "tnr_coef", tnr->coef); ia_css_debug_dtrace(level, "\t%-32s = %d\n", "tnr_threshold_Y", tnr->threshold_Y); - ia_css_debug_dtrace(level, "\t%-32s = %d\n" + ia_css_debug_dtrace(level, "\t%-32s = %d\n", "tnr_threshold_C", tnr->threshold_C); } diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/debug/interface/ia_css_debug.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/debug/interface/ia_css_debug.h index be7df3a30c21..91c105cc6204 100644 --- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/debug/interface/ia_css_debug.h +++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/debug/interface/ia_css_debug.h @@ -137,6 +137,7 @@ ia_css_debug_vdtrace(unsigned int level, const char *fmt, va_list args) sh_css_vprint(fmt, args); } +__printf(2, 3) extern void ia_css_debug_dtrace(unsigned int level, const char *fmt, ...); /*! @brief Dump sp thread's stack contents diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/debug/src/ia_css_debug.c b/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/debug/src/ia_css_debug.c index 030810bd0878..bcc0d464084f 100644 --- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/debug/src/ia_css_debug.c +++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/debug/src/ia_css_debug.c @@ -3148,8 +3148,8 @@ ia_css_debug_dump_pipe_config( ia_css_debug_dump_frame_info(&config->vf_output_info[i], "vf_output_info"); } - ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, "acc_extension: 0x%x\n", - config->acc_extension); + ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, "acc_extension: %p\n", + config->acc_extension); ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, "num_acc_stages: %d\n", config->num_acc_stages); ia_css_debug_dump_capture_config(&config->default_capture_config); @@ -3179,7 +3179,7 @@ ia_css_debug_dump_stream_config_source( ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, "timeout: %d\n", config->source.port.timeout); ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, "compression: %d\n", - config->source.port.compression); + config->source.port.compression.type); break; case IA_CSS_INPUT_MODE_TPG: ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, "source.tpg\n"); diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css.c b/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css.c index 73c76583610a..81a21a0c1391 100644 --- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css.c +++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css.c @@ -2003,7 +2003,7 @@ ia_css_enable_isys_event_queue(bool enable) void *sh_css_malloc(size_t size) { - ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, "sh_css_malloc() enter: size=%d\n",size); + ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, "sh_css_malloc() enter: size=%zu\n",size); /* FIXME: This first test can probably go away */ if (size == 0) return NULL; @@ -2016,7 +2016,7 @@ void *sh_css_calloc(size_t N, size_t size) { void *p; - ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, "sh_css_calloc() enter: N=%d, size=%d\n",N,size); + ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, "sh_css_calloc() enter: N=%zu, size=%zu\n",N,size); /* FIXME: this test can probably go away */ if (size > 0) { @@ -2059,7 +2059,8 @@ map_sp_threads(struct ia_css_stream *stream, bool map) enum ia_css_pipe_id pipe_id; assert(stream != NULL); - IA_CSS_ENTER_PRIVATE("stream = %p, map = %p", stream, map); + IA_CSS_ENTER_PRIVATE("stream = %p, map = %s", + stream, map ? "true" : "false"); if (stream == NULL) { IA_CSS_LEAVE_ERR_PRIVATE(IA_CSS_ERR_INVALID_ARGUMENTS); @@ -2766,7 +2767,7 @@ enum ia_css_err ia_css_irq_translate( *irq_infos = infos; ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, "ia_css_irq_translate() " - "leave: irq_infos=%p\n", infos); + "leave: irq_infos=%u\n", infos); return IA_CSS_SUCCESS; } @@ -4514,7 +4515,7 @@ ia_css_pipe_enqueue_buffer(struct ia_css_pipe *pipe, #else if (hmm_buffer_record) { #endif - IA_CSS_LOG("send vbuf=0x%x", h_vbuf); + IA_CSS_LOG("send vbuf=%p", h_vbuf); } else { return_err = IA_CSS_ERR_INTERNAL_ERROR; IA_CSS_ERROR("hmm_buffer_record[]: no available slots\n"); @@ -4624,7 +4625,7 @@ ia_css_pipe_dequeue_buffer(struct ia_css_pipe *pipe, ia_css_rmgr_rel_vbuf(hmm_buffer_pool, &hmm_buffer_record->h_vbuf); sh_css_hmm_buffer_record_reset(hmm_buffer_record); } else { - IA_CSS_ERROR("hmm_buffer_record not found (0x%p) buf_type(%d)", + IA_CSS_ERROR("hmm_buffer_record not found (0x%u) buf_type(%d)", ddr_buffer_addr, buf_type); IA_CSS_LEAVE_ERR(IA_CSS_ERR_INTERNAL_ERROR); return IA_CSS_ERR_INTERNAL_ERROR; @@ -4640,8 +4641,8 @@ ia_css_pipe_dequeue_buffer(struct ia_css_pipe *pipe, if ((ddr_buffer.kernel_ptr == 0) || (kernel_ptr != HOST_ADDRESS(ddr_buffer.kernel_ptr))) { IA_CSS_ERROR("kernel_ptr invalid"); - IA_CSS_ERROR("expected: (0x%p)", kernel_ptr); - IA_CSS_ERROR("actual: (0x%p)", HOST_ADDRESS(ddr_buffer.kernel_ptr)); + IA_CSS_ERROR("expected: (0x%llx)", (u64)kernel_ptr); + IA_CSS_ERROR("actual: (0x%llx)", (u64)HOST_ADDRESS(ddr_buffer.kernel_ptr)); IA_CSS_ERROR("buf_type: %d\n", buf_type); IA_CSS_LEAVE_ERR(IA_CSS_ERR_INTERNAL_ERROR); return IA_CSS_ERR_INTERNAL_ERROR; @@ -6621,7 +6622,7 @@ allocate_delay_frames(struct ia_css_pipe *pipe) IA_CSS_ENTER_PRIVATE(""); if (pipe == NULL) { - IA_CSS_ERROR("Invalid args - pipe %x", pipe); + IA_CSS_ERROR("Invalid args - pipe %p", pipe); return IA_CSS_ERR_INVALID_ARGUMENTS; } diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_mipi.c b/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_mipi.c index 7e3893c6c08a..36aaa3019a15 100644 --- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_mipi.c +++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_mipi.c @@ -681,7 +681,7 @@ send_mipi_frames(struct ia_css_pipe *pipe) unsigned int port = 0; #endif - IA_CSS_ENTER_PRIVATE("pipe=%d", pipe); + IA_CSS_ENTER_PRIVATE("pipe=%p", pipe); assert(pipe != NULL); assert(pipe->stream != NULL); diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_params.c b/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_params.c index 561f4a7236f7..d8c22e8cd3af 100644 --- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_params.c +++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_params.c @@ -3375,7 +3375,7 @@ enum ia_css_err ia_css_pipe_set_bci_scaler_lut(struct ia_css_pipe *pipe, #endif if (pipe->scaler_pp_lut == mmgr_NULL) { #ifndef ISP2401 - IA_CSS_LEAVE("lut(%p) err=%d", pipe->scaler_pp_lut, err); + IA_CSS_LEAVE("lut(%u) err=%d", pipe->scaler_pp_lut, err); return IA_CSS_ERR_CANNOT_ALLOCATE_MEMORY; #else ia_css_debug_dtrace(IA_CSS_DEBUG_ERROR, @@ -3397,7 +3397,7 @@ enum ia_css_err ia_css_pipe_set_bci_scaler_lut(struct ia_css_pipe *pipe, #endif } - IA_CSS_LEAVE("lut(%p) err=%d", pipe->scaler_pp_lut, err); + IA_CSS_LEAVE("lut(%u) err=%d", pipe->scaler_pp_lut, err); return err; } @@ -3437,7 +3437,7 @@ enum ia_css_err sh_css_params_map_and_store_default_gdc_lut(void) mmgr_store(default_gdc_lut, (int *)interleaved_lut_temp, sizeof(zoom_table)); - IA_CSS_LEAVE_PRIVATE("lut(%p) err=%d", default_gdc_lut, err); + IA_CSS_LEAVE_PRIVATE("lut(%u) err=%d", default_gdc_lut, err); return err; } @@ -3859,7 +3859,7 @@ sh_css_param_update_isp_params(struct ia_css_pipe *curr_pipe, /* When API change is implemented making good distinction between * stream config and pipe config this skipping code can be moved out of the #ifdef */ if (pipe_in && (pipe != pipe_in)) { - IA_CSS_LOG("skipping pipe %x", pipe); + IA_CSS_LOG("skipping pipe %p", pipe); continue; } @@ -4590,7 +4590,7 @@ free_ia_css_isp_parameter_set_info( unsigned int i; hrt_vaddress *addrs = (hrt_vaddress *)&isp_params_info.mem_map; - IA_CSS_ENTER_PRIVATE("ptr = %p", ptr); + IA_CSS_ENTER_PRIVATE("ptr = %u", ptr); /* sanity check - ptr must be valid */ if (!ia_css_refcount_is_valid(ptr)) { -- cgit v1.2.3-58-ga151 From 86675eccd1a531003c270a6f05954d49757c6bb5 Mon Sep 17 00:00:00 2001 From: Guru Das Srinagesh Date: Thu, 18 May 2017 10:50:11 -0300 Subject: [media] atomisp: use logical AND, not bitwise Fixes sparse warning "dubious: x & !y" in logical expression. Signed-off-by: Guru Das Srinagesh Signed-off-by: Greg Kroah-Hartman Signed-off-by: Mauro Carvalho Chehab --- .../media/atomisp/pci/atomisp2/css2400/runtime/binary/src/binary.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/binary/src/binary.c b/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/binary/src/binary.c index a8b93a756e41..ae0b229c9fb8 100644 --- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/binary/src/binary.c +++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/binary/src/binary.c @@ -1658,7 +1658,7 @@ ia_css_binary_find(struct ia_css_binary_descr *descr, candidate->internal.max_height); continue; } - if (!candidate->enable.ds && need_ds & !(xcandidate->num_output_pins > 1)) { + if (!candidate->enable.ds && need_ds && !(xcandidate->num_output_pins > 1)) { ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, "ia_css_binary_find() [%d] continue: !%d && %d\n", __LINE__, candidate->enable.ds, (int)need_ds); -- cgit v1.2.3-58-ga151 From cc6a612ab8ee00f80f3f0ca44caa37659565f0e8 Mon Sep 17 00:00:00 2001 From: Fabrizio Perria Date: Thu, 18 May 2017 10:50:12 -0300 Subject: [media] atomisp: Fix unnecessary initialization of static Fix checkpatch warning: removed unnecessary initialization of static variable "skip_fwload" to 0 in source atomisp_v4l2.c Signed-off-by: Fabrizio Perria Signed-off-by: Greg Kroah-Hartman Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/atomisp/pci/atomisp2/atomisp_v4l2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/media/atomisp/pci/atomisp2/atomisp_v4l2.c b/drivers/staging/media/atomisp/pci/atomisp2/atomisp_v4l2.c index e3fdbdba0b34..a0478077a012 100644 --- a/drivers/staging/media/atomisp/pci/atomisp2/atomisp_v4l2.c +++ b/drivers/staging/media/atomisp/pci/atomisp2/atomisp_v4l2.c @@ -51,7 +51,7 @@ /* G-Min addition: pull this in from intel_mid_pm.h */ #define CSTATE_EXIT_LATENCY_C1 1 -static uint skip_fwload = 0; +static uint skip_fwload; module_param(skip_fwload, uint, 0644); MODULE_PARM_DESC(skip_fwload, "Skip atomisp firmware load"); -- cgit v1.2.3-58-ga151 From c6cfe6edd67e9f0ab9f2307a27c93f15ea3844f6 Mon Sep 17 00:00:00 2001 From: Avraham Shukron Date: Fri, 19 May 2017 06:25:46 -0300 Subject: [media] atomisp: fixed sparse warnings Added "static" storage class to 4 not-declared functions Signed-off-by: Avraham Shukron Signed-off-by: Greg Kroah-Hartman --- .../media/atomisp/platform/intel-mid/atomisp_gmin_platform.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/staging/media/atomisp/platform/intel-mid/atomisp_gmin_platform.c b/drivers/staging/media/atomisp/platform/intel-mid/atomisp_gmin_platform.c index 5b4506a71126..15409e96449d 100644 --- a/drivers/staging/media/atomisp/platform/intel-mid/atomisp_gmin_platform.c +++ b/drivers/staging/media/atomisp/platform/intel-mid/atomisp_gmin_platform.c @@ -436,7 +436,7 @@ static int gmin_gpio1_ctrl(struct v4l2_subdev *subdev, int on) return -EINVAL; } -int gmin_v1p2_ctrl(struct v4l2_subdev *subdev, int on) +static int gmin_v1p2_ctrl(struct v4l2_subdev *subdev, int on) { struct gmin_subdev *gs = find_gmin_subdev(subdev); @@ -455,7 +455,8 @@ int gmin_v1p2_ctrl(struct v4l2_subdev *subdev, int on) return -EINVAL; } -int gmin_v1p8_ctrl(struct v4l2_subdev *subdev, int on) + +static int gmin_v1p8_ctrl(struct v4l2_subdev *subdev, int on) { struct gmin_subdev *gs = find_gmin_subdev(subdev); int ret; @@ -491,7 +492,7 @@ int gmin_v1p8_ctrl(struct v4l2_subdev *subdev, int on) return -EINVAL; } -int gmin_v2p8_ctrl(struct v4l2_subdev *subdev, int on) +static int gmin_v2p8_ctrl(struct v4l2_subdev *subdev, int on) { struct gmin_subdev *gs = find_gmin_subdev(subdev); int ret; @@ -527,7 +528,7 @@ int gmin_v2p8_ctrl(struct v4l2_subdev *subdev, int on) return -EINVAL; } -int gmin_flisclk_ctrl(struct v4l2_subdev *subdev, int on) +static int gmin_flisclk_ctrl(struct v4l2_subdev *subdev, int on) { int ret = 0; struct gmin_subdev *gs = find_gmin_subdev(subdev); -- cgit v1.2.3-58-ga151 From 76d041f276abd29bb7b29b1d847acec36249c9ab Mon Sep 17 00:00:00 2001 From: Valentin Vidic Date: Thu, 18 May 2017 10:50:14 -0300 Subject: [media] atomisp: drop unused qos variable Fixes a sparse warning: drivers/staging/media/atomisp/platform/intel-mid/intel_mid_pcihelpers.c:35:5: warning: symbol 'qos' was not declared. Should it be static? Signed-off-by: Valentin Vidic Signed-off-by: Greg Kroah-Hartman Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/atomisp/platform/intel-mid/intel_mid_pcihelpers.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/staging/media/atomisp/platform/intel-mid/intel_mid_pcihelpers.c b/drivers/staging/media/atomisp/platform/intel-mid/intel_mid_pcihelpers.c index a6c0f5f8c3f8..b01463361943 100644 --- a/drivers/staging/media/atomisp/platform/intel-mid/intel_mid_pcihelpers.c +++ b/drivers/staging/media/atomisp/platform/intel-mid/intel_mid_pcihelpers.c @@ -32,7 +32,6 @@ static DEFINE_SPINLOCK(msgbus_lock); static struct pci_dev *pci_root; static struct pm_qos_request pm_qos; -int qos; #define DW_I2C_NEED_QOS (platform_is(INTEL_ATOM_BYT)) -- cgit v1.2.3-58-ga151 From b739d0247a3213520a07e8dedaa191ef10880a15 Mon Sep 17 00:00:00 2001 From: Avraham Shukron Date: Thu, 18 May 2017 10:50:15 -0300 Subject: [media] atomisp: fixed coding style errors Fix for error (not warnings) reported by checkpatch.pl Specifically: - missing whitespace around "=" and after "," - indentation with spaces instead of tabs - lines starting with a whitespace This patch does not affect the compiled code in any way. Signed-off-by: Avraham Shukron Signed-off-by: Greg Kroah-Hartman Signed-off-by: Mauro Carvalho Chehab --- .../platform/intel-mid/atomisp_gmin_platform.c | 156 ++++++++++----------- .../platform/intel-mid/intel_mid_pcihelpers.c | 2 +- 2 files changed, 79 insertions(+), 79 deletions(-) diff --git a/drivers/staging/media/atomisp/platform/intel-mid/atomisp_gmin_platform.c b/drivers/staging/media/atomisp/platform/intel-mid/atomisp_gmin_platform.c index 15409e96449d..2a819ac6f9e2 100644 --- a/drivers/staging/media/atomisp/platform/intel-mid/atomisp_gmin_platform.c +++ b/drivers/staging/media/atomisp/platform/intel-mid/atomisp_gmin_platform.c @@ -51,7 +51,7 @@ struct gmin_subdev { static struct gmin_subdev gmin_subdevs[MAX_SUBDEVS]; -static enum { PMIC_UNSET = 0, PMIC_REGULATOR, PMIC_AXP, PMIC_TI , +static enum { PMIC_UNSET = 0, PMIC_REGULATOR, PMIC_AXP, PMIC_TI, PMIC_CRYSTALCOVE } pmic_id; /* The atomisp uses type==0 for the end-of-list marker, so leave space. */ @@ -152,13 +152,13 @@ const struct camera_af_platform_data *camera_get_af_platform_data(void) EXPORT_SYMBOL_GPL(camera_get_af_platform_data); int atomisp_register_i2c_module(struct v4l2_subdev *subdev, - struct camera_sensor_platform_data *plat_data, - enum intel_v4l2_subdev_type type) + struct camera_sensor_platform_data *plat_data, + enum intel_v4l2_subdev_type type) { int i; struct i2c_board_info *bi; struct gmin_subdev *gs; - struct i2c_client *client = v4l2_get_subdevdata(subdev); + struct i2c_client *client = v4l2_get_subdevdata(subdev); struct acpi_device *adev; dev_info(&client->dev, "register atomisp i2c module type %d\n", type); @@ -172,7 +172,7 @@ int atomisp_register_i2c_module(struct v4l2_subdev *subdev, if (adev) adev->power.flags.power_resources = 0; - for (i=0; i < MAX_SUBDEVS; i++) + for (i = 0; i < MAX_SUBDEVS; i++) if (!pdata.subdevs[i].type) break; @@ -206,7 +206,7 @@ struct v4l2_subdev *atomisp_gmin_find_subdev(struct i2c_adapter *adapter, struct i2c_board_info *board_info) { int i; - for (i=0; i < MAX_SUBDEVS && pdata.subdevs[i].type; i++) { + for (i = 0; i < MAX_SUBDEVS && pdata.subdevs[i].type; i++) { struct intel_v4l2_subdev_table *sd = &pdata.subdevs[i]; if (sd->v4l2_subdev.i2c_adapter_id == adapter->nr && sd->v4l2_subdev.board_info.addr == board_info->addr) @@ -270,45 +270,45 @@ static const struct gmin_cfg_var t100_vars[] = { }; static const struct gmin_cfg_var mrd7_vars[] = { - {"INT33F8:00_CamType", "1"}, - {"INT33F8:00_CsiPort", "1"}, - {"INT33F8:00_CsiLanes","2"}, - {"INT33F8:00_CsiFmt","13"}, - {"INT33F8:00_CsiBayer", "0"}, - {"INT33F8:00_CamClk", "0"}, - {"INT33F9:00_CamType", "1"}, - {"INT33F9:00_CsiPort", "0"}, - {"INT33F9:00_CsiLanes","1"}, - {"INT33F9:00_CsiFmt","13"}, - {"INT33F9:00_CsiBayer", "0"}, - {"INT33F9:00_CamClk", "1"}, - {}, + {"INT33F8:00_CamType", "1"}, + {"INT33F8:00_CsiPort", "1"}, + {"INT33F8:00_CsiLanes", "2"}, + {"INT33F8:00_CsiFmt", "13"}, + {"INT33F8:00_CsiBayer", "0"}, + {"INT33F8:00_CamClk", "0"}, + {"INT33F9:00_CamType", "1"}, + {"INT33F9:00_CsiPort", "0"}, + {"INT33F9:00_CsiLanes", "1"}, + {"INT33F9:00_CsiFmt", "13"}, + {"INT33F9:00_CsiBayer", "0"}, + {"INT33F9:00_CamClk", "1"}, + {}, }; static const struct gmin_cfg_var ecs7_vars[] = { - {"INT33BE:00_CsiPort", "1"}, - {"INT33BE:00_CsiLanes","2"}, - {"INT33BE:00_CsiFmt","13"}, - {"INT33BE:00_CsiBayer", "2"}, - {"INT33BE:00_CamClk", "0"}, - {"INT33F0:00_CsiPort", "0"}, - {"INT33F0:00_CsiLanes","1"}, - {"INT33F0:00_CsiFmt","13"}, - {"INT33F0:00_CsiBayer", "0"}, - {"INT33F0:00_CamClk", "1"}, - {"gmin_V2P8GPIO","402"}, - {}, + {"INT33BE:00_CsiPort", "1"}, + {"INT33BE:00_CsiLanes", "2"}, + {"INT33BE:00_CsiFmt", "13"}, + {"INT33BE:00_CsiBayer", "2"}, + {"INT33BE:00_CamClk", "0"}, + {"INT33F0:00_CsiPort", "0"}, + {"INT33F0:00_CsiLanes", "1"}, + {"INT33F0:00_CsiFmt", "13"}, + {"INT33F0:00_CsiBayer", "0"}, + {"INT33F0:00_CamClk", "1"}, + {"gmin_V2P8GPIO", "402"}, + {}, }; static const struct gmin_cfg_var i8880_vars[] = { - {"XXOV2680:00_CsiPort", "1"}, - {"XXOV2680:00_CsiLanes","1"}, - {"XXOV2680:00_CamClk","0"}, - {"XXGC0310:00_CsiPort", "0"}, - {"XXGC0310:00_CsiLanes", "1"}, - {"XXGC0310:00_CamClk", "1"}, - {}, + {"XXOV2680:00_CsiPort", "1"}, + {"XXOV2680:00_CsiLanes", "1"}, + {"XXOV2680:00_CamClk", "0"}, + {"XXGC0310:00_CsiPort", "0"}, + {"XXGC0310:00_CsiLanes", "1"}, + {"XXGC0310:00_CamClk", "1"}, + {}, }; static const struct { @@ -317,9 +317,9 @@ static const struct { } hard_vars[] = { { "BYT-T FFD8", ffrd8_vars }, { "T100TA", t100_vars }, - { "MRD7", mrd7_vars }, - { "ST70408", ecs7_vars }, - { "VTA0803", i8880_vars }, + { "MRD7", mrd7_vars }, + { "ST70408", ecs7_vars }, + { "VTA0803", i8880_vars }, }; @@ -343,7 +343,7 @@ static struct gmin_subdev *gmin_subdev_add(struct v4l2_subdev *subdev) { int i, ret; struct device *dev; - struct i2c_client *client = v4l2_get_subdevdata(subdev); + struct i2c_client *client = v4l2_get_subdevdata(subdev); if (!pmic_id) { @@ -355,7 +355,7 @@ static struct gmin_subdev *gmin_subdev_add(struct v4l2_subdev *subdev) dev = &client->dev; - for (i=0; i < MAX_SUBDEVS && gmin_subdevs[i].subdev; i++) + for (i = 0; i < MAX_SUBDEVS && gmin_subdevs[i].subdev; i++) ; if (i >= MAX_SUBDEVS) return NULL; @@ -410,7 +410,7 @@ static struct gmin_subdev *gmin_subdev_add(struct v4l2_subdev *subdev) static struct gmin_subdev *find_gmin_subdev(struct v4l2_subdev *subdev) { int i; - for (i=0; i < MAX_SUBDEVS; i++) + for (i = 0; i < MAX_SUBDEVS; i++) if (gmin_subdevs[i].subdev == subdev) return &gmin_subdevs[i]; return gmin_subdev_add(subdev); @@ -482,7 +482,7 @@ static int gmin_v1p8_ctrl(struct v4l2_subdev *subdev, int on) gpio_set_value(v1p8_gpio, on); if (gs->v1p8_reg) { - regulator_set_voltage(gs->v1p8_reg, 1800000, 1800000); + regulator_set_voltage(gs->v1p8_reg, 1800000, 1800000); if (on) return regulator_enable(gs->v1p8_reg); else @@ -518,7 +518,7 @@ static int gmin_v2p8_ctrl(struct v4l2_subdev *subdev, int on) gpio_set_value(v2p8_gpio, on); if (gs->v2p8_reg) { - regulator_set_voltage(gs->v2p8_reg, 2900000, 2900000); + regulator_set_voltage(gs->v2p8_reg, 2900000, 2900000); if (on) return regulator_enable(gs->v2p8_reg); else @@ -628,13 +628,13 @@ int gmin_get_config_var(struct device *dev, const char *var, char *out, size_t * int i, j, ret; unsigned long efilen; - if (dev && ACPI_COMPANION(dev)) - dev = &ACPI_COMPANION(dev)->dev; + if (dev && ACPI_COMPANION(dev)) + dev = &ACPI_COMPANION(dev)->dev; - if (dev) - ret = snprintf(var8, sizeof(var8), "%s_%s", dev_name(dev), var); - else - ret = snprintf(var8, sizeof(var8), "gmin_%s", var); + if (dev) + ret = snprintf(var8, sizeof(var8), "%s_%s", dev_name(dev), var); + else + ret = snprintf(var8, sizeof(var8), "gmin_%s", var); if (ret < 0 || ret >= sizeof(var8) - 1) return -EINVAL; @@ -693,7 +693,7 @@ int gmin_get_config_var(struct device *dev, const char *var, char *out, size_t * *out_len = efilen; if (ret) - dev_warn(dev, "Failed to find gmin variable %s\n", var8); + dev_warn(dev, "Failed to find gmin variable %s\n", var8); return ret; } @@ -719,31 +719,31 @@ EXPORT_SYMBOL_GPL(gmin_get_var_int); int camera_sensor_csi(struct v4l2_subdev *sd, u32 port, u32 lanes, u32 format, u32 bayer_order, int flag) { - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct camera_mipi_info *csi = NULL; - - if (flag) { - csi = kzalloc(sizeof(*csi), GFP_KERNEL); - if (!csi) { - dev_err(&client->dev, "out of memory\n"); - return -ENOMEM; - } - csi->port = port; - csi->num_lanes = lanes; - csi->input_format = format; - csi->raw_bayer_order = bayer_order; - v4l2_set_subdev_hostdata(sd, (void *)csi); - csi->metadata_format = ATOMISP_INPUT_FORMAT_EMBEDDED; - csi->metadata_effective_width = NULL; - dev_info(&client->dev, - "camera pdata: port: %d lanes: %d order: %8.8x\n", - port, lanes, bayer_order); - } else { - csi = v4l2_get_subdev_hostdata(sd); - kfree(csi); - } - - return 0; + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct camera_mipi_info *csi = NULL; + + if (flag) { + csi = kzalloc(sizeof(*csi), GFP_KERNEL); + if (!csi) { + dev_err(&client->dev, "out of memory\n"); + return -ENOMEM; + } + csi->port = port; + csi->num_lanes = lanes; + csi->input_format = format; + csi->raw_bayer_order = bayer_order; + v4l2_set_subdev_hostdata(sd, (void *)csi); + csi->metadata_format = ATOMISP_INPUT_FORMAT_EMBEDDED; + csi->metadata_effective_width = NULL; + dev_info(&client->dev, + "camera pdata: port: %d lanes: %d order: %8.8x\n", + port, lanes, bayer_order); + } else { + csi = v4l2_get_subdev_hostdata(sd); + kfree(csi); + } + + return 0; } EXPORT_SYMBOL_GPL(camera_sensor_csi); diff --git a/drivers/staging/media/atomisp/platform/intel-mid/intel_mid_pcihelpers.c b/drivers/staging/media/atomisp/platform/intel-mid/intel_mid_pcihelpers.c index b01463361943..c9eea71ece2c 100644 --- a/drivers/staging/media/atomisp/platform/intel-mid/intel_mid_pcihelpers.c +++ b/drivers/staging/media/atomisp/platform/intel-mid/intel_mid_pcihelpers.c @@ -22,7 +22,7 @@ #endif static inline int platform_is(u8 model) { - return (boot_cpu_data.x86_model == model); + return (boot_cpu_data.x86_model == model); } #include "../../include/asm/intel_mid_pcihelpers.h" -- cgit v1.2.3-58-ga151 From 8c7e9a738155b8c6c0bc3662d8f5a264106c712c Mon Sep 17 00:00:00 2001 From: Avraham Shukron Date: Thu, 18 May 2017 10:50:16 -0300 Subject: [media] atomisp: fix coding style warnings Fix for warnings reported by checkpatch.pl: - Multiline comment style - Bare "unsigned" - Missing blank line after declarations - Un-needed braces around single-statement branch Signed-off-by: Avraham Shukron Signed-off-by: Greg Kroah-Hartman Signed-off-by: Mauro Carvalho Chehab --- .../platform/intel-mid/atomisp_gmin_platform.c | 45 ++++++++++++++-------- .../platform/intel-mid/intel_mid_pcihelpers.c | 9 +++-- 2 files changed, 35 insertions(+), 19 deletions(-) diff --git a/drivers/staging/media/atomisp/platform/intel-mid/atomisp_gmin_platform.c b/drivers/staging/media/atomisp/platform/intel-mid/atomisp_gmin_platform.c index 2a819ac6f9e2..d68e9cf33aa7 100644 --- a/drivers/staging/media/atomisp/platform/intel-mid/atomisp_gmin_platform.c +++ b/drivers/staging/media/atomisp/platform/intel-mid/atomisp_gmin_platform.c @@ -119,7 +119,7 @@ static int af_power_ctrl(struct v4l2_subdev *subdev, int flag) /* * The power here is used for dw9817, * regulator is from rear sensor - */ + */ if (gs->v2p8_vcm_reg) { if (flag) return regulator_enable(gs->v2p8_vcm_reg); @@ -167,7 +167,8 @@ int atomisp_register_i2c_module(struct v4l2_subdev *subdev, * uses ACPI runtime power management for camera devices, but * we don't. Disable it, or else the rails will be needlessly * tickled during suspend/resume. This has caused power and - * performance issues on multiple devices. */ + * performance issues on multiple devices. + */ adev = ACPI_COMPANION(&client->dev); if (adev) adev->power.flags.power_resources = 0; @@ -182,7 +183,8 @@ int atomisp_register_i2c_module(struct v4l2_subdev *subdev, /* Note subtlety of initialization order: at the point where * this registration API gets called, the platform data * callbacks have probably already been invoked, so the - * gmin_subdev struct is already initialized for us. */ + * gmin_subdev struct is already initialized for us. + */ gs = find_gmin_subdev(subdev); pdata.subdevs[i].type = type; @@ -206,8 +208,10 @@ struct v4l2_subdev *atomisp_gmin_find_subdev(struct i2c_adapter *adapter, struct i2c_board_info *board_info) { int i; + for (i = 0; i < MAX_SUBDEVS && pdata.subdevs[i].type; i++) { struct intel_v4l2_subdev_table *sd = &pdata.subdevs[i]; + if (sd->v4l2_subdev.i2c_adapter_id == adapter->nr && sd->v4l2_subdev.board_info.addr == board_info->addr) return sd->subdev; @@ -261,7 +265,8 @@ static const struct gmin_cfg_var ffrd8_vars[] = { }; /* Cribbed from MCG defaults in the mt9m114 driver, not actually verified - * vs. T100 hardware */ + * vs. T100 hardware + */ static const struct gmin_cfg_var t100_vars[] = { { "INT33F0:00_CsiPort", "0" }, { "INT33F0:00_CsiLanes", "1" }, @@ -345,10 +350,8 @@ static struct gmin_subdev *gmin_subdev_add(struct v4l2_subdev *subdev) struct device *dev; struct i2c_client *client = v4l2_get_subdevdata(subdev); - if (!pmic_id) { - - pmic_id = PMIC_REGULATOR; - } + if (!pmic_id) + pmic_id = PMIC_REGULATOR; if (!client) return NULL; @@ -401,7 +404,8 @@ static struct gmin_subdev *gmin_subdev_add(struct v4l2_subdev *subdev) * API is broken with the current drivers, returning * "1" for a regulator that will then emit a * "unbalanced disable" WARNing if we try to disable - * it. */ + * it. + */ } return &gmin_subdevs[i]; @@ -410,6 +414,7 @@ static struct gmin_subdev *gmin_subdev_add(struct v4l2_subdev *subdev) static struct gmin_subdev *find_gmin_subdev(struct v4l2_subdev *subdev) { int i; + for (i = 0; i < MAX_SUBDEVS; i++) if (gmin_subdevs[i].subdev == subdev) return &gmin_subdevs[i]; @@ -419,6 +424,7 @@ static struct gmin_subdev *find_gmin_subdev(struct v4l2_subdev *subdev) static int gmin_gpio0_ctrl(struct v4l2_subdev *subdev, int on) { struct gmin_subdev *gs = find_gmin_subdev(subdev); + if (gs && gs->gpio0) { gpiod_set_value(gs->gpio0, on); return 0; @@ -429,6 +435,7 @@ static int gmin_gpio0_ctrl(struct v4l2_subdev *subdev, int on) static int gmin_gpio1_ctrl(struct v4l2_subdev *subdev, int on) { struct gmin_subdev *gs = find_gmin_subdev(subdev); + if (gs && gs->gpio1) { gpiod_set_value(gs->gpio1, on); return 0; @@ -532,6 +539,7 @@ static int gmin_flisclk_ctrl(struct v4l2_subdev *subdev, int on) { int ret = 0; struct gmin_subdev *gs = find_gmin_subdev(subdev); + if (on) ret = vlv2_plat_set_clock_freq(gs->clock_num, gs->clock_src); if (ret) @@ -596,6 +604,7 @@ struct camera_sensor_platform_data *gmin_camera_platform_data( enum atomisp_bayer_order csi_bayer) { struct gmin_subdev *gs = find_gmin_subdev(subdev); + gs->csi_fmt = csi_format; gs->csi_bayer = csi_bayer; @@ -618,8 +627,10 @@ EXPORT_SYMBOL_GPL(atomisp_gmin_register_vcm_control); /* Retrieves a device-specific configuration variable. The dev * argument should be a device with an ACPI companion, as all - * configuration is based on firmware ID. */ -int gmin_get_config_var(struct device *dev, const char *var, char *out, size_t *out_len) + * configuration is based on firmware ID. + */ +int gmin_get_config_var(struct device *dev, const char *var, char *out, + size_t *out_len) { char var8[CFG_VAR_NAME_MAX]; efi_char16_t var16[CFG_VAR_NAME_MAX]; @@ -641,7 +652,8 @@ int gmin_get_config_var(struct device *dev, const char *var, char *out, size_t * /* First check a hard-coded list of board-specific variables. * Some device firmwares lack the ability to set EFI variables at - * runtime. */ + * runtime. + */ for (i = 0; i < ARRAY_SIZE(hard_vars); i++) { if (dmi_match(DMI_BOARD_NAME, hard_vars[i].dmi_board_name)) { for (j = 0; hard_vars[i].vars[j].name; j++) { @@ -666,7 +678,8 @@ int gmin_get_config_var(struct device *dev, const char *var, char *out, size_t * } /* Our variable names are ASCII by construction, but EFI names - * are wide chars. Convert and zero-pad. */ + * are wide chars. Convert and zero-pad. + */ memset(var16, 0, sizeof(var16)); for (i = 0; i < sizeof(var8) && var8[i]; i++) var16[i] = var8[i]; @@ -679,7 +692,8 @@ int gmin_get_config_var(struct device *dev, const char *var, char *out, size_t * * implementation simply uses VariableName and VendorGuid from * the struct and ignores the rest, but it seems like there * ought to be an "official" efivar_entry registered - * somewhere? */ + * somewhere? + */ ev = kzalloc(sizeof(*ev), GFP_KERNEL); if (!ev) return -ENOMEM; @@ -750,7 +764,8 @@ EXPORT_SYMBOL_GPL(camera_sensor_csi); /* PCI quirk: The BYT ISP advertises PCI runtime PM but it doesn't * work. Disable so the kernel framework doesn't hang the device * trying. The driver itself does direct calls to the PUNIT to manage - * ISP power. */ + * ISP power. + */ static void isp_pm_cap_fixup(struct pci_dev *dev) { dev_info(&dev->dev, "Disabling PCI power management on camera ISP\n"); diff --git a/drivers/staging/media/atomisp/platform/intel-mid/intel_mid_pcihelpers.c b/drivers/staging/media/atomisp/platform/intel-mid/intel_mid_pcihelpers.c index c9eea71ece2c..cd452cc20fea 100644 --- a/drivers/staging/media/atomisp/platform/intel-mid/intel_mid_pcihelpers.c +++ b/drivers/staging/media/atomisp/platform/intel-mid/intel_mid_pcihelpers.c @@ -5,7 +5,8 @@ /* G-Min addition: "platform_is()" lives in intel_mid_pm.h in the MCG * tree, but it's just platform ID info and we don't want to pull in - * the whole SFI-based PM architecture. */ + * the whole SFI-based PM architecture. + */ #define INTEL_ATOM_MRST 0x26 #define INTEL_ATOM_MFLD 0x27 #define INTEL_ATOM_CLV 0x35 @@ -135,8 +136,8 @@ u32 intel_mid_msgbus_read32(u8 port, u32 addr) return data; } - EXPORT_SYMBOL(intel_mid_msgbus_read32); + void intel_mid_msgbus_write32(u8 port, u32 addr, u32 data) { unsigned long irq_flags; @@ -170,8 +171,8 @@ EXPORT_SYMBOL(intel_mid_soc_stepping); static bool is_south_complex_device(struct pci_dev *dev) { - unsigned base_class = dev->class >> 16; - unsigned sub_class = (dev->class & SUB_CLASS_MASK) >> 8; + unsigned int base_class = dev->class >> 16; + unsigned int sub_class = (dev->class & SUB_CLASS_MASK) >> 8; /* other than camera, pci bridges and display, * everything else are south complex devices. -- cgit v1.2.3-58-ga151 From c2c611b7b4f6cccbaea47e6b4a6853a129ddd1f8 Mon Sep 17 00:00:00 2001 From: Guru Das Srinagesh Date: Thu, 18 May 2017 10:50:17 -0300 Subject: [media] atomisp: Make undeclared symbols static Fix sparse warnings: "symbol not declared; should it be static?" Signed-off-by: Guru Das Srinagesh Signed-off-by: Greg Kroah-Hartman Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/atomisp/pci/atomisp2/atomisp_fops.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/atomisp2/atomisp_fops.c b/drivers/staging/media/atomisp/pci/atomisp2/atomisp_fops.c index 7ce8803cf6f9..c151c848cf8f 100644 --- a/drivers/staging/media/atomisp/pci/atomisp2/atomisp_fops.c +++ b/drivers/staging/media/atomisp/pci/atomisp2/atomisp_fops.c @@ -130,9 +130,9 @@ static int atomisp_q_one_metadata_buffer(struct atomisp_sub_device *asd, return 0; } -int atomisp_q_one_s3a_buffer(struct atomisp_sub_device *asd, - enum atomisp_input_stream_id stream_id, - enum atomisp_css_pipe_id css_pipe_id) +static int atomisp_q_one_s3a_buffer(struct atomisp_sub_device *asd, + enum atomisp_input_stream_id stream_id, + enum atomisp_css_pipe_id css_pipe_id) { struct atomisp_s3a_buf *s3a_buf; struct list_head *s3a_list; @@ -172,9 +172,9 @@ int atomisp_q_one_s3a_buffer(struct atomisp_sub_device *asd, return 0; } -int atomisp_q_one_dis_buffer(struct atomisp_sub_device *asd, - enum atomisp_input_stream_id stream_id, - enum atomisp_css_pipe_id css_pipe_id) +static int atomisp_q_one_dis_buffer(struct atomisp_sub_device *asd, + enum atomisp_input_stream_id stream_id, + enum atomisp_css_pipe_id css_pipe_id) { struct atomisp_dis_buf *dis_buf; unsigned long irqflags; @@ -744,7 +744,7 @@ static void atomisp_subdev_init_struct(struct atomisp_sub_device *asd) /* * file operation functions */ -unsigned int atomisp_subdev_users(struct atomisp_sub_device *asd) +static unsigned int atomisp_subdev_users(struct atomisp_sub_device *asd) { return asd->video_out_preview.users + asd->video_out_vf.users + -- cgit v1.2.3-58-ga151 From 02d978318b0983e7af6bbc9c1f9861b0d6983e37 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 18 May 2017 10:50:18 -0300 Subject: [media] atomisp: Fix -Werror=int-in-bool-context compile errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With gcc-7.1.1 I was getting the following compile error: error: ‘*’ in boolean context, suggest ‘&&’ instead The problem is the definition of CEIL_DIV: #define CEIL_DIV(a, b) ((b) ? ((a) + (b) - 1) / (b) : 0) Which when called as: CEIL_DIV(x, y * z) triggers this error, note we cannot do as the error suggests since b is evaluated multiple times. This commit fixes these compile errors. Signed-off-by: Hans de Goede Signed-off-by: Greg Kroah-Hartman Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/atomisp/pci/atomisp2/atomisp_compat_css20.c | 1 - .../pci/atomisp2/css2400/hive_isp_css_include/math_support.h | 6 +++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/atomisp2/atomisp_compat_css20.c b/drivers/staging/media/atomisp/pci/atomisp2/atomisp_compat_css20.c index b830b241e2e6..ad2c610d2ce3 100644 --- a/drivers/staging/media/atomisp/pci/atomisp2/atomisp_compat_css20.c +++ b/drivers/staging/media/atomisp/pci/atomisp2/atomisp_compat_css20.c @@ -2506,7 +2506,6 @@ static void __configure_capture_pp_input(struct atomisp_sub_device *asd, struct ia_css_pipe_extra_config *pipe_extra_configs = &stream_env->pipe_extra_configs[pipe_id]; unsigned int hor_ds_factor = 0, ver_ds_factor = 0; -#define CEIL_DIV(a, b) ((b) ? ((a) + (b) - 1) / (b) : 0) if (width == 0 && height == 0) return; diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/math_support.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/math_support.h index 48d84bc0ad9e..f74b405b0f39 100644 --- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/math_support.h +++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/math_support.h @@ -62,15 +62,15 @@ #define MAX(a, b) (((a) > (b)) ? (a) : (b)) #define MIN(a, b) (((a) < (b)) ? (a) : (b)) #ifdef ISP2401 -#define ROUND_DIV(a, b) ((b) ? ((a) + ((b) >> 1)) / (b) : 0) +#define ROUND_DIV(a, b) (((b) != 0) ? ((a) + ((b) >> 1)) / (b) : 0) #endif -#define CEIL_DIV(a, b) ((b) ? ((a) + (b) - 1) / (b) : 0) +#define CEIL_DIV(a, b) (((b) != 0) ? ((a) + (b) - 1) / (b) : 0) #define CEIL_MUL(a, b) (CEIL_DIV(a, b) * (b)) #define CEIL_MUL2(a, b) (((a) + (b) - 1) & ~((b) - 1)) #define CEIL_SHIFT(a, b) (((a) + (1 << (b)) - 1)>>(b)) #define CEIL_SHIFT_MUL(a, b) (CEIL_SHIFT(a, b) << (b)) #ifdef ISP2401 -#define ROUND_HALF_DOWN_DIV(a, b) ((b) ? ((a) + (b / 2) - 1) / (b) : 0) +#define ROUND_HALF_DOWN_DIV(a, b) (((b) != 0) ? ((a) + (b / 2) - 1) / (b) : 0) #define ROUND_HALF_DOWN_MUL(a, b) (ROUND_HALF_DOWN_DIV(a, b) * (b)) #endif -- cgit v1.2.3-58-ga151 From d1fec5bdeb18ff98fbe317a9454bcd1fe8da1bee Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 18 May 2017 10:50:19 -0300 Subject: [media] atomisp: one char read beyond end of string We should verify that "ix < max_len" before we test whether we have reached the NUL terminator. Fixes: a49d25364dfb ("staging/atomisp: Add support for the Intel IPU v2") Reported-by: David Binderman Signed-off-by: Dan Carpenter Signed-off-by: Greg Kroah-Hartman Signed-off-by: Mauro Carvalho Chehab --- .../pci/atomisp2/css2400/hive_isp_css_include/string_support.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/string_support.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/string_support.h index 568631698a3d..74b5a1c7ac9a 100644 --- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/string_support.h +++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/string_support.h @@ -72,9 +72,8 @@ static size_t strnlen_s( return 0; } - for (ix=0; - ((src_str[ix] != '\0') && (ix< max_len)); - ++ix) /*Nothing else to do*/; + for (ix = 0; ix < max_len && src_str[ix] != '\0'; ix++) + ; /* On Error, it will return src_size == max_len*/ return ix; -- cgit v1.2.3-58-ga151 From c32e3d1b490e89c592d0c59f10fd267c4847cbaf Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 18 May 2017 10:50:20 -0300 Subject: [media] atomisp: putting NULs in the wrong place We're putting the NUL terminators one space beyond where they belong. This doesn't show up in testing because all but the callers put a NUL in the correct place themselves. LOL. It causes a static checker warning about buffer overflows. Fixes: a49d25364dfb ("staging/atomisp: Add support for the Intel IPU v2") Signed-off-by: Dan Carpenter Signed-off-by: Greg Kroah-Hartman Signed-off-by: Mauro Carvalho Chehab --- .../pci/atomisp2/css2400/hive_isp_css_include/string_support.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/string_support.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/string_support.h index 74b5a1c7ac9a..c53241a7a281 100644 --- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/string_support.h +++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/string_support.h @@ -117,7 +117,7 @@ STORAGE_CLASS_INLINE int strncpy_s( /* dest_str is big enough for the len */ strncpy(dest_str, src_str, len); - dest_str[len+1] = '\0'; + dest_str[len] = '\0'; return 0; } @@ -157,7 +157,7 @@ STORAGE_CLASS_INLINE int strcpy_s( /* dest_str is big enough for the len */ strncpy(dest_str, src_str, len); - dest_str[len+1] = '\0'; + dest_str[len] = '\0'; return 0; } -- cgit v1.2.3-58-ga151 From 54221e7248a66e1748f4bc93790042294bb6914b Mon Sep 17 00:00:00 2001 From: Manny Vindiola Date: Thu, 18 May 2017 10:50:21 -0300 Subject: [media] atomisp: fix missing blank line coding style issue in atomisp_tpg.c This is a patch to the atomisp_tpg.c file that fixes up a missing blank line warning found by the checkpatch.pl tool Signed-off-by: Manny Vindiola Signed-off-by: Greg Kroah-Hartman Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/atomisp/pci/atomisp2/atomisp_tpg.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/media/atomisp/pci/atomisp2/atomisp_tpg.c b/drivers/staging/media/atomisp/pci/atomisp2/atomisp_tpg.c index 996d1bdebad4..48b96048cab4 100644 --- a/drivers/staging/media/atomisp/pci/atomisp2/atomisp_tpg.c +++ b/drivers/staging/media/atomisp/pci/atomisp2/atomisp_tpg.c @@ -56,6 +56,7 @@ static int tpg_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_format *format) { struct v4l2_mbus_framefmt *fmt = &format->format; + if (format->pad) return -EINVAL; /* only raw8 grbg is supported by TPG */ -- cgit v1.2.3-58-ga151 From bd7e31bbade02bc1e92aa00d5cf2cee2da66838a Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 11 May 2017 08:46:44 -0300 Subject: [media] ir-core: fix gcc-7 warning on bool arithmetic gcc-7 suggests that an expression using a bitwise not and a bitmask on a 'bool' variable is better written using boolean logic: drivers/media/rc/imon.c: In function 'imon_incoming_scancode': drivers/media/rc/imon.c:1725:22: error: '~' on a boolean expression [-Werror=bool-operation] ictx->pad_mouse = ~(ictx->pad_mouse) & 0x1; ^ drivers/media/rc/imon.c:1725:22: note: did you mean to use logical not? I agree. Fixes: 21677cfc562a ("V4L/DVB: ir-core: add imon driver") Signed-off-by: Arnd Bergmann Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/imon.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/rc/imon.c b/drivers/media/rc/imon.c index 3489010601b5..bd76534a2749 100644 --- a/drivers/media/rc/imon.c +++ b/drivers/media/rc/imon.c @@ -1722,7 +1722,7 @@ static void imon_incoming_scancode(struct imon_context *ictx, if (kc == KEY_KEYBOARD && !ictx->release_code) { ictx->last_keycode = kc; if (!nomouse) { - ictx->pad_mouse = ~(ictx->pad_mouse) & 0x1; + ictx->pad_mouse = !ictx->pad_mouse; dev_dbg(dev, "toggling to %s mode\n", ictx->pad_mouse ? "mouse" : "keyboard"); spin_unlock_irqrestore(&ictx->kc_lock, flags); -- cgit v1.2.3-58-ga151 From 17992979f6f3d3c531498e7f37bfdaf28f50848f Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 18 May 2017 08:30:20 -0300 Subject: [media] bcm3510: fix handling of VSB16 modulation There's a missing break for VSB16 modulation logic, with would cause it to return -EINVAL, instead of handling it. Fix it. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/bcm3510.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/dvb-frontends/bcm3510.c b/drivers/media/dvb-frontends/bcm3510.c index 617c5e29f919..30cfc0f2b575 100644 --- a/drivers/media/dvb-frontends/bcm3510.c +++ b/drivers/media/dvb-frontends/bcm3510.c @@ -538,6 +538,7 @@ static int bcm3510_set_frontend(struct dvb_frontend *fe) cmd.ACQUIRE0.MODE = 0x9; cmd.ACQUIRE1.SYM_RATE = 0x0; cmd.ACQUIRE1.IF_FREQ = 0x0; + break; default: return -EINVAL; } -- cgit v1.2.3-58-ga151 From b7c15d5d90661f6eaa49f7a7a72d482fe1923d4a Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 18 May 2017 08:55:41 -0300 Subject: [media] saa7164: better handle error codes Right now, the driver is doing the right thing for PVC_ERRORCODE_UNKNOWN and PVC_ERRORCODE_INVALID_CONTROL: for both, it returns an error code (SAA_ERR_NOT_SUPPORTED). However, it is printing two error messages instead of one on those cases. Fix the logic. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/saa7164/saa7164-cmd.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/pci/saa7164/saa7164-cmd.c b/drivers/media/pci/saa7164/saa7164-cmd.c index 175015ca79f2..dfebd77ada59 100644 --- a/drivers/media/pci/saa7164/saa7164-cmd.c +++ b/drivers/media/pci/saa7164/saa7164-cmd.c @@ -506,6 +506,8 @@ int saa7164_cmd_send(struct saa7164_dev *dev, u8 id, enum tmComResCmd command, dprintk(DBGLVL_CMD, "%s() UNKNOWN OR INVALID CONTROL\n", __func__); + ret = SAA_ERR_NOT_SUPPORTED; + break; default: dprintk(DBGLVL_CMD, "%s() UNKNOWN\n", __func__); ret = SAA_ERR_NOT_SUPPORTED; -- cgit v1.2.3-58-ga151 From a042023f1bde77ca01f6bd5e98a1b8ba8aed8b6c Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 18 May 2017 08:52:25 -0300 Subject: [media] bt8xx: add missing break The logic that handles CA_SET_PID is clearly missing a break: it prints that the command succeeded, but, due to the missing break, it would be returning -EOPNOTSUPP, as if the driver weren't supporting such ioctl. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/bt8xx/dst_ca.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/pci/bt8xx/dst_ca.c b/drivers/media/pci/bt8xx/dst_ca.c index 04d06c564602..90f4263452d3 100644 --- a/drivers/media/pci/bt8xx/dst_ca.c +++ b/drivers/media/pci/bt8xx/dst_ca.c @@ -637,6 +637,7 @@ static long dst_ca_ioctl(struct file *file, unsigned int cmd, unsigned long ioct goto free_mem_and_exit; } dprintk(verbose, DST_CA_INFO, 1, " -->CA_SET_PID Success !"); + break; default: result = -EOPNOTSUPP; } -- cgit v1.2.3-58-ga151 From 39c4806e0a6d8856d48ccea3af1d9569351facfe Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 18 May 2017 09:15:52 -0300 Subject: [media] dvb-usb-remote: don't write bogus debug messages When a REMOTE_KEY_PRESSED event happens, it does the right thing. However, if debug is enabled, it will print a bogus message warning that "key repeated". Fix it. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb/dvb-usb-remote.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/media/usb/dvb-usb/dvb-usb-remote.c b/drivers/media/usb/dvb-usb/dvb-usb-remote.c index 059ded59208e..f05f1fc80729 100644 --- a/drivers/media/usb/dvb-usb/dvb-usb-remote.c +++ b/drivers/media/usb/dvb-usb/dvb-usb-remote.c @@ -131,6 +131,11 @@ static void legacy_dvb_usb_read_remote_control(struct work_struct *work) case REMOTE_KEY_PRESSED: deb_rc("key pressed\n"); d->last_event = event; + input_event(d->input_dev, EV_KEY, event, 1); + input_sync(d->input_dev); + input_event(d->input_dev, EV_KEY, d->last_event, 0); + input_sync(d->input_dev); + break; case REMOTE_KEY_REPEAT: deb_rc("key repeated\n"); input_event(d->input_dev, EV_KEY, event, 1); -- cgit v1.2.3-58-ga151 From 06eeefe8e310bf955da7f82547c72c43e4653d97 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 18 May 2017 08:13:28 -0300 Subject: [media] media drivers: annotate fall-through Avoid warnings like those: drivers/media/pci/ddbridge/ddbridge-core.c: In function 'dvb_input_detach': drivers/media/pci/ddbridge/ddbridge-core.c:787:6: warning: this statement may fall through [-Wimplicit-fallthrough=] if (input->fe) { ^ drivers/media/pci/ddbridge/ddbridge-core.c:792:2: note: here case 4: ^~~~ ... On several cases, it is just that gcc 7.1 is not capable of understanding the comment, but on other places, we need an annotation. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/af9013.c | 1 + drivers/media/dvb-frontends/bcm3510.c | 3 +- drivers/media/dvb-frontends/dib7000p.c | 6 +-- drivers/media/dvb-frontends/drx39xyj/drxj.c | 20 +++++----- drivers/media/dvb-frontends/drxd_hard.c | 10 +++-- drivers/media/dvb-frontends/drxk_hard.c | 20 ++++++---- drivers/media/dvb-frontends/mt352.c | 1 + drivers/media/dvb-frontends/or51132.c | 4 +- drivers/media/dvb-frontends/s5h1411.c | 4 +- drivers/media/dvb-frontends/zl10353.c | 3 +- drivers/media/i2c/msp3400-kthreads.c | 1 + drivers/media/i2c/soc_camera/ov6650.c | 2 + drivers/media/pci/cx23885/cx23885-cards.c | 3 +- drivers/media/pci/cx88/cx88-video.c | 2 +- drivers/media/pci/ddbridge/ddbridge-core.c | 7 ++-- drivers/media/pci/saa7134/saa7134-cards.c | 4 +- drivers/media/pci/solo6x10/solo6x10-core.c | 1 + drivers/media/pci/solo6x10/solo6x10-i2c.c | 1 + drivers/media/platform/exynos4-is/fimc-capture.c | 3 ++ drivers/media/platform/marvell-ccic/mcam-core.c | 1 + drivers/media/platform/pxa_camera.c | 1 + drivers/media/platform/sh_vou.c | 2 + drivers/media/rc/iguanair.c | 1 + drivers/media/tuners/tda18271-fe.c | 2 +- drivers/media/tuners/xc5000.c | 1 + drivers/media/usb/cpia2/cpia2_core.c | 51 ++++++++++++++++-------- drivers/media/usb/cx231xx/cx231xx-video.c | 2 +- drivers/media/usb/dvb-usb-v2/af9015.c | 1 + drivers/media/usb/dvb-usb-v2/lmedm04.c | 1 + drivers/media/usb/dvb-usb/dib0700_devices.c | 1 + drivers/media/usb/dvb-usb/dw2102.c | 4 +- drivers/media/usb/gspca/ov519.c | 3 +- drivers/media/usb/pwc/pwc-v4l.c | 3 +- drivers/media/usb/usbvision/usbvision-i2c.c | 3 ++ drivers/media/usb/uvc/uvc_video.c | 2 +- 35 files changed, 117 insertions(+), 58 deletions(-) diff --git a/drivers/media/dvb-frontends/af9013.c b/drivers/media/dvb-frontends/af9013.c index b978002af4d8..51f942fc0669 100644 --- a/drivers/media/dvb-frontends/af9013.c +++ b/drivers/media/dvb-frontends/af9013.c @@ -535,6 +535,7 @@ static void af9013_statistics_work(struct work_struct *work) switch (state->statistics_step) { default: state->statistics_step = 0; + /* fall-through */ case 0: af9013_statistics_signal_strength(&state->fe); state->statistics_step++; diff --git a/drivers/media/dvb-frontends/bcm3510.c b/drivers/media/dvb-frontends/bcm3510.c index 30cfc0f2b575..ba63ad170d3c 100644 --- a/drivers/media/dvb-frontends/bcm3510.c +++ b/drivers/media/dvb-frontends/bcm3510.c @@ -773,7 +773,8 @@ static int bcm3510_init(struct dvb_frontend* fe) deb_info("attempting to download firmware\n"); if ((ret = bcm3510_init_cold(st)) < 0) return ret; - case JDEC_EEPROM_LOAD_WAIT: /* fall-through is wanted */ + /* fall-through */ + case JDEC_EEPROM_LOAD_WAIT: deb_info("firmware is loaded\n"); bcm3510_check_firmware_version(st); break; diff --git a/drivers/media/dvb-frontends/dib7000p.c b/drivers/media/dvb-frontends/dib7000p.c index 3815ea515364..1caa04d8f60f 100644 --- a/drivers/media/dvb-frontends/dib7000p.c +++ b/drivers/media/dvb-frontends/dib7000p.c @@ -279,10 +279,10 @@ static int dib7000p_set_power_mode(struct dib7000p_state *state, enum dib7000p_p if (state->version != SOC7090) reg_1280 &= ~((1 << 11)); reg_1280 &= ~(1 << 6); - /* fall through wanted to enable the interfaces */ - + /* fall-through */ + case DIB7000P_POWER_INTERFACE_ONLY: /* just leave power on the control-interfaces: GPIO and (I2C or SDIO) */ - case DIB7000P_POWER_INTERFACE_ONLY: /* TODO power up either SDIO or I2C */ + /* TODO power up either SDIO or I2C */ if (state->version == SOC7090) reg_1280 &= ~((1 << 7) | (1 << 5)); else diff --git a/drivers/media/dvb-frontends/drx39xyj/drxj.c b/drivers/media/dvb-frontends/drx39xyj/drxj.c index daeaf965dd56..14040c915dbb 100644 --- a/drivers/media/dvb-frontends/drx39xyj/drxj.c +++ b/drivers/media/dvb-frontends/drx39xyj/drxj.c @@ -2837,7 +2837,8 @@ ctrl_set_cfg_mpeg_output(struct drx_demod_instance *demod, struct drx_cfg_mpeg_o /* coef = 188/204 */ max_bit_rate = (ext_attr->curr_symbol_rate / 8) * nr_bits * 188; - /* pass through b/c Annex A/c need following settings */ + /* pass through as b/c Annex A/c need following settings */ + /* fall-through */ case DRX_STANDARD_ITU_B: rc = drxj_dap_write_reg16(dev_addr, FEC_OC_FCT_USAGE__A, FEC_OC_FCT_USAGE__PRE, 0); if (rc != 0) { @@ -4776,9 +4777,9 @@ set_frequency(struct drx_demod_instance *demod, No need to account for mirroring on RF */ switch (ext_attr->standard) { - case DRX_STANDARD_ITU_A: /* fallthrough */ - case DRX_STANDARD_ITU_C: /* fallthrough */ - case DRX_STANDARD_PAL_SECAM_LP: /* fallthrough */ + case DRX_STANDARD_ITU_A: + case DRX_STANDARD_ITU_C: + case DRX_STANDARD_PAL_SECAM_LP: case DRX_STANDARD_8VSB: select_pos_image = true; break; @@ -4787,11 +4788,12 @@ set_frequency(struct drx_demod_instance *demod, Sound carrier is already 3Mhz above centre frequency due to tuner setting so now add an extra shift of 1MHz... */ fm_frequency_shift = 1000; - case DRX_STANDARD_ITU_B: /* fallthrough */ - case DRX_STANDARD_NTSC: /* fallthrough */ - case DRX_STANDARD_PAL_SECAM_BG: /* fallthrough */ - case DRX_STANDARD_PAL_SECAM_DK: /* fallthrough */ - case DRX_STANDARD_PAL_SECAM_I: /* fallthrough */ + /*fall through */ + case DRX_STANDARD_ITU_B: + case DRX_STANDARD_NTSC: + case DRX_STANDARD_PAL_SECAM_BG: + case DRX_STANDARD_PAL_SECAM_DK: + case DRX_STANDARD_PAL_SECAM_I: case DRX_STANDARD_PAL_SECAM_L: select_pos_image = false; break; diff --git a/drivers/media/dvb-frontends/drxd_hard.c b/drivers/media/dvb-frontends/drxd_hard.c index 71910561005f..17638e08835a 100644 --- a/drivers/media/dvb-frontends/drxd_hard.c +++ b/drivers/media/dvb-frontends/drxd_hard.c @@ -1517,12 +1517,14 @@ static int SetDeviceTypeId(struct drxd_state *state) switch (deviceId) { case 4: state->diversity = 1; + /* fall through */ case 3: case 7: state->PGA = 1; break; case 6: state->diversity = 1; + /* fall through */ case 5: case 8: break; @@ -1969,7 +1971,8 @@ static int DRX_Start(struct drxd_state *state, s32 off) switch (p->transmission_mode) { default: /* Not set, detect it automatically */ operationMode |= SC_RA_RAM_OP_AUTO_MODE__M; - /* fall through , try first guess DRX_FFTMODE_8K */ + /* try first guess DRX_FFTMODE_8K */ + /* fall through */ case TRANSMISSION_MODE_8K: transmissionParams |= SC_RA_RAM_OP_PARAM_MODE_8K; if (state->type_A) { @@ -2143,8 +2146,8 @@ static int DRX_Start(struct drxd_state *state, s32 off) switch (p->modulation) { default: operationMode |= SC_RA_RAM_OP_AUTO_CONST__M; - /* fall through , try first guess - DRX_CONSTELLATION_QAM64 */ + /* try first guess DRX_CONSTELLATION_QAM64 */ + /* fall through */ case QAM_64: transmissionParams |= SC_RA_RAM_OP_PARAM_CONST_QAM64; if (state->type_A) { @@ -2280,6 +2283,7 @@ static int DRX_Start(struct drxd_state *state, s32 off) break; default: operationMode |= SC_RA_RAM_OP_AUTO_RATE__M; + /* fall through */ case FEC_2_3: transmissionParams |= SC_RA_RAM_OP_PARAM_RATE_2_3; if (state->type_A) { diff --git a/drivers/media/dvb-frontends/drxk_hard.c b/drivers/media/dvb-frontends/drxk_hard.c index 050fe34342d3..48a8aad47a74 100644 --- a/drivers/media/dvb-frontends/drxk_hard.c +++ b/drivers/media/dvb-frontends/drxk_hard.c @@ -3271,10 +3271,12 @@ static int dvbt_sc_command(struct drxk_state *state, case OFDM_SC_RA_RAM_CMD_PROGRAM_PARAM: status |= write16(state, OFDM_SC_RA_RAM_PARAM1__A, param1); /* All commands using 1 parameters */ + /* fall through */ case OFDM_SC_RA_RAM_CMD_SET_ECHO_TIMING: case OFDM_SC_RA_RAM_CMD_USER_IO: status |= write16(state, OFDM_SC_RA_RAM_PARAM0__A, param0); /* All commands using 0 parameters */ + /* fall through */ case OFDM_SC_RA_RAM_CMD_GET_OP_PARAM: case OFDM_SC_RA_RAM_CMD_NULL: /* Write command */ @@ -3782,7 +3784,8 @@ static int set_dvbt(struct drxk_state *state, u16 intermediate_freqk_hz, case TRANSMISSION_MODE_AUTO: default: operation_mode |= OFDM_SC_RA_RAM_OP_AUTO_MODE__M; - /* fall through , try first guess DRX_FFTMODE_8K */ + /* try first guess DRX_FFTMODE_8K */ + /* fall through */ case TRANSMISSION_MODE_8K: transmission_params |= OFDM_SC_RA_RAM_OP_PARAM_MODE_8K; break; @@ -3796,7 +3799,8 @@ static int set_dvbt(struct drxk_state *state, u16 intermediate_freqk_hz, default: case GUARD_INTERVAL_AUTO: operation_mode |= OFDM_SC_RA_RAM_OP_AUTO_GUARD__M; - /* fall through , try first guess DRX_GUARD_1DIV4 */ + /* try first guess DRX_GUARD_1DIV4 */ + /* fall through */ case GUARD_INTERVAL_1_4: transmission_params |= OFDM_SC_RA_RAM_OP_PARAM_GUARD_4; break; @@ -3817,9 +3821,9 @@ static int set_dvbt(struct drxk_state *state, u16 intermediate_freqk_hz, case HIERARCHY_NONE: default: operation_mode |= OFDM_SC_RA_RAM_OP_AUTO_HIER__M; - /* fall through , try first guess SC_RA_RAM_OP_PARAM_HIER_NO */ + /* try first guess SC_RA_RAM_OP_PARAM_HIER_NO */ /* transmission_params |= OFDM_SC_RA_RAM_OP_PARAM_HIER_NO; */ - /* break; */ + /* fall through */ case HIERARCHY_1: transmission_params |= OFDM_SC_RA_RAM_OP_PARAM_HIER_A1; break; @@ -3837,7 +3841,8 @@ static int set_dvbt(struct drxk_state *state, u16 intermediate_freqk_hz, case QAM_AUTO: default: operation_mode |= OFDM_SC_RA_RAM_OP_AUTO_CONST__M; - /* fall through , try first guess DRX_CONSTELLATION_QAM64 */ + /* try first guess DRX_CONSTELLATION_QAM64 */ + /* fall through */ case QAM_64: transmission_params |= OFDM_SC_RA_RAM_OP_PARAM_CONST_QAM64; break; @@ -3880,7 +3885,8 @@ static int set_dvbt(struct drxk_state *state, u16 intermediate_freqk_hz, case FEC_AUTO: default: operation_mode |= OFDM_SC_RA_RAM_OP_AUTO_RATE__M; - /* fall through , try first guess DRX_CODERATE_2DIV3 */ + /* try first guess DRX_CODERATE_2DIV3 */ + /* fall through */ case FEC_2_3: transmission_params |= OFDM_SC_RA_RAM_OP_PARAM_RATE_2_3; break; @@ -3914,7 +3920,7 @@ static int set_dvbt(struct drxk_state *state, u16 intermediate_freqk_hz, switch (state->props.bandwidth_hz) { case 0: state->props.bandwidth_hz = 8000000; - /* fall though */ + /* fall through */ case 8000000: bandwidth = DRXK_BANDWIDTH_8MHZ_IN_HZ; status = write16(state, OFDM_SC_RA_RAM_SRMM_FIX_FACT_8K__A, diff --git a/drivers/media/dvb-frontends/mt352.c b/drivers/media/dvb-frontends/mt352.c index e127090f2d22..d5fa96f0a6cd 100644 --- a/drivers/media/dvb-frontends/mt352.c +++ b/drivers/media/dvb-frontends/mt352.c @@ -211,6 +211,7 @@ static int mt352_set_parameters(struct dvb_frontend *fe) if (op->hierarchy == HIERARCHY_AUTO || op->hierarchy == HIERARCHY_NONE) break; + /* fall through */ default: return -EINVAL; } diff --git a/drivers/media/dvb-frontends/or51132.c b/drivers/media/dvb-frontends/or51132.c index 62aa00767015..5f2549c48eb0 100644 --- a/drivers/media/dvb-frontends/or51132.c +++ b/drivers/media/dvb-frontends/or51132.c @@ -493,8 +493,8 @@ start: switch (reg&0xff) { case 0x06: if (reg & 0x1000) usK = 3 << 24; - /* Fall through to QAM64 case */ - case 0x43: + /* fall through */ + case 0x43: /* QAM64 */ c = 150204167; break; case 0x45: diff --git a/drivers/media/dvb-frontends/s5h1411.c b/drivers/media/dvb-frontends/s5h1411.c index f29750a96196..dd09336a135b 100644 --- a/drivers/media/dvb-frontends/s5h1411.c +++ b/drivers/media/dvb-frontends/s5h1411.c @@ -51,7 +51,7 @@ static int debug; #define dprintk(arg...) do { \ if (debug) \ printk(arg); \ - } while (0) +} while (0) /* Register values to initialise the demod, defaults to VSB */ static struct init_tab { @@ -410,7 +410,7 @@ static int s5h1411_set_if_freq(struct dvb_frontend *fe, int KHz) default: dprintk("%s(%d KHz) Invalid, defaulting to 5380\n", __func__, KHz); - /* no break, need to continue */ + /* fall through */ case 5380: case 44000: s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0x38, 0x1be4); diff --git a/drivers/media/dvb-frontends/zl10353.c b/drivers/media/dvb-frontends/zl10353.c index 47c0549eb7b2..1c689f7f4ab8 100644 --- a/drivers/media/dvb-frontends/zl10353.c +++ b/drivers/media/dvb-frontends/zl10353.c @@ -211,7 +211,7 @@ static int zl10353_set_parameters(struct dvb_frontend *fe) break; default: c->bandwidth_hz = 8000000; - /* fall though */ + /* fall through */ case 8000000: zl10353_single_write(fe, MCLK_RATIO, 0x75); zl10353_single_write(fe, 0x64, 0x36); @@ -268,6 +268,7 @@ static int zl10353_set_parameters(struct dvb_frontend *fe) if (c->hierarchy == HIERARCHY_AUTO || c->hierarchy == HIERARCHY_NONE) break; + /* fall through */ default: return -EINVAL; } diff --git a/drivers/media/i2c/msp3400-kthreads.c b/drivers/media/i2c/msp3400-kthreads.c index 11fc593ed908..4dd01e9f553b 100644 --- a/drivers/media/i2c/msp3400-kthreads.c +++ b/drivers/media/i2c/msp3400-kthreads.c @@ -655,6 +655,7 @@ restart: break; case 0: /* 4.5 */ state->detected_std = V4L2_STD_MN; + /* fall-through */ default: no_second: state->second = msp3400c_carrier_detect_main[max1].cdo; diff --git a/drivers/media/i2c/soc_camera/ov6650.c b/drivers/media/i2c/soc_camera/ov6650.c index dbd6d92c589f..d2be64d54b22 100644 --- a/drivers/media/i2c/soc_camera/ov6650.c +++ b/drivers/media/i2c/soc_camera/ov6650.c @@ -709,6 +709,7 @@ static int ov6650_set_fmt(struct v4l2_subdev *sd, switch (mf->code) { case MEDIA_BUS_FMT_Y10_1X10: mf->code = MEDIA_BUS_FMT_Y8_1X8; + /* fall through */ case MEDIA_BUS_FMT_Y8_1X8: case MEDIA_BUS_FMT_YVYU8_2X8: case MEDIA_BUS_FMT_YUYV8_2X8: @@ -718,6 +719,7 @@ static int ov6650_set_fmt(struct v4l2_subdev *sd, break; default: mf->code = MEDIA_BUS_FMT_SBGGR8_1X8; + /* fall through */ case MEDIA_BUS_FMT_SBGGR8_1X8: mf->colorspace = V4L2_COLORSPACE_SRGB; break; diff --git a/drivers/media/pci/cx23885/cx23885-cards.c b/drivers/media/pci/cx23885/cx23885-cards.c index 9e39aea85df6..c48fa8e25a70 100644 --- a/drivers/media/pci/cx23885/cx23885-cards.c +++ b/drivers/media/pci/cx23885/cx23885-cards.c @@ -2081,7 +2081,7 @@ void cx23885_card_setup(struct cx23885_dev *dev) ts2->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */ ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */ ts2->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; - /* break omitted intentionally */ + /* fall-through */ case CX23885_BOARD_DVICO_FUSIONHDTV_5_EXP: ts1->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */ ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */ @@ -2238,6 +2238,7 @@ void cx23885_card_setup(struct cx23885_dev *dev) /* Currently only enabled for the integrated IR controller */ if (!enable_885_ir) break; + /* fall-through */ case CX23885_BOARD_HAUPPAUGE_HVR1250: case CX23885_BOARD_HAUPPAUGE_HVR1800: case CX23885_BOARD_HAUPPAUGE_IMPACTVCBE: diff --git a/drivers/media/pci/cx88/cx88-video.c b/drivers/media/pci/cx88/cx88-video.c index c7d4e87ccb64..36b4fb908e31 100644 --- a/drivers/media/pci/cx88/cx88-video.c +++ b/drivers/media/pci/cx88/cx88-video.c @@ -1420,7 +1420,7 @@ static int cx8800_initdev(struct pci_dev *pci_dev, request_module("rtc-isl1208"); core->i2c_rtc = i2c_new_device(&core->i2c_adap, &rtc_info); } - /* break intentionally omitted */ + /* fall-through */ case CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO: request_module("ir-kbd-i2c"); } diff --git a/drivers/media/pci/ddbridge/ddbridge-core.c b/drivers/media/pci/ddbridge/ddbridge-core.c index 340cff02dee2..09b5338f4f8b 100644 --- a/drivers/media/pci/ddbridge/ddbridge-core.c +++ b/drivers/media/pci/ddbridge/ddbridge-core.c @@ -789,9 +789,10 @@ static void dvb_input_detach(struct ddb_input *input) dvb_frontend_detach(input->fe); input->fe = NULL; } + /* fall-through */ case 4: dvb_net_release(&input->dvbnet); - + /* fall-through */ case 3: dvbdemux->dmx.close(&dvbdemux->dmx); dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, @@ -799,10 +800,10 @@ static void dvb_input_detach(struct ddb_input *input) dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, &input->mem_frontend); dvb_dmxdev_release(&input->dmxdev); - + /* fall-through */ case 2: dvb_dmx_release(&input->demux); - + /* fall-through */ case 1: dvb_unregister_adapter(adap); } diff --git a/drivers/media/pci/saa7134/saa7134-cards.c b/drivers/media/pci/saa7134/saa7134-cards.c index f79380faf499..9965d3531c80 100644 --- a/drivers/media/pci/saa7134/saa7134-cards.c +++ b/drivers/media/pci/saa7134/saa7134-cards.c @@ -7806,7 +7806,7 @@ int saa7134_board_init2(struct saa7134_dev *dev) dev->name, saa7134_boards[dev->board].name); break; } - /* break intentionally omitted */ + /* fall-through */ case SAA7134_BOARD_VIDEOMATE_DVBT_300: case SAA7134_BOARD_ASUS_EUROPA2_HYBRID: case SAA7134_BOARD_ASUS_EUROPA_HYBRID: @@ -7864,7 +7864,7 @@ int saa7134_board_init2(struct saa7134_dev *dev) break; case SAA7134_BOARD_HAUPPAUGE_HVR1110: hauppauge_eeprom(dev, dev->eedata+0x80); - /* break intentionally omitted */ + /* fall-through */ case SAA7134_BOARD_PINNACLE_PCTV_310i: case SAA7134_BOARD_KWORLD_DVBT_210: case SAA7134_BOARD_TEVION_DVBT_220RF: diff --git a/drivers/media/pci/solo6x10/solo6x10-core.c b/drivers/media/pci/solo6x10/solo6x10-core.c index f50d07229236..ca0873e47bea 100644 --- a/drivers/media/pci/solo6x10/solo6x10-core.c +++ b/drivers/media/pci/solo6x10/solo6x10-core.c @@ -511,6 +511,7 @@ static int solo_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) default: dev_warn(&pdev->dev, "Invalid chip_id 0x%02x, assuming 4 ch\n", chip_id); + /* fall through */ case 5: solo_dev->nr_chans = 4; solo_dev->nr_ext = 1; diff --git a/drivers/media/pci/solo6x10/solo6x10-i2c.c b/drivers/media/pci/solo6x10/solo6x10-i2c.c index e83bb79f9349..89f2f2a493c2 100644 --- a/drivers/media/pci/solo6x10/solo6x10-i2c.c +++ b/drivers/media/pci/solo6x10/solo6x10-i2c.c @@ -192,6 +192,7 @@ int solo_i2c_isr(struct solo_dev *solo_dev) } solo_dev->i2c_state = IIC_STATE_WRITE; + /* fall through */ case IIC_STATE_WRITE: ret = solo_i2c_handle_write(solo_dev); break; diff --git a/drivers/media/platform/exynos4-is/fimc-capture.c b/drivers/media/platform/exynos4-is/fimc-capture.c index 8a7cd07dbe28..db60a63c0768 100644 --- a/drivers/media/platform/exynos4-is/fimc-capture.c +++ b/drivers/media/platform/exynos4-is/fimc-capture.c @@ -1277,6 +1277,7 @@ static int fimc_cap_g_selection(struct file *file, void *fh, case V4L2_SEL_TGT_COMPOSE_DEFAULT: case V4L2_SEL_TGT_COMPOSE_BOUNDS: f = &ctx->d_frame; + /* fall through */ case V4L2_SEL_TGT_CROP_BOUNDS: case V4L2_SEL_TGT_CROP_DEFAULT: s->r.left = 0; @@ -1287,6 +1288,7 @@ static int fimc_cap_g_selection(struct file *file, void *fh, case V4L2_SEL_TGT_COMPOSE: f = &ctx->d_frame; + /* fall through */ case V4L2_SEL_TGT_CROP: s->r.left = f->offs_h; s->r.top = f->offs_v; @@ -1610,6 +1612,7 @@ static int fimc_subdev_get_selection(struct v4l2_subdev *sd, switch (sel->target) { case V4L2_SEL_TGT_COMPOSE_BOUNDS: f = &ctx->d_frame; + /* fall through */ case V4L2_SEL_TGT_CROP_BOUNDS: r->width = f->o_width; r->height = f->o_height; diff --git a/drivers/media/platform/marvell-ccic/mcam-core.c b/drivers/media/platform/marvell-ccic/mcam-core.c index a8bda6679422..8cac2f202099 100644 --- a/drivers/media/platform/marvell-ccic/mcam-core.c +++ b/drivers/media/platform/marvell-ccic/mcam-core.c @@ -393,6 +393,7 @@ static int mcam_alloc_dma_bufs(struct mcam_camera *cam, int loadtime) dma_free_coherent(cam->dev, cam->dma_buf_size, cam->dma_bufs[0], cam->dma_handles[0]); cam->nbufs = 0; + /* fall-through */ case 0: cam_err(cam, "Insufficient DMA buffers, cannot operate\n"); return -ENOMEM; diff --git a/drivers/media/platform/pxa_camera.c b/drivers/media/platform/pxa_camera.c index 6615f80fe059..5d3f04210a0b 100644 --- a/drivers/media/platform/pxa_camera.c +++ b/drivers/media/platform/pxa_camera.c @@ -1291,6 +1291,7 @@ static void pxa_camera_setup_cicr(struct pxa_camera_dev *pcdev, * transformation. Note that UYVY is the only format that * should be used if pxa framebuffer Overlay2 is used. */ + /* fall through */ case V4L2_PIX_FMT_UYVY: case V4L2_PIX_FMT_VYUY: case V4L2_PIX_FMT_YUYV: diff --git a/drivers/media/platform/sh_vou.c b/drivers/media/platform/sh_vou.c index 992d61a8b961..871da2a2a91c 100644 --- a/drivers/media/platform/sh_vou.c +++ b/drivers/media/platform/sh_vou.c @@ -229,6 +229,7 @@ static void sh_vou_stream_config(struct sh_vou_device *vou_dev) break; case V4L2_PIX_FMT_RGB565: dataswap ^= 1; + /* fall through */ case V4L2_PIX_FMT_RGB565X: row_coeff = 2; break; @@ -815,6 +816,7 @@ static u32 sh_vou_ntsc_mode(enum sh_vou_bus_fmt bus_fmt) default: pr_warn("%s(): Invalid bus-format code %d, using default 8-bit\n", __func__, bus_fmt); + /* fall through */ case SH_VOU_BUS_8BIT: return 1; case SH_VOU_BUS_16BIT: diff --git a/drivers/media/rc/iguanair.c b/drivers/media/rc/iguanair.c index ccf24fd7ec1b..8711a7ff55cc 100644 --- a/drivers/media/rc/iguanair.c +++ b/drivers/media/rc/iguanair.c @@ -113,6 +113,7 @@ static void process_ir_data(struct iguanair *ir, unsigned len) break; case CMD_TX_OVERFLOW: ir->tx_overflow = true; + /* fall through */ case CMD_RECEIVER_OFF: case CMD_RECEIVER_ON: case CMD_SEND: diff --git a/drivers/media/tuners/tda18271-fe.c b/drivers/media/tuners/tda18271-fe.c index b4e5fa2ff5e5..147155553648 100644 --- a/drivers/media/tuners/tda18271-fe.c +++ b/drivers/media/tuners/tda18271-fe.c @@ -960,7 +960,7 @@ static int tda18271_set_params(struct dvb_frontend *fe) break; case SYS_DVBC_ANNEX_B: bw = 6000000; - /* falltrough */ + /* fall through */ case SYS_DVBC_ANNEX_A: case SYS_DVBC_ANNEX_C: if (bw <= 6000000) { diff --git a/drivers/media/tuners/xc5000.c b/drivers/media/tuners/xc5000.c index e823aafce276..afdf0c9903d2 100644 --- a/drivers/media/tuners/xc5000.c +++ b/drivers/media/tuners/xc5000.c @@ -788,6 +788,7 @@ static int xc5000_set_digital_params(struct dvb_frontend *fe) if (!bw) bw = 6000000; /* fall to OFDM handling */ + /* fall through */ case SYS_DMBTH: case SYS_DVBT: case SYS_DVBT2: diff --git a/drivers/media/usb/cpia2/cpia2_core.c b/drivers/media/usb/cpia2/cpia2_core.c index b1d13444ff30..0efba0da0a45 100644 --- a/drivers/media/usb/cpia2/cpia2_core.c +++ b/drivers/media/usb/cpia2/cpia2_core.c @@ -173,7 +173,8 @@ int cpia2_do_command(struct camera_data *cam, cmd.start = CPIA2_VP_DEVICEH; break; case CPIA2_CMD_SET_VP_BRIGHTNESS: - cmd.buffer.block_data[0] = param; /* Then fall through */ + cmd.buffer.block_data[0] = param; + /* fall through */ case CPIA2_CMD_GET_VP_BRIGHTNESS: cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; cmd.reg_count = 1; @@ -183,14 +184,16 @@ int cpia2_do_command(struct camera_data *cam, cmd.start = CPIA2_VP5_EXPOSURE_TARGET; break; case CPIA2_CMD_SET_CONTRAST: - cmd.buffer.block_data[0] = param; /* Then fall through */ + cmd.buffer.block_data[0] = param; + /* fall through */ case CPIA2_CMD_GET_CONTRAST: cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; cmd.reg_count = 1; cmd.start = CPIA2_VP_YRANGE; break; case CPIA2_CMD_SET_VP_SATURATION: - cmd.buffer.block_data[0] = param; /* Then fall through */ + cmd.buffer.block_data[0] = param; + /* fall through */ case CPIA2_CMD_GET_VP_SATURATION: cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; cmd.reg_count = 1; @@ -200,28 +203,32 @@ int cpia2_do_command(struct camera_data *cam, cmd.start = CPIA2_VP5_MCUVSATURATION; break; case CPIA2_CMD_SET_VP_GPIO_DATA: - cmd.buffer.block_data[0] = param; /* Then fall through */ + cmd.buffer.block_data[0] = param; + /* fall through */ case CPIA2_CMD_GET_VP_GPIO_DATA: cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; cmd.reg_count = 1; cmd.start = CPIA2_VP_GPIO_DATA; break; case CPIA2_CMD_SET_VP_GPIO_DIRECTION: - cmd.buffer.block_data[0] = param; /* Then fall through */ + cmd.buffer.block_data[0] = param; + /* fall through */ case CPIA2_CMD_GET_VP_GPIO_DIRECTION: cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; cmd.reg_count = 1; cmd.start = CPIA2_VP_GPIO_DIRECTION; break; case CPIA2_CMD_SET_VC_MP_GPIO_DATA: - cmd.buffer.block_data[0] = param; /* Then fall through */ + cmd.buffer.block_data[0] = param; + /* fall through */ case CPIA2_CMD_GET_VC_MP_GPIO_DATA: cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC; cmd.reg_count = 1; cmd.start = CPIA2_VC_MP_DATA; break; case CPIA2_CMD_SET_VC_MP_GPIO_DIRECTION: - cmd.buffer.block_data[0] = param; /* Then fall through */ + cmd.buffer.block_data[0] = param; + /*fall through */ case CPIA2_CMD_GET_VC_MP_GPIO_DIRECTION: cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC; cmd.reg_count = 1; @@ -235,7 +242,8 @@ int cpia2_do_command(struct camera_data *cam, cmd.buffer.block_data[0] = param; break; case CPIA2_CMD_SET_FLICKER_MODES: - cmd.buffer.block_data[0] = param; /* Then fall through */ + cmd.buffer.block_data[0] = param; + /* fall through */ case CPIA2_CMD_GET_FLICKER_MODES: cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; cmd.reg_count = 1; @@ -280,8 +288,9 @@ int cpia2_do_command(struct camera_data *cam, cmd.start = CPIA2_SYSTEM_SYSTEM_CONTROL; cmd.buffer.block_data[0] = CPIA2_SYSTEM_CONTROL_CLEAR_ERR; break; - case CPIA2_CMD_SET_USER_MODE: /* Then fall through */ + case CPIA2_CMD_SET_USER_MODE: cmd.buffer.block_data[0] = param; + /* fall through */ case CPIA2_CMD_GET_USER_MODE: cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; cmd.reg_count = 1; @@ -300,14 +309,16 @@ int cpia2_do_command(struct camera_data *cam, cmd.buffer.block_data[0] = param; break; case CPIA2_CMD_SET_WAKEUP: - cmd.buffer.block_data[0] = param; /* Then fall through */ + cmd.buffer.block_data[0] = param; + /* fall through */ case CPIA2_CMD_GET_WAKEUP: cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC; cmd.reg_count = 1; cmd.start = CPIA2_VC_WAKEUP; break; case CPIA2_CMD_SET_PW_CONTROL: - cmd.buffer.block_data[0] = param; /* Then fall through */ + cmd.buffer.block_data[0] = param; + /* fall through */ case CPIA2_CMD_GET_PW_CONTROL: cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC; cmd.reg_count = 1; @@ -319,7 +330,8 @@ int cpia2_do_command(struct camera_data *cam, cmd.start = CPIA2_VP_SYSTEMSTATE; break; case CPIA2_CMD_SET_SYSTEM_CTRL: - cmd.buffer.block_data[0] = param; /* Then fall through */ + cmd.buffer.block_data[0] = param; + /* fall through */ case CPIA2_CMD_GET_SYSTEM_CTRL: cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_SYSTEM; @@ -327,21 +339,24 @@ int cpia2_do_command(struct camera_data *cam, cmd.start = CPIA2_SYSTEM_SYSTEM_CONTROL; break; case CPIA2_CMD_SET_VP_SYSTEM_CTRL: - cmd.buffer.block_data[0] = param; /* Then fall through */ + cmd.buffer.block_data[0] = param; + /* fall through */ case CPIA2_CMD_GET_VP_SYSTEM_CTRL: cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; cmd.reg_count = 1; cmd.start = CPIA2_VP_SYSTEMCTRL; break; case CPIA2_CMD_SET_VP_EXP_MODES: - cmd.buffer.block_data[0] = param; /* Then fall through */ + cmd.buffer.block_data[0] = param; + /* fall through */ case CPIA2_CMD_GET_VP_EXP_MODES: cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; cmd.reg_count = 1; cmd.start = CPIA2_VP_EXPOSURE_MODES; break; case CPIA2_CMD_SET_DEVICE_CONFIG: - cmd.buffer.block_data[0] = param; /* Then fall through */ + cmd.buffer.block_data[0] = param; + /* fall through */ case CPIA2_CMD_GET_DEVICE_CONFIG: cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; cmd.reg_count = 1; @@ -361,7 +376,8 @@ int cpia2_do_command(struct camera_data *cam, cmd.start = CPIA2_SENSOR_CR1; break; case CPIA2_CMD_SET_VC_CONTROL: - cmd.buffer.block_data[0] = param; /* Then fall through */ + cmd.buffer.block_data[0] = param; + /* fall through */ case CPIA2_CMD_GET_VC_CONTROL: cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC; cmd.reg_count = 1; @@ -395,7 +411,8 @@ int cpia2_do_command(struct camera_data *cam, case CPIA2_CMD_SET_USER_EFFECTS: /* Note: Be careful with this as this register can also affect flicker modes */ - cmd.buffer.block_data[0] = param; /* Then fall through */ + cmd.buffer.block_data[0] = param; + /* fall through */ case CPIA2_CMD_GET_USER_EFFECTS: cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; cmd.reg_count = 1; diff --git a/drivers/media/usb/cx231xx/cx231xx-video.c b/drivers/media/usb/cx231xx/cx231xx-video.c index 6414188ffdfa..f67f86876625 100644 --- a/drivers/media/usb/cx231xx/cx231xx-video.c +++ b/drivers/media/usb/cx231xx/cx231xx-video.c @@ -1134,7 +1134,7 @@ void cx231xx_v4l2_create_entities(struct cx231xx *dev) /* The DVB core will handle it */ if (dev->tuner_type == TUNER_ABSENT) continue; - /* fall though */ + /* fall through */ default: /* just to shut up a gcc warning */ ent->function = MEDIA_ENT_F_CONN_RF; break; diff --git a/drivers/media/usb/dvb-usb-v2/af9015.c b/drivers/media/usb/dvb-usb-v2/af9015.c index caa1e6101f58..612431ab0fb3 100644 --- a/drivers/media/usb/dvb-usb-v2/af9015.c +++ b/drivers/media/usb/dvb-usb-v2/af9015.c @@ -52,6 +52,7 @@ static int af9015_ctrl_msg(struct dvb_usb_device *d, struct req_t *req) case READ_I2C: write = 0; state->buf[2] |= 0x01; /* set I2C direction */ + /* fall through */ case WRITE_I2C: state->buf[0] = READ_WRITE_I2C; break; diff --git a/drivers/media/usb/dvb-usb-v2/lmedm04.c b/drivers/media/usb/dvb-usb-v2/lmedm04.c index 924adfdb660d..594360a63c18 100644 --- a/drivers/media/usb/dvb-usb-v2/lmedm04.c +++ b/drivers/media/usb/dvb-usb-v2/lmedm04.c @@ -1065,6 +1065,7 @@ static int dm04_lme2510_frontend_attach(struct dvb_usb_adapter *adap) } break; } + /* fall through */ case 0x22f0: st->i2c_gate = 5; adap->fe[0] = dvb_attach(m88rs2000_attach, diff --git a/drivers/media/usb/dvb-usb/dib0700_devices.c b/drivers/media/usb/dvb-usb/dib0700_devices.c index 85ab3fa48f9a..6a57fc6d3472 100644 --- a/drivers/media/usb/dvb-usb/dib0700_devices.c +++ b/drivers/media/usb/dvb-usb/dib0700_devices.c @@ -1659,6 +1659,7 @@ static int dib8096_set_param_override(struct dvb_frontend *fe) switch (band) { default: deb_info("Warning : Rf frequency (%iHz) is not in the supported range, using VHF switch ", fe->dtv_property_cache.frequency); + /* fall through */ case BAND_VHF: state->dib8000_ops.set_gpio(fe, 3, 0, 1); break; diff --git a/drivers/media/usb/dvb-usb/dw2102.c b/drivers/media/usb/dvb-usb/dw2102.c index 6e654e5026dd..57b187240110 100644 --- a/drivers/media/usb/dvb-usb/dw2102.c +++ b/drivers/media/usb/dvb-usb/dw2102.c @@ -1840,11 +1840,12 @@ static int dw2102_load_firmware(struct usb_device *dev, switch (le16_to_cpu(dev->descriptor.idProduct)) { case USB_PID_TEVII_S650: dw2104_properties.rc.core.rc_codes = RC_MAP_TEVII_NEC; + /* fall through */ case USB_PID_DW2104: reset = 1; dw210x_op_rw(dev, 0xc4, 0x0000, 0, &reset, 1, DW210X_WRITE_MSG); - /* break omitted intentionally */ + /* fall through */ case USB_PID_DW3101: reset = 0; dw210x_op_rw(dev, 0xbf, 0x0040, 0, &reset, 0, @@ -1877,6 +1878,7 @@ static int dw2102_load_firmware(struct usb_device *dev, break; } } + /* fall through */ case 0x2101: dw210x_op_rw(dev, 0xbc, 0x0030, 0, &reset16[0], 2, DW210X_READ_MSG); diff --git a/drivers/media/usb/gspca/ov519.c b/drivers/media/usb/gspca/ov519.c index f4c41f043cda..cdb79c5f0c38 100644 --- a/drivers/media/usb/gspca/ov519.c +++ b/drivers/media/usb/gspca/ov519.c @@ -3526,7 +3526,8 @@ static void ov511_mode_init_regs(struct sd *sd) sd->clockdiv = 0; break; } - /* Fall through for 640x480 case */ + /* For 640x480 case */ + /* fall through */ default: /* case 20: */ /* case 15: */ diff --git a/drivers/media/usb/pwc/pwc-v4l.c b/drivers/media/usb/pwc/pwc-v4l.c index 92f04db6bbae..043b2b97cee6 100644 --- a/drivers/media/usb/pwc/pwc-v4l.c +++ b/drivers/media/usb/pwc/pwc-v4l.c @@ -568,7 +568,8 @@ static int pwc_g_volatile_ctrl(struct v4l2_ctrl *ctrl) pdev->gain_valid = true; if (!DEVICE_USE_CODEC3(pdev->type)) break; - /* Fall through for CODEC3 where autogain also controls expo */ + /* For CODEC3 where autogain also controls expo */ + /* fall through */ case V4L2_CID_EXPOSURE_AUTO: if (pdev->exposure_valid && time_before(jiffies, pdev->last_exposure_update + HZ / 4)) { diff --git a/drivers/media/usb/usbvision/usbvision-i2c.c b/drivers/media/usb/usbvision/usbvision-i2c.c index 5a3f788ad033..fdf6b6e285da 100644 --- a/drivers/media/usb/usbvision/usbvision-i2c.c +++ b/drivers/media/usb/usbvision/usbvision-i2c.c @@ -311,10 +311,13 @@ usbvision_i2c_read_max4(struct usb_usbvision *usbvision, unsigned char addr, switch (len) { case 4: buf[3] = usbvision_read_reg(usbvision, USBVISION_SER_DAT4); + /* fall through */ case 3: buf[2] = usbvision_read_reg(usbvision, USBVISION_SER_DAT3); + /* fall through */ case 2: buf[1] = usbvision_read_reg(usbvision, USBVISION_SER_DAT2); + /* fall through */ case 1: buf[0] = usbvision_read_reg(usbvision, USBVISION_SER_DAT1); break; diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c index 47d93a938dde..a29f39d4e05b 100644 --- a/drivers/media/usb/uvc/uvc_video.c +++ b/drivers/media/usb/uvc/uvc_video.c @@ -1327,7 +1327,7 @@ static void uvc_video_complete(struct urb *urb) case -ENOENT: /* usb_kill_urb() called. */ if (stream->frozen) return; - + /* fall through */ case -ECONNRESET: /* usb_unlink_urb() called. */ case -ESHUTDOWN: /* The endpoint is being disabled. */ uvc_queue_cancel(queue, urb->status == -ESHUTDOWN); -- cgit v1.2.3-58-ga151 From a16e37726c444cbda91e73ed5f742e717bfe866f Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 18 May 2017 10:40:00 -0300 Subject: [media] s5p-jpeg: don't return a random width/height Gcc 7.1 complains about: drivers/media/platform/s5p-jpeg/jpeg-core.c: In function 's5p_jpeg_parse_hdr.isra.9': drivers/media/platform/s5p-jpeg/jpeg-core.c:1207:12: warning: 'width' may be used uninitialized in this function [-Wmaybe-uninitialized] result->w = width; ~~~~~~~~~~^~~~~~~ drivers/media/platform/s5p-jpeg/jpeg-core.c:1208:12: warning: 'height' may be used uninitialized in this function [-Wmaybe-uninitialized] result->h = height; ~~~~~~~~~~^~~~~~~~ Indeed the code would allow it to return a random value (although it shouldn't happen, in practice). So, explicitly set both to zero, just in case. Acked-by: Andrzej Pietrasiewicz Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-jpeg/jpeg-core.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.c b/drivers/media/platform/s5p-jpeg/jpeg-core.c index 52dc7941db65..1da2c94e1dca 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-core.c +++ b/drivers/media/platform/s5p-jpeg/jpeg-core.c @@ -1099,10 +1099,10 @@ static bool s5p_jpeg_parse_hdr(struct s5p_jpeg_q_data *result, struct s5p_jpeg_ctx *ctx) { int c, components = 0, notfound, n_dht = 0, n_dqt = 0; - unsigned int height, width, word, subsampling = 0, sos = 0, sof = 0, - sof_len = 0; - unsigned int dht[S5P_JPEG_MAX_MARKER], dht_len[S5P_JPEG_MAX_MARKER], - dqt[S5P_JPEG_MAX_MARKER], dqt_len[S5P_JPEG_MAX_MARKER]; + unsigned int height = 0, width = 0, word, subsampling = 0; + unsigned int sos = 0, sof = 0, sof_len = 0; + unsigned int dht[S5P_JPEG_MAX_MARKER], dht_len[S5P_JPEG_MAX_MARKER]; + unsigned int dqt[S5P_JPEG_MAX_MARKER], dqt_len[S5P_JPEG_MAX_MARKER]; long length; struct s5p_jpeg_buffer jpeg_buffer; -- cgit v1.2.3-58-ga151 From 36bcba973ad478042d1ffc6e89afd92e8bd17030 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 18 May 2017 10:48:03 -0300 Subject: [media] mtk_vcodec_dec: return error at mtk_vdec_pic_info_update() Gcc 7.1 complains that: drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c: In function 'mtk_vdec_pic_info_update': drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c:284:6: warning: variable 'ret' set but not used [-Wunused-but-set-variable] int ret; ^~~ Indeed, if debug is disabled, "ret" is never used. The best fix for it seems to make the fuction to return an error code. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c index a60b538686ea..843510979ad8 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c @@ -278,7 +278,7 @@ static void mtk_vdec_flush_decoder(struct mtk_vcodec_ctx *ctx) clean_free_buffer(ctx); } -static void mtk_vdec_pic_info_update(struct mtk_vcodec_ctx *ctx) +static int mtk_vdec_pic_info_update(struct mtk_vcodec_ctx *ctx) { unsigned int dpbsize = 0; int ret; @@ -288,7 +288,7 @@ static void mtk_vdec_pic_info_update(struct mtk_vcodec_ctx *ctx) &ctx->last_decoded_picinfo)) { mtk_v4l2_err("[%d]Error!! Cannot get param : GET_PARAM_PICTURE_INFO ERR", ctx->id); - return; + return -EINVAL; } if (ctx->last_decoded_picinfo.pic_w == 0 || @@ -296,12 +296,12 @@ static void mtk_vdec_pic_info_update(struct mtk_vcodec_ctx *ctx) ctx->last_decoded_picinfo.buf_w == 0 || ctx->last_decoded_picinfo.buf_h == 0) { mtk_v4l2_err("Cannot get correct pic info"); - return; + return -EINVAL; } if ((ctx->last_decoded_picinfo.pic_w == ctx->picinfo.pic_w) || (ctx->last_decoded_picinfo.pic_h == ctx->picinfo.pic_h)) - return; + return 0; mtk_v4l2_debug(1, "[%d]-> new(%d,%d), old(%d,%d), real(%d,%d)", @@ -316,6 +316,8 @@ static void mtk_vdec_pic_info_update(struct mtk_vcodec_ctx *ctx) mtk_v4l2_err("Incorrect dpb size, ret=%d", ret); ctx->dpb_size = dpbsize; + + return ret; } static void mtk_vdec_worker(struct work_struct *work) -- cgit v1.2.3-58-ga151 From 4c9ef4f150589478ac0b26bc7db1216c0af207fb Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 19 May 2017 07:34:04 -0300 Subject: [media] atomisp: disable several warnings when W=1 The atomisp currently produce hundreds of warnings when W=1. It is a known fact that this driver is currently in bad shape, and there are lot of things to be done here. We don't want to be bothered by those "minor" stuff for now, while the driver doesn't receive a major cleanup. So, disable those warnings. Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/atomisp/i2c/Makefile | 4 ++++ drivers/staging/media/atomisp/i2c/imx/Makefile | 5 +++++ drivers/staging/media/atomisp/i2c/ov5693/Makefile | 5 +++++ drivers/staging/media/atomisp/pci/atomisp2/Makefile | 6 ++++++ 4 files changed, 20 insertions(+) diff --git a/drivers/staging/media/atomisp/i2c/Makefile b/drivers/staging/media/atomisp/i2c/Makefile index 466517c7c8e6..a1afca6ec31f 100644 --- a/drivers/staging/media/atomisp/i2c/Makefile +++ b/drivers/staging/media/atomisp/i2c/Makefile @@ -19,3 +19,7 @@ obj-$(CONFIG_VIDEO_AP1302) += ap1302.o obj-$(CONFIG_VIDEO_LM3554) += lm3554.o +# HACK! While this driver is in bad shape, don't enable several warnings +# that would be otherwise enabled with W=1 +ccflags-y += -Wno-unused-but-set-variable -Wno-missing-prototypes \ + -Wno-unused-const-variable -Wno-missing-declarations diff --git a/drivers/staging/media/atomisp/i2c/imx/Makefile b/drivers/staging/media/atomisp/i2c/imx/Makefile index 6b13a3a66e49..0eceb7374bec 100644 --- a/drivers/staging/media/atomisp/i2c/imx/Makefile +++ b/drivers/staging/media/atomisp/i2c/imx/Makefile @@ -4,3 +4,8 @@ imx1x5-objs := imx.o drv201.o ad5816g.o dw9714.o dw9719.o dw9718.o vcm.o otp.o o ov8858_driver-objs := ../ov8858.o dw9718.o vcm.o obj-$(CONFIG_VIDEO_OV8858) += ov8858_driver.o + +# HACK! While this driver is in bad shape, don't enable several warnings +# that would be otherwise enabled with W=1 +ccflags-y += -Wno-unused-but-set-variable -Wno-missing-prototypes \ + -Wno-unused-const-variable -Wno-missing-declarations diff --git a/drivers/staging/media/atomisp/i2c/ov5693/Makefile b/drivers/staging/media/atomisp/i2c/ov5693/Makefile index c9c0e1245858..fd2ef2e3c31e 100644 --- a/drivers/staging/media/atomisp/i2c/ov5693/Makefile +++ b/drivers/staging/media/atomisp/i2c/ov5693/Makefile @@ -1 +1,6 @@ obj-$(CONFIG_VIDEO_OV5693) += ov5693.o + +# HACK! While this driver is in bad shape, don't enable several warnings +# that would be otherwise enabled with W=1 +ccflags-y += -Wno-unused-but-set-variable -Wno-missing-prototypes \ + -Wno-unused-const-variable -Wno-missing-declarations diff --git a/drivers/staging/media/atomisp/pci/atomisp2/Makefile b/drivers/staging/media/atomisp/pci/atomisp2/Makefile index f126a89a08e9..68a9ab1c3b61 100644 --- a/drivers/staging/media/atomisp/pci/atomisp2/Makefile +++ b/drivers/staging/media/atomisp/pci/atomisp2/Makefile @@ -353,3 +353,9 @@ DEFINES += -DSYSTEM_hive_isp_css_2400_system -DISP2400 ccflags-y += $(INCLUDES) $(DEFINES) -fno-common +# HACK! While this driver is in bad shape, don't enable several warnings +# that would be otherwise enabled with W=1 +ccflags-y += -Wno-unused-const-variable -Wno-missing-prototypes \ + -Wno-unused-but-set-variable -Wno-missing-declarations \ + -Wno-suggest-attribute=format -Wno-missing-prototypes \ + -Wno-implicit-fallthrough -- cgit v1.2.3-58-ga151 From c087fe3ec71bad9bcba731c328b0eb6f306c0de6 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 19 May 2017 09:01:24 -0300 Subject: [media] av7110: avoid switch fall through On two switches, this driver have unannotated switch fall through. in the first case, it falls through a return. On the second one, it prints undesired log messages on fall through. Solve that by copying the commands that it should be running. Gcc will very likely optimize it anyway, so this sholdn't be causing any harm, and shuts up gcc warnings. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/ttpci/av7110.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/media/pci/ttpci/av7110.c b/drivers/media/pci/ttpci/av7110.c index df9395c87178..f2905bd80366 100644 --- a/drivers/media/pci/ttpci/av7110.c +++ b/drivers/media/pci/ttpci/av7110.c @@ -336,6 +336,7 @@ static int DvbDmxFilterCallback(u8 *buffer1, size_t buffer1_len, av7110_p2t_write(buffer1, buffer1_len, dvbdmxfilter->feed->pid, &av7110->p2t_filter[dvbdmxfilter->index]); + return 0; default: return 0; } @@ -451,8 +452,12 @@ static void debiirq(unsigned long cookie) case DATA_CI_PUT: dprintk(4, "debi DATA_CI_PUT\n"); + xfer = TX_BUFF; + break; case DATA_MPEG_PLAY: dprintk(4, "debi DATA_MPEG_PLAY\n"); + xfer = TX_BUFF; + break; case DATA_BMP_LOAD: dprintk(4, "debi DATA_BMP_LOAD\n"); xfer = TX_BUFF; -- cgit v1.2.3-58-ga151 From 9ca3ae0d5e46a48b7eaf4afcdc68887e32692c17 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 19 May 2017 09:02:13 -0300 Subject: [media] zoran: annotate switch fall through There are two cases here that it does a switch fall through. Annotate it, in order to shut up gcc warnings. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/zoran/zoran_driver.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/pci/zoran/zoran_driver.c b/drivers/media/pci/zoran/zoran_driver.c index 180f3d7af3e1..a11cb501c550 100644 --- a/drivers/media/pci/zoran/zoran_driver.c +++ b/drivers/media/pci/zoran/zoran_driver.c @@ -534,6 +534,7 @@ static int zoran_v4l_queue_frame(struct zoran_fh *fh, int num) KERN_WARNING "%s: %s - queueing buffer %d in state DONE!?\n", ZR_DEVNAME(zr), __func__, num); + /* fall through */ case BUZ_STATE_USER: /* since there is at least one unused buffer there's room for at least * one more pend[] entry */ @@ -693,6 +694,7 @@ static int zoran_jpg_queue_frame(struct zoran_fh *fh, int num, KERN_WARNING "%s: %s - queing frame in BUZ_STATE_DONE state!?\n", ZR_DEVNAME(zr), __func__); + /* fall through */ case BUZ_STATE_USER: /* since there is at least one unused buffer there's room for at *least one more pend[] entry */ -- cgit v1.2.3-58-ga151 From d0994fe9ba22895e56b943ff3f03f2b39fcaa397 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 19 May 2017 09:03:17 -0300 Subject: [media] soc_camera: annotate a switch fall through Clearly, hsync and vsinc bool vars are part of the return logic on the second case of the switch. Annotate that, in order to shut up gcc warnings. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/soc_camera/soc_mediabus.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/platform/soc_camera/soc_mediabus.c b/drivers/media/platform/soc_camera/soc_mediabus.c index e3e665e1c503..57581f626f4c 100644 --- a/drivers/media/platform/soc_camera/soc_mediabus.c +++ b/drivers/media/platform/soc_camera/soc_mediabus.c @@ -494,6 +494,7 @@ unsigned int soc_mbus_config_compatible(const struct v4l2_mbus_config *cfg, V4L2_MBUS_HSYNC_ACTIVE_LOW); vsync = common_flags & (V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW); + /* fall through */ case V4L2_MBUS_BT656: pclk = common_flags & (V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING); -- cgit v1.2.3-58-ga151 From ec33fbd585f76b0803a90ee66804fa6f937dccaa Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 19 May 2017 09:05:19 -0300 Subject: [media] s2255drv: avoid a switch fall through On this driver, it can fall through a switch. I tried to annotate it, in order to shut up a gcc warning, but that didn't work, as the logic there is somewhat complex. So, instead, let's just repeat the code. gcc should likely optimize it anyway, and this makes the code better readable, IMHO. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/s2255/s2255drv.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/usb/s2255/s2255drv.c b/drivers/media/usb/s2255/s2255drv.c index a9d4484f7626..6a88b1dbb3a0 100644 --- a/drivers/media/usb/s2255/s2255drv.c +++ b/drivers/media/usb/s2255/s2255drv.c @@ -1803,6 +1803,8 @@ static int save_frame(struct s2255_dev *dev, struct s2255_pipeinfo *pipe_info) default: pr_info("s2255 unknown resp\n"); } + pdata++; + break; default: pdata++; break; -- cgit v1.2.3-58-ga151 From af3a8480646cf5816528c44210dd4a70fb2026e9 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 19 May 2017 09:06:07 -0300 Subject: [media] uvcvideo: annotate a switch fall through Without annotations, gcc 7.1 will complain. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/uvc/uvc_video.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c index a29f39d4e05b..fb86d6af398d 100644 --- a/drivers/media/usb/uvc/uvc_video.c +++ b/drivers/media/usb/uvc/uvc_video.c @@ -1323,7 +1323,7 @@ static void uvc_video_complete(struct urb *urb) default: uvc_printk(KERN_WARNING, "Non-zero status (%d) in video " "completion handler.\n", urb->status); - + /* fall through */ case -ENOENT: /* usb_kill_urb() called. */ if (stream->frozen) return; -- cgit v1.2.3-58-ga151 From a3f194c6fa928fa290e10cd1a8b5cddada4d3438 Mon Sep 17 00:00:00 2001 From: Hugues Fruchet Date: Fri, 5 May 2017 12:31:20 -0300 Subject: [media] dt-bindings: Document STM32 DCMI bindings This adds documentation of device tree bindings for the STM32 DCMI (Digital Camera Memory Interface). Acked-by: Rob Herring Signed-off-by: Hugues Fruchet Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../devicetree/bindings/media/st,stm32-dcmi.txt | 45 ++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/st,stm32-dcmi.txt diff --git a/Documentation/devicetree/bindings/media/st,stm32-dcmi.txt b/Documentation/devicetree/bindings/media/st,stm32-dcmi.txt new file mode 100644 index 000000000000..249790a93017 --- /dev/null +++ b/Documentation/devicetree/bindings/media/st,stm32-dcmi.txt @@ -0,0 +1,45 @@ +STMicroelectronics STM32 Digital Camera Memory Interface (DCMI) + +Required properties: +- compatible: "st,stm32-dcmi" +- reg: physical base address and length of the registers set for the device +- interrupts: should contain IRQ line for the DCMI +- resets: reference to a reset controller, + see Documentation/devicetree/bindings/reset/st,stm32-rcc.txt +- clocks: list of clock specifiers, corresponding to entries in + the clock-names property +- clock-names: must contain "mclk", which is the DCMI peripherial clock +- pinctrl: the pincontrol settings to configure muxing properly + for pins that connect to DCMI device. + See Documentation/devicetree/bindings/pinctrl/st,stm32-pinctrl.txt. +- dmas: phandle to DMA controller node, + see Documentation/devicetree/bindings/dma/stm32-dma.txt +- dma-names: must contain "tx", which is the transmit channel from DCMI to DMA + +DCMI supports a single port node with parallel bus. It should contain one +'port' child node with child 'endpoint' node. Please refer to the bindings +defined in Documentation/devicetree/bindings/media/video-interfaces.txt. + +Example: + + dcmi: dcmi@50050000 { + compatible = "st,stm32-dcmi"; + reg = <0x50050000 0x400>; + interrupts = <78>; + resets = <&rcc STM32F4_AHB2_RESET(DCMI)>; + clocks = <&rcc 0 STM32F4_AHB2_CLOCK(DCMI)>; + clock-names = "mclk"; + pinctrl-names = "default"; + pinctrl-0 = <&dcmi_pins>; + dmas = <&dma2 1 1 0x414 0x3>; + dma-names = "tx"; + port { + dcmi_0: endpoint { + remote-endpoint = <...>; + bus-width = <8>; + hsync-active = <0>; + vsync-active = <0>; + pclk-sample = <1>; + }; + }; + }; -- cgit v1.2.3-58-ga151 From 37404f91ef8b910e6117b7920c082d71aba27250 Mon Sep 17 00:00:00 2001 From: Hugues Fruchet Date: Fri, 5 May 2017 12:31:21 -0300 Subject: [media] stm32-dcmi: STM32 DCMI camera interface driver This V4L2 subdev driver enables Digital Camera Memory Interface (DCMI) of STMicroelectronics STM32 SoC series. Signed-off-by: Yannick Fertre Signed-off-by: Hugues Fruchet Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/Kconfig | 12 + drivers/media/platform/Makefile | 2 + drivers/media/platform/stm32/Makefile | 1 + drivers/media/platform/stm32/stm32-dcmi.c | 1403 +++++++++++++++++++++++++++++ 4 files changed, 1418 insertions(+) create mode 100644 drivers/media/platform/stm32/Makefile create mode 100644 drivers/media/platform/stm32/stm32-dcmi.c diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index ac026ee1ca07..f125f471076f 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -114,6 +114,18 @@ config VIDEO_S3C_CAMIF To compile this driver as a module, choose M here: the module will be called s3c-camif. +config VIDEO_STM32_DCMI + tristate "STM32 Digital Camera Memory Interface (DCMI) support" + depends on VIDEO_V4L2 && OF && HAS_DMA + depends on ARCH_STM32 || COMPILE_TEST + select VIDEOBUF2_DMA_CONTIG + ---help--- + This module makes the STM32 Digital Camera Memory Interface (DCMI) + available as a v4l2 device. + + To compile this driver as a module, choose M here: the module + will be called stm32-dcmi. + source "drivers/media/platform/soc_camera/Kconfig" source "drivers/media/platform/exynos4-is/Kconfig" source "drivers/media/platform/am437x/Kconfig" diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile index 63303d63c64c..231f3c2894c9 100644 --- a/drivers/media/platform/Makefile +++ b/drivers/media/platform/Makefile @@ -68,6 +68,8 @@ obj-$(CONFIG_VIDEO_RCAR_VIN) += rcar-vin/ obj-$(CONFIG_VIDEO_ATMEL_ISC) += atmel/ obj-$(CONFIG_VIDEO_ATMEL_ISI) += atmel/ +obj-$(CONFIG_VIDEO_STM32_DCMI) += stm32/ + ccflags-y += -I$(srctree)/drivers/media/i2c obj-$(CONFIG_VIDEO_MEDIATEK_VPU) += mtk-vpu/ diff --git a/drivers/media/platform/stm32/Makefile b/drivers/media/platform/stm32/Makefile new file mode 100644 index 000000000000..9b606a70985d --- /dev/null +++ b/drivers/media/platform/stm32/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_VIDEO_STM32_DCMI) += stm32-dcmi.o diff --git a/drivers/media/platform/stm32/stm32-dcmi.c b/drivers/media/platform/stm32/stm32-dcmi.c new file mode 100644 index 000000000000..348f025d5270 --- /dev/null +++ b/drivers/media/platform/stm32/stm32-dcmi.c @@ -0,0 +1,1403 @@ +/* + * Driver for STM32 Digital Camera Memory Interface + * + * Copyright (C) STMicroelectronics SA 2017 + * Authors: Yannick Fertre + * Hugues Fruchet + * for STMicroelectronics. + * License terms: GNU General Public License (GPL), version 2 + * + * This driver is based on atmel_isi.c + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "stm32-dcmi" + +/* Registers offset for DCMI */ +#define DCMI_CR 0x00 /* Control Register */ +#define DCMI_SR 0x04 /* Status Register */ +#define DCMI_RIS 0x08 /* Raw Interrupt Status register */ +#define DCMI_IER 0x0C /* Interrupt Enable Register */ +#define DCMI_MIS 0x10 /* Masked Interrupt Status register */ +#define DCMI_ICR 0x14 /* Interrupt Clear Register */ +#define DCMI_ESCR 0x18 /* Embedded Synchronization Code Register */ +#define DCMI_ESUR 0x1C /* Embedded Synchronization Unmask Register */ +#define DCMI_CWSTRT 0x20 /* Crop Window STaRT */ +#define DCMI_CWSIZE 0x24 /* Crop Window SIZE */ +#define DCMI_DR 0x28 /* Data Register */ +#define DCMI_IDR 0x2C /* IDentifier Register */ + +/* Bits definition for control register (DCMI_CR) */ +#define CR_CAPTURE BIT(0) +#define CR_CM BIT(1) +#define CR_CROP BIT(2) +#define CR_JPEG BIT(3) +#define CR_ESS BIT(4) +#define CR_PCKPOL BIT(5) +#define CR_HSPOL BIT(6) +#define CR_VSPOL BIT(7) +#define CR_FCRC_0 BIT(8) +#define CR_FCRC_1 BIT(9) +#define CR_EDM_0 BIT(10) +#define CR_EDM_1 BIT(11) +#define CR_ENABLE BIT(14) + +/* Bits definition for status register (DCMI_SR) */ +#define SR_HSYNC BIT(0) +#define SR_VSYNC BIT(1) +#define SR_FNE BIT(2) + +/* + * Bits definition for interrupt registers + * (DCMI_RIS, DCMI_IER, DCMI_MIS, DCMI_ICR) + */ +#define IT_FRAME BIT(0) +#define IT_OVR BIT(1) +#define IT_ERR BIT(2) +#define IT_VSYNC BIT(3) +#define IT_LINE BIT(4) + +enum state { + STOPPED = 0, + RUNNING, + STOPPING, +}; + +#define MIN_WIDTH 16U +#define MAX_WIDTH 2048U +#define MIN_HEIGHT 16U +#define MAX_HEIGHT 2048U + +#define TIMEOUT_MS 1000 + +struct dcmi_graph_entity { + struct device_node *node; + + struct v4l2_async_subdev asd; + struct v4l2_subdev *subdev; +}; + +struct dcmi_format { + u32 fourcc; + u32 mbus_code; + u8 bpp; +}; + +struct dcmi_buf { + struct vb2_v4l2_buffer vb; + bool prepared; + dma_addr_t paddr; + size_t size; + struct list_head list; +}; + +struct stm32_dcmi { + /* Protects the access of variables shared within the interrupt */ + spinlock_t irqlock; + struct device *dev; + void __iomem *regs; + struct resource *res; + struct reset_control *rstc; + int sequence; + struct list_head buffers; + struct dcmi_buf *active; + + struct v4l2_device v4l2_dev; + struct video_device *vdev; + struct v4l2_async_notifier notifier; + struct dcmi_graph_entity entity; + struct v4l2_format fmt; + + const struct dcmi_format **user_formats; + unsigned int num_user_formats; + const struct dcmi_format *current_fmt; + + /* Protect this data structure */ + struct mutex lock; + struct vb2_queue queue; + + struct v4l2_of_bus_parallel bus; + struct completion complete; + struct clk *mclk; + enum state state; + struct dma_chan *dma_chan; + dma_cookie_t dma_cookie; + u32 misr; + int errors_count; + int buffers_count; +}; + +static inline struct stm32_dcmi *notifier_to_dcmi(struct v4l2_async_notifier *n) +{ + return container_of(n, struct stm32_dcmi, notifier); +} + +static inline u32 reg_read(void __iomem *base, u32 reg) +{ + return readl_relaxed(base + reg); +} + +static inline void reg_write(void __iomem *base, u32 reg, u32 val) +{ + writel_relaxed(val, base + reg); +} + +static inline void reg_set(void __iomem *base, u32 reg, u32 mask) +{ + reg_write(base, reg, reg_read(base, reg) | mask); +} + +static inline void reg_clear(void __iomem *base, u32 reg, u32 mask) +{ + reg_write(base, reg, reg_read(base, reg) & ~mask); +} + +static int dcmi_start_capture(struct stm32_dcmi *dcmi); + +static void dcmi_dma_callback(void *param) +{ + struct stm32_dcmi *dcmi = (struct stm32_dcmi *)param; + struct dma_chan *chan = dcmi->dma_chan; + struct dma_tx_state state; + enum dma_status status; + + spin_lock(&dcmi->irqlock); + + /* Check DMA status */ + status = dmaengine_tx_status(chan, dcmi->dma_cookie, &state); + + switch (status) { + case DMA_IN_PROGRESS: + dev_dbg(dcmi->dev, "%s: Received DMA_IN_PROGRESS\n", __func__); + break; + case DMA_PAUSED: + dev_err(dcmi->dev, "%s: Received DMA_PAUSED\n", __func__); + break; + case DMA_ERROR: + dev_err(dcmi->dev, "%s: Received DMA_ERROR\n", __func__); + break; + case DMA_COMPLETE: + dev_dbg(dcmi->dev, "%s: Received DMA_COMPLETE\n", __func__); + + if (dcmi->active) { + struct dcmi_buf *buf = dcmi->active; + struct vb2_v4l2_buffer *vbuf = &dcmi->active->vb; + + vbuf->sequence = dcmi->sequence++; + vbuf->field = V4L2_FIELD_NONE; + vbuf->vb2_buf.timestamp = ktime_get_ns(); + vb2_set_plane_payload(&vbuf->vb2_buf, 0, buf->size); + vb2_buffer_done(&vbuf->vb2_buf, VB2_BUF_STATE_DONE); + dev_dbg(dcmi->dev, "buffer[%d] done seq=%d\n", + vbuf->vb2_buf.index, vbuf->sequence); + + dcmi->buffers_count++; + dcmi->active = NULL; + } + + /* Restart a new DMA transfer with next buffer */ + if (dcmi->state == RUNNING) { + if (list_empty(&dcmi->buffers)) { + dev_err(dcmi->dev, "%s: No more buffer queued, cannot capture buffer", + __func__); + dcmi->errors_count++; + dcmi->active = NULL; + + spin_unlock(&dcmi->irqlock); + return; + } + + dcmi->active = list_entry(dcmi->buffers.next, + struct dcmi_buf, list); + + list_del_init(&dcmi->active->list); + + if (dcmi_start_capture(dcmi)) { + dev_err(dcmi->dev, "%s: Cannot restart capture on DMA complete", + __func__); + + spin_unlock(&dcmi->irqlock); + return; + } + + /* Enable capture */ + reg_set(dcmi->regs, DCMI_CR, CR_CAPTURE); + } + + break; + default: + dev_err(dcmi->dev, "%s: Received unknown status\n", __func__); + break; + } + + spin_unlock(&dcmi->irqlock); +} + +static int dcmi_start_dma(struct stm32_dcmi *dcmi, + struct dcmi_buf *buf) +{ + struct dma_async_tx_descriptor *desc = NULL; + struct dma_slave_config config; + int ret; + + memset(&config, 0, sizeof(config)); + + config.src_addr = (dma_addr_t)dcmi->res->start + DCMI_DR; + config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + config.dst_maxburst = 4; + + /* Configure DMA channel */ + ret = dmaengine_slave_config(dcmi->dma_chan, &config); + if (ret < 0) { + dev_err(dcmi->dev, "%s: DMA channel config failed (%d)\n", + __func__, ret); + return ret; + } + + /* Prepare a DMA transaction */ + desc = dmaengine_prep_slave_single(dcmi->dma_chan, buf->paddr, + buf->size, + DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT); + if (!desc) { + dev_err(dcmi->dev, "%s: DMA dmaengine_prep_slave_single failed for buffer size %zu\n", + __func__, buf->size); + return -EINVAL; + } + + /* Set completion callback routine for notification */ + desc->callback = dcmi_dma_callback; + desc->callback_param = dcmi; + + /* Push current DMA transaction in the pending queue */ + dcmi->dma_cookie = dmaengine_submit(desc); + + dma_async_issue_pending(dcmi->dma_chan); + + return 0; +} + +static int dcmi_start_capture(struct stm32_dcmi *dcmi) +{ + int ret; + struct dcmi_buf *buf = dcmi->active; + + if (!buf) + return -EINVAL; + + ret = dcmi_start_dma(dcmi, buf); + if (ret) { + dcmi->errors_count++; + return ret; + } + + /* Enable capture */ + reg_set(dcmi->regs, DCMI_CR, CR_CAPTURE); + + return 0; +} + +static irqreturn_t dcmi_irq_thread(int irq, void *arg) +{ + struct stm32_dcmi *dcmi = arg; + + spin_lock(&dcmi->irqlock); + + /* Stop capture is required */ + if (dcmi->state == STOPPING) { + reg_clear(dcmi->regs, DCMI_IER, IT_FRAME | IT_OVR | IT_ERR); + + dcmi->state = STOPPED; + + complete(&dcmi->complete); + + spin_unlock(&dcmi->irqlock); + return IRQ_HANDLED; + } + + if ((dcmi->misr & IT_OVR) || (dcmi->misr & IT_ERR)) { + /* + * An overflow or an error has been detected, + * stop current DMA transfert & restart it + */ + dev_warn(dcmi->dev, "%s: Overflow or error detected\n", + __func__); + + dcmi->errors_count++; + dmaengine_terminate_all(dcmi->dma_chan); + + reg_set(dcmi->regs, DCMI_ICR, IT_FRAME | IT_OVR | IT_ERR); + + dev_dbg(dcmi->dev, "Restarting capture after DCMI error\n"); + + if (dcmi_start_capture(dcmi)) { + dev_err(dcmi->dev, "%s: Cannot restart capture on overflow or error\n", + __func__); + + spin_unlock(&dcmi->irqlock); + return IRQ_HANDLED; + } + } + + spin_unlock(&dcmi->irqlock); + return IRQ_HANDLED; +} + +static irqreturn_t dcmi_irq_callback(int irq, void *arg) +{ + struct stm32_dcmi *dcmi = arg; + + spin_lock(&dcmi->irqlock); + + dcmi->misr = reg_read(dcmi->regs, DCMI_MIS); + + /* Clear interrupt */ + reg_set(dcmi->regs, DCMI_ICR, IT_FRAME | IT_OVR | IT_ERR); + + spin_unlock(&dcmi->irqlock); + + return IRQ_WAKE_THREAD; +} + +static int dcmi_queue_setup(struct vb2_queue *vq, + unsigned int *nbuffers, + unsigned int *nplanes, + unsigned int sizes[], + struct device *alloc_devs[]) +{ + struct stm32_dcmi *dcmi = vb2_get_drv_priv(vq); + unsigned int size; + + size = dcmi->fmt.fmt.pix.sizeimage; + + /* Make sure the image size is large enough */ + if (*nplanes) + return sizes[0] < size ? -EINVAL : 0; + + *nplanes = 1; + sizes[0] = size; + + dcmi->active = NULL; + + dev_dbg(dcmi->dev, "Setup queue, count=%d, size=%d\n", + *nbuffers, size); + + return 0; +} + +static int dcmi_buf_init(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct dcmi_buf *buf = container_of(vbuf, struct dcmi_buf, vb); + + INIT_LIST_HEAD(&buf->list); + + return 0; +} + +static int dcmi_buf_prepare(struct vb2_buffer *vb) +{ + struct stm32_dcmi *dcmi = vb2_get_drv_priv(vb->vb2_queue); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct dcmi_buf *buf = container_of(vbuf, struct dcmi_buf, vb); + unsigned long size; + + size = dcmi->fmt.fmt.pix.sizeimage; + + if (vb2_plane_size(vb, 0) < size) { + dev_err(dcmi->dev, "%s data will not fit into plane (%lu < %lu)\n", + __func__, vb2_plane_size(vb, 0), size); + return -EINVAL; + } + + vb2_set_plane_payload(vb, 0, size); + + if (!buf->prepared) { + /* Get memory addresses */ + buf->paddr = + vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0); + buf->size = vb2_plane_size(&buf->vb.vb2_buf, 0); + buf->prepared = true; + + vb2_set_plane_payload(&buf->vb.vb2_buf, 0, buf->size); + + dev_dbg(dcmi->dev, "buffer[%d] phy=0x%pad size=%zu\n", + vb->index, &buf->paddr, buf->size); + } + + return 0; +} + +static void dcmi_buf_queue(struct vb2_buffer *vb) +{ + struct stm32_dcmi *dcmi = vb2_get_drv_priv(vb->vb2_queue); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct dcmi_buf *buf = container_of(vbuf, struct dcmi_buf, vb); + unsigned long flags = 0; + + spin_lock_irqsave(&dcmi->irqlock, flags); + + if ((dcmi->state == RUNNING) && (!dcmi->active)) { + dcmi->active = buf; + + dev_dbg(dcmi->dev, "Starting capture on buffer[%d] queued\n", + buf->vb.vb2_buf.index); + + if (dcmi_start_capture(dcmi)) { + dev_err(dcmi->dev, "%s: Cannot restart capture on overflow or error\n", + __func__); + + spin_unlock_irqrestore(&dcmi->irqlock, flags); + return; + } + } else { + /* Enqueue to video buffers list */ + list_add_tail(&buf->list, &dcmi->buffers); + } + + spin_unlock_irqrestore(&dcmi->irqlock, flags); +} + +static int dcmi_start_streaming(struct vb2_queue *vq, unsigned int count) +{ + struct stm32_dcmi *dcmi = vb2_get_drv_priv(vq); + struct dcmi_buf *buf, *node; + u32 val; + int ret; + + ret = clk_enable(dcmi->mclk); + if (ret) { + dev_err(dcmi->dev, "%s: Failed to start streaming, cannot enable clock", + __func__); + goto err_release_buffers; + } + + /* Enable stream on the sub device */ + ret = v4l2_subdev_call(dcmi->entity.subdev, video, s_stream, 1); + if (ret && ret != -ENOIOCTLCMD) { + dev_err(dcmi->dev, "%s: Failed to start streaming, subdev streamon error", + __func__); + goto err_disable_clock; + } + + spin_lock_irq(&dcmi->irqlock); + + val = reg_read(dcmi->regs, DCMI_CR); + + val &= ~(CR_PCKPOL | CR_HSPOL | CR_VSPOL | + CR_EDM_0 | CR_EDM_1 | CR_FCRC_0 | + CR_FCRC_1 | CR_JPEG | CR_ESS); + + /* Set bus width */ + switch (dcmi->bus.bus_width) { + case 14: + val &= CR_EDM_0 + CR_EDM_1; + break; + case 12: + val &= CR_EDM_1; + break; + case 10: + val &= CR_EDM_0; + break; + default: + /* Set bus width to 8 bits by default */ + break; + } + + /* Set vertical synchronization polarity */ + if (dcmi->bus.flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) + val |= CR_VSPOL; + + /* Set horizontal synchronization polarity */ + if (dcmi->bus.flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) + val |= CR_HSPOL; + + /* Set pixel clock polarity */ + if (dcmi->bus.flags & V4L2_MBUS_PCLK_SAMPLE_RISING) + val |= CR_PCKPOL; + + reg_write(dcmi->regs, DCMI_CR, val); + + /* Enable dcmi */ + reg_set(dcmi->regs, DCMI_CR, CR_ENABLE); + + dcmi->state = RUNNING; + + dcmi->sequence = 0; + dcmi->errors_count = 0; + dcmi->buffers_count = 0; + dcmi->active = NULL; + + /* + * Start transfer if at least one buffer has been queued, + * otherwise transfer is deferred at buffer queueing + */ + if (list_empty(&dcmi->buffers)) { + dev_dbg(dcmi->dev, "Start streaming is deferred to next buffer queueing\n"); + spin_unlock_irq(&dcmi->irqlock); + return 0; + } + + dcmi->active = list_entry(dcmi->buffers.next, struct dcmi_buf, list); + list_del_init(&dcmi->active->list); + + dev_dbg(dcmi->dev, "Start streaming, starting capture\n"); + + ret = dcmi_start_capture(dcmi); + if (ret) { + dev_err(dcmi->dev, "%s: Start streaming failed, cannot start capture", + __func__); + + spin_unlock_irq(&dcmi->irqlock); + goto err_subdev_streamoff; + } + + /* Enable interruptions */ + reg_set(dcmi->regs, DCMI_IER, IT_FRAME | IT_OVR | IT_ERR); + + spin_unlock_irq(&dcmi->irqlock); + + return 0; + +err_subdev_streamoff: + v4l2_subdev_call(dcmi->entity.subdev, video, s_stream, 0); + +err_disable_clock: + clk_disable(dcmi->mclk); + +err_release_buffers: + spin_lock_irq(&dcmi->irqlock); + /* + * Return all buffers to vb2 in QUEUED state. + * This will give ownership back to userspace + */ + if (dcmi->active) { + buf = dcmi->active; + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED); + dcmi->active = NULL; + } + list_for_each_entry_safe(buf, node, &dcmi->buffers, list) { + list_del_init(&buf->list); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED); + } + spin_unlock_irq(&dcmi->irqlock); + + return ret; +} + +static void dcmi_stop_streaming(struct vb2_queue *vq) +{ + struct stm32_dcmi *dcmi = vb2_get_drv_priv(vq); + struct dcmi_buf *buf, *node; + unsigned long time_ms = msecs_to_jiffies(TIMEOUT_MS); + long timeout; + int ret; + + /* Disable stream on the sub device */ + ret = v4l2_subdev_call(dcmi->entity.subdev, video, s_stream, 0); + if (ret && ret != -ENOIOCTLCMD) + dev_err(dcmi->dev, "stream off failed in subdev\n"); + + dcmi->state = STOPPING; + + timeout = wait_for_completion_interruptible_timeout(&dcmi->complete, + time_ms); + + spin_lock_irq(&dcmi->irqlock); + + /* Disable interruptions */ + reg_clear(dcmi->regs, DCMI_IER, IT_FRAME | IT_OVR | IT_ERR); + + /* Disable DCMI */ + reg_clear(dcmi->regs, DCMI_CR, CR_ENABLE); + + if (!timeout) { + dev_err(dcmi->dev, "Timeout during stop streaming\n"); + dcmi->state = STOPPED; + } + + /* Return all queued buffers to vb2 in ERROR state */ + if (dcmi->active) { + buf = dcmi->active; + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); + dcmi->active = NULL; + } + list_for_each_entry_safe(buf, node, &dcmi->buffers, list) { + list_del_init(&buf->list); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); + } + + spin_unlock_irq(&dcmi->irqlock); + + /* Stop all pending DMA operations */ + dmaengine_terminate_all(dcmi->dma_chan); + + clk_disable(dcmi->mclk); + + dev_dbg(dcmi->dev, "Stop streaming, errors=%d buffers=%d\n", + dcmi->errors_count, dcmi->buffers_count); +} + +static struct vb2_ops dcmi_video_qops = { + .queue_setup = dcmi_queue_setup, + .buf_init = dcmi_buf_init, + .buf_prepare = dcmi_buf_prepare, + .buf_queue = dcmi_buf_queue, + .start_streaming = dcmi_start_streaming, + .stop_streaming = dcmi_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +static int dcmi_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct stm32_dcmi *dcmi = video_drvdata(file); + + *fmt = dcmi->fmt; + + return 0; +} + +static const struct dcmi_format *find_format_by_fourcc(struct stm32_dcmi *dcmi, + unsigned int fourcc) +{ + unsigned int num_formats = dcmi->num_user_formats; + const struct dcmi_format *fmt; + unsigned int i; + + for (i = 0; i < num_formats; i++) { + fmt = dcmi->user_formats[i]; + if (fmt->fourcc == fourcc) + return fmt; + } + + return NULL; +} + +static int dcmi_try_fmt(struct stm32_dcmi *dcmi, struct v4l2_format *f, + const struct dcmi_format **current_fmt) +{ + const struct dcmi_format *dcmi_fmt; + struct v4l2_pix_format *pixfmt = &f->fmt.pix; + struct v4l2_subdev_pad_config pad_cfg; + struct v4l2_subdev_format format = { + .which = V4L2_SUBDEV_FORMAT_TRY, + }; + int ret; + + dcmi_fmt = find_format_by_fourcc(dcmi, pixfmt->pixelformat); + if (!dcmi_fmt) { + dcmi_fmt = dcmi->user_formats[dcmi->num_user_formats - 1]; + pixfmt->pixelformat = dcmi_fmt->fourcc; + } + + /* Limit to hardware capabilities */ + pixfmt->width = clamp(pixfmt->width, MIN_WIDTH, MAX_WIDTH); + pixfmt->height = clamp(pixfmt->height, MIN_HEIGHT, MAX_HEIGHT); + + v4l2_fill_mbus_format(&format.format, pixfmt, dcmi_fmt->mbus_code); + ret = v4l2_subdev_call(dcmi->entity.subdev, pad, set_fmt, + &pad_cfg, &format); + if (ret < 0) + return ret; + + v4l2_fill_pix_format(pixfmt, &format.format); + + pixfmt->field = V4L2_FIELD_NONE; + pixfmt->bytesperline = pixfmt->width * dcmi_fmt->bpp; + pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height; + + if (current_fmt) + *current_fmt = dcmi_fmt; + + return 0; +} + +static int dcmi_set_fmt(struct stm32_dcmi *dcmi, struct v4l2_format *f) +{ + struct v4l2_subdev_format format = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + const struct dcmi_format *current_fmt; + int ret; + + ret = dcmi_try_fmt(dcmi, f, ¤t_fmt); + if (ret) + return ret; + + v4l2_fill_mbus_format(&format.format, &f->fmt.pix, + current_fmt->mbus_code); + ret = v4l2_subdev_call(dcmi->entity.subdev, pad, + set_fmt, NULL, &format); + if (ret < 0) + return ret; + + dcmi->fmt = *f; + dcmi->current_fmt = current_fmt; + + return 0; +} + +static int dcmi_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct stm32_dcmi *dcmi = video_drvdata(file); + + if (vb2_is_streaming(&dcmi->queue)) + return -EBUSY; + + return dcmi_set_fmt(dcmi, f); +} + +static int dcmi_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct stm32_dcmi *dcmi = video_drvdata(file); + + return dcmi_try_fmt(dcmi, f, NULL); +} + +static int dcmi_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + struct stm32_dcmi *dcmi = video_drvdata(file); + + if (f->index >= dcmi->num_user_formats) + return -EINVAL; + + f->pixelformat = dcmi->user_formats[f->index]->fourcc; + return 0; +} + +static int dcmi_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + strlcpy(cap->driver, DRV_NAME, sizeof(cap->driver)); + strlcpy(cap->card, "STM32 Camera Memory Interface", + sizeof(cap->card)); + strlcpy(cap->bus_info, "platform:dcmi", sizeof(cap->bus_info)); + return 0; +} + +static int dcmi_enum_input(struct file *file, void *priv, + struct v4l2_input *i) +{ + if (i->index != 0) + return -EINVAL; + + i->type = V4L2_INPUT_TYPE_CAMERA; + strlcpy(i->name, "Camera", sizeof(i->name)); + return 0; +} + +static int dcmi_g_input(struct file *file, void *priv, unsigned int *i) +{ + *i = 0; + return 0; +} + +static int dcmi_s_input(struct file *file, void *priv, unsigned int i) +{ + if (i > 0) + return -EINVAL; + return 0; +} + +static int dcmi_enum_framesizes(struct file *file, void *fh, + struct v4l2_frmsizeenum *fsize) +{ + struct stm32_dcmi *dcmi = video_drvdata(file); + const struct dcmi_format *dcmi_fmt; + struct v4l2_subdev_frame_size_enum fse = { + .index = fsize->index, + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + int ret; + + dcmi_fmt = find_format_by_fourcc(dcmi, fsize->pixel_format); + if (!dcmi_fmt) + return -EINVAL; + + fse.code = dcmi_fmt->mbus_code; + + ret = v4l2_subdev_call(dcmi->entity.subdev, pad, enum_frame_size, + NULL, &fse); + if (ret) + return ret; + + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; + fsize->discrete.width = fse.max_width; + fsize->discrete.height = fse.max_height; + + return 0; +} + +static int dcmi_enum_frameintervals(struct file *file, void *fh, + struct v4l2_frmivalenum *fival) +{ + struct stm32_dcmi *dcmi = video_drvdata(file); + const struct dcmi_format *dcmi_fmt; + struct v4l2_subdev_frame_interval_enum fie = { + .index = fival->index, + .width = fival->width, + .height = fival->height, + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + int ret; + + dcmi_fmt = find_format_by_fourcc(dcmi, fival->pixel_format); + if (!dcmi_fmt) + return -EINVAL; + + fie.code = dcmi_fmt->mbus_code; + + ret = v4l2_subdev_call(dcmi->entity.subdev, pad, + enum_frame_interval, NULL, &fie); + if (ret) + return ret; + + fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; + fival->discrete = fie.interval; + + return 0; +} + +static const struct of_device_id stm32_dcmi_of_match[] = { + { .compatible = "st,stm32-dcmi"}, + { /* end node */ }, +}; +MODULE_DEVICE_TABLE(of, stm32_dcmi_of_match); + +static int dcmi_open(struct file *file) +{ + struct stm32_dcmi *dcmi = video_drvdata(file); + struct v4l2_subdev *sd = dcmi->entity.subdev; + int ret; + + if (mutex_lock_interruptible(&dcmi->lock)) + return -ERESTARTSYS; + + ret = v4l2_fh_open(file); + if (ret < 0) + goto unlock; + + if (!v4l2_fh_is_singular_file(file)) + goto fh_rel; + + ret = v4l2_subdev_call(sd, core, s_power, 1); + if (ret < 0 && ret != -ENOIOCTLCMD) + goto fh_rel; + + ret = dcmi_set_fmt(dcmi, &dcmi->fmt); + if (ret) + v4l2_subdev_call(sd, core, s_power, 0); +fh_rel: + if (ret) + v4l2_fh_release(file); +unlock: + mutex_unlock(&dcmi->lock); + return ret; +} + +static int dcmi_release(struct file *file) +{ + struct stm32_dcmi *dcmi = video_drvdata(file); + struct v4l2_subdev *sd = dcmi->entity.subdev; + bool fh_singular; + int ret; + + mutex_lock(&dcmi->lock); + + fh_singular = v4l2_fh_is_singular_file(file); + + ret = _vb2_fop_release(file, NULL); + + if (fh_singular) + v4l2_subdev_call(sd, core, s_power, 0); + + mutex_unlock(&dcmi->lock); + + return ret; +} + +static const struct v4l2_ioctl_ops dcmi_ioctl_ops = { + .vidioc_querycap = dcmi_querycap, + + .vidioc_try_fmt_vid_cap = dcmi_try_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = dcmi_g_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = dcmi_s_fmt_vid_cap, + .vidioc_enum_fmt_vid_cap = dcmi_enum_fmt_vid_cap, + + .vidioc_enum_input = dcmi_enum_input, + .vidioc_g_input = dcmi_g_input, + .vidioc_s_input = dcmi_s_input, + + .vidioc_enum_framesizes = dcmi_enum_framesizes, + .vidioc_enum_frameintervals = dcmi_enum_frameintervals, + + .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_expbuf = vb2_ioctl_expbuf, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +static const struct v4l2_file_operations dcmi_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = video_ioctl2, + .open = dcmi_open, + .release = dcmi_release, + .poll = vb2_fop_poll, + .mmap = vb2_fop_mmap, +#ifndef CONFIG_MMU + .get_unmapped_area = vb2_fop_get_unmapped_area, +#endif + .read = vb2_fop_read, +}; + +static int dcmi_set_default_fmt(struct stm32_dcmi *dcmi) +{ + struct v4l2_format f = { + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + .fmt.pix = { + .width = CIF_WIDTH, + .height = CIF_HEIGHT, + .field = V4L2_FIELD_NONE, + .pixelformat = dcmi->user_formats[0]->fourcc, + }, + }; + int ret; + + ret = dcmi_try_fmt(dcmi, &f, NULL); + if (ret) + return ret; + dcmi->current_fmt = dcmi->user_formats[0]; + dcmi->fmt = f; + return 0; +} + +static const struct dcmi_format dcmi_formats[] = { + { + .fourcc = V4L2_PIX_FMT_RGB565, + .mbus_code = MEDIA_BUS_FMT_RGB565_2X8_LE, + .bpp = 2, + }, { + .fourcc = V4L2_PIX_FMT_YUYV, + .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8, + .bpp = 2, + }, { + .fourcc = V4L2_PIX_FMT_UYVY, + .mbus_code = MEDIA_BUS_FMT_UYVY8_2X8, + .bpp = 2, + }, +}; + +static int dcmi_formats_init(struct stm32_dcmi *dcmi) +{ + const struct dcmi_format *dcmi_fmts[ARRAY_SIZE(dcmi_formats)]; + unsigned int num_fmts = 0, i, j; + struct v4l2_subdev *subdev = dcmi->entity.subdev; + struct v4l2_subdev_mbus_code_enum mbus_code = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + + while (!v4l2_subdev_call(subdev, pad, enum_mbus_code, + NULL, &mbus_code)) { + for (i = 0; i < ARRAY_SIZE(dcmi_formats); i++) { + if (dcmi_formats[i].mbus_code != mbus_code.code) + continue; + + /* Code supported, have we got this fourcc yet? */ + for (j = 0; j < num_fmts; j++) + if (dcmi_fmts[j]->fourcc == + dcmi_formats[i].fourcc) + /* Already available */ + break; + if (j == num_fmts) + /* New */ + dcmi_fmts[num_fmts++] = dcmi_formats + i; + } + mbus_code.index++; + } + + if (!num_fmts) + return -ENXIO; + + dcmi->num_user_formats = num_fmts; + dcmi->user_formats = devm_kcalloc(dcmi->dev, + num_fmts, sizeof(struct dcmi_format *), + GFP_KERNEL); + if (!dcmi->user_formats) { + dev_err(dcmi->dev, "could not allocate memory\n"); + return -ENOMEM; + } + + memcpy(dcmi->user_formats, dcmi_fmts, + num_fmts * sizeof(struct dcmi_format *)); + dcmi->current_fmt = dcmi->user_formats[0]; + + return 0; +} + +static int dcmi_graph_notify_complete(struct v4l2_async_notifier *notifier) +{ + struct stm32_dcmi *dcmi = notifier_to_dcmi(notifier); + int ret; + + dcmi->vdev->ctrl_handler = dcmi->entity.subdev->ctrl_handler; + ret = dcmi_formats_init(dcmi); + if (ret) { + dev_err(dcmi->dev, "No supported mediabus format found\n"); + return ret; + } + + ret = dcmi_set_default_fmt(dcmi); + if (ret) { + dev_err(dcmi->dev, "Could not set default format\n"); + return ret; + } + + ret = video_register_device(dcmi->vdev, VFL_TYPE_GRABBER, -1); + if (ret) { + dev_err(dcmi->dev, "Failed to register video device\n"); + return ret; + } + + dev_dbg(dcmi->dev, "Device registered as %s\n", + video_device_node_name(dcmi->vdev)); + return 0; +} + +static void dcmi_graph_notify_unbind(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *sd, + struct v4l2_async_subdev *asd) +{ + struct stm32_dcmi *dcmi = notifier_to_dcmi(notifier); + + dev_dbg(dcmi->dev, "Removing %s\n", video_device_node_name(dcmi->vdev)); + + /* Checks internaly if vdev has been init or not */ + video_unregister_device(dcmi->vdev); +} + +static int dcmi_graph_notify_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, + struct v4l2_async_subdev *asd) +{ + struct stm32_dcmi *dcmi = notifier_to_dcmi(notifier); + + dev_dbg(dcmi->dev, "Subdev %s bound\n", subdev->name); + + dcmi->entity.subdev = subdev; + + return 0; +} + +static int dcmi_graph_parse(struct stm32_dcmi *dcmi, struct device_node *node) +{ + struct device_node *ep = NULL; + struct device_node *remote; + + while (1) { + ep = of_graph_get_next_endpoint(node, ep); + if (!ep) + return -EINVAL; + + remote = of_graph_get_remote_port_parent(ep); + if (!remote) { + of_node_put(ep); + return -EINVAL; + } + + /* Remote node to connect */ + dcmi->entity.node = remote; + dcmi->entity.asd.match_type = V4L2_ASYNC_MATCH_OF; + dcmi->entity.asd.match.of.node = remote; + return 0; + } +} + +static int dcmi_graph_init(struct stm32_dcmi *dcmi) +{ + struct v4l2_async_subdev **subdevs = NULL; + int ret; + + /* Parse the graph to extract a list of subdevice DT nodes. */ + ret = dcmi_graph_parse(dcmi, dcmi->dev->of_node); + if (ret < 0) { + dev_err(dcmi->dev, "Graph parsing failed\n"); + return ret; + } + + /* Register the subdevices notifier. */ + subdevs = devm_kzalloc(dcmi->dev, sizeof(*subdevs), GFP_KERNEL); + if (!subdevs) { + of_node_put(dcmi->entity.node); + return -ENOMEM; + } + + subdevs[0] = &dcmi->entity.asd; + + dcmi->notifier.subdevs = subdevs; + dcmi->notifier.num_subdevs = 1; + dcmi->notifier.bound = dcmi_graph_notify_bound; + dcmi->notifier.unbind = dcmi_graph_notify_unbind; + dcmi->notifier.complete = dcmi_graph_notify_complete; + + ret = v4l2_async_notifier_register(&dcmi->v4l2_dev, &dcmi->notifier); + if (ret < 0) { + dev_err(dcmi->dev, "Notifier registration failed\n"); + of_node_put(dcmi->entity.node); + return ret; + } + + return 0; +} + +static int dcmi_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + const struct of_device_id *match = NULL; + struct v4l2_of_endpoint ep; + struct stm32_dcmi *dcmi; + struct vb2_queue *q; + struct dma_chan *chan; + struct clk *mclk; + int irq; + int ret = 0; + + match = of_match_device(of_match_ptr(stm32_dcmi_of_match), &pdev->dev); + if (!match) { + dev_err(&pdev->dev, "Could not find a match in devicetree\n"); + return -ENODEV; + } + + dcmi = devm_kzalloc(&pdev->dev, sizeof(struct stm32_dcmi), GFP_KERNEL); + if (!dcmi) + return -ENOMEM; + + dcmi->rstc = devm_reset_control_get(&pdev->dev, NULL); + if (IS_ERR(dcmi->rstc)) { + dev_err(&pdev->dev, "Could not get reset control\n"); + return -ENODEV; + } + + /* Get bus characteristics from devicetree */ + np = of_graph_get_next_endpoint(np, NULL); + if (!np) { + dev_err(&pdev->dev, "Could not find the endpoint\n"); + of_node_put(np); + return -ENODEV; + } + + ret = v4l2_of_parse_endpoint(np, &ep); + if (ret) { + dev_err(&pdev->dev, "Could not parse the endpoint\n"); + of_node_put(np); + return -ENODEV; + } + + if (ep.bus_type == V4L2_MBUS_CSI2) { + dev_err(&pdev->dev, "CSI bus not supported\n"); + of_node_put(np); + return -ENODEV; + } + dcmi->bus.flags = ep.bus.parallel.flags; + dcmi->bus.bus_width = ep.bus.parallel.bus_width; + dcmi->bus.data_shift = ep.bus.parallel.data_shift; + + of_node_put(np); + + irq = platform_get_irq(pdev, 0); + if (irq <= 0) { + dev_err(&pdev->dev, "Could not get irq\n"); + return -ENODEV; + } + + dcmi->res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!dcmi->res) { + dev_err(&pdev->dev, "Could not get resource\n"); + return -ENODEV; + } + + dcmi->regs = devm_ioremap_resource(&pdev->dev, dcmi->res); + if (IS_ERR(dcmi->regs)) { + dev_err(&pdev->dev, "Could not map registers\n"); + return PTR_ERR(dcmi->regs); + } + + ret = devm_request_threaded_irq(&pdev->dev, irq, dcmi_irq_callback, + dcmi_irq_thread, IRQF_ONESHOT, + dev_name(&pdev->dev), dcmi); + if (ret) { + dev_err(&pdev->dev, "Unable to request irq %d\n", irq); + return -ENODEV; + } + + mclk = devm_clk_get(&pdev->dev, "mclk"); + if (IS_ERR(mclk)) { + dev_err(&pdev->dev, "Unable to get mclk\n"); + return PTR_ERR(mclk); + } + + chan = dma_request_slave_channel(&pdev->dev, "tx"); + if (!chan) { + dev_info(&pdev->dev, "Unable to request DMA channel, defer probing\n"); + return -EPROBE_DEFER; + } + + ret = clk_prepare(mclk); + if (ret) { + dev_err(&pdev->dev, "Unable to prepare mclk %p\n", mclk); + goto err_dma_release; + } + + spin_lock_init(&dcmi->irqlock); + mutex_init(&dcmi->lock); + init_completion(&dcmi->complete); + INIT_LIST_HEAD(&dcmi->buffers); + + dcmi->dev = &pdev->dev; + dcmi->mclk = mclk; + dcmi->state = STOPPED; + dcmi->dma_chan = chan; + + q = &dcmi->queue; + + /* Initialize the top-level structure */ + ret = v4l2_device_register(&pdev->dev, &dcmi->v4l2_dev); + if (ret) + goto err_clk_unprepare; + + dcmi->vdev = video_device_alloc(); + if (!dcmi->vdev) { + ret = -ENOMEM; + goto err_device_unregister; + } + + /* Video node */ + dcmi->vdev->fops = &dcmi_fops; + dcmi->vdev->v4l2_dev = &dcmi->v4l2_dev; + dcmi->vdev->queue = &dcmi->queue; + strlcpy(dcmi->vdev->name, KBUILD_MODNAME, sizeof(dcmi->vdev->name)); + dcmi->vdev->release = video_device_release; + dcmi->vdev->ioctl_ops = &dcmi_ioctl_ops; + dcmi->vdev->lock = &dcmi->lock; + dcmi->vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | + V4L2_CAP_READWRITE; + video_set_drvdata(dcmi->vdev, dcmi); + + /* Buffer queue */ + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + q->io_modes = VB2_MMAP | VB2_READ | VB2_DMABUF; + q->lock = &dcmi->lock; + q->drv_priv = dcmi; + q->buf_struct_size = sizeof(struct dcmi_buf); + q->ops = &dcmi_video_qops; + q->mem_ops = &vb2_dma_contig_memops; + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->min_buffers_needed = 2; + q->dev = &pdev->dev; + + ret = vb2_queue_init(q); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to initialize vb2 queue\n"); + goto err_device_release; + } + + ret = dcmi_graph_init(dcmi); + if (ret < 0) + goto err_device_release; + + /* Reset device */ + ret = reset_control_assert(dcmi->rstc); + if (ret) { + dev_err(&pdev->dev, "Failed to assert the reset line\n"); + goto err_device_release; + } + + usleep_range(3000, 5000); + + ret = reset_control_deassert(dcmi->rstc); + if (ret) { + dev_err(&pdev->dev, "Failed to deassert the reset line\n"); + goto err_device_release; + } + + dev_info(&pdev->dev, "Probe done\n"); + + platform_set_drvdata(pdev, dcmi); + return 0; + +err_device_release: + video_device_release(dcmi->vdev); +err_device_unregister: + v4l2_device_unregister(&dcmi->v4l2_dev); +err_clk_unprepare: + clk_unprepare(dcmi->mclk); +err_dma_release: + dma_release_channel(dcmi->dma_chan); + + return ret; +} + +static int dcmi_remove(struct platform_device *pdev) +{ + struct stm32_dcmi *dcmi = platform_get_drvdata(pdev); + + v4l2_async_notifier_unregister(&dcmi->notifier); + v4l2_device_unregister(&dcmi->v4l2_dev); + clk_unprepare(dcmi->mclk); + dma_release_channel(dcmi->dma_chan); + + return 0; +} + +static struct platform_driver stm32_dcmi_driver = { + .probe = dcmi_probe, + .remove = dcmi_remove, + .driver = { + .name = DRV_NAME, + .of_match_table = of_match_ptr(stm32_dcmi_of_match), + }, +}; + +module_platform_driver(stm32_dcmi_driver); + +MODULE_AUTHOR("Yannick Fertre "); +MODULE_AUTHOR("Hugues Fruchet "); +MODULE_DESCRIPTION("STMicroelectronics STM32 Digital Camera Memory Interface driver"); +MODULE_LICENSE("GPL"); +MODULE_SUPPORTED_DEVICE("video"); -- cgit v1.2.3-58-ga151 From 4e0973a918b9a42e217093f078e04a61e5dd95a5 Mon Sep 17 00:00:00 2001 From: Devin Heitmueller Date: Sat, 20 Sep 2014 09:23:44 -0300 Subject: [media] cx88: Fix regression in initial video standard setting Setting initial standard at the top of cx8800_initdev would cause the first call to cx88_set_tvnorm() to return without programming any registers (leaving the driver saying it's set to NTSC but the hardware isn't programmed). Even worse, any subsequent attempt to explicitly set it to NTSC-M will return success but actually fail to program the underlying registers unless first changing the standard to something other than NTSC-M. Set the initial standard later in the process, and make sure the field is zero at the beginning to ensure that the call always goes through. This regression was introduced in the following commit: commit ccd6f1d488e7 ("[media] cx88: move width, height and field to core struct") Author: Hans Verkuil [media] cx88: move width, height and field to core struct Signed-off-by: Devin Heitmueller Cc: # for v3.19 and up Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/cx88/cx88-cards.c | 9 ++++++++- drivers/media/pci/cx88/cx88-video.c | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/media/pci/cx88/cx88-cards.c b/drivers/media/pci/cx88/cx88-cards.c index 73cc7a67a8bc..6df21b29ea17 100644 --- a/drivers/media/pci/cx88/cx88-cards.c +++ b/drivers/media/pci/cx88/cx88-cards.c @@ -3681,7 +3681,14 @@ struct cx88_core *cx88_core_create(struct pci_dev *pci, int nr) core->nr = nr; sprintf(core->name, "cx88[%d]", core->nr); - core->tvnorm = V4L2_STD_NTSC_M; + /* + * Note: Setting initial standard here would cause first call to + * cx88_set_tvnorm() to return without programming any registers. Leave + * it blank for at this point and it will get set later in + * cx8800_initdev() + */ + core->tvnorm = 0; + core->width = 320; core->height = 240; core->field = V4L2_FIELD_INTERLACED; diff --git a/drivers/media/pci/cx88/cx88-video.c b/drivers/media/pci/cx88/cx88-video.c index 36b4fb908e31..7d25ecd4404b 100644 --- a/drivers/media/pci/cx88/cx88-video.c +++ b/drivers/media/pci/cx88/cx88-video.c @@ -1435,7 +1435,7 @@ static int cx8800_initdev(struct pci_dev *pci_dev, /* initial device configuration */ mutex_lock(&core->lock); - cx88_set_tvnorm(core, core->tvnorm); + cx88_set_tvnorm(core, V4L2_STD_NTSC_M); v4l2_ctrl_handler_setup(&core->video_hdl); v4l2_ctrl_handler_setup(&core->audio_hdl); cx88_video_mux(core, 0); -- cgit v1.2.3-58-ga151 From 682559d9d5bdb986134c891951509ae19068f937 Mon Sep 17 00:00:00 2001 From: Songjun Wu Date: Thu, 20 Apr 2017 05:51:30 -0300 Subject: [media] atmel-isc: Set the default DMA memory burst size Sometimes 'DMA single access' is not enough to transfer a frame of image, '8-beat burst access' is set as the default DMA memory burst size. Signed-off-by: Songjun Wu Acked-by: Nicolas Ferre Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/atmel/atmel-isc.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/drivers/media/platform/atmel/atmel-isc.c b/drivers/media/platform/atmel/atmel-isc.c index c4b2115559a5..78d966233f80 100644 --- a/drivers/media/platform/atmel/atmel-isc.c +++ b/drivers/media/platform/atmel/atmel-isc.c @@ -239,13 +239,11 @@ static struct isc_format isc_formats[] = { { V4L2_PIX_FMT_YUV420, 0x0, 12, ISC_PFE_CFG0_BPS_EIGHT, ISC_BAY_CFG_BGBG, ISC_RLP_CFG_MODE_YYCC, - ISC_DCFG_IMODE_YC420P | ISC_DCFG_YMBSIZE_BEATS8 | - ISC_DCFG_CMBSIZE_BEATS8, ISC_DCTRL_DVIEW_PLANAR, 0x7fb, + ISC_DCFG_IMODE_YC420P, ISC_DCTRL_DVIEW_PLANAR, 0x7fb, false, false }, { V4L2_PIX_FMT_YUV422P, 0x0, 16, ISC_PFE_CFG0_BPS_EIGHT, ISC_BAY_CFG_BGBG, ISC_RLP_CFG_MODE_YYCC, - ISC_DCFG_IMODE_YC422P | ISC_DCFG_YMBSIZE_BEATS8 | - ISC_DCFG_CMBSIZE_BEATS8, ISC_DCTRL_DVIEW_PLANAR, 0x3fb, + ISC_DCFG_IMODE_YC422P, ISC_DCTRL_DVIEW_PLANAR, 0x3fb, false, false }, { V4L2_PIX_FMT_RGB565, MEDIA_BUS_FMT_RGB565_2X8_LE, 16, ISC_PFE_CFG0_BPS_EIGHT, ISC_BAY_CFG_BGBG, ISC_RLP_CFG_MODE_RGB565, @@ -700,8 +698,10 @@ static void isc_set_histogram(struct isc_device *isc) } static inline void isc_get_param(const struct isc_format *fmt, - u32 *rlp_mode, u32 *dcfg_imode) + u32 *rlp_mode, u32 *dcfg) { + *dcfg = ISC_DCFG_YMBSIZE_BEATS8; + switch (fmt->fourcc) { case V4L2_PIX_FMT_SBGGR10: case V4L2_PIX_FMT_SGBRG10: @@ -712,11 +712,11 @@ static inline void isc_get_param(const struct isc_format *fmt, case V4L2_PIX_FMT_SGRBG12: case V4L2_PIX_FMT_SRGGB12: *rlp_mode = fmt->reg_rlp_mode; - *dcfg_imode = fmt->reg_dcfg_imode; + *dcfg |= fmt->reg_dcfg_imode; break; default: *rlp_mode = ISC_RLP_CFG_MODE_DAT8; - *dcfg_imode = ISC_DCFG_IMODE_PACKED8; + *dcfg |= ISC_DCFG_IMODE_PACKED8; break; } } @@ -726,18 +726,19 @@ static int isc_configure(struct isc_device *isc) struct regmap *regmap = isc->regmap; const struct isc_format *current_fmt = isc->current_fmt; struct isc_subdev_entity *subdev = isc->current_subdev; - u32 pfe_cfg0, rlp_mode, dcfg_imode, mask, pipeline; + u32 pfe_cfg0, rlp_mode, dcfg, mask, pipeline; if (sensor_is_preferred(current_fmt)) { pfe_cfg0 = current_fmt->reg_bps; pipeline = 0x0; - isc_get_param(current_fmt, &rlp_mode, &dcfg_imode); + isc_get_param(current_fmt, &rlp_mode, &dcfg); isc->ctrls.hist_stat = HIST_INIT; } else { pfe_cfg0 = isc->raw_fmt->reg_bps; pipeline = current_fmt->pipeline; rlp_mode = current_fmt->reg_rlp_mode; - dcfg_imode = current_fmt->reg_dcfg_imode; + dcfg = current_fmt->reg_dcfg_imode | ISC_DCFG_YMBSIZE_BEATS8 | + ISC_DCFG_CMBSIZE_BEATS8; } pfe_cfg0 |= subdev->pfe_cfg0 | ISC_PFE_CFG0_MODE_PROGRESSIVE; @@ -750,7 +751,7 @@ static int isc_configure(struct isc_device *isc) regmap_update_bits(regmap, ISC_RLP_CFG, ISC_RLP_CFG_MODE_MASK, rlp_mode); - regmap_update_bits(regmap, ISC_DCFG, ISC_DCFG_IMODE_MASK, dcfg_imode); + regmap_write(regmap, ISC_DCFG, dcfg); /* Set the pipeline */ isc_set_pipeline(isc, pipeline); -- cgit v1.2.3-58-ga151 From d90b336f3f652ff0441e631a06236f785581c8f7 Mon Sep 17 00:00:00 2001 From: Devin Heitmueller Date: Fri, 21 Apr 2017 13:28:37 -0300 Subject: [media] mxl111sf: Fix driver to use heap allocate buffers for USB messages The recent changes in 4.9 to mandate USB buffers be heap allocated broke this driver, which was allocating the buffers on the stack. This resulted in the device failing at initialization. Introduce dedicated send/receive buffers as part of the state structure, and add a mutex to protect access to them. Note: we also had to tweak the API to mxl111sf_ctrl_msg to pass the pointer to the state struct rather than the device, since we need it inside the function to access the buffers and the mutex. This patch adjusts the callers to match the API change. Signed-off-by: Devin Heitmueller Reported-by: Doug Lung Cc: Michael Ira Krufky Cc: # for v4.5 and up Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/mxl111sf-i2c.c | 4 ++-- drivers/media/usb/dvb-usb-v2/mxl111sf.c | 32 +++++++++++++++++------------ drivers/media/usb/dvb-usb-v2/mxl111sf.h | 8 +++++++- 3 files changed, 28 insertions(+), 16 deletions(-) diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf-i2c.c b/drivers/media/usb/dvb-usb-v2/mxl111sf-i2c.c index ffb49c28b15a..0eb33e043079 100644 --- a/drivers/media/usb/dvb-usb-v2/mxl111sf-i2c.c +++ b/drivers/media/usb/dvb-usb-v2/mxl111sf-i2c.c @@ -316,7 +316,7 @@ fail: static int mxl111sf_i2c_send_data(struct mxl111sf_state *state, u8 index, u8 *wdata) { - int ret = mxl111sf_ctrl_msg(state->d, wdata[0], + int ret = mxl111sf_ctrl_msg(state, wdata[0], &wdata[1], 25, NULL, 0); mxl_fail(ret); @@ -326,7 +326,7 @@ static int mxl111sf_i2c_send_data(struct mxl111sf_state *state, static int mxl111sf_i2c_get_data(struct mxl111sf_state *state, u8 index, u8 *wdata, u8 *rdata) { - int ret = mxl111sf_ctrl_msg(state->d, wdata[0], + int ret = mxl111sf_ctrl_msg(state, wdata[0], &wdata[1], 25, rdata, 24); mxl_fail(ret); diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf.c b/drivers/media/usb/dvb-usb-v2/mxl111sf.c index abf69d8fa469..b0d5904a4ea6 100644 --- a/drivers/media/usb/dvb-usb-v2/mxl111sf.c +++ b/drivers/media/usb/dvb-usb-v2/mxl111sf.c @@ -24,9 +24,6 @@ #include "lgdt3305.h" #include "lg2160.h" -/* Max transfer size done by I2C transfer functions */ -#define MAX_XFER_SIZE 64 - int dvb_usb_mxl111sf_debug; module_param_named(debug, dvb_usb_mxl111sf_debug, int, 0644); MODULE_PARM_DESC(debug, "set debugging level (1=info, 2=xfer, 4=i2c, 8=reg, 16=adv (or-able))."); @@ -55,27 +52,34 @@ MODULE_PARM_DESC(rfswitch, "force rf switch position (0=auto, 1=ext, 2=int)."); DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); -int mxl111sf_ctrl_msg(struct dvb_usb_device *d, +int mxl111sf_ctrl_msg(struct mxl111sf_state *state, u8 cmd, u8 *wbuf, int wlen, u8 *rbuf, int rlen) { + struct dvb_usb_device *d = state->d; int wo = (rbuf == NULL || rlen == 0); /* write-only */ int ret; - u8 sndbuf[MAX_XFER_SIZE]; - if (1 + wlen > sizeof(sndbuf)) { + if (1 + wlen > MXL_MAX_XFER_SIZE) { pr_warn("%s: len=%d is too big!\n", __func__, wlen); return -EOPNOTSUPP; } pr_debug("%s(wlen = %d, rlen = %d)\n", __func__, wlen, rlen); - memset(sndbuf, 0, 1+wlen); + mutex_lock(&state->msg_lock); + memset(state->sndbuf, 0, 1+wlen); + memset(state->rcvbuf, 0, rlen); + + state->sndbuf[0] = cmd; + memcpy(&state->sndbuf[1], wbuf, wlen); - sndbuf[0] = cmd; - memcpy(&sndbuf[1], wbuf, wlen); + ret = (wo) ? dvb_usbv2_generic_write(d, state->sndbuf, 1+wlen) : + dvb_usbv2_generic_rw(d, state->sndbuf, 1+wlen, state->rcvbuf, + rlen); + + memcpy(rbuf, state->rcvbuf, rlen); + mutex_unlock(&state->msg_lock); - ret = (wo) ? dvb_usbv2_generic_write(d, sndbuf, 1+wlen) : - dvb_usbv2_generic_rw(d, sndbuf, 1+wlen, rbuf, rlen); mxl_fail(ret); return ret; @@ -91,7 +95,7 @@ int mxl111sf_read_reg(struct mxl111sf_state *state, u8 addr, u8 *data) u8 buf[2]; int ret; - ret = mxl111sf_ctrl_msg(state->d, MXL_CMD_REG_READ, &addr, 1, buf, 2); + ret = mxl111sf_ctrl_msg(state, MXL_CMD_REG_READ, &addr, 1, buf, 2); if (mxl_fail(ret)) { mxl_debug("error reading reg: 0x%02x", addr); goto fail; @@ -117,7 +121,7 @@ int mxl111sf_write_reg(struct mxl111sf_state *state, u8 addr, u8 data) pr_debug("W: (0x%02x, 0x%02x)\n", addr, data); - ret = mxl111sf_ctrl_msg(state->d, MXL_CMD_REG_WRITE, buf, 2, NULL, 0); + ret = mxl111sf_ctrl_msg(state, MXL_CMD_REG_WRITE, buf, 2, NULL, 0); if (mxl_fail(ret)) pr_err("error writing reg: 0x%02x, val: 0x%02x", addr, data); return ret; @@ -926,6 +930,8 @@ static int mxl111sf_init(struct dvb_usb_device *d) .len = sizeof(eeprom), .buf = eeprom }, }; + mutex_init(&state->msg_lock); + ret = get_chip_info(state); if (mxl_fail(ret)) pr_err("failed to get chip info during probe"); diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf.h b/drivers/media/usb/dvb-usb-v2/mxl111sf.h index 846260e0eec0..3e6f5880bd1e 100644 --- a/drivers/media/usb/dvb-usb-v2/mxl111sf.h +++ b/drivers/media/usb/dvb-usb-v2/mxl111sf.h @@ -19,6 +19,9 @@ #include #include +/* Max transfer size done by I2C transfer functions */ +#define MXL_MAX_XFER_SIZE 64 + #define MXL_EP1_REG_READ 1 #define MXL_EP2_REG_WRITE 2 #define MXL_EP3_INTERRUPT 3 @@ -86,6 +89,9 @@ struct mxl111sf_state { struct mutex fe_lock; u8 num_frontends; struct mxl111sf_adap_state adap_state[3]; + u8 sndbuf[MXL_MAX_XFER_SIZE]; + u8 rcvbuf[MXL_MAX_XFER_SIZE]; + struct mutex msg_lock; #ifdef CONFIG_MEDIA_CONTROLLER_DVB struct media_entity tuner; struct media_pad tuner_pads[2]; @@ -108,7 +114,7 @@ int mxl111sf_ctrl_program_regs(struct mxl111sf_state *state, /* needed for hardware i2c functions in mxl111sf-i2c.c: * mxl111sf_i2c_send_data / mxl111sf_i2c_get_data */ -int mxl111sf_ctrl_msg(struct dvb_usb_device *d, +int mxl111sf_ctrl_msg(struct mxl111sf_state *state, u8 cmd, u8 *wbuf, int wlen, u8 *rbuf, int rlen); #define mxl_printk(kern, fmt, arg...) \ -- cgit v1.2.3-58-ga151 From 13174c388a76005c1abe454e47a37cd57edd8c8d Mon Sep 17 00:00:00 2001 From: Pan Bian Date: Sun, 23 Apr 2017 06:26:45 -0300 Subject: [media] m5602_s5k83a: check return value of kthread_create Function kthread_create() returns an ERR_PTR on error. However, in function s5k83a_start(), its return value is used without validation. This may result in a bad memory access bug. This patch fixes the bug. Signed-off-by: Pan Bian Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/gspca/m5602/m5602_s5k83a.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/media/usb/gspca/m5602/m5602_s5k83a.c b/drivers/media/usb/gspca/m5602/m5602_s5k83a.c index be5e25d1a2e8..6ad8d4849680 100644 --- a/drivers/media/usb/gspca/m5602/m5602_s5k83a.c +++ b/drivers/media/usb/gspca/m5602/m5602_s5k83a.c @@ -345,6 +345,11 @@ int s5k83a_start(struct sd *sd) to assume that there is no better way of accomplishing this */ sd->rotation_thread = kthread_create(rotation_thread_function, sd, "rotation thread"); + if (IS_ERR(sd->rotation_thread)) { + err = PTR_ERR(sd->rotation_thread); + sd->rotation_thread = NULL; + return err; + } wake_up_process(sd->rotation_thread); /* Preinit the sensor */ -- cgit v1.2.3-58-ga151 From b9150e8131914df75d396a5730a599d358f016d8 Mon Sep 17 00:00:00 2001 From: Pan Bian Date: Sun, 23 Apr 2017 09:18:29 -0300 Subject: [media] cobalt: fix unchecked return values Function pci_find_ext_capability() may return 0, which is an invalid address. In function cobalt_pcie_status_show(), its return value is used without validation. This patch adds checks to validate the return address. Signed-off-by: Pan Bian Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/cobalt/cobalt-driver.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/pci/cobalt/cobalt-driver.c b/drivers/media/pci/cobalt/cobalt-driver.c index d5c911c09e2b..f8e173f3e9e2 100644 --- a/drivers/media/pci/cobalt/cobalt-driver.c +++ b/drivers/media/pci/cobalt/cobalt-driver.c @@ -205,6 +205,8 @@ void cobalt_pcie_status_show(struct cobalt *cobalt) offset = pci_find_capability(pci_dev, PCI_CAP_ID_EXP); bus_offset = pci_find_capability(pci_bus_dev, PCI_CAP_ID_EXP); + if (!offset || !bus_offset) + return; /* Device */ pci_read_config_dword(pci_dev, offset + PCI_EXP_DEVCAP, &capa); -- cgit v1.2.3-58-ga151 From 35378ce143071c2a6bad4b59a000e9b9f8f6ea67 Mon Sep 17 00:00:00 2001 From: Pan Bian Date: Sun, 23 Apr 2017 10:06:36 -0300 Subject: [media] cx25840: fix unchecked return values In functions cx25840_initialize(), cx231xx_initialize(), and cx23885_initialize(), the return value of create_singlethread_workqueue() is used without validation. This may result in NULL dereference and cause kernel crash. This patch fixes it. Signed-off-by: Pan Bian Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/cx25840/cx25840-core.c | 36 +++++++++++++++++++------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/drivers/media/i2c/cx25840/cx25840-core.c b/drivers/media/i2c/cx25840/cx25840-core.c index b8d3c070bfc1..39f51daa7558 100644 --- a/drivers/media/i2c/cx25840/cx25840-core.c +++ b/drivers/media/i2c/cx25840/cx25840-core.c @@ -416,11 +416,13 @@ static void cx25840_initialize(struct i2c_client *client) INIT_WORK(&state->fw_work, cx25840_work_handler); init_waitqueue_head(&state->fw_wait); q = create_singlethread_workqueue("cx25840_fw"); - prepare_to_wait(&state->fw_wait, &wait, TASK_UNINTERRUPTIBLE); - queue_work(q, &state->fw_work); - schedule(); - finish_wait(&state->fw_wait, &wait); - destroy_workqueue(q); + if (q) { + prepare_to_wait(&state->fw_wait, &wait, TASK_UNINTERRUPTIBLE); + queue_work(q, &state->fw_work); + schedule(); + finish_wait(&state->fw_wait, &wait); + destroy_workqueue(q); + } /* 6. */ cx25840_write(client, 0x115, 0x8c); @@ -630,11 +632,13 @@ static void cx23885_initialize(struct i2c_client *client) INIT_WORK(&state->fw_work, cx25840_work_handler); init_waitqueue_head(&state->fw_wait); q = create_singlethread_workqueue("cx25840_fw"); - prepare_to_wait(&state->fw_wait, &wait, TASK_UNINTERRUPTIBLE); - queue_work(q, &state->fw_work); - schedule(); - finish_wait(&state->fw_wait, &wait); - destroy_workqueue(q); + if (q) { + prepare_to_wait(&state->fw_wait, &wait, TASK_UNINTERRUPTIBLE); + queue_work(q, &state->fw_work); + schedule(); + finish_wait(&state->fw_wait, &wait); + destroy_workqueue(q); + } /* Call the cx23888 specific std setup func, we no longer rely on * the generic cx24840 func. @@ -748,11 +752,13 @@ static void cx231xx_initialize(struct i2c_client *client) INIT_WORK(&state->fw_work, cx25840_work_handler); init_waitqueue_head(&state->fw_wait); q = create_singlethread_workqueue("cx25840_fw"); - prepare_to_wait(&state->fw_wait, &wait, TASK_UNINTERRUPTIBLE); - queue_work(q, &state->fw_work); - schedule(); - finish_wait(&state->fw_wait, &wait); - destroy_workqueue(q); + if (q) { + prepare_to_wait(&state->fw_wait, &wait, TASK_UNINTERRUPTIBLE); + queue_work(q, &state->fw_work); + schedule(); + finish_wait(&state->fw_wait, &wait); + destroy_workqueue(q); + } cx25840_std_setup(client); -- cgit v1.2.3-58-ga151 From 017725c702ebd30abd42b55768869f3900d8909b Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 25 Apr 2017 18:32:23 -0300 Subject: [media] cx18: fix spelling mistake: "demodualtor" -> "demodulator" trivial fix to spelling mistake and add in a white space in a CX18_ERR error message Signed-off-by: Colin Ian King Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/cx18/cx18-dvb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/pci/cx18/cx18-dvb.c b/drivers/media/pci/cx18/cx18-dvb.c index d130d65828b0..53f4d6bf81fb 100644 --- a/drivers/media/pci/cx18/cx18-dvb.c +++ b/drivers/media/pci/cx18/cx18-dvb.c @@ -151,7 +151,7 @@ static int yuan_mpc718_mt352_reqfw(struct cx18_stream *stream, } if (ret) { - CX18_ERR("The MPC718 board variant with the MT352 DVB-Tdemodualtor will not work without it\n"); + CX18_ERR("The MPC718 board variant with the MT352 DVB-T demodulator will not work without it\n"); CX18_ERR("Run 'linux/Documentation/dvb/get_dvb_firmware mpc718' if you need the firmware\n"); } return ret; -- cgit v1.2.3-58-ga151 From 603c33a3216735119aed6dd4dbac89a214c0fb4e Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Wed, 26 Apr 2017 13:43:12 -0300 Subject: [media] em28xx: fix+improve the register (usb control message) debugging MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - avoid duplicate debugging messages in em28xx_read_reg_req_len() - do not describe successful usb transfers in em28xx_read_reg_len() as "failed" - report errors in em28xx_write_regs_req(), too - print the usb error numbers, too Signed-off-by: Frank Schäfer Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-core.c | 35 +++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c index 19ccff41c7eb..1d0d8cc06103 100644 --- a/drivers/media/usb/em28xx/em28xx-core.c +++ b/drivers/media/usb/em28xx/em28xx-core.c @@ -91,22 +91,16 @@ int em28xx_read_reg_req_len(struct em28xx *dev, u8 req, u16 reg, if (len > URB_MAX_CTRL_SIZE) return -EINVAL; - em28xx_regdbg("(pipe 0x%08x): IN: %02x %02x %02x %02x %02x %02x %02x %02x ", - pipe, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - req, 0, 0, - reg & 0xff, reg >> 8, - len & 0xff, len >> 8); - mutex_lock(&dev->ctrl_urb_lock); ret = usb_control_msg(udev, pipe, req, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 0x0000, reg, dev->urb_buf, len, HZ); if (ret < 0) { - em28xx_regdbg("(pipe 0x%08x): IN: %02x %02x %02x %02x %02x %02x %02x %02x failed\n", + em28xx_regdbg("(pipe 0x%08x): IN: %02x %02x %02x %02x %02x %02x %02x %02x failed with error %i\n", pipe, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, req, 0, 0, reg & 0xff, reg >> 8, - len & 0xff, len >> 8); + len & 0xff, len >> 8, ret); mutex_unlock(&dev->ctrl_urb_lock); return usb_translate_errors(ret); } @@ -116,7 +110,7 @@ int em28xx_read_reg_req_len(struct em28xx *dev, u8 req, u16 reg, mutex_unlock(&dev->ctrl_urb_lock); - em28xx_regdbg("(pipe 0x%08x): IN: %02x %02x %02x %02x %02x %02x %02x %02x failed <<< %*ph\n", + em28xx_regdbg("(pipe 0x%08x): IN: %02x %02x %02x %02x %02x %02x %02x %02x <<< %*ph\n", pipe, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, req, 0, 0, reg & 0xff, reg >> 8, @@ -164,13 +158,6 @@ int em28xx_write_regs_req(struct em28xx *dev, u8 req, u16 reg, char *buf, if ((len < 1) || (len > URB_MAX_CTRL_SIZE)) return -EINVAL; - em28xx_regdbg("(pipe 0x%08x): OUT: %02x %02x %02x %02x %02x %02x %02x %02x >>> %*ph\n", - pipe, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - req, 0, 0, - reg & 0xff, reg >> 8, - len & 0xff, len >> 8, len, buf); - mutex_lock(&dev->ctrl_urb_lock); memcpy(dev->urb_buf, buf, len); ret = usb_control_msg(udev, pipe, req, @@ -178,8 +165,22 @@ int em28xx_write_regs_req(struct em28xx *dev, u8 req, u16 reg, char *buf, 0x0000, reg, dev->urb_buf, len, HZ); mutex_unlock(&dev->ctrl_urb_lock); - if (ret < 0) + if (ret < 0) { + em28xx_regdbg("(pipe 0x%08x): OUT: %02x %02x %02x %02x %02x %02x %02x %02x >>> %*ph failed with error %i\n", + pipe, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + req, 0, 0, + reg & 0xff, reg >> 8, + len & 0xff, len >> 8, len, buf, ret); return usb_translate_errors(ret); + } + + em28xx_regdbg("(pipe 0x%08x): OUT: %02x %02x %02x %02x %02x %02x %02x %02x >>> %*ph\n", + pipe, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + req, 0, 0, + reg & 0xff, reg >> 8, + len & 0xff, len >> 8, len, buf); if (dev->wait_after_write) msleep(dev->wait_after_write); -- cgit v1.2.3-58-ga151 From 7b848ed60f409ce860023c79aa9a5c8d1833ebab Mon Sep 17 00:00:00 2001 From: Daniel Roschka Date: Thu, 27 Apr 2017 09:28:17 -0300 Subject: [media] uvcvideo: Quirk for webcam in MacBook Pro 2016 Add the probe def quirk for the webcam found in the Apple MacBook Pro 2016, to get it working out of the box. Signed-off-by: Daniel Roschka Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/uvc/uvc_driver.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c index 46d6be0bb316..602256ffe14d 100644 --- a/drivers/media/usb/uvc/uvc_driver.c +++ b/drivers/media/usb/uvc/uvc_driver.c @@ -2441,6 +2441,15 @@ static struct usb_device_id uvc_ids[] = { .bInterfaceProtocol = 0, .driver_info = UVC_QUIRK_PROBE_MINMAX | UVC_QUIRK_BUILTIN_ISIGHT }, + /* Apple Built-In iSight via iBridge */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x05ac, + .idProduct = 0x8600, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_PROBE_DEF }, /* Foxlink ("HP Webcam" on HP Mini 5103) */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, -- cgit v1.2.3-58-ga151 From e7b09f18ec79c002e2139d6a4df0110933743796 Mon Sep 17 00:00:00 2001 From: Peter Boström Date: Thu, 27 Apr 2017 09:28:18 -0300 Subject: [media] uvcvideo: Add iFunction or iInterface to device names MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Permits distinguishing between two /dev/videoX entries from the same physical UVC device (that naturally share the same iProduct name). This change matches current Windows behavior by prioritizing iFunction over iInterface, but unlike Windows it displays both iProduct and iFunction/iInterface strings when both are available. Signed-off-by: Peter Boström Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/uvc/uvc_driver.c | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c index 602256ffe14d..70842c5af05b 100644 --- a/drivers/media/usb/uvc/uvc_driver.c +++ b/drivers/media/usb/uvc/uvc_driver.c @@ -2013,6 +2013,7 @@ static int uvc_probe(struct usb_interface *intf, { struct usb_device *udev = interface_to_usbdev(intf); struct uvc_device *dev; + int function; int ret; if (id->idVendor && id->idProduct) @@ -2044,9 +2045,27 @@ static int uvc_probe(struct usb_interface *intf, strlcpy(dev->name, udev->product, sizeof dev->name); else snprintf(dev->name, sizeof dev->name, - "UVC Camera (%04x:%04x)", - le16_to_cpu(udev->descriptor.idVendor), - le16_to_cpu(udev->descriptor.idProduct)); + "UVC Camera (%04x:%04x)", + le16_to_cpu(udev->descriptor.idVendor), + le16_to_cpu(udev->descriptor.idProduct)); + + /* + * Add iFunction or iInterface to names when available as additional + * distinguishers between interfaces. iFunction is prioritized over + * iInterface which matches Windows behavior at the point of writing. + */ + if (intf->intf_assoc && intf->intf_assoc->iFunction != 0) + function = intf->intf_assoc->iFunction; + else + function = intf->cur_altsetting->desc.iInterface; + if (function != 0) { + size_t len; + + strlcat(dev->name, ": ", sizeof(dev->name)); + len = strlen(dev->name); + usb_string(udev, function, dev->name + len, + sizeof(dev->name) - len); + } /* Parse the Video Class control descriptor. */ if (uvc_parse_control(dev) < 0) { -- cgit v1.2.3-58-ga151 From 30a42a1c90bec5f8c482fe2d0fc444c9e1b15cd8 Mon Sep 17 00:00:00 2001 From: Petr Cvek Date: Mon, 1 May 2017 01:20:45 -0300 Subject: [media] pxa_camera: Add remaining Bayer 8 formats This patch adds Bayer 8 GBRG and RGGB support and move GRBG definition close to BGGR (so all Bayer 8 variants are together). No other changes are needed as the driver handles them as RAW data stream. The RGGB variant was tested in a modified OV9640 driver. Signed-off-by: Petr Cvek Acked-by: Robert Jarzmik Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/pxa_camera.c | 40 +++++++++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/drivers/media/platform/pxa_camera.c b/drivers/media/platform/pxa_camera.c index 5d3f04210a0b..eb9e9d5a101f 100644 --- a/drivers/media/platform/pxa_camera.c +++ b/drivers/media/platform/pxa_camera.c @@ -344,6 +344,36 @@ static const struct pxa_mbus_lookup mbus_fmt[] = { .order = PXA_MBUS_ORDER_LE, .layout = PXA_MBUS_LAYOUT_PACKED, }, +}, { + .code = MEDIA_BUS_FMT_SGBRG8_1X8, + .fmt = { + .fourcc = V4L2_PIX_FMT_SGBRG8, + .name = "Bayer 8 GBRG", + .bits_per_sample = 8, + .packing = PXA_MBUS_PACKING_NONE, + .order = PXA_MBUS_ORDER_LE, + .layout = PXA_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_SGRBG8_1X8, + .fmt = { + .fourcc = V4L2_PIX_FMT_SGRBG8, + .name = "Bayer 8 GRBG", + .bits_per_sample = 8, + .packing = PXA_MBUS_PACKING_NONE, + .order = PXA_MBUS_ORDER_LE, + .layout = PXA_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_SRGGB8_1X8, + .fmt = { + .fourcc = V4L2_PIX_FMT_SRGGB8, + .name = "Bayer 8 RGGB", + .bits_per_sample = 8, + .packing = PXA_MBUS_PACKING_NONE, + .order = PXA_MBUS_ORDER_LE, + .layout = PXA_MBUS_LAYOUT_PACKED, + }, }, { .code = MEDIA_BUS_FMT_SBGGR10_1X10, .fmt = { @@ -444,16 +474,6 @@ static const struct pxa_mbus_lookup mbus_fmt[] = { .order = PXA_MBUS_ORDER_LE, .layout = PXA_MBUS_LAYOUT_PACKED, }, -}, { - .code = MEDIA_BUS_FMT_SGRBG8_1X8, - .fmt = { - .fourcc = V4L2_PIX_FMT_SGRBG8, - .name = "Bayer 8 GRBG", - .bits_per_sample = 8, - .packing = PXA_MBUS_PACKING_NONE, - .order = PXA_MBUS_ORDER_LE, - .layout = PXA_MBUS_LAYOUT_PACKED, - }, }, { .code = MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8, .fmt = { -- cgit v1.2.3-58-ga151 From a14c5d29b4f762e5f752df14b6569fbf936f4ee0 Mon Sep 17 00:00:00 2001 From: Petr Cvek Date: Mon, 1 May 2017 01:21:10 -0300 Subject: [media] pxa_camera: Fix incorrect test in the image size generation During the transfer from the soc_camera a test in pxa_mbus_image_size() got removed. Without it any PXA_MBUS_LAYOUT_PACKED format causes either the return of a wrong value (PXA_MBUS_PACKING_2X8_PADHI doubles the correct value) or EINVAL (PXA_MBUS_PACKING_NONE and PXA_MBUS_PACKING_EXTEND16). This was observed in an error from the ffmpeg (for some of the YUYV subvariants). This patch re-adds the same test as in soc_camera version. Signed-off-by: Petr Cvek Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/pxa_camera.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/media/platform/pxa_camera.c b/drivers/media/platform/pxa_camera.c index eb9e9d5a101f..a572fc9b7a73 100644 --- a/drivers/media/platform/pxa_camera.c +++ b/drivers/media/platform/pxa_camera.c @@ -575,6 +575,9 @@ static s32 pxa_mbus_bytes_per_line(u32 width, const struct pxa_mbus_pixelfmt *mf static s32 pxa_mbus_image_size(const struct pxa_mbus_pixelfmt *mf, u32 bytes_per_line, u32 height) { + if (mf->layout == PXA_MBUS_LAYOUT_PACKED) + return bytes_per_line * height; + switch (mf->packing) { case PXA_MBUS_PACKING_2X8_PADHI: return bytes_per_line * height * 2; -- cgit v1.2.3-58-ga151 From a065c2e52744b302d408436fbc1f153696ae0bd0 Mon Sep 17 00:00:00 2001 From: Petr Cvek Date: Mon, 1 May 2017 01:21:29 -0300 Subject: [media] pxa_camera: Add (un)subscribe_event ioctl The v4l2-compliance complains about nonexistent vidioc_subscribe_event and vidioc_unsubscribe_event calls. Add them to fix the complaints. Signed-off-by: Petr Cvek Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/pxa_camera.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/media/platform/pxa_camera.c b/drivers/media/platform/pxa_camera.c index a572fc9b7a73..ab1fa911849a 100644 --- a/drivers/media/platform/pxa_camera.c +++ b/drivers/media/platform/pxa_camera.c @@ -37,7 +37,9 @@ #include #include #include +#include #include +#include #include #include @@ -2090,6 +2092,8 @@ static const struct v4l2_ioctl_ops pxa_camera_ioctl_ops = { .vidioc_g_register = pxac_vidioc_g_register, .vidioc_s_register = pxac_vidioc_s_register, #endif + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; static struct v4l2_clk_ops pxa_camera_mclk_ops = { -- cgit v1.2.3-58-ga151 From ce550362c5cf1e50f8116ba00f2e5bdb3ed151dd Mon Sep 17 00:00:00 2001 From: Petr Cvek Date: Mon, 1 May 2017 01:21:57 -0300 Subject: [media] pxa_camera: Fix a call with an uninitialized device pointer In 'commit 295ab497d6357 ("[media] media: platform: pxa_camera: make printk consistent")' a pointer to the device structure in mclk_get_divisor() was changed to pcdev_to_dev(pcdev). The pointer used by pcdev_to_dev() is still uninitialized during the call to mclk_get_divisor() as it happens in v4l2_device_register() at the end of the probe. The dev_warn and dev_dbg caused a line in the log: (NULL device *): Limiting master clock to 26000000 Fix this by using an initialized pointer from the platform_device (as before the old patch). Signed-off-by: Petr Cvek Acked-by: Robert Jarzmik Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/pxa_camera.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/pxa_camera.c b/drivers/media/platform/pxa_camera.c index ab1fa911849a..25e538bf0df7 100644 --- a/drivers/media/platform/pxa_camera.c +++ b/drivers/media/platform/pxa_camera.c @@ -1124,7 +1124,7 @@ static u32 mclk_get_divisor(struct platform_device *pdev, /* mclk <= ciclk / 4 (27.4.2) */ if (mclk > lcdclk / 4) { mclk = lcdclk / 4; - dev_warn(pcdev_to_dev(pcdev), + dev_warn(&pdev->dev, "Limiting master clock to %lu\n", mclk); } @@ -1135,7 +1135,7 @@ static u32 mclk_get_divisor(struct platform_device *pdev, if (pcdev->platform_flags & PXA_CAMERA_MCLK_EN) pcdev->mclk = lcdclk / (2 * (div + 1)); - dev_dbg(pcdev_to_dev(pcdev), "LCD clock %luHz, target freq %luHz, divisor %u\n", + dev_dbg(&pdev->dev, "LCD clock %luHz, target freq %luHz, divisor %u\n", lcdclk, mclk, div); return div; -- cgit v1.2.3-58-ga151 From 043f77edae8bd50f238b7afc6bdf49c4db0ed4ac Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 10 May 2017 03:36:56 -0300 Subject: [media] v4l2-ioctl.c: always copy G/S_EDID result The VIDIOC_G/S_EDID ioctls can return valid data even if an error is returned. Mark those ioctls accordingly. Rather than using an explicit 'if' to check for the ioctl (as was done until now for VIDIOC_QUERY_DV_TIMINGS) just set a new flag in the v4l2_ioctls array. Signed-off-by: Hans Verkuil Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ioctl.c | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index e5a2187381db..4f27cfa134a1 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -2472,20 +2472,22 @@ struct v4l2_ioctl_info { }; /* This control needs a priority check */ -#define INFO_FL_PRIO (1 << 0) +#define INFO_FL_PRIO (1 << 0) /* This control can be valid if the filehandle passes a control handler. */ -#define INFO_FL_CTRL (1 << 1) +#define INFO_FL_CTRL (1 << 1) /* This is a standard ioctl, no need for special code */ -#define INFO_FL_STD (1 << 2) +#define INFO_FL_STD (1 << 2) /* This is ioctl has its own function */ -#define INFO_FL_FUNC (1 << 3) +#define INFO_FL_FUNC (1 << 3) /* Queuing ioctl */ -#define INFO_FL_QUEUE (1 << 4) +#define INFO_FL_QUEUE (1 << 4) +/* Always copy back result, even on error */ +#define INFO_FL_ALWAYS_COPY (1 << 5) /* Zero struct from after the field to the end */ #define INFO_FL_CLEAR(v4l2_struct, field) \ ((offsetof(struct v4l2_struct, field) + \ sizeof(((struct v4l2_struct *)0)->field)) << 16) -#define INFO_FL_CLEAR_MASK (_IOC_SIZEMASK << 16) +#define INFO_FL_CLEAR_MASK (_IOC_SIZEMASK << 16) #define IOCTL_INFO_STD(_ioctl, _vidioc, _debug, _flags) \ [_IOC_NR(_ioctl)] = { \ @@ -2536,8 +2538,8 @@ static struct v4l2_ioctl_info v4l2_ioctls[] = { IOCTL_INFO_FNC(VIDIOC_QUERYMENU, v4l_querymenu, v4l_print_querymenu, INFO_FL_CTRL | INFO_FL_CLEAR(v4l2_querymenu, index)), IOCTL_INFO_STD(VIDIOC_G_INPUT, vidioc_g_input, v4l_print_u32, 0), IOCTL_INFO_FNC(VIDIOC_S_INPUT, v4l_s_input, v4l_print_u32, INFO_FL_PRIO), - IOCTL_INFO_STD(VIDIOC_G_EDID, vidioc_g_edid, v4l_print_edid, 0), - IOCTL_INFO_STD(VIDIOC_S_EDID, vidioc_s_edid, v4l_print_edid, INFO_FL_PRIO), + IOCTL_INFO_STD(VIDIOC_G_EDID, vidioc_g_edid, v4l_print_edid, INFO_FL_ALWAYS_COPY), + IOCTL_INFO_STD(VIDIOC_S_EDID, vidioc_s_edid, v4l_print_edid, INFO_FL_PRIO | INFO_FL_ALWAYS_COPY), IOCTL_INFO_STD(VIDIOC_G_OUTPUT, vidioc_g_output, v4l_print_u32, 0), IOCTL_INFO_FNC(VIDIOC_S_OUTPUT, v4l_s_output, v4l_print_u32, INFO_FL_PRIO), IOCTL_INFO_FNC(VIDIOC_ENUMOUTPUT, v4l_enumoutput, v4l_print_enumoutput, INFO_FL_CLEAR(v4l2_output, index)), @@ -2583,7 +2585,7 @@ static struct v4l2_ioctl_info v4l2_ioctls[] = { IOCTL_INFO_FNC(VIDIOC_CREATE_BUFS, v4l_create_bufs, v4l_print_create_buffers, INFO_FL_PRIO | INFO_FL_QUEUE), IOCTL_INFO_FNC(VIDIOC_PREPARE_BUF, v4l_prepare_buf, v4l_print_buffer, INFO_FL_QUEUE), IOCTL_INFO_STD(VIDIOC_ENUM_DV_TIMINGS, vidioc_enum_dv_timings, v4l_print_enum_dv_timings, INFO_FL_CLEAR(v4l2_enum_dv_timings, pad)), - IOCTL_INFO_STD(VIDIOC_QUERY_DV_TIMINGS, vidioc_query_dv_timings, v4l_print_dv_timings, 0), + IOCTL_INFO_STD(VIDIOC_QUERY_DV_TIMINGS, vidioc_query_dv_timings, v4l_print_dv_timings, INFO_FL_ALWAYS_COPY), IOCTL_INFO_STD(VIDIOC_DV_TIMINGS_CAP, vidioc_dv_timings_cap, v4l_print_dv_timings_cap, INFO_FL_CLEAR(v4l2_dv_timings_cap, type)), IOCTL_INFO_FNC(VIDIOC_ENUM_FREQ_BANDS, v4l_enum_freq_bands, v4l_print_freq_band, 0), IOCTL_INFO_FNC(VIDIOC_DBG_G_CHIP_INFO, v4l_dbg_g_chip_info, v4l_print_dbg_chip_info, INFO_FL_CLEAR(v4l2_dbg_chip_info, match)), @@ -2801,6 +2803,7 @@ video_usercopy(struct file *file, unsigned int cmd, unsigned long arg, void *parg = (void *)arg; long err = -EINVAL; bool has_array_args; + bool always_copy = false; size_t array_size = 0; void __user *user_ptr = NULL; void **kernel_ptr = NULL; @@ -2830,8 +2833,10 @@ video_usercopy(struct file *file, unsigned int cmd, unsigned long arg, */ if (v4l2_is_known_ioctl(cmd)) { u32 flags = v4l2_ioctls[_IOC_NR(cmd)].flags; + if (flags & INFO_FL_CLEAR_MASK) n = (flags & INFO_FL_CLEAR_MASK) >> 16; + always_copy = flags & INFO_FL_ALWAYS_COPY; } if (copy_from_user(parg, (void __user *)arg, n)) @@ -2885,9 +2890,11 @@ video_usercopy(struct file *file, unsigned int cmd, unsigned long arg, err = -EFAULT; goto out_array_args; } - /* VIDIOC_QUERY_DV_TIMINGS can return an error, but still have valid - results that must be returned. */ - if (err < 0 && cmd != VIDIOC_QUERY_DV_TIMINGS) + /* + * Some ioctls can return an error, but still have valid + * results that must be returned. + */ + if (err < 0 && !always_copy) goto out; out_array_args: -- cgit v1.2.3-58-ga151 From ab64a58976cd2ec9c479f40bec31c7ce93e1ccc9 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 12 May 2017 07:01:30 -0300 Subject: [media] usbvision: add missing USB-descriptor endianness conversions Add the missing endianness conversions to a debug call printing the USB device-descriptor idVendor and idProduct fields during probe. Signed-off-by: Johan Hovold Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/usbvision/usbvision-video.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/usb/usbvision/usbvision-video.c b/drivers/media/usb/usbvision/usbvision-video.c index f9c3325aa4d4..756322c4ac05 100644 --- a/drivers/media/usb/usbvision/usbvision-video.c +++ b/drivers/media/usb/usbvision/usbvision-video.c @@ -1427,8 +1427,8 @@ static int usbvision_probe(struct usb_interface *intf, int model, i, ret; PDEBUG(DBG_PROBE, "VID=%#04x, PID=%#04x, ifnum=%u", - dev->descriptor.idVendor, - dev->descriptor.idProduct, ifnum); + le16_to_cpu(dev->descriptor.idVendor), + le16_to_cpu(dev->descriptor.idProduct), ifnum); model = devid->driver_info; if (model < 0 || model >= usbvision_device_data_size) { -- cgit v1.2.3-58-ga151 From 2eeb329619a514b2d77a8ebd72f578dbe78bf52d Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Wed, 17 May 2017 18:19:00 -0300 Subject: [media] media: platform: coda: remove variable self assignment Remove variable self assignment. Addresses-Coverity-ID: 1408817 Signed-off-by: Gustavo A. R. Silva Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/coda/coda-common.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/media/platform/coda/coda-common.c b/drivers/media/platform/coda/coda-common.c index d523e990d509..a6699d872157 100644 --- a/drivers/media/platform/coda/coda-common.c +++ b/drivers/media/platform/coda/coda-common.c @@ -612,7 +612,6 @@ static int coda_try_fmt_vid_cap(struct file *file, void *priv, /* The h.264 decoder only returns complete 16x16 macroblocks */ if (codec && codec->src_fourcc == V4L2_PIX_FMT_H264) { - f->fmt.pix.width = f->fmt.pix.width; f->fmt.pix.height = round_up(f->fmt.pix.height, 16); f->fmt.pix.bytesperline = round_up(f->fmt.pix.width, 16); f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * -- cgit v1.2.3-58-ga151 From e201031df9d36a4c58f8380c9def7e6ae57fa1b6 Mon Sep 17 00:00:00 2001 From: Benjamin Gaignard Date: Thu, 18 May 2017 05:45:09 -0300 Subject: [media] cec: stih: allow to use max CEC logical addresses Hardware could support up to 16 logical addresses which is more than needed by CEC specifications. Let use CEC_MAX_LOG_ADDRS instead of limited it on one. stih_cec_adap_log_addr() function was alredy written to support multiple addresses requests. Signed-off-by: Benjamin Gaignard Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/sti/cec/stih-cec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/sti/cec/stih-cec.c b/drivers/media/platform/sti/cec/stih-cec.c index 39ff55145a82..df34a01ebb1d 100644 --- a/drivers/media/platform/sti/cec/stih-cec.c +++ b/drivers/media/platform/sti/cec/stih-cec.c @@ -353,7 +353,7 @@ static int stih_cec_probe(struct platform_device *pdev) cec->adap = cec_allocate_adapter(&sti_cec_adap_ops, cec, CEC_NAME, CEC_CAP_LOG_ADDRS | CEC_CAP_PASSTHROUGH | - CEC_CAP_TRANSMIT, 1); + CEC_CAP_TRANSMIT, CEC_MAX_LOG_ADDRS); ret = PTR_ERR_OR_ZERO(cec->adap); if (ret) return ret; -- cgit v1.2.3-58-ga151 From fd47bc412072563589245aefac372ee4a886cd7d Mon Sep 17 00:00:00 2001 From: Benjamin Gaignard Date: Thu, 18 May 2017 05:45:10 -0300 Subject: [media] cec: stih: fix typos in comments Minor fixes in comments Signed-off-by: Benjamin Gaignard Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/sti/cec/stih-cec.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/sti/cec/stih-cec.c b/drivers/media/platform/sti/cec/stih-cec.c index df34a01ebb1d..6f9f03670b56 100644 --- a/drivers/media/platform/sti/cec/stih-cec.c +++ b/drivers/media/platform/sti/cec/stih-cec.c @@ -1,6 +1,6 @@ /* * STIH4xx CEC driver - * Copyright (C) STMicroelectronic SA 2016 + * Copyright (C) STMicroelectronics SA 2016 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -213,7 +213,8 @@ static int stih_cec_adap_transmit(struct cec_adapter *adap, u8 attempts, for (i = 0; i < msg->len; i++) writeb(msg->msg[i], cec->regs + CEC_TX_DATA_BASE + i); - /* Start transmission, configure hardware to add start and stop bits + /* + * Start transmission, configure hardware to add start and stop bits * Signal free time is handled by the hardware */ writel(CEC_TX_AUTO_SOM_EN | CEC_TX_AUTO_EOM_EN | CEC_TX_START | -- cgit v1.2.3-58-ga151 From ac5c03b33a022b3c7736eb0cb1cc13fcb053ef01 Mon Sep 17 00:00:00 2001 From: Rene Hickersberger Date: Fri, 5 May 2017 11:09:58 -0300 Subject: [media] media: s5p-cec: Fixed spelling mistake Fixed spelling mistake of "successfully" Signed-off-by: Rene Hickersberger Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-cec/s5p_cec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/s5p-cec/s5p_cec.c b/drivers/media/platform/s5p-cec/s5p_cec.c index 664937b61fa4..65a223e578ed 100644 --- a/drivers/media/platform/s5p-cec/s5p_cec.c +++ b/drivers/media/platform/s5p-cec/s5p_cec.c @@ -235,7 +235,7 @@ static int s5p_cec_probe(struct platform_device *pdev) platform_set_drvdata(pdev, cec); pm_runtime_enable(dev); - dev_dbg(dev, "successfuly probed\n"); + dev_dbg(dev, "successfully probed\n"); return 0; err_delete_adapter: -- cgit v1.2.3-58-ga151 From c70b18f72b19a5b09a1ba3fd77b66fa4bf53b52b Mon Sep 17 00:00:00 2001 From: Devin Heitmueller Date: Wed, 19 Apr 2017 20:13:44 -0300 Subject: [media] au8522: don't attempt to configure unsupported VBI slicer Since we don't suppoort sliced VBI with the au0828/au8522, there is no need to configure the au8522 VBI slicer, which because of the coefficients requires a large amount of i2c traffic. Remove the relevant code. Note that this has no effect on raw VBI support, which is currently the only supported way to access VBI on this device. Signed-off-by: Devin Heitmueller Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/au8522_decoder.c | 38 ---------------------------- 1 file changed, 38 deletions(-) diff --git a/drivers/media/dvb-frontends/au8522_decoder.c b/drivers/media/dvb-frontends/au8522_decoder.c index a2e771305008..12a5c2ca7871 100644 --- a/drivers/media/dvb-frontends/au8522_decoder.c +++ b/drivers/media/dvb-frontends/au8522_decoder.c @@ -179,42 +179,6 @@ static inline struct au8522_state *to_state(struct v4l2_subdev *sd) return container_of(sd, struct au8522_state, sd); } -static void setup_vbi(struct au8522_state *state, int aud_input) -{ - int i; - - /* These are set to zero regardless of what mode we're in */ - au8522_writereg(state, AU8522_TVDEC_VBI_CTRL_H_REG017H, 0x00); - au8522_writereg(state, AU8522_TVDEC_VBI_CTRL_L_REG018H, 0x00); - au8522_writereg(state, AU8522_TVDEC_VBI_USER_TOTAL_BITS_REG019H, 0x00); - au8522_writereg(state, AU8522_TVDEC_VBI_USER_TUNIT_H_REG01AH, 0x00); - au8522_writereg(state, AU8522_TVDEC_VBI_USER_TUNIT_L_REG01BH, 0x00); - au8522_writereg(state, AU8522_TVDEC_VBI_USER_THRESH1_REG01CH, 0x00); - au8522_writereg(state, AU8522_TVDEC_VBI_USER_FRAME_PAT2_REG01EH, 0x00); - au8522_writereg(state, AU8522_TVDEC_VBI_USER_FRAME_PAT1_REG01FH, 0x00); - au8522_writereg(state, AU8522_TVDEC_VBI_USER_FRAME_PAT0_REG020H, 0x00); - au8522_writereg(state, AU8522_TVDEC_VBI_USER_FRAME_MASK2_REG021H, - 0x00); - au8522_writereg(state, AU8522_TVDEC_VBI_USER_FRAME_MASK1_REG022H, - 0x00); - au8522_writereg(state, AU8522_TVDEC_VBI_USER_FRAME_MASK0_REG023H, - 0x00); - - /* Setup the VBI registers */ - for (i = 0x30; i < 0x60; i++) - au8522_writereg(state, i, 0x40); - - /* For some reason, every register is 0x40 except register 0x44 - (confirmed via the HVR-950q USB capture) */ - au8522_writereg(state, 0x44, 0x60); - - /* Enable VBI (we always do this regardless of whether the user is - viewing closed caption info) */ - au8522_writereg(state, AU8522_TVDEC_VBI_CTRL_H_REG017H, - AU8522_TVDEC_VBI_CTRL_H_REG017H_CCON); - -} - static void setup_decoder_defaults(struct au8522_state *state, bool is_svideo) { int i; @@ -317,8 +281,6 @@ static void setup_decoder_defaults(struct au8522_state *state, bool is_svideo) AU8522_TOREGAAGC_REG0E5H_CVBS); au8522_writereg(state, AU8522_REG016H, AU8522_REG016H_CVBS); - setup_vbi(state, 0); - if (is_svideo) { /* Despite what the table says, for the HVR-950q we still need to be in CVBS mode for the S-Video input (reason unknown). */ -- cgit v1.2.3-58-ga151 From bb750c7b4cecd6c9d12f41cb71e301141bad8835 Mon Sep 17 00:00:00 2001 From: Devin Heitmueller Date: Wed, 19 Apr 2017 20:13:45 -0300 Subject: [media] au8522: don't touch i2c master registers on au8522 Some stray lines got inserted into the driver when I reverse engineered the I2C traffic (at the time I didn't know what the registers did). It turns up these registers muck with the onboard I2C master, which we don't use since we instead use the I2C gate. Remove the lines which can actually interfere with the operation of the bus. Signed-off-by: Devin Heitmueller Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/au8522_decoder.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/media/dvb-frontends/au8522_decoder.c b/drivers/media/dvb-frontends/au8522_decoder.c index 12a5c2ca7871..78117178f532 100644 --- a/drivers/media/dvb-frontends/au8522_decoder.c +++ b/drivers/media/dvb-frontends/au8522_decoder.c @@ -422,8 +422,6 @@ static void set_audio_input(struct au8522_state *state) au8522_writereg(state, AU8522_AUDIO_VOLUME_L_REG0F2H, 0x00); au8522_writereg(state, AU8522_AUDIO_VOLUME_R_REG0F3H, 0x00); au8522_writereg(state, AU8522_AUDIO_VOLUME_REG0F4H, 0x00); - au8522_writereg(state, AU8522_I2C_CONTROL_REG1_REG091H, 0x80); - au8522_writereg(state, AU8522_I2C_CONTROL_REG0_REG090H, 0x84); msleep(150); au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H, 0x00); msleep(10); -- cgit v1.2.3-58-ga151 From 427de05cf20f6b3bbcc79a6f3511fa13797fbdbe Mon Sep 17 00:00:00 2001 From: Devin Heitmueller Date: Wed, 19 Apr 2017 20:13:46 -0300 Subject: [media] au8522: rework setup of audio routing The original code was based on my reverse engineering of an I2C trace of the Windows driver. Now that I know what the registers actually do, restructure the code a bit, removing some unneeded register programming and fixing the sequencing of operations. This reduces the time it takes to change inputs from 1300ms down to 600ms (as measured by "time v4l2-ctl -i 0") Note this does not address outstanding issues related to the management of the module clocks and power control for the various blocks, which will be done in a separate patch. Signed-off-by: Devin Heitmueller Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/au8522_decoder.c | 29 ++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/drivers/media/dvb-frontends/au8522_decoder.c b/drivers/media/dvb-frontends/au8522_decoder.c index 78117178f532..281b5ac31e34 100644 --- a/drivers/media/dvb-frontends/au8522_decoder.c +++ b/drivers/media/dvb-frontends/au8522_decoder.c @@ -418,28 +418,29 @@ static void set_audio_input(struct au8522_state *state) lpfilter_coef[i].reg_val[0]); } - /* Setup audio */ - au8522_writereg(state, AU8522_AUDIO_VOLUME_L_REG0F2H, 0x00); - au8522_writereg(state, AU8522_AUDIO_VOLUME_R_REG0F3H, 0x00); - au8522_writereg(state, AU8522_AUDIO_VOLUME_REG0F4H, 0x00); - msleep(150); - au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H, 0x00); - msleep(10); - au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H, - AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_CVBS); - msleep(50); + /* Set the volume */ au8522_writereg(state, AU8522_AUDIO_VOLUME_L_REG0F2H, 0x7F); au8522_writereg(state, AU8522_AUDIO_VOLUME_R_REG0F3H, 0x7F); au8522_writereg(state, AU8522_AUDIO_VOLUME_REG0F4H, 0xff); - msleep(80); - au8522_writereg(state, AU8522_AUDIO_VOLUME_L_REG0F2H, 0x7F); - au8522_writereg(state, AU8522_AUDIO_VOLUME_R_REG0F3H, 0x7F); + + /* Not sure what this does */ au8522_writereg(state, AU8522_REG0F9H, AU8522_REG0F9H_AUDIO); + + /* Setup the audio mode to stereo DBX */ au8522_writereg(state, AU8522_AUDIO_MODE_REG0F1H, 0x82); msleep(70); - au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H, 0x09); + + /* Start the audio processing module */ + au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H, 0x9d); + + /* Set the audio frequency to 48 KHz */ au8522_writereg(state, AU8522_AUDIOFREQ_REG606H, 0x03); + + /* Set the I2S parameters (WS, LSB, mode, sample rate */ au8522_writereg(state, AU8522_I2S_CTRL_2_REG112H, 0xc2); + + /* Enable the I2S output */ + au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H, 0x09); } /* ----------------------------------------------------------------------- */ -- cgit v1.2.3-58-ga151 From 678de4b4306982b7b7beb70ea11cf21bd95ccf17 Mon Sep 17 00:00:00 2001 From: Devin Heitmueller Date: Wed, 19 Apr 2017 20:13:47 -0300 Subject: [media] au8522: remove note about VBI not being implemented I got this working a couple of years ago. Remove it from the list of known issues. Signed-off-by: Devin Heitmueller Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/au8522_decoder.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/media/dvb-frontends/au8522_decoder.c b/drivers/media/dvb-frontends/au8522_decoder.c index 281b5ac31e34..5e2164044fc6 100644 --- a/drivers/media/dvb-frontends/au8522_decoder.c +++ b/drivers/media/dvb-frontends/au8522_decoder.c @@ -17,7 +17,6 @@ /* Developer notes: * - * VBI support is not yet working * Enough is implemented here for CVBS and S-Video inputs, but the actual * analog demodulator code isn't implemented (not needed for xc5000 since it * has its own demodulator and outputs CVBS) -- cgit v1.2.3-58-ga151 From d6f79a915f35a2e16e413ed01dd6254ba54500ff Mon Sep 17 00:00:00 2001 From: Devin Heitmueller Date: Wed, 19 Apr 2017 20:13:48 -0300 Subject: [media] au8522: remove leading bit for register writes The leading bit in register values is actually an indicator as to whether to perform a read or write, so remove the bit from the register values, since the au8522_writereg() is now responsible for adding this bit automatically. Signed-off-by: Devin Heitmueller Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/au8522_dig.c | 200 +++++++++++++++---------------- 1 file changed, 100 insertions(+), 100 deletions(-) diff --git a/drivers/media/dvb-frontends/au8522_dig.c b/drivers/media/dvb-frontends/au8522_dig.c index 7ed326e43fc4..d117ddb8e0ef 100644 --- a/drivers/media/dvb-frontends/au8522_dig.c +++ b/drivers/media/dvb-frontends/au8522_dig.c @@ -271,9 +271,9 @@ static int au8522_set_if(struct dvb_frontend *fe, enum au8522_if_freq if_freq) return -EINVAL; } dprintk("%s() %s MHz\n", __func__, ifmhz); - au8522_writereg(state, 0x80b5, r0b5); - au8522_writereg(state, 0x80b6, r0b6); - au8522_writereg(state, 0x80b7, r0b7); + au8522_writereg(state, 0x00b5, r0b5); + au8522_writereg(state, 0x00b6, r0b6); + au8522_writereg(state, 0x00b7, r0b7); return 0; } @@ -283,33 +283,33 @@ static struct { u16 reg; u16 data; } VSB_mod_tab[] = { - { 0x8090, 0x84 }, + { 0x0090, 0x84 }, { 0x4092, 0x11 }, { 0x2005, 0x00 }, - { 0x8091, 0x80 }, - { 0x80a3, 0x0c }, - { 0x80a4, 0xe8 }, - { 0x8081, 0xc4 }, - { 0x80a5, 0x40 }, - { 0x80a7, 0x40 }, - { 0x80a6, 0x67 }, - { 0x8262, 0x20 }, - { 0x821c, 0x30 }, - { 0x80d8, 0x1a }, - { 0x8227, 0xa0 }, - { 0x8121, 0xff }, - { 0x80a8, 0xf0 }, - { 0x80a9, 0x05 }, - { 0x80aa, 0x77 }, - { 0x80ab, 0xf0 }, - { 0x80ac, 0x05 }, - { 0x80ad, 0x77 }, - { 0x80ae, 0x41 }, - { 0x80af, 0x66 }, - { 0x821b, 0xcc }, - { 0x821d, 0x80 }, - { 0x80a4, 0xe8 }, - { 0x8231, 0x13 }, + { 0x0091, 0x80 }, + { 0x00a3, 0x0c }, + { 0x00a4, 0xe8 }, + { 0x0081, 0xc4 }, + { 0x00a5, 0x40 }, + { 0x00a7, 0x40 }, + { 0x00a6, 0x67 }, + { 0x0262, 0x20 }, + { 0x021c, 0x30 }, + { 0x00d8, 0x1a }, + { 0x0227, 0xa0 }, + { 0x0121, 0xff }, + { 0x00a8, 0xf0 }, + { 0x00a9, 0x05 }, + { 0x00aa, 0x77 }, + { 0x00ab, 0xf0 }, + { 0x00ac, 0x05 }, + { 0x00ad, 0x77 }, + { 0x00ae, 0x41 }, + { 0x00af, 0x66 }, + { 0x021b, 0xcc }, + { 0x021d, 0x80 }, + { 0x00a4, 0xe8 }, + { 0x0231, 0x13 }, }; /* QAM64 Modulation table */ @@ -396,78 +396,78 @@ static struct { u16 reg; u16 data; } QAM256_mod_tab[] = { - { 0x80a3, 0x09 }, - { 0x80a4, 0x00 }, - { 0x8081, 0xc4 }, - { 0x80a5, 0x40 }, - { 0x80aa, 0x77 }, - { 0x80ad, 0x77 }, - { 0x80a6, 0x67 }, - { 0x8262, 0x20 }, - { 0x821c, 0x30 }, - { 0x80b8, 0x3e }, - { 0x80b9, 0xf0 }, - { 0x80ba, 0x01 }, - { 0x80bb, 0x18 }, - { 0x80bc, 0x50 }, - { 0x80bd, 0x00 }, - { 0x80be, 0xea }, - { 0x80bf, 0xef }, - { 0x80c0, 0xfc }, - { 0x80c1, 0xbd }, - { 0x80c2, 0x1f }, - { 0x80c3, 0xfc }, - { 0x80c4, 0xdd }, - { 0x80c5, 0xaf }, - { 0x80c6, 0x00 }, - { 0x80c7, 0x38 }, - { 0x80c8, 0x30 }, - { 0x80c9, 0x05 }, - { 0x80ca, 0x4a }, - { 0x80cb, 0xd0 }, - { 0x80cc, 0x01 }, - { 0x80cd, 0xd9 }, - { 0x80ce, 0x6f }, - { 0x80cf, 0xf9 }, - { 0x80d0, 0x70 }, - { 0x80d1, 0xdf }, - { 0x80d2, 0xf7 }, - { 0x80d3, 0xc2 }, - { 0x80d4, 0xdf }, - { 0x80d5, 0x02 }, - { 0x80d6, 0x9a }, - { 0x80d7, 0xd0 }, - { 0x8250, 0x0d }, - { 0x8251, 0xcd }, - { 0x8252, 0xe0 }, - { 0x8253, 0x05 }, - { 0x8254, 0xa7 }, - { 0x8255, 0xff }, - { 0x8256, 0xed }, - { 0x8257, 0x5b }, - { 0x8258, 0xae }, - { 0x8259, 0xe6 }, - { 0x825a, 0x3d }, - { 0x825b, 0x0f }, - { 0x825c, 0x0d }, - { 0x825d, 0xea }, - { 0x825e, 0xf2 }, - { 0x825f, 0x51 }, - { 0x8260, 0xf5 }, - { 0x8261, 0x06 }, - { 0x821a, 0x00 }, - { 0x8546, 0x40 }, - { 0x8210, 0x26 }, - { 0x8211, 0xf6 }, - { 0x8212, 0x84 }, - { 0x8213, 0x02 }, - { 0x8502, 0x01 }, - { 0x8121, 0x04 }, - { 0x8122, 0x04 }, - { 0x852e, 0x10 }, - { 0x80a4, 0xca }, - { 0x80a7, 0x40 }, - { 0x8526, 0x01 }, + { 0x00a3, 0x09 }, + { 0x00a4, 0x00 }, + { 0x0081, 0xc4 }, + { 0x00a5, 0x40 }, + { 0x00aa, 0x77 }, + { 0x00ad, 0x77 }, + { 0x00a6, 0x67 }, + { 0x0262, 0x20 }, + { 0x021c, 0x30 }, + { 0x00b8, 0x3e }, + { 0x00b9, 0xf0 }, + { 0x00ba, 0x01 }, + { 0x00bb, 0x18 }, + { 0x00bc, 0x50 }, + { 0x00bd, 0x00 }, + { 0x00be, 0xea }, + { 0x00bf, 0xef }, + { 0x00c0, 0xfc }, + { 0x00c1, 0xbd }, + { 0x00c2, 0x1f }, + { 0x00c3, 0xfc }, + { 0x00c4, 0xdd }, + { 0x00c5, 0xaf }, + { 0x00c6, 0x00 }, + { 0x00c7, 0x38 }, + { 0x00c8, 0x30 }, + { 0x00c9, 0x05 }, + { 0x00ca, 0x4a }, + { 0x00cb, 0xd0 }, + { 0x00cc, 0x01 }, + { 0x00cd, 0xd9 }, + { 0x00ce, 0x6f }, + { 0x00cf, 0xf9 }, + { 0x00d0, 0x70 }, + { 0x00d1, 0xdf }, + { 0x00d2, 0xf7 }, + { 0x00d3, 0xc2 }, + { 0x00d4, 0xdf }, + { 0x00d5, 0x02 }, + { 0x00d6, 0x9a }, + { 0x00d7, 0xd0 }, + { 0x0250, 0x0d }, + { 0x0251, 0xcd }, + { 0x0252, 0xe0 }, + { 0x0253, 0x05 }, + { 0x0254, 0xa7 }, + { 0x0255, 0xff }, + { 0x0256, 0xed }, + { 0x0257, 0x5b }, + { 0x0258, 0xae }, + { 0x0259, 0xe6 }, + { 0x025a, 0x3d }, + { 0x025b, 0x0f }, + { 0x025c, 0x0d }, + { 0x025d, 0xea }, + { 0x025e, 0xf2 }, + { 0x025f, 0x51 }, + { 0x0260, 0xf5 }, + { 0x0261, 0x06 }, + { 0x021a, 0x00 }, + { 0x0546, 0x40 }, + { 0x0210, 0x26 }, + { 0x0211, 0xf6 }, + { 0x0212, 0x84 }, + { 0x0213, 0x02 }, + { 0x0502, 0x01 }, + { 0x0121, 0x04 }, + { 0x0122, 0x04 }, + { 0x052e, 0x10 }, + { 0x00a4, 0xca }, + { 0x00a7, 0x40 }, + { 0x0526, 0x01 }, }; static struct { -- cgit v1.2.3-58-ga151 From 6211d28eabf40759d7e48fe23ee55e68aa8b9ba7 Mon Sep 17 00:00:00 2001 From: Devin Heitmueller Date: Wed, 19 Apr 2017 20:13:49 -0300 Subject: [media] au8522 Remove 0x4 bit for register reads The second highest bit in the register value is an indicator to do a register read, so remove it since now au8522_regread() inserts the bit automatically. Also remove a stray instance where we were actually trying to write to the I2C status register, which was actually a read. Signed-off-by: Devin Heitmueller Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/au8522_dig.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/drivers/media/dvb-frontends/au8522_dig.c b/drivers/media/dvb-frontends/au8522_dig.c index d117ddb8e0ef..3f3635f5a06a 100644 --- a/drivers/media/dvb-frontends/au8522_dig.c +++ b/drivers/media/dvb-frontends/au8522_dig.c @@ -284,7 +284,6 @@ static struct { u16 data; } VSB_mod_tab[] = { { 0x0090, 0x84 }, - { 0x4092, 0x11 }, { 0x2005, 0x00 }, { 0x0091, 0x80 }, { 0x00a3, 0x0c }, @@ -654,12 +653,12 @@ static int au8522_read_status(struct dvb_frontend *fe, enum fe_status *status) if (state->current_modulation == VSB_8) { dprintk("%s() Checking VSB_8\n", __func__); - reg = au8522_readreg(state, 0x4088); + reg = au8522_readreg(state, 0x0088); if ((reg & 0x03) == 0x03) *status |= FE_HAS_LOCK | FE_HAS_SYNC | FE_HAS_VITERBI; } else { dprintk("%s() Checking QAM\n", __func__); - reg = au8522_readreg(state, 0x4541); + reg = au8522_readreg(state, 0x0541); if (reg & 0x80) *status |= FE_HAS_VITERBI; if (reg & 0x20) @@ -745,17 +744,17 @@ static int au8522_read_snr(struct dvb_frontend *fe, u16 *snr) if (state->current_modulation == QAM_256) ret = au8522_mse2snr_lookup(qam256_mse2snr_tab, ARRAY_SIZE(qam256_mse2snr_tab), - au8522_readreg(state, 0x4522), + au8522_readreg(state, 0x0522), snr); else if (state->current_modulation == QAM_64) ret = au8522_mse2snr_lookup(qam64_mse2snr_tab, ARRAY_SIZE(qam64_mse2snr_tab), - au8522_readreg(state, 0x4522), + au8522_readreg(state, 0x0522), snr); else /* VSB_8 */ ret = au8522_mse2snr_lookup(vsb_mse2snr_tab, ARRAY_SIZE(vsb_mse2snr_tab), - au8522_readreg(state, 0x4311), + au8522_readreg(state, 0x0311), snr); if (state->config.led_cfg) @@ -804,9 +803,9 @@ static int au8522_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) struct au8522_state *state = fe->demodulator_priv; if (state->current_modulation == VSB_8) - *ucblocks = au8522_readreg(state, 0x4087); + *ucblocks = au8522_readreg(state, 0x0087); else - *ucblocks = au8522_readreg(state, 0x4543); + *ucblocks = au8522_readreg(state, 0x0543); return 0; } -- cgit v1.2.3-58-ga151 From b01052ab0924f07a80538974806f9681d8ea74d1 Mon Sep 17 00:00:00 2001 From: Devin Heitmueller Date: Wed, 19 Apr 2017 20:13:50 -0300 Subject: [media] au8522: fix lock detection to be more reliable Only looking at the lock register causes the status to float between locked and not locked when there is no signal. So improve the logic to also examine the state of the FSC PLL, which results in the lock status being consistently reported. Signed-off-by: Devin Heitmueller Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/au8522_decoder.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/media/dvb-frontends/au8522_decoder.c b/drivers/media/dvb-frontends/au8522_decoder.c index 5e2164044fc6..343dc92ef54e 100644 --- a/drivers/media/dvb-frontends/au8522_decoder.c +++ b/drivers/media/dvb-frontends/au8522_decoder.c @@ -623,10 +623,12 @@ static int au8522_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) int val = 0; struct au8522_state *state = to_state(sd); u8 lock_status; + u8 pll_status; /* Interrogate the decoder to see if we are getting a real signal */ lock_status = au8522_readreg(state, 0x00); - if (lock_status == 0xa2) + pll_status = au8522_readreg(state, 0x7e); + if ((lock_status == 0xa2) && (pll_status & 0x10)) vt->signal = 0xffff; else vt->signal = 0x00; -- cgit v1.2.3-58-ga151 From d9928a115ac5b1b0e77f42a13b47a2a0d46d96d3 Mon Sep 17 00:00:00 2001 From: Devin Heitmueller Date: Wed, 19 Apr 2017 20:13:52 -0300 Subject: [media] xc5000: Don't spin waiting for analog lock The xc5000 driver should not be spinning waiting for an analog lock. The ioctl() should be returning immediately and the application is responsible for polling for lock status. This behavior isn't very visible in cases where you tune to a valid channel, since lock is usually achieved much faster than 400ms. However it is highly visible where doing things like changing video standards, which sends tuning request for a frequency that is almost never going to have an actual channel on it. Also fixup the return values to treat zero as success and an actual error code on error (to be consistent with other functions). Note this change has no practical effect at this time as none of the callers inspect the return value. Signed-off-by: Devin Heitmueller Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/tuners/xc5000.c | 26 ++------------------------ 1 file changed, 2 insertions(+), 24 deletions(-) diff --git a/drivers/media/tuners/xc5000.c b/drivers/media/tuners/xc5000.c index afdf0c9903d2..0e7e4fdf9e50 100644 --- a/drivers/media/tuners/xc5000.c +++ b/drivers/media/tuners/xc5000.c @@ -565,38 +565,16 @@ static int xc_get_totalgain(struct xc5000_priv *priv, u16 *totalgain) return xc5000_readreg(priv, XREG_TOTALGAIN, totalgain); } -static u16 wait_for_lock(struct xc5000_priv *priv) -{ - u16 lock_state = 0; - int watch_dog_count = 40; - - while ((lock_state == 0) && (watch_dog_count > 0)) { - xc_get_lock_status(priv, &lock_state); - if (lock_state != 1) { - msleep(5); - watch_dog_count--; - } - } - return lock_state; -} - #define XC_TUNE_ANALOG 0 #define XC_TUNE_DIGITAL 1 static int xc_tune_channel(struct xc5000_priv *priv, u32 freq_hz, int mode) { - int found = 0; - dprintk(1, "%s(%u)\n", __func__, freq_hz); if (xc_set_rf_frequency(priv, freq_hz) != 0) - return 0; - - if (mode == XC_TUNE_ANALOG) { - if (wait_for_lock(priv) == 1) - found = 1; - } + return -EREMOTEIO; - return found; + return 0; } static int xc_set_xtal(struct dvb_frontend *fe) -- cgit v1.2.3-58-ga151 From d7590b5704d6489ec5e6c7f65e90f8126a910b23 Mon Sep 17 00:00:00 2001 From: Devin Heitmueller Date: Wed, 19 Apr 2017 20:13:53 -0300 Subject: [media] au8522: Set the initial modulation We need to set the initial modulation on driver setup, or else any calls to GET_FRONTEND prior to the first SET_FRONTEND call will get back garbage. Signed-off-by: Devin Heitmueller Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/au8522_common.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/dvb-frontends/au8522_common.c b/drivers/media/dvb-frontends/au8522_common.c index cf4ac240a01f..6722838c3707 100644 --- a/drivers/media/dvb-frontends/au8522_common.c +++ b/drivers/media/dvb-frontends/au8522_common.c @@ -234,6 +234,7 @@ int au8522_init(struct dvb_frontend *fe) chip, so that when it gets powered back up it won't think that it is already tuned */ state->current_frequency = 0; + state->current_modulation = VSB_8; au8522_writereg(state, 0xa4, 1 << 5); -- cgit v1.2.3-58-ga151 From 53460c53b7619c14e8564a3ba2c60fc27f303f6b Mon Sep 17 00:00:00 2001 From: Devin Heitmueller Date: Wed, 19 Apr 2017 20:13:55 -0300 Subject: [media] au0828: Add timer to restart TS stream if no data arrives on bulk endpoint For reasons unclear, we intermittently see a case where the tune is successful but the bulk stream fails to deliver any packets. Add a timer to automatically stop/start the data pump if we encounter such a case. Signed-off-by: Devin Heitmueller Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/au0828/au0828-dvb.c | 30 ++++++++++++++++++++++++++++++ drivers/media/usb/au0828/au0828.h | 2 ++ 2 files changed, 32 insertions(+) diff --git a/drivers/media/usb/au0828/au0828-dvb.c b/drivers/media/usb/au0828/au0828-dvb.c index 7e0c9b795e52..34dc7e062471 100644 --- a/drivers/media/usb/au0828/au0828-dvb.c +++ b/drivers/media/usb/au0828/au0828-dvb.c @@ -105,6 +105,15 @@ static struct tda18271_config hauppauge_woodbury_tunerconfig = { static void au0828_restart_dvb_streaming(struct work_struct *work); +static void au0828_bulk_timeout(unsigned long data) +{ + struct au0828_dev *dev = (struct au0828_dev *) data; + + dprintk(1, "%s called\n", __func__); + dev->bulk_timeout_running = 0; + schedule_work(&dev->restart_streaming); +} + /*-------------------------------------------------------------------*/ static void urb_completion(struct urb *purb) { @@ -138,6 +147,13 @@ static void urb_completion(struct urb *purb) ptr[0], purb->actual_length); schedule_work(&dev->restart_streaming); return; + } else if (dev->bulk_timeout_running == 1) { + /* The URB handler has fired, so cancel timer which would + * restart endpoint if we hadn't + */ + dprintk(1, "%s cancelling bulk timeout\n", __func__); + dev->bulk_timeout_running = 0; + del_timer(&dev->bulk_timeout); } /* Feed the transport payload into the kernel demux */ @@ -160,6 +176,11 @@ static int stop_urb_transfer(struct au0828_dev *dev) if (!dev->urb_streaming) return 0; + if (dev->bulk_timeout_running == 1) { + dev->bulk_timeout_running = 0; + del_timer(&dev->bulk_timeout); + } + dev->urb_streaming = false; for (i = 0; i < URB_COUNT; i++) { if (dev->urbs[i]) { @@ -232,6 +253,11 @@ static int start_urb_transfer(struct au0828_dev *dev) } dev->urb_streaming = true; + + /* If we don't valid data within 1 second, restart stream */ + mod_timer(&dev->bulk_timeout, jiffies + (HZ)); + dev->bulk_timeout_running = 1; + return 0; } @@ -622,6 +648,10 @@ int au0828_dvb_register(struct au0828_dev *dev) return ret; } + dev->bulk_timeout.function = au0828_bulk_timeout; + dev->bulk_timeout.data = (unsigned long) dev; + init_timer(&dev->bulk_timeout); + return 0; } diff --git a/drivers/media/usb/au0828/au0828.h b/drivers/media/usb/au0828/au0828.h index 88e59748ebc2..05e445fe0b77 100644 --- a/drivers/media/usb/au0828/au0828.h +++ b/drivers/media/usb/au0828/au0828.h @@ -195,6 +195,8 @@ struct au0828_dev { /* Digital */ struct au0828_dvb dvb; struct work_struct restart_streaming; + struct timer_list bulk_timeout; + int bulk_timeout_running; #ifdef CONFIG_VIDEO_AU0828_V4L2 /* Analog */ -- cgit v1.2.3-58-ga151 From 0f42b331b6f7e9281381cc240d8c3a27cf0fa5ca Mon Sep 17 00:00:00 2001 From: Oleh Kravchenko Date: Sat, 22 Apr 2017 12:47:13 -0300 Subject: [media] cx231xx: Initial support Astrometa T2hybrid This patch provide only digital support; The device is based on 24C02N EEPROM, Panasonic MN88473 demodulator, Rafael Micro R828D tuner and CX23102-11Z chipset; USB id: 15f4:0135. Status: - DVB-T/T2 works fine; - Composite works fine; - Analog not implemented. Signed-off-by: Oleh Kravchenko Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/cx231xx/Kconfig | 2 ++ drivers/media/usb/cx231xx/cx231xx-cards.c | 34 +++++++++++++++++++++ drivers/media/usb/cx231xx/cx231xx-dvb.c | 49 +++++++++++++++++++++++++++++++ drivers/media/usb/cx231xx/cx231xx.h | 1 + 4 files changed, 86 insertions(+) diff --git a/drivers/media/usb/cx231xx/Kconfig b/drivers/media/usb/cx231xx/Kconfig index 58de80bff4c7..6276d9b2198b 100644 --- a/drivers/media/usb/cx231xx/Kconfig +++ b/drivers/media/usb/cx231xx/Kconfig @@ -52,6 +52,8 @@ config VIDEO_CX231XX_DVB select DVB_SI2165 if MEDIA_SUBDRV_AUTOSELECT select DVB_SI2168 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_SI2157 if MEDIA_SUBDRV_AUTOSELECT + select DVB_MN88473 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_R820T if MEDIA_SUBDRV_AUTOSELECT ---help--- This adds support for DVB cards based on the diff --git a/drivers/media/usb/cx231xx/cx231xx-cards.c b/drivers/media/usb/cx231xx/cx231xx-cards.c index a1007d005290..e0daa9b6c2a0 100644 --- a/drivers/media/usb/cx231xx/cx231xx-cards.c +++ b/drivers/media/usb/cx231xx/cx231xx-cards.c @@ -868,6 +868,33 @@ struct cx231xx_board cx231xx_boards[] = { .amux = CX231XX_AMUX_LINE_IN, } }, }, + [CX231XX_BOARD_ASTROMETA_T2HYBRID] = { + .name = "Astrometa T2hybrid", + .tuner_type = TUNER_ABSENT, + .has_dvb = 1, + .output_mode = OUT_MODE_VIP11, + .agc_analog_digital_select_gpio = 0x01, + .ctl_pin_status_mask = 0xffffffc4, + .demod_addr = 0x18, /* 0x30 >> 1 */ + .demod_i2c_master = I2C_1_MUX_1, + .gpio_pin_status_mask = 0xa, + .norm = V4L2_STD_NTSC, + .tuner_addr = 0x3a, /* 0x74 >> 1 */ + .tuner_i2c_master = I2C_1_MUX_3, + .tuner_scl_gpio = 0x1a, + .tuner_sda_gpio = 0x1b, + .tuner_sif_gpio = 0x05, + .input = {{ + .type = CX231XX_VMUX_TELEVISION, + .vmux = CX231XX_VIN_1_1, + .amux = CX231XX_AMUX_VIDEO, + }, { + .type = CX231XX_VMUX_COMPOSITE1, + .vmux = CX231XX_VIN_2_1, + .amux = CX231XX_AMUX_LINE_IN, + }, + }, + }, }; const unsigned int cx231xx_bcount = ARRAY_SIZE(cx231xx_boards); @@ -937,6 +964,8 @@ struct usb_device_id cx231xx_id_table[] = { .driver_info = CX231XX_BOARD_TERRATEC_GRABBY}, {USB_DEVICE(0x1b80, 0xd3b2), .driver_info = CX231XX_BOARD_EVROMEDIA_FULL_HYBRID_FULLHD}, + {USB_DEVICE(0x15f4, 0x0135), + .driver_info = CX231XX_BOARD_ASTROMETA_T2HYBRID}, {}, }; @@ -1013,6 +1042,11 @@ void cx231xx_pre_card_setup(struct cx231xx *dev) dev_info(dev->dev, "Identified as %s (card=%d)\n", dev->board.name, dev->model); + if (CX231XX_BOARD_ASTROMETA_T2HYBRID == dev->model) { + /* turn on demodulator chip */ + cx231xx_set_gpio_value(dev, 0x03, 0x01); + } + /* set the direction for GPIO pins */ if (dev->board.tuner_gpio) { cx231xx_set_gpio_direction(dev, dev->board.tuner_gpio->bit, 1); diff --git a/drivers/media/usb/cx231xx/cx231xx-dvb.c b/drivers/media/usb/cx231xx/cx231xx-dvb.c index 46427fd3b220..ee3eeeb600f8 100644 --- a/drivers/media/usb/cx231xx/cx231xx-dvb.c +++ b/drivers/media/usb/cx231xx/cx231xx-dvb.c @@ -37,6 +37,8 @@ #include "mb86a20s.h" #include "si2157.h" #include "lgdt3306a.h" +#include "r820t.h" +#include "mn88473.h" MODULE_DESCRIPTION("driver for cx231xx based DVB cards"); MODULE_AUTHOR("Srinivasa Deevi "); @@ -164,6 +166,13 @@ static struct lgdt3306a_config hauppauge_955q_lgdt3306a_config = { .xtalMHz = 25, }; +static struct r820t_config astrometa_t2hybrid_r820t_config = { + .i2c_addr = 0x3a, /* 0x74 >> 1 */ + .xtal = 16000000, + .rafael_chip = CHIP_R828D, + .max_i2c_msg_len = 2, +}; + static inline void print_err_status(struct cx231xx *dev, int packet, int status) { char *errmsg = "Unknown"; @@ -1019,6 +1028,46 @@ static int dvb_init(struct cx231xx *dev) dev->dvb->i2c_client_tuner = client; break; } + case CX231XX_BOARD_ASTROMETA_T2HYBRID: + { + struct i2c_client *client; + struct i2c_board_info info = {}; + struct mn88473_config mn88473_config = {}; + + /* attach demodulator chip */ + mn88473_config.i2c_wr_max = 16; + mn88473_config.xtal = 25000000; + mn88473_config.fe = &dev->dvb->frontend; + + strlcpy(info.type, "mn88473", sizeof(info.type)); + info.addr = dev->board.demod_addr; + info.platform_data = &mn88473_config; + + request_module(info.type); + client = i2c_new_device(demod_i2c, &info); + + if (client == NULL || client->dev.driver == NULL) { + result = -ENODEV; + goto out_free; + } + + if (!try_module_get(client->dev.driver->owner)) { + i2c_unregister_device(client); + result = -ENODEV; + goto out_free; + } + + dvb->i2c_client_demod = client; + + /* define general-purpose callback pointer */ + dvb->frontend->callback = cx231xx_tuner_callback; + + /* attach tuner chip */ + dvb_attach(r820t_attach, dev->dvb->frontend, + tuner_i2c, + &astrometa_t2hybrid_r820t_config); + break; + } default: dev_err(dev->dev, "%s/2: The frontend of your DVB/ATSC card isn't supported yet\n", diff --git a/drivers/media/usb/cx231xx/cx231xx.h b/drivers/media/usb/cx231xx/cx231xx.h index d9792ea4bbc6..986c64ba5b56 100644 --- a/drivers/media/usb/cx231xx/cx231xx.h +++ b/drivers/media/usb/cx231xx/cx231xx.h @@ -79,6 +79,7 @@ #define CX231XX_BOARD_HAUPPAUGE_955Q 21 #define CX231XX_BOARD_TERRATEC_GRABBY 22 #define CX231XX_BOARD_EVROMEDIA_FULL_HYBRID_FULLHD 23 +#define CX231XX_BOARD_ASTROMETA_T2HYBRID 24 /* Limits minimum and default number of buffers */ #define CX231XX_MIN_BUF 4 -- cgit v1.2.3-58-ga151 From 834ebbc8f3a062f18131ff7ad7ddf27dc947e4c2 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Wed, 15 Mar 2017 08:31:35 -0300 Subject: [media] coda: simplify optional reset handling As of commit bb475230b8e5 ("reset: make optional functions really optional"), the reset framework API calls use NULL pointers to describe optional, non-present reset controls. This allows to return errors from devm_reset_control_get_optional without special cases and to call reset_control_reset unconditionally. Signed-off-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/coda/coda-common.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/drivers/media/platform/coda/coda-common.c b/drivers/media/platform/coda/coda-common.c index a6699d872157..0e8e50caa8ff 100644 --- a/drivers/media/platform/coda/coda-common.c +++ b/drivers/media/platform/coda/coda-common.c @@ -2062,8 +2062,7 @@ static int coda_hw_init(struct coda_dev *dev) if (ret) goto err_clk_ahb; - if (dev->rstc) - reset_control_reset(dev->rstc); + reset_control_reset(dev->rstc); /* * Copy the first CODA_ISRAM_SIZE in the internal SRAM. @@ -2447,13 +2446,8 @@ static int coda_probe(struct platform_device *pdev) dev->rstc = devm_reset_control_get_optional(&pdev->dev, NULL); if (IS_ERR(dev->rstc)) { ret = PTR_ERR(dev->rstc); - if (ret == -ENOENT || ret == -ENOTSUPP) { - dev->rstc = NULL; - } else { - dev_err(&pdev->dev, "failed get reset control: %d\n", - ret); - return ret; - } + dev_err(&pdev->dev, "failed get reset control: %d\n", ret); + return ret; } /* Get IRAM pool from device tree or platform data */ -- cgit v1.2.3-58-ga151 From e1082d28721081858a51f6c0148a3c7603d9e65e Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Wed, 3 May 2017 21:13:18 -0300 Subject: [media] media: i2c: initialize scalar variables Initialize scalar variables _pid_ and _ver_ to avoid a possible misbehavior. Addresses-Coverity-ID: 1324239 Addresses-Coverity-ID: 1324240 Signed-off-by: Gustavo A. R. Silva Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov2659.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/i2c/ov2659.c b/drivers/media/i2c/ov2659.c index 6e6367214d40..58615f836fb7 100644 --- a/drivers/media/i2c/ov2659.c +++ b/drivers/media/i2c/ov2659.c @@ -1308,7 +1308,8 @@ static const struct v4l2_subdev_internal_ops ov2659_subdev_internal_ops = { static int ov2659_detect(struct v4l2_subdev *sd) { struct i2c_client *client = v4l2_get_subdevdata(sd); - u8 pid, ver; + u8 pid = 0; + u8 ver = 0; int ret; dev_dbg(&client->dev, "%s:\n", __func__); -- cgit v1.2.3-58-ga151 From 1656df35d689ac6a93d7503725d9e62ce50c7f38 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Mon, 22 May 2017 05:13:25 -0300 Subject: [media] em28xx: fix spelling mistake: "missdetected" -> "misdetected" Trivial fix to spelling mistake in dev_err message Signed-off-by: Colin Ian King Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-cards.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index a12b599a1fa2..146341aeb782 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -2855,7 +2855,7 @@ static int em28xx_hint_board(struct em28xx *dev) "Your board has no unique USB ID.\n" "A hint were successfully done, based on eeprom hash.\n" "This method is not 100%% failproof.\n" - "If the board were missdetected, please email this log to:\n" + "If the board were misdetected, please email this log to:\n" "\tV4L Mailing List \n" "Board detected as %s\n", em28xx_boards[dev->model].name); @@ -2885,7 +2885,7 @@ static int em28xx_hint_board(struct em28xx *dev) "Your board has no unique USB ID.\n" "A hint were successfully done, based on i2c devicelist hash.\n" "This method is not 100%% failproof.\n" - "If the board were missdetected, please email this log to:\n" + "If the board were misdetected, please email this log to:\n" "\tV4L Mailing List \n" "Board detected as %s\n", em28xx_boards[dev->model].name); -- cgit v1.2.3-58-ga151 From 8e175b22e8640bf3a58e071af54190b909e4a944 Mon Sep 17 00:00:00 2001 From: A Sun Date: Sun, 26 Mar 2017 15:33:07 -0300 Subject: [media] mceusb: sporadic RX truncation corruption fix Intermittent RX truncation and loss of IR received data. This resulted in receive stream synchronization errors where driver attempted to incorrectly parse IR data (eg 0x90 below) as command response. [ 3969.139898] mceusb 1-1.2:1.0: processed IR data [ 3969.151315] mceusb 1-1.2:1.0: rx data: 00 90 (length=2) [ 3969.151321] mceusb 1-1.2:1.0: Unknown command 0x00 0x90 [ 3969.151336] mceusb 1-1.2:1.0: rx data: 98 0a 8d 0a 8e 0a 8e 0a 8e 0a 8e 0a 9a 0a 8e 0a 0b 3a 8e 00 80 41 59 00 00 (length=25) [ 3969.151341] mceusb 1-1.2:1.0: Raw IR data, 24 pulse/space samples [ 3969.151348] mceusb 1-1.2:1.0: Storing space with duration 500000 Bug trigger appears to be normal, but heavy, IR receiver use. Signed-off-by: A Sun Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/mceusb.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/media/rc/mceusb.c b/drivers/media/rc/mceusb.c index 93b16fe3ab38..e05fed2aaaa9 100644 --- a/drivers/media/rc/mceusb.c +++ b/drivers/media/rc/mceusb.c @@ -1337,8 +1337,13 @@ static int mceusb_dev_probe(struct usb_interface *intf, goto rc_dev_fail; /* wire up inbound data handler */ - usb_fill_int_urb(ir->urb_in, dev, pipe, ir->buf_in, maxp, - mceusb_dev_recv, ir, ep_in->bInterval); + if (usb_endpoint_xfer_int(ep_in)) + usb_fill_int_urb(ir->urb_in, dev, pipe, ir->buf_in, maxp, + mceusb_dev_recv, ir, ep_in->bInterval); + else + usb_fill_bulk_urb(ir->urb_in, dev, pipe, ir->buf_in, maxp, + mceusb_dev_recv, ir); + ir->urb_in->transfer_dma = ir->dma_in; ir->urb_in->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; -- cgit v1.2.3-58-ga151 From 5c80992e8ff8acf13048559d431227d1e10541a1 Mon Sep 17 00:00:00 2001 From: A Sun Date: Sun, 26 Mar 2017 16:04:51 -0300 Subject: [media] mceusb: fix inaccurate debug buffer dumps, and misleading debug messages Some dev_dbg messages are misleading. Some dev_dbg messages have inconsistent formatting. mceusb_dev_printdata() prints incorrect range of bytes (0 to len) in buffer which the driver will actually process next. Signed-off-by: A Sun Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/mceusb.c | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/drivers/media/rc/mceusb.c b/drivers/media/rc/mceusb.c index e05fed2aaaa9..17e75cd8399a 100644 --- a/drivers/media/rc/mceusb.c +++ b/drivers/media/rc/mceusb.c @@ -47,7 +47,6 @@ "device driver" #define DRIVER_NAME "mceusb" -#define USB_BUFLEN 32 /* USB reception buffer length */ #define USB_CTRL_MSG_SZ 2 /* Size of usb ctrl msg on gen1 hw */ #define MCE_G1_INIT_MSGS 40 /* Init messages on gen1 hw to throw out */ @@ -527,7 +526,7 @@ static int mceusb_cmd_datasize(u8 cmd, u8 subcmd) } static void mceusb_dev_printdata(struct mceusb_dev *ir, char *buf, - int offset, int len, bool out) + int buf_len, int offset, int len, bool out) { #if defined(DEBUG) || defined(CONFIG_DYNAMIC_DEBUG) char *inout; @@ -544,7 +543,8 @@ static void mceusb_dev_printdata(struct mceusb_dev *ir, char *buf, return; dev_dbg(dev, "%cx data: %*ph (length=%d)", - (out ? 't' : 'r'), min(len, USB_BUFLEN), buf, len); + (out ? 't' : 'r'), + min(len, buf_len - offset), buf + offset, len); inout = out ? "Request" : "Got"; @@ -701,7 +701,8 @@ static void mce_async_callback(struct urb *urb) case 0: len = urb->actual_length; - mceusb_dev_printdata(ir, urb->transfer_buffer, 0, len, true); + mceusb_dev_printdata(ir, urb->transfer_buffer, len, + 0, len, true); break; case -ECONNRESET: @@ -721,7 +722,7 @@ static void mce_async_callback(struct urb *urb) usb_free_urb(urb); } -/* request incoming or send outgoing usb packet - used to initialize remote */ +/* request outgoing (send) usb packet - used to initialize remote */ static void mce_request_packet(struct mceusb_dev *ir, unsigned char *data, int size) { @@ -732,7 +733,7 @@ static void mce_request_packet(struct mceusb_dev *ir, unsigned char *data, async_urb = usb_alloc_urb(0, GFP_KERNEL); if (unlikely(!async_urb)) { - dev_err(dev, "Error, couldn't allocate urb!\n"); + dev_err(dev, "Error, couldn't allocate urb!"); return; } @@ -758,17 +759,17 @@ static void mce_request_packet(struct mceusb_dev *ir, unsigned char *data, } memcpy(async_buf, data, size); - dev_dbg(dev, "receive request called (size=%#x)", size); + dev_dbg(dev, "send request called (size=%#x)", size); async_urb->transfer_buffer_length = size; async_urb->dev = ir->usbdev; res = usb_submit_urb(async_urb, GFP_ATOMIC); if (res) { - dev_err(dev, "receive request FAILED! (res=%d)", res); + dev_err(dev, "send request FAILED! (res=%d)", res); return; } - dev_dbg(dev, "receive request complete (res=%d)", res); + dev_dbg(dev, "send request complete (res=%d)", res); } static void mce_async_out(struct mceusb_dev *ir, unsigned char *data, int size) @@ -974,7 +975,7 @@ static void mceusb_process_ir_data(struct mceusb_dev *ir, int buf_len) switch (ir->parser_state) { case SUBCMD: ir->rem = mceusb_cmd_datasize(ir->cmd, ir->buf_in[i]); - mceusb_dev_printdata(ir, ir->buf_in, i - 1, + mceusb_dev_printdata(ir, ir->buf_in, buf_len, i - 1, ir->rem + 2, false); mceusb_handle_command(ir, i); ir->parser_state = CMD_DATA; @@ -986,7 +987,7 @@ static void mceusb_process_ir_data(struct mceusb_dev *ir, int buf_len) rawir.duration = (ir->buf_in[i] & MCE_PULSE_MASK) * US_TO_NS(MCE_TIME_UNIT); - dev_dbg(ir->dev, "Storing %s with duration %d", + dev_dbg(ir->dev, "Storing %s with duration %u", rawir.pulse ? "pulse" : "space", rawir.duration); @@ -1007,7 +1008,7 @@ static void mceusb_process_ir_data(struct mceusb_dev *ir, int buf_len) continue; } ir->rem = (ir->cmd & MCE_PACKET_LENGTH_MASK); - mceusb_dev_printdata(ir, ir->buf_in, + mceusb_dev_printdata(ir, ir->buf_in, buf_len, i, ir->rem + 1, false); if (ir->rem) ir->parser_state = PARSE_IRDATA; @@ -1348,10 +1349,10 @@ static int mceusb_dev_probe(struct usb_interface *intf, ir->urb_in->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; /* flush buffers on the device */ - dev_dbg(&intf->dev, "Flushing receive buffers\n"); + dev_dbg(&intf->dev, "Flushing receive buffers"); res = usb_submit_urb(ir->urb_in, GFP_KERNEL); if (res) - dev_err(&intf->dev, "failed to flush buffers: %d\n", res); + dev_err(&intf->dev, "failed to flush buffers: %d", res); /* figure out which firmware/emulator version this hardware has */ mceusb_get_emulator_version(ir); -- cgit v1.2.3-58-ga151 From 956bd18a2769bbdc979c6917efa8ad4600f50d0e Mon Sep 17 00:00:00 2001 From: Andi Shyti Date: Mon, 27 Mar 2017 10:34:35 -0300 Subject: [media] rc: ir-spi: remove unnecessary initialization Signed-off-by: Andi Shyti Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/ir-spi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/rc/ir-spi.c b/drivers/media/rc/ir-spi.c index c8863f36686a..4ca43383a8e8 100644 --- a/drivers/media/rc/ir-spi.c +++ b/drivers/media/rc/ir-spi.c @@ -58,7 +58,7 @@ static int ir_spi_tx(struct rc_dev *dev, /* convert the pulse/space signal to raw binary signal */ for (i = 0; i < count; i++) { int j; - u16 val = ((i + 1) % 2) ? idata->pulse : idata->space; + u16 val; if (len + buffer[i] >= IR_SPI_MAX_BUFSIZE) return -EINVAL; -- cgit v1.2.3-58-ga151 From 1ffc931c320a8ac5e106eac2aecaa1a4b4b2bea0 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Wed, 12 Apr 2017 16:28:42 -0300 Subject: [media] rc: meson-ir: remove irq from struct meson_ir The irq number is used in the probe function only, therefore just use a local variable. Signed-off-by: Heiner Kallweit Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/meson-ir.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/media/rc/meson-ir.c b/drivers/media/rc/meson-ir.c index 5576dbd6b1a4..a4128d7cb600 100644 --- a/drivers/media/rc/meson-ir.c +++ b/drivers/media/rc/meson-ir.c @@ -68,7 +68,6 @@ struct meson_ir { void __iomem *reg; struct rc_dev *rc; - int irq; spinlock_t lock; }; @@ -112,7 +111,7 @@ static int meson_ir_probe(struct platform_device *pdev) struct resource *res; const char *map_name; struct meson_ir *ir; - int ret; + int irq, ret; ir = devm_kzalloc(dev, sizeof(struct meson_ir), GFP_KERNEL); if (!ir) @@ -125,10 +124,10 @@ static int meson_ir_probe(struct platform_device *pdev) return PTR_ERR(ir->reg); } - ir->irq = platform_get_irq(pdev, 0); - if (ir->irq < 0) { + irq = platform_get_irq(pdev, 0); + if (irq < 0) { dev_err(dev, "no irq resource\n"); - return ir->irq; + return irq; } ir->rc = rc_allocate_device(RC_DRIVER_IR_RAW); @@ -158,7 +157,7 @@ static int meson_ir_probe(struct platform_device *pdev) goto out_free; } - ret = devm_request_irq(dev, ir->irq, meson_ir_irq, 0, "ir-meson", ir); + ret = devm_request_irq(dev, irq, meson_ir_irq, 0, "ir-meson", ir); if (ret) { dev_err(dev, "failed to request irq\n"); goto out_unreg; -- cgit v1.2.3-58-ga151 From e7a937b5c4142c626766d00558ea4b761a00edaf Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Wed, 12 Apr 2017 16:30:48 -0300 Subject: [media] rc: meson-ir: make use of the bitfield macros Make use of the bitfield macros thus partially hiding the complexity of dealing with bitfields. The patch also includes a minor fix to REG0_RATE_MASK, so far it was set to bit 0..10, but according to the spec it's bit 0..11. [mchehab@s-opensource.com: readd REG1_MODE_SHIFT and REG2_MODE_SHIFT that got removed on the original patch, as this will be used on another patch] Signed-off-by: Heiner Kallweit Reviewed-by: Neil Armstrong Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/meson-ir.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/media/rc/meson-ir.c b/drivers/media/rc/meson-ir.c index a4128d7cb600..faa9076e89fa 100644 --- a/drivers/media/rc/meson-ir.c +++ b/drivers/media/rc/meson-ir.c @@ -19,6 +19,7 @@ #include #include #include +#include #include @@ -36,7 +37,7 @@ /* only available on Meson 8b and newer */ #define IR_DEC_REG2 0x20 -#define REG0_RATE_MASK (BIT(11) - 1) +#define REG0_RATE_MASK GENMASK(11, 0) #define DECODE_MODE_NEC 0x0 #define DECODE_MODE_RAW 0x2 @@ -49,14 +50,13 @@ #define REG2_MODE_MASK GENMASK(3, 0) #define REG2_MODE_SHIFT 0 -#define REG1_TIME_IV_SHIFT 16 -#define REG1_TIME_IV_MASK ((BIT(13) - 1) << REG1_TIME_IV_SHIFT) +#define REG1_TIME_IV_MASK GENMASK(28, 16) -#define REG1_IRQSEL_MASK (BIT(2) | BIT(3)) -#define REG1_IRQSEL_NEC_MODE (0 << 2) -#define REG1_IRQSEL_RISE_FALL (1 << 2) -#define REG1_IRQSEL_FALL (2 << 2) -#define REG1_IRQSEL_RISE (3 << 2) +#define REG1_IRQSEL_MASK GENMASK(3, 2) +#define REG1_IRQSEL_NEC_MODE 0 +#define REG1_IRQSEL_RISE_FALL 1 +#define REG1_IRQSEL_FALL 2 +#define REG1_IRQSEL_RISE 3 #define REG1_RESET BIT(0) #define REG1_ENABLE BIT(15) @@ -91,7 +91,7 @@ static irqreturn_t meson_ir_irq(int irqno, void *dev_id) spin_lock(&ir->lock); duration = readl(ir->reg + IR_DEC_REG1); - duration = (duration & REG1_TIME_IV_MASK) >> REG1_TIME_IV_SHIFT; + duration = FIELD_GET(REG1_TIME_IV_MASK, duration); rawir.duration = US_TO_NS(duration * MESON_TRATE); rawir.pulse = !!(readl(ir->reg + IR_DEC_STATUS) & STATUS_IR_DEC_IN); @@ -170,16 +170,16 @@ static int meson_ir_probe(struct platform_device *pdev) /* Set general operation mode (= raw/software decoding) */ if (of_device_is_compatible(node, "amlogic,meson6-ir")) meson_ir_set_mask(ir, IR_DEC_REG1, REG1_MODE_MASK, - DECODE_MODE_RAW << REG1_MODE_SHIFT); + FIELD_PREP(REG1_MODE_MASK, DECODE_MODE_RAW)); else meson_ir_set_mask(ir, IR_DEC_REG2, REG2_MODE_MASK, - DECODE_MODE_RAW << REG2_MODE_SHIFT); + FIELD_PREP(REG2_MODE_MASK, DECODE_MODE_RAW)); /* Set rate */ meson_ir_set_mask(ir, IR_DEC_REG0, REG0_RATE_MASK, MESON_TRATE - 1); /* IRQ on rising and falling edges */ meson_ir_set_mask(ir, IR_DEC_REG1, REG1_IRQSEL_MASK, - REG1_IRQSEL_RISE_FALL); + FIELD_PREP(REG1_IRQSEL_MASK, REG1_IRQSEL_RISE_FALL)); /* Enable the decoder */ meson_ir_set_mask(ir, IR_DEC_REG1, REG1_ENABLE, REG1_ENABLE); -- cgit v1.2.3-58-ga151 From 705aa578d408f669cd28d9de076ff14bc659371c Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Wed, 12 Apr 2017 16:32:35 -0300 Subject: [media] rc: meson-ir: switch to managed rc device allocation / registration Switch to the managed versions of rc_allocate_device/rc_register_device, thus simplifying the code. Signed-off-by: Heiner Kallweit Reviewed-by: Neil Armstrong Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/meson-ir.c | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/drivers/media/rc/meson-ir.c b/drivers/media/rc/meson-ir.c index faa9076e89fa..664c3efe1f8f 100644 --- a/drivers/media/rc/meson-ir.c +++ b/drivers/media/rc/meson-ir.c @@ -130,7 +130,7 @@ static int meson_ir_probe(struct platform_device *pdev) return irq; } - ir->rc = rc_allocate_device(RC_DRIVER_IR_RAW); + ir->rc = devm_rc_allocate_device(dev, RC_DRIVER_IR_RAW); if (!ir->rc) { dev_err(dev, "failed to allocate rc device\n"); return -ENOMEM; @@ -142,7 +142,6 @@ static int meson_ir_probe(struct platform_device *pdev) ir->rc->input_id.bustype = BUS_HOST; map_name = of_get_property(node, "linux,rc-map-name", NULL); ir->rc->map_name = map_name ? map_name : RC_MAP_EMPTY; - ir->rc->dev.parent = dev; ir->rc->allowed_protocols = RC_BIT_ALL_IR_DECODER; ir->rc->rx_resolution = US_TO_NS(MESON_TRATE); ir->rc->timeout = MS_TO_NS(200); @@ -151,16 +150,16 @@ static int meson_ir_probe(struct platform_device *pdev) spin_lock_init(&ir->lock); platform_set_drvdata(pdev, ir); - ret = rc_register_device(ir->rc); + ret = devm_rc_register_device(dev, ir->rc); if (ret) { dev_err(dev, "failed to register rc device\n"); - goto out_free; + return ret; } ret = devm_request_irq(dev, irq, meson_ir_irq, 0, "ir-meson", ir); if (ret) { dev_err(dev, "failed to request irq\n"); - goto out_unreg; + return ret; } /* Reset the decoder */ @@ -186,13 +185,6 @@ static int meson_ir_probe(struct platform_device *pdev) dev_info(dev, "receiver initialized\n"); return 0; -out_unreg: - rc_unregister_device(ir->rc); - ir->rc = NULL; -out_free: - rc_free_device(ir->rc); - - return ret; } static int meson_ir_remove(struct platform_device *pdev) @@ -205,8 +197,6 @@ static int meson_ir_remove(struct platform_device *pdev) meson_ir_set_mask(ir, IR_DEC_REG1, REG1_ENABLE, 0); spin_unlock_irqrestore(&ir->lock, flags); - rc_unregister_device(ir->rc); - return 0; } -- cgit v1.2.3-58-ga151 From 137edc02e7f005ca2a7060b41968aa46a3b7b048 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Wed, 12 Apr 2017 16:33:57 -0300 Subject: [media] rc: meson-ir: use readl_relaxed in the interrupt handler We don't need the memory barriers here and an interrupt handler should be as fast as possible. Therefore switch to readl_relaxed. Signed-off-by: Heiner Kallweit Reviewed-by: Neil Armstrong Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/meson-ir.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/media/rc/meson-ir.c b/drivers/media/rc/meson-ir.c index 664c3efe1f8f..dfe9184e996c 100644 --- a/drivers/media/rc/meson-ir.c +++ b/drivers/media/rc/meson-ir.c @@ -85,16 +85,17 @@ static void meson_ir_set_mask(struct meson_ir *ir, unsigned int reg, static irqreturn_t meson_ir_irq(int irqno, void *dev_id) { struct meson_ir *ir = dev_id; - u32 duration; + u32 duration, status; DEFINE_IR_RAW_EVENT(rawir); spin_lock(&ir->lock); - duration = readl(ir->reg + IR_DEC_REG1); + duration = readl_relaxed(ir->reg + IR_DEC_REG1); duration = FIELD_GET(REG1_TIME_IV_MASK, duration); rawir.duration = US_TO_NS(duration * MESON_TRATE); - rawir.pulse = !!(readl(ir->reg + IR_DEC_STATUS) & STATUS_IR_DEC_IN); + status = readl_relaxed(ir->reg + IR_DEC_STATUS); + rawir.pulse = !!(status & STATUS_IR_DEC_IN); ir_raw_event_store_with_filter(ir->rc, &rawir); ir_raw_event_handle(ir->rc); -- cgit v1.2.3-58-ga151 From 611ee552a538b0c999b1bee0b7b142d69ebfd460 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Wed, 12 Apr 2017 16:34:50 -0300 Subject: [media] rc: meson-ir: change irq name to to of node name Switch the interrupt description to the default which is the of node name. This is more in line with the interrupt descriptions in other meson drivers. Signed-off-by: Heiner Kallweit Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/meson-ir.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/rc/meson-ir.c b/drivers/media/rc/meson-ir.c index dfe9184e996c..10c9d9bed53b 100644 --- a/drivers/media/rc/meson-ir.c +++ b/drivers/media/rc/meson-ir.c @@ -157,7 +157,7 @@ static int meson_ir_probe(struct platform_device *pdev) return ret; } - ret = devm_request_irq(dev, irq, meson_ir_irq, 0, "ir-meson", ir); + ret = devm_request_irq(dev, irq, meson_ir_irq, 0, NULL, ir); if (ret) { dev_err(dev, "failed to request irq\n"); return ret; -- cgit v1.2.3-58-ga151 From 842e39c0988c4ab4ce42dce512990e87f3eb97bc Mon Sep 17 00:00:00 2001 From: Devin Heitmueller Date: Wed, 19 Apr 2017 20:13:54 -0300 Subject: [media] rc: fix breakage in "make menuconfig" for media_build The Kconfig format is strict enough where if the indentation isn't correct then the "make menuconfig" will break. Fix the indentation to match all the other entries. Signed-off-by: Devin Heitmueller Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/Kconfig | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/media/rc/Kconfig b/drivers/media/rc/Kconfig index e422f3d56f76..5e83b76495f7 100644 --- a/drivers/media/rc/Kconfig +++ b/drivers/media/rc/Kconfig @@ -169,11 +169,11 @@ config IR_HIX5HD2 tristate "Hisilicon hix5hd2 IR remote control" depends on RC_CORE help - Say Y here if you want to use hisilicon hix5hd2 remote control. - To compile this driver as a module, choose M here: the module will be - called ir-hix5hd2. + Say Y here if you want to use hisilicon hix5hd2 remote control. + To compile this driver as a module, choose M here: the module will be + called ir-hix5hd2. - If you're not sure, select N here + If you're not sure, select N here config IR_IMON tristate "SoundGraph iMON Receiver and Display" -- cgit v1.2.3-58-ga151 From 48b0a69126dcc8659c2301eb41729445f444e36b Mon Sep 17 00:00:00 2001 From: Jonas Karlman Date: Tue, 25 Apr 2017 04:40:48 -0300 Subject: [media] rc: meson-ir: store raw event without processing This patch fixes meson-it driver by storing event without processing to avoid losing key pressed events when system is loaded and events are occurring too fast. This issue was reported at [1] [1] https://github.com/LibreELEC/linux-amlogic/pull/42 Signed-off-by: Jonas Karlman Signed-off-by: Neil Armstrong Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/meson-ir.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/rc/meson-ir.c b/drivers/media/rc/meson-ir.c index 10c9d9bed53b..3375b4e18764 100644 --- a/drivers/media/rc/meson-ir.c +++ b/drivers/media/rc/meson-ir.c @@ -97,7 +97,7 @@ static irqreturn_t meson_ir_irq(int irqno, void *dev_id) status = readl_relaxed(ir->reg + IR_DEC_STATUS); rawir.pulse = !!(status & STATUS_IR_DEC_IN); - ir_raw_event_store_with_filter(ir->rc, &rawir); + ir_raw_event_store(ir->rc, &rawir); ir_raw_event_handle(ir->rc); spin_unlock(&ir->lock); -- cgit v1.2.3-58-ga151 From 2aa1bd1c1ca82f57f3e6967d34c8c985dcc22f48 Mon Sep 17 00:00:00 2001 From: Alex Deryskyba Date: Tue, 25 Apr 2017 04:41:09 -0300 Subject: [media] rc: meson-ir: switch config to NEC decoding on shutdown On the Amlogic SoCs, the bootloader firmware can handle the IR hardware in order to Wake up or Power back the system when in suspend on shutdown mode. This patch switches the hardware configuration in a state usable by the firmware to permit powering the system back. Some vendor bootloader firmware were modified to switch to this configuration but it may not be the case for all available products. This patch was originally posted at [1]. [1] https://github.com/LibreELEC/linux-amlogic/pull/27 Signed-off-by: Alex Deryskyba Signed-off-by: Neil Armstrong Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/meson-ir.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/drivers/media/rc/meson-ir.c b/drivers/media/rc/meson-ir.c index 3375b4e18764..65566d569cb1 100644 --- a/drivers/media/rc/meson-ir.c +++ b/drivers/media/rc/meson-ir.c @@ -201,6 +201,32 @@ static int meson_ir_remove(struct platform_device *pdev) return 0; } +static void meson_ir_shutdown(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *node = dev->of_node; + struct meson_ir *ir = platform_get_drvdata(pdev); + unsigned long flags; + + spin_lock_irqsave(&ir->lock, flags); + + /* + * Set operation mode to NEC/hardware decoding to give + * bootloader a chance to power the system back on + */ + if (of_device_is_compatible(node, "amlogic,meson6-ir")) + meson_ir_set_mask(ir, IR_DEC_REG1, REG1_MODE_MASK, + DECODE_MODE_NEC << REG1_MODE_SHIFT); + else + meson_ir_set_mask(ir, IR_DEC_REG2, REG2_MODE_MASK, + DECODE_MODE_NEC << REG2_MODE_SHIFT); + + /* Set rate to default value */ + meson_ir_set_mask(ir, IR_DEC_REG0, REG0_RATE_MASK, 0x13); + + spin_unlock_irqrestore(&ir->lock, flags); +} + static const struct of_device_id meson_ir_match[] = { { .compatible = "amlogic,meson6-ir" }, { .compatible = "amlogic,meson8b-ir" }, @@ -212,6 +238,7 @@ MODULE_DEVICE_TABLE(of, meson_ir_match); static struct platform_driver meson_ir_driver = { .probe = meson_ir_probe, .remove = meson_ir_remove, + .shutdown = meson_ir_shutdown, .driver = { .name = DRIVER_NAME, .of_match_table = meson_ir_match, -- cgit v1.2.3-58-ga151 From a06854a600f6ccb72f097d86a44688949b867906 Mon Sep 17 00:00:00 2001 From: A Sun Date: Sun, 26 Mar 2017 15:28:08 -0300 Subject: [media] mceusb: RX -EPIPE (urb status = -32) lockup failure fix RX -EPIPE failure with infinite loop and flooding of [ 2851.966506] mceusb 1-1.2:1.0: Error: urb status = -32 log message at 8000 messages per second. Bug trigger appears to be normal, but heavy, IR receiver use. Driver and Linux host become unusable after error. Also seen at https://sourceforge.net/p/lirc/mailman/message/34886165/ Fix: Message reports RX usb halt (stall) condition requiring usb_clear_halt() call in non-interrupt context to recover. Add driver workqueue call to perform this recovery based on method in use for the usbnet device driver. Signed-off-by: A Sun Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/mceusb.c | 74 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 73 insertions(+), 1 deletion(-) diff --git a/drivers/media/rc/mceusb.c b/drivers/media/rc/mceusb.c index 17e75cd8399a..1e08c7dc0e82 100644 --- a/drivers/media/rc/mceusb.c +++ b/drivers/media/rc/mceusb.c @@ -36,12 +36,13 @@ #include #include #include +#include #include #include #include #include -#define DRIVER_VERSION "1.92" +#define DRIVER_VERSION "1.93" #define DRIVER_AUTHOR "Jarod Wilson " #define DRIVER_DESC "Windows Media Center Ed. eHome Infrared Transceiver " \ "device driver" @@ -416,6 +417,7 @@ struct mceusb_dev { /* usb */ struct usb_device *usbdev; struct urb *urb_in; + unsigned int pipe_in; struct usb_endpoint_descriptor *usb_ep_out; /* buffers and dma */ @@ -453,6 +455,16 @@ struct mceusb_dev { u8 num_rxports; /* number of receive sensors */ u8 txports_cabled; /* bitmask of transmitters with cable */ u8 rxports_active; /* bitmask of active receive sensors */ + + /* + * support for async error handler mceusb_deferred_kevent() + * where usb_clear_halt(), usb_reset_configuration(), + * usb_reset_device(), etc. must be done in process context + */ + struct work_struct kevent; + unsigned long kevent_flags; +# define EVENT_TX_HALT 0 +# define EVENT_RX_HALT 1 }; /* MCE Device Command Strings, generally a port and command pair */ @@ -686,6 +698,21 @@ static void mceusb_dev_printdata(struct mceusb_dev *ir, char *buf, #endif } +/* + * Schedule work that can't be done in interrupt handlers + * (mceusb_dev_recv() and mce_async_callback()) nor tasklets. + * Invokes mceusb_deferred_kevent() for recovering from + * error events specified by the kevent bit field. + */ +static void mceusb_defer_kevent(struct mceusb_dev *ir, int kevent) +{ + set_bit(kevent, &ir->kevent_flags); + if (!schedule_work(&ir->kevent)) + dev_err(ir->dev, "kevent %d may have been dropped", kevent); + else + dev_dbg(ir->dev, "kevent %d scheduled", kevent); +} + static void mce_async_callback(struct urb *urb) { struct mceusb_dev *ir; @@ -1053,6 +1080,11 @@ static void mceusb_dev_recv(struct urb *urb) return; case -EPIPE: + dev_err(ir->dev, "Error: urb status = %d (RX HALT)", + urb->status); + mceusb_defer_kevent(ir, EVENT_RX_HALT); + return; + default: dev_err(ir->dev, "Error: urb status = %d", urb->status); break; @@ -1171,6 +1203,37 @@ static void mceusb_flash_led(struct mceusb_dev *ir) mce_async_out(ir, FLASH_LED, sizeof(FLASH_LED)); } +/* + * Workqueue function + * for resetting or recovering device after occurrence of error events + * specified in ir->kevent bit field. + * Function runs (via schedule_work()) in non-interrupt context, for + * calls here (such as usb_clear_halt()) requiring non-interrupt context. + */ +static void mceusb_deferred_kevent(struct work_struct *work) +{ + struct mceusb_dev *ir = + container_of(work, struct mceusb_dev, kevent); + int status; + + if (test_bit(EVENT_RX_HALT, &ir->kevent_flags)) { + usb_unlink_urb(ir->urb_in); + status = usb_clear_halt(ir->usbdev, ir->pipe_in); + if (status < 0) { + dev_err(ir->dev, "rx clear halt error %d", + status); + return; + } + clear_bit(EVENT_RX_HALT, &ir->kevent_flags); + status = usb_submit_urb(ir->urb_in, GFP_KERNEL); + if (status < 0) { + dev_err(ir->dev, "rx unhalt submit urb error %d", + status); + return; + } + } +} + static struct rc_dev *mceusb_init_rc_dev(struct mceusb_dev *ir) { struct usb_device *udev = ir->usbdev; @@ -1304,6 +1367,7 @@ static int mceusb_dev_probe(struct usb_interface *intf, if (!ir) goto mem_alloc_fail; + ir->pipe_in = pipe; ir->buf_in = usb_alloc_coherent(dev, maxp, GFP_ATOMIC, &ir->dma_in); if (!ir->buf_in) goto buf_in_alloc_fail; @@ -1333,6 +1397,12 @@ static int mceusb_dev_probe(struct usb_interface *intf, snprintf(name + strlen(name), sizeof(name) - strlen(name), " %s", buf); + /* + * Initialize async USB error handler before registering + * or activating any mceusb RX and TX functions + */ + INIT_WORK(&ir->kevent, mceusb_deferred_kevent); + ir->rc = mceusb_init_rc_dev(ir); if (!ir->rc) goto rc_dev_fail; @@ -1386,6 +1456,7 @@ static int mceusb_dev_probe(struct usb_interface *intf, /* Error-handling path */ rc_dev_fail: + cancel_work_sync(&ir->kevent); usb_put_dev(ir->usbdev); usb_kill_urb(ir->urb_in); usb_free_urb(ir->urb_in); @@ -1411,6 +1482,7 @@ static void mceusb_dev_disconnect(struct usb_interface *intf) return; ir->usbdev = NULL; + cancel_work_sync(&ir->kevent); rc_unregister_device(ir->rc); usb_kill_urb(ir->urb_in); usb_free_urb(ir->urb_in); -- cgit v1.2.3-58-ga151 From c779a9cdf7c65291b94ad174e003348bc2ab7cdf Mon Sep 17 00:00:00 2001 From: A Sun Date: Thu, 13 Apr 2017 05:06:47 -0300 Subject: [media] mceusb: TX -EPIPE (urb status = -32) lockup fix Once IR blasting or mceusb device commands fail with mce_async_callback() TX -EPIPE error, all subsequent TX to device then fail with the same error. ... [ 249.986174] mceusb 1-1.2:1.0: requesting 38000 HZ carrier [ 249.986210] mceusb 1-1.2:1.0: send request called (size=0x4) [ 249.986256] mceusb 1-1.2:1.0: send request complete (res=0) [ 249.986403] mceusb 1-1.2:1.0: Error: request urb status = -32 (TX HALT) [ 249.999885] mceusb 1-1.2:1.0: send request called (size=0x3) [ 249.999929] mceusb 1-1.2:1.0: send request complete (res=0) [ 250.000013] mceusb 1-1.2:1.0: Error: request urb status = -32 (TX HALT) [ 250.019830] mceusb 1-1.2:1.0: send request called (size=0x21) [ 250.019868] mceusb 1-1.2:1.0: send request complete (res=0) [ 250.020007] mceusb 1-1.2:1.0: Error: request urb status = -32 (TX HALT) ... Fault simulation/injection is by executing the following USB operation in a mceusb instrumented driver, prior to TX I/O. retval = usb_control_msg(ir->usbdev, usb_sndctrlpipe(ir->usbdev, 0), USB_REQ_SET_FEATURE, USB_RECIP_ENDPOINT, USB_ENDPOINT_HALT, usb_pipeendpoint(ir->pipe_out), NULL, 0, USB_CTRL_SET_TIMEOUT); dev_dbg(ir->dev, "set halt retval, %d", retval); After setting halt state for the TX endpoint, perform an lirc "irsend" to generate TX traffic to device. After the TX HALT, the patch restores subsequent TX to working state. ... [ 508.009638] mceusb 1-1.2:1.0: send request called (size=0x3) [ 508.009697] mceusb 1-1.2:1.0: send request complete (res=0) [ 508.009847] mce_async_callback() [ 508.009864] mceusb 1-1.2:1.0: Error: request urb status = -32 (TX HALT) [ 508.009890] mceusb 1-1.2:1.0: kevent 0 scheduled [ 508.021552] mceusb 1-1.2:1.0: send request called (size=0x21) [ 508.021598] mceusb 1-1.2:1.0: send request complete (res=0) [ 508.021963] mce_async_callback() [ 508.021981] mceusb 1-1.2:1.0: tx data: 84 b0 0c 8c 0c 84 8c 0c 8c 0c 84 8c 0c 8c 0c 84 98 0c 98 0c 84 98 0c 8c 0c 84 8c 0c 8c 0c 81 8c 80 (length=33) [ 508.021997] mceusb 1-1.2:1.0: Raw IR data, 0 pulse/space samples [ 508.066627] mceusb 1-1.2:1.0: send request called (size=0x3) [ 508.066669] mceusb 1-1.2:1.0: send request complete (res=0) [ 508.066841] mce_async_callback() [ 508.066858] mceusb 1-1.2:1.0: tx data: 9f 08 03 (length=3) ... Signed-off-by: A Sun Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/mceusb.c | 53 ++++++++++++++++++++++++++++++----------------- 1 file changed, 34 insertions(+), 19 deletions(-) diff --git a/drivers/media/rc/mceusb.c b/drivers/media/rc/mceusb.c index 1e08c7dc0e82..4530237cbb67 100644 --- a/drivers/media/rc/mceusb.c +++ b/drivers/media/rc/mceusb.c @@ -419,6 +419,7 @@ struct mceusb_dev { struct urb *urb_in; unsigned int pipe_in; struct usb_endpoint_descriptor *usb_ep_out; + unsigned int pipe_out; /* buffers and dma */ unsigned char *buf_in; @@ -739,6 +740,11 @@ static void mce_async_callback(struct urb *urb) break; case -EPIPE: + dev_err(ir->dev, "Error: request urb status = %d (TX HALT)", + urb->status); + mceusb_defer_kevent(ir, EVENT_TX_HALT); + break; + default: dev_err(ir->dev, "Error: request urb status = %d", urb->status); break; @@ -753,7 +759,7 @@ static void mce_async_callback(struct urb *urb) static void mce_request_packet(struct mceusb_dev *ir, unsigned char *data, int size) { - int res, pipe; + int res; struct urb *async_urb; struct device *dev = ir->dev; unsigned char *async_buf; @@ -771,19 +777,14 @@ static void mce_request_packet(struct mceusb_dev *ir, unsigned char *data, } /* outbound data */ - if (usb_endpoint_xfer_int(ir->usb_ep_out)) { - pipe = usb_sndintpipe(ir->usbdev, - ir->usb_ep_out->bEndpointAddress); - usb_fill_int_urb(async_urb, ir->usbdev, pipe, async_buf, - size, mce_async_callback, ir, + if (usb_endpoint_xfer_int(ir->usb_ep_out)) + usb_fill_int_urb(async_urb, ir->usbdev, ir->pipe_out, + async_buf, size, mce_async_callback, ir, ir->usb_ep_out->bInterval); - } else { - pipe = usb_sndbulkpipe(ir->usbdev, - ir->usb_ep_out->bEndpointAddress); - usb_fill_bulk_urb(async_urb, ir->usbdev, pipe, - async_buf, size, mce_async_callback, - ir); - } + else + usb_fill_bulk_urb(async_urb, ir->usbdev, ir->pipe_out, + async_buf, size, mce_async_callback, ir); + memcpy(async_buf, data, size); dev_dbg(dev, "send request called (size=%#x)", size); @@ -1222,16 +1223,24 @@ static void mceusb_deferred_kevent(struct work_struct *work) if (status < 0) { dev_err(ir->dev, "rx clear halt error %d", status); - return; } clear_bit(EVENT_RX_HALT, &ir->kevent_flags); - status = usb_submit_urb(ir->urb_in, GFP_KERNEL); - if (status < 0) { - dev_err(ir->dev, "rx unhalt submit urb error %d", - status); - return; + if (status == 0) { + status = usb_submit_urb(ir->urb_in, GFP_KERNEL); + if (status < 0) { + dev_err(ir->dev, + "rx unhalt submit urb error %d", + status); + } } } + + if (test_bit(EVENT_TX_HALT, &ir->kevent_flags)) { + status = usb_clear_halt(ir->usbdev, ir->pipe_out); + if (status < 0) + dev_err(ir->dev, "tx clear halt error %d", status); + clear_bit(EVENT_TX_HALT, &ir->kevent_flags); + } } static struct rc_dev *mceusb_init_rc_dev(struct mceusb_dev *ir) @@ -1386,6 +1395,12 @@ static int mceusb_dev_probe(struct usb_interface *intf, /* Saving usb interface data for use by the transmitter routine */ ir->usb_ep_out = ep_out; + if (usb_endpoint_xfer_int(ep_out)) + ir->pipe_out = usb_sndintpipe(ir->usbdev, + ep_out->bEndpointAddress); + else + ir->pipe_out = usb_sndbulkpipe(ir->usbdev, + ep_out->bEndpointAddress); if (dev->descriptor.iManufacturer && usb_string(dev, dev->descriptor.iManufacturer, -- cgit v1.2.3-58-ga151 From 52e809f9fe3e4922e1cb539079ad1b262e163a3b Mon Sep 17 00:00:00 2001 From: David Härdeman Date: Mon, 1 May 2017 13:03:41 -0300 Subject: [media] lirc_dev: remove pointless functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit drv->set_use_inc and drv->set_use_dec are already optional so we can remove all dummy functions. Signed-off-by: David Härdeman Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/ir-lirc-codec.c | 14 ++------------ drivers/staging/media/lirc/lirc_zilog.c | 11 ----------- 2 files changed, 2 insertions(+), 23 deletions(-) diff --git a/drivers/media/rc/ir-lirc-codec.c b/drivers/media/rc/ir-lirc-codec.c index 8f0669c9894c..fc58745b26b8 100644 --- a/drivers/media/rc/ir-lirc-codec.c +++ b/drivers/media/rc/ir-lirc-codec.c @@ -327,16 +327,6 @@ static long ir_lirc_ioctl(struct file *filep, unsigned int cmd, return ret; } -static int ir_lirc_open(void *data) -{ - return 0; -} - -static void ir_lirc_close(void *data) -{ - return; -} - static const struct file_operations lirc_fops = { .owner = THIS_MODULE, .write = ir_lirc_transmit_ir, @@ -396,8 +386,8 @@ static int ir_lirc_register(struct rc_dev *dev) drv->features = features; drv->data = &dev->raw->lirc; drv->rbuf = NULL; - drv->set_use_inc = &ir_lirc_open; - drv->set_use_dec = &ir_lirc_close; + drv->set_use_inc = NULL; + drv->set_use_dec = NULL; drv->code_length = sizeof(struct ir_raw_event) * 8; drv->chunk_size = sizeof(int); drv->buffer_size = LIRCBUF_SIZE; diff --git a/drivers/staging/media/lirc/lirc_zilog.c b/drivers/staging/media/lirc/lirc_zilog.c index 8ce1db04414a..123933f1b9b0 100644 --- a/drivers/staging/media/lirc/lirc_zilog.c +++ b/drivers/staging/media/lirc/lirc_zilog.c @@ -497,15 +497,6 @@ static int lirc_thread(void *arg) return 0; } -static int set_use_inc(void *data) -{ - return 0; -} - -static void set_use_dec(void *data) -{ -} - /* safe read of a uint32 (always network byte order) */ static int read_uint32(unsigned char **data, unsigned char *endp, unsigned int *val) @@ -1396,8 +1387,6 @@ static struct lirc_driver lirc_template = { .buffer_size = BUFLEN / 2, .sample_rate = 0, /* tell lirc_dev to not start its own kthread */ .chunk_size = 2, - .set_use_inc = set_use_inc, - .set_use_dec = set_use_dec, .fops = &lirc_fops, .owner = THIS_MODULE, }; -- cgit v1.2.3-58-ga151 From 2c5a1f44667b75e75302d87e61b335e68f4078f1 Mon Sep 17 00:00:00 2001 From: David Härdeman Date: Mon, 1 May 2017 13:03:46 -0300 Subject: [media] lirc_dev: remove unused set_use_inc/set_use_dec MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since there are no users of this functionality, it can be removed altogether. Signed-off-by: David Härdeman Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/ir-lirc-codec.c | 2 -- drivers/media/rc/lirc_dev.c | 24 ++++++------------------ include/media/lirc_dev.h | 6 ------ 3 files changed, 6 insertions(+), 26 deletions(-) diff --git a/drivers/media/rc/ir-lirc-codec.c b/drivers/media/rc/ir-lirc-codec.c index fc58745b26b8..a30af91710fe 100644 --- a/drivers/media/rc/ir-lirc-codec.c +++ b/drivers/media/rc/ir-lirc-codec.c @@ -386,8 +386,6 @@ static int ir_lirc_register(struct rc_dev *dev) drv->features = features; drv->data = &dev->raw->lirc; drv->rbuf = NULL; - drv->set_use_inc = NULL; - drv->set_use_dec = NULL; drv->code_length = sizeof(struct ir_raw_event) * 8; drv->chunk_size = sizeof(int); drv->buffer_size = LIRCBUF_SIZE; diff --git a/drivers/media/rc/lirc_dev.c b/drivers/media/rc/lirc_dev.c index 42704552b005..05f600bd6c67 100644 --- a/drivers/media/rc/lirc_dev.c +++ b/drivers/media/rc/lirc_dev.c @@ -418,12 +418,6 @@ int lirc_unregister_driver(int minor) wake_up_interruptible(&ir->buf->wait_poll); } - mutex_lock(&ir->irctl_lock); - - if (ir->d.set_use_dec) - ir->d.set_use_dec(ir->d.data); - - mutex_unlock(&ir->irctl_lock); mutex_unlock(&lirc_dev_lock); device_del(&ir->dev); @@ -473,17 +467,13 @@ int lirc_dev_fop_open(struct inode *inode, struct file *file) goto error; } + if (ir->buf) + lirc_buffer_clear(ir->buf); + + if (ir->task) + wake_up_process(ir->task); + ir->open++; - if (ir->d.set_use_inc) - retval = ir->d.set_use_inc(ir->d.data); - if (retval) { - ir->open--; - } else { - if (ir->buf) - lirc_buffer_clear(ir->buf); - if (ir->task) - wake_up_process(ir->task); - } error: nonseekable_open(inode, file); @@ -508,8 +498,6 @@ int lirc_dev_fop_close(struct inode *inode, struct file *file) rc_close(ir->d.rdev); ir->open--; - if (ir->d.set_use_dec) - ir->d.set_use_dec(ir->d.data); if (!ret) mutex_unlock(&lirc_dev_lock); diff --git a/include/media/lirc_dev.h b/include/media/lirc_dev.h index cec7d35602d1..71c1c11950fe 100644 --- a/include/media/lirc_dev.h +++ b/include/media/lirc_dev.h @@ -165,10 +165,6 @@ static inline unsigned int lirc_buffer_write(struct lirc_buffer *buf, * have to write to the buffer by other means, like irq's * (see also lirc_serial.c). * - * @set_use_inc: set_use_inc will be called after device is opened - * - * @set_use_dec: set_use_dec will be called after device is closed - * * @rdev: Pointed to struct rc_dev associated with the LIRC * device. * @@ -198,8 +194,6 @@ struct lirc_driver { int max_timeout; int (*add_to_buf)(void *data, struct lirc_buffer *buf); struct lirc_buffer *rbuf; - int (*set_use_inc)(void *data); - void (*set_use_dec)(void *data); struct rc_dev *rdev; const struct file_operations *fops; struct device *dev; -- cgit v1.2.3-58-ga151 From c3104e1b42744156c414003043587d128de2b91f Mon Sep 17 00:00:00 2001 From: David Härdeman Date: Mon, 1 May 2017 13:03:56 -0300 Subject: [media] lirc_dev: remove sampling kthread MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There are no drivers which use this functionality. Signed-off-by: David Härdeman Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/lirc_dev.c | 94 +-------------------------------- drivers/staging/media/lirc/lirc_zilog.c | 1 - include/media/lirc_dev.h | 16 ------ 3 files changed, 2 insertions(+), 109 deletions(-) diff --git a/drivers/media/rc/lirc_dev.c b/drivers/media/rc/lirc_dev.c index 05f600bd6c67..a613970ce159 100644 --- a/drivers/media/rc/lirc_dev.c +++ b/drivers/media/rc/lirc_dev.c @@ -28,7 +28,6 @@ #include #include #include -#include #include #include #include @@ -57,9 +56,6 @@ struct irctl { struct device dev; struct cdev cdev; - - struct task_struct *task; - long jiffies_to_wait; }; static DEFINE_MUTEX(lirc_dev_lock); @@ -95,59 +91,6 @@ static void lirc_release(struct device *ld) kfree(ir); } -/* helper function - * reads key codes from driver and puts them into buffer - * returns 0 on success - */ -static int lirc_add_to_buf(struct irctl *ir) -{ - int res; - int got_data = -1; - - if (!ir->d.add_to_buf) - return 0; - - /* - * service the device as long as it is returning - * data and we have space - */ - do { - got_data++; - res = ir->d.add_to_buf(ir->d.data, ir->buf); - } while (!res); - - if (res == -ENODEV) - kthread_stop(ir->task); - - return got_data ? 0 : res; -} - -/* main function of the polling thread - */ -static int lirc_thread(void *irctl) -{ - struct irctl *ir = irctl; - - do { - if (ir->open) { - if (ir->jiffies_to_wait) { - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(ir->jiffies_to_wait); - } - if (kthread_should_stop()) - break; - if (!lirc_add_to_buf(ir)) - wake_up_interruptible(&ir->buf->wait_poll); - } else { - set_current_state(TASK_INTERRUPTIBLE); - schedule(); - } - } while (!kthread_should_stop()); - - return 0; -} - - static const struct file_operations lirc_dev_fops = { .owner = THIS_MODULE, .read = lirc_dev_fop_read, @@ -252,18 +195,8 @@ static int lirc_allocate_driver(struct lirc_driver *d) return -EBADRQC; } - if (d->sample_rate) { - if (2 > d->sample_rate || HZ < d->sample_rate) { - dev_err(d->dev, "invalid %d sample rate\n", - d->sample_rate); - return -EBADRQC; - } - if (!d->add_to_buf) { - dev_err(d->dev, "add_to_buf not set\n"); - return -EBADRQC; - } - } else if (!d->rbuf && !(d->fops && d->fops->read && - d->fops->poll && d->fops->unlocked_ioctl)) { + if (!d->rbuf && !(d->fops && d->fops->read && + d->fops->poll && d->fops->unlocked_ioctl)) { dev_err(d->dev, "undefined read, poll, ioctl\n"); return -EBADRQC; } @@ -312,22 +245,6 @@ static int lirc_allocate_driver(struct lirc_driver *d) dev_set_name(&ir->dev, "lirc%d", ir->d.minor); device_initialize(&ir->dev); - if (d->sample_rate) { - ir->jiffies_to_wait = HZ / d->sample_rate; - - /* try to fire up polling thread */ - ir->task = kthread_run(lirc_thread, (void *)ir, "lirc_dev"); - if (IS_ERR(ir->task)) { - dev_err(d->dev, "cannot run thread for minor = %d\n", - d->minor); - err = -ECHILD; - goto out_sysfs; - } - } else { - /* it means - wait for external event in task queue */ - ir->jiffies_to_wait = 0; - } - err = lirc_cdev_add(ir); if (err) goto out_sysfs; @@ -404,10 +321,6 @@ int lirc_unregister_driver(int minor) return -ENOENT; } - /* end up polling thread */ - if (ir->task) - kthread_stop(ir->task); - dev_dbg(ir->d.dev, "lirc_dev: driver %s unregistered from minor = %d\n", ir->d.name, ir->d.minor); @@ -470,9 +383,6 @@ int lirc_dev_fop_open(struct inode *inode, struct file *file) if (ir->buf) lirc_buffer_clear(ir->buf); - if (ir->task) - wake_up_process(ir->task); - ir->open++; error: diff --git a/drivers/staging/media/lirc/lirc_zilog.c b/drivers/staging/media/lirc/lirc_zilog.c index 123933f1b9b0..59f657daf31b 100644 --- a/drivers/staging/media/lirc/lirc_zilog.c +++ b/drivers/staging/media/lirc/lirc_zilog.c @@ -1385,7 +1385,6 @@ static struct lirc_driver lirc_template = { .minor = -1, .code_length = 13, .buffer_size = BUFLEN / 2, - .sample_rate = 0, /* tell lirc_dev to not start its own kthread */ .chunk_size = 2, .fops = &lirc_fops, .owner = THIS_MODULE, diff --git a/include/media/lirc_dev.h b/include/media/lirc_dev.h index 71c1c11950fe..01649b009922 100644 --- a/include/media/lirc_dev.h +++ b/include/media/lirc_dev.h @@ -133,12 +133,6 @@ static inline unsigned int lirc_buffer_write(struct lirc_buffer *buf, * @buffer_size: Number of FIFO buffers with @chunk_size size. If zero, * creates a buffer with BUFLEN size (16 bytes). * - * @sample_rate: if zero, the device will wait for an event with a new - * code to be parsed. Otherwise, specifies the sample - * rate for polling. Value should be between 0 - * and HZ. If equal to HZ, it would mean one polling per - * second. - * * @features: lirc compatible hardware features, like LIRC_MODE_RAW, * LIRC_CAN\_\*, as defined at include/media/lirc.h. * @@ -153,14 +147,6 @@ static inline unsigned int lirc_buffer_write(struct lirc_buffer *buf, * @max_timeout: Maximum timeout for record. Valid only if * LIRC_CAN_SET_REC_TIMEOUT is defined. * - * @add_to_buf: add_to_buf will be called after specified period of the - * time or triggered by the external event, this behavior - * depends on value of the sample_rate this function will - * be called in user context. This routine should return - * 0 if data was added to the buffer and -ENODATA if none - * was available. This should add some number of bits - * evenly divisible by code_length to the buffer. - * * @rbuf: if not NULL, it will be used as a read buffer, you will * have to write to the buffer by other means, like irq's * (see also lirc_serial.c). @@ -184,7 +170,6 @@ struct lirc_driver { int minor; __u32 code_length; unsigned int buffer_size; /* in chunks holding one code each */ - int sample_rate; __u32 features; unsigned int chunk_size; @@ -192,7 +177,6 @@ struct lirc_driver { void *data; int min_timeout; int max_timeout; - int (*add_to_buf)(void *data, struct lirc_buffer *buf); struct lirc_buffer *rbuf; struct rc_dev *rdev; const struct file_operations *fops; -- cgit v1.2.3-58-ga151 From 258232269590cee2acd4bb28b8e027ad340a85f9 Mon Sep 17 00:00:00 2001 From: David Härdeman Date: Mon, 1 May 2017 13:04:01 -0300 Subject: [media] lirc_dev: clarify error handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit out_sysfs is misleading, sysfs only comes into play after device_add(). Also, calling device_init() before the rest of struct dev is filled out is clearer. Signed-off-by: David Härdeman Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/lirc_dev.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/media/rc/lirc_dev.c b/drivers/media/rc/lirc_dev.c index a613970ce159..7d04f83c1ab6 100644 --- a/drivers/media/rc/lirc_dev.c +++ b/drivers/media/rc/lirc_dev.c @@ -238,16 +238,16 @@ static int lirc_allocate_driver(struct lirc_driver *d) ir->d = *d; + device_initialize(&ir->dev); ir->dev.devt = MKDEV(MAJOR(lirc_base_dev), ir->d.minor); ir->dev.class = lirc_class; ir->dev.parent = d->dev; ir->dev.release = lirc_release; dev_set_name(&ir->dev, "lirc%d", ir->d.minor); - device_initialize(&ir->dev); err = lirc_cdev_add(ir); if (err) - goto out_sysfs; + goto out_free_dev; ir->attached = 1; @@ -264,7 +264,7 @@ static int lirc_allocate_driver(struct lirc_driver *d) return minor; out_cdev: cdev_del(&ir->cdev); -out_sysfs: +out_free_dev: put_device(&ir->dev); out_lock: mutex_unlock(&lirc_dev_lock); -- cgit v1.2.3-58-ga151 From e0e3c77cc218d754eae9ddcc62d8ede5b31cf7c2 Mon Sep 17 00:00:00 2001 From: David Härdeman Date: Mon, 1 May 2017 13:04:06 -0300 Subject: [media] lirc_dev: make fops mandatory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Every caller of lirc_register_driver() passes their own fops and there are no users of lirc_dev_fop_write() in the kernel tree. Thus we can make fops mandatory and remove lirc_dev_fop_write(). Signed-off-by: David Härdeman Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/lirc_dev.c | 41 +++++------------------------------------ include/media/lirc_dev.h | 3 --- 2 files changed, 5 insertions(+), 39 deletions(-) diff --git a/drivers/media/rc/lirc_dev.c b/drivers/media/rc/lirc_dev.c index 7d04f83c1ab6..de7de0865a10 100644 --- a/drivers/media/rc/lirc_dev.c +++ b/drivers/media/rc/lirc_dev.c @@ -91,17 +91,6 @@ static void lirc_release(struct device *ld) kfree(ir); } -static const struct file_operations lirc_dev_fops = { - .owner = THIS_MODULE, - .read = lirc_dev_fop_read, - .write = lirc_dev_fop_write, - .poll = lirc_dev_fop_poll, - .unlocked_ioctl = lirc_dev_fop_ioctl, - .open = lirc_dev_fop_open, - .release = lirc_dev_fop_close, - .llseek = noop_llseek, -}; - static int lirc_cdev_add(struct irctl *ir) { struct lirc_driver *d = &ir->d; @@ -110,13 +99,11 @@ static int lirc_cdev_add(struct irctl *ir) cdev = &ir->cdev; - if (d->fops) { - cdev_init(cdev, d->fops); - cdev->owner = d->owner; - } else { - cdev_init(cdev, &lirc_dev_fops); - cdev->owner = THIS_MODULE; - } + if (!d->fops) + return -EINVAL; + + cdev_init(cdev, d->fops); + cdev->owner = d->owner; retval = kobject_set_name(&cdev->kobj, "lirc%d", d->minor); if (retval) return retval; @@ -638,24 +625,6 @@ void *lirc_get_pdata(struct file *file) EXPORT_SYMBOL(lirc_get_pdata); -ssize_t lirc_dev_fop_write(struct file *file, const char __user *buffer, - size_t length, loff_t *ppos) -{ - struct irctl *ir = irctls[iminor(file_inode(file))]; - - if (!ir) { - pr_err("called with invalid irctl\n"); - return -ENODEV; - } - - if (!ir->attached) - return -ENODEV; - - return -EINVAL; -} -EXPORT_SYMBOL(lirc_dev_fop_write); - - static int __init lirc_dev_init(void) { int retval; diff --git a/include/media/lirc_dev.h b/include/media/lirc_dev.h index 01649b009922..1f327e25a9be 100644 --- a/include/media/lirc_dev.h +++ b/include/media/lirc_dev.h @@ -210,7 +210,4 @@ unsigned int lirc_dev_fop_poll(struct file *file, poll_table *wait); long lirc_dev_fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg); ssize_t lirc_dev_fop_read(struct file *file, char __user *buffer, size_t length, loff_t *ppos); -ssize_t lirc_dev_fop_write(struct file *file, const char __user *buffer, - size_t length, loff_t *ppos); - #endif -- cgit v1.2.3-58-ga151 From 56481f0060a48938e31461efa2ad54d3b293ebe8 Mon Sep 17 00:00:00 2001 From: David Härdeman Date: Mon, 1 May 2017 13:04:11 -0300 Subject: [media] lirc_dev: merge lirc_register_driver() and lirc_allocate_driver() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Merging the two means that lirc_allocate_buffer() is called before device_add() and cdev_add() which makes more sense. This also simplifies the locking slightly because lirc_allocate_buffer() will always be called with lirc_dev_lock held. Signed-off-by: David Härdeman Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/lirc_dev.c | 41 +++++++++++++---------------------------- 1 file changed, 13 insertions(+), 28 deletions(-) diff --git a/drivers/media/rc/lirc_dev.c b/drivers/media/rc/lirc_dev.c index de7de0865a10..a4cfffecac36 100644 --- a/drivers/media/rc/lirc_dev.c +++ b/drivers/media/rc/lirc_dev.c @@ -120,8 +120,6 @@ static int lirc_allocate_buffer(struct irctl *ir) unsigned int buffer_size; struct lirc_driver *d = &ir->d; - mutex_lock(&lirc_dev_lock); - bytes_in_key = BITS_TO_LONGS(d->code_length) + (d->code_length % 8 ? 1 : 0); buffer_size = d->buffer_size ? d->buffer_size : BUFLEN / bytes_in_key; @@ -145,16 +143,15 @@ static int lirc_allocate_buffer(struct irctl *ir) } ir->buf_internal = true; + d->rbuf = ir->buf; } ir->chunk_size = ir->buf->chunk_size; out: - mutex_unlock(&lirc_dev_lock); - return err; } -static int lirc_allocate_driver(struct lirc_driver *d) +int lirc_register_driver(struct lirc_driver *d) { struct irctl *ir; int minor; @@ -225,6 +222,15 @@ static int lirc_allocate_driver(struct lirc_driver *d) ir->d = *d; + if (LIRC_CAN_REC(d->features)) { + err = lirc_allocate_buffer(irctls[minor]); + if (err) { + kfree(ir); + goto out_lock; + } + d->rbuf = ir->buf; + } + device_initialize(&ir->dev); ir->dev.devt = MKDEV(MAJOR(lirc_base_dev), ir->d.minor); ir->dev.class = lirc_class; @@ -248,7 +254,9 @@ static int lirc_allocate_driver(struct lirc_driver *d) dev_info(ir->d.dev, "lirc_dev: driver %s registered at minor = %d\n", ir->d.name, ir->d.minor); + return minor; + out_cdev: cdev_del(&ir->cdev); out_free_dev: @@ -258,29 +266,6 @@ out_lock: return err; } - -int lirc_register_driver(struct lirc_driver *d) -{ - int minor, err = 0; - - minor = lirc_allocate_driver(d); - if (minor < 0) - return minor; - - if (LIRC_CAN_REC(d->features)) { - err = lirc_allocate_buffer(irctls[minor]); - if (err) - lirc_unregister_driver(minor); - else - /* - * This is kind of a hack but ir-lirc-codec needs - * access to the buffer that lirc_dev allocated. - */ - d->rbuf = irctls[minor]->buf; - } - - return err ? err : minor; -} EXPORT_SYMBOL(lirc_register_driver); int lirc_unregister_driver(int minor) -- cgit v1.2.3-58-ga151 From bd16168da80dde6cfc6b7f89959206b74409cb86 Mon Sep 17 00:00:00 2001 From: David Härdeman Date: Mon, 1 May 2017 13:04:16 -0300 Subject: [media] lirc_zilog: remove module parameter minor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Always let the kernel decide what minor the lirc chardev gets. Signed-off-by: David Härdeman Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/lirc/lirc_zilog.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/drivers/staging/media/lirc/lirc_zilog.c b/drivers/staging/media/lirc/lirc_zilog.c index 59f657daf31b..df371e07e6c1 100644 --- a/drivers/staging/media/lirc/lirc_zilog.c +++ b/drivers/staging/media/lirc/lirc_zilog.c @@ -156,7 +156,6 @@ static struct mutex tx_data_lock; /* module parameters */ static bool debug; /* debug output */ static bool tx_only; /* only handle the IR Tx function */ -static int minor = -1; /* minor number */ /* struct IR reference counting */ @@ -184,10 +183,11 @@ static void release_ir_device(struct kref *ref) * ir->open_count == 0 - happens on final close() * ir_lock, tx_ref_lock, rx_ref_lock, all released */ - if (ir->l.minor >= 0 && ir->l.minor < MAX_IRCTL_DEVICES) { + if (ir->l.minor >= 0) { lirc_unregister_driver(ir->l.minor); - ir->l.minor = MAX_IRCTL_DEVICES; + ir->l.minor = -1; } + if (kfifo_initialized(&ir->rbuf.fifo)) lirc_buffer_free(&ir->rbuf); list_del(&ir->list); @@ -1597,12 +1597,11 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) } /* register with lirc */ - ir->l.minor = minor; /* module option: user requested minor number */ ir->l.minor = lirc_register_driver(&ir->l); - if (ir->l.minor < 0 || ir->l.minor >= MAX_IRCTL_DEVICES) { + if (ir->l.minor < 0) { dev_err(tx->ir->l.dev, - "%s: \"minor\" must be between 0 and %d (%d)!\n", - __func__, MAX_IRCTL_DEVICES-1, ir->l.minor); + "%s: lirc_register_driver() failed: %i\n", + __func__, ir->l.minor); ret = -EBADRQC; goto out_put_xx; } @@ -1674,9 +1673,6 @@ MODULE_LICENSE("GPL"); /* for compat with old name, which isn't all that accurate anymore */ MODULE_ALIAS("lirc_pvr150"); -module_param(minor, int, 0444); -MODULE_PARM_DESC(minor, "Preferred minor device number"); - module_param(debug, bool, 0644); MODULE_PARM_DESC(debug, "Enable debugging messages"); -- cgit v1.2.3-58-ga151 From 712551f0e45d668bd42cf5ce752f02fa0bba63f2 Mon Sep 17 00:00:00 2001 From: David Härdeman Date: Mon, 1 May 2017 13:04:21 -0300 Subject: [media] lirc_dev: remove lirc_irctl_init() and lirc_cdev_add() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These two functions only make the logic in lirc_register_driver() harder to follow. (Note that almost no other driver calls kobject_set_name() on their cdev so I simply removed that part). Signed-off-by: David Härdeman Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/lirc_dev.c | 44 ++++++++++++-------------------------------- 1 file changed, 12 insertions(+), 32 deletions(-) diff --git a/drivers/media/rc/lirc_dev.c b/drivers/media/rc/lirc_dev.c index a4cfffecac36..ad288760b3c3 100644 --- a/drivers/media/rc/lirc_dev.c +++ b/drivers/media/rc/lirc_dev.c @@ -65,15 +65,6 @@ static struct irctl *irctls[MAX_IRCTL_DEVICES]; /* Only used for sysfs but defined to void otherwise */ static struct class *lirc_class; -/* helper function - * initializes the irctl structure - */ -static void lirc_irctl_init(struct irctl *ir) -{ - mutex_init(&ir->irctl_lock); - ir->d.minor = NOPLUG; -} - static void lirc_release(struct device *ld) { struct irctl *ir = container_of(ld, struct irctl, dev); @@ -91,27 +82,6 @@ static void lirc_release(struct device *ld) kfree(ir); } -static int lirc_cdev_add(struct irctl *ir) -{ - struct lirc_driver *d = &ir->d; - struct cdev *cdev; - int retval; - - cdev = &ir->cdev; - - if (!d->fops) - return -EINVAL; - - cdev_init(cdev, d->fops); - cdev->owner = d->owner; - retval = kobject_set_name(&cdev->kobj, "lirc%d", d->minor); - if (retval) - return retval; - - cdev->kobj.parent = &ir->dev.kobj; - return cdev_add(cdev, ir->dev.devt, 1); -} - static int lirc_allocate_buffer(struct irctl *ir) { int err = 0; @@ -167,6 +137,11 @@ int lirc_register_driver(struct lirc_driver *d) return -EINVAL; } + if (!d->fops) { + pr_err("fops pointer not filled in!\n"); + return -EINVAL; + } + if (d->minor >= MAX_IRCTL_DEVICES) { dev_err(d->dev, "minor must be between 0 and %d!\n", MAX_IRCTL_DEVICES - 1); @@ -210,7 +185,8 @@ int lirc_register_driver(struct lirc_driver *d) err = -ENOMEM; goto out_lock; } - lirc_irctl_init(ir); + + mutex_init(&ir->irctl_lock); irctls[minor] = ir; d->minor = minor; @@ -238,7 +214,11 @@ int lirc_register_driver(struct lirc_driver *d) ir->dev.release = lirc_release; dev_set_name(&ir->dev, "lirc%d", ir->d.minor); - err = lirc_cdev_add(ir); + cdev_init(&ir->cdev, d->fops); + ir->cdev.owner = ir->d.owner; + ir->cdev.kobj.parent = &ir->dev.kobj; + + err = cdev_add(&ir->cdev, ir->dev.devt, 1); if (err) goto out_free_dev; -- cgit v1.2.3-58-ga151 From 5be2b76a9ca4ea5fd3e221114d62eeb0d78267ca Mon Sep 17 00:00:00 2001 From: David Härdeman Date: Mon, 1 May 2017 13:04:26 -0300 Subject: [media] lirc_dev: remove superfluous get/put_device() calls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit device_add() and friends already manage the references to the parent device so these calls aren't necessary. Signed-off-by: David Härdeman Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/lirc_dev.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/media/rc/lirc_dev.c b/drivers/media/rc/lirc_dev.c index ad288760b3c3..c2cb5e18e45d 100644 --- a/drivers/media/rc/lirc_dev.c +++ b/drivers/media/rc/lirc_dev.c @@ -69,8 +69,6 @@ static void lirc_release(struct device *ld) { struct irctl *ir = container_of(ld, struct irctl, dev); - put_device(ir->dev.parent); - if (ir->buf_internal) { lirc_buffer_free(ir->buf); kfree(ir->buf); @@ -230,8 +228,6 @@ int lirc_register_driver(struct lirc_driver *d) mutex_unlock(&lirc_dev_lock); - get_device(ir->dev.parent); - dev_info(ir->d.dev, "lirc_dev: driver %s registered at minor = %d\n", ir->d.name, ir->d.minor); -- cgit v1.2.3-58-ga151 From 8e435e572ef9a890512b76b00e42e16f5553ca8f Mon Sep 17 00:00:00 2001 From: David Härdeman Date: Mon, 1 May 2017 13:04:31 -0300 Subject: [media] lirc_dev: remove unused module parameter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The "debug" parameter isn't actually used anywhere. Signed-off-by: David Härdeman Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/lirc_dev.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/media/rc/lirc_dev.c b/drivers/media/rc/lirc_dev.c index c2cb5e18e45d..e01b6e635704 100644 --- a/drivers/media/rc/lirc_dev.c +++ b/drivers/media/rc/lirc_dev.c @@ -36,8 +36,6 @@ #include #include -static bool debug; - #define IRCTL_DEV_NAME "BaseRemoteCtl" #define NOPLUG -1 #define LOGHEAD "lirc_dev (%s[%d]): " @@ -623,6 +621,3 @@ module_exit(lirc_dev_exit); MODULE_DESCRIPTION("LIRC base driver module"); MODULE_AUTHOR("Artur Lipowski"); MODULE_LICENSE("GPL"); - -module_param(debug, bool, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(debug, "Enable debugging messages"); -- cgit v1.2.3-58-ga151 From 29debf3d9d214f600af38069e5954699934abe74 Mon Sep 17 00:00:00 2001 From: David Härdeman Date: Mon, 1 May 2017 13:04:37 -0300 Subject: [media] lirc_dev: return POLLHUP and POLLERR when device is gone MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Most drivers return both values when the device is gone. Signed-off-by: David Härdeman Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/lirc_dev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/rc/lirc_dev.c b/drivers/media/rc/lirc_dev.c index e01b6e635704..786059745f90 100644 --- a/drivers/media/rc/lirc_dev.c +++ b/drivers/media/rc/lirc_dev.c @@ -372,7 +372,7 @@ unsigned int lirc_dev_fop_poll(struct file *file, poll_table *wait) } if (!ir->attached) - return POLLERR; + return POLLHUP | POLLERR; if (ir->buf) { poll_wait(file, &ir->buf->wait_poll, wait); -- cgit v1.2.3-58-ga151 From 463015ddb44122d03bbbf8a39cd170a347b2a010 Mon Sep 17 00:00:00 2001 From: David Härdeman Date: Mon, 1 May 2017 13:04:47 -0300 Subject: [media] lirc_dev: cleanup includes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove superfluous includes and defines. Signed-off-by: David Härdeman Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/lirc_dev.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/drivers/media/rc/lirc_dev.c b/drivers/media/rc/lirc_dev.c index 786059745f90..db1e7b70c998 100644 --- a/drivers/media/rc/lirc_dev.c +++ b/drivers/media/rc/lirc_dev.c @@ -18,17 +18,10 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include -#include #include -#include #include -#include #include -#include #include -#include -#include -#include #include #include @@ -36,7 +29,6 @@ #include #include -#define IRCTL_DEV_NAME "BaseRemoteCtl" #define NOPLUG -1 #define LOGHEAD "lirc_dev (%s[%d]): " @@ -595,7 +587,7 @@ static int __init lirc_dev_init(void) } retval = alloc_chrdev_region(&lirc_base_dev, 0, MAX_IRCTL_DEVICES, - IRCTL_DEV_NAME); + "BaseRemoteCtl"); if (retval) { class_destroy(lirc_class); pr_err("alloc_chrdev_region failed\n"); -- cgit v1.2.3-58-ga151 From 2dc0b351fe7e27b0ceabcde0272bef75eb05bd48 Mon Sep 17 00:00:00 2001 From: David Härdeman Date: Mon, 1 May 2017 13:04:57 -0300 Subject: [media] lirc_dev: cleanup header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove some stuff from lirc_dev.h which is not used anywhere. Signed-off-by: David Härdeman Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- include/media/lirc_dev.h | 7 ------- 1 file changed, 7 deletions(-) diff --git a/include/media/lirc_dev.h b/include/media/lirc_dev.h index 1f327e25a9be..86d15a9b6c01 100644 --- a/include/media/lirc_dev.h +++ b/include/media/lirc_dev.h @@ -12,8 +12,6 @@ #define MAX_IRCTL_DEVICES 8 #define BUFLEN 16 -#define mod(n, div) ((n) % (div)) - #include #include #include @@ -90,11 +88,6 @@ static inline int lirc_buffer_empty(struct lirc_buffer *buf) return !lirc_buffer_len(buf); } -static inline int lirc_buffer_available(struct lirc_buffer *buf) -{ - return buf->size - (lirc_buffer_len(buf) / buf->chunk_size); -} - static inline unsigned int lirc_buffer_read(struct lirc_buffer *buf, unsigned char *dest) { -- cgit v1.2.3-58-ga151 From a387ffc8043b8b894cdc9d3f134b52d9c492ac26 Mon Sep 17 00:00:00 2001 From: David Härdeman Date: Mon, 1 May 2017 13:09:56 -0300 Subject: [media] rc-core: ati_remote - leave the internals of rc_dev alone MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The REP_DELAY setting on the input device is independent of hardware. This change should not change how to driver works (as it does a keydown/keyup and has no real repeat handling). Signed-off-by: David Härdeman Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/ati_remote.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/media/rc/ati_remote.c b/drivers/media/rc/ati_remote.c index 9cf3e69de16a..a4c6ad4f67c1 100644 --- a/drivers/media/rc/ati_remote.c +++ b/drivers/media/rc/ati_remote.c @@ -904,9 +904,6 @@ static int ati_remote_probe(struct usb_interface *interface, if (err) goto exit_kill_urbs; - /* use our delay for rc_dev */ - ati_remote->rdev->input_dev->rep[REP_DELAY] = repeat_delay; - /* Set up and register mouse input device */ if (mouse) { input_dev = input_allocate_device(); -- cgit v1.2.3-58-ga151 From a55a71da3db4acd5a374e0fb70a445266e71c6f1 Mon Sep 17 00:00:00 2001 From: David Härdeman Date: Mon, 1 May 2017 13:10:01 -0300 Subject: [media] rc-core: img-ir - leave the internals of rc_dev alone MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Changing the protocol does not imply that the keymap changes. Signed-off-by: David Härdeman Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/img-ir/img-ir-hw.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/media/rc/img-ir/img-ir-hw.c b/drivers/media/rc/img-ir/img-ir-hw.c index 431d33b36fb0..8d1439622533 100644 --- a/drivers/media/rc/img-ir/img-ir-hw.c +++ b/drivers/media/rc/img-ir/img-ir-hw.c @@ -702,10 +702,6 @@ static void img_ir_set_protocol(struct img_ir_priv *priv, u64 proto) { struct rc_dev *rdev = priv->hw.rdev; - spin_lock_irq(&rdev->rc_map.lock); - rdev->rc_map.rc_type = __ffs64(proto); - spin_unlock_irq(&rdev->rc_map.lock); - mutex_lock(&rdev->lock); rdev->enabled_protocols = proto; rdev->allowed_wakeup_protocols = proto; -- cgit v1.2.3-58-ga151 From 0781cb2860634656632c1722a1fe335ab9d138e5 Mon Sep 17 00:00:00 2001 From: David Härdeman Date: Mon, 1 May 2017 13:10:22 -0300 Subject: [media] rc-core: cx231xx - leave the internals of rc_dev alone MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Just some debug statements to change. Signed-off-by: David Härdeman Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/cx231xx/cx231xx-input.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/media/usb/cx231xx/cx231xx-input.c b/drivers/media/usb/cx231xx/cx231xx-input.c index 6e80f3c573f3..eecf074b0a48 100644 --- a/drivers/media/usb/cx231xx/cx231xx-input.c +++ b/drivers/media/usb/cx231xx/cx231xx-input.c @@ -30,7 +30,7 @@ static int get_key_isdbt(struct IR_i2c *ir, enum rc_type *protocol, int rc; u8 cmd, scancode; - dev_dbg(&ir->rc->input_dev->dev, "%s\n", __func__); + dev_dbg(&ir->rc->dev, "%s\n", __func__); /* poll IR chip */ rc = i2c_master_recv(ir->c, &cmd, 1); @@ -48,8 +48,7 @@ static int get_key_isdbt(struct IR_i2c *ir, enum rc_type *protocol, scancode = bitrev8(cmd); - dev_dbg(&ir->rc->input_dev->dev, "cmd %02x, scan = %02x\n", - cmd, scancode); + dev_dbg(&ir->rc->dev, "cmd %02x, scan = %02x\n", cmd, scancode); *protocol = RC_TYPE_OTHER; *pscancode = scancode; -- cgit v1.2.3-58-ga151 From 2270962c9dd341509e1a0abb7b32c1389045eb3e Mon Sep 17 00:00:00 2001 From: David Härdeman Date: Mon, 1 May 2017 13:10:27 -0300 Subject: [media] tm6000: key_addr is unused MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The key_addr member is only assigned, never used. So, remove it. Signed-off-by: David Härdeman Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/tm6000/tm6000-input.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/media/usb/tm6000/tm6000-input.c b/drivers/media/usb/tm6000/tm6000-input.c index 39c15bb2b20c..1a033f57fcc1 100644 --- a/drivers/media/usb/tm6000/tm6000-input.c +++ b/drivers/media/usb/tm6000/tm6000-input.c @@ -63,7 +63,6 @@ struct tm6000_IR { u8 wait:1; u8 pwled:2; u8 submit_urb:1; - u16 key_addr; struct urb *int_urb; /* IR device properties */ @@ -321,9 +320,6 @@ static int tm6000_ir_change_protocol(struct rc_dev *rc, u64 *rc_type) dprintk(2, "%s\n",__func__); - if ((rc->rc_map.scan) && (*rc_type == RC_BIT_NEC)) - ir->key_addr = ((rc->rc_map.scan[0].scancode >> 8) & 0xffff); - ir->rc_type = *rc_type; tm6000_ir_config(ir); -- cgit v1.2.3-58-ga151 From 32ddcbb5b8dc10bf922ff3a6666cb3748efca388 Mon Sep 17 00:00:00 2001 From: Ricardo Silva Date: Mon, 15 May 2017 16:40:12 -0300 Subject: [media] lirc_zilog: Fix whitespace style checks Fix style issues reported by checkpatch, affecting whitespace only: * CHECK: "Please don't use multiple blank lines". Two of these still triggering and left untouched because used for separating logical blocks (vars from functions, etc.). * CHECK: "spaces preferred around that ''". All fixed. * CHECK: "Alignment should match open parenthesis". All fixed except one on line 1161, left untouched for readability. Move towards recommended coding style without compromising readability. Signed-off-by: Ricardo Silva Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/lirc/lirc_zilog.c | 35 ++++++++++++++++----------------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/drivers/staging/media/lirc/lirc_zilog.c b/drivers/staging/media/lirc/lirc_zilog.c index df371e07e6c1..a8cfbee9c32e 100644 --- a/drivers/staging/media/lirc/lirc_zilog.c +++ b/drivers/staging/media/lirc/lirc_zilog.c @@ -472,7 +472,7 @@ static int lirc_thread(void *arg) /* if device not opened, we can sleep half a second */ if (atomic_read(&ir->open_count) == 0) { - schedule_timeout(HZ/2); + schedule_timeout(HZ / 2); continue; } @@ -499,7 +499,7 @@ static int lirc_thread(void *arg) /* safe read of a uint32 (always network byte order) */ static int read_uint32(unsigned char **data, - unsigned char *endp, unsigned int *val) + unsigned char *endp, unsigned int *val) { if (*data + 4 > endp) return 0; @@ -511,7 +511,7 @@ static int read_uint32(unsigned char **data, /* safe read of a uint8 */ static int read_uint8(unsigned char **data, - unsigned char *endp, unsigned char *val) + unsigned char *endp, unsigned char *val) { if (*data + 1 > endp) return 0; @@ -521,7 +521,7 @@ static int read_uint8(unsigned char **data, /* safe skipping of N bytes */ static int skip(unsigned char **data, - unsigned char *endp, unsigned int distance) + unsigned char *endp, unsigned int distance) { if (*data + distance > endp) return 0; @@ -531,7 +531,7 @@ static int skip(unsigned char **data, /* decompress key data into the given buffer */ static int get_key_data(unsigned char *buf, - unsigned int codeset, unsigned int key) + unsigned int codeset, unsigned int key) { unsigned char *data, *endp, *diffs, *key_block; unsigned char keys, ndiffs, id; @@ -801,7 +801,7 @@ static int fw_load(struct IR_tx *tx) goto corrupt; if (!read_uint32(&data, tx_data->endp, - &tx_data->num_code_sets)) + &tx_data->num_code_sets)) goto corrupt; dev_dbg(tx->ir->l.dev, "%u IR blaster codesets loaded\n", @@ -857,12 +857,12 @@ static int fw_load(struct IR_tx *tx) * global fixed */ if (!skip(&data, tx_data->endp, - 1 + TX_BLOCK_SIZE - num_global_fixed)) + 1 + TX_BLOCK_SIZE - num_global_fixed)) goto corrupt; /* Then we have keys-1 blocks of key id+diffs */ if (!skip(&data, tx_data->endp, - (ndiffs + 1) * (keys - 1))) + (ndiffs + 1) * (keys - 1))) goto corrupt; } ret = 0; @@ -1056,7 +1056,7 @@ static int send_code(struct IR_tx *tx, unsigned int code, unsigned int key) break; dev_dbg(tx->ir->l.dev, "NAK expected: i2c_master_send failed with %d (try %d)\n", - ret, i+1); + ret, i + 1); } if (ret != 1) { dev_err(tx->ir->l.dev, @@ -1222,7 +1222,7 @@ static unsigned int poll(struct file *filep, poll_table *wait) poll_wait(filep, &rbuf->wait_poll, wait); /* Indicate what ops could happen immediately without blocking */ - ret = lirc_buffer_empty(rbuf) ? 0 : (POLLIN|POLLRDNORM); + ret = lirc_buffer_empty(rbuf) ? 0 : (POLLIN | POLLRDNORM); dev_dbg(ir->l.dev, "poll result = %s\n", ret ? "POLLIN|POLLRDNORM" : "none"); @@ -1246,15 +1246,15 @@ static long ioctl(struct file *filep, unsigned int cmd, unsigned long arg) result = put_user(features, uptr); break; case LIRC_GET_REC_MODE: - if (!(features&LIRC_CAN_REC_MASK)) + if (!(features & LIRC_CAN_REC_MASK)) return -ENOSYS; result = put_user(LIRC_REC2MODE - (features&LIRC_CAN_REC_MASK), + (features & LIRC_CAN_REC_MASK), uptr); break; case LIRC_SET_REC_MODE: - if (!(features&LIRC_CAN_REC_MASK)) + if (!(features & LIRC_CAN_REC_MASK)) return -ENOSYS; result = get_user(mode, uptr); @@ -1262,13 +1262,13 @@ static long ioctl(struct file *filep, unsigned int cmd, unsigned long arg) result = -EINVAL; break; case LIRC_GET_SEND_MODE: - if (!(features&LIRC_CAN_SEND_MASK)) + if (!(features & LIRC_CAN_SEND_MASK)) return -ENOSYS; result = put_user(LIRC_MODE_PULSE, uptr); break; case LIRC_SET_SEND_MODE: - if (!(features&LIRC_CAN_SEND_MASK)) + if (!(features & LIRC_CAN_SEND_MASK)) return -ENOSYS; result = get_user(mode, uptr); @@ -1414,7 +1414,6 @@ static int ir_remove(struct i2c_client *client) return 0; } - /* ir_devices_lock must be held */ static struct IR *get_ir_device_by_adapter(struct i2c_adapter *adapter) { @@ -1455,7 +1454,7 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) return -ENXIO; pr_info("probing IR %s on %s (i2c-%d)\n", - tx_probe ? "Tx" : "Rx", adap->name, adap->nr); + tx_probe ? "Tx" : "Rx", adap->name, adap->nr); mutex_lock(&ir_devices_lock); @@ -1591,7 +1590,7 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) /* Proceed only if the Tx client is also ready */ if (tx == NULL) { pr_info("probe of IR Rx on %s (i2c-%d) done. Waiting on IR Tx.\n", - adap->name, adap->nr); + adap->name, adap->nr); goto out_ok; } } -- cgit v1.2.3-58-ga151 From 28b671b4bac52b4ad8e1a9e8aa7514da7efe924d Mon Sep 17 00:00:00 2001 From: Ricardo Silva Date: Mon, 15 May 2017 16:40:13 -0300 Subject: [media] lirc_zilog: Fix NULL comparisons style Fix all checkpatch reported issues for "CHECK: Comparison to NULL could be written...". Do these comparisons using the recommended coding style and consistent with other similar cases in the file, which already used the recommended way. Signed-off-by: Ricardo Silva Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/lirc/lirc_zilog.c | 48 ++++++++++++++++----------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/drivers/staging/media/lirc/lirc_zilog.c b/drivers/staging/media/lirc/lirc_zilog.c index a8cfbee9c32e..b9761a8b0bc2 100644 --- a/drivers/staging/media/lirc/lirc_zilog.c +++ b/drivers/staging/media/lirc/lirc_zilog.c @@ -215,7 +215,7 @@ static struct IR_rx *get_ir_rx(struct IR *ir) spin_lock(&ir->rx_ref_lock); rx = ir->rx; - if (rx != NULL) + if (rx) kref_get(&rx->ref); spin_unlock(&ir->rx_ref_lock); return rx; @@ -277,7 +277,7 @@ static struct IR_tx *get_ir_tx(struct IR *ir) spin_lock(&ir->tx_ref_lock); tx = ir->tx; - if (tx != NULL) + if (tx) kref_get(&tx->ref); spin_unlock(&ir->tx_ref_lock); return tx; @@ -327,12 +327,12 @@ static int add_to_buf(struct IR *ir) } rx = get_ir_rx(ir); - if (rx == NULL) + if (!rx) return -ENXIO; /* Ensure our rx->c i2c_client remains valid for the duration */ mutex_lock(&rx->client_lock); - if (rx->c == NULL) { + if (!rx->c) { mutex_unlock(&rx->client_lock); put_ir_rx(rx, false); return -ENXIO; @@ -388,7 +388,7 @@ static int add_to_buf(struct IR *ir) break; } schedule_timeout((100 * HZ + 999) / 1000); - if (tx != NULL) + if (tx) tx->need_boot = 1; ++failures; @@ -444,7 +444,7 @@ static int add_to_buf(struct IR *ir) } while (!lirc_buffer_full(rbuf)); mutex_unlock(&rx->client_lock); - if (tx != NULL) + if (tx) put_ir_tx(tx, false); put_ir_rx(rx, false); return ret; @@ -763,7 +763,7 @@ static int fw_load(struct IR_tx *tx) /* Parse the file */ tx_data = vmalloc(sizeof(*tx_data)); - if (tx_data == NULL) { + if (!tx_data) { release_firmware(fw_entry); ret = -ENOMEM; goto out; @@ -772,7 +772,7 @@ static int fw_load(struct IR_tx *tx) /* Copy the data so hotplug doesn't get confused and timeout */ tx_data->datap = vmalloc(fw_entry->size); - if (tx_data->datap == NULL) { + if (!tx_data->datap) { release_firmware(fw_entry); vfree(tx_data); ret = -ENOMEM; @@ -809,7 +809,7 @@ static int fw_load(struct IR_tx *tx) tx_data->code_sets = vmalloc( tx_data->num_code_sets * sizeof(char *)); - if (tx_data->code_sets == NULL) { + if (!tx_data->code_sets) { fw_unload_locked(); ret = -ENOMEM; goto out; @@ -896,7 +896,7 @@ static ssize_t read(struct file *filep, char __user *outbuf, size_t n, } rx = get_ir_rx(ir); - if (rx == NULL) + if (!rx) return -ENXIO; /* @@ -1102,12 +1102,12 @@ static ssize_t write(struct file *filep, const char __user *buf, size_t n, /* Get a struct IR_tx reference */ tx = get_ir_tx(ir); - if (tx == NULL) + if (!tx) return -ENXIO; /* Ensure our tx->c i2c_client remains valid for the duration */ mutex_lock(&tx->client_lock); - if (tx->c == NULL) { + if (!tx->c) { mutex_unlock(&tx->client_lock); put_ir_tx(tx, false); return -ENXIO; @@ -1206,7 +1206,7 @@ static unsigned int poll(struct file *filep, poll_table *wait) dev_dbg(ir->l.dev, "poll called\n"); rx = get_ir_rx(ir); - if (rx == NULL) { + if (!rx) { /* * Revisit this, if our poll function ever reports writeable * status for Tx @@ -1313,7 +1313,7 @@ static int open(struct inode *node, struct file *filep) /* find our IR struct */ ir = get_ir_device_by_minor(minor); - if (ir == NULL) + if (!ir) return -ENODEV; atomic_inc(&ir->open_count); @@ -1331,7 +1331,7 @@ static int close(struct inode *node, struct file *filep) /* find our IR struct */ struct IR *ir = filep->private_data; - if (ir == NULL) { + if (!ir) { pr_err("ir: close: no private_data attached to the file!\n"); return -ENODEV; } @@ -1395,7 +1395,7 @@ static int ir_remove(struct i2c_client *client) if (strncmp("ir_tx_z8", client->name, 8) == 0) { struct IR_tx *tx = i2c_get_clientdata(client); - if (tx != NULL) { + if (tx) { mutex_lock(&tx->client_lock); tx->c = NULL; mutex_unlock(&tx->client_lock); @@ -1404,7 +1404,7 @@ static int ir_remove(struct i2c_client *client) } else if (strncmp("ir_rx_z8", client->name, 8) == 0) { struct IR_rx *rx = i2c_get_clientdata(client); - if (rx != NULL) { + if (rx) { mutex_lock(&rx->client_lock); rx->c = NULL; mutex_unlock(&rx->client_lock); @@ -1460,7 +1460,7 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) /* Use a single struct IR instance for both the Rx and Tx functions */ ir = get_ir_device_by_adapter(adap); - if (ir == NULL) { + if (!ir) { ir = kzalloc(sizeof(struct IR), GFP_KERNEL); if (!ir) { ret = -ENOMEM; @@ -1534,7 +1534,7 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) fw_load(tx); /* Proceed only if the Rx client is also ready or not needed */ - if (rx == NULL && !tx_only) { + if (!rx && !tx_only) { dev_info(tx->ir->l.dev, "probe of IR Tx on %s (i2c-%d) done. Waiting on IR Rx.\n", adap->name, adap->nr); @@ -1588,7 +1588,7 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) } /* Proceed only if the Tx client is also ready */ - if (tx == NULL) { + if (!tx) { pr_info("probe of IR Rx on %s (i2c-%d) done. Waiting on IR Tx.\n", adap->name, adap->nr); goto out_ok; @@ -1609,9 +1609,9 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) adap->name, adap->nr, ir->l.minor); out_ok: - if (rx != NULL) + if (rx) put_ir_rx(rx, true); - if (tx != NULL) + if (tx) put_ir_tx(tx, true); put_ir_device(ir, true); dev_info(ir->l.dev, @@ -1621,10 +1621,10 @@ out_ok: return 0; out_put_xx: - if (rx != NULL) + if (rx) put_ir_rx(rx, true); out_put_tx: - if (tx != NULL) + if (tx) put_ir_tx(tx, true); out_put_ir: put_ir_device(ir, true); -- cgit v1.2.3-58-ga151 From 41a603f8d94c6e7627ddd4e13763b4523aa473ac Mon Sep 17 00:00:00 2001 From: Ricardo Silva Date: Mon, 15 May 2017 16:40:14 -0300 Subject: [media] lirc_zilog: Use __func__ for logging function name Fix all checkpatch reported issues for "CHECK: Prefer using '"%s...", __func__' to using '', ..." Use recommended style. Additionally, __func__ was already used in similar cases throughout the code, so make it all consistent. Signed-off-by: Ricardo Silva Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/lirc/lirc_zilog.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/staging/media/lirc/lirc_zilog.c b/drivers/staging/media/lirc/lirc_zilog.c index b9761a8b0bc2..4af0068ab0c5 100644 --- a/drivers/staging/media/lirc/lirc_zilog.c +++ b/drivers/staging/media/lirc/lirc_zilog.c @@ -1203,7 +1203,7 @@ static unsigned int poll(struct file *filep, poll_table *wait) struct lirc_buffer *rbuf = ir->l.rbuf; unsigned int ret; - dev_dbg(ir->l.dev, "poll called\n"); + dev_dbg(ir->l.dev, "%s called\n", __func__); rx = get_ir_rx(ir); if (!rx) { @@ -1211,7 +1211,7 @@ static unsigned int poll(struct file *filep, poll_table *wait) * Revisit this, if our poll function ever reports writeable * status for Tx */ - dev_dbg(ir->l.dev, "poll result = POLLERR\n"); + dev_dbg(ir->l.dev, "%s result = POLLERR\n", __func__); return POLLERR; } @@ -1224,7 +1224,7 @@ static unsigned int poll(struct file *filep, poll_table *wait) /* Indicate what ops could happen immediately without blocking */ ret = lirc_buffer_empty(rbuf) ? 0 : (POLLIN | POLLRDNORM); - dev_dbg(ir->l.dev, "poll result = %s\n", + dev_dbg(ir->l.dev, "%s result = %s\n", __func__, ret ? "POLLIN|POLLRDNORM" : "none"); return ret; } @@ -1332,7 +1332,8 @@ static int close(struct inode *node, struct file *filep) struct IR *ir = filep->private_data; if (!ir) { - pr_err("ir: close: no private_data attached to the file!\n"); + pr_err("ir: %s: no private_data attached to the file!\n", + __func__); return -ENODEV; } -- cgit v1.2.3-58-ga151 From d44e07c752ecfed1fb131cd0c5f103e299c22ab4 Mon Sep 17 00:00:00 2001 From: Ricardo Silva Date: Mon, 15 May 2017 16:40:15 -0300 Subject: [media] lirc_zilog: Use sizeof(*p) instead of sizeof(struct P) Fix all checkpatch reported issues for "CHECK: Prefer kzalloc(sizeof(*

)...) over kzalloc(sizeof(struct

)...)". Other similar case in the code already using recommended style, so make it all consistent with the recommended practice. Signed-off-by: Ricardo Silva Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/lirc/lirc_zilog.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/media/lirc/lirc_zilog.c b/drivers/staging/media/lirc/lirc_zilog.c index 4af0068ab0c5..48628bab62fc 100644 --- a/drivers/staging/media/lirc/lirc_zilog.c +++ b/drivers/staging/media/lirc/lirc_zilog.c @@ -1462,7 +1462,7 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) /* Use a single struct IR instance for both the Rx and Tx functions */ ir = get_ir_device_by_adapter(adap); if (!ir) { - ir = kzalloc(sizeof(struct IR), GFP_KERNEL); + ir = kzalloc(sizeof(*ir), GFP_KERNEL); if (!ir) { ret = -ENOMEM; goto out_no_ir; @@ -1502,7 +1502,7 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) rx = get_ir_rx(ir); /* Set up a struct IR_tx instance */ - tx = kzalloc(sizeof(struct IR_tx), GFP_KERNEL); + tx = kzalloc(sizeof(*tx), GFP_KERNEL); if (!tx) { ret = -ENOMEM; goto out_put_xx; @@ -1546,7 +1546,7 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) tx = get_ir_tx(ir); /* Set up a struct IR_rx instance */ - rx = kzalloc(sizeof(struct IR_rx), GFP_KERNEL); + rx = kzalloc(sizeof(*rx), GFP_KERNEL); if (!rx) { ret = -ENOMEM; goto out_put_xx; -- cgit v1.2.3-58-ga151 From da731edb5b7667d0752eeaf473896fc123b8e95f Mon Sep 17 00:00:00 2001 From: Ricardo Silva Date: Mon, 15 May 2017 16:40:16 -0300 Subject: [media] lirc_zilog: Fix unbalanced braces around if/else Fix all checkpatch reported issues for: * CHECK: "braces {} should be used on all arms of this statement". * CHECK: "Unbalanced braces around else statement". Make sure all if/else statements are balanced in terms of braces. Most cases in code are, but a few were left unbalanced, so put them all consistent with the recommended style. Signed-off-by: Ricardo Silva Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/lirc/lirc_zilog.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/staging/media/lirc/lirc_zilog.c b/drivers/staging/media/lirc/lirc_zilog.c index 48628bab62fc..015e41bd036e 100644 --- a/drivers/staging/media/lirc/lirc_zilog.c +++ b/drivers/staging/media/lirc/lirc_zilog.c @@ -545,9 +545,9 @@ static int get_key_data(unsigned char *buf, if (!read_uint32(&data, tx_data->endp, &i)) goto corrupt; - if (i == codeset) + if (i == codeset) { break; - else if (codeset > i) { + } else if (codeset > i) { base = pos + 1; --lim; } @@ -981,8 +981,9 @@ static int send_code(struct IR_tx *tx, unsigned int code, unsigned int key) "failed to get data for code %u, key %u -- check lircd.conf entries\n", code, key); return ret; - } else if (ret != 0) + } else if (ret != 0) { return ret; + } /* Send the data block */ ret = send_data_block(tx, data_block); @@ -1179,8 +1180,9 @@ static ssize_t write(struct file *filep, const char __user *buf, size_t n, schedule_timeout((100 * HZ + 999) / 1000); tx->need_boot = 1; ++failures; - } else + } else { i += sizeof(int); + } } /* Release i2c bus */ -- cgit v1.2.3-58-ga151 From 1beb5a7d1b17f187491599410709fd0544ea1c16 Mon Sep 17 00:00:00 2001 From: Sean Young Date: Wed, 17 May 2017 14:32:50 -0300 Subject: [media] sir_ir: attempt to free already free_irq If the probe fails (e.g. port already in use), rmmod causes null deref. Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/sir_ir.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/rc/sir_ir.c b/drivers/media/rc/sir_ir.c index 90a5f8fd5eea..c27d6b406d7c 100644 --- a/drivers/media/rc/sir_ir.c +++ b/drivers/media/rc/sir_ir.c @@ -381,6 +381,8 @@ static int sir_ir_probe(struct platform_device *dev) static int sir_ir_remove(struct platform_device *dev) { + drop_hardware(); + drop_port(); return 0; } @@ -421,8 +423,6 @@ pdev_alloc_fail: static void __exit sir_ir_exit(void) { - drop_hardware(); - drop_port(); platform_device_unregister(sir_ir_dev); platform_driver_unregister(&sir_ir_driver); } -- cgit v1.2.3-58-ga151 From b462e1b20dc39f4af05bcbf850e574c381b17038 Mon Sep 17 00:00:00 2001 From: Sean Young Date: Wed, 17 May 2017 14:32:51 -0300 Subject: [media] sir_ir: use dev managed resources Several error paths do not free up resources. This simplifies the code and fixes this. Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/sir_ir.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/media/rc/sir_ir.c b/drivers/media/rc/sir_ir.c index c27d6b406d7c..1ee41adb2ab1 100644 --- a/drivers/media/rc/sir_ir.c +++ b/drivers/media/rc/sir_ir.c @@ -334,14 +334,13 @@ static int init_port(void) setup_timer(&timerlist, sir_timeout, 0); /* get I/O port access and IRQ line */ - if (!request_region(io, 8, KBUILD_MODNAME)) { + if (!devm_request_region(&sir_ir_dev->dev, io, 8, KBUILD_MODNAME)) { pr_err("i/o port 0x%.4x already in use.\n", io); return -EBUSY; } - retval = request_irq(irq, sir_interrupt, 0, - KBUILD_MODNAME, NULL); + retval = devm_request_irq(&sir_ir_dev->dev, irq, sir_interrupt, 0, + KBUILD_MODNAME, NULL); if (retval < 0) { - release_region(io, 8); pr_err("IRQ %d already in use.\n", irq); return retval; } @@ -352,9 +351,7 @@ static int init_port(void) static void drop_port(void) { - free_irq(irq, NULL); del_timer_sync(&timerlist); - release_region(io, 8); } static int init_sir_ir(void) -- cgit v1.2.3-58-ga151 From f23f54087096e51ffc6765b7ddddb5f9eb517fa9 Mon Sep 17 00:00:00 2001 From: Sean Young Date: Wed, 17 May 2017 14:32:52 -0300 Subject: [media] sir_ir: remove init_port and drop_port functions These functions are too short and removing them makes the code more readable. Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/sir_ir.c | 27 +++++---------------------- 1 file changed, 5 insertions(+), 22 deletions(-) diff --git a/drivers/media/rc/sir_ir.c b/drivers/media/rc/sir_ir.c index 1ee41adb2ab1..fdac5704c30e 100644 --- a/drivers/media/rc/sir_ir.c +++ b/drivers/media/rc/sir_ir.c @@ -58,11 +58,9 @@ static int init_chrdev(void); static irqreturn_t sir_interrupt(int irq, void *dev_id); static void send_space(unsigned long len); static void send_pulse(unsigned long len); -static int init_hardware(void); +static void init_hardware(void); static void drop_hardware(void); /* Initialisation */ -static int init_port(void); -static void drop_port(void); static inline unsigned int sinp(int offset) { @@ -288,7 +286,7 @@ static void send_pulse(unsigned long len) } } -static int init_hardware(void) +static void init_hardware(void) { unsigned long flags; @@ -310,7 +308,6 @@ static int init_hardware(void) /* turn on UART */ outb(UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2, io + UART_MCR); spin_unlock_irqrestore(&hardware_lock, flags); - return 0; } static void drop_hardware(void) @@ -327,7 +324,7 @@ static void drop_hardware(void) /* SECTION: Initialisation */ -static int init_port(void) +static int init_sir_ir(void) { int retval; @@ -346,22 +343,8 @@ static int init_port(void) } pr_info("I/O port 0x%.4x, IRQ %d.\n", io, irq); - return 0; -} - -static void drop_port(void) -{ - del_timer_sync(&timerlist); -} - -static int init_sir_ir(void) -{ - int retval; - - retval = init_port(); - if (retval < 0) - return retval; init_hardware(); + return 0; } @@ -379,7 +362,7 @@ static int sir_ir_probe(struct platform_device *dev) static int sir_ir_remove(struct platform_device *dev) { drop_hardware(); - drop_port(); + del_timer_sync(&timerlist); return 0; } -- cgit v1.2.3-58-ga151 From c52f2ba747e14f4ae0b04397c0baac85754f2ca2 Mon Sep 17 00:00:00 2001 From: Sean Young Date: Wed, 17 May 2017 14:32:53 -0300 Subject: [media] sir_ir: remove init_chrdev and init_sir_ir functions Inlining these functions into the probe function makes it much more readable. Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/sir_ir.c | 58 ++++++++++++++++++----------------------------- 1 file changed, 22 insertions(+), 36 deletions(-) diff --git a/drivers/media/rc/sir_ir.c b/drivers/media/rc/sir_ir.c index fdac5704c30e..5ee3a2301c05 100644 --- a/drivers/media/rc/sir_ir.c +++ b/drivers/media/rc/sir_ir.c @@ -53,7 +53,6 @@ static DEFINE_SPINLOCK(hardware_lock); /* Communication with user-space */ static void add_read_queue(int flag, unsigned long val); -static int init_chrdev(void); /* Hardware */ static irqreturn_t sir_interrupt(int irq, void *dev_id); static void send_space(unsigned long len); @@ -120,28 +119,6 @@ static void add_read_queue(int flag, unsigned long val) ir_raw_event_store_with_filter(rcdev, &ev); } -static int init_chrdev(void) -{ - rcdev = devm_rc_allocate_device(&sir_ir_dev->dev, RC_DRIVER_IR_RAW); - if (!rcdev) - return -ENOMEM; - - rcdev->input_name = "SIR IrDA port"; - rcdev->input_phys = KBUILD_MODNAME "/input0"; - rcdev->input_id.bustype = BUS_HOST; - rcdev->input_id.vendor = 0x0001; - rcdev->input_id.product = 0x0001; - rcdev->input_id.version = 0x0100; - rcdev->tx_ir = sir_tx_ir; - rcdev->allowed_protocols = RC_BIT_ALL_IR_DECODER; - rcdev->driver_name = KBUILD_MODNAME; - rcdev->map_name = RC_MAP_RC6_MCE; - rcdev->timeout = IR_DEFAULT_TIMEOUT; - rcdev->dev.parent = &sir_ir_dev->dev; - - return devm_rc_register_device(&sir_ir_dev->dev, rcdev); -} - /* SECTION: Hardware */ static void sir_timeout(unsigned long data) { @@ -323,11 +300,27 @@ static void drop_hardware(void) } /* SECTION: Initialisation */ - -static int init_sir_ir(void) +static int sir_ir_probe(struct platform_device *dev) { int retval; + rcdev = devm_rc_allocate_device(&sir_ir_dev->dev, RC_DRIVER_IR_RAW); + if (!rcdev) + return -ENOMEM; + + rcdev->input_name = "SIR IrDA port"; + rcdev->input_phys = KBUILD_MODNAME "/input0"; + rcdev->input_id.bustype = BUS_HOST; + rcdev->input_id.vendor = 0x0001; + rcdev->input_id.product = 0x0001; + rcdev->input_id.version = 0x0100; + rcdev->tx_ir = sir_tx_ir; + rcdev->allowed_protocols = RC_BIT_ALL_IR_DECODER; + rcdev->driver_name = KBUILD_MODNAME; + rcdev->map_name = RC_MAP_RC6_MCE; + rcdev->timeout = IR_DEFAULT_TIMEOUT; + rcdev->dev.parent = &sir_ir_dev->dev; + setup_timer(&timerlist, sir_timeout, 0); /* get I/O port access and IRQ line */ @@ -343,20 +336,13 @@ static int init_sir_ir(void) } pr_info("I/O port 0x%.4x, IRQ %d.\n", io, irq); - init_hardware(); - - return 0; -} - -static int sir_ir_probe(struct platform_device *dev) -{ - int retval; - - retval = init_chrdev(); + retval = devm_rc_register_device(&sir_ir_dev->dev, rcdev); if (retval < 0) return retval; - return init_sir_ir(); + init_hardware(); + + return 0; } static int sir_ir_remove(struct platform_device *dev) -- cgit v1.2.3-58-ga151 From 614d651e1abda005ec9df5accef4b02aafcdf1ed Mon Sep 17 00:00:00 2001 From: Sean Young Date: Wed, 17 May 2017 14:32:54 -0300 Subject: [media] staging: remove todo and replace with lirc_zilog todo The lirc_zilog driver is the last remaining lirc driver, so the existing todo is no longer relevant. Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/lirc/TODO | 47 ++++++++++++++++++++++-------- drivers/staging/media/lirc/TODO.lirc_zilog | 36 ----------------------- 2 files changed, 35 insertions(+), 48 deletions(-) delete mode 100644 drivers/staging/media/lirc/TODO.lirc_zilog diff --git a/drivers/staging/media/lirc/TODO b/drivers/staging/media/lirc/TODO index cbea5d84fed3..a97800a8e127 100644 --- a/drivers/staging/media/lirc/TODO +++ b/drivers/staging/media/lirc/TODO @@ -1,13 +1,36 @@ -- All drivers should either be ported to ir-core, or dropped entirely - (see drivers/media/IR/mceusb.c vs. lirc_mceusb.c in lirc cvs for an - example of a previously completed port). - -- lirc_bt829 uses registers on a Mach64 VT, which has a separate kernel - framebuffer driver (atyfb) and userland X driver (mach64). It can't - simply be converted to a normal PCI driver, but ideally it should be - coordinated with the other drivers. - -Please send patches to: -Jarod Wilson -Greg Kroah-Hartman +1. Both ir-kbd-i2c and lirc_zilog provide support for RX events for +the chips supported by lirc_zilog. Before moving lirc_zilog out of staging: + +a. ir-kbd-i2c needs a module parameter added to allow the user to tell + ir-kbd-i2c to ignore Z8 IR units. + +b. lirc_zilog should provide Rx key presses to the rc core like ir-kbd-i2c + does. + + +2. lirc_zilog module ref-counting need examination. It has not been +verified that cdev and lirc_dev will take the proper module references on +lirc_zilog to prevent removal of lirc_zilog when the /dev/lircN device node +is open. + +(The good news is ref-counting of lirc_zilog internal structures appears to be +complete. Testing has shown the cx18 module can be unloaded out from under +irw + lircd + lirc_dev, with the /dev/lirc0 device node open, with no adverse +effects. The cx18 module could then be reloaded and irw properly began +receiving button presses again and ir_send worked without error.) + + +3. Bridge drivers, if able, should provide a chip reset() callback +to lirc_zilog via struct IR_i2c_init_data. cx18 and ivtv already have routines +to perform Z8 chip resets via GPIO manipulations. This would allow lirc_zilog +to bring the chip back to normal when it hangs, in the same places the +original lirc_pvr150 driver code does. This is not strictly needed, so it +is not required to move lirc_zilog out of staging. + +Note: Both lirc_zilog and ir-kbd-i2c support the Zilog Z8 for IR, as programmed +and installed on Hauppauge products. When working on either module, developers +must consider at least the following bridge drivers which mention an IR Rx unit +at address 0x71 (indicative of a Z8): + + ivtv cx18 hdpvr pvrusb2 bt8xx cx88 saa7134 diff --git a/drivers/staging/media/lirc/TODO.lirc_zilog b/drivers/staging/media/lirc/TODO.lirc_zilog deleted file mode 100644 index a97800a8e127..000000000000 --- a/drivers/staging/media/lirc/TODO.lirc_zilog +++ /dev/null @@ -1,36 +0,0 @@ -1. Both ir-kbd-i2c and lirc_zilog provide support for RX events for -the chips supported by lirc_zilog. Before moving lirc_zilog out of staging: - -a. ir-kbd-i2c needs a module parameter added to allow the user to tell - ir-kbd-i2c to ignore Z8 IR units. - -b. lirc_zilog should provide Rx key presses to the rc core like ir-kbd-i2c - does. - - -2. lirc_zilog module ref-counting need examination. It has not been -verified that cdev and lirc_dev will take the proper module references on -lirc_zilog to prevent removal of lirc_zilog when the /dev/lircN device node -is open. - -(The good news is ref-counting of lirc_zilog internal structures appears to be -complete. Testing has shown the cx18 module can be unloaded out from under -irw + lircd + lirc_dev, with the /dev/lirc0 device node open, with no adverse -effects. The cx18 module could then be reloaded and irw properly began -receiving button presses again and ir_send worked without error.) - - -3. Bridge drivers, if able, should provide a chip reset() callback -to lirc_zilog via struct IR_i2c_init_data. cx18 and ivtv already have routines -to perform Z8 chip resets via GPIO manipulations. This would allow lirc_zilog -to bring the chip back to normal when it hangs, in the same places the -original lirc_pvr150 driver code does. This is not strictly needed, so it -is not required to move lirc_zilog out of staging. - -Note: Both lirc_zilog and ir-kbd-i2c support the Zilog Z8 for IR, as programmed -and installed on Hauppauge products. When working on either module, developers -must consider at least the following bridge drivers which mention an IR Rx unit -at address 0x71 (indicative of a Z8): - - ivtv cx18 hdpvr pvrusb2 bt8xx cx88 saa7134 - -- cgit v1.2.3-58-ga151 From ca50c197bd9610ea984cfc0dc6855f183cbb46f8 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Fri, 12 Aug 2016 08:05:51 -0300 Subject: [media] v4l: fwnode: Support generic fwnode for parsing standardised properties The fwnode_handle is a more generic way than OF device_node to describe firmware nodes. Instead of the OF API, use more generic fwnode API to obtain the same information. As the V4L2 fwnode support will be required by a small minority of e.g. ACPI based systems (the same might actually go for OF), make this a module instead of embedding it in the videodev module. The origins of the V4L2 fwnode framework is in the V4L2 OF framework. Signed-off-by: Sakari Ailus Tested-by: Hans Verkuil Tested-by: Philipp Zabel Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/Kconfig | 3 + drivers/media/v4l2-core/Makefile | 1 + drivers/media/v4l2-core/v4l2-fwnode.c | 345 ++++++++++++++++++++++++++++++++++ include/media/v4l2-fwnode.h | 104 ++++++++++ 4 files changed, 453 insertions(+) create mode 100644 drivers/media/v4l2-core/v4l2-fwnode.c create mode 100644 include/media/v4l2-fwnode.h diff --git a/drivers/media/v4l2-core/Kconfig b/drivers/media/v4l2-core/Kconfig index 6b1b78ff1417..a35c33686abf 100644 --- a/drivers/media/v4l2-core/Kconfig +++ b/drivers/media/v4l2-core/Kconfig @@ -55,6 +55,9 @@ config V4L2_FLASH_LED_CLASS When in doubt, say N. +config V4L2_FWNODE + tristate + # Used by drivers that need Videobuf modules config VIDEOBUF_GEN tristate diff --git a/drivers/media/v4l2-core/Makefile b/drivers/media/v4l2-core/Makefile index 795a5352761d..cf77a6328feb 100644 --- a/drivers/media/v4l2-core/Makefile +++ b/drivers/media/v4l2-core/Makefile @@ -13,6 +13,7 @@ endif ifeq ($(CONFIG_OF),y) videodev-objs += v4l2-of.o endif +obj-$(CONFIG_V4L2_FWNODE) += v4l2-fwnode.o ifeq ($(CONFIG_TRACEPOINTS),y) videodev-objs += vb2-trace.o v4l2-trace.o endif diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c b/drivers/media/v4l2-core/v4l2-fwnode.c new file mode 100644 index 000000000000..153c53ca3925 --- /dev/null +++ b/drivers/media/v4l2-core/v4l2-fwnode.c @@ -0,0 +1,345 @@ +/* + * V4L2 fwnode binding parsing library + * + * The origins of the V4L2 fwnode library are in V4L2 OF library that + * formerly was located in v4l2-of.c. + * + * Copyright (c) 2016 Intel Corporation. + * Author: Sakari Ailus + * + * Copyright (C) 2012 - 2013 Samsung Electronics Co., Ltd. + * Author: Sylwester Nawrocki + * + * Copyright (C) 2012 Renesas Electronics Corp. + * Author: Guennadi Liakhovetski + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include + +static int v4l2_fwnode_endpoint_parse_csi_bus(struct fwnode_handle *fwnode, + struct v4l2_fwnode_endpoint *vep) +{ + struct v4l2_fwnode_bus_mipi_csi2 *bus = &vep->bus.mipi_csi2; + bool have_clk_lane = false; + unsigned int flags = 0, lanes_used = 0; + unsigned int i; + u32 v; + int rval; + + rval = fwnode_property_read_u32_array(fwnode, "data-lanes", NULL, 0); + if (rval > 0) { + u32 array[ARRAY_SIZE(bus->data_lanes)]; + + bus->num_data_lanes = + min_t(int, ARRAY_SIZE(bus->data_lanes), rval); + + fwnode_property_read_u32_array(fwnode, "data-lanes", array, + bus->num_data_lanes); + + for (i = 0; i < bus->num_data_lanes; i++) { + if (lanes_used & BIT(array[i])) + pr_warn("duplicated lane %u in data-lanes\n", + array[i]); + lanes_used |= BIT(array[i]); + + bus->data_lanes[i] = array[i]; + } + } + + rval = fwnode_property_read_u32_array(fwnode, "lane-polarities", NULL, + 0); + if (rval > 0) { + u32 array[ARRAY_SIZE(bus->lane_polarities)]; + + if (rval < 1 + bus->num_data_lanes /* clock + data */) { + pr_warn("too few lane-polarities entries (need %u, got %u)\n", + 1 + bus->num_data_lanes, rval); + return -EINVAL; + } + + fwnode_property_read_u32_array(fwnode, "lane-polarities", array, + 1 + bus->num_data_lanes); + + for (i = 0; i < 1 + bus->num_data_lanes; i++) + bus->lane_polarities[i] = array[i]; + } + + if (!fwnode_property_read_u32(fwnode, "clock-lanes", &v)) { + if (lanes_used & BIT(v)) + pr_warn("duplicated lane %u in clock-lanes\n", v); + lanes_used |= BIT(v); + + bus->clock_lane = v; + have_clk_lane = true; + } + + if (fwnode_property_present(fwnode, "clock-noncontinuous")) + flags |= V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK; + else if (have_clk_lane || bus->num_data_lanes > 0) + flags |= V4L2_MBUS_CSI2_CONTINUOUS_CLOCK; + + bus->flags = flags; + vep->bus_type = V4L2_MBUS_CSI2; + + return 0; +} + +static void v4l2_fwnode_endpoint_parse_parallel_bus( + struct fwnode_handle *fwnode, struct v4l2_fwnode_endpoint *vep) +{ + struct v4l2_fwnode_bus_parallel *bus = &vep->bus.parallel; + unsigned int flags = 0; + u32 v; + + if (!fwnode_property_read_u32(fwnode, "hsync-active", &v)) + flags |= v ? V4L2_MBUS_HSYNC_ACTIVE_HIGH : + V4L2_MBUS_HSYNC_ACTIVE_LOW; + + if (!fwnode_property_read_u32(fwnode, "vsync-active", &v)) + flags |= v ? V4L2_MBUS_VSYNC_ACTIVE_HIGH : + V4L2_MBUS_VSYNC_ACTIVE_LOW; + + if (!fwnode_property_read_u32(fwnode, "field-even-active", &v)) + flags |= v ? V4L2_MBUS_FIELD_EVEN_HIGH : + V4L2_MBUS_FIELD_EVEN_LOW; + if (flags) + vep->bus_type = V4L2_MBUS_PARALLEL; + else + vep->bus_type = V4L2_MBUS_BT656; + + if (!fwnode_property_read_u32(fwnode, "pclk-sample", &v)) + flags |= v ? V4L2_MBUS_PCLK_SAMPLE_RISING : + V4L2_MBUS_PCLK_SAMPLE_FALLING; + + if (!fwnode_property_read_u32(fwnode, "data-active", &v)) + flags |= v ? V4L2_MBUS_DATA_ACTIVE_HIGH : + V4L2_MBUS_DATA_ACTIVE_LOW; + + if (fwnode_property_present(fwnode, "slave-mode")) + flags |= V4L2_MBUS_SLAVE; + else + flags |= V4L2_MBUS_MASTER; + + if (!fwnode_property_read_u32(fwnode, "bus-width", &v)) + bus->bus_width = v; + + if (!fwnode_property_read_u32(fwnode, "data-shift", &v)) + bus->data_shift = v; + + if (!fwnode_property_read_u32(fwnode, "sync-on-green-active", &v)) + flags |= v ? V4L2_MBUS_VIDEO_SOG_ACTIVE_HIGH : + V4L2_MBUS_VIDEO_SOG_ACTIVE_LOW; + + bus->flags = flags; + +} + +/** + * v4l2_fwnode_endpoint_parse() - parse all fwnode node properties + * @fwnode: pointer to the endpoint's fwnode handle + * @vep: pointer to the V4L2 fwnode data structure + * + * All properties are optional. If none are found, we don't set any flags. This + * means the port has a static configuration and no properties have to be + * specified explicitly. If any properties that identify the bus as parallel + * are found and slave-mode isn't set, we set V4L2_MBUS_MASTER. Similarly, if + * we recognise the bus as serial CSI-2 and clock-noncontinuous isn't set, we + * set the V4L2_MBUS_CSI2_CONTINUOUS_CLOCK flag. The caller should hold a + * reference to @fwnode. + * + * NOTE: This function does not parse properties the size of which is variable + * without a low fixed limit. Please use v4l2_fwnode_endpoint_alloc_parse() in + * new drivers instead. + * + * Return: 0 on success or a negative error code on failure. + */ +int v4l2_fwnode_endpoint_parse(struct fwnode_handle *fwnode, + struct v4l2_fwnode_endpoint *vep) +{ + int rval; + + fwnode_graph_parse_endpoint(fwnode, &vep->base); + + /* Zero fields from bus_type to until the end */ + memset(&vep->bus_type, 0, sizeof(*vep) - + offsetof(typeof(*vep), bus_type)); + + rval = v4l2_fwnode_endpoint_parse_csi_bus(fwnode, vep); + if (rval) + return rval; + /* + * Parse the parallel video bus properties only if none + * of the MIPI CSI-2 specific properties were found. + */ + if (vep->bus.mipi_csi2.flags == 0) + v4l2_fwnode_endpoint_parse_parallel_bus(fwnode, vep); + + return 0; +} +EXPORT_SYMBOL_GPL(v4l2_fwnode_endpoint_parse); + +/* + * v4l2_fwnode_endpoint_free() - free the V4L2 fwnode acquired by + * v4l2_fwnode_endpoint_alloc_parse() + * @vep - the V4L2 fwnode the resources of which are to be released + * + * It is safe to call this function with NULL argument or on a V4L2 fwnode the + * parsing of which failed. + */ +void v4l2_fwnode_endpoint_free(struct v4l2_fwnode_endpoint *vep) +{ + if (IS_ERR_OR_NULL(vep)) + return; + + kfree(vep->link_frequencies); + kfree(vep); +} +EXPORT_SYMBOL_GPL(v4l2_fwnode_endpoint_free); + +/** + * v4l2_fwnode_endpoint_alloc_parse() - parse all fwnode node properties + * @fwnode: pointer to the endpoint's fwnode handle + * + * All properties are optional. If none are found, we don't set any flags. This + * means the port has a static configuration and no properties have to be + * specified explicitly. If any properties that identify the bus as parallel + * are found and slave-mode isn't set, we set V4L2_MBUS_MASTER. Similarly, if + * we recognise the bus as serial CSI-2 and clock-noncontinuous isn't set, we + * set the V4L2_MBUS_CSI2_CONTINUOUS_CLOCK flag. The caller should hold a + * reference to @fwnode. + * + * v4l2_fwnode_endpoint_alloc_parse() has two important differences to + * v4l2_fwnode_endpoint_parse(): + * + * 1. It also parses variable size data. + * + * 2. The memory it has allocated to store the variable size data must be freed + * using v4l2_fwnode_endpoint_free() when no longer needed. + * + * Return: Pointer to v4l2_fwnode_endpoint if successful, on an error pointer + * on error. + */ +struct v4l2_fwnode_endpoint *v4l2_fwnode_endpoint_alloc_parse( + struct fwnode_handle *fwnode) +{ + struct v4l2_fwnode_endpoint *vep; + int rval; + + vep = kzalloc(sizeof(*vep), GFP_KERNEL); + if (!vep) + return ERR_PTR(-ENOMEM); + + rval = v4l2_fwnode_endpoint_parse(fwnode, vep); + if (rval < 0) + goto out_err; + + rval = fwnode_property_read_u64_array(fwnode, "link-frequencies", + NULL, 0); + if (rval < 0) + goto out_err; + + vep->link_frequencies = + kmalloc_array(rval, sizeof(*vep->link_frequencies), GFP_KERNEL); + if (!vep->link_frequencies) { + rval = -ENOMEM; + goto out_err; + } + + vep->nr_of_link_frequencies = rval; + + rval = fwnode_property_read_u64_array(fwnode, "link-frequencies", + vep->link_frequencies, + vep->nr_of_link_frequencies); + if (rval < 0) + goto out_err; + + return vep; + +out_err: + v4l2_fwnode_endpoint_free(vep); + return ERR_PTR(rval); +} +EXPORT_SYMBOL_GPL(v4l2_fwnode_endpoint_alloc_parse); + +/** + * v4l2_fwnode_endpoint_parse_link() - parse a link between two endpoints + * @__fwnode: pointer to the endpoint's fwnode at the local end of the link + * @link: pointer to the V4L2 fwnode link data structure + * + * Fill the link structure with the local and remote nodes and port numbers. + * The local_node and remote_node fields are set to point to the local and + * remote port's parent nodes respectively (the port parent node being the + * parent node of the port node if that node isn't a 'ports' node, or the + * grand-parent node of the port node otherwise). + * + * A reference is taken to both the local and remote nodes, the caller must use + * v4l2_fwnode_endpoint_put_link() to drop the references when done with the + * link. + * + * Return: 0 on success, or -ENOLINK if the remote endpoint fwnode can't be + * found. + */ +int v4l2_fwnode_parse_link(struct fwnode_handle *__fwnode, + struct v4l2_fwnode_link *link) +{ + const char *port_prop = is_of_node(__fwnode) ? "reg" : "port"; + struct fwnode_handle *fwnode; + + memset(link, 0, sizeof(*link)); + + fwnode = fwnode_get_parent(__fwnode); + fwnode_property_read_u32(fwnode, port_prop, &link->local_port); + fwnode = fwnode_get_next_parent(fwnode); + if (is_of_node(fwnode) && + of_node_cmp(to_of_node(fwnode)->name, "ports") == 0) + fwnode = fwnode_get_next_parent(fwnode); + link->local_node = fwnode; + + fwnode = fwnode_graph_get_remote_endpoint(__fwnode); + if (!fwnode) { + fwnode_handle_put(fwnode); + return -ENOLINK; + } + + fwnode = fwnode_get_parent(fwnode); + fwnode_property_read_u32(fwnode, port_prop, &link->remote_port); + fwnode = fwnode_get_next_parent(fwnode); + if (is_of_node(fwnode) && + of_node_cmp(to_of_node(fwnode)->name, "ports") == 0) + fwnode = fwnode_get_next_parent(fwnode); + link->remote_node = fwnode; + + return 0; +} +EXPORT_SYMBOL_GPL(v4l2_fwnode_parse_link); + +/** + * v4l2_fwnode_put_link() - drop references to nodes in a link + * @link: pointer to the V4L2 fwnode link data structure + * + * Drop references to the local and remote nodes in the link. This function + * must be called on every link parsed with v4l2_fwnode_parse_link(). + */ +void v4l2_fwnode_put_link(struct v4l2_fwnode_link *link) +{ + fwnode_handle_put(link->local_node); + fwnode_handle_put(link->remote_node); +} +EXPORT_SYMBOL_GPL(v4l2_fwnode_put_link); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Sakari Ailus "); +MODULE_AUTHOR("Sylwester Nawrocki "); +MODULE_AUTHOR("Guennadi Liakhovetski "); diff --git a/include/media/v4l2-fwnode.h b/include/media/v4l2-fwnode.h new file mode 100644 index 000000000000..ecc1233a873e --- /dev/null +++ b/include/media/v4l2-fwnode.h @@ -0,0 +1,104 @@ +/* + * V4L2 fwnode binding parsing library + * + * Copyright (c) 2016 Intel Corporation. + * Author: Sakari Ailus + * + * Copyright (C) 2012 - 2013 Samsung Electronics Co., Ltd. + * Author: Sylwester Nawrocki + * + * Copyright (C) 2012 Renesas Electronics Corp. + * Author: Guennadi Liakhovetski + * + * 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. + */ +#ifndef _V4L2_FWNODE_H +#define _V4L2_FWNODE_H + +#include +#include +#include +#include + +#include + +struct fwnode_handle; + +/** + * struct v4l2_fwnode_bus_mipi_csi2 - MIPI CSI-2 bus data structure + * @flags: media bus (V4L2_MBUS_*) flags + * @data_lanes: an array of physical data lane indexes + * @clock_lane: physical lane index of the clock lane + * @num_data_lanes: number of data lanes + * @lane_polarities: polarity of the lanes. The order is the same of + * the physical lanes. + */ +struct v4l2_fwnode_bus_mipi_csi2 { + unsigned int flags; + unsigned char data_lanes[4]; + unsigned char clock_lane; + unsigned short num_data_lanes; + bool lane_polarities[5]; +}; + +/** + * struct v4l2_fwnode_bus_parallel - parallel data bus data structure + * @flags: media bus (V4L2_MBUS_*) flags + * @bus_width: bus width in bits + * @data_shift: data shift in bits + */ +struct v4l2_fwnode_bus_parallel { + unsigned int flags; + unsigned char bus_width; + unsigned char data_shift; +}; + +/** + * struct v4l2_fwnode_endpoint - the endpoint data structure + * @base: fwnode endpoint of the v4l2_fwnode + * @bus_type: bus type + * @bus: bus configuration data structure + * @link_frequencies: array of supported link frequencies + * @nr_of_link_frequencies: number of elements in link_frequenccies array + */ +struct v4l2_fwnode_endpoint { + struct fwnode_endpoint base; + /* + * Fields below this line will be zeroed by + * v4l2_fwnode_parse_endpoint() + */ + enum v4l2_mbus_type bus_type; + union { + struct v4l2_fwnode_bus_parallel parallel; + struct v4l2_fwnode_bus_mipi_csi2 mipi_csi2; + } bus; + u64 *link_frequencies; + unsigned int nr_of_link_frequencies; +}; + +/** + * struct v4l2_fwnode_link - a link between two endpoints + * @local_node: pointer to device_node of this endpoint + * @local_port: identifier of the port this endpoint belongs to + * @remote_node: pointer to device_node of the remote endpoint + * @remote_port: identifier of the port the remote endpoint belongs to + */ +struct v4l2_fwnode_link { + struct fwnode_handle *local_node; + unsigned int local_port; + struct fwnode_handle *remote_node; + unsigned int remote_port; +}; + +int v4l2_fwnode_endpoint_parse(struct fwnode_handle *fwnode, + struct v4l2_fwnode_endpoint *vep); +struct v4l2_fwnode_endpoint *v4l2_fwnode_endpoint_alloc_parse( + struct fwnode_handle *fwnode); +void v4l2_fwnode_endpoint_free(struct v4l2_fwnode_endpoint *vep); +int v4l2_fwnode_parse_link(struct fwnode_handle *fwnode, + struct v4l2_fwnode_link *link); +void v4l2_fwnode_put_link(struct v4l2_fwnode_link *link); + +#endif /* _V4L2_FWNODE_H */ -- cgit v1.2.3-58-ga151 From ecdf0cfe711b3780e829a6e24ffd3275f9cbfc2a Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Tue, 16 Aug 2016 06:54:59 -0300 Subject: [media] v4l: async: Add fwnode match support Add fwnode matching to complement OF node matching. And fwnode may also be an OF node. Do not enable fwnode matching yet. It will replace OF matching soon. Signed-off-by: Sakari Ailus Tested-by: Hans Verkuil Tested-by: Philipp Zabel Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-async.c | 15 +++++++++++++++ include/media/v4l2-async.h | 5 +++++ include/media/v4l2-subdev.h | 3 +++ 3 files changed, 23 insertions(+) diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c index 96cc733f35ef..ff32f95d94f0 100644 --- a/drivers/media/v4l2-core/v4l2-async.c +++ b/drivers/media/v4l2-core/v4l2-async.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -46,6 +47,16 @@ static bool match_of(struct v4l2_subdev *sd, struct v4l2_async_subdev *asd) of_node_full_name(asd->match.of.node)); } +static bool match_fwnode(struct v4l2_subdev *sd, struct v4l2_async_subdev *asd) +{ + if (!is_of_node(sd->fwnode) || !is_of_node(asd->match.fwnode.fwnode)) + return sd->fwnode == asd->match.fwnode.fwnode; + + return !of_node_cmp(of_node_full_name(to_of_node(sd->fwnode)), + of_node_full_name( + to_of_node(asd->match.fwnode.fwnode))); +} + static bool match_custom(struct v4l2_subdev *sd, struct v4l2_async_subdev *asd) { if (!asd->match.custom.match) @@ -80,6 +91,9 @@ static struct v4l2_async_subdev *v4l2_async_belongs(struct v4l2_async_notifier * case V4L2_ASYNC_MATCH_OF: match = match_of; break; + case V4L2_ASYNC_MATCH_FWNODE: + match = match_fwnode; + break; default: /* Cannot happen, unless someone breaks us */ WARN_ON(true); @@ -158,6 +172,7 @@ int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev, case V4L2_ASYNC_MATCH_DEVNAME: case V4L2_ASYNC_MATCH_I2C: case V4L2_ASYNC_MATCH_OF: + case V4L2_ASYNC_MATCH_FWNODE: break; default: dev_err(notifier->v4l2_dev ? notifier->v4l2_dev->dev : NULL, diff --git a/include/media/v4l2-async.h b/include/media/v4l2-async.h index 8e2a236a4d03..c3695fa2c903 100644 --- a/include/media/v4l2-async.h +++ b/include/media/v4l2-async.h @@ -32,6 +32,7 @@ struct v4l2_async_notifier; * @V4L2_ASYNC_MATCH_DEVNAME: Match will use the device name * @V4L2_ASYNC_MATCH_I2C: Match will check for I2C adapter ID and address * @V4L2_ASYNC_MATCH_OF: Match will use OF node + * @V4L2_ASYNC_MATCH_FWNODE: Match will use firmware node * * This enum is used by the asyncrhronous sub-device logic to define the * algorithm that will be used to match an asynchronous device. @@ -41,6 +42,7 @@ enum v4l2_async_match_type { V4L2_ASYNC_MATCH_DEVNAME, V4L2_ASYNC_MATCH_I2C, V4L2_ASYNC_MATCH_OF, + V4L2_ASYNC_MATCH_FWNODE, }; /** @@ -57,6 +59,9 @@ struct v4l2_async_subdev { struct { const struct device_node *node; } of; + struct { + struct fwnode_handle *fwnode; + } fwnode; struct { const char *name; } device_name; diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index 0ab1c5df6fac..5f1669c45642 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -788,6 +788,8 @@ struct v4l2_subdev_platform_data { * @devnode: subdev device node * @dev: pointer to the physical device, if any * @of_node: The device_node of the subdev, usually the same as dev->of_node. + * @fwnode: The fwnode_handle of the subdev, usually the same as + * either dev->of_node->fwnode or dev->fwnode (whichever is non-NULL). * @async_list: Links this subdev to a global subdev_list or @notifier->done * list. * @asd: Pointer to respective &struct v4l2_async_subdev. @@ -819,6 +821,7 @@ struct v4l2_subdev { struct video_device *devnode; struct device *dev; struct device_node *of_node; + struct fwnode_handle *fwnode; struct list_head async_list; struct v4l2_async_subdev *asd; struct v4l2_async_notifier *notifier; -- cgit v1.2.3-58-ga151 From 048ea05b4f4c8f8cf0a9d4c5fc7d16f867160764 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Wed, 31 Aug 2016 04:28:46 -0300 Subject: [media] v4l: flash led class: Use fwnode_handle instead of device_node in init Pass the more generic fwnode_handle to the init function than the device_node. Signed-off-by: Sakari Ailus Tested-by: Hans Verkuil Tested-by: Philipp Zabel Signed-off-by: Mauro Carvalho Chehab --- drivers/leds/leds-aat1290.c | 5 +++-- drivers/leds/leds-max77693.c | 5 +++-- drivers/media/v4l2-core/v4l2-flash-led-class.c | 12 ++++++------ include/media/v4l2-flash-led-class.h | 6 +++--- 4 files changed, 15 insertions(+), 13 deletions(-) diff --git a/drivers/leds/leds-aat1290.c b/drivers/leds/leds-aat1290.c index def3cf9f7e92..a21e19297745 100644 --- a/drivers/leds/leds-aat1290.c +++ b/drivers/leds/leds-aat1290.c @@ -503,8 +503,9 @@ static int aat1290_led_probe(struct platform_device *pdev) aat1290_init_v4l2_flash_config(led, &led_cfg, &v4l2_sd_cfg); /* Create V4L2 Flash subdev. */ - led->v4l2_flash = v4l2_flash_init(dev, sub_node, fled_cdev, NULL, - &v4l2_flash_ops, &v4l2_sd_cfg); + led->v4l2_flash = v4l2_flash_init(dev, of_fwnode_handle(sub_node), + fled_cdev, NULL, &v4l2_flash_ops, + &v4l2_sd_cfg); if (IS_ERR(led->v4l2_flash)) { ret = PTR_ERR(led->v4l2_flash); goto error_v4l2_flash_init; diff --git a/drivers/leds/leds-max77693.c b/drivers/leds/leds-max77693.c index 1eb58ef6aefe..2d3062d53325 100644 --- a/drivers/leds/leds-max77693.c +++ b/drivers/leds/leds-max77693.c @@ -930,8 +930,9 @@ static int max77693_register_led(struct max77693_sub_led *sub_led, max77693_init_v4l2_flash_config(sub_led, led_cfg, &v4l2_sd_cfg); /* Register in the V4L2 subsystem. */ - sub_led->v4l2_flash = v4l2_flash_init(dev, sub_node, fled_cdev, NULL, - &v4l2_flash_ops, &v4l2_sd_cfg); + sub_led->v4l2_flash = v4l2_flash_init(dev, of_fwnode_handle(sub_node), + fled_cdev, NULL, &v4l2_flash_ops, + &v4l2_sd_cfg); if (IS_ERR(sub_led->v4l2_flash)) { ret = PTR_ERR(sub_led->v4l2_flash); goto err_v4l2_flash_init; diff --git a/drivers/media/v4l2-core/v4l2-flash-led-class.c b/drivers/media/v4l2-core/v4l2-flash-led-class.c index 794e563f24f8..7b8288108e8a 100644 --- a/drivers/media/v4l2-core/v4l2-flash-led-class.c +++ b/drivers/media/v4l2-core/v4l2-flash-led-class.c @@ -12,7 +12,7 @@ #include #include #include -#include +#include #include #include #include @@ -612,7 +612,7 @@ static const struct v4l2_subdev_internal_ops v4l2_flash_subdev_internal_ops = { static const struct v4l2_subdev_ops v4l2_flash_subdev_ops; struct v4l2_flash *v4l2_flash_init( - struct device *dev, struct device_node *of_node, + struct device *dev, struct fwnode_handle *fwn, struct led_classdev_flash *fled_cdev, struct led_classdev_flash *iled_cdev, const struct v4l2_flash_ops *ops, @@ -638,7 +638,7 @@ struct v4l2_flash *v4l2_flash_init( v4l2_flash->iled_cdev = iled_cdev; v4l2_flash->ops = ops; sd->dev = dev; - sd->of_node = of_node ? of_node : led_cdev->dev->of_node; + sd->fwnode = fwn ? fwn : dev_fwnode(led_cdev->dev); v4l2_subdev_init(sd, &v4l2_flash_subdev_ops); sd->internal_ops = &v4l2_flash_subdev_internal_ops; sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; @@ -654,7 +654,7 @@ struct v4l2_flash *v4l2_flash_init( if (ret < 0) goto err_init_controls; - of_node_get(sd->of_node); + fwnode_handle_get(sd->fwnode); ret = v4l2_async_register_subdev(sd); if (ret < 0) @@ -663,7 +663,7 @@ struct v4l2_flash *v4l2_flash_init( return v4l2_flash; err_async_register_sd: - of_node_put(sd->of_node); + fwnode_handle_put(sd->fwnode); v4l2_ctrl_handler_free(sd->ctrl_handler); err_init_controls: media_entity_cleanup(&sd->entity); @@ -683,7 +683,7 @@ void v4l2_flash_release(struct v4l2_flash *v4l2_flash) v4l2_async_unregister_subdev(sd); - of_node_put(sd->of_node); + fwnode_handle_put(sd->fwnode); v4l2_ctrl_handler_free(sd->ctrl_handler); media_entity_cleanup(&sd->entity); diff --git a/include/media/v4l2-flash-led-class.h b/include/media/v4l2-flash-led-class.h index b0fe4d6f4a5f..f9dcd54c1745 100644 --- a/include/media/v4l2-flash-led-class.h +++ b/include/media/v4l2-flash-led-class.h @@ -108,7 +108,7 @@ static inline struct v4l2_flash *v4l2_ctrl_to_v4l2_flash(struct v4l2_ctrl *c) /** * v4l2_flash_init - initialize V4L2 flash led sub-device * @dev: flash device, e.g. an I2C device - * @of_node: of_node of the LED, may be NULL if the same as device's + * @fwn: fwnode_handle of the LED, may be NULL if the same as device's * @fled_cdev: LED flash class device to wrap * @iled_cdev: LED flash class device representing indicator LED associated * with fled_cdev, may be NULL @@ -122,7 +122,7 @@ static inline struct v4l2_flash *v4l2_ctrl_to_v4l2_flash(struct v4l2_ctrl *c) * PTR_ERR() to obtain the numeric return value. */ struct v4l2_flash *v4l2_flash_init( - struct device *dev, struct device_node *of_node, + struct device *dev, struct fwnode_handle *fwn, struct led_classdev_flash *fled_cdev, struct led_classdev_flash *iled_cdev, const struct v4l2_flash_ops *ops, @@ -138,7 +138,7 @@ void v4l2_flash_release(struct v4l2_flash *v4l2_flash); #else static inline struct v4l2_flash *v4l2_flash_init( - struct device *dev, struct device_node *of_node, + struct device *dev, struct fwnode_handle *fwn, struct led_classdev_flash *fled_cdev, struct led_classdev_flash *iled_cdev, const struct v4l2_flash_ops *ops, -- cgit v1.2.3-58-ga151 From 859969b38e2e9352f0227e1ef0be1dff4a3b7299 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Fri, 26 Aug 2016 20:17:25 -0300 Subject: [media] v4l: Switch from V4L2 OF not V4L2 fwnode API Switch users of the v4l2_of_ APIs to the more generic v4l2_fwnode_ APIs. Async OF matching is replaced by fwnode matching and OF matching support is removed. Signed-off-by: Sakari Ailus Acked-by: Benoit Parrot # i2c/ov2569.c, am437x/am437x-vpfe.c and ti-vpe/cal.c Tested-by: Hans Verkuil # Atmel sama5d3 board + ov2640 sensor Tested-by: Philipp Zabel Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/Kconfig | 11 +++++ drivers/media/i2c/adv7604.c | 7 +-- drivers/media/i2c/mt9v032.c | 7 +-- drivers/media/i2c/ov2659.c | 8 ++-- drivers/media/i2c/ov5645.c | 7 +-- drivers/media/i2c/ov5647.c | 7 +-- drivers/media/i2c/s5c73m3/s5c73m3-core.c | 7 +-- drivers/media/i2c/s5k5baf.c | 6 +-- drivers/media/i2c/smiapp/Kconfig | 1 + drivers/media/i2c/smiapp/smiapp-core.c | 29 ++++++------ drivers/media/i2c/tc358743.c | 11 +++-- drivers/media/i2c/tvp514x.c | 6 +-- drivers/media/i2c/tvp5150.c | 7 +-- drivers/media/i2c/tvp7002.c | 6 +-- drivers/media/platform/Kconfig | 4 ++ drivers/media/platform/am437x/Kconfig | 1 + drivers/media/platform/am437x/am437x-vpfe.c | 15 +++--- drivers/media/platform/atmel/Kconfig | 2 + drivers/media/platform/atmel/atmel-isc.c | 13 ++++-- drivers/media/platform/atmel/atmel-isi.c | 11 +++-- drivers/media/platform/exynos4-is/Kconfig | 2 + drivers/media/platform/exynos4-is/media-dev.c | 13 +++--- drivers/media/platform/exynos4-is/mipi-csis.c | 6 +-- drivers/media/platform/omap3isp/isp.c | 49 ++++++++++---------- drivers/media/platform/pxa_camera.c | 11 +++-- drivers/media/platform/rcar-vin/Kconfig | 1 + drivers/media/platform/rcar-vin/rcar-core.c | 19 ++++---- drivers/media/platform/soc_camera/soc_camera.c | 7 +-- drivers/media/platform/stm32/stm32-dcmi.c | 13 +++--- drivers/media/platform/ti-vpe/cal.c | 15 +++--- drivers/media/platform/xilinx/Kconfig | 1 + drivers/media/platform/xilinx/xilinx-vipp.c | 63 ++++++++++++++------------ drivers/media/v4l2-core/v4l2-async.c | 14 +----- include/media/v4l2-async.h | 5 -- include/media/v4l2-subdev.h | 2 - 35 files changed, 212 insertions(+), 175 deletions(-) diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index fd181c99ce11..7c23b7a1fd05 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -209,6 +209,7 @@ config VIDEO_ADV7604 depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API depends on GPIOLIB || COMPILE_TEST select HDMI + select V4L2_FWNODE ---help--- Support for the Analog Devices ADV7604 video decoder. @@ -322,6 +323,7 @@ config VIDEO_TC358743 tristate "Toshiba TC358743 decoder" depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API select HDMI + select V4L2_FWNODE ---help--- Support for the Toshiba TC358743 HDMI to MIPI CSI-2 bridge. @@ -331,6 +333,7 @@ config VIDEO_TC358743 config VIDEO_TVP514X tristate "Texas Instruments TVP514x video decoder" depends on VIDEO_V4L2 && I2C + select V4L2_FWNODE ---help--- This is a Video4Linux2 sensor-level driver for the TI TVP5146/47 decoder. It is currently working with the TI OMAP3 camera @@ -342,6 +345,7 @@ config VIDEO_TVP514X config VIDEO_TVP5150 tristate "Texas Instruments TVP5150 video decoder" depends on VIDEO_V4L2 && I2C + select V4L2_FWNODE ---help--- Support for the Texas Instruments TVP5150 video decoder. @@ -351,6 +355,7 @@ config VIDEO_TVP5150 config VIDEO_TVP7002 tristate "Texas Instruments TVP7002 video decoder" depends on VIDEO_V4L2 && I2C + select V4L2_FWNODE ---help--- Support for the Texas Instruments TVP7002 video decoder. @@ -532,6 +537,7 @@ config VIDEO_OV2659 tristate "OmniVision OV2659 sensor support" depends on VIDEO_V4L2 && I2C depends on MEDIA_CAMERA_SUPPORT + select V4L2_FWNODE ---help--- This is a Video4Linux2 sensor-level driver for the OmniVision OV2659 camera. @@ -544,6 +550,7 @@ config VIDEO_OV5645 depends on OF depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API depends on MEDIA_CAMERA_SUPPORT + select V4L2_FWNODE ---help--- This is a Video4Linux2 sensor-level driver for the OmniVision OV5645 camera. @@ -555,6 +562,7 @@ config VIDEO_OV5647 tristate "OmniVision OV5647 sensor support" depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API depends on MEDIA_CAMERA_SUPPORT + select V4L2_FWNODE ---help--- This is a Video4Linux2 sensor-level driver for the OmniVision OV5647 camera. @@ -647,6 +655,7 @@ config VIDEO_MT9V032 depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API depends on MEDIA_CAMERA_SUPPORT select REGMAP_I2C + select V4L2_FWNODE ---help--- This is a Video4Linux2 sensor-level driver for the Micron MT9V032 752x480 CMOS sensor. @@ -694,6 +703,7 @@ config VIDEO_S5K4ECGX config VIDEO_S5K5BAF tristate "Samsung S5K5BAF sensor support" depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + select V4L2_FWNODE ---help--- This is a V4L2 sensor-level driver for Samsung S5K5BAF 2M camera sensor with an embedded SoC image signal processor. @@ -704,6 +714,7 @@ source "drivers/media/i2c/et8ek8/Kconfig" config VIDEO_S5C73M3 tristate "Samsung S5C73M3 sensor support" depends on I2C && SPI && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + select V4L2_FWNODE ---help--- This is a V4L2 sensor-level driver for Samsung S5C73M3 8 Mpixel camera. diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index f1fa9cec489f..660bacb8f7d9 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -45,7 +46,7 @@ #include #include #include -#include +#include static int debug; module_param(debug, int, 0644); @@ -3069,7 +3070,7 @@ MODULE_DEVICE_TABLE(of, adv76xx_of_id); static int adv76xx_parse_dt(struct adv76xx_state *state) { - struct v4l2_of_endpoint bus_cfg; + struct v4l2_fwnode_endpoint bus_cfg; struct device_node *endpoint; struct device_node *np; unsigned int flags; @@ -3083,7 +3084,7 @@ static int adv76xx_parse_dt(struct adv76xx_state *state) if (!endpoint) return -EINVAL; - ret = v4l2_of_parse_endpoint(endpoint, &bus_cfg); + ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(endpoint), &bus_cfg); if (ret) { of_node_put(endpoint); return ret; diff --git a/drivers/media/i2c/mt9v032.c b/drivers/media/i2c/mt9v032.c index 2e7a6e62a358..8a430640c85d 100644 --- a/drivers/media/i2c/mt9v032.c +++ b/drivers/media/i2c/mt9v032.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -28,7 +29,7 @@ #include #include #include -#include +#include #include /* The first four rows are black rows. The active area spans 753x481 pixels. */ @@ -979,7 +980,7 @@ static struct mt9v032_platform_data * mt9v032_get_pdata(struct i2c_client *client) { struct mt9v032_platform_data *pdata = NULL; - struct v4l2_of_endpoint endpoint; + struct v4l2_fwnode_endpoint endpoint; struct device_node *np; struct property *prop; @@ -990,7 +991,7 @@ mt9v032_get_pdata(struct i2c_client *client) if (!np) return NULL; - if (v4l2_of_parse_endpoint(np, &endpoint) < 0) + if (v4l2_fwnode_endpoint_parse(of_fwnode_handle(np), &endpoint) < 0) goto done; pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL); diff --git a/drivers/media/i2c/ov2659.c b/drivers/media/i2c/ov2659.c index 58615f836fb7..122dd6c5eb38 100644 --- a/drivers/media/i2c/ov2659.c +++ b/drivers/media/i2c/ov2659.c @@ -42,9 +42,9 @@ #include #include #include +#include #include #include -#include #include #define DRIVER_NAME "ov2659" @@ -1347,7 +1347,7 @@ static struct ov2659_platform_data * ov2659_get_pdata(struct i2c_client *client) { struct ov2659_platform_data *pdata; - struct v4l2_of_endpoint *bus_cfg; + struct v4l2_fwnode_endpoint *bus_cfg; struct device_node *endpoint; if (!IS_ENABLED(CONFIG_OF) || !client->dev.of_node) @@ -1357,7 +1357,7 @@ ov2659_get_pdata(struct i2c_client *client) if (!endpoint) return NULL; - bus_cfg = v4l2_of_alloc_parse_endpoint(endpoint); + bus_cfg = v4l2_fwnode_endpoint_alloc_parse(of_fwnode_handle(endpoint)); if (IS_ERR(bus_cfg)) { pdata = NULL; goto done; @@ -1377,7 +1377,7 @@ ov2659_get_pdata(struct i2c_client *client) pdata->link_frequency = bus_cfg->link_frequencies[0]; done: - v4l2_of_free_endpoint(bus_cfg); + v4l2_fwnode_endpoint_free(bus_cfg); of_node_put(endpoint); return pdata; } diff --git a/drivers/media/i2c/ov5645.c b/drivers/media/i2c/ov5645.c index 57bd591ea54b..d1e844f7f03f 100644 --- a/drivers/media/i2c/ov5645.c +++ b/drivers/media/i2c/ov5645.c @@ -39,7 +39,7 @@ #include #include #include -#include +#include #include #define OV5645_VOLTAGE_ANALOG 2800000 @@ -87,7 +87,7 @@ struct ov5645 { struct device *dev; struct v4l2_subdev sd; struct media_pad pad; - struct v4l2_of_endpoint ep; + struct v4l2_fwnode_endpoint ep; struct v4l2_mbus_framefmt fmt; struct v4l2_rect crop; struct clk *xclk; @@ -1102,7 +1102,8 @@ static int ov5645_probe(struct i2c_client *client, return -EINVAL; } - ret = v4l2_of_parse_endpoint(endpoint, &ov5645->ep); + ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(endpoint), + &ov5645->ep); if (ret < 0) { dev_err(dev, "parsing endpoint node failed\n"); return ret; diff --git a/drivers/media/i2c/ov5647.c b/drivers/media/i2c/ov5647.c index f57a0b354cf6..95ce90fdb876 100644 --- a/drivers/media/i2c/ov5647.c +++ b/drivers/media/i2c/ov5647.c @@ -25,12 +25,13 @@ #include #include #include +#include #include #include #include +#include #include #include -#include #define SENSOR_NAME "ov5647" @@ -510,7 +511,7 @@ static const struct v4l2_subdev_internal_ops ov5647_subdev_internal_ops = { static int ov5647_parse_dt(struct device_node *np) { - struct v4l2_of_endpoint bus_cfg; + struct v4l2_fwnode_endpoint bus_cfg; struct device_node *ep; int ret; @@ -519,7 +520,7 @@ static int ov5647_parse_dt(struct device_node *np) if (!ep) return -EINVAL; - ret = v4l2_of_parse_endpoint(ep, &bus_cfg); + ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep), &bus_cfg); of_node_put(ep); return ret; diff --git a/drivers/media/i2c/s5c73m3/s5c73m3-core.c b/drivers/media/i2c/s5c73m3/s5c73m3-core.c index 3844853ab0a0..f434fb2ee6fc 100644 --- a/drivers/media/i2c/s5c73m3/s5c73m3-core.c +++ b/drivers/media/i2c/s5c73m3/s5c73m3-core.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -35,7 +36,7 @@ #include #include #include -#include +#include #include "s5c73m3.h" @@ -1602,7 +1603,7 @@ static int s5c73m3_get_platform_data(struct s5c73m3 *state) const struct s5c73m3_platform_data *pdata = dev->platform_data; struct device_node *node = dev->of_node; struct device_node *node_ep; - struct v4l2_of_endpoint ep; + struct v4l2_fwnode_endpoint ep; int ret; if (!node) { @@ -1639,7 +1640,7 @@ static int s5c73m3_get_platform_data(struct s5c73m3 *state) return 0; } - ret = v4l2_of_parse_endpoint(node_ep, &ep); + ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(node_ep), &ep); of_node_put(node_ep); if (ret) return ret; diff --git a/drivers/media/i2c/s5k5baf.c b/drivers/media/i2c/s5k5baf.c index db82ed05792e..962051b9939d 100644 --- a/drivers/media/i2c/s5k5baf.c +++ b/drivers/media/i2c/s5k5baf.c @@ -30,7 +30,7 @@ #include #include #include -#include +#include static int debug; module_param(debug, int, 0644); @@ -1841,7 +1841,7 @@ static int s5k5baf_parse_device_node(struct s5k5baf *state, struct device *dev) { struct device_node *node = dev->of_node; struct device_node *node_ep; - struct v4l2_of_endpoint ep; + struct v4l2_fwnode_endpoint ep; int ret; if (!node) { @@ -1868,7 +1868,7 @@ static int s5k5baf_parse_device_node(struct s5k5baf *state, struct device *dev) return -EINVAL; } - ret = v4l2_of_parse_endpoint(node_ep, &ep); + ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(node_ep), &ep); of_node_put(node_ep); if (ret) return ret; diff --git a/drivers/media/i2c/smiapp/Kconfig b/drivers/media/i2c/smiapp/Kconfig index 3149cda1d0db..f59718d8e51e 100644 --- a/drivers/media/i2c/smiapp/Kconfig +++ b/drivers/media/i2c/smiapp/Kconfig @@ -3,5 +3,6 @@ config VIDEO_SMIAPP depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && HAVE_CLK depends on MEDIA_CAMERA_SUPPORT select VIDEO_SMIAPP_PLL + select V4L2_FWNODE ---help--- This is a generic driver for SMIA++/SMIA camera modules. diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c index f4e92bdfe192..e0b0c032c4ac 100644 --- a/drivers/media/i2c/smiapp/smiapp-core.c +++ b/drivers/media/i2c/smiapp/smiapp-core.c @@ -27,12 +27,13 @@ #include #include #include +#include #include #include #include #include +#include #include -#include #include "smiapp.h" @@ -2784,19 +2785,20 @@ static int __maybe_unused smiapp_resume(struct device *dev) static struct smiapp_hwconfig *smiapp_get_hwconfig(struct device *dev) { struct smiapp_hwconfig *hwcfg; - struct v4l2_of_endpoint *bus_cfg; - struct device_node *ep; + struct v4l2_fwnode_endpoint *bus_cfg; + struct fwnode_handle *ep; + struct fwnode_handle *fwnode = dev_fwnode(dev); int i; int rval; - if (!dev->of_node) + if (!fwnode) return dev->platform_data; - ep = of_graph_get_next_endpoint(dev->of_node, NULL); + ep = fwnode_graph_get_next_endpoint(fwnode, NULL); if (!ep) return NULL; - bus_cfg = v4l2_of_alloc_parse_endpoint(ep); + bus_cfg = v4l2_fwnode_endpoint_alloc_parse(ep); if (IS_ERR(bus_cfg)) goto out_err; @@ -2817,11 +2819,10 @@ static struct smiapp_hwconfig *smiapp_get_hwconfig(struct device *dev) dev_dbg(dev, "lanes %u\n", hwcfg->lanes); /* NVM size is not mandatory */ - of_property_read_u32(dev->of_node, "nokia,nvm-size", - &hwcfg->nvm_size); + fwnode_property_read_u32(fwnode, "nokia,nvm-size", &hwcfg->nvm_size); - rval = of_property_read_u32(dev->of_node, "clock-frequency", - &hwcfg->ext_clk); + rval = fwnode_property_read_u32(fwnode, "clock-frequency", + &hwcfg->ext_clk); if (rval) { dev_warn(dev, "can't get clock-frequency\n"); goto out_err; @@ -2846,13 +2847,13 @@ static struct smiapp_hwconfig *smiapp_get_hwconfig(struct device *dev) dev_dbg(dev, "freq %d: %lld\n", i, hwcfg->op_sys_clock[i]); } - v4l2_of_free_endpoint(bus_cfg); - of_node_put(ep); + v4l2_fwnode_endpoint_free(bus_cfg); + fwnode_handle_put(ep); return hwcfg; out_err: - v4l2_of_free_endpoint(bus_cfg); - of_node_put(ep); + v4l2_fwnode_endpoint_free(bus_cfg); + fwnode_handle_put(ep); return NULL; } diff --git a/drivers/media/i2c/tc358743.c b/drivers/media/i2c/tc358743.c index 3251cba89e8f..3e5b09030303 100644 --- a/drivers/media/i2c/tc358743.c +++ b/drivers/media/i2c/tc358743.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -41,7 +42,7 @@ #include #include #include -#include +#include #include #include "tc358743_regs.h" @@ -76,7 +77,7 @@ static const struct v4l2_dv_timings_cap tc358743_timings_cap = { struct tc358743_state { struct tc358743_platform_data pdata; - struct v4l2_of_bus_mipi_csi2 bus; + struct v4l2_fwnode_bus_mipi_csi2 bus; struct v4l2_subdev sd; struct media_pad pad; struct v4l2_ctrl_handler hdl; @@ -1695,7 +1696,7 @@ static void tc358743_gpio_reset(struct tc358743_state *state) static int tc358743_probe_of(struct tc358743_state *state) { struct device *dev = &state->i2c_client->dev; - struct v4l2_of_endpoint *endpoint; + struct v4l2_fwnode_endpoint *endpoint; struct device_node *ep; struct clk *refclk; u32 bps_pr_lane; @@ -1715,7 +1716,7 @@ static int tc358743_probe_of(struct tc358743_state *state) return -EINVAL; } - endpoint = v4l2_of_alloc_parse_endpoint(ep); + endpoint = v4l2_fwnode_endpoint_alloc_parse(of_fwnode_handle(ep)); if (IS_ERR(endpoint)) { dev_err(dev, "failed to parse endpoint\n"); return PTR_ERR(endpoint); @@ -1803,7 +1804,7 @@ static int tc358743_probe_of(struct tc358743_state *state) disable_clk: clk_disable_unprepare(refclk); free_endpoint: - v4l2_of_free_endpoint(endpoint); + v4l2_fwnode_endpoint_free(endpoint); return ret; } #else diff --git a/drivers/media/i2c/tvp514x.c b/drivers/media/i2c/tvp514x.c index 07853d2252aa..ad2df998f9c5 100644 --- a/drivers/media/i2c/tvp514x.c +++ b/drivers/media/i2c/tvp514x.c @@ -38,7 +38,7 @@ #include #include #include -#include +#include #include #include #include @@ -998,7 +998,7 @@ static struct tvp514x_platform_data * tvp514x_get_pdata(struct i2c_client *client) { struct tvp514x_platform_data *pdata = NULL; - struct v4l2_of_endpoint bus_cfg; + struct v4l2_fwnode_endpoint bus_cfg; struct device_node *endpoint; unsigned int flags; @@ -1009,7 +1009,7 @@ tvp514x_get_pdata(struct i2c_client *client) if (!endpoint) return NULL; - if (v4l2_of_parse_endpoint(endpoint, &bus_cfg)) + if (v4l2_fwnode_endpoint_parse(of_fwnode_handle(endpoint), &bus_cfg)) goto done; pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL); diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c index 04e96b3057bb..9da4bf4f2c7a 100644 --- a/drivers/media/i2c/tvp5150.c +++ b/drivers/media/i2c/tvp5150.c @@ -12,10 +12,11 @@ #include #include #include +#include #include #include #include -#include +#include #include #include "tvp5150_reg.h" @@ -1358,7 +1359,7 @@ static int tvp5150_init(struct i2c_client *c) static int tvp5150_parse_dt(struct tvp5150 *decoder, struct device_node *np) { - struct v4l2_of_endpoint bus_cfg; + struct v4l2_fwnode_endpoint bus_cfg; struct device_node *ep; #ifdef CONFIG_MEDIA_CONTROLLER struct device_node *connectors, *child; @@ -1373,7 +1374,7 @@ static int tvp5150_parse_dt(struct tvp5150 *decoder, struct device_node *np) if (!ep) return -EINVAL; - ret = v4l2_of_parse_endpoint(ep, &bus_cfg); + ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep), &bus_cfg); if (ret) goto err; diff --git a/drivers/media/i2c/tvp7002.c b/drivers/media/i2c/tvp7002.c index 4c1190127c85..a26c1a3f7183 100644 --- a/drivers/media/i2c/tvp7002.c +++ b/drivers/media/i2c/tvp7002.c @@ -33,7 +33,7 @@ #include #include #include -#include +#include #include "tvp7002_reg.h" @@ -889,7 +889,7 @@ static const struct v4l2_subdev_ops tvp7002_ops = { static struct tvp7002_config * tvp7002_get_pdata(struct i2c_client *client) { - struct v4l2_of_endpoint bus_cfg; + struct v4l2_fwnode_endpoint bus_cfg; struct tvp7002_config *pdata = NULL; struct device_node *endpoint; unsigned int flags; @@ -901,7 +901,7 @@ tvp7002_get_pdata(struct i2c_client *client) if (!endpoint) return NULL; - if (v4l2_of_parse_endpoint(endpoint, &bus_cfg)) + if (v4l2_fwnode_endpoint_parse(of_fwnode_handle(endpoint), &bus_cfg)) goto done; pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL); diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index f125f471076f..a4b7cefbde32 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -82,6 +82,7 @@ config VIDEO_OMAP3 select ARM_DMA_USE_IOMMU select VIDEOBUF2_DMA_CONTIG select MFD_SYSCON + select V4L2_FWNODE ---help--- Driver for an OMAP 3 camera controller. @@ -97,6 +98,7 @@ config VIDEO_PXA27x depends on PXA27x || COMPILE_TEST select VIDEOBUF2_DMA_SG select SG_SPLIT + select V4L2_FWNODE ---help--- This is a v4l2 driver for the PXA27x Quick Capture Interface @@ -119,6 +121,7 @@ config VIDEO_STM32_DCMI depends on VIDEO_V4L2 && OF && HAS_DMA depends on ARCH_STM32 || COMPILE_TEST select VIDEOBUF2_DMA_CONTIG + select V4L2_FWNODE ---help--- This module makes the STM32 Digital Camera Memory Interface (DCMI) available as a v4l2 device. @@ -139,6 +142,7 @@ config VIDEO_TI_CAL depends on SOC_DRA7XX || COMPILE_TEST depends on HAS_DMA select VIDEOBUF2_DMA_CONTIG + select V4L2_FWNODE default n ---help--- Support for the TI CAL (Camera Adaptation Layer) block diff --git a/drivers/media/platform/am437x/Kconfig b/drivers/media/platform/am437x/Kconfig index 42d9c186710a..160e77e9a0fb 100644 --- a/drivers/media/platform/am437x/Kconfig +++ b/drivers/media/platform/am437x/Kconfig @@ -3,6 +3,7 @@ config VIDEO_AM437X_VPFE depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && HAS_DMA depends on SOC_AM43XX || COMPILE_TEST select VIDEOBUF2_DMA_CONTIG + select V4L2_FWNODE help Support for AM437x Video Processing Front End based Video Capture Driver. diff --git a/drivers/media/platform/am437x/am437x-vpfe.c b/drivers/media/platform/am437x/am437x-vpfe.c index 05489a401c5c..466aba8b0e00 100644 --- a/drivers/media/platform/am437x/am437x-vpfe.c +++ b/drivers/media/platform/am437x/am437x-vpfe.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -36,7 +37,7 @@ #include #include #include -#include +#include #include "am437x-vpfe.h" @@ -2303,7 +2304,8 @@ vpfe_async_bound(struct v4l2_async_notifier *notifier, vpfe_dbg(1, vpfe, "vpfe_async_bound\n"); for (i = 0; i < ARRAY_SIZE(vpfe->cfg->asd); i++) { - if (vpfe->cfg->asd[i]->match.of.node == asd[i].match.of.node) { + if (vpfe->cfg->asd[i]->match.fwnode.fwnode == + asd[i].match.fwnode.fwnode) { sdinfo = &vpfe->cfg->sub_devs[i]; vpfe->sd[i] = subdev; vpfe->sd[i]->grp_id = sdinfo->grp_id; @@ -2419,7 +2421,7 @@ static struct vpfe_config * vpfe_get_pdata(struct platform_device *pdev) { struct device_node *endpoint = NULL; - struct v4l2_of_endpoint bus_cfg; + struct v4l2_fwnode_endpoint bus_cfg; struct vpfe_subdev_info *sdinfo; struct vpfe_config *pdata; unsigned int flags; @@ -2463,7 +2465,8 @@ vpfe_get_pdata(struct platform_device *pdev) sdinfo->vpfe_param.if_type = VPFE_RAW_BAYER; } - err = v4l2_of_parse_endpoint(endpoint, &bus_cfg); + err = v4l2_fwnode_endpoint_parse(of_fwnode_handle(endpoint), + &bus_cfg); if (err) { dev_err(&pdev->dev, "Could not parse the endpoint\n"); goto done; @@ -2501,8 +2504,8 @@ vpfe_get_pdata(struct platform_device *pdev) goto done; } - pdata->asd[i]->match_type = V4L2_ASYNC_MATCH_OF; - pdata->asd[i]->match.of.node = rem; + pdata->asd[i]->match_type = V4L2_ASYNC_MATCH_FWNODE; + pdata->asd[i]->match.fwnode.fwnode = of_fwnode_handle(rem); of_node_put(rem); } diff --git a/drivers/media/platform/atmel/Kconfig b/drivers/media/platform/atmel/Kconfig index 9bd0f19b127f..55de751e5f51 100644 --- a/drivers/media/platform/atmel/Kconfig +++ b/drivers/media/platform/atmel/Kconfig @@ -4,6 +4,7 @@ config VIDEO_ATMEL_ISC depends on ARCH_AT91 || COMPILE_TEST select VIDEOBUF2_DMA_CONTIG select REGMAP_MMIO + select V4L2_FWNODE help This module makes the ATMEL Image Sensor Controller available as a v4l2 device. @@ -13,6 +14,7 @@ config VIDEO_ATMEL_ISI depends on VIDEO_V4L2 && OF && HAS_DMA depends on ARCH_AT91 || COMPILE_TEST select VIDEOBUF2_DMA_CONTIG + select V4L2_FWNODE ---help--- This module makes the ATMEL Image Sensor Interface available as a v4l2 device. diff --git a/drivers/media/platform/atmel/atmel-isc.c b/drivers/media/platform/atmel/atmel-isc.c index 78d966233f80..d6534252cdcd 100644 --- a/drivers/media/platform/atmel/atmel-isc.c +++ b/drivers/media/platform/atmel/atmel-isc.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -42,7 +43,7 @@ #include #include #include -#include +#include #include #include @@ -1685,7 +1686,7 @@ static int isc_parse_dt(struct device *dev, struct isc_device *isc) { struct device_node *np = dev->of_node; struct device_node *epn = NULL, *rem; - struct v4l2_of_endpoint v4l2_epn; + struct v4l2_fwnode_endpoint v4l2_epn; struct isc_subdev_entity *subdev_entity; unsigned int flags; int ret; @@ -1704,7 +1705,8 @@ static int isc_parse_dt(struct device *dev, struct isc_device *isc) continue; } - ret = v4l2_of_parse_endpoint(epn, &v4l2_epn); + ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(epn), + &v4l2_epn); if (ret) { of_node_put(rem); ret = -EINVAL; @@ -1739,8 +1741,9 @@ static int isc_parse_dt(struct device *dev, struct isc_device *isc) if (flags & V4L2_MBUS_PCLK_SAMPLE_FALLING) subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_PPOL_LOW; - subdev_entity->asd->match_type = V4L2_ASYNC_MATCH_OF; - subdev_entity->asd->match.of.node = rem; + subdev_entity->asd->match_type = V4L2_ASYNC_MATCH_FWNODE; + subdev_entity->asd->match.fwnode.fwnode = + of_fwnode_handle(rem); list_add_tail(&subdev_entity->list, &isc->subdev_entities); } diff --git a/drivers/media/platform/atmel/atmel-isi.c b/drivers/media/platform/atmel/atmel-isi.c index e4867f84514c..ef482cc704fe 100644 --- a/drivers/media/platform/atmel/atmel-isi.c +++ b/drivers/media/platform/atmel/atmel-isi.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -30,7 +31,7 @@ #include #include #include -#include +#include #include #include @@ -801,7 +802,7 @@ static int atmel_isi_parse_dt(struct atmel_isi *isi, struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; - struct v4l2_of_endpoint ep; + struct v4l2_fwnode_endpoint ep; int err; /* Default settings for ISI */ @@ -814,7 +815,7 @@ static int atmel_isi_parse_dt(struct atmel_isi *isi, return -EINVAL; } - err = v4l2_of_parse_endpoint(np, &ep); + err = v4l2_fwnode_endpoint_parse(of_fwnode_handle(np), &ep); of_node_put(np); if (err) { dev_err(&pdev->dev, "Could not parse the endpoint\n"); @@ -1126,8 +1127,8 @@ static int isi_graph_parse(struct atmel_isi *isi, struct device_node *node) /* Remote node to connect */ isi->entity.node = remote; - isi->entity.asd.match_type = V4L2_ASYNC_MATCH_OF; - isi->entity.asd.match.of.node = remote; + isi->entity.asd.match_type = V4L2_ASYNC_MATCH_FWNODE; + isi->entity.asd.match.fwnode.fwnode = of_fwnode_handle(remote); return 0; } } diff --git a/drivers/media/platform/exynos4-is/Kconfig b/drivers/media/platform/exynos4-is/Kconfig index 57d42c6172c5..c480efb755f5 100644 --- a/drivers/media/platform/exynos4-is/Kconfig +++ b/drivers/media/platform/exynos4-is/Kconfig @@ -4,6 +4,7 @@ config VIDEO_SAMSUNG_EXYNOS4_IS depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API depends on ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST depends on OF && COMMON_CLK + select V4L2_FWNODE help Say Y here to enable camera host interface devices for Samsung S5P and EXYNOS SoC series. @@ -32,6 +33,7 @@ config VIDEO_S5P_MIPI_CSIS tristate "S5P/EXYNOS MIPI-CSI2 receiver (MIPI-CSIS) driver" depends on REGULATOR select GENERIC_PHY + select V4L2_FWNODE help This is a V4L2 driver for Samsung S5P and EXYNOS4 SoC MIPI-CSI2 receiver (MIPI-CSIS) devices. diff --git a/drivers/media/platform/exynos4-is/media-dev.c b/drivers/media/platform/exynos4-is/media-dev.c index e82450e90a67..7d1cf78846c4 100644 --- a/drivers/media/platform/exynos4-is/media-dev.c +++ b/drivers/media/platform/exynos4-is/media-dev.c @@ -29,7 +29,7 @@ #include #include #include -#include +#include #include #include @@ -388,7 +388,7 @@ static int fimc_md_parse_port_node(struct fimc_md *fmd, { struct fimc_source_info *pd = &fmd->sensor[index].pdata; struct device_node *rem, *ep, *np; - struct v4l2_of_endpoint endpoint; + struct v4l2_fwnode_endpoint endpoint; int ret; /* Assume here a port node can have only one endpoint node. */ @@ -396,7 +396,7 @@ static int fimc_md_parse_port_node(struct fimc_md *fmd, if (!ep) return 0; - ret = v4l2_of_parse_endpoint(ep, &endpoint); + ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep), &endpoint); if (ret) { of_node_put(ep); return ret; @@ -453,8 +453,8 @@ static int fimc_md_parse_port_node(struct fimc_md *fmd, return -EINVAL; } - fmd->sensor[index].asd.match_type = V4L2_ASYNC_MATCH_OF; - fmd->sensor[index].asd.match.of.node = rem; + fmd->sensor[index].asd.match_type = V4L2_ASYNC_MATCH_FWNODE; + fmd->sensor[index].asd.match.fwnode.fwnode = of_fwnode_handle(rem); fmd->async_subdevs[index] = &fmd->sensor[index].asd; fmd->num_sensors++; @@ -1361,7 +1361,8 @@ static int subdev_notifier_bound(struct v4l2_async_notifier *notifier, /* Find platform data for this sensor subdev */ for (i = 0; i < ARRAY_SIZE(fmd->sensor); i++) - if (fmd->sensor[i].asd.match.of.node == subdev->dev->of_node) + if (fmd->sensor[i].asd.match.fwnode.fwnode == + of_fwnode_handle(subdev->dev->of_node)) si = &fmd->sensor[i]; if (si == NULL) diff --git a/drivers/media/platform/exynos4-is/mipi-csis.c b/drivers/media/platform/exynos4-is/mipi-csis.c index f819b29efc38..98c89873c2dc 100644 --- a/drivers/media/platform/exynos4-is/mipi-csis.c +++ b/drivers/media/platform/exynos4-is/mipi-csis.c @@ -30,7 +30,7 @@ #include #include #include -#include +#include #include #include "mipi-csis.h" @@ -718,7 +718,7 @@ static int s5pcsis_parse_dt(struct platform_device *pdev, struct csis_state *state) { struct device_node *node = pdev->dev.of_node; - struct v4l2_of_endpoint endpoint; + struct v4l2_fwnode_endpoint endpoint; int ret; if (of_property_read_u32(node, "clock-frequency", @@ -735,7 +735,7 @@ static int s5pcsis_parse_dt(struct platform_device *pdev, return -EINVAL; } /* Get port node and validate MIPI-CSI channel id. */ - ret = v4l2_of_parse_endpoint(node, &endpoint); + ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(node), &endpoint); if (ret) goto err; diff --git a/drivers/media/platform/omap3isp/isp.c b/drivers/media/platform/omap3isp/isp.c index 0d984a28a003..9df64c189883 100644 --- a/drivers/media/platform/omap3isp/isp.c +++ b/drivers/media/platform/omap3isp/isp.c @@ -55,6 +55,7 @@ #include #include #include +#include #include #include #include @@ -63,9 +64,9 @@ #include #include +#include #include #include -#include #include "isp.h" #include "ispreg.h" @@ -2007,20 +2008,20 @@ enum isp_of_phy { ISP_OF_PHY_CSIPHY2, }; -static int isp_of_parse_node(struct device *dev, struct device_node *node, - struct isp_async_subdev *isd) +static int isp_fwnode_parse(struct device *dev, struct fwnode_handle *fwnode, + struct isp_async_subdev *isd) { struct isp_bus_cfg *buscfg = &isd->bus; - struct v4l2_of_endpoint vep; + struct v4l2_fwnode_endpoint vep; unsigned int i; int ret; - ret = v4l2_of_parse_endpoint(node, &vep); + ret = v4l2_fwnode_endpoint_parse(fwnode, &vep); if (ret) return ret; - dev_dbg(dev, "parsing endpoint %s, interface %u\n", node->full_name, - vep.base.port); + dev_dbg(dev, "parsing endpoint %s, interface %u\n", + to_of_node(fwnode)->full_name, vep.base.port); switch (vep.base.port) { case ISP_OF_PHY_PARALLEL: @@ -2077,18 +2078,18 @@ static int isp_of_parse_node(struct device *dev, struct device_node *node, break; default: - dev_warn(dev, "%s: invalid interface %u\n", node->full_name, - vep.base.port); + dev_warn(dev, "%s: invalid interface %u\n", + to_of_node(fwnode)->full_name, vep.base.port); break; } return 0; } -static int isp_of_parse_nodes(struct device *dev, - struct v4l2_async_notifier *notifier) +static int isp_fwnodes_parse(struct device *dev, + struct v4l2_async_notifier *notifier) { - struct device_node *node = NULL; + struct fwnode_handle *fwnode = NULL; notifier->subdevs = devm_kcalloc( dev, ISP_MAX_SUBDEVS, sizeof(*notifier->subdevs), GFP_KERNEL); @@ -2096,7 +2097,8 @@ static int isp_of_parse_nodes(struct device *dev, return -ENOMEM; while (notifier->num_subdevs < ISP_MAX_SUBDEVS && - (node = of_graph_get_next_endpoint(dev->of_node, node))) { + (fwnode = fwnode_graph_get_next_endpoint( + of_fwnode_handle(dev->of_node), fwnode))) { struct isp_async_subdev *isd; isd = devm_kzalloc(dev, sizeof(*isd), GFP_KERNEL); @@ -2105,23 +2107,24 @@ static int isp_of_parse_nodes(struct device *dev, notifier->subdevs[notifier->num_subdevs] = &isd->asd; - if (isp_of_parse_node(dev, node, isd)) + if (isp_fwnode_parse(dev, fwnode, isd)) goto error; - isd->asd.match.of.node = of_graph_get_remote_port_parent(node); - if (!isd->asd.match.of.node) { + isd->asd.match.fwnode.fwnode = + fwnode_graph_get_remote_port_parent(fwnode); + if (!isd->asd.match.fwnode.fwnode) { dev_warn(dev, "bad remote port parent\n"); goto error; } - isd->asd.match_type = V4L2_ASYNC_MATCH_OF; + isd->asd.match_type = V4L2_ASYNC_MATCH_FWNODE; notifier->num_subdevs++; } return notifier->num_subdevs; error: - of_node_put(node); + fwnode_handle_put(fwnode); return -EINVAL; } @@ -2192,8 +2195,8 @@ static int isp_probe(struct platform_device *pdev) return -ENOMEM; } - ret = of_property_read_u32(pdev->dev.of_node, "ti,phy-type", - &isp->phy_type); + ret = fwnode_property_read_u32(of_fwnode_handle(pdev->dev.of_node), + "ti,phy-type", &isp->phy_type); if (ret) return ret; @@ -2202,12 +2205,12 @@ static int isp_probe(struct platform_device *pdev) if (IS_ERR(isp->syscon)) return PTR_ERR(isp->syscon); - ret = of_property_read_u32_index(pdev->dev.of_node, "syscon", 1, - &isp->syscon_offset); + ret = of_property_read_u32_index(pdev->dev.of_node, + "syscon", 1, &isp->syscon_offset); if (ret) return ret; - ret = isp_of_parse_nodes(&pdev->dev, &isp->notifier); + ret = isp_fwnodes_parse(&pdev->dev, &isp->notifier); if (ret < 0) return ret; diff --git a/drivers/media/platform/pxa_camera.c b/drivers/media/platform/pxa_camera.c index 25e538bf0df7..399095170b6e 100644 --- a/drivers/media/platform/pxa_camera.c +++ b/drivers/media/platform/pxa_camera.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -41,7 +42,7 @@ #include #include #include -#include +#include #include @@ -2270,7 +2271,7 @@ static int pxa_camera_pdata_from_dt(struct device *dev, { u32 mclk_rate; struct device_node *remote, *np = dev->of_node; - struct v4l2_of_endpoint ep; + struct v4l2_fwnode_endpoint ep; int err = of_property_read_u32(np, "clock-frequency", &mclk_rate); if (!err) { @@ -2284,7 +2285,7 @@ static int pxa_camera_pdata_from_dt(struct device *dev, return -EINVAL; } - err = v4l2_of_parse_endpoint(np, &ep); + err = v4l2_fwnode_endpoint_parse(of_fwnode_handle(np), &ep); if (err) { dev_err(dev, "could not parse endpoint\n"); goto out; @@ -2321,10 +2322,10 @@ static int pxa_camera_pdata_from_dt(struct device *dev, if (ep.bus.parallel.flags & V4L2_MBUS_PCLK_SAMPLE_FALLING) pcdev->platform_flags |= PXA_CAMERA_PCLK_EN; - asd->match_type = V4L2_ASYNC_MATCH_OF; + asd->match_type = V4L2_ASYNC_MATCH_FWNODE; remote = of_graph_get_remote_port(np); if (remote) { - asd->match.of.node = remote; + asd->match.fwnode.fwnode = of_fwnode_handle(remote); of_node_put(remote); } else { dev_notice(dev, "no remote for %s\n", of_node_full_name(np)); diff --git a/drivers/media/platform/rcar-vin/Kconfig b/drivers/media/platform/rcar-vin/Kconfig index 111d2a151f6a..af4c98b44d2e 100644 --- a/drivers/media/platform/rcar-vin/Kconfig +++ b/drivers/media/platform/rcar-vin/Kconfig @@ -3,6 +3,7 @@ config VIDEO_RCAR_VIN depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && OF && HAS_DMA && MEDIA_CONTROLLER depends on ARCH_RENESAS || COMPILE_TEST select VIDEOBUF2_DMA_CONTIG + select V4L2_FWNODE ---help--- Support for Renesas R-Car Video Input (VIN) driver. Supports R-Car Gen2 SoCs. diff --git a/drivers/media/platform/rcar-vin/rcar-core.c b/drivers/media/platform/rcar-vin/rcar-core.c index 098a0b1cc10a..264604a9bcf8 100644 --- a/drivers/media/platform/rcar-vin/rcar-core.c +++ b/drivers/media/platform/rcar-vin/rcar-core.c @@ -21,7 +21,7 @@ #include #include -#include +#include #include "rcar-vin.h" @@ -104,7 +104,8 @@ static int rvin_digital_notify_bound(struct v4l2_async_notifier *notifier, v4l2_set_subdev_hostdata(subdev, vin); - if (vin->digital.asd.match.of.node == subdev->dev->of_node) { + if (vin->digital.asd.match.fwnode.fwnode == + of_fwnode_handle(subdev->dev->of_node)) { vin_dbg(vin, "bound digital subdev %s\n", subdev->name); vin->digital.subdev = subdev; return 0; @@ -118,10 +119,10 @@ static int rvin_digitial_parse_v4l2(struct rvin_dev *vin, struct device_node *ep, struct v4l2_mbus_config *mbus_cfg) { - struct v4l2_of_endpoint v4l2_ep; + struct v4l2_fwnode_endpoint v4l2_ep; int ret; - ret = v4l2_of_parse_endpoint(ep, &v4l2_ep); + ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep), &v4l2_ep); if (ret) { vin_err(vin, "Could not parse v4l2 endpoint\n"); return -EINVAL; @@ -151,7 +152,7 @@ static int rvin_digital_graph_parse(struct rvin_dev *vin) struct device_node *ep, *np; int ret; - vin->digital.asd.match.of.node = NULL; + vin->digital.asd.match.fwnode.fwnode = NULL; vin->digital.subdev = NULL; /* @@ -175,8 +176,8 @@ static int rvin_digital_graph_parse(struct rvin_dev *vin) if (ret) return ret; - vin->digital.asd.match.of.node = np; - vin->digital.asd.match_type = V4L2_ASYNC_MATCH_OF; + vin->digital.asd.match.fwnode.fwnode = of_fwnode_handle(np); + vin->digital.asd.match_type = V4L2_ASYNC_MATCH_FWNODE; return 0; } @@ -190,7 +191,7 @@ static int rvin_digital_graph_init(struct rvin_dev *vin) if (ret) return ret; - if (!vin->digital.asd.match.of.node) { + if (!vin->digital.asd.match.fwnode.fwnode) { vin_dbg(vin, "No digital subdevice found\n"); return -ENODEV; } @@ -203,7 +204,7 @@ static int rvin_digital_graph_init(struct rvin_dev *vin) subdevs[0] = &vin->digital.asd; vin_dbg(vin, "Found digital subdevice %s\n", - of_node_full_name(subdevs[0]->match.of.node)); + of_node_full_name(to_of_node(subdevs[0]->match.fwnode.fwnode))); vin->notifier.num_subdevs = 1; vin->notifier.subdevs = subdevs; diff --git a/drivers/media/platform/soc_camera/soc_camera.c b/drivers/media/platform/soc_camera/soc_camera.c index 3c9421f4d8e3..45a0429d75bb 100644 --- a/drivers/media/platform/soc_camera/soc_camera.c +++ b/drivers/media/platform/soc_camera/soc_camera.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -36,7 +37,7 @@ #include #include #include -#include +#include #include /* Default to VGA resolution */ @@ -1512,8 +1513,8 @@ static int soc_of_bind(struct soc_camera_host *ici, if (!info) return -ENOMEM; - info->sasd.asd.match.of.node = remote; - info->sasd.asd.match_type = V4L2_ASYNC_MATCH_OF; + info->sasd.asd.match.fwnode.fwnode = of_fwnode_handle(remote); + info->sasd.asd.match_type = V4L2_ASYNC_MATCH_FWNODE; info->subdev = &info->sasd.asd; /* Or shall this be managed by the soc-camera device? */ diff --git a/drivers/media/platform/stm32/stm32-dcmi.c b/drivers/media/platform/stm32/stm32-dcmi.c index 348f025d5270..83d32a5d0f40 100644 --- a/drivers/media/platform/stm32/stm32-dcmi.c +++ b/drivers/media/platform/stm32/stm32-dcmi.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -29,9 +30,9 @@ #include #include #include +#include #include #include -#include #include #define DRV_NAME "stm32-dcmi" @@ -139,7 +140,7 @@ struct stm32_dcmi { struct mutex lock; struct vb2_queue queue; - struct v4l2_of_bus_parallel bus; + struct v4l2_fwnode_bus_parallel bus; struct completion complete; struct clk *mclk; enum state state; @@ -1143,8 +1144,8 @@ static int dcmi_graph_parse(struct stm32_dcmi *dcmi, struct device_node *node) /* Remote node to connect */ dcmi->entity.node = remote; - dcmi->entity.asd.match_type = V4L2_ASYNC_MATCH_OF; - dcmi->entity.asd.match.of.node = remote; + dcmi->entity.asd.match_type = V4L2_ASYNC_MATCH_FWNODE; + dcmi->entity.asd.match.fwnode.fwnode = of_fwnode_handle(remote); return 0; } } @@ -1190,7 +1191,7 @@ static int dcmi_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; const struct of_device_id *match = NULL; - struct v4l2_of_endpoint ep; + struct v4l2_fwnode_endpoint ep; struct stm32_dcmi *dcmi; struct vb2_queue *q; struct dma_chan *chan; @@ -1222,7 +1223,7 @@ static int dcmi_probe(struct platform_device *pdev) return -ENODEV; } - ret = v4l2_of_parse_endpoint(np, &ep); + ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(np), &ep); if (ret) { dev_err(&pdev->dev, "Could not parse the endpoint\n"); of_node_put(np); diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 7a058b6e03d0..177faa36bc16 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -21,7 +21,7 @@ #include #include -#include +#include #include #include #include @@ -270,7 +270,7 @@ struct cal_ctx { struct video_device vdev; struct v4l2_async_notifier notifier; struct v4l2_subdev *sensor; - struct v4l2_of_endpoint endpoint; + struct v4l2_fwnode_endpoint endpoint; struct v4l2_async_subdev asd; struct v4l2_async_subdev *asd_list[1]; @@ -608,7 +608,8 @@ static void csi2_lane_config(struct cal_ctx *ctx) u32 val = reg_read(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port)); u32 lane_mask = CAL_CSI2_COMPLEXIO_CFG_CLOCK_POSITION_MASK; u32 polarity_mask = CAL_CSI2_COMPLEXIO_CFG_CLOCK_POL_MASK; - struct v4l2_of_bus_mipi_csi2 *mipi_csi2 = &ctx->endpoint.bus.mipi_csi2; + struct v4l2_fwnode_bus_mipi_csi2 *mipi_csi2 = + &ctx->endpoint.bus.mipi_csi2; int lane; set_field(&val, mipi_csi2->clock_lane + 1, lane_mask); @@ -1643,7 +1644,7 @@ static int of_cal_create_instance(struct cal_ctx *ctx, int inst) struct platform_device *pdev = ctx->dev->pdev; struct device_node *ep_node, *port, *remote_ep, *sensor_node, *parent; - struct v4l2_of_endpoint *endpoint; + struct v4l2_fwnode_endpoint *endpoint; struct v4l2_async_subdev *asd; u32 regval = 0; int ret, index, found_port = 0, lane; @@ -1698,15 +1699,15 @@ static int of_cal_create_instance(struct cal_ctx *ctx, int inst) ctx_dbg(3, ctx, "can't get remote parent\n"); goto cleanup_exit; } - asd->match_type = V4L2_ASYNC_MATCH_OF; - asd->match.of.node = sensor_node; + asd->match_type = V4L2_ASYNC_MATCH_FWNODE; + asd->match.fwnode.fwnode = of_fwnode_handle(sensor_node); remote_ep = of_parse_phandle(ep_node, "remote-endpoint", 0); if (!remote_ep) { ctx_dbg(3, ctx, "can't get remote-endpoint\n"); goto cleanup_exit; } - v4l2_of_parse_endpoint(remote_ep, endpoint); + v4l2_fwnode_endpoint_parse(of_fwnode_handle(remote_ep), endpoint); if (endpoint->bus_type != V4L2_MBUS_CSI2) { ctx_err(ctx, "Port:%d sub-device %s is not a CSI2 device\n", diff --git a/drivers/media/platform/xilinx/Kconfig b/drivers/media/platform/xilinx/Kconfig index 84bae795b70d..a5d21b7c6e0b 100644 --- a/drivers/media/platform/xilinx/Kconfig +++ b/drivers/media/platform/xilinx/Kconfig @@ -2,6 +2,7 @@ config VIDEO_XILINX tristate "Xilinx Video IP (EXPERIMENTAL)" depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && OF && HAS_DMA select VIDEOBUF2_DMA_CONTIG + select V4L2_FWNODE ---help--- Driver for Xilinx Video IP Pipelines diff --git a/drivers/media/platform/xilinx/xilinx-vipp.c b/drivers/media/platform/xilinx/xilinx-vipp.c index feb3b2f1d874..ac4704388920 100644 --- a/drivers/media/platform/xilinx/xilinx-vipp.c +++ b/drivers/media/platform/xilinx/xilinx-vipp.c @@ -22,7 +22,7 @@ #include #include #include -#include +#include #include "xilinx-dma.h" #include "xilinx-vipp.h" @@ -74,7 +74,7 @@ static int xvip_graph_build_one(struct xvip_composite_device *xdev, struct media_pad *local_pad; struct media_pad *remote_pad; struct xvip_graph_entity *ent; - struct v4l2_of_link link; + struct v4l2_fwnode_link link; struct device_node *ep = NULL; struct device_node *next; int ret = 0; @@ -92,7 +92,7 @@ static int xvip_graph_build_one(struct xvip_composite_device *xdev, dev_dbg(xdev->dev, "processing endpoint %s\n", ep->full_name); - ret = v4l2_of_parse_link(ep, &link); + ret = v4l2_fwnode_parse_link(of_fwnode_handle(ep), &link); if (ret < 0) { dev_err(xdev->dev, "failed to parse link for %s\n", ep->full_name); @@ -103,9 +103,10 @@ static int xvip_graph_build_one(struct xvip_composite_device *xdev, * the link. */ if (link.local_port >= local->num_pads) { - dev_err(xdev->dev, "invalid port number %u on %s\n", - link.local_port, link.local_node->full_name); - v4l2_of_put_link(&link); + dev_err(xdev->dev, "invalid port number %u for %s\n", + link.local_port, + to_of_node(link.local_node)->full_name); + v4l2_fwnode_put_link(&link); ret = -EINVAL; break; } @@ -114,25 +115,28 @@ static int xvip_graph_build_one(struct xvip_composite_device *xdev, if (local_pad->flags & MEDIA_PAD_FL_SINK) { dev_dbg(xdev->dev, "skipping sink port %s:%u\n", - link.local_node->full_name, link.local_port); - v4l2_of_put_link(&link); + to_of_node(link.local_node)->full_name, + link.local_port); + v4l2_fwnode_put_link(&link); continue; } /* Skip DMA engines, they will be processed separately. */ - if (link.remote_node == xdev->dev->of_node) { + if (link.remote_node == of_fwnode_handle(xdev->dev->of_node)) { dev_dbg(xdev->dev, "skipping DMA port %s:%u\n", - link.local_node->full_name, link.local_port); - v4l2_of_put_link(&link); + to_of_node(link.local_node)->full_name, + link.local_port); + v4l2_fwnode_put_link(&link); continue; } /* Find the remote entity. */ - ent = xvip_graph_find_entity(xdev, link.remote_node); + ent = xvip_graph_find_entity(xdev, + to_of_node(link.remote_node)); if (ent == NULL) { dev_err(xdev->dev, "no entity found for %s\n", - link.remote_node->full_name); - v4l2_of_put_link(&link); + to_of_node(link.remote_node)->full_name); + v4l2_fwnode_put_link(&link); ret = -ENODEV; break; } @@ -141,15 +145,16 @@ static int xvip_graph_build_one(struct xvip_composite_device *xdev, if (link.remote_port >= remote->num_pads) { dev_err(xdev->dev, "invalid port number %u on %s\n", - link.remote_port, link.remote_node->full_name); - v4l2_of_put_link(&link); + link.remote_port, + to_of_node(link.remote_node)->full_name); + v4l2_fwnode_put_link(&link); ret = -EINVAL; break; } remote_pad = &remote->pads[link.remote_port]; - v4l2_of_put_link(&link); + v4l2_fwnode_put_link(&link); /* Create the media link. */ dev_dbg(xdev->dev, "creating %s:%u -> %s:%u link\n", @@ -194,7 +199,7 @@ static int xvip_graph_build_dma(struct xvip_composite_device *xdev) struct media_pad *source_pad; struct media_pad *sink_pad; struct xvip_graph_entity *ent; - struct v4l2_of_link link; + struct v4l2_fwnode_link link; struct device_node *ep = NULL; struct device_node *next; struct xvip_dma *dma; @@ -213,7 +218,7 @@ static int xvip_graph_build_dma(struct xvip_composite_device *xdev) dev_dbg(xdev->dev, "processing endpoint %s\n", ep->full_name); - ret = v4l2_of_parse_link(ep, &link); + ret = v4l2_fwnode_parse_link(of_fwnode_handle(ep), &link); if (ret < 0) { dev_err(xdev->dev, "failed to parse link for %s\n", ep->full_name); @@ -225,7 +230,7 @@ static int xvip_graph_build_dma(struct xvip_composite_device *xdev) if (dma == NULL) { dev_err(xdev->dev, "no DMA engine found for port %u\n", link.local_port); - v4l2_of_put_link(&link); + v4l2_fwnode_put_link(&link); ret = -EINVAL; break; } @@ -234,19 +239,21 @@ static int xvip_graph_build_dma(struct xvip_composite_device *xdev) dma->video.name); /* Find the remote entity. */ - ent = xvip_graph_find_entity(xdev, link.remote_node); + ent = xvip_graph_find_entity(xdev, + to_of_node(link.remote_node)); if (ent == NULL) { dev_err(xdev->dev, "no entity found for %s\n", - link.remote_node->full_name); - v4l2_of_put_link(&link); + to_of_node(link.remote_node)->full_name); + v4l2_fwnode_put_link(&link); ret = -ENODEV; break; } if (link.remote_port >= ent->entity->num_pads) { dev_err(xdev->dev, "invalid port number %u on %s\n", - link.remote_port, link.remote_node->full_name); - v4l2_of_put_link(&link); + link.remote_port, + to_of_node(link.remote_node)->full_name); + v4l2_fwnode_put_link(&link); ret = -EINVAL; break; } @@ -263,7 +270,7 @@ static int xvip_graph_build_dma(struct xvip_composite_device *xdev) sink_pad = &dma->pad; } - v4l2_of_put_link(&link); + v4l2_fwnode_put_link(&link); /* Create the media link. */ dev_dbg(xdev->dev, "creating %s:%u -> %s:%u link\n", @@ -383,8 +390,8 @@ static int xvip_graph_parse_one(struct xvip_composite_device *xdev, } entity->node = remote; - entity->asd.match_type = V4L2_ASYNC_MATCH_OF; - entity->asd.match.of.node = remote; + entity->asd.match_type = V4L2_ASYNC_MATCH_FWNODE; + entity->asd.match.fwnode.fwnode = of_fwnode_handle(remote); list_add_tail(&entity->list, &xdev->entities); xdev->num_subdevs++; } diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c index ff32f95d94f0..cbd919d4edd2 100644 --- a/drivers/media/v4l2-core/v4l2-async.c +++ b/drivers/media/v4l2-core/v4l2-async.c @@ -41,12 +41,6 @@ static bool match_devname(struct v4l2_subdev *sd, return !strcmp(asd->match.device_name.name, dev_name(sd->dev)); } -static bool match_of(struct v4l2_subdev *sd, struct v4l2_async_subdev *asd) -{ - return !of_node_cmp(of_node_full_name(sd->of_node), - of_node_full_name(asd->match.of.node)); -} - static bool match_fwnode(struct v4l2_subdev *sd, struct v4l2_async_subdev *asd) { if (!is_of_node(sd->fwnode) || !is_of_node(asd->match.fwnode.fwnode)) @@ -88,9 +82,6 @@ static struct v4l2_async_subdev *v4l2_async_belongs(struct v4l2_async_notifier * case V4L2_ASYNC_MATCH_I2C: match = match_i2c; break; - case V4L2_ASYNC_MATCH_OF: - match = match_of; - break; case V4L2_ASYNC_MATCH_FWNODE: match = match_fwnode; break; @@ -171,7 +162,6 @@ int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev, case V4L2_ASYNC_MATCH_CUSTOM: case V4L2_ASYNC_MATCH_DEVNAME: case V4L2_ASYNC_MATCH_I2C: - case V4L2_ASYNC_MATCH_OF: case V4L2_ASYNC_MATCH_FWNODE: break; default: @@ -295,8 +285,8 @@ int v4l2_async_register_subdev(struct v4l2_subdev *sd) * (struct v4l2_subdev.dev), and async sub-device does not * exist independently of the device at any point of time. */ - if (!sd->of_node && sd->dev) - sd->of_node = sd->dev->of_node; + if (!sd->fwnode && sd->dev) + sd->fwnode = dev_fwnode(sd->dev); mutex_lock(&list_lock); diff --git a/include/media/v4l2-async.h b/include/media/v4l2-async.h index c3695fa2c903..c69d8c8a66d0 100644 --- a/include/media/v4l2-async.h +++ b/include/media/v4l2-async.h @@ -31,7 +31,6 @@ struct v4l2_async_notifier; * v4l2_async_subdev.match ops * @V4L2_ASYNC_MATCH_DEVNAME: Match will use the device name * @V4L2_ASYNC_MATCH_I2C: Match will check for I2C adapter ID and address - * @V4L2_ASYNC_MATCH_OF: Match will use OF node * @V4L2_ASYNC_MATCH_FWNODE: Match will use firmware node * * This enum is used by the asyncrhronous sub-device logic to define the @@ -41,7 +40,6 @@ enum v4l2_async_match_type { V4L2_ASYNC_MATCH_CUSTOM, V4L2_ASYNC_MATCH_DEVNAME, V4L2_ASYNC_MATCH_I2C, - V4L2_ASYNC_MATCH_OF, V4L2_ASYNC_MATCH_FWNODE, }; @@ -56,9 +54,6 @@ enum v4l2_async_match_type { struct v4l2_async_subdev { enum v4l2_async_match_type match_type; union { - struct { - const struct device_node *node; - } of; struct { struct fwnode_handle *fwnode; } fwnode; diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index 5f1669c45642..a40760174797 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -787,7 +787,6 @@ struct v4l2_subdev_platform_data { * is attached. * @devnode: subdev device node * @dev: pointer to the physical device, if any - * @of_node: The device_node of the subdev, usually the same as dev->of_node. * @fwnode: The fwnode_handle of the subdev, usually the same as * either dev->of_node->fwnode or dev->fwnode (whichever is non-NULL). * @async_list: Links this subdev to a global subdev_list or @notifier->done @@ -820,7 +819,6 @@ struct v4l2_subdev { void *host_priv; struct video_device *devnode; struct device *dev; - struct device_node *of_node; struct fwnode_handle *fwnode; struct list_head async_list; struct v4l2_async_subdev *asd; -- cgit v1.2.3-58-ga151 From d6a60897e3db4b86112cfca318e5cd4f5bbfcda1 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Thu, 9 Mar 2017 08:21:46 -0300 Subject: [media] docs-rst: media: Switch documentation to V4L2 fwnode API Instead of including the V4L2 OF header in ReST documentation, use the V4L2 fwnode header instead. Signed-off-by: Sakari Ailus Reviewed-by: Laurent Pinchart Tested-by: Hans Verkuil Tested-by: Philipp Zabel Signed-off-by: Mauro Carvalho Chehab --- Documentation/media/kapi/v4l2-core.rst | 2 +- Documentation/media/kapi/v4l2-fwnode.rst | 3 +++ Documentation/media/kapi/v4l2-of.rst | 3 --- 3 files changed, 4 insertions(+), 4 deletions(-) create mode 100644 Documentation/media/kapi/v4l2-fwnode.rst delete mode 100644 Documentation/media/kapi/v4l2-of.rst diff --git a/Documentation/media/kapi/v4l2-core.rst b/Documentation/media/kapi/v4l2-core.rst index d8f6c46d26d5..c7434f38fd9c 100644 --- a/Documentation/media/kapi/v4l2-core.rst +++ b/Documentation/media/kapi/v4l2-core.rst @@ -19,7 +19,7 @@ Video4Linux devices v4l2-mc v4l2-mediabus v4l2-mem2mem - v4l2-of + v4l2-fwnode v4l2-rect v4l2-tuner v4l2-common diff --git a/Documentation/media/kapi/v4l2-fwnode.rst b/Documentation/media/kapi/v4l2-fwnode.rst new file mode 100644 index 000000000000..6c8bccdfeb25 --- /dev/null +++ b/Documentation/media/kapi/v4l2-fwnode.rst @@ -0,0 +1,3 @@ +V4L2 fwnode kAPI +^^^^^^^^^^^^^^^^ +.. kernel-doc:: include/media/v4l2-fwnode.h diff --git a/Documentation/media/kapi/v4l2-of.rst b/Documentation/media/kapi/v4l2-of.rst deleted file mode 100644 index 1ddf76b00944..000000000000 --- a/Documentation/media/kapi/v4l2-of.rst +++ /dev/null @@ -1,3 +0,0 @@ -V4L2 Open Firmware kAPI -^^^^^^^^^^^^^^^^^^^^^^^ -.. kernel-doc:: include/media/v4l2-of.h -- cgit v1.2.3-58-ga151 From 652e535efa745e173f1de28a3aa211a1e4ff38da Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Wed, 11 Jan 2017 08:37:32 -0200 Subject: [media] v4l: Remove V4L2 OF framework in favour of V4L2 fwnode framework All drivers have been converted from V4L2 OF to V4L2 fwnode. The V4L2 OF framework is now unused. Remove it. Signed-off-by: Sakari Ailus Reviewed-by: Laurent Pinchart Tested-by: Hans Verkuil Tested-by: Philipp Zabel Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/Makefile | 3 - drivers/media/v4l2-core/v4l2-of.c | 327 -------------------------------------- include/media/v4l2-of.h | 128 --------------- 3 files changed, 458 deletions(-) delete mode 100644 drivers/media/v4l2-core/v4l2-of.c delete mode 100644 include/media/v4l2-of.h diff --git a/drivers/media/v4l2-core/Makefile b/drivers/media/v4l2-core/Makefile index cf77a6328feb..098ad5fd5231 100644 --- a/drivers/media/v4l2-core/Makefile +++ b/drivers/media/v4l2-core/Makefile @@ -10,9 +10,6 @@ videodev-objs := v4l2-dev.o v4l2-ioctl.o v4l2-device.o v4l2-fh.o \ ifeq ($(CONFIG_COMPAT),y) videodev-objs += v4l2-compat-ioctl32.o endif -ifeq ($(CONFIG_OF),y) - videodev-objs += v4l2-of.o -endif obj-$(CONFIG_V4L2_FWNODE) += v4l2-fwnode.o ifeq ($(CONFIG_TRACEPOINTS),y) videodev-objs += vb2-trace.o v4l2-trace.o diff --git a/drivers/media/v4l2-core/v4l2-of.c b/drivers/media/v4l2-core/v4l2-of.c deleted file mode 100644 index 4f59f442dd0a..000000000000 --- a/drivers/media/v4l2-core/v4l2-of.c +++ /dev/null @@ -1,327 +0,0 @@ -/* - * V4L2 OF binding parsing library - * - * Copyright (C) 2012 - 2013 Samsung Electronics Co., Ltd. - * Author: Sylwester Nawrocki - * - * Copyright (C) 2012 Renesas Electronics Corp. - * Author: Guennadi Liakhovetski - * - * 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 -#include -#include -#include -#include -#include - -#include - -static int v4l2_of_parse_csi_bus(const struct device_node *node, - struct v4l2_of_endpoint *endpoint) -{ - struct v4l2_of_bus_mipi_csi2 *bus = &endpoint->bus.mipi_csi2; - struct property *prop; - bool have_clk_lane = false; - unsigned int flags = 0, lanes_used = 0; - u32 v; - - prop = of_find_property(node, "data-lanes", NULL); - if (prop) { - const __be32 *lane = NULL; - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(bus->data_lanes); i++) { - lane = of_prop_next_u32(prop, lane, &v); - if (!lane) - break; - - if (lanes_used & BIT(v)) - pr_warn("%s: duplicated lane %u in data-lanes\n", - node->full_name, v); - lanes_used |= BIT(v); - - bus->data_lanes[i] = v; - } - bus->num_data_lanes = i; - } - - prop = of_find_property(node, "lane-polarities", NULL); - if (prop) { - const __be32 *polarity = NULL; - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(bus->lane_polarities); i++) { - polarity = of_prop_next_u32(prop, polarity, &v); - if (!polarity) - break; - bus->lane_polarities[i] = v; - } - - if (i < 1 + bus->num_data_lanes /* clock + data */) { - pr_warn("%s: too few lane-polarities entries (need %u, got %u)\n", - node->full_name, 1 + bus->num_data_lanes, i); - return -EINVAL; - } - } - - if (!of_property_read_u32(node, "clock-lanes", &v)) { - if (lanes_used & BIT(v)) - pr_warn("%s: duplicated lane %u in clock-lanes\n", - node->full_name, v); - lanes_used |= BIT(v); - - bus->clock_lane = v; - have_clk_lane = true; - } - - if (of_get_property(node, "clock-noncontinuous", &v)) - flags |= V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK; - else if (have_clk_lane || bus->num_data_lanes > 0) - flags |= V4L2_MBUS_CSI2_CONTINUOUS_CLOCK; - - bus->flags = flags; - endpoint->bus_type = V4L2_MBUS_CSI2; - - return 0; -} - -static void v4l2_of_parse_parallel_bus(const struct device_node *node, - struct v4l2_of_endpoint *endpoint) -{ - struct v4l2_of_bus_parallel *bus = &endpoint->bus.parallel; - unsigned int flags = 0; - u32 v; - - if (!of_property_read_u32(node, "hsync-active", &v)) - flags |= v ? V4L2_MBUS_HSYNC_ACTIVE_HIGH : - V4L2_MBUS_HSYNC_ACTIVE_LOW; - - if (!of_property_read_u32(node, "vsync-active", &v)) - flags |= v ? V4L2_MBUS_VSYNC_ACTIVE_HIGH : - V4L2_MBUS_VSYNC_ACTIVE_LOW; - - if (!of_property_read_u32(node, "field-even-active", &v)) - flags |= v ? V4L2_MBUS_FIELD_EVEN_HIGH : - V4L2_MBUS_FIELD_EVEN_LOW; - if (flags) - endpoint->bus_type = V4L2_MBUS_PARALLEL; - else - endpoint->bus_type = V4L2_MBUS_BT656; - - if (!of_property_read_u32(node, "pclk-sample", &v)) - flags |= v ? V4L2_MBUS_PCLK_SAMPLE_RISING : - V4L2_MBUS_PCLK_SAMPLE_FALLING; - - if (!of_property_read_u32(node, "data-active", &v)) - flags |= v ? V4L2_MBUS_DATA_ACTIVE_HIGH : - V4L2_MBUS_DATA_ACTIVE_LOW; - - if (of_get_property(node, "slave-mode", &v)) - flags |= V4L2_MBUS_SLAVE; - else - flags |= V4L2_MBUS_MASTER; - - if (!of_property_read_u32(node, "bus-width", &v)) - bus->bus_width = v; - - if (!of_property_read_u32(node, "data-shift", &v)) - bus->data_shift = v; - - if (!of_property_read_u32(node, "sync-on-green-active", &v)) - flags |= v ? V4L2_MBUS_VIDEO_SOG_ACTIVE_HIGH : - V4L2_MBUS_VIDEO_SOG_ACTIVE_LOW; - - bus->flags = flags; - -} - -/** - * v4l2_of_parse_endpoint() - parse all endpoint node properties - * @node: pointer to endpoint device_node - * @endpoint: pointer to the V4L2 OF endpoint data structure - * - * All properties are optional. If none are found, we don't set any flags. - * This means the port has a static configuration and no properties have - * to be specified explicitly. - * If any properties that identify the bus as parallel are found and - * slave-mode isn't set, we set V4L2_MBUS_MASTER. Similarly, if we recognise - * the bus as serial CSI-2 and clock-noncontinuous isn't set, we set the - * V4L2_MBUS_CSI2_CONTINUOUS_CLOCK flag. - * The caller should hold a reference to @node. - * - * NOTE: This function does not parse properties the size of which is - * variable without a low fixed limit. Please use - * v4l2_of_alloc_parse_endpoint() in new drivers instead. - * - * Return: 0 on success or a negative error code on failure. - */ -int v4l2_of_parse_endpoint(const struct device_node *node, - struct v4l2_of_endpoint *endpoint) -{ - int rval; - - of_graph_parse_endpoint(node, &endpoint->base); - /* Zero fields from bus_type to until the end */ - memset(&endpoint->bus_type, 0, sizeof(*endpoint) - - offsetof(typeof(*endpoint), bus_type)); - - rval = v4l2_of_parse_csi_bus(node, endpoint); - if (rval) - return rval; - /* - * Parse the parallel video bus properties only if none - * of the MIPI CSI-2 specific properties were found. - */ - if (endpoint->bus.mipi_csi2.flags == 0) - v4l2_of_parse_parallel_bus(node, endpoint); - - return 0; -} -EXPORT_SYMBOL(v4l2_of_parse_endpoint); - -/* - * v4l2_of_free_endpoint() - free the endpoint acquired by - * v4l2_of_alloc_parse_endpoint() - * @endpoint - the endpoint the resources of which are to be released - * - * It is safe to call this function with NULL argument or on an - * endpoint the parsing of which failed. - */ -void v4l2_of_free_endpoint(struct v4l2_of_endpoint *endpoint) -{ - if (IS_ERR_OR_NULL(endpoint)) - return; - - kfree(endpoint->link_frequencies); - kfree(endpoint); -} -EXPORT_SYMBOL(v4l2_of_free_endpoint); - -/** - * v4l2_of_alloc_parse_endpoint() - parse all endpoint node properties - * @node: pointer to endpoint device_node - * - * All properties are optional. If none are found, we don't set any flags. - * This means the port has a static configuration and no properties have - * to be specified explicitly. - * If any properties that identify the bus as parallel are found and - * slave-mode isn't set, we set V4L2_MBUS_MASTER. Similarly, if we recognise - * the bus as serial CSI-2 and clock-noncontinuous isn't set, we set the - * V4L2_MBUS_CSI2_CONTINUOUS_CLOCK flag. - * The caller should hold a reference to @node. - * - * v4l2_of_alloc_parse_endpoint() has two important differences to - * v4l2_of_parse_endpoint(): - * - * 1. It also parses variable size data and - * - * 2. The memory it has allocated to store the variable size data must - * be freed using v4l2_of_free_endpoint() when no longer needed. - * - * Return: Pointer to v4l2_of_endpoint if successful, on error a - * negative error code. - */ -struct v4l2_of_endpoint *v4l2_of_alloc_parse_endpoint( - const struct device_node *node) -{ - struct v4l2_of_endpoint *endpoint; - int len; - int rval; - - endpoint = kzalloc(sizeof(*endpoint), GFP_KERNEL); - if (!endpoint) - return ERR_PTR(-ENOMEM); - - rval = v4l2_of_parse_endpoint(node, endpoint); - if (rval < 0) - goto out_err; - - if (of_get_property(node, "link-frequencies", &len)) { - endpoint->link_frequencies = kmalloc(len, GFP_KERNEL); - if (!endpoint->link_frequencies) { - rval = -ENOMEM; - goto out_err; - } - - endpoint->nr_of_link_frequencies = - len / sizeof(*endpoint->link_frequencies); - - rval = of_property_read_u64_array( - node, "link-frequencies", endpoint->link_frequencies, - endpoint->nr_of_link_frequencies); - if (rval < 0) - goto out_err; - } - - return endpoint; - -out_err: - v4l2_of_free_endpoint(endpoint); - return ERR_PTR(rval); -} -EXPORT_SYMBOL(v4l2_of_alloc_parse_endpoint); - -/** - * v4l2_of_parse_link() - parse a link between two endpoints - * @node: pointer to the endpoint at the local end of the link - * @link: pointer to the V4L2 OF link data structure - * - * Fill the link structure with the local and remote nodes and port numbers. - * The local_node and remote_node fields are set to point to the local and - * remote port's parent nodes respectively (the port parent node being the - * parent node of the port node if that node isn't a 'ports' node, or the - * grand-parent node of the port node otherwise). - * - * A reference is taken to both the local and remote nodes, the caller must use - * v4l2_of_put_link() to drop the references when done with the link. - * - * Return: 0 on success, or -ENOLINK if the remote endpoint can't be found. - */ -int v4l2_of_parse_link(const struct device_node *node, - struct v4l2_of_link *link) -{ - struct device_node *np; - - memset(link, 0, sizeof(*link)); - - np = of_get_parent(node); - of_property_read_u32(np, "reg", &link->local_port); - np = of_get_next_parent(np); - if (of_node_cmp(np->name, "ports") == 0) - np = of_get_next_parent(np); - link->local_node = np; - - np = of_parse_phandle(node, "remote-endpoint", 0); - if (!np) { - of_node_put(link->local_node); - return -ENOLINK; - } - - np = of_get_parent(np); - of_property_read_u32(np, "reg", &link->remote_port); - np = of_get_next_parent(np); - if (of_node_cmp(np->name, "ports") == 0) - np = of_get_next_parent(np); - link->remote_node = np; - - return 0; -} -EXPORT_SYMBOL(v4l2_of_parse_link); - -/** - * v4l2_of_put_link() - drop references to nodes in a link - * @link: pointer to the V4L2 OF link data structure - * - * Drop references to the local and remote nodes in the link. This function must - * be called on every link parsed with v4l2_of_parse_link(). - */ -void v4l2_of_put_link(struct v4l2_of_link *link) -{ - of_node_put(link->local_node); - of_node_put(link->remote_node); -} -EXPORT_SYMBOL(v4l2_of_put_link); diff --git a/include/media/v4l2-of.h b/include/media/v4l2-of.h deleted file mode 100644 index 4dc34b245d47..000000000000 --- a/include/media/v4l2-of.h +++ /dev/null @@ -1,128 +0,0 @@ -/* - * V4L2 OF binding parsing library - * - * Copyright (C) 2012 - 2013 Samsung Electronics Co., Ltd. - * Author: Sylwester Nawrocki - * - * Copyright (C) 2012 Renesas Electronics Corp. - * Author: Guennadi Liakhovetski - * - * 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. - */ -#ifndef _V4L2_OF_H -#define _V4L2_OF_H - -#include -#include -#include -#include - -#include - -struct device_node; - -/** - * struct v4l2_of_bus_mipi_csi2 - MIPI CSI-2 bus data structure - * @flags: media bus (V4L2_MBUS_*) flags - * @data_lanes: an array of physical data lane indexes - * @clock_lane: physical lane index of the clock lane - * @num_data_lanes: number of data lanes - * @lane_polarities: polarity of the lanes. The order is the same of - * the physical lanes. - */ -struct v4l2_of_bus_mipi_csi2 { - unsigned int flags; - unsigned char data_lanes[4]; - unsigned char clock_lane; - unsigned short num_data_lanes; - bool lane_polarities[5]; -}; - -/** - * struct v4l2_of_bus_parallel - parallel data bus data structure - * @flags: media bus (V4L2_MBUS_*) flags - * @bus_width: bus width in bits - * @data_shift: data shift in bits - */ -struct v4l2_of_bus_parallel { - unsigned int flags; - unsigned char bus_width; - unsigned char data_shift; -}; - -/** - * struct v4l2_of_endpoint - the endpoint data structure - * @base: struct of_endpoint containing port, id, and local of_node - * @bus_type: bus type - * @bus: bus configuration data structure - * @link_frequencies: array of supported link frequencies - * @nr_of_link_frequencies: number of elements in link_frequenccies array - */ -struct v4l2_of_endpoint { - struct of_endpoint base; - /* Fields below this line will be zeroed by v4l2_of_parse_endpoint() */ - enum v4l2_mbus_type bus_type; - union { - struct v4l2_of_bus_parallel parallel; - struct v4l2_of_bus_mipi_csi2 mipi_csi2; - } bus; - u64 *link_frequencies; - unsigned int nr_of_link_frequencies; -}; - -/** - * struct v4l2_of_link - a link between two endpoints - * @local_node: pointer to device_node of this endpoint - * @local_port: identifier of the port this endpoint belongs to - * @remote_node: pointer to device_node of the remote endpoint - * @remote_port: identifier of the port the remote endpoint belongs to - */ -struct v4l2_of_link { - struct device_node *local_node; - unsigned int local_port; - struct device_node *remote_node; - unsigned int remote_port; -}; - -#ifdef CONFIG_OF -int v4l2_of_parse_endpoint(const struct device_node *node, - struct v4l2_of_endpoint *endpoint); -struct v4l2_of_endpoint *v4l2_of_alloc_parse_endpoint( - const struct device_node *node); -void v4l2_of_free_endpoint(struct v4l2_of_endpoint *endpoint); -int v4l2_of_parse_link(const struct device_node *node, - struct v4l2_of_link *link); -void v4l2_of_put_link(struct v4l2_of_link *link); -#else /* CONFIG_OF */ - -static inline int v4l2_of_parse_endpoint(const struct device_node *node, - struct v4l2_of_endpoint *link) -{ - return -ENOSYS; -} - -static inline struct v4l2_of_endpoint *v4l2_of_alloc_parse_endpoint( - const struct device_node *node) -{ - return NULL; -} - -static inline void v4l2_of_free_endpoint(struct v4l2_of_endpoint *endpoint) -{ -} - -static inline int v4l2_of_parse_link(const struct device_node *node, - struct v4l2_of_link *link) -{ - return -ENOSYS; -} - -static inline void v4l2_of_put_link(struct v4l2_of_link *link) -{ -} - -#endif /* CONFIG_OF */ - -#endif /* _V4L2_OF_H */ -- cgit v1.2.3-58-ga151 From 6afaaab06a47f605424f1cbbcf10159fafdc12a8 Mon Sep 17 00:00:00 2001 From: Niklas Söderlund Date: Tue, 23 May 2017 21:15:24 -0300 Subject: [media] rcar-vin: reset bytesperline and sizeimage when resetting format MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These two were forgotten when refactoring the format reset code. If they are not also reset at the same time as width and height the format returned from G_FMT will not match reality. Signed-off-by: Niklas Söderlund Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rcar-vin/rcar-v4l2.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/media/platform/rcar-vin/rcar-v4l2.c b/drivers/media/platform/rcar-vin/rcar-v4l2.c index 2bbe6d495fa6..69bc4cfea6a8 100644 --- a/drivers/media/platform/rcar-vin/rcar-v4l2.c +++ b/drivers/media/platform/rcar-vin/rcar-v4l2.c @@ -151,6 +151,9 @@ static int rvin_reset_format(struct rvin_dev *vin) rvin_reset_crop_compose(vin); + vin->format.bytesperline = rvin_format_bytesperline(&vin->format); + vin->format.sizeimage = rvin_format_sizeimage(&vin->format); + return 0; } -- cgit v1.2.3-58-ga151 From b655741f2149296bb1b9efc1161d7125718108d7 Mon Sep 17 00:00:00 2001 From: Niklas Söderlund Date: Tue, 23 May 2017 21:15:25 -0300 Subject: [media] rcar-vin: use rvin_reset_format() in S_DV_TIMINGS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use rvin_reset_format() in rvin_s_dv_timings() instead of just resetting a few fields. This fixes an issue where the field format was not properly set after S_DV_TIMINGS. Signed-off-by: Niklas Söderlund Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rcar-vin/rcar-v4l2.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/media/platform/rcar-vin/rcar-v4l2.c b/drivers/media/platform/rcar-vin/rcar-v4l2.c index 69bc4cfea6a8..7ca27599b998 100644 --- a/drivers/media/platform/rcar-vin/rcar-v4l2.c +++ b/drivers/media/platform/rcar-vin/rcar-v4l2.c @@ -573,12 +573,8 @@ static int rvin_s_dv_timings(struct file *file, void *priv_fh, if (ret) return ret; - vin->source.width = timings->bt.width; - vin->source.height = timings->bt.height; - vin->format.width = timings->bt.width; - vin->format.height = timings->bt.height; - - return 0; + /* Changing the timings will change the width/height */ + return rvin_reset_format(vin); } static int rvin_g_dv_timings(struct file *file, void *priv_fh, -- cgit v1.2.3-58-ga151 From 831081626f78f09fd99564988b6c09bfc4ee6df7 Mon Sep 17 00:00:00 2001 From: Niklas Söderlund Date: Tue, 23 May 2017 21:15:26 -0300 Subject: [media] rcar-vin: fix how pads are handled for v4l2 subdevice operations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The rcar-vin driver only uses one pad, pad number 0. - All v4l2 operations that did not check that the requested operation was for pad 0 have been updated with a check to enforce this. - All v4l2 operations that stored (and later restored) the requested pad before substituting it for the subdevice pad number have been updated to not store the incoming pad and simply restore it to 0 after the subdevice operation is complete. Signed-off-by: Niklas Söderlund Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rcar-vin/rcar-v4l2.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/drivers/media/platform/rcar-vin/rcar-v4l2.c b/drivers/media/platform/rcar-vin/rcar-v4l2.c index 7ca27599b998..610f59e2a914 100644 --- a/drivers/media/platform/rcar-vin/rcar-v4l2.c +++ b/drivers/media/platform/rcar-vin/rcar-v4l2.c @@ -550,14 +550,16 @@ static int rvin_enum_dv_timings(struct file *file, void *priv_fh, { struct rvin_dev *vin = video_drvdata(file); struct v4l2_subdev *sd = vin_to_source(vin); - int pad, ret; + int ret; + + if (timings->pad) + return -EINVAL; - pad = timings->pad; timings->pad = vin->sink_pad_idx; ret = v4l2_subdev_call(sd, pad, enum_dv_timings, timings); - timings->pad = pad; + timings->pad = 0; return ret; } @@ -600,14 +602,16 @@ static int rvin_dv_timings_cap(struct file *file, void *priv_fh, { struct rvin_dev *vin = video_drvdata(file); struct v4l2_subdev *sd = vin_to_source(vin); - int pad, ret; + int ret; + + if (cap->pad) + return -EINVAL; - pad = cap->pad; cap->pad = vin->sink_pad_idx; ret = v4l2_subdev_call(sd, pad, dv_timings_cap, cap); - cap->pad = pad; + cap->pad = 0; return ret; } @@ -616,17 +620,16 @@ static int rvin_g_edid(struct file *file, void *fh, struct v4l2_edid *edid) { struct rvin_dev *vin = video_drvdata(file); struct v4l2_subdev *sd = vin_to_source(vin); - int input, ret; + int ret; if (edid->pad) return -EINVAL; - input = edid->pad; edid->pad = vin->sink_pad_idx; ret = v4l2_subdev_call(sd, pad, get_edid, edid); - edid->pad = input; + edid->pad = 0; return ret; } @@ -635,17 +638,16 @@ static int rvin_s_edid(struct file *file, void *fh, struct v4l2_edid *edid) { struct rvin_dev *vin = video_drvdata(file); struct v4l2_subdev *sd = vin_to_source(vin); - int input, ret; + int ret; if (edid->pad) return -EINVAL; - input = edid->pad; edid->pad = vin->sink_pad_idx; ret = v4l2_subdev_call(sd, pad, set_edid, edid); - edid->pad = input; + edid->pad = 0; return ret; } -- cgit v1.2.3-58-ga151 From 0ad17864613f29726f56d346091a9176a359e615 Mon Sep 17 00:00:00 2001 From: Niklas Söderlund Date: Tue, 23 May 2017 21:15:27 -0300 Subject: [media] rcar-vin: fix standard in input enumeration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver supports a single input only, which can be either analog or digital. If the subdevice supports dv_timings_cap the input is digital and the driver should not fill in the standard. Signed-off-by: Niklas Söderlund Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rcar-vin/rcar-v4l2.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/rcar-vin/rcar-v4l2.c b/drivers/media/platform/rcar-vin/rcar-v4l2.c index 610f59e2a914..7be52c2036bb 100644 --- a/drivers/media/platform/rcar-vin/rcar-v4l2.c +++ b/drivers/media/platform/rcar-vin/rcar-v4l2.c @@ -483,10 +483,14 @@ static int rvin_enum_input(struct file *file, void *priv, return ret; i->type = V4L2_INPUT_TYPE_CAMERA; - i->std = vin->vdev.tvnorms; - if (v4l2_subdev_has_op(sd, pad, dv_timings_cap)) + if (v4l2_subdev_has_op(sd, pad, dv_timings_cap)) { i->capabilities = V4L2_IN_CAP_DV_TIMINGS; + i->std = 0; + } else { + i->capabilities = V4L2_IN_CAP_STD; + i->std = vin->vdev.tvnorms; + } strlcpy(i->name, "Camera", sizeof(i->name)); -- cgit v1.2.3-58-ga151 From 1eb36419ecc3a93e068f6bff6297a1e6e55fb33d Mon Sep 17 00:00:00 2001 From: Niklas Söderlund Date: Tue, 23 May 2017 21:15:28 -0300 Subject: [media] rcar-vin: move subdev source and sink pad index to rvin_graph_entity MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It makes more sense to store the sink and source pads in struct rvin_graph_entity since that contains other subdevice related information. The data type to store pad information in is unsigned int and not int, change this. While we are at it drop the _idx suffix from the names, this never made sense. Signed-off-by: Niklas Söderlund Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rcar-vin/rcar-v4l2.c | 20 ++++++++++---------- drivers/media/platform/rcar-vin/rcar-vin.h | 9 +++++---- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/drivers/media/platform/rcar-vin/rcar-v4l2.c b/drivers/media/platform/rcar-vin/rcar-v4l2.c index 7be52c2036bb..1a75191539b0 100644 --- a/drivers/media/platform/rcar-vin/rcar-v4l2.c +++ b/drivers/media/platform/rcar-vin/rcar-v4l2.c @@ -111,7 +111,7 @@ static int rvin_reset_format(struct rvin_dev *vin) struct v4l2_mbus_framefmt *mf = &fmt.format; int ret; - fmt.pad = vin->src_pad_idx; + fmt.pad = vin->digital.source_pad; ret = v4l2_subdev_call(vin_to_source(vin), pad, get_fmt, NULL, &fmt); if (ret) @@ -178,7 +178,7 @@ static int __rvin_try_format_source(struct rvin_dev *vin, if (pad_cfg == NULL) return -ENOMEM; - format.pad = vin->src_pad_idx; + format.pad = vin->digital.source_pad; field = pix->field; @@ -559,7 +559,7 @@ static int rvin_enum_dv_timings(struct file *file, void *priv_fh, if (timings->pad) return -EINVAL; - timings->pad = vin->sink_pad_idx; + timings->pad = vin->digital.sink_pad; ret = v4l2_subdev_call(sd, pad, enum_dv_timings, timings); @@ -611,7 +611,7 @@ static int rvin_dv_timings_cap(struct file *file, void *priv_fh, if (cap->pad) return -EINVAL; - cap->pad = vin->sink_pad_idx; + cap->pad = vin->digital.sink_pad; ret = v4l2_subdev_call(sd, pad, dv_timings_cap, cap); @@ -629,7 +629,7 @@ static int rvin_g_edid(struct file *file, void *fh, struct v4l2_edid *edid) if (edid->pad) return -EINVAL; - edid->pad = vin->sink_pad_idx; + edid->pad = vin->digital.sink_pad; ret = v4l2_subdev_call(sd, pad, get_edid, edid); @@ -647,7 +647,7 @@ static int rvin_s_edid(struct file *file, void *fh, struct v4l2_edid *edid) if (edid->pad) return -EINVAL; - edid->pad = vin->sink_pad_idx; + edid->pad = vin->digital.sink_pad; ret = v4l2_subdev_call(sd, pad, set_edid, edid); @@ -920,19 +920,19 @@ int rvin_v4l2_probe(struct rvin_dev *vin) vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | V4L2_CAP_READWRITE; - vin->src_pad_idx = 0; + vin->digital.source_pad = 0; for (pad_idx = 0; pad_idx < sd->entity.num_pads; pad_idx++) if (sd->entity.pads[pad_idx].flags == MEDIA_PAD_FL_SOURCE) break; if (pad_idx >= sd->entity.num_pads) return -EINVAL; - vin->src_pad_idx = pad_idx; + vin->digital.source_pad = pad_idx; - vin->sink_pad_idx = 0; + vin->digital.sink_pad = 0; for (pad_idx = 0; pad_idx < sd->entity.num_pads; pad_idx++) if (sd->entity.pads[pad_idx].flags == MEDIA_PAD_FL_SINK) { - vin->sink_pad_idx = pad_idx; + vin->digital.sink_pad = pad_idx; break; } diff --git a/drivers/media/platform/rcar-vin/rcar-vin.h b/drivers/media/platform/rcar-vin/rcar-vin.h index 727e215c0871..9bfb5a7c4dc4 100644 --- a/drivers/media/platform/rcar-vin/rcar-vin.h +++ b/drivers/media/platform/rcar-vin/rcar-vin.h @@ -74,6 +74,8 @@ struct rvin_video_format { * @subdev: subdevice matched using async framework * @code: Media bus format from source * @mbus_cfg: Media bus format from DT + * @source_pad: source pad of remote subdevice + * @sink_pad: sink pad of remote subdevice */ struct rvin_graph_entity { struct v4l2_async_subdev asd; @@ -81,6 +83,9 @@ struct rvin_graph_entity { u32 code; struct v4l2_mbus_config mbus_cfg; + + unsigned int source_pad; + unsigned int sink_pad; }; /** @@ -91,8 +96,6 @@ struct rvin_graph_entity { * * @vdev: V4L2 video device associated with VIN * @v4l2_dev: V4L2 device - * @src_pad_idx: source pad index for media controller drivers - * @sink_pad_idx: sink pad index for media controller drivers * @ctrl_handler: V4L2 control handler * @notifier: V4L2 asynchronous subdevs notifier * @digital: entity in the DT for local digital subdevice @@ -121,8 +124,6 @@ struct rvin_dev { struct video_device vdev; struct v4l2_device v4l2_dev; - int src_pad_idx; - int sink_pad_idx; struct v4l2_ctrl_handler ctrl_handler; struct v4l2_async_notifier notifier; struct rvin_graph_entity digital; -- cgit v1.2.3-58-ga151 From 5de75b1decb6085f5f91462735ffbe0dd952dc9f Mon Sep 17 00:00:00 2001 From: Niklas Söderlund Date: Tue, 23 May 2017 21:15:29 -0300 Subject: [media] rcar-vin: refactor pad lookup code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The pad lookup code can be broken out to increase readability and to reduce code duplication. Signed-off-by: Niklas Söderlund Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rcar-vin/rcar-v4l2.c | 36 +++++++++++++++++------------ 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/drivers/media/platform/rcar-vin/rcar-v4l2.c b/drivers/media/platform/rcar-vin/rcar-v4l2.c index 1a75191539b0..90ea582fb48e 100644 --- a/drivers/media/platform/rcar-vin/rcar-v4l2.c +++ b/drivers/media/platform/rcar-vin/rcar-v4l2.c @@ -870,11 +870,25 @@ static void rvin_notify(struct v4l2_subdev *sd, } } +static int rvin_find_pad(struct v4l2_subdev *sd, int direction) +{ + unsigned int pad; + + if (sd->entity.num_pads <= 1) + return 0; + + for (pad = 0; pad < sd->entity.num_pads; pad++) + if (sd->entity.pads[pad].flags & direction) + return pad; + + return -EINVAL; +} + int rvin_v4l2_probe(struct rvin_dev *vin) { struct video_device *vdev = &vin->vdev; struct v4l2_subdev *sd = vin_to_source(vin); - int pad_idx, ret; + int ret; v4l2_set_subdev_hostdata(sd, vin); @@ -920,21 +934,13 @@ int rvin_v4l2_probe(struct rvin_dev *vin) vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | V4L2_CAP_READWRITE; - vin->digital.source_pad = 0; - for (pad_idx = 0; pad_idx < sd->entity.num_pads; pad_idx++) - if (sd->entity.pads[pad_idx].flags == MEDIA_PAD_FL_SOURCE) - break; - if (pad_idx >= sd->entity.num_pads) - return -EINVAL; - - vin->digital.source_pad = pad_idx; + ret = rvin_find_pad(sd, MEDIA_PAD_FL_SOURCE); + if (ret < 0) + return ret; + vin->digital.source_pad = ret; - vin->digital.sink_pad = 0; - for (pad_idx = 0; pad_idx < sd->entity.num_pads; pad_idx++) - if (sd->entity.pads[pad_idx].flags == MEDIA_PAD_FL_SINK) { - vin->digital.sink_pad = pad_idx; - break; - } + ret = rvin_find_pad(sd, MEDIA_PAD_FL_SINK); + vin->digital.sink_pad = ret < 0 ? 0 : ret; vin->format.pixelformat = RVIN_DEFAULT_FORMAT; rvin_reset_format(vin); -- cgit v1.2.3-58-ga151 From f4dbfbc75b6416a07f8106ee4931bfbc9c11f451 Mon Sep 17 00:00:00 2001 From: Niklas Söderlund Date: Tue, 23 May 2017 21:15:30 -0300 Subject: [media] rcar-vin: move pad lookup to async bound handler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Information about pads will be needed when enumerating the media bus codes in the async complete handler which is run before rvin_v4l2_probe(). Move the pad lookup to the async bound handler so they are available when needed. Signed-off-by: Niklas Söderlund [hans.verkuil@cisco.com: fix typo: surce -> source] Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rcar-vin/rcar-core.c | 30 ++++++++++++++++++++++++++++- drivers/media/platform/rcar-vin/rcar-v4l2.c | 22 --------------------- 2 files changed, 29 insertions(+), 23 deletions(-) diff --git a/drivers/media/platform/rcar-vin/rcar-core.c b/drivers/media/platform/rcar-vin/rcar-core.c index 264604a9bcf8..24ce6967f711 100644 --- a/drivers/media/platform/rcar-vin/rcar-core.c +++ b/drivers/media/platform/rcar-vin/rcar-core.c @@ -31,6 +31,20 @@ #define notifier_to_vin(n) container_of(n, struct rvin_dev, notifier) +static int rvin_find_pad(struct v4l2_subdev *sd, int direction) +{ + unsigned int pad; + + if (sd->entity.num_pads <= 1) + return 0; + + for (pad = 0; pad < sd->entity.num_pads; pad++) + if (sd->entity.pads[pad].flags & direction) + return pad; + + return -EINVAL; +} + static bool rvin_mbus_supported(struct rvin_graph_entity *entity) { struct v4l2_subdev *sd = entity->subdev; @@ -101,13 +115,27 @@ static int rvin_digital_notify_bound(struct v4l2_async_notifier *notifier, struct v4l2_async_subdev *asd) { struct rvin_dev *vin = notifier_to_vin(notifier); + int ret; v4l2_set_subdev_hostdata(subdev, vin); if (vin->digital.asd.match.fwnode.fwnode == of_fwnode_handle(subdev->dev->of_node)) { - vin_dbg(vin, "bound digital subdev %s\n", subdev->name); + /* Find source and sink pad of remote subdevice */ + + ret = rvin_find_pad(subdev, MEDIA_PAD_FL_SOURCE); + if (ret < 0) + return ret; + vin->digital.source_pad = ret; + + ret = rvin_find_pad(subdev, MEDIA_PAD_FL_SINK); + vin->digital.sink_pad = ret < 0 ? 0 : ret; + vin->digital.subdev = subdev; + + vin_dbg(vin, "bound subdev %s source pad: %u sink pad: %u\n", + subdev->name, vin->digital.source_pad, + vin->digital.sink_pad); return 0; } diff --git a/drivers/media/platform/rcar-vin/rcar-v4l2.c b/drivers/media/platform/rcar-vin/rcar-v4l2.c index 90ea582fb48e..be6f41bf82ac 100644 --- a/drivers/media/platform/rcar-vin/rcar-v4l2.c +++ b/drivers/media/platform/rcar-vin/rcar-v4l2.c @@ -870,20 +870,6 @@ static void rvin_notify(struct v4l2_subdev *sd, } } -static int rvin_find_pad(struct v4l2_subdev *sd, int direction) -{ - unsigned int pad; - - if (sd->entity.num_pads <= 1) - return 0; - - for (pad = 0; pad < sd->entity.num_pads; pad++) - if (sd->entity.pads[pad].flags & direction) - return pad; - - return -EINVAL; -} - int rvin_v4l2_probe(struct rvin_dev *vin) { struct video_device *vdev = &vin->vdev; @@ -934,14 +920,6 @@ int rvin_v4l2_probe(struct rvin_dev *vin) vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | V4L2_CAP_READWRITE; - ret = rvin_find_pad(sd, MEDIA_PAD_FL_SOURCE); - if (ret < 0) - return ret; - vin->digital.source_pad = ret; - - ret = rvin_find_pad(sd, MEDIA_PAD_FL_SINK); - vin->digital.sink_pad = ret < 0 ? 0 : ret; - vin->format.pixelformat = RVIN_DEFAULT_FORMAT; rvin_reset_format(vin); -- cgit v1.2.3-58-ga151 From 557d61672f3f5a0f66954602379008e549824ead Mon Sep 17 00:00:00 2001 From: Niklas Söderlund Date: Tue, 23 May 2017 21:15:31 -0300 Subject: [media] rcar-vin: use pad information when verifying media bus format MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use information about pad index when enumerating mbus codes. Signed-off-by: Niklas Söderlund Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rcar-vin/rcar-core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/platform/rcar-vin/rcar-core.c b/drivers/media/platform/rcar-vin/rcar-core.c index 24ce6967f711..94c5c8d905d9 100644 --- a/drivers/media/platform/rcar-vin/rcar-core.c +++ b/drivers/media/platform/rcar-vin/rcar-core.c @@ -53,6 +53,7 @@ static bool rvin_mbus_supported(struct rvin_graph_entity *entity) }; code.index = 0; + code.pad = entity->source_pad; while (!v4l2_subdev_call(sd, pad, enum_mbus_code, NULL, &code)) { code.index++; switch (code.code) { -- cgit v1.2.3-58-ga151 From 45a6e6043693c52e98103d423a3c0875cac397be Mon Sep 17 00:00:00 2001 From: Niklas Söderlund Date: Tue, 23 May 2017 21:15:32 -0300 Subject: [media] rcar-vin: decrease buffers needed to capture MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It's possible to grab frames using only one buffer, this should never have been set to anything else then 1. Signed-off-by: Niklas Söderlund Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rcar-vin/rcar-dma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/rcar-vin/rcar-dma.c b/drivers/media/platform/rcar-vin/rcar-dma.c index 9ccd5ff55e19..c37f7a2993fb 100644 --- a/drivers/media/platform/rcar-vin/rcar-dma.c +++ b/drivers/media/platform/rcar-vin/rcar-dma.c @@ -1183,7 +1183,7 @@ int rvin_dma_probe(struct rvin_dev *vin, int irq) q->ops = &rvin_qops; q->mem_ops = &vb2_dma_contig_memops; q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - q->min_buffers_needed = 2; + q->min_buffers_needed = 1; q->dev = vin->dev; ret = vb2_queue_init(q); -- cgit v1.2.3-58-ga151 From 9e9214478a90bd9ed6db8dd4612c026e74e328ed Mon Sep 17 00:00:00 2001 From: Niklas Söderlund Date: Tue, 23 May 2017 21:15:33 -0300 Subject: [media] rcar-vin: move functions which acts on hardware MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This only moves whole structs, defines and functions around, no code is changed inside any function. The reason for moving this code around is to prepare for refactoring and fixing of a start/stop stream bug without having to use forward declarations. Signed-off-by: Niklas Söderlund Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rcar-vin/rcar-dma.c | 182 ++++++++++++++--------------- 1 file changed, 91 insertions(+), 91 deletions(-) diff --git a/drivers/media/platform/rcar-vin/rcar-dma.c b/drivers/media/platform/rcar-vin/rcar-dma.c index c37f7a2993fb..11e979956429 100644 --- a/drivers/media/platform/rcar-vin/rcar-dma.c +++ b/drivers/media/platform/rcar-vin/rcar-dma.c @@ -119,6 +119,15 @@ #define VNDMR2_FTEV (1 << 17) #define VNDMR2_VLV(n) ((n & 0xf) << 12) +struct rvin_buffer { + struct vb2_v4l2_buffer vb; + struct list_head list; +}; + +#define to_buf_list(vb2_buffer) (&container_of(vb2_buffer, \ + struct rvin_buffer, \ + vb)->list) + static void rvin_write(struct rvin_dev *vin, u32 value, u32 offset) { iowrite32(value, vin->base + offset); @@ -269,48 +278,6 @@ static int rvin_setup(struct rvin_dev *vin) return 0; } -static void rvin_capture_on(struct rvin_dev *vin) -{ - vin_dbg(vin, "Capture on in %s mode\n", - vin->continuous ? "continuous" : "single"); - - if (vin->continuous) - /* Continuous Frame Capture Mode */ - rvin_write(vin, VNFC_C_FRAME, VNFC_REG); - else - /* Single Frame Capture Mode */ - rvin_write(vin, VNFC_S_FRAME, VNFC_REG); -} - -static void rvin_capture_off(struct rvin_dev *vin) -{ - /* Set continuous & single transfer off */ - rvin_write(vin, 0, VNFC_REG); -} - -static int rvin_capture_start(struct rvin_dev *vin) -{ - int ret; - - rvin_crop_scale_comp(vin); - - ret = rvin_setup(vin); - if (ret) - return ret; - - rvin_capture_on(vin); - - return 0; -} - -static void rvin_capture_stop(struct rvin_dev *vin) -{ - rvin_capture_off(vin); - - /* Disable module */ - rvin_write(vin, rvin_read(vin, VNMC_REG) & ~VNMC_ME, VNMC_REG); -} - static void rvin_disable_interrupts(struct rvin_dev *vin) { rvin_write(vin, 0, VNIE_REG); @@ -377,6 +344,88 @@ static void rvin_set_slot_addr(struct rvin_dev *vin, int slot, dma_addr_t addr) rvin_write(vin, offset, VNMB_REG(slot)); } +/* Moves a buffer from the queue to the HW slots */ +static bool rvin_fill_hw_slot(struct rvin_dev *vin, int slot) +{ + struct rvin_buffer *buf; + struct vb2_v4l2_buffer *vbuf; + dma_addr_t phys_addr_top; + + if (vin->queue_buf[slot] != NULL) + return true; + + if (list_empty(&vin->buf_list)) + return false; + + vin_dbg(vin, "Filling HW slot: %d\n", slot); + + /* Keep track of buffer we give to HW */ + buf = list_entry(vin->buf_list.next, struct rvin_buffer, list); + vbuf = &buf->vb; + list_del_init(to_buf_list(vbuf)); + vin->queue_buf[slot] = vbuf; + + /* Setup DMA */ + phys_addr_top = vb2_dma_contig_plane_dma_addr(&vbuf->vb2_buf, 0); + rvin_set_slot_addr(vin, slot, phys_addr_top); + + return true; +} + +static bool rvin_fill_hw(struct rvin_dev *vin) +{ + int slot, limit; + + limit = vin->continuous ? HW_BUFFER_NUM : 1; + + for (slot = 0; slot < limit; slot++) + if (!rvin_fill_hw_slot(vin, slot)) + return false; + return true; +} + +static void rvin_capture_on(struct rvin_dev *vin) +{ + vin_dbg(vin, "Capture on in %s mode\n", + vin->continuous ? "continuous" : "single"); + + if (vin->continuous) + /* Continuous Frame Capture Mode */ + rvin_write(vin, VNFC_C_FRAME, VNFC_REG); + else + /* Single Frame Capture Mode */ + rvin_write(vin, VNFC_S_FRAME, VNFC_REG); +} + +static void rvin_capture_off(struct rvin_dev *vin) +{ + /* Set continuous & single transfer off */ + rvin_write(vin, 0, VNFC_REG); +} + +static int rvin_capture_start(struct rvin_dev *vin) +{ + int ret; + + rvin_crop_scale_comp(vin); + + ret = rvin_setup(vin); + if (ret) + return ret; + + rvin_capture_on(vin); + + return 0; +} + +static void rvin_capture_stop(struct rvin_dev *vin) +{ + rvin_capture_off(vin); + + /* Disable module */ + rvin_write(vin, rvin_read(vin, VNMC_REG) & ~VNMC_ME, VNMC_REG); +} + /* ----------------------------------------------------------------------------- * Crop and Scaling Gen2 */ @@ -839,55 +888,6 @@ void rvin_scale_try(struct rvin_dev *vin, struct v4l2_pix_format *pix, #define RVIN_TIMEOUT_MS 100 #define RVIN_RETRIES 10 -struct rvin_buffer { - struct vb2_v4l2_buffer vb; - struct list_head list; -}; - -#define to_buf_list(vb2_buffer) (&container_of(vb2_buffer, \ - struct rvin_buffer, \ - vb)->list) - -/* Moves a buffer from the queue to the HW slots */ -static bool rvin_fill_hw_slot(struct rvin_dev *vin, int slot) -{ - struct rvin_buffer *buf; - struct vb2_v4l2_buffer *vbuf; - dma_addr_t phys_addr_top; - - if (vin->queue_buf[slot] != NULL) - return true; - - if (list_empty(&vin->buf_list)) - return false; - - vin_dbg(vin, "Filling HW slot: %d\n", slot); - - /* Keep track of buffer we give to HW */ - buf = list_entry(vin->buf_list.next, struct rvin_buffer, list); - vbuf = &buf->vb; - list_del_init(to_buf_list(vbuf)); - vin->queue_buf[slot] = vbuf; - - /* Setup DMA */ - phys_addr_top = vb2_dma_contig_plane_dma_addr(&vbuf->vb2_buf, 0); - rvin_set_slot_addr(vin, slot, phys_addr_top); - - return true; -} - -static bool rvin_fill_hw(struct rvin_dev *vin) -{ - int slot, limit; - - limit = vin->continuous ? HW_BUFFER_NUM : 1; - - for (slot = 0; slot < limit; slot++) - if (!rvin_fill_hw_slot(vin, slot)) - return false; - return true; -} - static irqreturn_t rvin_irq(int irq, void *data) { struct rvin_dev *vin = data; -- cgit v1.2.3-58-ga151 From 996ce3afbe33617c9c21b4a50a92a266d70c0b8b Mon Sep 17 00:00:00 2001 From: Niklas Söderlund Date: Tue, 23 May 2017 21:15:34 -0300 Subject: [media] rcar-vin: select capture mode based on free buffers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of selecting single or continuous capture mode based on how many buffers userspace intends to give us select capture mode based on number of free buffers we can allocate to hardware when the stream is started. This change is a prerequisite to enable the driver to switch from continuous to single capture mode (or the other way around) when the driver is stalled by userspace not feeding it buffers as fast as it consumes it. Signed-off-by: Niklas Söderlund Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rcar-vin/rcar-dma.c | 31 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/drivers/media/platform/rcar-vin/rcar-dma.c b/drivers/media/platform/rcar-vin/rcar-dma.c index 11e979956429..47d78f68d715 100644 --- a/drivers/media/platform/rcar-vin/rcar-dma.c +++ b/drivers/media/platform/rcar-vin/rcar-dma.c @@ -405,7 +405,21 @@ static void rvin_capture_off(struct rvin_dev *vin) static int rvin_capture_start(struct rvin_dev *vin) { - int ret; + struct rvin_buffer *buf, *node; + int bufs, ret; + + /* Count number of free buffers */ + bufs = 0; + list_for_each_entry_safe(buf, node, &vin->buf_list, list) + bufs++; + + /* Continuous capture requires more buffers then there are HW slots */ + vin->continuous = bufs > HW_BUFFER_NUM; + + if (!rvin_fill_hw(vin)) { + vin_err(vin, "HW not ready to start, not enough buffers available\n"); + return -EINVAL; + } rvin_crop_scale_comp(vin); @@ -1062,22 +1076,7 @@ static int rvin_start_streaming(struct vb2_queue *vq, unsigned int count) vin->state = RUNNING; vin->sequence = 0; - /* Continuous capture requires more buffers then there are HW slots */ - vin->continuous = count > HW_BUFFER_NUM; - - /* - * This should never happen but if we don't have enough - * buffers for HW bail out - */ - if (!rvin_fill_hw(vin)) { - vin_err(vin, "HW not ready to start, not enough buffers available\n"); - ret = -EINVAL; - goto out; - } - ret = rvin_capture_start(vin); -out: - /* Return all buffers if something went wrong */ if (ret) { return_all_buffers(vin, VB2_BUF_STATE_QUEUED); v4l2_subdev_call(sd, video, s_stream, 0); -- cgit v1.2.3-58-ga151 From 82ec4ca9b5c3b1410e2f068d3fbdd41a51c35baf Mon Sep 17 00:00:00 2001 From: Niklas Söderlund Date: Tue, 23 May 2017 21:15:35 -0300 Subject: [media] rcar-vin: allow switch between capturing modes when stalling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If userspace can't feed the driver with buffers as fast as the driver consumes them the driver will stop video capturing and wait for more buffers from userspace, the driver is stalled. Once it have been feed one or more free buffers it will recover from the stall and resume capturing. Instead of continue to capture using the same capture mode as before the stall allow the driver to choose between single and continuous mode based on free buffer availability. Do this by stopping capturing when the driver becomes stalled and restart capturing once it continues. By doing this the capture mode will be evaluated each time the driver is recovering from a stall. This behavior is needed to fix a bug where continuous capturing mode is used, userspace is about to stop the stream and is waiting for the last buffers to be returned from the driver and is not queuing any new buffers. In this case the driver becomes stalled when there are only 3 buffers remaining streaming will never resume since the driver is waiting for userspace to feed it more buffers before it can continue streaming. With this fix the driver will then switch to single capture mode for the last 3 buffers and a deadlock is avoided. The issue can be demonstrated using yavta. $ yavta -f RGB565 -s 640x480 -n 4 --capture=10 /dev/video22 Device /dev/video22 opened. Device `R_Car_VIN' on `platform:e6ef1000.video' (driver 'rcar_vin') supports video, capture, without mplanes. Video format set: RGB565 (50424752) 640x480 (stride 1280) field interlaced buffer size 614400 Video format: RGB565 (50424752) 640x480 (stride 1280) field interlaced buffer size 614400 4 buffers requested. length: 614400 offset: 0 timestamp type/source: mono/EoF Buffer 0/0 mapped at address 0xb6cc7000. length: 614400 offset: 614400 timestamp type/source: mono/EoF Buffer 1/0 mapped at address 0xb6c31000. length: 614400 offset: 1228800 timestamp type/source: mono/EoF Buffer 2/0 mapped at address 0xb6b9b000. length: 614400 offset: 1843200 timestamp type/source: mono/EoF Buffer 3/0 mapped at address 0xb6b05000. 0 (0) [-] interlaced 0 614400 B 38.240285 38.240303 12.421 fps ts mono/EoF 1 (1) [-] interlaced 1 614400 B 38.282329 38.282346 23.785 fps ts mono/EoF 2 (2) [-] interlaced 2 614400 B 38.322324 38.322338 25.003 fps ts mono/EoF 3 (3) [-] interlaced 3 614400 B 38.362318 38.362333 25.004 fps ts mono/EoF 4 (0) [-] interlaced 4 614400 B 38.402313 38.402328 25.003 fps ts mono/EoF 5 (1) [-] interlaced 5 614400 B 38.442307 38.442321 25.004 fps ts mono/EoF 6 (2) [-] interlaced 6 614400 B 38.482301 38.482316 25.004 fps ts mono/EoF 7 (3) [-] interlaced 7 614400 B 38.522295 38.522312 25.004 fps ts mono/EoF 8 (0) [-] interlaced 8 614400 B 38.562290 38.562306 25.003 fps ts mono/EoF This fix also allow the driver to switch to single capture mode if userspace doesn't feed it buffers fast enough. Or the other way around, if userspace suddenly feeds the driver buffers faster it can switch to continues capturing mode. Signed-off-by: Niklas Söderlund Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rcar-vin/rcar-dma.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/drivers/media/platform/rcar-vin/rcar-dma.c b/drivers/media/platform/rcar-vin/rcar-dma.c index 47d78f68d715..ae4febede5f7 100644 --- a/drivers/media/platform/rcar-vin/rcar-dma.c +++ b/drivers/media/platform/rcar-vin/rcar-dma.c @@ -429,6 +429,8 @@ static int rvin_capture_start(struct rvin_dev *vin) rvin_capture_on(vin); + vin->state = RUNNING; + return 0; } @@ -907,7 +909,7 @@ static irqreturn_t rvin_irq(int irq, void *data) struct rvin_dev *vin = data; u32 int_status, vnms; int slot; - unsigned int sequence, handled = 0; + unsigned int i, sequence, handled = 0; unsigned long flags; spin_lock_irqsave(&vin->qlock, flags); @@ -969,8 +971,20 @@ static irqreturn_t rvin_irq(int irq, void *data) * the VnMBm registers. */ if (vin->continuous) { - rvin_capture_off(vin); + rvin_capture_stop(vin); vin_dbg(vin, "IRQ %02d: hw not ready stop\n", sequence); + + /* Maybe we can continue in single capture mode */ + for (i = 0; i < HW_BUFFER_NUM; i++) { + if (vin->queue_buf[i]) { + list_add(to_buf_list(vin->queue_buf[i]), + &vin->buf_list); + vin->queue_buf[i] = NULL; + } + } + + if (!list_empty(&vin->buf_list)) + rvin_capture_start(vin); } } else { /* @@ -1055,8 +1069,7 @@ static void rvin_buffer_queue(struct vb2_buffer *vb) * capturing if HW is ready to continue. */ if (vin->state == STALLED) - if (rvin_fill_hw(vin)) - rvin_capture_on(vin); + rvin_capture_start(vin); spin_unlock_irqrestore(&vin->qlock, flags); } @@ -1073,7 +1086,6 @@ static int rvin_start_streaming(struct vb2_queue *vq, unsigned int count) spin_lock_irqsave(&vin->qlock, flags); - vin->state = RUNNING; vin->sequence = 0; ret = rvin_capture_start(vin); -- cgit v1.2.3-58-ga151 From bd8448f732f15b4b160fa8fe200305240c704deb Mon Sep 17 00:00:00 2001 From: Niklas Söderlund Date: Tue, 23 May 2017 21:15:36 -0300 Subject: [media] rcar-vin: refactor and fold in function after stall handling rework MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With the driver stopping and starting the stream each time the driver is stalled rvin_capture_off() can be folded in to the only caller. Signed-off-by: Niklas Söderlund Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rcar-vin/rcar-dma.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/drivers/media/platform/rcar-vin/rcar-dma.c b/drivers/media/platform/rcar-vin/rcar-dma.c index ae4febede5f7..b136844499f6 100644 --- a/drivers/media/platform/rcar-vin/rcar-dma.c +++ b/drivers/media/platform/rcar-vin/rcar-dma.c @@ -397,12 +397,6 @@ static void rvin_capture_on(struct rvin_dev *vin) rvin_write(vin, VNFC_S_FRAME, VNFC_REG); } -static void rvin_capture_off(struct rvin_dev *vin) -{ - /* Set continuous & single transfer off */ - rvin_write(vin, 0, VNFC_REG); -} - static int rvin_capture_start(struct rvin_dev *vin) { struct rvin_buffer *buf, *node; @@ -436,7 +430,8 @@ static int rvin_capture_start(struct rvin_dev *vin) static void rvin_capture_stop(struct rvin_dev *vin) { - rvin_capture_off(vin); + /* Set continuous & single transfer off */ + rvin_write(vin, 0, VNFC_REG); /* Disable module */ rvin_write(vin, rvin_read(vin, VNMC_REG) & ~VNMC_ME, VNMC_REG); -- cgit v1.2.3-58-ga151 From f8a668f72e79a2ee7c1aef7d36d3b59f9d4cf9a5 Mon Sep 17 00:00:00 2001 From: Niklas Söderlund Date: Tue, 23 May 2017 21:15:37 -0300 Subject: [media] rcar-vin: remove subdevice matching from bind and unbind callbacks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is only one subdevice registered with the async framework so there is no need for the driver to check which subdevice is bound or unbound. Remove these checks since the async framework preforms this. Signed-off-by: Niklas Söderlund [hans.verkuil@cisco.com: fix typo: surce -> source] Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rcar-vin/rcar-core.c | 40 +++++++++++------------------ 1 file changed, 15 insertions(+), 25 deletions(-) diff --git a/drivers/media/platform/rcar-vin/rcar-core.c b/drivers/media/platform/rcar-vin/rcar-core.c index 94c5c8d905d9..77dff047c41c 100644 --- a/drivers/media/platform/rcar-vin/rcar-core.c +++ b/drivers/media/platform/rcar-vin/rcar-core.c @@ -101,14 +101,9 @@ static void rvin_digital_notify_unbind(struct v4l2_async_notifier *notifier, { struct rvin_dev *vin = notifier_to_vin(notifier); - if (vin->digital.subdev == subdev) { - vin_dbg(vin, "unbind digital subdev %s\n", subdev->name); - rvin_v4l2_remove(vin); - vin->digital.subdev = NULL; - return; - } - - vin_err(vin, "no entity for subdev %s to unbind\n", subdev->name); + vin_dbg(vin, "unbind digital subdev %s\n", subdev->name); + rvin_v4l2_remove(vin); + vin->digital.subdev = NULL; } static int rvin_digital_notify_bound(struct v4l2_async_notifier *notifier, @@ -120,28 +115,23 @@ static int rvin_digital_notify_bound(struct v4l2_async_notifier *notifier, v4l2_set_subdev_hostdata(subdev, vin); - if (vin->digital.asd.match.fwnode.fwnode == - of_fwnode_handle(subdev->dev->of_node)) { - /* Find source and sink pad of remote subdevice */ + /* Find source and sink pad of remote subdevice */ - ret = rvin_find_pad(subdev, MEDIA_PAD_FL_SOURCE); - if (ret < 0) - return ret; - vin->digital.source_pad = ret; + ret = rvin_find_pad(subdev, MEDIA_PAD_FL_SOURCE); + if (ret < 0) + return ret; + vin->digital.source_pad = ret; - ret = rvin_find_pad(subdev, MEDIA_PAD_FL_SINK); - vin->digital.sink_pad = ret < 0 ? 0 : ret; + ret = rvin_find_pad(subdev, MEDIA_PAD_FL_SINK); + vin->digital.sink_pad = ret < 0 ? 0 : ret; - vin->digital.subdev = subdev; + vin->digital.subdev = subdev; - vin_dbg(vin, "bound subdev %s source pad: %u sink pad: %u\n", - subdev->name, vin->digital.source_pad, - vin->digital.sink_pad); - return 0; - } + vin_dbg(vin, "bound subdev %s source pad: %u sink pad: %u\n", + subdev->name, vin->digital.source_pad, + vin->digital.sink_pad); - vin_err(vin, "no entity for subdev %s to bind\n", subdev->name); - return -EINVAL; + return 0; } static int rvin_digitial_parse_v4l2(struct rvin_dev *vin, -- cgit v1.2.3-58-ga151 From 8afb72eda5f3f6f032a3b95b9ea59db2e1e2fcfc Mon Sep 17 00:00:00 2001 From: Niklas Söderlund Date: Tue, 23 May 2017 21:15:39 -0300 Subject: [media] rcar-vin: add missing error check to propagate error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The return value of __rvin_try_format_source is not checked, add a check and propagate the error. Signed-off-by: Niklas Söderlund Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rcar-vin/rcar-v4l2.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/rcar-vin/rcar-v4l2.c b/drivers/media/platform/rcar-vin/rcar-v4l2.c index be6f41bf82ac..846bcd87a24e 100644 --- a/drivers/media/platform/rcar-vin/rcar-v4l2.c +++ b/drivers/media/platform/rcar-vin/rcar-v4l2.c @@ -208,6 +208,7 @@ static int __rvin_try_format(struct rvin_dev *vin, { const struct rvin_video_format *info; u32 rwidth, rheight, walign; + int ret; /* Requested */ rwidth = pix->width; @@ -235,7 +236,9 @@ static int __rvin_try_format(struct rvin_dev *vin, pix->sizeimage = 0; /* Limit to source capabilities */ - __rvin_try_format_source(vin, which, pix, source); + ret = __rvin_try_format_source(vin, which, pix, source); + if (ret) + return ret; switch (pix->field) { case V4L2_FIELD_TOP: -- cgit v1.2.3-58-ga151 From e09c3481d91652de135295ac3bd6505fb2b32613 Mon Sep 17 00:00:00 2001 From: Niklas Söderlund Date: Tue, 23 May 2017 21:15:40 -0300 Subject: [media] rcar-vin: fix bug in pixelformat selection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the requested pixelformat is not supported fallback to the default format, do not revert the entire format. Signed-off-by: Niklas Söderlund Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rcar-vin/rcar-v4l2.c | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/drivers/media/platform/rcar-vin/rcar-v4l2.c b/drivers/media/platform/rcar-vin/rcar-v4l2.c index 846bcd87a24e..dd37ea811680 100644 --- a/drivers/media/platform/rcar-vin/rcar-v4l2.c +++ b/drivers/media/platform/rcar-vin/rcar-v4l2.c @@ -206,7 +206,6 @@ static int __rvin_try_format(struct rvin_dev *vin, struct v4l2_pix_format *pix, struct rvin_source_fmt *source) { - const struct rvin_video_format *info; u32 rwidth, rheight, walign; int ret; @@ -218,17 +217,11 @@ static int __rvin_try_format(struct rvin_dev *vin, if (pix->field == V4L2_FIELD_ANY) pix->field = vin->format.field; - /* - * Retrieve format information and select the current format if the - * requested format isn't supported. - */ - info = rvin_format_from_pixel(pix->pixelformat); - if (!info) { - vin_dbg(vin, "Format %x not found, keeping %x\n", - pix->pixelformat, vin->format.pixelformat); - *pix = vin->format; - pix->width = rwidth; - pix->height = rheight; + /* If requested format is not supported fallback to the default */ + if (!rvin_format_from_pixel(pix->pixelformat)) { + vin_dbg(vin, "Format 0x%x not found, using default 0x%x\n", + pix->pixelformat, RVIN_DEFAULT_FORMAT); + pix->pixelformat = RVIN_DEFAULT_FORMAT; } /* Always recalculate */ -- cgit v1.2.3-58-ga151 From da73ad2d53b5fe77cd70b958bb6c54d641473e06 Mon Sep 17 00:00:00 2001 From: Ulrich Hecht Date: Fri, 19 May 2017 10:07:03 -0300 Subject: [media] media: adv7180: Add adv7180cp, adv7180st bindings To differentiate between two classes of chip packages that have different numbers of input ports. Signed-off-by: Ulrich Hecht Acked-by: Rob Herring Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/devicetree/bindings/media/i2c/adv7180.txt | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/Documentation/devicetree/bindings/media/i2c/adv7180.txt b/Documentation/devicetree/bindings/media/i2c/adv7180.txt index 4da486f96ff6..552b6a82cb1f 100644 --- a/Documentation/devicetree/bindings/media/i2c/adv7180.txt +++ b/Documentation/devicetree/bindings/media/i2c/adv7180.txt @@ -6,6 +6,8 @@ digital interfaces like MIPI CSI-2 or parallel video. Required Properties : - compatible : value must be one of "adi,adv7180" + "adi,adv7180cp" + "adi,adv7180st" "adi,adv7182" "adi,adv7280" "adi,adv7280-m" @@ -15,6 +17,19 @@ Required Properties : "adi,adv7282" "adi,adv7282-m" +Device nodes of "adi,adv7180cp" and "adi,adv7180st" must contain one +'port' child node per device input and output port, in accordance with the +video interface bindings defined in +Documentation/devicetree/bindings/media/video-interfaces.txt. The port +nodes are numbered as follows. + + Port adv7180cp adv7180st +------------------------------------------------------------------- + Input 0-2 0-5 + Output 3 6 + +The digital output port node must contain at least one endpoint. + Optional Properties : - powerdown-gpios: reference to the GPIO connected to the powerdown pin, if any. -- cgit v1.2.3-58-ga151 From ce1ec5c07e0671cc6b5961bba9f740bb951d33f7 Mon Sep 17 00:00:00 2001 From: Ulrich Hecht Date: Fri, 19 May 2017 10:07:02 -0300 Subject: [media] media: adv7180: add adv7180cp, adv7180st compatible strings Used to differentiate between models with 3 and 6 inputs. Signed-off-by: Ulrich Hecht Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/adv7180.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/i2c/adv7180.c b/drivers/media/i2c/adv7180.c index bdbbf8cf27e4..78de7ddf5081 100644 --- a/drivers/media/i2c/adv7180.c +++ b/drivers/media/i2c/adv7180.c @@ -1452,6 +1452,8 @@ static SIMPLE_DEV_PM_OPS(adv7180_pm_ops, adv7180_suspend, adv7180_resume); #ifdef CONFIG_OF static const struct of_device_id adv7180_of_id[] = { { .compatible = "adi,adv7180", }, + { .compatible = "adi,adv7180cp", }, + { .compatible = "adi,adv7180st", }, { .compatible = "adi,adv7182", }, { .compatible = "adi,adv7280", }, { .compatible = "adi,adv7280-m", }, -- cgit v1.2.3-58-ga151 From b14ac545688d8cc4b2b707d71d106799ad476964 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Mon, 22 May 2017 13:34:48 -0300 Subject: [media] coda: improve colorimetry handling The hardware codec is not colorspace aware. We should trust userspace to set the correct colorimetry information on the OUTPUT queue and mirror the exact same setting on the CAPTURE queue. There is no reason to restrict colorspace to JPEG or REC709 only. Also, set the default colorspace, as returned by calling VIDIOC_TRY/S_FMT with V4L2_COLORSPACE_DEFAULT, initially. Signed-off-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/coda/coda-common.c | 51 ++++++++++++++++++++++--------- drivers/media/platform/coda/coda.h | 3 ++ 2 files changed, 39 insertions(+), 15 deletions(-) diff --git a/drivers/media/platform/coda/coda-common.c b/drivers/media/platform/coda/coda-common.c index 0e8e50caa8ff..4724dfef0643 100644 --- a/drivers/media/platform/coda/coda-common.c +++ b/drivers/media/platform/coda/coda-common.c @@ -430,10 +430,10 @@ static int coda_g_fmt(struct file *file, void *priv, f->fmt.pix.bytesperline = q_data->bytesperline; f->fmt.pix.sizeimage = q_data->sizeimage; - if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_JPEG) - f->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG; - else - f->fmt.pix.colorspace = ctx->colorspace; + f->fmt.pix.colorspace = ctx->colorspace; + f->fmt.pix.xfer_func = ctx->xfer_func; + f->fmt.pix.ycbcr_enc = ctx->ycbcr_enc; + f->fmt.pix.quantization = ctx->quantization; return 0; } @@ -599,6 +599,9 @@ static int coda_try_fmt_vid_cap(struct file *file, void *priv, } f->fmt.pix.colorspace = ctx->colorspace; + f->fmt.pix.xfer_func = ctx->xfer_func; + f->fmt.pix.ycbcr_enc = ctx->ycbcr_enc; + f->fmt.pix.quantization = ctx->quantization; q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); codec = coda_find_codec(ctx->dev, q_data_src->fourcc, @@ -634,6 +637,23 @@ static int coda_try_fmt_vid_cap(struct file *file, void *priv, return 0; } +static void coda_set_default_colorspace(struct v4l2_pix_format *fmt) +{ + enum v4l2_colorspace colorspace; + + if (fmt->pixelformat == V4L2_PIX_FMT_JPEG) + colorspace = V4L2_COLORSPACE_JPEG; + else if (fmt->width <= 720 && fmt->height <= 576) + colorspace = V4L2_COLORSPACE_SMPTE170M; + else + colorspace = V4L2_COLORSPACE_REC709; + + fmt->colorspace = colorspace; + fmt->xfer_func = V4L2_XFER_FUNC_DEFAULT; + fmt->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + fmt->quantization = V4L2_QUANTIZATION_DEFAULT; +} + static int coda_try_fmt_vid_out(struct file *file, void *priv, struct v4l2_format *f) { @@ -647,16 +667,8 @@ static int coda_try_fmt_vid_out(struct file *file, void *priv, if (ret < 0) return ret; - switch (f->fmt.pix.colorspace) { - case V4L2_COLORSPACE_REC709: - case V4L2_COLORSPACE_JPEG: - break; - default: - if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_JPEG) - f->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG; - else - f->fmt.pix.colorspace = V4L2_COLORSPACE_REC709; - } + if (f->fmt.pix.colorspace == V4L2_COLORSPACE_DEFAULT) + coda_set_default_colorspace(&f->fmt.pix); q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); codec = coda_find_codec(dev, f->fmt.pix.pixelformat, q_data_dst->fourcc); @@ -771,6 +783,9 @@ static int coda_s_fmt_vid_out(struct file *file, void *priv, return ret; ctx->colorspace = f->fmt.pix.colorspace; + ctx->xfer_func = f->fmt.pix.xfer_func; + ctx->ycbcr_enc = f->fmt.pix.ycbcr_enc; + ctx->quantization = f->fmt.pix.quantization; memset(&f_cap, 0, sizeof(f_cap)); f_cap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; @@ -1281,7 +1296,13 @@ static void set_default_params(struct coda_ctx *ctx) csize = coda_estimate_sizeimage(ctx, usize, max_w, max_h); ctx->params.codec_mode = ctx->codec->mode; - ctx->colorspace = V4L2_COLORSPACE_REC709; + if (ctx->cvd->src_formats[0] == V4L2_PIX_FMT_JPEG) + ctx->colorspace = V4L2_COLORSPACE_JPEG; + else + ctx->colorspace = V4L2_COLORSPACE_REC709; + ctx->xfer_func = V4L2_XFER_FUNC_DEFAULT; + ctx->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + ctx->quantization = V4L2_QUANTIZATION_DEFAULT; ctx->params.framerate = 30; /* Default formats for output and input queues */ diff --git a/drivers/media/platform/coda/coda.h b/drivers/media/platform/coda/coda.h index 20222befb9b2..308116d855e6 100644 --- a/drivers/media/platform/coda/coda.h +++ b/drivers/media/platform/coda/coda.h @@ -206,6 +206,9 @@ struct coda_ctx { enum coda_inst_type inst_type; const struct coda_codec *codec; enum v4l2_colorspace colorspace; + enum v4l2_xfer_func xfer_func; + enum v4l2_ycbcr_encoding ycbcr_enc; + enum v4l2_quantization quantization; struct coda_params params; struct v4l2_ctrl_handler ctrls; struct v4l2_fh fh; -- cgit v1.2.3-58-ga151 From 8946ab3a4cb46abe0113a267857fdb32b05a3f23 Mon Sep 17 00:00:00 2001 From: "Nori, Sekhar" Date: Fri, 26 May 2017 07:55:27 -0300 Subject: [media] davinci: vpif_capture: fix default pixel format for BT.656/BT.1120 video For both BT.656 and BT.1120 video, the pixel format used by VPIF is Y/CbCr 4:2:2 in semi-planar format (Luma in one plane and Chroma in another). This corresponds to NV16 pixel format. This is documented in section 36.2.3 of OMAP-L138 Technical Reference Manual, SPRUH77A. The VPIF driver incorrectly sets the default format to V4L2_PIX_FMT_YUV422P. Fix it. Reported-by: Alejandro Hernandez Signed-off-by: Sekhar Nori Acked-by: Kevin Hilman Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/davinci/vpif_capture.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/media/platform/davinci/vpif_capture.c b/drivers/media/platform/davinci/vpif_capture.c index 44f702752d3a..128e92d1dd5a 100644 --- a/drivers/media/platform/davinci/vpif_capture.c +++ b/drivers/media/platform/davinci/vpif_capture.c @@ -513,7 +513,7 @@ static int vpif_update_std_info(struct channel_obj *ch) if (ch->vpifparams.iface.if_type == VPIF_IF_RAW_BAYER) common->fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_SBGGR8; else - common->fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUV422P; + common->fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_NV16; common->fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; @@ -917,8 +917,8 @@ static int vpif_enum_fmt_vid_cap(struct file *file, void *priv, fmt->pixelformat = V4L2_PIX_FMT_SBGGR8; } else { fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - strcpy(fmt->description, "YCbCr4:2:2 YC Planar"); - fmt->pixelformat = V4L2_PIX_FMT_YUV422P; + strcpy(fmt->description, "YCbCr4:2:2 Semi-Planar"); + fmt->pixelformat = V4L2_PIX_FMT_NV16; } return 0; } @@ -946,8 +946,8 @@ static int vpif_try_fmt_vid_cap(struct file *file, void *priv, if (pixfmt->pixelformat != V4L2_PIX_FMT_SBGGR8) pixfmt->pixelformat = V4L2_PIX_FMT_SBGGR8; } else { - if (pixfmt->pixelformat != V4L2_PIX_FMT_YUV422P) - pixfmt->pixelformat = V4L2_PIX_FMT_YUV422P; + if (pixfmt->pixelformat != V4L2_PIX_FMT_NV16) + pixfmt->pixelformat = V4L2_PIX_FMT_NV16; } common->fmt.fmt.pix.pixelformat = pixfmt->pixelformat; -- cgit v1.2.3-58-ga151 From 9e9e6a78143bbb6cb9cffd29ab48d5f32def4e20 Mon Sep 17 00:00:00 2001 From: Pavel Machek Date: Sat, 27 May 2017 05:12:40 -0300 Subject: [media] Doc*/media/uapi: fix control name V4L2_CID_EXPOSURE_BIAS does not exist, fix documentation. Signed-off-by: Pavel Machek Acked-by: Sakari Ailus Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/media/uapi/v4l/extended-controls.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/media/uapi/v4l/extended-controls.rst b/Documentation/media/uapi/v4l/extended-controls.rst index abb105724c05..76c5b1aeaadd 100644 --- a/Documentation/media/uapi/v4l/extended-controls.rst +++ b/Documentation/media/uapi/v4l/extended-controls.rst @@ -2019,7 +2019,7 @@ enum v4l2_exposure_auto_type - dynamically vary the frame rate. By default this feature is disabled (0) and the frame rate must remain constant. -``V4L2_CID_EXPOSURE_BIAS (integer menu)`` +``V4L2_CID_AUTO_EXPOSURE_BIAS (integer menu)`` Determines the automatic exposure compensation, it is effective only when ``V4L2_CID_EXPOSURE_AUTO`` control is set to ``AUTO``, ``SHUTTER_PRIORITY`` or ``APERTURE_PRIORITY``. It is expressed in -- cgit v1.2.3-58-ga151 From 6fb05e0dd32e566facb96ea61a48c7488daa5ac3 Mon Sep 17 00:00:00 2001 From: Steven Toth Date: Tue, 6 Jun 2017 09:30:27 -0300 Subject: [media] saa7164: fix double fetch PCIe access condition Avoid a double fetch by reusing the values from the prior transfer. Originally reported via https://bugzilla.kernel.org/show_bug.cgi?id=195559 Thanks to Pengfei Wang for reporting. Signed-off-by: Steven Toth Reported-by: Pengfei Wang Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/saa7164/saa7164-bus.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/drivers/media/pci/saa7164/saa7164-bus.c b/drivers/media/pci/saa7164/saa7164-bus.c index b2ff82fa7116..ecfeac5cdbed 100644 --- a/drivers/media/pci/saa7164/saa7164-bus.c +++ b/drivers/media/pci/saa7164/saa7164-bus.c @@ -389,11 +389,11 @@ int saa7164_bus_get(struct saa7164_dev *dev, struct tmComResInfo* msg, msg_tmp.size = le16_to_cpu((__force __le16)msg_tmp.size); msg_tmp.command = le32_to_cpu((__force __le32)msg_tmp.command); msg_tmp.controlselector = le16_to_cpu((__force __le16)msg_tmp.controlselector); + memcpy(msg, &msg_tmp, sizeof(*msg)); /* No need to update the read positions, because this was a peek */ /* If the caller specifically want to peek, return */ if (peekonly) { - memcpy(msg, &msg_tmp, sizeof(*msg)); goto peekout; } @@ -438,21 +438,15 @@ int saa7164_bus_get(struct saa7164_dev *dev, struct tmComResInfo* msg, space_rem = bus->m_dwSizeGetRing - curr_grp; if (space_rem < sizeof(*msg)) { - /* msg wraps around the ring */ - memcpy_fromio(msg, bus->m_pdwGetRing + curr_grp, space_rem); - memcpy_fromio((u8 *)msg + space_rem, bus->m_pdwGetRing, - sizeof(*msg) - space_rem); if (buf) memcpy_fromio(buf, bus->m_pdwGetRing + sizeof(*msg) - space_rem, buf_size); } else if (space_rem == sizeof(*msg)) { - memcpy_fromio(msg, bus->m_pdwGetRing + curr_grp, sizeof(*msg)); if (buf) memcpy_fromio(buf, bus->m_pdwGetRing, buf_size); } else { /* Additional data wraps around the ring */ - memcpy_fromio(msg, bus->m_pdwGetRing + curr_grp, sizeof(*msg)); if (buf) { memcpy_fromio(buf, bus->m_pdwGetRing + curr_grp + sizeof(*msg), space_rem - sizeof(*msg)); @@ -465,15 +459,10 @@ int saa7164_bus_get(struct saa7164_dev *dev, struct tmComResInfo* msg, } else { /* No wrapping */ - memcpy_fromio(msg, bus->m_pdwGetRing + curr_grp, sizeof(*msg)); if (buf) memcpy_fromio(buf, bus->m_pdwGetRing + curr_grp + sizeof(*msg), buf_size); } - /* Convert from little endian to CPU */ - msg->size = le16_to_cpu((__force __le16)msg->size); - msg->command = le32_to_cpu((__force __le32)msg->command); - msg->controlselector = le16_to_cpu((__force __le16)msg->controlselector); /* Update the read positions, adjusting the ring */ saa7164_writel(bus->m_dwGetReadPos, new_grp); -- cgit v1.2.3-58-ga151 From ec62c9a54cc28dd20dfb08b4663d021028b1a97e Mon Sep 17 00:00:00 2001 From: Hugues Fruchet Date: Fri, 19 May 2017 07:04:52 -0300 Subject: [media] atmel-isi: code cleanup Ensure that ISI is clocked before starting sensor sub device. Remove un-needed type check in try_fmt(). Use clamp() macro for hardware capabilities. Fix wrong tabulation to space. Signed-off-by: Hugues Fruchet Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/atmel/atmel-isi.c | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/drivers/media/platform/atmel/atmel-isi.c b/drivers/media/platform/atmel/atmel-isi.c index ef482cc704fe..891fa2505efa 100644 --- a/drivers/media/platform/atmel/atmel-isi.c +++ b/drivers/media/platform/atmel/atmel-isi.c @@ -37,8 +37,8 @@ #include "atmel-isi.h" -#define MAX_SUPPORT_WIDTH 2048 -#define MAX_SUPPORT_HEIGHT 2048 +#define MAX_SUPPORT_WIDTH 2048U +#define MAX_SUPPORT_HEIGHT 2048U #define MIN_FRAME_RATE 15 #define FRAME_INTERVAL_MILLI_SEC (1000 / MIN_FRAME_RATE) @@ -425,6 +425,8 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count) struct frame_buffer *buf, *node; int ret; + pm_runtime_get_sync(isi->dev); + /* Enable stream on the sub device */ ret = v4l2_subdev_call(isi->entity.subdev, video, s_stream, 1); if (ret && ret != -ENOIOCTLCMD) { @@ -432,8 +434,6 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count) goto err_start_stream; } - pm_runtime_get_sync(isi->dev); - /* Reset ISI */ ret = atmel_isi_wait_status(isi, WAIT_ISI_RESET); if (ret < 0) { @@ -456,10 +456,11 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count) return 0; err_reset: - pm_runtime_put(isi->dev); v4l2_subdev_call(isi->entity.subdev, video, s_stream, 0); err_start_stream: + pm_runtime_put(isi->dev); + spin_lock_irq(&isi->irqlock); isi->active = NULL; /* Release all active buffers */ @@ -567,20 +568,15 @@ static int isi_try_fmt(struct atmel_isi *isi, struct v4l2_format *f, }; int ret; - if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - isi_fmt = find_format_by_fourcc(isi, pixfmt->pixelformat); if (!isi_fmt) { isi_fmt = isi->user_formats[isi->num_user_formats - 1]; pixfmt->pixelformat = isi_fmt->fourcc; } - /* Limit to Atmel ISC hardware capabilities */ - if (pixfmt->width > MAX_SUPPORT_WIDTH) - pixfmt->width = MAX_SUPPORT_WIDTH; - if (pixfmt->height > MAX_SUPPORT_HEIGHT) - pixfmt->height = MAX_SUPPORT_HEIGHT; + /* Limit to Atmel ISI hardware capabilities */ + pixfmt->width = clamp(pixfmt->width, 0U, MAX_SUPPORT_WIDTH); + pixfmt->height = clamp(pixfmt->height, 0U, MAX_SUPPORT_HEIGHT); v4l2_fill_mbus_format(&format.format, pixfmt, isi_fmt->mbus_code); ret = v4l2_subdev_call(isi->entity.subdev, pad, set_fmt, @@ -1059,7 +1055,7 @@ static int isi_graph_notify_complete(struct v4l2_async_notifier *notifier) struct atmel_isi *isi = notifier_to_isi(notifier); int ret; - isi->vdev->ctrl_handler = isi->entity.subdev->ctrl_handler; + isi->vdev->ctrl_handler = isi->entity.subdev->ctrl_handler; ret = isi_formats_init(isi); if (ret) { dev_err(isi->dev, "No supported mediabus format found\n"); -- cgit v1.2.3-58-ga151 From cc0140e2a04513ccdd950d939729e557d23cc910 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Fri, 26 May 2017 05:21:37 -0300 Subject: [media] v4l2-ctrls.c: Implement unlocked variant of v4l2_ctrl_handler_setup() Sometimes the caller is already holding the control handler mutex and using it to serialise something. Provide an unlocked variant of the same function to be used in those cases. Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ctrls.c | 21 +++++++++++++++++++-- include/media/v4l2-ctrls.h | 13 +++++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index ec42872d11cf..dec55d53d24a 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -2444,14 +2444,16 @@ int v4l2_ctrl_subdev_log_status(struct v4l2_subdev *sd) EXPORT_SYMBOL(v4l2_ctrl_subdev_log_status); /* Call s_ctrl for all controls owned by the handler */ -int v4l2_ctrl_handler_setup(struct v4l2_ctrl_handler *hdl) +int __v4l2_ctrl_handler_setup(struct v4l2_ctrl_handler *hdl) { struct v4l2_ctrl *ctrl; int ret = 0; if (hdl == NULL) return 0; - mutex_lock(hdl->lock); + + lockdep_assert_held(hdl->lock); + list_for_each_entry(ctrl, &hdl->ctrls, node) ctrl->done = false; @@ -2476,7 +2478,22 @@ int v4l2_ctrl_handler_setup(struct v4l2_ctrl_handler *hdl) if (ret) break; } + + return ret; +} +EXPORT_SYMBOL_GPL(__v4l2_ctrl_handler_setup); + +int v4l2_ctrl_handler_setup(struct v4l2_ctrl_handler *hdl) +{ + int ret; + + if (hdl == NULL) + return 0; + + mutex_lock(hdl->lock); + ret = __v4l2_ctrl_handler_setup(hdl); mutex_unlock(hdl->lock); + return ret; } EXPORT_SYMBOL(v4l2_ctrl_handler_setup); diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h index bee1404391dd..2d2aed56922f 100644 --- a/include/media/v4l2-ctrls.h +++ b/include/media/v4l2-ctrls.h @@ -457,6 +457,19 @@ static inline void v4l2_ctrl_unlock(struct v4l2_ctrl *ctrl) mutex_unlock(ctrl->handler->lock); } +/** + * __v4l2_ctrl_handler_setup() - Call the s_ctrl op for all controls belonging + * to the handler to initialize the hardware to the current control values. The + * caller is responsible for acquiring the control handler mutex on behalf of + * __v4l2_ctrl_handler_setup(). + * @hdl: The control handler. + * + * Button controls will be skipped, as are read-only controls. + * + * If @hdl == NULL, then this just returns 0. + */ +int __v4l2_ctrl_handler_setup(struct v4l2_ctrl_handler *hdl); + /** * v4l2_ctrl_handler_setup() - Call the s_ctrl op for all controls belonging * to the handler to initialize the hardware to the current control values. -- cgit v1.2.3-58-ga151 From 48e3e36a253c66d47843ebe78422cec30095e3d5 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Mon, 29 May 2017 11:45:43 -0300 Subject: [media] v4l2-ctrls: Correctly destroy mutex in v4l2_ctrl_handler_free() The mutex that was initialised in v4l2_ctrl_handler_init_class() was not destroyed in v4l2_ctrl_handler_free(). Do that. Additionally, explicitly refer to the ctrl handler's mutex in mutex initialisation for clarity. Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ctrls.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index dec55d53d24a..5aed7bd20ad2 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -1739,8 +1739,8 @@ int v4l2_ctrl_handler_init_class(struct v4l2_ctrl_handler *hdl, unsigned nr_of_controls_hint, struct lock_class_key *key, const char *name) { + mutex_init(&hdl->_lock); hdl->lock = &hdl->_lock; - mutex_init(hdl->lock); lockdep_set_class_and_name(hdl->lock, key, name); INIT_LIST_HEAD(&hdl->ctrls); INIT_LIST_HEAD(&hdl->ctrl_refs); @@ -1780,6 +1780,7 @@ void v4l2_ctrl_handler_free(struct v4l2_ctrl_handler *hdl) hdl->cached = NULL; hdl->error = 0; mutex_unlock(hdl->lock); + mutex_destroy(&hdl->_lock); } EXPORT_SYMBOL(v4l2_ctrl_handler_free); -- cgit v1.2.3-58-ga151 From 43b288baa8f39054dc5e7102a329180bbeb1e115 Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Wed, 31 May 2017 06:31:54 -0300 Subject: [media] tc358743: Handle return value of clk_prepare_enable clk_prepare_enable() can fail here and we must check its return value. Signed-off-by: Arvind Yadav Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/tc358743.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/media/i2c/tc358743.c b/drivers/media/i2c/tc358743.c index 3e5b09030303..8f8bb9ee3e6d 100644 --- a/drivers/media/i2c/tc358743.c +++ b/drivers/media/i2c/tc358743.c @@ -1731,7 +1731,11 @@ static int tc358743_probe_of(struct tc358743_state *state) state->bus = endpoint->bus.mipi_csi2; - clk_prepare_enable(refclk); + ret = clk_prepare_enable(refclk); + if (ret) { + dev_err(dev, "Failed! to enable clock\n"); + goto free_endpoint; + } state->pdata.refclk_hz = clk_get_rate(refclk); state->pdata.ddc5v_delay = DDC5V_DELAY_100_MS; -- cgit v1.2.3-58-ga151 From a7a04b5b2c6e060c80cd9617b351ffbd618fc587 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 31 May 2017 06:50:26 -0300 Subject: [media] cec: improve debug messages - use __func__ instead of writing the full function name - drop debug message in cec_config_log_addr since the same information will be reported later - use debug level 1 for errors and infrequent events, use level 2 for debugging CEC message traffic - log when a transmit is retried, very useful to know when debugging - debug messages now all start with lower case Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/cec/cec-adap.c | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/drivers/media/cec/cec-adap.c b/drivers/media/cec/cec-adap.c index 9dfc79800c71..fd6d9cccade7 100644 --- a/drivers/media/cec/cec-adap.c +++ b/drivers/media/cec/cec-adap.c @@ -392,7 +392,7 @@ int cec_thread_func(void *_adap) * happen and is an indication of a faulty CEC adapter * driver, or the CEC bus is in some weird state. */ - dprintk(0, "message %*ph timed out!\n", + dprintk(0, "%s: message %*ph timed out!\n", __func__, adap->transmitting->msg.len, adap->transmitting->msg.msg); /* Just give up on this. */ @@ -468,7 +468,7 @@ void cec_transmit_done(struct cec_adapter *adap, u8 status, u8 arb_lost_cnt, struct cec_msg *msg; u64 ts = ktime_get_ns(); - dprintk(2, "cec_transmit_done %02x\n", status); + dprintk(2, "%s: status %02x\n", __func__, status); mutex_lock(&adap->lock); data = adap->transmitting; if (!data) { @@ -477,7 +477,8 @@ void cec_transmit_done(struct cec_adapter *adap, u8 status, u8 arb_lost_cnt, * unplugged while the transmit is ongoing. Ignore this * transmit in that case. */ - dprintk(1, "cec_transmit_done without an ongoing transmit!\n"); + dprintk(1, "%s was called without an ongoing transmit!\n", + __func__); goto unlock; } @@ -504,6 +505,12 @@ void cec_transmit_done(struct cec_adapter *adap, u8 status, u8 arb_lost_cnt, !(status & (CEC_TX_STATUS_MAX_RETRIES | CEC_TX_STATUS_OK))) { /* Retry this message */ data->attempts--; + if (msg->timeout) + dprintk(2, "retransmit: %*ph (attempts: %d, wait for 0x%02x)\n", + msg->len, msg->msg, data->attempts, msg->reply); + else + dprintk(2, "retransmit: %*ph (attempts: %d)\n", + msg->len, msg->msg, data->attempts); /* Add the message in front of the transmit queue */ list_add(&data->list, &adap->transmit_queue); adap->transmit_queue_sz++; @@ -911,7 +918,7 @@ void cec_received_msg(struct cec_adapter *adap, struct cec_msg *msg) memset(msg->msg + msg->len, 0, sizeof(msg->msg) - msg->len); mutex_lock(&adap->lock); - dprintk(2, "cec_received_msg: %*ph\n", msg->len, msg->msg); + dprintk(2, "%s: %*ph\n", __func__, msg->len, msg->msg); /* Check if this message was for us (directed or broadcast). */ if (!cec_msg_is_broadcast(msg)) @@ -1112,9 +1119,6 @@ static int cec_config_log_addr(struct cec_adapter *adap, las->log_addr[idx] = log_addr; las->log_addr_mask |= 1 << log_addr; adap->phys_addrs[log_addr] = adap->phys_addr; - - dprintk(2, "claimed addr %d (%d)\n", log_addr, - las->primary_device_type[idx]); return 1; } @@ -1300,7 +1304,7 @@ configured: /* Report Physical Address */ cec_msg_report_physical_addr(&msg, adap->phys_addr, las->primary_device_type[i]); - dprintk(2, "config: la %d pa %x.%x.%x.%x\n", + dprintk(1, "config: la %d pa %x.%x.%x.%x\n", las->log_addr[i], cec_phys_addr_exp(adap->phys_addr)); cec_transmit_msg_fh(adap, &msg, NULL, false); @@ -1534,12 +1538,12 @@ int __cec_s_log_addrs(struct cec_adapter *adap, if (log_addrs->num_log_addrs == 2) { if (!(type_mask & ((1 << CEC_LOG_ADDR_TYPE_AUDIOSYSTEM) | (1 << CEC_LOG_ADDR_TYPE_TV)))) { - dprintk(1, "Two LAs is only allowed for audiosystem and TV\n"); + dprintk(1, "two LAs is only allowed for audiosystem and TV\n"); return -EINVAL; } if (!(type_mask & ((1 << CEC_LOG_ADDR_TYPE_PLAYBACK) | (1 << CEC_LOG_ADDR_TYPE_RECORD)))) { - dprintk(1, "An audiosystem/TV can only be combined with record or playback\n"); + dprintk(1, "an audiosystem/TV can only be combined with record or playback\n"); return -EINVAL; } } @@ -1653,7 +1657,7 @@ static int cec_receive_notify(struct cec_adapter *adap, struct cec_msg *msg, bool from_unregistered = init_laddr == 0xf; struct cec_msg tx_cec_msg = { }; - dprintk(1, "cec_receive_notify: %*ph\n", msg->len, msg->msg); + dprintk(2, "%s: %*ph\n", __func__, msg->len, msg->msg); /* If this is a CDC-Only device, then ignore any non-CDC messages */ if (cec_is_cdc_only(&adap->log_addrs) && @@ -1722,7 +1726,7 @@ static int cec_receive_notify(struct cec_adapter *adap, struct cec_msg *msg, if (!from_unregistered) adap->phys_addrs[init_laddr] = pa; - dprintk(1, "Reported physical address %x.%x.%x.%x for logical address %d\n", + dprintk(1, "reported physical address %x.%x.%x.%x for logical address %d\n", cec_phys_addr_exp(pa), init_laddr); break; } -- cgit v1.2.3-58-ga151 From 3cb0fe6f74e566b1e4df81f518db16cedb268fc5 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Fri, 2 Jun 2017 09:18:12 -0300 Subject: [media] tc358743: Add enum_mbus_code There was no way to query the supported mbus formats from this driver. enum_mbus_code is the function to expose that, so implement it. Signed-off-by: Dave Stevenson Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/tc358743.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/drivers/media/i2c/tc358743.c b/drivers/media/i2c/tc358743.c index 8f8bb9ee3e6d..6ba65c783161 100644 --- a/drivers/media/i2c/tc358743.c +++ b/drivers/media/i2c/tc358743.c @@ -1474,6 +1474,23 @@ static int tc358743_s_stream(struct v4l2_subdev *sd, int enable) /* --------------- PAD OPS --------------- */ +static int tc358743_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + switch (code->index) { + case 0: + code->code = MEDIA_BUS_FMT_RGB888_1X24; + break; + case 1: + code->code = MEDIA_BUS_FMT_UYVY8_1X16; + break; + default: + return -EINVAL; + } + return 0; +} + static int tc358743_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *format) @@ -1643,6 +1660,7 @@ static const struct v4l2_subdev_video_ops tc358743_video_ops = { }; static const struct v4l2_subdev_pad_ops tc358743_pad_ops = { + .enum_mbus_code = tc358743_enum_mbus_code, .set_fmt = tc358743_set_fmt, .get_fmt = tc358743_get_fmt, .get_edid = tc358743_g_edid, -- cgit v1.2.3-58-ga151 From 2da2391c0f200b07bdc4b8984d69112337dc6dc7 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Fri, 2 Jun 2017 09:18:13 -0300 Subject: [media] tc358743: Setup default mbus_fmt before registering Previously the mbus_fmt_code was set after the device was registered. If a connected sub-device called tc358743_get_fmt prior to that point it would get an invalid code back. Signed-off-by: Dave Stevenson Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/tc358743.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/i2c/tc358743.c b/drivers/media/i2c/tc358743.c index 6ba65c783161..7d20cac9711c 100644 --- a/drivers/media/i2c/tc358743.c +++ b/drivers/media/i2c/tc358743.c @@ -1910,6 +1910,8 @@ static int tc358743_probe(struct i2c_client *client, if (err < 0) goto err_hdl; + state->mbus_fmt_code = MEDIA_BUS_FMT_RGB888_1X24; + sd->dev = &client->dev; err = v4l2_async_register_subdev(sd); if (err < 0) @@ -1924,7 +1926,6 @@ static int tc358743_probe(struct i2c_client *client, tc358743_s_dv_timings(sd, &default_timing); - state->mbus_fmt_code = MEDIA_BUS_FMT_RGB888_1X24; tc358743_set_csi_color_space(sd); tc358743_init_interrupts(sd); -- cgit v1.2.3-58-ga151 From 4e66a52a2e4c832dfa35a39204d0f7ce717d4a4a Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Fri, 2 Jun 2017 09:18:14 -0300 Subject: [media] tc358743: Add support for platforms without IRQ line interrupts is listed as an optional property in the DT binding, but in reality the driver didn't work without it. The existing driver relied on having the interrupt line connected to the SoC to trigger handling various events. Add the option to poll the interrupt status register via a timer if no interrupt source is defined. Signed-off-by: Dave Stevenson Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/tc358743.c | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/drivers/media/i2c/tc358743.c b/drivers/media/i2c/tc358743.c index 7d20cac9711c..8c54013984ed 100644 --- a/drivers/media/i2c/tc358743.c +++ b/drivers/media/i2c/tc358743.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -62,6 +63,8 @@ MODULE_LICENSE("GPL"); #define I2C_MAX_XFER_SIZE (EDID_BLOCK_SIZE + 2) +#define POLL_INTERVAL_MS 1000 + static const struct v4l2_dv_timings_cap tc358743_timings_cap = { .type = V4L2_DV_BT_656_1120, /* keep this initialization for compatibility with GCC < 4.4.6 */ @@ -92,6 +95,9 @@ struct tc358743_state { struct delayed_work delayed_work_enable_hotplug; + struct timer_list timer; + struct work_struct work_i2c_poll; + /* edid */ u8 edid_blocks_written; @@ -1320,6 +1326,24 @@ static irqreturn_t tc358743_irq_handler(int irq, void *dev_id) return handled ? IRQ_HANDLED : IRQ_NONE; } +static void tc358743_irq_poll_timer(unsigned long arg) +{ + struct tc358743_state *state = (struct tc358743_state *)arg; + + schedule_work(&state->work_i2c_poll); + + mod_timer(&state->timer, jiffies + msecs_to_jiffies(POLL_INTERVAL_MS)); +} + +static void tc358743_work_i2c_poll(struct work_struct *work) +{ + struct tc358743_state *state = container_of(work, + struct tc358743_state, work_i2c_poll); + bool handled; + + tc358743_isr(&state->sd, 0, &handled); +} + static int tc358743_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh, struct v4l2_event_subscription *sub) { @@ -1938,6 +1962,14 @@ static int tc358743_probe(struct i2c_client *client, "tc358743", state); if (err) goto err_work_queues; + } else { + INIT_WORK(&state->work_i2c_poll, + tc358743_work_i2c_poll); + state->timer.data = (unsigned long)state; + state->timer.function = tc358743_irq_poll_timer; + state->timer.expires = jiffies + + msecs_to_jiffies(POLL_INTERVAL_MS); + add_timer(&state->timer); } tc358743_enable_interrupts(sd, tx_5v_power_present(sd)); @@ -1953,6 +1985,8 @@ static int tc358743_probe(struct i2c_client *client, return 0; err_work_queues: + if (!state->i2c_client->irq) + flush_work(&state->work_i2c_poll); cancel_delayed_work(&state->delayed_work_enable_hotplug); mutex_destroy(&state->confctl_mutex); err_hdl: @@ -1966,6 +2000,10 @@ static int tc358743_remove(struct i2c_client *client) struct v4l2_subdev *sd = i2c_get_clientdata(client); struct tc358743_state *state = to_state(sd); + if (!state->i2c_client->irq) { + del_timer_sync(&state->timer); + flush_work(&state->work_i2c_poll); + } cancel_delayed_work(&state->delayed_work_enable_hotplug); v4l2_async_unregister_subdev(sd); v4l2_device_unregister_subdev(sd); -- cgit v1.2.3-58-ga151 From 2d5a6ce71c72d98d4f7948672842e3e8c265a8b7 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 1 Jun 2017 04:45:59 -0300 Subject: [media] mceusb: fix memory leaks in error path Fix urb and transfer-buffer leaks in an urb-submission error path which may be hit when a device is disconnected. Fixes: 66e89522aff7 ("V4L/DVB: IR: add mceusb IR receiver driver") Cc: stable # 2.6.36 Cc: Jarod Wilson Signed-off-by: Johan Hovold Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/mceusb.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/rc/mceusb.c b/drivers/media/rc/mceusb.c index 4530237cbb67..04d6cd1818fb 100644 --- a/drivers/media/rc/mceusb.c +++ b/drivers/media/rc/mceusb.c @@ -795,6 +795,8 @@ static void mce_request_packet(struct mceusb_dev *ir, unsigned char *data, res = usb_submit_urb(async_urb, GFP_ATOMIC); if (res) { dev_err(dev, "send request FAILED! (res=%d)", res); + kfree(async_buf); + usb_free_urb(async_urb); return; } dev_dbg(dev, "send request complete (res=%d)", res); -- cgit v1.2.3-58-ga151 From fb3562c81e98e52f6acca2cd7ee36532634ec38e Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 1 Jun 2017 04:46:00 -0300 Subject: [media] mceusb: drop redundant urb reinitialisation Drop a since commit e1159cb35712 ("[media] mceusb: remove pointless mce_flush_rx_buffer function") redundant reinitialisation of two urb fields immediately after they have been initialised. Signed-off-by: Johan Hovold Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/mceusb.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/media/rc/mceusb.c b/drivers/media/rc/mceusb.c index 04d6cd1818fb..eb130694bbb8 100644 --- a/drivers/media/rc/mceusb.c +++ b/drivers/media/rc/mceusb.c @@ -789,9 +789,6 @@ static void mce_request_packet(struct mceusb_dev *ir, unsigned char *data, dev_dbg(dev, "send request called (size=%#x)", size); - async_urb->transfer_buffer_length = size; - async_urb->dev = ir->usbdev; - res = usb_submit_urb(async_urb, GFP_ATOMIC); if (res) { dev_err(dev, "send request FAILED! (res=%d)", res); -- cgit v1.2.3-58-ga151 From cc20ba4ed8576abfa10a17e81cb4521f474624f0 Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Sat, 6 May 2017 22:00:11 -0300 Subject: [media] ir-spi: Fix issues with lirc API The ir-spi driver has 2 issues which prevents it from working with lirc: 1. The ir-spi driver uses 16 bits of SPI data to create one cycle of the waveform. As such our SPI clock needs to be 16x faster than the carrier frequency. The driver is inconsistent in how it currently handles this. It initializes it to the carrier frequency: But the commit message has some example code which initialises it to 16x the carrier frequency: val = 608000; ret = ioctl(fd, LIRC_SET_SEND_CARRIER, &val); To maintain compatibility with lirc, always do the frequency adjustment in the driver. 2. lirc presents pulses in microseconds, but the ir-spi driver treats them as cycles of the carrier. Similar to other lirc drivers, do the conversion with DIV_ROUND_CLOSEST(). Fixes: fe052da49201 ("[media] rc: add support for IR LEDs driven through SPI") Cc: stable@vger.kernel.org Signed-off-by: Anton Blanchard Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/ir-spi.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/media/rc/ir-spi.c b/drivers/media/rc/ir-spi.c index 4ca43383a8e8..7e383b3fedd5 100644 --- a/drivers/media/rc/ir-spi.c +++ b/drivers/media/rc/ir-spi.c @@ -57,10 +57,13 @@ static int ir_spi_tx(struct rc_dev *dev, /* convert the pulse/space signal to raw binary signal */ for (i = 0; i < count; i++) { + unsigned int periods; int j; u16 val; - if (len + buffer[i] >= IR_SPI_MAX_BUFSIZE) + periods = DIV_ROUND_CLOSEST(buffer[i] * idata->freq, 1000000); + + if (len + periods >= IR_SPI_MAX_BUFSIZE) return -EINVAL; /* @@ -69,13 +72,13 @@ static int ir_spi_tx(struct rc_dev *dev, * contain a space duration. */ val = (i % 2) ? idata->space : idata->pulse; - for (j = 0; j < buffer[i]; j++) + for (j = 0; j < periods; j++) idata->tx_buf[len++] = val; } memset(&xfer, 0, sizeof(xfer)); - xfer.speed_hz = idata->freq; + xfer.speed_hz = idata->freq * 16; xfer.len = len * sizeof(*idata->tx_buf); xfer.tx_buf = idata->tx_buf; -- cgit v1.2.3-58-ga151 From 6e3520f2e20262fbf913ffff14f1d345c7b82c25 Mon Sep 17 00:00:00 2001 From: Kevin Hilman Date: Tue, 6 Jun 2017 20:37:38 -0300 Subject: [media] davinci: vpif_capture: drop compliance hack Capture driver silently overrides pixel format with a hack (according to the comments) to pass v4l2 compliance tests. This isn't needed for normal functionality, and works for composite video and raw camera capture without. In addition, the hack assumes that it only supports raw capture with a single format (SBGGR8) which isn't true. VPIF can also capture 10- and 12-bit raw formats as well. Forthcoming patches will enable VPIF input with raw-camera support and has been tested with 10-bit format from the aptina,mt9v032 sensor. Any compliance failures should be fixed with a real fix. Signed-off-by: Kevin Hilman Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/davinci/vpif_capture.c | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/drivers/media/platform/davinci/vpif_capture.c b/drivers/media/platform/davinci/vpif_capture.c index 128e92d1dd5a..fc5c7622660c 100644 --- a/drivers/media/platform/davinci/vpif_capture.c +++ b/drivers/media/platform/davinci/vpif_capture.c @@ -936,21 +936,6 @@ static int vpif_try_fmt_vid_cap(struct file *file, void *priv, struct channel_obj *ch = video_get_drvdata(vdev); struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; struct common_obj *common = &(ch->common[VPIF_VIDEO_INDEX]); - struct vpif_params *vpif_params = &ch->vpifparams; - - /* - * to supress v4l-compliance warnings silently correct - * the pixelformat - */ - if (vpif_params->iface.if_type == VPIF_IF_RAW_BAYER) { - if (pixfmt->pixelformat != V4L2_PIX_FMT_SBGGR8) - pixfmt->pixelformat = V4L2_PIX_FMT_SBGGR8; - } else { - if (pixfmt->pixelformat != V4L2_PIX_FMT_NV16) - pixfmt->pixelformat = V4L2_PIX_FMT_NV16; - } - - common->fmt.fmt.pix.pixelformat = pixfmt->pixelformat; vpif_update_std_info(ch); -- cgit v1.2.3-58-ga151 From 4a5f8ae50b66a40cd9cec8f72f5f64611504ddc1 Mon Sep 17 00:00:00 2001 From: Kevin Hilman Date: Tue, 6 Jun 2017 20:37:39 -0300 Subject: [media] davinci: vpif_capture: get subdevs from DT when available Enable getting of subdevs from DT ports and endpoints. The _get_pdata() function was larely inspired by (i.e. stolen from) am437x-vpfe.c Signed-off-by: Kevin Hilman Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/davinci/vpif_capture.c | 126 +++++++++++++++++++++++++- drivers/media/platform/davinci/vpif_display.c | 5 + include/media/davinci/vpif_types.h | 9 +- 3 files changed, 134 insertions(+), 6 deletions(-) diff --git a/drivers/media/platform/davinci/vpif_capture.c b/drivers/media/platform/davinci/vpif_capture.c index fc5c7622660c..b9d927d1e5a8 100644 --- a/drivers/media/platform/davinci/vpif_capture.c +++ b/drivers/media/platform/davinci/vpif_capture.c @@ -22,6 +22,8 @@ #include #include +#include +#include #include "vpif.h" #include "vpif_capture.h" @@ -655,7 +657,7 @@ static int vpif_input_to_subdev( /* loop through the sub device list to get the sub device info */ for (i = 0; i < vpif_cfg->subdev_count; i++) { subdev_info = &vpif_cfg->subdev_info[i]; - if (!strcmp(subdev_info->name, subdev_name)) + if (subdev_info && !strcmp(subdev_info->name, subdev_name)) return i; } return -1; @@ -1308,6 +1310,21 @@ static int vpif_async_bound(struct v4l2_async_notifier *notifier, { int i; + for (i = 0; i < vpif_obj.config->asd_sizes[0]; i++) { + struct v4l2_async_subdev *_asd = vpif_obj.config->asd[i]; + const struct device_node *node = _asd->match.of.node; + + if (node == subdev->of_node) { + vpif_obj.sd[i] = subdev; + vpif_obj.config->chan_config->inputs[i].subdev_name = + (char *)subdev->of_node->full_name; + vpif_dbg(2, debug, + "%s: setting input %d subdev_name = %s\n", + __func__, i, subdev->of_node->full_name); + return 0; + } + } + for (i = 0; i < vpif_obj.config->subdev_count; i++) if (!strcmp(vpif_obj.config->subdev_info[i].name, subdev->name)) { @@ -1403,6 +1420,105 @@ static int vpif_async_complete(struct v4l2_async_notifier *notifier) return vpif_probe_complete(); } +static struct vpif_capture_config * +vpif_capture_get_pdata(struct platform_device *pdev) +{ + struct device_node *endpoint = NULL; + struct v4l2_of_endpoint bus_cfg; + struct vpif_capture_config *pdata; + struct vpif_subdev_info *sdinfo; + struct vpif_capture_chan_config *chan; + unsigned int i; + + /* + * DT boot: OF node from parent device contains + * video ports & endpoints data. + */ + if (pdev->dev.parent && pdev->dev.parent->of_node) + pdev->dev.of_node = pdev->dev.parent->of_node; + if (!IS_ENABLED(CONFIG_OF) || !pdev->dev.of_node) + return pdev->dev.platform_data; + + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return NULL; + pdata->subdev_info = + devm_kzalloc(&pdev->dev, sizeof(*pdata->subdev_info) * + VPIF_CAPTURE_NUM_CHANNELS, GFP_KERNEL); + + if (!pdata->subdev_info) + return NULL; + + for (i = 0; i < VPIF_CAPTURE_NUM_CHANNELS; i++) { + struct device_node *rem; + unsigned int flags; + int err; + + endpoint = of_graph_get_next_endpoint(pdev->dev.of_node, + endpoint); + if (!endpoint) + break; + + sdinfo = &pdata->subdev_info[i]; + chan = &pdata->chan_config[i]; + chan->inputs = devm_kzalloc(&pdev->dev, + sizeof(*chan->inputs) * + VPIF_CAPTURE_NUM_CHANNELS, + GFP_KERNEL); + + chan->input_count++; + chan->inputs[i].input.type = V4L2_INPUT_TYPE_CAMERA; + chan->inputs[i].input.std = V4L2_STD_ALL; + chan->inputs[i].input.capabilities = V4L2_IN_CAP_STD; + + err = v4l2_of_parse_endpoint(endpoint, &bus_cfg); + if (err) { + dev_err(&pdev->dev, "Could not parse the endpoint\n"); + goto done; + } + dev_dbg(&pdev->dev, "Endpoint %s, bus_width = %d\n", + endpoint->full_name, bus_cfg.bus.parallel.bus_width); + flags = bus_cfg.bus.parallel.flags; + + if (flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) + chan->vpif_if.hd_pol = 1; + + if (flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) + chan->vpif_if.vd_pol = 1; + + rem = of_graph_get_remote_port_parent(endpoint); + if (!rem) { + dev_dbg(&pdev->dev, "Remote device at %s not found\n", + endpoint->full_name); + goto done; + } + + dev_dbg(&pdev->dev, "Remote device %s, %s found\n", + rem->name, rem->full_name); + sdinfo->name = rem->full_name; + + pdata->asd[i] = devm_kzalloc(&pdev->dev, + sizeof(struct v4l2_async_subdev), + GFP_KERNEL); + if (!pdata->asd[i]) { + of_node_put(rem); + pdata = NULL; + goto done; + } + + pdata->asd[i]->match_type = V4L2_ASYNC_MATCH_OF; + pdata->asd[i]->match.of.node = rem; + of_node_put(rem); + } + +done: + pdata->asd_sizes[0] = i; + pdata->subdev_count = i; + pdata->card_name = "DA850/OMAP-L138 Video Capture"; + + return pdata; +} + /** * vpif_probe : This function probes the vpif capture driver * @pdev: platform device pointer @@ -1419,6 +1535,12 @@ static __init int vpif_probe(struct platform_device *pdev) int res_idx = 0; int i, err; + pdev->dev.platform_data = vpif_capture_get_pdata(pdev); + if (!pdev->dev.platform_data) { + dev_warn(&pdev->dev, "Missing platform data. Giving up.\n"); + return -EINVAL; + } + if (!pdev->dev.platform_data) { dev_warn(&pdev->dev, "Missing platform data. Giving up.\n"); return -EINVAL; @@ -1459,7 +1581,7 @@ static __init int vpif_probe(struct platform_device *pdev) goto vpif_unregister; } - if (!vpif_obj.config->asd_sizes) { + if (!vpif_obj.config->asd_sizes[0]) { int i2c_id = vpif_obj.config->i2c_adapter_id; i2c_adap = i2c_get_adapter(i2c_id); diff --git a/drivers/media/platform/davinci/vpif_display.c b/drivers/media/platform/davinci/vpif_display.c index 7e5cf9923c8d..b5ac6ce626b3 100644 --- a/drivers/media/platform/davinci/vpif_display.c +++ b/drivers/media/platform/davinci/vpif_display.c @@ -1250,6 +1250,11 @@ static __init int vpif_probe(struct platform_device *pdev) return -EINVAL; } + if (!pdev->dev.platform_data) { + dev_warn(&pdev->dev, "Missing platform data. Giving up.\n"); + return -EINVAL; + } + vpif_dev = &pdev->dev; err = initialize_vpif(); diff --git a/include/media/davinci/vpif_types.h b/include/media/davinci/vpif_types.h index 385597da20dc..eae23e4e9b93 100644 --- a/include/media/davinci/vpif_types.h +++ b/include/media/davinci/vpif_types.h @@ -62,14 +62,14 @@ struct vpif_display_config { struct vpif_input { struct v4l2_input input; - const char *subdev_name; + char *subdev_name; u32 input_route; u32 output_route; }; struct vpif_capture_chan_config { struct vpif_interface vpif_if; - const struct vpif_input *inputs; + struct vpif_input *inputs; int input_count; }; @@ -81,7 +81,8 @@ struct vpif_capture_config { int subdev_count; int i2c_adapter_id; const char *card_name; - struct v4l2_async_subdev **asd; /* Flat array, arranged in groups */ - int *asd_sizes; /* 0-terminated array of asd group sizes */ + + struct v4l2_async_subdev *asd[VPIF_CAPTURE_MAX_CHANNELS]; + int asd_sizes[VPIF_CAPTURE_MAX_CHANNELS]; }; #endif /* _VPIF_TYPES_H */ -- cgit v1.2.3-58-ga151 From dde32d413447d7f12031d24c7898dd126e5eabe3 Mon Sep 17 00:00:00 2001 From: Kevin Hilman Date: Tue, 6 Jun 2017 20:37:40 -0300 Subject: [media] davinci: vpif_capture: cleanup raw camera support The current driver has a handful of hard-coded assumptions based on its primary use for capture of video signals. Cleanup those assumptions, and also query the subdev for format information and use that if available. Tested with 10-bit raw bayer input (SGRBG10) using the aptina,mt9v032 sensor, and also tested that composite video input still works from ti,tvp514x decoder. Both tests done on the da850-evm board with the add-on UI board. NOTE: Will need further testing for other sensors with different bus formats. Signed-off-by: Kevin Hilman Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/davinci/vpif_capture.c | 82 ++++++++++++++++++++++++++- 1 file changed, 80 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/davinci/vpif_capture.c b/drivers/media/platform/davinci/vpif_capture.c index b9d927d1e5a8..27357954f1bb 100644 --- a/drivers/media/platform/davinci/vpif_capture.c +++ b/drivers/media/platform/davinci/vpif_capture.c @@ -24,6 +24,9 @@ #include #include #include +#include + +#include #include "vpif.h" #include "vpif_capture.h" @@ -387,7 +390,8 @@ static irqreturn_t vpif_channel_isr(int irq, void *dev_id) common = &ch->common[i]; /* skip If streaming is not started in this channel */ /* Check the field format */ - if (1 == ch->vpifparams.std_info.frm_fmt) { + if (1 == ch->vpifparams.std_info.frm_fmt || + common->fmt.fmt.pix.field == V4L2_FIELD_NONE) { /* Progressive mode */ spin_lock(&common->irqlock); if (list_empty(&common->dma_queue)) { @@ -468,9 +472,38 @@ static int vpif_update_std_info(struct channel_obj *ch) struct vpif_channel_config_params *std_info = &vpifparams->std_info; struct video_obj *vid_ch = &ch->video; int index; + struct v4l2_pix_format *pixfmt = &common->fmt.fmt.pix; vpif_dbg(2, debug, "vpif_update_std_info\n"); + /* + * if called after try_fmt or g_fmt, there will already be a size + * so use that by default. + */ + if (pixfmt->width && pixfmt->height) { + if (pixfmt->field == V4L2_FIELD_ANY || + pixfmt->field == V4L2_FIELD_NONE) + pixfmt->field = V4L2_FIELD_NONE; + + vpifparams->iface.if_type = VPIF_IF_BT656; + if (pixfmt->pixelformat == V4L2_PIX_FMT_SGRBG10 || + pixfmt->pixelformat == V4L2_PIX_FMT_SBGGR8) + vpifparams->iface.if_type = VPIF_IF_RAW_BAYER; + + if (pixfmt->pixelformat == V4L2_PIX_FMT_SGRBG10) + vpifparams->params.data_sz = 1; /* 10 bits/pixel. */ + + /* + * For raw formats from camera sensors, we don't need + * the std_info from table lookup, so nothing else to do here. + */ + if (vpifparams->iface.if_type == VPIF_IF_RAW_BAYER) { + memset(std_info, 0, sizeof(struct vpif_channel_config_params)); + vpifparams->std_info.capture_format = 1; /* CCD/raw mode */ + return 0; + } + } + for (index = 0; index < vpif_ch_params_count; index++) { config = &vpif_ch_params[index]; if (config->hd_sd == 0) { @@ -939,6 +972,7 @@ static int vpif_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; struct common_obj *common = &(ch->common[VPIF_VIDEO_INDEX]); + common->fmt = *fmt; vpif_update_std_info(ch); pixfmt->field = common->fmt.fmt.pix.field; @@ -947,8 +981,17 @@ static int vpif_try_fmt_vid_cap(struct file *file, void *priv, pixfmt->width = common->fmt.fmt.pix.width; pixfmt->height = common->fmt.fmt.pix.height; pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height * 2; + if (pixfmt->pixelformat == V4L2_PIX_FMT_SGRBG10) { + pixfmt->bytesperline = common->fmt.fmt.pix.width * 2; + pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height; + } pixfmt->priv = 0; + dev_dbg(vpif_dev, "%s: %d x %d; pitch=%d pixelformat=0x%08x, field=%d, size=%d\n", __func__, + pixfmt->width, pixfmt->height, + pixfmt->bytesperline, pixfmt->pixelformat, + pixfmt->field, pixfmt->sizeimage); + return 0; } @@ -965,13 +1008,47 @@ static int vpif_g_fmt_vid_cap(struct file *file, void *priv, struct video_device *vdev = video_devdata(file); struct channel_obj *ch = video_get_drvdata(vdev); struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + struct v4l2_pix_format *pix_fmt = &fmt->fmt.pix; + struct v4l2_subdev_format format = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + struct v4l2_mbus_framefmt *mbus_fmt = &format.format; + int ret; /* Check the validity of the buffer type */ if (common->fmt.type != fmt->type) return -EINVAL; - /* Fill in the information about format */ + /* By default, use currently set fmt */ *fmt = common->fmt; + + /* If subdev has get_fmt, use that to override */ + ret = v4l2_subdev_call(ch->sd, pad, get_fmt, NULL, &format); + if (!ret && mbus_fmt->code) { + v4l2_fill_pix_format(pix_fmt, mbus_fmt); + pix_fmt->bytesperline = pix_fmt->width; + if (mbus_fmt->code == MEDIA_BUS_FMT_SGRBG10_1X10) { + /* e.g. mt9v032 */ + pix_fmt->pixelformat = V4L2_PIX_FMT_SGRBG10; + pix_fmt->bytesperline = pix_fmt->width * 2; + } else if (mbus_fmt->code == MEDIA_BUS_FMT_UYVY8_2X8) { + /* e.g. tvp514x */ + pix_fmt->pixelformat = V4L2_PIX_FMT_NV16; + pix_fmt->bytesperline = pix_fmt->width * 2; + } else { + dev_warn(vpif_dev, "%s: Unhandled media-bus format 0x%x\n", + __func__, mbus_fmt->code); + } + pix_fmt->sizeimage = pix_fmt->bytesperline * pix_fmt->height; + dev_dbg(vpif_dev, "%s: %d x %d; pitch=%d, pixelformat=0x%08x, code=0x%x, field=%d, size=%d\n", __func__, + pix_fmt->width, pix_fmt->height, + pix_fmt->bytesperline, pix_fmt->pixelformat, + mbus_fmt->code, pix_fmt->field, pix_fmt->sizeimage); + + common->fmt = *fmt; + vpif_update_std_info(ch); + } + return 0; } @@ -1358,6 +1435,7 @@ static int vpif_probe_complete(void) /* set initial format */ ch->video.stdid = V4L2_STD_525_60; memset(&ch->video.dv_timings, 0, sizeof(ch->video.dv_timings)); + common->fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; vpif_update_std_info(ch); /* Initialize vb2 queue */ -- cgit v1.2.3-58-ga151 From d989dc20c508cd82e2a95ff5d6c4bb091803f1c8 Mon Sep 17 00:00:00 2001 From: Jia-Ju Bai Date: Thu, 1 Jun 2017 04:13:54 -0300 Subject: [media] ivtv: Fix a sleep-in-atomic bug in snd_ivtv_pcm_hw_free The driver may sleep under a spin lock, and the function call path is: snd_ivtv_pcm_hw_free (acquire the lock by spin_lock_irqsave) vfree --> may sleep To fix it, the "substream->runtime->dma_area" is passed to a temporary value, and mark it NULL when holding the lock. The memory is freed by vfree through the temporary value outside the lock holding. Signed-off-by: Jia-Ju Bai [hans.verkuil@cisco.com: removed unnecessary 'if (dma_area)'] Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/ivtv/ivtv-alsa-pcm.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/media/pci/ivtv/ivtv-alsa-pcm.c b/drivers/media/pci/ivtv/ivtv-alsa-pcm.c index 807ead20d212..417d03da01f0 100644 --- a/drivers/media/pci/ivtv/ivtv-alsa-pcm.c +++ b/drivers/media/pci/ivtv/ivtv-alsa-pcm.c @@ -262,14 +262,16 @@ static int snd_ivtv_pcm_hw_free(struct snd_pcm_substream *substream) { struct snd_ivtv_card *itvsc = snd_pcm_substream_chip(substream); unsigned long flags; + unsigned char *dma_area = NULL; spin_lock_irqsave(&itvsc->slock, flags); if (substream->runtime->dma_area) { dprintk("freeing pcm capture region\n"); - vfree(substream->runtime->dma_area); + dma_area = substream->runtime->dma_area; substream->runtime->dma_area = NULL; } spin_unlock_irqrestore(&itvsc->slock, flags); + vfree(dma_area); return 0; } -- cgit v1.2.3-58-ga151 From a3dbff6eecad72333bae656681331aab27adee4d Mon Sep 17 00:00:00 2001 From: Jia-Ju Bai Date: Thu, 1 Jun 2017 04:17:51 -0300 Subject: [media] cx18: Fix a sleep-in-atomic bug in snd_cx18_pcm_hw_free The driver may sleep under a spin lock, and the function call path is: snd_cx18_pcm_hw_free (acquire the lock by spin_lock_irqsave) vfree --> may sleep To fix it, the "substream->runtime->dma_area" is passed to a temporary value, and mark it NULL when holding the lock. The memory is freed by vfree through the temporary value outside the lock holding. Signed-off-by: Jia-Ju Bai [hans.verkuil@cisco.com: removed unnecessary 'if (dma_area)'] Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/cx18/cx18-alsa-pcm.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/media/pci/cx18/cx18-alsa-pcm.c b/drivers/media/pci/cx18/cx18-alsa-pcm.c index 205a98da877c..f68ee57a9ae2 100644 --- a/drivers/media/pci/cx18/cx18-alsa-pcm.c +++ b/drivers/media/pci/cx18/cx18-alsa-pcm.c @@ -257,14 +257,16 @@ static int snd_cx18_pcm_hw_free(struct snd_pcm_substream *substream) { struct snd_cx18_card *cxsc = snd_pcm_substream_chip(substream); unsigned long flags; + unsigned char *dma_area = NULL; spin_lock_irqsave(&cxsc->slock, flags); if (substream->runtime->dma_area) { dprintk("freeing pcm capture region\n"); - vfree(substream->runtime->dma_area); + dma_area = substream->runtime->dma_area; substream->runtime->dma_area = NULL; } spin_unlock_irqrestore(&cxsc->slock, flags); + vfree(dma_area); return 0; } -- cgit v1.2.3-58-ga151 From 7e68c53387f1180745d6ef25d6425aef767b519e Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Tue, 6 Jun 2017 12:59:01 -0300 Subject: [media] coda: implement forced key frames Implement the V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME control to force IDR frames. This is useful to implement VFU (Video Fast Update) on RTP transmissions. We already force an IDR frame at the beginning of each GOP to work around a firmware bug on i.MX27, use the same mechanism to service IDR requests from userspace. Signed-off-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/coda/coda-bit.c | 12 ++++++++---- drivers/media/platform/coda/coda-common.c | 3 +++ drivers/media/platform/coda/coda.h | 1 + 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/drivers/media/platform/coda/coda-bit.c b/drivers/media/platform/coda/coda-bit.c index 403214e00e95..22e4630f3671 100644 --- a/drivers/media/platform/coda/coda-bit.c +++ b/drivers/media/platform/coda/coda-bit.c @@ -1247,12 +1247,18 @@ static int coda_prepare_encode(struct coda_ctx *ctx) dst_buf->sequence = ctx->osequence; ctx->osequence++; + force_ipicture = ctx->params.force_ipicture; + if (force_ipicture) + ctx->params.force_ipicture = false; + else if ((src_buf->sequence % ctx->params.gop_size) == 0) + force_ipicture = 1; + /* * Workaround coda firmware BUG that only marks the first * frame as IDR. This is a problem for some decoders that can't * recover when a frame is lost. */ - if (src_buf->sequence % ctx->params.gop_size) { + if (!force_ipicture) { src_buf->flags |= V4L2_BUF_FLAG_PFRAME; src_buf->flags &= ~V4L2_BUF_FLAG_KEYFRAME; } else { @@ -1291,8 +1297,7 @@ static int coda_prepare_encode(struct coda_ctx *ctx) pic_stream_buffer_size = q_data_dst->sizeimage; } - if (src_buf->flags & V4L2_BUF_FLAG_KEYFRAME) { - force_ipicture = 1; + if (force_ipicture) { switch (dst_fourcc) { case V4L2_PIX_FMT_H264: quant_param = ctx->params.h264_intra_qp; @@ -1309,7 +1314,6 @@ static int coda_prepare_encode(struct coda_ctx *ctx) break; } } else { - force_ipicture = 0; switch (dst_fourcc) { case V4L2_PIX_FMT_H264: quant_param = ctx->params.h264_inter_qp; diff --git a/drivers/media/platform/coda/coda-common.c b/drivers/media/platform/coda/coda-common.c index 4724dfef0643..78bd9a4ace0e 100644 --- a/drivers/media/platform/coda/coda-common.c +++ b/drivers/media/platform/coda/coda-common.c @@ -1700,6 +1700,9 @@ static int coda_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB: ctx->params.intra_refresh = ctrl->val; break; + case V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME: + ctx->params.force_ipicture = true; + break; case V4L2_CID_JPEG_COMPRESSION_QUALITY: coda_set_jpeg_compression_quality(ctx, ctrl->val); break; diff --git a/drivers/media/platform/coda/coda.h b/drivers/media/platform/coda/coda.h index 308116d855e6..76d059431ca1 100644 --- a/drivers/media/platform/coda/coda.h +++ b/drivers/media/platform/coda/coda.h @@ -135,6 +135,7 @@ struct coda_params { u32 vbv_size; u32 slice_max_bits; u32 slice_max_mb; + bool force_ipicture; }; struct coda_buffer_meta { -- cgit v1.2.3-58-ga151 From f903ebf751ddd58ee1a9c765888c0cad63176f4f Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Tue, 6 Jun 2017 12:59:02 -0300 Subject: [media] coda: copy headers in front of every I-frame That way we don't have to rely on userspace to inject the headers on IDR requests, and there is always enough information to start decoding at an I-frame. Signed-off-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/coda/coda-bit.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/media/platform/coda/coda-bit.c b/drivers/media/platform/coda/coda-bit.c index 22e4630f3671..2ec41375a896 100644 --- a/drivers/media/platform/coda/coda-bit.c +++ b/drivers/media/platform/coda/coda-bit.c @@ -1270,10 +1270,10 @@ static int coda_prepare_encode(struct coda_ctx *ctx) coda_set_gdi_regs(ctx); /* - * Copy headers at the beginning of the first frame for H.264 only. - * In MPEG4 they are already copied by the coda. + * Copy headers in front of the first frame and forced I frames for + * H.264 only. In MPEG4 they are already copied by the CODA. */ - if (src_buf->sequence == 0) { + if (src_buf->sequence == 0 || force_ipicture) { pic_stream_buffer_addr = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0) + ctx->vpu_header_size[0] + @@ -1386,7 +1386,8 @@ static void coda_finish_encode(struct coda_ctx *ctx) wr_ptr = coda_read(dev, CODA_REG_BIT_WR_PTR(ctx->reg_idx)); /* Calculate bytesused field */ - if (dst_buf->sequence == 0) { + if (dst_buf->sequence == 0 || + src_buf->flags & V4L2_BUF_FLAG_KEYFRAME) { vb2_set_plane_payload(&dst_buf->vb2_buf, 0, wr_ptr - start_ptr + ctx->vpu_header_size[0] + ctx->vpu_header_size[1] + -- cgit v1.2.3-58-ga151 From c5789f419d28dc4ba629557487b348563a9ea518 Mon Sep 17 00:00:00 2001 From: Minghsiu Tsai Date: Tue, 23 May 2017 00:24:09 -0300 Subject: [media] dt-bindings: mt8173: Fix mdp device tree If the mdp_* nodes are under an mdp sub-node, their corresponding platform device does not automatically get its iommu assigned properly. Fix this by moving the mdp component nodes up a level such that they are siblings of mdp and all other SoC subsystems. This also simplifies the device tree. Although it fixes iommu assignment issue, it also break compatibility with old device tree. So, the patch in driver is needed to iterate over sibling mdp device nodes, not child ones, to keep driver work properly. Signed-off-by: Minghsiu Tsai Acked-by: Rob Herring Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/devicetree/bindings/media/mediatek-mdp.txt | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/Documentation/devicetree/bindings/media/mediatek-mdp.txt b/Documentation/devicetree/bindings/media/mediatek-mdp.txt index 4182063a54db..0d03e3ae2be2 100644 --- a/Documentation/devicetree/bindings/media/mediatek-mdp.txt +++ b/Documentation/devicetree/bindings/media/mediatek-mdp.txt @@ -2,7 +2,7 @@ Media Data Path is used for scaling and color space conversion. -Required properties (controller (parent) node): +Required properties (controller node): - compatible: "mediatek,mt8173-mdp" - mediatek,vpu: the node of video processor unit, see Documentation/devicetree/bindings/media/mediatek-vpu.txt for details. @@ -32,21 +32,16 @@ Required properties (DMA function blocks, child node): for details. Example: -mdp { - compatible = "mediatek,mt8173-mdp"; - #address-cells = <2>; - #size-cells = <2>; - ranges; - mediatek,vpu = <&vpu>; - mdp_rdma0: rdma@14001000 { compatible = "mediatek,mt8173-mdp-rdma"; + "mediatek,mt8173-mdp"; reg = <0 0x14001000 0 0x1000>; clocks = <&mmsys CLK_MM_MDP_RDMA0>, <&mmsys CLK_MM_MUTEX_32K>; power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>; iommus = <&iommu M4U_PORT_MDP_RDMA0>; mediatek,larb = <&larb0>; + mediatek,vpu = <&vpu>; }; mdp_rdma1: rdma@14002000 { @@ -106,4 +101,3 @@ mdp { iommus = <&iommu M4U_PORT_MDP_WROT1>; mediatek,larb = <&larb4>; }; -}; -- cgit v1.2.3-58-ga151 From ba1f1f70c2c08fa71fc6b60f3071e33e8b549660 Mon Sep 17 00:00:00 2001 From: Daniel Kurtz Date: Tue, 23 May 2017 00:24:11 -0300 Subject: [media] media: mtk-mdp: Fix mdp device tree If the mdp_* nodes are under an mdp sub-node, their corresponding platform device does not automatically get its iommu assigned properly. Fix this by moving the mdp component nodes up a level such that they are siblings of mdp and all other SoC subsystems. This also simplifies the device tree. Although it fixes iommu assignment issue, it also break compatibility with old device tree. So, the patch in driver is needed to iterate over sibling mdp device nodes, not child ones, to keep driver work properly. Signed-off-by: Daniel Kurtz Signed-off-by: Minghsiu Tsai Signed-off-by: Matthias Brugger Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/mtk-mdp/mtk_mdp_core.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_core.c b/drivers/media/platform/mtk-mdp/mtk_mdp_core.c index 9e4eb7dcc424..81347558b24a 100644 --- a/drivers/media/platform/mtk-mdp/mtk_mdp_core.c +++ b/drivers/media/platform/mtk-mdp/mtk_mdp_core.c @@ -103,7 +103,7 @@ static int mtk_mdp_probe(struct platform_device *pdev) { struct mtk_mdp_dev *mdp; struct device *dev = &pdev->dev; - struct device_node *node; + struct device_node *node, *parent; int i, ret = 0; mdp = devm_kzalloc(dev, sizeof(*mdp), GFP_KERNEL); @@ -117,8 +117,16 @@ static int mtk_mdp_probe(struct platform_device *pdev) mutex_init(&mdp->lock); mutex_init(&mdp->vpulock); + /* Old dts had the components as child nodes */ + if (of_get_next_child(dev->of_node, NULL)) { + parent = dev->of_node; + dev_warn(dev, "device tree is out of date\n"); + } else { + parent = dev->of_node->parent; + } + /* Iterate over sibling MDP function blocks */ - for_each_child_of_node(dev->of_node, node) { + for_each_child_of_node(parent, node) { const struct of_device_id *of_id; enum mtk_mdp_comp_type comp_type; int comp_id; -- cgit v1.2.3-58-ga151 From a9fad21ab7f0b83f27f53544111cd43a2fbca034 Mon Sep 17 00:00:00 2001 From: Hirokazu Honda Date: Tue, 30 May 2017 06:53:58 -0300 Subject: [media] mtk-vcodec: Show mtk driver error without DEBUG definition A driver error message is shown without DEBUG definition to find an error and debug easily. Signed-off-by: Hirokazu Honda Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/mtk-vcodec/mtk_vcodec_util.h | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_util.h b/drivers/media/platform/mtk-vcodec/mtk_vcodec_util.h index 237e144c194f..06c254f5c171 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_util.h +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_util.h @@ -32,6 +32,15 @@ extern int mtk_v4l2_dbg_level; extern bool mtk_vcodec_dbg; +#define mtk_v4l2_err(fmt, args...) \ + pr_err("[MTK_V4L2][ERROR] %s:%d: " fmt "\n", __func__, __LINE__, \ + ##args) + +#define mtk_vcodec_err(h, fmt, args...) \ + pr_err("[MTK_VCODEC][ERROR][%d]: %s() " fmt "\n", \ + ((struct mtk_vcodec_ctx *)h->ctx)->id, __func__, ##args) + + #if defined(DEBUG) #define mtk_v4l2_debug(level, fmt, args...) \ @@ -41,11 +50,6 @@ extern bool mtk_vcodec_dbg; level, __func__, __LINE__, ##args); \ } while (0) -#define mtk_v4l2_err(fmt, args...) \ - pr_err("[MTK_V4L2][ERROR] %s:%d: " fmt "\n", __func__, __LINE__, \ - ##args) - - #define mtk_v4l2_debug_enter() mtk_v4l2_debug(3, "+") #define mtk_v4l2_debug_leave() mtk_v4l2_debug(3, "-") @@ -57,22 +61,16 @@ extern bool mtk_vcodec_dbg; __func__, ##args); \ } while (0) -#define mtk_vcodec_err(h, fmt, args...) \ - pr_err("[MTK_VCODEC][ERROR][%d]: %s() " fmt "\n", \ - ((struct mtk_vcodec_ctx *)h->ctx)->id, __func__, ##args) - #define mtk_vcodec_debug_enter(h) mtk_vcodec_debug(h, "+") #define mtk_vcodec_debug_leave(h) mtk_vcodec_debug(h, "-") #else #define mtk_v4l2_debug(level, fmt, args...) {} -#define mtk_v4l2_err(fmt, args...) {} #define mtk_v4l2_debug_enter() {} #define mtk_v4l2_debug_leave() {} #define mtk_vcodec_debug(h, fmt, args...) {} -#define mtk_vcodec_err(h, fmt, args...) {} #define mtk_vcodec_debug_enter(h) {} #define mtk_vcodec_debug_leave(h) {} -- cgit v1.2.3-58-ga151 From 74058c596b5007e2186258fccf20e1103cd88f39 Mon Sep 17 00:00:00 2001 From: Christoph Fanelsa Date: Tue, 16 May 2017 17:07:40 -0300 Subject: [media] staging: media: cxd2099: Fix checkpatch issues Fix checkpatch warnings of prefered using '%s..", __func__' as function name in a string Signed-off-by: Christoph Fanelsa Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/cxd2099/cxd2099.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/media/cxd2099/cxd2099.c b/drivers/staging/media/cxd2099/cxd2099.c index 18186d0fa1a6..370ecb959543 100644 --- a/drivers/staging/media/cxd2099/cxd2099.c +++ b/drivers/staging/media/cxd2099/cxd2099.c @@ -473,7 +473,7 @@ static int slot_shutdown(struct dvb_ca_en50221 *ca, int slot) { struct cxd *ci = ca->data; - dev_info(&ci->i2c->dev, "slot_shutdown\n"); + dev_info(&ci->i2c->dev, "%s\n", __func__); mutex_lock(&ci->lock); write_regm(ci, 0x09, 0x08, 0x08); write_regm(ci, 0x20, 0x80, 0x80); /* Reset CAM Mode */ @@ -564,7 +564,7 @@ static int read_data(struct dvb_ca_en50221 *ca, int slot, u8 *ebuf, int ecount) campoll(ci); mutex_unlock(&ci->lock); - dev_info(&ci->i2c->dev, "read_data\n"); + dev_info(&ci->i2c->dev, "%s\n", __func__); if (!ci->dr) return 0; @@ -584,7 +584,7 @@ static int write_data(struct dvb_ca_en50221 *ca, int slot, u8 *ebuf, int ecount) struct cxd *ci = ca->data; mutex_lock(&ci->lock); - dev_info(&ci->i2c->dev, "write_data %d\n", ecount); + dev_info(&ci->i2c->dev, "%s %d\n", __func__, ecount); write_reg(ci, 0x0d, ecount >> 8); write_reg(ci, 0x0e, ecount & 0xff); write_block(ci, 0x11, ebuf, ecount); -- cgit v1.2.3-58-ga151 From e1b28f132675963e06b54ba8e1acf13399325a8d Mon Sep 17 00:00:00 2001 From: Paolo Cretaro Date: Mon, 22 May 2017 18:04:46 -0300 Subject: [media] atomisp: use NULL instead of 0 for pointers Fix warning issued by sparse: Using plain integer as NULL pointer Signed-off-by: Paolo Cretaro Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/atomisp/i2c/ov5693/ov5693.c | 2 +- .../media/atomisp/pci/atomisp2/css2400/runtime/bufq/src/bufq.c | 2 +- .../media/atomisp/pci/atomisp2/css2400/runtime/spctrl/src/spctrl.c | 2 +- drivers/staging/media/atomisp/pci/atomisp2/hmm/hmm.c | 2 +- drivers/staging/media/atomisp/pci/atomisp2/hrt/hive_isp_css_mm_hrt.c | 4 ++-- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/staging/media/atomisp/i2c/ov5693/ov5693.c b/drivers/staging/media/atomisp/i2c/ov5693/ov5693.c index 5e9dafe7cc32..d6447398f5ef 100644 --- a/drivers/staging/media/atomisp/i2c/ov5693/ov5693.c +++ b/drivers/staging/media/atomisp/i2c/ov5693/ov5693.c @@ -706,7 +706,7 @@ static int ov5693_read_otp_reg_array(struct i2c_client *client, u16 size, { u16 index; int ret; - u16 *pVal = 0; + u16 *pVal = NULL; for (index = 0; index <= size; index++) { pVal = (u16 *) (buf + index); diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/bufq/src/bufq.c b/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/bufq/src/bufq.c index ed33d4c4c84a..5d40afd482f5 100644 --- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/bufq/src/bufq.c +++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/bufq/src/bufq.c @@ -239,7 +239,7 @@ static ia_css_queue_t *bufq_get_qhandle( enum sh_css_queue_id id, int thread) { - ia_css_queue_t *q = 0; + ia_css_queue_t *q = NULL; switch (type) { case sh_css_host2sp_buffer_queue: diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/spctrl/src/spctrl.c b/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/spctrl/src/spctrl.c index b36d7b00ebe8..18966d89602a 100644 --- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/spctrl/src/spctrl.c +++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/spctrl/src/spctrl.c @@ -57,7 +57,7 @@ enum ia_css_err ia_css_spctrl_load_fw(sp_ID_t sp_id, hrt_vaddress code_addr = mmgr_NULL; struct ia_css_sp_init_dmem_cfg *init_dmem_cfg; - if ((sp_id >= N_SP_ID) || (spctrl_cfg == 0)) + if ((sp_id >= N_SP_ID) || (spctrl_cfg == NULL)) return IA_CSS_ERR_INVALID_ARGUMENTS; spctrl_cofig_info[sp_id].code_addr = mmgr_NULL; diff --git a/drivers/staging/media/atomisp/pci/atomisp2/hmm/hmm.c b/drivers/staging/media/atomisp/pci/atomisp2/hmm/hmm.c index 57295397da3e..5e63073f3581 100644 --- a/drivers/staging/media/atomisp/pci/atomisp2/hmm/hmm.c +++ b/drivers/staging/media/atomisp/pci/atomisp2/hmm/hmm.c @@ -193,7 +193,7 @@ int hmm_init(void) * at the beginning, to avoid hmm_alloc return 0 in the * further allocation. */ - dummy_ptr = hmm_alloc(1, HMM_BO_PRIVATE, 0, 0, HMM_UNCACHED); + dummy_ptr = hmm_alloc(1, HMM_BO_PRIVATE, 0, NULL, HMM_UNCACHED); if (!ret) { ret = sysfs_create_group(&atomisp_dev->kobj, diff --git a/drivers/staging/media/atomisp/pci/atomisp2/hrt/hive_isp_css_mm_hrt.c b/drivers/staging/media/atomisp/pci/atomisp2/hrt/hive_isp_css_mm_hrt.c index 7dff22f59e29..2e78976bb2ac 100644 --- a/drivers/staging/media/atomisp/pci/atomisp2/hrt/hive_isp_css_mm_hrt.c +++ b/drivers/staging/media/atomisp/pci/atomisp2/hrt/hive_isp_css_mm_hrt.c @@ -55,7 +55,7 @@ static ia_css_ptr __hrt_isp_css_mm_alloc(size_t bytes, void *userptr, if (type == HRT_USR_PTR) { if (userptr == NULL) return hmm_alloc(bytes, HMM_BO_PRIVATE, 0, - 0, cached); + NULL, cached); else { if (num_pages < ((__page_align(bytes)) >> PAGE_SHIFT)) dev_err(atomisp_dev, @@ -94,7 +94,7 @@ ia_css_ptr hrt_isp_css_mm_alloc_user_ptr(size_t bytes, void *userptr, ia_css_ptr hrt_isp_css_mm_alloc_cached(size_t bytes) { if (my_userptr == NULL) - return hmm_alloc(bytes, HMM_BO_PRIVATE, 0, 0, + return hmm_alloc(bytes, HMM_BO_PRIVATE, 0, NULL, HMM_CACHED); else { if (my_num_pages < ((__page_align(bytes)) >> PAGE_SHIFT)) -- cgit v1.2.3-58-ga151 From 4518c3fe7ca7288e6ca8d0cbb977e4c725d0398a Mon Sep 17 00:00:00 2001 From: Juan Antonio Pedreira Martos Date: Tue, 23 May 2017 21:27:11 -0300 Subject: [media] staging: media: atomisp: fix non static symbol warnings Fix a couple of sparse warnings: drivers/staging/media/atomisp/pci/atomisp2/atomisp_v4l2.c:59:14: warning: symbol 'repool_pgnr' was not declared. Should it be static? drivers/staging/media/atomisp/pci/atomisp2/atomisp_v4l2.c:387:6: warning: symbol 'punit_ddr_dvfs_enable' was not declared. Should it be static? Mark these symbols as static, so they are no longer incorrectly exported. Signed-off-by: Juan Antonio Pedreira Martos Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/atomisp/pci/atomisp2/atomisp_v4l2.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/atomisp2/atomisp_v4l2.c b/drivers/staging/media/atomisp/pci/atomisp2/atomisp_v4l2.c index a0478077a012..a543def739fc 100644 --- a/drivers/staging/media/atomisp/pci/atomisp2/atomisp_v4l2.c +++ b/drivers/staging/media/atomisp/pci/atomisp2/atomisp_v4l2.c @@ -56,7 +56,7 @@ module_param(skip_fwload, uint, 0644); MODULE_PARM_DESC(skip_fwload, "Skip atomisp firmware load"); /* set reserved memory pool size in page */ -unsigned int repool_pgnr; +static unsigned int repool_pgnr; module_param(repool_pgnr, uint, 0644); MODULE_PARM_DESC(repool_pgnr, "Set the reserved memory pool size in page (default:0)"); @@ -384,7 +384,7 @@ done: * WA for DDR DVFS enable/disable * By default, ISP will force DDR DVFS 1600MHz before disable DVFS */ -void punit_ddr_dvfs_enable(bool enable) +static void punit_ddr_dvfs_enable(bool enable) { int reg = intel_mid_msgbus_read32(PUNIT_PORT, MRFLD_ISPSSDVFS); int door_bell = 1 << 8; -- cgit v1.2.3-58-ga151 From 5c9f9d602e54433483355b474353ba2e2de8caa3 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Fri, 26 May 2017 12:27:18 -0300 Subject: [media] atompisp: HAS_BL is never defined so lose it Kill off the HAS_BL define and the code and includes it brackets. We never define HAS_BL or use that functionality. Signed-off-by: Alan Cox Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../media/atomisp/pci/atomisp2/css2400/sh_css.c | 101 --------------------- .../atomisp/pci/atomisp2/css2400/sh_css_firmware.c | 19 +--- .../atomisp/pci/atomisp2/css2400/sh_css_internal.h | 7 -- 3 files changed, 1 insertion(+), 126 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css.c b/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css.c index 81a21a0c1391..19dc843ad19e 100644 --- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css.c +++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css.c @@ -98,18 +98,12 @@ static int thread_alive; #include "isp/modes/interface/input_buf.isp.h" -#if defined(HAS_BL) -#include "support/bootloader/interface/ia_css_blctrl.h" -#endif #if defined(HAS_RES_MGR) #include "components/acc_cluster/gen/host/acc_cluster.host.h" #endif /* Name of the sp program: should not be built-in */ #define SP_PROG_NAME "sp" -#if defined(HAS_BL) -#define BL_PROG_NAME "bootloader" -#endif /* Size of Refcount List */ #define REFCOUNT_SIZE 1000 @@ -1571,34 +1565,7 @@ enable_interrupts(enum ia_css_irq_type irq_type) } #endif -#if defined(HAS_BL) -static bool sh_css_setup_blctrl_config(const struct ia_css_fw_info *fw, - const char *program, - ia_css_blctrl_cfg *blctrl_cfg) -{ - if((fw == NULL)||(blctrl_cfg == NULL)) - return false; - blctrl_cfg->bl_entry = 0; - blctrl_cfg->program_name = (char *)(program); - -#if !defined(HRT_UNSCHED) - blctrl_cfg->ddr_data_offset = fw->blob.data_source; - blctrl_cfg->dmem_data_addr = fw->blob.data_target; - blctrl_cfg->dmem_bss_addr = fw->blob.bss_target; - blctrl_cfg->data_size = fw->blob.data_size ; - blctrl_cfg->bss_size = fw->blob.bss_size; - - blctrl_cfg->blctrl_state_dmem_addr = fw->info.bl.sw_state; - blctrl_cfg->blctrl_dma_cmd_list = fw->info.bl.dma_cmd_list; - blctrl_cfg->blctrl_nr_of_dma_cmds = fw->info.bl.num_dma_cmds; - blctrl_cfg->code_size = fw->blob.size; - blctrl_cfg->code = fw->blob.code; - blctrl_cfg->bl_entry = fw->info.bl.bl_entry; /* entry function ptr on Bootloader */ -#endif - return true; -} -#endif static bool sh_css_setup_spctrl_config(const struct ia_css_fw_info *fw, const char * program, ia_css_spctrl_cfg *spctrl_cfg) @@ -1708,9 +1675,6 @@ ia_css_init(const struct ia_css_env *env, { enum ia_css_err err; ia_css_spctrl_cfg spctrl_cfg; -#if defined(HAS_BL) - ia_css_blctrl_cfg blctrl_cfg; -#endif void (*flush_func)(struct ia_css_acc_fw *fw); hrt_data select, enable; @@ -1863,26 +1827,6 @@ ia_css_init(const struct ia_css_env *env, return err; } -#if defined(HAS_BL) - if (!sh_css_setup_blctrl_config(&sh_css_bl_fw, BL_PROG_NAME, &blctrl_cfg)) - return IA_CSS_ERR_INTERNAL_ERROR; - err = ia_css_blctrl_load_fw(&blctrl_cfg); - if (err != IA_CSS_SUCCESS) { - IA_CSS_LEAVE_ERR(err); - return err; - } - -#ifdef ISP2401 - err = ia_css_blctrl_add_target_fw_info(&sh_css_sp_fw, IA_CSS_SP0, - get_sp_code_addr(SP0_ID)); - -#endif - if (err != IA_CSS_SUCCESS) { - IA_CSS_LEAVE_ERR(err); - return err; - } -#endif /* HAS_BL */ - #if WITH_PC_MONITORING if (!thread_alive) { thread_alive++; @@ -2667,9 +2611,6 @@ ia_css_uninit(void) } ia_css_spctrl_unload_fw(SP0_ID); sh_css_sp_set_sp_running(false); -#if defined(HAS_BL) - ia_css_blctrl_unload_fw(); -#endif #if defined(USE_INPUT_SYSTEM_VERSION_2) || defined(USE_INPUT_SYSTEM_VERSION_2401) /* check and free any remaining mipi frames */ free_mipi_frames(NULL); @@ -10588,39 +10529,6 @@ ia_css_pipe_get_isp_pipe_version(const struct ia_css_pipe *pipe) return (unsigned int)pipe->config.isp_pipe_version; } -#if defined(HAS_BL) -#define BL_START_TIMEOUT_US 30000000 -static enum ia_css_err -ia_css_start_bl(void) -{ - enum ia_css_err err = IA_CSS_SUCCESS; - unsigned long timeout; - - IA_CSS_ENTER(""); - sh_css_start_bl(); - /* waiting for the Bootloader to complete execution */ - timeout = BL_START_TIMEOUT_US; - while((ia_css_blctrl_get_state() == BOOTLOADER_BUSY) && timeout) { - timeout--; - hrt_sleep(); - } - ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, - "Bootloader state %d\n", ia_css_blctrl_get_state()); - if (timeout == 0) { - ia_css_debug_dtrace(IA_CSS_DEBUG_ERROR, - "Bootloader Execution Timeout\n"); - err = IA_CSS_ERR_INTERNAL_ERROR; - } - if (ia_css_blctrl_get_state() != BOOTLOADER_OK) { - ia_css_debug_dtrace(IA_CSS_DEBUG_ERROR, - "Bootloader Execution Failed\n"); - err = IA_CSS_ERR_INTERNAL_ERROR; - } - IA_CSS_LEAVE_ERR(err); - return err; -} -#endif - #define SP_START_TIMEOUT_US 30000000 enum ia_css_err @@ -10630,15 +10538,6 @@ ia_css_start_sp(void) enum ia_css_err err = IA_CSS_SUCCESS; IA_CSS_ENTER(""); -#if defined(HAS_BL) - /* Starting bootloader before Sp0 and Sp1 - * and not exposing CSS API */ - err = ia_css_start_bl(); - if (err != IA_CSS_SUCCESS) { - IA_CSS_LEAVE("Bootloader fails"); - return err; - } -#endif sh_css_sp_start_isp(); /* waiting for the SP is completely started */ diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_firmware.c b/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_firmware.c index 34cc56f0b471..57b4fe5e603f 100644 --- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_firmware.c +++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_firmware.c @@ -63,9 +63,6 @@ static const char *release_version = STR(irci_ecr-master_20150911_0724); static char FW_rel_ver_name[MAX_FW_REL_VER_NAME] = "---"; struct ia_css_fw_info sh_css_sp_fw; -#if defined(HAS_BL) -struct ia_css_fw_info sh_css_bl_fw; -#endif /* HAS_BL */ struct ia_css_blob_descr *sh_css_blob_info; /* Only ISP blob info (no SP) */ unsigned sh_css_num_binaries; /* This includes 1 SP binary */ @@ -137,12 +134,7 @@ sh_css_load_blob_info(const char *fw, const struct ia_css_fw_info *bi, struct ia bd->blob = blob; bd->header = *bi; - if ((bi->type == ia_css_isp_firmware) || (bi->type == ia_css_sp_firmware) -#if defined(HAS_BL) - || (bi->type == ia_css_bootloader_firmware) -#endif /* HAS_BL */ - ) - { + if (bi->type == ia_css_isp_firmware || bi->type == ia_css_sp_firmware) { char *namebuffer; int namelength = (int)strlen(name); @@ -279,15 +271,6 @@ sh_css_load_firmware(const char *fw_data, err = setup_binary(bi, fw_data, &sh_css_sp_fw, i); if (err != IA_CSS_SUCCESS) return err; -#if defined(HAS_BL) - } else if (bi->type == ia_css_bootloader_firmware) { - if (i != BOOTLOADER_FIRMWARE) - return IA_CSS_ERR_INTERNAL_ERROR; - err = setup_binary(bi, fw_data, &sh_css_bl_fw, i); - if (err != IA_CSS_SUCCESS) - return err; - IA_CSS_LOG("Bootloader binary recognized\n"); -#endif } else { /* All subsequent binaries (including bootloaders) (i>NUM_OF_SPS+NUM_OF_BLS) are ISP firmware */ if (i < (NUM_OF_SPS + NUM_OF_BLS)) diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_internal.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_internal.h index e2b6f06ed099..5b2b78f96dc5 100644 --- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_internal.h +++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_internal.h @@ -154,18 +154,11 @@ /* Number of SP's */ #define NUM_OF_SPS 1 -#if defined(HAS_BL) -#define NUM_OF_BLS 1 -#else #define NUM_OF_BLS 0 -#endif /* Enum for order of Binaries */ enum sh_css_order_binaries { SP_FIRMWARE = 0, -#if defined(HAS_BL) - BOOTLOADER_FIRMWARE, -#endif ISP_FIRMWARE }; -- cgit v1.2.3-58-ga151 From 2310ae5c12ce5768cc8c14601fec8b256dd01e4e Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Fri, 26 May 2017 12:27:29 -0300 Subject: [media] atomisp: remove NUM_OF_BLS With the removal of the HAS_BL bootloader code the value of NUM_OF_BLS is an invariant zero. So let's get rid of it. Signed-off-by: Alan Cox Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../media/atomisp/pci/atomisp2/css2400/sh_css_firmware.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_firmware.c b/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_firmware.c index 57b4fe5e603f..a179de5212d9 100644 --- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_firmware.c +++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_firmware.c @@ -234,9 +234,9 @@ sh_css_load_firmware(const char *fw_data, sh_css_num_binaries = file_header->binary_nr; /* Only allocate memory for ISP blob info */ - if (sh_css_num_binaries > (NUM_OF_SPS + NUM_OF_BLS)) { + if (sh_css_num_binaries > NUM_OF_SPS) { sh_css_blob_info = kmalloc( - (sh_css_num_binaries - (NUM_OF_SPS + NUM_OF_BLS)) * + (sh_css_num_binaries - NUM_OF_SPS) * sizeof(*sh_css_blob_info), GFP_KERNEL); if (sh_css_blob_info == NULL) return IA_CSS_ERR_CANNOT_ALLOCATE_MEMORY; @@ -272,15 +272,15 @@ sh_css_load_firmware(const char *fw_data, if (err != IA_CSS_SUCCESS) return err; } else { - /* All subsequent binaries (including bootloaders) (i>NUM_OF_SPS+NUM_OF_BLS) are ISP firmware */ - if (i < (NUM_OF_SPS + NUM_OF_BLS)) + /* All subsequent binaries (including bootloaders) (i>NUM_OF_SPS) are ISP firmware */ + if (i < NUM_OF_SPS) return IA_CSS_ERR_INTERNAL_ERROR; if (bi->type != ia_css_isp_firmware) return IA_CSS_ERR_INTERNAL_ERROR; if (sh_css_blob_info == NULL) /* cannot happen but KW does not see this */ return IA_CSS_ERR_INTERNAL_ERROR; - sh_css_blob_info[i-(NUM_OF_SPS + NUM_OF_BLS)] = bd; + sh_css_blob_info[i - NUM_OF_SPS] = bd; } } -- cgit v1.2.3-58-ga151 From f16595ae2048e201365425a337ed5281ce60caf2 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Fri, 26 May 2017 12:27:40 -0300 Subject: [media] atomisp2: remove HRT_UNSCHED HRT_UNSCHED is never defined or set in the driver, so this is dead code that can be retired, simplifying the code a bit further. Signed-off-by: Alan Cox Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../pci/atomisp2/css2400/runtime/debug/src/ia_css_debug.c | 7 ------- .../atomisp/pci/atomisp2/css2400/runtime/spctrl/src/spctrl.c | 8 +------- drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css.c | 10 ++-------- .../media/atomisp/pci/atomisp2/css2400/sh_css_firmware.c | 5 ----- 4 files changed, 3 insertions(+), 27 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/debug/src/ia_css_debug.c b/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/debug/src/ia_css_debug.c index bcc0d464084f..0fa7cb2423d8 100644 --- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/debug/src/ia_css_debug.c +++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/debug/src/ia_css_debug.c @@ -176,7 +176,6 @@ void ia_css_debug_dtrace(unsigned int level, const char *fmt, ...) va_end(ap); } -#if !defined(HRT_UNSCHED) static void debug_dump_long_array_formatted( const sp_ID_t sp_id, hrt_address stack_sp_addr, @@ -249,12 +248,6 @@ void ia_css_debug_dump_sp_stack_info(void) { debug_dump_sp_stack_info(SP0_ID); } -#else -/* Empty def for crun */ -void ia_css_debug_dump_sp_stack_info(void) -{ -} -#endif /* #if !HRT_UNSCHED */ void ia_css_debug_set_dtrace_level(const unsigned int trace_level) diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/spctrl/src/spctrl.c b/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/spctrl/src/spctrl.c index 18966d89602a..d9178e80dab2 100644 --- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/spctrl/src/spctrl.c +++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/spctrl/src/spctrl.c @@ -62,12 +62,6 @@ enum ia_css_err ia_css_spctrl_load_fw(sp_ID_t sp_id, spctrl_cofig_info[sp_id].code_addr = mmgr_NULL; -#if defined(HRT_UNSCHED) - (void)init_dmem_cfg; - code_addr = mmgr_malloc(1); - if (code_addr == mmgr_NULL) - return IA_CSS_ERR_CANNOT_ALLOCATE_MEMORY; -#else init_dmem_cfg = &spctrl_cofig_info[sp_id].dmem_config; init_dmem_cfg->dmem_data_addr = spctrl_cfg->dmem_data_addr; init_dmem_cfg->dmem_bss_addr = spctrl_cfg->dmem_bss_addr; @@ -104,7 +98,7 @@ enum ia_css_err ia_css_spctrl_load_fw(sp_ID_t sp_id, code_addr = mmgr_NULL; return IA_CSS_ERR_INTERNAL_ERROR; } -#endif + spctrl_cofig_info[sp_id].sp_entry = spctrl_cfg->sp_entry; spctrl_cofig_info[sp_id].code_addr = code_addr; spctrl_cofig_info[sp_id].program_name = spctrl_cfg->program_name; diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css.c b/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css.c index 19dc843ad19e..231c3f8b535e 100644 --- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css.c +++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css.c @@ -1575,7 +1575,6 @@ static bool sh_css_setup_spctrl_config(const struct ia_css_fw_info *fw, spctrl_cfg->sp_entry = 0; spctrl_cfg->program_name = (char *)(program); -#if !defined(HRT_UNSCHED) spctrl_cfg->ddr_data_offset = fw->blob.data_source; spctrl_cfg->dmem_data_addr = fw->blob.data_target; spctrl_cfg->dmem_bss_addr = fw->blob.bss_target; @@ -1588,7 +1587,7 @@ static bool sh_css_setup_spctrl_config(const struct ia_css_fw_info *fw, spctrl_cfg->code_size = fw->blob.size; spctrl_cfg->code = fw->blob.code; spctrl_cfg->sp_entry = fw->info.sp.sp_entry; /* entry function ptr on SP */ -#endif + return true; } void @@ -8570,9 +8569,7 @@ remove_firmware(struct ia_css_fw_info **l, struct ia_css_fw_info *firmware) return; /* removing single and multiple firmware is handled in acc_unload_extension() */ } -#if !defined(HRT_UNSCHED) -static enum ia_css_err -upload_isp_code(struct ia_css_fw_info *firmware) +static enum ia_css_err upload_isp_code(struct ia_css_fw_info *firmware) { hrt_vaddress binary; @@ -8600,12 +8597,10 @@ upload_isp_code(struct ia_css_fw_info *firmware) return IA_CSS_ERR_CANNOT_ALLOCATE_MEMORY; return IA_CSS_SUCCESS; } -#endif static enum ia_css_err acc_load_extension(struct ia_css_fw_info *firmware) { -#if !defined(HRT_UNSCHED) enum ia_css_err err; struct ia_css_fw_info *hd = firmware; while (hd){ @@ -8614,7 +8609,6 @@ acc_load_extension(struct ia_css_fw_info *firmware) return err; hd = hd->next; } -#endif if (firmware == NULL) return IA_CSS_ERR_INVALID_ARGUMENTS; diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_firmware.c b/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_firmware.c index a179de5212d9..eecd8cf71951 100644 --- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_firmware.c +++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_firmware.c @@ -92,12 +92,7 @@ setup_binary(struct ia_css_fw_info *fw, const char *fw_data, struct ia_css_fw_in *sh_css_fw = *fw; -#if defined(HRT_UNSCHED) - sh_css_fw->blob.code = vmalloc(1); -#else sh_css_fw->blob.code = vmalloc(fw->blob.size); -#endif - if (sh_css_fw->blob.code == NULL) return IA_CSS_ERR_CANNOT_ALLOCATE_MEMORY; -- cgit v1.2.3-58-ga151 From 94e23b61486c979e92357f9c3a97a277209720ea Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Fri, 26 May 2017 12:27:54 -0300 Subject: [media] atomisp2: tidy up confused ifdefs The two drivers were machine merged and in this case the machine output was to say the least not optimal. Signed-off-by: Alan Cox Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../media/atomisp/pci/atomisp2/css2400/sh_css.c | 26 +++++----------------- 1 file changed, 5 insertions(+), 21 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css.c b/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css.c index 231c3f8b535e..8d44608b39a0 100644 --- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css.c +++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css.c @@ -1469,30 +1469,17 @@ static void start_pipe( copy_ovrd, input_mode, &me->stream->config.metadata_config, -#ifndef ISP2401 &me->stream->info.metadata_info -#else - &me->stream->info.metadata_info, -#endif #if !defined(HAS_NO_INPUT_SYSTEM) -#ifndef ISP2401 - , (input_mode==IA_CSS_INPUT_MODE_MEMORY)? -#else - (input_mode == IA_CSS_INPUT_MODE_MEMORY) ? -#endif + ,(input_mode==IA_CSS_INPUT_MODE_MEMORY) ? (mipi_port_ID_t)0 : -#ifndef ISP2401 me->stream->config.source.port.port -#else - me->stream->config.source.port.port, #endif +#ifdef ISP2401 + ,&me->config.internal_frame_origin_bqs_on_sctbl, + me->stream->isp_params_configs #endif -#ifndef ISP2401 - ); -#else - &me->config.internal_frame_origin_bqs_on_sctbl, - me->stream->isp_params_configs); -#endif + ); if (me->config.mode != IA_CSS_PIPE_MODE_COPY) { struct ia_css_pipeline_stage *stage; @@ -9815,9 +9802,6 @@ ia_css_stream_create(const struct ia_css_stream_config *stream_config, /* take over effective info */ effective_res = curr_pipe->config.input_effective_res; -#endif - -#ifndef ISP2401 err = ia_css_util_check_res( effective_res.width, effective_res.height); -- cgit v1.2.3-58-ga151 From 9dff81436d4d8aa252233bd146d882aaa04094e3 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 26 May 2017 12:28:05 -0300 Subject: [media] atomisp2: off by one in atomisp_s_input() The isp->inputs[] array has isp->input_cnt elements which have been initialized so this > should be >=. This bug is harmless. The check against ATOM_ISP_MAX_INPUTS prevents us from reading beyond the end of the array. The uninitialized elements are zeroed out so we will end up returning -EINVAL a few lines later because the .camera pointer is NULL. Signed-off-by: Dan Carpenter Signed-off-by: Alan Cox Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/atomisp/pci/atomisp2/atomisp_ioctl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/media/atomisp/pci/atomisp2/atomisp_ioctl.c b/drivers/staging/media/atomisp/pci/atomisp2/atomisp_ioctl.c index 6064bb823a47..aa0526ebaff1 100644 --- a/drivers/staging/media/atomisp/pci/atomisp2/atomisp_ioctl.c +++ b/drivers/staging/media/atomisp/pci/atomisp2/atomisp_ioctl.c @@ -683,7 +683,7 @@ static int atomisp_s_input(struct file *file, void *fh, unsigned int input) int ret; rt_mutex_lock(&isp->mutex); - if (input >= ATOM_ISP_MAX_INPUTS || input > isp->input_cnt) { + if (input >= ATOM_ISP_MAX_INPUTS || input >= isp->input_cnt) { dev_dbg(isp->dev, "input_cnt: %d\n", isp->input_cnt); ret = -EINVAL; goto error; -- cgit v1.2.3-58-ga151 From 4a2fcc0c91b3d8b12322f7568eb62ce9f97e7bcf Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Fri, 26 May 2017 12:28:41 -0300 Subject: [media] atomisp: eliminate dead code under HAS_RES_MGR This define is never set and these code paths are never used so they can go away. Signed-off-by: Alan Cox Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../css2400/isp/modes/interface/isp_const.h | 16 ---------- .../css2400/isp/modes/interface/isp_exprs.h | 23 --------------- .../atomisp2/css2400/runtime/binary/src/binary.c | 34 ---------------------- .../media/atomisp/pci/atomisp2/css2400/sh_css.c | 30 ------------------- 4 files changed, 103 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/modes/interface/isp_const.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/modes/interface/isp_const.h index 005eaaa9eb6c..2f215dc2ac32 100644 --- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/modes/interface/isp_const.h +++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/modes/interface/isp_const.h @@ -398,17 +398,6 @@ more details. * so the calc for the output buffer vmem size is: * ((width[vectors]/num_of_stripes) + 2[vectors]) */ -#if defined(HAS_RES_MGR) -#define MAX_VECTORS_PER_OUTPUT_LINE \ - (CEIL_DIV(CEIL_DIV(ISP_MAX_OUTPUT_WIDTH, ISP_NUM_STRIPES) + ISP_LEFT_PADDING, ISP_VEC_NELEMS) + \ - ITERATOR_VECTOR_INCREMENT) - -#define MAX_VECTORS_PER_INPUT_LINE CEIL_DIV(ISP_MAX_INPUT_WIDTH, ISP_VEC_NELEMS) -#define MAX_VECTORS_PER_INPUT_STRIPE (CEIL_ROUND_DIV_STRIPE(CEIL_DIV(ISP_MAX_INPUT_WIDTH, ISP_VEC_NELEMS) , \ - ISP_NUM_STRIPES, \ - ISP_LEFT_PADDING_VECS) + \ - ITERATOR_VECTOR_INCREMENT) -#else /* !defined(HAS_RES_MGR)*/ #define MAX_VECTORS_PER_OUTPUT_LINE \ CEIL_DIV(CEIL_DIV(ISP_MAX_OUTPUT_WIDTH, ISP_NUM_STRIPES) + ISP_LEFT_PADDING, ISP_VEC_NELEMS) @@ -417,7 +406,6 @@ more details. #define MAX_VECTORS_PER_INPUT_STRIPE CEIL_ROUND_DIV_STRIPE(MAX_VECTORS_PER_INPUT_LINE, \ ISP_NUM_STRIPES, \ ISP_LEFT_PADDING_VECS) -#endif /* HAS_RES_MGR */ /* Add 2 for left croppping */ @@ -470,15 +458,11 @@ more details. #define RAW_BUF_LINES ((ENABLE_RAW_BINNING || ENABLE_FIXED_BAYER_DS) ? 4 : 2) -#if defined(HAS_RES_MGR) -#define RAW_BUF_STRIDE (MAX_VECTORS_PER_INPUT_STRIPE) -#else /* !defined(HAS_RES_MGR) */ #define RAW_BUF_STRIDE \ (BINARY_ID == SH_CSS_BINARY_ID_POST_ISP ? MAX_VECTORS_PER_INPUT_CHUNK : \ ISP_NUM_STRIPES > 1 ? MAX_VECTORS_PER_INPUT_STRIPE+_ISP_EXTRA_PADDING_VECS : \ !ENABLE_CONTINUOUS ? MAX_VECTORS_PER_INPUT_LINE : \ MAX_VECTORS_PER_INPUT_CHUNK) -#endif /* HAS_RES_MGR */ /* [isp vmem] table size[vectors] per line per color (GR,R,B,GB), multiples of NWAY */ diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/modes/interface/isp_exprs.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/modes/interface/isp_exprs.h index 8b59a8caec52..e625ba62cc15 100644 --- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/modes/interface/isp_exprs.h +++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/modes/interface/isp_exprs.h @@ -214,24 +214,6 @@ more details. /******* STRIPING-RELATED MACROS *******/ #define NO_STRIPING (ISP_NUM_STRIPES == 1) -#if defined(HAS_RES_MGR) - -#define ISP_OUTPUT_CHUNK_VECS ISP_INTERNAL_WIDTH_VECS - -#if defined(__ISP) -#define VECTORS_PER_LINE ISP_INTERNAL_WIDTH_VECS -#else -#define VECTORS_PER_LINE \ - (NO_STRIPING ? ISP_INTERNAL_WIDTH_VECS \ - : ISP_IO_STRIPE_WIDTH_VECS(ISP_INTERNAL_WIDTH_VECS, ISP_LEFT_PADDING_VECS, ISP_NUM_STRIPES, ISP_MIN_STRIPE_WIDTH) ) -#endif - -#define VECTORS_PER_INPUT_LINE \ - (NO_STRIPING ? ISP_INPUT_WIDTH_VECS \ - : ISP_IO_STRIPE_WIDTH_VECS(ISP_INPUT_WIDTH_VECS, ISP_LEFT_PADDING_VECS, ISP_NUM_STRIPES, ISP_MIN_STRIPE_WIDTH) ) - -#else - #define ISP_OUTPUT_CHUNK_VECS \ (NO_STRIPING ? CEIL_DIV_CHUNKS(ISP_OUTPUT_VECS_EXTRA_CROP, OUTPUT_NUM_CHUNKS) \ : ISP_IO_STRIPE_WIDTH_VECS(ISP_OUTPUT_VECS_EXTRA_CROP, ISP_LEFT_PADDING_VECS, ISP_NUM_STRIPES, ISP_MIN_STRIPE_WIDTH) ) @@ -244,7 +226,6 @@ more details. (NO_STRIPING ? ISP_INPUT_WIDTH_VECS \ : ISP_IO_STRIPE_WIDTH_VECS(ISP_INPUT_WIDTH_VECS, ISP_LEFT_PADDING_VECS, ISP_NUM_STRIPES, ISP_MIN_STRIPE_WIDTH)+_ISP_EXTRA_PADDING_VECS) -#endif #define ISP_MAX_VF_OUTPUT_STRIPE_VECS \ (NO_STRIPING ? ISP_MAX_VF_OUTPUT_VECS \ @@ -282,11 +263,7 @@ more details. #define OUTPUT_VECTORS_PER_CHUNK CEIL_DIV_CHUNKS(VECTORS_PER_LINE,OUTPUT_NUM_CHUNKS) /* should be even?? */ -#if !defined(HAS_RES_MGR) #define OUTPUT_C_VECTORS_PER_CHUNK CEIL_DIV(OUTPUT_VECTORS_PER_CHUNK, 2) -#else -#define OUTPUT_C_VECTORS_PER_CHUNK CEIL_DIV(MAX_VECTORS_PER_CHUNK, 2) -#endif #ifndef ISP2401 /**** SCTBL defs *******/ diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/binary/src/binary.c b/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/binary/src/binary.c index ae0b229c9fb8..9f8a125f0d74 100644 --- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/binary/src/binary.c +++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/binary/src/binary.c @@ -36,10 +36,6 @@ #endif #include "camera/pipe/interface/ia_css_pipe_binarydesc.h" -#if defined(HAS_RES_MGR) -#include -#include -#endif #include "memory_access.h" @@ -110,10 +106,6 @@ ia_css_binary_internal_res(const struct ia_css_frame_info *in_info, internal_res->height = __ISP_INTERNAL_HEIGHT(isp_tmp_internal_height, info->pipeline.top_cropping, binary_dvs_env.height); -#if defined(HAS_RES_MGR) - internal_res->height = (bds_out_info == NULL) ? internal_res->height : bds_out_info->res.height; - internal_res->width = (bds_out_info == NULL) ? internal_res->width: bds_out_info->res.width; -#endif } #ifndef ISP2401 @@ -787,25 +779,6 @@ ia_css_binary_dvs_stat_grid_info( struct ia_css_grid_info *info, struct ia_css_pipe *pipe) { -#if defined(HAS_RES_MGR) - struct ia_css_dvs_stat_grid_info *dvs_stat_info; - unsigned int i; - - assert(binary != NULL); - assert(info != NULL); - dvs_stat_info = &info->dvs_grid.dvs_stat_grid_info; - - if (binary->info->sp.enable.dvs_stats) { - for (i = 0; i < IA_CSS_SKC_DVS_STAT_NUM_OF_LEVELS; i++) { - dvs_stat_info->grd_cfg[i].grd_start.enable = 1; - } - ia_css_dvs_stat_grid_calculate(pipe, dvs_stat_info); - } - else { - memset(dvs_stat_info, 0, sizeof(struct ia_css_dvs_stat_grid_info)); - } - -#endif (void)pipe; sh_css_binary_common_grid_info(binary, info); return; @@ -1087,9 +1060,6 @@ binary_in_frame_padded_width(int in_frame_width, #else /* in other cases, the left padding pixels are always 128 */ nr_of_left_paddings = 2*ISP_VEC_NELEMS; -#endif -#if defined(HAS_RES_MGR) - (void)dvs_env_width; #endif if (need_scaling) { /* In SDV use-case, we need to match left-padding of @@ -1101,9 +1071,7 @@ binary_in_frame_padded_width(int in_frame_width, 2*ISP_VEC_NELEMS); } else { /* Different than before, we do left&right padding. */ -#if !defined(HAS_RES_MGR) /* dvs env is included already */ in_frame_width += dvs_env_width; -#endif rval = CEIL_MUL(in_frame_width + (left_cropping ? nr_of_left_paddings : 0), @@ -1214,10 +1182,8 @@ ia_css_binary_fill_info(const struct ia_css_binary_xinfo *xinfo, binary->in_frame_info.res.width = in_info->res.width + info->pipeline.left_cropping; binary->in_frame_info.res.height = in_info->res.height + info->pipeline.top_cropping; -#if !defined(HAS_RES_MGR) /* dvs env is included already */ binary->in_frame_info.res.width += dvs_env_width; binary->in_frame_info.res.height += dvs_env_height; -#endif binary->in_frame_info.padded_width = binary_in_frame_padded_width(in_info->res.width, diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css.c b/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css.c index 8d44608b39a0..4f3a2ea725ac 100644 --- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css.c +++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css.c @@ -98,10 +98,6 @@ static int thread_alive; #include "isp/modes/interface/input_buf.isp.h" -#if defined(HAS_RES_MGR) -#include "components/acc_cluster/gen/host/acc_cluster.host.h" -#endif - /* Name of the sp program: should not be built-in */ #define SP_PROG_NAME "sp" /* Size of Refcount List */ @@ -6243,9 +6239,6 @@ static enum ia_css_err load_primary_binaries( capt_ldc_out_info; #else *pipe_vf_out_info; -#endif -#if defined(HAS_RES_MGR) - struct ia_css_frame_info bds_out_info; #endif enum ia_css_err err = IA_CSS_SUCCESS; struct ia_css_capture_settings *mycs; @@ -6368,10 +6361,6 @@ static enum ia_css_err load_primary_binaries( &cas_scaler_descr.out_info[i], &cas_scaler_descr.internal_out_info[i], &cas_scaler_descr.vf_info[i]); -#if defined(HAS_RES_MGR) - bds_out_info.res = pipe->config.bayer_ds_out_res; - yuv_scaler_descr.bds_out_info = &bds_out_info; -#endif err = ia_css_binary_find(&yuv_scaler_descr, &mycs->yuv_scaler_binary[i]); if (err != IA_CSS_SUCCESS) { @@ -6422,10 +6411,6 @@ static enum ia_css_err load_primary_binaries( &capture_pp_descr, &prim_out_info, #endif &capt_pp_out_info, &vf_info); -#if defined(HAS_RES_MGR) - bds_out_info.res = pipe->config.bayer_ds_out_res; - capture_pp_descr.bds_out_info = &bds_out_info; -#endif err = ia_css_binary_find(&capture_pp_descr, &mycs->capture_pp_binary); if (err != IA_CSS_SUCCESS) { @@ -6461,10 +6446,6 @@ static enum ia_css_err load_primary_binaries( if (pipe->enable_viewfinder[IA_CSS_PIPE_OUTPUT_STAGE_0] && (i == mycs->num_primary_stage - 1)) local_vf_info = &vf_info; ia_css_pipe_get_primary_binarydesc(pipe, &prim_descr[i], &prim_in_info, &prim_out_info, local_vf_info, i); -#if defined(HAS_RES_MGR) - bds_out_info.res = pipe->config.bayer_ds_out_res; - prim_descr[i].bds_out_info = &bds_out_info; -#endif err = ia_css_binary_find(&prim_descr[i], &mycs->primary_binary[i]); if (err != IA_CSS_SUCCESS) { IA_CSS_LEAVE_ERR_PRIVATE(err); @@ -6498,10 +6479,6 @@ static enum ia_css_err load_primary_binaries( ia_css_pipe_get_vfpp_binarydesc(pipe, &vf_pp_descr, vf_pp_in_info, pipe_vf_out_info); -#if defined(HAS_RES_MGR) - bds_out_info.res = pipe->config.bayer_ds_out_res; - vf_pp_descr.bds_out_info = &bds_out_info; -#endif err = ia_css_binary_find(&vf_pp_descr, &mycs->vf_pp_binary); if (err != IA_CSS_SUCCESS) { IA_CSS_LEAVE_ERR_PRIVATE(err); @@ -9822,13 +9799,6 @@ ia_css_stream_create(const struct ia_css_stream_config *stream_config, if (err != IA_CSS_SUCCESS) goto ERR; -#if defined(HAS_RES_MGR) - /* update acc configuration - striping info is ready */ - err = ia_css_update_cfg_stripe_info(curr_pipe); - if (err != IA_CSS_SUCCESS) - goto ERR; -#endif - /* handle each pipe */ pipe_info = &curr_pipe->info; for (j = 0; j < IA_CSS_PIPE_MAX_OUTPUT_STAGE; j++) { -- cgit v1.2.3-58-ga151 From 0ef9e6e5559306079200742ee40f5754672dd498 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Fri, 26 May 2017 12:28:55 -0300 Subject: [media] atomisp: unify sh_css_hmm_buffer_record_acquire The ISP2401 version of this function returns a pointer to the buffer, whilst the ISP2400 version returns a boolean if a slot is found. We can trivially unify the code to use the ISP2401 version. Signed-off-by: Alan Cox Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../media/atomisp/pci/atomisp2/css2400/sh_css.c | 40 ++-------------------- 1 file changed, 3 insertions(+), 37 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css.c b/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css.c index 4f3a2ea725ac..8e1cd1258f08 100644 --- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css.c +++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css.c @@ -375,13 +375,8 @@ sh_css_hmm_buffer_record_uninit(void); static void sh_css_hmm_buffer_record_reset(struct sh_css_hmm_buffer_record *buffer_record); -#ifndef ISP2401 -static bool -sh_css_hmm_buffer_record_acquire(struct ia_css_rmgr_vbuf_handle *h_vbuf, -#else static struct sh_css_hmm_buffer_record *sh_css_hmm_buffer_record_acquire(struct ia_css_rmgr_vbuf_handle *h_vbuf, -#endif enum ia_css_buffer_type type, hrt_address kernel_ptr); @@ -4423,21 +4418,9 @@ ia_css_pipe_enqueue_buffer(struct ia_css_pipe *pipe, } if (return_err == IA_CSS_SUCCESS) { -#ifndef ISP2401 - bool found_record = false; - found_record = sh_css_hmm_buffer_record_acquire( -#else - struct sh_css_hmm_buffer_record *hmm_buffer_record = NULL; - - hmm_buffer_record = sh_css_hmm_buffer_record_acquire( -#endif - h_vbuf, buf_type, - HOST_ADDRESS(ddr_buffer.kernel_ptr)); -#ifndef ISP2401 - if (found_record == true) { -#else - if (hmm_buffer_record) { -#endif + if (sh_css_hmm_buffer_record_acquire( + h_vbuf, buf_type, + HOST_ADDRESS(ddr_buffer.kernel_ptr))) { IA_CSS_LOG("send vbuf=%p", h_vbuf); } else { return_err = IA_CSS_ERR_INTERNAL_ERROR; @@ -11139,23 +11122,14 @@ sh_css_hmm_buffer_record_reset(struct sh_css_hmm_buffer_record *buffer_record) buffer_record->kernel_ptr = 0; } -#ifndef ISP2401 -static bool -sh_css_hmm_buffer_record_acquire(struct ia_css_rmgr_vbuf_handle *h_vbuf, -#else static struct sh_css_hmm_buffer_record *sh_css_hmm_buffer_record_acquire(struct ia_css_rmgr_vbuf_handle *h_vbuf, -#endif enum ia_css_buffer_type type, hrt_address kernel_ptr) { int i; struct sh_css_hmm_buffer_record *buffer_record = NULL; -#ifndef ISP2401 - bool found_record = false; -#else struct sh_css_hmm_buffer_record *out_buffer_record = NULL; -#endif assert(h_vbuf != NULL); assert((type > IA_CSS_BUFFER_TYPE_INVALID) && (type < IA_CSS_NUM_DYNAMIC_BUFFER_TYPE)); @@ -11168,21 +11142,13 @@ static struct sh_css_hmm_buffer_record buffer_record->type = type; buffer_record->h_vbuf = h_vbuf; buffer_record->kernel_ptr = kernel_ptr; -#ifndef ISP2401 - found_record = true; -#else out_buffer_record = buffer_record; -#endif break; } buffer_record++; } -#ifndef ISP2401 - return found_record; -#else return out_buffer_record; -#endif } static struct sh_css_hmm_buffer_record -- cgit v1.2.3-58-ga151 From b1c056e05b25ba7bf58c9c4f82a767dcfa052f43 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Fri, 26 May 2017 12:29:15 -0300 Subject: [media] atomisp: Unify load_preview_binaries for the most part ISP2401 introduced a rather sensible change to cut through the structure spaghetti. Adopt that for the ISP2400 as well. It makes no difference to the actual code other than readability. Signed-off-by: Alan Cox Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../media/atomisp/pci/atomisp2/css2400/sh_css.c | 40 +--------------------- 1 file changed, 1 insertion(+), 39 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css.c b/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css.c index 8e1cd1258f08..55d2a69f4e92 100644 --- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css.c +++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css.c @@ -2923,11 +2923,8 @@ load_preview_binaries(struct ia_css_pipe *pipe) #endif /* preview only have 1 output pin now */ struct ia_css_frame_info *pipe_out_info = &pipe->output_info[0]; -#ifdef ISP2401 struct ia_css_preview_settings *mycs = &pipe->pipe_settings.preview; -#endif - IA_CSS_ENTER_PRIVATE(""); assert(pipe != NULL); assert(pipe->stream != NULL); @@ -2939,11 +2936,7 @@ load_preview_binaries(struct ia_css_pipe *pipe) sensor = pipe->stream->config.mode == IA_CSS_INPUT_MODE_SENSOR; #endif -#ifndef ISP2401 - if (pipe->pipe_settings.preview.preview_binary.info) -#else if (mycs->preview_binary.info) -#endif return IA_CSS_SUCCESS; err = ia_css_util_check_input(&pipe->stream->config, false, false); @@ -2996,12 +2989,7 @@ load_preview_binaries(struct ia_css_pipe *pipe) &prev_vf_info); if (err != IA_CSS_SUCCESS) return err; - err = ia_css_binary_find(&preview_descr, -#ifndef ISP2401 - &pipe->pipe_settings.preview.preview_binary); -#else - &mycs->preview_binary); -#endif + err = ia_css_binary_find(&preview_descr, &mycs->preview_binary); if (err != IA_CSS_SUCCESS) return err; @@ -3017,24 +3005,15 @@ load_preview_binaries(struct ia_css_pipe *pipe) #endif /* The vf_pp binary is needed when (further) YUV downscaling is required */ -#ifndef ISP2401 - need_vf_pp |= pipe->pipe_settings.preview.preview_binary.out_frame_info[0].res.width != pipe_out_info->res.width; - need_vf_pp |= pipe->pipe_settings.preview.preview_binary.out_frame_info[0].res.height != pipe_out_info->res.height; -#else need_vf_pp |= mycs->preview_binary.out_frame_info[0].res.width != pipe_out_info->res.width; need_vf_pp |= mycs->preview_binary.out_frame_info[0].res.height != pipe_out_info->res.height; -#endif /* When vf_pp is needed, then the output format of the selected * preview binary must be yuv_line. If this is not the case, * then the preview binary selection is done again. */ if (need_vf_pp && -#ifndef ISP2401 - (pipe->pipe_settings.preview.preview_binary.out_frame_info[0].format != IA_CSS_FRAME_FORMAT_YUV_LINE)) { -#else (mycs->preview_binary.out_frame_info[0].format != IA_CSS_FRAME_FORMAT_YUV_LINE)) { -#endif /* Preview step 2 */ if (pipe->vf_yuv_ds_input_info.res.width) @@ -3055,11 +3034,7 @@ load_preview_binaries(struct ia_css_pipe *pipe) if (err != IA_CSS_SUCCESS) return err; err = ia_css_binary_find(&preview_descr, -#ifndef ISP2401 - &pipe->pipe_settings.preview.preview_binary); -#else &mycs->preview_binary); -#endif if (err != IA_CSS_SUCCESS) return err; } @@ -3069,18 +3044,10 @@ load_preview_binaries(struct ia_css_pipe *pipe) /* Viewfinder post-processing */ ia_css_pipe_get_vfpp_binarydesc(pipe, &vf_pp_descr, -#ifndef ISP2401 - &pipe->pipe_settings.preview.preview_binary.out_frame_info[0], -#else &mycs->preview_binary.out_frame_info[0], -#endif pipe_out_info); err = ia_css_binary_find(&vf_pp_descr, -#ifndef ISP2401 - &pipe->pipe_settings.preview.vf_pp_binary); -#else &mycs->vf_pp_binary); -#endif if (err != IA_CSS_SUCCESS) return err; } @@ -3106,13 +3073,8 @@ load_preview_binaries(struct ia_css_pipe *pipe) /* Copy */ if (need_isp_copy_binary) { err = load_copy_binary(pipe, -#ifndef ISP2401 - &pipe->pipe_settings.preview.copy_binary, - &pipe->pipe_settings.preview.preview_binary); -#else &mycs->copy_binary, &mycs->preview_binary); -#endif if (err != IA_CSS_SUCCESS) return err; } -- cgit v1.2.3-58-ga151 From 29a323ea90c900aaa9794e567b8d616580ac82ec Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Fri, 26 May 2017 12:29:25 -0300 Subject: [media] atomisp: Unify lut free logic ISP2401 introduced a helper for this which we can use just as well on the ISP2400 and remove some more noise differences. Signed-off-by: Alan Cox Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../staging/media/atomisp/pci/atomisp2/css2400/sh_css.c | 7 ------- .../media/atomisp/pci/atomisp2/css2400/sh_css_params.c | 14 -------------- 2 files changed, 21 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css.c b/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css.c index 55d2a69f4e92..dfef219c4bef 100644 --- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css.c +++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css.c @@ -2533,15 +2533,8 @@ ia_css_pipe_destroy(struct ia_css_pipe *pipe) break; } -#ifndef ISP2401 - if (pipe->scaler_pp_lut != mmgr_NULL) { - hmm_free(pipe->scaler_pp_lut); - pipe->scaler_pp_lut = mmgr_NULL; - } -#else sh_css_params_free_gdc_lut(pipe->scaler_pp_lut); pipe->scaler_pp_lut = mmgr_NULL; -#endif my_css.active_pipes[ia_css_pipe_get_pipe_num(pipe)] = NULL; sh_css_pipe_free_shading_table(pipe); diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_params.c b/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_params.c index d8c22e8cd3af..48224370b8bf 100644 --- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_params.c +++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_params.c @@ -3356,15 +3356,8 @@ enum ia_css_err ia_css_pipe_set_bci_scaler_lut(struct ia_css_pipe *pipe, } /* Free any existing tables. */ -#ifndef ISP2401 - if (pipe->scaler_pp_lut != mmgr_NULL) { - hmm_free(pipe->scaler_pp_lut); - pipe->scaler_pp_lut = mmgr_NULL; - } -#else sh_css_params_free_gdc_lut(pipe->scaler_pp_lut); pipe->scaler_pp_lut = mmgr_NULL; -#endif #ifndef ISP2401 if (store) { @@ -3445,15 +3438,8 @@ void sh_css_params_free_default_gdc_lut(void) { IA_CSS_ENTER_PRIVATE("void"); -#ifndef ISP2401 - if (default_gdc_lut != mmgr_NULL) { - hmm_free(default_gdc_lut); - default_gdc_lut = mmgr_NULL; - } -#else sh_css_params_free_gdc_lut(default_gdc_lut); default_gdc_lut = mmgr_NULL; -#endif IA_CSS_LEAVE_PRIVATE("void"); -- cgit v1.2.3-58-ga151 From 4950cfecd109735390c058e18adc5bd5bc38847e Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Fri, 26 May 2017 12:29:36 -0300 Subject: [media] atomisp: remove sh_css_irq - it contains nothing We won't be adding abstractions or moving them here so kill it. Signed-off-by: Alan Cox Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/atomisp/pci/atomisp2/Makefile | 1 - .../media/atomisp/pci/atomisp2/css2400/sh_css_irq.c | 16 ---------------- 2 files changed, 17 deletions(-) delete mode 100644 drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_irq.c diff --git a/drivers/staging/media/atomisp/pci/atomisp2/Makefile b/drivers/staging/media/atomisp/pci/atomisp2/Makefile index 68a9ab1c3b61..726eaa293c55 100644 --- a/drivers/staging/media/atomisp/pci/atomisp2/Makefile +++ b/drivers/staging/media/atomisp/pci/atomisp2/Makefile @@ -108,7 +108,6 @@ atomisp-objs += \ css2400/sh_css_metadata.o \ css2400/base/refcount/src/refcount.o \ css2400/base/circbuf/src/circbuf.o \ - css2400/sh_css_irq.o \ css2400/camera/pipe/src/pipe_binarydesc.o \ css2400/camera/pipe/src/pipe_util.o \ css2400/camera/pipe/src/pipe_stagedesc.o \ diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_irq.c b/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_irq.c deleted file mode 100644 index 37e954aea36f..000000000000 --- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_irq.c +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Support for Intel Camera Imaging ISP subsystem. - * Copyright (c) 2015, Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - */ - -/* This file will contain the code to implement the functions declared in ia_css_irq.h - and associated helper functions */ -- cgit v1.2.3-58-ga151 From fc993494134bfa76084529dda99ea8fcf23bec73 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Fri, 26 May 2017 12:29:48 -0300 Subject: [media] atomisp: de-duplicate sh_css_mmu_set_page_table_base_index Between the ISP2400 and ISP2401 code base this function moved file. The merge of the drivers left us with two version in ifdefs. Resolve this down to a single copy. Signed-off-by: Alan Cox Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../pci/atomisp2/css2400/ia_css_mmu_private.h | 2 -- .../media/atomisp/pci/atomisp2/css2400/sh_css.c | 24 +--------------------- .../atomisp/pci/atomisp2/css2400/sh_css_mmu.c | 6 ------ 3 files changed, 1 insertion(+), 31 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/ia_css_mmu_private.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/ia_css_mmu_private.h index 7c8500903b5c..1021e4f380a5 100644 --- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/ia_css_mmu_private.h +++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/ia_css_mmu_private.h @@ -1,4 +1,3 @@ -#ifdef ISP2401 /* * Support for Intel Camera Imaging ISP subsystem. * Copyright (c) 2015, Intel Corporation. @@ -28,4 +27,3 @@ void sh_css_mmu_set_page_table_base_index(hrt_data base_index); #endif /* __IA_CSS_MMU_PRIVATE_H */ -#endif diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css.c b/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css.c index dfef219c4bef..471f2be974e2 100644 --- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css.c +++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css.c @@ -64,7 +64,7 @@ #include "input_system.h" #endif #include "mmu_device.h" /* mmu_set_page_table_base_index(), ... */ -//#include "ia_css_mmu_private.h" /* sh_css_mmu_set_page_table_base_index() */ +#include "ia_css_mmu_private.h" /* sh_css_mmu_set_page_table_base_index() */ #include "gdc_device.h" /* HRT_GDC_N */ #include "dma.h" /* dma_set_max_burst_size() */ #include "irq.h" /* virq */ @@ -242,11 +242,6 @@ ia_css_reset_defaults(struct sh_css* css); static void sh_css_init_host_sp_control_vars(void); -#ifndef ISP2401 -static void -sh_css_mmu_set_page_table_base_index(hrt_data base_index); - -#endif static enum ia_css_err set_num_primary_stages(unsigned int *num, enum ia_css_pipe_version version); static bool @@ -2595,23 +2590,6 @@ ia_css_uninit(void) ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, "ia_css_uninit() leave: return_void\n"); } -#ifndef ISP2401 -/* Deprecated, this is an HRT backend function (memory_access.h) */ -static void -sh_css_mmu_set_page_table_base_index(hrt_data base_index) -{ - int i; - ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE_PRIVATE, "sh_css_mmu_set_page_table_base_index() enter: base_index=0x%08x\n",base_index); - my_css.page_table_base_index = base_index; - for (i = 0; i < (int)N_MMU_ID; i++) { - mmu_ID_t mmu_id = (mmu_ID_t)i; - mmu_set_page_table_base_index(mmu_id, base_index); - mmu_invalidate_cache(mmu_id); - } - ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE_PRIVATE, "sh_css_mmu_set_page_table_base_index() leave: return_void\n"); -} - -#endif #if defined(HAS_IRQ_MAP_VERSION_2) enum ia_css_err ia_css_irq_translate( unsigned int *irq_infos) diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_mmu.c b/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_mmu.c index 6de8472f1b07..237e38b2f0c1 100644 --- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_mmu.c +++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_mmu.c @@ -13,16 +13,12 @@ */ #include "ia_css_mmu.h" -#ifdef ISP2401 #include "ia_css_mmu_private.h" -#endif #include #include "sh_css_sp.h" #include "sh_css_firmware.h" #include "sp.h" -#ifdef ISP2401 #include "mmu_device.h" -#endif void ia_css_mmu_invalidate_cache(void) @@ -44,7 +40,6 @@ ia_css_mmu_invalidate_cache(void) } ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, "ia_css_mmu_invalidate_cache() leave\n"); } -#ifdef ISP2401 /* Deprecated, this is an HRT backend function (memory_access.h) */ void @@ -59,4 +54,3 @@ sh_css_mmu_set_page_table_base_index(hrt_data base_index) } IA_CSS_LEAVE_PRIVATE(""); } -#endif -- cgit v1.2.3-58-ga151 From 008d8e10054b3548cf9781083c26809486d7c07c Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 28 May 2017 09:31:47 -0300 Subject: [media] staging: atomisp: Fix calling efivar_entry_get() with unaligned arguments efivar_entry_get has certain alignment requirements and the atomisp platform code was not honoring these, causing an oops by triggering the WARN_ON in arch/x86/platform/efi/efi_64.c: virt_to_phys_or_null_size(). This commit fixes this by using the members of the efivar struct embedded in the efivar_entry struct we kzalloc as arguments to efivar_entry_get(), which is how all the other callers of efivar_entry_get() do this. Signed-off-by: Hans de Goede Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../atomisp/platform/intel-mid/atomisp_gmin_platform.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/drivers/staging/media/atomisp/platform/intel-mid/atomisp_gmin_platform.c b/drivers/staging/media/atomisp/platform/intel-mid/atomisp_gmin_platform.c index d68e9cf33aa7..fe996d0cb71c 100644 --- a/drivers/staging/media/atomisp/platform/intel-mid/atomisp_gmin_platform.c +++ b/drivers/staging/media/atomisp/platform/intel-mid/atomisp_gmin_platform.c @@ -635,9 +635,7 @@ int gmin_get_config_var(struct device *dev, const char *var, char *out, char var8[CFG_VAR_NAME_MAX]; efi_char16_t var16[CFG_VAR_NAME_MAX]; struct efivar_entry *ev; - u32 efiattr_dummy; int i, j, ret; - unsigned long efilen; if (dev && ACPI_COMPANION(dev)) dev = &ACPI_COMPANION(dev)->dev; @@ -699,15 +697,18 @@ int gmin_get_config_var(struct device *dev, const char *var, char *out, return -ENOMEM; memcpy(&ev->var.VariableName, var16, sizeof(var16)); ev->var.VendorGuid = GMIN_CFG_VAR_EFI_GUID; + ev->var.DataSize = *out_len; - efilen = *out_len; - ret = efivar_entry_get(ev, &efiattr_dummy, &efilen, out); + ret = efivar_entry_get(ev, &ev->var.Attributes, + &ev->var.DataSize, ev->var.Data); + if (ret == 0) { + memcpy(out, ev->var.Data, ev->var.DataSize); + *out_len = ev->var.DataSize; + } else { + dev_warn(dev, "Failed to find gmin variable %s\n", var8); + } kfree(ev); - *out_len = efilen; - - if (ret) - dev_warn(dev, "Failed to find gmin variable %s\n", var8); return ret; } -- cgit v1.2.3-58-ga151 From 797f91e6417abcc9a07f9c99dd9128ae7622ee81 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 28 May 2017 09:31:48 -0300 Subject: [media] staging: atomisp: Do not call dev_warn with a NULL device Do not call dev_warn with a NULL device, this silence the following 2 warnings: [ 14.392194] (NULL device *): Failed to find gmin variable gmin_V2P8GPIO [ 14.392257] (NULL device *): Failed to find gmin variable gmin_V1P8GPIO We could switch to using pr_warn for dev == NULL instead, but as comments in the source indicate, the check for these 2 special gmin variables with a NULL device is a workaround for 2 specific evaluation boards, so completely silencing the missing warning for these actually is a good thing. Signed-off-by: Hans de Goede Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../staging/media/atomisp/platform/intel-mid/atomisp_gmin_platform.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/media/atomisp/platform/intel-mid/atomisp_gmin_platform.c b/drivers/staging/media/atomisp/platform/intel-mid/atomisp_gmin_platform.c index fe996d0cb71c..edaae93af8f9 100644 --- a/drivers/staging/media/atomisp/platform/intel-mid/atomisp_gmin_platform.c +++ b/drivers/staging/media/atomisp/platform/intel-mid/atomisp_gmin_platform.c @@ -704,7 +704,7 @@ int gmin_get_config_var(struct device *dev, const char *var, char *out, if (ret == 0) { memcpy(out, ev->var.Data, ev->var.DataSize); *out_len = ev->var.DataSize; - } else { + } else if (dev) { dev_warn(dev, "Failed to find gmin variable %s\n", var8); } -- cgit v1.2.3-58-ga151 From 42c6d864acaede779a4e44f8ce8d23c1012a1e64 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 28 May 2017 09:31:49 -0300 Subject: [media] staging: atomisp: Set step to 0 for mt9m114 menu control menu controls are not allowed to have a step size, set step to 0 to fix an oops from the WARN_ON in v4l2_ctrl_new_custom() triggering because of this. Signed-off-by: Hans de Goede Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/atomisp/i2c/mt9m114.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/media/atomisp/i2c/mt9m114.c b/drivers/staging/media/atomisp/i2c/mt9m114.c index ced175c268d1..3fa915313e53 100644 --- a/drivers/staging/media/atomisp/i2c/mt9m114.c +++ b/drivers/staging/media/atomisp/i2c/mt9m114.c @@ -1499,7 +1499,7 @@ static struct v4l2_ctrl_config mt9m114_controls[] = { .type = V4L2_CTRL_TYPE_MENU, .min = 0, .max = 3, - .step = 1, + .step = 0, .def = 1, .flags = 0, }, -- cgit v1.2.3-58-ga151 From 6706e9008dc6df4093deff12f93a1072708e0aef Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 28 May 2017 09:31:50 -0300 Subject: [media] staging: atomisp: Add INT0310 ACPI id to gc0310 driver Add INT0310 ACPI id to gc0310 driver Signed-off-by: Hans de Goede Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/atomisp/i2c/gc0310.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/media/atomisp/i2c/gc0310.c b/drivers/staging/media/atomisp/i2c/gc0310.c index 1ec616a15086..350fd7fd5b86 100644 --- a/drivers/staging/media/atomisp/i2c/gc0310.c +++ b/drivers/staging/media/atomisp/i2c/gc0310.c @@ -1455,6 +1455,7 @@ out_free: static struct acpi_device_id gc0310_acpi_match[] = { {"XXGC0310"}, + {"INT0310"}, {}, }; -- cgit v1.2.3-58-ga151 From 22b2807dae2c79eb9f75a5c0e4cdfd418fd14fcf Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 28 May 2017 09:31:51 -0300 Subject: [media] staging: atomisp: Add OVTI2680 ACPI id to ov2680 driver Add OVTI2680 ACPI id to ov2680 driver Signed-off-by: Hans de Goede Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/atomisp/i2c/ov2680.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/media/atomisp/i2c/ov2680.c b/drivers/staging/media/atomisp/i2c/ov2680.c index 566091035c64..449aa2aa276f 100644 --- a/drivers/staging/media/atomisp/i2c/ov2680.c +++ b/drivers/staging/media/atomisp/i2c/ov2680.c @@ -1521,6 +1521,7 @@ out_free: static struct acpi_device_id ov2680_acpi_match[] = { {"XXOV2680"}, + {"OVTI2680"}, {}, }; MODULE_DEVICE_TABLE(acpi, ov2680_acpi_match); -- cgit v1.2.3-58-ga151 From da3e18059be96c67365e63e4d1e3f301d802c3c8 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 28 May 2017 09:31:52 -0300 Subject: [media] staging: atomisp: Ignore errors from second gpio in ov2680 driver As the existing comment in the driver indicates the sensor has only 1 pin, but some boards may have 2 gpios defined and we toggle both as we we don't know which one is the right one. However if the ACPI resources table defines only 1 gpio (as expected) the gpio1_ctrl call will always fail, causing the probing of the driver to file. This commit ignore the return value of the gpio1_ctrl call, fixing this. Signed-off-by: Hans de Goede Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/atomisp/i2c/ov2680.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/staging/media/atomisp/i2c/ov2680.c b/drivers/staging/media/atomisp/i2c/ov2680.c index 449aa2aa276f..6dd466558701 100644 --- a/drivers/staging/media/atomisp/i2c/ov2680.c +++ b/drivers/staging/media/atomisp/i2c/ov2680.c @@ -885,11 +885,12 @@ static int gpio_ctrl(struct v4l2_subdev *sd, bool flag) if (flag) { ret = dev->platform_data->gpio0_ctrl(sd, 1); usleep_range(10000, 15000); - ret |= dev->platform_data->gpio1_ctrl(sd, 1); + /* Ignore return from second gpio, it may not be there */ + dev->platform_data->gpio1_ctrl(sd, 1); usleep_range(10000, 15000); } else { - ret = dev->platform_data->gpio1_ctrl(sd, 0); - ret |= dev->platform_data->gpio0_ctrl(sd, 0); + dev->platform_data->gpio1_ctrl(sd, 0); + ret = dev->platform_data->gpio0_ctrl(sd, 0); } return ret; } -- cgit v1.2.3-58-ga151 From ef14aa3bc1564f714b4e30bea78762b29516cc48 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 28 May 2017 09:31:53 -0300 Subject: [media] staging: atomisp: Make ov2680 driver less chatty There is no reason for all this printk spamming and certainly not at an error log level. Signed-off-by: Hans de Goede Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/atomisp/i2c/ov2680.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/staging/media/atomisp/i2c/ov2680.c b/drivers/staging/media/atomisp/i2c/ov2680.c index 6dd466558701..3cabfe54c669 100644 --- a/drivers/staging/media/atomisp/i2c/ov2680.c +++ b/drivers/staging/media/atomisp/i2c/ov2680.c @@ -1191,9 +1191,8 @@ static int ov2680_detect(struct i2c_client *client) OV2680_SC_CMMN_SUB_ID, &high); revision = (u8) high & 0x0f; - dev_err(&client->dev, "sensor_revision id = 0x%x\n", id); - dev_err(&client->dev, "detect ov2680 success\n"); - dev_err(&client->dev, "################5##########\n"); + dev_info(&client->dev, "sensor_revision id = 0x%x\n", id); + return 0; } @@ -1448,8 +1447,6 @@ static int ov2680_probe(struct i2c_client *client, void *pdata; unsigned int i; - printk("++++ov2680_probe++++\n"); - dev_info(&client->dev, "++++ov2680_probe++++\n"); dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (!dev) { dev_err(&client->dev, "out of memory\n"); -- cgit v1.2.3-58-ga151 From f1e627a41db14a4bb7bb5ee39486528121ed11c2 Mon Sep 17 00:00:00 2001 From: Chen Guanqiao Date: Sun, 28 May 2017 15:06:41 -0300 Subject: [media] staging: atomisp: lm3554: fix sparse warnings(was not declared. Should it be static?) Fix "symbol 'xxxxxxx' was not declared. Should it be static?" sparse warnings. Signed-off-by: Chen Guanqiao Reviewed-by: Alan Cox Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/atomisp/i2c/lm3554.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/media/atomisp/i2c/lm3554.c b/drivers/staging/media/atomisp/i2c/lm3554.c index dd9c9c3ffff7..2b170c07aaba 100644 --- a/drivers/staging/media/atomisp/i2c/lm3554.c +++ b/drivers/staging/media/atomisp/i2c/lm3554.c @@ -497,7 +497,7 @@ static const struct v4l2_ctrl_ops ctrl_ops = { .g_volatile_ctrl = lm3554_g_volatile_ctrl }; -struct v4l2_ctrl_config lm3554_controls[] = { +static const struct v4l2_ctrl_config lm3554_controls[] = { { .ops = &ctrl_ops, .id = V4L2_CID_FLASH_TIMEOUT, @@ -825,7 +825,7 @@ static int lm3554_gpio_uninit(struct i2c_client *client) return 0; } -void *lm3554_platform_data_func(struct i2c_client *client) +static void *lm3554_platform_data_func(struct i2c_client *client) { static struct lm3554_platform_data platform_data; -- cgit v1.2.3-58-ga151 From af822177d614f985cb89dcf6e505e2d76a3f3683 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 2 Jun 2017 17:04:23 -0300 Subject: [media] staging: atomisp: Fix endless recursion in hmm_init hmm_init calls hmm_alloc to set dummy_ptr, hmm_alloc calls hmm_init when dummy_ptr is not yet set, which is the case in the call from hmm_init, so it calls hmm_init again, this continues until we have a stack overflow due to the recursion. This commit fixes this by adding a separate flag for tracking if hmm_init has been called. Not pretty, but it gets the job done, eventually we should be able to remove the hmm_init call from hmm_alloc. Signed-off-by: Hans de Goede Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/atomisp/pci/atomisp2/hmm/hmm.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/staging/media/atomisp/pci/atomisp2/hmm/hmm.c b/drivers/staging/media/atomisp/pci/atomisp2/hmm/hmm.c index 5e63073f3581..05eeff58a229 100644 --- a/drivers/staging/media/atomisp/pci/atomisp2/hmm/hmm.c +++ b/drivers/staging/media/atomisp/pci/atomisp2/hmm/hmm.c @@ -43,6 +43,7 @@ struct hmm_bo_device bo_device; struct hmm_pool dynamic_pool; struct hmm_pool reserved_pool; static ia_css_ptr dummy_ptr; +static bool hmm_initialized; struct _hmm_mem_stat hmm_mem_stat; /* p: private @@ -186,6 +187,8 @@ int hmm_init(void) if (ret) dev_err(atomisp_dev, "hmm_bo_device_init failed.\n"); + hmm_initialized = true; + /* * As hmm use NULL to indicate invalid ISP virtual address, * and ISP_VM_START is defined to 0 too, so we allocate @@ -217,6 +220,7 @@ void hmm_cleanup(void) dummy_ptr = 0; hmm_bo_device_exit(&bo_device); + hmm_initialized = false; } ia_css_ptr hmm_alloc(size_t bytes, enum hmm_bo_type type, @@ -229,7 +233,7 @@ ia_css_ptr hmm_alloc(size_t bytes, enum hmm_bo_type type, /* Check if we are initialized. In the ideal world we wouldn't need this but we can tackle it once the driver is a lot cleaner */ - if (!dummy_ptr) + if (!hmm_initialized) hmm_init(); /*Get page number from size*/ pgnr = size_to_pgnr_ceil(bytes); -- cgit v1.2.3-58-ga151 From 8ec6107aecca8d9051f3705784e6b92481aba3cd Mon Sep 17 00:00:00 2001 From: Jasmin Jessich Date: Sun, 7 May 2017 18:23:28 -0300 Subject: [media] dvb_ca_en50221: use foo *bar, instead of foo * bar Make checkpatch happier by fixing this warning: ERROR: "foo * bar" should be "foo *bar" Signed-off-by: Jasmin Jessich Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-core/dvb_ca_en50221.c | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/drivers/media/dvb-core/dvb_ca_en50221.c b/drivers/media/dvb-core/dvb_ca_en50221.c index d38bf9bce480..ce4b6920c0ae 100644 --- a/drivers/media/dvb-core/dvb_ca_en50221.c +++ b/drivers/media/dvb-core/dvb_ca_en50221.c @@ -193,8 +193,10 @@ static void dvb_ca_private_put(struct dvb_ca_private *ca) } static void dvb_ca_en50221_thread_wakeup(struct dvb_ca_private *ca); -static int dvb_ca_en50221_read_data(struct dvb_ca_private *ca, int slot, u8 * ebuf, int ecount); -static int dvb_ca_en50221_write_data(struct dvb_ca_private *ca, int slot, u8 * ebuf, int ecount); +static int dvb_ca_en50221_read_data(struct dvb_ca_private *ca, int slot, + u8 *ebuf, int ecount); +static int dvb_ca_en50221_write_data(struct dvb_ca_private *ca, int slot, + u8 *ebuf, int ecount); /** @@ -206,7 +208,7 @@ static int dvb_ca_en50221_write_data(struct dvb_ca_private *ca, int slot, u8 * e * @nlen: Number of bytes in needle. * @return Pointer into haystack needle was found at, or NULL if not found. */ -static char *findstr(char * haystack, int hlen, char * needle, int nlen) +static char *findstr(char *haystack, int hlen, char *needle, int nlen) { int i; @@ -390,7 +392,8 @@ static int dvb_ca_en50221_link_init(struct dvb_ca_private *ca, int slot) * @return 0 on success, nonzero on error. */ static int dvb_ca_en50221_read_tuple(struct dvb_ca_private *ca, int slot, - int *address, int *tupleType, int *tupleLength, u8 * tuple) + int *address, int *tupleType, + int *tupleLength, u8 *tuple) { int i; int _tupleType; @@ -621,7 +624,8 @@ static int dvb_ca_en50221_set_configoption(struct dvb_ca_private *ca, int slot) * * @return Number of bytes read, or < 0 on error */ -static int dvb_ca_en50221_read_data(struct dvb_ca_private *ca, int slot, u8 * ebuf, int ecount) +static int dvb_ca_en50221_read_data(struct dvb_ca_private *ca, int slot, + u8 *ebuf, int ecount) { int bytes_read; int status; @@ -745,7 +749,8 @@ exit: * * @return Number of bytes written, or < 0 on error. */ -static int dvb_ca_en50221_write_data(struct dvb_ca_private *ca, int slot, u8 * buf, int bytes_write) +static int dvb_ca_en50221_write_data(struct dvb_ca_private *ca, int slot, + u8 *buf, int bytes_write) { int status; int i; @@ -1345,7 +1350,8 @@ static long dvb_ca_en50221_io_ioctl(struct file *file, * @return Number of bytes read, or <0 on error. */ static ssize_t dvb_ca_en50221_io_write(struct file *file, - const char __user * buf, size_t count, loff_t * ppos) + const char __user *buf, size_t count, + loff_t *ppos) { struct dvb_device *dvbdev = file->private_data; struct dvb_ca_private *ca = dvbdev->priv; @@ -1485,8 +1491,8 @@ nextslot: * * @return Number of bytes read, or <0 on error. */ -static ssize_t dvb_ca_en50221_io_read(struct file *file, char __user * buf, - size_t count, loff_t * ppos) +static ssize_t dvb_ca_en50221_io_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) { struct dvb_device *dvbdev = file->private_data; struct dvb_ca_private *ca = dvbdev->priv; @@ -1664,7 +1670,7 @@ static int dvb_ca_en50221_io_release(struct inode *inode, struct file *file) * * @return Standard poll mask. */ -static unsigned int dvb_ca_en50221_io_poll(struct file *file, poll_table * wait) +static unsigned int dvb_ca_en50221_io_poll(struct file *file, poll_table *wait) { struct dvb_device *dvbdev = file->private_data; struct dvb_ca_private *ca = dvbdev->priv; -- cgit v1.2.3-58-ga151 From 97adc2b39e91b9037ef0a14d1e996acd09471c4b Mon Sep 17 00:00:00 2001 From: Jasmin Jessich Date: Sun, 7 May 2017 18:23:34 -0300 Subject: [media] dvb_ca_en50221: Fix wrong EXPORT_SYMBOL order Some EXPORT_SYMBOL() on this file don't match the name of functions that preceeds them. Also, some kernel-doc markups also have a wrong name. Fix it. Signed-off-by: Jasmin Jessich Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-core/dvb_ca_en50221.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/drivers/media/dvb-core/dvb_ca_en50221.c b/drivers/media/dvb-core/dvb_ca_en50221.c index ce4b6920c0ae..af694f2066a2 100644 --- a/drivers/media/dvb-core/dvb_ca_en50221.c +++ b/drivers/media/dvb-core/dvb_ca_en50221.c @@ -845,7 +845,6 @@ exit: exitnowrite: return status; } -EXPORT_SYMBOL(dvb_ca_en50221_camchange_irq); @@ -854,7 +853,7 @@ EXPORT_SYMBOL(dvb_ca_en50221_camchange_irq); /** - * dvb_ca_en50221_camready_irq - A CAM has been removed => shut it down. + * dvb_ca_en50221_slot_shutdown - A CAM has been removed => shut it down. * * @ca: CA instance. * @slot: Slot to shut down. @@ -875,11 +874,10 @@ static int dvb_ca_en50221_slot_shutdown(struct dvb_ca_private *ca, int slot) /* success */ return 0; } -EXPORT_SYMBOL(dvb_ca_en50221_camready_irq); /** - * dvb_ca_en50221_camready_irq - A CAMCHANGE IRQ has occurred. + * dvb_ca_en50221_camchange_irq - A CAMCHANGE IRQ has occurred. * * @ca: CA instance. * @slot: Slot concerned. @@ -904,7 +902,7 @@ void dvb_ca_en50221_camchange_irq(struct dvb_ca_en50221 *pubca, int slot, int ch atomic_inc(&ca->slot_info[slot].camchange_count); dvb_ca_en50221_thread_wakeup(ca); } -EXPORT_SYMBOL(dvb_ca_en50221_frda_irq); +EXPORT_SYMBOL(dvb_ca_en50221_camchange_irq); /** @@ -924,10 +922,11 @@ void dvb_ca_en50221_camready_irq(struct dvb_ca_en50221 *pubca, int slot) dvb_ca_en50221_thread_wakeup(ca); } } +EXPORT_SYMBOL(dvb_ca_en50221_camready_irq); /** - * An FR or DA IRQ has occurred. + * dvb_ca_en50221_frda_irq - An FR or DA IRQ has occurred. * * @ca: CA instance. * @slot: Slot concerned. @@ -954,7 +953,7 @@ void dvb_ca_en50221_frda_irq(struct dvb_ca_en50221 *pubca, int slot) break; } } - +EXPORT_SYMBOL(dvb_ca_en50221_frda_irq); /* ******************************************************************************** */ -- cgit v1.2.3-58-ga151 From 47b586f66a9e78c91586b9c363603a52c75840d7 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Sun, 7 May 2017 15:33:05 -0300 Subject: [media] pvrusb2: remove redundant check on cnt > 8 The 2nd check of cnt > 8 is redundant as cnt is already checked and thresholded to a maximum of 8 a few statements earlier. Remove this redundant 2nd check. Detected by CoverityScan, CID#114281 ("Logically dead code") Signed-off-by: Colin Ian King Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/pvrusb2/pvrusb2-i2c-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/usb/pvrusb2/pvrusb2-i2c-core.c b/drivers/media/usb/pvrusb2/pvrusb2-i2c-core.c index f727b54a53c6..20a52b785fff 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-i2c-core.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-i2c-core.c @@ -488,7 +488,7 @@ static int pvr2_i2c_xfer(struct i2c_adapter *i2c_adap, if ((ret > 0) || !(msgs[idx].flags & I2C_M_RD)) { if (cnt > 8) cnt = 8; printk(KERN_CONT " ["); - for (offs = 0; offs < (cnt>8?8:cnt); offs++) { + for (offs = 0; offs < cnt; offs++) { if (offs) printk(KERN_CONT " "); printk(KERN_CONT "%02x",msgs[idx].buf[offs]); } -- cgit v1.2.3-58-ga151 From a2d17962c9ca7ac66a132bbbfc6054559856e14e Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Thu, 8 Jun 2017 12:04:07 -0300 Subject: [media] davinci: Switch from V4L2 OF to V4L2 fwnode The DaVinci VPIF capture driver V4L2 OF support was added after the V4L2 OF framework got removed. Switch VPIF capture driver to V4L2 fwnode. Signed-off-by: Sakari Ailus --- drivers/media/platform/davinci/Kconfig | 1 + drivers/media/platform/davinci/vpif_capture.c | 21 ++++++++++++--------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/drivers/media/platform/davinci/Kconfig b/drivers/media/platform/davinci/Kconfig index 554e710de487..55982e681d77 100644 --- a/drivers/media/platform/davinci/Kconfig +++ b/drivers/media/platform/davinci/Kconfig @@ -22,6 +22,7 @@ config VIDEO_DAVINCI_VPIF_CAPTURE depends on HAS_DMA depends on I2C select VIDEOBUF2_DMA_CONTIG + select V4L2_FWNODE help Enables Davinci VPIF module used for capture devices. This module is used for capture on TI DM6467/DA850/OMAPL138 diff --git a/drivers/media/platform/davinci/vpif_capture.c b/drivers/media/platform/davinci/vpif_capture.c index 27357954f1bb..d78580f9e431 100644 --- a/drivers/media/platform/davinci/vpif_capture.c +++ b/drivers/media/platform/davinci/vpif_capture.c @@ -18,11 +18,12 @@ #include #include +#include #include #include +#include #include -#include #include #include @@ -1389,15 +1390,16 @@ static int vpif_async_bound(struct v4l2_async_notifier *notifier, for (i = 0; i < vpif_obj.config->asd_sizes[0]; i++) { struct v4l2_async_subdev *_asd = vpif_obj.config->asd[i]; - const struct device_node *node = _asd->match.of.node; + const struct fwnode_handle *fwnode = _asd->match.fwnode.fwnode; - if (node == subdev->of_node) { + if (fwnode == subdev->fwnode) { vpif_obj.sd[i] = subdev; vpif_obj.config->chan_config->inputs[i].subdev_name = - (char *)subdev->of_node->full_name; + (char *)to_of_node(subdev->fwnode)->full_name; vpif_dbg(2, debug, "%s: setting input %d subdev_name = %s\n", - __func__, i, subdev->of_node->full_name); + __func__, i, + to_of_node(subdev->fwnode)->full_name); return 0; } } @@ -1502,7 +1504,7 @@ static struct vpif_capture_config * vpif_capture_get_pdata(struct platform_device *pdev) { struct device_node *endpoint = NULL; - struct v4l2_of_endpoint bus_cfg; + struct v4l2_fwnode_endpoint bus_cfg; struct vpif_capture_config *pdata; struct vpif_subdev_info *sdinfo; struct vpif_capture_chan_config *chan; @@ -1549,7 +1551,8 @@ vpif_capture_get_pdata(struct platform_device *pdev) chan->inputs[i].input.std = V4L2_STD_ALL; chan->inputs[i].input.capabilities = V4L2_IN_CAP_STD; - err = v4l2_of_parse_endpoint(endpoint, &bus_cfg); + err = v4l2_fwnode_endpoint_parse(of_fwnode_handle(endpoint), + &bus_cfg); if (err) { dev_err(&pdev->dev, "Could not parse the endpoint\n"); goto done; @@ -1584,8 +1587,8 @@ vpif_capture_get_pdata(struct platform_device *pdev) goto done; } - pdata->asd[i]->match_type = V4L2_ASYNC_MATCH_OF; - pdata->asd[i]->match.of.node = rem; + pdata->asd[i]->match_type = V4L2_ASYNC_MATCH_FWNODE; + pdata->asd[i]->match.fwnode.fwnode = of_fwnode_handle(rem); of_node_put(rem); } -- cgit v1.2.3-58-ga151 From 13c6c6ea1a89fea426935501ddd064d271d836d6 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 8 Jun 2017 12:14:09 -0300 Subject: [media] platform/Makefile: don't depend on arch to include dirs Depending on arch configs to include dirs is evil, and makes harder to change drivers to work with COMPILE_TEST. Replace them by obj-y. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile index 231f3c2894c9..c3588d570f5d 100644 --- a/drivers/media/platform/Makefile +++ b/drivers/media/platform/Makefile @@ -44,9 +44,9 @@ obj-$(CONFIG_VIDEO_STI_HDMI_CEC) += sti/cec/ obj-$(CONFIG_VIDEO_STI_DELTA) += sti/delta/ -obj-$(CONFIG_BLACKFIN) += blackfin/ +obj-y += blackfin/ -obj-$(CONFIG_ARCH_DAVINCI) += davinci/ +obj-y += davinci/ obj-$(CONFIG_VIDEO_SH_VOU) += sh_vou.o -- cgit v1.2.3-58-ga151 From 586248a75b36d39aa60eb6e447b7f7e25e011e73 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Fri, 26 May 2017 10:00:48 -0300 Subject: [media] ad5820: unregister async sub-device The async sub-device was not unregistered in ad5820_remove() as it should have been; do it now. Also remove the now-redundant v4l2_device_unregister_subdev(). Signed-off-by: Sakari Ailus Acked-by: Pavel Machek Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ad5820.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/i2c/ad5820.c b/drivers/media/i2c/ad5820.c index 3d2a3c6b67d8..034ebf754007 100644 --- a/drivers/media/i2c/ad5820.c +++ b/drivers/media/i2c/ad5820.c @@ -341,7 +341,7 @@ static int ad5820_remove(struct i2c_client *client) struct v4l2_subdev *subdev = i2c_get_clientdata(client); struct ad5820_device *coil = to_ad5820_device(subdev); - v4l2_device_unregister_subdev(&coil->subdev); + v4l2_async_unregister_subdev(&coil->subdev); v4l2_ctrl_handler_free(&coil->ctrls); media_entity_cleanup(&coil->subdev.entity); mutex_destroy(&coil->power_lock); -- cgit v1.2.3-58-ga151 From 04f3fc7615c7f8843133cc59d4587d3cd547ce1c Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Sun, 28 May 2017 06:30:49 -0300 Subject: [media] rcar_vin: use proper name for the R-Car SoC It is 'R-Car', not 'RCar'. No code or binding changes, only descriptive text. Signed-off-by: Wolfram Sang Acked-by: Geert Uytterhoeven Acked-by: Rob Herring Signed-off-by: Mauro Carvalho Chehab --- Documentation/devicetree/bindings/media/rcar_vin.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/media/rcar_vin.txt b/Documentation/devicetree/bindings/media/rcar_vin.txt index 6a4e61cbe011..6e4ef8caf759 100644 --- a/Documentation/devicetree/bindings/media/rcar_vin.txt +++ b/Documentation/devicetree/bindings/media/rcar_vin.txt @@ -1,5 +1,5 @@ -Renesas RCar Video Input driver (rcar_vin) ------------------------------------------- +Renesas R-Car Video Input driver (rcar_vin) +------------------------------------------- The rcar_vin device provides video input capabilities for the Renesas R-Car family of devices. The current blocks are always slaves and suppot one input -- cgit v1.2.3-58-ga151 From 60829e62bb8a949d2d0f03b502c9a8ee83d22ab7 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Sun, 28 May 2017 06:30:50 -0300 Subject: [media] v4l: rcar_fdp1: use proper name for the R-Car SoC It is 'R-Car', not 'RCar'. No code or binding changes, only descriptive text. Signed-off-by: Wolfram Sang Acked-by: Kieran Bingham Acked-by: Geert Uytterhoeven Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rcar_fdp1.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/rcar_fdp1.c b/drivers/media/platform/rcar_fdp1.c index 42f25d241edd..0da0eba9202c 100644 --- a/drivers/media/platform/rcar_fdp1.c +++ b/drivers/media/platform/rcar_fdp1.c @@ -1,5 +1,5 @@ /* - * Renesas RCar Fine Display Processor + * Renesas R-Car Fine Display Processor * * Video format converter and frame deinterlacer device. * -- cgit v1.2.3-58-ga151 From 703ecba4dbfb8de9cad7420021dfcbd11007de3b Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 8 Jun 2017 12:34:44 -0300 Subject: [media] staging: css2400/Makefile: don't include non-existing files The atomisp css2400/Makefile includes a Makefile.common: include $(srctree)/$(src)/../Makefile.common Well, this file doesn't exist at the Kernel tree :-) So, don't include it. Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/atomisp/pci/atomisp2/css2400/Makefile | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/Makefile b/drivers/staging/media/atomisp/pci/atomisp2/css2400/Makefile index 04defaafa02c..ee5631b0e635 100644 --- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/Makefile +++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/Makefile @@ -1,4 +1,2 @@ ccflags-y += -DISP2400B0 ISP2400B0 := y - -include $(srctree)/$(src)/../Makefile.common -- cgit v1.2.3-58-ga151 From acc490bbb7f9958530506f10adce75c8818b6a96 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 8 Jun 2017 13:29:21 -0300 Subject: [media] atomisp: use correct dialect to disable warnings There's a Macro that checks if gcc supports a warning before disabling it. Use it, in order to avoid warnings when building with older gcc versions. Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/atomisp/i2c/Makefile | 6 ++++-- drivers/staging/media/atomisp/i2c/imx/Makefile | 6 ++++-- drivers/staging/media/atomisp/i2c/ov5693/Makefile | 6 ++++-- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/drivers/staging/media/atomisp/i2c/Makefile b/drivers/staging/media/atomisp/i2c/Makefile index a1afca6ec31f..be13fab92175 100644 --- a/drivers/staging/media/atomisp/i2c/Makefile +++ b/drivers/staging/media/atomisp/i2c/Makefile @@ -21,5 +21,7 @@ obj-$(CONFIG_VIDEO_LM3554) += lm3554.o # HACK! While this driver is in bad shape, don't enable several warnings # that would be otherwise enabled with W=1 -ccflags-y += -Wno-unused-but-set-variable -Wno-missing-prototypes \ - -Wno-unused-const-variable -Wno-missing-declarations +ccflags-y += $(call cc-disable-warning, unused-but-set-variable) +ccflags-y += $(call cc-disable-warning, unused-const-variable) +ccflags-y += $(call cc-disable-warning, missing-prototypes) +ccflags-y += $(call cc-disable-warning, missing-declarations) diff --git a/drivers/staging/media/atomisp/i2c/imx/Makefile b/drivers/staging/media/atomisp/i2c/imx/Makefile index 0eceb7374bec..b6578f09546e 100644 --- a/drivers/staging/media/atomisp/i2c/imx/Makefile +++ b/drivers/staging/media/atomisp/i2c/imx/Makefile @@ -7,5 +7,7 @@ obj-$(CONFIG_VIDEO_OV8858) += ov8858_driver.o # HACK! While this driver is in bad shape, don't enable several warnings # that would be otherwise enabled with W=1 -ccflags-y += -Wno-unused-but-set-variable -Wno-missing-prototypes \ - -Wno-unused-const-variable -Wno-missing-declarations +ccflags-y += $(call cc-disable-warning, unused-but-set-variable) +ccflags-y += $(call cc-disable-warning, unused-const-variable) +ccflags-y += $(call cc-disable-warning, missing-prototypes) +ccflags-y += $(call cc-disable-warning, missing-declarations) diff --git a/drivers/staging/media/atomisp/i2c/ov5693/Makefile b/drivers/staging/media/atomisp/i2c/ov5693/Makefile index fd2ef2e3c31e..4e3833aaec05 100644 --- a/drivers/staging/media/atomisp/i2c/ov5693/Makefile +++ b/drivers/staging/media/atomisp/i2c/ov5693/Makefile @@ -2,5 +2,7 @@ obj-$(CONFIG_VIDEO_OV5693) += ov5693.o # HACK! While this driver is in bad shape, don't enable several warnings # that would be otherwise enabled with W=1 -ccflags-y += -Wno-unused-but-set-variable -Wno-missing-prototypes \ - -Wno-unused-const-variable -Wno-missing-declarations +ccflags-y += $(call cc-disable-warning, unused-but-set-variable) +ccflags-y += $(call cc-disable-warning, unused-const-variable) +ccflags-y += $(call cc-disable-warning, missing-prototypes) +ccflags-y += $(call cc-disable-warning, missing-declarations) -- cgit v1.2.3-58-ga151 From 6aca5b8fb86681954e6715494e74106db505701c Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Wed, 31 May 2017 11:17:25 -0300 Subject: [media] vb2: Rename confusingly named internal buffer preparation functions Rename __qbuf_*() functions which are specific to a buffer type as __prepare_*() which matches with what they do. The naming was there for historical reasons; the purpose of the functions was changed without renaming them. Signed-off-by: Sakari Ailus Acked-by: Hans Verkuil Reviewed-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/videobuf2-core.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c index c0175ea7e7ad..9f3ce3b669c1 100644 --- a/drivers/media/v4l2-core/videobuf2-core.c +++ b/drivers/media/v4l2-core/videobuf2-core.c @@ -956,9 +956,9 @@ void vb2_discard_done(struct vb2_queue *q) EXPORT_SYMBOL_GPL(vb2_discard_done); /** - * __qbuf_mmap() - handle qbuf of an MMAP buffer + * __prepare_mmap() - prepare an MMAP buffer */ -static int __qbuf_mmap(struct vb2_buffer *vb, const void *pb) +static int __prepare_mmap(struct vb2_buffer *vb, const void *pb) { int ret = 0; @@ -969,9 +969,9 @@ static int __qbuf_mmap(struct vb2_buffer *vb, const void *pb) } /** - * __qbuf_userptr() - handle qbuf of a USERPTR buffer + * __prepare_userptr() - prepare a USERPTR buffer */ -static int __qbuf_userptr(struct vb2_buffer *vb, const void *pb) +static int __prepare_userptr(struct vb2_buffer *vb, const void *pb) { struct vb2_plane planes[VB2_MAX_PLANES]; struct vb2_queue *q = vb->vb2_queue; @@ -1087,9 +1087,9 @@ err: } /** - * __qbuf_dmabuf() - handle qbuf of a DMABUF buffer + * __prepare_dmabuf() - prepare a DMABUF buffer */ -static int __qbuf_dmabuf(struct vb2_buffer *vb, const void *pb) +static int __prepare_dmabuf(struct vb2_buffer *vb, const void *pb) { struct vb2_plane planes[VB2_MAX_PLANES]; struct vb2_queue *q = vb->vb2_queue; @@ -1255,13 +1255,13 @@ static int __buf_prepare(struct vb2_buffer *vb, const void *pb) switch (q->memory) { case VB2_MEMORY_MMAP: - ret = __qbuf_mmap(vb, pb); + ret = __prepare_mmap(vb, pb); break; case VB2_MEMORY_USERPTR: - ret = __qbuf_userptr(vb, pb); + ret = __prepare_userptr(vb, pb); break; case VB2_MEMORY_DMABUF: - ret = __qbuf_dmabuf(vb, pb); + ret = __prepare_dmabuf(vb, pb); break; default: WARN(1, "Invalid queue type\n"); -- cgit v1.2.3-58-ga151 From a136f59c0a1f1b09eb203951975e3fc5e8d3e722 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Wed, 31 May 2017 11:17:26 -0300 Subject: [media] vb2: Move buffer cache synchronisation to prepare from queue The buffer cache should be synchronised in buffer preparation, not when the buffer is queued to the device. Fix this. Mmap buffers do not need cache synchronisation since they are always coherent. Signed-off-by: Sakari Ailus Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/videobuf2-core.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c index 9f3ce3b669c1..3107e2196e54 100644 --- a/drivers/media/v4l2-core/videobuf2-core.c +++ b/drivers/media/v4l2-core/videobuf2-core.c @@ -1227,23 +1227,19 @@ err: static void __enqueue_in_driver(struct vb2_buffer *vb) { struct vb2_queue *q = vb->vb2_queue; - unsigned int plane; vb->state = VB2_BUF_STATE_ACTIVE; atomic_inc(&q->owned_by_drv_count); trace_vb2_buf_queue(q, vb); - /* sync buffers */ - for (plane = 0; plane < vb->num_planes; ++plane) - call_void_memop(vb, prepare, vb->planes[plane].mem_priv); - call_void_vb_qop(vb, buf_queue, vb); } static int __buf_prepare(struct vb2_buffer *vb, const void *pb) { struct vb2_queue *q = vb->vb2_queue; + unsigned int plane; int ret; if (q->error) { @@ -1268,11 +1264,19 @@ static int __buf_prepare(struct vb2_buffer *vb, const void *pb) ret = -EINVAL; } - if (ret) + if (ret) { dprintk(1, "buffer preparation failed: %d\n", ret); - vb->state = ret ? VB2_BUF_STATE_DEQUEUED : VB2_BUF_STATE_PREPARED; + vb->state = VB2_BUF_STATE_DEQUEUED; + return ret; + } - return ret; + /* sync buffers */ + for (plane = 0; plane < vb->num_planes; ++plane) + call_void_memop(vb, prepare, vb->planes[plane].mem_priv); + + vb->state = VB2_BUF_STATE_PREPARED; + + return 0; } int vb2_core_prepare_buf(struct vb2_queue *q, unsigned int index, void *pb) -- cgit v1.2.3-58-ga151 From 7eb2bcb2efccf07cc4bc31791e3c36bebe12b8ad Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Wed, 7 Jun 2017 06:33:02 -0300 Subject: [media] media-ioc-g-topology.rst: fix typos Fix what seems to be a few typos induced by copy/paste. [mchehab@s-opensource.com: fix a typo] Signed-off-by: Alexandre Courbot Signed-off-by: Mauro Carvalho Chehab --- Documentation/media/uapi/mediactl/media-ioc-g-topology.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Documentation/media/uapi/mediactl/media-ioc-g-topology.rst b/Documentation/media/uapi/mediactl/media-ioc-g-topology.rst index 48c9531f4db0..add8281494f8 100644 --- a/Documentation/media/uapi/mediactl/media-ioc-g-topology.rst +++ b/Documentation/media/uapi/mediactl/media-ioc-g-topology.rst @@ -241,7 +241,7 @@ desired arrays with the media graph elements. .. c:type:: media_v2_intf_devnode -.. flat-table:: struct media_v2_interface +.. flat-table:: struct media_v2_intf_devnode :header-rows: 0 :stub-columns: 0 :widths: 1 2 8 @@ -312,7 +312,7 @@ desired arrays with the media graph elements. .. c:type:: media_v2_link -.. flat-table:: struct media_v2_pad +.. flat-table:: struct media_v2_link :header-rows: 0 :stub-columns: 0 :widths: 1 2 8 @@ -324,7 +324,7 @@ desired arrays with the media graph elements. - ``id`` - - Unique ID for the pad. + - Unique ID for the link. - .. row 2 @@ -334,7 +334,7 @@ desired arrays with the media graph elements. - On pad to pad links: unique ID for the source pad. - On interface to entity links: unique ID for the interface. + On interface to entity links: unique ID for the entity. - .. row 3 -- cgit v1.2.3-58-ga151 From 47f910f0e0deb880c2114811f7ea1ec115a19ee4 Mon Sep 17 00:00:00 2001 From: Kieran Bingham Date: Wed, 7 Jun 2017 06:52:07 -0300 Subject: [media] v4l: subdev: tolerate null in media_entity_to_v4l2_subdev Return NULL, if a null entity is parsed for it's v4l2_subdev Currently, the callers of media_entity_to_v4l2_subdev() need to make sure that the subdev is non-NULL, for example, when it is obtained from media_entity_remote_pad(). As this is a recurring pattern, add the check at the macro in order to avoid additional checks at the callers. Signed-off-by: Kieran Bingham Reviewed-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- include/media/v4l2-subdev.h | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index a40760174797..0f92ebd2d710 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -826,8 +826,15 @@ struct v4l2_subdev { struct v4l2_subdev_platform_data *pdata; }; -#define media_entity_to_v4l2_subdev(ent) \ - container_of(ent, struct v4l2_subdev, entity) +#define media_entity_to_v4l2_subdev(ent) \ +({ \ + typeof(ent) __me_sd_ent = (ent); \ + \ + __me_sd_ent ? \ + container_of(__me_sd_ent, struct v4l2_subdev, entity) : \ + NULL; \ +}) + #define vdev_to_v4l2_subdev(vdev) \ ((struct v4l2_subdev *)video_get_drvdata(vdev)) -- cgit v1.2.3-58-ga151 From 1abbc60d5017d612b6108ba59faa6b2eb7ca015d Mon Sep 17 00:00:00 2001 From: Benjamin Gaignard Date: Wed, 31 May 2017 09:13:55 -0300 Subject: [media] dt-bindings: media: stm32 cec driver Add bindings documentation for stm32 CEC driver. Signed-off-by: Benjamin Gaignard Acked-by: Rob Herring Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../devicetree/bindings/media/st,stm32-cec.txt | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/st,stm32-cec.txt diff --git a/Documentation/devicetree/bindings/media/st,stm32-cec.txt b/Documentation/devicetree/bindings/media/st,stm32-cec.txt new file mode 100644 index 000000000000..6be2381c180d --- /dev/null +++ b/Documentation/devicetree/bindings/media/st,stm32-cec.txt @@ -0,0 +1,19 @@ +STMicroelectronics STM32 CEC driver + +Required properties: + - compatible : value should be "st,stm32-cec" + - reg : Physical base address of the IP registers and length of memory + mapped region. + - clocks : from common clock binding: handle to CEC clocks + - clock-names : from common clock binding: must be "cec" and "hdmi-cec". + - interrupts : CEC interrupt number to the CPU. + +Example for stm32f746: + +cec: cec@40006c00 { + compatible = "st,stm32-cec"; + reg = <0x40006C00 0x400>; + interrupts = <94>; + clocks = <&rcc 0 STM32F7_APB1_CLOCK(CEC)>, <&rcc 1 CLK_HDMI_CEC>; + clock-names = "cec", "hdmi-cec"; +}; -- cgit v1.2.3-58-ga151 From d69ae57453c80a2193bcdb3fd762efbec447eecf Mon Sep 17 00:00:00 2001 From: Benjamin Gaignard Date: Wed, 31 May 2017 10:23:48 -0300 Subject: [media] cec: add STM32 cec driver This patch add cec driver for STM32 platforms. cec hardware block isn't not always used with hdmi so cec notifier is not implemented. That will be done later when STM32 DSI driver will be available. Driver compliance has been tested with cec-ctl and cec-compliance tools. Signed-off-by: Benjamin Gaignard Signed-off-by: Yannick Fertre [hans.verkuil@cisco.com: modified platform/Makefile to use obj-y] Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/Kconfig | 12 + drivers/media/platform/Makefile | 2 + drivers/media/platform/stm32/Makefile | 1 + drivers/media/platform/stm32/stm32-cec.c | 362 +++++++++++++++++++++++++++++++ 4 files changed, 377 insertions(+) create mode 100644 drivers/media/platform/stm32/stm32-cec.c diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index 288d3b0dc812..8da521a8ead7 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -537,4 +537,16 @@ config VIDEO_STI_HDMI_CEC CEC bus is present in the HDMI connector and enables communication between compatible devices. +config VIDEO_STM32_HDMI_CEC + tristate "STMicroelectronics STM32 HDMI CEC driver" + depends on ARCH_STM32 || COMPILE_TEST + select REGMAP + select REGMAP_MMIO + select CEC_CORE + ---help--- + This is a driver for STM32 interface. It uses the + generic CEC framework interface. + CEC bus is present in the HDMI connector and enables communication + between compatible devices. + endif #CEC_PLATFORM_DRIVERS diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile index c3588d570f5d..6bbdf942e8c8 100644 --- a/drivers/media/platform/Makefile +++ b/drivers/media/platform/Makefile @@ -44,6 +44,8 @@ obj-$(CONFIG_VIDEO_STI_HDMI_CEC) += sti/cec/ obj-$(CONFIG_VIDEO_STI_DELTA) += sti/delta/ +obj-y += stm32/ + obj-y += blackfin/ obj-y += davinci/ diff --git a/drivers/media/platform/stm32/Makefile b/drivers/media/platform/stm32/Makefile index 9b606a70985d..07355091376b 100644 --- a/drivers/media/platform/stm32/Makefile +++ b/drivers/media/platform/stm32/Makefile @@ -1 +1,2 @@ obj-$(CONFIG_VIDEO_STM32_DCMI) += stm32-dcmi.o +obj-$(CONFIG_VIDEO_STM32_HDMI_CEC) += stm32-cec.o diff --git a/drivers/media/platform/stm32/stm32-cec.c b/drivers/media/platform/stm32/stm32-cec.c new file mode 100644 index 000000000000..9ab896b01ee8 --- /dev/null +++ b/drivers/media/platform/stm32/stm32-cec.c @@ -0,0 +1,362 @@ +/* + * STM32 CEC driver + * Copyright (C) STMicroelectronics SA 2017 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define CEC_NAME "stm32-cec" + +/* CEC registers */ +#define CEC_CR 0x0000 /* Control Register */ +#define CEC_CFGR 0x0004 /* ConFiGuration Register */ +#define CEC_TXDR 0x0008 /* Rx data Register */ +#define CEC_RXDR 0x000C /* Rx data Register */ +#define CEC_ISR 0x0010 /* Interrupt and status Register */ +#define CEC_IER 0x0014 /* Interrupt enable Register */ + +#define TXEOM BIT(2) +#define TXSOM BIT(1) +#define CECEN BIT(0) + +#define LSTN BIT(31) +#define OAR GENMASK(30, 16) +#define SFTOP BIT(8) +#define BRDNOGEN BIT(7) +#define LBPEGEN BIT(6) +#define BREGEN BIT(5) +#define BRESTP BIT(4) +#define RXTOL BIT(3) +#define SFT GENMASK(2, 0) +#define FULL_CFG (LSTN | SFTOP | BRDNOGEN | LBPEGEN | BREGEN | BRESTP \ + | RXTOL) + +#define TXACKE BIT(12) +#define TXERR BIT(11) +#define TXUDR BIT(10) +#define TXEND BIT(9) +#define TXBR BIT(8) +#define ARBLST BIT(7) +#define RXACKE BIT(6) +#define RXOVR BIT(2) +#define RXEND BIT(1) +#define RXBR BIT(0) + +#define ALL_TX_IT (TXEND | TXBR | TXACKE | TXERR | TXUDR | ARBLST) +#define ALL_RX_IT (RXEND | RXBR | RXACKE | RXOVR) + +struct stm32_cec { + struct cec_adapter *adap; + struct device *dev; + struct clk *clk_cec; + struct clk *clk_hdmi_cec; + struct reset_control *rstc; + struct regmap *regmap; + int irq; + u32 irq_status; + struct cec_msg rx_msg; + struct cec_msg tx_msg; + int tx_cnt; +}; + +static void cec_hw_init(struct stm32_cec *cec) +{ + regmap_update_bits(cec->regmap, CEC_CR, TXEOM | TXSOM | CECEN, 0); + + regmap_update_bits(cec->regmap, CEC_IER, ALL_TX_IT | ALL_RX_IT, + ALL_TX_IT | ALL_RX_IT); + + regmap_update_bits(cec->regmap, CEC_CFGR, FULL_CFG, FULL_CFG); +} + +static void stm32_tx_done(struct stm32_cec *cec, u32 status) +{ + if (status & (TXERR | TXUDR)) { + cec_transmit_done(cec->adap, CEC_TX_STATUS_ERROR, + 0, 0, 0, 1); + return; + } + + if (status & ARBLST) { + cec_transmit_done(cec->adap, CEC_TX_STATUS_ARB_LOST, + 1, 0, 0, 0); + return; + } + + if (status & TXACKE) { + cec_transmit_done(cec->adap, CEC_TX_STATUS_NACK, + 0, 1, 0, 0); + return; + } + + if (cec->irq_status & TXBR) { + /* send next byte */ + if (cec->tx_cnt < cec->tx_msg.len) + regmap_write(cec->regmap, CEC_TXDR, + cec->tx_msg.msg[cec->tx_cnt++]); + + /* TXEOM is set to command transmission of the last byte */ + if (cec->tx_cnt == cec->tx_msg.len) + regmap_update_bits(cec->regmap, CEC_CR, TXEOM, TXEOM); + } + + if (cec->irq_status & TXEND) + cec_transmit_done(cec->adap, CEC_TX_STATUS_OK, 0, 0, 0, 0); +} + +static void stm32_rx_done(struct stm32_cec *cec, u32 status) +{ + if (cec->irq_status & (RXACKE | RXOVR)) { + cec->rx_msg.len = 0; + return; + } + + if (cec->irq_status & RXBR) { + u32 val; + + regmap_read(cec->regmap, CEC_RXDR, &val); + cec->rx_msg.msg[cec->rx_msg.len++] = val & 0xFF; + } + + if (cec->irq_status & RXEND) { + cec_received_msg(cec->adap, &cec->rx_msg); + cec->rx_msg.len = 0; + } +} + +static irqreturn_t stm32_cec_irq_thread(int irq, void *arg) +{ + struct stm32_cec *cec = arg; + + if (cec->irq_status & ALL_TX_IT) + stm32_tx_done(cec, cec->irq_status); + + if (cec->irq_status & ALL_RX_IT) + stm32_rx_done(cec, cec->irq_status); + + cec->irq_status = 0; + + return IRQ_HANDLED; +} + +static irqreturn_t stm32_cec_irq_handler(int irq, void *arg) +{ + struct stm32_cec *cec = arg; + + regmap_read(cec->regmap, CEC_ISR, &cec->irq_status); + + regmap_update_bits(cec->regmap, CEC_ISR, + ALL_TX_IT | ALL_RX_IT, + ALL_TX_IT | ALL_RX_IT); + + return IRQ_WAKE_THREAD; +} + +static int stm32_cec_adap_enable(struct cec_adapter *adap, bool enable) +{ + struct stm32_cec *cec = adap->priv; + int ret = 0; + + if (enable) { + ret = clk_enable(cec->clk_cec); + if (ret) + dev_err(cec->dev, "fail to enable cec clock\n"); + + clk_enable(cec->clk_hdmi_cec); + regmap_update_bits(cec->regmap, CEC_CR, CECEN, CECEN); + } else { + clk_disable(cec->clk_cec); + clk_disable(cec->clk_hdmi_cec); + regmap_update_bits(cec->regmap, CEC_CR, CECEN, 0); + } + + return ret; +} + +static int stm32_cec_adap_log_addr(struct cec_adapter *adap, u8 logical_addr) +{ + struct stm32_cec *cec = adap->priv; + u32 oar = (1 << logical_addr) << 16; + + regmap_update_bits(cec->regmap, CEC_CR, CECEN, 0); + + if (logical_addr == CEC_LOG_ADDR_INVALID) + regmap_update_bits(cec->regmap, CEC_CFGR, OAR, 0); + else + regmap_update_bits(cec->regmap, CEC_CFGR, oar, oar); + + regmap_update_bits(cec->regmap, CEC_CR, CECEN, CECEN); + + return 0; +} + +static int stm32_cec_adap_transmit(struct cec_adapter *adap, u8 attempts, + u32 signal_free_time, struct cec_msg *msg) +{ + struct stm32_cec *cec = adap->priv; + + /* Copy message */ + cec->tx_msg = *msg; + cec->tx_cnt = 0; + + /* + * If the CEC message consists of only one byte, + * TXEOM must be set before of TXSOM. + */ + if (cec->tx_msg.len == 1) + regmap_update_bits(cec->regmap, CEC_CR, TXEOM, TXEOM); + + /* TXSOM is set to command transmission of the first byte */ + regmap_update_bits(cec->regmap, CEC_CR, TXSOM, TXSOM); + + /* Write the header (first byte of message) */ + regmap_write(cec->regmap, CEC_TXDR, cec->tx_msg.msg[0]); + cec->tx_cnt++; + + return 0; +} + +static const struct cec_adap_ops stm32_cec_adap_ops = { + .adap_enable = stm32_cec_adap_enable, + .adap_log_addr = stm32_cec_adap_log_addr, + .adap_transmit = stm32_cec_adap_transmit, +}; + +static const struct regmap_config stm32_cec_regmap_cfg = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = sizeof(u32), + .max_register = 0x14, + .fast_io = true, +}; + +static int stm32_cec_probe(struct platform_device *pdev) +{ + u32 caps = CEC_CAP_LOG_ADDRS | CEC_CAP_PASSTHROUGH | + CEC_CAP_TRANSMIT | CEC_CAP_RC | CEC_CAP_PHYS_ADDR | + CEC_MODE_MONITOR_ALL; + struct resource *res; + struct stm32_cec *cec; + void __iomem *mmio; + int ret; + + cec = devm_kzalloc(&pdev->dev, sizeof(*cec), GFP_KERNEL); + if (!cec) + return -ENOMEM; + + cec->dev = &pdev->dev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + mmio = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(mmio)) + return PTR_ERR(mmio); + + cec->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "cec", mmio, + &stm32_cec_regmap_cfg); + + if (IS_ERR(cec->regmap)) + return PTR_ERR(cec->regmap); + + cec->irq = platform_get_irq(pdev, 0); + if (cec->irq < 0) + return cec->irq; + + ret = devm_request_threaded_irq(&pdev->dev, cec->irq, + stm32_cec_irq_handler, + stm32_cec_irq_thread, + 0, + pdev->name, cec); + if (ret) + return ret; + + cec->clk_cec = devm_clk_get(&pdev->dev, "cec"); + if (IS_ERR(cec->clk_cec)) { + dev_err(&pdev->dev, "Cannot get cec clock\n"); + return PTR_ERR(cec->clk_cec); + } + + ret = clk_prepare(cec->clk_cec); + if (ret) { + dev_err(&pdev->dev, "Unable to prepare cec clock\n"); + return ret; + } + + cec->clk_hdmi_cec = devm_clk_get(&pdev->dev, "hdmi-cec"); + if (!IS_ERR(cec->clk_hdmi_cec)) { + ret = clk_prepare(cec->clk_hdmi_cec); + if (ret) { + dev_err(&pdev->dev, "Unable to prepare hdmi-cec clock\n"); + return ret; + } + } + + /* + * CEC_CAP_PHYS_ADDR caps should be removed when a cec notifier is + * available for example when a drm driver can provide edid + */ + cec->adap = cec_allocate_adapter(&stm32_cec_adap_ops, cec, + CEC_NAME, caps, CEC_MAX_LOG_ADDRS); + ret = PTR_ERR_OR_ZERO(cec->adap); + if (ret) + return ret; + + ret = cec_register_adapter(cec->adap, &pdev->dev); + if (ret) { + cec_delete_adapter(cec->adap); + return ret; + } + + cec_hw_init(cec); + + platform_set_drvdata(pdev, cec); + + return 0; +} + +static int stm32_cec_remove(struct platform_device *pdev) +{ + struct stm32_cec *cec = platform_get_drvdata(pdev); + + clk_unprepare(cec->clk_cec); + clk_unprepare(cec->clk_hdmi_cec); + + cec_unregister_adapter(cec->adap); + + return 0; +} + +static const struct of_device_id stm32_cec_of_match[] = { + { .compatible = "st,stm32-cec" }, + { /* end node */ } +}; +MODULE_DEVICE_TABLE(of, stm32_cec_of_match); + +static struct platform_driver stm32_cec_driver = { + .probe = stm32_cec_probe, + .remove = stm32_cec_remove, + .driver = { + .name = CEC_NAME, + .of_match_table = stm32_cec_of_match, + }, +}; + +module_platform_driver(stm32_cec_driver); + +MODULE_AUTHOR("Benjamin Gaignard "); +MODULE_AUTHOR("Yannick Fertre "); +MODULE_DESCRIPTION("STMicroelectronics STM32 Consumer Electronics Control"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-58-ga151 From 6709e03cbaad4190565ff52bb26e6b90da5d98af Mon Sep 17 00:00:00 2001 From: Sean Young Date: Fri, 2 Jun 2017 07:19:39 -0300 Subject: [media] sir_ir: annotate hardware config module parameters This module was merged after commit 5a8fc6a3cebb ("Annotate hardware config module parameters in drivers/media/"), so add add the missing hardware annotations. Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/sir_ir.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/rc/sir_ir.c b/drivers/media/rc/sir_ir.c index 5ee3a2301c05..20234ba0b318 100644 --- a/drivers/media/rc/sir_ir.c +++ b/drivers/media/rc/sir_ir.c @@ -400,10 +400,10 @@ MODULE_DESCRIPTION("Infrared receiver driver for SIR type serial ports"); MODULE_AUTHOR("Milan Pikula"); MODULE_LICENSE("GPL"); -module_param(io, int, 0444); +module_param_hw(io, int, ioport, 0444); MODULE_PARM_DESC(io, "I/O address base (0x3f8 or 0x2f8)"); -module_param(irq, int, 0444); +module_param_hw(irq, int, irq, 0444); MODULE_PARM_DESC(irq, "Interrupt (4 or 3)"); module_param(threshold, int, 0444); -- cgit v1.2.3-58-ga151 From f56928abaa6d73613d21f0cdcb7fd0b7f339b5bd Mon Sep 17 00:00:00 2001 From: David Härdeman Date: Wed, 3 May 2017 07:04:00 -0300 Subject: [media] rc-core: cleanup rc_register_device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The device core infrastructure is based on the presumption that once a driver calls device_add(), it must be ready to accept userspace interaction. This requires splitting rc_setup_rx_device() into two functions and reorganizing rc_register_device() so that as much work as possible is performed before calling device_add(). Signed-off-by: David Härdeman Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/rc-core-priv.h | 2 ++ drivers/media/rc/rc-ir-raw.c | 36 +++++++++++++------- drivers/media/rc/rc-main.c | 75 ++++++++++++++++++++++++++--------------- 3 files changed, 74 insertions(+), 39 deletions(-) diff --git a/drivers/media/rc/rc-core-priv.h b/drivers/media/rc/rc-core-priv.h index 0455b273c2fc..b3e7cac2c3ee 100644 --- a/drivers/media/rc/rc-core-priv.h +++ b/drivers/media/rc/rc-core-priv.h @@ -263,7 +263,9 @@ int ir_raw_gen_pl(struct ir_raw_event **ev, unsigned int max, * Routines from rc-raw.c to be used internally and by decoders */ u64 ir_raw_get_allowed_protocols(void); +int ir_raw_event_prepare(struct rc_dev *dev); int ir_raw_event_register(struct rc_dev *dev); +void ir_raw_event_free(struct rc_dev *dev); void ir_raw_event_unregister(struct rc_dev *dev); int ir_raw_handler_register(struct ir_raw_handler *ir_raw_handler); void ir_raw_handler_unregister(struct ir_raw_handler *ir_raw_handler); diff --git a/drivers/media/rc/rc-ir-raw.c b/drivers/media/rc/rc-ir-raw.c index a2fc1a1d58b0..b6d256f03847 100644 --- a/drivers/media/rc/rc-ir-raw.c +++ b/drivers/media/rc/rc-ir-raw.c @@ -486,15 +486,18 @@ EXPORT_SYMBOL(ir_raw_encode_scancode); /* * Used to (un)register raw event clients */ -int ir_raw_event_register(struct rc_dev *dev) +int ir_raw_event_prepare(struct rc_dev *dev) { - int rc; - struct ir_raw_handler *handler; - struct task_struct *thread; + static bool raw_init; /* 'false' default value, raw decoders loaded? */ if (!dev) return -EINVAL; + if (!raw_init) { + request_module("ir-lirc-codec"); + raw_init = true; + } + dev->raw = kzalloc(sizeof(*dev->raw), GFP_KERNEL); if (!dev->raw) return -ENOMEM; @@ -503,6 +506,14 @@ int ir_raw_event_register(struct rc_dev *dev) dev->change_protocol = change_protocol; INIT_KFIFO(dev->raw->kfifo); + return 0; +} + +int ir_raw_event_register(struct rc_dev *dev) +{ + struct ir_raw_handler *handler; + struct task_struct *thread; + /* * raw transmitters do not need any event registration * because the event is coming from userspace @@ -511,10 +522,8 @@ int ir_raw_event_register(struct rc_dev *dev) thread = kthread_run(ir_raw_event_thread, dev->raw, "rc%u", dev->minor); - if (IS_ERR(thread)) { - rc = PTR_ERR(thread); - goto out; - } + if (IS_ERR(thread)) + return PTR_ERR(thread); dev->raw->thread = thread; } @@ -527,11 +536,15 @@ int ir_raw_event_register(struct rc_dev *dev) mutex_unlock(&ir_raw_handler_lock); return 0; +} + +void ir_raw_event_free(struct rc_dev *dev) +{ + if (!dev) + return; -out: kfree(dev->raw); dev->raw = NULL; - return rc; } void ir_raw_event_unregister(struct rc_dev *dev) @@ -550,8 +563,7 @@ void ir_raw_event_unregister(struct rc_dev *dev) handler->raw_unregister(dev); mutex_unlock(&ir_raw_handler_lock); - kfree(dev->raw); - dev->raw = NULL; + ir_raw_event_free(dev); } /* diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c index 802e559cc30e..f3bc9f4e2b96 100644 --- a/drivers/media/rc/rc-main.c +++ b/drivers/media/rc/rc-main.c @@ -1663,7 +1663,7 @@ struct rc_dev *devm_rc_allocate_device(struct device *dev, } EXPORT_SYMBOL_GPL(devm_rc_allocate_device); -static int rc_setup_rx_device(struct rc_dev *dev) +static int rc_prepare_rx_device(struct rc_dev *dev) { int rc; struct rc_map *rc_map; @@ -1708,10 +1708,22 @@ static int rc_setup_rx_device(struct rc_dev *dev) dev->input_dev->phys = dev->input_phys; dev->input_dev->name = dev->input_name; + return 0; + +out_table: + ir_free_table(&dev->rc_map); + + return rc; +} + +static int rc_setup_rx_device(struct rc_dev *dev) +{ + int rc; + /* rc_open will be called here */ rc = input_register_device(dev->input_dev); if (rc) - goto out_table; + return rc; /* * Default delay of 250ms is too short for some protocols, especially @@ -1729,27 +1741,23 @@ static int rc_setup_rx_device(struct rc_dev *dev) dev->input_dev->rep[REP_PERIOD] = 125; return 0; - -out_table: - ir_free_table(&dev->rc_map); - - return rc; } static void rc_free_rx_device(struct rc_dev *dev) { - if (!dev || dev->driver_type == RC_DRIVER_IR_RAW_TX) + if (!dev) return; - ir_free_table(&dev->rc_map); + if (dev->input_dev) { + input_unregister_device(dev->input_dev); + dev->input_dev = NULL; + } - input_unregister_device(dev->input_dev); - dev->input_dev = NULL; + ir_free_table(&dev->rc_map); } int rc_register_device(struct rc_dev *dev) { - static bool raw_init; /* 'false' default value, raw decoders loaded? */ const char *path; int attr = 0; int minor; @@ -1776,30 +1784,39 @@ int rc_register_device(struct rc_dev *dev) dev->sysfs_groups[attr++] = &rc_dev_wakeup_filter_attr_grp; dev->sysfs_groups[attr++] = NULL; + if (dev->driver_type == RC_DRIVER_IR_RAW || + dev->driver_type == RC_DRIVER_IR_RAW_TX) { + rc = ir_raw_event_prepare(dev); + if (rc < 0) + goto out_minor; + } + + if (dev->driver_type != RC_DRIVER_IR_RAW_TX) { + rc = rc_prepare_rx_device(dev); + if (rc) + goto out_raw; + } + rc = device_add(&dev->dev); if (rc) - goto out_unlock; + goto out_rx_free; path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL); dev_info(&dev->dev, "%s as %s\n", dev->input_name ?: "Unspecified device", path ?: "N/A"); kfree(path); + if (dev->driver_type != RC_DRIVER_IR_RAW_TX) { + rc = rc_setup_rx_device(dev); + if (rc) + goto out_dev; + } + if (dev->driver_type == RC_DRIVER_IR_RAW || dev->driver_type == RC_DRIVER_IR_RAW_TX) { - if (!raw_init) { - request_module_nowait("ir-lirc-codec"); - raw_init = true; - } rc = ir_raw_event_register(dev); if (rc < 0) - goto out_dev; - } - - if (dev->driver_type != RC_DRIVER_IR_RAW_TX) { - rc = rc_setup_rx_device(dev); - if (rc) - goto out_raw; + goto out_rx; } /* Allow the RC sysfs nodes to be accessible */ @@ -1811,11 +1828,15 @@ int rc_register_device(struct rc_dev *dev) return 0; -out_raw: - ir_raw_event_unregister(dev); +out_rx: + rc_free_rx_device(dev); out_dev: device_del(&dev->dev); -out_unlock: +out_rx_free: + ir_free_table(&dev->rc_map); +out_raw: + ir_raw_event_free(dev); +out_minor: ida_simple_remove(&rc_ida, minor); return rc; } -- cgit v1.2.3-58-ga151 From 18726a349de262f5bf03fab73fc7c46e79e6c41e Mon Sep 17 00:00:00 2001 From: David Härdeman Date: Thu, 27 Apr 2017 17:34:08 -0300 Subject: [media] rc-core: cleanup rc_register_device pt2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that rc_register_device() is reorganised, the dev->initialized hack can be removed. Any driver which calls rc_register_device() must be prepared for the device to go live immediately. The dev->initialized commits that are relevant are commit c73bbaa4ec3e ("[media] rc-core: don't lock device at rc_register_device()") and commit 08aeb7c9a42a ("[media] rc: add locking to fix register/show race"). The original problem was that show_protocols() would access dev->rc_map.* and various other bits which are now properly initialized before device_add() is called. At the same time, remove the bogus "device is being removed" check. Signed-off-by: David Härdeman Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/rc-main.c | 67 +++++++--------------------------------------- include/media/rc-core.h | 2 -- 2 files changed, 10 insertions(+), 59 deletions(-) diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c index f3bc9f4e2b96..a9eba0013525 100644 --- a/drivers/media/rc/rc-main.c +++ b/drivers/media/rc/rc-main.c @@ -15,7 +15,6 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include -#include #include #include #include @@ -934,8 +933,8 @@ static bool lirc_is_present(void) * It returns the protocol names of supported protocols. * Enabled protocols are printed in brackets. * - * dev->lock is taken to guard against races between device - * registration, store_protocols and show_protocols. + * dev->lock is taken to guard against races between + * store_protocols and show_protocols. */ static ssize_t show_protocols(struct device *device, struct device_attribute *mattr, char *buf) @@ -945,13 +944,6 @@ static ssize_t show_protocols(struct device *device, char *tmp = buf; int i; - /* Device is being removed */ - if (!dev) - return -EINVAL; - - if (!atomic_read(&dev->initialized)) - return -ERESTARTSYS; - mutex_lock(&dev->lock); enabled = dev->enabled_protocols; @@ -1106,8 +1098,8 @@ static void ir_raw_load_modules(u64 *protocols) * See parse_protocol_change() for the valid commands. * Returns @len on success or a negative error code. * - * dev->lock is taken to guard against races between device - * registration, store_protocols and show_protocols. + * dev->lock is taken to guard against races between + * store_protocols and show_protocols. */ static ssize_t store_protocols(struct device *device, struct device_attribute *mattr, @@ -1119,13 +1111,6 @@ static ssize_t store_protocols(struct device *device, u64 old_protocols, new_protocols; ssize_t rc; - /* Device is being removed */ - if (!dev) - return -EINVAL; - - if (!atomic_read(&dev->initialized)) - return -ERESTARTSYS; - IR_dprintk(1, "Normal protocol change requested\n"); current_protocols = &dev->enabled_protocols; filter = &dev->scancode_filter; @@ -1200,7 +1185,7 @@ out: * Bits of the filter value corresponding to set bits in the filter mask are * compared against input scancodes and non-matching scancodes are discarded. * - * dev->lock is taken to guard against races between device registration, + * dev->lock is taken to guard against races between * store_filter and show_filter. */ static ssize_t show_filter(struct device *device, @@ -1212,13 +1197,6 @@ static ssize_t show_filter(struct device *device, struct rc_scancode_filter *filter; u32 val; - /* Device is being removed */ - if (!dev) - return -EINVAL; - - if (!atomic_read(&dev->initialized)) - return -ERESTARTSYS; - mutex_lock(&dev->lock); if (fattr->type == RC_FILTER_NORMAL) @@ -1251,7 +1229,7 @@ static ssize_t show_filter(struct device *device, * Bits of the filter value corresponding to set bits in the filter mask are * compared against input scancodes and non-matching scancodes are discarded. * - * dev->lock is taken to guard against races between device registration, + * dev->lock is taken to guard against races between * store_filter and show_filter. */ static ssize_t store_filter(struct device *device, @@ -1265,13 +1243,6 @@ static ssize_t store_filter(struct device *device, unsigned long val; int (*set_filter)(struct rc_dev *dev, struct rc_scancode_filter *filter); - /* Device is being removed */ - if (!dev) - return -EINVAL; - - if (!atomic_read(&dev->initialized)) - return -ERESTARTSYS; - ret = kstrtoul(buf, 0, &val); if (ret < 0) return ret; @@ -1372,8 +1343,8 @@ static const char * const proto_variant_names[] = { * It returns the protocol names of supported protocols. * The enabled protocols are printed in brackets. * - * dev->lock is taken to guard against races between device - * registration, store_protocols and show_protocols. + * dev->lock is taken to guard against races between + * store_wakeup_protocols and show_wakeup_protocols. */ static ssize_t show_wakeup_protocols(struct device *device, struct device_attribute *mattr, @@ -1385,13 +1356,6 @@ static ssize_t show_wakeup_protocols(struct device *device, char *tmp = buf; int i; - /* Device is being removed */ - if (!dev) - return -EINVAL; - - if (!atomic_read(&dev->initialized)) - return -ERESTARTSYS; - mutex_lock(&dev->lock); allowed = dev->allowed_wakeup_protocols; @@ -1431,8 +1395,8 @@ static ssize_t show_wakeup_protocols(struct device *device, * It is trigged by writing to /sys/class/rc/rc?/wakeup_protocols. * Returns @len on success or a negative error code. * - * dev->lock is taken to guard against races between device - * registration, store_protocols and show_protocols. + * dev->lock is taken to guard against races between + * store_wakeup_protocols and show_wakeup_protocols. */ static ssize_t store_wakeup_protocols(struct device *device, struct device_attribute *mattr, @@ -1444,13 +1408,6 @@ static ssize_t store_wakeup_protocols(struct device *device, u64 allowed; int i; - /* Device is being removed */ - if (!dev) - return -EINVAL; - - if (!atomic_read(&dev->initialized)) - return -ERESTARTSYS; - mutex_lock(&dev->lock); allowed = dev->allowed_wakeup_protocols; @@ -1773,7 +1730,6 @@ int rc_register_device(struct rc_dev *dev) dev->minor = minor; dev_set_name(&dev->dev, "rc%u", dev->minor); dev_set_drvdata(&dev->dev, dev); - atomic_set(&dev->initialized, 0); dev->dev.groups = dev->sysfs_groups; if (dev->driver_type != RC_DRIVER_IR_RAW_TX) @@ -1819,9 +1775,6 @@ int rc_register_device(struct rc_dev *dev) goto out_rx; } - /* Allow the RC sysfs nodes to be accessible */ - atomic_set(&dev->initialized, 1); - IR_dprintk(1, "Registered rc%u (driver: %s)\n", dev->minor, dev->driver_name ? dev->driver_name : "unknown"); diff --git a/include/media/rc-core.h b/include/media/rc-core.h index 73ddd721d7ba..78dea39a9b39 100644 --- a/include/media/rc-core.h +++ b/include/media/rc-core.h @@ -70,7 +70,6 @@ enum rc_filter_type { /** * struct rc_dev - represents a remote control device * @dev: driver model's view of this device - * @initialized: 1 if the device init has completed, 0 otherwise * @managed_alloc: devm_rc_allocate_device was used to create rc_dev * @sysfs_groups: sysfs attribute groups * @input_name: name of the input child device @@ -137,7 +136,6 @@ enum rc_filter_type { */ struct rc_dev { struct device dev; - atomic_t initialized; bool managed_alloc; const struct attribute_group *sysfs_groups[5]; const char *input_name; -- cgit v1.2.3-58-ga151 From 17d716e9264c43f541eccbd5b7b11278b1fb65cb Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Tue, 25 Apr 2017 03:19:43 -0300 Subject: [media] s5p-jpeg: fix recursive spinlock acquisition v4l2_m2m_job_finish(), which is called from the interrupt handler with slock acquired, can call the device_run() hook immediately if another context was in the queue. This hook also acquires slock, resulting in a deadlock for this scenario. Fix this by releasing slock right before calling v4l2_m2m_job_finish(). This is safe to do as the state of the hardware cannot change before v4l2_m2m_job_finish() is called anyway. Signed-off-by: Alexandre Courbot Acked-by: Jacek Anaszewski Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-jpeg/jpeg-core.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.c b/drivers/media/platform/s5p-jpeg/jpeg-core.c index 1da2c94e1dca..d1e3ebb22577 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-core.c +++ b/drivers/media/platform/s5p-jpeg/jpeg-core.c @@ -2642,13 +2642,13 @@ static irqreturn_t s5p_jpeg_irq(int irq, void *dev_id) if (curr_ctx->mode == S5P_JPEG_ENCODE) vb2_set_plane_payload(&dst_buf->vb2_buf, 0, payload_size); v4l2_m2m_buf_done(dst_buf, state); - v4l2_m2m_job_finish(jpeg->m2m_dev, curr_ctx->fh.m2m_ctx); curr_ctx->subsampling = s5p_jpeg_get_subsampling_mode(jpeg->regs); spin_unlock(&jpeg->slock); s5p_jpeg_clear_int(jpeg->regs); + v4l2_m2m_job_finish(jpeg->m2m_dev, curr_ctx->fh.m2m_ctx); return IRQ_HANDLED; } @@ -2707,11 +2707,12 @@ static irqreturn_t exynos4_jpeg_irq(int irq, void *priv) v4l2_m2m_buf_done(dst_vb, VB2_BUF_STATE_ERROR); } - v4l2_m2m_job_finish(jpeg->m2m_dev, curr_ctx->fh.m2m_ctx); if (jpeg->variant->version == SJPEG_EXYNOS4) curr_ctx->subsampling = exynos4_jpeg_get_frame_fmt(jpeg->regs); spin_unlock(&jpeg->slock); + + v4l2_m2m_job_finish(jpeg->m2m_dev, curr_ctx->fh.m2m_ctx); return IRQ_HANDLED; } @@ -2770,10 +2771,15 @@ static irqreturn_t exynos3250_jpeg_irq(int irq, void *dev_id) if (curr_ctx->mode == S5P_JPEG_ENCODE) vb2_set_plane_payload(&dst_buf->vb2_buf, 0, payload_size); v4l2_m2m_buf_done(dst_buf, state); - v4l2_m2m_job_finish(jpeg->m2m_dev, curr_ctx->fh.m2m_ctx); curr_ctx->subsampling = exynos3250_jpeg_get_subsampling_mode(jpeg->regs); + + spin_unlock(&jpeg->slock); + + v4l2_m2m_job_finish(jpeg->m2m_dev, curr_ctx->fh.m2m_ctx); + return IRQ_HANDLED; + exit_unlock: spin_unlock(&jpeg->slock); return IRQ_HANDLED; -- cgit v1.2.3-58-ga151 From 5ebbdf60d14b3dd2d88f7268c430ec37ca5b4eac Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Sun, 14 May 2017 13:47:41 -0300 Subject: [media] s5p-mfc: fix spelling mistake: "destionation" -> "destination" Trivial fix to spelling mistake in mfc_err error messages Signed-off-by: Colin Ian King Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c | 2 +- drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c index b41ee608c171..0913881219ff 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c @@ -1293,7 +1293,7 @@ static int s5p_mfc_run_init_dec_buffers(struct s5p_mfc_ctx *ctx) * First set the output frame buffers */ if (ctx->capture_state != QUEUE_BUFS_MMAPED) { - mfc_err("It seems that not all destionation buffers were mmaped\nMFC requires that all destination are mmaped before starting processing\n"); + mfc_err("It seems that not all destination buffers were mmaped\nMFC requires that all destination are mmaped before starting processing\n"); return -EAGAIN; } if (list_empty(&ctx->src_queue)) { diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c index 85880e9106be..88dbb9c341ec 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c @@ -1650,7 +1650,7 @@ static inline int s5p_mfc_run_init_dec_buffers(struct s5p_mfc_ctx *ctx) * s5p_mfc_alloc_dec_buffers(ctx); */ if (ctx->capture_state != QUEUE_BUFS_MMAPED) { - mfc_err("It seems that not all destionation buffers were\n" + mfc_err("It seems that not all destination buffers were\n" "mmaped.MFC requires that all destination are mmaped\n" "before starting processing.\n"); return -EAGAIN; -- cgit v1.2.3-58-ga151 From 6428f63b1da10dd326a5d7092c2be10286ef8aa6 Mon Sep 17 00:00:00 2001 From: Benjamin Gaignard Date: Mon, 29 May 2017 12:46:03 -0300 Subject: [media] exynos4-is: use devm_of_platform_populate() Usage of devm_of_platform_populate() simplify driver code and save somes lines Signed-off-by: Benjamin Gaignard Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/exynos4-is/fimc-is.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/media/platform/exynos4-is/fimc-is.c b/drivers/media/platform/exynos4-is/fimc-is.c index 7f92144a1de3..340d906db370 100644 --- a/drivers/media/platform/exynos4-is/fimc-is.c +++ b/drivers/media/platform/exynos4-is/fimc-is.c @@ -854,7 +854,7 @@ static int fimc_is_probe(struct platform_device *pdev) vb2_dma_contig_set_max_seg_size(dev, DMA_BIT_MASK(32)); - ret = of_platform_populate(dev->of_node, NULL, NULL, dev); + ret = devm_of_platform_populate(dev); if (ret < 0) goto err_pm; @@ -864,7 +864,7 @@ static int fimc_is_probe(struct platform_device *pdev) */ ret = fimc_is_register_subdevs(is); if (ret < 0) - goto err_of_dep; + goto err_pm; ret = fimc_is_debugfs_create(is); if (ret < 0) @@ -883,8 +883,6 @@ err_dfs: fimc_is_debugfs_remove(is); err_sd: fimc_is_unregister_subdevs(is); -err_of_dep: - of_platform_depopulate(dev); err_pm: if (!pm_runtime_enabled(dev)) fimc_is_runtime_suspend(dev); @@ -946,7 +944,6 @@ static int fimc_is_remove(struct platform_device *pdev) if (!pm_runtime_status_suspended(dev)) fimc_is_runtime_suspend(dev); free_irq(is->irq, is); - of_platform_depopulate(dev); fimc_is_unregister_subdevs(is); vb2_dma_contig_clear_max_seg_size(dev); fimc_is_put_clocks(is); -- cgit v1.2.3-58-ga151 From 57612890b7e7c511069b104deeca3c71db312a28 Mon Sep 17 00:00:00 2001 From: Thibault Saunier Date: Wed, 1 Mar 2017 08:51:07 -0300 Subject: [media] exynos-gsc: Use user configured colorspace if provided Use colorspace provided by the user as we are only doing scaling and color encoding conversion, we won't be able to transform the colorspace itself and the colorspace won't mater in that operation. Also always use output colorspace on the capture side. If the user does not provide a colorspace do not make it up, we might later while processing need to figure out the colorspace, which is possible depending on the frame size but do not ever guess and leak that guess to the userspace. Signed-off-by: Javier Martinez Canillas Signed-off-by: Thibault Saunier Reviewed-by: Andrzej Hajda Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/exynos-gsc/gsc-core.c | 9 ++++----- drivers/media/platform/exynos-gsc/gsc-core.h | 1 + 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/media/platform/exynos-gsc/gsc-core.c b/drivers/media/platform/exynos-gsc/gsc-core.c index 59a634201830..0241168c85af 100644 --- a/drivers/media/platform/exynos-gsc/gsc-core.c +++ b/drivers/media/platform/exynos-gsc/gsc-core.c @@ -454,6 +454,7 @@ int gsc_try_fmt_mplane(struct gsc_ctx *ctx, struct v4l2_format *f) } else { min_w = variant->pix_min->target_rot_dis_w; min_h = variant->pix_min->target_rot_dis_h; + pix_mp->colorspace = ctx->out_colorspace; } pr_debug("mod_x: %d, mod_y: %d, max_w: %d, max_h = %d", @@ -472,10 +473,8 @@ int gsc_try_fmt_mplane(struct gsc_ctx *ctx, struct v4l2_format *f) pix_mp->num_planes = fmt->num_planes; - if (pix_mp->width >= 1280) /* HD */ - pix_mp->colorspace = V4L2_COLORSPACE_REC709; - else /* SD */ - pix_mp->colorspace = V4L2_COLORSPACE_SMPTE170M; + if (V4L2_TYPE_IS_OUTPUT(f->type)) + ctx->out_colorspace = pix_mp->colorspace; for (i = 0; i < pix_mp->num_planes; ++i) { struct v4l2_plane_pix_format *plane_fmt = &pix_mp->plane_fmt[i]; @@ -519,8 +518,8 @@ int gsc_g_fmt_mplane(struct gsc_ctx *ctx, struct v4l2_format *f) pix_mp->height = frame->f_height; pix_mp->field = V4L2_FIELD_NONE; pix_mp->pixelformat = frame->fmt->pixelformat; - pix_mp->colorspace = V4L2_COLORSPACE_REC709; pix_mp->num_planes = frame->fmt->num_planes; + pix_mp->colorspace = ctx->out_colorspace; for (i = 0; i < pix_mp->num_planes; ++i) { pix_mp->plane_fmt[i].bytesperline = (frame->f_width * diff --git a/drivers/media/platform/exynos-gsc/gsc-core.h b/drivers/media/platform/exynos-gsc/gsc-core.h index 696217e9af66..715d9c9d8d30 100644 --- a/drivers/media/platform/exynos-gsc/gsc-core.h +++ b/drivers/media/platform/exynos-gsc/gsc-core.h @@ -376,6 +376,7 @@ struct gsc_ctx { struct v4l2_ctrl_handler ctrl_handler; struct gsc_ctrls gsc_ctrls; bool ctrls_rdy; + enum v4l2_colorspace out_colorspace; }; void gsc_set_prefbuf(struct gsc_dev *gsc, struct gsc_frame *frm); -- cgit v1.2.3-58-ga151 From 3966f5ce020cf24b814dbddf45d092aa2d133c1e Mon Sep 17 00:00:00 2001 From: Nicholas Mc Guire Date: Mon, 12 Dec 2016 23:58:02 -0200 Subject: [media] s5k6aa: set usleep_range() range greater than 0 As this is not in atomic context and it does not seem like a critical timing setting a range of 1ms allows the timer subsystem to optimize the hrtimer here. Signed-off-by: Nicholas Mc Guire Acked-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/s5k6aa.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/i2c/s5k6aa.c b/drivers/media/i2c/s5k6aa.c index faee11383cb7..9fd254a8e20d 100644 --- a/drivers/media/i2c/s5k6aa.c +++ b/drivers/media/i2c/s5k6aa.c @@ -838,7 +838,7 @@ static int __s5k6aa_power_on(struct s5k6aa *s5k6aa) if (s5k6aa->s_power) ret = s5k6aa->s_power(1); - usleep_range(4000, 4000); + usleep_range(4000, 5000); if (s5k6aa_gpio_deassert(s5k6aa, RST)) msleep(20); -- cgit v1.2.3-58-ga151 From acec3630155763c170c7ae6508cf973355464508 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Fri, 2 Jun 2017 00:43:41 -0300 Subject: [media] s3c-camif: fix arguments position in a function call Fix the position of arguments so camif->colorfx_cb, camif->colorfx_cr are passed in proper order to the camif_hw_set_effect() function. Addresses-Coverity-ID: 1248800 Addresses-Coverity-ID: 1269141 [s.nawrocki@samsung.com: edited commit message ] Signed-off-by: Gustavo A. R. Silva Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s3c-camif/camif-capture.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/s3c-camif/camif-capture.c b/drivers/media/platform/s3c-camif/camif-capture.c index 1b30be72f4f9..25c7a7d42292 100644 --- a/drivers/media/platform/s3c-camif/camif-capture.c +++ b/drivers/media/platform/s3c-camif/camif-capture.c @@ -80,7 +80,7 @@ static int s3c_camif_hw_init(struct camif_dev *camif, struct camif_vp *vp) camif_hw_set_test_pattern(camif, camif->test_pattern); if (variant->has_img_effect) camif_hw_set_effect(camif, camif->colorfx, - camif->colorfx_cb, camif->colorfx_cr); + camif->colorfx_cr, camif->colorfx_cb); if (variant->ip_revision == S3C6410_CAMIF_IP_REV) camif_hw_set_input_path(vp); camif_cfg_video_path(vp); @@ -364,7 +364,7 @@ irqreturn_t s3c_camif_irq_handler(int irq, void *priv) camif_hw_set_test_pattern(camif, camif->test_pattern); if (camif->variant->has_img_effect) camif_hw_set_effect(camif, camif->colorfx, - camif->colorfx_cb, camif->colorfx_cr); + camif->colorfx_cr, camif->colorfx_cb); vp->state &= ~ST_VP_CONFIG; } unlock: -- cgit v1.2.3-58-ga151 From 23111ec304166750479e17318b7175ff7db0d6ac Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 7 Jun 2017 11:46:08 -0300 Subject: [media] cec: add cec_s_phys_addr_from_edid helper function This function simplifies the integration of CEC in DRM drivers. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/media/kapi/cec-core.rst | 8 ++++++++ drivers/media/cec/cec-adap.c | 14 ++++++++++++++ include/media/cec.h | 9 +++++++++ 3 files changed, 31 insertions(+) diff --git a/Documentation/media/kapi/cec-core.rst b/Documentation/media/kapi/cec-core.rst index 7a04c5386dc8..278b358b2f2e 100644 --- a/Documentation/media/kapi/cec-core.rst +++ b/Documentation/media/kapi/cec-core.rst @@ -306,6 +306,14 @@ then the CEC adapter will be disabled. If you change a valid physical address to another valid physical address, then this function will first set the address to CEC_PHYS_ADDR_INVALID before enabling the new physical address. +.. c:function:: + void cec_s_phys_addr_from_edid(struct cec_adapter *adap, + const struct edid *edid); + +A helper function that extracts the physical address from the edid struct +and calls cec_s_phys_addr() with that address, or CEC_PHYS_ADDR_INVALID +if the EDID did not contain a physical address or edid was a NULL pointer. + .. c:function:: int cec_s_log_addrs(struct cec_adapter *adap, struct cec_log_addrs *log_addrs, bool block); diff --git a/drivers/media/cec/cec-adap.c b/drivers/media/cec/cec-adap.c index fd6d9cccade7..61e39bbe3cf9 100644 --- a/drivers/media/cec/cec-adap.c +++ b/drivers/media/cec/cec-adap.c @@ -28,6 +28,8 @@ #include #include +#include + #include "cec-priv.h" static void cec_fill_msg_report_features(struct cec_adapter *adap, @@ -1408,6 +1410,18 @@ void cec_s_phys_addr(struct cec_adapter *adap, u16 phys_addr, bool block) } EXPORT_SYMBOL_GPL(cec_s_phys_addr); +void cec_s_phys_addr_from_edid(struct cec_adapter *adap, + const struct edid *edid) +{ + u16 pa = CEC_PHYS_ADDR_INVALID; + + if (edid && edid->extensions) + pa = cec_get_edid_phys_addr((const u8 *)edid, + EDID_LENGTH * (edid->extensions + 1), NULL); + cec_s_phys_addr(adap, pa, false); +} +EXPORT_SYMBOL_GPL(cec_s_phys_addr_from_edid); + /* * Called from either the ioctl or a driver to set the logical addresses. * diff --git a/include/media/cec.h b/include/media/cec.h index bfa88d4d67e1..a548b292eeb1 100644 --- a/include/media/cec.h +++ b/include/media/cec.h @@ -206,6 +206,8 @@ static inline bool cec_is_sink(const struct cec_adapter *adap) #define cec_phys_addr_exp(pa) \ ((pa) >> 12), ((pa) >> 8) & 0xf, ((pa) >> 4) & 0xf, (pa) & 0xf +struct edid; + #if IS_ENABLED(CONFIG_CEC_CORE) struct cec_adapter *cec_allocate_adapter(const struct cec_adap_ops *ops, void *priv, const char *name, u32 caps, u8 available_las); @@ -217,6 +219,8 @@ int cec_s_log_addrs(struct cec_adapter *adap, struct cec_log_addrs *log_addrs, bool block); void cec_s_phys_addr(struct cec_adapter *adap, u16 phys_addr, bool block); +void cec_s_phys_addr_from_edid(struct cec_adapter *adap, + const struct edid *edid); int cec_transmit_msg(struct cec_adapter *adap, struct cec_msg *msg, bool block); @@ -326,6 +330,11 @@ static inline void cec_s_phys_addr(struct cec_adapter *adap, u16 phys_addr, { } +static inline void cec_s_phys_addr_from_edid(struct cec_adapter *adap, + const struct edid *edid) +{ +} + static inline u16 cec_get_edid_phys_addr(const u8 *edid, unsigned int size, unsigned int *offset) { -- cgit v1.2.3-58-ga151 From 135327899d2874f2b65b3a3626b37d87df9e6a05 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 7 Jun 2017 11:46:09 -0300 Subject: [media] cec: add cec_phys_addr_invalidate() helper function Simplifies setting the physical address to CEC_PHYS_ADDR_INVALID. Signed-off-by: Hans Verkuil Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- include/media/cec.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/include/media/cec.h b/include/media/cec.h index a548b292eeb1..3ce73951591e 100644 --- a/include/media/cec.h +++ b/include/media/cec.h @@ -360,4 +360,17 @@ static inline int cec_phys_addr_validate(u16 phys_addr, u16 *parent, u16 *port) #endif +/** + * cec_phys_addr_invalidate() - set the physical address to INVALID + * + * @adap: the CEC adapter + * + * This is a simple helper function to invalidate the physical + * address. + */ +static inline void cec_phys_addr_invalidate(struct cec_adapter *adap) +{ + cec_s_phys_addr(adap, CEC_PHYS_ADDR_INVALID, false); +} + #endif /* _MEDIA_CEC_H */ -- cgit v1.2.3-58-ga151 From c94cdc1e0c3589856de9d4ecafeeffdb349a8e0b Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 7 Jun 2017 11:46:10 -0300 Subject: [media] cec: add cec_transmit_attempt_done helper function A simpler variant of cec_transmit_done to be used where the HW does just a single attempt at a transmit. So if the status indicates an error, then the corresponding error count will always be 1 and this function figures that out based on the status argument. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/media/kapi/cec-core.rst | 10 ++++++++++ drivers/media/cec/cec-adap.c | 26 ++++++++++++++++++++++++++ include/media/cec.h | 6 ++++++ 3 files changed, 42 insertions(+) diff --git a/Documentation/media/kapi/cec-core.rst b/Documentation/media/kapi/cec-core.rst index 278b358b2f2e..8a65c69ed071 100644 --- a/Documentation/media/kapi/cec-core.rst +++ b/Documentation/media/kapi/cec-core.rst @@ -194,6 +194,11 @@ When a transmit finished (successfully or otherwise): void cec_transmit_done(struct cec_adapter *adap, u8 status, u8 arb_lost_cnt, u8 nack_cnt, u8 low_drive_cnt, u8 error_cnt); +or: + +.. c:function:: + void cec_transmit_attempt_done(struct cec_adapter *adap, u8 status); + The status can be one of: CEC_TX_STATUS_OK: @@ -231,6 +236,11 @@ to 1, if the hardware does support retry then either set these counters to 0 if the hardware provides no feedback of which errors occurred and how many times, or fill in the correct values as reported by the hardware. +The cec_transmit_attempt_done() function is a helper for cases where the +hardware never retries, so the transmit is always for just a single +attempt. It will call cec_transmit_done() in turn, filling in 1 for the +count argument corresponding to the status. Or all 0 if the status was OK. + When a CEC message was received: .. c:function:: diff --git a/drivers/media/cec/cec-adap.c b/drivers/media/cec/cec-adap.c index 61e39bbe3cf9..bd76c15ade4f 100644 --- a/drivers/media/cec/cec-adap.c +++ b/drivers/media/cec/cec-adap.c @@ -553,6 +553,32 @@ unlock: } EXPORT_SYMBOL_GPL(cec_transmit_done); +void cec_transmit_attempt_done(struct cec_adapter *adap, u8 status) +{ + switch (status) { + case CEC_TX_STATUS_OK: + cec_transmit_done(adap, status, 0, 0, 0, 0); + return; + case CEC_TX_STATUS_ARB_LOST: + cec_transmit_done(adap, status, 1, 0, 0, 0); + return; + case CEC_TX_STATUS_NACK: + cec_transmit_done(adap, status, 0, 1, 0, 0); + return; + case CEC_TX_STATUS_LOW_DRIVE: + cec_transmit_done(adap, status, 0, 0, 1, 0); + return; + case CEC_TX_STATUS_ERROR: + cec_transmit_done(adap, status, 0, 0, 0, 1); + return; + default: + /* Should never happen */ + WARN(1, "cec-%s: invalid status 0x%02x\n", adap->name, status); + return; + } +} +EXPORT_SYMBOL_GPL(cec_transmit_attempt_done); + /* * Called when waiting for a reply times out. */ diff --git a/include/media/cec.h b/include/media/cec.h index 3ce73951591e..a2e184d1df00 100644 --- a/include/media/cec.h +++ b/include/media/cec.h @@ -227,6 +227,12 @@ int cec_transmit_msg(struct cec_adapter *adap, struct cec_msg *msg, /* Called by the adapter */ void cec_transmit_done(struct cec_adapter *adap, u8 status, u8 arb_lost_cnt, u8 nack_cnt, u8 low_drive_cnt, u8 error_cnt); +/* + * Simplified version of cec_transmit_done for hardware that doesn't retry + * failed transmits. So this is always just one attempt in which case + * the status is sufficient. + */ +void cec_transmit_attempt_done(struct cec_adapter *adap, u8 status); void cec_received_msg(struct cec_adapter *adap, struct cec_msg *msg); /** -- cgit v1.2.3-58-ga151 From 2613cc6fd32c4997d8e9473f8249601a5e088ee7 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 7 Jun 2017 11:46:11 -0300 Subject: [media] stih-cec/vivid/pulse8/rainshadow: use cec_transmit_attempt_done Use the helper function cec_transmit_attempt_done instead of cec_transmit_done to simplify the code. Signed-off-by: Hans Verkuil Acked-by: Benjamin Gaignard Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/sti/cec/stih-cec.c | 9 ++++----- drivers/media/platform/vivid/vivid-cec.c | 6 +++--- drivers/media/usb/pulse8-cec/pulse8-cec.c | 9 +++------ drivers/media/usb/rainshadow-cec/rainshadow-cec.c | 9 +++------ 4 files changed, 13 insertions(+), 20 deletions(-) diff --git a/drivers/media/platform/sti/cec/stih-cec.c b/drivers/media/platform/sti/cec/stih-cec.c index 6f9f03670b56..dccbdaebb7a8 100644 --- a/drivers/media/platform/sti/cec/stih-cec.c +++ b/drivers/media/platform/sti/cec/stih-cec.c @@ -226,22 +226,21 @@ static int stih_cec_adap_transmit(struct cec_adapter *adap, u8 attempts, static void stih_tx_done(struct stih_cec *cec, u32 status) { if (status & CEC_TX_ERROR) { - cec_transmit_done(cec->adap, CEC_TX_STATUS_ERROR, 0, 0, 0, 1); + cec_transmit_attempt_done(cec->adap, CEC_TX_STATUS_ERROR); return; } if (status & CEC_TX_ARB_ERROR) { - cec_transmit_done(cec->adap, - CEC_TX_STATUS_ARB_LOST, 1, 0, 0, 0); + cec_transmit_attempt_done(cec->adap, CEC_TX_STATUS_ARB_LOST); return; } if (!(status & CEC_TX_ACK_GET_STS)) { - cec_transmit_done(cec->adap, CEC_TX_STATUS_NACK, 0, 1, 0, 0); + cec_transmit_attempt_done(cec->adap, CEC_TX_STATUS_NACK); return; } - cec_transmit_done(cec->adap, CEC_TX_STATUS_OK, 0, 0, 0, 0); + cec_transmit_attempt_done(cec->adap, CEC_TX_STATUS_OK); } static void stih_rx_done(struct stih_cec *cec, u32 status) diff --git a/drivers/media/platform/vivid/vivid-cec.c b/drivers/media/platform/vivid/vivid-cec.c index 653f4099f737..e15705758969 100644 --- a/drivers/media/platform/vivid/vivid-cec.c +++ b/drivers/media/platform/vivid/vivid-cec.c @@ -34,7 +34,7 @@ void vivid_cec_bus_free_work(struct vivid_dev *dev) cancel_delayed_work_sync(&cw->work); spin_lock(&dev->cec_slock); list_del(&cw->list); - cec_transmit_done(cw->adap, CEC_TX_STATUS_LOW_DRIVE, 0, 0, 1, 0); + cec_transmit_attempt_done(cw->adap, CEC_TX_STATUS_LOW_DRIVE); kfree(cw); } spin_unlock(&dev->cec_slock); @@ -84,7 +84,7 @@ static void vivid_cec_xfer_done_worker(struct work_struct *work) dev->cec_xfer_start_jiffies = 0; list_del(&cw->list); spin_unlock(&dev->cec_slock); - cec_transmit_done(cw->adap, cw->tx_status, 0, valid_dest ? 0 : 1, 0, 0); + cec_transmit_attempt_done(cw->adap, cw->tx_status); /* Broadcast message */ if (adap != dev->cec_rx_adap) @@ -105,7 +105,7 @@ static void vivid_cec_xfer_try_worker(struct work_struct *work) if (dev->cec_xfer_time_jiffies) { list_del(&cw->list); spin_unlock(&dev->cec_slock); - cec_transmit_done(cw->adap, CEC_TX_STATUS_ARB_LOST, 1, 0, 0, 0); + cec_transmit_attempt_done(cw->adap, CEC_TX_STATUS_ARB_LOST); kfree(cw); } else { INIT_DELAYED_WORK(&cw->work, vivid_cec_xfer_done_worker); diff --git a/drivers/media/usb/pulse8-cec/pulse8-cec.c b/drivers/media/usb/pulse8-cec/pulse8-cec.c index 1dfc2de1fe77..c843070f24c1 100644 --- a/drivers/media/usb/pulse8-cec/pulse8-cec.c +++ b/drivers/media/usb/pulse8-cec/pulse8-cec.c @@ -148,18 +148,15 @@ static void pulse8_irq_work_handler(struct work_struct *work) cec_received_msg(pulse8->adap, &pulse8->rx_msg); break; case MSGCODE_TRANSMIT_SUCCEEDED: - cec_transmit_done(pulse8->adap, CEC_TX_STATUS_OK, - 0, 0, 0, 0); + cec_transmit_attempt_done(pulse8->adap, CEC_TX_STATUS_OK); break; case MSGCODE_TRANSMIT_FAILED_ACK: - cec_transmit_done(pulse8->adap, CEC_TX_STATUS_NACK, - 0, 1, 0, 0); + cec_transmit_attempt_done(pulse8->adap, CEC_TX_STATUS_NACK); break; case MSGCODE_TRANSMIT_FAILED_LINE: case MSGCODE_TRANSMIT_FAILED_TIMEOUT_DATA: case MSGCODE_TRANSMIT_FAILED_TIMEOUT_LINE: - cec_transmit_done(pulse8->adap, CEC_TX_STATUS_ERROR, - 0, 0, 0, 1); + cec_transmit_attempt_done(pulse8->adap, CEC_TX_STATUS_ERROR); break; } } diff --git a/drivers/media/usb/rainshadow-cec/rainshadow-cec.c b/drivers/media/usb/rainshadow-cec/rainshadow-cec.c index ad468efc4399..f203699e9c1b 100644 --- a/drivers/media/usb/rainshadow-cec/rainshadow-cec.c +++ b/drivers/media/usb/rainshadow-cec/rainshadow-cec.c @@ -98,16 +98,13 @@ static void rain_process_msg(struct rain *rain) switch (stat) { case 1: - cec_transmit_done(rain->adap, CEC_TX_STATUS_OK, - 0, 0, 0, 0); + cec_transmit_attempt_done(rain->adap, CEC_TX_STATUS_OK); break; case 2: - cec_transmit_done(rain->adap, CEC_TX_STATUS_NACK, - 0, 1, 0, 0); + cec_transmit_attempt_done(rain->adap, CEC_TX_STATUS_NACK); break; default: - cec_transmit_done(rain->adap, CEC_TX_STATUS_LOW_DRIVE, - 0, 0, 0, 1); + cec_transmit_attempt_done(rain->adap, CEC_TX_STATUS_LOW_DRIVE); break; } } -- cgit v1.2.3-58-ga151 From f902c1e95d5dfe5102f8467d69dc51d505f832ee Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 7 Jun 2017 11:46:12 -0300 Subject: [media] cec: add CEC_CAP_NEEDS_HPD Add a new capability CEC_CAP_NEEDS_HPD. If this capability is set then the hardware can only use CEC if the HDMI Hotplug Detect pin is high. Such hardware cannot handle the corner case in the CEC specification where it is possible to transmit messages even if no hotplug signal is present (needed for some displays that turn off the HPD when in standby, but still have CEC enabled). Typically hardware that needs this capability have the HPD wired to the CEC block, often to a 'power' or 'active' pin. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/cec/cec-adap.c | 20 ++++++++++++++------ drivers/media/cec/cec-api.c | 5 ++++- drivers/media/cec/cec-core.c | 1 + include/media/cec.h | 1 + include/uapi/linux/cec.h | 2 ++ 5 files changed, 22 insertions(+), 7 deletions(-) diff --git a/drivers/media/cec/cec-adap.c b/drivers/media/cec/cec-adap.c index bd76c15ade4f..bf45977b2823 100644 --- a/drivers/media/cec/cec-adap.c +++ b/drivers/media/cec/cec-adap.c @@ -368,6 +368,8 @@ int cec_thread_func(void *_adap) * transmit should be canceled. */ err = wait_event_interruptible_timeout(adap->kthread_waitq, + (adap->needs_hpd && + (!adap->is_configured && !adap->is_configuring)) || kthread_should_stop() || (!adap->transmitting && !list_empty(&adap->transmit_queue)), @@ -383,7 +385,9 @@ int cec_thread_func(void *_adap) mutex_lock(&adap->lock); - if (kthread_should_stop()) { + if ((adap->needs_hpd && + (!adap->is_configured && !adap->is_configuring)) || + kthread_should_stop()) { cec_flush(adap); goto unlock; } @@ -682,7 +686,7 @@ int cec_transmit_msg_fh(struct cec_adapter *adap, struct cec_msg *msg, return -EINVAL; } if (!adap->is_configured && !adap->is_configuring) { - if (msg->msg[0] != 0xf0) { + if (adap->needs_hpd || msg->msg[0] != 0xf0) { dprintk(1, "%s: adapter is unconfigured\n", __func__); return -ENONET; } @@ -1158,7 +1162,9 @@ static int cec_config_log_addr(struct cec_adapter *adap, */ static void cec_adap_unconfigure(struct cec_adapter *adap) { - WARN_ON(adap->ops->adap_log_addr(adap, CEC_LOG_ADDR_INVALID)); + if (!adap->needs_hpd || + adap->phys_addr != CEC_PHYS_ADDR_INVALID) + WARN_ON(adap->ops->adap_log_addr(adap, CEC_LOG_ADDR_INVALID)); adap->log_addrs.log_addr_mask = 0; adap->is_configuring = false; adap->is_configured = false; @@ -1387,6 +1393,8 @@ void __cec_s_phys_addr(struct cec_adapter *adap, u16 phys_addr, bool block) if (phys_addr == adap->phys_addr || adap->devnode.unregistered) return; + dprintk(1, "new physical address %x.%x.%x.%x\n", + cec_phys_addr_exp(phys_addr)); if (phys_addr == CEC_PHYS_ADDR_INVALID || adap->phys_addr != CEC_PHYS_ADDR_INVALID) { adap->phys_addr = CEC_PHYS_ADDR_INVALID; @@ -1396,7 +1404,7 @@ void __cec_s_phys_addr(struct cec_adapter *adap, u16 phys_addr, bool block) if (adap->monitor_all_cnt) WARN_ON(call_op(adap, adap_monitor_all_enable, false)); mutex_lock(&adap->devnode.lock); - if (list_empty(&adap->devnode.fhs)) + if (adap->needs_hpd || list_empty(&adap->devnode.fhs)) WARN_ON(adap->ops->adap_enable(adap, false)); mutex_unlock(&adap->devnode.lock); if (phys_addr == CEC_PHYS_ADDR_INVALID) @@ -1404,7 +1412,7 @@ void __cec_s_phys_addr(struct cec_adapter *adap, u16 phys_addr, bool block) } mutex_lock(&adap->devnode.lock); - if (list_empty(&adap->devnode.fhs) && + if ((adap->needs_hpd || list_empty(&adap->devnode.fhs)) && adap->ops->adap_enable(adap, true)) { mutex_unlock(&adap->devnode.lock); return; @@ -1412,7 +1420,7 @@ void __cec_s_phys_addr(struct cec_adapter *adap, u16 phys_addr, bool block) if (adap->monitor_all_cnt && call_op(adap, adap_monitor_all_enable, true)) { - if (list_empty(&adap->devnode.fhs)) + if (adap->needs_hpd || list_empty(&adap->devnode.fhs)) WARN_ON(adap->ops->adap_enable(adap, false)); mutex_unlock(&adap->devnode.lock); return; diff --git a/drivers/media/cec/cec-api.c b/drivers/media/cec/cec-api.c index 0860fb458757..1359c3977101 100644 --- a/drivers/media/cec/cec-api.c +++ b/drivers/media/cec/cec-api.c @@ -202,7 +202,8 @@ static long cec_transmit(struct cec_adapter *adap, struct cec_fh *fh, err = -EPERM; else if (adap->is_configuring) err = -ENONET; - else if (!adap->is_configured && msg.msg[0] != 0xf0) + else if (!adap->is_configured && + (adap->needs_hpd || msg.msg[0] != 0xf0)) err = -ENONET; else if (cec_is_busy(adap, fh)) err = -EBUSY; @@ -521,6 +522,7 @@ static int cec_open(struct inode *inode, struct file *filp) mutex_lock(&devnode->lock); if (list_empty(&devnode->fhs) && + !adap->needs_hpd && adap->phys_addr == CEC_PHYS_ADDR_INVALID) { err = adap->ops->adap_enable(adap, true); if (err) { @@ -565,6 +567,7 @@ static int cec_release(struct inode *inode, struct file *filp) mutex_lock(&devnode->lock); list_del(&fh->list); if (list_empty(&devnode->fhs) && + !adap->needs_hpd && adap->phys_addr == CEC_PHYS_ADDR_INVALID) { WARN_ON(adap->ops->adap_enable(adap, false)); } diff --git a/drivers/media/cec/cec-core.c b/drivers/media/cec/cec-core.c index 2f87748ba4fc..b516d599d6c4 100644 --- a/drivers/media/cec/cec-core.c +++ b/drivers/media/cec/cec-core.c @@ -230,6 +230,7 @@ struct cec_adapter *cec_allocate_adapter(const struct cec_adap_ops *ops, adap->log_addrs.cec_version = CEC_OP_CEC_VERSION_2_0; adap->log_addrs.vendor_id = CEC_VENDOR_ID_NONE; adap->capabilities = caps; + adap->needs_hpd = caps & CEC_CAP_NEEDS_HPD; adap->available_log_addrs = available_las; adap->sequence = 0; adap->ops = ops; diff --git a/include/media/cec.h b/include/media/cec.h index a2e184d1df00..7e32e80b243e 100644 --- a/include/media/cec.h +++ b/include/media/cec.h @@ -164,6 +164,7 @@ struct cec_adapter { u8 available_log_addrs; u16 phys_addr; + bool needs_hpd; bool is_configuring; bool is_configured; u32 monitor_all_cnt; diff --git a/include/uapi/linux/cec.h b/include/uapi/linux/cec.h index a0dfe27bc6c7..44579a24f95d 100644 --- a/include/uapi/linux/cec.h +++ b/include/uapi/linux/cec.h @@ -336,6 +336,8 @@ static inline int cec_is_unconfigured(__u16 log_addr_mask) #define CEC_CAP_RC (1 << 4) /* Hardware can monitor all messages, not just directed and broadcast. */ #define CEC_CAP_MONITOR_ALL (1 << 5) +/* Hardware can use CEC only if the HDMI HPD pin is high. */ +#define CEC_CAP_NEEDS_HPD (1 << 6) /** * struct cec_caps - CEC capabilities structure. -- cgit v1.2.3-58-ga151 From 41c01d6582977c76f4ce827cc74e94d1892f9141 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 7 Jun 2017 11:46:13 -0300 Subject: [media] cec-ioc-adap-g-caps.rst: document CEC_CAP_NEEDS_HPD Document the new CEC_CAP_NEEDS_HPD capability. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/media/uapi/cec/cec-ioc-adap-g-caps.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Documentation/media/uapi/cec/cec-ioc-adap-g-caps.rst b/Documentation/media/uapi/cec/cec-ioc-adap-g-caps.rst index a0e961f11017..6d7bf7bef3eb 100644 --- a/Documentation/media/uapi/cec/cec-ioc-adap-g-caps.rst +++ b/Documentation/media/uapi/cec/cec-ioc-adap-g-caps.rst @@ -113,6 +113,14 @@ returns the information to the application. The ioctl never fails. - 0x00000020 - The CEC hardware can monitor all messages, not just directed and broadcast messages. + * .. _`CEC-CAP-NEEDS-HPD`: + + - ``CEC_CAP_NEEDS_HPD`` + - 0x00000040 + - The CEC hardware is only active if the HDMI Hotplug Detect pin is + high. This makes it impossible to use CEC to wake up displays that + set the HPD pin low when in standby mode, but keep the CEC bus + alive. -- cgit v1.2.3-58-ga151 From e8537b35bf48979803958c489876952d100afa0a Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 7 Jun 2017 11:46:14 -0300 Subject: [media] dt-bindings: media/s5p-cec.txt: document needs-hpd property Needed for boards that wire the CEC pin in such a way that it is unavailable when the HPD is low. Signed-off-by: Hans Verkuil Acked-by: Rob Herring Signed-off-by: Mauro Carvalho Chehab --- Documentation/devicetree/bindings/media/s5p-cec.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Documentation/devicetree/bindings/media/s5p-cec.txt b/Documentation/devicetree/bindings/media/s5p-cec.txt index 4bb08d9d940b..261af4d1a791 100644 --- a/Documentation/devicetree/bindings/media/s5p-cec.txt +++ b/Documentation/devicetree/bindings/media/s5p-cec.txt @@ -17,6 +17,12 @@ Required properties: - samsung,syscon-phandle - phandle to the PMU system controller - hdmi-phandle - phandle to the HDMI controller +Optional: + - needs-hpd : if present the CEC support is only available when the HPD + is high. Some boards only let the CEC pin through if the HPD is high, for + example if there is a level converter that uses the HPD to power up + or down. + Example: hdmicec: cec@100B0000 { -- cgit v1.2.3-58-ga151 From d0703944c19c423fa4fa047795147e786553ff1c Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 7 Jun 2017 11:46:15 -0300 Subject: [media] s5p_cec: set the CEC_CAP_NEEDS_HPD flag if needed Use the needs-hpd DT property to determine if the CEC_CAP_NEEDS_HPD should be set. Signed-off-by: Hans Verkuil Acked-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-cec/s5p_cec.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/s5p-cec/s5p_cec.c b/drivers/media/platform/s5p-cec/s5p_cec.c index 65a223e578ed..8e06071a7977 100644 --- a/drivers/media/platform/s5p-cec/s5p_cec.c +++ b/drivers/media/platform/s5p-cec/s5p_cec.c @@ -173,6 +173,7 @@ static int s5p_cec_probe(struct platform_device *pdev) struct platform_device *hdmi_dev; struct resource *res; struct s5p_cec_dev *cec; + bool needs_hpd = of_property_read_bool(pdev->dev.of_node, "needs-hpd"); int ret; np = of_parse_phandle(pdev->dev.of_node, "hdmi-phandle", 0); @@ -221,7 +222,8 @@ static int s5p_cec_probe(struct platform_device *pdev) cec->adap = cec_allocate_adapter(&s5p_cec_adap_ops, cec, CEC_NAME, CEC_CAP_LOG_ADDRS | CEC_CAP_TRANSMIT | - CEC_CAP_PASSTHROUGH | CEC_CAP_RC, 1); + CEC_CAP_PASSTHROUGH | CEC_CAP_RC | + (needs_hpd ? CEC_CAP_NEEDS_HPD : 0), 1); ret = PTR_ERR_OR_ZERO(cec->adap); if (ret) return ret; -- cgit v1.2.3-58-ga151 From af15e32fc9b1fd805d72b54524198612ac628b4d Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 9 Jun 2017 14:54:00 -0300 Subject: [media] dt-bindings: add media/cec.txt Document common HDMI CEC bindings. Add this to the MAINTAINERS file as well. Signed-off-by: Hans Verkuil Acked-by: Benjamin Gaignard Acked-by: Sylwester Nawrocki Reviewed-by: Rob Herring Signed-off-by: Mauro Carvalho Chehab --- Documentation/devicetree/bindings/media/cec.txt | 8 ++++++++ MAINTAINERS | 1 + 2 files changed, 9 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/cec.txt diff --git a/Documentation/devicetree/bindings/media/cec.txt b/Documentation/devicetree/bindings/media/cec.txt new file mode 100644 index 000000000000..22d7aae3d3d7 --- /dev/null +++ b/Documentation/devicetree/bindings/media/cec.txt @@ -0,0 +1,8 @@ +Common bindings for HDMI CEC adapters + +- hdmi-phandle: phandle to the HDMI controller. + +- needs-hpd: if present the CEC support is only available when the HPD + is high. Some boards only let the CEC pin through if the HPD is high, + for example if there is a level converter that uses the HPD to power + up or down. diff --git a/MAINTAINERS b/MAINTAINERS index 053c3bdd1fe5..4ac340d189a3 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3137,6 +3137,7 @@ F: include/media/cec.h F: include/media/cec-notifier.h F: include/uapi/linux/cec.h F: include/uapi/linux/cec-funcs.h +F: Documentation/devicetree/bindings/media/cec.txt CELL BROADBAND ENGINE ARCHITECTURE M: Arnd Bergmann -- cgit v1.2.3-58-ga151 From 94cce93c3da70ee1fbc44b1fdb6874128212b10d Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 9 Jun 2017 14:54:01 -0300 Subject: [media] dt-bindings: media/s5p-cec.txt, media/stih-cec.txt: refer to cec.txt Now that there is a cec.txt with common CEC bindings, update the two driver-specific bindings to refer to cec.txt. Signed-off-by: Hans Verkuil Acked-by: Benjamin Gaignard Acked-by: Sylwester Nawrocki Reviewed-by: Rob Herring Signed-off-by: Mauro Carvalho Chehab --- Documentation/devicetree/bindings/media/s5p-cec.txt | 6 ++---- Documentation/devicetree/bindings/media/stih-cec.txt | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/Documentation/devicetree/bindings/media/s5p-cec.txt b/Documentation/devicetree/bindings/media/s5p-cec.txt index 261af4d1a791..1b1a10ba48ce 100644 --- a/Documentation/devicetree/bindings/media/s5p-cec.txt +++ b/Documentation/devicetree/bindings/media/s5p-cec.txt @@ -15,13 +15,11 @@ Required properties: - clock-names : from common clock binding: must contain "hdmicec", corresponding to entry in the clocks property. - samsung,syscon-phandle - phandle to the PMU system controller - - hdmi-phandle - phandle to the HDMI controller + - hdmi-phandle - phandle to the HDMI controller, see also cec.txt. Optional: - needs-hpd : if present the CEC support is only available when the HPD - is high. Some boards only let the CEC pin through if the HPD is high, for - example if there is a level converter that uses the HPD to power up - or down. + is high. See cec.txt for more details. Example: diff --git a/Documentation/devicetree/bindings/media/stih-cec.txt b/Documentation/devicetree/bindings/media/stih-cec.txt index 289a08b33651..8be2a040c6c6 100644 --- a/Documentation/devicetree/bindings/media/stih-cec.txt +++ b/Documentation/devicetree/bindings/media/stih-cec.txt @@ -9,7 +9,7 @@ Required properties: - pinctrl-names: Contains only one value - "default" - pinctrl-0: Specifies the pin control groups used for CEC hardware. - resets: Reference to a reset controller - - hdmi-phandle: Phandle to the HDMI controller + - hdmi-phandle: Phandle to the HDMI controller, see also cec.txt. Example for STIH407: -- cgit v1.2.3-58-ga151 From 8d67ae25a9ea206f1ad53561511c1810d7838666 Mon Sep 17 00:00:00 2001 From: Ramesh Shanmugasundaram Date: Mon, 12 Jun 2017 10:26:13 -0300 Subject: [media] media: v4l2-ctrls: Reserve controls for MAX217X Reserve controls for MAX217X RF to Bits tuner family. These hybrid radio receiver chips are highly programmable and hence reserving 32 controls. Signed-off-by: Ramesh Shanmugasundaram Acked-by: Laurent Pinchart Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- include/uapi/linux/v4l2-controls.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h index 0d2e1e01fbd5..83b28b41123f 100644 --- a/include/uapi/linux/v4l2-controls.h +++ b/include/uapi/linux/v4l2-controls.h @@ -180,6 +180,11 @@ enum v4l2_colorfx { * We reserve 16 controls for this driver. */ #define V4L2_CID_USER_TC358743_BASE (V4L2_CID_USER_BASE + 0x1080) +/* The base for the max217x driver controls. + * We reserve 32 controls for this driver + */ +#define V4L2_CID_USER_MAX217X_BASE (V4L2_CID_USER_BASE + 0x1090) + /* MPEG-class control IDs */ /* The MPEG controls are applicable to all codec controls * and the 'MPEG' part of the define is historical */ -- cgit v1.2.3-58-ga151 From a45509c080a13a2cd7f3a4f5ab8733f57dd8f1a3 Mon Sep 17 00:00:00 2001 From: Ramesh Shanmugasundaram Date: Mon, 12 Jun 2017 10:26:14 -0300 Subject: [media] dt-bindings: media: Add MAX2175 binding description Add device tree binding documentation for MAX2175 RF to bits tuner device. Signed-off-by: Ramesh Shanmugasundaram Acked-by: Rob Herring Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../devicetree/bindings/media/i2c/max2175.txt | 59 ++++++++++++++++++++++ .../devicetree/bindings/property-units.txt | 1 + 2 files changed, 60 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/i2c/max2175.txt diff --git a/Documentation/devicetree/bindings/media/i2c/max2175.txt b/Documentation/devicetree/bindings/media/i2c/max2175.txt new file mode 100644 index 000000000000..02b4e9cd7b1b --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/max2175.txt @@ -0,0 +1,59 @@ +Maxim Integrated MAX2175 RF to Bits tuner +----------------------------------------- + +The MAX2175 IC is an advanced analog/digital hybrid-radio receiver with +RF to Bits® front-end designed for software-defined radio solutions. + +Required properties: +-------------------- +- compatible: "maxim,max2175" for MAX2175 RF-to-bits tuner. +- clocks: clock specifier. +- port: child port node corresponding to the I2S output, in accordance with + the video interface bindings defined in + Documentation/devicetree/bindings/media/video-interfaces.txt. The port + node must contain at least one endpoint. + +Optional properties: +-------------------- +- maxim,master : phandle to the master tuner if it is a slave. This + is used to define two tuners in diversity mode + (1 master, 1 slave). By default each tuner is an + individual master. +- maxim,refout-load : load capacitance value (in picofarads) on reference + output drive level. The possible load values are: + 0 (default - refout disabled) + 10 + 20 + 30 + 40 + 60 + 70 +- maxim,am-hiz-filter : empty property indicates the AM Hi-Z filter is used + in this hardware for AM antenna input. + +Example: +-------- + +Board specific DTS file + +/* Fixed XTAL clock node */ +maxim_xtal: clock { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <36864000>; +}; + +/* A tuner device instance under i2c bus */ +max2175_0: tuner@60 { + compatible = "maxim,max2175"; + reg = <0x60>; + clocks = <&maxim_xtal>; + maxim,refout-load = <10>; + + port { + max2175_0_ep: endpoint { + remote-endpoint = <&slave_rx_device>; + }; + }; + +}; diff --git a/Documentation/devicetree/bindings/property-units.txt b/Documentation/devicetree/bindings/property-units.txt index 12278d79f6c0..7c9f6ee918f1 100644 --- a/Documentation/devicetree/bindings/property-units.txt +++ b/Documentation/devicetree/bindings/property-units.txt @@ -28,6 +28,7 @@ Electricity -ohms : Ohms -micro-ohms : micro Ohms -microvolt : micro volts +-picofarads : picofarads Temperature ---------------------------------------- -- cgit v1.2.3-58-ga151 From b47b79d8a231d137ec9f9a5bef05f9e2f19a4347 Mon Sep 17 00:00:00 2001 From: Ramesh Shanmugasundaram Date: Tue, 13 Jun 2017 09:54:47 -0300 Subject: [media] media: i2c: max2175: Add MAX2175 support This patch adds driver support for the MAX2175 chip. This is Maxim Integrated's RF to Bits tuner front end chip designed for software-defined radio solutions. This driver exposes the tuner as a sub-device instance with standard and custom controls to configure the device. Signed-off-by: Ramesh Shanmugasundaram Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/media/v4l-drivers/index.rst | 1 + Documentation/media/v4l-drivers/max2175.rst | 62 ++ drivers/media/i2c/Kconfig | 12 + drivers/media/i2c/Makefile | 2 + drivers/media/i2c/max2175.c | 1453 +++++++++++++++++++++++++++ drivers/media/i2c/max2175.h | 109 ++ include/uapi/linux/max2175.h | 28 + 7 files changed, 1667 insertions(+) create mode 100644 Documentation/media/v4l-drivers/max2175.rst create mode 100644 drivers/media/i2c/max2175.c create mode 100644 drivers/media/i2c/max2175.h create mode 100644 include/uapi/linux/max2175.h diff --git a/Documentation/media/v4l-drivers/index.rst b/Documentation/media/v4l-drivers/index.rst index 90fe22a6414a..2e24d6806052 100644 --- a/Documentation/media/v4l-drivers/index.rst +++ b/Documentation/media/v4l-drivers/index.rst @@ -42,6 +42,7 @@ For more details see the file COPYING in the source distribution of Linux. davinci-vpbe fimc ivtv + max2175 meye omap3isp omap4_camera diff --git a/Documentation/media/v4l-drivers/max2175.rst b/Documentation/media/v4l-drivers/max2175.rst new file mode 100644 index 000000000000..04478c25d57a --- /dev/null +++ b/Documentation/media/v4l-drivers/max2175.rst @@ -0,0 +1,62 @@ +Maxim Integrated MAX2175 RF to bits tuner driver +================================================ + +The MAX2175 driver implements the following driver-specific controls: + +``V4L2_CID_MAX2175_I2S_ENABLE`` +------------------------------- + Enable/Disable I2S output of the tuner. This is a private control + that can be accessed only using the subdev interface. + Refer to Documentation/media/kapi/v4l2-controls for more details. + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + :widths: 1 4 + + * - ``(0)`` + - I2S output is disabled. + * - ``(1)`` + - I2S output is enabled. + +``V4L2_CID_MAX2175_HSLS`` +------------------------- + The high-side/low-side (HSLS) control of the tuner for a given band. + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + :widths: 1 4 + + * - ``(0)`` + - The LO frequency position is below the desired frequency. + * - ``(1)`` + - The LO frequency position is above the desired frequency. + +``V4L2_CID_MAX2175_RX_MODE (menu)`` +----------------------------------- + The Rx mode controls a number of preset parameters of the tuner like + sample clock (sck), sampling rate etc. These multiple settings are + provided under one single label called Rx mode in the datasheet. The + list below shows the supported modes with a brief description. + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + :widths: 1 4 + + * - ``"Europe modes"`` + * - ``"FM 1.2" (0)`` + - This configures FM band with a sample rate of 0.512 million + samples/sec with a 10.24 MHz sck. + * - ``"DAB 1.2" (1)`` + - This configures VHF band with a sample rate of 2.048 million + samples/sec with a 32.768 MHz sck. + + * - ``"North America modes"`` + * - ``"FM 1.0" (0)`` + - This configures FM band with a sample rate of 0.7441875 million + samples/sec with a 14.88375 MHz sck. + * - ``"DAB 1.2" (1)`` + - This configures FM band with a sample rate of 0.372 million + samples/sec with a 7.441875 MHz sck. diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index c380e2475c82..c0e6e78883b0 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -796,6 +796,18 @@ config VIDEO_SAA6752HS To compile this driver as a module, choose M here: the module will be called saa6752hs. +comment "SDR tuner chips" + +config SDR_MAX2175 + tristate "Maxim 2175 RF to Bits tuner" + depends on VIDEO_V4L2 && MEDIA_SDR_SUPPORT && I2C + ---help--- + Support for Maxim 2175 tuner. It is an advanced analog/digital + radio receiver with RF-to-Bits front-end designed for SDR solutions. + + To compile this driver as a module, choose M here; the + module will be called max2175. + comment "Miscellaneous helper chips" config VIDEO_THS7303 diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index 62323ec66be8..5a4a761f7383 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -86,3 +86,5 @@ obj-$(CONFIG_VIDEO_IR_I2C) += ir-kbd-i2c.o obj-$(CONFIG_VIDEO_ML86V7667) += ml86v7667.o obj-$(CONFIG_VIDEO_OV2659) += ov2659.o obj-$(CONFIG_VIDEO_TC358743) += tc358743.o + +obj-$(CONFIG_SDR_MAX2175) += max2175.o diff --git a/drivers/media/i2c/max2175.c b/drivers/media/i2c/max2175.c new file mode 100644 index 000000000000..0d28a80f8ed2 --- /dev/null +++ b/drivers/media/i2c/max2175.c @@ -0,0 +1,1453 @@ +/* + * Maxim Integrated MAX2175 RF to Bits tuner driver + * + * This driver & most of the hard coded values are based on the reference + * application delivered by Maxim for this device. + * + * Copyright (C) 2016 Maxim Integrated Products + * Copyright (C) 2017 Renesas Electronics Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "max2175.h" + +#define DRIVER_NAME "max2175" + +#define mxm_dbg(ctx, fmt, arg...) dev_dbg(&ctx->client->dev, fmt, ## arg) +#define mxm_err(ctx, fmt, arg...) dev_err(&ctx->client->dev, fmt, ## arg) + +/* Rx mode */ +struct max2175_rxmode { + enum max2175_band band; /* Associated band */ + u32 freq; /* Default freq in Hz */ + u8 i2s_word_size; /* Bit value */ +}; + +/* Register map to define preset values */ +struct max2175_reg_map { + u8 idx; /* Register index */ + u8 val; /* Register value */ +}; + +static const struct max2175_rxmode eu_rx_modes[] = { + /* EU modes */ + [MAX2175_EU_FM_1_2] = { MAX2175_BAND_FM, 98256000, 1 }, + [MAX2175_DAB_1_2] = { MAX2175_BAND_VHF, 182640000, 0 }, +}; + +static const struct max2175_rxmode na_rx_modes[] = { + /* NA modes */ + [MAX2175_NA_FM_1_0] = { MAX2175_BAND_FM, 98255520, 1 }, + [MAX2175_NA_FM_2_0] = { MAX2175_BAND_FM, 98255520, 6 }, +}; + +/* + * Preset values: + * Based on Maxim MAX2175 Register Table revision: 130p10 + */ +static const u8 full_fm_eu_1p0[] = { + 0x15, 0x04, 0xb8, 0xe3, 0x35, 0x18, 0x7c, 0x00, + 0x00, 0x7d, 0x40, 0x08, 0x70, 0x7a, 0x88, 0x91, + 0x61, 0x61, 0x61, 0x61, 0x5a, 0x0f, 0x34, 0x1c, + 0x14, 0x88, 0x33, 0x02, 0x00, 0x09, 0x00, 0x65, + 0x9f, 0x2b, 0x80, 0x00, 0x95, 0x05, 0x2c, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, + 0x4a, 0x08, 0xa8, 0x0e, 0x0e, 0x2f, 0x7e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xab, 0x5e, 0xa9, + 0xae, 0xbb, 0x57, 0x18, 0x3b, 0x03, 0x3b, 0x64, + 0x40, 0x60, 0x00, 0x2a, 0xbf, 0x3f, 0xff, 0x9f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, + 0xff, 0xfc, 0xef, 0x1c, 0x40, 0x00, 0x00, 0x02, + 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xac, 0x40, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x75, 0x00, 0x00, + 0x00, 0x47, 0x00, 0x00, 0x11, 0x3f, 0x22, 0x00, + 0xf1, 0x00, 0x41, 0x03, 0xb0, 0x00, 0x00, 0x00, + 0x1b, +}; + +static const u8 full_fm_na_1p0[] = { + 0x13, 0x08, 0x8d, 0xc0, 0x35, 0x18, 0x7d, 0x3f, + 0x7d, 0x75, 0x40, 0x08, 0x70, 0x7a, 0x88, 0x91, + 0x61, 0x61, 0x61, 0x61, 0x5c, 0x0f, 0x34, 0x1c, + 0x14, 0x88, 0x33, 0x02, 0x00, 0x01, 0x00, 0x65, + 0x9f, 0x2b, 0x80, 0x00, 0x95, 0x05, 0x2c, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, + 0x4a, 0x08, 0xa8, 0x0e, 0x0e, 0xaf, 0x7e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xab, 0x5e, 0xa9, + 0xae, 0xbb, 0x57, 0x18, 0x3b, 0x03, 0x3b, 0x64, + 0x40, 0x60, 0x00, 0x2a, 0xbf, 0x3f, 0xff, 0x9f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, + 0xff, 0xfc, 0xef, 0x1c, 0x40, 0x00, 0x00, 0x02, + 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xa6, 0x40, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x75, 0x00, 0x00, + 0x00, 0x35, 0x00, 0x00, 0x11, 0x3f, 0x22, 0x00, + 0xf1, 0x00, 0x41, 0x03, 0xb0, 0x00, 0x00, 0x00, + 0x1b, +}; + +/* DAB1.2 settings */ +static const struct max2175_reg_map dab12_map[] = { + { 0x01, 0x13 }, { 0x02, 0x0d }, { 0x03, 0x15 }, { 0x04, 0x55 }, + { 0x05, 0x0a }, { 0x06, 0xa0 }, { 0x07, 0x40 }, { 0x08, 0x00 }, + { 0x09, 0x00 }, { 0x0a, 0x7d }, { 0x0b, 0x4a }, { 0x0c, 0x28 }, + { 0x0e, 0x43 }, { 0x0f, 0xb5 }, { 0x10, 0x31 }, { 0x11, 0x9e }, + { 0x12, 0x68 }, { 0x13, 0x9e }, { 0x14, 0x68 }, { 0x15, 0x58 }, + { 0x16, 0x2f }, { 0x17, 0x3f }, { 0x18, 0x40 }, { 0x1a, 0x88 }, + { 0x1b, 0xaa }, { 0x1c, 0x9a }, { 0x1d, 0x00 }, { 0x1e, 0x00 }, + { 0x23, 0x80 }, { 0x24, 0x00 }, { 0x25, 0x00 }, { 0x26, 0x00 }, + { 0x27, 0x00 }, { 0x32, 0x08 }, { 0x33, 0xf8 }, { 0x36, 0x2d }, + { 0x37, 0x7e }, { 0x55, 0xaf }, { 0x56, 0x3f }, { 0x57, 0xf8 }, + { 0x58, 0x99 }, { 0x76, 0x00 }, { 0x77, 0x00 }, { 0x78, 0x02 }, + { 0x79, 0x40 }, { 0x82, 0x00 }, { 0x83, 0x00 }, { 0x85, 0x00 }, + { 0x86, 0x20 }, +}; + +/* EU FM 1.2 settings */ +static const struct max2175_reg_map fmeu1p2_map[] = { + { 0x01, 0x15 }, { 0x02, 0x04 }, { 0x03, 0xb8 }, { 0x04, 0xe3 }, + { 0x05, 0x35 }, { 0x06, 0x18 }, { 0x07, 0x7c }, { 0x08, 0x00 }, + { 0x09, 0x00 }, { 0x0a, 0x73 }, { 0x0b, 0x40 }, { 0x0c, 0x08 }, + { 0x0e, 0x7a }, { 0x0f, 0x88 }, { 0x10, 0x91 }, { 0x11, 0x61 }, + { 0x12, 0x61 }, { 0x13, 0x61 }, { 0x14, 0x61 }, { 0x15, 0x5a }, + { 0x16, 0x0f }, { 0x17, 0x34 }, { 0x18, 0x1c }, { 0x1a, 0x88 }, + { 0x1b, 0x33 }, { 0x1c, 0x02 }, { 0x1d, 0x00 }, { 0x1e, 0x01 }, + { 0x23, 0x80 }, { 0x24, 0x00 }, { 0x25, 0x95 }, { 0x26, 0x05 }, + { 0x27, 0x2c }, { 0x32, 0x08 }, { 0x33, 0xa8 }, { 0x36, 0x2f }, + { 0x37, 0x7e }, { 0x55, 0xbf }, { 0x56, 0x3f }, { 0x57, 0xff }, + { 0x58, 0x9f }, { 0x76, 0xac }, { 0x77, 0x40 }, { 0x78, 0x00 }, + { 0x79, 0x00 }, { 0x82, 0x47 }, { 0x83, 0x00 }, { 0x85, 0x11 }, + { 0x86, 0x3f }, +}; + +/* FM NA 1.0 settings */ +static const struct max2175_reg_map fmna1p0_map[] = { + { 0x01, 0x13 }, { 0x02, 0x08 }, { 0x03, 0x8d }, { 0x04, 0xc0 }, + { 0x05, 0x35 }, { 0x06, 0x18 }, { 0x07, 0x7d }, { 0x08, 0x3f }, + { 0x09, 0x7d }, { 0x0a, 0x75 }, { 0x0b, 0x40 }, { 0x0c, 0x08 }, + { 0x0e, 0x7a }, { 0x0f, 0x88 }, { 0x10, 0x91 }, { 0x11, 0x61 }, + { 0x12, 0x61 }, { 0x13, 0x61 }, { 0x14, 0x61 }, { 0x15, 0x5c }, + { 0x16, 0x0f }, { 0x17, 0x34 }, { 0x18, 0x1c }, { 0x1a, 0x88 }, + { 0x1b, 0x33 }, { 0x1c, 0x02 }, { 0x1d, 0x00 }, { 0x1e, 0x01 }, + { 0x23, 0x80 }, { 0x24, 0x00 }, { 0x25, 0x95 }, { 0x26, 0x05 }, + { 0x27, 0x2c }, { 0x32, 0x08 }, { 0x33, 0xa8 }, { 0x36, 0xaf }, + { 0x37, 0x7e }, { 0x55, 0xbf }, { 0x56, 0x3f }, { 0x57, 0xff }, + { 0x58, 0x9f }, { 0x76, 0xa6 }, { 0x77, 0x40 }, { 0x78, 0x00 }, + { 0x79, 0x00 }, { 0x82, 0x35 }, { 0x83, 0x00 }, { 0x85, 0x11 }, + { 0x86, 0x3f }, +}; + +/* FM NA 2.0 settings */ +static const struct max2175_reg_map fmna2p0_map[] = { + { 0x01, 0x13 }, { 0x02, 0x08 }, { 0x03, 0x8d }, { 0x04, 0xc0 }, + { 0x05, 0x35 }, { 0x06, 0x18 }, { 0x07, 0x7c }, { 0x08, 0x54 }, + { 0x09, 0xa7 }, { 0x0a, 0x55 }, { 0x0b, 0x42 }, { 0x0c, 0x48 }, + { 0x0e, 0x7a }, { 0x0f, 0x88 }, { 0x10, 0x91 }, { 0x11, 0x61 }, + { 0x12, 0x61 }, { 0x13, 0x61 }, { 0x14, 0x61 }, { 0x15, 0x5c }, + { 0x16, 0x0f }, { 0x17, 0x34 }, { 0x18, 0x1c }, { 0x1a, 0x88 }, + { 0x1b, 0x33 }, { 0x1c, 0x02 }, { 0x1d, 0x00 }, { 0x1e, 0x01 }, + { 0x23, 0x80 }, { 0x24, 0x00 }, { 0x25, 0x95 }, { 0x26, 0x05 }, + { 0x27, 0x2c }, { 0x32, 0x08 }, { 0x33, 0xa8 }, { 0x36, 0xaf }, + { 0x37, 0x7e }, { 0x55, 0xbf }, { 0x56, 0x3f }, { 0x57, 0xff }, + { 0x58, 0x9f }, { 0x76, 0xac }, { 0x77, 0xc0 }, { 0x78, 0x00 }, + { 0x79, 0x00 }, { 0x82, 0x6b }, { 0x83, 0x00 }, { 0x85, 0x11 }, + { 0x86, 0x3f }, +}; + +static const u16 ch_coeff_dab1[] = { + 0x001c, 0x0007, 0xffcd, 0x0056, 0xffa4, 0x0033, 0x0027, 0xff61, + 0x010e, 0xfec0, 0x0106, 0xffb8, 0xff1c, 0x023c, 0xfcb2, 0x039b, + 0xfd4e, 0x0055, 0x036a, 0xf7de, 0x0d21, 0xee72, 0x1499, 0x6a51, +}; + +static const u16 ch_coeff_fmeu[] = { + 0x0000, 0xffff, 0x0001, 0x0002, 0xfffa, 0xffff, 0x0015, 0xffec, + 0xffde, 0x0054, 0xfff9, 0xff52, 0x00b8, 0x00a2, 0xfe0a, 0x00af, + 0x02e3, 0xfc14, 0xfe89, 0x089d, 0xfa2e, 0xf30f, 0x25be, 0x4eb6, +}; + +static const u16 eq_coeff_fmeu1_ra02_m6db[] = { + 0x0040, 0xffc6, 0xfffa, 0x002c, 0x000d, 0xff90, 0x0037, 0x006e, + 0xffc0, 0xff5b, 0x006a, 0x00f0, 0xff57, 0xfe94, 0x0112, 0x0252, + 0xfe0c, 0xfc6a, 0x0385, 0x0553, 0xfa49, 0xf789, 0x0b91, 0x1a10, +}; + +static const u16 ch_coeff_fmna[] = { + 0x0001, 0x0003, 0xfffe, 0xfff4, 0x0000, 0x001f, 0x000c, 0xffbc, + 0xffd3, 0x007d, 0x0075, 0xff33, 0xff01, 0x0131, 0x01ef, 0xfe60, + 0xfc7a, 0x020e, 0x0656, 0xfd94, 0xf395, 0x02ab, 0x2857, 0x3d3f, +}; + +static const u16 eq_coeff_fmna1_ra02_m6db[] = { + 0xfff1, 0xffe1, 0xffef, 0x000e, 0x0030, 0x002f, 0xfff6, 0xffa7, + 0xff9d, 0x000a, 0x00a2, 0x00b5, 0xffea, 0xfed9, 0xfec5, 0x003d, + 0x0217, 0x021b, 0xff5a, 0xfc2b, 0xfcbd, 0x02c4, 0x0ac3, 0x0e85, +}; + +static const u8 adc_presets[2][23] = { + { + 0x83, 0x00, 0xcf, 0xb4, 0x0f, 0x2c, 0x0c, 0x49, + 0x00, 0x00, 0x00, 0x8c, 0x02, 0x02, 0x00, 0x04, + 0xec, 0x82, 0x4b, 0xcc, 0x01, 0x88, 0x0c, + }, + { + 0x83, 0x00, 0xcf, 0xb4, 0x0f, 0x2c, 0x0c, 0x49, + 0x00, 0x00, 0x00, 0x8c, 0x02, 0x20, 0x33, 0x8c, + 0x57, 0xd7, 0x59, 0xb7, 0x65, 0x0e, 0x0c, + }, +}; + +/* Tuner bands */ +static const struct v4l2_frequency_band eu_bands_rf = { + .tuner = 0, + .type = V4L2_TUNER_RF, + .index = 0, + .capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS, + .rangelow = 65000000, + .rangehigh = 240000000, +}; + +static const struct v4l2_frequency_band na_bands_rf = { + .tuner = 0, + .type = V4L2_TUNER_RF, + .index = 0, + .capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS, + .rangelow = 65000000, + .rangehigh = 108000000, +}; + +/* Regmap settings */ +static const struct regmap_range max2175_regmap_volatile_range[] = { + regmap_reg_range(0x30, 0x35), + regmap_reg_range(0x3a, 0x45), + regmap_reg_range(0x59, 0x5e), + regmap_reg_range(0x73, 0x75), +}; + +static const struct regmap_access_table max2175_volatile_regs = { + .yes_ranges = max2175_regmap_volatile_range, + .n_yes_ranges = ARRAY_SIZE(max2175_regmap_volatile_range), +}; + +static const struct reg_default max2175_reg_defaults[] = { + { 0x00, 0x07}, +}; + +static const struct regmap_config max2175_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0xff, + .reg_defaults = max2175_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(max2175_reg_defaults), + .volatile_table = &max2175_volatile_regs, + .cache_type = REGCACHE_FLAT, +}; + +struct max2175 { + struct v4l2_subdev sd; /* Sub-device */ + struct i2c_client *client; /* I2C client */ + + /* Controls */ + struct v4l2_ctrl_handler ctrl_hdl; + struct v4l2_ctrl *lna_gain; /* LNA gain value */ + struct v4l2_ctrl *if_gain; /* I/F gain value */ + struct v4l2_ctrl *pll_lock; /* PLL lock */ + struct v4l2_ctrl *i2s_en; /* I2S output enable */ + struct v4l2_ctrl *hsls; /* High-side/Low-side polarity */ + struct v4l2_ctrl *rx_mode; /* Receive mode */ + + /* Regmap */ + struct regmap *regmap; + + /* Cached configuration */ + u32 freq; /* Tuned freq In Hz */ + const struct max2175_rxmode *rx_modes; /* EU or NA modes */ + const struct v4l2_frequency_band *bands_rf; /* EU or NA bands */ + + /* Device settings */ + unsigned long xtal_freq; /* Ref Oscillator freq in Hz */ + u32 decim_ratio; + bool master; /* Master/Slave */ + bool am_hiz; /* AM Hi-Z filter */ + + /* ROM values */ + u8 rom_bbf_bw_am; + u8 rom_bbf_bw_fm; + u8 rom_bbf_bw_dab; + + /* Driver private variables */ + bool mode_resolved; /* Flag to sanity check settings */ +}; + +static inline struct max2175 *max2175_from_sd(struct v4l2_subdev *sd) +{ + return container_of(sd, struct max2175, sd); +} + +static inline struct max2175 *max2175_from_ctrl_hdl(struct v4l2_ctrl_handler *h) +{ + return container_of(h, struct max2175, ctrl_hdl); +} + +/* Get bitval of a given val */ +static inline u8 max2175_get_bitval(u8 val, u8 msb, u8 lsb) +{ + return (val & GENMASK(msb, lsb)) >> lsb; +} + +/* Read/Write bit(s) on top of regmap */ +static int max2175_read(struct max2175 *ctx, u8 idx, u8 *val) +{ + u32 regval; + int ret; + + ret = regmap_read(ctx->regmap, idx, ®val); + if (ret) + mxm_err(ctx, "read ret(%d): idx 0x%02x\n", ret, idx); + else + *val = regval; + + return ret; +} + +static int max2175_write(struct max2175 *ctx, u8 idx, u8 val) +{ + int ret; + + ret = regmap_write(ctx->regmap, idx, val); + if (ret) + mxm_err(ctx, "write ret(%d): idx 0x%02x val 0x%02x\n", + ret, idx, val); + + return ret; +} + +static u8 max2175_read_bits(struct max2175 *ctx, u8 idx, u8 msb, u8 lsb) +{ + u8 val; + + if (max2175_read(ctx, idx, &val)) + return 0; + + return max2175_get_bitval(val, msb, lsb); +} + +static int max2175_write_bits(struct max2175 *ctx, u8 idx, + u8 msb, u8 lsb, u8 newval) +{ + int ret = regmap_update_bits(ctx->regmap, idx, GENMASK(msb, lsb), + newval << lsb); + + if (ret) + mxm_err(ctx, "wbits ret(%d): idx 0x%02x\n", ret, idx); + + return ret; +} + +static int max2175_write_bit(struct max2175 *ctx, u8 idx, u8 bit, u8 newval) +{ + return max2175_write_bits(ctx, idx, bit, bit, newval); +} + +/* Checks expected pattern every msec until timeout */ +static int max2175_poll_timeout(struct max2175 *ctx, u8 idx, u8 msb, u8 lsb, + u8 exp_bitval, u32 timeout_us) +{ + unsigned int val; + + return regmap_read_poll_timeout(ctx->regmap, idx, val, + (max2175_get_bitval(val, msb, lsb) == exp_bitval), + 1000, timeout_us); +} + +static int max2175_poll_csm_ready(struct max2175 *ctx) +{ + int ret; + + ret = max2175_poll_timeout(ctx, 69, 1, 1, 0, 50000); + if (ret) + mxm_err(ctx, "csm not ready\n"); + + return ret; +} + +#define MAX2175_IS_BAND_AM(ctx) \ + (max2175_read_bits(ctx, 5, 1, 0) == MAX2175_BAND_AM) + +#define MAX2175_IS_BAND_VHF(ctx) \ + (max2175_read_bits(ctx, 5, 1, 0) == MAX2175_BAND_VHF) + +#define MAX2175_IS_FM_MODE(ctx) \ + (max2175_read_bits(ctx, 12, 5, 4) == 0) + +#define MAX2175_IS_FMHD_MODE(ctx) \ + (max2175_read_bits(ctx, 12, 5, 4) == 1) + +#define MAX2175_IS_DAB_MODE(ctx) \ + (max2175_read_bits(ctx, 12, 5, 4) == 2) + +static int max2175_band_from_freq(u32 freq) +{ + if (freq >= 144000 && freq <= 26100000) + return MAX2175_BAND_AM; + else if (freq >= 65000000 && freq <= 108000000) + return MAX2175_BAND_FM; + + return MAX2175_BAND_VHF; +} + +static void max2175_i2s_enable(struct max2175 *ctx, bool enable) +{ + if (enable) + /* Stuff bits are zeroed */ + max2175_write_bits(ctx, 104, 3, 0, 2); + else + /* Keep SCK alive */ + max2175_write_bits(ctx, 104, 3, 0, 9); + mxm_dbg(ctx, "i2s %sabled\n", enable ? "en" : "dis"); +} + +static void max2175_set_filter_coeffs(struct max2175 *ctx, u8 m_sel, + u8 bank, const u16 *coeffs) +{ + unsigned int i; + u8 coeff_addr, upper_address = 24; + + mxm_dbg(ctx, "set_filter_coeffs: m_sel %d bank %d\n", m_sel, bank); + max2175_write_bits(ctx, 114, 5, 4, m_sel); + + if (m_sel == 2) + upper_address = 12; + + for (i = 0; i < upper_address; i++) { + coeff_addr = i + bank * 24; + max2175_write(ctx, 115, coeffs[i] >> 8); + max2175_write(ctx, 116, coeffs[i]); + max2175_write(ctx, 117, coeff_addr | 1 << 7); + } + max2175_write_bit(ctx, 117, 7, 0); +} + +static void max2175_load_fmeu_1p2(struct max2175 *ctx) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(fmeu1p2_map); i++) + max2175_write(ctx, fmeu1p2_map[i].idx, fmeu1p2_map[i].val); + + ctx->decim_ratio = 36; + + /* Load the Channel Filter Coefficients into channel filter bank #2 */ + max2175_set_filter_coeffs(ctx, MAX2175_CH_MSEL, 0, ch_coeff_fmeu); + max2175_set_filter_coeffs(ctx, MAX2175_EQ_MSEL, 0, + eq_coeff_fmeu1_ra02_m6db); +} + +static void max2175_load_dab_1p2(struct max2175 *ctx) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(dab12_map); i++) + max2175_write(ctx, dab12_map[i].idx, dab12_map[i].val); + + ctx->decim_ratio = 1; + + /* Load the Channel Filter Coefficients into channel filter bank #2 */ + max2175_set_filter_coeffs(ctx, MAX2175_CH_MSEL, 2, ch_coeff_dab1); +} + +static void max2175_load_fmna_1p0(struct max2175 *ctx) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(fmna1p0_map); i++) + max2175_write(ctx, fmna1p0_map[i].idx, fmna1p0_map[i].val); +} + +static void max2175_load_fmna_2p0(struct max2175 *ctx) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(fmna2p0_map); i++) + max2175_write(ctx, fmna2p0_map[i].idx, fmna2p0_map[i].val); +} + +static void max2175_set_bbfilter(struct max2175 *ctx) +{ + if (MAX2175_IS_BAND_AM(ctx)) { + max2175_write_bits(ctx, 12, 3, 0, ctx->rom_bbf_bw_am); + mxm_dbg(ctx, "set_bbfilter AM: rom %d\n", ctx->rom_bbf_bw_am); + } else if (MAX2175_IS_DAB_MODE(ctx)) { + max2175_write_bits(ctx, 12, 3, 0, ctx->rom_bbf_bw_dab); + mxm_dbg(ctx, "set_bbfilter DAB: rom %d\n", ctx->rom_bbf_bw_dab); + } else { + max2175_write_bits(ctx, 12, 3, 0, ctx->rom_bbf_bw_fm); + mxm_dbg(ctx, "set_bbfilter FM: rom %d\n", ctx->rom_bbf_bw_fm); + } +} + +static bool max2175_set_csm_mode(struct max2175 *ctx, + enum max2175_csm_mode new_mode) +{ + int ret = max2175_poll_csm_ready(ctx); + + if (ret) + return ret; + + max2175_write_bits(ctx, 0, 2, 0, new_mode); + mxm_dbg(ctx, "set csm new mode %d\n", new_mode); + + /* Wait for a fixed settle down time depending on new mode */ + switch (new_mode) { + case MAX2175_PRESET_TUNE: + usleep_range(51100, 51500); /* 51.1ms */ + break; + /* + * Other mode switches need different sleep values depending on band & + * mode + */ + default: + break; + } + + return max2175_poll_csm_ready(ctx); +} + +static int max2175_csm_action(struct max2175 *ctx, + enum max2175_csm_mode action) +{ + int ret; + + mxm_dbg(ctx, "csm_action: %d\n", action); + + /* Other actions can be added in future when needed */ + ret = max2175_set_csm_mode(ctx, MAX2175_LOAD_TO_BUFFER); + if (ret) + return ret; + + return max2175_set_csm_mode(ctx, MAX2175_PRESET_TUNE); +} + +static int max2175_set_lo_freq(struct max2175 *ctx, u32 lo_freq) +{ + u8 lo_mult, loband_bits = 0, vcodiv_bits = 0; + u32 int_desired, frac_desired; + enum max2175_band band; + int ret; + + band = max2175_read_bits(ctx, 5, 1, 0); + switch (band) { + case MAX2175_BAND_AM: + lo_mult = 16; + break; + case MAX2175_BAND_FM: + if (lo_freq <= 74700000) { + lo_mult = 16; + } else if (lo_freq > 74700000 && lo_freq <= 110000000) { + loband_bits = 1; + lo_mult = 8; + } else { + loband_bits = 1; + vcodiv_bits = 3; + lo_mult = 8; + } + break; + case MAX2175_BAND_VHF: + if (lo_freq <= 210000000) + vcodiv_bits = 2; + else + vcodiv_bits = 1; + + loband_bits = 2; + lo_mult = 4; + break; + default: + loband_bits = 3; + vcodiv_bits = 2; + lo_mult = 2; + break; + } + + if (band == MAX2175_BAND_L) + lo_freq /= lo_mult; + else + lo_freq *= lo_mult; + + int_desired = lo_freq / ctx->xtal_freq; + frac_desired = div_u64((u64)(lo_freq % ctx->xtal_freq) << 20, + ctx->xtal_freq); + + /* Check CSM is not busy */ + ret = max2175_poll_csm_ready(ctx); + if (ret) + return ret; + + mxm_dbg(ctx, "lo_mult %u int %u frac %u\n", + lo_mult, int_desired, frac_desired); + + /* Write the calculated values to the appropriate registers */ + max2175_write(ctx, 1, int_desired); + max2175_write_bits(ctx, 2, 3, 0, (frac_desired >> 16) & 0xf); + max2175_write(ctx, 3, frac_desired >> 8); + max2175_write(ctx, 4, frac_desired); + max2175_write_bits(ctx, 5, 3, 2, loband_bits); + max2175_write_bits(ctx, 6, 7, 6, vcodiv_bits); + + return ret; +} + +/* + * Helper similar to DIV_ROUND_CLOSEST but an inline function that accepts s64 + * dividend and s32 divisor + */ +static inline s64 max2175_round_closest(s64 dividend, s32 divisor) +{ + if ((dividend > 0 && divisor > 0) || (dividend < 0 && divisor < 0)) + return div_s64(dividend + divisor / 2, divisor); + + return div_s64(dividend - divisor / 2, divisor); +} + +static int max2175_set_nco_freq(struct max2175 *ctx, s32 nco_freq) +{ + s32 clock_rate = ctx->xtal_freq / ctx->decim_ratio; + u32 nco_reg, abs_nco_freq = abs(nco_freq); + s64 nco_val_desired; + int ret; + + if (abs_nco_freq < clock_rate / 2) { + nco_val_desired = 2 * nco_freq; + } else { + nco_val_desired = 2 * (clock_rate - abs_nco_freq); + if (nco_freq < 0) + nco_val_desired = -nco_val_desired; + } + + nco_reg = max2175_round_closest(nco_val_desired << 20, clock_rate); + + if (nco_freq < 0) + nco_reg += 0x200000; + + /* Check CSM is not busy */ + ret = max2175_poll_csm_ready(ctx); + if (ret) + return ret; + + mxm_dbg(ctx, "freq %d desired %lld reg %u\n", + nco_freq, nco_val_desired, nco_reg); + + /* Write the calculated values to the appropriate registers */ + max2175_write_bits(ctx, 7, 4, 0, (nco_reg >> 16) & 0x1f); + max2175_write(ctx, 8, nco_reg >> 8); + max2175_write(ctx, 9, nco_reg); + + return ret; +} + +static int max2175_set_rf_freq_non_am_bands(struct max2175 *ctx, u64 freq, + u32 lo_pos) +{ + s64 adj_freq, low_if_freq; + int ret; + + mxm_dbg(ctx, "rf_freq: non AM bands\n"); + + if (MAX2175_IS_FM_MODE(ctx)) + low_if_freq = 128000; + else if (MAX2175_IS_FMHD_MODE(ctx)) + low_if_freq = 228000; + else + return max2175_set_lo_freq(ctx, freq); + + if (MAX2175_IS_BAND_VHF(ctx) == (lo_pos == MAX2175_LO_ABOVE_DESIRED)) + adj_freq = freq + low_if_freq; + else + adj_freq = freq - low_if_freq; + + ret = max2175_set_lo_freq(ctx, adj_freq); + if (ret) + return ret; + + return max2175_set_nco_freq(ctx, -low_if_freq); +} + +static int max2175_set_rf_freq(struct max2175 *ctx, u64 freq, u32 lo_pos) +{ + int ret; + + if (MAX2175_IS_BAND_AM(ctx)) + ret = max2175_set_nco_freq(ctx, freq); + else + ret = max2175_set_rf_freq_non_am_bands(ctx, freq, lo_pos); + + mxm_dbg(ctx, "set_rf_freq: ret %d freq %llu\n", ret, freq); + + return ret; +} + +static int max2175_tune_rf_freq(struct max2175 *ctx, u64 freq, u32 hsls) +{ + int ret; + + ret = max2175_set_rf_freq(ctx, freq, hsls); + if (ret) + return ret; + + ret = max2175_csm_action(ctx, MAX2175_BUFFER_PLUS_PRESET_TUNE); + if (ret) + return ret; + + mxm_dbg(ctx, "tune_rf_freq: old %u new %llu\n", ctx->freq, freq); + ctx->freq = freq; + + return ret; +} + +static void max2175_set_hsls(struct max2175 *ctx, u32 lo_pos) +{ + mxm_dbg(ctx, "set_hsls: lo_pos %u\n", lo_pos); + + if ((lo_pos == MAX2175_LO_BELOW_DESIRED) == MAX2175_IS_BAND_VHF(ctx)) + max2175_write_bit(ctx, 5, 4, 1); + else + max2175_write_bit(ctx, 5, 4, 0); +} + +static void max2175_set_eu_rx_mode(struct max2175 *ctx, u32 rx_mode) +{ + switch (rx_mode) { + case MAX2175_EU_FM_1_2: + max2175_load_fmeu_1p2(ctx); + break; + + case MAX2175_DAB_1_2: + max2175_load_dab_1p2(ctx); + break; + } + /* Master is the default setting */ + if (!ctx->master) + max2175_write_bit(ctx, 30, 7, 1); +} + +static void max2175_set_na_rx_mode(struct max2175 *ctx, u32 rx_mode) +{ + switch (rx_mode) { + case MAX2175_NA_FM_1_0: + max2175_load_fmna_1p0(ctx); + break; + case MAX2175_NA_FM_2_0: + max2175_load_fmna_2p0(ctx); + break; + } + /* Master is the default setting */ + if (!ctx->master) + max2175_write_bit(ctx, 30, 7, 1); + + ctx->decim_ratio = 27; + + /* Load the Channel Filter Coefficients into channel filter bank #2 */ + max2175_set_filter_coeffs(ctx, MAX2175_CH_MSEL, 0, ch_coeff_fmna); + max2175_set_filter_coeffs(ctx, MAX2175_EQ_MSEL, 0, + eq_coeff_fmna1_ra02_m6db); +} + +static int max2175_set_rx_mode(struct max2175 *ctx, u32 rx_mode) +{ + mxm_dbg(ctx, "set_rx_mode: %u am_hiz %u\n", rx_mode, ctx->am_hiz); + if (ctx->xtal_freq == MAX2175_EU_XTAL_FREQ) + max2175_set_eu_rx_mode(ctx, rx_mode); + else + max2175_set_na_rx_mode(ctx, rx_mode); + + if (ctx->am_hiz) { + mxm_dbg(ctx, "setting AM HiZ related config\n"); + max2175_write_bit(ctx, 50, 5, 1); + max2175_write_bit(ctx, 90, 7, 1); + max2175_write_bits(ctx, 73, 1, 0, 2); + max2175_write_bits(ctx, 80, 5, 0, 33); + } + + /* Load BB filter trim values saved in ROM */ + max2175_set_bbfilter(ctx); + + /* Set HSLS */ + max2175_set_hsls(ctx, ctx->hsls->cur.val); + + /* Use i2s enable settings */ + max2175_i2s_enable(ctx, ctx->i2s_en->cur.val); + + ctx->mode_resolved = true; + + return 0; +} + +static int max2175_rx_mode_from_freq(struct max2175 *ctx, u32 freq, u32 *mode) +{ + unsigned int i; + int band = max2175_band_from_freq(freq); + + /* Pick the first match always */ + for (i = 0; i <= ctx->rx_mode->maximum; i++) { + if (ctx->rx_modes[i].band == band) { + *mode = i; + mxm_dbg(ctx, "rx_mode_from_freq: freq %u mode %d\n", + freq, *mode); + return 0; + } + } + + return -EINVAL; +} + +static bool max2175_freq_rx_mode_valid(struct max2175 *ctx, + u32 mode, u32 freq) +{ + int band = max2175_band_from_freq(freq); + + return (ctx->rx_modes[mode].band == band); +} + +static void max2175_load_adc_presets(struct max2175 *ctx) +{ + unsigned int i, j; + + for (i = 0; i < ARRAY_SIZE(adc_presets); i++) + for (j = 0; j < ARRAY_SIZE(adc_presets[0]); j++) + max2175_write(ctx, 146 + j + i * 55, adc_presets[i][j]); +} + +static int max2175_init_power_manager(struct max2175 *ctx) +{ + int ret; + + /* Execute on-chip power-up/calibration */ + max2175_write_bit(ctx, 99, 2, 0); + usleep_range(1000, 1500); + max2175_write_bit(ctx, 99, 2, 1); + + /* Wait for the power manager to finish. */ + ret = max2175_poll_timeout(ctx, 69, 7, 7, 1, 50000); + if (ret) + mxm_err(ctx, "init pm failed\n"); + + return ret; +} + +static int max2175_recalibrate_adc(struct max2175 *ctx) +{ + int ret; + + /* ADC Re-calibration */ + max2175_write(ctx, 150, 0xff); + max2175_write(ctx, 205, 0xff); + max2175_write(ctx, 147, 0x20); + max2175_write(ctx, 147, 0x00); + max2175_write(ctx, 202, 0x20); + max2175_write(ctx, 202, 0x00); + + ret = max2175_poll_timeout(ctx, 69, 4, 3, 3, 50000); + if (ret) + mxm_err(ctx, "adc recalibration failed\n"); + + return ret; +} + +static u8 max2175_read_rom(struct max2175 *ctx, u8 row) +{ + u8 data = 0; + + max2175_write_bit(ctx, 56, 4, 0); + max2175_write_bits(ctx, 56, 3, 0, row); + + usleep_range(2000, 2500); + max2175_read(ctx, 58, &data); + + max2175_write_bits(ctx, 56, 3, 0, 0); + + mxm_dbg(ctx, "read_rom: row %d data 0x%02x\n", row, data); + + return data; +} + +static void max2175_load_from_rom(struct max2175 *ctx) +{ + u8 data = 0; + + data = max2175_read_rom(ctx, 0); + ctx->rom_bbf_bw_am = data & 0x0f; + max2175_write_bits(ctx, 81, 3, 0, data >> 4); + + data = max2175_read_rom(ctx, 1); + ctx->rom_bbf_bw_fm = data & 0x0f; + ctx->rom_bbf_bw_dab = data >> 4; + + data = max2175_read_rom(ctx, 2); + max2175_write_bits(ctx, 82, 4, 0, data & 0x1f); + max2175_write_bits(ctx, 82, 7, 5, data >> 5); + + data = max2175_read_rom(ctx, 3); + if (ctx->am_hiz) { + data &= 0x0f; + data |= (max2175_read_rom(ctx, 7) & 0x40) >> 2; + if (!data) + data |= 2; + } else { + data = (data & 0xf0) >> 4; + data |= (max2175_read_rom(ctx, 7) & 0x80) >> 3; + if (!data) + data |= 30; + } + max2175_write_bits(ctx, 80, 5, 0, data + 31); + + data = max2175_read_rom(ctx, 6); + max2175_write_bits(ctx, 81, 7, 6, data >> 6); +} + +static void max2175_load_full_fm_eu_1p0(struct max2175 *ctx) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(full_fm_eu_1p0); i++) + max2175_write(ctx, i + 1, full_fm_eu_1p0[i]); + + usleep_range(5000, 5500); + ctx->decim_ratio = 36; +} + +static void max2175_load_full_fm_na_1p0(struct max2175 *ctx) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(full_fm_na_1p0); i++) + max2175_write(ctx, i + 1, full_fm_na_1p0[i]); + + usleep_range(5000, 5500); + ctx->decim_ratio = 27; +} + +static int max2175_core_init(struct max2175 *ctx, u32 refout_bits) +{ + int ret; + + /* MAX2175 uses 36.864MHz clock for EU & 40.154MHz for NA region */ + if (ctx->xtal_freq == MAX2175_EU_XTAL_FREQ) + max2175_load_full_fm_eu_1p0(ctx); + else + max2175_load_full_fm_na_1p0(ctx); + + /* The default settings assume master */ + if (!ctx->master) + max2175_write_bit(ctx, 30, 7, 1); + + mxm_dbg(ctx, "refout_bits %u\n", refout_bits); + + /* Set REFOUT */ + max2175_write_bits(ctx, 56, 7, 5, refout_bits); + + /* ADC Reset */ + max2175_write_bit(ctx, 99, 1, 0); + usleep_range(1000, 1500); + max2175_write_bit(ctx, 99, 1, 1); + + /* Load ADC preset values */ + max2175_load_adc_presets(ctx); + + /* Initialize the power management state machine */ + ret = max2175_init_power_manager(ctx); + if (ret) + return ret; + + /* Recalibrate ADC */ + ret = max2175_recalibrate_adc(ctx); + if (ret) + return ret; + + /* Load ROM values to appropriate registers */ + max2175_load_from_rom(ctx); + + if (ctx->xtal_freq == MAX2175_EU_XTAL_FREQ) { + /* Load FIR coefficients into bank 0 */ + max2175_set_filter_coeffs(ctx, MAX2175_CH_MSEL, 0, + ch_coeff_fmeu); + max2175_set_filter_coeffs(ctx, MAX2175_EQ_MSEL, 0, + eq_coeff_fmeu1_ra02_m6db); + } else { + /* Load FIR coefficients into bank 0 */ + max2175_set_filter_coeffs(ctx, MAX2175_CH_MSEL, 0, + ch_coeff_fmna); + max2175_set_filter_coeffs(ctx, MAX2175_EQ_MSEL, 0, + eq_coeff_fmna1_ra02_m6db); + } + mxm_dbg(ctx, "core initialized\n"); + + return 0; +} + +static void max2175_s_ctrl_rx_mode(struct max2175 *ctx, u32 rx_mode) +{ + /* Load mode. Range check already done */ + max2175_set_rx_mode(ctx, rx_mode); + + mxm_dbg(ctx, "s_ctrl_rx_mode: %u curr freq %u\n", rx_mode, ctx->freq); + + /* Check if current freq valid for mode & update */ + if (max2175_freq_rx_mode_valid(ctx, rx_mode, ctx->freq)) + max2175_tune_rf_freq(ctx, ctx->freq, ctx->hsls->cur.val); + else + /* Use default freq of mode if current freq is not valid */ + max2175_tune_rf_freq(ctx, ctx->rx_modes[rx_mode].freq, + ctx->hsls->cur.val); +} + +static int max2175_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct max2175 *ctx = max2175_from_ctrl_hdl(ctrl->handler); + + mxm_dbg(ctx, "s_ctrl: id 0x%x, val %u\n", ctrl->id, ctrl->val); + switch (ctrl->id) { + case V4L2_CID_MAX2175_I2S_ENABLE: + max2175_i2s_enable(ctx, ctrl->val); + break; + case V4L2_CID_MAX2175_HSLS: + max2175_set_hsls(ctx, ctrl->val); + break; + case V4L2_CID_MAX2175_RX_MODE: + max2175_s_ctrl_rx_mode(ctx, ctrl->val); + break; + } + + return 0; +} + +static u32 max2175_get_lna_gain(struct max2175 *ctx) +{ + enum max2175_band band = max2175_read_bits(ctx, 5, 1, 0); + + switch (band) { + case MAX2175_BAND_AM: + return max2175_read_bits(ctx, 51, 3, 0); + case MAX2175_BAND_FM: + return max2175_read_bits(ctx, 50, 3, 0); + case MAX2175_BAND_VHF: + return max2175_read_bits(ctx, 52, 5, 0); + default: + return 0; + } +} + +static int max2175_g_volatile_ctrl(struct v4l2_ctrl *ctrl) +{ + struct max2175 *ctx = max2175_from_ctrl_hdl(ctrl->handler); + + switch (ctrl->id) { + case V4L2_CID_RF_TUNER_LNA_GAIN: + ctrl->val = max2175_get_lna_gain(ctx); + break; + case V4L2_CID_RF_TUNER_IF_GAIN: + ctrl->val = max2175_read_bits(ctx, 49, 4, 0); + break; + case V4L2_CID_RF_TUNER_PLL_LOCK: + ctrl->val = (max2175_read_bits(ctx, 60, 7, 6) == 3); + break; + } + + return 0; +}; + +static int max2175_set_freq_and_mode(struct max2175 *ctx, u32 freq) +{ + u32 rx_mode; + int ret; + + /* Get band from frequency */ + ret = max2175_rx_mode_from_freq(ctx, freq, &rx_mode); + if (ret) + return ret; + + mxm_dbg(ctx, "set_freq_and_mode: freq %u rx_mode %d\n", freq, rx_mode); + + /* Load mode */ + max2175_set_rx_mode(ctx, rx_mode); + ctx->rx_mode->cur.val = rx_mode; + + /* Tune to the new freq given */ + return max2175_tune_rf_freq(ctx, freq, ctx->hsls->cur.val); +} + +static int max2175_s_frequency(struct v4l2_subdev *sd, + const struct v4l2_frequency *vf) +{ + struct max2175 *ctx = max2175_from_sd(sd); + u32 freq; + int ret = 0; + + mxm_dbg(ctx, "s_freq: new %u curr %u, mode_resolved %d\n", + vf->frequency, ctx->freq, ctx->mode_resolved); + + if (vf->tuner != 0) + return -EINVAL; + + freq = clamp(vf->frequency, ctx->bands_rf->rangelow, + ctx->bands_rf->rangehigh); + + /* Check new freq valid for rx_mode if already resolved */ + if (ctx->mode_resolved && + max2175_freq_rx_mode_valid(ctx, ctx->rx_mode->cur.val, freq)) + ret = max2175_tune_rf_freq(ctx, freq, ctx->hsls->cur.val); + else + /* Find default rx_mode for freq and tune to it */ + ret = max2175_set_freq_and_mode(ctx, freq); + + mxm_dbg(ctx, "s_freq: ret %d curr %u mode_resolved %d mode %u\n", + ret, ctx->freq, ctx->mode_resolved, ctx->rx_mode->cur.val); + + return ret; +} + +static int max2175_g_frequency(struct v4l2_subdev *sd, + struct v4l2_frequency *vf) +{ + struct max2175 *ctx = max2175_from_sd(sd); + int ret = 0; + + if (vf->tuner != 0) + return -EINVAL; + + /* RF freq */ + vf->type = V4L2_TUNER_RF; + vf->frequency = ctx->freq; + + return ret; +} + +static int max2175_enum_freq_bands(struct v4l2_subdev *sd, + struct v4l2_frequency_band *band) +{ + struct max2175 *ctx = max2175_from_sd(sd); + + if (band->tuner != 0 || band->index != 0) + return -EINVAL; + + *band = *ctx->bands_rf; + + return 0; +} + +static int max2175_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +{ + struct max2175 *ctx = max2175_from_sd(sd); + + if (vt->index > 0) + return -EINVAL; + + strlcpy(vt->name, "RF", sizeof(vt->name)); + vt->type = V4L2_TUNER_RF; + vt->capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS; + vt->rangelow = ctx->bands_rf->rangelow; + vt->rangehigh = ctx->bands_rf->rangehigh; + + return 0; +} + +static int max2175_s_tuner(struct v4l2_subdev *sd, const struct v4l2_tuner *vt) +{ + /* Check tuner index is valid */ + if (vt->index > 0) + return -EINVAL; + + return 0; +} + +static const struct v4l2_subdev_tuner_ops max2175_tuner_ops = { + .s_frequency = max2175_s_frequency, + .g_frequency = max2175_g_frequency, + .enum_freq_bands = max2175_enum_freq_bands, + .g_tuner = max2175_g_tuner, + .s_tuner = max2175_s_tuner, +}; + +static const struct v4l2_subdev_ops max2175_ops = { + .tuner = &max2175_tuner_ops, +}; + +static const struct v4l2_ctrl_ops max2175_ctrl_ops = { + .s_ctrl = max2175_s_ctrl, + .g_volatile_ctrl = max2175_g_volatile_ctrl, +}; + +/* + * I2S output enable/disable configuration. This is a private control. + * Refer to Documentation/media/v4l-drivers/max2175 for more details. + */ +static const struct v4l2_ctrl_config max2175_i2s_en = { + .ops = &max2175_ctrl_ops, + .id = V4L2_CID_MAX2175_I2S_ENABLE, + .name = "I2S Enable", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .min = 0, + .max = 1, + .step = 1, + .def = 1, + .is_private = 1, +}; + +/* + * HSLS value control LO freq adjacent location configuration. + * Refer to Documentation/media/v4l-drivers/max2175 for more details. + */ +static const struct v4l2_ctrl_config max2175_hsls = { + .ops = &max2175_ctrl_ops, + .id = V4L2_CID_MAX2175_HSLS, + .name = "HSLS Above/Below Desired", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .min = 0, + .max = 1, + .step = 1, + .def = 1, +}; + +/* + * Rx modes below are a set of preset configurations that decides the tuner's + * sck and sample rate of transmission. They are separate for EU & NA regions. + * Refer to Documentation/media/v4l-drivers/max2175 for more details. + */ +static const char * const max2175_ctrl_eu_rx_modes[] = { + [MAX2175_EU_FM_1_2] = "EU FM 1.2", + [MAX2175_DAB_1_2] = "DAB 1.2", +}; + +static const char * const max2175_ctrl_na_rx_modes[] = { + [MAX2175_NA_FM_1_0] = "NA FM 1.0", + [MAX2175_NA_FM_2_0] = "NA FM 2.0", +}; + +static const struct v4l2_ctrl_config max2175_eu_rx_mode = { + .ops = &max2175_ctrl_ops, + .id = V4L2_CID_MAX2175_RX_MODE, + .name = "RX Mode", + .type = V4L2_CTRL_TYPE_MENU, + .max = ARRAY_SIZE(max2175_ctrl_eu_rx_modes) - 1, + .def = 0, + .qmenu = max2175_ctrl_eu_rx_modes, +}; + +static const struct v4l2_ctrl_config max2175_na_rx_mode = { + .ops = &max2175_ctrl_ops, + .id = V4L2_CID_MAX2175_RX_MODE, + .name = "RX Mode", + .type = V4L2_CTRL_TYPE_MENU, + .max = ARRAY_SIZE(max2175_ctrl_na_rx_modes) - 1, + .def = 0, + .qmenu = max2175_ctrl_na_rx_modes, +}; + +static int max2175_refout_load_to_bits(struct i2c_client *client, u32 load, + u32 *bits) +{ + if (load >= 0 && load <= 40) + *bits = load / 10; + else if (load >= 60 && load <= 70) + *bits = load / 10 - 1; + else + return -EINVAL; + + return 0; +} + +static int max2175_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + bool master = true, am_hiz = false; + u32 refout_load, refout_bits = 0; /* REFOUT disabled */ + struct v4l2_ctrl_handler *hdl; + struct fwnode_handle *fwnode; + struct device_node *np; + struct v4l2_subdev *sd; + struct regmap *regmap; + struct max2175 *ctx; + struct clk *clk; + int ret; + + /* Parse DT properties */ + np = of_parse_phandle(client->dev.of_node, "maxim,master", 0); + if (np) { + master = false; /* Slave tuner */ + of_node_put(np); + } + + fwnode = of_fwnode_handle(client->dev.of_node); + if (fwnode_property_present(fwnode, "maxim,am-hiz-filter")) + am_hiz = true; + + if (!fwnode_property_read_u32(fwnode, "maxim,refout-load", + &refout_load)) { + ret = max2175_refout_load_to_bits(client, refout_load, + &refout_bits); + if (ret) { + dev_err(&client->dev, "invalid refout_load %u\n", + refout_load); + return -EINVAL; + } + } + + clk = devm_clk_get(&client->dev, NULL); + if (IS_ERR(clk)) { + ret = PTR_ERR(clk); + dev_err(&client->dev, "cannot get clock %d\n", ret); + return -ENODEV; + } + + regmap = devm_regmap_init_i2c(client, &max2175_regmap_config); + if (IS_ERR(regmap)) { + ret = PTR_ERR(regmap); + dev_err(&client->dev, "regmap init failed %d\n", ret); + return -ENODEV; + } + + /* Alloc tuner context */ + ctx = devm_kzalloc(&client->dev, sizeof(*ctx), GFP_KERNEL); + if (ctx == NULL) + return -ENOMEM; + + sd = &ctx->sd; + ctx->master = master; + ctx->am_hiz = am_hiz; + ctx->mode_resolved = false; + ctx->regmap = regmap; + ctx->xtal_freq = clk_get_rate(clk); + dev_info(&client->dev, "xtal freq %luHz\n", ctx->xtal_freq); + + v4l2_i2c_subdev_init(sd, client, &max2175_ops); + ctx->client = client; + + sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE; + + /* Controls */ + hdl = &ctx->ctrl_hdl; + ret = v4l2_ctrl_handler_init(hdl, 7); + if (ret) + return ret; + + ctx->lna_gain = v4l2_ctrl_new_std(hdl, &max2175_ctrl_ops, + V4L2_CID_RF_TUNER_LNA_GAIN, + 0, 63, 1, 0); + ctx->lna_gain->flags |= (V4L2_CTRL_FLAG_VOLATILE | + V4L2_CTRL_FLAG_READ_ONLY); + ctx->if_gain = v4l2_ctrl_new_std(hdl, &max2175_ctrl_ops, + V4L2_CID_RF_TUNER_IF_GAIN, + 0, 31, 1, 0); + ctx->if_gain->flags |= (V4L2_CTRL_FLAG_VOLATILE | + V4L2_CTRL_FLAG_READ_ONLY); + ctx->pll_lock = v4l2_ctrl_new_std(hdl, &max2175_ctrl_ops, + V4L2_CID_RF_TUNER_PLL_LOCK, + 0, 1, 1, 0); + ctx->pll_lock->flags |= (V4L2_CTRL_FLAG_VOLATILE | + V4L2_CTRL_FLAG_READ_ONLY); + ctx->i2s_en = v4l2_ctrl_new_custom(hdl, &max2175_i2s_en, NULL); + ctx->hsls = v4l2_ctrl_new_custom(hdl, &max2175_hsls, NULL); + + if (ctx->xtal_freq == MAX2175_EU_XTAL_FREQ) { + ctx->rx_mode = v4l2_ctrl_new_custom(hdl, + &max2175_eu_rx_mode, NULL); + ctx->rx_modes = eu_rx_modes; + ctx->bands_rf = &eu_bands_rf; + } else { + ctx->rx_mode = v4l2_ctrl_new_custom(hdl, + &max2175_na_rx_mode, NULL); + ctx->rx_modes = na_rx_modes; + ctx->bands_rf = &na_bands_rf; + } + ctx->sd.ctrl_handler = &ctx->ctrl_hdl; + + /* Set the defaults */ + ctx->freq = ctx->bands_rf->rangelow; + + /* Register subdev */ + ret = v4l2_async_register_subdev(sd); + if (ret) { + dev_err(&client->dev, "register subdev failed\n"); + goto err_reg; + } + + /* Initialize device */ + ret = max2175_core_init(ctx, refout_bits); + if (ret) + goto err_init; + + ret = v4l2_ctrl_handler_setup(hdl); + if (ret) + goto err_init; + + return 0; + +err_init: + v4l2_async_unregister_subdev(sd); +err_reg: + v4l2_ctrl_handler_free(&ctx->ctrl_hdl); + + return ret; +} + +static int max2175_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct max2175 *ctx = max2175_from_sd(sd); + + v4l2_ctrl_handler_free(&ctx->ctrl_hdl); + v4l2_async_unregister_subdev(sd); + + return 0; +} + +static const struct i2c_device_id max2175_id[] = { + { DRIVER_NAME, 0}, + {}, +}; +MODULE_DEVICE_TABLE(i2c, max2175_id); + +static const struct of_device_id max2175_of_ids[] = { + { .compatible = "maxim,max2175", }, + { } +}; +MODULE_DEVICE_TABLE(of, max2175_of_ids); + +static struct i2c_driver max2175_driver = { + .driver = { + .name = DRIVER_NAME, + .of_match_table = max2175_of_ids, + }, + .probe = max2175_probe, + .remove = max2175_remove, + .id_table = max2175_id, +}; + +module_i2c_driver(max2175_driver); + +MODULE_DESCRIPTION("Maxim MAX2175 RF to Bits tuner driver"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Ramesh Shanmugasundaram "); diff --git a/drivers/media/i2c/max2175.h b/drivers/media/i2c/max2175.h new file mode 100644 index 000000000000..eb43373ce7e2 --- /dev/null +++ b/drivers/media/i2c/max2175.h @@ -0,0 +1,109 @@ +/* + * Maxim Integrated MAX2175 RF to Bits tuner driver + * + * This driver & most of the hard coded values are based on the reference + * application delivered by Maxim for this device. + * + * Copyright (C) 2016 Maxim Integrated Products + * Copyright (C) 2017 Renesas Electronics Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __MAX2175_H__ +#define __MAX2175_H__ + +#define MAX2175_EU_XTAL_FREQ 36864000 /* In Hz */ +#define MAX2175_NA_XTAL_FREQ 40186125 /* In Hz */ + +enum max2175_region { + MAX2175_REGION_EU = 0, /* Europe */ + MAX2175_REGION_NA, /* North America */ +}; + +enum max2175_band { + MAX2175_BAND_AM = 0, + MAX2175_BAND_FM, + MAX2175_BAND_VHF, + MAX2175_BAND_L, +}; + +enum max2175_eu_mode { + /* EU modes */ + MAX2175_EU_FM_1_2 = 0, + MAX2175_DAB_1_2, + + /* + * Other possible modes to add in future + * MAX2175_DAB_1_0, + * MAX2175_DAB_1_3, + * MAX2175_EU_FM_2_2, + * MAX2175_EU_FMHD_4_0, + * MAX2175_EU_AM_1_0, + * MAX2175_EU_AM_2_2, + */ +}; + +enum max2175_na_mode { + /* NA modes */ + MAX2175_NA_FM_1_0 = 0, + MAX2175_NA_FM_2_0, + + /* + * Other possible modes to add in future + * MAX2175_NA_FMHD_1_0, + * MAX2175_NA_FMHD_1_2, + * MAX2175_NA_AM_1_0, + * MAX2175_NA_AM_1_2, + */ +}; + +/* Supported I2S modes */ +enum { + MAX2175_I2S_MODE0 = 0, + MAX2175_I2S_MODE1, + MAX2175_I2S_MODE2, + MAX2175_I2S_MODE3, + MAX2175_I2S_MODE4, +}; + +/* Coefficient table groups */ +enum { + MAX2175_CH_MSEL = 0, + MAX2175_EQ_MSEL, + MAX2175_AA_MSEL, +}; + +/* HSLS LO injection polarity */ +enum { + MAX2175_LO_BELOW_DESIRED = 0, + MAX2175_LO_ABOVE_DESIRED, +}; + +/* Channel FSM modes */ +enum max2175_csm_mode { + MAX2175_LOAD_TO_BUFFER = 0, + MAX2175_PRESET_TUNE, + MAX2175_SEARCH, + MAX2175_AF_UPDATE, + MAX2175_JUMP_FAST_TUNE, + MAX2175_CHECK, + MAX2175_LOAD_AND_SWAP, + MAX2175_END, + MAX2175_BUFFER_PLUS_PRESET_TUNE, + MAX2175_BUFFER_PLUS_SEARCH, + MAX2175_BUFFER_PLUS_AF_UPDATE, + MAX2175_BUFFER_PLUS_JUMP_FAST_TUNE, + MAX2175_BUFFER_PLUS_CHECK, + MAX2175_BUFFER_PLUS_LOAD_AND_SWAP, + MAX2175_NO_ACTION +}; + +#endif /* __MAX2175_H__ */ diff --git a/include/uapi/linux/max2175.h b/include/uapi/linux/max2175.h new file mode 100644 index 000000000000..3ef5d264440f --- /dev/null +++ b/include/uapi/linux/max2175.h @@ -0,0 +1,28 @@ +/* + * max2175.h + * + * Maxim Integrated MAX2175 RF to Bits tuner driver - user space header file. + * + * Copyright (C) 2016 Maxim Integrated Products + * Copyright (C) 2017 Renesas Electronics Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __UAPI_MAX2175_H_ +#define __UAPI_MAX2175_H_ + +#include + +#define V4L2_CID_MAX2175_I2S_ENABLE (V4L2_CID_USER_MAX217X_BASE + 0x01) +#define V4L2_CID_MAX2175_HSLS (V4L2_CID_USER_MAX217X_BASE + 0x02) +#define V4L2_CID_MAX2175_RX_MODE (V4L2_CID_USER_MAX217X_BASE + 0x03) + +#endif /* __UAPI_MAX2175_H_ */ -- cgit v1.2.3-58-ga151 From c28f2118a2129f8e2c1cdf2454ffe4833885edff Mon Sep 17 00:00:00 2001 From: Ramesh Shanmugasundaram Date: Mon, 12 Jun 2017 10:26:16 -0300 Subject: [media] media: Add new SDR formats PC16, PC18 & PC20 This patch adds support for the three new SDR formats. These formats were prefixed with "planar" indicating I & Q data are not interleaved as in other formats. Here, I & Q data constitutes the top half and bottom half of the received buffer respectively. V4L2_SDR_FMT_PCU16BE - 14-bit complex (I & Q) unsigned big-endian sample inside 16-bit. V4L2 FourCC: PC16 V4L2_SDR_FMT_PCU18BE - 16-bit complex (I & Q) unsigned big-endian sample inside 18-bit. V4L2 FourCC: PC18 V4L2_SDR_FMT_PCU20BE - 18-bit complex (I & Q) unsigned big-endian sample inside 20-bit. V4L2 FourCC: PC20 Signed-off-by: Ramesh Shanmugasundaram Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ioctl.c | 3 +++ include/uapi/linux/videodev2.h | 3 +++ 2 files changed, 6 insertions(+) diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index 4f27cfa134a1..ce40183d9daa 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -1229,6 +1229,9 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt) case V4L2_SDR_FMT_CS8: descr = "Complex S8"; break; case V4L2_SDR_FMT_CS14LE: descr = "Complex S14LE"; break; case V4L2_SDR_FMT_RU12LE: descr = "Real U12LE"; break; + case V4L2_SDR_FMT_PCU16BE: descr = "Planar Complex U16BE"; break; + case V4L2_SDR_FMT_PCU18BE: descr = "Planar Complex U18BE"; break; + case V4L2_SDR_FMT_PCU20BE: descr = "Planar Complex U20BE"; break; case V4L2_TCH_FMT_DELTA_TD16: descr = "16-bit signed deltas"; break; case V4L2_TCH_FMT_DELTA_TD08: descr = "8-bit signed deltas"; break; case V4L2_TCH_FMT_TU16: descr = "16-bit unsigned touch data"; break; diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index 2b8feb86d09e..45cf7359822c 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -669,6 +669,9 @@ struct v4l2_pix_format { #define V4L2_SDR_FMT_CS8 v4l2_fourcc('C', 'S', '0', '8') /* complex s8 */ #define V4L2_SDR_FMT_CS14LE v4l2_fourcc('C', 'S', '1', '4') /* complex s14le */ #define V4L2_SDR_FMT_RU12LE v4l2_fourcc('R', 'U', '1', '2') /* real u12le */ +#define V4L2_SDR_FMT_PCU16BE v4l2_fourcc('P', 'C', '1', '6') /* planar complex u16be */ +#define V4L2_SDR_FMT_PCU18BE v4l2_fourcc('P', 'C', '1', '8') /* planar complex u18be */ +#define V4L2_SDR_FMT_PCU20BE v4l2_fourcc('P', 'C', '2', '0') /* planar complex u20be */ /* Touch formats - used for Touch devices */ #define V4L2_TCH_FMT_DELTA_TD16 v4l2_fourcc('T', 'D', '1', '6') /* 16-bit signed deltas */ -- cgit v1.2.3-58-ga151 From e744ee280311ac220930ae49417b79165c887a3a Mon Sep 17 00:00:00 2001 From: Ramesh Shanmugasundaram Date: Mon, 12 Jun 2017 10:26:17 -0300 Subject: [media] doc_rst: media: New SDR formats PC16, PC18 & PC20 This patch adds documentation for the three new SDR formats V4L2_SDR_FMT_PCU16BE V4L2_SDR_FMT_PCU18BE V4L2_SDR_FMT_PCU20BE Signed-off-by: Ramesh Shanmugasundaram Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../media/uapi/v4l/pixfmt-sdr-pcu16be.rst | 55 ++++++++++++++++++++++ .../media/uapi/v4l/pixfmt-sdr-pcu18be.rst | 55 ++++++++++++++++++++++ .../media/uapi/v4l/pixfmt-sdr-pcu20be.rst | 54 +++++++++++++++++++++ Documentation/media/uapi/v4l/sdr-formats.rst | 3 ++ 4 files changed, 167 insertions(+) create mode 100644 Documentation/media/uapi/v4l/pixfmt-sdr-pcu16be.rst create mode 100644 Documentation/media/uapi/v4l/pixfmt-sdr-pcu18be.rst create mode 100644 Documentation/media/uapi/v4l/pixfmt-sdr-pcu20be.rst diff --git a/Documentation/media/uapi/v4l/pixfmt-sdr-pcu16be.rst b/Documentation/media/uapi/v4l/pixfmt-sdr-pcu16be.rst new file mode 100644 index 000000000000..2de1b1a0f517 --- /dev/null +++ b/Documentation/media/uapi/v4l/pixfmt-sdr-pcu16be.rst @@ -0,0 +1,55 @@ +.. -*- coding: utf-8; mode: rst -*- + +.. _V4L2-SDR-FMT-PCU16BE: + +****************************** +V4L2_SDR_FMT_PCU16BE ('PC16') +****************************** + +Planar complex unsigned 16-bit big endian IQ sample + +Description +=========== + +This format contains a sequence of complex number samples. Each complex +number consist of two parts called In-phase and Quadrature (IQ). Both I +and Q are represented as a 16 bit unsigned big endian number stored in +32 bit space. The remaining unused bits within the 32 bit space will be +padded with 0. I value starts first and Q value starts at an offset +equalling half of the buffer size (i.e.) offset = buffersize/2. Out of +the 16 bits, bit 15:2 (14 bit) is data and bit 1:0 (2 bit) can be any +value. + +**Byte Order.** +Each cell is one byte. + +.. flat-table:: + :header-rows: 1 + :stub-columns: 0 + + * - Offset: + - Byte B0 + - Byte B1 + - Byte B2 + - Byte B3 + * - start + 0: + - I'\ :sub:`0[13:6]` + - I'\ :sub:`0[5:0]; B1[1:0]=pad` + - pad + - pad + * - start + 4: + - I'\ :sub:`1[13:6]` + - I'\ :sub:`1[5:0]; B1[1:0]=pad` + - pad + - pad + * - ... + * - start + offset: + - Q'\ :sub:`0[13:6]` + - Q'\ :sub:`0[5:0]; B1[1:0]=pad` + - pad + - pad + * - start + offset + 4: + - Q'\ :sub:`1[13:6]` + - Q'\ :sub:`1[5:0]; B1[1:0]=pad` + - pad + - pad diff --git a/Documentation/media/uapi/v4l/pixfmt-sdr-pcu18be.rst b/Documentation/media/uapi/v4l/pixfmt-sdr-pcu18be.rst new file mode 100644 index 000000000000..da8b26bf6b95 --- /dev/null +++ b/Documentation/media/uapi/v4l/pixfmt-sdr-pcu18be.rst @@ -0,0 +1,55 @@ +.. -*- coding: utf-8; mode: rst -*- + +.. _V4L2-SDR-FMT-PCU18BE: + +****************************** +V4L2_SDR_FMT_PCU18BE ('PC18') +****************************** + +Planar complex unsigned 18-bit big endian IQ sample + +Description +=========== + +This format contains a sequence of complex number samples. Each complex +number consist of two parts called In-phase and Quadrature (IQ). Both I +and Q are represented as a 18 bit unsigned big endian number stored in +32 bit space. The remaining unused bits within the 32 bit space will be +padded with 0. I value starts first and Q value starts at an offset +equalling half of the buffer size (i.e.) offset = buffersize/2. Out of +the 18 bits, bit 17:2 (16 bit) is data and bit 1:0 (2 bit) can be any +value. + +**Byte Order.** +Each cell is one byte. + +.. flat-table:: + :header-rows: 1 + :stub-columns: 0 + + * - Offset: + - Byte B0 + - Byte B1 + - Byte B2 + - Byte B3 + * - start + 0: + - I'\ :sub:`0[17:10]` + - I'\ :sub:`0[9:2]` + - I'\ :sub:`0[1:0]; B2[5:0]=pad` + - pad + * - start + 4: + - I'\ :sub:`1[17:10]` + - I'\ :sub:`1[9:2]` + - I'\ :sub:`1[1:0]; B2[5:0]=pad` + - pad + * - ... + * - start + offset: + - Q'\ :sub:`0[17:10]` + - Q'\ :sub:`0[9:2]` + - Q'\ :sub:`0[1:0]; B2[5:0]=pad` + - pad + * - start + offset + 4: + - Q'\ :sub:`1[17:10]` + - Q'\ :sub:`1[9:2]` + - Q'\ :sub:`1[1:0]; B2[5:0]=pad` + - pad diff --git a/Documentation/media/uapi/v4l/pixfmt-sdr-pcu20be.rst b/Documentation/media/uapi/v4l/pixfmt-sdr-pcu20be.rst new file mode 100644 index 000000000000..5499eed39477 --- /dev/null +++ b/Documentation/media/uapi/v4l/pixfmt-sdr-pcu20be.rst @@ -0,0 +1,54 @@ +.. -*- coding: utf-8; mode: rst -*- +.. _V4L2-SDR-FMT-PCU20BE: + +****************************** +V4L2_SDR_FMT_PCU20BE ('PC20') +****************************** + +Planar complex unsigned 20-bit big endian IQ sample + +Description +=========== + +This format contains a sequence of complex number samples. Each complex +number consist of two parts called In-phase and Quadrature (IQ). Both I +and Q are represented as a 20 bit unsigned big endian number stored in +32 bit space. The remaining unused bits within the 32 bit space will be +padded with 0. I value starts first and Q value starts at an offset +equalling half of the buffer size (i.e.) offset = buffersize/2. Out of +the 20 bits, bit 19:2 (18 bit) is data and bit 1:0 (2 bit) can be any +value. + +**Byte Order.** +Each cell is one byte. + +.. flat-table:: + :header-rows: 1 + :stub-columns: 0 + + * - Offset: + - Byte B0 + - Byte B1 + - Byte B2 + - Byte B3 + * - start + 0: + - I'\ :sub:`0[19:12]` + - I'\ :sub:`0[11:4]` + - I'\ :sub:`0[3:0]; B2[3:0]=pad` + - pad + * - start + 4: + - I'\ :sub:`1[19:12]` + - I'\ :sub:`1[11:4]` + - I'\ :sub:`1[3:0]; B2[3:0]=pad` + - pad + * - ... + * - start + offset: + - Q'\ :sub:`0[19:12]` + - Q'\ :sub:`0[11:4]` + - Q'\ :sub:`0[3:0]; B2[3:0]=pad` + - pad + * - start + offset + 4: + - Q'\ :sub:`1[19:12]` + - Q'\ :sub:`1[11:4]` + - Q'\ :sub:`1[3:0]; B2[3:0]=pad` + - pad diff --git a/Documentation/media/uapi/v4l/sdr-formats.rst b/Documentation/media/uapi/v4l/sdr-formats.rst index f863c08f1add..2037f5bad727 100644 --- a/Documentation/media/uapi/v4l/sdr-formats.rst +++ b/Documentation/media/uapi/v4l/sdr-formats.rst @@ -17,3 +17,6 @@ These formats are used for :ref:`SDR ` interface only. pixfmt-sdr-cs08 pixfmt-sdr-cs14le pixfmt-sdr-ru12le + pixfmt-sdr-pcu16be + pixfmt-sdr-pcu18be + pixfmt-sdr-pcu20be -- cgit v1.2.3-58-ga151 From 54a707ea011601f06cc5ef7fe6d0daac71ab0622 Mon Sep 17 00:00:00 2001 From: Ramesh Shanmugasundaram Date: Mon, 12 Jun 2017 10:26:18 -0300 Subject: [media] dt-bindings: media: Add Renesas R-Car DRIF binding Add binding documentation for Renesas R-Car Digital Radio Interface (DRIF) controller. Signed-off-by: Ramesh Shanmugasundaram Acked-by: Rob Herring Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../devicetree/bindings/media/renesas,drif.txt | 176 +++++++++++++++++++++ 1 file changed, 176 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/renesas,drif.txt diff --git a/Documentation/devicetree/bindings/media/renesas,drif.txt b/Documentation/devicetree/bindings/media/renesas,drif.txt new file mode 100644 index 000000000000..39516b94c28f --- /dev/null +++ b/Documentation/devicetree/bindings/media/renesas,drif.txt @@ -0,0 +1,176 @@ +Renesas R-Car Gen3 Digital Radio Interface controller (DRIF) +------------------------------------------------------------ + +R-Car Gen3 DRIF is a SPI like receive only slave device. A general +representation of DRIF interfacing with a master device is shown below. + ++---------------------+ +---------------------+ +| |-----SCK------->|CLK | +| Master |-----SS-------->|SYNC DRIFn (slave) | +| |-----SD0------->|D0 | +| |-----SD1------->|D1 | ++---------------------+ +---------------------+ + +As per datasheet, each DRIF channel (drifn) is made up of two internal +channels (drifn0 & drifn1). These two internal channels share the common +CLK & SYNC. Each internal channel has its own dedicated resources like +irq, dma channels, address space & clock. This internal split is not +visible to the external master device. + +The device tree model represents each internal channel as a separate node. +The internal channels sharing the CLK & SYNC are tied together by their +phandles using a property called "renesas,bonding". For the rest of +the documentation, unless explicitly stated, the word channel implies an +internal channel. + +When both internal channels are enabled they need to be managed together +as one (i.e.) they cannot operate alone as independent devices. Out of the +two, one of them needs to act as a primary device that accepts common +properties of both the internal channels. This channel is identified by a +property called "renesas,primary-bond". + +To summarize, + - When both the internal channels that are bonded together are enabled, + the zeroth channel is selected as primary-bond. This channels accepts + properties common to all the members of the bond. + - When only one of the bonded channels need to be enabled, the property + "renesas,bonding" or "renesas,primary-bond" will have no effect. That + enabled channel can act alone as any other independent device. + +Required properties of an internal channel: +------------------------------------------- +- compatible: "renesas,r8a7795-drif" if DRIF controller is a part of R8A7795 SoC. + "renesas,rcar-gen3-drif" for a generic R-Car Gen3 compatible device. + + When compatible with the generic version, nodes must list the + SoC-specific version corresponding to the platform first + followed by the generic version. + +- reg: offset and length of that channel. +- interrupts: associated with that channel. +- clocks: phandle and clock specifier of that channel. +- clock-names: clock input name string: "fck". +- dmas: phandles to the DMA channels. +- dma-names: names of the DMA channel: "rx". +- renesas,bonding: phandle to the other channel. + +Optional properties of an internal channel: +------------------------------------------- +- power-domains: phandle to the respective power domain. + +Required properties of an internal channel when: + - It is the only enabled channel of the bond (or) + - If it acts as primary among enabled bonds +-------------------------------------------------------- +- pinctrl-0: pin control group to be used for this channel. +- pinctrl-names: must be "default". +- renesas,primary-bond: empty property indicating the channel acts as primary + among the bonded channels. +- port: child port node corresponding to the data input, in accordance with + the video interface bindings defined in + Documentation/devicetree/bindings/media/video-interfaces.txt. The port + node must contain at least one endpoint. + +Optional endpoint property: +--------------------------- +- sync-active: Indicates sync signal polarity, 0/1 for low/high respectively. + This property maps to SYNCAC bit in the hardware manual. The + default is 1 (active high). + +Example: +-------- + +(1) Both internal channels enabled: +----------------------------------- + +When interfacing with a third party tuner device with two data pins as shown +below. + ++---------------------+ +---------------------+ +| |-----SCK------->|CLK | +| Master |-----SS-------->|SYNC DRIFn (slave) | +| |-----SD0------->|D0 | +| |-----SD1------->|D1 | ++---------------------+ +---------------------+ + + drif00: rif@e6f40000 { + compatible = "renesas,r8a7795-drif", + "renesas,rcar-gen3-drif"; + reg = <0 0xe6f40000 0 0x64>; + interrupts = ; + clocks = <&cpg CPG_MOD 515>; + clock-names = "fck"; + dmas = <&dmac1 0x20>, <&dmac2 0x20>; + dma-names = "rx", "rx"; + power-domains = <&sysc R8A7795_PD_ALWAYS_ON>; + renesas,bonding = <&drif01>; + renesas,primary-bond; + pinctrl-0 = <&drif0_pins>; + pinctrl-names = "default"; + port { + drif0_ep: endpoint { + remote-endpoint = <&tuner_ep>; + }; + }; + }; + + drif01: rif@e6f50000 { + compatible = "renesas,r8a7795-drif", + "renesas,rcar-gen3-drif"; + reg = <0 0xe6f50000 0 0x64>; + interrupts = ; + clocks = <&cpg CPG_MOD 514>; + clock-names = "fck"; + dmas = <&dmac1 0x22>, <&dmac2 0x22>; + dma-names = "rx", "rx"; + power-domains = <&sysc R8A7795_PD_ALWAYS_ON>; + renesas,bonding = <&drif00>; + }; + + +(2) Internal channel 1 alone is enabled: +---------------------------------------- + +When interfacing with a third party tuner device with one data pin as shown +below. + ++---------------------+ +---------------------+ +| |-----SCK------->|CLK | +| Master |-----SS-------->|SYNC DRIFn (slave) | +| | |D0 (unused) | +| |-----SD-------->|D1 | ++---------------------+ +---------------------+ + + drif00: rif@e6f40000 { + compatible = "renesas,r8a7795-drif", + "renesas,rcar-gen3-drif"; + reg = <0 0xe6f40000 0 0x64>; + interrupts = ; + clocks = <&cpg CPG_MOD 515>; + clock-names = "fck"; + dmas = <&dmac1 0x20>, <&dmac2 0x20>; + dma-names = "rx", "rx"; + power-domains = <&sysc R8A7795_PD_ALWAYS_ON>; + renesas,bonding = <&drif01>; + }; + + drif01: rif@e6f50000 { + compatible = "renesas,r8a7795-drif", + "renesas,rcar-gen3-drif"; + reg = <0 0xe6f50000 0 0x64>; + interrupts = ; + clocks = <&cpg CPG_MOD 514>; + clock-names = "fck"; + dmas = <&dmac1 0x22>, <&dmac2 0x22>; + dma-names = "rx", "rx"; + power-domains = <&sysc R8A7795_PD_ALWAYS_ON>; + renesas,bonding = <&drif00>; + pinctrl-0 = <&drif0_pins>; + pinctrl-names = "default"; + port { + drif0_ep: endpoint { + remote-endpoint = <&tuner_ep>; + sync-active = <0>; + }; + }; + }; -- cgit v1.2.3-58-ga151 From 7625ee981af166ddb569e2e6c0006e2af471326f Mon Sep 17 00:00:00 2001 From: Ramesh Shanmugasundaram Date: Mon, 12 Jun 2017 10:26:19 -0300 Subject: [media] media: platform: rcar_drif: Add DRIF support This patch adds Digital Radio Interface (DRIF) support to R-Car Gen3 SoCs. The driver exposes each instance of DRIF as a V4L2 SDR device. A DRIF device represents a channel and each channel can have one or two sub-channels respectively depending on the target board. DRIF supports only Rx functionality. It receives samples from a RF frontend tuner chip it is interfaced with. The combination of DRIF and the tuner device, which is registered as a sub-device, determines the receive sample rate and format. In order to be compliant as a V4L2 SDR device, DRIF needs to bind with the tuner device, which can be provided by a third party vendor. DRIF acts as a slave device and the tuner device acts as a master transmitting the samples. The driver allows asynchronous binding of a tuner device that is registered as a v4l2 sub-device. The driver can learn about the tuner it is interfaced with based on port endpoint properties of the device in device tree. The V4L2 SDR device inherits the controls exposed by the tuner device. The device can also be configured to use either one or both of the data pins at runtime based on the master (tuner) configuration. Signed-off-by: Ramesh Shanmugasundaram Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/Kconfig | 25 + drivers/media/platform/Makefile | 1 + drivers/media/platform/rcar_drif.c | 1498 ++++++++++++++++++++++++++++++++++++ 3 files changed, 1524 insertions(+) create mode 100644 drivers/media/platform/rcar_drif.c diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index 8da521a8ead7..54b0f756be30 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -550,3 +550,28 @@ config VIDEO_STM32_HDMI_CEC between compatible devices. endif #CEC_PLATFORM_DRIVERS + +menuconfig SDR_PLATFORM_DRIVERS + bool "SDR platform devices" + depends on MEDIA_SDR_SUPPORT + default n + ---help--- + Say Y here to enable support for platform-specific SDR Drivers. + +if SDR_PLATFORM_DRIVERS + +config VIDEO_RCAR_DRIF + tristate "Renesas Digitial Radio Interface (DRIF)" + depends on VIDEO_V4L2 && HAS_DMA + depends on ARCH_RENESAS || COMPILE_TEST + select VIDEOBUF2_VMALLOC + ---help--- + Say Y if you want to enable R-Car Gen3 DRIF support. DRIF is Digital + Radio Interface that interfaces with an RF front end chip. It is a + receiver of digital data which uses DMA to transfer received data to + a configured location for an application to use. + + To compile this driver as a module, choose M here; the module + will be called rcar_drif. + +endif # SDR_PLATFORM_DRIVERS diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile index 6bbdf942e8c8..349ccffb49db 100644 --- a/drivers/media/platform/Makefile +++ b/drivers/media/platform/Makefile @@ -54,6 +54,7 @@ obj-$(CONFIG_VIDEO_SH_VOU) += sh_vou.o obj-$(CONFIG_SOC_CAMERA) += soc_camera/ +obj-$(CONFIG_VIDEO_RCAR_DRIF) += rcar_drif.o obj-$(CONFIG_VIDEO_RENESAS_FCP) += rcar-fcp.o obj-$(CONFIG_VIDEO_RENESAS_FDP1) += rcar_fdp1.o obj-$(CONFIG_VIDEO_RENESAS_JPU) += rcar_jpu.o diff --git a/drivers/media/platform/rcar_drif.c b/drivers/media/platform/rcar_drif.c new file mode 100644 index 000000000000..522364ff0d5d --- /dev/null +++ b/drivers/media/platform/rcar_drif.c @@ -0,0 +1,1498 @@ +/* + * R-Car Gen3 Digital Radio Interface (DRIF) driver + * + * Copyright (C) 2017 Renesas Electronics Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/* + * The R-Car DRIF is a receive only MSIOF like controller with an + * external master device driving the SCK. It receives data into a FIFO, + * then this driver uses the SYS-DMAC engine to move the data from + * the device to memory. + * + * Each DRIF channel DRIFx (as per datasheet) contains two internal + * channels DRIFx0 & DRIFx1 within itself with each having its own resources + * like module clk, register set, irq and dma. These internal channels share + * common CLK & SYNC from master. The two data pins D0 & D1 shall be + * considered to represent the two internal channels. This internal split + * is not visible to the master device. + * + * Depending on the master device, a DRIF channel can use + * (1) both internal channels (D0 & D1) to receive data in parallel (or) + * (2) one internal channel (D0 or D1) to receive data + * + * The primary design goal of this controller is to act as a Digital Radio + * Interface that receives digital samples from a tuner device. Hence the + * driver exposes the device as a V4L2 SDR device. In order to qualify as + * a V4L2 SDR device, it should possess a tuner interface as mandated by the + * framework. This driver expects a tuner driver (sub-device) to bind + * asynchronously with this device and the combined drivers shall expose + * a V4L2 compliant SDR device. The DRIF driver is independent of the + * tuner vendor. + * + * The DRIF h/w can support I2S mode and Frame start synchronization pulse mode. + * This driver is tested for I2S mode only because of the availability of + * suitable master devices. Hence, not all configurable options of DRIF h/w + * like lsb/msb first, syncdl, dtdl etc. are exposed via DT and I2S defaults + * are used. These can be exposed later if needed after testing. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* DRIF register offsets */ +#define RCAR_DRIF_SITMDR1 0x00 +#define RCAR_DRIF_SITMDR2 0x04 +#define RCAR_DRIF_SITMDR3 0x08 +#define RCAR_DRIF_SIRMDR1 0x10 +#define RCAR_DRIF_SIRMDR2 0x14 +#define RCAR_DRIF_SIRMDR3 0x18 +#define RCAR_DRIF_SICTR 0x28 +#define RCAR_DRIF_SIFCTR 0x30 +#define RCAR_DRIF_SISTR 0x40 +#define RCAR_DRIF_SIIER 0x44 +#define RCAR_DRIF_SIRFDR 0x60 + +#define RCAR_DRIF_RFOVF BIT(3) /* Receive FIFO overflow */ +#define RCAR_DRIF_RFUDF BIT(4) /* Receive FIFO underflow */ +#define RCAR_DRIF_RFSERR BIT(5) /* Receive frame sync error */ +#define RCAR_DRIF_REOF BIT(7) /* Frame reception end */ +#define RCAR_DRIF_RDREQ BIT(12) /* Receive data xfer req */ +#define RCAR_DRIF_RFFUL BIT(13) /* Receive FIFO full */ + +/* SIRMDR1 */ +#define RCAR_DRIF_SIRMDR1_SYNCMD_FRAME (0 << 28) +#define RCAR_DRIF_SIRMDR1_SYNCMD_LR (3 << 28) + +#define RCAR_DRIF_SIRMDR1_SYNCAC_POL_HIGH (0 << 25) +#define RCAR_DRIF_SIRMDR1_SYNCAC_POL_LOW (1 << 25) + +#define RCAR_DRIF_SIRMDR1_MSB_FIRST (0 << 24) +#define RCAR_DRIF_SIRMDR1_LSB_FIRST (1 << 24) + +#define RCAR_DRIF_SIRMDR1_DTDL_0 (0 << 20) +#define RCAR_DRIF_SIRMDR1_DTDL_1 (1 << 20) +#define RCAR_DRIF_SIRMDR1_DTDL_2 (2 << 20) +#define RCAR_DRIF_SIRMDR1_DTDL_0PT5 (5 << 20) +#define RCAR_DRIF_SIRMDR1_DTDL_1PT5 (6 << 20) + +#define RCAR_DRIF_SIRMDR1_SYNCDL_0 (0 << 20) +#define RCAR_DRIF_SIRMDR1_SYNCDL_1 (1 << 20) +#define RCAR_DRIF_SIRMDR1_SYNCDL_2 (2 << 20) +#define RCAR_DRIF_SIRMDR1_SYNCDL_3 (3 << 20) +#define RCAR_DRIF_SIRMDR1_SYNCDL_0PT5 (5 << 20) +#define RCAR_DRIF_SIRMDR1_SYNCDL_1PT5 (6 << 20) + +#define RCAR_DRIF_MDR_GRPCNT(n) (((n) - 1) << 30) +#define RCAR_DRIF_MDR_BITLEN(n) (((n) - 1) << 24) +#define RCAR_DRIF_MDR_WDCNT(n) (((n) - 1) << 16) + +/* Hidden Transmit register that controls CLK & SYNC */ +#define RCAR_DRIF_SITMDR1_PCON BIT(30) + +#define RCAR_DRIF_SICTR_RX_RISING_EDGE BIT(26) +#define RCAR_DRIF_SICTR_RX_EN BIT(8) +#define RCAR_DRIF_SICTR_RESET BIT(0) + +/* Constants */ +#define RCAR_DRIF_NUM_HWBUFS 32 +#define RCAR_DRIF_MAX_DEVS 4 +#define RCAR_DRIF_DEFAULT_NUM_HWBUFS 16 +#define RCAR_DRIF_DEFAULT_HWBUF_SIZE (4 * PAGE_SIZE) +#define RCAR_DRIF_MAX_CHANNEL 2 +#define RCAR_SDR_BUFFER_SIZE SZ_64K + +/* Internal buffer status flags */ +#define RCAR_DRIF_BUF_DONE BIT(0) /* DMA completed */ +#define RCAR_DRIF_BUF_OVERFLOW BIT(1) /* Overflow detected */ + +#define to_rcar_drif_buf_pair(sdr, ch_num, idx) \ + (&((sdr)->ch[!(ch_num)]->buf[(idx)])) + +#define for_each_rcar_drif_channel(ch, ch_mask) \ + for_each_set_bit(ch, ch_mask, RCAR_DRIF_MAX_CHANNEL) + +/* Debug */ +#define rdrif_dbg(sdr, fmt, arg...) \ + dev_dbg(sdr->v4l2_dev.dev, fmt, ## arg) + +#define rdrif_err(sdr, fmt, arg...) \ + dev_err(sdr->v4l2_dev.dev, fmt, ## arg) + +/* Stream formats */ +struct rcar_drif_format { + u32 pixelformat; + u32 buffersize; + u32 bitlen; + u32 wdcnt; + u32 num_ch; +}; + +/* Format descriptions for capture */ +static const struct rcar_drif_format formats[] = { + { + .pixelformat = V4L2_SDR_FMT_PCU16BE, + .buffersize = RCAR_SDR_BUFFER_SIZE, + .bitlen = 16, + .wdcnt = 1, + .num_ch = 2, + }, + { + .pixelformat = V4L2_SDR_FMT_PCU18BE, + .buffersize = RCAR_SDR_BUFFER_SIZE, + .bitlen = 18, + .wdcnt = 1, + .num_ch = 2, + }, + { + .pixelformat = V4L2_SDR_FMT_PCU20BE, + .buffersize = RCAR_SDR_BUFFER_SIZE, + .bitlen = 20, + .wdcnt = 1, + .num_ch = 2, + }, +}; + +/* Buffer for a received frame from one or both internal channels */ +struct rcar_drif_frame_buf { + /* Common v4l buffer stuff -- must be first */ + struct vb2_v4l2_buffer vb; + struct list_head list; +}; + +/* OF graph endpoint's V4L2 async data */ +struct rcar_drif_graph_ep { + struct v4l2_subdev *subdev; /* Async matched subdev */ + struct v4l2_async_subdev asd; /* Async sub-device descriptor */ +}; + +/* DMA buffer */ +struct rcar_drif_hwbuf { + void *addr; /* CPU-side address */ + unsigned int status; /* Buffer status flags */ +}; + +/* Internal channel */ +struct rcar_drif { + struct rcar_drif_sdr *sdr; /* Group device */ + struct platform_device *pdev; /* Channel's pdev */ + void __iomem *base; /* Base register address */ + resource_size_t start; /* I/O resource offset */ + struct dma_chan *dmach; /* Reserved DMA channel */ + struct clk *clk; /* Module clock */ + struct rcar_drif_hwbuf buf[RCAR_DRIF_NUM_HWBUFS]; /* H/W bufs */ + dma_addr_t dma_handle; /* Handle for all bufs */ + unsigned int num; /* Channel number */ + bool acting_sdr; /* Channel acting as SDR device */ +}; + +/* DRIF V4L2 SDR */ +struct rcar_drif_sdr { + struct device *dev; /* Platform device */ + struct video_device *vdev; /* V4L2 SDR device */ + struct v4l2_device v4l2_dev; /* V4L2 device */ + + /* Videobuf2 queue and queued buffers list */ + struct vb2_queue vb_queue; + struct list_head queued_bufs; + spinlock_t queued_bufs_lock; /* Protects queued_bufs */ + spinlock_t dma_lock; /* To serialize DMA cb of channels */ + + struct mutex v4l2_mutex; /* To serialize ioctls */ + struct mutex vb_queue_mutex; /* To serialize streaming ioctls */ + struct v4l2_ctrl_handler ctrl_hdl; /* SDR control handler */ + struct v4l2_async_notifier notifier; /* For subdev (tuner) */ + struct rcar_drif_graph_ep ep; /* Endpoint V4L2 async data */ + + /* Current V4L2 SDR format ptr */ + const struct rcar_drif_format *fmt; + + /* Device tree SYNC properties */ + u32 mdr1; + + /* Internals */ + struct rcar_drif *ch[RCAR_DRIF_MAX_CHANNEL]; /* DRIFx0,1 */ + unsigned long hw_ch_mask; /* Enabled channels per DT */ + unsigned long cur_ch_mask; /* Used channels for an SDR FMT */ + u32 num_hw_ch; /* Num of DT enabled channels */ + u32 num_cur_ch; /* Num of used channels */ + u32 hwbuf_size; /* Each DMA buffer size */ + u32 produced; /* Buffers produced by sdr dev */ +}; + +/* Register access functions */ +static void rcar_drif_write(struct rcar_drif *ch, u32 offset, u32 data) +{ + writel(data, ch->base + offset); +} + +static u32 rcar_drif_read(struct rcar_drif *ch, u32 offset) +{ + return readl(ch->base + offset); +} + +/* Release DMA channels */ +static void rcar_drif_release_dmachannels(struct rcar_drif_sdr *sdr) +{ + unsigned int i; + + for_each_rcar_drif_channel(i, &sdr->cur_ch_mask) + if (sdr->ch[i]->dmach) { + dma_release_channel(sdr->ch[i]->dmach); + sdr->ch[i]->dmach = NULL; + } +} + +/* Allocate DMA channels */ +static int rcar_drif_alloc_dmachannels(struct rcar_drif_sdr *sdr) +{ + struct dma_slave_config dma_cfg; + unsigned int i; + int ret = -ENODEV; + + for_each_rcar_drif_channel(i, &sdr->cur_ch_mask) { + struct rcar_drif *ch = sdr->ch[i]; + + ch->dmach = dma_request_slave_channel(&ch->pdev->dev, "rx"); + if (!ch->dmach) { + rdrif_err(sdr, "ch%u: dma channel req failed\n", i); + goto dmach_error; + } + + /* Configure slave */ + memset(&dma_cfg, 0, sizeof(dma_cfg)); + dma_cfg.src_addr = (phys_addr_t)(ch->start + RCAR_DRIF_SIRFDR); + dma_cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + ret = dmaengine_slave_config(ch->dmach, &dma_cfg); + if (ret) { + rdrif_err(sdr, "ch%u: dma slave config failed\n", i); + goto dmach_error; + } + } + return 0; + +dmach_error: + rcar_drif_release_dmachannels(sdr); + return ret; +} + +/* Release queued vb2 buffers */ +static void rcar_drif_release_queued_bufs(struct rcar_drif_sdr *sdr, + enum vb2_buffer_state state) +{ + struct rcar_drif_frame_buf *fbuf, *tmp; + unsigned long flags; + + spin_lock_irqsave(&sdr->queued_bufs_lock, flags); + list_for_each_entry_safe(fbuf, tmp, &sdr->queued_bufs, list) { + list_del(&fbuf->list); + vb2_buffer_done(&fbuf->vb.vb2_buf, state); + } + spin_unlock_irqrestore(&sdr->queued_bufs_lock, flags); +} + +/* Set MDR defaults */ +static inline void rcar_drif_set_mdr1(struct rcar_drif_sdr *sdr) +{ + unsigned int i; + + /* Set defaults for enabled internal channels */ + for_each_rcar_drif_channel(i, &sdr->cur_ch_mask) { + /* Refer MSIOF section in manual for this register setting */ + rcar_drif_write(sdr->ch[i], RCAR_DRIF_SITMDR1, + RCAR_DRIF_SITMDR1_PCON); + + /* Setup MDR1 value */ + rcar_drif_write(sdr->ch[i], RCAR_DRIF_SIRMDR1, sdr->mdr1); + + rdrif_dbg(sdr, "ch%u: mdr1 = 0x%08x", + i, rcar_drif_read(sdr->ch[i], RCAR_DRIF_SIRMDR1)); + } +} + +/* Set DRIF receive format */ +static int rcar_drif_set_format(struct rcar_drif_sdr *sdr) +{ + unsigned int i; + + rdrif_dbg(sdr, "setfmt: bitlen %u wdcnt %u num_ch %u\n", + sdr->fmt->bitlen, sdr->fmt->wdcnt, sdr->fmt->num_ch); + + /* Sanity check */ + if (sdr->fmt->num_ch > sdr->num_cur_ch) { + rdrif_err(sdr, "fmt num_ch %u cur_ch %u mismatch\n", + sdr->fmt->num_ch, sdr->num_cur_ch); + return -EINVAL; + } + + /* Setup group, bitlen & wdcnt */ + for_each_rcar_drif_channel(i, &sdr->cur_ch_mask) { + u32 mdr; + + /* Two groups */ + mdr = RCAR_DRIF_MDR_GRPCNT(2) | + RCAR_DRIF_MDR_BITLEN(sdr->fmt->bitlen) | + RCAR_DRIF_MDR_WDCNT(sdr->fmt->wdcnt); + rcar_drif_write(sdr->ch[i], RCAR_DRIF_SIRMDR2, mdr); + + mdr = RCAR_DRIF_MDR_BITLEN(sdr->fmt->bitlen) | + RCAR_DRIF_MDR_WDCNT(sdr->fmt->wdcnt); + rcar_drif_write(sdr->ch[i], RCAR_DRIF_SIRMDR3, mdr); + + rdrif_dbg(sdr, "ch%u: new mdr[2,3] = 0x%08x, 0x%08x\n", + i, rcar_drif_read(sdr->ch[i], RCAR_DRIF_SIRMDR2), + rcar_drif_read(sdr->ch[i], RCAR_DRIF_SIRMDR3)); + } + return 0; +} + +/* Release DMA buffers */ +static void rcar_drif_release_buf(struct rcar_drif_sdr *sdr) +{ + unsigned int i; + + for_each_rcar_drif_channel(i, &sdr->cur_ch_mask) { + struct rcar_drif *ch = sdr->ch[i]; + + /* First entry contains the dma buf ptr */ + if (ch->buf[0].addr) { + dma_free_coherent(&ch->pdev->dev, + sdr->hwbuf_size * RCAR_DRIF_NUM_HWBUFS, + ch->buf[0].addr, ch->dma_handle); + ch->buf[0].addr = NULL; + } + } +} + +/* Request DMA buffers */ +static int rcar_drif_request_buf(struct rcar_drif_sdr *sdr) +{ + int ret = -ENOMEM; + unsigned int i, j; + void *addr; + + for_each_rcar_drif_channel(i, &sdr->cur_ch_mask) { + struct rcar_drif *ch = sdr->ch[i]; + + /* Allocate DMA buffers */ + addr = dma_alloc_coherent(&ch->pdev->dev, + sdr->hwbuf_size * RCAR_DRIF_NUM_HWBUFS, + &ch->dma_handle, GFP_KERNEL); + if (!addr) { + rdrif_err(sdr, + "ch%u: dma alloc failed. num hwbufs %u size %u\n", + i, RCAR_DRIF_NUM_HWBUFS, sdr->hwbuf_size); + goto error; + } + + /* Split the chunk and populate bufctxt */ + for (j = 0; j < RCAR_DRIF_NUM_HWBUFS; j++) { + ch->buf[j].addr = addr + (j * sdr->hwbuf_size); + ch->buf[j].status = 0; + } + } + return 0; +error: + return ret; +} + +/* Setup vb_queue minimum buffer requirements */ +static int rcar_drif_queue_setup(struct vb2_queue *vq, + unsigned int *num_buffers, unsigned int *num_planes, + unsigned int sizes[], struct device *alloc_devs[]) +{ + struct rcar_drif_sdr *sdr = vb2_get_drv_priv(vq); + + /* Need at least 16 buffers */ + if (vq->num_buffers + *num_buffers < 16) + *num_buffers = 16 - vq->num_buffers; + + *num_planes = 1; + sizes[0] = PAGE_ALIGN(sdr->fmt->buffersize); + rdrif_dbg(sdr, "num_bufs %d sizes[0] %d\n", *num_buffers, sizes[0]); + + return 0; +} + +/* Enqueue buffer */ +static void rcar_drif_buf_queue(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct rcar_drif_sdr *sdr = vb2_get_drv_priv(vb->vb2_queue); + struct rcar_drif_frame_buf *fbuf = + container_of(vbuf, struct rcar_drif_frame_buf, vb); + unsigned long flags; + + rdrif_dbg(sdr, "buf_queue idx %u\n", vb->index); + spin_lock_irqsave(&sdr->queued_bufs_lock, flags); + list_add_tail(&fbuf->list, &sdr->queued_bufs); + spin_unlock_irqrestore(&sdr->queued_bufs_lock, flags); +} + +/* Get a frame buf from list */ +static struct rcar_drif_frame_buf * +rcar_drif_get_fbuf(struct rcar_drif_sdr *sdr) +{ + struct rcar_drif_frame_buf *fbuf; + unsigned long flags; + + spin_lock_irqsave(&sdr->queued_bufs_lock, flags); + fbuf = list_first_entry_or_null(&sdr->queued_bufs, struct + rcar_drif_frame_buf, list); + if (!fbuf) { + /* + * App is late in enqueing buffers. Samples lost & there will + * be a gap in sequence number when app recovers + */ + rdrif_dbg(sdr, "\napp late: prod %u\n", sdr->produced); + spin_unlock_irqrestore(&sdr->queued_bufs_lock, flags); + return NULL; + } + list_del(&fbuf->list); + spin_unlock_irqrestore(&sdr->queued_bufs_lock, flags); + + return fbuf; +} + +/* Helpers to set/clear buf pair status */ +static inline bool rcar_drif_bufs_done(struct rcar_drif_hwbuf **buf) +{ + return (buf[0]->status & buf[1]->status & RCAR_DRIF_BUF_DONE); +} + +static inline bool rcar_drif_bufs_overflow(struct rcar_drif_hwbuf **buf) +{ + return ((buf[0]->status | buf[1]->status) & RCAR_DRIF_BUF_OVERFLOW); +} + +static inline void rcar_drif_bufs_clear(struct rcar_drif_hwbuf **buf, + unsigned int bit) +{ + unsigned int i; + + for (i = 0; i < RCAR_DRIF_MAX_CHANNEL; i++) + buf[i]->status &= ~bit; +} + +/* Channel DMA complete */ +static void rcar_drif_channel_complete(struct rcar_drif *ch, u32 idx) +{ + u32 str; + + ch->buf[idx].status |= RCAR_DRIF_BUF_DONE; + + /* Check for DRIF errors */ + str = rcar_drif_read(ch, RCAR_DRIF_SISTR); + if (unlikely(str & RCAR_DRIF_RFOVF)) { + /* Writing the same clears it */ + rcar_drif_write(ch, RCAR_DRIF_SISTR, str); + + /* Overflow: some samples are lost */ + ch->buf[idx].status |= RCAR_DRIF_BUF_OVERFLOW; + } +} + +/* DMA callback for each stage */ +static void rcar_drif_dma_complete(void *dma_async_param) +{ + struct rcar_drif *ch = dma_async_param; + struct rcar_drif_sdr *sdr = ch->sdr; + struct rcar_drif_hwbuf *buf[RCAR_DRIF_MAX_CHANNEL]; + struct rcar_drif_frame_buf *fbuf; + bool overflow = false; + u32 idx, produced; + unsigned int i; + + spin_lock(&sdr->dma_lock); + + /* DMA can be terminated while the callback was waiting on lock */ + if (!vb2_is_streaming(&sdr->vb_queue)) { + spin_unlock(&sdr->dma_lock); + return; + } + + idx = sdr->produced % RCAR_DRIF_NUM_HWBUFS; + rcar_drif_channel_complete(ch, idx); + + if (sdr->num_cur_ch == RCAR_DRIF_MAX_CHANNEL) { + buf[0] = ch->num ? to_rcar_drif_buf_pair(sdr, ch->num, idx) : + &ch->buf[idx]; + buf[1] = ch->num ? &ch->buf[idx] : + to_rcar_drif_buf_pair(sdr, ch->num, idx); + + /* Check if both DMA buffers are done */ + if (!rcar_drif_bufs_done(buf)) { + spin_unlock(&sdr->dma_lock); + return; + } + + /* Clear buf done status */ + rcar_drif_bufs_clear(buf, RCAR_DRIF_BUF_DONE); + + if (rcar_drif_bufs_overflow(buf)) { + overflow = true; + /* Clear the flag in status */ + rcar_drif_bufs_clear(buf, RCAR_DRIF_BUF_OVERFLOW); + } + } else { + buf[0] = &ch->buf[idx]; + if (buf[0]->status & RCAR_DRIF_BUF_OVERFLOW) { + overflow = true; + /* Clear the flag in status */ + buf[0]->status &= ~RCAR_DRIF_BUF_OVERFLOW; + } + } + + /* Buffer produced for consumption */ + produced = sdr->produced++; + spin_unlock(&sdr->dma_lock); + + rdrif_dbg(sdr, "ch%u: prod %u\n", ch->num, produced); + + /* Get fbuf */ + fbuf = rcar_drif_get_fbuf(sdr); + if (!fbuf) + return; + + for (i = 0; i < RCAR_DRIF_MAX_CHANNEL; i++) + memcpy(vb2_plane_vaddr(&fbuf->vb.vb2_buf, 0) + + i * sdr->hwbuf_size, buf[i]->addr, sdr->hwbuf_size); + + fbuf->vb.field = V4L2_FIELD_NONE; + fbuf->vb.sequence = produced; + fbuf->vb.vb2_buf.timestamp = ktime_get_ns(); + vb2_set_plane_payload(&fbuf->vb.vb2_buf, 0, sdr->fmt->buffersize); + + /* Set error state on overflow */ + vb2_buffer_done(&fbuf->vb.vb2_buf, + overflow ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE); +} + +static int rcar_drif_qbuf(struct rcar_drif *ch) +{ + struct rcar_drif_sdr *sdr = ch->sdr; + dma_addr_t addr = ch->dma_handle; + struct dma_async_tx_descriptor *rxd; + dma_cookie_t cookie; + int ret = -EIO; + + /* Setup cyclic DMA with given buffers */ + rxd = dmaengine_prep_dma_cyclic(ch->dmach, addr, + sdr->hwbuf_size * RCAR_DRIF_NUM_HWBUFS, + sdr->hwbuf_size, DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!rxd) { + rdrif_err(sdr, "ch%u: prep dma cyclic failed\n", ch->num); + return ret; + } + + /* Submit descriptor */ + rxd->callback = rcar_drif_dma_complete; + rxd->callback_param = ch; + cookie = dmaengine_submit(rxd); + if (dma_submit_error(cookie)) { + rdrif_err(sdr, "ch%u: dma submit failed\n", ch->num); + return ret; + } + + dma_async_issue_pending(ch->dmach); + return 0; +} + +/* Enable reception */ +static int rcar_drif_enable_rx(struct rcar_drif_sdr *sdr) +{ + unsigned int i; + u32 ctr; + int ret; + + /* + * When both internal channels are enabled, they can be synchronized + * only by the master + */ + + /* Enable receive */ + for_each_rcar_drif_channel(i, &sdr->cur_ch_mask) { + ctr = rcar_drif_read(sdr->ch[i], RCAR_DRIF_SICTR); + ctr |= (RCAR_DRIF_SICTR_RX_RISING_EDGE | + RCAR_DRIF_SICTR_RX_EN); + rcar_drif_write(sdr->ch[i], RCAR_DRIF_SICTR, ctr); + } + + /* Check receive enabled */ + for_each_rcar_drif_channel(i, &sdr->cur_ch_mask) { + ret = readl_poll_timeout(sdr->ch[i]->base + RCAR_DRIF_SICTR, + ctr, ctr & RCAR_DRIF_SICTR_RX_EN, 7, 100000); + if (ret) { + rdrif_err(sdr, "ch%u: rx en failed. ctr 0x%08x\n", i, + rcar_drif_read(sdr->ch[i], RCAR_DRIF_SICTR)); + break; + } + } + return ret; +} + +/* Disable reception */ +static void rcar_drif_disable_rx(struct rcar_drif_sdr *sdr) +{ + unsigned int i; + u32 ctr; + int ret; + + /* Disable receive */ + for_each_rcar_drif_channel(i, &sdr->cur_ch_mask) { + ctr = rcar_drif_read(sdr->ch[i], RCAR_DRIF_SICTR); + ctr &= ~RCAR_DRIF_SICTR_RX_EN; + rcar_drif_write(sdr->ch[i], RCAR_DRIF_SICTR, ctr); + } + + /* Check receive disabled */ + for_each_rcar_drif_channel(i, &sdr->cur_ch_mask) { + ret = readl_poll_timeout(sdr->ch[i]->base + RCAR_DRIF_SICTR, + ctr, !(ctr & RCAR_DRIF_SICTR_RX_EN), 7, 100000); + if (ret) + dev_warn(&sdr->vdev->dev, + "ch%u: failed to disable rx. ctr 0x%08x\n", + i, rcar_drif_read(sdr->ch[i], RCAR_DRIF_SICTR)); + } +} + +/* Stop channel */ +static void rcar_drif_stop_channel(struct rcar_drif *ch) +{ + /* Disable DMA receive interrupt */ + rcar_drif_write(ch, RCAR_DRIF_SIIER, 0x00000000); + + /* Terminate all DMA transfers */ + dmaengine_terminate_sync(ch->dmach); +} + +/* Stop receive operation */ +static void rcar_drif_stop(struct rcar_drif_sdr *sdr) +{ + unsigned int i; + + /* Disable Rx */ + rcar_drif_disable_rx(sdr); + + for_each_rcar_drif_channel(i, &sdr->cur_ch_mask) + rcar_drif_stop_channel(sdr->ch[i]); +} + +/* Start channel */ +static int rcar_drif_start_channel(struct rcar_drif *ch) +{ + struct rcar_drif_sdr *sdr = ch->sdr; + u32 ctr, str; + int ret; + + /* Reset receive */ + rcar_drif_write(ch, RCAR_DRIF_SICTR, RCAR_DRIF_SICTR_RESET); + ret = readl_poll_timeout(ch->base + RCAR_DRIF_SICTR, ctr, + !(ctr & RCAR_DRIF_SICTR_RESET), 7, 100000); + if (ret) { + rdrif_err(sdr, "ch%u: failed to reset rx. ctr 0x%08x\n", + ch->num, rcar_drif_read(ch, RCAR_DRIF_SICTR)); + return ret; + } + + /* Queue buffers for DMA */ + ret = rcar_drif_qbuf(ch); + if (ret) + return ret; + + /* Clear status register flags */ + str = RCAR_DRIF_RFFUL | RCAR_DRIF_REOF | RCAR_DRIF_RFSERR | + RCAR_DRIF_RFUDF | RCAR_DRIF_RFOVF; + rcar_drif_write(ch, RCAR_DRIF_SISTR, str); + + /* Enable DMA receive interrupt */ + rcar_drif_write(ch, RCAR_DRIF_SIIER, 0x00009000); + + return ret; +} + +/* Start receive operation */ +static int rcar_drif_start(struct rcar_drif_sdr *sdr) +{ + unsigned long enabled = 0; + unsigned int i; + int ret; + + for_each_rcar_drif_channel(i, &sdr->cur_ch_mask) { + ret = rcar_drif_start_channel(sdr->ch[i]); + if (ret) + goto start_error; + enabled |= BIT(i); + } + + ret = rcar_drif_enable_rx(sdr); + if (ret) + goto enable_error; + + sdr->produced = 0; + return ret; + +enable_error: + rcar_drif_disable_rx(sdr); +start_error: + for_each_rcar_drif_channel(i, &enabled) + rcar_drif_stop_channel(sdr->ch[i]); + + return ret; +} + +/* Start streaming */ +static int rcar_drif_start_streaming(struct vb2_queue *vq, unsigned int count) +{ + struct rcar_drif_sdr *sdr = vb2_get_drv_priv(vq); + unsigned long enabled = 0; + unsigned int i; + int ret; + + mutex_lock(&sdr->v4l2_mutex); + + for_each_rcar_drif_channel(i, &sdr->cur_ch_mask) { + ret = clk_prepare_enable(sdr->ch[i]->clk); + if (ret) + goto error; + enabled |= BIT(i); + } + + /* Set default MDRx settings */ + rcar_drif_set_mdr1(sdr); + + /* Set new format */ + ret = rcar_drif_set_format(sdr); + if (ret) + goto error; + + if (sdr->num_cur_ch == RCAR_DRIF_MAX_CHANNEL) + sdr->hwbuf_size = sdr->fmt->buffersize / RCAR_DRIF_MAX_CHANNEL; + else + sdr->hwbuf_size = sdr->fmt->buffersize; + + rdrif_dbg(sdr, "num hwbufs %u, hwbuf_size %u\n", + RCAR_DRIF_NUM_HWBUFS, sdr->hwbuf_size); + + /* Alloc DMA channel */ + ret = rcar_drif_alloc_dmachannels(sdr); + if (ret) + goto error; + + /* Request buffers */ + ret = rcar_drif_request_buf(sdr); + if (ret) + goto error; + + /* Start Rx */ + ret = rcar_drif_start(sdr); + if (ret) + goto error; + + mutex_unlock(&sdr->v4l2_mutex); + + return ret; + +error: + rcar_drif_release_queued_bufs(sdr, VB2_BUF_STATE_QUEUED); + rcar_drif_release_buf(sdr); + rcar_drif_release_dmachannels(sdr); + for_each_rcar_drif_channel(i, &enabled) + clk_disable_unprepare(sdr->ch[i]->clk); + + mutex_unlock(&sdr->v4l2_mutex); + + return ret; +} + +/* Stop streaming */ +static void rcar_drif_stop_streaming(struct vb2_queue *vq) +{ + struct rcar_drif_sdr *sdr = vb2_get_drv_priv(vq); + unsigned int i; + + mutex_lock(&sdr->v4l2_mutex); + + /* Stop hardware streaming */ + rcar_drif_stop(sdr); + + /* Return all queued buffers to vb2 */ + rcar_drif_release_queued_bufs(sdr, VB2_BUF_STATE_ERROR); + + /* Release buf */ + rcar_drif_release_buf(sdr); + + /* Release DMA channel resources */ + rcar_drif_release_dmachannels(sdr); + + for_each_rcar_drif_channel(i, &sdr->cur_ch_mask) + clk_disable_unprepare(sdr->ch[i]->clk); + + mutex_unlock(&sdr->v4l2_mutex); +} + +/* Vb2 ops */ +static const struct vb2_ops rcar_drif_vb2_ops = { + .queue_setup = rcar_drif_queue_setup, + .buf_queue = rcar_drif_buf_queue, + .start_streaming = rcar_drif_start_streaming, + .stop_streaming = rcar_drif_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +static int rcar_drif_querycap(struct file *file, void *fh, + struct v4l2_capability *cap) +{ + struct rcar_drif_sdr *sdr = video_drvdata(file); + + strlcpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver)); + strlcpy(cap->card, sdr->vdev->name, sizeof(cap->card)); + snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", + sdr->vdev->name); + + return 0; +} + +static int rcar_drif_set_default_format(struct rcar_drif_sdr *sdr) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(formats); i++) { + /* Matching fmt based on required channels is set as default */ + if (sdr->num_hw_ch == formats[i].num_ch) { + sdr->fmt = &formats[i]; + sdr->cur_ch_mask = sdr->hw_ch_mask; + sdr->num_cur_ch = sdr->num_hw_ch; + dev_dbg(sdr->dev, "default fmt[%u]: mask %lu num %u\n", + i, sdr->cur_ch_mask, sdr->num_cur_ch); + return 0; + } + } + return -EINVAL; +} + +static int rcar_drif_enum_fmt_sdr_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + if (f->index >= ARRAY_SIZE(formats)) + return -EINVAL; + + f->pixelformat = formats[f->index].pixelformat; + + return 0; +} + +static int rcar_drif_g_fmt_sdr_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct rcar_drif_sdr *sdr = video_drvdata(file); + + f->fmt.sdr.pixelformat = sdr->fmt->pixelformat; + f->fmt.sdr.buffersize = sdr->fmt->buffersize; + + return 0; +} + +static int rcar_drif_s_fmt_sdr_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct rcar_drif_sdr *sdr = video_drvdata(file); + struct vb2_queue *q = &sdr->vb_queue; + unsigned int i; + + if (vb2_is_busy(q)) + return -EBUSY; + + for (i = 0; i < ARRAY_SIZE(formats); i++) { + if (formats[i].pixelformat == f->fmt.sdr.pixelformat) + break; + } + + if (i == ARRAY_SIZE(formats)) + i = 0; /* Set the 1st format as default on no match */ + + sdr->fmt = &formats[i]; + f->fmt.sdr.pixelformat = sdr->fmt->pixelformat; + f->fmt.sdr.buffersize = formats[i].buffersize; + memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved)); + + /* + * If a format demands one channel only out of two + * enabled channels, pick the 0th channel. + */ + if (formats[i].num_ch < sdr->num_hw_ch) { + sdr->cur_ch_mask = BIT(0); + sdr->num_cur_ch = formats[i].num_ch; + } else { + sdr->cur_ch_mask = sdr->hw_ch_mask; + sdr->num_cur_ch = sdr->num_hw_ch; + } + + rdrif_dbg(sdr, "cur: idx %u mask %lu num %u\n", + i, sdr->cur_ch_mask, sdr->num_cur_ch); + + return 0; +} + +static int rcar_drif_try_fmt_sdr_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(formats); i++) { + if (formats[i].pixelformat == f->fmt.sdr.pixelformat) { + f->fmt.sdr.buffersize = formats[i].buffersize; + return 0; + } + } + + f->fmt.sdr.pixelformat = formats[0].pixelformat; + f->fmt.sdr.buffersize = formats[0].buffersize; + memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved)); + + return 0; +} + +/* Tuner subdev ioctls */ +static int rcar_drif_enum_freq_bands(struct file *file, void *priv, + struct v4l2_frequency_band *band) +{ + struct rcar_drif_sdr *sdr = video_drvdata(file); + + return v4l2_subdev_call(sdr->ep.subdev, tuner, enum_freq_bands, band); +} + +static int rcar_drif_g_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct rcar_drif_sdr *sdr = video_drvdata(file); + + return v4l2_subdev_call(sdr->ep.subdev, tuner, g_frequency, f); +} + +static int rcar_drif_s_frequency(struct file *file, void *priv, + const struct v4l2_frequency *f) +{ + struct rcar_drif_sdr *sdr = video_drvdata(file); + + return v4l2_subdev_call(sdr->ep.subdev, tuner, s_frequency, f); +} + +static int rcar_drif_g_tuner(struct file *file, void *priv, + struct v4l2_tuner *vt) +{ + struct rcar_drif_sdr *sdr = video_drvdata(file); + + return v4l2_subdev_call(sdr->ep.subdev, tuner, g_tuner, vt); +} + +static int rcar_drif_s_tuner(struct file *file, void *priv, + const struct v4l2_tuner *vt) +{ + struct rcar_drif_sdr *sdr = video_drvdata(file); + + return v4l2_subdev_call(sdr->ep.subdev, tuner, s_tuner, vt); +} + +static const struct v4l2_ioctl_ops rcar_drif_ioctl_ops = { + .vidioc_querycap = rcar_drif_querycap, + + .vidioc_enum_fmt_sdr_cap = rcar_drif_enum_fmt_sdr_cap, + .vidioc_g_fmt_sdr_cap = rcar_drif_g_fmt_sdr_cap, + .vidioc_s_fmt_sdr_cap = rcar_drif_s_fmt_sdr_cap, + .vidioc_try_fmt_sdr_cap = rcar_drif_try_fmt_sdr_cap, + + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + + .vidioc_s_frequency = rcar_drif_s_frequency, + .vidioc_g_frequency = rcar_drif_g_frequency, + .vidioc_s_tuner = rcar_drif_s_tuner, + .vidioc_g_tuner = rcar_drif_g_tuner, + .vidioc_enum_freq_bands = rcar_drif_enum_freq_bands, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, + .vidioc_log_status = v4l2_ctrl_log_status, +}; + +static const struct v4l2_file_operations rcar_drif_fops = { + .owner = THIS_MODULE, + .open = v4l2_fh_open, + .release = vb2_fop_release, + .read = vb2_fop_read, + .poll = vb2_fop_poll, + .mmap = vb2_fop_mmap, + .unlocked_ioctl = video_ioctl2, +}; + +static int rcar_drif_sdr_register(struct rcar_drif_sdr *sdr) +{ + int ret; + + /* Init video_device structure */ + sdr->vdev = video_device_alloc(); + if (!sdr->vdev) + return -ENOMEM; + + snprintf(sdr->vdev->name, sizeof(sdr->vdev->name), "R-Car DRIF"); + sdr->vdev->fops = &rcar_drif_fops; + sdr->vdev->ioctl_ops = &rcar_drif_ioctl_ops; + sdr->vdev->release = video_device_release; + sdr->vdev->lock = &sdr->v4l2_mutex; + sdr->vdev->queue = &sdr->vb_queue; + sdr->vdev->queue->lock = &sdr->vb_queue_mutex; + sdr->vdev->ctrl_handler = &sdr->ctrl_hdl; + sdr->vdev->v4l2_dev = &sdr->v4l2_dev; + sdr->vdev->device_caps = V4L2_CAP_SDR_CAPTURE | V4L2_CAP_TUNER | + V4L2_CAP_STREAMING | V4L2_CAP_READWRITE; + video_set_drvdata(sdr->vdev, sdr); + + /* Register V4L2 SDR device */ + ret = video_register_device(sdr->vdev, VFL_TYPE_SDR, -1); + if (ret) { + video_device_release(sdr->vdev); + sdr->vdev = NULL; + dev_err(sdr->dev, "failed video_register_device (%d)\n", ret); + } + + return ret; +} + +static void rcar_drif_sdr_unregister(struct rcar_drif_sdr *sdr) +{ + video_unregister_device(sdr->vdev); + sdr->vdev = NULL; +} + +/* Sub-device bound callback */ +static int rcar_drif_notify_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, + struct v4l2_async_subdev *asd) +{ + struct rcar_drif_sdr *sdr = + container_of(notifier, struct rcar_drif_sdr, notifier); + + if (sdr->ep.asd.match.fwnode.fwnode != + of_fwnode_handle(subdev->dev->of_node)) { + rdrif_err(sdr, "subdev %s cannot bind\n", subdev->name); + return -EINVAL; + } + + v4l2_set_subdev_hostdata(subdev, sdr); + sdr->ep.subdev = subdev; + rdrif_dbg(sdr, "bound asd %s\n", subdev->name); + + return 0; +} + +/* Sub-device unbind callback */ +static void rcar_drif_notify_unbind(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, + struct v4l2_async_subdev *asd) +{ + struct rcar_drif_sdr *sdr = + container_of(notifier, struct rcar_drif_sdr, notifier); + + if (sdr->ep.subdev != subdev) { + rdrif_err(sdr, "subdev %s is not bound\n", subdev->name); + return; + } + + /* Free ctrl handler if initialized */ + v4l2_ctrl_handler_free(&sdr->ctrl_hdl); + sdr->v4l2_dev.ctrl_handler = NULL; + sdr->ep.subdev = NULL; + + rcar_drif_sdr_unregister(sdr); + rdrif_dbg(sdr, "unbind asd %s\n", subdev->name); +} + +/* Sub-device registered notification callback */ +static int rcar_drif_notify_complete(struct v4l2_async_notifier *notifier) +{ + struct rcar_drif_sdr *sdr = + container_of(notifier, struct rcar_drif_sdr, notifier); + int ret; + + /* + * The subdev tested at this point uses 4 controls. Using 10 as a worst + * case scenario hint. When less controls are needed there will be some + * unused memory and when more controls are needed the framework uses + * hash to manage controls within this number. + */ + ret = v4l2_ctrl_handler_init(&sdr->ctrl_hdl, 10); + if (ret) + return -ENOMEM; + + sdr->v4l2_dev.ctrl_handler = &sdr->ctrl_hdl; + ret = v4l2_device_register_subdev_nodes(&sdr->v4l2_dev); + if (ret) { + rdrif_err(sdr, "failed: register subdev nodes ret %d\n", ret); + goto error; + } + + ret = v4l2_ctrl_add_handler(&sdr->ctrl_hdl, + sdr->ep.subdev->ctrl_handler, NULL); + if (ret) { + rdrif_err(sdr, "failed: ctrl add hdlr ret %d\n", ret); + goto error; + } + + ret = rcar_drif_sdr_register(sdr); + if (ret) + goto error; + + return ret; + +error: + v4l2_ctrl_handler_free(&sdr->ctrl_hdl); + + return ret; +} + +/* Read endpoint properties */ +static void rcar_drif_get_ep_properties(struct rcar_drif_sdr *sdr, + struct fwnode_handle *fwnode) +{ + u32 val; + + /* Set the I2S defaults for SIRMDR1*/ + sdr->mdr1 = RCAR_DRIF_SIRMDR1_SYNCMD_LR | RCAR_DRIF_SIRMDR1_MSB_FIRST | + RCAR_DRIF_SIRMDR1_DTDL_1 | RCAR_DRIF_SIRMDR1_SYNCDL_0; + + /* Parse sync polarity from endpoint */ + if (!fwnode_property_read_u32(fwnode, "sync-active", &val)) + sdr->mdr1 |= val ? RCAR_DRIF_SIRMDR1_SYNCAC_POL_HIGH : + RCAR_DRIF_SIRMDR1_SYNCAC_POL_LOW; + else + sdr->mdr1 |= RCAR_DRIF_SIRMDR1_SYNCAC_POL_HIGH; /* default */ + + dev_dbg(sdr->dev, "mdr1 0x%08x\n", sdr->mdr1); +} + +/* Parse sub-devs (tuner) to find a matching device */ +static int rcar_drif_parse_subdevs(struct rcar_drif_sdr *sdr) +{ + struct v4l2_async_notifier *notifier = &sdr->notifier; + struct fwnode_handle *fwnode, *ep; + + notifier->subdevs = devm_kzalloc(sdr->dev, sizeof(*notifier->subdevs), + GFP_KERNEL); + if (!notifier->subdevs) + return -ENOMEM; + + ep = fwnode_graph_get_next_endpoint(of_fwnode_handle(sdr->dev->of_node), + NULL); + if (!ep) + return 0; + + notifier->subdevs[notifier->num_subdevs] = &sdr->ep.asd; + fwnode = fwnode_graph_get_remote_port_parent(ep); + if (!fwnode) { + dev_warn(sdr->dev, "bad remote port parent\n"); + fwnode_handle_put(ep); + return -EINVAL; + } + + sdr->ep.asd.match.fwnode.fwnode = fwnode; + sdr->ep.asd.match_type = V4L2_ASYNC_MATCH_FWNODE; + notifier->num_subdevs++; + + /* Get the endpoint properties */ + rcar_drif_get_ep_properties(sdr, ep); + + fwnode_handle_put(fwnode); + fwnode_handle_put(ep); + + return 0; +} + +/* Check if the given device is the primary bond */ +static bool rcar_drif_primary_bond(struct platform_device *pdev) +{ + return of_property_read_bool(pdev->dev.of_node, "renesas,primary-bond"); +} + +/* Check if both devices of the bond are enabled */ +static struct device_node *rcar_drif_bond_enabled(struct platform_device *p) +{ + struct device_node *np; + + np = of_parse_phandle(p->dev.of_node, "renesas,bonding", 0); + if (np && of_device_is_available(np)) + return np; + + return NULL; +} + +/* Check if the bonded device is probed */ +static int rcar_drif_bond_available(struct rcar_drif_sdr *sdr, + struct device_node *np) +{ + struct platform_device *pdev; + struct rcar_drif *ch; + int ret = 0; + + pdev = of_find_device_by_node(np); + if (!pdev) { + dev_err(sdr->dev, "failed to get bonded device from node\n"); + return -ENODEV; + } + + device_lock(&pdev->dev); + ch = platform_get_drvdata(pdev); + if (ch) { + /* Update sdr data in the bonded device */ + ch->sdr = sdr; + + /* Update sdr with bonded device data */ + sdr->ch[ch->num] = ch; + sdr->hw_ch_mask |= BIT(ch->num); + } else { + /* Defer */ + dev_info(sdr->dev, "defer probe\n"); + ret = -EPROBE_DEFER; + } + device_unlock(&pdev->dev); + + put_device(&pdev->dev); + + return ret; +} + +/* V4L2 SDR device probe */ +static int rcar_drif_sdr_probe(struct rcar_drif_sdr *sdr) +{ + int ret; + + /* Validate any supported format for enabled channels */ + ret = rcar_drif_set_default_format(sdr); + if (ret) { + dev_err(sdr->dev, "failed to set default format\n"); + return ret; + } + + /* Set defaults */ + sdr->hwbuf_size = RCAR_DRIF_DEFAULT_HWBUF_SIZE; + + mutex_init(&sdr->v4l2_mutex); + mutex_init(&sdr->vb_queue_mutex); + spin_lock_init(&sdr->queued_bufs_lock); + spin_lock_init(&sdr->dma_lock); + INIT_LIST_HEAD(&sdr->queued_bufs); + + /* Init videobuf2 queue structure */ + sdr->vb_queue.type = V4L2_BUF_TYPE_SDR_CAPTURE; + sdr->vb_queue.io_modes = VB2_READ | VB2_MMAP | VB2_DMABUF; + sdr->vb_queue.drv_priv = sdr; + sdr->vb_queue.buf_struct_size = sizeof(struct rcar_drif_frame_buf); + sdr->vb_queue.ops = &rcar_drif_vb2_ops; + sdr->vb_queue.mem_ops = &vb2_vmalloc_memops; + sdr->vb_queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + + /* Init videobuf2 queue */ + ret = vb2_queue_init(&sdr->vb_queue); + if (ret) { + dev_err(sdr->dev, "failed: vb2_queue_init ret %d\n", ret); + return ret; + } + + /* Register the v4l2_device */ + ret = v4l2_device_register(sdr->dev, &sdr->v4l2_dev); + if (ret) { + dev_err(sdr->dev, "failed: v4l2_device_register ret %d\n", ret); + return ret; + } + + /* + * Parse subdevs after v4l2_device_register because if the subdev + * is already probed, bound and complete will be called immediately + */ + ret = rcar_drif_parse_subdevs(sdr); + if (ret) + goto error; + + sdr->notifier.bound = rcar_drif_notify_bound; + sdr->notifier.unbind = rcar_drif_notify_unbind; + sdr->notifier.complete = rcar_drif_notify_complete; + + /* Register notifier */ + ret = v4l2_async_notifier_register(&sdr->v4l2_dev, &sdr->notifier); + if (ret < 0) { + dev_err(sdr->dev, "failed: notifier register ret %d\n", ret); + goto error; + } + + return ret; + +error: + v4l2_device_unregister(&sdr->v4l2_dev); + + return ret; +} + +/* V4L2 SDR device remove */ +static void rcar_drif_sdr_remove(struct rcar_drif_sdr *sdr) +{ + v4l2_async_notifier_unregister(&sdr->notifier); + v4l2_device_unregister(&sdr->v4l2_dev); +} + +/* DRIF channel probe */ +static int rcar_drif_probe(struct platform_device *pdev) +{ + struct rcar_drif_sdr *sdr; + struct device_node *np; + struct rcar_drif *ch; + struct resource *res; + int ret; + + /* Reserve memory for enabled channel */ + ch = devm_kzalloc(&pdev->dev, sizeof(*ch), GFP_KERNEL); + if (!ch) + return -ENOMEM; + + ch->pdev = pdev; + + /* Module clock */ + ch->clk = devm_clk_get(&pdev->dev, "fck"); + if (IS_ERR(ch->clk)) { + ret = PTR_ERR(ch->clk); + dev_err(&pdev->dev, "clk get failed (%d)\n", ret); + return ret; + } + + /* Register map */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + ch->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(ch->base)) { + ret = PTR_ERR(ch->base); + dev_err(&pdev->dev, "ioremap failed (%d)\n", ret); + return ret; + } + ch->start = res->start; + platform_set_drvdata(pdev, ch); + + /* Check if both channels of the bond are enabled */ + np = rcar_drif_bond_enabled(pdev); + if (np) { + /* Check if current channel acting as primary-bond */ + if (!rcar_drif_primary_bond(pdev)) { + ch->num = 1; /* Primary bond is channel 0 always */ + of_node_put(np); + return 0; + } + } + + /* Reserve memory for SDR structure */ + sdr = devm_kzalloc(&pdev->dev, sizeof(*sdr), GFP_KERNEL); + if (!sdr) { + of_node_put(np); + return -ENOMEM; + } + ch->sdr = sdr; + sdr->dev = &pdev->dev; + + /* Establish links between SDR and channel(s) */ + sdr->ch[ch->num] = ch; + sdr->hw_ch_mask = BIT(ch->num); + if (np) { + /* Check if bonded device is ready */ + ret = rcar_drif_bond_available(sdr, np); + of_node_put(np); + if (ret) + return ret; + } + sdr->num_hw_ch = hweight_long(sdr->hw_ch_mask); + + return rcar_drif_sdr_probe(sdr); +} + +/* DRIF channel remove */ +static int rcar_drif_remove(struct platform_device *pdev) +{ + struct rcar_drif *ch = platform_get_drvdata(pdev); + struct rcar_drif_sdr *sdr = ch->sdr; + + /* Channel 0 will be the SDR instance */ + if (ch->num) + return 0; + + /* SDR instance */ + rcar_drif_sdr_remove(sdr); + + return 0; +} + +/* FIXME: Implement suspend/resume support */ +static int __maybe_unused rcar_drif_suspend(struct device *dev) +{ + return 0; +} + +static int __maybe_unused rcar_drif_resume(struct device *dev) +{ + return 0; +} + +static SIMPLE_DEV_PM_OPS(rcar_drif_pm_ops, rcar_drif_suspend, + rcar_drif_resume); + +static const struct of_device_id rcar_drif_of_table[] = { + { .compatible = "renesas,rcar-gen3-drif" }, + { } +}; +MODULE_DEVICE_TABLE(of, rcar_drif_of_table); + +#define RCAR_DRIF_DRV_NAME "rcar_drif" +static struct platform_driver rcar_drif_driver = { + .driver = { + .name = RCAR_DRIF_DRV_NAME, + .of_match_table = of_match_ptr(rcar_drif_of_table), + .pm = &rcar_drif_pm_ops, + }, + .probe = rcar_drif_probe, + .remove = rcar_drif_remove, +}; + +module_platform_driver(rcar_drif_driver); + +MODULE_DESCRIPTION("Renesas R-Car Gen3 DRIF driver"); +MODULE_ALIAS("platform:" RCAR_DRIF_DRV_NAME); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Ramesh Shanmugasundaram "); -- cgit v1.2.3-58-ga151 From fbe19d1f060794c001def92a18d3d80ce0083b28 Mon Sep 17 00:00:00 2001 From: Ramesh Shanmugasundaram Date: Mon, 12 Jun 2017 10:26:20 -0300 Subject: [media] MAINTAINERS: Add entry for R-Car DRIF & MAX2175 drivers Add maintainter entry for the R-Car DRIF and MAX2175 drivers. Signed-off-by: Ramesh Shanmugasundaram Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 4ac340d189a3..6ab1f8757570 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8032,6 +8032,16 @@ S: Maintained F: Documentation/hwmon/max20751 F: drivers/hwmon/max20751.c +MAX2175 SDR TUNER DRIVER +M: Ramesh Shanmugasundaram +L: linux-media@vger.kernel.org +T: git git://linuxtv.org/media_tree.git +S: Maintained +F: Documentation/devicetree/bindings/media/i2c/max2175.txt +F: Documentation/media/v4l-drivers/max2175.rst +F: drivers/media/i2c/max2175* +F: include/uapi/linux/max2175.h + MAX6650 HARDWARE MONITOR AND FAN CONTROLLER DRIVER L: linux-hwmon@vger.kernel.org S: Orphan @@ -8112,6 +8122,15 @@ L: linux-iio@vger.kernel.org S: Maintained F: drivers/iio/dac/cio-dac.c +MEDIA DRIVERS FOR RENESAS - DRIF +M: Ramesh Shanmugasundaram +L: linux-media@vger.kernel.org +L: linux-renesas-soc@vger.kernel.org +T: git git://linuxtv.org/media_tree.git +S: Supported +F: Documentation/devicetree/bindings/media/renesas,drif.txt +F: drivers/media/platform/rcar_drif.c + MEDIA DRIVERS FOR RENESAS - FCP M: Laurent Pinchart L: linux-media@vger.kernel.org -- cgit v1.2.3-58-ga151 From 8ca00927d61eb4dafe823387f1fb23913ffed5f0 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 20 Jun 2017 07:11:15 -0300 Subject: [media] max2175: remove an useless comparision load is an unsigned integer. So, it is always bigger or equal to zero, as reported by gcc: drivers/media/i2c/max2175.c: In function 'max2175_refout_load_to_bits': drivers/media/i2c/max2175.c:1272:11: warning: comparison of unsigned expression >= 0 is always true [-Wtype-limits] if (load >= 0 && load <= 40) ^~ Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/max2175.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/i2c/max2175.c b/drivers/media/i2c/max2175.c index 0d28a80f8ed2..a4736a8a7792 100644 --- a/drivers/media/i2c/max2175.c +++ b/drivers/media/i2c/max2175.c @@ -1269,7 +1269,7 @@ static const struct v4l2_ctrl_config max2175_na_rx_mode = { static int max2175_refout_load_to_bits(struct i2c_client *client, u32 load, u32 *bits) { - if (load >= 0 && load <= 40) + if (load <= 40) *bits = load / 10; else if (load >= 60 && load <= 70) *bits = load / 10 - 1; -- cgit v1.2.3-58-ga151 From 4e12ad40b76b76dc81413ab9e52b9f12465341a9 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Wed, 7 Jun 2017 15:33:40 -0300 Subject: [media] dt-bindings: Add bindings for video-multiplexer device Add bindings documentation for the video multiplexer device. Signed-off-by: Sascha Hauer Signed-off-by: Philipp Zabel Signed-off-by: Steve Longerbeam Acked-by: Sakari Ailus Reviewed-by: Sebastian Reichel Acked-by: Rob Herring Acked-by: Pavel Machek Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../devicetree/bindings/media/video-mux.txt | 60 ++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/video-mux.txt diff --git a/Documentation/devicetree/bindings/media/video-mux.txt b/Documentation/devicetree/bindings/media/video-mux.txt new file mode 100644 index 000000000000..63b9dc913e45 --- /dev/null +++ b/Documentation/devicetree/bindings/media/video-mux.txt @@ -0,0 +1,60 @@ +Video Multiplexer +================= + +Video multiplexers allow to select between multiple input ports. Video received +on the active input port is passed through to the output port. Muxes described +by this binding are controlled by a multiplexer controller that is described by +the bindings in Documentation/devicetree/bindings/mux/mux-controller.txt + +Required properties: +- compatible : should be "video-mux" +- mux-controls : mux controller node to use for operating the mux +- #address-cells: should be <1> +- #size-cells: should be <0> +- port@*: at least three port nodes containing endpoints connecting to the + source and sink devices according to of_graph bindings. The last port is + the output port, all others are inputs. + +Optionally, #address-cells, #size-cells, and port nodes can be grouped under a +ports node as described in Documentation/devicetree/bindings/graph.txt. + +Example: + + mux: mux-controller { + compatible = "gpio-mux"; + #mux-control-cells = <0>; + + mux-gpios = <&gpio1 15 GPIO_ACTIVE_HIGH>; + }; + + video-mux { + compatible = "video-mux"; + mux-controls = <&mux>; + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + mux_in0: endpoint { + remote-endpoint = <&video_source0_out>; + }; + }; + + port@1 { + reg = <1>; + + mux_in1: endpoint { + remote-endpoint = <&video_source1_out>; + }; + }; + + port@2 { + reg = <2>; + + mux_out: endpoint { + remote-endpoint = <&capture_interface_in>; + }; + }; + }; +}; -- cgit v1.2.3-58-ga151 From b45cd756368823ce9e19bcb8c69d575595df5c5a Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Wed, 7 Jun 2017 15:33:54 -0300 Subject: [media] add mux and video interface bridge entity functions Add two new media entity function definitions for video multiplexers and video interface bridges. - renamed MEDIA_ENT_F_MUX to MEDIA_ENT_F_VID_MUX Signed-off-by: Philipp Zabel Signed-off-by: Steve Longerbeam Acked-by: Sakari Ailus Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/media/uapi/mediactl/media-types.rst | 21 +++++++++++++++++++++ include/uapi/linux/media.h | 6 ++++++ 2 files changed, 27 insertions(+) diff --git a/Documentation/media/uapi/mediactl/media-types.rst b/Documentation/media/uapi/mediactl/media-types.rst index 2a5164aea2b4..71078565d644 100644 --- a/Documentation/media/uapi/mediactl/media-types.rst +++ b/Documentation/media/uapi/mediactl/media-types.rst @@ -299,6 +299,27 @@ Types and flags used to represent the media graph elements received on its sink pad and outputs the statistics data on its source pad. + - .. row 29 + + .. _MEDIA-ENT-F-VID-MUX: + + - ``MEDIA_ENT_F_VID_MUX`` + + - Video multiplexer. An entity capable of multiplexing must have at + least two sink pads and one source pad, and must pass the video + frame(s) received from the active sink pad to the source pad. + + - .. row 30 + + .. _MEDIA-ENT-F-VID-IF-BRIDGE: + + - ``MEDIA_ENT_F_VID_IF_BRIDGE`` + + - Video interface bridge. A video interface bridge entity must have at + least one sink pad and at least one source pad. It receives video + frames on its sink pad from an input video bus of one type (HDMI, eDP, + MIPI CSI-2, ...), and outputs them on its source pad to an output + video bus of another type (eDP, MIPI CSI-2, parallel, ...). .. tabularcolumns:: |p{5.5cm}|p{12.0cm}| diff --git a/include/uapi/linux/media.h b/include/uapi/linux/media.h index 4890787731b8..fac96c64fe51 100644 --- a/include/uapi/linux/media.h +++ b/include/uapi/linux/media.h @@ -104,6 +104,12 @@ struct media_device_info { #define MEDIA_ENT_F_PROC_VIDEO_SCALER (MEDIA_ENT_F_BASE + 0x4005) #define MEDIA_ENT_F_PROC_VIDEO_STATISTICS (MEDIA_ENT_F_BASE + 0x4006) +/* + * Switch and bridge entitites + */ +#define MEDIA_ENT_F_VID_MUX (MEDIA_ENT_F_BASE + 0x5001) +#define MEDIA_ENT_F_VID_IF_BRIDGE (MEDIA_ENT_F_BASE + 0x5002) + /* * Connectors */ -- cgit v1.2.3-58-ga151 From 68803ad4522f5defc091c29f75665d942f2d5e82 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Wed, 7 Jun 2017 15:33:55 -0300 Subject: [media] platform: add video-multiplexer subdevice driver This driver can handle SoC internal and external video bus multiplexers, controlled by mux controllers provided by the mux controller framework, such as MMIO register bitfields or GPIOs. The subdevice passes through the mbus configuration of the active input to the output side. Since the mux framework is not yet merged, this driver contains temporary mmio-mux support to work without the framework. The driver should be converted to use the multiplexer API once the "mux: minimal mux subsystem" and "mux: mmio-based syscon mux controller" patches are merged. Signed-off-by: Sascha Hauer Signed-off-by: Philipp Zabel Signed-off-by: Steve Longerbeam Acked-by: Sakari Ailus Acked-by: Pavel Machek [hans.verkuil@cisco.com: add 'select REGMAP' to Kconfig] Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/Kconfig | 7 + drivers/media/platform/Makefile | 2 + drivers/media/platform/video-mux.c | 334 +++++++++++++++++++++++++++++++++++++ 3 files changed, 343 insertions(+) create mode 100644 drivers/media/platform/video-mux.c diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index 54b0f756be30..3c1eb4f63936 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -74,6 +74,13 @@ config VIDEO_M32R_AR_M64278 To compile this driver as a module, choose M here: the module will be called arv. +config VIDEO_MUX + tristate "Video Multiplexer" + depends on OF && VIDEO_V4L2_SUBDEV_API && MEDIA_CONTROLLER + select REGMAP + help + This driver provides support for N:1 video bus multiplexers. + config VIDEO_OMAP3 tristate "OMAP 3 Camera support" depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API && ARCH_OMAP3 diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile index 349ccffb49db..ba7981d6f3c5 100644 --- a/drivers/media/platform/Makefile +++ b/drivers/media/platform/Makefile @@ -28,6 +28,8 @@ obj-$(CONFIG_VIDEO_SH_VEU) += sh_veu.o obj-$(CONFIG_VIDEO_MEM2MEM_DEINTERLACE) += m2m-deinterlace.o +obj-$(CONFIG_VIDEO_MUX) += video-mux.o + obj-$(CONFIG_VIDEO_S3C_CAMIF) += s3c-camif/ obj-$(CONFIG_VIDEO_SAMSUNG_EXYNOS4_IS) += exynos4-is/ obj-$(CONFIG_VIDEO_SAMSUNG_S5P_JPEG) += s5p-jpeg/ diff --git a/drivers/media/platform/video-mux.c b/drivers/media/platform/video-mux.c new file mode 100644 index 000000000000..665744716f73 --- /dev/null +++ b/drivers/media/platform/video-mux.c @@ -0,0 +1,334 @@ +/* + * video stream multiplexer controlled via mux control + * + * Copyright (C) 2013 Pengutronix, Sascha Hauer + * Copyright (C) 2016-2017 Pengutronix, Philipp Zabel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct video_mux { + struct v4l2_subdev subdev; + struct media_pad *pads; + struct v4l2_mbus_framefmt *format_mbus; + struct regmap_field *field; + struct mutex lock; + int active; +}; + +static inline struct video_mux *v4l2_subdev_to_video_mux(struct v4l2_subdev *sd) +{ + return container_of(sd, struct video_mux, subdev); +} + +static int video_mux_link_setup(struct media_entity *entity, + const struct media_pad *local, + const struct media_pad *remote, u32 flags) +{ + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); + struct video_mux *vmux = v4l2_subdev_to_video_mux(sd); + int ret = 0; + + /* + * The mux state is determined by the enabled sink pad link. + * Enabling or disabling the source pad link has no effect. + */ + if (local->flags & MEDIA_PAD_FL_SOURCE) + return 0; + + dev_dbg(sd->dev, "link setup '%s':%d->'%s':%d[%d]", + remote->entity->name, remote->index, local->entity->name, + local->index, flags & MEDIA_LNK_FL_ENABLED); + + mutex_lock(&vmux->lock); + + if (flags & MEDIA_LNK_FL_ENABLED) { + if (vmux->active == local->index) + goto out; + + if (vmux->active >= 0) { + ret = -EBUSY; + goto out; + } + + dev_dbg(sd->dev, "setting %d active\n", local->index); + ret = regmap_field_write(vmux->field, local->index); + if (ret < 0) + goto out; + vmux->active = local->index; + } else { + if (vmux->active != local->index) + goto out; + + dev_dbg(sd->dev, "going inactive\n"); + vmux->active = -1; + } + +out: + mutex_unlock(&vmux->lock); + return ret; +} + +static const struct media_entity_operations video_mux_ops = { + .link_setup = video_mux_link_setup, + .link_validate = v4l2_subdev_link_validate, +}; + +static int video_mux_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct video_mux *vmux = v4l2_subdev_to_video_mux(sd); + struct v4l2_subdev *upstream_sd; + struct media_pad *pad; + + if (vmux->active == -1) { + dev_err(sd->dev, "Can not start streaming on inactive mux\n"); + return -EINVAL; + } + + pad = media_entity_remote_pad(&sd->entity.pads[vmux->active]); + if (!pad) { + dev_err(sd->dev, "Failed to find remote source pad\n"); + return -ENOLINK; + } + + if (!is_media_entity_v4l2_subdev(pad->entity)) { + dev_err(sd->dev, "Upstream entity is not a v4l2 subdev\n"); + return -ENODEV; + } + + upstream_sd = media_entity_to_v4l2_subdev(pad->entity); + + return v4l2_subdev_call(upstream_sd, video, s_stream, enable); +} + +static const struct v4l2_subdev_video_ops video_mux_subdev_video_ops = { + .s_stream = video_mux_s_stream, +}; + +static struct v4l2_mbus_framefmt * +__video_mux_get_pad_format(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + unsigned int pad, u32 which) +{ + struct video_mux *vmux = v4l2_subdev_to_video_mux(sd); + + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + return v4l2_subdev_get_try_format(sd, cfg, pad); + case V4L2_SUBDEV_FORMAT_ACTIVE: + return &vmux->format_mbus[pad]; + default: + return NULL; + } +} + +static int video_mux_get_format(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *sdformat) +{ + struct video_mux *vmux = v4l2_subdev_to_video_mux(sd); + + mutex_lock(&vmux->lock); + + sdformat->format = *__video_mux_get_pad_format(sd, cfg, sdformat->pad, + sdformat->which); + + mutex_unlock(&vmux->lock); + + return 0; +} + +static int video_mux_set_format(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *sdformat) +{ + struct video_mux *vmux = v4l2_subdev_to_video_mux(sd); + struct v4l2_mbus_framefmt *mbusformat; + struct media_pad *pad = &vmux->pads[sdformat->pad]; + + mbusformat = __video_mux_get_pad_format(sd, cfg, sdformat->pad, + sdformat->which); + if (!mbusformat) + return -EINVAL; + + mutex_lock(&vmux->lock); + + /* Source pad mirrors active sink pad, no limitations on sink pads */ + if ((pad->flags & MEDIA_PAD_FL_SOURCE) && vmux->active >= 0) + sdformat->format = vmux->format_mbus[vmux->active]; + + *mbusformat = sdformat->format; + + mutex_unlock(&vmux->lock); + + return 0; +} + +static const struct v4l2_subdev_pad_ops video_mux_pad_ops = { + .get_fmt = video_mux_get_format, + .set_fmt = video_mux_set_format, +}; + +static const struct v4l2_subdev_ops video_mux_subdev_ops = { + .pad = &video_mux_pad_ops, + .video = &video_mux_subdev_video_ops, +}; + +static int video_mux_probe_mmio_mux(struct video_mux *vmux) +{ + struct device *dev = vmux->subdev.dev; + struct of_phandle_args args; + struct reg_field field; + struct regmap *regmap; + u32 reg, mask; + int ret; + + ret = of_parse_phandle_with_args(dev->of_node, "mux-controls", + "#mux-control-cells", 0, &args); + if (ret) + return ret; + + if (!of_device_is_compatible(args.np, "mmio-mux")) + return -EINVAL; + + regmap = syscon_node_to_regmap(args.np->parent); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + ret = of_property_read_u32_index(args.np, "mux-reg-masks", + 2 * args.args[0], ®); + if (!ret) + ret = of_property_read_u32_index(args.np, "mux-reg-masks", + 2 * args.args[0] + 1, &mask); + if (ret < 0) + return ret; + + field.reg = reg; + field.msb = fls(mask) - 1; + field.lsb = ffs(mask) - 1; + + vmux->field = devm_regmap_field_alloc(dev, regmap, field); + if (IS_ERR(vmux->field)) + return PTR_ERR(vmux->field); + + return 0; +} + +static int video_mux_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct device *dev = &pdev->dev; + struct device_node *ep; + struct video_mux *vmux; + unsigned int num_pads = 0; + int ret; + int i; + + vmux = devm_kzalloc(dev, sizeof(*vmux), GFP_KERNEL); + if (!vmux) + return -ENOMEM; + + platform_set_drvdata(pdev, vmux); + + v4l2_subdev_init(&vmux->subdev, &video_mux_subdev_ops); + snprintf(vmux->subdev.name, sizeof(vmux->subdev.name), "%s", np->name); + vmux->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + vmux->subdev.dev = dev; + + /* + * The largest numbered port is the output port. It determines + * total number of pads. + */ + for_each_endpoint_of_node(np, ep) { + struct of_endpoint endpoint; + + of_graph_parse_endpoint(ep, &endpoint); + num_pads = max(num_pads, endpoint.port + 1); + } + + if (num_pads < 2) { + dev_err(dev, "Not enough ports %d\n", num_pads); + return -EINVAL; + } + + ret = video_mux_probe_mmio_mux(vmux); + if (ret) { + if (ret != -EPROBE_DEFER) + dev_err(dev, "Failed to get mux: %d\n", ret); + return ret; + } + + mutex_init(&vmux->lock); + vmux->active = -1; + vmux->pads = devm_kcalloc(dev, num_pads, sizeof(*vmux->pads), + GFP_KERNEL); + vmux->format_mbus = devm_kcalloc(dev, num_pads, + sizeof(*vmux->format_mbus), + GFP_KERNEL); + + for (i = 0; i < num_pads - 1; i++) + vmux->pads[i].flags = MEDIA_PAD_FL_SINK; + vmux->pads[num_pads - 1].flags = MEDIA_PAD_FL_SOURCE; + + vmux->subdev.entity.function = MEDIA_ENT_F_VID_MUX; + ret = media_entity_pads_init(&vmux->subdev.entity, num_pads, + vmux->pads); + if (ret < 0) + return ret; + + vmux->subdev.entity.ops = &video_mux_ops; + + return v4l2_async_register_subdev(&vmux->subdev); +} + +static int video_mux_remove(struct platform_device *pdev) +{ + struct video_mux *vmux = platform_get_drvdata(pdev); + struct v4l2_subdev *sd = &vmux->subdev; + + v4l2_async_unregister_subdev(sd); + media_entity_cleanup(&sd->entity); + + return 0; +} + +static const struct of_device_id video_mux_dt_ids[] = { + { .compatible = "video-mux", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, video_mux_dt_ids); + +static struct platform_driver video_mux_driver = { + .probe = video_mux_probe, + .remove = video_mux_remove, + .driver = { + .of_match_table = video_mux_dt_ids, + .name = "video-mux", + }, +}; + +module_platform_driver(video_mux_driver); + +MODULE_DESCRIPTION("video stream multiplexer"); +MODULE_AUTHOR("Sascha Hauer, Pengutronix"); +MODULE_AUTHOR("Philipp Zabel, Pengutronix"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3-58-ga151 From e014a63cc685e8133d9d46d0ca41eceb279614f5 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Mon, 12 Jun 2017 09:24:24 -0300 Subject: [media] MAINTAINERS: add maintainer entry for video multiplexer v4l2 subdevice driver Add maintainer entry for the video multiplexer v4l2 subdevice driver that will control video bus multiplexers via the multiplexer framework. Signed-off-by: Philip Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 6ab1f8757570..fd22b66ffa34 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13545,6 +13545,12 @@ S: Maintained F: drivers/media/v4l2-core/videobuf2-* F: include/media/videobuf2-* +VIDEO MULTIPLEXER DRIVER +M: Philipp Zabel +L: linux-media@vger.kernel.org +S: Maintained +F: drivers/media/platform/video-mux.c + VIRTIO AND VHOST VSOCK DRIVER M: Stefan Hajnoczi L: kvm@vger.kernel.org -- cgit v1.2.3-58-ga151 From 8de73374f8451ca0c83105719761d3572762dfeb Mon Sep 17 00:00:00 2001 From: Steve Longerbeam Date: Wed, 7 Jun 2017 15:33:42 -0300 Subject: [media] dt/bindings: Add bindings for OV5640 Add device tree binding documentation for the OV5640 camera sensor. Signed-off-by: Steve Longerbeam Acked-by: Rob Herring Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../devicetree/bindings/media/i2c/ov5640.txt | 45 ++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/i2c/ov5640.txt diff --git a/Documentation/devicetree/bindings/media/i2c/ov5640.txt b/Documentation/devicetree/bindings/media/i2c/ov5640.txt new file mode 100644 index 000000000000..540b36c4b1f2 --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/ov5640.txt @@ -0,0 +1,45 @@ +* Omnivision OV5640 MIPI CSI-2 sensor + +Required Properties: +- compatible: should be "ovti,ov5640" +- clocks: reference to the xclk input clock. +- clock-names: should be "xclk". +- DOVDD-supply: Digital I/O voltage supply, 1.8 volts +- AVDD-supply: Analog voltage supply, 2.8 volts +- DVDD-supply: Digital core voltage supply, 1.5 volts + +Optional Properties: +- reset-gpios: reference to the GPIO connected to the reset pin, if any. + This is an active low signal to the OV5640. +- powerdown-gpios: reference to the GPIO connected to the powerdown pin, + if any. This is an active high signal to the OV5640. + +The device node must contain one 'port' child node for its digital output +video port, in accordance with the video interface bindings defined in +Documentation/devicetree/bindings/media/video-interfaces.txt. + +Example: + +&i2c1 { + ov5640: camera@3c { + compatible = "ovti,ov5640"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_ov5640>; + reg = <0x3c>; + clocks = <&clks IMX6QDL_CLK_CKO>; + clock-names = "xclk"; + DOVDD-supply = <&vgen4_reg>; /* 1.8v */ + AVDD-supply = <&vgen3_reg>; /* 2.8v */ + DVDD-supply = <&vgen2_reg>; /* 1.5v */ + powerdown-gpios = <&gpio1 19 GPIO_ACTIVE_HIGH>; + reset-gpios = <&gpio1 20 GPIO_ACTIVE_LOW>; + + port { + ov5640_to_mipi_csi2: endpoint { + remote-endpoint = <&mipi_csi2_from_ov5640>; + clock-lanes = <0>; + data-lanes = <1 2>; + }; + }; + }; +}; -- cgit v1.2.3-58-ga151 From 19a81c1426c185cf9e902e1a33a9eacc6c6040cc Mon Sep 17 00:00:00 2001 From: Steve Longerbeam Date: Wed, 7 Jun 2017 15:33:56 -0300 Subject: [media] add Omnivision OV5640 sensor driver This driver is based on ov5640_mipi.c from Freescale imx_3.10.17_1.0.0_beta branch, modified heavily to bring forward to latest interfaces and code cleanup. Signed-off-by: Steve Longerbeam Acked-by: Sakari Ailus Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/Kconfig | 10 + drivers/media/i2c/Makefile | 1 + drivers/media/i2c/ov5640.c | 2344 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 2355 insertions(+) create mode 100644 drivers/media/i2c/ov5640.c diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index c0e6e78883b0..9e2ffd3ba7fa 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -548,6 +548,16 @@ config VIDEO_OV2659 To compile this driver as a module, choose M here: the module will be called ov2659. +config VIDEO_OV5640 + tristate "OmniVision OV5640 sensor support" + depends on OF + depends on GPIOLIB && VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API + depends on MEDIA_CAMERA_SUPPORT + select V4L2_FWNODE + ---help--- + This is a Video4Linux2 sensor-level driver for the Omnivision + OV5640 camera sensor with a MIPI CSI-2 interface. + config VIDEO_OV5645 tristate "OmniVision OV5645 sensor support" depends on OF diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index 5a4a761f7383..9e81bcacf394 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -58,6 +58,7 @@ obj-$(CONFIG_VIDEO_SONY_BTF_MPX) += sony-btf-mpx.o obj-$(CONFIG_VIDEO_UPD64031A) += upd64031a.o obj-$(CONFIG_VIDEO_UPD64083) += upd64083.o obj-$(CONFIG_VIDEO_OV2640) += ov2640.o +obj-$(CONFIG_VIDEO_OV5640) += ov5640.o obj-$(CONFIG_VIDEO_OV5645) += ov5645.o obj-$(CONFIG_VIDEO_OV5647) += ov5647.o obj-$(CONFIG_VIDEO_OV7640) += ov7640.o diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c new file mode 100644 index 000000000000..1f5b483cf334 --- /dev/null +++ b/drivers/media/i2c/ov5640.c @@ -0,0 +1,2344 @@ +/* + * Copyright (C) 2011-2013 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright (C) 2014-2017 Mentor Graphics Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* min/typical/max system clock (xclk) frequencies */ +#define OV5640_XCLK_MIN 6000000 +#define OV5640_XCLK_MAX 24000000 + +#define OV5640_DEFAULT_SLAVE_ID 0x3c + +#define OV5640_REG_CHIP_ID 0x300a +#define OV5640_REG_PAD_OUTPUT00 0x3019 +#define OV5640_REG_SC_PLL_CTRL0 0x3034 +#define OV5640_REG_SC_PLL_CTRL1 0x3035 +#define OV5640_REG_SC_PLL_CTRL2 0x3036 +#define OV5640_REG_SC_PLL_CTRL3 0x3037 +#define OV5640_REG_SLAVE_ID 0x3100 +#define OV5640_REG_SYS_ROOT_DIVIDER 0x3108 +#define OV5640_REG_AWB_R_GAIN 0x3400 +#define OV5640_REG_AWB_G_GAIN 0x3402 +#define OV5640_REG_AWB_B_GAIN 0x3404 +#define OV5640_REG_AWB_MANUAL_CTRL 0x3406 +#define OV5640_REG_AEC_PK_EXPOSURE_HI 0x3500 +#define OV5640_REG_AEC_PK_EXPOSURE_MED 0x3501 +#define OV5640_REG_AEC_PK_EXPOSURE_LO 0x3502 +#define OV5640_REG_AEC_PK_MANUAL 0x3503 +#define OV5640_REG_AEC_PK_REAL_GAIN 0x350a +#define OV5640_REG_AEC_PK_VTS 0x350c +#define OV5640_REG_TIMING_HTS 0x380c +#define OV5640_REG_TIMING_VTS 0x380e +#define OV5640_REG_TIMING_TC_REG21 0x3821 +#define OV5640_REG_AEC_CTRL00 0x3a00 +#define OV5640_REG_AEC_B50_STEP 0x3a08 +#define OV5640_REG_AEC_B60_STEP 0x3a0a +#define OV5640_REG_AEC_CTRL0D 0x3a0d +#define OV5640_REG_AEC_CTRL0E 0x3a0e +#define OV5640_REG_AEC_CTRL0F 0x3a0f +#define OV5640_REG_AEC_CTRL10 0x3a10 +#define OV5640_REG_AEC_CTRL11 0x3a11 +#define OV5640_REG_AEC_CTRL1B 0x3a1b +#define OV5640_REG_AEC_CTRL1E 0x3a1e +#define OV5640_REG_AEC_CTRL1F 0x3a1f +#define OV5640_REG_HZ5060_CTRL00 0x3c00 +#define OV5640_REG_HZ5060_CTRL01 0x3c01 +#define OV5640_REG_SIGMADELTA_CTRL0C 0x3c0c +#define OV5640_REG_FRAME_CTRL01 0x4202 +#define OV5640_REG_MIPI_CTRL00 0x4800 +#define OV5640_REG_DEBUG_MODE 0x4814 +#define OV5640_REG_PRE_ISP_TEST_SET1 0x503d +#define OV5640_REG_SDE_CTRL0 0x5580 +#define OV5640_REG_SDE_CTRL1 0x5581 +#define OV5640_REG_SDE_CTRL3 0x5583 +#define OV5640_REG_SDE_CTRL4 0x5584 +#define OV5640_REG_SDE_CTRL5 0x5585 +#define OV5640_REG_AVG_READOUT 0x56a1 + +enum ov5640_mode_id { + OV5640_MODE_QCIF_176_144 = 0, + OV5640_MODE_QVGA_320_240, + OV5640_MODE_VGA_640_480, + OV5640_MODE_NTSC_720_480, + OV5640_MODE_PAL_720_576, + OV5640_MODE_XGA_1024_768, + OV5640_MODE_720P_1280_720, + OV5640_MODE_1080P_1920_1080, + OV5640_MODE_QSXGA_2592_1944, + OV5640_NUM_MODES, +}; + +enum ov5640_frame_rate { + OV5640_15_FPS = 0, + OV5640_30_FPS, + OV5640_NUM_FRAMERATES, +}; + +/* + * FIXME: remove this when a subdev API becomes available + * to set the MIPI CSI-2 virtual channel. + */ +static unsigned int virtual_channel; +module_param(virtual_channel, int, 0); +MODULE_PARM_DESC(virtual_channel, + "MIPI CSI-2 virtual channel (0..3), default 0"); + +static const int ov5640_framerates[] = { + [OV5640_15_FPS] = 15, + [OV5640_30_FPS] = 30, +}; + +/* regulator supplies */ +static const char * const ov5640_supply_name[] = { + "DOVDD", /* Digital I/O (1.8V) suppply */ + "DVDD", /* Digital Core (1.5V) supply */ + "AVDD", /* Analog (2.8V) supply */ +}; + +#define OV5640_NUM_SUPPLIES ARRAY_SIZE(ov5640_supply_name) + +/* + * Image size under 1280 * 960 are SUBSAMPLING + * Image size upper 1280 * 960 are SCALING + */ +enum ov5640_downsize_mode { + SUBSAMPLING, + SCALING, +}; + +struct reg_value { + u16 reg_addr; + u8 val; + u8 mask; + u32 delay_ms; +}; + +struct ov5640_mode_info { + enum ov5640_mode_id id; + enum ov5640_downsize_mode dn_mode; + u32 width; + u32 height; + const struct reg_value *reg_data; + u32 reg_data_size; +}; + +struct ov5640_ctrls { + struct v4l2_ctrl_handler handler; + struct { + struct v4l2_ctrl *auto_exp; + struct v4l2_ctrl *exposure; + }; + struct { + struct v4l2_ctrl *auto_wb; + struct v4l2_ctrl *blue_balance; + struct v4l2_ctrl *red_balance; + }; + struct { + struct v4l2_ctrl *auto_gain; + struct v4l2_ctrl *gain; + }; + struct v4l2_ctrl *brightness; + struct v4l2_ctrl *saturation; + struct v4l2_ctrl *contrast; + struct v4l2_ctrl *hue; + struct v4l2_ctrl *test_pattern; +}; + +struct ov5640_dev { + struct i2c_client *i2c_client; + struct v4l2_subdev sd; + struct media_pad pad; + struct v4l2_fwnode_endpoint ep; /* the parsed DT endpoint info */ + struct clk *xclk; /* system clock to OV5640 */ + u32 xclk_freq; + + struct regulator_bulk_data supplies[OV5640_NUM_SUPPLIES]; + struct gpio_desc *reset_gpio; + struct gpio_desc *pwdn_gpio; + + /* lock to protect all members below */ + struct mutex lock; + + int power_count; + + struct v4l2_mbus_framefmt fmt; + + const struct ov5640_mode_info *current_mode; + enum ov5640_frame_rate current_fr; + struct v4l2_fract frame_interval; + + struct ov5640_ctrls ctrls; + + u32 prev_sysclk, prev_hts; + u32 ae_low, ae_high, ae_target; + + bool pending_mode_change; + bool streaming; +}; + +static inline struct ov5640_dev *to_ov5640_dev(struct v4l2_subdev *sd) +{ + return container_of(sd, struct ov5640_dev, sd); +} + +static inline struct v4l2_subdev *ctrl_to_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct ov5640_dev, + ctrls.handler)->sd; +} + +/* + * FIXME: all of these register tables are likely filled with + * entries that set the register to their power-on default values, + * and which are otherwise not touched by this driver. Those entries + * should be identified and removed to speed register load time + * over i2c. + */ + +static const struct reg_value ov5640_init_setting_30fps_VGA[] = { + + {0x3103, 0x11, 0, 0}, {0x3008, 0x82, 0, 5}, {0x3008, 0x42, 0, 0}, + {0x3103, 0x03, 0, 0}, {0x3017, 0x00, 0, 0}, {0x3018, 0x00, 0, 0}, + {0x3034, 0x18, 0, 0}, {0x3035, 0x14, 0, 0}, {0x3036, 0x38, 0, 0}, + {0x3037, 0x13, 0, 0}, {0x3108, 0x01, 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}, + {0x3715, 0x78, 0, 0}, {0x3717, 0x01, 0, 0}, {0x370b, 0x60, 0, 0}, + {0x3705, 0x1a, 0, 0}, {0x3905, 0x02, 0, 0}, {0x3906, 0x10, 0, 0}, + {0x3901, 0x0a, 0, 0}, {0x3731, 0x12, 0, 0}, {0x3600, 0x08, 0, 0}, + {0x3601, 0x33, 0, 0}, {0x302d, 0x60, 0, 0}, {0x3620, 0x52, 0, 0}, + {0x371b, 0x20, 0, 0}, {0x471c, 0x50, 0, 0}, {0x3a13, 0x43, 0, 0}, + {0x3a18, 0x00, 0, 0}, {0x3a19, 0xf8, 0, 0}, {0x3635, 0x13, 0, 0}, + {0x3636, 0x03, 0, 0}, {0x3634, 0x40, 0, 0}, {0x3622, 0x01, 0, 0}, + {0x3c01, 0xa4, 0, 0}, {0x3c04, 0x28, 0, 0}, {0x3c05, 0x98, 0, 0}, + {0x3c06, 0x00, 0, 0}, {0x3c07, 0x08, 0, 0}, {0x3c08, 0x00, 0, 0}, + {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, + {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0}, + {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, + {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, + {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, + {0x3808, 0x02, 0, 0}, {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0}, + {0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0}, + {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0}, + {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, + {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, + {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, + {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0}, + {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, + {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, + {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x3000, 0x00, 0, 0}, + {0x3002, 0x1c, 0, 0}, {0x3004, 0xff, 0, 0}, {0x3006, 0xc3, 0, 0}, + {0x300e, 0x45, 0, 0}, {0x302e, 0x08, 0, 0}, {0x4300, 0x3f, 0, 0}, + {0x501f, 0x00, 0, 0}, {0x4713, 0x03, 0, 0}, {0x4407, 0x04, 0, 0}, + {0x440e, 0x00, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, + {0x4837, 0x0a, 0, 0}, {0x4800, 0x04, 0, 0}, {0x3824, 0x02, 0, 0}, + {0x5000, 0xa7, 0, 0}, {0x5001, 0xa3, 0, 0}, {0x5180, 0xff, 0, 0}, + {0x5181, 0xf2, 0, 0}, {0x5182, 0x00, 0, 0}, {0x5183, 0x14, 0, 0}, + {0x5184, 0x25, 0, 0}, {0x5185, 0x24, 0, 0}, {0x5186, 0x09, 0, 0}, + {0x5187, 0x09, 0, 0}, {0x5188, 0x09, 0, 0}, {0x5189, 0x88, 0, 0}, + {0x518a, 0x54, 0, 0}, {0x518b, 0xee, 0, 0}, {0x518c, 0xb2, 0, 0}, + {0x518d, 0x50, 0, 0}, {0x518e, 0x34, 0, 0}, {0x518f, 0x6b, 0, 0}, + {0x5190, 0x46, 0, 0}, {0x5191, 0xf8, 0, 0}, {0x5192, 0x04, 0, 0}, + {0x5193, 0x70, 0, 0}, {0x5194, 0xf0, 0, 0}, {0x5195, 0xf0, 0, 0}, + {0x5196, 0x03, 0, 0}, {0x5197, 0x01, 0, 0}, {0x5198, 0x04, 0, 0}, + {0x5199, 0x6c, 0, 0}, {0x519a, 0x04, 0, 0}, {0x519b, 0x00, 0, 0}, + {0x519c, 0x09, 0, 0}, {0x519d, 0x2b, 0, 0}, {0x519e, 0x38, 0, 0}, + {0x5381, 0x1e, 0, 0}, {0x5382, 0x5b, 0, 0}, {0x5383, 0x08, 0, 0}, + {0x5384, 0x0a, 0, 0}, {0x5385, 0x7e, 0, 0}, {0x5386, 0x88, 0, 0}, + {0x5387, 0x7c, 0, 0}, {0x5388, 0x6c, 0, 0}, {0x5389, 0x10, 0, 0}, + {0x538a, 0x01, 0, 0}, {0x538b, 0x98, 0, 0}, {0x5300, 0x08, 0, 0}, + {0x5301, 0x30, 0, 0}, {0x5302, 0x10, 0, 0}, {0x5303, 0x00, 0, 0}, + {0x5304, 0x08, 0, 0}, {0x5305, 0x30, 0, 0}, {0x5306, 0x08, 0, 0}, + {0x5307, 0x16, 0, 0}, {0x5309, 0x08, 0, 0}, {0x530a, 0x30, 0, 0}, + {0x530b, 0x04, 0, 0}, {0x530c, 0x06, 0, 0}, {0x5480, 0x01, 0, 0}, + {0x5481, 0x08, 0, 0}, {0x5482, 0x14, 0, 0}, {0x5483, 0x28, 0, 0}, + {0x5484, 0x51, 0, 0}, {0x5485, 0x65, 0, 0}, {0x5486, 0x71, 0, 0}, + {0x5487, 0x7d, 0, 0}, {0x5488, 0x87, 0, 0}, {0x5489, 0x91, 0, 0}, + {0x548a, 0x9a, 0, 0}, {0x548b, 0xaa, 0, 0}, {0x548c, 0xb8, 0, 0}, + {0x548d, 0xcd, 0, 0}, {0x548e, 0xdd, 0, 0}, {0x548f, 0xea, 0, 0}, + {0x5490, 0x1d, 0, 0}, {0x5580, 0x02, 0, 0}, {0x5583, 0x40, 0, 0}, + {0x5584, 0x10, 0, 0}, {0x5589, 0x10, 0, 0}, {0x558a, 0x00, 0, 0}, + {0x558b, 0xf8, 0, 0}, {0x5800, 0x23, 0, 0}, {0x5801, 0x14, 0, 0}, + {0x5802, 0x0f, 0, 0}, {0x5803, 0x0f, 0, 0}, {0x5804, 0x12, 0, 0}, + {0x5805, 0x26, 0, 0}, {0x5806, 0x0c, 0, 0}, {0x5807, 0x08, 0, 0}, + {0x5808, 0x05, 0, 0}, {0x5809, 0x05, 0, 0}, {0x580a, 0x08, 0, 0}, + {0x580b, 0x0d, 0, 0}, {0x580c, 0x08, 0, 0}, {0x580d, 0x03, 0, 0}, + {0x580e, 0x00, 0, 0}, {0x580f, 0x00, 0, 0}, {0x5810, 0x03, 0, 0}, + {0x5811, 0x09, 0, 0}, {0x5812, 0x07, 0, 0}, {0x5813, 0x03, 0, 0}, + {0x5814, 0x00, 0, 0}, {0x5815, 0x01, 0, 0}, {0x5816, 0x03, 0, 0}, + {0x5817, 0x08, 0, 0}, {0x5818, 0x0d, 0, 0}, {0x5819, 0x08, 0, 0}, + {0x581a, 0x05, 0, 0}, {0x581b, 0x06, 0, 0}, {0x581c, 0x08, 0, 0}, + {0x581d, 0x0e, 0, 0}, {0x581e, 0x29, 0, 0}, {0x581f, 0x17, 0, 0}, + {0x5820, 0x11, 0, 0}, {0x5821, 0x11, 0, 0}, {0x5822, 0x15, 0, 0}, + {0x5823, 0x28, 0, 0}, {0x5824, 0x46, 0, 0}, {0x5825, 0x26, 0, 0}, + {0x5826, 0x08, 0, 0}, {0x5827, 0x26, 0, 0}, {0x5828, 0x64, 0, 0}, + {0x5829, 0x26, 0, 0}, {0x582a, 0x24, 0, 0}, {0x582b, 0x22, 0, 0}, + {0x582c, 0x24, 0, 0}, {0x582d, 0x24, 0, 0}, {0x582e, 0x06, 0, 0}, + {0x582f, 0x22, 0, 0}, {0x5830, 0x40, 0, 0}, {0x5831, 0x42, 0, 0}, + {0x5832, 0x24, 0, 0}, {0x5833, 0x26, 0, 0}, {0x5834, 0x24, 0, 0}, + {0x5835, 0x22, 0, 0}, {0x5836, 0x22, 0, 0}, {0x5837, 0x26, 0, 0}, + {0x5838, 0x44, 0, 0}, {0x5839, 0x24, 0, 0}, {0x583a, 0x26, 0, 0}, + {0x583b, 0x28, 0, 0}, {0x583c, 0x42, 0, 0}, {0x583d, 0xce, 0, 0}, + {0x5025, 0x00, 0, 0}, {0x3a0f, 0x30, 0, 0}, {0x3a10, 0x28, 0, 0}, + {0x3a1b, 0x30, 0, 0}, {0x3a1e, 0x26, 0, 0}, {0x3a11, 0x60, 0, 0}, + {0x3a1f, 0x14, 0, 0}, {0x3008, 0x02, 0, 0}, {0x3c00, 0x04, 0, 300}, +}; + +static const struct reg_value ov5640_setting_30fps_VGA_640_480[] = { + + {0x3035, 0x14, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, + {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, + {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0}, + {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, + {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, + {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, + {0x3808, 0x02, 0, 0}, {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0}, + {0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0}, + {0x380e, 0x04, 0, 0}, {0x380f, 0x38, 0, 0}, {0x3810, 0x00, 0, 0}, + {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, + {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, + {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, + {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x0e, 0, 0}, + {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, + {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, + {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0}, + {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, + {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, {0x3503, 0x00, 0, 0}, +}; + +static const struct reg_value ov5640_setting_15fps_VGA_640_480[] = { + {0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, + {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, + {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0}, + {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, + {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, + {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, + {0x3808, 0x02, 0, 0}, {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0}, + {0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0}, + {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0}, + {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, + {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, + {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, + {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0}, + {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, + {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, + {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0}, + {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, + {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, +}; + +static const struct reg_value ov5640_setting_30fps_XGA_1024_768[] = { + + {0x3035, 0x14, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, + {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, + {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0}, + {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, + {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, + {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, + {0x3808, 0x02, 0, 0}, {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0}, + {0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0}, + {0x380e, 0x04, 0, 0}, {0x380f, 0x38, 0, 0}, {0x3810, 0x00, 0, 0}, + {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, + {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, + {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, + {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x0e, 0, 0}, + {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, + {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, + {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0}, + {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, + {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, {0x3503, 0x00, 0, 0}, + {0x3808, 0x04, 0, 0}, {0x3809, 0x00, 0, 0}, {0x380a, 0x03, 0, 0}, + {0x380b, 0x00, 0, 0}, {0x3035, 0x12, 0, 0}, +}; + +static const struct reg_value ov5640_setting_15fps_XGA_1024_768[] = { + {0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, + {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, + {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0}, + {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, + {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, + {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, + {0x3808, 0x02, 0, 0}, {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0}, + {0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0}, + {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0}, + {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, + {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, + {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, + {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0}, + {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, + {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, + {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0}, + {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, + {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, {0x3808, 0x04, 0, 0}, + {0x3809, 0x00, 0, 0}, {0x380a, 0x03, 0, 0}, {0x380b, 0x00, 0, 0}, +}; + +static const struct reg_value ov5640_setting_30fps_QVGA_320_240[] = { + {0x3035, 0x14, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, + {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, + {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0}, + {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, + {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, + {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, + {0x3808, 0x01, 0, 0}, {0x3809, 0x40, 0, 0}, {0x380a, 0x00, 0, 0}, + {0x380b, 0xf0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0}, + {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0}, + {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, + {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, + {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, + {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0}, + {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, + {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, + {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0}, + {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, + {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, +}; + +static const struct reg_value ov5640_setting_15fps_QVGA_320_240[] = { + {0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, + {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, + {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0}, + {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, + {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, + {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, + {0x3808, 0x01, 0, 0}, {0x3809, 0x40, 0, 0}, {0x380a, 0x00, 0, 0}, + {0x380b, 0xf0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0}, + {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0}, + {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, + {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, + {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, + {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0}, + {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, + {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, + {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0}, + {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, + {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, +}; + +static const struct reg_value ov5640_setting_30fps_QCIF_176_144[] = { + {0x3035, 0x14, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, + {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, + {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0}, + {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, + {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, + {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, + {0x3808, 0x00, 0, 0}, {0x3809, 0xb0, 0, 0}, {0x380a, 0x00, 0, 0}, + {0x380b, 0x90, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0}, + {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0}, + {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, + {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, + {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, + {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0}, + {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, + {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, + {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0}, + {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, + {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, +}; +static const struct reg_value ov5640_setting_15fps_QCIF_176_144[] = { + {0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, + {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, + {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0}, + {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, + {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, + {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, + {0x3808, 0x00, 0, 0}, {0x3809, 0xb0, 0, 0}, {0x380a, 0x00, 0, 0}, + {0x380b, 0x90, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0}, + {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0}, + {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, + {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, + {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, + {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0}, + {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, + {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, + {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0}, + {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, + {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, +}; + +static const struct reg_value ov5640_setting_30fps_NTSC_720_480[] = { + {0x3035, 0x12, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, + {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, + {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0}, + {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, + {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, + {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, + {0x3808, 0x02, 0, 0}, {0x3809, 0xd0, 0, 0}, {0x380a, 0x01, 0, 0}, + {0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0}, + {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0}, + {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x3c, 0, 0}, + {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, + {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, + {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0}, + {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, + {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, + {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0}, + {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, + {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, +}; + +static const struct reg_value ov5640_setting_15fps_NTSC_720_480[] = { + {0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, + {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, + {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0}, + {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, + {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, + {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, + {0x3808, 0x02, 0, 0}, {0x3809, 0xd0, 0, 0}, {0x380a, 0x01, 0, 0}, + {0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0}, + {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0}, + {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x3c, 0, 0}, + {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, + {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, + {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0}, + {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, + {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, + {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0}, + {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, + {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, +}; + +static const struct reg_value ov5640_setting_30fps_PAL_720_576[] = { + {0x3035, 0x12, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, + {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, + {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0}, + {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, + {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, + {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, + {0x3808, 0x02, 0, 0}, {0x3809, 0xd0, 0, 0}, {0x380a, 0x02, 0, 0}, + {0x380b, 0x40, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0}, + {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0}, + {0x3811, 0x38, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, + {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, + {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, + {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0}, + {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, + {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, + {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0}, + {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, + {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, +}; + +static const struct reg_value ov5640_setting_15fps_PAL_720_576[] = { + {0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, + {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, + {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0}, + {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, + {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, + {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, + {0x3808, 0x02, 0, 0}, {0x3809, 0xd0, 0, 0}, {0x380a, 0x02, 0, 0}, + {0x380b, 0x40, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0}, + {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0}, + {0x3811, 0x38, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, + {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, + {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, + {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0}, + {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, + {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, + {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0}, + {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, + {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, +}; + +static const struct reg_value ov5640_setting_30fps_720P_1280_720[] = { + {0x3008, 0x42, 0, 0}, + {0x3035, 0x21, 0, 0}, {0x3036, 0x54, 0, 0}, {0x3c07, 0x07, 0, 0}, + {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, + {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0}, + {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, + {0x3802, 0x00, 0, 0}, {0x3803, 0xfa, 0, 0}, {0x3804, 0x0a, 0, 0}, + {0x3805, 0x3f, 0, 0}, {0x3806, 0x06, 0, 0}, {0x3807, 0xa9, 0, 0}, + {0x3808, 0x05, 0, 0}, {0x3809, 0x00, 0, 0}, {0x380a, 0x02, 0, 0}, + {0x380b, 0xd0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x64, 0, 0}, + {0x380e, 0x02, 0, 0}, {0x380f, 0xe4, 0, 0}, {0x3810, 0x00, 0, 0}, + {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0}, + {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, + {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x02, 0, 0}, + {0x3a03, 0xe4, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0xbc, 0, 0}, + {0x3a0a, 0x01, 0, 0}, {0x3a0b, 0x72, 0, 0}, {0x3a0e, 0x01, 0, 0}, + {0x3a0d, 0x02, 0, 0}, {0x3a14, 0x02, 0, 0}, {0x3a15, 0xe4, 0, 0}, + {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x02, 0, 0}, + {0x4407, 0x04, 0, 0}, {0x460b, 0x37, 0, 0}, {0x460c, 0x20, 0, 0}, + {0x3824, 0x04, 0, 0}, {0x5001, 0x83, 0, 0}, {0x4005, 0x1a, 0, 0}, + {0x3008, 0x02, 0, 0}, {0x3503, 0, 0, 0}, +}; + +static const struct reg_value ov5640_setting_15fps_720P_1280_720[] = { + {0x3035, 0x41, 0, 0}, {0x3036, 0x54, 0, 0}, {0x3c07, 0x07, 0, 0}, + {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, + {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0}, + {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, + {0x3802, 0x00, 0, 0}, {0x3803, 0xfa, 0, 0}, {0x3804, 0x0a, 0, 0}, + {0x3805, 0x3f, 0, 0}, {0x3806, 0x06, 0, 0}, {0x3807, 0xa9, 0, 0}, + {0x3808, 0x05, 0, 0}, {0x3809, 0x00, 0, 0}, {0x380a, 0x02, 0, 0}, + {0x380b, 0xd0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x64, 0, 0}, + {0x380e, 0x02, 0, 0}, {0x380f, 0xe4, 0, 0}, {0x3810, 0x00, 0, 0}, + {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0}, + {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, + {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x02, 0, 0}, + {0x3a03, 0xe4, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0xbc, 0, 0}, + {0x3a0a, 0x01, 0, 0}, {0x3a0b, 0x72, 0, 0}, {0x3a0e, 0x01, 0, 0}, + {0x3a0d, 0x02, 0, 0}, {0x3a14, 0x02, 0, 0}, {0x3a15, 0xe4, 0, 0}, + {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x02, 0, 0}, + {0x4407, 0x04, 0, 0}, {0x460b, 0x37, 0, 0}, {0x460c, 0x20, 0, 0}, + {0x3824, 0x04, 0, 0}, {0x5001, 0x83, 0, 0}, +}; + +static const struct reg_value ov5640_setting_30fps_1080P_1920_1080[] = { + {0x3008, 0x42, 0, 0}, + {0x3035, 0x21, 0, 0}, {0x3036, 0x54, 0, 0}, {0x3c07, 0x08, 0, 0}, + {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, + {0x3820, 0x40, 0, 0}, {0x3821, 0x06, 0, 0}, {0x3814, 0x11, 0, 0}, + {0x3815, 0x11, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, + {0x3802, 0x00, 0, 0}, {0x3803, 0x00, 0, 0}, {0x3804, 0x0a, 0, 0}, + {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9f, 0, 0}, + {0x3808, 0x0a, 0, 0}, {0x3809, 0x20, 0, 0}, {0x380a, 0x07, 0, 0}, + {0x380b, 0x98, 0, 0}, {0x380c, 0x0b, 0, 0}, {0x380d, 0x1c, 0, 0}, + {0x380e, 0x07, 0, 0}, {0x380f, 0xb0, 0, 0}, {0x3810, 0x00, 0, 0}, + {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0}, + {0x3618, 0x04, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x21, 0, 0}, + {0x3709, 0x12, 0, 0}, {0x370c, 0x00, 0, 0}, {0x3a02, 0x03, 0, 0}, + {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0}, + {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, + {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, + {0x4001, 0x02, 0, 0}, {0x4004, 0x06, 0, 0}, {0x4713, 0x03, 0, 0}, + {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, + {0x3824, 0x02, 0, 0}, {0x5001, 0x83, 0, 0}, {0x3035, 0x11, 0, 0}, + {0x3036, 0x54, 0, 0}, {0x3c07, 0x07, 0, 0}, {0x3c08, 0x00, 0, 0}, + {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, + {0x3800, 0x01, 0, 0}, {0x3801, 0x50, 0, 0}, {0x3802, 0x01, 0, 0}, + {0x3803, 0xb2, 0, 0}, {0x3804, 0x08, 0, 0}, {0x3805, 0xef, 0, 0}, + {0x3806, 0x05, 0, 0}, {0x3807, 0xf1, 0, 0}, {0x3808, 0x07, 0, 0}, + {0x3809, 0x80, 0, 0}, {0x380a, 0x04, 0, 0}, {0x380b, 0x38, 0, 0}, + {0x380c, 0x09, 0, 0}, {0x380d, 0xc4, 0, 0}, {0x380e, 0x04, 0, 0}, + {0x380f, 0x60, 0, 0}, {0x3612, 0x2b, 0, 0}, {0x3708, 0x64, 0, 0}, + {0x3a02, 0x04, 0, 0}, {0x3a03, 0x60, 0, 0}, {0x3a08, 0x01, 0, 0}, + {0x3a09, 0x50, 0, 0}, {0x3a0a, 0x01, 0, 0}, {0x3a0b, 0x18, 0, 0}, + {0x3a0e, 0x03, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x04, 0, 0}, + {0x3a15, 0x60, 0, 0}, {0x4713, 0x02, 0, 0}, {0x4407, 0x04, 0, 0}, + {0x460b, 0x37, 0, 0}, {0x460c, 0x20, 0, 0}, {0x3824, 0x04, 0, 0}, + {0x4005, 0x1a, 0, 0}, {0x3008, 0x02, 0, 0}, + {0x3503, 0, 0, 0}, +}; + +static const struct reg_value ov5640_setting_15fps_1080P_1920_1080[] = { + {0x3008, 0x42, 0, 0}, + {0x3035, 0x21, 0, 0}, {0x3036, 0x54, 0, 0}, {0x3c07, 0x08, 0, 0}, + {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, + {0x3820, 0x40, 0, 0}, {0x3821, 0x06, 0, 0}, {0x3814, 0x11, 0, 0}, + {0x3815, 0x11, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, + {0x3802, 0x00, 0, 0}, {0x3803, 0x00, 0, 0}, {0x3804, 0x0a, 0, 0}, + {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9f, 0, 0}, + {0x3808, 0x0a, 0, 0}, {0x3809, 0x20, 0, 0}, {0x380a, 0x07, 0, 0}, + {0x380b, 0x98, 0, 0}, {0x380c, 0x0b, 0, 0}, {0x380d, 0x1c, 0, 0}, + {0x380e, 0x07, 0, 0}, {0x380f, 0xb0, 0, 0}, {0x3810, 0x00, 0, 0}, + {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0}, + {0x3618, 0x04, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x21, 0, 0}, + {0x3709, 0x12, 0, 0}, {0x370c, 0x00, 0, 0}, {0x3a02, 0x03, 0, 0}, + {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0}, + {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, + {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, + {0x4001, 0x02, 0, 0}, {0x4004, 0x06, 0, 0}, {0x4713, 0x03, 0, 0}, + {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, + {0x3824, 0x02, 0, 0}, {0x5001, 0x83, 0, 0}, {0x3035, 0x21, 0, 0}, + {0x3036, 0x54, 0, 1}, {0x3c07, 0x07, 0, 0}, {0x3c08, 0x00, 0, 0}, + {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, + {0x3800, 0x01, 0, 0}, {0x3801, 0x50, 0, 0}, {0x3802, 0x01, 0, 0}, + {0x3803, 0xb2, 0, 0}, {0x3804, 0x08, 0, 0}, {0x3805, 0xef, 0, 0}, + {0x3806, 0x05, 0, 0}, {0x3807, 0xf1, 0, 0}, {0x3808, 0x07, 0, 0}, + {0x3809, 0x80, 0, 0}, {0x380a, 0x04, 0, 0}, {0x380b, 0x38, 0, 0}, + {0x380c, 0x09, 0, 0}, {0x380d, 0xc4, 0, 0}, {0x380e, 0x04, 0, 0}, + {0x380f, 0x60, 0, 0}, {0x3612, 0x2b, 0, 0}, {0x3708, 0x64, 0, 0}, + {0x3a02, 0x04, 0, 0}, {0x3a03, 0x60, 0, 0}, {0x3a08, 0x01, 0, 0}, + {0x3a09, 0x50, 0, 0}, {0x3a0a, 0x01, 0, 0}, {0x3a0b, 0x18, 0, 0}, + {0x3a0e, 0x03, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x04, 0, 0}, + {0x3a15, 0x60, 0, 0}, {0x4713, 0x02, 0, 0}, {0x4407, 0x04, 0, 0}, + {0x460b, 0x37, 0, 0}, {0x460c, 0x20, 0, 0}, {0x3824, 0x04, 0, 0}, + {0x4005, 0x1a, 0, 0}, {0x3008, 0x02, 0, 0}, {0x3503, 0, 0, 0}, +}; + +static const struct reg_value ov5640_setting_15fps_QSXGA_2592_1944[] = { + {0x3820, 0x40, 0, 0}, {0x3821, 0x06, 0, 0}, + {0x3035, 0x21, 0, 0}, {0x3036, 0x54, 0, 0}, {0x3c07, 0x08, 0, 0}, + {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, + {0x3820, 0x40, 0, 0}, {0x3821, 0x06, 0, 0}, {0x3814, 0x11, 0, 0}, + {0x3815, 0x11, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, + {0x3802, 0x00, 0, 0}, {0x3803, 0x00, 0, 0}, {0x3804, 0x0a, 0, 0}, + {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9f, 0, 0}, + {0x3808, 0x0a, 0, 0}, {0x3809, 0x20, 0, 0}, {0x380a, 0x07, 0, 0}, + {0x380b, 0x98, 0, 0}, {0x380c, 0x0b, 0, 0}, {0x380d, 0x1c, 0, 0}, + {0x380e, 0x07, 0, 0}, {0x380f, 0xb0, 0, 0}, {0x3810, 0x00, 0, 0}, + {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0}, + {0x3618, 0x04, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x21, 0, 0}, + {0x3709, 0x12, 0, 0}, {0x370c, 0x00, 0, 0}, {0x3a02, 0x03, 0, 0}, + {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0}, + {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, + {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, + {0x4001, 0x02, 0, 0}, {0x4004, 0x06, 0, 0}, {0x4713, 0x03, 0, 0}, + {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, + {0x3824, 0x02, 0, 0}, {0x5001, 0x83, 0, 70}, +}; + +/* power-on sensor init reg table */ +static const struct ov5640_mode_info ov5640_mode_init_data = { + 0, SUBSAMPLING, 640, 480, ov5640_init_setting_30fps_VGA, + ARRAY_SIZE(ov5640_init_setting_30fps_VGA), +}; + +static const struct ov5640_mode_info +ov5640_mode_data[OV5640_NUM_FRAMERATES][OV5640_NUM_MODES] = { + { + {OV5640_MODE_QCIF_176_144, SUBSAMPLING, 176, 144, + ov5640_setting_15fps_QCIF_176_144, + ARRAY_SIZE(ov5640_setting_15fps_QCIF_176_144)}, + {OV5640_MODE_QVGA_320_240, SUBSAMPLING, 320, 240, + ov5640_setting_15fps_QVGA_320_240, + ARRAY_SIZE(ov5640_setting_15fps_QVGA_320_240)}, + {OV5640_MODE_VGA_640_480, SUBSAMPLING, 640, 480, + ov5640_setting_15fps_VGA_640_480, + ARRAY_SIZE(ov5640_setting_15fps_VGA_640_480)}, + {OV5640_MODE_NTSC_720_480, SUBSAMPLING, 720, 480, + ov5640_setting_15fps_NTSC_720_480, + ARRAY_SIZE(ov5640_setting_15fps_NTSC_720_480)}, + {OV5640_MODE_PAL_720_576, SUBSAMPLING, 720, 576, + ov5640_setting_15fps_PAL_720_576, + ARRAY_SIZE(ov5640_setting_15fps_PAL_720_576)}, + {OV5640_MODE_XGA_1024_768, SUBSAMPLING, 1024, 768, + ov5640_setting_15fps_XGA_1024_768, + ARRAY_SIZE(ov5640_setting_15fps_XGA_1024_768)}, + {OV5640_MODE_720P_1280_720, SUBSAMPLING, 1280, 720, + ov5640_setting_15fps_720P_1280_720, + ARRAY_SIZE(ov5640_setting_15fps_720P_1280_720)}, + {OV5640_MODE_1080P_1920_1080, SCALING, 1920, 1080, + ov5640_setting_15fps_1080P_1920_1080, + ARRAY_SIZE(ov5640_setting_15fps_1080P_1920_1080)}, + {OV5640_MODE_QSXGA_2592_1944, SCALING, 2592, 1944, + ov5640_setting_15fps_QSXGA_2592_1944, + ARRAY_SIZE(ov5640_setting_15fps_QSXGA_2592_1944)}, + }, { + {OV5640_MODE_QCIF_176_144, SUBSAMPLING, 176, 144, + ov5640_setting_30fps_QCIF_176_144, + ARRAY_SIZE(ov5640_setting_30fps_QCIF_176_144)}, + {OV5640_MODE_QVGA_320_240, SUBSAMPLING, 320, 240, + ov5640_setting_30fps_QVGA_320_240, + ARRAY_SIZE(ov5640_setting_30fps_QVGA_320_240)}, + {OV5640_MODE_VGA_640_480, SUBSAMPLING, 640, 480, + ov5640_setting_30fps_VGA_640_480, + ARRAY_SIZE(ov5640_setting_30fps_VGA_640_480)}, + {OV5640_MODE_NTSC_720_480, SUBSAMPLING, 720, 480, + ov5640_setting_30fps_NTSC_720_480, + ARRAY_SIZE(ov5640_setting_30fps_NTSC_720_480)}, + {OV5640_MODE_PAL_720_576, SUBSAMPLING, 720, 576, + ov5640_setting_30fps_PAL_720_576, + ARRAY_SIZE(ov5640_setting_30fps_PAL_720_576)}, + {OV5640_MODE_XGA_1024_768, SUBSAMPLING, 1024, 768, + ov5640_setting_30fps_XGA_1024_768, + ARRAY_SIZE(ov5640_setting_30fps_XGA_1024_768)}, + {OV5640_MODE_720P_1280_720, SUBSAMPLING, 1280, 720, + ov5640_setting_30fps_720P_1280_720, + ARRAY_SIZE(ov5640_setting_30fps_720P_1280_720)}, + {OV5640_MODE_1080P_1920_1080, SCALING, 1920, 1080, + ov5640_setting_30fps_1080P_1920_1080, + ARRAY_SIZE(ov5640_setting_30fps_1080P_1920_1080)}, + {OV5640_MODE_QSXGA_2592_1944, -1, 0, 0, NULL, 0}, + }, +}; + +static int ov5640_init_slave_id(struct ov5640_dev *sensor) +{ + struct i2c_client *client = sensor->i2c_client; + struct i2c_msg msg; + u8 buf[3]; + int ret; + + if (client->addr == OV5640_DEFAULT_SLAVE_ID) + return 0; + + buf[0] = OV5640_REG_SLAVE_ID >> 8; + buf[1] = OV5640_REG_SLAVE_ID & 0xff; + buf[2] = client->addr << 1; + + msg.addr = OV5640_DEFAULT_SLAVE_ID; + msg.flags = 0; + msg.buf = buf; + msg.len = sizeof(buf); + + ret = i2c_transfer(client->adapter, &msg, 1); + if (ret < 0) { + dev_err(&client->dev, "%s: failed with %d\n", __func__, ret); + return ret; + } + + return 0; +} + +static int ov5640_write_reg(struct ov5640_dev *sensor, u16 reg, u8 val) +{ + struct i2c_client *client = sensor->i2c_client; + struct i2c_msg msg; + u8 buf[3]; + int ret; + + buf[0] = reg >> 8; + buf[1] = reg & 0xff; + buf[2] = val; + + msg.addr = client->addr; + msg.flags = client->flags; + msg.buf = buf; + msg.len = sizeof(buf); + + ret = i2c_transfer(client->adapter, &msg, 1); + if (ret < 0) { + v4l2_err(&sensor->sd, "%s: error: reg=%x, val=%x\n", + __func__, reg, val); + return ret; + } + + return 0; +} + +static int ov5640_read_reg(struct ov5640_dev *sensor, u16 reg, u8 *val) +{ + struct i2c_client *client = sensor->i2c_client; + struct i2c_msg msg[2]; + u8 buf[2]; + int ret; + + buf[0] = reg >> 8; + buf[1] = reg & 0xff; + + msg[0].addr = client->addr; + msg[0].flags = client->flags; + msg[0].buf = buf; + msg[0].len = sizeof(buf); + + msg[1].addr = client->addr; + msg[1].flags = client->flags | I2C_M_RD; + msg[1].buf = buf; + msg[1].len = 1; + + ret = i2c_transfer(client->adapter, msg, 2); + if (ret < 0) + return ret; + + *val = buf[0]; + return 0; +} + +static int ov5640_read_reg16(struct ov5640_dev *sensor, u16 reg, u16 *val) +{ + u8 hi, lo; + int ret; + + ret = ov5640_read_reg(sensor, reg, &hi); + if (ret) + return ret; + ret = ov5640_read_reg(sensor, reg+1, &lo); + if (ret) + return ret; + + *val = ((u16)hi << 8) | (u16)lo; + return 0; +} + +static int ov5640_write_reg16(struct ov5640_dev *sensor, u16 reg, u16 val) +{ + int ret; + + ret = ov5640_write_reg(sensor, reg, val >> 8); + if (ret) + return ret; + + return ov5640_write_reg(sensor, reg + 1, val & 0xff); +} + +static int ov5640_mod_reg(struct ov5640_dev *sensor, u16 reg, + u8 mask, u8 val) +{ + u8 readval; + int ret; + + ret = ov5640_read_reg(sensor, reg, &readval); + if (ret) + return ret; + + readval &= ~mask; + val &= mask; + val |= readval; + + return ov5640_write_reg(sensor, reg, val); +} + +/* download ov5640 settings to sensor through i2c */ +static int ov5640_load_regs(struct ov5640_dev *sensor, + const struct ov5640_mode_info *mode) +{ + const struct reg_value *regs = mode->reg_data; + unsigned int i; + u32 delay_ms; + u16 reg_addr; + u8 mask, val; + int ret = 0; + + for (i = 0; i < mode->reg_data_size; ++i, ++regs) { + delay_ms = regs->delay_ms; + reg_addr = regs->reg_addr; + val = regs->val; + mask = regs->mask; + + if (mask) + ret = ov5640_mod_reg(sensor, reg_addr, mask, val); + else + ret = ov5640_write_reg(sensor, reg_addr, val); + if (ret) + break; + + if (delay_ms) + usleep_range(1000*delay_ms, 1000*delay_ms+100); + } + + return ret; +} + +/* read exposure, in number of line periods */ +static int ov5640_get_exposure(struct ov5640_dev *sensor) +{ + int exp, ret; + u8 temp; + + ret = ov5640_read_reg(sensor, OV5640_REG_AEC_PK_EXPOSURE_HI, &temp); + if (ret) + return ret; + exp = ((int)temp & 0x0f) << 16; + ret = ov5640_read_reg(sensor, OV5640_REG_AEC_PK_EXPOSURE_MED, &temp); + if (ret) + return ret; + exp |= ((int)temp << 8); + ret = ov5640_read_reg(sensor, OV5640_REG_AEC_PK_EXPOSURE_LO, &temp); + if (ret) + return ret; + exp |= (int)temp; + + return exp >> 4; +} + +/* write exposure, given number of line periods */ +static int ov5640_set_exposure(struct ov5640_dev *sensor, u32 exposure) +{ + int ret; + + exposure <<= 4; + + ret = ov5640_write_reg(sensor, + OV5640_REG_AEC_PK_EXPOSURE_LO, + exposure & 0xff); + if (ret) + return ret; + ret = ov5640_write_reg(sensor, + OV5640_REG_AEC_PK_EXPOSURE_MED, + (exposure >> 8) & 0xff); + if (ret) + return ret; + return ov5640_write_reg(sensor, + OV5640_REG_AEC_PK_EXPOSURE_HI, + (exposure >> 16) & 0x0f); +} + +static int ov5640_get_gain(struct ov5640_dev *sensor) +{ + u16 gain; + int ret; + + ret = ov5640_read_reg16(sensor, OV5640_REG_AEC_PK_REAL_GAIN, &gain); + if (ret) + return ret; + + return gain & 0x3ff; +} + +static int ov5640_set_stream(struct ov5640_dev *sensor, bool on) +{ + int ret; + + ret = ov5640_mod_reg(sensor, OV5640_REG_MIPI_CTRL00, BIT(5), + on ? 0 : BIT(5)); + if (ret) + return ret; + ret = ov5640_write_reg(sensor, OV5640_REG_PAD_OUTPUT00, + on ? 0x00 : 0x70); + if (ret) + return ret; + + return ov5640_write_reg(sensor, OV5640_REG_FRAME_CTRL01, + on ? 0x00 : 0x0f); +} + +static int ov5640_get_sysclk(struct ov5640_dev *sensor) +{ + /* calculate sysclk */ + u32 xvclk = sensor->xclk_freq / 10000; + u32 multiplier, prediv, VCO, sysdiv, pll_rdiv; + u32 sclk_rdiv_map[] = {1, 2, 4, 8}; + u32 bit_div2x = 1, sclk_rdiv, sysclk; + u8 temp1, temp2; + int ret; + + ret = ov5640_read_reg(sensor, OV5640_REG_SC_PLL_CTRL0, &temp1); + if (ret) + return ret; + temp2 = temp1 & 0x0f; + if (temp2 == 8 || temp2 == 10) + bit_div2x = temp2 / 2; + + ret = ov5640_read_reg(sensor, OV5640_REG_SC_PLL_CTRL1, &temp1); + if (ret) + return ret; + sysdiv = temp1 >> 4; + if (sysdiv == 0) + sysdiv = 16; + + ret = ov5640_read_reg(sensor, OV5640_REG_SC_PLL_CTRL2, &temp1); + if (ret) + return ret; + multiplier = temp1; + + ret = ov5640_read_reg(sensor, OV5640_REG_SC_PLL_CTRL3, &temp1); + if (ret) + return ret; + prediv = temp1 & 0x0f; + pll_rdiv = ((temp1 >> 4) & 0x01) + 1; + + ret = ov5640_read_reg(sensor, OV5640_REG_SYS_ROOT_DIVIDER, &temp1); + if (ret) + return ret; + temp2 = temp1 & 0x03; + sclk_rdiv = sclk_rdiv_map[temp2]; + + if (!prediv || !sysdiv || !pll_rdiv || !bit_div2x) + return -EINVAL; + + VCO = xvclk * multiplier / prediv; + + sysclk = VCO / sysdiv / pll_rdiv * 2 / bit_div2x / sclk_rdiv; + + return sysclk; +} + +static int ov5640_set_night_mode(struct ov5640_dev *sensor) +{ + /* read HTS from register settings */ + u8 mode; + int ret; + + ret = ov5640_read_reg(sensor, OV5640_REG_AEC_CTRL00, &mode); + if (ret) + return ret; + mode &= 0xfb; + return ov5640_write_reg(sensor, OV5640_REG_AEC_CTRL00, mode); +} + +static int ov5640_get_hts(struct ov5640_dev *sensor) +{ + /* read HTS from register settings */ + u16 hts; + int ret; + + ret = ov5640_read_reg16(sensor, OV5640_REG_TIMING_HTS, &hts); + if (ret) + return ret; + return hts; +} + +static int ov5640_get_vts(struct ov5640_dev *sensor) +{ + u16 vts; + int ret; + + ret = ov5640_read_reg16(sensor, OV5640_REG_TIMING_VTS, &vts); + if (ret) + return ret; + return vts; +} + +static int ov5640_set_vts(struct ov5640_dev *sensor, int vts) +{ + return ov5640_write_reg16(sensor, OV5640_REG_TIMING_VTS, vts); +} + +static int ov5640_get_light_freq(struct ov5640_dev *sensor) +{ + /* get banding filter value */ + int ret, light_freq = 0; + u8 temp, temp1; + + ret = ov5640_read_reg(sensor, OV5640_REG_HZ5060_CTRL01, &temp); + if (ret) + return ret; + + if (temp & 0x80) { + /* manual */ + ret = ov5640_read_reg(sensor, OV5640_REG_HZ5060_CTRL00, + &temp1); + if (ret) + return ret; + if (temp1 & 0x04) { + /* 50Hz */ + light_freq = 50; + } else { + /* 60Hz */ + light_freq = 60; + } + } else { + /* auto */ + ret = ov5640_read_reg(sensor, OV5640_REG_SIGMADELTA_CTRL0C, + &temp1); + if (ret) + return ret; + + if (temp1 & 0x01) { + /* 50Hz */ + light_freq = 50; + } else { + /* 60Hz */ + } + } + + return light_freq; +} + +static int ov5640_set_bandingfilter(struct ov5640_dev *sensor) +{ + u32 band_step60, max_band60, band_step50, max_band50, prev_vts; + int ret; + + /* read preview PCLK */ + ret = ov5640_get_sysclk(sensor); + if (ret < 0) + return ret; + if (ret == 0) + return -EINVAL; + sensor->prev_sysclk = ret; + /* read preview HTS */ + ret = ov5640_get_hts(sensor); + if (ret < 0) + return ret; + if (ret == 0) + return -EINVAL; + sensor->prev_hts = ret; + + /* read preview VTS */ + ret = ov5640_get_vts(sensor); + if (ret < 0) + return ret; + prev_vts = ret; + + + /* calculate banding filter */ + /* 60Hz */ + band_step60 = sensor->prev_sysclk * 100 / sensor->prev_hts * 100 / 120; + ret = ov5640_write_reg16(sensor, OV5640_REG_AEC_B60_STEP, band_step60); + if (ret) + return ret; + if (!band_step60) + return -EINVAL; + max_band60 = (int)((prev_vts - 4) / band_step60); + ret = ov5640_write_reg(sensor, OV5640_REG_AEC_CTRL0D, max_band60); + if (ret) + return ret; + + /* 50Hz */ + band_step50 = sensor->prev_sysclk * 100 / sensor->prev_hts; + ret = ov5640_write_reg16(sensor, OV5640_REG_AEC_B50_STEP, band_step50); + if (ret) + return ret; + if (!band_step50) + return -EINVAL; + max_band50 = (int)((prev_vts - 4) / band_step50); + return ov5640_write_reg(sensor, OV5640_REG_AEC_CTRL0E, max_band50); +} + +static int ov5640_set_ae_target(struct ov5640_dev *sensor, int target) +{ + /* stable in high */ + u32 fast_high, fast_low; + int ret; + + sensor->ae_low = target * 23 / 25; /* 0.92 */ + sensor->ae_high = target * 27 / 25; /* 1.08 */ + + fast_high = sensor->ae_high << 1; + if (fast_high > 255) + fast_high = 255; + + fast_low = sensor->ae_low >> 1; + + ret = ov5640_write_reg(sensor, OV5640_REG_AEC_CTRL0F, sensor->ae_high); + if (ret) + return ret; + ret = ov5640_write_reg(sensor, OV5640_REG_AEC_CTRL10, sensor->ae_low); + if (ret) + return ret; + ret = ov5640_write_reg(sensor, OV5640_REG_AEC_CTRL1B, sensor->ae_high); + if (ret) + return ret; + ret = ov5640_write_reg(sensor, OV5640_REG_AEC_CTRL1E, sensor->ae_low); + if (ret) + return ret; + ret = ov5640_write_reg(sensor, OV5640_REG_AEC_CTRL11, fast_high); + if (ret) + return ret; + return ov5640_write_reg(sensor, OV5640_REG_AEC_CTRL1F, fast_low); +} + +static int ov5640_binning_on(struct ov5640_dev *sensor) +{ + u8 temp; + int ret; + + ret = ov5640_read_reg(sensor, OV5640_REG_TIMING_TC_REG21, &temp); + if (ret) + return ret; + temp &= 0xfe; + return temp ? 1 : 0; +} + +static int ov5640_set_virtual_channel(struct ov5640_dev *sensor) +{ + u8 temp, channel = virtual_channel; + int ret; + + if (channel > 3) + return -EINVAL; + + ret = ov5640_read_reg(sensor, OV5640_REG_DEBUG_MODE, &temp); + if (ret) + return ret; + temp &= ~(3 << 6); + temp |= (channel << 6); + return ov5640_write_reg(sensor, OV5640_REG_DEBUG_MODE, temp); +} + +static const struct ov5640_mode_info * +ov5640_find_mode(struct ov5640_dev *sensor, enum ov5640_frame_rate fr, + int width, int height, bool nearest) +{ + const struct ov5640_mode_info *mode = NULL; + int i; + + for (i = OV5640_NUM_MODES - 1; i >= 0; i--) { + mode = &ov5640_mode_data[fr][i]; + + if (!mode->reg_data) + continue; + + if ((nearest && mode->width <= width && + mode->height <= height) || + (!nearest && mode->width == width && + mode->height == height)) + break; + } + + if (nearest && i < 0) + mode = &ov5640_mode_data[fr][0]; + + return mode; +} + +/* + * sensor changes between scaling and subsampling, go through + * exposure calculation + */ +static int ov5640_set_mode_exposure_calc( + struct ov5640_dev *sensor, const struct ov5640_mode_info *mode) +{ + u32 prev_shutter, prev_gain16; + u32 cap_shutter, cap_gain16; + u32 cap_sysclk, cap_hts, cap_vts; + u32 light_freq, cap_bandfilt, cap_maxband; + u32 cap_gain16_shutter; + u8 average; + int ret; + + if (mode->reg_data == NULL) + return -EINVAL; + + /* read preview shutter */ + ret = ov5640_get_exposure(sensor); + if (ret < 0) + return ret; + prev_shutter = ret; + ret = ov5640_binning_on(sensor); + if (ret < 0) + return ret; + if (ret && mode->id != OV5640_MODE_720P_1280_720 && + mode->id != OV5640_MODE_1080P_1920_1080) + prev_shutter *= 2; + + /* read preview gain */ + ret = ov5640_get_gain(sensor); + if (ret < 0) + return ret; + prev_gain16 = ret; + + /* get average */ + ret = ov5640_read_reg(sensor, OV5640_REG_AVG_READOUT, &average); + if (ret) + return ret; + + /* turn off night mode for capture */ + ret = ov5640_set_night_mode(sensor); + if (ret < 0) + return ret; + + /* Write capture setting */ + ret = ov5640_load_regs(sensor, mode); + if (ret < 0) + return ret; + + /* read capture VTS */ + ret = ov5640_get_vts(sensor); + if (ret < 0) + return ret; + cap_vts = ret; + ret = ov5640_get_hts(sensor); + if (ret < 0) + return ret; + if (ret == 0) + return -EINVAL; + cap_hts = ret; + + ret = ov5640_get_sysclk(sensor); + if (ret < 0) + return ret; + if (ret == 0) + return -EINVAL; + cap_sysclk = ret; + + /* calculate capture banding filter */ + ret = ov5640_get_light_freq(sensor); + if (ret < 0) + return ret; + light_freq = ret; + + if (light_freq == 60) { + /* 60Hz */ + cap_bandfilt = cap_sysclk * 100 / cap_hts * 100 / 120; + } else { + /* 50Hz */ + cap_bandfilt = cap_sysclk * 100 / cap_hts; + } + + if (!sensor->prev_sysclk) { + ret = ov5640_get_sysclk(sensor); + if (ret < 0) + return ret; + if (ret == 0) + return -EINVAL; + sensor->prev_sysclk = ret; + } + + if (!cap_bandfilt) + return -EINVAL; + + cap_maxband = (int)((cap_vts - 4) / cap_bandfilt); + + /* calculate capture shutter/gain16 */ + if (average > sensor->ae_low && average < sensor->ae_high) { + /* in stable range */ + cap_gain16_shutter = + prev_gain16 * prev_shutter * + cap_sysclk / sensor->prev_sysclk * + sensor->prev_hts / cap_hts * + sensor->ae_target / average; + } else { + cap_gain16_shutter = + prev_gain16 * prev_shutter * + cap_sysclk / sensor->prev_sysclk * + sensor->prev_hts / cap_hts; + } + + /* gain to shutter */ + if (cap_gain16_shutter < (cap_bandfilt * 16)) { + /* shutter < 1/100 */ + cap_shutter = cap_gain16_shutter / 16; + if (cap_shutter < 1) + cap_shutter = 1; + + cap_gain16 = cap_gain16_shutter / cap_shutter; + if (cap_gain16 < 16) + cap_gain16 = 16; + } else { + if (cap_gain16_shutter > (cap_bandfilt * cap_maxband * 16)) { + /* exposure reach max */ + cap_shutter = cap_bandfilt * cap_maxband; + if (!cap_shutter) + return -EINVAL; + + cap_gain16 = cap_gain16_shutter / cap_shutter; + } else { + /* 1/100 < (cap_shutter = n/100) =< max */ + cap_shutter = + ((int)(cap_gain16_shutter / 16 / cap_bandfilt)) + * cap_bandfilt; + if (!cap_shutter) + return -EINVAL; + + cap_gain16 = cap_gain16_shutter / cap_shutter; + } + } + + /* set capture gain */ + ret = __v4l2_ctrl_s_ctrl(sensor->ctrls.gain, cap_gain16); + if (ret) + return ret; + + /* write capture shutter */ + if (cap_shutter > (cap_vts - 4)) { + cap_vts = cap_shutter + 4; + ret = ov5640_set_vts(sensor, cap_vts); + if (ret < 0) + return ret; + } + + /* set exposure */ + return __v4l2_ctrl_s_ctrl(sensor->ctrls.exposure, cap_shutter); +} + +/* + * if sensor changes inside scaling or subsampling + * change mode directly + */ +static int ov5640_set_mode_direct(struct ov5640_dev *sensor, + const struct ov5640_mode_info *mode) +{ + int ret; + + if (mode->reg_data == NULL) + return -EINVAL; + + /* Write capture setting */ + ret = ov5640_load_regs(sensor, mode); + if (ret < 0) + return ret; + + /* turn auto gain/exposure back on for direct mode */ + ret = __v4l2_ctrl_s_ctrl(sensor->ctrls.auto_gain, 1); + if (ret) + return ret; + return __v4l2_ctrl_s_ctrl(sensor->ctrls.auto_exp, V4L2_EXPOSURE_AUTO); +} + +static int ov5640_set_mode(struct ov5640_dev *sensor, + const struct ov5640_mode_info *orig_mode) +{ + const struct ov5640_mode_info *mode = sensor->current_mode; + enum ov5640_downsize_mode dn_mode, orig_dn_mode; + int ret; + + dn_mode = mode->dn_mode; + orig_dn_mode = orig_mode->dn_mode; + + /* auto gain and exposure must be turned off when changing modes */ + ret = __v4l2_ctrl_s_ctrl(sensor->ctrls.auto_gain, 0); + if (ret) + return ret; + ret = __v4l2_ctrl_s_ctrl(sensor->ctrls.auto_exp, V4L2_EXPOSURE_MANUAL); + if (ret) + return ret; + + if ((dn_mode == SUBSAMPLING && orig_dn_mode == SCALING) || + (dn_mode == SCALING && orig_dn_mode == SUBSAMPLING)) { + /* + * change between subsampling and scaling + * go through exposure calucation + */ + ret = ov5640_set_mode_exposure_calc(sensor, mode); + } else { + /* + * change inside subsampling or scaling + * download firmware directly + */ + ret = ov5640_set_mode_direct(sensor, mode); + } + + if (ret < 0) + return ret; + + ret = ov5640_set_ae_target(sensor, sensor->ae_target); + if (ret < 0) + return ret; + ret = ov5640_get_light_freq(sensor); + if (ret < 0) + return ret; + ret = ov5640_set_bandingfilter(sensor); + if (ret < 0) + return ret; + ret = ov5640_set_virtual_channel(sensor); + if (ret < 0) + return ret; + + sensor->pending_mode_change = false; + + return 0; +} + +/* restore the last set video mode after chip power-on */ +static int ov5640_restore_mode(struct ov5640_dev *sensor) +{ + int ret; + + /* first load the initial register values */ + ret = ov5640_load_regs(sensor, &ov5640_mode_init_data); + if (ret < 0) + return ret; + + /* now restore the last capture mode */ + return ov5640_set_mode(sensor, &ov5640_mode_init_data); +} + +static void ov5640_power(struct ov5640_dev *sensor, bool enable) +{ + if (sensor->pwdn_gpio) + gpiod_set_value(sensor->pwdn_gpio, enable ? 0 : 1); +} + +static void ov5640_reset(struct ov5640_dev *sensor) +{ + if (!sensor->reset_gpio) + return; + + gpiod_set_value(sensor->reset_gpio, 0); + + /* camera power cycle */ + ov5640_power(sensor, false); + usleep_range(5000, 10000); + ov5640_power(sensor, true); + usleep_range(5000, 10000); + + gpiod_set_value(sensor->reset_gpio, 1); + usleep_range(1000, 2000); + + gpiod_set_value(sensor->reset_gpio, 0); + usleep_range(5000, 10000); +} + +static int ov5640_set_power(struct ov5640_dev *sensor, bool on) +{ + int ret = 0; + + if (on) { + clk_prepare_enable(sensor->xclk); + + ret = regulator_bulk_enable(OV5640_NUM_SUPPLIES, + sensor->supplies); + if (ret) + goto xclk_off; + + ov5640_reset(sensor); + ov5640_power(sensor, true); + + ret = ov5640_init_slave_id(sensor); + if (ret) + goto power_off; + + ret = ov5640_restore_mode(sensor); + if (ret) + goto power_off; + + /* + * start streaming briefly followed by stream off in + * order to coax the clock lane into LP-11 state. + */ + ret = ov5640_set_stream(sensor, true); + if (ret) + goto power_off; + usleep_range(1000, 2000); + ret = ov5640_set_stream(sensor, false); + if (ret) + goto power_off; + + return 0; + } + +power_off: + ov5640_power(sensor, false); + regulator_bulk_disable(OV5640_NUM_SUPPLIES, sensor->supplies); +xclk_off: + clk_disable_unprepare(sensor->xclk); + return ret; +} + +/* --------------- Subdev Operations --------------- */ + +static int ov5640_s_power(struct v4l2_subdev *sd, int on) +{ + struct ov5640_dev *sensor = to_ov5640_dev(sd); + int ret = 0; + + mutex_lock(&sensor->lock); + + /* + * If the power count is modified from 0 to != 0 or from != 0 to 0, + * update the power state. + */ + if (sensor->power_count == !on) { + ret = ov5640_set_power(sensor, !!on); + if (ret) + goto out; + } + + /* Update the power count. */ + sensor->power_count += on ? 1 : -1; + WARN_ON(sensor->power_count < 0); +out: + mutex_unlock(&sensor->lock); + + if (on && !ret && sensor->power_count == 1) { + /* restore controls */ + ret = v4l2_ctrl_handler_setup(&sensor->ctrls.handler); + } + + return ret; +} + +static int ov5640_try_frame_interval(struct ov5640_dev *sensor, + struct v4l2_fract *fi, + u32 width, u32 height) +{ + const struct ov5640_mode_info *mode; + u32 minfps, maxfps, fps; + int ret; + + minfps = ov5640_framerates[OV5640_15_FPS]; + maxfps = ov5640_framerates[OV5640_30_FPS]; + + if (fi->numerator == 0) { + fi->denominator = maxfps; + fi->numerator = 1; + return OV5640_30_FPS; + } + + fps = DIV_ROUND_CLOSEST(fi->denominator, fi->numerator); + + fi->numerator = 1; + if (fps > maxfps) + fi->denominator = maxfps; + else if (fps < minfps) + fi->denominator = minfps; + else if (2 * fps >= 2 * minfps + (maxfps - minfps)) + fi->denominator = maxfps; + else + fi->denominator = minfps; + + ret = (fi->denominator == minfps) ? OV5640_15_FPS : OV5640_30_FPS; + + mode = ov5640_find_mode(sensor, ret, width, height, false); + return mode ? ret : -EINVAL; +} + +static int ov5640_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct ov5640_dev *sensor = to_ov5640_dev(sd); + struct v4l2_mbus_framefmt *fmt; + + if (format->pad != 0) + return -EINVAL; + + mutex_lock(&sensor->lock); + + if (format->which == V4L2_SUBDEV_FORMAT_TRY) + fmt = v4l2_subdev_get_try_format(&sensor->sd, cfg, + format->pad); + else + fmt = &sensor->fmt; + + format->format = *fmt; + + mutex_unlock(&sensor->lock); + + return 0; +} + +static int ov5640_try_fmt_internal(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *fmt, + enum ov5640_frame_rate fr, + const struct ov5640_mode_info **new_mode) +{ + struct ov5640_dev *sensor = to_ov5640_dev(sd); + const struct ov5640_mode_info *mode; + + mode = ov5640_find_mode(sensor, fr, fmt->width, fmt->height, true); + if (!mode) + return -EINVAL; + + fmt->width = mode->width; + fmt->height = mode->height; + fmt->code = sensor->fmt.code; + + if (new_mode) + *new_mode = mode; + return 0; +} + +static int ov5640_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct ov5640_dev *sensor = to_ov5640_dev(sd); + const struct ov5640_mode_info *new_mode; + int ret; + + if (format->pad != 0) + return -EINVAL; + + mutex_lock(&sensor->lock); + + if (sensor->streaming) { + ret = -EBUSY; + goto out; + } + + ret = ov5640_try_fmt_internal(sd, &format->format, + sensor->current_fr, &new_mode); + if (ret) + goto out; + + if (format->which == V4L2_SUBDEV_FORMAT_TRY) { + struct v4l2_mbus_framefmt *fmt = + v4l2_subdev_get_try_format(sd, cfg, 0); + + *fmt = format->format; + goto out; + } + + sensor->current_mode = new_mode; + sensor->fmt = format->format; + sensor->pending_mode_change = true; +out: + mutex_unlock(&sensor->lock); + return ret; +} + + +/* + * Sensor Controls. + */ + +static int ov5640_set_ctrl_hue(struct ov5640_dev *sensor, int value) +{ + int ret; + + if (value) { + ret = ov5640_mod_reg(sensor, OV5640_REG_SDE_CTRL0, + BIT(0), BIT(0)); + if (ret) + return ret; + ret = ov5640_write_reg16(sensor, OV5640_REG_SDE_CTRL1, value); + } else { + ret = ov5640_mod_reg(sensor, OV5640_REG_SDE_CTRL0, BIT(0), 0); + } + + return ret; +} + +static int ov5640_set_ctrl_contrast(struct ov5640_dev *sensor, int value) +{ + int ret; + + if (value) { + ret = ov5640_mod_reg(sensor, OV5640_REG_SDE_CTRL0, + BIT(2), BIT(2)); + if (ret) + return ret; + ret = ov5640_write_reg(sensor, OV5640_REG_SDE_CTRL5, + value & 0xff); + } else { + ret = ov5640_mod_reg(sensor, OV5640_REG_SDE_CTRL0, BIT(2), 0); + } + + return ret; +} + +static int ov5640_set_ctrl_saturation(struct ov5640_dev *sensor, int value) +{ + int ret; + + if (value) { + ret = ov5640_mod_reg(sensor, OV5640_REG_SDE_CTRL0, + BIT(1), BIT(1)); + if (ret) + return ret; + ret = ov5640_write_reg(sensor, OV5640_REG_SDE_CTRL3, + value & 0xff); + if (ret) + return ret; + ret = ov5640_write_reg(sensor, OV5640_REG_SDE_CTRL4, + value & 0xff); + } else { + ret = ov5640_mod_reg(sensor, OV5640_REG_SDE_CTRL0, BIT(1), 0); + } + + return ret; +} + +static int ov5640_set_ctrl_white_balance(struct ov5640_dev *sensor, int awb) +{ + int ret; + + ret = ov5640_mod_reg(sensor, OV5640_REG_AWB_MANUAL_CTRL, + BIT(0), awb ? 0 : 1); + if (ret) + return ret; + + if (!awb) { + u16 red = (u16)sensor->ctrls.red_balance->val; + u16 blue = (u16)sensor->ctrls.blue_balance->val; + + ret = ov5640_write_reg16(sensor, OV5640_REG_AWB_R_GAIN, red); + if (ret) + return ret; + ret = ov5640_write_reg16(sensor, OV5640_REG_AWB_B_GAIN, blue); + } + + return ret; +} + +static int ov5640_set_ctrl_exposure(struct ov5640_dev *sensor, int exp) +{ + struct ov5640_ctrls *ctrls = &sensor->ctrls; + bool auto_exposure = (exp == V4L2_EXPOSURE_AUTO); + int ret = 0; + + if (ctrls->auto_exp->is_new) { + ret = ov5640_mod_reg(sensor, OV5640_REG_AEC_PK_MANUAL, + BIT(0), auto_exposure ? 0 : BIT(0)); + if (ret) + return ret; + } + + if (!auto_exposure && ctrls->exposure->is_new) { + u16 max_exp; + + ret = ov5640_read_reg16(sensor, OV5640_REG_AEC_PK_VTS, + &max_exp); + if (ret) + return ret; + ret = ov5640_get_vts(sensor); + if (ret < 0) + return ret; + max_exp += ret; + + if (ctrls->exposure->val < max_exp) + ret = ov5640_set_exposure(sensor, ctrls->exposure->val); + } + + return ret; +} + +static int ov5640_set_ctrl_gain(struct ov5640_dev *sensor, int auto_gain) +{ + struct ov5640_ctrls *ctrls = &sensor->ctrls; + int ret = 0; + + if (ctrls->auto_gain->is_new) { + ret = ov5640_mod_reg(sensor, OV5640_REG_AEC_PK_MANUAL, + BIT(1), ctrls->auto_gain->val ? 0 : BIT(1)); + if (ret) + return ret; + } + + if (!auto_gain && ctrls->gain->is_new) { + u16 gain = (u16)ctrls->gain->val; + + ret = ov5640_write_reg16(sensor, OV5640_REG_AEC_PK_REAL_GAIN, + gain & 0x3ff); + } + + return ret; +} + +static int ov5640_set_ctrl_test_pattern(struct ov5640_dev *sensor, int value) +{ + return ov5640_mod_reg(sensor, OV5640_REG_PRE_ISP_TEST_SET1, + 0xa4, value ? 0xa4 : 0); +} + +static int ov5640_g_volatile_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = ctrl_to_sd(ctrl); + struct ov5640_dev *sensor = to_ov5640_dev(sd); + int val; + + /* v4l2_ctrl_lock() locks our own mutex */ + + switch (ctrl->id) { + case V4L2_CID_AUTOGAIN: + if (!ctrl->val) + return 0; + val = ov5640_get_gain(sensor); + if (val < 0) + return val; + sensor->ctrls.gain->val = val; + break; + case V4L2_CID_EXPOSURE_AUTO: + if (ctrl->val == V4L2_EXPOSURE_MANUAL) + return 0; + val = ov5640_get_exposure(sensor); + if (val < 0) + return val; + sensor->ctrls.exposure->val = val; + break; + } + + return 0; +} + +static int ov5640_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = ctrl_to_sd(ctrl); + struct ov5640_dev *sensor = to_ov5640_dev(sd); + int ret; + + /* v4l2_ctrl_lock() locks our own mutex */ + + /* + * If the device is not powered up by the host driver do + * not apply any controls to H/W at this time. Instead + * the controls will be restored right after power-up. + */ + if (sensor->power_count == 0) + return 0; + + switch (ctrl->id) { + case V4L2_CID_AUTOGAIN: + ret = ov5640_set_ctrl_gain(sensor, ctrl->val); + break; + case V4L2_CID_EXPOSURE_AUTO: + ret = ov5640_set_ctrl_exposure(sensor, ctrl->val); + break; + case V4L2_CID_AUTO_WHITE_BALANCE: + ret = ov5640_set_ctrl_white_balance(sensor, ctrl->val); + break; + case V4L2_CID_HUE: + ret = ov5640_set_ctrl_hue(sensor, ctrl->val); + break; + case V4L2_CID_CONTRAST: + ret = ov5640_set_ctrl_contrast(sensor, ctrl->val); + break; + case V4L2_CID_SATURATION: + ret = ov5640_set_ctrl_saturation(sensor, ctrl->val); + break; + case V4L2_CID_TEST_PATTERN: + ret = ov5640_set_ctrl_test_pattern(sensor, ctrl->val); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static const struct v4l2_ctrl_ops ov5640_ctrl_ops = { + .g_volatile_ctrl = ov5640_g_volatile_ctrl, + .s_ctrl = ov5640_s_ctrl, +}; + +static const char * const test_pattern_menu[] = { + "Disabled", + "Color bars", +}; + +static int ov5640_init_controls(struct ov5640_dev *sensor) +{ + const struct v4l2_ctrl_ops *ops = &ov5640_ctrl_ops; + struct ov5640_ctrls *ctrls = &sensor->ctrls; + struct v4l2_ctrl_handler *hdl = &ctrls->handler; + int ret; + + v4l2_ctrl_handler_init(hdl, 32); + + /* we can use our own mutex for the ctrl lock */ + hdl->lock = &sensor->lock; + + /* Auto/manual white balance */ + ctrls->auto_wb = v4l2_ctrl_new_std(hdl, ops, + V4L2_CID_AUTO_WHITE_BALANCE, + 0, 1, 1, 1); + ctrls->blue_balance = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BLUE_BALANCE, + 0, 4095, 1, 0); + ctrls->red_balance = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_RED_BALANCE, + 0, 4095, 1, 0); + /* Auto/manual exposure */ + ctrls->auto_exp = v4l2_ctrl_new_std_menu(hdl, ops, + V4L2_CID_EXPOSURE_AUTO, + V4L2_EXPOSURE_MANUAL, 0, + V4L2_EXPOSURE_AUTO); + ctrls->exposure = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_EXPOSURE, + 0, 65535, 1, 0); + /* 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, + 0, 1023, 1, 0); + + ctrls->saturation = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_SATURATION, + 0, 255, 1, 64); + ctrls->hue = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HUE, + 0, 359, 1, 0); + ctrls->contrast = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_CONTRAST, + 0, 255, 1, 0); + ctrls->test_pattern = + v4l2_ctrl_new_std_menu_items(hdl, ops, V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(test_pattern_menu) - 1, + 0, 0, test_pattern_menu); + + if (hdl->error) { + ret = hdl->error; + goto free_ctrls; + } + + ctrls->gain->flags |= V4L2_CTRL_FLAG_VOLATILE; + ctrls->exposure->flags |= V4L2_CTRL_FLAG_VOLATILE; + + v4l2_ctrl_auto_cluster(3, &ctrls->auto_wb, 0, false); + v4l2_ctrl_auto_cluster(2, &ctrls->auto_gain, 0, true); + v4l2_ctrl_auto_cluster(2, &ctrls->auto_exp, 1, true); + + sensor->sd.ctrl_handler = hdl; + return 0; + +free_ctrls: + v4l2_ctrl_handler_free(hdl); + return ret; +} + +static int ov5640_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + if (fse->pad != 0) + return -EINVAL; + if (fse->index >= OV5640_NUM_MODES) + return -EINVAL; + + fse->min_width = fse->max_width = + ov5640_mode_data[0][fse->index].width; + fse->min_height = fse->max_height = + ov5640_mode_data[0][fse->index].height; + + return 0; +} + +static int ov5640_enum_frame_interval( + struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_interval_enum *fie) +{ + struct ov5640_dev *sensor = to_ov5640_dev(sd); + struct v4l2_fract tpf; + int ret; + + if (fie->pad != 0) + return -EINVAL; + if (fie->index >= OV5640_NUM_FRAMERATES) + return -EINVAL; + + tpf.numerator = 1; + tpf.denominator = ov5640_framerates[fie->index]; + + ret = ov5640_try_frame_interval(sensor, &tpf, + fie->width, fie->height); + if (ret < 0) + return -EINVAL; + + fie->interval = tpf; + return 0; +} + +static int ov5640_g_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *fi) +{ + struct ov5640_dev *sensor = to_ov5640_dev(sd); + + mutex_lock(&sensor->lock); + fi->interval = sensor->frame_interval; + mutex_unlock(&sensor->lock); + + return 0; +} + +static int ov5640_s_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *fi) +{ + struct ov5640_dev *sensor = to_ov5640_dev(sd); + const struct ov5640_mode_info *mode; + int frame_rate, ret = 0; + + if (fi->pad != 0) + return -EINVAL; + + mutex_lock(&sensor->lock); + + if (sensor->streaming) { + ret = -EBUSY; + goto out; + } + + mode = sensor->current_mode; + + frame_rate = ov5640_try_frame_interval(sensor, &fi->interval, + mode->width, mode->height); + if (frame_rate < 0) + frame_rate = OV5640_15_FPS; + + sensor->current_fr = frame_rate; + sensor->frame_interval = fi->interval; + sensor->pending_mode_change = true; +out: + mutex_unlock(&sensor->lock); + return ret; +} + +static int ov5640_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct ov5640_dev *sensor = to_ov5640_dev(sd); + + if (code->pad != 0) + return -EINVAL; + if (code->index != 0) + return -EINVAL; + + code->code = sensor->fmt.code; + + return 0; +} + +static int ov5640_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct ov5640_dev *sensor = to_ov5640_dev(sd); + int ret = 0; + + mutex_lock(&sensor->lock); + + if (sensor->streaming == !enable) { + if (enable && sensor->pending_mode_change) { + ret = ov5640_set_mode(sensor, sensor->current_mode); + if (ret) + goto out; + } + + ret = ov5640_set_stream(sensor, enable); + if (!ret) + sensor->streaming = enable; + } +out: + mutex_unlock(&sensor->lock); + return ret; +} + +static const struct v4l2_subdev_core_ops ov5640_core_ops = { + .s_power = ov5640_s_power, +}; + +static const struct v4l2_subdev_video_ops ov5640_video_ops = { + .g_frame_interval = ov5640_g_frame_interval, + .s_frame_interval = ov5640_s_frame_interval, + .s_stream = ov5640_s_stream, +}; + +static const struct v4l2_subdev_pad_ops ov5640_pad_ops = { + .enum_mbus_code = ov5640_enum_mbus_code, + .get_fmt = ov5640_get_fmt, + .set_fmt = ov5640_set_fmt, + .enum_frame_size = ov5640_enum_frame_size, + .enum_frame_interval = ov5640_enum_frame_interval, +}; + +static const struct v4l2_subdev_ops ov5640_subdev_ops = { + .core = &ov5640_core_ops, + .video = &ov5640_video_ops, + .pad = &ov5640_pad_ops, +}; + +static int ov5640_get_regulators(struct ov5640_dev *sensor) +{ + int i; + + for (i = 0; i < OV5640_NUM_SUPPLIES; i++) + sensor->supplies[i].supply = ov5640_supply_name[i]; + + return devm_regulator_bulk_get(&sensor->i2c_client->dev, + OV5640_NUM_SUPPLIES, + sensor->supplies); +} + +static int ov5640_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct fwnode_handle *endpoint; + struct ov5640_dev *sensor; + int ret; + + sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL); + if (!sensor) + return -ENOMEM; + + sensor->i2c_client = client; + sensor->fmt.code = MEDIA_BUS_FMT_UYVY8_2X8; + sensor->fmt.width = 640; + sensor->fmt.height = 480; + sensor->fmt.field = V4L2_FIELD_NONE; + sensor->frame_interval.numerator = 1; + sensor->frame_interval.denominator = ov5640_framerates[OV5640_30_FPS]; + sensor->current_fr = OV5640_30_FPS; + sensor->current_mode = + &ov5640_mode_data[OV5640_30_FPS][OV5640_MODE_VGA_640_480]; + sensor->pending_mode_change = true; + + sensor->ae_target = 52; + + endpoint = fwnode_graph_get_next_endpoint( + of_fwnode_handle(client->dev.of_node), NULL); + if (!endpoint) { + dev_err(dev, "endpoint node not found\n"); + return -EINVAL; + } + + ret = v4l2_fwnode_endpoint_parse(endpoint, &sensor->ep); + fwnode_handle_put(endpoint); + if (ret) { + dev_err(dev, "Could not parse endpoint\n"); + return ret; + } + + if (sensor->ep.bus_type != V4L2_MBUS_CSI2) { + dev_err(dev, "invalid bus type, must be MIPI CSI2\n"); + return -EINVAL; + } + + /* get system clock (xclk) */ + sensor->xclk = devm_clk_get(dev, "xclk"); + if (IS_ERR(sensor->xclk)) { + dev_err(dev, "failed to get xclk\n"); + return PTR_ERR(sensor->xclk); + } + + sensor->xclk_freq = clk_get_rate(sensor->xclk); + if (sensor->xclk_freq < OV5640_XCLK_MIN || + sensor->xclk_freq > OV5640_XCLK_MAX) { + dev_err(dev, "xclk frequency out of range: %d Hz\n", + sensor->xclk_freq); + return -EINVAL; + } + + /* request optional power down pin */ + sensor->pwdn_gpio = devm_gpiod_get_optional(dev, "powerdown", + GPIOD_OUT_HIGH); + /* request optional reset pin */ + sensor->reset_gpio = devm_gpiod_get_optional(dev, "reset", + GPIOD_OUT_HIGH); + + v4l2_i2c_subdev_init(&sensor->sd, client, &ov5640_subdev_ops); + + sensor->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE; + sensor->pad.flags = MEDIA_PAD_FL_SOURCE; + sensor->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&sensor->sd.entity, 1, &sensor->pad); + if (ret) + return ret; + + ret = ov5640_get_regulators(sensor); + if (ret) + return ret; + + mutex_init(&sensor->lock); + + ret = ov5640_init_controls(sensor); + if (ret) + goto entity_cleanup; + + ret = v4l2_async_register_subdev(&sensor->sd); + if (ret) + goto free_ctrls; + + return 0; + +free_ctrls: + v4l2_ctrl_handler_free(&sensor->ctrls.handler); +entity_cleanup: + mutex_destroy(&sensor->lock); + media_entity_cleanup(&sensor->sd.entity); + return ret; +} + +static int ov5640_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov5640_dev *sensor = to_ov5640_dev(sd); + + v4l2_async_unregister_subdev(&sensor->sd); + mutex_destroy(&sensor->lock); + media_entity_cleanup(&sensor->sd.entity); + v4l2_ctrl_handler_free(&sensor->ctrls.handler); + + return 0; +} + +static const struct i2c_device_id ov5640_id[] = { + {"ov5640", 0}, + {}, +}; +MODULE_DEVICE_TABLE(i2c, ov5640_id); + +static const struct of_device_id ov5640_dt_ids[] = { + { .compatible = "ovti,ov5640" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, ov5640_dt_ids); + +static struct i2c_driver ov5640_i2c_driver = { + .driver = { + .name = "ov5640", + .of_match_table = ov5640_dt_ids, + }, + .id_table = ov5640_id, + .probe = ov5640_probe, + .remove = ov5640_remove, +}; + +module_i2c_driver(ov5640_i2c_driver); + +MODULE_DESCRIPTION("OV5640 MIPI Camera Subdev Driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3-58-ga151 From 2d77a2a6a6570eb5afc26bb3ec53537dc06c4596 Mon Sep 17 00:00:00 2001 From: Steve Longerbeam Date: Sun, 11 Jun 2017 14:46:11 -0300 Subject: [media] MAINTAINERS: add entry for OV5640 sensor driver Add maintainer entry for the OV5640 V4L2 sensor driver. Signed-off-by: Steve Longerbeam Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index fd22b66ffa34..1b90cef7d9cf 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9439,6 +9439,13 @@ M: Harald Welte S: Maintained F: drivers/char/pcmcia/cm4040_cs.* +OMNIVISION OV5640 SENSOR DRIVER +M: Steve Longerbeam +L: linux-media@vger.kernel.org +T: git git://linuxtv.org/media_tree.git +S: Maintained +F: drivers/media/i2c/ov5640.c + OMNIVISION OV5647 SENSOR DRIVER M: Ramiro Oliveira L: linux-media@vger.kernel.org -- cgit v1.2.3-58-ga151 From bb9e07713581df054e2f64be70a1ed19f3f44e9e Mon Sep 17 00:00:00 2001 From: Steve Longerbeam Date: Sat, 10 Jun 2017 15:56:24 -0300 Subject: [media] dt-bindings: Add bindings for i.MX media driver Add bindings documentation for the i.MX media driver. Signed-off-by: Steve Longerbeam Acked-by: Rob Herring Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/devicetree/bindings/media/imx.txt | 53 +++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/imx.txt diff --git a/Documentation/devicetree/bindings/media/imx.txt b/Documentation/devicetree/bindings/media/imx.txt new file mode 100644 index 000000000000..77f4b0a7fd2b --- /dev/null +++ b/Documentation/devicetree/bindings/media/imx.txt @@ -0,0 +1,53 @@ +Freescale i.MX Media Video Device +================================= + +Video Media Controller node +--------------------------- + +This is the media controller node for video capture support. It is a +virtual device that lists the camera serial interface nodes that the +media device will control. + +Required properties: +- compatible : "fsl,imx-capture-subsystem"; +- ports : Should contain a list of phandles pointing to camera + sensor interface ports of IPU devices + +example: + +capture-subsystem { + compatible = "fsl,imx-capture-subsystem"; + ports = <&ipu1_csi0>, <&ipu1_csi1>; +}; + + +mipi_csi2 node +-------------- + +This is the device node for the MIPI CSI-2 Receiver core in the i.MX +SoC. This is a Synopsys Designware MIPI CSI-2 host controller core +combined with a D-PHY core mixed into the same register block. In +addition this device consists of an i.MX-specific "CSI2IPU gasket" +glue logic, also controlled from the same register block. The CSI2IPU +gasket demultiplexes the four virtual channel streams from the host +controller's 32-bit output image bus onto four 16-bit parallel busses +to the i.MX IPU CSIs. + +Required properties: +- compatible : "fsl,imx6-mipi-csi2"; +- reg : physical base address and length of the register set; +- clocks : the MIPI CSI-2 receiver requires three clocks: hsi_tx + (the D-PHY clock), video_27m (D-PHY PLL reference + clock), and eim_podf; +- clock-names : must contain "dphy", "ref", "pix"; +- port@* : five port nodes must exist, containing endpoints + connecting to the source and sink devices according to + of_graph bindings. The first port is an input port, + connecting with a MIPI CSI-2 source, and ports 1 + through 4 are output ports connecting with parallel + bus sink endpoint nodes and correspond to the four + MIPI CSI-2 virtual channel outputs. + +Optional properties: +- interrupts : must contain two level-triggered interrupts, + in order: 100 and 101; -- cgit v1.2.3-58-ga151 From a2bce3794a2122425abce5b0359d075b790930bc Mon Sep 17 00:00:00 2001 From: Steve Longerbeam Date: Wed, 7 Jun 2017 15:33:57 -0300 Subject: [media] media: Add userspace header file for i.MX This adds a header file for use by userspace programs wanting to interact with the i.MX media driver. It defines custom events and v4l2 controls for the i.MX v4l2 subdevices. Signed-off-by: Steve Longerbeam Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- include/linux/imx-media.h | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 include/linux/imx-media.h diff --git a/include/linux/imx-media.h b/include/linux/imx-media.h new file mode 100644 index 000000000000..77221ecad6fc --- /dev/null +++ b/include/linux/imx-media.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2014-2017 Mentor Graphics Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the + * License, or (at your option) any later version + */ + +#ifndef __LINUX_IMX_MEDIA_H__ +#define __LINUX_IMX_MEDIA_H__ + +/* + * events from the subdevs + */ +#define V4L2_EVENT_IMX_CLASS V4L2_EVENT_PRIVATE_START +#define V4L2_EVENT_IMX_FRAME_INTERVAL_ERROR (V4L2_EVENT_IMX_CLASS + 1) + +enum imx_ctrl_id { + V4L2_CID_IMX_FIM_ENABLE = (V4L2_CID_USER_IMX_BASE + 0), + V4L2_CID_IMX_FIM_NUM, + V4L2_CID_IMX_FIM_TOLERANCE_MIN, + V4L2_CID_IMX_FIM_TOLERANCE_MAX, + V4L2_CID_IMX_FIM_NUM_SKIP, + V4L2_CID_IMX_FIM_ICAP_EDGE, + V4L2_CID_IMX_FIM_ICAP_CHANNEL, +}; + +#endif -- cgit v1.2.3-58-ga151 From e130291212df5ce8160cd2e35387c96439863ad3 Mon Sep 17 00:00:00 2001 From: Steve Longerbeam Date: Sat, 10 Jun 2017 16:00:29 -0300 Subject: [media] media: Add i.MX media core driver Add the core media driver for i.MX SOC. Switch from the v4l2_of_ APIs to the v4l2_fwnode_ APIs. Add the bayer formats to imx-media's list of supported pixel and bus formats. Signed-off-by: Steve Longerbeam Signed-off-by: Philipp Zabel Signed-off-by: Russell King Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/media/v4l-drivers/imx.rst | 614 ++++++++++++++++ drivers/staging/media/Kconfig | 2 + drivers/staging/media/Makefile | 1 + drivers/staging/media/imx/Kconfig | 7 + drivers/staging/media/imx/Makefile | 5 + drivers/staging/media/imx/imx-media-dev.c | 667 +++++++++++++++++ drivers/staging/media/imx/imx-media-fim.c | 494 +++++++++++++ drivers/staging/media/imx/imx-media-internal-sd.c | 349 +++++++++ drivers/staging/media/imx/imx-media-of.c | 270 +++++++ drivers/staging/media/imx/imx-media-utils.c | 834 ++++++++++++++++++++++ drivers/staging/media/imx/imx-media.h | 323 +++++++++ include/media/imx.h | 15 + include/uapi/linux/v4l2-controls.h | 4 + 13 files changed, 3585 insertions(+) create mode 100644 Documentation/media/v4l-drivers/imx.rst create mode 100644 drivers/staging/media/imx/Kconfig create mode 100644 drivers/staging/media/imx/Makefile create mode 100644 drivers/staging/media/imx/imx-media-dev.c create mode 100644 drivers/staging/media/imx/imx-media-fim.c create mode 100644 drivers/staging/media/imx/imx-media-internal-sd.c create mode 100644 drivers/staging/media/imx/imx-media-of.c create mode 100644 drivers/staging/media/imx/imx-media-utils.c create mode 100644 drivers/staging/media/imx/imx-media.h create mode 100644 include/media/imx.h diff --git a/Documentation/media/v4l-drivers/imx.rst b/Documentation/media/v4l-drivers/imx.rst new file mode 100644 index 000000000000..e0ee0f1aeb05 --- /dev/null +++ b/Documentation/media/v4l-drivers/imx.rst @@ -0,0 +1,614 @@ +i.MX Video Capture Driver +========================= + +Introduction +------------ + +The Freescale i.MX5/6 contains an Image Processing Unit (IPU), which +handles the flow of image frames to and from capture devices and +display devices. + +For image capture, the IPU contains the following internal subunits: + +- Image DMA Controller (IDMAC) +- Camera Serial Interface (CSI) +- Image Converter (IC) +- Sensor Multi-FIFO Controller (SMFC) +- Image Rotator (IRT) +- Video De-Interlacing or Combining Block (VDIC) + +The IDMAC is the DMA controller for transfer of image frames to and from +memory. Various dedicated DMA channels exist for both video capture and +display paths. During transfer, the IDMAC is also capable of vertical +image flip, 8x8 block transfer (see IRT description), pixel component +re-ordering (for example UYVY to YUYV) within the same colorspace, and +even packed <--> planar conversion. It can also perform a simple +de-interlacing by interleaving even and odd lines during transfer +(without motion compensation which requires the VDIC). + +The CSI is the backend capture unit that interfaces directly with +camera sensors over Parallel, BT.656/1120, and MIPI CSI-2 busses. + +The IC handles color-space conversion, resizing (downscaling and +upscaling), horizontal flip, and 90/270 degree rotation operations. + +There are three independent "tasks" within the IC that can carry out +conversions concurrently: pre-process encoding, pre-process viewfinder, +and post-processing. Within each task, conversions are split into three +sections: downsizing section, main section (upsizing, flip, colorspace +conversion, and graphics plane combining), and rotation section. + +The IPU time-shares the IC task operations. The time-slice granularity +is one burst of eight pixels in the downsizing section, one image line +in the main processing section, one image frame in the rotation section. + +The SMFC is composed of four independent FIFOs that each can transfer +captured frames from sensors directly to memory concurrently via four +IDMAC channels. + +The IRT carries out 90 and 270 degree image rotation operations. The +rotation operation is carried out on 8x8 pixel blocks at a time. This +operation is supported by the IDMAC which handles the 8x8 block transfer +along with block reordering, in coordination with vertical flip. + +The VDIC handles the conversion of interlaced video to progressive, with +support for different motion compensation modes (low, medium, and high +motion). The deinterlaced output frames from the VDIC can be sent to the +IC pre-process viewfinder task for further conversions. The VDIC also +contains a Combiner that combines two image planes, with alpha blending +and color keying. + +In addition to the IPU internal subunits, there are also two units +outside the IPU that are also involved in video capture on i.MX: + +- MIPI CSI-2 Receiver for camera sensors with the MIPI CSI-2 bus + interface. This is a Synopsys DesignWare core. +- Two video multiplexers for selecting among multiple sensor inputs + to send to a CSI. + +For more info, refer to the latest versions of the i.MX5/6 reference +manuals [#f1]_ and [#f2]_. + + +Features +-------- + +Some of the features of this driver include: + +- Many different pipelines can be configured via media controller API, + that correspond to the hardware video capture pipelines supported in + the i.MX. + +- Supports parallel, BT.565, and MIPI CSI-2 interfaces. + +- Concurrent independent streams, by configuring pipelines to multiple + video capture interfaces using independent entities. + +- Scaling, color-space conversion, horizontal and vertical flip, and + image rotation via IC task subdevs. + +- Many pixel formats supported (RGB, packed and planar YUV, partial + planar YUV). + +- The VDIC subdev supports motion compensated de-interlacing, with three + motion compensation modes: low, medium, and high motion. Pipelines are + defined that allow sending frames to the VDIC subdev directly from the + CSI. There is also support in the future for sending frames to the + VDIC from memory buffers via a output/mem2mem devices. + +- Includes a Frame Interval Monitor (FIM) that can correct vertical sync + problems with the ADV718x video decoders. + + +Entities +-------- + +imx6-mipi-csi2 +-------------- + +This is the MIPI CSI-2 receiver entity. It has one sink pad to receive +the MIPI CSI-2 stream (usually from a MIPI CSI-2 camera sensor). It has +four source pads, corresponding to the four MIPI CSI-2 demuxed virtual +channel outputs. Multpiple source pads can be enabled to independently +stream from multiple virtual channels. + +This entity actually consists of two sub-blocks. One is the MIPI CSI-2 +core. This is a Synopsys Designware MIPI CSI-2 core. The other sub-block +is a "CSI-2 to IPU gasket". The gasket acts as a demultiplexer of the +four virtual channels streams, providing four separate parallel buses +containing each virtual channel that are routed to CSIs or video +multiplexers as described below. + +On i.MX6 solo/dual-lite, all four virtual channel buses are routed to +two video multiplexers. Both CSI0 and CSI1 can receive any virtual +channel, as selected by the video multiplexers. + +On i.MX6 Quad, virtual channel 0 is routed to IPU1-CSI0 (after selected +by a video mux), virtual channels 1 and 2 are hard-wired to IPU1-CSI1 +and IPU2-CSI0, respectively, and virtual channel 3 is routed to +IPU2-CSI1 (again selected by a video mux). + +ipuX_csiY_mux +------------- + +These are the video multiplexers. They have two or more sink pads to +select from either camera sensors with a parallel interface, or from +MIPI CSI-2 virtual channels from imx6-mipi-csi2 entity. They have a +single source pad that routes to a CSI (ipuX_csiY entities). + +On i.MX6 solo/dual-lite, there are two video mux entities. One sits +in front of IPU1-CSI0 to select between a parallel sensor and any of +the four MIPI CSI-2 virtual channels (a total of five sink pads). The +other mux sits in front of IPU1-CSI1, and again has five sink pads to +select between a parallel sensor and any of the four MIPI CSI-2 virtual +channels. + +On i.MX6 Quad, there are two video mux entities. One sits in front of +IPU1-CSI0 to select between a parallel sensor and MIPI CSI-2 virtual +channel 0 (two sink pads). The other mux sits in front of IPU2-CSI1 to +select between a parallel sensor and MIPI CSI-2 virtual channel 3 (two +sink pads). + +ipuX_csiY +--------- + +These are the CSI entities. They have a single sink pad receiving from +either a video mux or from a MIPI CSI-2 virtual channel as described +above. + +This entity has two source pads. The first source pad can link directly +to the ipuX_vdic entity or the ipuX_ic_prp entity, using hardware links +that require no IDMAC memory buffer transfer. + +When the direct source pad is routed to the ipuX_ic_prp entity, frames +from the CSI can be processed by one or both of the IC pre-processing +tasks. + +When the direct source pad is routed to the ipuX_vdic entity, the VDIC +will carry out motion-compensated de-interlace using "high motion" mode +(see description of ipuX_vdic entity). + +The second source pad sends video frames directly to memory buffers +via the SMFC and an IDMAC channel, bypassing IC pre-processing. This +source pad is routed to a capture device node, with a node name of the +format "ipuX_csiY capture". + +Note that since the IDMAC source pad makes use of an IDMAC channel, it +can do pixel reordering within the same colorspace. For example, the +sink pad can take UYVY2X8, but the IDMAC source pad can output YUYV2X8. +If the sink pad is receiving YUV, the output at the capture device can +also be converted to a planar YUV format such as YUV420. + +It will also perform simple de-interlace without motion compensation, +which is activated if the sink pad's field type is an interlaced type, +and the IDMAC source pad field type is set to none. + +This subdev can generate the following event when enabling the second +IDMAC source pad: + +- V4L2_EVENT_IMX_FRAME_INTERVAL_ERROR + +The user application can subscribe to this event from the ipuX_csiY +subdev node. This event is generated by the Frame Interval Monitor +(see below for more on the FIM). + +Cropping in ipuX_csiY +--------------------- + +The CSI supports cropping the incoming raw sensor frames. This is +implemented in the ipuX_csiY entities at the sink pad, using the +crop selection subdev API. + +The CSI also supports fixed divide-by-two downscaling indepently in +width and height. This is implemented in the ipuX_csiY entities at +the sink pad, using the compose selection subdev API. + +The output rectangle at the ipuX_csiY source pad is the same as +the compose rectangle at the sink pad. So the source pad rectangle +cannot be negotiated, it must be set using the compose selection +API at sink pad (if /2 downscale is desired, otherwise source pad +rectangle is equal to incoming rectangle). + +To give an example of crop and /2 downscale, this will crop a +1280x960 input frame to 640x480, and then /2 downscale in both +dimensions to 320x240 (assumes ipu1_csi0 is linked to ipu1_csi0_mux): + +media-ctl -V "'ipu1_csi0_mux':2[fmt:UYVY2X8/1280x960]" +media-ctl -V "'ipu1_csi0':0[crop:(0,0)/640x480]" +media-ctl -V "'ipu1_csi0':0[compose:(0,0)/320x240]" + +Frame Skipping in ipuX_csiY +--------------------------- + +The CSI supports frame rate decimation, via frame skipping. Frame +rate decimation is specified by setting the frame intervals at +sink and source pads. The ipuX_csiY entity then applies the best +frame skip setting to the CSI to achieve the desired frame rate +at the source pad. + +The following example reduces an assumed incoming 60 Hz frame +rate by half at the IDMAC output source pad: + +media-ctl -V "'ipu1_csi0':0[fmt:UYVY2X8/640x480@1/60]" +media-ctl -V "'ipu1_csi0':2[fmt:UYVY2X8/640x480@1/30]" + +Frame Interval Monitor in ipuX_csiY +----------------------------------- + +The adv718x decoders can occasionally send corrupt fields during +NTSC/PAL signal re-sync (too little or too many video lines). When +this happens, the IPU triggers a mechanism to re-establish vertical +sync by adding 1 dummy line every frame, which causes a rolling effect +from image to image, and can last a long time before a stable image is +recovered. Or sometimes the mechanism doesn't work at all, causing a +permanent split image (one frame contains lines from two consecutive +captured images). + +From experiment it was found that during image rolling, the frame +intervals (elapsed time between two EOF's) drop below the nominal +value for the current standard, by about one frame time (60 usec), +and remain at that value until rolling stops. + +While the reason for this observation isn't known (the IPU dummy +line mechanism should show an increase in the intervals by 1 line +time every frame, not a fixed value), we can use it to detect the +corrupt fields using a frame interval monitor. If the FIM detects a +bad frame interval, the ipuX_csiY subdev will send the event +V4L2_EVENT_IMX_FRAME_INTERVAL_ERROR. Userland can register with +the FIM event notification on the ipuX_csiY subdev device node. +Userland can issue a streaming restart when this event is received +to correct the rolling/split image. + +The ipuX_csiY subdev includes custom controls to tweak some dials for +FIM. If one of these controls is changed during streaming, the FIM will +be reset and will continue at the new settings. + +- V4L2_CID_IMX_FIM_ENABLE + +Enable/disable the FIM. + +- V4L2_CID_IMX_FIM_NUM + +How many frame interval measurements to average before comparing against +the nominal frame interval reported by the sensor. This can reduce noise +caused by interrupt latency. + +- V4L2_CID_IMX_FIM_TOLERANCE_MIN + +If the averaged intervals fall outside nominal by this amount, in +microseconds, the V4L2_EVENT_IMX_FRAME_INTERVAL_ERROR event is sent. + +- V4L2_CID_IMX_FIM_TOLERANCE_MAX + +If any intervals are higher than this value, those samples are +discarded and do not enter into the average. This can be used to +discard really high interval errors that might be due to interrupt +latency from high system load. + +- V4L2_CID_IMX_FIM_NUM_SKIP + +How many frames to skip after a FIM reset or stream restart before +FIM begins to average intervals. + +- V4L2_CID_IMX_FIM_ICAP_CHANNEL +- V4L2_CID_IMX_FIM_ICAP_EDGE + +These controls will configure an input capture channel as the method +for measuring frame intervals. This is superior to the default method +of measuring frame intervals via EOF interrupt, since it is not subject +to uncertainty errors introduced by interrupt latency. + +Input capture requires hardware support. A VSYNC signal must be routed +to one of the i.MX6 input capture channel pads. + +V4L2_CID_IMX_FIM_ICAP_CHANNEL configures which i.MX6 input capture +channel to use. This must be 0 or 1. + +V4L2_CID_IMX_FIM_ICAP_EDGE configures which signal edge will trigger +input capture events. By default the input capture method is disabled +with a value of IRQ_TYPE_NONE. Set this control to IRQ_TYPE_EDGE_RISING, +IRQ_TYPE_EDGE_FALLING, or IRQ_TYPE_EDGE_BOTH to enable input capture, +triggered on the given signal edge(s). + +When input capture is disabled, frame intervals will be measured via +EOF interrupt. + + +ipuX_vdic +--------- + +The VDIC carries out motion compensated de-interlacing, with three +motion compensation modes: low, medium, and high motion. The mode is +specified with the menu control V4L2_CID_DEINTERLACING_MODE. It has +two sink pads and a single source pad. + +The direct sink pad receives from an ipuX_csiY direct pad. With this +link the VDIC can only operate in high motion mode. + +When the IDMAC sink pad is activated, it receives from an output +or mem2mem device node. With this pipeline, it can also operate +in low and medium modes, because these modes require receiving +frames from memory buffers. Note that an output or mem2mem device +is not implemented yet, so this sink pad currently has no links. + +The source pad routes to the IC pre-processing entity ipuX_ic_prp. + +ipuX_ic_prp +----------- + +This is the IC pre-processing entity. It acts as a router, routing +data from its sink pad to one or both of its source pads. + +It has a single sink pad. The sink pad can receive from the ipuX_csiY +direct pad, or from ipuX_vdic. + +This entity has two source pads. One source pad routes to the +pre-process encode task entity (ipuX_ic_prpenc), the other to the +pre-process viewfinder task entity (ipuX_ic_prpvf). Both source pads +can be activated at the same time if the sink pad is receiving from +ipuX_csiY. Only the source pad to the pre-process viewfinder task entity +can be activated if the sink pad is receiving from ipuX_vdic (frames +from the VDIC can only be processed by the pre-process viewfinder task). + +ipuX_ic_prpenc +-------------- + +This is the IC pre-processing encode entity. It has a single sink +pad from ipuX_ic_prp, and a single source pad. The source pad is +routed to a capture device node, with a node name of the format +"ipuX_ic_prpenc capture". + +This entity performs the IC pre-process encode task operations: +color-space conversion, resizing (downscaling and upscaling), +horizontal and vertical flip, and 90/270 degree rotation. Flip +and rotation are provided via standard V4L2 controls. + +Like the ipuX_csiY IDMAC source, it can also perform simple de-interlace +without motion compensation, and pixel reordering. + +ipuX_ic_prpvf +------------- + +This is the IC pre-processing viewfinder entity. It has a single sink +pad from ipuX_ic_prp, and a single source pad. The source pad is routed +to a capture device node, with a node name of the format +"ipuX_ic_prpvf capture". + +It is identical in operation to ipuX_ic_prpenc, with the same resizing +and CSC operations and flip/rotation controls. It will receive and +process de-interlaced frames from the ipuX_vdic if ipuX_ic_prp is +receiving from ipuX_vdic. + +Like the ipuX_csiY IDMAC source, it can perform simple de-interlace +without motion compensation. However, note that if the ipuX_vdic is +included in the pipeline (ipuX_ic_prp is receiving from ipuX_vdic), +it's not possible to use simple de-interlace in ipuX_ic_prpvf, since +the ipuX_vdic has already carried out de-interlacing (with motion +compensation) and therefore the field type output from ipuX_ic_prp can +only be none. + +Capture Pipelines +----------------- + +The following describe the various use-cases supported by the pipelines. + +The links shown do not include the backend sensor, video mux, or mipi +csi-2 receiver links. This depends on the type of sensor interface +(parallel or mipi csi-2). So these pipelines begin with: + +sensor -> ipuX_csiY_mux -> ... + +for parallel sensors, or: + +sensor -> imx6-mipi-csi2 -> (ipuX_csiY_mux) -> ... + +for mipi csi-2 sensors. The imx6-mipi-csi2 receiver may need to route +to the video mux (ipuX_csiY_mux) before sending to the CSI, depending +on the mipi csi-2 virtual channel, hence ipuX_csiY_mux is shown in +parenthesis. + +Unprocessed Video Capture: +-------------------------- + +Send frames directly from sensor to camera device interface node, with +no conversions, via ipuX_csiY IDMAC source pad: + +-> ipuX_csiY:2 -> ipuX_csiY capture + +IC Direct Conversions: +---------------------- + +This pipeline uses the preprocess encode entity to route frames directly +from the CSI to the IC, to carry out scaling up to 1024x1024 resolution, +CSC, flipping, and image rotation: + +-> ipuX_csiY:1 -> 0:ipuX_ic_prp:1 -> 0:ipuX_ic_prpenc:1 -> + ipuX_ic_prpenc capture + +Motion Compensated De-interlace: +-------------------------------- + +This pipeline routes frames from the CSI direct pad to the VDIC entity to +support motion-compensated de-interlacing (high motion mode only), +scaling up to 1024x1024, CSC, flip, and rotation: + +-> ipuX_csiY:1 -> 0:ipuX_vdic:2 -> 0:ipuX_ic_prp:2 -> + 0:ipuX_ic_prpvf:1 -> ipuX_ic_prpvf capture + + +Usage Notes +----------- + +To aid in configuration and for backward compatibility with V4L2 +applications that access controls only from video device nodes, the +capture device interfaces inherit controls from the active entities +in the current pipeline, so controls can be accessed either directly +from the subdev or from the active capture device interface. For +example, the FIM controls are available either from the ipuX_csiY +subdevs or from the active capture device. + +The following are specific usage notes for the Sabre* reference +boards: + + +SabreLite with OV5642 and OV5640 +-------------------------------- + +This platform requires the OmniVision OV5642 module with a parallel +camera interface, and the OV5640 module with a MIPI CSI-2 +interface. Both modules are available from Boundary Devices: + +https://boundarydevices.com/product/nit6x_5mp +https://boundarydevices.com/product/nit6x_5mp_mipi + +Note that if only one camera module is available, the other sensor +node can be disabled in the device tree. + +The OV5642 module is connected to the parallel bus input on the i.MX +internal video mux to IPU1 CSI0. It's i2c bus connects to i2c bus 2. + +The MIPI CSI-2 OV5640 module is connected to the i.MX internal MIPI CSI-2 +receiver, and the four virtual channel outputs from the receiver are +routed as follows: vc0 to the IPU1 CSI0 mux, vc1 directly to IPU1 CSI1, +vc2 directly to IPU2 CSI0, and vc3 to the IPU2 CSI1 mux. The OV5640 is +also connected to i2c bus 2 on the SabreLite, therefore the OV5642 and +OV5640 must not share the same i2c slave address. + +The following basic example configures unprocessed video capture +pipelines for both sensors. The OV5642 is routed to ipu1_csi0, and +the OV5640, transmitting on MIPI CSI-2 virtual channel 1 (which is +imx6-mipi-csi2 pad 2), is routed to ipu1_csi1. Both sensors are +configured to output 640x480, and the OV5642 outputs YUYV2X8, the +OV5640 UYVY2X8: + +.. code-block:: none + + # Setup links for OV5642 + media-ctl -l "'ov5642 1-0042':0 -> 'ipu1_csi0_mux':1[1]" + media-ctl -l "'ipu1_csi0_mux':2 -> 'ipu1_csi0':0[1]" + media-ctl -l "'ipu1_csi0':2 -> 'ipu1_csi0 capture':0[1]" + # Setup links for OV5640 + media-ctl -l "'ov5640 1-0040':0 -> 'imx6-mipi-csi2':0[1]" + media-ctl -l "'imx6-mipi-csi2':2 -> 'ipu1_csi1':0[1]" + media-ctl -l "'ipu1_csi1':2 -> 'ipu1_csi1 capture':0[1]" + # Configure pads for OV5642 pipeline + media-ctl -V "'ov5642 1-0042':0 [fmt:YUYV2X8/640x480 field:none]" + media-ctl -V "'ipu1_csi0_mux':2 [fmt:YUYV2X8/640x480 field:none]" + media-ctl -V "'ipu1_csi0':2 [fmt:AYUV32/640x480 field:none]" + # Configure pads for OV5640 pipeline + media-ctl -V "'ov5640 1-0040':0 [fmt:UYVY2X8/640x480 field:none]" + media-ctl -V "'imx6-mipi-csi2':2 [fmt:UYVY2X8/640x480 field:none]" + media-ctl -V "'ipu1_csi1':2 [fmt:AYUV32/640x480 field:none]" + +Streaming can then begin independently on the capture device nodes +"ipu1_csi0 capture" and "ipu1_csi1 capture". The v4l2-ctl tool can +be used to select any supported YUV pixelformat on the capture device +nodes, including planar. + +SabreAuto with ADV7180 decoder +------------------------------ + +On the SabreAuto, an on-board ADV7180 SD decoder is connected to the +parallel bus input on the internal video mux to IPU1 CSI0. + +The following example configures a pipeline to capture from the ADV7180 +video decoder, assuming NTSC 720x480 input signals, with Motion +Compensated de-interlacing. Pad field types assume the adv7180 outputs +"interlaced". $outputfmt can be any format supported by the ipu1_ic_prpvf +entity at its output pad: + +.. code-block:: none + + # Setup links + media-ctl -l "'adv7180 3-0021':0 -> 'ipu1_csi0_mux':1[1]" + media-ctl -l "'ipu1_csi0_mux':2 -> 'ipu1_csi0':0[1]" + media-ctl -l "'ipu1_csi0':1 -> 'ipu1_vdic':0[1]" + media-ctl -l "'ipu1_vdic':2 -> 'ipu1_ic_prp':0[1]" + media-ctl -l "'ipu1_ic_prp':2 -> 'ipu1_ic_prpvf':0[1]" + media-ctl -l "'ipu1_ic_prpvf':1 -> 'ipu1_ic_prpvf capture':0[1]" + # Configure pads + media-ctl -V "'adv7180 3-0021':0 [fmt:UYVY2X8/720x480]" + media-ctl -V "'ipu1_csi0_mux':2 [fmt:UYVY2X8/720x480 field:interlaced]" + media-ctl -V "'ipu1_csi0':1 [fmt:AYUV32/720x480 field:interlaced]" + media-ctl -V "'ipu1_vdic':2 [fmt:AYUV32/720x480 field:none]" + media-ctl -V "'ipu1_ic_prp':2 [fmt:AYUV32/720x480 field:none]" + media-ctl -V "'ipu1_ic_prpvf':1 [fmt:$outputfmt field:none]" + +Streaming can then begin on the capture device node at +"ipu1_ic_prpvf capture". The v4l2-ctl tool can be used to select any +supported YUV or RGB pixelformat on the capture device node. + +This platform accepts Composite Video analog inputs to the ADV7180 on +Ain1 (connector J42). + +SabreSD with MIPI CSI-2 OV5640 +------------------------------ + +Similarly to SabreLite, the SabreSD supports a parallel interface +OV5642 module on IPU1 CSI0, and a MIPI CSI-2 OV5640 module. The OV5642 +connects to i2c bus 1 and the OV5640 to i2c bus 2. + +The device tree for SabreSD includes OF graphs for both the parallel +OV5642 and the MIPI CSI-2 OV5640, but as of this writing only the MIPI +CSI-2 OV5640 has been tested, so the OV5642 node is currently disabled. +The OV5640 module connects to MIPI connector J5 (sorry I don't have the +compatible module part number or URL). + +The following example configures a direct conversion pipeline to capture +from the OV5640, transmitting on MIPI CSI-2 virtual channel 1. $sensorfmt +can be any format supported by the OV5640. $sensordim is the frame +dimension part of $sensorfmt (minus the mbus pixel code). $outputfmt can +be any format supported by the ipu1_ic_prpenc entity at its output pad: + +.. code-block:: none + + # Setup links + media-ctl -l "'ov5640 1-003c':0 -> 'imx6-mipi-csi2':0[1]" + media-ctl -l "'imx6-mipi-csi2':2 -> 'ipu1_csi1':0[1]" + media-ctl -l "'ipu1_csi1':1 -> 'ipu1_ic_prp':0[1]" + media-ctl -l "'ipu1_ic_prp':1 -> 'ipu1_ic_prpenc':0[1]" + media-ctl -l "'ipu1_ic_prpenc':1 -> 'ipu1_ic_prpenc capture':0[1]" + # Configure pads + media-ctl -V "'ov5640 1-003c':0 [fmt:$sensorfmt field:none]" + media-ctl -V "'imx6-mipi-csi2':2 [fmt:$sensorfmt field:none]" + media-ctl -V "'ipu1_csi1':1 [fmt:AYUV32/$sensordim field:none]" + media-ctl -V "'ipu1_ic_prp':1 [fmt:AYUV32/$sensordim field:none]" + media-ctl -V "'ipu1_ic_prpenc':1 [fmt:$outputfmt field:none]" + +Streaming can then begin on "ipu1_ic_prpenc capture" node. The v4l2-ctl +tool can be used to select any supported YUV or RGB pixelformat on the +capture device node. + + +Known Issues +------------ + +1. When using 90 or 270 degree rotation control at capture resolutions + near the IC resizer limit of 1024x1024, and combined with planar + pixel formats (YUV420, YUV422p), frame capture will often fail with + no end-of-frame interrupts from the IDMAC channel. To work around + this, use lower resolution and/or packed formats (YUYV, RGB3, etc.) + when 90 or 270 rotations are needed. + + +File list +--------- + +drivers/staging/media/imx/ +include/media/imx.h +include/linux/imx-media.h + +References +---------- + +.. [#f1] http://www.nxp.com/assets/documents/data/en/reference-manuals/IMX6DQRM.pdf +.. [#f2] http://www.nxp.com/assets/documents/data/en/reference-manuals/IMX6SDLRM.pdf + + +Authors +------- +Steve Longerbeam +Philipp Zabel +Russell King + +Copyright (C) 2012-2017 Mentor Graphics Inc. diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig index dbda4d9a08e7..f8c25ee082ef 100644 --- a/drivers/staging/media/Kconfig +++ b/drivers/staging/media/Kconfig @@ -27,6 +27,8 @@ source "drivers/staging/media/cxd2099/Kconfig" source "drivers/staging/media/davinci_vpfe/Kconfig" +source "drivers/staging/media/imx/Kconfig" + source "drivers/staging/media/omap4iss/Kconfig" # Keep LIRC at the end, as it has sub-menus diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile index c04600c81264..ac090c5fce30 100644 --- a/drivers/staging/media/Makefile +++ b/drivers/staging/media/Makefile @@ -1,5 +1,6 @@ obj-$(CONFIG_I2C_BCM2048) += bcm2048/ obj-$(CONFIG_DVB_CXD2099) += cxd2099/ +obj-$(CONFIG_VIDEO_IMX_MEDIA) += imx/ obj-$(CONFIG_LIRC_STAGING) += lirc/ obj-$(CONFIG_VIDEO_DM365_VPFE) += davinci_vpfe/ obj-$(CONFIG_VIDEO_OMAP4) += omap4iss/ diff --git a/drivers/staging/media/imx/Kconfig b/drivers/staging/media/imx/Kconfig new file mode 100644 index 000000000000..5e79a36ce225 --- /dev/null +++ b/drivers/staging/media/imx/Kconfig @@ -0,0 +1,7 @@ +config VIDEO_IMX_MEDIA + tristate "i.MX5/6 V4L2 media core driver" + depends on MEDIA_CONTROLLER && VIDEO_V4L2 && ARCH_MXC && IMX_IPUV3_CORE + select V4L2_FWNODE + ---help--- + Say yes here to enable support for video4linux media controller + driver for the i.MX5/6 SOC. diff --git a/drivers/staging/media/imx/Makefile b/drivers/staging/media/imx/Makefile new file mode 100644 index 000000000000..ddd7d94dbac9 --- /dev/null +++ b/drivers/staging/media/imx/Makefile @@ -0,0 +1,5 @@ +imx-media-objs := imx-media-dev.o imx-media-internal-sd.o imx-media-of.o +imx-media-common-objs := imx-media-utils.o imx-media-fim.o + +obj-$(CONFIG_VIDEO_IMX_MEDIA) += imx-media.o +obj-$(CONFIG_VIDEO_IMX_MEDIA) += imx-media-common.o diff --git a/drivers/staging/media/imx/imx-media-dev.c b/drivers/staging/media/imx/imx-media-dev.c new file mode 100644 index 000000000000..48cbc7716758 --- /dev/null +++ b/drivers/staging/media/imx/imx-media-dev.c @@ -0,0 +1,667 @@ +/* + * V4L2 Media Controller Driver for Freescale i.MX5/6 SOC + * + * Copyright (c) 2016 Mentor Graphics Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include