Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.15.
  1// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
  2// Copyright(c) 2023 Intel Corporation
  3
  4/*
  5 * Soundwire Intel ops for LunarLake
  6 */
  7
  8#include <linux/acpi.h>
  9#include <linux/cleanup.h>
 10#include <linux/device.h>
 11#include <linux/soundwire/sdw_registers.h>
 12#include <linux/soundwire/sdw.h>
 13#include <linux/soundwire/sdw_intel.h>
 14#include <sound/hdaudio.h>
 15#include <sound/hda-mlink.h>
 16#include <sound/hda_register.h>
 17#include <sound/pcm_params.h>
 18#include "cadence_master.h"
 19#include "bus.h"
 20#include "intel.h"
 21
 22/*
 23 * shim vendor-specific (vs) ops
 24 */
 25
 26static void intel_shim_vs_init(struct sdw_intel *sdw)
 27{
 28	void __iomem *shim_vs = sdw->link_res->shim_vs;
 29	struct sdw_bus *bus = &sdw->cdns.bus;
 30	struct sdw_intel_prop *intel_prop;
 31	u16 clde;
 32	u16 doaise2;
 33	u16 dodse2;
 34	u16 clds;
 35	u16 clss;
 36	u16 doaise;
 37	u16 doais;
 38	u16 dodse;
 39	u16 dods;
 40	u16 act;
 41
 42	intel_prop = bus->vendor_specific_prop;
 43	clde = intel_prop->clde;
 44	doaise2 = intel_prop->doaise2;
 45	dodse2 = intel_prop->dodse2;
 46	clds = intel_prop->clds;
 47	clss = intel_prop->clss;
 48	doaise = intel_prop->doaise;
 49	doais = intel_prop->doais;
 50	dodse = intel_prop->dodse;
 51	dods = intel_prop->dods;
 52
 53	act = intel_readw(shim_vs, SDW_SHIM2_INTEL_VS_ACTMCTL);
 54	u16p_replace_bits(&act, clde, SDW_SHIM3_INTEL_VS_ACTMCTL_CLDE);
 55	u16p_replace_bits(&act, doaise2, SDW_SHIM3_INTEL_VS_ACTMCTL_DOAISE2);
 56	u16p_replace_bits(&act, dodse2, SDW_SHIM3_INTEL_VS_ACTMCTL_DODSE2);
 57	u16p_replace_bits(&act, clds, SDW_SHIM3_INTEL_VS_ACTMCTL_CLDS);
 58	u16p_replace_bits(&act, clss, SDW_SHIM3_INTEL_VS_ACTMCTL_CLSS);
 59	u16p_replace_bits(&act, doaise, SDW_SHIM2_INTEL_VS_ACTMCTL_DOAISE);
 60	u16p_replace_bits(&act, doais, SDW_SHIM2_INTEL_VS_ACTMCTL_DOAIS);
 61	u16p_replace_bits(&act, dodse, SDW_SHIM2_INTEL_VS_ACTMCTL_DODSE);
 62	u16p_replace_bits(&act, dods, SDW_SHIM2_INTEL_VS_ACTMCTL_DODS);
 63	act |= SDW_SHIM2_INTEL_VS_ACTMCTL_DACTQE;
 64	intel_writew(shim_vs, SDW_SHIM2_INTEL_VS_ACTMCTL, act);
 65	usleep_range(10, 15);
 66}
 67
 68static void intel_shim_vs_set_clock_source(struct sdw_intel *sdw, u32 source)
 69{
 70	void __iomem *shim_vs = sdw->link_res->shim_vs;
 71	u32 val;
 72
 73	val = intel_readl(shim_vs, SDW_SHIM2_INTEL_VS_LVSCTL);
 74
 75	u32p_replace_bits(&val, source, SDW_SHIM2_INTEL_VS_LVSCTL_MLCS);
 76
 77	intel_writel(shim_vs, SDW_SHIM2_INTEL_VS_LVSCTL, val);
 78
 79	dev_dbg(sdw->cdns.dev, "clock source %d LVSCTL %#x\n", source, val);
 80}
 81
 82static int intel_shim_check_wake(struct sdw_intel *sdw)
 83{
 84	/*
 85	 * We follow the HDaudio example and resume unconditionally
 86	 * without checking the WAKESTS bit for that specific link
 87	 */
 88
 89	return 1;
 90}
 91
 92static void intel_shim_wake(struct sdw_intel *sdw, bool wake_enable)
 93{
 94	u16 lsdiid = 0;
 95	u16 wake_en;
 96	u16 wake_sts;
 97	int ret;
 98
 99	mutex_lock(sdw->link_res->shim_lock);
100
101	ret = hdac_bus_eml_sdw_get_lsdiid_unlocked(sdw->link_res->hbus, sdw->instance, &lsdiid);
102	if (ret < 0)
103		goto unlock;
104
105	wake_en = snd_hdac_chip_readw(sdw->link_res->hbus, WAKEEN);
106
107	if (wake_enable) {
108		/* Enable the wakeup */
109		wake_en |= lsdiid;
110
111		snd_hdac_chip_writew(sdw->link_res->hbus, WAKEEN, wake_en);
112	} else {
113		/* Disable the wake up interrupt */
114		wake_en &= ~lsdiid;
115		snd_hdac_chip_writew(sdw->link_res->hbus, WAKEEN, wake_en);
116
117		/* Clear wake status (W1C) */
118		wake_sts = snd_hdac_chip_readw(sdw->link_res->hbus, STATESTS);
119		wake_sts |= lsdiid;
120		snd_hdac_chip_writew(sdw->link_res->hbus, STATESTS, wake_sts);
121	}
122unlock:
123	mutex_unlock(sdw->link_res->shim_lock);
124}
125
126static int intel_link_power_up(struct sdw_intel *sdw)
127{
128	struct sdw_bus *bus = &sdw->cdns.bus;
129	struct sdw_master_prop *prop = &bus->prop;
130	u32 *shim_mask = sdw->link_res->shim_mask;
131	unsigned int link_id = sdw->instance;
132	u32 clock_source;
133	u32 syncprd;
134	int ret;
135
136	if (prop->mclk_freq % 6000000) {
137		if (prop->mclk_freq % 2400000) {
138			syncprd = SDW_SHIM_SYNC_SYNCPRD_VAL_24_576;
139			clock_source = SDW_SHIM2_MLCS_CARDINAL_CLK;
140		} else {
141			syncprd = SDW_SHIM_SYNC_SYNCPRD_VAL_38_4;
142			clock_source = SDW_SHIM2_MLCS_XTAL_CLK;
143		}
144	} else {
145		syncprd = SDW_SHIM_SYNC_SYNCPRD_VAL_96;
146		clock_source = SDW_SHIM2_MLCS_AUDIO_PLL_CLK;
147	}
148
149	mutex_lock(sdw->link_res->shim_lock);
150
151	ret = hdac_bus_eml_sdw_power_up_unlocked(sdw->link_res->hbus, link_id);
152	if (ret < 0) {
153		dev_err(sdw->cdns.dev, "%s: hdac_bus_eml_sdw_power_up failed: %d\n",
154			__func__, ret);
155		goto out;
156	}
157
158	intel_shim_vs_set_clock_source(sdw, clock_source);
159
160	if (!*shim_mask) {
161		/* we first need to program the SyncPRD/CPU registers */
162		dev_dbg(sdw->cdns.dev, "first link up, programming SYNCPRD\n");
163
164		ret =  hdac_bus_eml_sdw_set_syncprd_unlocked(sdw->link_res->hbus, syncprd);
165		if (ret < 0) {
166			dev_err(sdw->cdns.dev, "%s: hdac_bus_eml_sdw_set_syncprd failed: %d\n",
167				__func__, ret);
168			goto out;
169		}
170
171		/* SYNCPU will change once link is active */
172		ret =  hdac_bus_eml_sdw_wait_syncpu_unlocked(sdw->link_res->hbus);
173		if (ret < 0) {
174			dev_err(sdw->cdns.dev, "%s: hdac_bus_eml_sdw_wait_syncpu failed: %d\n",
175				__func__, ret);
176			goto out;
177		}
178
179		hdac_bus_eml_enable_interrupt_unlocked(sdw->link_res->hbus, true,
180						       AZX_REG_ML_LEPTR_ID_SDW, true);
181	}
182
183	*shim_mask |= BIT(link_id);
184
185	sdw->cdns.link_up = true;
186
187	intel_shim_vs_init(sdw);
188
189out:
190	mutex_unlock(sdw->link_res->shim_lock);
191
192	return ret;
193}
194
195static int intel_link_power_down(struct sdw_intel *sdw)
196{
197	u32 *shim_mask = sdw->link_res->shim_mask;
198	unsigned int link_id = sdw->instance;
199	int ret;
200
201	mutex_lock(sdw->link_res->shim_lock);
202
203	sdw->cdns.link_up = false;
204
205	*shim_mask &= ~BIT(link_id);
206
207	if (!*shim_mask)
208		hdac_bus_eml_enable_interrupt_unlocked(sdw->link_res->hbus, true,
209						       AZX_REG_ML_LEPTR_ID_SDW, false);
210
211	ret = hdac_bus_eml_sdw_power_down_unlocked(sdw->link_res->hbus, link_id);
212	if (ret < 0) {
213		dev_err(sdw->cdns.dev, "%s: hdac_bus_eml_sdw_power_down failed: %d\n",
214			__func__, ret);
215
216		/*
217		 * we leave the sdw->cdns.link_up flag as false since we've disabled
218		 * the link at this point and cannot handle interrupts any longer.
219		 */
220	}
221
222	mutex_unlock(sdw->link_res->shim_lock);
223
224	return ret;
225}
226
227static void intel_sync_arm(struct sdw_intel *sdw)
228{
229	unsigned int link_id = sdw->instance;
230
231	mutex_lock(sdw->link_res->shim_lock);
232
233	hdac_bus_eml_sdw_sync_arm_unlocked(sdw->link_res->hbus, link_id);
234
235	mutex_unlock(sdw->link_res->shim_lock);
236}
237
238static int intel_sync_go_unlocked(struct sdw_intel *sdw)
239{
240	int ret;
241
242	ret = hdac_bus_eml_sdw_sync_go_unlocked(sdw->link_res->hbus);
243	if (ret < 0)
244		dev_err(sdw->cdns.dev, "%s: SyncGO clear failed: %d\n", __func__, ret);
245
246	return ret;
247}
248
249static int intel_sync_go(struct sdw_intel *sdw)
250{
251	int ret;
252
253	mutex_lock(sdw->link_res->shim_lock);
254
255	ret = intel_sync_go_unlocked(sdw);
256
257	mutex_unlock(sdw->link_res->shim_lock);
258
259	return ret;
260}
261
262static bool intel_check_cmdsync_unlocked(struct sdw_intel *sdw)
263{
264	return hdac_bus_eml_sdw_check_cmdsync_unlocked(sdw->link_res->hbus);
265}
266
267/* DAI callbacks */
268static int intel_params_stream(struct sdw_intel *sdw,
269			       struct snd_pcm_substream *substream,
270			       struct snd_soc_dai *dai,
271			       struct snd_pcm_hw_params *hw_params,
272			       int link_id, int alh_stream_id)
273{
274	struct sdw_intel_link_res *res = sdw->link_res;
275	struct sdw_intel_stream_params_data params_data;
276
277	params_data.substream = substream;
278	params_data.dai = dai;
279	params_data.hw_params = hw_params;
280	params_data.link_id = link_id;
281	params_data.alh_stream_id = alh_stream_id;
282
283	if (res->ops && res->ops->params_stream && res->dev)
284		return res->ops->params_stream(res->dev,
285					       &params_data);
286	return -EIO;
287}
288
289static int intel_free_stream(struct sdw_intel *sdw,
290			     struct snd_pcm_substream *substream,
291			     struct snd_soc_dai *dai,
292			     int link_id)
293
294{
295	struct sdw_intel_link_res *res = sdw->link_res;
296	struct sdw_intel_stream_free_data free_data;
297
298	free_data.substream = substream;
299	free_data.dai = dai;
300	free_data.link_id = link_id;
301
302	if (res->ops && res->ops->free_stream && res->dev)
303		return res->ops->free_stream(res->dev,
304					     &free_data);
305
306	return 0;
307}
308
309/*
310 * DAI operations
311 */
312static int intel_hw_params(struct snd_pcm_substream *substream,
313			   struct snd_pcm_hw_params *params,
314			   struct snd_soc_dai *dai)
315{
316	struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai);
317	struct sdw_intel *sdw = cdns_to_intel(cdns);
318	struct sdw_cdns_dai_runtime *dai_runtime;
319	struct sdw_cdns_pdi *pdi;
320	struct sdw_stream_config sconfig;
321	int ch, dir;
322	int ret;
323
324	dai_runtime = cdns->dai_runtime_array[dai->id];
325	if (!dai_runtime)
326		return -EIO;
327
328	ch = params_channels(params);
329	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
330		dir = SDW_DATA_DIR_RX;
331	else
332		dir = SDW_DATA_DIR_TX;
333
334	pdi = sdw_cdns_alloc_pdi(cdns, &cdns->pcm, ch, dir, dai->id);
335	if (!pdi)
336		return -EINVAL;
337
338	/* use same definitions for alh_id as previous generations */
339	pdi->intel_alh_id = (sdw->instance * 16) + pdi->num + 3;
340	if (pdi->num >= 2)
341		pdi->intel_alh_id += 2;
342
343	/* the SHIM will be configured in the callback functions */
344
345	sdw_cdns_config_stream(cdns, ch, dir, pdi);
346
347	/* store pdi and state, may be needed in prepare step */
348	dai_runtime->paused = false;
349	dai_runtime->suspended = false;
350	dai_runtime->pdi = pdi;
351
352	/* Inform DSP about PDI stream number */
353	ret = intel_params_stream(sdw, substream, dai, params,
354				  sdw->instance,
355				  pdi->intel_alh_id);
356	if (ret)
357		return ret;
358
359	sconfig.direction = dir;
360	sconfig.ch_count = ch;
361	sconfig.frame_rate = params_rate(params);
362	sconfig.type = dai_runtime->stream_type;
363
364	sconfig.bps = snd_pcm_format_width(params_format(params));
365
366	/* Port configuration */
367	struct sdw_port_config *pconfig __free(kfree) = kzalloc(sizeof(*pconfig),
368								GFP_KERNEL);
369	if (!pconfig)
370		return -ENOMEM;
371
372	pconfig->num = pdi->num;
373	pconfig->ch_mask = (1 << ch) - 1;
374
375	ret = sdw_stream_add_master(&cdns->bus, &sconfig,
376				    pconfig, 1, dai_runtime->stream);
377	if (ret)
378		dev_err(cdns->dev, "add master to stream failed:%d\n", ret);
379
380	return ret;
381}
382
383static int intel_prepare(struct snd_pcm_substream *substream,
384			 struct snd_soc_dai *dai)
385{
386	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
387	struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai);
388	struct sdw_intel *sdw = cdns_to_intel(cdns);
389	struct sdw_cdns_dai_runtime *dai_runtime;
390	struct snd_pcm_hw_params *hw_params;
391	int ch, dir;
392
393	dai_runtime = cdns->dai_runtime_array[dai->id];
394	if (!dai_runtime) {
395		dev_err(dai->dev, "failed to get dai runtime in %s\n",
396			__func__);
397		return -EIO;
398	}
399
400	hw_params = &rtd->dpcm[substream->stream].hw_params;
401	if (dai_runtime->suspended) {
402		dai_runtime->suspended = false;
403
404		/*
405		 * .prepare() is called after system resume, where we
406		 * need to reinitialize the SHIM/ALH/Cadence IP.
407		 * .prepare() is also called to deal with underflows,
408		 * but in those cases we cannot touch ALH/SHIM
409		 * registers
410		 */
411
412		/* configure stream */
413		ch = params_channels(hw_params);
414		if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
415			dir = SDW_DATA_DIR_RX;
416		else
417			dir = SDW_DATA_DIR_TX;
418
419		/* the SHIM will be configured in the callback functions */
420
421		sdw_cdns_config_stream(cdns, ch, dir, dai_runtime->pdi);
422	}
423
424	/* Inform DSP about PDI stream number */
425	return intel_params_stream(sdw, substream, dai, hw_params, sdw->instance,
426				   dai_runtime->pdi->intel_alh_id);
427}
428
429static int
430intel_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
431{
432	struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai);
433	struct sdw_intel *sdw = cdns_to_intel(cdns);
434	struct sdw_cdns_dai_runtime *dai_runtime;
435	int ret;
436
437	dai_runtime = cdns->dai_runtime_array[dai->id];
438	if (!dai_runtime)
439		return -EIO;
440
441	/*
442	 * The sdw stream state will transition to RELEASED when stream->
443	 * master_list is empty. So the stream state will transition to
444	 * DEPREPARED for the first cpu-dai and to RELEASED for the last
445	 * cpu-dai.
446	 */
447	ret = sdw_stream_remove_master(&cdns->bus, dai_runtime->stream);
448	if (ret < 0) {
449		dev_err(dai->dev, "remove master from stream %s failed: %d\n",
450			dai_runtime->stream->name, ret);
451		return ret;
452	}
453
454	ret = intel_free_stream(sdw, substream, dai, sdw->instance);
455	if (ret < 0) {
456		dev_err(dai->dev, "intel_free_stream: failed %d\n", ret);
457		return ret;
458	}
459
460	dai_runtime->pdi = NULL;
461
462	return 0;
463}
464
465static int intel_pcm_set_sdw_stream(struct snd_soc_dai *dai,
466				    void *stream, int direction)
467{
468	return cdns_set_sdw_stream(dai, stream, direction);
469}
470
471static void *intel_get_sdw_stream(struct snd_soc_dai *dai,
472				  int direction)
473{
474	struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai);
475	struct sdw_cdns_dai_runtime *dai_runtime;
476
477	dai_runtime = cdns->dai_runtime_array[dai->id];
478	if (!dai_runtime)
479		return ERR_PTR(-EINVAL);
480
481	return dai_runtime->stream;
482}
483
484static int intel_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai)
485{
486	struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai);
487	struct sdw_intel *sdw = cdns_to_intel(cdns);
488	struct sdw_intel_link_res *res = sdw->link_res;
489	struct sdw_cdns_dai_runtime *dai_runtime;
490	int ret = 0;
491
492	/*
493	 * The .trigger callback is used to program HDaudio DMA and send required IPC to audio
494	 * firmware.
495	 */
496	if (res->ops && res->ops->trigger) {
497		ret = res->ops->trigger(substream, cmd, dai);
498		if (ret < 0)
499			return ret;
500	}
501
502	dai_runtime = cdns->dai_runtime_array[dai->id];
503	if (!dai_runtime) {
504		dev_err(dai->dev, "failed to get dai runtime in %s\n",
505			__func__);
506		return -EIO;
507	}
508
509	switch (cmd) {
510	case SNDRV_PCM_TRIGGER_SUSPEND:
511
512		/*
513		 * The .prepare callback is used to deal with xruns and resume operations.
514		 * In the case of xruns, the DMAs and SHIM registers cannot be touched,
515		 * but for resume operations the DMAs and SHIM registers need to be initialized.
516		 * the .trigger callback is used to track the suspend case only.
517		 */
518
519		dai_runtime->suspended = true;
520
521		break;
522
523	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
524		dai_runtime->paused = true;
525		break;
526	case SNDRV_PCM_TRIGGER_STOP:
527	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
528		dai_runtime->paused = false;
529		break;
530	default:
531		break;
532	}
533
534	return ret;
535}
536
537static const struct snd_soc_dai_ops intel_pcm_dai_ops = {
538	.hw_params = intel_hw_params,
539	.prepare = intel_prepare,
540	.hw_free = intel_hw_free,
541	.trigger = intel_trigger,
542	.set_stream = intel_pcm_set_sdw_stream,
543	.get_stream = intel_get_sdw_stream,
544};
545
546static const struct snd_soc_component_driver dai_component = {
547	.name			= "soundwire",
548};
549
550/*
551 * PDI routines
552 */
553static void intel_pdi_init(struct sdw_intel *sdw,
554			   struct sdw_cdns_stream_config *config)
555{
556	void __iomem *shim = sdw->link_res->shim;
557	int pcm_cap;
558
559	/* PCM Stream Capability */
560	pcm_cap = intel_readw(shim, SDW_SHIM2_PCMSCAP);
561
562	config->pcm_bd = FIELD_GET(SDW_SHIM2_PCMSCAP_BSS, pcm_cap);
563	config->pcm_in = FIELD_GET(SDW_SHIM2_PCMSCAP_ISS, pcm_cap);
564	config->pcm_out = FIELD_GET(SDW_SHIM2_PCMSCAP_ISS, pcm_cap);
565
566	dev_dbg(sdw->cdns.dev, "PCM cap bd:%d in:%d out:%d\n",
567		config->pcm_bd, config->pcm_in, config->pcm_out);
568}
569
570static int
571intel_pdi_get_ch_cap(struct sdw_intel *sdw, unsigned int pdi_num)
572{
573	void __iomem *shim = sdw->link_res->shim;
574
575	/* zero based values for channel count in register */
576	return intel_readw(shim, SDW_SHIM2_PCMSYCHC(pdi_num)) + 1;
577}
578
579static void intel_pdi_get_ch_update(struct sdw_intel *sdw,
580				    struct sdw_cdns_pdi *pdi,
581				    unsigned int num_pdi,
582				    unsigned int *num_ch)
583{
584	int ch_count = 0;
585	int i;
586
587	for (i = 0; i < num_pdi; i++) {
588		pdi->ch_count = intel_pdi_get_ch_cap(sdw, pdi->num);
589		ch_count += pdi->ch_count;
590		pdi++;
591	}
592
593	*num_ch = ch_count;
594}
595
596static void intel_pdi_stream_ch_update(struct sdw_intel *sdw,
597				       struct sdw_cdns_streams *stream)
598{
599	intel_pdi_get_ch_update(sdw, stream->bd, stream->num_bd,
600				&stream->num_ch_bd);
601
602	intel_pdi_get_ch_update(sdw, stream->in, stream->num_in,
603				&stream->num_ch_in);
604
605	intel_pdi_get_ch_update(sdw, stream->out, stream->num_out,
606				&stream->num_ch_out);
607}
608
609static int intel_create_dai(struct sdw_cdns *cdns,
610			    struct snd_soc_dai_driver *dais,
611			    enum intel_pdi_type type,
612			    u32 num, u32 off, u32 max_ch)
613{
614	int i;
615
616	if (!num)
617		return 0;
618
619	for (i = off; i < (off + num); i++) {
620		dais[i].name = devm_kasprintf(cdns->dev, GFP_KERNEL,
621					      "SDW%d Pin%d",
622					      cdns->instance, i);
623		if (!dais[i].name)
624			return -ENOMEM;
625
626		if (type == INTEL_PDI_BD || type == INTEL_PDI_OUT) {
627			dais[i].playback.channels_min = 1;
628			dais[i].playback.channels_max = max_ch;
629		}
630
631		if (type == INTEL_PDI_BD || type == INTEL_PDI_IN) {
632			dais[i].capture.channels_min = 1;
633			dais[i].capture.channels_max = max_ch;
634		}
635
636		dais[i].ops = &intel_pcm_dai_ops;
637	}
638
639	return 0;
640}
641
642static int intel_register_dai(struct sdw_intel *sdw)
643{
644	struct sdw_cdns_dai_runtime **dai_runtime_array;
645	struct sdw_cdns_stream_config config;
646	struct sdw_cdns *cdns = &sdw->cdns;
647	struct sdw_cdns_streams *stream;
648	struct snd_soc_dai_driver *dais;
649	int num_dai;
650	int ret;
651	int off = 0;
652
653	/* Read the PDI config and initialize cadence PDI */
654	intel_pdi_init(sdw, &config);
655	ret = sdw_cdns_pdi_init(cdns, config);
656	if (ret)
657		return ret;
658
659	intel_pdi_stream_ch_update(sdw, &sdw->cdns.pcm);
660
661	/* DAIs are created based on total number of PDIs supported */
662	num_dai = cdns->pcm.num_pdi;
663
664	dai_runtime_array = devm_kcalloc(cdns->dev, num_dai,
665					 sizeof(struct sdw_cdns_dai_runtime *),
666					 GFP_KERNEL);
667	if (!dai_runtime_array)
668		return -ENOMEM;
669	cdns->dai_runtime_array = dai_runtime_array;
670
671	dais = devm_kcalloc(cdns->dev, num_dai, sizeof(*dais), GFP_KERNEL);
672	if (!dais)
673		return -ENOMEM;
674
675	/* Create PCM DAIs */
676	stream = &cdns->pcm;
677
678	ret = intel_create_dai(cdns, dais, INTEL_PDI_IN, cdns->pcm.num_in,
679			       off, stream->num_ch_in);
680	if (ret)
681		return ret;
682
683	off += cdns->pcm.num_in;
684	ret = intel_create_dai(cdns, dais, INTEL_PDI_OUT, cdns->pcm.num_out,
685			       off, stream->num_ch_out);
686	if (ret)
687		return ret;
688
689	off += cdns->pcm.num_out;
690	ret = intel_create_dai(cdns, dais, INTEL_PDI_BD, cdns->pcm.num_bd,
691			       off, stream->num_ch_bd);
692	if (ret)
693		return ret;
694
695	return devm_snd_soc_register_component(cdns->dev, &dai_component,
696					       dais, num_dai);
697}
698
699static void intel_program_sdi(struct sdw_intel *sdw, int dev_num)
700{
701	int ret;
702
703	ret = hdac_bus_eml_sdw_set_lsdiid(sdw->link_res->hbus, sdw->instance, dev_num);
704	if (ret < 0)
705		dev_err(sdw->cdns.dev, "%s: could not set lsdiid for link %d %d\n",
706			__func__, sdw->instance, dev_num);
707}
708
709static int intel_get_link_count(struct sdw_intel *sdw)
710{
711	int ret;
712
713	ret = hdac_bus_eml_get_count(sdw->link_res->hbus, true, AZX_REG_ML_LEPTR_ID_SDW);
714	if (!ret) {
715		dev_err(sdw->cdns.dev, "%s: could not retrieve link count\n", __func__);
716		return -ENODEV;
717	}
718
719	if (ret > SDW_INTEL_MAX_LINKS) {
720		dev_err(sdw->cdns.dev, "%s: link count %d exceed max %d\n", __func__, ret, SDW_INTEL_MAX_LINKS);
721		return -EINVAL;
722	}
723
724	return ret;
725}
726
727const struct sdw_intel_hw_ops sdw_intel_lnl_hw_ops = {
728	.debugfs_init = intel_ace2x_debugfs_init,
729	.debugfs_exit = intel_ace2x_debugfs_exit,
730
731	.get_link_count = intel_get_link_count,
732
733	.register_dai = intel_register_dai,
734
735	.check_clock_stop = intel_check_clock_stop,
736	.start_bus = intel_start_bus,
737	.start_bus_after_reset = intel_start_bus_after_reset,
738	.start_bus_after_clock_stop = intel_start_bus_after_clock_stop,
739	.stop_bus = intel_stop_bus,
740
741	.link_power_up = intel_link_power_up,
742	.link_power_down = intel_link_power_down,
743
744	.shim_check_wake = intel_shim_check_wake,
745	.shim_wake = intel_shim_wake,
746
747	.pre_bank_switch = intel_pre_bank_switch,
748	.post_bank_switch = intel_post_bank_switch,
749
750	.sync_arm = intel_sync_arm,
751	.sync_go_unlocked = intel_sync_go_unlocked,
752	.sync_go = intel_sync_go,
753	.sync_check_cmdsync_unlocked = intel_check_cmdsync_unlocked,
754
755	.program_sdi = intel_program_sdi,
756};
757EXPORT_SYMBOL_NS(sdw_intel_lnl_hw_ops, "SOUNDWIRE_INTEL");
758
759MODULE_IMPORT_NS("SND_SOC_SOF_HDA_MLINK");