Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 150
[RFC DO NOT MERGE]SOF D0ix support#1154
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
9a64948693ce2598774abb5f97d6809b403eb9b77525ee229bfc94c1c8ce226f164f74c0c413d4bfd3bf0db26fded4cd5eFile filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -402,9 +402,22 @@ static int sof_probe_continue(struct snd_sof_dev *sdev) | ||
| dev_dbg(sdev->dev, "created machine %s\n", | ||
| dev_name(&plat_data->pdev_mach->dev)); | ||
| plat_data->pdev_d0ix = | ||
| platform_device_register_data(sdev->dev, "sof_d0ix", | ||
| PLATFORM_DEVID_NONE, sdev, | ||
| sizeof(*sdev)); | ||
| if (IS_ERR(plat_data->pdev_d0ix)) { | ||
| ret = PTR_ERR(plat_data->pdev_d0ix); | ||
| goto fw_run_err; | ||
| } | ||
| if (plat_data->sof_probe_complete) | ||
| plat_data->sof_probe_complete(sdev->dev); | ||
| /* initialize default D0 sub-state */ | ||
| sdev->d0_substate = SOF_DSP_D0I0; | ||
Member There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would do the initialization earlier, since the sof_probe_complete is where you typically enable pm_runtime Author There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. makes sense. | ||
| return 0; | ||
| #if !IS_ENABLED(CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE) | ||
| @@ -513,6 +526,10 @@ int snd_sof_device_remove(struct device *dev) | ||
| snd_sof_free_debug(sdev); | ||
| snd_sof_free_trace(sdev); | ||
| /* Unregister D0Ix power management device */ | ||
| if (!IS_ERR_OR_NULL(pdata->pdev_d0ix)) | ||
| platform_device_unregister(pdata->pdev_d0ix); | ||
| /* | ||
| * Unregister machine driver. This will unbind the snd_card which | ||
| * will remove the component driver and unload the topology | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -97,6 +97,7 @@ const struct snd_sof_dsp_ops sof_apl_ops = { | ||
| .runtime_resume = hda_dsp_runtime_resume, | ||
| .runtime_idle = hda_dsp_runtime_idle, | ||
| .set_hw_params_upon_resume = hda_dsp_set_hw_params_upon_resume, | ||
| .set_state = hda_dsp_set_state, | ||
Collaborator There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @keyonjie incorrect commit title and message. Please align with cnl commit. Author There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks. | ||
| }; | ||
| EXPORT_SYMBOL(sof_apl_ops); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -282,6 +282,52 @@ void hda_dsp_ipc_int_disable(struct snd_sof_dev *sdev) | ||
| HDA_DSP_REG_HIPCCTL_BUSY | HDA_DSP_REG_HIPCCTL_DONE, 0); | ||
| } | ||
| static void hda_dsp_wait_d0i3c_done(struct snd_sof_dev *sdev, int *retry) | ||
Member There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this handles retries so should return an error if there is no change after all the retries | ||
| { | ||
| struct hdac_bus *bus = sof_to_bus(sdev); | ||
| u8 reg; | ||
| reg = snd_hdac_chip_readb(bus, VS_D0I3C); | ||
| while ((reg & SOF_HDA_VS_D0I3C_CIP) && --*retry) { | ||
| usleep_range(10, 15); | ||
| reg = snd_hdac_chip_readb(bus, VS_D0I3C); | ||
Collaborator There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This pattern occurs so often - when you have to perform a calculation to verify a condition and then repeat an action while the condition holds, so you end up with that maybe we have to add an exception to kernel coding style guidelines to allow but for now we have to do the former... Author There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So what's your suggestion here @lyakh ? Collaborator There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @keyonjie write to LKML to suggest add this exception to the Coding Style :-) | ||
| } | ||
| } | ||
| int hda_dsp_set_state(struct snd_sof_dev *sdev, | ||
Member There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. set_power_state | ||
| enum sof_d0_substate d0_substate) | ||
| { | ||
| struct hdac_bus *bus = sof_to_bus(sdev); | ||
| u8 value; | ||
| int retry = 50; | ||
| /* Write to D0I3C after Command-In-Progress bit is cleared */ | ||
| hda_dsp_wait_d0i3c_done(sdev, &retry); | ||
| ||
| if (!retry) { | ||
| dev_err(bus->dev, "CIP timeout before update D0I3C!\n"); | ||
| return -ETIMEDOUT; | ||
Collaborator There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. so here you'll just propagate the error back to the caller | ||
| } | ||
| /* Update D0I3C register */ | ||
| value = d0_substate == SOF_DSP_D0I3 ? SOF_HDA_VS_D0I3C_I3 : 0; | ||
| snd_hdac_chip_updateb(bus, VS_D0I3C, SOF_HDA_VS_D0I3C_I3, value); | ||
| /* Wait for cmd in progress to be cleared before exiting the function */ | ||
| retry = 50; | ||
| hda_dsp_wait_d0i3c_done(sdev, &retry); | ||
| if (!retry) { | ||
| dev_err(bus->dev, "CIP timeout after D0I3C updated!\n"); | ||
| return -ETIMEDOUT; | ||
| } | ||
Member There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. might be worth having a helper to read the register, it's not very nice as is. Author There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. OK, I can split it out, make something like hda_dsp_wait_d0i3c_done(u32 retry). | ||
| dev_dbg(bus->dev, "D0I3C updated, register = 0x%x\n", | ||
| snd_hdac_chip_readb(bus, VS_D0I3C)); | ||
| return 0; | ||
| } | ||
| static int hda_suspend(struct snd_sof_dev *sdev, bool runtime_suspend) | ||
| { | ||
| struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -65,6 +65,10 @@ | ||
| #define SOF_HDA_PPCTL_PIE BIT(31) | ||
| #define SOF_HDA_PPCTL_GPROCEN BIT(30) | ||
| /* D0I3C Register fields */ | ||
| #define SOF_HDA_VS_D0I3C_CIP BIT(0) /* Command-In-Progress */ | ||
| #define SOF_HDA_VS_D0I3C_I3 BIT(2) /* D0i3 enable bit */ | ||
| /* DPIB entry size: 8 Bytes = 2 DWords */ | ||
| #define SOF_HDA_DPIB_ENTRY_SIZE 0x8 | ||
| @@ -452,6 +456,9 @@ int hda_dsp_core_reset_power_down(struct snd_sof_dev *sdev, | ||
| void hda_dsp_ipc_int_enable(struct snd_sof_dev *sdev); | ||
| void hda_dsp_ipc_int_disable(struct snd_sof_dev *sdev); | ||
| int hda_dsp_set_state(struct snd_sof_dev *sdev, | ||
Member There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggestion: Author There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. OK, thanks. Author There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry, @plbossart you didn't get my point here. This helper is only aimed to set d0i3c register, not including sending IPC to FW, so the CTX_SAVE/RESTORE should not belong to here. | ||
| enum sof_d0_substate d0_substate); | ||
| int hda_dsp_suspend(struct snd_sof_dev *sdev); | ||
| int hda_dsp_resume(struct snd_sof_dev *sdev); | ||
| int hda_dsp_runtime_suspend(struct snd_sof_dev *sdev); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -193,6 +193,15 @@ static inline int snd_sof_dsp_set_clk(struct snd_sof_dev *sdev, u32 freq) | ||
| return 0; | ||
| } | ||
| static inline int snd_sof_dsp_set_state(struct snd_sof_dev *sdev, | ||
Member There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. set_power_state Author There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. OK. | ||
| enum sof_d0_substate d0_substate) | ||
| { | ||
| if (sof_ops(sdev)->set_state) | ||
| return sof_ops(sdev)->set_state(sdev, d0_substate); | ||
| return 0; | ||
| } | ||
| /* debug */ | ||
| static inline void snd_sof_dsp_dbg_dump(struct snd_sof_dev *sdev, u32 flags) | ||
| { | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -422,6 +422,7 @@ static int sof_pcm_open(struct snd_pcm_substream *substream) | ||
| struct snd_soc_component *component = | ||
| snd_soc_rtdcom_lookup(rtd, DRV_NAME); | ||
| struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); | ||
| struct snd_sof_pdata *plat_data = sdev->pdata; | ||
| struct snd_sof_pcm *spcm; | ||
| struct snd_soc_tplg_stream_caps *caps; | ||
| int ret; | ||
| @@ -486,10 +487,16 @@ static int sof_pcm_open(struct snd_pcm_substream *substream) | ||
| spcm->prepared[substream->stream] = false; | ||
| ret = snd_sof_pcm_platform_open(sdev, substream); | ||
| if (ret < 0) | ||
| if (ret < 0) { | ||
| dev_err(sdev->dev, "error: pcm open failed %d\n", ret); | ||
| return ret; | ||
| } | ||
| if (!spcm->stream[substream->stream].d0i3_compatible) | ||
| pm_runtime_get_sync(&plat_data->pdev_d0ix->dev); | ||
Member There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. no, this is officially horrible. NAK NAK NAK | ||
| return 0; | ||
| return ret; | ||
| } | ||
| static int sof_pcm_close(struct snd_pcm_substream *substream) | ||
| @@ -498,6 +505,7 @@ static int sof_pcm_close(struct snd_pcm_substream *substream) | ||
| struct snd_soc_component *component = | ||
| snd_soc_rtdcom_lookup(rtd, DRV_NAME); | ||
| struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); | ||
| struct snd_sof_pdata *plat_data = sdev->pdata; | ||
| struct snd_sof_pcm *spcm; | ||
| int err; | ||
| @@ -522,6 +530,11 @@ static int sof_pcm_close(struct snd_pcm_substream *substream) | ||
| */ | ||
| } | ||
| if (!spcm->stream[substream->stream].d0i3_compatible) { | ||
| pm_runtime_mark_last_busy(&plat_data->pdev_d0ix->dev); | ||
| pm_runtime_put_autosuspend(&plat_data->pdev_d0ix->dev); | ||
| } | ||
Collaborator There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I actually would rather comment to the commit message of this patch - it looks very much like re-inventing runtime-PM to me, can we convert this to use the standard RT-PM APIs? Member There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yes, it feels like re-invention but no it's not. pm_runtime is only about DO-D3. When the device is in D0i3 (which is platform-specific), pm_runtime considers the device as 'active'. So indeed we need to implement a custom usage/refcount and decision making. It's not necessarily elegant but it's not generic either so the ability to rely on a common framework for stuff that's hardware/firmware specific is limited.
| ||
| return 0; | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -326,6 +326,9 @@ static int sof_resume(struct device *dev, bool runtime_resume) | ||
| "error: ctx_restore ipc error during resume %d\n", | ||
| ret); | ||
| /* initialize default D0 sub-state */ | ||
| sdev->d0_substate = SOF_DSP_D0I0; | ||
| return ret; | ||
| } | ||
| @@ -419,3 +422,35 @@ int snd_sof_suspend(struct device *dev) | ||
| return sof_suspend(dev, false); | ||
| } | ||
| EXPORT_SYMBOL(snd_sof_suspend); | ||
| int snd_sof_set_dsp_state(struct snd_sof_dev *sdev, | ||
| enum sof_d0_substate d0_substate) | ||
| { | ||
| int ret; | ||
| dev_dbg(sdev->dev, "setting ADSP D0 substate:%d\n", d0_substate); | ||
| /* Todo: destroy/reload unused pipelines/widgets in FW */ | ||
| /* do platform specific set_state */ | ||
| ret = snd_sof_dsp_set_state(sdev, d0_substate); | ||
| if (ret < 0) { | ||
| dev_err(sdev->dev, | ||
| "error: d0i3_exit ipc error %d\n", ret); | ||
Member There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. remove d0i3, this this set_power_state ipc error | ||
| return ret; | ||
| } | ||
| /* sending set_state IPC */ | ||
| ret = sof_send_pm_ipc(sdev, SOF_IPC_PM_SET_STATE | d0_substate); | ||
Member There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. and isn't it the other way around, you first send the IPC then set the D0i3C bit? | ||
| if (ret < 0) { | ||
| dev_err(sdev->dev, | ||
| "error: d0i3_exit ipc error %d\n", ret); | ||
Member There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same here | ||
| return ret; | ||
| } | ||
| /* update dsp D0 sub-state */ | ||
| sdev->d0_substate = d0_substate; | ||
| return 0; | ||
| } | ||
| EXPORT_SYMBOL(snd_sof_set_dsp_state); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,84 @@ | ||
| // SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) | ||
| // | ||
| // This file is provided under a dual BSD/GPLv2 license. When using or | ||
| // redistributing this file, you may do so under either license. | ||
| // | ||
| // Copyright(c) 2019 Intel Corporation. All rights reserved. | ||
| // | ||
| // Author: Keyon Jie <yang.jie@linux.intel.com> | ||
| // | ||
| #include <linux/device.h> | ||
| #include <linux/module.h> | ||
| #include <linux/platform_device.h> | ||
| #include <linux/pm_runtime.h> | ||
| #include <sound/sof.h> | ||
| #include "ops.h" | ||
| #ifdef CONFIG_PM | ||
| static int sof_d0ix_suspend(struct device *dev) | ||
| { | ||
| struct snd_sof_dev *sdev = dev_get_platdata(dev); | ||
| dev_dbg(dev, "Suspending to D0i3...\n"); | ||
| return snd_sof_set_dsp_state(sdev, SOF_DSP_D0I3); | ||
| } | ||
| static int sof_d0ix_resume(struct device *dev) | ||
| { | ||
| struct snd_sof_dev *sdev = dev_get_platdata(dev); | ||
| dev_dbg(dev, "Resuming from D0i3...\n"); | ||
| return snd_sof_set_dsp_state(sdev, SOF_DSP_D0I0); | ||
| } | ||
| #else | ||
| #define sof_d0ix_suspend NULL | ||
| #define sof_d0ix_resume NULL | ||
| #endif | ||
| static int pm_d0ix_probe(struct platform_device *pdev) | ||
| { | ||
| /* allow runtime_pm */ | ||
| pm_runtime_set_autosuspend_delay(&pdev->dev, SND_SOF_D0I3_DELAY_MS); | ||
| pm_runtime_use_autosuspend(&pdev->dev); | ||
| pm_runtime_allow(&pdev->dev); | ||
| pm_runtime_enable(&pdev->dev); | ||
| pm_runtime_mark_last_busy(&pdev->dev); | ||
| pm_runtime_put_noidle(&pdev->dev); | ||
| dev_dbg(&pdev->dev, "%s done.\n", __func__); | ||
| return 0; | ||
| } | ||
| static int pm_d0ix_remove(struct platform_device *pdev) | ||
| { | ||
| return 0; | ||
| } | ||
Collaborator There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @keyonjie is remove() really needed if you dont do anything here? | ||
| const struct dev_pm_ops d0ix_pm_ops = { | ||
| SET_RUNTIME_PM_OPS(sof_d0ix_suspend, sof_d0ix_resume, NULL) | ||
| }; | ||
| static struct platform_driver sof_d0ix_driver = { | ||
| .driver = { | ||
| .name = "sof_d0ix", | ||
| .pm = &d0ix_pm_ops, | ||
| }, | ||
| .probe = pm_d0ix_probe, | ||
| .remove = pm_d0ix_remove, | ||
| }; | ||
| module_platform_driver(sof_d0ix_driver); | ||
Member There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Adding platform devices is not really something we want. We cannot control when the probe happens and in general it adds more issues. I am inches away from stating 'NAK' Collaborator There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @keyonjie I'm also not too keen on adding a platform device just for the purpose of PM. It doesn't seem to be very logical to me. I'll comment with more explanations where @plbossart has explained why RTPM isn't the right approach here. | ||
| MODULE_DESCRIPTION("SOF D0Ix driver"); | ||
| MODULE_AUTHOR("Keyon Jie"); | ||
| MODULE_LICENSE("Dual BSD/GPL"); | ||
| MODULE_ALIAS("platform:sof_d0ix"); | ||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SET_POWER_STATE