summaryrefslogtreecommitdiff
path: root/sound/soc/intel/avs
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/intel/avs')
-rw-r--r--sound/soc/intel/avs/Makefile13
-rw-r--r--sound/soc/intel/avs/apl.c80
-rw-r--r--sound/soc/intel/avs/avs.h194
-rw-r--r--sound/soc/intel/avs/board_selection.c449
-rw-r--r--sound/soc/intel/avs/boards/Kconfig40
-rw-r--r--sound/soc/intel/avs/boards/Makefile38
-rw-r--r--sound/soc/intel/avs/boards/da7219.c81
-rw-r--r--sound/soc/intel/avs/boards/dmic.c105
-rw-r--r--sound/soc/intel/avs/boards/es8336.c82
-rw-r--r--sound/soc/intel/avs/boards/hdaudio.c67
-rw-r--r--sound/soc/intel/avs/boards/i2s_test.c139
-rw-r--r--sound/soc/intel/avs/boards/max98357a.c55
-rw-r--r--sound/soc/intel/avs/boards/max98373.c57
-rw-r--r--sound/soc/intel/avs/boards/max98927.c59
-rw-r--r--sound/soc/intel/avs/boards/nau8825.c73
-rw-r--r--sound/soc/intel/avs/boards/pcm3168a.c155
-rw-r--r--sound/soc/intel/avs/boards/probe.c67
-rw-r--r--sound/soc/intel/avs/boards/rt274.c73
-rw-r--r--sound/soc/intel/avs/boards/rt286.c73
-rw-r--r--sound/soc/intel/avs/boards/rt298.c74
-rw-r--r--sound/soc/intel/avs/boards/rt5514.c197
-rw-r--r--sound/soc/intel/avs/boards/rt5640.c271
-rw-r--r--sound/soc/intel/avs/boards/rt5663.c66
-rw-r--r--sound/soc/intel/avs/boards/rt5682.c66
-rw-r--r--sound/soc/intel/avs/boards/ssm4567.c64
-rw-r--r--sound/soc/intel/avs/cldma.c46
-rw-r--r--sound/soc/intel/avs/cldma.h7
-rw-r--r--sound/soc/intel/avs/cnl.c92
-rw-r--r--sound/soc/intel/avs/control.c191
-rw-r--r--sound/soc/intel/avs/control.h14
-rw-r--r--sound/soc/intel/avs/core.c398
-rw-r--r--sound/soc/intel/avs/debug.h91
-rw-r--r--sound/soc/intel/avs/debugfs.c34
-rw-r--r--sound/soc/intel/avs/dsp.c7
-rw-r--r--sound/soc/intel/avs/icl.c203
-rw-r--r--sound/soc/intel/avs/ipc.c177
-rw-r--r--sound/soc/intel/avs/lnl.c28
-rw-r--r--sound/soc/intel/avs/loader.c118
-rw-r--r--sound/soc/intel/avs/messages.c286
-rw-r--r--sound/soc/intel/avs/messages.h174
-rw-r--r--sound/soc/intel/avs/mtl.c201
-rw-r--r--sound/soc/intel/avs/path.c811
-rw-r--r--sound/soc/intel/avs/path.h25
-rw-r--r--sound/soc/intel/avs/pcm.c733
-rw-r--r--sound/soc/intel/avs/pcm.h16
-rw-r--r--sound/soc/intel/avs/probes.c62
-rw-r--r--sound/soc/intel/avs/ptl.c99
-rw-r--r--sound/soc/intel/avs/registers.h112
-rw-r--r--sound/soc/intel/avs/skl.c98
-rw-r--r--sound/soc/intel/avs/sysfs.c35
-rw-r--r--sound/soc/intel/avs/tgl.c87
-rw-r--r--sound/soc/intel/avs/topology.c604
-rw-r--r--sound/soc/intel/avs/topology.h49
-rw-r--r--sound/soc/intel/avs/trace.c2
-rw-r--r--sound/soc/intel/avs/trace.h42
-rw-r--r--sound/soc/intel/avs/utils.c10
-rw-r--r--sound/soc/intel/avs/utils.h77
57 files changed, 5749 insertions, 1818 deletions
diff --git a/sound/soc/intel/avs/Makefile b/sound/soc/intel/avs/Makefile
index 460ee6599daf..576dc0da381d 100644
--- a/sound/soc/intel/avs/Makefile
+++ b/sound/soc/intel/avs/Makefile
@@ -1,16 +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
-snd-soc-avs-objs += cldma.o
-snd-soc-avs-objs += skl.o apl.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 1860099c782a..b922eeaba843 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,16 +8,35 @@
#include <linux/devcoredump.h>
#include <linux/slab.h>
+#include <sound/hdaudio_ext.h>
#include "avs.h"
+#include "debug.h"
#include "messages.h"
#include "path.h"
+#include "registers.h"
#include "topology.h"
-static int __maybe_unused
-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)
+static irqreturn_t avs_apl_dsp_interrupt(struct avs_dev *adev)
{
- struct apl_log_state_info *info;
+ 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)
+{
+ struct avs_apl_log_state_info *info;
u32 size, num_cores = adev->hw_cfg.dsp_cores;
int ret, i;
@@ -47,10 +66,11 @@ apl_enable_logs(struct avs_dev *adev, enum avs_log_enable enable, u32 aging_peri
return 0;
}
+#endif
-static int apl_log_buffer_status(struct avs_dev *adev, union avs_notify_msg *msg)
+int avs_apl_log_buffer_status(struct avs_dev *adev, union avs_notify_msg *msg)
{
- struct apl_log_buffer_layout layout;
+ struct avs_apl_log_buffer_layout layout;
void __iomem *addr, *buf;
addr = avs_log_buffer_addr(adev, msg->log.core);
@@ -63,11 +83,11 @@ static int apl_log_buffer_status(struct avs_dev *adev, union avs_notify_msg *msg
/* consume the logs regardless of consumer presence */
goto update_read_ptr;
- buf = apl_log_payload_addr(addr);
+ buf = avs_apl_log_payload_addr(addr);
if (layout.read_ptr > layout.write_ptr) {
avs_dump_fw_log(adev, buf + layout.read_ptr,
- apl_log_payload_size(adev) - layout.read_ptr);
+ avs_apl_log_payload_size(adev) - layout.read_ptr);
layout.read_ptr = 0;
}
avs_dump_fw_log_wakeup(adev, buf + layout.read_ptr, layout.write_ptr - layout.read_ptr);
@@ -77,7 +97,8 @@ update_read_ptr:
return 0;
}
-static int apl_wait_log_entry(struct avs_dev *adev, u32 core, struct apl_log_buffer_layout *layout)
+static int avs_apl_wait_log_entry(struct avs_dev *adev, u32 core,
+ struct avs_apl_log_buffer_layout *layout)
{
unsigned long timeout;
void __iomem *addr;
@@ -99,14 +120,14 @@ static int apl_wait_log_entry(struct avs_dev *adev, u32 core, struct apl_log_buf
}
/* reads log header and tests its type */
-#define apl_is_entry_stackdump(addr) ((readl(addr) >> 30) & 0x1)
+#define avs_apl_is_entry_stackdump(addr) ((readl(addr) >> 30) & 0x1)
-static int apl_coredump(struct avs_dev *adev, union avs_notify_msg *msg)
+int avs_apl_coredump(struct avs_dev *adev, union avs_notify_msg *msg)
{
- struct apl_log_buffer_layout layout;
+ 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;
@@ -124,9 +145,9 @@ static int apl_coredump(struct avs_dev *adev, union avs_notify_msg *msg)
if (!addr)
goto exit;
- buf = apl_log_payload_addr(addr);
+ buf = avs_apl_log_payload_addr(addr);
memcpy_fromio(&layout, addr, sizeof(layout));
- if (!apl_is_entry_stackdump(buf + layout.read_ptr)) {
+ if (!avs_apl_is_entry_stackdump(buf + layout.read_ptr)) {
union avs_notify_msg lbs_msg = AVS_NOTIFICATION(LOG_BUFFER_STATUS);
/*
@@ -142,11 +163,11 @@ static int apl_coredump(struct avs_dev *adev, union avs_notify_msg *msg)
do {
u32 count;
- if (apl_wait_log_entry(adev, msg->ext.coredump.core_id, &layout))
+ if (avs_apl_wait_log_entry(adev, msg->ext.coredump.core_id, &layout))
break;
if (layout.read_ptr > layout.write_ptr) {
- count = apl_log_payload_size(adev) - layout.read_ptr;
+ count = avs_apl_log_payload_size(adev) - layout.read_ptr;
memcpy_fromio(pos + offset, buf + layout.read_ptr, count);
layout.read_ptr = 0;
offset += count;
@@ -165,7 +186,7 @@ exit:
return 0;
}
-static bool apl_lp_streaming(struct avs_dev *adev)
+static bool avs_apl_lp_streaming(struct avs_dev *adev)
{
struct avs_path *path;
@@ -201,7 +222,7 @@ static bool apl_lp_streaming(struct avs_dev *adev)
return true;
}
-static bool apl_d0ix_toggle(struct avs_dev *adev, struct avs_ipc_msg *tx, bool wake)
+bool avs_apl_d0ix_toggle(struct avs_dev *adev, struct avs_ipc_msg *tx, bool wake)
{
/* wake in all cases */
if (wake)
@@ -215,10 +236,10 @@ static bool apl_d0ix_toggle(struct avs_dev *adev, struct avs_ipc_msg *tx, bool w
* Note: for cAVS 1.5+ and 1.8, D0IX is LP-firmware transition,
* not the power-gating mechanism known from cAVS 2.0.
*/
- return apl_lp_streaming(adev);
+ return avs_apl_lp_streaming(adev);
}
-static int apl_set_d0ix(struct avs_dev *adev, bool enable)
+int avs_apl_set_d0ix(struct avs_dev *adev, bool enable)
{
bool streaming = false;
int ret;
@@ -231,20 +252,19 @@ static int apl_set_d0ix(struct avs_dev *adev, bool enable)
return AVS_IPC_RET(ret);
}
-const struct avs_dsp_ops apl_dsp_ops = {
+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_dsp_irq_handler,
- .irq_thread = avs_dsp_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,
.transfer_mods = avs_hda_transfer_modules,
- .log_buffer_offset = skl_log_buffer_offset,
- .log_buffer_status = apl_log_buffer_status,
- .coredump = apl_coredump,
- .d0ix_toggle = apl_d0ix_toggle,
- .set_d0ix = apl_set_d0ix,
+ .log_buffer_offset = avs_skl_log_buffer_offset,
+ .log_buffer_status = avs_apl_log_buffer_status,
+ .coredump = avs_apl_coredump,
+ .d0ix_toggle = avs_apl_d0ix_toggle,
+ .set_d0ix = avs_apl_set_d0ix,
AVS_SET_ENABLE_LOGS_OP(apl)
};
diff --git a/sound/soc/intel/avs/avs.h b/sound/soc/intel/avs/avs.h
index 0cf38c9e768e..0f8ddd0e9e5f 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>
@@ -22,7 +22,6 @@
struct avs_dev;
struct avs_tplg;
struct avs_tplg_library;
-struct avs_soc_component;
struct avs_ipc_msg;
#ifdef CONFIG_ACPI
@@ -46,12 +45,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)(int, void *);
- irqreturn_t (* const irq_thread)(int, void *);
+ 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);
@@ -64,15 +63,38 @@ struct avs_dsp_ops {
#define avs_dsp_op(adev, op, ...) \
((adev)->spec->dsp_ops->op(adev, ## __VA_ARGS__))
-extern const struct avs_dsp_ops skl_dsp_ops;
-extern const struct avs_dsp_ops apl_dsp_ops;
+extern const struct avs_dsp_ops avs_skl_dsp_ops;
+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)
+struct avs_sram_spec {
+ const u32 base_offset;
+ const u32 window_size;
+};
+
+struct avs_hipc_spec {
+ const u32 req_offset;
+ const u32 req_ext_offset;
+ const u32 req_busy_mask;
+ const u32 ack_offset;
+ const u32 ack_done_mask;
+ const u32 rsp_offset;
+ const u32 rsp_busy_mask;
+ const u32 ctl_offset;
+ const u32 sts_offset;
+};
+
/* Platform specific descriptor */
struct avs_spec {
const char *name;
@@ -82,13 +104,12 @@ struct avs_spec {
const u32 core_init_mask; /* used during DSP boot */
const u64 attributes; /* bitmask of AVS_PLATATTR_* */
- const u32 sram_base_offset;
- const u32 sram_window_size;
- const u32 rom_status;
+ const struct avs_sram_spec *sram;
+ const struct avs_hipc_spec *hipc;
};
struct avs_fw_entry {
- char *name;
+ const char *name;
const struct firmware *fw;
struct list_head node;
@@ -121,16 +142,17 @@ struct avs_dev {
struct avs_mods_info *mods_info;
struct ida **mod_idas;
struct mutex modres_mutex;
+ void *modcfg_buf; /* module configuration buffer */
struct ida ppl_ida;
struct list_head fw_list;
int *core_refs; /* reference count per core */
char **lib_names;
int num_lp_paths;
+ atomic_t l1sen_counter; /* controls whether L1SEN should be disabled */
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;
@@ -224,39 +246,20 @@ struct avs_ipc {
#define AVS_IPC_RET(ret) \
(((ret) <= 0) ? (ret) : -AVS_EIPC)
-static inline void avs_ipc_err(struct avs_dev *adev, struct avs_ipc_msg *tx,
- const char *name, int error)
-{
- /*
- * If IPC channel is blocked e.g.: due to ongoing recovery,
- * -EPERM error code is expected and thus it's not an actual error.
- *
- * Unsupported IPCs are of no harm either.
- */
- if (error == -EPERM || error == AVS_IPC_NOT_SUPPORTED)
- dev_dbg(adev->dev, "%s 0x%08x 0x%08x failed: %d\n", name,
- tx->glb.primary, tx->glb.ext.val, error);
- else
- dev_err(adev->dev, "%s 0x%08x 0x%08x failed: %d\n", name,
- tx->glb.primary, tx->glb.ext.val, error);
-}
-
-irqreturn_t avs_dsp_irq_handler(int irq, void *dev_id);
-irqreturn_t avs_dsp_irq_thread(int irq, void *dev_id);
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);
-int avs_dsp_send_msg(struct avs_dev *adev,
- struct avs_ipc_msg *request, struct avs_ipc_msg *reply);
+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);
+int avs_dsp_send_msg(struct avs_dev *adev, struct avs_ipc_msg *request,
+ struct avs_ipc_msg *reply, const char *name);
/* Two variants below are for messages that control DSP power states. */
int avs_dsp_send_pm_msg_timeout(struct avs_dev *adev, struct avs_ipc_msg *request,
- struct avs_ipc_msg *reply, int timeout, bool wake_d0i0);
+ struct avs_ipc_msg *reply, int timeout, bool wake_d0i0,
+ const char *name);
int avs_dsp_send_pm_msg(struct avs_dev *adev, struct avs_ipc_msg *request,
- struct avs_ipc_msg *reply, bool wake_d0i0);
-int avs_dsp_send_rom_msg_timeout(struct avs_dev *adev,
- struct avs_ipc_msg *request, int timeout);
-int avs_dsp_send_rom_msg(struct avs_dev *adev, struct avs_ipc_msg *request);
+ struct avs_ipc_msg *reply, bool wake_d0i0, const char *name);
+int avs_dsp_send_rom_msg_timeout(struct avs_dev *adev, struct avs_ipc_msg *request, int timeout,
+ const char *name);
+int avs_dsp_send_rom_msg(struct avs_dev *adev, struct avs_ipc_msg *request, const char *name);
void avs_dsp_interrupt_control(struct avs_dev *adev, bool enable);
int avs_ipc_init(struct avs_ipc *ipc, struct device *dev);
void avs_ipc_block(struct avs_ipc *ipc);
@@ -264,7 +267,26 @@ 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);
-int skl_log_buffer_offset(struct avs_dev *adev, u32 core);
+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,
+ u32 fifo_full_period, unsigned long resource_mask, u32 *priorities);
+int avs_skl_log_buffer_offset(struct avs_dev *adev, u32 core);
+int avs_icl_log_buffer_offset(struct avs_dev *adev, u32 core);
+int avs_apl_log_buffer_status(struct avs_dev *adev, union avs_notify_msg *msg);
+int avs_apl_coredump(struct avs_dev *adev, union avs_notify_msg *msg);
+bool avs_apl_d0ix_toggle(struct avs_dev *adev, struct avs_ipc_msg *tx, bool wake);
+bool avs_icl_d0ix_toggle(struct avs_dev *adev, struct avs_ipc_msg *tx, bool wake);
+int avs_apl_set_d0ix(struct avs_dev *adev, bool enable);
+int avs_icl_set_d0ix(struct avs_dev *adev, bool enable);
/* Firmware resources management */
@@ -309,6 +331,8 @@ int avs_hda_load_library(struct avs_dev *adev, struct firmware *lib, u32 id);
int avs_hda_transfer_modules(struct avs_dev *adev, bool load,
struct avs_module_entry *mods, u32 num_mods);
+int avs_icl_load_basefw(struct avs_dev *adev, struct firmware *fw);
+
/* Soc component members */
struct avs_soc_component {
@@ -323,89 +347,21 @@ 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_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,
- unsigned long *tdms);
-int avs_hda_platform_register(struct avs_dev *adev, const char *name);
+int avs_register_dmic_component(struct avs_dev *adev, const char *name);
+int avs_register_i2s_component(struct avs_dev *adev, const char *name, unsigned long port_mask,
+ unsigned long *tdms);
+int avs_register_hda_component(struct avs_dev *adev, const char *name);
+int avs_register_component(struct device *dev, const char *name,
+ struct snd_soc_component_driver *drv,
+ struct snd_soc_dai_driver *cpu_dais, int num_cpu_dais);
int avs_register_all_boards(struct avs_dev *adev);
void avs_unregister_all_boards(struct avs_dev *adev);
-/* Firmware tracing helpers */
-
-#define avs_log_buffer_size(adev) \
- ((adev)->fw_cfg.trace_log_bytes / (adev)->hw_cfg.dsp_cores)
-
-#define avs_log_buffer_addr(adev, core) \
-({ \
- s32 __offset = avs_dsp_op(adev, log_buffer_offset, core); \
- (__offset < 0) ? NULL : \
- (avs_sram_addr(adev, AVS_DEBUG_WINDOW) + __offset); \
-})
-
-static inline int avs_log_buffer_status_locked(struct avs_dev *adev, union avs_notify_msg *msg)
-{
- unsigned long flags;
- int ret;
-
- spin_lock_irqsave(&adev->trace_lock, flags);
- ret = avs_dsp_op(adev, log_buffer_status, msg);
- spin_unlock_irqrestore(&adev->trace_lock, flags);
-
- return ret;
-}
+int avs_parse_sched_cfg(struct avs_dev *adev, const char *buf, size_t len);
-struct apl_log_buffer_layout {
- u32 read_ptr;
- u32 write_ptr;
- u8 buffer[];
-} __packed;
+/* Filesystems integration */
-#define apl_log_payload_size(adev) \
- (avs_log_buffer_size(adev) - sizeof(struct apl_log_buffer_layout))
-
-#define apl_log_payload_addr(addr) \
- (addr + sizeof(struct apl_log_buffer_layout))
-
-#ifdef CONFIG_DEBUG_FS
-#define AVS_SET_ENABLE_LOGS_OP(name) \
- .enable_logs = name##_enable_logs
-
-bool avs_logging_fw(struct avs_dev *adev);
-void avs_dump_fw_log(struct avs_dev *adev, const void __iomem *src, unsigned int len);
-void avs_dump_fw_log_wakeup(struct avs_dev *adev, const void __iomem *src, unsigned int len);
-
-int avs_probe_platform_register(struct avs_dev *adev, const char *name);
-
-void avs_debugfs_init(struct avs_dev *adev);
-void avs_debugfs_exit(struct avs_dev *adev);
-#else
-#define AVS_SET_ENABLE_LOGS_OP(name)
-
-static inline bool avs_logging_fw(struct avs_dev *adev)
-{
- return false;
-}
-
-static inline void avs_dump_fw_log(struct avs_dev *adev, const void __iomem *src, unsigned int len)
-{
-}
-
-static inline void
-avs_dump_fw_log_wakeup(struct avs_dev *adev, const void __iomem *src, unsigned int len)
-{
-}
-
-static inline int avs_probe_platform_register(struct avs_dev *adev, const char *name)
-{
- return 0;
-}
-
-static inline void avs_debugfs_init(struct avs_dev *adev) { }
-static inline void avs_debugfs_exit(struct avs_dev *adev) { }
-#endif
+extern const struct attribute_group *avs_attr_groups[];
#endif /* __SOUND_SOC_INTEL_AVS_H */
diff --git a/sound/soc/intel/avs/board_selection.c b/sound/soc/intel/avs/board_selection.c
index 59a13feec57b..52e6266a7cb8 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,24 @@
#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 "debug.h"
+#include "pcm.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[] = {
{
@@ -51,19 +58,13 @@ static const struct dmi_system_id kblr_dmi_table[] = {
static struct snd_soc_acpi_mach *dmi_match_quirk(void *arg)
{
struct snd_soc_acpi_mach *mach = arg;
- const struct dmi_system_id *dmi_id;
struct dmi_system_id *dmi_table;
- if (mach->quirk_data == NULL)
- return mach;
-
dmi_table = (struct dmi_system_id *)mach->quirk_data;
- dmi_id = dmi_first_match(dmi_table);
- if (!dmi_id)
- return NULL;
-
- return mach;
+ if (!dmi_table || dmi_first_match(dmi_table))
+ return mach;
+ return NULL;
}
#define AVS_SSP(x) (BIT(x))
@@ -136,6 +137,15 @@ static struct snd_soc_acpi_mach avs_kbl_i2s_machines[] = {
.tplg_filename = "max98927-tplg.bin",
},
{
+ .id = "10EC5514",
+ .drv_name = "avs_rt5514",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(0),
+ },
+ .pdata = (struct avs_mach_pdata[]){ { .tdms = (unsigned long[]){ 0x2 } } },
+ .tplg_filename = "rt5514-tplg.bin",
+ },
+ {
.id = "10EC5663",
.drv_name = "avs_rt5663",
.mach_params = {
@@ -193,7 +203,9 @@ static struct snd_soc_acpi_mach avs_apl_i2s_machines[] = {
.mach_params = {
.i2s_link_mask = AVS_SSP_RANGE(0, 5),
},
- .pdata = (unsigned long[]){ 0, 0, 0x14, 0, 0, 0 }, /* SSP2 TDMs */
+ .pdata = (struct avs_mach_pdata[]){ {
+ .tdms = (unsigned long[]){ 0x1, 0x1, 0x14, 0x1, 0x1, 0x1 }
+ } },
.tplg_filename = "tdf8532-tplg.bin",
},
{
@@ -227,50 +239,119 @@ static struct snd_soc_acpi_mach avs_gml_i2s_machines[] = {
{},
};
-static struct snd_soc_acpi_mach avs_test_i2s_machines[] = {
+static struct snd_soc_acpi_mach avs_cnl_i2s_machines[] = {
{
- .drv_name = "avs_i2s_test",
+ .id = "INT34C2",
+ .drv_name = "avs_rt274",
.mach_params = {
.i2s_link_mask = AVS_SSP(0),
},
- .tplg_filename = "i2s-test-tplg.bin",
+ .tplg_filename = "rt274-tplg.bin",
},
{
- .drv_name = "avs_i2s_test",
+ .id = "10EC5682",
+ .drv_name = "avs_rt5682",
.mach_params = {
.i2s_link_mask = AVS_SSP(1),
},
- .tplg_filename = "i2s-test-tplg.bin",
+ .tplg_filename = "rt5682-tplg.bin",
},
+ {},
+};
+
+static struct snd_soc_acpi_mach avs_icl_i2s_machines[] = {
{
- .drv_name = "avs_i2s_test",
+ .id = "INT343A",
+ .drv_name = "avs_rt298",
.mach_params = {
- .i2s_link_mask = AVS_SSP(2),
+ .i2s_link_mask = AVS_SSP(0),
},
- .tplg_filename = "i2s-test-tplg.bin",
+ .tplg_filename = "rt298-tplg.bin",
},
{
- .drv_name = "avs_i2s_test",
+ .id = "INT34C2",
+ .drv_name = "avs_rt274",
.mach_params = {
- .i2s_link_mask = AVS_SSP(3),
+ .i2s_link_mask = AVS_SSP(0),
},
- .tplg_filename = "i2s-test-tplg.bin",
+ .tplg_filename = "rt274-tplg.bin",
},
+ {},
+};
+
+static struct snd_soc_acpi_mach avs_tgl_i2s_machines[] = {
{
- .drv_name = "avs_i2s_test",
+ .id = "INT34C2",
+ .drv_name = "avs_rt274",
.mach_params = {
- .i2s_link_mask = AVS_SSP(4),
+ .i2s_link_mask = AVS_SSP(0),
},
- .tplg_filename = "i2s-test-tplg.bin",
+ .tplg_filename = "rt274-tplg.bin",
},
{
- .drv_name = "avs_i2s_test",
+ .id = "10EC0298",
+ .drv_name = "avs_rt298",
.mach_params = {
- .i2s_link_mask = AVS_SSP(5),
+ .i2s_link_mask = AVS_SSP(0),
},
- .tplg_filename = "i2s-test-tplg.bin",
+ .tplg_filename = "rt298-tplg.bin",
},
- /* no NULL terminator, as we depend on ARRAY SIZE due to .id == NULL */
+ {
+ .id = "10EC1308",
+ .drv_name = "avs_rt1308",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(1),
+ },
+ .tplg_filename = "rt1308-tplg.bin",
+ },
+ {
+ .id = "10EC5640",
+ .uid = "1",
+ .drv_name = "avs_rt5640",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(0),
+ },
+ .tplg_filename = "rt5640-tplg.bin",
+ },
+ {
+ .id = "10EC5640",
+ .uid = "3",
+ .drv_name = "avs_rt5640",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(1),
+ },
+ .tplg_filename = "rt5640-tplg.bin",
+ },
+ {
+ .id = "10EC5640",
+ .uid = "2",
+ .drv_name = "avs_rt5640",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(2),
+ },
+ .tplg_filename = "rt5640-tplg.bin",
+ },
+ {
+ .id = "ESSX8336",
+ .drv_name = "avs_es8336",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(0),
+ },
+ .tplg_filename = "es8336-tplg.bin",
+ },
+ {},
+};
+
+static struct snd_soc_acpi_mach avs_mbl_i2s_machines[] = {
+ {
+ .id = "PCM3168A",
+ .drv_name = "avs_pcm3168a",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(0) | AVS_SSP(2),
+ },
+ .tplg_filename = "pcm3168a-tplg.bin",
+ },
+ {}
};
struct avs_acpi_boards {
@@ -283,185 +364,221 @@ struct avs_acpi_boards {
/* supported I2S boards per platform */
static const struct avs_acpi_boards i2s_boards[] = {
- AVS_MACH_ENTRY(HDA_SKL_LP, avs_skl_i2s_machines),
- AVS_MACH_ENTRY(HDA_KBL_LP, avs_kbl_i2s_machines),
- AVS_MACH_ENTRY(HDA_APL, avs_apl_i2s_machines),
- AVS_MACH_ENTRY(HDA_GML, avs_gml_i2s_machines),
- {},
+ AVS_MACH_ENTRY(HDA_SKL_LP, avs_skl_i2s_machines),
+ AVS_MACH_ENTRY(HDA_KBL_LP, avs_kbl_i2s_machines),
+ AVS_MACH_ENTRY(HDA_APL, avs_apl_i2s_machines),
+ AVS_MACH_ENTRY(HDA_GML, avs_gml_i2s_machines),
+ AVS_MACH_ENTRY(HDA_CNL_LP, avs_cnl_i2s_machines),
+ AVS_MACH_ENTRY(HDA_CNL_H, avs_cnl_i2s_machines),
+ AVS_MACH_ENTRY(HDA_CML_LP, avs_cnl_i2s_machines),
+ 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_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)
+static struct snd_soc_acpi_mach *avs_get_i2s_machines(struct avs_dev *adev)
{
int id, i;
id = adev->base.pci->device;
for (i = 0; i < ARRAY_SIZE(i2s_boards); i++)
if (i2s_boards[i].id == id)
- return &i2s_boards[i];
+ return i2s_boards[i].machs;
return NULL;
}
-/* platform devices owned by AVS audio are removed with this hook */
-static void board_pdev_unregister(void *data)
+/* Platform devices spawned by AVS driver are removed with this hook. */
+static void avs_unregister_board(void *pdev)
{
- platform_device_unregister(data);
+ platform_device_unregister(pdev);
}
-static int __maybe_unused avs_register_probe_board(struct avs_dev *adev)
+static struct platform_device *avs_register_board(struct avs_dev *adev, const char *name,
+ const void *data, size_t size)
{
- struct platform_device *board;
- struct snd_soc_acpi_mach mach = {{0}};
+ struct platform_device *pdev;
int ret;
- ret = avs_probe_platform_register(adev, "probe-platform");
- if (ret < 0)
- return ret;
+ pdev = platform_device_register_data(NULL, name, PLATFORM_DEVID_AUTO, data, size);
+ if (IS_ERR(pdev))
+ return pdev;
- mach.mach_params.platform = "probe-platform";
+ ret = devm_add_action_or_reset(adev->dev, avs_unregister_board, pdev);
+ if (ret)
+ return ERR_PTR(ret);
- board = platform_device_register_data(NULL, "avs_probe_mb", PLATFORM_DEVID_NONE,
- (const void *)&mach, sizeof(mach));
- if (IS_ERR(board)) {
- dev_err(adev->dev, "probe board register failed\n");
- return PTR_ERR(board);
- }
+ return pdev;
+}
- ret = devm_add_action(adev->dev, board_pdev_unregister, board);
- if (ret < 0) {
- platform_device_unregister(board);
- return ret;
- }
- return 0;
+static struct platform_device *avs_register_board_pdata(struct avs_dev *adev, const char *name,
+ struct snd_soc_acpi_mach *mach,
+ struct hda_codec *codec,
+ unsigned long *tdms, char *codec_name)
+{
+ struct avs_mach_pdata *pdata;
+
+ pdata = devm_kzalloc(adev->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return ERR_PTR(-ENOMEM);
+
+ pdata->codec = codec;
+ pdata->tdms = tdms;
+ pdata->codec_name = codec_name;
+ pdata->obsolete_card_names = obsolete_card_names;
+ mach->pdata = pdata;
+
+ return avs_register_board(adev, name, mach, sizeof(*mach));
+}
+
+static int __maybe_unused avs_register_probe_board(struct avs_dev *adev)
+{
+ struct platform_device *pdev;
+
+ pdev = avs_register_board(adev, "avs_probe_mb", NULL, 0);
+ if (IS_ERR(pdev))
+ return PTR_ERR(pdev);
+
+ return avs_register_probe_component(adev, dev_name(&pdev->dev));
}
static int avs_register_dmic_board(struct avs_dev *adev)
{
- struct platform_device *codec, *board;
- struct snd_soc_acpi_mach mach = {{0}};
- int ret;
+ static struct snd_soc_acpi_mach mach = {
+ .tplg_filename = "dmic-tplg.bin",
+ };
+ struct platform_device *pdev;
+ char *codec_name;
- 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;
}
- codec = platform_device_register_simple("dmic-codec", PLATFORM_DEVID_NONE, NULL, 0);
- if (IS_ERR(codec)) {
- dev_err(adev->dev, "dmic codec register failed\n");
- return PTR_ERR(codec);
- }
+ /* DMIC present in Intel PCH is enumerated statically. */
+ pdev = avs_register_board(adev, "dmic-codec", NULL, 0);
+ if (IS_ERR(pdev))
+ return PTR_ERR(pdev);
- ret = devm_add_action(adev->dev, board_pdev_unregister, codec);
- if (ret < 0) {
- platform_device_unregister(codec);
- return ret;
- }
+ codec_name = devm_kstrdup(adev->dev, dev_name(&pdev->dev), GFP_KERNEL);
+ if (!codec_name)
+ return -ENOMEM;
- ret = avs_dmic_platform_register(adev, "dmic-platform");
- if (ret < 0)
- return ret;
+ pdev = avs_register_board_pdata(adev, "avs_dmic", &mach, NULL, NULL, codec_name);
+ if (IS_ERR(pdev))
+ return PTR_ERR(pdev);
- mach.tplg_filename = "dmic-tplg.bin";
- mach.mach_params.platform = "dmic-platform";
+ return avs_register_dmic_component(adev, dev_name(&pdev->dev));
+}
- board = platform_device_register_data(NULL, "avs_dmic", PLATFORM_DEVID_NONE,
- (const void *)&mach, sizeof(mach));
- if (IS_ERR(board)) {
- dev_err(adev->dev, "dmic board register failed\n");
- return PTR_ERR(board);
- }
+static int avs_register_i2s_test_board(struct avs_dev *adev, int ssp_port, int tdm_slot)
+{
+ struct snd_soc_acpi_mach mach = {{0}};
+ struct platform_device *pdev;
+ unsigned long *tdms;
+
+ tdms = devm_kcalloc(adev->dev, ssp_port + 1, sizeof(*tdms), GFP_KERNEL);
+ mach.tplg_filename = devm_kasprintf(adev->dev, GFP_KERNEL,
+ AVS_STRING_FMT("i2s", "-test-tplg.bin",
+ ssp_port, tdm_slot));
+ if (!tdms || !mach.tplg_filename)
+ return -ENOMEM;
- ret = devm_add_action(adev->dev, board_pdev_unregister, board);
- if (ret < 0) {
- platform_device_unregister(board);
- return ret;
- }
+ tdms[ssp_port] = BIT(tdm_slot);
+ mach.drv_name = "avs_i2s_test";
+ mach.mach_params.i2s_link_mask = AVS_SSP(ssp_port);
- return 0;
+ pdev = avs_register_board_pdata(adev, mach.drv_name, &mach, NULL, tdms, NULL);
+ if (IS_ERR(pdev))
+ return PTR_ERR(pdev);
+
+ return avs_register_i2s_component(adev, dev_name(&pdev->dev), AVS_SSP(ssp_port), tdms);
}
-static int avs_register_i2s_board(struct avs_dev *adev, struct snd_soc_acpi_mach *mach)
+static int avs_register_i2s_test_boards(struct avs_dev *adev)
{
- struct platform_device *board;
- int num_ssps;
- char *name;
- int ret;
+ int max_ssps = adev->hw_cfg.i2s_caps.ctrl_count;
+ int ssp_port, tdm_slot, ret;
+ unsigned long tdm_slots;
+ u32 *array, num_elems;
- num_ssps = adev->hw_cfg.i2s_caps.ctrl_count;
- if (fls(mach->mach_params.i2s_link_mask) > num_ssps) {
- dev_err(adev->dev, "Platform supports %d SSPs but board %s requires SSP%ld\n",
- num_ssps, mach->drv_name,
- (unsigned long)__fls(mach->mach_params.i2s_link_mask));
- return -ENODEV;
- }
-
- name = devm_kasprintf(adev->dev, GFP_KERNEL, "%s.%d-platform", mach->drv_name,
- mach->mach_params.i2s_link_mask);
- if (!name)
- return -ENOMEM;
+ if (!i2s_test)
+ return 0;
- ret = avs_i2s_platform_register(adev, name, mach->mach_params.i2s_link_mask, mach->pdata);
- if (ret < 0)
+ 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->mach_params.platform = name;
-
- board = platform_device_register_data(NULL, mach->drv_name, mach->mach_params.i2s_link_mask,
- (const void *)mach, sizeof(*mach));
- if (IS_ERR(board)) {
- dev_err(adev->dev, "ssp board register failed\n");
- return PTR_ERR(board);
+ 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;
}
- ret = devm_add_action(adev->dev, board_pdev_unregister, board);
- if (ret < 0) {
- platform_device_unregister(board);
- return 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)
+static int avs_register_i2s_board(struct avs_dev *adev, struct snd_soc_acpi_mach *mach)
{
- const struct avs_acpi_boards *boards;
- struct snd_soc_acpi_mach *mach;
- int ret;
+ u32 i2s_mask = mach->mach_params.i2s_link_mask;
+ struct platform_device *pdev;
+ unsigned long *tdms = NULL;
- if (!adev->nhlt || !intel_nhlt_has_endpoint_type(adev->nhlt, NHLT_LINK_SSP)) {
- dev_dbg(adev->dev, "no I2S endpoints present\n");
- return 0;
- }
+ if (mach->pdata)
+ tdms = ((struct avs_mach_pdata *)mach->pdata)->tdms;
- if (i2s_test) {
- int i, num_ssps;
+ pdev = avs_register_board_pdata(adev, mach->drv_name, mach, NULL, tdms, NULL);
+ if (IS_ERR(pdev))
+ return PTR_ERR(pdev);
- 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);
+ return avs_register_i2s_component(adev, dev_name(&pdev->dev), i2s_mask, tdms);
+}
- mach = avs_test_i2s_machines;
+static int avs_register_i2s_boards(struct avs_dev *adev)
+{
+ int num_ssps = adev->hw_cfg.i2s_caps.ctrl_count;
+ struct snd_soc_acpi_mach *machs;
+ struct snd_soc_acpi_mach *mach;
+ int ret;
- 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);
- }
+ if (!acpi_nhlt_find_endpoint(ACPI_NHLT_LINKTYPE_SSP, -1, -1, -1)) {
+ dev_dbg(adev->dev, "no I2S endpoints present\n");
return 0;
}
- boards = avs_get_i2s_boards(adev);
- if (!boards) {
+ machs = avs_get_i2s_machines(adev);
+ if (!machs) {
dev_dbg(adev->dev, "no I2S endpoints supported\n");
return 0;
}
- for (mach = boards->machs; mach->id[0]; mach++) {
+ for (mach = machs; mach->id[0]; mach++) {
if (!acpi_dev_present(mach->id, mach->uid, -1))
continue;
+ if (fls(mach->mach_params.i2s_link_mask) > num_ssps) {
+ dev_err(adev->dev, "Platform supports %d SSPs but board %s requires SSP%ld\n",
+ num_ssps, mach->drv_name,
+ (unsigned long)__fls(mach->mach_params.i2s_link_mask));
+ continue;
+ }
if (mach->machine_quirk)
if (!mach->machine_quirk(mach))
continue;
@@ -476,42 +593,20 @@ static int avs_register_i2s_boards(struct avs_dev *adev)
static int avs_register_hda_board(struct avs_dev *adev, struct hda_codec *codec)
{
- struct snd_soc_acpi_mach mach = {{0}};
- struct platform_device *board;
struct hdac_device *hdev = &codec->core;
- char *pname;
- int ret, id;
-
- pname = devm_kasprintf(adev->dev, GFP_KERNEL, "%s-platform", dev_name(&hdev->dev));
- if (!pname)
- return -ENOMEM;
-
- ret = avs_hda_platform_register(adev, pname);
- if (ret < 0)
- return ret;
+ struct snd_soc_acpi_mach mach = {{0}};
+ struct platform_device *pdev;
- mach.pdata = codec;
- mach.mach_params.platform = pname;
mach.tplg_filename = devm_kasprintf(adev->dev, GFP_KERNEL, "hda-%08x-tplg.bin",
hdev->vendor_id);
if (!mach.tplg_filename)
return -ENOMEM;
- id = adev->base.core.idx * HDA_MAX_CODECS + hdev->addr;
- board = platform_device_register_data(NULL, "avs_hdaudio", id, (const void *)&mach,
- sizeof(mach));
- if (IS_ERR(board)) {
- dev_err(adev->dev, "hda board register failed\n");
- return PTR_ERR(board);
- }
-
- ret = devm_add_action(adev->dev, board_pdev_unregister, board);
- if (ret < 0) {
- platform_device_unregister(board);
- return ret;
- }
+ pdev = avs_register_board_pdata(adev, "avs_hdaudio", &mach, codec, NULL, NULL);
+ if (IS_ERR(pdev))
+ return PTR_ERR(pdev);
- return 0;
+ return avs_register_hda_component(adev, dev_name(&pdev->dev));
}
static int avs_register_hda_boards(struct avs_dev *adev)
@@ -554,6 +649,10 @@ int avs_register_all_boards(struct avs_dev *adev)
dev_warn(adev->dev, "enumerate DMIC endpoints failed: %d\n",
ret);
+ ret = avs_register_i2s_test_boards(adev);
+ if (ret)
+ dev_dbg(adev->dev, "enumerate I2S TEST endpoints failed: %d\n", ret);
+
ret = avs_register_i2s_boards(adev);
if (ret < 0)
dev_warn(adev->dev, "enumerate I2S endpoints failed: %d\n",
diff --git a/sound/soc/intel/avs/boards/Kconfig b/sound/soc/intel/avs/boards/Kconfig
index 07353d37ecae..82f50207bb2f 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
@@ -125,6 +143,28 @@ config SND_SOC_INTEL_AVS_MACH_RT298
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_RT5514
+ tristate "rt5514 in I2S mode"
+ depends on I2C
+ depends on MFD_INTEL_LPSS || COMPILE_TEST
+ select SND_SOC_RT5514
+ help
+ This adds support for ASoC machine driver with RT5514 I2S audio codec.
+ 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_RT5640
+ tristate "rt5640 in I2S mode"
+ depends on I2C
+ depends on MFD_INTEL_LPSS || COMPILE_TEST
+ select SND_SOC_RT5640
+ help
+ This adds support for ASoC machine board connecting AVS with RT5640,
+ components representing Intel AudioDSP and Realtek 5640 codec respectively.
+ The codec chip is present on I2C bus and the streaming occurs over I2S
+ interface.
+ Say Y or m if you have such a device.
+
config SND_SOC_INTEL_AVS_MACH_RT5663
tristate "rt5663 in I2S mode"
depends on I2C
diff --git a/sound/soc/intel/avs/boards/Makefile b/sound/soc/intel/avs/boards/Makefile
index 34347bcd1e7d..46ef1babda34 100644
--- a/sound/soc/intel/avs/boards/Makefile
+++ b/sound/soc/intel/avs/boards/Makefile
@@ -1,21 +1,24 @@
# 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-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-rt5640-y := rt5640.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
@@ -26,10 +29,13 @@ 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
obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_RT298) += snd-soc-avs-rt298.o
+obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_RT5514) += snd-soc-avs-rt5514.o
+obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_RT5640) += snd-soc-avs-rt5640.o
obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_RT5663) += snd-soc-avs-rt5663.o
obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_RT5682) += snd-soc-avs-rt5682.o
obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_SSM4567) += snd-soc-avs-ssm4567.o
diff --git a/sound/soc/intel/avs/boards/da7219.c b/sound/soc/intel/avs/boards/da7219.c
index 85014d98f7e8..2b17abcbd2bc 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>
//
@@ -16,6 +16,7 @@
#include <sound/soc-dapm.h>
#include <uapi/linux/input-event-codes.h>
#include "../../../codecs/da7219.h"
+#include "../utils.h"
#define DA7219_DAI_NAME "da7219-hifi"
@@ -28,8 +29,7 @@ static const struct snd_kcontrol_new card_controls[] = {
static int platform_clock_control(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *k, int event)
{
- struct snd_soc_dapm_context *dapm = w->dapm;
- struct snd_soc_card *card = dapm->card;
+ struct snd_soc_card *card = snd_soc_dapm_to_card(w->dapm);
struct snd_soc_dai *codec_dai;
int ret = 0;
@@ -90,7 +90,7 @@ static const struct snd_soc_jack_pin card_headset_pins[] = {
static int avs_da7219_codec_init(struct snd_soc_pcm_runtime *runtime)
{
- struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(runtime, 0);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(runtime, 0);
struct snd_soc_component *component = codec_dai->component;
struct snd_soc_card *card = runtime->card;
struct snd_soc_jack_pin *pins;
@@ -112,7 +112,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;
@@ -140,7 +141,7 @@ static int avs_da7219_codec_init(struct snd_soc_pcm_runtime *runtime)
static void avs_da7219_codec_exit(struct snd_soc_pcm_runtime *rtd)
{
- snd_soc_component_set_jack(asoc_rtd_to_codec(rtd, 0)->component, NULL, NULL);
+ snd_soc_component_set_jack(snd_soc_rtd_to_codec(rtd, 0)->component, NULL, NULL);
}
static int
@@ -163,7 +164,7 @@ avs_da7219_be_fixup(struct snd_soc_pcm_runtime *runrime, struct snd_pcm_hw_param
return 0;
}
-static int avs_create_dai_link(struct device *dev, const char *platform_name, int ssp_port,
+static int avs_create_dai_link(struct device *dev, int ssp_port, int tdm_slot,
struct snd_soc_dai_link **dai_link)
{
struct snd_soc_dai_link_component *platform;
@@ -174,69 +175,57 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in
if (!dl || !platform)
return -ENOMEM;
- platform->name = platform_name;
-
dl->name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-Codec", ssp_port);
+ 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), GFP_KERNEL);
if (!dl->name || !dl->cpus || !dl->codecs)
return -ENOMEM;
- dl->cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d Pin", ssp_port);
+ dl->cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
+ AVS_STRING_FMT("SSP", " Pin", ssp_port, tdm_slot));
dl->codecs->name = devm_kasprintf(dev, GFP_KERNEL, "i2c-DLGS7219:00");
dl->codecs->dai_name = devm_kasprintf(dev, GFP_KERNEL, DA7219_DAI_NAME);
if (!dl->cpus->dai_name || !dl->codecs->name || !dl->codecs->dai_name)
return -ENOMEM;
+ platform->name = dev_name(dev);
dl->num_cpus = 1;
dl->num_codecs = 1;
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;
- const char *pname;
- int ssp_port, ret;
+ int ssp_port, tdm_slot, ret;
mach = dev_get_platdata(dev);
- pname = mach->mach_params.platform;
- ssp_port = __ffs(mach->mach_params.i2s_link_mask);
+ pdata = mach->pdata;
- ret = avs_create_dai_link(dev, pname, ssp_port, &dai_link);
+ ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot);
+ if (ret)
+ return ret;
+
+ ret = avs_create_dai_link(dev, ssp_port, tdm_slot, &dai_link);
if (ret) {
dev_err(dev, "Failed to create dai link: %d", ret);
return ret;
@@ -247,11 +236,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;
@@ -263,23 +255,28 @@ static int avs_da7219_probe(struct platform_device *pdev)
card->fully_routed = true;
snd_soc_card_set_drvdata(card, jack);
- 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_da7219_driver_ids[] = {
+ {
+ .name = "avs_da7219",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(platform, avs_da7219_driver_ids);
+
static struct platform_driver avs_da7219_driver = {
.probe = avs_da7219_probe,
.driver = {
.name = "avs_da7219",
.pm = &snd_soc_pm_ops,
},
+ .id_table = avs_da7219_driver_ids,
};
module_platform_driver(avs_da7219_driver);
+MODULE_DESCRIPTION("Intel da7219 machine driver");
MODULE_AUTHOR("Cezary Rojewski <cezary.rojewski@intel.com>");
MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:avs_da7219");
diff --git a/sound/soc/intel/avs/boards/dmic.c b/sound/soc/intel/avs/boards/dmic.c
index c270646faf86..bf6f580a5164 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,33 +10,10 @@
#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")));
-SND_SOC_DAILINK_DEF(dmic_codec, DAILINK_COMP_ARRAY(COMP_CODEC("dmic-codec", "dmic-hifi")));
-/* Name overridden on probe */
-SND_SOC_DAILINK_DEF(platform, DAILINK_COMP_ARRAY(COMP_PLATFORM("")));
-
-static struct snd_soc_dai_link card_dai_links[] = {
- /* Back ends */
- {
- .name = "DMIC",
- .id = 0,
- .dpcm_capture = 1,
- .nonatomic = 1,
- .no_pcm = 1,
- SND_SOC_DAILINK_REG(dmic_pin, dmic_codec, platform),
- },
- {
- .name = "DMIC WoV",
- .id = 1,
- .dpcm_capture = 1,
- .nonatomic = 1,
- .no_pcm = 1,
- .ignore_suspend = 1,
- SND_SOC_DAILINK_REG(dmic_wov_pin, dmic_codec, platform),
- },
-};
static const struct snd_soc_dapm_widget card_widgets[] = {
SND_SOC_DAPM_MIC("SoC DMIC", NULL),
@@ -46,46 +23,104 @@ static const struct snd_soc_dapm_route card_routes[] = {
{"DMic", NULL, "SoC DMIC"},
};
+static int avs_create_dai_links(struct device *dev, const char *codec_name,
+ 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;
+
+ dl->codecs = devm_kzalloc(dev, sizeof(*dl->codecs), GFP_KERNEL);
+ if (!dl->codecs)
+ return -ENOMEM;
+
+ dl->codecs->name = devm_kstrdup(dev, codec_name, GFP_KERNEL);
+ dl->codecs->dai_name = devm_kasprintf(dev, GFP_KERNEL, "dmic-hifi");
+ if (!dl->codecs->name || !dl->codecs->dai_name)
+ 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].nonatomic = 1;
+ dl[0].no_pcm = 1;
+ dl[0].capture_only = 1;
+ memcpy(&dl[1], &dl[0], sizeof(*dl));
+
+ dl[0].name = "DMIC";
+ dl[0].cpus = dmic_pin;
+ dl[0].id = 0;
+ dl[1].name = "DMIC WoV";
+ dl[1].cpus = dmic_wov_pin;
+ dl[1].id = 1;
+ dl[1].ignore_suspend = 1;
+
+ *links = dl;
+ *num_links = num_dl;
+ return 0;
+}
+
static int avs_dmic_probe(struct platform_device *pdev)
{
+ struct device *dev = &pdev->dev;
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";
+ ret = avs_create_dai_links(dev, pdata->codec_name, &card->dai_link, &card->num_links);
+ if (ret)
+ return ret;
+
+ 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;
- card->num_links = ARRAY_SIZE(card_dai_links);
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;
- ret = snd_soc_fixup_dai_links_platform_name(card, mach->mach_params.platform);
- 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[] = {
+ {
+ .name = "avs_dmic",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(platform, avs_dmic_driver_ids);
+
static struct platform_driver avs_dmic_driver = {
.probe = avs_dmic_probe,
.driver = {
.name = "avs_dmic",
.pm = &snd_soc_pm_ops,
},
+ .id_table = avs_dmic_driver_ids,
};
module_platform_driver(avs_dmic_driver);
+MODULE_DESCRIPTION("Intel DMIC machine driver");
MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:avs_dmic");
diff --git a/sound/soc/intel/avs/boards/es8336.c b/sound/soc/intel/avs/boards/es8336.c
index 0a023f871d93..301cfb3cf15b 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,8 @@
#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"
@@ -37,7 +38,7 @@ static const struct acpi_gpio_mapping speaker_gpios[] = {
static int avs_es8336_speaker_power_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_card *card = w->dapm->card;
+ struct snd_soc_card *card = snd_soc_dapm_to_card(w->dapm);
struct avs_card_drvdata *data;
bool speaker_en;
@@ -84,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,
@@ -97,9 +98,10 @@ static struct snd_soc_jack_pin card_headset_pins[] = {
static int avs_es8336_codec_init(struct snd_soc_pcm_runtime *runtime)
{
- struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(runtime, 0);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(runtime, 0);
struct snd_soc_component *component = codec_dai->component;
struct snd_soc_card *card = runtime->card;
+ struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
struct snd_soc_jack_pin *pins;
struct avs_card_drvdata *data;
struct gpio_desc *gpiod;
@@ -108,11 +110,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;
@@ -130,7 +133,7 @@ static int avs_es8336_codec_init(struct snd_soc_pcm_runtime *runtime)
snd_jack_set_key(data->jack.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
snd_soc_component_set_jack(component, &data->jack, NULL);
- card->dapm.idle_bias_off = true;
+ snd_soc_dapm_set_idle_bias(dapm, false);
return 0;
}
@@ -138,7 +141,7 @@ static int avs_es8336_codec_init(struct snd_soc_pcm_runtime *runtime)
static void avs_es8336_codec_exit(struct snd_soc_pcm_runtime *runtime)
{
struct avs_card_drvdata *data = snd_soc_card_get_drvdata(runtime->card);
- struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(runtime, 0);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(runtime, 0);
snd_soc_component_set_jack(codec_dai->component, NULL, NULL);
gpiod_put(data->gpiod);
@@ -147,14 +150,14 @@ static void avs_es8336_codec_exit(struct snd_soc_pcm_runtime *runtime)
static int avs_es8336_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
- struct snd_soc_pcm_runtime *runtime = asoc_substream_to_rtd(substream);
- struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(runtime, 0);
+ struct snd_soc_pcm_runtime *runtime = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(runtime, 0);
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:
@@ -193,7 +196,8 @@ static int avs_es8336_be_fixup(struct snd_soc_pcm_runtime *runtime,
return 0;
}
-static int avs_create_dai_link(struct device *dev, const char *platform_name, int ssp_port,
+
+static int avs_create_dai_link(struct device *dev, int ssp_port, int tdm_slot,
struct snd_soc_dai_link **dai_link)
{
struct snd_soc_dai_link_component *platform;
@@ -204,20 +208,21 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in
if (!dl || !platform)
return -ENOMEM;
- platform->name = platform_name;
-
- dl->name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-Codec", ssp_port);
+ 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), GFP_KERNEL);
if (!dl->name || !dl->cpus || !dl->codecs)
return -ENOMEM;
- dl->cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d Pin", ssp_port);
+ dl->cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
+ AVS_STRING_FMT("SSP", " Pin", ssp_port, tdm_slot));
dl->codecs->name = devm_kasprintf(dev, GFP_KERNEL, "i2c-ESSX8336:00");
dl->codecs->dai_name = devm_kasprintf(dev, GFP_KERNEL, ES8336_CODEC_DAI);
if (!dl->cpus->dai_name || !dl->codecs->name || !dl->codecs->dai_name)
return -ENOMEM;
+ platform->name = dev_name(dev);
dl->num_cpus = 1;
dl->num_codecs = 1;
dl->platforms = platform;
@@ -230,8 +235,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;
@@ -257,17 +260,20 @@ 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;
- const char *pname;
- int ssp_port, ret;
+ int ssp_port, tdm_slot, ret;
mach = dev_get_platdata(dev);
- pname = mach->mach_params.platform;
- ssp_port = __ffs(mach->mach_params.i2s_link_mask);
+ pdata = mach->pdata;
- ret = avs_create_dai_link(dev, pname, ssp_port, &dai_link);
+ ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot);
+ if (ret)
+ return ret;
+
+ ret = avs_create_dai_link(dev, ssp_port, tdm_slot, &dai_link);
if (ret) {
dev_err(dev, "Failed to create dai link: %d", ret);
return ret;
@@ -278,7 +284,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;
@@ -294,22 +305,27 @@ static int avs_es8336_probe(struct platform_device *pdev)
card->fully_routed = true;
snd_soc_card_set_drvdata(card, data);
- 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_es8336_driver_ids[] = {
+ {
+ .name = "avs_es8336",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(platform, avs_es8336_driver_ids);
+
static struct platform_driver avs_es8336_driver = {
.probe = avs_es8336_probe,
.driver = {
.name = "avs_es8336",
.pm = &snd_soc_pm_ops,
},
+ .id_table = avs_es8336_driver_ids,
};
module_platform_driver(avs_es8336_driver);
+MODULE_DESCRIPTION("Intel es8336 machine driver");
MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:avs_es8336");
diff --git a/sound/soc/intel/avs/boards/hdaudio.c b/sound/soc/intel/avs/boards/hdaudio.c
index cb00bc86ac94..aec769e2396c 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,9 +13,10 @@
#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)
+ struct snd_soc_dai_link **links)
{
struct snd_soc_dai_link_component *platform;
struct snd_soc_dai_link *dl;
@@ -28,7 +29,7 @@ static int avs_create_dai_links(struct device *dev, struct hda_codec *codec, int
if (!dl || !platform)
return -ENOMEM;
- platform->name = platform_name;
+ platform->name = dev_name(dev);
pcm = list_first_entry(&codec->pcm_list_head, struct hda_pcm, list);
for (i = 0; i < pcm_count; i++, pcm = list_next_entry(pcm, list)) {
@@ -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,10 @@ 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;
+
dl[i].codecs->dai_name = pcm->name;
dl[i].num_codecs = 1;
dl[i].num_cpus = 1;
@@ -94,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;
@@ -123,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;
@@ -130,14 +134,15 @@ 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;
list_for_each_entry(pcm, &codec->pcm_list_head, list)
pcm_count++;
- ret = avs_create_dai_links(card->dev, codec, pcm_count, mach->mach_params.platform, &links);
+ ret = avs_create_dai_links(card->dev, codec, pcm_count, &links);
if (ret < 0) {
dev_err(card->dev, "create links failed: %d\n", ret);
return ret;
@@ -152,17 +157,13 @@ static int avs_probing_link_init(struct snd_soc_pcm_runtime *rtm)
return 0;
}
-SND_SOC_DAILINK_DEF(dummy, DAILINK_COMP_ARRAY(COMP_DUMMY()));
-
-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 = dummy,
- .num_cpus = ARRAY_SIZE(dummy),
+ .cpus = &snd_soc_dummy_dlc,
+ .num_cpus = 1,
.init = avs_probing_link_init,
};
@@ -170,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))
@@ -190,11 +193,11 @@ 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;
- binder->platforms->name = mach->mach_params.platform;
+ binder->platforms->name = dev_name(dev);
binder->num_platforms = 1;
binder->codecs->dai_name = "codec-probing-DAI";
binder->num_codecs = 1;
@@ -203,7 +206,19 @@ 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 = devm_kasprintf(dev, GFP_KERNEL, "hdaudioB%dD%d", codec->bus->core.idx,
+ codec->core.addr);
+ if (!card->name)
+ return -ENOMEM;
+ } 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;
@@ -212,15 +227,24 @@ 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[] = {
+ {
+ .name = "avs_hdaudio",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(platform, avs_hdaudio_driver_ids);
+
static struct platform_driver avs_hdaudio_driver = {
.probe = avs_hdaudio_probe,
.driver = {
.name = "avs_hdaudio",
.pm = &snd_soc_pm_ops,
},
+ .id_table = avs_hdaudio_driver_ids,
};
module_platform_driver(avs_hdaudio_driver)
@@ -228,4 +252,3 @@ module_platform_driver(avs_hdaudio_driver)
MODULE_DESCRIPTION("Intel HD-Audio machine driver");
MODULE_AUTHOR("Cezary Rojewski <cezary.rojewski@intel.com>");
MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:avs_hdaudio");
diff --git a/sound/soc/intel/avs/boards/i2s_test.c b/sound/soc/intel/avs/boards/i2s_test.c
index bc3065c6ceda..9a6b89ffdf14 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>
@@ -12,8 +12,9 @@
#include <sound/soc.h>
#include <sound/soc-acpi.h>
#include <sound/soc-dapm.h>
+#include "../utils.h"
-static int avs_create_dai_link(struct device *dev, const char *platform_name, int ssp_port,
+static int avs_create_dai_link(struct device *dev, int ssp_port, int tdm_slot,
struct snd_soc_dai_link **dai_link)
{
struct snd_soc_dai_link_component *platform;
@@ -24,18 +25,19 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in
if (!dl || !platform)
return -ENOMEM;
- platform->name = platform_name;
-
- dl->name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-Codec", ssp_port);
+ 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);
if (!dl->name || !dl->cpus)
return -ENOMEM;
- dl->cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d Pin", ssp_port);
- dl->codecs = &asoc_dummy_dlc;
+ dl->cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
+ AVS_STRING_FMT("SSP", " Pin", ssp_port, tdm_slot));
+ dl->codecs = &snd_soc_dummy_dlc;
if (!dl->cpus->dai_name || !dl->codecs->name || !dl->codecs->dai_name)
return -ENOMEM;
+ platform->name = dev_name(dev);
dl->num_cpus = 1;
dl->num_codecs = 1;
dl->platforms = platform;
@@ -43,136 +45,85 @@ 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,
- 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, "ssp%dpb", ssp_port);
- dr[0].source = devm_kasprintf(dev, GFP_KERNEL, "ssp%d Tx", ssp_port);
- if (!dr[0].sink || !dr[0].source)
- return -ENOMEM;
-
- dr[1].sink = devm_kasprintf(dev, GFP_KERNEL, "ssp%d Rx", ssp_port);
- dr[1].source = devm_kasprintf(dev, GFP_KERNEL, "ssp%dcp", ssp_port);
- 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,
- 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, "ssp%dpb", ssp_port);
- 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, "ssp%dcp", ssp_port);
- 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, ret;
+ int ssp_port, tdm_slot, ret;
mach = dev_get_platdata(dev);
- pname = mach->mach_params.platform;
- ssp_port = __ffs(mach->mach_params.i2s_link_mask);
+ pdata = mach->pdata;
+
+ if (!avs_mach_singular_ssp(mach)) {
+ dev_err(dev, "Invalid SSP configuration\n");
+ return -EINVAL;
+ }
+ ssp_port = avs_mach_ssp_port(mach);
+
+ if (!avs_mach_singular_tdm(mach, ssp_port)) {
+ dev_err(dev, "Invalid TDM configuration\n");
+ return -EINVAL;
+ }
+ tdm_slot = avs_mach_ssp_tdm(mach, ssp_port);
card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
if (!card)
return -ENOMEM;
- card->name = devm_kasprintf(dev, GFP_KERNEL, "ssp%d-loopback", ssp_port);
+ 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;
- ret = avs_create_dai_link(dev, pname, ssp_port, &dai_link);
+ ret = avs_create_dai_link(dev, ssp_port, tdm_slot, &dai_link);
if (ret) {
dev_err(dev, "Failed to create dai link: %d\n", ret);
return ret;
}
- ret = avs_create_dapm_routes(dev, ssp_port, &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, &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[] = {
+ {
+ .name = "avs_i2s_test",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(platform, avs_i2s_test_driver_ids);
+
static struct platform_driver avs_i2s_test_driver = {
.probe = avs_i2s_test_probe,
.driver = {
.name = "avs_i2s_test",
.pm = &snd_soc_pm_ops,
},
+ .id_table = avs_i2s_test_driver_ids,
};
module_platform_driver(avs_i2s_test_driver);
+MODULE_DESCRIPTION("Intel i2s test machine driver");
MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:avs_i2s_test");
diff --git a/sound/soc/intel/avs/boards/max98357a.c b/sound/soc/intel/avs/boards/max98357a.c
index b9b20562c691..e9a87804f918 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>
@@ -12,6 +12,7 @@
#include <sound/soc.h>
#include <sound/soc-acpi.h>
#include <sound/soc-dapm.h>
+#include "../utils.h"
static const struct snd_kcontrol_new card_controls[] = {
SOC_DAPM_PIN_SWITCH("Spk"),
@@ -45,7 +46,7 @@ avs_max98357a_be_fixup(struct snd_soc_pcm_runtime *runrime, struct snd_pcm_hw_pa
return 0;
}
-static int avs_create_dai_link(struct device *dev, const char *platform_name, int ssp_port,
+static int avs_create_dai_link(struct device *dev, int ssp_port, int tdm_slot,
struct snd_soc_dai_link **dai_link)
{
struct snd_soc_dai_link_component *platform;
@@ -56,30 +57,31 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in
if (!dl || !platform)
return -ENOMEM;
- platform->name = platform_name;
-
- dl->name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-Codec", ssp_port);
+ 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), GFP_KERNEL);
if (!dl->name || !dl->cpus || !dl->codecs)
return -ENOMEM;
- dl->cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d Pin", ssp_port);
+ dl->cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
+ AVS_STRING_FMT("SSP", " Pin", ssp_port, tdm_slot));
dl->codecs->name = devm_kasprintf(dev, GFP_KERNEL, "MX98357A:00");
dl->codecs->dai_name = devm_kasprintf(dev, GFP_KERNEL, "HiFi");
if (!dl->cpus->dai_name || !dl->codecs->name || !dl->codecs->dai_name)
return -ENOMEM;
+ platform->name = dev_name(dev);
dl->num_cpus = 1;
dl->num_codecs = 1;
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;
@@ -90,16 +92,19 @@ 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;
- int ssp_port, ret;
+ int ssp_port, tdm_slot, ret;
mach = dev_get_platdata(dev);
- pname = mach->mach_params.platform;
- ssp_port = __ffs(mach->mach_params.i2s_link_mask);
+ pdata = mach->pdata;
+
+ ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot);
+ if (ret)
+ return ret;
- ret = avs_create_dai_link(dev, pname, ssp_port, &dai_link);
+ ret = avs_create_dai_link(dev, ssp_port, tdm_slot, &dai_link);
if (ret) {
dev_err(dev, "Failed to create dai link: %d", ret);
return ret;
@@ -109,7 +114,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;
@@ -122,22 +132,27 @@ static int avs_max98357a_probe(struct platform_device *pdev)
card->num_dapm_routes = ARRAY_SIZE(card_base_routes);
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_max98357a_driver_ids[] = {
+ {
+ .name = "avs_max98357a",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(platform, avs_max98357a_driver_ids);
+
static struct platform_driver avs_max98357a_driver = {
.probe = avs_max98357a_probe,
.driver = {
.name = "avs_max98357a",
.pm = &snd_soc_pm_ops,
},
+ .id_table = avs_max98357a_driver_ids,
};
module_platform_driver(avs_max98357a_driver)
+MODULE_DESCRIPTION("Intel max98357a machine driver");
MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:avs_max98357a");
diff --git a/sound/soc/intel/avs/boards/max98373.c b/sound/soc/intel/avs/boards/max98373.c
index 3833251ade26..8b45b643ca29 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>
@@ -12,6 +12,7 @@
#include <sound/soc.h>
#include <sound/soc-acpi.h>
#include <sound/soc-dapm.h>
+#include "../utils.h"
#define MAX98373_DEV0_NAME "i2c-MX98373:00"
#define MAX98373_DEV1_NAME "i2c-MX98373:01"
@@ -66,7 +67,7 @@ avs_max98373_be_fixup(struct snd_soc_pcm_runtime *runrime, struct snd_pcm_hw_par
static int avs_max98373_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
- struct snd_soc_pcm_runtime *runtime = asoc_substream_to_rtd(substream);
+ struct snd_soc_pcm_runtime *runtime = snd_soc_substream_to_rtd(substream);
struct snd_soc_dai *codec_dai;
int ret, i;
@@ -94,7 +95,7 @@ static const struct snd_soc_ops avs_max98373_ops = {
.hw_params = avs_max98373_hw_params,
};
-static int avs_create_dai_link(struct device *dev, const char *platform_name, int ssp_port,
+static int avs_create_dai_link(struct device *dev, int ssp_port, int tdm_slot,
struct snd_soc_dai_link **dai_link)
{
struct snd_soc_dai_link_component *platform;
@@ -105,15 +106,15 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in
if (!dl || !platform)
return -ENOMEM;
- platform->name = platform_name;
-
- dl->name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-Codec", ssp_port);
+ 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;
- dl->cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d Pin", ssp_port);
+ dl->cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
+ AVS_STRING_FMT("SSP", " Pin", ssp_port, tdm_slot));
dl->codecs[0].name = devm_kasprintf(dev, GFP_KERNEL, MAX98373_DEV0_NAME);
dl->codecs[0].dai_name = devm_kasprintf(dev, GFP_KERNEL, MAX98373_CODEC_NAME);
dl->codecs[1].name = devm_kasprintf(dev, GFP_KERNEL, MAX98373_DEV1_NAME);
@@ -122,6 +123,7 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in
!dl->codecs[1].name || !dl->codecs[1].dai_name)
return -ENOMEM;
+ platform->name = dev_name(dev);
dl->num_cpus = 1;
dl->num_codecs = 2;
dl->platforms = platform;
@@ -131,8 +133,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;
@@ -145,16 +145,19 @@ 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;
- int ssp_port, ret;
+ int ssp_port, tdm_slot, ret;
mach = dev_get_platdata(dev);
- pname = mach->mach_params.platform;
- ssp_port = __ffs(mach->mach_params.i2s_link_mask);
+ pdata = mach->pdata;
+
+ ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot);
+ if (ret)
+ return ret;
- ret = avs_create_dai_link(dev, pname, ssp_port, &dai_link);
+ ret = avs_create_dai_link(dev, ssp_port, tdm_slot, &dai_link);
if (ret) {
dev_err(dev, "Failed to create dai link: %d", ret);
return ret;
@@ -164,7 +167,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;
@@ -179,22 +187,27 @@ static int avs_max98373_probe(struct platform_device *pdev)
card->num_dapm_routes = ARRAY_SIZE(card_base_routes);
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_max98373_driver_ids[] = {
+ {
+ .name = "avs_max98373",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(platform, avs_max98373_driver_ids);
+
static struct platform_driver avs_max98373_driver = {
.probe = avs_max98373_probe,
.driver = {
.name = "avs_max98373",
.pm = &snd_soc_pm_ops,
},
+ .id_table = avs_max98373_driver_ids,
};
module_platform_driver(avs_max98373_driver)
+MODULE_DESCRIPTION("Intel max98373 machine driver");
MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:avs_max98373");
diff --git a/sound/soc/intel/avs/boards/max98927.c b/sound/soc/intel/avs/boards/max98927.c
index 09b231bf4e6d..db073125fa4d 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>
@@ -12,6 +12,7 @@
#include <sound/soc.h>
#include <sound/soc-acpi.h>
#include <sound/soc-dapm.h>
+#include "../utils.h"
#define MAX98927_DEV0_NAME "i2c-MX98927:00"
#define MAX98927_DEV1_NAME "i2c-MX98927:01"
@@ -66,7 +67,7 @@ avs_max98927_be_fixup(struct snd_soc_pcm_runtime *runrime, struct snd_pcm_hw_par
static int avs_max98927_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
- struct snd_soc_pcm_runtime *runtime = asoc_substream_to_rtd(substream);
+ struct snd_soc_pcm_runtime *runtime = snd_soc_substream_to_rtd(substream);
struct snd_soc_dai *codec_dai;
int ret = 0;
int i;
@@ -91,7 +92,7 @@ static const struct snd_soc_ops avs_max98927_ops = {
.hw_params = avs_max98927_hw_params,
};
-static int avs_create_dai_link(struct device *dev, const char *platform_name, int ssp_port,
+static int avs_create_dai_link(struct device *dev, int ssp_port, int tdm_slot,
struct snd_soc_dai_link **dai_link)
{
struct snd_soc_dai_link_component *platform;
@@ -102,15 +103,15 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in
if (!dl || !platform)
return -ENOMEM;
- platform->name = platform_name;
-
- dl->name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-Codec", ssp_port);
+ 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;
- dl->cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d Pin", ssp_port);
+ dl->cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
+ AVS_STRING_FMT("SSP", " Pin", ssp_port, tdm_slot));
dl->codecs[0].name = devm_kasprintf(dev, GFP_KERNEL, MAX98927_DEV0_NAME);
dl->codecs[0].dai_name = devm_kasprintf(dev, GFP_KERNEL, MAX98927_CODEC_NAME);
dl->codecs[1].name = devm_kasprintf(dev, GFP_KERNEL, MAX98927_DEV1_NAME);
@@ -119,17 +120,16 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in
!dl->codecs[1].name || !dl->codecs[1].dai_name)
return -ENOMEM;
+ platform->name = dev_name(dev);
dl->num_cpus = 1;
dl->num_codecs = 2;
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;
@@ -142,16 +142,19 @@ 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;
- int ssp_port, ret;
+ int ssp_port, tdm_slot, ret;
mach = dev_get_platdata(dev);
- pname = mach->mach_params.platform;
- ssp_port = __ffs(mach->mach_params.i2s_link_mask);
+ pdata = mach->pdata;
+
+ ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot);
+ if (ret)
+ return ret;
- ret = avs_create_dai_link(dev, pname, ssp_port, &dai_link);
+ ret = avs_create_dai_link(dev, ssp_port, tdm_slot, &dai_link);
if (ret) {
dev_err(dev, "Failed to create dai link: %d", ret);
return ret;
@@ -161,7 +164,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;
@@ -176,22 +184,27 @@ static int avs_max98927_probe(struct platform_device *pdev)
card->num_dapm_routes = ARRAY_SIZE(card_base_routes);
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_max98927_driver_ids[] = {
+ {
+ .name = "avs_max98927",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(platform, avs_max98927_driver_ids);
+
static struct platform_driver avs_max98927_driver = {
.probe = avs_max98927_probe,
.driver = {
.name = "avs_max98927",
.pm = &snd_soc_pm_ops,
},
+ .id_table = avs_max98927_driver_ids,
};
module_platform_driver(avs_max98927_driver)
+MODULE_DESCRIPTION("Intel max98927 machine driver");
MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:avs_max98927");
diff --git a/sound/soc/intel/avs/boards/nau8825.c b/sound/soc/intel/avs/boards/nau8825.c
index 38c5087d98e9..d44edacbfc9a 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>
@@ -16,14 +16,14 @@
#include <sound/soc.h>
#include <sound/soc-acpi.h>
#include "../../../codecs/nau8825.h"
+#include "../utils.h"
#define SKL_NUVOTON_CODEC_DAI "nau8825-hifi"
static int
avs_nau8825_clock_control(struct snd_soc_dapm_widget *w, struct snd_kcontrol *control, int event)
{
- struct snd_soc_dapm_context *dapm = w->dapm;
- struct snd_soc_card *card = dapm->card;
+ struct snd_soc_card *card = snd_soc_dapm_to_card(w->dapm);
struct snd_soc_dai *codec_dai;
int ret;
@@ -66,7 +66,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,
@@ -87,7 +87,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;
@@ -95,7 +96,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)
@@ -106,12 +107,12 @@ static int avs_nau8825_codec_init(struct snd_soc_pcm_runtime *runtime)
snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEUP);
snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN);
- return snd_soc_component_set_jack(asoc_rtd_to_codec(runtime, 0)->component, jack, NULL);
+ return snd_soc_component_set_jack(snd_soc_rtd_to_codec(runtime, 0)->component, jack, NULL);
}
static void avs_nau8825_codec_exit(struct snd_soc_pcm_runtime *rtd)
{
- snd_soc_component_set_jack(asoc_rtd_to_codec(rtd, 0)->component, NULL, NULL);
+ snd_soc_component_set_jack(snd_soc_rtd_to_codec(rtd, 0)->component, NULL, NULL);
}
static int
@@ -138,8 +139,8 @@ avs_nau8825_be_fixup(struct snd_soc_pcm_runtime *runtime, struct snd_pcm_hw_para
static int avs_nau8825_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_soc_pcm_runtime *rtm = asoc_substream_to_rtd(substream);
- struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtm, 0);
+ struct snd_soc_pcm_runtime *rtm = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtm, 0);
int ret = 0;
switch (cmd) {
@@ -170,7 +171,7 @@ static const struct snd_soc_ops avs_nau8825_ops = {
.trigger = avs_nau8825_trigger,
};
-static int avs_create_dai_link(struct device *dev, const char *platform_name, int ssp_port,
+static int avs_create_dai_link(struct device *dev, int ssp_port, int tdm_slot,
struct snd_soc_dai_link **dai_link)
{
struct snd_soc_dai_link_component *platform;
@@ -181,34 +182,33 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in
if (!dl || !platform)
return -ENOMEM;
- platform->name = platform_name;
-
- dl->name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-Codec", ssp_port);
+ 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), GFP_KERNEL);
if (!dl->name || !dl->cpus || !dl->codecs)
return -ENOMEM;
- dl->cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d Pin", ssp_port);
+ dl->cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
+ AVS_STRING_FMT("SSP", " Pin", ssp_port, tdm_slot));
dl->codecs->name = devm_kasprintf(dev, GFP_KERNEL, "i2c-10508825:00");
dl->codecs->dai_name = devm_kasprintf(dev, GFP_KERNEL, SKL_NUVOTON_CODEC_DAI);
if (!dl->cpus->dai_name || !dl->codecs->name || !dl->codecs->dai_name)
return -ENOMEM;
+ platform->name = dev_name(dev);
dl->num_cpus = 1;
dl->num_codecs = 1;
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;
@@ -244,17 +244,20 @@ 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;
- const char *pname;
- int ssp_port, ret;
+ int ssp_port, tdm_slot, ret;
mach = dev_get_platdata(dev);
- pname = mach->mach_params.platform;
- ssp_port = __ffs(mach->mach_params.i2s_link_mask);
+ pdata = mach->pdata;
+
+ ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot);
+ if (ret)
+ return ret;
- ret = avs_create_dai_link(dev, pname, ssp_port, &dai_link);
+ ret = avs_create_dai_link(dev, ssp_port, tdm_slot, &dai_link);
if (ret) {
dev_err(dev, "Failed to create dai link: %d", ret);
return ret;
@@ -265,7 +268,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;
@@ -281,22 +289,27 @@ static int avs_nau8825_probe(struct platform_device *pdev)
card->fully_routed = true;
snd_soc_card_set_drvdata(card, jack);
- 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_nau8825_driver_ids[] = {
+ {
+ .name = "avs_nau8825",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(platform, avs_nau8825_driver_ids);
+
static struct platform_driver avs_nau8825_driver = {
.probe = avs_nau8825_probe,
.driver = {
.name = "avs_nau8825",
.pm = &snd_soc_pm_ops,
},
+ .id_table = avs_nau8825_driver_ids,
};
module_platform_driver(avs_nau8825_driver)
+MODULE_DESCRIPTION("Intel nau8825 machine driver");
MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:avs_nau8825");
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 411acaee74f9..73884f8a535c 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>
@@ -9,56 +9,75 @@
#include <linux/device.h>
#include <linux/module.h>
#include <sound/soc.h>
-#include <sound/soc-acpi.h>
-SND_SOC_DAILINK_DEF(dummy, DAILINK_COMP_ARRAY(COMP_DUMMY()));
-SND_SOC_DAILINK_DEF(probe_cp, DAILINK_COMP_ARRAY(COMP_CPU("Probe Extraction CPU DAI")));
-SND_SOC_DAILINK_DEF(platform, DAILINK_COMP_ARRAY(COMP_PLATFORM("probe-platform")));
+static int avs_create_dai_links(struct device *dev, struct snd_soc_dai_link **links, int *num_links)
+{
+ struct snd_soc_dai_link *dl;
-static struct snd_soc_dai_link probe_mb_dai_links[] = {
- {
- .name = "Compress Probe Capture",
- .nonatomic = 1,
- SND_SOC_DAILINK_REG(probe_cp, dummy, platform),
- },
-};
+ dl = devm_kzalloc(dev, sizeof(*dl), GFP_KERNEL);
+ if (!dl)
+ return -ENOMEM;
+
+ dl->cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL);
+ dl->platforms = devm_kzalloc(dev, sizeof(*dl->platforms), GFP_KERNEL);
+ if (!dl->cpus || !dl->platforms)
+ return -ENOMEM;
+
+ dl->name = "Compress Probe Capture";
+ dl->cpus->dai_name = "Probe Extraction CPU DAI";
+ dl->num_cpus = 1;
+ dl->codecs = &snd_soc_dummy_dlc;
+ dl->num_codecs = 1;
+ dl->platforms->name = dev_name(dev);
+ dl->num_platforms = 1;
+ dl->nonatomic = 1;
+
+ *links = dl;
+ *num_links = 1;
+ return 0;
+}
static int avs_probe_mb_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- struct snd_soc_acpi_mach *mach;
struct snd_soc_card *card;
int ret;
- mach = dev_get_platdata(dev);
-
card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
if (!card)
return -ENOMEM;
- card->name = "avs_probe_mb";
+ ret = avs_create_dai_links(dev, &card->dai_link, &card->num_links);
+ if (ret)
+ return ret;
+
+ 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;
- card->num_links = ARRAY_SIZE(probe_mb_dai_links);
card->fully_routed = true;
- ret = snd_soc_fixup_dai_links_platform_name(card, mach->mach_params.platform);
- 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[] = {
+ {
+ .name = "avs_probe_mb",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(platform, avs_probe_mb_driver_ids);
+
static struct platform_driver avs_probe_mb_driver = {
.probe = avs_probe_mb_probe,
.driver = {
.name = "avs_probe_mb",
.pm = &snd_soc_pm_ops,
},
+ .id_table = avs_probe_mb_driver_ids,
};
module_platform_driver(avs_probe_mb_driver);
+MODULE_DESCRIPTION("Intel probe machine driver");
MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:avs_probe_mb");
diff --git a/sound/soc/intel/avs/boards/rt274.c b/sound/soc/intel/avs/boards/rt274.c
index ebfee54814ce..a689f4c80867 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>
@@ -13,6 +13,7 @@
#include <sound/soc.h>
#include <sound/soc-acpi.h>
#include "../../../codecs/rt274.h"
+#include "../utils.h"
#define AVS_RT274_FREQ_OUT 24000000
#define AVS_RT274_BE_FIXUP_RATE 48000
@@ -26,8 +27,7 @@ static const struct snd_kcontrol_new card_controls[] = {
static int
avs_rt274_clock_control(struct snd_soc_dapm_widget *w, struct snd_kcontrol *control, int event)
{
- struct snd_soc_dapm_context *dapm = w->dapm;
- struct snd_soc_card *card = dapm->card;
+ struct snd_soc_card *card = snd_soc_dapm_to_card(w->dapm);
struct snd_soc_dai *codec_dai;
int ret;
@@ -74,7 +74,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,
@@ -87,21 +87,24 @@ static struct snd_soc_jack_pin card_headset_pins[] = {
static int avs_rt274_codec_init(struct snd_soc_pcm_runtime *runtime)
{
- struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(runtime, 0);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(runtime, 0);
struct snd_soc_component *component = codec_dai->component;
struct snd_soc_jack_pin *pins;
struct snd_soc_jack *jack;
struct snd_soc_card *card = runtime->card;
+ struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
int num_pins, ret;
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;
@@ -114,14 +117,14 @@ static int avs_rt274_codec_init(struct snd_soc_pcm_runtime *runtime)
return ret;
}
- card->dapm.idle_bias_off = true;
+ snd_soc_dapm_set_idle_bias(dapm, false);
return 0;
}
static void avs_rt274_codec_exit(struct snd_soc_pcm_runtime *rtd)
{
- snd_soc_component_set_jack(asoc_rtd_to_codec(rtd, 0)->component, NULL, NULL);
+ snd_soc_component_set_jack(snd_soc_rtd_to_codec(rtd, 0)->component, NULL, NULL);
}
static int avs_rt274_be_fixup(struct snd_soc_pcm_runtime *runtime, struct snd_pcm_hw_params *params)
@@ -144,7 +147,7 @@ static int avs_rt274_be_fixup(struct snd_soc_pcm_runtime *runtime, struct snd_pc
return 0;
}
-static int avs_create_dai_link(struct device *dev, const char *platform_name, int ssp_port,
+static int avs_create_dai_link(struct device *dev, int ssp_port, int tdm_slot,
struct snd_soc_dai_link **dai_link)
{
struct snd_soc_dai_link_component *platform;
@@ -155,33 +158,32 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in
if (!dl || !platform)
return -ENOMEM;
- platform->name = platform_name;
-
- dl->name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-Codec", ssp_port);
+ 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), GFP_KERNEL);
if (!dl->name || !dl->cpus || !dl->codecs)
return -ENOMEM;
- dl->cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d Pin", ssp_port);
+ dl->cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
+ AVS_STRING_FMT("SSP", " Pin", ssp_port, tdm_slot));
dl->codecs->name = devm_kasprintf(dev, GFP_KERNEL, "i2c-INT34C2:00");
dl->codecs->dai_name = devm_kasprintf(dev, GFP_KERNEL, RT274_CODEC_DAI);
if (!dl->cpus->dai_name || !dl->codecs->name || !dl->codecs->dai_name)
return -ENOMEM;
+ platform->name = dev_name(dev);
dl->num_cpus = 1;
dl->num_codecs = 1;
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;
@@ -207,17 +209,20 @@ 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;
- const char *pname;
- int ssp_port, ret;
+ int ssp_port, tdm_slot, ret;
mach = dev_get_platdata(dev);
- pname = mach->mach_params.platform;
- ssp_port = __ffs(mach->mach_params.i2s_link_mask);
+ pdata = mach->pdata;
+
+ ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot);
+ if (ret)
+ return ret;
- ret = avs_create_dai_link(dev, pname, ssp_port, &dai_link);
+ ret = avs_create_dai_link(dev, ssp_port, tdm_slot, &dai_link);
if (ret) {
dev_err(dev, "Failed to create dai link: %d", ret);
return ret;
@@ -228,7 +233,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;
@@ -244,22 +254,27 @@ static int avs_rt274_probe(struct platform_device *pdev)
card->fully_routed = true;
snd_soc_card_set_drvdata(card, jack);
- 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_rt274_driver_ids[] = {
+ {
+ .name = "avs_rt274",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(platform, avs_rt274_driver_ids);
+
static struct platform_driver avs_rt274_driver = {
.probe = avs_rt274_probe,
.driver = {
.name = "avs_rt274",
.pm = &snd_soc_pm_ops,
},
+ .id_table = avs_rt274_driver_ids,
};
module_platform_driver(avs_rt274_driver);
+MODULE_DESCRIPTION("Intel rt274 machine driver");
MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:avs_rt274");
diff --git a/sound/soc/intel/avs/boards/rt286.c b/sound/soc/intel/avs/boards/rt286.c
index 84cf9a0c8dfe..4c9ac545555a 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>
@@ -13,6 +13,7 @@
#include <sound/soc.h>
#include <sound/soc-acpi.h>
#include "../../../codecs/rt286.h"
+#include "../utils.h"
#define RT286_CODEC_DAI "rt286-aif1"
@@ -37,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,
@@ -58,21 +59,22 @@ 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;
- return snd_soc_component_set_jack(asoc_rtd_to_codec(runtime, 0)->component, jack, NULL);
+ return snd_soc_component_set_jack(snd_soc_rtd_to_codec(runtime, 0)->component, jack, NULL);
}
static void avs_rt286_codec_exit(struct snd_soc_pcm_runtime *rtd)
{
- snd_soc_component_set_jack(asoc_rtd_to_codec(rtd, 0)->component, NULL, NULL);
+ snd_soc_component_set_jack(snd_soc_rtd_to_codec(rtd, 0)->component, NULL, NULL);
}
static int avs_rt286_be_fixup(struct snd_soc_pcm_runtime *runtime, struct snd_pcm_hw_params *params)
@@ -98,8 +100,8 @@ static int avs_rt286_be_fixup(struct snd_soc_pcm_runtime *runtime, struct snd_pc
static int
avs_rt286_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params)
{
- struct snd_soc_pcm_runtime *runtime = asoc_substream_to_rtd(substream);
- struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(runtime, 0);
+ struct snd_soc_pcm_runtime *runtime = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(runtime, 0);
int ret;
ret = snd_soc_dai_set_sysclk(codec_dai, RT286_SCLK_S_PLL, 24000000, SND_SOC_CLOCK_IN);
@@ -113,7 +115,7 @@ static const struct snd_soc_ops avs_rt286_ops = {
.hw_params = avs_rt286_hw_params,
};
-static int avs_create_dai_link(struct device *dev, const char *platform_name, int ssp_port,
+static int avs_create_dai_link(struct device *dev, int ssp_port, int tdm_slot,
struct snd_soc_dai_link **dai_link)
{
struct snd_soc_dai_link_component *platform;
@@ -124,34 +126,33 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in
if (!dl || !platform)
return -ENOMEM;
- platform->name = platform_name;
-
- dl->name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-Codec", ssp_port);
+ 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), GFP_KERNEL);
if (!dl->name || !dl->cpus || !dl->codecs)
return -ENOMEM;
- dl->cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d Pin", ssp_port);
+ dl->cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
+ AVS_STRING_FMT("SSP", " Pin", ssp_port, tdm_slot));
dl->codecs->name = devm_kasprintf(dev, GFP_KERNEL, "i2c-INT343A:00");
dl->codecs->dai_name = devm_kasprintf(dev, GFP_KERNEL, RT286_CODEC_DAI);
if (!dl->cpus->dai_name || !dl->codecs->name || !dl->codecs->dai_name)
return -ENOMEM;
+ platform->name = dev_name(dev);
dl->num_cpus = 1;
dl->num_codecs = 1;
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;
@@ -177,17 +178,21 @@ 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;
- const char *pname;
- int ssp_port, ret;
+ int ssp_port, tdm_slot, ret;
mach = dev_get_platdata(dev);
- pname = mach->mach_params.platform;
- ssp_port = __ffs(mach->mach_params.i2s_link_mask);
+ pdata = mach->pdata;
+
+ ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot);
+ if (ret)
+ return ret;
+
+ ret = avs_create_dai_link(dev, ssp_port, tdm_slot, &dai_link);
- ret = avs_create_dai_link(dev, pname, ssp_port, &dai_link);
if (ret) {
dev_err(dev, "Failed to create dai link: %d", ret);
return ret;
@@ -198,7 +203,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;
@@ -214,22 +224,27 @@ static int avs_rt286_probe(struct platform_device *pdev)
card->fully_routed = true;
snd_soc_card_set_drvdata(card, jack);
- 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_rt286_driver_ids[] = {
+ {
+ .name = "avs_rt286",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(platform, avs_rt286_driver_ids);
+
static struct platform_driver avs_rt286_driver = {
.probe = avs_rt286_probe,
.driver = {
.name = "avs_rt286",
.pm = &snd_soc_pm_ops,
},
+ .id_table = avs_rt286_driver_ids,
};
module_platform_driver(avs_rt286_driver);
+MODULE_DESCRIPTION("Intel rt286 machine driver");
MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:avs_rt286");
diff --git a/sound/soc/intel/avs/boards/rt298.c b/sound/soc/intel/avs/boards/rt298.c
index 3b0e2b1a3251..2d7a7748d577 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>
@@ -14,6 +14,7 @@
#include <sound/soc.h>
#include <sound/soc-acpi.h>
#include "../../../codecs/rt298.h"
+#include "../utils.h"
#define RT298_CODEC_DAI "rt298-aif1"
@@ -48,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,
@@ -69,21 +70,22 @@ 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;
- return snd_soc_component_set_jack(asoc_rtd_to_codec(runtime, 0)->component, jack, NULL);
+ return snd_soc_component_set_jack(snd_soc_rtd_to_codec(runtime, 0)->component, jack, NULL);
}
static void avs_rt298_codec_exit(struct snd_soc_pcm_runtime *rtd)
{
- snd_soc_component_set_jack(asoc_rtd_to_codec(rtd, 0)->component, NULL, NULL);
+ snd_soc_component_set_jack(snd_soc_rtd_to_codec(rtd, 0)->component, NULL, NULL);
}
static int avs_rt298_be_fixup(struct snd_soc_pcm_runtime *runtime, struct snd_pcm_hw_params *params)
@@ -109,8 +111,8 @@ static int avs_rt298_be_fixup(struct snd_soc_pcm_runtime *runtime, struct snd_pc
static int
avs_rt298_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params)
{
- struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
- struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
unsigned int clk_freq;
int ret;
@@ -130,7 +132,7 @@ static const struct snd_soc_ops avs_rt298_ops = {
.hw_params = avs_rt298_hw_params,
};
-static int avs_create_dai_link(struct device *dev, const char *platform_name, int ssp_port,
+static int avs_create_dai_link(struct device *dev, int ssp_port, int tdm_slot,
struct snd_soc_dai_link **dai_link)
{
struct snd_soc_dai_link_component *platform;
@@ -141,37 +143,36 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in
if (!dl || !platform)
return -ENOMEM;
- platform->name = platform_name;
-
- dl->name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-Codec", ssp_port);
+ 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), GFP_KERNEL);
if (!dl->name || !dl->cpus || !dl->codecs)
return -ENOMEM;
- dl->cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d Pin", ssp_port);
+ dl->cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
+ AVS_STRING_FMT("SSP", " Pin", ssp_port, tdm_slot));
dl->codecs->name = devm_kasprintf(dev, GFP_KERNEL, "i2c-INT343A:00");
dl->codecs->dai_name = devm_kasprintf(dev, GFP_KERNEL, RT298_CODEC_DAI);
if (!dl->cpus->dai_name || !dl->codecs->name || !dl->codecs->dai_name)
return -ENOMEM;
+ platform->name = dev_name(dev);
dl->num_cpus = 1;
dl->num_codecs = 1;
dl->platforms = platform;
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;
@@ -197,17 +198,20 @@ 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;
- const char *pname;
- int ssp_port, ret;
+ int ssp_port, tdm_slot, ret;
mach = dev_get_platdata(dev);
- pname = mach->mach_params.platform;
- ssp_port = __ffs(mach->mach_params.i2s_link_mask);
+ pdata = mach->pdata;
+
+ ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot);
+ if (ret)
+ return ret;
- ret = avs_create_dai_link(dev, pname, ssp_port, &dai_link);
+ ret = avs_create_dai_link(dev, ssp_port, tdm_slot, &dai_link);
if (ret) {
dev_err(dev, "Failed to create dai link: %d", ret);
return ret;
@@ -218,7 +222,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;
@@ -234,22 +243,27 @@ static int avs_rt298_probe(struct platform_device *pdev)
card->fully_routed = true;
snd_soc_card_set_drvdata(card, jack);
- 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_rt298_driver_ids[] = {
+ {
+ .name = "avs_rt298",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(platform, avs_rt298_driver_ids);
+
static struct platform_driver avs_rt298_driver = {
.probe = avs_rt298_probe,
.driver = {
.name = "avs_rt298",
.pm = &snd_soc_pm_ops,
},
+ .id_table = avs_rt298_driver_ids,
};
module_platform_driver(avs_rt298_driver);
+MODULE_DESCRIPTION("Intel rt298 machine driver");
MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:avs_rt298");
diff --git a/sound/soc/intel/avs/boards/rt5514.c b/sound/soc/intel/avs/boards/rt5514.c
new file mode 100644
index 000000000000..22139eaad83a
--- /dev/null
+++ b/sound/soc/intel/avs/boards/rt5514.c
@@ -0,0 +1,197 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2021-2023 Intel Corporation
+//
+// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+//
+
+#include <linux/clk.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-acpi.h>
+#include "../../../codecs/rt5514.h"
+#include "../utils.h"
+
+#define RT5514_CODEC_DAI "rt5514-aif1"
+
+static const struct snd_soc_dapm_widget card_widgets[] = {
+ SND_SOC_DAPM_MIC("DMIC", NULL),
+};
+
+static const struct snd_soc_dapm_route card_base_routes[] = {
+ /* DMIC */
+ { "DMIC1L", NULL, "DMIC" },
+ { "DMIC1R", NULL, "DMIC" },
+ { "DMIC2L", NULL, "DMIC" },
+ { "DMIC2R", NULL, "DMIC" },
+};
+
+static int avs_rt5514_codec_init(struct snd_soc_pcm_runtime *runtime)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(runtime->card);
+ int ret = snd_soc_dapm_ignore_suspend(dapm, "DMIC");
+
+ if (ret)
+ dev_err(runtime->dev, "DMIC - Ignore suspend failed = %d\n", ret);
+
+ return ret;
+}
+
+static int avs_rt5514_be_fixup(struct snd_soc_pcm_runtime *runtime,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate, *channels;
+ struct snd_mask *fmt;
+
+ rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+ channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+
+ rate->min = rate->max = 48000;
+ channels->min = channels->max = 4;
+
+ snd_mask_none(fmt);
+ snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S16_LE);
+
+ return 0;
+}
+
+static int avs_rt5514_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ int ret;
+
+ ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xF, 0, 8, 16);
+ if (ret < 0) {
+ dev_err(rtd->dev, "set TDM slot err:%d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, RT5514_SCLK_S_MCLK, 24576000, SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ dev_err(rtd->dev, "set sysclk err: %d\n", ret);
+
+ return ret;
+}
+
+static const struct snd_soc_ops avs_rt5514_ops = {
+ .hw_params = avs_rt5514_hw_params,
+};
+
+static int avs_create_dai_link(struct device *dev, int ssp_port, int tdm_slot,
+ struct snd_soc_dai_link **dai_link)
+{
+ struct snd_soc_dai_link_component *platform;
+ struct snd_soc_dai_link *dl;
+
+ dl = devm_kzalloc(dev, sizeof(*dl), GFP_KERNEL);
+ platform = devm_kzalloc(dev, sizeof(*platform), GFP_KERNEL);
+ if (!dl || !platform)
+ return -ENOMEM;
+
+ 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), GFP_KERNEL);
+ if (!dl->name || !dl->cpus || !dl->codecs)
+ return -ENOMEM;
+
+ dl->cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
+ AVS_STRING_FMT("SSP", " Pin", ssp_port, tdm_slot));
+ dl->codecs->name = devm_kasprintf(dev, GFP_KERNEL, "i2c-10EC5514:00");
+ dl->codecs->dai_name = devm_kasprintf(dev, GFP_KERNEL, RT5514_CODEC_DAI);
+ if (!dl->cpus->dai_name || !dl->codecs->name || !dl->codecs->dai_name)
+ return -ENOMEM;
+
+ platform->name = dev_name(dev);
+ dl->num_cpus = 1;
+ dl->num_codecs = 1;
+ 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_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->capture_only = 1;
+ dl->ops = &avs_rt5514_ops;
+
+ *dai_link = dl;
+
+ return 0;
+}
+
+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;
+ int ssp_port, tdm_slot, ret;
+
+ mach = dev_get_platdata(dev);
+ pdata = mach->pdata;
+
+ ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot);
+ if (ret)
+ return ret;
+
+ ret = avs_create_dai_link(dev, ssp_port, tdm_slot, &dai_link);
+ if (ret) {
+ dev_err(dev, "Failed to create dai link: %d", ret);
+ return ret;
+ }
+
+ card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
+ if (!card)
+ return -ENOMEM;
+
+ 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;
+ card->num_links = 1;
+ card->dapm_widgets = card_widgets;
+ card->num_dapm_widgets = ARRAY_SIZE(card_widgets);
+ card->dapm_routes = card_base_routes;
+ card->num_dapm_routes = ARRAY_SIZE(card_base_routes);
+ card->fully_routed = true;
+
+ return devm_snd_soc_register_deferrable_card(dev, card);
+}
+
+static const struct platform_device_id avs_rt5514_driver_ids[] = {
+ {
+ .name = "avs_rt5514",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(platform, avs_rt5514_driver_ids);
+
+static struct platform_driver avs_rt5514_driver = {
+ .probe = avs_rt5514_probe,
+ .driver = {
+ .name = "avs_rt5514",
+ .pm = &snd_soc_pm_ops,
+ },
+ .id_table = avs_rt5514_driver_ids,
+};
+
+module_platform_driver(avs_rt5514_driver);
+
+MODULE_DESCRIPTION("Intel rt5514 machine driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/intel/avs/boards/rt5640.c b/sound/soc/intel/avs/boards/rt5640.c
new file mode 100644
index 000000000000..2990d32f2301
--- /dev/null
+++ b/sound/soc/intel/avs/boards/rt5640.c
@@ -0,0 +1,271 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2022-2025 Intel Corporation
+//
+// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+//
+
+#include <linux/module.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-acpi.h>
+#include "../../../codecs/rt5640.h"
+#include "../utils.h"
+
+#define AVS_RT5640_MCLK_HZ 19200000
+#define RT5640_CODEC_DAI "rt5640-aif1"
+
+static const struct snd_soc_dapm_widget card_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_MIC("Mic Jack", NULL),
+ SND_SOC_DAPM_SPK("Speaker", NULL),
+};
+
+static const struct snd_soc_dapm_route card_routes[] = {
+ { "Headphone Jack", NULL, "HPOR" },
+ { "Headphone Jack", NULL, "HPOL" },
+ { "IN2P", NULL, "Mic Jack" },
+ { "IN2P", NULL, "MICBIAS1" },
+ { "Speaker", NULL, "SPOLP" },
+ { "Speaker", NULL, "SPOLN" },
+ { "Speaker", NULL, "SPORP" },
+ { "Speaker", NULL, "SPORN" },
+};
+
+static const struct snd_soc_jack_pin card_headset_pins[] = {
+ {
+ .pin = "Headphone Jack",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Mic Jack",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
+static int avs_rt5640_codec_init(struct snd_soc_pcm_runtime *runtime)
+{
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(runtime, 0);
+ struct snd_soc_card *card = runtime->card;
+ struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
+ struct snd_soc_jack_pin *pins;
+ struct snd_soc_jack *jack;
+ int num_pins, ret;
+
+ 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);
+ if (!pins)
+ return -ENOMEM;
+
+ ret = snd_soc_card_jack_new_pins(card, "Headset Jack", SND_JACK_HEADSET, jack, pins,
+ num_pins);
+ if (ret)
+ return ret;
+
+ snd_soc_component_set_jack(codec_dai->component, jack, NULL);
+ snd_soc_dapm_set_idle_bias(dapm, false);
+
+ return 0;
+}
+
+static void avs_rt5640_codec_exit(struct snd_soc_pcm_runtime *runtime)
+{
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(runtime, 0);
+
+ snd_soc_component_set_jack(codec_dai->component, NULL, NULL);
+}
+
+static int avs_rt5640_be_fixup(struct snd_soc_pcm_runtime *runtime,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_mask *fmask = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+
+ /* Format 24/32 is MSB-aligned for HDAudio and LSB-aligned for I2S. */
+ if (params_format(params) == SNDRV_PCM_FORMAT_S32_LE)
+ snd_mask_set_format(fmask, SNDRV_PCM_FORMAT_S24_LE);
+
+ return 0;
+}
+
+static int avs_rt5640_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *runtime = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(runtime, 0);
+ int ret;
+
+ ret = snd_soc_dai_set_pll(codec_dai, 0, RT5640_PLL1_S_MCLK, AVS_RT5640_MCLK_HZ,
+ params_rate(params) * 512);
+ if (ret < 0) {
+ dev_err(runtime->dev, "Set codec PLL failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, RT5640_SCLK_S_PLL1, params_rate(params) * 512,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(runtime->dev, "Set codec SCLK failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = rt5640_sel_asrc_clk_src(codec_dai->component,
+ RT5640_DA_STEREO_FILTER | RT5640_AD_STEREO_FILTER |
+ RT5640_DA_MONO_L_FILTER | RT5640_DA_MONO_R_FILTER |
+ RT5640_AD_MONO_L_FILTER | RT5640_AD_MONO_R_FILTER,
+ RT5640_CLK_SEL_ASRC);
+ if (ret)
+ dev_err(runtime->dev, "Set codec ASRC failed: %d\n", ret);
+
+ return ret;
+}
+
+static const struct snd_soc_ops avs_rt5640_ops = {
+ .hw_params = avs_rt5640_hw_params,
+};
+
+static int avs_create_dai_link(struct device *dev, int ssp_port, int tdm_slot,
+ struct snd_soc_acpi_mach *mach,
+ struct snd_soc_dai_link **dai_link)
+{
+ struct snd_soc_dai_link_component *platform;
+ struct snd_soc_dai_link *dl;
+ u32 uid = 0;
+ int ret;
+
+ if (mach->uid) {
+ ret = kstrtou32(mach->uid, 0, &uid);
+ if (ret)
+ return ret;
+ uid--; /* 0-based indexing. */
+ }
+
+ dl = devm_kzalloc(dev, sizeof(*dl), GFP_KERNEL);
+ platform = devm_kzalloc(dev, sizeof(*platform), GFP_KERNEL);
+ if (!dl || !platform)
+ return -ENOMEM;
+
+ 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), GFP_KERNEL);
+ if (!dl->name || !dl->cpus || !dl->codecs)
+ return -ENOMEM;
+
+ dl->cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
+ AVS_STRING_FMT("SSP", " Pin", ssp_port, tdm_slot));
+ dl->codecs->name = devm_kasprintf(dev, GFP_KERNEL, "i2c-10EC5640:0%d", uid);
+ dl->codecs->dai_name = devm_kasprintf(dev, GFP_KERNEL, RT5640_CODEC_DAI);
+ if (!dl->cpus->dai_name || !dl->codecs->name || !dl->codecs->dai_name)
+ return -ENOMEM;
+
+ platform->name = dev_name(dev);
+ dl->num_cpus = 1;
+ dl->num_codecs = 1;
+ 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_CBC_CFC;
+ dl->init = avs_rt5640_codec_init;
+ dl->exit = avs_rt5640_codec_exit;
+ dl->be_hw_params_fixup = avs_rt5640_be_fixup;
+ dl->ops = &avs_rt5640_ops;
+ dl->nonatomic = 1;
+ dl->no_pcm = 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, RT5640_CODEC_DAI);
+
+ 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, RT5640_CODEC_DAI);
+ 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_rt5640_probe(struct platform_device *pdev)
+{
+ struct snd_soc_dai_link *dai_link;
+ struct device *dev = &pdev->dev;
+ struct snd_soc_acpi_mach *mach;
+ struct snd_soc_card *card;
+ struct snd_soc_jack *jack;
+ int ssp_port, tdm_slot, ret;
+
+ mach = dev_get_platdata(dev);
+
+ ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot);
+ if (ret)
+ return ret;
+
+ ret = avs_create_dai_link(dev, ssp_port, tdm_slot, mach, &dai_link);
+ if (ret) {
+ dev_err(dev, "Failed to create dai link: %d", ret);
+ return ret;
+ }
+
+ jack = devm_kzalloc(dev, sizeof(*jack), GFP_KERNEL);
+ card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
+ if (!jack || !card)
+ return -ENOMEM;
+
+ if (mach->uid) {
+ card->name = devm_kasprintf(dev, GFP_KERNEL, "AVS I2S ALC5640.%s", mach->uid);
+ if (!card->name)
+ return -ENOMEM;
+ } else {
+ card->name = "AVS I2S ALC5640";
+ }
+ card->driver_name = "avs_rt5640";
+ card->long_name = card->name;
+ 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->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;
+ snd_soc_card_set_drvdata(card, jack);
+
+ return devm_snd_soc_register_deferrable_card(dev, card);
+}
+
+static const struct platform_device_id avs_rt5640_driver_ids[] = {
+ {
+ .name = "avs_rt5640",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(platform, avs_rt5640_driver_ids);
+
+static struct platform_driver avs_rt5640_driver = {
+ .probe = avs_rt5640_probe,
+ .driver = {
+ .name = "avs_rt5640",
+ .pm = &snd_soc_pm_ops,
+ },
+ .id_table = avs_rt5640_driver_ids,
+};
+
+module_platform_driver(avs_rt5640_driver);
+
+MODULE_DESCRIPTION("Intel rt5640 machine driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/intel/avs/boards/rt5663.c b/sound/soc/intel/avs/boards/rt5663.c
index 770b36d05bf4..68fea325376a 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>
@@ -15,6 +15,7 @@
#include <sound/soc.h>
#include <sound/soc-acpi.h>
#include "../../../codecs/rt5663.h"
+#include "../utils.h"
#define RT5663_CODEC_DAI "rt5663-aif"
@@ -42,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,
@@ -64,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;
@@ -79,14 +81,14 @@ static int avs_rt5663_codec_init(struct snd_soc_pcm_runtime *runtime)
snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEUP);
snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN);
- snd_soc_component_set_jack(asoc_rtd_to_codec(runtime, 0)->component, jack, NULL);
+ snd_soc_component_set_jack(snd_soc_rtd_to_codec(runtime, 0)->component, jack, NULL);
return 0;
}
static void avs_rt5663_codec_exit(struct snd_soc_pcm_runtime *runtime)
{
- snd_soc_component_set_jack(asoc_rtd_to_codec(runtime, 0)->component, NULL, NULL);
+ snd_soc_component_set_jack(snd_soc_rtd_to_codec(runtime, 0)->component, NULL, NULL);
}
static int
@@ -113,8 +115,8 @@ avs_rt5663_be_fixup(struct snd_soc_pcm_runtime *runtime, struct snd_pcm_hw_param
static int avs_rt5663_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
- struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
- struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
int ret;
/* use ASRC for internal clocks, as PLL rate isn't multiple of BCLK */
@@ -132,7 +134,7 @@ static const struct snd_soc_ops avs_rt5663_ops = {
};
-static int avs_create_dai_link(struct device *dev, const char *platform_name, int ssp_port,
+static int avs_create_dai_link(struct device *dev, int ssp_port, int tdm_slot,
struct snd_soc_dai_link **dai_link)
{
struct snd_soc_dai_link_component *platform;
@@ -143,20 +145,21 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in
if (!dl || !platform)
return -ENOMEM;
- platform->name = platform_name;
-
- dl->name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-Codec", ssp_port);
+ 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), GFP_KERNEL);
if (!dl->name || !dl->cpus || !dl->codecs)
return -ENOMEM;
- dl->cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d Pin", ssp_port);
+ dl->cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
+ AVS_STRING_FMT("SSP", " Pin", ssp_port, tdm_slot));
dl->codecs->name = devm_kasprintf(dev, GFP_KERNEL, "i2c-10EC5663:00");
dl->codecs->dai_name = devm_kasprintf(dev, GFP_KERNEL, RT5663_CODEC_DAI);
if (!dl->cpus->dai_name || !dl->codecs->name || !dl->codecs->dai_name)
return -ENOMEM;
+ platform->name = dev_name(dev);
dl->num_cpus = 1;
dl->num_codecs = 1;
dl->platforms = platform;
@@ -168,8 +171,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;
@@ -196,17 +197,20 @@ 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;
- const char *pname;
- int ssp_port, ret;
+ int ssp_port, tdm_slot, ret;
mach = dev_get_platdata(dev);
- pname = mach->mach_params.platform;
- ssp_port = __ffs(mach->mach_params.i2s_link_mask);
+ pdata = mach->pdata;
+
+ ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot);
+ if (ret)
+ return ret;
- ret = avs_create_dai_link(dev, pname, ssp_port, &dai_link);
+ ret = avs_create_dai_link(dev, ssp_port, tdm_slot, &dai_link);
if (ret) {
dev_err(dev, "Failed to create dai link: %d", ret);
return ret;
@@ -217,7 +221,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;
@@ -233,22 +242,27 @@ static int avs_rt5663_probe(struct platform_device *pdev)
card->fully_routed = true;
snd_soc_card_set_drvdata(card, priv);
- 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_rt5663_driver_ids[] = {
+ {
+ .name = "avs_rt5663",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(platform, avs_rt5663_driver_ids);
+
static struct platform_driver avs_rt5663_driver = {
.probe = avs_rt5663_probe,
.driver = {
.name = "avs_rt5663",
.pm = &snd_soc_pm_ops,
},
+ .id_table = avs_rt5663_driver_ids,
};
module_platform_driver(avs_rt5663_driver);
+MODULE_DESCRIPTION("Intel rt5663 machine driver");
MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:avs_rt5663");
diff --git a/sound/soc/intel/avs/boards/rt5682.c b/sound/soc/intel/avs/boards/rt5682.c
index b93468ae0977..81863728da1d 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>
@@ -21,6 +21,7 @@
#include <sound/soc-acpi.h>
#include "../../common/soc-intel-quirks.h"
#include "../../../codecs/rt5682.h"
+#include "../utils.h"
#define AVS_RT5682_SSP_CODEC(quirk) ((quirk) & GENMASK(2, 0))
#define AVS_RT5682_SSP_CODEC_MASK (GENMASK(2, 0))
@@ -79,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,
@@ -92,7 +93,7 @@ static struct snd_soc_jack_pin card_jack_pins[] = {
static int avs_rt5682_codec_init(struct snd_soc_pcm_runtime *runtime)
{
- struct snd_soc_component *component = asoc_rtd_to_codec(runtime, 0)->component;
+ struct snd_soc_component *component = snd_soc_rtd_to_codec(runtime, 0)->component;
struct snd_soc_card *card = runtime->card;
struct snd_soc_jack_pin *pins;
struct snd_soc_jack *jack;
@@ -101,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;
@@ -137,14 +139,14 @@ static int avs_rt5682_codec_init(struct snd_soc_pcm_runtime *runtime)
static void avs_rt5682_codec_exit(struct snd_soc_pcm_runtime *rtd)
{
- snd_soc_component_set_jack(asoc_rtd_to_codec(rtd, 0)->component, NULL, NULL);
+ snd_soc_component_set_jack(snd_soc_rtd_to_codec(rtd, 0)->component, NULL, NULL);
}
static int
avs_rt5682_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params)
{
- struct snd_soc_pcm_runtime *runtime = asoc_substream_to_rtd(substream);
- struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(runtime, 0);
+ struct snd_soc_pcm_runtime *runtime = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(runtime, 0);
int pll_source, freq_in, freq_out;
int ret;
@@ -202,7 +204,7 @@ avs_rt5682_be_fixup(struct snd_soc_pcm_runtime *runtime, struct snd_pcm_hw_param
return 0;
}
-static int avs_create_dai_link(struct device *dev, const char *platform_name, int ssp_port,
+static int avs_create_dai_link(struct device *dev, int ssp_port, int tdm_slot,
struct snd_soc_dai_link **dai_link)
{
struct snd_soc_dai_link_component *platform;
@@ -213,20 +215,21 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in
if (!dl || !platform)
return -ENOMEM;
- platform->name = platform_name;
-
- dl->name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-Codec", ssp_port);
+ 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), GFP_KERNEL);
if (!dl->name || !dl->cpus || !dl->codecs)
return -ENOMEM;
- dl->cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d Pin", ssp_port);
+ dl->cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
+ AVS_STRING_FMT("SSP", " Pin", ssp_port, tdm_slot));
dl->codecs->name = devm_kasprintf(dev, GFP_KERNEL, "i2c-10EC5682:00");
dl->codecs->dai_name = devm_kasprintf(dev, GFP_KERNEL, AVS_RT5682_CODEC_DAI_NAME);
if (!dl->cpus->dai_name || !dl->codecs->name || !dl->codecs->dai_name)
return -ENOMEM;
+ platform->name = dev_name(dev);
dl->num_cpus = 1;
dl->num_codecs = 1;
dl->platforms = platform;
@@ -239,8 +242,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;
@@ -266,11 +267,11 @@ 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;
- const char *pname;
- int ssp_port, ret;
+ int ssp_port, tdm_slot, ret;
if (pdev->id_entry && pdev->id_entry->driver_data)
avs_rt5682_quirk = (unsigned long)pdev->id_entry->driver_data;
@@ -279,10 +280,13 @@ static int avs_rt5682_probe(struct platform_device *pdev)
dev_dbg(dev, "avs_rt5682_quirk = %lx\n", avs_rt5682_quirk);
mach = dev_get_platdata(dev);
- pname = mach->mach_params.platform;
- ssp_port = __ffs(mach->mach_params.i2s_link_mask);
+ pdata = mach->pdata;
+
+ ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot);
+ if (ret)
+ return ret;
- ret = avs_create_dai_link(dev, pname, ssp_port, &dai_link);
+ ret = avs_create_dai_link(dev, ssp_port, tdm_slot, &dai_link);
if (ret) {
dev_err(dev, "Failed to create dai link: %d", ret);
return ret;
@@ -293,7 +297,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;
@@ -309,23 +318,28 @@ static int avs_rt5682_probe(struct platform_device *pdev)
card->fully_routed = true;
snd_soc_card_set_drvdata(card, jack);
- 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_rt5682_driver_ids[] = {
+ {
+ .name = "avs_rt5682",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(platform, avs_rt5682_driver_ids);
+
static struct platform_driver avs_rt5682_driver = {
.probe = avs_rt5682_probe,
.driver = {
.name = "avs_rt5682",
.pm = &snd_soc_pm_ops,
},
+ .id_table = avs_rt5682_driver_ids,
};
module_platform_driver(avs_rt5682_driver)
+MODULE_DESCRIPTION("Intel rt5682 machine driver");
MODULE_AUTHOR("Cezary Rojewski <cezary.rojewski@intel.com>");
MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:avs_rt5682");
diff --git a/sound/soc/intel/avs/boards/ssm4567.c b/sound/soc/intel/avs/boards/ssm4567.c
index 7324869d6132..ae0e6e27a8b8 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>
@@ -14,6 +14,7 @@
#include <sound/soc.h>
#include <sound/soc-acpi.h>
#include "../../../codecs/nau8825.h"
+#include "../utils.h"
#define SKL_SSM_CODEC_DAI "ssm4567-hifi"
@@ -36,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[] = {
@@ -50,12 +49,12 @@ static int avs_ssm4567_codec_init(struct snd_soc_pcm_runtime *runtime)
int ret;
/* Slot 1 for left */
- ret = snd_soc_dai_set_tdm_slot(asoc_rtd_to_codec(runtime, 0), 0x01, 0x01, 2, 48);
+ ret = snd_soc_dai_set_tdm_slot(snd_soc_rtd_to_codec(runtime, 0), 0x01, 0x01, 2, 48);
if (ret < 0)
return ret;
/* Slot 2 for right */
- ret = snd_soc_dai_set_tdm_slot(asoc_rtd_to_codec(runtime, 1), 0x02, 0x02, 2, 48);
+ ret = snd_soc_dai_set_tdm_slot(snd_soc_rtd_to_codec(runtime, 1), 0x02, 0x02, 2, 48);
if (ret < 0)
return ret;
@@ -82,7 +81,7 @@ avs_ssm4567_be_fixup(struct snd_soc_pcm_runtime *runrime, struct snd_pcm_hw_para
return 0;
}
-static int avs_create_dai_link(struct device *dev, const char *platform_name, int ssp_port,
+static int avs_create_dai_link(struct device *dev, int ssp_port, int tdm_slot,
struct snd_soc_dai_link **dai_link)
{
struct snd_soc_dai_link_component *platform;
@@ -93,15 +92,15 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in
if (!dl || !platform)
return -ENOMEM;
- platform->name = platform_name;
-
- dl->name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-Codec", ssp_port);
+ 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;
- dl->cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d Pin", ssp_port);
+ dl->cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
+ AVS_STRING_FMT("SSP", " Pin", ssp_port, tdm_slot));
dl->codecs[0].name = devm_kasprintf(dev, GFP_KERNEL, "i2c-INT343B:00");
dl->codecs[0].dai_name = devm_kasprintf(dev, GFP_KERNEL, "ssm4567-hifi");
dl->codecs[1].name = devm_kasprintf(dev, GFP_KERNEL, "i2c-INT343B:01");
@@ -110,18 +109,17 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in
!dl->codecs[1].name || !dl->codecs[1].dai_name)
return -ENOMEM;
+ platform->name = dev_name(dev);
dl->num_cpus = 1;
dl->num_codecs = 2;
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;
@@ -133,16 +131,19 @@ 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;
- int ssp_port, ret;
+ int ssp_port, tdm_slot, ret;
mach = dev_get_platdata(dev);
- pname = mach->mach_params.platform;
- ssp_port = __ffs(mach->mach_params.i2s_link_mask);
+ pdata = mach->pdata;
+
+ ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot);
+ if (ret)
+ return ret;
- ret = avs_create_dai_link(dev, pname, ssp_port, &dai_link);
+ ret = avs_create_dai_link(dev, ssp_port, tdm_slot, &dai_link);
if (ret) {
dev_err(dev, "Failed to create dai link: %d", ret);
return ret;
@@ -152,7 +153,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;
@@ -166,24 +172,28 @@ 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[] = {
+ {
+ .name = "avs_ssm4567",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(platform, avs_ssm4567_driver_ids);
+
static struct platform_driver avs_ssm4567_driver = {
.probe = avs_ssm4567_probe,
.driver = {
.name = "avs_ssm4567",
.pm = &snd_soc_pm_ops,
},
+ .id_table = avs_ssm4567_driver_ids,
};
module_platform_driver(avs_ssm4567_driver)
+MODULE_DESCRIPTION("Intel ssm4567 machine driver");
MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:avs_ssm4567");
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 754fcf9ee585..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>
*/
@@ -8,7 +8,9 @@
#ifndef __SOUND_SOC_INTEL_AVS_CLDMA_H
#define __SOUND_SOC_INTEL_AVS_CLDMA_H
-#define AVS_CL_DEFAULT_BUFFER_SIZE (32 * PAGE_SIZE)
+#include <linux/sizes.h>
+
+#define AVS_CL_DEFAULT_BUFFER_SIZE SZ_128K
struct hda_cldma;
extern struct hda_cldma code_loader;
@@ -22,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
new file mode 100644
index 000000000000..5b5359e9128b
--- /dev/null
+++ b/sound/soc/intel/avs/cnl.c
@@ -0,0 +1,92 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2021-2024 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 "debug.h"
+#include "messages.h"
+#include "registers.h"
+
+static void avs_cnl_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;
+ 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,
+ .dsp_interrupt = avs_cnl_dsp_interrupt,
+ .int_control = avs_dsp_interrupt_control,
+ .load_basefw = avs_hda_load_basefw,
+ .load_lib = avs_hda_load_library,
+ .transfer_mods = avs_hda_transfer_modules,
+ .log_buffer_offset = avs_skl_log_buffer_offset,
+ .log_buffer_status = avs_apl_log_buffer_status,
+ .coredump = avs_apl_coredump,
+ .d0ix_toggle = avs_apl_d0ix_toggle,
+ .set_d0ix = avs_apl_set_d0ix,
+ AVS_SET_ENABLE_LOGS_OP(apl)
+};
diff --git a/sound/soc/intel/avs/control.c b/sound/soc/intel/avs/control.c
index 3dfa2e9816db..a8f05de338e0 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"
@@ -14,11 +15,10 @@
static struct avs_dev *avs_get_kcontrol_adev(struct snd_kcontrol *kcontrol)
{
- struct snd_soc_dapm_widget *w;
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_to_dapm(kcontrol);
+ struct device *dev = snd_soc_dapm_to_dev(dapm);
- w = snd_soc_dapm_kcontrol_widget(kcontrol);
-
- return to_avs_dev(w->dapm->component->dev);
+ return to_avs_dev(dev);
}
static struct avs_path_module *avs_get_volume_module(struct avs_dev *adev, u32 id)
@@ -31,8 +31,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 +47,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;
- /* prevent access to modules while path is being constructed */
- mutex_lock(&adev->path_mutex);
+ 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) {
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);
+
+ 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;
+ }
- if (ctlvol < 0 || ctlvol > mc->max)
- return -EINVAL;
+ memcpy(ctl_data->values, input, sizeof(ctl_data->values));
+ return 1;
+}
- /* prevent access to modules while path is being constructed */
- mutex_lock(&adev->path_mutex);
+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;
- if (*volume != ctlvol) {
- *volume = ctlvol;
- changed = 1;
- }
+ 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 859b217fc761..6e0e65584c7f 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,18 +14,22 @@
// 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 "debug.h"
+#include "messages.h"
+#include "pcm.h"
static u32 pgctl_mask = AZX_PGCTL_LSRMD_MASK;
module_param(pgctl_mask, uint, 0444);
@@ -51,14 +55,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)
@@ -68,9 +75,16 @@ void avs_hda_clock_gating_enable(struct avs_dev *adev, bool enable)
void avs_hda_l1sen_enable(struct avs_dev *adev, bool enable)
{
- u32 value = enable ? AZX_VS_EM2_L1SEN : 0;
-
- snd_hdac_chip_updatel(&adev->base.core, VS_EM2, AZX_VS_EM2_L1SEN, value);
+ 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,
+ AZX_VS_EM2_L1SEN);
+ } else {
+ if (atomic_dec_return(&adev->l1sen_counter) == -1)
+ snd_hdac_chip_updatel(&adev->base.core, VS_EM2, AZX_VS_EM2_L1SEN, 0);
+ }
}
static int avs_hdac_bus_init_streams(struct hdac_bus *bus)
@@ -91,6 +105,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;
@@ -106,7 +121,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;
}
@@ -143,7 +159,7 @@ static int probe_codec(struct hdac_bus *bus, int addr)
/* configure effectively creates new ASoC component */
ret = snd_hda_codec_configure(codec);
if (ret < 0) {
- dev_err(bus->dev, "failed to config codec %d\n", ret);
+ dev_warn(bus->dev, "failed to config codec #%d: %d\n", addr, ret);
return ret;
}
@@ -152,15 +168,16 @@ static int probe_codec(struct hdac_bus *bus, int addr)
static void avs_hdac_bus_probe_codecs(struct hdac_bus *bus)
{
- int c;
+ int ret, c;
/* First try to probe all given codec slots */
for (c = 0; c < HDA_MAX_CODECS; c++) {
if (!(bus->codec_mask & BIT(c)))
continue;
- if (!probe_codec(bus, c))
- /* success, continue probing */
+ ret = probe_codec(bus, c);
+ /* Ignore codecs with no supporting driver. */
+ if (!ret || ret == -ENODEV)
continue;
/*
@@ -191,10 +208,6 @@ static void avs_hda_probe_work(struct work_struct *work)
pm_runtime_set_active(bus->dev); /* clear runtime_error flag */
- ret = snd_hdac_i915_init(bus);
- if (ret < 0)
- dev_info(bus->dev, "i915 init unsuccessful: %d\n", ret);
-
snd_hdac_display_power(bus, HDA_CODEC_IDX_CONTROLLER, true);
avs_hdac_bus_init_chip(bus, true);
avs_hdac_bus_probe_codecs(bus);
@@ -206,22 +219,19 @@ 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);
/* configure PM */
pm_runtime_set_autosuspend_delay(bus->dev, 2000);
pm_runtime_use_autosuspend(bus->dev);
- pm_runtime_mark_last_busy(bus->dev);
pm_runtime_put_autosuspend(bus->dev);
pm_runtime_allow(bus->dev);
}
@@ -245,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;
@@ -254,67 +264,78 @@ 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 = context;
+ 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 = 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);
+
+ /* Unmask GIE, masked in irq_handler(). */
+ snd_hdac_chip_updatel(bus, INTCTL, AZX_INT_GLOBAL_EN, AZX_INT_GLOBAL_EN);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t avs_dsp_irq_handler(int irq, void *dev_id)
+{
+ struct avs_dev *adev = dev_id;
+
+ return avs_hda_irq_handler(irq, &adev->base.core);
+}
- snd_hdac_bus_handle_stream_irq(bus, status, hdac_update_stream);
+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;
- /* 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);
+ 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;
}
@@ -326,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);
@@ -380,6 +401,10 @@ static int avs_bus_init(struct avs_dev *adev, struct pci_dev *pci, const struct
if (ret < 0)
return ret;
+ adev->modcfg_buf = devm_kzalloc(dev, AVS_MAILBOX_SIZE, GFP_KERNEL);
+ if (!adev->modcfg_buf)
+ return -ENOMEM;
+
adev->dev = dev;
adev->spec = (const struct avs_spec *)id->driver_data;
adev->ipc = ipc;
@@ -405,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)
@@ -415,23 +446,23 @@ static int avs_pci_probe(struct pci_dev *pci, const struct pci_device_id *id)
adev = devm_kzalloc(dev, sizeof(*adev), GFP_KERNEL);
if (!adev)
return -ENOMEM;
+ bus = &adev->base.core;
+
ret = avs_bus_init(adev, pci, id);
if (ret < 0) {
dev_err(dev, "failed to init avs bus: %d\n", ret);
return ret;
}
- ret = pci_request_regions(pci, "AVS HDAudio");
+ ret = pcim_request_all_regions(pci, "AVS HDAudio");
if (ret < 0)
return ret;
- bus = &adev->base.core;
bus->addr = pci_resource_start(pci, 0);
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);
@@ -465,10 +496,22 @@ static int avs_pci_probe(struct pci_dev *pci, const struct pci_device_id *id)
pci_set_drvdata(pci, bus);
device_disable_async_suspend(dev);
+ ret = snd_hdac_i915_init(bus);
+ if (ret == -EPROBE_DEFER)
+ goto err_i915_init;
+ else if (ret < 0)
+ dev_info(bus->dev, "i915 init unsuccessful: %d\n", ret);
+
schedule_work(&adev->probe_work);
return 0;
+err_i915_init:
+ pci_free_irq(pci, 0, adev);
+ pci_free_irq(pci, 0, bus);
+ pci_free_irq_vectors(pci);
+ pci_clear_master(pci);
+ pci_set_drvdata(pci, NULL);
err_acquire_irq:
snd_hdac_bus_free_stream_pages(bus);
snd_hdac_ext_stream_free_all(bus);
@@ -476,8 +519,6 @@ err_init_streams:
iounmap(adev->dsp_ba);
err_remap_bar4:
iounmap(bus->remap_addr);
-err_remap_bar0:
- pci_release_regions(pci);
return ret;
}
@@ -497,8 +538,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);
@@ -515,9 +554,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);
@@ -551,7 +589,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);
@@ -579,7 +616,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;
@@ -640,7 +677,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;
@@ -663,41 +700,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);
}
@@ -709,39 +746,147 @@ 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,
+};
+
+static const struct avs_sram_spec apl_sram_spec = {
+ .base_offset = APL_ADSP_SRAM_BASE_OFFSET,
+ .window_size = APL_ADSP_SRAM_WINDOW_SIZE,
+};
+
+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 = {
+ .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 = 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 = {
+ .req_offset = CNL_ADSP_REG_HIPCIDR,
+ .req_ext_offset = CNL_ADSP_REG_HIPCIDD,
+ .req_busy_mask = CNL_ADSP_HIPCIDR_BUSY,
+ .ack_offset = CNL_ADSP_REG_HIPCIDA,
+ .ack_done_mask = CNL_ADSP_HIPCIDA_DONE,
+ .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 = {
.name = "skl",
- .min_fw_version = {
- .major = 9,
- .minor = 21,
- .hotfix = 0,
- .build = 4732,
- },
- .dsp_ops = &skl_dsp_ops,
+ .min_fw_version = { 9, 21, 0, 4732 },
+ .dsp_ops = &avs_skl_dsp_ops,
.core_init_mask = 1,
.attributes = AVS_PLATATTR_CLDMA,
- .sram_base_offset = SKL_ADSP_SRAM_BASE_OFFSET,
- .sram_window_size = SKL_ADSP_SRAM_WINDOW_SIZE,
- .rom_status = SKL_ADSP_SRAM_BASE_OFFSET,
+ .sram = &skl_sram_spec,
+ .hipc = &skl_hipc_spec,
};
static const struct avs_spec apl_desc = {
.name = "apl",
- .min_fw_version = {
- .major = 9,
- .minor = 22,
- .hotfix = 1,
- .build = 4323,
- },
- .dsp_ops = &apl_dsp_ops,
+ .min_fw_version = { 9, 22, 1, 4323 },
+ .dsp_ops = &avs_apl_dsp_ops,
.core_init_mask = 3,
.attributes = AVS_PLATATTR_IMR,
- .sram_base_offset = APL_ADSP_SRAM_BASE_OFFSET,
- .sram_window_size = APL_ADSP_SRAM_WINDOW_SIZE,
- .rom_status = APL_ADSP_SRAM_BASE_OFFSET,
+ .sram = &apl_sram_spec,
+ .hipc = &apl_hipc_spec,
+};
+
+static const struct avs_spec cnl_desc = {
+ .name = "cnl",
+ .min_fw_version = { 10, 23, 0, 5314 },
+ .dsp_ops = &avs_cnl_dsp_ops,
+ .core_init_mask = 1,
+ .attributes = AVS_PLATATTR_IMR,
+ .sram = &apl_sram_spec,
+ .hipc = &cnl_hipc_spec,
+};
+
+static const struct avs_spec icl_desc = {
+ .name = "icl",
+ .min_fw_version = { 10, 23, 0, 5040 },
+ .dsp_ops = &avs_icl_dsp_ops,
+ .core_init_mask = 1,
+ .attributes = AVS_PLATATTR_IMR,
+ .sram = &apl_sram_spec,
+ .hipc = &cnl_hipc_spec,
+};
+
+static const struct avs_spec jsl_desc = {
+ .name = "jsl",
+ .min_fw_version = { 10, 26, 0, 5872 },
+ .dsp_ops = &avs_icl_dsp_ops,
+ .core_init_mask = 1,
+ .attributes = AVS_PLATATTR_IMR,
+ .sram = &apl_sram_spec,
+ .hipc = &cnl_hipc_spec,
+};
+
+#define AVS_TGL_BASED_SPEC(sname, min) \
+static const struct avs_spec sname##_desc = { \
+ .name = #sname, \
+ .min_fw_version = { 10, min, 0, 5646 }, \
+ .dsp_ops = &avs_tgl_dsp_ops, \
+ .core_init_mask = 1, \
+ .attributes = AVS_PLATATTR_IMR, \
+ .sram = &apl_sram_spec, \
+ .hipc = &cnl_hipc_spec, \
+}
+
+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[] = {
@@ -753,6 +898,33 @@ static const struct pci_device_id avs_ids[] = {
{ PCI_DEVICE_DATA(INTEL, HDA_CML_S, &skl_desc) },
{ PCI_DEVICE_DATA(INTEL, HDA_APL, &apl_desc) },
{ PCI_DEVICE_DATA(INTEL, HDA_GML, &apl_desc) },
+ { PCI_DEVICE_DATA(INTEL, HDA_CNL_LP, &cnl_desc) },
+ { PCI_DEVICE_DATA(INTEL, HDA_CNL_H, &cnl_desc) },
+ { PCI_DEVICE_DATA(INTEL, HDA_CML_LP, &cnl_desc) },
+ { PCI_DEVICE_DATA(INTEL, HDA_CML_H, &cnl_desc) },
+ { PCI_DEVICE_DATA(INTEL, HDA_RKL_S, &cnl_desc) },
+ { PCI_DEVICE_DATA(INTEL, HDA_ICL_LP, &icl_desc) },
+ { PCI_DEVICE_DATA(INTEL, HDA_ICL_N, &icl_desc) },
+ { PCI_DEVICE_DATA(INTEL, HDA_ICL_H, &icl_desc) },
+ { PCI_DEVICE_DATA(INTEL, HDA_JSL_N, &jsl_desc) },
+ { PCI_DEVICE_DATA(INTEL, HDA_LKF, &lkf_desc) },
+ { PCI_DEVICE_DATA(INTEL, HDA_TGL_LP, &tgl_desc) },
+ { PCI_DEVICE_DATA(INTEL, HDA_TGL_H, &tgl_desc) },
+ { PCI_DEVICE_DATA(INTEL, HDA_CML_R, &tgl_desc) },
+ { PCI_DEVICE_DATA(INTEL, HDA_EHL_0, &ehl_desc) },
+ { PCI_DEVICE_DATA(INTEL, HDA_EHL_3, &ehl_desc) },
+ { PCI_DEVICE_DATA(INTEL, HDA_ADL_S, &adl_desc) },
+ { PCI_DEVICE_DATA(INTEL, HDA_ADL_P, &adl_desc) },
+ { PCI_DEVICE_DATA(INTEL, HDA_ADL_PS, &adl_desc) },
+ { PCI_DEVICE_DATA(INTEL, HDA_ADL_M, &adl_desc) },
+ { PCI_DEVICE_DATA(INTEL, HDA_ADL_PX, &adl_desc) },
+ { PCI_DEVICE_DATA(INTEL, HDA_ADL_N, &adl_n_desc) },
+ { PCI_DEVICE_DATA(INTEL, HDA_RPL_S, &adl_desc) },
+ { PCI_DEVICE_DATA(INTEL, HDA_RPL_P_0, &adl_desc) },
+ { 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);
@@ -763,8 +935,9 @@ static struct pci_driver avs_pci_driver = {
.probe = avs_pci_probe,
.remove = avs_pci_remove,
.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);
@@ -773,3 +946,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/debug.h b/sound/soc/intel/avs/debug.h
new file mode 100644
index 000000000000..94fe8729a5c1
--- /dev/null
+++ b/sound/soc/intel/avs/debug.h
@@ -0,0 +1,91 @@
+/* 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>
+ */
+
+#ifndef __SOUND_SOC_INTEL_AVS_DEBUG_H
+#define __SOUND_SOC_INTEL_AVS_DEBUG_H
+
+#include "messages.h"
+#include "registers.h"
+
+struct avs_dev;
+
+#define avs_log_buffer_size(adev) \
+ ((adev)->fw_cfg.trace_log_bytes / (adev)->hw_cfg.dsp_cores)
+
+#define avs_log_buffer_addr(adev, core) \
+({ \
+ s32 __offset = avs_dsp_op(adev, log_buffer_offset, core); \
+ (__offset < 0) ? NULL : \
+ (avs_sram_addr(adev, AVS_DEBUG_WINDOW) + __offset); \
+})
+
+static inline int avs_log_buffer_status_locked(struct avs_dev *adev, union avs_notify_msg *msg)
+{
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&adev->trace_lock, flags);
+ ret = avs_dsp_op(adev, log_buffer_status, msg);
+ spin_unlock_irqrestore(&adev->trace_lock, flags);
+
+ return ret;
+}
+
+struct avs_apl_log_buffer_layout {
+ u32 read_ptr;
+ 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))
+
+#define avs_apl_log_payload_addr(addr) \
+ (addr + sizeof(struct avs_apl_log_buffer_layout))
+
+#ifdef CONFIG_DEBUG_FS
+int avs_register_probe_component(struct avs_dev *adev, const char *name);
+
+#define AVS_SET_ENABLE_LOGS_OP(name) \
+ .enable_logs = avs_##name##_enable_logs
+
+bool avs_logging_fw(struct avs_dev *adev);
+void avs_dump_fw_log(struct avs_dev *adev, const void __iomem *src, unsigned int len);
+void avs_dump_fw_log_wakeup(struct avs_dev *adev, const void __iomem *src, unsigned int len);
+
+void avs_debugfs_init(struct avs_dev *adev);
+void avs_debugfs_exit(struct avs_dev *adev);
+
+#else
+static inline int avs_register_probe_component(struct avs_dev *adev, const char *name)
+{
+ return -EOPNOTSUPP;
+}
+
+#define AVS_SET_ENABLE_LOGS_OP(name)
+
+static inline bool avs_logging_fw(struct avs_dev *adev)
+{
+ return false;
+}
+
+static inline void avs_dump_fw_log(struct avs_dev *adev, const void __iomem *src, unsigned int len)
+{
+}
+
+static inline void avs_dump_fw_log_wakeup(struct avs_dev *adev, const void __iomem *src,
+ unsigned int len)
+{
+}
+
+static inline void avs_debugfs_init(struct avs_dev *adev) { }
+static inline void avs_debugfs_exit(struct avs_dev *adev) { }
+#endif
+
+#endif
diff --git a/sound/soc/intel/avs/debugfs.c b/sound/soc/intel/avs/debugfs.c
index bdd388ec01ea..701c247227bf 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,8 +10,10 @@
#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 "debug.h"
#include "messages.h"
static unsigned int __kfifo_fromio(struct kfifo *fifo, const void __iomem *src, unsigned int len)
@@ -68,7 +70,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 +94,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)
@@ -119,16 +119,13 @@ static ssize_t probe_points_read(struct file *file, char __user *to, size_t coun
}
for (i = 0; i < num_desc; i++) {
- ret = snprintf(buf + len, PAGE_SIZE - len,
- "Id: %#010x Purpose: %d Node id: %#x\n",
- desc[i].id.value, desc[i].purpose, desc[i].node_id.val);
- if (ret < 0)
- goto free_desc;
+ ret = scnprintf(buf + len, PAGE_SIZE - len,
+ "Id: %#010x Purpose: %d Node id: %#x\n",
+ desc[i].id.value, desc[i].purpose, desc[i].node_id.val);
len += ret;
}
ret = simple_read_from_buffer(to, count, ppos, buf, len);
-free_desc:
kfree(desc);
exit:
kfree(buf);
@@ -145,7 +142,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 +167,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 +179,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;
@@ -236,6 +232,9 @@ static int strace_open(struct inode *inode, struct file *file)
struct avs_dev *adev = inode->i_private;
int ret;
+ if (!try_module_get(adev->dev->driver->owner))
+ return -ENODEV;
+
if (kfifo_initialized(&adev->trace_fifo))
return -EBUSY;
@@ -270,6 +269,7 @@ static int strace_release(struct inode *inode, struct file *file)
spin_unlock_irqrestore(&adev->trace_lock, flags);
+ module_put(adev->dev->driver->owner);
return 0;
}
@@ -313,7 +313,6 @@ err_ipc:
if (!adev->logged_resources) {
avs_dsp_enable_d0ix(adev);
err_d0ix:
- pm_runtime_mark_last_busy(adev->dev);
pm_runtime_put_autosuspend(adev->dev);
}
@@ -340,7 +339,6 @@ static int disable_logs(struct avs_dev *adev, u32 resource_mask)
/* If that's the last resource, allow for D3. */
if (!adev->logged_resources) {
avs_dsp_enable_d0ix(adev);
- pm_runtime_mark_last_busy(adev->dev);
pm_runtime_put_autosuspend(adev->dev);
}
@@ -367,11 +365,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.
@@ -379,6 +380,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
new file mode 100644
index 000000000000..d655e727bebd
--- /dev/null
+++ b/sound/soc/intel/avs/icl.c
@@ -0,0 +1,203 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2021-2024 Intel Corporation
+//
+// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+//
+
+#include <linux/slab.h>
+#include <sound/hdaudio.h>
+#include <sound/hdaudio_ext.h>
+#include "avs.h"
+#include "debug.h"
+#include "messages.h"
+
+#define ICL_VS_LTRP_GB_ICCMAX 95
+
+#ifdef CONFIG_DEBUG_FS
+int avs_icl_enable_logs(struct avs_dev *adev, enum avs_log_enable enable, u32 aging_period,
+ u32 fifo_full_period, unsigned long resource_mask, u32 *priorities)
+{
+ struct avs_icl_log_state_info *info;
+ u32 size, num_libs = adev->fw_cfg.max_libs_count;
+ int i, ret;
+
+ if (fls_long(resource_mask) > num_libs)
+ return -EINVAL;
+ size = struct_size(info, logs_priorities_mask, num_libs);
+ info = kzalloc(size, GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ info->aging_timer_period = aging_period;
+ info->fifo_full_timer_period = fifo_full_period;
+ info->enable = enable;
+ if (enable)
+ for_each_set_bit(i, &resource_mask, num_libs)
+ info->logs_priorities_mask[i] = *priorities++;
+
+ ret = avs_ipc_set_enable_logs(adev, (u8 *)info, size);
+ kfree(info);
+ if (ret)
+ return AVS_IPC_RET(ret);
+
+ return 0;
+}
+#endif
+
+union avs_icl_memwnd2_slot_type {
+ u32 val;
+ struct {
+ u32 resource_id:8;
+ 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[SZ_4K];
+ };
+ 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 })
+#define AVS_ICL_SLOT_CRITICAL_LOG \
+ ((union avs_icl_memwnd2_slot_type) { 0x54524300U })
+#define AVS_ICL_SLOT_DEBUG_LOG \
+ ((union avs_icl_memwnd2_slot_type) { 0x474f4c00U })
+#define AVS_ICL_SLOT_GDB_STUB \
+ ((union avs_icl_memwnd2_slot_type) { 0x42444700U })
+#define AVS_ICL_SLOT_BROKEN \
+ ((union avs_icl_memwnd2_slot_type) { 0x44414544U })
+
+static int avs_icl_slot_offset(struct avs_dev *adev, union avs_icl_memwnd2_slot_type slot_type)
+{
+ struct avs_icl_memwnd2_desc desc[AVS_ICL_MEMWND2_SLOTS_COUNT];
+ int i;
+
+ memcpy_fromio(&desc, avs_sram_addr(adev, AVS_DEBUG_WINDOW), sizeof(desc));
+
+ 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) + i * SZ_4K;
+ return -ENXIO;
+}
+
+int avs_icl_log_buffer_offset(struct avs_dev *adev, u32 core)
+{
+ union avs_icl_memwnd2_slot_type slot_type = AVS_ICL_SLOT_DEBUG_LOG;
+ int ret;
+
+ slot_type.resource_id = core;
+ ret = avs_icl_slot_offset(adev, slot_type);
+ if (ret < 0)
+ dev_dbg(adev->dev, "No slot offset found for: %x\n",
+ slot_type.val);
+
+ return ret;
+}
+
+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;
+}
+
+int avs_icl_set_d0ix(struct avs_dev *adev, bool enable)
+{
+ int ret;
+
+ ret = avs_ipc_set_d0ix(adev, enable, false);
+ return AVS_IPC_RET(ret);
+}
+
+int avs_icl_load_basefw(struct avs_dev *adev, struct firmware *fw)
+{
+ struct hdac_bus *bus = &adev->base.core;
+ struct hdac_ext_stream *host_stream;
+ struct snd_pcm_substream substream;
+ struct snd_dma_buffer dmab;
+ unsigned int sd_fmt;
+ u8 ltrp_gb;
+ int ret;
+
+ /*
+ * ICCMAX:
+ *
+ * For ICL+ platforms, as per HW recommendation LTRP_GB is set to 95us
+ * during FW load. Its original value shall be restored once load completes.
+ *
+ * To avoid DMI/OPIO L1 entry during the load procedure, additional CAPTURE
+ * stream is allocated and set to run.
+ */
+
+ memset(&substream, 0, sizeof(substream));
+ substream.stream = SNDRV_PCM_STREAM_CAPTURE;
+
+ host_stream = snd_hdac_ext_stream_assign(bus, &substream, HDAC_EXT_STREAM_TYPE_HOST);
+ if (!host_stream)
+ return -EBUSY;
+
+ ltrp_gb = snd_hdac_chip_readb(bus, VS_LTRP) & AZX_REG_VS_LTRP_GB_MASK;
+ /* Carries no real data, use default format. */
+ sd_fmt = snd_hdac_stream_format(1, 32, 48000);
+
+ ret = snd_hdac_dsp_prepare(hdac_stream(host_stream), sd_fmt, fw->size, &dmab);
+ if (ret < 0)
+ goto release_stream;
+
+ snd_hdac_chip_updateb(bus, VS_LTRP, AZX_REG_VS_LTRP_GB_MASK, ICL_VS_LTRP_GB_ICCMAX);
+
+ spin_lock(&bus->reg_lock);
+ snd_hdac_stream_start(hdac_stream(host_stream));
+ spin_unlock(&bus->reg_lock);
+
+ ret = avs_hda_load_basefw(adev, fw);
+
+ spin_lock(&bus->reg_lock);
+ snd_hdac_stream_stop(hdac_stream(host_stream));
+ spin_unlock(&bus->reg_lock);
+
+ snd_hdac_dsp_cleanup(hdac_stream(host_stream), &dmab);
+
+release_stream:
+ snd_hdac_ext_stream_release(host_stream, HDAC_EXT_STREAM_TYPE_HOST);
+ snd_hdac_chip_updateb(bus, VS_LTRP, AZX_REG_VS_LTRP_GB_MASK, ltrp_gb);
+
+ return ret;
+}
+
+const struct avs_dsp_ops avs_icl_dsp_ops = {
+ .power = avs_dsp_core_power,
+ .reset = avs_dsp_core_reset,
+ .stall = avs_dsp_core_stall,
+ .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,
+ .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/ipc.c b/sound/soc/intel/avs/ipc.c
index bdf013c3dd12..c0feb9edd7f6 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>
@@ -10,6 +10,7 @@
#include <linux/slab.h>
#include <sound/hdaudio_ext.h>
#include "avs.h"
+#include "debug.h"
#include "messages.h"
#include "registers.h"
#include "trace.h"
@@ -141,7 +142,6 @@ static void avs_dsp_recovery(struct avs_dev *adev)
if (ret < 0)
dev_err(adev->dev, "dsp reboot failed: %d\n", ret);
- pm_runtime_mark_last_busy(adev->dev);
pm_runtime_enable(adev->dev);
pm_request_autosuspend(adev->dev);
@@ -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,88 +305,14 @@ void avs_dsp_process_response(struct avs_dev *adev, u64 header)
complete(&ipc->busy_completion);
}
-irqreturn_t avs_dsp_irq_handler(int irq, void *dev_id)
-{
- struct avs_dev *adev = dev_id;
- struct avs_ipc *ipc = adev->ipc;
- 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, SKL_ADSP_REG_HIPCIE);
- hipc_rsp = snd_hdac_adsp_readl(adev, SKL_ADSP_REG_HIPCT);
-
- /* DSP acked host's request */
- if (hipc_ack & SKL_ADSP_HIPCIE_DONE) {
- /*
- * As an extra precaution, mask done interrupt. Code executed
- * due to complete() found below does not assume any masking.
- */
- snd_hdac_adsp_updatel(adev, SKL_ADSP_REG_HIPCCTL,
- AVS_ADSP_HIPCCTL_DONE, 0);
-
- complete(&ipc->done_completion);
-
- /* tell DSP it has our attention */
- snd_hdac_adsp_updatel(adev, SKL_ADSP_REG_HIPCIE,
- SKL_ADSP_HIPCIE_DONE,
- SKL_ADSP_HIPCIE_DONE);
- /* unmask done interrupt */
- snd_hdac_adsp_updatel(adev, SKL_ADSP_REG_HIPCCTL,
- AVS_ADSP_HIPCCTL_DONE,
- AVS_ADSP_HIPCCTL_DONE);
- ret = IRQ_HANDLED;
- }
-
- /* DSP sent new response to process */
- if (hipc_rsp & SKL_ADSP_HIPCT_BUSY) {
- /* mask busy interrupt */
- snd_hdac_adsp_updatel(adev, SKL_ADSP_REG_HIPCCTL,
- AVS_ADSP_HIPCCTL_BUSY, 0);
-
- ret = IRQ_WAKE_THREAD;
- }
-
- return ret;
-}
-
-irqreturn_t avs_dsp_irq_thread(int irq, void *dev_id)
-{
- struct avs_dev *adev = dev_id;
- union avs_reply_msg msg;
- u32 hipct, hipcte;
-
- hipct = snd_hdac_adsp_readl(adev, SKL_ADSP_REG_HIPCT);
- hipcte = snd_hdac_adsp_readl(adev, SKL_ADSP_REG_HIPCTE);
-
- /* ensure DSP sent new response to process */
- if (!(hipct & SKL_ADSP_HIPCT_BUSY))
- return IRQ_NONE;
-
- msg.primary = hipct;
- msg.ext.val = 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);
- /* unmask busy interrupt */
- snd_hdac_adsp_updatel(adev, SKL_ADSP_REG_HIPCCTL,
- AVS_ADSP_HIPCCTL_BUSY, AVS_ADSP_HIPCCTL_BUSY);
-
- return IRQ_HANDLED;
-}
-
static bool avs_ipc_is_busy(struct avs_ipc *ipc)
{
struct avs_dev *adev = to_avs_dev(ipc->dev);
+ const struct avs_spec *const spec = adev->spec;
u32 hipc_rsp;
- hipc_rsp = snd_hdac_adsp_readl(adev, SKL_ADSP_REG_HIPCT);
- return hipc_rsp & SKL_ADSP_HIPCT_BUSY;
+ hipc_rsp = snd_hdac_adsp_readl(adev, spec->hipc->rsp_offset);
+ return hipc_rsp & spec->hipc->rsp_busy_mask;
}
static int avs_ipc_wait_busy_completion(struct avs_ipc *ipc, int timeout)
@@ -440,22 +370,26 @@ 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)
{
- u64 reg = ULONG_MAX;
-
- tx->header |= SKL_ADSP_HIPCI_BUSY;
- if (read_fwregs)
- reg = readq(avs_sram_addr(adev, AVS_FW_REGS_WINDOW));
+ const struct avs_spec *const spec = adev->spec;
+ u32 sts = UINT_MAX;
+ u32 lec = UINT_MAX;
+
+ tx->header |= spec->hipc->req_busy_mask;
+ 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);
- snd_hdac_adsp_writel(adev, SKL_ADSP_REG_HIPCIE, tx->header >> 32);
- snd_hdac_adsp_writel(adev, SKL_ADSP_REG_HIPCI, tx->header & UINT_MAX);
+ snd_hdac_adsp_writel(adev, spec->hipc->req_ext_offset, tx->header >> 32);
+ snd_hdac_adsp_writel(adev, spec->hipc->req_offset, tx->header & UINT_MAX);
}
static int avs_dsp_do_send_msg(struct avs_dev *adev, struct avs_ipc_msg *request,
- struct avs_ipc_msg *reply, int timeout)
+ struct avs_ipc_msg *reply, int timeout, const char *name)
{
struct avs_ipc *ipc = adev->ipc;
int ret;
@@ -482,6 +416,19 @@ static int avs_dsp_do_send_msg(struct avs_dev *adev, struct avs_ipc_msg *request
}
ret = ipc->rx.rsp.status;
+ /*
+ * If IPC channel is blocked e.g.: due to ongoing recovery,
+ * -EPERM error code is expected and thus it's not an actual error.
+ *
+ * Unsupported IPCs are of no harm either.
+ */
+ if (ret == -EPERM || ret == AVS_IPC_NOT_SUPPORTED)
+ dev_dbg(adev->dev, "%s (0x%08x 0x%08x) failed: %d\n",
+ name, request->glb.primary, request->glb.ext.val, ret);
+ else if (ret)
+ dev_err(adev->dev, "%s (0x%08x 0x%08x) failed: %d\n",
+ name, request->glb.primary, request->glb.ext.val, ret);
+
if (reply) {
reply->header = ipc->rx.header;
reply->size = ipc->rx.size;
@@ -496,7 +443,7 @@ exit:
static int avs_dsp_send_msg_sequence(struct avs_dev *adev, struct avs_ipc_msg *request,
struct avs_ipc_msg *reply, int timeout, bool wake_d0i0,
- bool schedule_d0ix)
+ bool schedule_d0ix, const char *name)
{
int ret;
@@ -507,7 +454,7 @@ static int avs_dsp_send_msg_sequence(struct avs_dev *adev, struct avs_ipc_msg *r
return ret;
}
- ret = avs_dsp_do_send_msg(adev, request, reply, timeout);
+ ret = avs_dsp_do_send_msg(adev, request, reply, timeout, name);
if (ret)
return ret;
@@ -519,34 +466,37 @@ static int avs_dsp_send_msg_sequence(struct avs_dev *adev, struct avs_ipc_msg *r
}
int avs_dsp_send_msg_timeout(struct avs_dev *adev, struct avs_ipc_msg *request,
- struct avs_ipc_msg *reply, int timeout)
+ struct avs_ipc_msg *reply, int timeout, const char *name)
{
bool wake_d0i0 = avs_dsp_op(adev, d0ix_toggle, request, true);
bool schedule_d0ix = avs_dsp_op(adev, d0ix_toggle, request, false);
- return avs_dsp_send_msg_sequence(adev, request, reply, timeout, wake_d0i0, schedule_d0ix);
+ return avs_dsp_send_msg_sequence(adev, request, reply, timeout, wake_d0i0, schedule_d0ix,
+ name);
}
int avs_dsp_send_msg(struct avs_dev *adev, struct avs_ipc_msg *request,
- struct avs_ipc_msg *reply)
+ struct avs_ipc_msg *reply, const char *name)
{
- return avs_dsp_send_msg_timeout(adev, request, reply, adev->ipc->default_timeout_ms);
+ return avs_dsp_send_msg_timeout(adev, request, reply, adev->ipc->default_timeout_ms, name);
}
int avs_dsp_send_pm_msg_timeout(struct avs_dev *adev, struct avs_ipc_msg *request,
- struct avs_ipc_msg *reply, int timeout, bool wake_d0i0)
+ struct avs_ipc_msg *reply, int timeout, bool wake_d0i0,
+ const char *name)
{
- return avs_dsp_send_msg_sequence(adev, request, reply, timeout, wake_d0i0, false);
+ return avs_dsp_send_msg_sequence(adev, request, reply, timeout, wake_d0i0, false, name);
}
int avs_dsp_send_pm_msg(struct avs_dev *adev, struct avs_ipc_msg *request,
- struct avs_ipc_msg *reply, bool wake_d0i0)
+ struct avs_ipc_msg *reply, bool wake_d0i0, const char *name)
{
return avs_dsp_send_pm_msg_timeout(adev, request, reply, adev->ipc->default_timeout_ms,
- wake_d0i0);
+ wake_d0i0, name);
}
-static int avs_dsp_do_send_rom_msg(struct avs_dev *adev, struct avs_ipc_msg *request, int timeout)
+static int avs_dsp_do_send_rom_msg(struct avs_dev *adev, struct avs_ipc_msg *request, int timeout,
+ const char *name)
{
struct avs_ipc *ipc = adev->ipc;
int ret;
@@ -568,24 +518,29 @@ static int avs_dsp_do_send_rom_msg(struct avs_dev *adev, struct avs_ipc_msg *req
ret = wait_for_completion_timeout(&ipc->done_completion, msecs_to_jiffies(timeout));
ret = ret ? 0 : -ETIMEDOUT;
}
+ if (ret)
+ dev_err(adev->dev, "%s (0x%08x 0x%08x) failed: %d\n",
+ name, request->glb.primary, request->glb.ext.val, ret);
mutex_unlock(&ipc->msg_mutex);
return ret;
}
-int avs_dsp_send_rom_msg_timeout(struct avs_dev *adev, struct avs_ipc_msg *request, int timeout)
+int avs_dsp_send_rom_msg_timeout(struct avs_dev *adev, struct avs_ipc_msg *request, int timeout,
+ const char *name)
{
- return avs_dsp_do_send_rom_msg(adev, request, timeout);
+ return avs_dsp_do_send_rom_msg(adev, request, timeout, name);
}
-int avs_dsp_send_rom_msg(struct avs_dev *adev, struct avs_ipc_msg *request)
+int avs_dsp_send_rom_msg(struct avs_dev *adev, struct avs_ipc_msg *request, const char *name)
{
- return avs_dsp_send_rom_msg_timeout(adev, request, adev->ipc->default_timeout_ms);
+ return avs_dsp_send_rom_msg_timeout(adev, request, adev->ipc->default_timeout_ms, name);
}
void avs_dsp_interrupt_control(struct avs_dev *adev, bool enable)
{
+ const struct avs_spec *const spec = adev->spec;
u32 value, mask;
/*
@@ -597,7 +552,7 @@ void avs_dsp_interrupt_control(struct avs_dev *adev, bool enable)
mask = AVS_ADSP_HIPCCTL_DONE | AVS_ADSP_HIPCCTL_BUSY;
value = enable ? mask : 0;
- snd_hdac_adsp_updatel(adev, SKL_ADSP_REG_HIPCCTL, mask, value);
+ snd_hdac_adsp_updatel(adev, spec->hipc->ctl_offset, mask, value);
}
int avs_ipc_init(struct avs_ipc *ipc, struct device *dev)
diff --git a/sound/soc/intel/avs/lnl.c b/sound/soc/intel/avs/lnl.c
new file mode 100644
index 000000000000..4fbc62bfd6c5
--- /dev/null
+++ b/sound/soc/intel/avs/lnl.c
@@ -0,0 +1,28 @@
+// 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 "debug.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 56bb0a59249d..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->rom_status, 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;
}
@@ -371,7 +377,7 @@ int avs_hda_load_basefw(struct avs_dev *adev, struct firmware *fw)
hstream = hdac_stream(estream);
/* code loading performed with default format */
- sdfmt = snd_hdac_calc_stream_format(48000, 1, SNDRV_PCM_FORMAT_S32_LE, 32, 0);
+ sdfmt = snd_hdac_stream_format(1, 32, 48000);
ret = snd_hdac_dsp_prepare(hstream, sdfmt, fw->size, &dmab);
if (ret < 0)
goto release_stream;
@@ -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);
}
@@ -438,7 +445,7 @@ int avs_hda_load_library(struct avs_dev *adev, struct firmware *lib, u32 id)
stream = hdac_stream(estream);
/* code loading performed with default format */
- sdfmt = snd_hdac_calc_stream_format(48000, 1, SNDRV_PCM_FORMAT_S32_LE, 32, 0);
+ sdfmt = snd_hdac_stream_format(1, 32, 48000);
ret = snd_hdac_dsp_prepare(stream, sdfmt, lib->size, &dmab);
if (ret < 0)
goto release_stream;
@@ -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,35 +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_boot_firmware(adev, true);
- if (ret < 0) {
- dev_err(adev->dev, "firmware boot failed: %d\n", ret);
+ return 0;
+}
+
+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,
@@ -694,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 f887ab5b0311..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>
@@ -16,71 +16,52 @@ int avs_ipc_set_boot_config(struct avs_dev *adev, u32 dma_id, u32 purge)
{
union avs_global_msg msg = AVS_GLOBAL_REQUEST(ROM_CONTROL);
struct avs_ipc_msg request = {{0}};
- int ret;
msg.boot_cfg.rom_ctrl_msg_type = AVS_ROM_SET_BOOT_CONFIG;
msg.boot_cfg.dma_id = dma_id;
msg.boot_cfg.purge_request = purge;
request.header = msg.val;
- ret = avs_dsp_send_rom_msg(adev, &request);
- if (ret)
- avs_ipc_err(adev, &request, "set boot config", ret);
-
- return ret;
+ return avs_dsp_send_rom_msg(adev, &request, "set boot config");
}
int avs_ipc_load_modules(struct avs_dev *adev, u16 *mod_ids, u32 num_mod_ids)
{
union avs_global_msg msg = AVS_GLOBAL_REQUEST(LOAD_MULTIPLE_MODULES);
struct avs_ipc_msg request;
- int ret;
msg.load_multi_mods.mod_cnt = num_mod_ids;
request.header = msg.val;
request.data = mod_ids;
request.size = sizeof(*mod_ids) * num_mod_ids;
- ret = avs_dsp_send_msg_timeout(adev, &request, NULL, AVS_CL_TIMEOUT_MS);
- if (ret)
- avs_ipc_err(adev, &request, "load multiple modules", ret);
-
- return ret;
+ return avs_dsp_send_msg_timeout(adev, &request, NULL, AVS_CL_TIMEOUT_MS,
+ "load multiple modules");
}
int avs_ipc_unload_modules(struct avs_dev *adev, u16 *mod_ids, u32 num_mod_ids)
{
union avs_global_msg msg = AVS_GLOBAL_REQUEST(UNLOAD_MULTIPLE_MODULES);
struct avs_ipc_msg request;
- int ret;
msg.load_multi_mods.mod_cnt = num_mod_ids;
request.header = msg.val;
request.data = mod_ids;
request.size = sizeof(*mod_ids) * num_mod_ids;
- ret = avs_dsp_send_msg(adev, &request, NULL);
- if (ret)
- avs_ipc_err(adev, &request, "unload multiple modules", ret);
-
- return ret;
+ return avs_dsp_send_msg(adev, &request, NULL, "unload multiple modules");
}
int avs_ipc_load_library(struct avs_dev *adev, u32 dma_id, u32 lib_id)
{
union avs_global_msg msg = AVS_GLOBAL_REQUEST(LOAD_LIBRARY);
struct avs_ipc_msg request = {{0}};
- int ret;
msg.load_lib.dma_id = dma_id;
msg.load_lib.lib_id = lib_id;
request.header = msg.val;
- ret = avs_dsp_send_msg_timeout(adev, &request, NULL, AVS_CL_TIMEOUT_MS);
- if (ret)
- avs_ipc_err(adev, &request, "load library", ret);
-
- return ret;
+ return avs_dsp_send_msg_timeout(adev, &request, NULL, AVS_CL_TIMEOUT_MS, "load library");
}
int avs_ipc_create_pipeline(struct avs_dev *adev, u16 req_size, u8 priority,
@@ -88,7 +69,6 @@ int avs_ipc_create_pipeline(struct avs_dev *adev, u16 req_size, u8 priority,
{
union avs_global_msg msg = AVS_GLOBAL_REQUEST(CREATE_PIPELINE);
struct avs_ipc_msg request = {{0}};
- int ret;
msg.create_ppl.ppl_mem_size = req_size;
msg.create_ppl.ppl_priority = priority;
@@ -97,27 +77,18 @@ int avs_ipc_create_pipeline(struct avs_dev *adev, u16 req_size, u8 priority,
msg.ext.create_ppl.attributes = attributes;
request.header = msg.val;
- ret = avs_dsp_send_msg(adev, &request, NULL);
- if (ret)
- avs_ipc_err(adev, &request, "create pipeline", ret);
-
- return ret;
+ return avs_dsp_send_msg(adev, &request, NULL, "create pipeline");
}
int avs_ipc_delete_pipeline(struct avs_dev *adev, u8 instance_id)
{
union avs_global_msg msg = AVS_GLOBAL_REQUEST(DELETE_PIPELINE);
struct avs_ipc_msg request = {{0}};
- int ret;
msg.ppl.instance_id = instance_id;
request.header = msg.val;
- ret = avs_dsp_send_msg(adev, &request, NULL);
- if (ret)
- avs_ipc_err(adev, &request, "delete pipeline", ret);
-
- return ret;
+ return avs_dsp_send_msg(adev, &request, NULL, "delete pipeline");
}
int avs_ipc_set_pipeline_state(struct avs_dev *adev, u8 instance_id,
@@ -125,17 +96,12 @@ int avs_ipc_set_pipeline_state(struct avs_dev *adev, u8 instance_id,
{
union avs_global_msg msg = AVS_GLOBAL_REQUEST(SET_PIPELINE_STATE);
struct avs_ipc_msg request = {{0}};
- int ret;
msg.set_ppl_state.ppl_id = instance_id;
msg.set_ppl_state.state = state;
request.header = msg.val;
- ret = avs_dsp_send_msg(adev, &request, NULL);
- if (ret)
- avs_ipc_err(adev, &request, "set pipeline state", ret);
-
- return ret;
+ return avs_dsp_send_msg(adev, &request, NULL, "set pipeline state");
}
int avs_ipc_get_pipeline_state(struct avs_dev *adev, u8 instance_id,
@@ -149,13 +115,9 @@ int avs_ipc_get_pipeline_state(struct avs_dev *adev, u8 instance_id,
msg.get_ppl_state.ppl_id = instance_id;
request.header = msg.val;
- ret = avs_dsp_send_msg(adev, &request, &reply);
- if (ret) {
- avs_ipc_err(adev, &request, "get pipeline state", ret);
- return ret;
- }
-
- *state = reply.rsp.ext.get_ppl_state.state;
+ ret = avs_dsp_send_msg(adev, &request, &reply, "get pipeline state");
+ if (!ret)
+ *state = reply.rsp.ext.get_ppl_state.state;
return ret;
}
@@ -183,7 +145,6 @@ int avs_ipc_init_instance(struct avs_dev *adev, u16 module_id, u8 instance_id,
{
union avs_module_msg msg = AVS_MODULE_REQUEST(INIT_INSTANCE);
struct avs_ipc_msg request;
- int ret;
msg.module_id = module_id;
msg.instance_id = instance_id;
@@ -197,11 +158,7 @@ int avs_ipc_init_instance(struct avs_dev *adev, u16 module_id, u8 instance_id,
request.data = param;
request.size = param_size;
- ret = avs_dsp_send_msg(adev, &request, NULL);
- if (ret)
- avs_ipc_err(adev, &request, "init instance", ret);
-
- return ret;
+ return avs_dsp_send_msg(adev, &request, NULL, "init instance");
}
/*
@@ -222,17 +179,12 @@ int avs_ipc_delete_instance(struct avs_dev *adev, u16 module_id, u8 instance_id)
{
union avs_module_msg msg = AVS_MODULE_REQUEST(DELETE_INSTANCE);
struct avs_ipc_msg request = {{0}};
- int ret;
msg.module_id = module_id;
msg.instance_id = instance_id;
request.header = msg.val;
- ret = avs_dsp_send_msg(adev, &request, NULL);
- if (ret)
- avs_ipc_err(adev, &request, "delete instance", ret);
-
- return ret;
+ return avs_dsp_send_msg(adev, &request, NULL, "delete instance");
}
/*
@@ -252,7 +204,6 @@ int avs_ipc_bind(struct avs_dev *adev, u16 module_id, u8 instance_id,
{
union avs_module_msg msg = AVS_MODULE_REQUEST(BIND);
struct avs_ipc_msg request = {{0}};
- int ret;
msg.module_id = module_id;
msg.instance_id = instance_id;
@@ -262,11 +213,7 @@ int avs_ipc_bind(struct avs_dev *adev, u16 module_id, u8 instance_id,
msg.ext.bind_unbind.src_queue = src_queue;
request.header = msg.val;
- ret = avs_dsp_send_msg(adev, &request, NULL);
- if (ret)
- avs_ipc_err(adev, &request, "bind modules", ret);
-
- return ret;
+ return avs_dsp_send_msg(adev, &request, NULL, "bind modules");
}
/*
@@ -286,7 +233,6 @@ int avs_ipc_unbind(struct avs_dev *adev, u16 module_id, u8 instance_id,
{
union avs_module_msg msg = AVS_MODULE_REQUEST(UNBIND);
struct avs_ipc_msg request = {{0}};
- int ret;
msg.module_id = module_id;
msg.instance_id = instance_id;
@@ -296,11 +242,7 @@ int avs_ipc_unbind(struct avs_dev *adev, u16 module_id, u8 instance_id,
msg.ext.bind_unbind.src_queue = src_queue;
request.header = msg.val;
- ret = avs_dsp_send_msg(adev, &request, NULL);
- if (ret)
- avs_ipc_err(adev, &request, "unbind modules", ret);
-
- return ret;
+ return avs_dsp_send_msg(adev, &request, NULL, "unbind modules");
}
static int __avs_ipc_set_large_config(struct avs_dev *adev, u16 module_id, u8 instance_id,
@@ -309,7 +251,6 @@ static int __avs_ipc_set_large_config(struct avs_dev *adev, u16 module_id, u8 in
{
union avs_module_msg msg = AVS_MODULE_REQUEST(LARGE_CONFIG_SET);
struct avs_ipc_msg request;
- int ret;
msg.module_id = module_id;
msg.instance_id = instance_id;
@@ -322,11 +263,7 @@ static int __avs_ipc_set_large_config(struct avs_dev *adev, u16 module_id, u8 in
request.data = request_data;
request.size = request_size;
- ret = avs_dsp_send_msg(adev, &request, NULL);
- if (ret)
- avs_ipc_err(adev, &request, "large config set", ret);
-
- return ret;
+ return avs_dsp_send_msg(adev, &request, NULL, "large config set");
}
int avs_ipc_set_large_config(struct avs_dev *adev, u16 module_id,
@@ -398,9 +335,8 @@ int avs_ipc_get_large_config(struct avs_dev *adev, u16 module_id, u8 instance_id
request.size = request_size;
reply.size = AVS_MAILBOX_SIZE;
- ret = avs_dsp_send_msg(adev, &request, &reply);
+ ret = avs_dsp_send_msg(adev, &request, &reply, "large config get");
if (ret) {
- avs_ipc_err(adev, &request, "large config get", ret);
kfree(reply.data);
return ret;
}
@@ -422,7 +358,6 @@ int avs_ipc_set_dx(struct avs_dev *adev, u32 core_mask, bool powerup)
union avs_module_msg msg = AVS_MODULE_REQUEST(SET_DX);
struct avs_ipc_msg request;
struct avs_dxstate_info dx;
- int ret;
dx.core_mask = core_mask;
dx.dx_mask = powerup ? core_mask : 0;
@@ -430,11 +365,7 @@ int avs_ipc_set_dx(struct avs_dev *adev, u32 core_mask, bool powerup)
request.data = &dx;
request.size = sizeof(dx);
- ret = avs_dsp_send_pm_msg(adev, &request, NULL, true);
- if (ret)
- avs_ipc_err(adev, &request, "set dx", ret);
-
- return ret;
+ return avs_dsp_send_pm_msg(adev, &request, NULL, true, "set dx");
}
/*
@@ -447,18 +378,14 @@ int avs_ipc_set_d0ix(struct avs_dev *adev, bool enable_pg, bool streaming)
{
union avs_module_msg msg = AVS_MODULE_REQUEST(SET_D0IX);
struct avs_ipc_msg request = {{0}};
- int ret;
msg.ext.set_d0ix.wake = enable_pg;
msg.ext.set_d0ix.streaming = streaming;
+ msg.ext.set_d0ix.prevent_pg = !enable_pg;
request.header = msg.val;
- ret = avs_dsp_send_pm_msg(adev, &request, NULL, false);
- if (ret)
- avs_ipc_err(adev, &request, "set d0ix", ret);
-
- return ret;
+ return avs_dsp_send_pm_msg(adev, &request, NULL, false, "set d0ix");
}
int avs_ipc_get_fw_config(struct avs_dev *adev, struct avs_fw_cfg *cfg)
@@ -473,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);
@@ -575,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;
}
@@ -590,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);
@@ -663,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;
}
@@ -702,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)
{
@@ -731,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 7f23a304b4a9..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>
@@ -9,9 +9,11 @@
#ifndef __SOUND_SOC_INTEL_AVS_MSGS_H
#define __SOUND_SOC_INTEL_AVS_MSGS_H
+#include <linux/sizes.h>
+
struct avs_dev;
-#define AVS_MAILBOX_SIZE 4096
+#define AVS_MAILBOX_SIZE SZ_4K
enum avs_msg_target {
AVS_FW_GEN_MSG = 0,
@@ -91,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,
@@ -143,12 +149,17 @@ union avs_module_msg {
u32 src_queue:3;
} bind_unbind;
struct {
+ /* pre-IceLake */
u32 wake:1;
u32 streaming:1;
+ /* IceLake and onwards */
+ u32 prevent_pg:1;
+ u32 prevent_local_cg:1;
} set_d0ix;
} ext;
};
} __packed;
+static_assert(sizeof(union avs_module_msg) == 8);
#define AVS_IPC_NOT_SUPPORTED 15
@@ -184,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,
@@ -220,6 +232,7 @@ union avs_notify_msg {
} ext;
};
} __packed;
+static_assert(sizeof(union avs_notify_msg) == 8);
#define AVS_MSG(hdr) { .val = hdr }
@@ -258,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;
@@ -266,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;
@@ -273,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 {
@@ -326,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);
@@ -357,22 +374,50 @@ enum avs_skl_log_priority {
AVS_SKL_LOG_VERBOSE,
};
-struct skl_log_state {
+struct avs_skl_log_state {
u32 enable;
u32 min_priority;
} __packed;
+static_assert(sizeof(struct avs_skl_log_state) == 8);
-struct skl_log_state_info {
+struct avs_skl_log_state_info {
u32 core_mask;
- struct skl_log_state logs_core[];
+ struct avs_skl_log_state logs_core[];
} __packed;
+static_assert(sizeof(struct avs_skl_log_state_info) == 4);
-struct apl_log_state_info {
+struct avs_apl_log_state_info {
u32 aging_timer_period;
u32 fifo_full_timer_period;
u32 core_mask;
- struct skl_log_state logs_core[];
+ 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,
+ AVS_ICL_LOG_HIGH,
+ AVS_ICL_LOG_MEDIUM,
+ AVS_ICL_LOG_LOW,
+ AVS_ICL_LOG_VERBOSE,
+};
+
+enum avs_icl_log_source {
+ AVS_ICL_LOG_INFRA = 0,
+ AVS_ICL_LOG_HAL,
+ AVS_ICL_LOG_MODULE,
+ AVS_ICL_LOG_AUDIO,
+ AVS_ICL_LOG_SENSING,
+ AVS_ICL_LOG_ULP_INFRA,
+};
+
+struct avs_icl_log_state_info {
+ u32 aging_timer_period;
+ u32 fifo_full_timer_period;
+ 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);
@@ -408,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 {
@@ -432,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,
@@ -491,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;
@@ -507,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;
@@ -529,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)
{
@@ -547,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);
@@ -594,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,
@@ -636,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;
@@ -650,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;
@@ -658,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;
@@ -673,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,
@@ -696,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;
@@ -705,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 \
@@ -717,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;
@@ -734,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;
@@ -742,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;
@@ -788,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,
};
@@ -815,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,
@@ -823,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 {
@@ -830,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
@@ -848,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,
@@ -864,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,
@@ -876,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..d8bdd03275d7
--- /dev/null
+++ b/sound/soc/intel/avs/mtl.c
@@ -0,0 +1,201 @@
+// 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 "debug.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 adbe23a47847..c8b586aced20 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"
@@ -87,7 +88,7 @@ static bool avs_test_hw_params(struct snd_pcm_hw_params *params,
return (params_rate(params) == fmt->sampling_freq &&
params_channels(params) == fmt->num_channels &&
params_physical_width(params) == fmt->bit_depth &&
- params_width(params) == fmt->valid_bit_depth);
+ snd_pcm_hw_params_bits(params) == fmt->valid_bit_depth);
}
static struct avs_tplg_path *
@@ -114,158 +115,364 @@ avs_path_find_variant(struct avs_dev *adev,
return NULL;
}
-__maybe_unused
-static bool avs_dma_type_is_host(u32 dma_type)
+static struct avs_tplg_path *avs_condpath_find_variant(struct avs_dev *adev,
+ struct avs_tplg_path_template *template,
+ struct avs_path *source,
+ struct avs_path *sink)
{
- return dma_type == AVS_DMA_HDA_HOST_OUTPUT ||
- dma_type == AVS_DMA_HDA_HOST_INPUT;
-}
+ struct avs_tplg_path *variant;
-__maybe_unused
-static bool avs_dma_type_is_link(u32 dma_type)
-{
- return !avs_dma_type_is_host(dma_type);
-}
+ list_for_each_entry(variant, &template->path_list, node) {
+ if (variant->source_path_id == source->template->id &&
+ variant->sink_path_id == sink->template->id)
+ return variant;
+ }
-__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;
+ return NULL;
}
-__maybe_unused
-static bool avs_dma_type_is_input(u32 dma_type)
+static bool avs_tplg_path_template_id_equal(struct avs_tplg_path_template_id *id,
+ struct avs_tplg_path_template_id *id2)
{
- return !avs_dma_type_is_output(dma_type);
+ return id->id == id2->id && !strcmp(id->tplg_name, id2->tplg_name);
}
-static int avs_copier_create(struct avs_dev *adev, struct avs_path_module *mod)
+static struct avs_path *avs_condpath_find_match(struct avs_dev *adev,
+ struct avs_tplg_path_template *template,
+ struct avs_path *path, int dir)
{
- 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;
+ struct avs_tplg_path_template_id *id, *id2;
- dma_type = t->cfg_ext->copier.dma_type;
- node_id.dma_type = dma_type;
+ if (dir) {
+ id = &template->source;
+ id2 = &template->sink;
+ } else {
+ id = &template->sink;
+ id2 = &template->source;
+ }
- switch (dma_type) {
- struct avs_audio_format *fmt;
- int direction;
+ /* Check whether this path is either source or sink of condpath template. */
+ if (id->id != path->template->owner->id ||
+ strcmp(id->tplg_name, path->template->owner->owner->name))
+ return NULL;
- 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;
- }
+ /* Unidirectional condpaths are allowed. */
+ if (avs_tplg_path_template_id_equal(id, id2))
+ return path;
- 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;
+ /* Now find the counterpart. */
+ return avs_path_find_path(adev, id2->tplg_name, id2->id);
+}
- break;
+static struct acpi_nhlt_config *
+avs_nhlt_config_or_default(struct avs_dev *adev, struct avs_tplg_module *t);
- 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;
+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)
+{
+ 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;
+ }
+
+ if (!module_template->nhlt_config) {
+ 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++;
+ }
}
+ }
+
+ 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);
+ }
- 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;
+ return i;
+}
+
+static void avs_init_node_id(union avs_connector_node_id *node_id,
+ struct avs_tplg_modcfg_ext *te, u32 dma_id)
+{
+ 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:
- /* HOST gateway's vindex is dynamically assigned with DMA id */
- node_id.vindex = mod->owner->owner->dma_id;
+ /* 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 = t->cfg_ext->copier.vindex.val |
- mod->owner->owner->dma_id;
+ node_id->vindex = te->copier.vindex.val | dma_id;
break;
- case INVALID_OBJECT_ID:
default:
- node_id = INVALID_NODE_ID;
+ *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);
+/* Every BLOB contains at least gateway attributes. */
+static struct acpi_nhlt_config *default_blob = (struct acpi_nhlt_config *)&(u32[2]) {4};
- cfg = kzalloc(cfg_size, GFP_KERNEL);
- if (!cfg)
- return -ENOMEM;
+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;
+
+ te = t->cfg_ext;
+
+ switch (te->copier.dma_type) {
+ case AVS_DMA_I2S_LINK_OUTPUT:
+ 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:
+ link_type = ACPI_NHLT_LINKTYPE_PDM;
+ dev_type = -1; /* ignored */
+ bus_id = 0;
+ dir = SNDRV_PCM_STREAM_CAPTURE;
+ fmt = t->in_fmt;
+ break;
+
+ default:
+ return default_blob;
+ }
+
+ /* 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:
+ case AVS_DMA_HDA_LINK_OUTPUT:
+ case AVS_DMA_HDA_LINK_INPUT:
+ return 0;
+ default:
+ break;
+ }
+
+ 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 *)&gtw->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;
+
+ if (t->nhlt_config)
+ blob = t->nhlt_config->blob;
+ else
+ 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;
+ 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);
- kfree(cfg);
+ 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;
@@ -281,47 +488,126 @@ 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 size;
+ 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. */
- size = struct_size(cfg, vols, 1);
- cfg = kzalloc(size, GFP_KERNEL);
- if (!cfg)
- return -ENOMEM;
+ cfg_size = struct_size(cfg, vols, 1);
+ if (cfg_size > AVS_MAILBOX_SIZE)
+ return -EINVAL;
+ cfg = adev->modcfg_buf;
+ memset(cfg, 0, cfg_size);
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->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, size, &mod->instance_id);
+ t->domain, cfg, cfg_size, &mod->instance_id);
+ if (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;
+ }
- kfree(cfg);
- 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)
@@ -337,7 +623,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;
@@ -368,6 +654,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;
@@ -480,10 +767,11 @@ static int avs_modext_create(struct avs_dev *adev, struct avs_path_module *mod)
num_pins = tcfg->generic.num_input_pins + tcfg->generic.num_output_pins;
cfg_size = struct_size(cfg, pin_fmts, num_pins);
- cfg = kzalloc(cfg_size, GFP_KERNEL);
- if (!cfg)
- return -ENOMEM;
+ if (cfg_size > AVS_MAILBOX_SIZE)
+ return -EINVAL;
+ cfg = adev->modcfg_buf;
+ memset(cfg, 0, cfg_size);
cfg->base.cpc = t->cfg_base->cpc;
cfg->base.ibs = t->cfg_base->ibs;
cfg->base.obs = t->cfg_base->obs;
@@ -505,7 +793,6 @@ static int avs_modext_create(struct avs_dev *adev, struct avs_path_module *mod)
ret = avs_dsp_init_module(adev, mod->module_id, mod->owner->instance_id,
t->core_id, t->domain, cfg, cfg_size,
&mod->instance_id);
- kfree(cfg);
return ret;
}
@@ -535,6 +822,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)
@@ -548,6 +836,33 @@ static int avs_path_module_type_create(struct avs_dev *adev, struct avs_path_mod
return avs_modext_create(adev, mod);
}
+static int avs_path_module_send_init_configs(struct avs_dev *adev, struct avs_path_module *mod)
+{
+ struct avs_soc_component *acomp;
+
+ acomp = to_avs_soc_component(mod->template->owner->owner->owner->owner->comp);
+
+ u32 num_ids = mod->template->num_config_ids;
+ u32 *ids = mod->template->config_ids;
+
+ for (int i = 0; i < num_ids; i++) {
+ struct avs_tplg_init_config *config = &acomp->tplg->init_configs[ids[i]];
+ size_t len = config->length;
+ void *data = config->data;
+ u32 param = config->param;
+ int ret;
+
+ ret = avs_ipc_set_large_config(adev, mod->module_id, mod->instance_id,
+ param, data, len);
+ if (ret) {
+ dev_err(adev->dev, "send initial module config failed: %d\n", ret);
+ return AVS_IPC_RET(ret);
+ }
+ }
+
+ return 0;
+}
+
static void avs_path_module_free(struct avs_dev *adev, struct avs_path_module *mod)
{
kfree(mod);
@@ -581,6 +896,12 @@ avs_path_module_create(struct avs_dev *adev,
return ERR_PTR(ret);
}
+ ret = avs_path_module_send_init_configs(adev, mod);
+ if (ret) {
+ kfree(mod);
+ return ERR_PTR(ret);
+ }
+
return mod;
}
@@ -678,8 +999,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);
@@ -786,6 +1105,10 @@ static int avs_path_init(struct avs_dev *adev, struct avs_path *path,
path->dma_id = dma_id;
INIT_LIST_HEAD(&path->ppl_list);
INIT_LIST_HEAD(&path->node);
+ INIT_LIST_HEAD(&path->source_list);
+ INIT_LIST_HEAD(&path->sink_list);
+ INIT_LIST_HEAD(&path->source_node);
+ INIT_LIST_HEAD(&path->sink_node);
/* create all the pipelines */
list_for_each_entry(tppl, &template->ppl_list, node) {
@@ -869,12 +1192,129 @@ err:
return ERR_PTR(ret);
}
+static void avs_condpath_free(struct avs_dev *adev, struct avs_path *path)
+{
+ int ret;
+
+ list_del(&path->source_node);
+ list_del(&path->sink_node);
+
+ ret = avs_path_reset(path);
+ if (ret < 0)
+ dev_err(adev->dev, "reset condpath failed: %d\n", ret);
+
+ ret = avs_path_unbind(path);
+ if (ret < 0)
+ dev_err(adev->dev, "unbind condpath failed: %d\n", ret);
+
+ avs_path_free_unlocked(path);
+}
+
+static struct avs_path *avs_condpath_create(struct avs_dev *adev,
+ struct avs_tplg_path *template,
+ struct avs_path *source,
+ struct avs_path *sink)
+{
+ struct avs_path *path;
+ int ret;
+
+ path = avs_path_create_unlocked(adev, 0, template);
+ if (IS_ERR(path))
+ return path;
+
+ ret = avs_path_bind(path);
+ if (ret)
+ goto err_bind;
+
+ ret = avs_path_reset(path);
+ if (ret)
+ goto err_reset;
+
+ path->source = source;
+ path->sink = sink;
+ list_add_tail(&path->source_node, &source->source_list);
+ list_add_tail(&path->sink_node, &sink->sink_list);
+
+ return path;
+
+err_reset:
+ avs_path_unbind(path);
+err_bind:
+ avs_path_free_unlocked(path);
+ return ERR_PTR(ret);
+}
+
+static int avs_condpaths_walk(struct avs_dev *adev, struct avs_path *path, int dir)
+{
+ struct avs_soc_component *acomp;
+ struct avs_path *source, *sink;
+ struct avs_path **other;
+
+ if (dir) {
+ source = path;
+ other = &sink;
+ } else {
+ sink = path;
+ other = &source;
+ }
+
+ list_for_each_entry(acomp, &adev->comp_list, node) {
+ for (int i = 0; i < acomp->tplg->num_condpath_tmpls; i++) {
+ struct avs_tplg_path_template *template;
+ struct avs_tplg_path *variant;
+ struct avs_path *cpath;
+
+ template = &acomp->tplg->condpath_tmpls[i];
+
+ /* Do not create unidirectional condpaths twice. */
+ if (avs_tplg_path_template_id_equal(&template->source,
+ &template->sink) && dir)
+ continue;
+
+ *other = avs_condpath_find_match(adev, template, path, dir);
+ if (!*other)
+ continue;
+
+ variant = avs_condpath_find_variant(adev, template, source, sink);
+ if (!variant)
+ continue;
+
+ cpath = avs_condpath_create(adev, variant, source, sink);
+ if (IS_ERR(cpath))
+ return PTR_ERR(cpath);
+ }
+ }
+
+ return 0;
+}
+
+/* Caller responsible for holding adev->path_mutex. */
+static int avs_condpaths_walk_all(struct avs_dev *adev, struct avs_path *path)
+{
+ int ret;
+
+ ret = avs_condpaths_walk(adev, path, SNDRV_PCM_STREAM_CAPTURE);
+ if (ret)
+ return ret;
+
+ return avs_condpaths_walk(adev, path, SNDRV_PCM_STREAM_PLAYBACK);
+}
+
void avs_path_free(struct avs_path *path)
{
+ struct avs_path *cpath, *csave;
struct avs_dev *adev = path->owner;
mutex_lock(&adev->path_mutex);
+
+ /* Free all condpaths this path spawned. */
+ list_for_each_entry_safe(cpath, csave, &path->source_list, source_node)
+ avs_condpath_free(path->owner, cpath);
+ list_for_each_entry_safe(cpath, csave, &path->sink_list, sink_node)
+ avs_condpath_free(path->owner, cpath);
+
avs_path_free_unlocked(path);
+
mutex_unlock(&adev->path_mutex);
}
@@ -885,6 +1325,7 @@ struct avs_path *avs_path_create(struct avs_dev *adev, u32 dma_id,
{
struct avs_tplg_path *variant;
struct avs_path *path;
+ int ret;
variant = avs_path_find_variant(adev, template, fe_params, be_params);
if (!variant) {
@@ -898,7 +1339,16 @@ struct avs_path *avs_path_create(struct avs_dev *adev, u32 dma_id,
mutex_lock(&adev->comp_list_mutex);
path = avs_path_create_unlocked(adev, dma_id, variant);
+ if (IS_ERR(path))
+ goto exit;
+
+ ret = avs_condpaths_walk_all(adev, path);
+ if (ret) {
+ avs_path_free_unlocked(path);
+ path = ERR_PTR(ret);
+ }
+exit:
mutex_unlock(&adev->comp_list_mutex);
mutex_unlock(&adev->path_mutex);
@@ -1021,6 +1471,42 @@ int avs_path_reset(struct avs_path *path)
return 0;
}
+static int avs_condpath_pause(struct avs_dev *adev, struct avs_path *cpath)
+{
+ struct avs_path_pipeline *ppl;
+ int ret;
+
+ if (cpath->state == AVS_PPL_STATE_PAUSED)
+ return 0;
+
+ list_for_each_entry_reverse(ppl, &cpath->ppl_list, node) {
+ ret = avs_ipc_set_pipeline_state(adev, ppl->instance_id, AVS_PPL_STATE_PAUSED);
+ if (ret) {
+ dev_err(adev->dev, "pause cpath failed: %d\n", ret);
+ cpath->state = AVS_PPL_STATE_INVALID;
+ return AVS_IPC_RET(ret);
+ }
+ }
+
+ cpath->state = AVS_PPL_STATE_PAUSED;
+ return 0;
+}
+
+static void avs_condpaths_pause(struct avs_dev *adev, struct avs_path *path)
+{
+ struct avs_path *cpath;
+
+ mutex_lock(&adev->path_mutex);
+
+ /* If either source or sink stops, so do the attached conditional paths. */
+ list_for_each_entry(cpath, &path->source_list, source_node)
+ avs_condpath_pause(adev, cpath);
+ list_for_each_entry(cpath, &path->sink_list, sink_node)
+ avs_condpath_pause(adev, cpath);
+
+ mutex_unlock(&adev->path_mutex);
+}
+
int avs_path_pause(struct avs_path *path)
{
struct avs_path_pipeline *ppl;
@@ -1030,6 +1516,8 @@ int avs_path_pause(struct avs_path *path)
if (path->state == AVS_PPL_STATE_PAUSED)
return 0;
+ avs_condpaths_pause(adev, path);
+
list_for_each_entry_reverse(ppl, &path->ppl_list, node) {
ret = avs_ipc_set_pipeline_state(adev, ppl->instance_id,
AVS_PPL_STATE_PAUSED);
@@ -1044,6 +1532,50 @@ int avs_path_pause(struct avs_path *path)
return 0;
}
+static int avs_condpath_run(struct avs_dev *adev, struct avs_path *cpath, int trigger)
+{
+ struct avs_path_pipeline *ppl;
+ int ret;
+
+ if (cpath->state == AVS_PPL_STATE_RUNNING)
+ return 0;
+
+ list_for_each_entry(ppl, &cpath->ppl_list, node) {
+ if (ppl->template->cfg->trigger != trigger)
+ continue;
+
+ ret = avs_ipc_set_pipeline_state(adev, ppl->instance_id, AVS_PPL_STATE_RUNNING);
+ if (ret) {
+ dev_err(adev->dev, "run cpath failed: %d\n", ret);
+ cpath->state = AVS_PPL_STATE_INVALID;
+ return AVS_IPC_RET(ret);
+ }
+ }
+
+ cpath->state = AVS_PPL_STATE_RUNNING;
+ return 0;
+}
+
+static void avs_condpaths_run(struct avs_dev *adev, struct avs_path *path, int trigger)
+{
+ struct avs_path *cpath;
+
+ mutex_lock(&adev->path_mutex);
+
+ /* Run conditional paths only if source and sink are both running. */
+ list_for_each_entry(cpath, &path->source_list, source_node)
+ if (cpath->source->state == AVS_PPL_STATE_RUNNING &&
+ cpath->sink->state == AVS_PPL_STATE_RUNNING)
+ avs_condpath_run(adev, cpath, trigger);
+
+ list_for_each_entry(cpath, &path->sink_list, sink_node)
+ if (cpath->source->state == AVS_PPL_STATE_RUNNING &&
+ cpath->sink->state == AVS_PPL_STATE_RUNNING)
+ avs_condpath_run(adev, cpath, trigger);
+
+ mutex_unlock(&adev->path_mutex);
+}
+
int avs_path_run(struct avs_path *path, int trigger)
{
struct avs_path_pipeline *ppl;
@@ -1067,5 +1599,10 @@ int avs_path_run(struct avs_path *path, int trigger)
}
path->state = AVS_PPL_STATE_RUNNING;
+
+ /* Granular pipeline triggering not intended for conditional paths. */
+ if (trigger == AVS_TPLG_TRIGGER_AUTO)
+ avs_condpaths_run(adev, path, trigger);
+
return 0;
}
diff --git a/sound/soc/intel/avs/path.h b/sound/soc/intel/avs/path.h
index 657f7b093e80..ceb89971a902 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>
@@ -13,11 +13,24 @@
#include "avs.h"
#include "topology.h"
+#define AVS_COND_TYPE_NONE 0
+#define AVS_COND_TYPE_AECREF 1
+
struct avs_path {
u32 dma_id;
struct list_head ppl_list;
u32 state;
+ /* condpath navigation for standard paths */
+ struct list_head source_list;
+ struct list_head sink_list;
+
+ /* conditional path fields */
+ struct avs_path *source;
+ struct avs_path *sink;
+ struct list_head source_node;
+ struct list_head sink_node;
+
struct avs_tplg_path *template;
struct avs_dev *owner;
/* device path management */
@@ -69,4 +82,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 1fbb2c2fadb5..4a6deb599c88 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,19 +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;
};
@@ -55,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 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 = asoc_substream_to_rtd(substream);
- struct avs_dev *adev = to_avs_dev(dai->dev);
+ 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);
@@ -76,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,
@@ -91,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);
@@ -108,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);
@@ -127,7 +206,8 @@ static int avs_dai_be_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_pcm_runtime *fe, *be;
struct snd_soc_dpcm *dpcm;
- be = asoc_substream_to_rtd(substream);
+ 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;
@@ -136,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;
@@ -158,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 = asoc_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)
{
@@ -208,15 +265,10 @@ 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)
{
- struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct avs_dma_data *data;
int ret = 0;
@@ -264,104 +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 = asoc_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(asoc_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 = asoc_substream_to_rtd(substream);
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct hdac_ext_stream *link_stream = runtime->private_data;
- struct hdac_ext_link *link;
- struct hda_codec *codec;
- struct hdac_bus *bus;
+ 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;
+ const struct snd_pcm_hw_params *p;
+ struct avs_dma_data *data;
unsigned int format_val;
+ unsigned int bits;
int ret;
+ 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(asoc_rtd_to_codec(rtd, 0)->dev);
- bus = &codec->bus->core;
- format_val = snd_hdac_calc_stream_format(runtime->rate, runtime->channels, runtime->format,
- runtime->sample_bits, 0);
+ stream_info = snd_soc_dai_get_pcm_stream(dai, substream->stream);
+ bits = snd_hdac_stream_format_bits(params_format(p), params_subformat(p),
+ stream_info->sig_bits);
+ format_val = snd_hdac_stream_format(params_channels(p), bits, params_rate(p));
- snd_hdac_ext_stream_decouple(bus, link_stream, true);
+ 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;
@@ -372,15 +487,13 @@ static int avs_dai_hda_be_prepare(struct snd_pcm_substream *substream, struct sn
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 = asoc_substream_to_rtd(substream);
- struct hdac_ext_stream *link_stream;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
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:
@@ -389,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) {
@@ -412,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)
@@ -436,82 +549,111 @@ 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 = asoc_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);
+ disable_work_sync(&data->period_elapsed_work);
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,
@@ -534,7 +676,8 @@ static int avs_dai_fe_hw_params(struct snd_pcm_substream *substream,
hdac_stream(host_stream)->period_bytes = 0;
hdac_stream(host_stream)->format_val = 0;
- fe = asoc_substream_to_rtd(substream);
+ 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;
@@ -601,16 +744,19 @@ 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;
+ 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;
- struct hdac_bus *bus;
unsigned int format_val;
+ struct hdac_bus *bus;
+ unsigned int bits;
int ret;
data = snd_soc_dai_get_dma_data(dai, substream);
host_stream = data->host_stream;
+ if (runtime->state == SNDRV_PCM_STATE_XRUN)
+ hdac_stream(host_stream)->prepared = false;
if (hdac_stream(host_stream)->prepared)
return 0;
@@ -618,18 +764,20 @@ static int avs_dai_fe_prepare(struct snd_pcm_substream *substream, struct snd_so
snd_hdac_ext_stream_decouple(bus, data->host_stream, true);
snd_hdac_stream_reset(hdac_stream(host_stream));
- format_val = snd_hdac_calc_stream_format(runtime->rate, runtime->channels, runtime->format,
- runtime->sample_bits, 0);
+ stream_info = snd_soc_dai_get_pcm_stream(dai, substream->stream);
+ bits = snd_hdac_stream_format_bits(runtime->format, runtime->subformat,
+ stream_info->sig_bits);
+ format_val = snd_hdac_stream_format(runtime->channels, bits, runtime->rate);
ret = snd_hdac_stream_set_params(hdac_stream(host_stream), format_val);
if (ret < 0)
return ret;
- ret = snd_hdac_stream_setup(hdac_stream(host_stream));
+ ret = snd_hdac_ext_host_stream_setup(host_stream, false);
if (ret < 0)
return ret;
- ret = avs_dai_prepare(adev, substream, dai);
+ ret = avs_dai_prepare(substream, dai);
if (ret)
return ret;
@@ -637,9 +785,82 @@ static int avs_dai_fe_prepare(struct snd_pcm_substream *substream, struct snd_so
return 0;
}
+static void avs_hda_stream_start(struct hdac_bus *bus, struct hdac_ext_stream *host_stream)
+{
+ struct hdac_stream *first_running = NULL;
+ struct hdac_stream *pos;
+ struct avs_dev *adev = hdac_to_avs(bus);
+
+ list_for_each_entry(pos, &bus->stream_list, list) {
+ if (pos->running) {
+ if (first_running)
+ break; /* more than one running */
+ first_running = pos;
+ }
+ }
+
+ /*
+ * If host_stream is a CAPTURE stream and will be the only one running,
+ * disable L1SEN to avoid sound clipping.
+ */
+ if (!first_running) {
+ if (hdac_stream(host_stream)->direction == SNDRV_PCM_STREAM_CAPTURE)
+ avs_hda_l1sen_enable(adev, false);
+ snd_hdac_stream_start(hdac_stream(host_stream));
+ return;
+ }
+
+ snd_hdac_stream_start(hdac_stream(host_stream));
+ /*
+ * If host_stream is the first stream to break the rule above,
+ * re-enable L1SEN.
+ */
+ if (list_entry_is_head(pos, &bus->stream_list, list) &&
+ first_running->direction == SNDRV_PCM_STREAM_CAPTURE)
+ avs_hda_l1sen_enable(adev, true);
+}
+
+static void avs_hda_stream_stop(struct hdac_bus *bus, struct hdac_ext_stream *host_stream)
+{
+ struct hdac_stream *first_running = NULL;
+ struct hdac_stream *pos;
+ struct avs_dev *adev = hdac_to_avs(bus);
+
+ list_for_each_entry(pos, &bus->stream_list, list) {
+ if (pos == hdac_stream(host_stream))
+ continue; /* ignore stream that is about to be stopped */
+ if (pos->running) {
+ if (first_running)
+ break; /* more than one running */
+ first_running = pos;
+ }
+ }
+
+ /*
+ * If host_stream is a CAPTURE stream and is the only one running,
+ * re-enable L1SEN.
+ */
+ if (!first_running) {
+ snd_hdac_stream_stop(hdac_stream(host_stream));
+ if (hdac_stream(host_stream)->direction == SNDRV_PCM_STREAM_CAPTURE)
+ avs_hda_l1sen_enable(adev, true);
+ return;
+ }
+
+ /*
+ * If by stopping host_stream there is only a single, CAPTURE stream running
+ * left, disable L1SEN to avoid sound clipping.
+ */
+ if (list_entry_is_head(pos, &bus->stream_list, list) &&
+ first_running->direction == SNDRV_PCM_STREAM_CAPTURE)
+ avs_hda_l1sen_enable(adev, false);
+
+ snd_hdac_stream_stop(hdac_stream(host_stream));
+}
+
static int avs_dai_fe_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai)
{
- struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct avs_dma_data *data;
struct hdac_ext_stream *host_stream;
struct hdac_bus *bus;
@@ -658,7 +879,7 @@ static int avs_dai_fe_trigger(struct snd_pcm_substream *substream, int cmd, stru
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
spin_lock_irqsave(&bus->reg_lock, flags);
- snd_hdac_stream_start(hdac_stream(host_stream));
+ avs_hda_stream_start(bus, host_stream);
spin_unlock_irqrestore(&bus->reg_lock, flags);
/* Timeout on DRSM poll shall not stop the resume so ignore the result. */
@@ -688,7 +909,7 @@ static int avs_dai_fe_trigger(struct snd_pcm_substream *substream, int cmd, stru
dev_err(dai->dev, "pause FE path failed: %d\n", ret);
spin_lock_irqsave(&bus->reg_lock, flags);
- snd_hdac_stream_stop(hdac_stream(host_stream));
+ avs_hda_stream_stop(bus, host_stream);
spin_unlock_irqrestore(&bus->reg_lock, flags);
ret = avs_path_reset(data->path);
@@ -761,7 +982,6 @@ static int avs_component_load_libraries(struct avs_soc_component *acomp)
if (!ret)
ret = avs_module_info_init(adev, false);
- pm_runtime_mark_last_busy(adev->dev);
pm_runtime_put_autosuspend(adev->dev);
return ret;
@@ -796,6 +1016,29 @@ static int avs_component_probe(struct snd_soc_component *component)
ret = avs_load_topology(component, filename);
kfree(filename);
+ if (ret == -ENOENT && !strncmp(mach->tplg_filename, "hda-", 4)) {
+ unsigned int vendor_id;
+
+ if (sscanf(mach->tplg_filename, "hda-%08x-tplg.bin", &vendor_id) != 1)
+ return ret;
+
+ if (((vendor_id >> 16) & 0xFFFF) == 0x8086)
+ mach->tplg_filename = devm_kasprintf(adev->dev, GFP_KERNEL,
+ "hda-8086-generic-tplg.bin");
+ 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)
+ return -ENOMEM;
+
+ dev_info(card->dev, "trying to load fallback topology %s\n", mach->tplg_filename);
+ ret = avs_load_topology(component, filename);
+ kfree(filename);
+ }
if (ret < 0)
return ret;
@@ -847,7 +1090,7 @@ static int avs_dai_resume_hw_params(struct snd_soc_dai *dai, struct avs_dma_data
int ret;
substream = data->substream;
- rtd = asoc_substream_to_rtd(substream);
+ rtd = snd_soc_substream_to_rtd(substream);
ret = dai->driver->ops->hw_params(substream, &rtd->dpcm[substream->stream].hw_params, dai);
if (ret)
@@ -942,7 +1185,7 @@ static int avs_component_pm_op(struct snd_soc_component *component, bool be,
for_each_component_dais(component, dai) {
data = snd_soc_dai_dma_data_get_playback(dai);
if (data) {
- rtd = asoc_substream_to_rtd(data->substream);
+ rtd = snd_soc_substream_to_rtd(data->substream);
if (rtd->dai_link->no_pcm == be && !rtd->dai_link->ignore_suspend) {
ret = op(dai, data);
if (ret < 0) {
@@ -955,7 +1198,7 @@ static int avs_component_pm_op(struct snd_soc_component *component, bool be,
data = snd_soc_dai_dma_data_get_capture(dai);
if (data) {
- rtd = asoc_substream_to_rtd(data->substream);
+ rtd = snd_soc_substream_to_rtd(data->substream);
if (rtd->dai_link->no_pcm == be && !rtd->dai_link->ignore_suspend) {
ret = op(dai, data);
if (ret < 0) {
@@ -1046,8 +1289,10 @@ static const struct snd_pcm_hardware avs_pcm_hardware = {
SNDRV_PCM_INFO_RESUME |
SNDRV_PCM_INFO_NO_PERIOD_WAKEUP,
.formats = SNDRV_PCM_FMTBIT_S16_LE |
- SNDRV_PCM_FMTBIT_S24_LE |
SNDRV_PCM_FMTBIT_S32_LE,
+ .subformats = SNDRV_PCM_SUBFMTBIT_MSBITS_20 |
+ SNDRV_PCM_SUBFMTBIT_MSBITS_24 |
+ SNDRV_PCM_SUBFMTBIT_MSBITS_MAX,
.buffer_bytes_max = AZX_MAX_BUF_SIZE,
.period_bytes_min = 128,
.period_bytes_max = AZX_MAX_BUF_SIZE / 2,
@@ -1059,7 +1304,7 @@ static const struct snd_pcm_hardware avs_pcm_hardware = {
static int avs_component_open(struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
- struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
/* only FE DAI links are handled here */
if (rtd->dai_link->no_pcm)
@@ -1077,12 +1322,12 @@ static unsigned int avs_hda_stream_dpib_read(struct hdac_ext_stream *stream)
static snd_pcm_uframes_t
avs_component_pointer(struct snd_soc_component *component, struct snd_pcm_substream *substream)
{
- struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct avs_dma_data *data;
struct hdac_ext_stream *host_stream;
unsigned int pos;
- data = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream);
+ data = snd_soc_dai_get_dma_data(snd_soc_rtd_to_cpu(rtd, 0), substream);
if (!data->host_stream)
return 0;
@@ -1107,7 +1352,7 @@ static int avs_component_mmap(struct snd_soc_component *component,
static int avs_component_construct(struct snd_soc_component *component,
struct snd_soc_pcm_runtime *rtd)
{
- struct snd_soc_dai *dai = asoc_rtd_to_cpu(rtd, 0);
+ struct snd_soc_dai *dai = snd_soc_rtd_to_cpu(rtd, 0);
struct snd_pcm *pcm = rtd->pcm;
if (dai->driver->playback.channels_min)
@@ -1123,7 +1368,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,
@@ -1137,9 +1382,9 @@ static const struct snd_soc_component_driver avs_component_driver = {
.topology_name_prefix = "intel/avs",
};
-int avs_soc_component_register(struct device *dev, const char *name,
- const struct snd_soc_component_driver *drv,
- struct snd_soc_dai_driver *cpu_dais, int num_cpu_dais)
+int avs_register_component(struct device *dev, const char *name,
+ struct snd_soc_component_driver *drv,
+ struct snd_soc_dai_driver *cpu_dais, int num_cpu_dais)
{
struct avs_soc_component *acomp;
int ret;
@@ -1148,21 +1393,24 @@ int avs_soc_component_register(struct device *dev, const char *name,
if (!acomp)
return -ENOMEM;
+ acomp->base.name = devm_kstrdup(dev, name, GFP_KERNEL);
+ if (!acomp->base.name)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&acomp->node);
+
+ drv->use_dai_pcm_id = !obsolete_card_names;
+
ret = snd_soc_component_initialize(&acomp->base, drv, dev);
if (ret < 0)
return ret;
- /* force name change after ASoC is done with its init */
- acomp->base.name = name;
- INIT_LIST_HEAD(&acomp->node);
-
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,
@@ -1173,7 +1421,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,
@@ -1184,72 +1431,100 @@ static struct snd_soc_dai_driver dmic_cpu_dais[] = {
},
};
-int avs_dmic_platform_register(struct avs_dev *adev, const char *name)
+int avs_register_dmic_component(struct avs_dev *adev, const char *name)
{
- return avs_soc_component_register(adev->dev, name, &avs_component_driver, dmic_cpu_dais,
- ARRAY_SIZE(dmic_cpu_dais));
+ 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_register_component(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_S24_LE |
SNDRV_PCM_FMTBIT_S32_LE,
+ .subformats = SNDRV_PCM_SUBFMTBIT_MSBITS_20 |
+ SNDRV_PCM_SUBFMTBIT_MSBITS_24 |
+ SNDRV_PCM_SUBFMTBIT_MSBITS_MAX,
},
.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_S24_LE |
SNDRV_PCM_FMTBIT_S32_LE,
+ .subformats = SNDRV_PCM_SUBFMTBIT_MSBITS_20 |
+ SNDRV_PCM_SUBFMTBIT_MSBITS_24 |
+ SNDRV_PCM_SUBFMTBIT_MSBITS_MAX,
},
};
-int avs_i2s_platform_register(struct avs_dev *adev, const char *name, unsigned long port_mask,
- unsigned long *tdms)
+int avs_register_i2s_component(struct avs_dev *adev, const char *name, unsigned long port_mask,
+ 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;
- cpu_count = hweight_long(port_mask);
+ 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)
+ if (!tdms || test_bit(0, &tdms[i]))
+ cpu_count++;
if (tdms)
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;
dai = cpus;
for_each_set_bit(i, &port_mask, ssp_count) {
- memcpy(dai, &i2s_dai_template, sizeof(*dai));
+ if (!tdms || test_bit(0, &tdms[i])) {
+ memcpy(dai, &i2s_dai_template, sizeof(*dai));
- dai->name =
- devm_kasprintf(adev->dev, GFP_KERNEL, "SSP%d Pin", i);
- dai->playback.stream_name =
- devm_kasprintf(adev->dev, GFP_KERNEL, "ssp%d Tx", i);
- dai->capture.stream_name =
- devm_kasprintf(adev->dev, GFP_KERNEL, "ssp%d Rx", i);
+ dai->name =
+ devm_kasprintf(adev->dev, GFP_KERNEL, "SSP%d Pin", i);
+ dai->playback.stream_name =
+ devm_kasprintf(adev->dev, GFP_KERNEL, "ssp%d Tx", i);
+ dai->capture.stream_name =
+ devm_kasprintf(adev->dev, GFP_KERNEL, "ssp%d Rx", i);
- if (!dai->name || !dai->playback.stream_name || !dai->capture.stream_name)
- return -ENOMEM;
- dai++;
+ if (!dai->name || !dai->playback.stream_name || !dai->capture.stream_name)
+ return -ENOMEM;
+ dai->ops = ops;
+ dai++;
+ }
}
if (!tdms)
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 =
@@ -1261,12 +1536,13 @@ 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++;
}
}
plat_register:
- return avs_soc_component_register(adev->dev, name, &avs_component_driver, cpus, cpu_count);
+ return avs_register_component(adev->dev, name, &avs_component_driver, cpus, cpu_count);
}
/* HD-Audio CPU DAI template */
@@ -1274,19 +1550,23 @@ 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_S24_LE |
SNDRV_PCM_FMTBIT_S32_LE,
+ .subformats = SNDRV_PCM_SUBFMTBIT_MSBITS_20 |
+ SNDRV_PCM_SUBFMTBIT_MSBITS_24 |
+ SNDRV_PCM_SUBFMTBIT_MSBITS_MAX,
},
.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_S24_LE |
SNDRV_PCM_FMTBIT_S32_LE,
+ .subformats = SNDRV_PCM_SUBFMTBIT_MSBITS_20 |
+ SNDRV_PCM_SUBFMTBIT_MSBITS_24 |
+ SNDRV_PCM_SUBFMTBIT_MSBITS_MAX,
},
};
@@ -1294,12 +1574,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;
@@ -1319,6 +1601,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;
@@ -1328,7 +1611,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)
@@ -1340,7 +1624,7 @@ static int avs_component_hda_probe(struct snd_soc_component *component)
return -ENOMEM;
cname = dev_name(&codec->core.dev);
- dapm = snd_soc_component_get_dapm(component);
+ dapm = snd_soc_component_to_dapm(component);
pcm = list_first_entry(&codec->pcm_list_head, struct hda_pcm, list);
for (i = 0; i < pcm_count; i++, pcm = list_next_entry(pcm, list)) {
@@ -1363,6 +1647,15 @@ static int avs_component_hda_probe(struct snd_soc_component *component)
ret = -ENOMEM;
goto exit;
}
+
+ if (!hda_codec_is_display(codec)) {
+ dais[i].playback.formats = pcm->stream[0].formats;
+ dais[i].playback.subformats = pcm->stream[0].subformats;
+ dais[i].playback.rates = pcm->stream[0].rates;
+ dais[i].playback.channels_min = pcm->stream[0].channels_min;
+ dais[i].playback.channels_max = pcm->stream[0].channels_max;
+ dais[i].playback.sig_bits = pcm->stream[0].maxbps;
+ }
}
if (pcm->stream[1].substreams) {
@@ -1373,6 +1666,15 @@ static int avs_component_hda_probe(struct snd_soc_component *component)
ret = -ENOMEM;
goto exit;
}
+
+ if (!hda_codec_is_display(codec)) {
+ dais[i].capture.formats = pcm->stream[1].formats;
+ dais[i].capture.subformats = pcm->stream[1].subformats;
+ dais[i].capture.rates = pcm->stream[1].rates;
+ dais[i].capture.channels_min = pcm->stream[1].channels_min;
+ dais[i].capture.channels_max = pcm->stream[1].channels_max;
+ dais[i].capture.sig_bits = pcm->stream[1].maxbps;
+ }
}
dai = snd_soc_register_dai(component, &dais[i], false);
@@ -1387,6 +1689,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;
}
}
@@ -1401,16 +1704,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 = asoc_substream_to_rtd(substream);
- struct hdac_ext_stream *link_stream;
- struct hda_codec *codec;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
if (!rtd->dai_link->no_pcm) {
struct snd_pcm_hardware hwparams = avs_pcm_hardware;
@@ -1442,41 +1743,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(asoc_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 = asoc_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,
@@ -1491,8 +1767,7 @@ static const struct snd_soc_component_driver avs_hda_component_driver = {
.topology_name_prefix = "intel/avs",
};
-int avs_hda_platform_register(struct avs_dev *adev, const char *name)
+int avs_register_hda_component(struct avs_dev *adev, const char *name)
{
- return avs_soc_component_register(adev->dev, name,
- &avs_hda_component_driver, NULL, 0);
+ return avs_register_component(adev->dev, name, &avs_hda_component_driver, NULL, 0);
}
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 4cab8c6c4576..74096236984a 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>
@@ -11,24 +11,32 @@
#include <sound/hdaudio.h>
#include <sound/soc.h>
#include "avs.h"
+#include "debug.h"
#include "messages.h"
-static int avs_dsp_init_probe(struct avs_dev *adev, union avs_connector_node_id node_id,
- size_t buffer_size)
+static int avs_dsp_init_probe(struct avs_dev *adev, struct snd_compr_params *params, int bps,
+ union avs_connector_node_id node_id, size_t buffer_size)
{
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
- * frame sizes are unused. It is also not owned by any pipeline.
+ * Probe module uses no cycles, input and output frame sizes are unused.
+ * It is also not owned by any pipeline.
*/
cfg.base.ibs = 1;
/* BSS module descriptor is always segment of index=2. */
cfg.base.is_pages = mentry.segments[2].flags.length;
+ cfg.base.audio_fmt.sampling_freq = params->codec.sample_rate;
+ cfg.base.audio_fmt.bit_depth = bps;
+ cfg.base.audio_fmt.num_channels = params->codec.ch_out;
+ cfg.base.audio_fmt.valid_bit_depth = bps;
cfg.gtw_cfg.node_id = node_id;
cfg.gtw_cfg.dma_buffer_size = buffer_size;
@@ -39,11 +47,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)
@@ -123,8 +132,6 @@ static int avs_probe_compr_set_params(struct snd_compr_stream *cstream,
struct hdac_ext_stream *host_stream = avs_compr_get_host_stream(cstream);
struct snd_compr_runtime *rtd = cstream->runtime;
struct avs_dev *adev = to_avs_dev(dai->dev);
- /* compr params do not store bit depth, default to S32_LE. */
- snd_pcm_format_t format = SNDRV_PCM_FORMAT_S32_LE;
unsigned int format_val;
int bps, ret;
@@ -137,15 +144,14 @@ static int avs_probe_compr_set_params(struct snd_compr_stream *cstream,
ret = snd_compr_malloc_pages(cstream, rtd->buffer_size);
if (ret < 0)
return ret;
- bps = snd_pcm_format_physical_width(format);
+ bps = snd_pcm_format_physical_width(params->codec.format);
if (bps < 0)
return bps;
- format_val = snd_hdac_calc_stream_format(params->codec.sample_rate, params->codec.ch_out,
- format, bps, 0);
+ format_val = snd_hdac_stream_format(params->codec.ch_out, bps, params->codec.sample_rate);
ret = snd_hdac_stream_set_params(hdac_stream(host_stream), format_val);
if (ret < 0)
return ret;
- ret = snd_hdac_stream_setup(hdac_stream(host_stream));
+ ret = snd_hdac_stream_setup(hdac_stream(host_stream), false);
if (ret < 0)
return ret;
@@ -162,7 +168,7 @@ static int avs_probe_compr_set_params(struct snd_compr_stream *cstream,
node_id.vindex = hdac_stream(host_stream)->stream_tag - 1;
node_id.dma_type = AVS_DMA_HDA_HOST_INPUT;
- ret = avs_dsp_init_probe(adev, node_id, rtd->dma_bytes);
+ ret = avs_dsp_init_probe(adev, params, bps, node_id, rtd->dma_bytes);
if (ret < 0) {
dev_err(dai->dev, "probe init failed: %d\n", ret);
avs_dsp_enable_d0ix(adev);
@@ -210,7 +216,7 @@ static int avs_probe_compr_trigger(struct snd_compr_stream *cstream, int cmd,
}
static int avs_probe_compr_pointer(struct snd_compr_stream *cstream,
- struct snd_compr_tstamp *tstamp, struct snd_soc_dai *dai)
+ struct snd_compr_tstamp64 *tstamp, struct snd_soc_dai *dai)
{
struct hdac_ext_stream *host_stream = avs_compr_get_host_stream(cstream);
struct snd_soc_pcm_stream *pstream;
@@ -287,8 +293,22 @@ static const struct snd_soc_component_driver avs_probe_component_driver = {
.module_get_upon_open = 1, /* increment refcount when a stream is opened */
};
-int avs_probe_platform_register(struct avs_dev *adev, const char *name)
+int avs_register_probe_component(struct avs_dev *adev, const char *name)
{
- return avs_soc_component_register(adev->dev, name, &avs_probe_component_driver,
- probe_cpu_dais, ARRAY_SIZE(probe_cpu_dais));
+ struct snd_soc_component *component;
+ int ret;
+
+ component = devm_kzalloc(adev->dev, sizeof(*component), GFP_KERNEL);
+ if (!component)
+ return -ENOMEM;
+
+ component->name = devm_kstrdup(adev->dev, name, GFP_KERNEL);
+ if (!component->name)
+ return -ENOMEM;
+
+ ret = snd_soc_component_initialize(component, &avs_probe_component_driver, adev->dev);
+ if (ret)
+ return ret;
+
+ return snd_soc_add_component(component, probe_cpu_dais, ARRAY_SIZE(probe_cpu_dais));
}
diff --git a/sound/soc/intel/avs/ptl.c b/sound/soc/intel/avs/ptl.c
new file mode 100644
index 000000000000..07da9b0aa2b8
--- /dev/null
+++ b/sound/soc/intel/avs/ptl.c
@@ -0,0 +1,99 @@
+// 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 "debug.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 2b464e466ed5..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,10 @@
#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
#define AZX_PCIREG_CGCTL 0x48
#define AZX_PGCTL_LSRMD_MASK BIT(4)
@@ -31,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)
@@ -48,18 +54,69 @@
#define SKL_ADSP_HIPCIE_DONE BIT(30)
#define SKL_ADSP_HIPCT_BUSY BIT(31)
+/* CNL Intel HD Audio Inter-Processor Communication Registers */
+#define CNL_ADSP_IPC_BASE 0xC0
+#define CNL_ADSP_REG_HIPCTDR (CNL_ADSP_IPC_BASE + 0x00)
+#define CNL_ADSP_REG_HIPCTDA (CNL_ADSP_IPC_BASE + 0x04)
+#define CNL_ADSP_REG_HIPCTDD (CNL_ADSP_IPC_BASE + 0x08)
+#define CNL_ADSP_REG_HIPCIDR (CNL_ADSP_IPC_BASE + 0x10)
+#define CNL_ADSP_REG_HIPCIDA (CNL_ADSP_IPC_BASE + 0x14)
+#define CNL_ADSP_REG_HIPCIDD (CNL_ADSP_IPC_BASE + 0x18)
+#define CNL_ADSP_REG_HIPCCTL (CNL_ADSP_IPC_BASE + 0x28)
+
+#define CNL_ADSP_HIPCTDR_BUSY BIT(31)
+#define CNL_ADSP_HIPCTDA_DONE BIT(31)
+#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 PAGE_SIZE
+#define AVS_WINDOW_CHUNK_SIZE SZ_4K
#define AVS_FW_REGS_SIZE AVS_WINDOW_CHUNK_SIZE
#define AVS_FW_REGS_WINDOW 0
/* DSP -> HOST communication window */
@@ -70,8 +127,8 @@
/* registry I/O helpers */
#define avs_sram_offset(adev, window_idx) \
- ((adev)->spec->sram_base_offset + \
- (adev)->spec->sram_window_size * (window_idx))
+ ((adev)->spec->sram->base_offset + \
+ (adev)->spec->sram->window_size * (window_idx))
#define avs_sram_addr(adev, window_idx) \
((adev)->dsp_ba + avs_sram_offset(adev, window_idx))
@@ -81,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 6bb8bbc70442..8fb86f364ff3 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,13 +10,76 @@
#include <linux/slab.h>
#include <sound/hdaudio_ext.h>
#include "avs.h"
+#include "cldma.h"
+#include "debug.h"
#include "messages.h"
+#include "registers.h"
+
+void avs_skl_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, 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
-skl_enable_logs(struct avs_dev *adev, enum avs_log_enable enable, u32 aging_period,
- u32 fifo_full_period, unsigned long resource_mask, u32 *priorities)
+avs_skl_enable_logs(struct avs_dev *adev, enum avs_log_enable enable, u32 aging_period,
+ u32 fifo_full_period, unsigned long resource_mask, u32 *priorities)
{
- struct skl_log_state_info *info;
+ struct avs_skl_log_state_info *info;
u32 size, num_cores = adev->hw_cfg.dsp_cores;
int ret, i;
@@ -45,7 +108,7 @@ skl_enable_logs(struct avs_dev *adev, enum avs_log_enable enable, u32 aging_peri
return 0;
}
-int skl_log_buffer_offset(struct avs_dev *adev, u32 core)
+int avs_skl_log_buffer_offset(struct avs_dev *adev, u32 core)
{
return core * avs_log_buffer_size(adev);
}
@@ -53,8 +116,7 @@ int skl_log_buffer_offset(struct avs_dev *adev, u32 core)
/* fw DbgLogWp registers */
#define FW_REGS_DBG_LOG_WP(core) (0x30 + 0x4 * core)
-static int
-skl_log_buffer_status(struct avs_dev *adev, union avs_notify_msg *msg)
+static int avs_skl_log_buffer_status(struct avs_dev *adev, union avs_notify_msg *msg)
{
void __iomem *buf;
u16 size, write, offset;
@@ -74,7 +136,7 @@ skl_log_buffer_status(struct avs_dev *adev, union avs_notify_msg *msg)
return 0;
}
-static int skl_coredump(struct avs_dev *adev, union avs_notify_msg *msg)
+static int avs_skl_coredump(struct avs_dev *adev, union avs_notify_msg *msg)
{
u8 *dump;
@@ -88,33 +150,31 @@ static int skl_coredump(struct avs_dev *adev, union avs_notify_msg *msg)
return 0;
}
-static bool
-skl_d0ix_toggle(struct avs_dev *adev, struct avs_ipc_msg *tx, bool wake)
+static bool avs_skl_d0ix_toggle(struct avs_dev *adev, struct avs_ipc_msg *tx, bool wake)
{
/* unsupported on cAVS 1.5 hw */
return false;
}
-static int skl_set_d0ix(struct avs_dev *adev, bool enable)
+static int avs_skl_set_d0ix(struct avs_dev *adev, bool enable)
{
/* unsupported on cAVS 1.5 hw */
return 0;
}
-const struct avs_dsp_ops skl_dsp_ops = {
+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_dsp_irq_handler,
- .irq_thread = avs_dsp_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,
.transfer_mods = avs_cldma_transfer_modules,
- .log_buffer_offset = skl_log_buffer_offset,
- .log_buffer_status = skl_log_buffer_status,
- .coredump = skl_coredump,
- .d0ix_toggle = skl_d0ix_toggle,
- .set_d0ix = skl_set_d0ix,
+ .log_buffer_offset = avs_skl_log_buffer_offset,
+ .log_buffer_status = avs_skl_log_buffer_status,
+ .coredump = avs_skl_coredump,
+ .d0ix_toggle = avs_skl_d0ix_toggle,
+ .set_d0ix = avs_skl_set_d0ix,
AVS_SET_ENABLE_LOGS_OP(skl)
};
diff --git a/sound/soc/intel/avs/sysfs.c b/sound/soc/intel/avs/sysfs.c
new file mode 100644
index 000000000000..74b2e6f38d76
--- /dev/null
+++ b/sound/soc/intel/avs/sysfs.c
@@ -0,0 +1,35 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2021-2024 Intel Corporation
+//
+// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+//
+
+#include <linux/sysfs.h>
+#include "avs.h"
+
+static ssize_t fw_version_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct avs_dev *adev = to_avs_dev(dev);
+ struct avs_fw_version *fw_version = &adev->fw_cfg.fw_version;
+
+ return sysfs_emit(buf, "%d.%d.%d.%d\n", fw_version->major, fw_version->minor,
+ fw_version->hotfix, fw_version->build);
+}
+static DEVICE_ATTR_RO(fw_version);
+
+static struct attribute *avs_fw_attrs[] = {
+ &dev_attr_fw_version.attr,
+ NULL
+};
+
+static const struct attribute_group avs_attr_group = {
+ .name = "avs",
+ .attrs = avs_fw_attrs,
+};
+
+const struct attribute_group *avs_attr_groups[] = {
+ &avs_attr_group,
+ NULL
+};
diff --git a/sound/soc/intel/avs/tgl.c b/sound/soc/intel/avs/tgl.c
new file mode 100644
index 000000000000..afb066516101
--- /dev/null
+++ b/sound/soc/intel/avs/tgl.c
@@ -0,0 +1,87 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// 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 "debug.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)
+{
+ core_mask &= AVS_MAIN_CORE_MASK;
+
+ if (!core_mask)
+ return 0;
+ return avs_dsp_core_power(adev, core_mask, power);
+}
+
+static int avs_tgl_dsp_core_reset(struct avs_dev *adev, u32 core_mask, bool reset)
+{
+ core_mask &= AVS_MAIN_CORE_MASK;
+
+ if (!core_mask)
+ return 0;
+ return avs_dsp_core_reset(adev, core_mask, reset);
+}
+
+static int avs_tgl_dsp_core_stall(struct avs_dev *adev, u32 core_mask, bool stall)
+{
+ core_mask &= AVS_MAIN_CORE_MASK;
+
+ if (!core_mask)
+ return 0;
+ 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,
+ .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,
+ .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/topology.c b/sound/soc/intel/avs/topology.c
index 45d0eb2a8e71..9033f683393c 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>
@@ -15,6 +15,7 @@
#include "avs.h"
#include "control.h"
#include "topology.h"
+#include "utils.h"
/* Get pointer to vendor array at the specified offset. */
#define avs_tplg_vendor_array_at(array, offset) \
@@ -349,6 +350,7 @@ AVS_DEFINE_PTR_PARSER(modcfg_base, struct avs_tplg_modcfg_base, modcfgs_base);
AVS_DEFINE_PTR_PARSER(modcfg_ext, struct avs_tplg_modcfg_ext, modcfgs_ext);
AVS_DEFINE_PTR_PARSER(pplcfg, struct avs_tplg_pplcfg, pplcfgs);
AVS_DEFINE_PTR_PARSER(binding, struct avs_tplg_binding, bindings);
+AVS_DEFINE_PTR_PARSER(nhlt_config, struct avs_tplg_nhlt_config, nhlt_configs);
static int
parse_audio_format_bitfield(struct snd_soc_component *comp, void *elem, void *object, u32 offset)
@@ -371,23 +373,67 @@ parse_audio_format_bitfield(struct snd_soc_component *comp, void *elem, void *ob
return 0;
}
+static int avs_ssp_sprint(char *buf, size_t size, const char *fmt, int port, int tdm)
+{
+ char *needle = strstr(fmt, "%d");
+ int retsize;
+
+ /*
+ * If there is %d present in fmt string it should be replaced by either
+ * SSP or SSP:TDM, where SSP and TDM are numbers, all other formatting
+ * will be ignored.
+ */
+ if (needle) {
+ retsize = scnprintf(buf, min_t(size_t, size, needle - fmt + 1), "%s", fmt);
+ retsize += scnprintf(buf + retsize, size - retsize, "%d", port);
+ if (tdm)
+ retsize += scnprintf(buf + retsize, size - retsize, ":%d", tdm);
+ retsize += scnprintf(buf + retsize, size - retsize, "%s", needle + 2);
+ return retsize;
+ }
+
+ return snprintf(buf, size, "%s", fmt);
+}
+
static int parse_link_formatted_string(struct snd_soc_component *comp, void *elem,
void *object, u32 offset)
{
struct snd_soc_tplg_vendor_string_elem *tuple = elem;
struct snd_soc_acpi_mach *mach = dev_get_platdata(comp->card->dev);
char *val = (char *)((u8 *)object + offset);
+ int ssp_port, tdm_slot;
/*
* Dynamic naming - string formats, e.g.: ssp%d - supported only for
* topologies describing single device e.g.: an I2S codec on SSP0.
*/
- if (hweight_long(mach->mach_params.i2s_link_mask) != 1)
+ if (!avs_mach_singular_ssp(mach))
+ return avs_parse_string_token(comp, elem, object, offset);
+
+ ssp_port = avs_mach_ssp_port(mach);
+ if (!avs_mach_singular_tdm(mach, ssp_port))
return avs_parse_string_token(comp, elem, object, offset);
- snprintf(val, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, tuple->string,
- __ffs(mach->mach_params.i2s_link_mask));
+ tdm_slot = avs_mach_ssp_tdm(mach, ssp_port);
+ avs_ssp_sprint(val, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, tuple->string, ssp_port, tdm_slot);
+
+ return 0;
+}
+
+static int avs_parse_nhlt_config_size(struct snd_soc_component *comp, void *elem, void *object,
+ u32 offset)
+{
+ struct snd_soc_tplg_vendor_value_elem *tuple = elem;
+ struct acpi_nhlt_config **blob = (struct acpi_nhlt_config **)((u8 *)object + offset);
+ u32 size;
+
+ size = le32_to_cpu(tuple->value);
+ *blob = devm_kzalloc(comp->card->dev, struct_size(*blob, capabilities, size), GFP_KERNEL);
+ if (!*blob)
+ return -ENOMEM;
+
+ (*blob)->capabilities_size = size;
return 0;
}
@@ -786,6 +832,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[] = {
@@ -813,6 +919,7 @@ static void
assign_copier_gtw_instance(struct snd_soc_component *comp, struct avs_tplg_modcfg_ext *cfg)
{
struct snd_soc_acpi_mach *mach;
+ int ssp_port, tdm_slot;
if (!guid_equal(&cfg->type, &AVS_COPIER_MOD_UUID))
return;
@@ -826,11 +933,22 @@ assign_copier_gtw_instance(struct snd_soc_component *comp, struct avs_tplg_modcf
return;
}
+ /* If topology sets value don't overwrite it */
+ if (cfg->copier.vindex.val)
+ return;
+
mach = dev_get_platdata(comp->card->dev);
- /* Automatic assignment only when board describes single SSP. */
- if (hweight_long(mach->mach_params.i2s_link_mask) == 1 && !cfg->copier.vindex.i2s.instance)
- cfg->copier.vindex.i2s.instance = __ffs(mach->mach_params.i2s_link_mask);
+ if (!avs_mach_singular_ssp(mach))
+ return;
+ ssp_port = avs_mach_ssp_port(mach);
+
+ if (!avs_mach_singular_tdm(mach, ssp_port))
+ return;
+ tdm_slot = avs_mach_ssp_tdm(mach, ssp_port);
+
+ cfg->copier.vindex.i2s.instance = ssp_port;
+ cfg->copier.vindex.i2s.time_slot = tdm_slot;
}
static int avs_tplg_parse_modcfg_ext(struct snd_soc_component *comp,
@@ -1077,6 +1195,27 @@ static const struct avs_tplg_token_parser module_parsers[] = {
.offset = offsetof(struct avs_tplg_module, ctl_id),
.parse = avs_parse_byte_token,
},
+ {
+ .token = AVS_TKN_MOD_INIT_CONFIG_NUM_IDS_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_module, num_config_ids),
+ .parse = avs_parse_byte_token,
+ },
+ {
+ .token = AVS_TKN_MOD_NHLT_CONFIG_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_module, nhlt_config),
+ .parse = avs_parse_nhlt_config_ptr,
+ },
+};
+
+static const struct avs_tplg_token_parser init_config_parsers[] = {
+ {
+ .token = AVS_TKN_MOD_INIT_CONFIG_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = 0,
+ .parse = avs_parse_word_token,
+ },
};
static struct avs_tplg_module *
@@ -1084,17 +1223,50 @@ avs_tplg_module_create(struct snd_soc_component *comp, struct avs_tplg_pipeline
struct snd_soc_tplg_vendor_array *tuples, u32 block_size)
{
struct avs_tplg_module *module;
+ u32 esize;
int ret;
+ /* See where config id block starts. */
+ ret = avs_tplg_vendor_entry_size(tuples, block_size,
+ AVS_TKN_MOD_INIT_CONFIG_ID_U32, &esize);
+ if (ret)
+ return ERR_PTR(ret);
+
module = devm_kzalloc(comp->card->dev, sizeof(*module), GFP_KERNEL);
if (!module)
return ERR_PTR(-ENOMEM);
ret = avs_parse_tokens(comp, module, module_parsers,
- ARRAY_SIZE(module_parsers), tuples, block_size);
+ ARRAY_SIZE(module_parsers), tuples, esize);
if (ret < 0)
return ERR_PTR(ret);
+ block_size -= esize;
+ /* Parse trailing config ids if any. */
+ if (block_size) {
+ u32 num_config_ids = module->num_config_ids;
+ u32 *config_ids;
+
+ if (!num_config_ids)
+ return ERR_PTR(-EINVAL);
+
+ config_ids = devm_kcalloc(comp->card->dev, num_config_ids, sizeof(*config_ids),
+ GFP_KERNEL);
+ if (!config_ids)
+ return ERR_PTR(-ENOMEM);
+
+ tuples = avs_tplg_vendor_array_at(tuples, esize);
+ ret = parse_dictionary_entries(comp, tuples, block_size,
+ config_ids, num_config_ids, sizeof(*config_ids),
+ AVS_TKN_MOD_INIT_CONFIG_ID_U32,
+ init_config_parsers,
+ ARRAY_SIZE(init_config_parsers));
+ if (ret)
+ return ERR_PTR(ret);
+
+ module->config_ids = config_ids;
+ }
+
module->owner = owner;
INIT_LIST_HEAD(&module->node);
@@ -1238,6 +1410,27 @@ static const struct avs_tplg_token_parser path_parsers[] = {
},
};
+static const struct avs_tplg_token_parser condpath_parsers[] = {
+ {
+ .token = AVS_TKN_CONDPATH_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_path, id),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_CONDPATH_SOURCE_PATH_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_path, source_path_id),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_CONDPATH_SINK_PATH_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_path, sink_path_id),
+ .parse = avs_parse_word_token,
+ },
+};
+
static struct avs_tplg_path *
avs_tplg_path_create(struct snd_soc_component *comp, struct avs_tplg_path_template *owner,
struct snd_soc_tplg_vendor_array *tuples, u32 block_size,
@@ -1305,6 +1498,39 @@ static const struct avs_tplg_token_parser path_tmpl_parsers[] = {
},
};
+static const struct avs_tplg_token_parser condpath_tmpl_parsers[] = {
+ {
+ .token = AVS_TKN_CONDPATH_TMPL_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_path_template, id),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_CONDPATH_TMPL_SOURCE_TPLG_NAME_STRING,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_STRING,
+ .offset = offsetof(struct avs_tplg_path_template, source.tplg_name),
+ .parse = avs_parse_string_token,
+ },
+ {
+ .token = AVS_TKN_CONDPATH_TMPL_SOURCE_PATH_TMPL_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_path_template, source.id),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_CONDPATH_TMPL_SINK_TPLG_NAME_STRING,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_STRING,
+ .offset = offsetof(struct avs_tplg_path_template, sink.tplg_name),
+ .parse = avs_parse_string_token,
+ },
+ {
+ .token = AVS_TKN_CONDPATH_TMPL_SINK_PATH_TMPL_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_path_template, sink.id),
+ .parse = avs_parse_word_token,
+ },
+};
+
static int parse_path_template(struct snd_soc_component *comp,
struct snd_soc_tplg_vendor_array *tuples, u32 block_size,
struct avs_tplg_path_template *template,
@@ -1375,26 +1601,236 @@ avs_tplg_path_template_create(struct snd_soc_component *comp, struct avs_tplg *o
return template;
}
+static int avs_tplg_parse_condpath_templates(struct snd_soc_component *comp,
+ struct snd_soc_tplg_vendor_array *tuples,
+ u32 block_size)
+{
+ struct avs_soc_component *acomp = to_avs_soc_component(comp);
+ struct avs_tplg *tplg = acomp->tplg;
+ int ret, i;
+
+ ret = parse_dictionary_header(comp, tuples, (void **)&tplg->condpath_tmpls,
+ &tplg->num_condpath_tmpls,
+ sizeof(*tplg->condpath_tmpls),
+ AVS_TKN_MANIFEST_NUM_CONDPATH_TMPLS_U32);
+ if (ret)
+ return ret;
+
+ block_size -= le32_to_cpu(tuples->size);
+ /* With header parsed, move on to parsing entries. */
+ tuples = avs_tplg_vendor_array_next(tuples);
+
+ for (i = 0; i < tplg->num_condpath_tmpls; i++) {
+ struct avs_tplg_path_template *template;
+ u32 esize;
+
+ template = &tplg->condpath_tmpls[i];
+ template->owner = tplg; /* Used when building sysfs hierarchy. */
+ INIT_LIST_HEAD(&template->path_list);
+ INIT_LIST_HEAD(&template->node);
+
+ ret = avs_tplg_vendor_entry_size(tuples, block_size,
+ AVS_TKN_CONDPATH_TMPL_ID_U32, &esize);
+ if (ret)
+ return ret;
+
+ ret = parse_path_template(comp, tuples, esize, template,
+ condpath_tmpl_parsers,
+ ARRAY_SIZE(condpath_tmpl_parsers),
+ condpath_parsers,
+ ARRAY_SIZE(condpath_parsers));
+ if (ret < 0) {
+ dev_err(comp->dev, "parse condpath_tmpl: %d failed: %d\n", i, ret);
+ return ret;
+ }
+
+ block_size -= esize;
+ tuples = avs_tplg_vendor_array_at(tuples, esize);
+ }
+
+ return 0;
+}
+
+static const struct avs_tplg_token_parser mod_init_config_parsers[] = {
+ {
+ .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,
+ },
+ {
+ .token = AVS_TKN_INIT_CONFIG_PARAM_U8,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_BYTE,
+ .offset = offsetof(struct avs_tplg_init_config, param),
+ .parse = avs_parse_byte_token,
+ },
+ {
+ .token = AVS_TKN_INIT_CONFIG_LENGTH_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_init_config, length),
+ .parse = avs_parse_word_token,
+ },
+};
+
+static int avs_tplg_parse_initial_configs(struct snd_soc_component *comp,
+ struct snd_soc_tplg_vendor_array *tuples,
+ u32 block_size, u32 *offset)
+{
+ struct avs_soc_component *acomp = to_avs_soc_component(comp);
+ struct avs_tplg *tplg = acomp->tplg;
+ int ret, i;
+
+ *offset = 0;
+
+ /* Parse tuple section telling how many init configs there are. */
+ ret = parse_dictionary_header(comp, tuples, (void **)&tplg->init_configs,
+ &tplg->num_init_configs,
+ sizeof(*tplg->init_configs),
+ AVS_TKN_MANIFEST_NUM_INIT_CONFIGS_U32);
+ if (ret)
+ return ret;
+
+ block_size -= le32_to_cpu(tuples->size);
+ *offset += le32_to_cpu(tuples->size);
+ /* With header parsed, move on to parsing entries. */
+ tuples = avs_tplg_vendor_array_next(tuples);
+
+ for (i = 0; i < tplg->num_init_configs && block_size > 0; i++) {
+ struct avs_tplg_init_config *config = &tplg->init_configs[i];
+ struct snd_soc_tplg_vendor_array *tmp;
+ void *init_config_data;
+ u32 esize;
+
+ /*
+ * Usually to get section length we search for first token of next group of data,
+ * but in this case we can't as tuples are followed by raw data.
+ */
+ tmp = avs_tplg_vendor_array_next(tuples);
+ esize = le32_to_cpu(tuples->size) + le32_to_cpu(tmp->size);
+ *offset += esize;
+
+ ret = parse_dictionary_entries(comp, tuples, esize, config, 1, sizeof(*config),
+ AVS_TKN_INIT_CONFIG_ID_U32,
+ mod_init_config_parsers,
+ ARRAY_SIZE(mod_init_config_parsers));
+
+ block_size -= esize;
+
+ /* handle raw data section */
+ init_config_data = (void *)tuples + esize;
+ esize = config->length;
+ *offset += esize;
+
+ config->data = devm_kmemdup(comp->card->dev, init_config_data, esize, GFP_KERNEL);
+ if (!config->data)
+ return -ENOMEM;
+
+ tuples = init_config_data + esize;
+ block_size -= esize;
+ }
+
+ return 0;
+}
+
+static const struct avs_tplg_token_parser mod_nhlt_config_parsers[] = {
+ {
+ .token = AVS_TKN_NHLT_CONFIG_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_nhlt_config, id),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_NHLT_CONFIG_SIZE_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_nhlt_config, blob),
+ .parse = avs_parse_nhlt_config_size,
+ },
+};
+
+static int avs_tplg_parse_nhlt_configs(struct snd_soc_component *comp,
+ struct snd_soc_tplg_vendor_array *tuples,
+ u32 block_size)
+{
+ struct avs_soc_component *acomp = to_avs_soc_component(comp);
+ struct avs_tplg *tplg = acomp->tplg;
+ int ret, i;
+
+ /* Parse the header section to know how many entries there are. */
+ ret = parse_dictionary_header(comp, tuples, (void **)&tplg->nhlt_configs,
+ &tplg->num_nhlt_configs,
+ sizeof(*tplg->nhlt_configs),
+ AVS_TKN_MANIFEST_NUM_NHLT_CONFIGS_U32);
+ if (ret)
+ return ret;
+
+ block_size -= le32_to_cpu(tuples->size);
+ /* With the header parsed, move on to parsing entries. */
+ tuples = avs_tplg_vendor_array_next(tuples);
+
+ for (i = 0; i < tplg->num_nhlt_configs && block_size > 0; i++) {
+ struct avs_tplg_nhlt_config *config;
+ u32 esize;
+
+ config = &tplg->nhlt_configs[i];
+ esize = le32_to_cpu(tuples->size);
+
+ ret = parse_dictionary_entries(comp, tuples, esize, config, 1, sizeof(*config),
+ AVS_TKN_NHLT_CONFIG_ID_U32,
+ mod_nhlt_config_parsers,
+ ARRAY_SIZE(mod_nhlt_config_parsers));
+ if (ret)
+ return ret;
+ /* With tuples parsed, the blob shall be allocated. */
+ if (!config->blob)
+ return -EINVAL;
+
+ /* Consume the raw data and move to the next entry. */
+ memcpy(config->blob->capabilities, (u8 *)tuples + esize,
+ config->blob->capabilities_size);
+ esize += config->blob->capabilities_size;
+
+ block_size -= esize;
+ tuples = avs_tplg_vendor_array_at(tuples, esize);
+ }
+
+ return 0;
+}
+
static int avs_route_load(struct snd_soc_component *comp, int index,
struct snd_soc_dapm_route *route)
{
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];
- u32 port;
+ int ssp_port, tdm_slot;
+ char *buf;
/* See parse_link_formatted_string() for dynamic naming when(s). */
- if (hweight_long(mach->mach_params.i2s_link_mask) == 1) {
- port = __ffs(mach->mach_params.i2s_link_mask);
-
- snprintf(buf, len, route->source, port);
- strscpy((char *)route->source, buf, len);
- snprintf(buf, len, route->sink, port);
- strscpy((char *)route->sink, buf, len);
- if (route->control) {
- snprintf(buf, len, route->control, port);
- strscpy((char *)route->control, buf, len);
- }
+ if (!avs_mach_singular_ssp(mach))
+ return 0;
+ ssp_port = avs_mach_ssp_port(mach);
+
+ if (!avs_mach_singular_tdm(mach, ssp_port))
+ 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);
+ 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);
+ 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);
+ route->control = buf;
}
return 0;
@@ -1408,10 +1844,13 @@ static int avs_widget_load(struct snd_soc_component *comp, int index,
struct avs_tplg_path_template *template;
struct avs_soc_component *acomp = to_avs_soc_component(comp);
struct avs_tplg *tplg;
+ int ssp_port, tdm_slot;
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;
@@ -1419,16 +1858,28 @@ static int avs_widget_load(struct snd_soc_component *comp, int index,
tplg = acomp->tplg;
mach = dev_get_platdata(comp->card->dev);
+ if (!avs_mach_singular_ssp(mach))
+ goto static_name;
+ ssp_port = avs_mach_ssp_port(mach);
/* See parse_link_formatted_string() for dynamic naming when(s). */
- if (hweight_long(mach->mach_params.i2s_link_mask) == 1) {
+ if (avs_mach_singular_tdm(mach, ssp_port)) {
+ /* 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);
+
+ buf = kmalloc(size, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+ avs_ssp_sprint(buf, size, dw->name, ssp_port, tdm_slot);
kfree(w->name);
/* w->name is freed later by soc_tplg_dapm_widget_create() */
- w->name = kasprintf(GFP_KERNEL, dw->name, __ffs(mach->mach_params.i2s_link_mask));
- if (!w->name)
- return -ENOMEM;
+ w->name = buf;
}
+static_name:
template = avs_tplg_path_template_create(comp, tplg, dw->priv.array,
le32_to_cpu(dw->priv.size));
if (IS_ERR(template)) {
@@ -1456,8 +1907,16 @@ static int avs_dai_load(struct snd_soc_component *comp, int index,
struct snd_soc_dai_driver *dai_drv, struct snd_soc_tplg_pcm *pcm,
struct snd_soc_dai *dai)
{
- if (pcm)
+ u32 fe_subformats = SNDRV_PCM_SUBFMTBIT_MSBITS_20 |
+ SNDRV_PCM_SUBFMTBIT_MSBITS_24 |
+ SNDRV_PCM_SUBFMTBIT_MSBITS_MAX;
+
+ if (pcm) {
dai_drv->ops = &avs_dai_fe_ops;
+ dai_drv->capture.subformats = fe_subformats;
+ dai_drv->playback.subformats = fe_subformats;
+ }
+
return 0;
}
@@ -1476,6 +1935,9 @@ static int avs_link_load(struct snd_soc_component *comp, int index, struct snd_s
/* Open LINK (BE) pipes last and close them first to prevent xruns. */
link->trigger[0] = SND_SOC_DPCM_TRIGGER_PRE;
link->trigger[1] = SND_SOC_DPCM_TRIGGER_PRE;
+ } else {
+ /* Do not ignore codec capabilities. */
+ link->dpcm_merged_format = 1;
}
return 0;
@@ -1502,6 +1964,7 @@ static int avs_manifest(struct snd_soc_component *comp, int index,
struct snd_soc_tplg_vendor_array *tuples = manifest->priv.array;
struct avs_soc_component *acomp = to_avs_soc_component(comp);
size_t remaining = le32_to_cpu(manifest->priv.size);
+ bool has_init_config = true;
u32 offset;
int ret;
@@ -1599,17 +2062,83 @@ static int avs_manifest(struct snd_soc_component *comp, int index,
remaining -= offset;
tuples = avs_tplg_vendor_array_at(tuples, offset);
+ ret = avs_tplg_vendor_array_lookup(tuples, remaining,
+ AVS_TKN_MANIFEST_NUM_CONDPATH_TMPLS_U32, &offset);
+ if (ret) {
+ dev_err(comp->dev, "condpath lookup failed: %d\n", ret);
+ return ret;
+ }
+
/* Bindings dictionary. */
- return avs_tplg_parse_bindings(comp, tuples, remaining);
+ ret = avs_tplg_parse_bindings(comp, tuples, offset);
+ if (ret < 0)
+ return ret;
+
+ remaining -= offset;
+ tuples = avs_tplg_vendor_array_at(tuples, offset);
+
+ ret = avs_tplg_vendor_array_lookup(tuples, remaining,
+ AVS_TKN_MANIFEST_NUM_INIT_CONFIGS_U32, &offset);
+ if (ret == -ENOENT) {
+ dev_dbg(comp->dev, "init config lookup failed: %d\n", ret);
+ has_init_config = false;
+ } else if (ret) {
+ dev_err(comp->dev, "init config lookup failed: %d\n", ret);
+ return ret;
+ }
+
+ /* Condpaths dictionary. */
+ ret = avs_tplg_parse_condpath_templates(comp, tuples,
+ has_init_config ? offset : remaining);
+ if (ret < 0)
+ return ret;
+
+ if (!has_init_config)
+ return 0;
+
+ remaining -= offset;
+ tuples = avs_tplg_vendor_array_at(tuples, offset);
+
+ /* Initial configs dictionary. */
+ ret = avs_tplg_parse_initial_configs(comp, tuples, remaining, &offset);
+ if (ret < 0)
+ return ret;
+
+ remaining -= offset;
+ tuples = avs_tplg_vendor_array_at(tuples, offset);
+
+ ret = avs_tplg_vendor_array_lookup(tuples, remaining,
+ AVS_TKN_MANIFEST_NUM_NHLT_CONFIGS_U32, &offset);
+ if (ret == -ENOENT)
+ return 0;
+ if (ret) {
+ dev_err(comp->dev, "NHLT config lookup failed: %d\n", ret);
+ return ret;
+ }
+
+ tuples = avs_tplg_vendor_array_at(tuples, offset);
+
+ /* NHLT configs dictionary. */
+ return avs_tplg_parse_nhlt_configs(comp, tuples, remaining);
}
-#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,
},
};
@@ -1631,18 +2160,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;
@@ -1653,12 +2184,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 6e1c8e9b2496..1cf7455b6c01 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>
@@ -33,6 +33,12 @@ struct avs_tplg {
u32 num_pplcfgs;
struct avs_tplg_binding *bindings;
u32 num_bindings;
+ struct avs_tplg_path_template *condpath_tmpls;
+ u32 num_condpath_tmpls;
+ struct avs_tplg_init_config *init_configs;
+ u32 num_init_configs;
+ struct avs_tplg_nhlt_config *nhlt_configs;
+ u32 num_nhlt_configs;
struct list_head path_tmpl_list;
};
@@ -71,13 +77,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 {
@@ -103,6 +116,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;
};
};
@@ -140,6 +158,10 @@ struct avs_tplg_path_template {
struct snd_soc_dapm_widget *w;
+ /* Conditional path. */
+ struct avs_tplg_path_template_id source;
+ struct avs_tplg_path_template_id sink;
+
struct list_head path_list;
struct avs_tplg *owner;
@@ -147,12 +169,28 @@ struct avs_tplg_path_template {
struct list_head node;
};
+struct avs_tplg_init_config {
+ u32 id;
+
+ u8 param;
+ size_t length;
+ void *data;
+};
+
+struct avs_tplg_nhlt_config {
+ u32 id;
+ struct acpi_nhlt_config *blob;
+};
+
struct avs_tplg_path {
u32 id;
/* Path format requirements. */
struct avs_audio_format *fe_fmt;
struct avs_audio_format *be_fmt;
+ /* Condpath path-variant requirements. */
+ u32 source_path_id;
+ u32 sink_path_id;
struct list_head ppl_list;
@@ -183,6 +221,9 @@ struct avs_tplg_module {
u8 domain;
struct avs_tplg_modcfg_ext *cfg_ext;
u32 ctl_id;
+ u32 num_config_ids;
+ u32 *config_ids;
+ struct avs_tplg_nhlt_config *nhlt_config;
struct avs_tplg_pipeline *owner;
/* Pipeline modules management. */
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
new file mode 100644
index 000000000000..955a40d2c30c
--- /dev/null
+++ b/sound/soc/intel/avs/utils.h
@@ -0,0 +1,77 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright(c) 2023 Intel Corporation
+ *
+ * Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+ * Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+ */
+
+#ifndef __SOUND_SOC_INTEL_AVS_UTILS_H
+#define __SOUND_SOC_INTEL_AVS_UTILS_H
+
+#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;
+}
+
+static inline u32 avs_mach_ssp_port(struct snd_soc_acpi_mach *mach)
+{
+ return __ffs(mach->mach_params.i2s_link_mask);
+}
+
+static inline bool avs_mach_singular_tdm(struct snd_soc_acpi_mach *mach, u32 port)
+{
+ 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)
+{
+ struct avs_mach_pdata *pdata = mach->pdata;
+ unsigned long *tdms = pdata->tdms;
+
+ return tdms ? __ffs(tdms[port]) : 0;
+}
+
+static inline int avs_mach_get_ssp_tdm(struct device *dev, struct snd_soc_acpi_mach *mach,
+ int *ssp_port, int *tdm_slot)
+{
+ int port;
+
+ if (!avs_mach_singular_ssp(mach)) {
+ dev_err(dev, "Invalid SSP configuration\n");
+ return -EINVAL;
+ }
+ port = avs_mach_ssp_port(mach);
+
+ if (!avs_mach_singular_tdm(mach, port)) {
+ dev_err(dev, "Invalid TDM configuration\n");
+ return -EINVAL;
+ }
+ *ssp_port = port;
+ *tdm_slot = avs_mach_ssp_tdm(mach, *ssp_port);
+
+ return 0;
+}
+
+/*
+ * Macro to easily generate format strings
+ */
+#define AVS_STRING_FMT(prefix, suffix, ssp, tdm) \
+ (tdm) ? prefix "%d:%d" suffix : prefix "%d" suffix, (ssp), (tdm)
+
+#endif