linux/drivers/staging/media/meson/vdec/codec_mpeg12.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright (C) 2018 BayLibre, SAS
   4 * Author: Maxime Jourdan <mjourdan@baylibre.com>
   5 */
   6
   7#include <media/v4l2-mem2mem.h>
   8#include <media/videobuf2-dma-contig.h>
   9
  10#include "codec_mpeg12.h"
  11#include "dos_regs.h"
  12#include "vdec_helpers.h"
  13
  14#define SIZE_WORKSPACE          SZ_128K
  15/* Offset substracted by the firmware from the workspace paddr */
  16#define WORKSPACE_OFFSET        (5 * SZ_1K)
  17
  18/* map firmware registers to known MPEG1/2 functions */
  19#define MREG_SEQ_INFO           AV_SCRATCH_4
  20        #define MPEG2_SEQ_DAR_MASK      GENMASK(3, 0)
  21        #define MPEG2_DAR_4_3           2
  22        #define MPEG2_DAR_16_9          3
  23        #define MPEG2_DAR_221_100       4
  24#define MREG_PIC_INFO           AV_SCRATCH_5
  25#define MREG_PIC_WIDTH          AV_SCRATCH_6
  26#define MREG_PIC_HEIGHT         AV_SCRATCH_7
  27#define MREG_BUFFERIN           AV_SCRATCH_8
  28#define MREG_BUFFEROUT          AV_SCRATCH_9
  29#define MREG_CMD                AV_SCRATCH_A
  30#define MREG_CO_MV_START        AV_SCRATCH_B
  31#define MREG_ERROR_COUNT        AV_SCRATCH_C
  32#define MREG_FRAME_OFFSET       AV_SCRATCH_D
  33#define MREG_WAIT_BUFFER        AV_SCRATCH_E
  34#define MREG_FATAL_ERROR        AV_SCRATCH_F
  35
  36#define PICINFO_PROG            0x00008000
  37#define PICINFO_TOP_FIRST       0x00002000
  38
  39struct codec_mpeg12 {
  40        /* Buffer for the MPEG1/2 Workspace */
  41        void      *workspace_vaddr;
  42        dma_addr_t workspace_paddr;
  43};
  44
  45static const u8 eos_sequence[SZ_1K] = { 0x00, 0x00, 0x01, 0xB7 };
  46
  47static const u8 *codec_mpeg12_eos_sequence(u32 *len)
  48{
  49        *len = ARRAY_SIZE(eos_sequence);
  50        return eos_sequence;
  51}
  52
  53static int codec_mpeg12_can_recycle(struct amvdec_core *core)
  54{
  55        return !amvdec_read_dos(core, MREG_BUFFERIN);
  56}
  57
  58static void codec_mpeg12_recycle(struct amvdec_core *core, u32 buf_idx)
  59{
  60        amvdec_write_dos(core, MREG_BUFFERIN, buf_idx + 1);
  61}
  62
  63static int codec_mpeg12_start(struct amvdec_session *sess)
  64{
  65        struct amvdec_core *core = sess->core;
  66        struct codec_mpeg12 *mpeg12;
  67        int ret;
  68
  69        mpeg12 = kzalloc(sizeof(*mpeg12), GFP_KERNEL);
  70        if (!mpeg12)
  71                return -ENOMEM;
  72
  73        /* Allocate some memory for the MPEG1/2 decoder's state */
  74        mpeg12->workspace_vaddr = dma_alloc_coherent(core->dev, SIZE_WORKSPACE,
  75                                                     &mpeg12->workspace_paddr,
  76                                                     GFP_KERNEL);
  77        if (!mpeg12->workspace_vaddr) {
  78                dev_err(core->dev, "Failed to request MPEG 1/2 Workspace\n");
  79                ret = -ENOMEM;
  80                goto free_mpeg12;
  81        }
  82
  83        ret = amvdec_set_canvases(sess, (u32[]){ AV_SCRATCH_0, 0 },
  84                                        (u32[]){ 8, 0 });
  85        if (ret)
  86                goto free_workspace;
  87
  88        amvdec_write_dos(core, POWER_CTL_VLD, BIT(4));
  89        amvdec_write_dos(core, MREG_CO_MV_START,
  90                         mpeg12->workspace_paddr + WORKSPACE_OFFSET);
  91
  92        amvdec_write_dos(core, MPEG1_2_REG, 0);
  93        amvdec_write_dos(core, PSCALE_CTRL, 0);
  94        amvdec_write_dos(core, PIC_HEAD_INFO, 0x380);
  95        amvdec_write_dos(core, M4_CONTROL_REG, 0);
  96        amvdec_write_dos(core, MREG_BUFFERIN, 0);
  97        amvdec_write_dos(core, MREG_BUFFEROUT, 0);
  98        amvdec_write_dos(core, MREG_CMD, (sess->width << 16) | sess->height);
  99        amvdec_write_dos(core, MREG_ERROR_COUNT, 0);
 100        amvdec_write_dos(core, MREG_FATAL_ERROR, 0);
 101        amvdec_write_dos(core, MREG_WAIT_BUFFER, 0);
 102
 103        sess->keyframe_found = 1;
 104        sess->priv = mpeg12;
 105
 106        return 0;
 107
 108free_workspace:
 109        dma_free_coherent(core->dev, SIZE_WORKSPACE, mpeg12->workspace_vaddr,
 110                          mpeg12->workspace_paddr);
 111free_mpeg12:
 112        kfree(mpeg12);
 113
 114        return ret;
 115}
 116
 117static int codec_mpeg12_stop(struct amvdec_session *sess)
 118{
 119        struct codec_mpeg12 *mpeg12 = sess->priv;
 120        struct amvdec_core *core = sess->core;
 121
 122        if (mpeg12->workspace_vaddr)
 123                dma_free_coherent(core->dev, SIZE_WORKSPACE,
 124                                  mpeg12->workspace_vaddr,
 125                                  mpeg12->workspace_paddr);
 126
 127        return 0;
 128}
 129
 130static void codec_mpeg12_update_dar(struct amvdec_session *sess)
 131{
 132        struct amvdec_core *core = sess->core;
 133        u32 seq = amvdec_read_dos(core, MREG_SEQ_INFO);
 134        u32 ar = seq & MPEG2_SEQ_DAR_MASK;
 135
 136        switch (ar) {
 137        case MPEG2_DAR_4_3:
 138                amvdec_set_par_from_dar(sess, 4, 3);
 139                break;
 140        case MPEG2_DAR_16_9:
 141                amvdec_set_par_from_dar(sess, 16, 9);
 142                break;
 143        case MPEG2_DAR_221_100:
 144                amvdec_set_par_from_dar(sess, 221, 100);
 145                break;
 146        default:
 147                sess->pixelaspect.numerator = 1;
 148                sess->pixelaspect.denominator = 1;
 149                break;
 150        }
 151}
 152
 153static irqreturn_t codec_mpeg12_threaded_isr(struct amvdec_session *sess)
 154{
 155        struct amvdec_core *core = sess->core;
 156        u32 reg;
 157        u32 pic_info;
 158        u32 is_progressive;
 159        u32 buffer_index;
 160        u32 field = V4L2_FIELD_NONE;
 161        u32 offset;
 162
 163        amvdec_write_dos(core, ASSIST_MBOX1_CLR_REG, 1);
 164        reg = amvdec_read_dos(core, MREG_FATAL_ERROR);
 165        if (reg == 1) {
 166                dev_err(core->dev, "MPEG1/2 fatal error\n");
 167                amvdec_abort(sess);
 168                return IRQ_HANDLED;
 169        }
 170
 171        reg = amvdec_read_dos(core, MREG_BUFFEROUT);
 172        if (!reg)
 173                return IRQ_HANDLED;
 174
 175        /* Unclear what this means */
 176        if ((reg & GENMASK(23, 17)) == GENMASK(23, 17))
 177                goto end;
 178
 179        pic_info = amvdec_read_dos(core, MREG_PIC_INFO);
 180        is_progressive = pic_info & PICINFO_PROG;
 181
 182        if (!is_progressive)
 183                field = (pic_info & PICINFO_TOP_FIRST) ?
 184                        V4L2_FIELD_INTERLACED_TB :
 185                        V4L2_FIELD_INTERLACED_BT;
 186
 187        codec_mpeg12_update_dar(sess);
 188        buffer_index = ((reg & 0xf) - 1) & 7;
 189        offset = amvdec_read_dos(core, MREG_FRAME_OFFSET);
 190        amvdec_dst_buf_done_idx(sess, buffer_index, offset, field);
 191
 192end:
 193        amvdec_write_dos(core, MREG_BUFFEROUT, 0);
 194        return IRQ_HANDLED;
 195}
 196
 197static irqreturn_t codec_mpeg12_isr(struct amvdec_session *sess)
 198{
 199        return IRQ_WAKE_THREAD;
 200}
 201
 202struct amvdec_codec_ops codec_mpeg12_ops = {
 203        .start = codec_mpeg12_start,
 204        .stop = codec_mpeg12_stop,
 205        .isr = codec_mpeg12_isr,
 206        .threaded_isr = codec_mpeg12_threaded_isr,
 207        .can_recycle = codec_mpeg12_can_recycle,
 208        .recycle = codec_mpeg12_recycle,
 209        .eos_sequence = codec_mpeg12_eos_sequence,
 210};
 211