| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * Hantro VPU codec driver |
| * |
| * Copyright (C) 2018 Rockchip Electronics Co., Ltd. |
| */ |
| |
| #include "hantro.h" |
| |
| /* |
| * probs table with packed |
| */ |
| struct vp8_prob_tbl_packed { |
| u8 prob_mb_skip_false; |
| u8 prob_intra; |
| u8 prob_ref_last; |
| u8 prob_ref_golden; |
| u8 prob_segment[3]; |
| u8 padding0; |
| |
| u8 prob_luma_16x16_pred_mode[4]; |
| u8 prob_chroma_pred_mode[3]; |
| u8 padding1; |
| |
| /* mv prob */ |
| u8 prob_mv_context[2][19]; |
| u8 padding2[2]; |
| |
| /* coeff probs */ |
| u8 prob_coeffs[4][8][3][11]; |
| u8 padding3[96]; |
| }; |
| |
| /* |
| * filter taps taken to 7-bit precision, |
| * reference RFC6386#Page-16, filters[8][6] |
| */ |
| const u32 hantro_vp8_dec_mc_filter[8][6] = { |
| { 0, 0, 128, 0, 0, 0 }, |
| { 0, -6, 123, 12, -1, 0 }, |
| { 2, -11, 108, 36, -8, 1 }, |
| { 0, -9, 93, 50, -6, 0 }, |
| { 3, -16, 77, 77, -16, 3 }, |
| { 0, -6, 50, 93, -9, 0 }, |
| { 1, -8, 36, 108, -11, 2 }, |
| { 0, -1, 12, 123, -6, 0 } |
| }; |
| |
| void hantro_vp8_prob_update(struct hantro_ctx *ctx, |
| const struct v4l2_ctrl_vp8_frame_header *hdr) |
| { |
| const struct v4l2_vp8_entropy_header *entropy = &hdr->entropy_header; |
| u32 i, j, k; |
| u8 *dst; |
| |
| /* first probs */ |
| dst = ctx->vp8_dec.prob_tbl.cpu; |
| |
| dst[0] = hdr->prob_skip_false; |
| dst[1] = hdr->prob_intra; |
| dst[2] = hdr->prob_last; |
| dst[3] = hdr->prob_gf; |
| dst[4] = hdr->segment_header.segment_probs[0]; |
| dst[5] = hdr->segment_header.segment_probs[1]; |
| dst[6] = hdr->segment_header.segment_probs[2]; |
| dst[7] = 0; |
| |
| dst += 8; |
| dst[0] = entropy->y_mode_probs[0]; |
| dst[1] = entropy->y_mode_probs[1]; |
| dst[2] = entropy->y_mode_probs[2]; |
| dst[3] = entropy->y_mode_probs[3]; |
| dst[4] = entropy->uv_mode_probs[0]; |
| dst[5] = entropy->uv_mode_probs[1]; |
| dst[6] = entropy->uv_mode_probs[2]; |
| dst[7] = 0; /*unused */ |
| |
| /* mv probs */ |
| dst += 8; |
| dst[0] = entropy->mv_probs[0][0]; /* is short */ |
| dst[1] = entropy->mv_probs[1][0]; |
| dst[2] = entropy->mv_probs[0][1]; /* sign */ |
| dst[3] = entropy->mv_probs[1][1]; |
| dst[4] = entropy->mv_probs[0][8 + 9]; |
| dst[5] = entropy->mv_probs[0][9 + 9]; |
| dst[6] = entropy->mv_probs[1][8 + 9]; |
| dst[7] = entropy->mv_probs[1][9 + 9]; |
| dst += 8; |
| for (i = 0; i < 2; ++i) { |
| for (j = 0; j < 8; j += 4) { |
| dst[0] = entropy->mv_probs[i][j + 9 + 0]; |
| dst[1] = entropy->mv_probs[i][j + 9 + 1]; |
| dst[2] = entropy->mv_probs[i][j + 9 + 2]; |
| dst[3] = entropy->mv_probs[i][j + 9 + 3]; |
| dst += 4; |
| } |
| } |
| for (i = 0; i < 2; ++i) { |
| dst[0] = entropy->mv_probs[i][0 + 2]; |
| dst[1] = entropy->mv_probs[i][1 + 2]; |
| dst[2] = entropy->mv_probs[i][2 + 2]; |
| dst[3] = entropy->mv_probs[i][3 + 2]; |
| dst[4] = entropy->mv_probs[i][4 + 2]; |
| dst[5] = entropy->mv_probs[i][5 + 2]; |
| dst[6] = entropy->mv_probs[i][6 + 2]; |
| dst[7] = 0; /*unused */ |
| dst += 8; |
| } |
| |
| /* coeff probs (header part) */ |
| dst = ctx->vp8_dec.prob_tbl.cpu; |
| dst += (8 * 7); |
| for (i = 0; i < 4; ++i) { |
| for (j = 0; j < 8; ++j) { |
| for (k = 0; k < 3; ++k) { |
| dst[0] = entropy->coeff_probs[i][j][k][0]; |
| dst[1] = entropy->coeff_probs[i][j][k][1]; |
| dst[2] = entropy->coeff_probs[i][j][k][2]; |
| dst[3] = entropy->coeff_probs[i][j][k][3]; |
| dst += 4; |
| } |
| } |
| } |
| |
| /* coeff probs (footer part) */ |
| dst = ctx->vp8_dec.prob_tbl.cpu; |
| dst += (8 * 55); |
| for (i = 0; i < 4; ++i) { |
| for (j = 0; j < 8; ++j) { |
| for (k = 0; k < 3; ++k) { |
| dst[0] = entropy->coeff_probs[i][j][k][4]; |
| dst[1] = entropy->coeff_probs[i][j][k][5]; |
| dst[2] = entropy->coeff_probs[i][j][k][6]; |
| dst[3] = entropy->coeff_probs[i][j][k][7]; |
| dst[4] = entropy->coeff_probs[i][j][k][8]; |
| dst[5] = entropy->coeff_probs[i][j][k][9]; |
| dst[6] = entropy->coeff_probs[i][j][k][10]; |
| dst[7] = 0; /*unused */ |
| dst += 8; |
| } |
| } |
| } |
| } |
| |
| int hantro_vp8_dec_init(struct hantro_ctx *ctx) |
| { |
| struct hantro_dev *vpu = ctx->dev; |
| struct hantro_aux_buf *aux_buf; |
| unsigned int mb_width, mb_height; |
| size_t segment_map_size; |
| int ret; |
| |
| /* segment map table size calculation */ |
| mb_width = DIV_ROUND_UP(ctx->dst_fmt.width, 16); |
| mb_height = DIV_ROUND_UP(ctx->dst_fmt.height, 16); |
| segment_map_size = round_up(DIV_ROUND_UP(mb_width * mb_height, 4), 64); |
| |
| /* |
| * In context init the dma buffer for segment map must be allocated. |
| * And the data in segment map buffer must be set to all zero. |
| */ |
| aux_buf = &ctx->vp8_dec.segment_map; |
| aux_buf->size = segment_map_size; |
| aux_buf->cpu = dma_alloc_coherent(vpu->dev, aux_buf->size, |
| &aux_buf->dma, GFP_KERNEL); |
| if (!aux_buf->cpu) |
| return -ENOMEM; |
| |
| /* |
| * Allocate probability table buffer, |
| * total 1208 bytes, 4K page is far enough. |
| */ |
| aux_buf = &ctx->vp8_dec.prob_tbl; |
| aux_buf->size = sizeof(struct vp8_prob_tbl_packed); |
| aux_buf->cpu = dma_alloc_coherent(vpu->dev, aux_buf->size, |
| &aux_buf->dma, GFP_KERNEL); |
| if (!aux_buf->cpu) { |
| ret = -ENOMEM; |
| goto err_free_seg_map; |
| } |
| |
| return 0; |
| |
| err_free_seg_map: |
| dma_free_coherent(vpu->dev, ctx->vp8_dec.segment_map.size, |
| ctx->vp8_dec.segment_map.cpu, |
| ctx->vp8_dec.segment_map.dma); |
| |
| return ret; |
| } |
| |
| void hantro_vp8_dec_exit(struct hantro_ctx *ctx) |
| { |
| struct hantro_vp8_dec_hw_ctx *vp8_dec = &ctx->vp8_dec; |
| struct hantro_dev *vpu = ctx->dev; |
| |
| dma_free_coherent(vpu->dev, vp8_dec->segment_map.size, |
| vp8_dec->segment_map.cpu, vp8_dec->segment_map.dma); |
| dma_free_coherent(vpu->dev, vp8_dec->prob_tbl.size, |
| vp8_dec->prob_tbl.cpu, vp8_dec->prob_tbl.dma); |
| } |