Skip to content

Commit

Permalink
ASoC: Intel: sof_cs42l42: support arbitrary DAI link sequence
Browse files Browse the repository at this point in the history
Currently there are two types of DAI Link sequences defined in
topology files, one starts from speaker link and the other starts from
headphone link.

Type 1: SPK - HP - DMIC - HDMI
Type 2: HP - DMIC - HDMI - SPK

Here we add a macro SOF_CS42L42_DAILINK to define the DAI Link
sequence in quirk and refactor the sof_card_dai_links_create()
function to support arbitrary DAI Link sequence.

Reviewed-by: Bard Liao <bard.liao@intel.com>
Signed-off-by: Brent Lu <brent.lu@intel.com>
Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Link: https://lore.kernel.org/r/20210712203240.46960-2-pierre-louis.bossart@linux.intel.com
Signed-off-by: Mark Brown <broonie@kernel.org>
  • Loading branch information
brentlu authored and broonie committed Jul 14, 2021
1 parent eb82bae commit 3cb97cf
Showing 1 changed file with 230 additions and 112 deletions.
342 changes: 230 additions & 112 deletions sound/soc/intel/boards/sof_cs42l42.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,19 @@
#define SOF_CS42L42_NUM_HDMIDEV_MASK (GENMASK(9, 7))
#define SOF_CS42L42_NUM_HDMIDEV(quirk) \
(((quirk) << SOF_CS42L42_NUM_HDMIDEV_SHIFT) & SOF_CS42L42_NUM_HDMIDEV_MASK)
#define SOF_MAX98357A_SPEAKER_AMP_PRESENT BIT(10)
#define SOF_CS42L42_DAILINK_SHIFT 10
#define SOF_CS42L42_DAILINK_MASK (GENMASK(24, 10))
#define SOF_CS42L42_DAILINK(link1, link2, link3, link4, link5) \
((((link1) | ((link2) << 3) | ((link3) << 6) | ((link4) << 9) | ((link5) << 12)) << SOF_CS42L42_DAILINK_SHIFT) & SOF_CS42L42_DAILINK_MASK)
#define SOF_MAX98357A_SPEAKER_AMP_PRESENT BIT(25)

enum {
LINK_NONE = 0,
LINK_HP = 1,
LINK_SPK = 2,
LINK_DMIC = 3,
LINK_HDMI = 4,
};

/* Default: SSP2 */
static unsigned long sof_cs42l42_quirk = SOF_CS42L42_SSP_CODEC(2);
Expand Down Expand Up @@ -265,133 +277,166 @@ static struct snd_soc_dai_link_component dmic_component[] = {
}
};

static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev,
int ssp_codec,
int ssp_amp,
int dmic_be_num,
int hdmi_num)
static int create_spk_amp_dai_links(struct device *dev,
struct snd_soc_dai_link *links,
struct snd_soc_dai_link_component *cpus,
int *id, int ssp_amp)
{
struct snd_soc_dai_link_component *idisp_components;
struct snd_soc_dai_link_component *cpus;
struct snd_soc_dai_link *links;
int i, id = 0;

links = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link) *
sof_audio_card_cs42l42.num_links, GFP_KERNEL);
cpus = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link_component) *
sof_audio_card_cs42l42.num_links, GFP_KERNEL);
if (!links || !cpus)
goto devm_err;
int ret = 0;

/* speaker amp */
if (sof_cs42l42_quirk & SOF_SPEAKER_AMP_PRESENT) {
links[id].name = devm_kasprintf(dev, GFP_KERNEL,
"SSP%d-Codec", ssp_amp);
if (!links[id].name)
goto devm_err;
if (!(sof_cs42l42_quirk & SOF_SPEAKER_AMP_PRESENT))
return 0;

links[id].id = id;
links[*id].name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-Codec",
ssp_amp);
if (!links[*id].name) {
ret = -ENOMEM;
goto devm_err;
}

if (sof_cs42l42_quirk & SOF_MAX98357A_SPEAKER_AMP_PRESENT) {
max_98357a_dai_link(&links[id]);
} else {
dev_err(dev, "no amp defined\n");
goto devm_err;
}
links[*id].id = *id;

links[id].platforms = platform_component;
links[id].num_platforms = ARRAY_SIZE(platform_component);
links[id].dpcm_playback = 1;
links[id].no_pcm = 1;
links[id].cpus = &cpus[id];
links[id].num_cpus = 1;

links[id].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
"SSP%d Pin",
ssp_amp);
if (!links[id].cpus->dai_name)
goto devm_err;
if (sof_cs42l42_quirk & SOF_MAX98357A_SPEAKER_AMP_PRESENT) {
max_98357a_dai_link(&links[*id]);
} else {
dev_err(dev, "no amp defined\n");
ret = -EINVAL;
goto devm_err;
}

id++;
links[*id].platforms = platform_component;
links[*id].num_platforms = ARRAY_SIZE(platform_component);
links[*id].dpcm_playback = 1;
links[*id].no_pcm = 1;
links[*id].cpus = &cpus[*id];
links[*id].num_cpus = 1;

links[*id].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
"SSP%d Pin", ssp_amp);
if (!links[*id].cpus->dai_name) {
ret = -ENOMEM;
goto devm_err;
}

(*id)++;

devm_err:
return ret;
}

static int create_hp_codec_dai_links(struct device *dev,
struct snd_soc_dai_link *links,
struct snd_soc_dai_link_component *cpus,
int *id, int ssp_codec)
{
/* codec SSP */
links[id].name = devm_kasprintf(dev, GFP_KERNEL,
"SSP%d-Codec", ssp_codec);
if (!links[id].name)
links[*id].name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-Codec",
ssp_codec);
if (!links[*id].name)
goto devm_err;

links[id].id = id;
links[id].codecs = cs42l42_component;
links[id].num_codecs = ARRAY_SIZE(cs42l42_component);
links[id].platforms = platform_component;
links[id].num_platforms = ARRAY_SIZE(platform_component);
links[id].init = sof_cs42l42_init;
links[id].exit = sof_cs42l42_exit;
links[id].ops = &sof_cs42l42_ops;
links[id].dpcm_playback = 1;
links[id].dpcm_capture = 1;
links[id].no_pcm = 1;
links[id].cpus = &cpus[id];
links[id].num_cpus = 1;

links[id].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
"SSP%d Pin",
ssp_codec);
if (!links[id].cpus->dai_name)
links[*id].id = *id;
links[*id].codecs = cs42l42_component;
links[*id].num_codecs = ARRAY_SIZE(cs42l42_component);
links[*id].platforms = platform_component;
links[*id].num_platforms = ARRAY_SIZE(platform_component);
links[*id].init = sof_cs42l42_init;
links[*id].exit = sof_cs42l42_exit;
links[*id].ops = &sof_cs42l42_ops;
links[*id].dpcm_playback = 1;
links[*id].dpcm_capture = 1;
links[*id].no_pcm = 1;
links[*id].cpus = &cpus[*id];
links[*id].num_cpus = 1;

links[*id].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
"SSP%d Pin",
ssp_codec);
if (!links[*id].cpus->dai_name)
goto devm_err;

id++;
(*id)++;

return 0;

devm_err:
return -ENOMEM;
}

static int create_dmic_dai_links(struct device *dev,
struct snd_soc_dai_link *links,
struct snd_soc_dai_link_component *cpus,
int *id, int dmic_be_num)
{
int i;

/* dmic */
if (dmic_be_num > 0) {
/* at least we have dmic01 */
links[id].name = "dmic01";
links[id].cpus = &cpus[id];
links[id].cpus->dai_name = "DMIC01 Pin";
links[id].init = dmic_init;
if (dmic_be_num > 1) {
/* set up 2 BE links at most */
links[id + 1].name = "dmic16k";
links[id + 1].cpus = &cpus[id + 1];
links[id + 1].cpus->dai_name = "DMIC16k Pin";
dmic_be_num = 2;
}
if (dmic_be_num <= 0)
return 0;

/* at least we have dmic01 */
links[*id].name = "dmic01";
links[*id].cpus = &cpus[*id];
links[*id].cpus->dai_name = "DMIC01 Pin";
links[*id].init = dmic_init;
if (dmic_be_num > 1) {
/* set up 2 BE links at most */
links[*id + 1].name = "dmic16k";
links[*id + 1].cpus = &cpus[*id + 1];
links[*id + 1].cpus->dai_name = "DMIC16k Pin";
dmic_be_num = 2;
}

for (i = 0; i < dmic_be_num; i++) {
links[id].id = id;
links[id].num_cpus = 1;
links[id].codecs = dmic_component;
links[id].num_codecs = ARRAY_SIZE(dmic_component);
links[id].platforms = platform_component;
links[id].num_platforms = ARRAY_SIZE(platform_component);
links[id].ignore_suspend = 1;
links[id].dpcm_capture = 1;
links[id].no_pcm = 1;
id++;
links[*id].id = *id;
links[*id].num_cpus = 1;
links[*id].codecs = dmic_component;
links[*id].num_codecs = ARRAY_SIZE(dmic_component);
links[*id].platforms = platform_component;
links[*id].num_platforms = ARRAY_SIZE(platform_component);
links[*id].ignore_suspend = 1;
links[*id].dpcm_capture = 1;
links[*id].no_pcm = 1;

(*id)++;
}

return 0;
}

static int create_hdmi_dai_links(struct device *dev,
struct snd_soc_dai_link *links,
struct snd_soc_dai_link_component *cpus,
int *id, int hdmi_num)
{
struct snd_soc_dai_link_component *idisp_components;
int i;

/* HDMI */
if (hdmi_num > 0) {
idisp_components = devm_kzalloc(dev,
sizeof(struct snd_soc_dai_link_component) *
hdmi_num, GFP_KERNEL);
if (!idisp_components)
goto devm_err;
}
if (hdmi_num <= 0)
return 0;

idisp_components = devm_kzalloc(dev,
sizeof(struct snd_soc_dai_link_component) *
hdmi_num, GFP_KERNEL);
if (!idisp_components)
goto devm_err;

for (i = 1; i <= hdmi_num; i++) {
links[id].name = devm_kasprintf(dev, GFP_KERNEL,
"iDisp%d", i);
if (!links[id].name)
links[*id].name = devm_kasprintf(dev, GFP_KERNEL,
"iDisp%d", i);
if (!links[*id].name)
goto devm_err;

links[id].id = id;
links[id].cpus = &cpus[id];
links[id].num_cpus = 1;
links[id].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
"iDisp%d Pin", i);
if (!links[id].cpus->dai_name)
links[*id].id = *id;
links[*id].cpus = &cpus[*id];
links[*id].num_cpus = 1;
links[*id].cpus->dai_name = devm_kasprintf(dev,
GFP_KERNEL,
"iDisp%d Pin",
i);
if (!links[*id].cpus->dai_name)
goto devm_err;

idisp_components[i - 1].name = "ehdaudio0D2";
Expand All @@ -402,14 +447,86 @@ static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev,
if (!idisp_components[i - 1].dai_name)
goto devm_err;

links[id].codecs = &idisp_components[i - 1];
links[id].num_codecs = 1;
links[id].platforms = platform_component;
links[id].num_platforms = ARRAY_SIZE(platform_component);
links[id].init = sof_hdmi_init;
links[id].dpcm_playback = 1;
links[id].no_pcm = 1;
id++;
links[*id].codecs = &idisp_components[i - 1];
links[*id].num_codecs = 1;
links[*id].platforms = platform_component;
links[*id].num_platforms = ARRAY_SIZE(platform_component);
links[*id].init = sof_hdmi_init;
links[*id].dpcm_playback = 1;
links[*id].no_pcm = 1;

(*id)++;
}

return 0;

devm_err:
return -ENOMEM;
}

static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev,
int ssp_codec,
int ssp_amp,
int dmic_be_num,
int hdmi_num)
{
struct snd_soc_dai_link_component *cpus;
struct snd_soc_dai_link *links;
int ret, id = 0, link_seq;

links = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link) *
sof_audio_card_cs42l42.num_links, GFP_KERNEL);
cpus = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link_component) *
sof_audio_card_cs42l42.num_links, GFP_KERNEL);
if (!links || !cpus)
goto devm_err;

link_seq = (sof_cs42l42_quirk & SOF_CS42L42_DAILINK_MASK) >> SOF_CS42L42_DAILINK_SHIFT;

while (link_seq) {
int link_type = link_seq & 0x07;

switch (link_type) {
case LINK_HP:
ret = create_hp_codec_dai_links(dev, links, cpus, &id, ssp_codec);
if (ret < 0) {
dev_err(dev, "fail to create hp codec dai links, ret %d\n",
ret);
goto devm_err;
}
break;
case LINK_SPK:
ret = create_spk_amp_dai_links(dev, links, cpus, &id, ssp_amp);
if (ret < 0) {
dev_err(dev, "fail to create spk amp dai links, ret %d\n",
ret);
goto devm_err;
}
break;
case LINK_DMIC:
ret = create_dmic_dai_links(dev, links, cpus, &id, dmic_be_num);
if (ret < 0) {
dev_err(dev, "fail to create dmic dai links, ret %d\n",
ret);
goto devm_err;
}
break;
case LINK_HDMI:
ret = create_hdmi_dai_links(dev, links, cpus, &id, hdmi_num);
if (ret < 0) {
dev_err(dev, "fail to create hdmi dai links, ret %d\n",
ret);
goto devm_err;
}
break;
case LINK_NONE:
/* caught here if it's not used as terminator in macro */
default:
dev_err(dev, "invalid link type %d\n", link_type);
goto devm_err;
}

link_seq >>= 3;
}

return links;
Expand Down Expand Up @@ -490,7 +607,8 @@ static const struct platform_device_id board_ids[] = {
.driver_data = (kernel_ulong_t)(SOF_CS42L42_SSP_CODEC(2) |
SOF_SPEAKER_AMP_PRESENT |
SOF_MAX98357A_SPEAKER_AMP_PRESENT |
SOF_CS42L42_SSP_AMP(1)),
SOF_CS42L42_SSP_AMP(1)) |
SOF_CS42L42_DAILINK(LINK_SPK, LINK_HP, LINK_DMIC, LINK_HDMI, LINK_NONE),
},
{ }
};
Expand Down

0 comments on commit 3cb97cf

Please sign in to comment.