aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/sound/soc/sof/intel
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/sof/intel')
-rw-r--r--sound/soc/sof/intel/Kconfig404
-rw-r--r--sound/soc/sof/intel/Makefile55
-rw-r--r--sound/soc/sof/intel/apl.c167
-rw-r--r--sound/soc/sof/intel/atom.c421
-rw-r--r--sound/soc/sof/intel/atom.h74
-rw-r--r--sound/soc/sof/intel/bdw.c208
-rw-r--r--sound/soc/sof/intel/byt.c967
-rw-r--r--sound/soc/sof/intel/cnl.c438
-rw-r--r--sound/soc/sof/intel/ext_manifest.h35
-rw-r--r--sound/soc/sof/intel/hda-bus.c83
-rw-r--r--sound/soc/sof/intel/hda-codec.c341
-rw-r--r--sound/soc/sof/intel/hda-common-ops.c108
-rw-r--r--sound/soc/sof/intel/hda-compress.c114
-rw-r--r--sound/soc/sof/intel/hda-ctrl.c134
-rw-r--r--sound/soc/sof/intel/hda-dai-ops.c660
-rw-r--r--sound/soc/sof/intel/hda-dai.c996
-rw-r--r--sound/soc/sof/intel/hda-dsp.c1079
-rw-r--r--sound/soc/sof/intel/hda-ipc.c366
-rw-r--r--sound/soc/sof/intel/hda-ipc.h7
-rw-r--r--sound/soc/sof/intel/hda-loader-skl.c578
-rw-r--r--sound/soc/sof/intel/hda-loader.c643
-rw-r--r--sound/soc/sof/intel/hda-mlink.c1120
-rw-r--r--sound/soc/sof/intel/hda-pcm.c232
-rw-r--r--sound/soc/sof/intel/hda-probes.c150
-rw-r--r--sound/soc/sof/intel/hda-sdw-bpt.c445
-rw-r--r--sound/soc/sof/intel/hda-stream.c846
-rw-r--r--sound/soc/sof/intel/hda-trace.c36
-rw-r--r--sound/soc/sof/intel/hda.c1456
-rw-r--r--sound/soc/sof/intel/hda.h550
-rw-r--r--sound/soc/sof/intel/icl.c196
-rw-r--r--sound/soc/sof/intel/intel-ipc.c92
-rw-r--r--sound/soc/sof/intel/lnl.c189
-rw-r--r--sound/soc/sof/intel/lnl.h21
-rw-r--r--sound/soc/sof/intel/mtl.c817
-rw-r--r--sound/soc/sof/intel/mtl.h134
-rw-r--r--sound/soc/sof/intel/pci-apl.c111
-rw-r--r--sound/soc/sof/intel/pci-cnl.c149
-rw-r--r--sound/soc/sof/intel/pci-icl.c115
-rw-r--r--sound/soc/sof/intel/pci-lnl.c84
-rw-r--r--sound/soc/sof/intel/pci-mtl.c147
-rw-r--r--sound/soc/sof/intel/pci-ptl.c113
-rw-r--r--sound/soc/sof/intel/pci-skl.c95
-rw-r--r--sound/soc/sof/intel/pci-tgl.c324
-rw-r--r--sound/soc/sof/intel/pci-tng.c250
-rw-r--r--sound/soc/sof/intel/ptl.c154
-rw-r--r--sound/soc/sof/intel/ptl.h19
-rw-r--r--sound/soc/sof/intel/shim.h51
-rw-r--r--sound/soc/sof/intel/skl.c117
-rw-r--r--sound/soc/sof/intel/telemetry.c96
-rw-r--r--sound/soc/sof/intel/telemetry.h35
-rw-r--r--sound/soc/sof/intel/tgl.c252
-rw-r--r--sound/soc/sof/intel/tracepoints.c5
52 files changed, 12851 insertions, 3428 deletions
diff --git a/sound/soc/sof/intel/Kconfig b/sound/soc/sof/intel/Kconfig
index 3aaf25e4f766..dc1d21de4ab7 100644
--- a/sound/soc/sof/intel/Kconfig
+++ b/sound/soc/sof/intel/Kconfig
@@ -9,35 +9,11 @@ config SND_SOC_SOF_INTEL_TOPLEVEL
if SND_SOC_SOF_INTEL_TOPLEVEL
-config SND_SOC_SOF_INTEL_ACPI
- def_tristate SND_SOC_SOF_ACPI
- select SND_SOC_SOF_BAYTRAIL if SND_SOC_SOF_BAYTRAIL_SUPPORT
- select SND_SOC_SOF_BROADWELL if SND_SOC_SOF_BROADWELL_SUPPORT
- help
- This option is not user-selectable but automagically handled by
- 'select' statements at a higher level
-
-config SND_SOC_SOF_INTEL_PCI
- def_tristate SND_SOC_SOF_PCI
- select SND_SOC_SOF_MERRIFIELD if SND_SOC_SOF_MERRIFIELD_SUPPORT
- select SND_SOC_SOF_APOLLOLAKE if SND_SOC_SOF_APOLLOLAKE_SUPPORT
- select SND_SOC_SOF_GEMINILAKE if SND_SOC_SOF_GEMINILAKE_SUPPORT
- select SND_SOC_SOF_CANNONLAKE if SND_SOC_SOF_CANNONLAKE_SUPPORT
- select SND_SOC_SOF_COFFEELAKE if SND_SOC_SOF_COFFEELAKE_SUPPORT
- select SND_SOC_SOF_ICELAKE if SND_SOC_SOF_ICELAKE_SUPPORT
- select SND_SOC_SOF_COMETLAKE if SND_SOC_SOF_COMETLAKE_SUPPORT
- select SND_SOC_SOF_TIGERLAKE if SND_SOC_SOF_TIGERLAKE_SUPPORT
- select SND_SOC_SOF_ELKHARTLAKE if SND_SOC_SOF_ELKHARTLAKE_SUPPORT
- select SND_SOC_SOF_JASPERLAKE if SND_SOC_SOF_JASPERLAKE_SUPPORT
- help
- This option is not user-selectable but automagically handled by
- 'select' statements at a higher level
-
config SND_SOC_SOF_INTEL_HIFI_EP_IPC
tristate
help
This option is not user-selectable but automagically handled by
- 'select' statements at a higher level
+ 'select' statements at a higher level.
config SND_SOC_SOF_INTEL_ATOM_HIFI_EP
tristate
@@ -45,290 +21,376 @@ config SND_SOC_SOF_INTEL_ATOM_HIFI_EP
select SND_SOC_SOF_INTEL_HIFI_EP_IPC
help
This option is not user-selectable but automagically handled by
- 'select' statements at a higher level
+ 'select' statements at a higher level.
config SND_SOC_SOF_INTEL_COMMON
tristate
+ select SND_SOC_SOF
select SND_SOC_ACPI_INTEL_MATCH
select SND_SOC_SOF_XTENSA
select SND_SOC_INTEL_MACH
select SND_SOC_ACPI if ACPI
+ select SND_INTEL_DSP_CONFIG
help
This option is not user-selectable but automagically handled by
- 'select' statements at a higher level
+ 'select' statements at a higher level.
-if SND_SOC_SOF_INTEL_ACPI
-
-config SND_SOC_SOF_BAYTRAIL_SUPPORT
- bool "SOF support for Baytrail, Braswell and Cherrytrail"
- depends on SND_SST_ATOM_HIFI2_PLATFORM_ACPI=n
- help
- This adds support for Sound Open Firmware for Intel(R) platforms
- using the Baytrail, Braswell or Cherrytrail processors.
- This option is mutually exclusive with the Atom/SST and Baytrail
- legacy drivers. If you want to enable SOF on Baytrail/Cherrytrail,
- you need to deselect those options first.
- SOF does not support Baytrail-CR for now, so this option is not
- recommended for distros. At some point all legacy drivers will be
- deprecated but not before all userspace firmware/topology/UCM files
- are made available to downstream distros.
- Say Y if you want to enable SOF on Baytrail/Cherrytrail
- If unsure select "N".
+if SND_SOC_SOF_ACPI
config SND_SOC_SOF_BAYTRAIL
- tristate
+ tristate "SOF support for Baytrail, Braswell and Cherrytrail"
+ default SND_SOC_SOF_ACPI
+ select SND_SOC_SOF_IPC3
+ select SND_SOC_SOF_INTEL_COMMON
select SND_SOC_SOF_INTEL_ATOM_HIFI_EP
- help
- This option is not user-selectable but automagically handled by
- 'select' statements at a higher level
-
-config SND_SOC_SOF_BROADWELL_SUPPORT
- bool "SOF support for Broadwell"
- depends on SND_SOC_INTEL_HASWELL=n
+ select SND_SOC_SOF_ACPI_DEV
+ select IOSF_MBI if X86 && PCI
help
This adds support for Sound Open Firmware for Intel(R) platforms
- using the Broadwell processors.
- This option is mutually exclusive with the Haswell/Broadwell legacy
- driver. If you want to enable SOF on Broadwell you need to deselect
- the legacy driver first.
- SOF does fully support Broadwell yet, so this option is not
- recommended for distros. At some point all legacy drivers will be
- deprecated but not before all userspace firmware/topology/UCM files
- are made available to downstream distros.
- Say Y if you want to enable SOF on Broadwell
+ using the Baytrail, Braswell or Cherrytrail processors.
+ This option can coexist in the same build with the Atom legacy
+ drivers, currently the default but which will be deprecated
+ at some point.
+ Existing firmware/topology binaries and UCM configurations
+ typically located in the root file system are already
+ compatible with both SOF or Atom/SST legacy drivers.
+ This is a recommended option for distributions.
+ Say Y if you want to enable SOF on Baytrail/Cherrytrail.
If unsure select "N".
config SND_SOC_SOF_BROADWELL
- tristate
+ tristate "SOF support for Broadwell"
+ default SND_SOC_SOF_ACPI
+ select SND_SOC_SOF_IPC3
select SND_SOC_SOF_INTEL_COMMON
select SND_SOC_SOF_INTEL_HIFI_EP_IPC
+ select SND_SOC_SOF_ACPI_DEV
help
- This option is not user-selectable but automagically handled by
- 'select' statements at a higher level
+ This adds support for Sound Open Firmware for Intel(R) platforms
+ using the Broadwell processors.
+ This option can coexist in the same build with the default 'catpt'
+ driver.
+ Existing firmware/topology binaries and UCM configurations typically
+ located in the root file system are already compatible with both SOF
+ or catpt drivers.
+ SOF does not fully support Broadwell and has limitations related to
+ DMA and suspend-resume, this is not a recommended option for
+ distributions.
+ Say Y if you want to enable SOF on Broadwell.
+ If unsure select "N".
-endif ## SND_SOC_SOF_INTEL_ACPI
+endif ## SND_SOC_SOF_ACPI
-if SND_SOC_SOF_INTEL_PCI
+if SND_SOC_SOF_PCI
-config SND_SOC_SOF_MERRIFIELD_SUPPORT
- bool "SOF support for Tangier/Merrifield"
+config SND_SOC_SOF_MERRIFIELD
+ tristate "SOF support for Tangier/Merrifield"
+ default SND_SOC_SOF_PCI
+ select SND_SOC_SOF_PCI_DEV
+ select SND_SOC_SOF_IPC3
+ select SND_SOC_SOF_INTEL_ATOM_HIFI_EP
help
This adds support for Sound Open Firmware for Intel(R) platforms
using the Tangier/Merrifield processors.
Say Y if you have such a device.
If unsure select "N".
-config SND_SOC_SOF_MERRIFIELD
+config SND_SOC_SOF_INTEL_SKL
tristate
- select SND_SOC_SOF_INTEL_ATOM_HIFI_EP
+ select SND_SOC_SOF_HDA_GENERIC
+ select SND_SOC_SOF_IPC4
+
+config SND_SOC_SOF_SKYLAKE
+ tristate "SOF support for SkyLake"
+ default SND_SOC_SOF_PCI
+ select SND_SOC_SOF_INTEL_SKL
help
- This option is not user-selectable but automagically handled by
- 'select' statements at a higher level
+ This adds support for the Intel(R) platforms using the SkyLake processors.
+ Say Y if you have such a device.
+ If unsure select "N".
+ This is intended only for developers and not a recommend option for distros.
-config SND_SOC_SOF_APOLLOLAKE_SUPPORT
- bool "SOF support for Apollolake"
+config SND_SOC_SOF_KABYLAKE
+ tristate "SOF support for KabyLake"
+ default SND_SOC_SOF_PCI
+ select SND_SOC_SOF_INTEL_SKL
help
- This adds support for Sound Open Firmware for Intel(R) platforms
- using the Apollolake processors.
+ This adds support for the Intel(R) platforms using the KabyLake processors.
Say Y if you have such a device.
If unsure select "N".
+ This is intended only for developers and not a recommend option for distros.
-config SND_SOC_SOF_APOLLOLAKE
+config SND_SOC_SOF_INTEL_APL
tristate
- select SND_SOC_SOF_HDA_COMMON
+ select SND_SOC_SOF_HDA_GENERIC
+ select SND_SOC_SOF_IPC3
+ select SND_SOC_SOF_IPC4
+
+config SND_SOC_SOF_APOLLOLAKE
+ tristate "SOF support for Apollolake"
+ default SND_SOC_SOF_PCI
+ select SND_SOC_SOF_INTEL_APL
help
- This option is not user-selectable but automagically handled by
- 'select' statements at a higher level
+ This adds support for Sound Open Firmware for Intel(R) platforms
+ using the Apollolake processors.
+ Say Y if you have such a device.
+ If unsure select "N".
-config SND_SOC_SOF_GEMINILAKE_SUPPORT
- bool "SOF support for GeminiLake"
+config SND_SOC_SOF_GEMINILAKE
+ tristate "SOF support for GeminiLake"
+ default SND_SOC_SOF_PCI
+ select SND_SOC_SOF_INTEL_APL
help
This adds support for Sound Open Firmware for Intel(R) platforms
using the Geminilake processors.
Say Y if you have such a device.
If unsure select "N".
-config SND_SOC_SOF_GEMINILAKE
+config SND_SOC_SOF_INTEL_CNL
tristate
- select SND_SOC_SOF_HDA_COMMON
- help
- This option is not user-selectable but automagically handled by
- 'select' statements at a higher level
+ select SND_SOC_SOF_HDA_GENERIC
+ select SND_SOC_SOF_INTEL_SOUNDWIRE_LINK_BASELINE
+ select SND_SOC_SOF_IPC3
+ select SND_SOC_SOF_IPC4
-config SND_SOC_SOF_CANNONLAKE_SUPPORT
- bool "SOF support for Cannonlake"
+config SND_SOC_SOF_CANNONLAKE
+ tristate "SOF support for Cannonlake"
+ default SND_SOC_SOF_PCI
+ select SND_SOC_SOF_INTEL_CNL
help
This adds support for Sound Open Firmware for Intel(R) platforms
using the Cannonlake processors.
Say Y if you have such a device.
If unsure select "N".
-config SND_SOC_SOF_CANNONLAKE
- tristate
- select SND_SOC_SOF_HDA_COMMON
- help
- This option is not user-selectable but automagically handled by
- 'select' statements at a higher level
-
-config SND_SOC_SOF_COFFEELAKE_SUPPORT
- bool "SOF support for CoffeeLake"
+config SND_SOC_SOF_COFFEELAKE
+ tristate "SOF support for CoffeeLake"
+ default SND_SOC_SOF_PCI
+ select SND_SOC_SOF_INTEL_CNL
help
This adds support for Sound Open Firmware for Intel(R) platforms
using the Coffeelake processors.
Say Y if you have such a device.
If unsure select "N".
-config SND_SOC_SOF_COFFEELAKE
- tristate
- select SND_SOC_SOF_HDA_COMMON
+config SND_SOC_SOF_COMETLAKE
+ tristate "SOF support for CometLake"
+ default SND_SOC_SOF_PCI
+ select SND_SOC_SOF_INTEL_CNL
help
- This option is not user-selectable but automagically handled by
- 'select' statements at a higher level
+ This adds support for Sound Open Firmware for Intel(R) platforms
+ using the Cometlake processors.
+ If unsure select "N".
+
+config SND_SOC_SOF_INTEL_ICL
+ tristate
+ select SND_SOC_SOF_HDA_GENERIC
+ select SND_SOC_SOF_INTEL_SOUNDWIRE_LINK_BASELINE
+ select SND_SOC_SOF_IPC3
+ select SND_SOC_SOF_IPC4
+ select SND_SOC_SOF_INTEL_CNL
-config SND_SOC_SOF_ICELAKE_SUPPORT
- bool "SOF support for Icelake"
+config SND_SOC_SOF_ICELAKE
+ tristate "SOF support for Icelake"
+ default SND_SOC_SOF_PCI
+ select SND_SOC_SOF_INTEL_ICL
help
This adds support for Sound Open Firmware for Intel(R) platforms
using the Icelake processors.
Say Y if you have such a device.
If unsure select "N".
-config SND_SOC_SOF_ICELAKE
- tristate
- select SND_SOC_SOF_HDA_COMMON
+config SND_SOC_SOF_JASPERLAKE
+ tristate "SOF support for JasperLake"
+ default SND_SOC_SOF_PCI
+ select SND_SOC_SOF_INTEL_ICL
help
- This option is not user-selectable but automagically handled by
- 'select' statements at a higher level
+ This adds support for Sound Open Firmware for Intel(R) platforms
+ using the JasperLake processors.
+ Say Y if you have such a device.
+ If unsure select "N".
-config SND_SOC_SOF_COMETLAKE
+config SND_SOC_SOF_INTEL_TGL
tristate
- select SND_SOC_SOF_HDA_COMMON
- help
- This option is not user-selectable but automagically handled by
- 'select' statements at a higher level
+ select SND_SOC_SOF_HDA_GENERIC
+ select SND_SOC_SOF_INTEL_SOUNDWIRE_LINK_BASELINE
+ select SND_SOC_SOF_IPC3
+ select SND_SOC_SOF_IPC4
+ select SND_SOC_SOF_INTEL_CNL
-config SND_SOC_SOF_COMETLAKE_SUPPORT
- bool
+config SND_SOC_SOF_TIGERLAKE
+ tristate "SOF support for Tigerlake"
+ default SND_SOC_SOF_PCI
+ select SND_SOC_SOF_INTEL_TGL
+ help
+ This adds support for Sound Open Firmware for Intel(R) platforms
+ using the Tigerlake processors.
+ Say Y if you have such a device.
+ If unsure select "N".
-config SND_SOC_SOF_COMETLAKE_LP_SUPPORT
- bool "SOF support for CometLake"
- select SND_SOC_SOF_COMETLAKE_SUPPORT
+config SND_SOC_SOF_ELKHARTLAKE
+ tristate "SOF support for ElkhartLake"
+ default SND_SOC_SOF_PCI
+ select SND_SOC_SOF_INTEL_TGL
help
This adds support for Sound Open Firmware for Intel(R) platforms
- using the Cometlake processors.
+ using the ElkhartLake processors.
+ Say Y if you have such a device.
If unsure select "N".
-config SND_SOC_SOF_TIGERLAKE_SUPPORT
- bool "SOF support for Tigerlake"
+config SND_SOC_SOF_ALDERLAKE
+ tristate "SOF support for Alderlake"
+ default SND_SOC_SOF_PCI
+ select SND_SOC_SOF_INTEL_TGL
help
This adds support for Sound Open Firmware for Intel(R) platforms
- using the Tigerlake processors.
+ using the Alderlake processors.
Say Y if you have such a device.
If unsure select "N".
-config SND_SOC_SOF_TIGERLAKE
+config SND_SOC_SOF_INTEL_MTL
tristate
- select SND_SOC_SOF_HDA_COMMON
- help
- This option is not user-selectable but automagically handled by
- 'select' statements at a higher level
+ select SND_SOC_SOF_HDA_GENERIC
+ select SND_SOC_SOF_INTEL_SOUNDWIRE_LINK_BASELINE
+ select SND_SOC_SOF_IPC4
-config SND_SOC_SOF_ELKHARTLAKE_SUPPORT
- bool "SOF support for ElkhartLake"
+config SND_SOC_SOF_METEORLAKE
+ tristate "SOF support for Meteorlake"
+ default SND_SOC_SOF_PCI
+ select SND_SOC_SOF_INTEL_MTL
help
This adds support for Sound Open Firmware for Intel(R) platforms
- using the ElkhartLake processors.
+ using the Meteorlake processors.
Say Y if you have such a device.
If unsure select "N".
-config SND_SOC_SOF_ELKHARTLAKE
+config SND_SOC_SOF_INTEL_LNL
tristate
- select SND_SOC_SOF_HDA_COMMON
- help
- This option is not user-selectable but automagically handled by
- 'select' statements at a higher level
+ select SND_SOC_SOF_HDA_GENERIC
+ select SND_SOC_SOF_INTEL_SOUNDWIRE_LINK_BASELINE
+ select SND_SOF_SOF_HDA_SDW_BPT if SND_SOC_SOF_INTEL_SOUNDWIRE
+ select SND_SOC_SOF_IPC4
+ select SND_SOC_SOF_INTEL_MTL
-config SND_SOC_SOF_JASPERLAKE_SUPPORT
- bool "SOF support for JasperLake"
+config SND_SOC_SOF_LUNARLAKE
+ tristate "SOF support for Lunarlake"
+ default SND_SOC_SOF_PCI
+ select SND_SOC_SOF_INTEL_LNL
help
This adds support for Sound Open Firmware for Intel(R) platforms
- using the JasperLake processors.
+ using the Lunarlake processors.
Say Y if you have such a device.
If unsure select "N".
-config SND_SOC_SOF_JASPERLAKE
+config SND_SOC_SOF_INTEL_PTL
tristate
select SND_SOC_SOF_HDA_COMMON
+ select SND_SOC_SOF_INTEL_SOUNDWIRE_LINK_BASELINE
+ select SND_SOC_SOF_IPC4
+ select SND_SOC_SOF_INTEL_LNL
+
+config SND_SOC_SOF_PANTHERLAKE
+ tristate "SOF support for Pantherlake"
+ default SND_SOC_SOF_PCI
+ select SND_SOC_SOF_INTEL_PTL
help
- This option is not user-selectable but automagically handled by
- 'select' statements at a higher level
+ This adds support for Sound Open Firmware for Intel(R) platforms
+ using the Pantherlake processors.
+ Say Y if you have such a device.
+ If unsure select "N".
config SND_SOC_SOF_HDA_COMMON
tristate
+
+config SND_SOC_SOF_HDA_GENERIC
+ tristate
+ select SND_SOC_SOF_HDA_COMMON
select SND_SOC_SOF_INTEL_COMMON
+ select SND_SOC_SOF_PCI_DEV
+ select SND_INTEL_DSP_CONFIG
select SND_SOC_SOF_HDA_LINK_BASELINE
+ select SND_SOC_SOF_HDA_PROBES
+ select SND_SOC_SOF_HDA_MLINK if SND_SOC_SOF_HDA_LINK
+ help
+ This option is not user-selectable but automagically handled by
+ 'select' statements at a higher level.
+
+config SND_SOC_SOF_HDA_MLINK
+ tristate
help
This option is not user-selectable but automagically handled by
- 'select' statements at a higher level
+ 'select' statements at a higher level.
-if SND_SOC_SOF_HDA_COMMON
+if SND_SOC_SOF_HDA_GENERIC
config SND_SOC_SOF_HDA_LINK
bool "SOF support for HDA Links(HDA/HDMI)"
- depends on SND_SOC_SOF_NOCODEC=n
- select SND_SOC_SOF_PROBE_WORK_QUEUE
help
This adds support for HDA links(HDA/HDMI) with Sound Open Firmware
- for Intel(R) platforms.
+ for Intel(R) platforms.
Say Y if you want to enable HDA links with SOF.
If unsure select "N".
config SND_SOC_SOF_HDA_AUDIO_CODEC
bool "SOF support for HDAudio codecs"
depends on SND_SOC_SOF_HDA_LINK
+ select SND_SOC_SOF_PROBE_WORK_QUEUE
help
This adds support for HDAudio codecs with Sound Open Firmware
- for Intel(R) platforms.
+ for Intel(R) platforms.
Say Y if you want to enable HDAudio codecs with SOF.
If unsure select "N".
-config SND_SOC_SOF_HDA_PROBES
- bool "SOF enable probes over HDA"
- depends on SND_SOC_SOF_DEBUG_PROBES
- help
- This option enables the data probing for Intel(R).
- Intel(R) Skylake and newer platforms.
- Say Y if you want to enable probes.
- If unsure, select "N".
+endif ## SND_SOC_SOF_HDA_GENERIC
-config SND_SOC_SOF_HDA_ALWAYS_ENABLE_DMI_L1
- bool "SOF enable DMI Link L1"
+config SND_SOF_SOF_HDA_SDW_BPT
+ tristate
+ select SND_HDA_EXT_CORE
help
- This option enables DMI L1 for both playback and capture
- and disables known workarounds for specific HDaudio platforms.
- Only use to look into power optimizations on platforms not
- affected by DMI L1 issues. This option is not recommended.
- Say Y if you want to enable DMI Link L1
- If unsure, select "N".
-
-endif ## SND_SOC_SOF_HDA_COMMON
+ This option is not user-selectable but automagically handled by
+ 'select' statements at a higher level.
config SND_SOC_SOF_HDA_LINK_BASELINE
tristate
select SND_SOC_SOF_HDA if SND_SOC_SOF_HDA_LINK
help
This option is not user-selectable but automagically handled by
- 'select' statements at a higher level
+ 'select' statements at a higher level.
config SND_SOC_SOF_HDA
tristate
select SND_HDA_EXT_CORE if SND_SOC_SOF_HDA_LINK
select SND_SOC_HDAC_HDA if SND_SOC_SOF_HDA_AUDIO_CODEC
- select SND_INTEL_DSP_CONFIG
help
This option is not user-selectable but automagically handled by
- 'select' statements at a higher level
+ 'select' statements at a higher level.
+
+config SND_SOC_SOF_HDA_PROBES
+ tristate
+ select SND_SOC_SOF_DEBUG_PROBES
+ help
+ The option enables the data probing for Intel(R) Skylake and newer
+ (HDA) platforms.
+ This option is not user-selectable but automagically handled by
+ 'select' statements at a higher level.
+
+config SND_SOC_SOF_INTEL_SOUNDWIRE_LINK_BASELINE
+ tristate
+ select SOUNDWIRE_INTEL if SND_SOC_SOF_INTEL_SOUNDWIRE != n
+ select SND_INTEL_SOUNDWIRE_ACPI if SND_SOC_SOF_INTEL_SOUNDWIRE != n
+
+config SND_SOC_SOF_INTEL_SOUNDWIRE
+ tristate "SOF support for SoundWire"
+ default SND_SOC_SOF_INTEL_SOUNDWIRE_LINK_BASELINE
+ depends on SND_SOC_SOF_INTEL_SOUNDWIRE_LINK_BASELINE
+ depends on ACPI && SOUNDWIRE
+ depends on !(SOUNDWIRE=m && SND_SOC_SOF_INTEL_SOUNDWIRE_LINK_BASELINE=y)
+ help
+ This adds support for SoundWire with Sound Open Firmware
+ for Intel(R) platforms.
+ Say Y if you want to enable SoundWire links with SOF.
+ If unsure select "N".
-endif ## SND_SOC_SOF_INTEL_PCI
+endif ## SND_SOC_SOF_PCI
endif ## SND_SOC_SOF_INTEL_TOPLEVEL
diff --git a/sound/soc/sof/intel/Makefile b/sound/soc/sof/intel/Makefile
index f7e9358f1f06..aab803a495b1 100644
--- a/sound/soc/sof/intel/Makefile
+++ b/sound/soc/sof/intel/Makefile
@@ -1,20 +1,51 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
-snd-sof-intel-byt-objs := byt.o
-snd-sof-intel-bdw-objs := bdw.o
+snd-sof-acpi-intel-byt-y := byt.o
+snd-sof-acpi-intel-bdw-y := bdw.o
-snd-sof-intel-ipc-objs := intel-ipc.o
-
-snd-sof-intel-hda-common-objs := hda.o hda-loader.o hda-stream.o hda-trace.o \
+snd-sof-intel-hda-common-y := hda-loader.o hda-stream.o hda-trace.o \
hda-dsp.o hda-ipc.o hda-ctrl.o hda-pcm.o \
- hda-dai.o hda-bus.o \
- apl.o cnl.o
-snd-sof-intel-hda-common-$(CONFIG_SND_SOC_SOF_HDA_PROBES) += hda-compress.o
+ hda-dai.o hda-dai-ops.o hda-bus.o \
+ telemetry.o tracepoints.o
+
+snd-sof-intel-hda-generic-y := hda.o hda-common-ops.o
+
+snd-sof-intel-hda-mlink-y := hda-mlink.o
+
+snd-sof-intel-hda-sdw-bpt-objs := hda-sdw-bpt.o
+
+snd-sof-intel-hda-common-$(CONFIG_SND_SOC_SOF_HDA_PROBES) += hda-probes.o
-snd-sof-intel-hda-objs := hda-codec.o
+snd-sof-intel-hda-y := hda-codec.o
-obj-$(CONFIG_SND_SOC_SOF_INTEL_ATOM_HIFI_EP) += snd-sof-intel-byt.o
-obj-$(CONFIG_SND_SOC_SOF_BROADWELL) += snd-sof-intel-bdw.o
-obj-$(CONFIG_SND_SOC_SOF_INTEL_HIFI_EP_IPC) += snd-sof-intel-ipc.o
+snd-sof-intel-atom-y := atom.o
+
+obj-$(CONFIG_SND_SOC_SOF_INTEL_ATOM_HIFI_EP) += snd-sof-intel-atom.o
+obj-$(CONFIG_SND_SOC_SOF_BAYTRAIL) += snd-sof-acpi-intel-byt.o
+obj-$(CONFIG_SND_SOC_SOF_BROADWELL) += snd-sof-acpi-intel-bdw.o
obj-$(CONFIG_SND_SOC_SOF_HDA_COMMON) += snd-sof-intel-hda-common.o
+obj-$(CONFIG_SND_SOC_SOF_HDA_GENERIC) += snd-sof-intel-hda-generic.o
+obj-$(CONFIG_SND_SOC_SOF_HDA_MLINK) += snd-sof-intel-hda-mlink.o
obj-$(CONFIG_SND_SOC_SOF_HDA) += snd-sof-intel-hda.o
+
+obj-$(CONFIG_SND_SOF_SOF_HDA_SDW_BPT) += snd-sof-intel-hda-sdw-bpt.o
+
+snd-sof-pci-intel-tng-y := pci-tng.o
+snd-sof-pci-intel-skl-y := pci-skl.o skl.o hda-loader-skl.o
+snd-sof-pci-intel-apl-y := pci-apl.o apl.o
+snd-sof-pci-intel-cnl-y := pci-cnl.o cnl.o
+snd-sof-pci-intel-icl-y := pci-icl.o icl.o
+snd-sof-pci-intel-tgl-y := pci-tgl.o tgl.o
+snd-sof-pci-intel-mtl-y := pci-mtl.o mtl.o
+snd-sof-pci-intel-lnl-y := pci-lnl.o lnl.o
+snd-sof-pci-intel-ptl-y := pci-ptl.o ptl.o
+
+obj-$(CONFIG_SND_SOC_SOF_MERRIFIELD) += snd-sof-pci-intel-tng.o
+obj-$(CONFIG_SND_SOC_SOF_INTEL_SKL) += snd-sof-pci-intel-skl.o
+obj-$(CONFIG_SND_SOC_SOF_INTEL_APL) += snd-sof-pci-intel-apl.o
+obj-$(CONFIG_SND_SOC_SOF_INTEL_CNL) += snd-sof-pci-intel-cnl.o
+obj-$(CONFIG_SND_SOC_SOF_INTEL_ICL) += snd-sof-pci-intel-icl.o
+obj-$(CONFIG_SND_SOC_SOF_INTEL_TGL) += snd-sof-pci-intel-tgl.o
+obj-$(CONFIG_SND_SOC_SOF_INTEL_MTL) += snd-sof-pci-intel-mtl.o
+obj-$(CONFIG_SND_SOC_SOF_INTEL_LNL) += snd-sof-pci-intel-lnl.o
+obj-$(CONFIG_SND_SOC_SOF_INTEL_PTL) += snd-sof-pci-intel-ptl.o
diff --git a/sound/soc/sof/intel/apl.c b/sound/soc/sof/intel/apl.c
index 9e29d4fd393a..76a92eaa1359 100644
--- a/sound/soc/sof/intel/apl.c
+++ b/sound/soc/sof/intel/apl.c
@@ -3,7 +3,7 @@
// 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) 2018 Intel Corporation. All rights reserved.
+// Copyright(c) 2018 Intel Corporation
//
// Authors: Liam Girdwood <liam.r.girdwood@linux.intel.com>
// Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
@@ -15,6 +15,8 @@
* Hardware interface for audio DSP on Apollolake and GeminiLake
*/
+#include <sound/sof/ext_manifest4.h>
+#include "../ipc4-priv.h"
#include "../sof-priv.h"
#include "hda.h"
#include "../sof-audio.h"
@@ -26,117 +28,94 @@ static const struct snd_sof_debugfs_map apl_dsp_debugfs[] = {
};
/* apollolake ops */
-const struct snd_sof_dsp_ops sof_apl_ops = {
- /* probe and remove */
- .probe = hda_dsp_probe,
- .remove = hda_dsp_remove,
-
- /* Register IO */
- .write = sof_io_write,
- .read = sof_io_read,
- .write64 = sof_io_write64,
- .read64 = sof_io_read64,
-
- /* Block IO */
- .block_read = sof_block_read,
- .block_write = sof_block_write,
-
- /* doorbell */
- .irq_thread = hda_dsp_ipc_irq_thread,
-
- /* ipc */
- .send_msg = hda_dsp_ipc_send_msg,
- .fw_ready = sof_fw_ready,
- .get_mailbox_offset = hda_dsp_ipc_get_mailbox_offset,
- .get_window_offset = hda_dsp_ipc_get_window_offset,
-
- .ipc_msg_data = hda_ipc_msg_data,
- .ipc_pcm_params = hda_ipc_pcm_params,
-
- /* machine driver */
- .machine_select = hda_machine_select,
- .machine_register = sof_machine_register,
- .machine_unregister = sof_machine_unregister,
- .set_mach_params = hda_set_mach_params,
+struct snd_sof_dsp_ops sof_apl_ops;
+
+int sof_apl_ops_init(struct snd_sof_dev *sdev)
+{
+ /* common defaults */
+ memcpy(&sof_apl_ops, &sof_hda_common_ops, sizeof(struct snd_sof_dsp_ops));
+
+ /* probe/remove/shutdown */
+ sof_apl_ops.shutdown = hda_dsp_shutdown;
+
+ if (sdev->pdata->ipc_type == SOF_IPC_TYPE_3) {
+ /* doorbell */
+ sof_apl_ops.irq_thread = hda_dsp_ipc_irq_thread;
+
+ /* ipc */
+ sof_apl_ops.send_msg = hda_dsp_ipc_send_msg;
+
+ /* debug */
+ sof_apl_ops.ipc_dump = hda_ipc_dump;
+
+ sof_apl_ops.set_power_state = hda_dsp_set_power_state_ipc3;
+ }
+
+ if (sdev->pdata->ipc_type == SOF_IPC_TYPE_4) {
+ struct sof_ipc4_fw_data *ipc4_data;
+
+ sdev->private = kzalloc(sizeof(*ipc4_data), GFP_KERNEL);
+ if (!sdev->private)
+ return -ENOMEM;
+
+ ipc4_data = sdev->private;
+ ipc4_data->manifest_fw_hdr_offset = SOF_MAN4_FW_HDR_OFFSET;
+
+ ipc4_data->mtrace_type = SOF_IPC4_MTRACE_INTEL_CAVS_1_5;
+
+ /* External library loading support */
+ ipc4_data->load_library = hda_dsp_ipc4_load_library;
+
+ /* doorbell */
+ sof_apl_ops.irq_thread = hda_dsp_ipc4_irq_thread;
+
+ /* ipc */
+ sof_apl_ops.send_msg = hda_dsp_ipc4_send_msg;
+
+ /* debug */
+ sof_apl_ops.ipc_dump = hda_ipc4_dump;
+
+ sof_apl_ops.set_power_state = hda_dsp_set_power_state_ipc4;
+ }
+
+ /* set DAI driver ops */
+ hda_set_dai_drv_ops(sdev, &sof_apl_ops);
/* debug */
- .debug_map = apl_dsp_debugfs,
- .debug_map_count = ARRAY_SIZE(apl_dsp_debugfs),
- .dbg_dump = hda_dsp_dump,
- .ipc_dump = hda_ipc_dump,
-
- /* stream callbacks */
- .pcm_open = hda_dsp_pcm_open,
- .pcm_close = hda_dsp_pcm_close,
- .pcm_hw_params = hda_dsp_pcm_hw_params,
- .pcm_hw_free = hda_dsp_stream_hw_free,
- .pcm_trigger = hda_dsp_pcm_trigger,
- .pcm_pointer = hda_dsp_pcm_pointer,
-
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_PROBES)
- /* probe callbacks */
- .probe_assign = hda_probe_compr_assign,
- .probe_free = hda_probe_compr_free,
- .probe_set_params = hda_probe_compr_set_params,
- .probe_trigger = hda_probe_compr_trigger,
- .probe_pointer = hda_probe_compr_pointer,
-#endif
-
- /* firmware loading */
- .load_firmware = snd_sof_load_firmware_raw,
+ sof_apl_ops.debug_map = apl_dsp_debugfs;
+ sof_apl_ops.debug_map_count = ARRAY_SIZE(apl_dsp_debugfs);
/* firmware run */
- .run = hda_dsp_cl_boot_firmware,
+ sof_apl_ops.run = hda_dsp_cl_boot_firmware;
/* pre/post fw run */
- .pre_fw_run = hda_dsp_pre_fw_run,
- .post_fw_run = hda_dsp_post_fw_run,
-
- /* dsp core power up/down */
- .core_power_up = hda_dsp_enable_core,
- .core_power_down = hda_dsp_core_reset_power_down,
-
- /* trace callback */
- .trace_init = hda_dsp_trace_init,
- .trace_release = hda_dsp_trace_release,
- .trace_trigger = hda_dsp_trace_trigger,
-
- /* DAI drivers */
- .drv = skl_dai,
- .num_drv = SOF_SKL_NUM_DAIS,
-
- /* PM */
- .suspend = hda_dsp_suspend,
- .resume = hda_dsp_resume,
- .runtime_suspend = hda_dsp_runtime_suspend,
- .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_power_state = hda_dsp_set_power_state,
-
- /* ALSA HW info flags */
- .hw_info = SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_MMAP_VALID |
- SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_PAUSE |
- SNDRV_PCM_INFO_NO_PERIOD_WAKEUP,
-
- .arch_ops = &sof_xtensa_arch_ops,
+ sof_apl_ops.post_fw_run = hda_dsp_post_fw_run;
+
+ /* dsp core get/put */
+ sof_apl_ops.core_get = hda_dsp_core_get;
+
+ return 0;
};
-EXPORT_SYMBOL_NS(sof_apl_ops, SND_SOC_SOF_INTEL_HDA_COMMON);
const struct sof_intel_dsp_desc apl_chip_info = {
/* Apollolake */
.cores_num = 2,
.init_core_mask = 1,
- .cores_mask = HDA_DSP_CORE_MASK(0) | HDA_DSP_CORE_MASK(1),
+ .host_managed_cores_mask = GENMASK(1, 0),
.ipc_req = HDA_DSP_REG_HIPCI,
.ipc_req_mask = HDA_DSP_REG_HIPCI_BUSY,
.ipc_ack = HDA_DSP_REG_HIPCIE,
.ipc_ack_mask = HDA_DSP_REG_HIPCIE_DONE,
.ipc_ctl = HDA_DSP_REG_HIPCCTL,
+ .rom_status_reg = HDA_DSP_SRAM_REG_ROM_STATUS,
.rom_init_timeout = 150,
.ssp_count = APL_SSP_COUNT,
.ssp_base_offset = APL_SSP_BASE_OFFSET,
+ .d0i3_offset = SOF_HDA_VS_D0I3C,
+ .quirks = SOF_INTEL_PROCEN_FMT_QUIRK,
+ .check_ipc_irq = hda_dsp_check_ipc_irq,
+ .cl_init = cl_dsp_init,
+ .power_down_dsp = hda_power_down_dsp,
+ .disable_interrupts = hda_dsp_disable_interrupts,
+ .hw_ip_version = SOF_INTEL_CAVS_1_5_PLUS,
};
-EXPORT_SYMBOL_NS(apl_chip_info, SND_SOC_SOF_INTEL_HDA_COMMON);
diff --git a/sound/soc/sof/intel/atom.c b/sound/soc/sof/intel/atom.c
new file mode 100644
index 000000000000..0d364bcdcfa9
--- /dev/null
+++ b/sound/soc/sof/intel/atom.c
@@ -0,0 +1,421 @@
+// SPDX-License-Identifier: (GPL-2.0-only 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) 2018-2021 Intel Corporation
+//
+// Author: Liam Girdwood <liam.r.girdwood@linux.intel.com>
+//
+
+/*
+ * Hardware interface for audio DSP on Atom devices
+ */
+
+#include <linux/module.h>
+#include <sound/sof.h>
+#include <sound/sof/xtensa.h>
+#include <sound/soc-acpi.h>
+#include <sound/soc-acpi-intel-match.h>
+#include <sound/intel-dsp-config.h>
+#include "../ops.h"
+#include "shim.h"
+#include "atom.h"
+#include "../sof-acpi-dev.h"
+#include "../sof-audio.h"
+#include "../../intel/common/soc-intel-quirks.h"
+
+static void atom_host_done(struct snd_sof_dev *sdev);
+static void atom_dsp_done(struct snd_sof_dev *sdev);
+
+/*
+ * Debug
+ */
+
+static void atom_get_registers(struct snd_sof_dev *sdev,
+ struct sof_ipc_dsp_oops_xtensa *xoops,
+ struct sof_ipc_panic_info *panic_info,
+ u32 *stack, size_t stack_words)
+{
+ u32 offset = sdev->dsp_oops_offset;
+
+ /* first read regsisters */
+ sof_mailbox_read(sdev, offset, xoops, sizeof(*xoops));
+
+ /* note: variable AR register array is not read */
+
+ /* then get panic info */
+ if (xoops->arch_hdr.totalsize > EXCEPT_MAX_HDR_SIZE) {
+ dev_err(sdev->dev, "invalid header size 0x%x. FW oops is bogus\n",
+ xoops->arch_hdr.totalsize);
+ return;
+ }
+ offset += xoops->arch_hdr.totalsize;
+ sof_mailbox_read(sdev, offset, panic_info, sizeof(*panic_info));
+
+ /* then get the stack */
+ offset += sizeof(*panic_info);
+ sof_mailbox_read(sdev, offset, stack, stack_words * sizeof(u32));
+}
+
+void atom_dump(struct snd_sof_dev *sdev, u32 flags)
+{
+ struct sof_ipc_dsp_oops_xtensa xoops;
+ struct sof_ipc_panic_info panic_info;
+ u32 stack[STACK_DUMP_SIZE];
+ u64 status, panic, imrd, imrx;
+
+ /* now try generic SOF status messages */
+ status = snd_sof_dsp_read64(sdev, DSP_BAR, SHIM_IPCD);
+ panic = snd_sof_dsp_read64(sdev, DSP_BAR, SHIM_IPCX);
+ atom_get_registers(sdev, &xoops, &panic_info, stack,
+ STACK_DUMP_SIZE);
+ sof_print_oops_and_stack(sdev, KERN_ERR, status, panic, &xoops,
+ &panic_info, stack, STACK_DUMP_SIZE);
+
+ /* provide some context for firmware debug */
+ imrx = snd_sof_dsp_read64(sdev, DSP_BAR, SHIM_IMRX);
+ imrd = snd_sof_dsp_read64(sdev, DSP_BAR, SHIM_IMRD);
+ dev_err(sdev->dev,
+ "error: ipc host -> DSP: pending %s complete %s raw 0x%llx\n",
+ str_yes_no(panic & SHIM_IPCX_BUSY),
+ str_yes_no(panic & SHIM_IPCX_DONE), panic);
+ dev_err(sdev->dev,
+ "error: mask host: pending %s complete %s raw 0x%llx\n",
+ str_yes_no(imrx & SHIM_IMRX_BUSY),
+ str_yes_no(imrx & SHIM_IMRX_DONE), imrx);
+ dev_err(sdev->dev,
+ "error: ipc DSP -> host: pending %s complete %s raw 0x%llx\n",
+ str_yes_no(status & SHIM_IPCD_BUSY),
+ str_yes_no(status & SHIM_IPCD_DONE), status);
+ dev_err(sdev->dev,
+ "error: mask DSP: pending %s complete %s raw 0x%llx\n",
+ str_yes_no(imrd & SHIM_IMRD_BUSY),
+ str_yes_no(imrd & SHIM_IMRD_DONE), imrd);
+
+}
+EXPORT_SYMBOL_NS(atom_dump, "SND_SOC_SOF_INTEL_ATOM_HIFI_EP");
+
+/*
+ * IPC Doorbell IRQ handler and thread.
+ */
+
+irqreturn_t atom_irq_handler(int irq, void *context)
+{
+ struct snd_sof_dev *sdev = context;
+ u64 ipcx, ipcd;
+ int ret = IRQ_NONE;
+
+ ipcx = snd_sof_dsp_read64(sdev, DSP_BAR, SHIM_IPCX);
+ ipcd = snd_sof_dsp_read64(sdev, DSP_BAR, SHIM_IPCD);
+
+ if (ipcx & SHIM_BYT_IPCX_DONE) {
+
+ /* reply message from DSP, Mask Done interrupt first */
+ snd_sof_dsp_update_bits64_unlocked(sdev, DSP_BAR,
+ SHIM_IMRX,
+ SHIM_IMRX_DONE,
+ SHIM_IMRX_DONE);
+ ret = IRQ_WAKE_THREAD;
+ }
+
+ if (ipcd & SHIM_BYT_IPCD_BUSY) {
+
+ /* new message from DSP, Mask Busy interrupt first */
+ snd_sof_dsp_update_bits64_unlocked(sdev, DSP_BAR,
+ SHIM_IMRX,
+ SHIM_IMRX_BUSY,
+ SHIM_IMRX_BUSY);
+ ret = IRQ_WAKE_THREAD;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_NS(atom_irq_handler, "SND_SOC_SOF_INTEL_ATOM_HIFI_EP");
+
+irqreturn_t atom_irq_thread(int irq, void *context)
+{
+ struct snd_sof_dev *sdev = context;
+ u64 ipcx, ipcd;
+
+ ipcx = snd_sof_dsp_read64(sdev, DSP_BAR, SHIM_IPCX);
+ ipcd = snd_sof_dsp_read64(sdev, DSP_BAR, SHIM_IPCD);
+
+ /* reply message from DSP */
+ if (ipcx & SHIM_BYT_IPCX_DONE) {
+
+ spin_lock_irq(&sdev->ipc_lock);
+
+ /*
+ * handle immediate reply from DSP core. If the msg is
+ * found, set done bit in cmd_done which is called at the
+ * end of message processing function, else set it here
+ * because the done bit can't be set in cmd_done function
+ * which is triggered by msg
+ */
+ snd_sof_ipc_process_reply(sdev, ipcx);
+
+ atom_dsp_done(sdev);
+
+ spin_unlock_irq(&sdev->ipc_lock);
+ }
+
+ /* new message from DSP */
+ if (ipcd & SHIM_BYT_IPCD_BUSY) {
+
+ /* Handle messages from DSP Core */
+ if ((ipcd & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC) {
+ snd_sof_dsp_panic(sdev, PANIC_OFFSET(ipcd) + MBOX_OFFSET,
+ true);
+ } else {
+ snd_sof_ipc_msgs_rx(sdev);
+ }
+
+ atom_host_done(sdev);
+ }
+
+ return IRQ_HANDLED;
+}
+EXPORT_SYMBOL_NS(atom_irq_thread, "SND_SOC_SOF_INTEL_ATOM_HIFI_EP");
+
+int atom_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg)
+{
+ /* unmask and prepare to receive Done interrupt */
+ snd_sof_dsp_update_bits64_unlocked(sdev, DSP_BAR, SHIM_IMRX,
+ SHIM_IMRX_DONE, 0);
+
+ /* send the message */
+ sof_mailbox_write(sdev, sdev->host_box.offset, msg->msg_data,
+ msg->msg_size);
+ snd_sof_dsp_write64(sdev, DSP_BAR, SHIM_IPCX, SHIM_BYT_IPCX_BUSY);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS(atom_send_msg, "SND_SOC_SOF_INTEL_ATOM_HIFI_EP");
+
+int atom_get_mailbox_offset(struct snd_sof_dev *sdev)
+{
+ return MBOX_OFFSET;
+}
+EXPORT_SYMBOL_NS(atom_get_mailbox_offset, "SND_SOC_SOF_INTEL_ATOM_HIFI_EP");
+
+int atom_get_window_offset(struct snd_sof_dev *sdev, u32 id)
+{
+ return MBOX_OFFSET;
+}
+EXPORT_SYMBOL_NS(atom_get_window_offset, "SND_SOC_SOF_INTEL_ATOM_HIFI_EP");
+
+static void atom_host_done(struct snd_sof_dev *sdev)
+{
+ /* clear BUSY bit and set DONE bit - accept new messages */
+ snd_sof_dsp_update_bits64_unlocked(sdev, DSP_BAR, SHIM_IPCD,
+ SHIM_BYT_IPCD_BUSY |
+ SHIM_BYT_IPCD_DONE,
+ SHIM_BYT_IPCD_DONE);
+
+ /* unmask and prepare to receive next new message */
+ snd_sof_dsp_update_bits64_unlocked(sdev, DSP_BAR, SHIM_IMRX,
+ SHIM_IMRX_BUSY, 0);
+}
+
+static void atom_dsp_done(struct snd_sof_dev *sdev)
+{
+ /* clear DONE bit - tell DSP we have completed */
+ snd_sof_dsp_update_bits64_unlocked(sdev, DSP_BAR, SHIM_IPCX,
+ SHIM_BYT_IPCX_DONE, 0);
+}
+
+/*
+ * DSP control.
+ */
+
+int atom_run(struct snd_sof_dev *sdev)
+{
+ int tries = 10;
+
+ /* release stall and wait to unstall */
+ snd_sof_dsp_update_bits64(sdev, DSP_BAR, SHIM_CSR,
+ SHIM_BYT_CSR_STALL, 0x0);
+ while (tries--) {
+ if (!(snd_sof_dsp_read64(sdev, DSP_BAR, SHIM_CSR) &
+ SHIM_BYT_CSR_PWAITMODE))
+ break;
+ msleep(100);
+ }
+ if (tries < 0)
+ return -ENODEV;
+
+ /* return init core mask */
+ return 1;
+}
+EXPORT_SYMBOL_NS(atom_run, "SND_SOC_SOF_INTEL_ATOM_HIFI_EP");
+
+int atom_reset(struct snd_sof_dev *sdev)
+{
+ /* put DSP into reset, set reset vector and stall */
+ snd_sof_dsp_update_bits64(sdev, DSP_BAR, SHIM_CSR,
+ SHIM_BYT_CSR_RST | SHIM_BYT_CSR_VECTOR_SEL |
+ SHIM_BYT_CSR_STALL,
+ SHIM_BYT_CSR_RST | SHIM_BYT_CSR_VECTOR_SEL |
+ SHIM_BYT_CSR_STALL);
+
+ usleep_range(10, 15);
+
+ /* take DSP out of reset and keep stalled for FW loading */
+ snd_sof_dsp_update_bits64(sdev, DSP_BAR, SHIM_CSR,
+ SHIM_BYT_CSR_RST, 0);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS(atom_reset, "SND_SOC_SOF_INTEL_ATOM_HIFI_EP");
+
+static const char *fixup_tplg_name(struct snd_sof_dev *sdev,
+ const char *sof_tplg_filename,
+ const char *ssp_str)
+{
+ const char *tplg_filename = NULL;
+ const char *split_ext;
+ char *filename, *tmp;
+
+ filename = kstrdup(sof_tplg_filename, GFP_KERNEL);
+ if (!filename)
+ return NULL;
+
+ /* this assumes a .tplg extension */
+ tmp = filename;
+ split_ext = strsep(&tmp, ".");
+ if (split_ext)
+ tplg_filename = devm_kasprintf(sdev->dev, GFP_KERNEL,
+ "%s-%s.tplg",
+ split_ext, ssp_str);
+ kfree(filename);
+
+ return tplg_filename;
+}
+
+struct snd_soc_acpi_mach *atom_machine_select(struct snd_sof_dev *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 platform_device *pdev;
+ const char *tplg_filename;
+
+ mach = snd_soc_acpi_find_machine(desc->machines);
+ if (!mach) {
+ dev_warn(sdev->dev, "warning: No matching ASoC machine driver found\n");
+ return NULL;
+ }
+
+ pdev = to_platform_device(sdev->dev);
+ if (soc_intel_is_byt_cr(pdev)) {
+ dev_dbg(sdev->dev,
+ "BYT-CR detected, SSP0 used instead of SSP2\n");
+
+ tplg_filename = fixup_tplg_name(sdev,
+ mach->sof_tplg_filename,
+ "ssp0");
+ } else {
+ tplg_filename = mach->sof_tplg_filename;
+ }
+
+ if (!tplg_filename) {
+ dev_dbg(sdev->dev,
+ "error: no topology filename\n");
+ return NULL;
+ }
+
+ sof_pdata->tplg_filename = tplg_filename;
+ mach->mach_params.acpi_ipc_irq_index = desc->irqindex_host_ipc;
+
+ return mach;
+}
+EXPORT_SYMBOL_NS(atom_machine_select, "SND_SOC_SOF_INTEL_ATOM_HIFI_EP");
+
+/* Atom DAIs */
+struct snd_soc_dai_driver atom_dai[] = {
+{
+ .name = "ssp0-port",
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 8,
+ },
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 8,
+ },
+},
+{
+ .name = "ssp1-port",
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 8,
+ },
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 8,
+ },
+},
+{
+ .name = "ssp2-port",
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 8,
+ },
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 8,
+ }
+},
+{
+ .name = "ssp3-port",
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 8,
+ },
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 8,
+ },
+},
+{
+ .name = "ssp4-port",
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 8,
+ },
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 8,
+ },
+},
+{
+ .name = "ssp5-port",
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 8,
+ },
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 8,
+ },
+},
+};
+EXPORT_SYMBOL_NS(atom_dai, "SND_SOC_SOF_INTEL_ATOM_HIFI_EP");
+
+void atom_set_mach_params(struct snd_soc_acpi_mach *mach,
+ struct snd_sof_dev *sdev)
+{
+ struct snd_sof_pdata *pdata = sdev->pdata;
+ const struct sof_dev_desc *desc = pdata->desc;
+ struct snd_soc_acpi_mach_params *mach_params;
+
+ mach_params = &mach->mach_params;
+ mach_params->platform = dev_name(sdev->dev);
+ mach_params->num_dai_drivers = desc->ops->num_drv;
+ mach_params->dai_drivers = desc->ops->drv;
+}
+EXPORT_SYMBOL_NS(atom_set_mach_params, "SND_SOC_SOF_INTEL_ATOM_HIFI_EP");
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_DESCRIPTION("SOF support for Atom platforms");
diff --git a/sound/soc/sof/intel/atom.h b/sound/soc/sof/intel/atom.h
new file mode 100644
index 000000000000..20fb19102cb0
--- /dev/null
+++ b/sound/soc/sof/intel/atom.h
@@ -0,0 +1,74 @@
+/* SPDX-License-Identifier: (GPL-2.0-only 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) 2017-2021 Intel Corporation
+ *
+ * Author: Liam Girdwood <liam.r.girdwood@linux.intel.com>
+ */
+
+#ifndef __SOF_INTEL_ATOM_H
+#define __SOF_INTEL_ATOM_H
+
+/* DSP memories */
+#define IRAM_OFFSET 0x0C0000
+#define IRAM_SIZE (80 * 1024)
+#define DRAM_OFFSET 0x100000
+#define DRAM_SIZE (160 * 1024)
+#define SHIM_OFFSET 0x140000
+#define SHIM_SIZE_BYT 0x100
+#define SHIM_SIZE_CHT 0x118
+#define MBOX_OFFSET 0x144000
+#define MBOX_SIZE 0x1000
+#define EXCEPT_OFFSET 0x800
+#define EXCEPT_MAX_HDR_SIZE 0x400
+
+/* DSP peripherals */
+#define DMAC0_OFFSET 0x098000
+#define DMAC1_OFFSET 0x09c000
+#define DMAC2_OFFSET 0x094000
+#define DMAC_SIZE 0x420
+#define SSP0_OFFSET 0x0a0000
+#define SSP1_OFFSET 0x0a1000
+#define SSP2_OFFSET 0x0a2000
+#define SSP3_OFFSET 0x0a4000
+#define SSP4_OFFSET 0x0a5000
+#define SSP5_OFFSET 0x0a6000
+#define SSP_SIZE 0x100
+
+#define STACK_DUMP_SIZE 32
+
+#define PCI_BAR_SIZE 0x200000
+
+#define PANIC_OFFSET(x) (((x) & GENMASK_ULL(47, 32)) >> 32)
+
+/*
+ * Debug
+ */
+
+#define MBOX_DUMP_SIZE 0x30
+
+/* BARs */
+#define DSP_BAR 0
+#define PCI_BAR 1
+#define IMR_BAR 2
+
+irqreturn_t atom_irq_handler(int irq, void *context);
+irqreturn_t atom_irq_thread(int irq, void *context);
+
+int atom_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg);
+int atom_get_mailbox_offset(struct snd_sof_dev *sdev);
+int atom_get_window_offset(struct snd_sof_dev *sdev, u32 id);
+
+int atom_run(struct snd_sof_dev *sdev);
+int atom_reset(struct snd_sof_dev *sdev);
+void atom_dump(struct snd_sof_dev *sdev, u32 flags);
+
+struct snd_soc_acpi_mach *atom_machine_select(struct snd_sof_dev *sdev);
+void atom_set_mach_params(struct snd_soc_acpi_mach *mach,
+ struct snd_sof_dev *sdev);
+
+extern struct snd_soc_dai_driver atom_dai[];
+
+#endif
diff --git a/sound/soc/sof/intel/bdw.c b/sound/soc/sof/intel/bdw.c
index 99fd0bd7276e..f1287d509835 100644
--- a/sound/soc/sof/intel/bdw.c
+++ b/sound/soc/sof/intel/bdw.c
@@ -3,7 +3,7 @@
// 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) 2018 Intel Corporation. All rights reserved.
+// Copyright(c) 2018 Intel Corporation
//
// Author: Liam Girdwood <liam.r.girdwood@linux.intel.com>
//
@@ -15,8 +15,12 @@
#include <linux/module.h>
#include <sound/sof.h>
#include <sound/sof/xtensa.h>
+#include <sound/soc-acpi.h>
+#include <sound/soc-acpi-intel-match.h>
+#include <sound/intel-dsp-config.h>
#include "../ops.h"
#include "shim.h"
+#include "../sof-acpi-dev.h"
#include "../sof-audio.h"
/* BARs */
@@ -71,7 +75,6 @@ static const struct snd_sof_debugfs_map bdw_debugfs[] = {
static void bdw_host_done(struct snd_sof_dev *sdev);
static void bdw_dsp_done(struct snd_sof_dev *sdev);
-static void bdw_get_reply(struct snd_sof_dev *sdev);
/*
* DSP Control.
@@ -255,28 +258,28 @@ static void bdw_dump(struct snd_sof_dev *sdev, u32 flags)
panic = snd_sof_dsp_read(sdev, BDW_DSP_BAR, SHIM_IPCX);
bdw_get_registers(sdev, &xoops, &panic_info, stack,
BDW_STACK_DUMP_SIZE);
- snd_sof_get_status(sdev, status, panic, &xoops, &panic_info, stack,
- BDW_STACK_DUMP_SIZE);
+ sof_print_oops_and_stack(sdev, KERN_ERR, status, panic, &xoops,
+ &panic_info, stack, BDW_STACK_DUMP_SIZE);
/* provide some context for firmware debug */
imrx = snd_sof_dsp_read(sdev, BDW_DSP_BAR, SHIM_IMRX);
imrd = snd_sof_dsp_read(sdev, BDW_DSP_BAR, SHIM_IMRD);
dev_err(sdev->dev,
"error: ipc host -> DSP: pending %s complete %s raw 0x%8.8x\n",
- (panic & SHIM_IPCX_BUSY) ? "yes" : "no",
- (panic & SHIM_IPCX_DONE) ? "yes" : "no", panic);
+ str_yes_no(panic & SHIM_IPCX_BUSY),
+ str_yes_no(panic & SHIM_IPCX_DONE), panic);
dev_err(sdev->dev,
"error: mask host: pending %s complete %s raw 0x%8.8x\n",
- (imrx & SHIM_IMRX_BUSY) ? "yes" : "no",
- (imrx & SHIM_IMRX_DONE) ? "yes" : "no", imrx);
+ str_yes_no(imrx & SHIM_IMRX_BUSY),
+ str_yes_no(imrx & SHIM_IMRX_DONE), imrx);
dev_err(sdev->dev,
"error: ipc DSP -> host: pending %s complete %s raw 0x%8.8x\n",
- (status & SHIM_IPCD_BUSY) ? "yes" : "no",
- (status & SHIM_IPCD_DONE) ? "yes" : "no", status);
+ str_yes_no(status & SHIM_IPCD_BUSY),
+ str_yes_no(status & SHIM_IPCD_DONE), status);
dev_err(sdev->dev,
"error: mask DSP: pending %s complete %s raw 0x%8.8x\n",
- (imrd & SHIM_IMRD_BUSY) ? "yes" : "no",
- (imrd & SHIM_IMRD_DONE) ? "yes" : "no", imrd);
+ str_yes_no(imrd & SHIM_IMRD_BUSY),
+ str_yes_no(imrd & SHIM_IMRD_DONE), imrd);
}
/*
@@ -322,8 +325,7 @@ static irqreturn_t bdw_irq_thread(int irq, void *context)
* because the done bit can't be set in cmd_done function
* which is triggered by msg
*/
- bdw_get_reply(sdev);
- snd_sof_ipc_reply(sdev, ipcx);
+ snd_sof_ipc_process_reply(sdev, ipcx);
bdw_dsp_done(sdev);
@@ -342,8 +344,8 @@ static irqreturn_t bdw_irq_thread(int irq, void *context)
/* Handle messages from DSP Core */
if ((ipcd & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC) {
- snd_sof_dsp_panic(sdev, BDW_PANIC_OFFSET(ipcx) +
- MBOX_OFFSET);
+ snd_sof_dsp_panic(sdev, BDW_PANIC_OFFSET(ipcx) + MBOX_OFFSET,
+ true);
} else {
snd_sof_ipc_msgs_rx(sdev);
}
@@ -368,45 +370,6 @@ static int bdw_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg)
return 0;
}
-static void bdw_get_reply(struct snd_sof_dev *sdev)
-{
- struct snd_sof_ipc_msg *msg = sdev->msg;
- struct sof_ipc_reply reply;
- int ret = 0;
-
- /*
- * Sometimes, there is unexpected reply ipc arriving. The reply
- * ipc belongs to none of the ipcs sent from driver.
- * In this case, the driver must ignore the ipc.
- */
- if (!msg) {
- dev_warn(sdev->dev, "unexpected ipc interrupt raised!\n");
- return;
- }
-
- /* get reply */
- sof_mailbox_read(sdev, sdev->host_box.offset, &reply, sizeof(reply));
-
- if (reply.error < 0) {
- memcpy(msg->reply_data, &reply, sizeof(reply));
- ret = reply.error;
- } else {
- /* reply correct size ? */
- if (reply.hdr.size != msg->reply_size) {
- dev_err(sdev->dev, "error: reply expected %zu got %u bytes\n",
- msg->reply_size, reply.hdr.size);
- ret = -EINVAL;
- }
-
- /* read the message */
- if (msg->reply_size > 0)
- sof_mailbox_read(sdev, sdev->host_box.offset,
- msg->reply_data, msg->reply_size);
- }
-
- msg->reply_error = ret;
-}
-
static int bdw_get_mailbox_offset(struct snd_sof_dev *sdev)
{
return MBOX_OFFSET;
@@ -447,12 +410,20 @@ static int bdw_probe(struct snd_sof_dev *sdev)
{
struct snd_sof_pdata *pdata = sdev->pdata;
const struct sof_dev_desc *desc = pdata->desc;
- struct platform_device *pdev =
- container_of(sdev->dev, struct platform_device, dev);
+ struct platform_device *pdev = to_platform_device(sdev->dev);
+ const struct sof_intel_dsp_desc *chip;
struct resource *mmio;
u32 base, size;
int ret;
+ chip = get_chip_info(sdev->pdata);
+ if (!chip) {
+ dev_err(sdev->dev, "error: no such device supported\n");
+ return -EIO;
+ }
+
+ sdev->num_cores = chip->cores_num;
+
/* LPE base */
mmio = platform_get_resource(pdev, IORESOURCE_MEM,
desc->resindex_lpe_base);
@@ -531,13 +502,13 @@ static int bdw_probe(struct snd_sof_dev *sdev)
return ret;
}
- /* set default mailbox */
- snd_sof_dsp_mailbox_init(sdev, MBOX_OFFSET, MBOX_SIZE, 0, 0);
+ /* set default mailbox offset for FW ready message */
+ sdev->dsp_box.offset = MBOX_OFFSET;
return ret;
}
-static void bdw_machine_select(struct snd_sof_dev *sdev)
+static struct snd_soc_acpi_mach *bdw_machine_select(struct snd_sof_dev *sdev)
{
struct snd_sof_pdata *sof_pdata = sdev->pdata;
const struct sof_dev_desc *desc = sof_pdata->desc;
@@ -546,21 +517,26 @@ static void bdw_machine_select(struct snd_sof_dev *sdev)
mach = snd_soc_acpi_find_machine(desc->machines);
if (!mach) {
dev_warn(sdev->dev, "warning: No matching ASoC machine driver found\n");
- return;
+ return NULL;
}
sof_pdata->tplg_filename = mach->sof_tplg_filename;
mach->mach_params.acpi_ipc_irq_index = desc->irqindex_host_ipc;
- sof_pdata->machine = mach;
+
+ return mach;
}
-static void bdw_set_mach_params(const struct snd_soc_acpi_mach *mach,
- struct device *dev)
+static void bdw_set_mach_params(struct snd_soc_acpi_mach *mach,
+ struct snd_sof_dev *sdev)
{
+ struct snd_sof_pdata *pdata = sdev->pdata;
+ const struct sof_dev_desc *desc = pdata->desc;
struct snd_soc_acpi_mach_params *mach_params;
- mach_params = (struct snd_soc_acpi_mach_params *)&mach->mach_params;
- mach_params->platform = dev_name(dev);
+ mach_params = &mach->mach_params;
+ mach_params->platform = dev_name(sdev->dev);
+ mach_params->num_dai_drivers = desc->ops->num_drv;
+ mach_params->dai_drivers = desc->ops->drv;
}
/* Broadwell DAIs */
@@ -590,7 +566,7 @@ static struct snd_soc_dai_driver bdw_dai[] = {
};
/* broadwell ops */
-const struct snd_sof_dsp_ops sof_bdw_ops = {
+static const struct snd_sof_dsp_ops sof_bdw_ops = {
/*Device init */
.probe = bdw_probe,
@@ -598,24 +574,23 @@ const struct snd_sof_dsp_ops sof_bdw_ops = {
.run = bdw_run,
.reset = bdw_reset,
- /* Register IO */
- .write = sof_io_write,
- .read = sof_io_read,
- .write64 = sof_io_write64,
- .read64 = sof_io_read64,
+ /* Register IO uses direct mmio */
/* Block IO */
.block_read = sof_block_read,
.block_write = sof_block_write,
+ /* Mailbox IO */
+ .mailbox_read = sof_mailbox_read,
+ .mailbox_write = sof_mailbox_write,
+
/* ipc */
.send_msg = bdw_send_msg,
- .fw_ready = sof_fw_ready,
.get_mailbox_offset = bdw_get_mailbox_offset,
.get_window_offset = bdw_get_window_offset,
- .ipc_msg_data = intel_ipc_msg_data,
- .ipc_pcm_params = intel_ipc_pcm_params,
+ .ipc_msg_data = sof_ipc_msg_data,
+ .set_stream_data_offset = sof_set_stream_data_offset,
/* machine driver */
.machine_select = bdw_machine_select,
@@ -627,13 +602,11 @@ const struct snd_sof_dsp_ops sof_bdw_ops = {
.debug_map = bdw_debugfs,
.debug_map_count = ARRAY_SIZE(bdw_debugfs),
.dbg_dump = bdw_dump,
+ .debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem,
/* stream callbacks */
- .pcm_open = intel_pcm_open,
- .pcm_close = intel_pcm_close,
-
- /* Module loading */
- .load_module = snd_sof_parse_module_memcpy,
+ .pcm_open = sof_stream_pcm_open,
+ .pcm_close = sof_stream_pcm_close,
/*Firmware loading */
.load_firmware = snd_sof_load_firmware_memcpy,
@@ -649,16 +622,77 @@ const struct snd_sof_dsp_ops sof_bdw_ops = {
SNDRV_PCM_INFO_PAUSE |
SNDRV_PCM_INFO_BATCH,
- .arch_ops = &sof_xtensa_arch_ops,
+ .dsp_arch_ops = &sof_xtensa_arch_ops,
};
-EXPORT_SYMBOL_NS(sof_bdw_ops, SND_SOC_SOF_BROADWELL);
-const struct sof_intel_dsp_desc bdw_chip_info = {
+static const struct sof_intel_dsp_desc bdw_chip_info = {
.cores_num = 1,
- .cores_mask = 1,
+ .host_managed_cores_mask = 1,
+ .hw_ip_version = SOF_INTEL_BROADWELL,
+};
+
+static const struct sof_dev_desc sof_acpi_broadwell_desc = {
+ .machines = snd_soc_acpi_intel_broadwell_machines,
+ .resindex_lpe_base = 0,
+ .resindex_pcicfg_base = 1,
+ .resindex_imr_base = -1,
+ .irqindex_host_ipc = 0,
+ .chip_info = &bdw_chip_info,
+ .ipc_supported_mask = BIT(SOF_IPC_TYPE_3),
+ .ipc_default = SOF_IPC_TYPE_3,
+ .default_fw_path = {
+ [SOF_IPC_TYPE_3] = "intel/sof",
+ },
+ .default_tplg_path = {
+ [SOF_IPC_TYPE_3] = "intel/sof-tplg",
+ },
+ .default_fw_filename = {
+ [SOF_IPC_TYPE_3] = "sof-bdw.ri",
+ },
+ .nocodec_tplg_filename = "sof-bdw-nocodec.tplg",
+ .ops = &sof_bdw_ops,
+};
+
+static const struct acpi_device_id sof_broadwell_match[] = {
+ { "INT3438", (unsigned long)&sof_acpi_broadwell_desc },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, sof_broadwell_match);
+
+static int sof_broadwell_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ const struct acpi_device_id *id;
+ const struct sof_dev_desc *desc;
+ int ret;
+
+ id = acpi_match_device(dev->driver->acpi_match_table, dev);
+ if (!id)
+ return -ENODEV;
+
+ ret = snd_intel_acpi_dsp_driver_probe(dev, id->id);
+ if (ret != SND_INTEL_DSP_DRIVER_ANY && ret != SND_INTEL_DSP_DRIVER_SOF) {
+ dev_dbg(dev, "SOF ACPI driver not selected, aborting probe\n");
+ return -ENODEV;
+ }
+
+ desc = (const struct sof_dev_desc *)id->driver_data;
+ return sof_acpi_probe(pdev, desc);
+}
+
+/* acpi_driver definition */
+static struct platform_driver snd_sof_acpi_intel_bdw_driver = {
+ .probe = sof_broadwell_probe,
+ .remove = sof_acpi_remove,
+ .driver = {
+ .name = "sof-audio-acpi-intel-bdw",
+ .pm = pm_ptr(&sof_acpi_pm),
+ .acpi_match_table = sof_broadwell_match,
+ },
};
-EXPORT_SYMBOL_NS(bdw_chip_info, SND_SOC_SOF_BROADWELL);
+module_platform_driver(snd_sof_acpi_intel_bdw_driver);
MODULE_LICENSE("Dual BSD/GPL");
-MODULE_IMPORT_NS(SND_SOC_SOF_INTEL_HIFI_EP_IPC);
-MODULE_IMPORT_NS(SND_SOC_SOF_XTENSA);
+MODULE_DESCRIPTION("SOF support for Broadwell platforms");
+MODULE_IMPORT_NS("SND_SOC_SOF_XTENSA");
+MODULE_IMPORT_NS("SND_SOC_SOF_ACPI_DEV");
diff --git a/sound/soc/sof/intel/byt.c b/sound/soc/sof/intel/byt.c
index 49f67f1b94e0..18208f77b84d 100644
--- a/sound/soc/sof/intel/byt.c
+++ b/sound/soc/sof/intel/byt.c
@@ -3,7 +3,7 @@
// 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) 2018 Intel Corporation. All rights reserved.
+// Copyright(c) 2018 Intel Corporation
//
// Author: Liam Girdwood <liam.r.girdwood@linux.intel.com>
//
@@ -15,658 +15,70 @@
#include <linux/module.h>
#include <sound/sof.h>
#include <sound/sof/xtensa.h>
+#include <sound/soc-acpi.h>
+#include <sound/soc-acpi-intel-match.h>
+#include <sound/intel-dsp-config.h>
#include "../ops.h"
+#include "atom.h"
#include "shim.h"
+#include "../sof-acpi-dev.h"
#include "../sof-audio.h"
#include "../../intel/common/soc-intel-quirks.h"
-/* DSP memories */
-#define IRAM_OFFSET 0x0C0000
-#define IRAM_SIZE (80 * 1024)
-#define DRAM_OFFSET 0x100000
-#define DRAM_SIZE (160 * 1024)
-#define SHIM_OFFSET 0x140000
-#define SHIM_SIZE_BYT 0x100
-#define SHIM_SIZE_CHT 0x118
-#define MBOX_OFFSET 0x144000
-#define MBOX_SIZE 0x1000
-#define EXCEPT_OFFSET 0x800
-#define EXCEPT_MAX_HDR_SIZE 0x400
-
-/* DSP peripherals */
-#define DMAC0_OFFSET 0x098000
-#define DMAC1_OFFSET 0x09c000
-#define DMAC2_OFFSET 0x094000
-#define DMAC_SIZE 0x420
-#define SSP0_OFFSET 0x0a0000
-#define SSP1_OFFSET 0x0a1000
-#define SSP2_OFFSET 0x0a2000
-#define SSP3_OFFSET 0x0a4000
-#define SSP4_OFFSET 0x0a5000
-#define SSP5_OFFSET 0x0a6000
-#define SSP_SIZE 0x100
-
-#define BYT_STACK_DUMP_SIZE 32
-
-#define BYT_PCI_BAR_SIZE 0x200000
-
-#define BYT_PANIC_OFFSET(x) (((x) & GENMASK_ULL(47, 32)) >> 32)
-
-/*
- * Debug
- */
-
-#define MBOX_DUMP_SIZE 0x30
-
-/* BARs */
-#define BYT_DSP_BAR 0
-#define BYT_PCI_BAR 1
-#define BYT_IMR_BAR 2
-
static const struct snd_sof_debugfs_map byt_debugfs[] = {
- {"dmac0", BYT_DSP_BAR, DMAC0_OFFSET, DMAC_SIZE,
+ {"dmac0", DSP_BAR, DMAC0_OFFSET, DMAC_SIZE,
SOF_DEBUGFS_ACCESS_ALWAYS},
- {"dmac1", BYT_DSP_BAR, DMAC1_OFFSET, DMAC_SIZE,
+ {"dmac1", DSP_BAR, DMAC1_OFFSET, DMAC_SIZE,
SOF_DEBUGFS_ACCESS_ALWAYS},
- {"ssp0", BYT_DSP_BAR, SSP0_OFFSET, SSP_SIZE,
+ {"ssp0", DSP_BAR, SSP0_OFFSET, SSP_SIZE,
SOF_DEBUGFS_ACCESS_ALWAYS},
- {"ssp1", BYT_DSP_BAR, SSP1_OFFSET, SSP_SIZE,
+ {"ssp1", DSP_BAR, SSP1_OFFSET, SSP_SIZE,
SOF_DEBUGFS_ACCESS_ALWAYS},
- {"ssp2", BYT_DSP_BAR, SSP2_OFFSET, SSP_SIZE,
+ {"ssp2", DSP_BAR, SSP2_OFFSET, SSP_SIZE,
SOF_DEBUGFS_ACCESS_ALWAYS},
- {"iram", BYT_DSP_BAR, IRAM_OFFSET, IRAM_SIZE,
+ {"iram", DSP_BAR, IRAM_OFFSET, IRAM_SIZE,
SOF_DEBUGFS_ACCESS_D0_ONLY},
- {"dram", BYT_DSP_BAR, DRAM_OFFSET, DRAM_SIZE,
+ {"dram", DSP_BAR, DRAM_OFFSET, DRAM_SIZE,
SOF_DEBUGFS_ACCESS_D0_ONLY},
- {"shim", BYT_DSP_BAR, SHIM_OFFSET, SHIM_SIZE_BYT,
+ {"shim", DSP_BAR, SHIM_OFFSET, SHIM_SIZE_BYT,
SOF_DEBUGFS_ACCESS_ALWAYS},
};
-static void byt_host_done(struct snd_sof_dev *sdev);
-static void byt_dsp_done(struct snd_sof_dev *sdev);
-static void byt_get_reply(struct snd_sof_dev *sdev);
-
-/*
- * Debug
- */
-
-static void byt_get_registers(struct snd_sof_dev *sdev,
- struct sof_ipc_dsp_oops_xtensa *xoops,
- struct sof_ipc_panic_info *panic_info,
- u32 *stack, size_t stack_words)
-{
- u32 offset = sdev->dsp_oops_offset;
-
- /* first read regsisters */
- sof_mailbox_read(sdev, offset, xoops, sizeof(*xoops));
-
- /* note: variable AR register array is not read */
-
- /* then get panic info */
- if (xoops->arch_hdr.totalsize > EXCEPT_MAX_HDR_SIZE) {
- dev_err(sdev->dev, "invalid header size 0x%x. FW oops is bogus\n",
- xoops->arch_hdr.totalsize);
- return;
- }
- offset += xoops->arch_hdr.totalsize;
- sof_mailbox_read(sdev, offset, panic_info, sizeof(*panic_info));
-
- /* then get the stack */
- offset += sizeof(*panic_info);
- sof_mailbox_read(sdev, offset, stack, stack_words * sizeof(u32));
-}
-
-static void byt_dump(struct snd_sof_dev *sdev, u32 flags)
-{
- struct sof_ipc_dsp_oops_xtensa xoops;
- struct sof_ipc_panic_info panic_info;
- u32 stack[BYT_STACK_DUMP_SIZE];
- u64 status, panic, imrd, imrx;
-
- /* now try generic SOF status messages */
- status = snd_sof_dsp_read64(sdev, BYT_DSP_BAR, SHIM_IPCD);
- panic = snd_sof_dsp_read64(sdev, BYT_DSP_BAR, SHIM_IPCX);
- byt_get_registers(sdev, &xoops, &panic_info, stack,
- BYT_STACK_DUMP_SIZE);
- snd_sof_get_status(sdev, status, panic, &xoops, &panic_info, stack,
- BYT_STACK_DUMP_SIZE);
-
- /* provide some context for firmware debug */
- imrx = snd_sof_dsp_read64(sdev, BYT_DSP_BAR, SHIM_IMRX);
- imrd = snd_sof_dsp_read64(sdev, BYT_DSP_BAR, SHIM_IMRD);
- dev_err(sdev->dev,
- "error: ipc host -> DSP: pending %s complete %s raw 0x%llx\n",
- (panic & SHIM_IPCX_BUSY) ? "yes" : "no",
- (panic & SHIM_IPCX_DONE) ? "yes" : "no", panic);
- dev_err(sdev->dev,
- "error: mask host: pending %s complete %s raw 0x%llx\n",
- (imrx & SHIM_IMRX_BUSY) ? "yes" : "no",
- (imrx & SHIM_IMRX_DONE) ? "yes" : "no", imrx);
- dev_err(sdev->dev,
- "error: ipc DSP -> host: pending %s complete %s raw 0x%llx\n",
- (status & SHIM_IPCD_BUSY) ? "yes" : "no",
- (status & SHIM_IPCD_DONE) ? "yes" : "no", status);
- dev_err(sdev->dev,
- "error: mask DSP: pending %s complete %s raw 0x%llx\n",
- (imrd & SHIM_IMRD_BUSY) ? "yes" : "no",
- (imrd & SHIM_IMRD_DONE) ? "yes" : "no", imrd);
-
-}
-
-/*
- * IPC Doorbell IRQ handler and thread.
- */
-
-static irqreturn_t byt_irq_handler(int irq, void *context)
-{
- struct snd_sof_dev *sdev = context;
- u64 ipcx, ipcd;
- int ret = IRQ_NONE;
-
- ipcx = snd_sof_dsp_read64(sdev, BYT_DSP_BAR, SHIM_IPCX);
- ipcd = snd_sof_dsp_read64(sdev, BYT_DSP_BAR, SHIM_IPCD);
-
- if (ipcx & SHIM_BYT_IPCX_DONE) {
-
- /* reply message from DSP, Mask Done interrupt first */
- snd_sof_dsp_update_bits64_unlocked(sdev, BYT_DSP_BAR,
- SHIM_IMRX,
- SHIM_IMRX_DONE,
- SHIM_IMRX_DONE);
- ret = IRQ_WAKE_THREAD;
- }
-
- if (ipcd & SHIM_BYT_IPCD_BUSY) {
-
- /* new message from DSP, Mask Busy interrupt first */
- snd_sof_dsp_update_bits64_unlocked(sdev, BYT_DSP_BAR,
- SHIM_IMRX,
- SHIM_IMRX_BUSY,
- SHIM_IMRX_BUSY);
- ret = IRQ_WAKE_THREAD;
- }
-
- return ret;
-}
-
-static irqreturn_t byt_irq_thread(int irq, void *context)
-{
- struct snd_sof_dev *sdev = context;
- u64 ipcx, ipcd;
-
- ipcx = snd_sof_dsp_read64(sdev, BYT_DSP_BAR, SHIM_IPCX);
- ipcd = snd_sof_dsp_read64(sdev, BYT_DSP_BAR, SHIM_IPCD);
-
- /* reply message from DSP */
- if (ipcx & SHIM_BYT_IPCX_DONE) {
-
- spin_lock_irq(&sdev->ipc_lock);
-
- /*
- * handle immediate reply from DSP core. If the msg is
- * found, set done bit in cmd_done which is called at the
- * end of message processing function, else set it here
- * because the done bit can't be set in cmd_done function
- * which is triggered by msg
- */
- byt_get_reply(sdev);
- snd_sof_ipc_reply(sdev, ipcx);
-
- byt_dsp_done(sdev);
-
- spin_unlock_irq(&sdev->ipc_lock);
- }
-
- /* new message from DSP */
- if (ipcd & SHIM_BYT_IPCD_BUSY) {
-
- /* Handle messages from DSP Core */
- if ((ipcd & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC) {
- snd_sof_dsp_panic(sdev, BYT_PANIC_OFFSET(ipcd) +
- MBOX_OFFSET);
- } else {
- snd_sof_ipc_msgs_rx(sdev);
- }
-
- byt_host_done(sdev);
- }
-
- return IRQ_HANDLED;
-}
-
-static int byt_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg)
-{
- /* unmask and prepare to receive Done interrupt */
- snd_sof_dsp_update_bits64_unlocked(sdev, BYT_DSP_BAR, SHIM_IMRX,
- SHIM_IMRX_DONE, 0);
-
- /* send the message */
- sof_mailbox_write(sdev, sdev->host_box.offset, msg->msg_data,
- msg->msg_size);
- snd_sof_dsp_write64(sdev, BYT_DSP_BAR, SHIM_IPCX, SHIM_BYT_IPCX_BUSY);
-
- return 0;
-}
-
-static void byt_get_reply(struct snd_sof_dev *sdev)
-{
- struct snd_sof_ipc_msg *msg = sdev->msg;
- struct sof_ipc_reply reply;
- int ret = 0;
-
- /*
- * Sometimes, there is unexpected reply ipc arriving. The reply
- * ipc belongs to none of the ipcs sent from driver.
- * In this case, the driver must ignore the ipc.
- */
- if (!msg) {
- dev_warn(sdev->dev, "unexpected ipc interrupt raised!\n");
- return;
- }
-
- /* get reply */
- sof_mailbox_read(sdev, sdev->host_box.offset, &reply, sizeof(reply));
-
- if (reply.error < 0) {
- memcpy(msg->reply_data, &reply, sizeof(reply));
- ret = reply.error;
- } else {
- /* reply correct size ? */
- if (reply.hdr.size != msg->reply_size) {
- dev_err(sdev->dev, "error: reply expected %zu got %u bytes\n",
- msg->reply_size, reply.hdr.size);
- ret = -EINVAL;
- }
-
- /* read the message */
- if (msg->reply_size > 0)
- sof_mailbox_read(sdev, sdev->host_box.offset,
- msg->reply_data, msg->reply_size);
- }
-
- msg->reply_error = ret;
-}
-
-static int byt_get_mailbox_offset(struct snd_sof_dev *sdev)
-{
- return MBOX_OFFSET;
-}
-
-static int byt_get_window_offset(struct snd_sof_dev *sdev, u32 id)
-{
- return MBOX_OFFSET;
-}
-
-static void byt_host_done(struct snd_sof_dev *sdev)
-{
- /* clear BUSY bit and set DONE bit - accept new messages */
- snd_sof_dsp_update_bits64_unlocked(sdev, BYT_DSP_BAR, SHIM_IPCD,
- SHIM_BYT_IPCD_BUSY |
- SHIM_BYT_IPCD_DONE,
- SHIM_BYT_IPCD_DONE);
-
- /* unmask and prepare to receive next new message */
- snd_sof_dsp_update_bits64_unlocked(sdev, BYT_DSP_BAR, SHIM_IMRX,
- SHIM_IMRX_BUSY, 0);
-}
-
-static void byt_dsp_done(struct snd_sof_dev *sdev)
-{
- /* clear DONE bit - tell DSP we have completed */
- snd_sof_dsp_update_bits64_unlocked(sdev, BYT_DSP_BAR, SHIM_IPCX,
- SHIM_BYT_IPCX_DONE, 0);
-}
-
-/*
- * DSP control.
- */
-
-static int byt_run(struct snd_sof_dev *sdev)
-{
- int tries = 10;
-
- /* release stall and wait to unstall */
- snd_sof_dsp_update_bits64(sdev, BYT_DSP_BAR, SHIM_CSR,
- SHIM_BYT_CSR_STALL, 0x0);
- while (tries--) {
- if (!(snd_sof_dsp_read64(sdev, BYT_DSP_BAR, SHIM_CSR) &
- SHIM_BYT_CSR_PWAITMODE))
- break;
- msleep(100);
- }
- if (tries < 0) {
- dev_err(sdev->dev, "error: unable to run DSP firmware\n");
- byt_dump(sdev, SOF_DBG_REGS | SOF_DBG_MBOX);
- return -ENODEV;
- }
-
- /* return init core mask */
- return 1;
-}
-
-static int byt_reset(struct snd_sof_dev *sdev)
-{
- /* put DSP into reset, set reset vector and stall */
- snd_sof_dsp_update_bits64(sdev, BYT_DSP_BAR, SHIM_CSR,
- SHIM_BYT_CSR_RST | SHIM_BYT_CSR_VECTOR_SEL |
- SHIM_BYT_CSR_STALL,
- SHIM_BYT_CSR_RST | SHIM_BYT_CSR_VECTOR_SEL |
- SHIM_BYT_CSR_STALL);
-
- usleep_range(10, 15);
-
- /* take DSP out of reset and keep stalled for FW loading */
- snd_sof_dsp_update_bits64(sdev, BYT_DSP_BAR, SHIM_CSR,
- SHIM_BYT_CSR_RST, 0);
-
- return 0;
-}
-
-static const char *fixup_tplg_name(struct snd_sof_dev *sdev,
- const char *sof_tplg_filename,
- const char *ssp_str)
-{
- const char *tplg_filename = NULL;
- char *filename;
- char *split_ext;
-
- filename = devm_kstrdup(sdev->dev, sof_tplg_filename, GFP_KERNEL);
- if (!filename)
- return NULL;
-
- /* this assumes a .tplg extension */
- split_ext = strsep(&filename, ".");
- if (split_ext) {
- tplg_filename = devm_kasprintf(sdev->dev, GFP_KERNEL,
- "%s-%s.tplg",
- split_ext, ssp_str);
- if (!tplg_filename)
- return NULL;
- }
- return tplg_filename;
-}
-
-static void byt_machine_select(struct snd_sof_dev *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 platform_device *pdev;
- const char *tplg_filename;
-
- mach = snd_soc_acpi_find_machine(desc->machines);
- if (!mach) {
- dev_warn(sdev->dev, "warning: No matching ASoC machine driver found\n");
- return;
- }
-
- pdev = to_platform_device(sdev->dev);
- if (soc_intel_is_byt_cr(pdev)) {
- dev_dbg(sdev->dev,
- "BYT-CR detected, SSP0 used instead of SSP2\n");
-
- tplg_filename = fixup_tplg_name(sdev,
- mach->sof_tplg_filename,
- "ssp0");
- } else {
- tplg_filename = mach->sof_tplg_filename;
- }
-
- if (!tplg_filename) {
- dev_dbg(sdev->dev,
- "error: no topology filename\n");
- return;
- }
-
- sof_pdata->tplg_filename = tplg_filename;
- mach->mach_params.acpi_ipc_irq_index = desc->irqindex_host_ipc;
- sof_pdata->machine = mach;
-}
-
-static void byt_set_mach_params(const struct snd_soc_acpi_mach *mach,
- struct device *dev)
-{
- struct snd_soc_acpi_mach_params *mach_params;
-
- mach_params = (struct snd_soc_acpi_mach_params *)&mach->mach_params;
- mach_params->platform = dev_name(dev);
-}
-
-/* Baytrail DAIs */
-static struct snd_soc_dai_driver byt_dai[] = {
-{
- .name = "ssp0-port",
- .playback = {
- .channels_min = 1,
- .channels_max = 8,
- },
- .capture = {
- .channels_min = 1,
- .channels_max = 8,
- },
-},
-{
- .name = "ssp1-port",
- .playback = {
- .channels_min = 1,
- .channels_max = 8,
- },
- .capture = {
- .channels_min = 1,
- .channels_max = 8,
- },
-},
-{
- .name = "ssp2-port",
- .playback = {
- .channels_min = 1,
- .channels_max = 8,
- },
- .capture = {
- .channels_min = 1,
- .channels_max = 8,
- }
-},
-{
- .name = "ssp3-port",
- .playback = {
- .channels_min = 1,
- .channels_max = 8,
- },
- .capture = {
- .channels_min = 1,
- .channels_max = 8,
- },
-},
-{
- .name = "ssp4-port",
- .playback = {
- .channels_min = 1,
- .channels_max = 8,
- },
- .capture = {
- .channels_min = 1,
- .channels_max = 8,
- },
-},
-{
- .name = "ssp5-port",
- .playback = {
- .channels_min = 1,
- .channels_max = 8,
- },
- .capture = {
- .channels_min = 1,
- .channels_max = 8,
- },
-},
-};
-
-/*
- * Probe and remove.
- */
-
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_MERRIFIELD)
-
-static int tangier_pci_probe(struct snd_sof_dev *sdev)
-{
- struct snd_sof_pdata *pdata = sdev->pdata;
- const struct sof_dev_desc *desc = pdata->desc;
- struct pci_dev *pci = to_pci_dev(sdev->dev);
- u32 base, size;
- int ret;
-
- /* DSP DMA can only access low 31 bits of host memory */
- ret = dma_coerce_mask_and_coherent(&pci->dev, DMA_BIT_MASK(31));
- if (ret < 0) {
- dev_err(sdev->dev, "error: failed to set DMA mask %d\n", ret);
- return ret;
- }
-
- /* LPE base */
- base = pci_resource_start(pci, desc->resindex_lpe_base) - IRAM_OFFSET;
- size = BYT_PCI_BAR_SIZE;
-
- dev_dbg(sdev->dev, "LPE PHY base at 0x%x size 0x%x", base, size);
- sdev->bar[BYT_DSP_BAR] = devm_ioremap(sdev->dev, base, size);
- if (!sdev->bar[BYT_DSP_BAR]) {
- dev_err(sdev->dev, "error: failed to ioremap LPE base 0x%x size 0x%x\n",
- base, size);
- return -ENODEV;
- }
- dev_dbg(sdev->dev, "LPE VADDR %p\n", sdev->bar[BYT_DSP_BAR]);
-
- /* IMR base - optional */
- if (desc->resindex_imr_base == -1)
- goto irq;
-
- base = pci_resource_start(pci, desc->resindex_imr_base);
- size = pci_resource_len(pci, desc->resindex_imr_base);
-
- /* some BIOSes don't map IMR */
- if (base == 0x55aa55aa || base == 0x0) {
- dev_info(sdev->dev, "IMR not set by BIOS. Ignoring\n");
- goto irq;
- }
-
- dev_dbg(sdev->dev, "IMR base at 0x%x size 0x%x", base, size);
- sdev->bar[BYT_IMR_BAR] = devm_ioremap(sdev->dev, base, size);
- if (!sdev->bar[BYT_IMR_BAR]) {
- dev_err(sdev->dev, "error: failed to ioremap IMR base 0x%x size 0x%x\n",
- base, size);
- return -ENODEV;
- }
- dev_dbg(sdev->dev, "IMR VADDR %p\n", sdev->bar[BYT_IMR_BAR]);
-
-irq:
- /* register our IRQ */
- sdev->ipc_irq = pci->irq;
- dev_dbg(sdev->dev, "using IRQ %d\n", sdev->ipc_irq);
- ret = devm_request_threaded_irq(sdev->dev, sdev->ipc_irq,
- byt_irq_handler, byt_irq_thread,
- 0, "AudioDSP", sdev);
- if (ret < 0) {
- dev_err(sdev->dev, "error: failed to register IRQ %d\n",
- sdev->ipc_irq);
- return ret;
- }
-
- /* enable BUSY and disable DONE Interrupt by default */
- snd_sof_dsp_update_bits64(sdev, BYT_DSP_BAR, SHIM_IMRX,
- SHIM_IMRX_BUSY | SHIM_IMRX_DONE,
- SHIM_IMRX_DONE);
-
- /* set default mailbox offset for FW ready message */
- sdev->dsp_box.offset = MBOX_OFFSET;
-
- return ret;
-}
-
-const struct snd_sof_dsp_ops sof_tng_ops = {
- /* device init */
- .probe = tangier_pci_probe,
-
- /* DSP core boot / reset */
- .run = byt_run,
- .reset = byt_reset,
-
- /* Register IO */
- .write = sof_io_write,
- .read = sof_io_read,
- .write64 = sof_io_write64,
- .read64 = sof_io_read64,
-
- /* Block IO */
- .block_read = sof_block_read,
- .block_write = sof_block_write,
-
- /* doorbell */
- .irq_handler = byt_irq_handler,
- .irq_thread = byt_irq_thread,
-
- /* ipc */
- .send_msg = byt_send_msg,
- .fw_ready = sof_fw_ready,
- .get_mailbox_offset = byt_get_mailbox_offset,
- .get_window_offset = byt_get_window_offset,
-
- .ipc_msg_data = intel_ipc_msg_data,
- .ipc_pcm_params = intel_ipc_pcm_params,
-
- /* machine driver */
- .machine_select = byt_machine_select,
- .machine_register = sof_machine_register,
- .machine_unregister = sof_machine_unregister,
- .set_mach_params = byt_set_mach_params,
-
- /* debug */
- .debug_map = byt_debugfs,
- .debug_map_count = ARRAY_SIZE(byt_debugfs),
- .dbg_dump = byt_dump,
-
- /* stream callbacks */
- .pcm_open = intel_pcm_open,
- .pcm_close = intel_pcm_close,
-
- /* module loading */
- .load_module = snd_sof_parse_module_memcpy,
-
- /*Firmware loading */
- .load_firmware = snd_sof_load_firmware_memcpy,
-
- /* DAI drivers */
- .drv = byt_dai,
- .num_drv = 3, /* we have only 3 SSPs on byt*/
-
- /* ALSA HW info flags */
- .hw_info = SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_MMAP_VALID |
- SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_PAUSE |
- SNDRV_PCM_INFO_BATCH,
-
- .arch_ops = &sof_xtensa_arch_ops,
-};
-EXPORT_SYMBOL_NS(sof_tng_ops, SND_SOC_SOF_MERRIFIELD);
-
-const struct sof_intel_dsp_desc tng_chip_info = {
- .cores_num = 1,
- .cores_mask = 1,
+static const struct snd_sof_debugfs_map cht_debugfs[] = {
+ {"dmac0", DSP_BAR, DMAC0_OFFSET, DMAC_SIZE,
+ SOF_DEBUGFS_ACCESS_ALWAYS},
+ {"dmac1", DSP_BAR, DMAC1_OFFSET, DMAC_SIZE,
+ SOF_DEBUGFS_ACCESS_ALWAYS},
+ {"dmac2", DSP_BAR, DMAC2_OFFSET, DMAC_SIZE,
+ SOF_DEBUGFS_ACCESS_ALWAYS},
+ {"ssp0", DSP_BAR, SSP0_OFFSET, SSP_SIZE,
+ SOF_DEBUGFS_ACCESS_ALWAYS},
+ {"ssp1", DSP_BAR, SSP1_OFFSET, SSP_SIZE,
+ SOF_DEBUGFS_ACCESS_ALWAYS},
+ {"ssp2", DSP_BAR, SSP2_OFFSET, SSP_SIZE,
+ SOF_DEBUGFS_ACCESS_ALWAYS},
+ {"ssp3", DSP_BAR, SSP3_OFFSET, SSP_SIZE,
+ SOF_DEBUGFS_ACCESS_ALWAYS},
+ {"ssp4", DSP_BAR, SSP4_OFFSET, SSP_SIZE,
+ SOF_DEBUGFS_ACCESS_ALWAYS},
+ {"ssp5", DSP_BAR, SSP5_OFFSET, SSP_SIZE,
+ SOF_DEBUGFS_ACCESS_ALWAYS},
+ {"iram", DSP_BAR, IRAM_OFFSET, IRAM_SIZE,
+ SOF_DEBUGFS_ACCESS_D0_ONLY},
+ {"dram", DSP_BAR, DRAM_OFFSET, DRAM_SIZE,
+ SOF_DEBUGFS_ACCESS_D0_ONLY},
+ {"shim", DSP_BAR, SHIM_OFFSET, SHIM_SIZE_CHT,
+ SOF_DEBUGFS_ACCESS_ALWAYS},
};
-EXPORT_SYMBOL_NS(tng_chip_info, SND_SOC_SOF_MERRIFIELD);
-
-#endif /* CONFIG_SND_SOC_SOF_MERRIFIELD */
-
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_BAYTRAIL)
static void byt_reset_dsp_disable_int(struct snd_sof_dev *sdev)
{
/* Disable Interrupt from both sides */
- snd_sof_dsp_update_bits64(sdev, BYT_DSP_BAR, SHIM_IMRX, 0x3, 0x3);
- snd_sof_dsp_update_bits64(sdev, BYT_DSP_BAR, SHIM_IMRD, 0x3, 0x3);
+ snd_sof_dsp_update_bits64(sdev, DSP_BAR, SHIM_IMRX, 0x3, 0x3);
+ snd_sof_dsp_update_bits64(sdev, DSP_BAR, SHIM_IMRD, 0x3, 0x3);
/* Put DSP into reset, set reset vector */
- snd_sof_dsp_update_bits64(sdev, BYT_DSP_BAR, SHIM_CSR,
+ snd_sof_dsp_update_bits64(sdev, DSP_BAR, SHIM_CSR,
SHIM_BYT_CSR_RST | SHIM_BYT_CSR_VECTOR_SEL,
SHIM_BYT_CSR_RST | SHIM_BYT_CSR_VECTOR_SEL);
}
@@ -681,57 +93,36 @@ static int byt_suspend(struct snd_sof_dev *sdev, u32 target_state)
static int byt_resume(struct snd_sof_dev *sdev)
{
/* enable BUSY and disable DONE Interrupt by default */
- snd_sof_dsp_update_bits64(sdev, BYT_DSP_BAR, SHIM_IMRX,
+ snd_sof_dsp_update_bits64(sdev, DSP_BAR, SHIM_IMRX,
SHIM_IMRX_BUSY | SHIM_IMRX_DONE,
SHIM_IMRX_DONE);
return 0;
}
-static int byt_remove(struct snd_sof_dev *sdev)
+static void byt_remove(struct snd_sof_dev *sdev)
{
byt_reset_dsp_disable_int(sdev);
-
- return 0;
}
-static const struct snd_sof_debugfs_map cht_debugfs[] = {
- {"dmac0", BYT_DSP_BAR, DMAC0_OFFSET, DMAC_SIZE,
- SOF_DEBUGFS_ACCESS_ALWAYS},
- {"dmac1", BYT_DSP_BAR, DMAC1_OFFSET, DMAC_SIZE,
- SOF_DEBUGFS_ACCESS_ALWAYS},
- {"dmac2", BYT_DSP_BAR, DMAC2_OFFSET, DMAC_SIZE,
- SOF_DEBUGFS_ACCESS_ALWAYS},
- {"ssp0", BYT_DSP_BAR, SSP0_OFFSET, SSP_SIZE,
- SOF_DEBUGFS_ACCESS_ALWAYS},
- {"ssp1", BYT_DSP_BAR, SSP1_OFFSET, SSP_SIZE,
- SOF_DEBUGFS_ACCESS_ALWAYS},
- {"ssp2", BYT_DSP_BAR, SSP2_OFFSET, SSP_SIZE,
- SOF_DEBUGFS_ACCESS_ALWAYS},
- {"ssp3", BYT_DSP_BAR, SSP3_OFFSET, SSP_SIZE,
- SOF_DEBUGFS_ACCESS_ALWAYS},
- {"ssp4", BYT_DSP_BAR, SSP4_OFFSET, SSP_SIZE,
- SOF_DEBUGFS_ACCESS_ALWAYS},
- {"ssp5", BYT_DSP_BAR, SSP5_OFFSET, SSP_SIZE,
- SOF_DEBUGFS_ACCESS_ALWAYS},
- {"iram", BYT_DSP_BAR, IRAM_OFFSET, IRAM_SIZE,
- SOF_DEBUGFS_ACCESS_D0_ONLY},
- {"dram", BYT_DSP_BAR, DRAM_OFFSET, DRAM_SIZE,
- SOF_DEBUGFS_ACCESS_D0_ONLY},
- {"shim", BYT_DSP_BAR, SHIM_OFFSET, SHIM_SIZE_CHT,
- SOF_DEBUGFS_ACCESS_ALWAYS},
-};
-
static int byt_acpi_probe(struct snd_sof_dev *sdev)
{
struct snd_sof_pdata *pdata = sdev->pdata;
const struct sof_dev_desc *desc = pdata->desc;
- struct platform_device *pdev =
- container_of(sdev->dev, struct platform_device, dev);
+ struct platform_device *pdev = to_platform_device(sdev->dev);
+ const struct sof_intel_dsp_desc *chip;
struct resource *mmio;
u32 base, size;
int ret;
+ chip = get_chip_info(sdev->pdata);
+ if (!chip) {
+ dev_err(sdev->dev, "error: no such device supported\n");
+ return -EIO;
+ }
+
+ sdev->num_cores = chip->cores_num;
+
/* DSP DMA can only access low 31 bits of host memory */
ret = dma_coerce_mask_and_coherent(sdev->dev, DMA_BIT_MASK(31));
if (ret < 0) {
@@ -752,17 +143,17 @@ static int byt_acpi_probe(struct snd_sof_dev *sdev)
}
dev_dbg(sdev->dev, "LPE PHY base at 0x%x size 0x%x", base, size);
- sdev->bar[BYT_DSP_BAR] = devm_ioremap(sdev->dev, base, size);
- if (!sdev->bar[BYT_DSP_BAR]) {
+ sdev->bar[DSP_BAR] = devm_ioremap(sdev->dev, base, size);
+ if (!sdev->bar[DSP_BAR]) {
dev_err(sdev->dev, "error: failed to ioremap LPE base 0x%x size 0x%x\n",
base, size);
return -ENODEV;
}
- dev_dbg(sdev->dev, "LPE VADDR %p\n", sdev->bar[BYT_DSP_BAR]);
+ dev_dbg(sdev->dev, "LPE VADDR %p\n", sdev->bar[DSP_BAR]);
/* TODO: add offsets */
- sdev->mmio_bar = BYT_DSP_BAR;
- sdev->mailbox_bar = BYT_DSP_BAR;
+ sdev->mmio_bar = DSP_BAR;
+ sdev->mailbox_bar = DSP_BAR;
/* IMR base - optional */
if (desc->resindex_imr_base == -1)
@@ -786,13 +177,13 @@ static int byt_acpi_probe(struct snd_sof_dev *sdev)
}
dev_dbg(sdev->dev, "IMR base at 0x%x size 0x%x", base, size);
- sdev->bar[BYT_IMR_BAR] = devm_ioremap(sdev->dev, base, size);
- if (!sdev->bar[BYT_IMR_BAR]) {
+ sdev->bar[IMR_BAR] = devm_ioremap(sdev->dev, base, size);
+ if (!sdev->bar[IMR_BAR]) {
dev_err(sdev->dev, "error: failed to ioremap IMR base 0x%x size 0x%x\n",
base, size);
return -ENODEV;
}
- dev_dbg(sdev->dev, "IMR VADDR %p\n", sdev->bar[BYT_IMR_BAR]);
+ dev_dbg(sdev->dev, "IMR VADDR %p\n", sdev->bar[IMR_BAR]);
irq:
/* register our IRQ */
@@ -802,7 +193,7 @@ irq:
dev_dbg(sdev->dev, "using IRQ %d\n", sdev->ipc_irq);
ret = devm_request_threaded_irq(sdev->dev, sdev->ipc_irq,
- byt_irq_handler, byt_irq_thread,
+ atom_irq_handler, atom_irq_thread,
IRQF_SHARED, "AudioDSP", sdev);
if (ret < 0) {
dev_err(sdev->dev, "error: failed to register IRQ %d\n",
@@ -811,7 +202,7 @@ irq:
}
/* enable BUSY and disable DONE Interrupt by default */
- snd_sof_dsp_update_bits64(sdev, BYT_DSP_BAR, SHIM_IMRX,
+ snd_sof_dsp_update_bits64(sdev, DSP_BAR, SHIM_IMRX,
SHIM_IMRX_BUSY | SHIM_IMRX_DONE,
SHIM_IMRX_DONE);
@@ -822,55 +213,52 @@ irq:
}
/* baytrail ops */
-const struct snd_sof_dsp_ops sof_byt_ops = {
+static const struct snd_sof_dsp_ops sof_byt_ops = {
/* device init */
.probe = byt_acpi_probe,
.remove = byt_remove,
/* DSP core boot / reset */
- .run = byt_run,
- .reset = byt_reset,
+ .run = atom_run,
+ .reset = atom_reset,
- /* Register IO */
- .write = sof_io_write,
- .read = sof_io_read,
- .write64 = sof_io_write64,
- .read64 = sof_io_read64,
+ /* Register IO uses direct mmio */
/* Block IO */
.block_read = sof_block_read,
.block_write = sof_block_write,
+ /* Mailbox IO */
+ .mailbox_read = sof_mailbox_read,
+ .mailbox_write = sof_mailbox_write,
+
/* doorbell */
- .irq_handler = byt_irq_handler,
- .irq_thread = byt_irq_thread,
+ .irq_handler = atom_irq_handler,
+ .irq_thread = atom_irq_thread,
/* ipc */
- .send_msg = byt_send_msg,
- .fw_ready = sof_fw_ready,
- .get_mailbox_offset = byt_get_mailbox_offset,
- .get_window_offset = byt_get_window_offset,
+ .send_msg = atom_send_msg,
+ .get_mailbox_offset = atom_get_mailbox_offset,
+ .get_window_offset = atom_get_window_offset,
- .ipc_msg_data = intel_ipc_msg_data,
- .ipc_pcm_params = intel_ipc_pcm_params,
+ .ipc_msg_data = sof_ipc_msg_data,
+ .set_stream_data_offset = sof_set_stream_data_offset,
/* machine driver */
- .machine_select = byt_machine_select,
+ .machine_select = atom_machine_select,
.machine_register = sof_machine_register,
.machine_unregister = sof_machine_unregister,
- .set_mach_params = byt_set_mach_params,
+ .set_mach_params = atom_set_mach_params,
/* debug */
.debug_map = byt_debugfs,
.debug_map_count = ARRAY_SIZE(byt_debugfs),
- .dbg_dump = byt_dump,
+ .dbg_dump = atom_dump,
+ .debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem,
/* stream callbacks */
- .pcm_open = intel_pcm_open,
- .pcm_close = intel_pcm_close,
-
- /* module loading */
- .load_module = snd_sof_parse_module_memcpy,
+ .pcm_open = sof_stream_pcm_open,
+ .pcm_close = sof_stream_pcm_close,
/*Firmware loading */
.load_firmware = snd_sof_load_firmware_memcpy,
@@ -880,7 +268,7 @@ const struct snd_sof_dsp_ops sof_byt_ops = {
.resume = byt_resume,
/* DAI drivers */
- .drv = byt_dai,
+ .drv = atom_dai,
.num_drv = 3, /* we have only 3 SSPs on byt*/
/* ALSA HW info flags */
@@ -890,66 +278,62 @@ const struct snd_sof_dsp_ops sof_byt_ops = {
SNDRV_PCM_INFO_PAUSE |
SNDRV_PCM_INFO_BATCH,
- .arch_ops = &sof_xtensa_arch_ops,
+ .dsp_arch_ops = &sof_xtensa_arch_ops,
};
-EXPORT_SYMBOL_NS(sof_byt_ops, SND_SOC_SOF_BAYTRAIL);
-const struct sof_intel_dsp_desc byt_chip_info = {
+static const struct sof_intel_dsp_desc byt_chip_info = {
.cores_num = 1,
- .cores_mask = 1,
+ .host_managed_cores_mask = 1,
+ .hw_ip_version = SOF_INTEL_BAYTRAIL,
};
-EXPORT_SYMBOL_NS(byt_chip_info, SND_SOC_SOF_BAYTRAIL);
/* cherrytrail and braswell ops */
-const struct snd_sof_dsp_ops sof_cht_ops = {
+static const struct snd_sof_dsp_ops sof_cht_ops = {
/* device init */
.probe = byt_acpi_probe,
.remove = byt_remove,
/* DSP core boot / reset */
- .run = byt_run,
- .reset = byt_reset,
+ .run = atom_run,
+ .reset = atom_reset,
- /* Register IO */
- .write = sof_io_write,
- .read = sof_io_read,
- .write64 = sof_io_write64,
- .read64 = sof_io_read64,
+ /* Register IO uses direct mmio */
/* Block IO */
.block_read = sof_block_read,
.block_write = sof_block_write,
+ /* Mailbox IO */
+ .mailbox_read = sof_mailbox_read,
+ .mailbox_write = sof_mailbox_write,
+
/* doorbell */
- .irq_handler = byt_irq_handler,
- .irq_thread = byt_irq_thread,
+ .irq_handler = atom_irq_handler,
+ .irq_thread = atom_irq_thread,
/* ipc */
- .send_msg = byt_send_msg,
- .fw_ready = sof_fw_ready,
- .get_mailbox_offset = byt_get_mailbox_offset,
- .get_window_offset = byt_get_window_offset,
+ .send_msg = atom_send_msg,
+ .get_mailbox_offset = atom_get_mailbox_offset,
+ .get_window_offset = atom_get_window_offset,
- .ipc_msg_data = intel_ipc_msg_data,
- .ipc_pcm_params = intel_ipc_pcm_params,
+ .ipc_msg_data = sof_ipc_msg_data,
+ .set_stream_data_offset = sof_set_stream_data_offset,
/* machine driver */
- .machine_select = byt_machine_select,
+ .machine_select = atom_machine_select,
.machine_register = sof_machine_register,
.machine_unregister = sof_machine_unregister,
- .set_mach_params = byt_set_mach_params,
+ .set_mach_params = atom_set_mach_params,
/* debug */
.debug_map = cht_debugfs,
.debug_map_count = ARRAY_SIZE(cht_debugfs),
- .dbg_dump = byt_dump,
+ .dbg_dump = atom_dump,
+ .debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem,
/* stream callbacks */
- .pcm_open = intel_pcm_open,
- .pcm_close = intel_pcm_close,
-
- /* module loading */
- .load_module = snd_sof_parse_module_memcpy,
+ .pcm_open = sof_stream_pcm_open,
+ .pcm_close = sof_stream_pcm_close,
/*Firmware loading */
.load_firmware = snd_sof_load_firmware_memcpy,
@@ -959,9 +343,9 @@ const struct snd_sof_dsp_ops sof_cht_ops = {
.resume = byt_resume,
/* DAI drivers */
- .drv = byt_dai,
+ .drv = atom_dai,
/* all 6 SSPs may be available for cherrytrail */
- .num_drv = ARRAY_SIZE(byt_dai),
+ .num_drv = 6,
/* ALSA HW info flags */
.hw_info = SNDRV_PCM_INFO_MMAP |
@@ -970,18 +354,127 @@ const struct snd_sof_dsp_ops sof_cht_ops = {
SNDRV_PCM_INFO_PAUSE |
SNDRV_PCM_INFO_BATCH,
- .arch_ops = &sof_xtensa_arch_ops,
+ .dsp_arch_ops = &sof_xtensa_arch_ops,
};
-EXPORT_SYMBOL_NS(sof_cht_ops, SND_SOC_SOF_BAYTRAIL);
-const struct sof_intel_dsp_desc cht_chip_info = {
+static const struct sof_intel_dsp_desc cht_chip_info = {
.cores_num = 1,
- .cores_mask = 1,
+ .host_managed_cores_mask = 1,
+ .hw_ip_version = SOF_INTEL_BAYTRAIL,
+};
+
+/* BYTCR uses different IRQ index */
+static const struct sof_dev_desc sof_acpi_baytrailcr_desc = {
+ .machines = snd_soc_acpi_intel_baytrail_machines,
+ .resindex_lpe_base = 0,
+ .resindex_pcicfg_base = 1,
+ .resindex_imr_base = 2,
+ .irqindex_host_ipc = 0,
+ .chip_info = &byt_chip_info,
+ .ipc_supported_mask = BIT(SOF_IPC_TYPE_3),
+ .ipc_default = SOF_IPC_TYPE_3,
+ .default_fw_path = {
+ [SOF_IPC_TYPE_3] = "intel/sof",
+ },
+ .default_tplg_path = {
+ [SOF_IPC_TYPE_3] = "intel/sof-tplg",
+ },
+ .default_fw_filename = {
+ [SOF_IPC_TYPE_3] = "sof-byt.ri",
+ },
+ .nocodec_tplg_filename = "sof-byt-nocodec.tplg",
+ .ops = &sof_byt_ops,
+};
+
+static const struct sof_dev_desc sof_acpi_baytrail_desc = {
+ .machines = snd_soc_acpi_intel_baytrail_machines,
+ .resindex_lpe_base = 0,
+ .resindex_pcicfg_base = 1,
+ .resindex_imr_base = 2,
+ .irqindex_host_ipc = 5,
+ .chip_info = &byt_chip_info,
+ .ipc_supported_mask = BIT(SOF_IPC_TYPE_3),
+ .ipc_default = SOF_IPC_TYPE_3,
+ .default_fw_path = {
+ [SOF_IPC_TYPE_3] = "intel/sof",
+ },
+ .default_tplg_path = {
+ [SOF_IPC_TYPE_3] = "intel/sof-tplg",
+ },
+ .default_fw_filename = {
+ [SOF_IPC_TYPE_3] = "sof-byt.ri",
+ },
+ .nocodec_tplg_filename = "sof-byt-nocodec.tplg",
+ .ops = &sof_byt_ops,
+};
+
+static const struct sof_dev_desc sof_acpi_cherrytrail_desc = {
+ .machines = snd_soc_acpi_intel_cherrytrail_machines,
+ .resindex_lpe_base = 0,
+ .resindex_pcicfg_base = 1,
+ .resindex_imr_base = 2,
+ .irqindex_host_ipc = 5,
+ .chip_info = &cht_chip_info,
+ .ipc_supported_mask = BIT(SOF_IPC_TYPE_3),
+ .ipc_default = SOF_IPC_TYPE_3,
+ .default_fw_path = {
+ [SOF_IPC_TYPE_3] = "intel/sof",
+ },
+ .default_tplg_path = {
+ [SOF_IPC_TYPE_3] = "intel/sof-tplg",
+ },
+ .default_fw_filename = {
+ [SOF_IPC_TYPE_3] = "sof-cht.ri",
+ },
+ .nocodec_tplg_filename = "sof-cht-nocodec.tplg",
+ .ops = &sof_cht_ops,
+};
+
+static const struct acpi_device_id sof_baytrail_match[] = {
+ { "80860F28", (unsigned long)&sof_acpi_baytrail_desc },
+ { "808622A8", (unsigned long)&sof_acpi_cherrytrail_desc },
+ { }
};
-EXPORT_SYMBOL_NS(cht_chip_info, SND_SOC_SOF_BAYTRAIL);
+MODULE_DEVICE_TABLE(acpi, sof_baytrail_match);
-#endif /* CONFIG_SND_SOC_SOF_BAYTRAIL */
+static int sof_baytrail_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ const struct sof_dev_desc *desc;
+ const struct acpi_device_id *id;
+ int ret;
+
+ id = acpi_match_device(dev->driver->acpi_match_table, dev);
+ if (!id)
+ return -ENODEV;
+
+ ret = snd_intel_acpi_dsp_driver_probe(dev, id->id);
+ if (ret != SND_INTEL_DSP_DRIVER_ANY && ret != SND_INTEL_DSP_DRIVER_SOF) {
+ dev_dbg(dev, "SOF ACPI driver not selected, aborting probe\n");
+ return -ENODEV;
+ }
+
+ desc = (const struct sof_dev_desc *)id->driver_data;
+ if (desc == &sof_acpi_baytrail_desc && soc_intel_is_byt_cr(pdev))
+ desc = &sof_acpi_baytrailcr_desc;
+
+ return sof_acpi_probe(pdev, desc);
+}
+
+/* acpi_driver definition */
+static struct platform_driver snd_sof_acpi_intel_byt_driver = {
+ .probe = sof_baytrail_probe,
+ .remove = sof_acpi_remove,
+ .driver = {
+ .name = "sof-audio-acpi-intel-byt",
+ .pm = pm_ptr(&sof_acpi_pm),
+ .acpi_match_table = sof_baytrail_match,
+ },
+};
+module_platform_driver(snd_sof_acpi_intel_byt_driver);
MODULE_LICENSE("Dual BSD/GPL");
-MODULE_IMPORT_NS(SND_SOC_SOF_INTEL_HIFI_EP_IPC);
-MODULE_IMPORT_NS(SND_SOC_SOF_XTENSA);
+MODULE_DESCRIPTION("SOF support for Baytrail/Cherrytrail");
+MODULE_IMPORT_NS("SND_SOC_SOF_XTENSA");
+MODULE_IMPORT_NS("SND_SOC_SOF_ACPI_DEV");
+MODULE_IMPORT_NS("SND_SOC_SOF_INTEL_ATOM_HIFI_EP");
diff --git a/sound/soc/sof/intel/cnl.c b/sound/soc/sof/intel/cnl.c
index 16db0f50d139..385e5339f0a4 100644
--- a/sound/soc/sof/intel/cnl.c
+++ b/sound/soc/sof/intel/cnl.c
@@ -3,7 +3,7 @@
// 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) 2018 Intel Corporation. All rights reserved.
+// Copyright(c) 2018 Intel Corporation
//
// Authors: Liam Girdwood <liam.r.girdwood@linux.intel.com>
// Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
@@ -15,6 +15,10 @@
* Hardware interface for audio DSP on Cannonlake.
*/
+#include <sound/sof/ext_manifest4.h>
+#include <sound/sof/ipc4/header.h>
+#include <trace/events/sof_intel.h>
+#include "../ipc4-priv.h"
#include "../ops.h"
#include "hda.h"
#include "hda-ipc.h"
@@ -29,7 +33,86 @@ static const struct snd_sof_debugfs_map cnl_dsp_debugfs[] = {
static void cnl_ipc_host_done(struct snd_sof_dev *sdev);
static void cnl_ipc_dsp_done(struct snd_sof_dev *sdev);
-static irqreturn_t cnl_ipc_irq_thread(int irq, void *context)
+irqreturn_t cnl_ipc4_irq_thread(int irq, void *context)
+{
+ struct sof_ipc4_msg notification_data = {{ 0 }};
+ struct snd_sof_dev *sdev = context;
+ bool ack_received = false;
+ bool ipc_irq = false;
+ u32 hipcida, hipctdr;
+
+ hipcida = snd_sof_dsp_read(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCIDA);
+ hipctdr = snd_sof_dsp_read(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCTDR);
+ if (hipcida & CNL_DSP_REG_HIPCIDA_DONE) {
+ /* DSP received the message */
+ snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR,
+ CNL_DSP_REG_HIPCCTL,
+ CNL_DSP_REG_HIPCCTL_DONE, 0);
+ cnl_ipc_dsp_done(sdev);
+
+ ipc_irq = true;
+ ack_received = true;
+ }
+
+ if (hipctdr & CNL_DSP_REG_HIPCTDR_BUSY) {
+ /* Message from DSP (reply or notification) */
+ u32 hipctdd = snd_sof_dsp_read(sdev, HDA_DSP_BAR,
+ CNL_DSP_REG_HIPCTDD);
+ u32 primary = hipctdr & CNL_DSP_REG_HIPCTDR_MSG_MASK;
+ u32 extension = hipctdd & CNL_DSP_REG_HIPCTDD_MSG_MASK;
+
+ if (primary & SOF_IPC4_MSG_DIR_MASK) {
+ /* Reply received */
+ if (likely(sdev->fw_state == SOF_FW_BOOT_COMPLETE)) {
+ struct sof_ipc4_msg *data = sdev->ipc->msg.reply_data;
+
+ data->primary = primary;
+ data->extension = extension;
+
+ spin_lock_irq(&sdev->ipc_lock);
+
+ snd_sof_ipc_get_reply(sdev);
+ cnl_ipc_host_done(sdev);
+ snd_sof_ipc_reply(sdev, data->primary);
+
+ spin_unlock_irq(&sdev->ipc_lock);
+ } else {
+ dev_dbg_ratelimited(sdev->dev,
+ "IPC reply before FW_READY: %#x|%#x\n",
+ primary, extension);
+ }
+ } else {
+ /* Notification received */
+ notification_data.primary = primary;
+ notification_data.extension = extension;
+
+ sdev->ipc->msg.rx_data = &notification_data;
+ snd_sof_ipc_msgs_rx(sdev);
+ sdev->ipc->msg.rx_data = NULL;
+
+ /* Let DSP know that we have finished processing the message */
+ cnl_ipc_host_done(sdev);
+ }
+
+ ipc_irq = true;
+ }
+
+ if (!ipc_irq)
+ /* This interrupt is not shared so no need to return IRQ_NONE. */
+ dev_dbg_ratelimited(sdev->dev, "nothing to do in IPC IRQ thread\n");
+
+ if (ack_received) {
+ struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata;
+
+ if (hdev->delayed_ipc_tx_msg)
+ cnl_ipc4_send_msg(sdev, hdev->delayed_ipc_tx_msg);
+ }
+
+ return IRQ_HANDLED;
+}
+EXPORT_SYMBOL_NS(cnl_ipc4_irq_thread, "SND_SOC_SOF_INTEL_CNL");
+
+irqreturn_t cnl_ipc_irq_thread(int irq, void *context)
{
struct snd_sof_dev *sdev = context;
u32 hipci;
@@ -50,24 +133,27 @@ static irqreturn_t cnl_ipc_irq_thread(int irq, void *context)
msg_ext = hipci & CNL_DSP_REG_HIPCIDR_MSG_MASK;
msg = hipcida & CNL_DSP_REG_HIPCIDA_MSG_MASK;
- dev_vdbg(sdev->dev,
- "ipc: firmware response, msg:0x%x, msg_ext:0x%x\n",
- msg, msg_ext);
+ trace_sof_intel_ipc_firmware_response(sdev, msg, msg_ext);
/* mask Done interrupt */
snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR,
CNL_DSP_REG_HIPCCTL,
CNL_DSP_REG_HIPCCTL_DONE, 0);
- spin_lock_irq(&sdev->ipc_lock);
+ if (likely(sdev->fw_state == SOF_FW_BOOT_COMPLETE)) {
+ spin_lock_irq(&sdev->ipc_lock);
- /* handle immediate reply from DSP core */
- hda_dsp_ipc_get_reply(sdev);
- snd_sof_ipc_reply(sdev, msg);
+ /* handle immediate reply from DSP core */
+ hda_dsp_ipc_get_reply(sdev);
+ snd_sof_ipc_reply(sdev, msg);
- cnl_ipc_dsp_done(sdev);
+ cnl_ipc_dsp_done(sdev);
- spin_unlock_irq(&sdev->ipc_lock);
+ spin_unlock_irq(&sdev->ipc_lock);
+ } else {
+ dev_dbg_ratelimited(sdev->dev, "IPC reply before FW_READY: %#x\n",
+ msg);
+ }
ipc_irq = true;
}
@@ -77,14 +163,27 @@ static irqreturn_t cnl_ipc_irq_thread(int irq, void *context)
msg = hipctdr & CNL_DSP_REG_HIPCTDR_MSG_MASK;
msg_ext = hipctdd & CNL_DSP_REG_HIPCTDD_MSG_MASK;
- dev_vdbg(sdev->dev,
- "ipc: firmware initiated, msg:0x%x, msg_ext:0x%x\n",
- msg, msg_ext);
+ trace_sof_intel_ipc_firmware_initiated(sdev, msg, msg_ext);
/* handle messages from DSP */
- if ((hipctdr & SOF_IPC_PANIC_MAGIC_MASK) ==
- SOF_IPC_PANIC_MAGIC) {
- snd_sof_dsp_panic(sdev, HDA_DSP_PANIC_OFFSET(msg_ext));
+ if ((hipctdr & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC) {
+ struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
+ bool non_recoverable = true;
+
+ /*
+ * This is a PANIC message!
+ *
+ * If it is arriving during firmware boot and it is not
+ * the last boot attempt then change the non_recoverable
+ * to false as the DSP might be able to boot in the next
+ * iteration(s)
+ */
+ if (sdev->fw_state == SOF_FW_BOOT_IN_PROGRESS &&
+ hda->boot_iteration < HDA_FW_BOOT_ATTEMPTS)
+ non_recoverable = false;
+
+ snd_sof_dsp_panic(sdev, HDA_DSP_PANIC_OFFSET(msg_ext),
+ non_recoverable);
} else {
snd_sof_ipc_msgs_rx(sdev);
}
@@ -104,6 +203,7 @@ static irqreturn_t cnl_ipc_irq_thread(int irq, void *context)
return IRQ_HANDLED;
}
+EXPORT_SYMBOL_NS(cnl_ipc_irq_thread, "SND_SOC_SOF_INTEL_CNL");
static void cnl_ipc_host_done(struct snd_sof_dev *sdev)
{
@@ -146,11 +246,9 @@ static void cnl_ipc_dsp_done(struct snd_sof_dev *sdev)
static bool cnl_compact_ipc_compress(struct snd_sof_ipc_msg *msg,
u32 *dr, u32 *dd)
{
- struct sof_ipc_pm_gate *pm_gate;
-
- if (msg->header == (SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_GATE)) {
- pm_gate = msg->msg_data;
+ struct sof_ipc_pm_gate *pm_gate = msg->msg_data;
+ if (pm_gate->hdr.cmd == (SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_GATE)) {
/* send the compact message via the primary register */
*dr = HDA_IPC_MSG_COMPACT | HDA_IPC_PM_GATE;
@@ -163,8 +261,34 @@ static bool cnl_compact_ipc_compress(struct snd_sof_ipc_msg *msg,
return false;
}
-static int cnl_ipc_send_msg(struct snd_sof_dev *sdev,
- struct snd_sof_ipc_msg *msg)
+int cnl_ipc4_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg)
+{
+ struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata;
+ struct sof_ipc4_msg *msg_data = msg->msg_data;
+
+ if (hda_ipc4_tx_is_busy(sdev)) {
+ hdev->delayed_ipc_tx_msg = msg;
+ return 0;
+ }
+
+ hdev->delayed_ipc_tx_msg = NULL;
+
+ /* send the message via mailbox */
+ if (msg_data->data_size)
+ sof_mailbox_write(sdev, sdev->host_box.offset, msg_data->data_ptr,
+ msg_data->data_size);
+
+ snd_sof_dsp_write(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCIDD, msg_data->extension);
+ snd_sof_dsp_write(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCIDR,
+ msg_data->primary | CNL_DSP_REG_HIPCIDR_BUSY);
+
+ hda_dsp_ipc4_schedule_d0i3_work(hdev, msg);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS(cnl_ipc4_send_msg, "SND_SOC_SOF_INTEL_CNL");
+
+int cnl_ipc_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg)
{
struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata;
struct sof_ipc_cmd_hdr *hdr;
@@ -202,7 +326,7 @@ static int cnl_ipc_send_msg(struct snd_sof_dev *sdev,
* IPCs are sent at a high-rate. mod_delayed_work()
* modifies the timer if the work is pending.
* Also, a new delayed work should not be queued after the
- * the CTX_SAVE IPC, which is sent before the DSP enters D3.
+ * CTX_SAVE IPC, which is sent before the DSP enters D3.
*/
if (hdr->cmd != (SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_CTX_SAVE))
mod_delayed_work(system_wq, &hdev->d0i3_work,
@@ -210,8 +334,9 @@ static int cnl_ipc_send_msg(struct snd_sof_dev *sdev,
return 0;
}
+EXPORT_SYMBOL_NS(cnl_ipc_send_msg, "SND_SOC_SOF_INTEL_CNL");
-static void cnl_ipc_dump(struct snd_sof_dev *sdev)
+void cnl_ipc_dump(struct snd_sof_dev *sdev)
{
u32 hipcctl;
u32 hipcida;
@@ -230,190 +355,165 @@ static void cnl_ipc_dump(struct snd_sof_dev *sdev)
"error: host status 0x%8.8x dsp status 0x%8.8x mask 0x%8.8x\n",
hipcida, hipctdr, hipcctl);
}
+EXPORT_SYMBOL_NS(cnl_ipc_dump, "SND_SOC_SOF_INTEL_CNL");
-/* cannonlake ops */
-const struct snd_sof_dsp_ops sof_cnl_ops = {
- /* probe and remove */
- .probe = hda_dsp_probe,
- .remove = hda_dsp_remove,
+void cnl_ipc4_dump(struct snd_sof_dev *sdev)
+{
+ u32 hipcidr, hipcidd, hipcida, hipctdr, hipctdd, hipctda, hipcctl;
+
+ hda_ipc_irq_dump(sdev);
+
+ hipcidr = snd_sof_dsp_read(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCIDR);
+ hipcidd = snd_sof_dsp_read(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCIDD);
+ hipcida = snd_sof_dsp_read(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCIDA);
+ hipctdr = snd_sof_dsp_read(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCTDR);
+ hipctdd = snd_sof_dsp_read(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCTDD);
+ hipctda = snd_sof_dsp_read(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCTDA);
+ hipcctl = snd_sof_dsp_read(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCCTL);
+
+ /* dump the IPC regs */
+ /* TODO: parse the raw msg */
+ dev_err(sdev->dev,
+ "Host IPC initiator: %#x|%#x|%#x, target: %#x|%#x|%#x, ctl: %#x\n",
+ hipcidr, hipcidd, hipcida, hipctdr, hipctdd, hipctda, hipcctl);
+}
+EXPORT_SYMBOL_NS(cnl_ipc4_dump, "SND_SOC_SOF_INTEL_CNL");
- /* Register IO */
- .write = sof_io_write,
- .read = sof_io_read,
- .write64 = sof_io_write64,
- .read64 = sof_io_read64,
+/* cannonlake ops */
+struct snd_sof_dsp_ops sof_cnl_ops;
+EXPORT_SYMBOL_NS(sof_cnl_ops, "SND_SOC_SOF_INTEL_CNL");
- /* Block IO */
- .block_read = sof_block_read,
- .block_write = sof_block_write,
+int sof_cnl_ops_init(struct snd_sof_dev *sdev)
+{
+ /* common defaults */
+ memcpy(&sof_cnl_ops, &sof_hda_common_ops, sizeof(struct snd_sof_dsp_ops));
- /* doorbell */
- .irq_thread = cnl_ipc_irq_thread,
+ /* probe/remove/shutdown */
+ sof_cnl_ops.shutdown = hda_dsp_shutdown;
/* ipc */
- .send_msg = cnl_ipc_send_msg,
- .fw_ready = sof_fw_ready,
- .get_mailbox_offset = hda_dsp_ipc_get_mailbox_offset,
- .get_window_offset = hda_dsp_ipc_get_window_offset,
+ if (sdev->pdata->ipc_type == SOF_IPC_TYPE_3) {
+ /* doorbell */
+ sof_cnl_ops.irq_thread = cnl_ipc_irq_thread;
+
+ /* ipc */
+ sof_cnl_ops.send_msg = cnl_ipc_send_msg;
+
+ /* debug */
+ sof_cnl_ops.ipc_dump = cnl_ipc_dump;
+
+ sof_cnl_ops.set_power_state = hda_dsp_set_power_state_ipc3;
+ }
+
+ if (sdev->pdata->ipc_type == SOF_IPC_TYPE_4) {
+ struct sof_ipc4_fw_data *ipc4_data;
+
+ sdev->private = kzalloc(sizeof(*ipc4_data), GFP_KERNEL);
+ if (!sdev->private)
+ return -ENOMEM;
+
+ ipc4_data = sdev->private;
+ ipc4_data->manifest_fw_hdr_offset = SOF_MAN4_FW_HDR_OFFSET;
+
+ ipc4_data->mtrace_type = SOF_IPC4_MTRACE_INTEL_CAVS_1_8;
+
+ /* External library loading support */
+ ipc4_data->load_library = hda_dsp_ipc4_load_library;
+
+ /* doorbell */
+ sof_cnl_ops.irq_thread = cnl_ipc4_irq_thread;
- .ipc_msg_data = hda_ipc_msg_data,
- .ipc_pcm_params = hda_ipc_pcm_params,
+ /* ipc */
+ sof_cnl_ops.send_msg = cnl_ipc4_send_msg;
- /* machine driver */
- .machine_select = hda_machine_select,
- .machine_register = sof_machine_register,
- .machine_unregister = sof_machine_unregister,
- .set_mach_params = hda_set_mach_params,
+ /* debug */
+ sof_cnl_ops.ipc_dump = cnl_ipc4_dump;
+
+ sof_cnl_ops.set_power_state = hda_dsp_set_power_state_ipc4;
+ }
+
+ /* set DAI driver ops */
+ hda_set_dai_drv_ops(sdev, &sof_cnl_ops);
/* debug */
- .debug_map = cnl_dsp_debugfs,
- .debug_map_count = ARRAY_SIZE(cnl_dsp_debugfs),
- .dbg_dump = hda_dsp_dump,
- .ipc_dump = cnl_ipc_dump,
-
- /* stream callbacks */
- .pcm_open = hda_dsp_pcm_open,
- .pcm_close = hda_dsp_pcm_close,
- .pcm_hw_params = hda_dsp_pcm_hw_params,
- .pcm_hw_free = hda_dsp_stream_hw_free,
- .pcm_trigger = hda_dsp_pcm_trigger,
- .pcm_pointer = hda_dsp_pcm_pointer,
-
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_PROBES)
- /* probe callbacks */
- .probe_assign = hda_probe_compr_assign,
- .probe_free = hda_probe_compr_free,
- .probe_set_params = hda_probe_compr_set_params,
- .probe_trigger = hda_probe_compr_trigger,
- .probe_pointer = hda_probe_compr_pointer,
-#endif
-
- /* firmware loading */
- .load_firmware = snd_sof_load_firmware_raw,
+ sof_cnl_ops.debug_map = cnl_dsp_debugfs;
+ sof_cnl_ops.debug_map_count = ARRAY_SIZE(cnl_dsp_debugfs);
/* pre/post fw run */
- .pre_fw_run = hda_dsp_pre_fw_run,
- .post_fw_run = hda_dsp_post_fw_run,
-
- /* dsp core power up/down */
- .core_power_up = hda_dsp_enable_core,
- .core_power_down = hda_dsp_core_reset_power_down,
+ sof_cnl_ops.post_fw_run = hda_dsp_post_fw_run;
/* firmware run */
- .run = hda_dsp_cl_boot_firmware,
-
- /* trace callback */
- .trace_init = hda_dsp_trace_init,
- .trace_release = hda_dsp_trace_release,
- .trace_trigger = hda_dsp_trace_trigger,
-
- /* DAI drivers */
- .drv = skl_dai,
- .num_drv = SOF_SKL_NUM_DAIS,
-
- /* PM */
- .suspend = hda_dsp_suspend,
- .resume = hda_dsp_resume,
- .runtime_suspend = hda_dsp_runtime_suspend,
- .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_power_state = hda_dsp_set_power_state,
-
- /* ALSA HW info flags */
- .hw_info = SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_MMAP_VALID |
- SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_PAUSE |
- SNDRV_PCM_INFO_NO_PERIOD_WAKEUP,
-
- .arch_ops = &sof_xtensa_arch_ops,
+ sof_cnl_ops.run = hda_dsp_cl_boot_firmware;
+
+ /* dsp core get/put */
+ sof_cnl_ops.core_get = hda_dsp_core_get;
+
+ return 0;
};
-EXPORT_SYMBOL_NS(sof_cnl_ops, SND_SOC_SOF_INTEL_HDA_COMMON);
+EXPORT_SYMBOL_NS(sof_cnl_ops_init, "SND_SOC_SOF_INTEL_CNL");
const struct sof_intel_dsp_desc cnl_chip_info = {
/* Cannonlake */
.cores_num = 4,
.init_core_mask = 1,
- .cores_mask = HDA_DSP_CORE_MASK(0) |
- HDA_DSP_CORE_MASK(1) |
- HDA_DSP_CORE_MASK(2) |
- HDA_DSP_CORE_MASK(3),
+ .host_managed_cores_mask = GENMASK(3, 0),
.ipc_req = CNL_DSP_REG_HIPCIDR,
.ipc_req_mask = CNL_DSP_REG_HIPCIDR_BUSY,
.ipc_ack = CNL_DSP_REG_HIPCIDA,
.ipc_ack_mask = CNL_DSP_REG_HIPCIDA_DONE,
.ipc_ctl = CNL_DSP_REG_HIPCCTL,
+ .rom_status_reg = HDA_DSP_SRAM_REG_ROM_STATUS,
.rom_init_timeout = 300,
.ssp_count = CNL_SSP_COUNT,
.ssp_base_offset = CNL_SSP_BASE_OFFSET,
+ .sdw_shim_base = SDW_SHIM_BASE,
+ .sdw_alh_base = SDW_ALH_BASE,
+ .d0i3_offset = SOF_HDA_VS_D0I3C,
+ .read_sdw_lcount = hda_sdw_check_lcount_common,
+ .enable_sdw_irq = hda_common_enable_sdw_irq,
+ .check_sdw_irq = hda_common_check_sdw_irq,
+ .check_sdw_wakeen_irq = hda_sdw_check_wakeen_irq_common,
+ .sdw_process_wakeen = hda_sdw_process_wakeen_common,
+ .check_ipc_irq = hda_dsp_check_ipc_irq,
+ .cl_init = cl_dsp_init,
+ .power_down_dsp = hda_power_down_dsp,
+ .disable_interrupts = hda_dsp_disable_interrupts,
+ .hw_ip_version = SOF_INTEL_CAVS_1_8,
};
-EXPORT_SYMBOL_NS(cnl_chip_info, SND_SOC_SOF_INTEL_HDA_COMMON);
-
-const struct sof_intel_dsp_desc icl_chip_info = {
- /* Icelake */
- .cores_num = 4,
- .init_core_mask = 1,
- .cores_mask = HDA_DSP_CORE_MASK(0) |
- HDA_DSP_CORE_MASK(1) |
- HDA_DSP_CORE_MASK(2) |
- HDA_DSP_CORE_MASK(3),
- .ipc_req = CNL_DSP_REG_HIPCIDR,
- .ipc_req_mask = CNL_DSP_REG_HIPCIDR_BUSY,
- .ipc_ack = CNL_DSP_REG_HIPCIDA,
- .ipc_ack_mask = CNL_DSP_REG_HIPCIDA_DONE,
- .ipc_ctl = CNL_DSP_REG_HIPCCTL,
- .rom_init_timeout = 300,
- .ssp_count = ICL_SSP_COUNT,
- .ssp_base_offset = CNL_SSP_BASE_OFFSET,
-};
-EXPORT_SYMBOL_NS(icl_chip_info, SND_SOC_SOF_INTEL_HDA_COMMON);
-
-const struct sof_intel_dsp_desc tgl_chip_info = {
- /* Tigerlake */
- .cores_num = 4,
- .init_core_mask = 1,
- .cores_mask = HDA_DSP_CORE_MASK(0),
- .ipc_req = CNL_DSP_REG_HIPCIDR,
- .ipc_req_mask = CNL_DSP_REG_HIPCIDR_BUSY,
- .ipc_ack = CNL_DSP_REG_HIPCIDA,
- .ipc_ack_mask = CNL_DSP_REG_HIPCIDA_DONE,
- .ipc_ctl = CNL_DSP_REG_HIPCCTL,
- .rom_init_timeout = 300,
- .ssp_count = ICL_SSP_COUNT,
- .ssp_base_offset = CNL_SSP_BASE_OFFSET,
-};
-EXPORT_SYMBOL_NS(tgl_chip_info, SND_SOC_SOF_INTEL_HDA_COMMON);
-
-const struct sof_intel_dsp_desc ehl_chip_info = {
- /* Elkhartlake */
- .cores_num = 4,
- .init_core_mask = 1,
- .cores_mask = HDA_DSP_CORE_MASK(0),
- .ipc_req = CNL_DSP_REG_HIPCIDR,
- .ipc_req_mask = CNL_DSP_REG_HIPCIDR_BUSY,
- .ipc_ack = CNL_DSP_REG_HIPCIDA,
- .ipc_ack_mask = CNL_DSP_REG_HIPCIDA_DONE,
- .ipc_ctl = CNL_DSP_REG_HIPCCTL,
- .rom_init_timeout = 300,
- .ssp_count = ICL_SSP_COUNT,
- .ssp_base_offset = CNL_SSP_BASE_OFFSET,
-};
-EXPORT_SYMBOL_NS(ehl_chip_info, SND_SOC_SOF_INTEL_HDA_COMMON);
+/*
+ * JasperLake is technically derived from IceLake, and should be in
+ * described in icl.c. However since JasperLake was designed with
+ * two cores, it cannot support the IceLake-specific power-up sequences
+ * which rely on core3. To simplify, JasperLake uses the CannonLake ops and
+ * is described in cnl.c
+ */
const struct sof_intel_dsp_desc jsl_chip_info = {
/* Jasperlake */
.cores_num = 2,
.init_core_mask = 1,
- .cores_mask = HDA_DSP_CORE_MASK(0) |
- HDA_DSP_CORE_MASK(1),
+ .host_managed_cores_mask = GENMASK(1, 0),
.ipc_req = CNL_DSP_REG_HIPCIDR,
.ipc_req_mask = CNL_DSP_REG_HIPCIDR_BUSY,
.ipc_ack = CNL_DSP_REG_HIPCIDA,
.ipc_ack_mask = CNL_DSP_REG_HIPCIDA_DONE,
.ipc_ctl = CNL_DSP_REG_HIPCCTL,
+ .rom_status_reg = HDA_DSP_SRAM_REG_ROM_STATUS,
.rom_init_timeout = 300,
.ssp_count = ICL_SSP_COUNT,
.ssp_base_offset = CNL_SSP_BASE_OFFSET,
+ .sdw_shim_base = SDW_SHIM_BASE,
+ .sdw_alh_base = SDW_ALH_BASE,
+ .d0i3_offset = SOF_HDA_VS_D0I3C,
+ .read_sdw_lcount = hda_sdw_check_lcount_common,
+ .enable_sdw_irq = hda_common_enable_sdw_irq,
+ .check_sdw_irq = hda_common_check_sdw_irq,
+ .check_sdw_wakeen_irq = hda_sdw_check_wakeen_irq_common,
+ .sdw_process_wakeen = hda_sdw_process_wakeen_common,
+ .check_ipc_irq = hda_dsp_check_ipc_irq,
+ .cl_init = cl_dsp_init,
+ .power_down_dsp = hda_power_down_dsp,
+ .disable_interrupts = hda_dsp_disable_interrupts,
+ .hw_ip_version = SOF_INTEL_CAVS_2_0,
};
-EXPORT_SYMBOL_NS(jsl_chip_info, SND_SOC_SOF_INTEL_HDA_COMMON);
+EXPORT_SYMBOL_NS(jsl_chip_info, "SND_SOC_SOF_INTEL_CNL");
diff --git a/sound/soc/sof/intel/ext_manifest.h b/sound/soc/sof/intel/ext_manifest.h
new file mode 100644
index 000000000000..1ca19c691852
--- /dev/null
+++ b/sound/soc/sof/intel/ext_manifest.h
@@ -0,0 +1,35 @@
+/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) 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) 2020 Intel Corporation
+ */
+
+/*
+ * Intel extended manifest is a extra place to store Intel cavs specific
+ * metadata about firmware, for example LPRO/HPRO configuration is
+ * Intel cavs specific. This part of output binary is not signed.
+ */
+
+#ifndef __INTEL_CAVS_EXT_MANIFEST_H__
+#define __INTEL_CAVS_EXT_MANIFEST_H__
+
+#include <sound/sof/ext_manifest.h>
+
+/* EXT_MAN_ELEM_PLATFORM_CONFIG_DATA elements identificators */
+enum sof_cavs_config_elem_type {
+ SOF_EXT_MAN_CAVS_CONFIG_EMPTY = 0,
+ SOF_EXT_MAN_CAVS_CONFIG_CAVS_LPRO = 1,
+ SOF_EXT_MAN_CAVS_CONFIG_OUTBOX_SIZE = 2,
+ SOF_EXT_MAN_CAVS_CONFIG_INBOX_SIZE = 3,
+};
+
+/* EXT_MAN_ELEM_PLATFORM_CONFIG_DATA elements */
+struct sof_ext_man_cavs_config_data {
+ struct sof_ext_man_elem_header hdr;
+
+ struct sof_config_elem elems[];
+} __packed;
+
+#endif /* __INTEL_CAVS_EXT_MANIFEST_H__ */
diff --git a/sound/soc/sof/intel/hda-bus.c b/sound/soc/sof/intel/hda-bus.c
index 789148e5584b..6492e1cefbfb 100644
--- a/sound/soc/sof/intel/hda-bus.c
+++ b/sound/soc/sof/intel/hda-bus.c
@@ -3,30 +3,86 @@
// 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) 2018 Intel Corporation. All rights reserved.
+// Copyright(c) 2018 Intel Corporation
//
// Authors: Keyon Jie <yang.jie@linux.intel.com>
#include <linux/io.h>
#include <sound/hdaudio.h>
+#include <sound/hda_i915.h>
+#include <sound/hda_codec.h>
+#include <sound/hda_register.h>
#include "../sof-priv.h"
#include "hda.h"
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC)
#include "../../codecs/hdac_hda.h"
#define sof_hda_ext_ops snd_soc_hdac_hda_get_ops()
-#else
-#define sof_hda_ext_ops NULL
+
+static void update_codec_wake_enable(struct hdac_bus *bus, unsigned int addr, bool link_power)
+{
+ unsigned int mask = snd_hdac_chip_readw(bus, WAKEEN);
+
+ if (link_power)
+ mask &= ~BIT(addr);
+ else
+ mask |= BIT(addr);
+
+ snd_hdac_chip_updatew(bus, WAKEEN, STATESTS_INT_MASK, mask);
+}
+
+static void sof_hda_bus_link_power(struct hdac_device *codec, bool enable)
+{
+ struct hdac_bus *bus = codec->bus;
+ bool oldstate = test_bit(codec->addr, &bus->codec_powered);
+
+ snd_hdac_ext_bus_link_power(codec, enable);
+
+ if (enable == oldstate)
+ return;
+
+ /*
+ * Both codec driver and controller can hold references to
+ * display power. To avoid unnecessary power-up/down cycles,
+ * controller doesn't immediately release its reference.
+ *
+ * If the codec driver powers down the link, release
+ * the controller reference as well.
+ */
+ if (codec->addr == HDA_IDISP_ADDR && !enable)
+ snd_hdac_display_power(bus, HDA_CODEC_IDX_CONTROLLER, false);
+
+ /* WAKEEN needs to be set for disabled links */
+ update_codec_wake_enable(bus, codec->addr, enable);
+}
+
+static const struct hdac_bus_ops bus_core_ops = {
+ .command = snd_hdac_bus_send_cmd,
+ .get_response = snd_hdac_bus_get_response,
+ .link_power = sof_hda_bus_link_power,
+};
#endif
/*
* This can be used for both with/without hda link support.
*/
-void sof_hda_bus_init(struct hdac_bus *bus, struct device *dev)
+void sof_hda_bus_init(struct snd_sof_dev *sdev, struct device *dev)
{
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
- snd_hdac_ext_bus_init(bus, dev, NULL, sof_hda_ext_ops);
-#else /* CONFIG_SND_SOC_SOF_HDA */
+ struct hdac_bus *bus = sof_to_bus(sdev);
+
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_LINK)
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC)
+ const struct sof_intel_dsp_desc *chip = get_chip_info(sdev->pdata);
+
+ snd_hdac_ext_bus_init(bus, dev, &bus_core_ops, sof_hda_ext_ops);
+
+ if (chip && chip->hw_ip_version >= SOF_INTEL_ACE_2_0)
+ bus->use_pio_for_commands = true;
+#else
+ snd_hdac_ext_bus_init(bus, dev, NULL, NULL);
+#endif
+#else
+
memset(bus, 0, sizeof(*bus));
bus->dev = dev;
@@ -41,5 +97,16 @@ void sof_hda_bus_init(struct hdac_bus *bus, struct device *dev)
bus->idx = 0;
spin_lock_init(&bus->reg_lock);
-#endif /* CONFIG_SND_SOC_SOF_HDA */
+#endif /* CONFIG_SND_SOC_SOF_HDA_LINK */
+}
+EXPORT_SYMBOL_NS(sof_hda_bus_init, "SND_SOC_SOF_INTEL_HDA_COMMON");
+
+void sof_hda_bus_exit(struct snd_sof_dev *sdev)
+{
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_LINK)
+ struct hdac_bus *bus = sof_to_bus(sdev);
+
+ snd_hdac_ext_bus_exit(bus);
+#endif
}
+EXPORT_SYMBOL_NS(sof_hda_bus_exit, "SND_SOC_SOF_INTEL_HDA_COMMON");
diff --git a/sound/soc/sof/intel/hda-codec.c b/sound/soc/sof/intel/hda-codec.c
index 2c5c451fa19d..2f9925830d1d 100644
--- a/sound/soc/sof/intel/hda-codec.c
+++ b/sound/soc/sof/intel/hda-codec.c
@@ -1,9 +1,6 @@
-// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+// SPDX-License-Identifier: GPL-2.0-only
//
-// 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) 2018 Intel Corporation. All rights reserved.
+// Copyright(c) 2018 Intel Corporation
//
// Authors: Keyon Jie <yang.jie@linux.intel.com>
//
@@ -16,13 +13,18 @@
#include <sound/sof.h>
#include "../ops.h"
#include "hda.h"
+
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC)
#include "../../codecs/hdac_hda.h"
-#endif /* CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC */
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC)
+#define CODEC_PROBE_RETRIES 3
+
#define IDISP_VID_INTEL 0x80860000
+static int hda_codec_mask = -1;
+module_param_named(codec_mask, hda_codec_mask, int, 0444);
+MODULE_PARM_DESC(codec_mask, "SOF HDA codec mask for probing");
+
/* load the legacy HDA codec driver */
static int request_codec_module(struct hda_codec *codec)
{
@@ -52,8 +54,16 @@ static int request_codec_module(struct hda_codec *codec)
static int hda_codec_load_module(struct hda_codec *codec)
{
- int ret = request_codec_module(codec);
+ int ret;
+
+ ret = snd_hdac_device_register(&codec->core);
+ if (ret) {
+ dev_err(&codec->core.dev, "failed to register hdac device\n");
+ put_device(&codec->core.dev);
+ return ret;
+ }
+ ret = request_codec_module(codec);
if (ret <= 0) {
codec->probe_id = HDA_CODEC_ID_GENERIC;
ret = request_codec_module(codec);
@@ -63,29 +73,45 @@ static int hda_codec_load_module(struct hda_codec *codec)
}
/* enable controller wake up event for all codecs with jack connectors */
-void hda_codec_jack_wake_enable(struct snd_sof_dev *sdev)
+void hda_codec_jack_wake_enable(struct snd_sof_dev *sdev, bool enable)
{
struct hda_bus *hbus = sof_to_hbus(sdev);
struct hdac_bus *bus = sof_to_bus(sdev);
struct hda_codec *codec;
unsigned int mask = 0;
+ unsigned int val = 0;
- list_for_each_codec(codec, hbus)
- if (codec->jacktbl.used)
+ if (IS_ENABLED(CONFIG_SND_SOC_SOF_NOCODEC_DEBUG_SUPPORT) &&
+ sof_debug_check_flag(SOF_DBG_FORCE_NOCODEC))
+ return;
+
+ if (enable) {
+ list_for_each_codec(codec, hbus) {
+ /* only set WAKEEN when needed for HDaudio codecs */
+ mask |= BIT(codec->core.addr);
+ if (codec->jacktbl.used)
+ val |= BIT(codec->core.addr);
+ }
+ } else {
+ list_for_each_codec(codec, hbus) {
+ /* reset WAKEEN only HDaudio codecs */
mask |= BIT(codec->core.addr);
+ }
+ }
- snd_hdac_chip_updatew(bus, WAKEEN, STATESTS_INT_MASK, mask);
+ snd_hdac_chip_updatew(bus, WAKEEN, mask & STATESTS_INT_MASK, val);
}
+EXPORT_SYMBOL_NS_GPL(hda_codec_jack_wake_enable, "SND_SOC_SOF_HDA_AUDIO_CODEC");
/* check jack status after resuming from suspend mode */
void hda_codec_jack_check(struct snd_sof_dev *sdev)
{
struct hda_bus *hbus = sof_to_hbus(sdev);
- struct hdac_bus *bus = sof_to_bus(sdev);
struct hda_codec *codec;
- /* disable controller Wake Up event*/
- snd_hdac_chip_updatew(bus, WAKEEN, STATESTS_INT_MASK, 0);
+ if (IS_ENABLED(CONFIG_SND_SOC_SOF_NOCODEC_DEBUG_SUPPORT) &&
+ sof_debug_check_flag(SOF_DBG_FORCE_NOCODEC))
+ return;
list_for_each_codec(codec, hbus)
/*
@@ -93,15 +119,9 @@ void hda_codec_jack_check(struct snd_sof_dev *sdev)
* has been recorded in STATESTS
*/
if (codec->jacktbl.used)
- schedule_delayed_work(&codec->jackpoll_work,
- codec->jackpoll_interval);
+ pm_request_resume(&codec->core.dev);
}
-#else
-void hda_codec_jack_wake_enable(struct snd_sof_dev *sdev) {}
-void hda_codec_jack_check(struct snd_sof_dev *sdev) {}
-#endif /* CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC */
-EXPORT_SYMBOL_NS(hda_codec_jack_wake_enable, SND_SOC_SOF_HDA_AUDIO_CODEC);
-EXPORT_SYMBOL_NS(hda_codec_jack_check, SND_SOC_SOF_HDA_AUDIO_CODEC);
+EXPORT_SYMBOL_NS_GPL(hda_codec_jack_check, "SND_SOC_SOF_HDA_AUDIO_CODEC");
#if IS_ENABLED(CONFIG_SND_HDA_GENERIC)
#define is_generic_config(bus) \
@@ -110,48 +130,63 @@ EXPORT_SYMBOL_NS(hda_codec_jack_check, SND_SOC_SOF_HDA_AUDIO_CODEC);
#define is_generic_config(x) 0
#endif
+static struct hda_codec *hda_codec_device_init(struct hdac_bus *bus, int addr, int type)
+{
+ struct hda_codec *codec;
+
+ codec = snd_hda_codec_device_init(to_hda_bus(bus), addr, "ehdaudio%dD%d", bus->idx, addr);
+ if (IS_ERR(codec)) {
+ dev_err(bus->dev, "device init failed for hdac device\n");
+ return codec;
+ }
+
+ codec->core.type = type;
+
+ return codec;
+}
+
/* probe individual codec */
-static int hda_codec_probe(struct snd_sof_dev *sdev, int address,
- bool hda_codec_use_common_hdmi)
+static int hda_codec_probe(struct snd_sof_dev *sdev, int address)
{
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC)
struct hdac_hda_priv *hda_priv;
-#endif
struct hda_bus *hbus = sof_to_hbus(sdev);
- struct hdac_device *hdev;
struct hda_codec *codec;
u32 hda_cmd = (address << 28) | (AC_NODE_ROOT << 20) |
(AC_VERB_PARAMETERS << 8) | AC_PAR_VENDOR_ID;
u32 resp = -1;
- int ret;
+ int ret, retry = 0;
+
+ do {
+ mutex_lock(&hbus->core.cmd_mutex);
+ snd_hdac_bus_send_cmd(&hbus->core, hda_cmd);
+ snd_hdac_bus_get_response(&hbus->core, address, &resp);
+ mutex_unlock(&hbus->core.cmd_mutex);
+ } while (resp == -1 && retry++ < CODEC_PROBE_RETRIES);
- mutex_lock(&hbus->core.cmd_mutex);
- snd_hdac_bus_send_cmd(&hbus->core, hda_cmd);
- snd_hdac_bus_get_response(&hbus->core, address, &resp);
- mutex_unlock(&hbus->core.cmd_mutex);
if (resp == -1)
return -EIO;
dev_dbg(sdev->dev, "HDA codec #%d probed OK: response: %x\n",
address, resp);
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC)
hda_priv = devm_kzalloc(sdev->dev, sizeof(*hda_priv), GFP_KERNEL);
if (!hda_priv)
return -ENOMEM;
- hda_priv->codec.bus = hbus;
- hdev = &hda_priv->codec.core;
- codec = &hda_priv->codec;
-
- ret = snd_hdac_ext_bus_device_init(&hbus->core, address, hdev);
+ codec = hda_codec_device_init(&hbus->core, address, HDA_DEV_LEGACY);
+ ret = PTR_ERR_OR_ZERO(codec);
if (ret < 0)
return ret;
+ hda_priv->codec = codec;
+ hda_priv->dev_index = address;
+ dev_set_drvdata(&codec->core.dev, hda_priv);
+
if ((resp & 0xFFFF0000) == IDISP_VID_INTEL) {
- if (!hdev->bus->audio_component) {
+ if (!hbus->core.audio_component) {
dev_dbg(sdev->dev,
"iDisp hw present but no driver\n");
- return -ENOENT;
+ ret = -ENOENT;
+ goto out;
}
hda_priv->need_display_power = true;
}
@@ -161,48 +196,40 @@ static int hda_codec_probe(struct snd_sof_dev *sdev, int address,
else
codec->probe_id = 0;
+ ret = hda_codec_load_module(codec);
/*
- * if common HDMI codec driver is not used, codec load
- * is skipped here and hdac_hdmi is used instead
+ * handle ret==0 (no driver bound) as an error, but pass
+ * other return codes without modification
*/
- if (hda_codec_use_common_hdmi ||
- (resp & 0xFFFF0000) != IDISP_VID_INTEL) {
- hdev->type = HDA_DEV_LEGACY;
- ret = hda_codec_load_module(codec);
- /*
- * handle ret==0 (no driver bound) as an error, but pass
- * other return codes without modification
- */
- if (ret == 0)
- ret = -ENOENT;
- }
+ if (ret == 0)
+ ret = -ENOENT;
- return ret;
-#else
- hdev = devm_kzalloc(sdev->dev, sizeof(*hdev), GFP_KERNEL);
- if (!hdev)
- return -ENOMEM;
-
- ret = snd_hdac_ext_bus_device_init(&hbus->core, address, hdev);
+out:
+ if (ret < 0) {
+ snd_hdac_device_unregister(&codec->core);
+ put_device(&codec->core.dev);
+ }
return ret;
-#endif
}
/* Codec initialization */
-void hda_codec_probe_bus(struct snd_sof_dev *sdev,
- bool hda_codec_use_common_hdmi)
+void hda_codec_probe_bus(struct snd_sof_dev *sdev)
{
struct hdac_bus *bus = sof_to_bus(sdev);
int i, ret;
+ if (IS_ENABLED(CONFIG_SND_SOC_SOF_NOCODEC_DEBUG_SUPPORT) &&
+ sof_debug_check_flag(SOF_DBG_FORCE_NOCODEC))
+ return;
+
/* probe codecs in avail slots */
for (i = 0; i < HDA_MAX_CODECS; i++) {
if (!(bus->codec_mask & (1 << i)))
continue;
- ret = hda_codec_probe(sdev, i, hda_codec_use_common_hdmi);
+ ret = hda_codec_probe(sdev, i);
if (ret < 0) {
dev_warn(bus->dev, "codec #%d probe error, ret: %d\n",
i, ret);
@@ -210,27 +237,193 @@ void hda_codec_probe_bus(struct snd_sof_dev *sdev,
}
}
}
-EXPORT_SYMBOL_NS(hda_codec_probe_bus, SND_SOC_SOF_HDA_AUDIO_CODEC);
+EXPORT_SYMBOL_NS_GPL(hda_codec_probe_bus, "SND_SOC_SOF_HDA_AUDIO_CODEC");
+
+void hda_codec_check_for_state_change(struct snd_sof_dev *sdev)
+{
+ struct hdac_bus *bus = sof_to_bus(sdev);
+ unsigned int codec_mask;
+
+ codec_mask = snd_hdac_chip_readw(bus, STATESTS);
+ if (codec_mask) {
+ hda_codec_jack_check(sdev);
+ snd_hdac_chip_writew(bus, STATESTS, codec_mask);
+ }
+}
+EXPORT_SYMBOL_NS_GPL(hda_codec_check_for_state_change, "SND_SOC_SOF_HDA_AUDIO_CODEC");
+
+void hda_codec_detect_mask(struct snd_sof_dev *sdev)
+{
+ struct hdac_bus *bus = sof_to_bus(sdev);
+
+ if (IS_ENABLED(CONFIG_SND_SOC_SOF_NOCODEC_DEBUG_SUPPORT) &&
+ sof_debug_check_flag(SOF_DBG_FORCE_NOCODEC))
+ return;
+
+ /* Accept unsolicited responses */
+ snd_hdac_chip_updatel(bus, GCTL, AZX_GCTL_UNSOL, AZX_GCTL_UNSOL);
+
+ /* detect codecs */
+ if (!bus->codec_mask) {
+ bus->codec_mask = snd_hdac_chip_readw(bus, STATESTS);
+ dev_dbg(bus->dev, "codec_mask = 0x%lx\n", bus->codec_mask);
+ }
+
+ if (hda_codec_mask != -1) {
+ bus->codec_mask &= hda_codec_mask;
+ dev_dbg(bus->dev, "filtered codec_mask = 0x%lx\n",
+ bus->codec_mask);
+ }
+}
+EXPORT_SYMBOL_NS_GPL(hda_codec_detect_mask, "SND_SOC_SOF_HDA_AUDIO_CODEC");
+
+void hda_codec_init_cmd_io(struct snd_sof_dev *sdev)
+{
+ struct hdac_bus *bus = sof_to_bus(sdev);
+
+ if (IS_ENABLED(CONFIG_SND_SOC_SOF_NOCODEC_DEBUG_SUPPORT) &&
+ sof_debug_check_flag(SOF_DBG_FORCE_NOCODEC))
+ return;
+
+ /* initialize the codec command I/O */
+ snd_hdac_bus_init_cmd_io(bus);
+}
+EXPORT_SYMBOL_NS_GPL(hda_codec_init_cmd_io, "SND_SOC_SOF_HDA_AUDIO_CODEC");
-#if IS_ENABLED(CONFIG_SND_HDA_CODEC_HDMI) || \
- IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI)
+void hda_codec_resume_cmd_io(struct snd_sof_dev *sdev)
+{
+ struct hdac_bus *bus = sof_to_bus(sdev);
+
+ if (IS_ENABLED(CONFIG_SND_SOC_SOF_NOCODEC_DEBUG_SUPPORT) &&
+ sof_debug_check_flag(SOF_DBG_FORCE_NOCODEC))
+ return;
+
+ /* set up CORB/RIRB buffers if was on before suspend */
+ if (bus->cmd_dma_state)
+ snd_hdac_bus_init_cmd_io(bus);
+}
+EXPORT_SYMBOL_NS_GPL(hda_codec_resume_cmd_io, "SND_SOC_SOF_HDA_AUDIO_CODEC");
+
+void hda_codec_stop_cmd_io(struct snd_sof_dev *sdev)
+{
+ struct hdac_bus *bus = sof_to_bus(sdev);
+
+ if (IS_ENABLED(CONFIG_SND_SOC_SOF_NOCODEC_DEBUG_SUPPORT) &&
+ sof_debug_check_flag(SOF_DBG_FORCE_NOCODEC))
+ return;
+
+ /* initialize the codec command I/O */
+ snd_hdac_bus_stop_cmd_io(bus);
+}
+EXPORT_SYMBOL_NS_GPL(hda_codec_stop_cmd_io, "SND_SOC_SOF_HDA_AUDIO_CODEC");
+
+void hda_codec_suspend_cmd_io(struct snd_sof_dev *sdev)
+{
+ struct hdac_bus *bus = sof_to_bus(sdev);
+
+ if (IS_ENABLED(CONFIG_SND_SOC_SOF_NOCODEC_DEBUG_SUPPORT) &&
+ sof_debug_check_flag(SOF_DBG_FORCE_NOCODEC))
+ return;
+
+ /* stop the CORB/RIRB DMA if it is On */
+ if (bus->cmd_dma_state)
+ snd_hdac_bus_stop_cmd_io(bus);
+
+}
+EXPORT_SYMBOL_NS_GPL(hda_codec_suspend_cmd_io, "SND_SOC_SOF_HDA_AUDIO_CODEC");
+
+void hda_codec_rirb_status_clear(struct snd_sof_dev *sdev)
+{
+ struct hdac_bus *bus = sof_to_bus(sdev);
+
+ if (IS_ENABLED(CONFIG_SND_SOC_SOF_NOCODEC_DEBUG_SUPPORT) &&
+ sof_debug_check_flag(SOF_DBG_FORCE_NOCODEC))
+ return;
+
+ /* clear rirb status */
+ snd_hdac_chip_writeb(bus, RIRBSTS, RIRB_INT_MASK);
+}
+EXPORT_SYMBOL_NS_GPL(hda_codec_rirb_status_clear, "SND_SOC_SOF_HDA_AUDIO_CODEC");
+
+void hda_codec_set_codec_wakeup(struct snd_sof_dev *sdev, bool status)
+{
+ struct hdac_bus *bus = sof_to_bus(sdev);
+
+ if (sof_debug_check_flag(SOF_DBG_FORCE_NOCODEC))
+ return;
+
+ snd_hdac_set_codec_wakeup(bus, status);
+}
+EXPORT_SYMBOL_NS_GPL(hda_codec_set_codec_wakeup, "SND_SOC_SOF_HDA_AUDIO_CODEC");
+
+bool hda_codec_check_rirb_status(struct snd_sof_dev *sdev)
+{
+ struct hdac_bus *bus = sof_to_bus(sdev);
+ bool active = false;
+ u32 rirb_status;
+
+ if (IS_ENABLED(CONFIG_SND_SOC_SOF_NOCODEC_DEBUG_SUPPORT) &&
+ sof_debug_check_flag(SOF_DBG_FORCE_NOCODEC))
+ return false;
+
+ rirb_status = snd_hdac_chip_readb(bus, RIRBSTS);
+ if (rirb_status & RIRB_INT_MASK) {
+ /*
+ * Clearing the interrupt status here ensures
+ * that no interrupt gets masked after the RIRB
+ * wp is read in snd_hdac_bus_update_rirb.
+ */
+ snd_hdac_chip_writeb(bus, RIRBSTS,
+ RIRB_INT_MASK);
+ active = true;
+ if (rirb_status & RIRB_INT_RESPONSE)
+ snd_hdac_bus_update_rirb(bus);
+ }
+ return active;
+}
+EXPORT_SYMBOL_NS_GPL(hda_codec_check_rirb_status, "SND_SOC_SOF_HDA_AUDIO_CODEC");
+
+void hda_codec_device_remove(struct snd_sof_dev *sdev)
+{
+ struct hdac_bus *bus = sof_to_bus(sdev);
+
+ if (IS_ENABLED(CONFIG_SND_SOC_SOF_NOCODEC_DEBUG_SUPPORT) &&
+ sof_debug_check_flag(SOF_DBG_FORCE_NOCODEC))
+ return;
+
+ /* codec removal, invoke bus_device_remove */
+ snd_hdac_ext_bus_device_remove(bus);
+}
+EXPORT_SYMBOL_NS_GPL(hda_codec_device_remove, "SND_SOC_SOF_HDA_AUDIO_CODEC");
+
+#endif /* CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC */
+
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC) && IS_ENABLED(CONFIG_SND_HDA_CODEC_HDMI)
void hda_codec_i915_display_power(struct snd_sof_dev *sdev, bool enable)
{
struct hdac_bus *bus = sof_to_bus(sdev);
+ if (IS_ENABLED(CONFIG_SND_SOC_SOF_NOCODEC_DEBUG_SUPPORT) &&
+ sof_debug_check_flag(SOF_DBG_FORCE_NOCODEC))
+ return;
+
if (HDA_IDISP_CODEC(bus->codec_mask)) {
dev_dbg(bus->dev, "Turning i915 HDAC power %d\n", enable);
snd_hdac_display_power(bus, HDA_CODEC_IDX_CONTROLLER, enable);
}
}
-EXPORT_SYMBOL_NS(hda_codec_i915_display_power, SND_SOC_SOF_HDA_AUDIO_CODEC_I915);
+EXPORT_SYMBOL_NS_GPL(hda_codec_i915_display_power, "SND_SOC_SOF_HDA_AUDIO_CODEC_I915");
int hda_codec_i915_init(struct snd_sof_dev *sdev)
{
struct hdac_bus *bus = sof_to_bus(sdev);
int ret;
+ if (IS_ENABLED(CONFIG_SND_SOC_SOF_NOCODEC_DEBUG_SUPPORT) &&
+ sof_debug_check_flag(SOF_DBG_FORCE_NOCODEC))
+ return 0;
+
/* i915 exposes a HDA codec for HDMI audio */
ret = snd_hdac_i915_init(bus);
if (ret < 0)
@@ -241,12 +434,16 @@ int hda_codec_i915_init(struct snd_sof_dev *sdev)
return 0;
}
-EXPORT_SYMBOL_NS(hda_codec_i915_init, SND_SOC_SOF_HDA_AUDIO_CODEC_I915);
+EXPORT_SYMBOL_NS_GPL(hda_codec_i915_init, "SND_SOC_SOF_HDA_AUDIO_CODEC_I915");
int hda_codec_i915_exit(struct snd_sof_dev *sdev)
{
struct hdac_bus *bus = sof_to_bus(sdev);
+ if (IS_ENABLED(CONFIG_SND_SOC_SOF_NOCODEC_DEBUG_SUPPORT) &&
+ sof_debug_check_flag(SOF_DBG_FORCE_NOCODEC))
+ return 0;
+
if (!bus->audio_component)
return 0;
@@ -255,8 +452,10 @@ int hda_codec_i915_exit(struct snd_sof_dev *sdev)
return snd_hdac_i915_exit(bus);
}
-EXPORT_SYMBOL_NS(hda_codec_i915_exit, SND_SOC_SOF_HDA_AUDIO_CODEC_I915);
+EXPORT_SYMBOL_NS_GPL(hda_codec_i915_exit, "SND_SOC_SOF_HDA_AUDIO_CODEC_I915");
+MODULE_SOFTDEP("pre: snd-hda-codec-hdmi");
#endif
MODULE_LICENSE("Dual BSD/GPL");
+MODULE_DESCRIPTION("SOF support for HDaudio codecs");
diff --git a/sound/soc/sof/intel/hda-common-ops.c b/sound/soc/sof/intel/hda-common-ops.c
new file mode 100644
index 000000000000..746b426b1329
--- /dev/null
+++ b/sound/soc/sof/intel/hda-common-ops.c
@@ -0,0 +1,108 @@
+// SPDX-License-Identifier: (GPL-2.0-only 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) 2022 Intel Corporation
+//
+
+/*
+ * common ops for SKL+ HDAudio platforms
+ */
+
+#include "../sof-priv.h"
+#include "hda.h"
+#include "../sof-audio.h"
+
+const struct snd_sof_dsp_ops sof_hda_common_ops = {
+ /* probe/remove/shutdown */
+ .probe_early = hda_dsp_probe_early,
+ .probe = hda_dsp_probe,
+ .remove = hda_dsp_remove,
+ .remove_late = hda_dsp_remove_late,
+
+ /* Register IO uses direct mmio */
+
+ /* Block IO */
+ .block_read = sof_block_read,
+ .block_write = sof_block_write,
+
+ /* Mailbox IO */
+ .mailbox_read = sof_mailbox_read,
+ .mailbox_write = sof_mailbox_write,
+
+ /* ipc */
+ .get_mailbox_offset = hda_dsp_ipc_get_mailbox_offset,
+ .get_window_offset = hda_dsp_ipc_get_window_offset,
+
+ .ipc_msg_data = hda_ipc_msg_data,
+ .set_stream_data_offset = hda_set_stream_data_offset,
+
+ /* machine driver */
+ .machine_select = hda_machine_select,
+ .machine_register = sof_machine_register,
+ .machine_unregister = sof_machine_unregister,
+ .set_mach_params = hda_set_mach_params,
+
+ /* debug */
+ .dbg_dump = hda_dsp_dump,
+ .debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem,
+
+ /* stream callbacks */
+ .pcm_open = hda_dsp_pcm_open,
+ .pcm_close = hda_dsp_pcm_close,
+ .pcm_hw_params = hda_dsp_pcm_hw_params,
+ .pcm_hw_free = hda_dsp_stream_hw_free,
+ .pcm_trigger = hda_dsp_pcm_trigger,
+ .pcm_pointer = hda_dsp_pcm_pointer,
+ .pcm_ack = hda_dsp_pcm_ack,
+
+ .get_dai_frame_counter = hda_dsp_get_stream_llp,
+ .get_host_byte_counter = hda_dsp_get_stream_ldp,
+
+ /* firmware loading */
+ .load_firmware = snd_sof_load_firmware_raw,
+
+ /* pre/post fw run */
+ .pre_fw_run = hda_dsp_pre_fw_run,
+
+ /* firmware run */
+ .run = hda_dsp_cl_boot_firmware,
+
+ /* parse platform specific extended manifest */
+ .parse_platform_ext_manifest = hda_dsp_ext_man_get_cavs_config_data,
+
+ /* dsp core get/put */
+
+ /* trace callback */
+ .trace_init = hda_dsp_trace_init,
+ .trace_release = hda_dsp_trace_release,
+ .trace_trigger = hda_dsp_trace_trigger,
+
+ /* client ops */
+ .register_ipc_clients = hda_register_clients,
+ .unregister_ipc_clients = hda_unregister_clients,
+
+ /* DAI drivers */
+ .drv = skl_dai,
+ .num_drv = SOF_SKL_NUM_DAIS,
+ .is_chain_dma_supported = hda_is_chain_dma_supported,
+
+ /* PM */
+ .suspend = hda_dsp_suspend,
+ .resume = hda_dsp_resume,
+ .runtime_suspend = hda_dsp_runtime_suspend,
+ .runtime_resume = hda_dsp_runtime_resume,
+ .runtime_idle = hda_dsp_runtime_idle,
+ .set_hw_params_upon_resume = hda_dsp_set_hw_params_upon_resume,
+
+ /* ALSA HW info flags */
+ .hw_info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_NO_PERIOD_WAKEUP,
+
+ .dsp_arch_ops = &sof_xtensa_arch_ops,
+};
+EXPORT_SYMBOL_NS(sof_hda_common_ops, "SND_SOC_SOF_INTEL_HDA_GENERIC");
diff --git a/sound/soc/sof/intel/hda-compress.c b/sound/soc/sof/intel/hda-compress.c
deleted file mode 100644
index 53c08034fa22..000000000000
--- a/sound/soc/sof/intel/hda-compress.c
+++ /dev/null
@@ -1,114 +0,0 @@
-// SPDX-License-Identifier: (GPL-2.0-only 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-2020 Intel Corporation. All rights reserved.
-//
-// Author: Cezary Rojewski <cezary.rojewski@intel.com>
-//
-
-#include <sound/hdaudio_ext.h>
-#include <sound/soc.h>
-#include "../sof-priv.h"
-#include "hda.h"
-
-static inline struct hdac_ext_stream *
-hda_compr_get_stream(struct snd_compr_stream *cstream)
-{
- return cstream->runtime->private_data;
-}
-
-int hda_probe_compr_assign(struct snd_sof_dev *sdev,
- struct snd_compr_stream *cstream,
- struct snd_soc_dai *dai)
-{
- struct hdac_ext_stream *stream;
-
- stream = hda_dsp_stream_get(sdev, cstream->direction);
- if (!stream)
- return -EBUSY;
-
- hdac_stream(stream)->curr_pos = 0;
- hdac_stream(stream)->cstream = cstream;
- cstream->runtime->private_data = stream;
-
- return hdac_stream(stream)->stream_tag;
-}
-
-int hda_probe_compr_free(struct snd_sof_dev *sdev,
- struct snd_compr_stream *cstream,
- struct snd_soc_dai *dai)
-{
- struct hdac_ext_stream *stream = hda_compr_get_stream(cstream);
- int ret;
-
- ret = hda_dsp_stream_put(sdev, cstream->direction,
- hdac_stream(stream)->stream_tag);
- if (ret < 0) {
- dev_dbg(sdev->dev, "stream put failed: %d\n", ret);
- return ret;
- }
-
- hdac_stream(stream)->cstream = NULL;
- cstream->runtime->private_data = NULL;
-
- return 0;
-}
-
-int hda_probe_compr_set_params(struct snd_sof_dev *sdev,
- struct snd_compr_stream *cstream,
- struct snd_compr_params *params,
- struct snd_soc_dai *dai)
-{
- struct hdac_ext_stream *stream = hda_compr_get_stream(cstream);
- struct hdac_stream *hstream = hdac_stream(stream);
- struct snd_dma_buffer *dmab;
- u32 bits, rate;
- int bps, ret;
-
- dmab = cstream->runtime->dma_buffer_p;
- /* compr params do not store bit depth, default to S32_LE */
- bps = snd_pcm_format_physical_width(SNDRV_PCM_FORMAT_S32_LE);
- if (bps < 0)
- return bps;
- bits = hda_dsp_get_bits(sdev, bps);
- rate = hda_dsp_get_mult_div(sdev, params->codec.sample_rate);
-
- hstream->format_val = rate | bits | (params->codec.ch_out - 1);
- hstream->bufsize = cstream->runtime->buffer_size;
- hstream->period_bytes = cstream->runtime->fragment_size;
- hstream->no_period_wakeup = 0;
-
- ret = hda_dsp_stream_hw_params(sdev, stream, dmab, NULL);
- if (ret < 0) {
- dev_err(sdev->dev, "error: hdac prepare failed: %x\n", ret);
- return ret;
- }
-
- return 0;
-}
-
-int hda_probe_compr_trigger(struct snd_sof_dev *sdev,
- struct snd_compr_stream *cstream, int cmd,
- struct snd_soc_dai *dai)
-{
- struct hdac_ext_stream *stream = hda_compr_get_stream(cstream);
-
- return hda_dsp_stream_trigger(sdev, stream, cmd);
-}
-
-int hda_probe_compr_pointer(struct snd_sof_dev *sdev,
- struct snd_compr_stream *cstream,
- struct snd_compr_tstamp *tstamp,
- struct snd_soc_dai *dai)
-{
- struct hdac_ext_stream *stream = hda_compr_get_stream(cstream);
- struct snd_soc_pcm_stream *pstream;
-
- pstream = &dai->driver->capture;
- tstamp->copied_total = hdac_stream(stream)->curr_pos;
- tstamp->sampling_rate = snd_pcm_rate_bit_to_rate(pstream->rates);
-
- return 0;
-}
diff --git a/sound/soc/sof/intel/hda-ctrl.c b/sound/soc/sof/intel/hda-ctrl.c
index fa5f0a718901..4f34fd919a00 100644
--- a/sound/soc/sof/intel/hda-ctrl.c
+++ b/sound/soc/sof/intel/hda-ctrl.c
@@ -3,7 +3,7 @@
// 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) 2018 Intel Corporation. All rights reserved.
+// Copyright(c) 2018 Intel Corporation
//
// Authors: Liam Girdwood <liam.r.girdwood@linux.intel.com>
// Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
@@ -19,15 +19,10 @@
#include <sound/hdaudio_ext.h>
#include <sound/hda_register.h>
#include <sound/hda_component.h>
+#include <sound/hda-mlink.h>
#include "../ops.h"
#include "hda.h"
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
-static int hda_codec_mask = -1;
-module_param_named(codec_mask, hda_codec_mask, int, 0444);
-MODULE_PARM_DESC(codec_mask, "SOF HDA codec mask for probing");
-#endif
-
/*
* HDA Operations.
*/
@@ -133,6 +128,7 @@ int hda_dsp_ctrl_get_caps(struct snd_sof_dev *sdev)
return 0;
}
+EXPORT_SYMBOL_NS(hda_dsp_ctrl_get_caps, "SND_SOC_SOF_INTEL_HDA_COMMON");
void hda_dsp_ctrl_ppcap_enable(struct snd_sof_dev *sdev, bool enable)
{
@@ -141,6 +137,7 @@ void hda_dsp_ctrl_ppcap_enable(struct snd_sof_dev *sdev, bool enable)
snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL,
SOF_HDA_PPCTL_GPROCEN, val);
}
+EXPORT_SYMBOL_NS(hda_dsp_ctrl_ppcap_enable, "SND_SOC_SOF_INTEL_HDA_COMMON");
void hda_dsp_ctrl_ppcap_int_enable(struct snd_sof_dev *sdev, bool enable)
{
@@ -149,6 +146,7 @@ void hda_dsp_ctrl_ppcap_int_enable(struct snd_sof_dev *sdev, bool enable)
snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL,
SOF_HDA_PPCTL_PIE, val);
}
+EXPORT_SYMBOL_NS(hda_dsp_ctrl_ppcap_int_enable, "SND_SOC_SOF_INTEL_HDA_COMMON");
void hda_dsp_ctrl_misc_clock_gating(struct snd_sof_dev *sdev, bool enable)
{
@@ -164,16 +162,18 @@ void hda_dsp_ctrl_misc_clock_gating(struct snd_sof_dev *sdev, bool enable)
*/
int hda_dsp_ctrl_clock_power_gating(struct snd_sof_dev *sdev, bool enable)
{
+ struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
u32 val;
/* enable/disable audio dsp clock gating */
val = enable ? PCI_CGCTL_ADSPDCGE : 0;
snd_sof_pci_update_bits(sdev, PCI_CGCTL, PCI_CGCTL_ADSPDCGE, val);
- /* enable/disable DMI Link L1 support */
+ /* disable the DMI link when requested. But enable only if it wasn't disabled previously */
val = enable ? HDA_VS_INTEL_EM2_L1SEN : 0;
- snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, HDA_VS_INTEL_EM2,
- HDA_VS_INTEL_EM2_L1SEN, val);
+ if (!enable || !hda->l1_disabled)
+ snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, HDA_VS_INTEL_EM2,
+ HDA_VS_INTEL_EM2_L1SEN, val);
/* enable/disable audio dsp power gating */
val = enable ? 0 : PCI_PGCTL_ADSPPGD;
@@ -181,93 +181,66 @@ int hda_dsp_ctrl_clock_power_gating(struct snd_sof_dev *sdev, bool enable)
return 0;
}
+EXPORT_SYMBOL_NS(hda_dsp_ctrl_clock_power_gating, "SND_SOC_SOF_INTEL_HDA_COMMON");
-int hda_dsp_ctrl_init_chip(struct snd_sof_dev *sdev, bool full_reset)
+int hda_dsp_ctrl_init_chip(struct snd_sof_dev *sdev)
{
struct hdac_bus *bus = sof_to_bus(sdev);
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
- struct hdac_ext_link *hlink;
-#endif
struct hdac_stream *stream;
int sd_offset, ret = 0;
+ u32 gctl;
if (bus->chip_init)
return 0;
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
- snd_hdac_set_codec_wakeup(bus, true);
-#endif
- hda_dsp_ctrl_misc_clock_gating(sdev, false);
-
- if (full_reset) {
- /* reset HDA controller */
- ret = hda_dsp_ctrl_link_reset(sdev, true);
- if (ret < 0) {
- dev_err(sdev->dev, "error: failed to reset HDA controller\n");
- goto err;
- }
-
- usleep_range(500, 1000);
+ hda_codec_set_codec_wakeup(sdev, true);
- /* exit HDA controller reset */
- ret = hda_dsp_ctrl_link_reset(sdev, false);
- if (ret < 0) {
- dev_err(sdev->dev, "error: failed to exit HDA controller reset\n");
- goto err;
- }
+ hda_dsp_ctrl_misc_clock_gating(sdev, false);
- usleep_range(1000, 1200);
- }
+ /* clear WAKE_STS if not in reset */
+ gctl = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, SOF_HDA_GCTL);
+ if (gctl & SOF_HDA_GCTL_RESET)
+ snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR,
+ SOF_HDA_WAKESTS, SOF_HDA_WAKESTS_INT_MASK);
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
- /* check to see if controller is ready */
- if (!snd_hdac_chip_readb(bus, GCTL)) {
- dev_dbg(bus->dev, "controller not ready!\n");
- ret = -EBUSY;
+ /* reset HDA controller */
+ ret = hda_dsp_ctrl_link_reset(sdev, true);
+ if (ret < 0) {
+ dev_err(sdev->dev, "error: failed to reset HDA controller\n");
goto err;
}
- /* Accept unsolicited responses */
- snd_hdac_chip_updatel(bus, GCTL, AZX_GCTL_UNSOL, AZX_GCTL_UNSOL);
+ usleep_range(500, 1000);
- /* detect codecs */
- if (!bus->codec_mask) {
- bus->codec_mask = snd_hdac_chip_readw(bus, STATESTS);
- dev_dbg(bus->dev, "codec_mask = 0x%lx\n", bus->codec_mask);
+ /* exit HDA controller reset */
+ ret = hda_dsp_ctrl_link_reset(sdev, false);
+ if (ret < 0) {
+ dev_err(sdev->dev, "error: failed to exit HDA controller reset\n");
+ goto err;
}
+ usleep_range(1000, 1200);
- if (hda_codec_mask != -1) {
- bus->codec_mask &= hda_codec_mask;
- dev_dbg(bus->dev, "filtered codec_mask = 0x%lx\n",
- bus->codec_mask);
- }
-#endif
+ hda_codec_detect_mask(sdev);
/* clear stream status */
list_for_each_entry(stream, &bus->stream_list, list) {
sd_offset = SOF_STREAM_SD_OFFSET(stream);
snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR,
- sd_offset + SOF_HDA_ADSP_REG_CL_SD_STS,
+ sd_offset + SOF_HDA_ADSP_REG_SD_STS,
SOF_HDA_CL_DMA_SD_INT_MASK);
}
/* clear WAKESTS */
snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, SOF_HDA_WAKESTS,
- SOF_HDA_WAKESTS_INT_MASK);
+ bus->codec_mask);
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
- /* clear rirb status */
- snd_hdac_chip_writeb(bus, RIRBSTS, RIRB_INT_MASK);
-#endif
+ hda_codec_rirb_status_clear(sdev);
/* clear interrupt status register */
snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTSTS,
SOF_HDA_INT_CTRL_EN | SOF_HDA_INT_ALL_STREAM);
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
- /* initialize the codec command I/O */
- snd_hdac_bus_init_cmd_io(bus);
-#endif
+ hda_codec_init_cmd_io(sdev);
/* enable CIE and GIE interrupts */
snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTCTL,
@@ -282,22 +255,18 @@ int hda_dsp_ctrl_init_chip(struct snd_sof_dev *sdev, bool full_reset)
upper_32_bits(bus->posbuf.addr));
}
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
- /* Reset stream-to-link mapping */
- list_for_each_entry(hlink, &bus->hlink_list, list)
- writel(0, hlink->ml_addr + AZX_REG_ML_LOSIDV);
-#endif
+ hda_bus_ml_reset_losidv(bus);
bus->chip_init = true;
err:
hda_dsp_ctrl_misc_clock_gating(sdev, true);
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
- snd_hdac_set_codec_wakeup(bus, false);
-#endif
+
+ hda_codec_set_codec_wakeup(sdev, false);
return ret;
}
+EXPORT_SYMBOL_NS(hda_dsp_ctrl_init_chip, "SND_SOC_SOF_INTEL_HDA_COMMON");
void hda_dsp_ctrl_stop_chip(struct snd_sof_dev *sdev)
{
@@ -313,7 +282,7 @@ void hda_dsp_ctrl_stop_chip(struct snd_sof_dev *sdev)
sd_offset = SOF_STREAM_SD_OFFSET(stream);
snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR,
sd_offset +
- SOF_HDA_ADSP_REG_CL_SD_CTL,
+ SOF_HDA_ADSP_REG_SD_CTL,
SOF_HDA_CL_DMA_SD_INT_MASK,
0);
}
@@ -331,7 +300,7 @@ void hda_dsp_ctrl_stop_chip(struct snd_sof_dev *sdev)
list_for_each_entry(stream, &bus->stream_list, list) {
sd_offset = SOF_STREAM_SD_OFFSET(stream);
snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR,
- sd_offset + SOF_HDA_ADSP_REG_CL_SD_STS,
+ sd_offset + SOF_HDA_ADSP_REG_SD_STS,
SOF_HDA_CL_DMA_SD_INT_MASK);
}
@@ -339,21 +308,16 @@ void hda_dsp_ctrl_stop_chip(struct snd_sof_dev *sdev)
snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, SOF_HDA_WAKESTS,
SOF_HDA_WAKESTS_INT_MASK);
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
- /* clear rirb status */
- snd_hdac_chip_writeb(bus, RIRBSTS, RIRB_INT_MASK);
-#endif
+ hda_codec_rirb_status_clear(sdev);
/* clear interrupt status register */
snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTSTS,
SOF_HDA_INT_CTRL_EN | SOF_HDA_INT_ALL_STREAM);
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
- /* disable CORB/RIRB */
- snd_hdac_bus_stop_cmd_io(bus);
-#endif
+ hda_codec_stop_cmd_io(sdev);
+
/* disable position buffer */
- if (bus->posbuf.addr) {
+ if (bus->use_posbuf && bus->posbuf.addr) {
snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR,
SOF_HDA_ADSP_DPLBASE, 0);
snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR,
@@ -362,3 +326,9 @@ void hda_dsp_ctrl_stop_chip(struct snd_sof_dev *sdev)
bus->chip_init = false;
}
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_DESCRIPTION("SOF helpers for HDaudio platforms");
+MODULE_IMPORT_NS("SND_SOC_SOF_HDA_MLINK");
+MODULE_IMPORT_NS("SND_SOC_SOF_HDA_AUDIO_CODEC");
+MODULE_IMPORT_NS("SND_SOC_SOF_HDA_AUDIO_CODEC_I915");
diff --git a/sound/soc/sof/intel/hda-dai-ops.c b/sound/soc/sof/intel/hda-dai-ops.c
new file mode 100644
index 000000000000..92681ca7f24d
--- /dev/null
+++ b/sound/soc/sof/intel/hda-dai-ops.c
@@ -0,0 +1,660 @@
+// SPDX-License-Identifier: (GPL-2.0-only 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) 2022 Intel Corporation
+
+#include <sound/pcm_params.h>
+#include <sound/hdaudio_ext.h>
+#include <sound/hda_register.h>
+#include <sound/hda-mlink.h>
+#include <sound/sof/ipc4/header.h>
+#include <uapi/sound/sof/header.h>
+#include "../ipc4-priv.h"
+#include "../ipc4-topology.h"
+#include "../sof-priv.h"
+#include "../sof-audio.h"
+#include "hda.h"
+
+/* These ops are only applicable for the HDA DAI's in their current form */
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_LINK)
+/*
+ * This function checks if the host dma channel corresponding
+ * to the link DMA stream_tag argument is assigned to one
+ * of the FEs connected to the BE DAI.
+ */
+static bool hda_check_fes(struct snd_soc_pcm_runtime *rtd,
+ int dir, int stream_tag)
+{
+ struct snd_pcm_substream *fe_substream;
+ struct hdac_stream *fe_hstream;
+ struct snd_soc_dpcm *dpcm;
+
+ for_each_dpcm_fe(rtd, dir, dpcm) {
+ fe_substream = snd_soc_dpcm_get_substream(dpcm->fe, dir);
+ fe_hstream = fe_substream->runtime->private_data;
+ if (fe_hstream->stream_tag == stream_tag)
+ return true;
+ }
+
+ return false;
+}
+
+static struct hdac_ext_stream *
+hda_link_stream_assign(struct hdac_bus *bus, struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct sof_intel_hda_stream *hda_stream;
+ const struct sof_intel_dsp_desc *chip;
+ struct snd_sof_dev *sdev;
+ struct hdac_ext_stream *res = NULL;
+ struct hdac_stream *hstream = NULL;
+
+ int stream_dir = substream->stream;
+
+ if (!bus->ppcap) {
+ dev_err(bus->dev, "stream type not supported\n");
+ return NULL;
+ }
+
+ spin_lock_irq(&bus->reg_lock);
+ list_for_each_entry(hstream, &bus->stream_list, list) {
+ struct hdac_ext_stream *hext_stream =
+ stream_to_hdac_ext_stream(hstream);
+ if (hstream->direction != substream->stream)
+ continue;
+
+ hda_stream = hstream_to_sof_hda_stream(hext_stream);
+ sdev = hda_stream->sdev;
+ chip = get_chip_info(sdev->pdata);
+
+ /* check if link is available */
+ if (!hext_stream->link_locked) {
+ /*
+ * choose the first available link for platforms that do not have the
+ * PROCEN_FMT_QUIRK set.
+ */
+ if (!(chip->quirks & SOF_INTEL_PROCEN_FMT_QUIRK)) {
+ res = hext_stream;
+ break;
+ }
+
+ if (hstream->opened) {
+ /*
+ * check if the stream tag matches the stream
+ * tag of one of the connected FEs
+ */
+ if (hda_check_fes(rtd, stream_dir,
+ hstream->stream_tag)) {
+ res = hext_stream;
+ break;
+ }
+ } else {
+ res = hext_stream;
+
+ /*
+ * This must be a hostless stream.
+ * So reserve the host DMA channel.
+ */
+ hda_stream->host_reserved = 1;
+ break;
+ }
+ }
+ }
+
+ if (res) {
+ /* Make sure that host and link DMA is decoupled. */
+ snd_hdac_ext_stream_decouple_locked(bus, res, true);
+
+ res->link_locked = 1;
+ res->link_substream = substream;
+ }
+ spin_unlock_irq(&bus->reg_lock);
+
+ return res;
+}
+
+static struct hdac_ext_stream *hda_get_hext_stream(struct snd_sof_dev *sdev,
+ struct snd_soc_dai *cpu_dai,
+ struct snd_pcm_substream *substream)
+{
+ return snd_soc_dai_get_dma_data(cpu_dai, substream);
+}
+
+static struct hdac_ext_stream *hda_ipc4_get_hext_stream(struct snd_sof_dev *sdev,
+ struct snd_soc_dai *cpu_dai,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_sof_widget *pipe_widget;
+ struct sof_ipc4_pipeline *pipeline;
+ struct snd_sof_widget *swidget;
+ struct snd_soc_dapm_widget *w;
+
+ w = snd_soc_dai_get_widget(cpu_dai, substream->stream);
+ swidget = w->dobj.private;
+ pipe_widget = swidget->spipe->pipe_widget;
+ pipeline = pipe_widget->private;
+
+ /* mark pipeline so that it can be skipped during FE trigger */
+ pipeline->skip_during_fe_trigger = true;
+
+ return snd_soc_dai_get_dma_data(cpu_dai, substream);
+}
+
+static struct hdac_ext_stream *hda_assign_hext_stream(struct snd_sof_dev *sdev,
+ struct snd_soc_dai *cpu_dai,
+ struct snd_pcm_substream *substream)
+{
+ struct hdac_ext_stream *hext_stream;
+
+ hext_stream = hda_link_stream_assign(sof_to_bus(sdev), substream);
+ if (!hext_stream)
+ return NULL;
+
+ snd_soc_dai_set_dma_data(cpu_dai, substream, (void *)hext_stream);
+
+ return hext_stream;
+}
+
+static void hda_release_hext_stream(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
+ struct snd_pcm_substream *substream)
+{
+ struct hdac_ext_stream *hext_stream = hda_get_hext_stream(sdev, cpu_dai, substream);
+
+ snd_soc_dai_set_dma_data(cpu_dai, substream, NULL);
+ snd_hdac_ext_stream_release(hext_stream, HDAC_EXT_STREAM_TYPE_LINK);
+}
+
+static void hda_setup_hext_stream(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_stream,
+ unsigned int format_val)
+{
+ snd_hdac_ext_stream_setup(hext_stream, format_val);
+}
+
+static void hda_reset_hext_stream(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_stream)
+{
+ snd_hdac_ext_stream_reset(hext_stream);
+}
+
+static void hda_codec_dai_set_stream(struct snd_sof_dev *sdev,
+ struct snd_pcm_substream *substream,
+ struct hdac_stream *hstream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+
+ /* set the hdac_stream in the codec dai */
+ snd_soc_dai_set_stream(codec_dai, hstream, substream->stream);
+}
+
+static unsigned int hda_calc_stream_format(struct snd_sof_dev *sdev,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ unsigned int link_bps;
+ unsigned int format_val;
+ unsigned int bits;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ link_bps = codec_dai->driver->playback.sig_bits;
+ else
+ link_bps = codec_dai->driver->capture.sig_bits;
+
+ bits = snd_hdac_stream_format_bits(params_format(params), SNDRV_PCM_SUBFORMAT_STD,
+ link_bps);
+ format_val = snd_hdac_stream_format(params_channels(params), bits, params_rate(params));
+
+ dev_dbg(sdev->dev, "format_val=%#x, rate=%d, ch=%d, format=%d\n", format_val,
+ params_rate(params), params_channels(params), params_format(params));
+
+ return format_val;
+}
+
+static struct hdac_ext_link *hda_get_hlink(struct snd_sof_dev *sdev,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ struct hdac_bus *bus = sof_to_bus(sdev);
+
+ return snd_hdac_ext_bus_get_hlink_by_name(bus, codec_dai->component->name);
+}
+
+static unsigned int generic_calc_stream_format(struct snd_sof_dev *sdev,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ unsigned int format_val;
+ unsigned int bits;
+
+ bits = snd_hdac_stream_format_bits(params_format(params), SNDRV_PCM_SUBFORMAT_STD,
+ params_physical_width(params));
+ format_val = snd_hdac_stream_format(params_channels(params), bits, params_rate(params));
+
+ dev_dbg(sdev->dev, "format_val=%#x, rate=%d, ch=%d, format=%d\n", format_val,
+ params_rate(params), params_channels(params), params_format(params));
+
+ return format_val;
+}
+
+static unsigned int dmic_calc_stream_format(struct snd_sof_dev *sdev,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ unsigned int format_val;
+ snd_pcm_format_t format;
+ unsigned int channels;
+ unsigned int width;
+ unsigned int bits;
+
+ channels = params_channels(params);
+ format = params_format(params);
+ width = params_physical_width(params);
+
+ if (format == SNDRV_PCM_FORMAT_S16_LE) {
+ format = SNDRV_PCM_FORMAT_S32_LE;
+ channels /= 2;
+ width = 32;
+ }
+
+ bits = snd_hdac_stream_format_bits(format, SNDRV_PCM_SUBFORMAT_STD, width);
+ format_val = snd_hdac_stream_format(channels, bits, params_rate(params));
+
+ dev_dbg(sdev->dev, "format_val=%#x, rate=%d, ch=%d, format=%d\n", format_val,
+ params_rate(params), channels, format);
+
+ return format_val;
+}
+
+static struct hdac_ext_link *ssp_get_hlink(struct snd_sof_dev *sdev,
+ struct snd_pcm_substream *substream)
+{
+ struct hdac_bus *bus = sof_to_bus(sdev);
+
+ return hdac_bus_eml_ssp_get_hlink(bus);
+}
+
+static struct hdac_ext_link *dmic_get_hlink(struct snd_sof_dev *sdev,
+ struct snd_pcm_substream *substream)
+{
+ struct hdac_bus *bus = sof_to_bus(sdev);
+
+ return hdac_bus_eml_dmic_get_hlink(bus);
+}
+
+static struct hdac_ext_link *sdw_get_hlink(struct snd_sof_dev *sdev,
+ struct snd_pcm_substream *substream)
+{
+ struct hdac_bus *bus = sof_to_bus(sdev);
+
+ return hdac_bus_eml_sdw_get_hlink(bus);
+}
+
+static int hda_ipc4_pre_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
+ struct snd_pcm_substream *substream, int cmd)
+{
+ struct sof_ipc4_fw_data *ipc4_data = sdev->private;
+ struct snd_sof_widget *pipe_widget;
+ struct sof_ipc4_pipeline *pipeline;
+ struct snd_sof_widget *swidget;
+ struct snd_soc_dapm_widget *w;
+ int ret = 0;
+
+ w = snd_soc_dai_get_widget(cpu_dai, substream->stream);
+ swidget = w->dobj.private;
+ pipe_widget = swidget->spipe->pipe_widget;
+ pipeline = pipe_widget->private;
+
+ if (pipe_widget->instance_id < 0)
+ return 0;
+
+ mutex_lock(&ipc4_data->pipeline_state_mutex);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_STOP:
+ ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
+ SOF_IPC4_PIPE_PAUSED);
+ if (ret < 0)
+ goto out;
+
+ pipeline->state = SOF_IPC4_PIPE_PAUSED;
+ break;
+ default:
+ dev_err(sdev->dev, "unknown trigger command %d\n", cmd);
+ ret = -EINVAL;
+ }
+out:
+ mutex_unlock(&ipc4_data->pipeline_state_mutex);
+ return ret;
+}
+
+static int hda_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
+ struct snd_pcm_substream *substream, int cmd)
+{
+ struct hdac_ext_stream *hext_stream = snd_soc_dai_get_dma_data(cpu_dai, substream);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ snd_hdac_ext_stream_start(hext_stream);
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ /*
+ * Save the LLP registers since in case of PAUSE the LLP
+ * register are not reset to 0, the delay calculation will use
+ * the saved offsets for compensating the delay calculation.
+ */
+ hext_stream->pplcllpl = readl(hext_stream->pplc_addr + AZX_REG_PPLCLLPL);
+ hext_stream->pplcllpu = readl(hext_stream->pplc_addr + AZX_REG_PPLCLLPU);
+ snd_hdac_ext_stream_clear(hext_stream);
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_STOP:
+ hext_stream->pplcllpl = 0;
+ hext_stream->pplcllpu = 0;
+ snd_hdac_ext_stream_clear(hext_stream);
+ break;
+ default:
+ dev_err(sdev->dev, "unknown trigger command %d\n", cmd);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int hda_ipc4_post_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
+ struct snd_pcm_substream *substream, int cmd)
+{
+ struct sof_ipc4_fw_data *ipc4_data = sdev->private;
+ struct snd_sof_widget *pipe_widget;
+ struct sof_ipc4_pipeline *pipeline;
+ struct snd_sof_widget *swidget;
+ struct snd_soc_dapm_widget *w;
+ int ret = 0;
+
+ w = snd_soc_dai_get_widget(cpu_dai, substream->stream);
+ swidget = w->dobj.private;
+ pipe_widget = swidget->spipe->pipe_widget;
+ pipeline = pipe_widget->private;
+
+ if (pipe_widget->instance_id < 0)
+ return 0;
+
+ mutex_lock(&ipc4_data->pipeline_state_mutex);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ if (pipeline->state != SOF_IPC4_PIPE_PAUSED) {
+ ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
+ SOF_IPC4_PIPE_PAUSED);
+ if (ret < 0)
+ goto out;
+ pipeline->state = SOF_IPC4_PIPE_PAUSED;
+ }
+
+ ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
+ SOF_IPC4_PIPE_RUNNING);
+ if (ret < 0)
+ goto out;
+ pipeline->state = SOF_IPC4_PIPE_RUNNING;
+ swidget->spipe->started_count++;
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
+ SOF_IPC4_PIPE_RUNNING);
+ if (ret < 0)
+ goto out;
+ pipeline->state = SOF_IPC4_PIPE_RUNNING;
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_STOP:
+ /*
+ * STOP/SUSPEND trigger is invoked only once when all users of this pipeline have
+ * been stopped. So, clear the started_count so that the pipeline can be reset
+ */
+ swidget->spipe->started_count = 0;
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ break;
+ default:
+ dev_err(sdev->dev, "unknown trigger command %d\n", cmd);
+ ret = -EINVAL;
+ break;
+ }
+out:
+ mutex_unlock(&ipc4_data->pipeline_state_mutex);
+ return ret;
+}
+
+static const struct hda_dai_widget_dma_ops hda_ipc4_dma_ops = {
+ .get_hext_stream = hda_ipc4_get_hext_stream,
+ .assign_hext_stream = hda_assign_hext_stream,
+ .release_hext_stream = hda_release_hext_stream,
+ .setup_hext_stream = hda_setup_hext_stream,
+ .reset_hext_stream = hda_reset_hext_stream,
+ .pre_trigger = hda_ipc4_pre_trigger,
+ .trigger = hda_trigger,
+ .post_trigger = hda_ipc4_post_trigger,
+ .codec_dai_set_stream = hda_codec_dai_set_stream,
+ .calc_stream_format = hda_calc_stream_format,
+ .get_hlink = hda_get_hlink,
+};
+
+static const struct hda_dai_widget_dma_ops ssp_ipc4_dma_ops = {
+ .get_hext_stream = hda_ipc4_get_hext_stream,
+ .assign_hext_stream = hda_assign_hext_stream,
+ .release_hext_stream = hda_release_hext_stream,
+ .setup_hext_stream = hda_setup_hext_stream,
+ .reset_hext_stream = hda_reset_hext_stream,
+ .pre_trigger = hda_ipc4_pre_trigger,
+ .trigger = hda_trigger,
+ .post_trigger = hda_ipc4_post_trigger,
+ .calc_stream_format = generic_calc_stream_format,
+ .get_hlink = ssp_get_hlink,
+};
+
+static const struct hda_dai_widget_dma_ops dmic_ipc4_dma_ops = {
+ .get_hext_stream = hda_ipc4_get_hext_stream,
+ .assign_hext_stream = hda_assign_hext_stream,
+ .release_hext_stream = hda_release_hext_stream,
+ .setup_hext_stream = hda_setup_hext_stream,
+ .reset_hext_stream = hda_reset_hext_stream,
+ .pre_trigger = hda_ipc4_pre_trigger,
+ .trigger = hda_trigger,
+ .post_trigger = hda_ipc4_post_trigger,
+ .calc_stream_format = dmic_calc_stream_format,
+ .get_hlink = dmic_get_hlink,
+};
+
+static const struct hda_dai_widget_dma_ops sdw_ipc4_dma_ops = {
+ .get_hext_stream = hda_ipc4_get_hext_stream,
+ .assign_hext_stream = hda_assign_hext_stream,
+ .release_hext_stream = hda_release_hext_stream,
+ .setup_hext_stream = hda_setup_hext_stream,
+ .reset_hext_stream = hda_reset_hext_stream,
+ .pre_trigger = hda_ipc4_pre_trigger,
+ .trigger = hda_trigger,
+ .post_trigger = hda_ipc4_post_trigger,
+ .calc_stream_format = generic_calc_stream_format,
+ .get_hlink = sdw_get_hlink,
+};
+
+static const struct hda_dai_widget_dma_ops hda_ipc4_chain_dma_ops = {
+ .get_hext_stream = hda_get_hext_stream,
+ .assign_hext_stream = hda_assign_hext_stream,
+ .release_hext_stream = hda_release_hext_stream,
+ .setup_hext_stream = hda_setup_hext_stream,
+ .reset_hext_stream = hda_reset_hext_stream,
+ .trigger = hda_trigger,
+ .codec_dai_set_stream = hda_codec_dai_set_stream,
+ .calc_stream_format = hda_calc_stream_format,
+ .get_hlink = hda_get_hlink,
+};
+
+static const struct hda_dai_widget_dma_ops sdw_ipc4_chain_dma_ops = {
+ .get_hext_stream = hda_get_hext_stream,
+ .assign_hext_stream = hda_assign_hext_stream,
+ .release_hext_stream = hda_release_hext_stream,
+ .setup_hext_stream = hda_setup_hext_stream,
+ .reset_hext_stream = hda_reset_hext_stream,
+ .trigger = hda_trigger,
+ .calc_stream_format = generic_calc_stream_format,
+ .get_hlink = sdw_get_hlink,
+};
+
+static int hda_ipc3_post_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
+ struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, substream->stream);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_STOP:
+ {
+ struct snd_sof_dai_config_data data = { 0 };
+ int ret;
+
+ data.dai_data = DMA_CHAN_INVALID;
+ ret = hda_dai_config(w, SOF_DAI_CONFIG_FLAGS_HW_FREE, &data);
+ if (ret < 0)
+ return ret;
+
+ break;
+ }
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ return hda_dai_config(w, SOF_DAI_CONFIG_FLAGS_PAUSE, NULL);
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static const struct hda_dai_widget_dma_ops hda_ipc3_dma_ops = {
+ .get_hext_stream = hda_get_hext_stream,
+ .assign_hext_stream = hda_assign_hext_stream,
+ .release_hext_stream = hda_release_hext_stream,
+ .setup_hext_stream = hda_setup_hext_stream,
+ .reset_hext_stream = hda_reset_hext_stream,
+ .trigger = hda_trigger,
+ .post_trigger = hda_ipc3_post_trigger,
+ .codec_dai_set_stream = hda_codec_dai_set_stream,
+ .calc_stream_format = hda_calc_stream_format,
+ .get_hlink = hda_get_hlink,
+};
+
+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,
+ .codec_dai_set_stream = hda_codec_dai_set_stream,
+ .calc_stream_format = hda_calc_stream_format,
+ .get_hlink = hda_get_hlink,
+};
+
+static const struct hda_dai_widget_dma_ops sdw_dspless_dma_ops = {
+ .get_hext_stream = hda_dspless_get_hext_stream,
+ .setup_hext_stream = hda_dspless_setup_hext_stream,
+ .calc_stream_format = generic_calc_stream_format,
+ .get_hlink = sdw_get_hlink,
+};
+
+#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_LINK)
+ struct snd_sof_dai *sdai;
+ const struct sof_intel_dsp_desc *chip;
+
+ chip = get_chip_info(sdev->pdata);
+ sdai = swidget->private;
+
+ if (sdev->dspless_mode_selected) {
+ switch (sdai->type) {
+ case SOF_DAI_INTEL_HDA:
+ return &hda_dspless_dma_ops;
+ case SOF_DAI_INTEL_ALH:
+ if (chip->hw_ip_version < SOF_INTEL_ACE_2_0)
+ return NULL;
+ return &sdw_dspless_dma_ops;
+ default:
+ return NULL;
+ }
+ }
+
+ switch (sdev->pdata->ipc_type) {
+ case SOF_IPC_TYPE_3:
+ {
+ struct sof_dai_private_data *private = sdai->private;
+
+ if (private->dai_config->type == SOF_DAI_INTEL_HDA)
+ return &hda_ipc3_dma_ops;
+ break;
+ }
+ case SOF_IPC_TYPE_4:
+ {
+ struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget;
+ struct sof_ipc4_pipeline *pipeline = pipe_widget->private;
+
+ switch (sdai->type) {
+ case SOF_DAI_INTEL_HDA:
+ if (pipeline->use_chain_dma)
+ return &hda_ipc4_chain_dma_ops;
+
+ return &hda_ipc4_dma_ops;
+ case SOF_DAI_INTEL_SSP:
+ if (chip->hw_ip_version < SOF_INTEL_ACE_2_0)
+ return NULL;
+ return &ssp_ipc4_dma_ops;
+ case SOF_DAI_INTEL_DMIC:
+ if (chip->hw_ip_version < SOF_INTEL_ACE_2_0)
+ return NULL;
+ return &dmic_ipc4_dma_ops;
+ case SOF_DAI_INTEL_ALH:
+ if (chip->hw_ip_version < SOF_INTEL_ACE_2_0)
+ return NULL;
+ if (pipeline->use_chain_dma)
+ return &sdw_ipc4_chain_dma_ops;
+ return &sdw_ipc4_dma_ops;
+
+ default:
+ break;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+#endif
+ return NULL;
+}
diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c
index c6cb8c212eca..883d0d3bae9e 100644
--- a/sound/soc/sof/intel/hda-dai.c
+++ b/sound/soc/sof/intel/hda-dai.c
@@ -3,416 +3,772 @@
// 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) 2018 Intel Corporation. All rights reserved.
+// Copyright(c) 2018 Intel Corporation
//
// Authors: Keyon Jie <yang.jie@linux.intel.com>
//
#include <sound/pcm_params.h>
#include <sound/hdaudio_ext.h>
+#include <sound/hda-mlink.h>
+#include <sound/hda_register.h>
+#include <sound/intel-nhlt.h>
+#include <sound/sof/ipc4/header.h>
+#include <uapi/sound/sof/header.h>
+#include "../ipc4-priv.h"
+#include "../ipc4-topology.h"
#include "../sof-priv.h"
#include "../sof-audio.h"
#include "hda.h"
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
-
-struct hda_pipe_params {
- u8 host_dma_id;
- u8 link_dma_id;
- u32 ch;
- u32 s_freq;
- u32 s_fmt;
- u8 linktype;
- snd_pcm_format_t format;
- int link_index;
- int stream;
- unsigned int host_bps;
- unsigned int link_bps;
-};
-
/*
- * This function checks if the host dma channel corresponding
- * to the link DMA stream_tag argument is assigned to one
- * of the FEs connected to the BE DAI.
+ * The default method is to fetch NHLT from BIOS. With this parameter set
+ * it is possible to override that with NHLT in the SOF topology manifest.
*/
-static bool hda_check_fes(struct snd_soc_pcm_runtime *rtd,
- int dir, int stream_tag)
+static bool hda_use_tplg_nhlt;
+module_param_named(sof_use_tplg_nhlt, hda_use_tplg_nhlt, bool, 0444);
+MODULE_PARM_DESC(sof_use_tplg_nhlt, "SOF topology nhlt override");
+
+int hda_dai_config(struct snd_soc_dapm_widget *w, unsigned int flags,
+ struct snd_sof_dai_config_data *data)
{
- struct snd_pcm_substream *fe_substream;
- struct hdac_stream *fe_hstream;
- struct snd_soc_dpcm *dpcm;
+ struct snd_sof_widget *swidget = w->dobj.private;
+ const struct sof_ipc_tplg_ops *tplg_ops;
+ struct snd_sof_dev *sdev;
+ int ret;
- for_each_dpcm_fe(rtd, dir, dpcm) {
- fe_substream = snd_soc_dpcm_get_substream(dpcm->fe, dir);
- fe_hstream = fe_substream->runtime->private_data;
- if (fe_hstream->stream_tag == stream_tag)
- return true;
+ if (!swidget)
+ return 0;
+
+ sdev = widget_to_sdev(w);
+ 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) {
+ dev_err(sdev->dev, "DAI config with flags %x failed for widget %s\n",
+ flags, w->name);
+ return ret;
+ }
}
- return false;
+ return 0;
}
+EXPORT_SYMBOL_NS(hda_dai_config, "SND_SOC_SOF_INTEL_HDA_COMMON");
-static struct hdac_ext_stream *
- hda_link_stream_assign(struct hdac_bus *bus,
- struct snd_pcm_substream *substream)
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_LINK)
+
+static struct snd_sof_dev *dai_to_sdev(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *cpu_dai)
{
- struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
- struct sof_intel_hda_stream *hda_stream;
- struct hdac_ext_stream *res = NULL;
- struct hdac_stream *stream = NULL;
+ struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, substream->stream);
+
+ return widget_to_sdev(w);
+}
+
+static const struct hda_dai_widget_dma_ops *
+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_widget *swidget = w->dobj.private;
+ struct snd_sof_dev *sdev;
+ struct snd_sof_dai *sdai;
- int stream_dir = substream->stream;
+ sdev = widget_to_sdev(w);
- if (!bus->ppcap) {
- dev_err(bus->dev, "stream type not supported\n");
+ if (!swidget) {
+ dev_err(sdev->dev, "%s: swidget is NULL\n", __func__);
return NULL;
}
- list_for_each_entry(stream, &bus->stream_list, list) {
- struct hdac_ext_stream *hstream =
- stream_to_hdac_ext_stream(stream);
- if (stream->direction != substream->stream)
- continue;
-
- hda_stream = hstream_to_sof_hda_stream(hstream);
-
- /* check if link is available */
- if (!hstream->link_locked) {
- if (stream->opened) {
- /*
- * check if the stream tag matches the stream
- * tag of one of the connected FEs
- */
- if (hda_check_fes(rtd, stream_dir,
- stream->stream_tag)) {
- res = hstream;
- break;
- }
- } else {
- res = hstream;
-
- /*
- * This must be a hostless stream.
- * So reserve the host DMA channel.
- */
- hda_stream->host_reserved = 1;
- break;
- }
- }
+ if (sdev->dspless_mode_selected)
+ return hda_select_dai_widget_ops(sdev, swidget);
+
+ 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;
+
+ /* check if mandatory ops are set */
+ if (!ops || !ops->get_hext_stream)
+ return NULL;
+
+ sdai->platform_private = ops;
}
- if (res) {
+ return sdai->platform_private;
+}
+
+static int
+hda_link_dma_cleanup(struct snd_pcm_substream *substream,
+ struct hdac_ext_stream *hext_stream,
+ struct snd_soc_dai *cpu_dai, bool release)
+{
+ const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, cpu_dai);
+ struct sof_intel_hda_stream *hda_stream;
+ struct hdac_ext_link *hlink;
+ struct snd_sof_dev *sdev;
+ int stream_tag;
+
+ if (!ops) {
+ dev_err(cpu_dai->dev, "DAI widget ops not set\n");
+ return -EINVAL;
+ }
+
+ sdev = dai_to_sdev(substream, cpu_dai);
+
+ hlink = ops->get_hlink(sdev, substream);
+ if (!hlink)
+ return -EINVAL;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ stream_tag = hdac_stream(hext_stream)->stream_tag;
+ snd_hdac_ext_bus_link_clear_stream_id(hlink, stream_tag);
+ }
+
+ if (!release) {
/*
- * Decouple host and link DMA. The decoupled flag
- * is updated in snd_hdac_ext_stream_decouple().
+ * Force stream reconfiguration without releasing the channel on
+ * subsequent stream restart (without free), including LinkDMA
+ * reset.
+ * The stream is released via hda_dai_hw_free()
*/
- if (!res->decoupled)
- snd_hdac_ext_stream_decouple(bus, res, true);
- spin_lock_irq(&bus->reg_lock);
- res->link_locked = 1;
- res->link_substream = substream;
- spin_unlock_irq(&bus->reg_lock);
+ hext_stream->link_prepared = 0;
+ return 0;
}
- return res;
+ if (ops->release_hext_stream)
+ ops->release_hext_stream(sdev, cpu_dai, substream);
+
+ hext_stream->link_prepared = 0;
+
+ /* free the host DMA channel reserved by hostless streams */
+ hda_stream = hstream_to_sof_hda_stream(hext_stream);
+ hda_stream->host_reserved = 0;
+
+ return 0;
}
-static int hda_link_dma_params(struct hdac_ext_stream *stream,
- struct hda_pipe_params *params)
+static int hda_link_dma_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *cpu_dai)
{
- struct hdac_stream *hstream = &stream->hstream;
- unsigned char stream_tag = hstream->stream_tag;
- struct hdac_bus *bus = hstream->bus;
- struct hdac_ext_link *link;
- unsigned int format_val;
+ const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, cpu_dai);
+ struct hdac_ext_stream *hext_stream;
+ struct hdac_stream *hstream;
+ struct hdac_ext_link *hlink;
+ struct snd_sof_dev *sdev;
+ int stream_tag;
- snd_hdac_ext_stream_decouple(bus, stream, true);
- snd_hdac_ext_link_stream_reset(stream);
+ if (!ops) {
+ dev_err(cpu_dai->dev, "DAI widget ops not set\n");
+ return -EINVAL;
+ }
- format_val = snd_hdac_calc_stream_format(params->s_freq, params->ch,
- params->format,
- params->link_bps, 0);
+ sdev = dai_to_sdev(substream, cpu_dai);
- dev_dbg(bus->dev, "format_val=%d, rate=%d, ch=%d, format=%d\n",
- format_val, params->s_freq, params->ch, params->format);
+ hlink = ops->get_hlink(sdev, substream);
+ if (!hlink)
+ return -EINVAL;
- snd_hdac_ext_link_stream_setup(stream, format_val);
+ hext_stream = ops->get_hext_stream(sdev, cpu_dai, substream);
- if (stream->hstream.direction == SNDRV_PCM_STREAM_PLAYBACK) {
- list_for_each_entry(link, &bus->hlink_list, list) {
- if (link->index == params->link_index)
- snd_hdac_ext_link_set_stream_id(link,
- stream_tag);
- }
+ if (!hext_stream) {
+ if (ops->assign_hext_stream)
+ hext_stream = ops->assign_hext_stream(sdev, cpu_dai, substream);
}
- stream->link_prepared = 1;
+ if (!hext_stream)
+ return -EBUSY;
+
+ hstream = &hext_stream->hstream;
+ stream_tag = hstream->stream_tag;
+
+ if (hext_stream->hstream.direction == SNDRV_PCM_STREAM_PLAYBACK)
+ snd_hdac_ext_bus_link_set_stream_id(hlink, stream_tag);
+
+ /* set the hdac_stream in the codec dai */
+ if (ops->codec_dai_set_stream)
+ ops->codec_dai_set_stream(sdev, substream, hstream);
+
+ if (ops->reset_hext_stream)
+ ops->reset_hext_stream(sdev, hext_stream);
+
+ if (ops->calc_stream_format && ops->setup_hext_stream) {
+ unsigned int format_val = ops->calc_stream_format(sdev, substream, params);
+
+ ops->setup_hext_stream(sdev, hext_stream, format_val);
+ }
+
+ hext_stream->link_prepared = 1;
return 0;
}
-/* Send DAI_CONFIG IPC to the DAI that matches the dai_name and direction */
-static int hda_link_config_ipc(struct sof_intel_hda_stream *hda_stream,
- const char *dai_name, int channel, int dir)
+static int __maybe_unused hda_dai_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *cpu_dai)
{
- struct sof_ipc_dai_config *config;
- struct snd_sof_dai *sof_dai;
- struct sof_ipc_reply reply;
- int ret = 0;
+ const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, cpu_dai);
+ struct hdac_ext_stream *hext_stream;
+ struct snd_sof_dev *sdev = dai_to_sdev(substream, cpu_dai);
- list_for_each_entry(sof_dai, &hda_stream->sdev->dai_list, list) {
- if (!sof_dai->cpu_dai_name)
- continue;
+ if (!ops) {
+ dev_err(cpu_dai->dev, "DAI widget ops not set\n");
+ return -EINVAL;
+ }
- if (!strcmp(dai_name, sof_dai->cpu_dai_name) &&
- dir == sof_dai->comp_dai.direction) {
- config = sof_dai->dai_config;
+ hext_stream = ops->get_hext_stream(sdev, cpu_dai, substream);
+ if (!hext_stream)
+ return 0;
- if (!config) {
- dev_err(hda_stream->sdev->dev,
- "error: no config for DAI %s\n",
- sof_dai->name);
- return -EINVAL;
- }
+ return hda_link_dma_cleanup(substream, hext_stream, cpu_dai, true);
+}
+
+static int __maybe_unused hda_dai_hw_params_data(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai,
+ struct snd_sof_dai_config_data *data,
+ unsigned int flags)
+{
+ struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(dai, substream->stream);
+ const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, dai);
+ struct hdac_ext_stream *hext_stream;
+ struct snd_sof_dev *sdev = widget_to_sdev(w);
+ int ret;
- /* update config with stream tag */
- config->hda.link_dma_ch = channel;
+ if (!ops) {
+ dev_err(sdev->dev, "DAI widget ops not set\n");
+ return -EINVAL;
+ }
- /* send IPC */
- ret = sof_ipc_tx_message(hda_stream->sdev->ipc,
- config->hdr.cmd,
- config,
- config->hdr.size,
- &reply, sizeof(reply));
+ hext_stream = ops->get_hext_stream(sdev, dai, substream);
+ if (hext_stream && hext_stream->link_prepared)
+ return 0;
- if (ret < 0)
- dev_err(hda_stream->sdev->dev,
- "error: failed to set dai config for %s\n",
- sof_dai->name);
+ ret = hda_link_dma_hw_params(substream, params, dai);
+ if (ret < 0)
+ return ret;
+
+ hext_stream = ops->get_hext_stream(sdev, dai, substream);
+
+ flags |= SOF_DAI_CONFIG_FLAGS_2_STEP_STOP << SOF_DAI_CONFIG_FLAGS_QUIRK_SHIFT;
+ data->dai_data = hdac_stream(hext_stream)->stream_tag - 1;
+
+ return hda_dai_config(w, flags, data);
+}
+
+static int __maybe_unused hda_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_sof_dai_config_data data = { 0 };
+ unsigned int flags = SOF_DAI_CONFIG_FLAGS_HW_PARAMS;
+
+ return hda_dai_hw_params_data(substream, params, dai, &data, flags);
+}
+
+/*
+ * In contrast to IPC3, the dai trigger in IPC4 mixes pipeline state changes
+ * (over IPC channel) and DMA state change (direct host register changes).
+ */
+static int __maybe_unused hda_dai_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, dai);
+ struct hdac_ext_stream *hext_stream;
+ struct snd_sof_dev *sdev;
+ int ret;
+
+ if (!ops) {
+ dev_err(dai->dev, "DAI widget ops not set\n");
+ return -EINVAL;
+ }
+
+ dev_dbg(dai->dev, "cmd=%d dai %s direction %d\n", cmd,
+ dai->name, substream->stream);
+
+ sdev = dai_to_sdev(substream, dai);
+
+ hext_stream = ops->get_hext_stream(sdev, dai, substream);
+ if (!hext_stream)
+ return -EINVAL;
+
+ if (ops->pre_trigger) {
+ ret = ops->pre_trigger(sdev, dai, substream, cmd);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (ops->trigger) {
+ ret = ops->trigger(sdev, dai, substream, cmd);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (ops->post_trigger) {
+ ret = ops->post_trigger(sdev, dai, substream, cmd);
+ if (ret < 0)
+ return ret;
+ }
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ ret = hda_link_dma_cleanup(substream, hext_stream, dai,
+ cmd != SNDRV_PCM_TRIGGER_STOP);
+ if (ret < 0) {
+ dev_err(sdev->dev, "%s: failed to clean up link DMA\n", __func__);
return ret;
}
+ break;
+ default:
+ break;
}
- return -EINVAL;
+ return 0;
}
-static int hda_link_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *dai)
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC)
+
+static int hda_dai_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
{
- struct hdac_stream *hstream = substream->runtime->private_data;
- struct hdac_bus *bus = hstream->bus;
- struct hdac_ext_stream *link_dev;
- struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
- struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
- struct sof_intel_hda_stream *hda_stream;
- struct hda_pipe_params p_params = {0};
- struct hdac_ext_link *link;
- int stream_tag;
- int ret;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ int stream = substream->stream;
+
+ return hda_dai_hw_params(substream, &rtd->dpcm[stream].hw_params, dai);
+}
+
+static const struct snd_soc_dai_ops hda_dai_ops = {
+ .hw_params = hda_dai_hw_params,
+ .hw_free = hda_dai_hw_free,
+ .trigger = hda_dai_trigger,
+ .prepare = hda_dai_prepare,
+};
+
+#endif
+
+static struct sof_ipc4_copier *widget_to_copier(struct snd_soc_dapm_widget *w)
+{
+ struct snd_sof_widget *swidget = w->dobj.private;
+ struct snd_sof_dai *sdai = swidget->private;
+ struct sof_ipc4_copier *ipc4_copier = (struct sof_ipc4_copier *)sdai->private;
+
+ return ipc4_copier;
+}
- /* get stored dma data if resuming from system suspend */
- link_dev = snd_soc_dai_get_dma_data(dai, substream);
- if (!link_dev) {
- link_dev = hda_link_stream_assign(bus, substream);
- if (!link_dev)
- return -EBUSY;
+static int non_hda_dai_hw_params_data(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *cpu_dai,
+ struct snd_sof_dai_config_data *data,
+ unsigned int flags)
+{
+ struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, substream->stream);
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct sof_ipc4_dma_config_tlv *dma_config_tlv;
+ const struct hda_dai_widget_dma_ops *ops;
+ struct sof_ipc4_dma_config *dma_config;
+ struct sof_ipc4_copier *ipc4_copier;
+ struct hdac_ext_stream *hext_stream;
+ struct hdac_stream *hstream;
+ struct snd_sof_dev *sdev;
+ struct snd_soc_dai *dai;
+ int cpu_dai_id;
+ int stream_id;
+ int ret;
- snd_soc_dai_set_dma_data(dai, substream, (void *)link_dev);
+ ops = hda_dai_get_ops(substream, cpu_dai);
+ if (!ops) {
+ dev_err(cpu_dai->dev, "DAI widget ops not set\n");
+ return -EINVAL;
}
- stream_tag = hdac_stream(link_dev)->stream_tag;
+ sdev = widget_to_sdev(w);
+ hext_stream = ops->get_hext_stream(sdev, cpu_dai, substream);
- hda_stream = hstream_to_sof_hda_stream(link_dev);
+ /* nothing more to do if the link is already prepared */
+ if (hext_stream && hext_stream->link_prepared)
+ return 0;
- /* update the DSP with the new tag */
- ret = hda_link_config_ipc(hda_stream, dai->name, stream_tag - 1,
- substream->stream);
- if (ret < 0)
+ /* use HDaudio stream handling */
+ ret = hda_dai_hw_params_data(substream, params, cpu_dai, data, flags);
+ if (ret < 0) {
+ dev_err(cpu_dai->dev, "%s: hda_dai_hw_params_data failed: %d\n", __func__, ret);
return ret;
+ }
- link = snd_hdac_ext_bus_get_link(bus, codec_dai->component->name);
- if (!link)
- return -EINVAL;
+ if (sdev->dspless_mode_selected)
+ return 0;
+
+ /* get stream_id */
+ hext_stream = ops->get_hext_stream(sdev, cpu_dai, substream);
+
+ if (!hext_stream) {
+ dev_err(cpu_dai->dev, "%s: no hext_stream found\n", __func__);
+ return -ENODEV;
+ }
+
+ hstream = &hext_stream->hstream;
+ stream_id = hstream->stream_tag;
+
+ if (!stream_id) {
+ dev_err(cpu_dai->dev, "%s: no stream_id allocated\n", __func__);
+ return -ENODEV;
+ }
+
+ /* configure TLV */
+ ipc4_copier = widget_to_copier(w);
+
+ for_each_rtd_cpu_dais(rtd, cpu_dai_id, dai) {
+ if (dai == cpu_dai)
+ break;
+ }
+
+ dma_config_tlv = &ipc4_copier->dma_config_tlv[cpu_dai_id];
+ dma_config_tlv->type = SOF_IPC4_GTW_DMA_CONFIG_ID;
+ /* dma_config_priv_size is zero */
+ dma_config_tlv->length = sizeof(dma_config_tlv->dma_config);
+
+ dma_config = &dma_config_tlv->dma_config;
+
+ dma_config->dma_method = SOF_IPC4_DMA_METHOD_HDA;
+ dma_config->pre_allocated_by_host = 1;
+ dma_config->dma_channel_id = stream_id - 1;
+ dma_config->stream_id = stream_id;
+ /*
+ * Currently we use a DMA for each device in ALH blob. The device will
+ * be copied in sof_ipc4_prepare_copier_module.
+ */
+ dma_config->dma_stream_channel_map.device_count = 1;
+ dma_config->dma_priv_config_size = 0;
+
+ return 0;
+}
- /* set the stream tag in the codec dai dma params */
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- snd_soc_dai_set_tdm_slot(codec_dai, stream_tag, 0, 0, 0);
- else
- snd_soc_dai_set_tdm_slot(codec_dai, 0, stream_tag, 0, 0);
-
- p_params.s_fmt = snd_pcm_format_width(params_format(params));
- p_params.ch = params_channels(params);
- p_params.s_freq = params_rate(params);
- p_params.stream = substream->stream;
- p_params.link_dma_id = stream_tag - 1;
- p_params.link_index = link->index;
- p_params.format = params_format(params);
-
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- p_params.link_bps = codec_dai->driver->playback.sig_bits;
- else
- p_params.link_bps = codec_dai->driver->capture.sig_bits;
-
- return hda_link_dma_params(link_dev, &p_params);
+static int non_hda_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct snd_sof_dai_config_data data = { 0 };
+ unsigned int flags = SOF_DAI_CONFIG_FLAGS_HW_PARAMS;
+
+ return non_hda_dai_hw_params_data(substream, params, cpu_dai, &data, flags);
}
-static int hda_link_pcm_prepare(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
+static int non_hda_dai_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *cpu_dai)
{
- struct hdac_ext_stream *link_dev =
- snd_soc_dai_get_dma_data(dai, substream);
- struct snd_sof_dev *sdev =
- snd_soc_component_get_drvdata(dai->component);
- struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
int stream = substream->stream;
- if (link_dev->link_prepared)
- return 0;
+ return non_hda_dai_hw_params(substream, &rtd->dpcm[stream].hw_params, cpu_dai);
+}
- dev_dbg(sdev->dev, "hda: prepare stream dir %d\n", substream->stream);
+static const struct snd_soc_dai_ops ssp_dai_ops = {
+ .hw_params = non_hda_dai_hw_params,
+ .hw_free = hda_dai_hw_free,
+ .trigger = hda_dai_trigger,
+ .prepare = non_hda_dai_prepare,
+};
- return hda_link_hw_params(substream, &rtd->dpcm[stream].hw_params,
- dai);
-}
+static const struct snd_soc_dai_ops dmic_dai_ops = {
+ .hw_params = non_hda_dai_hw_params,
+ .hw_free = hda_dai_hw_free,
+ .trigger = hda_dai_trigger,
+ .prepare = non_hda_dai_prepare,
+};
-static int hda_link_pcm_trigger(struct snd_pcm_substream *substream,
- int cmd, struct snd_soc_dai *dai)
+int sdw_hda_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *cpu_dai,
+ int link_id,
+ int intel_alh_id)
{
- struct hdac_ext_stream *link_dev =
- snd_soc_dai_get_dma_data(dai, substream);
- struct sof_intel_hda_stream *hda_stream;
- struct snd_soc_pcm_runtime *rtd;
- struct hdac_ext_link *link;
- struct hdac_stream *hstream;
- struct hdac_bus *bus;
- int stream_tag;
+ struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, substream->stream);
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct sof_ipc4_dma_config_tlv *dma_config_tlv;
+ struct snd_sof_dai_config_data data = { 0 };
+ unsigned int flags = SOF_DAI_CONFIG_FLAGS_HW_PARAMS;
+ const struct hda_dai_widget_dma_ops *ops;
+ struct sof_ipc4_dma_config *dma_config;
+ struct sof_ipc4_copier *ipc4_copier;
+ struct hdac_ext_stream *hext_stream;
+ struct snd_soc_dai *dai;
+ struct snd_sof_dev *sdev;
+ bool cpu_dai_found = false;
+ int cpu_dai_id;
+ int ch_mask;
int ret;
+ int i;
- hstream = substream->runtime->private_data;
- bus = hstream->bus;
- rtd = asoc_substream_to_rtd(substream);
+ if (!w) {
+ dev_err(cpu_dai->dev, "%s widget not found, check amp link num in the topology\n",
+ cpu_dai->name);
+ return -EINVAL;
+ }
- link = snd_hdac_ext_bus_get_link(bus, asoc_rtd_to_codec(rtd, 0)->component->name);
- if (!link)
+ ops = hda_dai_get_ops(substream, cpu_dai);
+ if (!ops) {
+ dev_err(cpu_dai->dev, "DAI widget ops not set\n");
return -EINVAL;
+ }
- hda_stream = hstream_to_sof_hda_stream(link_dev);
+ sdev = widget_to_sdev(w);
+ hext_stream = ops->get_hext_stream(sdev, cpu_dai, substream);
- dev_dbg(dai->dev, "In %s cmd=%d\n", __func__, cmd);
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_RESUME:
- /* set up hw_params */
- ret = hda_link_pcm_prepare(substream, dai);
- if (ret < 0) {
- dev_err(dai->dev,
- "error: setting up hw_params during resume\n");
- return ret;
+ /* nothing more to do if the link is already prepared */
+ if (hext_stream && hext_stream->link_prepared)
+ return 0;
+
+ /*
+ * reset the PCMSyCM registers to handle a prepare callback when the PCM is restarted
+ * due to xruns or after a call to snd_pcm_drain/drop()
+ */
+ ret = hdac_bus_eml_sdw_map_stream_ch(sof_to_bus(sdev), link_id, cpu_dai->id,
+ 0, 0, substream->stream);
+ if (ret < 0) {
+ dev_err(cpu_dai->dev, "%s: hdac_bus_eml_sdw_map_stream_ch failed %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ data.dai_index = (link_id << 8) | cpu_dai->id;
+ data.dai_node_id = intel_alh_id;
+ ret = non_hda_dai_hw_params_data(substream, params, cpu_dai, &data, flags);
+ if (ret < 0) {
+ dev_err(cpu_dai->dev, "%s: non_hda_dai_hw_params failed %d\n", __func__, ret);
+ return ret;
+ }
+
+ hext_stream = ops->get_hext_stream(sdev, cpu_dai, substream);
+ if (!hext_stream)
+ return -ENODEV;
+
+ /*
+ * in the case of SoundWire we need to program the PCMSyCM registers. In case
+ * of aggregated devices, we need to define the channel mask for each sublink
+ * by reconstructing the split done in soc-pcm.c
+ */
+ for_each_rtd_cpu_dais(rtd, cpu_dai_id, dai) {
+ if (dai == cpu_dai) {
+ cpu_dai_found = true;
+ break;
}
+ }
- fallthrough;
- case SNDRV_PCM_TRIGGER_START:
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- snd_hdac_ext_link_stream_start(link_dev);
- break;
- case SNDRV_PCM_TRIGGER_SUSPEND:
- case SNDRV_PCM_TRIGGER_STOP:
- /*
- * clear link DMA channel. It will be assigned when
- * hw_params is set up again after resume.
- */
- ret = hda_link_config_ipc(hda_stream, dai->name,
- DMA_CHAN_INVALID, substream->stream);
- if (ret < 0)
- return ret;
+ if (!cpu_dai_found)
+ return -ENODEV;
+
+ ch_mask = GENMASK(params_channels(params) - 1, 0);
+
+ ret = hdac_bus_eml_sdw_map_stream_ch(sof_to_bus(sdev), link_id, cpu_dai->id,
+ ch_mask,
+ hdac_stream(hext_stream)->stream_tag,
+ substream->stream);
+ if (ret < 0) {
+ dev_err(cpu_dai->dev, "%s: hdac_bus_eml_sdw_map_stream_ch failed %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ if (sdev->dspless_mode_selected)
+ return 0;
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- stream_tag = hdac_stream(link_dev)->stream_tag;
- snd_hdac_ext_link_clear_stream_id(link, stream_tag);
+ ipc4_copier = widget_to_copier(w);
+ dma_config_tlv = &ipc4_copier->dma_config_tlv[cpu_dai_id];
+ dma_config = &dma_config_tlv->dma_config;
+ dma_config->dma_stream_channel_map.mapping[0].device = data.dai_index;
+ dma_config->dma_stream_channel_map.mapping[0].channel_mask = ch_mask;
+
+ /*
+ * copy the dma_config_tlv to all ipc4_copier in the same link. Because only one copier
+ * will be handled in sof_ipc4_prepare_copier_module.
+ */
+ for_each_rtd_cpu_dais(rtd, i, dai) {
+ w = snd_soc_dai_get_widget(dai, substream->stream);
+ if (!w) {
+ dev_err(cpu_dai->dev,
+ "%s widget not found, check amp link num in the topology\n",
+ dai->name);
+ return -EINVAL;
}
+ ipc4_copier = widget_to_copier(w);
+ memcpy(&ipc4_copier->dma_config_tlv[cpu_dai_id], dma_config_tlv,
+ sizeof(*dma_config_tlv));
+ }
+ return 0;
+}
+EXPORT_SYMBOL_NS(sdw_hda_dai_hw_params, "SND_SOC_SOF_INTEL_HDA_COMMON");
- link_dev->link_prepared = 0;
+int sdw_hda_dai_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *cpu_dai,
+ int link_id)
+{
+ struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, substream->stream);
+ struct snd_sof_dev *sdev;
+ int ret;
- fallthrough;
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- snd_hdac_ext_link_stream_clear(link_dev);
- break;
- default:
- return -EINVAL;
+ ret = hda_dai_hw_free(substream, cpu_dai);
+ if (ret < 0) {
+ dev_err(cpu_dai->dev, "%s: non_hda_dai_hw_free failed %d\n", __func__, ret);
+ return ret;
}
+
+ sdev = widget_to_sdev(w);
+
+ /* in the case of SoundWire we need to reset the PCMSyCM registers */
+ ret = hdac_bus_eml_sdw_map_stream_ch(sof_to_bus(sdev), link_id, cpu_dai->id,
+ 0, 0, substream->stream);
+ if (ret < 0) {
+ dev_err(cpu_dai->dev, "%s: hdac_bus_eml_sdw_map_stream_ch failed %d\n",
+ __func__, ret);
+ return ret;
+ }
+
return 0;
}
+EXPORT_SYMBOL_NS(sdw_hda_dai_hw_free, "SND_SOC_SOF_INTEL_HDA_COMMON");
-static int hda_link_hw_free(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
+int sdw_hda_dai_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *cpu_dai)
+{
+ return hda_dai_trigger(substream, cmd, cpu_dai);
+}
+EXPORT_SYMBOL_NS(sdw_hda_dai_trigger, "SND_SOC_SOF_INTEL_HDA_COMMON");
+
+static int hda_dai_suspend(struct hdac_bus *bus)
{
- unsigned int stream_tag;
- struct sof_intel_hda_stream *hda_stream;
- struct hdac_bus *bus;
- struct hdac_ext_link *link;
- struct hdac_stream *hstream;
struct snd_soc_pcm_runtime *rtd;
- struct hdac_ext_stream *link_dev;
+ struct hdac_ext_stream *hext_stream;
+ struct hdac_stream *s;
int ret;
- hstream = substream->runtime->private_data;
- bus = hstream->bus;
- rtd = asoc_substream_to_rtd(substream);
- link_dev = snd_soc_dai_get_dma_data(dai, substream);
+ /* set internal flag for BE */
+ list_for_each_entry(s, &bus->stream_list, list) {
- if (!link_dev) {
- dev_dbg(dai->dev,
- "%s: link_dev is not assigned\n", __func__);
- return -EINVAL;
+ hext_stream = stream_to_hdac_ext_stream(s);
+
+ /*
+ * clear stream. This should already be taken care for running
+ * streams when the SUSPEND trigger is called. But paused
+ * streams do not get suspended, so this needs to be done
+ * explicitly during suspend.
+ */
+ if (hext_stream->link_substream) {
+ const struct hda_dai_widget_dma_ops *ops;
+ struct snd_sof_widget *swidget;
+ struct snd_soc_dapm_widget *w;
+ struct snd_soc_dai *cpu_dai;
+ struct snd_sof_dev *sdev;
+ struct snd_sof_dai *sdai;
+
+ rtd = snd_soc_substream_to_rtd(hext_stream->link_substream);
+ cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ w = snd_soc_dai_get_widget(cpu_dai, hdac_stream(hext_stream)->direction);
+ swidget = w->dobj.private;
+ sdev = widget_to_sdev(w);
+ sdai = swidget->private;
+ ops = sdai->platform_private;
+
+ if (rtd->dpcm[hext_stream->link_substream->stream].state !=
+ SND_SOC_DPCM_STATE_PAUSED)
+ continue;
+
+ /* for consistency with TRIGGER_SUSPEND */
+ if (ops->post_trigger) {
+ ret = ops->post_trigger(sdev, cpu_dai,
+ hext_stream->link_substream,
+ SNDRV_PCM_TRIGGER_SUSPEND);
+ if (ret < 0)
+ return ret;
+ }
+
+ ret = hda_link_dma_cleanup(hext_stream->link_substream,
+ hext_stream, cpu_dai, true);
+ if (ret < 0)
+ return ret;
+ }
}
- hda_stream = hstream_to_sof_hda_stream(link_dev);
+ return 0;
+}
- /* free the link DMA channel in the FW */
- ret = hda_link_config_ipc(hda_stream, dai->name, DMA_CHAN_INVALID,
- substream->stream);
- if (ret < 0)
- return ret;
+static void ssp_set_dai_drv_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *ops)
+{
+ const struct sof_intel_dsp_desc *chip;
+ int i;
- link = snd_hdac_ext_bus_get_link(bus, asoc_rtd_to_codec(rtd, 0)->component->name);
- if (!link)
- return -EINVAL;
+ chip = get_chip_info(sdev->pdata);
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- stream_tag = hdac_stream(link_dev)->stream_tag;
- snd_hdac_ext_link_clear_stream_id(link, stream_tag);
+ if (chip->hw_ip_version >= SOF_INTEL_ACE_2_0) {
+ for (i = 0; i < ops->num_drv; i++) {
+ if (strstr(ops->drv[i].name, "SSP"))
+ ops->drv[i].ops = &ssp_dai_ops;
+ }
}
+}
- snd_soc_dai_set_dma_data(dai, substream, NULL);
- snd_hdac_ext_stream_release(link_dev, HDAC_EXT_STREAM_TYPE_LINK);
- link_dev->link_prepared = 0;
+static void dmic_set_dai_drv_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *ops)
+{
+ const struct sof_intel_dsp_desc *chip;
+ int i;
- /* free the host DMA channel reserved by hostless streams */
- hda_stream->host_reserved = 0;
+ chip = get_chip_info(sdev->pdata);
- return 0;
+ if (chip->hw_ip_version >= SOF_INTEL_ACE_2_0) {
+ for (i = 0; i < ops->num_drv; i++) {
+ if (strstr(ops->drv[i].name, "DMIC"))
+ ops->drv[i].ops = &dmic_dai_ops;
+ }
+ }
}
-static const struct snd_soc_dai_ops hda_link_dai_ops = {
- .hw_params = hda_link_hw_params,
- .hw_free = hda_link_hw_free,
- .trigger = hda_link_pcm_trigger,
- .prepare = hda_link_pcm_prepare,
-};
+#else
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_PROBES)
-#include "../compress.h"
+static inline void ssp_set_dai_drv_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *ops) {}
+static inline void dmic_set_dai_drv_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *ops) {}
-static struct snd_soc_cdai_ops sof_probe_compr_ops = {
- .startup = sof_probe_compr_open,
- .shutdown = sof_probe_compr_free,
- .set_params = sof_probe_compr_set_params,
- .trigger = sof_probe_compr_trigger,
- .pointer = sof_probe_compr_pointer,
-};
+#endif /* CONFIG_SND_SOC_SOF_HDA_LINK */
+void hda_set_dai_drv_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *ops)
+{
+ int i;
+
+ for (i = 0; i < ops->num_drv; i++) {
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC)
+ if (strstr(ops->drv[i].name, "iDisp") ||
+ strstr(ops->drv[i].name, "Analog") ||
+ strstr(ops->drv[i].name, "Digital"))
+ ops->drv[i].ops = &hda_dai_ops;
#endif
-#endif
+ }
+
+ ssp_set_dai_drv_ops(sdev, ops);
+ dmic_set_dai_drv_ops(sdev, ops);
+
+ if (sdev->pdata->ipc_type == SOF_IPC_TYPE_4 && !hda_use_tplg_nhlt) {
+ struct sof_ipc4_fw_data *ipc4_data = sdev->private;
+
+ ipc4_data->nhlt = intel_nhlt_init(sdev->dev);
+ }
+}
+EXPORT_SYMBOL_NS(hda_set_dai_drv_ops, "SND_SOC_SOF_INTEL_HDA_COMMON");
+
+void hda_ops_free(struct snd_sof_dev *sdev)
+{
+ if (sdev->pdata->ipc_type == SOF_IPC_TYPE_4) {
+ struct sof_ipc4_fw_data *ipc4_data = sdev->private;
+
+ if (!hda_use_tplg_nhlt)
+ intel_nhlt_free(ipc4_data->nhlt);
+
+ kfree(sdev->private);
+ sdev->private = NULL;
+ }
+}
+EXPORT_SYMBOL_NS(hda_ops_free, "SND_SOC_SOF_INTEL_HDA_COMMON");
/*
* common dai driver for skl+ platforms.
@@ -500,10 +856,9 @@ struct snd_soc_dai_driver skl_dai[] = {
.channels_max = 4,
},
},
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC)
{
.name = "iDisp1 Pin",
- .ops = &hda_link_dai_ops,
.playback = {
.channels_min = 1,
.channels_max = 8,
@@ -511,7 +866,6 @@ struct snd_soc_dai_driver skl_dai[] = {
},
{
.name = "iDisp2 Pin",
- .ops = &hda_link_dai_ops,
.playback = {
.channels_min = 1,
.channels_max = 8,
@@ -519,7 +873,6 @@ struct snd_soc_dai_driver skl_dai[] = {
},
{
.name = "iDisp3 Pin",
- .ops = &hda_link_dai_ops,
.playback = {
.channels_min = 1,
.channels_max = 8,
@@ -527,7 +880,6 @@ struct snd_soc_dai_driver skl_dai[] = {
},
{
.name = "iDisp4 Pin",
- .ops = &hda_link_dai_ops,
.playback = {
.channels_min = 1,
.channels_max = 8,
@@ -535,7 +887,6 @@ struct snd_soc_dai_driver skl_dai[] = {
},
{
.name = "Analog CPU DAI",
- .ops = &hda_link_dai_ops,
.playback = {
.channels_min = 1,
.channels_max = 16,
@@ -547,7 +898,6 @@ struct snd_soc_dai_driver skl_dai[] = {
},
{
.name = "Digital CPU DAI",
- .ops = &hda_link_dai_ops,
.playback = {
.channels_min = 1,
.channels_max = 16,
@@ -559,7 +909,6 @@ struct snd_soc_dai_driver skl_dai[] = {
},
{
.name = "Alt Analog CPU DAI",
- .ops = &hda_link_dai_ops,
.playback = {
.channels_min = 1,
.channels_max = 16,
@@ -569,20 +918,25 @@ struct snd_soc_dai_driver skl_dai[] = {
.channels_max = 16,
},
},
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_PROBES)
-{
- .name = "Probe Extraction CPU DAI",
- .compress_new = snd_soc_new_compress,
- .cops = &sof_probe_compr_ops,
- .capture = {
- .stream_name = "Probe Extraction",
- .channels_min = 1,
- .channels_max = 8,
- .rates = SNDRV_PCM_RATE_48000,
- .rate_min = 48000,
- .rate_max = 48000,
- },
-},
-#endif
#endif
};
+EXPORT_SYMBOL_NS(skl_dai, "SND_SOC_SOF_INTEL_HDA_COMMON");
+
+int hda_dsp_dais_suspend(struct snd_sof_dev *sdev)
+{
+ /*
+ * In the corner case where a SUSPEND happens during a PAUSE, the ALSA core
+ * does not throw the TRIGGER_SUSPEND. This leaves the DAIs in an unbalanced state.
+ * Since the component suspend is called last, we can trap this corner case
+ * and force the DAIs to release their resources.
+ */
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_LINK)
+ int ret;
+
+ ret = hda_dai_suspend(sof_to_bus(sdev));
+ if (ret < 0)
+ return ret;
+#endif
+
+ return 0;
+}
diff --git a/sound/soc/sof/intel/hda-dsp.c b/sound/soc/sof/intel/hda-dsp.c
index ed4d65a29d3a..f64e8a6a9a33 100644
--- a/sound/soc/sof/intel/hda-dsp.c
+++ b/sound/soc/sof/intel/hda-dsp.c
@@ -3,7 +3,7 @@
// 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) 2018 Intel Corporation. All rights reserved.
+// Copyright(c) 2018 Intel Corporation
//
// Authors: Liam Girdwood <liam.r.girdwood@linux.intel.com>
// Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
@@ -18,11 +18,23 @@
#include <linux/module.h>
#include <sound/hdaudio_ext.h>
#include <sound/hda_register.h>
+#include <sound/hda-mlink.h>
+#include <trace/events/sof_intel.h>
+#include <sound/sof/xtensa.h>
#include "../sof-audio.h"
#include "../ops.h"
#include "hda.h"
+#include "mtl.h"
#include "hda-ipc.h"
+#define EXCEPT_MAX_HDR_SIZE 0x400
+#define HDA_EXT_ROM_STATUS_SIZE 8
+
+struct hda_dsp_msg_code {
+ u32 code;
+ const char *text;
+};
+
static bool hda_enable_trace_D0I3_S0;
#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG)
module_param_named(enable_trace_D0I3_S0, hda_enable_trace_D0I3_S0, bool, 0444);
@@ -30,11 +42,91 @@ MODULE_PARM_DESC(enable_trace_D0I3_S0,
"SOF HDA enable trace when the DSP is in D0I3 in S0");
#endif
+static void hda_get_interfaces(struct snd_sof_dev *sdev, u32 *interface_mask)
+{
+ const struct sof_intel_dsp_desc *chip;
+
+ 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[SOF_DAI_DSP_ACCESS] = BIT(SOF_DAI_INTEL_SSP);
+ break;
+ case SOF_INTEL_CAVS_1_5:
+ case SOF_INTEL_CAVS_1_5_PLUS:
+ interface_mask[SOF_DAI_DSP_ACCESS] =
+ BIT(SOF_DAI_INTEL_SSP) | BIT(SOF_DAI_INTEL_DMIC) | BIT(SOF_DAI_INTEL_HDA);
+ interface_mask[SOF_DAI_HOST_ACCESS] = 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[SOF_DAI_DSP_ACCESS] =
+ BIT(SOF_DAI_INTEL_SSP) | BIT(SOF_DAI_INTEL_DMIC) |
+ BIT(SOF_DAI_INTEL_HDA) | BIT(SOF_DAI_INTEL_ALH);
+ interface_mask[SOF_DAI_HOST_ACCESS] = BIT(SOF_DAI_INTEL_HDA);
+ break;
+ case SOF_INTEL_ACE_2_0:
+ case SOF_INTEL_ACE_3_0:
+ interface_mask[SOF_DAI_DSP_ACCESS] =
+ BIT(SOF_DAI_INTEL_SSP) | BIT(SOF_DAI_INTEL_DMIC) |
+ BIT(SOF_DAI_INTEL_HDA) | BIT(SOF_DAI_INTEL_ALH);
+ /* all interfaces accessible without DSP */
+ interface_mask[SOF_DAI_HOST_ACCESS] =
+ interface_mask[SOF_DAI_DSP_ACCESS];
+ break;
+ default:
+ break;
+ }
+}
+
+u32 hda_get_interface_mask(struct snd_sof_dev *sdev)
+{
+ u32 interface_mask[SOF_DAI_ACCESS_NUM] = { 0 };
+
+ hda_get_interfaces(sdev, interface_mask);
+
+ return interface_mask[sdev->dspless_mode_selected];
+}
+EXPORT_SYMBOL_NS(hda_get_interface_mask, "SND_SOC_SOF_INTEL_HDA_COMMON");
+
+bool hda_is_chain_dma_supported(struct snd_sof_dev *sdev, u32 dai_type)
+{
+ u32 interface_mask[SOF_DAI_ACCESS_NUM] = { 0 };
+ const struct sof_intel_dsp_desc *chip;
+
+ if (sdev->dspless_mode_selected)
+ return false;
+
+ hda_get_interfaces(sdev, interface_mask);
+
+ if (!(interface_mask[SOF_DAI_DSP_ACCESS] & BIT(dai_type)))
+ return false;
+
+ if (dai_type == SOF_DAI_INTEL_HDA)
+ return true;
+
+ switch (dai_type) {
+ case SOF_DAI_INTEL_SSP:
+ case SOF_DAI_INTEL_DMIC:
+ case SOF_DAI_INTEL_ALH:
+ chip = get_chip_info(sdev->pdata);
+ if (chip->hw_ip_version < SOF_INTEL_ACE_2_0)
+ return false;
+ return true;
+ default:
+ return false;
+ }
+}
+EXPORT_SYMBOL_NS(hda_is_chain_dma_supported, "SND_SOC_SOF_INTEL_HDA_COMMON");
+
/*
* DSP Core control.
*/
-int hda_dsp_core_reset_enter(struct snd_sof_dev *sdev, unsigned int core_mask)
+static int hda_dsp_core_reset_enter(struct snd_sof_dev *sdev, unsigned int core_mask)
{
u32 adspcs;
u32 reset;
@@ -44,7 +136,7 @@ int hda_dsp_core_reset_enter(struct snd_sof_dev *sdev, unsigned int core_mask)
reset = HDA_DSP_ADSPCS_CRST_MASK(core_mask);
snd_sof_dsp_update_bits_unlocked(sdev, HDA_DSP_BAR,
HDA_DSP_REG_ADSPCS,
- reset, reset),
+ reset, reset);
/* poll with timeout to check if operation successful */
ret = snd_sof_dsp_read_poll_timeout(sdev, HDA_DSP_BAR,
@@ -73,7 +165,7 @@ int hda_dsp_core_reset_enter(struct snd_sof_dev *sdev, unsigned int core_mask)
return ret;
}
-int hda_dsp_core_reset_leave(struct snd_sof_dev *sdev, unsigned int core_mask)
+static int hda_dsp_core_reset_leave(struct snd_sof_dev *sdev, unsigned int core_mask)
{
unsigned int crst;
u32 adspcs;
@@ -124,6 +216,33 @@ int hda_dsp_core_stall_reset(struct snd_sof_dev *sdev, unsigned int core_mask)
/* set reset state */
return hda_dsp_core_reset_enter(sdev, core_mask);
}
+EXPORT_SYMBOL_NS(hda_dsp_core_stall_reset, "SND_SOC_SOF_INTEL_HDA_COMMON");
+
+bool hda_dsp_core_is_enabled(struct snd_sof_dev *sdev, unsigned int core_mask)
+{
+ int val;
+ bool is_enable;
+
+ val = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPCS);
+
+#define MASK_IS_EQUAL(v, m, field) ({ \
+ u32 _m = field(m); \
+ ((v) & _m) == _m; \
+})
+
+ is_enable = MASK_IS_EQUAL(val, core_mask, HDA_DSP_ADSPCS_CPA_MASK) &&
+ MASK_IS_EQUAL(val, core_mask, HDA_DSP_ADSPCS_SPA_MASK) &&
+ !(val & HDA_DSP_ADSPCS_CRST_MASK(core_mask)) &&
+ !(val & HDA_DSP_ADSPCS_CSTALL_MASK(core_mask));
+
+#undef MASK_IS_EQUAL
+
+ dev_dbg(sdev->dev, "DSP core(s) enabled? %d : core_mask %x\n",
+ is_enable, core_mask);
+
+ return is_enable;
+}
+EXPORT_SYMBOL_NS(hda_dsp_core_is_enabled, "SND_SOC_SOF_INTEL_HDA_COMMON");
int hda_dsp_core_run(struct snd_sof_dev *sdev, unsigned int core_mask)
{
@@ -151,6 +270,7 @@ int hda_dsp_core_run(struct snd_sof_dev *sdev, unsigned int core_mask)
return ret;
}
+EXPORT_SYMBOL_NS(hda_dsp_core_run, "SND_SOC_SOF_INTEL_HDA_COMMON");
/*
* Power Management.
@@ -158,10 +278,18 @@ int hda_dsp_core_run(struct snd_sof_dev *sdev, unsigned int core_mask)
int hda_dsp_core_power_up(struct snd_sof_dev *sdev, unsigned int core_mask)
{
+ struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
+ const struct sof_intel_dsp_desc *chip = hda->desc;
unsigned int cpa;
u32 adspcs;
int ret;
+ /* restrict core_mask to host managed cores mask */
+ core_mask &= chip->host_managed_cores_mask;
+ /* return if core_mask is not valid */
+ if (!core_mask)
+ return 0;
+
/* update bits */
snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPCS,
HDA_DSP_ADSPCS_SPA_MASK(core_mask),
@@ -194,8 +322,9 @@ int hda_dsp_core_power_up(struct snd_sof_dev *sdev, unsigned int core_mask)
return ret;
}
+EXPORT_SYMBOL_NS(hda_dsp_core_power_up, "SND_SOC_SOF_INTEL_HDA_COMMON");
-int hda_dsp_core_power_down(struct snd_sof_dev *sdev, unsigned int core_mask)
+static int hda_dsp_core_power_down(struct snd_sof_dev *sdev, unsigned int core_mask)
{
u32 adspcs;
int ret;
@@ -207,7 +336,7 @@ int hda_dsp_core_power_down(struct snd_sof_dev *sdev, unsigned int core_mask)
ret = snd_sof_dsp_read_poll_timeout(sdev, HDA_DSP_BAR,
HDA_DSP_REG_ADSPCS, adspcs,
- !(adspcs & HDA_DSP_ADSPCS_SPA_MASK(core_mask)),
+ !(adspcs & HDA_DSP_ADSPCS_CPA_MASK(core_mask)),
HDA_DSP_REG_POLL_INTERVAL_US,
HDA_DSP_PD_TIMEOUT * USEC_PER_MSEC);
if (ret < 0)
@@ -218,31 +347,17 @@ int hda_dsp_core_power_down(struct snd_sof_dev *sdev, unsigned int core_mask)
return ret;
}
-bool hda_dsp_core_is_enabled(struct snd_sof_dev *sdev,
- unsigned int core_mask)
-{
- int val;
- bool is_enable;
-
- val = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPCS);
-
- is_enable = (val & HDA_DSP_ADSPCS_CPA_MASK(core_mask)) &&
- (val & HDA_DSP_ADSPCS_SPA_MASK(core_mask)) &&
- !(val & HDA_DSP_ADSPCS_CRST_MASK(core_mask)) &&
- !(val & HDA_DSP_ADSPCS_CSTALL_MASK(core_mask));
-
- dev_dbg(sdev->dev, "DSP core(s) enabled? %d : core_mask %x\n",
- is_enable, core_mask);
-
- return is_enable;
-}
-
int hda_dsp_enable_core(struct snd_sof_dev *sdev, unsigned int core_mask)
{
+ struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
+ const struct sof_intel_dsp_desc *chip = hda->desc;
int ret;
- /* return if core is already enabled */
- if (hda_dsp_core_is_enabled(sdev, core_mask))
+ /* restrict core_mask to host managed cores mask */
+ core_mask &= chip->host_managed_cores_mask;
+
+ /* return if core_mask is not valid or cores are already enabled */
+ if (!core_mask || hda_dsp_core_is_enabled(sdev, core_mask))
return 0;
/* power up */
@@ -255,12 +370,22 @@ int hda_dsp_enable_core(struct snd_sof_dev *sdev, unsigned int core_mask)
return hda_dsp_core_run(sdev, core_mask);
}
+EXPORT_SYMBOL_NS(hda_dsp_enable_core, "SND_SOC_SOF_INTEL_HDA_COMMON");
int hda_dsp_core_reset_power_down(struct snd_sof_dev *sdev,
unsigned int core_mask)
{
+ struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
+ const struct sof_intel_dsp_desc *chip = hda->desc;
int ret;
+ /* restrict core_mask to host managed cores mask */
+ core_mask &= chip->host_managed_cores_mask;
+
+ /* return if core_mask is not valid */
+ if (!core_mask)
+ return 0;
+
/* place core in reset prior to power down */
ret = hda_dsp_core_stall_reset(sdev, core_mask);
if (ret < 0) {
@@ -286,12 +411,16 @@ int hda_dsp_core_reset_power_down(struct snd_sof_dev *sdev,
return ret;
}
+EXPORT_SYMBOL_NS(hda_dsp_core_reset_power_down, "SND_SOC_SOF_INTEL_HDA_COMMON");
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,
@@ -301,12 +430,16 @@ void hda_dsp_ipc_int_enable(struct snd_sof_dev *sdev)
snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPIC,
HDA_DSP_ADSPIC_IPC, HDA_DSP_ADSPIC_IPC);
}
+EXPORT_SYMBOL_NS(hda_dsp_ipc_int_enable, "SND_SOC_SOF_INTEL_HDA_COMMON");
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);
@@ -315,13 +448,17 @@ void hda_dsp_ipc_int_disable(struct snd_sof_dev *sdev)
snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, chip->ipc_ctl,
HDA_DSP_REG_HIPCCTL_BUSY | HDA_DSP_REG_HIPCCTL_DONE, 0);
}
+EXPORT_SYMBOL_NS(hda_dsp_ipc_int_disable, "SND_SOC_SOF_INTEL_HDA_COMMON");
static int hda_dsp_wait_d0i3c_done(struct snd_sof_dev *sdev)
{
- struct hdac_bus *bus = sof_to_bus(sdev);
int retry = HDA_DSP_REG_POLL_RETRY_COUNT;
+ struct snd_sof_pdata *pdata = sdev->pdata;
+ const struct sof_intel_dsp_desc *chip;
- while (snd_hdac_chip_readb(bus, VS_D0I3C) & SOF_HDA_VS_D0I3C_CIP) {
+ chip = get_chip_info(pdata);
+ while (snd_sof_dsp_read8(sdev, HDA_DSP_HDA_BAR, chip->d0i3_offset) &
+ SOF_HDA_VS_D0I3C_CIP) {
if (!retry--)
return -ETIMEDOUT;
usleep_range(10, 15);
@@ -332,50 +469,87 @@ static int hda_dsp_wait_d0i3c_done(struct snd_sof_dev *sdev)
static int hda_dsp_send_pm_gate_ipc(struct snd_sof_dev *sdev, u32 flags)
{
- struct sof_ipc_pm_gate pm_gate;
- struct sof_ipc_reply reply;
+ const struct sof_ipc_pm_ops *pm_ops = sof_ipc_get_ops(sdev, pm);
- memset(&pm_gate, 0, sizeof(pm_gate));
+ if (pm_ops && pm_ops->set_pm_gate)
+ return pm_ops->set_pm_gate(sdev, flags);
- /* configure pm_gate ipc message */
- pm_gate.hdr.size = sizeof(pm_gate);
- pm_gate.hdr.cmd = SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_GATE;
- pm_gate.flags = flags;
-
- /* send pm_gate ipc to dsp */
- return sof_ipc_tx_message_no_pm(sdev->ipc, pm_gate.hdr.cmd,
- &pm_gate, sizeof(pm_gate), &reply,
- sizeof(reply));
+ return 0;
}
static int hda_dsp_update_d0i3c_register(struct snd_sof_dev *sdev, u8 value)
{
- struct hdac_bus *bus = sof_to_bus(sdev);
+ struct snd_sof_pdata *pdata = sdev->pdata;
+ const struct sof_intel_dsp_desc *chip;
int ret;
+ u8 reg;
+
+ chip = get_chip_info(pdata);
/* Write to D0I3C after Command-In-Progress bit is cleared */
ret = hda_dsp_wait_d0i3c_done(sdev);
if (ret < 0) {
- dev_err(bus->dev, "CIP timeout before D0I3C update!\n");
+ dev_err(sdev->dev, "CIP timeout before D0I3C update!\n");
return ret;
}
/* Update D0I3C register */
- snd_hdac_chip_updateb(bus, VS_D0I3C, SOF_HDA_VS_D0I3C_I3, value);
+ snd_sof_dsp_update8(sdev, HDA_DSP_HDA_BAR, chip->d0i3_offset,
+ SOF_HDA_VS_D0I3C_I3, value);
+
+ /*
+ * The value written to the D0I3C::I3 bit may not be taken into account immediately.
+ * A delay is recommended before checking if D0I3C::CIP is cleared
+ */
+ usleep_range(30, 40);
/* Wait for cmd in progress to be cleared before exiting the function */
ret = hda_dsp_wait_d0i3c_done(sdev);
if (ret < 0) {
- dev_err(bus->dev, "CIP timeout after D0I3C update!\n");
+ dev_err(sdev->dev, "CIP timeout after D0I3C update!\n");
return ret;
}
- dev_vdbg(bus->dev, "D0I3C updated, register = 0x%x\n",
- snd_hdac_chip_readb(bus, VS_D0I3C));
+ reg = snd_sof_dsp_read8(sdev, HDA_DSP_HDA_BAR, chip->d0i3_offset);
+ /* Confirm d0i3 state changed with paranoia check */
+ if ((reg ^ value) & SOF_HDA_VS_D0I3C_I3) {
+ dev_err(sdev->dev, "failed to update D0I3C!\n");
+ return -EIO;
+ }
+
+ trace_sof_intel_D0I3C_updated(sdev, reg);
return 0;
}
+/*
+ * d0i3 streaming is enabled if all the active streams can
+ * work in d0i3 state and playback is enabled
+ */
+static bool hda_dsp_d0i3_streaming_applicable(struct snd_sof_dev *sdev)
+{
+ struct snd_pcm_substream *substream;
+ struct snd_sof_pcm *spcm;
+ bool playback_active = false;
+ int dir;
+
+ list_for_each_entry(spcm, &sdev->pcm_list, list) {
+ for_each_pcm_streams(dir) {
+ substream = spcm->stream[dir].substream;
+ if (!substream || !substream->runtime)
+ continue;
+
+ if (!spcm->stream[dir].d0i3_compatible)
+ return false;
+
+ if (dir == SNDRV_PCM_STREAM_PLAYBACK)
+ playback_active = true;
+ }
+ }
+
+ return playback_active;
+}
+
static int hda_dsp_set_D0_state(struct snd_sof_dev *sdev,
const struct sof_dsp_power_state *target_state)
{
@@ -413,10 +587,13 @@ static int hda_dsp_set_D0_state(struct snd_sof_dev *sdev,
* when the DSP enters D0I3 while the system is in S0
* for debug purpose.
*/
- if (!sdev->dtrace_is_supported ||
+ if (!sdev->fw_trace_is_supported ||
!hda_enable_trace_D0I3_S0 ||
sdev->system_suspend_target != SOF_SUSPEND_NONE)
flags = HDA_PM_NO_DMA_TRACE;
+
+ if (hda_dsp_d0i3_streaming_applicable(sdev))
+ flags |= HDA_PM_PG_STREAMING;
} else {
/* prevent power gating in D0I0 */
flags = HDA_PM_PPG;
@@ -478,15 +655,9 @@ static void hda_dsp_state_log(struct snd_sof_dev *sdev)
case SOF_DSP_PM_D2:
dev_dbg(sdev->dev, "Current DSP power state: D2\n");
break;
- case SOF_DSP_PM_D3_HOT:
- dev_dbg(sdev->dev, "Current DSP power state: D3_HOT\n");
- break;
case SOF_DSP_PM_D3:
dev_dbg(sdev->dev, "Current DSP power state: D3\n");
break;
- case SOF_DSP_PM_D3_COLD:
- dev_dbg(sdev->dev, "Current DSP power state: D3_COLD\n");
- break;
default:
dev_dbg(sdev->dev, "Unknown DSP power state: %d\n",
sdev->dsp_power_state.state);
@@ -501,31 +672,11 @@ static void hda_dsp_state_log(struct snd_sof_dev *sdev)
* is called again either because of a new IPC sent to the DSP or
* during system suspend/resume.
*/
-int hda_dsp_set_power_state(struct snd_sof_dev *sdev,
- const struct sof_dsp_power_state *target_state)
+static int hda_dsp_set_power_state(struct snd_sof_dev *sdev,
+ const struct sof_dsp_power_state *target_state)
{
int ret = 0;
- /*
- * When the DSP is already in D0I3 and the target state is D0I3,
- * it could be the case that the DSP is in D0I3 during S0
- * and the system is suspending to S0Ix. Therefore,
- * hda_dsp_set_D0_state() must be called to disable trace DMA
- * by sending the PM_GATE IPC to the FW.
- */
- if (target_state->substate == SOF_HDA_DSP_PM_D0I3 &&
- sdev->system_suspend_target == SOF_SUSPEND_S0IX)
- goto set_state;
-
- /*
- * For all other cases, return without doing anything if
- * the DSP is already in the target state.
- */
- if (target_state->state == sdev->dsp_power_state.state &&
- target_state->substate == sdev->dsp_power_state.substate)
- return 0;
-
-set_state:
switch (target_state->state) {
case SOF_DSP_PM_D0:
ret = hda_dsp_set_D0_state(sdev, target_state);
@@ -557,6 +708,44 @@ set_state:
return ret;
}
+int hda_dsp_set_power_state_ipc3(struct snd_sof_dev *sdev,
+ const struct sof_dsp_power_state *target_state)
+{
+ /*
+ * When the DSP is already in D0I3 and the target state is D0I3,
+ * it could be the case that the DSP is in D0I3 during S0
+ * and the system is suspending to S0Ix. Therefore,
+ * hda_dsp_set_D0_state() must be called to disable trace DMA
+ * by sending the PM_GATE IPC to the FW.
+ */
+ if (target_state->substate == SOF_HDA_DSP_PM_D0I3 &&
+ sdev->system_suspend_target == SOF_SUSPEND_S0IX)
+ return hda_dsp_set_power_state(sdev, target_state);
+
+ /*
+ * For all other cases, return without doing anything if
+ * the DSP is already in the target state.
+ */
+ if (target_state->state == sdev->dsp_power_state.state &&
+ target_state->substate == sdev->dsp_power_state.substate)
+ return 0;
+
+ return hda_dsp_set_power_state(sdev, target_state);
+}
+EXPORT_SYMBOL_NS(hda_dsp_set_power_state_ipc3, "SND_SOC_SOF_INTEL_HDA_COMMON");
+
+int hda_dsp_set_power_state_ipc4(struct snd_sof_dev *sdev,
+ const struct sof_dsp_power_state *target_state)
+{
+ /* Return without doing anything if the DSP is already in the target state */
+ if (target_state->state == sdev->dsp_power_state.state &&
+ target_state->substate == sdev->dsp_power_state.substate)
+ return 0;
+
+ return hda_dsp_set_power_state(sdev, target_state);
+}
+EXPORT_SYMBOL_NS(hda_dsp_set_power_state_ipc4, "SND_SOC_SOF_INTEL_HDA_COMMON");
+
/*
* Audio DSP states may transform as below:-
*
@@ -591,35 +780,60 @@ static int hda_suspend(struct snd_sof_dev *sdev, bool runtime_suspend)
{
struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
const struct sof_intel_dsp_desc *chip = hda->desc;
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
struct hdac_bus *bus = sof_to_bus(sdev);
-#endif
- int ret;
+ bool imr_lost = false;
+ int ret, j;
- hda_sdw_int_enable(sdev, false);
+ /*
+ * The memory used for IMR boot loses its content in deeper than S3
+ * state on CAVS platforms.
+ * On ACE platforms due to the system architecture the IMR content is
+ * lost at S3 state already, they are tailored for s2idle use.
+ * We must not try IMR boot on next power up in these cases as it will
+ * fail.
+ */
+ if (sdev->system_suspend_target > SOF_SUSPEND_S3 ||
+ (chip->hw_ip_version >= SOF_INTEL_ACE_1_0 &&
+ sdev->system_suspend_target == SOF_SUSPEND_S3))
+ imr_lost = true;
- /* disable IPC interrupts */
- hda_dsp_ipc_int_disable(sdev);
+ /*
+ * In case of firmware crash or boot failure set the skip_imr_boot to true
+ * as well in order to try to re-load the firmware to do a 'cold' boot.
+ */
+ if (imr_lost || sdev->fw_state == SOF_FW_CRASHED ||
+ sdev->fw_state == SOF_FW_BOOT_FAILED)
+ hda->skip_imr_boot = true;
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
- if (runtime_suspend)
- hda_codec_jack_wake_enable(sdev);
+ ret = chip->disable_interrupts(sdev);
+ if (ret < 0)
+ return ret;
- /* power down all hda link */
- snd_hdac_ext_bus_link_power_down_all(bus);
-#endif
+ /* make sure that no irq handler is pending before shutdown */
+ synchronize_irq(sdev->ipc_irq);
+
+ hda_codec_jack_wake_enable(sdev, runtime_suspend);
+
+ /* power down all hda links */
+ hda_bus_ml_suspend(bus);
- /* power down DSP */
- ret = hda_dsp_core_reset_power_down(sdev, chip->cores_mask);
+ if (sdev->dspless_mode_selected)
+ goto skip_dsp;
+
+ ret = chip->power_down_dsp(sdev);
if (ret < 0) {
- dev_err(sdev->dev,
- "error: failed to power down core during suspend\n");
+ dev_err(sdev->dev, "failed to power down DSP during suspend\n");
return ret;
}
+ /* reset ref counts for all cores */
+ for (j = 0; j < chip->cores_num; j++)
+ sdev->dsp_core_ref_count[j] = 0;
+
/* 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);
@@ -644,10 +858,6 @@ static int hda_suspend(struct snd_sof_dev *sdev, bool runtime_suspend)
static int hda_resume(struct snd_sof_dev *sdev, bool runtime_resume)
{
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
- struct hdac_bus *bus = sof_to_bus(sdev);
- struct hdac_ext_link *hlink = NULL;
-#endif
int ret;
/* display codec must be powered before link reset */
@@ -660,32 +870,29 @@ static int hda_resume(struct snd_sof_dev *sdev, bool runtime_resume)
snd_sof_pci_update_bits(sdev, PCI_TCSEL, 0x07, 0);
/* reset and start hda controller */
- ret = hda_dsp_ctrl_init_chip(sdev, true);
+ ret = hda_dsp_ctrl_init_chip(sdev);
if (ret < 0) {
dev_err(sdev->dev,
"error: failed to start controller after resume\n");
- return ret;
+ goto cleanup;
}
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
/* check jack status */
- if (runtime_resume)
- hda_codec_jack_check(sdev);
-
- /* turn off the links that were off before suspend */
- list_for_each_entry(hlink, &bus->hlink_list, list) {
- if (!hlink->ref_count)
- snd_hdac_ext_bus_link_power_down(hlink);
+ if (runtime_resume) {
+ hda_codec_jack_wake_enable(sdev, false);
+ if (sdev->system_suspend_target == SOF_SUSPEND_NONE)
+ hda_codec_jack_check(sdev);
}
- /* check dma status and clean up CORB/RIRB buffers */
- if (!bus->cmd_dma_state)
- snd_hdac_bus_stop_cmd_io(bus);
-#endif
+ if (!sdev->dspless_mode_selected) {
+ /* enable ppcap interrupt */
+ hda_dsp_ctrl_ppcap_enable(sdev, true);
+ hda_dsp_ctrl_ppcap_int_enable(sdev, true);
+ }
- /* 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 */
+ hda_codec_i915_display_power(sdev, false);
return 0;
}
@@ -693,39 +900,26 @@ static int hda_resume(struct snd_sof_dev *sdev, bool runtime_resume)
int hda_dsp_resume(struct snd_sof_dev *sdev)
{
struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
+ struct hdac_bus *bus = sof_to_bus(sdev);
struct pci_dev *pci = to_pci_dev(sdev->dev);
const struct sof_dsp_power_state target_state = {
.state = SOF_DSP_PM_D0,
.substate = SOF_HDA_DSP_PM_D0I0,
};
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
- struct hdac_bus *bus = sof_to_bus(sdev);
- struct hdac_ext_link *hlink = NULL;
-#endif
int ret;
/* resume from D0I3 */
if (sdev->dsp_power_state.state == SOF_DSP_PM_D0) {
- hda_codec_i915_display_power(sdev, true);
-
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
- /* power up links that were active before suspend */
- list_for_each_entry(hlink, &bus->hlink_list, list) {
- if (hlink->ref_count) {
- ret = snd_hdac_ext_bus_link_power_up(hlink);
- if (ret < 0) {
- dev_dbg(sdev->dev,
- "error %x in %s: failed to power up links",
- ret, __func__);
- return ret;
- }
- }
+ ret = hda_bus_ml_resume(bus);
+ if (ret < 0) {
+ dev_err(sdev->dev,
+ "error %d in %s: failed to power up links",
+ ret, __func__);
+ return ret;
}
/* set up CORB/RIRB buffers if was on before suspend */
- if (bus->cmd_dma_state)
- snd_hdac_bus_init_cmd_io(bus);
-#endif
+ hda_codec_resume_cmd_io(sdev);
/* Set DSP power state */
ret = snd_sof_dsp_set_power_state(sdev, &target_state);
@@ -736,7 +930,7 @@ int hda_dsp_resume(struct snd_sof_dev *sdev)
}
/* restore L1SEN bit */
- if (hda->l1_support_changed)
+ if (hda->l1_disabled)
snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR,
HDA_VS_INTEL_EM2,
HDA_VS_INTEL_EM2_L1SEN, 0);
@@ -754,6 +948,7 @@ int hda_dsp_resume(struct snd_sof_dev *sdev)
return snd_sof_dsp_set_power_state(sdev, &target_state);
}
+EXPORT_SYMBOL_NS(hda_dsp_resume, "SND_SOC_SOF_INTEL_HDA_COMMON");
int hda_dsp_runtime_resume(struct snd_sof_dev *sdev)
{
@@ -769,6 +964,7 @@ int hda_dsp_runtime_resume(struct snd_sof_dev *sdev)
return snd_sof_dsp_set_power_state(sdev, &target_state);
}
+EXPORT_SYMBOL_NS(hda_dsp_runtime_resume, "SND_SOC_SOF_INTEL_HDA_COMMON");
int hda_dsp_runtime_idle(struct snd_sof_dev *sdev)
{
@@ -782,14 +978,25 @@ int hda_dsp_runtime_idle(struct snd_sof_dev *sdev)
return 0;
}
+EXPORT_SYMBOL_NS(hda_dsp_runtime_idle, "SND_SOC_SOF_INTEL_HDA_COMMON");
int hda_dsp_runtime_suspend(struct snd_sof_dev *sdev)
{
+ struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
const struct sof_dsp_power_state target_state = {
.state = SOF_DSP_PM_D3,
};
int ret;
+ if (!sdev->dspless_mode_selected) {
+ /* cancel any attempt for DSP D0I3 */
+ cancel_delayed_work_sync(&hda->d0i3_work);
+
+ /* Cancel the microphone privacy work if mic privacy is active */
+ if (hda->mic_privacy.active)
+ cancel_work_sync(&hda->mic_privacy.work);
+ }
+
/* stop hda controller and power dsp off */
ret = hda_suspend(sdev, true);
if (ret < 0)
@@ -797,6 +1004,7 @@ int hda_dsp_runtime_suspend(struct snd_sof_dev *sdev)
return snd_sof_dsp_set_power_state(sdev, &target_state);
}
+EXPORT_SYMBOL_NS(hda_dsp_runtime_suspend, "SND_SOC_SOF_INTEL_HDA_COMMON");
int hda_dsp_suspend(struct snd_sof_dev *sdev, u32 target_state)
{
@@ -810,13 +1018,16 @@ 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) {
- /* we can't keep a wakeref to display driver at suspend */
- hda_codec_i915_display_power(sdev, false);
+ /* Cancel the microphone privacy work if mic privacy is active */
+ if (hda->mic_privacy.active)
+ cancel_work_sync(&hda->mic_privacy.work);
+ }
+ if (target_state == SOF_DSP_PM_D0) {
/* Set DSP power state */
ret = snd_sof_dsp_set_power_state(sdev, &target_dsp_state);
if (ret < 0) {
@@ -827,26 +1038,21 @@ int hda_dsp_suspend(struct snd_sof_dev *sdev, u32 target_state)
}
/* enable L1SEN to make sure the system can enter S0Ix */
- hda->l1_support_changed =
- snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR,
- HDA_VS_INTEL_EM2,
- HDA_VS_INTEL_EM2_L1SEN,
- HDA_VS_INTEL_EM2_L1SEN);
+ if (hda->l1_disabled)
+ snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, HDA_VS_INTEL_EM2,
+ HDA_VS_INTEL_EM2_L1SEN, HDA_VS_INTEL_EM2_L1SEN);
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
/* stop the CORB/RIRB DMA if it is On */
- if (bus->cmd_dma_state)
- snd_hdac_bus_stop_cmd_io(bus);
+ hda_codec_suspend_cmd_io(sdev);
/* no link can be powered in s0ix state */
- ret = snd_hdac_ext_bus_link_power_down_all(bus);
+ ret = hda_bus_ml_suspend(bus);
if (ret < 0) {
- dev_dbg(sdev->dev,
+ dev_err(sdev->dev,
"error %d in %s: failed to power down links",
ret, __func__);
return ret;
}
-#endif
/* enable the system waking up via IPC IRQ */
enable_irq_wake(pci->irq);
@@ -863,48 +1069,100 @@ int hda_dsp_suspend(struct snd_sof_dev *sdev, u32 target_state)
return snd_sof_dsp_set_power_state(sdev, &target_dsp_state);
}
+EXPORT_SYMBOL_NS(hda_dsp_suspend, "SND_SOC_SOF_INTEL_HDA_COMMON");
-int hda_dsp_set_hw_params_upon_resume(struct snd_sof_dev *sdev)
+static unsigned int hda_dsp_check_for_dma_streams(struct snd_sof_dev *sdev)
{
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
struct hdac_bus *bus = sof_to_bus(sdev);
- struct snd_soc_pcm_runtime *rtd;
- struct hdac_ext_stream *stream;
- struct hdac_ext_link *link;
struct hdac_stream *s;
- const char *name;
- int stream_tag;
+ unsigned int active_streams = 0;
+ int sd_offset;
+ u32 val;
- /* set internal flag for BE */
list_for_each_entry(s, &bus->stream_list, list) {
- stream = stream_to_hdac_ext_stream(s);
+ sd_offset = SOF_STREAM_SD_OFFSET(s);
+ val = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR,
+ sd_offset);
+ if (val & SOF_HDA_SD_CTL_DMA_START)
+ active_streams |= BIT(s->index);
+ }
- /*
- * clear stream. This should already be taken care for running
- * streams when the SUSPEND trigger is called. But paused
- * streams do not get suspended, so this needs to be done
- * explicitly during suspend.
- */
- if (stream->link_substream) {
- rtd = asoc_substream_to_rtd(stream->link_substream);
- name = asoc_rtd_to_codec(rtd, 0)->component->name;
- link = snd_hdac_ext_bus_get_link(bus, name);
- if (!link)
- return -EINVAL;
+ return active_streams;
+}
- stream->link_prepared = 0;
+static int hda_dsp_s5_quirk(struct snd_sof_dev *sdev)
+{
+ int ret;
- if (hdac_stream(stream)->direction ==
- SNDRV_PCM_STREAM_CAPTURE)
- continue;
+ /*
+ * Do not assume a certain timing between the prior
+ * suspend flow, and running of this quirk function.
+ * This is needed if the controller was just put
+ * to reset before calling this function.
+ */
+ usleep_range(500, 1000);
- stream_tag = hdac_stream(stream)->stream_tag;
- snd_hdac_ext_link_clear_stream_id(link, stream_tag);
- }
+ /*
+ * Take controller out of reset to flush DMA
+ * transactions.
+ */
+ ret = hda_dsp_ctrl_link_reset(sdev, false);
+ if (ret < 0)
+ return ret;
+
+ usleep_range(500, 1000);
+
+ /* Restore state for shutdown, back to reset */
+ ret = hda_dsp_ctrl_link_reset(sdev, true);
+ if (ret < 0)
+ return ret;
+
+ return ret;
+}
+
+int hda_dsp_shutdown_dma_flush(struct snd_sof_dev *sdev)
+{
+ unsigned int active_streams;
+ int ret, ret2;
+
+ /* check if DMA cleanup has been successful */
+ active_streams = hda_dsp_check_for_dma_streams(sdev);
+
+ sdev->system_suspend_target = SOF_SUSPEND_S3;
+ ret = snd_sof_suspend(sdev->dev);
+
+ if (active_streams) {
+ dev_warn(sdev->dev,
+ "There were active DSP streams (%#x) at shutdown, trying to recover\n",
+ active_streams);
+ ret2 = hda_dsp_s5_quirk(sdev);
+ if (ret2 < 0)
+ dev_err(sdev->dev, "shutdown recovery failed (%d)\n", ret2);
}
-#endif
- return 0;
+
+ return ret;
+}
+EXPORT_SYMBOL_NS(hda_dsp_shutdown_dma_flush, "SND_SOC_SOF_INTEL_HDA_COMMON");
+
+int hda_dsp_shutdown(struct snd_sof_dev *sdev)
+{
+ sdev->system_suspend_target = SOF_SUSPEND_S3;
+ return snd_sof_suspend(sdev->dev);
}
+EXPORT_SYMBOL_NS(hda_dsp_shutdown, "SND_SOC_SOF_INTEL_HDA_COMMON");
+
+int hda_dsp_set_hw_params_upon_resume(struct snd_sof_dev *sdev)
+{
+ int ret;
+
+ /* make sure all DAI resources are freed */
+ ret = hda_dsp_dais_suspend(sdev);
+ if (ret < 0)
+ dev_warn(sdev->dev, "%s: failure in hda_dsp_dais_suspend\n", __func__);
+
+ return ret;
+}
+EXPORT_SYMBOL_NS(hda_dsp_set_hw_params_upon_resume, "SND_SOC_SOF_INTEL_HDA_COMMON");
void hda_dsp_d0i3_work(struct work_struct *work)
{
@@ -913,19 +1171,15 @@ void hda_dsp_d0i3_work(struct work_struct *work)
d0i3_work.work);
struct hdac_bus *bus = &hdev->hbus.core;
struct snd_sof_dev *sdev = dev_get_drvdata(bus->dev);
- struct sof_dsp_power_state target_state;
+ struct sof_dsp_power_state target_state = {
+ .state = SOF_DSP_PM_D0,
+ .substate = SOF_HDA_DSP_PM_D0I3,
+ };
int ret;
- target_state.state = SOF_DSP_PM_D0;
-
/* DSP can enter D0I3 iff only D0I3-compatible streams are active */
- if (snd_sof_dsp_only_d0i3_compatible_stream_active(sdev))
- target_state.substate = SOF_HDA_DSP_PM_D0I3;
- else
- target_state.substate = SOF_HDA_DSP_PM_D0I0;
-
- /* remain in D0I0 */
- if (target_state.substate == SOF_HDA_DSP_PM_D0I0)
+ if (!snd_sof_dsp_only_d0i3_compatible_stream_active(sdev))
+ /* remain in D0I0 */
return;
/* This can fail but error cannot be propagated */
@@ -935,3 +1189,446 @@ void hda_dsp_d0i3_work(struct work_struct *work)
"error: failed to set DSP state %d substate %d\n",
target_state.state, target_state.substate);
}
+EXPORT_SYMBOL_NS(hda_dsp_d0i3_work, "SND_SOC_SOF_INTEL_HDA_COMMON");
+
+int hda_dsp_core_get(struct snd_sof_dev *sdev, int core)
+{
+ const struct sof_ipc_pm_ops *pm_ops = sdev->ipc->ops->pm;
+ int ret, ret1;
+
+ /* power up core */
+ ret = hda_dsp_enable_core(sdev, BIT(core));
+ if (ret < 0) {
+ dev_err(sdev->dev, "failed to power up core %d with err: %d\n",
+ core, ret);
+ return ret;
+ }
+
+ /* No need to send IPC for primary core or if FW boot is not complete */
+ if (sdev->fw_state != SOF_FW_BOOT_COMPLETE || core == SOF_DSP_PRIMARY_CORE)
+ return 0;
+
+ /* No need to continue the set_core_state ops is not available */
+ if (!pm_ops->set_core_state)
+ return 0;
+
+ /* Now notify DSP for secondary cores */
+ ret = pm_ops->set_core_state(sdev, core, true);
+ if (ret < 0) {
+ dev_err(sdev->dev, "failed to enable secondary core '%d' failed with %d\n",
+ core, ret);
+ goto power_down;
+ }
+
+ return ret;
+
+power_down:
+ /* power down core if it is host managed and return the original error if this fails too */
+ ret1 = hda_dsp_core_reset_power_down(sdev, BIT(core));
+ if (ret1 < 0)
+ dev_err(sdev->dev, "failed to power down core: %d with err: %d\n", core, ret1);
+
+ return ret;
+}
+EXPORT_SYMBOL_NS(hda_dsp_core_get, "SND_SOC_SOF_INTEL_HDA_COMMON");
+
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE)
+void hda_common_enable_sdw_irq(struct snd_sof_dev *sdev, bool enable)
+{
+ struct sof_intel_hda_dev *hdev;
+
+ hdev = sdev->pdata->hw_pdata;
+
+ if (!hdev->sdw)
+ return;
+
+ snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPIC2,
+ HDA_DSP_REG_ADSPIC2_SNDW,
+ enable ? HDA_DSP_REG_ADSPIC2_SNDW : 0);
+}
+EXPORT_SYMBOL_NS(hda_common_enable_sdw_irq, "SND_SOC_SOF_INTEL_HDA_COMMON");
+
+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);
+}
+EXPORT_SYMBOL_NS(hda_sdw_int_enable, "SND_SOC_SOF_INTEL_HDA_COMMON");
+
+int hda_sdw_check_lcount_common(struct snd_sof_dev *sdev)
+{
+ struct sof_intel_hda_dev *hdev;
+ struct sdw_intel_ctx *ctx;
+ u32 caps;
+
+ hdev = sdev->pdata->hw_pdata;
+ ctx = hdev->sdw;
+
+ caps = snd_sof_dsp_read(sdev, HDA_DSP_BAR, ctx->shim_base + SDW_SHIM_LCAP);
+ caps &= SDW_SHIM_LCAP_LCOUNT_MASK;
+
+ /* Check HW supported vs property value */
+ if (caps < ctx->count) {
+ dev_err(sdev->dev,
+ "%s: BIOS master count %d is larger than hardware capabilities %d\n",
+ __func__, ctx->count, caps);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_NS(hda_sdw_check_lcount_common, "SND_SOC_SOF_INTEL_HDA_COMMON");
+
+int hda_sdw_check_lcount_ext(struct snd_sof_dev *sdev)
+{
+ struct sof_intel_hda_dev *hdev;
+ struct sdw_intel_ctx *ctx;
+ struct hdac_bus *bus;
+ u32 slcount;
+
+ bus = sof_to_bus(sdev);
+
+ hdev = sdev->pdata->hw_pdata;
+ ctx = hdev->sdw;
+
+ slcount = hdac_bus_eml_get_count(bus, true, AZX_REG_ML_LEPTR_ID_SDW);
+
+ /* Check HW supported vs property value */
+ if (slcount < ctx->count) {
+ dev_err(sdev->dev,
+ "%s: BIOS master count %d is larger than hardware capabilities %d\n",
+ __func__, ctx->count, slcount);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_NS(hda_sdw_check_lcount_ext, "SND_SOC_SOF_INTEL_HDA_COMMON");
+
+int hda_sdw_check_lcount(struct snd_sof_dev *sdev)
+{
+ const struct sof_intel_dsp_desc *chip;
+
+ chip = get_chip_info(sdev->pdata);
+ if (chip && chip->read_sdw_lcount)
+ return chip->read_sdw_lcount(sdev);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS(hda_sdw_check_lcount, "SND_SOC_SOF_INTEL_HDA_COMMON");
+
+void hda_sdw_process_wakeen(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;
+
+ chip = get_chip_info(sdev->pdata);
+ if (chip && chip->sdw_process_wakeen)
+ chip->sdw_process_wakeen(sdev);
+}
+EXPORT_SYMBOL_NS(hda_sdw_process_wakeen, "SND_SOC_SOF_INTEL_HDA_COMMON");
+
+#endif
+
+int hda_dsp_disable_interrupts(struct snd_sof_dev *sdev)
+{
+ hda_sdw_int_enable(sdev, false);
+ hda_dsp_ipc_int_disable(sdev);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS(hda_dsp_disable_interrupts, "SND_SOC_SOF_INTEL_HDA_COMMON");
+
+static const struct hda_dsp_msg_code hda_dsp_rom_fw_error_texts[] = {
+ {HDA_DSP_ROM_CSE_ERROR, "error: cse error"},
+ {HDA_DSP_ROM_CSE_WRONG_RESPONSE, "error: cse wrong response"},
+ {HDA_DSP_ROM_IMR_TO_SMALL, "error: IMR too small"},
+ {HDA_DSP_ROM_BASE_FW_NOT_FOUND, "error: base fw not found"},
+ {HDA_DSP_ROM_CSE_VALIDATION_FAILED, "error: signature verification failed"},
+ {HDA_DSP_ROM_IPC_FATAL_ERROR, "error: ipc fatal error"},
+ {HDA_DSP_ROM_L2_CACHE_ERROR, "error: L2 cache error"},
+ {HDA_DSP_ROM_LOAD_OFFSET_TO_SMALL, "error: load offset too small"},
+ {HDA_DSP_ROM_API_PTR_INVALID, "error: API ptr invalid"},
+ {HDA_DSP_ROM_BASEFW_INCOMPAT, "error: base fw incompatible"},
+ {HDA_DSP_ROM_UNHANDLED_INTERRUPT, "error: unhandled interrupt"},
+ {HDA_DSP_ROM_MEMORY_HOLE_ECC, "error: ECC memory hole"},
+ {HDA_DSP_ROM_KERNEL_EXCEPTION, "error: kernel exception"},
+ {HDA_DSP_ROM_USER_EXCEPTION, "error: user exception"},
+ {HDA_DSP_ROM_UNEXPECTED_RESET, "error: unexpected reset"},
+ {HDA_DSP_ROM_NULL_FW_ENTRY, "error: null FW entry point"},
+};
+
+#define FSR_ROM_STATE_ENTRY(state) {FSR_STATE_ROM_##state, #state}
+static const struct hda_dsp_msg_code cavs_fsr_rom_state_names[] = {
+ FSR_ROM_STATE_ENTRY(INIT),
+ FSR_ROM_STATE_ENTRY(INIT_DONE),
+ FSR_ROM_STATE_ENTRY(CSE_MANIFEST_LOADED),
+ FSR_ROM_STATE_ENTRY(FW_MANIFEST_LOADED),
+ FSR_ROM_STATE_ENTRY(FW_FW_LOADED),
+ FSR_ROM_STATE_ENTRY(FW_ENTERED),
+ FSR_ROM_STATE_ENTRY(VERIFY_FEATURE_MASK),
+ FSR_ROM_STATE_ENTRY(GET_LOAD_OFFSET),
+ FSR_ROM_STATE_ENTRY(FETCH_ROM_EXT),
+ FSR_ROM_STATE_ENTRY(FETCH_ROM_EXT_DONE),
+ /* CSE states */
+ FSR_ROM_STATE_ENTRY(CSE_IMR_REQUEST),
+ FSR_ROM_STATE_ENTRY(CSE_IMR_GRANTED),
+ FSR_ROM_STATE_ENTRY(CSE_VALIDATE_IMAGE_REQUEST),
+ FSR_ROM_STATE_ENTRY(CSE_IMAGE_VALIDATED),
+ FSR_ROM_STATE_ENTRY(CSE_IPC_IFACE_INIT),
+ FSR_ROM_STATE_ENTRY(CSE_IPC_RESET_PHASE_1),
+ FSR_ROM_STATE_ENTRY(CSE_IPC_OPERATIONAL_ENTRY),
+ FSR_ROM_STATE_ENTRY(CSE_IPC_OPERATIONAL),
+ FSR_ROM_STATE_ENTRY(CSE_IPC_DOWN),
+};
+
+static const struct hda_dsp_msg_code ace_fsr_rom_state_names[] = {
+ FSR_ROM_STATE_ENTRY(INIT),
+ FSR_ROM_STATE_ENTRY(INIT_DONE),
+ FSR_ROM_STATE_ENTRY(CSE_MANIFEST_LOADED),
+ FSR_ROM_STATE_ENTRY(FW_MANIFEST_LOADED),
+ FSR_ROM_STATE_ENTRY(FW_FW_LOADED),
+ FSR_ROM_STATE_ENTRY(FW_ENTERED),
+ FSR_ROM_STATE_ENTRY(VERIFY_FEATURE_MASK),
+ FSR_ROM_STATE_ENTRY(GET_LOAD_OFFSET),
+ FSR_ROM_STATE_ENTRY(RESET_VECTOR_DONE),
+ FSR_ROM_STATE_ENTRY(PURGE_BOOT),
+ FSR_ROM_STATE_ENTRY(RESTORE_BOOT),
+ FSR_ROM_STATE_ENTRY(FW_ENTRY_POINT),
+ FSR_ROM_STATE_ENTRY(VALIDATE_PUB_KEY),
+ FSR_ROM_STATE_ENTRY(POWER_DOWN_HPSRAM),
+ FSR_ROM_STATE_ENTRY(POWER_DOWN_ULPSRAM),
+ FSR_ROM_STATE_ENTRY(POWER_UP_ULPSRAM_STACK),
+ FSR_ROM_STATE_ENTRY(POWER_UP_HPSRAM_DMA),
+ FSR_ROM_STATE_ENTRY(BEFORE_EP_POINTER_READ),
+ FSR_ROM_STATE_ENTRY(VALIDATE_MANIFEST),
+ FSR_ROM_STATE_ENTRY(VALIDATE_FW_MODULE),
+ FSR_ROM_STATE_ENTRY(PROTECT_IMR_REGION),
+ FSR_ROM_STATE_ENTRY(PUSH_MODEL_ROUTINE),
+ FSR_ROM_STATE_ENTRY(PULL_MODEL_ROUTINE),
+ FSR_ROM_STATE_ENTRY(VALIDATE_PKG_DIR),
+ FSR_ROM_STATE_ENTRY(VALIDATE_CPD),
+ FSR_ROM_STATE_ENTRY(VALIDATE_CSS_MAN_HEADER),
+ FSR_ROM_STATE_ENTRY(VALIDATE_BLOB_SVN),
+ FSR_ROM_STATE_ENTRY(VERIFY_IFWI_PARTITION),
+ FSR_ROM_STATE_ENTRY(REMOVE_ACCESS_CONTROL),
+ FSR_ROM_STATE_ENTRY(AUTH_BYPASS),
+ FSR_ROM_STATE_ENTRY(AUTH_ENABLED),
+ FSR_ROM_STATE_ENTRY(INIT_DMA),
+ FSR_ROM_STATE_ENTRY(PURGE_FW_ENTRY),
+ FSR_ROM_STATE_ENTRY(PURGE_FW_END),
+ FSR_ROM_STATE_ENTRY(CLEAN_UP_BSS_DONE),
+ FSR_ROM_STATE_ENTRY(IMR_RESTORE_ENTRY),
+ FSR_ROM_STATE_ENTRY(IMR_RESTORE_END),
+ FSR_ROM_STATE_ENTRY(FW_MANIFEST_IN_DMA_BUFF),
+ FSR_ROM_STATE_ENTRY(LOAD_CSE_MAN_TO_IMR),
+ FSR_ROM_STATE_ENTRY(LOAD_FW_MAN_TO_IMR),
+ FSR_ROM_STATE_ENTRY(LOAD_FW_CODE_TO_IMR),
+ FSR_ROM_STATE_ENTRY(FW_LOADING_DONE),
+ FSR_ROM_STATE_ENTRY(FW_CODE_LOADED),
+ FSR_ROM_STATE_ENTRY(VERIFY_IMAGE_TYPE),
+ FSR_ROM_STATE_ENTRY(AUTH_API_INIT),
+ FSR_ROM_STATE_ENTRY(AUTH_API_PROC),
+ FSR_ROM_STATE_ENTRY(AUTH_API_FIRST_BUSY),
+ FSR_ROM_STATE_ENTRY(AUTH_API_FIRST_RESULT),
+ FSR_ROM_STATE_ENTRY(AUTH_API_CLEANUP),
+};
+
+#define FSR_BRINGUP_STATE_ENTRY(state) {FSR_STATE_BRINGUP_##state, #state}
+static const struct hda_dsp_msg_code fsr_bringup_state_names[] = {
+ FSR_BRINGUP_STATE_ENTRY(INIT),
+ FSR_BRINGUP_STATE_ENTRY(INIT_DONE),
+ FSR_BRINGUP_STATE_ENTRY(HPSRAM_LOAD),
+ FSR_BRINGUP_STATE_ENTRY(UNPACK_START),
+ FSR_BRINGUP_STATE_ENTRY(IMR_RESTORE),
+ FSR_BRINGUP_STATE_ENTRY(FW_ENTERED),
+};
+
+#define FSR_WAIT_STATE_ENTRY(state) {FSR_WAIT_FOR_##state, #state}
+static const struct hda_dsp_msg_code fsr_wait_state_names[] = {
+ FSR_WAIT_STATE_ENTRY(IPC_BUSY),
+ FSR_WAIT_STATE_ENTRY(IPC_DONE),
+ FSR_WAIT_STATE_ENTRY(CACHE_INVALIDATION),
+ FSR_WAIT_STATE_ENTRY(LP_SRAM_OFF),
+ FSR_WAIT_STATE_ENTRY(DMA_BUFFER_FULL),
+ FSR_WAIT_STATE_ENTRY(CSE_CSR),
+};
+
+#define FSR_MODULE_NAME_ENTRY(mod) [FSR_MOD_##mod] = #mod
+static const char * const fsr_module_names[] = {
+ FSR_MODULE_NAME_ENTRY(ROM),
+ FSR_MODULE_NAME_ENTRY(ROM_BYP),
+ FSR_MODULE_NAME_ENTRY(BASE_FW),
+ FSR_MODULE_NAME_ENTRY(LP_BOOT),
+ FSR_MODULE_NAME_ENTRY(BRNGUP),
+ FSR_MODULE_NAME_ENTRY(ROM_EXT),
+};
+
+static const char *
+hda_dsp_get_state_text(u32 code, const struct hda_dsp_msg_code *msg_code,
+ size_t array_size)
+{
+ int i;
+
+ for (i = 0; i < array_size; i++) {
+ if (code == msg_code[i].code)
+ return msg_code[i].text;
+ }
+
+ return NULL;
+}
+
+void hda_dsp_get_state(struct snd_sof_dev *sdev, const char *level)
+{
+ const struct sof_intel_dsp_desc *chip = get_chip_info(sdev->pdata);
+ const char *state_text, *error_text, *module_text;
+ u32 fsr, state, wait_state, module, error_code;
+
+ fsr = snd_sof_dsp_read(sdev, HDA_DSP_BAR, chip->rom_status_reg);
+ state = FSR_TO_STATE_CODE(fsr);
+ wait_state = FSR_TO_WAIT_STATE_CODE(fsr);
+ module = FSR_TO_MODULE_CODE(fsr);
+
+ if (module > FSR_MOD_ROM_EXT)
+ module_text = "unknown";
+ else
+ module_text = fsr_module_names[module];
+
+ if (module == FSR_MOD_BRNGUP) {
+ state_text = hda_dsp_get_state_text(state, fsr_bringup_state_names,
+ ARRAY_SIZE(fsr_bringup_state_names));
+ } else {
+ if (chip->hw_ip_version < SOF_INTEL_ACE_1_0)
+ state_text = hda_dsp_get_state_text(state,
+ cavs_fsr_rom_state_names,
+ ARRAY_SIZE(cavs_fsr_rom_state_names));
+ else
+ state_text = hda_dsp_get_state_text(state,
+ ace_fsr_rom_state_names,
+ ARRAY_SIZE(ace_fsr_rom_state_names));
+ }
+
+ /* not for us, must be generic sof message */
+ if (!state_text) {
+ dev_printk(level, sdev->dev, "%#010x: unknown ROM status value\n", fsr);
+ return;
+ }
+
+ if (wait_state) {
+ const char *wait_state_text;
+
+ wait_state_text = hda_dsp_get_state_text(wait_state, fsr_wait_state_names,
+ ARRAY_SIZE(fsr_wait_state_names));
+ if (!wait_state_text)
+ wait_state_text = "unknown";
+
+ dev_printk(level, sdev->dev,
+ "%#010x: module: %s, state: %s, waiting for: %s, %s\n",
+ fsr, module_text, state_text, wait_state_text,
+ fsr & FSR_HALTED ? "not running" : "running");
+ } else {
+ dev_printk(level, sdev->dev, "%#010x: module: %s, state: %s, %s\n",
+ fsr, module_text, state_text,
+ fsr & FSR_HALTED ? "not running" : "running");
+ }
+
+ error_code = snd_sof_dsp_read(sdev, HDA_DSP_BAR, chip->rom_status_reg + 4);
+ if (!error_code)
+ return;
+
+ error_text = hda_dsp_get_state_text(error_code, hda_dsp_rom_fw_error_texts,
+ ARRAY_SIZE(hda_dsp_rom_fw_error_texts));
+ if (!error_text)
+ error_text = "unknown";
+
+ if (state == FSR_STATE_FW_ENTERED)
+ dev_printk(level, sdev->dev, "status code: %#x (%s)\n", error_code,
+ error_text);
+ else
+ dev_printk(level, sdev->dev, "error code: %#x (%s)\n", error_code,
+ error_text);
+}
+EXPORT_SYMBOL_NS(hda_dsp_get_state, "SND_SOC_SOF_INTEL_HDA_COMMON");
+
+static void hda_dsp_get_registers(struct snd_sof_dev *sdev,
+ struct sof_ipc_dsp_oops_xtensa *xoops,
+ struct sof_ipc_panic_info *panic_info,
+ u32 *stack, size_t stack_words)
+{
+ u32 offset = sdev->dsp_oops_offset;
+
+ /* first read registers */
+ sof_mailbox_read(sdev, offset, xoops, sizeof(*xoops));
+
+ /* note: variable AR register array is not read */
+
+ /* then get panic info */
+ if (xoops->arch_hdr.totalsize > EXCEPT_MAX_HDR_SIZE) {
+ dev_err(sdev->dev, "invalid header size 0x%x. FW oops is bogus\n",
+ xoops->arch_hdr.totalsize);
+ return;
+ }
+ offset += xoops->arch_hdr.totalsize;
+ sof_block_read(sdev, sdev->mmio_bar, offset,
+ panic_info, sizeof(*panic_info));
+
+ /* then get the stack */
+ offset += sizeof(*panic_info);
+ sof_block_read(sdev, sdev->mmio_bar, offset, stack,
+ stack_words * sizeof(u32));
+}
+
+/* dump the first 8 dwords representing the extended ROM status */
+void hda_dsp_dump_ext_rom_status(struct snd_sof_dev *sdev, const char *level,
+ u32 flags)
+{
+ const struct sof_intel_dsp_desc *chip;
+ char msg[128];
+ int len = 0;
+ u32 value;
+ int i;
+
+ chip = get_chip_info(sdev->pdata);
+ for (i = 0; i < HDA_EXT_ROM_STATUS_SIZE; i++) {
+ value = snd_sof_dsp_read(sdev, HDA_DSP_BAR, chip->rom_status_reg + i * 0x4);
+ len += scnprintf(msg + len, sizeof(msg) - len, " 0x%x", value);
+ }
+
+ dev_printk(level, sdev->dev, "extended rom status: %s", msg);
+
+}
+
+void hda_dsp_dump(struct snd_sof_dev *sdev, u32 flags)
+{
+ char *level = (flags & SOF_DBG_DUMP_OPTIONAL) ? KERN_DEBUG : KERN_ERR;
+ struct sof_ipc_dsp_oops_xtensa xoops;
+ struct sof_ipc_panic_info panic_info;
+ u32 stack[HDA_DSP_STACK_DUMP_SIZE];
+
+ /* print ROM/FW status */
+ hda_dsp_get_state(sdev, level);
+
+ /* The firmware register dump only available with IPC3 */
+ if (flags & SOF_DBG_DUMP_REGS && sdev->pdata->ipc_type == SOF_IPC_TYPE_3) {
+ u32 status = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_SRAM_REG_FW_STATUS);
+ u32 panic = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_SRAM_REG_FW_TRACEP);
+
+ hda_dsp_get_registers(sdev, &xoops, &panic_info, stack,
+ HDA_DSP_STACK_DUMP_SIZE);
+ sof_print_oops_and_stack(sdev, level, status, panic, &xoops,
+ &panic_info, stack, HDA_DSP_STACK_DUMP_SIZE);
+ } else {
+ hda_dsp_dump_ext_rom_status(sdev, level, flags);
+ }
+}
+EXPORT_SYMBOL_NS(hda_dsp_dump, "SND_SOC_SOF_INTEL_HDA_COMMON");
diff --git a/sound/soc/sof/intel/hda-ipc.c b/sound/soc/sof/intel/hda-ipc.c
index c91aa951df22..f3fbf43a70c2 100644
--- a/sound/soc/sof/intel/hda-ipc.c
+++ b/sound/soc/sof/intel/hda-ipc.c
@@ -3,7 +3,7 @@
// 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) 2018 Intel Corporation. All rights reserved.
+// Copyright(c) 2018 Intel Corporation
//
// Authors: Liam Girdwood <liam.r.girdwood@linux.intel.com>
// Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
@@ -15,8 +15,16 @@
* Hardware interface for generic Intel audio DSP HDA IP
*/
+#include <sound/hda_register.h>
+#include <sound/sof/ipc4/header.h>
+#include <trace/events/sof_intel.h>
#include "../ops.h"
#include "hda.h"
+#include "telemetry.h"
+
+EXPORT_TRACEPOINT_SYMBOL(sof_intel_ipc_firmware_initiated);
+EXPORT_TRACEPOINT_SYMBOL(sof_intel_ipc_firmware_response);
+EXPORT_TRACEPOINT_SYMBOL(sof_intel_hda_irq_ipc_check);
static void hda_dsp_ipc_host_done(struct snd_sof_dev *sdev)
{
@@ -64,13 +72,67 @@ int hda_dsp_ipc_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg)
return 0;
}
+EXPORT_SYMBOL_NS(hda_dsp_ipc_send_msg, "SND_SOC_SOF_INTEL_HDA_COMMON");
+
+static inline bool hda_dsp_ipc4_pm_msg(u32 primary)
+{
+ /* pm setting is only supported by module msg */
+ if (SOF_IPC4_MSG_IS_MODULE_MSG(primary) != SOF_IPC4_MODULE_MSG)
+ return false;
+
+ if (SOF_IPC4_MSG_TYPE_GET(primary) == SOF_IPC4_MOD_SET_DX ||
+ SOF_IPC4_MSG_TYPE_GET(primary) == SOF_IPC4_MOD_SET_D0IX)
+ return true;
+
+ return false;
+}
+
+void hda_dsp_ipc4_schedule_d0i3_work(struct sof_intel_hda_dev *hdev,
+ struct snd_sof_ipc_msg *msg)
+{
+ struct sof_ipc4_msg *msg_data = msg->msg_data;
+
+ /* Schedule a delayed work for d0i3 entry after sending non-pm ipc msg */
+ if (hda_dsp_ipc4_pm_msg(msg_data->primary))
+ return;
+
+ mod_delayed_work(system_wq, &hdev->d0i3_work,
+ msecs_to_jiffies(SOF_HDA_D0I3_WORK_DELAY_MS));
+}
+EXPORT_SYMBOL_NS(hda_dsp_ipc4_schedule_d0i3_work, "SND_SOC_SOF_INTEL_HDA_COMMON");
+
+int hda_dsp_ipc4_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg)
+{
+ struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata;
+ struct sof_ipc4_msg *msg_data = msg->msg_data;
+
+ if (hda_ipc4_tx_is_busy(sdev)) {
+ hdev->delayed_ipc_tx_msg = msg;
+ return 0;
+ }
+
+ hdev->delayed_ipc_tx_msg = NULL;
+
+ /* send the message via mailbox */
+ if (msg_data->data_size)
+ sof_mailbox_write(sdev, sdev->host_box.offset, msg_data->data_ptr,
+ msg_data->data_size);
+
+ snd_sof_dsp_write(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCIE, msg_data->extension);
+ snd_sof_dsp_write(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCI,
+ msg_data->primary | HDA_DSP_REG_HIPCI_BUSY);
+
+ hda_dsp_ipc4_schedule_d0i3_work(hdev, msg);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS(hda_dsp_ipc4_send_msg, "SND_SOC_SOF_INTEL_HDA_COMMON");
void hda_dsp_ipc_get_reply(struct snd_sof_dev *sdev)
{
struct snd_sof_ipc_msg *msg = sdev->msg;
struct sof_ipc_reply reply;
struct sof_ipc_cmd_hdr *hdr;
- int ret = 0;
/*
* Sometimes, there is unexpected reply ipc arriving. The reply
@@ -94,36 +156,96 @@ void hda_dsp_ipc_get_reply(struct snd_sof_dev *sdev)
reply.hdr.cmd = SOF_IPC_GLB_REPLY;
reply.hdr.size = sizeof(reply);
memcpy(msg->reply_data, &reply, sizeof(reply));
- goto out;
+
+ msg->reply_error = 0;
+ } else {
+ snd_sof_ipc_get_reply(sdev);
}
+}
+EXPORT_SYMBOL_NS(hda_dsp_ipc_get_reply, "SND_SOC_SOF_INTEL_HDA_COMMON");
- /* get IPC reply from DSP in the mailbox */
- sof_mailbox_read(sdev, sdev->host_box.offset, &reply,
- sizeof(reply));
+irqreturn_t hda_dsp_ipc4_irq_thread(int irq, void *context)
+{
+ struct sof_ipc4_msg notification_data = {{ 0 }};
+ struct snd_sof_dev *sdev = context;
+ bool ack_received = false;
+ bool ipc_irq = false;
+ u32 hipcie, hipct;
- if (reply.error < 0) {
- memcpy(msg->reply_data, &reply, sizeof(reply));
- ret = reply.error;
- } else {
- /* reply correct size ? */
- if (reply.hdr.size != msg->reply_size &&
- /* getter payload is never known upfront */
- !(reply.hdr.cmd & SOF_IPC_GLB_PROBE)) {
- dev_err(sdev->dev, "error: reply expected %zu got %u bytes\n",
- msg->reply_size, reply.hdr.size);
- ret = -EINVAL;
+ hipcie = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCIE);
+ hipct = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCT);
+
+ if (hipcie & HDA_DSP_REG_HIPCIE_DONE) {
+ /* DSP received the message */
+ snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCCTL,
+ HDA_DSP_REG_HIPCCTL_DONE, 0);
+ hda_dsp_ipc_dsp_done(sdev);
+
+ ipc_irq = true;
+ ack_received = true;
+ }
+
+ if (hipct & HDA_DSP_REG_HIPCT_BUSY) {
+ /* Message from DSP (reply or notification) */
+ u32 hipcte = snd_sof_dsp_read(sdev, HDA_DSP_BAR,
+ HDA_DSP_REG_HIPCTE);
+ u32 primary = hipct & HDA_DSP_REG_HIPCT_MSG_MASK;
+ u32 extension = hipcte & HDA_DSP_REG_HIPCTE_MSG_MASK;
+
+ /* mask BUSY interrupt */
+ snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCCTL,
+ HDA_DSP_REG_HIPCCTL_BUSY, 0);
+
+ if (primary & SOF_IPC4_MSG_DIR_MASK) {
+ /* Reply received */
+ if (likely(sdev->fw_state == SOF_FW_BOOT_COMPLETE)) {
+ struct sof_ipc4_msg *data = sdev->ipc->msg.reply_data;
+
+ data->primary = primary;
+ data->extension = extension;
+
+ spin_lock_irq(&sdev->ipc_lock);
+
+ snd_sof_ipc_get_reply(sdev);
+ hda_dsp_ipc_host_done(sdev);
+ snd_sof_ipc_reply(sdev, data->primary);
+
+ spin_unlock_irq(&sdev->ipc_lock);
+ } else {
+ dev_dbg_ratelimited(sdev->dev,
+ "IPC reply before FW_READY: %#x|%#x\n",
+ primary, extension);
+ }
+ } else {
+ /* Notification received */
+
+ notification_data.primary = primary;
+ notification_data.extension = extension;
+ sdev->ipc->msg.rx_data = &notification_data;
+ snd_sof_ipc_msgs_rx(sdev);
+ sdev->ipc->msg.rx_data = NULL;
+
+ /* Let DSP know that we have finished processing the message */
+ hda_dsp_ipc_host_done(sdev);
}
- /* read the message */
- if (msg->reply_size > 0)
- sof_mailbox_read(sdev, sdev->host_box.offset,
- msg->reply_data, msg->reply_size);
+ ipc_irq = true;
}
-out:
- msg->reply_error = ret;
+ if (!ipc_irq)
+ /* This interrupt is not shared so no need to return IRQ_NONE. */
+ dev_dbg_ratelimited(sdev->dev, "nothing to do in IPC IRQ thread\n");
+
+ if (ack_received) {
+ struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata;
+ if (hdev->delayed_ipc_tx_msg)
+ hda_dsp_ipc4_send_msg(sdev, hdev->delayed_ipc_tx_msg);
+ }
+
+ return IRQ_HANDLED;
}
+EXPORT_SYMBOL_NS(hda_dsp_ipc4_irq_thread, "SND_SOC_SOF_INTEL_HDA_COMMON");
/* IPC handler thread */
irqreturn_t hda_dsp_ipc_irq_thread(int irq, void *context)
@@ -149,9 +271,7 @@ irqreturn_t hda_dsp_ipc_irq_thread(int irq, void *context)
msg = hipci & HDA_DSP_REG_HIPCI_MSG_MASK;
msg_ext = hipcie & HDA_DSP_REG_HIPCIE_MSG_MASK;
- dev_vdbg(sdev->dev,
- "ipc: firmware response, msg:0x%x, msg_ext:0x%x\n",
- msg, msg_ext);
+ trace_sof_intel_ipc_firmware_response(sdev, msg, msg_ext);
/* mask Done interrupt */
snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR,
@@ -168,16 +288,21 @@ irqreturn_t hda_dsp_ipc_irq_thread(int irq, void *context)
* place, the message might not yet be marked as expecting a
* reply.
*/
- spin_lock_irq(&sdev->ipc_lock);
+ if (likely(sdev->fw_state == SOF_FW_BOOT_COMPLETE)) {
+ spin_lock_irq(&sdev->ipc_lock);
- /* handle immediate reply from DSP core */
- hda_dsp_ipc_get_reply(sdev);
- snd_sof_ipc_reply(sdev, msg);
+ /* handle immediate reply from DSP core */
+ hda_dsp_ipc_get_reply(sdev);
+ snd_sof_ipc_reply(sdev, msg);
- /* set the done bit */
- hda_dsp_ipc_dsp_done(sdev);
+ /* set the done bit */
+ hda_dsp_ipc_dsp_done(sdev);
- spin_unlock_irq(&sdev->ipc_lock);
+ spin_unlock_irq(&sdev->ipc_lock);
+ } else {
+ dev_dbg_ratelimited(sdev->dev, "IPC reply before FW_READY: %#x\n",
+ msg);
+ }
ipc_irq = true;
}
@@ -187,9 +312,7 @@ irqreturn_t hda_dsp_ipc_irq_thread(int irq, void *context)
msg = hipct & HDA_DSP_REG_HIPCT_MSG_MASK;
msg_ext = hipcte & HDA_DSP_REG_HIPCTE_MSG_MASK;
- dev_vdbg(sdev->dev,
- "ipc: firmware initiated, msg:0x%x, msg_ext:0x%x\n",
- msg, msg_ext);
+ trace_sof_intel_ipc_firmware_initiated(sdev, msg, msg_ext);
/* mask BUSY interrupt */
snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR,
@@ -198,8 +321,23 @@ irqreturn_t hda_dsp_ipc_irq_thread(int irq, void *context)
/* handle messages from DSP */
if ((hipct & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC) {
- /* this is a PANIC message !! */
- snd_sof_dsp_panic(sdev, HDA_DSP_PANIC_OFFSET(msg_ext));
+ struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
+ bool non_recoverable = true;
+
+ /*
+ * This is a PANIC message!
+ *
+ * If it is arriving during firmware boot and it is not
+ * the last boot attempt then change the non_recoverable
+ * to false as the DSP might be able to boot in the next
+ * iteration(s)
+ */
+ if (sdev->fw_state == SOF_FW_BOOT_IN_PROGRESS &&
+ hda->boot_iteration < HDA_FW_BOOT_ATTEMPTS)
+ non_recoverable = false;
+
+ snd_sof_dsp_panic(sdev, HDA_DSP_PANIC_OFFSET(msg_ext),
+ non_recoverable);
} else {
/* normal message - process normally */
snd_sof_ipc_msgs_rx(sdev);
@@ -220,16 +358,21 @@ irqreturn_t hda_dsp_ipc_irq_thread(int irq, void *context)
return IRQ_HANDLED;
}
+EXPORT_SYMBOL_NS(hda_dsp_ipc_irq_thread, "SND_SOC_SOF_INTEL_HDA_COMMON");
/* Check if an IPC IRQ occurred */
bool hda_dsp_check_ipc_irq(struct snd_sof_dev *sdev)
{
+ struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
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);
- dev_vdbg(sdev->dev, "irq handler: irq_status:0x%x\n", irq_status);
+ trace_sof_intel_hda_irq_ipc_check(sdev, irq_status);
/* invalid message ? */
if (irq_status == 0xffffffff)
@@ -239,62 +382,175 @@ bool hda_dsp_check_ipc_irq(struct snd_sof_dev *sdev)
if (irq_status & HDA_DSP_ADSPIS_IPC)
ret = true;
+ /* CLDMA message ? */
+ if (irq_status & HDA_DSP_ADSPIS_CL_DMA) {
+ hda->code_loading = 0;
+ wake_up(&hda->waitq);
+ ret = false;
+ }
+
out:
return ret;
}
+EXPORT_SYMBOL_NS(hda_dsp_check_ipc_irq, "SND_SOC_SOF_INTEL_HDA_COMMON");
int hda_dsp_ipc_get_mailbox_offset(struct snd_sof_dev *sdev)
{
return HDA_DSP_MBOX_UPLINK_OFFSET;
}
+EXPORT_SYMBOL_NS(hda_dsp_ipc_get_mailbox_offset, "SND_SOC_SOF_INTEL_HDA_COMMON");
int hda_dsp_ipc_get_window_offset(struct snd_sof_dev *sdev, u32 id)
{
return SRAM_WINDOW_OFFSET(id);
}
+EXPORT_SYMBOL_NS(hda_dsp_ipc_get_window_offset, "SND_SOC_SOF_INTEL_HDA_COMMON");
-void hda_ipc_msg_data(struct snd_sof_dev *sdev,
- struct snd_pcm_substream *substream,
- void *p, size_t sz)
+int hda_ipc_msg_data(struct snd_sof_dev *sdev,
+ struct snd_sof_pcm_stream *sps,
+ void *p, size_t sz)
{
- if (!substream || !sdev->stream_box.size) {
+ if (!sps || !sdev->stream_box.size) {
sof_mailbox_read(sdev, sdev->dsp_box.offset, p, sz);
} else {
+ struct snd_pcm_substream *substream = sps->substream;
struct hdac_stream *hstream = substream->runtime->private_data;
struct sof_intel_hda_stream *hda_stream;
hda_stream = container_of(hstream,
struct sof_intel_hda_stream,
- hda_stream.hstream);
+ hext_stream.hstream);
/* The stream might already be closed */
- if (hstream)
- sof_mailbox_read(sdev, hda_stream->stream.posn_offset,
- p, sz);
+ if (!hstream)
+ return -ESTRPIPE;
+
+ sof_mailbox_read(sdev, hda_stream->sof_intel_stream.posn_offset, p, sz);
}
+
+ return 0;
}
+EXPORT_SYMBOL_NS(hda_ipc_msg_data, "SND_SOC_SOF_INTEL_HDA_COMMON");
-int hda_ipc_pcm_params(struct snd_sof_dev *sdev,
- struct snd_pcm_substream *substream,
- const struct sof_ipc_pcm_params_reply *reply)
+int hda_set_stream_data_offset(struct snd_sof_dev *sdev,
+ struct snd_sof_pcm_stream *sps,
+ size_t posn_offset)
{
+ struct snd_pcm_substream *substream = sps->substream;
struct hdac_stream *hstream = substream->runtime->private_data;
struct sof_intel_hda_stream *hda_stream;
- /* validate offset */
- size_t posn_offset = reply->posn_offset;
hda_stream = container_of(hstream, struct sof_intel_hda_stream,
- hda_stream.hstream);
+ hext_stream.hstream);
/* check for unaligned offset or overflow */
if (posn_offset > sdev->stream_box.size ||
posn_offset % sizeof(struct sof_ipc_stream_posn) != 0)
return -EINVAL;
- hda_stream->stream.posn_offset = sdev->stream_box.offset + posn_offset;
+ hda_stream->sof_intel_stream.posn_offset = sdev->stream_box.offset + posn_offset;
dev_dbg(sdev->dev, "pcm: stream dir %d, posn mailbox offset is %zu",
- substream->stream, hda_stream->stream.posn_offset);
+ substream->stream, hda_stream->sof_intel_stream.posn_offset);
return 0;
}
+EXPORT_SYMBOL_NS(hda_set_stream_data_offset, "SND_SOC_SOF_INTEL_HDA_COMMON");
+
+void hda_ipc4_dsp_dump(struct snd_sof_dev *sdev, u32 flags)
+{
+ char *level = (flags & SOF_DBG_DUMP_OPTIONAL) ? KERN_DEBUG : KERN_ERR;
+
+ /* print ROM/FW status */
+ hda_dsp_get_state(sdev, level);
+
+ if (flags & SOF_DBG_DUMP_REGS)
+ sof_ipc4_intel_dump_telemetry_state(sdev, flags);
+ else
+ hda_dsp_dump_ext_rom_status(sdev, level, flags);
+}
+EXPORT_SYMBOL_NS(hda_ipc4_dsp_dump, "SND_SOC_SOF_INTEL_HDA_COMMON");
+
+bool hda_check_ipc_irq(struct snd_sof_dev *sdev)
+{
+ const struct sof_intel_dsp_desc *chip;
+
+ chip = get_chip_info(sdev->pdata);
+ if (chip && chip->check_ipc_irq)
+ return chip->check_ipc_irq(sdev);
+
+ return false;
+}
+EXPORT_SYMBOL_NS(hda_check_ipc_irq, "SND_SOC_SOF_INTEL_HDA_COMMON");
+
+void hda_ipc_irq_dump(struct snd_sof_dev *sdev)
+{
+ u32 adspis;
+ u32 intsts;
+ u32 intctl;
+ u32 ppsts;
+ u8 rirbsts;
+
+ /* read key IRQ stats and config registers */
+ adspis = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPIS);
+ intsts = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTSTS);
+ intctl = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTCTL);
+ ppsts = snd_sof_dsp_read(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPSTS);
+ rirbsts = snd_sof_dsp_read8(sdev, HDA_DSP_HDA_BAR, AZX_REG_RIRBSTS);
+
+ dev_err(sdev->dev, "hda irq intsts 0x%8.8x intlctl 0x%8.8x rirb %2.2x\n",
+ intsts, intctl, rirbsts);
+ dev_err(sdev->dev, "dsp irq ppsts 0x%8.8x adspis 0x%8.8x\n", ppsts, adspis);
+}
+EXPORT_SYMBOL_NS(hda_ipc_irq_dump, "SND_SOC_SOF_INTEL_HDA_COMMON");
+
+void hda_ipc_dump(struct snd_sof_dev *sdev)
+{
+ u32 hipcie;
+ u32 hipct;
+ u32 hipcctl;
+
+ hda_ipc_irq_dump(sdev);
+
+ /* read IPC status */
+ hipcie = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCIE);
+ hipct = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCT);
+ hipcctl = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCCTL);
+
+ /* dump the IPC regs */
+ /* TODO: parse the raw msg */
+ dev_err(sdev->dev, "host status 0x%8.8x dsp status 0x%8.8x mask 0x%8.8x\n",
+ hipcie, hipct, hipcctl);
+}
+EXPORT_SYMBOL_NS(hda_ipc_dump, "SND_SOC_SOF_INTEL_HDA_COMMON");
+
+void hda_ipc4_dump(struct snd_sof_dev *sdev)
+{
+ u32 hipci, hipcie, hipct, hipcte, hipcctl;
+
+ hda_ipc_irq_dump(sdev);
+
+ hipci = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCI);
+ hipcie = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCIE);
+ hipct = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCT);
+ hipcte = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCTE);
+ hipcctl = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCCTL);
+
+ /* dump the IPC regs */
+ /* TODO: parse the raw msg */
+ dev_err(sdev->dev, "Host IPC initiator: %#x|%#x, target: %#x|%#x, ctl: %#x\n",
+ hipci, hipcie, hipct, hipcte, hipcctl);
+}
+EXPORT_SYMBOL_NS(hda_ipc4_dump, "SND_SOC_SOF_INTEL_HDA_COMMON");
+
+bool hda_ipc4_tx_is_busy(struct snd_sof_dev *sdev)
+{
+ struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
+ const struct sof_intel_dsp_desc *chip = hda->desc;
+ u32 val;
+
+ val = snd_sof_dsp_read(sdev, HDA_DSP_BAR, chip->ipc_req);
+
+ return !!(val & chip->ipc_req_mask);
+}
+EXPORT_SYMBOL_NS(hda_ipc4_tx_is_busy, "SND_SOC_SOF_INTEL_HDA_COMMON");
diff --git a/sound/soc/sof/intel/hda-ipc.h b/sound/soc/sof/intel/hda-ipc.h
index ade4c3191a39..ad9478b8c390 100644
--- a/sound/soc/sof/intel/hda-ipc.h
+++ b/sound/soc/sof/intel/hda-ipc.h
@@ -3,7 +3,7 @@
* 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.
+ * Copyright(c) 2019 Intel Corporation
*
* Author: Keyon Jie <yang.jie@linux.intel.com>
*/
@@ -48,4 +48,9 @@
#define HDA_PM_PG_STREAMING BIT(1)
#define HDA_PM_PG_RSVD BIT(0)
+irqreturn_t cnl_ipc_irq_thread(int irq, void *context);
+int cnl_ipc_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg);
+void cnl_ipc_dump(struct snd_sof_dev *sdev);
+void cnl_ipc4_dump(struct snd_sof_dev *sdev);
+
#endif
diff --git a/sound/soc/sof/intel/hda-loader-skl.c b/sound/soc/sof/intel/hda-loader-skl.c
new file mode 100644
index 000000000000..f38178c904de
--- /dev/null
+++ b/sound/soc/sof/intel/hda-loader-skl.c
@@ -0,0 +1,578 @@
+// SPDX-License-Identifier: (GPL-2.0-only 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) 2018-2022 Intel Corporation
+//
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/firmware.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <sound/hdaudio_ext.h>
+#include <sound/sof.h>
+#include <sound/pcm_params.h>
+
+#include "../sof-priv.h"
+#include "../ops.h"
+#include "hda.h"
+
+#define HDA_SKL_WAIT_TIMEOUT 500 /* 500 msec */
+#define HDA_SKL_CLDMA_MAX_BUFFER_SIZE (32 * PAGE_SIZE)
+
+/* Stream Reset */
+#define HDA_CL_SD_CTL_SRST_SHIFT 0
+#define HDA_CL_SD_CTL_SRST(x) (((x) & 0x1) << \
+ HDA_CL_SD_CTL_SRST_SHIFT)
+
+/* Stream Run */
+#define HDA_CL_SD_CTL_RUN_SHIFT 1
+#define HDA_CL_SD_CTL_RUN(x) (((x) & 0x1) << \
+ HDA_CL_SD_CTL_RUN_SHIFT)
+
+/* Interrupt On Completion Enable */
+#define HDA_CL_SD_CTL_IOCE_SHIFT 2
+#define HDA_CL_SD_CTL_IOCE(x) (((x) & 0x1) << \
+ HDA_CL_SD_CTL_IOCE_SHIFT)
+
+/* FIFO Error Interrupt Enable */
+#define HDA_CL_SD_CTL_FEIE_SHIFT 3
+#define HDA_CL_SD_CTL_FEIE(x) (((x) & 0x1) << \
+ HDA_CL_SD_CTL_FEIE_SHIFT)
+
+/* Descriptor Error Interrupt Enable */
+#define HDA_CL_SD_CTL_DEIE_SHIFT 4
+#define HDA_CL_SD_CTL_DEIE(x) (((x) & 0x1) << \
+ HDA_CL_SD_CTL_DEIE_SHIFT)
+
+/* FIFO Limit Change */
+#define HDA_CL_SD_CTL_FIFOLC_SHIFT 5
+#define HDA_CL_SD_CTL_FIFOLC(x) (((x) & 0x1) << \
+ HDA_CL_SD_CTL_FIFOLC_SHIFT)
+
+/* Stripe Control */
+#define HDA_CL_SD_CTL_STRIPE_SHIFT 16
+#define HDA_CL_SD_CTL_STRIPE(x) (((x) & 0x3) << \
+ HDA_CL_SD_CTL_STRIPE_SHIFT)
+
+/* Traffic Priority */
+#define HDA_CL_SD_CTL_TP_SHIFT 18
+#define HDA_CL_SD_CTL_TP(x) (((x) & 0x1) << \
+ HDA_CL_SD_CTL_TP_SHIFT)
+
+/* Bidirectional Direction Control */
+#define HDA_CL_SD_CTL_DIR_SHIFT 19
+#define HDA_CL_SD_CTL_DIR(x) (((x) & 0x1) << \
+ HDA_CL_SD_CTL_DIR_SHIFT)
+
+/* Stream Number */
+#define HDA_CL_SD_CTL_STRM_SHIFT 20
+#define HDA_CL_SD_CTL_STRM(x) (((x) & 0xf) << \
+ HDA_CL_SD_CTL_STRM_SHIFT)
+
+#define HDA_CL_SD_CTL_INT(x) \
+ (HDA_CL_SD_CTL_IOCE(x) | \
+ HDA_CL_SD_CTL_FEIE(x) | \
+ HDA_CL_SD_CTL_DEIE(x))
+
+#define HDA_CL_SD_CTL_INT_MASK \
+ (HDA_CL_SD_CTL_IOCE(1) | \
+ HDA_CL_SD_CTL_FEIE(1) | \
+ HDA_CL_SD_CTL_DEIE(1))
+
+#define DMA_ADDRESS_128_BITS_ALIGNMENT 7
+#define BDL_ALIGN(x) ((x) >> DMA_ADDRESS_128_BITS_ALIGNMENT)
+
+/* Buffer Descriptor List Lower Base Address */
+#define HDA_CL_SD_BDLPLBA_SHIFT 7
+#define HDA_CL_SD_BDLPLBA_MASK GENMASK(31, 7)
+#define HDA_CL_SD_BDLPLBA(x) \
+ ((BDL_ALIGN(lower_32_bits(x)) << HDA_CL_SD_BDLPLBA_SHIFT) & \
+ HDA_CL_SD_BDLPLBA_MASK)
+
+/* Buffer Descriptor List Upper Base Address */
+#define HDA_CL_SD_BDLPUBA(x) \
+ (upper_32_bits(x))
+
+/* Software Position in Buffer Enable */
+#define HDA_CL_SPBFIFO_SPBFCCTL_SPIBE_SHIFT 0
+#define HDA_CL_SPBFIFO_SPBFCCTL_SPIBE_MASK \
+ (1 << HDA_CL_SPBFIFO_SPBFCCTL_SPIBE_SHIFT)
+
+#define HDA_CL_SPBFIFO_SPBFCCTL_SPIBE(x) \
+ (((x) << HDA_CL_SPBFIFO_SPBFCCTL_SPIBE_SHIFT) & \
+ HDA_CL_SPBFIFO_SPBFCCTL_SPIBE_MASK)
+
+#define HDA_CL_DMA_SD_INT_COMPLETE 0x4
+
+static int cl_skl_cldma_setup_bdle(struct snd_sof_dev *sdev,
+ struct snd_dma_buffer *dmab_data,
+ __le32 **bdlp, int size, int with_ioc)
+{
+ phys_addr_t addr = virt_to_phys(dmab_data->area);
+ __le32 *bdl = *bdlp;
+
+ /*
+ * This code is simplified by using one fragment of physical memory and assuming
+ * all the code fits. This could be improved with scatter-gather but the firmware
+ * size is limited by DSP memory anyways
+ */
+ bdl[0] = cpu_to_le32(lower_32_bits(addr));
+ bdl[1] = cpu_to_le32(upper_32_bits(addr));
+ bdl[2] = cpu_to_le32(size);
+ bdl[3] = (!with_ioc) ? 0 : cpu_to_le32(0x01);
+
+ return 1; /* one fragment */
+}
+
+static void cl_skl_cldma_stream_run(struct snd_sof_dev *sdev, bool enable)
+{
+ int sd_offset = SOF_HDA_ADSP_LOADER_BASE;
+ unsigned char val;
+ int retries;
+ u32 run = enable ? 0x1 : 0;
+
+ snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR,
+ sd_offset + SOF_HDA_ADSP_REG_SD_CTL,
+ HDA_CL_SD_CTL_RUN(1), HDA_CL_SD_CTL_RUN(run));
+
+ retries = 300;
+ do {
+ udelay(3);
+
+ /* waiting for hardware to report the stream Run bit set */
+ val = snd_sof_dsp_read(sdev, HDA_DSP_BAR,
+ sd_offset + SOF_HDA_ADSP_REG_SD_CTL);
+ val &= HDA_CL_SD_CTL_RUN(1);
+ if (enable && val)
+ break;
+ else if (!enable && !val)
+ break;
+ } while (--retries);
+
+ if (retries == 0)
+ dev_err(sdev->dev, "%s: failed to set Run bit=%d enable=%d\n",
+ __func__, val, enable);
+}
+
+static void cl_skl_cldma_stream_clear(struct snd_sof_dev *sdev)
+{
+ int sd_offset = SOF_HDA_ADSP_LOADER_BASE;
+
+ /* make sure Run bit is cleared before setting stream register */
+ cl_skl_cldma_stream_run(sdev, 0);
+
+ /* Disable the Interrupt On Completion, FIFO Error Interrupt,
+ * Descriptor Error Interrupt and set the cldma stream number to 0.
+ */
+ snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR,
+ sd_offset + SOF_HDA_ADSP_REG_SD_CTL,
+ HDA_CL_SD_CTL_INT_MASK, HDA_CL_SD_CTL_INT(0));
+ snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR,
+ sd_offset + SOF_HDA_ADSP_REG_SD_CTL,
+ HDA_CL_SD_CTL_STRM(0xf), HDA_CL_SD_CTL_STRM(0));
+
+ snd_sof_dsp_write(sdev, HDA_DSP_BAR,
+ sd_offset + SOF_HDA_ADSP_REG_SD_BDLPL, HDA_CL_SD_BDLPLBA(0));
+ snd_sof_dsp_write(sdev, HDA_DSP_BAR,
+ sd_offset + SOF_HDA_ADSP_REG_SD_BDLPU, 0);
+
+ /* Set the Cyclic Buffer Length to 0. */
+ snd_sof_dsp_write(sdev, HDA_DSP_BAR,
+ sd_offset + SOF_HDA_ADSP_REG_SD_CBL, 0);
+ /* Set the Last Valid Index. */
+ snd_sof_dsp_write(sdev, HDA_DSP_BAR,
+ sd_offset + SOF_HDA_ADSP_REG_SD_LVI, 0);
+}
+
+static void cl_skl_cldma_setup_spb(struct snd_sof_dev *sdev,
+ unsigned int size, bool enable)
+{
+ int sd_offset = SOF_DSP_REG_CL_SPBFIFO;
+
+ if (enable)
+ snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR,
+ sd_offset + SOF_HDA_ADSP_REG_CL_SPBFIFO_SPBFCCTL,
+ HDA_CL_SPBFIFO_SPBFCCTL_SPIBE_MASK,
+ HDA_CL_SPBFIFO_SPBFCCTL_SPIBE(1));
+
+ snd_sof_dsp_write(sdev, HDA_DSP_BAR,
+ sd_offset + SOF_HDA_ADSP_REG_CL_SPBFIFO_SPIB, size);
+}
+
+static void cl_skl_cldma_set_intr(struct snd_sof_dev *sdev, bool enable)
+{
+ u32 val = enable ? HDA_DSP_ADSPIC_CL_DMA : 0;
+
+ snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPIC,
+ HDA_DSP_ADSPIC_CL_DMA, val);
+}
+
+static void cl_skl_cldma_cleanup_spb(struct snd_sof_dev *sdev)
+{
+ int sd_offset = SOF_DSP_REG_CL_SPBFIFO;
+
+ snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR,
+ sd_offset + SOF_HDA_ADSP_REG_CL_SPBFIFO_SPBFCCTL,
+ HDA_CL_SPBFIFO_SPBFCCTL_SPIBE_MASK,
+ HDA_CL_SPBFIFO_SPBFCCTL_SPIBE(0));
+
+ snd_sof_dsp_write(sdev, HDA_DSP_BAR,
+ sd_offset + SOF_HDA_ADSP_REG_CL_SPBFIFO_SPIB, 0);
+}
+
+static void cl_skl_cldma_setup_controller(struct snd_sof_dev *sdev,
+ struct snd_dma_buffer *dmab_bdl,
+ unsigned int max_size, u32 count)
+{
+ int sd_offset = SOF_HDA_ADSP_LOADER_BASE;
+
+ /* Clear the stream first and then set it. */
+ cl_skl_cldma_stream_clear(sdev);
+
+ /* setting the stream register */
+ snd_sof_dsp_write(sdev, HDA_DSP_BAR,
+ sd_offset + SOF_HDA_ADSP_REG_SD_BDLPL,
+ HDA_CL_SD_BDLPLBA(dmab_bdl->addr));
+ snd_sof_dsp_write(sdev, HDA_DSP_BAR,
+ sd_offset + SOF_HDA_ADSP_REG_SD_BDLPU,
+ HDA_CL_SD_BDLPUBA(dmab_bdl->addr));
+
+ /* Set the Cyclic Buffer Length. */
+ snd_sof_dsp_write(sdev, HDA_DSP_BAR,
+ sd_offset + SOF_HDA_ADSP_REG_SD_CBL, max_size);
+ /* Set the Last Valid Index. */
+ snd_sof_dsp_write(sdev, HDA_DSP_BAR,
+ sd_offset + SOF_HDA_ADSP_REG_SD_LVI, count - 1);
+
+ /* Set the Interrupt On Completion, FIFO Error Interrupt,
+ * Descriptor Error Interrupt and the cldma stream number.
+ */
+ snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR,
+ sd_offset + SOF_HDA_ADSP_REG_SD_CTL,
+ HDA_CL_SD_CTL_INT_MASK, HDA_CL_SD_CTL_INT(1));
+ snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR,
+ sd_offset + SOF_HDA_ADSP_REG_SD_CTL,
+ HDA_CL_SD_CTL_STRM(0xf),
+ HDA_CL_SD_CTL_STRM(1));
+}
+
+static int cl_stream_prepare_skl(struct snd_sof_dev *sdev,
+ struct snd_dma_buffer *dmab,
+ struct snd_dma_buffer *dmab_bdl)
+
+{
+ unsigned int bufsize = HDA_SKL_CLDMA_MAX_BUFFER_SIZE;
+ __le32 *bdl;
+ int frags;
+ int ret;
+
+ ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, sdev->dev, bufsize, dmab);
+ if (ret < 0) {
+ dev_err(sdev->dev, "%s: failed to alloc fw buffer: %x\n", __func__, ret);
+ return ret;
+ }
+
+ ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, sdev->dev, bufsize, dmab_bdl);
+ if (ret < 0) {
+ dev_err(sdev->dev, "%s: failed to alloc blde: %x\n", __func__, ret);
+ snd_dma_free_pages(dmab);
+ return ret;
+ }
+
+ bdl = (__le32 *)dmab_bdl->area;
+ frags = cl_skl_cldma_setup_bdle(sdev, dmab, &bdl, bufsize, 1);
+ cl_skl_cldma_setup_controller(sdev, dmab_bdl, bufsize, frags);
+
+ return ret;
+}
+
+static void cl_cleanup_skl(struct snd_sof_dev *sdev,
+ struct snd_dma_buffer *dmab,
+ struct snd_dma_buffer *dmab_bdl)
+{
+ cl_skl_cldma_cleanup_spb(sdev);
+ cl_skl_cldma_stream_clear(sdev);
+ snd_dma_free_pages(dmab);
+ snd_dma_free_pages(dmab_bdl);
+}
+
+static int cl_dsp_init_skl(struct snd_sof_dev *sdev,
+ struct snd_dma_buffer *dmab,
+ struct snd_dma_buffer *dmab_bdl)
+{
+ struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
+ const struct sof_intel_dsp_desc *chip = hda->desc;
+ unsigned int status;
+ u32 flags;
+ int ret;
+
+ /* check if the init_core is already enabled, if yes, reset and make it run,
+ * if not, powerdown and enable it again.
+ */
+ if (hda_dsp_core_is_enabled(sdev, chip->init_core_mask)) {
+ /* if enabled, reset it, and run the init_core. */
+ ret = hda_dsp_core_stall_reset(sdev, chip->init_core_mask);
+ if (ret < 0)
+ goto err;
+
+ ret = hda_dsp_core_run(sdev, chip->init_core_mask);
+ if (ret < 0) {
+ dev_err(sdev->dev, "%s: dsp core start failed %d\n", __func__, ret);
+ goto err;
+ }
+ } else {
+ /* if not enabled, power down it first and then powerup and run
+ * the init_core.
+ */
+ ret = hda_dsp_core_reset_power_down(sdev, chip->init_core_mask);
+ if (ret < 0) {
+ dev_err(sdev->dev, "%s: dsp core0 disable fail: %d\n", __func__, ret);
+ goto err;
+ }
+ ret = hda_dsp_enable_core(sdev, chip->init_core_mask);
+ if (ret < 0) {
+ dev_err(sdev->dev, "%s: dsp core0 enable fail: %d\n", __func__, ret);
+ goto err;
+ }
+ }
+
+ /* prepare DMA for code loader stream */
+ ret = cl_stream_prepare_skl(sdev, dmab, dmab_bdl);
+ if (ret < 0) {
+ dev_err(sdev->dev, "%s: dma prepare fw loading err: %x\n", __func__, ret);
+ return ret;
+ }
+
+ /* enable the interrupt */
+ snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPIC,
+ HDA_DSP_ADSPIC_IPC, HDA_DSP_ADSPIC_IPC);
+
+ /* enable IPC DONE interrupt */
+ snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, chip->ipc_ctl,
+ HDA_DSP_REG_HIPCCTL_DONE,
+ HDA_DSP_REG_HIPCCTL_DONE);
+
+ /* enable IPC BUSY interrupt */
+ snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, chip->ipc_ctl,
+ HDA_DSP_REG_HIPCCTL_BUSY,
+ HDA_DSP_REG_HIPCCTL_BUSY);
+
+ /* polling the ROM init status information. */
+ ret = snd_sof_dsp_read_poll_timeout(sdev, HDA_DSP_BAR,
+ chip->rom_status_reg, status,
+ (FSR_TO_STATE_CODE(status)
+ == FSR_STATE_INIT_DONE),
+ HDA_DSP_REG_POLL_INTERVAL_US,
+ chip->rom_init_timeout *
+ USEC_PER_MSEC);
+ if (ret < 0)
+ goto err;
+
+ return ret;
+
+err:
+ flags = SOF_DBG_DUMP_PCI | SOF_DBG_DUMP_MBOX;
+
+ snd_sof_dsp_dbg_dump(sdev, "Boot failed\n", flags);
+ cl_cleanup_skl(sdev, dmab, dmab_bdl);
+ hda_dsp_core_reset_power_down(sdev, chip->init_core_mask);
+ return ret;
+}
+
+static void cl_skl_cldma_fill_buffer(struct snd_sof_dev *sdev,
+ struct snd_dma_buffer *dmab,
+ unsigned int bufsize,
+ unsigned int copysize,
+ const void *curr_pos,
+ bool intr_enable)
+{
+ struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
+
+ /* copy the image into the buffer with the maximum buffer size. */
+ unsigned int size = (bufsize == copysize) ? bufsize : copysize;
+
+ memcpy(dmab->area, curr_pos, size);
+
+ /* Set the wait condition for every load. */
+ hda->code_loading = 1;
+
+ /* Set the interrupt. */
+ if (intr_enable)
+ cl_skl_cldma_set_intr(sdev, true);
+
+ /* Set the SPB. */
+ cl_skl_cldma_setup_spb(sdev, size, true);
+
+ /* Trigger the code loading stream. */
+ cl_skl_cldma_stream_run(sdev, true);
+}
+
+static int cl_skl_cldma_wait_interruptible(struct snd_sof_dev *sdev,
+ bool intr_wait)
+{
+ struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
+ const struct sof_intel_dsp_desc *chip = hda->desc;
+ int sd_offset = SOF_HDA_ADSP_LOADER_BASE;
+ u8 cl_dma_intr_status;
+
+ /*
+ * Wait for CLDMA interrupt to inform the binary segment transfer is
+ * complete.
+ */
+ if (!wait_event_timeout(hda->waitq, !hda->code_loading,
+ msecs_to_jiffies(HDA_SKL_WAIT_TIMEOUT))) {
+ dev_err(sdev->dev, "cldma copy timeout\n");
+ dev_err(sdev->dev, "ROM code=%#x: FW status=%#x\n",
+ snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_SRAM_REG_ROM_ERROR),
+ snd_sof_dsp_read(sdev, HDA_DSP_BAR, chip->rom_status_reg));
+ return -EIO;
+ }
+
+ /* now check DMA interrupt status */
+ cl_dma_intr_status = snd_sof_dsp_read(sdev, HDA_DSP_BAR,
+ sd_offset + SOF_HDA_ADSP_REG_SD_STS);
+
+ if (!(cl_dma_intr_status & HDA_CL_DMA_SD_INT_COMPLETE)) {
+ dev_err(sdev->dev, "cldma copy failed\n");
+ return -EIO;
+ }
+
+ dev_dbg(sdev->dev, "cldma buffer copy complete\n");
+ return 0;
+}
+
+static int
+cl_skl_cldma_copy_to_buf(struct snd_sof_dev *sdev,
+ struct snd_dma_buffer *dmab,
+ const void *bin,
+ u32 total_size, u32 bufsize)
+{
+ unsigned int bytes_left = total_size;
+ const void *curr_pos = bin;
+ int ret;
+
+ if (total_size <= 0)
+ return -EINVAL;
+
+ while (bytes_left > 0) {
+ if (bytes_left > bufsize) {
+ dev_dbg(sdev->dev, "cldma copy %#x bytes\n", bufsize);
+
+ cl_skl_cldma_fill_buffer(sdev, dmab, bufsize, bufsize, curr_pos, true);
+
+ ret = cl_skl_cldma_wait_interruptible(sdev, false);
+ if (ret < 0) {
+ dev_err(sdev->dev, "%s: fw failed to load. %#x bytes remaining\n",
+ __func__, bytes_left);
+ return ret;
+ }
+
+ bytes_left -= bufsize;
+ curr_pos += bufsize;
+ } else {
+ dev_dbg(sdev->dev, "cldma copy %#x bytes\n", bytes_left);
+
+ cl_skl_cldma_set_intr(sdev, false);
+ cl_skl_cldma_fill_buffer(sdev, dmab, bufsize, bytes_left, curr_pos, false);
+ return 0;
+ }
+ }
+
+ return bytes_left;
+}
+
+static int cl_copy_fw_skl(struct snd_sof_dev *sdev,
+ struct snd_dma_buffer *dmab)
+
+{
+ const struct firmware *fw = sdev->basefw.fw;
+ struct firmware stripped_firmware;
+ unsigned int bufsize = HDA_SKL_CLDMA_MAX_BUFFER_SIZE;
+ int ret;
+
+ stripped_firmware.data = fw->data + sdev->basefw.payload_offset;
+ stripped_firmware.size = fw->size - sdev->basefw.payload_offset;
+
+ dev_dbg(sdev->dev, "firmware size: %#zx buffer size %#x\n", fw->size, bufsize);
+
+ ret = cl_skl_cldma_copy_to_buf(sdev, dmab, stripped_firmware.data,
+ stripped_firmware.size, bufsize);
+ if (ret < 0)
+ dev_err(sdev->dev, "%s: fw copy failed %d\n", __func__, ret);
+
+ return ret;
+}
+
+int hda_dsp_cl_boot_firmware_skl(struct snd_sof_dev *sdev)
+{
+ struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
+ const struct sof_intel_dsp_desc *chip = hda->desc;
+ struct snd_dma_buffer dmab_bdl;
+ struct snd_dma_buffer dmab;
+ unsigned int reg;
+ u32 flags;
+ int ret;
+
+ ret = cl_dsp_init_skl(sdev, &dmab, &dmab_bdl);
+
+ /* retry enabling core and ROM load. seemed to help */
+ if (ret < 0) {
+ ret = cl_dsp_init_skl(sdev, &dmab, &dmab_bdl);
+ if (ret < 0) {
+ dev_err(sdev->dev, "Error code=%#x: FW status=%#x\n",
+ snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_SRAM_REG_ROM_ERROR),
+ snd_sof_dsp_read(sdev, HDA_DSP_BAR, chip->rom_status_reg));
+ dev_err(sdev->dev, "Core En/ROM load fail:%d\n", ret);
+ return ret;
+ }
+ }
+
+ dev_dbg(sdev->dev, "ROM init successful\n");
+
+ /* at this point DSP ROM has been initialized and should be ready for
+ * code loading and firmware boot
+ */
+ ret = cl_copy_fw_skl(sdev, &dmab);
+ if (ret < 0) {
+ dev_err(sdev->dev, "%s: load firmware failed : %d\n", __func__, ret);
+ goto err;
+ }
+
+ ret = snd_sof_dsp_read_poll_timeout(sdev, HDA_DSP_BAR,
+ chip->rom_status_reg, reg,
+ (FSR_TO_STATE_CODE(reg)
+ == FSR_STATE_ROM_BASEFW_ENTERED),
+ HDA_DSP_REG_POLL_INTERVAL_US,
+ HDA_DSP_BASEFW_TIMEOUT_US);
+
+ dev_dbg(sdev->dev, "Firmware download successful, booting...\n");
+
+ cl_skl_cldma_stream_run(sdev, false);
+ cl_cleanup_skl(sdev, &dmab, &dmab_bdl);
+
+ if (!ret)
+ return chip->init_core_mask;
+
+ return ret;
+
+err:
+ flags = SOF_DBG_DUMP_PCI | SOF_DBG_DUMP_MBOX;
+
+ snd_sof_dsp_dbg_dump(sdev, "Boot failed\n", flags);
+
+ /* power down DSP */
+ hda_dsp_core_reset_power_down(sdev, chip->init_core_mask);
+ cl_skl_cldma_stream_run(sdev, false);
+ cl_cleanup_skl(sdev, &dmab, &dmab_bdl);
+
+ dev_err(sdev->dev, "%s: load fw failed err: %d\n", __func__, ret);
+ return ret;
+}
diff --git a/sound/soc/sof/intel/hda-loader.c b/sound/soc/sof/intel/hda-loader.c
index 441d05cda604..49085ca7b46b 100644
--- a/sound/soc/sof/intel/hda-loader.c
+++ b/sound/soc/sof/intel/hda-loader.c
@@ -3,7 +3,7 @@
// 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) 2018 Intel Corporation. All rights reserved.
+// Copyright(c) 2018 Intel Corporation
//
// Authors: Liam Girdwood <liam.r.girdwood@linux.intel.com>
// Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
@@ -17,102 +17,142 @@
#include <linux/firmware.h>
#include <sound/hdaudio_ext.h>
+#include <sound/hda_register.h>
#include <sound/sof.h>
+#include <sound/sof/ipc4/header.h>
+#include "ext_manifest.h"
+#include "../ipc4-priv.h"
#include "../ops.h"
+#include "../sof-priv.h"
#include "hda.h"
-#define HDA_FW_BOOT_ATTEMPTS 3
+static bool persistent_cl_buffer = true;
+module_param(persistent_cl_buffer, bool, 0444);
+MODULE_PARM_DESC(persistent_cl_buffer, "Persistent Code Loader DMA buffer "
+ "(default = Y, use N to force buffer re-allocation)");
-static int cl_stream_prepare(struct snd_sof_dev *sdev, unsigned int format,
- unsigned int size, struct snd_dma_buffer *dmab,
- int direction)
+static void hda_ssp_set_cbp_cfp(struct snd_sof_dev *sdev)
{
- struct hdac_ext_stream *dsp_stream;
- struct hdac_stream *hstream;
- struct pci_dev *pci = to_pci_dev(sdev->dev);
- int ret;
+ struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
+ const struct sof_intel_dsp_desc *chip = hda->desc;
+ int i;
- if (direction != SNDRV_PCM_STREAM_PLAYBACK) {
- dev_err(sdev->dev, "error: code loading DMA is playback only\n");
- return -EINVAL;
+ /* DSP is powered up, set all SSPs to clock consumer/codec provider mode */
+ for (i = 0; i < chip->ssp_count; i++) {
+ snd_sof_dsp_update_bits_unlocked(sdev, HDA_DSP_BAR,
+ chip->ssp_base_offset
+ + i * SSP_DEV_MEM_SIZE
+ + SSP_SSC1_OFFSET,
+ SSP_SET_CBP_CFP,
+ SSP_SET_CBP_CFP);
}
+}
- dsp_stream = hda_dsp_stream_get(sdev, direction);
+struct hdac_ext_stream*
+hda_cl_prepare(struct device *dev, unsigned int format, unsigned int size,
+ struct snd_dma_buffer *dmab, bool persistent_buffer, int direction,
+ bool is_iccmax)
+{
+ struct snd_sof_dev *sdev = dev_get_drvdata(dev);
+ struct hdac_ext_stream *hext_stream;
+ struct hdac_stream *hstream;
+ int ret;
- if (!dsp_stream) {
+ hext_stream = hda_dsp_stream_get(sdev, direction, 0);
+
+ if (!hext_stream) {
dev_err(sdev->dev, "error: no stream available\n");
- return -ENODEV;
+ return ERR_PTR(-ENODEV);
}
- hstream = &dsp_stream->hstream;
+ hstream = &hext_stream->hstream;
hstream->substream = NULL;
- /* allocate DMA buffer */
- ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV_SG, &pci->dev, size, dmab);
- if (ret < 0) {
- dev_err(sdev->dev, "error: memory alloc failed: %x\n", ret);
- goto error;
+ /*
+ * Allocate DMA buffer if it is temporary or if the buffer is intended
+ * to be persistent but not yet allocated.
+ * We cannot rely solely on !dmab->area as caller might use a struct on
+ * stack (when it is temporary) without clearing it to 0.
+ */
+ if (!persistent_buffer || !dmab->area) {
+ ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV_SG, dev, size, dmab);
+ if (ret < 0) {
+ dev_err(sdev->dev, "%s: memory alloc failed: %d\n",
+ __func__, ret);
+ goto out_put;
+ }
}
hstream->period_bytes = 0;/* initialize period_bytes */
hstream->format_val = format;
hstream->bufsize = size;
- ret = hda_dsp_stream_hw_params(sdev, dsp_stream, dmab, NULL);
- if (ret < 0) {
- dev_err(sdev->dev, "error: hdac prepare failed: %x\n", ret);
- goto error;
+ if (is_iccmax) {
+ ret = hda_dsp_iccmax_stream_hw_params(sdev, hext_stream, dmab, NULL);
+ if (ret < 0) {
+ dev_err(sdev->dev, "error: iccmax stream prepare failed: %d\n", ret);
+ goto out_free;
+ }
+ } else {
+ ret = hda_dsp_stream_hw_params(sdev, hext_stream, dmab, NULL);
+ if (ret < 0) {
+ dev_err(sdev->dev, "error: hdac prepare failed: %d\n", ret);
+ goto out_free;
+ }
+ hda_dsp_stream_spib_config(sdev, hext_stream, HDA_DSP_SPIB_ENABLE, size);
}
- hda_dsp_stream_spib_config(sdev, dsp_stream, HDA_DSP_SPIB_ENABLE, size);
+ return hext_stream;
- return hstream->stream_tag;
-
-error:
- hda_dsp_stream_put(sdev, direction, hstream->stream_tag);
+out_free:
snd_dma_free_pages(dmab);
- return ret;
+ dmab->area = NULL;
+ dmab->bytes = 0;
+ hstream->bufsize = 0;
+ hstream->format_val = 0;
+out_put:
+ hda_dsp_stream_put(sdev, direction, hstream->stream_tag);
+ return ERR_PTR(ret);
}
+EXPORT_SYMBOL_NS(hda_cl_prepare, "SND_SOC_SOF_INTEL_HDA_COMMON");
/*
- * first boot sequence has some extra steps. core 0 waits for power
- * status on core 1, so power up core 1 also momentarily, keep it in
- * reset/stall and then turn it off
+ * first boot sequence has some extra steps.
+ * power on all host managed cores and only unstall/run the boot core to boot the
+ * DSP then turn off all non boot cores (if any) is powered on.
*/
-static int cl_dsp_init(struct snd_sof_dev *sdev, const void *fwdata,
- u32 fwsize, int stream_tag)
+int cl_dsp_init(struct snd_sof_dev *sdev, int stream_tag, bool imr_boot)
{
struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
const struct sof_intel_dsp_desc *chip = hda->desc;
- unsigned int status;
+ unsigned int status, target_status;
+ u32 flags, ipc_hdr, j;
+ unsigned long mask;
+ char *dump_msg;
int ret;
- int i;
/* step 1: power up corex */
- ret = hda_dsp_core_power_up(sdev, chip->cores_mask);
+ ret = hda_dsp_core_power_up(sdev, chip->host_managed_cores_mask);
if (ret < 0) {
- dev_err(sdev->dev, "error: dsp core 0/1 power up failed\n");
+ if (hda->boot_iteration == HDA_FW_BOOT_ATTEMPTS)
+ dev_err(sdev->dev, "error: dsp core 0/1 power up failed\n");
goto err;
}
- /* DSP is powered up, set all SSPs to slave mode */
- for (i = 0; i < chip->ssp_count; i++) {
- snd_sof_dsp_update_bits_unlocked(sdev, HDA_DSP_BAR,
- chip->ssp_base_offset
- + i * SSP_DEV_MEM_SIZE
- + SSP_SSC1_OFFSET,
- SSP_SET_SLAVE,
- SSP_SET_SLAVE);
- }
+ hda_ssp_set_cbp_cfp(sdev);
+
+ /* step 2: Send ROM_CONTROL command (stream_tag is ignored for IMR boot) */
+ ipc_hdr = chip->ipc_req_mask | HDA_DSP_ROM_IPC_CONTROL;
+ if (!imr_boot)
+ ipc_hdr |= HDA_DSP_ROM_IPC_PURGE_FW | ((stream_tag - 1) << 9);
- /* step 2: purge FW request */
- snd_sof_dsp_write(sdev, HDA_DSP_BAR, chip->ipc_req,
- chip->ipc_req_mask | (HDA_DSP_IPC_PURGE_FW |
- ((stream_tag - 1) << 9)));
+ snd_sof_dsp_write(sdev, HDA_DSP_BAR, chip->ipc_req, ipc_hdr);
/* step 3: unset core 0 reset state & unstall/run core 0 */
- ret = hda_dsp_core_run(sdev, HDA_DSP_CORE_MASK(0));
+ ret = hda_dsp_core_run(sdev, chip->init_core_mask);
if (ret < 0) {
- dev_err(sdev->dev, "error: dsp core start failed %d\n", ret);
+ if (hda->boot_iteration == HDA_FW_BOOT_ATTEMPTS)
+ dev_err(sdev->dev,
+ "error: dsp core start failed %d\n", ret);
ret = -EIO;
goto err;
}
@@ -126,8 +166,10 @@ static int cl_dsp_init(struct snd_sof_dev *sdev, const void *fwdata,
HDA_DSP_INIT_TIMEOUT_US);
if (ret < 0) {
- dev_err(sdev->dev, "error: %s: timeout for HIPCIE done\n",
- __func__);
+ if (hda->boot_iteration == HDA_FW_BOOT_ATTEMPTS)
+ dev_err(sdev->dev,
+ "error: %s: timeout for HIPCIE done\n",
+ __func__);
goto err;
}
@@ -137,48 +179,80 @@ static int cl_dsp_init(struct snd_sof_dev *sdev, const void *fwdata,
chip->ipc_ack_mask,
chip->ipc_ack_mask);
- /* step 5: power down corex */
- ret = hda_dsp_core_power_down(sdev,
- chip->cores_mask & ~(HDA_DSP_CORE_MASK(0)));
+ /* step 5: power down cores that are no longer needed */
+ ret = hda_dsp_core_reset_power_down(sdev, chip->host_managed_cores_mask &
+ ~(chip->init_core_mask));
if (ret < 0) {
- dev_err(sdev->dev, "error: dsp core x power down failed\n");
+ if (hda->boot_iteration == HDA_FW_BOOT_ATTEMPTS)
+ dev_err(sdev->dev,
+ "error: dsp core x power down failed\n");
goto err;
}
/* step 6: enable IPC interrupts */
hda_dsp_ipc_int_enable(sdev);
- /* step 7: wait for ROM init */
+ /*
+ * step 7:
+ * - Cold/Full boot: wait for ROM init to proceed to download the firmware
+ * - IMR boot: wait for ROM firmware entered (firmware booted up from IMR)
+ */
+ if (imr_boot)
+ target_status = FSR_STATE_FW_ENTERED;
+ else
+ target_status = FSR_STATE_INIT_DONE;
+
ret = snd_sof_dsp_read_poll_timeout(sdev, HDA_DSP_BAR,
- HDA_DSP_SRAM_REG_ROM_STATUS, status,
- ((status & HDA_DSP_ROM_STS_MASK)
- == HDA_DSP_ROM_INIT),
+ chip->rom_status_reg, status,
+ (FSR_TO_STATE_CODE(status) == target_status),
HDA_DSP_REG_POLL_INTERVAL_US,
chip->rom_init_timeout *
USEC_PER_MSEC);
- if (!ret)
+ if (!ret) {
+ /* set enabled cores mask and increment ref count for cores in init_core_mask */
+ sdev->enabled_cores_mask |= chip->init_core_mask;
+ mask = sdev->enabled_cores_mask;
+ for_each_set_bit(j, &mask, SOF_MAX_DSP_NUM_CORES)
+ sdev->dsp_core_ref_count[j]++;
return 0;
+ }
- dev_err(sdev->dev,
- "error: %s: timeout HDA_DSP_SRAM_REG_ROM_STATUS read\n",
- __func__);
+ if (hda->boot_iteration == HDA_FW_BOOT_ATTEMPTS)
+ dev_err(sdev->dev,
+ "%s: timeout with rom_status_reg (%#x) read\n",
+ __func__, chip->rom_status_reg);
err:
- hda_dsp_dump(sdev, SOF_DBG_REGS | SOF_DBG_PCI | SOF_DBG_MBOX);
- hda_dsp_core_reset_power_down(sdev, chip->cores_mask);
+ flags = SOF_DBG_DUMP_PCI | SOF_DBG_DUMP_MBOX | SOF_DBG_DUMP_OPTIONAL;
+
+ /* after max boot attempts make sure that the dump is printed */
+ if (hda->boot_iteration == HDA_FW_BOOT_ATTEMPTS)
+ flags &= ~SOF_DBG_DUMP_OPTIONAL;
+ dump_msg = kasprintf(GFP_KERNEL, "Boot iteration failed: %d/%d",
+ hda->boot_iteration, HDA_FW_BOOT_ATTEMPTS);
+ snd_sof_dsp_dbg_dump(sdev, dump_msg, flags);
+ hda_dsp_core_reset_power_down(sdev, chip->host_managed_cores_mask);
+
+ kfree(dump_msg);
return ret;
}
+EXPORT_SYMBOL_NS(cl_dsp_init, "SND_SOC_SOF_INTEL_HDA_COMMON");
-static int cl_trigger(struct snd_sof_dev *sdev,
- struct hdac_ext_stream *stream, int cmd)
+int hda_cl_trigger(struct device *dev, struct hdac_ext_stream *hext_stream, int cmd)
{
- struct hdac_stream *hstream = &stream->hstream;
+ struct snd_sof_dev *sdev = dev_get_drvdata(dev);
+ struct hdac_stream *hstream = &hext_stream->hstream;
int sd_offset = SOF_STREAM_SD_OFFSET(hstream);
+ struct sof_intel_hda_stream *hda_stream;
/* code loader is special case that reuses stream ops */
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
+ hda_stream = container_of(hext_stream, struct sof_intel_hda_stream,
+ hext_stream);
+ reinit_completion(&hda_stream->ioc);
+
snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTCTL,
1 << hstream->index,
1 << hstream->index);
@@ -193,71 +267,71 @@ static int cl_trigger(struct snd_sof_dev *sdev,
hstream->running = true;
return 0;
default:
- return hda_dsp_stream_trigger(sdev, stream, cmd);
+ return hda_dsp_stream_trigger(sdev, hext_stream, cmd);
}
}
+EXPORT_SYMBOL_NS(hda_cl_trigger, "SND_SOC_SOF_INTEL_HDA_COMMON");
-static struct hdac_ext_stream *get_stream_with_tag(struct snd_sof_dev *sdev,
- int tag)
+int hda_cl_cleanup(struct device *dev, struct snd_dma_buffer *dmab,
+ bool persistent_buffer, struct hdac_ext_stream *hext_stream)
{
- struct hdac_bus *bus = sof_to_bus(sdev);
- struct hdac_stream *s;
-
- /* get stream with tag */
- list_for_each_entry(s, &bus->stream_list, list) {
- if (s->direction == SNDRV_PCM_STREAM_PLAYBACK &&
- s->stream_tag == tag) {
- return stream_to_hdac_ext_stream(s);
- }
- }
-
- return NULL;
-}
-
-static int cl_cleanup(struct snd_sof_dev *sdev, struct snd_dma_buffer *dmab,
- struct hdac_ext_stream *stream)
-{
- struct hdac_stream *hstream = &stream->hstream;
+ struct snd_sof_dev *sdev = dev_get_drvdata(dev);
+ struct hdac_stream *hstream = &hext_stream->hstream;
int sd_offset = SOF_STREAM_SD_OFFSET(hstream);
- int ret;
+ int ret = 0;
- ret = hda_dsp_stream_spib_config(sdev, stream, HDA_DSP_SPIB_DISABLE, 0);
+ if (hstream->direction == SNDRV_PCM_STREAM_PLAYBACK)
+ ret = hda_dsp_stream_spib_config(sdev, hext_stream, HDA_DSP_SPIB_DISABLE, 0);
+ else
+ snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset,
+ SOF_HDA_SD_CTL_DMA_START, 0);
- hda_dsp_stream_put(sdev, SNDRV_PCM_STREAM_PLAYBACK,
- hstream->stream_tag);
+ hda_dsp_stream_put(sdev, hstream->direction, hstream->stream_tag);
hstream->running = 0;
hstream->substream = NULL;
/* reset BDL address */
snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR,
- sd_offset + SOF_HDA_ADSP_REG_CL_SD_BDLPL, 0);
+ sd_offset + SOF_HDA_ADSP_REG_SD_BDLPL, 0);
snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR,
- sd_offset + SOF_HDA_ADSP_REG_CL_SD_BDLPU, 0);
+ sd_offset + SOF_HDA_ADSP_REG_SD_BDLPU, 0);
snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, sd_offset, 0);
- snd_dma_free_pages(dmab);
- dmab->area = NULL;
- hstream->bufsize = 0;
- hstream->format_val = 0;
+
+ if (!persistent_buffer) {
+ snd_dma_free_pages(dmab);
+ dmab->area = NULL;
+ dmab->bytes = 0;
+ hstream->bufsize = 0;
+ hstream->format_val = 0;
+ }
return ret;
}
+EXPORT_SYMBOL_NS(hda_cl_cleanup, "SND_SOC_SOF_INTEL_HDA_COMMON");
+
+#define HDA_CL_DMA_IOC_TIMEOUT_MS 500
-static int cl_copy_fw(struct snd_sof_dev *sdev, struct hdac_ext_stream *stream)
+int hda_cl_copy_fw(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_stream)
{
+ struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
+ const struct sof_intel_dsp_desc *chip = hda->desc;
unsigned int reg;
int ret, status;
- ret = cl_trigger(sdev, stream, SNDRV_PCM_TRIGGER_START);
+ dev_dbg(sdev->dev, "Code loader DMA starting\n");
+
+ ret = hda_cl_trigger(sdev->dev, hext_stream, SNDRV_PCM_TRIGGER_START);
if (ret < 0) {
dev_err(sdev->dev, "error: DMA trigger start failed\n");
return ret;
}
+ dev_dbg(sdev->dev, "waiting for FW_ENTERED status\n");
+
status = snd_sof_dsp_read_poll_timeout(sdev, HDA_DSP_BAR,
- HDA_DSP_SRAM_REG_ROM_STATUS, reg,
- ((reg & HDA_DSP_ROM_STS_MASK)
- == HDA_DSP_ROM_FW_ENTERED),
+ chip->rom_status_reg, reg,
+ (FSR_TO_STATE_CODE(reg) == FSR_STATE_FW_ENTERED),
HDA_DSP_REG_POLL_INTERVAL_US,
HDA_DSP_BASEFW_TIMEOUT_US);
@@ -268,81 +342,159 @@ static int cl_copy_fw(struct snd_sof_dev *sdev, struct hdac_ext_stream *stream)
if (status < 0) {
dev_err(sdev->dev,
- "error: %s: timeout HDA_DSP_SRAM_REG_ROM_STATUS read\n",
- __func__);
+ "%s: timeout with rom_status_reg (%#x) read\n",
+ __func__, chip->rom_status_reg);
+ } else {
+ dev_dbg(sdev->dev, "Code loader FW_ENTERED status\n");
}
- ret = cl_trigger(sdev, stream, SNDRV_PCM_TRIGGER_STOP);
+ ret = hda_cl_trigger(sdev->dev, hext_stream, SNDRV_PCM_TRIGGER_STOP);
if (ret < 0) {
dev_err(sdev->dev, "error: DMA trigger stop failed\n");
if (!status)
status = ret;
+ } else {
+ dev_dbg(sdev->dev, "Code loader DMA stopped\n");
}
return status;
}
+int hda_dsp_cl_boot_firmware_iccmax(struct snd_sof_dev *sdev)
+{
+ struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
+ struct hdac_ext_stream *iccmax_stream;
+ int ret, ret1;
+ u8 original_gb;
+
+ /* save the original LTRP guardband value */
+ original_gb = snd_sof_dsp_read8(sdev, HDA_DSP_HDA_BAR, HDA_VS_INTEL_LTRP) &
+ HDA_VS_INTEL_LTRP_GB_MASK;
+
+ /*
+ * Prepare capture stream for ICCMAX. We do not need to store
+ * the data, so use a buffer of PAGE_SIZE for receiving.
+ */
+ iccmax_stream = hda_cl_prepare(sdev->dev, HDA_CL_STREAM_FORMAT, PAGE_SIZE,
+ &hda->iccmax_dmab, persistent_cl_buffer,
+ SNDRV_PCM_STREAM_CAPTURE, true);
+ if (IS_ERR(iccmax_stream)) {
+ dev_err(sdev->dev, "error: dma prepare for ICCMAX stream failed\n");
+ return PTR_ERR(iccmax_stream);
+ }
+
+ ret = hda_dsp_cl_boot_firmware(sdev);
+
+ /*
+ * Perform iccmax stream cleanup. This should be done even if firmware loading fails.
+ * If the cleanup also fails, we return the initial error
+ */
+ ret1 = hda_cl_cleanup(sdev->dev, &hda->iccmax_dmab,
+ persistent_cl_buffer, iccmax_stream);
+ if (ret1 < 0) {
+ dev_err(sdev->dev, "error: ICCMAX stream cleanup failed\n");
+
+ /* set return value to indicate cleanup failure */
+ if (!ret)
+ ret = ret1;
+ }
+
+ /* restore the original guardband value after FW boot */
+ snd_sof_dsp_update8(sdev, HDA_DSP_HDA_BAR, HDA_VS_INTEL_LTRP,
+ HDA_VS_INTEL_LTRP_GB_MASK, original_gb);
+
+ return ret;
+}
+EXPORT_SYMBOL_NS(hda_dsp_cl_boot_firmware_iccmax, "SND_SOC_SOF_INTEL_CNL");
+
+static int hda_dsp_boot_imr(struct snd_sof_dev *sdev)
+{
+ const struct sof_intel_dsp_desc *chip_info;
+ int ret;
+
+ chip_info = get_chip_info(sdev->pdata);
+ if (chip_info->cl_init)
+ ret = chip_info->cl_init(sdev, 0, true);
+ else
+ ret = -EINVAL;
+
+ if (!ret)
+ hda_sdw_process_wakeen(sdev);
+
+ return ret;
+}
+
int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev)
{
+ struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
struct snd_sof_pdata *plat_data = sdev->pdata;
const struct sof_dev_desc *desc = plat_data->desc;
const struct sof_intel_dsp_desc *chip_info;
- struct hdac_ext_stream *stream;
+ struct hdac_ext_stream *hext_stream;
struct firmware stripped_firmware;
- int ret, ret1, tag, i;
+ int ret, ret1, i;
+
+ if (hda->imrboot_supported && !sdev->first_boot && !hda->skip_imr_boot) {
+ dev_dbg(sdev->dev, "IMR restore supported, booting from IMR directly\n");
+ hda->boot_iteration = 0;
+ ret = hda_dsp_boot_imr(sdev);
+ if (!ret) {
+ hda->booted_from_imr = true;
+ return 0;
+ }
+
+ dev_warn(sdev->dev, "IMR restore failed, trying to cold boot\n");
+ }
+
+ hda->booted_from_imr = false;
chip_info = desc->chip_info;
- if (plat_data->fw->size <= plat_data->fw_offset) {
+ if (sdev->basefw.fw->size <= sdev->basefw.payload_offset) {
dev_err(sdev->dev, "error: firmware size must be greater than firmware offset\n");
return -EINVAL;
}
- stripped_firmware.data = plat_data->fw->data + plat_data->fw_offset;
- stripped_firmware.size = plat_data->fw->size - plat_data->fw_offset;
-
/* init for booting wait */
init_waitqueue_head(&sdev->boot_wait);
/* prepare DMA for code loader stream */
- tag = cl_stream_prepare(sdev, 0x40, stripped_firmware.size,
- &sdev->dmab, SNDRV_PCM_STREAM_PLAYBACK);
-
- if (tag < 0) {
- dev_err(sdev->dev, "error: dma prepare for fw loading err: %x\n",
- tag);
- return tag;
+ stripped_firmware.size = sdev->basefw.fw->size - sdev->basefw.payload_offset;
+ hext_stream = hda_cl_prepare(sdev->dev, HDA_CL_STREAM_FORMAT,
+ stripped_firmware.size,
+ &hda->cl_dmab, persistent_cl_buffer,
+ SNDRV_PCM_STREAM_PLAYBACK, false);
+ if (IS_ERR(hext_stream)) {
+ dev_err(sdev->dev, "error: dma prepare for fw loading failed\n");
+ return PTR_ERR(hext_stream);
}
- /* get stream with tag */
- stream = get_stream_with_tag(sdev, tag);
- if (!stream) {
- dev_err(sdev->dev,
- "error: could not get stream with stream tag %d\n",
- tag);
- ret = -ENODEV;
- goto err;
+ /*
+ * Copy the payload to the DMA buffer if it is temporary or if the
+ * buffer is persistent but it does not have the basefw payload either
+ * because this is the first boot and the buffer needs to be initialized,
+ * or a library got loaded and it replaced the basefw.
+ */
+ if (!persistent_cl_buffer || !hda->cl_dmab_contains_basefw) {
+ stripped_firmware.data = sdev->basefw.fw->data + sdev->basefw.payload_offset;
+ memcpy(hda->cl_dmab.area, stripped_firmware.data, stripped_firmware.size);
+ hda->cl_dmab_contains_basefw = true;
}
- memcpy(sdev->dmab.area, stripped_firmware.data,
- stripped_firmware.size);
-
/* try ROM init a few times before giving up */
for (i = 0; i < HDA_FW_BOOT_ATTEMPTS; i++) {
- ret = cl_dsp_init(sdev, stripped_firmware.data,
- stripped_firmware.size, tag);
+ dev_dbg(sdev->dev,
+ "Attempting iteration %d of Core En/ROM load...\n", i);
+
+ hda->boot_iteration = i + 1;
+ if (chip_info->cl_init)
+ ret = chip_info->cl_init(sdev, hext_stream->hstream.stream_tag, false);
+ else
+ ret = -EINVAL;
/* don't retry anymore if successful */
if (!ret)
break;
-
- dev_dbg(sdev->dev, "iteration %d of Core En/ROM load failed: %d\n",
- i, ret);
- dev_dbg(sdev->dev, "Error code=0x%x: FW status=0x%x\n",
- snd_sof_dsp_read(sdev, HDA_DSP_BAR,
- HDA_DSP_SRAM_REG_ROM_ERROR),
- snd_sof_dsp_read(sdev, HDA_DSP_BAR,
- HDA_DSP_SRAM_REG_ROM_STATUS));
}
if (i == HDA_FW_BOOT_ATTEMPTS) {
@@ -370,14 +522,22 @@ int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev)
hda_sdw_process_wakeen(sdev);
/*
- * at this point DSP ROM has been initialized and
- * should be ready for code loading and firmware boot
+ * Set the boot_iteration to the last attempt, indicating that the
+ * DSP ROM has been initialized and from this point there will be no
+ * retry done to boot.
+ *
+ * Continue with code loading and firmware boot
*/
- ret = cl_copy_fw(sdev, stream);
- if (!ret)
+ hda->boot_iteration = HDA_FW_BOOT_ATTEMPTS;
+ ret = hda_cl_copy_fw(sdev, hext_stream);
+ if (!ret) {
dev_dbg(sdev->dev, "Firmware download successful, booting...\n");
- else
- dev_err(sdev->dev, "error: load fw failed ret: %d\n", ret);
+ hda->skip_imr_boot = false;
+ } else {
+ snd_sof_dsp_dbg_dump(sdev, "Firmware download failed",
+ SOF_DBG_DUMP_PCI | SOF_DBG_DUMP_MBOX);
+ hda->skip_imr_boot = true;
+ }
cleanup:
/*
@@ -385,7 +545,8 @@ cleanup:
* This should be done even if firmware loading fails.
* If the cleanup also fails, we return the initial error
*/
- ret1 = cl_cleanup(sdev, &sdev->dmab, stream);
+ ret1 = hda_cl_cleanup(sdev->dev, &hda->cl_dmab,
+ persistent_cl_buffer, hext_stream);
if (ret1 < 0) {
dev_err(sdev->dev, "error: Code loader DSP cleanup failed\n");
@@ -395,46 +556,170 @@ cleanup:
}
/*
- * return master core id if both fw copy
+ * return primary core id if both fw copy
* and stream clean up are successful
*/
if (!ret)
return chip_info->init_core_mask;
- /* dump dsp registers and disable DSP upon error */
-err:
- hda_dsp_dump(sdev, SOF_DBG_REGS | SOF_DBG_PCI | SOF_DBG_MBOX);
-
/* disable DSP */
- snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR,
- SOF_HDA_REG_PP_PPCTL,
- SOF_HDA_PPCTL_GPROCEN, 0);
+ hda_dsp_ctrl_ppcap_enable(sdev, false);
+
return ret;
}
+EXPORT_SYMBOL_NS(hda_dsp_cl_boot_firmware, "SND_SOC_SOF_INTEL_HDA_COMMON");
-/* pre fw run operations */
-int hda_dsp_pre_fw_run(struct snd_sof_dev *sdev)
+int hda_dsp_ipc4_load_library(struct snd_sof_dev *sdev,
+ struct sof_ipc4_fw_library *fw_lib, bool reload)
{
- /* disable clock gating and power gating */
- return hda_dsp_ctrl_clock_power_gating(sdev, false);
+ struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
+ struct sof_ipc4_fw_data *ipc4_data = sdev->private;
+ struct hdac_ext_stream *hext_stream;
+ struct firmware stripped_firmware;
+ struct sof_ipc4_msg msg = {};
+ int ret, ret1;
+
+ /* if IMR booting is enabled and fw context is saved for D3 state, skip the loading */
+ if (reload && hda->booted_from_imr && ipc4_data->fw_context_save)
+ return 0;
+
+ /* the fw_lib has been verified during loading, we can trust the validity here */
+ stripped_firmware.data = fw_lib->sof_fw.fw->data + fw_lib->sof_fw.payload_offset;
+ stripped_firmware.size = fw_lib->sof_fw.fw->size - fw_lib->sof_fw.payload_offset;
+
+ /*
+ * force re-allocation of the cl_dmab if the preserved DMA buffer is
+ * smaller than what is needed for the library
+ */
+ if (persistent_cl_buffer && stripped_firmware.size > hda->cl_dmab.bytes) {
+ snd_dma_free_pages(&hda->cl_dmab);
+ hda->cl_dmab.area = NULL;
+ hda->cl_dmab.bytes = 0;
+ }
+
+ /* prepare DMA for code loader stream */
+ hext_stream = hda_cl_prepare(sdev->dev, HDA_CL_STREAM_FORMAT,
+ stripped_firmware.size,
+ &hda->cl_dmab, persistent_cl_buffer,
+ SNDRV_PCM_STREAM_PLAYBACK, false);
+ if (IS_ERR(hext_stream)) {
+ dev_err(sdev->dev, "%s: DMA prepare failed\n", __func__);
+ return PTR_ERR(hext_stream);
+ }
+
+ memcpy(hda->cl_dmab.area, stripped_firmware.data, stripped_firmware.size);
+ hda->cl_dmab_contains_basefw = false;
+
+ /*
+ * 1st stage: SOF_IPC4_GLB_LOAD_LIBRARY_PREPARE
+ * Message includes the dma_id to be prepared for the library loading.
+ * If the firmware does not have support for the message, we will
+ * receive -EOPNOTSUPP. In this case we will use single step library
+ * loading and proceed to send the LOAD_LIBRARY message.
+ */
+ msg.primary = hext_stream->hstream.stream_tag - 1;
+ msg.primary |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_GLB_LOAD_LIBRARY_PREPARE);
+ msg.primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST);
+ msg.primary |= SOF_IPC4_MSG_TARGET(SOF_IPC4_FW_GEN_MSG);
+ ret = sof_ipc_tx_message_no_reply(sdev->ipc, &msg, 0);
+ if (!ret) {
+ int sd_offset = SOF_STREAM_SD_OFFSET(&hext_stream->hstream);
+ unsigned int status;
+
+ /*
+ * Make sure that the FIFOS value is not 0 in SDxFIFOS register
+ * which indicates that the firmware set the GEN bit and we can
+ * continue to start the DMA
+ */
+ ret = snd_sof_dsp_read_poll_timeout(sdev, HDA_DSP_HDA_BAR,
+ sd_offset + SOF_HDA_ADSP_REG_SD_FIFOSIZE,
+ status,
+ status & SOF_HDA_SD_FIFOSIZE_FIFOS_MASK,
+ HDA_DSP_REG_POLL_INTERVAL_US,
+ HDA_DSP_BASEFW_TIMEOUT_US);
+
+ if (ret < 0)
+ dev_warn(sdev->dev,
+ "%s: timeout waiting for FIFOS\n", __func__);
+ } else if (ret != -EOPNOTSUPP) {
+ goto cleanup;
+ }
+
+ ret = hda_cl_trigger(sdev->dev, hext_stream, SNDRV_PCM_TRIGGER_START);
+ if (ret < 0) {
+ dev_err(sdev->dev, "%s: DMA trigger start failed\n", __func__);
+ goto cleanup;
+ }
+
+ /*
+ * 2nd stage: LOAD_LIBRARY
+ * Message includes the dma_id and the lib_id, the dma_id must be
+ * identical to the one sent via LOAD_LIBRARY_PREPARE
+ */
+ msg.primary &= ~SOF_IPC4_MSG_TYPE_MASK;
+ msg.primary |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_GLB_LOAD_LIBRARY);
+ msg.primary |= SOF_IPC4_GLB_LOAD_LIBRARY_LIB_ID(fw_lib->id);
+ ret = sof_ipc_tx_message_no_reply(sdev->ipc, &msg, 0);
+
+ /* Stop the DMA channel */
+ ret1 = hda_cl_trigger(sdev->dev, hext_stream, SNDRV_PCM_TRIGGER_STOP);
+ if (ret1 < 0) {
+ dev_err(sdev->dev, "%s: DMA trigger stop failed\n", __func__);
+ if (!ret)
+ ret = ret1;
+ }
+
+cleanup:
+ /* clean up even in case of error and return the first error */
+ ret1 = hda_cl_cleanup(sdev->dev, &hda->cl_dmab, persistent_cl_buffer,
+ hext_stream);
+ if (ret1 < 0) {
+ dev_err(sdev->dev, "%s: Code loader DSP cleanup failed\n", __func__);
+
+ /* set return value to indicate cleanup failure */
+ if (!ret)
+ ret = ret1;
+ }
+
+ return ret;
}
+EXPORT_SYMBOL_NS(hda_dsp_ipc4_load_library, "SND_SOC_SOF_INTEL_HDA_COMMON");
-/* post fw run operations */
-int hda_dsp_post_fw_run(struct snd_sof_dev *sdev)
+int hda_dsp_ext_man_get_cavs_config_data(struct snd_sof_dev *sdev,
+ const struct sof_ext_man_elem_header *hdr)
{
- int ret;
+ const struct sof_ext_man_cavs_config_data *config_data =
+ container_of(hdr, struct sof_ext_man_cavs_config_data, hdr);
+ struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
+ int i, elem_num;
- if (sdev->first_boot) {
- ret = hda_sdw_startup(sdev);
- if (ret < 0) {
- dev_err(sdev->dev,
- "error: could not startup SoundWire links\n");
- return ret;
- }
+ /* calculate total number of config data elements */
+ elem_num = (hdr->size - sizeof(struct sof_ext_man_elem_header))
+ / sizeof(struct sof_config_elem);
+ if (elem_num <= 0) {
+ dev_err(sdev->dev, "cavs config data is inconsistent: %d\n", elem_num);
+ return -EINVAL;
}
- hda_sdw_int_enable(sdev, true);
+ for (i = 0; i < elem_num; i++)
+ switch (config_data->elems[i].token) {
+ case SOF_EXT_MAN_CAVS_CONFIG_EMPTY:
+ /* skip empty token */
+ break;
+ case SOF_EXT_MAN_CAVS_CONFIG_CAVS_LPRO:
+ hda->clk_config_lpro = config_data->elems[i].value;
+ dev_dbg(sdev->dev, "FW clock config: %s\n",
+ hda->clk_config_lpro ? "LPRO" : "HPRO");
+ break;
+ case SOF_EXT_MAN_CAVS_CONFIG_OUTBOX_SIZE:
+ case SOF_EXT_MAN_CAVS_CONFIG_INBOX_SIZE:
+ /* These elements are defined but not being used yet. No warn is required */
+ break;
+ default:
+ dev_info(sdev->dev, "unsupported token type: %d\n",
+ config_data->elems[i].token);
+ }
- /* re-enable clock gating and power gating */
- return hda_dsp_ctrl_clock_power_gating(sdev, true);
+ return 0;
}
+EXPORT_SYMBOL_NS(hda_dsp_ext_man_get_cavs_config_data, "SND_SOC_SOF_INTEL_HDA_COMMON");
diff --git a/sound/soc/sof/intel/hda-mlink.c b/sound/soc/sof/intel/hda-mlink.c
new file mode 100644
index 000000000000..ce561fe52bd5
--- /dev/null
+++ b/sound/soc/sof/intel/hda-mlink.c
@@ -0,0 +1,1120 @@
+// SPDX-License-Identifier: (GPL-2.0-only 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) 2022 Intel Corporation
+//
+
+/*
+ * Management of HDaudio multi-link (capabilities, power, coupling)
+ */
+
+#include <sound/hdaudio_ext.h>
+#include <sound/hda_register.h>
+#include <sound/hda-mlink.h>
+
+#include <linux/bitfield.h>
+#include <linux/module.h>
+#include <linux/string_choices.h>
+
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_MLINK)
+
+/* worst-case number of sublinks is used for sublink refcount array allocation only */
+#define HDAML_MAX_SUBLINKS (AZX_ML_LCTL_CPA_SHIFT - AZX_ML_LCTL_SPA_SHIFT)
+
+/**
+ * struct hdac_ext2_link - HDAudio extended+alternate link
+ *
+ * @hext_link: hdac_ext_link
+ * @alt: flag set for alternate extended links
+ * @intc: boolean for interrupt capable
+ * @ofls: boolean for offload support
+ * @lss: boolean for link synchronization capabilities
+ * @slcount: sublink count
+ * @elid: extended link ID (AZX_REG_ML_LEPTR_ID_ defines)
+ * @elver: extended link version
+ * @leptr: extended link pointer
+ * @eml_lock: mutual exclusion to access shared registers e.g. CPA/SPA bits
+ * in LCTL register
+ * @sublink_ref_count: array of refcounts, required to power-manage sublinks independently
+ * @base_ptr: pointer to shim/ip/shim_vs space
+ * @instance_offset: offset between each of @slcount instances managed by link
+ * @shim_offset: offset to SHIM register base
+ * @ip_offset: offset to IP register base
+ * @shim_vs_offset: offset to vendor-specific (VS) SHIM base
+ * @mic_privacy_mask: bitmask of sublinks where mic privacy is applied
+ */
+struct hdac_ext2_link {
+ struct hdac_ext_link hext_link;
+
+ /* read directly from LCAP register */
+ bool alt;
+ bool intc;
+ bool ofls;
+ bool lss;
+ int slcount;
+ int elid;
+ int elver;
+ u32 leptr;
+
+ struct mutex eml_lock; /* prevent concurrent access to e.g. CPA/SPA */
+ int sublink_ref_count[HDAML_MAX_SUBLINKS];
+
+ /* internal values computed from LCAP contents */
+ void __iomem *base_ptr;
+ u32 instance_offset;
+ u32 shim_offset;
+ u32 ip_offset;
+ u32 shim_vs_offset;
+
+ unsigned long mic_privacy_mask;
+};
+
+#define hdac_ext_link_to_ext2(h) container_of(h, struct hdac_ext2_link, hext_link)
+
+#define AZX_REG_SDW_INSTANCE_OFFSET 0x8000
+#define AZX_REG_SDW_SHIM_OFFSET 0x0
+#define AZX_REG_SDW_IP_OFFSET 0x100
+#define AZX_REG_SDW_VS_SHIM_OFFSET 0x6000
+#define AZX_REG_SDW_SHIM_PCMSyCM(y) (0x16 + 0x4 * (y))
+
+/* only one instance supported */
+#define AZX_REG_INTEL_DMIC_SHIM_OFFSET 0x0
+#define AZX_REG_INTEL_DMIC_IP_OFFSET 0x100
+#define AZX_REG_INTEL_DMIC_VS_SHIM_OFFSET 0x6000
+
+#define AZX_REG_INTEL_SSP_INSTANCE_OFFSET 0x1000
+#define AZX_REG_INTEL_SSP_SHIM_OFFSET 0x0
+#define AZX_REG_INTEL_SSP_IP_OFFSET 0x100
+#define AZX_REG_INTEL_SSP_VS_SHIM_OFFSET 0xC00
+
+/* only one instance supported */
+#define AZX_REG_INTEL_UAOL_SHIM_OFFSET 0x0
+#define AZX_REG_INTEL_UAOL_IP_OFFSET 0x100
+#define AZX_REG_INTEL_UAOL_VS_SHIM_OFFSET 0xC00
+
+/* Microphone privacy */
+#define AZX_REG_INTEL_VS_SHIM_PVCCS 0x10
+#define AZX_REG_INTEL_VS_SHIM_PVCCS_MDSTSCHGIE BIT(0)
+#define AZX_REG_INTEL_VS_SHIM_PVCCS_MDSTSCHG BIT(8)
+#define AZX_REG_INTEL_VS_SHIM_PVCCS_MDSTS BIT(9)
+#define AZX_REG_INTEL_VS_SHIM_PVCCS_FMDIS BIT(10)
+
+/* HDAML section - this part follows sequences in the hardware specification,
+ * including naming conventions and the use of the hdaml_ prefix.
+ * The code is intentionally minimal with limited dependencies on frameworks or
+ * helpers. Locking and scanning lists is handled at a higher level
+ */
+
+static int hdaml_lnk_enum(struct device *dev, struct hdac_ext2_link *h2link,
+ void __iomem *remap_addr, void __iomem *ml_addr, int link_idx)
+{
+ struct hdac_ext_link *hlink = &h2link->hext_link;
+ u32 base_offset;
+
+ hlink->lcaps = readl(ml_addr + AZX_REG_ML_LCAP);
+
+ h2link->alt = FIELD_GET(AZX_ML_HDA_LCAP_ALT, hlink->lcaps);
+
+ /* handle alternate extensions */
+ if (!h2link->alt) {
+ h2link->slcount = 1;
+
+ /*
+ * LSDIID is initialized by hardware for HDaudio link,
+ * it needs to be setup by software for alternate links
+ */
+ hlink->lsdiid = readw(ml_addr + AZX_REG_ML_LSDIID);
+
+ dev_dbg(dev, "Link %d: HDAudio - lsdiid=%d\n",
+ link_idx, hlink->lsdiid);
+
+ return 0;
+ }
+
+ h2link->intc = FIELD_GET(AZX_ML_HDA_LCAP_INTC, hlink->lcaps);
+ h2link->ofls = FIELD_GET(AZX_ML_HDA_LCAP_OFLS, hlink->lcaps);
+ h2link->lss = FIELD_GET(AZX_ML_HDA_LCAP_LSS, hlink->lcaps);
+
+ /* read slcount (increment due to zero-based hardware representation */
+ h2link->slcount = FIELD_GET(AZX_ML_HDA_LCAP_SLCOUNT, hlink->lcaps) + 1;
+ dev_dbg(dev, "Link %d: HDAudio extended - sublink count %d\n",
+ link_idx, h2link->slcount);
+
+ /* find IP ID and offsets */
+ h2link->leptr = readl(ml_addr + AZX_REG_ML_LEPTR);
+
+ h2link->elid = FIELD_GET(AZX_REG_ML_LEPTR_ID, h2link->leptr);
+
+ base_offset = FIELD_GET(AZX_REG_ML_LEPTR_PTR, h2link->leptr);
+ h2link->base_ptr = remap_addr + base_offset;
+
+ switch (h2link->elid) {
+ case AZX_REG_ML_LEPTR_ID_SDW:
+ h2link->instance_offset = AZX_REG_SDW_INSTANCE_OFFSET;
+ h2link->shim_offset = AZX_REG_SDW_SHIM_OFFSET;
+ h2link->ip_offset = AZX_REG_SDW_IP_OFFSET;
+ h2link->shim_vs_offset = AZX_REG_SDW_VS_SHIM_OFFSET;
+ dev_dbg(dev, "Link %d: HDAudio extended - SoundWire alternate link, leptr.ptr %#x\n",
+ link_idx, base_offset);
+ break;
+ case AZX_REG_ML_LEPTR_ID_INTEL_DMIC:
+ h2link->shim_offset = AZX_REG_INTEL_DMIC_SHIM_OFFSET;
+ h2link->ip_offset = AZX_REG_INTEL_DMIC_IP_OFFSET;
+ h2link->shim_vs_offset = AZX_REG_INTEL_DMIC_VS_SHIM_OFFSET;
+ dev_dbg(dev, "Link %d: HDAudio extended - INTEL DMIC alternate link, leptr.ptr %#x\n",
+ link_idx, base_offset);
+ break;
+ case AZX_REG_ML_LEPTR_ID_INTEL_SSP:
+ h2link->instance_offset = AZX_REG_INTEL_SSP_INSTANCE_OFFSET;
+ h2link->shim_offset = AZX_REG_INTEL_SSP_SHIM_OFFSET;
+ h2link->ip_offset = AZX_REG_INTEL_SSP_IP_OFFSET;
+ h2link->shim_vs_offset = AZX_REG_INTEL_SSP_VS_SHIM_OFFSET;
+ dev_dbg(dev, "Link %d: HDAudio extended - INTEL SSP alternate link, leptr.ptr %#x\n",
+ link_idx, base_offset);
+ break;
+ case AZX_REG_ML_LEPTR_ID_INTEL_UAOL:
+ h2link->shim_offset = AZX_REG_INTEL_UAOL_SHIM_OFFSET;
+ h2link->ip_offset = AZX_REG_INTEL_UAOL_IP_OFFSET;
+ h2link->shim_vs_offset = AZX_REG_INTEL_UAOL_VS_SHIM_OFFSET;
+ dev_dbg(dev, "Link %d: HDAudio extended - INTEL UAOL alternate link, leptr.ptr %#x\n",
+ link_idx, base_offset);
+ break;
+ default:
+ dev_err(dev, "Link %d: HDAudio extended - Unsupported alternate link, leptr.id=%#02x value\n",
+ link_idx, h2link->elid);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/*
+ * Hardware recommendations are to wait ~10us before checking any hardware transition
+ * reported by bits changing status.
+ * This value does not need to be super-precise, a slack of 5us is perfectly acceptable.
+ * The worst-case is about 1ms before reporting an issue
+ */
+#define HDAML_POLL_DELAY_MIN_US 10
+#define HDAML_POLL_DELAY_SLACK_US 5
+#define HDAML_POLL_DELAY_RETRY 100
+
+static int check_sublink_power(u32 __iomem *lctl, int sublink, bool enabled)
+{
+ int mask = BIT(sublink) << AZX_ML_LCTL_CPA_SHIFT;
+ int retry = HDAML_POLL_DELAY_RETRY;
+ u32 val;
+
+ usleep_range(HDAML_POLL_DELAY_MIN_US,
+ HDAML_POLL_DELAY_MIN_US + HDAML_POLL_DELAY_SLACK_US);
+ do {
+ val = readl(lctl);
+ if (enabled) {
+ if (val & mask)
+ return 0;
+ } else {
+ if (!(val & mask))
+ return 0;
+ }
+ usleep_range(HDAML_POLL_DELAY_MIN_US,
+ HDAML_POLL_DELAY_MIN_US + HDAML_POLL_DELAY_SLACK_US);
+
+ } while (--retry);
+
+ return -EIO;
+}
+
+static int hdaml_link_init(u32 __iomem *lctl, int sublink)
+{
+ u32 val;
+ u32 mask = BIT(sublink) << AZX_ML_LCTL_SPA_SHIFT;
+
+ val = readl(lctl);
+ val |= mask;
+
+ writel(val, lctl);
+
+ return check_sublink_power(lctl, sublink, true);
+}
+
+static int hdaml_link_shutdown(u32 __iomem *lctl, int sublink)
+{
+ u32 val;
+ u32 mask;
+
+ val = readl(lctl);
+ mask = BIT(sublink) << AZX_ML_LCTL_SPA_SHIFT;
+ val &= ~mask;
+
+ writel(val, lctl);
+
+ return check_sublink_power(lctl, sublink, false);
+}
+
+static void hdaml_link_enable_interrupt(u32 __iomem *lctl, bool enable)
+{
+ u32 val;
+
+ val = readl(lctl);
+ if (enable)
+ val |= AZX_ML_LCTL_INTEN;
+ else
+ val &= ~AZX_ML_LCTL_INTEN;
+
+ writel(val, lctl);
+}
+
+static bool hdaml_link_check_interrupt(u32 __iomem *lctl)
+{
+ u32 val;
+
+ val = readl(lctl);
+
+ return val & AZX_ML_LCTL_INTSTS;
+}
+
+static int hdaml_wait_bit(void __iomem *base, int offset, u32 mask, u32 target)
+{
+ int timeout = HDAML_POLL_DELAY_RETRY;
+ u32 reg_read;
+
+ do {
+ reg_read = readl(base + offset);
+ if ((reg_read & mask) == target)
+ return 0;
+
+ timeout--;
+ usleep_range(HDAML_POLL_DELAY_MIN_US,
+ HDAML_POLL_DELAY_MIN_US + HDAML_POLL_DELAY_SLACK_US);
+ } while (timeout != 0);
+
+ return -EAGAIN;
+}
+
+static void hdaml_link_set_syncprd(u32 __iomem *lsync, u32 syncprd)
+{
+ u32 val;
+
+ val = readl(lsync);
+ val &= ~AZX_REG_ML_LSYNC_SYNCPRD;
+ val |= (syncprd & AZX_REG_ML_LSYNC_SYNCPRD);
+
+ /*
+ * set SYNCPU but do not wait. The bit is cleared by hardware when
+ * the link becomes active.
+ */
+ val |= AZX_REG_ML_LSYNC_SYNCPU;
+
+ writel(val, lsync);
+}
+
+static int hdaml_link_wait_syncpu(u32 __iomem *lsync)
+{
+ return hdaml_wait_bit(lsync, 0, AZX_REG_ML_LSYNC_SYNCPU, 0);
+}
+
+static void hdaml_link_sync_arm(u32 __iomem *lsync, int sublink)
+{
+ u32 val;
+
+ val = readl(lsync);
+ val |= (AZX_REG_ML_LSYNC_CMDSYNC << sublink);
+
+ writel(val, lsync);
+}
+
+static void hdaml_link_sync_go(u32 __iomem *lsync)
+{
+ u32 val;
+
+ val = readl(lsync);
+ val |= AZX_REG_ML_LSYNC_SYNCGO;
+
+ writel(val, lsync);
+}
+
+static bool hdaml_link_check_cmdsync(u32 __iomem *lsync, u32 cmdsync_mask)
+{
+ u32 val;
+
+ val = readl(lsync);
+
+ return !!(val & cmdsync_mask);
+}
+
+static u16 hdaml_link_get_lsdiid(u16 __iomem *lsdiid)
+{
+ return readw(lsdiid);
+}
+
+static void hdaml_link_set_lsdiid(u16 __iomem *lsdiid, int dev_num)
+{
+ u16 val;
+
+ val = readw(lsdiid);
+ val |= BIT(dev_num);
+
+ writew(val, lsdiid);
+}
+
+static void hdaml_shim_map_stream_ch(u16 __iomem *pcmsycm, int lchan, int hchan,
+ int stream_id, int dir)
+{
+ u16 val;
+
+ val = readw(pcmsycm);
+
+ u16p_replace_bits(&val, lchan, GENMASK(3, 0));
+ u16p_replace_bits(&val, hchan, GENMASK(7, 4));
+ u16p_replace_bits(&val, stream_id, GENMASK(13, 8));
+ u16p_replace_bits(&val, dir, BIT(15));
+
+ writew(val, pcmsycm);
+}
+
+static void hdaml_lctl_offload_enable(u32 __iomem *lctl, bool enable)
+{
+ u32 val = readl(lctl);
+
+ if (enable)
+ val |= AZX_ML_LCTL_OFLEN;
+ else
+ val &= ~AZX_ML_LCTL_OFLEN;
+
+ writel(val, lctl);
+}
+
+/* END HDAML section */
+
+static int hda_ml_alloc_h2link(struct hdac_bus *bus, int index)
+{
+ struct hdac_ext2_link *h2link;
+ struct hdac_ext_link *hlink;
+ int ret;
+
+ h2link = kzalloc(sizeof(*h2link), GFP_KERNEL);
+ if (!h2link)
+ return -ENOMEM;
+
+ /* basic initialization */
+ hlink = &h2link->hext_link;
+
+ hlink->index = index;
+ hlink->bus = bus;
+ hlink->ml_addr = bus->mlcap + AZX_ML_BASE + (AZX_ML_INTERVAL * index);
+
+ ret = hdaml_lnk_enum(bus->dev, h2link, bus->remap_addr, hlink->ml_addr, index);
+ if (ret < 0) {
+ kfree(h2link);
+ return ret;
+ }
+
+ mutex_init(&h2link->eml_lock);
+
+ list_add_tail(&hlink->list, &bus->hlink_list);
+
+ /*
+ * HDaudio regular links are powered-on by default, the
+ * refcount needs to be initialized.
+ */
+ if (!h2link->alt)
+ hlink->ref_count = 1;
+
+ return 0;
+}
+
+int hda_bus_ml_init(struct hdac_bus *bus)
+{
+ u32 link_count;
+ int ret;
+ int i;
+
+ if (!bus->mlcap)
+ return 0;
+
+ link_count = readl(bus->mlcap + AZX_REG_ML_MLCD) + 1;
+
+ dev_dbg(bus->dev, "HDAudio Multi-Link count: %d\n", link_count);
+
+ for (i = 0; i < link_count; i++) {
+ ret = hda_ml_alloc_h2link(bus, i);
+ if (ret < 0) {
+ hda_bus_ml_free(bus);
+ return ret;
+ }
+ }
+ return 0;
+}
+EXPORT_SYMBOL_NS(hda_bus_ml_init, "SND_SOC_SOF_HDA_MLINK");
+
+void hda_bus_ml_free(struct hdac_bus *bus)
+{
+ struct hdac_ext_link *hlink, *_h;
+ struct hdac_ext2_link *h2link;
+
+ if (!bus->mlcap)
+ return;
+
+ list_for_each_entry_safe(hlink, _h, &bus->hlink_list, list) {
+ list_del(&hlink->list);
+ h2link = hdac_ext_link_to_ext2(hlink);
+
+ mutex_destroy(&h2link->eml_lock);
+ kfree(h2link);
+ }
+}
+EXPORT_SYMBOL_NS(hda_bus_ml_free, "SND_SOC_SOF_HDA_MLINK");
+
+static struct hdac_ext2_link *
+find_ext2_link(struct hdac_bus *bus, bool alt, int elid)
+{
+ struct hdac_ext_link *hlink;
+
+ list_for_each_entry(hlink, &bus->hlink_list, list) {
+ struct hdac_ext2_link *h2link = hdac_ext_link_to_ext2(hlink);
+
+ if (h2link->alt == alt && h2link->elid == elid)
+ return h2link;
+ }
+
+ return NULL;
+}
+
+int hdac_bus_eml_get_count(struct hdac_bus *bus, bool alt, int elid)
+{
+ struct hdac_ext2_link *h2link;
+
+ h2link = find_ext2_link(bus, alt, elid);
+ if (!h2link)
+ return 0;
+
+ return h2link->slcount;
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_get_count, "SND_SOC_SOF_HDA_MLINK");
+
+void hdac_bus_eml_enable_interrupt_unlocked(struct hdac_bus *bus, bool alt, int elid, bool enable)
+{
+ struct hdac_ext2_link *h2link;
+ struct hdac_ext_link *hlink;
+
+ h2link = find_ext2_link(bus, alt, elid);
+ if (!h2link)
+ return;
+
+ if (!h2link->intc)
+ return;
+
+ hlink = &h2link->hext_link;
+
+ hdaml_link_enable_interrupt(hlink->ml_addr + AZX_REG_ML_LCTL, enable);
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_enable_interrupt_unlocked, "SND_SOC_SOF_HDA_MLINK");
+
+void hdac_bus_eml_enable_interrupt(struct hdac_bus *bus, bool alt, int elid, bool enable)
+{
+ struct hdac_ext2_link *h2link;
+ struct hdac_ext_link *hlink;
+
+ h2link = find_ext2_link(bus, alt, elid);
+ if (!h2link)
+ return;
+
+ if (!h2link->intc)
+ return;
+
+ hlink = &h2link->hext_link;
+
+ mutex_lock(&h2link->eml_lock);
+
+ hdaml_link_enable_interrupt(hlink->ml_addr + AZX_REG_ML_LCTL, enable);
+
+ mutex_unlock(&h2link->eml_lock);
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_enable_interrupt, "SND_SOC_SOF_HDA_MLINK");
+
+bool hdac_bus_eml_check_interrupt(struct hdac_bus *bus, bool alt, int elid)
+{
+ struct hdac_ext2_link *h2link;
+ struct hdac_ext_link *hlink;
+
+ h2link = find_ext2_link(bus, alt, elid);
+ if (!h2link)
+ return false;
+
+ if (!h2link->intc)
+ return false;
+
+ hlink = &h2link->hext_link;
+
+ return hdaml_link_check_interrupt(hlink->ml_addr + AZX_REG_ML_LCTL);
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_check_interrupt, "SND_SOC_SOF_HDA_MLINK");
+
+int hdac_bus_eml_set_syncprd_unlocked(struct hdac_bus *bus, bool alt, int elid, u32 syncprd)
+{
+ struct hdac_ext2_link *h2link;
+ struct hdac_ext_link *hlink;
+
+ h2link = find_ext2_link(bus, alt, elid);
+ if (!h2link)
+ return 0;
+
+ if (!h2link->lss)
+ return 0;
+
+ hlink = &h2link->hext_link;
+
+ hdaml_link_set_syncprd(hlink->ml_addr + AZX_REG_ML_LSYNC, syncprd);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_set_syncprd_unlocked, "SND_SOC_SOF_HDA_MLINK");
+
+int hdac_bus_eml_sdw_set_syncprd_unlocked(struct hdac_bus *bus, u32 syncprd)
+{
+ return hdac_bus_eml_set_syncprd_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW, syncprd);
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_set_syncprd_unlocked, "SND_SOC_SOF_HDA_MLINK");
+
+int hdac_bus_eml_wait_syncpu_unlocked(struct hdac_bus *bus, bool alt, int elid)
+{
+ struct hdac_ext2_link *h2link;
+ struct hdac_ext_link *hlink;
+
+ h2link = find_ext2_link(bus, alt, elid);
+ if (!h2link)
+ return 0;
+
+ if (!h2link->lss)
+ return 0;
+
+ hlink = &h2link->hext_link;
+
+ return hdaml_link_wait_syncpu(hlink->ml_addr + AZX_REG_ML_LSYNC);
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_wait_syncpu_unlocked, "SND_SOC_SOF_HDA_MLINK");
+
+int hdac_bus_eml_sdw_wait_syncpu_unlocked(struct hdac_bus *bus)
+{
+ return hdac_bus_eml_wait_syncpu_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW);
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_wait_syncpu_unlocked, "SND_SOC_SOF_HDA_MLINK");
+
+void hdac_bus_eml_sync_arm_unlocked(struct hdac_bus *bus, bool alt, int elid, int sublink)
+{
+ struct hdac_ext2_link *h2link;
+ struct hdac_ext_link *hlink;
+
+ h2link = find_ext2_link(bus, alt, elid);
+ if (!h2link)
+ return;
+
+ if (!h2link->lss)
+ return;
+
+ hlink = &h2link->hext_link;
+
+ hdaml_link_sync_arm(hlink->ml_addr + AZX_REG_ML_LSYNC, sublink);
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_sync_arm_unlocked, "SND_SOC_SOF_HDA_MLINK");
+
+void hdac_bus_eml_sdw_sync_arm_unlocked(struct hdac_bus *bus, int sublink)
+{
+ hdac_bus_eml_sync_arm_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW, sublink);
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_sync_arm_unlocked, "SND_SOC_SOF_HDA_MLINK");
+
+int hdac_bus_eml_sync_go_unlocked(struct hdac_bus *bus, bool alt, int elid)
+{
+ struct hdac_ext2_link *h2link;
+ struct hdac_ext_link *hlink;
+
+ h2link = find_ext2_link(bus, alt, elid);
+ if (!h2link)
+ return 0;
+
+ if (!h2link->lss)
+ return 0;
+
+ hlink = &h2link->hext_link;
+
+ hdaml_link_sync_go(hlink->ml_addr + AZX_REG_ML_LSYNC);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_sync_go_unlocked, "SND_SOC_SOF_HDA_MLINK");
+
+int hdac_bus_eml_sdw_sync_go_unlocked(struct hdac_bus *bus)
+{
+ return hdac_bus_eml_sync_go_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW);
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_sync_go_unlocked, "SND_SOC_SOF_HDA_MLINK");
+
+bool hdac_bus_eml_check_cmdsync_unlocked(struct hdac_bus *bus, bool alt, int elid)
+{
+ struct hdac_ext2_link *h2link;
+ struct hdac_ext_link *hlink;
+ u32 cmdsync_mask;
+
+ h2link = find_ext2_link(bus, alt, elid);
+ if (!h2link)
+ return 0;
+
+ if (!h2link->lss)
+ return 0;
+
+ hlink = &h2link->hext_link;
+
+ cmdsync_mask = GENMASK(AZX_REG_ML_LSYNC_CMDSYNC_SHIFT + h2link->slcount - 1,
+ AZX_REG_ML_LSYNC_CMDSYNC_SHIFT);
+
+ return hdaml_link_check_cmdsync(hlink->ml_addr + AZX_REG_ML_LSYNC,
+ cmdsync_mask);
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_check_cmdsync_unlocked, "SND_SOC_SOF_HDA_MLINK");
+
+bool hdac_bus_eml_sdw_check_cmdsync_unlocked(struct hdac_bus *bus)
+{
+ return hdac_bus_eml_check_cmdsync_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW);
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_check_cmdsync_unlocked, "SND_SOC_SOF_HDA_MLINK");
+
+static int hdac_bus_eml_power_up_base(struct hdac_bus *bus, bool alt, int elid, int sublink,
+ bool eml_lock)
+{
+ struct hdac_ext2_link *h2link;
+ struct hdac_ext_link *hlink;
+ int ret = 0;
+
+ h2link = find_ext2_link(bus, alt, elid);
+ if (!h2link)
+ return -ENODEV;
+
+ if (sublink >= h2link->slcount)
+ return -EINVAL;
+
+ hlink = &h2link->hext_link;
+
+ if (eml_lock)
+ mutex_lock(&h2link->eml_lock);
+
+ if (!alt) {
+ if (++hlink->ref_count > 1)
+ goto skip_init;
+ } else {
+ if (++h2link->sublink_ref_count[sublink] > 1)
+ goto skip_init;
+ }
+
+ ret = hdaml_link_init(hlink->ml_addr + AZX_REG_ML_LCTL, sublink);
+ if ((h2link->mic_privacy_mask & BIT(sublink)) && !ret) {
+ u16 __iomem *pvccs = h2link->base_ptr +
+ h2link->shim_vs_offset +
+ sublink * h2link->instance_offset +
+ AZX_REG_INTEL_VS_SHIM_PVCCS;
+ u16 val = readw(pvccs);
+
+ writew(val | AZX_REG_INTEL_VS_SHIM_PVCCS_MDSTSCHGIE, pvccs);
+
+ if (val & AZX_REG_INTEL_VS_SHIM_PVCCS_MDSTS)
+ dev_dbg(bus->dev,
+ "sublink %d (%d:%d): Mic privacy is enabled\n",
+ sublink, alt, elid);
+ }
+
+skip_init:
+ if (eml_lock)
+ mutex_unlock(&h2link->eml_lock);
+
+ return ret;
+}
+
+int hdac_bus_eml_power_up(struct hdac_bus *bus, bool alt, int elid, int sublink)
+{
+ return hdac_bus_eml_power_up_base(bus, alt, elid, sublink, true);
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_power_up, "SND_SOC_SOF_HDA_MLINK");
+
+int hdac_bus_eml_power_up_unlocked(struct hdac_bus *bus, bool alt, int elid, int sublink)
+{
+ return hdac_bus_eml_power_up_base(bus, alt, elid, sublink, false);
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_power_up_unlocked, "SND_SOC_SOF_HDA_MLINK");
+
+static int hdac_bus_eml_power_down_base(struct hdac_bus *bus, bool alt, int elid, int sublink,
+ bool eml_lock)
+{
+ struct hdac_ext2_link *h2link;
+ struct hdac_ext_link *hlink;
+ int ret = 0;
+
+ h2link = find_ext2_link(bus, alt, elid);
+ if (!h2link)
+ return -ENODEV;
+
+ if (sublink >= h2link->slcount)
+ return -EINVAL;
+
+ hlink = &h2link->hext_link;
+
+ if (eml_lock)
+ mutex_lock(&h2link->eml_lock);
+
+ if (!alt) {
+ if (--hlink->ref_count > 0)
+ goto skip_shutdown;
+ } else {
+ if (--h2link->sublink_ref_count[sublink] > 0)
+ goto skip_shutdown;
+ }
+
+ if (h2link->mic_privacy_mask & BIT(sublink)) {
+ u16 __iomem *pvccs = h2link->base_ptr +
+ h2link->shim_vs_offset +
+ sublink * h2link->instance_offset +
+ AZX_REG_INTEL_VS_SHIM_PVCCS;
+
+ writew(readw(pvccs) & ~AZX_REG_INTEL_VS_SHIM_PVCCS_MDSTSCHGIE, pvccs);
+ }
+
+ ret = hdaml_link_shutdown(hlink->ml_addr + AZX_REG_ML_LCTL, sublink);
+
+skip_shutdown:
+ if (eml_lock)
+ mutex_unlock(&h2link->eml_lock);
+
+ return ret;
+}
+
+int hdac_bus_eml_power_down(struct hdac_bus *bus, bool alt, int elid, int sublink)
+{
+ return hdac_bus_eml_power_down_base(bus, alt, elid, sublink, true);
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_power_down, "SND_SOC_SOF_HDA_MLINK");
+
+int hdac_bus_eml_power_down_unlocked(struct hdac_bus *bus, bool alt, int elid, int sublink)
+{
+ return hdac_bus_eml_power_down_base(bus, alt, elid, sublink, false);
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_power_down_unlocked, "SND_SOC_SOF_HDA_MLINK");
+
+int hdac_bus_eml_sdw_power_up_unlocked(struct hdac_bus *bus, int sublink)
+{
+ return hdac_bus_eml_power_up_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW, sublink);
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_power_up_unlocked, "SND_SOC_SOF_HDA_MLINK");
+
+int hdac_bus_eml_sdw_power_down_unlocked(struct hdac_bus *bus, int sublink)
+{
+ return hdac_bus_eml_power_down_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW, sublink);
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_power_down_unlocked, "SND_SOC_SOF_HDA_MLINK");
+
+int hdac_bus_eml_sdw_get_lsdiid_unlocked(struct hdac_bus *bus, int sublink, u16 *lsdiid)
+{
+ struct hdac_ext2_link *h2link;
+ struct hdac_ext_link *hlink;
+
+ h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_SDW);
+ if (!h2link)
+ return -ENODEV;
+
+ hlink = &h2link->hext_link;
+
+ *lsdiid = hdaml_link_get_lsdiid(hlink->ml_addr + AZX_REG_ML_LSDIID_OFFSET(sublink));
+
+ return 0;
+} EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_get_lsdiid_unlocked, "SND_SOC_SOF_HDA_MLINK");
+
+int hdac_bus_eml_sdw_set_lsdiid(struct hdac_bus *bus, int sublink, int dev_num)
+{
+ struct hdac_ext2_link *h2link;
+ struct hdac_ext_link *hlink;
+
+ h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_SDW);
+ if (!h2link)
+ return -ENODEV;
+
+ hlink = &h2link->hext_link;
+
+ mutex_lock(&h2link->eml_lock);
+
+ hdaml_link_set_lsdiid(hlink->ml_addr + AZX_REG_ML_LSDIID_OFFSET(sublink), dev_num);
+
+ mutex_unlock(&h2link->eml_lock);
+
+ return 0;
+} EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_set_lsdiid, "SND_SOC_SOF_HDA_MLINK");
+
+/*
+ * the 'y' parameter comes from the PCMSyCM hardware register naming. 'y' refers to the
+ * PDI index, i.e. the FIFO used for RX or TX
+ */
+int hdac_bus_eml_sdw_map_stream_ch(struct hdac_bus *bus, int sublink, int y,
+ int channel_mask, int stream_id, int dir)
+{
+ struct hdac_ext2_link *h2link;
+ u16 __iomem *pcmsycm;
+ int hchan;
+ int lchan;
+ u16 val;
+
+ h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_SDW);
+ if (!h2link)
+ return -ENODEV;
+
+ pcmsycm = h2link->base_ptr + h2link->shim_offset +
+ h2link->instance_offset * sublink +
+ AZX_REG_SDW_SHIM_PCMSyCM(y);
+
+ if (channel_mask) {
+ hchan = __fls(channel_mask);
+ lchan = __ffs(channel_mask);
+ } else {
+ hchan = 0;
+ lchan = 0;
+ }
+
+ mutex_lock(&h2link->eml_lock);
+
+ hdaml_shim_map_stream_ch(pcmsycm, lchan, hchan,
+ stream_id, dir);
+
+ mutex_unlock(&h2link->eml_lock);
+
+ val = readw(pcmsycm);
+
+ dev_dbg(bus->dev, "sublink %d channel_mask %#x stream_id %d dir %d pcmscm %#x\n",
+ sublink, channel_mask, stream_id, dir, val);
+
+ return 0;
+} EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_map_stream_ch, "SND_SOC_SOF_HDA_MLINK");
+
+void hda_bus_ml_put_all(struct hdac_bus *bus)
+{
+ struct hdac_ext_link *hlink;
+
+ list_for_each_entry(hlink, &bus->hlink_list, list) {
+ struct hdac_ext2_link *h2link = hdac_ext_link_to_ext2(hlink);
+
+ if (!h2link->alt)
+ snd_hdac_ext_bus_link_put(bus, hlink);
+ }
+}
+EXPORT_SYMBOL_NS(hda_bus_ml_put_all, "SND_SOC_SOF_HDA_MLINK");
+
+void hda_bus_ml_reset_losidv(struct hdac_bus *bus)
+{
+ struct hdac_ext_link *hlink;
+
+ /* Reset stream-to-link mapping */
+ list_for_each_entry(hlink, &bus->hlink_list, list)
+ writel(0, hlink->ml_addr + AZX_REG_ML_LOSIDV);
+}
+EXPORT_SYMBOL_NS(hda_bus_ml_reset_losidv, "SND_SOC_SOF_HDA_MLINK");
+
+int hda_bus_ml_resume(struct hdac_bus *bus)
+{
+ struct hdac_ext_link *hlink;
+ int ret;
+
+ /* power up links that were active before suspend */
+ list_for_each_entry(hlink, &bus->hlink_list, list) {
+ struct hdac_ext2_link *h2link = hdac_ext_link_to_ext2(hlink);
+
+ if (!h2link->alt && hlink->ref_count) {
+ ret = snd_hdac_ext_bus_link_power_up(hlink);
+ if (ret < 0)
+ return ret;
+ }
+ }
+ return 0;
+}
+EXPORT_SYMBOL_NS(hda_bus_ml_resume, "SND_SOC_SOF_HDA_MLINK");
+
+int hda_bus_ml_suspend(struct hdac_bus *bus)
+{
+ struct hdac_ext_link *hlink;
+ int ret;
+
+ list_for_each_entry(hlink, &bus->hlink_list, list) {
+ struct hdac_ext2_link *h2link = hdac_ext_link_to_ext2(hlink);
+
+ if (!h2link->alt) {
+ ret = snd_hdac_ext_bus_link_power_down(hlink);
+ if (ret < 0)
+ return ret;
+ }
+ }
+ return 0;
+}
+EXPORT_SYMBOL_NS(hda_bus_ml_suspend, "SND_SOC_SOF_HDA_MLINK");
+
+struct mutex *hdac_bus_eml_get_mutex(struct hdac_bus *bus, bool alt, int elid)
+{
+ struct hdac_ext2_link *h2link;
+
+ h2link = find_ext2_link(bus, alt, elid);
+ if (!h2link)
+ return NULL;
+
+ return &h2link->eml_lock;
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_get_mutex, "SND_SOC_SOF_HDA_MLINK");
+
+struct hdac_ext_link *hdac_bus_eml_ssp_get_hlink(struct hdac_bus *bus)
+{
+ struct hdac_ext2_link *h2link;
+
+ h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_INTEL_SSP);
+ if (!h2link)
+ return NULL;
+
+ return &h2link->hext_link;
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_ssp_get_hlink, "SND_SOC_SOF_HDA_MLINK");
+
+struct hdac_ext_link *hdac_bus_eml_dmic_get_hlink(struct hdac_bus *bus)
+{
+ struct hdac_ext2_link *h2link;
+
+ h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_INTEL_DMIC);
+ if (!h2link)
+ return NULL;
+
+ return &h2link->hext_link;
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_dmic_get_hlink, "SND_SOC_SOF_HDA_MLINK");
+
+struct hdac_ext_link *hdac_bus_eml_sdw_get_hlink(struct hdac_bus *bus)
+{
+ struct hdac_ext2_link *h2link;
+
+ h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_SDW);
+ if (!h2link)
+ return NULL;
+
+ return &h2link->hext_link;
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_get_hlink, "SND_SOC_SOF_HDA_MLINK");
+
+int hdac_bus_eml_enable_offload(struct hdac_bus *bus, bool alt, int elid, bool enable)
+{
+ struct hdac_ext2_link *h2link;
+ struct hdac_ext_link *hlink;
+
+ h2link = find_ext2_link(bus, alt, elid);
+ if (!h2link)
+ return -ENODEV;
+
+ if (!h2link->ofls)
+ return 0;
+
+ hlink = &h2link->hext_link;
+
+ mutex_lock(&h2link->eml_lock);
+
+ hdaml_lctl_offload_enable(hlink->ml_addr + AZX_REG_ML_LCTL, enable);
+
+ mutex_unlock(&h2link->eml_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_enable_offload, "SND_SOC_SOF_HDA_MLINK");
+
+void hdac_bus_eml_set_mic_privacy_mask(struct hdac_bus *bus, bool alt, int elid,
+ unsigned long mask)
+{
+ struct hdac_ext2_link *h2link;
+
+ if (!mask)
+ return;
+
+ h2link = find_ext2_link(bus, alt, elid);
+ if (!h2link)
+ return;
+
+ if (__fls(mask) > h2link->slcount) {
+ dev_warn(bus->dev,
+ "%s: invalid sublink mask for %d:%d, slcount %d: %#lx\n",
+ __func__, alt, elid, h2link->slcount, mask);
+ return;
+ }
+
+ dev_dbg(bus->dev, "sublink mask for %d:%d, slcount %d: %#lx\n", alt,
+ elid, h2link->slcount, mask);
+
+ h2link->mic_privacy_mask = mask;
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_set_mic_privacy_mask, "SND_SOC_SOF_HDA_MLINK");
+
+bool hdac_bus_eml_is_mic_privacy_changed(struct hdac_bus *bus, bool alt, int elid)
+{
+ struct hdac_ext2_link *h2link;
+ bool changed = false;
+ u16 __iomem *pvccs;
+ int i;
+
+ h2link = find_ext2_link(bus, alt, elid);
+ if (!h2link)
+ return false;
+
+ /* The change in privacy state needs to be acked for each link */
+ for_each_set_bit(i, &h2link->mic_privacy_mask, h2link->slcount) {
+ u16 val;
+
+ if (h2link->sublink_ref_count[i] == 0)
+ continue;
+
+ pvccs = h2link->base_ptr +
+ h2link->shim_vs_offset +
+ i * h2link->instance_offset +
+ AZX_REG_INTEL_VS_SHIM_PVCCS;
+
+ val = readw(pvccs);
+ if (val & AZX_REG_INTEL_VS_SHIM_PVCCS_MDSTSCHG) {
+ writew(val, pvccs);
+ changed = true;
+ }
+ }
+
+ return changed;
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_is_mic_privacy_changed, "SND_SOC_SOF_HDA_MLINK");
+
+bool hdac_bus_eml_get_mic_privacy_state(struct hdac_bus *bus, bool alt, int elid)
+{
+ struct hdac_ext2_link *h2link;
+ u16 __iomem *pvccs;
+ bool state;
+ int i;
+
+ h2link = find_ext2_link(bus, alt, elid);
+ if (!h2link)
+ return false;
+
+ for_each_set_bit(i, &h2link->mic_privacy_mask, h2link->slcount) {
+ if (h2link->sublink_ref_count[i] == 0)
+ continue;
+
+ /* Return the privacy state from the first active link */
+ pvccs = h2link->base_ptr +
+ h2link->shim_vs_offset +
+ i * h2link->instance_offset +
+ AZX_REG_INTEL_VS_SHIM_PVCCS;
+
+ state = readw(pvccs) & AZX_REG_INTEL_VS_SHIM_PVCCS_MDSTS;
+ dev_dbg(bus->dev, "alt: %d, elid: %d: Mic privacy is %s\n", alt,
+ elid, str_enabled_disabled(state));
+
+ return state;
+ }
+
+ return false;
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_get_mic_privacy_state, "SND_SOC_SOF_HDA_MLINK");
+
+#endif
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_DESCRIPTION("SOF support for HDaudio multi-link");
diff --git a/sound/soc/sof/intel/hda-pcm.c b/sound/soc/sof/intel/hda-pcm.c
index b527d5958ae5..1dd8d2092c3b 100644
--- a/sound/soc/sof/intel/hda-pcm.c
+++ b/sound/soc/sof/intel/hda-pcm.c
@@ -3,7 +3,7 @@
// 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) 2018 Intel Corporation. All rights reserved.
+// Copyright(c) 2018 Intel Corporation
//
// Authors: Liam Girdwood <liam.r.girdwood@linux.intel.com>
// Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
@@ -15,8 +15,10 @@
* Hardware interface for generic Intel audio DSP HDA IP
*/
+#include <linux/moduleparam.h>
#include <sound/hda_register.h>
#include <sound/pcm_params.h>
+#include <trace/events/sof_intel.h>
#include "../sof-audio.h"
#include "../ops.h"
#include "hda.h"
@@ -27,6 +29,19 @@
#define SDnFMT_BITS(x) ((x) << 4)
#define SDnFMT_CHAN(x) ((x) << 0)
+static bool hda_always_enable_dmi_l1;
+module_param_named(always_enable_dmi_l1, hda_always_enable_dmi_l1, bool, 0444);
+MODULE_PARM_DESC(always_enable_dmi_l1, "SOF HDA always enable DMI l1");
+
+static bool hda_disable_rewinds;
+module_param_named(disable_rewinds, hda_disable_rewinds, bool, 0444);
+MODULE_PARM_DESC(disable_rewinds, "SOF HDA disable rewinds");
+
+static int hda_force_pause_support = -1;
+module_param_named(force_pause_support, hda_force_pause_support, int, 0444);
+MODULE_PARM_DESC(force_pause_support,
+ "Pause support: -1: Use default, 0: Disable, 1: Enable (default -1)");
+
u32 hda_dsp_get_mult_div(struct snd_sof_dev *sdev, int rate)
{
switch (rate) {
@@ -84,70 +99,93 @@ u32 hda_dsp_get_bits(struct snd_sof_dev *sdev, int sample_bits)
int hda_dsp_pcm_hw_params(struct snd_sof_dev *sdev,
struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
- struct sof_ipc_stream_params *ipc_params)
+ struct snd_sof_platform_stream_params *platform_params)
{
struct hdac_stream *hstream = substream->runtime->private_data;
- struct hdac_ext_stream *stream = stream_to_hdac_ext_stream(hstream);
+ struct hdac_ext_stream *hext_stream = stream_to_hdac_ext_stream(hstream);
struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
struct snd_dma_buffer *dmab;
- struct sof_ipc_fw_version *v = &sdev->fw_ready.version;
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) &&
(params->flags & SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP);
- ret = hda_dsp_stream_hw_params(sdev, stream, dmab, params);
+ ret = hda_dsp_stream_hw_params(sdev, hext_stream, dmab, params);
if (ret < 0) {
- dev_err(sdev->dev, "error: hdac prepare failed: %x\n", ret);
+ dev_err(sdev->dev, "error: hdac prepare failed: %d\n", ret);
return ret;
}
- /* disable SPIB, to enable buffer wrap for stream */
- hda_dsp_stream_spib_config(sdev, stream, HDA_DSP_SPIB_DISABLE, 0);
-
- /* update no_stream_position flag for ipc params */
- if (hda && hda->no_ipc_position) {
- /* For older ABIs set host_period_bytes to zero to inform
- * FW we don't want position updates. Newer versions use
- * no_stream_position for this purpose.
- */
- if (v->abi_version < SOF_ABI_VER(3, 10, 0))
- ipc_params->host_period_bytes = 0;
- else
- ipc_params->no_stream_position = 1;
- }
+ /* enable SPIB when rewinds are disabled */
+ if (hda_disable_rewinds)
+ hda_dsp_stream_spib_config(sdev, hext_stream, HDA_DSP_SPIB_ENABLE, 0);
+ else
+ hda_dsp_stream_spib_config(sdev, hext_stream, HDA_DSP_SPIB_DISABLE, 0);
+
+ if (hda)
+ platform_params->no_ipc_position = hda->no_ipc_position;
+
+ platform_params->stream_tag = hstream->stream_tag;
+
+ return 0;
+}
+EXPORT_SYMBOL_NS(hda_dsp_pcm_hw_params, "SND_SOC_SOF_INTEL_HDA_COMMON");
+
+/* update SPIB register with appl position */
+int hda_dsp_pcm_ack(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream)
+{
+ struct hdac_stream *hstream = substream->runtime->private_data;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ ssize_t appl_pos, buf_size;
+ u32 spib;
+
+ appl_pos = frames_to_bytes(runtime, runtime->control->appl_ptr);
+ buf_size = frames_to_bytes(runtime, runtime->buffer_size);
+
+ spib = appl_pos % buf_size;
- ipc_params->stream_tag = hstream->stream_tag;
+ /* Allowable value for SPIB is 1 byte to max buffer size */
+ if (!spib)
+ spib = buf_size;
+
+ sof_io_write(sdev, hstream->spib_addr, spib);
return 0;
}
+EXPORT_SYMBOL_NS(hda_dsp_pcm_ack, "SND_SOC_SOF_INTEL_HDA_COMMON");
int hda_dsp_pcm_trigger(struct snd_sof_dev *sdev,
struct snd_pcm_substream *substream, int cmd)
{
struct hdac_stream *hstream = substream->runtime->private_data;
- struct hdac_ext_stream *stream = stream_to_hdac_ext_stream(hstream);
+ struct hdac_ext_stream *hext_stream = stream_to_hdac_ext_stream(hstream);
- return hda_dsp_stream_trigger(sdev, stream, cmd);
+ return hda_dsp_stream_trigger(sdev, hext_stream, cmd);
}
+EXPORT_SYMBOL_NS(hda_dsp_pcm_trigger, "SND_SOC_SOF_INTEL_HDA_COMMON");
snd_pcm_uframes_t hda_dsp_pcm_pointer(struct snd_sof_dev *sdev,
struct snd_pcm_substream *substream)
{
- struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct snd_soc_component *scomp = sdev->component;
struct hdac_stream *hstream = substream->runtime->private_data;
struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
@@ -167,68 +205,119 @@ snd_pcm_uframes_t hda_dsp_pcm_pointer(struct snd_sof_dev *sdev,
goto found;
}
- /*
- * DPIB/posbuf position mode:
- * For Playback, Use DPIB register from HDA space which
- * reflects the actual data transferred.
- * For Capture, Use the position buffer for pointer, as DPIB
- * is not accurate enough, its update may be completed
- * earlier than the data written to DDR.
- */
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- pos = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR,
- AZX_REG_VS_SDXDPIB_XBASE +
- (AZX_REG_VS_SDXDPIB_XINTERVAL *
- hstream->index));
- } else {
- /*
- * For capture stream, we need more workaround to fix the
- * position incorrect issue:
- *
- * 1. Wait at least 20us before reading position buffer after
- * the interrupt generated(IOC), to make sure position update
- * happens on frame boundary i.e. 20.833uSec for 48KHz.
- * 2. Perform a dummy Read to DPIB register to flush DMA
- * position value.
- * 3. Read the DMA Position from posbuf. Now the readback
- * value should be >= period boundary.
- */
- usleep_range(20, 21);
- snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR,
- AZX_REG_VS_SDXDPIB_XBASE +
- (AZX_REG_VS_SDXDPIB_XINTERVAL *
- hstream->index));
- pos = snd_hdac_stream_get_pos_posbuf(hstream);
- }
-
- if (pos >= hstream->bufsize)
- pos = 0;
-
+ pos = hda_dsp_stream_get_position(hstream, substream->stream, true);
found:
pos = bytes_to_frames(substream->runtime, pos);
- dev_vdbg(sdev->dev, "PCM: stream %d dir %d position %lu\n",
- hstream->index, substream->stream, pos);
+ trace_sof_intel_hda_dsp_pcm(sdev, hstream, substream, pos);
return pos;
}
+EXPORT_SYMBOL_NS(hda_dsp_pcm_pointer, "SND_SOC_SOF_INTEL_HDA_COMMON");
int hda_dsp_pcm_open(struct snd_sof_dev *sdev,
struct snd_pcm_substream *substream)
{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_component *scomp = sdev->component;
struct hdac_ext_stream *dsp_stream;
+ struct snd_sof_pcm *spcm;
int direction = substream->stream;
+ u32 flags = 0;
+
+ spcm = snd_sof_find_spcm_dai(scomp, rtd);
+ if (!spcm) {
+ dev_err(sdev->dev, "error: can't find PCM with DAI ID %d\n", rtd->dai_link->id);
+ return -EINVAL;
+ }
+
+ /*
+ * if we want the .ack to work, we need to prevent the control from being mapped.
+ * The status can still be mapped.
+ */
+ if (hda_disable_rewinds)
+ runtime->hw.info |= SNDRV_PCM_INFO_NO_REWINDS | SNDRV_PCM_INFO_SYNC_APPLPTR;
+
+ /*
+ * All playback streams are DMI L1 capable, capture streams need
+ * pause push/release to be disabled
+ */
+ if (hda_always_enable_dmi_l1 && direction == SNDRV_PCM_STREAM_CAPTURE)
+ runtime->hw.info &= ~SNDRV_PCM_INFO_PAUSE;
+
+ /*
+ * Do not advertise the PAUSE support if it is forced to be disabled via
+ * module parameter or if the pause_supported is false for the PCM
+ * device
+ */
+ if (hda_force_pause_support == 0 ||
+ (hda_force_pause_support == -1 &&
+ !spcm->stream[substream->stream].pause_supported))
+ runtime->hw.info &= ~SNDRV_PCM_INFO_PAUSE;
- dsp_stream = hda_dsp_stream_get(sdev, direction);
+ if (hda_always_enable_dmi_l1 ||
+ direction == SNDRV_PCM_STREAM_PLAYBACK ||
+ spcm->stream[substream->stream].d0i3_compatible)
+ flags |= SOF_HDA_STREAM_DMI_L1_COMPATIBLE;
+ dsp_stream = hda_dsp_stream_get(sdev, direction, flags);
if (!dsp_stream) {
dev_err(sdev->dev, "error: no stream available\n");
return -ENODEV;
}
+ /* minimum as per HDA spec */
+ snd_pcm_hw_constraint_step(substream->runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 4);
+
+ /* avoid circular buffer wrap in middle of period */
+ snd_pcm_hw_constraint_integer(substream->runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+
+ /* Limit the maximum number of periods to not exceed the BDL entries count */
+ if (runtime->hw.periods_max > HDA_DSP_MAX_BDL_ENTRIES)
+ snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIODS,
+ runtime->hw.periods_min,
+ HDA_DSP_MAX_BDL_ENTRIES);
+
+ /* 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);
+
+ /*
+ * The dsp_max_burst_size_in_ms is the length of the maximum burst size
+ * of the host DMA in the ALSA buffer.
+ *
+ * On playback start the DMA will transfer dsp_max_burst_size_in_ms
+ * amount of data in one initial burst to fill up the host DMA buffer.
+ * Consequent DMA burst sizes are shorter and their length can vary.
+ * To make sure that userspace allocate large enough ALSA buffer we need
+ * to place a constraint on the buffer time.
+ *
+ * On capture the DMA will transfer 1ms chunks.
+ *
+ * Exact dsp_max_burst_size_in_ms constraint is racy, so set the
+ * constraint to a minimum of 2x dsp_max_burst_size_in_ms.
+ */
+ if (spcm->stream[direction].dsp_max_burst_size_in_ms)
+ snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_BUFFER_TIME,
+ spcm->stream[direction].dsp_max_burst_size_in_ms * USEC_PER_MSEC * 2,
+ UINT_MAX);
+
/* binding pcm substream to hda stream */
substream->runtime->private_data = &dsp_stream->hstream;
+
+ /*
+ * Reset the llp cache values (they are used for LLP compensation in
+ * case the counter is not reset)
+ */
+ dsp_stream->pplcllpl = 0;
+ dsp_stream->pplcllpu = 0;
+
return 0;
}
+EXPORT_SYMBOL_NS(hda_dsp_pcm_open, "SND_SOC_SOF_INTEL_HDA_COMMON");
int hda_dsp_pcm_close(struct snd_sof_dev *sdev,
struct snd_pcm_substream *substream)
@@ -248,3 +337,4 @@ int hda_dsp_pcm_close(struct snd_sof_dev *sdev,
substream->runtime->private_data = NULL;
return 0;
}
+EXPORT_SYMBOL_NS(hda_dsp_pcm_close, "SND_SOC_SOF_INTEL_HDA_COMMON");
diff --git a/sound/soc/sof/intel/hda-probes.c b/sound/soc/sof/intel/hda-probes.c
new file mode 100644
index 000000000000..c645346c2c84
--- /dev/null
+++ b/sound/soc/sof/intel/hda-probes.c
@@ -0,0 +1,150 @@
+// SPDX-License-Identifier: (GPL-2.0-only 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-2021 Intel Corporation
+//
+// Author: Cezary Rojewski <cezary.rojewski@intel.com>
+// Converted to SOF client:
+// Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
+// Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
+//
+
+#include <linux/module.h>
+#include <sound/hdaudio_ext.h>
+#include <sound/soc.h>
+#include "../sof-priv.h"
+#include "../sof-client-probes.h"
+#include "../sof-client.h"
+#include "hda.h"
+
+static inline struct hdac_ext_stream *
+hda_compr_get_stream(struct snd_compr_stream *cstream)
+{
+ return cstream->runtime->private_data;
+}
+
+static int hda_probes_compr_startup(struct sof_client_dev *cdev,
+ struct snd_compr_stream *cstream,
+ struct snd_soc_dai *dai, u32 *stream_id)
+{
+ struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev);
+ struct hdac_ext_stream *hext_stream;
+
+ hext_stream = hda_dsp_stream_get(sdev, cstream->direction, 0);
+ if (!hext_stream)
+ return -EBUSY;
+
+ hdac_stream(hext_stream)->curr_pos = 0;
+ hdac_stream(hext_stream)->cstream = cstream;
+ cstream->runtime->private_data = hext_stream;
+
+ *stream_id = hdac_stream(hext_stream)->stream_tag;
+
+ return 0;
+}
+
+static int hda_probes_compr_shutdown(struct sof_client_dev *cdev,
+ struct snd_compr_stream *cstream,
+ struct snd_soc_dai *dai)
+{
+ struct hdac_ext_stream *hext_stream = hda_compr_get_stream(cstream);
+ struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev);
+ int ret;
+
+ ret = hda_dsp_stream_put(sdev, cstream->direction,
+ hdac_stream(hext_stream)->stream_tag);
+ if (ret < 0) {
+ dev_dbg(sdev->dev, "stream put failed: %d\n", ret);
+ return ret;
+ }
+
+ hdac_stream(hext_stream)->cstream = NULL;
+ cstream->runtime->private_data = NULL;
+
+ return 0;
+}
+
+static int hda_probes_compr_set_params(struct sof_client_dev *cdev,
+ struct snd_compr_stream *cstream,
+ struct snd_compr_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct hdac_ext_stream *hext_stream = hda_compr_get_stream(cstream);
+ struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev);
+ struct hdac_stream *hstream = hdac_stream(hext_stream);
+ struct snd_dma_buffer *dmab;
+ u32 bits, rate;
+ int bps, ret;
+
+ dmab = cstream->runtime->dma_buffer_p;
+ /* compr params do not store bit depth, default to S32_LE */
+ bps = snd_pcm_format_physical_width(SNDRV_PCM_FORMAT_S32_LE);
+ if (bps < 0)
+ return bps;
+ bits = hda_dsp_get_bits(sdev, bps);
+ rate = hda_dsp_get_mult_div(sdev, params->codec.sample_rate);
+
+ hstream->format_val = rate | bits | (params->codec.ch_out - 1);
+ hstream->bufsize = cstream->runtime->buffer_size;
+ hstream->period_bytes = cstream->runtime->fragment_size;
+ hstream->no_period_wakeup = 0;
+
+ ret = hda_dsp_stream_hw_params(sdev, hext_stream, dmab, NULL);
+ if (ret < 0) {
+ dev_err(sdev->dev, "error: hdac prepare failed: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int hda_probes_compr_trigger(struct sof_client_dev *cdev,
+ struct snd_compr_stream *cstream,
+ int cmd, struct snd_soc_dai *dai)
+{
+ struct hdac_ext_stream *hext_stream = hda_compr_get_stream(cstream);
+ struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev);
+
+ return hda_dsp_stream_trigger(sdev, hext_stream, cmd);
+}
+
+static int hda_probes_compr_pointer(struct sof_client_dev *cdev,
+ struct snd_compr_stream *cstream,
+ struct snd_compr_tstamp *tstamp,
+ struct snd_soc_dai *dai)
+{
+ struct hdac_ext_stream *hext_stream = hda_compr_get_stream(cstream);
+ struct snd_soc_pcm_stream *pstream;
+
+ pstream = &dai->driver->capture;
+ tstamp->copied_total = hdac_stream(hext_stream)->curr_pos;
+ tstamp->sampling_rate = snd_pcm_rate_bit_to_rate(pstream->rates);
+
+ return 0;
+}
+
+/* SOF client implementation */
+static const struct sof_probes_host_ops hda_probes_ops = {
+ .startup = hda_probes_compr_startup,
+ .shutdown = hda_probes_compr_shutdown,
+ .set_params = hda_probes_compr_set_params,
+ .trigger = hda_probes_compr_trigger,
+ .pointer = hda_probes_compr_pointer,
+};
+
+int hda_probes_register(struct snd_sof_dev *sdev)
+{
+ return sof_client_dev_register(sdev, "hda-probes", 0, &hda_probes_ops,
+ sizeof(hda_probes_ops));
+}
+EXPORT_SYMBOL_NS(hda_probes_register, "SND_SOC_SOF_INTEL_HDA_COMMON");
+
+void hda_probes_unregister(struct snd_sof_dev *sdev)
+{
+ sof_client_dev_unregister(sdev, "hda-probes", 0);
+}
+EXPORT_SYMBOL_NS(hda_probes_unregister, "SND_SOC_SOF_INTEL_HDA_COMMON");
+
+MODULE_IMPORT_NS("SND_SOC_SOF_CLIENT");
diff --git a/sound/soc/sof/intel/hda-sdw-bpt.c b/sound/soc/sof/intel/hda-sdw-bpt.c
new file mode 100644
index 000000000000..1327f1cad0bc
--- /dev/null
+++ b/sound/soc/sof/intel/hda-sdw-bpt.c
@@ -0,0 +1,445 @@
+// SPDX-License-Identifier: (GPL-2.0-only 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) 2025 Intel Corporation.
+//
+
+/*
+ * Hardware interface for SoundWire BPT support with HDA DMA
+ */
+
+#include <sound/hdaudio_ext.h>
+#include <sound/hda-mlink.h>
+#include <sound/hda-sdw-bpt.h>
+#include <sound/sof.h>
+#include <sound/sof/ipc4/header.h>
+#include "../ops.h"
+#include "../sof-priv.h"
+#include "../ipc4-priv.h"
+#include "hda.h"
+
+#define BPT_FREQUENCY 192000 /* The max rate defined in rate_bits[] hdac_device.c */
+#define BPT_MULTIPLIER ((BPT_FREQUENCY / 48000) - 1)
+#define BPT_CHAIN_DMA_FIFO_MS 10
+/*
+ * This routine is directly inspired by sof_ipc4_chain_dma_trigger(),
+ * with major simplifications since there are no pipelines defined
+ * and no dependency on ALSA hw_params
+ */
+static int chain_dma_trigger(struct snd_sof_dev *sdev, unsigned int stream_tag,
+ int direction, int state)
+{
+ struct sof_ipc4_fw_data *ipc4_data = sdev->private;
+ bool allocate, enable, set_fifo_size;
+ struct sof_ipc4_msg msg = {{ 0 }};
+ int dma_id;
+
+ if (sdev->pdata->ipc_type != SOF_IPC_TYPE_4)
+ return -EOPNOTSUPP;
+
+ switch (state) {
+ case SOF_IPC4_PIPE_RUNNING: /* Allocate and start the chain */
+ allocate = true;
+ enable = true;
+ set_fifo_size = true;
+ break;
+ case SOF_IPC4_PIPE_PAUSED: /* Stop the chain */
+ allocate = true;
+ enable = false;
+ set_fifo_size = false;
+ break;
+ case SOF_IPC4_PIPE_RESET: /* Deallocate chain resources and remove the chain */
+ allocate = false;
+ enable = false;
+ set_fifo_size = false;
+ break;
+ default:
+ dev_err(sdev->dev, "Unexpected state %d", state);
+ return -EINVAL;
+ }
+
+ msg.primary = SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_GLB_CHAIN_DMA);
+ msg.primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST);
+ msg.primary |= SOF_IPC4_MSG_TARGET(SOF_IPC4_FW_GEN_MSG);
+
+ /* for BPT/BRA we can use the same stream tag for host and link */
+ dma_id = stream_tag - 1;
+ if (direction == SNDRV_PCM_STREAM_CAPTURE)
+ dma_id += ipc4_data->num_playback_streams;
+
+ msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_HOST_ID(dma_id);
+ msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_LINK_ID(dma_id);
+
+ /* For BPT/BRA we use 32 bits so SCS is not set */
+
+ /* CHAIN DMA needs at least 2ms */
+ if (set_fifo_size)
+ msg.extension |= SOF_IPC4_GLB_EXT_CHAIN_DMA_FIFO_SIZE(BPT_FREQUENCY / 1000 *
+ BPT_CHAIN_DMA_FIFO_MS *
+ sizeof(u32));
+
+ if (allocate)
+ msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_ALLOCATE_MASK;
+
+ if (enable)
+ msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_ENABLE_MASK;
+
+ return sof_ipc_tx_message_no_reply(sdev->ipc, &msg, 0);
+}
+
+static int hda_sdw_bpt_dma_prepare(struct device *dev, struct hdac_ext_stream **sdw_bpt_stream,
+ struct snd_dma_buffer *dmab_bdl, u32 bpt_num_bytes,
+ unsigned int num_channels, int direction)
+{
+ struct snd_sof_dev *sdev = dev_get_drvdata(dev);
+ struct hdac_ext_stream *bpt_stream;
+ unsigned int format = HDA_CL_STREAM_FORMAT;
+
+ /*
+ * the baseline format needs to be adjusted to
+ * bandwidth requirements
+ */
+ format |= (num_channels - 1);
+ format |= BPT_MULTIPLIER << AC_FMT_MULT_SHIFT;
+
+ dev_dbg(dev, "direction %d format_val %#x\n", direction, format);
+
+ bpt_stream = hda_cl_prepare(dev, format, bpt_num_bytes, dmab_bdl, false, direction, false);
+ if (IS_ERR(bpt_stream)) {
+ dev_err(sdev->dev, "%s: SDW BPT DMA prepare failed: dir %d\n",
+ __func__, direction);
+ return PTR_ERR(bpt_stream);
+ }
+ *sdw_bpt_stream = bpt_stream;
+
+ if (!sdev->dspless_mode_selected) {
+ struct hdac_stream *hstream;
+ u32 mask;
+
+ /* decouple host and link DMA if the DSP is used */
+ hstream = &bpt_stream->hstream;
+ mask = BIT(hstream->index);
+
+ snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL, mask, mask);
+
+ snd_hdac_ext_stream_reset(bpt_stream);
+
+ snd_hdac_ext_stream_setup(bpt_stream, format);
+ }
+
+ if (hdac_stream(bpt_stream)->direction == SNDRV_PCM_STREAM_PLAYBACK) {
+ struct hdac_bus *bus = sof_to_bus(sdev);
+ struct hdac_ext_link *hlink;
+ int stream_tag;
+
+ stream_tag = hdac_stream(bpt_stream)->stream_tag;
+ hlink = hdac_bus_eml_sdw_get_hlink(bus);
+
+ snd_hdac_ext_bus_link_set_stream_id(hlink, stream_tag);
+ }
+ return 0;
+}
+
+static int hda_sdw_bpt_dma_deprepare(struct device *dev, struct hdac_ext_stream *sdw_bpt_stream,
+ struct snd_dma_buffer *dmab_bdl)
+{
+ struct snd_sof_dev *sdev = dev_get_drvdata(dev);
+ struct hdac_stream *hstream;
+ u32 mask;
+ int ret;
+
+ ret = hda_cl_cleanup(sdev->dev, dmab_bdl, true, sdw_bpt_stream);
+ if (ret < 0) {
+ dev_err(sdev->dev, "%s: SDW BPT DMA cleanup failed\n",
+ __func__);
+ return ret;
+ }
+
+ if (hdac_stream(sdw_bpt_stream)->direction == SNDRV_PCM_STREAM_PLAYBACK) {
+ struct hdac_bus *bus = sof_to_bus(sdev);
+ struct hdac_ext_link *hlink;
+ int stream_tag;
+
+ stream_tag = hdac_stream(sdw_bpt_stream)->stream_tag;
+ hlink = hdac_bus_eml_sdw_get_hlink(bus);
+
+ snd_hdac_ext_bus_link_clear_stream_id(hlink, stream_tag);
+ }
+
+ if (!sdev->dspless_mode_selected) {
+ /* Release CHAIN_DMA resources */
+ ret = chain_dma_trigger(sdev, hdac_stream(sdw_bpt_stream)->stream_tag,
+ hdac_stream(sdw_bpt_stream)->direction,
+ SOF_IPC4_PIPE_RESET);
+ if (ret < 0)
+ dev_err(sdev->dev, "%s: chain_dma_trigger PIPE_RESET failed: %d\n",
+ __func__, ret);
+
+ /* couple host and link DMA */
+ hstream = &sdw_bpt_stream->hstream;
+ mask = BIT(hstream->index);
+
+ snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL, mask, 0);
+ }
+
+ return 0;
+}
+
+static int hda_sdw_bpt_dma_enable(struct device *dev, struct hdac_ext_stream *sdw_bpt_stream)
+{
+ struct snd_sof_dev *sdev = dev_get_drvdata(dev);
+ int ret;
+
+ ret = hda_cl_trigger(sdev->dev, sdw_bpt_stream, SNDRV_PCM_TRIGGER_START);
+ if (ret < 0)
+ dev_err(sdev->dev, "%s: SDW BPT DMA trigger start failed\n", __func__);
+
+ if (!sdev->dspless_mode_selected) {
+ /* the chain DMA needs to be programmed before the DMAs */
+ ret = chain_dma_trigger(sdev, hdac_stream(sdw_bpt_stream)->stream_tag,
+ hdac_stream(sdw_bpt_stream)->direction,
+ SOF_IPC4_PIPE_RUNNING);
+ if (ret < 0) {
+ dev_err(sdev->dev, "%s: chain_dma_trigger failed: %d\n",
+ __func__, ret);
+ hda_cl_trigger(sdev->dev, sdw_bpt_stream, SNDRV_PCM_TRIGGER_STOP);
+ return ret;
+ }
+ snd_hdac_ext_stream_start(sdw_bpt_stream);
+ }
+
+ return ret;
+}
+
+static int hda_sdw_bpt_dma_disable(struct device *dev, struct hdac_ext_stream *sdw_bpt_stream)
+{
+ struct snd_sof_dev *sdev = dev_get_drvdata(dev);
+ int ret;
+
+ if (!sdev->dspless_mode_selected) {
+ snd_hdac_ext_stream_clear(sdw_bpt_stream);
+
+ ret = chain_dma_trigger(sdev, hdac_stream(sdw_bpt_stream)->stream_tag,
+ hdac_stream(sdw_bpt_stream)->direction,
+ SOF_IPC4_PIPE_PAUSED);
+ if (ret < 0)
+ dev_err(sdev->dev, "%s: chain_dma_trigger PIPE_PAUSED failed: %d\n",
+ __func__, ret);
+ }
+
+ ret = hda_cl_trigger(sdev->dev, sdw_bpt_stream, SNDRV_PCM_TRIGGER_STOP);
+ if (ret < 0)
+ dev_err(sdev->dev, "%s: SDW BPT DMA trigger stop failed\n", __func__);
+
+ return ret;
+}
+
+int hda_sdw_bpt_open(struct device *dev, int link_id, struct hdac_ext_stream **bpt_tx_stream,
+ struct snd_dma_buffer *dmab_tx_bdl, u32 bpt_tx_num_bytes,
+ u32 tx_dma_bandwidth, struct hdac_ext_stream **bpt_rx_stream,
+ struct snd_dma_buffer *dmab_rx_bdl, u32 bpt_rx_num_bytes,
+ u32 rx_dma_bandwidth)
+{
+ struct snd_sof_dev *sdev = dev_get_drvdata(dev);
+ unsigned int num_channels_tx;
+ unsigned int num_channels_rx;
+ int ret1;
+ int ret;
+
+ num_channels_tx = DIV_ROUND_UP(tx_dma_bandwidth, BPT_FREQUENCY * 32);
+
+ ret = hda_sdw_bpt_dma_prepare(dev, bpt_tx_stream, dmab_tx_bdl, bpt_tx_num_bytes,
+ num_channels_tx, SNDRV_PCM_STREAM_PLAYBACK);
+ if (ret < 0) {
+ dev_err(dev, "%s: hda_sdw_bpt_dma_prepare failed for TX: %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ num_channels_rx = DIV_ROUND_UP(rx_dma_bandwidth, BPT_FREQUENCY * 32);
+
+ ret = hda_sdw_bpt_dma_prepare(dev, bpt_rx_stream, dmab_rx_bdl, bpt_rx_num_bytes,
+ num_channels_rx, SNDRV_PCM_STREAM_CAPTURE);
+ if (ret < 0) {
+ dev_err(dev, "%s: hda_sdw_bpt_dma_prepare failed for RX: %d\n",
+ __func__, ret);
+
+ ret1 = hda_sdw_bpt_dma_deprepare(dev, *bpt_tx_stream, dmab_tx_bdl);
+ if (ret1 < 0)
+ dev_err(dev, "%s: hda_sdw_bpt_dma_deprepare failed for TX: %d\n",
+ __func__, ret1);
+ return ret;
+ }
+
+ /* we need to map the channels in PCMSyCM registers */
+ ret = hdac_bus_eml_sdw_map_stream_ch(sof_to_bus(sdev), link_id,
+ 0, /* cpu_dai->id -> PDI0 */
+ GENMASK(num_channels_tx - 1, 0),
+ hdac_stream(*bpt_tx_stream)->stream_tag,
+ SNDRV_PCM_STREAM_PLAYBACK);
+ if (ret < 0) {
+ dev_err(dev, "%s: hdac_bus_eml_sdw_map_stream_ch failed for TX: %d\n",
+ __func__, ret);
+ goto close;
+ }
+
+ ret = hdac_bus_eml_sdw_map_stream_ch(sof_to_bus(sdev), link_id,
+ 1, /* cpu_dai->id -> PDI1 */
+ GENMASK(num_channels_rx - 1, 0),
+ hdac_stream(*bpt_rx_stream)->stream_tag,
+ SNDRV_PCM_STREAM_CAPTURE);
+ if (!ret)
+ return 0;
+
+ dev_err(dev, "%s: hdac_bus_eml_sdw_map_stream_ch failed for RX: %d\n",
+ __func__, ret);
+
+close:
+ ret1 = hda_sdw_bpt_close(dev, *bpt_tx_stream, dmab_tx_bdl, *bpt_rx_stream, dmab_rx_bdl);
+ if (ret1 < 0)
+ dev_err(dev, "%s: hda_sdw_bpt_close failed: %d\n",
+ __func__, ret1);
+
+ return ret;
+}
+EXPORT_SYMBOL_NS(hda_sdw_bpt_open, "SND_SOC_SOF_INTEL_HDA_SDW_BPT");
+
+int hda_sdw_bpt_send_async(struct device *dev, struct hdac_ext_stream *bpt_tx_stream,
+ struct hdac_ext_stream *bpt_rx_stream)
+{
+ int ret1;
+ int ret;
+
+ ret = hda_sdw_bpt_dma_enable(dev, bpt_tx_stream);
+ if (ret < 0) {
+ dev_err(dev, "%s: hda_sdw_bpt_dma_enable failed for TX: %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = hda_sdw_bpt_dma_enable(dev, bpt_rx_stream);
+ if (ret < 0) {
+ dev_err(dev, "%s: hda_sdw_bpt_dma_enable failed for RX: %d\n",
+ __func__, ret);
+
+ ret1 = hda_sdw_bpt_dma_disable(dev, bpt_tx_stream);
+ if (ret1 < 0)
+ dev_err(dev, "%s: hda_sdw_bpt_dma_disable failed for TX: %d\n",
+ __func__, ret1);
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_NS(hda_sdw_bpt_send_async, "SND_SOC_SOF_INTEL_HDA_SDW_BPT");
+
+/*
+ * 3s is several orders of magnitude larger than what is needed for a
+ * typical firmware download.
+ */
+#define HDA_BPT_IOC_TIMEOUT_MS 3000
+
+int hda_sdw_bpt_wait(struct device *dev, struct hdac_ext_stream *bpt_tx_stream,
+ struct hdac_ext_stream *bpt_rx_stream)
+{
+ struct sof_intel_hda_stream *hda_tx_stream;
+ struct sof_intel_hda_stream *hda_rx_stream;
+ snd_pcm_uframes_t tx_position;
+ snd_pcm_uframes_t rx_position;
+ unsigned long time_tx_left;
+ unsigned long time_rx_left;
+ int ret = 0;
+ int ret1;
+ int i;
+
+ hda_tx_stream = container_of(bpt_tx_stream, struct sof_intel_hda_stream, hext_stream);
+ hda_rx_stream = container_of(bpt_rx_stream, struct sof_intel_hda_stream, hext_stream);
+
+ time_tx_left = wait_for_completion_timeout(&hda_tx_stream->ioc,
+ msecs_to_jiffies(HDA_BPT_IOC_TIMEOUT_MS));
+ if (!time_tx_left) {
+ tx_position = hda_dsp_stream_get_position(hdac_stream(bpt_tx_stream),
+ SNDRV_PCM_STREAM_PLAYBACK, false);
+ dev_err(dev, "%s: SDW BPT TX DMA did not complete: %ld\n",
+ __func__, tx_position);
+ ret = -ETIMEDOUT;
+ goto dma_disable;
+ }
+
+ /* Make sure the DMA is flushed */
+ i = 0;
+ do {
+ tx_position = hda_dsp_stream_get_position(hdac_stream(bpt_tx_stream),
+ SNDRV_PCM_STREAM_PLAYBACK, false);
+ usleep_range(1000, 1010);
+ i++;
+ } while (tx_position && i < HDA_BPT_IOC_TIMEOUT_MS);
+ if (tx_position) {
+ dev_err(dev, "%s: SDW BPT TX DMA position %ld was not cleared\n",
+ __func__, tx_position);
+ ret = -ETIMEDOUT;
+ goto dma_disable;
+ }
+
+ /* the wait should be minimal here */
+ time_rx_left = wait_for_completion_timeout(&hda_rx_stream->ioc,
+ msecs_to_jiffies(HDA_BPT_IOC_TIMEOUT_MS));
+ if (!time_rx_left) {
+ rx_position = hda_dsp_stream_get_position(hdac_stream(bpt_rx_stream),
+ SNDRV_PCM_STREAM_CAPTURE, false);
+ dev_err(dev, "%s: SDW BPT RX DMA did not complete: %ld\n",
+ __func__, rx_position);
+ ret = -ETIMEDOUT;
+ goto dma_disable;
+ }
+
+ /* Make sure the DMA is flushed */
+ i = 0;
+ do {
+ rx_position = hda_dsp_stream_get_position(hdac_stream(bpt_rx_stream),
+ SNDRV_PCM_STREAM_CAPTURE, false);
+ usleep_range(1000, 1010);
+ i++;
+ } while (rx_position && i < HDA_BPT_IOC_TIMEOUT_MS);
+ if (rx_position) {
+ dev_err(dev, "%s: SDW BPT RX DMA position %ld was not cleared\n",
+ __func__, rx_position);
+ ret = -ETIMEDOUT;
+ goto dma_disable;
+ }
+
+dma_disable:
+ ret1 = hda_sdw_bpt_dma_disable(dev, bpt_rx_stream);
+ if (!ret)
+ ret = ret1;
+
+ ret1 = hda_sdw_bpt_dma_disable(dev, bpt_tx_stream);
+ if (!ret)
+ ret = ret1;
+
+ return ret;
+}
+EXPORT_SYMBOL_NS(hda_sdw_bpt_wait, "SND_SOC_SOF_INTEL_HDA_SDW_BPT");
+
+int hda_sdw_bpt_close(struct device *dev, struct hdac_ext_stream *bpt_tx_stream,
+ struct snd_dma_buffer *dmab_tx_bdl, struct hdac_ext_stream *bpt_rx_stream,
+ struct snd_dma_buffer *dmab_rx_bdl)
+{
+ int ret;
+ int ret1;
+
+ ret = hda_sdw_bpt_dma_deprepare(dev, bpt_rx_stream, dmab_rx_bdl);
+
+ ret1 = hda_sdw_bpt_dma_deprepare(dev, bpt_tx_stream, dmab_tx_bdl);
+ if (!ret)
+ ret = ret1;
+
+ return ret;
+}
+EXPORT_SYMBOL_NS(hda_sdw_bpt_close, "SND_SOC_SOF_INTEL_HDA_SDW_BPT");
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_DESCRIPTION("SOF helpers for HDaudio SoundWire BPT");
+MODULE_IMPORT_NS("SND_SOC_SOF_INTEL_HDA_COMMON");
+MODULE_IMPORT_NS("SND_SOC_SOF_HDA_MLINK");
diff --git a/sound/soc/sof/intel/hda-stream.c b/sound/soc/sof/intel/hda-stream.c
index 1bda14c3590c..aa6b0247d5c9 100644
--- a/sound/soc/sof/intel/hda-stream.c
+++ b/sound/soc/sof/intel/hda-stream.c
@@ -3,7 +3,7 @@
// 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) 2018 Intel Corporation. All rights reserved.
+// Copyright(c) 2018 Intel Corporation
//
// Authors: Liam Girdwood <liam.r.girdwood@linux.intel.com>
// Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
@@ -15,20 +15,55 @@
* Hardware interface for generic Intel audio DSP HDA IP
*/
-#include <linux/pm_runtime.h>
#include <sound/hdaudio_ext.h>
#include <sound/hda_register.h>
#include <sound/sof.h>
+#include <trace/events/sof_intel.h>
#include "../ops.h"
#include "../sof-audio.h"
+#include "../ipc4-priv.h"
#include "hda.h"
+int sof_hda_position_quirk = SOF_HDA_POSITION_QUIRK_USE_DPIB_REGISTERS;
+module_param_named(position_quirk, sof_hda_position_quirk, int, 0444);
+MODULE_PARM_DESC(position_quirk, "SOF HDaudio position quirk");
+EXPORT_SYMBOL_NS(sof_hda_position_quirk, "SND_SOC_SOF_INTEL_HDA_COMMON");
+
+#define HDA_LTRP_GB_VALUE_US 95
+
+static inline const char *hda_hstream_direction_str(struct hdac_stream *hstream)
+{
+ if (hstream->direction == SNDRV_PCM_STREAM_PLAYBACK)
+ return "Playback";
+ else
+ return "Capture";
+}
+
+static char *hda_hstream_dbg_get_stream_info_str(struct hdac_stream *hstream)
+{
+ struct snd_soc_pcm_runtime *rtd;
+
+ if (hstream->substream)
+ rtd = snd_soc_substream_to_rtd(hstream->substream);
+ else if (hstream->cstream)
+ rtd = hstream->cstream->private_data;
+ else
+ /* Non audio DMA user, like dma-trace */
+ return kasprintf(GFP_KERNEL, "-- (%s, stream_tag: %u)",
+ hda_hstream_direction_str(hstream),
+ hstream->stream_tag);
+
+ return kasprintf(GFP_KERNEL, "dai_link \"%s\" (%s, stream_tag: %u)",
+ rtd->dai_link->name, hda_hstream_direction_str(hstream),
+ hstream->stream_tag);
+}
+
/*
* set up one of BDL entries for a stream
*/
static int hda_setup_bdle(struct snd_sof_dev *sdev,
struct snd_dma_buffer *dmab,
- struct hdac_stream *stream,
+ struct hdac_stream *hstream,
struct sof_intel_dsp_bdl **bdlp,
int offset, int size, int ioc)
{
@@ -39,7 +74,7 @@ static int hda_setup_bdle(struct snd_sof_dev *sdev,
dma_addr_t addr;
int chunk;
- if (stream->frags >= HDA_DSP_MAX_BDL_ENTRIES) {
+ if (hstream->frags >= HDA_DSP_MAX_BDL_ENTRIES) {
dev_err(sdev->dev, "error: stream frags exceeded\n");
return -EINVAL;
}
@@ -62,11 +97,8 @@ static int hda_setup_bdle(struct snd_sof_dev *sdev,
size -= chunk;
bdl->ioc = (size || !ioc) ? 0 : cpu_to_le32(0x01);
bdl++;
- stream->frags++;
+ hstream->frags++;
offset += chunk;
-
- dev_vdbg(sdev->dev, "bdl, frags:%d, chunk size:0x%x;\n",
- stream->frags, chunk);
}
*bdlp = bdl;
@@ -79,47 +111,73 @@ static int hda_setup_bdle(struct snd_sof_dev *sdev,
*/
int hda_dsp_stream_setup_bdl(struct snd_sof_dev *sdev,
struct snd_dma_buffer *dmab,
- struct hdac_stream *stream)
+ struct hdac_stream *hstream)
{
struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
struct sof_intel_dsp_bdl *bdl;
int i, offset, period_bytes, periods;
int remain, ioc;
- period_bytes = stream->period_bytes;
- dev_dbg(sdev->dev, "period_bytes:0x%x\n", period_bytes);
- if (!period_bytes)
- period_bytes = stream->bufsize;
+ period_bytes = hstream->period_bytes;
+ dev_dbg(sdev->dev, "period_bytes: %#x, bufsize: %#x\n", period_bytes,
+ hstream->bufsize);
+
+ if (!period_bytes) {
+ unsigned int chunk_size;
+
+ chunk_size = snd_sgbuf_get_chunk_size(dmab, 0, hstream->bufsize);
+
+ period_bytes = hstream->bufsize;
+
+ /*
+ * HDA spec demands that the LVI value must be at least one
+ * before the DMA operation can begin. This means that there
+ * must be at least two BDLE present for the transfer.
+ *
+ * If the buffer is not a single continuous area then the
+ * hda_setup_bdle() will create multiple BDLEs for each segment.
+ * If the memory is a single continuous area, force it to be
+ * split into two 'periods', otherwise the transfer will be
+ * split to multiple BDLE for each chunk in hda_setup_bdle()
+ *
+ * Note: period_bytes == 0 can only happen for firmware or
+ * library loading. The data size is 4K aligned, which ensures
+ * that the second chunk's start address will be 128-byte
+ * aligned.
+ */
+ if (chunk_size == hstream->bufsize)
+ period_bytes /= 2;
+ }
- periods = stream->bufsize / period_bytes;
+ periods = hstream->bufsize / period_bytes;
- dev_dbg(sdev->dev, "periods:%d\n", periods);
+ dev_dbg(sdev->dev, "periods: %d\n", periods);
- remain = stream->bufsize % period_bytes;
+ remain = hstream->bufsize % period_bytes;
if (remain)
periods++;
/* program the initial BDL entries */
- bdl = (struct sof_intel_dsp_bdl *)stream->bdl.area;
+ bdl = (struct sof_intel_dsp_bdl *)hstream->bdl.area;
offset = 0;
- stream->frags = 0;
+ hstream->frags = 0;
/*
* set IOC if don't use position IPC
* and period_wakeup needed.
*/
ioc = hda->no_ipc_position ?
- !stream->no_period_wakeup : 0;
+ !hstream->no_period_wakeup : 0;
for (i = 0; i < periods; i++) {
if (i == (periods - 1) && remain)
/* set the last small entry */
offset = hda_setup_bdle(sdev, dmab,
- stream, &bdl, offset,
+ hstream, &bdl, offset,
remain, 0);
else
offset = hda_setup_bdle(sdev, dmab,
- stream, &bdl, offset,
+ hstream, &bdl, offset,
period_bytes, ioc);
}
@@ -127,10 +185,10 @@ int hda_dsp_stream_setup_bdl(struct snd_sof_dev *sdev,
}
int hda_dsp_stream_spib_config(struct snd_sof_dev *sdev,
- struct hdac_ext_stream *stream,
+ struct hdac_ext_stream *hext_stream,
int enable, u32 size)
{
- struct hdac_stream *hstream = &stream->hstream;
+ struct hdac_stream *hstream = &hext_stream->hstream;
u32 mask;
if (!sdev->bar[HDA_DSP_SPIB_BAR]) {
@@ -146,18 +204,20 @@ int hda_dsp_stream_spib_config(struct snd_sof_dev *sdev,
enable << hstream->index);
/* set the SPIB value */
- sof_io_write(sdev, stream->spib_addr, size);
+ sof_io_write(sdev, hstream->spib_addr, size);
return 0;
}
/* get next unused stream */
struct hdac_ext_stream *
-hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction)
+hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction, u32 flags)
{
+ const struct sof_intel_dsp_desc *chip_info = get_chip_info(sdev->pdata);
+ struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
struct hdac_bus *bus = sof_to_bus(sdev);
struct sof_intel_hda_stream *hda_stream;
- struct hdac_ext_stream *stream = NULL;
+ struct hdac_ext_stream *hext_stream = NULL;
struct hdac_stream *s;
spin_lock_irq(&bus->reg_lock);
@@ -165,10 +225,10 @@ hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction)
/* get an unused stream */
list_for_each_entry(s, &bus->stream_list, list) {
if (s->direction == direction && !s->opened) {
- stream = stream_to_hdac_ext_stream(s);
- hda_stream = container_of(stream,
+ hext_stream = stream_to_hdac_ext_stream(s);
+ hda_stream = container_of(hext_stream,
struct sof_intel_hda_stream,
- hda_stream);
+ hext_stream);
/* check if the host DMA channel is reserved */
if (hda_stream->host_reserved)
continue;
@@ -181,83 +241,138 @@ hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction)
spin_unlock_irq(&bus->reg_lock);
/* stream found ? */
- if (!stream)
- dev_err(sdev->dev, "error: no free %s streams\n",
- direction == SNDRV_PCM_STREAM_PLAYBACK ?
- "playback" : "capture");
+ if (!hext_stream) {
+ dev_err(sdev->dev, "error: no free %s streams\n", snd_pcm_direction_name(direction));
+ return hext_stream;
+ }
+
+ hda_stream->flags = flags;
/*
- * Disable DMI Link L1 entry when capture stream is opened.
+ * Prevent DMI Link L1 entry for streams that don't support it.
* Workaround to address a known issue with host DMA that results
- * in xruns during pause/release in capture scenarios.
+ * in xruns during pause/release in capture scenarios. This is not needed for the ACE IP.
*/
- if (!IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_ALWAYS_ENABLE_DMI_L1))
- if (stream && direction == SNDRV_PCM_STREAM_CAPTURE)
- snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR,
- HDA_VS_INTEL_EM2,
- HDA_VS_INTEL_EM2_L1SEN, 0);
+ if (chip_info->hw_ip_version < SOF_INTEL_ACE_1_0 &&
+ !(flags & SOF_HDA_STREAM_DMI_L1_COMPATIBLE)) {
+ snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR,
+ HDA_VS_INTEL_EM2,
+ HDA_VS_INTEL_EM2_L1SEN, 0);
+ hda->l1_disabled = true;
+ }
- return stream;
+ return hext_stream;
}
/* free a stream */
int hda_dsp_stream_put(struct snd_sof_dev *sdev, int direction, int stream_tag)
{
+ const struct sof_intel_dsp_desc *chip_info = get_chip_info(sdev->pdata);
+ struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
struct hdac_bus *bus = sof_to_bus(sdev);
+ struct sof_intel_hda_stream *hda_stream;
+ struct hdac_ext_stream *hext_stream;
struct hdac_stream *s;
- bool active_capture_stream = false;
+ bool dmi_l1_enable = true;
bool found = false;
spin_lock_irq(&bus->reg_lock);
/*
- * close stream matching the stream tag
- * and check if there are any open capture streams.
+ * close stream matching the stream tag and check if there are any open streams
+ * that are DMI L1 incompatible.
*/
list_for_each_entry(s, &bus->stream_list, list) {
+ hext_stream = stream_to_hdac_ext_stream(s);
+ hda_stream = container_of(hext_stream, struct sof_intel_hda_stream, hext_stream);
+
if (!s->opened)
continue;
if (s->direction == direction && s->stream_tag == stream_tag) {
s->opened = false;
found = true;
- } else if (s->direction == SNDRV_PCM_STREAM_CAPTURE) {
- active_capture_stream = true;
+ } else if (!(hda_stream->flags & SOF_HDA_STREAM_DMI_L1_COMPATIBLE)) {
+ dmi_l1_enable = false;
}
}
spin_unlock_irq(&bus->reg_lock);
- /* Enable DMI L1 entry if there are no capture streams open */
- if (!IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_ALWAYS_ENABLE_DMI_L1))
- if (!active_capture_stream)
- snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR,
- HDA_VS_INTEL_EM2,
- HDA_VS_INTEL_EM2_L1SEN,
- HDA_VS_INTEL_EM2_L1SEN);
+ /* Enable DMI L1 if permitted */
+ if (chip_info->hw_ip_version < SOF_INTEL_ACE_1_0 && dmi_l1_enable) {
+ snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, HDA_VS_INTEL_EM2,
+ HDA_VS_INTEL_EM2_L1SEN, HDA_VS_INTEL_EM2_L1SEN);
+ hda->l1_disabled = false;
+ }
if (!found) {
- dev_dbg(sdev->dev, "stream_tag %d not opened!\n", stream_tag);
+ dev_err(sdev->dev, "%s: stream_tag %d not opened!\n",
+ __func__, stream_tag);
return -ENODEV;
}
return 0;
}
+static int hda_dsp_stream_reset(struct snd_sof_dev *sdev, struct hdac_stream *hstream)
+{
+ int sd_offset = SOF_STREAM_SD_OFFSET(hstream);
+ int timeout = HDA_DSP_STREAM_RESET_TIMEOUT;
+ u32 val;
+
+ /* enter stream reset */
+ snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset, SOF_STREAM_SD_OFFSET_CRST,
+ SOF_STREAM_SD_OFFSET_CRST);
+ do {
+ val = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, sd_offset);
+ if (val & SOF_STREAM_SD_OFFSET_CRST)
+ break;
+ } while (--timeout);
+ if (timeout == 0) {
+ dev_err(sdev->dev, "timeout waiting for stream reset\n");
+ return -ETIMEDOUT;
+ }
+
+ timeout = HDA_DSP_STREAM_RESET_TIMEOUT;
+
+ /* exit stream reset and wait to read a zero before reading any other register */
+ snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset, SOF_STREAM_SD_OFFSET_CRST, 0x0);
+
+ /* wait for hardware to report that stream is out of reset */
+ udelay(3);
+ do {
+ val = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, sd_offset);
+ if ((val & SOF_STREAM_SD_OFFSET_CRST) == 0)
+ break;
+ } while (--timeout);
+ if (timeout == 0) {
+ dev_err(sdev->dev, "timeout waiting for stream to exit reset\n");
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
int hda_dsp_stream_trigger(struct snd_sof_dev *sdev,
- struct hdac_ext_stream *stream, int cmd)
+ struct hdac_ext_stream *hext_stream, int cmd)
{
- struct hdac_stream *hstream = &stream->hstream;
+ struct hdac_stream *hstream = &hext_stream->hstream;
int sd_offset = SOF_STREAM_SD_OFFSET(hstream);
u32 dma_start = SOF_HDA_SD_CTL_DMA_START;
- int ret;
+ int ret = 0;
u32 run;
/* cmd must be for audio stream */
switch (cmd) {
- case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ if (!sdev->dspless_mode_selected)
+ break;
+ fallthrough;
case SNDRV_PCM_TRIGGER_START:
+ if (hstream->running)
+ break;
+
snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTCTL,
1 << hstream->index,
1 << hstream->index);
@@ -276,17 +391,15 @@ int hda_dsp_stream_trigger(struct snd_sof_dev *sdev,
HDA_DSP_REG_POLL_INTERVAL_US,
HDA_DSP_STREAM_RUN_TIMEOUT);
- if (ret < 0) {
- dev_err(sdev->dev,
- "error: %s: cmd %d: timeout on STREAM_SD_OFFSET read\n",
- __func__, cmd);
- return ret;
- }
+ if (ret >= 0)
+ hstream->running = true;
- hstream->running = true;
break;
- case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ if (!sdev->dspless_mode_selected)
+ break;
+ fallthrough;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_STOP:
snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR,
sd_offset,
@@ -299,26 +412,103 @@ int hda_dsp_stream_trigger(struct snd_sof_dev *sdev,
HDA_DSP_REG_POLL_INTERVAL_US,
HDA_DSP_STREAM_RUN_TIMEOUT);
- if (ret < 0) {
- dev_err(sdev->dev,
- "error: %s: cmd %d: timeout on STREAM_SD_OFFSET read\n",
- __func__, cmd);
- return ret;
- }
-
- snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, sd_offset +
- SOF_HDA_ADSP_REG_CL_SD_STS,
- SOF_HDA_CL_DMA_SD_INT_MASK);
+ if (ret >= 0) {
+ snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR,
+ sd_offset + SOF_HDA_ADSP_REG_SD_STS,
+ SOF_HDA_CL_DMA_SD_INT_MASK);
- hstream->running = false;
- snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTCTL,
- 1 << hstream->index, 0x0);
+ hstream->running = false;
+ snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR,
+ SOF_HDA_INTCTL,
+ 1 << hstream->index, 0x0);
+ }
break;
default:
dev_err(sdev->dev, "error: unknown command: %d\n", cmd);
return -EINVAL;
}
+ if (ret < 0) {
+ char *stream_name = hda_hstream_dbg_get_stream_info_str(hstream);
+
+ dev_err(sdev->dev,
+ "%s: cmd %d on %s: timeout on STREAM_SD_OFFSET read\n",
+ __func__, cmd, stream_name ? stream_name : "unknown stream");
+ kfree(stream_name);
+ }
+
+ return ret;
+}
+
+/* minimal recommended programming for ICCMAX stream */
+int hda_dsp_iccmax_stream_hw_params(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_stream,
+ struct snd_dma_buffer *dmab,
+ struct snd_pcm_hw_params *params)
+{
+ struct hdac_stream *hstream = &hext_stream->hstream;
+ int sd_offset = SOF_STREAM_SD_OFFSET(hstream);
+ int ret;
+ u32 mask = 0x1 << hstream->index;
+
+ if (!hext_stream) {
+ dev_err(sdev->dev, "error: no stream available\n");
+ return -ENODEV;
+ }
+
+ if (!dmab) {
+ dev_err(sdev->dev, "error: no dma buffer allocated!\n");
+ return -ENODEV;
+ }
+
+ if (hstream->posbuf)
+ *hstream->posbuf = 0;
+
+ /* reset BDL address */
+ snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR,
+ sd_offset + SOF_HDA_ADSP_REG_SD_BDLPL,
+ 0x0);
+ snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR,
+ sd_offset + SOF_HDA_ADSP_REG_SD_BDLPU,
+ 0x0);
+
+ hstream->frags = 0;
+
+ ret = hda_dsp_stream_setup_bdl(sdev, dmab, hstream);
+ if (ret < 0) {
+ dev_err(sdev->dev, "error: set up of BDL failed\n");
+ return ret;
+ }
+
+ /* program BDL address */
+ snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR,
+ sd_offset + SOF_HDA_ADSP_REG_SD_BDLPL,
+ (u32)hstream->bdl.addr);
+ snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR,
+ sd_offset + SOF_HDA_ADSP_REG_SD_BDLPU,
+ upper_32_bits(hstream->bdl.addr));
+
+ /* program cyclic buffer length */
+ snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR,
+ sd_offset + SOF_HDA_ADSP_REG_SD_CBL,
+ hstream->bufsize);
+
+ /* program last valid index */
+ snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR,
+ sd_offset + SOF_HDA_ADSP_REG_SD_LVI,
+ 0xffff, (hstream->frags - 1));
+
+ /* 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);
+
+ /* Follow HW recommendation to set the guardband value to 95us during FW boot */
+ snd_sof_dsp_update8(sdev, HDA_DSP_HDA_BAR, HDA_VS_INTEL_LTRP,
+ HDA_VS_INTEL_LTRP_GB_MASK, HDA_LTRP_GB_VALUE_US);
+
+ /* start DMA */
+ snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset,
+ SOF_HDA_SD_CTL_DMA_START, SOF_HDA_SD_CTL_DMA_START);
+
return 0;
}
@@ -327,33 +517,37 @@ int hda_dsp_stream_trigger(struct snd_sof_dev *sdev,
* and normal stream.
*/
int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev,
- struct hdac_ext_stream *stream,
+ struct hdac_ext_stream *hext_stream,
struct snd_dma_buffer *dmab,
struct snd_pcm_hw_params *params)
{
+ const struct sof_intel_dsp_desc *chip = get_chip_info(sdev->pdata);
struct hdac_bus *bus = sof_to_bus(sdev);
- struct hdac_stream *hstream = &stream->hstream;
- int sd_offset = SOF_STREAM_SD_OFFSET(hstream);
- int ret, timeout = HDA_DSP_STREAM_RESET_TIMEOUT;
+ struct hdac_stream *hstream;
+ int sd_offset, ret;
u32 dma_start = SOF_HDA_SD_CTL_DMA_START;
- u32 val, mask;
+ u32 mask;
u32 run;
- if (!stream) {
+ if (!hext_stream) {
dev_err(sdev->dev, "error: no stream available\n");
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);
-
if (!dmab) {
dev_err(sdev->dev, "error: no dma buffer allocated!\n");
return -ENODEV;
}
+ 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,
SOF_HDA_CL_DMA_SD_INT_MASK |
@@ -366,58 +560,34 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev,
HDA_DSP_STREAM_RUN_TIMEOUT);
if (ret < 0) {
+ char *stream_name = hda_hstream_dbg_get_stream_info_str(hstream);
+
dev_err(sdev->dev,
- "error: %s: timeout on STREAM_SD_OFFSET read1\n",
- __func__);
+ "%s: on %s: timeout on STREAM_SD_OFFSET read1\n",
+ __func__, stream_name ? stream_name : "unknown stream");
+ kfree(stream_name);
return ret;
}
snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR,
- sd_offset + SOF_HDA_ADSP_REG_CL_SD_STS,
+ sd_offset + SOF_HDA_ADSP_REG_SD_STS,
SOF_HDA_CL_DMA_SD_INT_MASK,
SOF_HDA_CL_DMA_SD_INT_MASK);
/* stream reset */
- snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset, 0x1,
- 0x1);
- udelay(3);
- do {
- val = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR,
- sd_offset);
- if (val & 0x1)
- break;
- } while (--timeout);
- if (timeout == 0) {
- dev_err(sdev->dev, "error: stream reset failed\n");
- return -ETIMEDOUT;
- }
-
- timeout = HDA_DSP_STREAM_RESET_TIMEOUT;
- snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset, 0x1,
- 0x0);
-
- /* wait for hardware to report that stream is out of reset */
- udelay(3);
- do {
- val = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR,
- sd_offset);
- if ((val & 0x1) == 0)
- break;
- } while (--timeout);
- if (timeout == 0) {
- dev_err(sdev->dev, "error: timeout waiting for stream reset\n");
- return -ETIMEDOUT;
- }
+ ret = hda_dsp_stream_reset(sdev, hstream);
+ if (ret < 0)
+ return ret;
if (hstream->posbuf)
*hstream->posbuf = 0;
/* reset BDL address */
snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR,
- sd_offset + SOF_HDA_ADSP_REG_CL_SD_BDLPL,
+ sd_offset + SOF_HDA_ADSP_REG_SD_BDLPL,
0x0);
snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR,
- sd_offset + SOF_HDA_ADSP_REG_CL_SD_BDLPU,
+ sd_offset + SOF_HDA_ADSP_REG_SD_BDLPU,
0x0);
/* clear stream status */
@@ -432,14 +602,17 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev,
HDA_DSP_STREAM_RUN_TIMEOUT);
if (ret < 0) {
+ char *stream_name = hda_hstream_dbg_get_stream_info_str(hstream);
+
dev_err(sdev->dev,
- "error: %s: timeout on STREAM_SD_OFFSET read2\n",
- __func__);
+ "%s: on %s: timeout on STREAM_SD_OFFSET read1\n",
+ __func__, stream_name ? stream_name : "unknown stream");
+ kfree(stream_name);
return ret;
}
snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR,
- sd_offset + SOF_HDA_ADSP_REG_CL_SD_STS,
+ sd_offset + SOF_HDA_ADSP_REG_SD_STS,
SOF_HDA_CL_DMA_SD_INT_MASK,
SOF_HDA_CL_DMA_SD_INT_MASK);
@@ -459,11 +632,12 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev,
/* program cyclic buffer length */
snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR,
- sd_offset + SOF_HDA_ADSP_REG_CL_SD_CBL,
+ sd_offset + SOF_HDA_ADSP_REG_SD_CBL,
hstream->bufsize);
/*
* Recommended hardware programming sequence for HDAudio DMA format
+ * on earlier platforms - this is not needed on newer platforms
*
* 1. Put DMA into coupled mode by clearing PPCTL.PROCEN bit
* for corresponding stream index before the time of writing
@@ -473,36 +647,39 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev,
* enable decoupled mode
*/
- /* 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);
+ 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,
sd_offset +
- SOF_HDA_ADSP_REG_CL_SD_FORMAT,
+ SOF_HDA_ADSP_REG_SD_FORMAT,
0xffff, hstream->format_val);
- /* 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);
+ 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,
- sd_offset + SOF_HDA_ADSP_REG_CL_SD_LVI,
+ sd_offset + SOF_HDA_ADSP_REG_SD_LVI,
0xffff, (hstream->frags - 1));
/* program BDL address */
snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR,
- sd_offset + SOF_HDA_ADSP_REG_CL_SD_BDLPL,
+ sd_offset + SOF_HDA_ADSP_REG_SD_BDLPL,
(u32)hstream->bdl.addr);
snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR,
- sd_offset + SOF_HDA_ADSP_REG_CL_SD_BDLPU,
+ sd_offset + SOF_HDA_ADSP_REG_SD_BDLPU,
upper_32_bits(hstream->bdl.addr));
- /* enable position buffer */
- if (!(snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, SOF_HDA_ADSP_DPLBASE)
- & SOF_HDA_ADSP_DPLBASE_ENABLE)) {
+ /* enable position buffer, if needed */
+ if (bus->use_posbuf && bus->posbuf.addr &&
+ !(snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, SOF_HDA_ADSP_DPLBASE)
+ & SOF_HDA_ADSP_DPLBASE_ENABLE)) {
snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, SOF_HDA_ADSP_DPUBASE,
upper_32_bits(bus->posbuf.addr));
snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, SOF_HDA_ADSP_DPLBASE,
@@ -520,8 +697,8 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev,
hstream->fifo_size =
snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR,
sd_offset +
- SOF_HDA_ADSP_REG_CL_SD_FIFOSIZE);
- hstream->fifo_size &= 0xffff;
+ SOF_HDA_ADSP_REG_SD_FIFOSIZE);
+ hstream->fifo_size &= SOF_HDA_SD_FIFOSIZE_FIFOS_MASK;
hstream->fifo_size += 1;
} else {
hstream->fifo_size = 0;
@@ -533,24 +710,35 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev,
int hda_dsp_stream_hw_free(struct snd_sof_dev *sdev,
struct snd_pcm_substream *substream)
{
- struct hdac_stream *stream = substream->runtime->private_data;
- struct hdac_ext_stream *link_dev = container_of(stream,
- struct hdac_ext_stream,
- hstream);
- struct hdac_bus *bus = sof_to_bus(sdev);
- u32 mask = 0x1 << stream->index;
+ struct hdac_stream *hstream = substream->runtime->private_data;
+ struct hdac_ext_stream *hext_stream = container_of(hstream,
+ struct hdac_ext_stream,
+ hstream);
+ int ret;
- spin_lock_irq(&bus->reg_lock);
- /* couple host and link DMA if link DMA channel is idle */
- if (!link_dev->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);
+ ret = hda_dsp_stream_reset(sdev, hstream);
+ if (ret < 0)
+ return ret;
+
+ if (!sdev->dspless_mode_selected) {
+ struct hdac_bus *bus = sof_to_bus(sdev);
+ u32 mask = BIT(hstream->index);
- stream->substream = NULL;
+ 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);
+
+ hstream->substream = NULL;
return 0;
}
+EXPORT_SYMBOL_NS(hda_dsp_stream_hw_free, "SND_SOC_SOF_INTEL_HDA_COMMON");
bool hda_dsp_check_stream_irq(struct snd_sof_dev *sdev)
{
@@ -561,8 +749,9 @@ bool hda_dsp_check_stream_irq(struct snd_sof_dev *sdev)
/* The function can be called at irq thread, so use spin_lock_irq */
spin_lock_irq(&bus->reg_lock);
- status = snd_hdac_chip_readl(bus, INTSTS);
- dev_vdbg(bus->dev, "stream irq, INTSTS status: 0x%x\n", status);
+ status = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTSTS);
+
+ trace_sof_intel_hda_dsp_check_stream_irq(sdev, status);
/* if Register inaccessible, ignore it.*/
if (status != 0xffffffff)
@@ -572,14 +761,16 @@ bool hda_dsp_check_stream_irq(struct snd_sof_dev *sdev)
return ret;
}
+EXPORT_SYMBOL_NS(hda_dsp_check_stream_irq, "SND_SOC_SOF_INTEL_HDA_COMMON");
static void
-hda_dsp_set_bytes_transferred(struct hdac_stream *hstream, u64 buffer_size)
+hda_dsp_compr_bytes_transferred(struct hdac_stream *hstream, int direction)
{
+ u64 buffer_size = hstream->bufsize;
u64 prev_pos, pos, num_bytes;
div64_u64_rem(hstream->curr_pos, buffer_size, &prev_pos);
- pos = snd_hdac_stream_get_pos_posbuf(hstream);
+ pos = hda_dsp_stream_get_position(hstream, direction, false);
if (pos < prev_pos)
num_bytes = (buffer_size - prev_pos) + pos;
@@ -598,25 +789,38 @@ static bool hda_dsp_stream_check(struct hdac_bus *bus, u32 status)
list_for_each_entry(s, &bus->stream_list, list) {
if (status & BIT(s->index) && s->opened) {
- sd_status = snd_hdac_stream_readb(s, SD_STS);
+ sd_status = readb(s->sd_addr + SOF_HDA_ADSP_REG_SD_STS);
- dev_vdbg(bus->dev, "stream %d status 0x%x\n",
- s->index, sd_status);
+ trace_sof_intel_hda_dsp_stream_status(bus->dev, s, sd_status);
- snd_hdac_stream_writeb(s, SD_STS, sd_status);
+ writeb(sd_status, s->sd_addr + SOF_HDA_ADSP_REG_SD_STS);
active = true;
- if ((!s->substream && !s->cstream) ||
- !s->running ||
- (sd_status & SOF_HDA_CL_DMA_SD_INT_COMPLETE) == 0)
+ if (!s->running)
+ continue;
+ if ((sd_status & SOF_HDA_CL_DMA_SD_INT_COMPLETE) == 0)
continue;
+ if (!s->substream && !s->cstream) {
+ /*
+ * when no substream is found, the DMA may used for code loading
+ * or data transfers which can rely on wait_for_completion()
+ */
+ struct sof_intel_hda_stream *hda_stream;
+ struct hdac_ext_stream *hext_stream;
- /* Inform ALSA only in case not do that with IPC */
+ hext_stream = stream_to_hdac_ext_stream(s);
+ hda_stream = container_of(hext_stream, struct sof_intel_hda_stream,
+ hext_stream);
+
+ complete(&hda_stream->ioc);
+ continue;
+ }
+
+ /* Inform ALSA only if the IPC position is not used */
if (s->substream && sof_hda->no_ipc_position) {
snd_sof_pcm_period_elapsed(s->substream);
} else if (s->cstream) {
- hda_dsp_set_bytes_transferred(s,
- s->cstream->runtime->buffer_size);
+ hda_dsp_compr_bytes_transferred(s, s->cstream->direction);
snd_compr_fragment_elapsed(s->cstream);
}
}
@@ -629,9 +833,6 @@ irqreturn_t hda_dsp_stream_threaded_handler(int irq, void *context)
{
struct snd_sof_dev *sdev = context;
struct hdac_bus *bus = sof_to_bus(sdev);
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
- u32 rirb_status;
-#endif
bool active;
u32 status;
int i;
@@ -643,39 +844,26 @@ irqreturn_t hda_dsp_stream_threaded_handler(int irq, void *context)
for (i = 0, active = true; i < 10 && active; i++) {
spin_lock_irq(&bus->reg_lock);
- status = snd_hdac_chip_readl(bus, INTSTS);
+ status = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTSTS);
/* check streams */
active = hda_dsp_stream_check(bus, status);
/* check and clear RIRB interrupt */
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
if (status & AZX_INT_CTRL_EN) {
- rirb_status = snd_hdac_chip_readb(bus, RIRBSTS);
- if (rirb_status & RIRB_INT_MASK) {
- /*
- * Clearing the interrupt status here ensures
- * that no interrupt gets masked after the RIRB
- * wp is read in snd_hdac_bus_update_rirb.
- */
- snd_hdac_chip_writeb(bus, RIRBSTS,
- RIRB_INT_MASK);
- active = true;
- if (rirb_status & RIRB_INT_RESPONSE)
- snd_hdac_bus_update_rirb(bus);
- }
+ active |= hda_codec_check_rirb_status(sdev);
}
-#endif
spin_unlock_irq(&bus->reg_lock);
}
return IRQ_HANDLED;
}
+EXPORT_SYMBOL_NS(hda_dsp_stream_threaded_handler, "SND_SOC_SOF_INTEL_HDA_COMMON");
int hda_dsp_stream_init(struct snd_sof_dev *sdev)
{
struct hdac_bus *bus = sof_to_bus(sdev);
- struct hdac_ext_stream *stream;
+ struct hdac_ext_stream *hext_stream;
struct hdac_stream *hstream;
struct pci_dev *pci = to_pci_dev(sdev->dev);
struct sof_intel_hda_dev *sof_hda = bus_to_sof_hda(bus);
@@ -718,18 +906,19 @@ int hda_dsp_stream_init(struct snd_sof_dev *sdev)
return -ENOMEM;
}
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
- /* mem alloc for the CORB/RIRB ringbuffers */
+ /*
+ * mem alloc for the CORB/RIRB ringbuffers - this will be used only for
+ * HDAudio codecs
+ */
ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &pci->dev,
PAGE_SIZE, &bus->rb);
if (ret < 0) {
dev_err(sdev->dev, "error: RB alloc failed\n");
return -ENOMEM;
}
-#endif
- /* create capture streams */
- for (i = 0; i < num_capture; i++) {
+ /* create capture and playback streams */
+ for (i = 0; i < num_total; i++) {
struct sof_intel_hda_stream *hda_stream;
hda_stream = devm_kzalloc(sdev->dev, sizeof(*hda_stream),
@@ -738,93 +927,47 @@ int hda_dsp_stream_init(struct snd_sof_dev *sdev)
return -ENOMEM;
hda_stream->sdev = sdev;
+ init_completion(&hda_stream->ioc);
- stream = &hda_stream->hda_stream;
-
- stream->pphc_addr = sdev->bar[HDA_DSP_PP_BAR] +
- SOF_HDA_PPHC_BASE + SOF_HDA_PPHC_INTERVAL * i;
+ hext_stream = &hda_stream->hext_stream;
- stream->pplc_addr = sdev->bar[HDA_DSP_PP_BAR] +
- SOF_HDA_PPLC_BASE + SOF_HDA_PPLC_MULTI * num_total +
- SOF_HDA_PPLC_INTERVAL * i;
-
- /* do we support SPIB */
- if (sdev->bar[HDA_DSP_SPIB_BAR]) {
- stream->spib_addr = sdev->bar[HDA_DSP_SPIB_BAR] +
- SOF_HDA_SPIB_BASE + SOF_HDA_SPIB_INTERVAL * i +
- SOF_HDA_SPIB_SPIB;
+ 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;
- stream->fifo_addr = sdev->bar[HDA_DSP_SPIB_BAR] +
- SOF_HDA_SPIB_BASE + SOF_HDA_SPIB_INTERVAL * i +
- SOF_HDA_SPIB_MAXFIFO;
+ 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 = &stream->hstream;
- hstream->bus = bus;
- hstream->sd_int_sta_mask = 1 << i;
- hstream->index = i;
- sd_offset = SOF_STREAM_SD_OFFSET(hstream);
- hstream->sd_addr = sdev->bar[HDA_DSP_HDA_BAR] + sd_offset;
- hstream->stream_tag = i + 1;
- hstream->opened = false;
- hstream->running = false;
- hstream->direction = SNDRV_PCM_STREAM_CAPTURE;
-
- /* memory alloc for stream BDL */
- ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &pci->dev,
- HDA_DSP_BDL_SIZE, &hstream->bdl);
- if (ret < 0) {
- dev_err(sdev->dev, "error: stream bdl dma alloc failed\n");
- return -ENOMEM;
- }
- hstream->posbuf = (__le32 *)(bus->posbuf.area +
- (hstream->index) * 8);
-
- list_add_tail(&hstream->list, &bus->stream_list);
- }
-
- /* create playback streams */
- for (i = num_capture; i < num_total; i++) {
- struct sof_intel_hda_stream *hda_stream;
-
- hda_stream = devm_kzalloc(sdev->dev, sizeof(*hda_stream),
- GFP_KERNEL);
- if (!hda_stream)
- return -ENOMEM;
-
- hda_stream->sdev = sdev;
-
- stream = &hda_stream->hda_stream;
-
- /* we always have DSP support */
- stream->pphc_addr = sdev->bar[HDA_DSP_PP_BAR] +
- SOF_HDA_PPHC_BASE + SOF_HDA_PPHC_INTERVAL * i;
-
- 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;
/* do we support SPIB */
if (sdev->bar[HDA_DSP_SPIB_BAR]) {
- stream->spib_addr = sdev->bar[HDA_DSP_SPIB_BAR] +
+ hstream->spib_addr = sdev->bar[HDA_DSP_SPIB_BAR] +
SOF_HDA_SPIB_BASE + SOF_HDA_SPIB_INTERVAL * i +
SOF_HDA_SPIB_SPIB;
- stream->fifo_addr = sdev->bar[HDA_DSP_SPIB_BAR] +
+ hstream->fifo_addr = sdev->bar[HDA_DSP_SPIB_BAR] +
SOF_HDA_SPIB_BASE + SOF_HDA_SPIB_INTERVAL * i +
SOF_HDA_SPIB_MAXFIFO;
}
- hstream = &stream->hstream;
hstream->bus = bus;
hstream->sd_int_sta_mask = 1 << i;
hstream->index = i;
sd_offset = SOF_STREAM_SD_OFFSET(hstream);
hstream->sd_addr = sdev->bar[HDA_DSP_HDA_BAR] + sd_offset;
- hstream->stream_tag = i - num_capture + 1;
hstream->opened = false;
hstream->running = false;
- hstream->direction = SNDRV_PCM_STREAM_PLAYBACK;
+
+ if (i < num_capture) {
+ hstream->stream_tag = i + 1;
+ hstream->direction = SNDRV_PCM_STREAM_CAPTURE;
+ } else {
+ hstream->stream_tag = i - num_capture + 1;
+ hstream->direction = SNDRV_PCM_STREAM_PLAYBACK;
+ }
/* mem alloc for stream BDL */
ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &pci->dev,
@@ -843,25 +986,32 @@ int hda_dsp_stream_init(struct snd_sof_dev *sdev)
/* store total stream count (playback + capture) from GCAP */
sof_hda->stream_max = num_total;
+ /* store stream count from GCAP required for CHAIN_DMA */
+ if (sdev->pdata->ipc_type == SOF_IPC_TYPE_4) {
+ struct sof_ipc4_fw_data *ipc4_data = sdev->private;
+
+ ipc4_data->num_playback_streams = num_playback;
+ ipc4_data->num_capture_streams = num_capture;
+ }
+
return 0;
}
+EXPORT_SYMBOL_NS(hda_dsp_stream_init, "SND_SOC_SOF_INTEL_HDA_COMMON");
void hda_dsp_stream_free(struct snd_sof_dev *sdev)
{
struct hdac_bus *bus = sof_to_bus(sdev);
struct hdac_stream *s, *_s;
- struct hdac_ext_stream *stream;
+ struct hdac_ext_stream *hext_stream;
struct sof_intel_hda_stream *hda_stream;
/* free position buffer */
if (bus->posbuf.area)
snd_dma_free_pages(&bus->posbuf);
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
- /* free position buffer */
+ /* free CORB/RIRB buffer - only used for HDaudio codecs */
if (bus->rb.area)
snd_dma_free_pages(&bus->rb);
-#endif
list_for_each_entry_safe(s, _s, &bus->stream_list, list) {
/* TODO: decouple */
@@ -870,9 +1020,169 @@ void hda_dsp_stream_free(struct snd_sof_dev *sdev)
if (s->bdl.area)
snd_dma_free_pages(&s->bdl);
list_del(&s->list);
- stream = stream_to_hdac_ext_stream(s);
- hda_stream = container_of(stream, struct sof_intel_hda_stream,
- hda_stream);
+ hext_stream = stream_to_hdac_ext_stream(s);
+ hda_stream = container_of(hext_stream, struct sof_intel_hda_stream,
+ hext_stream);
devm_kfree(sdev->dev, hda_stream);
}
}
+EXPORT_SYMBOL_NS(hda_dsp_stream_free, "SND_SOC_SOF_INTEL_HDA_COMMON");
+
+snd_pcm_uframes_t hda_dsp_stream_get_position(struct hdac_stream *hstream,
+ int direction, bool can_sleep)
+{
+ struct hdac_ext_stream *hext_stream = stream_to_hdac_ext_stream(hstream);
+ struct sof_intel_hda_stream *hda_stream = hstream_to_sof_hda_stream(hext_stream);
+ struct snd_sof_dev *sdev = hda_stream->sdev;
+ snd_pcm_uframes_t pos;
+
+ switch (sof_hda_position_quirk) {
+ case SOF_HDA_POSITION_QUIRK_USE_SKYLAKE_LEGACY:
+ /*
+ * This legacy code, inherited from the Skylake driver,
+ * mixes DPIB registers and DPIB DDR updates and
+ * does not seem to follow any known hardware recommendations.
+ * It's not clear e.g. why there is a different flow
+ * for capture and playback, the only information that matters is
+ * what traffic class is used, and on all SOF-enabled platforms
+ * only VC0 is supported so the work-around was likely not necessary
+ * and quite possibly wrong.
+ */
+
+ /* DPIB/posbuf position mode:
+ * For Playback, Use DPIB register from HDA space which
+ * reflects the actual data transferred.
+ * For Capture, Use the position buffer for pointer, as DPIB
+ * is not accurate enough, its update may be completed
+ * earlier than the data written to DDR.
+ */
+ if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
+ pos = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR,
+ AZX_REG_VS_SDXDPIB_XBASE +
+ (AZX_REG_VS_SDXDPIB_XINTERVAL *
+ hstream->index));
+ } else {
+ /*
+ * For capture stream, we need more workaround to fix the
+ * position incorrect issue:
+ *
+ * 1. Wait at least 20us before reading position buffer after
+ * the interrupt generated(IOC), to make sure position update
+ * happens on frame boundary i.e. 20.833uSec for 48KHz.
+ * 2. Perform a dummy Read to DPIB register to flush DMA
+ * position value.
+ * 3. Read the DMA Position from posbuf. Now the readback
+ * value should be >= period boundary.
+ */
+ if (can_sleep)
+ usleep_range(20, 21);
+
+ snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR,
+ AZX_REG_VS_SDXDPIB_XBASE +
+ (AZX_REG_VS_SDXDPIB_XINTERVAL *
+ hstream->index));
+ pos = snd_hdac_stream_get_pos_posbuf(hstream);
+ }
+ break;
+ case SOF_HDA_POSITION_QUIRK_USE_DPIB_REGISTERS:
+ /*
+ * In case VC1 traffic is disabled this is the recommended option
+ */
+ pos = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR,
+ AZX_REG_VS_SDXDPIB_XBASE +
+ (AZX_REG_VS_SDXDPIB_XINTERVAL *
+ hstream->index));
+ break;
+ case SOF_HDA_POSITION_QUIRK_USE_DPIB_DDR_UPDATE:
+ /*
+ * This is the recommended option when VC1 is enabled.
+ * While this isn't needed for SOF platforms it's added for
+ * consistency and debug.
+ */
+ pos = snd_hdac_stream_get_pos_posbuf(hstream);
+ break;
+ default:
+ dev_err_once(sdev->dev, "hda_position_quirk value %d not supported\n",
+ sof_hda_position_quirk);
+ pos = 0;
+ break;
+ }
+
+ if (pos >= hstream->bufsize)
+ pos = 0;
+
+ return pos;
+}
+EXPORT_SYMBOL_NS(hda_dsp_stream_get_position, "SND_SOC_SOF_INTEL_HDA_COMMON");
+
+#define merge_u64(u32_u, u32_l) (((u64)(u32_u) << 32) | (u32_l))
+
+/**
+ * hda_dsp_get_stream_llp - Retrieve the LLP (Linear Link Position) of the stream
+ * @sdev: SOF device
+ * @component: ASoC component
+ * @substream: PCM substream
+ *
+ * Returns the raw Linear Link Position value
+ */
+u64 hda_dsp_get_stream_llp(struct snd_sof_dev *sdev,
+ struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct hdac_stream *hstream = substream->runtime->private_data;
+ struct hdac_ext_stream *hext_stream = stream_to_hdac_ext_stream(hstream);
+ u32 llp_l, llp_u;
+
+ /*
+ * The pplc_addr have been calculated during probe in
+ * hda_dsp_stream_init():
+ * pplc_addr = sdev->bar[HDA_DSP_PP_BAR] +
+ * SOF_HDA_PPLC_BASE +
+ * SOF_HDA_PPLC_MULTI * total_stream +
+ * SOF_HDA_PPLC_INTERVAL * stream_index
+ *
+ * Use this pre-calculated address to avoid repeated re-calculation.
+ */
+ llp_l = readl(hext_stream->pplc_addr + AZX_REG_PPLCLLPL);
+ llp_u = readl(hext_stream->pplc_addr + AZX_REG_PPLCLLPU);
+
+ /* Compensate the LLP counter with the saved offset */
+ if (hext_stream->pplcllpl || hext_stream->pplcllpu)
+ return merge_u64(llp_u, llp_l) -
+ merge_u64(hext_stream->pplcllpu, hext_stream->pplcllpl);
+
+ return merge_u64(llp_u, llp_l);
+}
+EXPORT_SYMBOL_NS(hda_dsp_get_stream_llp, "SND_SOC_SOF_INTEL_HDA_COMMON");
+
+/**
+ * hda_dsp_get_stream_ldp - Retrieve the LDP (Linear DMA Position) of the stream
+ * @sdev: SOF device
+ * @component: ASoC component
+ * @substream: PCM substream
+ *
+ * Returns the raw Linear Link Position value
+ */
+u64 hda_dsp_get_stream_ldp(struct snd_sof_dev *sdev,
+ struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct hdac_stream *hstream = substream->runtime->private_data;
+ struct hdac_ext_stream *hext_stream = stream_to_hdac_ext_stream(hstream);
+ u32 ldp_l, ldp_u;
+
+ /*
+ * The pphc_addr have been calculated during probe in
+ * hda_dsp_stream_init():
+ * pphc_addr = sdev->bar[HDA_DSP_PP_BAR] +
+ * SOF_HDA_PPHC_BASE +
+ * SOF_HDA_PPHC_INTERVAL * stream_index
+ *
+ * Use this pre-calculated address to avoid repeated re-calculation.
+ */
+ ldp_l = readl(hext_stream->pphc_addr + AZX_REG_PPHCLDPL);
+ ldp_u = readl(hext_stream->pphc_addr + AZX_REG_PPHCLDPU);
+
+ return ((u64)ldp_u << 32) | ldp_l;
+}
+EXPORT_SYMBOL_NS(hda_dsp_get_stream_ldp, "SND_SOC_SOF_INTEL_HDA_COMMON");
diff --git a/sound/soc/sof/intel/hda-trace.c b/sound/soc/sof/intel/hda-trace.c
index 1eb746d5adeb..5da8188ffcfe 100644
--- a/sound/soc/sof/intel/hda-trace.c
+++ b/sound/soc/sof/intel/hda-trace.c
@@ -3,7 +3,7 @@
// 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) 2018 Intel Corporation. All rights reserved.
+// Copyright(c) 2018 Intel Corporation
//
// Authors: Liam Girdwood <liam.r.girdwood@linux.intel.com>
// Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
@@ -19,31 +19,31 @@
#include "../ops.h"
#include "hda.h"
-static int hda_dsp_trace_prepare(struct snd_sof_dev *sdev)
+static int hda_dsp_trace_prepare(struct snd_sof_dev *sdev, struct snd_dma_buffer *dmab)
{
struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
- struct hdac_ext_stream *stream = hda->dtrace_stream;
- struct hdac_stream *hstream = &stream->hstream;
- struct snd_dma_buffer *dmab = &sdev->dmatb;
+ struct hdac_ext_stream *hext_stream = hda->dtrace_stream;
+ struct hdac_stream *hstream = &hext_stream->hstream;
int ret;
hstream->period_bytes = 0;/* initialize period_bytes */
- hstream->bufsize = sdev->dmatb.bytes;
+ hstream->bufsize = dmab->bytes;
- ret = hda_dsp_stream_hw_params(sdev, stream, dmab, NULL);
+ ret = hda_dsp_stream_hw_params(sdev, hext_stream, dmab, NULL);
if (ret < 0)
- dev_err(sdev->dev, "error: hdac prepare failed: %x\n", ret);
+ dev_err(sdev->dev, "error: hdac prepare failed: %d\n", ret);
return ret;
}
-int hda_dsp_trace_init(struct snd_sof_dev *sdev, u32 *stream_tag)
+int hda_dsp_trace_init(struct snd_sof_dev *sdev, struct snd_dma_buffer *dmab,
+ struct sof_ipc_dma_trace_params_ext *dtrace_params)
{
struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
int ret;
- hda->dtrace_stream = hda_dsp_stream_get(sdev,
- SNDRV_PCM_STREAM_CAPTURE);
+ hda->dtrace_stream = hda_dsp_stream_get(sdev, SNDRV_PCM_STREAM_CAPTURE,
+ SOF_HDA_STREAM_DMI_L1_COMPATIBLE);
if (!hda->dtrace_stream) {
dev_err(sdev->dev,
@@ -51,22 +51,24 @@ int hda_dsp_trace_init(struct snd_sof_dev *sdev, u32 *stream_tag)
return -ENODEV;
}
- *stream_tag = hda->dtrace_stream->hstream.stream_tag;
+ dtrace_params->stream_tag = hda->dtrace_stream->hstream.stream_tag;
/*
* initialize capture stream, set BDL address and return corresponding
* stream tag which will be sent to the firmware by IPC message.
*/
- ret = hda_dsp_trace_prepare(sdev);
+ ret = hda_dsp_trace_prepare(sdev, dmab);
if (ret < 0) {
- dev_err(sdev->dev, "error: hdac trace init failed: %x\n", ret);
- hda_dsp_stream_put(sdev, SNDRV_PCM_STREAM_CAPTURE, *stream_tag);
+ dev_err(sdev->dev, "error: hdac trace init failed: %d\n", ret);
+ hda_dsp_stream_put(sdev, SNDRV_PCM_STREAM_CAPTURE,
+ dtrace_params->stream_tag);
hda->dtrace_stream = NULL;
- *stream_tag = 0;
+ dtrace_params->stream_tag = 0;
}
return ret;
}
+EXPORT_SYMBOL_NS(hda_dsp_trace_init, "SND_SOC_SOF_INTEL_HDA_COMMON");
int hda_dsp_trace_release(struct snd_sof_dev *sdev)
{
@@ -85,6 +87,7 @@ int hda_dsp_trace_release(struct snd_sof_dev *sdev)
dev_dbg(sdev->dev, "DMA trace stream is not opened!\n");
return -ENODEV;
}
+EXPORT_SYMBOL_NS(hda_dsp_trace_release, "SND_SOC_SOF_INTEL_HDA_COMMON");
int hda_dsp_trace_trigger(struct snd_sof_dev *sdev, int cmd)
{
@@ -92,3 +95,4 @@ int hda_dsp_trace_trigger(struct snd_sof_dev *sdev, int cmd)
return hda_dsp_stream_trigger(sdev, hda->dtrace_stream, cmd);
}
+EXPORT_SYMBOL_NS(hda_dsp_trace_trigger, "SND_SOC_SOF_INTEL_HDA_COMMON");
diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c
index 63ca920c8e6e..bdfe388da198 100644
--- a/sound/soc/sof/intel/hda.c
+++ b/sound/soc/sof/intel/hda.c
@@ -3,7 +3,7 @@
// 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) 2018 Intel Corporation. All rights reserved.
+// Copyright(c) 2018 Intel Corporation
//
// Authors: Liam Girdwood <liam.r.girdwood@linux.intel.com>
// Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
@@ -19,16 +19,24 @@
#include <sound/hda_register.h>
#include <linux/acpi.h>
+#include <linux/debugfs.h>
#include <linux/module.h>
#include <linux/soundwire/sdw.h>
#include <linux/soundwire/sdw_intel.h>
+#include <sound/intel-dsp-config.h>
#include <sound/intel-nhlt.h>
+#include <sound/soc-acpi-intel-ssp-common.h>
#include <sound/sof.h>
#include <sound/sof/xtensa.h>
+#include <sound/hda-mlink.h>
#include "../sof-audio.h"
+#include "../sof-pci-dev.h"
#include "../ops.h"
+#include "../ipc4-topology.h"
#include "hda.h"
+#include <trace/events/sof_intel.h>
+
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
#include <sound/soc-acpi-intel-match.h>
#endif
@@ -36,8 +44,6 @@
/* platform specific devices */
#include "shim.h"
-#define EXCEPT_MAX_HDR_SIZE 0x400
-
#if IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE)
/*
@@ -53,82 +59,89 @@ MODULE_PARM_DESC(sdw_clock_stop_quirks, "SOF SoundWire clock stop quirks");
static int sdw_params_stream(struct device *dev,
struct sdw_intel_stream_params_data *params_data)
{
- struct snd_sof_dev *sdev = dev_get_drvdata(dev);
struct snd_soc_dai *d = params_data->dai;
- struct sof_ipc_dai_config config;
- struct sof_ipc_reply reply;
- int link_id = params_data->link_id;
- int alh_stream_id = params_data->alh_stream_id;
- int ret;
- u32 size = sizeof(config);
-
- memset(&config, 0, size);
- config.hdr.size = size;
- config.hdr.cmd = SOF_IPC_GLB_DAI_MSG | SOF_IPC_DAI_CONFIG;
- config.type = SOF_DAI_INTEL_ALH;
- config.dai_index = (link_id << 8) | (d->id);
- config.alh.stream_id = alh_stream_id;
-
- /* send message to DSP */
- ret = sof_ipc_tx_message(sdev->ipc,
- config.hdr.cmd, &config, size, &reply,
- sizeof(reply));
- if (ret < 0) {
- dev_err(sdev->dev,
- "error: failed to set DAI hw_params for link %d dai->id %d ALH %d\n",
- link_id, d->id, alh_stream_id);
+ struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(d, params_data->substream->stream);
+ struct snd_sof_dai_config_data data = { 0 };
+
+ if (!w) {
+ dev_err(dev, "%s widget not found, check amp link num in the topology\n",
+ d->name);
+ return -EINVAL;
}
+ data.dai_index = (params_data->link_id << 8) | d->id;
+ data.dai_data = params_data->alh_stream_id;
+ data.dai_node_id = data.dai_data;
- return ret;
+ return hda_dai_config(w, SOF_DAI_CONFIG_FLAGS_HW_PARAMS, &data);
}
-static int sdw_free_stream(struct device *dev,
- struct sdw_intel_stream_free_data *free_data)
+static int sdw_params_free(struct device *dev, struct sdw_intel_stream_free_data *free_data)
{
- struct snd_sof_dev *sdev = dev_get_drvdata(dev);
struct snd_soc_dai *d = free_data->dai;
- struct sof_ipc_dai_config config;
- struct sof_ipc_reply reply;
- int link_id = free_data->link_id;
- int ret;
- u32 size = sizeof(config);
-
- memset(&config, 0, size);
- config.hdr.size = size;
- config.hdr.cmd = SOF_IPC_GLB_DAI_MSG | SOF_IPC_DAI_CONFIG;
- config.type = SOF_DAI_INTEL_ALH;
- config.dai_index = (link_id << 8) | d->id;
- config.alh.stream_id = 0xFFFF; /* invalid value on purpose */
-
- /* send message to DSP */
- ret = sof_ipc_tx_message(sdev->ipc,
- config.hdr.cmd, &config, size, &reply,
- sizeof(reply));
- if (ret < 0) {
- dev_err(sdev->dev,
- "error: failed to free stream for link %d dai->id %d\n",
- link_id, d->id);
+ struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(d, free_data->substream->stream);
+ struct snd_sof_dev *sdev = widget_to_sdev(w);
+
+ if (sdev->pdata->ipc_type == SOF_IPC_TYPE_4) {
+ struct snd_sof_widget *swidget = w->dobj.private;
+ struct snd_sof_dai *dai = swidget->private;
+ struct sof_ipc4_copier_data *copier_data;
+ struct sof_ipc4_copier *ipc4_copier;
+
+ ipc4_copier = dai->private;
+ ipc4_copier->dai_index = 0;
+ copier_data = &ipc4_copier->data;
+
+ /* clear the node ID */
+ copier_data->gtw_cfg.node_id &= ~SOF_IPC4_NODE_INDEX_MASK;
}
- return ret;
+ return 0;
}
-static const struct sdw_intel_ops sdw_callback = {
+struct sdw_intel_ops sdw_callback = {
.params_stream = sdw_params_stream,
- .free_stream = sdw_free_stream,
+ .free_stream = sdw_params_free,
};
-void hda_sdw_int_enable(struct snd_sof_dev *sdev, bool enable)
+static int sdw_ace2x_params_stream(struct device *dev,
+ struct sdw_intel_stream_params_data *params_data)
{
- sdw_intel_enable_irq(sdev->bar[HDA_DSP_BAR], enable);
+ return sdw_hda_dai_hw_params(params_data->substream,
+ params_data->hw_params,
+ params_data->dai,
+ params_data->link_id,
+ params_data->alh_stream_id);
}
+static int sdw_ace2x_free_stream(struct device *dev,
+ struct sdw_intel_stream_free_data *free_data)
+{
+ return sdw_hda_dai_hw_free(free_data->substream,
+ free_data->dai,
+ free_data->link_id);
+}
+
+static int sdw_ace2x_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai)
+{
+ return sdw_hda_dai_trigger(substream, cmd, dai);
+}
+
+static struct sdw_intel_ops sdw_ace2x_callback = {
+ .params_stream = sdw_ace2x_params_stream,
+ .free_stream = sdw_ace2x_free_stream,
+ .trigger = sdw_ace2x_trigger,
+};
+
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 */
@@ -143,6 +156,7 @@ static int hda_sdw_acpi_scan(struct snd_sof_dev *sdev)
static int hda_sdw_probe(struct snd_sof_dev *sdev)
{
+ const struct sof_intel_dsp_desc *chip;
struct sof_intel_hda_dev *hdev;
struct sdw_intel_res res;
void *sdw;
@@ -151,13 +165,44 @@ static int hda_sdw_probe(struct snd_sof_dev *sdev)
memset(&res, 0, sizeof(res));
- res.mmio_base = sdev->bar[HDA_DSP_BAR];
+ chip = get_chip_info(sdev->pdata);
+ if (chip->hw_ip_version < SOF_INTEL_ACE_2_0) {
+ res.mmio_base = sdev->bar[HDA_DSP_BAR];
+ res.hw_ops = &sdw_intel_cnl_hw_ops;
+ res.shim_base = hdev->desc->sdw_shim_base;
+ res.alh_base = hdev->desc->sdw_alh_base;
+ res.ext = false;
+ res.ops = &sdw_callback;
+ } else {
+ /*
+ * retrieve eml_lock needed to protect shared registers
+ * in the HDaudio multi-link areas
+ */
+ res.eml_lock = hdac_bus_eml_get_mutex(sof_to_bus(sdev), true,
+ AZX_REG_ML_LEPTR_ID_SDW);
+ if (!res.eml_lock)
+ return -ENODEV;
+
+ res.mmio_base = sdev->bar[HDA_DSP_HDA_BAR];
+ /*
+ * the SHIM and SoundWire register offsets are link-specific
+ * and will be determined when adding auxiliary devices
+ */
+ res.hw_ops = &sdw_intel_lnl_hw_ops;
+ res.ext = true;
+ res.ops = &sdw_ace2x_callback;
+
+ /* ACE3+ supports microphone privacy */
+ if (chip->hw_ip_version >= SOF_INTEL_ACE_3_0)
+ res.mic_privacy = true;
+ }
res.irq = sdev->ipc_irq;
res.handle = hdev->info.handle;
res.parent = sdev->dev;
- res.ops = &sdw_callback;
+
res.dev = sdev->dev;
res.clock_stop_quirks = sdw_clock_stop_quirks;
+ res.hbus = sof_to_bus(sdev);
/*
* ops and arg fields are not populated for now,
@@ -184,14 +229,24 @@ static int hda_sdw_probe(struct snd_sof_dev *sdev)
int hda_sdw_startup(struct snd_sof_dev *sdev)
{
struct sof_intel_hda_dev *hdev;
+ struct snd_sof_pdata *pdata = sdev->pdata;
+ int ret;
hdev = sdev->pdata->hw_pdata;
if (!hdev->sdw)
return 0;
+ if (pdata->machine && !pdata->machine->mach_params.link_mask)
+ return 0;
+
+ ret = hda_sdw_check_lcount(sdev);
+ if (ret < 0)
+ return ret;
+
return sdw_intel_startup(hdev->sdw);
}
+EXPORT_SYMBOL_NS(hda_sdw_startup, "SND_SOC_SOF_INTEL_HDA_GENERIC");
static int hda_sdw_exit(struct snd_sof_dev *sdev)
{
@@ -199,16 +254,16 @@ static int hda_sdw_exit(struct snd_sof_dev *sdev)
hdev = sdev->pdata->hw_pdata;
- hda_sdw_int_enable(sdev, false);
-
if (hdev->sdw)
sdw_intel_exit(hdev->sdw);
hdev->sdw = NULL;
+ hda_sdw_int_enable(sdev, false);
+
return 0;
}
-static bool hda_dsp_check_sdw_irq(struct snd_sof_dev *sdev)
+bool hda_common_check_sdw_irq(struct snd_sof_dev *sdev)
{
struct sof_intel_hda_dev *hdev;
bool ret = false;
@@ -233,257 +288,201 @@ static bool hda_dsp_check_sdw_irq(struct snd_sof_dev *sdev)
out:
return ret;
}
+EXPORT_SYMBOL_NS(hda_common_check_sdw_irq, "SND_SOC_SOF_INTEL_HDA_GENERIC");
+
+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);
+
+ return false;
+}
static irqreturn_t hda_dsp_sdw_thread(int irq, void *context)
{
return sdw_intel_thread(irq, context);
}
-static bool hda_sdw_check_wakeen_irq(struct snd_sof_dev *sdev)
+bool hda_sdw_check_wakeen_irq_common(struct snd_sof_dev *sdev)
{
struct sof_intel_hda_dev *hdev;
hdev = sdev->pdata->hw_pdata;
if (hdev->sdw &&
snd_sof_dsp_read(sdev, HDA_DSP_BAR,
- HDA_DSP_REG_SNDW_WAKE_STS))
+ hdev->desc->sdw_shim_base + SDW_SHIM_WAKESTS))
return true;
return false;
}
+EXPORT_SYMBOL_NS(hda_sdw_check_wakeen_irq_common, "SND_SOC_SOF_INTEL_HDA_GENERIC");
-void hda_sdw_process_wakeen(struct snd_sof_dev *sdev)
+static bool hda_sdw_check_wakeen_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_wakeen_irq)
+ return chip->check_sdw_wakeen_irq(sdev);
+
+ return false;
+}
+
+void hda_sdw_process_wakeen_common(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;
sdw_intel_process_wakeen_event(hdev->sdw);
}
+EXPORT_SYMBOL_NS(hda_sdw_process_wakeen_common, "SND_SOC_SOF_INTEL_HDA_GENERIC");
-#endif
-
-/*
- * Debug
- */
+static bool hda_dsp_sdw_check_mic_privacy_irq(struct snd_sof_dev *sdev)
+{
+ const struct sof_intel_dsp_desc *chip;
-struct hda_dsp_msg_code {
- u32 code;
- const char *msg;
-};
+ chip = get_chip_info(sdev->pdata);
+ if (chip && chip->check_mic_privacy_irq)
+ return chip->check_mic_privacy_irq(sdev, true,
+ AZX_REG_ML_LEPTR_ID_SDW);
-static bool hda_use_msi = IS_ENABLED(CONFIG_PCI);
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG)
-module_param_named(use_msi, hda_use_msi, bool, 0444);
-MODULE_PARM_DESC(use_msi, "SOF HDA use PCI MSI mode");
-#endif
+ return false;
+}
-static char *hda_model;
-module_param(hda_model, charp, 0444);
-MODULE_PARM_DESC(hda_model, "Use the given HDA board model.");
+static void hda_dsp_sdw_process_mic_privacy(struct snd_sof_dev *sdev)
+{
+ const struct sof_intel_dsp_desc *chip;
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
-static int hda_dmic_num = -1;
-module_param_named(dmic_num, hda_dmic_num, int, 0444);
-MODULE_PARM_DESC(dmic_num, "SOF HDA DMIC number");
+ chip = get_chip_info(sdev->pdata);
+ if (chip && chip->process_mic_privacy)
+ chip->process_mic_privacy(sdev, true, AZX_REG_ML_LEPTR_ID_SDW);
+}
-static bool hda_codec_use_common_hdmi = IS_ENABLED(CONFIG_SND_HDA_CODEC_HDMI);
-module_param_named(use_common_hdmi, hda_codec_use_common_hdmi, bool, 0444);
-MODULE_PARM_DESC(use_common_hdmi, "SOF HDA use common HDMI codec driver");
-#endif
+#else /* IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE) */
+static inline int hda_sdw_acpi_scan(struct snd_sof_dev *sdev)
+{
+ return 0;
+}
-static const struct hda_dsp_msg_code hda_dsp_rom_msg[] = {
- {HDA_DSP_ROM_FW_MANIFEST_LOADED, "status: manifest loaded"},
- {HDA_DSP_ROM_FW_FW_LOADED, "status: fw loaded"},
- {HDA_DSP_ROM_FW_ENTERED, "status: fw entered"},
- {HDA_DSP_ROM_CSE_ERROR, "error: cse error"},
- {HDA_DSP_ROM_CSE_WRONG_RESPONSE, "error: cse wrong response"},
- {HDA_DSP_ROM_IMR_TO_SMALL, "error: IMR too small"},
- {HDA_DSP_ROM_BASE_FW_NOT_FOUND, "error: base fw not found"},
- {HDA_DSP_ROM_CSE_VALIDATION_FAILED, "error: signature verification failed"},
- {HDA_DSP_ROM_IPC_FATAL_ERROR, "error: ipc fatal error"},
- {HDA_DSP_ROM_L2_CACHE_ERROR, "error: L2 cache error"},
- {HDA_DSP_ROM_LOAD_OFFSET_TO_SMALL, "error: load offset too small"},
- {HDA_DSP_ROM_API_PTR_INVALID, "error: API ptr invalid"},
- {HDA_DSP_ROM_BASEFW_INCOMPAT, "error: base fw incompatible"},
- {HDA_DSP_ROM_UNHANDLED_INTERRUPT, "error: unhandled interrupt"},
- {HDA_DSP_ROM_MEMORY_HOLE_ECC, "error: ECC memory hole"},
- {HDA_DSP_ROM_KERNEL_EXCEPTION, "error: kernel exception"},
- {HDA_DSP_ROM_USER_EXCEPTION, "error: user exception"},
- {HDA_DSP_ROM_UNEXPECTED_RESET, "error: unexpected reset"},
- {HDA_DSP_ROM_NULL_FW_ENTRY, "error: null FW entry point"},
-};
+static inline int hda_sdw_probe(struct snd_sof_dev *sdev)
+{
+ return 0;
+}
-static void hda_dsp_get_status_skl(struct snd_sof_dev *sdev)
+static inline int hda_sdw_exit(struct snd_sof_dev *sdev)
{
- u32 status;
- int i;
+ return 0;
+}
- status = snd_sof_dsp_read(sdev, HDA_DSP_BAR,
- HDA_ADSP_FW_STATUS_SKL);
+static inline bool hda_dsp_check_sdw_irq(struct snd_sof_dev *sdev)
+{
+ return false;
+}
- for (i = 0; i < ARRAY_SIZE(hda_dsp_rom_msg); i++) {
- if (status == hda_dsp_rom_msg[i].code) {
- dev_err(sdev->dev, "%s - code %8.8x\n",
- hda_dsp_rom_msg[i].msg, status);
- return;
- }
- }
+static inline irqreturn_t hda_dsp_sdw_thread(int irq, void *context)
+{
+ return IRQ_HANDLED;
+}
- /* not for us, must be generic sof message */
- dev_dbg(sdev->dev, "unknown ROM status value %8.8x\n", status);
+static inline bool hda_sdw_check_wakeen_irq(struct snd_sof_dev *sdev)
+{
+ return false;
}
-static void hda_dsp_get_status(struct snd_sof_dev *sdev)
+static inline bool hda_dsp_sdw_check_mic_privacy_irq(struct snd_sof_dev *sdev)
{
- u32 status;
- int i;
+ return false;
+}
- status = snd_sof_dsp_read(sdev, HDA_DSP_BAR,
- HDA_DSP_SRAM_REG_ROM_STATUS);
+static inline void hda_dsp_sdw_process_mic_privacy(struct snd_sof_dev *sdev) { }
- for (i = 0; i < ARRAY_SIZE(hda_dsp_rom_msg); i++) {
- if (status == hda_dsp_rom_msg[i].code) {
- dev_err(sdev->dev, "%s - code %8.8x\n",
- hda_dsp_rom_msg[i].msg, status);
- return;
- }
- }
+#endif /* IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE) */
- /* not for us, must be generic sof message */
- dev_dbg(sdev->dev, "unknown ROM status value %8.8x\n", status);
+/* pre fw run operations */
+int hda_dsp_pre_fw_run(struct snd_sof_dev *sdev)
+{
+ /* disable clock gating and power gating */
+ return hda_dsp_ctrl_clock_power_gating(sdev, false);
}
-static void hda_dsp_get_registers(struct snd_sof_dev *sdev,
- struct sof_ipc_dsp_oops_xtensa *xoops,
- struct sof_ipc_panic_info *panic_info,
- u32 *stack, size_t stack_words)
+/* post fw run operations */
+int hda_dsp_post_fw_run(struct snd_sof_dev *sdev)
{
- u32 offset = sdev->dsp_oops_offset;
+ int ret;
- /* first read registers */
- sof_mailbox_read(sdev, offset, xoops, sizeof(*xoops));
+ if (sdev->first_boot) {
+ struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata;
- /* note: variable AR register array is not read */
+ ret = hda_sdw_startup(sdev);
+ if (ret < 0) {
+ dev_err(sdev->dev,
+ "error: could not startup SoundWire links\n");
+ return ret;
+ }
- /* then get panic info */
- if (xoops->arch_hdr.totalsize > EXCEPT_MAX_HDR_SIZE) {
- dev_err(sdev->dev, "invalid header size 0x%x. FW oops is bogus\n",
- xoops->arch_hdr.totalsize);
- return;
+ /* Check if IMR boot is usable */
+ if (!sof_debug_check_flag(SOF_DBG_IGNORE_D3_PERSISTENT) &&
+ (sdev->fw_ready.flags & SOF_IPC_INFO_D3_PERSISTENT ||
+ sdev->pdata->ipc_type == SOF_IPC_TYPE_4)) {
+ hdev->imrboot_supported = true;
+ debugfs_create_bool("skip_imr_boot",
+ 0644, sdev->debugfs_root,
+ &hdev->skip_imr_boot);
+ }
}
- offset += xoops->arch_hdr.totalsize;
- sof_block_read(sdev, sdev->mmio_bar, offset,
- panic_info, sizeof(*panic_info));
-
- /* then get the stack */
- offset += sizeof(*panic_info);
- sof_block_read(sdev, sdev->mmio_bar, offset, stack,
- stack_words * sizeof(u32));
-}
-void hda_dsp_dump_skl(struct snd_sof_dev *sdev, u32 flags)
-{
- struct sof_ipc_dsp_oops_xtensa xoops;
- struct sof_ipc_panic_info panic_info;
- u32 stack[HDA_DSP_STACK_DUMP_SIZE];
- u32 status, panic;
-
- /* try APL specific status message types first */
- hda_dsp_get_status_skl(sdev);
-
- /* now try generic SOF status messages */
- status = snd_sof_dsp_read(sdev, HDA_DSP_BAR,
- HDA_ADSP_ERROR_CODE_SKL);
-
- /*TODO: Check: there is no define in spec, but it is used in the code*/
- panic = snd_sof_dsp_read(sdev, HDA_DSP_BAR,
- HDA_ADSP_ERROR_CODE_SKL + 0x4);
-
- if (sdev->fw_state == SOF_FW_BOOT_COMPLETE) {
- hda_dsp_get_registers(sdev, &xoops, &panic_info, stack,
- HDA_DSP_STACK_DUMP_SIZE);
- snd_sof_get_status(sdev, status, panic, &xoops, &panic_info,
- stack, HDA_DSP_STACK_DUMP_SIZE);
- } else {
- dev_err(sdev->dev, "error: status = 0x%8.8x panic = 0x%8.8x\n",
- status, panic);
- hda_dsp_get_status_skl(sdev);
- }
-}
+ hda_sdw_int_enable(sdev, true);
-void hda_dsp_dump(struct snd_sof_dev *sdev, u32 flags)
-{
- struct sof_ipc_dsp_oops_xtensa xoops;
- struct sof_ipc_panic_info panic_info;
- u32 stack[HDA_DSP_STACK_DUMP_SIZE];
- u32 status, panic;
-
- /* try APL specific status message types first */
- hda_dsp_get_status(sdev);
-
- /* now try generic SOF status messages */
- status = snd_sof_dsp_read(sdev, HDA_DSP_BAR,
- HDA_DSP_SRAM_REG_FW_STATUS);
- panic = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_SRAM_REG_FW_TRACEP);
-
- if (sdev->fw_state == SOF_FW_BOOT_COMPLETE) {
- hda_dsp_get_registers(sdev, &xoops, &panic_info, stack,
- HDA_DSP_STACK_DUMP_SIZE);
- snd_sof_get_status(sdev, status, panic, &xoops, &panic_info,
- stack, HDA_DSP_STACK_DUMP_SIZE);
- } else {
- dev_err(sdev->dev, "error: status = 0x%8.8x panic = 0x%8.8x\n",
- status, panic);
- hda_dsp_get_status(sdev);
- }
+ /* re-enable clock gating and power gating */
+ return hda_dsp_ctrl_clock_power_gating(sdev, true);
}
+EXPORT_SYMBOL_NS(hda_dsp_post_fw_run, "SND_SOC_SOF_INTEL_HDA_GENERIC");
-void hda_ipc_irq_dump(struct snd_sof_dev *sdev)
-{
- struct hdac_bus *bus = sof_to_bus(sdev);
- u32 adspis;
- u32 intsts;
- u32 intctl;
- u32 ppsts;
- u8 rirbsts;
-
- /* read key IRQ stats and config registers */
- adspis = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPIS);
- intsts = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTSTS);
- intctl = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTCTL);
- ppsts = snd_sof_dsp_read(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPSTS);
- rirbsts = snd_hdac_chip_readb(bus, RIRBSTS);
-
- dev_err(sdev->dev,
- "error: hda irq intsts 0x%8.8x intlctl 0x%8.8x rirb %2.2x\n",
- intsts, intctl, rirbsts);
- dev_err(sdev->dev,
- "error: dsp irq ppsts 0x%8.8x adspis 0x%8.8x\n",
- ppsts, adspis);
-}
+/*
+ * Debug
+ */
-void hda_ipc_dump(struct snd_sof_dev *sdev)
-{
- u32 hipcie;
- u32 hipct;
- u32 hipcctl;
-
- hda_ipc_irq_dump(sdev);
-
- /* read IPC status */
- hipcie = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCIE);
- hipct = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCT);
- hipcctl = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCCTL);
-
- /* dump the IPC regs */
- /* TODO: parse the raw msg */
- dev_err(sdev->dev,
- "error: host status 0x%8.8x dsp status 0x%8.8x mask 0x%8.8x\n",
- hipcie, hipct, hipcctl);
-}
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG)
+static bool hda_use_msi = true;
+module_param_named(use_msi, hda_use_msi, bool, 0444);
+MODULE_PARM_DESC(use_msi, "SOF HDA use PCI MSI mode");
+#else
+#define hda_use_msi (1)
+#endif
+
+static char *hda_model;
+module_param(hda_model, charp, 0444);
+MODULE_PARM_DESC(hda_model, "Use the given HDA board model.");
+
+static int dmic_num_override = -1;
+module_param_named(dmic_num, dmic_num_override, int, 0444);
+MODULE_PARM_DESC(dmic_num, "SOF HDA DMIC number");
+
+static int mclk_id_override = -1;
+module_param_named(mclk_id, mclk_id_override, int, 0444);
+MODULE_PARM_DESC(mclk_id, "SOF SSP mclk_id");
+
+static int bt_link_mask_override;
+module_param_named(bt_link_mask, bt_link_mask_override, int, 0444);
+MODULE_PARM_DESC(bt_link_mask, "SOF BT offload link mask");
static int hda_init(struct snd_sof_dev *sdev)
{
@@ -496,9 +495,12 @@ static int hda_init(struct snd_sof_dev *sdev)
bus = sof_to_bus(sdev);
/* HDA bus init */
- sof_hda_bus_init(bus, &pci->dev);
+ sof_hda_bus_init(sdev, &pci->dev);
- bus->use_posbuf = 1;
+ if (sof_hda_position_quirk == SOF_HDA_POSITION_QUIRK_USE_DPIB_REGISTERS)
+ bus->use_posbuf = 0;
+ else
+ bus->use_posbuf = 1;
bus->bdl_pos_adj = 0;
bus->sync_write = 1;
@@ -509,9 +511,7 @@ static int hda_init(struct snd_sof_dev *sdev)
/* initialise hdac bus */
bus->addr = pci_resource_start(pci, 0);
-#if IS_ENABLED(CONFIG_PCI)
bus->remap_addr = pci_ioremap_bar(pci, 0);
-#endif
if (!bus->remap_addr) {
dev_err(bus->dev, "error: ioremap error\n");
return -ENXIO;
@@ -522,87 +522,113 @@ static int hda_init(struct snd_sof_dev *sdev)
/* init i915 and HDMI codecs */
ret = hda_codec_i915_init(sdev);
- if (ret < 0)
- dev_warn(sdev->dev, "init of i915 and HDMI codec failed\n");
+ if (ret < 0 && ret != -ENODEV) {
+ dev_err_probe(sdev->dev, ret, "init of i915 and HDMI codec failed\n");
+ goto out;
+ }
/* get controller capabilities */
ret = hda_dsp_ctrl_get_caps(sdev);
- if (ret < 0)
+ if (ret < 0) {
dev_err(sdev->dev, "error: get caps error\n");
+ hda_codec_i915_exit(sdev);
+ }
+
+out:
+ if (ret < 0)
+ iounmap(sof_to_bus(sdev)->remap_addr);
return ret;
}
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
-
-static int check_nhlt_dmic(struct snd_sof_dev *sdev)
+static int check_dmic_num(struct snd_sof_dev *sdev)
{
+ struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata;
struct nhlt_acpi_table *nhlt;
- int dmic_num;
+ int dmic_num = 0;
- nhlt = intel_nhlt_init(sdev->dev);
- if (nhlt) {
+ nhlt = hdev->nhlt;
+ if (nhlt)
dmic_num = intel_nhlt_get_dmic_geo(sdev->dev, nhlt);
- intel_nhlt_free(nhlt);
- if (dmic_num == 2 || dmic_num == 4)
- return dmic_num;
+
+ dev_info(sdev->dev, "DMICs detected in NHLT tables: %d\n", dmic_num);
+
+ /* allow for module parameter override */
+ if (dmic_num_override != -1) {
+ dev_dbg(sdev->dev,
+ "overriding DMICs detected in NHLT tables %d by kernel param %d\n",
+ dmic_num, dmic_num_override);
+ dmic_num = dmic_num_override;
}
- return 0;
+ if (dmic_num < 0 || dmic_num > 4) {
+ dev_dbg(sdev->dev, "invalid dmic_number %d\n", dmic_num);
+ dmic_num = 0;
+ }
+
+ return dmic_num;
}
-static const char *fixup_tplg_name(struct snd_sof_dev *sdev,
- const char *sof_tplg_filename,
- const char *idisp_str,
- const char *dmic_str)
+static int check_nhlt_ssp_mask(struct snd_sof_dev *sdev, u8 device_type)
{
- const char *tplg_filename = NULL;
- char *filename;
- char *split_ext;
-
- filename = devm_kstrdup(sdev->dev, sof_tplg_filename, GFP_KERNEL);
- if (!filename)
- return NULL;
-
- /* this assumes a .tplg extension */
- split_ext = strsep(&filename, ".");
- if (split_ext) {
- tplg_filename = devm_kasprintf(sdev->dev, GFP_KERNEL,
- "%s%s%s.tplg",
- split_ext, idisp_str, dmic_str);
- if (!tplg_filename)
- return NULL;
+ struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata;
+ struct nhlt_acpi_table *nhlt;
+ int ssp_mask = 0;
+
+ nhlt = hdev->nhlt;
+ if (!nhlt)
+ return ssp_mask;
+
+ if (intel_nhlt_has_endpoint_type(nhlt, NHLT_LINK_SSP)) {
+ ssp_mask = intel_nhlt_ssp_endpoint_mask(nhlt, device_type);
+ if (ssp_mask)
+ dev_info(sdev->dev, "NHLT device %s(%d) detected, ssp_mask %#x\n",
+ device_type == NHLT_DEVICE_BT ? "BT" : "I2S",
+ device_type, ssp_mask);
}
- return tplg_filename;
+
+ return ssp_mask;
}
-#endif
+static int check_nhlt_ssp_mclk_mask(struct snd_sof_dev *sdev, int ssp_num)
+{
+ struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata;
+ struct nhlt_acpi_table *nhlt;
+
+ nhlt = hdev->nhlt;
+ if (!nhlt)
+ return 0;
+
+ return intel_nhlt_ssp_mclk_mask(nhlt, ssp_num);
+}
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;
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
- struct hdac_ext_link *hlink;
-#endif
struct sof_intel_hda_dev *hdev = pdata->hw_pdata;
u32 link_mask;
int ret = 0;
- device_disable_async_suspend(bus->dev);
-
/* check if dsp is there */
if (bus->ppcap)
dev_dbg(sdev->dev, "PP capability, will probe DSP later.\n");
/* Init HDA controller after i915 init */
- ret = hda_dsp_ctrl_init_chip(sdev, true);
+ ret = hda_dsp_ctrl_init_chip(sdev);
if (ret < 0) {
dev_err(bus->dev, "error: init chip failed with ret: %d\n",
ret);
return ret;
}
+ hda_bus_ml_init(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) {
@@ -631,34 +657,15 @@ static int hda_init_caps(struct snd_sof_dev *sdev)
skip_soundwire:
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
- if (bus->mlcap)
- snd_hdac_ext_bus_get_ml_capabilities(bus);
-
/* create codec instances */
- hda_codec_probe_bus(sdev, hda_codec_use_common_hdmi);
+ hda_codec_probe_bus(sdev);
if (!HDA_IDISP_CODEC(bus->codec_mask))
hda_codec_i915_display_power(sdev, false);
- /*
- * we are done probing so decrement link counts
- */
- list_for_each_entry(hlink, &bus->hlink_list, list)
- snd_hdac_ext_bus_link_put(bus, hlink);
-#endif
- return 0;
-}
-
-static const struct sof_intel_dsp_desc
- *get_chip_info(struct snd_sof_pdata *pdata)
-{
- const struct sof_dev_desc *desc = pdata->desc;
- const struct sof_intel_dsp_desc *chip_info;
-
- chip_info = desc->chip_info;
+ hda_bus_ml_put_all(bus);
- return chip_info;
+ return 0;
}
static irqreturn_t hda_dsp_interrupt_handler(int irq, void *context)
@@ -690,17 +697,33 @@ static irqreturn_t hda_dsp_interrupt_thread(int irq, void *context)
struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata;
/* deal with streams and controller first */
- if (hda_dsp_check_stream_irq(sdev))
+ if (hda_dsp_check_stream_irq(sdev)) {
+ trace_sof_intel_hda_irq(sdev, "stream");
hda_dsp_stream_threaded_handler(irq, sdev);
+ }
- if (hda_dsp_check_ipc_irq(sdev))
+ if (hda_check_ipc_irq(sdev)) {
+ trace_sof_intel_hda_irq(sdev, "ipc");
sof_ops(sdev)->irq_thread(irq, sdev);
+ }
+
+ if (hda_dsp_check_sdw_irq(sdev)) {
+ trace_sof_intel_hda_irq(sdev, "sdw");
- if (hda_dsp_check_sdw_irq(sdev))
hda_dsp_sdw_thread(irq, hdev->sdw);
- if (hda_sdw_check_wakeen_irq(sdev))
+ if (hda_dsp_sdw_check_mic_privacy_irq(sdev)) {
+ trace_sof_intel_hda_irq(sdev, "mic privacy");
+ hda_dsp_sdw_process_mic_privacy(sdev);
+ }
+ }
+
+ if (hda_sdw_check_wakeen_irq(sdev)) {
+ trace_sof_intel_hda_irq(sdev, "wakeen");
hda_sdw_process_wakeen(sdev);
+ }
+
+ hda_codec_check_for_state_change(sdev);
/* enable GIE interrupt */
snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR,
@@ -711,29 +734,32 @@ static irqreturn_t hda_dsp_interrupt_thread(int irq, void *context)
return IRQ_HANDLED;
}
-int hda_dsp_probe(struct snd_sof_dev *sdev)
+int hda_dsp_probe_early(struct snd_sof_dev *sdev)
{
struct pci_dev *pci = to_pci_dev(sdev->dev);
struct sof_intel_hda_dev *hdev;
- struct hdac_bus *bus;
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_once(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) {
@@ -743,11 +769,26 @@ int hda_dsp_probe(struct snd_sof_dev *sdev)
goto err;
}
+ sdev->num_cores = chip->cores_num;
+
hdev = devm_kzalloc(sdev->dev, sizeof(*hdev), GFP_KERNEL);
if (!hdev)
return -ENOMEM;
sdev->pdata->hw_pdata = hdev;
hdev->desc = chip;
+ ret = hda_init(sdev);
+
+err:
+ return ret;
+}
+EXPORT_SYMBOL_NS(hda_dsp_probe_early, "SND_SOC_SOF_INTEL_HDA_GENERIC");
+
+int hda_dsp_probe(struct snd_sof_dev *sdev)
+{
+ struct pci_dev *pci = to_pci_dev(sdev->dev);
+ struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata;
+ const struct sof_intel_dsp_desc *chip;
+ int ret = 0;
hdev->dmic_dev = platform_device_register_data(sdev->dev, "dmic-codec",
PLATFORM_DEVID_NONE,
@@ -767,16 +808,14 @@ int hda_dsp_probe(struct snd_sof_dev *sdev)
hdev->no_ipc_position = sof_ops(sdev)->pcm_pointer ? 1 : 0;
#endif
- /* 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)
+ hdev->no_ipc_position = 1;
+
+ if (sdev->dspless_mode_selected)
+ goto skip_dsp_setup;
/* DSP base */
-#if IS_ENABLED(CONFIG_PCI)
sdev->bar[HDA_DSP_BAR] = pci_ioremap_bar(pci, HDA_DSP_BAR);
-#endif
if (!sdev->bar[HDA_DSP_BAR]) {
dev_err(sdev->dev, "error: ioremap error\n");
ret = -ENXIO;
@@ -785,16 +824,14 @@ 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(&pci->dev, DMA_BIT_MASK(64))) {
- dev_dbg(sdev->dev, "DMA mask is 64 bit\n");
- dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(64));
- } else {
+ if (dma_set_mask_and_coherent(&pci->dev, DMA_BIT_MASK(64))) {
dev_dbg(sdev->dev, "DMA mask is 32 bit\n");
- dma_set_mask(&pci->dev, DMA_BIT_MASK(32));
- dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(32));
+ dma_set_mask_and_coherent(&pci->dev, DMA_BIT_MASK(32));
}
+ dma_set_max_seg_size(&pci->dev, UINT_MAX);
/* init streams */
ret = hda_dsp_stream_init(sdev);
@@ -853,17 +890,37 @@ 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;
+
+ INIT_DELAYED_WORK(&hdev->d0i3_work, hda_dsp_d0i3_work);
+ }
+
+ chip = get_chip_info(sdev->pdata);
+ if (chip && chip->hw_ip_version >= SOF_INTEL_ACE_2_0) {
+ ret = hda_sdw_startup(sdev);
+ if (ret < 0) {
+ dev_err(sdev->dev, "could not startup SoundWire links\n");
+ goto disable_pp_cap;
+ }
+ }
- /* set default mailbox offset for FW ready message */
- sdev->dsp_box.offset = HDA_DSP_MBOX_UPLINK_OFFSET;
+ init_waitqueue_head(&hdev->waitq);
- INIT_DELAYED_WORK(&hdev->d0i3_work, hda_dsp_d0i3_work);
+ hdev->nhlt = intel_nhlt_init(sdev->dev);
return 0;
+disable_pp_cap:
+ if (!sdev->dspless_mode_selected) {
+ hda_dsp_ctrl_ppcap_int_enable(sdev, false);
+ hda_dsp_ctrl_ppcap_enable(sdev, false);
+ }
free_ipc_irq:
free_irq(sdev->ipc_irq, sdev);
free_irq_vector:
@@ -872,81 +929,104 @@ 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:
- iounmap(bus->remap_addr);
- hda_codec_i915_exit(sdev);
-err:
+ platform_device_unregister(hdev->dmic_dev);
+
return ret;
}
+EXPORT_SYMBOL_NS(hda_dsp_probe, "SND_SOC_SOF_INTEL_HDA_GENERIC");
-int hda_dsp_remove(struct snd_sof_dev *sdev)
+void hda_dsp_remove(struct snd_sof_dev *sdev)
{
struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
- struct hdac_bus *bus = sof_to_bus(sdev);
- struct pci_dev *pci = to_pci_dev(sdev->dev);
const struct sof_intel_dsp_desc *chip = hda->desc;
+ struct pci_dev *pci = to_pci_dev(sdev->dev);
+ struct nhlt_acpi_table *nhlt = hda->nhlt;
- /* cancel any attempt for DSP D0I3 */
- cancel_delayed_work_sync(&hda->d0i3_work);
+ if (nhlt)
+ intel_nhlt_free(nhlt);
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
- /* codec removal, invoke bus_device_remove */
- snd_hdac_ext_bus_device_remove(bus);
-#endif
+ if (!sdev->dspless_mode_selected)
+ /* cancel any attempt for DSP D0I3 */
+ cancel_delayed_work_sync(&hda->d0i3_work);
+
+ hda_codec_device_remove(sdev);
hda_sdw_exit(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 */
+ hda_dsp_ctrl_ppcap_int_enable(sdev, false);
+ }
/* 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);
- /* disable cores */
- if (chip)
- hda_dsp_core_reset_power_down(sdev, chip->cores_mask);
+ if (sdev->dspless_mode_selected)
+ goto skip_disable_dsp;
+
+ /* Cancel the microphone privacy work if mic privacy is active */
+ if (hda->mic_privacy.active)
+ cancel_work_sync(&hda->mic_privacy.work);
+
+ /* no need to check for error as the DSP will be disabled anyway */
+ if (chip && chip->power_down_dsp)
+ chip->power_down_dsp(sdev);
/* disable DSP */
- snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL,
- SOF_HDA_PPCTL_GPROCEN, 0);
+ hda_dsp_ctrl_ppcap_enable(sdev, false);
+
+ /* Free the persistent DMA buffers used for base firmware download */
+ if (hda->cl_dmab.area)
+ snd_dma_free_pages(&hda->cl_dmab);
+ if (hda->iccmax_dmab.area)
+ snd_dma_free_pages(&hda->iccmax_dmab);
+skip_disable_dsp:
free_irq(sdev->ipc_irq, sdev);
if (sdev->msi_enabled)
pci_free_irq_vectors(pci);
hda_dsp_stream_free(sdev);
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
- snd_hdac_link_free_all(bus);
-#endif
- iounmap(sdev->bar[HDA_DSP_BAR]);
- iounmap(bus->remap_addr);
+ hda_bus_ml_free(sof_to_bus(sdev));
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
- snd_hdac_ext_bus_exit(bus);
-#endif
+ if (!sdev->dspless_mode_selected)
+ iounmap(sdev->bar[HDA_DSP_BAR]);
+}
+EXPORT_SYMBOL_NS(hda_dsp_remove, "SND_SOC_SOF_INTEL_HDA_GENERIC");
+
+void hda_dsp_remove_late(struct snd_sof_dev *sdev)
+{
+ iounmap(sof_to_bus(sdev)->remap_addr);
+ sof_hda_bus_exit(sdev);
hda_codec_i915_exit(sdev);
+}
- return 0;
+int hda_power_down_dsp(struct snd_sof_dev *sdev)
+{
+ struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
+ const struct sof_intel_dsp_desc *chip = hda->desc;
+
+ return hda_dsp_core_reset_power_down(sdev, chip->host_managed_cores_mask);
}
+EXPORT_SYMBOL_NS(hda_power_down_dsp, "SND_SOC_SOF_INTEL_HDA_GENERIC");
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
-static int hda_generic_machine_select(struct snd_sof_dev *sdev)
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC)
+static void hda_generic_machine_select(struct snd_sof_dev *sdev,
+ struct snd_soc_acpi_mach **mach)
{
struct hdac_bus *bus = sof_to_bus(sdev);
struct snd_soc_acpi_mach_params *mach_params;
struct snd_soc_acpi_mach *hda_mach;
struct snd_sof_pdata *pdata = sdev->pdata;
const char *tplg_filename;
- const char *idisp_str;
- const char *dmic_str;
- int dmic_num = 0;
int codec_num = 0;
int i;
@@ -969,127 +1049,88 @@ static int hda_generic_machine_select(struct snd_sof_dev *sdev)
* - one HDMI codec, and/or
* - one external HDAudio codec
*/
- if (!pdata->machine && codec_num <= 2) {
- hda_mach = snd_soc_acpi_intel_hda_machines;
+ if (!*mach && codec_num <= 2) {
+ bool tplg_fixup = false;
- /* topology: use the info from hda_machines */
- pdata->tplg_filename =
- hda_mach->sof_tplg_filename;
+ /*
+ * make a local copy of the match array since we might
+ * be modifying it
+ */
+ hda_mach = devm_kmemdup_array(sdev->dev,
+ snd_soc_acpi_intel_hda_machines,
+ 2, /* we have one entry + sentinel in the array */
+ sizeof(snd_soc_acpi_intel_hda_machines[0]),
+ GFP_KERNEL);
+ if (!hda_mach) {
+ dev_err(bus->dev,
+ "%s: failed to duplicate the HDA match table\n",
+ __func__);
+ return;
+ }
dev_info(bus->dev, "using HDA machine driver %s now\n",
hda_mach->drv_name);
- if (codec_num == 1 && HDA_IDISP_CODEC(bus->codec_mask))
- idisp_str = "-idisp";
- else
- idisp_str = "";
-
- /* first check NHLT for DMICs */
- dmic_num = check_nhlt_dmic(sdev);
-
- /* allow for module parameter override */
- if (hda_dmic_num != -1)
- dmic_num = hda_dmic_num;
-
- switch (dmic_num) {
- case 2:
- dmic_str = "-2ch";
- break;
- case 4:
- dmic_str = "-4ch";
- break;
- default:
- dmic_num = 0;
- dmic_str = "";
- break;
+ /*
+ * topology: use the info from hda_machines since tplg file name
+ * is not overwritten
+ */
+ if (!pdata->tplg_filename)
+ tplg_fixup = true;
+
+ if (tplg_fixup &&
+ codec_num == 1 && HDA_IDISP_CODEC(bus->codec_mask)) {
+ tplg_filename = devm_kasprintf(sdev->dev, GFP_KERNEL,
+ "%s-idisp",
+ hda_mach->sof_tplg_filename);
+ if (!tplg_filename)
+ return;
+
+ hda_mach->sof_tplg_filename = tplg_filename;
}
- tplg_filename = pdata->tplg_filename;
- tplg_filename = fixup_tplg_name(sdev, tplg_filename,
- idisp_str, dmic_str);
- if (!tplg_filename)
- return -EINVAL;
+ if (codec_num == 2 ||
+ (codec_num == 1 && !HDA_IDISP_CODEC(bus->codec_mask))) {
+ /*
+ * Prevent SoundWire links from starting when an external
+ * HDaudio codec is used
+ */
+ hda_mach->mach_params.link_mask = 0;
+ } else {
+ /*
+ * Allow SoundWire links to start when no external HDaudio codec
+ * was detected. This will not create a SoundWire card but
+ * will help detect if any SoundWire codec reports as ATTACHED.
+ */
+ struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata;
- dev_info(bus->dev,
- "DMICs detected in NHLT tables: %d\n",
- dmic_num);
+ hda_mach->mach_params.link_mask = hdev->info.link_mask;
+ }
- pdata->machine = hda_mach;
- pdata->tplg_filename = tplg_filename;
+ *mach = hda_mach;
}
}
/* used by hda machine driver to create dai links */
- if (pdata->machine) {
- mach_params = (struct snd_soc_acpi_mach_params *)
- &pdata->machine->mach_params;
+ if (*mach) {
+ mach_params = &(*mach)->mach_params;
mach_params->codec_mask = bus->codec_mask;
- mach_params->common_hdmi_codec_drv = hda_codec_use_common_hdmi;
- mach_params->dmic_num = dmic_num;
}
-
- return 0;
}
#else
-static int hda_generic_machine_select(struct snd_sof_dev *sdev)
+static void hda_generic_machine_select(struct snd_sof_dev *sdev,
+ struct snd_soc_acpi_mach **mach)
{
- return 0;
}
#endif
#if IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE)
-/* Check if all Slaves defined on the link can be found */
-static bool link_slaves_found(struct snd_sof_dev *sdev,
- const struct snd_soc_acpi_link_adr *link,
- struct sdw_intel_ctx *sdw)
-{
- struct hdac_bus *bus = sof_to_bus(sdev);
- struct sdw_intel_slave_id *ids = sdw->ids;
- int num_slaves = sdw->num_slaves;
- unsigned int part_id, link_id, unique_id, mfg_id;
- int i, j;
-
- for (i = 0; i < link->num_adr; i++) {
- u64 adr = link->adr_d[i].adr;
-
- mfg_id = SDW_MFG_ID(adr);
- part_id = SDW_PART_ID(adr);
- link_id = SDW_DISCO_LINK_ID(adr);
- for (j = 0; j < num_slaves; j++) {
- if (ids[j].link_id != link_id ||
- ids[j].id.part_id != part_id ||
- ids[j].id.mfg_id != mfg_id)
- continue;
- /*
- * we have to check unique id
- * if there is more than one
- * Slave on the link
- */
- unique_id = SDW_UNIQUE_ID(adr);
- if (link->num_adr == 1 ||
- ids[j].id.unique_id == SDW_IGNORED_UNIQUE_ID ||
- ids[j].id.unique_id == unique_id) {
- dev_dbg(bus->dev,
- "found %x at link %d\n",
- part_id, link_id);
- break;
- }
- }
- if (j == num_slaves) {
- dev_dbg(bus->dev,
- "Slave %x not found\n",
- part_id);
- return false;
- }
- }
- return true;
-}
-static int hda_sdw_machine_select(struct snd_sof_dev *sdev)
+static struct snd_soc_acpi_mach *hda_sdw_machine_select(struct snd_sof_dev *sdev)
{
struct snd_sof_pdata *pdata = sdev->pdata;
const struct snd_soc_acpi_link_adr *link;
- struct hdac_bus *bus = sof_to_bus(sdev);
+ struct sdw_peripherals *peripherals;
struct snd_soc_acpi_mach *mach;
struct sof_intel_hda_dev *hdev;
u32 link_mask;
@@ -1098,112 +1139,405 @@ static int hda_sdw_machine_select(struct snd_sof_dev *sdev)
hdev = pdata->hw_pdata;
link_mask = hdev->info.link_mask;
+ if (!link_mask) {
+ dev_info(sdev->dev, "SoundWire links not enabled\n");
+ return NULL;
+ }
+
+ if (!hdev->sdw) {
+ dev_dbg(sdev->dev, "SoundWire context not allocated\n");
+ return NULL;
+ }
+
+ if (!hdev->sdw->peripherals || !hdev->sdw->peripherals->num_peripherals) {
+ dev_warn(sdev->dev, "No SoundWire peripheral detected in ACPI tables\n");
+ return NULL;
+ }
+
/*
* Select SoundWire machine driver if needed using the
* alternate tables. This case deals with SoundWire-only
* machines, for mixed cases with I2C/I2S the detection relies
* on the HID list.
*/
- if (link_mask && !pdata->machine) {
- for (mach = pdata->desc->alt_machines;
- mach && mach->link_mask; mach++) {
- /*
- * On some platforms such as Up Extreme all links
- * are enabled but only one link can be used by
- * external codec. Instead of exact match of two masks,
- * first check whether link_mask of mach is subset of
- * link_mask supported by hw and then go on searching
- * link_adr
- */
- if (~link_mask & mach->link_mask)
- continue;
+ for (mach = pdata->desc->alt_machines;
+ mach && mach->link_mask; mach++) {
+ /*
+ * On some platforms such as Up Extreme all links
+ * are enabled but only one link can be used by
+ * external codec. Instead of exact match of two masks,
+ * first check whether link_mask of mach is subset of
+ * link_mask supported by hw and then go on searching
+ * link_adr
+ */
+ if (~link_mask & mach->link_mask)
+ continue;
- /* No need to match adr if there is no links defined */
- if (!mach->links)
- break;
+ /* No need to match adr if there is no links defined */
+ if (!mach->links)
+ break;
- link = mach->links;
- for (i = 0; i < hdev->info.count && link->num_adr;
- i++, link++) {
- /*
- * Try next machine if any expected Slaves
- * are not found on this link.
- */
- if (!link_slaves_found(sdev, link, hdev->sdw))
- break;
- }
- /* Found if all Slaves are checked */
- if (i == hdev->info.count || !link->num_adr)
+ link = mach->links;
+ for (i = 0; i < hdev->info.count && link->num_adr;
+ i++, link++) {
+ /*
+ * Try next machine if any expected Slaves
+ * are not found on this link.
+ */
+ if (!snd_soc_acpi_sdw_link_slaves_found(sdev->dev, link,
+ hdev->sdw->peripherals))
break;
}
- if (mach && mach->link_mask) {
- dev_dbg(bus->dev,
- "SoundWire machine driver %s topology %s\n",
- mach->drv_name,
- mach->sof_tplg_filename);
- pdata->machine = mach;
- mach->mach_params.links = mach->links;
- mach->mach_params.link_mask = mach->link_mask;
- mach->mach_params.platform = dev_name(sdev->dev);
- pdata->fw_filename = mach->sof_fw_filename;
- pdata->tplg_filename = mach->sof_tplg_filename;
- } else {
- dev_info(sdev->dev,
- "No SoundWire machine driver found\n");
- }
+ /* Found if all Slaves are checked */
+ if (i == hdev->info.count || !link->num_adr)
+ if (!mach->machine_check || mach->machine_check(hdev->sdw))
+ break;
}
+ if (mach && mach->link_mask) {
+ mach->mach_params.links = mach->links;
+ mach->mach_params.link_mask = mach->link_mask;
+ mach->mach_params.platform = dev_name(sdev->dev);
- return 0;
+ return mach;
+ }
+
+ dev_info(sdev->dev, "No SoundWire machine driver found for the ACPI-reported configuration:\n");
+ peripherals = hdev->sdw->peripherals;
+ for (i = 0; i < peripherals->num_peripherals; i++)
+ dev_info(sdev->dev, "link %d mfg_id 0x%04x part_id 0x%04x version %#x\n",
+ peripherals->array[i]->bus->link_id,
+ peripherals->array[i]->id.mfg_id,
+ peripherals->array[i]->id.part_id,
+ peripherals->array[i]->id.sdw_version);
+
+ return NULL;
}
#else
-static int hda_sdw_machine_select(struct snd_sof_dev *sdev)
+static struct snd_soc_acpi_mach *hda_sdw_machine_select(struct snd_sof_dev *sdev)
{
- return 0;
+ return NULL;
}
#endif
-void hda_set_mach_params(const struct snd_soc_acpi_mach *mach,
- struct device *dev)
+void hda_set_mach_params(struct snd_soc_acpi_mach *mach,
+ struct snd_sof_dev *sdev)
{
+ struct snd_sof_pdata *pdata = sdev->pdata;
+ const struct sof_dev_desc *desc = pdata->desc;
struct snd_soc_acpi_mach_params *mach_params;
- mach_params = (struct snd_soc_acpi_mach_params *)&mach->mach_params;
- mach_params->platform = dev_name(dev);
+ mach_params = &mach->mach_params;
+ mach_params->platform = dev_name(sdev->dev);
+ if (IS_ENABLED(CONFIG_SND_SOC_SOF_NOCODEC_DEBUG_SUPPORT) &&
+ sof_debug_check_flag(SOF_DBG_FORCE_NOCODEC))
+ mach_params->num_dai_drivers = SOF_SKL_NUM_DAIS_NOCODEC;
+ else
+ mach_params->num_dai_drivers = desc->ops->num_drv;
+ mach_params->dai_drivers = desc->ops->drv;
}
-void hda_machine_select(struct snd_sof_dev *sdev)
+static int check_tplg_quirk_mask(struct snd_soc_acpi_mach *mach)
{
+ u32 dmic_ssp_quirk;
+ u32 codec_amp_name_quirk;
+
+ /*
+ * In current implementation dmic and ssp quirks are designed for es8336
+ * machine driver and could not be mixed with codec name and amp name
+ * quirks.
+ */
+ dmic_ssp_quirk = mach->tplg_quirk_mask &
+ (SND_SOC_ACPI_TPLG_INTEL_DMIC_NUMBER | SND_SOC_ACPI_TPLG_INTEL_SSP_NUMBER);
+ codec_amp_name_quirk = mach->tplg_quirk_mask &
+ (SND_SOC_ACPI_TPLG_INTEL_AMP_NAME | SND_SOC_ACPI_TPLG_INTEL_CODEC_NAME);
+
+ if (dmic_ssp_quirk && codec_amp_name_quirk)
+ return -EINVAL;
+
+ return 0;
+}
+
+static char *remove_file_ext(const char *tplg_filename)
+{
+ char *filename, *tmp;
+
+ filename = kstrdup(tplg_filename, GFP_KERNEL);
+ if (!filename)
+ return NULL;
+
+ /* remove file extension if exist */
+ tmp = filename;
+ return strsep(&tmp, ".");
+}
+
+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 hdac_bus *bus = sof_to_bus(sdev);
+ struct snd_soc_acpi_mach *mach = NULL;
+ enum snd_soc_acpi_intel_codec codec_type, amp_type;
+ const char *tplg_filename;
+ const char *tplg_suffix;
+ bool amp_name_valid;
+ bool i2s_mach_found = false;
+ bool sdw_mach_found = false;
+
+ /* 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)
+ i2s_mach_found = true;
+ }
- mach = snd_soc_acpi_find_machine(desc->machines);
+ /*
+ * If I2S fails and no external HDaudio codec is detected,
+ * try SoundWire if it is supported
+ */
+ if (!mach && !HDA_EXT_CODEC(bus->codec_mask) &&
+ (interface_mask & BIT(SOF_DAI_INTEL_ALH))) {
+ mach = hda_sdw_machine_select(sdev);
+ if (mach)
+ sdw_mach_found = true;
+ }
+
+ /*
+ * Choose HDA generic machine driver if mach is NULL.
+ * Otherwise, set certain mach params.
+ */
+ hda_generic_machine_select(sdev, &mach);
+ if (!mach) {
+ dev_warn(sdev->dev, "warning: No matching ASoC machine driver found\n");
+ return NULL;
+ }
+
+ /* report BT offload link mask to machine driver */
+ mach->mach_params.bt_link_mask = check_nhlt_ssp_mask(sdev, NHLT_DEVICE_BT);
+
+ dev_info(sdev->dev, "BT link detected in NHLT tables: %#x\n",
+ mach->mach_params.bt_link_mask);
+
+ /* allow for module parameter override */
+ if (bt_link_mask_override) {
+ dev_dbg(sdev->dev, "overriding BT link detected in NHLT tables %#x by kernel param %#x\n",
+ mach->mach_params.bt_link_mask, bt_link_mask_override);
+ mach->mach_params.bt_link_mask = bt_link_mask_override;
+ }
+
+ if (hweight_long(mach->mach_params.bt_link_mask) > 1) {
+ dev_warn(sdev->dev, "invalid BT link mask %#x found, reset the mask\n",
+ mach->mach_params.bt_link_mask);
+ mach->mach_params.bt_link_mask = 0;
+ }
+
+ /*
+ * Fixup tplg file name by appending dmic num, ssp num, codec/amplifier
+ * name string if quirk flag is set.
+ */
if (mach) {
- sof_pdata->tplg_filename = mach->sof_tplg_filename;
- sof_pdata->machine = mach;
+ bool tplg_fixup = false;
+ bool dmic_fixup = false;
+
+ /*
+ * If tplg file name is overridden, use it instead of
+ * the one set in mach table
+ */
+ if (!sof_pdata->tplg_filename) {
+ /* remove file extension if it exists */
+ tplg_filename = remove_file_ext(mach->sof_tplg_filename);
+ if (!tplg_filename)
+ return NULL;
+
+ sof_pdata->tplg_filename = tplg_filename;
+ tplg_fixup = true;
+ }
+
+ /*
+ * Checking quirk mask integrity; some quirk flags could not be
+ * set concurrently.
+ */
+ if (tplg_fixup &&
+ check_tplg_quirk_mask(mach)) {
+ dev_err(sdev->dev, "Invalid tplg quirk mask 0x%x\n",
+ mach->tplg_quirk_mask);
+ return NULL;
+ }
+
+ /* report to machine driver if any DMICs are found */
+ mach->mach_params.dmic_num = check_dmic_num(sdev);
+
+ if (sdw_mach_found || mach->tplg_quirk_mask & SND_SOC_ACPI_TPLG_INTEL_DMIC_NUMBER)
+ dmic_fixup = true;
+
+ if (tplg_fixup &&
+ dmic_fixup &&
+ mach->mach_params.dmic_num) {
+ tplg_filename = devm_kasprintf(sdev->dev, GFP_KERNEL,
+ "%s%s%d%s",
+ sof_pdata->tplg_filename,
+ i2s_mach_found ? "-dmic" : "-",
+ mach->mach_params.dmic_num,
+ "ch");
+ if (!tplg_filename)
+ return NULL;
+
+ sof_pdata->tplg_filename = tplg_filename;
+ }
if (mach->link_mask) {
mach->mach_params.links = mach->links;
mach->mach_params.link_mask = mach->link_mask;
}
+
+ /* report SSP link mask to machine driver */
+ mach->mach_params.i2s_link_mask = check_nhlt_ssp_mask(sdev, NHLT_DEVICE_I2S);
+
+ if (tplg_fixup &&
+ mach->tplg_quirk_mask & SND_SOC_ACPI_TPLG_INTEL_SSP_NUMBER &&
+ mach->mach_params.i2s_link_mask) {
+ const struct sof_intel_dsp_desc *chip = get_chip_info(sdev->pdata);
+ int ssp_num;
+ int mclk_mask;
+
+ if (hweight_long(mach->mach_params.i2s_link_mask) > 1 &&
+ !(mach->tplg_quirk_mask & SND_SOC_ACPI_TPLG_INTEL_SSP_MSB))
+ dev_warn(sdev->dev, "More than one SSP exposed by NHLT, choosing MSB\n");
+
+ /* fls returns 1-based results, SSPs indices are 0-based */
+ ssp_num = fls(mach->mach_params.i2s_link_mask) - 1;
+
+ if (ssp_num >= chip->ssp_count) {
+ dev_err(sdev->dev, "Invalid SSP %d, max on this platform is %d\n",
+ ssp_num, chip->ssp_count);
+ return NULL;
+ }
+
+ tplg_filename = devm_kasprintf(sdev->dev, GFP_KERNEL,
+ "%s%s%d",
+ sof_pdata->tplg_filename,
+ "-ssp",
+ ssp_num);
+ if (!tplg_filename)
+ return NULL;
+
+ sof_pdata->tplg_filename = tplg_filename;
+
+ mclk_mask = check_nhlt_ssp_mclk_mask(sdev, ssp_num);
+
+ if (mclk_mask < 0) {
+ dev_err(sdev->dev, "Invalid MCLK configuration\n");
+ return NULL;
+ }
+
+ dev_dbg(sdev->dev, "MCLK mask %#x found in NHLT\n", mclk_mask);
+
+ if (mclk_mask) {
+ dev_info(sdev->dev, "Overriding topology with MCLK mask %#x from NHLT\n", mclk_mask);
+ sdev->mclk_id_override = true;
+ sdev->mclk_id_quirk = (mclk_mask & BIT(0)) ? 0 : 1;
+ }
+ }
+
+ amp_type = snd_soc_acpi_intel_detect_amp_type(sdev->dev);
+ codec_type = snd_soc_acpi_intel_detect_codec_type(sdev->dev);
+ amp_name_valid = amp_type != CODEC_NONE && amp_type != codec_type;
+
+ if (tplg_fixup && amp_name_valid &&
+ mach->tplg_quirk_mask & SND_SOC_ACPI_TPLG_INTEL_AMP_NAME) {
+ tplg_suffix = snd_soc_acpi_intel_get_amp_tplg_suffix(amp_type);
+ if (!tplg_suffix) {
+ dev_err(sdev->dev, "no tplg suffix found, amp %d\n",
+ amp_type);
+ return NULL;
+ }
+
+ tplg_filename = devm_kasprintf(sdev->dev, GFP_KERNEL,
+ "%s-%s",
+ sof_pdata->tplg_filename,
+ tplg_suffix);
+ if (!tplg_filename)
+ return NULL;
+
+ sof_pdata->tplg_filename = tplg_filename;
+ }
+
+
+ if (tplg_fixup &&
+ mach->tplg_quirk_mask & SND_SOC_ACPI_TPLG_INTEL_CODEC_NAME &&
+ codec_type != CODEC_NONE) {
+ tplg_suffix = snd_soc_acpi_intel_get_codec_tplg_suffix(codec_type);
+ if (!tplg_suffix) {
+ dev_err(sdev->dev, "no tplg suffix found, codec %d\n",
+ codec_type);
+ return NULL;
+ }
+
+ tplg_filename = devm_kasprintf(sdev->dev, GFP_KERNEL,
+ "%s-%s",
+ sof_pdata->tplg_filename,
+ tplg_suffix);
+ if (!tplg_filename)
+ return NULL;
+
+ sof_pdata->tplg_filename = tplg_filename;
+ }
+
+ if (tplg_fixup) {
+ tplg_filename = devm_kasprintf(sdev->dev, GFP_KERNEL,
+ "%s%s",
+ sof_pdata->tplg_filename,
+ ".tplg");
+ if (!tplg_filename)
+ return NULL;
+
+ sof_pdata->tplg_filename = tplg_filename;
+ }
+
+ /* check if mclk_id should be modified from topology defaults */
+ if (mclk_id_override >= 0) {
+ dev_info(sdev->dev, "Overriding topology with MCLK %d from kernel_parameter\n", mclk_id_override);
+ sdev->mclk_id_override = true;
+ sdev->mclk_id_quirk = mclk_id_override;
+ }
}
- /*
- * If I2S fails, try SoundWire
- */
- hda_sdw_machine_select(sdev);
+ return mach;
+}
- /*
- * Choose HDA generic machine driver if mach is NULL.
- * Otherwise, set certain mach params.
- */
- hda_generic_machine_select(sdev);
+int hda_pci_intel_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
+{
+ int ret;
- if (!sof_pdata->machine)
- dev_warn(sdev->dev, "warning: No matching ASoC machine driver found\n");
+ ret = snd_intel_dsp_driver_probe(pci);
+ if (ret != SND_INTEL_DSP_DRIVER_ANY && ret != SND_INTEL_DSP_DRIVER_SOF) {
+ dev_dbg(&pci->dev, "SOF PCI driver not selected, aborting probe\n");
+ return -ENODEV;
+ }
+
+ return sof_pci_probe(pci, pci_id);
+}
+EXPORT_SYMBOL_NS(hda_pci_intel_probe, "SND_SOC_SOF_INTEL_HDA_GENERIC");
+
+int hda_register_clients(struct snd_sof_dev *sdev)
+{
+ return hda_probes_register(sdev);
+}
+
+void hda_unregister_clients(struct snd_sof_dev *sdev)
+{
+ hda_probes_unregister(sdev);
}
MODULE_LICENSE("Dual BSD/GPL");
-MODULE_IMPORT_NS(SND_SOC_SOF_HDA_AUDIO_CODEC);
-MODULE_IMPORT_NS(SND_SOC_SOF_HDA_AUDIO_CODEC_I915);
-MODULE_IMPORT_NS(SND_SOC_SOF_XTENSA);
+MODULE_DESCRIPTION("SOF support for HDaudio platforms");
+MODULE_IMPORT_NS("SND_SOC_SOF_PCI_DEV");
+MODULE_IMPORT_NS("SND_SOC_SOF_HDA_AUDIO_CODEC");
+MODULE_IMPORT_NS("SND_SOC_SOF_HDA_AUDIO_CODEC_I915");
+MODULE_IMPORT_NS("SND_SOC_SOF_XTENSA");
+MODULE_IMPORT_NS("SND_INTEL_SOUNDWIRE_ACPI");
+MODULE_IMPORT_NS("SOUNDWIRE_INTEL_INIT");
+MODULE_IMPORT_NS("SOUNDWIRE_INTEL");
+MODULE_IMPORT_NS("SND_SOC_SOF_HDA_MLINK");
+MODULE_IMPORT_NS("SND_SOC_SOF_INTEL_HDA_COMMON");
+MODULE_IMPORT_NS("SND_SOC_ACPI_INTEL_MATCH");
diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h
index fe452f0d0ec7..e14f82c0831f 100644
--- a/sound/soc/sof/intel/hda.h
+++ b/sound/soc/sof/intel/hda.h
@@ -3,7 +3,7 @@
* 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) 2017 Intel Corporation. All rights reserved.
+ * Copyright(c) 2017 Intel Corporation
*
* Author: Liam Girdwood <liam.r.girdwood@linux.intel.com>
*/
@@ -11,11 +11,14 @@
#ifndef __SOF_INTEL_HDA_H
#define __SOF_INTEL_HDA_H
+#include <linux/completion.h>
#include <linux/soundwire/sdw.h>
#include <linux/soundwire/sdw_intel.h>
#include <sound/compress_driver.h>
#include <sound/hda_codec.h>
#include <sound/hdaudio_ext.h>
+#include "../sof-client-probes.h"
+#include "../sof-audio.h"
#include "shim.h"
/* PCI registers */
@@ -120,19 +123,22 @@
#define SOF_HDA_ADSP_DPLBASE_ENABLE 0x01
/* Stream Registers */
-#define SOF_HDA_ADSP_REG_CL_SD_CTL 0x00
-#define SOF_HDA_ADSP_REG_CL_SD_STS 0x03
-#define SOF_HDA_ADSP_REG_CL_SD_LPIB 0x04
-#define SOF_HDA_ADSP_REG_CL_SD_CBL 0x08
-#define SOF_HDA_ADSP_REG_CL_SD_LVI 0x0C
-#define SOF_HDA_ADSP_REG_CL_SD_FIFOW 0x0E
-#define SOF_HDA_ADSP_REG_CL_SD_FIFOSIZE 0x10
-#define SOF_HDA_ADSP_REG_CL_SD_FORMAT 0x12
-#define SOF_HDA_ADSP_REG_CL_SD_FIFOL 0x14
-#define SOF_HDA_ADSP_REG_CL_SD_BDLPL 0x18
-#define SOF_HDA_ADSP_REG_CL_SD_BDLPU 0x1C
+#define SOF_HDA_ADSP_REG_SD_CTL 0x00
+#define SOF_HDA_ADSP_REG_SD_STS 0x03
+#define SOF_HDA_ADSP_REG_SD_LPIB 0x04
+#define SOF_HDA_ADSP_REG_SD_CBL 0x08
+#define SOF_HDA_ADSP_REG_SD_LVI 0x0C
+#define SOF_HDA_ADSP_REG_SD_FIFOW 0x0E
+#define SOF_HDA_ADSP_REG_SD_FIFOSIZE 0x10
+#define SOF_HDA_ADSP_REG_SD_FORMAT 0x12
+#define SOF_HDA_ADSP_REG_SD_FIFOL 0x14
+#define SOF_HDA_ADSP_REG_SD_BDLPL 0x18
+#define SOF_HDA_ADSP_REG_SD_BDLPU 0x1C
#define SOF_HDA_ADSP_SD_ENTRY_SIZE 0x20
+/* SDxFIFOS FIFOS */
+#define SOF_HDA_SD_FIFOSIZE_FIFOS_MASK GENMASK(15, 0)
+
/* CL: Software Position Based FIFO Capability Registers */
#define SOF_DSP_REG_CL_SPBFIFO \
(SOF_HDA_ADSP_LOADER_BASE + 0x20)
@@ -185,13 +191,71 @@
#define HDA_DSP_STACK_DUMP_SIZE 32
+/* ROM/FW status register */
+#define FSR_STATE_MASK GENMASK(23, 0)
+#define FSR_WAIT_STATE_MASK GENMASK(27, 24)
+#define FSR_MODULE_MASK GENMASK(30, 28)
+#define FSR_HALTED BIT(31)
+#define FSR_TO_STATE_CODE(x) ((x) & FSR_STATE_MASK)
+#define FSR_TO_WAIT_STATE_CODE(x) (((x) & FSR_WAIT_STATE_MASK) >> 24)
+#define FSR_TO_MODULE_CODE(x) (((x) & FSR_MODULE_MASK) >> 28)
+
+/* Wait states */
+#define FSR_WAIT_FOR_IPC_BUSY 0x1
+#define FSR_WAIT_FOR_IPC_DONE 0x2
+#define FSR_WAIT_FOR_CACHE_INVALIDATION 0x3
+#define FSR_WAIT_FOR_LP_SRAM_OFF 0x4
+#define FSR_WAIT_FOR_DMA_BUFFER_FULL 0x5
+#define FSR_WAIT_FOR_CSE_CSR 0x6
+
+/* Module codes */
+#define FSR_MOD_ROM 0x0
+#define FSR_MOD_ROM_BYP 0x1
+#define FSR_MOD_BASE_FW 0x2
+#define FSR_MOD_LP_BOOT 0x3
+#define FSR_MOD_BRNGUP 0x4
+#define FSR_MOD_ROM_EXT 0x5
+
+/* State codes (module dependent) */
+/* Module independent states */
+#define FSR_STATE_INIT 0x0
+#define FSR_STATE_INIT_DONE 0x1
+#define FSR_STATE_FW_ENTERED 0x5
+
+/* ROM states */
+#define FSR_STATE_ROM_INIT FSR_STATE_INIT
+#define FSR_STATE_ROM_INIT_DONE FSR_STATE_INIT_DONE
+#define FSR_STATE_ROM_CSE_MANIFEST_LOADED 0x2
+#define FSR_STATE_ROM_FW_MANIFEST_LOADED 0x3
+#define FSR_STATE_ROM_FW_FW_LOADED 0x4
+#define FSR_STATE_ROM_FW_ENTERED FSR_STATE_FW_ENTERED
+#define FSR_STATE_ROM_VERIFY_FEATURE_MASK 0x6
+#define FSR_STATE_ROM_GET_LOAD_OFFSET 0x7
+#define FSR_STATE_ROM_FETCH_ROM_EXT 0x8
+#define FSR_STATE_ROM_FETCH_ROM_EXT_DONE 0x9
+#define FSR_STATE_ROM_BASEFW_ENTERED 0xf /* SKL */
+
+/* (ROM) CSE states */
+#define FSR_STATE_ROM_CSE_IMR_REQUEST 0x10
+#define FSR_STATE_ROM_CSE_IMR_GRANTED 0x11
+#define FSR_STATE_ROM_CSE_VALIDATE_IMAGE_REQUEST 0x12
+#define FSR_STATE_ROM_CSE_IMAGE_VALIDATED 0x13
+
+#define FSR_STATE_ROM_CSE_IPC_IFACE_INIT 0x20
+#define FSR_STATE_ROM_CSE_IPC_RESET_PHASE_1 0x21
+#define FSR_STATE_ROM_CSE_IPC_OPERATIONAL_ENTRY 0x22
+#define FSR_STATE_ROM_CSE_IPC_OPERATIONAL 0x23
+#define FSR_STATE_ROM_CSE_IPC_DOWN 0x24
+
+/* BRINGUP (or BRNGUP) states */
+#define FSR_STATE_BRINGUP_INIT FSR_STATE_INIT
+#define FSR_STATE_BRINGUP_INIT_DONE FSR_STATE_INIT_DONE
+#define FSR_STATE_BRINGUP_HPSRAM_LOAD 0x2
+#define FSR_STATE_BRINGUP_UNPACK_START 0X3
+#define FSR_STATE_BRINGUP_IMR_RESTORE 0x4
+#define FSR_STATE_BRINGUP_FW_ENTERED FSR_STATE_FW_ENTERED
+
/* ROM status/error values */
-#define HDA_DSP_ROM_STS_MASK GENMASK(23, 0)
-#define HDA_DSP_ROM_INIT 0x1
-#define HDA_DSP_ROM_FW_MANIFEST_LOADED 0x3
-#define HDA_DSP_ROM_FW_FW_LOADED 0x4
-#define HDA_DSP_ROM_FW_ENTERED 0x5
-#define HDA_DSP_ROM_RFW_START 0xf
#define HDA_DSP_ROM_CSE_ERROR 40
#define HDA_DSP_ROM_CSE_WRONG_RESPONSE 41
#define HDA_DSP_ROM_IMR_TO_SMALL 42
@@ -208,7 +272,9 @@
#define HDA_DSP_ROM_USER_EXCEPTION 0xBEEF0000
#define HDA_DSP_ROM_UNEXPECTED_RESET 0xDECAF000
#define HDA_DSP_ROM_NULL_FW_ENTRY 0x4c4c4e55
-#define HDA_DSP_IPC_PURGE_FW 0x01004000
+
+#define HDA_DSP_ROM_IPC_CONTROL 0x01000000
+#define HDA_DSP_ROM_IPC_PURGE_FW 0x00004000
/* various timeout values */
#define HDA_DSP_PU_TIMEOUT 50
@@ -221,8 +287,8 @@
#define HDA_DSP_REG_POLL_INTERVAL_US 500 /* 0.5 msec */
#define HDA_DSP_REG_POLL_RETRY_COUNT 50
-#define HDA_DSP_ADSPIC_IPC 1
-#define HDA_DSP_ADSPIS_IPC 1
+#define HDA_DSP_ADSPIC_IPC BIT(0)
+#define HDA_DSP_ADSPIS_IPC BIT(0)
/* Intel HD Audio General DSP Registers */
#define HDA_DSP_GEN_BASE 0x0
@@ -232,8 +298,8 @@
#define HDA_DSP_REG_ADSPIC2 (HDA_DSP_GEN_BASE + 0x10)
#define HDA_DSP_REG_ADSPIS2 (HDA_DSP_GEN_BASE + 0x14)
+#define HDA_DSP_REG_ADSPIC2_SNDW BIT(5)
#define HDA_DSP_REG_ADSPIS2_SNDW BIT(5)
-#define HDA_DSP_REG_SNDW_WAKE_STS 0x2C192
/* Intel HD Audio Inter-Processor Communication Registers */
#define HDA_DSP_IPC_BASE 0x40
@@ -246,6 +312,8 @@
/* Intel Vendor Specific Registers */
#define HDA_VS_INTEL_EM2 0x1030
#define HDA_VS_INTEL_EM2_L1SEN BIT(13)
+#define HDA_VS_INTEL_LTRP 0x1048
+#define HDA_VS_INTEL_LTRP_GB_MASK 0x3F
/* HIPCI */
#define HDA_DSP_REG_HIPCI_BUSY BIT(31)
@@ -266,13 +334,14 @@
/* HIPCTE */
#define HDA_DSP_REG_HIPCTE_MSG_MASK 0x3FFFFFFF
-#define HDA_DSP_ADSPIC_CL_DMA 0x2
-#define HDA_DSP_ADSPIS_CL_DMA 0x2
+#define HDA_DSP_ADSPIC_CL_DMA BIT(1)
+#define HDA_DSP_ADSPIS_CL_DMA BIT(1)
/* Delay before scheduling D0i3 entry */
#define BXT_D0I3_DELAY 5000
#define FW_CL_STREAM_NUMBER 0x1
+#define HDA_FW_BOOT_ATTEMPTS 3
/* ADSPCS - Audio DSP Control & Status */
@@ -304,9 +373,6 @@
#define HDA_DSP_ADSPCS_CPA_SHIFT 24
#define HDA_DSP_ADSPCS_CPA_MASK(cm) ((cm) << HDA_DSP_ADSPCS_CPA_SHIFT)
-/* Mask for a given core index, c = 0.. number of supported cores - 1 */
-#define HDA_DSP_CORE_MASK(c) BIT(c)
-
/*
* Mask for a given number of cores
* nc = number of supported cores
@@ -352,19 +418,16 @@
(HDA_DSP_BDL_SIZE / sizeof(struct sof_intel_dsp_bdl))
/* Number of DAIs */
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
+#define SOF_SKL_NUM_DAIS_NOCODEC 8
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_PROBES)
-#define SOF_SKL_NUM_DAIS 16
-#else
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC)
#define SOF_SKL_NUM_DAIS 15
-#endif
-
#else
-#define SOF_SKL_NUM_DAIS 8
+#define SOF_SKL_NUM_DAIS SOF_SKL_NUM_DAIS_NOCODEC
#endif
/* Intel HD Audio SRAM Window 0*/
+#define HDA_DSP_SRAM_REG_ROM_STATUS_SKL 0x8000
#define HDA_ADSP_SRAM0_BASE_SKL 0x8000
/* Firmware status window */
@@ -382,14 +445,19 @@
#define APL_SSP_COUNT 6
#define CNL_SSP_COUNT 3
#define ICL_SSP_COUNT 6
+#define TGL_SSP_COUNT 3
+#define MTL_SSP_COUNT 3
/* SSP Registers */
#define SSP_SSC1_OFFSET 0x4
-#define SSP_SET_SCLK_SLAVE BIT(25)
-#define SSP_SET_SFRM_SLAVE BIT(24)
-#define SSP_SET_SLAVE (SSP_SET_SCLK_SLAVE | SSP_SET_SFRM_SLAVE)
+#define SSP_SET_SCLK_CONSUMER BIT(25)
+#define SSP_SET_SFRM_CONSUMER BIT(24)
+#define SSP_SET_CBP_CFP (SSP_SET_SCLK_CONSUMER | SSP_SET_SFRM_CONSUMER)
-#define HDA_IDISP_CODEC(x) ((x) & BIT(2))
+#define HDA_EXT_ADDR 0
+#define HDA_EXT_CODEC(x) ((x) & BIT(HDA_EXT_ADDR))
+#define HDA_IDISP_ADDR 2
+#define HDA_IDISP_CODEC(x) ((x) & BIT(HDA_IDISP_ADDR))
struct sof_intel_dsp_bdl {
__le32 addr_l;
@@ -403,6 +471,9 @@ struct sof_intel_dsp_bdl {
#define SOF_HDA_PLAYBACK 0
#define SOF_HDA_CAPTURE 1
+/* stream flags */
+#define SOF_HDA_STREAM_DMI_L1_COMPATIBLE 1
+
/*
* Time in ms for opportunistic D0I3 entry delay.
* This has been deliberately chosen to be long to avoid race conditions.
@@ -416,8 +487,27 @@ enum sof_hda_D0_substate {
SOF_HDA_DSP_PM_D0I3, /* low power D0 substate */
};
+struct sof_ace3_mic_privacy {
+ bool active;
+ struct work_struct work;
+};
+
/* represents DSP HDA controller frontend - i.e. host facing control */
struct sof_intel_hda_dev {
+ bool imrboot_supported;
+ bool skip_imr_boot;
+ bool booted_from_imr;
+
+ int boot_iteration;
+
+ /*
+ * DMA buffers for base firmware download. By default the buffers are
+ * allocated once and kept through the lifetime of the driver.
+ * See module parameter: persistent_cl_buffer
+ */
+ struct snd_dma_buffer cl_dmab;
+ bool cl_dmab_contains_basefw;
+ struct snd_dma_buffer iccmax_dmab;
struct hda_bus hbus;
@@ -434,7 +524,7 @@ struct sof_intel_hda_dev {
u32 stream_max;
/* PM related */
- bool l1_support_changed;/* during suspend, is L1SEN changed or not */
+ bool l1_disabled;/* is DMI link L1 disabled? */
/* DMIC device */
struct platform_device *dmic_dev;
@@ -447,6 +537,26 @@ struct sof_intel_hda_dev {
/* sdw context allocated by SoundWire driver */
struct sdw_intel_ctx *sdw;
+
+ /* FW clock config, 0:HPRO, 1:LPRO */
+ bool clk_config_lpro;
+
+ wait_queue_head_t waitq;
+ bool code_loading;
+
+ /* Intel NHLT information */
+ struct nhlt_acpi_table *nhlt;
+
+ /* work queue for mic privacy state change notification sending */
+ struct sof_ace3_mic_privacy mic_privacy;
+
+ /*
+ * Pointing to the IPC message if immediate sending was not possible
+ * because the downlink communication channel was BUSY at the time.
+ * The message will be re-tried when the channel becomes free (the ACK
+ * is received from the DSP for the previous message)
+ */
+ struct snd_sof_ipc_msg *delayed_ipc_tx_msg;
};
static inline struct hdac_bus *sof_to_bus(struct snd_sof_dev *s)
@@ -465,13 +575,15 @@ static inline struct hda_bus *sof_to_hbus(struct snd_sof_dev *s)
struct sof_intel_hda_stream {
struct snd_sof_dev *sdev;
- struct hdac_ext_stream hda_stream;
- struct sof_intel_stream stream;
+ struct hdac_ext_stream hext_stream;
+ struct sof_intel_stream sof_intel_stream;
int host_reserved; /* reserve host DMA channel */
+ u32 flags;
+ struct completion ioc;
};
#define hstream_to_sof_hda_stream(hstream) \
- container_of(hstream, struct sof_intel_hda_stream, hda_stream)
+ container_of(hstream, struct sof_intel_hda_stream, hext_stream)
#define bus_to_sof_hda(bus) \
container_of(bus, struct sof_intel_hda_dev, hbus.core)
@@ -480,41 +592,52 @@ struct sof_intel_hda_stream {
(SOF_HDA_ADSP_SD_ENTRY_SIZE * ((s)->index) \
+ SOF_HDA_ADSP_LOADER_BASE)
+#define SOF_STREAM_SD_OFFSET_CRST 0x1
+
+/*
+ * DAI support
+ */
+bool hda_is_chain_dma_supported(struct snd_sof_dev *sdev, u32 dai_type);
+
/*
* DSP Core services.
*/
+int hda_dsp_probe_early(struct snd_sof_dev *sdev);
int hda_dsp_probe(struct snd_sof_dev *sdev);
-int hda_dsp_remove(struct snd_sof_dev *sdev);
-int hda_dsp_core_reset_enter(struct snd_sof_dev *sdev,
- unsigned int core_mask);
-int hda_dsp_core_reset_leave(struct snd_sof_dev *sdev,
- unsigned int core_mask);
-int hda_dsp_core_stall_reset(struct snd_sof_dev *sdev, unsigned int core_mask);
-int hda_dsp_core_run(struct snd_sof_dev *sdev, unsigned int core_mask);
+void hda_dsp_remove(struct snd_sof_dev *sdev);
+void hda_dsp_remove_late(struct snd_sof_dev *sdev);
int hda_dsp_core_power_up(struct snd_sof_dev *sdev, unsigned int core_mask);
+int hda_dsp_core_run(struct snd_sof_dev *sdev, unsigned int core_mask);
int hda_dsp_enable_core(struct snd_sof_dev *sdev, unsigned int core_mask);
-int hda_dsp_core_power_down(struct snd_sof_dev *sdev, unsigned int core_mask);
-bool hda_dsp_core_is_enabled(struct snd_sof_dev *sdev,
- unsigned int core_mask);
int hda_dsp_core_reset_power_down(struct snd_sof_dev *sdev,
unsigned int core_mask);
+int hda_power_down_dsp(struct snd_sof_dev *sdev);
+int hda_dsp_core_get(struct snd_sof_dev *sdev, int core);
void hda_dsp_ipc_int_enable(struct snd_sof_dev *sdev);
void hda_dsp_ipc_int_disable(struct snd_sof_dev *sdev);
+bool hda_dsp_core_is_enabled(struct snd_sof_dev *sdev, unsigned int core_mask);
-int hda_dsp_set_power_state(struct snd_sof_dev *sdev,
- const struct sof_dsp_power_state *target_state);
+int hda_dsp_set_power_state_ipc3(struct snd_sof_dev *sdev,
+ const struct sof_dsp_power_state *target_state);
+int hda_dsp_set_power_state_ipc4(struct snd_sof_dev *sdev,
+ const struct sof_dsp_power_state *target_state);
int hda_dsp_suspend(struct snd_sof_dev *sdev, u32 target_state);
int hda_dsp_resume(struct snd_sof_dev *sdev);
int hda_dsp_runtime_suspend(struct snd_sof_dev *sdev);
int hda_dsp_runtime_resume(struct snd_sof_dev *sdev);
int hda_dsp_runtime_idle(struct snd_sof_dev *sdev);
+int hda_dsp_shutdown_dma_flush(struct snd_sof_dev *sdev);
+int hda_dsp_shutdown(struct snd_sof_dev *sdev);
int hda_dsp_set_hw_params_upon_resume(struct snd_sof_dev *sdev);
-void hda_dsp_dump_skl(struct snd_sof_dev *sdev, u32 flags);
void hda_dsp_dump(struct snd_sof_dev *sdev, u32 flags);
+void hda_ipc4_dsp_dump(struct snd_sof_dev *sdev, u32 flags);
void hda_ipc_dump(struct snd_sof_dev *sdev);
void hda_ipc_irq_dump(struct snd_sof_dev *sdev);
void hda_dsp_d0i3_work(struct work_struct *work);
+int hda_dsp_disable_interrupts(struct snd_sof_dev *sdev);
+bool hda_check_ipc_irq(struct snd_sof_dev *sdev);
+u32 hda_get_interface_mask(struct snd_sof_dev *sdev);
/*
* DSP PCM Operations.
@@ -528,13 +651,14 @@ int hda_dsp_pcm_close(struct snd_sof_dev *sdev,
int hda_dsp_pcm_hw_params(struct snd_sof_dev *sdev,
struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
- struct sof_ipc_stream_params *ipc_params);
+ struct snd_sof_platform_stream_params *platform_params);
int hda_dsp_stream_hw_free(struct snd_sof_dev *sdev,
struct snd_pcm_substream *substream);
int hda_dsp_pcm_trigger(struct snd_sof_dev *sdev,
struct snd_pcm_substream *substream, int cmd);
snd_pcm_uframes_t hda_dsp_pcm_pointer(struct snd_sof_dev *sdev,
struct snd_pcm_substream *substream);
+int hda_dsp_pcm_ack(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream);
/*
* DSP Stream Operations.
@@ -543,54 +667,44 @@ snd_pcm_uframes_t hda_dsp_pcm_pointer(struct snd_sof_dev *sdev,
int hda_dsp_stream_init(struct snd_sof_dev *sdev);
void hda_dsp_stream_free(struct snd_sof_dev *sdev);
int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev,
- struct hdac_ext_stream *stream,
+ struct hdac_ext_stream *hext_stream,
struct snd_dma_buffer *dmab,
struct snd_pcm_hw_params *params);
+int hda_dsp_iccmax_stream_hw_params(struct snd_sof_dev *sdev,
+ struct hdac_ext_stream *hext_stream,
+ struct snd_dma_buffer *dmab,
+ struct snd_pcm_hw_params *params);
int hda_dsp_stream_trigger(struct snd_sof_dev *sdev,
- struct hdac_ext_stream *stream, int cmd);
+ struct hdac_ext_stream *hext_stream, int cmd);
irqreturn_t hda_dsp_stream_threaded_handler(int irq, void *context);
int hda_dsp_stream_setup_bdl(struct snd_sof_dev *sdev,
struct snd_dma_buffer *dmab,
- struct hdac_stream *stream);
+ struct hdac_stream *hstream);
bool hda_dsp_check_ipc_irq(struct snd_sof_dev *sdev);
bool hda_dsp_check_stream_irq(struct snd_sof_dev *sdev);
+snd_pcm_uframes_t hda_dsp_stream_get_position(struct hdac_stream *hstream,
+ int direction, bool can_sleep);
+u64 hda_dsp_get_stream_llp(struct snd_sof_dev *sdev,
+ struct snd_soc_component *component,
+ struct snd_pcm_substream *substream);
+u64 hda_dsp_get_stream_ldp(struct snd_sof_dev *sdev,
+ struct snd_soc_component *component,
+ struct snd_pcm_substream *substream);
+
struct hdac_ext_stream *
- hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction);
+ hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction, u32 flags);
int hda_dsp_stream_put(struct snd_sof_dev *sdev, int direction, int stream_tag);
int hda_dsp_stream_spib_config(struct snd_sof_dev *sdev,
- struct hdac_ext_stream *stream,
+ struct hdac_ext_stream *hext_stream,
int enable, u32 size);
-void hda_ipc_msg_data(struct snd_sof_dev *sdev,
- struct snd_pcm_substream *substream,
- void *p, size_t sz);
-int hda_ipc_pcm_params(struct snd_sof_dev *sdev,
- struct snd_pcm_substream *substream,
- const struct sof_ipc_pcm_params_reply *reply);
-
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_PROBES)
-/*
- * Probe Compress Operations.
- */
-int hda_probe_compr_assign(struct snd_sof_dev *sdev,
- struct snd_compr_stream *cstream,
- struct snd_soc_dai *dai);
-int hda_probe_compr_free(struct snd_sof_dev *sdev,
- struct snd_compr_stream *cstream,
- struct snd_soc_dai *dai);
-int hda_probe_compr_set_params(struct snd_sof_dev *sdev,
- struct snd_compr_stream *cstream,
- struct snd_compr_params *params,
- struct snd_soc_dai *dai);
-int hda_probe_compr_trigger(struct snd_sof_dev *sdev,
- struct snd_compr_stream *cstream, int cmd,
- struct snd_soc_dai *dai);
-int hda_probe_compr_pointer(struct snd_sof_dev *sdev,
- struct snd_compr_stream *cstream,
- struct snd_compr_tstamp *tstamp,
- struct snd_soc_dai *dai);
-#endif
+int hda_ipc_msg_data(struct snd_sof_dev *sdev,
+ struct snd_sof_pcm_stream *sps,
+ void *p, size_t sz);
+int hda_set_stream_data_offset(struct snd_sof_dev *sdev,
+ struct snd_sof_pcm_stream *sps,
+ size_t posn_offset);
/*
* DSP IPC Operations.
@@ -604,16 +718,36 @@ int hda_dsp_ipc_get_window_offset(struct snd_sof_dev *sdev, u32 id);
irqreturn_t hda_dsp_ipc_irq_thread(int irq, void *context);
int hda_dsp_ipc_cmd_done(struct snd_sof_dev *sdev, int dir);
+void hda_dsp_get_state(struct snd_sof_dev *sdev, const char *level);
+void hda_dsp_dump_ext_rom_status(struct snd_sof_dev *sdev, const char *level,
+ u32 flags);
+
/*
* DSP Code loader.
*/
int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev);
-int hda_dsp_cl_boot_firmware_skl(struct snd_sof_dev *sdev);
+int hda_dsp_cl_boot_firmware_iccmax(struct snd_sof_dev *sdev);
+int hda_cl_copy_fw(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_stream);
+
+struct hdac_ext_stream *hda_cl_prepare(struct device *dev, unsigned int format,
+ unsigned int size, struct snd_dma_buffer *dmab,
+ bool persistent_buffer, int direction,
+ bool is_iccmax);
+int hda_cl_trigger(struct device *dev, struct hdac_ext_stream *hext_stream, int cmd);
+
+int hda_cl_cleanup(struct device *dev, struct snd_dma_buffer *dmab,
+ bool persistent_buffer, struct hdac_ext_stream *hext_stream);
+int cl_dsp_init(struct snd_sof_dev *sdev, int stream_tag, bool imr_boot);
+#define HDA_CL_STREAM_FORMAT 0x40
/* pre and post fw run ops */
int hda_dsp_pre_fw_run(struct snd_sof_dev *sdev);
int hda_dsp_post_fw_run(struct snd_sof_dev *sdev);
+/* parse platform specific ext manifest ops */
+int hda_dsp_ext_man_get_cavs_config_data(struct snd_sof_dev *sdev,
+ const struct sof_ext_man_elem_header *hdr);
+
/*
* HDA Controller Operations.
*/
@@ -623,27 +757,51 @@ void hda_dsp_ctrl_ppcap_int_enable(struct snd_sof_dev *sdev, bool enable);
int hda_dsp_ctrl_link_reset(struct snd_sof_dev *sdev, bool reset);
void hda_dsp_ctrl_misc_clock_gating(struct snd_sof_dev *sdev, bool enable);
int hda_dsp_ctrl_clock_power_gating(struct snd_sof_dev *sdev, bool enable);
-int hda_dsp_ctrl_init_chip(struct snd_sof_dev *sdev, bool full_reset);
+int hda_dsp_ctrl_init_chip(struct snd_sof_dev *sdev);
void hda_dsp_ctrl_stop_chip(struct snd_sof_dev *sdev);
/*
* HDA bus operations.
*/
-void sof_hda_bus_init(struct hdac_bus *bus, struct device *dev);
+void sof_hda_bus_init(struct snd_sof_dev *sdev, struct device *dev);
+void sof_hda_bus_exit(struct snd_sof_dev *sdev);
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC)
/*
* HDA Codec operations.
*/
-void hda_codec_probe_bus(struct snd_sof_dev *sdev,
- bool hda_codec_use_common_hdmi);
-void hda_codec_jack_wake_enable(struct snd_sof_dev *sdev);
+void hda_codec_probe_bus(struct snd_sof_dev *sdev);
+void hda_codec_jack_wake_enable(struct snd_sof_dev *sdev, bool enable);
void hda_codec_jack_check(struct snd_sof_dev *sdev);
+void hda_codec_check_for_state_change(struct snd_sof_dev *sdev);
+void hda_codec_init_cmd_io(struct snd_sof_dev *sdev);
+void hda_codec_resume_cmd_io(struct snd_sof_dev *sdev);
+void hda_codec_stop_cmd_io(struct snd_sof_dev *sdev);
+void hda_codec_suspend_cmd_io(struct snd_sof_dev *sdev);
+void hda_codec_detect_mask(struct snd_sof_dev *sdev);
+void hda_codec_rirb_status_clear(struct snd_sof_dev *sdev);
+bool hda_codec_check_rirb_status(struct snd_sof_dev *sdev);
+void hda_codec_set_codec_wakeup(struct snd_sof_dev *sdev, bool status);
+void hda_codec_device_remove(struct snd_sof_dev *sdev);
+
+#else
+
+static inline void hda_codec_probe_bus(struct snd_sof_dev *sdev) { }
+static inline void hda_codec_jack_wake_enable(struct snd_sof_dev *sdev, bool enable) { }
+static inline void hda_codec_jack_check(struct snd_sof_dev *sdev) { }
+static inline void hda_codec_check_for_state_change(struct snd_sof_dev *sdev) { }
+static inline void hda_codec_init_cmd_io(struct snd_sof_dev *sdev) { }
+static inline void hda_codec_resume_cmd_io(struct snd_sof_dev *sdev) { }
+static inline void hda_codec_stop_cmd_io(struct snd_sof_dev *sdev) { }
+static inline void hda_codec_suspend_cmd_io(struct snd_sof_dev *sdev) { }
+static inline void hda_codec_detect_mask(struct snd_sof_dev *sdev) { }
+static inline void hda_codec_rirb_status_clear(struct snd_sof_dev *sdev) { }
+static inline bool hda_codec_check_rirb_status(struct snd_sof_dev *sdev) { return false; }
+static inline void hda_codec_set_codec_wakeup(struct snd_sof_dev *sdev, bool status) { }
+static inline void hda_codec_device_remove(struct snd_sof_dev *sdev) { }
-#endif /* CONFIG_SND_SOC_SOF_HDA */
+#endif /* CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC */
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) && \
- (IS_ENABLED(CONFIG_SND_HDA_CODEC_HDMI) || \
- IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI))
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC) && IS_ENABLED(CONFIG_SND_HDA_CODEC_HDMI)
void hda_codec_i915_display_power(struct snd_sof_dev *sdev, bool enable);
int hda_codec_i915_init(struct snd_sof_dev *sdev);
@@ -651,8 +809,7 @@ int hda_codec_i915_exit(struct snd_sof_dev *sdev);
#else
-static inline void hda_codec_i915_display_power(struct snd_sof_dev *sdev,
- bool enable) { }
+static inline void hda_codec_i915_display_power(struct snd_sof_dev *sdev, bool enable) { }
static inline int hda_codec_i915_init(struct snd_sof_dev *sdev) { return 0; }
static inline int hda_codec_i915_exit(struct snd_sof_dev *sdev) { return 0; }
@@ -661,7 +818,8 @@ static inline int hda_codec_i915_exit(struct snd_sof_dev *sdev) { return 0; }
/*
* Trace Control.
*/
-int hda_dsp_trace_init(struct snd_sof_dev *sdev, u32 *stream_tag);
+int hda_dsp_trace_init(struct snd_sof_dev *sdev, struct snd_dma_buffer *dmab,
+ struct sof_ipc_dma_trace_params_ext *dtrace_params);
int hda_dsp_trace_release(struct snd_sof_dev *sdev);
int hda_dsp_trace_trigger(struct snd_sof_dev *sdev, int cmd);
@@ -670,76 +828,228 @@ int hda_dsp_trace_trigger(struct snd_sof_dev *sdev, int cmd);
*/
#if IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE)
+int hda_sdw_check_lcount_common(struct snd_sof_dev *sdev);
+int hda_sdw_check_lcount_ext(struct snd_sof_dev *sdev);
+int hda_sdw_check_lcount(struct snd_sof_dev *sdev);
int hda_sdw_startup(struct snd_sof_dev *sdev);
+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);
+bool hda_sdw_check_wakeen_irq_common(struct snd_sof_dev *sdev);
+void hda_sdw_process_wakeen_common(struct snd_sof_dev *sdev);
void hda_sdw_process_wakeen(struct snd_sof_dev *sdev);
+bool hda_common_check_sdw_irq(struct snd_sof_dev *sdev);
#else
-static inline int hda_sdw_acpi_scan(struct snd_sof_dev *sdev)
+static inline int hda_sdw_check_lcount_common(struct snd_sof_dev *sdev)
{
return 0;
}
-static inline int hda_sdw_probe(struct snd_sof_dev *sdev)
+static inline int hda_sdw_check_lcount_ext(struct snd_sof_dev *sdev)
{
return 0;
}
-static inline int hda_sdw_startup(struct snd_sof_dev *sdev)
+static inline int hda_sdw_check_lcount(struct snd_sof_dev *sdev)
{
return 0;
}
-static inline int hda_sdw_exit(struct snd_sof_dev *sdev)
+static inline int hda_sdw_startup(struct snd_sof_dev *sdev)
{
return 0;
}
+static inline void hda_common_enable_sdw_irq(struct snd_sof_dev *sdev, bool enable)
+{
+}
+
static inline void hda_sdw_int_enable(struct snd_sof_dev *sdev, bool enable)
{
}
-static inline bool hda_dsp_check_sdw_irq(struct snd_sof_dev *sdev)
+static inline bool hda_sdw_check_wakeen_irq_common(struct snd_sof_dev *sdev)
{
return false;
}
-static inline irqreturn_t hda_dsp_sdw_thread(int irq, void *context)
+static inline void hda_sdw_process_wakeen_common(struct snd_sof_dev *sdev)
{
- return IRQ_HANDLED;
}
-static inline bool hda_sdw_check_wakeen_irq(struct snd_sof_dev *sdev)
+static inline void hda_sdw_process_wakeen(struct snd_sof_dev *sdev)
{
- return false;
}
-static inline void hda_sdw_process_wakeen(struct snd_sof_dev *sdev)
+static inline bool hda_common_check_sdw_irq(struct snd_sof_dev *sdev)
{
+ return false;
}
+
#endif
+int sdw_hda_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *cpu_dai,
+ int link_id,
+ int intel_alh_id);
+
+int sdw_hda_dai_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *cpu_dai,
+ int link_id);
+
+int sdw_hda_dai_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *cpu_dai);
+
/* common dai driver */
extern struct snd_soc_dai_driver skl_dai[];
+int hda_dsp_dais_suspend(struct snd_sof_dev *sdev);
/*
* Platform Specific HW abstraction Ops.
*/
-extern const struct snd_sof_dsp_ops sof_apl_ops;
-extern const struct snd_sof_dsp_ops sof_cnl_ops;
+extern const struct snd_sof_dsp_ops sof_hda_common_ops;
+
+extern struct snd_sof_dsp_ops sof_skl_ops;
+int sof_skl_ops_init(struct snd_sof_dev *sdev);
+extern struct snd_sof_dsp_ops sof_apl_ops;
+int sof_apl_ops_init(struct snd_sof_dev *sdev);
+extern struct snd_sof_dsp_ops sof_cnl_ops;
+int sof_cnl_ops_init(struct snd_sof_dev *sdev);
+extern struct snd_sof_dsp_ops sof_tgl_ops;
+int sof_tgl_ops_init(struct snd_sof_dev *sdev);
+extern struct snd_sof_dsp_ops sof_icl_ops;
+int sof_icl_ops_init(struct snd_sof_dev *sdev);
+extern const struct sof_intel_dsp_desc skl_chip_info;
extern const struct sof_intel_dsp_desc apl_chip_info;
extern const struct sof_intel_dsp_desc cnl_chip_info;
-extern const struct sof_intel_dsp_desc skl_chip_info;
extern const struct sof_intel_dsp_desc icl_chip_info;
extern const struct sof_intel_dsp_desc tgl_chip_info;
+extern const struct sof_intel_dsp_desc tglh_chip_info;
extern const struct sof_intel_dsp_desc ehl_chip_info;
extern const struct sof_intel_dsp_desc jsl_chip_info;
+extern const struct sof_intel_dsp_desc adls_chip_info;
+extern const struct sof_intel_dsp_desc mtl_chip_info;
+extern const struct sof_intel_dsp_desc arl_s_chip_info;
+extern const struct sof_intel_dsp_desc lnl_chip_info;
+extern const struct sof_intel_dsp_desc ptl_chip_info;
+extern const struct sof_intel_dsp_desc wcl_chip_info;
+
+/* Probes support */
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_PROBES)
+int hda_probes_register(struct snd_sof_dev *sdev);
+void hda_probes_unregister(struct snd_sof_dev *sdev);
+#else
+static inline int hda_probes_register(struct snd_sof_dev *sdev)
+{
+ return 0;
+}
+
+static inline void hda_probes_unregister(struct snd_sof_dev *sdev)
+{
+}
+#endif /* CONFIG_SND_SOC_SOF_HDA_PROBES */
+
+/* SOF client registration for HDA platforms */
+int hda_register_clients(struct snd_sof_dev *sdev);
+void hda_unregister_clients(struct snd_sof_dev *sdev);
/* machine driver select */
-void hda_machine_select(struct snd_sof_dev *sdev);
-void hda_set_mach_params(const struct snd_soc_acpi_mach *mach,
- struct device *dev);
+struct snd_soc_acpi_mach *hda_machine_select(struct snd_sof_dev *sdev);
+void hda_set_mach_params(struct snd_soc_acpi_mach *mach,
+ struct snd_sof_dev *sdev);
+
+/* PCI driver selection and probe */
+int hda_pci_intel_probe(struct pci_dev *pci, const struct pci_device_id *pci_id);
+
+struct snd_sof_dai;
+struct sof_ipc_dai_config;
+
+#define SOF_HDA_POSITION_QUIRK_USE_SKYLAKE_LEGACY (0) /* previous implementation */
+#define SOF_HDA_POSITION_QUIRK_USE_DPIB_REGISTERS (1) /* recommended if VC0 only */
+#define SOF_HDA_POSITION_QUIRK_USE_DPIB_DDR_UPDATE (2) /* recommended with VC0 or VC1 */
+
+extern int sof_hda_position_quirk;
+
+void hda_set_dai_drv_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *ops);
+void hda_ops_free(struct snd_sof_dev *sdev);
+
+/* SKL/KBL */
+int hda_dsp_cl_boot_firmware_skl(struct snd_sof_dev *sdev);
+int hda_dsp_core_stall_reset(struct snd_sof_dev *sdev, unsigned int core_mask);
+
+/* IPC4 */
+irqreturn_t cnl_ipc4_irq_thread(int irq, void *context);
+int cnl_ipc4_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg);
+irqreturn_t hda_dsp_ipc4_irq_thread(int irq, void *context);
+bool hda_ipc4_tx_is_busy(struct snd_sof_dev *sdev);
+void hda_dsp_ipc4_schedule_d0i3_work(struct sof_intel_hda_dev *hdev,
+ struct snd_sof_ipc_msg *msg);
+int hda_dsp_ipc4_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg);
+void hda_ipc4_dump(struct snd_sof_dev *sdev);
+extern struct sdw_intel_ops sdw_callback;
+
+struct sof_ipc4_fw_library;
+int hda_dsp_ipc4_load_library(struct snd_sof_dev *sdev,
+ struct sof_ipc4_fw_library *fw_lib, bool reload);
+
+/**
+ * struct hda_dai_widget_dma_ops - DAI DMA ops optional by default unless specified otherwise
+ * @get_hext_stream: Mandatory function pointer to get the saved pointer to struct hdac_ext_stream
+ * @assign_hext_stream: Function pointer to assign a hdac_ext_stream
+ * @release_hext_stream: Function pointer to release the hdac_ext_stream
+ * @setup_hext_stream: Function pointer for hdac_ext_stream setup
+ * @reset_hext_stream: Function pointer for hdac_ext_stream reset
+ * @pre_trigger: Function pointer for DAI DMA pre-trigger actions
+ * @trigger: Function pointer for DAI DMA trigger actions
+ * @post_trigger: Function pointer for DAI DMA post-trigger actions
+ * @codec_dai_set_stream: Function pointer to set codec-side stream information
+ * @calc_stream_format: Function pointer to determine stream format from hw_params and
+ * for HDaudio codec DAI from the .sig bits
+ * @get_hlink: Mandatory function pointer to retrieve hlink, mainly to program LOSIDV
+ * for legacy HDaudio links or program HDaudio Extended Link registers.
+ */
+struct hda_dai_widget_dma_ops {
+ struct hdac_ext_stream *(*get_hext_stream)(struct snd_sof_dev *sdev,
+ struct snd_soc_dai *cpu_dai,
+ struct snd_pcm_substream *substream);
+ struct hdac_ext_stream *(*assign_hext_stream)(struct snd_sof_dev *sdev,
+ struct snd_soc_dai *cpu_dai,
+ struct snd_pcm_substream *substream);
+ void (*release_hext_stream)(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
+ struct snd_pcm_substream *substream);
+ void (*setup_hext_stream)(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_stream,
+ unsigned int format_val);
+ void (*reset_hext_stream)(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_sream);
+ int (*pre_trigger)(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
+ struct snd_pcm_substream *substream, int cmd);
+ int (*trigger)(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
+ struct snd_pcm_substream *substream, int cmd);
+ int (*post_trigger)(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
+ struct snd_pcm_substream *substream, int cmd);
+ void (*codec_dai_set_stream)(struct snd_sof_dev *sdev,
+ struct snd_pcm_substream *substream,
+ struct hdac_stream *hstream);
+ unsigned int (*calc_stream_format)(struct snd_sof_dev *sdev,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params);
+ struct hdac_ext_link * (*get_hlink)(struct snd_sof_dev *sdev,
+ struct snd_pcm_substream *substream);
+};
+
+const struct hda_dai_widget_dma_ops *
+hda_select_dai_widget_ops(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget);
+int hda_dai_config(struct snd_soc_dapm_widget *w, unsigned int flags,
+ struct snd_sof_dai_config_data *data);
+
+static inline struct snd_sof_dev *widget_to_sdev(struct snd_soc_dapm_widget *w)
+{
+ struct snd_sof_widget *swidget = w->dobj.private;
+ struct snd_soc_component *component = swidget->scomp;
+
+ return snd_soc_component_get_drvdata(component);
+}
#endif
diff --git a/sound/soc/sof/intel/icl.c b/sound/soc/sof/intel/icl.c
new file mode 100644
index 000000000000..dad6bc72ad37
--- /dev/null
+++ b/sound/soc/sof/intel/icl.c
@@ -0,0 +1,196 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+//
+// Copyright(c) 2020 Intel Corporation
+//
+// Author: Fred Oh <fred.oh@linux.intel.com>
+//
+
+/*
+ * Hardware interface for audio DSP on IceLake.
+ */
+
+#include <linux/kernel.h>
+#include <linux/kconfig.h>
+#include <linux/export.h>
+#include <linux/bits.h>
+#include "../ipc4-priv.h"
+#include "../ops.h"
+#include "hda.h"
+#include "hda-ipc.h"
+#include "../sof-audio.h"
+
+#define ICL_DSP_HPRO_CORE_ID 3
+
+static const struct snd_sof_debugfs_map icl_dsp_debugfs[] = {
+ {"hda", HDA_DSP_HDA_BAR, 0, 0x4000, SOF_DEBUGFS_ACCESS_ALWAYS},
+ {"pp", HDA_DSP_PP_BAR, 0, 0x1000, SOF_DEBUGFS_ACCESS_ALWAYS},
+ {"dsp", HDA_DSP_BAR, 0, 0x10000, SOF_DEBUGFS_ACCESS_ALWAYS},
+};
+
+static int icl_dsp_core_stall(struct snd_sof_dev *sdev, unsigned int core_mask)
+{
+ struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
+ const struct sof_intel_dsp_desc *chip = hda->desc;
+
+ /* make sure core_mask in host managed cores */
+ core_mask &= chip->host_managed_cores_mask;
+ if (!core_mask) {
+ dev_err(sdev->dev, "error: core_mask is not in host managed cores\n");
+ return -EINVAL;
+ }
+
+ /* stall core */
+ snd_sof_dsp_update_bits_unlocked(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPCS,
+ HDA_DSP_ADSPCS_CSTALL_MASK(core_mask),
+ HDA_DSP_ADSPCS_CSTALL_MASK(core_mask));
+
+ return 0;
+}
+
+/*
+ * post fw run operation for ICL.
+ * Core 3 will be powered up and in stall when HPRO is enabled
+ */
+static int icl_dsp_post_fw_run(struct snd_sof_dev *sdev)
+{
+ struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
+ int ret;
+
+ if (sdev->first_boot) {
+ struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata;
+
+ ret = hda_sdw_startup(sdev);
+ if (ret < 0) {
+ dev_err(sdev->dev, "error: could not startup SoundWire links\n");
+ return ret;
+ }
+
+ /* Check if IMR boot is usable */
+ if (!sof_debug_check_flag(SOF_DBG_IGNORE_D3_PERSISTENT) &&
+ sdev->fw_ready.flags & SOF_IPC_INFO_D3_PERSISTENT)
+ hdev->imrboot_supported = true;
+ }
+
+ hda_sdw_int_enable(sdev, true);
+
+ /*
+ * The recommended HW programming sequence for ICL is to
+ * power up core 3 and keep it in stall if HPRO is enabled.
+ */
+ if (!hda->clk_config_lpro) {
+ ret = hda_dsp_enable_core(sdev, BIT(ICL_DSP_HPRO_CORE_ID));
+ if (ret < 0) {
+ dev_err(sdev->dev, "error: dsp core power up failed on core %d\n",
+ ICL_DSP_HPRO_CORE_ID);
+ return ret;
+ }
+
+ sdev->enabled_cores_mask |= BIT(ICL_DSP_HPRO_CORE_ID);
+ sdev->dsp_core_ref_count[ICL_DSP_HPRO_CORE_ID]++;
+
+ snd_sof_dsp_stall(sdev, BIT(ICL_DSP_HPRO_CORE_ID));
+ }
+
+ /* re-enable clock gating and power gating */
+ return hda_dsp_ctrl_clock_power_gating(sdev, true);
+}
+
+/* Icelake ops */
+struct snd_sof_dsp_ops sof_icl_ops;
+
+int sof_icl_ops_init(struct snd_sof_dev *sdev)
+{
+ /* common defaults */
+ memcpy(&sof_icl_ops, &sof_hda_common_ops, sizeof(struct snd_sof_dsp_ops));
+
+ /* probe/remove/shutdown */
+ sof_icl_ops.shutdown = hda_dsp_shutdown;
+
+ if (sdev->pdata->ipc_type == SOF_IPC_TYPE_3) {
+ /* doorbell */
+ sof_icl_ops.irq_thread = cnl_ipc_irq_thread;
+
+ /* ipc */
+ sof_icl_ops.send_msg = cnl_ipc_send_msg;
+
+ /* debug */
+ sof_icl_ops.ipc_dump = cnl_ipc_dump;
+
+ sof_icl_ops.set_power_state = hda_dsp_set_power_state_ipc3;
+ }
+
+ if (sdev->pdata->ipc_type == SOF_IPC_TYPE_4) {
+ struct sof_ipc4_fw_data *ipc4_data;
+
+ sdev->private = kzalloc(sizeof(*ipc4_data), GFP_KERNEL);
+ if (!sdev->private)
+ return -ENOMEM;
+
+ ipc4_data = sdev->private;
+ ipc4_data->manifest_fw_hdr_offset = SOF_MAN4_FW_HDR_OFFSET;
+
+ ipc4_data->mtrace_type = SOF_IPC4_MTRACE_INTEL_CAVS_2;
+
+ /* External library loading support */
+ ipc4_data->load_library = hda_dsp_ipc4_load_library;
+
+ /* doorbell */
+ sof_icl_ops.irq_thread = cnl_ipc4_irq_thread;
+
+ /* ipc */
+ sof_icl_ops.send_msg = cnl_ipc4_send_msg;
+
+ /* debug */
+ sof_icl_ops.ipc_dump = cnl_ipc4_dump;
+
+ sof_icl_ops.set_power_state = hda_dsp_set_power_state_ipc4;
+ }
+
+ /* debug */
+ sof_icl_ops.debug_map = icl_dsp_debugfs;
+ sof_icl_ops.debug_map_count = ARRAY_SIZE(icl_dsp_debugfs);
+
+ /* pre/post fw run */
+ sof_icl_ops.post_fw_run = icl_dsp_post_fw_run;
+
+ /* firmware run */
+ sof_icl_ops.run = hda_dsp_cl_boot_firmware_iccmax;
+ sof_icl_ops.stall = icl_dsp_core_stall;
+
+ /* dsp core get/put */
+ sof_icl_ops.core_get = hda_dsp_core_get;
+
+ /* set DAI driver ops */
+ hda_set_dai_drv_ops(sdev, &sof_icl_ops);
+
+ return 0;
+};
+
+const struct sof_intel_dsp_desc icl_chip_info = {
+ /* Icelake */
+ .cores_num = 4,
+ .init_core_mask = 1,
+ .host_managed_cores_mask = GENMASK(3, 0),
+ .ipc_req = CNL_DSP_REG_HIPCIDR,
+ .ipc_req_mask = CNL_DSP_REG_HIPCIDR_BUSY,
+ .ipc_ack = CNL_DSP_REG_HIPCIDA,
+ .ipc_ack_mask = CNL_DSP_REG_HIPCIDA_DONE,
+ .ipc_ctl = CNL_DSP_REG_HIPCCTL,
+ .rom_status_reg = HDA_DSP_SRAM_REG_ROM_STATUS,
+ .rom_init_timeout = 300,
+ .ssp_count = ICL_SSP_COUNT,
+ .ssp_base_offset = CNL_SSP_BASE_OFFSET,
+ .sdw_shim_base = SDW_SHIM_BASE,
+ .sdw_alh_base = SDW_ALH_BASE,
+ .d0i3_offset = SOF_HDA_VS_D0I3C,
+ .read_sdw_lcount = hda_sdw_check_lcount_common,
+ .enable_sdw_irq = hda_common_enable_sdw_irq,
+ .check_sdw_irq = hda_common_check_sdw_irq,
+ .check_sdw_wakeen_irq = hda_sdw_check_wakeen_irq_common,
+ .sdw_process_wakeen = hda_sdw_process_wakeen_common,
+ .check_ipc_irq = hda_dsp_check_ipc_irq,
+ .cl_init = cl_dsp_init,
+ .power_down_dsp = hda_power_down_dsp,
+ .disable_interrupts = hda_dsp_disable_interrupts,
+ .hw_ip_version = SOF_INTEL_CAVS_2_0,
+};
diff --git a/sound/soc/sof/intel/intel-ipc.c b/sound/soc/sof/intel/intel-ipc.c
deleted file mode 100644
index 310f9168c124..000000000000
--- a/sound/soc/sof/intel/intel-ipc.c
+++ /dev/null
@@ -1,92 +0,0 @@
-// SPDX-License-Identifier: (GPL-2.0-only 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.
-//
-// Authors: Guennadi Liakhovetski <guennadi.liakhovetski@linux.intel.com>
-
-/* Intel-specific SOF IPC code */
-
-#include <linux/device.h>
-#include <linux/export.h>
-#include <linux/module.h>
-#include <linux/types.h>
-
-#include <sound/pcm.h>
-#include <sound/sof/stream.h>
-
-#include "../ops.h"
-#include "../sof-priv.h"
-
-struct intel_stream {
- size_t posn_offset;
-};
-
-/* Mailbox-based Intel IPC implementation */
-void intel_ipc_msg_data(struct snd_sof_dev *sdev,
- struct snd_pcm_substream *substream,
- void *p, size_t sz)
-{
- if (!substream || !sdev->stream_box.size) {
- sof_mailbox_read(sdev, sdev->dsp_box.offset, p, sz);
- } else {
- struct intel_stream *stream = substream->runtime->private_data;
-
- /* The stream might already be closed */
- if (stream)
- sof_mailbox_read(sdev, stream->posn_offset, p, sz);
- }
-}
-EXPORT_SYMBOL_NS(intel_ipc_msg_data, SND_SOC_SOF_INTEL_HIFI_EP_IPC);
-
-int intel_ipc_pcm_params(struct snd_sof_dev *sdev,
- struct snd_pcm_substream *substream,
- const struct sof_ipc_pcm_params_reply *reply)
-{
- struct intel_stream *stream = substream->runtime->private_data;
- size_t posn_offset = reply->posn_offset;
-
- /* check if offset is overflow or it is not aligned */
- if (posn_offset > sdev->stream_box.size ||
- posn_offset % sizeof(struct sof_ipc_stream_posn) != 0)
- return -EINVAL;
-
- stream->posn_offset = sdev->stream_box.offset + posn_offset;
-
- dev_dbg(sdev->dev, "pcm: stream dir %d, posn mailbox offset is %zu",
- substream->stream, stream->posn_offset);
-
- return 0;
-}
-EXPORT_SYMBOL_NS(intel_ipc_pcm_params, SND_SOC_SOF_INTEL_HIFI_EP_IPC);
-
-int intel_pcm_open(struct snd_sof_dev *sdev,
- struct snd_pcm_substream *substream)
-{
- struct intel_stream *stream = kmalloc(sizeof(*stream), GFP_KERNEL);
-
- if (!stream)
- return -ENOMEM;
-
- /* binding pcm substream to hda stream */
- substream->runtime->private_data = stream;
-
- return 0;
-}
-EXPORT_SYMBOL_NS(intel_pcm_open, SND_SOC_SOF_INTEL_HIFI_EP_IPC);
-
-int intel_pcm_close(struct snd_sof_dev *sdev,
- struct snd_pcm_substream *substream)
-{
- struct intel_stream *stream = substream->runtime->private_data;
-
- substream->runtime->private_data = NULL;
- kfree(stream);
-
- return 0;
-}
-EXPORT_SYMBOL_NS(intel_pcm_close, SND_SOC_SOF_INTEL_HIFI_EP_IPC);
-
-MODULE_LICENSE("Dual BSD/GPL");
diff --git a/sound/soc/sof/intel/lnl.c b/sound/soc/sof/intel/lnl.c
new file mode 100644
index 000000000000..2f3222040f98
--- /dev/null
+++ b/sound/soc/sof/intel/lnl.c
@@ -0,0 +1,189 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+//
+// Copyright(c) 2023 Intel Corporation
+
+/*
+ * Hardware interface for audio DSP on LunarLake.
+ */
+
+#include <linux/debugfs.h>
+#include <linux/firmware.h>
+#include <sound/hda_register.h>
+#include <sound/sof/ipc4/header.h>
+#include <trace/events/sof_intel.h>
+#include "../ipc4-priv.h"
+#include "../ops.h"
+#include "hda.h"
+#include "hda-ipc.h"
+#include "../sof-audio.h"
+#include "mtl.h"
+#include "lnl.h"
+#include <sound/hda-mlink.h>
+
+/* this helps allows the DSP to setup DMIC/SSP */
+static int hdac_bus_offload_dmic_ssp(struct hdac_bus *bus, bool enable)
+{
+ int ret;
+
+ ret = hdac_bus_eml_enable_offload(bus, true,
+ AZX_REG_ML_LEPTR_ID_INTEL_SSP, enable);
+ if (ret < 0)
+ return ret;
+
+ ret = hdac_bus_eml_enable_offload(bus, true,
+ AZX_REG_ML_LEPTR_ID_INTEL_DMIC, enable);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int lnl_hda_dsp_probe(struct snd_sof_dev *sdev)
+{
+ int ret;
+
+ ret = hda_dsp_probe(sdev);
+ if (ret < 0)
+ return ret;
+
+ return hdac_bus_offload_dmic_ssp(sof_to_bus(sdev), true);
+}
+
+static void lnl_hda_dsp_remove(struct snd_sof_dev *sdev)
+{
+ int ret;
+
+ ret = hdac_bus_offload_dmic_ssp(sof_to_bus(sdev), false);
+ if (ret < 0)
+ dev_warn(sdev->dev,
+ "Failed to disable offload for DMIC/SSP: %d\n", ret);
+
+ hda_dsp_remove(sdev);
+}
+
+static int lnl_hda_dsp_resume(struct snd_sof_dev *sdev)
+{
+ int ret;
+
+ ret = hda_dsp_resume(sdev);
+ if (ret < 0)
+ return ret;
+
+ return hdac_bus_offload_dmic_ssp(sof_to_bus(sdev), true);
+}
+
+static int lnl_hda_dsp_runtime_resume(struct snd_sof_dev *sdev)
+{
+ int ret;
+
+ ret = hda_dsp_runtime_resume(sdev);
+ if (ret < 0)
+ return ret;
+
+ return hdac_bus_offload_dmic_ssp(sof_to_bus(sdev), true);
+}
+
+static int lnl_dsp_post_fw_run(struct snd_sof_dev *sdev)
+{
+ if (sdev->first_boot) {
+ struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
+
+ /* Check if IMR boot is usable */
+ if (!sof_debug_check_flag(SOF_DBG_IGNORE_D3_PERSISTENT)) {
+ hda->imrboot_supported = true;
+ debugfs_create_bool("skip_imr_boot",
+ 0644, sdev->debugfs_root,
+ &hda->skip_imr_boot);
+ }
+ }
+
+ return 0;
+}
+
+int sof_lnl_set_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *dsp_ops)
+{
+ int ret;
+
+ ret = sof_mtl_set_ops(sdev, dsp_ops);
+ if (ret)
+ return ret;
+
+ /* probe/remove */
+ if (!sdev->dspless_mode_selected) {
+ dsp_ops->probe = lnl_hda_dsp_probe;
+ dsp_ops->remove = lnl_hda_dsp_remove;
+ }
+
+ /* post fw run */
+ dsp_ops->post_fw_run = lnl_dsp_post_fw_run;
+
+ /* PM */
+ if (!sdev->dspless_mode_selected) {
+ dsp_ops->resume = lnl_hda_dsp_resume;
+ dsp_ops->runtime_resume = lnl_hda_dsp_runtime_resume;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_NS(sof_lnl_set_ops, "SND_SOC_SOF_INTEL_LNL");
+
+/* Check if an SDW IRQ occurred */
+bool lnl_dsp_check_sdw_irq(struct snd_sof_dev *sdev)
+{
+ struct hdac_bus *bus = sof_to_bus(sdev);
+
+ return hdac_bus_eml_check_interrupt(bus, true, AZX_REG_ML_LEPTR_ID_SDW);
+}
+EXPORT_SYMBOL_NS(lnl_dsp_check_sdw_irq, "SND_SOC_SOF_INTEL_LNL");
+
+int lnl_dsp_disable_interrupts(struct snd_sof_dev *sdev)
+{
+ mtl_disable_ipc_interrupts(sdev);
+ return mtl_enable_interrupts(sdev, false);
+}
+EXPORT_SYMBOL_NS(lnl_dsp_disable_interrupts, "SND_SOC_SOF_INTEL_LNL");
+
+bool lnl_sdw_check_wakeen_irq(struct snd_sof_dev *sdev)
+{
+ struct hdac_bus *bus = sof_to_bus(sdev);
+ u16 wake_sts;
+
+ /*
+ * we need to use the global HDaudio WAKEEN/STS to be able to
+ * detect wakes in low-power modes. The link-specific information
+ * is handled in the process_wakeen() helper, this helper only
+ * detects a SoundWire wake without identifying the link.
+ */
+ wake_sts = snd_hdac_chip_readw(bus, STATESTS);
+
+ /* filter out the range of SDIs that can be set for SoundWire */
+ return wake_sts & GENMASK(SDW_MAX_DEVICES, SDW_INTEL_DEV_NUM_IDA_MIN);
+}
+EXPORT_SYMBOL_NS(lnl_sdw_check_wakeen_irq, "SND_SOC_SOF_INTEL_LNL");
+
+const struct sof_intel_dsp_desc lnl_chip_info = {
+ .cores_num = 5,
+ .init_core_mask = BIT(0),
+ .host_managed_cores_mask = BIT(0),
+ .ipc_req = MTL_DSP_REG_HFIPCXIDR,
+ .ipc_req_mask = MTL_DSP_REG_HFIPCXIDR_BUSY,
+ .ipc_ack = MTL_DSP_REG_HFIPCXIDA,
+ .ipc_ack_mask = MTL_DSP_REG_HFIPCXIDA_DONE,
+ .ipc_ctl = MTL_DSP_REG_HFIPCXCTL,
+ .rom_status_reg = LNL_DSP_REG_HFDSC,
+ .rom_init_timeout = 300,
+ .ssp_count = MTL_SSP_COUNT,
+ .d0i3_offset = MTL_HDA_VS_D0I3C,
+ .read_sdw_lcount = hda_sdw_check_lcount_ext,
+ .check_sdw_irq = lnl_dsp_check_sdw_irq,
+ .check_sdw_wakeen_irq = lnl_sdw_check_wakeen_irq,
+ .sdw_process_wakeen = hda_sdw_process_wakeen_common,
+ .check_ipc_irq = mtl_dsp_check_ipc_irq,
+ .cl_init = mtl_dsp_cl_init,
+ .power_down_dsp = mtl_power_down_dsp,
+ .disable_interrupts = lnl_dsp_disable_interrupts,
+ .hw_ip_version = SOF_INTEL_ACE_2_0,
+};
+
+MODULE_IMPORT_NS("SND_SOC_SOF_INTEL_MTL");
+MODULE_IMPORT_NS("SND_SOC_SOF_HDA_MLINK");
diff --git a/sound/soc/sof/intel/lnl.h b/sound/soc/sof/intel/lnl.h
new file mode 100644
index 000000000000..2837f818ac51
--- /dev/null
+++ b/sound/soc/sof/intel/lnl.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: (GPL-2.0-only 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) 2024 Intel Corporation
+ */
+
+#ifndef __SOF_INTEL_LNL_H
+#define __SOF_INTEL_LNL_H
+
+#define LNL_DSP_REG_HFDSC 0x160200 /* DSP core0 status */
+#define LNL_DSP_REG_HFDEC 0x160204 /* DSP core0 error */
+
+int sof_lnl_set_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *dsp_ops);
+
+bool lnl_dsp_check_sdw_irq(struct snd_sof_dev *sdev);
+int lnl_dsp_disable_interrupts(struct snd_sof_dev *sdev);
+bool lnl_sdw_check_wakeen_irq(struct snd_sof_dev *sdev);
+
+#endif /* __SOF_INTEL_LNL_H */
diff --git a/sound/soc/sof/intel/mtl.c b/sound/soc/sof/intel/mtl.c
new file mode 100644
index 000000000000..2242c96bfa51
--- /dev/null
+++ b/sound/soc/sof/intel/mtl.c
@@ -0,0 +1,817 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+//
+// Copyright(c) 2022 Intel Corporation
+//
+// Authors: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
+//
+
+/*
+ * Hardware interface for audio DSP on Meteorlake.
+ */
+
+#include <linux/debugfs.h>
+#include <linux/firmware.h>
+#include <linux/string_choices.h>
+#include <sound/sof/ipc4/header.h>
+#include <trace/events/sof_intel.h>
+#include "../ipc4-priv.h"
+#include "../ops.h"
+#include "hda.h"
+#include "hda-ipc.h"
+#include "../sof-audio.h"
+#include "mtl.h"
+#include "telemetry.h"
+
+static const struct snd_sof_debugfs_map mtl_dsp_debugfs[] = {
+ {"hda", HDA_DSP_HDA_BAR, 0, 0x4000, SOF_DEBUGFS_ACCESS_ALWAYS},
+ {"pp", HDA_DSP_PP_BAR, 0, 0x1000, SOF_DEBUGFS_ACCESS_ALWAYS},
+ {"dsp", HDA_DSP_BAR, 0, 0x10000, SOF_DEBUGFS_ACCESS_ALWAYS},
+ {"fw_regs", HDA_DSP_BAR, MTL_SRAM_WINDOW_OFFSET(0), 0x1000, SOF_DEBUGFS_ACCESS_D0_ONLY},
+};
+
+static void mtl_ipc_host_done(struct snd_sof_dev *sdev)
+{
+ /*
+ * clear busy interrupt to tell dsp controller this interrupt has been accepted,
+ * not trigger it again
+ */
+ snd_sof_dsp_update_bits_forced(sdev, HDA_DSP_BAR, MTL_DSP_REG_HFIPCXTDR,
+ MTL_DSP_REG_HFIPCXTDR_BUSY, MTL_DSP_REG_HFIPCXTDR_BUSY);
+ /*
+ * clear busy bit to ack dsp the msg has been processed and send reply msg to dsp
+ */
+ snd_sof_dsp_update_bits_forced(sdev, HDA_DSP_BAR, MTL_DSP_REG_HFIPCXTDA,
+ MTL_DSP_REG_HFIPCXTDA_BUSY, 0);
+}
+
+static void mtl_ipc_dsp_done(struct snd_sof_dev *sdev)
+{
+ /*
+ * set DONE bit - tell DSP we have received the reply msg from DSP, and processed it,
+ * don't send more reply to host
+ */
+ snd_sof_dsp_update_bits_forced(sdev, HDA_DSP_BAR, MTL_DSP_REG_HFIPCXIDA,
+ MTL_DSP_REG_HFIPCXIDA_DONE, MTL_DSP_REG_HFIPCXIDA_DONE);
+
+ /* unmask Done interrupt */
+ snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, MTL_DSP_REG_HFIPCXCTL,
+ MTL_DSP_REG_HFIPCXCTL_DONE, MTL_DSP_REG_HFIPCXCTL_DONE);
+}
+
+/* Check if an IPC IRQ occurred */
+bool mtl_dsp_check_ipc_irq(struct snd_sof_dev *sdev)
+{
+ u32 irq_status;
+ u32 hfintipptr;
+
+ if (sdev->dspless_mode_selected)
+ return false;
+
+ /* read Interrupt IP Pointer */
+ hfintipptr = snd_sof_dsp_read(sdev, HDA_DSP_BAR, MTL_HFINTIPPTR) & MTL_HFINTIPPTR_PTR_MASK;
+ irq_status = snd_sof_dsp_read(sdev, HDA_DSP_BAR, hfintipptr + MTL_DSP_IRQSTS);
+
+ trace_sof_intel_hda_irq_ipc_check(sdev, irq_status);
+
+ if (irq_status != U32_MAX && (irq_status & MTL_DSP_IRQSTS_IPC))
+ return true;
+
+ return false;
+}
+EXPORT_SYMBOL_NS(mtl_dsp_check_ipc_irq, "SND_SOC_SOF_INTEL_MTL");
+
+/* Check if an SDW IRQ occurred */
+static bool mtl_dsp_check_sdw_irq(struct snd_sof_dev *sdev)
+{
+ u32 irq_status;
+ u32 hfintipptr;
+
+ /* read Interrupt IP Pointer */
+ hfintipptr = snd_sof_dsp_read(sdev, HDA_DSP_BAR, MTL_HFINTIPPTR) & MTL_HFINTIPPTR_PTR_MASK;
+ irq_status = snd_sof_dsp_read(sdev, HDA_DSP_BAR, hfintipptr + MTL_DSP_IRQSTS);
+
+ if (irq_status != U32_MAX && (irq_status & MTL_DSP_IRQSTS_SDW))
+ return true;
+
+ return false;
+}
+
+static int mtl_ipc_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg)
+{
+ struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata;
+ struct sof_ipc4_msg *msg_data = msg->msg_data;
+
+ if (hda_ipc4_tx_is_busy(sdev)) {
+ hdev->delayed_ipc_tx_msg = msg;
+ return 0;
+ }
+
+ hdev->delayed_ipc_tx_msg = NULL;
+
+ /* send the message via mailbox */
+ if (msg_data->data_size)
+ sof_mailbox_write(sdev, sdev->host_box.offset, msg_data->data_ptr,
+ msg_data->data_size);
+
+ snd_sof_dsp_write(sdev, HDA_DSP_BAR, MTL_DSP_REG_HFIPCXIDDY,
+ msg_data->extension);
+ snd_sof_dsp_write(sdev, HDA_DSP_BAR, MTL_DSP_REG_HFIPCXIDR,
+ msg_data->primary | MTL_DSP_REG_HFIPCXIDR_BUSY);
+
+ hda_dsp_ipc4_schedule_d0i3_work(hdev, msg);
+
+ return 0;
+}
+
+void mtl_enable_ipc_interrupts(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,
+ MTL_DSP_REG_HFIPCXCTL_BUSY | MTL_DSP_REG_HFIPCXCTL_DONE,
+ MTL_DSP_REG_HFIPCXCTL_BUSY | MTL_DSP_REG_HFIPCXCTL_DONE);
+}
+
+void mtl_disable_ipc_interrupts(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 DONE and BUSY interrupts */
+ snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, chip->ipc_ctl,
+ MTL_DSP_REG_HFIPCXCTL_BUSY | MTL_DSP_REG_HFIPCXCTL_DONE, 0);
+}
+EXPORT_SYMBOL_NS(mtl_disable_ipc_interrupts, "SND_SOC_SOF_INTEL_MTL");
+
+static void mtl_enable_sdw_irq(struct snd_sof_dev *sdev, bool enable)
+{
+ u32 hipcie;
+ u32 mask;
+ u32 val;
+ int ret;
+
+ if (sdev->dspless_mode_selected)
+ return;
+
+ /* Enable/Disable SoundWire interrupt */
+ mask = MTL_DSP_REG_HfSNDWIE_IE_MASK;
+ if (enable)
+ val = mask;
+ else
+ val = 0;
+
+ snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, MTL_DSP_REG_HfSNDWIE, mask, val);
+
+ /* check if operation was successful */
+ ret = snd_sof_dsp_read_poll_timeout(sdev, HDA_DSP_BAR, MTL_DSP_REG_HfSNDWIE, hipcie,
+ (hipcie & mask) == val,
+ HDA_DSP_REG_POLL_INTERVAL_US, HDA_DSP_RESET_TIMEOUT_US);
+ if (ret < 0)
+ dev_err(sdev->dev, "failed to set SoundWire IPC interrupt %s\n",
+ str_enable_disable(enable));
+}
+
+int mtl_enable_interrupts(struct snd_sof_dev *sdev, bool enable)
+{
+ u32 hfintipptr;
+ u32 irqinten;
+ u32 hipcie;
+ u32 mask;
+ u32 val;
+ int ret;
+
+ if (sdev->dspless_mode_selected)
+ return 0;
+
+ /* read Interrupt IP Pointer */
+ hfintipptr = snd_sof_dsp_read(sdev, HDA_DSP_BAR, MTL_HFINTIPPTR) & MTL_HFINTIPPTR_PTR_MASK;
+
+ /* Enable/Disable Host IPC and SOUNDWIRE */
+ mask = MTL_IRQ_INTEN_L_HOST_IPC_MASK | MTL_IRQ_INTEN_L_SOUNDWIRE_MASK;
+ if (enable)
+ val = mask;
+ else
+ val = 0;
+
+ snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, hfintipptr, mask, val);
+
+ /* check if operation was successful */
+ ret = snd_sof_dsp_read_poll_timeout(sdev, HDA_DSP_BAR, hfintipptr, irqinten,
+ (irqinten & mask) == val,
+ HDA_DSP_REG_POLL_INTERVAL_US, HDA_DSP_RESET_TIMEOUT_US);
+ if (ret < 0) {
+ dev_err(sdev->dev, "failed to %s Host IPC and/or SOUNDWIRE\n",
+ str_enable_disable(enable));
+ return ret;
+ }
+
+ /* Enable/Disable Host IPC interrupt*/
+ mask = MTL_DSP_REG_HfHIPCIE_IE_MASK;
+ if (enable)
+ val = mask;
+ else
+ val = 0;
+
+ snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, MTL_DSP_REG_HfHIPCIE, mask, val);
+
+ /* check if operation was successful */
+ ret = snd_sof_dsp_read_poll_timeout(sdev, HDA_DSP_BAR, MTL_DSP_REG_HfHIPCIE, hipcie,
+ (hipcie & mask) == val,
+ HDA_DSP_REG_POLL_INTERVAL_US, HDA_DSP_RESET_TIMEOUT_US);
+ if (ret < 0) {
+ dev_err(sdev->dev, "failed to set Host IPC interrupt %s\n",
+ str_enable_disable(enable));
+ return ret;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_NS(mtl_enable_interrupts, "SND_SOC_SOF_INTEL_MTL");
+
+/* pre fw run operations */
+static int mtl_dsp_pre_fw_run(struct snd_sof_dev *sdev)
+{
+ struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata;
+ u32 dsphfpwrsts;
+ u32 dsphfdsscs;
+ u32 cpa;
+ u32 pgs;
+ int ret;
+ u32 dsppwrctl;
+ u32 dsppwrsts;
+ const struct sof_intel_dsp_desc *chip;
+
+ chip = get_chip_info(sdev->pdata);
+ if (chip->hw_ip_version > SOF_INTEL_ACE_2_0) {
+ dsppwrctl = PTL_HFPWRCTL2;
+ dsppwrsts = PTL_HFPWRSTS2;
+ } else {
+ dsppwrctl = MTL_HFPWRCTL;
+ dsppwrsts = MTL_HFPWRSTS;
+ }
+
+ /* Set the DSP subsystem power on */
+ snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, MTL_HFDSSCS,
+ MTL_HFDSSCS_SPA_MASK, MTL_HFDSSCS_SPA_MASK);
+
+ /* Wait for unstable CPA read (1 then 0 then 1) just after setting SPA bit */
+ usleep_range(1000, 1010);
+
+ /* poll with timeout to check if operation successful */
+ cpa = MTL_HFDSSCS_CPA_MASK;
+ ret = snd_sof_dsp_read_poll_timeout(sdev, HDA_DSP_BAR, MTL_HFDSSCS, dsphfdsscs,
+ (dsphfdsscs & cpa) == cpa, HDA_DSP_REG_POLL_INTERVAL_US,
+ HDA_DSP_RESET_TIMEOUT_US);
+ if (ret < 0) {
+ dev_err(sdev->dev, "failed to enable DSP subsystem\n");
+ return ret;
+ }
+
+ /* Power up gated-DSP-0 domain in order to access the DSP shim register block. */
+ snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, dsppwrctl,
+ MTL_HFPWRCTL_WPDSPHPXPG, MTL_HFPWRCTL_WPDSPHPXPG);
+
+ usleep_range(1000, 1010);
+
+ /* poll with timeout to check if operation successful */
+ pgs = MTL_HFPWRSTS_DSPHPXPGS_MASK;
+ ret = snd_sof_dsp_read_poll_timeout(sdev, HDA_DSP_BAR, dsppwrsts, dsphfpwrsts,
+ (dsphfpwrsts & pgs) == pgs,
+ HDA_DSP_REG_POLL_INTERVAL_US,
+ HDA_DSP_RESET_TIMEOUT_US);
+ if (ret < 0)
+ dev_err(sdev->dev, "failed to power up gated DSP domain\n");
+
+ /* if SoundWire is used, make sure it is not power-gated */
+ if (hdev->info.handle && hdev->info.link_mask > 0)
+ snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, MTL_HFPWRCTL,
+ MTL_HfPWRCTL_WPIOXPG(1), MTL_HfPWRCTL_WPIOXPG(1));
+
+ return ret;
+}
+
+static int mtl_dsp_post_fw_run(struct snd_sof_dev *sdev)
+{
+ int ret;
+
+ if (sdev->first_boot) {
+ struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata;
+
+ ret = hda_sdw_startup(sdev);
+ if (ret < 0) {
+ dev_err(sdev->dev, "could not startup SoundWire links\n");
+ return ret;
+ }
+
+ /* Check if IMR boot is usable */
+ if (!sof_debug_check_flag(SOF_DBG_IGNORE_D3_PERSISTENT)) {
+ hdev->imrboot_supported = true;
+ debugfs_create_bool("skip_imr_boot",
+ 0644, sdev->debugfs_root,
+ &hdev->skip_imr_boot);
+ }
+ }
+
+ hda_sdw_int_enable(sdev, true);
+ return 0;
+}
+
+static void mtl_dsp_dump(struct snd_sof_dev *sdev, u32 flags)
+{
+ char *level = (flags & SOF_DBG_DUMP_OPTIONAL) ? KERN_DEBUG : KERN_ERR;
+ u32 fwsts;
+ u32 fwlec;
+
+ hda_dsp_get_state(sdev, level);
+ fwsts = snd_sof_dsp_read(sdev, HDA_DSP_BAR, MTL_DSP_ROM_STS);
+ fwlec = snd_sof_dsp_read(sdev, HDA_DSP_BAR, MTL_DSP_ROM_ERROR);
+
+ if (fwsts != 0xffffffff)
+ dev_err(sdev->dev, "Firmware state: %#x, status/error code: %#x\n",
+ fwsts, fwlec);
+
+ sof_ipc4_intel_dump_telemetry_state(sdev, flags);
+}
+
+static bool mtl_dsp_primary_core_is_enabled(struct snd_sof_dev *sdev)
+{
+ int val;
+
+ val = snd_sof_dsp_read(sdev, HDA_DSP_BAR, MTL_DSP2CXCTL_PRIMARY_CORE);
+ if (val != U32_MAX && val & MTL_DSP2CXCTL_PRIMARY_CORE_CPA_MASK)
+ return true;
+
+ return false;
+}
+
+static int mtl_dsp_core_power_up(struct snd_sof_dev *sdev, int core)
+{
+ unsigned int cpa;
+ u32 dspcxctl;
+ int ret;
+
+ /* Only the primary core can be powered up by the host */
+ if (core != SOF_DSP_PRIMARY_CORE || mtl_dsp_primary_core_is_enabled(sdev))
+ return 0;
+
+ /* Program the owner of the IP & shim registers (10: Host CPU) */
+ snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, MTL_DSP2CXCTL_PRIMARY_CORE,
+ MTL_DSP2CXCTL_PRIMARY_CORE_OSEL,
+ 0x2 << MTL_DSP2CXCTL_PRIMARY_CORE_OSEL_SHIFT);
+
+ /* enable SPA bit */
+ snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, MTL_DSP2CXCTL_PRIMARY_CORE,
+ MTL_DSP2CXCTL_PRIMARY_CORE_SPA_MASK,
+ MTL_DSP2CXCTL_PRIMARY_CORE_SPA_MASK);
+
+ /* Wait for unstable CPA read (1 then 0 then 1) just after setting SPA bit */
+ usleep_range(1000, 1010);
+
+ /* poll with timeout to check if operation successful */
+ cpa = MTL_DSP2CXCTL_PRIMARY_CORE_CPA_MASK;
+ ret = snd_sof_dsp_read_poll_timeout(sdev, HDA_DSP_BAR, MTL_DSP2CXCTL_PRIMARY_CORE, dspcxctl,
+ (dspcxctl & cpa) == cpa, HDA_DSP_REG_POLL_INTERVAL_US,
+ HDA_DSP_RESET_TIMEOUT_US);
+ if (ret < 0) {
+ dev_err(sdev->dev, "%s: timeout on MTL_DSP2CXCTL_PRIMARY_CORE read\n",
+ __func__);
+ return ret;
+ }
+
+ /* set primary core mask and refcount to 1 */
+ sdev->enabled_cores_mask = BIT(SOF_DSP_PRIMARY_CORE);
+ sdev->dsp_core_ref_count[SOF_DSP_PRIMARY_CORE] = 1;
+
+ return 0;
+}
+
+static int mtl_dsp_core_power_down(struct snd_sof_dev *sdev, int core)
+{
+ u32 dspcxctl;
+ int ret;
+
+ /* Only the primary core can be powered down by the host */
+ if (core != SOF_DSP_PRIMARY_CORE || !mtl_dsp_primary_core_is_enabled(sdev))
+ return 0;
+
+ /* disable SPA bit */
+ snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, MTL_DSP2CXCTL_PRIMARY_CORE,
+ MTL_DSP2CXCTL_PRIMARY_CORE_SPA_MASK, 0);
+
+ /* Wait for unstable CPA read (0 then 1 then 0) just after setting SPA bit */
+ usleep_range(1000, 1010);
+
+ ret = snd_sof_dsp_read_poll_timeout(sdev, HDA_DSP_BAR, MTL_DSP2CXCTL_PRIMARY_CORE, dspcxctl,
+ !(dspcxctl & MTL_DSP2CXCTL_PRIMARY_CORE_CPA_MASK),
+ HDA_DSP_REG_POLL_INTERVAL_US,
+ HDA_DSP_PD_TIMEOUT * USEC_PER_MSEC);
+ if (ret < 0) {
+ dev_err(sdev->dev, "failed to power down primary core\n");
+ return ret;
+ }
+
+ sdev->enabled_cores_mask = 0;
+ sdev->dsp_core_ref_count[SOF_DSP_PRIMARY_CORE] = 0;
+
+ return 0;
+}
+
+int mtl_power_down_dsp(struct snd_sof_dev *sdev)
+{
+ u32 dsphfdsscs, cpa;
+ int ret;
+
+ /* first power down core */
+ ret = mtl_dsp_core_power_down(sdev, SOF_DSP_PRIMARY_CORE);
+ if (ret) {
+ dev_err(sdev->dev, "mtl dsp power down error, %d\n", ret);
+ return ret;
+ }
+
+ /* Set the DSP subsystem power down */
+ snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, MTL_HFDSSCS,
+ MTL_HFDSSCS_SPA_MASK, 0);
+
+ /* Wait for unstable CPA read (0 then 1 then 0) just after setting SPA bit */
+ usleep_range(1000, 1010);
+
+ /* poll with timeout to check if operation successful */
+ cpa = MTL_HFDSSCS_CPA_MASK;
+ dsphfdsscs = snd_sof_dsp_read(sdev, HDA_DSP_BAR, MTL_HFDSSCS);
+ return snd_sof_dsp_read_poll_timeout(sdev, HDA_DSP_BAR, MTL_HFDSSCS, dsphfdsscs,
+ (dsphfdsscs & cpa) == 0, HDA_DSP_REG_POLL_INTERVAL_US,
+ HDA_DSP_RESET_TIMEOUT_US);
+}
+EXPORT_SYMBOL_NS(mtl_power_down_dsp, "SND_SOC_SOF_INTEL_MTL");
+
+int mtl_dsp_cl_init(struct snd_sof_dev *sdev, int stream_tag, bool imr_boot)
+{
+ struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
+ const struct sof_intel_dsp_desc *chip = hda->desc;
+ unsigned int status, target_status;
+ u32 ipc_hdr, flags;
+ char *dump_msg;
+ int ret;
+
+ /* step 1: purge FW request */
+ ipc_hdr = chip->ipc_req_mask | HDA_DSP_ROM_IPC_CONTROL;
+ if (!imr_boot)
+ ipc_hdr |= HDA_DSP_ROM_IPC_PURGE_FW | ((stream_tag - 1) << 9);
+
+ snd_sof_dsp_write(sdev, HDA_DSP_BAR, chip->ipc_req, ipc_hdr);
+
+ /* step 2: power up primary core */
+ ret = mtl_dsp_core_power_up(sdev, SOF_DSP_PRIMARY_CORE);
+ if (ret < 0) {
+ if (hda->boot_iteration == HDA_FW_BOOT_ATTEMPTS)
+ dev_err(sdev->dev, "dsp core 0/1 power up failed\n");
+ goto err;
+ }
+
+ dev_dbg(sdev->dev, "Primary core power up successful\n");
+
+ /* step 3: wait for IPC DONE bit from ROM */
+ ret = snd_sof_dsp_read_poll_timeout(sdev, HDA_DSP_BAR, chip->ipc_ack, status,
+ ((status & chip->ipc_ack_mask) == chip->ipc_ack_mask),
+ HDA_DSP_REG_POLL_INTERVAL_US, HDA_DSP_INIT_TIMEOUT_US);
+ if (ret < 0) {
+ if (hda->boot_iteration == HDA_FW_BOOT_ATTEMPTS)
+ dev_err(sdev->dev, "timeout waiting for purge IPC done\n");
+ goto err;
+ }
+
+ /* set DONE bit to clear the reply IPC message */
+ snd_sof_dsp_update_bits_forced(sdev, HDA_DSP_BAR, chip->ipc_ack, chip->ipc_ack_mask,
+ chip->ipc_ack_mask);
+
+ /* step 4: enable interrupts */
+ ret = mtl_enable_interrupts(sdev, true);
+ if (ret < 0) {
+ if (hda->boot_iteration == HDA_FW_BOOT_ATTEMPTS)
+ dev_err(sdev->dev, "%s: failed to enable interrupts\n", __func__);
+ goto err;
+ }
+
+ mtl_enable_ipc_interrupts(sdev);
+
+ if (chip->rom_status_reg == MTL_DSP_ROM_STS) {
+ /*
+ * Workaround: when the ROM status register is pointing to
+ * the SRAM window (MTL_DSP_ROM_STS) the platform cannot catch
+ * ROM_INIT_DONE because of a very short timing window.
+ * Follow the recommendations and skip target state waiting.
+ */
+ return 0;
+ }
+
+ /*
+ * step 7:
+ * - Cold/Full boot: wait for ROM init to proceed to download the firmware
+ * - IMR boot: wait for ROM firmware entered (firmware booted up from IMR)
+ */
+ if (imr_boot)
+ target_status = FSR_STATE_FW_ENTERED;
+ else
+ target_status = FSR_STATE_INIT_DONE;
+
+ ret = snd_sof_dsp_read_poll_timeout(sdev, HDA_DSP_BAR,
+ chip->rom_status_reg, status,
+ (FSR_TO_STATE_CODE(status) == target_status),
+ HDA_DSP_REG_POLL_INTERVAL_US,
+ chip->rom_init_timeout *
+ USEC_PER_MSEC);
+
+ if (!ret)
+ return 0;
+
+ if (hda->boot_iteration == HDA_FW_BOOT_ATTEMPTS)
+ dev_err(sdev->dev,
+ "%s: timeout with rom_status_reg (%#x) read\n",
+ __func__, chip->rom_status_reg);
+
+err:
+ flags = SOF_DBG_DUMP_PCI | SOF_DBG_DUMP_MBOX | SOF_DBG_DUMP_OPTIONAL;
+
+ /* after max boot attempts make sure that the dump is printed */
+ if (hda->boot_iteration == HDA_FW_BOOT_ATTEMPTS)
+ flags &= ~SOF_DBG_DUMP_OPTIONAL;
+
+ dump_msg = kasprintf(GFP_KERNEL, "Boot iteration failed: %d/%d",
+ hda->boot_iteration, HDA_FW_BOOT_ATTEMPTS);
+ snd_sof_dsp_dbg_dump(sdev, dump_msg, flags);
+ mtl_enable_interrupts(sdev, false);
+ mtl_dsp_core_power_down(sdev, SOF_DSP_PRIMARY_CORE);
+
+ kfree(dump_msg);
+ return ret;
+}
+EXPORT_SYMBOL_NS(mtl_dsp_cl_init, "SND_SOC_SOF_INTEL_MTL");
+
+static irqreturn_t mtl_ipc_irq_thread(int irq, void *context)
+{
+ struct sof_ipc4_msg notification_data = {{ 0 }};
+ struct snd_sof_dev *sdev = context;
+ bool ack_received = false;
+ bool ipc_irq = false;
+ u32 hipcida;
+ u32 hipctdr;
+
+ hipcida = snd_sof_dsp_read(sdev, HDA_DSP_BAR, MTL_DSP_REG_HFIPCXIDA);
+ hipctdr = snd_sof_dsp_read(sdev, HDA_DSP_BAR, MTL_DSP_REG_HFIPCXTDR);
+
+ /* reply message from DSP */
+ if (hipcida & MTL_DSP_REG_HFIPCXIDA_DONE) {
+ /* DSP received the message */
+ snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, MTL_DSP_REG_HFIPCXCTL,
+ MTL_DSP_REG_HFIPCXCTL_DONE, 0);
+
+ mtl_ipc_dsp_done(sdev);
+
+ ipc_irq = true;
+ ack_received = true;
+ }
+
+ if (hipctdr & MTL_DSP_REG_HFIPCXTDR_BUSY) {
+ /* Message from DSP (reply or notification) */
+ u32 extension = snd_sof_dsp_read(sdev, HDA_DSP_BAR, MTL_DSP_REG_HFIPCXTDDY);
+ u32 primary = hipctdr & MTL_DSP_REG_HFIPCXTDR_MSG_MASK;
+
+ /*
+ * ACE fw sends a new fw ipc message to host to
+ * notify the status of the last host ipc message
+ */
+ if (primary & SOF_IPC4_MSG_DIR_MASK) {
+ /* Reply received */
+ if (likely(sdev->fw_state == SOF_FW_BOOT_COMPLETE)) {
+ struct sof_ipc4_msg *data = sdev->ipc->msg.reply_data;
+
+ data->primary = primary;
+ data->extension = extension;
+
+ spin_lock_irq(&sdev->ipc_lock);
+
+ snd_sof_ipc_get_reply(sdev);
+ mtl_ipc_host_done(sdev);
+ snd_sof_ipc_reply(sdev, data->primary);
+
+ spin_unlock_irq(&sdev->ipc_lock);
+ } else {
+ dev_dbg_ratelimited(sdev->dev,
+ "IPC reply before FW_READY: %#x|%#x\n",
+ primary, extension);
+ }
+ } else {
+ /* Notification received */
+ notification_data.primary = primary;
+ notification_data.extension = extension;
+
+ sdev->ipc->msg.rx_data = &notification_data;
+ snd_sof_ipc_msgs_rx(sdev);
+ sdev->ipc->msg.rx_data = NULL;
+
+ mtl_ipc_host_done(sdev);
+ }
+
+ ipc_irq = true;
+ }
+
+ if (!ipc_irq) {
+ /* This interrupt is not shared so no need to return IRQ_NONE. */
+ dev_dbg_ratelimited(sdev->dev, "nothing to do in IPC IRQ thread\n");
+ }
+
+ if (ack_received) {
+ struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata;
+
+ if (hdev->delayed_ipc_tx_msg)
+ mtl_ipc_send_msg(sdev, hdev->delayed_ipc_tx_msg);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int mtl_dsp_ipc_get_mailbox_offset(struct snd_sof_dev *sdev)
+{
+ return MTL_DSP_MBOX_UPLINK_OFFSET;
+}
+
+static int mtl_dsp_ipc_get_window_offset(struct snd_sof_dev *sdev, u32 id)
+{
+ return MTL_SRAM_WINDOW_OFFSET(id);
+}
+
+static void mtl_ipc_dump(struct snd_sof_dev *sdev)
+{
+ u32 hipcidr, hipcidd, hipcida, hipctdr, hipctdd, hipctda, hipcctl;
+
+ hipcidr = snd_sof_dsp_read(sdev, HDA_DSP_BAR, MTL_DSP_REG_HFIPCXIDR);
+ hipcidd = snd_sof_dsp_read(sdev, HDA_DSP_BAR, MTL_DSP_REG_HFIPCXIDDY);
+ hipcida = snd_sof_dsp_read(sdev, HDA_DSP_BAR, MTL_DSP_REG_HFIPCXIDA);
+ hipctdr = snd_sof_dsp_read(sdev, HDA_DSP_BAR, MTL_DSP_REG_HFIPCXTDR);
+ hipctdd = snd_sof_dsp_read(sdev, HDA_DSP_BAR, MTL_DSP_REG_HFIPCXTDDY);
+ hipctda = snd_sof_dsp_read(sdev, HDA_DSP_BAR, MTL_DSP_REG_HFIPCXTDA);
+ hipcctl = snd_sof_dsp_read(sdev, HDA_DSP_BAR, MTL_DSP_REG_HFIPCXCTL);
+
+ dev_err(sdev->dev,
+ "Host IPC initiator: %#x|%#x|%#x, target: %#x|%#x|%#x, ctl: %#x\n",
+ hipcidr, hipcidd, hipcida, hipctdr, hipctdd, hipctda, hipcctl);
+}
+
+static int mtl_dsp_disable_interrupts(struct snd_sof_dev *sdev)
+{
+ mtl_enable_sdw_irq(sdev, false);
+ mtl_disable_ipc_interrupts(sdev);
+ return mtl_enable_interrupts(sdev, false);
+}
+
+static int mtl_dsp_core_get(struct snd_sof_dev *sdev, int core)
+{
+ const struct sof_ipc_pm_ops *pm_ops = sdev->ipc->ops->pm;
+
+ if (core == SOF_DSP_PRIMARY_CORE)
+ return mtl_dsp_core_power_up(sdev, SOF_DSP_PRIMARY_CORE);
+
+ if (pm_ops->set_core_state)
+ return pm_ops->set_core_state(sdev, core, true);
+
+ return 0;
+}
+
+static int mtl_dsp_core_put(struct snd_sof_dev *sdev, int core)
+{
+ const struct sof_ipc_pm_ops *pm_ops = sdev->ipc->ops->pm;
+ int ret;
+
+ if (pm_ops->set_core_state) {
+ ret = pm_ops->set_core_state(sdev, core, false);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (core == SOF_DSP_PRIMARY_CORE)
+ return mtl_dsp_core_power_down(sdev, SOF_DSP_PRIMARY_CORE);
+
+ return 0;
+}
+
+int sof_mtl_set_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *dsp_ops)
+{
+ struct sof_ipc4_fw_data *ipc4_data;
+
+ /* common defaults */
+ memcpy(dsp_ops, &sof_hda_common_ops, sizeof(struct snd_sof_dsp_ops));
+
+ /* shutdown */
+ dsp_ops->shutdown = hda_dsp_shutdown;
+
+ /* doorbell */
+ dsp_ops->irq_thread = mtl_ipc_irq_thread;
+
+ /* ipc */
+ dsp_ops->send_msg = mtl_ipc_send_msg;
+ dsp_ops->get_mailbox_offset = mtl_dsp_ipc_get_mailbox_offset;
+ dsp_ops->get_window_offset = mtl_dsp_ipc_get_window_offset;
+
+ /* debug */
+ dsp_ops->debug_map = mtl_dsp_debugfs;
+ dsp_ops->debug_map_count = ARRAY_SIZE(mtl_dsp_debugfs);
+ dsp_ops->dbg_dump = mtl_dsp_dump;
+ dsp_ops->ipc_dump = mtl_ipc_dump;
+
+ /* pre/post fw run */
+ dsp_ops->pre_fw_run = mtl_dsp_pre_fw_run;
+ dsp_ops->post_fw_run = mtl_dsp_post_fw_run;
+
+ /* parse platform specific extended manifest */
+ dsp_ops->parse_platform_ext_manifest = NULL;
+
+ /* dsp core get/put */
+ dsp_ops->core_get = mtl_dsp_core_get;
+ dsp_ops->core_put = mtl_dsp_core_put;
+
+ sdev->private = kzalloc(sizeof(struct sof_ipc4_fw_data), GFP_KERNEL);
+ if (!sdev->private)
+ return -ENOMEM;
+
+ ipc4_data = sdev->private;
+ ipc4_data->manifest_fw_hdr_offset = SOF_MAN4_FW_HDR_OFFSET;
+
+ ipc4_data->mtrace_type = SOF_IPC4_MTRACE_INTEL_CAVS_2;
+
+ ipc4_data->fw_context_save = true;
+
+ /* External library loading support */
+ ipc4_data->load_library = hda_dsp_ipc4_load_library;
+
+ dsp_ops->set_power_state = hda_dsp_set_power_state_ipc4;
+
+ /* set DAI ops */
+ hda_set_dai_drv_ops(sdev, dsp_ops);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS(sof_mtl_set_ops, "SND_SOC_SOF_INTEL_MTL");
+
+const struct sof_intel_dsp_desc mtl_chip_info = {
+ .cores_num = 3,
+ .init_core_mask = BIT(0),
+ .host_managed_cores_mask = BIT(0),
+ .ipc_req = MTL_DSP_REG_HFIPCXIDR,
+ .ipc_req_mask = MTL_DSP_REG_HFIPCXIDR_BUSY,
+ .ipc_ack = MTL_DSP_REG_HFIPCXIDA,
+ .ipc_ack_mask = MTL_DSP_REG_HFIPCXIDA_DONE,
+ .ipc_ctl = MTL_DSP_REG_HFIPCXCTL,
+ .rom_status_reg = MTL_DSP_REG_HFFLGPXQWY,
+ .rom_init_timeout = 300,
+ .ssp_count = MTL_SSP_COUNT,
+ .ssp_base_offset = CNL_SSP_BASE_OFFSET,
+ .sdw_shim_base = SDW_SHIM_BASE_ACE,
+ .sdw_alh_base = SDW_ALH_BASE_ACE,
+ .d0i3_offset = MTL_HDA_VS_D0I3C,
+ .read_sdw_lcount = hda_sdw_check_lcount_common,
+ .enable_sdw_irq = mtl_enable_sdw_irq,
+ .check_sdw_irq = mtl_dsp_check_sdw_irq,
+ .check_sdw_wakeen_irq = hda_sdw_check_wakeen_irq_common,
+ .sdw_process_wakeen = hda_sdw_process_wakeen_common,
+ .check_ipc_irq = mtl_dsp_check_ipc_irq,
+ .cl_init = mtl_dsp_cl_init,
+ .power_down_dsp = mtl_power_down_dsp,
+ .disable_interrupts = mtl_dsp_disable_interrupts,
+ .hw_ip_version = SOF_INTEL_ACE_1_0,
+};
+
+const struct sof_intel_dsp_desc arl_s_chip_info = {
+ .cores_num = 2,
+ .init_core_mask = BIT(0),
+ .host_managed_cores_mask = BIT(0),
+ .ipc_req = MTL_DSP_REG_HFIPCXIDR,
+ .ipc_req_mask = MTL_DSP_REG_HFIPCXIDR_BUSY,
+ .ipc_ack = MTL_DSP_REG_HFIPCXIDA,
+ .ipc_ack_mask = MTL_DSP_REG_HFIPCXIDA_DONE,
+ .ipc_ctl = MTL_DSP_REG_HFIPCXCTL,
+ .rom_status_reg = MTL_DSP_REG_HFFLGPXQWY,
+ .rom_init_timeout = 300,
+ .ssp_count = MTL_SSP_COUNT,
+ .ssp_base_offset = CNL_SSP_BASE_OFFSET,
+ .sdw_shim_base = SDW_SHIM_BASE_ACE,
+ .sdw_alh_base = SDW_ALH_BASE_ACE,
+ .d0i3_offset = MTL_HDA_VS_D0I3C,
+ .read_sdw_lcount = hda_sdw_check_lcount_common,
+ .enable_sdw_irq = mtl_enable_sdw_irq,
+ .check_sdw_irq = mtl_dsp_check_sdw_irq,
+ .check_sdw_wakeen_irq = hda_sdw_check_wakeen_irq_common,
+ .sdw_process_wakeen = hda_sdw_process_wakeen_common,
+ .check_ipc_irq = mtl_dsp_check_ipc_irq,
+ .cl_init = mtl_dsp_cl_init,
+ .power_down_dsp = mtl_power_down_dsp,
+ .disable_interrupts = mtl_dsp_disable_interrupts,
+ .hw_ip_version = SOF_INTEL_ACE_1_0,
+};
diff --git a/sound/soc/sof/intel/mtl.h b/sound/soc/sof/intel/mtl.h
new file mode 100644
index 000000000000..e01a1536709e
--- /dev/null
+++ b/sound/soc/sof/intel/mtl.h
@@ -0,0 +1,134 @@
+/* SPDX-License-Identifier: (GPL-2.0-only 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) 2020-2022 Intel Corporation
+ */
+
+/* DSP Registers */
+#define MTL_HFDSSCS 0x1000
+#define MTL_HFDSSCS_SPA_MASK BIT(16)
+#define MTL_HFDSSCS_CPA_MASK BIT(24)
+#define MTL_HFSNDWIE 0x114C
+#define MTL_HFPWRCTL 0x1D18
+#define PTL_HFPWRCTL2 0x1D20
+#define MTL_HfPWRCTL_WPIOXPG(x) BIT((x) + 8)
+#define MTL_HFPWRCTL_WPDSPHPXPG BIT(0)
+#define MTL_HFPWRSTS 0x1D1C
+#define PTL_HFPWRSTS2 0x1D24
+#define MTL_HFPWRSTS_DSPHPXPGS_MASK BIT(0)
+#define MTL_HFINTIPPTR 0x1108
+#define MTL_IRQ_INTEN_L_HOST_IPC_MASK BIT(0)
+#define MTL_IRQ_INTEN_L_SOUNDWIRE_MASK BIT(6)
+#define MTL_HFINTIPPTR_PTR_MASK GENMASK(20, 0)
+
+#define MTL_HDA_VS_D0I3C 0x1D4A
+
+#define MTL_DSP2CXCAP_PRIMARY_CORE 0x178D00
+#define MTL_DSP2CXCTL_PRIMARY_CORE 0x178D04
+#define MTL_DSP2CXCTL_PRIMARY_CORE_SPA_MASK BIT(0)
+#define MTL_DSP2CXCTL_PRIMARY_CORE_CPA_MASK BIT(8)
+#define MTL_DSP2CXCTL_PRIMARY_CORE_OSEL GENMASK(25, 24)
+#define MTL_DSP2CXCTL_PRIMARY_CORE_OSEL_SHIFT 24
+
+/* IPC Registers */
+#define MTL_DSP_REG_HFIPCXTDR 0x73200
+#define MTL_DSP_REG_HFIPCXTDR_BUSY BIT(31)
+#define MTL_DSP_REG_HFIPCXTDR_MSG_MASK GENMASK(30, 0)
+#define MTL_DSP_REG_HFIPCXTDA 0x73204
+#define MTL_DSP_REG_HFIPCXTDA_BUSY BIT(31)
+#define MTL_DSP_REG_HFIPCXIDR 0x73210
+#define MTL_DSP_REG_HFIPCXIDR_BUSY BIT(31)
+#define MTL_DSP_REG_HFIPCXIDR_MSG_MASK GENMASK(30, 0)
+#define MTL_DSP_REG_HFIPCXIDA 0x73214
+#define MTL_DSP_REG_HFIPCXIDA_DONE BIT(31)
+#define MTL_DSP_REG_HFIPCXIDA_MSG_MASK GENMASK(30, 0)
+#define MTL_DSP_REG_HFIPCXCTL 0x73228
+#define MTL_DSP_REG_HFIPCXCTL_BUSY BIT(0)
+#define MTL_DSP_REG_HFIPCXCTL_DONE BIT(1)
+#define MTL_DSP_REG_HFIPCXTDDY 0x73300
+#define MTL_DSP_REG_HFIPCXIDDY 0x73380
+#define MTL_DSP_REG_HfHIPCIE 0x1140
+#define MTL_DSP_REG_HfHIPCIE_IE_MASK BIT(0)
+#define MTL_DSP_REG_HfSNDWIE 0x114C
+#define MTL_DSP_REG_HfSNDWIE_IE_MASK GENMASK(3, 0)
+
+#define MTL_DSP_IRQSTS 0x20
+#define MTL_DSP_IRQSTS_IPC BIT(0)
+#define MTL_DSP_IRQSTS_SDW BIT(6)
+
+#define MTL_DSP_REG_POLL_INTERVAL_US 10 /* 10 us */
+
+/* Memory windows */
+#define MTL_SRAM_WINDOW_OFFSET(x) (0x180000 + 0x8000 * (x))
+
+#define MTL_DSP_MBOX_UPLINK_OFFSET (MTL_SRAM_WINDOW_OFFSET(0) + 0x1000)
+#define MTL_DSP_MBOX_UPLINK_SIZE 0x1000
+#define MTL_DSP_MBOX_DOWNLINK_OFFSET MTL_SRAM_WINDOW_OFFSET(1)
+#define MTL_DSP_MBOX_DOWNLINK_SIZE 0x1000
+
+/* FW registers */
+#define MTL_DSP_ROM_STS MTL_SRAM_WINDOW_OFFSET(0) /* ROM status */
+#define MTL_DSP_ROM_ERROR (MTL_SRAM_WINDOW_OFFSET(0) + 0x4) /* ROM error code */
+
+#define MTL_DSP_REG_HFFLGPXQWY 0x163200 /* DSP core0 status */
+#define MTL_DSP_REG_HFFLGPXQWY_ERROR 0x163204 /* DSP core0 error */
+
+/* FSR status codes */
+#define FSR_STATE_ROM_RESET_VECTOR_DONE 0x8
+#define FSR_STATE_ROM_PURGE_BOOT 0x9
+#define FSR_STATE_ROM_RESTORE_BOOT 0xA
+#define FSR_STATE_ROM_FW_ENTRY_POINT 0xB
+#define FSR_STATE_ROM_VALIDATE_PUB_KEY 0xC
+#define FSR_STATE_ROM_POWER_DOWN_HPSRAM 0xD
+#define FSR_STATE_ROM_POWER_DOWN_ULPSRAM 0xE
+#define FSR_STATE_ROM_POWER_UP_ULPSRAM_STACK 0xF
+#define FSR_STATE_ROM_POWER_UP_HPSRAM_DMA 0x10
+#define FSR_STATE_ROM_BEFORE_EP_POINTER_READ 0x11
+#define FSR_STATE_ROM_VALIDATE_MANIFEST 0x12
+#define FSR_STATE_ROM_VALIDATE_FW_MODULE 0x13
+#define FSR_STATE_ROM_PROTECT_IMR_REGION 0x14
+#define FSR_STATE_ROM_PUSH_MODEL_ROUTINE 0x15
+#define FSR_STATE_ROM_PULL_MODEL_ROUTINE 0x16
+#define FSR_STATE_ROM_VALIDATE_PKG_DIR 0x17
+#define FSR_STATE_ROM_VALIDATE_CPD 0x18
+#define FSR_STATE_ROM_VALIDATE_CSS_MAN_HEADER 0x19
+#define FSR_STATE_ROM_VALIDATE_BLOB_SVN 0x1A
+#define FSR_STATE_ROM_VERIFY_IFWI_PARTITION 0x1B
+#define FSR_STATE_ROM_REMOVE_ACCESS_CONTROL 0x1C
+#define FSR_STATE_ROM_AUTH_BYPASS 0x1D
+#define FSR_STATE_ROM_AUTH_ENABLED 0x1E
+#define FSR_STATE_ROM_INIT_DMA 0x1F
+#define FSR_STATE_ROM_PURGE_FW_ENTRY 0x20
+#define FSR_STATE_ROM_PURGE_FW_END 0x21
+#define FSR_STATE_ROM_CLEAN_UP_BSS_DONE 0x22
+#define FSR_STATE_ROM_IMR_RESTORE_ENTRY 0x23
+#define FSR_STATE_ROM_IMR_RESTORE_END 0x24
+#define FSR_STATE_ROM_FW_MANIFEST_IN_DMA_BUFF 0x25
+#define FSR_STATE_ROM_LOAD_CSE_MAN_TO_IMR 0x26
+#define FSR_STATE_ROM_LOAD_FW_MAN_TO_IMR 0x27
+#define FSR_STATE_ROM_LOAD_FW_CODE_TO_IMR 0x28
+#define FSR_STATE_ROM_FW_LOADING_DONE 0x29
+#define FSR_STATE_ROM_FW_CODE_LOADED 0x2A
+#define FSR_STATE_ROM_VERIFY_IMAGE_TYPE 0x2B
+#define FSR_STATE_ROM_AUTH_API_INIT 0x2C
+#define FSR_STATE_ROM_AUTH_API_PROC 0x2D
+#define FSR_STATE_ROM_AUTH_API_FIRST_BUSY 0x2E
+#define FSR_STATE_ROM_AUTH_API_FIRST_RESULT 0x2F
+#define FSR_STATE_ROM_AUTH_API_CLEANUP 0x30
+
+#define MTL_DSP_REG_HfIMRIS1 0x162088
+#define MTL_DSP_REG_HfIMRIS1_IU_MASK BIT(0)
+
+bool mtl_dsp_check_ipc_irq(struct snd_sof_dev *sdev);
+
+void mtl_enable_ipc_interrupts(struct snd_sof_dev *sdev);
+void mtl_disable_ipc_interrupts(struct snd_sof_dev *sdev);
+
+int mtl_enable_interrupts(struct snd_sof_dev *sdev, bool enable);
+
+int mtl_power_down_dsp(struct snd_sof_dev *sdev);
+int mtl_dsp_cl_init(struct snd_sof_dev *sdev, int stream_tag, bool imr_boot);
+
+int sof_mtl_set_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *dsp_ops);
diff --git a/sound/soc/sof/intel/pci-apl.c b/sound/soc/sof/intel/pci-apl.c
new file mode 100644
index 000000000000..0bf7ee753bc3
--- /dev/null
+++ b/sound/soc/sof/intel/pci-apl.c
@@ -0,0 +1,111 @@
+// SPDX-License-Identifier: (GPL-2.0-only 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) 2018-2021 Intel Corporation
+//
+// Author: Liam Girdwood <liam.r.girdwood@linux.intel.com>
+//
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <sound/soc-acpi.h>
+#include <sound/soc-acpi-intel-match.h>
+#include <sound/sof.h>
+#include "../ops.h"
+#include "../sof-pci-dev.h"
+
+/* platform specific devices */
+#include "hda.h"
+
+static const struct sof_dev_desc bxt_desc = {
+ .machines = snd_soc_acpi_intel_bxt_machines,
+ .use_acpi_target_states = true,
+ .resindex_lpe_base = 0,
+ .resindex_pcicfg_base = -1,
+ .resindex_imr_base = -1,
+ .irqindex_host_ipc = -1,
+ .chip_info = &apl_chip_info,
+ .ipc_supported_mask = BIT(SOF_IPC_TYPE_3) | BIT(SOF_IPC_TYPE_4),
+ .ipc_default = SOF_IPC_TYPE_3,
+ .dspless_mode_supported = true, /* Only supported for HDaudio */
+ .default_fw_path = {
+ [SOF_IPC_TYPE_3] = "intel/sof",
+ [SOF_IPC_TYPE_4] = "intel/avs/apl",
+ },
+ .default_lib_path = {
+ [SOF_IPC_TYPE_4] = "intel/avs-lib/apl",
+ },
+ .default_tplg_path = {
+ [SOF_IPC_TYPE_3] = "intel/sof-tplg",
+ [SOF_IPC_TYPE_4] = "intel/avs-tplg",
+ },
+ .default_fw_filename = {
+ [SOF_IPC_TYPE_3] = "sof-apl.ri",
+ [SOF_IPC_TYPE_4] = "dsp_basefw.bin",
+ },
+ .nocodec_tplg_filename = "sof-apl-nocodec.tplg",
+ .ops = &sof_apl_ops,
+ .ops_init = sof_apl_ops_init,
+ .ops_free = hda_ops_free,
+};
+
+static const struct sof_dev_desc glk_desc = {
+ .machines = snd_soc_acpi_intel_glk_machines,
+ .use_acpi_target_states = true,
+ .resindex_lpe_base = 0,
+ .resindex_pcicfg_base = -1,
+ .resindex_imr_base = -1,
+ .irqindex_host_ipc = -1,
+ .chip_info = &apl_chip_info,
+ .ipc_supported_mask = BIT(SOF_IPC_TYPE_3) | BIT(SOF_IPC_TYPE_4),
+ .ipc_default = SOF_IPC_TYPE_3,
+ .dspless_mode_supported = true, /* Only supported for HDaudio */
+ .default_fw_path = {
+ [SOF_IPC_TYPE_3] = "intel/sof",
+ [SOF_IPC_TYPE_4] = "intel/avs/glk",
+ },
+ .default_lib_path = {
+ [SOF_IPC_TYPE_4] = "intel/avs-lib/glk",
+ },
+ .default_tplg_path = {
+ [SOF_IPC_TYPE_3] = "intel/sof-tplg",
+ [SOF_IPC_TYPE_4] = "intel/avs-tplg",
+ },
+ .default_fw_filename = {
+ [SOF_IPC_TYPE_3] = "sof-glk.ri",
+ [SOF_IPC_TYPE_4] = "dsp_basefw.bin",
+ },
+ .nocodec_tplg_filename = "sof-glk-nocodec.tplg",
+ .ops = &sof_apl_ops,
+ .ops_init = sof_apl_ops_init,
+ .ops_free = hda_ops_free,
+};
+
+/* PCI IDs */
+static const struct pci_device_id sof_pci_ids[] = {
+ { PCI_DEVICE_DATA(INTEL, HDA_APL, &bxt_desc) },
+ { PCI_DEVICE_DATA(INTEL, HDA_GML, &glk_desc) },
+ { 0, }
+};
+MODULE_DEVICE_TABLE(pci, sof_pci_ids);
+
+/* pci_driver definition */
+static struct pci_driver snd_sof_pci_intel_apl_driver = {
+ .name = "sof-audio-pci-intel-apl",
+ .id_table = sof_pci_ids,
+ .probe = hda_pci_intel_probe,
+ .remove = sof_pci_remove,
+ .shutdown = sof_pci_shutdown,
+ .driver = {
+ .pm = pm_ptr(&sof_pci_pm),
+ },
+};
+module_pci_driver(snd_sof_pci_intel_apl_driver);
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_DESCRIPTION("SOF support for ApolloLake platforms");
+MODULE_IMPORT_NS("SND_SOC_SOF_INTEL_HDA_GENERIC");
+MODULE_IMPORT_NS("SND_SOC_SOF_INTEL_HDA_COMMON");
+MODULE_IMPORT_NS("SND_SOC_SOF_PCI_DEV");
diff --git a/sound/soc/sof/intel/pci-cnl.c b/sound/soc/sof/intel/pci-cnl.c
new file mode 100644
index 000000000000..de48640024e4
--- /dev/null
+++ b/sound/soc/sof/intel/pci-cnl.c
@@ -0,0 +1,149 @@
+// SPDX-License-Identifier: (GPL-2.0-only 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) 2018 Intel Corporation
+//
+// Author: Liam Girdwood <liam.r.girdwood@linux.intel.com>
+//
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <sound/soc-acpi.h>
+#include <sound/soc-acpi-intel-match.h>
+#include <sound/sof.h>
+#include "../ops.h"
+#include "../sof-pci-dev.h"
+
+/* platform specific devices */
+#include "hda.h"
+
+static const struct sof_dev_desc cnl_desc = {
+ .machines = snd_soc_acpi_intel_cnl_machines,
+ .alt_machines = snd_soc_acpi_intel_cnl_sdw_machines,
+ .use_acpi_target_states = true,
+ .resindex_lpe_base = 0,
+ .resindex_pcicfg_base = -1,
+ .resindex_imr_base = -1,
+ .irqindex_host_ipc = -1,
+ .chip_info = &cnl_chip_info,
+ .ipc_supported_mask = BIT(SOF_IPC_TYPE_3) | BIT(SOF_IPC_TYPE_4),
+ .ipc_default = SOF_IPC_TYPE_3,
+ .dspless_mode_supported = true, /* Only supported for HDaudio */
+ .default_fw_path = {
+ [SOF_IPC_TYPE_3] = "intel/sof",
+ [SOF_IPC_TYPE_4] = "intel/avs/cnl",
+ },
+ .default_lib_path = {
+ [SOF_IPC_TYPE_4] = "intel/avs-lib/cnl",
+ },
+ .default_tplg_path = {
+ [SOF_IPC_TYPE_3] = "intel/sof-tplg",
+ [SOF_IPC_TYPE_4] = "intel/avs-tplg",
+ },
+ .default_fw_filename = {
+ [SOF_IPC_TYPE_3] = "sof-cnl.ri",
+ [SOF_IPC_TYPE_4] = "dsp_basefw.bin",
+ },
+ .nocodec_tplg_filename = "sof-cnl-nocodec.tplg",
+ .ops = &sof_cnl_ops,
+ .ops_init = sof_cnl_ops_init,
+ .ops_free = hda_ops_free,
+};
+
+static const struct sof_dev_desc cfl_desc = {
+ .machines = snd_soc_acpi_intel_cfl_machines,
+ .alt_machines = snd_soc_acpi_intel_cfl_sdw_machines,
+ .use_acpi_target_states = true,
+ .resindex_lpe_base = 0,
+ .resindex_pcicfg_base = -1,
+ .resindex_imr_base = -1,
+ .irqindex_host_ipc = -1,
+ .chip_info = &cnl_chip_info,
+ .ipc_supported_mask = BIT(SOF_IPC_TYPE_3) | BIT(SOF_IPC_TYPE_4),
+ .ipc_default = SOF_IPC_TYPE_3,
+ .dspless_mode_supported = true, /* Only supported for HDaudio */
+ .default_fw_path = {
+ [SOF_IPC_TYPE_3] = "intel/sof",
+ [SOF_IPC_TYPE_4] = "intel/avs/cnl",
+ },
+ .default_lib_path = {
+ [SOF_IPC_TYPE_4] = "intel/avs-lib/cnl",
+ },
+ .default_tplg_path = {
+ [SOF_IPC_TYPE_3] = "intel/sof-tplg",
+ [SOF_IPC_TYPE_4] = "intel/avs-tplg",
+ },
+ .default_fw_filename = {
+ [SOF_IPC_TYPE_3] = "sof-cfl.ri",
+ [SOF_IPC_TYPE_4] = "dsp_basefw.bin",
+ },
+ .nocodec_tplg_filename = "sof-cnl-nocodec.tplg",
+ .ops = &sof_cnl_ops,
+ .ops_init = sof_cnl_ops_init,
+ .ops_free = hda_ops_free,
+};
+
+static const struct sof_dev_desc cml_desc = {
+ .machines = snd_soc_acpi_intel_cml_machines,
+ .alt_machines = snd_soc_acpi_intel_cml_sdw_machines,
+ .use_acpi_target_states = true,
+ .resindex_lpe_base = 0,
+ .resindex_pcicfg_base = -1,
+ .resindex_imr_base = -1,
+ .irqindex_host_ipc = -1,
+ .chip_info = &cnl_chip_info,
+ .ipc_supported_mask = BIT(SOF_IPC_TYPE_3) | BIT(SOF_IPC_TYPE_4),
+ .ipc_default = SOF_IPC_TYPE_3,
+ .dspless_mode_supported = true, /* Only supported for HDaudio */
+ .default_fw_path = {
+ [SOF_IPC_TYPE_3] = "intel/sof",
+ [SOF_IPC_TYPE_4] = "intel/avs/cnl",
+ },
+ .default_lib_path = {
+ [SOF_IPC_TYPE_4] = "intel/avs-lib/cnl",
+ },
+ .default_tplg_path = {
+ [SOF_IPC_TYPE_3] = "intel/sof-tplg",
+ [SOF_IPC_TYPE_4] = "intel/avs-tplg",
+ },
+ .default_fw_filename = {
+ [SOF_IPC_TYPE_3] = "sof-cml.ri",
+ [SOF_IPC_TYPE_4] = "dsp_basefw.bin",
+ },
+ .nocodec_tplg_filename = "sof-cnl-nocodec.tplg",
+ .ops = &sof_cnl_ops,
+ .ops_init = sof_cnl_ops_init,
+ .ops_free = hda_ops_free,
+};
+
+/* PCI IDs */
+static const struct pci_device_id sof_pci_ids[] = {
+ { PCI_DEVICE_DATA(INTEL, HDA_CNL_LP, &cnl_desc) },
+ { PCI_DEVICE_DATA(INTEL, HDA_CNL_H, &cfl_desc) },
+ { PCI_DEVICE_DATA(INTEL, HDA_CML_LP, &cml_desc) },
+ { PCI_DEVICE_DATA(INTEL, HDA_CML_H, &cml_desc) },
+ { PCI_DEVICE_DATA(INTEL, HDA_CML_S, &cml_desc) },
+ { 0, }
+};
+MODULE_DEVICE_TABLE(pci, sof_pci_ids);
+
+/* pci_driver definition */
+static struct pci_driver snd_sof_pci_intel_cnl_driver = {
+ .name = "sof-audio-pci-intel-cnl",
+ .id_table = sof_pci_ids,
+ .probe = hda_pci_intel_probe,
+ .remove = sof_pci_remove,
+ .shutdown = sof_pci_shutdown,
+ .driver = {
+ .pm = pm_ptr(&sof_pci_pm),
+ },
+};
+module_pci_driver(snd_sof_pci_intel_cnl_driver);
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_DESCRIPTION("SOF support for CannonLake platforms");
+MODULE_IMPORT_NS("SND_SOC_SOF_INTEL_HDA_GENERIC");
+MODULE_IMPORT_NS("SND_SOC_SOF_INTEL_HDA_COMMON");
+MODULE_IMPORT_NS("SND_SOC_SOF_PCI_DEV");
diff --git a/sound/soc/sof/intel/pci-icl.c b/sound/soc/sof/intel/pci-icl.c
new file mode 100644
index 000000000000..fd219e654844
--- /dev/null
+++ b/sound/soc/sof/intel/pci-icl.c
@@ -0,0 +1,115 @@
+// SPDX-License-Identifier: (GPL-2.0-only 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) 2018-2021 Intel Corporation
+//
+// Author: Liam Girdwood <liam.r.girdwood@linux.intel.com>
+//
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <sound/soc-acpi.h>
+#include <sound/soc-acpi-intel-match.h>
+#include <sound/sof.h>
+#include "../ops.h"
+#include "../sof-pci-dev.h"
+
+/* platform specific devices */
+#include "hda.h"
+
+static const struct sof_dev_desc icl_desc = {
+ .machines = snd_soc_acpi_intel_icl_machines,
+ .alt_machines = snd_soc_acpi_intel_icl_sdw_machines,
+ .use_acpi_target_states = true,
+ .resindex_lpe_base = 0,
+ .resindex_pcicfg_base = -1,
+ .resindex_imr_base = -1,
+ .irqindex_host_ipc = -1,
+ .chip_info = &icl_chip_info,
+ .ipc_supported_mask = BIT(SOF_IPC_TYPE_3) | BIT(SOF_IPC_TYPE_4),
+ .ipc_default = SOF_IPC_TYPE_3,
+ .dspless_mode_supported = true, /* Only supported for HDaudio */
+ .default_fw_path = {
+ [SOF_IPC_TYPE_3] = "intel/sof",
+ [SOF_IPC_TYPE_4] = "intel/avs/icl",
+ },
+ .default_lib_path = {
+ [SOF_IPC_TYPE_4] = "intel/avs-lib/icl",
+ },
+ .default_tplg_path = {
+ [SOF_IPC_TYPE_3] = "intel/sof-tplg",
+ [SOF_IPC_TYPE_4] = "intel/avs-tplg",
+ },
+ .default_fw_filename = {
+ [SOF_IPC_TYPE_3] = "sof-icl.ri",
+ [SOF_IPC_TYPE_4] = "dsp_basefw.bin",
+ },
+ .nocodec_tplg_filename = "sof-icl-nocodec.tplg",
+ .ops = &sof_icl_ops,
+ .ops_init = sof_icl_ops_init,
+ .ops_free = hda_ops_free,
+};
+
+static const struct sof_dev_desc jsl_desc = {
+ .machines = snd_soc_acpi_intel_jsl_machines,
+ .use_acpi_target_states = true,
+ .resindex_lpe_base = 0,
+ .resindex_pcicfg_base = -1,
+ .resindex_imr_base = -1,
+ .irqindex_host_ipc = -1,
+ .chip_info = &jsl_chip_info,
+ .ipc_supported_mask = BIT(SOF_IPC_TYPE_3) | BIT(SOF_IPC_TYPE_4),
+ .ipc_default = SOF_IPC_TYPE_3,
+ .dspless_mode_supported = true, /* Only supported for HDaudio */
+ .default_fw_path = {
+ [SOF_IPC_TYPE_3] = "intel/sof",
+ [SOF_IPC_TYPE_4] = "intel/avs/jsl",
+ },
+ .default_lib_path = {
+ [SOF_IPC_TYPE_4] = "intel/avs-lib/jsl",
+ },
+ .default_tplg_path = {
+ [SOF_IPC_TYPE_3] = "intel/sof-tplg",
+ [SOF_IPC_TYPE_4] = "intel/avs-tplg",
+ },
+ .default_fw_filename = {
+ [SOF_IPC_TYPE_3] = "sof-jsl.ri",
+ [SOF_IPC_TYPE_4] = "dsp_basefw.bin",
+ },
+ .nocodec_tplg_filename = "sof-jsl-nocodec.tplg",
+ .ops = &sof_cnl_ops,
+ .ops_init = sof_cnl_ops_init,
+ .ops_free = hda_ops_free,
+};
+
+/* PCI IDs */
+static const struct pci_device_id sof_pci_ids[] = {
+ { PCI_DEVICE_DATA(INTEL, HDA_ICL_LP, &icl_desc) },
+ { PCI_DEVICE_DATA(INTEL, HDA_ICL_H, &icl_desc) },
+ { PCI_DEVICE_DATA(INTEL, HDA_ICL_N, &jsl_desc) },
+ { PCI_DEVICE_DATA(INTEL, HDA_JSL_N, &jsl_desc) },
+ { 0, }
+};
+MODULE_DEVICE_TABLE(pci, sof_pci_ids);
+
+/* pci_driver definition */
+static struct pci_driver snd_sof_pci_intel_icl_driver = {
+ .name = "sof-audio-pci-intel-icl",
+ .id_table = sof_pci_ids,
+ .probe = hda_pci_intel_probe,
+ .remove = sof_pci_remove,
+ .shutdown = sof_pci_shutdown,
+ .driver = {
+ .pm = pm_ptr(&sof_pci_pm),
+ },
+};
+module_pci_driver(snd_sof_pci_intel_icl_driver);
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_DESCRIPTION("SOF support for IceLake platforms");
+MODULE_IMPORT_NS("SND_SOC_SOF_INTEL_HDA_GENERIC");
+MODULE_IMPORT_NS("SND_SOC_SOF_INTEL_HDA_COMMON");
+MODULE_IMPORT_NS("SND_SOC_SOF_INTEL_CNL");
+MODULE_IMPORT_NS("SND_SOC_SOF_PCI_DEV");
diff --git a/sound/soc/sof/intel/pci-lnl.c b/sound/soc/sof/intel/pci-lnl.c
new file mode 100644
index 000000000000..ae379c23f008
--- /dev/null
+++ b/sound/soc/sof/intel/pci-lnl.c
@@ -0,0 +1,84 @@
+// SPDX-License-Identifier: (GPL-2.0-only 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) 2023 Intel Corporation
+//
+// Author: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
+//
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <sound/soc-acpi.h>
+#include <sound/soc-acpi-intel-match.h>
+#include <sound/sof.h>
+#include "../ops.h"
+#include "../sof-pci-dev.h"
+
+/* platform specific devices */
+#include "hda.h"
+#include "lnl.h"
+
+/* LunarLake ops */
+static struct snd_sof_dsp_ops sof_lnl_ops;
+
+static int sof_lnl_ops_init(struct snd_sof_dev *sdev)
+{
+ return sof_lnl_set_ops(sdev, &sof_lnl_ops);
+}
+
+static const struct sof_dev_desc lnl_desc = {
+ .use_acpi_target_states = true,
+ .machines = snd_soc_acpi_intel_lnl_machines,
+ .alt_machines = snd_soc_acpi_intel_lnl_sdw_machines,
+ .resindex_lpe_base = 0,
+ .resindex_pcicfg_base = -1,
+ .resindex_imr_base = -1,
+ .irqindex_host_ipc = -1,
+ .chip_info = &lnl_chip_info,
+ .ipc_supported_mask = BIT(SOF_IPC_TYPE_4),
+ .ipc_default = SOF_IPC_TYPE_4,
+ .dspless_mode_supported = true,
+ .default_fw_path = {
+ [SOF_IPC_TYPE_4] = "intel/sof-ipc4/lnl",
+ },
+ .default_lib_path = {
+ [SOF_IPC_TYPE_4] = "intel/sof-ipc4-lib/lnl",
+ },
+ .default_tplg_path = {
+ [SOF_IPC_TYPE_4] = "intel/sof-ipc4-tplg",
+ },
+ .default_fw_filename = {
+ [SOF_IPC_TYPE_4] = "sof-lnl.ri",
+ },
+ .nocodec_tplg_filename = "sof-lnl-nocodec.tplg",
+ .ops = &sof_lnl_ops,
+ .ops_init = sof_lnl_ops_init,
+};
+
+/* PCI IDs */
+static const struct pci_device_id sof_pci_ids[] = {
+ { PCI_DEVICE_DATA(INTEL, HDA_LNL_P, &lnl_desc) }, /* LNL-P */
+ { 0, }
+};
+MODULE_DEVICE_TABLE(pci, sof_pci_ids);
+
+/* pci_driver definition */
+static struct pci_driver snd_sof_pci_intel_lnl_driver = {
+ .name = "sof-audio-pci-intel-lnl",
+ .id_table = sof_pci_ids,
+ .probe = hda_pci_intel_probe,
+ .remove = sof_pci_remove,
+ .shutdown = sof_pci_shutdown,
+ .driver = {
+ .pm = pm_ptr(&sof_pci_pm),
+ },
+};
+module_pci_driver(snd_sof_pci_intel_lnl_driver);
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_DESCRIPTION("SOF support for LunarLake platforms");
+MODULE_IMPORT_NS("SND_SOC_SOF_INTEL_HDA_GENERIC");
+MODULE_IMPORT_NS("SND_SOC_SOF_INTEL_HDA_COMMON");
+MODULE_IMPORT_NS("SND_SOC_SOF_PCI_DEV");
diff --git a/sound/soc/sof/intel/pci-mtl.c b/sound/soc/sof/intel/pci-mtl.c
new file mode 100644
index 000000000000..7b2533999195
--- /dev/null
+++ b/sound/soc/sof/intel/pci-mtl.c
@@ -0,0 +1,147 @@
+// SPDX-License-Identifier: (GPL-2.0-only 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) 2018-2022 Intel Corporation
+//
+// Author: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
+//
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <sound/soc-acpi.h>
+#include <sound/soc-acpi-intel-match.h>
+#include <sound/sof.h>
+#include "../ops.h"
+#include "../sof-pci-dev.h"
+
+/* platform specific devices */
+#include "hda.h"
+#include "mtl.h"
+
+/* Meteorlake ops */
+static struct snd_sof_dsp_ops sof_mtl_ops;
+
+static int sof_mtl_ops_init(struct snd_sof_dev *sdev)
+{
+ return sof_mtl_set_ops(sdev, &sof_mtl_ops);
+}
+
+static const struct sof_dev_desc mtl_desc = {
+ .use_acpi_target_states = true,
+ .machines = snd_soc_acpi_intel_mtl_machines,
+ .alt_machines = snd_soc_acpi_intel_mtl_sdw_machines,
+ .resindex_lpe_base = 0,
+ .resindex_pcicfg_base = -1,
+ .resindex_imr_base = -1,
+ .irqindex_host_ipc = -1,
+ .chip_info = &mtl_chip_info,
+ .ipc_supported_mask = BIT(SOF_IPC_TYPE_4),
+ .ipc_default = SOF_IPC_TYPE_4,
+ .dspless_mode_supported = true, /* Only supported for HDaudio */
+ .default_fw_path = {
+ [SOF_IPC_TYPE_4] = "intel/sof-ipc4/mtl",
+ },
+ .default_lib_path = {
+ [SOF_IPC_TYPE_4] = "intel/sof-ipc4-lib/mtl",
+ },
+ .default_tplg_path = {
+ [SOF_IPC_TYPE_4] = "intel/sof-ace-tplg",
+ },
+ .default_fw_filename = {
+ [SOF_IPC_TYPE_4] = "sof-mtl.ri",
+ },
+ .nocodec_tplg_filename = "sof-mtl-nocodec.tplg",
+ .ops = &sof_mtl_ops,
+ .ops_init = sof_mtl_ops_init,
+ .ops_free = hda_ops_free,
+};
+
+static const struct sof_dev_desc arl_desc = {
+ .use_acpi_target_states = true,
+ .machines = snd_soc_acpi_intel_arl_machines,
+ .alt_machines = snd_soc_acpi_intel_arl_sdw_machines,
+ .resindex_lpe_base = 0,
+ .resindex_pcicfg_base = -1,
+ .resindex_imr_base = -1,
+ .irqindex_host_ipc = -1,
+ .chip_info = &mtl_chip_info,
+ .ipc_supported_mask = BIT(SOF_IPC_TYPE_4),
+ .ipc_default = SOF_IPC_TYPE_4,
+ .dspless_mode_supported = true, /* Only supported for HDaudio */
+ .default_fw_path = {
+ [SOF_IPC_TYPE_4] = "intel/sof-ipc4/arl",
+ },
+ .default_lib_path = {
+ [SOF_IPC_TYPE_4] = "intel/sof-ipc4-lib/arl",
+ },
+ .default_tplg_path = {
+ [SOF_IPC_TYPE_4] = "intel/sof-ace-tplg",
+ },
+ .default_fw_filename = {
+ [SOF_IPC_TYPE_4] = "sof-arl.ri",
+ },
+ .nocodec_tplg_filename = "sof-arl-nocodec.tplg",
+ .ops = &sof_mtl_ops,
+ .ops_init = sof_mtl_ops_init,
+ .ops_free = hda_ops_free,
+};
+
+static const struct sof_dev_desc arl_s_desc = {
+ .use_acpi_target_states = true,
+ .machines = snd_soc_acpi_intel_arl_machines,
+ .alt_machines = snd_soc_acpi_intel_arl_sdw_machines,
+ .resindex_lpe_base = 0,
+ .resindex_pcicfg_base = -1,
+ .resindex_imr_base = -1,
+ .irqindex_host_ipc = -1,
+ .chip_info = &arl_s_chip_info,
+ .ipc_supported_mask = BIT(SOF_IPC_TYPE_4),
+ .ipc_default = SOF_IPC_TYPE_4,
+ .dspless_mode_supported = true, /* Only supported for HDaudio */
+ .default_fw_path = {
+ [SOF_IPC_TYPE_4] = "intel/sof-ipc4/arl-s",
+ },
+ .default_lib_path = {
+ [SOF_IPC_TYPE_4] = "intel/sof-ipc4-lib/arl-s",
+ },
+ .default_tplg_path = {
+ [SOF_IPC_TYPE_4] = "intel/sof-ace-tplg",
+ },
+ .default_fw_filename = {
+ [SOF_IPC_TYPE_4] = "sof-arl-s.ri",
+ },
+ .nocodec_tplg_filename = "sof-arl-nocodec.tplg",
+ .ops = &sof_mtl_ops,
+ .ops_init = sof_mtl_ops_init,
+ .ops_free = hda_ops_free,
+};
+
+/* PCI IDs */
+static const struct pci_device_id sof_pci_ids[] = {
+ { PCI_DEVICE_DATA(INTEL, HDA_MTL, &mtl_desc) },
+ { PCI_DEVICE_DATA(INTEL, HDA_ARL_S, &arl_s_desc) },
+ { PCI_DEVICE_DATA(INTEL, HDA_ARL, &arl_desc) },
+ { 0, }
+};
+MODULE_DEVICE_TABLE(pci, sof_pci_ids);
+
+/* pci_driver definition */
+static struct pci_driver snd_sof_pci_intel_mtl_driver = {
+ .name = "sof-audio-pci-intel-mtl",
+ .id_table = sof_pci_ids,
+ .probe = hda_pci_intel_probe,
+ .remove = sof_pci_remove,
+ .shutdown = sof_pci_shutdown,
+ .driver = {
+ .pm = pm_ptr(&sof_pci_pm),
+ },
+};
+module_pci_driver(snd_sof_pci_intel_mtl_driver);
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_DESCRIPTION("SOF support for MeteorLake platforms");
+MODULE_IMPORT_NS("SND_SOC_SOF_INTEL_HDA_GENERIC");
+MODULE_IMPORT_NS("SND_SOC_SOF_INTEL_HDA_COMMON");
+MODULE_IMPORT_NS("SND_SOC_SOF_PCI_DEV");
diff --git a/sound/soc/sof/intel/pci-ptl.c b/sound/soc/sof/intel/pci-ptl.c
new file mode 100644
index 000000000000..68f6a9841633
--- /dev/null
+++ b/sound/soc/sof/intel/pci-ptl.c
@@ -0,0 +1,113 @@
+// SPDX-License-Identifier: (GPL-2.0-only 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) 2024 Intel Corporation.
+//
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <sound/soc-acpi.h>
+#include <sound/soc-acpi-intel-match.h>
+#include <sound/sof.h>
+#include "../ops.h"
+#include "../sof-pci-dev.h"
+
+/* platform specific devices */
+#include "hda.h"
+#include "ptl.h"
+
+/* PantherLake ops */
+static struct snd_sof_dsp_ops sof_ptl_ops;
+
+static int sof_ptl_ops_init(struct snd_sof_dev *sdev)
+{
+ return sof_ptl_set_ops(sdev, &sof_ptl_ops);
+}
+
+static const struct sof_dev_desc ptl_desc = {
+ .use_acpi_target_states = true,
+ .machines = snd_soc_acpi_intel_ptl_machines,
+ .alt_machines = snd_soc_acpi_intel_ptl_sdw_machines,
+ .resindex_lpe_base = 0,
+ .resindex_pcicfg_base = -1,
+ .resindex_imr_base = -1,
+ .irqindex_host_ipc = -1,
+ .chip_info = &ptl_chip_info,
+ .ipc_supported_mask = BIT(SOF_IPC_TYPE_4),
+ .ipc_default = SOF_IPC_TYPE_4,
+ .dspless_mode_supported = true,
+ .default_fw_path = {
+ [SOF_IPC_TYPE_4] = "intel/sof-ipc4/ptl",
+ },
+ .default_lib_path = {
+ [SOF_IPC_TYPE_4] = "intel/sof-ipc4-lib/ptl",
+ },
+ .default_tplg_path = {
+ [SOF_IPC_TYPE_4] = "intel/sof-ipc4-tplg",
+ },
+ .default_fw_filename = {
+ [SOF_IPC_TYPE_4] = "sof-ptl.ri",
+ },
+ .nocodec_tplg_filename = "sof-ptl-nocodec.tplg",
+ .ops = &sof_ptl_ops,
+ .ops_init = sof_ptl_ops_init,
+};
+
+static const struct sof_dev_desc wcl_desc = {
+ .use_acpi_target_states = true,
+ .machines = snd_soc_acpi_intel_ptl_machines,
+ .alt_machines = snd_soc_acpi_intel_ptl_sdw_machines,
+ .resindex_lpe_base = 0,
+ .resindex_pcicfg_base = -1,
+ .resindex_imr_base = -1,
+ .irqindex_host_ipc = -1,
+ .chip_info = &wcl_chip_info,
+ .ipc_supported_mask = BIT(SOF_IPC_TYPE_4),
+ .ipc_default = SOF_IPC_TYPE_4,
+ .dspless_mode_supported = true,
+ .default_fw_path = {
+ [SOF_IPC_TYPE_4] = "intel/sof-ipc4/wcl",
+ },
+ .default_lib_path = {
+ [SOF_IPC_TYPE_4] = "intel/sof-ipc4-lib/wcl",
+ },
+ .default_tplg_path = {
+ [SOF_IPC_TYPE_4] = "intel/sof-ipc4-tplg",
+ },
+ .default_fw_filename = {
+ [SOF_IPC_TYPE_4] = "sof-wcl.ri",
+ },
+ .nocodec_tplg_filename = "sof-ptl-nocodec.tplg",
+ .ops = &sof_ptl_ops,
+ .ops_init = sof_ptl_ops_init,
+};
+
+/* PCI IDs */
+static const struct pci_device_id sof_pci_ids[] = {
+ { PCI_DEVICE_DATA(INTEL, HDA_PTL, &ptl_desc) }, /* PTL */
+ { PCI_DEVICE_DATA(INTEL, HDA_PTL_H, &ptl_desc) }, /* PTL-H */
+ { PCI_DEVICE_DATA(INTEL, HDA_WCL, &wcl_desc) }, /* WCL */
+ { 0, }
+};
+MODULE_DEVICE_TABLE(pci, sof_pci_ids);
+
+/* pci_driver definition */
+static struct pci_driver snd_sof_pci_intel_ptl_driver = {
+ .name = "sof-audio-pci-intel-ptl",
+ .id_table = sof_pci_ids,
+ .probe = hda_pci_intel_probe,
+ .remove = sof_pci_remove,
+ .shutdown = sof_pci_shutdown,
+ .driver = {
+ .pm = pm_ptr(&sof_pci_pm),
+ },
+};
+module_pci_driver(snd_sof_pci_intel_ptl_driver);
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_DESCRIPTION("SOF support for PantherLake platforms");
+MODULE_IMPORT_NS("SND_SOC_SOF_INTEL_HDA_GENERIC");
+MODULE_IMPORT_NS("SND_SOC_SOF_INTEL_HDA_COMMON");
+MODULE_IMPORT_NS("SND_SOC_SOF_PCI_DEV");
diff --git a/sound/soc/sof/intel/pci-skl.c b/sound/soc/sof/intel/pci-skl.c
new file mode 100644
index 000000000000..a16945dc35f7
--- /dev/null
+++ b/sound/soc/sof/intel/pci-skl.c
@@ -0,0 +1,95 @@
+// SPDX-License-Identifier: (GPL-2.0-only 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) 2018-2022 Intel Corporation
+//
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <sound/soc-acpi.h>
+#include <sound/soc-acpi-intel-match.h>
+#include <sound/sof.h>
+#include "../ops.h"
+#include "../sof-pci-dev.h"
+
+/* platform specific devices */
+#include "hda.h"
+
+static struct sof_dev_desc skl_desc = {
+ .machines = snd_soc_acpi_intel_skl_machines,
+ .resindex_lpe_base = 0,
+ .resindex_pcicfg_base = -1,
+ .resindex_imr_base = -1,
+ .chip_info = &skl_chip_info,
+ .irqindex_host_ipc = -1,
+ .ipc_supported_mask = BIT(SOF_IPC_TYPE_4),
+ .ipc_default = SOF_IPC_TYPE_4,
+ .dspless_mode_supported = true, /* Only supported for HDaudio */
+ .default_fw_path = {
+ [SOF_IPC_TYPE_4] = "intel/avs/skl",
+ },
+ .default_tplg_path = {
+ [SOF_IPC_TYPE_4] = "intel/avs-tplg",
+ },
+ .default_fw_filename = {
+ [SOF_IPC_TYPE_4] = "dsp_basefw.bin",
+ },
+ .nocodec_tplg_filename = "sof-skl-nocodec.tplg",
+ .ops = &sof_skl_ops,
+ .ops_init = sof_skl_ops_init,
+ .ops_free = hda_ops_free,
+};
+
+static struct sof_dev_desc kbl_desc = {
+ .machines = snd_soc_acpi_intel_kbl_machines,
+ .resindex_lpe_base = 0,
+ .resindex_pcicfg_base = -1,
+ .resindex_imr_base = -1,
+ .chip_info = &skl_chip_info,
+ .irqindex_host_ipc = -1,
+ .ipc_supported_mask = BIT(SOF_IPC_TYPE_4),
+ .ipc_default = SOF_IPC_TYPE_4,
+ .dspless_mode_supported = true, /* Only supported for HDaudio */
+ .default_fw_path = {
+ [SOF_IPC_TYPE_4] = "intel/avs/kbl",
+ },
+ .default_tplg_path = {
+ [SOF_IPC_TYPE_4] = "intel/avs-tplg",
+ },
+ .default_fw_filename = {
+ [SOF_IPC_TYPE_4] = "dsp_basefw.bin",
+ },
+ .nocodec_tplg_filename = "sof-kbl-nocodec.tplg",
+ .ops = &sof_skl_ops,
+ .ops_init = sof_skl_ops_init,
+ .ops_free = hda_ops_free,
+};
+
+/* PCI IDs */
+static const struct pci_device_id sof_pci_ids[] = {
+ { PCI_DEVICE_DATA(INTEL, HDA_SKL_LP, &skl_desc) },
+ { PCI_DEVICE_DATA(INTEL, HDA_KBL_LP, &kbl_desc) },
+ { 0, }
+};
+MODULE_DEVICE_TABLE(pci, sof_pci_ids);
+
+/* pci_driver definition */
+static struct pci_driver snd_sof_pci_intel_skl_driver = {
+ .name = "sof-audio-pci-intel-skl",
+ .id_table = sof_pci_ids,
+ .probe = hda_pci_intel_probe,
+ .remove = sof_pci_remove,
+ .shutdown = sof_pci_shutdown,
+ .driver = {
+ .pm = pm_ptr(&sof_pci_pm),
+ },
+};
+module_pci_driver(snd_sof_pci_intel_skl_driver);
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_DESCRIPTION("SOF support for SkyLake platforms");
+MODULE_IMPORT_NS("SND_SOC_SOF_INTEL_HDA_GENERIC");
+MODULE_IMPORT_NS("SND_SOC_SOF_INTEL_HDA_COMMON");
+MODULE_IMPORT_NS("SND_SOC_SOF_PCI_DEV");
diff --git a/sound/soc/sof/intel/pci-tgl.c b/sound/soc/sof/intel/pci-tgl.c
new file mode 100644
index 000000000000..437c43819825
--- /dev/null
+++ b/sound/soc/sof/intel/pci-tgl.c
@@ -0,0 +1,324 @@
+// SPDX-License-Identifier: (GPL-2.0-only 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) 2018-2021 Intel Corporation
+//
+// Author: Liam Girdwood <liam.r.girdwood@linux.intel.com>
+//
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <sound/soc-acpi.h>
+#include <sound/soc-acpi-intel-match.h>
+#include <sound/sof.h>
+#include "../ops.h"
+#include "../sof-pci-dev.h"
+
+/* platform specific devices */
+#include "hda.h"
+
+static const struct sof_dev_desc tgl_desc = {
+ .machines = snd_soc_acpi_intel_tgl_machines,
+ .alt_machines = snd_soc_acpi_intel_tgl_sdw_machines,
+ .use_acpi_target_states = true,
+ .resindex_lpe_base = 0,
+ .resindex_pcicfg_base = -1,
+ .resindex_imr_base = -1,
+ .irqindex_host_ipc = -1,
+ .chip_info = &tgl_chip_info,
+ .ipc_supported_mask = BIT(SOF_IPC_TYPE_3) | BIT(SOF_IPC_TYPE_4),
+ .ipc_default = SOF_IPC_TYPE_3,
+ .dspless_mode_supported = true, /* Only supported for HDaudio */
+ .default_fw_path = {
+ [SOF_IPC_TYPE_3] = "intel/sof",
+ [SOF_IPC_TYPE_4] = "intel/sof-ipc4/tgl",
+ },
+ .default_lib_path = {
+ [SOF_IPC_TYPE_4] = "intel/sof-ipc4-lib/tgl",
+ },
+ .default_tplg_path = {
+ [SOF_IPC_TYPE_3] = "intel/sof-tplg",
+ [SOF_IPC_TYPE_4] = "intel/sof-ipc4-tplg",
+ },
+ .default_fw_filename = {
+ [SOF_IPC_TYPE_3] = "sof-tgl.ri",
+ [SOF_IPC_TYPE_4] = "sof-tgl.ri",
+ },
+ .nocodec_tplg_filename = "sof-tgl-nocodec.tplg",
+ .ops = &sof_tgl_ops,
+ .ops_init = sof_tgl_ops_init,
+ .ops_free = hda_ops_free,
+};
+
+static const struct sof_dev_desc tglh_desc = {
+ .machines = snd_soc_acpi_intel_tgl_machines,
+ .alt_machines = snd_soc_acpi_intel_tgl_sdw_machines,
+ .use_acpi_target_states = true,
+ .resindex_lpe_base = 0,
+ .resindex_pcicfg_base = -1,
+ .resindex_imr_base = -1,
+ .irqindex_host_ipc = -1,
+ .chip_info = &tglh_chip_info,
+ .ipc_supported_mask = BIT(SOF_IPC_TYPE_3) | BIT(SOF_IPC_TYPE_4),
+ .ipc_default = SOF_IPC_TYPE_3,
+ .dspless_mode_supported = true, /* Only supported for HDaudio */
+ .default_fw_path = {
+ [SOF_IPC_TYPE_3] = "intel/sof",
+ [SOF_IPC_TYPE_4] = "intel/sof-ipc4/tgl-h",
+ },
+ .default_lib_path = {
+ [SOF_IPC_TYPE_4] = "intel/sof-ipc4-lib/tgl-h",
+ },
+ .default_tplg_path = {
+ [SOF_IPC_TYPE_3] = "intel/sof-tplg",
+ [SOF_IPC_TYPE_4] = "intel/sof-ipc4-tplg",
+ },
+ .default_fw_filename = {
+ [SOF_IPC_TYPE_3] = "sof-tgl-h.ri",
+ [SOF_IPC_TYPE_4] = "sof-tgl-h.ri",
+ },
+ .nocodec_tplg_filename = "sof-tgl-nocodec.tplg",
+ .ops = &sof_tgl_ops,
+ .ops_init = sof_tgl_ops_init,
+ .ops_free = hda_ops_free,
+};
+
+static const struct sof_dev_desc ehl_desc = {
+ .machines = snd_soc_acpi_intel_ehl_machines,
+ .use_acpi_target_states = true,
+ .resindex_lpe_base = 0,
+ .resindex_pcicfg_base = -1,
+ .resindex_imr_base = -1,
+ .irqindex_host_ipc = -1,
+ .chip_info = &ehl_chip_info,
+ .ipc_supported_mask = BIT(SOF_IPC_TYPE_3) | BIT(SOF_IPC_TYPE_4),
+ .ipc_default = SOF_IPC_TYPE_3,
+ .dspless_mode_supported = true, /* Only supported for HDaudio */
+ .default_fw_path = {
+ [SOF_IPC_TYPE_3] = "intel/sof",
+ [SOF_IPC_TYPE_4] = "intel/sof-ipc4/ehl",
+ },
+ .default_lib_path = {
+ [SOF_IPC_TYPE_4] = "intel/sof-ipc4-lib/ehl",
+ },
+ .default_tplg_path = {
+ [SOF_IPC_TYPE_3] = "intel/sof-tplg",
+ [SOF_IPC_TYPE_4] = "intel/sof-ipc4-tplg",
+ },
+ .default_fw_filename = {
+ [SOF_IPC_TYPE_3] = "sof-ehl.ri",
+ [SOF_IPC_TYPE_4] = "sof-ehl.ri",
+ },
+ .nocodec_tplg_filename = "sof-ehl-nocodec.tplg",
+ .ops = &sof_tgl_ops,
+ .ops_init = sof_tgl_ops_init,
+ .ops_free = hda_ops_free,
+};
+
+static const struct sof_dev_desc adls_desc = {
+ .machines = snd_soc_acpi_intel_adl_machines,
+ .alt_machines = snd_soc_acpi_intel_adl_sdw_machines,
+ .use_acpi_target_states = true,
+ .resindex_lpe_base = 0,
+ .resindex_pcicfg_base = -1,
+ .resindex_imr_base = -1,
+ .irqindex_host_ipc = -1,
+ .chip_info = &adls_chip_info,
+ .ipc_supported_mask = BIT(SOF_IPC_TYPE_3) | BIT(SOF_IPC_TYPE_4),
+ .ipc_default = SOF_IPC_TYPE_3,
+ .dspless_mode_supported = true, /* Only supported for HDaudio */
+ .default_fw_path = {
+ [SOF_IPC_TYPE_3] = "intel/sof",
+ [SOF_IPC_TYPE_4] = "intel/sof-ipc4/adl-s",
+ },
+ .default_lib_path = {
+ [SOF_IPC_TYPE_4] = "intel/sof-ipc4-lib/adl-s",
+ },
+ .default_tplg_path = {
+ [SOF_IPC_TYPE_3] = "intel/sof-tplg",
+ [SOF_IPC_TYPE_4] = "intel/sof-ipc4-tplg",
+ },
+ .default_fw_filename = {
+ [SOF_IPC_TYPE_3] = "sof-adl-s.ri",
+ [SOF_IPC_TYPE_4] = "sof-adl-s.ri",
+ },
+ .nocodec_tplg_filename = "sof-adl-nocodec.tplg",
+ .ops = &sof_tgl_ops,
+ .ops_init = sof_tgl_ops_init,
+ .ops_free = hda_ops_free,
+};
+
+static const struct sof_dev_desc adl_desc = {
+ .machines = snd_soc_acpi_intel_adl_machines,
+ .alt_machines = snd_soc_acpi_intel_adl_sdw_machines,
+ .use_acpi_target_states = true,
+ .resindex_lpe_base = 0,
+ .resindex_pcicfg_base = -1,
+ .resindex_imr_base = -1,
+ .irqindex_host_ipc = -1,
+ .chip_info = &tgl_chip_info,
+ .ipc_supported_mask = BIT(SOF_IPC_TYPE_3) | BIT(SOF_IPC_TYPE_4),
+ .ipc_default = SOF_IPC_TYPE_3,
+ .dspless_mode_supported = true, /* Only supported for HDaudio */
+ .default_fw_path = {
+ [SOF_IPC_TYPE_3] = "intel/sof",
+ [SOF_IPC_TYPE_4] = "intel/sof-ipc4/adl",
+ },
+ .default_lib_path = {
+ [SOF_IPC_TYPE_4] = "intel/sof-ipc4-lib/adl",
+ },
+ .default_tplg_path = {
+ [SOF_IPC_TYPE_3] = "intel/sof-tplg",
+ [SOF_IPC_TYPE_4] = "intel/sof-ipc4-tplg",
+ },
+ .default_fw_filename = {
+ [SOF_IPC_TYPE_3] = "sof-adl.ri",
+ [SOF_IPC_TYPE_4] = "sof-adl.ri",
+ },
+ .nocodec_tplg_filename = "sof-adl-nocodec.tplg",
+ .ops = &sof_tgl_ops,
+ .ops_init = sof_tgl_ops_init,
+ .ops_free = hda_ops_free,
+};
+
+static const struct sof_dev_desc adln_desc = {
+ .machines = snd_soc_acpi_intel_adl_machines,
+ .alt_machines = snd_soc_acpi_intel_adl_sdw_machines,
+ .use_acpi_target_states = true,
+ .resindex_lpe_base = 0,
+ .resindex_pcicfg_base = -1,
+ .resindex_imr_base = -1,
+ .irqindex_host_ipc = -1,
+ .chip_info = &tgl_chip_info,
+ .ipc_supported_mask = BIT(SOF_IPC_TYPE_3) | BIT(SOF_IPC_TYPE_4),
+ .ipc_default = SOF_IPC_TYPE_3,
+ .dspless_mode_supported = true, /* Only supported for HDaudio */
+ .default_fw_path = {
+ [SOF_IPC_TYPE_3] = "intel/sof",
+ [SOF_IPC_TYPE_4] = "intel/sof-ipc4/adl-n",
+ },
+ .default_lib_path = {
+ [SOF_IPC_TYPE_4] = "intel/sof-ipc4-lib/adl-n",
+ },
+ .default_tplg_path = {
+ [SOF_IPC_TYPE_3] = "intel/sof-tplg",
+ [SOF_IPC_TYPE_4] = "intel/sof-ipc4-tplg",
+ },
+ .default_fw_filename = {
+ [SOF_IPC_TYPE_3] = "sof-adl-n.ri",
+ [SOF_IPC_TYPE_4] = "sof-adl-n.ri",
+ },
+ .nocodec_tplg_filename = "sof-adl-nocodec.tplg",
+ .ops = &sof_tgl_ops,
+ .ops_init = sof_tgl_ops_init,
+ .ops_free = hda_ops_free,
+};
+
+static const struct sof_dev_desc rpls_desc = {
+ .machines = snd_soc_acpi_intel_rpl_machines,
+ .alt_machines = snd_soc_acpi_intel_rpl_sdw_machines,
+ .use_acpi_target_states = true,
+ .resindex_lpe_base = 0,
+ .resindex_pcicfg_base = -1,
+ .resindex_imr_base = -1,
+ .irqindex_host_ipc = -1,
+ .chip_info = &adls_chip_info,
+ .ipc_supported_mask = BIT(SOF_IPC_TYPE_3) | BIT(SOF_IPC_TYPE_4),
+ .ipc_default = SOF_IPC_TYPE_3,
+ .dspless_mode_supported = true, /* Only supported for HDaudio */
+ .default_fw_path = {
+ [SOF_IPC_TYPE_3] = "intel/sof",
+ [SOF_IPC_TYPE_4] = "intel/sof-ipc4/rpl-s",
+ },
+ .default_lib_path = {
+ [SOF_IPC_TYPE_4] = "intel/sof-ipc4-lib/rpl-s",
+ },
+ .default_tplg_path = {
+ [SOF_IPC_TYPE_3] = "intel/sof-tplg",
+ [SOF_IPC_TYPE_4] = "intel/sof-ipc4-tplg",
+ },
+ .default_fw_filename = {
+ [SOF_IPC_TYPE_3] = "sof-rpl-s.ri",
+ [SOF_IPC_TYPE_4] = "sof-rpl-s.ri",
+ },
+ .nocodec_tplg_filename = "sof-rpl-nocodec.tplg",
+ .ops = &sof_tgl_ops,
+ .ops_init = sof_tgl_ops_init,
+ .ops_free = hda_ops_free,
+};
+
+static const struct sof_dev_desc rpl_desc = {
+ .machines = snd_soc_acpi_intel_rpl_machines,
+ .alt_machines = snd_soc_acpi_intel_rpl_sdw_machines,
+ .use_acpi_target_states = true,
+ .resindex_lpe_base = 0,
+ .resindex_pcicfg_base = -1,
+ .resindex_imr_base = -1,
+ .irqindex_host_ipc = -1,
+ .chip_info = &tgl_chip_info,
+ .ipc_supported_mask = BIT(SOF_IPC_TYPE_3) | BIT(SOF_IPC_TYPE_4),
+ .ipc_default = SOF_IPC_TYPE_3,
+ .dspless_mode_supported = true, /* Only supported for HDaudio */
+ .default_fw_path = {
+ [SOF_IPC_TYPE_3] = "intel/sof",
+ [SOF_IPC_TYPE_4] = "intel/sof-ipc4/rpl",
+ },
+ .default_lib_path = {
+ [SOF_IPC_TYPE_4] = "intel/sof-ipc4-lib/rpl",
+ },
+ .default_tplg_path = {
+ [SOF_IPC_TYPE_3] = "intel/sof-tplg",
+ [SOF_IPC_TYPE_4] = "intel/sof-ipc4-tplg",
+ },
+ .default_fw_filename = {
+ [SOF_IPC_TYPE_3] = "sof-rpl.ri",
+ [SOF_IPC_TYPE_4] = "sof-rpl.ri",
+ },
+ .nocodec_tplg_filename = "sof-rpl-nocodec.tplg",
+ .ops = &sof_tgl_ops,
+ .ops_init = sof_tgl_ops_init,
+ .ops_free = hda_ops_free,
+};
+
+/* PCI IDs */
+static const struct pci_device_id sof_pci_ids[] = {
+ { PCI_DEVICE_DATA(INTEL, HDA_TGL_LP, &tgl_desc) },
+ { PCI_DEVICE_DATA(INTEL, HDA_TGL_H, &tglh_desc) },
+ { PCI_DEVICE_DATA(INTEL, HDA_EHL_0, &ehl_desc) },
+ { PCI_DEVICE_DATA(INTEL, HDA_EHL_3, &ehl_desc) },
+ { PCI_DEVICE_DATA(INTEL, HDA_ADL_S, &adls_desc) },
+ { PCI_DEVICE_DATA(INTEL, HDA_RPL_S, &rpls_desc) },
+ { PCI_DEVICE_DATA(INTEL, HDA_ADL_P, &adl_desc) },
+ { PCI_DEVICE_DATA(INTEL, HDA_ADL_PS, &adl_desc) },
+ { PCI_DEVICE_DATA(INTEL, HDA_RPL_P_0, &rpl_desc) },
+ { PCI_DEVICE_DATA(INTEL, HDA_RPL_P_1, &rpl_desc) },
+ { PCI_DEVICE_DATA(INTEL, HDA_ADL_M, &adl_desc) },
+ { PCI_DEVICE_DATA(INTEL, HDA_ADL_PX, &adl_desc) },
+ { PCI_DEVICE_DATA(INTEL, HDA_RPL_M, &rpl_desc) },
+ { PCI_DEVICE_DATA(INTEL, HDA_RPL_PX, &rpl_desc) },
+ { PCI_DEVICE_DATA(INTEL, HDA_ADL_N, &adln_desc) },
+ { 0, }
+};
+MODULE_DEVICE_TABLE(pci, sof_pci_ids);
+
+/* pci_driver definition */
+static struct pci_driver snd_sof_pci_intel_tgl_driver = {
+ .name = "sof-audio-pci-intel-tgl",
+ .id_table = sof_pci_ids,
+ .probe = hda_pci_intel_probe,
+ .remove = sof_pci_remove,
+ .shutdown = sof_pci_shutdown,
+ .driver = {
+ .pm = pm_ptr(&sof_pci_pm),
+ },
+};
+module_pci_driver(snd_sof_pci_intel_tgl_driver);
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_DESCRIPTION("SOF support for TigerLake platforms");
+MODULE_IMPORT_NS("SND_SOC_SOF_INTEL_HDA_GENERIC");
+MODULE_IMPORT_NS("SND_SOC_SOF_INTEL_HDA_COMMON");
+MODULE_IMPORT_NS("SND_SOC_SOF_INTEL_CNL");
+MODULE_IMPORT_NS("SND_SOC_SOF_PCI_DEV");
diff --git a/sound/soc/sof/intel/pci-tng.c b/sound/soc/sof/intel/pci-tng.c
new file mode 100644
index 000000000000..0c11cc1fd820
--- /dev/null
+++ b/sound/soc/sof/intel/pci-tng.c
@@ -0,0 +1,250 @@
+// SPDX-License-Identifier: (GPL-2.0-only 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) 2018-2021 Intel Corporation
+//
+// Author: Liam Girdwood <liam.r.girdwood@linux.intel.com>
+//
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <sound/soc-acpi.h>
+#include <sound/soc-acpi-intel-match.h>
+#include <sound/sof.h>
+#include "../ops.h"
+#include "atom.h"
+#include "../sof-pci-dev.h"
+#include "../sof-audio.h"
+
+/* platform specific devices */
+#include "shim.h"
+
+static struct snd_soc_acpi_mach sof_tng_machines[] = {
+ {
+ .id = "INT343A",
+ .drv_name = "edison",
+ .sof_tplg_filename = "sof-byt.tplg",
+ },
+ {}
+};
+
+static const struct snd_sof_debugfs_map tng_debugfs[] = {
+ {"dmac0", DSP_BAR, DMAC0_OFFSET, DMAC_SIZE,
+ SOF_DEBUGFS_ACCESS_ALWAYS},
+ {"dmac1", DSP_BAR, DMAC1_OFFSET, DMAC_SIZE,
+ SOF_DEBUGFS_ACCESS_ALWAYS},
+ {"ssp0", DSP_BAR, SSP0_OFFSET, SSP_SIZE,
+ SOF_DEBUGFS_ACCESS_ALWAYS},
+ {"ssp1", DSP_BAR, SSP1_OFFSET, SSP_SIZE,
+ SOF_DEBUGFS_ACCESS_ALWAYS},
+ {"ssp2", DSP_BAR, SSP2_OFFSET, SSP_SIZE,
+ SOF_DEBUGFS_ACCESS_ALWAYS},
+ {"iram", DSP_BAR, IRAM_OFFSET, IRAM_SIZE,
+ SOF_DEBUGFS_ACCESS_D0_ONLY},
+ {"dram", DSP_BAR, DRAM_OFFSET, DRAM_SIZE,
+ SOF_DEBUGFS_ACCESS_D0_ONLY},
+ {"shim", DSP_BAR, SHIM_OFFSET, SHIM_SIZE_BYT,
+ SOF_DEBUGFS_ACCESS_ALWAYS},
+};
+
+static int tangier_pci_probe(struct snd_sof_dev *sdev)
+{
+ struct snd_sof_pdata *pdata = sdev->pdata;
+ const struct sof_dev_desc *desc = pdata->desc;
+ struct pci_dev *pci = to_pci_dev(sdev->dev);
+ const struct sof_intel_dsp_desc *chip;
+ u32 base, size;
+ int ret;
+
+ chip = get_chip_info(sdev->pdata);
+ if (!chip) {
+ dev_err(sdev->dev, "error: no such device supported\n");
+ return -EIO;
+ }
+
+ sdev->num_cores = chip->cores_num;
+
+ /* DSP DMA can only access low 31 bits of host memory */
+ ret = dma_coerce_mask_and_coherent(&pci->dev, DMA_BIT_MASK(31));
+ if (ret < 0) {
+ dev_err(sdev->dev, "error: failed to set DMA mask %d\n", ret);
+ return ret;
+ }
+
+ /* LPE base */
+ base = pci_resource_start(pci, desc->resindex_lpe_base) - IRAM_OFFSET;
+ size = PCI_BAR_SIZE;
+
+ dev_dbg(sdev->dev, "LPE PHY base at 0x%x size 0x%x", base, size);
+ sdev->bar[DSP_BAR] = devm_ioremap(sdev->dev, base, size);
+ if (!sdev->bar[DSP_BAR]) {
+ dev_err(sdev->dev, "error: failed to ioremap LPE base 0x%x size 0x%x\n",
+ base, size);
+ return -ENODEV;
+ }
+ dev_dbg(sdev->dev, "LPE VADDR %p\n", sdev->bar[DSP_BAR]);
+
+ /* IMR base - optional */
+ if (desc->resindex_imr_base == -1)
+ goto irq;
+
+ base = pci_resource_start(pci, desc->resindex_imr_base);
+ size = pci_resource_len(pci, desc->resindex_imr_base);
+
+ /* some BIOSes don't map IMR */
+ if (base == 0x55aa55aa || base == 0x0) {
+ dev_info(sdev->dev, "IMR not set by BIOS. Ignoring\n");
+ goto irq;
+ }
+
+ dev_dbg(sdev->dev, "IMR base at 0x%x size 0x%x", base, size);
+ sdev->bar[IMR_BAR] = devm_ioremap(sdev->dev, base, size);
+ if (!sdev->bar[IMR_BAR]) {
+ dev_err(sdev->dev, "error: failed to ioremap IMR base 0x%x size 0x%x\n",
+ base, size);
+ return -ENODEV;
+ }
+ dev_dbg(sdev->dev, "IMR VADDR %p\n", sdev->bar[IMR_BAR]);
+
+irq:
+ /* register our IRQ */
+ sdev->ipc_irq = pci->irq;
+ dev_dbg(sdev->dev, "using IRQ %d\n", sdev->ipc_irq);
+ ret = devm_request_threaded_irq(sdev->dev, sdev->ipc_irq,
+ atom_irq_handler, atom_irq_thread,
+ 0, "AudioDSP", sdev);
+ if (ret < 0) {
+ dev_err(sdev->dev, "error: failed to register IRQ %d\n",
+ sdev->ipc_irq);
+ return ret;
+ }
+
+ /* enable BUSY and disable DONE Interrupt by default */
+ snd_sof_dsp_update_bits64(sdev, DSP_BAR, SHIM_IMRX,
+ SHIM_IMRX_BUSY | SHIM_IMRX_DONE,
+ SHIM_IMRX_DONE);
+
+ /* set default mailbox offset for FW ready message */
+ sdev->dsp_box.offset = MBOX_OFFSET;
+
+ return ret;
+}
+
+const struct snd_sof_dsp_ops sof_tng_ops = {
+ /* device init */
+ .probe = tangier_pci_probe,
+
+ /* DSP core boot / reset */
+ .run = atom_run,
+ .reset = atom_reset,
+
+ /* Register IO uses direct mmio */
+
+ /* Block IO */
+ .block_read = sof_block_read,
+ .block_write = sof_block_write,
+
+ /* Mailbox IO */
+ .mailbox_read = sof_mailbox_read,
+ .mailbox_write = sof_mailbox_write,
+
+ /* doorbell */
+ .irq_handler = atom_irq_handler,
+ .irq_thread = atom_irq_thread,
+
+ /* ipc */
+ .send_msg = atom_send_msg,
+ .get_mailbox_offset = atom_get_mailbox_offset,
+ .get_window_offset = atom_get_window_offset,
+
+ .ipc_msg_data = sof_ipc_msg_data,
+ .set_stream_data_offset = sof_set_stream_data_offset,
+
+ /* machine driver */
+ .machine_select = atom_machine_select,
+ .machine_register = sof_machine_register,
+ .machine_unregister = sof_machine_unregister,
+ .set_mach_params = atom_set_mach_params,
+
+ /* debug */
+ .debug_map = tng_debugfs,
+ .debug_map_count = ARRAY_SIZE(tng_debugfs),
+ .dbg_dump = atom_dump,
+ .debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem,
+
+ /* stream callbacks */
+ .pcm_open = sof_stream_pcm_open,
+ .pcm_close = sof_stream_pcm_close,
+
+ /*Firmware loading */
+ .load_firmware = snd_sof_load_firmware_memcpy,
+
+ /* DAI drivers */
+ .drv = atom_dai,
+ .num_drv = 3, /* we have only 3 SSPs on byt*/
+
+ /* ALSA HW info flags */
+ .hw_info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_BATCH,
+
+ .dsp_arch_ops = &sof_xtensa_arch_ops,
+};
+
+const struct sof_intel_dsp_desc tng_chip_info = {
+ .cores_num = 1,
+ .host_managed_cores_mask = 1,
+ .hw_ip_version = SOF_INTEL_TANGIER,
+};
+
+static const struct sof_dev_desc tng_desc = {
+ .machines = sof_tng_machines,
+ .resindex_lpe_base = 3, /* IRAM, but subtract IRAM offset */
+ .resindex_pcicfg_base = -1,
+ .resindex_imr_base = 0,
+ .irqindex_host_ipc = -1,
+ .chip_info = &tng_chip_info,
+ .ipc_supported_mask = BIT(SOF_IPC_TYPE_3),
+ .ipc_default = SOF_IPC_TYPE_3,
+ .default_fw_path = {
+ [SOF_IPC_TYPE_3] = "intel/sof",
+ },
+ .default_tplg_path = {
+ [SOF_IPC_TYPE_3] = "intel/sof-tplg",
+ },
+ .default_fw_filename = {
+ [SOF_IPC_TYPE_3] = "sof-byt.ri",
+ },
+ .nocodec_tplg_filename = "sof-byt.tplg",
+ .ops = &sof_tng_ops,
+};
+
+/* PCI IDs */
+static const struct pci_device_id sof_pci_ids[] = {
+ { PCI_DEVICE_DATA(INTEL, SST_TNG, &tng_desc) },
+ { 0, }
+};
+MODULE_DEVICE_TABLE(pci, sof_pci_ids);
+
+/* pci_driver definition */
+static struct pci_driver snd_sof_pci_intel_tng_driver = {
+ .name = "sof-audio-pci-intel-tng",
+ .id_table = sof_pci_ids,
+ .probe = sof_pci_probe,
+ .remove = sof_pci_remove,
+ .shutdown = sof_pci_shutdown,
+ .driver = {
+ .pm = pm_ptr(&sof_pci_pm),
+ },
+};
+module_pci_driver(snd_sof_pci_intel_tng_driver);
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_DESCRIPTION("SOF support for Tangier platforms");
+MODULE_IMPORT_NS("SND_SOC_SOF_XTENSA");
+MODULE_IMPORT_NS("SND_SOC_SOF_PCI_DEV");
+MODULE_IMPORT_NS("SND_SOC_SOF_INTEL_ATOM_HIFI_EP");
diff --git a/sound/soc/sof/intel/ptl.c b/sound/soc/sof/intel/ptl.c
new file mode 100644
index 000000000000..875d18193b05
--- /dev/null
+++ b/sound/soc/sof/intel/ptl.c
@@ -0,0 +1,154 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+//
+// Copyright(c) 2025 Intel Corporation
+
+/*
+ * Hardware interface for audio DSP on PantherLake.
+ */
+
+#include <sound/hda_register.h>
+#include <sound/hda-mlink.h>
+#include <sound/sof/ipc4/header.h>
+#include "../ipc4-priv.h"
+#include "../ops.h"
+#include "hda.h"
+#include "hda-ipc.h"
+#include "../sof-audio.h"
+#include "mtl.h"
+#include "lnl.h"
+#include "ptl.h"
+
+static bool sof_ptl_check_mic_privacy_irq(struct snd_sof_dev *sdev, bool alt,
+ int elid)
+{
+ if (!alt || elid != AZX_REG_ML_LEPTR_ID_SDW)
+ return false;
+
+ return hdac_bus_eml_is_mic_privacy_changed(sof_to_bus(sdev), alt, elid);
+}
+
+static void sof_ptl_mic_privacy_work(struct work_struct *work)
+{
+ struct sof_intel_hda_dev *hdev = container_of(work,
+ struct sof_intel_hda_dev,
+ mic_privacy.work);
+ struct hdac_bus *bus = &hdev->hbus.core;
+ struct snd_sof_dev *sdev = dev_get_drvdata(bus->dev);
+ bool state;
+
+ /*
+ * The microphone privacy state is only available via Soundwire shim
+ * in PTL
+ * The work is only scheduled on change.
+ */
+ state = hdac_bus_eml_get_mic_privacy_state(bus, 1,
+ AZX_REG_ML_LEPTR_ID_SDW);
+ sof_ipc4_mic_privacy_state_change(sdev, state);
+}
+
+static void sof_ptl_process_mic_privacy(struct snd_sof_dev *sdev, bool alt,
+ int elid)
+{
+ struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata;
+
+ if (!alt || elid != AZX_REG_ML_LEPTR_ID_SDW)
+ return;
+
+ /*
+ * Schedule the work to read the microphone privacy state and send IPC
+ * message about the new state to the firmware
+ */
+ schedule_work(&hdev->mic_privacy.work);
+}
+
+static void sof_ptl_set_mic_privacy(struct snd_sof_dev *sdev,
+ struct sof_ipc4_intel_mic_privacy_cap *caps)
+{
+ struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata;
+ u32 micpvcp;
+
+ if (!caps || !caps->capabilities_length)
+ return;
+
+ micpvcp = caps->capabilities[0];
+
+ /* No need to set the mic privacy if it is not enabled or forced */
+ if (!(micpvcp & PTL_MICPVCP_DDZE_ENABLED) ||
+ micpvcp & PTL_MICPVCP_DDZE_FORCED)
+ return;
+
+ hdac_bus_eml_set_mic_privacy_mask(sof_to_bus(sdev), true,
+ AZX_REG_ML_LEPTR_ID_SDW,
+ PTL_MICPVCP_GET_SDW_MASK(micpvcp));
+
+ INIT_WORK(&hdev->mic_privacy.work, sof_ptl_mic_privacy_work);
+ hdev->mic_privacy.active = true;
+}
+
+int sof_ptl_set_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *dsp_ops)
+{
+ struct sof_ipc4_fw_data *ipc4_data;
+ int ret;
+
+ ret = sof_lnl_set_ops(sdev, dsp_ops);
+ if (ret)
+ return ret;
+
+ ipc4_data = sdev->private;
+ ipc4_data->intel_configure_mic_privacy = sof_ptl_set_mic_privacy;
+
+ return 0;
+};
+EXPORT_SYMBOL_NS(sof_ptl_set_ops, "SND_SOC_SOF_INTEL_PTL");
+
+const struct sof_intel_dsp_desc ptl_chip_info = {
+ .cores_num = 5,
+ .init_core_mask = BIT(0),
+ .host_managed_cores_mask = BIT(0),
+ .ipc_req = MTL_DSP_REG_HFIPCXIDR,
+ .ipc_req_mask = MTL_DSP_REG_HFIPCXIDR_BUSY,
+ .ipc_ack = MTL_DSP_REG_HFIPCXIDA,
+ .ipc_ack_mask = MTL_DSP_REG_HFIPCXIDA_DONE,
+ .ipc_ctl = MTL_DSP_REG_HFIPCXCTL,
+ .rom_status_reg = LNL_DSP_REG_HFDSC,
+ .rom_init_timeout = 300,
+ .ssp_count = MTL_SSP_COUNT,
+ .d0i3_offset = MTL_HDA_VS_D0I3C,
+ .read_sdw_lcount = hda_sdw_check_lcount_ext,
+ .check_sdw_irq = lnl_dsp_check_sdw_irq,
+ .check_sdw_wakeen_irq = lnl_sdw_check_wakeen_irq,
+ .check_ipc_irq = mtl_dsp_check_ipc_irq,
+ .check_mic_privacy_irq = sof_ptl_check_mic_privacy_irq,
+ .process_mic_privacy = sof_ptl_process_mic_privacy,
+ .cl_init = mtl_dsp_cl_init,
+ .power_down_dsp = mtl_power_down_dsp,
+ .disable_interrupts = lnl_dsp_disable_interrupts,
+ .hw_ip_version = SOF_INTEL_ACE_3_0,
+};
+
+const struct sof_intel_dsp_desc wcl_chip_info = {
+ .cores_num = 3,
+ .init_core_mask = BIT(0),
+ .host_managed_cores_mask = BIT(0),
+ .ipc_req = MTL_DSP_REG_HFIPCXIDR,
+ .ipc_req_mask = MTL_DSP_REG_HFIPCXIDR_BUSY,
+ .ipc_ack = MTL_DSP_REG_HFIPCXIDA,
+ .ipc_ack_mask = MTL_DSP_REG_HFIPCXIDA_DONE,
+ .ipc_ctl = MTL_DSP_REG_HFIPCXCTL,
+ .rom_status_reg = LNL_DSP_REG_HFDSC,
+ .rom_init_timeout = 300,
+ .ssp_count = MTL_SSP_COUNT,
+ .d0i3_offset = MTL_HDA_VS_D0I3C,
+ .read_sdw_lcount = hda_sdw_check_lcount_ext,
+ .check_sdw_irq = lnl_dsp_check_sdw_irq,
+ .check_sdw_wakeen_irq = lnl_sdw_check_wakeen_irq,
+ .check_ipc_irq = mtl_dsp_check_ipc_irq,
+ .cl_init = mtl_dsp_cl_init,
+ .power_down_dsp = mtl_power_down_dsp,
+ .disable_interrupts = lnl_dsp_disable_interrupts,
+ .hw_ip_version = SOF_INTEL_ACE_3_0,
+};
+
+MODULE_IMPORT_NS("SND_SOC_SOF_INTEL_MTL");
+MODULE_IMPORT_NS("SND_SOC_SOF_INTEL_LNL");
+MODULE_IMPORT_NS("SND_SOC_SOF_HDA_MLINK");
diff --git a/sound/soc/sof/intel/ptl.h b/sound/soc/sof/intel/ptl.h
new file mode 100644
index 000000000000..6a7ef11f411e
--- /dev/null
+++ b/sound/soc/sof/intel/ptl.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: (GPL-2.0-only 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) 2025 Intel Corporation
+ */
+
+#ifndef __SOF_INTEL_PTL_H
+#define __SOF_INTEL_PTL_H
+
+#define PTL_MICPVCP_DDZE_FORCED BIT(16)
+#define PTL_MICPVCP_DDZE_ENABLED BIT(17)
+#define PTL_MICPVCP_DDZLS_SDW GENMASK(26, 20)
+#define PTL_MICPVCP_GET_SDW_MASK(x) (((x) & PTL_MICPVCP_DDZLS_SDW) >> 20)
+
+int sof_ptl_set_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *dsp_ops);
+
+#endif /* __SOF_INTEL_PTL_H */
diff --git a/sound/soc/sof/intel/shim.h b/sound/soc/sof/intel/shim.h
index 6fe8b004b50e..d4372f0bff7e 100644
--- a/sound/soc/sof/intel/shim.h
+++ b/sound/soc/sof/intel/shim.h
@@ -3,7 +3,7 @@
* 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) 2017 Intel Corporation. All rights reserved.
+ * Copyright(c) 2017 Intel Corporation
*
* Author: Liam Girdwood <liam.r.girdwood@linux.intel.com>
*/
@@ -11,6 +11,20 @@
#ifndef __SOF_INTEL_SHIM_H
#define __SOF_INTEL_SHIM_H
+enum sof_intel_hw_ip_version {
+ SOF_INTEL_TANGIER,
+ SOF_INTEL_BAYTRAIL,
+ SOF_INTEL_BROADWELL,
+ SOF_INTEL_CAVS_1_5, /* SkyLake, KabyLake, AmberLake */
+ SOF_INTEL_CAVS_1_5_PLUS,/* ApolloLake, GeminiLake */
+ SOF_INTEL_CAVS_1_8, /* CannonLake, CometLake, CoffeeLake */
+ SOF_INTEL_CAVS_2_0, /* IceLake, JasperLake */
+ SOF_INTEL_CAVS_2_5, /* TigerLake, AlderLake */
+ SOF_INTEL_ACE_1_0, /* MeteorLake */
+ SOF_INTEL_ACE_2_0, /* LunarLake */
+ SOF_INTEL_ACE_3_0, /* PantherLake */
+};
+
/*
* SHIM registers for BYT, BSW, CHT, BDW
*/
@@ -151,33 +165,54 @@
#define PCI_PMCS 0x84
#define PCI_PMCS_PS_MASK 0x3
+/* Intel quirks */
+#define SOF_INTEL_PROCEN_FMT_QUIRK BIT(0)
+
/* DSP hardware descriptor */
struct sof_intel_dsp_desc {
int cores_num;
- int cores_mask;
+ int host_managed_cores_mask;
int init_core_mask; /* cores available after fw boot */
int ipc_req;
int ipc_req_mask;
int ipc_ack;
int ipc_ack_mask;
int ipc_ctl;
+ int rom_status_reg;
int rom_init_timeout;
int ssp_count; /* ssp count of the platform */
int ssp_base_offset; /* base address of the SSPs */
+ u32 sdw_shim_base;
+ u32 sdw_alh_base;
+ u32 d0i3_offset;
+ u32 quirks;
+ enum sof_intel_hw_ip_version hw_ip_version;
+ int (*read_sdw_lcount)(struct snd_sof_dev *sdev);
+ void (*enable_sdw_irq)(struct snd_sof_dev *sdev, bool enable);
+ bool (*check_sdw_irq)(struct snd_sof_dev *sdev);
+ bool (*check_sdw_wakeen_irq)(struct snd_sof_dev *sdev);
+ void (*sdw_process_wakeen)(struct snd_sof_dev *sdev);
+ bool (*check_ipc_irq)(struct snd_sof_dev *sdev);
+ bool (*check_mic_privacy_irq)(struct snd_sof_dev *sdev, bool alt, int elid);
+ void (*process_mic_privacy)(struct snd_sof_dev *sdev, bool alt, int elid);
+ int (*power_down_dsp)(struct snd_sof_dev *sdev);
+ int (*disable_interrupts)(struct snd_sof_dev *sdev);
+ int (*cl_init)(struct snd_sof_dev *sdev, int stream_tag, bool imr_boot);
};
extern const struct snd_sof_dsp_ops sof_tng_ops;
-extern const struct snd_sof_dsp_ops sof_byt_ops;
-extern const struct snd_sof_dsp_ops sof_cht_ops;
-extern const struct snd_sof_dsp_ops sof_bdw_ops;
-extern const struct sof_intel_dsp_desc byt_chip_info;
-extern const struct sof_intel_dsp_desc cht_chip_info;
-extern const struct sof_intel_dsp_desc bdw_chip_info;
extern const struct sof_intel_dsp_desc tng_chip_info;
struct sof_intel_stream {
size_t posn_offset;
};
+static inline const struct sof_intel_dsp_desc *get_chip_info(struct snd_sof_pdata *pdata)
+{
+ const struct sof_dev_desc *desc = pdata->desc;
+
+ return desc->chip_info;
+}
+
#endif
diff --git a/sound/soc/sof/intel/skl.c b/sound/soc/sof/intel/skl.c
new file mode 100644
index 000000000000..0696bce65e33
--- /dev/null
+++ b/sound/soc/sof/intel/skl.c
@@ -0,0 +1,117 @@
+// SPDX-License-Identifier: (GPL-2.0-only 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) 2018-2022 Intel Corporation
+//
+
+/*
+ * Hardware interface for audio DSP on Skylake and Kabylake.
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/firmware.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <sound/hdaudio_ext.h>
+#include <sound/pcm_params.h>
+#include <sound/sof.h>
+#include <sound/sof/ext_manifest4.h>
+
+#include "../sof-priv.h"
+#include "../ipc4-priv.h"
+#include "../ops.h"
+#include "hda.h"
+#include "../sof-audio.h"
+
+#define SRAM_MEMORY_WINDOW_BASE 0x8000
+
+static const __maybe_unused struct snd_sof_debugfs_map skl_dsp_debugfs[] = {
+ {"hda", HDA_DSP_HDA_BAR, 0, 0x4000},
+ {"pp", HDA_DSP_PP_BAR, 0, 0x1000},
+ {"dsp", HDA_DSP_BAR, 0, 0x10000},
+};
+
+static int skl_dsp_ipc_get_window_offset(struct snd_sof_dev *sdev, u32 id)
+{
+ return SRAM_MEMORY_WINDOW_BASE + (0x2000 * id);
+}
+
+static int skl_dsp_ipc_get_mailbox_offset(struct snd_sof_dev *sdev)
+{
+ return SRAM_MEMORY_WINDOW_BASE + 0x1000;
+}
+
+/* skylake ops */
+struct snd_sof_dsp_ops sof_skl_ops;
+EXPORT_SYMBOL_NS(sof_skl_ops, "SND_SOC_SOF_INTEL_HDA_COMMON");
+
+int sof_skl_ops_init(struct snd_sof_dev *sdev)
+{
+ struct sof_ipc4_fw_data *ipc4_data;
+
+ /* common defaults */
+ memcpy(&sof_skl_ops, &sof_hda_common_ops, sizeof(struct snd_sof_dsp_ops));
+
+ /* probe/remove/shutdown */
+ sof_skl_ops.shutdown = hda_dsp_shutdown;
+
+ sdev->private = kzalloc(sizeof(*ipc4_data), GFP_KERNEL);
+ if (!sdev->private)
+ return -ENOMEM;
+
+ ipc4_data = sdev->private;
+ ipc4_data->manifest_fw_hdr_offset = SOF_MAN4_FW_HDR_OFFSET_CAVS_1_5;
+
+ ipc4_data->mtrace_type = SOF_IPC4_MTRACE_INTEL_CAVS_1_5;
+
+ sof_skl_ops.get_window_offset = skl_dsp_ipc_get_window_offset;
+ sof_skl_ops.get_mailbox_offset = skl_dsp_ipc_get_mailbox_offset;
+
+ /* doorbell */
+ sof_skl_ops.irq_thread = hda_dsp_ipc4_irq_thread;
+
+ /* ipc */
+ sof_skl_ops.send_msg = hda_dsp_ipc4_send_msg;
+
+ /* set DAI driver ops */
+ hda_set_dai_drv_ops(sdev, &sof_skl_ops);
+
+ /* debug */
+ sof_skl_ops.debug_map = skl_dsp_debugfs;
+ sof_skl_ops.debug_map_count = ARRAY_SIZE(skl_dsp_debugfs);
+ sof_skl_ops.ipc_dump = hda_ipc4_dump;
+
+ /* firmware run */
+ sof_skl_ops.run = hda_dsp_cl_boot_firmware_skl;
+
+ /* pre/post fw run */
+ sof_skl_ops.post_fw_run = hda_dsp_post_fw_run;
+
+ return 0;
+};
+EXPORT_SYMBOL_NS(sof_skl_ops_init, "SND_SOC_SOF_INTEL_HDA_COMMON");
+
+const struct sof_intel_dsp_desc skl_chip_info = {
+ .cores_num = 2,
+ .init_core_mask = 1,
+ .host_managed_cores_mask = GENMASK(1, 0),
+ .ipc_req = HDA_DSP_REG_HIPCI,
+ .ipc_req_mask = HDA_DSP_REG_HIPCI_BUSY,
+ .ipc_ack = HDA_DSP_REG_HIPCIE,
+ .ipc_ack_mask = HDA_DSP_REG_HIPCIE_DONE,
+ .ipc_ctl = HDA_DSP_REG_HIPCCTL,
+ .rom_status_reg = HDA_DSP_SRAM_REG_ROM_STATUS_SKL,
+ .rom_init_timeout = 300,
+ .check_ipc_irq = hda_dsp_check_ipc_irq,
+ .power_down_dsp = hda_power_down_dsp,
+ .disable_interrupts = hda_dsp_disable_interrupts,
+ .hw_ip_version = SOF_INTEL_CAVS_1_5,
+};
+EXPORT_SYMBOL_NS(skl_chip_info, "SND_SOC_SOF_INTEL_HDA_COMMON");
diff --git a/sound/soc/sof/intel/telemetry.c b/sound/soc/sof/intel/telemetry.c
new file mode 100644
index 000000000000..dcaaf03599db
--- /dev/null
+++ b/sound/soc/sof/intel/telemetry.c
@@ -0,0 +1,96 @@
+// SPDX-License-Identifier: (GPL-2.0-only 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) 2023 Intel Corporation
+
+/* telemetry data queried from debug window */
+
+#include <sound/sof/ipc4/header.h>
+#include <sound/sof/xtensa.h>
+#include "../ipc4-priv.h"
+#include "../sof-priv.h"
+#include "hda.h"
+#include "telemetry.h"
+
+void sof_ipc4_intel_dump_telemetry_state(struct snd_sof_dev *sdev, u32 flags)
+{
+ static const char invalid_slot_msg[] = "Core dump is not available due to";
+ struct sof_ipc4_telemetry_slot_data *telemetry_data;
+ struct sof_ipc_dsp_oops_xtensa *xoops;
+ struct xtensa_arch_block *block;
+ u32 slot_offset;
+ char *level;
+
+ level = (flags & SOF_DBG_DUMP_OPTIONAL) ? KERN_DEBUG : KERN_ERR;
+
+ slot_offset = sof_ipc4_find_debug_slot_offset_by_type(sdev, SOF_IPC4_DEBUG_SLOT_TELEMETRY);
+ if (!slot_offset)
+ return;
+
+ telemetry_data = kmalloc(sizeof(*telemetry_data), GFP_KERNEL);
+ if (!telemetry_data)
+ return;
+ sof_mailbox_read(sdev, slot_offset, telemetry_data, sizeof(*telemetry_data));
+ if (telemetry_data->separator != XTENSA_CORE_DUMP_SEPARATOR) {
+ dev_err(sdev->dev, "%s invalid separator %#x\n", invalid_slot_msg,
+ telemetry_data->separator);
+ goto free_telemetry_data;
+ }
+
+ block = kmalloc(sizeof(*block), GFP_KERNEL);
+ if (!block)
+ goto free_telemetry_data;
+
+ sof_mailbox_read(sdev, slot_offset + sizeof(*telemetry_data), block, sizeof(*block));
+ if (block->soc != XTENSA_SOC_INTEL_ADSP) {
+ dev_err(sdev->dev, "%s invalid SOC %d\n", invalid_slot_msg, block->soc);
+ goto free_block;
+ }
+
+ if (telemetry_data->hdr.id[0] != COREDUMP_HDR_ID0 ||
+ telemetry_data->hdr.id[1] != COREDUMP_HDR_ID1 ||
+ telemetry_data->arch_hdr.id != COREDUMP_ARCH_HDR_ID) {
+ dev_err(sdev->dev, "%s invalid coredump header %c%c, arch hdr %c\n",
+ invalid_slot_msg, telemetry_data->hdr.id[0],
+ telemetry_data->hdr.id[1],
+ telemetry_data->arch_hdr.id);
+ goto free_block;
+ }
+
+ switch (block->toolchain) {
+ case XTENSA_TOOL_CHAIN_ZEPHYR:
+ dev_printk(level, sdev->dev, "FW is built with Zephyr toolchain\n");
+ break;
+ case XTENSA_TOOL_CHAIN_XCC:
+ dev_printk(level, sdev->dev, "FW is built with XCC toolchain\n");
+ break;
+ default:
+ dev_printk(level, sdev->dev, "Unknown toolchain is used\n");
+ break;
+ }
+
+ xoops = kzalloc(struct_size(xoops, ar, XTENSA_CORE_AR_REGS_COUNT), GFP_KERNEL);
+ if (!xoops)
+ goto free_block;
+
+ xoops->exccause = block->exccause;
+ xoops->excvaddr = block->excvaddr;
+ xoops->epc1 = block->pc;
+ xoops->ps = block->ps;
+ xoops->sar = block->sar;
+
+ xoops->plat_hdr.numaregs = XTENSA_CORE_AR_REGS_COUNT;
+ memcpy((void *)xoops->ar, block->ar, XTENSA_CORE_AR_REGS_COUNT * sizeof(u32));
+
+ sof_oops(sdev, level, xoops);
+ sof_stack(sdev, level, xoops, NULL, 0);
+
+ kfree(xoops);
+free_block:
+ kfree(block);
+free_telemetry_data:
+ kfree(telemetry_data);
+}
+EXPORT_SYMBOL_NS(sof_ipc4_intel_dump_telemetry_state, "SND_SOC_SOF_INTEL_HDA_COMMON");
diff --git a/sound/soc/sof/intel/telemetry.h b/sound/soc/sof/intel/telemetry.h
new file mode 100644
index 000000000000..e4e91943a41a
--- /dev/null
+++ b/sound/soc/sof/intel/telemetry.h
@@ -0,0 +1,35 @@
+/* SPDX-License-Identifier: (GPL-2.0-only 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) 2023 Intel Corporation
+ *
+ * telemetry data in debug windows
+ */
+
+#ifndef _SOF_INTEL_TELEMETRY_H
+#define _SOF_INTEL_TELEMETRY_H
+
+#include "../ipc4-telemetry.h"
+
+struct xtensa_arch_block {
+ u8 soc; /* should be equal to XTENSA_SOC_INTEL_ADSP */
+ u16 version;
+ u8 toolchain; /* ZEPHYR or XCC */
+
+ u32 pc;
+ u32 exccause;
+ u32 excvaddr;
+ u32 sar;
+ u32 ps;
+ u32 scompare1;
+ u32 ar[XTENSA_CORE_AR_REGS_COUNT];
+ u32 lbeg;
+ u32 lend;
+ u32 lcount;
+} __packed;
+
+void sof_ipc4_intel_dump_telemetry_state(struct snd_sof_dev *sdev, u32 flags);
+
+#endif /* _SOF_INTEL_TELEMETRY_H */
diff --git a/sound/soc/sof/intel/tgl.c b/sound/soc/sof/intel/tgl.c
new file mode 100644
index 000000000000..df2d26b78ddc
--- /dev/null
+++ b/sound/soc/sof/intel/tgl.c
@@ -0,0 +1,252 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+//
+// Copyright(c) 2020 Intel Corporation
+//
+// Authors: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
+//
+
+/*
+ * Hardware interface for audio DSP on Tigerlake.
+ */
+
+#include <sound/sof/ext_manifest4.h>
+#include "../ipc4-priv.h"
+#include "../ops.h"
+#include "hda.h"
+#include "hda-ipc.h"
+#include "../sof-audio.h"
+
+static const struct snd_sof_debugfs_map tgl_dsp_debugfs[] = {
+ {"hda", HDA_DSP_HDA_BAR, 0, 0x4000, SOF_DEBUGFS_ACCESS_ALWAYS},
+ {"pp", HDA_DSP_PP_BAR, 0, 0x1000, SOF_DEBUGFS_ACCESS_ALWAYS},
+ {"dsp", HDA_DSP_BAR, 0, 0x10000, SOF_DEBUGFS_ACCESS_ALWAYS},
+};
+
+static const struct snd_sof_debugfs_map tgl_ipc4_dsp_debugfs[] = {
+ {"hda", HDA_DSP_HDA_BAR, 0, 0x4000, SOF_DEBUGFS_ACCESS_ALWAYS},
+ {"pp", HDA_DSP_PP_BAR, 0, 0x1000, SOF_DEBUGFS_ACCESS_ALWAYS},
+ {"dsp", HDA_DSP_BAR, 0, 0x10000, SOF_DEBUGFS_ACCESS_ALWAYS},
+ {"fw_regs", HDA_DSP_BAR, SRAM_WINDOW_OFFSET(0), 0x1000, SOF_DEBUGFS_ACCESS_D0_ONLY},
+};
+
+static int tgl_dsp_core_get(struct snd_sof_dev *sdev, int core)
+{
+ const struct sof_ipc_pm_ops *pm_ops = sdev->ipc->ops->pm;
+
+ /* power up primary core if not already powered up and return */
+ if (core == SOF_DSP_PRIMARY_CORE)
+ return hda_dsp_enable_core(sdev, BIT(core));
+
+ if (pm_ops->set_core_state)
+ return pm_ops->set_core_state(sdev, core, true);
+
+ return 0;
+}
+
+static int tgl_dsp_core_put(struct snd_sof_dev *sdev, int core)
+{
+ const struct sof_ipc_pm_ops *pm_ops = sdev->ipc->ops->pm;
+ int ret;
+
+ if (pm_ops->set_core_state) {
+ ret = pm_ops->set_core_state(sdev, core, false);
+ if (ret < 0)
+ return ret;
+ }
+
+ /* power down primary core and return */
+ if (core == SOF_DSP_PRIMARY_CORE)
+ return hda_dsp_core_reset_power_down(sdev, BIT(core));
+
+ return 0;
+}
+
+/* Tigerlake ops */
+struct snd_sof_dsp_ops sof_tgl_ops;
+
+int sof_tgl_ops_init(struct snd_sof_dev *sdev)
+{
+ /* common defaults */
+ memcpy(&sof_tgl_ops, &sof_hda_common_ops, sizeof(struct snd_sof_dsp_ops));
+
+ /* probe/remove/shutdown */
+ sof_tgl_ops.shutdown = hda_dsp_shutdown_dma_flush;
+
+ if (sdev->pdata->ipc_type == SOF_IPC_TYPE_3) {
+ /* doorbell */
+ sof_tgl_ops.irq_thread = cnl_ipc_irq_thread;
+
+ /* ipc */
+ sof_tgl_ops.send_msg = cnl_ipc_send_msg;
+
+ /* debug */
+ sof_tgl_ops.ipc_dump = cnl_ipc_dump;
+ sof_tgl_ops.debug_map = tgl_dsp_debugfs;
+ sof_tgl_ops.debug_map_count = ARRAY_SIZE(tgl_dsp_debugfs);
+
+ sof_tgl_ops.set_power_state = hda_dsp_set_power_state_ipc3;
+ }
+
+ if (sdev->pdata->ipc_type == SOF_IPC_TYPE_4) {
+ struct sof_ipc4_fw_data *ipc4_data;
+
+ sdev->private = kzalloc(sizeof(*ipc4_data), GFP_KERNEL);
+ if (!sdev->private)
+ return -ENOMEM;
+
+ ipc4_data = sdev->private;
+ ipc4_data->manifest_fw_hdr_offset = SOF_MAN4_FW_HDR_OFFSET;
+
+ ipc4_data->mtrace_type = SOF_IPC4_MTRACE_INTEL_CAVS_2;
+
+ ipc4_data->fw_context_save = true;
+
+ /* External library loading support */
+ ipc4_data->load_library = hda_dsp_ipc4_load_library;
+
+ /* doorbell */
+ sof_tgl_ops.irq_thread = cnl_ipc4_irq_thread;
+
+ /* ipc */
+ sof_tgl_ops.send_msg = cnl_ipc4_send_msg;
+
+ /* debug */
+ sof_tgl_ops.ipc_dump = cnl_ipc4_dump;
+ sof_tgl_ops.dbg_dump = hda_ipc4_dsp_dump;
+ sof_tgl_ops.debug_map = tgl_ipc4_dsp_debugfs;
+ sof_tgl_ops.debug_map_count = ARRAY_SIZE(tgl_ipc4_dsp_debugfs);
+
+ sof_tgl_ops.set_power_state = hda_dsp_set_power_state_ipc4;
+ }
+
+ /* set DAI driver ops */
+ hda_set_dai_drv_ops(sdev, &sof_tgl_ops);
+
+ /* pre/post fw run */
+ sof_tgl_ops.post_fw_run = hda_dsp_post_fw_run;
+
+ /* firmware run */
+ sof_tgl_ops.run = hda_dsp_cl_boot_firmware_iccmax;
+
+ /* dsp core get/put */
+ sof_tgl_ops.core_get = tgl_dsp_core_get;
+ sof_tgl_ops.core_put = tgl_dsp_core_put;
+
+ return 0;
+};
+
+const struct sof_intel_dsp_desc tgl_chip_info = {
+ /* Tigerlake , Alderlake */
+ .cores_num = 4,
+ .init_core_mask = 1,
+ .host_managed_cores_mask = BIT(0),
+ .ipc_req = CNL_DSP_REG_HIPCIDR,
+ .ipc_req_mask = CNL_DSP_REG_HIPCIDR_BUSY,
+ .ipc_ack = CNL_DSP_REG_HIPCIDA,
+ .ipc_ack_mask = CNL_DSP_REG_HIPCIDA_DONE,
+ .ipc_ctl = CNL_DSP_REG_HIPCCTL,
+ .rom_status_reg = HDA_DSP_SRAM_REG_ROM_STATUS,
+ .rom_init_timeout = 300,
+ .ssp_count = TGL_SSP_COUNT,
+ .ssp_base_offset = CNL_SSP_BASE_OFFSET,
+ .sdw_shim_base = SDW_SHIM_BASE,
+ .sdw_alh_base = SDW_ALH_BASE,
+ .d0i3_offset = SOF_HDA_VS_D0I3C,
+ .read_sdw_lcount = hda_sdw_check_lcount_common,
+ .enable_sdw_irq = hda_common_enable_sdw_irq,
+ .check_sdw_irq = hda_common_check_sdw_irq,
+ .check_sdw_wakeen_irq = hda_sdw_check_wakeen_irq_common,
+ .sdw_process_wakeen = hda_sdw_process_wakeen_common,
+ .check_ipc_irq = hda_dsp_check_ipc_irq,
+ .cl_init = cl_dsp_init,
+ .power_down_dsp = hda_power_down_dsp,
+ .disable_interrupts = hda_dsp_disable_interrupts,
+ .hw_ip_version = SOF_INTEL_CAVS_2_5,
+};
+
+const struct sof_intel_dsp_desc tglh_chip_info = {
+ /* Tigerlake-H */
+ .cores_num = 2,
+ .init_core_mask = 1,
+ .host_managed_cores_mask = BIT(0),
+ .ipc_req = CNL_DSP_REG_HIPCIDR,
+ .ipc_req_mask = CNL_DSP_REG_HIPCIDR_BUSY,
+ .ipc_ack = CNL_DSP_REG_HIPCIDA,
+ .ipc_ack_mask = CNL_DSP_REG_HIPCIDA_DONE,
+ .ipc_ctl = CNL_DSP_REG_HIPCCTL,
+ .rom_status_reg = HDA_DSP_SRAM_REG_ROM_STATUS,
+ .rom_init_timeout = 300,
+ .ssp_count = TGL_SSP_COUNT,
+ .ssp_base_offset = CNL_SSP_BASE_OFFSET,
+ .sdw_shim_base = SDW_SHIM_BASE,
+ .sdw_alh_base = SDW_ALH_BASE,
+ .d0i3_offset = SOF_HDA_VS_D0I3C,
+ .read_sdw_lcount = hda_sdw_check_lcount_common,
+ .enable_sdw_irq = hda_common_enable_sdw_irq,
+ .check_sdw_irq = hda_common_check_sdw_irq,
+ .check_sdw_wakeen_irq = hda_sdw_check_wakeen_irq_common,
+ .sdw_process_wakeen = hda_sdw_process_wakeen_common,
+ .check_ipc_irq = hda_dsp_check_ipc_irq,
+ .cl_init = cl_dsp_init,
+ .power_down_dsp = hda_power_down_dsp,
+ .disable_interrupts = hda_dsp_disable_interrupts,
+ .hw_ip_version = SOF_INTEL_CAVS_2_5,
+};
+
+const struct sof_intel_dsp_desc ehl_chip_info = {
+ /* Elkhartlake */
+ .cores_num = 4,
+ .init_core_mask = 1,
+ .host_managed_cores_mask = BIT(0),
+ .ipc_req = CNL_DSP_REG_HIPCIDR,
+ .ipc_req_mask = CNL_DSP_REG_HIPCIDR_BUSY,
+ .ipc_ack = CNL_DSP_REG_HIPCIDA,
+ .ipc_ack_mask = CNL_DSP_REG_HIPCIDA_DONE,
+ .ipc_ctl = CNL_DSP_REG_HIPCCTL,
+ .rom_status_reg = HDA_DSP_SRAM_REG_ROM_STATUS,
+ .rom_init_timeout = 300,
+ .ssp_count = TGL_SSP_COUNT,
+ .ssp_base_offset = CNL_SSP_BASE_OFFSET,
+ .sdw_shim_base = SDW_SHIM_BASE,
+ .sdw_alh_base = SDW_ALH_BASE,
+ .d0i3_offset = SOF_HDA_VS_D0I3C,
+ .read_sdw_lcount = hda_sdw_check_lcount_common,
+ .enable_sdw_irq = hda_common_enable_sdw_irq,
+ .check_sdw_irq = hda_common_check_sdw_irq,
+ .check_sdw_wakeen_irq = hda_sdw_check_wakeen_irq_common,
+ .sdw_process_wakeen = hda_sdw_process_wakeen_common,
+ .check_ipc_irq = hda_dsp_check_ipc_irq,
+ .cl_init = cl_dsp_init,
+ .power_down_dsp = hda_power_down_dsp,
+ .disable_interrupts = hda_dsp_disable_interrupts,
+ .hw_ip_version = SOF_INTEL_CAVS_2_5,
+};
+
+const struct sof_intel_dsp_desc adls_chip_info = {
+ /* Alderlake-S */
+ .cores_num = 2,
+ .init_core_mask = BIT(0),
+ .host_managed_cores_mask = BIT(0),
+ .ipc_req = CNL_DSP_REG_HIPCIDR,
+ .ipc_req_mask = CNL_DSP_REG_HIPCIDR_BUSY,
+ .ipc_ack = CNL_DSP_REG_HIPCIDA,
+ .ipc_ack_mask = CNL_DSP_REG_HIPCIDA_DONE,
+ .ipc_ctl = CNL_DSP_REG_HIPCCTL,
+ .rom_status_reg = HDA_DSP_SRAM_REG_ROM_STATUS,
+ .rom_init_timeout = 300,
+ .ssp_count = TGL_SSP_COUNT,
+ .ssp_base_offset = CNL_SSP_BASE_OFFSET,
+ .sdw_shim_base = SDW_SHIM_BASE,
+ .sdw_alh_base = SDW_ALH_BASE,
+ .d0i3_offset = SOF_HDA_VS_D0I3C,
+ .read_sdw_lcount = hda_sdw_check_lcount_common,
+ .enable_sdw_irq = hda_common_enable_sdw_irq,
+ .check_sdw_irq = hda_common_check_sdw_irq,
+ .check_sdw_wakeen_irq = hda_sdw_check_wakeen_irq_common,
+ .sdw_process_wakeen = hda_sdw_process_wakeen_common,
+ .check_ipc_irq = hda_dsp_check_ipc_irq,
+ .cl_init = cl_dsp_init,
+ .power_down_dsp = hda_power_down_dsp,
+ .disable_interrupts = hda_dsp_disable_interrupts,
+ .hw_ip_version = SOF_INTEL_CAVS_2_5,
+};
diff --git a/sound/soc/sof/intel/tracepoints.c b/sound/soc/sof/intel/tracepoints.c
new file mode 100644
index 000000000000..9e3260a062c2
--- /dev/null
+++ b/sound/soc/sof/intel/tracepoints.c
@@ -0,0 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0
+#define CREATE_TRACE_POINTS
+#include <trace/events/sof_intel.h>
+
+EXPORT_TRACEPOINT_SYMBOL(sof_intel_hda_irq);