From 0c65f573a4a33cd4eed20b2ae7c94e0fe272ad09 Mon Sep 17 00:00:00 2001 From: Herman Chen Date: Fri, 27 Dec 2019 15:03:07 +0800 Subject: [PATCH] video: rockchip: mpp: deal with issue for px30 264/265 switch px30 grf switch method: a) ensure clk on b) ensure power on c) disable all iommu d) switch grf e) enable current iommu Signed-off-by: Herman Chen Change-Id: I1015ea9df6d8745f0a4922a1f684082660fac314 Signed-off-by: Ding Wei --- drivers/video/rockchip/mpp/mpp_common.c | 112 ++++++++++++++++++- drivers/video/rockchip/mpp/mpp_common.h | 8 ++ drivers/video/rockchip/mpp/mpp_iommu.c | 142 ++++++++++++++++++++++++ drivers/video/rockchip/mpp/mpp_iommu.h | 19 +++- drivers/video/rockchip/mpp/mpp_rkvdec.c | 29 +++++ drivers/video/rockchip/mpp/mpp_vdpu2.c | 29 +++++ drivers/video/rockchip/mpp/mpp_vepu2.c | 29 +++++ 7 files changed, 365 insertions(+), 3 deletions(-) diff --git a/drivers/video/rockchip/mpp/mpp_common.c b/drivers/video/rockchip/mpp/mpp_common.c index d2041f88b311..3f6893d0c3c8 100644 --- a/drivers/video/rockchip/mpp/mpp_common.c +++ b/drivers/video/rockchip/mpp/mpp_common.c @@ -388,7 +388,10 @@ static int mpp_dev_reset(struct mpp_dev *mpp) * before running, we have to switch grf ctrl bit to ensure * working in current hardware */ - mpp_set_grf(mpp->grf_info); + if (mpp->hw_ops->set_grf) + mpp->hw_ops->set_grf(mpp); + else + mpp_set_grf(mpp->grf_info); if (mpp->hw_ops->reduce_freq) mpp->hw_ops->reduce_freq(mpp); @@ -457,7 +460,15 @@ static int mpp_task_run(struct mpp_dev *mpp, * before running, we have to switch grf ctrl bit to ensure * working in current hardware */ - mpp_set_grf(mpp->grf_info); + if (mpp->hw_ops->set_grf) { + ret = mpp->hw_ops->set_grf(mpp); + if (ret) { + dev_err(mpp->dev, "set grf failed\n"); + return ret; + } + } else { + mpp_set_grf(mpp->grf_info); + } /* * for iommu share hardware, should attach to ensure * working in current device @@ -664,6 +675,7 @@ int mpp_taskqueue_init(struct mpp_taskqueue *queue, mutex_init(&queue->lock); atomic_set(&queue->running, 0); INIT_LIST_HEAD(&queue->pending); + INIT_LIST_HEAD(&queue->mmu_link); INIT_WORK(&queue->work, mpp_task_try_run); queue->srv = srv; @@ -1469,6 +1481,20 @@ int mpp_safe_unreset(struct reset_control *rst) return 0; } +#define MPP_GRF_VAL_MASK 0xFFFF + +u32 mpp_get_grf(struct mpp_grf_info *grf_info) +{ + u32 val = 0; + + if (grf_info->grf && grf_info->val) + regmap_read(grf_info->grf, + grf_info->offset, + &val); + + return (val & MPP_GRF_VAL_MASK); +} + int mpp_set_grf(struct mpp_grf_info *grf_info) { if (grf_info->grf && grf_info->val) @@ -1525,3 +1551,85 @@ int mpp_read_req(struct mpp_dev *mpp, u32 *regs, return 0; } + +int px30_workaround_combo_init(struct mpp_dev *mpp) +{ + struct mpp_rk_iommu *iommu = NULL, *loop = NULL, *n; + struct platform_device *pdev = mpp->iommu_info->pdev; + + /* find whether exist in iommu link */ + list_for_each_entry_safe(loop, n, &mpp->queue->mmu_link, link) { + if (loop->base_addr[0] == pdev->resource[0].start) { + iommu = loop; + break; + } + } + /* if not exist, add it */ + if (!iommu) { + int i; + struct resource *res; + void __iomem *base; + + iommu = devm_kzalloc(mpp->srv->dev, sizeof(*iommu), GFP_KERNEL); + for (i = 0; i < pdev->num_resources; i++) { + res = platform_get_resource(pdev, IORESOURCE_MEM, i); + if (!res) + continue; + base = devm_ioremap(&pdev->dev, + res->start, resource_size(res)); + if (IS_ERR(base)) + continue; + iommu->base_addr[i] = res->start; + iommu->bases[i] = base; + iommu->mmu_num++; + } + iommu->grf_val = mpp->grf_info->val & MPP_GRF_VAL_MASK; + iommu->dte_addr = mpp_iommu_get_dte_addr(iommu); + INIT_LIST_HEAD(&iommu->link); + mutex_lock(&mpp->queue->lock); + list_add_tail(&iommu->link, &mpp->queue->mmu_link); + mutex_unlock(&mpp->queue->lock); + } + mpp->iommu_info->iommu = iommu; + + return 0; +} + +int px30_workaround_combo_switch_grf(struct mpp_dev *mpp) +{ + int ret = 0; + u32 curr_val; + u32 next_val; + bool pd_is_on; + struct mpp_rk_iommu *loop = NULL, *n; + + if (!mpp->grf_info->grf || !mpp->grf_info->val) + return 0; + + curr_val = mpp_get_grf(mpp->grf_info); + next_val = mpp->grf_info->val & MPP_GRF_VAL_MASK; + if (curr_val == next_val) + return 0; + + pd_is_on = rockchip_pmu_pd_is_on(mpp->dev); + if (!pd_is_on) + rockchip_pmu_pd_on(mpp->dev); + mpp->hw_ops->power_on(mpp); + + list_for_each_entry_safe(loop, n, &mpp->queue->mmu_link, link) { + /* update iommu parameters */ + if (loop->grf_val == curr_val) + loop->is_paged = mpp_iommu_is_paged(loop); + /* disable all iommu */ + mpp_iommu_disable(loop); + } + mpp_set_grf(mpp->grf_info); + /* enable current iommu */ + ret = mpp_iommu_enable(mpp->iommu_info->iommu); + + mpp->hw_ops->power_off(mpp); + if (!pd_is_on) + rockchip_pmu_pd_off(mpp->dev); + + return ret; +} diff --git a/drivers/video/rockchip/mpp/mpp_common.h b/drivers/video/rockchip/mpp/mpp_common.h index d421763ceed2..ded08e9111eb 100644 --- a/drivers/video/rockchip/mpp/mpp_common.h +++ b/drivers/video/rockchip/mpp/mpp_common.h @@ -254,6 +254,8 @@ struct mpp_taskqueue { atomic_t running; struct mpp_task *cur_task; struct mpp_service *srv; + /* link to mmu iommu node */ + struct list_head mmu_link; }; struct mpp_reset_clk { @@ -311,6 +313,7 @@ struct mpp_hw_ops { struct mpp_task *mpp_task); int (*reduce_freq)(struct mpp_dev *mpp); int (*reset)(struct mpp_dev *mpp); + int (*set_grf)(struct mpp_dev *mpp); }; /* @@ -383,6 +386,7 @@ mpp_reset_control_get(struct mpp_dev *mpp, const char *name); int mpp_safe_reset(struct reset_control *rst); int mpp_safe_unreset(struct reset_control *rst); +u32 mpp_get_grf(struct mpp_grf_info *grf_info); int mpp_set_grf(struct mpp_grf_info *grf_info); int mpp_time_record(struct mpp_task *task); @@ -435,6 +439,10 @@ static inline u32 mpp_read_relaxed(struct mpp_dev *mpp, u32 reg) return val; } +/* workaround according hardware */ +int px30_workaround_combo_init(struct mpp_dev *mpp); +int px30_workaround_combo_switch_grf(struct mpp_dev *mpp); + extern const struct file_operations rockchip_mpp_fops; extern struct platform_driver rockchip_rkvdec_driver; diff --git a/drivers/video/rockchip/mpp/mpp_iommu.c b/drivers/video/rockchip/mpp/mpp_iommu.c index 9398e37b25f9..114850a23d79 100644 --- a/drivers/video/rockchip/mpp/mpp_iommu.c +++ b/drivers/video/rockchip/mpp/mpp_iommu.c @@ -11,9 +11,12 @@ #ifdef CONFIG_ARM_DMA_USE_IOMMU #include #endif +#include #include #include #include +#include +#include #include #include @@ -350,17 +353,33 @@ struct mpp_iommu_info * mpp_iommu_probe(struct device *dev) { int ret = 0; + struct device_node *np = NULL; + struct platform_device *pdev = NULL; struct mpp_iommu_info *info = NULL; #ifdef CONFIG_ARM_DMA_USE_IOMMU struct dma_iommu_mapping *mapping; #endif + np = of_parse_phandle(dev->of_node, "iommus", 0); + if (!np || !of_device_is_available(np)) { + mpp_err("failed to get device node\n"); + ret = -ENODEV; + goto err; + } + pdev = of_find_device_by_node(np); + if (!pdev) { + mpp_err("failed to get platform device\n"); + ret = -ENODEV; + goto err; + } + info = kzalloc(sizeof(*info), GFP_KERNEL); if (!info) { ret = -ENOMEM; goto err; } info->dev = dev; + info->pdev = pdev; info->group = iommu_group_get(dev); if (!info->group) { @@ -411,3 +430,126 @@ int mpp_iommu_remove(struct mpp_iommu_info *info) return 0; } + +#define RK_MMU_DTE_ADDR 0x00 /* Directory table address */ +#define RK_MMU_STATUS 0x04 +#define RK_MMU_COMMAND 0x08 +#define RK_MMU_INT_MASK 0x1C /* IRQ enable */ + +/* RK_MMU_COMMAND command values */ +#define RK_MMU_CMD_ENABLE_PAGING 0 /* Enable memory translation */ +#define RK_MMU_CMD_DISABLE_PAGING 1 /* Disable memory translation */ +#define RK_MMU_CMD_ENABLE_STALL 2 /* Stall paging to allow other cmds */ +#define RK_MMU_CMD_DISABLE_STALL 3 /* Stop stall re-enables paging */ +#define RK_MMU_CMD_ZAP_CACHE 4 /* Shoot down entire IOTLB */ +#define RK_MMU_CMD_PAGE_FAULT_DONE 5 /* Clear page fault */ +#define RK_MMU_CMD_FORCE_RESET 6 /* Reset all registers */ + +/* RK_MMU_INT_* register fields */ +#define RK_MMU_IRQ_MASK 0x03 +/* RK_MMU_STATUS fields */ +#define RK_MMU_STATUS_PAGING_ENABLED BIT(0) +#define RK_MMU_STATUS_STALL_ACTIVE BIT(2) + +bool mpp_iommu_is_paged(struct mpp_rk_iommu *iommu) +{ + int i; + u32 status; + bool active = true; + + for (i = 0; i < iommu->mmu_num; i++) { + status = readl(iommu->bases[i] + RK_MMU_STATUS); + active &= !!(status & RK_MMU_STATUS_PAGING_ENABLED); + } + + return active; +} + +u32 mpp_iommu_get_dte_addr(struct mpp_rk_iommu *iommu) +{ + return readl(iommu->bases[0] + RK_MMU_DTE_ADDR); +} + +int mpp_iommu_enable(struct mpp_rk_iommu *iommu) +{ + int i; + + /* iommu should be paging disable */ + if (mpp_iommu_is_paged(iommu)) { + mpp_err("iommu disable failed\n"); + return -ENOMEM; + } + + /* enable stall */ + for (i = 0; i < iommu->mmu_num; i++) + writel(RK_MMU_CMD_ENABLE_STALL, + iommu->bases[i] + RK_MMU_COMMAND); + udelay(2); + /* force reset */ + for (i = 0; i < iommu->mmu_num; i++) + writel(RK_MMU_CMD_FORCE_RESET, + iommu->bases[i] + RK_MMU_COMMAND); + udelay(2); + + for (i = 0; i < iommu->mmu_num; i++) { + /* restore dte and status */ + writel(iommu->dte_addr, + iommu->bases[i] + RK_MMU_DTE_ADDR); + /* zap cache */ + writel(RK_MMU_CMD_ZAP_CACHE, + iommu->bases[i] + RK_MMU_COMMAND); + /* irq mask */ + writel(RK_MMU_IRQ_MASK, + iommu->bases[i] + RK_MMU_INT_MASK); + } + udelay(2); + /* enable paging */ + for (i = 0; i < iommu->mmu_num; i++) + writel(RK_MMU_CMD_ENABLE_PAGING, + iommu->bases[i] + RK_MMU_COMMAND); + udelay(2); + /* disable stall */ + for (i = 0; i < iommu->mmu_num; i++) + writel(RK_MMU_CMD_DISABLE_STALL, + iommu->bases[i] + RK_MMU_COMMAND); + udelay(2); + + /* iommu should be paging enable */ + iommu->is_paged = mpp_iommu_is_paged(iommu); + if (!iommu->is_paged) { + mpp_err("iommu enable failed\n"); + return -EINVAL; + } + + return 0; +} + +int mpp_iommu_disable(struct mpp_rk_iommu *iommu) +{ + int i; + u32 dte; + + if (iommu->is_paged) { + dte = readl(iommu->bases[0] + RK_MMU_DTE_ADDR); + if (!dte) + return -EINVAL; + udelay(2); + /* enable stall */ + for (i = 0; i < iommu->mmu_num; i++) + writel(RK_MMU_CMD_ENABLE_STALL, + iommu->bases[i] + RK_MMU_COMMAND); + udelay(2); + /* disable paging */ + for (i = 0; i < iommu->mmu_num; i++) + writel(RK_MMU_CMD_DISABLE_PAGING, + iommu->bases[i] + RK_MMU_COMMAND); + udelay(2); + /* disable stall */ + for (i = 0; i < iommu->mmu_num; i++) + writel(RK_MMU_CMD_DISABLE_STALL, + iommu->bases[i] + RK_MMU_COMMAND); + udelay(2); + } + + return 0; +} diff --git a/drivers/video/rockchip/mpp/mpp_iommu.h b/drivers/video/rockchip/mpp/mpp_iommu.h index 864413ea27dd..e2cb42707096 100644 --- a/drivers/video/rockchip/mpp/mpp_iommu.h +++ b/drivers/video/rockchip/mpp/mpp_iommu.h @@ -46,10 +46,22 @@ struct mpp_dma_session { struct device *dev; }; +struct mpp_rk_iommu { + struct list_head link; + u32 grf_val; + int mmu_num; + u32 base_addr[2]; + void __iomem *bases[2]; + u32 dte_addr; + u32 is_paged; +}; + struct mpp_iommu_info { - struct device *dev; + struct device *dev; + struct platform_device *pdev; struct iommu_domain *domain; struct iommu_group *group; + struct mpp_rk_iommu *iommu; }; struct mpp_dma_session * @@ -78,4 +90,9 @@ int mpp_iommu_remove(struct mpp_iommu_info *info); int mpp_iommu_attach(struct mpp_iommu_info *info); int mpp_iommu_detach(struct mpp_iommu_info *info); +bool mpp_iommu_is_paged(struct mpp_rk_iommu *iommu); +u32 mpp_iommu_get_dte_addr(struct mpp_rk_iommu *iommu); +int mpp_iommu_enable(struct mpp_rk_iommu *iommu); +int mpp_iommu_disable(struct mpp_rk_iommu *iommu); + #endif diff --git a/drivers/video/rockchip/mpp/mpp_rkvdec.c b/drivers/video/rockchip/mpp/mpp_rkvdec.c index ef8cab74772a..9ef5f69d80fd 100644 --- a/drivers/video/rockchip/mpp/mpp_rkvdec.c +++ b/drivers/video/rockchip/mpp/mpp_rkvdec.c @@ -1166,6 +1166,12 @@ static int rkvdec_init(struct mpp_dev *mpp) return 0; } +static int rkvdec_px30_init(struct mpp_dev *mpp) +{ + rkvdec_init(mpp); + return px30_workaround_combo_init(mpp); +} + static int rkvdec_3328_iommu_hdl(struct iommu_domain *iommu, struct device *iommu_dev, unsigned long iova, @@ -1522,6 +1528,17 @@ static struct mpp_hw_ops rkvdec_v1_hw_ops = { .reset = rkvdec_reset, }; +static struct mpp_hw_ops rkvdec_px30_hw_ops = { + .init = rkvdec_px30_init, + .power_on = rkvdec_power_on, + .power_off = rkvdec_power_off, + .get_freq = rkvdec_get_freq, + .set_freq = rkvdec_set_freq, + .reduce_freq = rkvdec_reduce_freq, + .reset = rkvdec_reset, + .set_grf = px30_workaround_combo_switch_grf, +}; + static struct mpp_hw_ops rkvdec_3399_hw_ops = { .init = rkvdec_init, .power_on = rkvdec_power_on, @@ -1573,6 +1590,14 @@ static const struct mpp_dev_var rk_hevcdec_data = { .dev_ops = &rkvdec_v1_dev_ops, }; +static const struct mpp_dev_var rk_hevcdec_px30_data = { + .device_type = MPP_DEVICE_HEVC_DEC, + .hw_info = &rk_hevcdec_hw_info, + .trans_info = rk_hevcdec_trans, + .hw_ops = &rkvdec_px30_hw_ops, + .dev_ops = &rkvdec_v1_dev_ops, +}; + static const struct mpp_dev_var rkvdec_v1_data = { .device_type = MPP_DEVICE_RKVDEC, .hw_info = &rkvdec_v1_hw_info, @@ -1602,6 +1627,10 @@ static const struct of_device_id mpp_rkvdec_dt_match[] = { .compatible = "rockchip,hevc-decoder", .data = &rk_hevcdec_data, }, + { + .compatible = "rockchip,hevc-decoder-px30", + .data = &rk_hevcdec_px30_data, + }, { .compatible = "rockchip,rkv-decoder-v1", .data = &rkvdec_v1_data, diff --git a/drivers/video/rockchip/mpp/mpp_vdpu2.c b/drivers/video/rockchip/mpp/mpp_vdpu2.c index 347edb1d8d9f..951ef1a064be 100644 --- a/drivers/video/rockchip/mpp/mpp_vdpu2.c +++ b/drivers/video/rockchip/mpp/mpp_vdpu2.c @@ -516,6 +516,12 @@ static int vdpu_init(struct mpp_dev *mpp) return 0; } +static int vdpu_px30_init(struct mpp_dev *mpp) +{ + vdpu_init(mpp); + return px30_workaround_combo_init(mpp); +} + static int vdpu_power_on(struct mpp_dev *mpp) { struct vdpu_dev *dec = to_vdpu_dev(mpp); @@ -655,6 +661,17 @@ static struct mpp_hw_ops vdpu_v2_hw_ops = { .reset = vdpu_reset, }; +static struct mpp_hw_ops vdpu_px30_hw_ops = { + .init = vdpu_px30_init, + .power_on = vdpu_power_on, + .power_off = vdpu_power_off, + .get_freq = vdpu_get_freq, + .set_freq = vdpu_set_freq, + .reduce_freq = vdpu_reduce_freq, + .reset = vdpu_reset, + .set_grf = px30_workaround_combo_switch_grf, +}; + static struct mpp_dev_ops vdpu_v2_dev_ops = { .alloc_task = vdpu_alloc_task, .prepare = vdpu_prepare, @@ -674,11 +691,23 @@ static const struct mpp_dev_var vdpu_v2_data = { .dev_ops = &vdpu_v2_dev_ops, }; +static const struct mpp_dev_var vdpu_px30_data = { + .device_type = MPP_DEVICE_VDPU2, + .hw_info = &vdpu_v2_hw_info, + .trans_info = vdpu_v2_trans, + .hw_ops = &vdpu_px30_hw_ops, + .dev_ops = &vdpu_v2_dev_ops, +}; + static const struct of_device_id mpp_vdpu2_dt_match[] = { { .compatible = "rockchip,vpu-decoder-v2", .data = &vdpu_v2_data, }, + { + .compatible = "rockchip,vpu-decoder-px30", + .data = &vdpu_px30_data, + }, {}, }; diff --git a/drivers/video/rockchip/mpp/mpp_vepu2.c b/drivers/video/rockchip/mpp/mpp_vepu2.c index a3e101cff6d6..d24440ff1b9f 100644 --- a/drivers/video/rockchip/mpp/mpp_vepu2.c +++ b/drivers/video/rockchip/mpp/mpp_vepu2.c @@ -483,6 +483,12 @@ static int vepu_init(struct mpp_dev *mpp) return 0; } +static int vepu_px30_init(struct mpp_dev *mpp) +{ + vepu_init(mpp); + return px30_workaround_combo_init(mpp); +} + static int vepu_power_on(struct mpp_dev *mpp) { struct vepu_dev *enc = to_vepu_dev(mpp); @@ -571,6 +577,17 @@ static struct mpp_hw_ops vepu_v2_hw_ops = { .reset = vepu_reset, }; +static struct mpp_hw_ops vepu_px30_hw_ops = { + .init = vepu_px30_init, + .power_on = vepu_power_on, + .power_off = vepu_power_off, + .get_freq = vepu_get_freq, + .set_freq = vepu_set_freq, + .reduce_freq = vepu_reduce_freq, + .reset = vepu_reset, + .set_grf = px30_workaround_combo_switch_grf, +}; + static struct mpp_dev_ops vepu_v2_dev_ops = { .alloc_task = vepu_alloc_task, .prepare = vepu_prepare, @@ -590,11 +607,23 @@ static const struct mpp_dev_var vepu_v2_data = { .dev_ops = &vepu_v2_dev_ops, }; +static const struct mpp_dev_var vepu_px30_data = { + .device_type = MPP_DEVICE_VEPU2, + .hw_info = &vepu_v2_hw_info, + .trans_info = trans_rk_vepu2, + .hw_ops = &vepu_px30_hw_ops, + .dev_ops = &vepu_v2_dev_ops, +}; + static const struct of_device_id mpp_vepu2_dt_match[] = { { .compatible = "rockchip,vpu-encoder-v2", .data = &vepu_v2_data, }, + { + .compatible = "rockchip,vpu-encoder-px30", + .data = &vepu_px30_data, + }, {}, };