Paul Kocialkowski <paul.kocialkow...@bootlin.com> writes: > From: Boris Brezillon <boris.brezil...@bootlin.com> > > Add a debugfs entry and helper for reporting HVS underrun errors as > well as helpers for masking and unmasking the underrun interrupts. > Add an IRQ handler and initial IRQ configuration. > Rework related register definitions to take the channel number. > > Signed-off-by: Boris Brezillon <boris.brezil...@bootlin.com> > Signed-off-by: Paul Kocialkowski <paul.kocialkow...@bootlin.com> > --- > drivers/gpu/drm/vc4/vc4_debugfs.c | 1 + > drivers/gpu/drm/vc4/vc4_drv.h | 10 ++++ > drivers/gpu/drm/vc4/vc4_hvs.c | 92 +++++++++++++++++++++++++++++++ > drivers/gpu/drm/vc4/vc4_kms.c | 4 ++ > drivers/gpu/drm/vc4/vc4_regs.h | 49 +++++----------- > 5 files changed, 121 insertions(+), 35 deletions(-) > > diff --git a/drivers/gpu/drm/vc4/vc4_debugfs.c > b/drivers/gpu/drm/vc4/vc4_debugfs.c > index 7a0003de71ab..64021bcba041 100644 > --- a/drivers/gpu/drm/vc4/vc4_debugfs.c > +++ b/drivers/gpu/drm/vc4/vc4_debugfs.c > @@ -23,6 +23,7 @@ static const struct drm_info_list vc4_debugfs_list[] = { > {"vec_regs", vc4_vec_debugfs_regs, 0}, > {"txp_regs", vc4_txp_debugfs_regs, 0}, > {"hvs_regs", vc4_hvs_debugfs_regs, 0}, > + {"hvs_underrun", vc4_hvs_debugfs_underrun, 0}, > {"crtc0_regs", vc4_crtc_debugfs_regs, 0, (void *)(uintptr_t)0}, > {"crtc1_regs", vc4_crtc_debugfs_regs, 0, (void *)(uintptr_t)1}, > {"crtc2_regs", vc4_crtc_debugfs_regs, 0, (void *)(uintptr_t)2}, > diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h > index 2c635f001c71..b34da7b6ee6b 100644 > --- a/drivers/gpu/drm/vc4/vc4_drv.h > +++ b/drivers/gpu/drm/vc4/vc4_drv.h > @@ -185,6 +185,13 @@ struct vc4_dev { > /* Bitmask of the current bin_alloc used for overflow memory. */ > uint32_t bin_alloc_overflow; > > + /* Incremented when an underrun error happened after an atomic commit. > + * This is particularly useful to detect when a specific modeset is too > + * demanding in term of memory or HVS bandwidth which is hard to guess > + * at atomic check time. > + */ > + atomic_t underrun; > + > struct work_struct overflow_mem_work; > > int power_refcount; > @@ -773,6 +780,9 @@ void vc4_irq_reset(struct drm_device *dev); > extern struct platform_driver vc4_hvs_driver; > void vc4_hvs_dump_state(struct drm_device *dev); > int vc4_hvs_debugfs_regs(struct seq_file *m, void *unused); > +int vc4_hvs_debugfs_underrun(struct seq_file *m, void *unused); > +void vc4_hvs_unmask_underrun(struct drm_device *dev); > +void vc4_hvs_mask_underrun(struct drm_device *dev); > > /* vc4_kms.c */ > int vc4_kms_load(struct drm_device *dev); > diff --git a/drivers/gpu/drm/vc4/vc4_hvs.c b/drivers/gpu/drm/vc4/vc4_hvs.c > index 5d8c749c9749..d5bc3bcd3e51 100644 > --- a/drivers/gpu/drm/vc4/vc4_hvs.c > +++ b/drivers/gpu/drm/vc4/vc4_hvs.c > @@ -22,6 +22,7 @@ > * each CRTC. > */ > > +#include <drm/drm_atomic_helper.h> > #include <linux/component.h> > #include "vc4_drv.h" > #include "vc4_regs.h" > @@ -102,6 +103,18 @@ int vc4_hvs_debugfs_regs(struct seq_file *m, void > *unused) > > return 0; > } > + > +int vc4_hvs_debugfs_underrun(struct seq_file *m, void *data) > +{ > + struct drm_info_node *node = m->private; > + struct drm_device *dev = node->minor->dev; > + struct vc4_dev *vc4 = to_vc4_dev(dev); > + struct drm_printer p = drm_seq_file_printer(m); > + > + drm_printf(&p, "%d\n", atomic_read(&vc4->underrun)); > + > + return 0; > +} > #endif > > /* The filter kernel is composed of dwords each containing 3 9-bit > @@ -166,6 +179,67 @@ static int vc4_hvs_upload_linear_kernel(struct vc4_hvs > *hvs, > return 0; > } > > +void vc4_hvs_mask_underrun(struct drm_device *dev) > +{ > + struct vc4_dev *vc4 = to_vc4_dev(dev); > + u32 dispctrl = HVS_READ(SCALER_DISPCTRL); > + > + dispctrl &= ~(SCALER_DISPCTRL_DSPEISLUR(0) | > + SCALER_DISPCTRL_DSPEISLUR(1) | > + SCALER_DISPCTRL_DSPEISLUR(2)); > + > + HVS_WRITE(SCALER_DISPCTRL, dispctrl); > +} > + > +void vc4_hvs_unmask_underrun(struct drm_device *dev) > +{ > + struct vc4_dev *vc4 = to_vc4_dev(dev); > + u32 dispctrl = HVS_READ(SCALER_DISPCTRL); > + > + dispctrl |= SCALER_DISPCTRL_DSPEISLUR(0) | > + SCALER_DISPCTRL_DSPEISLUR(1) | > + SCALER_DISPCTRL_DSPEISLUR(2); > + > + HVS_WRITE(SCALER_DISPSTAT, > + SCALER_DISPSTAT_EUFLOW(0) | > + SCALER_DISPSTAT_EUFLOW(1) | > + SCALER_DISPSTAT_EUFLOW(2)); > + HVS_WRITE(SCALER_DISPCTRL, dispctrl); > +} > + > +static void vc4_hvs_report_underrun(struct drm_device *dev) > +{ > + struct vc4_dev *vc4 = to_vc4_dev(dev); > + > + atomic_inc(&vc4->underrun); > + DRM_DEV_ERROR(dev->dev, "HVS underrun\n"); > +} > + > +static irqreturn_t vc4_hvs_irq_handler(int irq, void *data) > +{ > + struct drm_device *dev = data; > + struct vc4_dev *vc4 = to_vc4_dev(dev); > + irqreturn_t irqret = IRQ_NONE; > + u32 status; > + > + status = HVS_READ(SCALER_DISPSTAT); > + > + if (status & (SCALER_DISPSTAT_EUFLOW(0) | > + SCALER_DISPSTAT_EUFLOW(1) | > + SCALER_DISPSTAT_EUFLOW(2))) { > + vc4_hvs_mask_underrun(dev); > + vc4_hvs_report_underrun(dev); > + > + irqret = IRQ_HANDLED; > + } > + > + HVS_WRITE(SCALER_DISPSTAT, SCALER_DISPSTAT_IRQMASK(0) | > + SCALER_DISPSTAT_IRQMASK(1) | > + SCALER_DISPSTAT_IRQMASK(2)); > + > + return irqret; > +} > + > static int vc4_hvs_bind(struct device *dev, struct device *master, void > *data) > { > struct platform_device *pdev = to_platform_device(dev); > @@ -219,15 +293,33 @@ static int vc4_hvs_bind(struct device *dev, struct > device *master, void *data) > dispctrl = HVS_READ(SCALER_DISPCTRL); > > dispctrl |= SCALER_DISPCTRL_ENABLE; > + dispctrl |= SCALER_DISPCTRL_DSPEISLUR(0) | SCALER_DISPCTRL_DISPEIRQ(0) | > + SCALER_DISPCTRL_DSPEISLUR(1) | SCALER_DISPCTRL_DISPEIRQ(1) | > + SCALER_DISPCTRL_DSPEISLUR(2) | SCALER_DISPCTRL_DISPEIRQ(2); > > /* Set DSP3 (PV1) to use HVS channel 2, which would otherwise > * be unused. > */ > dispctrl &= ~SCALER_DISPCTRL_DSP3_MUX_MASK; > + dispctrl &= ~(SCALER_DISPCTRL_DMAEIRQ | > + SCALER_DISPCTRL_SLVWREIRQ | > + SCALER_DISPCTRL_SLVRDEIRQ | > + SCALER_DISPCTRL_DSPEIEOF(0) | > + SCALER_DISPCTRL_DSPEIEOF(1) | > + SCALER_DISPCTRL_DSPEIEOF(2) | > + SCALER_DISPCTRL_DSPEIEOLN(0) | > + SCALER_DISPCTRL_DSPEIEOLN(1) | > + SCALER_DISPCTRL_DSPEIEOLN(2) | > + SCALER_DISPCTRL_SCLEIRQ); > dispctrl |= VC4_SET_FIELD(2, SCALER_DISPCTRL_DSP3_MUX); > > HVS_WRITE(SCALER_DISPCTRL, dispctrl); > > + ret = devm_request_irq(dev, platform_get_irq(pdev, 0), > + vc4_hvs_irq_handler, 0, "vc4 hvs", drm); > + if (ret) > + return ret; > + > return 0; > } > > diff --git a/drivers/gpu/drm/vc4/vc4_kms.c b/drivers/gpu/drm/vc4/vc4_kms.c > index 91b8c72ff361..3c87fbcd0b17 100644 > --- a/drivers/gpu/drm/vc4/vc4_kms.c > +++ b/drivers/gpu/drm/vc4/vc4_kms.c > @@ -139,6 +139,8 @@ vc4_atomic_complete_commit(struct drm_atomic_state *state) > struct drm_device *dev = state->dev; > struct vc4_dev *vc4 = to_vc4_dev(dev); > > + vc4_hvs_mask_underrun(dev); > + > drm_atomic_helper_wait_for_fences(dev, state, false); > > drm_atomic_helper_wait_for_dependencies(state); > @@ -155,6 +157,8 @@ vc4_atomic_complete_commit(struct drm_atomic_state *state) > > drm_atomic_helper_commit_hw_done(state); > > + vc4_hvs_unmask_underrun(dev); > + > drm_atomic_helper_wait_for_flip_done(dev, state); > > drm_atomic_helper_cleanup_planes(dev, state);
I think the mask/unmask in here could race against another atomic_commit happening on another CRTC in parallel. I guess maybe we should mask off interrupts on the specific channel being modified. However, given that we're just talking about a debugfs entry and user-space testing tool, I'm fine with this as-is. Other than my concern for patch #4, patch 1-3 are: Reviewed-by: Eric Anholt <e...@anholt.net>
signature.asc
Description: PGP signature