media: stm32-dcmi: add JPEG support
authorHugues Fruchet <hugues.fruchet@st.com>
Wed, 28 Feb 2018 17:20:16 +0000 (12:20 -0500)
committerMauro Carvalho Chehab <mchehab@s-opensource.com>
Wed, 21 Mar 2018 15:53:09 +0000 (11:53 -0400)
Add DCMI JPEG support.

Signed-off-by: Hugues Fruchet <hugues.fruchet@st.com>
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
drivers/media/platform/stm32/stm32-dcmi.c

index b46725b..2e1933d 100644 (file)
@@ -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,
        },
 };