]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - drivers/media/platform/s5p-jpeg/jpeg-core.c
[media] s5p-jpeg: add support for 5433
[linux.git] / drivers / media / platform / s5p-jpeg / jpeg-core.c
index b17091b9dfeb76f8c2587ae91ef9ef68e4df27d7..8b81ff63923db6ea90266790ba48e8b050d910f6 100644 (file)
@@ -626,6 +626,7 @@ static int s5p_jpeg_to_user_subsampling(struct s5p_jpeg_ctx *ctx)
                        return V4L2_JPEG_CHROMA_SUBSAMPLING_411;
                return exynos3250_decoded_subsampling[ctx->subsampling];
        case SJPEG_EXYNOS4:
+       case SJPEG_EXYNOS5433:
                if (ctx->subsampling > 2)
                        return V4L2_JPEG_CHROMA_SUBSAMPLING_420;
                return exynos4x12_decoded_subsampling[ctx->subsampling];
@@ -750,6 +751,211 @@ static void exynos4_jpeg_set_huff_tbl(void __iomem *base)
                                                        ARRAY_SIZE(hactblg0));
 }
 
+static inline int __exynos4_huff_tbl(int class, int id, bool lenval)
+{
+       /*
+        * class: 0 - DC, 1 - AC
+        * id: 0 - Y, 1 - Cb/Cr
+        */
+       if (class) {
+               if (id)
+                       return lenval ? EXYNOS4_HUFF_TBL_HACCL :
+                               EXYNOS4_HUFF_TBL_HACCV;
+               return lenval ? EXYNOS4_HUFF_TBL_HACLL : EXYNOS4_HUFF_TBL_HACLV;
+
+       }
+       /* class == 0 */
+       if (id)
+               return lenval ? EXYNOS4_HUFF_TBL_HDCCL : EXYNOS4_HUFF_TBL_HDCCV;
+
+       return lenval ? EXYNOS4_HUFF_TBL_HDCLL : EXYNOS4_HUFF_TBL_HDCLV;
+}
+
+static inline int exynos4_huff_tbl_len(int class, int id)
+{
+       return __exynos4_huff_tbl(class, id, true);
+}
+
+static inline int exynos4_huff_tbl_val(int class, int id)
+{
+       return __exynos4_huff_tbl(class, id, false);
+}
+
+static int get_byte(struct s5p_jpeg_buffer *buf);
+static int get_word_be(struct s5p_jpeg_buffer *buf, unsigned int *word);
+static void skip(struct s5p_jpeg_buffer *buf, long len);
+
+static void exynos4_jpeg_parse_decode_h_tbl(struct s5p_jpeg_ctx *ctx)
+{
+       struct s5p_jpeg *jpeg = ctx->jpeg;
+       struct vb2_buffer *vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
+       struct s5p_jpeg_buffer jpeg_buffer;
+       unsigned int word;
+       int c, x, components;
+
+       jpeg_buffer.size = 2; /* Ls */
+       jpeg_buffer.data =
+               (unsigned long)vb2_plane_vaddr(vb, 0) + ctx->out_q.sos + 2;
+       jpeg_buffer.curr = 0;
+
+       word = 0;
+
+       if (get_word_be(&jpeg_buffer, &word))
+               return;
+       jpeg_buffer.size = (long)word - 2;
+       jpeg_buffer.data += 2;
+       jpeg_buffer.curr = 0;
+
+       components = get_byte(&jpeg_buffer);
+       if (components == -1)
+               return;
+       while (components--) {
+               c = get_byte(&jpeg_buffer);
+               if (c == -1)
+                       return;
+               x = get_byte(&jpeg_buffer);
+               if (x == -1)
+                       return;
+               exynos4_jpeg_select_dec_h_tbl(jpeg->regs, c,
+                                       (((x >> 4) & 0x1) << 1) | (x & 0x1));
+       }
+
+}
+
+static void exynos4_jpeg_parse_huff_tbl(struct s5p_jpeg_ctx *ctx)
+{
+       struct s5p_jpeg *jpeg = ctx->jpeg;
+       struct vb2_buffer *vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
+       struct s5p_jpeg_buffer jpeg_buffer;
+       unsigned int word;
+       int c, i, n, j;
+
+       for (j = 0; j < ctx->out_q.dht.n; ++j) {
+               jpeg_buffer.size = ctx->out_q.dht.len[j];
+               jpeg_buffer.data = (unsigned long)vb2_plane_vaddr(vb, 0) +
+                                  ctx->out_q.dht.marker[j];
+               jpeg_buffer.curr = 0;
+
+               word = 0;
+               while (jpeg_buffer.curr < jpeg_buffer.size) {
+                       char id, class;
+
+                       c = get_byte(&jpeg_buffer);
+                       if (c == -1)
+                               return;
+                       id = c & 0xf;
+                       class = (c >> 4) & 0xf;
+                       n = 0;
+                       for (i = 0; i < 16; ++i) {
+                               c = get_byte(&jpeg_buffer);
+                               if (c == -1)
+                                       return;
+                               word |= c << ((i % 4) * 8);
+                               if ((i + 1) % 4 == 0) {
+                                       writel(word, jpeg->regs +
+                                       exynos4_huff_tbl_len(class, id) +
+                                       (i / 4) * 4);
+                                       word = 0;
+                               }
+                               n += c;
+                       }
+                       word = 0;
+                       for (i = 0; i < n; ++i) {
+                               c = get_byte(&jpeg_buffer);
+                               if (c == -1)
+                                       return;
+                               word |= c << ((i % 4) * 8);
+                               if ((i + 1) % 4 == 0) {
+                                       writel(word, jpeg->regs +
+                                       exynos4_huff_tbl_val(class, id) +
+                                       (i / 4) * 4);
+                                       word = 0;
+                               }
+                       }
+                       if (i % 4) {
+                               writel(word, jpeg->regs +
+                               exynos4_huff_tbl_val(class, id) + (i / 4) * 4);
+                       }
+                       word = 0;
+               }
+       }
+}
+
+static void exynos4_jpeg_parse_decode_q_tbl(struct s5p_jpeg_ctx *ctx)
+{
+       struct s5p_jpeg *jpeg = ctx->jpeg;
+       struct vb2_buffer *vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
+       struct s5p_jpeg_buffer jpeg_buffer;
+       unsigned int word;
+       int c, x, components;
+
+       jpeg_buffer.size = ctx->out_q.sof_len;
+       jpeg_buffer.data =
+               (unsigned long)vb2_plane_vaddr(vb, 0) + ctx->out_q.sof;
+       jpeg_buffer.curr = 0;
+
+       word = 0;
+
+       skip(&jpeg_buffer, 5); /* P, Y, X */
+       components = get_byte(&jpeg_buffer);
+       if (components == -1)
+               return;
+
+       exynos4_jpeg_set_dec_components(jpeg->regs, components);
+
+       while (components--) {
+               c = get_byte(&jpeg_buffer);
+               if (c == -1)
+                       return;
+               skip(&jpeg_buffer, 1);
+               x = get_byte(&jpeg_buffer);
+               if (x == -1)
+                       return;
+               exynos4_jpeg_select_dec_q_tbl(jpeg->regs, c, x);
+       }
+}
+
+static void exynos4_jpeg_parse_q_tbl(struct s5p_jpeg_ctx *ctx)
+{
+       struct s5p_jpeg *jpeg = ctx->jpeg;
+       struct vb2_buffer *vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
+       struct s5p_jpeg_buffer jpeg_buffer;
+       unsigned int word;
+       int c, i, j;
+
+       for (j = 0; j < ctx->out_q.dqt.n; ++j) {
+               jpeg_buffer.size = ctx->out_q.dqt.len[j];
+               jpeg_buffer.data = (unsigned long)vb2_plane_vaddr(vb, 0) +
+                                  ctx->out_q.dqt.marker[j];
+               jpeg_buffer.curr = 0;
+
+               word = 0;
+               while (jpeg_buffer.size - jpeg_buffer.curr >= 65) {
+                       char id;
+
+                       c = get_byte(&jpeg_buffer);
+                       if (c == -1)
+                               return;
+                       id = c & 0xf;
+                       /* nonzero means extended mode - not supported */
+                       if ((c >> 4) & 0xf)
+                               return;
+                       for (i = 0; i < 64; ++i) {
+                               c = get_byte(&jpeg_buffer);
+                               if (c == -1)
+                                       return;
+                               word |= c << ((i % 4) * 8);
+                               if ((i + 1) % 4 == 0) {
+                                       writel(word, jpeg->regs +
+                                       EXYNOS4_QTBL_CONTENT(id) + (i / 4) * 4);
+                                       word = 0;
+                               }
+                       }
+                       word = 0;
+               }
+       }
+}
+
 /*
  * ============================================================================
  * Device file operations
@@ -894,8 +1100,11 @@ static bool s5p_jpeg_parse_hdr(struct s5p_jpeg_q_data *result,
                               unsigned long buffer, unsigned long size,
                               struct s5p_jpeg_ctx *ctx)
 {
-       int c, components = 0, notfound;
-       unsigned int height, width, word, subsampling = 0;
+       int c, components = 0, notfound, n_dht = 0, n_dqt = 0;
+       unsigned int height, width, word, subsampling = 0, sos = 0, sof = 0,
+                    sof_len = 0;
+       unsigned int dht[S5P_JPEG_MAX_MARKER], dht_len[S5P_JPEG_MAX_MARKER],
+                    dqt[S5P_JPEG_MAX_MARKER], dqt_len[S5P_JPEG_MAX_MARKER];
        long length;
        struct s5p_jpeg_buffer jpeg_buffer;
 
@@ -904,7 +1113,7 @@ static bool s5p_jpeg_parse_hdr(struct s5p_jpeg_q_data *result,
        jpeg_buffer.curr = 0;
 
        notfound = 1;
-       while (notfound) {
+       while (notfound || !sos) {
                c = get_byte(&jpeg_buffer);
                if (c == -1)
                        return false;
@@ -923,6 +1132,11 @@ static bool s5p_jpeg_parse_hdr(struct s5p_jpeg_q_data *result,
                case SOF0:
                        if (get_word_be(&jpeg_buffer, &word))
                                break;
+                       length = (long)word - 2;
+                       if (!length)
+                               return false;
+                       sof = jpeg_buffer.curr; /* after 0xffc0 */
+                       sof_len = length;
                        if (get_byte(&jpeg_buffer) == -1)
                                break;
                        if (get_word_be(&jpeg_buffer, &height))
@@ -932,7 +1146,6 @@ static bool s5p_jpeg_parse_hdr(struct s5p_jpeg_q_data *result,
                        components = get_byte(&jpeg_buffer);
                        if (components == -1)
                                break;
-                       notfound = 0;
 
                        if (components == 1) {
                                subsampling = 0x33;
@@ -941,8 +1154,40 @@ static bool s5p_jpeg_parse_hdr(struct s5p_jpeg_q_data *result,
                                subsampling = get_byte(&jpeg_buffer);
                                skip(&jpeg_buffer, 1);
                        }
-
+                       if (components > 3)
+                               return false;
                        skip(&jpeg_buffer, components * 2);
+                       notfound = 0;
+                       break;
+
+               case DQT:
+                       if (get_word_be(&jpeg_buffer, &word))
+                               break;
+                       length = (long)word - 2;
+                       if (!length)
+                               return false;
+                       if (n_dqt >= S5P_JPEG_MAX_MARKER)
+                               return false;
+                       dqt[n_dqt] = jpeg_buffer.curr; /* after 0xffdb */
+                       dqt_len[n_dqt++] = length;
+                       skip(&jpeg_buffer, length);
+                       break;
+
+               case DHT:
+                       if (get_word_be(&jpeg_buffer, &word))
+                               break;
+                       length = (long)word - 2;
+                       if (!length)
+                               return false;
+                       if (n_dht >= S5P_JPEG_MAX_MARKER)
+                               return false;
+                       dht[n_dht] = jpeg_buffer.curr; /* after 0xffc4 */
+                       dht_len[n_dht++] = length;
+                       skip(&jpeg_buffer, length);
+                       break;
+
+               case SOS:
+                       sos = jpeg_buffer.curr - 2; /* 0xffda */
                        break;
 
                /* skip payload-less markers */
@@ -963,7 +1208,20 @@ static bool s5p_jpeg_parse_hdr(struct s5p_jpeg_q_data *result,
        }
        result->w = width;
        result->h = height;
-       result->size = components;
+       result->sos = sos;
+       result->dht.n = n_dht;
+       while (n_dht--) {
+               result->dht.marker[n_dht] = dht[n_dht];
+               result->dht.len[n_dht] = dht_len[n_dht];
+       }
+       result->dqt.n = n_dqt;
+       while (n_dqt--) {
+               result->dqt.marker[n_dqt] = dqt[n_dqt];
+               result->dqt.len[n_dqt] = dqt_len[n_dqt];
+       }
+       result->sof = sof;
+       result->sof_len = sof_len;
+       result->size = result->components = components;
 
        switch (subsampling) {
        case 0x11:
@@ -982,7 +1240,7 @@ static bool s5p_jpeg_parse_hdr(struct s5p_jpeg_q_data *result,
                return false;
        }
 
-       return !notfound;
+       return !notfound && sos;
 }
 
 static int s5p_jpeg_querycap(struct file *file, void *priv,
@@ -1226,8 +1484,7 @@ static int s5p_jpeg_try_fmt_vid_cap(struct file *file, void *priv,
                return -EINVAL;
        }
 
-       if ((ctx->jpeg->variant->version != SJPEG_EXYNOS4) ||
-           (ctx->mode != S5P_JPEG_DECODE))
+       if (!ctx->jpeg->variant->hw_ex4_compat || ctx->mode != S5P_JPEG_DECODE)
                goto exit;
 
        /*
@@ -1350,7 +1607,7 @@ static int s5p_jpeg_s_fmt(struct s5p_jpeg_ctx *ct, struct v4l2_format *f)
                 * the JPEG_IMAGE_SIZE register. In order to avoid sysmmu
                 * page fault calculate proper buffer size in such a case.
                 */
-               if (ct->jpeg->variant->version == SJPEG_EXYNOS4 &&
+               if (ct->jpeg->variant->hw_ex4_compat &&
                    f_type == FMT_TYPE_OUTPUT && ct->mode == S5P_JPEG_ENCODE)
                        q_data->size = exynos4_jpeg_get_output_buffer_size(ct,
                                                        f,
@@ -1889,9 +2146,36 @@ static void exynos4_jpeg_set_jpeg_addr(struct s5p_jpeg_ctx *ctx)
                vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
 
        jpeg_addr = vb2_dma_contig_plane_dma_addr(vb, 0);
+       if (jpeg->variant->version == SJPEG_EXYNOS5433 &&
+           ctx->mode == S5P_JPEG_DECODE)
+               jpeg_addr += ctx->out_q.sos;
        exynos4_jpeg_set_stream_buf_address(jpeg->regs, jpeg_addr);
 }
 
+static inline void exynos4_jpeg_set_img_fmt(void __iomem *base,
+                                           unsigned int img_fmt)
+{
+       __exynos4_jpeg_set_img_fmt(base, img_fmt, SJPEG_EXYNOS4);
+}
+
+static inline void exynos5433_jpeg_set_img_fmt(void __iomem *base,
+                                              unsigned int img_fmt)
+{
+       __exynos4_jpeg_set_img_fmt(base, img_fmt, SJPEG_EXYNOS5433);
+}
+
+static inline void exynos4_jpeg_set_enc_out_fmt(void __iomem *base,
+                                               unsigned int out_fmt)
+{
+       __exynos4_jpeg_set_enc_out_fmt(base, out_fmt, SJPEG_EXYNOS4);
+}
+
+static inline void exynos5433_jpeg_set_enc_out_fmt(void __iomem *base,
+                                                  unsigned int out_fmt)
+{
+       __exynos4_jpeg_set_enc_out_fmt(base, out_fmt, SJPEG_EXYNOS5433);
+}
+
 static void exynos4_jpeg_device_run(void *priv)
 {
        struct s5p_jpeg_ctx *ctx = priv;
@@ -1899,11 +2183,11 @@ static void exynos4_jpeg_device_run(void *priv)
        unsigned int bitstream_size;
        unsigned long flags;
 
-       spin_lock_irqsave(&ctx->jpeg->slock, flags);
+       spin_lock_irqsave(&jpeg->slock, flags);
 
        if (ctx->mode == S5P_JPEG_ENCODE) {
                exynos4_jpeg_sw_reset(jpeg->regs);
-               exynos4_jpeg_set_interrupt(jpeg->regs);
+               exynos4_jpeg_set_interrupt(jpeg->regs, jpeg->variant->version);
                exynos4_jpeg_set_huf_table_enable(jpeg->regs, 1);
 
                exynos4_jpeg_set_huff_tbl(jpeg->regs);
@@ -1920,27 +2204,56 @@ static void exynos4_jpeg_device_run(void *priv)
                exynos4_jpeg_set_stream_size(jpeg->regs, ctx->cap_q.w,
                                                        ctx->cap_q.h);
 
-               exynos4_jpeg_set_enc_out_fmt(jpeg->regs, ctx->subsampling);
-               exynos4_jpeg_set_img_fmt(jpeg->regs, ctx->out_q.fmt->fourcc);
+               if (ctx->jpeg->variant->version == SJPEG_EXYNOS4) {
+                       exynos4_jpeg_set_enc_out_fmt(jpeg->regs,
+                                                    ctx->subsampling);
+                       exynos4_jpeg_set_img_fmt(jpeg->regs,
+                                                ctx->out_q.fmt->fourcc);
+               } else {
+                       exynos5433_jpeg_set_enc_out_fmt(jpeg->regs,
+                                                       ctx->subsampling);
+                       exynos5433_jpeg_set_img_fmt(jpeg->regs,
+                                                   ctx->out_q.fmt->fourcc);
+               }
                exynos4_jpeg_set_img_addr(ctx);
                exynos4_jpeg_set_jpeg_addr(ctx);
                exynos4_jpeg_set_encode_hoff_cnt(jpeg->regs,
                                                        ctx->out_q.fmt->fourcc);
        } else {
                exynos4_jpeg_sw_reset(jpeg->regs);
-               exynos4_jpeg_set_interrupt(jpeg->regs);
+               exynos4_jpeg_set_interrupt(jpeg->regs,
+                                          jpeg->variant->version);
                exynos4_jpeg_set_img_addr(ctx);
                exynos4_jpeg_set_jpeg_addr(ctx);
-               exynos4_jpeg_set_img_fmt(jpeg->regs, ctx->cap_q.fmt->fourcc);
 
-               bitstream_size = DIV_ROUND_UP(ctx->out_q.size, 32);
+               if (jpeg->variant->version == SJPEG_EXYNOS5433) {
+                       exynos4_jpeg_parse_huff_tbl(ctx);
+                       exynos4_jpeg_parse_decode_h_tbl(ctx);
+
+                       exynos4_jpeg_parse_q_tbl(ctx);
+                       exynos4_jpeg_parse_decode_q_tbl(ctx);
+
+                       exynos4_jpeg_set_huf_table_enable(jpeg->regs, 1);
+
+                       exynos4_jpeg_set_stream_size(jpeg->regs, ctx->cap_q.w,
+                                       ctx->cap_q.h);
+                       exynos5433_jpeg_set_enc_out_fmt(jpeg->regs,
+                                                       ctx->subsampling);
+                       exynos5433_jpeg_set_img_fmt(jpeg->regs,
+                                                   ctx->cap_q.fmt->fourcc);
+                       bitstream_size = DIV_ROUND_UP(ctx->out_q.size, 16);
+               } else {
+                       exynos4_jpeg_set_img_fmt(jpeg->regs,
+                                                ctx->cap_q.fmt->fourcc);
+                       bitstream_size = DIV_ROUND_UP(ctx->out_q.size, 32);
+               }
 
                exynos4_jpeg_set_dec_bitstream_size(jpeg->regs, bitstream_size);
        }
 
        exynos4_jpeg_set_enc_dec_mode(jpeg->regs, ctx->mode);
 
-       spin_unlock_irqrestore(&ctx->jpeg->slock, flags);
+       spin_unlock_irqrestore(&jpeg->slock, flags);
 }
 
 static void exynos3250_jpeg_set_img_addr(struct s5p_jpeg_ctx *ctx)
@@ -2187,6 +2500,17 @@ static void s5p_jpeg_buf_queue(struct vb2_buffer *vb)
                q_data = &ctx->out_q;
                q_data->w = tmp.w;
                q_data->h = tmp.h;
+               q_data->sos = tmp.sos;
+               memcpy(q_data->dht.marker, tmp.dht.marker,
+                      sizeof(tmp.dht.marker));
+               memcpy(q_data->dht.len, tmp.dht.len, sizeof(tmp.dht.len));
+               q_data->dht.n = tmp.dht.n;
+               memcpy(q_data->dqt.marker, tmp.dqt.marker,
+                      sizeof(tmp.dqt.marker));
+               memcpy(q_data->dqt.len, tmp.dqt.len, sizeof(tmp.dqt.len));
+               q_data->dqt.n = tmp.dqt.n;
+               q_data->sof = tmp.sof;
+               q_data->sof_len = tmp.sof_len;
 
                q_data = &ctx->cap_q;
                q_data->w = tmp.w;
@@ -2373,7 +2697,8 @@ static irqreturn_t exynos4_jpeg_irq(int irq, void *priv)
        }
 
        v4l2_m2m_job_finish(jpeg->m2m_dev, curr_ctx->fh.m2m_ctx);
-       curr_ctx->subsampling = exynos4_jpeg_get_frame_fmt(jpeg->regs);
+       if (jpeg->variant->version == SJPEG_EXYNOS4)
+               curr_ctx->subsampling = exynos4_jpeg_get_frame_fmt(jpeg->regs);
 
        spin_unlock(&jpeg->slock);
        return IRQ_HANDLED;
@@ -2725,6 +3050,7 @@ static struct s5p_jpeg_variant exynos4_jpeg_drvdata = {
        .htbl_reinit    = 1,
        .clk_names      = {"jpeg"},
        .num_clocks     = 1,
+       .hw_ex4_compat  = 1,
 };
 
 static struct s5p_jpeg_variant exynos5420_jpeg_drvdata = {
@@ -2738,6 +3064,17 @@ static struct s5p_jpeg_variant exynos5420_jpeg_drvdata = {
        .num_clocks     = 1,
 };
 
+static struct s5p_jpeg_variant exynos5433_jpeg_drvdata = {
+       .version        = SJPEG_EXYNOS5433,
+       .jpeg_irq       = exynos4_jpeg_irq,
+       .m2m_ops        = &exynos4_jpeg_m2m_ops,
+       .fmt_ver_flag   = SJPEG_FMT_FLAG_EXYNOS4,
+       .htbl_reinit    = 1,
+       .clk_names      = {"pclk", "aclk", "aclk_xiu", "sclk"},
+       .num_clocks     = 4,
+       .hw_ex4_compat  = 1,
+};
+
 static const struct of_device_id samsung_jpeg_match[] = {
        {
                .compatible = "samsung,s5pv210-jpeg",
@@ -2754,6 +3091,9 @@ static const struct of_device_id samsung_jpeg_match[] = {
        }, {
                .compatible = "samsung,exynos5420-jpeg",
                .data = &exynos5420_jpeg_drvdata,
+       }, {
+               .compatible = "samsung,exynos5433-jpeg",
+               .data = &exynos5433_jpeg_drvdata,
        },
        {},
 };