mirror of
https://github.com/torvalds/linux.git
synced 2024-11-23 12:42:02 +00:00
Merge series "ASoC: SOF: Intel: add SoundWire support" from Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>:
This patchset provides the support for SoundWire support on Intel CometLake, IcelLake and TigerLake RVP platforms and form-factor devices to be released 'soon'. The bulk of the code is about detecting a valid SoundWire configuration from ACPI, and implementing the interfaces suggested in '[PATCH 0/8] soundwire: remove platform devices, add SOF interfaces' for interrupts, PCI wakes and clock-stop configurations. Since that SoundWire series will not be in 5.7, the build support for SOF w/ SoundWire is not provided for now, and fall-back functions will be used. This code is tested on a daily basis in the SOF tree and is not expected to change in significant ways. Changes since v2: Corrected error in ACPI table (thanks Amadeusz) Added patch 11 to add reset cycle required on some SoundWire platforms Bard Liao (1): ASoC: SOF: Intel: hda: merge IPC, stream and SoundWire interrupt handlers Pierre-Louis Bossart (8): ASoC: soc-acpi: expand description of _ADR-based devices ASoC: SOF: Intel: add SoundWire configuration interface ASoC: SOF: IPC: dai-intel: move ALH declarations in header file ASoC: SOF: Intel: hda: add SoundWire stream config/free callbacks ASoC: SOF: Intel: hda: initial SoundWire machine driver autodetect ASoC: SOF: Intel: hda: disable SoundWire interrupts on suspend ASoC: SOF: Intel: hda: add parameter to control SoundWire clock stop quirks ASoC: SOF: Intel: hda-ctrl: add reset cycle before parsing capabilities Rander Wang (2): ASoC: SOF: Intel: hda: add WAKEEN interrupt support for SoundWire Asoc: SOF: Intel: hda: check SoundWire wakeen interrupt in irq thread include/sound/soc-acpi.h | 39 +- include/sound/sof/dai-intel.h | 18 +- .../intel/common/soc-acpi-intel-cml-match.c | 87 +++- .../intel/common/soc-acpi-intel-icl-match.c | 97 ++++- .../intel/common/soc-acpi-intel-tgl-match.c | 49 ++- sound/soc/sof/intel/hda-ctrl.c | 25 +- sound/soc/sof/intel/hda-dsp.c | 2 + sound/soc/sof/intel/hda-loader.c | 31 ++ sound/soc/sof/intel/hda.c | 400 ++++++++++++++++++ sound/soc/sof/intel/hda.h | 66 +++ 10 files changed, 750 insertions(+), 64 deletions(-) -- 2.20.1
This commit is contained in:
commit
ef11a6ff2b
@ -75,18 +75,45 @@ struct snd_soc_acpi_mach_params {
|
||||
};
|
||||
|
||||
/**
|
||||
* snd_soc_acpi_link_adr: ACPI-based list of _ADR, with a variable
|
||||
* number of devices per link
|
||||
*
|
||||
* snd_soc_acpi_endpoint - endpoint descriptor
|
||||
* @num: endpoint number (mandatory, unique per device)
|
||||
* @aggregated: 0 (independent) or 1 (logically grouped)
|
||||
* @group_position: zero-based order (only when @aggregated is 1)
|
||||
* @group_id: platform-unique group identifier (only when @aggregrated is 1)
|
||||
*/
|
||||
struct snd_soc_acpi_endpoint {
|
||||
u8 num;
|
||||
u8 aggregated;
|
||||
u8 group_position;
|
||||
u8 group_id;
|
||||
};
|
||||
|
||||
/**
|
||||
* snd_soc_acpi_adr_device - descriptor for _ADR-enumerated device
|
||||
* @adr: 64 bit ACPI _ADR value
|
||||
* @num_endpoints: number of endpoints for this device
|
||||
* @endpoints: array of endpoints
|
||||
*/
|
||||
struct snd_soc_acpi_adr_device {
|
||||
const u64 adr;
|
||||
const u8 num_endpoints;
|
||||
const struct snd_soc_acpi_endpoint *endpoints;
|
||||
};
|
||||
|
||||
/**
|
||||
* snd_soc_acpi_link_adr - ACPI-based list of _ADR enumerated devices
|
||||
* @mask: one bit set indicates the link this list applies to
|
||||
* @num_adr: ARRAY_SIZE of adr
|
||||
* @adr: array of _ADR (represented as u64).
|
||||
* @num_adr: ARRAY_SIZE of devices
|
||||
* @adr_d: array of devices
|
||||
*
|
||||
* The number of devices per link can be more than 1, e.g. in SoundWire
|
||||
* multi-drop configurations.
|
||||
*/
|
||||
|
||||
struct snd_soc_acpi_link_adr {
|
||||
const u32 mask;
|
||||
const u32 num_adr;
|
||||
const u64 *adr;
|
||||
const struct snd_soc_acpi_adr_device *adr_d;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -87,6 +87,15 @@ struct sof_ipc_dai_hda_params {
|
||||
uint32_t link_dma_ch;
|
||||
} __packed;
|
||||
|
||||
/* ALH Configuration Request - SOF_IPC_DAI_ALH_CONFIG */
|
||||
struct sof_ipc_dai_alh_params {
|
||||
struct sof_ipc_hdr hdr;
|
||||
uint32_t stream_id;
|
||||
|
||||
/* reserved for future use */
|
||||
uint32_t reserved[15];
|
||||
} __packed;
|
||||
|
||||
/* DMIC Configuration Request - SOF_IPC_DAI_DMIC_CONFIG */
|
||||
|
||||
/* This struct is defined per 2ch PDM controller available in the platform.
|
||||
@ -179,13 +188,4 @@ struct sof_ipc_dai_dmic_params {
|
||||
struct sof_ipc_dai_dmic_pdm_ctrl pdm[0];
|
||||
} __packed;
|
||||
|
||||
/* ALH Configuration Request - SOF_IPC_DAI_ALH_CONFIG */
|
||||
struct sof_ipc_dai_alh_params {
|
||||
struct sof_ipc_hdr hdr;
|
||||
uint32_t stream_id;
|
||||
|
||||
/* reserved for future use */
|
||||
uint32_t reserved[15];
|
||||
} __packed;
|
||||
|
||||
#endif
|
||||
|
@ -59,42 +59,95 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cml_machines[] = {
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_cml_machines);
|
||||
|
||||
static const u64 rt711_0_adr[] = {
|
||||
0x000010025D071100
|
||||
static const struct snd_soc_acpi_endpoint single_endpoint = {
|
||||
.num = 0,
|
||||
.aggregated = 0,
|
||||
.group_position = 0,
|
||||
.group_id = 0,
|
||||
};
|
||||
|
||||
static const u64 rt1308_1_adr[] = {
|
||||
0x000110025D130800
|
||||
static const struct snd_soc_acpi_endpoint spk_l_endpoint = {
|
||||
.num = 0,
|
||||
.aggregated = 1,
|
||||
.group_position = 0,
|
||||
.group_id = 1,
|
||||
};
|
||||
|
||||
static const u64 rt1308_2_adr[] = {
|
||||
0x000210025D130800
|
||||
static const struct snd_soc_acpi_endpoint spk_r_endpoint = {
|
||||
.num = 0,
|
||||
.aggregated = 1,
|
||||
.group_position = 1,
|
||||
.group_id = 1,
|
||||
};
|
||||
|
||||
static const u64 rt715_3_adr[] = {
|
||||
0x000310025D071500
|
||||
static const struct snd_soc_acpi_adr_device rt711_0_adr[] = {
|
||||
{
|
||||
.adr = 0x000010025D071100,
|
||||
.num_endpoints = 1,
|
||||
.endpoints = &single_endpoint,
|
||||
}
|
||||
};
|
||||
|
||||
static const struct snd_soc_acpi_adr_device rt1308_1_adr[] = {
|
||||
{
|
||||
.adr = 0x000110025D130800,
|
||||
.num_endpoints = 1,
|
||||
.endpoints = &single_endpoint,
|
||||
}
|
||||
};
|
||||
|
||||
static const struct snd_soc_acpi_adr_device rt1308_2_adr[] = {
|
||||
{
|
||||
.adr = 0x000210025D130800,
|
||||
.num_endpoints = 1,
|
||||
.endpoints = &single_endpoint,
|
||||
}
|
||||
};
|
||||
|
||||
static const struct snd_soc_acpi_adr_device rt1308_1_group1_adr[] = {
|
||||
{
|
||||
.adr = 0x000110025D130800,
|
||||
.num_endpoints = 1,
|
||||
.endpoints = &spk_l_endpoint,
|
||||
}
|
||||
};
|
||||
|
||||
static const struct snd_soc_acpi_adr_device rt1308_2_group1_adr[] = {
|
||||
{
|
||||
.adr = 0x000210025D130800,
|
||||
.num_endpoints = 1,
|
||||
.endpoints = &spk_r_endpoint,
|
||||
}
|
||||
};
|
||||
|
||||
static const struct snd_soc_acpi_adr_device rt715_3_adr[] = {
|
||||
{
|
||||
.adr = 0x000310025D071500,
|
||||
.num_endpoints = 1,
|
||||
.endpoints = &single_endpoint,
|
||||
}
|
||||
};
|
||||
|
||||
static const struct snd_soc_acpi_link_adr cml_3_in_1_default[] = {
|
||||
{
|
||||
.mask = BIT(0),
|
||||
.num_adr = ARRAY_SIZE(rt711_0_adr),
|
||||
.adr = rt711_0_adr,
|
||||
.adr_d = rt711_0_adr,
|
||||
},
|
||||
{
|
||||
.mask = BIT(1),
|
||||
.num_adr = ARRAY_SIZE(rt1308_1_adr),
|
||||
.adr = rt1308_1_adr,
|
||||
.num_adr = ARRAY_SIZE(rt1308_1_group1_adr),
|
||||
.adr_d = rt1308_1_group1_adr,
|
||||
},
|
||||
{
|
||||
.mask = BIT(2),
|
||||
.num_adr = ARRAY_SIZE(rt1308_2_adr),
|
||||
.adr = rt1308_2_adr,
|
||||
.num_adr = ARRAY_SIZE(rt1308_2_group1_adr),
|
||||
.adr_d = rt1308_2_group1_adr,
|
||||
},
|
||||
{
|
||||
.mask = BIT(3),
|
||||
.num_adr = ARRAY_SIZE(rt715_3_adr),
|
||||
.adr = rt715_3_adr,
|
||||
.adr_d = rt715_3_adr,
|
||||
},
|
||||
{}
|
||||
};
|
||||
@ -103,17 +156,17 @@ static const struct snd_soc_acpi_link_adr cml_3_in_1_mono_amp[] = {
|
||||
{
|
||||
.mask = BIT(0),
|
||||
.num_adr = ARRAY_SIZE(rt711_0_adr),
|
||||
.adr = rt711_0_adr,
|
||||
.adr_d = rt711_0_adr,
|
||||
},
|
||||
{
|
||||
.mask = BIT(1),
|
||||
.num_adr = ARRAY_SIZE(rt1308_1_adr),
|
||||
.adr = rt1308_1_adr,
|
||||
.adr_d = rt1308_1_adr,
|
||||
},
|
||||
{
|
||||
.mask = BIT(3),
|
||||
.num_adr = ARRAY_SIZE(rt715_3_adr),
|
||||
.adr = rt715_3_adr,
|
||||
.adr_d = rt715_3_adr,
|
||||
},
|
||||
{}
|
||||
};
|
||||
|
@ -33,55 +33,112 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_icl_machines[] = {
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_icl_machines);
|
||||
|
||||
static const u64 rt700_0_adr[] = {
|
||||
0x000010025D070000
|
||||
static const struct snd_soc_acpi_endpoint single_endpoint = {
|
||||
.num = 0,
|
||||
.aggregated = 0,
|
||||
.group_position = 0,
|
||||
.group_id = 0,
|
||||
};
|
||||
|
||||
static const struct snd_soc_acpi_endpoint spk_l_endpoint = {
|
||||
.num = 0,
|
||||
.aggregated = 1,
|
||||
.group_position = 0,
|
||||
.group_id = 1,
|
||||
};
|
||||
|
||||
static const struct snd_soc_acpi_endpoint spk_r_endpoint = {
|
||||
.num = 0,
|
||||
.aggregated = 1,
|
||||
.group_position = 1,
|
||||
.group_id = 1,
|
||||
};
|
||||
|
||||
static const struct snd_soc_acpi_adr_device rt700_0_adr[] = {
|
||||
{
|
||||
.adr = 0x000010025D070000,
|
||||
.num_endpoints = 1,
|
||||
.endpoints = &single_endpoint,
|
||||
}
|
||||
};
|
||||
|
||||
static const struct snd_soc_acpi_link_adr icl_rvp[] = {
|
||||
{
|
||||
.mask = BIT(0),
|
||||
.num_adr = ARRAY_SIZE(rt700_0_adr),
|
||||
.adr = rt700_0_adr,
|
||||
.adr_d = rt700_0_adr,
|
||||
},
|
||||
{}
|
||||
};
|
||||
|
||||
static const u64 rt711_0_adr[] = {
|
||||
0x000010025D071100
|
||||
static const struct snd_soc_acpi_adr_device rt711_0_adr[] = {
|
||||
{
|
||||
.adr = 0x000010025D071100,
|
||||
.num_endpoints = 1,
|
||||
.endpoints = &single_endpoint,
|
||||
}
|
||||
};
|
||||
|
||||
static const u64 rt1308_1_adr[] = {
|
||||
0x000110025D130800
|
||||
static const struct snd_soc_acpi_adr_device rt1308_1_adr[] = {
|
||||
{
|
||||
.adr = 0x000110025D130800,
|
||||
.num_endpoints = 1,
|
||||
.endpoints = &single_endpoint,
|
||||
}
|
||||
};
|
||||
|
||||
static const u64 rt1308_2_adr[] = {
|
||||
0x000210025D130800
|
||||
static const struct snd_soc_acpi_adr_device rt1308_2_adr[] = {
|
||||
{
|
||||
.adr = 0x000210025D130800,
|
||||
.num_endpoints = 1,
|
||||
.endpoints = &single_endpoint,
|
||||
}
|
||||
};
|
||||
|
||||
static const u64 rt715_3_adr[] = {
|
||||
0x000310025D071500
|
||||
static const struct snd_soc_acpi_adr_device rt1308_1_group1_adr[] = {
|
||||
{
|
||||
.adr = 0x000110025D130800,
|
||||
.num_endpoints = 1,
|
||||
.endpoints = &spk_l_endpoint,
|
||||
}
|
||||
};
|
||||
|
||||
static const struct snd_soc_acpi_adr_device rt1308_2_group1_adr[] = {
|
||||
{
|
||||
.adr = 0x000210025D130800,
|
||||
.num_endpoints = 1,
|
||||
.endpoints = &spk_r_endpoint,
|
||||
}
|
||||
};
|
||||
|
||||
static const struct snd_soc_acpi_adr_device rt715_3_adr[] = {
|
||||
{
|
||||
.adr = 0x000310025D071500,
|
||||
.num_endpoints = 1,
|
||||
.endpoints = &single_endpoint,
|
||||
}
|
||||
};
|
||||
|
||||
static const struct snd_soc_acpi_link_adr icl_3_in_1_default[] = {
|
||||
{
|
||||
.mask = BIT(0),
|
||||
.num_adr = ARRAY_SIZE(rt711_0_adr),
|
||||
.adr = rt711_0_adr,
|
||||
.adr_d = rt711_0_adr,
|
||||
},
|
||||
{
|
||||
.mask = BIT(1),
|
||||
.num_adr = ARRAY_SIZE(rt1308_1_adr),
|
||||
.adr = rt1308_1_adr,
|
||||
.num_adr = ARRAY_SIZE(rt1308_1_group1_adr),
|
||||
.adr_d = rt1308_1_group1_adr,
|
||||
},
|
||||
{
|
||||
.mask = BIT(2),
|
||||
.num_adr = ARRAY_SIZE(rt1308_2_adr),
|
||||
.adr = rt1308_2_adr,
|
||||
.num_adr = ARRAY_SIZE(rt1308_2_group1_adr),
|
||||
.adr_d = rt1308_2_group1_adr,
|
||||
},
|
||||
{
|
||||
.mask = BIT(3),
|
||||
.num_adr = ARRAY_SIZE(rt715_3_adr),
|
||||
.adr = rt715_3_adr,
|
||||
.adr_d = rt715_3_adr,
|
||||
},
|
||||
{}
|
||||
};
|
||||
@ -90,17 +147,17 @@ static const struct snd_soc_acpi_link_adr icl_3_in_1_mono_amp[] = {
|
||||
{
|
||||
.mask = BIT(0),
|
||||
.num_adr = ARRAY_SIZE(rt711_0_adr),
|
||||
.adr = rt711_0_adr,
|
||||
.adr_d = rt711_0_adr,
|
||||
},
|
||||
{
|
||||
.mask = BIT(1),
|
||||
.num_adr = ARRAY_SIZE(rt1308_1_adr),
|
||||
.adr = rt1308_1_adr,
|
||||
.adr_d = rt1308_1_adr,
|
||||
},
|
||||
{
|
||||
.mask = BIT(3),
|
||||
.num_adr = ARRAY_SIZE(rt715_3_adr),
|
||||
.adr = rt715_3_adr,
|
||||
.adr_d = rt715_3_adr,
|
||||
},
|
||||
{}
|
||||
};
|
||||
|
@ -14,20 +14,53 @@ static struct snd_soc_acpi_codecs tgl_codecs = {
|
||||
.codecs = {"MX98357A"}
|
||||
};
|
||||
|
||||
static const u64 rt711_0_adr[] = {
|
||||
0x000010025D071100
|
||||
static const struct snd_soc_acpi_endpoint single_endpoint = {
|
||||
.num = 0,
|
||||
.aggregated = 0,
|
||||
.group_position = 0,
|
||||
.group_id = 0,
|
||||
};
|
||||
|
||||
static const u64 rt1308_1_adr[] = {
|
||||
0x000120025D130800,
|
||||
0x000122025D130800
|
||||
static const struct snd_soc_acpi_endpoint spk_l_endpoint = {
|
||||
.num = 0,
|
||||
.aggregated = 1,
|
||||
.group_position = 0,
|
||||
.group_id = 1,
|
||||
};
|
||||
|
||||
static const struct snd_soc_acpi_endpoint spk_r_endpoint = {
|
||||
.num = 0,
|
||||
.aggregated = 1,
|
||||
.group_position = 1,
|
||||
.group_id = 1,
|
||||
};
|
||||
|
||||
static const struct snd_soc_acpi_adr_device rt711_0_adr[] = {
|
||||
{
|
||||
.adr = 0x000010025D071100,
|
||||
.num_endpoints = 1,
|
||||
.endpoints = &single_endpoint,
|
||||
}
|
||||
};
|
||||
|
||||
static const struct snd_soc_acpi_adr_device rt1308_1_adr[] = {
|
||||
{
|
||||
.adr = 0x000120025D130800,
|
||||
.num_endpoints = 1,
|
||||
.endpoints = &spk_l_endpoint,
|
||||
},
|
||||
{
|
||||
.adr = 0x000122025D130800,
|
||||
.num_endpoints = 1,
|
||||
.endpoints = &spk_r_endpoint,
|
||||
}
|
||||
};
|
||||
|
||||
static const struct snd_soc_acpi_link_adr tgl_i2s_rt1308[] = {
|
||||
{
|
||||
.mask = BIT(0),
|
||||
.num_adr = ARRAY_SIZE(rt711_0_adr),
|
||||
.adr = rt711_0_adr,
|
||||
.adr_d = rt711_0_adr,
|
||||
},
|
||||
{}
|
||||
};
|
||||
@ -36,12 +69,12 @@ static const struct snd_soc_acpi_link_adr tgl_rvp[] = {
|
||||
{
|
||||
.mask = BIT(0),
|
||||
.num_adr = ARRAY_SIZE(rt711_0_adr),
|
||||
.adr = rt711_0_adr,
|
||||
.adr_d = rt711_0_adr,
|
||||
},
|
||||
{
|
||||
.mask = BIT(1),
|
||||
.num_adr = ARRAY_SIZE(rt1308_1_adr),
|
||||
.adr = rt1308_1_adr,
|
||||
.adr_d = rt1308_1_adr,
|
||||
},
|
||||
{}
|
||||
};
|
||||
|
@ -65,15 +65,32 @@ int hda_dsp_ctrl_get_caps(struct snd_sof_dev *sdev)
|
||||
struct hdac_bus *bus = sof_to_bus(sdev);
|
||||
u32 cap, offset, feature;
|
||||
int count = 0;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* On some devices, one reset cycle is necessary before reading
|
||||
* capabilities
|
||||
*/
|
||||
ret = hda_dsp_ctrl_link_reset(sdev, true);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = hda_dsp_ctrl_link_reset(sdev, false);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
offset = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, SOF_HDA_LLCH);
|
||||
|
||||
do {
|
||||
cap = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, offset);
|
||||
|
||||
dev_dbg(sdev->dev, "checking for capabilities at offset 0x%x\n",
|
||||
offset & SOF_HDA_CAP_NEXT_MASK);
|
||||
|
||||
cap = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, offset);
|
||||
|
||||
if (cap == -1) {
|
||||
dev_dbg(bus->dev, "Invalid capability reg read\n");
|
||||
break;
|
||||
}
|
||||
|
||||
feature = (cap & SOF_HDA_CAP_ID_MASK) >> SOF_HDA_CAP_ID_OFF;
|
||||
|
||||
switch (feature) {
|
||||
@ -106,8 +123,8 @@ int hda_dsp_ctrl_get_caps(struct snd_sof_dev *sdev)
|
||||
bus->mlcap = bus->remap_addr + offset;
|
||||
break;
|
||||
default:
|
||||
dev_vdbg(sdev->dev, "found capability %d at 0x%x\n",
|
||||
feature, offset);
|
||||
dev_dbg(sdev->dev, "found capability %d at 0x%x\n",
|
||||
feature, offset);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -594,6 +594,8 @@ static int hda_suspend(struct snd_sof_dev *sdev, bool runtime_suspend)
|
||||
#endif
|
||||
int ret;
|
||||
|
||||
hda_sdw_int_enable(sdev, false);
|
||||
|
||||
/* disable IPC interrupts */
|
||||
hda_dsp_ipc_int_disable(sdev);
|
||||
|
||||
|
@ -346,6 +346,24 @@ int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev)
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/*
|
||||
* When a SoundWire link is in clock stop state, a Slave
|
||||
* device may trigger in-band wakes for events such as jack
|
||||
* insertion or acoustic event detection. This event will lead
|
||||
* to a WAKEEN interrupt, handled by the PCI device and routed
|
||||
* to PME if the PCI device is in D3. The resume function in
|
||||
* audio PCI driver will be invoked by ACPI for PME event and
|
||||
* initialize the device and process WAKEEN interrupt.
|
||||
*
|
||||
* The WAKEEN interrupt should be processed ASAP to prevent an
|
||||
* interrupt flood, otherwise other interrupts, such IPC,
|
||||
* cannot work normally. The WAKEEN is handled after the ROM
|
||||
* is initialized successfully, which ensures power rails are
|
||||
* enabled before accessing the SoundWire SHIM registers
|
||||
*/
|
||||
if (!sdev->first_boot)
|
||||
hda_sdw_process_wakeen(sdev);
|
||||
|
||||
/*
|
||||
* at this point DSP ROM has been initialized and
|
||||
* should be ready for code loading and firmware boot
|
||||
@ -399,6 +417,19 @@ int hda_dsp_pre_fw_run(struct snd_sof_dev *sdev)
|
||||
/* post fw run operations */
|
||||
int hda_dsp_post_fw_run(struct snd_sof_dev *sdev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (sdev->first_boot) {
|
||||
ret = hda_sdw_startup(sdev);
|
||||
if (ret < 0) {
|
||||
dev_err(sdev->dev,
|
||||
"error: could not startup SoundWire links\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
hda_sdw_int_enable(sdev, true);
|
||||
|
||||
/* re-enable clock gating and power gating */
|
||||
return hda_dsp_ctrl_clock_power_gating(sdev, true);
|
||||
}
|
||||
|
@ -18,10 +18,14 @@
|
||||
#include <sound/hdaudio_ext.h>
|
||||
#include <sound/hda_register.h>
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/soundwire/sdw.h>
|
||||
#include <linux/soundwire/sdw_intel.h>
|
||||
#include <sound/intel-nhlt.h>
|
||||
#include <sound/sof.h>
|
||||
#include <sound/sof/xtensa.h>
|
||||
#include "../sof-audio.h"
|
||||
#include "../ops.h"
|
||||
#include "hda.h"
|
||||
|
||||
@ -34,6 +38,235 @@
|
||||
|
||||
#define EXCEPT_MAX_HDR_SIZE 0x400
|
||||
|
||||
#if IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE)
|
||||
|
||||
/*
|
||||
* The default for SoundWire clock stop quirks is to power gate the IP
|
||||
* and do a Bus Reset, this will need to be modified when the DSP
|
||||
* needs to remain in D0i3 so that the Master does not lose context
|
||||
* and enumeration is not required on clock restart
|
||||
*/
|
||||
static int sdw_clock_stop_quirks = SDW_INTEL_CLK_STOP_BUS_RESET;
|
||||
module_param(sdw_clock_stop_quirks, int, 0444);
|
||||
MODULE_PARM_DESC(sdw_clock_stop_quirks, "SOF SoundWire clock stop quirks");
|
||||
|
||||
static int sdw_params_stream(struct device *dev,
|
||||
struct sdw_intel_stream_params_data *params_data)
|
||||
{
|
||||
struct snd_sof_dev *sdev = dev_get_drvdata(dev);
|
||||
struct snd_soc_dai *d = params_data->dai;
|
||||
struct sof_ipc_dai_config config;
|
||||
struct sof_ipc_reply reply;
|
||||
int link_id = params_data->link_id;
|
||||
int alh_stream_id = params_data->alh_stream_id;
|
||||
int ret;
|
||||
u32 size = sizeof(config);
|
||||
|
||||
memset(&config, 0, size);
|
||||
config.hdr.size = size;
|
||||
config.hdr.cmd = SOF_IPC_GLB_DAI_MSG | SOF_IPC_DAI_CONFIG;
|
||||
config.type = SOF_DAI_INTEL_ALH;
|
||||
config.dai_index = (link_id << 8) | (d->id);
|
||||
config.alh.stream_id = alh_stream_id;
|
||||
|
||||
/* send message to DSP */
|
||||
ret = sof_ipc_tx_message(sdev->ipc,
|
||||
config.hdr.cmd, &config, size, &reply,
|
||||
sizeof(reply));
|
||||
if (ret < 0) {
|
||||
dev_err(sdev->dev,
|
||||
"error: failed to set DAI hw_params for link %d dai->id %d ALH %d\n",
|
||||
link_id, d->id, alh_stream_id);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sdw_free_stream(struct device *dev,
|
||||
struct sdw_intel_stream_free_data *free_data)
|
||||
{
|
||||
struct snd_sof_dev *sdev = dev_get_drvdata(dev);
|
||||
struct snd_soc_dai *d = free_data->dai;
|
||||
struct sof_ipc_dai_config config;
|
||||
struct sof_ipc_reply reply;
|
||||
int link_id = free_data->link_id;
|
||||
int ret;
|
||||
u32 size = sizeof(config);
|
||||
|
||||
memset(&config, 0, size);
|
||||
config.hdr.size = size;
|
||||
config.hdr.cmd = SOF_IPC_GLB_DAI_MSG | SOF_IPC_DAI_CONFIG;
|
||||
config.type = SOF_DAI_INTEL_ALH;
|
||||
config.dai_index = (link_id << 8) | d->id;
|
||||
config.alh.stream_id = 0xFFFF; /* invalid value on purpose */
|
||||
|
||||
/* send message to DSP */
|
||||
ret = sof_ipc_tx_message(sdev->ipc,
|
||||
config.hdr.cmd, &config, size, &reply,
|
||||
sizeof(reply));
|
||||
if (ret < 0) {
|
||||
dev_err(sdev->dev,
|
||||
"error: failed to free stream for link %d dai->id %d\n",
|
||||
link_id, d->id);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct sdw_intel_ops sdw_callback = {
|
||||
.params_stream = sdw_params_stream,
|
||||
.free_stream = sdw_free_stream,
|
||||
};
|
||||
|
||||
void hda_sdw_int_enable(struct snd_sof_dev *sdev, bool enable)
|
||||
{
|
||||
sdw_intel_enable_irq(sdev->bar[HDA_DSP_BAR], enable);
|
||||
}
|
||||
|
||||
static int hda_sdw_acpi_scan(struct snd_sof_dev *sdev)
|
||||
{
|
||||
struct sof_intel_hda_dev *hdev;
|
||||
acpi_handle handle;
|
||||
int ret;
|
||||
|
||||
handle = ACPI_HANDLE(sdev->dev);
|
||||
|
||||
/* save ACPI info for the probe step */
|
||||
hdev = sdev->pdata->hw_pdata;
|
||||
|
||||
ret = sdw_intel_acpi_scan(handle, &hdev->info);
|
||||
if (ret < 0) {
|
||||
dev_err(sdev->dev, "%s failed\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hda_sdw_probe(struct snd_sof_dev *sdev)
|
||||
{
|
||||
struct sof_intel_hda_dev *hdev;
|
||||
struct sdw_intel_res res;
|
||||
void *sdw;
|
||||
|
||||
hdev = sdev->pdata->hw_pdata;
|
||||
|
||||
memset(&res, 0, sizeof(res));
|
||||
|
||||
res.mmio_base = sdev->bar[HDA_DSP_BAR];
|
||||
res.irq = sdev->ipc_irq;
|
||||
res.handle = hdev->info.handle;
|
||||
res.parent = sdev->dev;
|
||||
res.ops = &sdw_callback;
|
||||
res.dev = sdev->dev;
|
||||
res.clock_stop_quirks = sdw_clock_stop_quirks;
|
||||
|
||||
/*
|
||||
* ops and arg fields are not populated for now,
|
||||
* they will be needed when the DAI callbacks are
|
||||
* provided
|
||||
*/
|
||||
|
||||
/* we could filter links here if needed, e.g for quirks */
|
||||
res.count = hdev->info.count;
|
||||
res.link_mask = hdev->info.link_mask;
|
||||
|
||||
sdw = sdw_intel_probe(&res);
|
||||
if (!sdw) {
|
||||
dev_err(sdev->dev, "error: SoundWire probe failed\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* save context */
|
||||
hdev->sdw = sdw;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hda_sdw_startup(struct snd_sof_dev *sdev)
|
||||
{
|
||||
struct sof_intel_hda_dev *hdev;
|
||||
|
||||
hdev = sdev->pdata->hw_pdata;
|
||||
|
||||
if (!hdev->sdw)
|
||||
return 0;
|
||||
|
||||
return sdw_intel_startup(hdev->sdw);
|
||||
}
|
||||
|
||||
static int hda_sdw_exit(struct snd_sof_dev *sdev)
|
||||
{
|
||||
struct sof_intel_hda_dev *hdev;
|
||||
|
||||
hdev = sdev->pdata->hw_pdata;
|
||||
|
||||
hda_sdw_int_enable(sdev, false);
|
||||
|
||||
if (hdev->sdw)
|
||||
sdw_intel_exit(hdev->sdw);
|
||||
hdev->sdw = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool hda_dsp_check_sdw_irq(struct snd_sof_dev *sdev)
|
||||
{
|
||||
struct sof_intel_hda_dev *hdev;
|
||||
bool ret = false;
|
||||
u32 irq_status;
|
||||
|
||||
hdev = sdev->pdata->hw_pdata;
|
||||
|
||||
if (!hdev->sdw)
|
||||
return ret;
|
||||
|
||||
/* store status */
|
||||
irq_status = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPIS2);
|
||||
|
||||
/* invalid message ? */
|
||||
if (irq_status == 0xffffffff)
|
||||
goto out;
|
||||
|
||||
/* SDW message ? */
|
||||
if (irq_status & HDA_DSP_REG_ADSPIS2_SNDW)
|
||||
ret = true;
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static irqreturn_t hda_dsp_sdw_thread(int irq, void *context)
|
||||
{
|
||||
return sdw_intel_thread(irq, context);
|
||||
}
|
||||
|
||||
static bool hda_sdw_check_wakeen_irq(struct snd_sof_dev *sdev)
|
||||
{
|
||||
struct sof_intel_hda_dev *hdev;
|
||||
|
||||
hdev = sdev->pdata->hw_pdata;
|
||||
if (hdev->sdw &&
|
||||
snd_sof_dsp_read(sdev, HDA_DSP_BAR,
|
||||
HDA_DSP_REG_SNDW_WAKE_STS))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void hda_sdw_process_wakeen(struct snd_sof_dev *sdev)
|
||||
{
|
||||
struct sof_intel_hda_dev *hdev;
|
||||
|
||||
hdev = sdev->pdata->hw_pdata;
|
||||
if (!hdev->sdw)
|
||||
return;
|
||||
|
||||
sdw_intel_process_wakeen_event(hdev->sdw);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Debug
|
||||
*/
|
||||
@ -346,9 +579,12 @@ static const char *fixup_tplg_name(struct snd_sof_dev *sdev,
|
||||
static int hda_init_caps(struct snd_sof_dev *sdev)
|
||||
{
|
||||
struct hdac_bus *bus = sof_to_bus(sdev);
|
||||
struct snd_sof_pdata *pdata = sdev->pdata;
|
||||
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
|
||||
struct hdac_ext_link *hlink;
|
||||
#endif
|
||||
struct sof_intel_hda_dev *hdev = pdata->hw_pdata;
|
||||
u32 link_mask;
|
||||
int ret = 0;
|
||||
|
||||
device_disable_async_suspend(bus->dev);
|
||||
@ -365,6 +601,34 @@ static int hda_init_caps(struct snd_sof_dev *sdev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* scan SoundWire capabilities exposed by DSDT */
|
||||
ret = hda_sdw_acpi_scan(sdev);
|
||||
if (ret < 0) {
|
||||
dev_dbg(sdev->dev, "skipping SoundWire, ACPI scan error\n");
|
||||
goto skip_soundwire;
|
||||
}
|
||||
|
||||
link_mask = hdev->info.link_mask;
|
||||
if (!link_mask) {
|
||||
dev_dbg(sdev->dev, "skipping SoundWire, no links enabled\n");
|
||||
goto skip_soundwire;
|
||||
}
|
||||
|
||||
/*
|
||||
* probe/allocate SoundWire resources.
|
||||
* The hardware configuration takes place in hda_sdw_startup
|
||||
* after power rails are enabled.
|
||||
* It's entirely possible to have a mix of I2S/DMIC/SoundWire
|
||||
* devices, so we allocate the resources in all cases.
|
||||
*/
|
||||
ret = hda_sdw_probe(sdev);
|
||||
if (ret < 0) {
|
||||
dev_err(sdev->dev, "error: SoundWire probe error\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
skip_soundwire:
|
||||
|
||||
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
|
||||
if (bus->mlcap)
|
||||
snd_hdac_ext_bus_get_ml_capabilities(bus);
|
||||
@ -421,6 +685,7 @@ static irqreturn_t hda_dsp_interrupt_handler(int irq, void *context)
|
||||
static irqreturn_t hda_dsp_interrupt_thread(int irq, void *context)
|
||||
{
|
||||
struct snd_sof_dev *sdev = context;
|
||||
struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata;
|
||||
|
||||
/* deal with streams and controller first */
|
||||
if (hda_dsp_check_stream_irq(sdev))
|
||||
@ -429,6 +694,12 @@ static irqreturn_t hda_dsp_interrupt_thread(int irq, void *context)
|
||||
if (hda_dsp_check_ipc_irq(sdev))
|
||||
sof_ops(sdev)->irq_thread(irq, sdev);
|
||||
|
||||
if (hda_dsp_check_sdw_irq(sdev))
|
||||
hda_dsp_sdw_thread(irq, hdev->sdw);
|
||||
|
||||
if (hda_sdw_check_wakeen_irq(sdev))
|
||||
hda_sdw_process_wakeen(sdev);
|
||||
|
||||
/* enable GIE interrupt */
|
||||
snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR,
|
||||
SOF_HDA_INTCTL,
|
||||
@ -622,6 +893,8 @@ int hda_dsp_remove(struct snd_sof_dev *sdev)
|
||||
snd_hdac_ext_bus_device_remove(bus);
|
||||
#endif
|
||||
|
||||
hda_sdw_exit(sdev);
|
||||
|
||||
if (!IS_ERR_OR_NULL(hda->dmic_dev))
|
||||
platform_device_unregister(hda->dmic_dev);
|
||||
|
||||
@ -758,6 +1031,123 @@ static int hda_generic_machine_select(struct snd_sof_dev *sdev)
|
||||
}
|
||||
#endif
|
||||
|
||||
#if IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE)
|
||||
/* Check if all Slaves defined on the link can be found */
|
||||
static bool link_slaves_found(struct snd_sof_dev *sdev,
|
||||
const struct snd_soc_acpi_link_adr *link,
|
||||
struct sdw_intel_ctx *sdw)
|
||||
{
|
||||
struct hdac_bus *bus = sof_to_bus(sdev);
|
||||
struct sdw_intel_slave_id *ids = sdw->ids;
|
||||
int num_slaves = sdw->num_slaves;
|
||||
unsigned int part_id, link_id, unique_id, mfg_id;
|
||||
int i, j;
|
||||
|
||||
for (i = 0; i < link->num_adr; i++) {
|
||||
u64 adr = link->adr_d[i].adr;
|
||||
|
||||
mfg_id = SDW_MFG_ID(adr);
|
||||
part_id = SDW_PART_ID(adr);
|
||||
link_id = SDW_DISCO_LINK_ID(adr);
|
||||
for (j = 0; j < num_slaves; j++) {
|
||||
if (ids[j].link_id != link_id ||
|
||||
ids[j].id.part_id != part_id ||
|
||||
ids[j].id.mfg_id != mfg_id)
|
||||
continue;
|
||||
/*
|
||||
* we have to check unique id
|
||||
* if there is more than one
|
||||
* Slave on the link
|
||||
*/
|
||||
unique_id = SDW_UNIQUE_ID(adr);
|
||||
if (link->num_adr == 1 ||
|
||||
ids[j].id.unique_id == SDW_IGNORED_UNIQUE_ID ||
|
||||
ids[j].id.unique_id == unique_id) {
|
||||
dev_dbg(bus->dev,
|
||||
"found %x at link %d\n",
|
||||
part_id, link_id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (j == num_slaves) {
|
||||
dev_dbg(bus->dev,
|
||||
"Slave %x not found\n",
|
||||
part_id);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static int hda_sdw_machine_select(struct snd_sof_dev *sdev)
|
||||
{
|
||||
struct snd_sof_pdata *pdata = sdev->pdata;
|
||||
const struct snd_soc_acpi_link_adr *link;
|
||||
struct hdac_bus *bus = sof_to_bus(sdev);
|
||||
struct snd_soc_acpi_mach *mach;
|
||||
struct sof_intel_hda_dev *hdev;
|
||||
u32 link_mask;
|
||||
int i;
|
||||
|
||||
hdev = pdata->hw_pdata;
|
||||
link_mask = hdev->info.link_mask;
|
||||
|
||||
/*
|
||||
* Select SoundWire machine driver if needed using the
|
||||
* alternate tables. This case deals with SoundWire-only
|
||||
* machines, for mixed cases with I2C/I2S the detection relies
|
||||
* on the HID list.
|
||||
*/
|
||||
if (link_mask && !pdata->machine) {
|
||||
for (mach = pdata->desc->alt_machines;
|
||||
mach && mach->link_mask; mach++) {
|
||||
if (mach->link_mask != link_mask)
|
||||
continue;
|
||||
|
||||
/* No need to match adr if there is no links defined */
|
||||
if (!mach->links)
|
||||
break;
|
||||
|
||||
link = mach->links;
|
||||
for (i = 0; i < hdev->info.count && link->num_adr;
|
||||
i++, link++) {
|
||||
/*
|
||||
* Try next machine if any expected Slaves
|
||||
* are not found on this link.
|
||||
*/
|
||||
if (!link_slaves_found(sdev, link, hdev->sdw))
|
||||
break;
|
||||
}
|
||||
/* Found if all Slaves are checked */
|
||||
if (i == hdev->info.count || !link->num_adr)
|
||||
break;
|
||||
}
|
||||
if (mach && mach->link_mask) {
|
||||
dev_dbg(bus->dev,
|
||||
"SoundWire machine driver %s topology %s\n",
|
||||
mach->drv_name,
|
||||
mach->sof_tplg_filename);
|
||||
pdata->machine = mach;
|
||||
mach->mach_params.links = mach->links;
|
||||
mach->mach_params.link_mask = mach->link_mask;
|
||||
mach->mach_params.platform = dev_name(sdev->dev);
|
||||
pdata->fw_filename = mach->sof_fw_filename;
|
||||
pdata->tplg_filename = mach->sof_tplg_filename;
|
||||
} else {
|
||||
dev_info(sdev->dev,
|
||||
"No SoundWire machine driver found\n");
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
static int hda_sdw_machine_select(struct snd_sof_dev *sdev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
void hda_set_mach_params(const struct snd_soc_acpi_mach *mach,
|
||||
struct device *dev)
|
||||
{
|
||||
@ -777,8 +1167,18 @@ void hda_machine_select(struct snd_sof_dev *sdev)
|
||||
if (mach) {
|
||||
sof_pdata->tplg_filename = mach->sof_tplg_filename;
|
||||
sof_pdata->machine = mach;
|
||||
|
||||
if (mach->link_mask) {
|
||||
mach->mach_params.links = mach->links;
|
||||
mach->mach_params.link_mask = mach->link_mask;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If I2S fails, try SoundWire
|
||||
*/
|
||||
hda_sdw_machine_select(sdev);
|
||||
|
||||
/*
|
||||
* Choose HDA generic machine driver if mach is NULL.
|
||||
* Otherwise, set certain mach params.
|
||||
|
@ -11,6 +11,8 @@
|
||||
#ifndef __SOF_INTEL_HDA_H
|
||||
#define __SOF_INTEL_HDA_H
|
||||
|
||||
#include <linux/soundwire/sdw.h>
|
||||
#include <linux/soundwire/sdw_intel.h>
|
||||
#include <sound/compress_driver.h>
|
||||
#include <sound/hda_codec.h>
|
||||
#include <sound/hdaudio_ext.h>
|
||||
@ -230,6 +232,9 @@
|
||||
#define HDA_DSP_REG_ADSPIC2 (HDA_DSP_GEN_BASE + 0x10)
|
||||
#define HDA_DSP_REG_ADSPIS2 (HDA_DSP_GEN_BASE + 0x14)
|
||||
|
||||
#define HDA_DSP_REG_ADSPIS2_SNDW BIT(5)
|
||||
#define HDA_DSP_REG_SNDW_WAKE_STS 0x2C192
|
||||
|
||||
/* Intel HD Audio Inter-Processor Communication Registers */
|
||||
#define HDA_DSP_IPC_BASE 0x40
|
||||
#define HDA_DSP_REG_HIPCT (HDA_DSP_IPC_BASE + 0x00)
|
||||
@ -436,6 +441,12 @@ struct sof_intel_hda_dev {
|
||||
|
||||
/* delayed work to enter D0I3 opportunistically */
|
||||
struct delayed_work d0i3_work;
|
||||
|
||||
/* ACPI information stored between scan and probe steps */
|
||||
struct sdw_intel_acpi_info info;
|
||||
|
||||
/* sdw context allocated by SoundWire driver */
|
||||
struct sdw_intel_ctx *sdw;
|
||||
};
|
||||
|
||||
static inline struct hdac_bus *sof_to_bus(struct snd_sof_dev *s)
|
||||
@ -654,6 +665,61 @@ int hda_dsp_trace_init(struct snd_sof_dev *sdev, u32 *stream_tag);
|
||||
int hda_dsp_trace_release(struct snd_sof_dev *sdev);
|
||||
int hda_dsp_trace_trigger(struct snd_sof_dev *sdev, int cmd);
|
||||
|
||||
/*
|
||||
* SoundWire support
|
||||
*/
|
||||
#if IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE)
|
||||
|
||||
int hda_sdw_startup(struct snd_sof_dev *sdev);
|
||||
void hda_sdw_int_enable(struct snd_sof_dev *sdev, bool enable);
|
||||
void hda_sdw_process_wakeen(struct snd_sof_dev *sdev);
|
||||
|
||||
#else
|
||||
|
||||
static inline int hda_sdw_acpi_scan(struct snd_sof_dev *sdev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int hda_sdw_probe(struct snd_sof_dev *sdev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int hda_sdw_startup(struct snd_sof_dev *sdev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int hda_sdw_exit(struct snd_sof_dev *sdev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void hda_sdw_int_enable(struct snd_sof_dev *sdev, bool enable)
|
||||
{
|
||||
}
|
||||
|
||||
static inline bool hda_dsp_check_sdw_irq(struct snd_sof_dev *sdev)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline irqreturn_t hda_dsp_sdw_thread(int irq, void *context)
|
||||
{
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static inline bool hda_sdw_check_wakeen_irq(struct snd_sof_dev *sdev)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline void hda_sdw_process_wakeen(struct snd_sof_dev *sdev)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
/* common dai driver */
|
||||
extern struct snd_soc_dai_driver skl_dai[];
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user