ASoC: SOF: Intel: hda: make DSPless mode work with DSP disabled in BIOS

When the DSP is disabled in the BIOS, the DSP_BAR and PP_BAR cannot be
accessed.

One possible objection noted in initial reviews is that this patch
adds a number of branches. However the number of branches is actually
limited in probe/suspend/resume routines mostly, so there isn't really
a degradation in terms of readability and maintainability. Adding yet
another level of abstraction/ops/callbacks would increase complexity
and not really help in terms of code reuse or readability and
maintainability. A split between controller and DSP driver would be
even more invasive.

Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Reviewed-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
Reviewed-by: Péter Ujfalusi <peter.ujfalusi@linux.intel.com>
Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
Link: https://lore.kernel.org/r/20230404092115.27949-7-peter.ujfalusi@linux.intel.com
Signed-off-by: Mark Brown <broonie@kernel.org>
This commit is contained in:
Pierre-Louis Bossart 2023-04-04 12:21:09 +03:00 committed by Mark Brown
parent 1f7b5d52be
commit 9fc6786f54
No known key found for this signature in database
GPG Key ID: 24D68B725D5487D0
4 changed files with 107 additions and 45 deletions

View File

@ -321,6 +321,9 @@ void hda_dsp_ipc_int_enable(struct snd_sof_dev *sdev)
struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
const struct sof_intel_dsp_desc *chip = hda->desc;
if (sdev->dspless_mode_selected)
return;
/* enable IPC DONE and BUSY interrupts */
snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, chip->ipc_ctl,
HDA_DSP_REG_HIPCCTL_DONE | HDA_DSP_REG_HIPCCTL_BUSY,
@ -336,6 +339,9 @@ void hda_dsp_ipc_int_disable(struct snd_sof_dev *sdev)
struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
const struct sof_intel_dsp_desc *chip = hda->desc;
if (sdev->dspless_mode_selected)
return;
/* disable IPC interrupt */
snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPIC,
HDA_DSP_ADSPIC_IPC, 0);
@ -681,6 +687,9 @@ static int hda_suspend(struct snd_sof_dev *sdev, bool runtime_suspend)
/* power down all hda links */
hda_bus_ml_suspend(bus);
if (sdev->dspless_mode_selected)
goto skip_dsp;
ret = chip->power_down_dsp(sdev);
if (ret < 0) {
dev_err(sdev->dev, "failed to power down DSP during suspend\n");
@ -694,6 +703,7 @@ static int hda_suspend(struct snd_sof_dev *sdev, bool runtime_suspend)
/* disable ppcap interrupt */
hda_dsp_ctrl_ppcap_enable(sdev, false);
hda_dsp_ctrl_ppcap_int_enable(sdev, false);
skip_dsp:
/* disable hda bus irq and streams */
hda_dsp_ctrl_stop_chip(sdev);
@ -744,9 +754,11 @@ static int hda_resume(struct snd_sof_dev *sdev, bool runtime_resume)
hda_codec_jack_check(sdev);
}
/* enable ppcap interrupt */
hda_dsp_ctrl_ppcap_enable(sdev, true);
hda_dsp_ctrl_ppcap_int_enable(sdev, true);
if (!sdev->dspless_mode_selected) {
/* enable ppcap interrupt */
hda_dsp_ctrl_ppcap_enable(sdev, true);
hda_dsp_ctrl_ppcap_int_enable(sdev, true);
}
cleanup:
/* display codec can powered off after controller init */
@ -843,8 +855,10 @@ int hda_dsp_runtime_suspend(struct snd_sof_dev *sdev)
};
int ret;
/* cancel any attempt for DSP D0I3 */
cancel_delayed_work_sync(&hda->d0i3_work);
if (!sdev->dspless_mode_selected) {
/* cancel any attempt for DSP D0I3 */
cancel_delayed_work_sync(&hda->d0i3_work);
}
/* stop hda controller and power dsp off */
ret = hda_suspend(sdev, true);
@ -866,8 +880,10 @@ int hda_dsp_suspend(struct snd_sof_dev *sdev, u32 target_state)
};
int ret;
/* cancel any attempt for DSP D0I3 */
cancel_delayed_work_sync(&hda->d0i3_work);
if (!sdev->dspless_mode_selected) {
/* cancel any attempt for DSP D0I3 */
cancel_delayed_work_sync(&hda->d0i3_work);
}
if (target_state == SOF_DSP_PM_D0) {
/* Set DSP power state */

View File

@ -355,6 +355,9 @@ bool hda_dsp_check_ipc_irq(struct snd_sof_dev *sdev)
bool ret = false;
u32 irq_status;
if (sdev->dspless_mode_selected)
return false;
/* store status */
irq_status = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPIS);
trace_sof_intel_hda_irq_ipc_check(sdev, irq_status);

View File

@ -874,12 +874,14 @@ int hda_dsp_stream_init(struct snd_sof_dev *sdev)
hext_stream = &hda_stream->hext_stream;
hext_stream->pphc_addr = sdev->bar[HDA_DSP_PP_BAR] +
SOF_HDA_PPHC_BASE + SOF_HDA_PPHC_INTERVAL * i;
if (sdev->bar[HDA_DSP_PP_BAR]) {
hext_stream->pphc_addr = sdev->bar[HDA_DSP_PP_BAR] +
SOF_HDA_PPHC_BASE + SOF_HDA_PPHC_INTERVAL * i;
hext_stream->pplc_addr = sdev->bar[HDA_DSP_PP_BAR] +
SOF_HDA_PPLC_BASE + SOF_HDA_PPLC_MULTI * num_total +
SOF_HDA_PPLC_INTERVAL * i;
hext_stream->pplc_addr = sdev->bar[HDA_DSP_PP_BAR] +
SOF_HDA_PPLC_BASE + SOF_HDA_PPLC_MULTI * num_total +
SOF_HDA_PPLC_INTERVAL * i;
}
hstream = &hext_stream->hstream;
@ -930,13 +932,14 @@ int hda_dsp_stream_init(struct snd_sof_dev *sdev)
hext_stream = &hda_stream->hext_stream;
/* we always have DSP support */
hext_stream->pphc_addr = sdev->bar[HDA_DSP_PP_BAR] +
SOF_HDA_PPHC_BASE + SOF_HDA_PPHC_INTERVAL * i;
if (sdev->bar[HDA_DSP_PP_BAR]) {
hext_stream->pphc_addr = sdev->bar[HDA_DSP_PP_BAR] +
SOF_HDA_PPHC_BASE + SOF_HDA_PPHC_INTERVAL * i;
hext_stream->pplc_addr = sdev->bar[HDA_DSP_PP_BAR] +
SOF_HDA_PPLC_BASE + SOF_HDA_PPLC_MULTI * num_total +
SOF_HDA_PPLC_INTERVAL * i;
hext_stream->pplc_addr = sdev->bar[HDA_DSP_PP_BAR] +
SOF_HDA_PPLC_BASE + SOF_HDA_PPLC_MULTI * num_total +
SOF_HDA_PPLC_INTERVAL * i;
}
hstream = &hext_stream->hstream;

View File

@ -122,8 +122,12 @@ void hda_common_enable_sdw_irq(struct snd_sof_dev *sdev, bool enable)
void hda_sdw_int_enable(struct snd_sof_dev *sdev, bool enable)
{
u32 interface_mask = hda_get_interface_mask(sdev);
const struct sof_intel_dsp_desc *chip;
if (!(interface_mask & BIT(SOF_DAI_INTEL_ALH)))
return;
chip = get_chip_info(sdev->pdata);
if (chip && chip->enable_sdw_irq)
chip->enable_sdw_irq(sdev, enable);
@ -131,10 +135,14 @@ void hda_sdw_int_enable(struct snd_sof_dev *sdev, bool enable)
static int hda_sdw_acpi_scan(struct snd_sof_dev *sdev)
{
u32 interface_mask = hda_get_interface_mask(sdev);
struct sof_intel_hda_dev *hdev;
acpi_handle handle;
int ret;
if (!(interface_mask & BIT(SOF_DAI_INTEL_ALH)))
return -EINVAL;
handle = ACPI_HANDLE(sdev->dev);
/* save ACPI info for the probe step */
@ -288,8 +296,12 @@ out:
static bool hda_dsp_check_sdw_irq(struct snd_sof_dev *sdev)
{
u32 interface_mask = hda_get_interface_mask(sdev);
const struct sof_intel_dsp_desc *chip;
if (!(interface_mask & BIT(SOF_DAI_INTEL_ALH)))
return false;
chip = get_chip_info(sdev->pdata);
if (chip && chip->check_sdw_irq)
return chip->check_sdw_irq(sdev);
@ -304,8 +316,12 @@ static irqreturn_t hda_dsp_sdw_thread(int irq, void *context)
static bool hda_sdw_check_wakeen_irq(struct snd_sof_dev *sdev)
{
u32 interface_mask = hda_get_interface_mask(sdev);
struct sof_intel_hda_dev *hdev;
if (!(interface_mask & BIT(SOF_DAI_INTEL_ALH)))
return false;
hdev = sdev->pdata->hw_pdata;
if (hdev->sdw &&
snd_sof_dsp_read(sdev, HDA_DSP_BAR,
@ -317,8 +333,12 @@ static bool hda_sdw_check_wakeen_irq(struct snd_sof_dev *sdev)
void hda_sdw_process_wakeen(struct snd_sof_dev *sdev)
{
u32 interface_mask = hda_get_interface_mask(sdev);
struct sof_intel_hda_dev *hdev;
if (!(interface_mask & BIT(SOF_DAI_INTEL_ALH)))
return;
hdev = sdev->pdata->hw_pdata;
if (!hdev->sdw)
return;
@ -1010,21 +1030,25 @@ int hda_dsp_probe(struct snd_sof_dev *sdev)
const struct sof_intel_dsp_desc *chip;
int ret = 0;
/*
* detect DSP by checking class/subclass/prog-id information
* class=04 subclass 03 prog-if 00: no DSP, legacy driver is required
* class=04 subclass 01 prog-if 00: DSP is present
* (and may be required e.g. for DMIC or SSP support)
* class=04 subclass 03 prog-if 80: either of DSP or legacy mode works
*/
if (pci->class == 0x040300) {
dev_err(sdev->dev, "error: the DSP is not enabled on this platform, aborting probe\n");
return -ENODEV;
} else if (pci->class != 0x040100 && pci->class != 0x040380) {
dev_err(sdev->dev, "error: unknown PCI class/subclass/prog-if 0x%06x found, aborting probe\n", pci->class);
return -ENODEV;
if (!sdev->dspless_mode_selected) {
/*
* detect DSP by checking class/subclass/prog-id information
* class=04 subclass 03 prog-if 00: no DSP, legacy driver is required
* class=04 subclass 01 prog-if 00: DSP is present
* (and may be required e.g. for DMIC or SSP support)
* class=04 subclass 03 prog-if 80: either of DSP or legacy mode works
*/
if (pci->class == 0x040300) {
dev_err(sdev->dev, "the DSP is not enabled on this platform, aborting probe\n");
return -ENODEV;
} else if (pci->class != 0x040100 && pci->class != 0x040380) {
dev_err(sdev->dev, "unknown PCI class/subclass/prog-if 0x%06x found, aborting probe\n",
pci->class);
return -ENODEV;
}
dev_info(sdev->dev, "DSP detected with PCI class/subclass/prog-if 0x%06x\n",
pci->class);
}
dev_info(sdev->dev, "DSP detected with PCI class/subclass/prog-if 0x%06x\n", pci->class);
chip = get_chip_info(sdev->pdata);
if (!chip) {
@ -1069,6 +1093,9 @@ int hda_dsp_probe(struct snd_sof_dev *sdev)
if (ret < 0)
goto hdac_bus_unmap;
if (sdev->dspless_mode_selected)
goto skip_dsp_setup;
/* DSP base */
sdev->bar[HDA_DSP_BAR] = pci_ioremap_bar(pci, HDA_DSP_BAR);
if (!sdev->bar[HDA_DSP_BAR]) {
@ -1079,6 +1106,7 @@ int hda_dsp_probe(struct snd_sof_dev *sdev)
sdev->mmio_bar = HDA_DSP_BAR;
sdev->mailbox_bar = HDA_DSP_BAR;
skip_dsp_setup:
/* allow 64bit DMA address if supported by H/W */
if (dma_set_mask_and_coherent(&pci->dev, DMA_BIT_MASK(64))) {
@ -1144,14 +1172,16 @@ int hda_dsp_probe(struct snd_sof_dev *sdev)
if (ret < 0)
goto free_ipc_irq;
/* enable ppcap interrupt */
hda_dsp_ctrl_ppcap_enable(sdev, true);
hda_dsp_ctrl_ppcap_int_enable(sdev, true);
if (!sdev->dspless_mode_selected) {
/* enable ppcap interrupt */
hda_dsp_ctrl_ppcap_enable(sdev, true);
hda_dsp_ctrl_ppcap_int_enable(sdev, true);
/* set default mailbox offset for FW ready message */
sdev->dsp_box.offset = HDA_DSP_MBOX_UPLINK_OFFSET;
/* set default mailbox offset for FW ready message */
sdev->dsp_box.offset = HDA_DSP_MBOX_UPLINK_OFFSET;
INIT_DELAYED_WORK(&hdev->d0i3_work, hda_dsp_d0i3_work);
INIT_DELAYED_WORK(&hdev->d0i3_work, hda_dsp_d0i3_work);
}
init_waitqueue_head(&hdev->waitq);
@ -1167,7 +1197,8 @@ free_irq_vector:
free_streams:
hda_dsp_stream_free(sdev);
/* dsp_unmap: not currently used */
iounmap(sdev->bar[HDA_DSP_BAR]);
if (!sdev->dspless_mode_selected)
iounmap(sdev->bar[HDA_DSP_BAR]);
hdac_bus_unmap:
platform_device_unregister(hdev->dmic_dev);
iounmap(bus->remap_addr);
@ -1187,8 +1218,9 @@ int hda_dsp_remove(struct snd_sof_dev *sdev)
if (nhlt)
intel_nhlt_free(nhlt);
/* cancel any attempt for DSP D0I3 */
cancel_delayed_work_sync(&hda->d0i3_work);
if (!sdev->dspless_mode_selected)
/* cancel any attempt for DSP D0I3 */
cancel_delayed_work_sync(&hda->d0i3_work);
hda_codec_device_remove(sdev);
@ -1197,14 +1229,19 @@ int hda_dsp_remove(struct snd_sof_dev *sdev)
if (!IS_ERR_OR_NULL(hda->dmic_dev))
platform_device_unregister(hda->dmic_dev);
/* disable DSP IRQ */
snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL,
SOF_HDA_PPCTL_PIE, 0);
if (!sdev->dspless_mode_selected) {
/* disable DSP IRQ */
snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL,
SOF_HDA_PPCTL_PIE, 0);
}
/* disable CIE and GIE interrupts */
snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTCTL,
SOF_HDA_INT_CTRL_EN | SOF_HDA_INT_GLOBAL_EN, 0);
if (sdev->dspless_mode_selected)
goto skip_disable_dsp;
/* no need to check for error as the DSP will be disabled anyway */
if (chip && chip->power_down_dsp)
chip->power_down_dsp(sdev);
@ -1213,6 +1250,7 @@ int hda_dsp_remove(struct snd_sof_dev *sdev)
snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL,
SOF_HDA_PPCTL_GPROCEN, 0);
skip_disable_dsp:
free_irq(sdev->ipc_irq, sdev);
if (sdev->msi_enabled)
pci_free_irq_vectors(pci);
@ -1221,7 +1259,9 @@ int hda_dsp_remove(struct snd_sof_dev *sdev)
hda_bus_ml_free(sof_to_bus(sdev));
iounmap(sdev->bar[HDA_DSP_BAR]);
if (!sdev->dspless_mode_selected)
iounmap(sdev->bar[HDA_DSP_BAR]);
iounmap(bus->remap_addr);
sof_hda_bus_exit(sdev);