diff options
Diffstat (limited to 'sound/soc/intel/avs')
55 files changed, 2763 insertions, 1069 deletions
diff --git a/sound/soc/intel/avs/Makefile b/sound/soc/intel/avs/Makefile index 5480500337f8..576dc0da381d 100644 --- a/sound/soc/intel/avs/Makefile +++ b/sound/soc/intel/avs/Makefile @@ -1,17 +1,17 @@ # SPDX-License-Identifier: GPL-2.0-only -snd-soc-avs-objs := dsp.o ipc.o messages.o utils.o core.o loader.o \ - topology.o path.o pcm.o board_selection.o control.o \ - sysfs.o -snd-soc-avs-objs += cldma.o -snd-soc-avs-objs += skl.o apl.o cnl.o icl.o tgl.o +snd-soc-avs-y := dsp.o ipc.o messages.o utils.o core.o loader.o \ + topology.o path.o pcm.o board_selection.o control.o \ + sysfs.o +snd-soc-avs-y += cldma.o +snd-soc-avs-y += skl.o apl.o cnl.o icl.o tgl.o mtl.o lnl.o ptl.o -snd-soc-avs-objs += trace.o +snd-soc-avs-y += trace.o # tell define_trace.h where to find the trace header CFLAGS_trace.o := -I$(src) ifneq ($(CONFIG_DEBUG_FS),) -snd-soc-avs-objs += probes.o debugfs.o +snd-soc-avs-y += probes.o debugfs.o endif obj-$(CONFIG_SND_SOC_INTEL_AVS) += snd-soc-avs.o diff --git a/sound/soc/intel/avs/apl.c b/sound/soc/intel/avs/apl.c index c21ecaef9eba..3dccf0a57a3a 100644 --- a/sound/soc/intel/avs/apl.c +++ b/sound/soc/intel/avs/apl.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only // -// Copyright(c) 2021-2022 Intel Corporation. All rights reserved. +// Copyright(c) 2021-2022 Intel Corporation // // Authors: Cezary Rojewski <cezary.rojewski@intel.com> // Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> @@ -8,11 +8,29 @@ #include <linux/devcoredump.h> #include <linux/slab.h> +#include <sound/hdaudio_ext.h> #include "avs.h" #include "messages.h" #include "path.h" +#include "registers.h" #include "topology.h" +static irqreturn_t avs_apl_dsp_interrupt(struct avs_dev *adev) +{ + u32 adspis = snd_hdac_adsp_readl(adev, AVS_ADSP_REG_ADSPIS); + irqreturn_t ret = IRQ_NONE; + + if (adspis == UINT_MAX) + return ret; + + if (adspis & AVS_ADSP_ADSPIS_IPC) { + avs_skl_ipc_interrupt(adev); + ret = IRQ_HANDLED; + } + + return ret; +} + #ifdef CONFIG_DEBUG_FS int avs_apl_enable_logs(struct avs_dev *adev, enum avs_log_enable enable, u32 aging_period, u32 fifo_full_period, unsigned long resource_mask, u32 *priorities) @@ -108,7 +126,7 @@ int avs_apl_coredump(struct avs_dev *adev, union avs_notify_msg *msg) struct avs_apl_log_buffer_layout layout; void __iomem *addr, *buf; size_t dump_size; - u16 offset = 0; + u32 offset = 0; u8 *dump, *pos; dump_size = AVS_FW_REGS_SIZE + msg->ext.coredump.stack_dump_size; @@ -237,8 +255,7 @@ const struct avs_dsp_ops avs_apl_dsp_ops = { .power = avs_dsp_core_power, .reset = avs_dsp_core_reset, .stall = avs_dsp_core_stall, - .irq_handler = avs_irq_handler, - .irq_thread = avs_skl_irq_thread, + .dsp_interrupt = avs_apl_dsp_interrupt, .int_control = avs_dsp_interrupt_control, .load_basefw = avs_hda_load_basefw, .load_lib = avs_hda_load_library, diff --git a/sound/soc/intel/avs/avs.h b/sound/soc/intel/avs/avs.h index f80f79415344..4c096afc5848 100644 --- a/sound/soc/intel/avs/avs.h +++ b/sound/soc/intel/avs/avs.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright(c) 2021-2022 Intel Corporation. All rights reserved. + * Copyright(c) 2021-2022 Intel Corporation * * Authors: Cezary Rojewski <cezary.rojewski@intel.com> * Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> @@ -46,12 +46,12 @@ struct avs_dsp_ops { int (* const power)(struct avs_dev *, u32, bool); int (* const reset)(struct avs_dev *, u32, bool); int (* const stall)(struct avs_dev *, u32, bool); - irqreturn_t (* const irq_handler)(struct avs_dev *); - irqreturn_t (* const irq_thread)(struct avs_dev *); + irqreturn_t (* const dsp_interrupt)(struct avs_dev *); void (* const int_control)(struct avs_dev *, bool); int (* const load_basefw)(struct avs_dev *, struct firmware *); int (* const load_lib)(struct avs_dev *, struct firmware *, u32); int (* const transfer_mods)(struct avs_dev *, bool, struct avs_module_entry *, u32); + int (* const config_basefw)(struct avs_dev *); int (* const enable_logs)(struct avs_dev *, enum avs_log_enable, u32, u32, unsigned long, u32 *); int (* const log_buffer_offset)(struct avs_dev *, u32); @@ -69,9 +69,12 @@ extern const struct avs_dsp_ops avs_apl_dsp_ops; extern const struct avs_dsp_ops avs_cnl_dsp_ops; extern const struct avs_dsp_ops avs_icl_dsp_ops; extern const struct avs_dsp_ops avs_tgl_dsp_ops; +extern const struct avs_dsp_ops avs_ptl_dsp_ops; #define AVS_PLATATTR_CLDMA BIT_ULL(0) #define AVS_PLATATTR_IMR BIT_ULL(1) +#define AVS_PLATATTR_ACE BIT_ULL(2) +#define AVS_PLATATTR_ALTHDA BIT_ULL(3) #define avs_platattr_test(adev, attr) \ ((adev)->spec->attributes & AVS_PLATATTR_##attr) @@ -79,7 +82,6 @@ extern const struct avs_dsp_ops avs_tgl_dsp_ops; struct avs_sram_spec { const u32 base_offset; const u32 window_size; - const u32 rom_status_offset; }; struct avs_hipc_spec { @@ -91,6 +93,7 @@ struct avs_hipc_spec { const u32 rsp_offset; const u32 rsp_busy_mask; const u32 ctl_offset; + const u32 sts_offset; }; /* Platform specific descriptor */ @@ -107,7 +110,7 @@ struct avs_spec { }; struct avs_fw_entry { - char *name; + const char *name; const struct firmware *fw; struct list_head node; @@ -151,7 +154,6 @@ struct avs_dev { struct completion fw_ready; struct work_struct probe_work; - struct nhlt_acpi_table *nhlt; struct list_head comp_list; struct mutex comp_list_mutex; struct list_head path_list; @@ -245,7 +247,6 @@ struct avs_ipc { #define AVS_IPC_RET(ret) \ (((ret) <= 0) ? (ret) : -AVS_EIPC) -irqreturn_t avs_irq_handler(struct avs_dev *adev); void avs_dsp_process_response(struct avs_dev *adev, u64 header); int avs_dsp_send_msg_timeout(struct avs_dev *adev, struct avs_ipc_msg *request, struct avs_ipc_msg *reply, int timeout, const char *name); @@ -267,8 +268,14 @@ void avs_ipc_block(struct avs_ipc *ipc); int avs_dsp_disable_d0ix(struct avs_dev *adev); int avs_dsp_enable_d0ix(struct avs_dev *adev); -irqreturn_t avs_skl_irq_thread(struct avs_dev *adev); -irqreturn_t avs_cnl_irq_thread(struct avs_dev *adev); +int avs_mtl_core_power(struct avs_dev *adev, u32 core_mask, bool power); +int avs_mtl_core_reset(struct avs_dev *adev, u32 core_mask, bool power); +int avs_mtl_core_stall(struct avs_dev *adev, u32 core_mask, bool stall); +int avs_lnl_core_stall(struct avs_dev *adev, u32 core_mask, bool stall); +void avs_mtl_interrupt_control(struct avs_dev *adev, bool enable); +void avs_skl_ipc_interrupt(struct avs_dev *adev); +irqreturn_t avs_cnl_dsp_interrupt(struct avs_dev *adev); +irqreturn_t avs_mtl_dsp_interrupt(struct avs_dev *adev); int avs_apl_enable_logs(struct avs_dev *adev, enum avs_log_enable enable, u32 aging_period, u32 fifo_full_period, unsigned long resource_mask, u32 *priorities); int avs_icl_enable_logs(struct avs_dev *adev, enum avs_log_enable enable, u32 aging_period, @@ -342,7 +349,7 @@ struct avs_soc_component { extern const struct snd_soc_dai_ops avs_dai_fe_ops; int avs_soc_component_register(struct device *dev, const char *name, - const struct snd_soc_component_driver *drv, + struct snd_soc_component_driver *drv, struct snd_soc_dai_driver *cpu_dais, int num_cpu_dais); int avs_dmic_platform_register(struct avs_dev *adev, const char *name); int avs_i2s_platform_register(struct avs_dev *adev, const char *name, unsigned long port_mask, @@ -381,6 +388,7 @@ struct avs_apl_log_buffer_layout { u32 write_ptr; u8 buffer[]; } __packed; +static_assert(sizeof(struct avs_apl_log_buffer_layout) == 8); #define avs_apl_log_payload_size(adev) \ (avs_log_buffer_size(adev) - sizeof(struct avs_apl_log_buffer_layout)) diff --git a/sound/soc/intel/avs/board_selection.c b/sound/soc/intel/avs/board_selection.c index 8360ce557401..673ccf162023 100644 --- a/sound/soc/intel/avs/board_selection.c +++ b/sound/soc/intel/avs/board_selection.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only // -// Copyright(c) 2021-2022 Intel Corporation. All rights reserved. +// Copyright(c) 2021-2022 Intel Corporation // // Authors: Cezary Rojewski <cezary.rojewski@intel.com> // Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> @@ -10,17 +10,22 @@ #include <linux/module.h> #include <linux/dmi.h> #include <linux/pci.h> +#include <acpi/nhlt.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" +#include "utils.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 char *i2s_test; +module_param(i2s_test, charp, 0444); +MODULE_PARM_DESC(i2s_test, "Use I2S test-board instead of ACPI, i2s_test=ssp0tdm,ssp1tdm,... 0 to ignore port"); + +bool obsolete_card_names = IS_ENABLED(CONFIG_SND_SOC_INTEL_AVS_CARDNAME_OBSOLETE); +module_param_named(obsolete_card_names, obsolete_card_names, bool, 0444); +MODULE_PARM_DESC(obsolete_card_names, "Use obsolete card names 0=no, 1=yes"); static const struct dmi_system_id kbl_dmi_table[] = { { @@ -141,7 +146,7 @@ static struct snd_soc_acpi_mach avs_kbl_i2s_machines[] = { .mach_params = { .i2s_link_mask = AVS_SSP(0), }, - .pdata = (unsigned long[]){ 0x2, 0, 0, 0, 0, 0 }, /* SSP0 TDMs */ + .pdata = (struct avs_mach_pdata[]){ { .tdms = (unsigned long[]){ 0x2 } } }, .tplg_filename = "rt5514-tplg.bin", }, { @@ -202,7 +207,9 @@ static struct snd_soc_acpi_mach avs_apl_i2s_machines[] = { .mach_params = { .i2s_link_mask = AVS_SSP_RANGE(0, 5), }, - .pdata = (unsigned long[]){ 0x1, 0x1, 0x14, 0x1, 0x1, 0x1 }, /* SSP2 TDMs */ + .pdata = (struct avs_mach_pdata[]){ { + .tdms = (unsigned long[]){ 0x1, 0x1, 0x14, 0x1, 0x1, 0x1 } + } }, .tplg_filename = "tdf8532-tplg.bin", }, { @@ -312,50 +319,16 @@ static struct snd_soc_acpi_mach avs_tgl_i2s_machines[] = { {}, }; -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", - }, +static struct snd_soc_acpi_mach avs_mbl_i2s_machines[] = { { - .drv_name = "avs_i2s_test", + .id = "PCM3168A", + .drv_name = "avs_pcm3168a", .mach_params = { - .i2s_link_mask = AVS_SSP(3), + .i2s_link_mask = AVS_SSP(0) | AVS_SSP(2), }, - .tplg_filename = "i2s-test-tplg.bin", + .tplg_filename = "pcm3168a-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 { @@ -378,10 +351,12 @@ static const struct avs_acpi_boards i2s_boards[] = { AVS_MACH_ENTRY(HDA_ICL_LP, avs_icl_i2s_machines), AVS_MACH_ENTRY(HDA_TGL_LP, avs_tgl_i2s_machines), AVS_MACH_ENTRY(HDA_EHL_0, avs_tgl_i2s_machines), + AVS_MACH_ENTRY(HDA_ADL_N, avs_mbl_i2s_machines), AVS_MACH_ENTRY(HDA_ADL_P, avs_tgl_i2s_machines), AVS_MACH_ENTRY(HDA_RPL_P_0, avs_tgl_i2s_machines), - AVS_MACH_ENTRY(HDA_RPL_M, avs_tgl_i2s_machines), - {}, + AVS_MACH_ENTRY(HDA_RPL_M, avs_mbl_i2s_machines), + AVS_MACH_ENTRY(HDA_FCL, avs_tgl_i2s_machines), + { }, }; static const struct avs_acpi_boards *avs_get_i2s_boards(struct avs_dev *adev) @@ -432,10 +407,10 @@ static int avs_register_dmic_board(struct avs_dev *adev) { struct platform_device *codec, *board; struct snd_soc_acpi_mach mach = {{0}}; + struct avs_mach_pdata *pdata; int ret; - if (!adev->nhlt || - !intel_nhlt_has_endpoint_type(adev->nhlt, NHLT_LINK_DMIC)) { + if (!acpi_nhlt_find_endpoint(ACPI_NHLT_LINKTYPE_PDM, -1, -1, -1)) { dev_dbg(adev->dev, "no DMIC endpoints present\n"); return 0; } @@ -456,6 +431,11 @@ static int avs_register_dmic_board(struct avs_dev *adev) if (ret < 0) return ret; + pdata = devm_kzalloc(adev->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + pdata->obsolete_card_names = obsolete_card_names; + mach.pdata = pdata; mach.tplg_filename = "dmic-tplg.bin"; mach.mach_params.platform = "dmic-platform"; @@ -478,9 +458,11 @@ static int avs_register_dmic_board(struct avs_dev *adev) static int avs_register_i2s_board(struct avs_dev *adev, struct snd_soc_acpi_mach *mach) { struct platform_device *board; + struct avs_mach_pdata *pdata; int num_ssps; char *name; int ret; + int uid; num_ssps = adev->hw_cfg.i2s_caps.ctrl_count; if (fls(mach->mach_params.i2s_link_mask) > num_ssps) { @@ -490,18 +472,29 @@ static int avs_register_i2s_board(struct avs_dev *adev, struct snd_soc_acpi_mach return -ENODEV; } - name = devm_kasprintf(adev->dev, GFP_KERNEL, "%s.%d-platform", mach->drv_name, - mach->mach_params.i2s_link_mask); + pdata = mach->pdata; + if (!pdata) + pdata = devm_kzalloc(adev->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + pdata->obsolete_card_names = obsolete_card_names; + mach->pdata = pdata; + + uid = mach->mach_params.i2s_link_mask; + if (avs_mach_singular_ssp(mach)) + uid = (uid << AVS_CHANNELS_MAX) + avs_mach_ssp_tdm(mach, avs_mach_ssp_port(mach)); + + name = devm_kasprintf(adev->dev, GFP_KERNEL, "%s.%d-platform", mach->drv_name, uid); if (!name) return -ENOMEM; - ret = avs_i2s_platform_register(adev, name, mach->mach_params.i2s_link_mask, mach->pdata); + ret = avs_i2s_platform_register(adev, name, mach->mach_params.i2s_link_mask, pdata->tdms); 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, + board = platform_device_register_data(NULL, mach->drv_name, uid, (const void *)mach, sizeof(*mach)); if (IS_ERR(board)) { dev_err(adev->dev, "ssp board register failed\n"); @@ -517,35 +510,82 @@ static int avs_register_i2s_board(struct avs_dev *adev, struct snd_soc_acpi_mach return 0; } -static int avs_register_i2s_boards(struct avs_dev *adev) +static int avs_register_i2s_test_board(struct avs_dev *adev, int ssp_port, int tdm_slot) { - const struct avs_acpi_boards *boards; struct snd_soc_acpi_mach *mach; + int tdm_mask = BIT(tdm_slot); + unsigned long *tdm_cfg; + char *tplg_name; 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; + mach = devm_kzalloc(adev->dev, sizeof(*mach), GFP_KERNEL); + tdm_cfg = devm_kcalloc(adev->dev, ssp_port + 1, sizeof(unsigned long), GFP_KERNEL); + tplg_name = devm_kasprintf(adev->dev, GFP_KERNEL, AVS_STRING_FMT("i2s", "-test-tplg.bin", + ssp_port, tdm_slot)); + if (!mach || !tdm_cfg || !tplg_name) + return -ENOMEM; + + mach->drv_name = "avs_i2s_test"; + mach->mach_params.i2s_link_mask = AVS_SSP(ssp_port); + tdm_cfg[ssp_port] = tdm_mask; + mach->pdata = tdm_cfg; + mach->tplg_filename = tplg_name; + + 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 ret; } - if (i2s_test) { - int i, num_ssps; + return 0; +} - 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); +static int avs_register_i2s_test_boards(struct avs_dev *adev) +{ + int max_ssps = adev->hw_cfg.i2s_caps.ctrl_count; + int ssp_port, tdm_slot, ret; + unsigned long tdm_slots; + u32 *array, num_elems; + + ret = parse_int_array(i2s_test, strlen(i2s_test), (int **)&array); + if (ret) { + dev_err(adev->dev, "failed to parse i2s_test parameter\n"); + return ret; + } - mach = avs_test_i2s_machines; + num_elems = *array; + if (num_elems > max_ssps) { + dev_err(adev->dev, "board supports only %d SSP, %d specified\n", + max_ssps, num_elems); + return -EINVAL; + } - 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); + for (ssp_port = 0; ssp_port < num_elems; ssp_port++) { + tdm_slots = array[1 + ssp_port]; + for_each_set_bit(tdm_slot, &tdm_slots, 16) { + ret = avs_register_i2s_test_board(adev, ssp_port, tdm_slot); + if (ret) + 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 (!acpi_nhlt_find_endpoint(ACPI_NHLT_LINKTYPE_SSP, -1, -1, -1)) { + dev_dbg(adev->dev, "no I2S endpoints present\n"); return 0; } + if (i2s_test) + return avs_register_i2s_test_boards(adev); + boards = avs_get_i2s_boards(adev); if (!boards) { dev_dbg(adev->dev, "no I2S endpoints supported\n"); @@ -572,6 +612,7 @@ 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 avs_mach_pdata *pdata; struct hdac_device *hdev = &codec->core; char *pname; int ret, id; @@ -580,11 +621,17 @@ static int avs_register_hda_board(struct avs_dev *adev, struct hda_codec *codec) if (!pname) return -ENOMEM; + pdata = devm_kzalloc(adev->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + pdata->obsolete_card_names = obsolete_card_names; + pdata->codec = codec; + ret = avs_hda_platform_register(adev, pname); if (ret < 0) return ret; - mach.pdata = codec; + mach.pdata = pdata; mach.mach_params.platform = pname; mach.tplg_filename = devm_kasprintf(adev->dev, GFP_KERNEL, "hda-%08x-tplg.bin", hdev->vendor_id); diff --git a/sound/soc/intel/avs/boards/Kconfig b/sound/soc/intel/avs/boards/Kconfig index 00b0f6c176d6..8b654181004e 100644 --- a/sound/soc/intel/avs/boards/Kconfig +++ b/sound/soc/intel/avs/boards/Kconfig @@ -4,6 +4,14 @@ menu "Intel AVS Machine drivers" comment "Available DSP configurations" +config SND_SOC_INTEL_AVS_CARDNAME_OBSOLETE + bool "Use obsolete card names" + default n + help + Use obsolete names for some of avs cards. This option should be + used if your system depends on old card names, for example having + not up to date UCM files. + config SND_SOC_INTEL_AVS_MACH_DA7219 tristate "da7219 I2S board" depends on I2C @@ -87,6 +95,16 @@ config SND_SOC_INTEL_AVS_MACH_NAU8825 Say Y or m if you have such a device. This is a recommended option. If unsure select "N". +config SND_SOC_INTEL_AVS_MACH_PCM3168A + tristate "pcm3168a I2S board" + depends on I2C + depends on MFD_INTEL_LPSS || COMPILE_TEST + select SND_SOC_PCM3168A_I2C + help + This adds support for AVS with PCM3168A I2C codec configuration. + Say Y or m if you have such a device. This is a recommended option. + If unsure select "N". + config SND_SOC_INTEL_AVS_MACH_PROBE tristate "Probing (data) board" depends on DEBUG_FS diff --git a/sound/soc/intel/avs/boards/Makefile b/sound/soc/intel/avs/boards/Makefile index 0ff21d55be24..a95256b94dc8 100644 --- a/sound/soc/intel/avs/boards/Makefile +++ b/sound/soc/intel/avs/boards/Makefile @@ -1,22 +1,23 @@ # SPDX-License-Identifier: GPL-2.0-only -snd-soc-avs-da7219-objs := da7219.o -snd-soc-avs-dmic-objs := dmic.o -snd-soc-avs-es8336-objs := es8336.o -snd-soc-avs-hdaudio-objs := hdaudio.o -snd-soc-avs-i2s-test-objs := i2s_test.o -snd-soc-avs-max98927-objs := max98927.o -snd-soc-avs-max98357a-objs := max98357a.o -snd-soc-avs-max98373-objs := max98373.o -snd-soc-avs-nau8825-objs := nau8825.o -snd-soc-avs-probe-objs := probe.o -snd-soc-avs-rt274-objs := rt274.o -snd-soc-avs-rt286-objs := rt286.o -snd-soc-avs-rt298-objs := rt298.o -snd-soc-avs-rt5514-objs := rt5514.o -snd-soc-avs-rt5663-objs := rt5663.o -snd-soc-avs-rt5682-objs := rt5682.o -snd-soc-avs-ssm4567-objs := ssm4567.o +snd-soc-avs-da7219-y := da7219.o +snd-soc-avs-dmic-y := dmic.o +snd-soc-avs-es8336-y := es8336.o +snd-soc-avs-hdaudio-y := hdaudio.o +snd-soc-avs-i2s-test-y := i2s_test.o +snd-soc-avs-max98927-y := max98927.o +snd-soc-avs-max98357a-y := max98357a.o +snd-soc-avs-max98373-y := max98373.o +snd-soc-avs-nau8825-y := nau8825.o +snd-soc-avs-pcm3168a-y := pcm3168a.o +snd-soc-avs-probe-y := probe.o +snd-soc-avs-rt274-y := rt274.o +snd-soc-avs-rt286-y := rt286.o +snd-soc-avs-rt298-y := rt298.o +snd-soc-avs-rt5514-y := rt5514.o +snd-soc-avs-rt5663-y := rt5663.o +snd-soc-avs-rt5682-y := rt5682.o +snd-soc-avs-ssm4567-y := ssm4567.o obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_DA7219) += snd-soc-avs-da7219.o obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_DMIC) += snd-soc-avs-dmic.o @@ -27,6 +28,7 @@ obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_MAX98927) += snd-soc-avs-max98927.o obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_MAX98357A) += snd-soc-avs-max98357a.o obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_MAX98373) += snd-soc-avs-max98373.o obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_NAU8825) += snd-soc-avs-nau8825.o +obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_PCM3168A) += snd-soc-avs-pcm3168a.o obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_PROBE) += snd-soc-avs-probe.o obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_RT274) += snd-soc-avs-rt274.o obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_RT286) += snd-soc-avs-rt286.o diff --git a/sound/soc/intel/avs/boards/da7219.c b/sound/soc/intel/avs/boards/da7219.c index c018f84fe025..3ef0db254142 100644 --- a/sound/soc/intel/avs/boards/da7219.c +++ b/sound/soc/intel/avs/boards/da7219.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only // -// Copyright(c) 2021-2022 Intel Corporation. All rights reserved. +// Copyright(c) 2021-2022 Intel Corporation // // Author: Cezary Rojewski <cezary.rojewski@intel.com> // @@ -113,7 +113,8 @@ static int avs_da7219_codec_init(struct snd_soc_pcm_runtime *runtime) } num_pins = ARRAY_SIZE(card_headset_pins); - pins = devm_kmemdup(card->dev, card_headset_pins, sizeof(*pins) * num_pins, GFP_KERNEL); + pins = devm_kmemdup_array(card->dev, card_headset_pins, num_pins, + sizeof(card_headset_pins[0]), GFP_KERNEL); if (!pins) return -ENOMEM; @@ -197,39 +198,23 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in dl->platforms = platform; dl->num_platforms = 1; dl->id = 0; - dl->dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS; + dl->dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC; dl->be_hw_params_fixup = avs_da7219_be_fixup; dl->init = avs_da7219_codec_init; dl->exit = avs_da7219_codec_exit; dl->nonatomic = 1; dl->no_pcm = 1; - dl->dpcm_capture = 1; - dl->dpcm_playback = 1; *dai_link = dl; return 0; } -static int avs_card_suspend_pre(struct snd_soc_card *card) -{ - struct snd_soc_dai *codec_dai = snd_soc_card_get_codec_dai(card, DA7219_DAI_NAME); - - return snd_soc_component_set_jack(codec_dai->component, NULL, NULL); -} - -static int avs_card_resume_post(struct snd_soc_card *card) -{ - struct snd_soc_dai *codec_dai = snd_soc_card_get_codec_dai(card, DA7219_DAI_NAME); - struct snd_soc_jack *jack = snd_soc_card_get_drvdata(card); - - return snd_soc_component_set_jack(codec_dai->component, jack, NULL); -} - static int avs_da7219_probe(struct platform_device *pdev) { struct snd_soc_dai_link *dai_link; struct snd_soc_acpi_mach *mach; + struct avs_mach_pdata *pdata; struct snd_soc_card *card; struct snd_soc_jack *jack; struct device *dev = &pdev->dev; @@ -238,6 +223,7 @@ static int avs_da7219_probe(struct platform_device *pdev) mach = dev_get_platdata(dev); pname = mach->mach_params.platform; + pdata = mach->pdata; ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot); if (ret) @@ -254,11 +240,14 @@ static int avs_da7219_probe(struct platform_device *pdev) if (!jack || !card) return -ENOMEM; - card->name = "avs_da7219"; + if (pdata->obsolete_card_names) { + card->name = "avs_da7219"; + } else { + card->driver_name = "avs_da7219"; + card->long_name = card->name = "AVS I2S DA7219"; + } card->dev = dev; card->owner = THIS_MODULE; - card->suspend_pre = avs_card_suspend_pre; - card->resume_post = avs_card_resume_post; card->dai_link = dai_link; card->num_links = 1; card->controls = card_controls; @@ -274,7 +263,7 @@ static int avs_da7219_probe(struct platform_device *pdev) if (ret) return ret; - return devm_snd_soc_register_card(dev, card); + return devm_snd_soc_register_deferrable_card(dev, card); } static const struct platform_device_id avs_da7219_driver_ids[] = { @@ -296,5 +285,6 @@ static struct platform_driver avs_da7219_driver = { module_platform_driver(avs_da7219_driver); +MODULE_DESCRIPTION("Intel da7219 machine driver"); MODULE_AUTHOR("Cezary Rojewski <cezary.rojewski@intel.com>"); MODULE_LICENSE("GPL"); diff --git a/sound/soc/intel/avs/boards/dmic.c b/sound/soc/intel/avs/boards/dmic.c index ba2bc7f689eb..a1448a98874d 100644 --- a/sound/soc/intel/avs/boards/dmic.c +++ b/sound/soc/intel/avs/boards/dmic.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only // -// Copyright(c) 2021-2022 Intel Corporation. All rights reserved. +// Copyright(c) 2021-2022 Intel Corporation // // Authors: Cezary Rojewski <cezary.rojewski@intel.com> // Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> @@ -10,6 +10,7 @@ #include <linux/module.h> #include <sound/soc.h> #include <sound/soc-acpi.h> +#include "../utils.h" SND_SOC_DAILINK_DEF(dmic_pin, DAILINK_COMP_ARRAY(COMP_CPU("DMIC Pin"))); SND_SOC_DAILINK_DEF(dmic_wov_pin, DAILINK_COMP_ARRAY(COMP_CPU("DMIC WoV Pin"))); @@ -22,7 +23,7 @@ static struct snd_soc_dai_link card_dai_links[] = { { .name = "DMIC", .id = 0, - .dpcm_capture = 1, + .capture_only = 1, .nonatomic = 1, .no_pcm = 1, SND_SOC_DAILINK_REG(dmic_pin, dmic_codec, platform), @@ -30,7 +31,7 @@ static struct snd_soc_dai_link card_dai_links[] = { { .name = "DMIC WoV", .id = 1, - .dpcm_capture = 1, + .capture_only = 1, .nonatomic = 1, .no_pcm = 1, .ignore_suspend = 1, @@ -49,17 +50,24 @@ static const struct snd_soc_dapm_route card_routes[] = { static int avs_dmic_probe(struct platform_device *pdev) { struct snd_soc_acpi_mach *mach; + struct avs_mach_pdata *pdata; struct snd_soc_card *card; struct device *dev = &pdev->dev; int ret; mach = dev_get_platdata(dev); + pdata = mach->pdata; card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL); if (!card) return -ENOMEM; - card->name = "avs_dmic"; + if (pdata->obsolete_card_names) { + card->name = "avs_dmic"; + } else { + card->driver_name = "avs_dmic"; + card->long_name = card->name = "AVS DMIC"; + } card->dev = dev; card->owner = THIS_MODULE; card->dai_link = card_dai_links; @@ -74,7 +82,7 @@ static int avs_dmic_probe(struct platform_device *pdev) if (ret) return ret; - return devm_snd_soc_register_card(dev, card); + return devm_snd_soc_register_deferrable_card(dev, card); } static const struct platform_device_id avs_dmic_driver_ids[] = { @@ -96,4 +104,5 @@ static struct platform_driver avs_dmic_driver = { module_platform_driver(avs_dmic_driver); +MODULE_DESCRIPTION("Intel DMIC machine driver"); MODULE_LICENSE("GPL"); diff --git a/sound/soc/intel/avs/boards/es8336.c b/sound/soc/intel/avs/boards/es8336.c index 1090082e7d5b..1955f2d383c5 100644 --- a/sound/soc/intel/avs/boards/es8336.c +++ b/sound/soc/intel/avs/boards/es8336.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only // -// Copyright(c) 2023 Intel Corporation. All rights reserved. +// Copyright(c) 2023 Intel Corporation // // Authors: Cezary Rojewski <cezary.rojewski@intel.com> // Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> @@ -18,7 +18,7 @@ #include <sound/pcm_params.h> #include <sound/soc.h> #include <sound/soc-acpi.h> -#include <asm/intel-family.h> +#include <asm/cpu_device_id.h> #include "../utils.h" #define ES8336_CODEC_DAI "ES8316 HiFi" @@ -85,7 +85,7 @@ static const struct snd_kcontrol_new card_controls[] = { SOC_DAPM_PIN_SWITCH("Internal Mic"), }; -static struct snd_soc_jack_pin card_headset_pins[] = { +static const struct snd_soc_jack_pin card_headset_pins[] = { { .pin = "Headphone", .mask = SND_JACK_HEADPHONE, @@ -109,11 +109,12 @@ static int avs_es8336_codec_init(struct snd_soc_pcm_runtime *runtime) data = snd_soc_card_get_drvdata(card); num_pins = ARRAY_SIZE(card_headset_pins); - pins = devm_kmemdup(card->dev, card_headset_pins, sizeof(*pins) * num_pins, GFP_KERNEL); + pins = devm_kmemdup_array(card->dev, card_headset_pins, num_pins, + sizeof(card_headset_pins[0]), GFP_KERNEL); if (!pins) return -ENOMEM; - ret = snd_soc_card_jack_new_pins(card, "Headset", SND_JACK_HEADSET | SND_JACK_BTN_0, + ret = snd_soc_card_jack_new_pins(card, "Headset Jack", SND_JACK_HEADSET | SND_JACK_BTN_0, &data->jack, pins, num_pins); if (ret) return ret; @@ -153,9 +154,9 @@ static int avs_es8336_hw_params(struct snd_pcm_substream *substream, int clk_freq; int ret; - switch (boot_cpu_data.x86_model) { - case INTEL_FAM6_KABYLAKE_L: - case INTEL_FAM6_KABYLAKE: + switch (boot_cpu_data.x86_vfm) { + case INTEL_KABYLAKE_L: + case INTEL_KABYLAKE: clk_freq = 24000000; break; default: @@ -233,8 +234,6 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in dl->ops = &avs_es8336_ops; dl->nonatomic = 1; dl->no_pcm = 1; - dl->dpcm_capture = 1; - dl->dpcm_playback = 1; *dai_link = dl; @@ -260,6 +259,7 @@ static int avs_es8336_probe(struct platform_device *pdev) { struct snd_soc_dai_link *dai_link; struct snd_soc_acpi_mach *mach; + struct avs_mach_pdata *pdata; struct avs_card_drvdata *data; struct snd_soc_card *card; struct device *dev = &pdev->dev; @@ -268,6 +268,7 @@ static int avs_es8336_probe(struct platform_device *pdev) mach = dev_get_platdata(dev); pname = mach->mach_params.platform; + pdata = mach->pdata; ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot); if (ret) @@ -284,7 +285,12 @@ static int avs_es8336_probe(struct platform_device *pdev) if (!data || !card) return -ENOMEM; - card->name = "avs_es8336"; + if (pdata->obsolete_card_names) { + card->name = "avs_es8336"; + } else { + card->driver_name = "avs_es8336"; + card->long_name = card->name = "AVS I2S ES8336"; + } card->dev = dev; card->owner = THIS_MODULE; card->suspend_pre = avs_card_suspend_pre; @@ -304,7 +310,7 @@ static int avs_es8336_probe(struct platform_device *pdev) if (ret) return ret; - return devm_snd_soc_register_card(dev, card); + return devm_snd_soc_register_deferrable_card(dev, card); } static const struct platform_device_id avs_es8336_driver_ids[] = { @@ -326,4 +332,5 @@ static struct platform_driver avs_es8336_driver = { module_platform_driver(avs_es8336_driver); +MODULE_DESCRIPTION("Intel es8336 machine driver"); MODULE_LICENSE("GPL"); diff --git a/sound/soc/intel/avs/boards/hdaudio.c b/sound/soc/intel/avs/boards/hdaudio.c index 79b4aca41333..19b2255a8ac3 100644 --- a/sound/soc/intel/avs/boards/hdaudio.c +++ b/sound/soc/intel/avs/boards/hdaudio.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only // -// Copyright(c) 2021-2022 Intel Corporation. All rights reserved. +// Copyright(c) 2021-2022 Intel Corporation // // Authors: Cezary Rojewski <cezary.rojewski@intel.com> // Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> @@ -13,6 +13,7 @@ #include <sound/soc.h> #include <sound/soc-acpi.h> #include "../../../codecs/hda.h" +#include "../utils.h" static int avs_create_dai_links(struct device *dev, struct hda_codec *codec, int pcm_count, const char *platform_name, struct snd_soc_dai_link **links) @@ -39,8 +40,6 @@ static int avs_create_dai_links(struct device *dev, struct hda_codec *codec, int dl[i].id = i; dl[i].nonatomic = 1; dl[i].no_pcm = 1; - dl[i].dpcm_playback = 1; - dl[i].dpcm_capture = 1; dl[i].platforms = platform; dl[i].num_platforms = 1; dl[i].ignore_pmdown_time = 1; @@ -54,7 +53,7 @@ static int avs_create_dai_links(struct device *dev, struct hda_codec *codec, int if (!dl[i].cpus->dai_name) return -ENOMEM; - dl[i].codecs->name = devm_kstrdup(dev, cname, GFP_KERNEL); + dl[i].codecs->name = devm_kstrdup_const(dev, cname, GFP_KERNEL); if (!dl[i].codecs->name) return -ENOMEM; @@ -97,7 +96,8 @@ avs_card_hdmi_pcm_at(struct snd_soc_card *card, int hdmi_idx) static int avs_card_late_probe(struct snd_soc_card *card) { struct snd_soc_acpi_mach *mach = dev_get_platdata(card->dev); - struct hda_codec *codec = mach->pdata; + struct avs_mach_pdata *pdata = mach->pdata; + struct hda_codec *codec = pdata->codec; struct hda_pcm *hpcm; /* Topology pcm indexing is 1-based */ int i = 1; @@ -126,6 +126,7 @@ static int avs_card_late_probe(struct snd_soc_card *card) static int avs_probing_link_init(struct snd_soc_pcm_runtime *rtm) { struct snd_soc_acpi_mach *mach; + struct avs_mach_pdata *pdata; struct snd_soc_dai_link *links = NULL; struct snd_soc_card *card = rtm->card; struct hda_codec *codec; @@ -133,7 +134,8 @@ static int avs_probing_link_init(struct snd_soc_pcm_runtime *rtm) int ret, pcm_count = 0; mach = dev_get_platdata(card->dev); - codec = mach->pdata; + pdata = mach->pdata; + codec = pdata->codec; if (list_empty(&codec->pcm_list_head)) return -EINVAL; @@ -155,13 +157,11 @@ static int avs_probing_link_init(struct snd_soc_pcm_runtime *rtm) return 0; } -static struct snd_soc_dai_link probing_link = { +static const struct snd_soc_dai_link probing_link = { .name = "probing-LINK", .id = -1, .nonatomic = 1, .no_pcm = 1, - .dpcm_playback = 1, - .dpcm_capture = 1, .cpus = &snd_soc_dummy_dlc, .num_cpus = 1, .init = avs_probing_link_init, @@ -171,12 +171,14 @@ static int avs_hdaudio_probe(struct platform_device *pdev) { struct snd_soc_dai_link *binder; struct snd_soc_acpi_mach *mach; + struct avs_mach_pdata *pdata; struct snd_soc_card *card; struct device *dev = &pdev->dev; struct hda_codec *codec; mach = dev_get_platdata(dev); - codec = mach->pdata; + pdata = mach->pdata; + codec = pdata->codec; /* codec may be unloaded before card's probe() fires */ if (!device_is_registered(&codec->core.dev)) @@ -191,7 +193,7 @@ static int avs_hdaudio_probe(struct platform_device *pdev) if (!binder->platforms || !binder->codecs) return -ENOMEM; - binder->codecs->name = devm_kstrdup(dev, dev_name(&codec->core.dev), GFP_KERNEL); + binder->codecs->name = devm_kstrdup_const(dev, dev_name(&codec->core.dev), GFP_KERNEL); if (!binder->codecs->name) return -ENOMEM; @@ -204,7 +206,16 @@ static int avs_hdaudio_probe(struct platform_device *pdev) if (!card) return -ENOMEM; - card->name = binder->codecs->name; + if (pdata->obsolete_card_names) { + card->name = binder->codecs->name; + } else { + card->driver_name = "avs_hdaudio"; + if (hda_codec_is_display(codec)) + card->long_name = card->name = "AVS HDMI"; + else + card->long_name = card->name = "AVS HD-Audio"; + } + card->dev = dev; card->owner = THIS_MODULE; card->dai_link = binder; @@ -213,7 +224,7 @@ static int avs_hdaudio_probe(struct platform_device *pdev) if (hda_codec_is_display(codec)) card->late_probe = avs_card_late_probe; - return devm_snd_soc_register_card(dev, card); + return devm_snd_soc_register_deferrable_card(dev, card); } static const struct platform_device_id avs_hdaudio_driver_ids[] = { diff --git a/sound/soc/intel/avs/boards/i2s_test.c b/sound/soc/intel/avs/boards/i2s_test.c index 28f254eb0d03..f7b6d7715738 100644 --- a/sound/soc/intel/avs/boards/i2s_test.c +++ b/sound/soc/intel/avs/boards/i2s_test.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only // -// Copyright(c) 2021-2022 Intel Corporation. All rights reserved. +// Copyright(c) 2021-2022 Intel Corporation // // Authors: Cezary Rojewski <cezary.rojewski@intel.com> // Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> @@ -46,88 +46,25 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in dl->id = 0; dl->nonatomic = 1; dl->no_pcm = 1; - dl->dpcm_capture = 1; - dl->dpcm_playback = 1; *dai_link = dl; return 0; } -static int avs_create_dapm_routes(struct device *dev, int ssp_port, int tdm_slot, - struct snd_soc_dapm_route **routes, int *num_routes) -{ - struct snd_soc_dapm_route *dr; - const int num_dr = 2; - - dr = devm_kcalloc(dev, num_dr, sizeof(*dr), GFP_KERNEL); - if (!dr) - return -ENOMEM; - - dr[0].sink = devm_kasprintf(dev, GFP_KERNEL, - AVS_STRING_FMT("ssp", "pb", ssp_port, tdm_slot)); - dr[0].source = devm_kasprintf(dev, GFP_KERNEL, - AVS_STRING_FMT("ssp", " Tx", ssp_port, tdm_slot)); - if (!dr[0].sink || !dr[0].source) - return -ENOMEM; - - dr[1].sink = devm_kasprintf(dev, GFP_KERNEL, - AVS_STRING_FMT("ssp", " Rx", ssp_port, tdm_slot)); - dr[1].source = devm_kasprintf(dev, GFP_KERNEL, - AVS_STRING_FMT("ssp", "cp", ssp_port, tdm_slot)); - if (!dr[1].sink || !dr[1].source) - return -ENOMEM; - - *routes = dr; - *num_routes = num_dr; - - return 0; -} - -static int avs_create_dapm_widgets(struct device *dev, int ssp_port, int tdm_slot, - struct snd_soc_dapm_widget **widgets, int *num_widgets) -{ - struct snd_soc_dapm_widget *dw; - const int num_dw = 2; - - dw = devm_kcalloc(dev, num_dw, sizeof(*dw), GFP_KERNEL); - if (!dw) - return -ENOMEM; - - dw[0].id = snd_soc_dapm_hp; - dw[0].reg = SND_SOC_NOPM; - dw[0].name = devm_kasprintf(dev, GFP_KERNEL, - AVS_STRING_FMT("ssp", "pb", ssp_port, tdm_slot)); - if (!dw[0].name) - return -ENOMEM; - - dw[1].id = snd_soc_dapm_mic; - dw[1].reg = SND_SOC_NOPM; - dw[1].name = devm_kasprintf(dev, GFP_KERNEL, - AVS_STRING_FMT("ssp", "cp", ssp_port, tdm_slot)); - if (!dw[1].name) - return -ENOMEM; - - *widgets = dw; - *num_widgets = num_dw; - - return 0; -} - static int avs_i2s_test_probe(struct platform_device *pdev) { - struct snd_soc_dapm_widget *widgets; - struct snd_soc_dapm_route *routes; struct snd_soc_dai_link *dai_link; struct snd_soc_acpi_mach *mach; + struct avs_mach_pdata *pdata; struct snd_soc_card *card; struct device *dev = &pdev->dev; const char *pname; - int num_routes, num_widgets; int ssp_port, tdm_slot, ret; mach = dev_get_platdata(dev); pname = mach->mach_params.platform; + pdata = mach->pdata; if (!avs_mach_singular_ssp(mach)) { dev_err(dev, "Invalid SSP configuration\n"); @@ -145,8 +82,15 @@ static int avs_i2s_test_probe(struct platform_device *pdev) if (!card) return -ENOMEM; - card->name = devm_kasprintf(dev, GFP_KERNEL, - AVS_STRING_FMT("ssp", "-loopback", ssp_port, tdm_slot)); + if (pdata->obsolete_card_names) { + card->name = devm_kasprintf(dev, GFP_KERNEL, + AVS_STRING_FMT("ssp", "-loopback", ssp_port, tdm_slot)); + } else { + card->driver_name = "avs_i2s_test"; + card->long_name = card->name = devm_kasprintf(dev, GFP_KERNEL, + AVS_STRING_FMT("AVS I2S TEST-", "", + ssp_port, tdm_slot)); + } if (!card->name) return -ENOMEM; @@ -156,33 +100,17 @@ static int avs_i2s_test_probe(struct platform_device *pdev) return ret; } - ret = avs_create_dapm_routes(dev, ssp_port, tdm_slot, &routes, &num_routes); - if (ret) { - dev_err(dev, "Failed to create dapm routes: %d\n", ret); - return ret; - } - - ret = avs_create_dapm_widgets(dev, ssp_port, tdm_slot, &widgets, &num_widgets); - if (ret) { - dev_err(dev, "Failed to create dapm widgets: %d\n", ret); - return ret; - } - card->dev = dev; card->owner = THIS_MODULE; card->dai_link = dai_link; card->num_links = 1; - card->dapm_routes = routes; - card->num_dapm_routes = num_routes; - card->dapm_widgets = widgets; - card->num_dapm_widgets = num_widgets; card->fully_routed = true; ret = snd_soc_fixup_dai_links_platform_name(card, pname); if (ret) return ret; - return devm_snd_soc_register_card(dev, card); + return devm_snd_soc_register_deferrable_card(dev, card); } static const struct platform_device_id avs_i2s_test_driver_ids[] = { @@ -204,4 +132,5 @@ static struct platform_driver avs_i2s_test_driver = { module_platform_driver(avs_i2s_test_driver); +MODULE_DESCRIPTION("Intel i2s test machine driver"); MODULE_LICENSE("GPL"); diff --git a/sound/soc/intel/avs/boards/max98357a.c b/sound/soc/intel/avs/boards/max98357a.c index a83b95f25129..72053f83e98b 100644 --- a/sound/soc/intel/avs/boards/max98357a.c +++ b/sound/soc/intel/avs/boards/max98357a.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only // -// Copyright(c) 2021-2022 Intel Corporation. All rights reserved. +// Copyright(c) 2021-2022 Intel Corporation // // Authors: Cezary Rojewski <cezary.rojewski@intel.com> // Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> @@ -78,11 +78,11 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in dl->platforms = platform; dl->num_platforms = 1; dl->id = 0; - dl->dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS; + dl->dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC; dl->be_hw_params_fixup = avs_max98357a_be_fixup; dl->nonatomic = 1; dl->no_pcm = 1; - dl->dpcm_playback = 1; + dl->playback_only = 1; *dai_link = dl; @@ -93,6 +93,7 @@ static int avs_max98357a_probe(struct platform_device *pdev) { struct snd_soc_dai_link *dai_link; struct snd_soc_acpi_mach *mach; + struct avs_mach_pdata *pdata; struct snd_soc_card *card; struct device *dev = &pdev->dev; const char *pname; @@ -100,6 +101,7 @@ static int avs_max98357a_probe(struct platform_device *pdev) mach = dev_get_platdata(dev); pname = mach->mach_params.platform; + pdata = mach->pdata; ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot); if (ret) @@ -115,7 +117,12 @@ static int avs_max98357a_probe(struct platform_device *pdev) if (!card) return -ENOMEM; - card->name = "avs_max98357a"; + if (pdata->obsolete_card_names) { + card->name = "avs_max98357a"; + } else { + card->driver_name = "avs_max98357a"; + card->long_name = card->name = "AVS I2S MAX98357A"; + } card->dev = dev; card->owner = THIS_MODULE; card->dai_link = dai_link; @@ -132,7 +139,7 @@ static int avs_max98357a_probe(struct platform_device *pdev) if (ret) return ret; - return devm_snd_soc_register_card(dev, card); + return devm_snd_soc_register_deferrable_card(dev, card); } static const struct platform_device_id avs_max98357a_driver_ids[] = { @@ -154,4 +161,5 @@ static struct platform_driver avs_max98357a_driver = { module_platform_driver(avs_max98357a_driver) +MODULE_DESCRIPTION("Intel max98357a machine driver"); MODULE_LICENSE("GPL"); diff --git a/sound/soc/intel/avs/boards/max98373.c b/sound/soc/intel/avs/boards/max98373.c index 3b980a025e6f..cdba1c3ee20b 100644 --- a/sound/soc/intel/avs/boards/max98373.c +++ b/sound/soc/intel/avs/boards/max98373.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only // -// Copyright(c) 2022 Intel Corporation. All rights reserved. +// Copyright(c) 2022 Intel Corporation // // Authors: Cezary Rojewski <cezary.rojewski@intel.com> // Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> @@ -111,7 +111,7 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in dl->name = devm_kasprintf(dev, GFP_KERNEL, AVS_STRING_FMT("SSP", "-Codec", ssp_port, tdm_slot)); dl->cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL); - dl->codecs = devm_kzalloc(dev, sizeof(*dl->codecs) * 2, GFP_KERNEL); + dl->codecs = devm_kcalloc(dev, 2, sizeof(*dl->codecs), GFP_KERNEL); if (!dl->name || !dl->cpus || !dl->codecs) return -ENOMEM; @@ -134,8 +134,6 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in dl->be_hw_params_fixup = avs_max98373_be_fixup; dl->nonatomic = 1; dl->no_pcm = 1; - dl->dpcm_capture = 1; - dl->dpcm_playback = 1; dl->ignore_pmdown_time = 1; dl->ops = &avs_max98373_ops; @@ -148,6 +146,7 @@ static int avs_max98373_probe(struct platform_device *pdev) { struct snd_soc_dai_link *dai_link; struct snd_soc_acpi_mach *mach; + struct avs_mach_pdata *pdata; struct snd_soc_card *card; struct device *dev = &pdev->dev; const char *pname; @@ -155,6 +154,7 @@ static int avs_max98373_probe(struct platform_device *pdev) mach = dev_get_platdata(dev); pname = mach->mach_params.platform; + pdata = mach->pdata; ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot); if (ret) @@ -170,7 +170,12 @@ static int avs_max98373_probe(struct platform_device *pdev) if (!card) return -ENOMEM; - card->name = "avs_max98373"; + if (pdata->obsolete_card_names) { + card->name = "avs_max98373"; + } else { + card->driver_name = "avs_max98373"; + card->long_name = card->name = "AVS I2S MAX98373"; + } card->dev = dev; card->owner = THIS_MODULE; card->dai_link = dai_link; @@ -189,7 +194,7 @@ static int avs_max98373_probe(struct platform_device *pdev) if (ret) return ret; - return devm_snd_soc_register_card(dev, card); + return devm_snd_soc_register_deferrable_card(dev, card); } static const struct platform_device_id avs_max98373_driver_ids[] = { @@ -211,4 +216,5 @@ static struct platform_driver avs_max98373_driver = { module_platform_driver(avs_max98373_driver) +MODULE_DESCRIPTION("Intel max98373 machine driver"); MODULE_LICENSE("GPL"); diff --git a/sound/soc/intel/avs/boards/max98927.c b/sound/soc/intel/avs/boards/max98927.c index 86dd2b228df3..a68e227044c5 100644 --- a/sound/soc/intel/avs/boards/max98927.c +++ b/sound/soc/intel/avs/boards/max98927.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only // -// Copyright(c) 2022 Intel Corporation. All rights reserved. +// Copyright(c) 2022 Intel Corporation // // Authors: Cezary Rojewski <cezary.rojewski@intel.com> // Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> @@ -108,7 +108,7 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in dl->name = devm_kasprintf(dev, GFP_KERNEL, AVS_STRING_FMT("SSP", "-Codec", ssp_port, tdm_slot)); dl->cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL); - dl->codecs = devm_kzalloc(dev, sizeof(*dl->codecs) * 2, GFP_KERNEL); + dl->codecs = devm_kcalloc(dev, 2, sizeof(*dl->codecs), GFP_KERNEL); if (!dl->name || !dl->cpus || !dl->codecs) return -ENOMEM; @@ -127,12 +127,10 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in dl->platforms = platform; dl->num_platforms = 1; dl->id = 0; - dl->dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS; + dl->dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC; dl->be_hw_params_fixup = avs_max98927_be_fixup; dl->nonatomic = 1; dl->no_pcm = 1; - dl->dpcm_capture = 1; - dl->dpcm_playback = 1; dl->ignore_pmdown_time = 1; dl->ops = &avs_max98927_ops; @@ -145,6 +143,7 @@ static int avs_max98927_probe(struct platform_device *pdev) { struct snd_soc_dai_link *dai_link; struct snd_soc_acpi_mach *mach; + struct avs_mach_pdata *pdata; struct snd_soc_card *card; struct device *dev = &pdev->dev; const char *pname; @@ -152,6 +151,7 @@ static int avs_max98927_probe(struct platform_device *pdev) mach = dev_get_platdata(dev); pname = mach->mach_params.platform; + pdata = mach->pdata; ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot); if (ret) @@ -167,7 +167,12 @@ static int avs_max98927_probe(struct platform_device *pdev) if (!card) return -ENOMEM; - card->name = "avs_max98927"; + if (pdata->obsolete_card_names) { + card->name = "avs_max98927"; + } else { + card->driver_name = "avs_max98927"; + card->long_name = card->name = "AVS I2S MAX98927"; + } card->dev = dev; card->owner = THIS_MODULE; card->dai_link = dai_link; @@ -186,7 +191,7 @@ static int avs_max98927_probe(struct platform_device *pdev) if (ret) return ret; - return devm_snd_soc_register_card(dev, card); + return devm_snd_soc_register_deferrable_card(dev, card); } static const struct platform_device_id avs_max98927_driver_ids[] = { @@ -208,4 +213,5 @@ static struct platform_driver avs_max98927_driver = { module_platform_driver(avs_max98927_driver) +MODULE_DESCRIPTION("Intel max98927 machine driver"); MODULE_LICENSE("GPL"); diff --git a/sound/soc/intel/avs/boards/nau8825.c b/sound/soc/intel/avs/boards/nau8825.c index 1c1e2083f474..3fb1a5d07ae1 100644 --- a/sound/soc/intel/avs/boards/nau8825.c +++ b/sound/soc/intel/avs/boards/nau8825.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only // -// Copyright(c) 2021-2022 Intel Corporation. All rights reserved. +// Copyright(c) 2021-2022 Intel Corporation // // Authors: Cezary Rojewski <cezary.rojewski@intel.com> // Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> @@ -67,7 +67,7 @@ static const struct snd_soc_dapm_route card_base_routes[] = { { "Headset Mic", NULL, "Platform Clock" }, }; -static struct snd_soc_jack_pin card_headset_pins[] = { +static const struct snd_soc_jack_pin card_headset_pins[] = { { .pin = "Headphone Jack", .mask = SND_JACK_HEADPHONE, @@ -88,7 +88,8 @@ static int avs_nau8825_codec_init(struct snd_soc_pcm_runtime *runtime) jack = snd_soc_card_get_drvdata(card); num_pins = ARRAY_SIZE(card_headset_pins); - pins = devm_kmemdup(card->dev, card_headset_pins, sizeof(*pins) * num_pins, GFP_KERNEL); + pins = devm_kmemdup_array(card->dev, card_headset_pins, num_pins, + sizeof(card_headset_pins[0]), GFP_KERNEL); if (!pins) return -ENOMEM; @@ -96,7 +97,7 @@ static int avs_nau8825_codec_init(struct snd_soc_pcm_runtime *runtime) * 4 buttons here map to the google Reference headset. * The use of these buttons can be decided by the user space. */ - ret = snd_soc_card_jack_new_pins(card, "Headset", SND_JACK_HEADSET | SND_JACK_BTN_0 | + ret = snd_soc_card_jack_new_pins(card, "Headset Jack", SND_JACK_HEADSET | SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2 | SND_JACK_BTN_3, jack, pins, num_pins); if (ret) @@ -203,15 +204,13 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in dl->platforms = platform; dl->num_platforms = 1; dl->id = 0; - dl->dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS; + dl->dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC; dl->init = avs_nau8825_codec_init; dl->exit = avs_nau8825_codec_exit; dl->be_hw_params_fixup = avs_nau8825_be_fixup; dl->ops = &avs_nau8825_ops; dl->nonatomic = 1; dl->no_pcm = 1; - dl->dpcm_capture = 1; - dl->dpcm_playback = 1; *dai_link = dl; @@ -247,6 +246,7 @@ static int avs_nau8825_probe(struct platform_device *pdev) { struct snd_soc_dai_link *dai_link; struct snd_soc_acpi_mach *mach; + struct avs_mach_pdata *pdata; struct snd_soc_card *card; struct snd_soc_jack *jack; struct device *dev = &pdev->dev; @@ -255,6 +255,7 @@ static int avs_nau8825_probe(struct platform_device *pdev) mach = dev_get_platdata(dev); pname = mach->mach_params.platform; + pdata = mach->pdata; ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot); if (ret) @@ -271,7 +272,12 @@ static int avs_nau8825_probe(struct platform_device *pdev) if (!jack || !card) return -ENOMEM; - card->name = "avs_nau8825"; + if (pdata->obsolete_card_names) { + card->name = "avs_nau8825"; + } else { + card->driver_name = "avs_nau8825"; + card->long_name = card->name = "AVS I2S NAU8825"; + } card->dev = dev; card->owner = THIS_MODULE; card->suspend_pre = avs_card_suspend_pre; @@ -291,7 +297,7 @@ static int avs_nau8825_probe(struct platform_device *pdev) if (ret) return ret; - return devm_snd_soc_register_card(dev, card); + return devm_snd_soc_register_deferrable_card(dev, card); } static const struct platform_device_id avs_nau8825_driver_ids[] = { @@ -313,4 +319,5 @@ static struct platform_driver avs_nau8825_driver = { module_platform_driver(avs_nau8825_driver) +MODULE_DESCRIPTION("Intel nau8825 machine driver"); MODULE_LICENSE("GPL"); diff --git a/sound/soc/intel/avs/boards/pcm3168a.c b/sound/soc/intel/avs/boards/pcm3168a.c new file mode 100644 index 000000000000..b5bebadbbcb2 --- /dev/null +++ b/sound/soc/intel/avs/boards/pcm3168a.c @@ -0,0 +1,155 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright(c) 2024-2025 Intel Corporation +// +// Author: Cezary Rojewski <cezary.rojewski@intel.com> +// + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-acpi.h> +#include "../utils.h" + +static const struct snd_soc_dapm_widget card_widgets[] = { + SND_SOC_DAPM_HP("CPB Stereo HP 1", NULL), + SND_SOC_DAPM_HP("CPB Stereo HP 2", NULL), + SND_SOC_DAPM_HP("CPB Stereo HP 3", NULL), + SND_SOC_DAPM_LINE("CPB Line Out", NULL), + SND_SOC_DAPM_MIC("CPB Stereo Mic 1", NULL), + SND_SOC_DAPM_MIC("CPB Stereo Mic 2", NULL), + SND_SOC_DAPM_LINE("CPB Line In", NULL), +}; + +static const struct snd_soc_dapm_route card_routes[] = { + { "CPB Stereo HP 1", NULL, "AOUT1L" }, + { "CPB Stereo HP 1", NULL, "AOUT1R" }, + { "CPB Stereo HP 2", NULL, "AOUT2L" }, + { "CPB Stereo HP 2", NULL, "AOUT2R" }, + { "CPB Stereo HP 3", NULL, "AOUT3L" }, + { "CPB Stereo HP 3", NULL, "AOUT3R" }, + { "CPB Line Out", NULL, "AOUT4L" }, + { "CPB Line Out", NULL, "AOUT4R" }, + + { "AIN1L", NULL, "CPB Stereo Mic 1" }, + { "AIN1R", NULL, "CPB Stereo Mic 1" }, + { "AIN2L", NULL, "CPB Stereo Mic 2" }, + { "AIN2R", NULL, "CPB Stereo Mic 2" }, + { "AIN3L", NULL, "CPB Line In" }, + { "AIN3R", NULL, "CPB Line In" }, +}; + +static int avs_pcm3168a_be_fixup(struct snd_soc_pcm_runtime *runtime, + struct snd_pcm_hw_params *params) +{ + struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + + /* Set SSP to 24 bit. */ + snd_mask_none(fmt); + snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S24_LE); + + return 0; +} + +SND_SOC_DAILINK_DEF(pcm3168a_dac, + DAILINK_COMP_ARRAY(COMP_CODEC("i2c-PCM3168A:00", "pcm3168a-dac"))); +SND_SOC_DAILINK_DEF(pcm3168a_adc, + DAILINK_COMP_ARRAY(COMP_CODEC("i2c-PCM3168A:00", "pcm3168a-adc"))); +SND_SOC_DAILINK_DEF(cpu_ssp0, DAILINK_COMP_ARRAY(COMP_CPU("SSP0 Pin"))); +SND_SOC_DAILINK_DEF(cpu_ssp2, DAILINK_COMP_ARRAY(COMP_CPU("SSP2 Pin"))); + +static int avs_create_dai_links(struct device *dev, struct snd_soc_dai_link **links, int *num_links) +{ + struct snd_soc_dai_link_component *platform; + struct snd_soc_dai_link *dl; + const int num_dl = 2; + + dl = devm_kcalloc(dev, num_dl, sizeof(*dl), GFP_KERNEL); + platform = devm_kzalloc(dev, sizeof(*platform), GFP_KERNEL); + if (!dl || !platform) + return -ENOMEM; + + platform->name = dev_name(dev); + dl[0].num_cpus = 1; + dl[0].num_codecs = 1; + dl[0].platforms = platform; + dl[0].num_platforms = 1; + dl[0].dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBP_CFP; + dl[0].be_hw_params_fixup = avs_pcm3168a_be_fixup; + dl[0].nonatomic = 1; + dl[0].no_pcm = 1; + memcpy(&dl[1], &dl[0], sizeof(*dl)); + + dl[0].name = "SSP0-Codec-dac"; + dl[0].cpus = cpu_ssp0; + dl[0].codecs = pcm3168a_dac; + dl[1].name = "SSP2-Codec-adc"; + dl[1].cpus = cpu_ssp2; + dl[1].codecs = pcm3168a_adc; + + *links = dl; + *num_links = num_dl; + return 0; +} + +static int avs_pcm3168a_probe(struct platform_device *pdev) +{ + struct snd_soc_acpi_mach *mach; + struct avs_mach_pdata *pdata; + struct device *dev = &pdev->dev; + struct snd_soc_card *card; + int ret; + + mach = dev_get_platdata(dev); + pdata = mach->pdata; + + card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL); + if (!card) + return -ENOMEM; + + ret = avs_create_dai_links(dev, &card->dai_link, &card->num_links); + if (ret) + return ret; + + if (pdata->obsolete_card_names) { + card->name = "avs_pcm3168a"; + } else { + card->driver_name = "avs_pcm3168a"; + card->long_name = card->name = "AVS I2S PCM3168A"; + } + card->dev = dev; + card->owner = THIS_MODULE; + card->dapm_widgets = card_widgets; + card->num_dapm_widgets = ARRAY_SIZE(card_widgets); + card->dapm_routes = card_routes; + card->num_dapm_routes = ARRAY_SIZE(card_routes); + card->fully_routed = true; + + return devm_snd_soc_register_deferrable_card(dev, card); +} + +static const struct platform_device_id avs_pcm3168a_driver_ids[] = { + { + .name = "avs_pcm3168a", + }, + {}, +}; +MODULE_DEVICE_TABLE(platform, avs_pcm3168a_driver_ids); + +static struct platform_driver avs_pcm3168a_driver = { + .probe = avs_pcm3168a_probe, + .driver = { + .name = "avs_pcm3168a", + .pm = &snd_soc_pm_ops, + }, + .id_table = avs_pcm3168a_driver_ids, +}; + +module_platform_driver(avs_pcm3168a_driver); + +MODULE_DESCRIPTION("Intel pcm3168a machine driver"); +MODULE_AUTHOR("Cezary Rojewski <cezary.rojewski@intel.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/intel/avs/boards/probe.c b/sound/soc/intel/avs/boards/probe.c index a9469b5ecb40..06c1f19f27aa 100644 --- a/sound/soc/intel/avs/boards/probe.c +++ b/sound/soc/intel/avs/boards/probe.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only // -// Copyright(c) 2021-2022 Intel Corporation. All rights reserved. +// Copyright(c) 2021-2022 Intel Corporation // // Authors: Cezary Rojewski <cezary.rojewski@intel.com> // Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> @@ -36,7 +36,8 @@ static int avs_probe_mb_probe(struct platform_device *pdev) if (!card) return -ENOMEM; - card->name = "avs_probe_mb"; + card->driver_name = "avs_probe_mb"; + card->long_name = card->name = "AVS PROBE"; card->dev = dev; card->owner = THIS_MODULE; card->dai_link = probe_mb_dai_links; @@ -47,7 +48,7 @@ static int avs_probe_mb_probe(struct platform_device *pdev) if (ret) return ret; - return devm_snd_soc_register_card(dev, card); + return devm_snd_soc_register_deferrable_card(dev, card); } static const struct platform_device_id avs_probe_mb_driver_ids[] = { @@ -69,4 +70,5 @@ static struct platform_driver avs_probe_mb_driver = { module_platform_driver(avs_probe_mb_driver); +MODULE_DESCRIPTION("Intel probe machine driver"); MODULE_LICENSE("GPL"); diff --git a/sound/soc/intel/avs/boards/rt274.c b/sound/soc/intel/avs/boards/rt274.c index bfcb8845fd15..ec5382925157 100644 --- a/sound/soc/intel/avs/boards/rt274.c +++ b/sound/soc/intel/avs/boards/rt274.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only // -// Copyright(c) 2021-2022 Intel Corporation. All rights reserved. +// Copyright(c) 2021-2022 Intel Corporation // // Authors: Cezary Rojewski <cezary.rojewski@intel.com> // Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> @@ -75,7 +75,7 @@ static const struct snd_soc_dapm_route card_base_routes[] = { {"MIC", NULL, "Platform Clock"}, }; -static struct snd_soc_jack_pin card_headset_pins[] = { +static const struct snd_soc_jack_pin card_headset_pins[] = { { .pin = "Headphone Jack", .mask = SND_JACK_HEADPHONE, @@ -98,11 +98,13 @@ static int avs_rt274_codec_init(struct snd_soc_pcm_runtime *runtime) jack = snd_soc_card_get_drvdata(card); num_pins = ARRAY_SIZE(card_headset_pins); - pins = devm_kmemdup(card->dev, card_headset_pins, sizeof(*pins) * num_pins, GFP_KERNEL); + pins = devm_kmemdup_array(card->dev, card_headset_pins, num_pins, + sizeof(card_headset_pins[0]), GFP_KERNEL); if (!pins) return -ENOMEM; - ret = snd_soc_card_jack_new_pins(card, "Headset", SND_JACK_HEADSET, jack, pins, num_pins); + ret = snd_soc_card_jack_new_pins(card, "Headset Jack", SND_JACK_HEADSET, jack, pins, + num_pins); if (ret) return ret; @@ -177,14 +179,12 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in dl->platforms = platform; dl->num_platforms = 1; dl->id = 0; - dl->dai_fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS; + dl->dai_fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC; dl->init = avs_rt274_codec_init; dl->exit = avs_rt274_codec_exit; dl->be_hw_params_fixup = avs_rt274_be_fixup; dl->nonatomic = 1; dl->no_pcm = 1; - dl->dpcm_capture = 1; - dl->dpcm_playback = 1; *dai_link = dl; @@ -210,6 +210,7 @@ static int avs_rt274_probe(struct platform_device *pdev) { struct snd_soc_dai_link *dai_link; struct snd_soc_acpi_mach *mach; + struct avs_mach_pdata *pdata; struct snd_soc_card *card; struct snd_soc_jack *jack; struct device *dev = &pdev->dev; @@ -218,6 +219,7 @@ static int avs_rt274_probe(struct platform_device *pdev) mach = dev_get_platdata(dev); pname = mach->mach_params.platform; + pdata = mach->pdata; ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot); if (ret) @@ -234,7 +236,12 @@ static int avs_rt274_probe(struct platform_device *pdev) if (!jack || !card) return -ENOMEM; - card->name = "avs_rt274"; + if (pdata->obsolete_card_names) { + card->name = "avs_rt274"; + } else { + card->driver_name = "avs_rt274"; + card->long_name = card->name = "AVS I2S ALC274"; + } card->dev = dev; card->owner = THIS_MODULE; card->suspend_pre = avs_card_suspend_pre; @@ -254,7 +261,7 @@ static int avs_rt274_probe(struct platform_device *pdev) if (ret) return ret; - return devm_snd_soc_register_card(dev, card); + return devm_snd_soc_register_deferrable_card(dev, card); } static const struct platform_device_id avs_rt274_driver_ids[] = { @@ -276,4 +283,5 @@ static struct platform_driver avs_rt274_driver = { module_platform_driver(avs_rt274_driver); +MODULE_DESCRIPTION("Intel rt274 machine driver"); MODULE_LICENSE("GPL"); diff --git a/sound/soc/intel/avs/boards/rt286.c b/sound/soc/intel/avs/boards/rt286.c index 28d7d86b1cc9..2566e971ce1c 100644 --- a/sound/soc/intel/avs/boards/rt286.c +++ b/sound/soc/intel/avs/boards/rt286.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only // -// Copyright(c) 2021-2022 Intel Corporation. All rights reserved. +// Copyright(c) 2021-2022 Intel Corporation // // Authors: Cezary Rojewski <cezary.rojewski@intel.com> // Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> @@ -38,7 +38,7 @@ static const struct snd_soc_dapm_route card_base_routes[] = { {"Speaker", NULL, "SPOL"}, }; -static struct snd_soc_jack_pin card_headset_pins[] = { +static const struct snd_soc_jack_pin card_headset_pins[] = { { .pin = "Headphone Jack", .mask = SND_JACK_HEADPHONE, @@ -59,12 +59,13 @@ static int avs_rt286_codec_init(struct snd_soc_pcm_runtime *runtime) jack = snd_soc_card_get_drvdata(card); num_pins = ARRAY_SIZE(card_headset_pins); - pins = devm_kmemdup(card->dev, card_headset_pins, sizeof(*pins) * num_pins, GFP_KERNEL); + pins = devm_kmemdup_array(card->dev, card_headset_pins, num_pins, + sizeof(card_headset_pins[0]), GFP_KERNEL); if (!pins) return -ENOMEM; - ret = snd_soc_card_jack_new_pins(card, "Headset", SND_JACK_HEADSET | SND_JACK_BTN_0, jack, - pins, num_pins); + ret = snd_soc_card_jack_new_pins(card, "Headset Jack", SND_JACK_HEADSET | SND_JACK_BTN_0, + jack, pins, num_pins); if (ret) return ret; @@ -146,15 +147,13 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in dl->platforms = platform; dl->num_platforms = 1; dl->id = 0; - dl->dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS; + dl->dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC; dl->init = avs_rt286_codec_init; dl->exit = avs_rt286_codec_exit; dl->be_hw_params_fixup = avs_rt286_be_fixup; dl->ops = &avs_rt286_ops; dl->nonatomic = 1; dl->no_pcm = 1; - dl->dpcm_capture = 1; - dl->dpcm_playback = 1; *dai_link = dl; @@ -180,6 +179,7 @@ static int avs_rt286_probe(struct platform_device *pdev) { struct snd_soc_dai_link *dai_link; struct snd_soc_acpi_mach *mach; + struct avs_mach_pdata *pdata; struct snd_soc_card *card; struct snd_soc_jack *jack; struct device *dev = &pdev->dev; @@ -188,6 +188,7 @@ static int avs_rt286_probe(struct platform_device *pdev) mach = dev_get_platdata(dev); pname = mach->mach_params.platform; + pdata = mach->pdata; ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot); if (ret) @@ -205,7 +206,12 @@ static int avs_rt286_probe(struct platform_device *pdev) if (!jack || !card) return -ENOMEM; - card->name = "avs_rt286"; + if (pdata->obsolete_card_names) { + card->name = "avs_rt286"; + } else { + card->driver_name = "avs_rt286"; + card->long_name = card->name = "AVS I2S ALC286"; + } card->dev = dev; card->owner = THIS_MODULE; card->suspend_pre = avs_card_suspend_pre; @@ -225,7 +231,7 @@ static int avs_rt286_probe(struct platform_device *pdev) if (ret) return ret; - return devm_snd_soc_register_card(dev, card); + return devm_snd_soc_register_deferrable_card(dev, card); } static const struct platform_device_id avs_rt286_driver_ids[] = { @@ -247,4 +253,5 @@ static struct platform_driver avs_rt286_driver = { module_platform_driver(avs_rt286_driver); +MODULE_DESCRIPTION("Intel rt286 machine driver"); MODULE_LICENSE("GPL"); diff --git a/sound/soc/intel/avs/boards/rt298.c b/sound/soc/intel/avs/boards/rt298.c index 80f490b9e118..7be34c8ad167 100644 --- a/sound/soc/intel/avs/boards/rt298.c +++ b/sound/soc/intel/avs/boards/rt298.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only // -// Copyright(c) 2021-2022 Intel Corporation. All rights reserved. +// Copyright(c) 2021-2022 Intel Corporation // // Authors: Cezary Rojewski <cezary.rojewski@intel.com> // Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> @@ -49,7 +49,7 @@ static const struct snd_soc_dapm_route card_base_routes[] = { {"Speaker", NULL, "SPOL"}, }; -static struct snd_soc_jack_pin card_headset_pins[] = { +static const struct snd_soc_jack_pin card_headset_pins[] = { { .pin = "Headphone Jack", .mask = SND_JACK_HEADPHONE, @@ -70,12 +70,13 @@ static int avs_rt298_codec_init(struct snd_soc_pcm_runtime *runtime) jack = snd_soc_card_get_drvdata(card); num_pins = ARRAY_SIZE(card_headset_pins); - pins = devm_kmemdup(card->dev, card_headset_pins, sizeof(*pins) * num_pins, GFP_KERNEL); + pins = devm_kmemdup_array(card->dev, card_headset_pins, num_pins, + sizeof(card_headset_pins[0]), GFP_KERNEL); if (!pins) return -ENOMEM; - ret = snd_soc_card_jack_new_pins(card, "Headset", SND_JACK_HEADSET | SND_JACK_BTN_0, jack, - pins, num_pins); + ret = snd_soc_card_jack_new_pins(card, "Headset Jack", SND_JACK_HEADSET | SND_JACK_BTN_0, + jack, pins, num_pins); if (ret) return ret; @@ -164,17 +165,15 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in dl->num_platforms = 1; dl->id = 0; if (dmi_first_match(kblr_dmi_table)) - dl->dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS; + dl->dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC; else - dl->dai_fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS; + dl->dai_fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC; dl->init = avs_rt298_codec_init; dl->exit = avs_rt298_codec_exit; dl->be_hw_params_fixup = avs_rt298_be_fixup; dl->ops = &avs_rt298_ops; dl->nonatomic = 1; dl->no_pcm = 1; - dl->dpcm_capture = 1; - dl->dpcm_playback = 1; *dai_link = dl; @@ -200,6 +199,7 @@ static int avs_rt298_probe(struct platform_device *pdev) { struct snd_soc_dai_link *dai_link; struct snd_soc_acpi_mach *mach; + struct avs_mach_pdata *pdata; struct snd_soc_card *card; struct snd_soc_jack *jack; struct device *dev = &pdev->dev; @@ -208,6 +208,7 @@ static int avs_rt298_probe(struct platform_device *pdev) mach = dev_get_platdata(dev); pname = mach->mach_params.platform; + pdata = mach->pdata; ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot); if (ret) @@ -224,7 +225,12 @@ static int avs_rt298_probe(struct platform_device *pdev) if (!jack || !card) return -ENOMEM; - card->name = "avs_rt298"; + if (pdata->obsolete_card_names) { + card->name = "avs_rt298"; + } else { + card->driver_name = "avs_rt298"; + card->long_name = card->name = "AVS I2S ALC298"; + } card->dev = dev; card->owner = THIS_MODULE; card->suspend_pre = avs_card_suspend_pre; @@ -244,7 +250,7 @@ static int avs_rt298_probe(struct platform_device *pdev) if (ret) return ret; - return devm_snd_soc_register_card(dev, card); + return devm_snd_soc_register_deferrable_card(dev, card); } static const struct platform_device_id avs_rt298_driver_ids[] = { @@ -266,4 +272,5 @@ static struct platform_driver avs_rt298_driver = { module_platform_driver(avs_rt298_driver); +MODULE_DESCRIPTION("Intel rt298 machine driver"); MODULE_LICENSE("GPL"); diff --git a/sound/soc/intel/avs/boards/rt5514.c b/sound/soc/intel/avs/boards/rt5514.c index 60105f453ae2..45f091f2ce22 100644 --- a/sound/soc/intel/avs/boards/rt5514.c +++ b/sound/soc/intel/avs/boards/rt5514.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only // -// Copyright(c) 2021-2023 Intel Corporation. All rights reserved. +// Copyright(c) 2021-2023 Intel Corporation // // Authors: Cezary Rojewski <cezary.rojewski@intel.com> // Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> @@ -116,12 +116,12 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in dl->platforms = platform; dl->num_platforms = 1; dl->id = 0; - dl->dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS; + dl->dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC; dl->init = avs_rt5514_codec_init; dl->be_hw_params_fixup = avs_rt5514_be_fixup; dl->nonatomic = 1; dl->no_pcm = 1; - dl->dpcm_capture = 1; + dl->capture_only = 1; dl->ops = &avs_rt5514_ops; *dai_link = dl; @@ -133,6 +133,7 @@ static int avs_rt5514_probe(struct platform_device *pdev) { struct snd_soc_dai_link *dai_link; struct snd_soc_acpi_mach *mach; + struct avs_mach_pdata *pdata; struct snd_soc_card *card; struct device *dev = &pdev->dev; const char *pname; @@ -140,6 +141,7 @@ static int avs_rt5514_probe(struct platform_device *pdev) mach = dev_get_platdata(dev); pname = mach->mach_params.platform; + pdata = mach->pdata; ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot); if (ret) @@ -155,7 +157,12 @@ static int avs_rt5514_probe(struct platform_device *pdev) if (!card) return -ENOMEM; - card->name = "avs_rt5514"; + if (pdata->obsolete_card_names) { + card->name = "avs_rt5514"; + } else { + card->driver_name = "avs_rt5514"; + card->long_name = card->name = "AVS I2S ALC5514"; + } card->dev = dev; card->owner = THIS_MODULE; card->dai_link = dai_link; @@ -170,7 +177,7 @@ static int avs_rt5514_probe(struct platform_device *pdev) if (ret) return ret; - return devm_snd_soc_register_card(dev, card); + return devm_snd_soc_register_deferrable_card(dev, card); } static const struct platform_device_id avs_rt5514_driver_ids[] = { @@ -192,4 +199,5 @@ static struct platform_driver avs_rt5514_driver = { module_platform_driver(avs_rt5514_driver); +MODULE_DESCRIPTION("Intel rt5514 machine driver"); MODULE_LICENSE("GPL"); diff --git a/sound/soc/intel/avs/boards/rt5663.c b/sound/soc/intel/avs/boards/rt5663.c index b4762c2a7bf2..51648801710a 100644 --- a/sound/soc/intel/avs/boards/rt5663.c +++ b/sound/soc/intel/avs/boards/rt5663.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only // -// Copyright(c) 2022-2023 Intel Corporation. All rights reserved. +// Copyright(c) 2022-2023 Intel Corporation // // Authors: Cezary Rojewski <cezary.rojewski@intel.com> // Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> @@ -43,7 +43,7 @@ static const struct snd_soc_dapm_route card_routes[] = { { "IN1N", NULL, "Headset Mic" }, }; -static struct snd_soc_jack_pin card_headset_pins[] = { +static const struct snd_soc_jack_pin card_headset_pins[] = { { .pin = "Headphone Jack", .mask = SND_JACK_HEADPHONE, @@ -65,7 +65,8 @@ static int avs_rt5663_codec_init(struct snd_soc_pcm_runtime *runtime) jack = &priv->jack; num_pins = ARRAY_SIZE(card_headset_pins); - pins = devm_kmemdup(card->dev, card_headset_pins, sizeof(*pins) * num_pins, GFP_KERNEL); + pins = devm_kmemdup_array(card->dev, card_headset_pins, num_pins, + sizeof(card_headset_pins[0]), GFP_KERNEL); if (!pins) return -ENOMEM; @@ -171,8 +172,6 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in dl->be_hw_params_fixup = avs_rt5663_be_fixup; dl->nonatomic = 1; dl->no_pcm = 1; - dl->dpcm_capture = 1; - dl->dpcm_playback = 1; dl->ops = &avs_rt5663_ops; *dai_link = dl; @@ -199,6 +198,7 @@ static int avs_rt5663_probe(struct platform_device *pdev) { struct snd_soc_dai_link *dai_link; struct snd_soc_acpi_mach *mach; + struct avs_mach_pdata *pdata; struct snd_soc_card *card; struct rt5663_private *priv; struct device *dev = &pdev->dev; @@ -207,6 +207,7 @@ static int avs_rt5663_probe(struct platform_device *pdev) mach = dev_get_platdata(dev); pname = mach->mach_params.platform; + pdata = mach->pdata; ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot); if (ret) @@ -223,7 +224,12 @@ static int avs_rt5663_probe(struct platform_device *pdev) if (!priv || !card) return -ENOMEM; - card->name = "avs_rt5663"; + if (pdata->obsolete_card_names) { + card->name = "avs_rt5663"; + } else { + card->driver_name = "avs_rt5663"; + card->long_name = card->name = "AVS I2S ALC5663"; + } card->dev = dev; card->owner = THIS_MODULE; card->suspend_pre = avs_card_suspend_pre; @@ -243,7 +249,7 @@ static int avs_rt5663_probe(struct platform_device *pdev) if (ret) return ret; - return devm_snd_soc_register_card(dev, card); + return devm_snd_soc_register_deferrable_card(dev, card); } static const struct platform_device_id avs_rt5663_driver_ids[] = { @@ -265,4 +271,5 @@ static struct platform_driver avs_rt5663_driver = { module_platform_driver(avs_rt5663_driver); +MODULE_DESCRIPTION("Intel rt5663 machine driver"); MODULE_LICENSE("GPL"); diff --git a/sound/soc/intel/avs/boards/rt5682.c b/sound/soc/intel/avs/boards/rt5682.c index 243f979fda98..9677b9ebeff1 100644 --- a/sound/soc/intel/avs/boards/rt5682.c +++ b/sound/soc/intel/avs/boards/rt5682.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only // -// Copyright(c) 2021-2022 Intel Corporation. All rights reserved. +// Copyright(c) 2021-2022 Intel Corporation // // Authors: Cezary Rojewski <cezary.rojewski@intel.com> // Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> @@ -80,7 +80,7 @@ static const struct snd_soc_dapm_route card_base_routes[] = { { "IN1P", NULL, "Headset Mic" }, }; -static struct snd_soc_jack_pin card_jack_pins[] = { +static const struct snd_soc_jack_pin card_jack_pins[] = { { .pin = "Headphone Jack", .mask = SND_JACK_HEADPHONE, @@ -102,7 +102,8 @@ static int avs_rt5682_codec_init(struct snd_soc_pcm_runtime *runtime) jack = snd_soc_card_get_drvdata(card); num_pins = ARRAY_SIZE(card_jack_pins); - pins = devm_kmemdup(card->dev, card_jack_pins, sizeof(*pins) * num_pins, GFP_KERNEL); + pins = devm_kmemdup_array(card->dev, card_jack_pins, num_pins, + sizeof(card_jack_pins[0]), GFP_KERNEL); if (!pins) return -ENOMEM; @@ -242,8 +243,6 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in dl->ops = &avs_rt5682_ops; dl->nonatomic = 1; dl->no_pcm = 1; - dl->dpcm_capture = 1; - dl->dpcm_playback = 1; *dai_link = dl; @@ -269,6 +268,7 @@ static int avs_rt5682_probe(struct platform_device *pdev) { struct snd_soc_dai_link *dai_link; struct snd_soc_acpi_mach *mach; + struct avs_mach_pdata *pdata; struct snd_soc_card *card; struct snd_soc_jack *jack; struct device *dev = &pdev->dev; @@ -283,6 +283,7 @@ static int avs_rt5682_probe(struct platform_device *pdev) mach = dev_get_platdata(dev); pname = mach->mach_params.platform; + pdata = mach->pdata; ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot); if (ret) @@ -299,7 +300,12 @@ static int avs_rt5682_probe(struct platform_device *pdev) if (!jack || !card) return -ENOMEM; - card->name = "avs_rt5682"; + if (pdata->obsolete_card_names) { + card->name = "avs_rt5682"; + } else { + card->driver_name = "avs_rt5682"; + card->long_name = card->name = "AVS I2S ALC5682"; + } card->dev = dev; card->owner = THIS_MODULE; card->suspend_pre = avs_card_suspend_pre; @@ -319,7 +325,7 @@ static int avs_rt5682_probe(struct platform_device *pdev) if (ret) return ret; - return devm_snd_soc_register_card(dev, card); + return devm_snd_soc_register_deferrable_card(dev, card); } static const struct platform_device_id avs_rt5682_driver_ids[] = { @@ -341,5 +347,6 @@ static struct platform_driver avs_rt5682_driver = { module_platform_driver(avs_rt5682_driver) +MODULE_DESCRIPTION("Intel rt5682 machine driver"); MODULE_AUTHOR("Cezary Rojewski <cezary.rojewski@intel.com>"); MODULE_LICENSE("GPL"); diff --git a/sound/soc/intel/avs/boards/ssm4567.c b/sound/soc/intel/avs/boards/ssm4567.c index 4a0e136835ff..3786eef8c494 100644 --- a/sound/soc/intel/avs/boards/ssm4567.c +++ b/sound/soc/intel/avs/boards/ssm4567.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only // -// Copyright(c) 2021-2022 Intel Corporation. All rights reserved. +// Copyright(c) 2021-2022 Intel Corporation // // Authors: Cezary Rojewski <cezary.rojewski@intel.com> // Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> @@ -37,8 +37,6 @@ static const struct snd_kcontrol_new card_controls[] = { static const struct snd_soc_dapm_widget card_widgets[] = { SND_SOC_DAPM_SPK("Left Speaker", NULL), SND_SOC_DAPM_SPK("Right Speaker", NULL), - SND_SOC_DAPM_SPK("DP1", NULL), - SND_SOC_DAPM_SPK("DP2", NULL), }; static const struct snd_soc_dapm_route card_base_routes[] = { @@ -99,7 +97,7 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in dl->name = devm_kasprintf(dev, GFP_KERNEL, AVS_STRING_FMT("SSP", "-Codec", ssp_port, tdm_slot)); dl->cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL); - dl->codecs = devm_kzalloc(dev, sizeof(*dl->codecs) * 2, GFP_KERNEL); + dl->codecs = devm_kcalloc(dev, 2, sizeof(*dl->codecs), GFP_KERNEL); if (!dl->name || !dl->cpus || !dl->codecs) return -ENOMEM; @@ -118,13 +116,11 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in dl->platforms = platform; dl->num_platforms = 1; dl->id = 0; - dl->dai_fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_IB_NF | SND_SOC_DAIFMT_CBS_CFS; + dl->dai_fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_IB_NF | SND_SOC_DAIFMT_CBC_CFC; dl->init = avs_ssm4567_codec_init; dl->be_hw_params_fixup = avs_ssm4567_be_fixup; dl->nonatomic = 1; dl->no_pcm = 1; - dl->dpcm_capture = 1; - dl->dpcm_playback = 1; dl->ignore_pmdown_time = 1; *dai_link = dl; @@ -136,6 +132,7 @@ static int avs_ssm4567_probe(struct platform_device *pdev) { struct snd_soc_dai_link *dai_link; struct snd_soc_acpi_mach *mach; + struct avs_mach_pdata *pdata; struct snd_soc_card *card; struct device *dev = &pdev->dev; const char *pname; @@ -143,6 +140,7 @@ static int avs_ssm4567_probe(struct platform_device *pdev) mach = dev_get_platdata(dev); pname = mach->mach_params.platform; + pdata = mach->pdata; ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot); if (ret) @@ -158,7 +156,12 @@ static int avs_ssm4567_probe(struct platform_device *pdev) if (!card) return -ENOMEM; - card->name = "avs_ssm4567-adi"; + if (pdata->obsolete_card_names) { + card->name = "avs_ssm4567"; + } else { + card->driver_name = "avs_ssm4567"; + card->long_name = card->name = "AVS I2S SSM4567"; + } card->dev = dev; card->owner = THIS_MODULE; card->dai_link = dai_link; @@ -172,13 +175,12 @@ static int avs_ssm4567_probe(struct platform_device *pdev) card->dapm_routes = card_base_routes; card->num_dapm_routes = ARRAY_SIZE(card_base_routes); card->fully_routed = true; - card->disable_route_checks = true; ret = snd_soc_fixup_dai_links_platform_name(card, pname); if (ret) return ret; - return devm_snd_soc_register_card(dev, card); + return devm_snd_soc_register_deferrable_card(dev, card); } static const struct platform_device_id avs_ssm4567_driver_ids[] = { @@ -200,4 +202,5 @@ static struct platform_driver avs_ssm4567_driver = { module_platform_driver(avs_ssm4567_driver) +MODULE_DESCRIPTION("Intel ssm4567 machine driver"); MODULE_LICENSE("GPL"); diff --git a/sound/soc/intel/avs/cldma.c b/sound/soc/intel/avs/cldma.c index d7a9390b5e48..61326d7059b1 100644 --- a/sound/soc/intel/avs/cldma.c +++ b/sound/soc/intel/avs/cldma.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only // -// Copyright(c) 2021-2022 Intel Corporation. All rights reserved. +// Copyright(c) 2021-2022 Intel Corporation // // Author: Cezary Rojewski <cezary.rojewski@intel.com> // @@ -35,7 +35,7 @@ struct hda_cldma { unsigned int buffer_size; unsigned int num_periods; - unsigned int stream_tag; + unsigned char stream_tag; void __iomem *sd_addr; struct snd_dma_buffer dmab_data; @@ -248,32 +248,20 @@ void hda_cldma_setup(struct hda_cldma *cl) snd_hdac_stream_writel(cl, CL_SPBFCTL, 1); } -static irqreturn_t cldma_irq_handler(int irq, void *dev_id) +void hda_cldma_interrupt(struct hda_cldma *cl) { - struct hda_cldma *cl = dev_id; - u32 adspis; - - adspis = snd_hdac_adsp_readl(cl, AVS_ADSP_REG_ADSPIS); - if (adspis == UINT_MAX) - return IRQ_NONE; - if (!(adspis & AVS_ADSP_ADSPIS_CLDMA)) - return IRQ_NONE; - - cl->sd_status = snd_hdac_stream_readb(cl, SD_STS); - dev_warn(cl->dev, "%s sd_status: 0x%08x\n", __func__, cl->sd_status); - /* disable CLDMA interrupt */ snd_hdac_adsp_updatel(cl, AVS_ADSP_REG_ADSPIC, AVS_ADSP_ADSPIC_CLDMA, 0); - complete(&cl->completion); + cl->sd_status = snd_hdac_stream_readb(cl, SD_STS); + dev_dbg(cl->dev, "%s sd_status: 0x%08x\n", __func__, cl->sd_status); - return IRQ_HANDLED; + complete(&cl->completion); } int hda_cldma_init(struct hda_cldma *cl, struct hdac_bus *bus, void __iomem *dsp_ba, unsigned int buffer_size) { - struct pci_dev *pci = to_pci_dev(bus->dev); int ret; ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV_SG, bus->dev, buffer_size, &cl->dmab_data); @@ -281,8 +269,10 @@ int hda_cldma_init(struct hda_cldma *cl, struct hdac_bus *bus, void __iomem *dsp return ret; ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, bus->dev, BDL_SIZE, &cl->dmab_bdl); - if (ret < 0) - goto alloc_err; + if (ret < 0) { + snd_dma_free_pages(&cl->dmab_data); + return ret; + } cl->dev = bus->dev; cl->bus = bus; @@ -290,27 +280,11 @@ int hda_cldma_init(struct hda_cldma *cl, struct hdac_bus *bus, void __iomem *dsp cl->buffer_size = buffer_size; cl->sd_addr = dsp_ba + AZX_CL_SD_BASE; - ret = pci_request_irq(pci, 0, cldma_irq_handler, NULL, cl, "CLDMA"); - if (ret < 0) { - dev_err(cl->dev, "Failed to request CLDMA IRQ handler: %d\n", ret); - goto req_err; - } - return 0; - -req_err: - snd_dma_free_pages(&cl->dmab_bdl); -alloc_err: - snd_dma_free_pages(&cl->dmab_data); - - return ret; } void hda_cldma_free(struct hda_cldma *cl) { - struct pci_dev *pci = to_pci_dev(cl->dev); - - pci_free_irq(pci, 0, cl); snd_dma_free_pages(&cl->dmab_data); snd_dma_free_pages(&cl->dmab_bdl); } diff --git a/sound/soc/intel/avs/cldma.h b/sound/soc/intel/avs/cldma.h index 223d3431ab81..7f9b2b1c566e 100644 --- a/sound/soc/intel/avs/cldma.h +++ b/sound/soc/intel/avs/cldma.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright(c) 2021-2022 Intel Corporation. All rights reserved. + * Copyright(c) 2021-2022 Intel Corporation * * Author: Cezary Rojewski <cezary.rojewski@intel.com> */ @@ -24,6 +24,7 @@ int hda_cldma_reset(struct hda_cldma *cl); void hda_cldma_set_data(struct hda_cldma *cl, void *data, unsigned int size); void hda_cldma_setup(struct hda_cldma *cl); +void hda_cldma_interrupt(struct hda_cldma *cl); int hda_cldma_init(struct hda_cldma *cl, struct hdac_bus *bus, void __iomem *dsp_ba, unsigned int buffer_size); void hda_cldma_free(struct hda_cldma *cl); diff --git a/sound/soc/intel/avs/cnl.c b/sound/soc/intel/avs/cnl.c index 5423c29ecc4e..03f8fb0dc187 100644 --- a/sound/soc/intel/avs/cnl.c +++ b/sound/soc/intel/avs/cnl.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only // -// Copyright(c) 2021-2024 Intel Corporation. All rights reserved. +// Copyright(c) 2021-2024 Intel Corporation // // Authors: Cezary Rojewski <cezary.rojewski@intel.com> // Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> @@ -9,45 +9,75 @@ #include <sound/hdaudio_ext.h> #include "avs.h" #include "messages.h" +#include "registers.h" -irqreturn_t avs_cnl_irq_thread(struct avs_dev *adev) +static void avs_cnl_ipc_interrupt(struct avs_dev *adev) { - union avs_reply_msg msg; - u32 hipctdr, hipctdd, hipctda; - - hipctdr = snd_hdac_adsp_readl(adev, CNL_ADSP_REG_HIPCTDR); - hipctdd = snd_hdac_adsp_readl(adev, CNL_ADSP_REG_HIPCTDD); - - /* Ensure DSP sent new response to process. */ - if (!(hipctdr & CNL_ADSP_HIPCTDR_BUSY)) - return IRQ_NONE; - - msg.primary = hipctdr; - msg.ext.val = hipctdd; - avs_dsp_process_response(adev, msg.val); - - /* Tell DSP we accepted its message. */ - snd_hdac_adsp_updatel(adev, CNL_ADSP_REG_HIPCTDR, - CNL_ADSP_HIPCTDR_BUSY, CNL_ADSP_HIPCTDR_BUSY); - /* Ack this response. */ - snd_hdac_adsp_updatel(adev, CNL_ADSP_REG_HIPCTDA, - CNL_ADSP_HIPCTDA_DONE, CNL_ADSP_HIPCTDA_DONE); - /* HW might have been clock gated, give some time for change to propagate. */ - snd_hdac_adsp_readl_poll(adev, CNL_ADSP_REG_HIPCTDA, hipctda, - !(hipctda & CNL_ADSP_HIPCTDA_DONE), 10, 1000); - /* Unmask busy interrupt. */ - snd_hdac_adsp_updatel(adev, CNL_ADSP_REG_HIPCCTL, - AVS_ADSP_HIPCCTL_BUSY, AVS_ADSP_HIPCCTL_BUSY); - - return IRQ_HANDLED; + const struct avs_spec *spec = adev->spec; + u32 hipc_ack, hipc_rsp; + + snd_hdac_adsp_updatel(adev, spec->hipc->ctl_offset, + AVS_ADSP_HIPCCTL_DONE | AVS_ADSP_HIPCCTL_BUSY, 0); + + hipc_ack = snd_hdac_adsp_readl(adev, spec->hipc->ack_offset); + hipc_rsp = snd_hdac_adsp_readl(adev, spec->hipc->rsp_offset); + + /* DSP acked host's request. */ + if (hipc_ack & spec->hipc->ack_done_mask) { + complete(&adev->ipc->done_completion); + + /* Tell DSP it has our attention. */ + snd_hdac_adsp_updatel(adev, spec->hipc->ack_offset, spec->hipc->ack_done_mask, + spec->hipc->ack_done_mask); + } + + /* DSP sent new response to process. */ + if (hipc_rsp & spec->hipc->rsp_busy_mask) { + union avs_reply_msg msg; + u32 hipctda; + + msg.primary = snd_hdac_adsp_readl(adev, CNL_ADSP_REG_HIPCTDR); + msg.ext.val = snd_hdac_adsp_readl(adev, CNL_ADSP_REG_HIPCTDD); + + avs_dsp_process_response(adev, msg.val); + + /* Tell DSP we accepted its message. */ + snd_hdac_adsp_updatel(adev, CNL_ADSP_REG_HIPCTDR, + CNL_ADSP_HIPCTDR_BUSY, CNL_ADSP_HIPCTDR_BUSY); + /* Ack this response. */ + snd_hdac_adsp_updatel(adev, CNL_ADSP_REG_HIPCTDA, + CNL_ADSP_HIPCTDA_DONE, CNL_ADSP_HIPCTDA_DONE); + /* HW might have been clock gated, give some time for change to propagate. */ + snd_hdac_adsp_readl_poll(adev, CNL_ADSP_REG_HIPCTDA, hipctda, + !(hipctda & CNL_ADSP_HIPCTDA_DONE), 10, 1000); + } + + snd_hdac_adsp_updatel(adev, spec->hipc->ctl_offset, + AVS_ADSP_HIPCCTL_DONE | AVS_ADSP_HIPCCTL_BUSY, + AVS_ADSP_HIPCCTL_DONE | AVS_ADSP_HIPCCTL_BUSY); +} + +irqreturn_t avs_cnl_dsp_interrupt(struct avs_dev *adev) +{ + u32 adspis = snd_hdac_adsp_readl(adev, AVS_ADSP_REG_ADSPIS); + irqreturn_t ret = IRQ_NONE; + + if (adspis == UINT_MAX) + return ret; + + if (adspis & AVS_ADSP_ADSPIS_IPC) { + avs_cnl_ipc_interrupt(adev); + ret = IRQ_HANDLED; + } + + return ret; } const struct avs_dsp_ops avs_cnl_dsp_ops = { .power = avs_dsp_core_power, .reset = avs_dsp_core_reset, .stall = avs_dsp_core_stall, - .irq_handler = avs_irq_handler, - .irq_thread = avs_cnl_irq_thread, + .dsp_interrupt = avs_cnl_dsp_interrupt, .int_control = avs_dsp_interrupt_control, .load_basefw = avs_hda_load_basefw, .load_lib = avs_hda_load_library, diff --git a/sound/soc/intel/avs/control.c b/sound/soc/intel/avs/control.c index 3dfa2e9816db..2e01dc75a15a 100644 --- a/sound/soc/intel/avs/control.c +++ b/sound/soc/intel/avs/control.c @@ -1,11 +1,12 @@ // SPDX-License-Identifier: GPL-2.0-only // -// Copyright(c) 2021-2022 Intel Corporation. All rights reserved. +// Copyright(c) 2021-2022 Intel Corporation // // Authors: Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> // Cezary Rojewski <cezary.rojewski@intel.com> // +#include <linux/cleanup.h> #include <sound/soc.h> #include "avs.h" #include "control.h" @@ -31,8 +32,11 @@ static struct avs_path_module *avs_get_volume_module(struct avs_dev *adev, u32 i list_for_each_entry(path, &adev->path_list, node) { list_for_each_entry(ppl, &path->ppl_list, node) { list_for_each_entry(mod, &ppl->mod_list, node) { - if (guid_equal(&mod->template->cfg_ext->type, &AVS_PEAKVOL_MOD_UUID) - && mod->template->ctl_id == id) { + guid_t *type = &mod->template->cfg_ext->type; + + if ((guid_equal(type, &AVS_PEAKVOL_MOD_UUID) || + guid_equal(type, &AVS_GAIN_MOD_UUID)) && + mod->template->ctl_id == id) { spin_unlock(&adev->path_list_lock); return mod; } @@ -44,70 +48,168 @@ static struct avs_path_module *avs_get_volume_module(struct avs_dev *adev, u32 i return NULL; } -int avs_control_volume_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +int avs_control_volume_get(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *uctl) { - struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; - struct avs_control_data *ctl_data = (struct avs_control_data *)mc->dobj.private; - struct avs_dev *adev = avs_get_kcontrol_adev(kcontrol); - struct avs_volume_cfg *dspvols = NULL; + struct soc_mixer_control *mc = (struct soc_mixer_control *)kctl->private_value; + struct avs_control_data *ctl_data = mc->dobj.private; struct avs_path_module *active_module; + struct avs_volume_cfg *dspvols; + struct avs_dev *adev; size_t num_dspvols; - int ret = 0; + int ret, i; + + adev = avs_get_kcontrol_adev(kctl); - /* prevent access to modules while path is being constructed */ - mutex_lock(&adev->path_mutex); + /* Prevent access to modules while path is being constructed. */ + guard(mutex)(&adev->path_mutex); active_module = avs_get_volume_module(adev, ctl_data->id); if (active_module) { ret = avs_ipc_peakvol_get_volume(adev, active_module->module_id, active_module->instance_id, &dspvols, &num_dspvols); - if (!ret) - ucontrol->value.integer.value[0] = dspvols[0].target_volume; + if (ret) + return AVS_IPC_RET(ret); - ret = AVS_IPC_RET(ret); + /* Do not copy more than the control can store. */ + num_dspvols = min_t(u32, num_dspvols, SND_SOC_TPLG_MAX_CHAN); + for (i = 0; i < num_dspvols; i++) + ctl_data->values[i] = dspvols[i].target_volume; kfree(dspvols); - } else { - ucontrol->value.integer.value[0] = ctl_data->volume; } - mutex_unlock(&adev->path_mutex); - return ret; + memcpy(uctl->value.integer.value, ctl_data->values, sizeof(ctl_data->values)); + return 0; } -int avs_control_volume_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +int avs_control_volume_put(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *uctl) { - struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; - struct avs_control_data *ctl_data = (struct avs_control_data *)mc->dobj.private; - struct avs_dev *adev = avs_get_kcontrol_adev(kcontrol); - long *volume = &ctl_data->volume; struct avs_path_module *active_module; - struct avs_volume_cfg dspvol = {0}; - long ctlvol = ucontrol->value.integer.value[0]; - int ret = 0, changed = 0; + struct avs_control_data *ctl_data; + struct soc_mixer_control *mc; + struct avs_dev *adev; + long *input; + int ret, i; + + mc = (struct soc_mixer_control *)kctl->private_value; + ctl_data = mc->dobj.private; + adev = avs_get_kcontrol_adev(kctl); + input = uctl->value.integer.value; + i = 0; + + /* mc->num_channels can be 0. */ + do { + if (input[i] < mc->min || input[i] > mc->max) + return -EINVAL; + } while (++i < mc->num_channels); + + if (!memcmp(ctl_data->values, input, sizeof(ctl_data->values))) + return 0; + + /* Prevent access to modules while path is being constructed. */ + guard(mutex)(&adev->path_mutex); - if (ctlvol < 0 || ctlvol > mc->max) - return -EINVAL; + active_module = avs_get_volume_module(adev, ctl_data->id); + if (active_module) { + ret = avs_peakvol_set_volume(adev, active_module, mc, input); + if (ret) + return ret; + } - /* prevent access to modules while path is being constructed */ - mutex_lock(&adev->path_mutex); + memcpy(ctl_data->values, input, sizeof(ctl_data->values)); + return 1; +} - if (*volume != ctlvol) { - *volume = ctlvol; - changed = 1; - } +int avs_control_volume_info(struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo) +{ + struct soc_mixer_control *mc = (struct soc_mixer_control *)kctl->private_value; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = max_t(u32, 1, mc->num_channels); + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mc->max; + return 0; +} + +int avs_control_mute_get(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *uctl) +{ + struct soc_mixer_control *mc = (struct soc_mixer_control *)kctl->private_value; + struct avs_control_data *ctl_data = mc->dobj.private; + struct avs_path_module *active_module; + struct avs_mute_cfg *dspmutes; + struct avs_dev *adev; + size_t num_dspmutes; + int ret, i; + + adev = avs_get_kcontrol_adev(kctl); + + /* Prevent access to modules while path is being constructed. */ + guard(mutex)(&adev->path_mutex); active_module = avs_get_volume_module(adev, ctl_data->id); if (active_module) { - dspvol.channel_id = AVS_ALL_CHANNELS_MASK; - dspvol.target_volume = *volume; + ret = avs_ipc_peakvol_get_mute(adev, active_module->module_id, + active_module->instance_id, &dspmutes, + &num_dspmutes); + if (ret) + return AVS_IPC_RET(ret); + + /* Do not copy more than the control can store. */ + num_dspmutes = min_t(u32, num_dspmutes, SND_SOC_TPLG_MAX_CHAN); + for (i = 0; i < num_dspmutes; i++) + ctl_data->values[i] = !dspmutes[i].mute; + kfree(dspmutes); + } - ret = avs_ipc_peakvol_set_volume(adev, active_module->module_id, - active_module->instance_id, &dspvol); - ret = AVS_IPC_RET(ret); + memcpy(uctl->value.integer.value, ctl_data->values, sizeof(ctl_data->values)); + return 0; +} + +int avs_control_mute_put(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *uctl) +{ + struct avs_path_module *active_module; + struct avs_control_data *ctl_data; + struct soc_mixer_control *mc; + struct avs_dev *adev; + long *input; + int ret, i; + + mc = (struct soc_mixer_control *)kctl->private_value; + ctl_data = mc->dobj.private; + adev = avs_get_kcontrol_adev(kctl); + input = uctl->value.integer.value; + i = 0; + + /* mc->num_channels can be 0. */ + do { + if (input[i] < mc->min || input[i] > mc->max) + return -EINVAL; + } while (++i < mc->num_channels); + + if (!memcmp(ctl_data->values, input, sizeof(ctl_data->values))) + return 0; + + /* Prevent access to modules while path is being constructed. */ + guard(mutex)(&adev->path_mutex); + + active_module = avs_get_volume_module(adev, ctl_data->id); + if (active_module) { + ret = avs_peakvol_set_mute(adev, active_module, mc, input); + if (ret) + return ret; } - mutex_unlock(&adev->path_mutex); + memcpy(ctl_data->values, input, sizeof(ctl_data->values)); + return 1; +} + +int avs_control_mute_info(struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo) +{ + struct soc_mixer_control *mc = (struct soc_mixer_control *)kctl->private_value; - return ret ? ret : changed; + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = max_t(u32, 1, mc->num_channels); + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mc->max; + return 0; } diff --git a/sound/soc/intel/avs/control.h b/sound/soc/intel/avs/control.h index 08631bde13c3..08b2919e4629 100644 --- a/sound/soc/intel/avs/control.h +++ b/sound/soc/intel/avs/control.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright(c) 2021-2022 Intel Corporation. All rights reserved. + * Copyright(c) 2021-2022 Intel Corporation * * Authors: Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> * Cezary Rojewski <cezary.rojewski@intel.com> @@ -10,14 +10,18 @@ #define __SOUND_SOC_INTEL_AVS_CTRL_H #include <sound/control.h> +#include <uapi/sound/asoc.h> struct avs_control_data { u32 id; - - long volume; + long values[SND_SOC_TPLG_MAX_CHAN]; }; -int avs_control_volume_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); -int avs_control_volume_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); +int avs_control_volume_get(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *uctl); +int avs_control_volume_put(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *uctl); +int avs_control_volume_info(struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo); +int avs_control_mute_get(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *uctl); +int avs_control_mute_put(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *uctl); +int avs_control_mute_info(struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo); #endif diff --git a/sound/soc/intel/avs/core.c b/sound/soc/intel/avs/core.c index d7f8940099ce..ec1b3f55cb5c 100644 --- a/sound/soc/intel/avs/core.c +++ b/sound/soc/intel/avs/core.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only // -// Copyright(c) 2021-2022 Intel Corporation. All rights reserved. +// Copyright(c) 2021-2022 Intel Corporation // // Authors: Cezary Rojewski <cezary.rojewski@intel.com> // Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> @@ -14,19 +14,21 @@ // foundation of this driver // +#include <linux/acpi.h> #include <linux/module.h> #include <linux/pci.h> +#include <acpi/nhlt.h> #include <sound/hda_codec.h> #include <sound/hda_i915.h> #include <sound/hda_register.h> #include <sound/hdaudio.h> #include <sound/hdaudio_ext.h> #include <sound/intel-dsp-config.h> -#include <sound/intel-nhlt.h> #include "../../codecs/hda.h" #include "avs.h" #include "cldma.h" #include "messages.h" +#include "pcm.h" static u32 pgctl_mask = AZX_PGCTL_LSRMD_MASK; module_param(pgctl_mask, uint, 0444); @@ -52,14 +54,17 @@ void avs_hda_power_gating_enable(struct avs_dev *adev, bool enable) { u32 value = enable ? 0 : pgctl_mask; - avs_hda_update_config_dword(&adev->base.core, AZX_PCIREG_PGCTL, pgctl_mask, value); + if (!avs_platattr_test(adev, ACE)) + avs_hda_update_config_dword(&adev->base.core, AZX_PCIREG_PGCTL, pgctl_mask, value); } static void avs_hdac_clock_gating_enable(struct hdac_bus *bus, bool enable) { + struct avs_dev *adev = hdac_to_avs(bus); u32 value = enable ? cgctl_mask : 0; - avs_hda_update_config_dword(bus, AZX_PCIREG_CGCTL, cgctl_mask, value); + if (!avs_platattr_test(adev, ACE)) + avs_hda_update_config_dword(bus, AZX_PCIREG_CGCTL, cgctl_mask, value); } void avs_hda_clock_gating_enable(struct avs_dev *adev, bool enable) @@ -69,6 +74,8 @@ void avs_hda_clock_gating_enable(struct avs_dev *adev, bool enable) void avs_hda_l1sen_enable(struct avs_dev *adev, bool enable) { + if (avs_platattr_test(adev, ACE)) + return; if (enable) { if (atomic_inc_and_test(&adev->l1sen_counter)) snd_hdac_chip_updatel(&adev->base.core, VS_EM2, AZX_VS_EM2_L1SEN, @@ -97,6 +104,7 @@ static int avs_hdac_bus_init_streams(struct hdac_bus *bus) static bool avs_hdac_bus_init_chip(struct hdac_bus *bus, bool full_reset) { + struct avs_dev *adev = hdac_to_avs(bus); struct hdac_ext_link *hlink; bool ret; @@ -112,7 +120,8 @@ static bool avs_hdac_bus_init_chip(struct hdac_bus *bus, bool full_reset) /* Set DUM bit to address incorrect position reporting for capture * streams. In order to do so, CTRL needs to be out of reset state */ - snd_hdac_chip_updatel(bus, VS_EM2, AZX_VS_EM2_DUM, AZX_VS_EM2_DUM); + if (!avs_platattr_test(adev, ACE)) + snd_hdac_chip_updatel(bus, VS_EM2, AZX_VS_EM2_DUM, AZX_VS_EM2_DUM); return ret; } @@ -209,15 +218,13 @@ static void avs_hda_probe_work(struct work_struct *work) snd_hdac_ext_bus_ppcap_enable(bus, true); snd_hdac_ext_bus_ppcap_int_enable(bus, true); + avs_debugfs_init(adev); ret = avs_dsp_first_boot_firmware(adev); if (ret < 0) return; - adev->nhlt = intel_nhlt_init(adev->dev); - if (!adev->nhlt) - dev_info(bus->dev, "platform has no NHLT\n"); - avs_debugfs_init(adev); + acpi_nhlt_get_gbl_table(); avs_register_all_boards(adev); @@ -248,7 +255,7 @@ static void hdac_stream_update_pos(struct hdac_stream *stream, u64 buffer_size) static void hdac_update_stream(struct hdac_bus *bus, struct hdac_stream *stream) { if (stream->substream) { - snd_pcm_period_elapsed(stream->substream); + avs_period_elapsed(stream->substream); } else if (stream->cstream) { u64 buffer_size = stream->cstream->runtime->buffer_size; @@ -257,67 +264,55 @@ static void hdac_update_stream(struct hdac_bus *bus, struct hdac_stream *stream) } } -static irqreturn_t hdac_bus_irq_handler(int irq, void *context) +static irqreturn_t avs_hda_interrupt(struct hdac_bus *bus) { - struct hdac_bus *bus = context; - u32 mask, int_enable; + irqreturn_t ret = IRQ_NONE; u32 status; - int ret = IRQ_NONE; - - if (!pm_runtime_active(bus->dev)) - return ret; - - spin_lock(&bus->reg_lock); status = snd_hdac_chip_readl(bus, INTSTS); - if (status == 0 || status == UINT_MAX) { - spin_unlock(&bus->reg_lock); - return ret; - } + if (snd_hdac_bus_handle_stream_irq(bus, status, hdac_update_stream)) + ret = IRQ_HANDLED; - /* clear rirb int */ + spin_lock_irq(&bus->reg_lock); + /* Clear RIRB interrupt. */ status = snd_hdac_chip_readb(bus, RIRBSTS); if (status & RIRB_INT_MASK) { if (status & RIRB_INT_RESPONSE) snd_hdac_bus_update_rirb(bus); snd_hdac_chip_writeb(bus, RIRBSTS, RIRB_INT_MASK); - } - - mask = (0x1 << bus->num_streams) - 1; - - status = snd_hdac_chip_readl(bus, INTSTS); - status &= mask; - if (status) { - /* Disable stream interrupts; Re-enable in bottom half */ - int_enable = snd_hdac_chip_readl(bus, INTCTL); - snd_hdac_chip_writel(bus, INTCTL, (int_enable & (~mask))); - ret = IRQ_WAKE_THREAD; - } else { ret = IRQ_HANDLED; } - spin_unlock(&bus->reg_lock); + spin_unlock_irq(&bus->reg_lock); return ret; } -static irqreturn_t hdac_bus_irq_thread(int irq, void *context) +static irqreturn_t avs_hda_irq_handler(int irq, void *dev_id) +{ + struct hdac_bus *bus = dev_id; + u32 intsts; + + intsts = snd_hdac_chip_readl(bus, INTSTS); + if (intsts == UINT_MAX || !(intsts & AZX_INT_GLOBAL_EN)) + return IRQ_NONE; + + /* Mask GIE, unmasked in irq_thread(). */ + snd_hdac_chip_updatel(bus, INTCTL, AZX_INT_GLOBAL_EN, 0); + + return IRQ_WAKE_THREAD; +} + +static irqreturn_t avs_hda_irq_thread(int irq, void *dev_id) { - struct hdac_bus *bus = context; + struct hdac_bus *bus = dev_id; u32 status; - u32 int_enable; - u32 mask; - unsigned long flags; status = snd_hdac_chip_readl(bus, INTSTS); + if (status & ~AZX_INT_GLOBAL_EN) + avs_hda_interrupt(bus); - snd_hdac_bus_handle_stream_irq(bus, status, hdac_update_stream); - - /* Re-enable stream interrupts */ - mask = (0x1 << bus->num_streams) - 1; - spin_lock_irqsave(&bus->reg_lock, flags); - int_enable = snd_hdac_chip_readl(bus, INTCTL); - snd_hdac_chip_writel(bus, INTCTL, (int_enable | mask)); - spin_unlock_irqrestore(&bus->reg_lock, flags); + /* Unmask GIE, masked in irq_handler(). */ + snd_hdac_chip_updatel(bus, INTCTL, AZX_INT_GLOBAL_EN, AZX_INT_GLOBAL_EN); return IRQ_HANDLED; } @@ -326,14 +321,23 @@ static irqreturn_t avs_dsp_irq_handler(int irq, void *dev_id) { struct avs_dev *adev = dev_id; - return avs_dsp_op(adev, irq_handler); + return avs_hda_irq_handler(irq, &adev->base.core); } static irqreturn_t avs_dsp_irq_thread(int irq, void *dev_id) { struct avs_dev *adev = dev_id; + struct hdac_bus *bus = &adev->base.core; + u32 status; - return avs_dsp_op(adev, irq_thread); + status = readl(bus->ppcap + AZX_REG_PP_PPSTS); + if (status & AZX_PPCTL_PIE) + avs_dsp_op(adev, dsp_interrupt); + + /* Unmask GIE, masked in irq_handler(). */ + snd_hdac_chip_updatel(bus, INTCTL, AZX_INT_GLOBAL_EN, AZX_INT_GLOBAL_EN); + + return IRQ_HANDLED; } static int avs_hdac_acquire_irq(struct avs_dev *adev) @@ -343,13 +347,13 @@ static int avs_hdac_acquire_irq(struct avs_dev *adev) int ret; /* request one and check that we only got one interrupt */ - ret = pci_alloc_irq_vectors(pci, 1, 1, PCI_IRQ_MSI | PCI_IRQ_LEGACY); + ret = pci_alloc_irq_vectors(pci, 1, 1, PCI_IRQ_MSI | PCI_IRQ_INTX); if (ret != 1) { dev_err(adev->dev, "Failed to allocate IRQ vector: %d\n", ret); return ret; } - ret = pci_request_irq(pci, 0, hdac_bus_irq_handler, hdac_bus_irq_thread, bus, + ret = pci_request_irq(pci, 0, avs_hda_irq_handler, avs_hda_irq_thread, bus, KBUILD_MODNAME); if (ret < 0) { dev_err(adev->dev, "Failed to request stream IRQ handler: %d\n", ret); @@ -426,8 +430,14 @@ static int avs_pci_probe(struct pci_dev *pci, const struct pci_device_id *id) int ret; ret = snd_intel_dsp_driver_probe(pci); - if (ret != SND_INTEL_DSP_DRIVER_ANY && ret != SND_INTEL_DSP_DRIVER_AVS) + switch (ret) { + case SND_INTEL_DSP_DRIVER_ANY: + case SND_INTEL_DSP_DRIVER_SST: + case SND_INTEL_DSP_DRIVER_AVS: + break; + default: return -ENODEV; + } ret = pcim_enable_device(pci); if (ret < 0) @@ -442,7 +452,7 @@ static int avs_pci_probe(struct pci_dev *pci, const struct pci_device_id *id) return ret; } - ret = pci_request_regions(pci, "AVS HDAudio"); + ret = pcim_request_all_regions(pci, "AVS HDAudio"); if (ret < 0) return ret; @@ -451,8 +461,7 @@ static int avs_pci_probe(struct pci_dev *pci, const struct pci_device_id *id) bus->remap_addr = pci_ioremap_bar(pci, 0); if (!bus->remap_addr) { dev_err(bus->dev, "ioremap error\n"); - ret = -ENXIO; - goto err_remap_bar0; + return -ENXIO; } adev->dsp_ba = pci_ioremap_bar(pci, 4); @@ -509,8 +518,6 @@ err_init_streams: iounmap(adev->dsp_ba); err_remap_bar4: iounmap(bus->remap_addr); -err_remap_bar0: - pci_release_regions(pci); return ret; } @@ -530,8 +537,6 @@ static void avs_pci_shutdown(struct pci_dev *pci) snd_hdac_bus_stop_chip(bus); snd_hdac_display_power(bus, HDA_CODEC_IDX_CONTROLLER, false); - if (avs_platattr_test(adev, CLDMA)) - pci_free_irq(pci, 0, &code_loader); pci_free_irq(pci, 0, adev); pci_free_irq(pci, 0, bus); pci_free_irq_vectors(pci); @@ -548,9 +553,8 @@ static void avs_pci_remove(struct pci_dev *pci) avs_unregister_all_boards(adev); + acpi_nhlt_put_gbl_table(); avs_debugfs_exit(adev); - if (adev->nhlt) - intel_nhlt_free(adev->nhlt); if (avs_platattr_test(adev, CLDMA)) hda_cldma_free(&code_loader); @@ -584,7 +588,6 @@ static void avs_pci_remove(struct pci_dev *pci) pci_free_irq_vectors(pci); iounmap(bus->remap_addr); iounmap(adev->dsp_ba); - pci_release_regions(pci); /* Firmware is not needed anymore */ avs_release_firmwares(adev); @@ -612,7 +615,7 @@ static int avs_suspend_standby(struct avs_dev *adev) return 0; } -static int __maybe_unused avs_suspend_common(struct avs_dev *adev, bool low_power) +static int avs_suspend_common(struct avs_dev *adev, bool low_power) { struct hdac_bus *bus = &adev->base.core; int ret; @@ -673,7 +676,7 @@ static int avs_resume_standby(struct avs_dev *adev) return 0; } -static int __maybe_unused avs_resume_common(struct avs_dev *adev, bool low_power, bool purge) +static int avs_resume_common(struct avs_dev *adev, bool low_power, bool purge) { struct hdac_bus *bus = &adev->base.core; int ret; @@ -696,41 +699,41 @@ static int __maybe_unused avs_resume_common(struct avs_dev *adev, bool low_power return 0; } -static int __maybe_unused avs_suspend(struct device *dev) +static int avs_suspend(struct device *dev) { return avs_suspend_common(to_avs_dev(dev), true); } -static int __maybe_unused avs_resume(struct device *dev) +static int avs_resume(struct device *dev) { return avs_resume_common(to_avs_dev(dev), true, true); } -static int __maybe_unused avs_runtime_suspend(struct device *dev) +static int avs_runtime_suspend(struct device *dev) { return avs_suspend_common(to_avs_dev(dev), true); } -static int __maybe_unused avs_runtime_resume(struct device *dev) +static int avs_runtime_resume(struct device *dev) { return avs_resume_common(to_avs_dev(dev), true, false); } -static int __maybe_unused avs_freeze(struct device *dev) +static int avs_freeze(struct device *dev) { return avs_suspend_common(to_avs_dev(dev), false); } -static int __maybe_unused avs_thaw(struct device *dev) +static int avs_thaw(struct device *dev) { return avs_resume_common(to_avs_dev(dev), false, true); } -static int __maybe_unused avs_poweroff(struct device *dev) +static int avs_poweroff(struct device *dev) { return avs_suspend_common(to_avs_dev(dev), false); } -static int __maybe_unused avs_restore(struct device *dev) +static int avs_restore(struct device *dev) { return avs_resume_common(to_avs_dev(dev), false, true); } @@ -742,19 +745,22 @@ static const struct dev_pm_ops avs_dev_pm = { .thaw = avs_thaw, .poweroff = avs_poweroff, .restore = avs_restore, - SET_RUNTIME_PM_OPS(avs_runtime_suspend, avs_runtime_resume, NULL) + RUNTIME_PM_OPS(avs_runtime_suspend, avs_runtime_resume, NULL) }; static const struct avs_sram_spec skl_sram_spec = { .base_offset = SKL_ADSP_SRAM_BASE_OFFSET, .window_size = SKL_ADSP_SRAM_WINDOW_SIZE, - .rom_status_offset = SKL_ADSP_SRAM_BASE_OFFSET, }; static const struct avs_sram_spec apl_sram_spec = { .base_offset = APL_ADSP_SRAM_BASE_OFFSET, .window_size = APL_ADSP_SRAM_WINDOW_SIZE, - .rom_status_offset = APL_ADSP_SRAM_BASE_OFFSET, +}; + +static const struct avs_sram_spec mtl_sram_spec = { + .base_offset = MTL_ADSP_SRAM_BASE_OFFSET, + .window_size = MTL_ADSP_SRAM_WINDOW_SIZE, }; static const struct avs_hipc_spec skl_hipc_spec = { @@ -766,6 +772,19 @@ static const struct avs_hipc_spec skl_hipc_spec = { .rsp_offset = SKL_ADSP_REG_HIPCT, .rsp_busy_mask = SKL_ADSP_HIPCT_BUSY, .ctl_offset = SKL_ADSP_REG_HIPCCTL, + .sts_offset = SKL_ADSP_SRAM_BASE_OFFSET, +}; + +static const struct avs_hipc_spec apl_hipc_spec = { + .req_offset = SKL_ADSP_REG_HIPCI, + .req_ext_offset = SKL_ADSP_REG_HIPCIE, + .req_busy_mask = SKL_ADSP_HIPCI_BUSY, + .ack_offset = SKL_ADSP_REG_HIPCIE, + .ack_done_mask = SKL_ADSP_HIPCIE_DONE, + .rsp_offset = SKL_ADSP_REG_HIPCT, + .rsp_busy_mask = SKL_ADSP_HIPCT_BUSY, + .ctl_offset = SKL_ADSP_REG_HIPCCTL, + .sts_offset = APL_ADSP_SRAM_BASE_OFFSET, }; static const struct avs_hipc_spec cnl_hipc_spec = { @@ -777,6 +796,19 @@ static const struct avs_hipc_spec cnl_hipc_spec = { .rsp_offset = CNL_ADSP_REG_HIPCTDR, .rsp_busy_mask = CNL_ADSP_HIPCTDR_BUSY, .ctl_offset = CNL_ADSP_REG_HIPCCTL, + .sts_offset = APL_ADSP_SRAM_BASE_OFFSET, +}; + +static const struct avs_hipc_spec lnl_hipc_spec = { + .req_offset = MTL_REG_HfIPCxIDR, + .req_ext_offset = MTL_REG_HfIPCxIDD, + .req_busy_mask = MTL_HfIPCxIDR_BUSY, + .ack_offset = MTL_REG_HfIPCxIDA, + .ack_done_mask = MTL_HfIPCxIDA_DONE, + .rsp_offset = MTL_REG_HfIPCxTDR, + .rsp_busy_mask = MTL_HfIPCxTDR_BUSY, + .ctl_offset = MTL_REG_HfIPCxCTL, + .sts_offset = LNL_REG_HfDFR(0), }; static const struct avs_spec skl_desc = { @@ -796,7 +828,7 @@ static const struct avs_spec apl_desc = { .core_init_mask = 3, .attributes = AVS_PLATATTR_IMR, .sram = &apl_sram_spec, - .hipc = &skl_hipc_spec, + .hipc = &apl_hipc_spec, }; static const struct avs_spec cnl_desc = { @@ -829,10 +861,10 @@ static const struct avs_spec jsl_desc = { .hipc = &cnl_hipc_spec, }; -#define AVS_TGL_BASED_SPEC(sname) \ +#define AVS_TGL_BASED_SPEC(sname, min) \ static const struct avs_spec sname##_desc = { \ .name = #sname, \ - .min_fw_version = { 10, 29, 0, 5646 }, \ + .min_fw_version = { 10, min, 0, 5646 }, \ .dsp_ops = &avs_tgl_dsp_ops, \ .core_init_mask = 1, \ .attributes = AVS_PLATATTR_IMR, \ @@ -840,11 +872,21 @@ static const struct avs_spec sname##_desc = { \ .hipc = &cnl_hipc_spec, \ } -AVS_TGL_BASED_SPEC(lkf); -AVS_TGL_BASED_SPEC(tgl); -AVS_TGL_BASED_SPEC(ehl); -AVS_TGL_BASED_SPEC(adl); -AVS_TGL_BASED_SPEC(adl_n); +AVS_TGL_BASED_SPEC(lkf, 28); +AVS_TGL_BASED_SPEC(tgl, 29); +AVS_TGL_BASED_SPEC(ehl, 30); +AVS_TGL_BASED_SPEC(adl, 35); +AVS_TGL_BASED_SPEC(adl_n, 35); + +static const struct avs_spec fcl_desc = { + .name = "fcl", + .min_fw_version = { 0 }, + .dsp_ops = &avs_ptl_dsp_ops, + .core_init_mask = 1, + .attributes = AVS_PLATATTR_IMR | AVS_PLATATTR_ACE | AVS_PLATATTR_ALTHDA, + .sram = &mtl_sram_spec, + .hipc = &lnl_hipc_spec, +}; static const struct pci_device_id avs_ids[] = { { PCI_DEVICE_DATA(INTEL, HDA_SKL_LP, &skl_desc) }, @@ -881,6 +923,7 @@ static const struct pci_device_id avs_ids[] = { { PCI_DEVICE_DATA(INTEL, HDA_RPL_P_1, &adl_desc) }, { PCI_DEVICE_DATA(INTEL, HDA_RPL_M, &adl_desc) }, { PCI_DEVICE_DATA(INTEL, HDA_RPL_PX, &adl_desc) }, + { PCI_DEVICE_DATA(INTEL, HDA_FCL, &fcl_desc) }, { 0 } }; MODULE_DEVICE_TABLE(pci, avs_ids); @@ -893,7 +936,7 @@ static struct pci_driver avs_pci_driver = { .shutdown = avs_pci_shutdown, .dev_groups = avs_attr_groups, .driver = { - .pm = &avs_dev_pm, + .pm = pm_ptr(&avs_dev_pm), }, }; module_pci_driver(avs_pci_driver); @@ -902,3 +945,14 @@ MODULE_AUTHOR("Cezary Rojewski <cezary.rojewski@intel.com>"); MODULE_AUTHOR("Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>"); MODULE_DESCRIPTION("Intel cAVS sound driver"); MODULE_LICENSE("GPL"); +MODULE_FIRMWARE("intel/avs/skl/dsp_basefw.bin"); +MODULE_FIRMWARE("intel/avs/apl/dsp_basefw.bin"); +MODULE_FIRMWARE("intel/avs/cnl/dsp_basefw.bin"); +MODULE_FIRMWARE("intel/avs/icl/dsp_basefw.bin"); +MODULE_FIRMWARE("intel/avs/jsl/dsp_basefw.bin"); +MODULE_FIRMWARE("intel/avs/lkf/dsp_basefw.bin"); +MODULE_FIRMWARE("intel/avs/tgl/dsp_basefw.bin"); +MODULE_FIRMWARE("intel/avs/ehl/dsp_basefw.bin"); +MODULE_FIRMWARE("intel/avs/adl/dsp_basefw.bin"); +MODULE_FIRMWARE("intel/avs/adl_n/dsp_basefw.bin"); +MODULE_FIRMWARE("intel/fcl/dsp_basefw.bin"); diff --git a/sound/soc/intel/avs/debugfs.c b/sound/soc/intel/avs/debugfs.c index 4dfbff0ce508..c625cf879f17 100644 --- a/sound/soc/intel/avs/debugfs.c +++ b/sound/soc/intel/avs/debugfs.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only // -// Copyright(c) 2021-2022 Intel Corporation. All rights reserved. +// Copyright(c) 2021-2022 Intel Corporation // // Authors: Cezary Rojewski <cezary.rojewski@intel.com> // Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> @@ -10,6 +10,7 @@ #include <linux/kfifo.h> #include <linux/wait.h> #include <linux/sched/signal.h> +#include <linux/string_helpers.h> #include <sound/soc.h> #include "avs.h" #include "messages.h" @@ -68,7 +69,6 @@ static ssize_t fw_regs_read(struct file *file, char __user *to, size_t count, lo static const struct file_operations fw_regs_fops = { .open = simple_open, .read = fw_regs_read, - .llseek = no_llseek, }; static ssize_t debug_window_read(struct file *file, char __user *to, size_t count, loff_t *ppos) @@ -93,7 +93,6 @@ static ssize_t debug_window_read(struct file *file, char __user *to, size_t coun static const struct file_operations debug_window_fops = { .open = simple_open, .read = debug_window_read, - .llseek = no_llseek, }; static ssize_t probe_points_read(struct file *file, char __user *to, size_t count, loff_t *ppos) @@ -145,7 +144,7 @@ static ssize_t probe_points_write(struct file *file, const char __user *from, si int ret; ret = parse_int_array_user(from, count, (int **)&array); - if (ret < 0) + if (ret) return ret; num_elems = *array; @@ -170,7 +169,6 @@ static const struct file_operations probe_points_fops = { .open = simple_open, .read = probe_points_read, .write = probe_points_write, - .llseek = no_llseek, }; static ssize_t probe_points_disconnect_write(struct file *file, const char __user *from, @@ -183,7 +181,7 @@ static ssize_t probe_points_disconnect_write(struct file *file, const char __use int ret; ret = parse_int_array_user(from, count, (int **)&array); - if (ret < 0) + if (ret) return ret; num_elems = *array; @@ -371,11 +369,14 @@ static ssize_t trace_control_write(struct file *file, const char __user *from, s int ret; ret = parse_int_array_user(from, count, (int **)&array); - if (ret < 0) + if (ret) return ret; num_elems = *array; - resource_mask = array[1]; + if (!num_elems) { + ret = -EINVAL; + goto free_array; + } /* * Disable if just resource mask is provided - no log priority flags. @@ -383,6 +384,7 @@ static ssize_t trace_control_write(struct file *file, const char __user *from, s * Enable input format: mask, prio1, .., prioN * Where 'N' equals number of bits set in the 'mask'. */ + resource_mask = array[1]; if (num_elems == 1) { ret = disable_logs(adev, resource_mask); } else { diff --git a/sound/soc/intel/avs/dsp.c b/sound/soc/intel/avs/dsp.c index aa03af4473e9..464bd6859182 100644 --- a/sound/soc/intel/avs/dsp.c +++ b/sound/soc/intel/avs/dsp.c @@ -1,18 +1,17 @@ // SPDX-License-Identifier: GPL-2.0-only // -// Copyright(c) 2021-2022 Intel Corporation. All rights reserved. +// Copyright(c) 2021-2022 Intel Corporation // // Authors: Cezary Rojewski <cezary.rojewski@intel.com> // Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> // +#include <linux/string_choices.h> #include <sound/hdaudio_ext.h> #include "avs.h" #include "registers.h" #include "trace.h" -#define AVS_ADSPCS_INTERVAL_US 500 -#define AVS_ADSPCS_TIMEOUT_US 50000 #define AVS_ADSPCS_DELAY_US 1000 int avs_dsp_core_power(struct avs_dev *adev, u32 core_mask, bool power) @@ -39,7 +38,7 @@ int avs_dsp_core_power(struct avs_dev *adev, u32 core_mask, bool power) AVS_ADSPCS_TIMEOUT_US); if (ret) dev_err(adev->dev, "core_mask %d power %s failed: %d\n", - core_mask, power ? "on" : "off", ret); + core_mask, str_on_off(power), ret); return ret; } diff --git a/sound/soc/intel/avs/icl.c b/sound/soc/intel/avs/icl.c index 9d9921e1cd4d..f8d327ea2656 100644 --- a/sound/soc/intel/avs/icl.c +++ b/sound/soc/intel/avs/icl.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only // -// Copyright(c) 2021-2024 Intel Corporation. All rights reserved. +// Copyright(c) 2021-2024 Intel Corporation // // Authors: Cezary Rojewski <cezary.rojewski@intel.com> // Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> @@ -52,22 +52,25 @@ union avs_icl_memwnd2_slot_type { u32 type:24; }; } __packed; +static_assert(sizeof(union avs_icl_memwnd2_slot_type) == 4); struct avs_icl_memwnd2_desc { u32 resource_id; union avs_icl_memwnd2_slot_type slot_id; u32 vma; } __packed; +static_assert(sizeof(struct avs_icl_memwnd2_desc) == 12); #define AVS_ICL_MEMWND2_SLOTS_COUNT 15 struct avs_icl_memwnd2 { union { struct avs_icl_memwnd2_desc slot_desc[AVS_ICL_MEMWND2_SLOTS_COUNT]; - u8 rsvd[PAGE_SIZE]; + u8 rsvd[SZ_4K]; }; - u8 slot_array[AVS_ICL_MEMWND2_SLOTS_COUNT][PAGE_SIZE]; + u8 slot_array[AVS_ICL_MEMWND2_SLOTS_COUNT][SZ_4K]; } __packed; +static_assert(sizeof(struct avs_icl_memwnd2) == 65536); #define AVS_ICL_SLOT_UNUSED \ ((union avs_icl_memwnd2_slot_type) { 0x00000000U }) @@ -89,8 +92,7 @@ static int avs_icl_slot_offset(struct avs_dev *adev, union avs_icl_memwnd2_slot_ for (i = 0; i < AVS_ICL_MEMWND2_SLOTS_COUNT; i++) if (desc[i].slot_id.val == slot_type.val) - return offsetof(struct avs_icl_memwnd2, slot_array) + - avs_skl_log_buffer_offset(adev, i); + return offsetof(struct avs_icl_memwnd2, slot_array) + i * SZ_4K; return -ENXIO; } @@ -110,6 +112,10 @@ int avs_icl_log_buffer_offset(struct avs_dev *adev, u32 core) bool avs_icl_d0ix_toggle(struct avs_dev *adev, struct avs_ipc_msg *tx, bool wake) { + /* Full-power when starting DMA engines. */ + if (tx->glb.set_ppl_state.state == AVS_PPL_STATE_RUNNING) + return true; + /* Payload-less IPCs do not take part in d0ix toggling. */ return tx->size; } @@ -182,8 +188,7 @@ const struct avs_dsp_ops avs_icl_dsp_ops = { .power = avs_dsp_core_power, .reset = avs_dsp_core_reset, .stall = avs_dsp_core_stall, - .irq_handler = avs_irq_handler, - .irq_thread = avs_cnl_irq_thread, + .dsp_interrupt = avs_cnl_dsp_interrupt, .int_control = avs_dsp_interrupt_control, .load_basefw = avs_icl_load_basefw, .load_lib = avs_hda_load_library, diff --git a/sound/soc/intel/avs/ipc.c b/sound/soc/intel/avs/ipc.c index ad0e535b3c2e..0314f9d4ea5f 100644 --- a/sound/soc/intel/avs/ipc.c +++ b/sound/soc/intel/avs/ipc.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only // -// Copyright(c) 2021-2022 Intel Corporation. All rights reserved. +// Copyright(c) 2021-2022 Intel Corporation // // Authors: Cezary Rojewski <cezary.rojewski@intel.com> // Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> @@ -169,7 +169,9 @@ static void avs_dsp_exception_caught(struct avs_dev *adev, union avs_notify_msg dev_crit(adev->dev, "communication severed, rebooting dsp..\n"); - cancel_delayed_work_sync(&ipc->d0ix_work); + /* Avoid deadlock as the exception may be the response to SET_D0IX. */ + if (current_work() != &ipc->d0ix_work.work) + cancel_delayed_work_sync(&ipc->d0ix_work); ipc->in_d0ix = false; /* Re-enabled on recovery completion. */ pm_runtime_disable(adev->dev); @@ -184,10 +186,11 @@ static void avs_dsp_receive_rx(struct avs_dev *adev, u64 header) { struct avs_ipc *ipc = adev->ipc; union avs_reply_msg msg = AVS_MSG(header); - u64 reg; + u32 sts, lec; - reg = readq(avs_sram_addr(adev, AVS_FW_REGS_WINDOW)); - trace_avs_ipc_reply_msg(header, reg); + sts = snd_hdac_adsp_readl(adev, AVS_FW_REG_STATUS(adev)); + lec = snd_hdac_adsp_readl(adev, AVS_FW_REG_ERROR(adev)); + trace_avs_ipc_reply_msg(header, sts, lec); ipc->rx.header = header; /* Abort copying payload if request processing was unsuccessful. */ @@ -209,10 +212,11 @@ static void avs_dsp_process_notification(struct avs_dev *adev, u64 header) union avs_notify_msg msg = AVS_MSG(header); size_t data_size = 0; void *data = NULL; - u64 reg; + u32 sts, lec; - reg = readq(avs_sram_addr(adev, AVS_FW_REGS_WINDOW)); - trace_avs_ipc_notify_msg(header, reg); + sts = snd_hdac_adsp_readl(adev, AVS_FW_REG_STATUS(adev)); + lec = snd_hdac_adsp_readl(adev, AVS_FW_REG_ERROR(adev)); + trace_avs_ipc_notify_msg(header, sts, lec); /* Ignore spurious notifications until handshake is established. */ if (!adev->ipc->ready && msg.notify_msg_type != AVS_NOTIFY_FW_READY) { @@ -301,54 +305,6 @@ void avs_dsp_process_response(struct avs_dev *adev, u64 header) complete(&ipc->busy_completion); } -irqreturn_t avs_irq_handler(struct avs_dev *adev) -{ - struct avs_ipc *ipc = adev->ipc; - const struct avs_spec *const spec = adev->spec; - u32 adspis, hipc_rsp, hipc_ack; - irqreturn_t ret = IRQ_NONE; - - adspis = snd_hdac_adsp_readl(adev, AVS_ADSP_REG_ADSPIS); - if (adspis == UINT_MAX || !(adspis & AVS_ADSP_ADSPIS_IPC)) - return ret; - - hipc_ack = snd_hdac_adsp_readl(adev, spec->hipc->ack_offset); - hipc_rsp = snd_hdac_adsp_readl(adev, spec->hipc->rsp_offset); - - /* DSP acked host's request */ - if (hipc_ack & spec->hipc->ack_done_mask) { - /* - * As an extra precaution, mask done interrupt. Code executed - * due to complete() found below does not assume any masking. - */ - snd_hdac_adsp_updatel(adev, spec->hipc->ctl_offset, - AVS_ADSP_HIPCCTL_DONE, 0); - - complete(&ipc->done_completion); - - /* tell DSP it has our attention */ - snd_hdac_adsp_updatel(adev, spec->hipc->ack_offset, - spec->hipc->ack_done_mask, - spec->hipc->ack_done_mask); - /* unmask done interrupt */ - snd_hdac_adsp_updatel(adev, spec->hipc->ctl_offset, - AVS_ADSP_HIPCCTL_DONE, - AVS_ADSP_HIPCCTL_DONE); - ret = IRQ_HANDLED; - } - - /* DSP sent new response to process */ - if (hipc_rsp & spec->hipc->rsp_busy_mask) { - /* mask busy interrupt */ - snd_hdac_adsp_updatel(adev, spec->hipc->ctl_offset, - AVS_ADSP_HIPCCTL_BUSY, 0); - - ret = IRQ_WAKE_THREAD; - } - - return ret; -} - static bool avs_ipc_is_busy(struct avs_ipc *ipc) { struct avs_dev *adev = to_avs_dev(ipc->dev); @@ -415,13 +371,16 @@ static void avs_ipc_msg_init(struct avs_ipc *ipc, struct avs_ipc_msg *reply) static void avs_dsp_send_tx(struct avs_dev *adev, struct avs_ipc_msg *tx, bool read_fwregs) { const struct avs_spec *const spec = adev->spec; - u64 reg = ULONG_MAX; + u32 sts = UINT_MAX; + u32 lec = UINT_MAX; tx->header |= spec->hipc->req_busy_mask; - if (read_fwregs) - reg = readq(avs_sram_addr(adev, AVS_FW_REGS_WINDOW)); + if (read_fwregs) { + sts = snd_hdac_adsp_readl(adev, AVS_FW_REG_STATUS(adev)); + lec = snd_hdac_adsp_readl(adev, AVS_FW_REG_ERROR(adev)); + } - trace_avs_request(tx, reg); + trace_avs_request(tx, sts, lec); if (tx->size) memcpy_toio(avs_downlink_addr(adev), tx->data, tx->size); diff --git a/sound/soc/intel/avs/lnl.c b/sound/soc/intel/avs/lnl.c new file mode 100644 index 000000000000..03208596dfb1 --- /dev/null +++ b/sound/soc/intel/avs/lnl.c @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright(c) 2021-2025 Intel Corporation + * + * Authors: Cezary Rojewski <cezary.rojewski@intel.com> + * Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> + */ + +#include <sound/hdaudio_ext.h> +#include "avs.h" +#include "registers.h" + +int avs_lnl_core_stall(struct avs_dev *adev, u32 core_mask, bool stall) +{ + struct hdac_bus *bus = &adev->base.core; + struct hdac_ext_link *hlink; + int ret; + + ret = avs_mtl_core_stall(adev, core_mask, stall); + + /* On unstall, route interrupts from the links to the DSP firmware. */ + if (!ret && !stall) + list_for_each_entry(hlink, &bus->hlink_list, list) + snd_hdac_updatel(hlink->ml_addr, AZX_REG_ML_LCTL, AZX_ML_LCTL_OFLEN, + AZX_ML_LCTL_OFLEN); + return ret; +} diff --git a/sound/soc/intel/avs/loader.c b/sound/soc/intel/avs/loader.c index 8e34d3536082..353e343b1d28 100644 --- a/sound/soc/intel/avs/loader.c +++ b/sound/soc/intel/avs/loader.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only // -// Copyright(c) 2021-2022 Intel Corporation. All rights reserved. +// Copyright(c) 2021-2022 Intel Corporation // // Authors: Cezary Rojewski <cezary.rojewski@intel.com> // Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> @@ -9,6 +9,7 @@ #include <linux/firmware.h> #include <linux/module.h> #include <linux/slab.h> +#include <linux/string.h> #include <sound/hdaudio.h> #include <sound/hdaudio_ext.h> #include "avs.h" @@ -56,6 +57,7 @@ struct avs_fw_manifest { u32 feature_mask; struct avs_fw_version version; } __packed; +static_assert(sizeof(struct avs_fw_manifest) == 36); struct avs_fw_ext_manifest { u32 id; @@ -64,6 +66,7 @@ struct avs_fw_ext_manifest { u16 version_minor; u32 entries; } __packed; +static_assert(sizeof(struct avs_fw_ext_manifest) == 16); static int avs_fw_ext_manifest_strip(struct firmware *fw) { @@ -165,7 +168,8 @@ int avs_cldma_load_basefw(struct avs_dev *adev, struct firmware *fw) (reg & AVS_ROM_INIT_DONE) == AVS_ROM_INIT_DONE, AVS_ROM_INIT_POLLING_US, SKL_ROM_INIT_TIMEOUT_US); if (ret < 0) { - dev_err(adev->dev, "rom init timeout: %d\n", ret); + dev_err(adev->dev, "rom init failed: %d, status: 0x%08x, lec: 0x%08x\n", + ret, reg, snd_hdac_adsp_readl(adev, AVS_FW_REG_ERROR(adev))); avs_dsp_core_disable(adev, AVS_MAIN_CORE_MASK); return ret; } @@ -178,7 +182,8 @@ int avs_cldma_load_basefw(struct avs_dev *adev, struct firmware *fw) AVS_FW_INIT_POLLING_US, AVS_FW_INIT_TIMEOUT_US); hda_cldma_stop(cl); if (ret < 0) { - dev_err(adev->dev, "transfer fw failed: %d\n", ret); + dev_err(adev->dev, "transfer fw failed: %d, status: 0x%08x, lec: 0x%08x\n", + ret, reg, snd_hdac_adsp_readl(adev, AVS_FW_REG_ERROR(adev))); avs_dsp_core_disable(adev, AVS_MAIN_CORE_MASK); return ret; } @@ -306,12 +311,13 @@ avs_hda_init_rom(struct avs_dev *adev, unsigned int dma_id, bool purge) } /* await ROM init */ - ret = snd_hdac_adsp_readq_poll(adev, spec->sram->rom_status_offset, reg, + ret = snd_hdac_adsp_readl_poll(adev, spec->hipc->sts_offset, reg, (reg & 0xF) == AVS_ROM_INIT_DONE || (reg & 0xF) == APL_ROM_FW_ENTERED, AVS_ROM_INIT_POLLING_US, APL_ROM_INIT_TIMEOUT_US); if (ret < 0) { - dev_err(adev->dev, "rom init timeout: %d\n", ret); + dev_err(adev->dev, "rom init failed: %d, status: 0x%08x, lec: 0x%08x\n", + ret, reg, snd_hdac_adsp_readl(adev, AVS_FW_REG_ERROR(adev))); goto err; } @@ -335,15 +341,15 @@ static int avs_imr_load_basefw(struct avs_dev *adev) /* DMA id ignored when flashing from IMR as no transfer occurs. */ ret = avs_hda_init_rom(adev, 0, false); - if (ret < 0) { - dev_err(adev->dev, "rom init failed: %d\n", ret); + if (ret < 0) return ret; - } ret = wait_for_completion_timeout(&adev->fw_ready, msecs_to_jiffies(AVS_FW_INIT_TIMEOUT_MS)); if (!ret) { - dev_err(adev->dev, "firmware ready timeout\n"); + dev_err(adev->dev, "firmware ready timeout, status: 0x%08x, lec: 0x%08x\n", + snd_hdac_adsp_readl(adev, AVS_FW_REG_STATUS(adev)), + snd_hdac_adsp_readl(adev, AVS_FW_REG_ERROR(adev))); avs_dsp_core_disable(adev, AVS_MAIN_CORE_MASK); return -ETIMEDOUT; } @@ -390,7 +396,7 @@ int avs_hda_load_basefw(struct avs_dev *adev, struct firmware *fw) ret = avs_hda_init_rom(adev, dma_id, true); if (!ret) break; - dev_info(adev->dev, "#%d rom init fail: %d\n", i + 1, ret); + dev_info(adev->dev, "#%d rom init failed: %d\n", i + 1, ret); } if (ret < 0) goto cleanup_resources; @@ -402,7 +408,8 @@ int avs_hda_load_basefw(struct avs_dev *adev, struct firmware *fw) AVS_FW_INIT_POLLING_US, AVS_FW_INIT_TIMEOUT_US); snd_hdac_dsp_trigger(hstream, false); if (ret < 0) { - dev_err(adev->dev, "transfer fw failed: %d\n", ret); + dev_err(adev->dev, "transfer fw failed: %d, status: 0x%08x, lec: 0x%08x\n", + ret, reg, snd_hdac_adsp_readl(adev, AVS_FW_REG_ERROR(adev))); avs_dsp_core_disable(adev, AVS_MAIN_CORE_MASK); } @@ -535,7 +542,7 @@ int avs_dsp_load_libraries(struct avs_dev *adev, struct avs_tplg_library *libs, if (ret) return ret; - strncpy(adev->lib_names[id], man->name, AVS_LIB_NAME_SIZE); + strscpy(adev->lib_names[id], man->name, AVS_LIB_NAME_SIZE); id++; next_lib: i++; @@ -582,7 +589,9 @@ static int avs_dsp_load_basefw(struct avs_dev *adev) ret = wait_for_completion_timeout(&adev->fw_ready, msecs_to_jiffies(AVS_FW_INIT_TIMEOUT_MS)); if (!ret) { - dev_err(adev->dev, "firmware ready timeout\n"); + dev_err(adev->dev, "firmware ready timeout, status: 0x%08x, lec: 0x%08x\n", + snd_hdac_adsp_readl(adev, AVS_FW_REG_STATUS(adev)), + snd_hdac_adsp_readl(adev, AVS_FW_REG_ERROR(adev))); avs_dsp_core_disable(adev, AVS_MAIN_CORE_MASK); ret = -ETIMEDOUT; goto release_fw; @@ -595,7 +604,7 @@ release_fw: return ret; } -int avs_dsp_boot_firmware(struct avs_dev *adev, bool purge) +static int avs_load_firmware(struct avs_dev *adev, bool purge) { struct avs_soc_component *acomp; int ret, i; @@ -649,39 +658,49 @@ reenable_gating: return 0; } -int avs_dsp_first_boot_firmware(struct avs_dev *adev) +static int avs_config_basefw(struct avs_dev *adev) { - int ret, i; + int ret; - if (avs_platattr_test(adev, CLDMA)) { - ret = hda_cldma_init(&code_loader, &adev->base.core, - adev->dsp_ba, AVS_CL_DEFAULT_BUFFER_SIZE); - if (ret < 0) { - dev_err(adev->dev, "cldma init failed: %d\n", ret); + if (adev->spec->dsp_ops->config_basefw) { + ret = avs_dsp_op(adev, config_basefw); + if (ret) return ret; - } } - ret = avs_dsp_core_disable(adev, AVS_MAIN_CORE_MASK); - if (ret < 0) - return ret; + return 0; +} - ret = avs_dsp_boot_firmware(adev, true); - if (ret < 0) { - dev_err(adev->dev, "firmware boot failed: %d\n", ret); +int avs_dsp_boot_firmware(struct avs_dev *adev, bool purge) +{ + int ret; + + ret = avs_load_firmware(adev, purge); + if (ret) return ret; - } + + return avs_config_basefw(adev); +} + +static int avs_dsp_alloc_resources(struct avs_dev *adev) +{ + struct hdac_ext_link *link; + int ret, i; ret = avs_ipc_get_hw_config(adev, &adev->hw_cfg); - if (ret) { - dev_err(adev->dev, "get hw cfg failed: %d\n", ret); + if (ret) return AVS_IPC_RET(ret); - } ret = avs_ipc_get_fw_config(adev, &adev->fw_cfg); - if (ret) { - dev_err(adev->dev, "get fw cfg failed: %d\n", ret); + if (ret) return AVS_IPC_RET(ret); + + /* If hw allows, read capabilities directly from it. */ + if (avs_platattr_test(adev, ALTHDA)) { + link = snd_hdac_ext_bus_get_hlink_by_id(&adev->base.core, + AZX_REG_ML_LEPTR_ID_INTEL_SSP); + if (link) + adev->hw_cfg.i2s_caps.ctrl_count = link->slcount; } adev->core_refs = devm_kcalloc(adev->dev, adev->hw_cfg.dsp_cores, @@ -698,9 +717,34 @@ int avs_dsp_first_boot_firmware(struct avs_dev *adev) } /* basefw always occupies slot 0 */ - strcpy(&adev->lib_names[0][0], "BASEFW"); + strscpy(adev->lib_names[0], "BASEFW", AVS_LIB_NAME_SIZE); ida_init(&adev->ppl_ida); - return 0; } + +int avs_dsp_first_boot_firmware(struct avs_dev *adev) +{ + int ret; + + if (avs_platattr_test(adev, CLDMA)) { + ret = hda_cldma_init(&code_loader, &adev->base.core, + adev->dsp_ba, AVS_CL_DEFAULT_BUFFER_SIZE); + if (ret < 0) { + dev_err(adev->dev, "cldma init failed: %d\n", ret); + return ret; + } + } + + ret = avs_dsp_core_disable(adev, AVS_MAIN_CORE_MASK); + if (ret < 0) + return ret; + + ret = avs_dsp_boot_firmware(adev, true); + if (ret < 0) { + dev_err(adev->dev, "firmware boot failed: %d\n", ret); + return ret; + } + + return avs_dsp_alloc_resources(adev); +} diff --git a/sound/soc/intel/avs/messages.c b/sound/soc/intel/avs/messages.c index f874e4f0d95f..a5ba27983091 100644 --- a/sound/soc/intel/avs/messages.c +++ b/sound/soc/intel/avs/messages.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only // -// Copyright(c) 2021-2022 Intel Corporation. All rights reserved. +// Copyright(c) 2021-2022 Intel Corporation // // Authors: Cezary Rojewski <cezary.rojewski@intel.com> // Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> @@ -400,10 +400,12 @@ int avs_ipc_get_fw_config(struct avs_dev *adev, struct avs_fw_cfg *cfg) AVS_BASEFW_FIRMWARE_CONFIG, NULL, 0, &payload, &payload_size); if (ret) - return ret; + goto err; /* Non-zero payload expected for FIRMWARE_CONFIG. */ - if (!payload_size) - return -EREMOTEIO; + if (!payload_size) { + ret = -EREMOTEIO; + goto err; + } while (offset < payload_size) { tlv = (struct avs_tlv *)(payload + offset); @@ -502,6 +504,47 @@ int avs_ipc_get_fw_config(struct avs_dev *adev, struct avs_fw_cfg *cfg) /* No longer needed, free it as it's owned by the get_large_config() caller. */ kfree(payload); +err: + if (ret) + dev_err(adev->dev, "get fw cfg failed: %d\n", ret); + return ret; +} + +int avs_ipc_set_fw_config(struct avs_dev *adev, size_t num_tlvs, ...) +{ + struct avs_tlv *tlv; + void *payload; + size_t offset; + va_list args; + int ret, i; + + payload = kzalloc(AVS_MAILBOX_SIZE, GFP_KERNEL); + if (!payload) + return -ENOMEM; + + va_start(args, num_tlvs); + for (offset = i = 0; i < num_tlvs && offset < AVS_MAILBOX_SIZE - sizeof(*tlv); i++) { + tlv = (struct avs_tlv *)(payload + offset); + tlv->type = va_arg(args, u32); + tlv->length = va_arg(args, u32); + + offset += sizeof(*tlv) + tlv->length; + if (offset > AVS_MAILBOX_SIZE) + break; + + memcpy(tlv->value, va_arg(args, u8*), tlv->length); + } + + if (i == num_tlvs) + ret = avs_ipc_set_large_config(adev, AVS_BASEFW_MOD_ID, AVS_BASEFW_INST_ID, + AVS_BASEFW_FIRMWARE_CONFIG, payload, offset); + else + ret = -ERANGE; + + va_end(args); + kfree(payload); + if (ret) + dev_err(adev->dev, "set fw cfg failed: %d\n", ret); return ret; } @@ -517,10 +560,12 @@ int avs_ipc_get_hw_config(struct avs_dev *adev, struct avs_hw_cfg *cfg) AVS_BASEFW_HARDWARE_CONFIG, NULL, 0, &payload, &payload_size); if (ret) - return ret; + goto err; /* Non-zero payload expected for HARDWARE_CONFIG. */ - if (!payload_size) - return -EREMOTEIO; + if (!payload_size) { + ret = -EREMOTEIO; + goto err; + } while (offset < payload_size) { tlv = (struct avs_tlv *)(payload + offset); @@ -590,6 +635,9 @@ int avs_ipc_get_hw_config(struct avs_dev *adev, struct avs_hw_cfg *cfg) exit: /* No longer needed, free it as it's owned by the get_large_config() caller. */ kfree(payload); +err: + if (ret) + dev_err(adev->dev, "get hw cfg failed: %d\n", ret); return ret; } @@ -629,13 +677,6 @@ int avs_ipc_copier_set_sink_format(struct avs_dev *adev, u16 module_id, (u8 *)&cpr_fmt, sizeof(cpr_fmt)); } -int avs_ipc_peakvol_set_volume(struct avs_dev *adev, u16 module_id, u8 instance_id, - struct avs_volume_cfg *vol) -{ - return avs_ipc_set_large_config(adev, module_id, instance_id, AVS_PEAKVOL_VOLUME, (u8 *)vol, - sizeof(*vol)); -} - int avs_ipc_peakvol_get_volume(struct avs_dev *adev, u16 module_id, u8 instance_id, struct avs_volume_cfg **vols, size_t *num_vols) { @@ -658,6 +699,110 @@ int avs_ipc_peakvol_get_volume(struct avs_dev *adev, u16 module_id, u8 instance_ return 0; } +int avs_ipc_peakvol_set_volume(struct avs_dev *adev, u16 module_id, u8 instance_id, + struct avs_volume_cfg *vol) +{ + return avs_ipc_set_large_config(adev, module_id, instance_id, AVS_PEAKVOL_VOLUME, + (u8 *)vol, sizeof(*vol)); +} + +int avs_ipc_peakvol_set_volumes(struct avs_dev *adev, u16 module_id, u8 instance_id, + struct avs_volume_cfg *vols, size_t num_vols) +{ + struct avs_tlv *tlv; + size_t offset; + size_t size; + u8 *payload; + int ret, i; + + size = num_vols * sizeof(*vols); + size += num_vols * sizeof(*tlv); + if (size > AVS_MAILBOX_SIZE) + return -EINVAL; + + payload = kzalloc(AVS_MAILBOX_SIZE, GFP_KERNEL); + if (!payload) + return -ENOMEM; + + for (offset = i = 0; i < num_vols; i++) { + tlv = (struct avs_tlv *)(payload + offset); + + tlv->type = AVS_PEAKVOL_VOLUME; + tlv->length = sizeof(*vols); + memcpy(tlv->value, &vols[i], tlv->length); + + offset += sizeof(*tlv) + tlv->length; + } + + ret = avs_ipc_set_large_config(adev, module_id, instance_id, AVS_VENDOR_CONFIG, payload, + size); + kfree(payload); + return ret; +} + +int avs_ipc_peakvol_get_mute(struct avs_dev *adev, u16 module_id, u8 instance_id, + struct avs_mute_cfg **mutes, size_t *num_mutes) +{ + size_t payload_size; + u8 *payload; + int ret; + + ret = avs_ipc_get_large_config(adev, module_id, instance_id, AVS_PEAKVOL_MUTE, NULL, 0, + &payload, &payload_size); + if (ret) + return ret; + + /* Non-zero payload expected for PEAKVOL_MUTE. */ + if (!payload_size) + return -EREMOTEIO; + + *mutes = (struct avs_mute_cfg *)payload; + *num_mutes = payload_size / sizeof(**mutes); + + return 0; +} + +int avs_ipc_peakvol_set_mute(struct avs_dev *adev, u16 module_id, u8 instance_id, + struct avs_mute_cfg *mute) +{ + return avs_ipc_set_large_config(adev, module_id, instance_id, AVS_PEAKVOL_MUTE, + (u8 *)mute, sizeof(*mute)); +} + +int avs_ipc_peakvol_set_mutes(struct avs_dev *adev, u16 module_id, u8 instance_id, + struct avs_mute_cfg *mutes, size_t num_mutes) +{ + struct avs_tlv *tlv; + size_t offset; + size_t size; + u8 *payload; + int ret, i; + + size = num_mutes * sizeof(*mutes); + size += num_mutes * sizeof(*tlv); + if (size > AVS_MAILBOX_SIZE) + return -EINVAL; + + payload = kzalloc(AVS_MAILBOX_SIZE, GFP_KERNEL); + if (!payload) + return -ENOMEM; + + for (offset = i = 0; i < num_mutes; i++) { + tlv = (struct avs_tlv *)(payload + offset); + + tlv->type = AVS_PEAKVOL_MUTE; + tlv->length = sizeof(*mutes); + memcpy(tlv->value, &mutes[i], tlv->length); + + offset += sizeof(*tlv) + tlv->length; + } + + ret = avs_ipc_set_large_config(adev, module_id, instance_id, AVS_VENDOR_CONFIG, payload, + size); + kfree(payload); + return ret; +} + #ifdef CONFIG_DEBUG_FS int avs_ipc_set_enable_logs(struct avs_dev *adev, u8 *log_info, size_t size) { diff --git a/sound/soc/intel/avs/messages.h b/sound/soc/intel/avs/messages.h index 4e609a08863c..55c04b0142ae 100644 --- a/sound/soc/intel/avs/messages.h +++ b/sound/soc/intel/avs/messages.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright(c) 2021-2022 Intel Corporation. All rights reserved. + * Copyright(c) 2021-2022 Intel Corporation * * Authors: Cezary Rojewski <cezary.rojewski@intel.com> * Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> @@ -93,12 +93,16 @@ union avs_global_msg { } ext; }; } __packed; +static_assert(sizeof(union avs_global_msg) == 8); struct avs_tlv { u32 type; u32 length; u32 value[]; } __packed; +static_assert(sizeof(struct avs_tlv) == 8); + +#define avs_tlv_size(tlv) struct_size(tlv, value, (tlv)->length / 4) enum avs_module_msg_type { AVS_MOD_INIT_INSTANCE = 0, @@ -155,6 +159,7 @@ union avs_module_msg { } ext; }; } __packed; +static_assert(sizeof(union avs_module_msg) == 8); #define AVS_IPC_NOT_SUPPORTED 15 @@ -190,6 +195,7 @@ union avs_reply_msg { } ext; }; } __packed; +static_assert(sizeof(union avs_reply_msg) == 8); enum avs_notify_msg_type { AVS_NOTIFY_PHRASE_DETECTED = 4, @@ -226,6 +232,7 @@ union avs_notify_msg { } ext; }; } __packed; +static_assert(sizeof(union avs_notify_msg) == 8); #define AVS_MSG(hdr) { .val = hdr } @@ -264,6 +271,7 @@ struct avs_notify_voice_data { u16 kpd_score; u16 reserved; } __packed; +static_assert(sizeof(struct avs_notify_voice_data) == 4); struct avs_notify_res_data { u32 resource_type; @@ -272,6 +280,7 @@ struct avs_notify_res_data { u32 reserved; u32 data[6]; } __packed; +static_assert(sizeof(struct avs_notify_res_data) == 40); struct avs_notify_mod_data { u32 module_instance_id; @@ -279,6 +288,7 @@ struct avs_notify_mod_data { u32 data_size; u32 data[]; } __packed; +static_assert(sizeof(struct avs_notify_mod_data) == 12); /* ROM messages */ enum avs_rom_control_msg_type { @@ -332,6 +342,7 @@ struct avs_dxstate_info { u32 core_mask; /* which cores are subject for power transition */ u32 dx_mask; /* bit[n]=1 core n goes to D0, bit[n]=0 it goes to D3 */ } __packed; +static_assert(sizeof(struct avs_dxstate_info) == 8); int avs_ipc_set_dx(struct avs_dev *adev, u32 core_mask, bool powerup); int avs_ipc_set_d0ix(struct avs_dev *adev, bool enable_pg, bool streaming); @@ -367,11 +378,13 @@ struct avs_skl_log_state { u32 enable; u32 min_priority; } __packed; +static_assert(sizeof(struct avs_skl_log_state) == 8); struct avs_skl_log_state_info { u32 core_mask; struct avs_skl_log_state logs_core[]; } __packed; +static_assert(sizeof(struct avs_skl_log_state_info) == 4); struct avs_apl_log_state_info { u32 aging_timer_period; @@ -379,6 +392,7 @@ struct avs_apl_log_state_info { u32 core_mask; struct avs_skl_log_state logs_core[]; } __packed; +static_assert(sizeof(struct avs_apl_log_state_info) == 12); enum avs_icl_log_priority { AVS_ICL_LOG_CRITICAL = 0, @@ -403,6 +417,7 @@ struct avs_icl_log_state_info { u32 enable; u32 logs_priorities_mask[]; } __packed; +static_assert(sizeof(struct avs_icl_log_state_info) == 12); int avs_ipc_set_enable_logs(struct avs_dev *adev, u8 *log_info, size_t size); @@ -438,6 +453,8 @@ enum avs_fw_cfg_params { AVS_FW_CFG_RESERVED, AVS_FW_CFG_POWER_GATING_POLICY, AVS_FW_CFG_ASSERT_MODE, + AVS_FW_CFG_RESERVED2, + AVS_FW_CFG_BUS_HARDWARE_ID, }; struct avs_fw_cfg { @@ -462,7 +479,14 @@ struct avs_fw_cfg { u32 power_gating_policy; }; +struct avs_bus_hwid { + u32 device; + u32 subsystem; + u8 revision; +}; + int avs_ipc_get_fw_config(struct avs_dev *adev, struct avs_fw_cfg *cfg); +int avs_ipc_set_fw_config(struct avs_dev *adev, size_t num_tlvs, ...); enum avs_hw_cfg_params { AVS_HW_CFG_AVS_VER, @@ -521,6 +545,7 @@ struct avs_module_type { u32 lib_code:1; u32 rsvd:24; } __packed; +static_assert(sizeof(struct avs_module_type) == 4); union avs_segment_flags { u32 ul; @@ -537,12 +562,14 @@ union avs_segment_flags { u32 length:16; }; } __packed; +static_assert(sizeof(union avs_segment_flags) == 4); struct avs_segment_desc { union avs_segment_flags flags; u32 v_base_addr; u32 file_offset; } __packed; +static_assert(sizeof(struct avs_segment_desc) == 12); struct avs_module_entry { u16 module_id; @@ -559,11 +586,13 @@ struct avs_module_entry { u16 instance_bss_size; struct avs_segment_desc segments[3]; } __packed; +static_assert(sizeof(struct avs_module_entry) == 116); struct avs_mods_info { u32 count; struct avs_module_entry entries[]; } __packed; +static_assert(sizeof(struct avs_mods_info) == 4); static inline bool avs_module_entry_is_loaded(struct avs_module_entry *mentry) { @@ -577,6 +606,7 @@ struct avs_sys_time { u32 val_l; u32 val_u; } __packed; +static_assert(sizeof(struct avs_sys_time) == 8); int avs_ipc_set_system_time(struct avs_dev *adev); @@ -624,6 +654,9 @@ int avs_ipc_set_system_time(struct avs_dev *adev); #define AVS_INTELWOV_MOD_UUID \ GUID_INIT(0xEC774FA9, 0x28D3, 0x424A, 0x90, 0xE4, 0x69, 0xF9, 0x84, 0xF1, 0xEE, 0xB7) +#define AVS_WOVHOSTM_MOD_UUID \ + GUID_INIT(0xF9ED62B7, 0x092E, 0x4A90, 0x8F, 0x4D, 0x82, 0xDA, 0xA8, 0xB3, 0x8F, 0x3B) + /* channel map */ enum avs_channel_index { AVS_CHANNEL_LEFT = 0, @@ -666,8 +699,9 @@ enum avs_sample_type { AVS_SAMPLE_TYPE_FLOAT = 4, }; -#define AVS_CHANNELS_MAX 8 +#define AVS_COEFF_CHANNELS_MAX 8 #define AVS_ALL_CHANNELS_MASK UINT_MAX +#define AVS_CHANNELS_MAX 16 struct avs_audio_format { u32 sampling_freq; @@ -680,6 +714,7 @@ struct avs_audio_format { u32 sample_type:8; u32 reserved:8; } __packed; +static_assert(sizeof(struct avs_audio_format) == 24); struct avs_modcfg_base { u32 cpc; @@ -688,12 +723,14 @@ struct avs_modcfg_base { u32 is_pages; struct avs_audio_format audio_fmt; } __packed; +static_assert(sizeof(struct avs_modcfg_base) == 40); struct avs_pin_format { u32 pin_index; u32 iobs; struct avs_audio_format audio_fmt; } __packed; +static_assert(sizeof(struct avs_pin_format) == 32); struct avs_modcfg_ext { struct avs_modcfg_base base; @@ -703,6 +740,7 @@ struct avs_modcfg_ext { /* input pin formats followed by output ones */ struct avs_pin_format pin_fmts[]; } __packed; +static_assert(sizeof(struct avs_modcfg_ext) == 56); enum avs_dma_type { AVS_DMA_HDA_HOST_OUTPUT = 0, @@ -726,6 +764,7 @@ union avs_virtual_index { u8 instance:3; } dmic; } __packed; +static_assert(sizeof(union avs_virtual_index) == 1); union avs_connector_node_id { u32 val; @@ -735,6 +774,7 @@ union avs_connector_node_id { u32 rsvd:19; }; } __packed; +static_assert(sizeof(union avs_connector_node_id) == 4); #define INVALID_PIPELINE_ID 0xFF #define INVALID_NODE_ID \ @@ -747,16 +787,45 @@ union avs_gtw_attributes { u32 rsvd:31; }; } __packed; +static_assert(sizeof(union avs_gtw_attributes) == 4); + +#define AVS_GTW_DMA_CONFIG_ID 0x1000 +#define AVS_DMA_METHOD_HDA 1 + +struct avs_dma_device_stream_channel_map { + u32 device_address; + u32 channel_map; +} __packed; +static_assert(sizeof(struct avs_dma_device_stream_channel_map) == 8); + +struct avs_dma_stream_channel_map { + u32 device_count; + struct avs_dma_device_stream_channel_map map[16]; +} __packed; +static_assert(sizeof(struct avs_dma_stream_channel_map) == 132); + +struct avs_dma_cfg { + u8 dma_method; + u8 pre_allocated; + u16 rsvd; + u32 dma_channel_id; + u32 stream_id; + struct avs_dma_stream_channel_map map; + u32 config_size; + u8 config[] __counted_by(config_size); +} __packed; +static_assert(sizeof(struct avs_dma_cfg) == 148); struct avs_copier_gtw_cfg { union avs_connector_node_id node_id; u32 dma_buffer_size; u32 config_length; - struct { + union { union avs_gtw_attributes attrs; - u32 blob[]; + DECLARE_FLEX_ARRAY(u32, blob); } config; } __packed; +static_assert(sizeof(struct avs_copier_gtw_cfg) == 16); struct avs_copier_cfg { struct avs_modcfg_base base; @@ -764,6 +833,7 @@ struct avs_copier_cfg { u32 feature_mask; struct avs_copier_gtw_cfg gtw_cfg; } __packed; +static_assert(sizeof(struct avs_copier_cfg) == 84); struct avs_volume_cfg { u32 channel_id; @@ -772,45 +842,62 @@ struct avs_volume_cfg { u32 reserved; /* alignment */ u64 curve_duration; } __packed; +static_assert(sizeof(struct avs_volume_cfg) == 24); + +struct avs_mute_cfg { + u32 channel_id; + u32 mute; + u32 curve_type; + u32 reserved; /* alignment */ + u64 curve_duration; +} __packed; +static_assert(sizeof(struct avs_mute_cfg) == 24); struct avs_peakvol_cfg { struct avs_modcfg_base base; struct avs_volume_cfg vols[]; } __packed; +static_assert(sizeof(struct avs_peakvol_cfg) == 40); struct avs_micsel_cfg { struct avs_modcfg_base base; struct avs_audio_format out_fmt; } __packed; +static_assert(sizeof(struct avs_micsel_cfg) == 64); struct avs_mux_cfg { struct avs_modcfg_base base; struct avs_audio_format ref_fmt; struct avs_audio_format out_fmt; } __packed; +static_assert(sizeof(struct avs_mux_cfg) == 88); struct avs_updown_mixer_cfg { struct avs_modcfg_base base; u32 out_channel_config; u32 coefficients_select; - s32 coefficients[AVS_CHANNELS_MAX]; + s32 coefficients[AVS_COEFF_CHANNELS_MAX]; u32 channel_map; } __packed; +static_assert(sizeof(struct avs_updown_mixer_cfg) == 84); struct avs_src_cfg { struct avs_modcfg_base base; u32 out_freq; } __packed; +static_assert(sizeof(struct avs_src_cfg) == 44); struct avs_probe_gtw_cfg { union avs_connector_node_id node_id; u32 dma_buffer_size; } __packed; +static_assert(sizeof(struct avs_probe_gtw_cfg) == 8); struct avs_probe_cfg { struct avs_modcfg_base base; struct avs_probe_gtw_cfg gtw_cfg; } __packed; +static_assert(sizeof(struct avs_probe_cfg) == 48); struct avs_aec_cfg { struct avs_modcfg_base base; @@ -818,24 +905,38 @@ struct avs_aec_cfg { struct avs_audio_format out_fmt; u32 cpc_lp_mode; } __packed; +static_assert(sizeof(struct avs_aec_cfg) == 92); struct avs_asrc_cfg { struct avs_modcfg_base base; u32 out_freq; - u32 rsvd0:1; - u32 mode:1; + u32 mode:2; u32 rsvd2:2; u32 disable_jitter_buffer:1; u32 rsvd3:27; } __packed; +static_assert(sizeof(struct avs_asrc_cfg) == 48); struct avs_wov_cfg { struct avs_modcfg_base base; u32 cpc_lp_mode; } __packed; +static_assert(sizeof(struct avs_wov_cfg) == 44); + +struct avs_whm_cfg { + struct avs_modcfg_base base; + /* Audio format for output pin 0 */ + struct avs_audio_format ref_fmt; + struct avs_audio_format out_fmt; + u32 wake_tick_period; + struct avs_copier_gtw_cfg gtw_cfg; +} __packed; +static_assert(sizeof(struct avs_whm_cfg) == 108); /* Module runtime parameters */ +#define AVS_VENDOR_CONFIG 0xFF + enum avs_copier_runtime_param { AVS_COPIER_SET_SINK_FORMAT = 2, }; @@ -845,6 +946,7 @@ struct avs_copier_sink_format { struct avs_audio_format src_fmt; struct avs_audio_format sink_fmt; } __packed; +static_assert(sizeof(struct avs_copier_sink_format) == 52); int avs_ipc_copier_set_sink_format(struct avs_dev *adev, u16 module_id, u8 instance_id, u32 sink_id, @@ -853,6 +955,7 @@ int avs_ipc_copier_set_sink_format(struct avs_dev *adev, u16 module_id, enum avs_peakvol_runtime_param { AVS_PEAKVOL_VOLUME = 0, + AVS_PEAKVOL_MUTE = 3, }; enum avs_audio_curve_type { @@ -860,10 +963,18 @@ enum avs_audio_curve_type { AVS_AUDIO_CURVE_WINDOWS_FADE = 1, }; -int avs_ipc_peakvol_set_volume(struct avs_dev *adev, u16 module_id, u8 instance_id, - struct avs_volume_cfg *vol); int avs_ipc_peakvol_get_volume(struct avs_dev *adev, u16 module_id, u8 instance_id, struct avs_volume_cfg **vols, size_t *num_vols); +int avs_ipc_peakvol_set_volume(struct avs_dev *adev, u16 module_id, u8 instance_id, + struct avs_volume_cfg *vol); +int avs_ipc_peakvol_set_volumes(struct avs_dev *adev, u16 module_id, u8 instance_id, + struct avs_volume_cfg *vols, size_t num_vols); +int avs_ipc_peakvol_get_mute(struct avs_dev *adev, u16 module_id, u8 instance_id, + struct avs_mute_cfg **mutes, size_t *num_mutes); +int avs_ipc_peakvol_set_mute(struct avs_dev *adev, u16 module_id, u8 instance_id, + struct avs_mute_cfg *mute); +int avs_ipc_peakvol_set_mutes(struct avs_dev *adev, u16 module_id, u8 instance_id, + struct avs_mute_cfg *mutes, size_t num_mutes); #define AVS_PROBE_INST_ID 0 @@ -878,6 +989,7 @@ struct avs_probe_dma { union avs_connector_node_id node_id; u32 dma_buffer_size; } __packed; +static_assert(sizeof(struct avs_probe_dma) == 8); enum avs_probe_type { AVS_PROBE_TYPE_INPUT = 0, @@ -894,6 +1006,7 @@ union avs_probe_point_id { u32 index:6; } id; } __packed; +static_assert(sizeof(union avs_probe_point_id) == 4); enum avs_connection_purpose { AVS_CONNECTION_PURPOSE_EXTRACT = 0, @@ -906,6 +1019,7 @@ struct avs_probe_point_desc { u32 purpose; union avs_connector_node_id node_id; } __packed; +static_assert(sizeof(struct avs_probe_point_desc) == 12); int avs_ipc_probe_get_dma(struct avs_dev *adev, struct avs_probe_dma **dmas, size_t *num_dmas); int avs_ipc_probe_attach_dma(struct avs_dev *adev, struct avs_probe_dma *dmas, size_t num_dmas); diff --git a/sound/soc/intel/avs/mtl.c b/sound/soc/intel/avs/mtl.c new file mode 100644 index 000000000000..e7b7915b2a82 --- /dev/null +++ b/sound/soc/intel/avs/mtl.c @@ -0,0 +1,200 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright(c) 2021-2025 Intel Corporation + * + * Authors: Cezary Rojewski <cezary.rojewski@intel.com> + * Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> + */ + +#include <sound/hdaudio_ext.h> +#include "avs.h" +#include "registers.h" +#include "trace.h" + +#define MTL_HfDSSGBL_BASE 0x1000 +#define MTL_REG_HfDSSCS (MTL_HfDSSGBL_BASE + 0x0) +#define MTL_HfDSSCS_SPA BIT(16) +#define MTL_HfDSSCS_CPA BIT(24) + +#define MTL_DSPCS_BASE 0x178D00 +#define MTL_REG_DSPCCTL (MTL_DSPCS_BASE + 0x4) +#define MTL_DSPCCTL_SPA BIT(0) +#define MTL_DSPCCTL_CPA BIT(8) +#define MTL_DSPCCTL_OSEL GENMASK(25, 24) +#define MTL_DSPCCTL_OSEL_HOST BIT(25) + +#define MTL_HfINT_BASE 0x1100 +#define MTL_REG_HfINTIPPTR (MTL_HfINT_BASE + 0x8) +#define MTL_REG_HfHIPCIE (MTL_HfINT_BASE + 0x40) +#define MTL_HfINTIPPTR_PTR GENMASK(20, 0) +#define MTL_HfHIPCIE_IE BIT(0) + +#define MTL_DWICTL_INTENL_IE BIT(0) +#define MTL_DWICTL_FINALSTATUSL_IPC BIT(0) /* same as ADSPIS_IPC */ + +static int avs_mtl_core_power_on(struct avs_dev *adev) +{ + u32 reg; + int ret; + + /* Power up DSP domain. */ + snd_hdac_adsp_updatel(adev, MTL_REG_HfDSSCS, MTL_HfDSSCS_SPA, MTL_HfDSSCS_SPA); + trace_avs_dsp_core_op(1, AVS_MAIN_CORE_MASK, "power dsp", true); + + ret = snd_hdac_adsp_readl_poll(adev, MTL_REG_HfDSSCS, reg, + (reg & MTL_HfDSSCS_CPA) == MTL_HfDSSCS_CPA, + AVS_ADSPCS_INTERVAL_US, AVS_ADSPCS_TIMEOUT_US); + if (ret) { + dev_err(adev->dev, "power on domain dsp failed: %d\n", ret); + return ret; + } + + /* Prevent power gating of DSP domain. */ + snd_hdac_adsp_updatel(adev, MTL_REG_HfPWRCTL, MTL_HfPWRCTL_WPDSPHPxPG, + MTL_HfPWRCTL_WPDSPHPxPG); + trace_avs_dsp_core_op(1, AVS_MAIN_CORE_MASK, "prevent dsp PG", true); + + ret = snd_hdac_adsp_readl_poll(adev, MTL_REG_HfPWRSTS, reg, + (reg & MTL_HfPWRSTS_DSPHPxPGS) == MTL_HfPWRSTS_DSPHPxPGS, + AVS_ADSPCS_INTERVAL_US, AVS_ADSPCS_TIMEOUT_US); + + /* Set ownership to HOST. */ + snd_hdac_adsp_updatel(adev, MTL_REG_DSPCCTL, MTL_DSPCCTL_OSEL, MTL_DSPCCTL_OSEL_HOST); + return ret; +} + +static int avs_mtl_core_power_off(struct avs_dev *adev) +{ + u32 reg; + + /* Allow power gating of DSP domain. No STS polling as HOST is only one of its users. */ + snd_hdac_adsp_updatel(adev, MTL_REG_HfPWRCTL, MTL_HfPWRCTL_WPDSPHPxPG, 0); + trace_avs_dsp_core_op(0, AVS_MAIN_CORE_MASK, "allow dsp pg", false); + + /* Power down DSP domain. */ + snd_hdac_adsp_updatel(adev, MTL_REG_HfDSSCS, MTL_HfDSSCS_SPA, 0); + trace_avs_dsp_core_op(0, AVS_MAIN_CORE_MASK, "power dsp", false); + + return snd_hdac_adsp_readl_poll(adev, MTL_REG_HfDSSCS, reg, + (reg & MTL_HfDSSCS_CPA) == 0, + AVS_ADSPCS_INTERVAL_US, AVS_ADSPCS_TIMEOUT_US); +} + +int avs_mtl_core_power(struct avs_dev *adev, u32 core_mask, bool power) +{ + core_mask &= AVS_MAIN_CORE_MASK; + if (!core_mask) + return 0; + + if (power) + return avs_mtl_core_power_on(adev); + return avs_mtl_core_power_off(adev); +} + +int avs_mtl_core_reset(struct avs_dev *adev, u32 core_mask, bool power) +{ + /* No logical equivalent on ACE 1.x. */ + return 0; +} + +int avs_mtl_core_stall(struct avs_dev *adev, u32 core_mask, bool stall) +{ + u32 value, reg; + int ret; + + core_mask &= AVS_MAIN_CORE_MASK; + if (!core_mask) + return 0; + + value = snd_hdac_adsp_readl(adev, MTL_REG_DSPCCTL); + trace_avs_dsp_core_op(value, core_mask, "stall", stall); + if (value == UINT_MAX) + return 0; + + value = stall ? 0 : MTL_DSPCCTL_SPA; + snd_hdac_adsp_updatel(adev, MTL_REG_DSPCCTL, MTL_DSPCCTL_SPA, value); + + value = stall ? 0 : MTL_DSPCCTL_CPA; + ret = snd_hdac_adsp_readl_poll(adev, MTL_REG_DSPCCTL, + reg, (reg & MTL_DSPCCTL_CPA) == value, + AVS_ADSPCS_INTERVAL_US, AVS_ADSPCS_TIMEOUT_US); + if (ret) + dev_err(adev->dev, "core_mask %d %sstall failed: %d\n", + core_mask, stall ? "" : "un", ret); + return ret; +} + +static void avs_mtl_ipc_interrupt(struct avs_dev *adev) +{ + const struct avs_spec *spec = adev->spec; + u32 hipc_ack, hipc_rsp; + + snd_hdac_adsp_updatel(adev, spec->hipc->ctl_offset, + AVS_ADSP_HIPCCTL_DONE | AVS_ADSP_HIPCCTL_BUSY, 0); + + hipc_ack = snd_hdac_adsp_readl(adev, spec->hipc->ack_offset); + hipc_rsp = snd_hdac_adsp_readl(adev, spec->hipc->rsp_offset); + + /* DSP acked host's request. */ + if (hipc_ack & spec->hipc->ack_done_mask) { + complete(&adev->ipc->done_completion); + + /* Tell DSP it has our attention. */ + snd_hdac_adsp_updatel(adev, spec->hipc->ack_offset, spec->hipc->ack_done_mask, + spec->hipc->ack_done_mask); + } + + /* DSP sent new response to process. */ + if (hipc_rsp & spec->hipc->rsp_busy_mask) { + union avs_reply_msg msg; + + msg.primary = snd_hdac_adsp_readl(adev, MTL_REG_HfIPCxTDR); + msg.ext.val = snd_hdac_adsp_readl(adev, MTL_REG_HfIPCxTDD); + + avs_dsp_process_response(adev, msg.val); + + /* Tell DSP we accepted its message. */ + snd_hdac_adsp_updatel(adev, MTL_REG_HfIPCxTDR, + MTL_HfIPCxTDR_BUSY, MTL_HfIPCxTDR_BUSY); + /* Ack this response. */ + snd_hdac_adsp_updatel(adev, MTL_REG_HfIPCxTDA, MTL_HfIPCxTDA_BUSY, 0); + } + + snd_hdac_adsp_updatel(adev, spec->hipc->ctl_offset, + AVS_ADSP_HIPCCTL_DONE | AVS_ADSP_HIPCCTL_BUSY, + AVS_ADSP_HIPCCTL_DONE | AVS_ADSP_HIPCCTL_BUSY); +} + +irqreturn_t avs_mtl_dsp_interrupt(struct avs_dev *adev) +{ + u32 adspis = snd_hdac_adsp_readl(adev, MTL_DWICTL_REG_FINALSTATUSL); + irqreturn_t ret = IRQ_NONE; + + if (adspis == UINT_MAX) + return ret; + + if (adspis & MTL_DWICTL_FINALSTATUSL_IPC) { + avs_mtl_ipc_interrupt(adev); + ret = IRQ_HANDLED; + } + + return ret; +} + +void avs_mtl_interrupt_control(struct avs_dev *adev, bool enable) +{ + if (enable) { + snd_hdac_adsp_updatel(adev, MTL_DWICTL_REG_INTENL, MTL_DWICTL_INTENL_IE, + MTL_DWICTL_INTENL_IE); + snd_hdac_adsp_updatew(adev, MTL_REG_HfHIPCIE, MTL_HfHIPCIE_IE, MTL_HfHIPCIE_IE); + snd_hdac_adsp_updatel(adev, MTL_REG_HfIPCxCTL, AVS_ADSP_HIPCCTL_DONE, + AVS_ADSP_HIPCCTL_DONE); + snd_hdac_adsp_updatel(adev, MTL_REG_HfIPCxCTL, AVS_ADSP_HIPCCTL_BUSY, + AVS_ADSP_HIPCCTL_BUSY); + } else { + snd_hdac_adsp_updatel(adev, MTL_REG_HfIPCxCTL, AVS_ADSP_HIPCCTL_BUSY, 0); + snd_hdac_adsp_updatel(adev, MTL_REG_HfIPCxCTL, AVS_ADSP_HIPCCTL_DONE, 0); + snd_hdac_adsp_updatew(adev, MTL_REG_HfHIPCIE, MTL_HfHIPCIE_IE, 0); + snd_hdac_adsp_updatel(adev, MTL_DWICTL_REG_INTENL, MTL_DWICTL_INTENL_IE, 0); + } +} diff --git a/sound/soc/intel/avs/path.c b/sound/soc/intel/avs/path.c index e785fc2a7008..e8e6b1c7fc90 100644 --- a/sound/soc/intel/avs/path.c +++ b/sound/soc/intel/avs/path.c @@ -1,12 +1,13 @@ // SPDX-License-Identifier: GPL-2.0-only // -// Copyright(c) 2021 Intel Corporation. All rights reserved. +// Copyright(c) 2021 Intel Corporation // // Authors: Cezary Rojewski <cezary.rojewski@intel.com> // Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> // -#include <sound/intel-nhlt.h> +#include <linux/acpi.h> +#include <acpi/nhlt.h> #include <sound/pcm_params.h> #include <sound/soc.h> #include "avs.h" @@ -114,157 +115,310 @@ avs_path_find_variant(struct avs_dev *adev, return NULL; } -__maybe_unused -static bool avs_dma_type_is_host(u32 dma_type) -{ - return dma_type == AVS_DMA_HDA_HOST_OUTPUT || - dma_type == AVS_DMA_HDA_HOST_INPUT; -} +static struct acpi_nhlt_config * +avs_nhlt_config_or_default(struct avs_dev *adev, struct avs_tplg_module *t); -__maybe_unused -static bool avs_dma_type_is_link(u32 dma_type) +int avs_path_set_constraint(struct avs_dev *adev, struct avs_tplg_path_template *template, + struct snd_pcm_hw_constraint_list *rate_list, + struct snd_pcm_hw_constraint_list *channels_list, + struct snd_pcm_hw_constraint_list *sample_bits_list) { - return !avs_dma_type_is_host(dma_type); -} + struct avs_tplg_path *path_template; + unsigned int *rlist, *clist, *slist; + size_t i; + + i = 0; + list_for_each_entry(path_template, &template->path_list, node) + i++; + + rlist = kcalloc(i, sizeof(*rlist), GFP_KERNEL); + clist = kcalloc(i, sizeof(*clist), GFP_KERNEL); + slist = kcalloc(i, sizeof(*slist), GFP_KERNEL); + if (!rlist || !clist || !slist) + return -ENOMEM; + + i = 0; + list_for_each_entry(path_template, &template->path_list, node) { + struct avs_tplg_pipeline *pipeline_template; + + list_for_each_entry(pipeline_template, &path_template->ppl_list, node) { + struct avs_tplg_module *module_template; + + list_for_each_entry(module_template, &pipeline_template->mod_list, node) { + const guid_t *type = &module_template->cfg_ext->type; + struct acpi_nhlt_config *blob; + + if (!guid_equal(type, &AVS_COPIER_MOD_UUID) && + !guid_equal(type, &AVS_WOVHOSTM_MOD_UUID)) + continue; + + switch (module_template->cfg_ext->copier.dma_type) { + case AVS_DMA_DMIC_LINK_INPUT: + case AVS_DMA_I2S_LINK_OUTPUT: + case AVS_DMA_I2S_LINK_INPUT: + break; + default: + continue; + } + + blob = avs_nhlt_config_or_default(adev, module_template); + if (IS_ERR(blob)) + continue; + + rlist[i] = path_template->fe_fmt->sampling_freq; + clist[i] = path_template->fe_fmt->num_channels; + slist[i] = path_template->fe_fmt->bit_depth; + i++; + } + } + } -__maybe_unused -static bool avs_dma_type_is_output(u32 dma_type) -{ - return dma_type == AVS_DMA_HDA_HOST_OUTPUT || - dma_type == AVS_DMA_HDA_LINK_OUTPUT || - dma_type == AVS_DMA_I2S_LINK_OUTPUT; + if (i) { + rate_list->count = i; + rate_list->list = rlist; + channels_list->count = i; + channels_list->list = clist; + sample_bits_list->count = i; + sample_bits_list->list = slist; + } else { + kfree(rlist); + kfree(clist); + kfree(slist); + } + + return i; } -__maybe_unused -static bool avs_dma_type_is_input(u32 dma_type) +static void avs_init_node_id(union avs_connector_node_id *node_id, + struct avs_tplg_modcfg_ext *te, u32 dma_id) { - return !avs_dma_type_is_output(dma_type); + node_id->val = 0; + node_id->dma_type = te->copier.dma_type; + + switch (node_id->dma_type) { + case AVS_DMA_DMIC_LINK_INPUT: + case AVS_DMA_I2S_LINK_OUTPUT: + case AVS_DMA_I2S_LINK_INPUT: + /* Gateway's virtual index is statically assigned in the topology. */ + node_id->vindex = te->copier.vindex.val; + break; + + case AVS_DMA_HDA_HOST_OUTPUT: + case AVS_DMA_HDA_HOST_INPUT: + /* Gateway's virtual index is dynamically assigned with DMA ID */ + node_id->vindex = dma_id; + break; + + case AVS_DMA_HDA_LINK_OUTPUT: + case AVS_DMA_HDA_LINK_INPUT: + node_id->vindex = te->copier.vindex.val | dma_id; + break; + + default: + *node_id = INVALID_NODE_ID; + break; + } } -static int avs_copier_create(struct avs_dev *adev, struct avs_path_module *mod) -{ - struct nhlt_acpi_table *nhlt = adev->nhlt; - struct avs_tplg_module *t = mod->template; - struct avs_copier_cfg *cfg; - struct nhlt_specific_cfg *ep_blob; - union avs_connector_node_id node_id = {0}; - size_t cfg_size, data_size = 0; - void *data = NULL; - u32 dma_type; - int ret; +/* Every BLOB contains at least gateway attributes. */ +static struct acpi_nhlt_config *default_blob = (struct acpi_nhlt_config *)&(u32[2]) {4}; - dma_type = t->cfg_ext->copier.dma_type; - node_id.dma_type = dma_type; +static struct acpi_nhlt_config * +avs_nhlt_config_or_default(struct avs_dev *adev, struct avs_tplg_module *t) +{ + struct acpi_nhlt_format_config *fmtcfg; + struct avs_tplg_modcfg_ext *te; + struct avs_audio_format *fmt; + int link_type, dev_type; + int bus_id, dir; - switch (dma_type) { - struct avs_audio_format *fmt; - int direction; + te = t->cfg_ext; + switch (te->copier.dma_type) { case AVS_DMA_I2S_LINK_OUTPUT: - case AVS_DMA_I2S_LINK_INPUT: - if (avs_dma_type_is_input(dma_type)) - direction = SNDRV_PCM_STREAM_CAPTURE; - else - direction = SNDRV_PCM_STREAM_PLAYBACK; - - if (t->cfg_ext->copier.blob_fmt) - fmt = t->cfg_ext->copier.blob_fmt; - else if (direction == SNDRV_PCM_STREAM_CAPTURE) - fmt = t->in_fmt; - else - fmt = t->cfg_ext->copier.out_fmt; - - ep_blob = intel_nhlt_get_endpoint_blob(adev->dev, - nhlt, t->cfg_ext->copier.vindex.i2s.instance, - NHLT_LINK_SSP, fmt->valid_bit_depth, fmt->bit_depth, - fmt->num_channels, fmt->sampling_freq, direction, - NHLT_DEVICE_I2S); - if (!ep_blob) { - dev_err(adev->dev, "no I2S ep_blob found\n"); - return -ENOENT; - } - - data = ep_blob->caps; - data_size = ep_blob->size; - /* I2S gateway's vindex is statically assigned in topology */ - node_id.vindex = t->cfg_ext->copier.vindex.val; + link_type = ACPI_NHLT_LINKTYPE_SSP; + dev_type = ACPI_NHLT_DEVICETYPE_CODEC; + bus_id = te->copier.vindex.i2s.instance; + dir = SNDRV_PCM_STREAM_PLAYBACK; + fmt = te->copier.out_fmt; + break; + case AVS_DMA_I2S_LINK_INPUT: + link_type = ACPI_NHLT_LINKTYPE_SSP; + dev_type = ACPI_NHLT_DEVICETYPE_CODEC; + bus_id = te->copier.vindex.i2s.instance; + dir = SNDRV_PCM_STREAM_CAPTURE; + fmt = t->in_fmt; break; case AVS_DMA_DMIC_LINK_INPUT: - direction = SNDRV_PCM_STREAM_CAPTURE; - - if (t->cfg_ext->copier.blob_fmt) - fmt = t->cfg_ext->copier.blob_fmt; - else - fmt = t->in_fmt; - - ep_blob = intel_nhlt_get_endpoint_blob(adev->dev, nhlt, 0, - NHLT_LINK_DMIC, fmt->valid_bit_depth, - fmt->bit_depth, fmt->num_channels, - fmt->sampling_freq, direction, NHLT_DEVICE_DMIC); - if (!ep_blob) { - dev_err(adev->dev, "no DMIC ep_blob found\n"); - return -ENOENT; - } + link_type = ACPI_NHLT_LINKTYPE_PDM; + dev_type = -1; /* ignored */ + bus_id = 0; + dir = SNDRV_PCM_STREAM_CAPTURE; + fmt = t->in_fmt; + break; - data = ep_blob->caps; - data_size = ep_blob->size; - /* DMIC gateway's vindex is statically assigned in topology */ - node_id.vindex = t->cfg_ext->copier.vindex.val; + default: + return default_blob; + } - break; + /* Override format selection if necessary. */ + if (te->copier.blob_fmt) + fmt = te->copier.blob_fmt; + + fmtcfg = acpi_nhlt_find_fmtcfg(link_type, dev_type, dir, bus_id, + fmt->num_channels, fmt->sampling_freq, fmt->valid_bit_depth, + fmt->bit_depth); + if (!fmtcfg) { + dev_warn(adev->dev, "Endpoint format configuration not found.\n"); + return ERR_PTR(-ENOENT); + } + + if (fmtcfg->config.capabilities_size < default_blob->capabilities_size) + return ERR_PTR(-ETOOSMALL); + /* The firmware expects the payload to be DWORD-aligned. */ + if (fmtcfg->config.capabilities_size % sizeof(u32)) + return ERR_PTR(-EINVAL); + + return &fmtcfg->config; +} + +static int avs_append_dma_cfg(struct avs_dev *adev, struct avs_copier_gtw_cfg *gtw, + struct avs_tplg_module *t, u32 dma_id, size_t *cfg_size) +{ + u32 dma_type = t->cfg_ext->copier.dma_type; + struct avs_dma_cfg *dma; + struct avs_tlv *tlv; + size_t tlv_size; + + if (!avs_platattr_test(adev, ALTHDA)) + return 0; + switch (dma_type) { case AVS_DMA_HDA_HOST_OUTPUT: case AVS_DMA_HDA_HOST_INPUT: - /* HOST gateway's vindex is dynamically assigned with DMA id */ - node_id.vindex = mod->owner->owner->dma_id; - break; - case AVS_DMA_HDA_LINK_OUTPUT: case AVS_DMA_HDA_LINK_INPUT: - node_id.vindex = t->cfg_ext->copier.vindex.val | - mod->owner->owner->dma_id; - break; - - case INVALID_OBJECT_ID: + return 0; default: - node_id = INVALID_NODE_ID; break; } - cfg_size = sizeof(*cfg) + data_size; - /* Every config-BLOB contains gateway attributes. */ - if (data_size) - cfg_size -= sizeof(cfg->gtw_cfg.config.attrs); - if (cfg_size > AVS_MAILBOX_SIZE) - return -EINVAL; + tlv_size = sizeof(*tlv) + sizeof(*dma); + if (*cfg_size + tlv_size > AVS_MAILBOX_SIZE) + return -E2BIG; + + /* DMA config is a TLV tailing the existing payload. */ + tlv = (struct avs_tlv *)>w->config.blob[gtw->config_length]; + tlv->type = AVS_GTW_DMA_CONFIG_ID; + tlv->length = sizeof(*dma); + + dma = (struct avs_dma_cfg *)tlv->value; + memset(dma, 0, sizeof(*dma)); + dma->dma_method = AVS_DMA_METHOD_HDA; + dma->pre_allocated = true; + dma->dma_channel_id = dma_id; + dma->stream_id = dma_id + 1; + + gtw->config_length += tlv_size / sizeof(u32); + *cfg_size += tlv_size; + + return 0; +} + +static int avs_fill_gtw_config(struct avs_dev *adev, struct avs_copier_gtw_cfg *gtw, + struct avs_tplg_module *t, u32 dma_id, size_t *cfg_size) +{ + struct acpi_nhlt_config *blob; + size_t gtw_size; + + blob = avs_nhlt_config_or_default(adev, t); + if (IS_ERR(blob)) + return PTR_ERR(blob); + gtw_size = blob->capabilities_size; + if (*cfg_size + gtw_size > AVS_MAILBOX_SIZE) + return -E2BIG; + + gtw->config_length = gtw_size / sizeof(u32); + memcpy(gtw->config.blob, blob->capabilities, blob->capabilities_size); + *cfg_size += gtw_size; + + return avs_append_dma_cfg(adev, gtw, t, dma_id, cfg_size); +} + +static int avs_copier_create(struct avs_dev *adev, struct avs_path_module *mod) +{ + struct avs_tplg_module *t = mod->template; + struct avs_tplg_modcfg_ext *te; + struct avs_copier_cfg *cfg; + size_t cfg_size; + u32 dma_id; + int ret; + + te = t->cfg_ext; cfg = adev->modcfg_buf; - memset(cfg, 0, cfg_size); + dma_id = mod->owner->owner->dma_id; + cfg_size = offsetof(struct avs_copier_cfg, gtw_cfg.config); + + ret = avs_fill_gtw_config(adev, &cfg->gtw_cfg, t, dma_id, &cfg_size); + if (ret) + return ret; + cfg->base.cpc = t->cfg_base->cpc; cfg->base.ibs = t->cfg_base->ibs; cfg->base.obs = t->cfg_base->obs; cfg->base.is_pages = t->cfg_base->is_pages; cfg->base.audio_fmt = *t->in_fmt; - cfg->out_fmt = *t->cfg_ext->copier.out_fmt; - cfg->feature_mask = t->cfg_ext->copier.feature_mask; - cfg->gtw_cfg.node_id = node_id; - cfg->gtw_cfg.dma_buffer_size = t->cfg_ext->copier.dma_buffer_size; - /* config_length in DWORDs */ - cfg->gtw_cfg.config_length = DIV_ROUND_UP(data_size, 4); - if (data) - memcpy(&cfg->gtw_cfg.config, data, data_size); + cfg->out_fmt = *te->copier.out_fmt; + cfg->feature_mask = te->copier.feature_mask; + avs_init_node_id(&cfg->gtw_cfg.node_id, te, dma_id); + cfg->gtw_cfg.dma_buffer_size = te->copier.dma_buffer_size; + mod->gtw_attrs = cfg->gtw_cfg.config.attrs; + ret = avs_dsp_init_module(adev, mod->module_id, mod->owner->instance_id, t->core_id, + t->domain, cfg, cfg_size, &mod->instance_id); + return ret; +} + +static int avs_whm_create(struct avs_dev *adev, struct avs_path_module *mod) +{ + struct avs_tplg_module *t = mod->template; + struct avs_tplg_modcfg_ext *te; + struct avs_whm_cfg *cfg; + size_t cfg_size; + u32 dma_id; + int ret; + + te = t->cfg_ext; + cfg = adev->modcfg_buf; + dma_id = mod->owner->owner->dma_id; + cfg_size = offsetof(struct avs_whm_cfg, gtw_cfg.config); + + ret = avs_fill_gtw_config(adev, &cfg->gtw_cfg, t, dma_id, &cfg_size); + if (ret) + return ret; + + cfg->base.cpc = t->cfg_base->cpc; + cfg->base.ibs = t->cfg_base->ibs; + cfg->base.obs = t->cfg_base->obs; + cfg->base.is_pages = t->cfg_base->is_pages; + cfg->base.audio_fmt = *t->in_fmt; + cfg->ref_fmt = *te->whm.ref_fmt; + cfg->out_fmt = *te->whm.out_fmt; + cfg->wake_tick_period = te->whm.wake_tick_period; + avs_init_node_id(&cfg->gtw_cfg.node_id, te, dma_id); + cfg->gtw_cfg.dma_buffer_size = te->whm.dma_buffer_size; mod->gtw_attrs = cfg->gtw_cfg.config.attrs; - ret = avs_dsp_init_module(adev, mod->module_id, mod->owner->instance_id, - t->core_id, t->domain, cfg, cfg_size, - &mod->instance_id); + ret = avs_dsp_init_module(adev, mod->module_id, mod->owner->instance_id, t->core_id, + t->domain, cfg, cfg_size, &mod->instance_id); return ret; } -static struct avs_control_data *avs_get_module_control(struct avs_path_module *mod) +static struct soc_mixer_control *avs_get_module_control(struct avs_path_module *mod, + const char *name) { struct avs_tplg_module *t = mod->template; struct avs_tplg_path_template *path_tmpl; @@ -280,27 +434,93 @@ static struct avs_control_data *avs_get_module_control(struct avs_path_module *m mc = (struct soc_mixer_control *)w->kcontrols[i]->private_value; ctl_data = (struct avs_control_data *)mc->dobj.private; - if (ctl_data->id == t->ctl_id) - return ctl_data; + if (ctl_data->id == t->ctl_id && strstr(w->kcontrols[i]->id.name, name)) + return mc; } return NULL; } +int avs_peakvol_set_volume(struct avs_dev *adev, struct avs_path_module *mod, + struct soc_mixer_control *mc, long *input) +{ + struct avs_volume_cfg vols[SND_SOC_TPLG_MAX_CHAN] = {{0}}; + struct avs_control_data *ctl_data; + struct avs_tplg_module *t; + int ret, i; + + ctl_data = mc->dobj.private; + t = mod->template; + if (!input) + input = ctl_data->values; + + if (mc->num_channels) { + for (i = 0; i < mc->num_channels; i++) { + vols[i].channel_id = i; + vols[i].target_volume = input[i]; + vols[i].curve_type = t->cfg_ext->peakvol.curve_type; + vols[i].curve_duration = t->cfg_ext->peakvol.curve_duration; + } + + ret = avs_ipc_peakvol_set_volumes(adev, mod->module_id, mod->instance_id, vols, + mc->num_channels); + return AVS_IPC_RET(ret); + } + + /* Target all channels if no individual selected. */ + vols[0].channel_id = AVS_ALL_CHANNELS_MASK; + vols[0].target_volume = input[0]; + vols[0].curve_type = t->cfg_ext->peakvol.curve_type; + vols[0].curve_duration = t->cfg_ext->peakvol.curve_duration; + + ret = avs_ipc_peakvol_set_volume(adev, mod->module_id, mod->instance_id, &vols[0]); + return AVS_IPC_RET(ret); +} + +int avs_peakvol_set_mute(struct avs_dev *adev, struct avs_path_module *mod, + struct soc_mixer_control *mc, long *input) +{ + struct avs_mute_cfg mutes[SND_SOC_TPLG_MAX_CHAN] = {{0}}; + struct avs_control_data *ctl_data; + struct avs_tplg_module *t; + int ret, i; + + ctl_data = mc->dobj.private; + t = mod->template; + if (!input) + input = ctl_data->values; + + if (mc->num_channels) { + for (i = 0; i < mc->num_channels; i++) { + mutes[i].channel_id = i; + mutes[i].mute = !input[i]; + mutes[i].curve_type = t->cfg_ext->peakvol.curve_type; + mutes[i].curve_duration = t->cfg_ext->peakvol.curve_duration; + } + + ret = avs_ipc_peakvol_set_mutes(adev, mod->module_id, mod->instance_id, mutes, + mc->num_channels); + return AVS_IPC_RET(ret); + } + + /* Target all channels if no individual selected. */ + mutes[0].channel_id = AVS_ALL_CHANNELS_MASK; + mutes[0].mute = !input[0]; + mutes[0].curve_type = t->cfg_ext->peakvol.curve_type; + mutes[0].curve_duration = t->cfg_ext->peakvol.curve_duration; + + ret = avs_ipc_peakvol_set_mute(adev, mod->module_id, mod->instance_id, &mutes[0]); + return AVS_IPC_RET(ret); +} + static int avs_peakvol_create(struct avs_dev *adev, struct avs_path_module *mod) { struct avs_tplg_module *t = mod->template; - struct avs_control_data *ctl_data; + struct soc_mixer_control *mc; struct avs_peakvol_cfg *cfg; - int volume = S32_MAX; size_t cfg_size; int ret; - ctl_data = avs_get_module_control(mod); - if (ctl_data) - volume = ctl_data->volume; - - /* As 2+ channels controls are unsupported, have a single block for all channels. */ cfg_size = struct_size(cfg, vols, 1); if (cfg_size > AVS_MAILBOX_SIZE) return -EINVAL; @@ -312,15 +532,28 @@ static int avs_peakvol_create(struct avs_dev *adev, struct avs_path_module *mod) cfg->base.obs = t->cfg_base->obs; cfg->base.is_pages = t->cfg_base->is_pages; cfg->base.audio_fmt = *t->in_fmt; - cfg->vols[0].target_volume = volume; cfg->vols[0].channel_id = AVS_ALL_CHANNELS_MASK; - cfg->vols[0].curve_type = AVS_AUDIO_CURVE_NONE; - cfg->vols[0].curve_duration = 0; + cfg->vols[0].target_volume = S32_MAX; + cfg->vols[0].curve_type = t->cfg_ext->peakvol.curve_type; + cfg->vols[0].curve_duration = t->cfg_ext->peakvol.curve_duration; ret = avs_dsp_init_module(adev, mod->module_id, mod->owner->instance_id, t->core_id, t->domain, cfg, cfg_size, &mod->instance_id); + if (ret) + return ret; - return ret; + /* Now configure both VOLUME and MUTE parameters. */ + mc = avs_get_module_control(mod, "Volume"); + if (mc) { + ret = avs_peakvol_set_volume(adev, mod, mc, NULL); + if (ret) + return ret; + } + + mc = avs_get_module_control(mod, "Switch"); + if (mc) + return avs_peakvol_set_mute(adev, mod, mc, NULL); + return 0; } static int avs_updown_mix_create(struct avs_dev *adev, struct avs_path_module *mod) @@ -336,7 +569,7 @@ static int avs_updown_mix_create(struct avs_dev *adev, struct avs_path_module *m cfg.base.audio_fmt = *t->in_fmt; cfg.out_channel_config = t->cfg_ext->updown_mix.out_channel_config; cfg.coefficients_select = t->cfg_ext->updown_mix.coefficients_select; - for (i = 0; i < AVS_CHANNELS_MAX; i++) + for (i = 0; i < AVS_COEFF_CHANNELS_MAX; i++) cfg.coefficients[i] = t->cfg_ext->updown_mix.coefficients[i]; cfg.channel_map = t->cfg_ext->updown_mix.channel_map; @@ -367,6 +600,7 @@ static int avs_asrc_create(struct avs_dev *adev, struct avs_path_module *mod) struct avs_tplg_module *t = mod->template; struct avs_asrc_cfg cfg; + memset(&cfg, 0, sizeof(cfg)); cfg.base.cpc = t->cfg_base->cpc; cfg.base.ibs = t->cfg_base->ibs; cfg.base.obs = t->cfg_base->obs; @@ -534,6 +768,7 @@ static struct avs_module_create avs_module_create[] = { { &AVS_ASRC_MOD_UUID, avs_asrc_create }, { &AVS_INTELWOV_MOD_UUID, avs_wov_create }, { &AVS_PROBE_MOD_UUID, avs_probe_create }, + { &AVS_WOVHOSTM_MOD_UUID, avs_whm_create }, }; static int avs_path_module_type_create(struct avs_dev *adev, struct avs_path_module *mod) @@ -710,8 +945,6 @@ static int avs_path_pipeline_arm(struct avs_dev *adev, /* bind current module to next module on list */ source = mod; sink = list_next_entry(mod, node); - if (!source || !sink) - return -EINVAL; ret = avs_ipc_bind(adev, source->module_id, source->instance_id, sink->module_id, sink->instance_id, 0, 0); diff --git a/sound/soc/intel/avs/path.h b/sound/soc/intel/avs/path.h index 657f7b093e80..c65ed84aa853 100644 --- a/sound/soc/intel/avs/path.h +++ b/sound/soc/intel/avs/path.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright(c) 2021 Intel Corporation. All rights reserved. + * Copyright(c) 2021 Intel Corporation * * Authors: Cezary Rojewski <cezary.rojewski@intel.com> * Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> @@ -69,4 +69,14 @@ int avs_path_reset(struct avs_path *path); int avs_path_pause(struct avs_path *path); int avs_path_run(struct avs_path *path, int trigger); +int avs_path_set_constraint(struct avs_dev *adev, struct avs_tplg_path_template *template, + struct snd_pcm_hw_constraint_list *rate_list, + struct snd_pcm_hw_constraint_list *channels_list, + struct snd_pcm_hw_constraint_list *sample_bits_list); + +int avs_peakvol_set_volume(struct avs_dev *adev, struct avs_path_module *mod, + struct soc_mixer_control *mc, long *input); +int avs_peakvol_set_mute(struct avs_dev *adev, struct avs_path_module *mod, + struct soc_mixer_control *mc, long *input); + #endif diff --git a/sound/soc/intel/avs/pcm.c b/sound/soc/intel/avs/pcm.c index 2cafbc392cdb..0efe490024b0 100644 --- a/sound/soc/intel/avs/pcm.c +++ b/sound/soc/intel/avs/pcm.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only // -// Copyright(c) 2021-2022 Intel Corporation. All rights reserved. +// Copyright(c) 2021-2022 Intel Corporation // // Authors: Cezary Rojewski <cezary.rojewski@intel.com> // Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> @@ -16,20 +16,28 @@ #include <sound/soc-component.h> #include "avs.h" #include "path.h" +#include "pcm.h" #include "topology.h" +#include "utils.h" #include "../../codecs/hda.h" struct avs_dma_data { struct avs_tplg_path_template *template; struct avs_path *path; - /* - * link stream is stored within substream's runtime - * private_data to fulfill the needs of codec BE path - * - * host stream assigned - */ - struct hdac_ext_stream *host_stream; + struct avs_dev *adev; + /* LINK-stream utilized in BE operations while HOST in FE ones. */ + union { + struct hdac_ext_stream *link_stream; + struct hdac_ext_stream *host_stream; + }; + + struct snd_pcm_hw_constraint_list rate_list; + struct snd_pcm_hw_constraint_list channels_list; + struct snd_pcm_hw_constraint_list sample_bits_list; + + struct work_struct period_elapsed_work; + struct hdac_ext_link *link; struct snd_pcm_substream *substream; }; @@ -56,15 +64,66 @@ avs_dai_find_path_template(struct snd_soc_dai *dai, bool is_fe, int direction) return dw->priv; } -static int avs_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai, bool is_fe, - const struct snd_soc_dai_ops *ops) +static void avs_period_elapsed_work(struct work_struct *work) +{ + struct avs_dma_data *data = container_of(work, struct avs_dma_data, period_elapsed_work); + + snd_pcm_period_elapsed(data->substream); +} + +void avs_period_elapsed(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); - struct avs_dev *adev = to_avs_dev(dai->dev); + struct snd_soc_dai *dai = snd_soc_rtd_to_cpu(rtd, 0); + struct avs_dma_data *data = snd_soc_dai_get_dma_data(dai, substream); + + schedule_work(&data->period_elapsed_work); +} + +static int hw_rule_param_size(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule); +static int avs_hw_constraints_init(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_pcm_hw_constraint_list *r, *c, *s; + struct avs_dma_data *data; + int ret; + + ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); + if (ret < 0) + return ret; + + data = snd_soc_dai_get_dma_data(dai, substream); + r = &(data->rate_list); + c = &(data->channels_list); + s = &(data->sample_bits_list); + + ret = avs_path_set_constraint(data->adev, data->template, r, c, s); + if (ret <= 0) + return ret; + + ret = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, r); + if (ret < 0) + return ret; + + ret = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, c); + if (ret < 0) + return ret; + + ret = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_SAMPLE_BITS, s); + if (ret < 0) + return ret; + + return 0; +} + +static int avs_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + struct avs_dev *adev = to_avs_dev(dai->component->dev); struct avs_tplg_path_template *template; struct avs_dma_data *data; - template = avs_dai_find_path_template(dai, is_fe, substream->stream); + template = avs_dai_find_path_template(dai, !rtd->dai_link->no_pcm, substream->stream); if (!template) { dev_err(dai->dev, "no %s path for dai %s, invalid tplg?\n", snd_pcm_stream_str(substream), dai->name); @@ -77,12 +136,32 @@ static int avs_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_d data->substream = substream; data->template = template; + data->adev = adev; + INIT_WORK(&data->period_elapsed_work, avs_period_elapsed_work); snd_soc_dai_set_dma_data(dai, substream, data); if (rtd->dai_link->ignore_suspend) adev->num_lp_paths++; - return 0; + return avs_hw_constraints_init(substream, dai); +} + +static void avs_dai_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + struct avs_dma_data *data; + + data = snd_soc_dai_get_dma_data(dai, substream); + + if (rtd->dai_link->ignore_suspend) + data->adev->num_lp_paths--; + + kfree(data->rate_list.list); + kfree(data->channels_list.list); + kfree(data->sample_bits_list.list); + + snd_soc_dai_set_dma_data(dai, substream, NULL); + kfree(data); } static int avs_dai_hw_params(struct snd_pcm_substream *substream, @@ -92,7 +171,6 @@ static int avs_dai_hw_params(struct snd_pcm_substream *substream, { struct avs_dma_data *data; struct avs_path *path; - struct avs_dev *adev = to_avs_dev(dai->dev); int ret; data = snd_soc_dai_get_dma_data(dai, substream); @@ -109,7 +187,7 @@ static int avs_dai_hw_params(struct snd_pcm_substream *substream, params_rate(be_hw_params), params_channels(be_hw_params), params_width(be_hw_params), params_physical_width(be_hw_params)); - path = avs_path_create(adev, dma_id, data->template, fe_hw_params, be_hw_params); + path = avs_path_create(data->adev, dma_id, data->template, fe_hw_params, be_hw_params); if (IS_ERR(path)) { ret = PTR_ERR(path); dev_err(dai->dev, "create path failed: %d\n", ret); @@ -129,6 +207,7 @@ static int avs_dai_be_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dpcm *dpcm; be = snd_soc_substream_to_rtd(substream); + /* dpcm_fe_dai_open() guarantees the list is not empty at this point. */ for_each_dpcm_fe(be, substream->stream, dpcm) { fe = dpcm->fe; fe_hw_params = &fe->dpcm[substream->stream].hw_params; @@ -137,8 +216,7 @@ static int avs_dai_be_hw_params(struct snd_pcm_substream *substream, return avs_dai_hw_params(substream, fe_hw_params, be_hw_params, dai, dma_id); } -static int avs_dai_prepare(struct avs_dev *adev, struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) +static int avs_dai_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct avs_dma_data *data; int ret; @@ -159,28 +237,6 @@ static int avs_dai_prepare(struct avs_dev *adev, struct snd_pcm_substream *subst return ret; } -static const struct snd_soc_dai_ops avs_dai_nonhda_be_ops; - -static int avs_dai_nonhda_be_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) -{ - return avs_dai_startup(substream, dai, false, &avs_dai_nonhda_be_ops); -} - -static void avs_dai_nonhda_be_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) -{ - struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); - struct avs_dev *adev = to_avs_dev(dai->dev); - struct avs_dma_data *data; - - if (rtd->dai_link->ignore_suspend) - adev->num_lp_paths--; - - data = snd_soc_dai_get_dma_data(dai, substream); - - snd_soc_dai_set_dma_data(dai, substream, NULL); - kfree(data); -} - static int avs_dai_nonhda_be_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params, struct snd_soc_dai *dai) { @@ -209,11 +265,6 @@ static int avs_dai_nonhda_be_hw_free(struct snd_pcm_substream *substream, struct return 0; } -static int avs_dai_nonhda_be_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) -{ - return avs_dai_prepare(to_avs_dev(dai->dev), substream, dai); -} - static int avs_dai_nonhda_be_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { @@ -265,108 +316,167 @@ static int avs_dai_nonhda_be_trigger(struct snd_pcm_substream *substream, int cm } static const struct snd_soc_dai_ops avs_dai_nonhda_be_ops = { - .startup = avs_dai_nonhda_be_startup, - .shutdown = avs_dai_nonhda_be_shutdown, + .startup = avs_dai_startup, + .shutdown = avs_dai_shutdown, .hw_params = avs_dai_nonhda_be_hw_params, .hw_free = avs_dai_nonhda_be_hw_free, - .prepare = avs_dai_nonhda_be_prepare, + .prepare = avs_dai_prepare, .trigger = avs_dai_nonhda_be_trigger, }; -static const struct snd_soc_dai_ops avs_dai_hda_be_ops; +static int __avs_dai_hda_be_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai, + struct hdac_ext_link *link) +{ + struct hdac_ext_stream *link_stream; + struct avs_dma_data *data; + int ret; + + ret = avs_dai_startup(substream, dai); + if (ret) + return ret; + + data = snd_soc_dai_get_dma_data(dai, substream); + link_stream = snd_hdac_ext_stream_assign(&data->adev->base.core, substream, + HDAC_EXT_STREAM_TYPE_LINK); + if (!link_stream) { + avs_dai_shutdown(substream, dai); + return -EBUSY; + } + + data->link_stream = link_stream; + data->link = link; + return 0; +} static int avs_dai_hda_be_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - return avs_dai_startup(substream, dai, false, &avs_dai_hda_be_ops); + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + struct hdac_ext_link *link; + struct avs_dma_data *data; + struct hda_codec *codec; + int ret; + + codec = dev_to_hda_codec(snd_soc_rtd_to_codec(rtd, 0)->dev); + + link = snd_hdac_ext_bus_get_hlink_by_addr(&codec->bus->core, codec->core.addr); + if (!link) + return -EINVAL; + + ret = __avs_dai_hda_be_startup(substream, dai, link); + if (!ret) { + data = snd_soc_dai_get_dma_data(dai, substream); + substream->runtime->private_data = data->link_stream; + } + + return ret; +} + +static int avs_dai_i2shda_be_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) +{ + struct avs_dev *adev = to_avs_dev(dai->component->dev); + struct hdac_ext_link *link; + + link = snd_hdac_ext_bus_get_hlink_by_id(&adev->base.core, AZX_REG_ML_LEPTR_ID_INTEL_SSP); + if (!link) + return -EINVAL; + return __avs_dai_hda_be_startup(substream, dai, link); +} + +static int avs_dai_dmichda_be_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) +{ + struct avs_dev *adev = to_avs_dev(dai->component->dev); + struct hdac_ext_link *link; + + link = snd_hdac_ext_bus_get_hlink_by_id(&adev->base.core, AZX_REG_ML_LEPTR_ID_INTEL_DMIC); + if (!link) + return -EINVAL; + return __avs_dai_hda_be_startup(substream, dai, link); } static void avs_dai_hda_be_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - return avs_dai_nonhda_be_shutdown(substream, dai); + struct avs_dma_data *data = snd_soc_dai_get_dma_data(dai, substream); + + snd_hdac_ext_stream_release(data->link_stream, HDAC_EXT_STREAM_TYPE_LINK); + substream->runtime->private_data = NULL; + avs_dai_shutdown(substream, dai); +} + +static void avs_dai_althda_be_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) +{ + struct avs_dma_data *data = snd_soc_dai_get_dma_data(dai, substream); + + snd_hdac_ext_stream_release(data->link_stream, HDAC_EXT_STREAM_TYPE_LINK); + avs_dai_shutdown(substream, dai); } static int avs_dai_hda_be_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params, struct snd_soc_dai *dai) { struct avs_dma_data *data; - struct hdac_ext_stream *link_stream; data = snd_soc_dai_get_dma_data(dai, substream); if (data->path) return 0; - link_stream = substream->runtime->private_data; - return avs_dai_be_hw_params(substream, hw_params, dai, - hdac_stream(link_stream)->stream_tag - 1); + hdac_stream(data->link_stream)->stream_tag - 1); } static int avs_dai_hda_be_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - struct avs_dma_data *data; - struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); struct hdac_ext_stream *link_stream; - struct hdac_ext_link *link; - struct hda_codec *codec; - - dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name); + struct avs_dma_data *data; data = snd_soc_dai_get_dma_data(dai, substream); if (!data->path) return 0; - link_stream = substream->runtime->private_data; + link_stream = data->link_stream; link_stream->link_prepared = false; avs_path_free(data->path); data->path = NULL; /* clear link <-> stream mapping */ - codec = dev_to_hda_codec(snd_soc_rtd_to_codec(rtd, 0)->dev); - link = snd_hdac_ext_bus_get_hlink_by_addr(&codec->bus->core, codec->core.addr); - if (!link) - return -EINVAL; - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - snd_hdac_ext_bus_link_clear_stream_id(link, hdac_stream(link_stream)->stream_tag); + snd_hdac_ext_bus_link_clear_stream_id(data->link, + hdac_stream(link_stream)->stream_tag); return 0; } static int avs_dai_hda_be_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); - struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_soc_pcm_stream *stream_info; + struct snd_soc_pcm_runtime *be = snd_soc_substream_to_rtd(substream); + const struct snd_soc_pcm_stream *stream_info; struct hdac_ext_stream *link_stream; - struct hdac_ext_link *link; - struct hda_codec *codec; - struct hdac_bus *bus; + const struct snd_pcm_hw_params *p; + struct avs_dma_data *data; unsigned int format_val; unsigned int bits; int ret; - link_stream = runtime->private_data; + data = snd_soc_dai_get_dma_data(dai, substream); + link_stream = data->link_stream; + p = &be->dpcm[substream->stream].hw_params; + if (link_stream->link_prepared) return 0; - codec = dev_to_hda_codec(snd_soc_rtd_to_codec(rtd, 0)->dev); - bus = &codec->bus->core; stream_info = snd_soc_dai_get_pcm_stream(dai, substream->stream); - bits = snd_hdac_stream_format_bits(runtime->format, runtime->subformat, + bits = snd_hdac_stream_format_bits(params_format(p), params_subformat(p), stream_info->sig_bits); - format_val = snd_hdac_stream_format(runtime->channels, bits, runtime->rate); + format_val = snd_hdac_stream_format(params_channels(p), bits, params_rate(p)); + snd_hdac_ext_stream_decouple(&data->adev->base.core, link_stream, true); snd_hdac_ext_stream_reset(link_stream); snd_hdac_ext_stream_setup(link_stream, format_val); - link = snd_hdac_ext_bus_get_hlink_by_addr(bus, codec->core.addr); - if (!link) - return -EINVAL; - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - snd_hdac_ext_bus_link_set_stream_id(link, hdac_stream(link_stream)->stream_tag); + snd_hdac_ext_bus_link_set_stream_id(data->link, + hdac_stream(link_stream)->stream_tag); - ret = avs_dai_prepare(to_avs_dev(dai->dev), substream, dai); + ret = avs_dai_prepare(substream, dai); if (ret) return ret; @@ -378,14 +488,12 @@ static int avs_dai_hda_be_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); - struct hdac_ext_stream *link_stream; struct avs_dma_data *data; int ret = 0; dev_dbg(dai->dev, "entry %s cmd=%d\n", __func__, cmd); data = snd_soc_dai_get_dma_data(dai, substream); - link_stream = substream->runtime->private_data; switch (cmd) { case SNDRV_PCM_TRIGGER_RESUME: @@ -394,7 +502,7 @@ static int avs_dai_hda_be_trigger(struct snd_pcm_substream *substream, int cmd, fallthrough; case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - snd_hdac_ext_stream_start(link_stream); + snd_hdac_ext_stream_start(data->link_stream); ret = avs_path_pause(data->path); if (ret < 0) { @@ -417,7 +525,7 @@ static int avs_dai_hda_be_trigger(struct snd_pcm_substream *substream, int cmd, if (ret < 0) dev_err(dai->dev, "pause BE path failed: %d\n", ret); - snd_hdac_ext_stream_clear(link_stream); + snd_hdac_ext_stream_clear(data->link_stream); ret = avs_path_reset(data->path); if (ret < 0) @@ -441,82 +549,110 @@ static const struct snd_soc_dai_ops avs_dai_hda_be_ops = { .trigger = avs_dai_hda_be_trigger, }; -static const unsigned int rates[] = { - 8000, 11025, 12000, 16000, - 22050, 24000, 32000, 44100, - 48000, 64000, 88200, 96000, - 128000, 176400, 192000, +static const struct snd_soc_dai_ops avs_dai_i2shda_be_ops = { + .startup = avs_dai_i2shda_be_startup, + .shutdown = avs_dai_althda_be_shutdown, + .hw_params = avs_dai_hda_be_hw_params, + .hw_free = avs_dai_hda_be_hw_free, + .prepare = avs_dai_hda_be_prepare, + .trigger = avs_dai_hda_be_trigger, }; -static const struct snd_pcm_hw_constraint_list hw_rates = { - .count = ARRAY_SIZE(rates), - .list = rates, - .mask = 0, +static const struct snd_soc_dai_ops avs_dai_dmichda_be_ops = { + .startup = avs_dai_dmichda_be_startup, + .shutdown = avs_dai_althda_be_shutdown, + .hw_params = avs_dai_hda_be_hw_params, + .hw_free = avs_dai_hda_be_hw_free, + .prepare = avs_dai_hda_be_prepare, + .trigger = avs_dai_hda_be_trigger, }; -const struct snd_soc_dai_ops avs_dai_fe_ops; +static int hw_rule_param_size(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) +{ + struct snd_interval *interval = hw_param_interval(params, rule->var); + struct snd_interval to; -static int avs_dai_fe_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) + snd_interval_any(&to); + to.integer = interval->integer; + to.max = interval->max; + /* + * Commonly 2ms buffer size is used in HDA scenarios whereas 4ms is used + * when streaming through GPDMA. Align to the latter to account for both. + */ + to.min = params_rate(params) / 1000 * 4; + + if (rule->var == SNDRV_PCM_HW_PARAM_PERIOD_SIZE) + to.min /= params_periods(params); + + return snd_interval_refine(interval, &to); +} + +static int avs_pcm_hw_constraints_init(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; - struct avs_dma_data *data; - struct avs_dev *adev = to_avs_dev(dai->dev); - struct hdac_bus *bus = &adev->base.core; + int ret; + + ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); + if (ret < 0) + return ret; + + /* Avoid wrap-around with wall-clock. */ + ret = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_TIME, 20, 178000000); + if (ret < 0) + return ret; + + /* Adjust buffer and period size based on the audio format. */ + snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, hw_rule_param_size, NULL, + SNDRV_PCM_HW_PARAM_FORMAT, SNDRV_PCM_HW_PARAM_CHANNELS, + SNDRV_PCM_HW_PARAM_RATE, -1); + snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, hw_rule_param_size, NULL, + SNDRV_PCM_HW_PARAM_FORMAT, SNDRV_PCM_HW_PARAM_CHANNELS, + SNDRV_PCM_HW_PARAM_RATE, -1); + + return 0; +} + +static int avs_dai_fe_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) +{ struct hdac_ext_stream *host_stream; + struct avs_dma_data *data; + struct hdac_bus *bus; int ret; - ret = avs_dai_startup(substream, dai, true, &avs_dai_fe_ops); + ret = avs_pcm_hw_constraints_init(substream); + if (ret) + return ret; + + ret = avs_dai_startup(substream, dai); if (ret) return ret; data = snd_soc_dai_get_dma_data(dai, substream); + bus = &data->adev->base.core; host_stream = snd_hdac_ext_stream_assign(bus, substream, HDAC_EXT_STREAM_TYPE_HOST); if (!host_stream) { - ret = -EBUSY; - goto err; + avs_dai_shutdown(substream, dai); + return -EBUSY; } data->host_stream = host_stream; - ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); - if (ret < 0) - goto err; - - /* avoid wrap-around with wall-clock */ - ret = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_TIME, 20, 178000000); - if (ret < 0) - goto err; - - ret = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_rates); - if (ret < 0) - goto err; - snd_pcm_set_sync(substream); dev_dbg(dai->dev, "%s fe STARTUP tag %d str %p", __func__, hdac_stream(host_stream)->stream_tag, substream); return 0; - -err: - kfree(data); - return ret; } static void avs_dai_fe_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); - struct avs_dev *adev = to_avs_dev(dai->dev); struct avs_dma_data *data; - if (rtd->dai_link->ignore_suspend) - adev->num_lp_paths--; - data = snd_soc_dai_get_dma_data(dai, substream); - snd_soc_dai_set_dma_data(dai, substream, NULL); snd_hdac_ext_stream_release(data->host_stream, HDAC_EXT_STREAM_TYPE_HOST); - kfree(data); + avs_dai_shutdown(substream, dai); } static int avs_dai_fe_hw_params(struct snd_pcm_substream *substream, @@ -540,6 +676,7 @@ static int avs_dai_fe_hw_params(struct snd_pcm_substream *substream, hdac_stream(host_stream)->format_val = 0; fe = snd_soc_substream_to_rtd(substream); + /* dpcm_fe_dai_open() guarantees the list is not empty at this point. */ for_each_dpcm_be(fe, substream->stream, dpcm) { be = dpcm->be; be_hw_params = &be->dpcm[substream->stream].hw_params; @@ -606,11 +743,11 @@ static int avs_dai_fe_hw_free(struct snd_pcm_substream *substream, struct snd_so static int avs_dai_fe_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_soc_pcm_stream *stream_info; + const struct snd_soc_pcm_stream *stream_info; struct avs_dma_data *data; - struct avs_dev *adev = to_avs_dev(dai->dev); struct hdac_ext_stream *host_stream; unsigned int format_val; + struct hdac_bus *bus; unsigned int bits; int ret; @@ -620,6 +757,8 @@ static int avs_dai_fe_prepare(struct snd_pcm_substream *substream, struct snd_so if (hdac_stream(host_stream)->prepared) return 0; + bus = hdac_stream(host_stream)->bus; + snd_hdac_ext_stream_decouple(bus, data->host_stream, true); snd_hdac_stream_reset(hdac_stream(host_stream)); stream_info = snd_soc_dai_get_pcm_stream(dai, substream->stream); @@ -635,7 +774,7 @@ static int avs_dai_fe_prepare(struct snd_pcm_substream *substream, struct snd_so if (ret < 0) return ret; - ret = avs_dai_prepare(adev, substream, dai); + ret = avs_dai_prepare(substream, dai); if (ret) return ret; @@ -887,7 +1026,8 @@ static int avs_component_probe(struct snd_soc_component *component) else mach->tplg_filename = devm_kasprintf(adev->dev, GFP_KERNEL, "hda-generic-tplg.bin"); - + if (!mach->tplg_filename) + return -ENOMEM; filename = kasprintf(GFP_KERNEL, "%s/%s", component->driver->topology_name_prefix, mach->tplg_filename); if (!filename) @@ -1226,7 +1366,7 @@ static int avs_component_construct(struct snd_soc_component *component, return 0; } -static const struct snd_soc_component_driver avs_component_driver = { +static struct snd_soc_component_driver avs_component_driver = { .name = "avs-pcm", .probe = avs_component_probe, .remove = avs_component_remove, @@ -1241,7 +1381,7 @@ static const struct snd_soc_component_driver avs_component_driver = { }; int avs_soc_component_register(struct device *dev, const char *name, - const struct snd_soc_component_driver *drv, + struct snd_soc_component_driver *drv, struct snd_soc_dai_driver *cpu_dais, int num_cpu_dais) { struct avs_soc_component *acomp; @@ -1259,13 +1399,14 @@ int avs_soc_component_register(struct device *dev, const char *name, acomp->base.name = name; INIT_LIST_HEAD(&acomp->node); + drv->use_dai_pcm_id = !obsolete_card_names; + return snd_soc_add_component(&acomp->base, cpu_dais, num_cpu_dais); } static struct snd_soc_dai_driver dmic_cpu_dais[] = { { .name = "DMIC Pin", - .ops = &avs_dai_nonhda_be_ops, .capture = { .stream_name = "DMIC Rx", .channels_min = 1, @@ -1276,7 +1417,6 @@ static struct snd_soc_dai_driver dmic_cpu_dais[] = { }, { .name = "DMIC WoV Pin", - .ops = &avs_dai_nonhda_be_ops, .capture = { .stream_name = "DMIC WoV Rx", .channels_min = 1, @@ -1289,17 +1429,27 @@ static struct snd_soc_dai_driver dmic_cpu_dais[] = { int avs_dmic_platform_register(struct avs_dev *adev, const char *name) { + const struct snd_soc_dai_ops *ops; + + if (avs_platattr_test(adev, ALTHDA)) + ops = &avs_dai_dmichda_be_ops; + else + ops = &avs_dai_nonhda_be_ops; + + dmic_cpu_dais[0].ops = ops; + dmic_cpu_dais[1].ops = ops; return avs_soc_component_register(adev->dev, name, &avs_component_driver, dmic_cpu_dais, ARRAY_SIZE(dmic_cpu_dais)); } static const struct snd_soc_dai_driver i2s_dai_template = { - .ops = &avs_dai_nonhda_be_ops, .playback = { .channels_min = 1, - .channels_max = 8, + .channels_max = AVS_CHANNELS_MAX, .rates = SNDRV_PCM_RATE_8000_192000 | - SNDRV_PCM_RATE_KNOT, + SNDRV_PCM_RATE_12000 | + SNDRV_PCM_RATE_24000 | + SNDRV_PCM_RATE_128000, .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE, .subformats = SNDRV_PCM_SUBFMTBIT_MSBITS_20 | @@ -1308,9 +1458,11 @@ static const struct snd_soc_dai_driver i2s_dai_template = { }, .capture = { .channels_min = 1, - .channels_max = 8, + .channels_max = AVS_CHANNELS_MAX, .rates = SNDRV_PCM_RATE_8000_192000 | - SNDRV_PCM_RATE_KNOT, + SNDRV_PCM_RATE_12000 | + SNDRV_PCM_RATE_24000 | + SNDRV_PCM_RATE_128000, .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE, .subformats = SNDRV_PCM_SUBFMTBIT_MSBITS_20 | @@ -1323,10 +1475,15 @@ int avs_i2s_platform_register(struct avs_dev *adev, const char *name, unsigned l unsigned long *tdms) { struct snd_soc_dai_driver *cpus, *dai; + const struct snd_soc_dai_ops *ops; size_t ssp_count, cpu_count; int i, j; ssp_count = adev->hw_cfg.i2s_caps.ctrl_count; + if (avs_platattr_test(adev, ALTHDA)) + ops = &avs_dai_i2shda_be_ops; + else + ops = &avs_dai_nonhda_be_ops; cpu_count = 0; for_each_set_bit(i, &port_mask, ssp_count) @@ -1336,7 +1493,7 @@ int avs_i2s_platform_register(struct avs_dev *adev, const char *name, unsigned l for_each_set_bit(i, &port_mask, ssp_count) cpu_count += hweight_long(tdms[i]); - cpus = devm_kzalloc(adev->dev, sizeof(*cpus) * cpu_count, GFP_KERNEL); + cpus = devm_kcalloc(adev->dev, cpu_count, sizeof(*cpus), GFP_KERNEL); if (!cpus) return -ENOMEM; @@ -1354,6 +1511,7 @@ int avs_i2s_platform_register(struct avs_dev *adev, const char *name, unsigned l if (!dai->name || !dai->playback.stream_name || !dai->capture.stream_name) return -ENOMEM; + dai->ops = ops; dai++; } } @@ -1362,7 +1520,7 @@ int avs_i2s_platform_register(struct avs_dev *adev, const char *name, unsigned l goto plat_register; for_each_set_bit(i, &port_mask, ssp_count) { - for_each_set_bit(j, &tdms[i], ssp_count) { + for_each_set_bit(j, &tdms[i], AVS_CHANNELS_MAX) { memcpy(dai, &i2s_dai_template, sizeof(*dai)); dai->name = @@ -1374,6 +1532,7 @@ int avs_i2s_platform_register(struct avs_dev *adev, const char *name, unsigned l if (!dai->name || !dai->playback.stream_name || !dai->capture.stream_name) return -ENOMEM; + dai->ops = ops; dai++; } } @@ -1387,7 +1546,7 @@ static const struct snd_soc_dai_driver hda_cpu_dai = { .ops = &avs_dai_hda_be_ops, .playback = { .channels_min = 1, - .channels_max = 8, + .channels_max = AVS_CHANNELS_MAX, .rates = SNDRV_PCM_RATE_8000_192000, .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE, @@ -1397,7 +1556,7 @@ static const struct snd_soc_dai_driver hda_cpu_dai = { }, .capture = { .channels_min = 1, - .channels_max = 8, + .channels_max = AVS_CHANNELS_MAX, .rates = SNDRV_PCM_RATE_8000_192000, .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE, @@ -1411,12 +1570,14 @@ static void avs_component_hda_unregister_dais(struct snd_soc_component *componen { struct snd_soc_acpi_mach *mach; struct snd_soc_dai *dai, *save; + struct avs_mach_pdata *pdata; struct hda_codec *codec; char name[32]; mach = dev_get_platdata(component->card->dev); - codec = mach->pdata; - sprintf(name, "%s-cpu", dev_name(&codec->core.dev)); + pdata = mach->pdata; + codec = pdata->codec; + snprintf(name, sizeof(name), "%s-cpu", dev_name(&codec->core.dev)); for_each_component_dais_safe(component, dai, save) { int stream; @@ -1436,6 +1597,7 @@ static int avs_component_hda_probe(struct snd_soc_component *component) struct snd_soc_dapm_context *dapm; struct snd_soc_dai_driver *dais; struct snd_soc_acpi_mach *mach; + struct avs_mach_pdata *pdata; struct hda_codec *codec; struct hda_pcm *pcm; const char *cname; @@ -1445,7 +1607,8 @@ static int avs_component_hda_probe(struct snd_soc_component *component) if (!mach) return -EINVAL; - codec = mach->pdata; + pdata = mach->pdata; + codec = pdata->codec; if (list_empty(&codec->pcm_list_head)) return -EINVAL; list_for_each_entry(pcm, &codec->pcm_list_head, list) @@ -1522,6 +1685,7 @@ static int avs_component_hda_probe(struct snd_soc_component *component) if (ret < 0) { dev_err(component->dev, "create widgets failed: %d\n", ret); + snd_soc_unregister_dai(dai); goto exit; } } @@ -1536,16 +1700,14 @@ exit: static void avs_component_hda_remove(struct snd_soc_component *component) { - avs_component_hda_unregister_dais(component); avs_component_remove(component); + avs_component_hda_unregister_dais(component); } static int avs_component_hda_open(struct snd_soc_component *component, struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); - struct hdac_ext_stream *link_stream; - struct hda_codec *codec; if (!rtd->dai_link->no_pcm) { struct snd_pcm_hardware hwparams = avs_pcm_hardware; @@ -1577,41 +1739,16 @@ static int avs_component_hda_open(struct snd_soc_component *component, return snd_soc_set_runtime_hwparams(substream, &hwparams); } - codec = dev_to_hda_codec(snd_soc_rtd_to_codec(rtd, 0)->dev); - link_stream = snd_hdac_ext_stream_assign(&codec->bus->core, substream, - HDAC_EXT_STREAM_TYPE_LINK); - if (!link_stream) - return -EBUSY; - - substream->runtime->private_data = link_stream; - return 0; -} - -static int avs_component_hda_close(struct snd_soc_component *component, - struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); - struct hdac_ext_stream *link_stream; - - /* only BE DAI links are handled here */ - if (!rtd->dai_link->no_pcm) - return 0; - - link_stream = substream->runtime->private_data; - snd_hdac_ext_stream_release(link_stream, HDAC_EXT_STREAM_TYPE_LINK); - substream->runtime->private_data = NULL; - return 0; } -static const struct snd_soc_component_driver avs_hda_component_driver = { +static struct snd_soc_component_driver avs_hda_component_driver = { .name = "avs-hda-pcm", .probe = avs_component_hda_probe, .remove = avs_component_hda_remove, .suspend = avs_component_suspend, .resume = avs_component_resume, .open = avs_component_hda_open, - .close = avs_component_hda_close, .pointer = avs_component_pointer, .mmap = avs_component_mmap, .pcm_construct = avs_component_construct, diff --git a/sound/soc/intel/avs/pcm.h b/sound/soc/intel/avs/pcm.h new file mode 100644 index 000000000000..0f3615c90398 --- /dev/null +++ b/sound/soc/intel/avs/pcm.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright(c) 2024 Intel Corporation + * + * Authors: Cezary Rojewski <cezary.rojewski@intel.com> + * Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> + */ + +#ifndef __SOUND_SOC_INTEL_AVS_PCM_H +#define __SOUND_SOC_INTEL_AVS_PCM_H + +#include <sound/pcm.h> + +void avs_period_elapsed(struct snd_pcm_substream *substream); + +#endif diff --git a/sound/soc/intel/avs/probes.c b/sound/soc/intel/avs/probes.c index 817e543036f2..a42736b9aa55 100644 --- a/sound/soc/intel/avs/probes.c +++ b/sound/soc/intel/avs/probes.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only // -// Copyright(c) 2021-2022 Intel Corporation. All rights reserved. +// Copyright(c) 2021-2022 Intel Corporation // // Authors: Cezary Rojewski <cezary.rojewski@intel.com> // Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> @@ -19,8 +19,11 @@ static int avs_dsp_init_probe(struct avs_dev *adev, union avs_connector_node_id struct avs_probe_cfg cfg = {{0}}; struct avs_module_entry mentry; u8 dummy; + int ret; - avs_get_module_entry(adev, &AVS_PROBE_MOD_UUID, &mentry); + ret = avs_get_module_entry(adev, &AVS_PROBE_MOD_UUID, &mentry); + if (ret) + return ret; /* * Probe module uses no cycles, audio data format and input and output @@ -39,11 +42,12 @@ static int avs_dsp_init_probe(struct avs_dev *adev, union avs_connector_node_id static void avs_dsp_delete_probe(struct avs_dev *adev) { struct avs_module_entry mentry; + int ret; - avs_get_module_entry(adev, &AVS_PROBE_MOD_UUID, &mentry); - - /* There is only ever one probe module instance. */ - avs_dsp_delete_module(adev, mentry.module_id, 0, INVALID_PIPELINE_ID, 0); + ret = avs_get_module_entry(adev, &AVS_PROBE_MOD_UUID, &mentry); + if (!ret) + /* There is only ever one probe module instance. */ + avs_dsp_delete_module(adev, mentry.module_id, 0, INVALID_PIPELINE_ID, 0); } static inline struct hdac_ext_stream *avs_compr_get_host_stream(struct snd_compr_stream *cstream) @@ -280,7 +284,7 @@ static struct snd_soc_dai_driver probe_cpu_dais[] = { }, }; -static const struct snd_soc_component_driver avs_probe_component_driver = { +static struct snd_soc_component_driver avs_probe_component_driver = { .name = "avs-probe-compr", .compress_ops = &avs_probe_compress_ops, .module_get_upon_open = 1, /* increment refcount when a stream is opened */ diff --git a/sound/soc/intel/avs/ptl.c b/sound/soc/intel/avs/ptl.c new file mode 100644 index 000000000000..2be4b545c91d --- /dev/null +++ b/sound/soc/intel/avs/ptl.c @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright(c) 2024-2025 Intel Corporation + * + * Authors: Cezary Rojewski <cezary.rojewski@intel.com> + * Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> + */ + +#include <sound/hdaudio_ext.h> +#include "avs.h" +#include "registers.h" +#include "trace.h" + +#define MTL_HfDSSGBL_BASE 0x1000 +#define MTL_REG_HfDSSCS (MTL_HfDSSGBL_BASE + 0x0) +#define MTL_HfDSSCS_SPA BIT(16) +#define MTL_HfDSSCS_CPA BIT(24) + +#define MTL_DSPCS_BASE 0x178D00 +#define MTL_REG_DSPCCTL (MTL_DSPCS_BASE + 0x4) +#define MTL_DSPCCTL_OSEL GENMASK(25, 24) +#define MTL_DSPCCTL_OSEL_HOST BIT(25) + +static int avs_ptl_core_power_on(struct avs_dev *adev) +{ + u32 reg; + int ret; + + /* Power up DSP domain. */ + snd_hdac_adsp_updatel(adev, MTL_REG_HfDSSCS, MTL_HfDSSCS_SPA, MTL_HfDSSCS_SPA); + trace_avs_dsp_core_op(1, AVS_MAIN_CORE_MASK, "power dsp", true); + + ret = snd_hdac_adsp_readl_poll(adev, MTL_REG_HfDSSCS, reg, + (reg & MTL_HfDSSCS_CPA) == MTL_HfDSSCS_CPA, + AVS_ADSPCS_INTERVAL_US, AVS_ADSPCS_TIMEOUT_US); + if (ret) { + dev_err(adev->dev, "power on domain dsp failed: %d\n", ret); + return ret; + } + + /* Prevent power gating of DSP domain. */ + snd_hdac_adsp_updatel(adev, MTL_REG_HfPWRCTL2, MTL_HfPWRCTL2_WPDSPHPxPG, + MTL_HfPWRCTL2_WPDSPHPxPG); + trace_avs_dsp_core_op(1, AVS_MAIN_CORE_MASK, "prevent dsp PG", true); + + ret = snd_hdac_adsp_readl_poll(adev, MTL_REG_HfPWRSTS2, reg, + (reg & MTL_HfPWRSTS2_DSPHPxPGS) == MTL_HfPWRSTS2_DSPHPxPGS, + AVS_ADSPCS_INTERVAL_US, AVS_ADSPCS_TIMEOUT_US); + + /* Set ownership to HOST. */ + snd_hdac_adsp_updatel(adev, MTL_REG_DSPCCTL, MTL_DSPCCTL_OSEL, MTL_DSPCCTL_OSEL_HOST); + return ret; +} + +static int avs_ptl_core_power_off(struct avs_dev *adev) +{ + u32 reg; + + /* Allow power gating of DSP domain. No STS polling as HOST is only one of its users. */ + snd_hdac_adsp_updatel(adev, MTL_REG_HfPWRCTL2, MTL_HfPWRCTL2_WPDSPHPxPG, 0); + trace_avs_dsp_core_op(0, AVS_MAIN_CORE_MASK, "allow dsp pg", false); + + /* Power down DSP domain. */ + snd_hdac_adsp_updatel(adev, MTL_REG_HfDSSCS, MTL_HfDSSCS_SPA, 0); + trace_avs_dsp_core_op(0, AVS_MAIN_CORE_MASK, "power dsp", false); + + return snd_hdac_adsp_readl_poll(adev, MTL_REG_HfDSSCS, reg, + (reg & MTL_HfDSSCS_CPA) == 0, + AVS_ADSPCS_INTERVAL_US, AVS_ADSPCS_TIMEOUT_US); +} + +static int avs_ptl_core_power(struct avs_dev *adev, u32 core_mask, bool power) +{ + core_mask &= AVS_MAIN_CORE_MASK; + if (!core_mask) + return 0; + + if (power) + return avs_ptl_core_power_on(adev); + return avs_ptl_core_power_off(adev); +} + +const struct avs_dsp_ops avs_ptl_dsp_ops = { + .power = avs_ptl_core_power, + .reset = avs_mtl_core_reset, + .stall = avs_lnl_core_stall, + .dsp_interrupt = avs_mtl_dsp_interrupt, + .int_control = avs_mtl_interrupt_control, + .load_basefw = avs_hda_load_basefw, + .load_lib = avs_hda_load_library, + .transfer_mods = avs_hda_transfer_modules, + .log_buffer_offset = avs_icl_log_buffer_offset, + .log_buffer_status = avs_apl_log_buffer_status, + .coredump = avs_apl_coredump, + .d0ix_toggle = avs_icl_d0ix_toggle, + .set_d0ix = avs_icl_set_d0ix, + AVS_SET_ENABLE_LOGS_OP(icl) +}; diff --git a/sound/soc/intel/avs/registers.h b/sound/soc/intel/avs/registers.h index 6126adca500c..97767882ffa1 100644 --- a/sound/soc/intel/avs/registers.h +++ b/sound/soc/intel/avs/registers.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright(c) 2021-2022 Intel Corporation. All rights reserved. + * Copyright(c) 2021-2022 Intel Corporation * * Authors: Cezary Rojewski <cezary.rojewski@intel.com> * Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> @@ -9,6 +9,8 @@ #ifndef __SOUND_SOC_INTEL_AVS_REGS_H #define __SOUND_SOC_INTEL_AVS_REGS_H +#include <linux/io-64-nonatomic-lo-hi.h> +#include <linux/iopoll.h> #include <linux/sizes.h> #define AZX_PCIREG_PGCTL 0x44 @@ -33,6 +35,8 @@ #define AVS_ADSPCS_CSTALL_MASK(cm) ((cm) << 8) #define AVS_ADSPCS_SPA_MASK(cm) ((cm) << 16) #define AVS_ADSPCS_CPA_MASK(cm) ((cm) << 24) +#define AVS_ADSPCS_INTERVAL_US 500 +#define AVS_ADSPCS_TIMEOUT_US 10000 #define AVS_MAIN_CORE_MASK BIT(0) #define AVS_ADSP_HIPCCTL_BUSY BIT(0) @@ -65,16 +69,52 @@ #define CNL_ADSP_HIPCIDR_BUSY BIT(31) #define CNL_ADSP_HIPCIDA_DONE BIT(31) +/* MTL Intel HOST Inter-Processor Communication Registers */ +#define MTL_HfIPC_BASE 0x73000 +#define MTL_REG_HfIPCxTDR (MTL_HfIPC_BASE + 0x200) +#define MTL_REG_HfIPCxTDA (MTL_HfIPC_BASE + 0x204) +#define MTL_REG_HfIPCxIDR (MTL_HfIPC_BASE + 0x210) +#define MTL_REG_HfIPCxIDA (MTL_HfIPC_BASE + 0x214) +#define MTL_REG_HfIPCxCTL (MTL_HfIPC_BASE + 0x228) +#define MTL_REG_HfIPCxTDD (MTL_HfIPC_BASE + 0x300) +#define MTL_REG_HfIPCxIDD (MTL_HfIPC_BASE + 0x380) + +#define MTL_HfIPCxTDR_BUSY BIT(31) +#define MTL_HfIPCxTDA_BUSY BIT(31) +#define MTL_HfIPCxIDR_BUSY BIT(31) +#define MTL_HfIPCxIDA_DONE BIT(31) + +#define MTL_HfFLV_BASE 0x162000 +#define MTL_REG_HfFLGP(x, y) (MTL_HfFLV_BASE + 0x1200 + (x) * 0x20 + (y) * 0x08) +#define LNL_REG_HfDFR(x) (0x160200 + (x) * 0x8) + +#define MTL_DWICTL_BASE 0x1800 +#define MTL_DWICTL_REG_INTENL (MTL_DWICTL_BASE + 0x0) +#define MTL_DWICTL_REG_FINALSTATUSL (MTL_DWICTL_BASE + 0x30) + +#define MTL_HfPMCCU_BASE 0x1D00 +#define MTL_REG_HfCLKCTL (MTL_HfPMCCU_BASE + 0x10) +#define MTL_REG_HfPWRCTL (MTL_HfPMCCU_BASE + 0x18) +#define MTL_REG_HfPWRSTS (MTL_HfPMCCU_BASE + 0x1C) +#define MTL_REG_HfPWRCTL2 (MTL_HfPMCCU_BASE + 0x20) +#define MTL_REG_HfPWRSTS2 (MTL_HfPMCCU_BASE + 0x24) +#define MTL_HfPWRCTL_WPDSPHPxPG BIT(0) +#define MTL_HfPWRSTS_DSPHPxPGS BIT(0) +#define MTL_HfPWRCTL2_WPDSPHPxPG BIT(0) +#define MTL_HfPWRSTS2_DSPHPxPGS BIT(0) + /* Intel HD Audio SRAM windows base addresses */ #define SKL_ADSP_SRAM_BASE_OFFSET 0x8000 #define SKL_ADSP_SRAM_WINDOW_SIZE 0x2000 #define APL_ADSP_SRAM_BASE_OFFSET 0x80000 #define APL_ADSP_SRAM_WINDOW_SIZE 0x20000 +#define MTL_ADSP_SRAM_BASE_OFFSET 0x180000 +#define MTL_ADSP_SRAM_WINDOW_SIZE 0x8000 /* Constants used when accessing SRAM, space shared with firmware */ -#define AVS_FW_REG_BASE(adev) ((adev)->spec->sram->base_offset) +#define AVS_FW_REG_BASE(adev) ((adev)->spec->hipc->sts_offset) #define AVS_FW_REG_STATUS(adev) (AVS_FW_REG_BASE(adev) + 0x0) -#define AVS_FW_REG_ERROR_CODE(adev) (AVS_FW_REG_BASE(adev) + 0x4) +#define AVS_FW_REG_ERROR(adev) (AVS_FW_REG_BASE(adev) + 0x4) #define AVS_WINDOW_CHUNK_SIZE SZ_4K #define AVS_FW_REGS_SIZE AVS_WINDOW_CHUNK_SIZE @@ -98,4 +138,47 @@ #define avs_downlink_addr(adev) \ avs_sram_addr(adev, AVS_DOWNLINK_WINDOW) +#define snd_hdac_adsp_writeb(adev, reg, value) \ + snd_hdac_reg_writeb(&(adev)->base.core, (adev)->dsp_ba + (reg), value) +#define snd_hdac_adsp_readb(adev, reg) \ + snd_hdac_reg_readb(&(adev)->base.core, (adev)->dsp_ba + (reg)) +#define snd_hdac_adsp_writew(adev, reg, value) \ + snd_hdac_reg_writew(&(adev)->base.core, (adev)->dsp_ba + (reg), value) +#define snd_hdac_adsp_readw(adev, reg) \ + snd_hdac_reg_readw(&(adev)->base.core, (adev)->dsp_ba + (reg)) +#define snd_hdac_adsp_writel(adev, reg, value) \ + snd_hdac_reg_writel(&(adev)->base.core, (adev)->dsp_ba + (reg), value) +#define snd_hdac_adsp_readl(adev, reg) \ + snd_hdac_reg_readl(&(adev)->base.core, (adev)->dsp_ba + (reg)) +#define snd_hdac_adsp_writeq(adev, reg, value) \ + snd_hdac_reg_writeq(&(adev)->base.core, (adev)->dsp_ba + (reg), value) +#define snd_hdac_adsp_readq(adev, reg) \ + snd_hdac_reg_readq(&(adev)->base.core, (adev)->dsp_ba + (reg)) + +#define snd_hdac_adsp_updateb(adev, reg, mask, val) \ + snd_hdac_adsp_writeb(adev, reg, \ + (snd_hdac_adsp_readb(adev, reg) & ~(mask)) | (val)) +#define snd_hdac_adsp_updatew(adev, reg, mask, val) \ + snd_hdac_adsp_writew(adev, reg, \ + (snd_hdac_adsp_readw(adev, reg) & ~(mask)) | (val)) +#define snd_hdac_adsp_updatel(adev, reg, mask, val) \ + snd_hdac_adsp_writel(adev, reg, \ + (snd_hdac_adsp_readl(adev, reg) & ~(mask)) | (val)) +#define snd_hdac_adsp_updateq(adev, reg, mask, val) \ + snd_hdac_adsp_writeq(adev, reg, \ + (snd_hdac_adsp_readq(adev, reg) & ~(mask)) | (val)) + +#define snd_hdac_adsp_readb_poll(adev, reg, val, cond, delay_us, timeout_us) \ + readb_poll_timeout((adev)->dsp_ba + (reg), val, cond, \ + delay_us, timeout_us) +#define snd_hdac_adsp_readw_poll(adev, reg, val, cond, delay_us, timeout_us) \ + readw_poll_timeout((adev)->dsp_ba + (reg), val, cond, \ + delay_us, timeout_us) +#define snd_hdac_adsp_readl_poll(adev, reg, val, cond, delay_us, timeout_us) \ + readl_poll_timeout((adev)->dsp_ba + (reg), val, cond, \ + delay_us, timeout_us) +#define snd_hdac_adsp_readq_poll(adev, reg, val, cond, delay_us, timeout_us) \ + readq_poll_timeout((adev)->dsp_ba + (reg), val, cond, \ + delay_us, timeout_us) + #endif /* __SOUND_SOC_INTEL_AVS_REGS_H */ diff --git a/sound/soc/intel/avs/skl.c b/sound/soc/intel/avs/skl.c index d19f8953993f..d66ef000de9e 100644 --- a/sound/soc/intel/avs/skl.c +++ b/sound/soc/intel/avs/skl.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only // -// Copyright(c) 2021-2022 Intel Corporation. All rights reserved. +// Copyright(c) 2021-2022 Intel Corporation // // Authors: Cezary Rojewski <cezary.rojewski@intel.com> // Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> @@ -10,31 +10,68 @@ #include <linux/slab.h> #include <sound/hdaudio_ext.h> #include "avs.h" +#include "cldma.h" #include "messages.h" +#include "registers.h" -irqreturn_t avs_skl_irq_thread(struct avs_dev *adev) +void avs_skl_ipc_interrupt(struct avs_dev *adev) { - union avs_reply_msg msg; - u32 hipct, hipcte; + const struct avs_spec *spec = adev->spec; + u32 hipc_ack, hipc_rsp; - hipct = snd_hdac_adsp_readl(adev, SKL_ADSP_REG_HIPCT); - hipcte = snd_hdac_adsp_readl(adev, SKL_ADSP_REG_HIPCTE); + snd_hdac_adsp_updatel(adev, spec->hipc->ctl_offset, + AVS_ADSP_HIPCCTL_DONE | AVS_ADSP_HIPCCTL_BUSY, 0); - /* Ensure DSP sent new response to process. */ - if (!(hipct & SKL_ADSP_HIPCT_BUSY)) - return IRQ_NONE; + hipc_ack = snd_hdac_adsp_readl(adev, spec->hipc->ack_offset); + hipc_rsp = snd_hdac_adsp_readl(adev, spec->hipc->rsp_offset); - msg.primary = hipct; - msg.ext.val = hipcte; - avs_dsp_process_response(adev, msg.val); + /* DSP acked host's request. */ + if (hipc_ack & spec->hipc->ack_done_mask) { + complete(&adev->ipc->done_completion); - /* Tell DSP we accepted its message. */ - snd_hdac_adsp_updatel(adev, SKL_ADSP_REG_HIPCT, SKL_ADSP_HIPCT_BUSY, SKL_ADSP_HIPCT_BUSY); - /* Unmask busy interrupt. */ - snd_hdac_adsp_updatel(adev, SKL_ADSP_REG_HIPCCTL, AVS_ADSP_HIPCCTL_BUSY, - AVS_ADSP_HIPCCTL_BUSY); + /* Tell DSP it has our attention. */ + snd_hdac_adsp_updatel(adev, spec->hipc->ack_offset, spec->hipc->ack_done_mask, + spec->hipc->ack_done_mask); + } - return IRQ_HANDLED; + /* DSP sent new response to process */ + if (hipc_rsp & spec->hipc->rsp_busy_mask) { + union avs_reply_msg msg; + + msg.primary = snd_hdac_adsp_readl(adev, SKL_ADSP_REG_HIPCT); + msg.ext.val = snd_hdac_adsp_readl(adev, SKL_ADSP_REG_HIPCTE); + + avs_dsp_process_response(adev, msg.val); + + /* Tell DSP we accepted its message. */ + snd_hdac_adsp_updatel(adev, SKL_ADSP_REG_HIPCT, SKL_ADSP_HIPCT_BUSY, + SKL_ADSP_HIPCT_BUSY); + } + + snd_hdac_adsp_updatel(adev, spec->hipc->ctl_offset, + AVS_ADSP_HIPCCTL_DONE | AVS_ADSP_HIPCCTL_BUSY, + AVS_ADSP_HIPCCTL_DONE | AVS_ADSP_HIPCCTL_BUSY); +} + +static irqreturn_t avs_skl_dsp_interrupt(struct avs_dev *adev) +{ + u32 adspis = snd_hdac_adsp_readl(adev, AVS_ADSP_REG_ADSPIS); + irqreturn_t ret = IRQ_NONE; + + if (adspis == UINT_MAX) + return ret; + + if (adspis & AVS_ADSP_ADSPIS_CLDMA) { + hda_cldma_interrupt(&code_loader); + ret = IRQ_HANDLED; + } + + if (adspis & AVS_ADSP_ADSPIS_IPC) { + avs_skl_ipc_interrupt(adev); + ret = IRQ_HANDLED; + } + + return ret; } static int __maybe_unused @@ -128,8 +165,7 @@ const struct avs_dsp_ops avs_skl_dsp_ops = { .power = avs_dsp_core_power, .reset = avs_dsp_core_reset, .stall = avs_dsp_core_stall, - .irq_handler = avs_irq_handler, - .irq_thread = avs_skl_irq_thread, + .dsp_interrupt = avs_skl_dsp_interrupt, .int_control = avs_dsp_interrupt_control, .load_basefw = avs_cldma_load_basefw, .load_lib = avs_cldma_load_library, diff --git a/sound/soc/intel/avs/sysfs.c b/sound/soc/intel/avs/sysfs.c index cce21636fbc0..74b2e6f38d76 100644 --- a/sound/soc/intel/avs/sysfs.c +++ b/sound/soc/intel/avs/sysfs.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only // -// Copyright(c) 2021-2024 Intel Corporation. All rights reserved. +// Copyright(c) 2021-2024 Intel Corporation // // Authors: Cezary Rojewski <cezary.rojewski@intel.com> // Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> diff --git a/sound/soc/intel/avs/tgl.c b/sound/soc/intel/avs/tgl.c index 0e052e7f6bac..9dbb3ad0954a 100644 --- a/sound/soc/intel/avs/tgl.c +++ b/sound/soc/intel/avs/tgl.c @@ -1,12 +1,16 @@ // SPDX-License-Identifier: GPL-2.0-only // -// Copyright(c) 2021-2024 Intel Corporation. All rights reserved. +// Copyright(c) 2021-2024 Intel Corporation // // Authors: Cezary Rojewski <cezary.rojewski@intel.com> // Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> // +#include <linux/pci.h> #include "avs.h" +#include "messages.h" + +#define CPUID_TSC_LEAF 0x15 static int avs_tgl_dsp_core_power(struct avs_dev *adev, u32 core_mask, bool power) { @@ -35,16 +39,44 @@ static int avs_tgl_dsp_core_stall(struct avs_dev *adev, u32 core_mask, bool stal return avs_dsp_core_stall(adev, core_mask, stall); } +static int avs_tgl_config_basefw(struct avs_dev *adev) +{ + struct pci_dev *pci = adev->base.pci; + struct avs_bus_hwid hwid; + int ret; +#ifdef CONFIG_X86 + unsigned int ecx; + +#include <asm/cpuid/api.h> + ecx = cpuid_ecx(CPUID_TSC_LEAF); + if (ecx) { + ret = avs_ipc_set_fw_config(adev, 1, AVS_FW_CFG_XTAL_FREQ_HZ, sizeof(ecx), &ecx); + if (ret) + return AVS_IPC_RET(ret); + } +#endif + + hwid.device = pci->device; + hwid.subsystem = pci->subsystem_vendor | (pci->subsystem_device << 16); + hwid.revision = pci->revision; + + ret = avs_ipc_set_fw_config(adev, 1, AVS_FW_CFG_BUS_HARDWARE_ID, sizeof(hwid), &hwid); + if (ret) + return AVS_IPC_RET(ret); + + return 0; +} + const struct avs_dsp_ops avs_tgl_dsp_ops = { .power = avs_tgl_dsp_core_power, .reset = avs_tgl_dsp_core_reset, .stall = avs_tgl_dsp_core_stall, - .irq_handler = avs_irq_handler, - .irq_thread = avs_cnl_irq_thread, + .dsp_interrupt = avs_cnl_dsp_interrupt, .int_control = avs_dsp_interrupt_control, .load_basefw = avs_icl_load_basefw, .load_lib = avs_hda_load_library, .transfer_mods = avs_hda_transfer_modules, + .config_basefw = avs_tgl_config_basefw, .log_buffer_offset = avs_icl_log_buffer_offset, .log_buffer_status = avs_apl_log_buffer_status, .coredump = avs_apl_coredump, diff --git a/sound/soc/intel/avs/topology.c b/sound/soc/intel/avs/topology.c index 13061bd1488b..f2e4ad8b8e14 100644 --- a/sound/soc/intel/avs/topology.c +++ b/sound/soc/intel/avs/topology.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only // -// Copyright(c) 2021 Intel Corporation. All rights reserved. +// Copyright(c) 2021 Intel Corporation // // Authors: Cezary Rojewski <cezary.rojewski@intel.com> // Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> @@ -815,6 +815,66 @@ static const struct avs_tplg_token_parser modcfg_ext_parsers[] = { .offset = offsetof(struct avs_tplg_modcfg_ext, generic.num_output_pins), .parse = avs_parse_short_token, }, + { + .token = AVS_TKN_MODCFG_WHM_REF_AFMT_ID_U32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = offsetof(struct avs_tplg_modcfg_ext, whm.ref_fmt), + .parse = avs_parse_audio_format_ptr, + }, + { + .token = AVS_TKN_MODCFG_WHM_OUT_AFMT_ID_U32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = offsetof(struct avs_tplg_modcfg_ext, whm.out_fmt), + .parse = avs_parse_audio_format_ptr, + }, + { + .token = AVS_TKN_MODCFG_WHM_WAKE_TICK_PERIOD_U32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = offsetof(struct avs_tplg_modcfg_ext, whm.wake_tick_period), + .parse = avs_parse_word_token, + }, + { + .token = AVS_TKN_MODCFG_WHM_VINDEX_U8, + .type = SND_SOC_TPLG_TUPLE_TYPE_BYTE, + .offset = offsetof(struct avs_tplg_modcfg_ext, whm.vindex), + .parse = avs_parse_byte_token, + }, + { + .token = AVS_TKN_MODCFG_WHM_DMA_TYPE_U32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = offsetof(struct avs_tplg_modcfg_ext, whm.dma_type), + .parse = avs_parse_word_token, + }, + { + .token = AVS_TKN_MODCFG_WHM_DMABUFF_SIZE_U32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = offsetof(struct avs_tplg_modcfg_ext, whm.dma_buffer_size), + .parse = avs_parse_word_token, + }, + { + .token = AVS_TKN_MODCFG_WHM_BLOB_AFMT_ID_U32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = offsetof(struct avs_tplg_modcfg_ext, whm.blob_fmt), + .parse = avs_parse_audio_format_ptr, + }, + { + .token = AVS_TKN_MODCFG_PEAKVOL_VOLUME_U32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = offsetof(struct avs_tplg_modcfg_ext, peakvol.target_volume), + .parse = avs_parse_word_token, + }, + { + .token = AVS_TKN_MODCFG_PEAKVOL_CURVE_TYPE_U32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = offsetof(struct avs_tplg_modcfg_ext, peakvol.curve_type), + .parse = avs_parse_word_token, + }, + { + .token = AVS_TKN_MODCFG_PEAKVOL_CURVE_DURATION_U32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = offsetof(struct avs_tplg_modcfg_ext, peakvol.curve_duration), + .parse = avs_parse_word_token, + }, }; static const struct avs_tplg_token_parser pin_format_parsers[] = { @@ -1466,7 +1526,7 @@ avs_tplg_path_template_create(struct snd_soc_component *comp, struct avs_tplg *o static const struct avs_tplg_token_parser mod_init_config_parsers[] = { { - .token = AVS_TKN_MOD_INIT_CONFIG_ID_U32, + .token = AVS_TKN_INIT_CONFIG_ID_U32, .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, .offset = offsetof(struct avs_tplg_init_config, id), .parse = avs_parse_word_token, @@ -1519,7 +1579,7 @@ static int avs_tplg_parse_initial_configs(struct snd_soc_component *comp, esize = le32_to_cpu(tuples->size) + le32_to_cpu(tmp->size); ret = parse_dictionary_entries(comp, tuples, esize, config, 1, sizeof(*config), - AVS_TKN_MOD_INIT_CONFIG_ID_U32, + AVS_TKN_INIT_CONFIG_ID_U32, mod_init_config_parsers, ARRAY_SIZE(mod_init_config_parsers)); @@ -1545,8 +1605,8 @@ static int avs_route_load(struct snd_soc_component *comp, int index, { struct snd_soc_acpi_mach *mach = dev_get_platdata(comp->card->dev); size_t len = SNDRV_CTL_ELEM_ID_NAME_MAXLEN; - char buf[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; int ssp_port, tdm_slot; + char *buf; /* See parse_link_formatted_string() for dynamic naming when(s). */ if (!avs_mach_singular_ssp(mach)) @@ -1557,13 +1617,24 @@ static int avs_route_load(struct snd_soc_component *comp, int index, return 0; tdm_slot = avs_mach_ssp_tdm(mach, ssp_port); + buf = devm_kzalloc(comp->card->dev, len, GFP_KERNEL); + if (!buf) + return -ENOMEM; avs_ssp_sprint(buf, len, route->source, ssp_port, tdm_slot); - strscpy((char *)route->source, buf, len); + route->source = buf; + + buf = devm_kzalloc(comp->card->dev, len, GFP_KERNEL); + if (!buf) + return -ENOMEM; avs_ssp_sprint(buf, len, route->sink, ssp_port, tdm_slot); - strscpy((char *)route->sink, buf, len); + route->sink = buf; + if (route->control) { + buf = devm_kzalloc(comp->card->dev, len, GFP_KERNEL); + if (!buf) + return -ENOMEM; avs_ssp_sprint(buf, len, route->control, ssp_port, tdm_slot); - strscpy((char *)route->control, buf, len); + route->control = buf; } return 0; @@ -1582,6 +1653,8 @@ static int avs_widget_load(struct snd_soc_component *comp, int index, if (!le32_to_cpu(dw->priv.size)) return 0; + w->no_wname_in_kcontrol_name = true; + if (w->ignore_suspend && !AVS_S0IX_SUPPORTED) { dev_info_once(comp->dev, "Device does not support S0IX, check BIOS settings\n"); w->ignore_suspend = false; @@ -1595,8 +1668,8 @@ static int avs_widget_load(struct snd_soc_component *comp, int index, /* See parse_link_formatted_string() for dynamic naming when(s). */ if (avs_mach_singular_tdm(mach, ssp_port)) { - /* size is based on possible %d -> SSP:TDM, where SSP and TDM < 10 + '\0' */ - size_t size = strlen(dw->name) + 2; + /* size is based on possible %d -> SSP:TDM, where SSP and TDM < 16 + '\0' */ + size_t size = strlen(dw->name) + 3; char *buf; tdm_slot = avs_mach_ssp_tdm(mach, ssp_port); @@ -1832,13 +1905,23 @@ static int avs_manifest(struct snd_soc_component *comp, int index, return 0; } -#define AVS_CONTROL_OPS_VOLUME 257 +enum { + AVS_CONTROL_OPS_VOLUME = 257, + AVS_CONTROL_OPS_MUTE, +}; static const struct snd_soc_tplg_kcontrol_ops avs_control_ops[] = { { .id = AVS_CONTROL_OPS_VOLUME, .get = avs_control_volume_get, .put = avs_control_volume_put, + .info = avs_control_volume_info, + }, + { + .id = AVS_CONTROL_OPS_MUTE, + .get = avs_control_mute_get, + .put = avs_control_mute_put, + .info = avs_control_mute_info, }, }; @@ -1860,18 +1943,20 @@ avs_control_load(struct snd_soc_component *comp, int index, struct snd_kcontrol_ struct avs_control_data *ctl_data; struct soc_mixer_control *mc; size_t block_size; - int ret; + int ret, i; switch (le32_to_cpu(hdr->type)) { case SND_SOC_TPLG_TYPE_MIXER: - tmc = container_of(hdr, typeof(*tmc), hdr); - tuples = tmc->priv.array; - block_size = le32_to_cpu(tmc->priv.size); break; default: return -EINVAL; } + mc = (struct soc_mixer_control *)ctmpl->private_value; + tmc = container_of(hdr, typeof(*tmc), hdr); + tuples = tmc->priv.array; + block_size = le32_to_cpu(tmc->priv.size); + ctl_data = devm_kzalloc(comp->card->dev, sizeof(*ctl_data), GFP_KERNEL); if (!ctl_data) return -ENOMEM; @@ -1882,12 +1967,17 @@ avs_control_load(struct snd_soc_component *comp, int index, struct snd_kcontrol_ if (ret) return ret; - mc = (struct soc_mixer_control *)ctmpl->private_value; mc->dobj.private = ctl_data; + if (tmc->invert) { + ctl_data->values[0] = mc->max; + for (i = 1; i < mc->num_channels; i++) + ctl_data->values[i] = mc->max; + } + return 0; } -static struct snd_soc_tplg_ops avs_tplg_ops = { +static const struct snd_soc_tplg_ops avs_tplg_ops = { .io_ops = avs_control_ops, .io_ops_count = ARRAY_SIZE(avs_control_ops), .control_load = avs_control_load, diff --git a/sound/soc/intel/avs/topology.h b/sound/soc/intel/avs/topology.h index 6a59dd766603..f5601a4e3ec8 100644 --- a/sound/soc/intel/avs/topology.h +++ b/sound/soc/intel/avs/topology.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright(c) 2021 Intel Corporation. All rights reserved. + * Copyright(c) 2021 Intel Corporation * * Authors: Cezary Rojewski <cezary.rojewski@intel.com> * Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> @@ -74,13 +74,20 @@ struct avs_tplg_modcfg_ext { union avs_virtual_index vindex; u32 dma_type; u32 dma_buffer_size; - u32 config_length; - /* config_data part of priv data */ } copier; struct { + struct avs_audio_format *ref_fmt; + struct avs_audio_format *out_fmt; + u32 wake_tick_period; + union avs_virtual_index vindex; + u32 dma_type; + u32 dma_buffer_size; + struct avs_audio_format *blob_fmt; /* optional override */ + } whm; + struct { u32 out_channel_config; u32 coefficients_select; - s32 coefficients[AVS_CHANNELS_MAX]; + s32 coefficients[AVS_COEFF_CHANNELS_MAX]; u32 channel_map; } updown_mix; struct { @@ -106,6 +113,11 @@ struct avs_tplg_modcfg_ext { struct { struct avs_audio_format *out_fmt; } micsel; + struct { + u32 target_volume; + u32 curve_type; + u32 curve_duration; + } peakvol; }; }; diff --git a/sound/soc/intel/avs/trace.c b/sound/soc/intel/avs/trace.c index c63eea909b5e..a98da521db0f 100644 --- a/sound/soc/intel/avs/trace.c +++ b/sound/soc/intel/avs/trace.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only // -// Copyright(c) 2021-2022 Intel Corporation. All rights reserved. +// Copyright(c) 2021-2022 Intel Corporation // // Author: Cezary Rojewski <cezary.rojewski@intel.com> // Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> diff --git a/sound/soc/intel/avs/trace.h b/sound/soc/intel/avs/trace.h index 855b06bb14b0..f4288d0ad5ef 100644 --- a/sound/soc/intel/avs/trace.h +++ b/sound/soc/intel/avs/trace.h @@ -24,7 +24,7 @@ TRACE_EVENT(avs_dsp_core_op, TP_fast_assign( __entry->reg = reg; __entry->mask = mask; - __assign_str(op, op); + __assign_str(op); __entry->flag = flag; ), @@ -37,60 +37,62 @@ TRACE_EVENT(avs_dsp_core_op, void trace_avs_msg_payload(const void *data, size_t size); -#define trace_avs_request(msg, fwregs) \ +#define trace_avs_request(msg, sts, lec) \ ({ \ - trace_avs_ipc_request_msg((msg)->header, fwregs); \ + trace_avs_ipc_request_msg((msg)->header, sts, lec); \ trace_avs_msg_payload((msg)->data, (msg)->size); \ }) -#define trace_avs_reply(msg, fwregs) \ +#define trace_avs_reply(msg, sts, lec) \ ({ \ - trace_avs_ipc_reply_msg((msg)->header, fwregs); \ + trace_avs_ipc_reply_msg((msg)->header, sts, lec); \ trace_avs_msg_payload((msg)->data, (msg)->size); \ }) -#define trace_avs_notify(msg, fwregs) \ +#define trace_avs_notify(msg, sts, lec) \ ({ \ - trace_avs_ipc_notify_msg((msg)->header, fwregs); \ + trace_avs_ipc_notify_msg((msg)->header, sts, lec); \ trace_avs_msg_payload((msg)->data, (msg)->size); \ }) #endif DECLARE_EVENT_CLASS(avs_ipc_msg_hdr, - TP_PROTO(u64 header, u64 fwregs), + TP_PROTO(u64 header, u32 sts, u32 lec), - TP_ARGS(header, fwregs), + TP_ARGS(header, sts, lec), TP_STRUCT__entry( __field(u64, header) - __field(u64, fwregs) + __field(u32, sts) + __field(u32, lec) ), TP_fast_assign( __entry->header = header; - __entry->fwregs = fwregs; + __entry->sts = sts; + __entry->lec = lec; ), TP_printk("primary: 0x%08X, extension: 0x%08X,\n" - "fwstatus: 0x%08X, fwerror: 0x%08X", + "status: 0x%08X, error: 0x%08X", lower_32_bits(__entry->header), upper_32_bits(__entry->header), - lower_32_bits(__entry->fwregs), upper_32_bits(__entry->fwregs)) + __entry->sts, __entry->lec) ); DEFINE_EVENT(avs_ipc_msg_hdr, avs_ipc_request_msg, - TP_PROTO(u64 header, u64 fwregs), - TP_ARGS(header, fwregs) + TP_PROTO(u64 header, u32 sts, u32 lec), + TP_ARGS(header, sts, lec) ); DEFINE_EVENT(avs_ipc_msg_hdr, avs_ipc_reply_msg, - TP_PROTO(u64 header, u64 fwregs), - TP_ARGS(header, fwregs) + TP_PROTO(u64 header, u32 sts, u32 lec), + TP_ARGS(header, sts, lec) ); DEFINE_EVENT(avs_ipc_msg_hdr, avs_ipc_notify_msg, - TP_PROTO(u64 header, u64 fwregs), - TP_ARGS(header, fwregs) + TP_PROTO(u64 header, u32 sts, u32 lec), + TP_ARGS(header, sts, lec) ); TRACE_EVENT_CONDITION(avs_ipc_msg_payload, @@ -135,7 +137,7 @@ TRACE_EVENT(avs_d0ix, ), TP_fast_assign( - __assign_str(op, op); + __assign_str(op); __entry->proceed = proceed; __entry->header = header; ), diff --git a/sound/soc/intel/avs/utils.c b/sound/soc/intel/avs/utils.c index 82416b86662d..81f9b67d8e29 100644 --- a/sound/soc/intel/avs/utils.c +++ b/sound/soc/intel/avs/utils.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only // -// Copyright(c) 2021-2022 Intel Corporation. All rights reserved. +// Copyright(c) 2021-2022 Intel Corporation // // Authors: Cezary Rojewski <cezary.rojewski@intel.com> // Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> @@ -250,7 +250,7 @@ int avs_request_firmware(struct avs_dev *adev, const struct firmware **fw_p, con if (!entry) return -ENOMEM; - entry->name = kstrdup(name, GFP_KERNEL); + entry->name = kstrdup_const(name, GFP_KERNEL); if (!entry->name) { kfree(entry); return -ENOMEM; @@ -258,7 +258,7 @@ int avs_request_firmware(struct avs_dev *adev, const struct firmware **fw_p, con ret = request_firmware(&entry->fw, name, adev->dev); if (ret < 0) { - kfree(entry->name); + kfree_const(entry->name); kfree(entry); return ret; } @@ -282,7 +282,7 @@ void avs_release_last_firmware(struct avs_dev *adev) list_del(&entry->node); release_firmware(entry->fw); - kfree(entry->name); + kfree_const(entry->name); kfree(entry); } @@ -296,7 +296,7 @@ void avs_release_firmwares(struct avs_dev *adev) list_for_each_entry_safe(entry, tmp, &adev->fw_list, node) { list_del(&entry->node); release_firmware(entry->fw); - kfree(entry->name); + kfree_const(entry->name); kfree(entry); } } diff --git a/sound/soc/intel/avs/utils.h b/sound/soc/intel/avs/utils.h index 0b82a98ed024..955a40d2c30c 100644 --- a/sound/soc/intel/avs/utils.h +++ b/sound/soc/intel/avs/utils.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright(c) 2023 Intel Corporation. All rights reserved. + * Copyright(c) 2023 Intel Corporation * * Authors: Cezary Rojewski <cezary.rojewski@intel.com> * Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> @@ -11,6 +11,16 @@ #include <sound/soc-acpi.h> +extern bool obsolete_card_names; + +struct avs_mach_pdata { + struct hda_codec *codec; + unsigned long *tdms; + char *codec_name; /* DMIC only */ + + bool obsolete_card_names; +}; + static inline bool avs_mach_singular_ssp(struct snd_soc_acpi_mach *mach) { return hweight_long(mach->mach_params.i2s_link_mask) == 1; @@ -23,14 +33,16 @@ static inline u32 avs_mach_ssp_port(struct snd_soc_acpi_mach *mach) static inline bool avs_mach_singular_tdm(struct snd_soc_acpi_mach *mach, u32 port) { - unsigned long *tdms = mach->pdata; + struct avs_mach_pdata *pdata = mach->pdata; + unsigned long *tdms = pdata->tdms; return !tdms || (hweight_long(tdms[port]) == 1); } static inline u32 avs_mach_ssp_tdm(struct snd_soc_acpi_mach *mach, u32 port) { - unsigned long *tdms = mach->pdata; + struct avs_mach_pdata *pdata = mach->pdata; + unsigned long *tdms = pdata->tdms; return tdms ? __ffs(tdms[port]) : 0; } |