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

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
...@@ -93,6 +93,11 @@ enum state { ...@@ -93,6 +93,11 @@ enum state {
#define MIN_HEIGHT 16U #define MIN_HEIGHT 16U
#define MAX_HEIGHT 2048U #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 #define TIMEOUT_MS 1000
struct dcmi_graph_entity { struct dcmi_graph_entity {
...@@ -191,49 +196,41 @@ static inline void reg_clear(void __iomem *base, u32 reg, u32 mask) ...@@ -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 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 vb2_v4l2_buffer *vbuf;
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);
switch (status) { if (!buf)
case DMA_IN_PROGRESS: return;
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) { vbuf = &buf->vb;
struct dcmi_buf *buf = dcmi->active;
struct vb2_v4l2_buffer *vbuf = &dcmi->active->vb;
vbuf->sequence = dcmi->sequence++; vbuf->sequence = dcmi->sequence++;
vbuf->field = V4L2_FIELD_NONE; vbuf->field = V4L2_FIELD_NONE;
vbuf->vb2_buf.timestamp = ktime_get_ns(); vbuf->vb2_buf.timestamp = ktime_get_ns();
vb2_set_plane_payload(&vbuf->vb2_buf, 0, buf->size); vb2_set_plane_payload(&vbuf->vb2_buf, 0, bytesused);
vb2_buffer_done(&vbuf->vb2_buf, VB2_BUF_STATE_DONE); vb2_buffer_done(&vbuf->vb2_buf,
dev_dbg(dcmi->dev, "buffer[%d] done seq=%d\n", err ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE);
vbuf->vb2_buf.index, vbuf->sequence); dev_dbg(dcmi->dev, "buffer[%d] done seq=%d, bytesused=%zu\n",
vbuf->vb2_buf.index, vbuf->sequence, bytesused);
dcmi->buffers_count++; dcmi->buffers_count++;
dcmi->active = NULL; 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 */ /* Restart a new DMA transfer with next buffer */
if (dcmi->state == RUNNING) {
if (list_empty(&dcmi->buffers)) { if (list_empty(&dcmi->buffers)) {
dev_err(dcmi->dev, "%s: No more buffer queued, cannot capture buffer\n", dev_err(dcmi->dev, "%s: No more buffer queued, cannot capture buffer\n",
__func__); __func__);
...@@ -241,28 +238,54 @@ static void dcmi_dma_callback(void *param) ...@@ -241,28 +238,54 @@ static void dcmi_dma_callback(void *param)
dcmi->active = NULL; dcmi->active = NULL;
spin_unlock_irq(&dcmi->irqlock); spin_unlock_irq(&dcmi->irqlock);
return; return -EINVAL;
} }
dcmi->active = list_entry(dcmi->buffers.next, dcmi->active = list_entry(dcmi->buffers.next,
struct dcmi_buf, list); struct dcmi_buf, list);
list_del_init(&dcmi->active->list); list_del_init(&dcmi->active->list);
spin_unlock_irq(&dcmi->irqlock); spin_unlock_irq(&dcmi->irqlock);
if (dcmi_start_capture(dcmi))
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", dev_err(dcmi->dev, "%s: Cannot restart capture on DMA complete\n",
__func__); __func__);
return;
}
break; break;
default: default:
dev_err(dcmi->dev, "%s: Received unknown status\n", __func__); dev_err(dcmi->dev, "%s: Received unknown status\n", __func__);
break; break;
} }
spin_unlock_irq(&dcmi->irqlock);
} }
static int dcmi_start_dma(struct stm32_dcmi *dcmi, static int dcmi_start_dma(struct stm32_dcmi *dcmi,
...@@ -355,6 +378,52 @@ static void dcmi_set_crop(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); 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) static irqreturn_t dcmi_irq_thread(int irq, void *arg)
{ {
struct stm32_dcmi *dcmi = arg; struct stm32_dcmi *dcmi = arg;
...@@ -379,6 +448,14 @@ static irqreturn_t dcmi_irq_thread(int irq, void *arg) ...@@ -379,6 +448,14 @@ static irqreturn_t dcmi_irq_thread(int irq, void *arg)
dcmi->overrun_count++; 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); spin_unlock_irq(&dcmi->irqlock);
return IRQ_HANDLED; return IRQ_HANDLED;
} }
...@@ -552,6 +629,10 @@ static int dcmi_start_streaming(struct vb2_queue *vq, unsigned int count) ...@@ -552,6 +629,10 @@ static int dcmi_start_streaming(struct vb2_queue *vq, unsigned int count)
if (dcmi->do_crop) if (dcmi->do_crop)
dcmi_set_crop(dcmi); 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 */ /* Enable dcmi */
reg_set(dcmi->regs, DCMI_CR, CR_ENABLE); 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, ...@@ -752,6 +833,7 @@ static int dcmi_try_fmt(struct stm32_dcmi *dcmi, struct v4l2_format *f,
struct v4l2_subdev_format format = { struct v4l2_subdev_format format = {
.which = V4L2_SUBDEV_FORMAT_TRY, .which = V4L2_SUBDEV_FORMAT_TRY,
}; };
bool do_crop;
int ret; int ret;
sd_fmt = find_format_by_fourcc(dcmi, pix->pixelformat); 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, ...@@ -761,10 +843,19 @@ static int dcmi_try_fmt(struct stm32_dcmi *dcmi, struct v4l2_format *f,
} }
/* Limit to hardware capabilities */ /* 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->width = clamp(pix->width, MIN_WIDTH, MAX_WIDTH);
pix->height = clamp(pix->height, MIN_HEIGHT, MAX_HEIGHT); 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; struct dcmi_framesize outer_sd_fsize;
/* /*
* If crop is requested and sensor have discrete frame sizes, * 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, ...@@ -788,7 +879,7 @@ static int dcmi_try_fmt(struct stm32_dcmi *dcmi, struct v4l2_format *f,
sd_fsize.width = pix->width; sd_fsize.width = pix->width;
sd_fsize.height = pix->height; sd_fsize.height = pix->height;
if (dcmi->do_crop) { if (do_crop) {
struct v4l2_rect c = dcmi->crop; struct v4l2_rect c = dcmi->crop;
struct v4l2_rect max_rect; struct v4l2_rect max_rect;
...@@ -843,6 +934,10 @@ static int dcmi_set_fmt(struct stm32_dcmi *dcmi, struct v4l2_format *f) ...@@ -843,6 +934,10 @@ static int dcmi_set_fmt(struct stm32_dcmi *dcmi, struct v4l2_format *f)
if (ret) if (ret)
return ret; return ret;
/* Disable crop if JPEG is requested */
if (pix->pixelformat == V4L2_PIX_FMT_JPEG)
dcmi->do_crop = false;
/* pix to mbus format */ /* pix to mbus format */
v4l2_fill_mbus_format(mf, pix, v4l2_fill_mbus_format(mf, pix,
sd_format->mbus_code); sd_format->mbus_code);
...@@ -1334,6 +1429,10 @@ static const struct dcmi_format dcmi_formats[] = { ...@@ -1334,6 +1429,10 @@ static const struct dcmi_format dcmi_formats[] = {
.fourcc = V4L2_PIX_FMT_UYVY, .fourcc = V4L2_PIX_FMT_UYVY,
.mbus_code = MEDIA_BUS_FMT_UYVY8_2X8, .mbus_code = MEDIA_BUS_FMT_UYVY8_2X8,
.bpp = 2, .bpp = 2,
}, {
.fourcc = V4L2_PIX_FMT_JPEG,
.mbus_code = MEDIA_BUS_FMT_JPEG_1X8,
.bpp = 1,
}, },
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment