/** * Copyright (C) 2016 Fuzhou Rockchip Electronics Co., Ltd * author: chenhengming chm@rock-chips.com * Alpha Lin, alpha.lin@rock-chips.com * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and * may be copied, distributed, and modified under those terms. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * */ #include <linux/clk.h> #include <linux/compiler.h> #include <linux/delay.h> #include <linux/slab.h> #include <linux/types.h> #include <linux/uaccess.h> #include <linux/reset.h> #include "vpu_iommu_ops.h" #include "mpp_service.h" #include "mpp_dev_common.h" #include "mpp_dev_rkvenc.h" #define MPP_ALIGN_SIZE 0x1000 #define LINK_TABLE_START 12 #define LINK_TABLE_LEN 128 #define RKVENC_ENC_START 0x004 #define RKVENC_LKT_NUM(x) (((x) & 0xff) << 0) #define RKVENC_CMD(x) (((x) & 0x3) << 8) #define RKVENC_CLK_GATE_EN BIT(16) #define RKVENC_SAFE_CLR 0x008 #define RKVENC_LKT_ADDR 0x00c #define RKVENC_INT_EN 0x010 #define RKVENC_INT_EN_SAFE_CLEAR BIT(2) #define RKVENC_INT_EN_TIMEOUT BIT(8) #define RKVENC_INT_MSK 0x014 #define RKVENC_INT_MSK_OVERFLOW BIT(4) #define RKVENC_INT_MSK_W_FIFO_FULL BIT(5) #define RKVENC_INT_MSK_W_CHN_ERROR BIT(6) #define RKVENC_INT_MSK_R_CHN_ERROR BIT(7) #define RKVENC_INT_MSK_TIMEOUT BIT(8) #define RKVENC_INT_CLR 0x018 #define RKVENC_INT_STATUS 0x01c #define RKVENC_ONE_FRAME_FINISH BIT(0) #define RKVENC_LINK_TABLE_FINISH BIT(1) #define RKVENC_SAFE_CLEAR_FINISH BIT(2) #define RKVENC_ONE_SLICE_FINISH BIT(3) #define RKVENC_BIT_STREAM_OVERFLOW BIT(4) #define RKVENC_AXI_WRITE_FIFO_FULL BIT(5) #define RKVENC_AXI_WRITE_CHANNEL_ERROR BIT(6) #define RKVENC_AXI_READ_CHANNEL_ERROR BIT(7) #define RKVENC_TIMEOUT_ERROR BIT(8) #define RKVENC_INT_ERROR_BITS ((RKVENC_BIT_STREAM_OVERFLOW) | \ (RKVENC_AXI_WRITE_FIFO_FULL) | \ (RKVENC_AXI_WRITE_CHANNEL_ERROR) |\ (RKVENC_AXI_READ_CHANNEL_ERROR) | \ (RKVENC_TIMEOUT_ERROR)) #define RKVENC_ENC_PIC 0x034 #define RKVENC_ENC_PIC_NODE_INT_EN BIT(31) #define RKVENC_ENC_WDG 0x038 #define RKVENC_PPLN_ENC_LMT(x) (((x) & 0xff) << 0) #define RKVENC_OSD_CFG 0x1c0 #define RKVENC_OSD_PLT_TYPE BIT(17) #define RKVENC_OSD_CLK_SEL_BIT BIT(16) #define RKVENC_STATUS(i) (0x210 + (4 * (i))) #define RKVENC_BSL_STATUS 0x210 #define RKVENC_BITSTREAM_LENGTH(x) ((x) & 0x7FFFFFF) #define RKVENC_ENC_STATUS 0x220 #define RKVENC_ENC_STATUS_ENC(x) (((x) >> 0) & 0x3) #define RKVENC_LKT_STATUS 0x224 #define RKVENC_LKT_STATUS_FNUM_ENC(x) (((x) >> 0) & 0xff) #define RKVENC_LKT_STATUS_FNUM_CFG(x) (((x) >> 8) & 0xff) #define RKVENC_LKT_STATUS_FNUM_INT(x) (((x) >> 16) & 0xff) #define RKVENC_OSD_PLT(i) (0x400 + (4 * (i))) #define to_rkvenc_ctx(ctx) \ container_of(ctx, struct rkvenc_ctx, ictx) #define to_rkvenc_session(session) \ container_of(session, struct rkvenc_session, isession) #define to_rkvenc_dev(dev) \ container_of(dev, struct rockchip_rkvenc_dev, idev) /* * file handle translate information */ static const char trans_tbl_rkvenc[] = { 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 124, 125, 126, 127, 128, 129, 130, 131 }; static struct mpp_trans_info trans_rkvenc[1] = { [0] = { .count = sizeof(trans_tbl_rkvenc), .table = trans_tbl_rkvenc, }, }; static struct mpp_dev_rkvenc_reg mpp_rkvenc_dummy_reg = { .enc_rsl = 0x00070007, /* 64x64 */ .enc_pic = 0x00001714, /* h264, qp 30 */ .enc_wdg = 0x00000002, .dtrns_map = 0x00007000, .dtrns_cfg = 0x0000007f, .src_fmt = 0x00000018, /* nv12 */ .src_strd = 0x003f003f, .sli_spl = 0x00000004, .me_rnge = 0x00002f7b, .me_cnst = 0x000e0505, .me_ram = 0x000e79ab, .rc_qp = 0x07340000, .rdo_cfg = 0x00000002, .synt_nal = 0x00000017, .synt_sps = 0x0000019c, .synt_pps = 0x01000d03, .synt_sli0 = 0x00000002, }; static int rockchip_mpp_rkvenc_reset(struct rockchip_mpp_dev *mpp); /* * In order to workaround hw bug which make the first frame run failure with * timeout interrupt occur, we make a dummy 64x64 encoding on power on here to * cover the hw bug. */ static void rockchip_mpp_war_init(struct rockchip_mpp_dev *mpp) { struct rockchip_rkvenc_dev *enc = to_rkvenc_dev(mpp); size_t img_width = 64; size_t img_height = 64; size_t img_y_size = img_width * img_height; size_t img_uv_size = img_y_size / 2; size_t img_u_size = img_uv_size / 2; size_t img_size = img_y_size + img_uv_size; enc->war_reg = &mpp_rkvenc_dummy_reg; /* 4k align required */ enc->war_reg->adr_rfpw = enc->war_dma_addr; enc->war_reg->adr_srcy = enc->war_reg->adr_rfpw + img_size; enc->war_reg->adr_srcu = enc->war_reg->adr_srcy + img_y_size; enc->war_reg->adr_srcv = enc->war_reg->adr_srcu + img_u_size; enc->war_reg->adr_bsbb = enc->war_reg->adr_srcv + img_u_size; enc->war_reg->adr_bsbt = enc->war_reg->adr_bsbb + img_size; enc->war_reg->adr_bsbr = enc->war_reg->adr_bsbb; enc->war_reg->adr_bsbw = enc->war_reg->adr_bsbb; /* 1k align required */ enc->war_reg->adr_dspw = enc->war_dma_addr + 0x4000; enc->war_reg->adr_dspr = enc->war_reg->adr_dspw + 0x400; enc->dummy_ctx = kzalloc(sizeof(*enc->dummy_ctx), GFP_KERNEL); if (!enc->dummy_ctx) return; enc->dummy_ctx->ictx.mpp = mpp; enc->dummy_ctx->ictx.session = NULL; enc->dummy_ctx->mode = RKVENC_MODE_ONEFRAME; enc->dummy_ctx->cfg.mode = RKVENC_MODE_ONEFRAME; atomic_set(&enc->dummy_ctx_in_used, 0); memcpy(enc->dummy_ctx->cfg.elem[0].reg, enc->war_reg, sizeof(*enc->war_reg)); enc->dummy_ctx->cfg.elem[0].reg_num = sizeof(*enc->war_reg) / 4; } static void rockchip_mpp_rkvenc_cfg_palette(struct rockchip_mpp_dev *mpp, struct mpp_session *isession) { struct rkvenc_session *session; int i; u32 reg; mpp_debug_enter(); if (!isession) { mpp_debug(DEBUG_TASK_INFO, "fake ctx, do not cfg palette\n"); return; } session = to_rkvenc_session(isession); if (!session->palette_valid) return; reg = mpp_read(mpp, RKVENC_OSD_CFG); mpp_write(mpp, reg & (~RKVENC_OSD_CLK_SEL_BIT), RKVENC_OSD_CFG); for (i = 0; i < RKVENC_OSD_PLT_LEN; i++) mpp_write(mpp, session->palette.plalette[i].elem, RKVENC_OSD_PLT(i)); mpp_write(mpp, reg | RKVENC_OSD_CLK_SEL_BIT, RKVENC_OSD_CFG); mpp_debug_leave(); } static struct mpp_ctx *rockchip_mpp_rkvenc_init(struct rockchip_mpp_dev *mpp, struct mpp_session *session, void __user *src, u32 size) { struct rockchip_rkvenc_dev *enc = to_rkvenc_dev(mpp); struct rkvenc_ctx *ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); int i; mpp_debug_enter(); if (!ctx) return NULL; /* HW defeat workaround start */ if (!mpp_dev_is_power_on(mpp) && enc->dummy_ctx && atomic_inc_return(&enc->dummy_ctx_in_used) == 1) { mpp_debug(DEBUG_RESET, "add a dummy ctx\n"); mpp_srv_pending_locked(mpp->srv, &enc->dummy_ctx->ictx); } mpp_dev_common_ctx_init(mpp, &ctx->ictx); ctx->ictx.session = session; ctx->mode = RKVENC_MODE_LINKTABLE_FIX; size = size > sizeof(ctx->cfg) ? sizeof(ctx->cfg) : size; if (copy_from_user(&ctx->cfg, src, size)) { mpp_err("error: copy_from_user failed in reg_init\n"); kfree(ctx); return NULL; } ctx->mode = ctx->cfg.mode; if (ctx->mode >= RKVENC_MODE_NUM || ctx->mode == RKVENC_MODE_NONE) { mpp_err("Invalid rkvenc running mode %d\n", (int)ctx->mode); kfree(ctx); return NULL; } else if (ctx->mode == RKVENC_MODE_ONEFRAME && ctx->cfg.tbl_num > 1) { mpp_err("Configuration miss match, ignore redundant cfg\n"); ctx->cfg.tbl_num = 1; } mpp_debug(DEBUG_SET_REG, "tbl num %u, mode %u\n", ctx->cfg.tbl_num, ctx->cfg.mode); for (i = 0; i < ctx->cfg.tbl_num; i++) { if (mpp_reg_address_translate(mpp, ctx->cfg.elem[i].reg, &ctx->ictx, 0) < 0) { mpp_err("error: translate reg address failed.\n"); if (unlikely(mpp_dev_debug & DEBUG_DUMP_ERR_REG)) mpp_dump_reg_mem(ctx->cfg.elem[i].reg, ctx->cfg.elem[i].reg_num); mpp_dev_common_ctx_deinit(mpp, &ctx->ictx); kfree(ctx); return NULL; } mpp_debug(DEBUG_SET_REG, "extra info cnt %u, magic %08x", ctx->cfg.elem[i].ext_inf.cnt, ctx->cfg.elem[i].ext_inf.magic); mpp_translate_extra_info(&ctx->ictx, &ctx->cfg.elem[i].ext_inf, ctx->cfg.elem[i].reg); } mpp_debug_leave(); return &ctx->ictx; } static int rockchip_mpp_rkvenc_reset_init(struct rockchip_mpp_dev *mpp) { struct rockchip_rkvenc_dev *enc = to_rkvenc_dev(mpp); mpp_debug(DEBUG_RESET, "reset init in:\n"); enc->rst_a = devm_reset_control_get(mpp->dev, "video_a"); enc->rst_h = devm_reset_control_get(mpp->dev, "video_h"); enc->rst_v = devm_reset_control_get(mpp->dev, "video_c"); if (IS_ERR_OR_NULL(enc->rst_a)) { mpp_err("No aclk reset resource define\n"); enc->rst_a = NULL; } if (IS_ERR_OR_NULL(enc->rst_h)) { mpp_err("No hclk reset resource define\n"); enc->rst_h = NULL; } if (IS_ERR_OR_NULL(enc->rst_v)) { mpp_err("No core reset resource define\n"); enc->rst_v = NULL; } return 0; } static int rockchip_mpp_rkvenc_reset(struct rockchip_mpp_dev *mpp) { struct rockchip_rkvenc_dev *enc = to_rkvenc_dev(mpp); int cnt = 100; if (enc->rst_a && enc->rst_h && enc->rst_v) { mpp_debug(DEBUG_RESET, "reset in\n"); mpp_write(mpp, 0, RKVENC_INT_EN); mpp_write(mpp, 1, RKVENC_SAFE_CLR); while (cnt-- > 0) { int status; usleep_range(100, 200); status = mpp_read(mpp, RKVENC_ENC_STATUS); if (status & 4) { mpp_debug(DEBUG_RESET, "st_enc %08x\n", status); break; } } reset_control_assert(enc->rst_v); reset_control_assert(enc->rst_a); reset_control_assert(enc->rst_h); udelay(1); reset_control_deassert(enc->rst_v); reset_control_deassert(enc->rst_a); reset_control_deassert(enc->rst_h); mpp_debug(DEBUG_RESET, "reset out\n"); } return 0; } static int rockchip_mpp_rkvenc_prepare(struct rockchip_mpp_dev *mpp) { struct rkvenc_ctx *ctx_curr; struct rkvenc_ctx *ctx_ready; struct rockchip_rkvenc_dev *enc = to_rkvenc_dev(mpp); u32 lkt_status; u32 fnum_int; u32 fnum_cfg; u32 fnum_enc; u8 *cpu_addr; int i; u32 reg = 0; mpp_debug_enter(); if (!mpp_srv_is_running(mpp->srv)) return 0; /* if service running, determine link table mode */ ctx_curr = to_rkvenc_ctx(mpp_srv_get_current_ctx(mpp->srv)); ctx_ready = to_rkvenc_ctx(mpp_srv_get_pending_ctx(mpp->srv)); if (ctx_curr->mode != RKVENC_MODE_LINKTABLE_UPDATE || ctx_ready->mode != ctx_curr->mode) { mpp_debug(DEBUG_TASK_INFO, "link table condition not fulfill\n"); return -1; } lkt_status = mpp_read(mpp, RKVENC_LKT_STATUS); fnum_int = RKVENC_LKT_STATUS_FNUM_INT(lkt_status); fnum_cfg = RKVENC_LKT_STATUS_FNUM_CFG(lkt_status); fnum_enc = RKVENC_LKT_STATUS_FNUM_ENC(lkt_status); cpu_addr = (u8 *)enc->lkt_cpu_addr + fnum_cfg * LINK_TABLE_LEN * 4; mpp_dev_power_on(mpp); mpp_debug(DEBUG_GET_REG, "frame number int %u, cfg %u, enc %u\n", fnum_int, fnum_cfg, fnum_enc); for (i = 0; i < ctx_ready->cfg.tbl_num; i++) { u32 *src = ctx_ready->cfg.elem[i].reg; memcpy(cpu_addr + i * LINK_TABLE_LEN * 4, &src[LINK_TABLE_START], LINK_TABLE_LEN * 4); } reg = RKVENC_CLK_GATE_EN | RKVENC_CMD(ctx_curr->mode) | RKVENC_LKT_NUM(ctx_ready->cfg.tbl_num); mpp_write_relaxed(mpp, reg, RKVENC_ENC_START); /* remove from pending queue */ mpp_dev_common_ctx_deinit(mpp, &ctx_ready->ictx); mpp_debug_leave(); return 0; } static int rockchip_mpp_rkvenc_run(struct rockchip_mpp_dev *mpp) { struct rkvenc_ctx *ctx = to_rkvenc_ctx(mpp_srv_get_current_ctx(mpp->srv)); struct rockchip_rkvenc_dev *enc = to_rkvenc_dev(mpp); u32 reg; int i; mpp_debug_enter(); switch (ctx->mode) { case RKVENC_MODE_ONEFRAME: { u32 *src = ctx->cfg.elem[0].reg; for (i = 2; i < (LINK_TABLE_START + LINK_TABLE_LEN); i++) mpp_write_relaxed(mpp, src[i], i * 4); rockchip_mpp_rkvenc_cfg_palette(mpp, ctx->ictx.session); mpp_write_relaxed(mpp, 0x1ff, RKVENC_INT_EN); reg = RKVENC_CLK_GATE_EN | RKVENC_CMD(1); mpp_write(mpp, reg, RKVENC_ENC_START); break; } case RKVENC_MODE_LINKTABLE_FIX: case RKVENC_MODE_LINKTABLE_UPDATE: { for (i = 0; i < ctx->cfg.tbl_num; i++) { u32 *src = ctx->cfg.elem[i].reg; memcpy(enc->lkt_cpu_addr + i * LINK_TABLE_LEN * 4, &src[LINK_TABLE_START], LINK_TABLE_LEN * 4); } rockchip_mpp_rkvenc_cfg_palette(mpp, ctx->ictx.session); mpp_write_relaxed(mpp, enc->lkt_dma_addr, RKVENC_LKT_ADDR); mpp_write_relaxed(mpp, 0xffffffff, RKVENC_INT_EN); reg = RKVENC_LKT_NUM(ctx->cfg.tbl_num) | RKVENC_CMD(RKVENC_MODE_LINKTABLE_FIX) | RKVENC_CLK_GATE_EN; mpp_write_relaxed(mpp, reg, RKVENC_ENC_START); break; } default: break; } mpp_debug_leave(); return 0; } static int rockchip_mpp_rkvenc_done(struct rockchip_mpp_dev *mpp) { struct mpp_ctx *ictx = mpp_srv_get_current_ctx(mpp->srv); struct rkvenc_ctx *ctx; struct rockchip_rkvenc_dev *enc = to_rkvenc_dev(mpp); struct rkvenc_result *result; int i; mpp_debug_enter(); if (IS_ERR_OR_NULL(ictx)) { mpp_err("Invaidate context to save result\n"); return -1; } ctx = to_rkvenc_ctx(ictx); if (enc->irq_status & RKVENC_INT_ERROR_BITS) /* * according to war running, if the dummy encoding * running with timeout, we enable a safe clear process, * we reset the ip, and complete the war procedure. */ atomic_inc(&mpp->reset_request); if (ctx == enc->dummy_ctx) { mpp_debug(DEBUG_RESET, "war done\n"); /* for war do not trigger service done process */ list_del_init(&ictx->status_link); atomic_set(&enc->dummy_ctx_in_used, 0); /* dummy ctx, do not trigger service to wake up done process */ return -1; } result = &ctx->result; switch (ctx->mode) { case RKVENC_MODE_ONEFRAME: result->tbl_num = 1; result->elem[0].status = enc->irq_status; for (i = 0; i < sizeof(result->elem[0].result) / 4; i++) result->elem[0].result[i] = mpp_read(mpp, RKVENC_STATUS(i)); break; case RKVENC_MODE_LINKTABLE_FIX: case RKVENC_MODE_LINKTABLE_UPDATE: { u32 lkt_status = mpp_read(mpp, RKVENC_LKT_STATUS); u32 fnum_int = RKVENC_LKT_STATUS_FNUM_INT(lkt_status); u32 fnum_cfg = RKVENC_LKT_STATUS_FNUM_CFG(lkt_status); u32 fnum_enc = RKVENC_LKT_STATUS_FNUM_ENC(lkt_status); u32 *lkt_cpu_addr = (u32 *)enc->lkt_cpu_addr; if (unlikely(mpp_dev_debug & DEBUG_DUMP_ERR_REG)) mpp_dump_reg_mem(lkt_cpu_addr, LINK_TABLE_LEN); result->tbl_num = fnum_int; for (i = 0; i < fnum_int; i++) { result->elem[i].status = enc->irq_status; memcpy(result->elem[i].result, &lkt_cpu_addr[i * LINK_TABLE_LEN + 120], sizeof(result->elem[i].result)); mpp_debug(DEBUG_GET_REG, "stream length %u\n", result->elem[i].result[0]); } mpp_debug(DEBUG_GET_REG, "frame number %u, %u, %u\n", fnum_int, fnum_cfg, fnum_enc); break; } default: break; } mpp_debug_leave(); return 0; } static int rockchip_mpp_rkvenc_irq(struct rockchip_mpp_dev *mpp) { struct rockchip_rkvenc_dev *enc = to_rkvenc_dev(mpp); enc->irq_status = mpp_read(mpp, RKVENC_INT_STATUS); mpp_debug_enter(); if (enc->irq_status == 0) return -1; mpp_debug(DEBUG_IRQ_STATUS, "irq_status: %08x\n", enc->irq_status); mpp_write(mpp, 0xffffffff, RKVENC_INT_CLR); if (enc->irq_status & RKVENC_INT_ERROR_BITS) { mpp_err("error irq %08x\n", enc->irq_status); /* time out error */ mpp_write(mpp, RKVENC_INT_ERROR_BITS, RKVENC_INT_MSK); } mpp_debug_leave(); return 0; } static int rockchip_mpp_rkvenc_result(struct rockchip_mpp_dev *mpp, struct mpp_ctx *ictx, u32 __user *dst) { struct rkvenc_ctx *ctx = to_rkvenc_ctx(ictx); struct rkvenc_result *result = &ctx->result; unsigned long tbl_size = sizeof(result->tbl_num) + sizeof(result->elem[0]) * result->tbl_num; switch (ctx->mode) { case RKVENC_MODE_ONEFRAME: case RKVENC_MODE_LINKTABLE_FIX: case RKVENC_MODE_LINKTABLE_UPDATE: { if (copy_to_user(dst, &ctx->result, tbl_size)) { mpp_err("copy result to user failed\n"); return -1; } break; } default: mpp_err("invalid context mode %d\n", (int)ctx->mode); return -1; } return 0; } static long rockchip_mpp_rkvenc_ioctl(struct mpp_session *isession, unsigned int cmd, unsigned long arg) { struct rkvenc_session *session = to_rkvenc_session(isession); mpp_debug_enter(); switch (cmd) { case MPP_DEV_RKVENC_SET_COLOR_PALETTE: if (copy_from_user(&session->palette, (void __user *)arg, sizeof(session->palette))) { mpp_err("copy palette from user failed\n"); return -EINVAL; } session->palette_valid = true; break; default: mpp_err("%s, unknown ioctl cmd %x\n", dev_name(isession->mpp->dev), cmd); break; } mpp_debug_leave(); return 0; } static struct mpp_session *mpp_dev_rkvenc_open(struct rockchip_mpp_dev *mpp) { struct rkvenc_session *session = kzalloc(sizeof(*session), GFP_KERNEL); mpp_debug_enter(); if (!session) return NULL; session->palette_valid = false; mpp_debug_leave(); return &session->isession; } static void mpp_dev_rkvenc_free(struct mpp_session *isession) { struct rkvenc_session *session = to_rkvenc_session(isession); kfree(session); } struct mpp_dev_ops rkvenc_ops = { .init = rockchip_mpp_rkvenc_init, .prepare = rockchip_mpp_rkvenc_prepare, .run = rockchip_mpp_rkvenc_run, .done = rockchip_mpp_rkvenc_done, .irq = rockchip_mpp_rkvenc_irq, .result = rockchip_mpp_rkvenc_result, .ioctl = rockchip_mpp_rkvenc_ioctl, .open = mpp_dev_rkvenc_open, .free = mpp_dev_rkvenc_free, }; static void rockchip_mpp_rkvenc_power_on(struct rockchip_mpp_dev *mpp) { struct rockchip_rkvenc_dev *enc = to_rkvenc_dev(mpp); if (enc->aclk) clk_prepare_enable(enc->aclk); if (enc->hclk) clk_prepare_enable(enc->hclk); if (enc->core) clk_prepare_enable(enc->core); /* * Because hw cannot reset status fully in all its modules, we make a * reset here to make sure the hw status fully reset. */ rockchip_mpp_rkvenc_reset(mpp); } static void rockchip_mpp_rkvenc_power_off(struct rockchip_mpp_dev *mpp) { struct rockchip_rkvenc_dev *enc = to_rkvenc_dev(mpp); if (enc->core) clk_disable_unprepare(enc->core); if (enc->hclk) clk_disable_unprepare(enc->hclk); if (enc->aclk) clk_disable_unprepare(enc->aclk); } static int rockchip_mpp_rkvenc_probe(struct rockchip_mpp_dev *mpp) { struct rockchip_rkvenc_dev *enc = to_rkvenc_dev(mpp); struct mpp_session *session = list_first_entry(&mpp->srv->session, struct mpp_session, list_session); int ret; size_t tmp; enc->idev.ops = &rkvenc_ops; enc->lkt_hdl = vpu_iommu_alloc(mpp->iommu_info, session, LINK_TABLE_LEN * 4 * 256, MPP_ALIGN_SIZE); if (enc->lkt_hdl < 0) { dev_err(mpp->dev, "allocate link table buffer failure\n"); return -1; } ret = vpu_iommu_map_iommu(mpp->iommu_info, session, enc->lkt_hdl, &enc->lkt_dma_addr, &tmp); if (ret < 0) { dev_err(mpp->dev, "get link table dma_addr failed\n"); goto fail; } enc->lkt_cpu_addr = vpu_iommu_map_kernel(mpp->iommu_info, session, enc->lkt_hdl); /* * buffer for workaround context running, include input picture, output * stream, reconstruction picture. we set the output stream buffer to 1 * time picture size, so the total buffer size is 3 times picture size, * 64 * 64 * 3 / 2 * 3 = 4.5 * 4k. */ enc->war_hdl = vpu_iommu_alloc(mpp->iommu_info, session, MPP_ALIGN_SIZE * 5, MPP_ALIGN_SIZE); if (enc->war_hdl < 0) { dev_err(mpp->dev, "allocate workaround buffer failure\n"); goto fail; } ret = vpu_iommu_map_iommu(mpp->iommu_info, session, enc->war_hdl, &enc->war_dma_addr, &tmp); if (ret < 0) { dev_err(mpp->dev, "get war dma_addr failed\n"); goto fail; } rockchip_mpp_war_init(mpp); enc->aclk = devm_clk_get(mpp->dev, "aclk_vcodec"); if (IS_ERR_OR_NULL(enc->aclk)) { dev_err(mpp->dev, "failed on clk_get aclk\n"); goto fail; } enc->hclk = devm_clk_get(mpp->dev, "hclk_vcodec"); if (IS_ERR_OR_NULL(enc->hclk)) { dev_err(mpp->dev, "failed on clk_get hclk\n"); goto fail; } enc->core = devm_clk_get(mpp->dev, "clk_core"); if (IS_ERR_OR_NULL(enc->core)) { dev_err(mpp->dev, "failed on clk_get core\n"); goto fail; } rockchip_mpp_rkvenc_reset_init(mpp); return 0; fail: kfree(enc->dummy_ctx); if (enc->war_hdl >= 0) { vpu_iommu_unmap_iommu(mpp->iommu_info, session, enc->war_hdl); vpu_iommu_free(mpp->iommu_info, session, enc->war_hdl); } if (enc->lkt_cpu_addr) vpu_iommu_unmap_kernel(mpp->iommu_info, session, enc->lkt_hdl); if (enc->lkt_hdl >= 0) { vpu_iommu_unmap_iommu(mpp->iommu_info, session, enc->lkt_hdl); vpu_iommu_free(mpp->iommu_info, session, enc->lkt_hdl); } return -1; } static void rockchip_mpp_rkvenc_remove(struct rockchip_mpp_dev *mpp) { struct rockchip_rkvenc_dev *enc = to_rkvenc_dev(mpp); struct mpp_session *session = list_first_entry(&mpp->srv->session, struct mpp_session, list_session); vpu_iommu_unmap_kernel(mpp->iommu_info, session, enc->lkt_hdl); vpu_iommu_unmap_iommu(mpp->iommu_info, session, enc->lkt_hdl); vpu_iommu_free(mpp->iommu_info, session, enc->lkt_hdl); vpu_iommu_unmap_iommu(mpp->iommu_info, session, enc->war_hdl); vpu_iommu_free(mpp->iommu_info, session, enc->war_hdl); kfree(enc->dummy_ctx); } const struct rockchip_mpp_dev_variant rkvenc_variant = { .data_len = sizeof(struct rockchip_rkvenc_dev), .reg_len = 140, .trans_info = trans_rkvenc, .hw_probe = rockchip_mpp_rkvenc_probe, .hw_remove = rockchip_mpp_rkvenc_remove, .power_on = rockchip_mpp_rkvenc_power_on, .power_off = rockchip_mpp_rkvenc_power_off, .reset = rockchip_mpp_rkvenc_reset, }; EXPORT_SYMBOL(rkvenc_variant);