diff --git a/include/sound/sof.h b/include/sound/sof.h index 266e66318f9c..d3c41f87ac31 100644 --- a/include/sound/sof.h +++ b/include/sound/sof.h @@ -21,6 +21,7 @@ struct snd_sof_dev; /** * enum sof_fw_state - DSP firmware state definitions * @SOF_FW_BOOT_NOT_STARTED: firmware boot is not yet started + * @SOF_DSPLESS_MODE: DSP is not used * @SOF_FW_BOOT_PREPARE: preparing for boot (firmware loading for exaqmple) * @SOF_FW_BOOT_IN_PROGRESS: firmware boot is in progress * @SOF_FW_BOOT_FAILED: firmware boot failed @@ -31,6 +32,7 @@ struct snd_sof_dev; */ enum sof_fw_state { SOF_FW_BOOT_NOT_STARTED = 0, + SOF_DSPLESS_MODE, SOF_FW_BOOT_PREPARE, SOF_FW_BOOT_IN_PROGRESS, SOF_FW_BOOT_FAILED, @@ -130,6 +132,9 @@ struct sof_dev_desc { unsigned int ipc_supported_mask; enum sof_ipc_type ipc_default; + /* The platform supports DSPless mode */ + bool dspless_mode_supported; + /* defaults paths for firmware, library and topology files */ const char *default_fw_path[SOF_IPC_TYPE_COUNT]; const char *default_lib_path[SOF_IPC_TYPE_COUNT]; diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index 7de8673a01ce..9a9d82220fd0 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -208,6 +208,11 @@ static int sof_probe_continue(struct snd_sof_dev *sdev) /* set up platform component driver */ snd_sof_new_platform_drv(sdev); + if (sdev->dspless_mode_selected) { + sof_set_fw_state(sdev, SOF_DSPLESS_MODE); + goto skip_dsp_init; + } + /* register any debug/trace capabilities */ ret = snd_sof_dbg_init(sdev); if (ret < 0) { @@ -266,6 +271,7 @@ static int sof_probe_continue(struct snd_sof_dev *sdev) dev_dbg(sdev->dev, "SOF firmware trace disabled\n"); } +skip_dsp_init: /* hereafter all FW boot flows are for PM reasons */ sdev->first_boot = false; @@ -365,6 +371,15 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data) if (sof_core_debug) dev_info(dev, "sof_debug value: %#x\n", sof_core_debug); + if (sof_debug_check_flag(SOF_DBG_DSPLESS_MODE)) { + if (plat_data->desc->dspless_mode_supported) { + dev_info(dev, "Switching to DSPless mode\n"); + sdev->dspless_mode_selected = true; + } else { + dev_info(dev, "DSPless mode is not supported by the platform\n"); + } + } + /* check IPC support */ if (!(BIT(plat_data->ipc_type) & plat_data->desc->ipc_supported_mask)) { dev_err(dev, "ipc_type %d is not supported on this platform, mask is %#x\n", @@ -378,12 +393,18 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data) return ret; /* check all mandatory ops */ - if (!sof_ops(sdev) || !sof_ops(sdev)->probe || !sof_ops(sdev)->run || - !sof_ops(sdev)->block_read || !sof_ops(sdev)->block_write || - !sof_ops(sdev)->send_msg || !sof_ops(sdev)->load_firmware || - !sof_ops(sdev)->ipc_msg_data) { + if (!sof_ops(sdev) || !sof_ops(sdev)->probe) { sof_ops_free(sdev); - dev_err(dev, "error: missing mandatory ops\n"); + dev_err(dev, "missing mandatory ops\n"); + return -EINVAL; + } + + if (!sdev->dspless_mode_selected && + (!sof_ops(sdev)->run || !sof_ops(sdev)->block_read || + !sof_ops(sdev)->block_write || !sof_ops(sdev)->send_msg || + !sof_ops(sdev)->load_firmware || !sof_ops(sdev)->ipc_msg_data)) { + sof_ops_free(sdev); + dev_err(dev, "missing mandatory DSP ops\n"); return -EINVAL; } diff --git a/sound/soc/sof/debug.c b/sound/soc/sof/debug.c index ade0507328af..b42b5982cbbc 100644 --- a/sound/soc/sof/debug.c +++ b/sound/soc/sof/debug.c @@ -370,6 +370,7 @@ static const struct soc_fw_state_info { const char *name; } fw_state_dbg[] = { {SOF_FW_BOOT_NOT_STARTED, "SOF_FW_BOOT_NOT_STARTED"}, + {SOF_DSPLESS_MODE, "SOF_DSPLESS_MODE"}, {SOF_FW_BOOT_PREPARE, "SOF_FW_BOOT_PREPARE"}, {SOF_FW_BOOT_IN_PROGRESS, "SOF_FW_BOOT_IN_PROGRESS"}, {SOF_FW_BOOT_FAILED, "SOF_FW_BOOT_FAILED"}, diff --git a/sound/soc/sof/intel/hda-dai-ops.c b/sound/soc/sof/intel/hda-dai-ops.c index de48f13259f1..4b39cecacd68 100644 --- a/sound/soc/sof/intel/hda-dai-ops.c +++ b/sound/soc/sof/intel/hda-dai-ops.c @@ -319,13 +319,44 @@ static const struct hda_dai_widget_dma_ops hda_ipc3_dma_ops = { .post_trigger = hda_ipc3_post_trigger, }; +static struct hdac_ext_stream * +hda_dspless_get_hext_stream(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai, + struct snd_pcm_substream *substream) +{ + struct hdac_stream *hstream = substream->runtime->private_data; + + return stream_to_hdac_ext_stream(hstream); +} + +static void hda_dspless_setup_hext_stream(struct snd_sof_dev *sdev, + struct hdac_ext_stream *hext_stream, + unsigned int format_val) +{ + /* + * Save the format_val which was adjusted by the maxbps of the codec. + * This information is not available on the FE side since there we are + * using dummy_codec. + */ + hext_stream->hstream.format_val = format_val; +} + +static const struct hda_dai_widget_dma_ops hda_dspless_dma_ops = { + .get_hext_stream = hda_dspless_get_hext_stream, + .setup_hext_stream = hda_dspless_setup_hext_stream, +}; + #endif const struct hda_dai_widget_dma_ops * hda_select_dai_widget_ops(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) { #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC) - struct snd_sof_dai *sdai = swidget->private; + struct snd_sof_dai *sdai; + + if (sdev->dspless_mode_selected) + return &hda_dspless_dma_ops; + + sdai = swidget->private; switch (sdev->pdata->ipc_type) { case SOF_IPC: diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c index 46a17afdd1ea..0435b7f251aa 100644 --- a/sound/soc/sof/intel/hda-dai.c +++ b/sound/soc/sof/intel/hda-dai.c @@ -31,11 +31,18 @@ int hda_dai_config(struct snd_soc_dapm_widget *w, unsigned int flags, struct snd_sof_dai_config_data *data) { struct snd_sof_widget *swidget = w->dobj.private; - struct snd_soc_component *component = swidget->scomp; - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); - const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); + const struct sof_ipc_tplg_ops *tplg_ops; + struct snd_soc_component *component; + struct snd_sof_dev *sdev; int ret; + if (!swidget) + return 0; + + component = swidget->scomp; + sdev = snd_soc_component_get_drvdata(component); + tplg_ops = sof_ipc_get_ops(sdev, tplg); + if (tplg_ops && tplg_ops->dai_config) { ret = tplg_ops->dai_config(sdev, swidget, flags, data); if (ret < 0) { @@ -56,13 +63,21 @@ hda_dai_get_ops(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, substream->stream); struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(cpu_dai->component); struct snd_sof_widget *swidget = w->dobj.private; - struct snd_sof_dai *sdai = swidget->private; + struct snd_sof_dai *sdai; + + /* + * The swidget parameter of hda_select_dai_widget_ops() is ignored in + * case of DSPless mode + */ + if (sdev->dspless_mode_selected) + return hda_select_dai_widget_ops(sdev, NULL); + + sdai = swidget->private; /* select and set the DAI widget ops if not set already */ if (!sdai->platform_private) { const struct hda_dai_widget_dma_ops *ops = hda_select_dai_widget_ops(sdev, swidget); - if (!ops) return NULL; diff --git a/sound/soc/sof/intel/hda-dsp.c b/sound/soc/sof/intel/hda-dsp.c index c9231aeacc53..23c05e6f424a 100644 --- a/sound/soc/sof/intel/hda-dsp.c +++ b/sound/soc/sof/intel/hda-dsp.c @@ -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 */ diff --git a/sound/soc/sof/intel/hda-ipc.c b/sound/soc/sof/intel/hda-ipc.c index df541b22b2d2..a838dddb1d32 100644 --- a/sound/soc/sof/intel/hda-ipc.c +++ b/sound/soc/sof/intel/hda-ipc.c @@ -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); diff --git a/sound/soc/sof/intel/hda-pcm.c b/sound/soc/sof/intel/hda-pcm.c index dc0b359ed9b6..981e7b699bdb 100644 --- a/sound/soc/sof/intel/hda-pcm.c +++ b/sound/soc/sof/intel/hda-pcm.c @@ -101,18 +101,23 @@ int hda_dsp_pcm_hw_params(struct snd_sof_dev *sdev, struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; struct snd_dma_buffer *dmab; int ret; - u32 size, rate, bits; - - size = params_buffer_bytes(params); - rate = hda_dsp_get_mult_div(sdev, params_rate(params)); - bits = hda_dsp_get_bits(sdev, params_width(params)); hstream->substream = substream; dmab = substream->runtime->dma_buffer_p; - hstream->format_val = rate | bits | (params_channels(params) - 1); - hstream->bufsize = size; + /* + * Use the codec required format val (which is link_bps adjusted) when + * the DSP is not in use + */ + if (!sdev->dspless_mode_selected) { + u32 rate = hda_dsp_get_mult_div(sdev, params_rate(params)); + u32 bits = hda_dsp_get_bits(sdev, params_width(params)); + + hstream->format_val = rate | bits | (params_channels(params) - 1); + } + + hstream->bufsize = params_buffer_bytes(params); hstream->period_bytes = params_period_bytes(params); hstream->no_period_wakeup = (params->info & SNDRV_PCM_INFO_NO_PERIOD_WAKEUP) && @@ -249,6 +254,11 @@ int hda_dsp_pcm_open(struct snd_sof_dev *sdev, snd_pcm_hw_constraint_integer(substream->runtime, SNDRV_PCM_HW_PARAM_PERIODS); + /* Only S16 and S32 supported by HDA hardware when used without DSP */ + if (sdev->dspless_mode_selected) + snd_pcm_hw_constraint_mask64(substream->runtime, SNDRV_PCM_HW_PARAM_FORMAT, + SNDRV_PCM_FMTBIT_S16 | SNDRV_PCM_FMTBIT_S32); + /* binding pcm substream to hda stream */ substream->runtime->private_data = &dsp_stream->hstream; return 0; diff --git a/sound/soc/sof/intel/hda-stream.c b/sound/soc/sof/intel/hda-stream.c index c37ef581637f..79d818e6a0fa 100644 --- a/sound/soc/sof/intel/hda-stream.c +++ b/sound/soc/sof/intel/hda-stream.c @@ -485,9 +485,8 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev, { const struct sof_intel_dsp_desc *chip = get_chip_info(sdev->pdata); struct hdac_bus *bus = sof_to_bus(sdev); - struct hdac_stream *hstream = &hext_stream->hstream; - int sd_offset = SOF_STREAM_SD_OFFSET(hstream); - int ret; + struct hdac_stream *hstream; + int sd_offset, ret; u32 dma_start = SOF_HDA_SD_CTL_DMA_START; u32 mask; u32 run; @@ -502,10 +501,14 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev, return -ENODEV; } - /* decouple host and link DMA */ - mask = 0x1 << hstream->index; - snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL, - mask, mask); + hstream = &hext_stream->hstream; + sd_offset = SOF_STREAM_SD_OFFSET(hstream); + mask = BIT(hstream->index); + + /* decouple host and link DMA if the DSP is used */ + if (!sdev->dspless_mode_selected) + snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL, + mask, mask); /* clear stream status */ snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset, @@ -606,11 +609,10 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev, * enable decoupled mode */ - if (chip->quirks & SOF_INTEL_PROCEN_FMT_QUIRK) { + if (!sdev->dspless_mode_selected && (chip->quirks & SOF_INTEL_PROCEN_FMT_QUIRK)) /* couple host and link DMA, disable DSP features */ snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL, mask, 0); - } /* program stream format */ snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, @@ -618,11 +620,10 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev, SOF_HDA_ADSP_REG_SD_FORMAT, 0xffff, hstream->format_val); - if (chip->quirks & SOF_INTEL_PROCEN_FMT_QUIRK) { + if (!sdev->dspless_mode_selected && (chip->quirks & SOF_INTEL_PROCEN_FMT_QUIRK)) /* decouple host and link DMA, enable DSP features */ snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL, mask, mask); - } /* program last valid index */ snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, @@ -675,20 +676,23 @@ int hda_dsp_stream_hw_free(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_stream = container_of(hstream, struct hdac_ext_stream, hstream); - struct hdac_bus *bus = sof_to_bus(sdev); - u32 mask = 0x1 << hstream->index; int ret; ret = hda_dsp_stream_reset(sdev, hstream); if (ret < 0) return ret; - spin_lock_irq(&bus->reg_lock); - /* couple host and link DMA if link DMA channel is idle */ - if (!hext_stream->link_locked) - snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, - SOF_HDA_REG_PP_PPCTL, mask, 0); - spin_unlock_irq(&bus->reg_lock); + if (!sdev->dspless_mode_selected) { + struct hdac_bus *bus = sof_to_bus(sdev); + u32 mask = BIT(hstream->index); + + spin_lock_irq(&bus->reg_lock); + /* couple host and link DMA if link DMA channel is idle */ + if (!hext_stream->link_locked) + snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, + SOF_HDA_REG_PP_PPCTL, mask, 0); + spin_unlock_irq(&bus->reg_lock); + } hda_dsp_stream_spib_config(sdev, hext_stream, HDA_DSP_SPIB_DISABLE, 0); @@ -870,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; @@ -926,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; diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index 65389c7278e2..483ec80f3160 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -44,6 +44,39 @@ #define EXCEPT_MAX_HDR_SIZE 0x400 #define HDA_EXT_ROM_STATUS_SIZE 8 +static u32 hda_get_interface_mask(struct snd_sof_dev *sdev) +{ + const struct sof_intel_dsp_desc *chip; + u32 interface_mask[2] = { 0 }; + + chip = get_chip_info(sdev->pdata); + switch (chip->hw_ip_version) { + case SOF_INTEL_TANGIER: + case SOF_INTEL_BAYTRAIL: + case SOF_INTEL_BROADWELL: + interface_mask[0] = BIT(SOF_DAI_INTEL_SSP); + break; + case SOF_INTEL_CAVS_1_5: + case SOF_INTEL_CAVS_1_5_PLUS: + interface_mask[0] = BIT(SOF_DAI_INTEL_SSP) | BIT(SOF_DAI_INTEL_DMIC) | + BIT(SOF_DAI_INTEL_HDA); + interface_mask[1] = BIT(SOF_DAI_INTEL_HDA); + break; + case SOF_INTEL_CAVS_1_8: + case SOF_INTEL_CAVS_2_0: + case SOF_INTEL_CAVS_2_5: + case SOF_INTEL_ACE_1_0: + interface_mask[0] = BIT(SOF_DAI_INTEL_SSP) | BIT(SOF_DAI_INTEL_DMIC) | + BIT(SOF_DAI_INTEL_HDA) | BIT(SOF_DAI_INTEL_ALH); + interface_mask[1] = BIT(SOF_DAI_INTEL_HDA); + break; + default: + break; + } + + return interface_mask[sdev->dspless_mode_selected]; +} + #if IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE) /* @@ -89,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); @@ -98,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 */ @@ -255,8 +296,12 @@ bool hda_common_check_sdw_irq(struct snd_sof_dev *sdev) 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); @@ -271,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, @@ -284,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; @@ -845,6 +898,7 @@ static int dmic_detect_topology_fixup(struct snd_sof_dev *sdev, static int hda_init_caps(struct snd_sof_dev *sdev) { + u32 interface_mask = hda_get_interface_mask(sdev); struct hdac_bus *bus = sof_to_bus(sdev); struct snd_sof_pdata *pdata = sdev->pdata; struct sof_intel_hda_dev *hdev = pdata->hw_pdata; @@ -865,6 +919,10 @@ static int hda_init_caps(struct snd_sof_dev *sdev) hda_bus_ml_get_capabilities(bus); + /* Skip SoundWire if it is not supported */ + if (!(interface_mask & BIT(SOF_DAI_INTEL_ALH))) + goto skip_soundwire; + /* scan SoundWire capabilities exposed by DSDT */ ret = hda_sdw_acpi_scan(sdev); if (ret < 0) { @@ -972,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) { @@ -1022,12 +1084,18 @@ int hda_dsp_probe(struct snd_sof_dev *sdev) hdev->no_ipc_position = sof_ops(sdev)->pcm_pointer ? 1 : 0; #endif + if (sdev->dspless_mode_selected) + hdev->no_ipc_position = 1; + /* set up HDA base */ bus = sof_to_bus(sdev); ret = hda_init(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]) { @@ -1038,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))) { @@ -1103,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); @@ -1126,7 +1197,8 @@ int hda_dsp_probe(struct snd_sof_dev *sdev) 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); @@ -1146,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); @@ -1156,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); @@ -1172,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); @@ -1180,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); @@ -1486,12 +1567,16 @@ void hda_set_mach_params(struct snd_soc_acpi_mach *mach, struct snd_soc_acpi_mach *hda_machine_select(struct snd_sof_dev *sdev) { + u32 interface_mask = hda_get_interface_mask(sdev); struct snd_sof_pdata *sof_pdata = sdev->pdata; const struct sof_dev_desc *desc = sof_pdata->desc; - struct snd_soc_acpi_mach *mach; + struct snd_soc_acpi_mach *mach = NULL; const char *tplg_filename; - mach = snd_soc_acpi_find_machine(desc->machines); + /* Try I2S or DMIC if it is supported */ + if (interface_mask & (BIT(SOF_DAI_INTEL_SSP) | BIT(SOF_DAI_INTEL_DMIC))) + mach = snd_soc_acpi_find_machine(desc->machines); + if (mach) { bool add_extension = false; bool tplg_fixup = false; @@ -1598,10 +1683,8 @@ struct snd_soc_acpi_mach *hda_machine_select(struct snd_sof_dev *sdev) } } - /* - * If I2S fails, try SoundWire - */ - if (!mach) + /* If I2S fails, try SoundWire if it is supported */ + if (!mach && (interface_mask & BIT(SOF_DAI_INTEL_ALH))) mach = hda_sdw_machine_select(sdev); /* diff --git a/sound/soc/sof/intel/pci-apl.c b/sound/soc/sof/intel/pci-apl.c index aff6cb573c27..69cad5a6bc72 100644 --- a/sound/soc/sof/intel/pci-apl.c +++ b/sound/soc/sof/intel/pci-apl.c @@ -29,6 +29,7 @@ static const struct sof_dev_desc bxt_desc = { .chip_info = &apl_chip_info, .ipc_supported_mask = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4), .ipc_default = SOF_IPC, + .dspless_mode_supported = true, /* Only supported for HDaudio */ .default_fw_path = { [SOF_IPC] = "intel/sof", [SOF_INTEL_IPC4] = "intel/avs/apl", @@ -60,6 +61,7 @@ static const struct sof_dev_desc glk_desc = { .chip_info = &apl_chip_info, .ipc_supported_mask = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4), .ipc_default = SOF_IPC, + .dspless_mode_supported = true, /* Only supported for HDaudio */ .default_fw_path = { [SOF_IPC] = "intel/sof", [SOF_INTEL_IPC4] = "intel/avs/glk", diff --git a/sound/soc/sof/intel/pci-cnl.c b/sound/soc/sof/intel/pci-cnl.c index 4c0c1c369dcd..8895508a0be6 100644 --- a/sound/soc/sof/intel/pci-cnl.c +++ b/sound/soc/sof/intel/pci-cnl.c @@ -30,6 +30,7 @@ static const struct sof_dev_desc cnl_desc = { .chip_info = &cnl_chip_info, .ipc_supported_mask = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4), .ipc_default = SOF_IPC, + .dspless_mode_supported = true, /* Only supported for HDaudio */ .default_fw_path = { [SOF_IPC] = "intel/sof", [SOF_INTEL_IPC4] = "intel/avs/cnl", @@ -62,6 +63,7 @@ static const struct sof_dev_desc cfl_desc = { .chip_info = &cnl_chip_info, .ipc_supported_mask = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4), .ipc_default = SOF_IPC, + .dspless_mode_supported = true, /* Only supported for HDaudio */ .default_fw_path = { [SOF_IPC] = "intel/sof", [SOF_INTEL_IPC4] = "intel/avs/cnl", @@ -94,6 +96,7 @@ static const struct sof_dev_desc cml_desc = { .chip_info = &cnl_chip_info, .ipc_supported_mask = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4), .ipc_default = SOF_IPC, + .dspless_mode_supported = true, /* Only supported for HDaudio */ .default_fw_path = { [SOF_IPC] = "intel/sof", [SOF_INTEL_IPC4] = "intel/avs/cnl", diff --git a/sound/soc/sof/intel/pci-icl.c b/sound/soc/sof/intel/pci-icl.c index 6785669113b3..5fb5a820693e 100644 --- a/sound/soc/sof/intel/pci-icl.c +++ b/sound/soc/sof/intel/pci-icl.c @@ -30,6 +30,7 @@ static const struct sof_dev_desc icl_desc = { .chip_info = &icl_chip_info, .ipc_supported_mask = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4), .ipc_default = SOF_IPC, + .dspless_mode_supported = true, /* Only supported for HDaudio */ .default_fw_path = { [SOF_IPC] = "intel/sof", [SOF_INTEL_IPC4] = "intel/avs/icl", @@ -61,6 +62,7 @@ static const struct sof_dev_desc jsl_desc = { .chip_info = &jsl_chip_info, .ipc_supported_mask = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4), .ipc_default = SOF_IPC, + .dspless_mode_supported = true, /* Only supported for HDaudio */ .default_fw_path = { [SOF_IPC] = "intel/sof", [SOF_INTEL_IPC4] = "intel/avs/jsl", diff --git a/sound/soc/sof/intel/pci-mtl.c b/sound/soc/sof/intel/pci-mtl.c index b183dc0014b4..e276e1e37fed 100644 --- a/sound/soc/sof/intel/pci-mtl.c +++ b/sound/soc/sof/intel/pci-mtl.c @@ -31,6 +31,7 @@ static const struct sof_dev_desc mtl_desc = { .chip_info = &mtl_chip_info, .ipc_supported_mask = BIT(SOF_INTEL_IPC4), .ipc_default = SOF_INTEL_IPC4, + .dspless_mode_supported = true, /* Only supported for HDaudio */ .default_fw_path = { [SOF_INTEL_IPC4] = "intel/sof-ipc4/mtl", }, diff --git a/sound/soc/sof/intel/pci-skl.c b/sound/soc/sof/intel/pci-skl.c index 5b4bccf81965..5e69af6eed34 100644 --- a/sound/soc/sof/intel/pci-skl.c +++ b/sound/soc/sof/intel/pci-skl.c @@ -26,6 +26,7 @@ static struct sof_dev_desc skl_desc = { .irqindex_host_ipc = -1, .ipc_supported_mask = BIT(SOF_INTEL_IPC4), .ipc_default = SOF_INTEL_IPC4, + .dspless_mode_supported = true, /* Only supported for HDaudio */ .default_fw_path = { [SOF_INTEL_IPC4] = "intel/avs/skl", }, @@ -50,6 +51,7 @@ static struct sof_dev_desc kbl_desc = { .irqindex_host_ipc = -1, .ipc_supported_mask = BIT(SOF_INTEL_IPC4), .ipc_default = SOF_INTEL_IPC4, + .dspless_mode_supported = true, /* Only supported for HDaudio */ .default_fw_path = { [SOF_INTEL_IPC4] = "intel/avs/kbl", }, diff --git a/sound/soc/sof/intel/pci-tgl.c b/sound/soc/sof/intel/pci-tgl.c index 22e769e0831d..ca37ff1bbd2a 100644 --- a/sound/soc/sof/intel/pci-tgl.c +++ b/sound/soc/sof/intel/pci-tgl.c @@ -30,6 +30,7 @@ static const struct sof_dev_desc tgl_desc = { .chip_info = &tgl_chip_info, .ipc_supported_mask = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4), .ipc_default = SOF_IPC, + .dspless_mode_supported = true, /* Only supported for HDaudio */ .default_fw_path = { [SOF_IPC] = "intel/sof", [SOF_INTEL_IPC4] = "intel/avs/tgl", @@ -62,6 +63,7 @@ static const struct sof_dev_desc tglh_desc = { .chip_info = &tglh_chip_info, .ipc_supported_mask = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4), .ipc_default = SOF_IPC, + .dspless_mode_supported = true, /* Only supported for HDaudio */ .default_fw_path = { [SOF_IPC] = "intel/sof", [SOF_INTEL_IPC4] = "intel/avs/tgl-h", @@ -93,6 +95,7 @@ static const struct sof_dev_desc ehl_desc = { .chip_info = &ehl_chip_info, .ipc_supported_mask = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4), .ipc_default = SOF_IPC, + .dspless_mode_supported = true, /* Only supported for HDaudio */ .default_fw_path = { [SOF_IPC] = "intel/sof", [SOF_INTEL_IPC4] = "intel/avs/ehl", @@ -125,6 +128,7 @@ static const struct sof_dev_desc adls_desc = { .chip_info = &adls_chip_info, .ipc_supported_mask = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4), .ipc_default = SOF_IPC, + .dspless_mode_supported = true, /* Only supported for HDaudio */ .default_fw_path = { [SOF_IPC] = "intel/sof", [SOF_INTEL_IPC4] = "intel/avs/adl-s", @@ -157,6 +161,7 @@ static const struct sof_dev_desc adl_desc = { .chip_info = &tgl_chip_info, .ipc_supported_mask = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4), .ipc_default = SOF_IPC, + .dspless_mode_supported = true, /* Only supported for HDaudio */ .default_fw_path = { [SOF_IPC] = "intel/sof", [SOF_INTEL_IPC4] = "intel/avs/adl", @@ -189,6 +194,7 @@ static const struct sof_dev_desc adl_n_desc = { .chip_info = &tgl_chip_info, .ipc_supported_mask = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4), .ipc_default = SOF_IPC, + .dspless_mode_supported = true, /* Only supported for HDaudio */ .default_fw_path = { [SOF_IPC] = "intel/sof", [SOF_INTEL_IPC4] = "intel/avs/adl-n", @@ -221,6 +227,7 @@ static const struct sof_dev_desc rpls_desc = { .chip_info = &adls_chip_info, .ipc_supported_mask = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4), .ipc_default = SOF_IPC, + .dspless_mode_supported = true, /* Only supported for HDaudio */ .default_fw_path = { [SOF_IPC] = "intel/sof", [SOF_INTEL_IPC4] = "intel/avs/rpl-s", @@ -253,6 +260,7 @@ static const struct sof_dev_desc rpl_desc = { .chip_info = &tgl_chip_info, .ipc_supported_mask = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4), .ipc_default = SOF_IPC, + .dspless_mode_supported = true, /* Only supported for HDaudio */ .default_fw_path = { [SOF_IPC] = "intel/sof", [SOF_INTEL_IPC4] = "intel/avs/rpl", diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index d9b4633bba7a..127b68caf9e1 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -330,6 +330,11 @@ static int sof_pcm_trigger(struct snd_soc_component *component, spcm->stream[substream->stream].suspend_ignored = true; return 0; } + + /* On suspend the DMA must be stopped in DSPless mode */ + if (sdev->dspless_mode_selected) + reset_hw_params = true; + fallthrough; case SNDRV_PCM_TRIGGER_STOP: ipc_first = true; @@ -705,7 +710,6 @@ void snd_sof_new_platform_drv(struct snd_sof_dev *sdev) pd->pcm_construct = sof_pcm_new; pd->ignore_machine = drv_name; - pd->be_hw_params_fixup = sof_pcm_dai_link_fixup; pd->be_pcm_base = SOF_BE_PCM_BASE; pd->use_dai_pcm_id = true; pd->topology_name_prefix = "sof"; @@ -714,4 +718,11 @@ void snd_sof_new_platform_drv(struct snd_sof_dev *sdev) pd->module_get_upon_open = 1; pd->legacy_dai_naming = 1; + + /* + * The fixup is only needed when the DSP is in use as with the DSPless + * mode we are directly using the audio interface + */ + if (!sdev->dspless_mode_selected) + pd->be_hw_params_fixup = sof_pcm_dai_link_fixup; } diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c index 8d3383085d12..c74ce8d414e7 100644 --- a/sound/soc/sof/pm.c +++ b/sound/soc/sof/pm.c @@ -103,6 +103,11 @@ static int sof_resume(struct device *dev, bool runtime_resume) return ret; } + if (sdev->dspless_mode_selected) { + sof_set_fw_state(sdev, SOF_DSPLESS_MODE); + return 0; + } + /* * Nothing further to be done for platforms that support the low power * D0 substate. Resume trace and return when resuming from diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index 4f12e137ff63..7651644fcd62 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -688,7 +688,7 @@ int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, struct snd_sof_widget *pipe_widget; struct snd_sof_pipeline *spipe; - if (!swidget) + if (!swidget || sdev->dspless_mode_selected) continue; spipe = swidget->spipe; diff --git a/sound/soc/sof/sof-client.c b/sound/soc/sof/sof-client.c index 9017f0864cdd..d6b7caa0cf03 100644 --- a/sound/soc/sof/sof-client.c +++ b/sound/soc/sof/sof-client.c @@ -130,6 +130,9 @@ int sof_register_clients(struct snd_sof_dev *sdev) { int ret; + if (sdev->dspless_mode_selected) + return 0; + /* Register platform independent client devices */ ret = sof_register_ipc_flood_test(sdev); if (ret) { diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 5f919162a555..1170989bea57 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -48,6 +48,7 @@ struct snd_sof_pcm_stream; #define SOF_DBG_FORCE_NOCODEC BIT(10) /* ignore all codec-related * configurations */ +#define SOF_DBG_DSPLESS_MODE BIT(15) /* Do not initialize and use the DSP */ /* Flag definitions used for controlling the DSP dump behavior */ #define SOF_DBG_DUMP_REGS BIT(0) @@ -528,6 +529,16 @@ struct snd_sof_dev { spinlock_t ipc_lock; /* lock for IPC users */ spinlock_t hw_lock; /* lock for HW IO access */ + /* + * When true the DSP is not used. + * It is set under the following condition: + * User sets the SOF_DBG_DSPLESS_MODE flag in sof_debug module parameter + * and + * the platform advertises that it can support such mode + * pdata->desc->dspless_mode_supported is true. + */ + bool dspless_mode_selected; + /* Main, Base firmware image */ struct sof_firmware basefw; diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index bc8ca1e05b83..d3d536b0a8f5 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -1144,8 +1144,12 @@ static void sof_disconnect_dai_widget(struct snd_soc_component *scomp, static int spcm_bind(struct snd_soc_component *scomp, struct snd_sof_pcm *spcm, int dir) { + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct snd_sof_widget *host_widget; + if (sdev->dspless_mode_selected) + return 0; + host_widget = snd_sof_find_swidget_sname(scomp, spcm->pcm.caps[dir].name, dir); @@ -2270,6 +2274,126 @@ static struct snd_soc_tplg_ops sof_tplg_ops = { .bytes_ext_ops_count = ARRAY_SIZE(sof_bytes_ext_ops), }; +static int snd_sof_dspless_kcontrol(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + return 0; +} + +static const struct snd_soc_tplg_kcontrol_ops sof_dspless_io_ops[] = { + {SOF_TPLG_KCTL_VOL_ID, snd_sof_dspless_kcontrol, snd_sof_dspless_kcontrol}, + {SOF_TPLG_KCTL_BYTES_ID, snd_sof_dspless_kcontrol, snd_sof_dspless_kcontrol}, + {SOF_TPLG_KCTL_ENUM_ID, snd_sof_dspless_kcontrol, snd_sof_dspless_kcontrol}, + {SOF_TPLG_KCTL_SWITCH_ID, snd_sof_dspless_kcontrol, snd_sof_dspless_kcontrol}, +}; + +static int snd_sof_dspless_bytes_ext_get(struct snd_kcontrol *kcontrol, + unsigned int __user *binary_data, + unsigned int size) +{ + return 0; +} + +static int snd_sof_dspless_bytes_ext_put(struct snd_kcontrol *kcontrol, + const unsigned int __user *binary_data, + unsigned int size) +{ + return 0; +} + +static const struct snd_soc_tplg_bytes_ext_ops sof_dspless_bytes_ext_ops[] = { + {SOF_TPLG_KCTL_BYTES_ID, snd_sof_dspless_bytes_ext_get, snd_sof_dspless_bytes_ext_put}, + {SOF_TPLG_KCTL_BYTES_VOLATILE_RO, snd_sof_dspless_bytes_ext_get}, +}; + +/* external widget init - used for any driver specific init */ +static int sof_dspless_widget_ready(struct snd_soc_component *scomp, int index, + struct snd_soc_dapm_widget *w, + struct snd_soc_tplg_dapm_widget *tw) +{ + if (WIDGET_IS_DAI(w->id)) { + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_widget *swidget; + struct snd_sof_dai dai; + int ret; + + swidget = kzalloc(sizeof(*swidget), GFP_KERNEL); + if (!swidget) + return -ENOMEM; + + memset(&dai, 0, sizeof(dai)); + + ret = sof_connect_dai_widget(scomp, w, tw, &dai); + if (ret) { + kfree(swidget); + return ret; + } + + swidget->scomp = scomp; + swidget->widget = w; + mutex_init(&swidget->setup_mutex); + w->dobj.private = swidget; + list_add(&swidget->list, &sdev->widget_list); + } + + return 0; +} + +static int sof_dspless_widget_unload(struct snd_soc_component *scomp, + struct snd_soc_dobj *dobj) +{ + struct snd_soc_dapm_widget *w = container_of(dobj, struct snd_soc_dapm_widget, dobj); + + if (WIDGET_IS_DAI(w->id)) { + struct snd_sof_widget *swidget = dobj->private; + + sof_disconnect_dai_widget(scomp, w); + + if (!swidget) + return 0; + + /* remove and free swidget object */ + list_del(&swidget->list); + kfree(swidget); + } + + return 0; +} + +static int sof_dspless_link_load(struct snd_soc_component *scomp, int index, + struct snd_soc_dai_link *link, + struct snd_soc_tplg_link_config *cfg) +{ + link->platforms->name = dev_name(scomp->dev); + + /* Set nonatomic property for FE dai links for FE-BE compatibility */ + if (!link->no_pcm) + link->nonatomic = true; + + return 0; +} + +static struct snd_soc_tplg_ops sof_dspless_tplg_ops = { + /* external widget init - used for any driver specific init */ + .widget_ready = sof_dspless_widget_ready, + .widget_unload = sof_dspless_widget_unload, + + /* FE DAI - used for any driver specific init */ + .dai_load = sof_dai_load, + .dai_unload = sof_dai_unload, + + /* DAI link - used for any driver specific init */ + .link_load = sof_dspless_link_load, + + /* vendor specific kcontrol handlers available for binding */ + .io_ops = sof_dspless_io_ops, + .io_ops_count = ARRAY_SIZE(sof_dspless_io_ops), + + /* vendor specific bytes ext handlers available for binding */ + .bytes_ext_ops = sof_dspless_bytes_ext_ops, + .bytes_ext_ops_count = ARRAY_SIZE(sof_dspless_bytes_ext_ops), +}; + int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file) { struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); @@ -2287,7 +2411,11 @@ int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file) return ret; } - ret = snd_soc_tplg_component_load(scomp, &sof_tplg_ops, fw); + if (sdev->dspless_mode_selected) + ret = snd_soc_tplg_component_load(scomp, &sof_dspless_tplg_ops, fw); + else + ret = snd_soc_tplg_component_load(scomp, &sof_tplg_ops, fw); + if (ret < 0) { dev_err(scomp->dev, "error: tplg component load failed %d\n", ret);