From 8d0f6e13f9e5bacbd62ae0e8b16ab2e21728efd3 Mon Sep 17 00:00:00 2001 From: Benjamin Gaignard Date: Tue, 5 Dec 2017 09:51:07 -0500 Subject: media: platform: stm32: Adopt SPDX identifier Add SPDX identifiers to files under stm32 directory Signed-off-by: Benjamin Gaignard Acked-by: Philippe Ombredanne Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/stm32/stm32-dcmi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media/platform/stm32/stm32-dcmi.c') diff --git a/drivers/media/platform/stm32/stm32-dcmi.c b/drivers/media/platform/stm32/stm32-dcmi.c index 9460b3080dca..3319d9cb1b12 100644 --- a/drivers/media/platform/stm32/stm32-dcmi.c +++ b/drivers/media/platform/stm32/stm32-dcmi.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Driver for STM32 Digital Camera Memory Interface * @@ -5,7 +6,6 @@ * Authors: Yannick Fertre * Hugues Fruchet * for STMicroelectronics. - * License terms: GNU General Public License (GPL), version 2 * * This driver is based on atmel_isi.c * -- cgit v1.2.3 From 2a18115d43b209cf8f6b9a4deb735c4786478625 Mon Sep 17 00:00:00 2001 From: Hugues Fruchet Date: Wed, 7 Feb 2018 12:35:34 -0500 Subject: media: stm32-dcmi: remove redundant capture enable Remove redundant capture enable already done in dcmi_start_capture(). Signed-off-by: Hugues Fruchet Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/stm32/stm32-dcmi.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers/media/platform/stm32/stm32-dcmi.c') diff --git a/drivers/media/platform/stm32/stm32-dcmi.c b/drivers/media/platform/stm32/stm32-dcmi.c index 3319d9cb1b12..0993b55b8bd3 100644 --- a/drivers/media/platform/stm32/stm32-dcmi.c +++ b/drivers/media/platform/stm32/stm32-dcmi.c @@ -255,9 +255,6 @@ static void dcmi_dma_callback(void *param) spin_unlock(&dcmi->irqlock); return; } - - /* Enable capture */ - reg_set(dcmi->regs, DCMI_CR, CR_CAPTURE); } break; -- cgit v1.2.3 From ffb1bdc225d125e41f548fa268ca1a687ec4cecc Mon Sep 17 00:00:00 2001 From: Hugues Fruchet Date: Wed, 7 Feb 2018 12:35:35 -0500 Subject: media: stm32-dcmi: remove redundant clear of interrupt flags It is already cleared in dcmi_irq_callback(). Signed-off-by: Hugues Fruchet Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/stm32/stm32-dcmi.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/media/platform/stm32/stm32-dcmi.c') diff --git a/drivers/media/platform/stm32/stm32-dcmi.c b/drivers/media/platform/stm32/stm32-dcmi.c index 0993b55b8bd3..7deab02e23f6 100644 --- a/drivers/media/platform/stm32/stm32-dcmi.c +++ b/drivers/media/platform/stm32/stm32-dcmi.c @@ -385,8 +385,6 @@ static irqreturn_t dcmi_irq_thread(int irq, void *arg) 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)) { -- cgit v1.2.3 From 2c737e9c29b1fb7e585470c8247b9ec8ba5285c4 Mon Sep 17 00:00:00 2001 From: Hugues Fruchet Date: Wed, 7 Feb 2018 12:35:36 -0500 Subject: media: stm32-dcmi: improve error trace points Fix some missing "\n". Trace error returned by subdev streamon/streamoff. Remove extra "0x" unneeded with %pad formatter. Signed-off-by: Hugues Fruchet Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/stm32/stm32-dcmi.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) (limited to 'drivers/media/platform/stm32/stm32-dcmi.c') diff --git a/drivers/media/platform/stm32/stm32-dcmi.c b/drivers/media/platform/stm32/stm32-dcmi.c index 7deab02e23f6..6baea432e382 100644 --- a/drivers/media/platform/stm32/stm32-dcmi.c +++ b/drivers/media/platform/stm32/stm32-dcmi.c @@ -234,7 +234,7 @@ static void dcmi_dma_callback(void *param) /* 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", + dev_err(dcmi->dev, "%s: No more buffer queued, cannot capture buffer\n", __func__); dcmi->errors_count++; dcmi->active = NULL; @@ -249,7 +249,7 @@ static void dcmi_dma_callback(void *param) list_del_init(&dcmi->active->list); if (dcmi_start_capture(dcmi)) { - dev_err(dcmi->dev, "%s: Cannot restart capture on DMA complete", + dev_err(dcmi->dev, "%s: Cannot restart capture on DMA complete\n", __func__); spin_unlock(&dcmi->irqlock); @@ -478,7 +478,7 @@ static int dcmi_buf_prepare(struct vb2_buffer *vb) vb2_set_plane_payload(&buf->vb.vb2_buf, 0, buf->size); - dev_dbg(dcmi->dev, "buffer[%d] phy=0x%pad size=%zu\n", + dev_dbg(dcmi->dev, "buffer[%d] phy=%pad size=%zu\n", vb->index, &buf->paddr, buf->size); } @@ -524,7 +524,7 @@ static int dcmi_start_streaming(struct vb2_queue *vq, unsigned int count) ret = clk_enable(dcmi->mclk); if (ret) { - dev_err(dcmi->dev, "%s: Failed to start streaming, cannot enable clock", + dev_err(dcmi->dev, "%s: Failed to start streaming, cannot enable clock\n", __func__); goto err_release_buffers; } @@ -600,7 +600,7 @@ static int dcmi_start_streaming(struct vb2_queue *vq, unsigned int count) ret = dcmi_start_capture(dcmi); if (ret) { - dev_err(dcmi->dev, "%s: Start streaming failed, cannot start capture", + dev_err(dcmi->dev, "%s: Start streaming failed, cannot start capture\n", __func__); spin_unlock_irq(&dcmi->irqlock); @@ -651,7 +651,8 @@ static void dcmi_stop_streaming(struct vb2_queue *vq) /* 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"); + dev_err(dcmi->dev, "%s: Failed to stop streaming, subdev streamoff error (%d)\n", + __func__, ret); dcmi->state = STOPPING; @@ -667,7 +668,8 @@ static void dcmi_stop_streaming(struct vb2_queue *vq) reg_clear(dcmi->regs, DCMI_CR, CR_ENABLE); if (!timeout) { - dev_err(dcmi->dev, "Timeout during stop streaming\n"); + dev_err(dcmi->dev, "%s: Timeout during stop streaming\n", + __func__); dcmi->state = STOPPED; } -- cgit v1.2.3 From aebd90aa3244e3539001919dcfbf00027d5ab63c Mon Sep 17 00:00:00 2001 From: Hugues Fruchet Date: Thu, 8 Feb 2018 06:00:45 -0500 Subject: media: stm32-dcmi: add g/s_parm framerate support Add g/s_parm framerate support by calling subdev g/s_frame_interval ops. This allows user to control sensor framerate by calling ioctl G/S_PARM. Signed-off-by: Hugues Fruchet Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/stm32/stm32-dcmi.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'drivers/media/platform/stm32/stm32-dcmi.c') diff --git a/drivers/media/platform/stm32/stm32-dcmi.c b/drivers/media/platform/stm32/stm32-dcmi.c index 6baea432e382..cc104a0805e2 100644 --- a/drivers/media/platform/stm32/stm32-dcmi.c +++ b/drivers/media/platform/stm32/stm32-dcmi.c @@ -1164,6 +1164,22 @@ static int dcmi_enum_framesizes(struct file *file, void *fh, return 0; } +static int dcmi_g_parm(struct file *file, void *priv, + struct v4l2_streamparm *p) +{ + struct stm32_dcmi *dcmi = video_drvdata(file); + + return v4l2_g_parm_cap(video_devdata(file), dcmi->entity.subdev, p); +} + +static int dcmi_s_parm(struct file *file, void *priv, + struct v4l2_streamparm *p) +{ + struct stm32_dcmi *dcmi = video_drvdata(file); + + return v4l2_s_parm_cap(video_devdata(file), dcmi->entity.subdev, p); +} + static int dcmi_enum_frameintervals(struct file *file, void *fh, struct v4l2_frmivalenum *fival) { @@ -1266,6 +1282,9 @@ static const struct v4l2_ioctl_ops dcmi_ioctl_ops = { .vidioc_g_input = dcmi_g_input, .vidioc_s_input = dcmi_s_input, + .vidioc_g_parm = dcmi_g_parm, + .vidioc_s_parm = dcmi_s_parm, + .vidioc_enum_framesizes = dcmi_enum_framesizes, .vidioc_enum_frameintervals = dcmi_enum_frameintervals, -- cgit v1.2.3 From f8c5363db34e4d1bbcbaeeb5cbb2762abb876162 Mon Sep 17 00:00:00 2001 From: Hugues Fruchet Date: Thu, 22 Feb 2018 04:49:14 -0500 Subject: media: stm32-dcmi: fix lock scheme Fix lock scheme leading to spurious freeze. Signed-off-by: Hugues Fruchet Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/stm32/stm32-dcmi.c | 57 +++++++++++++------------------ 1 file changed, 24 insertions(+), 33 deletions(-) (limited to 'drivers/media/platform/stm32/stm32-dcmi.c') diff --git a/drivers/media/platform/stm32/stm32-dcmi.c b/drivers/media/platform/stm32/stm32-dcmi.c index cc104a0805e2..37d82d3e33f9 100644 --- a/drivers/media/platform/stm32/stm32-dcmi.c +++ b/drivers/media/platform/stm32/stm32-dcmi.c @@ -197,7 +197,7 @@ static void dcmi_dma_callback(void *param) struct dma_tx_state state; enum dma_status status; - spin_lock(&dcmi->irqlock); + spin_lock_irq(&dcmi->irqlock); /* Check DMA status */ status = dmaengine_tx_status(chan, dcmi->dma_cookie, &state); @@ -239,7 +239,7 @@ static void dcmi_dma_callback(void *param) dcmi->errors_count++; dcmi->active = NULL; - spin_unlock(&dcmi->irqlock); + spin_unlock_irq(&dcmi->irqlock); return; } @@ -248,13 +248,11 @@ static void dcmi_dma_callback(void *param) list_del_init(&dcmi->active->list); - if (dcmi_start_capture(dcmi)) { + spin_unlock_irq(&dcmi->irqlock); + if (dcmi_start_capture(dcmi)) dev_err(dcmi->dev, "%s: Cannot restart capture on DMA complete\n", __func__); - - spin_unlock(&dcmi->irqlock); - return; - } + return; } break; @@ -263,7 +261,7 @@ static void dcmi_dma_callback(void *param) break; } - spin_unlock(&dcmi->irqlock); + spin_unlock_irq(&dcmi->irqlock); } static int dcmi_start_dma(struct stm32_dcmi *dcmi, @@ -360,7 +358,7 @@ static irqreturn_t dcmi_irq_thread(int irq, void *arg) { struct stm32_dcmi *dcmi = arg; - spin_lock(&dcmi->irqlock); + spin_lock_irq(&dcmi->irqlock); /* Stop capture is required */ if (dcmi->state == STOPPING) { @@ -370,7 +368,7 @@ static irqreturn_t dcmi_irq_thread(int irq, void *arg) complete(&dcmi->complete); - spin_unlock(&dcmi->irqlock); + spin_unlock_irq(&dcmi->irqlock); return IRQ_HANDLED; } @@ -383,35 +381,34 @@ static irqreturn_t dcmi_irq_thread(int irq, void *arg) __func__); dcmi->errors_count++; - dmaengine_terminate_all(dcmi->dma_chan); - dev_dbg(dcmi->dev, "Restarting capture after DCMI error\n"); - if (dcmi_start_capture(dcmi)) { + spin_unlock_irq(&dcmi->irqlock); + dmaengine_terminate_all(dcmi->dma_chan); + + 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; - } + return IRQ_HANDLED; } - spin_unlock(&dcmi->irqlock); + spin_unlock_irq(&dcmi->irqlock); return IRQ_HANDLED; } static irqreturn_t dcmi_irq_callback(int irq, void *arg) { struct stm32_dcmi *dcmi = arg; + unsigned long flags; - spin_lock(&dcmi->irqlock); + spin_lock_irqsave(&dcmi->irqlock, flags); 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); + spin_unlock_irqrestore(&dcmi->irqlock, flags); return IRQ_WAKE_THREAD; } @@ -490,9 +487,8 @@ 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); + spin_lock_irq(&dcmi->irqlock); if ((dcmi->state == RUNNING) && (!dcmi->active)) { dcmi->active = buf; @@ -500,19 +496,15 @@ static void dcmi_buf_queue(struct vb2_buffer *vb) dev_dbg(dcmi->dev, "Starting capture on buffer[%d] queued\n", buf->vb.vb2_buf.index); - if (dcmi_start_capture(dcmi)) { + spin_unlock_irq(&dcmi->irqlock); + 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_irq(&dcmi->irqlock); } - - spin_unlock_irqrestore(&dcmi->irqlock, flags); } static int dcmi_start_streaming(struct vb2_queue *vq, unsigned int count) @@ -598,20 +590,17 @@ static int dcmi_start_streaming(struct vb2_queue *vq, unsigned int count) dev_dbg(dcmi->dev, "Start streaming, starting capture\n"); + spin_unlock_irq(&dcmi->irqlock); ret = dcmi_start_capture(dcmi); if (ret) { dev_err(dcmi->dev, "%s: Start streaming failed, cannot start capture\n", __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: @@ -654,7 +643,9 @@ static void dcmi_stop_streaming(struct vb2_queue *vq) dev_err(dcmi->dev, "%s: Failed to stop streaming, subdev streamoff error (%d)\n", __func__, ret); + spin_lock_irq(&dcmi->irqlock); dcmi->state = STOPPING; + spin_unlock_irq(&dcmi->irqlock); timeout = wait_for_completion_interruptible_timeout(&dcmi->complete, time_ms); -- cgit v1.2.3 From a1029f552c3f66dc205eea6473e64040ca5023b5 Mon Sep 17 00:00:00 2001 From: Hugues Fruchet Date: Thu, 8 Mar 2018 05:46:23 -0500 Subject: media: stm32-dcmi: rework overrun/error case Do not stop/restart dma on overrun or errors. Dma will be restarted on current frame transfer completion. Frame transfer completion is ensured even if overrun or error occurs by DCMI continuous capture mode which restarts data transfer at next frame sync. Do no warn on overrun while in irq thread, this slows down system and lead to more overrun errors. Use a counter instead and log errors at stop streaming. Signed-off-by: Hugues Fruchet Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/stm32/stm32-dcmi.c | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) (limited to 'drivers/media/platform/stm32/stm32-dcmi.c') diff --git a/drivers/media/platform/stm32/stm32-dcmi.c b/drivers/media/platform/stm32/stm32-dcmi.c index 37d82d3e33f9..95ace0ffa687 100644 --- a/drivers/media/platform/stm32/stm32-dcmi.c +++ b/drivers/media/platform/stm32/stm32-dcmi.c @@ -160,6 +160,7 @@ struct stm32_dcmi { dma_cookie_t dma_cookie; u32 misr; int errors_count; + int overrun_count; int buffers_count; }; @@ -373,23 +374,9 @@ static irqreturn_t dcmi_irq_thread(int irq, void *arg) } 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++; - dev_dbg(dcmi->dev, "Restarting capture after DCMI error\n"); - - spin_unlock_irq(&dcmi->irqlock); - dmaengine_terminate_all(dcmi->dma_chan); - - if (dcmi_start_capture(dcmi)) - dev_err(dcmi->dev, "%s: Cannot restart capture on overflow or error\n", - __func__); - return IRQ_HANDLED; + if (dcmi->misr & IT_OVR) + dcmi->overrun_count++; } spin_unlock_irq(&dcmi->irqlock); @@ -572,6 +559,7 @@ static int dcmi_start_streaming(struct vb2_queue *vq, unsigned int count) dcmi->sequence = 0; dcmi->errors_count = 0; + dcmi->overrun_count = 0; dcmi->buffers_count = 0; dcmi->active = NULL; @@ -682,8 +670,13 @@ static void dcmi_stop_streaming(struct vb2_queue *vq) clk_disable(dcmi->mclk); - dev_dbg(dcmi->dev, "Stop streaming, errors=%d buffers=%d\n", - dcmi->errors_count, dcmi->buffers_count); + if (dcmi->errors_count) + dev_warn(dcmi->dev, "Some errors found while streaming: errors=%d (overrun=%d), buffers=%d\n", + dcmi->errors_count, dcmi->overrun_count, + dcmi->buffers_count); + dev_dbg(dcmi->dev, "Stop streaming, errors=%d (overrun=%d), buffers=%d\n", + dcmi->errors_count, dcmi->overrun_count, + dcmi->buffers_count); } static const struct vb2_ops dcmi_video_qops = { -- cgit v1.2.3 From b4ee319480e6d8a066b9522e65cee41795496951 Mon Sep 17 00:00:00 2001 From: Hugues Fruchet Date: Thu, 22 Feb 2018 04:51:27 -0500 Subject: media: stm32-dcmi: fix unnecessary parentheses Fix unnecessary parentheses in if conditions. Detected by checkpatch.pl --strict. Signed-off-by: Hugues Fruchet Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/stm32/stm32-dcmi.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers/media/platform/stm32/stm32-dcmi.c') diff --git a/drivers/media/platform/stm32/stm32-dcmi.c b/drivers/media/platform/stm32/stm32-dcmi.c index 95ace0ffa687..b46725b19499 100644 --- a/drivers/media/platform/stm32/stm32-dcmi.c +++ b/drivers/media/platform/stm32/stm32-dcmi.c @@ -477,7 +477,7 @@ static void dcmi_buf_queue(struct vb2_buffer *vb) spin_lock_irq(&dcmi->irqlock); - if ((dcmi->state == RUNNING) && (!dcmi->active)) { + if (dcmi->state == RUNNING && !dcmi->active) { dcmi->active = buf; dev_dbg(dcmi->dev, "Starting capture on buffer[%d] queued\n", @@ -730,7 +730,7 @@ static void __find_outer_frame_size(struct stm32_dcmi *dcmi, int h_err = (fsize->height - pix->height); int err = w_err + h_err; - if ((w_err >= 0) && (h_err >= 0) && (err < min_err)) { + if (w_err >= 0 && h_err >= 0 && err < min_err) { min_err = err; match = fsize; } @@ -1065,10 +1065,10 @@ static int dcmi_s_selection(struct file *file, void *priv, r.top = clamp_t(s32, r.top, 0, pix.height - r.height); r.left = clamp_t(s32, r.left, 0, pix.width - r.width); - if (!((r.top == dcmi->sd_bounds.top) && - (r.left == dcmi->sd_bounds.left) && - (r.width == dcmi->sd_bounds.width) && - (r.height == dcmi->sd_bounds.height))) { + if (!(r.top == dcmi->sd_bounds.top && + r.left == dcmi->sd_bounds.left && + r.width == dcmi->sd_bounds.width && + r.height == dcmi->sd_bounds.height)) { /* Crop if request is different than sensor resolution */ dcmi->do_crop = true; dcmi->crop = r; -- cgit v1.2.3 From 3e187c81d405474ef6361acf6d875657940cc2c0 Mon Sep 17 00:00:00 2001 From: Hugues Fruchet Date: Wed, 28 Feb 2018 12:20:16 -0500 Subject: media: stm32-dcmi: add JPEG support Add DCMI JPEG support. Signed-off-by: Hugues Fruchet Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/stm32/stm32-dcmi.c | 193 ++++++++++++++++++++++-------- 1 file changed, 146 insertions(+), 47 deletions(-) (limited to 'drivers/media/platform/stm32/stm32-dcmi.c') diff --git a/drivers/media/platform/stm32/stm32-dcmi.c b/drivers/media/platform/stm32/stm32-dcmi.c index b46725b19499..2e1933d872ee 100644 --- a/drivers/media/platform/stm32/stm32-dcmi.c +++ b/drivers/media/platform/stm32/stm32-dcmi.c @@ -93,6 +93,11 @@ enum state { #define MIN_HEIGHT 16U #define MAX_HEIGHT 2048U +#define MIN_JPEG_WIDTH 16U +#define MAX_JPEG_WIDTH 2592U +#define MIN_JPEG_HEIGHT 16U +#define MAX_JPEG_HEIGHT 2592U + #define TIMEOUT_MS 1000 struct dcmi_graph_entity { @@ -191,14 +196,67 @@ static inline void reg_clear(void __iomem *base, u32 reg, u32 mask) static int dcmi_start_capture(struct stm32_dcmi *dcmi); +static void dcmi_buffer_done(struct stm32_dcmi *dcmi, + struct dcmi_buf *buf, + size_t bytesused, + int err) +{ + struct vb2_v4l2_buffer *vbuf; + + if (!buf) + return; + + vbuf = &buf->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, bytesused); + vb2_buffer_done(&vbuf->vb2_buf, + err ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE); + dev_dbg(dcmi->dev, "buffer[%d] done seq=%d, bytesused=%zu\n", + vbuf->vb2_buf.index, vbuf->sequence, bytesused); + + dcmi->buffers_count++; + dcmi->active = NULL; +} + +static int dcmi_restart_capture(struct stm32_dcmi *dcmi) +{ + spin_lock_irq(&dcmi->irqlock); + + if (dcmi->state != RUNNING) { + spin_unlock_irq(&dcmi->irqlock); + return -EINVAL; + } + + /* Restart a new DMA transfer with next buffer */ + if (list_empty(&dcmi->buffers)) { + dev_err(dcmi->dev, "%s: No more buffer queued, cannot capture buffer\n", + __func__); + dcmi->errors_count++; + dcmi->active = NULL; + + spin_unlock_irq(&dcmi->irqlock); + return -EINVAL; + } + + dcmi->active = list_entry(dcmi->buffers.next, + struct dcmi_buf, list); + list_del_init(&dcmi->active->list); + + spin_unlock_irq(&dcmi->irqlock); + + return dcmi_start_capture(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_irq(&dcmi->irqlock); + struct dcmi_buf *buf = dcmi->active; /* Check DMA status */ status = dmaengine_tx_status(chan, dcmi->dma_cookie, &state); @@ -216,53 +274,18 @@ static void dcmi_dma_callback(void *param) 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\n", - __func__); - dcmi->errors_count++; - dcmi->active = NULL; - - spin_unlock_irq(&dcmi->irqlock); - return; - } - - dcmi->active = list_entry(dcmi->buffers.next, - struct dcmi_buf, list); - - list_del_init(&dcmi->active->list); - - spin_unlock_irq(&dcmi->irqlock); - if (dcmi_start_capture(dcmi)) - dev_err(dcmi->dev, "%s: Cannot restart capture on DMA complete\n", - __func__); - return; - } + /* Return buffer to V4L2 */ + dcmi_buffer_done(dcmi, buf, buf->size, 0); + /* Restart capture */ + if (dcmi_restart_capture(dcmi)) + dev_err(dcmi->dev, "%s: Cannot restart capture on DMA complete\n", + __func__); break; default: dev_err(dcmi->dev, "%s: Received unknown status\n", __func__); break; } - - spin_unlock_irq(&dcmi->irqlock); } static int dcmi_start_dma(struct stm32_dcmi *dcmi, @@ -355,6 +378,52 @@ static void dcmi_set_crop(struct stm32_dcmi *dcmi) reg_set(dcmi->regs, DCMI_CR, CR_CROP); } +static void dcmi_process_jpeg(struct stm32_dcmi *dcmi) +{ + struct dma_tx_state state; + enum dma_status status; + struct dma_chan *chan = dcmi->dma_chan; + struct dcmi_buf *buf = dcmi->active; + + if (!buf) + return; + + /* + * Because of variable JPEG buffer size sent by sensor, + * DMA transfer never completes due to transfer size + * never reached. + * In order to ensure that all the JPEG data are transferred + * in active buffer memory, DMA is drained. + * Then DMA tx status gives the amount of data transferred + * to memory, which is then returned to V4L2 through the active + * buffer payload. + */ + + /* Drain DMA */ + dmaengine_synchronize(chan); + + /* Get DMA residue to get JPEG size */ + status = dmaengine_tx_status(chan, dcmi->dma_cookie, &state); + if (status != DMA_ERROR && state.residue < buf->size) { + /* Return JPEG buffer to V4L2 with received JPEG buffer size */ + dcmi_buffer_done(dcmi, buf, buf->size - state.residue, 0); + } else { + dcmi->errors_count++; + dev_err(dcmi->dev, "%s: Cannot get JPEG size from DMA\n", + __func__); + /* Return JPEG buffer to V4L2 in ERROR state */ + dcmi_buffer_done(dcmi, buf, 0, -EIO); + } + + /* Abort DMA operation */ + dmaengine_terminate_all(dcmi->dma_chan); + + /* Restart capture */ + if (dcmi_restart_capture(dcmi)) + dev_err(dcmi->dev, "%s: Cannot restart capture on JPEG received\n", + __func__); +} + static irqreturn_t dcmi_irq_thread(int irq, void *arg) { struct stm32_dcmi *dcmi = arg; @@ -379,6 +448,14 @@ static irqreturn_t dcmi_irq_thread(int irq, void *arg) dcmi->overrun_count++; } + if (dcmi->sd_format->fourcc == V4L2_PIX_FMT_JPEG && + dcmi->misr & IT_FRAME) { + /* JPEG received */ + spin_unlock_irq(&dcmi->irqlock); + dcmi_process_jpeg(dcmi); + return IRQ_HANDLED; + } + spin_unlock_irq(&dcmi->irqlock); return IRQ_HANDLED; } @@ -552,6 +629,10 @@ static int dcmi_start_streaming(struct vb2_queue *vq, unsigned int count) if (dcmi->do_crop) dcmi_set_crop(dcmi); + /* Enable jpeg capture */ + if (dcmi->sd_format->fourcc == V4L2_PIX_FMT_JPEG) + reg_set(dcmi->regs, DCMI_CR, CR_CM);/* Snapshot mode */ + /* Enable dcmi */ reg_set(dcmi->regs, DCMI_CR, CR_ENABLE); @@ -752,6 +833,7 @@ static int dcmi_try_fmt(struct stm32_dcmi *dcmi, struct v4l2_format *f, struct v4l2_subdev_format format = { .which = V4L2_SUBDEV_FORMAT_TRY, }; + bool do_crop; int ret; sd_fmt = find_format_by_fourcc(dcmi, pix->pixelformat); @@ -761,10 +843,19 @@ static int dcmi_try_fmt(struct stm32_dcmi *dcmi, struct v4l2_format *f, } /* Limit to hardware capabilities */ - pix->width = clamp(pix->width, MIN_WIDTH, MAX_WIDTH); - pix->height = clamp(pix->height, MIN_HEIGHT, MAX_HEIGHT); + if (pix->pixelformat == V4L2_PIX_FMT_JPEG) { + pix->width = clamp(pix->width, MIN_JPEG_WIDTH, MAX_JPEG_WIDTH); + pix->height = + clamp(pix->height, MIN_JPEG_HEIGHT, MAX_JPEG_HEIGHT); + } else { + pix->width = clamp(pix->width, MIN_WIDTH, MAX_WIDTH); + pix->height = clamp(pix->height, MIN_HEIGHT, MAX_HEIGHT); + } + + /* No crop if JPEG is requested */ + do_crop = dcmi->do_crop && (pix->pixelformat != V4L2_PIX_FMT_JPEG); - if (dcmi->do_crop && dcmi->num_of_sd_framesizes) { + if (do_crop && dcmi->num_of_sd_framesizes) { struct dcmi_framesize outer_sd_fsize; /* * If crop is requested and sensor have discrete frame sizes, @@ -788,7 +879,7 @@ static int dcmi_try_fmt(struct stm32_dcmi *dcmi, struct v4l2_format *f, sd_fsize.width = pix->width; sd_fsize.height = pix->height; - if (dcmi->do_crop) { + if (do_crop) { struct v4l2_rect c = dcmi->crop; struct v4l2_rect max_rect; @@ -843,6 +934,10 @@ static int dcmi_set_fmt(struct stm32_dcmi *dcmi, struct v4l2_format *f) if (ret) return ret; + /* Disable crop if JPEG is requested */ + if (pix->pixelformat == V4L2_PIX_FMT_JPEG) + dcmi->do_crop = false; + /* pix to mbus format */ v4l2_fill_mbus_format(mf, pix, sd_format->mbus_code); @@ -1334,6 +1429,10 @@ static const struct dcmi_format dcmi_formats[] = { .fourcc = V4L2_PIX_FMT_UYVY, .mbus_code = MEDIA_BUS_FMT_UYVY8_2X8, .bpp = 2, + }, { + .fourcc = V4L2_PIX_FMT_JPEG, + .mbus_code = MEDIA_BUS_FMT_JPEG_1X8, + .bpp = 1, }, }; -- cgit v1.2.3