Add support for transparent DMA transfer of JPEG data with MIPI-CSI2
USER1 format. In JPEG mode the color effect, scaling and cropping
is not supported as well as image rotation and flipping thus these
controls are marked as inactive if V4L2_PIX_FMT_JPEG pixel format
was selected.

Signed-off-by: Sylwester Nawrocki <s.nawro...@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.p...@samsung.com>
---
 drivers/media/video/s5p-fimc/fimc-capture.c |   17 ++++++-
 drivers/media/video/s5p-fimc/fimc-core.c    |   70 ++++++++++++++-------------
 drivers/media/video/s5p-fimc/fimc-core.h    |    7 ++-
 drivers/media/video/s5p-fimc/fimc-reg.c     |   31 ++++++++----
 drivers/media/video/s5p-fimc/regs-fimc.h    |    8 +--
 5 files changed, 81 insertions(+), 52 deletions(-)

diff --git a/drivers/media/video/s5p-fimc/fimc-capture.c 
b/drivers/media/video/s5p-fimc/fimc-capture.c
index 62fd8a1..adbbb63 100644
--- a/drivers/media/video/s5p-fimc/fimc-capture.c
+++ b/drivers/media/video/s5p-fimc/fimc-capture.c
@@ -60,7 +60,7 @@ static int fimc_init_capture(struct fimc_dev *fimc)
                fimc_hw_set_mainscaler(ctx);
                fimc_hw_set_target_format(ctx);
                fimc_hw_set_rotation(ctx);
-               fimc_hw_set_effect(ctx);
+               fimc_hw_set_effect(ctx, false);
                fimc_hw_set_output_path(ctx);
                fimc_hw_set_out_dma(ctx);
                clear_bit(ST_CAPT_APPLY_CFG, &fimc->state);
@@ -731,6 +731,17 @@ static int fimc_cap_try_fmt_mplane(struct file *file, void 
*fh,
        return 0;
 }
 
+static void fimc_capture_mark_jpeg_xfer(struct fimc_ctx *ctx, bool jpeg)
+{
+       ctx->scaler.enabled = !jpeg;
+       fimc_ctrls_activate(ctx, !jpeg);
+
+       if (jpeg)
+               set_bit(ST_CAPT_JPEG, &ctx->fimc_dev->state);
+       else
+               clear_bit(ST_CAPT_JPEG, &ctx->fimc_dev->state);
+}
+
 static int fimc_capture_set_format(struct fimc_dev *fimc, struct v4l2_format 
*f)
 {
        struct fimc_ctx *ctx = fimc->vid_cap.ctx;
@@ -783,6 +794,8 @@ static int fimc_capture_set_format(struct fimc_dev *fimc, 
struct v4l2_format *f)
        if (!(ctx->state & FIMC_DST_CROP))
                set_frame_crop(ff, 0, 0, pix->width, pix->height);
 
+       fimc_capture_mark_jpeg_xfer(ctx, fimc_fmt_is_jpeg(ff->fmt->color));
+
        /* Reset cropping and set format at the camera interface input */
        if (!fimc->vid_cap.user_subdev_api) {
                ctx->s_frame.fmt = s_fmt;
@@ -1133,6 +1146,8 @@ static int fimc_subdev_set_fmt(struct v4l2_subdev *sd,
                *mf = fmt->format;
                return 0;
        }
+       fimc_capture_mark_jpeg_xfer(ctx, fimc_fmt_is_jpeg(ffmt->color));
+
        ff = fmt->pad == FIMC_SD_PAD_SINK ?
                &ctx->s_frame : &ctx->d_frame;
 
diff --git a/drivers/media/video/s5p-fimc/fimc-core.c 
b/drivers/media/video/s5p-fimc/fimc-core.c
index ffcccb2..52f0862 100644
--- a/drivers/media/video/s5p-fimc/fimc-core.c
+++ b/drivers/media/video/s5p-fimc/fimc-core.c
@@ -159,22 +159,28 @@ static struct fimc_fmt fimc_formats[] = {
                .memplanes      = 2,
                .colplanes      = 2,
                .flags          = FMT_FLAGS_M2M,
+       }, {
+               .name           = "JPEG encoded data",
+               .fourcc         = V4L2_PIX_FMT_JPEG,
+               .color          = S5P_FIMC_JPEG,
+               .depth          = { 8 },
+               .memplanes      = 1,
+               .colplanes      = 1,
+               .mbus_code      = V4L2_MBUS_FMT_JPEG_1X8,
+               .flags          = FMT_FLAGS_CAM,
        },
 };
 
-int fimc_check_scaler_ratio(int sw, int sh, int dw, int dh, int rot)
+int fimc_check_scaler_ratio(struct fimc_ctx *ctx, int sw, int sh,
+                           int dw, int dh, int rotation)
 {
-       int tx, ty;
+       if (rotation == 90 || rotation == 270)
+               swap(dw, dh);
 
-       if (rot == 90 || rot == 270) {
-               ty = dw;
-               tx = dh;
-       } else {
-               tx = dw;
-               ty = dh;
-       }
+       if (!ctx->scaler.enabled)
+               return (sw == dw && sh == dh) ? 0 : -EINVAL;
 
-       if ((sw >= SCALER_MAX_HRATIO * tx) || (sh >= SCALER_MAX_VRATIO * ty))
+       if ((sw >= SCALER_MAX_HRATIO * dw) || (sh >= SCALER_MAX_VRATIO * dh))
                return -EINVAL;
 
        return 0;
@@ -321,7 +327,7 @@ static int stop_streaming(struct vb2_queue *q)
        return 0;
 }
 
-static void fimc_capture_irq_handler(struct fimc_dev *fimc)
+void fimc_capture_irq_handler(struct fimc_dev *fimc, bool final)
 {
        struct fimc_vid_cap *cap = &fimc->vid_cap;
        struct fimc_vid_buffer *v_buf;
@@ -329,7 +335,7 @@ static void fimc_capture_irq_handler(struct fimc_dev *fimc)
        struct timespec ts;
 
        if (!list_empty(&cap->active_buf_q) &&
-           test_bit(ST_CAPT_RUN, &fimc->state)) {
+           test_bit(ST_CAPT_RUN, &fimc->state) && final) {
                ktime_get_real_ts(&ts);
 
                v_buf = active_queue_pop(cap);
@@ -364,7 +370,8 @@ static void fimc_capture_irq_handler(struct fimc_dev *fimc)
        }
 
        if (cap->active_buf_cnt == 0) {
-               clear_bit(ST_CAPT_RUN, &fimc->state);
+               if (final)
+                       clear_bit(ST_CAPT_RUN, &fimc->state);
 
                if (++cap->buf_index >= FIMC_MAX_OUT_BUFS)
                        cap->buf_index = 0;
@@ -407,14 +414,12 @@ static irqreturn_t fimc_irq_handler(int irq, void *priv)
                        spin_unlock(&ctx->slock);
                }
                return IRQ_HANDLED;
-       } else {
-               if (test_bit(ST_CAPT_PEND, &fimc->state)) {
-                       fimc_capture_irq_handler(fimc);
-
-                       if (cap->active_buf_cnt == 1) {
-                               fimc_deactivate_capture(fimc);
-                               clear_bit(ST_CAPT_STREAM, &fimc->state);
-                       }
+       } else if (test_bit(ST_CAPT_PEND, &fimc->state)) {
+               fimc_capture_irq_handler(fimc,
+                                !test_bit(ST_CAPT_JPEG, &fimc->state));
+               if (cap->active_buf_cnt == 1) {
+                       fimc_deactivate_capture(fimc);
+                       clear_bit(ST_CAPT_STREAM, &fimc->state);
                }
        }
 out:
@@ -586,9 +591,6 @@ int fimc_prepare_config(struct fimc_ctx *ctx, u32 flags)
                fimc_set_yuv_order(ctx);
        }
 
-       /* Input DMA mode is not allowed when the scaler is disabled. */
-       ctx->scaler.enabled = 1;
-
        if (flags & FIMC_SRC_ADDR) {
                vb = v4l2_m2m_next_src_buf(ctx->m2m_ctx);
                ret = fimc_prepare_addr(ctx, vb, s_frame, &s_frame->paddr);
@@ -643,7 +645,7 @@ static void fimc_dma_run(void *priv)
                fimc_hw_set_mainscaler(ctx);
                fimc_hw_set_target_format(ctx);
                fimc_hw_set_rotation(ctx);
-               fimc_hw_set_effect(ctx);
+               fimc_hw_set_effect(ctx, false);
        }
 
        fimc_hw_set_output_path(ctx);
@@ -773,7 +775,7 @@ static int fimc_s_ctrl(struct v4l2_ctrl *ctrl)
        case V4L2_CID_ROTATE:
                if (fimc_capture_pending(fimc) ||
                    fimc_ctx_state_is_set(FIMC_DST_FMT | FIMC_SRC_FMT, ctx)) {
-                       ret = fimc_check_scaler_ratio(ctx->s_frame.width,
+                       ret = fimc_check_scaler_ratio(ctx, ctx->s_frame.width,
                                        ctx->s_frame.height, ctx->d_frame.width,
                                        ctx->d_frame.height, ctrl->val);
                }
@@ -1098,6 +1100,8 @@ static int fimc_m2m_s_fmt_mplane(struct file *file, void 
*fh,
 
        fimc_fill_frame(frame, f);
 
+       ctx->scaler.enabled = 1;
+
        if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
                fimc_ctx_state_lock_set(FIMC_PARAMS | FIMC_DST_FMT, ctx);
        else
@@ -1269,15 +1273,13 @@ static int fimc_m2m_s_crop(struct file *file, void *fh, 
struct v4l2_crop *cr)
        /* Check to see if scaling ratio is within supported range */
        if (fimc_ctx_state_is_set(FIMC_DST_FMT | FIMC_SRC_FMT, ctx)) {
                if (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
-                       ret = fimc_check_scaler_ratio(cr->c.width, cr->c.height,
-                                                     ctx->d_frame.width,
-                                                     ctx->d_frame.height,
-                                                     ctx->rotation);
+                       ret = fimc_check_scaler_ratio(ctx, cr->c.width,
+                                       cr->c.height, ctx->d_frame.width,
+                                       ctx->d_frame.height, ctx->rotation);
                } else {
-                       ret = fimc_check_scaler_ratio(ctx->s_frame.width,
-                                                     ctx->s_frame.height,
-                                                     cr->c.width, cr->c.height,
-                                                     ctx->rotation);
+                       ret = fimc_check_scaler_ratio(ctx, ctx->s_frame.width,
+                                       ctx->s_frame.height, cr->c.width,
+                                       cr->c.height, ctx->rotation);
                }
                if (ret) {
                        v4l2_err(fimc->m2m.vfd, "Out of scaler range\n");
diff --git a/drivers/media/video/s5p-fimc/fimc-core.h 
b/drivers/media/video/s5p-fimc/fimc-core.h
index e4520b2..9b44875 100644
--- a/drivers/media/video/s5p-fimc/fimc-core.h
+++ b/drivers/media/video/s5p-fimc/fimc-core.h
@@ -66,6 +66,7 @@ enum fimc_dev_flags {
        ST_CAPT_SHUT,
        ST_CAPT_BUSY,
        ST_CAPT_APPLY_CFG,
+       ST_CAPT_JPEG,
 };
 
 #define fimc_m2m_active(dev) test_bit(ST_M2M_RUN, &(dev)->state)
@@ -669,7 +670,7 @@ void fimc_hw_en_irq(struct fimc_dev *fimc, int enable);
 void fimc_hw_set_prescaler(struct fimc_ctx *ctx);
 void fimc_hw_set_mainscaler(struct fimc_ctx *ctx);
 void fimc_hw_en_capture(struct fimc_ctx *ctx);
-void fimc_hw_set_effect(struct fimc_ctx *ctx);
+void fimc_hw_set_effect(struct fimc_ctx *ctx, bool active);
 void fimc_hw_set_in_dma(struct fimc_ctx *ctx);
 void fimc_hw_set_input_path(struct fimc_ctx *ctx);
 void fimc_hw_set_output_path(struct fimc_ctx *ctx);
@@ -697,7 +698,8 @@ void fimc_adjust_mplane_format(struct fimc_fmt *fmt, u32 
width, u32 height,
 struct fimc_fmt *fimc_find_format(u32 *pixelformat, u32 *mbus_code,
                                  unsigned int mask, int index);
 
-int fimc_check_scaler_ratio(int sw, int sh, int dw, int dh, int rot);
+int fimc_check_scaler_ratio(struct fimc_ctx *ctx, int sw, int sh,
+                           int dw, int dh, int rotation);
 int fimc_set_scaler_info(struct fimc_ctx *ctx);
 int fimc_prepare_config(struct fimc_ctx *ctx, u32 flags);
 int fimc_prepare_addr(struct fimc_ctx *ctx, struct vb2_buffer *vb,
@@ -705,6 +707,7 @@ int fimc_prepare_addr(struct fimc_ctx *ctx, struct 
vb2_buffer *vb,
 void fimc_prepare_dma_offset(struct fimc_ctx *ctx, struct fimc_frame *f);
 void fimc_set_yuv_order(struct fimc_ctx *ctx);
 void fimc_fill_frame(struct fimc_frame *frame, struct v4l2_format *f);
+void fimc_capture_irq_handler(struct fimc_dev *fimc, bool done);
 
 int fimc_register_m2m_device(struct fimc_dev *fimc,
                             struct v4l2_device *v4l2_dev);
diff --git a/drivers/media/video/s5p-fimc/fimc-reg.c 
b/drivers/media/video/s5p-fimc/fimc-reg.c
index a1fff02..2a1ae51 100644
--- a/drivers/media/video/s5p-fimc/fimc-reg.c
+++ b/drivers/media/video/s5p-fimc/fimc-reg.c
@@ -352,17 +352,19 @@ void fimc_hw_en_capture(struct fimc_ctx *ctx)
        writel(cfg | S5P_CIIMGCPT_IMGCPTEN, dev->regs + S5P_CIIMGCPT);
 }
 
-void fimc_hw_set_effect(struct fimc_ctx *ctx)
+void fimc_hw_set_effect(struct fimc_ctx *ctx, bool active)
 {
        struct fimc_dev *dev = ctx->fimc_dev;
        struct fimc_effect *effect = &ctx->effect;
-       u32 cfg = (S5P_CIIMGEFF_IE_ENABLE | S5P_CIIMGEFF_IE_SC_AFTER);
-
-       cfg |= effect->type;
+       u32 cfg = 0;
 
-       if (effect->type == S5P_FIMC_EFFECT_ARBITRARY) {
-               cfg |= S5P_CIIMGEFF_PAT_CB(effect->pat_cb);
-               cfg |= S5P_CIIMGEFF_PAT_CR(effect->pat_cr);
+       if (active) {
+               cfg |= S5P_CIIMGEFF_IE_SC_AFTER | S5P_CIIMGEFF_IE_ENABLE;
+               cfg |= effect->type;
+               if (effect->type == S5P_FIMC_EFFECT_ARBITRARY) {
+                       cfg |= S5P_CIIMGEFF_PAT_CB(effect->pat_cb);
+                       cfg |= S5P_CIIMGEFF_PAT_CR(effect->pat_cr);
+               }
        }
 
        writel(cfg, dev->regs + S5P_CIIMGEFF);
@@ -592,6 +594,9 @@ int fimc_hw_set_camera_source(struct fimc_dev *fimc,
                        else if (bus_width == 16)
                                cfg |= S5P_CISRCFMT_ITU601_16BIT;
                } /* else defaults to ITU-R BT.656 8-bit */
+       } else if (cam->bus_type == FIMC_MIPI_CSI2) {
+               if (fimc_fmt_is_jpeg(f->fmt->color))
+                       cfg |= S5P_CISRCFMT_ITU601_8BIT;
        }
 
        cfg |= S5P_CISRCFMT_HSIZE(f->o_width) | S5P_CISRCFMT_VSIZE(f->o_height);
@@ -633,7 +638,7 @@ int fimc_hw_set_camera_type(struct fimc_dev *fimc,
        /* Select ITU B interface, disable Writeback path and test pattern. */
        cfg &= ~(S5P_CIGCTRL_TESTPAT_MASK | S5P_CIGCTRL_SELCAM_ITU_A |
                S5P_CIGCTRL_SELCAM_MIPI | S5P_CIGCTRL_CAMIF_SELWB |
-               S5P_CIGCTRL_SELCAM_MIPI_A);
+               S5P_CIGCTRL_SELCAM_MIPI_A | S5P_CIGCTRL_CAM_JPEG);
 
        if (cam->bus_type == FIMC_MIPI_CSI2) {
                cfg |= S5P_CIGCTRL_SELCAM_MIPI;
@@ -642,9 +647,15 @@ int fimc_hw_set_camera_type(struct fimc_dev *fimc,
                        cfg |= S5P_CIGCTRL_SELCAM_MIPI_A;
 
                /* TODO: add remaining supported formats. */
-               if (vid_cap->mf.code == V4L2_MBUS_FMT_VYUY8_2X8) {
+               switch (vid_cap->mf.code) {
+               case V4L2_MBUS_FMT_VYUY8_2X8:
                        tmp = S5P_CSIIMGFMT_YCBCR422_8BIT;
-               } else {
+                       break;
+               case V4L2_MBUS_FMT_JPEG_1X8:
+                       tmp = S5P_CSIIMGFMT_USER(1);
+                       cfg |= S5P_CIGCTRL_CAM_JPEG;
+                       break;
+               default:
                        v4l2_err(fimc->vid_cap.vfd,
                                 "Not supported camera pixel format: %d",
                                 vid_cap->mf.code);
diff --git a/drivers/media/video/s5p-fimc/regs-fimc.h 
b/drivers/media/video/s5p-fimc/regs-fimc.h
index 0fea3e6..94d2302 100644
--- a/drivers/media/video/s5p-fimc/regs-fimc.h
+++ b/drivers/media/video/s5p-fimc/regs-fimc.h
@@ -54,6 +54,7 @@
 #define S5P_CIGCTRL_IRQ_CLR            (1 << 19)
 #define S5P_CIGCTRL_IRQ_ENABLE         (1 << 16)
 #define S5P_CIGCTRL_SHDW_DISABLE       (1 << 12)
+#define S5P_CIGCTRL_CAM_JPEG           (1 << 8)
 #define S5P_CIGCTRL_SELCAM_MIPI_A      (1 << 7)
 #define S5P_CIGCTRL_CAMIF_SELWB                (1 << 6)
 /* 0 - ITU601; 1 - ITU709 */
@@ -184,7 +185,6 @@
 
 /* Image effect */
 #define S5P_CIIMGEFF                   0xd0
-#define S5P_CIIMGEFF_IE_DISABLE                (0 << 30)
 #define S5P_CIIMGEFF_IE_ENABLE         (1 << 30)
 #define S5P_CIIMGEFF_IE_SC_BEFORE      (0 << 29)
 #define S5P_CIIMGEFF_IE_SC_AFTER       (1 << 29)
@@ -286,10 +286,8 @@
 #define S5P_CSIIMGFMT_RAW8             0x2a
 #define S5P_CSIIMGFMT_RAW10            0x2b
 #define S5P_CSIIMGFMT_RAW12            0x2c
-#define S5P_CSIIMGFMT_USER1            0x30
-#define S5P_CSIIMGFMT_USER2            0x31
-#define S5P_CSIIMGFMT_USER3            0x32
-#define S5P_CSIIMGFMT_USER4            0x33
+/* User defined formats. x = 0...16. */
+#define S5P_CSIIMGFMT_USER(x)          (0x30 + x - 1)
 
 /* Output frame buffer sequence mask */
 #define S5P_CIFCNTSEQ                  0x1FC
-- 
1.7.6

--
To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to