Skip to content

Commit 95a3072

Browse files
committed
ASoC: SOF: Intel: HDA: add multi-card audio client registration
Add Intel-specific multi-card audio client registration that creates separate sound cards per codec function type when the multi_card module parameter is set. The registration groups SoundWire endpoints by DAI type (jack, amp, mic) and creates a card per group using codec-specific names (e.g. cs42l43, cs35l56) with a generic function name fallback (jack, speaker, mic). DMIC and HDMI are registered as additional separate cards. Individual card registration failures are non-fatal to allow partial audio functionality when some components fail to probe. When multi_card is not set (default), falls back to the generic single-card sof_register_audio_client(). This is experimental and opt-in via: modprobe snd-sof-intel-hda-generic multi_card=1 Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
1 parent 3e74fdd commit 95a3072

3 files changed

Lines changed: 246 additions & 2 deletions

File tree

sound/soc/sof/intel/hda-common-ops.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,8 +106,8 @@ const struct snd_sof_dsp_ops sof_hda_common_ops = {
106106
.unregister_ipc_clients = hda_unregister_clients,
107107

108108
/* audio client */
109-
.register_audio_client = sof_register_audio_client,
110-
.unregister_audio_client = sof_unregister_audio_client,
109+
.register_audio_client = hda_register_audio_client,
110+
.unregister_audio_client = hda_unregister_audio_client,
111111

112112
/* DAI drivers */
113113
.is_chain_dma_supported = hda_is_chain_dma_supported,

sound/soc/sof/intel/hda.c

Lines changed: 237 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131
#include <sound/sof/xtensa.h>
3232
#include <sound/hda-mlink.h>
3333
#include "../sof-audio.h"
34+
#include "../sof-client.h"
35+
#include "../sof-client-audio.h"
3436
#include "../sof-pci-dev.h"
3537
#include "../ops.h"
3638
#include "../ipc4-topology.h"
@@ -1767,6 +1769,241 @@ int hda_pci_intel_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
17671769
}
17681770
EXPORT_SYMBOL_NS(hda_pci_intel_probe, "SND_SOC_SOF_INTEL_HDA_GENERIC");
17691771

1772+
static bool multi_card;
1773+
module_param(multi_card, bool, 0444);
1774+
MODULE_PARM_DESC(multi_card, "Split audio into per-function cards. Experimental.");
1775+
1776+
struct hda_sdw_func_card {
1777+
u32 dai_type_bit;
1778+
const char *card_name;
1779+
};
1780+
1781+
static const struct hda_sdw_func_card sdw_func_cards[] = {
1782+
{ BIT(SOC_SDW_DAI_TYPE_JACK), "jack" },
1783+
{ BIT(SOC_SDW_DAI_TYPE_AMP), "speaker" },
1784+
{ BIT(SOC_SDW_DAI_TYPE_MIC), "mic" },
1785+
};
1786+
1787+
/*
1788+
* Group SDW DAI types that share physical codec devices. Types on the
1789+
* same device must be on the same card because ASoC components can only
1790+
* be bound to one card.
1791+
*
1792+
* Returns the number of groups. Each group's merged DAI type bitmask
1793+
* is stored in groups[] and the representative codec name in names[].
1794+
*/
1795+
static int hda_sdw_group_dai_types(const struct snd_soc_acpi_mach *mach,
1796+
u32 *groups, const char **names,
1797+
int max_groups)
1798+
{
1799+
const struct snd_soc_acpi_link_adr *adr_link;
1800+
int num_groups = 0;
1801+
int i, j, k;
1802+
1803+
for (adr_link = mach->mach_params.links;
1804+
adr_link && adr_link->num_adr; adr_link++) {
1805+
for (i = 0; i < adr_link->num_adr; i++) {
1806+
const struct snd_soc_acpi_adr_device *adr_dev =
1807+
&adr_link->adr_d[i];
1808+
struct asoc_sdw_codec_info *codec_info;
1809+
const char *dev_name = NULL;
1810+
u32 dev_types = 0;
1811+
int merged = -1;
1812+
1813+
codec_info = asoc_sdw_find_codec_info_part(adr_dev->adr);
1814+
if (!codec_info)
1815+
continue;
1816+
1817+
for (j = 0; j < adr_dev->num_endpoints; j++) {
1818+
int ep_num = adr_dev->endpoints[j].num;
1819+
1820+
if (ep_num < codec_info->dai_num)
1821+
dev_types |=
1822+
BIT(codec_info->dais[ep_num].dai_type);
1823+
}
1824+
1825+
if (!dev_types)
1826+
continue;
1827+
1828+
/*
1829+
* Pick a representative codec name: prefer
1830+
* name_prefix for non-amp codecs, fall back to
1831+
* the first DAI's component_name for amps.
1832+
*/
1833+
if (!codec_info->is_amp)
1834+
dev_name = codec_info->name_prefix;
1835+
else if (codec_info->dai_num &&
1836+
codec_info->dais[0].component_name)
1837+
dev_name = codec_info->dais[0].component_name;
1838+
1839+
/* Merge with any existing group that overlaps */
1840+
for (k = 0; k < num_groups; k++) {
1841+
if (!(groups[k] & dev_types))
1842+
continue;
1843+
1844+
if (merged < 0) {
1845+
groups[k] |= dev_types;
1846+
if (dev_name)
1847+
names[k] = dev_name;
1848+
merged = k;
1849+
} else {
1850+
groups[merged] |= groups[k];
1851+
groups[k] = groups[--num_groups];
1852+
names[k] = names[num_groups];
1853+
k--;
1854+
}
1855+
}
1856+
1857+
if (merged < 0 && num_groups < max_groups) {
1858+
groups[num_groups] = dev_types;
1859+
names[num_groups] = dev_name;
1860+
num_groups++;
1861+
}
1862+
}
1863+
}
1864+
1865+
return num_groups;
1866+
}
1867+
1868+
static int hda_register_audio_client_multi(struct snd_sof_dev *sdev)
1869+
{
1870+
static const struct snd_soc_acpi_link_adr empty_links[] = { {} };
1871+
const struct snd_soc_acpi_mach *mach = sdev->pdata->machine;
1872+
struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata;
1873+
struct sof_audio_client_pdata pdata;
1874+
u32 dai_groups[ARRAY_SIZE(sdw_func_cards)];
1875+
const char *group_names[ARRAY_SIZE(sdw_func_cards)];
1876+
int num_groups;
1877+
int card_idx = 0;
1878+
int ret, i, j;
1879+
1880+
if (!mach)
1881+
return -ENODEV;
1882+
1883+
num_groups = hda_sdw_group_dai_types(mach, dai_groups, group_names,
1884+
ARRAY_SIZE(dai_groups));
1885+
1886+
dev_dbg(sdev->dev, "multi_card: %d SDW groups, dmic: %d, idisp: %s\n",
1887+
num_groups, mach->mach_params.dmic_num,
1888+
HDA_IDISP_CODEC(mach->mach_params.codec_mask) ? "yes" : "no");
1889+
1890+
sof_audio_client_init_pdata(sdev, &pdata);
1891+
1892+
/* Create one card per SDW DAI type group */
1893+
for (i = 0; i < num_groups; i++) {
1894+
const char *card_name;
1895+
1896+
/*
1897+
* Prefer codec device name (e.g. "cs42l43") over the
1898+
* generic function type name (e.g. "jack").
1899+
*/
1900+
card_name = group_names[i];
1901+
if (!card_name) {
1902+
for (j = 0; j < ARRAY_SIZE(sdw_func_cards); j++) {
1903+
if (dai_groups[i] & sdw_func_cards[j].dai_type_bit) {
1904+
card_name = sdw_func_cards[j].card_name;
1905+
break;
1906+
}
1907+
}
1908+
}
1909+
if (!card_name)
1910+
continue;
1911+
1912+
dev_dbg(sdev->dev, "multi_card: group %d mask %#x card %s\n",
1913+
i, dai_groups[i], card_name);
1914+
1915+
memcpy(&pdata.machine, mach, sizeof(pdata.machine));
1916+
pdata.machine.mach_params.dai_type_mask = dai_groups[i];
1917+
pdata.machine.mach_params.codec_mask &= ~BIT(HDA_IDISP_ADDR);
1918+
pdata.machine.mach_params.dmic_num = 0;
1919+
pdata.machine.mach_params.card_name = card_name;
1920+
ret = sof_client_dev_register(sdev, "audio", card_idx,
1921+
&pdata, sizeof(pdata));
1922+
if (ret) {
1923+
dev_warn(sdev->dev,
1924+
"multi_card: failed to register card %s: %d\n",
1925+
card_name, ret);
1926+
continue;
1927+
}
1928+
card_idx++;
1929+
}
1930+
1931+
/* DMIC card */
1932+
if (mach->mach_params.dmic_num) {
1933+
memcpy(&pdata.machine, mach, sizeof(pdata.machine));
1934+
pdata.machine.mach_params.links = empty_links;
1935+
pdata.machine.mach_params.link_mask = 0;
1936+
pdata.machine.mach_params.codec_mask &= ~BIT(HDA_IDISP_ADDR);
1937+
pdata.machine.mach_params.card_name = "dmic";
1938+
ret = sof_client_dev_register(sdev, "audio", card_idx,
1939+
&pdata, sizeof(pdata));
1940+
if (ret)
1941+
dev_warn(sdev->dev,
1942+
"multi_card: failed to register card dmic: %d\n",
1943+
ret);
1944+
else
1945+
card_idx++;
1946+
}
1947+
1948+
/* HDMI card */
1949+
if (HDA_IDISP_CODEC(mach->mach_params.codec_mask)) {
1950+
memcpy(&pdata.machine, mach, sizeof(pdata.machine));
1951+
pdata.machine.mach_params.links = empty_links;
1952+
pdata.machine.mach_params.link_mask = 0;
1953+
pdata.machine.mach_params.codec_mask = BIT(HDA_IDISP_ADDR);
1954+
pdata.machine.mach_params.dmic_num = 0;
1955+
pdata.machine.mach_params.card_name = "hdmi";
1956+
ret = sof_client_dev_register(sdev, "audio", card_idx,
1957+
&pdata, sizeof(pdata));
1958+
if (ret)
1959+
dev_warn(sdev->dev,
1960+
"multi_card: failed to register card hdmi: %d\n",
1961+
ret);
1962+
else
1963+
card_idx++;
1964+
}
1965+
1966+
hdev->num_audio_clients = card_idx;
1967+
1968+
return 0;
1969+
}
1970+
1971+
int hda_register_audio_client(struct snd_sof_dev *sdev)
1972+
{
1973+
if (multi_card) {
1974+
const struct snd_soc_acpi_mach *mach = sdev->pdata->machine;
1975+
1976+
if (mach && !sdev->pdata->disable_function_topology &&
1977+
mach->get_function_tplg_files)
1978+
return hda_register_audio_client_multi(sdev);
1979+
1980+
dev_warn(sdev->dev,
1981+
"multi_card is only supported with function topologies, using single card\n");
1982+
}
1983+
1984+
return sof_register_audio_client(sdev);
1985+
}
1986+
1987+
void hda_unregister_audio_client(struct snd_sof_dev *sdev)
1988+
{
1989+
if (multi_card) {
1990+
const struct snd_soc_acpi_mach *mach = sdev->pdata->machine;
1991+
1992+
if (mach && !sdev->pdata->disable_function_topology &&
1993+
mach->get_function_tplg_files) {
1994+
struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata;
1995+
int i;
1996+
1997+
for (i = hdev->num_audio_clients - 1; i >= 0; i--)
1998+
sof_client_dev_unregister(sdev, "audio", i);
1999+
hdev->num_audio_clients = 0;
2000+
return;
2001+
}
2002+
}
2003+
2004+
sof_unregister_audio_client(sdev);
2005+
}
2006+
17702007
int hda_register_clients(struct snd_sof_dev *sdev)
17712008
{
17722009
return hda_probes_register(sdev);

sound/soc/sof/intel/hda.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -580,6 +580,9 @@ struct sof_intel_hda_dev {
580580
* is received from the DSP for the previous message)
581581
*/
582582
struct snd_sof_ipc_msg *delayed_ipc_tx_msg;
583+
584+
/* number of audio client devices registered in multi-card mode */
585+
int num_audio_clients;
583586
};
584587

585588
static inline struct hdac_bus *sof_to_bus(struct snd_sof_dev *s)
@@ -1012,6 +1015,10 @@ static inline void hda_probes_unregister(struct snd_sof_dev *sdev)
10121015
int hda_register_clients(struct snd_sof_dev *sdev);
10131016
void hda_unregister_clients(struct snd_sof_dev *sdev);
10141017

1018+
/* SOF audio client registration for HDA platforms (multi-card) */
1019+
int hda_register_audio_client(struct snd_sof_dev *sdev);
1020+
void hda_unregister_audio_client(struct snd_sof_dev *sdev);
1021+
10151022
/* machine driver select */
10161023
struct snd_soc_acpi_mach *hda_machine_select(struct snd_sof_dev *sdev);
10171024
void hda_set_mach_params(struct snd_soc_acpi_mach *mach,

0 commit comments

Comments
 (0)