Skip to content
Snippets Groups Projects
Commit 352ee610 authored by Nicolas Dufresne's avatar Nicolas Dufresne
Browse files

media: rkvdec: Restore iommu addresses on errors


On errors, the rkvdec chip self resets. This can clear the addresses
programmed in the iommu. This case is signaled by the
RKVDEC_SOFTRESET_RDY status bit.

Since the iommu framework does not have a restore functionality, and
as recommended by the iommu subsystem maintainers, this patch
restores the iommu programming by attaching an empty domain, detaching
it and re-attaching the device allocated domain.

Suggested-by: default avatarDetlev Casanova <detlev.casanova@collabora.com>
Signed-of-by: default avatarNicolas Dufresne <nicolas.dufresne@collabora.com>
parent 4f114c5a
No related branches found
No related tags found
No related merge requests found
......@@ -11,6 +11,7 @@
#include <linux/clk.h>
#include <linux/interrupt.h>
#include <linux/iommu.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
......@@ -1055,24 +1056,43 @@ static void rkvdec_v4l2_cleanup(struct rkvdec_dev *rkvdec)
v4l2_device_unregister(&rkvdec->v4l2_dev);
}
static void rkvdec_iommu_restore(struct rkvdec_dev *rkvdec)
{
if (rkvdec->iommu_domain && rkvdec->empty_domain) {
/*
* To rewrite mapping into the attached IOMMU core, attach a new empty domain that
* will program an empty table, then attach the default domain again to reprogram
* all cached mappings.
* This is safely done in this interrupt handler to make sure no memory get mapped
* through the IOMMU while the empty domain is attached.
*/
iommu_attach_device(rkvdec->empty_domain, rkvdec->dev);
iommu_detach_device(rkvdec->empty_domain, rkvdec->dev);
iommu_attach_device(rkvdec->iommu_domain, rkvdec->dev);
}
}
static irqreturn_t rkvdec_irq_handler(int irq, void *priv)
{
struct rkvdec_dev *rkvdec = priv;
struct rkvdec_ctx *ctx = v4l2_m2m_get_curr_priv(rkvdec->m2m_dev);
enum vb2_buffer_state state;
u32 status;
status = readl(rkvdec->regs + RKVDEC_REG_INTERRUPT);
state = (status & RKVDEC_RDY_STA) ?
VB2_BUF_STATE_DONE : VB2_BUF_STATE_ERROR;
writel(0, rkvdec->regs + RKVDEC_REG_INTERRUPT);
if (cancel_delayed_work(&rkvdec->watchdog_work)) {
struct rkvdec_ctx *ctx;
ctx = v4l2_m2m_get_curr_priv(rkvdec->m2m_dev);
rkvdec_job_finish(ctx, state);
if (status & RKVDEC_RDY_STA) {
state = VB2_BUF_STATE_DONE;
} else {
state = VB2_BUF_STATE_ERROR;
if (status & RKVDEC_SOFTRESET_RDY)
rkvdec_iommu_restore(rkvdec);
}
if (cancel_delayed_work(&rkvdec->watchdog_work))
rkvdec_job_finish(ctx, state);
return IRQ_HANDLED;
}
......@@ -1140,6 +1160,14 @@ static int rkvdec_probe(struct platform_device *pdev)
return ret;
}
rkvdec->iommu_domain = iommu_get_domain_for_dev(&pdev->dev);
if (rkvdec->iommu_domain) {
rkvdec->empty_domain = iommu_paging_domain_alloc(rkvdec->dev);
if (!rkvdec->empty_domain)
dev_warn(rkvdec->dev, "cannot alloc new empty domain\n");
}
vb2_dma_contig_set_max_seg_size(&pdev->dev, DMA_BIT_MASK(32));
irq = platform_get_irq(pdev, 0);
......@@ -1179,6 +1207,9 @@ static void rkvdec_remove(struct platform_device *pdev)
rkvdec_v4l2_cleanup(rkvdec);
pm_runtime_disable(&pdev->dev);
pm_runtime_dont_use_autosuspend(&pdev->dev);
if (rkvdec->empty_domain)
iommu_domain_free(rkvdec->empty_domain);
}
#ifdef CONFIG_PM
......
......@@ -110,6 +110,8 @@ struct rkvdec_dev {
void __iomem *regs;
struct mutex vdev_lock; /* serializes ioctls */
struct delayed_work watchdog_work;
struct iommu_domain *iommu_domain;
struct iommu_domain *empty_domain;
};
struct rkvdec_ctx {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment