Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

fbdev: sh_mobile_hdmi: Re-init regs before irq re-enable on resume

When the PM domain containing the HDMI hardware block is powered down,
the HDMI register values (incl. interrupt polarity settings) are lost.
During resume, after powering up the PM domain, interrupts are
re-enabled, and an interrupt storm happens due to incorrect interrupt
polarity settings:

irq 163: nobody cared (try booting with the "irqpoll" option)
...
Disabling IRQ #163

To fix this, re-initialize the interrupt polarity settings, and the
htop1 register block (if present), during resume.

As the .suspend_noirq() and .resume_noirq() callbacks are not called
when using the generic PM domain, the normal .resume() callback is used,
and the device interrupt needs to be disabled/enabled manually.

This fixes resume from s2ram with power down of the A4MP PM domain on
r8a7740/Armadillo.

Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>

authored by

Geert Uytterhoeven and committed by
Tomi Valkeinen
a00d91ea 5b789da8

+42 -2
+42 -2
drivers/video/fbdev/sh_mobile_hdmi.c
··· 281 281 u8 edid_block_addr; 282 282 u8 edid_segment_nr; 283 283 u8 edid_blocks; 284 + int irq; 284 285 struct clk *hdmi_clk; 285 286 struct device *dev; 286 287 struct delayed_work edid_work; ··· 1300 1299 hdmi->dev = &pdev->dev; 1301 1300 hdmi->entity.owner = THIS_MODULE; 1302 1301 hdmi->entity.ops = &sh_hdmi_ops; 1302 + hdmi->irq = irq; 1303 1303 1304 1304 hdmi->hdmi_clk = clk_get(&pdev->dev, "ick"); 1305 1305 if (IS_ERR(hdmi->hdmi_clk)) { ··· 1417 1415 { 1418 1416 struct sh_hdmi *hdmi = entity_to_sh_hdmi(platform_get_drvdata(pdev)); 1419 1417 struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 1420 - int irq = platform_get_irq(pdev, 0); 1421 1418 1422 1419 snd_soc_unregister_codec(&pdev->dev); 1423 1420 1424 1421 /* No new work will be scheduled, wait for running ISR */ 1425 - free_irq(irq, hdmi); 1422 + free_irq(hdmi->irq, hdmi); 1426 1423 /* Wait for already scheduled work */ 1427 1424 cancel_delayed_work_sync(&hdmi->edid_work); 1428 1425 pm_runtime_put(&pdev->dev); ··· 1436 1435 return 0; 1437 1436 } 1438 1437 1438 + static int sh_hdmi_suspend(struct device *dev) 1439 + { 1440 + struct platform_device *pdev = to_platform_device(dev); 1441 + struct sh_hdmi *hdmi = entity_to_sh_hdmi(platform_get_drvdata(pdev)); 1442 + 1443 + disable_irq(hdmi->irq); 1444 + /* Wait for already scheduled work */ 1445 + cancel_delayed_work_sync(&hdmi->edid_work); 1446 + return 0; 1447 + } 1448 + 1449 + static int sh_hdmi_resume(struct device *dev) 1450 + { 1451 + struct platform_device *pdev = to_platform_device(dev); 1452 + struct sh_mobile_hdmi_info *pdata = dev_get_platdata(dev); 1453 + struct sh_hdmi *hdmi = entity_to_sh_hdmi(platform_get_drvdata(pdev)); 1454 + 1455 + /* Re-init interrupt polarity */ 1456 + if (pdata->flags & HDMI_OUTPUT_PUSH_PULL) 1457 + hdmi_bit_set(hdmi, 0x02, 0x02, HDMI_SYSTEM_CTRL); 1458 + 1459 + if (pdata->flags & HDMI_OUTPUT_POLARITY_HI) 1460 + hdmi_bit_set(hdmi, 0x01, 0x01, HDMI_SYSTEM_CTRL); 1461 + 1462 + /* Re-init htop1 */ 1463 + if (hdmi->htop1) 1464 + sh_hdmi_htop1_init(hdmi); 1465 + 1466 + /* Now it's safe to enable interrupts again */ 1467 + enable_irq(hdmi->irq); 1468 + return 0; 1469 + } 1470 + 1471 + static const struct dev_pm_ops sh_hdmi_pm_ops = { 1472 + .suspend = sh_hdmi_suspend, 1473 + .resume = sh_hdmi_resume, 1474 + }; 1475 + 1439 1476 static struct platform_driver sh_hdmi_driver = { 1440 1477 .remove = __exit_p(sh_hdmi_remove), 1441 1478 .driver = { 1442 1479 .name = "sh-mobile-hdmi", 1480 + .pm = &sh_hdmi_pm_ops, 1443 1481 }, 1444 1482 }; 1445 1483