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

ASoC: hdac_hdmi: Fix codec power state in S3 during playback

If the system enters S3 during a playback, codec power needs to
be turned OFF during suspend and restored during resume. With
this patch the AFG node is set to D3 and codec power is turned
OFF during controller suspend call.

During resume, the codec power is left in ON state if the
playback was in progress while suspending.

Also setting power state for AFG node is optimized. With this the
loop with timeout is removed and codec_read is used instead.

Signed-off-by: Subhransu S. Prusty <subhransu.s.prusty@intel.com>
Signed-off-by: Vinod Koul <vinod.koul@intel.com>
Signed-off-by: Mark Brown <broonie@kernel.org>

authored by

Subhransu S. Prusty and committed by
Mark Brown
1b377ccd 0fee1798

+38 -45
+38 -45
sound/soc/codecs/hdac_hdmi.c
··· 1420 1420 } 1421 1421 1422 1422 #ifdef CONFIG_PM 1423 + static int hdmi_codec_prepare(struct device *dev) 1424 + { 1425 + struct hdac_ext_device *edev = to_hda_ext_device(dev); 1426 + struct hdac_device *hdac = &edev->hdac; 1427 + 1428 + pm_runtime_get_sync(&edev->hdac.dev); 1429 + 1430 + /* 1431 + * Power down afg. 1432 + * codec_read is preferred over codec_write to set the power state. 1433 + * This way verb is send to set the power state and response 1434 + * is received. So setting power state is ensured without using loop 1435 + * to read the state. 1436 + */ 1437 + snd_hdac_codec_read(hdac, hdac->afg, 0, AC_VERB_SET_POWER_STATE, 1438 + AC_PWRST_D3); 1439 + 1440 + return 0; 1441 + } 1442 + 1423 1443 static void hdmi_codec_complete(struct device *dev) 1424 1444 { 1425 1445 struct hdac_ext_device *edev = to_hda_ext_device(dev); 1426 1446 struct hdac_hdmi_priv *hdmi = edev->private_data; 1427 1447 struct hdac_hdmi_pin *pin; 1428 1448 struct hdac_device *hdac = &edev->hdac; 1429 - struct hdac_bus *bus = hdac->bus; 1430 - int err; 1431 - unsigned long timeout; 1449 + 1450 + /* Power up afg */ 1451 + snd_hdac_codec_read(hdac, hdac->afg, 0, AC_VERB_SET_POWER_STATE, 1452 + AC_PWRST_D0); 1432 1453 1433 1454 hdac_hdmi_skl_enable_all_pins(&edev->hdac); 1434 1455 hdac_hdmi_skl_enable_dp12(&edev->hdac); 1435 - 1436 - /* Power up afg */ 1437 - if (!snd_hdac_check_power_state(hdac, hdac->afg, AC_PWRST_D0)) { 1438 - 1439 - snd_hdac_codec_write(hdac, hdac->afg, 0, 1440 - AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 1441 - 1442 - /* Wait till power state is set to D0 */ 1443 - timeout = jiffies + msecs_to_jiffies(1000); 1444 - while (!snd_hdac_check_power_state(hdac, hdac->afg, AC_PWRST_D0) 1445 - && time_before(jiffies, timeout)) { 1446 - msleep(50); 1447 - } 1448 - } 1449 1456 1450 1457 /* 1451 1458 * As the ELD notify callback request is not entertained while the ··· 1462 1455 list_for_each_entry(pin, &hdmi->pin_list, head) 1463 1456 hdac_hdmi_present_sense(pin, 1); 1464 1457 1465 - /* 1466 - * Codec power is turned ON during controller resume. 1467 - * Turn it OFF here 1468 - */ 1469 - err = snd_hdac_display_power(bus, false); 1470 - if (err < 0) { 1471 - dev_err(bus->dev, 1472 - "Cannot turn OFF display power on i915, err: %d\n", 1473 - err); 1474 - } 1458 + pm_runtime_put_sync(&edev->hdac.dev); 1475 1459 } 1476 1460 #else 1461 + #define hdmi_codec_prepare NULL 1477 1462 #define hdmi_codec_complete NULL 1478 1463 #endif 1479 1464 ··· 1556 1557 struct hdac_ext_device *edev = to_hda_ext_device(dev); 1557 1558 struct hdac_device *hdac = &edev->hdac; 1558 1559 struct hdac_bus *bus = hdac->bus; 1559 - unsigned long timeout; 1560 1560 int err; 1561 1561 1562 1562 dev_dbg(dev, "Enter: %s\n", __func__); ··· 1564 1566 if (!bus) 1565 1567 return 0; 1566 1568 1567 - /* Power down afg */ 1568 - if (!snd_hdac_check_power_state(hdac, hdac->afg, AC_PWRST_D3)) { 1569 - snd_hdac_codec_write(hdac, hdac->afg, 0, 1570 - AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 1571 - 1572 - /* Wait till power state is set to D3 */ 1573 - timeout = jiffies + msecs_to_jiffies(1000); 1574 - while (!snd_hdac_check_power_state(hdac, hdac->afg, AC_PWRST_D3) 1575 - && time_before(jiffies, timeout)) { 1576 - 1577 - msleep(50); 1578 - } 1579 - } 1580 - 1569 + /* 1570 + * Power down afg. 1571 + * codec_read is preferred over codec_write to set the power state. 1572 + * This way verb is send to set the power state and response 1573 + * is received. So setting power state is ensured without using loop 1574 + * to read the state. 1575 + */ 1576 + snd_hdac_codec_read(hdac, hdac->afg, 0, AC_VERB_SET_POWER_STATE, 1577 + AC_PWRST_D3); 1581 1578 err = snd_hdac_display_power(bus, false); 1582 1579 if (err < 0) { 1583 1580 dev_err(bus->dev, "Cannot turn on display power on i915\n"); ··· 1605 1612 hdac_hdmi_skl_enable_dp12(&edev->hdac); 1606 1613 1607 1614 /* Power up afg */ 1608 - if (!snd_hdac_check_power_state(hdac, hdac->afg, AC_PWRST_D0)) 1609 - snd_hdac_codec_write(hdac, hdac->afg, 0, 1610 - AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 1615 + snd_hdac_codec_read(hdac, hdac->afg, 0, AC_VERB_SET_POWER_STATE, 1616 + AC_PWRST_D0); 1611 1617 1612 1618 return 0; 1613 1619 } ··· 1617 1625 1618 1626 static const struct dev_pm_ops hdac_hdmi_pm = { 1619 1627 SET_RUNTIME_PM_OPS(hdac_hdmi_runtime_suspend, hdac_hdmi_runtime_resume, NULL) 1628 + .prepare = hdmi_codec_prepare, 1620 1629 .complete = hdmi_codec_complete, 1621 1630 }; 1622 1631