[3/3] bus: ti-sysc: Implement display subsystem reset quirk
diff mbox series

Message ID 20200224191230.30972-4-tony@atomide.com
State New
Headers show
Series
  • ti-sysc changes for probing DSS with dts data
Related show

Commit Message

Tony Lindgren Feb. 24, 2020, 7:12 p.m. UTC
The display subsystem (DSS) needs the child outputs disabled for reset.
In order to prepare to probe DSS without legacy platform data, let's
implement sysc_pre_reset_quirk_dss() similar to what we have for the
platform data with omap_dss_reset().

Note that we cannot directly use the old omap_dss_reset() without
platform data callbacks and updating omap_dss_reset() to understand
struct device. And we will be dropping omap_dss_reset() anyways when
all the SoCs are probing with device tree, so let's not mess with the
legacy code at all.

Cc: Jyri Sarha <jsarha@ti.com>
Cc: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Cc: Tomi Valkeinen <tomi.valkeinen@ti.com>
Signed-off-by: Tony Lindgren <tony@atomide.com>
---
 drivers/bus/ti-sysc.c                 | 131 +++++++++++++++++++++++++-
 include/linux/platform_data/ti-sysc.h |   1 +
 2 files changed, 129 insertions(+), 3 deletions(-)

Comments

Tomi Valkeinen March 3, 2020, 6:02 a.m. UTC | #1
On 24/02/2020 21:12, Tony Lindgren wrote:
> The display subsystem (DSS) needs the child outputs disabled for reset.
> In order to prepare to probe DSS without legacy platform data, let's
> implement sysc_pre_reset_quirk_dss() similar to what we have for the
> platform data with omap_dss_reset().
> 
> Note that we cannot directly use the old omap_dss_reset() without
> platform data callbacks and updating omap_dss_reset() to understand
> struct device. And we will be dropping omap_dss_reset() anyways when
> all the SoCs are probing with device tree, so let's not mess with the
> legacy code at all.
> 
> Cc: Jyri Sarha <jsarha@ti.com>
> Cc: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> Cc: Tomi Valkeinen <tomi.valkeinen@ti.com>
> Signed-off-by: Tony Lindgren <tony@atomide.com>
> ---
>   drivers/bus/ti-sysc.c                 | 131 +++++++++++++++++++++++++-
>   include/linux/platform_data/ti-sysc.h |   1 +
>   2 files changed, 129 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/bus/ti-sysc.c b/drivers/bus/ti-sysc.c
> --- a/drivers/bus/ti-sysc.c
> +++ b/drivers/bus/ti-sysc.c
> @@ -1303,11 +1303,11 @@ static const struct sysc_revision_quirk sysc_revision_quirks[] = {
>   	SYSC_QUIRK("dcan", 0x48480000, 0x20, -ENODEV, -ENODEV, 0xa3170504, 0xffffffff,
>   		   SYSC_QUIRK_CLKDM_NOAUTO),
>   	SYSC_QUIRK("dss", 0x4832a000, 0, 0x10, 0x14, 0x00000020, 0xffffffff,
> -		   SYSC_QUIRK_OPT_CLKS_IN_RESET),
> +		   SYSC_QUIRK_OPT_CLKS_IN_RESET | SYSC_MODULE_QUIRK_DSS_RESET),
>   	SYSC_QUIRK("dss", 0x58000000, 0, -ENODEV, 0x14, 0x00000040, 0xffffffff,
> -		   SYSC_QUIRK_OPT_CLKS_IN_RESET),
> +		   SYSC_QUIRK_OPT_CLKS_IN_RESET | SYSC_MODULE_QUIRK_DSS_RESET),
>   	SYSC_QUIRK("dss", 0x58000000, 0, -ENODEV, 0x14, 0x00000061, 0xffffffff,
> -		   SYSC_QUIRK_OPT_CLKS_IN_RESET),
> +		   SYSC_QUIRK_OPT_CLKS_IN_RESET | SYSC_MODULE_QUIRK_DSS_RESET),
>   	SYSC_QUIRK("dwc3", 0x48880000, 0, 0x10, -ENODEV, 0x500a0200, 0xffffffff,
>   		   SYSC_QUIRK_CLKDM_NOAUTO),
>   	SYSC_QUIRK("dwc3", 0x488c0000, 0, 0x10, -ENODEV, 0x500a0200, 0xffffffff,
> @@ -1468,6 +1468,128 @@ static void sysc_init_revision_quirks(struct sysc *ddata)
>   	}
>   }
>   
> +/*
> + * DSS needs dispc outputs disabled to reset modules. Returns mask of
> + * enabled DSS interrupts. Eventually we may be able to do this on
> + * dispc init rather than top-level DSS init.
> + */
> +static u32 sysc_quirk_dispc(struct sysc *ddata, int dispc_offset,
> +			    bool disable)
> +{
> +	bool lcd_en, digit_en, lcd2_en = false, lcd3_en = false;
> +	const int lcd_en_mask = BIT(0), digit_en_mask = BIT(1);
> +	int manager_count;
> +	bool framedonetv_irq;
> +	u32 val, irq_mask = 0;
> +
> +	switch (sysc_soc->soc) {
> +	case SOC_2420 ... SOC_3630:
> +		manager_count = 2;
> +		framedonetv_irq = false;
> +		break;
> +	case SOC_4430 ... SOC_4470:
> +		manager_count = 3;
> +		break;
> +	case SOC_5430:
> +	case SOC_DRA7:
> +		manager_count = 4;
> +		break;
> +	case SOC_AM4:
> +		manager_count = 1;
> +		break;
> +	case SOC_UNKNOWN:
> +	default:
> +		return 0;
> +	};
> +
> +	/* Remap the whole module range to be able to reset dispc outputs */
> +	devm_iounmap(ddata->dev, ddata->module_va);
> +	ddata->module_va = devm_ioremap(ddata->dev,
> +					ddata->module_pa,
> +					ddata->module_size);

Why is this needed? The range is not mapped when sysc_pre_reset_quirk_dss() is called? This will 
unmap and remap twice, as this function is called twice. And then left mapped.

> +	if (!ddata->module_va)
> +		return -EIO;
> +
> +	/* DISP_CONTROL */
> +	val = sysc_read(ddata, dispc_offset + 0x40);

Defines for dss/dispc register offsets could have been copied from the platform display.c and used 
in this file.

> +	lcd_en = val & lcd_en_mask;
> +	digit_en = val & digit_en_mask;
> +	if (lcd_en)
> +		irq_mask |= BIT(0);			/* FRAMEDONE */
> +	if (digit_en) {
> +		if (framedonetv_irq)
> +			irq_mask |= BIT(24);		/* FRAMEDONETV */
> +		else
> +			irq_mask |= BIT(2) | BIT(3);	/* EVSYNC bits */
> +	}
> +	if (disable & (lcd_en | digit_en))
> +		sysc_write(ddata, dispc_offset + 0x40,
> +			   val & ~(lcd_en_mask | digit_en_mask));
> +
> +	if (manager_count <= 2)
> +		return irq_mask;
> +
> +	/* DISPC_CONTROL2 */
> +	val = sysc_read(ddata, dispc_offset + 0x238);
> +	lcd2_en = val & lcd_en_mask;
> +	if (lcd2_en)
> +		irq_mask |= BIT(22);			/* FRAMEDONE2 */
> +	if (disable && lcd2_en)
> +		sysc_write(ddata, dispc_offset + 0x238,
> +			   val & ~lcd_en_mask);
> +
> +	if (manager_count <= 3)
> +		return irq_mask;
> +
> +	/* DISPC_CONTROL3 */
> +	val = sysc_read(ddata, dispc_offset + 0x848);
> +	lcd3_en = val & lcd_en_mask;
> +	if (lcd3_en)
> +		irq_mask |= BIT(30);			/* FRAMEDONE3 */
> +	if (disable && lcd3_en)
> +		sysc_write(ddata, dispc_offset + 0x848,
> +			   val & ~lcd_en_mask);
> +
> +	return irq_mask;
> +}
> +
> +/* DSS needs child outputs disabled and SDI registers cleared for reset */
> +static void sysc_pre_reset_quirk_dss(struct sysc *ddata)
> +{
> +	const int dispc_offset = 0x1000;
> +	int error;
> +	u32 irq_mask, val;
> +
> +	/* Get enabled outputs */
> +	irq_mask = sysc_quirk_dispc(ddata, dispc_offset, false);
> +	if (!irq_mask)
> +		return;
> +
> +	/* Clear IRQSTATUS */
> +	sysc_write(ddata, 0x1000 + 0x18, irq_mask);

dispc_offset instead of 0x1000.

> +
> +	/* Disable outputs */
> +	val = sysc_quirk_dispc(ddata, dispc_offset, true);
> +
> +	/* Poll IRQSTATUS */
> +	error = readl_poll_timeout(ddata->module_va + dispc_offset + 0x18,
> +				   val, val != irq_mask, 100, 50);
> +	if (error)
> +		dev_warn(ddata->dev, "%s: timed out %08x !+ %08x\n",
> +			 __func__, val, irq_mask);
> +
> +	if (sysc_soc->soc == SOC_3430) {
> +		/* Clear DSS_SDI_CONTROL */
> +		sysc_write(ddata, dispc_offset + 0x44, 0);
> +
> +		/* Clear DSS_PLL_CONTROL */
> +		sysc_write(ddata, dispc_offset + 0x48, 0);

These are not dispc registers, but dss registers.

> +	}
> +
> +	/* Clear DSS_CONTROL to switch DSS clock sources to PRCM if not */
> +	sysc_write(ddata, dispc_offset + 0x40, 0);

Same here.

  Tomi
Tony Lindgren March 3, 2020, 3:13 p.m. UTC | #2
Hi,

* Tomi Valkeinen <tomi.valkeinen@ti.com> [200303 06:03]:
> On 24/02/2020 21:12, Tony Lindgren wrote:
> > +	/* Remap the whole module range to be able to reset dispc outputs */
> > +	devm_iounmap(ddata->dev, ddata->module_va);
> > +	ddata->module_va = devm_ioremap(ddata->dev,
> > +					ddata->module_pa,
> > +					ddata->module_size);
> 
> Why is this needed? The range is not mapped when sysc_pre_reset_quirk_dss()
> is called? This will unmap and remap twice, as this function is called
> twice. And then left mapped.

That's because by default we only ioremap the module revision, sysconfig
and sysstatus register are and provide the rest as a range for the child
nodes.

In the dss quirk case we need to tinker with registers also in the dispc
range, and at the parent dss probe time dispc has not probed yet.

We may be able to eventually move the reset quirk to dispc, but then
it won't happen in the current setup until after dss top level driver
has loaded.

We leave the module range ioremapped as we still need to access
sysconfig related registers for PM runtime.

> > +	if (!ddata->module_va)
> > +		return -EIO;
> > +
> > +	/* DISP_CONTROL */
> > +	val = sysc_read(ddata, dispc_offset + 0x40);
> 
> Defines for dss/dispc register offsets could have been copied from the
> platform display.c and used in this file.

Yeah I though about that, but decided to keep everything local to
the quirk handling. We could have them defined in some dss header
though.

> > +	/* Clear IRQSTATUS */
> > +	sysc_write(ddata, 0x1000 + 0x18, irq_mask);
> 
> dispc_offset instead of 0x1000.

OK

> > +
> > +	/* Disable outputs */
> > +	val = sysc_quirk_dispc(ddata, dispc_offset, true);
> > +
> > +	/* Poll IRQSTATUS */
> > +	error = readl_poll_timeout(ddata->module_va + dispc_offset + 0x18,
> > +				   val, val != irq_mask, 100, 50);
> > +	if (error)
> > +		dev_warn(ddata->dev, "%s: timed out %08x !+ %08x\n",
> > +			 __func__, val, irq_mask);
> > +
> > +	if (sysc_soc->soc == SOC_3430) {
> > +		/* Clear DSS_SDI_CONTROL */
> > +		sysc_write(ddata, dispc_offset + 0x44, 0);
> > +
> > +		/* Clear DSS_PLL_CONTROL */
> > +		sysc_write(ddata, dispc_offset + 0x48, 0);
> 
> These are not dispc registers, but dss registers.

Ouch. Thanks for catching this, will include in the fix.

> > +	}
> > +
> > +	/* Clear DSS_CONTROL to switch DSS clock sources to PRCM if not */
> > +	sysc_write(ddata, dispc_offset + 0x40, 0);
> 
> Same here.

OK

Regards,

Tony
Tomi Valkeinen March 3, 2020, 3:35 p.m. UTC | #3
On 03/03/2020 17:13, Tony Lindgren wrote:
> Hi,
> 
> * Tomi Valkeinen <tomi.valkeinen@ti.com> [200303 06:03]:
>> On 24/02/2020 21:12, Tony Lindgren wrote:
>>> +	/* Remap the whole module range to be able to reset dispc outputs */
>>> +	devm_iounmap(ddata->dev, ddata->module_va);
>>> +	ddata->module_va = devm_ioremap(ddata->dev,
>>> +					ddata->module_pa,
>>> +					ddata->module_size);
>>
>> Why is this needed? The range is not mapped when sysc_pre_reset_quirk_dss()
>> is called? This will unmap and remap twice, as this function is called
>> twice. And then left mapped.
> 
> That's because by default we only ioremap the module revision, sysconfig
> and sysstatus register are and provide the rest as a range for the child
> nodes.
> 
> In the dss quirk case we need to tinker with registers also in the dispc
> range, and at the parent dss probe time dispc has not probed yet.
> 
> We may be able to eventually move the reset quirk to dispc, but then
> it won't happen in the current setup until after dss top level driver
> has loaded.
> 
> We leave the module range ioremapped as we still need to access
> sysconfig related registers for PM runtime.

Ok, makes sense. I guess a minor improvement would be to unmap & remap once in 
sysc_pre_reset_quirk_dss before calling sysc_quirk_dispc.

  Tomi
Tony Lindgren March 3, 2020, 3:44 p.m. UTC | #4
* Tomi Valkeinen <tomi.valkeinen@ti.com> [200303 15:36]:
> On 03/03/2020 17:13, Tony Lindgren wrote:
> > Hi,
> > 
> > * Tomi Valkeinen <tomi.valkeinen@ti.com> [200303 06:03]:
> > > On 24/02/2020 21:12, Tony Lindgren wrote:
> > > > +	/* Remap the whole module range to be able to reset dispc outputs */
> > > > +	devm_iounmap(ddata->dev, ddata->module_va);
> > > > +	ddata->module_va = devm_ioremap(ddata->dev,
> > > > +					ddata->module_pa,
> > > > +					ddata->module_size);
> > > 
> > > Why is this needed? The range is not mapped when sysc_pre_reset_quirk_dss()
> > > is called? This will unmap and remap twice, as this function is called
> > > twice. And then left mapped.
> > 
> > That's because by default we only ioremap the module revision, sysconfig
> > and sysstatus register are and provide the rest as a range for the child
> > nodes.
> > 
> > In the dss quirk case we need to tinker with registers also in the dispc
> > range, and at the parent dss probe time dispc has not probed yet.
> > 
> > We may be able to eventually move the reset quirk to dispc, but then
> > it won't happen in the current setup until after dss top level driver
> > has loaded.
> > 
> > We leave the module range ioremapped as we still need to access
> > sysconfig related registers for PM runtime.
> 
> Ok, makes sense. I guess a minor improvement would be to unmap & remap once
> in sysc_pre_reset_quirk_dss before calling sysc_quirk_dispc.

Yeah well we'd have to sprawl the module specific quirk checks
there too then.

I thought about using the whole module range for modules with a large
IO range, but so far DSS is the only one needing a quirk hadling
covering also child modules like this.

Regards,

Tony
Tony Lindgren March 3, 2020, 3:49 p.m. UTC | #5
* Tony Lindgren <tony@atomide.com> [200303 15:14]:
> * Tomi Valkeinen <tomi.valkeinen@ti.com> [200303 06:03]:
> > On 24/02/2020 21:12, Tony Lindgren wrote:
> > > +	if (sysc_soc->soc == SOC_3430) {
> > > +		/* Clear DSS_SDI_CONTROL */
> > > +		sysc_write(ddata, dispc_offset + 0x44, 0);
> > > +
> > > +		/* Clear DSS_PLL_CONTROL */
> > > +		sysc_write(ddata, dispc_offset + 0x48, 0);
> > 
> > These are not dispc registers, but dss registers.
> 
> Ouch. Thanks for catching this, will include in the fix.
> 
> > > +	}
> > > +
> > > +	/* Clear DSS_CONTROL to switch DSS clock sources to PRCM if not */
> > > +	sysc_write(ddata, dispc_offset + 0x40, 0);
> > 
> > Same here.

Below is a fix using dispc offset for dss registers.

Regards,

Tony

8< ----------------------
From tony Mon Sep 17 00:00:00 2001
From: Tony Lindgren <tony@atomide.com>
Date: Tue, 3 Mar 2020 07:17:43 -0800
Subject: [PATCH] bus: ti-sysc: Fix wrong offset for display subsystem
 reset quirk

Commit 7324a7a0d5e2 ("bus: ti-sysc: Implement display subsystem reset
quirk") added support for DSS reset, but is using dispc offset also for
DSS also registers as reported by Tomi Valkeinen <tomi.valkeinen@ti.com>.
Also, we're not using dispc_offset for dispc IRQSTATUS register so let's
fix that too.

Fixes: 7324a7a0d5e2 ("bus: ti-sysc: Implement display subsystem reset quirk")
Reported-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Signed-off-by: Tony Lindgren <tony@atomide.com>
---
 drivers/bus/ti-sysc.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/drivers/bus/ti-sysc.c b/drivers/bus/ti-sysc.c
--- a/drivers/bus/ti-sysc.c
+++ b/drivers/bus/ti-sysc.c
@@ -1566,7 +1566,7 @@ static void sysc_pre_reset_quirk_dss(struct sysc *ddata)
 		return;
 
 	/* Clear IRQSTATUS */
-	sysc_write(ddata, 0x1000 + 0x18, irq_mask);
+	sysc_write(ddata, dispc_offset + 0x18, irq_mask);
 
 	/* Disable outputs */
 	val = sysc_quirk_dispc(ddata, dispc_offset, true);
@@ -1580,14 +1580,14 @@ static void sysc_pre_reset_quirk_dss(struct sysc *ddata)
 
 	if (sysc_soc->soc == SOC_3430) {
 		/* Clear DSS_SDI_CONTROL */
-		sysc_write(ddata, dispc_offset + 0x44, 0);
+		sysc_write(ddata, 0x44, 0);
 
 		/* Clear DSS_PLL_CONTROL */
-		sysc_write(ddata, dispc_offset + 0x48, 0);
+		sysc_write(ddata, 0x48, 0);
 	}
 
 	/* Clear DSS_CONTROL to switch DSS clock sources to PRCM if not */
-	sysc_write(ddata, dispc_offset + 0x40, 0);
+	sysc_write(ddata, 0x40, 0);
 }
 
 /* 1-wire needs module's internal clocks enabled for reset */
Tomi Valkeinen March 4, 2020, 7:02 a.m. UTC | #6
On 03/03/2020 17:49, Tony Lindgren wrote:
> * Tony Lindgren <tony@atomide.com> [200303 15:14]:
>> * Tomi Valkeinen <tomi.valkeinen@ti.com> [200303 06:03]:
>>> On 24/02/2020 21:12, Tony Lindgren wrote:
>>>> +	if (sysc_soc->soc == SOC_3430) {
>>>> +		/* Clear DSS_SDI_CONTROL */
>>>> +		sysc_write(ddata, dispc_offset + 0x44, 0);
>>>> +
>>>> +		/* Clear DSS_PLL_CONTROL */
>>>> +		sysc_write(ddata, dispc_offset + 0x48, 0);
>>>
>>> These are not dispc registers, but dss registers.
>>
>> Ouch. Thanks for catching this, will include in the fix.
>>
>>>> +	}
>>>> +
>>>> +	/* Clear DSS_CONTROL to switch DSS clock sources to PRCM if not */
>>>> +	sysc_write(ddata, dispc_offset + 0x40, 0);
>>>
>>> Same here.
> 
> Below is a fix using dispc offset for dss registers.
> 
> Regards,
> 
> Tony
> 
> 8< ----------------------
>  From tony Mon Sep 17 00:00:00 2001
> From: Tony Lindgren <tony@atomide.com>
> Date: Tue, 3 Mar 2020 07:17:43 -0800
> Subject: [PATCH] bus: ti-sysc: Fix wrong offset for display subsystem
>   reset quirk
> 
> Commit 7324a7a0d5e2 ("bus: ti-sysc: Implement display subsystem reset
> quirk") added support for DSS reset, but is using dispc offset also for
> DSS also registers as reported by Tomi Valkeinen <tomi.valkeinen@ti.com>.
> Also, we're not using dispc_offset for dispc IRQSTATUS register so let's
> fix that too.
> 
> Fixes: 7324a7a0d5e2 ("bus: ti-sysc: Implement display subsystem reset quirk")
> Reported-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
> Signed-off-by: Tony Lindgren <tony@atomide.com>
> ---
>   drivers/bus/ti-sysc.c | 8 ++++----
>   1 file changed, 4 insertions(+), 4 deletions(-)
> 
> diff --git a/drivers/bus/ti-sysc.c b/drivers/bus/ti-sysc.c
> --- a/drivers/bus/ti-sysc.c
> +++ b/drivers/bus/ti-sysc.c
> @@ -1566,7 +1566,7 @@ static void sysc_pre_reset_quirk_dss(struct sysc *ddata)
>   		return;
>   
>   	/* Clear IRQSTATUS */
> -	sysc_write(ddata, 0x1000 + 0x18, irq_mask);
> +	sysc_write(ddata, dispc_offset + 0x18, irq_mask);
>   
>   	/* Disable outputs */
>   	val = sysc_quirk_dispc(ddata, dispc_offset, true);
> @@ -1580,14 +1580,14 @@ static void sysc_pre_reset_quirk_dss(struct sysc *ddata)
>   
>   	if (sysc_soc->soc == SOC_3430) {
>   		/* Clear DSS_SDI_CONTROL */
> -		sysc_write(ddata, dispc_offset + 0x44, 0);
> +		sysc_write(ddata, 0x44, 0);
>   
>   		/* Clear DSS_PLL_CONTROL */
> -		sysc_write(ddata, dispc_offset + 0x48, 0);
> +		sysc_write(ddata, 0x48, 0);
>   	}
>   
>   	/* Clear DSS_CONTROL to switch DSS clock sources to PRCM if not */
> -	sysc_write(ddata, dispc_offset + 0x40, 0);
> +	sysc_write(ddata, 0x40, 0);
>   }
>   
>   /* 1-wire needs module's internal clocks enabled for reset */
> 

Reviewed-by: Tomi Valkeinen <tomi.valkeinen@ti.com>

  Tomi

Patch
diff mbox series

diff --git a/drivers/bus/ti-sysc.c b/drivers/bus/ti-sysc.c
--- a/drivers/bus/ti-sysc.c
+++ b/drivers/bus/ti-sysc.c
@@ -1303,11 +1303,11 @@  static const struct sysc_revision_quirk sysc_revision_quirks[] = {
 	SYSC_QUIRK("dcan", 0x48480000, 0x20, -ENODEV, -ENODEV, 0xa3170504, 0xffffffff,
 		   SYSC_QUIRK_CLKDM_NOAUTO),
 	SYSC_QUIRK("dss", 0x4832a000, 0, 0x10, 0x14, 0x00000020, 0xffffffff,
-		   SYSC_QUIRK_OPT_CLKS_IN_RESET),
+		   SYSC_QUIRK_OPT_CLKS_IN_RESET | SYSC_MODULE_QUIRK_DSS_RESET),
 	SYSC_QUIRK("dss", 0x58000000, 0, -ENODEV, 0x14, 0x00000040, 0xffffffff,
-		   SYSC_QUIRK_OPT_CLKS_IN_RESET),
+		   SYSC_QUIRK_OPT_CLKS_IN_RESET | SYSC_MODULE_QUIRK_DSS_RESET),
 	SYSC_QUIRK("dss", 0x58000000, 0, -ENODEV, 0x14, 0x00000061, 0xffffffff,
-		   SYSC_QUIRK_OPT_CLKS_IN_RESET),
+		   SYSC_QUIRK_OPT_CLKS_IN_RESET | SYSC_MODULE_QUIRK_DSS_RESET),
 	SYSC_QUIRK("dwc3", 0x48880000, 0, 0x10, -ENODEV, 0x500a0200, 0xffffffff,
 		   SYSC_QUIRK_CLKDM_NOAUTO),
 	SYSC_QUIRK("dwc3", 0x488c0000, 0, 0x10, -ENODEV, 0x500a0200, 0xffffffff,
@@ -1468,6 +1468,128 @@  static void sysc_init_revision_quirks(struct sysc *ddata)
 	}
 }
 
+/*
+ * DSS needs dispc outputs disabled to reset modules. Returns mask of
+ * enabled DSS interrupts. Eventually we may be able to do this on
+ * dispc init rather than top-level DSS init.
+ */
+static u32 sysc_quirk_dispc(struct sysc *ddata, int dispc_offset,
+			    bool disable)
+{
+	bool lcd_en, digit_en, lcd2_en = false, lcd3_en = false;
+	const int lcd_en_mask = BIT(0), digit_en_mask = BIT(1);
+	int manager_count;
+	bool framedonetv_irq;
+	u32 val, irq_mask = 0;
+
+	switch (sysc_soc->soc) {
+	case SOC_2420 ... SOC_3630:
+		manager_count = 2;
+		framedonetv_irq = false;
+		break;
+	case SOC_4430 ... SOC_4470:
+		manager_count = 3;
+		break;
+	case SOC_5430:
+	case SOC_DRA7:
+		manager_count = 4;
+		break;
+	case SOC_AM4:
+		manager_count = 1;
+		break;
+	case SOC_UNKNOWN:
+	default:
+		return 0;
+	};
+
+	/* Remap the whole module range to be able to reset dispc outputs */
+	devm_iounmap(ddata->dev, ddata->module_va);
+	ddata->module_va = devm_ioremap(ddata->dev,
+					ddata->module_pa,
+					ddata->module_size);
+	if (!ddata->module_va)
+		return -EIO;
+
+	/* DISP_CONTROL */
+	val = sysc_read(ddata, dispc_offset + 0x40);
+	lcd_en = val & lcd_en_mask;
+	digit_en = val & digit_en_mask;
+	if (lcd_en)
+		irq_mask |= BIT(0);			/* FRAMEDONE */
+	if (digit_en) {
+		if (framedonetv_irq)
+			irq_mask |= BIT(24);		/* FRAMEDONETV */
+		else
+			irq_mask |= BIT(2) | BIT(3);	/* EVSYNC bits */
+	}
+	if (disable & (lcd_en | digit_en))
+		sysc_write(ddata, dispc_offset + 0x40,
+			   val & ~(lcd_en_mask | digit_en_mask));
+
+	if (manager_count <= 2)
+		return irq_mask;
+
+	/* DISPC_CONTROL2 */
+	val = sysc_read(ddata, dispc_offset + 0x238);
+	lcd2_en = val & lcd_en_mask;
+	if (lcd2_en)
+		irq_mask |= BIT(22);			/* FRAMEDONE2 */
+	if (disable && lcd2_en)
+		sysc_write(ddata, dispc_offset + 0x238,
+			   val & ~lcd_en_mask);
+
+	if (manager_count <= 3)
+		return irq_mask;
+
+	/* DISPC_CONTROL3 */
+	val = sysc_read(ddata, dispc_offset + 0x848);
+	lcd3_en = val & lcd_en_mask;
+	if (lcd3_en)
+		irq_mask |= BIT(30);			/* FRAMEDONE3 */
+	if (disable && lcd3_en)
+		sysc_write(ddata, dispc_offset + 0x848,
+			   val & ~lcd_en_mask);
+
+	return irq_mask;
+}
+
+/* DSS needs child outputs disabled and SDI registers cleared for reset */
+static void sysc_pre_reset_quirk_dss(struct sysc *ddata)
+{
+	const int dispc_offset = 0x1000;
+	int error;
+	u32 irq_mask, val;
+
+	/* Get enabled outputs */
+	irq_mask = sysc_quirk_dispc(ddata, dispc_offset, false);
+	if (!irq_mask)
+		return;
+
+	/* Clear IRQSTATUS */
+	sysc_write(ddata, 0x1000 + 0x18, irq_mask);
+
+	/* Disable outputs */
+	val = sysc_quirk_dispc(ddata, dispc_offset, true);
+
+	/* Poll IRQSTATUS */
+	error = readl_poll_timeout(ddata->module_va + dispc_offset + 0x18,
+				   val, val != irq_mask, 100, 50);
+	if (error)
+		dev_warn(ddata->dev, "%s: timed out %08x !+ %08x\n",
+			 __func__, val, irq_mask);
+
+	if (sysc_soc->soc == SOC_3430) {
+		/* Clear DSS_SDI_CONTROL */
+		sysc_write(ddata, dispc_offset + 0x44, 0);
+
+		/* Clear DSS_PLL_CONTROL */
+		sysc_write(ddata, dispc_offset + 0x48, 0);
+	}
+
+	/* Clear DSS_CONTROL to switch DSS clock sources to PRCM if not */
+	sysc_write(ddata, dispc_offset + 0x40, 0);
+}
+
 /* 1-wire needs module's internal clocks enabled for reset */
 static void sysc_pre_reset_quirk_hdq1w(struct sysc *ddata)
 {
@@ -1606,6 +1728,9 @@  static void sysc_init_module_quirks(struct sysc *ddata)
 	if (ddata->cfg.quirks & SYSC_MODULE_QUIRK_AESS)
 		ddata->module_enable_quirk = sysc_module_enable_quirk_aess;
 
+	if (ddata->cfg.quirks & SYSC_MODULE_QUIRK_DSS_RESET)
+		ddata->pre_reset_quirk = sysc_pre_reset_quirk_dss;
+
 	if (ddata->cfg.quirks & SYSC_MODULE_QUIRK_RTC_UNLOCK) {
 		ddata->module_unlock_quirk = sysc_module_unlock_quirk_rtc;
 		ddata->module_lock_quirk = sysc_module_lock_quirk_rtc;
diff --git a/include/linux/platform_data/ti-sysc.h b/include/linux/platform_data/ti-sysc.h
--- a/include/linux/platform_data/ti-sysc.h
+++ b/include/linux/platform_data/ti-sysc.h
@@ -49,6 +49,7 @@  struct sysc_regbits {
 	s8 emufree_shift;
 };
 
+#define SYSC_MODULE_QUIRK_DSS_RESET	BIT(23)
 #define SYSC_MODULE_QUIRK_RTC_UNLOCK	BIT(22)
 #define SYSC_QUIRK_CLKDM_NOAUTO		BIT(21)
 #define SYSC_QUIRK_FORCE_MSTANDBY	BIT(20)