diff mbox series

[1/4] ASoC: Intel: sof_cs42l42: support JSL DAI link sequence

Message ID 20210606004102.26190-2-brent.lu@intel.com (mailing list archive)
State New, archived
Headers show
Series Support CS42L42 on JSL platform | expand

Commit Message

Brent Lu June 6, 2021, 12:40 a.m. UTC
The backend DAI link sequence of GLK platform is different from the
sequence of other platforms. We refactor the sof_card_dai_links_create()
function to support both style.

GLK: SPK - HP - DMIC - HDMI
Other: HP - DMIC - HDMI - SPK

Signed-off-by: Brent Lu <brent.lu@intel.com>
---
 sound/soc/intel/boards/sof_cs42l42.c | 318 ++++++++++++++++++---------
 1 file changed, 208 insertions(+), 110 deletions(-)

Comments

Pierre-Louis Bossart June 7, 2021, 1:32 p.m. UTC | #1
On 6/5/21 7:40 PM, Brent Lu wrote:
> The backend DAI link sequence of GLK platform is different from the
> sequence of other platforms. We refactor the sof_card_dai_links_create()
> function to support both style.
> 
> GLK: SPK - HP - DMIC - HDMI
> Other: HP - DMIC - HDMI - SPK

I am really confused here.
The dailink sequence is whatever we want it to be. What matters is that 
the dailink ID matches what is in the topology.

Is this saying that the GLK and JSL topologies did not follow any sort 
of convention? Can you elaborate more on what is the issue?

Put differently, why can't we fix the topology instead with a reorder of 
the dailinks?

> 
> Signed-off-by: Brent Lu <brent.lu@intel.com>
> ---
>   sound/soc/intel/boards/sof_cs42l42.c | 318 ++++++++++++++++++---------
>   1 file changed, 208 insertions(+), 110 deletions(-)
> 
> diff --git a/sound/soc/intel/boards/sof_cs42l42.c b/sound/soc/intel/boards/sof_cs42l42.c
> index 8919d3ba3c89..e3171242f612 100644
> --- a/sound/soc/intel/boards/sof_cs42l42.c
> +++ b/sound/soc/intel/boards/sof_cs42l42.c
> @@ -259,133 +259,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";
> @@ -396,14 +429,79 @@ 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;
> +
> +	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;
> +
> +	if (soc_intel_is_glk()) {
> +		/* gemini lake starts from spk link */
> +		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;
> +		}
> +	}
> +
> +	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;
> +	}
> +
> +	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;
> +	}
> +
> +	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;
> +	}
> +
> +	if (!soc_intel_is_glk()) {
> +		/* other platforms end with spk link */
> +		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;
> +		}
>   	}
>   
>   	return links;
>
Brent Lu June 7, 2021, 2:23 p.m. UTC | #2
> On 6/5/21 7:40 PM, Brent Lu wrote:
> > The backend DAI link sequence of GLK platform is different from the
> > sequence of other platforms. We refactor the
> > sof_card_dai_links_create() function to support both style.
> >
> > GLK: SPK - HP - DMIC - HDMI
> > Other: HP - DMIC - HDMI - SPK
> 
> I am really confused here.
> The dailink sequence is whatever we want it to be. What matters is that the
> dailink ID matches what is in the topology.
> 
> Is this saying that the GLK and JSL topologies did not follow any sort of
> convention? Can you elaborate more on what is the issue?
> 
> Put differently, why can't we fix the topology instead with a reorder of the
> dailinks?
> 

snd_soc_find_dai_link() checked both dai link name and id when matching
topology and machine driver. Soundcard registration would fail if their id
doesn't match.

Cs42l42 is sharing topology with DA7219's topology source file sof-glk-da7219.m4
on GLK platform. The configuration is:

dai link id 0 is for spk
#SSP 1 (ID: 0) with 19.2 MHz mclk with MCLK_ID 1 (unused), 1.536 MHz blck
DAI_CONFIG(SSP, 1, 0, SSP1-Codec,

dai link id 1 is for headphone
#SSP 2 (ID: 1) with 19.2 MHz mclk with MCLK_ID 1, 1.92 MHz bclk
DAI_CONFIG(SSP, 2, 1, SSP2-Codec,

dai link id 2 is for dmic
DAI_CONFIG(DMIC, 0, 2, dmic01,

dai link id 3/4/5 is for hdmi
DAI_CONFIG(HDA, 3, 3, iDisp1,
DAI_CONFIG(HDA, 4, 4, iDisp2,
DAI_CONFIG(HDA, 5, 5, iDisp3,

When on JSL, we plan to share topology with rt5682 which has different dai link
sequence:
sof-jsl-rt5682.m4:

dai link id 0 is for headphone
DAI_CONFIG(SSP, 0, 0, SSP0-Codec,

dai link id 6 is for spk
# SSP 1 (ID: 6)
DAI_CONFIG(SSP, SPK_INDEX, 6, SPK_NAME,
	SET_SSP_CONFIG)

dai link id 3/4/5 is for hdmi
# 4 HDMI/DP outputs (ID: 3,4,5)
DAI_CONFIG(HDA, 0, 3, iDisp1,
DAI_CONFIG(HDA, 1, 4, iDisp2,
DAI_CONFIG(HDA, 2, 5, iDisp3,

I'm not sure if there is convention about the sequence to follow?


Regards,
Brent
Pierre-Louis Bossart June 7, 2021, 8:49 p.m. UTC | #3
> Cs42l42 is sharing topology with DA7219's topology source file sof-glk-da7219.m4
> on GLK platform. The configuration is:
> 
> dai link id 0 is for spk
> #SSP 1 (ID: 0) with 19.2 MHz mclk with MCLK_ID 1 (unused), 1.536 MHz blck
> DAI_CONFIG(SSP, 1, 0, SSP1-Codec,
> 
> dai link id 1 is for headphone
> #SSP 2 (ID: 1) with 19.2 MHz mclk with MCLK_ID 1, 1.92 MHz bclk
> DAI_CONFIG(SSP, 2, 1, SSP2-Codec,
> 
> dai link id 2 is for dmic
> DAI_CONFIG(DMIC, 0, 2, dmic01,
> 
> dai link id 3/4/5 is for hdmi
> DAI_CONFIG(HDA, 3, 3, iDisp1,
> DAI_CONFIG(HDA, 4, 4, iDisp2,
> DAI_CONFIG(HDA, 5, 5, iDisp3,
> 
> When on JSL, we plan to share topology with rt5682 which has different dai link
> sequence:
> sof-jsl-rt5682.m4:
> 
> dai link id 0 is for headphone
> DAI_CONFIG(SSP, 0, 0, SSP0-Codec,
> 
> dai link id 6 is for spk
> # SSP 1 (ID: 6)
> DAI_CONFIG(SSP, SPK_INDEX, 6, SPK_NAME,
> 	SET_SSP_CONFIG)
> 
> dai link id 3/4/5 is for hdmi
> # 4 HDMI/DP outputs (ID: 3,4,5)
> DAI_CONFIG(HDA, 0, 3, iDisp1,
> DAI_CONFIG(HDA, 1, 4, iDisp2,
> DAI_CONFIG(HDA, 2, 5, iDisp3,
> 
> I'm not sure if there is convention about the sequence to follow?

ok, now I get what you are trying to do.

Unfortunately there are no conventions so far, and since we have to be 
backwards-compatible with topology files already released we will need 
to deal with the different configurations in this machine driver, you're 
right about this.

The code you suggested is fine, but we can future-proof it a bit.

Instead of assuming any order depending on GLK or !GLK, we can add a BE 
'base' for headphone, amp, DMIC and DMIC each (represented as a constant 
structure) and point to different configurations depending on a quirk. 
That way we can deal with other permutations such as HP - SPK - HDMI - DMIC
diff mbox series

Patch

diff --git a/sound/soc/intel/boards/sof_cs42l42.c b/sound/soc/intel/boards/sof_cs42l42.c
index 8919d3ba3c89..e3171242f612 100644
--- a/sound/soc/intel/boards/sof_cs42l42.c
+++ b/sound/soc/intel/boards/sof_cs42l42.c
@@ -259,133 +259,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";
@@ -396,14 +429,79 @@  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;
+
+	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;
+
+	if (soc_intel_is_glk()) {
+		/* gemini lake starts from spk link */
+		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;
+		}
+	}
+
+	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;
+	}
+
+	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;
+	}
+
+	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;
+	}
+
+	if (!soc_intel_is_glk()) {
+		/* other platforms end with spk link */
+		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;
+		}
 	}
 
 	return links;