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_rkvdec2_link.c | 2533 ++++++++++++++++++++++++++++++++++++++++------------------ 1 files changed, 1,722 insertions(+), 811 deletions(-) diff --git a/kernel/drivers/video/rockchip/mpp/mpp_rkvdec2_link.c b/kernel/drivers/video/rockchip/mpp/mpp_rkvdec2_link.c index dfe74e0..7c5a58d 100644 --- a/kernel/drivers/video/rockchip/mpp/mpp_rkvdec2_link.c +++ b/kernel/drivers/video/rockchip/mpp/mpp_rkvdec2_link.c @@ -12,115 +12,15 @@ #include <linux/slab.h> #include <soc/rockchip/pm_domains.h> #include <soc/rockchip/rockchip_dmc.h> +#include <soc/rockchip/rockchip_iommu.h> #include "mpp_rkvdec2_link.h" #include "hack/mpp_rkvdec2_link_hack_rk3568.c" -#ifdef CONFIG_PM_DEVFREQ -#include "../../../devfreq/governor.h" -#endif +#define RKVDEC2_LINK_HACK_TASK_FLAG (0xff) -#define WAIT_TIMEOUT_MS (2000) - -#define RKVDEC_MAX_WRITE_PART 6 -#define RKVDEC_MAX_READ_PART 2 - -struct rkvdec_link_part { - /* register offset of table buffer */ - u32 tb_reg_off; - /* start idx of task register */ - u32 reg_start; - /* number of task register */ - u32 reg_num; -}; - -struct rkvdec_link_status { - u32 dec_num_mask; - u32 err_flag_base; - u32 err_flag_bit; -}; - -struct rkvdec_link_info { - dma_addr_t iova; - /* total register for link table buffer */ - u32 tb_reg_num; - /* next link table addr in table buffer */ - u32 tb_reg_next; - /* current read back addr in table buffer */ - u32 tb_reg_r; - /* secondary enable in table buffer */ - u32 tb_reg_second_en; - u32 part_w_num; - u32 part_r_num; - - struct rkvdec_link_part part_w[RKVDEC_MAX_WRITE_PART]; - struct rkvdec_link_part part_r[RKVDEC_MAX_READ_PART]; - - /* interrupt read back in table buffer */ - u32 tb_reg_int; - bool hack_setup; - struct rkvdec_link_status reg_status; -}; - -struct rkvdec_link_info rkvdec_link_rk3568_hw_info = { - .tb_reg_num = 202, - .tb_reg_next = 0, - .tb_reg_r = 1, - .tb_reg_second_en = 8, - - .part_w_num = 6, - .part_r_num = 2, - .part_w[0] = { - .tb_reg_off = 4, - .reg_start = 8, - .reg_num = 20, - }, - .part_w[1] = { - .tb_reg_off = 24, - .reg_start = 64, - .reg_num = 52, - }, - .part_w[2] = { - .tb_reg_off = 76, - .reg_start = 128, - .reg_num = 16, - }, - .part_w[3] = { - .tb_reg_off = 92, - .reg_start = 160, - .reg_num = 40, - }, - .part_w[4] = { - .tb_reg_off = 132, - .reg_start = 224, - .reg_num = 16, - }, - .part_w[5] = { - .tb_reg_off = 148, - .reg_start = 256, - .reg_num = 16, - }, - .part_r[0] = { - .tb_reg_off = 164, - .reg_start = 224, - .reg_num = 10, - }, - .part_r[1] = { - .tb_reg_off = 174, - .reg_start = 258, - .reg_num = 28, - }, - .tb_reg_int = 164, - .hack_setup = 1, - .reg_status = { - .dec_num_mask = 0x3fffffff, - .err_flag_base = 0x010, - .err_flag_bit = BIT(31), - }, -}; - -/* vdpu382 link hw info */ +/* vdpu381 link hw info for rk3588 */ struct rkvdec_link_info rkvdec_link_v2_hw_info = { .tb_reg_num = 218, .tb_reg_next = 0, @@ -170,13 +70,139 @@ .reg_num = 28, }, .tb_reg_int = 180, + .tb_reg_cycle = 195, .hack_setup = 0, + .reg_status = { + .dec_num_mask = 0x3fffffff, + .err_flag_base = 0x010, + .err_flag_bit = BIT(31), + }, +}; + +/* vdpu34x link hw info for rk356x */ +struct rkvdec_link_info rkvdec_link_rk356x_hw_info = { + .tb_reg_num = 202, + .tb_reg_next = 0, + .tb_reg_r = 1, + .tb_reg_second_en = 8, + + .part_w_num = 6, + .part_r_num = 2, + .part_w[0] = { + .tb_reg_off = 4, + .reg_start = 8, + .reg_num = 20, + }, + .part_w[1] = { + .tb_reg_off = 24, + .reg_start = 64, + .reg_num = 52, + }, + .part_w[2] = { + .tb_reg_off = 76, + .reg_start = 128, + .reg_num = 16, + }, + .part_w[3] = { + .tb_reg_off = 92, + .reg_start = 160, + .reg_num = 40, + }, + .part_w[4] = { + .tb_reg_off = 132, + .reg_start = 224, + .reg_num = 16, + }, + .part_w[5] = { + .tb_reg_off = 148, + .reg_start = 256, + .reg_num = 16, + }, + .part_r[0] = { + .tb_reg_off = 164, + .reg_start = 224, + .reg_num = 10, + }, + .part_r[1] = { + .tb_reg_off = 174, + .reg_start = 258, + .reg_num = 28, + }, + .tb_reg_int = 164, + .tb_reg_cycle = 179, + .hack_setup = 1, + .reg_status = { + .dec_num_mask = 0x3fffffff, + .err_flag_base = 0x010, + .err_flag_bit = BIT(31), + }, +}; + +/* vdpu382 link hw info */ +struct rkvdec_link_info rkvdec_link_vdpu382_hw_info = { + .tb_reg_num = 222, + .tb_reg_next = 0, + .tb_reg_r = 1, + .tb_reg_second_en = 8, + + .part_w_num = 6, + .part_r_num = 2, + .part_w[0] = { + .tb_reg_off = 4, + .reg_start = 8, + .reg_num = 28, + }, + .part_w[1] = { + .tb_reg_off = 32, + .reg_start = 64, + .reg_num = 52, + }, + .part_w[2] = { + .tb_reg_off = 84, + .reg_start = 128, + .reg_num = 16, + }, + .part_w[3] = { + .tb_reg_off = 100, + .reg_start = 160, + .reg_num = 48, + }, + .part_w[4] = { + .tb_reg_off = 148, + .reg_start = 224, + .reg_num = 16, + }, + .part_w[5] = { + .tb_reg_off = 164, + .reg_start = 256, + .reg_num = 16, + }, + .part_r[0] = { + .tb_reg_off = 180, + .reg_start = 224, + .reg_num = 12, + }, + .part_r[1] = { + .tb_reg_off = 192, + .reg_start = 258, + .reg_num = 30, + }, + .tb_reg_int = 180, + .hack_setup = 0, + .tb_reg_cycle = 197, .reg_status = { .dec_num_mask = 0x000fffff, .err_flag_base = 0x024, .err_flag_bit = BIT(8), }, }; + +static void rkvdec2_link_free_task(struct kref *ref); +static void rkvdec2_link_timeout_proc(struct work_struct *work_s); +static int rkvdec2_link_iommu_fault_handle(struct iommu_domain *iommu, + struct device *iommu_dev, + unsigned long iova, + int status, void *arg); static void rkvdec_link_status_update(struct rkvdec_link_dev *dev) { @@ -228,7 +254,7 @@ u32 *reg = NULL; u32 i, j; - for (i = 0; i < dev->task_size; i++) { + for (i = 0; i < dev->task_capacity; i++) { reg = table_base + i * reg_count; mpp_err("slot %d link config iova %08x:\n", i, @@ -275,9 +301,8 @@ { mpp_err("dump link counter from %s\n", func); - mpp_err("task write %d read %d send %d recv %d run %d decoded %d total %d\n", - dev->task_write, dev->task_read, dev->task_send, dev->task_recv, - dev->task_to_run, dev->task_decoded, dev->task_total); + mpp_err("task pending %d running %d\n", + atomic_read(&dev->task_pending), dev->task_running); } int rkvdec_link_dump(struct mpp_dev *mpp) @@ -290,160 +315,6 @@ rkvdec_link_counter(__func__, dev); rkvdec_core_reg_dump(__func__, dev); rkvdec_link_node_dump(__func__, dev); - - return 0; -} - -static int rkvdec_link_get_task_write(struct rkvdec_link_dev *dev) -{ - int idx = dev->task_write < dev->task_size ? dev->task_write : - dev->task_write - dev->task_size; - - return idx; -} -static int rkvdec_link_inc_task_write(struct rkvdec_link_dev *dev) -{ - int task_write = rkvdec_link_get_task_write(dev); - - dev->task_write++; - if (dev->task_write >= dev->task_size * 2) - dev->task_write = 0; - - return task_write; -} -static int rkvdec_link_get_task_read(struct rkvdec_link_dev *dev) -{ - int idx = dev->task_read < dev->task_size ? dev->task_read : - dev->task_read - dev->task_size; - - return idx; -} -static int rkvdec_link_inc_task_read(struct rkvdec_link_dev *dev) -{ - int task_read = rkvdec_link_get_task_read(dev); - - dev->task_read++; - if (dev->task_read >= dev->task_size * 2) - dev->task_read = 0; - - return task_read; -} -static int rkvdec_link_get_task_hw_queue_length(struct rkvdec_link_dev *dev) -{ - int len; - - if (dev->task_send <= dev->task_recv) - len = dev->task_send + dev->task_size - dev->task_recv; - else - len = dev->task_send - dev->task_recv - dev->task_size; - - return len; -} -static int rkvdec_link_get_task_send(struct rkvdec_link_dev *dev) -{ - int idx = dev->task_send < dev->task_size ? dev->task_send : - dev->task_send - dev->task_size; - - return idx; -} -static int rkvdec_link_inc_task_send(struct rkvdec_link_dev *dev) -{ - int task_send = rkvdec_link_get_task_send(dev); - - dev->task_send++; - if (dev->task_send >= dev->task_size * 2) - dev->task_send = 0; - - return task_send; -} -static int rkvdec_link_inc_task_recv(struct rkvdec_link_dev *dev) -{ - int task_recv = dev->task_recv; - - dev->task_recv++; - if (dev->task_recv >= dev->task_size * 2) - dev->task_recv = 0; - - return task_recv; -} - -static int rkvdec_link_get_next_slot(struct rkvdec_link_dev *dev) -{ - int next = -1; - - if (dev->task_write == dev->task_read) - return next; - - next = rkvdec_link_get_task_write(dev); - - return next; -} - -static int rkvdec_link_write_task_to_slot(struct rkvdec_link_dev *dev, int idx, - struct mpp_task *mpp_task) -{ - u32 i, off, s, n; - struct rkvdec_link_part *part; - struct rkvdec_link_info *info; - struct mpp_dma_buffer *table; - struct rkvdec2_task *task; - int slot_idx; - u32 *tb_reg; - - if (idx < 0 || idx >= dev->task_size) { - mpp_err("send invalid task index %d\n", idx); - return -1; - } - - info = dev->info; - part = info->part_w; - table = dev->table; - task = to_rkvdec2_task(mpp_task); - - slot_idx = rkvdec_link_inc_task_write(dev); - if (idx != slot_idx) - dev_info(dev->dev, "slot index mismatch %d vs %d\n", - idx, slot_idx); - - if (task->need_hack) { - tb_reg = (u32 *)table->vaddr + slot_idx * dev->link_reg_count; - - rkvdec2_3568_hack_fix_link(tb_reg + 4); - - /* setup error mode flag */ - dev->tasks_hw[slot_idx] = NULL; - dev->task_to_run++; - dev->task_prepared++; - slot_idx = rkvdec_link_inc_task_write(dev); - } - - tb_reg = (u32 *)table->vaddr + slot_idx * dev->link_reg_count; - - for (i = 0; i < info->part_w_num; i++) { - off = part[i].tb_reg_off; - s = part[i].reg_start; - n = part[i].reg_num; - memcpy(&tb_reg[off], &task->reg[s], n * sizeof(u32)); - } - - /* setup error mode flag */ - tb_reg[9] |= BIT(18) | BIT(9); - tb_reg[info->tb_reg_second_en] |= RKVDEC_WAIT_RESET_EN; - - /* memset read registers */ - part = info->part_r; - for (i = 0; i < info->part_r_num; i++) { - off = part[i].tb_reg_off; - n = part[i].reg_num; - memset(&tb_reg[off], 0, n * sizeof(u32)); - } - - dev->tasks_hw[slot_idx] = mpp_task; - task->slot_idx = slot_idx; - dev->task_to_run++; - dev->task_prepared++; - mpp_dbg_link_flow("slot %d write task %d\n", slot_idx, - mpp_task->task_index); return 0; } @@ -467,34 +338,20 @@ mpp_write_relaxed(mpp, RKVDEC_REG_CLR_CACHE2_BASE, 1); } -static int rkvdec_link_send_task_to_hw(struct rkvdec_link_dev *dev, - struct mpp_task *mpp_task, - int slot_idx, u32 task_to_run, - int resend) +static int rkvdec2_link_enqueue(struct rkvdec_link_dev *link_dec, + struct mpp_task *mpp_task) { - void __iomem *reg_base = dev->reg_base; - struct mpp_dma_buffer *table = dev->table; - u32 task_total = dev->task_total; - u32 mode_start = 0; - u32 val; + void __iomem *reg_base = link_dec->reg_base; + struct rkvdec2_task *task = to_rkvdec2_task(mpp_task); + struct mpp_dma_buffer *table = task->table; + u32 link_en = 0; + u32 frame_num = 1; + u32 link_mode; + u32 timing_en = link_dec->mpp->srv->timing_en; - /* write address */ - if (!task_to_run || task_to_run > dev->task_size || - slot_idx < 0 || slot_idx >= dev->task_size) { - mpp_err("invalid task send cfg at %d count %d\n", - slot_idx, task_to_run); - rkvdec_link_counter("error on send", dev); - return 0; - } - - val = task_to_run; - if (!task_total || resend) - mode_start = 1; - - if (mode_start) { - u32 iova = table->iova + slot_idx * dev->link_node_size; - - rkvdec2_clear_cache(dev->mpp); + link_en = readl(reg_base + RKVDEC_LINK_EN_BASE); + if (!link_en) { + rkvdec2_clear_cache(link_dec->mpp); /* cleanup counter in hardware */ writel(0, reg_base + RKVDEC_LINK_MODE_BASE); /* start config before all registers are set */ @@ -504,54 +361,31 @@ wmb(); /* clear counter and enable link mode hardware */ writel(RKVDEC_LINK_BIT_EN, reg_base + RKVDEC_LINK_EN_BASE); - - dev->task_total = 0; - dev->task_decoded = 0; - - writel_relaxed(iova, reg_base + RKVDEC_LINK_CFG_ADDR_BASE); - } else { - val |= RKVDEC_LINK_BIT_ADD_MODE; - } - - if (!resend) { - u32 i; - - for (i = 0; i < task_to_run; i++) { - int next_idx = rkvdec_link_inc_task_send(dev); - struct mpp_task *task_ddr = dev->tasks_hw[next_idx]; - - if (!task_ddr) - continue; - - set_bit(TASK_STATE_START, &task_ddr->state); - schedule_delayed_work(&task_ddr->timeout_work, - msecs_to_jiffies(200)); - mpp_time_record(task_ddr); - } - } else { - if (task_total) - dev_info(dev->dev, "resend with total %d\n", task_total); - } + writel_relaxed(table->iova, reg_base + RKVDEC_LINK_CFG_ADDR_BASE); + link_mode = frame_num; + } else + link_mode = (frame_num | RKVDEC_LINK_BIT_ADD_MODE); /* set link mode */ - writel_relaxed(val, reg_base + RKVDEC_LINK_MODE_BASE); + writel_relaxed(link_mode, reg_base + RKVDEC_LINK_MODE_BASE); /* start config before all registers are set */ wmb(); + mpp_iommu_flush_tlb(link_dec->mpp->iommu_info); + mpp_task_run_begin(mpp_task, timing_en, MPP_WORK_TIMEOUT_DELAY); + + link_dec->task_running++; /* configure done */ writel(RKVDEC_LINK_BIT_CFG_DONE, reg_base + RKVDEC_LINK_CFG_CTRL_BASE); - - mpp_dbg_link_flow("slot %d enable task %d mode %s\n", slot_idx, - task_to_run, mode_start ? "start" : "add"); - if (mode_start) { + if (!link_en) { /* start hardware before all registers are set */ wmb(); /* clear counter and enable link mode hardware */ writel(RKVDEC_LINK_BIT_EN, reg_base + RKVDEC_LINK_EN_BASE); } + mpp_task_run_end(mpp_task, timing_en); - dev->task_total += task_to_run; return 0; } @@ -563,8 +397,7 @@ struct mpp_dma_buffer *table = link_dec->table; struct rkvdec_link_info *info = link_dec->info; struct rkvdec_link_part *part = info->part_r; - int slot_idx = task->slot_idx; - u32 *tb_reg = (u32 *)(table->vaddr + slot_idx * link_dec->link_node_size); + u32 *tb_reg = (u32 *)table->vaddr; u32 off, s, n; u32 i; @@ -584,154 +417,71 @@ return 0; } -static int rkvdec_link_isr_recv_task(struct mpp_dev *mpp, - struct rkvdec_link_dev *link_dec, - int count) -{ - struct rkvdec_link_info *info = link_dec->info; - u32 *table_base = (u32 *)link_dec->table->vaddr; - int i; - - for (i = 0; i < count; i++) { - int idx = rkvdec_link_get_task_read(link_dec); - struct mpp_task *mpp_task = link_dec->tasks_hw[idx]; - struct rkvdec2_task *task = NULL; - u32 *regs = NULL; - u32 irq_status = 0; - - if (!mpp_task) { - regs = table_base + idx * link_dec->link_reg_count; - mpp_dbg_link_flow("slot %d read task stuff\n", idx); - - link_dec->stuff_total++; - if (link_dec->statistic_count && - regs[RKVDEC_LINK_REG_CYCLE_CNT]) { - link_dec->stuff_cycle_sum += - regs[RKVDEC_LINK_REG_CYCLE_CNT]; - link_dec->stuff_cnt++; - if (link_dec->stuff_cnt >= - link_dec->statistic_count) { - dev_info( - link_dec->dev, "hw cycle %u\n", - (u32)(link_dec->stuff_cycle_sum / - link_dec->statistic_count)); - link_dec->stuff_cycle_sum = 0; - link_dec->stuff_cnt = 0; - } - } - - if (link_dec->error && (i == (count - 1))) { - link_dec->stuff_err++; - - irq_status = mpp_read_relaxed(mpp, RKVDEC_REG_INT_EN); - dev_info(link_dec->dev, "found stuff task error irq %08x %u/%u\n", - irq_status, link_dec->stuff_err, - link_dec->stuff_total); - - if (link_dec->stuff_on_error) { - dev_info(link_dec->dev, "stuff task error again %u/%u\n", - link_dec->stuff_err, - link_dec->stuff_total); - } - - link_dec->stuff_on_error = 1; - /* resend task */ - link_dec->decoded--; - } else { - link_dec->stuff_on_error = 0; - rkvdec_link_inc_task_recv(link_dec); - rkvdec_link_inc_task_read(link_dec); - link_dec->task_running--; - link_dec->task_prepared--; - } - - continue; - } - - mpp_time_diff(mpp_task); - task = to_rkvdec2_task(mpp_task); - regs = table_base + idx * link_dec->link_reg_count; - irq_status = regs[info->tb_reg_int]; - mpp_dbg_link_flow("slot %d rd task %d\n", idx, - mpp_task->task_index); - - task->irq_status = irq_status ? irq_status : mpp->irq_status; - - cancel_delayed_work_sync(&mpp_task->timeout_work); - set_bit(TASK_STATE_HANDLE, &mpp_task->state); - - if (link_dec->statistic_count && - regs[RKVDEC_LINK_REG_CYCLE_CNT]) { - link_dec->task_cycle_sum += - regs[RKVDEC_LINK_REG_CYCLE_CNT]; - link_dec->task_cnt++; - if (link_dec->task_cnt >= link_dec->statistic_count) { - dev_info(link_dec->dev, "hw cycle %u\n", - (u32)(link_dec->task_cycle_sum / - link_dec->statistic_count)); - link_dec->task_cycle_sum = 0; - link_dec->task_cnt = 0; - } - } - - rkvdec2_link_finish(mpp, mpp_task); - - set_bit(TASK_STATE_FINISH, &mpp_task->state); - - list_del_init(&mpp_task->queue_link); - link_dec->task_running--; - link_dec->task_prepared--; - - rkvdec_link_inc_task_recv(link_dec); - rkvdec_link_inc_task_read(link_dec); - - if (test_bit(TASK_STATE_ABORT, &mpp_task->state)) - set_bit(TASK_STATE_ABORT_READY, &mpp_task->state); - - set_bit(TASK_STATE_PROC_DONE, &mpp_task->state); - /* Wake up the GET thread */ - wake_up(&task->wait); - } - - return 0; -} - static void *rkvdec2_link_prepare(struct mpp_dev *mpp, struct mpp_task *mpp_task) { - struct mpp_task *out_task = NULL; struct rkvdec2_dev *dec = to_rkvdec2_dev(mpp); struct rkvdec_link_dev *link_dec = dec->link_dec; - int ret = 0; - int slot_idx; + struct mpp_dma_buffer *table = NULL; + struct rkvdec_link_part *part; + struct rkvdec2_task *task = to_rkvdec2_task(mpp_task); + struct rkvdec_link_info *info = link_dec->info; + u32 i, off, s, n; + u32 *tb_reg; mpp_debug_enter(); - slot_idx = rkvdec_link_get_next_slot(link_dec); - if (slot_idx < 0) { - mpp_err("capacity %d running %d\n", - mpp->task_capacity, link_dec->task_running); - dev_err(link_dec->dev, "no slot to write on get next slot\n"); - goto done; + if (test_bit(TASK_STATE_PREPARE, &mpp_task->state)) { + dev_err(mpp->dev, "task %d has prepared\n", mpp_task->task_index); + return mpp_task; } - ret = rkvdec_link_write_task_to_slot(link_dec, slot_idx, mpp_task); - if (ret >= 0) - out_task = mpp_task; - else - dev_err(mpp->dev, "no slot to write\n"); + table = list_first_entry_or_null(&link_dec->unused_list, struct mpp_dma_buffer, link); -done: + if (!table) + return NULL; + + /* fill regs value */ + tb_reg = (u32 *)table->vaddr; + part = info->part_w; + for (i = 0; i < info->part_w_num; i++) { + off = part[i].tb_reg_off; + s = part[i].reg_start; + n = part[i].reg_num; + memcpy(&tb_reg[off], &task->reg[s], n * sizeof(u32)); + } + + /* setup error mode flag */ + tb_reg[9] |= BIT(18) | BIT(9); + tb_reg[info->tb_reg_second_en] |= RKVDEC_WAIT_RESET_EN; + + /* memset read registers */ + part = info->part_r; + for (i = 0; i < info->part_r_num; i++) { + off = part[i].tb_reg_off; + n = part[i].reg_num; + memset(&tb_reg[off], 0, n * sizeof(u32)); + } + + list_move_tail(&table->link, &link_dec->used_list); + task->table = table; + set_bit(TASK_STATE_PREPARE, &mpp_task->state); + + mpp_dbg_link("session %d task %d prepare pending %d running %d\n", + mpp_task->session->index, mpp_task->task_index, + atomic_read(&link_dec->task_pending), link_dec->task_running); mpp_debug_leave(); - return out_task; + return mpp_task; } static int rkvdec2_link_reset(struct mpp_dev *mpp) { - struct rkvdec2_dev *dec = to_rkvdec2_dev(mpp); dev_info(mpp->dev, "resetting...\n"); + + disable_irq(mpp->irq); + mpp_iommu_disable_irq(mpp->iommu_info); /* FIXME lock resource lock of the other devices in combo */ mpp_iommu_down_write(mpp->iommu_info); @@ -740,11 +490,8 @@ rockchip_save_qos(mpp->dev); - mutex_lock(&dec->sip_reset_lock); - rockchip_dmcfreq_lock(); - sip_smc_vpu_reset(0, 0, 0); - rockchip_dmcfreq_unlock(); - mutex_unlock(&dec->sip_reset_lock); + if (mpp->hw_ops->reset) + mpp->hw_ops->reset(mpp); rockchip_restore_qos(mpp->dev); @@ -757,6 +504,8 @@ mpp_reset_up_write(mpp->reset_group); mpp_iommu_up_write(mpp->iommu_info); + enable_irq(mpp->irq); + mpp_iommu_enable_irq(mpp->iommu_info); dev_info(mpp->dev, "reset done\n"); return 0; @@ -768,15 +517,7 @@ struct rkvdec_link_dev *link_dec = dec->link_dec; u32 irq_status = 0; - if (!atomic_read(&link_dec->power_enabled)) { - dev_info(link_dec->dev, "irq on power off\n"); - return -1; - } - irq_status = readl(link_dec->reg_base + RKVDEC_LINK_IRQ_BASE); - - mpp_debug(DEBUG_IRQ_STATUS, "irq_status: %08x\n", irq_status); - mpp_dbg_link_flow("link irq %08x\n", irq_status); if (irq_status & RKVDEC_LINK_BIT_IRQ_RAW) { u32 enabled = readl(link_dec->reg_base + RKVDEC_LINK_EN_BASE); @@ -791,96 +532,14 @@ link_dec->irq_status = irq_status; mpp->irq_status = mpp_read_relaxed(mpp, RKVDEC_REG_INT_EN); - mpp_dbg_link_flow("core irq %08x\n", mpp->irq_status); writel_relaxed(0, link_dec->reg_base + RKVDEC_LINK_IRQ_BASE); } + mpp_debug(DEBUG_IRQ_STATUS | DEBUG_LINK_TABLE, "irq_status: %08x : %08x\n", + irq_status, mpp->irq_status); + return 0; -} - -static int rkvdec2_link_isr(struct mpp_dev *mpp) -{ - struct rkvdec2_dev *dec = to_rkvdec2_dev(mpp); - struct rkvdec_link_dev *link_dec = dec->link_dec; - struct rkvdec_link_info *link_info = link_dec->info; - /* keep irq_status */ - u32 irq_status = link_dec->irq_status; - u32 prev_dec_num; - int count = 0; - u32 len = 0; - u32 need_reset = atomic_read(&mpp->reset_request); - u32 task_timeout = link_dec->task_on_timeout; - - mpp_debug_enter(); - - disable_irq(mpp->irq); - rkvdec_link_status_update(link_dec); - link_dec->irq_status = irq_status; - prev_dec_num = link_dec->task_decoded; - - if (!link_dec->enabled || task_timeout) { - u32 val; - - if (task_timeout) - rkvdec_link_reg_dump("timeout", link_dec); - - val = mpp_read(mpp, 224 * 4); - if (link_info->hack_setup && !(val & BIT(2))) { - /* only for rk356x */ - dev_info(mpp->dev, "frame not complete\n"); - link_dec->decoded++; - } - } - count = (int)link_dec->decoded - (int)prev_dec_num; - - /* handle counter wrap */ - if (link_dec->enabled && !count && !need_reset) { - /* process extra isr when task is processed */ - enable_irq(mpp->irq); - goto done; - } - - /* get previous ready task */ - if (count) { - rkvdec_link_isr_recv_task(mpp, link_dec, count); - link_dec->task_decoded = link_dec->decoded; - } - - if (!link_dec->enabled || need_reset) - goto do_reset; - - enable_irq(mpp->irq); - goto done; - -do_reset: - /* NOTE: irq may run with reset */ - atomic_inc(&mpp->reset_request); - rkvdec2_link_reset(mpp); - link_dec->task_decoded = 0; - link_dec->task_total = 0; - enable_irq(mpp->irq); - - if (link_dec->total == link_dec->decoded) - goto done; - - len = rkvdec_link_get_task_hw_queue_length(link_dec); - if (len > link_dec->task_size) - rkvdec_link_counter("invalid len", link_dec); - - if (len) { - int slot_idx = rkvdec_link_get_task_read(link_dec); - struct mpp_task *mpp_task = NULL; - - mpp_task = link_dec->tasks_hw[slot_idx]; - rkvdec_link_send_task_to_hw(link_dec, mpp_task, - slot_idx, len, 1); - } - -done: - mpp_debug_leave(); - - return IRQ_HANDLED; } int rkvdec2_link_remove(struct mpp_dev *mpp, struct rkvdec_link_dev *link_dec) @@ -933,12 +592,6 @@ } link_dec->table = table; - link_dec->task_size = task_capacity; - link_dec->task_count = 0; - link_dec->task_write = 0; - link_dec->task_read = link_dec->task_size; - link_dec->task_send = 0; - link_dec->task_recv = link_dec->task_size; return 0; err_free_node: @@ -977,18 +630,13 @@ struct rkvdec_link_dev *link_dec = NULL; struct device *dev = &pdev->dev; struct mpp_dev *mpp = &dec->mpp; + struct mpp_dma_buffer *table; + int i; mpp_debug_enter(); link_dec = devm_kzalloc(dev, sizeof(*link_dec), GFP_KERNEL); if (!link_dec) { - ret = -ENOMEM; - goto done; - } - - link_dec->tasks_hw = devm_kzalloc(dev, sizeof(*link_dec->tasks_hw) * - mpp->task_capacity, GFP_KERNEL); - if (!link_dec->tasks_hw) { ret = -ENOMEM; goto done; } @@ -1014,12 +662,33 @@ if (ret) goto done; - if (link_dec->info->hack_setup) + /* alloc table pointer array */ + table = devm_kmalloc_array(mpp->dev, mpp->task_capacity, + sizeof(*table), GFP_KERNEL | __GFP_ZERO); + if (!table) + return -ENOMEM; + + /* init table array */ + link_dec->table_array = table; + INIT_LIST_HEAD(&link_dec->used_list); + INIT_LIST_HEAD(&link_dec->unused_list); + for (i = 0; i < mpp->task_capacity; i++) { + table[i].iova = link_dec->table->iova + i * link_dec->link_node_size; + table[i].vaddr = link_dec->table->vaddr + i * link_dec->link_node_size; + table[i].size = link_dec->link_node_size; + INIT_LIST_HEAD(&table[i].link); + list_add_tail(&table[i].link, &link_dec->unused_list); + } + + if (dec->fix) rkvdec2_link_hack_data_setup(dec->fix); + + mpp->fault_handler = rkvdec2_link_iommu_fault_handle; link_dec->mpp = mpp; link_dec->dev = dev; atomic_set(&link_dec->task_timeout, 0); + atomic_set(&link_dec->task_pending, 0); atomic_set(&link_dec->power_enabled, 0); link_dec->irq_enabled = 1; @@ -1033,11 +702,6 @@ devm_iounmap(dev, link_dec->reg_base); link_dec->reg_base = NULL; } - if (link_dec->tasks_hw) { - devm_kfree(dev, link_dec->tasks_hw); - link_dec->tasks_hw = NULL; - } - devm_kfree(dev, link_dec); link_dec = NULL; } @@ -1055,13 +719,13 @@ struct mpp_task *task = container_of(ref, struct mpp_task, ref); if (!task->session) { - mpp_err("task %d task->session is null.\n", task->task_index); + mpp_err("task %d task->session is null.\n", task->task_id); return; } session = task->session; mpp_debug_func(DEBUG_TASK_INFO, "task %d:%d state 0x%lx\n", - session->index, task->task_index, task->state); + session->index, task->task_id, task->state); if (!session->mpp) { mpp_err("session %d session->mpp is null.\n", session->index); return; @@ -1080,30 +744,16 @@ kthread_queue_work(&mpp->queue->worker, &mpp->work); } -static void rkvdec2_link_trigger_timeout(struct mpp_dev *mpp) -{ - struct rkvdec2_dev *dec = to_rkvdec2_dev(mpp); - struct rkvdec_link_dev *link_dec = dec->link_dec; - - atomic_inc(&link_dec->task_timeout); - rkvdec2_link_trigger_work(mpp); -} - -static void rkvdec2_link_trigger_irq(struct mpp_dev *mpp) -{ - struct rkvdec2_dev *dec = to_rkvdec2_dev(mpp); - struct rkvdec_link_dev *link_dec = dec->link_dec; - - link_dec->task_irq++; - rkvdec2_link_trigger_work(mpp); -} - -static void rkvdec2_link_power_on(struct mpp_dev *mpp) +static int rkvdec2_link_power_on(struct mpp_dev *mpp) { struct rkvdec2_dev *dec = to_rkvdec2_dev(mpp); struct rkvdec_link_dev *link_dec = dec->link_dec; if (!atomic_xchg(&link_dec->power_enabled, 1)) { + if (mpp_iommu_attach(mpp->iommu_info)) { + dev_err(mpp->dev, "mpp_iommu_attach failed\n"); + return -ENODATA; + } pm_runtime_get_sync(mpp->dev); pm_stay_awake(mpp->dev); @@ -1112,31 +762,17 @@ if (!link_dec->irq_enabled) { enable_irq(mpp->irq); + mpp_iommu_enable_irq(mpp->iommu_info); link_dec->irq_enabled = 1; } mpp_clk_set_rate(&dec->aclk_info, CLK_MODE_ADVANCED); mpp_clk_set_rate(&dec->cabac_clk_info, CLK_MODE_ADVANCED); mpp_clk_set_rate(&dec->hevc_cabac_clk_info, CLK_MODE_ADVANCED); - -#ifdef CONFIG_PM_DEVFREQ - if (dec->devfreq) { - unsigned long core_rate_hz; - - mutex_lock(&dec->devfreq->lock); - core_rate_hz = mpp_get_clk_info_rate_hz(&dec->core_clk_info, - CLK_MODE_ADVANCED); - if (dec->core_rate_hz != core_rate_hz) { - dec->core_rate_hz = core_rate_hz; - update_devfreq(dec->devfreq); - } - mutex_unlock(&dec->devfreq->lock); - - return; - } -#endif - mpp_clk_set_rate(&dec->core_clk_info, CLK_MODE_ADVANCED); + mpp_devfreq_set_core_rate(mpp, CLK_MODE_ADVANCED); + mpp_iommu_dev_activate(mpp->iommu_info, mpp); } + return 0; } static void rkvdec2_link_power_off(struct mpp_dev *mpp) @@ -1146,6 +782,7 @@ if (atomic_xchg(&link_dec->power_enabled, 0)) { disable_irq(mpp->irq); + mpp_iommu_disable_irq(mpp->iommu_info); link_dec->irq_enabled = 0; if (mpp->hw_ops->clk_off) @@ -1154,175 +791,282 @@ pm_relax(mpp->dev); pm_runtime_put_sync_suspend(mpp->dev); - link_dec->task_decoded = 0; - link_dec->task_total = 0; - mpp_clk_set_rate(&dec->aclk_info, CLK_MODE_NORMAL); mpp_clk_set_rate(&dec->cabac_clk_info, CLK_MODE_NORMAL); mpp_clk_set_rate(&dec->hevc_cabac_clk_info, CLK_MODE_NORMAL); - -#ifdef CONFIG_PM_DEVFREQ - if (dec->devfreq) { - unsigned long core_rate_hz; - - mutex_lock(&dec->devfreq->lock); - core_rate_hz = mpp_get_clk_info_rate_hz(&dec->core_clk_info, - CLK_MODE_NORMAL); - if (dec->core_rate_hz != core_rate_hz) { - dec->core_rate_hz = core_rate_hz; - update_devfreq(dec->devfreq); - } - mutex_unlock(&dec->devfreq->lock); - - return; - } -#endif - mpp_clk_set_rate(&dec->core_clk_info, CLK_MODE_NORMAL); + mpp_devfreq_set_core_rate(mpp, CLK_MODE_NORMAL); + mpp_iommu_dev_deactivate(mpp->iommu_info, mpp); } } static void rkvdec2_link_timeout_proc(struct work_struct *work_s) { struct mpp_dev *mpp; + struct rkvdec2_dev *dec; struct mpp_session *session; struct mpp_task *task = container_of(to_delayed_work(work_s), struct mpp_task, timeout_work); if (test_and_set_bit(TASK_STATE_HANDLE, &task->state)) { mpp_err("task %d state %lx has been handled\n", - task->task_index, task->state); + task->task_id, task->state); return; } if (!task->session) { - mpp_err("task %d session is null.\n", task->task_index); + mpp_err("task %d session is null.\n", task->task_id); return; } session = task->session; if (!session->mpp) { mpp_err("task %d:%d mpp is null.\n", session->index, - task->task_index); + task->task_id); return; } mpp = session->mpp; - rkvdec2_link_trigger_timeout(mpp); + set_bit(TASK_STATE_TIMEOUT, &task->state); + + dec = to_rkvdec2_dev(mpp); + atomic_inc(&dec->link_dec->task_timeout); + + dev_err(mpp->dev, "session %d task %d state %#lx timeout, cnt %d\n", + session->index, task->task_index, task->state, + atomic_read(&dec->link_dec->task_timeout)); + + rkvdec2_link_trigger_work(mpp); } -static void mpp_taskqueue_scan_pending_abort_task(struct mpp_taskqueue *queue) +static int rkvdec2_link_iommu_fault_handle(struct iommu_domain *iommu, + struct device *iommu_dev, + unsigned long iova, + int status, void *arg) { - struct mpp_task *task, *n; + struct mpp_dev *mpp = (struct mpp_dev *)arg; + struct rkvdec2_dev *dec = to_rkvdec2_dev(mpp); + struct mpp_task *mpp_task = NULL, *n; + struct mpp_taskqueue *queue; - mutex_lock(&queue->pending_lock); - /* Check and pop all timeout task */ - list_for_each_entry_safe(task, n, &queue->pending_list, queue_link) { - struct mpp_session *session = task->session; + dev_err(iommu_dev, "fault addr 0x%08lx status %x arg %p\n", + iova, status, arg); - if (test_bit(TASK_STATE_ABORT, &task->state)) { - mutex_lock(&session->pending_lock); - /* wait and signal */ - list_del_init(&task->queue_link); - mutex_unlock(&session->pending_lock); - kref_put(&task->ref, rkvdec2_link_free_task); + if (!mpp) { + dev_err(iommu_dev, "pagefault without device to handle\n"); + return 0; + } + queue = mpp->queue; + list_for_each_entry_safe(mpp_task, n, &queue->running_list, queue_link) { + struct rkvdec_link_info *info = dec->link_dec->info; + struct rkvdec2_task *task = to_rkvdec2_task(mpp_task); + u32 *tb_reg = (u32 *)task->table->vaddr; + u32 irq_status = tb_reg[info->tb_reg_int]; + + if (!irq_status) { + mpp_task_dump_mem_region(mpp, mpp_task); + break; } } - mutex_unlock(&queue->pending_lock); + + mpp_task_dump_hw_reg(mpp); + /* + * 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); + dec->mmu_fault = 1; + + return 0; +} + +static void rkvdec2_link_resend(struct mpp_dev *mpp) +{ + struct rkvdec2_dev *dec = to_rkvdec2_dev(mpp); + struct rkvdec_link_dev *link_dec = dec->link_dec; + struct mpp_taskqueue *queue = mpp->queue; + struct mpp_task *mpp_task, *n; + + link_dec->task_running = 0; + list_for_each_entry_safe(mpp_task, n, &queue->running_list, queue_link) { + dev_err(mpp->dev, "resend task %d\n", mpp_task->task_index); + cancel_delayed_work_sync(&mpp_task->timeout_work); + clear_bit(TASK_STATE_TIMEOUT, &mpp_task->state); + clear_bit(TASK_STATE_HANDLE, &mpp_task->state); + rkvdec2_link_enqueue(link_dec, mpp_task); + } } static void rkvdec2_link_try_dequeue(struct mpp_dev *mpp) { struct rkvdec2_dev *dec = to_rkvdec2_dev(mpp); struct rkvdec_link_dev *link_dec = dec->link_dec; - struct mpp_task *task; struct mpp_taskqueue *queue = mpp->queue; - int task_irq = link_dec->task_irq; - int task_irq_prev = link_dec->task_irq_prev; - int task_timeout = atomic_read(&link_dec->task_timeout); + struct mpp_task *mpp_task = NULL, *n; + struct rkvdec_link_info *info = link_dec->info; + u32 reset_flag = 0; + u32 iommu_fault = dec->mmu_fault && (mpp->irq_status & RKVDEC_TIMEOUT_STA); + u32 link_en = atomic_read(&link_dec->power_enabled) ? + readl(link_dec->reg_base + RKVDEC_LINK_EN_BASE) : 0; + u32 force_dequeue = iommu_fault || !link_en; + u32 dequeue_cnt = 0; - if (!link_dec->task_running) - goto done; + list_for_each_entry_safe(mpp_task, n, &queue->running_list, queue_link) { + /* + * Because there are multiple tasks enqueue at the same time, + * soft timeout may be triggered at the same time, but in reality only + * first task is being timeout because of the hardware stuck, + * so only process the first task. + */ + u32 timeout_flag = dequeue_cnt ? 0 : test_bit(TASK_STATE_TIMEOUT, &mpp_task->state); + struct rkvdec2_task *task = to_rkvdec2_task(mpp_task); + u32 *tb_reg = (u32 *)task->table->vaddr; + u32 abort_flag = test_bit(TASK_STATE_ABORT, &mpp_task->state); + u32 irq_status = tb_reg[info->tb_reg_int]; + u32 task_done = irq_status || timeout_flag || abort_flag; - if (task_timeout != link_dec->task_timeout_prev) { - dev_info(link_dec->dev, "process task timeout\n"); - atomic_inc(&mpp->reset_request); - link_dec->task_on_timeout = - task_timeout - link_dec->task_timeout_prev; - goto proc; + /* + * there are some cases will cause hw cannot write reg to ddr: + * 1. iommu pagefault + * 2. link stop(link_en == 0) because of err task, it is a rk356x issue. + * so need force dequeue one task. + */ + if (force_dequeue) + task_done = 1; + + if (!task_done) + break; + + dequeue_cnt++; + /* check hack task only for rk356x*/ + if (task->need_hack == RKVDEC2_LINK_HACK_TASK_FLAG) { + cancel_delayed_work_sync(&mpp_task->timeout_work); + list_move_tail(&task->table->link, &link_dec->unused_list); + list_del_init(&mpp_task->queue_link); + link_dec->task_running--; + link_dec->hack_task_running--; + kfree(task); + mpp_dbg_link("hack running %d irq_status %#08x timeout %d abort %d\n", + link_dec->hack_task_running, irq_status, + timeout_flag, abort_flag); + continue; + } + + /* + * if timeout/abort/force dequeue found, reset and stop hw first. + */ + if ((timeout_flag || abort_flag || force_dequeue) && !reset_flag) { + dev_err(mpp->dev, "session %d task %d timeout %d abort %d force_dequeue %d\n", + mpp_task->session->index, mpp_task->task_index, + timeout_flag, abort_flag, force_dequeue); + rkvdec2_link_reset(mpp); + reset_flag = 1; + dec->mmu_fault = 0; + mpp->irq_status = 0; + force_dequeue = 0; + } + + cancel_delayed_work_sync(&mpp_task->timeout_work); + + task->irq_status = irq_status; + mpp_task->hw_cycles = tb_reg[info->tb_reg_cycle]; + mpp_time_diff_with_hw_time(mpp_task, dec->cycle_clk->real_rate_hz); + rkvdec2_link_finish(mpp, mpp_task); + + list_move_tail(&task->table->link, &link_dec->unused_list); + list_del_init(&mpp_task->queue_link); + link_dec->task_running--; + + set_bit(TASK_STATE_HANDLE, &mpp_task->state); + set_bit(TASK_STATE_PROC_DONE, &mpp_task->state); + set_bit(TASK_STATE_FINISH, &mpp_task->state); + set_bit(TASK_STATE_DONE, &mpp_task->state); + if (test_bit(TASK_STATE_ABORT, &mpp_task->state)) + set_bit(TASK_STATE_ABORT_READY, &mpp_task->state); + + mpp_dbg_link("session %d task %d irq_status %#08x timeout %d abort %d\n", + mpp_task->session->index, mpp_task->task_index, + irq_status, timeout_flag, abort_flag); + + if (irq_status & RKVDEC_INT_ERROR_MASK) { + dev_err(mpp->dev, + "session %d task %d irq_status %#08x timeout %u abort %u\n", + mpp_task->session->index, mpp_task->task_index, + irq_status, timeout_flag, abort_flag); + if (!reset_flag) + atomic_inc(&mpp->reset_request); + } + + wake_up(&mpp_task->wait); + kref_put(&mpp_task->ref, rkvdec2_link_free_task); } - if (task_irq == task_irq_prev) - goto done; - - if (!atomic_read(&link_dec->power_enabled)) { - dev_info(link_dec->dev, "dequeue on power off\n"); - goto done; - } - -proc: - task = list_first_entry_or_null(&queue->running_list, struct mpp_task, - queue_link); - if (!task) { - mpp_err("can found task on trydequeue with %d running task\n", - link_dec->task_running); - goto done; - } - - /* Check and process all finished task */ - rkvdec2_link_isr(mpp); - -done: - link_dec->task_irq_prev = task_irq; - link_dec->task_timeout_prev = task_timeout; - link_dec->task_on_timeout = 0; - - mpp_taskqueue_scan_pending_abort_task(queue); - - /* TODO: if reset is needed do reset here */ + /* resend running task after reset */ + if (reset_flag && !list_empty(&queue->running_list)) + rkvdec2_link_resend(mpp); } -static int mpp_task_queue(struct mpp_dev *mpp, struct mpp_task *task) +static int mpp_task_queue(struct mpp_dev *mpp, struct mpp_task *mpp_task) { struct rkvdec2_dev *dec = to_rkvdec2_dev(mpp); struct rkvdec_link_dev *link_dec = dec->link_dec; - u32 task_to_run = 0; - int slot_idx = 0; - int ret; + struct mpp_taskqueue *queue = mpp->queue; + struct rkvdec2_task *task = to_rkvdec2_task(mpp_task); mpp_debug_enter(); - /* - * for iommu share hardware, should attach to ensure - * working in current device - */ - ret = mpp_iommu_attach(mpp->iommu_info); - if (ret) { - dev_err(mpp->dev, "mpp_iommu_attach failed\n"); - return -ENODATA; - } - rkvdec2_link_power_on(mpp); - mpp_debug(DEBUG_TASK_INFO, "pid %d, start hw %s\n", - task->session->pid, dev_name(mpp->dev)); - /* prepare the task for running */ - if (test_and_set_bit(TASK_STATE_PREPARE, &task->state)) - mpp_err("task %d has been prepare twice\n", task->task_index); + /* hack for rk356x */ + if (task->need_hack) { + u32 *tb_reg; + struct mpp_dma_buffer *table; + struct rkvdec2_task *hack_task; + struct rkvdec_link_info *info = link_dec->info; - rkvdec2_link_prepare(mpp, task); + /* need reserved 2 unused task for need hack task */ + if (link_dec->task_running > (link_dec->task_capacity - 2)) + return -EBUSY; - task_to_run = link_dec->task_to_run; - if (!task_to_run) { - dev_err(link_dec->dev, "nothing to run\n"); - goto done; + table = list_first_entry_or_null(&link_dec->unused_list, + struct mpp_dma_buffer, + link); + if (!table) + return -EBUSY; + + hack_task = kzalloc(sizeof(*hack_task), GFP_KERNEL); + + if (!hack_task) + return -ENOMEM; + + mpp_task_init(mpp_task->session, &hack_task->mpp_task); + INIT_DELAYED_WORK(&hack_task->mpp_task.timeout_work, + rkvdec2_link_timeout_proc); + + tb_reg = (u32 *)table->vaddr; + memset(tb_reg + info->part_r[0].tb_reg_off, 0, info->part_r[0].reg_num); + rkvdec2_3568_hack_fix_link(tb_reg + 4); + list_move_tail(&table->link, &link_dec->used_list); + hack_task->table = table; + hack_task->need_hack = RKVDEC2_LINK_HACK_TASK_FLAG; + rkvdec2_link_enqueue(link_dec, &hack_task->mpp_task); + mpp_taskqueue_pending_to_run(queue, &hack_task->mpp_task); + link_dec->hack_task_running++; + mpp_dbg_link("hack task send to hw, hack running %d\n", + link_dec->hack_task_running); } - mpp_reset_down_read(mpp->reset_group); - link_dec->task_to_run = 0; - slot_idx = rkvdec_link_get_task_send(link_dec); - link_dec->task_running += task_to_run; - rkvdec_link_send_task_to_hw(link_dec, task, slot_idx, task_to_run, 0); + /* process normal */ + if (!rkvdec2_link_prepare(mpp, mpp_task)) + return -EBUSY; -done: + rkvdec2_link_enqueue(link_dec, mpp_task); + + set_bit(TASK_STATE_RUNNING, &mpp_task->state); + atomic_dec(&link_dec->task_pending); + mpp_taskqueue_pending_to_run(queue, mpp_task); + + mpp_dbg_link("session %d task %d send to hw pending %d running %d\n", + mpp_task->session->index, mpp_task->task_index, + atomic_read(&link_dec->task_pending), link_dec->task_running); mpp_debug_leave(); return 0; @@ -1334,7 +1078,7 @@ int ret = rkvdec2_link_irq(mpp); if (!ret) - rkvdec2_link_trigger_irq(mpp); + rkvdec2_link_trigger_work(mpp); return IRQ_HANDLED; } @@ -1372,7 +1116,6 @@ struct mpp_task *task) { set_bit(TASK_STATE_DONE, &task->state); - kref_put(&task->ref, rkvdec2_link_free_task); return 0; } @@ -1381,10 +1124,10 @@ struct mpp_task_msgs *msgs) { struct mpp_task *task = NULL; - struct rkvdec2_task *dec_task = NULL; struct mpp_dev *mpp = session->mpp; - u32 fmt; struct rkvdec_link_info *link_info = mpp->var->hw_info->link_info; + struct rkvdec2_dev *dec = to_rkvdec2_dev(mpp); + struct rkvdec_link_dev *link_dec = dec->link_dec; task = rkvdec2_alloc_task(session, msgs); if (!task) { @@ -1393,6 +1136,9 @@ } if (link_info->hack_setup) { + u32 fmt; + struct rkvdec2_task *dec_task = NULL; + dec_task = to_rkvdec2_task(task); fmt = RKVDEC_GET_FORMAT(dec_task->reg[RKVDEC_REG_FORMAT_INDEX]); dec_task->need_hack = (fmt == RKVDEC_FMT_H264D); @@ -1401,6 +1147,7 @@ kref_init(&task->ref); atomic_set(&task->abort_request, 0); task->task_index = atomic_fetch_inc(&mpp->task_index); + task->task_id = atomic_fetch_inc(&mpp->queue->task_id); INIT_DELAYED_WORK(&task->timeout_work, rkvdec2_link_timeout_proc); atomic_inc(&session->task_count); @@ -1414,6 +1161,7 @@ mutex_lock(&mpp->queue->pending_lock); list_add_tail(&task->queue_link, &mpp->queue->pending_list); mutex_unlock(&mpp->queue->pending_lock); + atomic_inc(&link_dec->task_pending); /* push current task to queue */ atomic_inc(&mpp->task_count); @@ -1430,7 +1178,6 @@ { struct mpp_dev *mpp = session->mpp; struct mpp_task *mpp_task; - struct rkvdec2_task *task; int ret; mpp_task = mpp_session_get_pending_task(session); @@ -1439,20 +1186,16 @@ return -EIO; } - task = to_rkvdec2_task(mpp_task); - ret = wait_event_timeout(task->wait, task_is_done(mpp_task), - msecs_to_jiffies(WAIT_TIMEOUT_MS)); - if (ret) { - ret = rkvdec2_result(mpp, mpp_task, msgs); + ret = wait_event_interruptible(mpp_task->wait, task_is_done(mpp_task)); + if (ret == -ERESTARTSYS) + mpp_err("wait task break by signal\n"); - mpp_session_pop_done(session, mpp_task); - } else { - mpp_err("task %d:%d statue %lx timeout -> abort\n", - session->index, mpp_task->task_index, mpp_task->state); + ret = rkvdec2_result(mpp, mpp_task, msgs); - atomic_inc(&mpp_task->abort_request); - set_bit(TASK_STATE_ABORT, &mpp_task->state); - } + mpp_session_pop_done(session, mpp_task); + mpp_debug_func(DEBUG_TASK_INFO, "wait done session %d:%d count %d task %d state %lx\n", + session->device_type, session->index, atomic_read(&session->task_count), + mpp_task->task_index, mpp_task->state); mpp_session_pop_pending(session, mpp_task); return ret; @@ -1461,32 +1204,25 @@ void rkvdec2_link_worker(struct kthread_work *work_s) { struct mpp_dev *mpp = container_of(work_s, struct mpp_dev, work); - struct rkvdec2_dev *dec = to_rkvdec2_dev(mpp); - struct rkvdec_link_dev *link_dec = dec->link_dec; struct mpp_task *task; struct mpp_taskqueue *queue = mpp->queue; + u32 all_done; mpp_debug_enter(); - /* - * process timeout and finished task. - */ + /* dequeue running task */ rkvdec2_link_try_dequeue(mpp); -again: + /* process reset */ if (atomic_read(&mpp->reset_request)) { - if (link_dec->task_running || link_dec->task_prepared) - goto done; - - disable_irq(mpp->irq); rkvdec2_link_reset(mpp); - link_dec->task_decoded = 0; - link_dec->task_total = 0; - enable_irq(mpp->irq); + /* resend running task after reset */ + if (!list_empty(&queue->running_list)) + rkvdec2_link_resend(mpp); } - /* - * process pending queue to find the task to accept. - */ + +again: + /* get pending task to process */ mutex_lock(&queue->pending_lock); task = list_first_entry_or_null(&queue->pending_list, struct mpp_task, queue_link); @@ -1494,80 +1230,37 @@ if (!task) goto done; - if (test_bit(TASK_STATE_ABORT, &task->state)) { - struct rkvdec2_task *dec_task = to_rkvdec2_task(task); - + /* check abort task */ + if (atomic_read(&task->abort_request)) { mutex_lock(&queue->pending_lock); list_del_init(&task->queue_link); - kref_get(&task->ref); set_bit(TASK_STATE_ABORT_READY, &task->state); set_bit(TASK_STATE_PROC_DONE, &task->state); mutex_unlock(&queue->pending_lock); - wake_up(&dec_task->wait); + wake_up(&task->wait); kref_put(&task->ref, rkvdec2_link_free_task); goto again; } - /* - * if target device can accept more task send the task to run. - */ - if (link_dec->task_running >= link_dec->task_capacity - 2) - goto done; - - if (mpp_task_queue(mpp, task)) { - /* failed to run */ - mpp_err("%p failed to process task %p:%d\n", - mpp, task, task->task_index); - } else { - mutex_lock(&queue->pending_lock); - set_bit(TASK_STATE_RUNNING, &task->state); - list_move_tail(&task->queue_link, &queue->running_list); - mutex_unlock(&queue->pending_lock); + /* queue task to hw */ + if (!mpp_task_queue(mpp, task)) goto again; - } + done: + + /* if no task in pending and running list, power off device */ + mutex_lock(&queue->pending_lock); + all_done = list_empty(&queue->pending_list) && list_empty(&queue->running_list); + mutex_unlock(&queue->pending_lock); + + if (all_done) + rkvdec2_link_power_off(mpp); + + mpp_session_cleanup_detach(queue, work_s); + mpp_debug_leave(); - - if (link_dec->task_irq != link_dec->task_irq_prev || - atomic_read(&link_dec->task_timeout) != link_dec->task_timeout_prev) - rkvdec2_link_trigger_work(mpp); - - /* if no task for running power off device */ - { - u32 all_done = 0; - - mutex_lock(&queue->pending_lock); - all_done = list_empty(&queue->pending_list); - mutex_unlock(&queue->pending_lock); - - if (all_done && !link_dec->task_running && !link_dec->task_prepared) - rkvdec2_link_power_off(mpp); - } - - mutex_lock(&queue->session_lock); - while (queue->detach_count) { - struct mpp_session *session = NULL; - - session = list_first_entry_or_null(&queue->session_detach, struct mpp_session, - session_link); - if (session) { - list_del_init(&session->session_link); - queue->detach_count--; - } - - mutex_unlock(&queue->session_lock); - - if (session) { - mpp_dbg_session("%s detach count %d\n", dev_name(mpp->dev), - queue->detach_count); - mpp_session_deinit(session); - } - - mutex_lock(&queue->session_lock); - } - mutex_unlock(&queue->session_lock); } void rkvdec2_link_session_deinit(struct mpp_session *session) @@ -1580,9 +1273,9 @@ if (session->dma) { mpp_dbg_session("session %d destroy dma\n", session->index); - mpp_iommu_down_read(mpp->iommu_info); + mpp_iommu_down_write(mpp->iommu_info); mpp_dma_session_destroy(session->dma); - mpp_iommu_up_read(mpp->iommu_info); + mpp_iommu_up_write(mpp->iommu_info); session->dma = NULL; } if (session->srv) { @@ -1598,3 +1291,1221 @@ mpp_debug_leave(); } + +#define RKVDEC2_1080P_PIXELS (1920*1080) +#define RKVDEC2_4K_PIXELS (4096*2304) +#define RKVDEC2_8K_PIXELS (7680*4320) +#define RKVDEC2_CCU_TIMEOUT_20MS (0xefffff) +#define RKVDEC2_CCU_TIMEOUT_50MS (0x2cfffff) +#define RKVDEC2_CCU_TIMEOUT_100MS (0x4ffffff) + +static u32 rkvdec2_ccu_get_timeout_threshold(struct rkvdec2_task *task) +{ + u32 pixels = task->pixels; + + if (pixels < RKVDEC2_1080P_PIXELS) + return RKVDEC2_CCU_TIMEOUT_20MS; + else if (pixels < RKVDEC2_4K_PIXELS) + return RKVDEC2_CCU_TIMEOUT_50MS; + else + return RKVDEC2_CCU_TIMEOUT_100MS; +} + +int rkvdec2_attach_ccu(struct device *dev, struct rkvdec2_dev *dec) +{ + int ret; + struct device_node *np; + struct platform_device *pdev; + struct rkvdec2_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; + + ret = of_property_read_u32(dev->of_node, "rockchip,core-mask", &dec->core_mask); + if (ret) + return ret; + dev_info(dev, "core_mask=%08x\n", dec->core_mask); + + /* if not the main-core, then attach the main core domain to current */ + if (dec->mpp.core_id != 0) { + struct mpp_taskqueue *queue; + struct mpp_iommu_info *ccu_info, *cur_info; + + queue = dec->mpp.queue; + /* set the ccu-domain for current device */ + ccu_info = queue->cores[0]->iommu_info; + cur_info = dec->mpp.iommu_info; + if (cur_info) + cur_info->domain = ccu_info->domain; + mpp_iommu_attach(cur_info); + } + + dec->ccu = ccu; + + dev_info(dev, "attach ccu as core %d\n", dec->mpp.core_id); + mpp_debug_enter(); + + return 0; +} + +static void rkvdec2_ccu_timeout_work(struct work_struct *work_s) +{ + struct mpp_dev *mpp; + struct mpp_task *task = container_of(to_delayed_work(work_s), + struct mpp_task, timeout_work); + + if (test_and_set_bit(TASK_STATE_HANDLE, &task->state)) { + mpp_err("task %d state %lx has been handled\n", + task->task_id, task->state); + return; + } + + if (!task->session) { + mpp_err("task %d session is null.\n", task->task_id); + return; + } + mpp = mpp_get_task_used_device(task, task->session); + mpp_err("%s, task %d state %#lx timeout\n", dev_name(mpp->dev), + task->task_index, task->state); + set_bit(TASK_STATE_TIMEOUT, &task->state); + atomic_inc(&mpp->reset_request); + atomic_inc(&mpp->queue->reset_request); + kthread_queue_work(&mpp->queue->worker, &mpp->work); +} + +int rkvdec2_ccu_link_init(struct platform_device *pdev, struct rkvdec2_dev *dec) +{ + struct resource *res; + struct rkvdec_link_dev *link_dec; + struct device *dev = &pdev->dev; + + mpp_debug_enter(); + + /* link structure */ + link_dec = devm_kzalloc(dev, sizeof(*link_dec), GFP_KERNEL); + if (!link_dec) + return -ENOMEM; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "link"); + if (!res) + return -ENOMEM; + + link_dec->info = dec->mpp.var->hw_info->link_info; + link_dec->reg_base = devm_ioremap(dev, res->start, resource_size(res)); + if (!link_dec->reg_base) { + dev_err(dev, "ioremap failed for resource %pR\n", res); + return -ENOMEM; + } + + dec->link_dec = link_dec; + + mpp_debug_leave(); + + return 0; +} + +static int rkvdec2_ccu_power_on(struct mpp_taskqueue *queue, + struct rkvdec2_ccu *ccu) +{ + if (!atomic_xchg(&ccu->power_enabled, 1)) { + u32 i; + struct mpp_dev *mpp; + + /* ccu pd and clk on */ + pm_runtime_get_sync(ccu->dev); + pm_stay_awake(ccu->dev); + mpp_clk_safe_enable(ccu->aclk_info.clk); + /* core pd and clk on */ + for (i = 0; i < queue->core_count; i++) { + struct rkvdec2_dev *dec; + + mpp = queue->cores[i]; + dec = to_rkvdec2_dev(mpp); + pm_runtime_get_sync(mpp->dev); + pm_stay_awake(mpp->dev); + if (mpp->hw_ops->clk_on) + mpp->hw_ops->clk_on(mpp); + + mpp_clk_set_rate(&dec->aclk_info, CLK_MODE_NORMAL); + mpp_clk_set_rate(&dec->cabac_clk_info, CLK_MODE_NORMAL); + mpp_clk_set_rate(&dec->hevc_cabac_clk_info, CLK_MODE_NORMAL); + mpp_devfreq_set_core_rate(mpp, CLK_MODE_NORMAL); + mpp_iommu_dev_activate(mpp->iommu_info, mpp); + } + mpp_debug(DEBUG_CCU, "power on\n"); + } + + return 0; +} + +static int rkvdec2_ccu_power_off(struct mpp_taskqueue *queue, + struct rkvdec2_ccu *ccu) +{ + if (atomic_xchg(&ccu->power_enabled, 0)) { + u32 i; + struct mpp_dev *mpp; + + /* ccu pd and clk off */ + mpp_clk_safe_disable(ccu->aclk_info.clk); + pm_relax(ccu->dev); + pm_runtime_mark_last_busy(ccu->dev); + pm_runtime_put_autosuspend(ccu->dev); + /* core pd and clk off */ + for (i = 0; i < queue->core_count; i++) { + mpp = queue->cores[i]; + + if (mpp->hw_ops->clk_off) + mpp->hw_ops->clk_off(mpp); + pm_relax(mpp->dev); + pm_runtime_mark_last_busy(mpp->dev); + pm_runtime_put_autosuspend(mpp->dev); + mpp_iommu_dev_deactivate(mpp->iommu_info, mpp); + } + mpp_debug(DEBUG_CCU, "power off\n"); + } + + return 0; +} + +static int rkvdec2_soft_ccu_dequeue(struct mpp_taskqueue *queue) +{ + struct mpp_task *mpp_task = NULL, *n; + + mpp_debug_enter(); + + list_for_each_entry_safe(mpp_task, n, + &queue->running_list, + queue_link) { + struct mpp_dev *mpp = mpp_get_task_used_device(mpp_task, mpp_task->session); + struct rkvdec2_dev *dec = to_rkvdec2_dev(mpp); + u32 irq_status = mpp->irq_status; + u32 timeout_flag = test_bit(TASK_STATE_TIMEOUT, &mpp_task->state); + u32 abort_flag = test_bit(TASK_STATE_ABORT, &mpp_task->state); + u32 timing_en = mpp->srv->timing_en; + + if (irq_status || timeout_flag || abort_flag) { + struct rkvdec2_task *task = to_rkvdec2_task(mpp_task); + + if (timing_en) { + mpp_task->on_irq = ktime_get(); + set_bit(TASK_TIMING_IRQ, &mpp_task->state); + + mpp_task->on_cancel_timeout = mpp_task->on_irq; + set_bit(TASK_TIMING_TO_CANCEL, &mpp_task->state); + + mpp_task->on_isr = mpp_task->on_irq; + set_bit(TASK_TIMING_ISR, &mpp_task->state); + } + + set_bit(TASK_STATE_HANDLE, &mpp_task->state); + cancel_delayed_work(&mpp_task->timeout_work); + mpp_task->hw_cycles = mpp_read(mpp, RKVDEC_PERF_WORKING_CNT); + mpp_time_diff_with_hw_time(mpp_task, dec->cycle_clk->real_rate_hz); + task->irq_status = irq_status; + mpp_debug(DEBUG_IRQ_CHECK, "irq_status=%08x, timeout=%u, abort=%u\n", + irq_status, timeout_flag, abort_flag); + if (irq_status && mpp->dev_ops->finish) + mpp->dev_ops->finish(mpp, mpp_task); + else + task->reg[RKVDEC_REG_INT_EN_INDEX] = RKVDEC_TIMEOUT_STA; + + set_bit(TASK_STATE_FINISH, &mpp_task->state); + set_bit(TASK_STATE_DONE, &mpp_task->state); + + set_bit(mpp->core_id, &queue->core_idle); + mpp_dbg_core("set core %d idle %lx\n", mpp->core_id, queue->core_idle); + /* Wake up the GET thread */ + wake_up(&mpp_task->wait); + /* free task */ + list_del_init(&mpp_task->queue_link); + kref_put(&mpp_task->ref, mpp_free_task); + } else { + /* NOTE: break when meet not finish */ + break; + } + } + + mpp_debug_leave(); + return 0; +} + +static int rkvdec2_soft_ccu_reset(struct mpp_taskqueue *queue, + struct rkvdec2_ccu *ccu) +{ + int i; + + for (i = queue->core_count - 1; i >= 0; i--) { + u32 val; + + struct mpp_dev *mpp = queue->cores[i]; + struct rkvdec2_dev *dec = to_rkvdec2_dev(mpp); + + if (mpp->disable) + continue; + + dev_info(mpp->dev, "resetting for err %#x\n", mpp->irq_status); + disable_hardirq(mpp->irq); + + /* foce idle, disconnect core and ccu */ + writel(dec->core_mask, ccu->reg_base + RKVDEC_CCU_CORE_IDLE_BASE); + + /* soft reset */ + mpp_write(mpp, RKVDEC_REG_IMPORTANT_BASE, RKVDEC_SOFTREST_EN); + udelay(5); + val = mpp_read(mpp, RKVDEC_REG_INT_EN); + if (!(val & RKVDEC_SOFT_RESET_READY)) + mpp_err("soft reset fail, int %08x\n", val); + mpp_write(mpp, RKVDEC_REG_INT_EN, 0); + + /* check bus idle */ + val = mpp_read(mpp, RKVDEC_REG_DEBUG_INT_BASE); + if (!(val & RKVDEC_BIT_BUS_IDLE)) + mpp_err("bus busy\n"); + + if (IS_REACHABLE(CONFIG_ROCKCHIP_SIP)) { + /* sip reset */ + rockchip_dmcfreq_lock(); + sip_smc_vpu_reset(i, 0, 0); + rockchip_dmcfreq_unlock(); + } else { + rkvdec2_reset(mpp); + } + /* clear error mask */ + writel(dec->core_mask & RKVDEC_CCU_CORE_RW_MASK, + ccu->reg_base + RKVDEC_CCU_CORE_ERR_BASE); + /* connect core and ccu */ + writel(dec->core_mask & RKVDEC_CCU_CORE_RW_MASK, + ccu->reg_base + RKVDEC_CCU_CORE_IDLE_BASE); + mpp_iommu_refresh(mpp->iommu_info, mpp->dev); + atomic_set(&mpp->reset_request, 0); + + enable_irq(mpp->irq); + dev_info(mpp->dev, "reset done\n"); + } + atomic_set(&queue->reset_request, 0); + + return 0; +} + +void *rkvdec2_ccu_alloc_task(struct mpp_session *session, + struct mpp_task_msgs *msgs) +{ + int ret; + struct rkvdec2_task *task; + + task = kzalloc(sizeof(*task), GFP_KERNEL); + if (!task) + return NULL; + + ret = rkvdec2_task_init(session->mpp, session, task, msgs); + if (ret) { + kfree(task); + return NULL; + } + + return &task->mpp_task; +} + +static struct mpp_dev *rkvdec2_ccu_dev_match_by_iommu(struct mpp_taskqueue *queue, + struct device *iommu_dev) +{ + struct mpp_dev *mpp = NULL; + struct rkvdec2_dev *dec = NULL; + u32 mmu[2] = {0, 0x40}; + u32 i; + + for (i = 0; i < queue->core_count; i++) { + struct mpp_dev *core = queue->cores[i]; + + if (&core->iommu_info->pdev->dev == iommu_dev) { + mpp = core; + dec = to_rkvdec2_dev(mpp); + } + } + + if (!dec || !dec->mmu_base) + goto out; + + /* there are two iommus */ + for (i = 0; i < 2; i++) { + u32 status = readl(dec->mmu_base + mmu[i] + 0x4); + u32 iova = readl(dec->mmu_base + mmu[i] + 0xc); + u32 is_write = (status & BIT(5)) ? 1 : 0; + + if (status && iova) + dev_err(iommu_dev, "core %d pagfault at iova %#08x type %s status %#x\n", + mpp->core_id, iova, is_write ? "write" : "read", status); + } +out: + return mpp; +} + +int rkvdec2_soft_ccu_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 mpp_taskqueue *queue = mpp->queue; + struct mpp_task *mpp_task; + + mpp_debug_enter(); + + mpp = rkvdec2_ccu_dev_match_by_iommu(queue, iommu_dev); + if (!mpp) { + dev_err(iommu_dev, "iommu fault, but no dev match\n"); + return 0; + } + mpp_task = mpp->cur_task; + 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); + atomic_inc(&mpp->queue->reset_request); + kthread_queue_work(&mpp->queue->worker, &mpp->work); + + mpp_debug_leave(); + + return 0; +} + +int rkvdec2_hard_ccu_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 mpp_taskqueue *queue = mpp->queue; + struct mpp_task *mpp_task = NULL, *n; + struct rkvdec2_dev *dec; + u32 err_task_iova; + + mpp_debug_enter(); + + mpp = rkvdec2_ccu_dev_match_by_iommu(queue, iommu_dev); + if (!mpp) { + dev_err(iommu_dev, "iommu fault, but no dev match\n"); + return 0; + } + + dec = to_rkvdec2_dev(mpp); + err_task_iova = readl(dec->link_dec->reg_base + 0x4); + dev_err(mpp->dev, "core %d err task iova %#08x\n", mpp->core_id, err_task_iova); + rockchip_iommu_mask_irq(mpp->dev); + + list_for_each_entry_safe(mpp_task, n, &queue->running_list, queue_link) { + struct rkvdec2_task *task = to_rkvdec2_task(mpp_task); + + if ((u32)task->table->iova == err_task_iova) { + mpp_task_dump_mem_region(mpp, mpp_task); + set_bit(TASK_STATE_ABORT, &mpp_task->state); + break; + } + } + atomic_inc(&mpp->queue->reset_request); + kthread_queue_work(&mpp->queue->worker, &mpp->work); + + mpp_debug_leave(); + + return 0; +} + +irqreturn_t rkvdec2_soft_ccu_irq(int irq, void *param) +{ + struct mpp_dev *mpp = param; + u32 irq_status = mpp_read_relaxed(mpp, RKVDEC_REG_INT_EN); + + if (irq_status & RKVDEC_IRQ_RAW) { + mpp_debug(DEBUG_IRQ_STATUS, "irq_status=%08x\n", irq_status); + if (irq_status & RKVDEC_INT_ERROR_MASK) { + atomic_inc(&mpp->reset_request); + atomic_inc(&mpp->queue->reset_request); + } + mpp_write(mpp, RKVDEC_REG_INT_EN, 0); + mpp->irq_status = irq_status; + kthread_queue_work(&mpp->queue->worker, &mpp->work); + return IRQ_HANDLED; + } + return IRQ_NONE; +} + +static inline int rkvdec2_set_core_info(u32 *reg, int idx) +{ + u32 val = (idx << 16) & RKVDEC_REG_FILM_IDX_MASK; + + reg[RKVDEC_REG_CORE_CTRL_INDEX] &= ~RKVDEC_REG_FILM_IDX_MASK; + + reg[RKVDEC_REG_CORE_CTRL_INDEX] |= val; + + return 0; +} + +static int rkvdec2_soft_ccu_enqueue(struct mpp_dev *mpp, struct mpp_task *mpp_task) +{ + u32 i, reg_en, reg; + struct rkvdec2_dev *dec = to_rkvdec2_dev(mpp); + struct rkvdec2_task *task = to_rkvdec2_task(mpp_task); + u32 timing_en = mpp->srv->timing_en; + + mpp_debug_enter(); + + /* set reg for link */ + reg = RKVDEC_LINK_BIT_CORE_WORK_MODE | RKVDEC_LINK_BIT_CCU_WORK_MODE; + writel_relaxed(reg, dec->link_dec->reg_base + RKVDEC_LINK_IRQ_BASE); + + /* set reg for ccu */ + writel_relaxed(RKVDEC_CCU_BIT_WORK_EN, dec->ccu->reg_base + RKVDEC_CCU_WORK_BASE); + writel_relaxed(RKVDEC_CCU_BIT_WORK_MODE, dec->ccu->reg_base + RKVDEC_CCU_WORK_MODE_BASE); + writel_relaxed(dec->core_mask, dec->ccu->reg_base + RKVDEC_CCU_CORE_WORK_BASE); + + /* set cache size */ + reg = RKVDEC_CACHE_PERMIT_CACHEABLE_ACCESS | + RKVDEC_CACHE_PERMIT_READ_ALLOCATE; + if (!mpp_debug_unlikely(DEBUG_CACHE_32B)) + reg |= RKVDEC_CACHE_LINE_SIZE_64_BYTES; + + mpp_write_relaxed(mpp, RKVDEC_REG_CACHE0_SIZE_BASE, reg); + mpp_write_relaxed(mpp, RKVDEC_REG_CACHE1_SIZE_BASE, reg); + mpp_write_relaxed(mpp, RKVDEC_REG_CACHE2_SIZE_BASE, reg); + /* clear cache */ + mpp_write_relaxed(mpp, RKVDEC_REG_CLR_CACHE0_BASE, 1); + mpp_write_relaxed(mpp, RKVDEC_REG_CLR_CACHE1_BASE, 1); + mpp_write_relaxed(mpp, RKVDEC_REG_CLR_CACHE2_BASE, 1); + + mpp_iommu_flush_tlb(mpp->iommu_info); + /* disable multicore pu/colmv offset req timeout reset */ + task->reg[RKVDEC_REG_EN_MODE_SET] |= BIT(1); + task->reg[RKVDEC_REG_TIMEOUT_THRESHOLD] = rkvdec2_ccu_get_timeout_threshold(task); + /* set registers for hardware */ + reg_en = mpp_task->hw_info->reg_en; + for (i = 0; i < task->w_req_cnt; i++) { + int s, e; + struct mpp_request *req = &task->w_reqs[i]; + + s = req->offset / sizeof(u32); + e = s + req->size / sizeof(u32); + mpp_write_req(mpp, task->reg, s, e, reg_en); + } + /* init current task */ + mpp->cur_task = mpp_task; + + mpp_task_run_begin(mpp_task, timing_en, MPP_WORK_TIMEOUT_DELAY); + + mpp->irq_status = 0; + writel_relaxed(dec->core_mask, dec->ccu->reg_base + RKVDEC_CCU_CORE_STA_BASE); + /* Flush the register before the start the device */ + wmb(); + mpp_write(mpp, RKVDEC_REG_START_EN_BASE, task->reg[reg_en] | RKVDEC_START_EN); + + mpp_task_run_end(mpp_task, timing_en); + + mpp_debug_leave(); + + return 0; +} + +static struct mpp_dev *rkvdec2_get_idle_core(struct mpp_taskqueue *queue, + struct mpp_task *mpp_task) +{ + u32 i = 0; + struct rkvdec2_dev *dec = NULL; + + for (i = 0; i < queue->core_count; i++) { + struct mpp_dev *mpp = queue->cores[i]; + struct rkvdec2_dev *core = to_rkvdec2_dev(mpp); + + if (mpp->disable) + continue; + + if (test_bit(i, &queue->core_idle)) { + if (!dec) { + dec = core; + continue; + } + /* set the less work core */ + if (core->task_index < dec->task_index) + dec = core; + } + } + /* if get core */ + if (dec) { + mpp_task->mpp = &dec->mpp; + mpp_task->core_id = dec->mpp.core_id; + clear_bit(mpp_task->core_id, &queue->core_idle); + dec->task_index++; + atomic_inc(&dec->mpp.task_count); + mpp_dbg_core("clear core %d idle\n", mpp_task->core_id); + return mpp_task->mpp; + } + + return NULL; +} + +static bool rkvdec2_core_working(struct mpp_taskqueue *queue) +{ + struct mpp_dev *mpp; + bool flag = false; + u32 i = 0; + + for (i = 0; i < queue->core_count; i++) { + mpp = queue->cores[i]; + if (mpp->disable) + continue; + if (!test_bit(i, &queue->core_idle)) { + flag = true; + break; + } + } + + return flag; +} + +void rkvdec2_soft_ccu_worker(struct kthread_work *work_s) +{ + struct mpp_task *mpp_task; + struct mpp_dev *mpp = container_of(work_s, struct mpp_dev, work); + struct mpp_taskqueue *queue = mpp->queue; + struct rkvdec2_dev *dec = to_rkvdec2_dev(mpp); + u32 timing_en = mpp->srv->timing_en; + + mpp_debug_enter(); + + /* 1. process all finished task in running list */ + rkvdec2_soft_ccu_dequeue(queue); + + /* 2. process reset request */ + if (atomic_read(&queue->reset_request)) { + if (!rkvdec2_core_working(queue)) { + rkvdec2_ccu_power_on(queue, dec->ccu); + rkvdec2_soft_ccu_reset(queue, dec->ccu); + } + } + + /* 3. process pending task */ + while (1) { + if (atomic_read(&queue->reset_request)) + break; + /* get one task form pending list */ + mutex_lock(&queue->pending_lock); + mpp_task = list_first_entry_or_null(&queue->pending_list, + struct mpp_task, queue_link); + mutex_unlock(&queue->pending_lock); + if (!mpp_task) + break; + + if (test_bit(TASK_STATE_ABORT, &mpp_task->state)) { + mutex_lock(&queue->pending_lock); + list_del_init(&mpp_task->queue_link); + + set_bit(TASK_STATE_ABORT_READY, &mpp_task->state); + set_bit(TASK_STATE_PROC_DONE, &mpp_task->state); + + mutex_unlock(&queue->pending_lock); + wake_up(&mpp_task->wait); + kref_put(&mpp_task->ref, rkvdec2_link_free_task); + continue; + } + /* find one core is idle */ + mpp = rkvdec2_get_idle_core(queue, mpp_task); + if (!mpp) + break; + + if (timing_en) { + mpp_task->on_run = ktime_get(); + set_bit(TASK_TIMING_RUN, &mpp_task->state); + } + + /* set session index */ + rkvdec2_set_core_info(mpp_task->reg, mpp_task->session->index); + /* set rcb buffer */ + mpp_set_rcbbuf(mpp, mpp_task->session, mpp_task); + + INIT_DELAYED_WORK(&mpp_task->timeout_work, rkvdec2_ccu_timeout_work); + rkvdec2_ccu_power_on(queue, dec->ccu); + rkvdec2_soft_ccu_enqueue(mpp, mpp_task); + /* pending to running */ + mpp_taskqueue_pending_to_run(queue, mpp_task); + set_bit(TASK_STATE_RUNNING, &mpp_task->state); + } + + /* 4. poweroff when running and pending list are empty */ + if (list_empty(&queue->running_list) && + list_empty(&queue->pending_list)) + rkvdec2_ccu_power_off(queue, dec->ccu); + + /* 5. check session detach out of queue */ + mpp_session_cleanup_detach(queue, work_s); + + mpp_debug_leave(); +} + +int rkvdec2_ccu_alloc_table(struct rkvdec2_dev *dec, + struct rkvdec_link_dev *link_dec) +{ + int ret, i; + struct mpp_dma_buffer *table; + struct mpp_dev *mpp = &dec->mpp; + + mpp_debug_enter(); + + /* alloc table pointer array */ + table = devm_kmalloc_array(mpp->dev, mpp->task_capacity, + sizeof(*table), GFP_KERNEL | __GFP_ZERO); + if (!table) + return -ENOMEM; + + /* alloc table buffer */ + ret = rkvdec2_link_alloc_table(mpp, link_dec); + if (ret) + return ret; + + /* init table array */ + dec->ccu->table_array = table; + for (i = 0; i < mpp->task_capacity; i++) { + table[i].iova = link_dec->table->iova + i * link_dec->link_node_size; + table[i].vaddr = link_dec->table->vaddr + i * link_dec->link_node_size; + table[i].size = link_dec->link_node_size; + INIT_LIST_HEAD(&table[i].link); + list_add_tail(&table[i].link, &dec->ccu->unused_list); + } + + return 0; +} + +static void rkvdec2_dump_ccu(struct rkvdec2_ccu *ccu) +{ + u32 i; + + for (i = 0; i < 10; i++) + mpp_err("ccu:reg[%d]=%08x\n", i, readl(ccu->reg_base + 4 * i)); + + for (i = 16; i < 22; i++) + mpp_err("ccu:reg[%d]=%08x\n", i, readl(ccu->reg_base + 4 * i)); +} + +static void rkvdec2_dump_link(struct rkvdec2_dev *dec) +{ + u32 i; + + for (i = 0; i < 10; i++) + mpp_err("link:reg[%d]=%08x\n", i, readl(dec->link_dec->reg_base + 4 * i)); +} + +static void rkvdec2_dump_core(struct mpp_dev *mpp, struct rkvdec2_task *task) +{ + u32 j; + + if (task) { + for (j = 0; j < 273; j++) + mpp_err("reg[%d]=%08x, %08x\n", j, mpp_read(mpp, j*4), task->reg[j]); + } else { + for (j = 0; j < 273; j++) + mpp_err("reg[%d]=%08x\n", j, mpp_read(mpp, j*4)); + } +} + +irqreturn_t rkvdec2_hard_ccu_irq(int irq, void *param) +{ + u32 irq_status; + struct mpp_dev *mpp = param; + struct rkvdec2_dev *dec = to_rkvdec2_dev(mpp); + + irq_status = readl(dec->link_dec->reg_base + RKVDEC_LINK_IRQ_BASE); + dec->ccu->ccu_core_work_mode = readl(dec->ccu->reg_base + RKVDEC_CCU_CORE_WORK_BASE); + if (irq_status & RKVDEC_LINK_BIT_IRQ_RAW) { + dec->link_dec->irq_status = irq_status; + mpp->irq_status = mpp_read(mpp, RKVDEC_REG_INT_EN); + mpp_debug(DEBUG_IRQ_STATUS, "core %d link_irq=%08x, core_irq=%08x\n", + mpp->core_id, irq_status, mpp->irq_status); + + writel(irq_status & 0xfffff0ff, + dec->link_dec->reg_base + RKVDEC_LINK_IRQ_BASE); + + kthread_queue_work(&mpp->queue->worker, &mpp->work); + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +static int rkvdec2_hard_ccu_finish(struct rkvdec_link_info *hw, struct rkvdec2_task *task) +{ + u32 i, off, s, n; + struct rkvdec_link_part *part = hw->part_r; + u32 *tb_reg = (u32 *)task->table->vaddr; + + mpp_debug_enter(); + + for (i = 0; i < hw->part_r_num; i++) { + off = part[i].tb_reg_off; + s = part[i].reg_start; + n = part[i].reg_num; + memcpy(&task->reg[s], &tb_reg[off], n * sizeof(u32)); + } + /* revert hack for irq status */ + task->reg[RKVDEC_REG_INT_EN_INDEX] = task->irq_status; + + mpp_debug_leave(); + + return 0; +} + +static int rkvdec2_hard_ccu_dequeue(struct mpp_taskqueue *queue, + struct rkvdec2_ccu *ccu, + struct rkvdec_link_info *hw) +{ + struct mpp_task *mpp_task = NULL, *n; + u32 dump_reg = 0; + u32 dequeue_none = 0; + + mpp_debug_enter(); + list_for_each_entry_safe(mpp_task, n, &queue->running_list, queue_link) { + u32 timeout_flag = test_bit(TASK_STATE_TIMEOUT, &mpp_task->state); + u32 abort_flag = test_bit(TASK_STATE_ABORT, &mpp_task->state); + struct rkvdec2_task *task = to_rkvdec2_task(mpp_task); + u32 *tb_reg = (u32 *)task->table->vaddr; + u32 irq_status = tb_reg[hw->tb_reg_int]; + u32 ccu_decoded_num, ccu_total_dec_num; + + ccu_decoded_num = readl(ccu->reg_base + RKVDEC_CCU_DEC_NUM_BASE); + ccu_total_dec_num = readl(ccu->reg_base + RKVDEC_CCU_TOTAL_NUM_BASE); + mpp_debug(DEBUG_IRQ_CHECK, + "session %d task %d w:h[%d %d] err %d irq_status %#x timeout=%u abort=%u iova %08x next %08x ccu[%d %d]\n", + mpp_task->session->index, mpp_task->task_index, task->width, + task->height, !!(irq_status & RKVDEC_INT_ERROR_MASK), irq_status, + timeout_flag, abort_flag, (u32)task->table->iova, + ((u32 *)task->table->vaddr)[hw->tb_reg_next], + ccu_decoded_num, ccu_total_dec_num); + + if (irq_status || timeout_flag || abort_flag) { + struct rkvdec2_dev *dec = to_rkvdec2_dev(queue->cores[0]); + + set_bit(TASK_STATE_HANDLE, &mpp_task->state); + cancel_delayed_work(&mpp_task->timeout_work); + mpp_task->hw_cycles = tb_reg[hw->tb_reg_cycle]; + mpp_time_diff_with_hw_time(mpp_task, dec->cycle_clk->real_rate_hz); + task->irq_status = irq_status ? irq_status : RKVDEC_ERROR_STA; + + if (irq_status) + rkvdec2_hard_ccu_finish(hw, task); + + set_bit(TASK_STATE_FINISH, &mpp_task->state); + set_bit(TASK_STATE_DONE, &mpp_task->state); + + if (timeout_flag && !dump_reg && mpp_debug_unlikely(DEBUG_DUMP_ERR_REG)) { + u32 i; + + mpp_err("###### ccu #####\n"); + rkvdec2_dump_ccu(ccu); + for (i = 0; i < queue->core_count; i++) { + mpp_err("###### core %d #####\n", i); + rkvdec2_dump_link(to_rkvdec2_dev(queue->cores[i])); + rkvdec2_dump_core(queue->cores[i], task); + } + dump_reg = 1; + } + list_move_tail(&task->table->link, &ccu->unused_list); + /* free task */ + list_del_init(&mpp_task->queue_link); + /* Wake up the GET thread */ + wake_up(&mpp_task->wait); + if ((irq_status & RKVDEC_INT_ERROR_MASK) || timeout_flag) { + pr_err("session %d task %d irq_status %#x timeout=%u abort=%u\n", + mpp_task->session->index, mpp_task->task_index, + irq_status, timeout_flag, abort_flag); + atomic_inc(&queue->reset_request); + } + + kref_put(&mpp_task->ref, mpp_free_task); + } else { + dequeue_none++; + /* + * there are only 2 cores, + * if dequeue not finish task more than 2, + * means the others task still not get run by hw, can break early. + */ + if (dequeue_none > 2) + break; + } + } + + mpp_debug_leave(); + return 0; +} + +static int rkvdec2_hard_ccu_reset(struct mpp_taskqueue *queue, struct rkvdec2_ccu *ccu) +{ + int i = 0; + + mpp_debug_enter(); + + /* reset and active core */ + for (i = 0; i < queue->core_count; i++) { + u32 val = 0; + struct mpp_dev *mpp = queue->cores[i]; + struct rkvdec2_dev *dec = to_rkvdec2_dev(mpp); + + if (mpp->disable) + continue; + dev_info(mpp->dev, "resetting...\n"); + disable_hardirq(mpp->irq); + /* force idle */ + writel(dec->core_mask, ccu->reg_base + RKVDEC_CCU_CORE_IDLE_BASE); + writel(0, ccu->reg_base + RKVDEC_CCU_WORK_BASE); + + { + /* soft reset */ + u32 val; + + mpp_write(mpp, RKVDEC_REG_IMPORTANT_BASE, RKVDEC_SOFTREST_EN); + udelay(5); + val = mpp_read(mpp, RKVDEC_REG_INT_EN); + if (!(val & RKVDEC_SOFT_RESET_READY)) + mpp_err("soft reset fail, int %08x\n", val); + + // /* cru reset */ + // dev_info(mpp->dev, "cru reset\n"); + // rkvdec2_reset(mpp); + } +#if IS_ENABLED(CONFIG_ROCKCHIP_SIP) + rockchip_dmcfreq_lock(); + sip_smc_vpu_reset(i, 0, 0); + rockchip_dmcfreq_unlock(); +#else + rkvdec2_reset(mpp); +#endif + mpp_iommu_refresh(mpp->iommu_info, mpp->dev); + enable_irq(mpp->irq); + atomic_set(&mpp->reset_request, 0); + val = mpp_read_relaxed(mpp, 272*4); + dev_info(mpp->dev, "reset done, idle %d\n", (val & 1)); + } + /* reset ccu */ + mpp_safe_reset(ccu->rst_a); + udelay(5); + mpp_safe_unreset(ccu->rst_a); + + mpp_debug_leave(); + return 0; +} + +static struct mpp_task * +rkvdec2_hard_ccu_prepare(struct mpp_task *mpp_task, + struct rkvdec2_ccu *ccu, struct rkvdec_link_info *hw) +{ + u32 i, off, s, n; + u32 *tb_reg; + struct mpp_dma_buffer *table = NULL; + struct rkvdec_link_part *part; + struct rkvdec2_task *task = to_rkvdec2_task(mpp_task); + + mpp_debug_enter(); + + if (test_bit(TASK_STATE_PREPARE, &mpp_task->state)) + return mpp_task; + + /* ensure that cur table iova points to the next link table*/ + { + struct mpp_dma_buffer *table0 = NULL, *table1 = NULL, *n; + + list_for_each_entry_safe(table, n, &ccu->unused_list, link) { + if (!table0) { + table0 = table; + continue; + } + if (!table1) + table1 = table; + break; + } + if (!table0 || !table1) + return NULL; + ((u32 *)table0->vaddr)[hw->tb_reg_next] = table1->iova; + table = table0; + } + + /* set session idx */ + rkvdec2_set_core_info(task->reg, mpp_task->session->index); + tb_reg = (u32 *)table->vaddr; + part = hw->part_w; + + /* disable multicore pu/colmv offset req timeout reset */ + task->reg[RKVDEC_REG_EN_MODE_SET] |= BIT(1); + task->reg[RKVDEC_REG_TIMEOUT_THRESHOLD] = rkvdec2_ccu_get_timeout_threshold(task); + + for (i = 0; i < hw->part_w_num; i++) { + off = part[i].tb_reg_off; + s = part[i].reg_start; + n = part[i].reg_num; + memcpy(&tb_reg[off], &task->reg[s], n * sizeof(u32)); + } + + /* memset read registers */ + part = hw->part_r; + for (i = 0; i < hw->part_r_num; i++) { + off = part[i].tb_reg_off; + n = part[i].reg_num; + memset(&tb_reg[off], 0, n * sizeof(u32)); + } + list_move_tail(&table->link, &ccu->used_list); + task->table = table; + set_bit(TASK_STATE_PREPARE, &mpp_task->state); + mpp_dbg_ccu("session %d task %d iova %08x next %08x\n", + mpp_task->session->index, mpp_task->task_index, (u32)task->table->iova, + ((u32 *)task->table->vaddr)[hw->tb_reg_next]); + + mpp_debug_leave(); + + return mpp_task; +} + +static int rkvdec2_ccu_link_fix_rcb_regs(struct rkvdec2_dev *dec) +{ + int ret = 0; + u32 i, val; + u32 reg, reg_idx, rcb_size, rcb_offset; + + if (!dec->rcb_iova && !dec->rcb_info_count) + goto done; + /* check whether fixed */ + val = readl(dec->link_dec->reg_base + RKVDEC_LINK_IRQ_BASE); + if (val & RKVDEC_CCU_BIT_FIX_RCB) + goto done; + /* set registers */ + rcb_offset = 0; + for (i = 0; i < dec->rcb_info_count; i += 2) { + reg_idx = dec->rcb_infos[i]; + rcb_size = dec->rcb_infos[i + 1]; + mpp_debug(DEBUG_SRAM_INFO, + "rcb: reg %u size %u offset %u sram_size %u rcb_size %u\n", + reg_idx, rcb_size, rcb_offset, dec->sram_size, dec->rcb_size); + if ((rcb_offset + rcb_size) > dec->rcb_size) { + mpp_err("rcb: reg[%u] set failed.\n", reg_idx); + ret = -ENOMEM; + goto done; + } + reg = dec->rcb_iova + rcb_offset; + mpp_write(&dec->mpp, reg_idx * sizeof(u32), reg); + rcb_offset += rcb_size; + } + + val |= RKVDEC_CCU_BIT_FIX_RCB; + writel(val, dec->link_dec->reg_base + RKVDEC_LINK_IRQ_BASE); +done: + return ret; +} + +static int rkvdec2_hard_ccu_enqueue(struct rkvdec2_ccu *ccu, + struct mpp_task *mpp_task, + struct mpp_taskqueue *queue, + struct mpp_dev *mpp) +{ + u32 ccu_en, work_mode, link_mode; + struct rkvdec2_task *task = to_rkvdec2_task(mpp_task); + u32 timing_en = mpp->srv->timing_en; + + mpp_debug_enter(); + + if (test_bit(TASK_STATE_START, &mpp_task->state)) + goto done; + + ccu_en = readl(ccu->reg_base + RKVDEC_CCU_WORK_BASE); + mpp_dbg_ccu("ccu_en=%d\n", ccu_en); + if (!ccu_en) { + u32 i; + + /* set work mode */ + work_mode = 0; + for (i = 0; i < queue->core_count; i++) { + u32 val; + struct mpp_dev *core = queue->cores[i]; + struct rkvdec2_dev *dec = to_rkvdec2_dev(core); + + if (mpp->disable) + continue; + work_mode |= dec->core_mask; + rkvdec2_ccu_link_fix_rcb_regs(dec); + /* control by ccu */ + val = readl(dec->link_dec->reg_base + RKVDEC_LINK_IRQ_BASE); + val |= RKVDEC_LINK_BIT_CCU_WORK_MODE; + writel(val, dec->link_dec->reg_base + RKVDEC_LINK_IRQ_BASE); + } + writel(work_mode, ccu->reg_base + RKVDEC_CCU_CORE_WORK_BASE); + ccu->ccu_core_work_mode = readl(ccu->reg_base + RKVDEC_CCU_CORE_WORK_BASE); + mpp_dbg_ccu("ccu_work_mode=%08x, ccu_work_status=%08x\n", + readl(ccu->reg_base + RKVDEC_CCU_CORE_WORK_BASE), + readl(ccu->reg_base + RKVDEC_CCU_CORE_STA_BASE)); + + /* set auto gating */ + writel(RKVDEC_CCU_BIT_AUTOGATE, ccu->reg_base + RKVDEC_CCU_CTRL_BASE); + /* link start base */ + writel(task->table->iova, ccu->reg_base + RKVDEC_CCU_CFG_ADDR_BASE); + /* enable link */ + writel(RKVDEC_CCU_BIT_WORK_EN, ccu->reg_base + RKVDEC_CCU_WORK_BASE); + } + + /* set link mode */ + link_mode = ccu_en ? RKVDEC_CCU_BIT_ADD_MODE : 0; + writel(link_mode | RKVDEC_LINK_ADD_CFG_NUM, ccu->reg_base + RKVDEC_CCU_LINK_MODE_BASE); + + /* flush tlb before starting hardware */ + mpp_iommu_flush_tlb(mpp->iommu_info); + /* wmb */ + wmb(); + INIT_DELAYED_WORK(&mpp_task->timeout_work, rkvdec2_ccu_timeout_work); + mpp_task_run_begin(mpp_task, timing_en, MPP_WORK_TIMEOUT_DELAY); + /* configure done */ + writel(RKVDEC_CCU_BIT_CFG_DONE, ccu->reg_base + RKVDEC_CCU_CFG_DONE_BASE); + mpp_task_run_end(mpp_task, timing_en); + + set_bit(TASK_STATE_RUNNING, &mpp_task->state); + mpp_dbg_ccu("session %d task %d iova=%08x task->state=%lx link_mode=%08x\n", + mpp_task->session->index, mpp_task->task_index, + (u32)task->table->iova, mpp_task->state, + readl(ccu->reg_base + RKVDEC_CCU_LINK_MODE_BASE)); +done: + mpp_debug_leave(); + + return 0; +} + +static void rkvdec2_hard_ccu_resend_tasks(struct mpp_dev *mpp, struct mpp_taskqueue *queue) +{ + struct rkvdec2_task *task_pre = NULL; + struct mpp_task *loop = NULL, *n; + struct rkvdec2_dev *dec = to_rkvdec2_dev(mpp); + + /* re sort running list */ + list_for_each_entry_safe(loop, n, &queue->running_list, queue_link) { + struct rkvdec2_task *task = to_rkvdec2_task(loop); + u32 *tb_reg = (u32 *)task->table->vaddr; + u32 irq_status = tb_reg[dec->link_dec->info->tb_reg_int]; + + if (!irq_status) { + if (task_pre) { + tb_reg = (u32 *)task_pre->table->vaddr; + tb_reg[dec->link_dec->info->tb_reg_next] = task->table->iova; + } + task_pre = task; + } + } + + if (task_pre) { + struct mpp_dma_buffer *tbl; + u32 *tb_reg; + + tbl = list_first_entry_or_null(&dec->ccu->unused_list, + struct mpp_dma_buffer, link); + WARN_ON(!tbl); + if (tbl) { + tb_reg = (u32 *)task_pre->table->vaddr; + tb_reg[dec->link_dec->info->tb_reg_next] = tbl->iova; + } + } + + /* resend */ + list_for_each_entry_safe(loop, n, &queue->running_list, queue_link) { + struct rkvdec2_task *task = to_rkvdec2_task(loop); + u32 *tb_reg = (u32 *)task->table->vaddr; + u32 irq_status = tb_reg[dec->link_dec->info->tb_reg_int]; + + mpp_dbg_ccu("reback: session %d task %d iova %08x next %08x irq_status 0x%08x\n", + loop->session->index, loop->task_index, (u32)task->table->iova, + tb_reg[dec->link_dec->info->tb_reg_next], irq_status); + + if (!irq_status) { + cancel_delayed_work(&loop->timeout_work); + clear_bit(TASK_STATE_START, &loop->state); + rkvdec2_hard_ccu_enqueue(dec->ccu, loop, queue, mpp); + } + } +} + +void rkvdec2_hard_ccu_worker(struct kthread_work *work_s) +{ + struct mpp_task *mpp_task; + struct mpp_dev *mpp = container_of(work_s, struct mpp_dev, work); + struct mpp_taskqueue *queue = mpp->queue; + struct rkvdec2_dev *dec = to_rkvdec2_dev(mpp); + + mpp_debug_enter(); + + /* 1. process all finished task in running list */ + rkvdec2_hard_ccu_dequeue(queue, dec->ccu, dec->link_dec->info); + + /* 2. process reset request */ + if (atomic_read(&queue->reset_request) && + (list_empty(&queue->running_list) || !dec->ccu->ccu_core_work_mode)) { + /* + * cancel running list timeout work to avoid + * sw timeout causeby reset long time + */ + struct mpp_task *loop = NULL, *n; + + list_for_each_entry_safe(loop, n, &queue->running_list, queue_link) { + cancel_delayed_work(&loop->timeout_work); + } + /* reset process */ + rkvdec2_hard_ccu_reset(queue, dec->ccu); + atomic_set(&queue->reset_request, 0); + + /* relink running task iova in list, and resend them to hw */ + if (!list_empty(&queue->running_list)) + rkvdec2_hard_ccu_resend_tasks(mpp, queue); + } + + /* 3. process pending task */ + while (1) { + if (atomic_read(&queue->reset_request)) + break; + + /* get one task form pending list */ + mutex_lock(&queue->pending_lock); + mpp_task = list_first_entry_or_null(&queue->pending_list, + struct mpp_task, queue_link); + mutex_unlock(&queue->pending_lock); + + if (!mpp_task) + break; + if (test_bit(TASK_STATE_ABORT, &mpp_task->state)) { + mutex_lock(&queue->pending_lock); + list_del_init(&mpp_task->queue_link); + mutex_unlock(&queue->pending_lock); + kref_put(&mpp_task->ref, mpp_free_task); + continue; + } + + mpp_task = rkvdec2_hard_ccu_prepare(mpp_task, dec->ccu, dec->link_dec->info); + if (!mpp_task) + break; + + rkvdec2_ccu_power_on(queue, dec->ccu); + rkvdec2_hard_ccu_enqueue(dec->ccu, mpp_task, queue, mpp); + mpp_taskqueue_pending_to_run(queue, mpp_task); + } + + /* 4. poweroff when running and pending list are empty */ + mutex_lock(&queue->pending_lock); + if (list_empty(&queue->running_list) && + list_empty(&queue->pending_list)) + rkvdec2_ccu_power_off(queue, dec->ccu); + mutex_unlock(&queue->pending_lock); + + /* 5. check session detach out of queue */ + mpp_session_cleanup_detach(queue, work_s); + + mpp_debug_leave(); +} -- Gitblit v1.6.2