linux/drivers/staging/media/meson/vdec/vdec_helpers.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 <linux/gcd.h>
   8#include <media/v4l2-mem2mem.h>
   9#include <media/v4l2-event.h>
  10#include <media/videobuf2-dma-contig.h>
  11
  12#include "vdec_helpers.h"
  13
  14#define NUM_CANVAS_NV12 2
  15#define NUM_CANVAS_YUV420 3
  16
  17u32 amvdec_read_dos(struct amvdec_core *core, u32 reg)
  18{
  19        return readl_relaxed(core->dos_base + reg);
  20}
  21EXPORT_SYMBOL_GPL(amvdec_read_dos);
  22
  23void amvdec_write_dos(struct amvdec_core *core, u32 reg, u32 val)
  24{
  25        writel_relaxed(val, core->dos_base + reg);
  26}
  27EXPORT_SYMBOL_GPL(amvdec_write_dos);
  28
  29void amvdec_write_dos_bits(struct amvdec_core *core, u32 reg, u32 val)
  30{
  31        amvdec_write_dos(core, reg, amvdec_read_dos(core, reg) | val);
  32}
  33EXPORT_SYMBOL_GPL(amvdec_write_dos_bits);
  34
  35void amvdec_clear_dos_bits(struct amvdec_core *core, u32 reg, u32 val)
  36{
  37        amvdec_write_dos(core, reg, amvdec_read_dos(core, reg) & ~val);
  38}
  39EXPORT_SYMBOL_GPL(amvdec_clear_dos_bits);
  40
  41u32 amvdec_read_parser(struct amvdec_core *core, u32 reg)
  42{
  43        return readl_relaxed(core->esparser_base + reg);
  44}
  45EXPORT_SYMBOL_GPL(amvdec_read_parser);
  46
  47void amvdec_write_parser(struct amvdec_core *core, u32 reg, u32 val)
  48{
  49        writel_relaxed(val, core->esparser_base + reg);
  50}
  51EXPORT_SYMBOL_GPL(amvdec_write_parser);
  52
  53/* 4 KiB per 64x32 block */
  54u32 amvdec_am21c_body_size(u32 width, u32 height)
  55{
  56        u32 width_64 = ALIGN(width, 64) / 64;
  57        u32 height_32 = ALIGN(height, 32) / 32;
  58
  59        return SZ_4K * width_64 * height_32;
  60}
  61EXPORT_SYMBOL_GPL(amvdec_am21c_body_size);
  62
  63/* 32 bytes per 128x64 block */
  64u32 amvdec_am21c_head_size(u32 width, u32 height)
  65{
  66        u32 width_128 = ALIGN(width, 128) / 128;
  67        u32 height_64 = ALIGN(height, 64) / 64;
  68
  69        return 32 * width_128 * height_64;
  70}
  71EXPORT_SYMBOL_GPL(amvdec_am21c_head_size);
  72
  73u32 amvdec_am21c_size(u32 width, u32 height)
  74{
  75        return ALIGN(amvdec_am21c_body_size(width, height) +
  76                     amvdec_am21c_head_size(width, height), SZ_64K);
  77}
  78EXPORT_SYMBOL_GPL(amvdec_am21c_size);
  79
  80static int canvas_alloc(struct amvdec_session *sess, u8 *canvas_id)
  81{
  82        int ret;
  83
  84        if (sess->canvas_num >= MAX_CANVAS) {
  85                dev_err(sess->core->dev, "Reached max number of canvas\n");
  86                return -ENOMEM;
  87        }
  88
  89        ret = meson_canvas_alloc(sess->core->canvas, canvas_id);
  90        if (ret)
  91                return ret;
  92
  93        sess->canvas_alloc[sess->canvas_num++] = *canvas_id;
  94        return 0;
  95}
  96
  97static int set_canvas_yuv420m(struct amvdec_session *sess,
  98                              struct vb2_buffer *vb, u32 width,
  99                              u32 height, u32 reg)
 100{
 101        struct amvdec_core *core = sess->core;
 102        u8 canvas_id[NUM_CANVAS_YUV420]; /* Y U V */
 103        dma_addr_t buf_paddr[NUM_CANVAS_YUV420]; /* Y U V */
 104        int ret, i;
 105
 106        for (i = 0; i < NUM_CANVAS_YUV420; ++i) {
 107                ret = canvas_alloc(sess, &canvas_id[i]);
 108                if (ret)
 109                        return ret;
 110
 111                buf_paddr[i] =
 112                    vb2_dma_contig_plane_dma_addr(vb, i);
 113        }
 114
 115        /* Y plane */
 116        meson_canvas_config(core->canvas, canvas_id[0], buf_paddr[0],
 117                            width, height, MESON_CANVAS_WRAP_NONE,
 118                            MESON_CANVAS_BLKMODE_LINEAR,
 119                            MESON_CANVAS_ENDIAN_SWAP64);
 120
 121        /* U plane */
 122        meson_canvas_config(core->canvas, canvas_id[1], buf_paddr[1],
 123                            width / 2, height / 2, MESON_CANVAS_WRAP_NONE,
 124                            MESON_CANVAS_BLKMODE_LINEAR,
 125                            MESON_CANVAS_ENDIAN_SWAP64);
 126
 127        /* V plane */
 128        meson_canvas_config(core->canvas, canvas_id[2], buf_paddr[2],
 129                            width / 2, height / 2, MESON_CANVAS_WRAP_NONE,
 130                            MESON_CANVAS_BLKMODE_LINEAR,
 131                            MESON_CANVAS_ENDIAN_SWAP64);
 132
 133        amvdec_write_dos(core, reg,
 134                         ((canvas_id[2]) << 16) |
 135                         ((canvas_id[1]) << 8)  |
 136                         (canvas_id[0]));
 137
 138        return 0;
 139}
 140
 141static int set_canvas_nv12m(struct amvdec_session *sess,
 142                            struct vb2_buffer *vb, u32 width,
 143                            u32 height, u32 reg)
 144{
 145        struct amvdec_core *core = sess->core;
 146        u8 canvas_id[NUM_CANVAS_NV12]; /* Y U/V */
 147        dma_addr_t buf_paddr[NUM_CANVAS_NV12]; /* Y U/V */
 148        int ret, i;
 149
 150        for (i = 0; i < NUM_CANVAS_NV12; ++i) {
 151                ret = canvas_alloc(sess, &canvas_id[i]);
 152                if (ret)
 153                        return ret;
 154
 155                buf_paddr[i] =
 156                    vb2_dma_contig_plane_dma_addr(vb, i);
 157        }
 158
 159        /* Y plane */
 160        meson_canvas_config(core->canvas, canvas_id[0], buf_paddr[0],
 161                            width, height, MESON_CANVAS_WRAP_NONE,
 162                            MESON_CANVAS_BLKMODE_LINEAR,
 163                            MESON_CANVAS_ENDIAN_SWAP64);
 164
 165        /* U/V plane */
 166        meson_canvas_config(core->canvas, canvas_id[1], buf_paddr[1],
 167                            width, height / 2, MESON_CANVAS_WRAP_NONE,
 168                            MESON_CANVAS_BLKMODE_LINEAR,
 169                            MESON_CANVAS_ENDIAN_SWAP64);
 170
 171        amvdec_write_dos(core, reg,
 172                         ((canvas_id[1]) << 16) |
 173                         ((canvas_id[1]) << 8)  |
 174                         (canvas_id[0]));
 175
 176        return 0;
 177}
 178
 179int amvdec_set_canvases(struct amvdec_session *sess,
 180                        u32 reg_base[], u32 reg_num[])
 181{
 182        struct v4l2_m2m_buffer *buf;
 183        u32 pixfmt = sess->pixfmt_cap;
 184        u32 width = ALIGN(sess->width, 32);
 185        u32 height = ALIGN(sess->height, 32);
 186        u32 reg_cur = reg_base[0];
 187        u32 reg_num_cur = 0;
 188        u32 reg_base_cur = 0;
 189        int i = 0;
 190        int ret;
 191
 192        v4l2_m2m_for_each_dst_buf(sess->m2m_ctx, buf) {
 193                if (!reg_base[reg_base_cur])
 194                        return -EINVAL;
 195
 196                reg_cur = reg_base[reg_base_cur] + reg_num_cur * 4;
 197
 198                switch (pixfmt) {
 199                case V4L2_PIX_FMT_NV12M:
 200                        ret = set_canvas_nv12m(sess, &buf->vb.vb2_buf, width,
 201                                               height, reg_cur);
 202                        if (ret)
 203                                return ret;
 204                        break;
 205                case V4L2_PIX_FMT_YUV420M:
 206                        ret = set_canvas_yuv420m(sess, &buf->vb.vb2_buf, width,
 207                                                 height, reg_cur);
 208                        if (ret)
 209                                return ret;
 210                        break;
 211                default:
 212                        dev_err(sess->core->dev, "Unsupported pixfmt %08X\n",
 213                                pixfmt);
 214                        return -EINVAL;
 215                }
 216
 217                reg_num_cur++;
 218                if (reg_num_cur >= reg_num[reg_base_cur]) {
 219                        reg_base_cur++;
 220                        reg_num_cur = 0;
 221                }
 222
 223                sess->fw_idx_to_vb2_idx[i++] = buf->vb.vb2_buf.index;
 224        }
 225
 226        return 0;
 227}
 228EXPORT_SYMBOL_GPL(amvdec_set_canvases);
 229
 230void amvdec_add_ts(struct amvdec_session *sess, u64 ts,
 231                   struct v4l2_timecode tc, u32 offset, u32 vbuf_flags)
 232{
 233        struct amvdec_timestamp *new_ts;
 234        unsigned long flags;
 235
 236        new_ts = kzalloc(sizeof(*new_ts), GFP_KERNEL);
 237        new_ts->ts = ts;
 238        new_ts->tc = tc;
 239        new_ts->offset = offset;
 240        new_ts->flags = vbuf_flags;
 241
 242        spin_lock_irqsave(&sess->ts_spinlock, flags);
 243        list_add_tail(&new_ts->list, &sess->timestamps);
 244        spin_unlock_irqrestore(&sess->ts_spinlock, flags);
 245}
 246EXPORT_SYMBOL_GPL(amvdec_add_ts);
 247
 248void amvdec_remove_ts(struct amvdec_session *sess, u64 ts)
 249{
 250        struct amvdec_timestamp *tmp;
 251        unsigned long flags;
 252
 253        spin_lock_irqsave(&sess->ts_spinlock, flags);
 254        list_for_each_entry(tmp, &sess->timestamps, list) {
 255                if (tmp->ts == ts) {
 256                        list_del(&tmp->list);
 257                        kfree(tmp);
 258                        goto unlock;
 259                }
 260        }
 261        dev_warn(sess->core->dev_dec,
 262                 "Couldn't remove buffer with timestamp %llu from list\n", ts);
 263
 264unlock:
 265        spin_unlock_irqrestore(&sess->ts_spinlock, flags);
 266}
 267EXPORT_SYMBOL_GPL(amvdec_remove_ts);
 268
 269static void dst_buf_done(struct amvdec_session *sess,
 270                         struct vb2_v4l2_buffer *vbuf,
 271                         u32 field, u64 timestamp,
 272                         struct v4l2_timecode timecode, u32 flags)
 273{
 274        struct device *dev = sess->core->dev_dec;
 275        u32 output_size = amvdec_get_output_size(sess);
 276
 277        switch (sess->pixfmt_cap) {
 278        case V4L2_PIX_FMT_NV12M:
 279                vbuf->vb2_buf.planes[0].bytesused = output_size;
 280                vbuf->vb2_buf.planes[1].bytesused = output_size / 2;
 281                break;
 282        case V4L2_PIX_FMT_YUV420M:
 283                vbuf->vb2_buf.planes[0].bytesused = output_size;
 284                vbuf->vb2_buf.planes[1].bytesused = output_size / 4;
 285                vbuf->vb2_buf.planes[2].bytesused = output_size / 4;
 286                break;
 287        }
 288
 289        vbuf->vb2_buf.timestamp = timestamp;
 290        vbuf->sequence = sess->sequence_cap++;
 291        vbuf->flags = flags;
 292        vbuf->timecode = timecode;
 293
 294        if (sess->should_stop &&
 295            atomic_read(&sess->esparser_queued_bufs) <= 1) {
 296                const struct v4l2_event ev = { .type = V4L2_EVENT_EOS };
 297
 298                dev_dbg(dev, "Signaling EOS, sequence_cap = %u\n",
 299                        sess->sequence_cap - 1);
 300                v4l2_event_queue_fh(&sess->fh, &ev);
 301                vbuf->flags |= V4L2_BUF_FLAG_LAST;
 302        } else if (sess->status == STATUS_NEEDS_RESUME) {
 303                /* Mark LAST for drained show frames during a source change */
 304                vbuf->flags |= V4L2_BUF_FLAG_LAST;
 305                sess->sequence_cap = 0;
 306        } else if (sess->should_stop)
 307                dev_dbg(dev, "should_stop, %u bufs remain\n",
 308                        atomic_read(&sess->esparser_queued_bufs));
 309
 310        dev_dbg(dev, "Buffer %u done, ts = %llu, flags = %08X\n",
 311                vbuf->vb2_buf.index, timestamp, flags);
 312        vbuf->field = field;
 313        v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_DONE);
 314
 315        /* Buffer done probably means the vififo got freed */
 316        schedule_work(&sess->esparser_queue_work);
 317}
 318
 319void amvdec_dst_buf_done(struct amvdec_session *sess,
 320                         struct vb2_v4l2_buffer *vbuf, u32 field)
 321{
 322        struct device *dev = sess->core->dev_dec;
 323        struct amvdec_timestamp *tmp;
 324        struct list_head *timestamps = &sess->timestamps;
 325        struct v4l2_timecode timecode;
 326        u64 timestamp;
 327        u32 vbuf_flags;
 328        unsigned long flags;
 329
 330        spin_lock_irqsave(&sess->ts_spinlock, flags);
 331        if (list_empty(timestamps)) {
 332                dev_err(dev, "Buffer %u done but list is empty\n",
 333                        vbuf->vb2_buf.index);
 334
 335                v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR);
 336                spin_unlock_irqrestore(&sess->ts_spinlock, flags);
 337                return;
 338        }
 339
 340        tmp = list_first_entry(timestamps, struct amvdec_timestamp, list);
 341        timestamp = tmp->ts;
 342        timecode = tmp->tc;
 343        vbuf_flags = tmp->flags;
 344        list_del(&tmp->list);
 345        kfree(tmp);
 346        spin_unlock_irqrestore(&sess->ts_spinlock, flags);
 347
 348        dst_buf_done(sess, vbuf, field, timestamp, timecode, vbuf_flags);
 349        atomic_dec(&sess->esparser_queued_bufs);
 350}
 351EXPORT_SYMBOL_GPL(amvdec_dst_buf_done);
 352
 353void amvdec_dst_buf_done_offset(struct amvdec_session *sess,
 354                                struct vb2_v4l2_buffer *vbuf,
 355                                u32 offset, u32 field, bool allow_drop)
 356{
 357        struct device *dev = sess->core->dev_dec;
 358        struct amvdec_timestamp *match = NULL;
 359        struct amvdec_timestamp *tmp, *n;
 360        struct v4l2_timecode timecode = { 0 };
 361        u64 timestamp = 0;
 362        u32 vbuf_flags = 0;
 363        unsigned long flags;
 364
 365        spin_lock_irqsave(&sess->ts_spinlock, flags);
 366
 367        /* Look for our vififo offset to get the corresponding timestamp. */
 368        list_for_each_entry_safe(tmp, n, &sess->timestamps, list) {
 369                if (tmp->offset > offset) {
 370                        /*
 371                         * Delete any record that remained unused for 32 match
 372                         * checks
 373                         */
 374                        if (tmp->used_count++ >= 32) {
 375                                list_del(&tmp->list);
 376                                kfree(tmp);
 377                        }
 378                        break;
 379                }
 380
 381                match = tmp;
 382        }
 383
 384        if (!match) {
 385                dev_err(dev, "Buffer %u done but can't match offset (%08X)\n",
 386                        vbuf->vb2_buf.index, offset);
 387        } else {
 388                timestamp = match->ts;
 389                timecode = match->tc;
 390                vbuf_flags = match->flags;
 391                list_del(&match->list);
 392                kfree(match);
 393        }
 394        spin_unlock_irqrestore(&sess->ts_spinlock, flags);
 395
 396        dst_buf_done(sess, vbuf, field, timestamp, timecode, vbuf_flags);
 397        if (match)
 398                atomic_dec(&sess->esparser_queued_bufs);
 399}
 400EXPORT_SYMBOL_GPL(amvdec_dst_buf_done_offset);
 401
 402void amvdec_dst_buf_done_idx(struct amvdec_session *sess,
 403                             u32 buf_idx, u32 offset, u32 field)
 404{
 405        struct vb2_v4l2_buffer *vbuf;
 406        struct device *dev = sess->core->dev_dec;
 407
 408        vbuf = v4l2_m2m_dst_buf_remove_by_idx(sess->m2m_ctx,
 409                                              sess->fw_idx_to_vb2_idx[buf_idx]);
 410
 411        if (!vbuf) {
 412                dev_err(dev,
 413                        "Buffer %u done but it doesn't exist in m2m_ctx\n",
 414                        buf_idx);
 415                return;
 416        }
 417
 418        if (offset != -1)
 419                amvdec_dst_buf_done_offset(sess, vbuf, offset, field, true);
 420        else
 421                amvdec_dst_buf_done(sess, vbuf, field);
 422}
 423EXPORT_SYMBOL_GPL(amvdec_dst_buf_done_idx);
 424
 425void amvdec_set_par_from_dar(struct amvdec_session *sess,
 426                             u32 dar_num, u32 dar_den)
 427{
 428        u32 div;
 429
 430        sess->pixelaspect.numerator = sess->height * dar_num;
 431        sess->pixelaspect.denominator = sess->width * dar_den;
 432        div = gcd(sess->pixelaspect.numerator, sess->pixelaspect.denominator);
 433        sess->pixelaspect.numerator /= div;
 434        sess->pixelaspect.denominator /= div;
 435}
 436EXPORT_SYMBOL_GPL(amvdec_set_par_from_dar);
 437
 438void amvdec_src_change(struct amvdec_session *sess, u32 width,
 439                       u32 height, u32 dpb_size)
 440{
 441        static const struct v4l2_event ev = {
 442                .type = V4L2_EVENT_SOURCE_CHANGE,
 443                .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION };
 444
 445        v4l2_ctrl_s_ctrl(sess->ctrl_min_buf_capture, dpb_size);
 446
 447        /*
 448         * Check if the capture queue is already configured well for our
 449         * usecase. If so, keep decoding with it and do not send the event
 450         */
 451        if (sess->streamon_cap &&
 452            sess->width == width &&
 453            sess->height == height &&
 454            dpb_size <= sess->num_dst_bufs) {
 455                sess->fmt_out->codec_ops->resume(sess);
 456                return;
 457        }
 458
 459        sess->changed_format = 0;
 460        sess->width = width;
 461        sess->height = height;
 462        sess->status = STATUS_NEEDS_RESUME;
 463
 464        dev_dbg(sess->core->dev, "Res. changed (%ux%u), DPB size %u\n",
 465                width, height, dpb_size);
 466        v4l2_event_queue_fh(&sess->fh, &ev);
 467}
 468EXPORT_SYMBOL_GPL(amvdec_src_change);
 469
 470void amvdec_abort(struct amvdec_session *sess)
 471{
 472        dev_info(sess->core->dev, "Aborting decoding session!\n");
 473        vb2_queue_error(&sess->m2m_ctx->cap_q_ctx.q);
 474        vb2_queue_error(&sess->m2m_ctx->out_q_ctx.q);
 475}
 476EXPORT_SYMBOL_GPL(amvdec_abort);
 477