Commit 3e187c81 authored by Hugues Fruchet's avatar Hugues Fruchet Committed by Mauro Carvalho Chehab
Browse files

media: stm32-dcmi: add JPEG support



Add DCMI JPEG support.

Signed-off-by: default avatarHugues Fruchet <hugues.fruchet@st.com>
Signed-off-by: default avatarHans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@s-opensource.com>
parent b4ee3194
Loading
Loading
Loading
Loading
+146 −47
Original line number Diff line number Diff line
@@ -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,49 +196,41 @@ static inline void reg_clear(void __iomem *base, u32 reg, u32 mask)

static int dcmi_start_capture(struct stm32_dcmi *dcmi);

static void dcmi_dma_callback(void *param)
static void dcmi_buffer_done(struct stm32_dcmi *dcmi,
			     struct dcmi_buf *buf,
			     size_t bytesused,
			     int err)
{
	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);

	/* Check DMA status */
	status = dmaengine_tx_status(chan, dcmi->dma_cookie, &state);
	struct vb2_v4l2_buffer *vbuf;

	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 (!buf)
		return;

		if (dcmi->active) {
			struct dcmi_buf *buf = dcmi->active;
			struct vb2_v4l2_buffer *vbuf = &dcmi->active->vb;
	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, 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);
	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 (dcmi->state == RUNNING) {
	if (list_empty(&dcmi->buffers)) {
		dev_err(dcmi->dev, "%s: No more buffer queued, cannot capture buffer\n",
			__func__);
@@ -241,28 +238,54 @@ static void dcmi_dma_callback(void *param)
		dcmi->active = NULL;

		spin_unlock_irq(&dcmi->irqlock);
				return;
		return -EINVAL;
	}

	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 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;
	struct dcmi_buf *buf = dcmi->active;

	/* 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__);

		/* 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 */
	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,
	},
};