From 2f7c68cb55ecb7331f2381deb497c27155f32faf Mon Sep 17 00:00:00 2001 From: hc <hc@nodka.com> Date: Wed, 03 Jan 2024 09:43:39 +0000 Subject: [PATCH] update kernel to 5.10.198 --- kernel/drivers/video/rockchip/mpp/mpp_vepu2.c | 442 +++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 files changed, 415 insertions(+), 27 deletions(-) diff --git a/kernel/drivers/video/rockchip/mpp/mpp_vepu2.c b/kernel/drivers/video/rockchip/mpp/mpp_vepu2.c index 75188f2..fb5f5e5 100644 --- a/kernel/drivers/video/rockchip/mpp/mpp_vepu2.c +++ b/kernel/drivers/video/rockchip/mpp/mpp_vepu2.c @@ -22,6 +22,7 @@ #include <linux/proc_fs.h> #include <linux/nospec.h> #include <soc/rockchip/pm_domains.h> +#include <soc/rockchip/rockchip_iommu.h> #include "mpp_debug.h" #include "mpp_common.h" @@ -36,6 +37,8 @@ #define VEPU2_REG_HW_ID_INDEX -1 /* INVALID */ #define VEPU2_REG_START_INDEX 0 #define VEPU2_REG_END_INDEX 183 +#define VEPU2_REG_OUT_INDEX (77) +#define VEPU2_REG_STRM_INDEX (53) #define VEPU2_REG_ENC_EN 0x19c #define VEPU2_REG_ENC_EN_INDEX (103) @@ -97,6 +100,8 @@ u32 width; u32 height; u32 pixels; + struct mpp_dma_buffer *bs_buf; + u32 offset_bs; }; struct vepu_session_priv { @@ -121,6 +126,18 @@ #endif struct reset_control *rst_a; struct reset_control *rst_h; + /* for ccu(central control unit) */ + struct vepu_ccu *ccu; + bool disable_work; +}; + +struct vepu_ccu { + u32 core_num; + /* lock for core attach */ + spinlock_t lock; + struct mpp_dev *main_core; + struct mpp_dev *cores[MPP_MAX_CORE_NUM]; + unsigned long core_idle; }; static struct mpp_hw_info vepu_v2_hw_info = { @@ -167,7 +184,13 @@ struct mpp_task_msgs *msgs) { int ret; + int fd_bs; int fmt = VEPU2_GET_FORMAT(task->reg[VEPU2_REG_ENC_EN_INDEX]); + + if (session->msg_flags & MPP_FLAGS_REG_NO_OFFSET) + fd_bs = task->reg[VEPU2_REG_OUT_INDEX]; + else + fd_bs = task->reg[VEPU2_REG_OUT_INDEX] & 0x3ff; ret = mpp_translate_reg_address(session, &task->mpp_task, fmt, task->reg, &task->off_inf); @@ -176,6 +199,15 @@ mpp_translate_reg_offset_info(&task->mpp_task, &task->off_inf, task->reg); + + if (fmt == VEPU2_FMT_JPEGE) { + struct mpp_dma_buffer *bs_buf = mpp_dma_find_buffer_fd(session->dma, fd_bs); + + task->offset_bs = mpp_query_reg_offset_info(&task->off_inf, VEPU2_REG_OUT_INDEX); + 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; + } return 0; } @@ -281,12 +313,54 @@ return NULL; } +static void *vepu_prepare(struct mpp_dev *mpp, struct mpp_task *mpp_task) +{ + struct vepu_dev *enc = to_vepu_dev(mpp); + struct vepu_ccu *ccu = enc->ccu; + unsigned long core_idle; + unsigned long flags; + s32 core_id; + u32 i; + + spin_lock_irqsave(&ccu->lock, flags); + + core_idle = ccu->core_idle; + + for (i = 0; i < ccu->core_num; i++) { + struct mpp_dev *mpp = ccu->cores[i]; + + if (mpp && mpp->disable) + clear_bit(mpp->core_id, &core_idle); + } + + core_id = find_first_bit(&core_idle, ccu->core_num); + if (core_id >= ARRAY_SIZE(ccu->cores)) { + mpp_task = NULL; + mpp_dbg_core("core %d all busy %lx\n", core_id, ccu->core_idle); + goto done; + } + + core_id = array_index_nospec(core_id, MPP_MAX_CORE_NUM); + clear_bit(core_id, &ccu->core_idle); + mpp_task->mpp = ccu->cores[core_id]; + mpp_task->core_id = core_id; + + mpp_dbg_core("core cnt %d core %d set idle %lx -> %lx\n", + ccu->core_num, core_id, core_idle, ccu->core_idle); + +done: + spin_unlock_irqrestore(&ccu->lock, flags); + + return mpp_task; +} + static int vepu_run(struct mpp_dev *mpp, struct mpp_task *mpp_task) { u32 i; u32 reg_en; struct vepu_task *task = to_vepu_task(mpp_task); + u32 timing_en = mpp->srv->timing_en; mpp_debug_enter(); @@ -305,16 +379,32 @@ mpp_write_req(mpp, task->reg, s, e, reg_en); } + + /* flush tlb before starting hardware */ + mpp_iommu_flush_tlb(mpp->iommu_info); + /* init current task */ mpp->cur_task = mpp_task; + + mpp_task_run_begin(mpp_task, timing_en, MPP_WORK_TIMEOUT_DELAY); + /* Last, flush the registers */ wmb(); mpp_write(mpp, VEPU2_REG_ENC_EN, task->reg[reg_en] | VEPU2_ENC_START); + mpp_task_run_end(mpp_task, timing_en); + mpp_debug_leave(); return 0; +} + +static int vepu_px30_run(struct mpp_dev *mpp, + struct mpp_task *mpp_task) +{ + mpp_iommu_flush_tlb(mpp->iommu_info); + return vepu_run(mpp, mpp_task); } static int vepu_irq(struct mpp_dev *mpp) @@ -333,6 +423,9 @@ u32 err_mask; struct vepu_task *task = NULL; struct mpp_task *mpp_task = mpp->cur_task; + unsigned long core_idle; + struct vepu_dev *enc = to_vepu_dev(mpp); + struct vepu_ccu *ccu = enc->ccu; /* FIXME use a spin lock here */ if (!mpp_task) { @@ -354,6 +447,14 @@ atomic_inc(&mpp->reset_request); mpp_task_finish(mpp_task->session, mpp_task); + /* the whole vepu has no ccu that manage multi core */ + if (ccu) { + core_idle = ccu->core_idle; + set_bit(mpp->core_id, &ccu->core_idle); + + mpp_dbg_core("core %d isr idle %lx -> %lx\n", mpp->core_id, core_idle, + ccu->core_idle); + } mpp_debug_leave(); @@ -380,6 +481,11 @@ /* revert hack for irq status */ task->reg[VEPU2_REG_INT_INDEX] = task->irq_status; + if (task->bs_buf) + mpp_dma_buf_sync(task->bs_buf, 0, + task->reg[VEPU2_REG_STRM_INDEX] / 8 + + task->offset_bs, + DMA_FROM_DEVICE, true); mpp_debug_leave(); return 0; @@ -522,7 +628,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; @@ -555,8 +661,9 @@ mutex_lock(&mpp->srv->session_lock); list_for_each_entry_safe(session, n, &mpp->srv->session_list, - session_link) { - if (session->device_type != MPP_DEVICE_VEPU2) + service_link) { + if (session->device_type != MPP_DEVICE_VEPU2 && + session->device_type != MPP_DEVICE_VEPU2_JPEG) continue; if (!session->priv) continue; @@ -571,13 +678,28 @@ static int vepu_procfs_init(struct mpp_dev *mpp) { struct vepu_dev *enc = to_vepu_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; + if (enc->ccu) + snprintf(name, sizeof(name) - 1, "%s%d", + mpp->dev->of_node->name, mpp->core_id); + else + snprintf(name, sizeof(name) - 1, "%s", + mpp->dev->of_node->name); + + 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); + mpp_procfs_create_u32("aclk", 0644, enc->procfs, &enc->aclk_info.debug_rate_hz); mpp_procfs_create_u32("session_buffers", 0644, @@ -588,6 +710,17 @@ return 0; } + +static int vepu_procfs_ccu_init(struct mpp_dev *mpp) +{ + struct vepu_dev *enc = to_vepu_dev(mpp); + + if (!enc->procfs) + goto done; + +done: + return 0; +} #else static inline int vepu_procfs_remove(struct mpp_dev *mpp) { @@ -595,6 +728,11 @@ } static inline int vepu_procfs_init(struct mpp_dev *mpp) +{ + return 0; +} + +static inline int vepu_procfs_ccu_init(struct mpp_dev *mpp) { return 0; } @@ -721,18 +859,68 @@ static int vepu_reset(struct mpp_dev *mpp) { struct vepu_dev *enc = to_vepu_dev(mpp); + struct vepu_ccu *ccu = enc->ccu; + mpp_write(mpp, VEPU2_REG_ENC_EN, 0); + udelay(5); if (enc->rst_a && enc->rst_h) { /* Don't skip this or iommu won't work after reset */ - rockchip_pmu_idle_request(mpp->dev, true); + mpp_pmu_idle_request(mpp, true); mpp_safe_reset(enc->rst_a); mpp_safe_reset(enc->rst_h); udelay(5); mpp_safe_unreset(enc->rst_a); mpp_safe_unreset(enc->rst_h); - rockchip_pmu_idle_request(mpp->dev, false); + mpp_pmu_idle_request(mpp, false); } mpp_write(mpp, VEPU2_REG_INT, VEPU2_INT_CLEAR); + + if (ccu) { + set_bit(mpp->core_id, &ccu->core_idle); + mpp_dbg_core("core %d reset idle %lx\n", mpp->core_id, ccu->core_idle); + } + + return 0; +} + +static int vepu2_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_task *mpp_task; + struct vepu_dev *enc = to_vepu_dev(mpp); + struct vepu_ccu *ccu = enc->ccu; + + dev_err(iommu_dev, "fault addr 0x%08lx status %x arg %p\n", + iova, status, arg); + + if (ccu) { + int i; + struct mpp_dev *core; + + for (i = 0; i < ccu->core_num; i++) { + core = ccu->cores[i]; + if (core->iommu_info && (&core->iommu_info->pdev->dev == iommu_dev)) { + mpp = core; + break; + } + } + } + + if (!mpp) { + dev_err(iommu_dev, "pagefault without device to handle\n"); + return 0; + } + mpp_task = mpp->cur_task; + if (mpp_task) + mpp_task_dump_mem_region(mpp, mpp_task); + + 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); return 0; } @@ -771,6 +959,36 @@ .dump_session = vepu_dump_session, }; +static struct mpp_dev_ops vepu_px30_dev_ops = { + .alloc_task = vepu_alloc_task, + .run = vepu_px30_run, + .irq = vepu_irq, + .isr = vepu_isr, + .finish = vepu_finish, + .result = vepu_result, + .free_task = vepu_free_task, + .ioctl = vepu_control, + .init_session = vepu_init_session, + .free_session = vepu_free_session, + .dump_session = vepu_dump_session, +}; + +static struct mpp_dev_ops vepu_ccu_dev_ops = { + .alloc_task = vepu_alloc_task, + .prepare = vepu_prepare, + .run = vepu_run, + .irq = vepu_irq, + .isr = vepu_isr, + .finish = vepu_finish, + .result = vepu_result, + .free_task = vepu_free_task, + .ioctl = vepu_control, + .init_session = vepu_init_session, + .free_session = vepu_free_session, + .dump_session = vepu_dump_session, +}; + + static const struct mpp_dev_var vepu_v2_data = { .device_type = MPP_DEVICE_VEPU2, .hw_info = &vepu_v2_hw_info, @@ -784,7 +1002,15 @@ .hw_info = &vepu_v2_hw_info, .trans_info = trans_rk_vepu2, .hw_ops = &vepu_px30_hw_ops, - .dev_ops = &vepu_v2_dev_ops, + .dev_ops = &vepu_px30_dev_ops, +}; + +static const struct mpp_dev_var vepu_ccu_data = { + .device_type = MPP_DEVICE_VEPU2_JPEG, + .hw_info = &vepu_v2_hw_info, + .trans_info = trans_rk_vepu2, + .hw_ops = &vepu_v2_hw_ops, + .dev_ops = &vepu_ccu_dev_ops, }; static const struct of_device_id mpp_vepu2_dt_match[] = { @@ -798,10 +1024,83 @@ .data = &vepu_px30_data, }, #endif +#ifdef CONFIG_CPU_RK3588 + { + .compatible = "rockchip,vpu-jpege-core", + .data = &vepu_ccu_data, + }, + { + .compatible = "rockchip,vpu-jpege-ccu", + }, +#endif {}, }; -static int vepu_probe(struct platform_device *pdev) +static int vepu_ccu_probe(struct platform_device *pdev) +{ + struct vepu_ccu *ccu; + struct device *dev = &pdev->dev; + + ccu = devm_kzalloc(dev, sizeof(*ccu), GFP_KERNEL); + if (!ccu) + return -ENOMEM; + + platform_set_drvdata(pdev, ccu); + spin_lock_init(&ccu->lock); + return 0; +} + +static int vepu_attach_ccu(struct device *dev, struct vepu_dev *enc) +{ + struct device_node *np; + struct platform_device *pdev; + struct vepu_ccu *ccu; + unsigned long flags; + + 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; + + spin_lock_irqsave(&ccu->lock, flags); + ccu->core_num++; + ccu->cores[enc->mpp.core_id] = &enc->mpp; + set_bit(enc->mpp.core_id, &ccu->core_idle); + spin_unlock_irqrestore(&ccu->lock, flags); + + /* 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; + mpp_iommu_attach(cur_info); + } + enc->ccu = ccu; + + dev_info(dev, "attach ccu success\n"); + return 0; +} + +static int vepu_core_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct vepu_dev *enc = NULL; @@ -809,18 +1108,73 @@ const struct of_device_id *match = NULL; int ret = 0; - dev_info(dev, "probe device\n"); enc = devm_kzalloc(dev, sizeof(struct vepu_dev), GFP_KERNEL); if (!enc) 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_vepu2_dt_match, pdev->dev.of_node); if (match) mpp->var = (struct mpp_dev_var *)match->data; + + mpp->core_id = of_alias_get_id(pdev->dev.of_node, "jpege"); + } + + ret = mpp_dev_probe(mpp, pdev); + if (ret) { + dev_err(dev, "probe sub driver failed\n"); + return -EINVAL; + } + /* current device attach to ccu */ + ret = vepu_attach_ccu(dev, enc); + if (ret) + return ret; + + ret = devm_request_threaded_irq(dev, mpp->irq, + mpp_dev_irq, + mpp_dev_isr_sched, + IRQF_SHARED, + dev_name(dev), mpp); + if (ret) { + dev_err(dev, "register interrupter runtime failed\n"); + return -EINVAL; + } + + mpp->fault_handler = vepu2_iommu_fault_handle; + mpp->session_max_buffers = VEPU2_SESSION_MAX_BUFFERS; + vepu_procfs_init(mpp); + vepu_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 vepu_probe_default(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct vepu_dev *enc = NULL; + struct mpp_dev *mpp = NULL; + const struct of_device_id *match = NULL; + int ret = 0; + + enc = devm_kzalloc(dev, sizeof(struct vepu_dev), GFP_KERNEL); + if (!enc) + return -ENOMEM; + + mpp = &enc->mpp; + platform_set_drvdata(pdev, mpp); + + if (pdev->dev.of_node) { + match = of_match_node(mpp_vepu2_dt_match, pdev->dev.of_node); + if (match) + mpp->var = (struct mpp_dev_var *)match->data; + + mpp->core_id = of_alias_get_id(pdev->dev.of_node, "vepu"); } ret = mpp_dev_probe(mpp, pdev); @@ -839,43 +1193,77 @@ return -EINVAL; } + mpp->fault_handler = vepu2_iommu_fault_handle; mpp->session_max_buffers = VEPU2_SESSION_MAX_BUFFERS; vepu_procfs_init(mpp); /* register current device to mpp service */ mpp_dev_register_srv(mpp, mpp->srv); - dev_info(dev, "probing finish\n"); return 0; +} + +static int vepu_probe(struct platform_device *pdev) +{ + int ret; + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + + dev_info(dev, "probing start\n"); + + if (strstr(np->name, "ccu")) + ret = vepu_ccu_probe(pdev); + else if (strstr(np->name, "core")) + ret = vepu_core_probe(pdev); + else + ret = vepu_probe_default(pdev); + + dev_info(dev, "probing finish\n"); + + return ret; } static int vepu_remove(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct vepu_dev *enc = platform_get_drvdata(pdev); + struct device_node *np = dev->of_node; - dev_info(dev, "remove device\n"); - mpp_dev_remove(&enc->mpp); - vepu_procfs_remove(&enc->mpp); + if (strstr(np->name, "ccu")) { + dev_info(dev, "remove ccu device\n"); + } else if (strstr(np->name, "core")) { + struct mpp_dev *mpp = dev_get_drvdata(dev); + struct vepu_dev *enc = to_vepu_dev(mpp); + + dev_info(dev, "remove core\n"); + if (enc->ccu) { + s32 core_id = mpp->core_id; + struct vepu_ccu *ccu = enc->ccu; + unsigned long flags; + + spin_lock_irqsave(&ccu->lock, flags); + ccu->core_num--; + ccu->cores[core_id] = NULL; + clear_bit(core_id, &ccu->core_idle); + spin_unlock_irqrestore(&ccu->lock, flags); + } + mpp_dev_remove(&enc->mpp); + vepu_procfs_remove(&enc->mpp); + } else { + struct mpp_dev *mpp = dev_get_drvdata(dev); + + dev_info(dev, "remove device\n"); + mpp_dev_remove(mpp); + vepu_procfs_remove(mpp); + } return 0; } static void vepu_shutdown(struct platform_device *pdev) { - int ret; - int val; struct device *dev = &pdev->dev; - struct vepu_dev *enc = platform_get_drvdata(pdev); - struct mpp_dev *mpp = &enc->mpp; - dev_info(dev, "shutdown device\n"); - - atomic_inc(&mpp->srv->shutdown_request); - ret = readx_poll_timeout(atomic_read, - &mpp->task_count, - val, val == 0, 20000, 200000); - if (ret == -ETIMEDOUT) - dev_err(dev, "wait total running time out\n"); + if (!strstr(dev_name(dev), "ccu")) + mpp_dev_shutdown(pdev); } struct platform_driver rockchip_vepu2_driver = { -- Gitblit v1.6.2