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_vepu2.c | 422 +++++++++++++++++++++++++++++++++++++++++++++++++--- 1 files changed, 397 insertions(+), 25 deletions(-) diff --git a/kernel/drivers/video/rockchip/mpp/mpp_vepu2.c b/kernel/drivers/video/rockchip/mpp/mpp_vepu2.c index d7dd2c1..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,6 +313,47 @@ 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) { @@ -327,6 +400,13 @@ 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) { mpp->irq_status = mpp_read(mpp, VEPU2_REG_INT); @@ -343,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) { @@ -364,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(); @@ -390,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; @@ -532,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; @@ -565,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; @@ -581,8 +678,19 @@ 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; @@ -602,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) { @@ -609,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; } @@ -735,6 +859,7 @@ 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); @@ -749,6 +874,53 @@ 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; } @@ -787,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, @@ -800,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[] = { @@ -814,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; @@ -825,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); @@ -855,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