summaryrefslogtreecommitdiff
path: root/sound/soc/intel/avs/board_selection.c
diff options
context:
space:
mode:
authorMark Brown <broonie@kernel.org>2022-05-17 18:19:35 +0100
committerMark Brown <broonie@kernel.org>2022-05-17 18:19:35 +0100
commitec432e2a51412bde3d2d9fa785d3812f55ccd696 (patch)
tree7a7aa98f38ceb78129d0a26bccd437f8161269ce /sound/soc/intel/avs/board_selection.c
parent37a86b32bf0e5c5ca23567d7b120306b9ac8497d (diff)
parentc8c960c109716d96cfd1de65396fb8021eed4202 (diff)
downloadlinux-ec432e2a51412bde3d2d9fa785d3812f55ccd696.tar.gz
ASoC: Intel: avs: Driver core and PCM operations
Merge series from Cezary Rojewski <cezary.rojewski@intel.com>: Part three of main AVS driver series. This series was originally part of the initial series which was later divided [1] into smaller, easier-to-review chunks. Thus, many patches found here were already present on the list. This series consists of code typical to many drivers - PCI driver operations, trace ability, PM operations - as well as PCM handlers for all standard audio interfaces, that is, HDA, I2S and DMIC are found here. Series starts with updating firmware boot flow - libraries are no longer ignored. This change is dependent on already merged topology code [2] and because of that could not be part of the initial series [1]. PCM operations are split into four changes. First component operations alone i.e. operations which are usually agnostic towards path position (FE/BE). Then it continues with "generic" FE operations - there is no interface split here as from Intel ADSP point of view, FE, or HOST side as it's called in the specs, involves HD-Audio operations only. BE (also known as LINK) side on the other hand is divided into "non-HD-Audio" and HD-Audio part. The former represents transfer over DMIC and I2S interfaces both. While patches implementing standard PCI driver operations along (again standard) HD-Audio initialization routines followed up by power management handlers are two major ones, series covers also other important subjects such as: While patches implementing standard PCI driver operations along (again standard) HD-Audio initialization routines followed up by power management handlers are two major ones, series covers also other important subjects such as: - event tracing - preparation for firmware tracing (debugability) - coredump (debugability) - recovery flow (attempt recovery after IPC timeout or exception) - D0ix (D0 device substate, complements standard power management) Series is finalized by actual addition of supported platforms: SKL and APL-based. Platform-specific files are limited to firmware-specific bits, that is, bits that are specific to given firmware generation. Everything else is shared and is part of already upstream messaging code found in ipc.c, messages.c and messages.h files. Changes in v3: - addressed (hopefully) trace-code compilation under .configs with CONFIG_FTRACE dropped Changes in v2: - usage of avs_releast_last_firmware() dropped in error path for library loading procedure as suggested by Pierre - 'link_mask' usage replaced with 'i2s_link_mask' as requested by Pierre. Existing code addressed with new patch: "ASoC: Intel: avs: Replace link_mask usage with i2s_link_mask" - fixed possible race during recovery flow (->recovering flag is now atomic and tested in single location only). - dropped platform prefixes for basically all i2s board descriptors - 'ssp_test' renamed to 'i2s_test' to match naming convention of other boards - simplified PM implementation for current series, 'low_power' bits moved to future series - replaced SND_INTEL_DSP_DRIVER_SST with _AVS as suggested by Mark. Required changes to intel-dspcfg will be added in future series - number of typos across commit messages addressed [1]: https://lore.kernel.org/all/20220311153544.136854-1-cezary.rojewski@intel.com/ [2]: https://lore.kernel.org/all/20220331135246.993089-1-cezary.rojewski@intel.com/ Cezary Rojewski (14): ASoC: Intel: avs: Account for libraries when booting basefw ASoC: Intel: avs: Generic soc component driver ASoC: Intel: avs: Generic PCM FE operations ASoC: Intel: avs: non-HDA PCM BE operations ASoC: Intel: avs: HDA PCM BE operations ASoC: Intel: avs: Coredump and recovery flow ASoC: Intel: avs: Prepare for firmware tracing ASoC: Intel: avs: D0ix power state support ASoC: Intel: avs: Event tracing ASoC: Intel: avs: Machine board registration ASoC: Intel: avs: PCI driver implementation ASoC: Intel: avs: Power management ASoC: Intel: avs: SKL-based platforms support ASoC: Intel: avs: APL-based platforms support Piotr Maziarz (1): ASoC: Intel: avs: Replace link_mask usage with i2s_link_mask include/sound/intel-dsp-config.h | 3 +- include/sound/soc-acpi.h | 2 + sound/soc/intel/Kconfig | 2 + sound/soc/intel/avs/Makefile | 7 +- sound/soc/intel/avs/apl.c | 250 ++++++ sound/soc/intel/avs/avs.h | 79 ++ sound/soc/intel/avs/board_selection.c | 501 +++++++++++ sound/soc/intel/avs/core.c | 631 +++++++++++++ sound/soc/intel/avs/dsp.c | 27 +- sound/soc/intel/avs/ipc.c | 253 +++++- sound/soc/intel/avs/loader.c | 84 ++ sound/soc/intel/avs/messages.c | 35 +- sound/soc/intel/avs/messages.h | 51 ++ sound/soc/intel/avs/pcm.c | 1182 +++++++++++++++++++++++++ sound/soc/intel/avs/registers.h | 8 + sound/soc/intel/avs/skl.c | 125 +++ sound/soc/intel/avs/topology.c | 14 +- sound/soc/intel/avs/trace.c | 33 + sound/soc/intel/avs/trace.h | 154 ++++ sound/soc/intel/avs/utils.c | 23 + 20 files changed, 3443 insertions(+), 21 deletions(-) create mode 100644 sound/soc/intel/avs/apl.c create mode 100644 sound/soc/intel/avs/board_selection.c create mode 100644 sound/soc/intel/avs/pcm.c create mode 100644 sound/soc/intel/avs/skl.c create mode 100644 sound/soc/intel/avs/trace.c create mode 100644 sound/soc/intel/avs/trace.h -- 2.25.1
Diffstat (limited to 'sound/soc/intel/avs/board_selection.c')
-rw-r--r--sound/soc/intel/avs/board_selection.c501
1 files changed, 501 insertions, 0 deletions
diff --git a/sound/soc/intel/avs/board_selection.c b/sound/soc/intel/avs/board_selection.c
new file mode 100644
index 000000000000..80cb0164678a
--- /dev/null
+++ b/sound/soc/intel/avs/board_selection.c
@@ -0,0 +1,501 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2021-2022 Intel Corporation. All rights reserved.
+//
+// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+//
+
+#include <linux/acpi.h>
+#include <linux/module.h>
+#include <linux/dmi.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <sound/hda_codec.h>
+#include <sound/hda_register.h>
+#include <sound/intel-nhlt.h>
+#include <sound/soc-acpi.h>
+#include <sound/soc-component.h>
+#include "avs.h"
+
+static bool i2s_test;
+module_param(i2s_test, bool, 0444);
+MODULE_PARM_DESC(i2s_test, "Probe I2S test-board and skip all other I2S boards");
+
+static const struct dmi_system_id kbl_dmi_table[] = {
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),
+ DMI_MATCH(DMI_BOARD_NAME, "Skylake Y LPDDR3 RVP3"),
+ },
+ },
+ {}
+};
+
+static const struct dmi_system_id kblr_dmi_table[] = {
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),
+ DMI_MATCH(DMI_BOARD_NAME, "Kabylake R DDR4 RVP"),
+ },
+ },
+ {}
+};
+
+static struct snd_soc_acpi_mach *dmi_match_quirk(void *arg)
+{
+ struct snd_soc_acpi_mach *mach = arg;
+ const struct dmi_system_id *dmi_id;
+ struct dmi_system_id *dmi_table;
+
+ if (mach->quirk_data == NULL)
+ return mach;
+
+ dmi_table = (struct dmi_system_id *)mach->quirk_data;
+
+ dmi_id = dmi_first_match(dmi_table);
+ if (!dmi_id)
+ return NULL;
+
+ return mach;
+}
+
+#define AVS_SSP(x) (BIT(x))
+#define AVS_SSP_RANGE(a, b) (GENMASK(b, a))
+
+/* supported I2S board codec configurations */
+static struct snd_soc_acpi_mach avs_skl_i2s_machines[] = {
+ {
+ .id = "INT343A",
+ .drv_name = "avs_rt286",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(0),
+ },
+ .tplg_filename = "rt286-tplg.bin",
+ },
+ {
+ .id = "10508825",
+ .drv_name = "avs_nau8825",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(1),
+ },
+ .tplg_filename = "nau8825-tplg.bin",
+ },
+ {
+ .id = "INT343B",
+ .drv_name = "avs_ssm4567",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(0),
+ },
+ .tplg_filename = "ssm4567-tplg.bin",
+ },
+ {
+ .id = "MX98357A",
+ .drv_name = "avs_max98357a",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(0),
+ },
+ .tplg_filename = "max98357a-tplg.bin",
+ },
+ {},
+};
+
+static struct snd_soc_acpi_mach avs_kbl_i2s_machines[] = {
+ {
+ .id = "INT343A",
+ .drv_name = "avs_rt286",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(0),
+ },
+ .quirk_data = &kbl_dmi_table,
+ .machine_quirk = dmi_match_quirk,
+ .tplg_filename = "rt286-tplg.bin",
+ },
+ {
+ .id = "INT343A",
+ .drv_name = "avs_rt298",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(0),
+ },
+ .quirk_data = &kblr_dmi_table,
+ .machine_quirk = dmi_match_quirk,
+ .tplg_filename = "rt298-tplg.bin",
+ },
+ {
+ .id = "MX98373",
+ .drv_name = "avs_max98373",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(0),
+ },
+ .tplg_filename = "max98373-tplg.bin",
+ },
+ {
+ .id = "DLGS7219",
+ .drv_name = "avs_da7219",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(1),
+ },
+ .tplg_filename = "da7219-tplg.bin",
+ },
+ {},
+};
+
+static struct snd_soc_acpi_mach avs_apl_i2s_machines[] = {
+ {
+ .id = "INT343A",
+ .drv_name = "avs_rt298",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(5),
+ },
+ .tplg_filename = "rt298-tplg.bin",
+ },
+ {
+ .id = "INT34C3",
+ .drv_name = "avs_tdf8532",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP_RANGE(0, 5),
+ },
+ .pdata = (unsigned long[]){ 0, 0, 0x14, 0, 0, 0 }, /* SSP2 TDMs */
+ .tplg_filename = "tdf8532-tplg.bin",
+ },
+ {
+ .id = "MX98357A",
+ .drv_name = "avs_max98357a",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(5),
+ },
+ .tplg_filename = "max98357a-tplg.bin",
+ },
+ {
+ .id = "DLGS7219",
+ .drv_name = "avs_da7219",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(1),
+ },
+ .tplg_filename = "da7219-tplg.bin",
+ },
+ {},
+};
+
+static struct snd_soc_acpi_mach avs_gml_i2s_machines[] = {
+ {
+ .id = "INT343A",
+ .drv_name = "avs_rt298",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(2),
+ },
+ .tplg_filename = "rt298-tplg.bin",
+ },
+ {},
+};
+
+static struct snd_soc_acpi_mach avs_test_i2s_machines[] = {
+ {
+ .drv_name = "avs_i2s_test",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(0),
+ },
+ .tplg_filename = "i2s-test-tplg.bin",
+ },
+ {
+ .drv_name = "avs_i2s_test",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(1),
+ },
+ .tplg_filename = "i2s-test-tplg.bin",
+ },
+ {
+ .drv_name = "avs_i2s_test",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(2),
+ },
+ .tplg_filename = "i2s-test-tplg.bin",
+ },
+ {
+ .drv_name = "avs_i2s_test",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(3),
+ },
+ .tplg_filename = "i2s-test-tplg.bin",
+ },
+ {
+ .drv_name = "avs_i2s_test",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(4),
+ },
+ .tplg_filename = "i2s-test-tplg.bin",
+ },
+ {
+ .drv_name = "avs_i2s_test",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(5),
+ },
+ .tplg_filename = "i2s-test-tplg.bin",
+ },
+ /* no NULL terminator, as we depend on ARRAY SIZE due to .id == NULL */
+};
+
+struct avs_acpi_boards {
+ int id;
+ struct snd_soc_acpi_mach *machs;
+};
+
+#define AVS_MACH_ENTRY(_id, _mach) \
+ { .id = (_id), .machs = (_mach), }
+
+/* supported I2S boards per platform */
+static const struct avs_acpi_boards i2s_boards[] = {
+ AVS_MACH_ENTRY(0x9d70, avs_skl_i2s_machines), /* SKL */
+ AVS_MACH_ENTRY(0x9d71, avs_kbl_i2s_machines), /* KBL */
+ AVS_MACH_ENTRY(0x5a98, avs_apl_i2s_machines), /* APL */
+ AVS_MACH_ENTRY(0x3198, avs_gml_i2s_machines), /* GML */
+ {},
+};
+
+static const struct avs_acpi_boards *avs_get_i2s_boards(struct avs_dev *adev)
+{
+ int id, i;
+
+ id = adev->base.pci->device;
+ for (i = 0; i < ARRAY_SIZE(i2s_boards); i++)
+ if (i2s_boards[i].id == id)
+ return &i2s_boards[i];
+ return NULL;
+}
+
+/* platform devices owned by AVS audio are removed with this hook */
+static void board_pdev_unregister(void *data)
+{
+ platform_device_unregister(data);
+}
+
+static int avs_register_dmic_board(struct avs_dev *adev)
+{
+ struct platform_device *codec, *board;
+ struct snd_soc_acpi_mach mach = {{0}};
+ int ret;
+
+ if (!adev->nhlt ||
+ !intel_nhlt_has_endpoint_type(adev->nhlt, NHLT_LINK_DMIC)) {
+ dev_dbg(adev->dev, "no DMIC endpoints present\n");
+ return 0;
+ }
+
+ codec = platform_device_register_simple("dmic-codec", PLATFORM_DEVID_NONE, NULL, 0);
+ if (IS_ERR(codec)) {
+ dev_err(adev->dev, "dmic codec register failed\n");
+ return PTR_ERR(codec);
+ }
+
+ ret = devm_add_action(adev->dev, board_pdev_unregister, codec);
+ if (ret < 0) {
+ platform_device_unregister(codec);
+ return ret;
+ }
+
+ ret = avs_dmic_platform_register(adev, "dmic-platform");
+ if (ret < 0)
+ return ret;
+
+ mach.tplg_filename = "dmic-tplg.bin";
+ mach.mach_params.platform = "dmic-platform";
+
+ board = platform_device_register_data(NULL, "avs_dmic", PLATFORM_DEVID_NONE,
+ (const void *)&mach, sizeof(mach));
+ if (IS_ERR(board)) {
+ dev_err(adev->dev, "dmic board register failed\n");
+ return PTR_ERR(board);
+ }
+
+ ret = devm_add_action(adev->dev, board_pdev_unregister, board);
+ if (ret < 0) {
+ platform_device_unregister(board);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int avs_register_i2s_board(struct avs_dev *adev, struct snd_soc_acpi_mach *mach)
+{
+ struct platform_device *board;
+ int num_ssps;
+ char *name;
+ int ret;
+
+ num_ssps = adev->hw_cfg.i2s_caps.ctrl_count;
+ if (fls(mach->mach_params.i2s_link_mask) > num_ssps) {
+ dev_err(adev->dev, "Platform supports %d SSPs but board %s requires SSP%ld\n",
+ num_ssps, mach->drv_name, __fls(mach->mach_params.i2s_link_mask));
+ return -ENODEV;
+ }
+
+ name = devm_kasprintf(adev->dev, GFP_KERNEL, "%s.%d-platform", mach->drv_name,
+ mach->mach_params.i2s_link_mask);
+ if (!name)
+ return -ENOMEM;
+
+ ret = avs_i2s_platform_register(adev, name, mach->mach_params.i2s_link_mask, mach->pdata);
+ if (ret < 0)
+ return ret;
+
+ mach->mach_params.platform = name;
+
+ board = platform_device_register_data(NULL, mach->drv_name, mach->mach_params.i2s_link_mask,
+ (const void *)mach, sizeof(*mach));
+ if (IS_ERR(board)) {
+ dev_err(adev->dev, "ssp board register failed\n");
+ return PTR_ERR(board);
+ }
+
+ ret = devm_add_action(adev->dev, board_pdev_unregister, board);
+ if (ret < 0) {
+ platform_device_unregister(board);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int avs_register_i2s_boards(struct avs_dev *adev)
+{
+ const struct avs_acpi_boards *boards;
+ struct snd_soc_acpi_mach *mach;
+ int ret;
+
+ if (!adev->nhlt || !intel_nhlt_has_endpoint_type(adev->nhlt, NHLT_LINK_SSP)) {
+ dev_dbg(adev->dev, "no I2S endpoints present\n");
+ return 0;
+ }
+
+ if (i2s_test) {
+ int i, num_ssps;
+
+ num_ssps = adev->hw_cfg.i2s_caps.ctrl_count;
+ /* constrain just in case FW says there can be more SSPs than possible */
+ num_ssps = min_t(int, ARRAY_SIZE(avs_test_i2s_machines), num_ssps);
+
+ mach = avs_test_i2s_machines;
+
+ for (i = 0; i < num_ssps; i++) {
+ ret = avs_register_i2s_board(adev, &mach[i]);
+ if (ret < 0)
+ dev_warn(adev->dev, "register i2s %s failed: %d\n", mach->drv_name,
+ ret);
+ }
+ return 0;
+ }
+
+ boards = avs_get_i2s_boards(adev);
+ if (!boards) {
+ dev_dbg(adev->dev, "no I2S endpoints supported\n");
+ return 0;
+ }
+
+ for (mach = boards->machs; mach->id[0]; mach++) {
+ if (!acpi_dev_present(mach->id, NULL, -1))
+ continue;
+
+ if (mach->machine_quirk)
+ if (!mach->machine_quirk(mach))
+ continue;
+
+ ret = avs_register_i2s_board(adev, mach);
+ if (ret < 0)
+ dev_warn(adev->dev, "register i2s %s failed: %d\n", mach->drv_name, ret);
+ }
+
+ return 0;
+}
+
+static int avs_register_hda_board(struct avs_dev *adev, struct hda_codec *codec)
+{
+ struct snd_soc_acpi_mach mach = {{0}};
+ struct platform_device *board;
+ struct hdac_device *hdev = &codec->core;
+ char *pname;
+ int ret, id;
+
+ pname = devm_kasprintf(adev->dev, GFP_KERNEL, "%s-platform", dev_name(&hdev->dev));
+ if (!pname)
+ return -ENOMEM;
+
+ ret = avs_hda_platform_register(adev, pname);
+ if (ret < 0)
+ return ret;
+
+ mach.pdata = codec;
+ mach.mach_params.platform = pname;
+ mach.tplg_filename = devm_kasprintf(adev->dev, GFP_KERNEL, "hda-%08x-tplg.bin",
+ hdev->vendor_id);
+ if (!mach.tplg_filename)
+ return -ENOMEM;
+
+ id = adev->base.core.idx * HDA_MAX_CODECS + hdev->addr;
+ board = platform_device_register_data(NULL, "avs_hdaudio", id, (const void *)&mach,
+ sizeof(mach));
+ if (IS_ERR(board)) {
+ dev_err(adev->dev, "hda board register failed\n");
+ return PTR_ERR(board);
+ }
+
+ ret = devm_add_action(adev->dev, board_pdev_unregister, board);
+ if (ret < 0) {
+ platform_device_unregister(board);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int avs_register_hda_boards(struct avs_dev *adev)
+{
+ struct hdac_bus *bus = &adev->base.core;
+ struct hdac_device *hdev;
+ int ret;
+
+ if (!bus->num_codecs) {
+ dev_dbg(adev->dev, "no HDA endpoints present\n");
+ return 0;
+ }
+
+ list_for_each_entry(hdev, &bus->codec_list, list) {
+ struct hda_codec *codec;
+
+ codec = dev_to_hda_codec(&hdev->dev);
+
+ ret = avs_register_hda_board(adev, codec);
+ if (ret < 0)
+ dev_warn(adev->dev, "register hda-%08x failed: %d\n",
+ codec->core.vendor_id, ret);
+ }
+
+ return 0;
+}
+
+int avs_register_all_boards(struct avs_dev *adev)
+{
+ int ret;
+
+ ret = avs_register_dmic_board(adev);
+ if (ret < 0)
+ dev_warn(adev->dev, "enumerate DMIC endpoints failed: %d\n",
+ ret);
+
+ ret = avs_register_i2s_boards(adev);
+ if (ret < 0)
+ dev_warn(adev->dev, "enumerate I2S endpoints failed: %d\n",
+ ret);
+
+ ret = avs_register_hda_boards(adev);
+ if (ret < 0)
+ dev_warn(adev->dev, "enumerate HDA endpoints failed: %d\n",
+ ret);
+
+ return 0;
+}
+
+void avs_unregister_all_boards(struct avs_dev *adev)
+{
+ snd_soc_unregister_component(adev->dev);
+}