Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 | // SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2016 MediaTek Inc. * Author: PC Chen <pc.chen@mediatek.com> */ #include <linux/module.h> #include <linux/slab.h> #include "../vdec_drv_if.h" #include "../mtk_vcodec_dec.h" #include "../../common/mtk_vcodec_intr.h" #include "../vdec_vpu_if.h" #include "../vdec_drv_base.h" #define NAL_NON_IDR_SLICE 0x01 #define NAL_IDR_SLICE 0x05 #define NAL_H264_PPS 0x08 #define NAL_TYPE(value) ((value) & 0x1F) #define BUF_PREDICTION_SZ (32 * 1024) #define MB_UNIT_LEN 16 /* motion vector size (bytes) for every macro block */ #define HW_MB_STORE_SZ 64 #define H264_MAX_FB_NUM 17 #define HDR_PARSING_BUF_SZ 1024 #define DEC_ERR_RET(ret) ((ret) >> 16) #define H264_ERR_NOT_VALID 3 /** * struct h264_fb - h264 decode frame buffer information * @vdec_fb_va : virtual address of struct vdec_fb * @y_fb_dma : dma address of Y frame buffer (luma) * @c_fb_dma : dma address of C frame buffer (chroma) * @poc : picture order count of frame buffer * @reserved : for 8 bytes alignment */ struct h264_fb { uint64_t vdec_fb_va; uint64_t y_fb_dma; uint64_t c_fb_dma; int32_t poc; uint32_t reserved; }; /** * struct h264_ring_fb_list - ring frame buffer list * @fb_list : frame buffer array * @read_idx : read index * @write_idx : write index * @count : buffer count in list * @reserved : for 8 bytes alignment */ struct h264_ring_fb_list { struct h264_fb fb_list[H264_MAX_FB_NUM]; unsigned int read_idx; unsigned int write_idx; unsigned int count; unsigned int reserved; }; /** * struct vdec_h264_dec_info - decode information * @dpb_sz : decoding picture buffer size * @resolution_changed : resolution change happen * @realloc_mv_buf : flag to notify driver to re-allocate mv buffer * @reserved : for 8 bytes alignment * @bs_dma : Input bit-stream buffer dma address * @y_fb_dma : Y frame buffer dma address * @c_fb_dma : C frame buffer dma address * @vdec_fb_va : VDEC frame buffer struct virtual address */ struct vdec_h264_dec_info { uint32_t dpb_sz; uint32_t resolution_changed; uint32_t realloc_mv_buf; uint32_t reserved; uint64_t bs_dma; uint64_t y_fb_dma; uint64_t c_fb_dma; uint64_t vdec_fb_va; }; /** * struct vdec_h264_vsi - shared memory for decode information exchange * between VPU and Host. * The memory is allocated by VPU then mapping to Host * in vpu_dec_init() and freed in vpu_dec_deinit() * by VPU. * AP-W/R : AP is writer/reader on this item * VPU-W/R: VPU is write/reader on this item * @hdr_buf : Header parsing buffer (AP-W, VPU-R) * @pred_buf_dma : HW working predication buffer dma address (AP-W, VPU-R) * @mv_buf_dma : HW working motion vector buffer dma address (AP-W, VPU-R) * @list_free : free frame buffer ring list (AP-W/R, VPU-W) * @list_disp : display frame buffer ring list (AP-R, VPU-W) * @dec : decode information (AP-R, VPU-W) * @pic : picture information (AP-R, VPU-W) * @crop : crop information (AP-R, VPU-W) */ struct vdec_h264_vsi { unsigned char hdr_buf[HDR_PARSING_BUF_SZ]; uint64_t pred_buf_dma; uint64_t mv_buf_dma[H264_MAX_FB_NUM]; struct h264_ring_fb_list list_free; struct h264_ring_fb_list list_disp; struct vdec_h264_dec_info dec; struct vdec_pic_info pic; struct v4l2_rect crop; }; /** * struct vdec_h264_inst - h264 decoder instance * @num_nalu : how many nalus be decoded * @ctx : point to mtk_vcodec_dec_ctx * @pred_buf : HW working predication buffer * @mv_buf : HW working motion vector buffer * @vpu : VPU instance * @vsi : VPU shared information */ struct vdec_h264_inst { unsigned int num_nalu; struct mtk_vcodec_dec_ctx *ctx; struct mtk_vcodec_mem pred_buf; struct mtk_vcodec_mem mv_buf[H264_MAX_FB_NUM]; struct vdec_vpu_inst vpu; struct vdec_h264_vsi *vsi; }; static unsigned int get_mv_buf_size(unsigned int width, unsigned int height) { return HW_MB_STORE_SZ * (width/MB_UNIT_LEN) * (height/MB_UNIT_LEN); } static int allocate_predication_buf(struct vdec_h264_inst *inst) { int err = 0; inst->pred_buf.size = BUF_PREDICTION_SZ; err = mtk_vcodec_mem_alloc(inst->ctx, &inst->pred_buf); if (err) { mtk_vdec_err(inst->ctx, "failed to allocate ppl buf"); return err; } inst->vsi->pred_buf_dma = inst->pred_buf.dma_addr; return 0; } static void free_predication_buf(struct vdec_h264_inst *inst) { struct mtk_vcodec_mem *mem = NULL; inst->vsi->pred_buf_dma = 0; mem = &inst->pred_buf; if (mem->va) mtk_vcodec_mem_free(inst->ctx, mem); } static int alloc_mv_buf(struct vdec_h264_inst *inst, struct vdec_pic_info *pic) { int i; int err; struct mtk_vcodec_mem *mem = NULL; unsigned int buf_sz = get_mv_buf_size(pic->buf_w, pic->buf_h); for (i = 0; i < H264_MAX_FB_NUM; i++) { mem = &inst->mv_buf[i]; if (mem->va) mtk_vcodec_mem_free(inst->ctx, mem); mem->size = buf_sz; err = mtk_vcodec_mem_alloc(inst->ctx, mem); if (err) { mtk_vdec_err(inst->ctx, "failed to allocate mv buf"); return err; } inst->vsi->mv_buf_dma[i] = mem->dma_addr; } return 0; } static void free_mv_buf(struct vdec_h264_inst *inst) { int i; struct mtk_vcodec_mem *mem = NULL; for (i = 0; i < H264_MAX_FB_NUM; i++) { inst->vsi->mv_buf_dma[i] = 0; mem = &inst->mv_buf[i]; if (mem->va) mtk_vcodec_mem_free(inst->ctx, mem); } } static int check_list_validity(struct vdec_h264_inst *inst, bool disp_list) { struct h264_ring_fb_list *list; list = disp_list ? &inst->vsi->list_disp : &inst->vsi->list_free; if (list->count > H264_MAX_FB_NUM || list->read_idx >= H264_MAX_FB_NUM || list->write_idx >= H264_MAX_FB_NUM) { mtk_vdec_err(inst->ctx, "%s list err: cnt=%d r_idx=%d w_idx=%d", disp_list ? "disp" : "free", list->count, list->read_idx, list->write_idx); return -EINVAL; } return 0; } static void put_fb_to_free(struct vdec_h264_inst *inst, struct vdec_fb *fb) { struct h264_ring_fb_list *list; if (fb) { if (check_list_validity(inst, false)) return; list = &inst->vsi->list_free; if (list->count == H264_MAX_FB_NUM) { mtk_vdec_err(inst->ctx, "[FB] put fb free_list full"); return; } mtk_vdec_debug(inst->ctx, "[FB] put fb into free_list @(%p, %llx)", fb->base_y.va, (u64)fb->base_y.dma_addr); list->fb_list[list->write_idx].vdec_fb_va = (u64)(uintptr_t)fb; list->write_idx = (list->write_idx == H264_MAX_FB_NUM - 1) ? 0 : list->write_idx + 1; list->count++; } } static void get_pic_info(struct vdec_h264_inst *inst, struct vdec_pic_info *pic) { *pic = inst->vsi->pic; mtk_vdec_debug(inst->ctx, "pic(%d, %d), buf(%d, %d)", pic->pic_w, pic->pic_h, pic->buf_w, pic->buf_h); mtk_vdec_debug(inst->ctx, "fb size: Y(%d), C(%d)", pic->fb_sz[0], pic->fb_sz[1]); } static void get_crop_info(struct vdec_h264_inst *inst, struct v4l2_rect *cr) { cr->left = inst->vsi->crop.left; cr->top = inst->vsi->crop.top; cr->width = inst->vsi->crop.width; cr->height = inst->vsi->crop.height; mtk_vdec_debug(inst->ctx, "l=%d, t=%d, w=%d, h=%d", cr->left, cr->top, cr->width, cr->height); } static void get_dpb_size(struct vdec_h264_inst *inst, unsigned int *dpb_sz) { *dpb_sz = inst->vsi->dec.dpb_sz; mtk_vdec_debug(inst->ctx, "sz=%d", *dpb_sz); } static int vdec_h264_init(struct mtk_vcodec_dec_ctx *ctx) { struct vdec_h264_inst *inst = NULL; int err; inst = kzalloc(sizeof(*inst), GFP_KERNEL); if (!inst) return -ENOMEM; inst->ctx = ctx; inst->vpu.id = IPI_VDEC_H264; inst->vpu.ctx = ctx; err = vpu_dec_init(&inst->vpu); if (err) { mtk_vdec_err(ctx, "vdec_h264 init err=%d", err); goto error_free_inst; } inst->vsi = (struct vdec_h264_vsi *)inst->vpu.vsi; err = allocate_predication_buf(inst); if (err) goto error_deinit; mtk_vdec_debug(ctx, "H264 Instance >> %p", inst); ctx->drv_handle = inst; return 0; error_deinit: vpu_dec_deinit(&inst->vpu); error_free_inst: kfree(inst); return err; } static void vdec_h264_deinit(void *h_vdec) { struct vdec_h264_inst *inst = (struct vdec_h264_inst *)h_vdec; vpu_dec_deinit(&inst->vpu); free_predication_buf(inst); free_mv_buf(inst); kfree(inst); } static int find_start_code(unsigned char *data, unsigned int data_sz) { if (data_sz > 3 && data[0] == 0 && data[1] == 0 && data[2] == 1) return 3; if (data_sz > 4 && data[0] == 0 && data[1] == 0 && data[2] == 0 && data[3] == 1) return 4; return -1; } static int vdec_h264_decode(void *h_vdec, struct mtk_vcodec_mem *bs, struct vdec_fb *fb, bool *res_chg) { struct vdec_h264_inst *inst = (struct vdec_h264_inst *)h_vdec; struct vdec_vpu_inst *vpu = &inst->vpu; int nal_start_idx = 0; int err = 0; unsigned int nal_start; unsigned int nal_type; unsigned char *buf; unsigned int buf_sz; unsigned int data[2]; uint64_t vdec_fb_va = (u64)(uintptr_t)fb; uint64_t y_fb_dma = fb ? (u64)fb->base_y.dma_addr : 0; uint64_t c_fb_dma = fb ? (u64)fb->base_c.dma_addr : 0; mtk_vdec_debug(inst->ctx, "+ [%d] FB y_dma=%llx c_dma=%llx va=%p", ++inst->num_nalu, y_fb_dma, c_fb_dma, fb); /* bs NULL means flush decoder */ if (bs == NULL) return vpu_dec_reset(vpu); buf = (unsigned char *)bs->va; buf_sz = bs->size; nal_start_idx = find_start_code(buf, buf_sz); if (nal_start_idx < 0) { mtk_vdec_err(inst->ctx, "invalid nal start code"); err = -EIO; goto err_free_fb_out; } nal_start = buf[nal_start_idx]; nal_type = NAL_TYPE(buf[nal_start_idx]); mtk_vdec_debug(inst->ctx, "\n + NALU[%d] type %d +\n", inst->num_nalu, nal_type); if (nal_type == NAL_H264_PPS) { buf_sz -= nal_start_idx; if (buf_sz > HDR_PARSING_BUF_SZ) { err = -EILSEQ; goto err_free_fb_out; } memcpy(inst->vsi->hdr_buf, buf + nal_start_idx, buf_sz); } inst->vsi->dec.bs_dma = (uint64_t)bs->dma_addr; inst->vsi->dec.y_fb_dma = y_fb_dma; inst->vsi->dec.c_fb_dma = c_fb_dma; inst->vsi->dec.vdec_fb_va = vdec_fb_va; data[0] = buf_sz; data[1] = nal_start; err = vpu_dec_start(vpu, data, 2); if (err) { if (err > 0 && (DEC_ERR_RET(err) == H264_ERR_NOT_VALID)) { mtk_vdec_err(inst->ctx, "- error bitstream - err = %d -", err); err = -EIO; } goto err_free_fb_out; } *res_chg = inst->vsi->dec.resolution_changed; if (*res_chg) { struct vdec_pic_info pic; mtk_vdec_debug(inst->ctx, "- resolution changed -"); get_pic_info(inst, &pic); if (inst->vsi->dec.realloc_mv_buf) { err = alloc_mv_buf(inst, &pic); if (err) goto err_free_fb_out; } } if (nal_type == NAL_NON_IDR_SLICE || nal_type == NAL_IDR_SLICE) { /* wait decoder done interrupt */ err = mtk_vcodec_wait_for_done_ctx(inst->ctx, MTK_INST_IRQ_RECEIVED, WAIT_INTR_TIMEOUT_MS, 0); if (err) goto err_free_fb_out; vpu_dec_end(vpu); } mtk_vdec_debug(inst->ctx, "\n - NALU[%d] type=%d -\n", inst->num_nalu, nal_type); return 0; err_free_fb_out: put_fb_to_free(inst, fb); mtk_vdec_err(inst->ctx, "\n - NALU[%d] err=%d -\n", inst->num_nalu, err); return err; } static void vdec_h264_get_fb(struct vdec_h264_inst *inst, struct h264_ring_fb_list *list, bool disp_list, struct vdec_fb **out_fb) { struct vdec_fb *fb; if (check_list_validity(inst, disp_list)) return; if (list->count == 0) { mtk_vdec_debug(inst->ctx, "[FB] there is no %s fb", disp_list ? "disp" : "free"); *out_fb = NULL; return; } fb = (struct vdec_fb *) (uintptr_t)list->fb_list[list->read_idx].vdec_fb_va; fb->status |= (disp_list ? FB_ST_DISPLAY : FB_ST_FREE); *out_fb = fb; mtk_vdec_debug(inst->ctx, "[FB] get %s fb st=%d poc=%d %llx", disp_list ? "disp" : "free", fb->status, list->fb_list[list->read_idx].poc, list->fb_list[list->read_idx].vdec_fb_va); list->read_idx = (list->read_idx == H264_MAX_FB_NUM - 1) ? 0 : list->read_idx + 1; list->count--; } static int vdec_h264_get_param(void *h_vdec, enum vdec_get_param_type type, void *out) { struct vdec_h264_inst *inst = (struct vdec_h264_inst *)h_vdec; switch (type) { case GET_PARAM_DISP_FRAME_BUFFER: vdec_h264_get_fb(inst, &inst->vsi->list_disp, true, out); break; case GET_PARAM_FREE_FRAME_BUFFER: vdec_h264_get_fb(inst, &inst->vsi->list_free, false, out); break; case GET_PARAM_PIC_INFO: get_pic_info(inst, out); break; case GET_PARAM_DPB_SIZE: get_dpb_size(inst, out); break; case GET_PARAM_CROP_INFO: get_crop_info(inst, out); break; default: mtk_vdec_err(inst->ctx, "invalid get parameter type=%d", type); return -EINVAL; } return 0; } const struct vdec_common_if vdec_h264_if = { .init = vdec_h264_init, .decode = vdec_h264_decode, .get_param = vdec_h264_get_param, .deinit = vdec_h264_deinit, }; |