From 9370bb92b2d16684ee45cf24e879c93c509162da Mon Sep 17 00:00:00 2001 From: hc <hc@nodka.com> Date: Thu, 19 Dec 2024 01:47:39 +0000 Subject: [PATCH] add wifi6 8852be driver --- kernel/drivers/video/rockchip/mpp/mpp_rkvenc2.c | 1382 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 files changed, 1,324 insertions(+), 58 deletions(-) diff --git a/kernel/drivers/video/rockchip/mpp/mpp_rkvenc2.c b/kernel/drivers/video/rockchip/mpp/mpp_rkvenc2.c index 90cdc13..111c106 100644 --- a/kernel/drivers/video/rockchip/mpp/mpp_rkvenc2.c +++ b/kernel/drivers/video/rockchip/mpp/mpp_rkvenc2.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: (GPL-2.0+ OR MIT) /* - * Copyright (c) 2022 Rockchip Electronics Co., Ltd + * Copyright (c) 2021 Rockchip Electronics Co., Ltd * * author: * Ding Wei, leo.ding@rock-chips.com @@ -32,6 +32,7 @@ #include <soc/rockchip/rockchip_ipa.h> #include <soc/rockchip/rockchip_opp_select.h> #include <soc/rockchip/rockchip_system_monitor.h> +#include <soc/rockchip/rockchip_iommu.h> #include "mpp_debug.h" #include "mpp_iommu.h" @@ -41,6 +42,11 @@ #define RKVENC_SESSION_MAX_BUFFERS 40 #define RKVENC_MAX_CORE_NUM 4 +#define RKVENC_MAX_DCHS_ID 4 +#define RKVENC_MAX_SLICE_FIFO_LEN 256 +#define RKVENC_SCLR_DONE_STA BIT(2) +#define RKVENC_WDG 0x38 +#define TIMEOUT_MS 100 #define to_rkvenc_info(info) \ container_of(info, struct rkvenc_hw_info, hw) @@ -116,6 +122,93 @@ u32 err_mask; }; +#define INT_STA_ENC_DONE_STA BIT(0) +#define INT_STA_SCLR_DONE_STA BIT(2) +#define INT_STA_SLC_DONE_STA BIT(3) +#define INT_STA_BSF_OFLW_STA BIT(4) +#define INT_STA_BRSP_OTSD_STA BIT(5) +#define INT_STA_WBUS_ERR_STA BIT(6) +#define INT_STA_RBUS_ERR_STA BIT(7) +#define INT_STA_WDG_STA BIT(8) + +#define INT_STA_ERROR (INT_STA_BRSP_OTSD_STA | \ + INT_STA_WBUS_ERR_STA | \ + INT_STA_RBUS_ERR_STA | \ + INT_STA_WDG_STA) + +#define DCHS_REG_OFFSET (0x304) +#define DCHS_CLASS_OFFSET (33) +#define DCHS_TXE (0x10) +#define DCHS_RXE (0x20) + +/* dual core hand-shake info */ +union rkvenc2_dual_core_handshake_id { + u64 val; + struct { + u32 txid : 2; + u32 rxid : 2; + u32 txe : 1; + u32 rxe : 1; + u32 working : 1; + u32 reserve0 : 1; + u32 txid_orig : 2; + u32 rxid_orig : 2; + u32 txid_map : 2; + u32 rxid_map : 2; + u32 offset : 11; + u32 reserve1 : 1; + u32 txe_orig : 1; + u32 rxe_orig : 1; + u32 txe_map : 1; + u32 rxe_map : 1; + u32 session_id; + }; +}; + +#define RKVENC2_REG_INT_EN (8) +#define RKVENC2_BIT_SLICE_DONE_EN BIT(3) + +#define RKVENC2_REG_INT_MASK (9) +#define RKVENC2_BIT_SLICE_DONE_MASK BIT(3) + +#define RKVENC2_REG_EXT_LINE_BUF_BASE (22) + +#define RKVENC2_REG_ENC_PIC (32) +#define RKVENC2_BIT_ENC_STND BIT(0) +#define RKVENC2_BIT_VAL_H264 0 +#define RKVENC2_BIT_VAL_H265 1 +#define RKVENC2_BIT_SLEN_FIFO BIT(30) + +#define RKVENC2_REG_SLI_SPLIT (56) +#define RKVENC2_BIT_SLI_SPLIT BIT(0) +#define RKVENC2_BIT_SLI_FLUSH BIT(15) + +#define RKVENC2_REG_SLICE_NUM_BASE (0x4034) +#define RKVENC2_REG_SLICE_LEN_BASE (0x4038) + +#define RKVENC2_REG_ST_BSB (0x402c) +#define RKVENC2_REG_ADR_BSBT (0x2b0) +#define RKVENC2_REG_ADR_BSBB (0x2b4) +#define RKVENC2_REG_ADR_BSBR (0x2b8) +#define RKVENC2_REG_ADR_BSBS (0x2bc) + +union rkvenc2_slice_len_info { + u32 val; + + struct { + u32 slice_len : 31; + u32 last : 1; + }; +}; + +struct rkvenc_poll_slice_cfg { + s32 poll_type; + s32 poll_ret; + s32 count_max; + s32 count_ret; + union rkvenc2_slice_len_info slice_info[]; +}; + struct rkvenc_task { struct mpp_task mpp_task; int fmt; @@ -138,7 +231,20 @@ u32 r_req_cnt; struct mpp_request r_reqs[MPP_MAX_MSG_NUM]; struct mpp_dma_buffer *table; - u32 task_no; + + union rkvenc2_dual_core_handshake_id dchs_id; + + /* split output / slice mode info */ + u32 task_split; + u32 task_split_done; + u32 last_slice_found; + u32 slice_wr_cnt; + u32 slice_rd_cnt; + DECLARE_KFIFO(slice_info, union rkvenc2_slice_len_info, RKVENC_MAX_SLICE_FIFO_LEN); + + /* jpege bitstream */ + struct mpp_dma_buffer *bs_buf; + u32 offset_bs; }; #define RKVENC_MAX_RCB_NUM (4) @@ -180,6 +286,9 @@ struct reset_control *rst_a; struct reset_control *rst_h; struct reset_control *rst_core; + /* for ccu */ + struct rkvenc_ccu *ccu; + struct list_head core_link; /* internal rcb-memory */ u32 sram_size; @@ -187,10 +296,98 @@ dma_addr_t sram_iova; u32 sram_enabled; struct page *rcb_page; + + u32 bs_overflow; + +#ifdef CONFIG_PM_DEVFREQ + struct rockchip_opp_info opp_info; + struct monitor_dev_info *mdev_info; + struct opp_table *opp_table; +#endif }; +struct rkvenc_ccu { + u32 core_num; + /* lock for core attach */ + struct mutex lock; + struct list_head core_list; + struct mpp_dev *main_core; + + spinlock_t lock_dchs; + union rkvenc2_dual_core_handshake_id dchs[RKVENC_MAX_CORE_NUM]; +}; static struct rkvenc_hw_info rkvenc_v2_hw_info = { + .hw = { + .reg_num = 254, + .reg_id = 0, + .reg_en = 4, + .reg_start = 160, + .reg_end = 253, + }, + .reg_class = RKVENC_CLASS_BUTT, + .reg_msg[RKVENC_CLASS_BASE] = { + .base_s = 0x0000, + .base_e = 0x0058, + }, + .reg_msg[RKVENC_CLASS_PIC] = { + .base_s = 0x0280, + .base_e = 0x03f4, + }, + .reg_msg[RKVENC_CLASS_RC] = { + .base_s = 0x1000, + .base_e = 0x10e0, + }, + .reg_msg[RKVENC_CLASS_PAR] = { + .base_s = 0x1700, + .base_e = 0x1cd4, + }, + .reg_msg[RKVENC_CLASS_SQI] = { + .base_s = 0x2000, + .base_e = 0x21e4, + }, + .reg_msg[RKVENC_CLASS_SCL] = { + .base_s = 0x2200, + .base_e = 0x2c98, + }, + .reg_msg[RKVENC_CLASS_OSD] = { + .base_s = 0x3000, + .base_e = 0x347c, + }, + .reg_msg[RKVENC_CLASS_ST] = { + .base_s = 0x4000, + .base_e = 0x42cc, + }, + .reg_msg[RKVENC_CLASS_DEBUG] = { + .base_s = 0x5000, + .base_e = 0x5354, + }, + .fd_class = RKVENC_CLASS_FD_BUTT, + .fd_reg[RKVENC_CLASS_FD_BASE] = { + .class = RKVENC_CLASS_PIC, + .base_fmt = RKVENC_FMT_BASE, + }, + .fd_reg[RKVENC_CLASS_FD_OSD] = { + .class = RKVENC_CLASS_OSD, + .base_fmt = RKVENC_FMT_OSD_BASE, + }, + .fmt_reg = { + .class = RKVENC_CLASS_PIC, + .base = 0x0300, + .bitpos = 0, + .bitlen = 1, + }, + .enc_start_base = 0x0010, + .enc_clr_base = 0x0014, + .int_en_base = 0x0020, + .int_mask_base = 0x0024, + .int_clr_base = 0x0028, + .int_sta_base = 0x002c, + .enc_wdg_base = 0x0038, + .err_mask = 0x03f0, +}; + +static struct rkvenc_hw_info rkvenc_540c_hw_info = { .hw = { .reg_num = 254, .reg_id = 0, @@ -259,28 +456,50 @@ .enc_wdg_base = 0x0038, .err_mask = 0x27d0, }; - /* * file handle translate information for v2 */ static const u16 trans_tbl_h264e_v2[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, +}; + +static const u16 trans_tbl_h264e_v2_osd[] = { + 20, 21, 22, 23, 24, 25, 26, 27, +}; + +static const u16 trans_tbl_h265e_v2[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, +}; + +static const u16 trans_tbl_h265e_v2_osd[] = { + 20, 21, 22, 23, 24, 25, 26, 27, +}; + +/* + * file handle translate information for 540c + */ +static const u16 trans_tbl_h264e_540c[] = { 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, // /* renc and ref wrap */ // 24, 25, 26, 27, }; -static const u16 trans_tbl_h264e_v2_osd[] = { +static const u16 trans_tbl_h264e_540c_osd[] = { 3, 4, 12, 13, 21, 22, 30, 31, 39, 40, 48, 49, 57, 58, 66, 67, }; -static const u16 trans_tbl_h265e_v2[] = { +static const u16 trans_tbl_h265e_540c[] = { 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23 }; -static const u16 trans_tbl_h265e_v2_osd[] = { +static const u16 trans_tbl_h265e_540c_osd[] = { 3, 4, 12, 13, 21, 22, 30, 31, 39, 40, 48, 49, 57, 58, 66, 67, }; @@ -311,6 +530,25 @@ [RKVENC_FMT_H265E_OSD] = { .count = ARRAY_SIZE(trans_tbl_h265e_v2_osd), .table = trans_tbl_h265e_v2_osd, + }, +}; + +static struct mpp_trans_info trans_rkvenc_540c[] = { + [RKVENC_FMT_H264E] = { + .count = ARRAY_SIZE(trans_tbl_h264e_540c), + .table = trans_tbl_h264e_540c, + }, + [RKVENC_FMT_H264E_OSD] = { + .count = ARRAY_SIZE(trans_tbl_h264e_540c_osd), + .table = trans_tbl_h264e_540c_osd, + }, + [RKVENC_FMT_H265E] = { + .count = ARRAY_SIZE(trans_tbl_h265e_540c), + .table = trans_tbl_h265e_540c, + }, + [RKVENC_FMT_H265E_OSD] = { + .count = ARRAY_SIZE(trans_tbl_h265e_540c_osd), + .table = trans_tbl_h265e_540c_osd, }, [RKVENC_FMT_JPEGE] = { .count = ARRAY_SIZE(trans_tbl_jpege), @@ -345,6 +583,7 @@ for (i = 0; i < reg_class; i++) { kfree(task->reg[i].data); + task->reg[i].data = NULL; task->reg[i].size = 0; } @@ -570,7 +809,6 @@ return 0; } - static int rkvenc2_set_rcbbuf(struct mpp_dev *mpp, struct mpp_session *session, struct rkvenc_task *task) { @@ -616,6 +854,56 @@ return 0; } +static void rkvenc2_setup_task_id(u32 session_id, struct rkvenc_task *task) +{ + u32 val = task->reg[RKVENC_CLASS_PIC].data[DCHS_CLASS_OFFSET]; + + /* always enable tx */ + val |= DCHS_TXE; + + task->reg[RKVENC_CLASS_PIC].data[DCHS_CLASS_OFFSET] = val; + task->dchs_id.val = (((u64)session_id << 32) | val); + + task->dchs_id.txid_orig = task->dchs_id.txid; + task->dchs_id.rxid_orig = task->dchs_id.rxid; + task->dchs_id.txid_map = task->dchs_id.txid; + task->dchs_id.rxid_map = task->dchs_id.rxid; + + task->dchs_id.txe_orig = task->dchs_id.txe; + task->dchs_id.rxe_orig = task->dchs_id.rxe; + task->dchs_id.txe_map = task->dchs_id.txe; + task->dchs_id.rxe_map = task->dchs_id.rxe; +} + +static void rkvenc2_check_split_task(struct rkvenc_task *task) +{ + u32 slen_fifo_en = 0; + u32 sli_split_en = 0; + + if (task->reg[RKVENC_CLASS_PIC].valid) { + u32 *reg = task->reg[RKVENC_CLASS_PIC].data; + u32 enc_stnd = reg[RKVENC2_REG_ENC_PIC] & RKVENC2_BIT_ENC_STND; + + slen_fifo_en = (reg[RKVENC2_REG_ENC_PIC] & RKVENC2_BIT_SLEN_FIFO) ? 1 : 0; + sli_split_en = (reg[RKVENC2_REG_SLI_SPLIT] & RKVENC2_BIT_SLI_SPLIT) ? 1 : 0; + + /* + * FIXUP: rkvenc2 hardware bug: + * H.264 encoding has bug when external line buffer and slice flush both + * are enabled. + */ + if (sli_split_en && slen_fifo_en && + enc_stnd == RKVENC2_BIT_VAL_H264 && + reg[RKVENC2_REG_EXT_LINE_BUF_BASE]) + reg[RKVENC2_REG_SLI_SPLIT] &= ~RKVENC2_BIT_SLI_FLUSH; + } + + task->task_split = sli_split_en && slen_fifo_en; + + if (task->task_split) + INIT_KFIFO(task->slice_info); +} + static void *rkvenc_alloc_task(struct mpp_session *session, struct mpp_task_msgs *msgs) { @@ -650,6 +938,7 @@ u32 off; const u16 *tbl; struct rkvenc_hw_info *hw = task->hw_info; + int fd_bs = -1; for (i = 0; i < hw->fd_class; i++) { u32 class = hw->fd_reg[i].class; @@ -659,6 +948,15 @@ if (!reg) continue; + + if (fmt == RKVENC_FMT_JPEGE && class == RKVENC_CLASS_PIC && fd_bs == -1) { + int bs_index; + + bs_index = mpp->var->trans_info[fmt].table[2]; + fd_bs = reg[bs_index]; + task->offset_bs = mpp_query_reg_offset_info(&task->off_inf, + bs_index + ss); + } ret = mpp_translate_reg_address(session, mpp_task, fmt, reg, NULL); if (ret) @@ -672,9 +970,19 @@ reg[tbl[j]] += off; } } + + if (fd_bs >= 0) { + struct mpp_dma_buffer *bs_buf = + mpp_dma_find_buffer_fd(session->dma, fd_bs); + + if (bs_buf && task->offset_bs > 0) + mpp_dma_buf_sync(bs_buf, 0, task->offset_bs, DMA_TO_DEVICE, false); + task->bs_buf = bs_buf; + } } - rkvenc2_set_rcbbuf(mpp, session, task); + rkvenc2_setup_task_id(session->index, task); task->clk_mode = CLK_MODE_NORMAL; + rkvenc2_check_split_task(task); mpp_debug_leave(); @@ -692,6 +1000,201 @@ return NULL; } +static void *rkvenc2_prepare(struct mpp_dev *mpp, struct mpp_task *mpp_task) +{ + struct mpp_taskqueue *queue = mpp->queue; + unsigned long core_idle; + unsigned long flags; + u32 core_id_max; + s32 core_id; + u32 i; + + spin_lock_irqsave(&queue->running_lock, flags); + + core_idle = queue->core_idle; + core_id_max = queue->core_id_max; + + for (i = 0; i <= core_id_max; i++) { + struct mpp_dev *mpp = queue->cores[i]; + + if (mpp && mpp->disable) + clear_bit(i, &core_idle); + } + + core_id = find_first_bit(&core_idle, core_id_max + 1); + + if (core_id >= core_id_max + 1 || !queue->cores[core_id]) { + mpp_task = NULL; + mpp_dbg_core("core %d all busy %lx\n", core_id, core_idle); + } else { + struct rkvenc_task *task = to_rkvenc_task(mpp_task); + + clear_bit(core_id, &queue->core_idle); + mpp_task->mpp = queue->cores[core_id]; + mpp_task->core_id = core_id; + rkvenc2_set_rcbbuf(mpp_task->mpp, mpp_task->session, task); + mpp_dbg_core("core %d set idle %lx -> %lx\n", core_id, + core_idle, queue->core_idle); + } + + spin_unlock_irqrestore(&queue->running_lock, flags); + + return mpp_task; +} + +static void rkvenc2_patch_dchs(struct rkvenc_dev *enc, struct rkvenc_task *task) +{ + struct rkvenc_ccu *ccu; + union rkvenc2_dual_core_handshake_id *dchs; + union rkvenc2_dual_core_handshake_id *task_dchs = &task->dchs_id; + int core_num; + int core_id = enc->mpp.core_id; + unsigned long flags; + int i; + + if (!enc->ccu) + return; + + if (core_id >= RKVENC_MAX_CORE_NUM) { + dev_err(enc->mpp.dev, "invalid core id %d max %d\n", + core_id, RKVENC_MAX_CORE_NUM); + return; + } + + ccu = enc->ccu; + dchs = ccu->dchs; + core_num = ccu->core_num; + + spin_lock_irqsave(&ccu->lock_dchs, flags); + + if (dchs[core_id].working) { + spin_unlock_irqrestore(&ccu->lock_dchs, flags); + + mpp_err("can not config when core %d is still working\n", core_id); + return; + } + + if (mpp_debug_unlikely(DEBUG_CORE)) + pr_info("core tx:rx 0 %s %d:%d %d:%d -- 1 %s %d:%d %d:%d -- task %d %d:%d %d:%d\n", + dchs[0].working ? "work" : "idle", + dchs[0].txid, dchs[0].txe, dchs[0].rxid, dchs[0].rxe, + dchs[1].working ? "work" : "idle", + dchs[1].txid, dchs[1].txe, dchs[1].rxid, dchs[1].rxe, + core_id, task_dchs->txid, task_dchs->txe, + task_dchs->rxid, task_dchs->rxe); + + /* always use new id as */ + { + struct mpp_task *mpp_task = &task->mpp_task; + unsigned long id_valid = (unsigned long)-1; + int txid_map = -1; + int rxid_map = -1; + + /* scan all used id */ + for (i = 0; i < core_num; i++) { + if (!dchs[i].working) + continue; + + clear_bit(dchs[i].txid_map, &id_valid); + clear_bit(dchs[i].rxid_map, &id_valid); + } + + if (task_dchs->rxe) { + for (i = 0; i < core_num; i++) { + if (i == core_id) + continue; + + if (!dchs[i].working) + continue; + + if (task_dchs->session_id != dchs[i].session_id) + continue; + + if (task_dchs->rxid_orig != dchs[i].txid_orig) + continue; + + rxid_map = dchs[i].txid_map; + break; + } + } + + txid_map = find_first_bit(&id_valid, RKVENC_MAX_DCHS_ID); + if (txid_map == RKVENC_MAX_DCHS_ID) { + spin_unlock_irqrestore(&ccu->lock_dchs, flags); + + mpp_err("task %d:%d on core %d failed to find a txid\n", + mpp_task->session->pid, mpp_task->task_id, + mpp_task->core_id); + return; + } + + clear_bit(txid_map, &id_valid); + task_dchs->txid_map = txid_map; + + if (rxid_map < 0) { + rxid_map = find_first_bit(&id_valid, RKVENC_MAX_DCHS_ID); + if (rxid_map == RKVENC_MAX_DCHS_ID) { + spin_unlock_irqrestore(&ccu->lock_dchs, flags); + + mpp_err("task %d:%d on core %d failed to find a rxid\n", + mpp_task->session->pid, mpp_task->task_id, + mpp_task->core_id); + return; + } + + task_dchs->rxe_map = 0; + } + + task_dchs->rxid_map = rxid_map; + } + + task_dchs->txid = task_dchs->txid_map; + task_dchs->rxid = task_dchs->rxid_map; + task_dchs->rxe = task_dchs->rxe_map; + + dchs[core_id].val = task_dchs->val; + task->reg[RKVENC_CLASS_PIC].data[DCHS_CLASS_OFFSET] = task_dchs->val; + + dchs[core_id].working = 1; + + spin_unlock_irqrestore(&ccu->lock_dchs, flags); +} + +static void rkvenc2_update_dchs(struct rkvenc_dev *enc, struct rkvenc_task *task) +{ + struct rkvenc_ccu *ccu = enc->ccu; + int core_id = enc->mpp.core_id; + unsigned long flags; + + if (!ccu) + return; + + if (core_id >= RKVENC_MAX_CORE_NUM) { + dev_err(enc->mpp.dev, "invalid core id %d max %d\n", + core_id, RKVENC_MAX_CORE_NUM); + return; + } + + spin_lock_irqsave(&ccu->lock_dchs, flags); + ccu->dchs[core_id].val = 0; + + if (mpp_debug_unlikely(DEBUG_CORE)) { + union rkvenc2_dual_core_handshake_id *dchs = ccu->dchs; + union rkvenc2_dual_core_handshake_id *task_dchs = &task->dchs_id; + + pr_info("core %d task done\n", core_id); + pr_info("core tx:rx 0 %s %d:%d %d:%d -- 1 %s %d:%d %d:%d -- task %d %d:%d %d:%d\n", + dchs[0].working ? "work" : "idle", + dchs[0].txid, dchs[0].txe, dchs[0].rxid, dchs[0].rxe, + dchs[1].working ? "work" : "idle", + dchs[1].txid, dchs[1].txe, dchs[1].rxid, dchs[1].rxe, + core_id, task_dchs->txid, task_dchs->txe, + task_dchs->rxid, task_dchs->rxe); + } + + spin_unlock_irqrestore(&ccu->lock_dchs, flags); +} + static int rkvenc_run(struct mpp_dev *mpp, struct mpp_task *mpp_task) { u32 i, j; @@ -699,6 +1202,8 @@ struct rkvenc_dev *enc = to_rkvenc_dev(mpp); struct rkvenc_task *task = to_rkvenc_task(mpp_task); struct rkvenc_hw_info *hw = enc->hw_info; + u32 timing_en = mpp->srv->timing_en; + u32 timeout_thd; mpp_debug_enter(); @@ -709,6 +1214,8 @@ /* clear hardware counter */ mpp_write_relaxed(mpp, 0x5300, 0x2); + + rkvenc2_patch_dchs(enc, task); for (i = 0; i < task->w_req_cnt; i++) { int ret; @@ -735,35 +1242,170 @@ } } + if (mpp_debug_unlikely(DEBUG_CORE)) + dev_info(mpp->dev, "core %d dchs %08x\n", mpp->core_id, + mpp_read_relaxed(&enc->mpp, DCHS_REG_OFFSET)); + + /* flush tlb before starting hardware */ + mpp_iommu_flush_tlb(mpp->iommu_info); + /* init current task */ mpp->cur_task = mpp_task; + + /* + * reconfig timeout threshold. + * bit0-bit23,x1024 core clk cycles + */ + timeout_thd = mpp_read(mpp, RKVENC_WDG) & 0xff000000; + timeout_thd |= TIMEOUT_MS * clk_get_rate(enc->core_clk_info.clk) / 1024000; + mpp_write(mpp, RKVENC_WDG, timeout_thd); + + mpp_task_run_begin(mpp_task, timing_en, MPP_WORK_TIMEOUT_DELAY); + /* Flush the register before the start the device */ wmb(); mpp_write(mpp, enc->hw_info->enc_start_base, start_val); + + mpp_task_run_end(mpp_task, timing_en); mpp_debug_leave(); return 0; } +static void rkvenc2_read_slice_len(struct mpp_dev *mpp, struct rkvenc_task *task, + u32 last) +{ + u32 sli_num = mpp_read_relaxed(mpp, RKVENC2_REG_SLICE_NUM_BASE); + union rkvenc2_slice_len_info slice_info; + u32 task_id = task->mpp_task.task_id; + u32 i; + + mpp_dbg_slice("task %d wr %3d len start %s\n", task_id, + sli_num, last ? "last" : ""); + + for (i = 0; i < sli_num; i++) { + slice_info.val = mpp_read_relaxed(mpp, RKVENC2_REG_SLICE_LEN_BASE); + + if (last && i == sli_num - 1) { + task->last_slice_found = 1; + slice_info.last = 1; + } + + mpp_dbg_slice("task %d wr %3d len %d %s\n", task_id, + task->slice_wr_cnt, slice_info.slice_len, + slice_info.last ? "last" : ""); + + kfifo_in(&task->slice_info, &slice_info, 1); + task->slice_wr_cnt++; + } + + /* Fixup for async between last flag and slice number register */ + if (last && !task->last_slice_found) { + mpp_dbg_slice("task %d mark last slice\n", task_id); + slice_info.last = 1; + slice_info.slice_len = 0; + kfifo_in(&task->slice_info, &slice_info, 1); + } +} + static int rkvenc_irq(struct mpp_dev *mpp) { struct rkvenc_dev *enc = to_rkvenc_dev(mpp); struct rkvenc_hw_info *hw = enc->hw_info; + struct mpp_task *mpp_task = NULL; + struct rkvenc_task *task = NULL; + u32 irq_status; + int ret = IRQ_NONE; mpp_debug_enter(); - mpp->irq_status = mpp_read(mpp, hw->int_sta_base); - if (!mpp->irq_status) - return IRQ_NONE; - mpp_write(mpp, hw->int_mask_base, 0x100); - mpp_write(mpp, hw->int_clr_base, 0xffffffff); - udelay(5); - mpp_write(mpp, hw->int_sta_base, 0); + irq_status = mpp_read(mpp, hw->int_sta_base); + + mpp_debug(DEBUG_IRQ_STATUS, "%s irq_status: %08x\n", + dev_name(mpp->dev), irq_status); + + if (!irq_status) + return ret; + + /* clear int first */ + mpp_write(mpp, hw->int_clr_base, irq_status); + + /* + * prevent watch dog irq storm. + * The encoder did not stop working when watchdog interrupt is triggered, + * it still check timeout and trigger watch dog irq. + */ + if (irq_status & INT_STA_WDG_STA) + mpp_write(mpp, hw->int_mask_base, INT_STA_WDG_STA); + + if (mpp->cur_task) { + mpp_task = mpp->cur_task; + task = to_rkvenc_task(mpp_task); + } + + /* 1. read slice number and slice length */ + if (task && task->task_split && + (irq_status & (INT_STA_SLC_DONE_STA | INT_STA_ENC_DONE_STA))) { + mpp_time_part_diff(mpp_task); + rkvenc2_read_slice_len(mpp, task, irq_status & INT_STA_ENC_DONE_STA); + wake_up(&mpp_task->wait); + } + + /* 2. process slice irq */ + if (irq_status & INT_STA_SLC_DONE_STA) + ret = IRQ_HANDLED; + + /* 3. process bitstream overflow */ + if (irq_status & INT_STA_BSF_OFLW_STA) { + u32 bs_rd = mpp_read(mpp, RKVENC2_REG_ADR_BSBR); + u32 bs_wr = mpp_read(mpp, RKVENC2_REG_ST_BSB); + u32 bs_top = mpp_read(mpp, RKVENC2_REG_ADR_BSBT); + u32 bs_bot = mpp_read(mpp, RKVENC2_REG_ADR_BSBB); + + if (mpp_task) + dev_err(mpp->dev, "task %d found bitstream overflow [%#08x %#08x %#08x %#08x]\n", + mpp_task->task_index, bs_top, bs_bot, bs_wr, bs_rd); + bs_wr += 128; + if (bs_wr >= bs_top) + bs_wr = bs_bot; + + /* update write addr for enc continue */ + mpp_write(mpp, RKVENC2_REG_ADR_BSBS, bs_wr); + enc->bs_overflow = 1; + + ret = IRQ_HANDLED; + } + + /* 4. process frame irq */ + if (irq_status & INT_STA_ENC_DONE_STA) { + mpp->irq_status = irq_status; + + if (enc->bs_overflow) { + mpp->irq_status |= INT_STA_BSF_OFLW_STA; + enc->bs_overflow = 0; + } + + ret = IRQ_WAKE_THREAD; + } + + /* 5. process error irq */ + if (irq_status & INT_STA_ERROR) { + mpp->irq_status = irq_status; + + dev_err(mpp->dev, "found error status %08x\n", irq_status); + + ret = IRQ_WAKE_THREAD; + } mpp_debug_leave(); - return IRQ_WAKE_THREAD; + return ret; +} + +static int vepu540c_irq(struct mpp_dev *mpp) +{ + return rkvenc_irq(mpp); } static int rkvenc_isr(struct mpp_dev *mpp) @@ -771,6 +1413,8 @@ struct rkvenc_task *task; struct mpp_task *mpp_task; struct rkvenc_dev *enc = to_rkvenc_dev(mpp); + struct mpp_taskqueue *queue = mpp->queue; + unsigned long core_idle; mpp_debug_enter(); @@ -783,17 +1427,30 @@ mpp_task = mpp->cur_task; mpp_time_diff(mpp_task); mpp->cur_task = NULL; + + if (mpp_task->mpp && mpp_task->mpp != mpp) + dev_err(mpp->dev, "mismatch core dev %p:%p\n", mpp_task->mpp, mpp); + task = to_rkvenc_task(mpp_task); task->irq_status = mpp->irq_status; - mpp_debug(DEBUG_IRQ_STATUS, "irq_status: %08x\n", task->irq_status); + + rkvenc2_update_dchs(enc, task); if (task->irq_status & enc->hw_info->err_mask) { atomic_inc(&mpp->reset_request); + /* dump register */ if (mpp_debug_unlikely(DEBUG_DUMP_ERR_REG)) - mpp_task_dump_hw_reg(mpp, mpp_task); + mpp_task_dump_hw_reg(mpp); } + mpp_task_finish(mpp_task->session, mpp_task); + + core_idle = queue->core_idle; + set_bit(mpp->core_id, &queue->core_idle); + + mpp_dbg_core("core %d isr idle %lx -> %lx\n", mpp->core_id, core_idle, + queue->core_idle); mpp_debug_leave(); @@ -824,6 +1481,14 @@ reg[j] = mpp_read_relaxed(mpp, msg.offset + j * sizeof(u32)); } + + if (task->bs_buf) { + u32 bs_size = mpp_read(mpp, 0x4064); + + mpp_dma_buf_sync(task->bs_buf, 0, bs_size + task->offset_bs, + DMA_FROM_DEVICE, true); + } + /* revert hack for irq status */ reg = rkvenc_get_class_reg(task, task->hw_info->int_sta_base); if (reg) @@ -975,7 +1640,7 @@ } seq_puts(seq, "\n"); /* item data*/ - seq_printf(seq, "|%8p|", session); + seq_printf(seq, "|%8d|", session->index); seq_printf(seq, "%8s|", mpp_device_name[session->device_type]); for (i = ENC_INFO_BASE; i < ENC_INFO_BUTT; i++) { u32 flag = priv->codec_info[i].flag; @@ -1008,7 +1673,7 @@ mutex_lock(&mpp->srv->session_lock); list_for_each_entry_safe(session, n, &mpp->srv->session_list, - session_link) { + service_link) { if (session->device_type != MPP_DEVICE_RKVENC) continue; if (!session->priv) @@ -1024,13 +1689,25 @@ static int rkvenc_procfs_init(struct mpp_dev *mpp) { struct rkvenc_dev *enc = to_rkvenc_dev(mpp); + char name[32]; - enc->procfs = proc_mkdir(mpp->dev->of_node->name, mpp->srv->procfs); + if (!mpp->dev || !mpp->dev->of_node || !mpp->dev->of_node->name || + !mpp->srv || !mpp->srv->procfs) + return -EINVAL; + + snprintf(name, sizeof(name) - 1, "%s%d", + mpp->dev->of_node->name, mpp->core_id); + + enc->procfs = proc_mkdir(name, mpp->srv->procfs); if (IS_ERR_OR_NULL(enc->procfs)) { mpp_err("failed on open procfs\n"); enc->procfs = NULL; return -EIO; } + + /* for common mpp_dev options */ + mpp_procfs_create_common(enc->procfs, mpp); + /* for debug */ mpp_procfs_create_u32("aclk", 0644, enc->procfs, &enc->aclk_info.debug_rate_hz); @@ -1045,6 +1722,16 @@ return 0; } +static int rkvenc_procfs_ccu_init(struct mpp_dev *mpp) +{ + struct rkvenc_dev *enc = to_rkvenc_dev(mpp); + + if (!enc->procfs) + goto done; + +done: + return 0; +} #else static inline int rkvenc_procfs_remove(struct mpp_dev *mpp) { @@ -1056,6 +1743,124 @@ return 0; } +static inline int rkvenc_procfs_ccu_init(struct mpp_dev *mpp) +{ + return 0; +} +#endif + +#ifdef CONFIG_PM_DEVFREQ +static int rk3588_venc_set_read_margin(struct device *dev, + struct rockchip_opp_info *opp_info, + u32 rm) +{ + if (!opp_info->grf || !opp_info->volt_rm_tbl) + return 0; + + if (rm == opp_info->current_rm || rm == UINT_MAX) + return 0; + + dev_dbg(dev, "set rm to %d\n", rm); + + regmap_write(opp_info->grf, 0x214, 0x001c0000 | (rm << 2)); + regmap_write(opp_info->grf, 0x218, 0x001c0000 | (rm << 2)); + regmap_write(opp_info->grf, 0x220, 0x003c0000 | (rm << 2)); + regmap_write(opp_info->grf, 0x224, 0x003c0000 | (rm << 2)); + + opp_info->current_rm = rm; + + return 0; +} + +static const struct rockchip_opp_data rk3588_venc_opp_data = { + .set_read_margin = rk3588_venc_set_read_margin, +}; + +static const struct of_device_id rockchip_rkvenc_of_match[] = { + { + .compatible = "rockchip,rk3588", + .data = (void *)&rk3588_venc_opp_data, + }, + {}, +}; + +static struct monitor_dev_profile venc_mdevp = { + .type = MONITOR_TYPE_DEV, + .update_volt = rockchip_monitor_check_rate_volt, +}; + +static int rkvenc_devfreq_init(struct mpp_dev *mpp) +{ + struct rkvenc_dev *enc = to_rkvenc_dev(mpp); + struct clk *clk_core = enc->core_clk_info.clk; + struct device *dev = mpp->dev; + struct opp_table *reg_table = NULL; + struct opp_table *clk_table = NULL; + const char *const reg_names[] = { "venc", "mem" }; + int ret = 0; + + if (!clk_core) + return 0; + + if (of_find_property(dev->of_node, "venc-supply", NULL) && + of_find_property(dev->of_node, "mem-supply", NULL)) { + reg_table = dev_pm_opp_set_regulators(dev, reg_names, 2); + if (IS_ERR(reg_table)) + return PTR_ERR(reg_table); + } else { + reg_table = dev_pm_opp_set_regulators(dev, reg_names, 1); + if (IS_ERR(reg_table)) + return PTR_ERR(reg_table); + } + enc->opp_table = reg_table; + + clk_table = dev_pm_opp_set_clkname(dev, "clk_core"); + if (IS_ERR(clk_table)) { + ret = PTR_ERR(clk_table); + goto put_opp_reg; + } + + rockchip_get_opp_data(rockchip_rkvenc_of_match, &enc->opp_info); + ret = rockchip_init_opp_table(dev, &enc->opp_info, "leakage", "venc"); + if (ret) { + dev_err(dev, "failed to init_opp_table\n"); + goto put_opp_clk; + } + + enc->mdev_info = rockchip_system_monitor_register(dev, &venc_mdevp); + if (IS_ERR(enc->mdev_info)) { + dev_dbg(dev, "without system monitor\n"); + enc->mdev_info = NULL; + } + + return 0; + +put_opp_clk: + dev_pm_opp_put_clkname(enc->opp_table); +put_opp_reg: + dev_pm_opp_put_regulators(enc->opp_table); + enc->opp_table = NULL; + + return ret; +} + +static int rkvenc_devfreq_remove(struct mpp_dev *mpp) +{ + struct rkvenc_dev *enc = to_rkvenc_dev(mpp); + + if (enc->mdev_info) { + rockchip_system_monitor_unregister(enc->mdev_info); + enc->mdev_info = NULL; + } + if (enc->opp_table) { + rockchip_uninit_opp_table(mpp->dev, &enc->opp_info); + dev_pm_opp_put_clkname(enc->opp_table); + dev_pm_opp_put_regulators(enc->opp_table); + enc->opp_table = NULL; + } + + return 0; +} #endif static int rkvenc_init(struct mpp_dev *mpp) @@ -1094,26 +1899,60 @@ if (!enc->rst_core) mpp_err("No core reset resource define\n"); +#ifdef CONFIG_PM_DEVFREQ + ret = rkvenc_devfreq_init(mpp); + if (ret) + mpp_err("failed to add venc devfreq\n"); +#endif + return 0; +} + +static int rkvenc_exit(struct mpp_dev *mpp) +{ +#ifdef CONFIG_PM_DEVFREQ + rkvenc_devfreq_remove(mpp); +#endif + + return 0; +} + +static int rkvenc_soft_reset(struct mpp_dev *mpp) +{ + struct rkvenc_dev *enc = to_rkvenc_dev(mpp); + struct rkvenc_hw_info *hw = enc->hw_info; + u32 rst_status = 0; + int ret = 0; + + /* safe reset */ + mpp_write(mpp, hw->int_mask_base, 0x3FF); + mpp_write(mpp, hw->enc_clr_base, 0x3); + ret = readl_relaxed_poll_timeout(mpp->reg_base + hw->int_sta_base, + rst_status, + rst_status & RKVENC_SCLR_DONE_STA, + 0, 5); + mpp_write(mpp, hw->int_clr_base, 0xffffffff); + mpp_write(mpp, hw->int_sta_base, 0); + + return ret; + } static int rkvenc_reset(struct mpp_dev *mpp) { struct rkvenc_dev *enc = to_rkvenc_dev(mpp); - struct rkvenc_hw_info *hw = enc->hw_info; + int ret = 0; + struct mpp_taskqueue *queue = mpp->queue; mpp_debug_enter(); - /* safe reset */ - mpp_write(mpp, hw->int_mask_base, 0x3FF); - mpp_write(mpp, hw->enc_clr_base, 0x1); - udelay(5); - mpp_write(mpp, hw->int_clr_base, 0xffffffff); - mpp_write(mpp, hw->int_sta_base, 0); + /* safe reset first*/ + ret = rkvenc_soft_reset(mpp); /* cru reset */ - if (enc->rst_a && enc->rst_h && enc->rst_core) { - rockchip_pmu_idle_request(mpp->dev, true); + if (ret && enc->rst_a && enc->rst_h && enc->rst_core) { + mpp_err("soft reset timeout, use cru reset\n"); + mpp_pmu_idle_request(mpp, true); mpp_safe_reset(enc->rst_a); mpp_safe_reset(enc->rst_h); mpp_safe_reset(enc->rst_core); @@ -1121,8 +1960,14 @@ mpp_safe_unreset(enc->rst_a); mpp_safe_unreset(enc->rst_h); mpp_safe_unreset(enc->rst_core); - rockchip_pmu_idle_request(mpp->dev, false); + mpp_pmu_idle_request(mpp, false); } + + set_bit(mpp->core_id, &queue->core_idle); + if (enc->ccu) + enc->ccu->dchs[mpp->core_id].val = 0; + + mpp_dbg_core("core %d reset idle %lx\n", mpp->core_id, queue->core_idle); mpp_debug_leave(); @@ -1162,8 +2007,177 @@ return 0; } +#define RKVENC2_WORK_TIMEOUT_DELAY (200) +#define RKVENC2_WAIT_TIMEOUT_DELAY (2000) + +static void rkvenc2_task_pop_pending(struct mpp_task *task) +{ + struct mpp_session *session = task->session; + + mutex_lock(&session->pending_lock); + list_del_init(&task->pending_link); + mutex_unlock(&session->pending_lock); + + kref_put(&task->ref, mpp_free_task); +} + +static int rkvenc2_task_default_process(struct mpp_dev *mpp, + struct mpp_task *task) +{ + int ret = 0; + + if (mpp->dev_ops && mpp->dev_ops->result) + ret = mpp->dev_ops->result(mpp, task, NULL); + + mpp_debug_func(DEBUG_TASK_INFO, "kref_read %d, ret %d\n", + kref_read(&task->ref), ret); + + rkvenc2_task_pop_pending(task); + + return ret; +} + +#define RKVENC2_TIMEOUT_DUMP_REG_START (0x5100) +#define RKVENC2_TIMEOUT_DUMP_REG_END (0x5160) + +static void rkvenc2_task_timeout_process(struct mpp_session *session, + struct mpp_task *task) +{ + atomic_inc(&task->abort_request); + set_bit(TASK_STATE_ABORT, &task->state); + + mpp_err("session %d:%d count %d task %d ref %d timeout\n", + session->pid, session->index, atomic_read(&session->task_count), + task->task_id, kref_read(&task->ref)); + + if (task->mpp) { + struct mpp_dev *mpp = task->mpp; + u32 start = RKVENC2_TIMEOUT_DUMP_REG_START; + u32 end = RKVENC2_TIMEOUT_DUMP_REG_END; + u32 offset; + + dev_err(mpp->dev, "core %d dump timeout status:\n", mpp->core_id); + + for (offset = start; offset < end; offset += sizeof(u32)) + mpp_reg_show(mpp, offset); + } + + rkvenc2_task_pop_pending(task); +} + +static int rkvenc2_wait_result(struct mpp_session *session, + struct mpp_task_msgs *msgs) +{ + struct rkvenc_poll_slice_cfg cfg; + struct rkvenc_task *enc_task; + struct mpp_request *req; + struct mpp_task *task; + struct mpp_dev *mpp; + union rkvenc2_slice_len_info slice_info; + u32 task_id; + int ret = 0; + + mutex_lock(&session->pending_lock); + task = list_first_entry_or_null(&session->pending_list, + struct mpp_task, + pending_link); + mutex_unlock(&session->pending_lock); + if (!task) { + mpp_err("session %p pending list is empty!\n", session); + return -EIO; + } + + mpp = mpp_get_task_used_device(task, session); + enc_task = to_rkvenc_task(task); + task_id = task->task_id; + + req = cmpxchg(&msgs->poll_req, msgs->poll_req, NULL); + + if (!enc_task->task_split || enc_task->task_split_done) { +task_done_ret: + ret = wait_event_interruptible(task->wait, test_bit(TASK_STATE_DONE, &task->state)); + if (ret == -ERESTARTSYS) + mpp_err("wait task break by signal in normal mode\n"); + + return rkvenc2_task_default_process(mpp, task); + + } + + /* not slice return just wait all slice length */ + if (!req) { + do { + ret = wait_event_interruptible(task->wait, kfifo_out(&enc_task->slice_info, + &slice_info, 1)); + if (ret == -ERESTARTSYS) { + mpp_err("wait task break by signal in slice all mode\n"); + return 0; + } + mpp_dbg_slice("task %d rd %3d len %d %s\n", + task_id, enc_task->slice_rd_cnt, slice_info.slice_len, + slice_info.last ? "last" : ""); + + enc_task->slice_rd_cnt++; + + if (slice_info.last) + goto task_done_ret; + } while (1); + } + + if (copy_from_user(&cfg, req->data, sizeof(cfg))) { + mpp_err("copy_from_user failed\n"); + return -EINVAL; + } + + mpp_dbg_slice("task %d poll irq %d:%d\n", task->task_id, + cfg.count_max, cfg.count_ret); + cfg.count_ret = 0; + + /* handle slice mode poll return */ + do { + ret = wait_event_interruptible(task->wait, kfifo_out(&enc_task->slice_info, + &slice_info, 1)); + if (ret == -ERESTARTSYS) { + mpp_err("wait task break by signal in slice one mode\n"); + return 0; + } + mpp_dbg_slice("core %d task %d rd %3d len %d %s\n", task_id, + mpp->core_id, enc_task->slice_rd_cnt, slice_info.slice_len, + slice_info.last ? "last" : ""); + enc_task->slice_rd_cnt++; + if (cfg.count_ret < cfg.count_max) { + struct rkvenc_poll_slice_cfg __user *ucfg = + (struct rkvenc_poll_slice_cfg __user *)(req->data); + u32 __user *dst = (u32 __user *)(ucfg + 1); + + /* Do NOT return here when put_user error. Just continue */ + if (put_user(slice_info.val, dst + cfg.count_ret)) + ret = -EFAULT; + + cfg.count_ret++; + if (put_user(cfg.count_ret, &ucfg->count_ret)) + ret = -EFAULT; + } + + if (slice_info.last) { + enc_task->task_split_done = 1; + goto task_done_ret; + } + + if (cfg.count_ret >= cfg.count_max) + return 0; + + if (ret < 0) + return ret; + } while (!ret); + + rkvenc2_task_timeout_process(session, task); + + return ret; +} + static struct mpp_hw_ops rkvenc_hw_ops = { .init = rkvenc_init, + .exit = rkvenc_exit, .clk_on = rkvenc_clk_on, .clk_off = rkvenc_clk_off, .set_freq = rkvenc_set_freq, @@ -1171,6 +2185,7 @@ }; static struct mpp_dev_ops rkvenc_dev_ops_v2 = { + .wait_result = rkvenc2_wait_result, .alloc_task = rkvenc_alloc_task, .run = rkvenc_run, .irq = rkvenc_irq, @@ -1184,6 +2199,36 @@ .dump_session = rkvenc_dump_session, }; +static struct mpp_dev_ops rkvenc_ccu_dev_ops = { + .wait_result = rkvenc2_wait_result, + .alloc_task = rkvenc_alloc_task, + .prepare = rkvenc2_prepare, + .run = rkvenc_run, + .irq = rkvenc_irq, + .isr = rkvenc_isr, + .finish = rkvenc_finish, + .result = rkvenc_result, + .free_task = rkvenc_free_task, + .ioctl = rkvenc_control, + .init_session = rkvenc_init_session, + .free_session = rkvenc_free_session, + .dump_session = rkvenc_dump_session, +}; + +static struct mpp_dev_ops vepu540c_dev_ops_v2 = { + .wait_result = rkvenc2_wait_result, + .alloc_task = rkvenc_alloc_task, + .run = rkvenc_run, + .irq = vepu540c_irq, + .isr = rkvenc_isr, + .finish = rkvenc_finish, + .result = rkvenc_result, + .free_task = rkvenc_free_task, + .ioctl = rkvenc_control, + .init_session = rkvenc_init_session, + .free_session = rkvenc_free_session, + .dump_session = rkvenc_dump_session, +}; static const struct mpp_dev_var rkvenc_v2_data = { .device_type = MPP_DEVICE_RKVENC, @@ -1193,13 +2238,127 @@ .dev_ops = &rkvenc_dev_ops_v2, }; +static const struct mpp_dev_var rkvenc_540c_data = { + .device_type = MPP_DEVICE_RKVENC, + .hw_info = &rkvenc_540c_hw_info.hw, + .trans_info = trans_rkvenc_540c, + .hw_ops = &rkvenc_hw_ops, + .dev_ops = &vepu540c_dev_ops_v2, +}; + +static const struct mpp_dev_var rkvenc_ccu_data = { + .device_type = MPP_DEVICE_RKVENC, + .hw_info = &rkvenc_v2_hw_info.hw, + .trans_info = trans_rkvenc_v2, + .hw_ops = &rkvenc_hw_ops, + .dev_ops = &rkvenc_ccu_dev_ops, +}; + static const struct of_device_id mpp_rkvenc_dt_match[] = { { .compatible = "rockchip,rkv-encoder-v2", .data = &rkvenc_v2_data, }, +#ifdef CONFIG_CPU_RK3528 + { + .compatible = "rockchip,rkv-encoder-rk3528", + .data = &rkvenc_540c_data, + }, +#endif +#ifdef CONFIG_CPU_RK3562 + { + .compatible = "rockchip,rkv-encoder-rk3562", + .data = &rkvenc_540c_data, + }, +#endif +#ifdef CONFIG_CPU_RK3588 + { + .compatible = "rockchip,rkv-encoder-v2-core", + .data = &rkvenc_ccu_data, + }, + { + .compatible = "rockchip,rkv-encoder-v2-ccu", + }, +#endif {}, }; + +static int rkvenc_ccu_probe(struct platform_device *pdev) +{ + struct rkvenc_ccu *ccu; + struct device *dev = &pdev->dev; + + ccu = devm_kzalloc(dev, sizeof(*ccu), GFP_KERNEL); + if (!ccu) + return -ENOMEM; + + platform_set_drvdata(pdev, ccu); + + mutex_init(&ccu->lock); + INIT_LIST_HEAD(&ccu->core_list); + spin_lock_init(&ccu->lock_dchs); + + return 0; +} + +static int rkvenc_attach_ccu(struct device *dev, struct rkvenc_dev *enc) +{ + struct device_node *np; + struct platform_device *pdev; + struct rkvenc_ccu *ccu; + + mpp_debug_enter(); + + np = of_parse_phandle(dev->of_node, "rockchip,ccu", 0); + if (!np || !of_device_is_available(np)) + return -ENODEV; + + pdev = of_find_device_by_node(np); + of_node_put(np); + if (!pdev) + return -ENODEV; + + ccu = platform_get_drvdata(pdev); + if (!ccu) + return -ENOMEM; + + INIT_LIST_HEAD(&enc->core_link); + mutex_lock(&ccu->lock); + ccu->core_num++; + list_add_tail(&enc->core_link, &ccu->core_list); + mutex_unlock(&ccu->lock); + + /* attach the ccu-domain to current core */ + if (!ccu->main_core) { + /** + * set the first device for the main-core, + * then the domain of the main-core named ccu-domain + */ + ccu->main_core = &enc->mpp; + } else { + struct mpp_iommu_info *ccu_info, *cur_info; + + /* set the ccu-domain for current device */ + ccu_info = ccu->main_core->iommu_info; + cur_info = enc->mpp.iommu_info; + + if (cur_info) { + cur_info->domain = ccu_info->domain; + cur_info->rw_sem = ccu_info->rw_sem; + } + mpp_iommu_attach(cur_info); + + /* increase main core message capacity */ + ccu->main_core->msgs_cap++; + enc->mpp.msgs_cap = 0; + } + enc->ccu = ccu; + + dev_info(dev, "attach ccu as core %d\n", enc->mpp.core_id); + mpp_debug_enter(); + + return 0; +} static int rkvenc2_alloc_rcbbuf(struct platform_device *pdev, struct rkvenc_dev *enc) { @@ -1215,10 +2374,9 @@ /* get rcb iova start and size */ ret = device_property_read_u32_array(dev, "rockchip,rcb-iova", vals, 2); - if (ret) { - dev_err(dev, "could not find property rcb-iova\n"); + if (ret) return ret; - } + iova = PAGE_ALIGN(vals[0]); sram_used = PAGE_ALIGN(vals[1]); if (!sram_used) { @@ -1300,6 +2458,101 @@ return ret; } +static int rkvenc2_iommu_fault_handle(struct iommu_domain *iommu, + struct device *iommu_dev, + unsigned long iova, int status, void *arg) +{ + struct mpp_dev *mpp = (struct mpp_dev *)arg; + struct rkvenc_dev *enc = to_rkvenc_dev(mpp); + struct mpp_task *mpp_task; + struct rkvenc_ccu *ccu = enc->ccu; + + if (ccu) { + struct rkvenc_dev *core = NULL, *n; + + list_for_each_entry_safe(core, n, &ccu->core_list, core_link) { + if (core->mpp.iommu_info && + (&core->mpp.iommu_info->pdev->dev == iommu_dev)) { + mpp = &core->mpp; + break; + } + } + } + mpp_task = mpp->cur_task; + dev_info(mpp->dev, "core %d page fault found dchs %08x\n", + mpp->core_id, mpp_read_relaxed(&enc->mpp, DCHS_REG_OFFSET)); + + if (mpp_task) + mpp_task_dump_mem_region(mpp, mpp_task); + + /* + * Mask iommu irq, in order for iommu not repeatedly trigger pagefault. + * Until the pagefault task finish by hw timeout. + */ + rockchip_iommu_mask_irq(mpp->dev); + + return 0; +} + +static int rkvenc_core_probe(struct platform_device *pdev) +{ + int ret = 0; + struct device *dev = &pdev->dev; + struct rkvenc_dev *enc = NULL; + struct mpp_dev *mpp = NULL; + + enc = devm_kzalloc(dev, sizeof(*enc), GFP_KERNEL); + if (!enc) + return -ENOMEM; + + mpp = &enc->mpp; + platform_set_drvdata(pdev, mpp); + + if (pdev->dev.of_node) { + struct device_node *np = pdev->dev.of_node; + const struct of_device_id *match = NULL; + + match = of_match_node(mpp_rkvenc_dt_match, np); + if (match) + mpp->var = (struct mpp_dev_var *)match->data; + + mpp->core_id = of_alias_get_id(np, "rkvenc"); + } + + ret = mpp_dev_probe(mpp, pdev); + if (ret) + return ret; + + /* attach core to ccu */ + ret = rkvenc_attach_ccu(dev, enc); + if (ret) { + dev_err(dev, "attach ccu failed\n"); + return ret; + } + rkvenc2_alloc_rcbbuf(pdev, enc); + + ret = devm_request_threaded_irq(dev, mpp->irq, + mpp_dev_irq, + mpp_dev_isr_sched, + IRQF_ONESHOT, + dev_name(dev), mpp); + if (ret) { + dev_err(dev, "register interrupter runtime failed\n"); + return -EINVAL; + } + mpp->session_max_buffers = RKVENC_SESSION_MAX_BUFFERS; + enc->hw_info = to_rkvenc_info(mpp->var->hw_info); + mpp->fault_handler = rkvenc2_iommu_fault_handle; + rkvenc_procfs_init(mpp); + rkvenc_procfs_ccu_init(mpp); + + /* if current is main-core, register current device to mpp service */ + if (mpp == enc->ccu->main_core) + mpp_dev_register_srv(mpp, mpp->srv); + + return 0; +} + static int rkvenc_probe_default(struct platform_device *pdev) { int ret = 0; @@ -1313,7 +2566,7 @@ return -ENOMEM; mpp = &enc->mpp; - platform_set_drvdata(pdev, enc); + platform_set_drvdata(pdev, mpp); if (pdev->dev.of_node) { match = of_match_node(mpp_rkvenc_dt_match, pdev->dev.of_node); @@ -1353,10 +2606,16 @@ { int ret = 0; struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; dev_info(dev, "probing start\n"); - ret = rkvenc_probe_default(pdev); + if (strstr(np->name, "ccu")) + ret = rkvenc_ccu_probe(pdev); + else if (strstr(np->name, "core")) + ret = rkvenc_core_probe(pdev); + else + ret = rkvenc_probe_default(pdev); dev_info(dev, "probing finish\n"); @@ -1369,8 +2628,9 @@ if (enc->rcb_page) { size_t page_size = PAGE_ALIGN(enc->sram_used - enc->sram_size); + int order = min(get_order(page_size), MAX_ORDER); - __free_pages(enc->rcb_page, get_order(page_size)); + __free_pages(enc->rcb_page, order); } if (enc->sram_iova) { domain = enc->mpp.iommu_info->domain; @@ -1383,13 +2643,33 @@ static int rkvenc_remove(struct platform_device *pdev) { struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; - struct rkvenc_dev *enc = platform_get_drvdata(pdev); + if (strstr(np->name, "ccu")) { + dev_info(dev, "remove ccu\n"); + } else if (strstr(np->name, "core")) { + struct mpp_dev *mpp = dev_get_drvdata(dev); + struct rkvenc_dev *enc = to_rkvenc_dev(mpp); - dev_info(dev, "remove device\n"); - rkvenc2_free_rcbbuf(pdev, enc); - mpp_dev_remove(&enc->mpp); - rkvenc_procfs_remove(&enc->mpp); + dev_info(dev, "remove core\n"); + if (enc->ccu) { + mutex_lock(&enc->ccu->lock); + list_del_init(&enc->core_link); + enc->ccu->core_num--; + mutex_unlock(&enc->ccu->lock); + } + rkvenc2_free_rcbbuf(pdev, enc); + mpp_dev_remove(&enc->mpp); + rkvenc_procfs_remove(&enc->mpp); + } else { + struct mpp_dev *mpp = dev_get_drvdata(dev); + struct rkvenc_dev *enc = to_rkvenc_dev(mpp); + + dev_info(dev, "remove device\n"); + rkvenc2_free_rcbbuf(pdev, enc); + mpp_dev_remove(mpp); + rkvenc_procfs_remove(mpp); + } return 0; } @@ -1397,23 +2677,9 @@ static void rkvenc_shutdown(struct platform_device *pdev) { struct device *dev = &pdev->dev; - int ret; - int val; - struct rkvenc_dev *enc = platform_get_drvdata(pdev); - struct mpp_dev *mpp = &enc->mpp; - dev_info(dev, "shutdown device\n"); - - if (mpp->srv) - atomic_inc(&mpp->srv->shutdown_request); - - ret = readx_poll_timeout(atomic_read, - &mpp->task_count, - val, val == 0, 1000, 200000); - if (ret == -ETIMEDOUT) - dev_err(dev, "wait total running time out\n"); - - dev_info(dev, "shutdown success\n"); + if (!strstr(dev_name(dev), "ccu")) + mpp_dev_shutdown(pdev); } struct platform_driver rockchip_rkvenc2_driver = { -- Gitblit v1.6.2