1// SPDX-License-Identifier: GPL-2.0-only
2// Copyright (c) 2020 Intel Corporation
3
4/*
5 *  sof_sdw - ASOC Machine driver for Intel SoundWire platforms
6 */
7
8#include <linux/device.h>
9#include <linux/dmi.h>
10#include <linux/module.h>
11#include <linux/soundwire/sdw.h>
12#include <linux/soundwire/sdw_type.h>
13#include <sound/soc.h>
14#include <sound/soc-acpi.h>
15#include "sof_sdw_common.h"
16#include "../../codecs/rt711.h"
17
18unsigned long sof_sdw_quirk = RT711_JD1;
19static int quirk_override = -1;
20module_param_named(quirk, quirk_override, int, 0444);
21MODULE_PARM_DESC(quirk, "Board-specific quirk override");
22
23#define INC_ID(BE, CPU, LINK)	do { (BE)++; (CPU)++; (LINK)++; } while (0)
24
25static void log_quirks(struct device *dev)
26{
27	if (SOF_RT711_JDSRC(sof_sdw_quirk))
28		dev_dbg(dev, "quirk realtek,jack-detect-source %ld\n",
29			SOF_RT711_JDSRC(sof_sdw_quirk));
30	if (sof_sdw_quirk & SOF_SDW_FOUR_SPK)
31		dev_dbg(dev, "quirk SOF_SDW_FOUR_SPK enabled\n");
32	if (sof_sdw_quirk & SOF_SDW_TGL_HDMI)
33		dev_dbg(dev, "quirk SOF_SDW_TGL_HDMI enabled\n");
34	if (sof_sdw_quirk & SOF_SDW_PCH_DMIC)
35		dev_dbg(dev, "quirk SOF_SDW_PCH_DMIC enabled\n");
36	if (SOF_SSP_GET_PORT(sof_sdw_quirk))
37		dev_dbg(dev, "SSP port %ld\n",
38			SOF_SSP_GET_PORT(sof_sdw_quirk));
39	if (sof_sdw_quirk & SOF_RT715_DAI_ID_FIX)
40		dev_dbg(dev, "quirk SOF_RT715_DAI_ID_FIX enabled\n");
41	if (sof_sdw_quirk & SOF_SDW_NO_AGGREGATION)
42		dev_dbg(dev, "quirk SOF_SDW_NO_AGGREGATION enabled\n");
43}
44
45static int sof_sdw_quirk_cb(const struct dmi_system_id *id)
46{
47	sof_sdw_quirk = (unsigned long)id->driver_data;
48	return 1;
49}
50
51static const struct dmi_system_id sof_sdw_quirk_table[] = {
52	/* CometLake devices */
53	{
54		.callback = sof_sdw_quirk_cb,
55		.matches = {
56			DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),
57			DMI_MATCH(DMI_PRODUCT_NAME, "CometLake Client"),
58		},
59		.driver_data = (void *)SOF_SDW_PCH_DMIC,
60	},
61	{
62		.callback = sof_sdw_quirk_cb,
63		.matches = {
64			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
65			DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "09C6")
66		},
67		.driver_data = (void *)(RT711_JD2 |
68					SOF_RT715_DAI_ID_FIX),
69	},
70	{
71		/* early version of SKU 09C6 */
72		.callback = sof_sdw_quirk_cb,
73		.matches = {
74			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
75			DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0983")
76		},
77		.driver_data = (void *)(RT711_JD2 |
78					SOF_RT715_DAI_ID_FIX),
79	},
80	{
81		.callback = sof_sdw_quirk_cb,
82		.matches = {
83			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
84			DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "098F"),
85		},
86		.driver_data = (void *)(RT711_JD2 |
87					SOF_RT715_DAI_ID_FIX |
88					SOF_SDW_FOUR_SPK),
89	},
90	{
91		.callback = sof_sdw_quirk_cb,
92		.matches = {
93			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
94			DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0990"),
95		},
96		.driver_data = (void *)(RT711_JD2 |
97					SOF_RT715_DAI_ID_FIX |
98					SOF_SDW_FOUR_SPK),
99	},
100	/* IceLake devices */
101	{
102		.callback = sof_sdw_quirk_cb,
103		.matches = {
104			DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),
105			DMI_MATCH(DMI_PRODUCT_NAME, "Ice Lake Client"),
106		},
107		.driver_data = (void *)SOF_SDW_PCH_DMIC,
108	},
109	/* TigerLake devices */
110	{
111		.callback = sof_sdw_quirk_cb,
112		.matches = {
113			DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),
114			DMI_MATCH(DMI_PRODUCT_NAME,
115				  "Tiger Lake Client Platform"),
116		},
117		.driver_data = (void *)(SOF_SDW_TGL_HDMI |
118					RT711_JD1 |
119					SOF_SDW_PCH_DMIC |
120					SOF_SSP_PORT(SOF_I2S_SSP2)),
121	},
122	{
123		.callback = sof_sdw_quirk_cb,
124		.matches = {
125			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
126			DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0A3E")
127		},
128		.driver_data = (void *)(SOF_SDW_TGL_HDMI |
129					RT711_JD2 |
130					SOF_RT715_DAI_ID_FIX),
131	},
132	{
133		.callback = sof_sdw_quirk_cb,
134		.matches = {
135			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
136			DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0A5E")
137		},
138		.driver_data = (void *)(SOF_SDW_TGL_HDMI |
139					RT711_JD2 |
140					SOF_RT715_DAI_ID_FIX |
141					SOF_SDW_FOUR_SPK),
142	},
143	{
144		.callback = sof_sdw_quirk_cb,
145		.matches = {
146			DMI_MATCH(DMI_SYS_VENDOR, "Google"),
147			DMI_MATCH(DMI_PRODUCT_NAME, "Volteer"),
148		},
149		.driver_data = (void *)(SOF_SDW_TGL_HDMI |
150					SOF_SDW_PCH_DMIC |
151					SOF_SDW_FOUR_SPK),
152	},
153	{
154		.callback = sof_sdw_quirk_cb,
155		.matches = {
156			DMI_MATCH(DMI_SYS_VENDOR, "Google"),
157			DMI_MATCH(DMI_PRODUCT_NAME, "Ripto"),
158		},
159		.driver_data = (void *)(SOF_SDW_TGL_HDMI |
160					SOF_SDW_PCH_DMIC |
161					SOF_SDW_FOUR_SPK),
162	},
163	{
164		/*
165		 * this entry covers multiple HP SKUs. The family name
166		 * does not seem robust enough, so we use a partial
167		 * match that ignores the product name suffix
168		 * (e.g. 15-eb1xxx, 14t-ea000 or 13-aw2xxx)
169		 */
170		.callback = sof_sdw_quirk_cb,
171		.matches = {
172			DMI_MATCH(DMI_SYS_VENDOR, "HP"),
173			DMI_MATCH(DMI_PRODUCT_NAME, "HP Spectre x360 Convertible"),
174		},
175		.driver_data = (void *)(SOF_SDW_TGL_HDMI |
176					SOF_SDW_PCH_DMIC |
177					RT711_JD2),
178	},
179	/* TigerLake-SDCA devices */
180	{
181		.callback = sof_sdw_quirk_cb,
182		.matches = {
183			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
184			DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0A32")
185		},
186		.driver_data = (void *)(SOF_SDW_TGL_HDMI |
187					RT711_JD2 |
188					SOF_RT715_DAI_ID_FIX |
189					SOF_SDW_FOUR_SPK),
190	},
191	/* AlderLake devices */
192	{
193		.callback = sof_sdw_quirk_cb,
194		.matches = {
195			DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),
196			DMI_MATCH(DMI_PRODUCT_NAME, "Alder Lake Client Platform"),
197		},
198		.driver_data = (void *)(RT711_JD1 |
199					SOF_SDW_TGL_HDMI |
200					SOF_RT715_DAI_ID_FIX |
201					SOF_SDW_PCH_DMIC),
202	},
203	{
204		.callback = sof_sdw_quirk_cb,
205		.matches = {
206			DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),
207			DMI_MATCH(DMI_PRODUCT_NAME, "Meteor Lake Client Platform"),
208		},
209		.driver_data = (void *)(RT711_JD2_100K),
210	},
211	{
212		.callback = sof_sdw_quirk_cb,
213		.matches = {
214			DMI_MATCH(DMI_SYS_VENDOR, "Google"),
215			DMI_MATCH(DMI_PRODUCT_NAME, "Rex"),
216		},
217		.driver_data = (void *)(SOF_SDW_PCH_DMIC),
218	},
219	/* LunarLake devices */
220	{
221		.callback = sof_sdw_quirk_cb,
222		.matches = {
223			DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),
224			DMI_MATCH(DMI_PRODUCT_NAME, "Lunar Lake Client Platform"),
225		},
226		.driver_data = (void *)(RT711_JD2_100K),
227	},
228	{}
229};
230
231static struct snd_soc_dai_link_component dmic_component[] = {
232	{
233		.name = "dmic-codec",
234		.dai_name = "dmic-hifi",
235	}
236};
237
238static struct snd_soc_dai_link_component platform_component[] = {
239	{
240		/* name might be overridden during probe */
241		.name = "0000:00:1f.3"
242	}
243};
244
245/* these wrappers are only needed to avoid typecast compilation errors */
246int sdw_startup(struct snd_pcm_substream *substream)
247{
248	return sdw_startup_stream(substream);
249}
250
251int sdw_prepare(struct snd_pcm_substream *substream)
252{
253	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
254	struct sdw_stream_runtime *sdw_stream;
255	struct snd_soc_dai *dai;
256
257	/* Find stream from first CPU DAI */
258	dai = asoc_rtd_to_cpu(rtd, 0);
259
260	sdw_stream = snd_soc_dai_get_stream(dai, substream->stream);
261
262	if (IS_ERR(sdw_stream)) {
263		dev_err(rtd->dev, "no stream found for DAI %s", dai->name);
264		return PTR_ERR(sdw_stream);
265	}
266
267	return sdw_prepare_stream(sdw_stream);
268}
269
270int sdw_trigger(struct snd_pcm_substream *substream, int cmd)
271{
272	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
273	struct sdw_stream_runtime *sdw_stream;
274	struct snd_soc_dai *dai;
275	int ret;
276
277	/* Find stream from first CPU DAI */
278	dai = asoc_rtd_to_cpu(rtd, 0);
279
280	sdw_stream = snd_soc_dai_get_stream(dai, substream->stream);
281
282	if (IS_ERR(sdw_stream)) {
283		dev_err(rtd->dev, "no stream found for DAI %s", dai->name);
284		return PTR_ERR(sdw_stream);
285	}
286
287	switch (cmd) {
288	case SNDRV_PCM_TRIGGER_START:
289	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
290	case SNDRV_PCM_TRIGGER_RESUME:
291		ret = sdw_enable_stream(sdw_stream);
292		break;
293
294	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
295	case SNDRV_PCM_TRIGGER_SUSPEND:
296	case SNDRV_PCM_TRIGGER_STOP:
297		ret = sdw_disable_stream(sdw_stream);
298		break;
299	default:
300		ret = -EINVAL;
301		break;
302	}
303
304	if (ret)
305		dev_err(rtd->dev, "%s trigger %d failed: %d", __func__, cmd, ret);
306
307	return ret;
308}
309
310int sdw_hw_free(struct snd_pcm_substream *substream)
311{
312	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
313	struct sdw_stream_runtime *sdw_stream;
314	struct snd_soc_dai *dai;
315
316	/* Find stream from first CPU DAI */
317	dai = asoc_rtd_to_cpu(rtd, 0);
318
319	sdw_stream = snd_soc_dai_get_stream(dai, substream->stream);
320
321	if (IS_ERR(sdw_stream)) {
322		dev_err(rtd->dev, "no stream found for DAI %s", dai->name);
323		return PTR_ERR(sdw_stream);
324	}
325
326	return sdw_deprepare_stream(sdw_stream);
327}
328
329void sdw_shutdown(struct snd_pcm_substream *substream)
330{
331	sdw_shutdown_stream(substream);
332}
333
334static const struct snd_soc_ops sdw_ops = {
335	.startup = sdw_startup,
336	.prepare = sdw_prepare,
337	.trigger = sdw_trigger,
338	.hw_free = sdw_hw_free,
339	.shutdown = sdw_shutdown,
340};
341
342static struct sof_sdw_codec_info codec_info_list[] = {
343	{
344		.part_id = 0x700,
345		.direction = {true, true},
346		.dai_name = "rt700-aif1",
347		.init = sof_sdw_rt700_init,
348	},
349	{
350		.part_id = 0x711,
351		.version_id = 3,
352		.direction = {true, true},
353		.dai_name = "rt711-sdca-aif1",
354		.init = sof_sdw_rt711_sdca_init,
355		.exit = sof_sdw_rt711_sdca_exit,
356	},
357	{
358		.part_id = 0x711,
359		.version_id = 2,
360		.direction = {true, true},
361		.dai_name = "rt711-aif1",
362		.init = sof_sdw_rt711_init,
363		.exit = sof_sdw_rt711_exit,
364	},
365	{
366		.part_id = 0x1308,
367		.acpi_id = "10EC1308",
368		.direction = {true, false},
369		.dai_name = "rt1308-aif",
370		.ops = &sof_sdw_rt1308_i2s_ops,
371		.init = sof_sdw_rt1308_init,
372	},
373	{
374		.part_id = 0x1316,
375		.direction = {true, true},
376		.dai_name = "rt1316-aif",
377		.init = sof_sdw_rt1316_init,
378	},
379	{
380		.part_id = 0x714,
381		.version_id = 3,
382		.direction = {false, true},
383		.ignore_pch_dmic = true,
384		.dai_name = "rt715-aif2",
385		.init = sof_sdw_rt715_sdca_init,
386	},
387	{
388		.part_id = 0x715,
389		.version_id = 3,
390		.direction = {false, true},
391		.ignore_pch_dmic = true,
392		.dai_name = "rt715-aif2",
393		.init = sof_sdw_rt715_sdca_init,
394	},
395	{
396		.part_id = 0x714,
397		.version_id = 2,
398		.direction = {false, true},
399		.ignore_pch_dmic = true,
400		.dai_name = "rt715-aif2",
401		.init = sof_sdw_rt715_init,
402	},
403	{
404		.part_id = 0x715,
405		.version_id = 2,
406		.direction = {false, true},
407		.ignore_pch_dmic = true,
408		.dai_name = "rt715-aif2",
409		.init = sof_sdw_rt715_init,
410	},
411	{
412		.part_id = 0x8373,
413		.direction = {true, true},
414		.dai_name = "max98373-aif1",
415		.init = sof_sdw_mx8373_init,
416		.codec_card_late_probe = sof_sdw_mx8373_late_probe,
417	},
418	{
419		.part_id = 0x5682,
420		.direction = {true, true},
421		.dai_name = "rt5682-sdw",
422		.init = sof_sdw_rt5682_init,
423	},
424};
425
426static inline int find_codec_info_part(u64 adr)
427{
428	unsigned int part_id, sdw_version;
429	int i;
430
431	part_id = SDW_PART_ID(adr);
432	sdw_version = SDW_VERSION(adr);
433	for (i = 0; i < ARRAY_SIZE(codec_info_list); i++)
434		/*
435		 * A codec info is for all sdw version with the part id if
436		 * version_id is not specified in the codec info.
437		 */
438		if (part_id == codec_info_list[i].part_id &&
439		    (!codec_info_list[i].version_id ||
440		     sdw_version == codec_info_list[i].version_id))
441			return i;
442
443	return -EINVAL;
444
445}
446
447static inline int find_codec_info_acpi(const u8 *acpi_id)
448{
449	int i;
450
451	if (!acpi_id[0])
452		return -EINVAL;
453
454	for (i = 0; i < ARRAY_SIZE(codec_info_list); i++)
455		if (!memcmp(codec_info_list[i].acpi_id, acpi_id,
456			    ACPI_ID_LEN))
457			break;
458
459	if (i == ARRAY_SIZE(codec_info_list))
460		return -EINVAL;
461
462	return i;
463}
464
465/*
466 * get BE dailink number and CPU DAI number based on sdw link adr.
467 * Since some sdw slaves may be aggregated, the CPU DAI number
468 * may be larger than the number of BE dailinks.
469 */
470static int get_sdw_dailink_info(const struct snd_soc_acpi_link_adr *links,
471				int *sdw_be_num, int *sdw_cpu_dai_num)
472{
473	const struct snd_soc_acpi_link_adr *link;
474	bool group_visited[SDW_MAX_GROUPS];
475	bool no_aggregation;
476	int i;
477
478	no_aggregation = sof_sdw_quirk & SOF_SDW_NO_AGGREGATION;
479	*sdw_cpu_dai_num = 0;
480	*sdw_be_num  = 0;
481
482	if (!links)
483		return -EINVAL;
484
485	for (i = 0; i < SDW_MAX_GROUPS; i++)
486		group_visited[i] = false;
487
488	for (link = links; link->num_adr; link++) {
489		const struct snd_soc_acpi_endpoint *endpoint;
490		int codec_index;
491		int stream;
492		u64 adr;
493
494		adr = link->adr_d->adr;
495		codec_index = find_codec_info_part(adr);
496		if (codec_index < 0)
497			return codec_index;
498
499		endpoint = link->adr_d->endpoints;
500
501		/* count DAI number for playback and capture */
502		for_each_pcm_streams(stream) {
503			if (!codec_info_list[codec_index].direction[stream])
504				continue;
505
506			(*sdw_cpu_dai_num)++;
507
508			/* count BE for each non-aggregated slave or group */
509			if (!endpoint->aggregated || no_aggregation ||
510			    !group_visited[endpoint->group_id])
511				(*sdw_be_num)++;
512		}
513
514		if (endpoint->aggregated)
515			group_visited[endpoint->group_id] = true;
516	}
517
518	return 0;
519}
520
521static void init_dai_link(struct snd_soc_dai_link *dai_links, int be_id,
522			  char *name, int playback, int capture,
523			  struct snd_soc_dai_link_component *cpus,
524			  int cpus_num,
525			  struct snd_soc_dai_link_component *codecs,
526			  int codecs_num,
527			  int (*init)(struct snd_soc_pcm_runtime *rtd),
528			  const struct snd_soc_ops *ops)
529{
530	dai_links->id = be_id;
531	dai_links->name = name;
532	dai_links->platforms = platform_component;
533	dai_links->num_platforms = ARRAY_SIZE(platform_component);
534	dai_links->nonatomic = true;
535	dai_links->no_pcm = 1;
536	dai_links->cpus = cpus;
537	dai_links->num_cpus = cpus_num;
538	dai_links->codecs = codecs;
539	dai_links->num_codecs = codecs_num;
540	dai_links->dpcm_playback = playback;
541	dai_links->dpcm_capture = capture;
542	dai_links->init = init;
543	dai_links->ops = ops;
544}
545
546static bool is_unique_device(const struct snd_soc_acpi_link_adr *link,
547			     unsigned int sdw_version,
548			     unsigned int mfg_id,
549			     unsigned int part_id,
550			     unsigned int class_id,
551			     int index_in_link
552			    )
553{
554	int i;
555
556	for (i = 0; i < link->num_adr; i++) {
557		unsigned int sdw1_version, mfg1_id, part1_id, class1_id;
558		u64 adr;
559
560		/* skip itself */
561		if (i == index_in_link)
562			continue;
563
564		adr = link->adr_d[i].adr;
565
566		sdw1_version = SDW_VERSION(adr);
567		mfg1_id = SDW_MFG_ID(adr);
568		part1_id = SDW_PART_ID(adr);
569		class1_id = SDW_CLASS_ID(adr);
570
571		if (sdw_version == sdw1_version &&
572		    mfg_id == mfg1_id &&
573		    part_id == part1_id &&
574		    class_id == class1_id)
575			return false;
576	}
577
578	return true;
579}
580
581static int create_codec_dai_name(struct device *dev,
582				 const struct snd_soc_acpi_link_adr *link,
583				 struct snd_soc_dai_link_component *codec,
584				 int offset,
585				 struct snd_soc_codec_conf *codec_conf,
586				 int codec_count,
587				 int *codec_conf_index)
588{
589	int i;
590
591	/* sanity check */
592	if (*codec_conf_index + link->num_adr > codec_count) {
593		dev_err(dev, "codec_conf: out-of-bounds access requested\n");
594		return -EINVAL;
595	}
596
597	for (i = 0; i < link->num_adr; i++) {
598		unsigned int sdw_version, unique_id, mfg_id;
599		unsigned int link_id, part_id, class_id;
600		int codec_index, comp_index;
601		char *codec_str;
602		u64 adr;
603
604		adr = link->adr_d[i].adr;
605
606		sdw_version = SDW_VERSION(adr);
607		link_id = SDW_DISCO_LINK_ID(adr);
608		unique_id = SDW_UNIQUE_ID(adr);
609		mfg_id = SDW_MFG_ID(adr);
610		part_id = SDW_PART_ID(adr);
611		class_id = SDW_CLASS_ID(adr);
612
613		comp_index = i + offset;
614		if (is_unique_device(link, sdw_version, mfg_id, part_id,
615				     class_id, i)) {
616			codec_str = "sdw:%x:%x:%x:%x";
617			codec[comp_index].name =
618				devm_kasprintf(dev, GFP_KERNEL, codec_str,
619					       link_id, mfg_id, part_id,
620					       class_id);
621		} else {
622			codec_str = "sdw:%x:%x:%x:%x:%x";
623			codec[comp_index].name =
624				devm_kasprintf(dev, GFP_KERNEL, codec_str,
625					       link_id, mfg_id, part_id,
626					       class_id, unique_id);
627		}
628
629		if (!codec[comp_index].name)
630			return -ENOMEM;
631
632		codec_index = find_codec_info_part(adr);
633		if (codec_index < 0)
634			return codec_index;
635
636		codec[comp_index].dai_name =
637			codec_info_list[codec_index].dai_name;
638
639		codec_conf[*codec_conf_index].dlc = codec[comp_index];
640		codec_conf[*codec_conf_index].name_prefix = link->adr_d[i].name_prefix;
641
642		++*codec_conf_index;
643	}
644
645	return 0;
646}
647
648static int set_codec_init_func(const struct snd_soc_acpi_link_adr *link,
649			       struct snd_soc_dai_link *dai_links,
650			       bool playback, int group_id)
651{
652	int i;
653
654	do {
655		/*
656		 * Initialize the codec. If codec is part of an aggregated
657		 * group (group_id>0), initialize all codecs belonging to
658		 * same group.
659		 */
660		for (i = 0; i < link->num_adr; i++) {
661			int codec_index;
662
663			codec_index = find_codec_info_part(link->adr_d[i].adr);
664
665			if (codec_index < 0)
666				return codec_index;
667			/* The group_id is > 0 iff the codec is aggregated */
668			if (link->adr_d[i].endpoints->group_id != group_id)
669				continue;
670			if (codec_info_list[codec_index].init)
671				codec_info_list[codec_index].init(link,
672						dai_links,
673						&codec_info_list[codec_index],
674						playback);
675		}
676		link++;
677	} while (link->mask && group_id);
678
679	return 0;
680}
681
682/*
683 * check endpoint status in slaves and gather link ID for all slaves in
684 * the same group to generate different CPU DAI. Now only support
685 * one sdw link with all slaves set with only single group id.
686 *
687 * one slave on one sdw link with aggregated = 0
688 * one sdw BE DAI <---> one-cpu DAI <---> one-codec DAI
689 *
690 * two or more slaves on one sdw link with aggregated = 0
691 * one sdw BE DAI  <---> one-cpu DAI <---> multi-codec DAIs
692 *
693 * multiple links with multiple slaves with aggregated = 1
694 * one sdw BE DAI  <---> 1 .. N CPU DAIs <----> 1 .. N codec DAIs
695 */
696static int get_slave_info(const struct snd_soc_acpi_link_adr *adr_link,
697			  struct device *dev, int *cpu_dai_id, int *cpu_dai_num,
698			  int *codec_num, int *group_id,
699			  bool *group_generated)
700{
701	const struct snd_soc_acpi_adr_device *adr_d;
702	const struct snd_soc_acpi_link_adr *adr_next;
703	bool no_aggregation;
704	int index = 0;
705
706	no_aggregation = sof_sdw_quirk & SOF_SDW_NO_AGGREGATION;
707	*codec_num = adr_link->num_adr;
708	adr_d = adr_link->adr_d;
709
710	/* make sure the link mask has a single bit set */
711	if (!is_power_of_2(adr_link->mask))
712		return -EINVAL;
713
714	cpu_dai_id[index++] = ffs(adr_link->mask) - 1;
715	if (!adr_d->endpoints->aggregated || no_aggregation) {
716		*cpu_dai_num = 1;
717		*group_id = 0;
718		return 0;
719	}
720
721	*group_id = adr_d->endpoints->group_id;
722
723	/* gather other link ID of slaves in the same group */
724	for (adr_next = adr_link + 1; adr_next && adr_next->num_adr;
725		adr_next++) {
726		const struct snd_soc_acpi_endpoint *endpoint;
727
728		endpoint = adr_next->adr_d->endpoints;
729		if (!endpoint->aggregated ||
730		    endpoint->group_id != *group_id)
731			continue;
732
733		/* make sure the link mask has a single bit set */
734		if (!is_power_of_2(adr_next->mask))
735			return -EINVAL;
736
737		if (index >= SDW_MAX_CPU_DAIS) {
738			dev_err(dev, " cpu_dai_id array overflows");
739			return -EINVAL;
740		}
741
742		cpu_dai_id[index++] = ffs(adr_next->mask) - 1;
743		*codec_num += adr_next->num_adr;
744	}
745
746	/*
747	 * indicate CPU DAIs for this group have been generated
748	 * to avoid generating CPU DAIs for this group again.
749	 */
750	group_generated[*group_id] = true;
751	*cpu_dai_num = index;
752
753	return 0;
754}
755
756static int create_sdw_dailink(struct device *dev, int *be_index,
757			      struct snd_soc_dai_link *dai_links,
758			      int sdw_be_num, int sdw_cpu_dai_num,
759			      struct snd_soc_dai_link_component *cpus,
760			      const struct snd_soc_acpi_link_adr *link,
761			      int *cpu_id, bool *group_generated,
762			      struct snd_soc_codec_conf *codec_conf,
763			      int codec_count,
764			      int *codec_conf_index,
765			      bool *ignore_pch_dmic)
766{
767	const struct snd_soc_acpi_link_adr *link_next;
768	struct snd_soc_dai_link_component *codecs;
769	int cpu_dai_id[SDW_MAX_CPU_DAIS];
770	int cpu_dai_num, cpu_dai_index;
771	unsigned int group_id;
772	int codec_idx = 0;
773	int i = 0, j = 0;
774	int codec_index;
775	int codec_num;
776	int stream;
777	int ret;
778	int k;
779
780	ret = get_slave_info(link, dev, cpu_dai_id, &cpu_dai_num, &codec_num,
781			     &group_id, group_generated);
782	if (ret)
783		return ret;
784
785	codecs = devm_kcalloc(dev, codec_num, sizeof(*codecs), GFP_KERNEL);
786	if (!codecs)
787		return -ENOMEM;
788
789	/* generate codec name on different links in the same group */
790	for (link_next = link; link_next && link_next->num_adr &&
791	     i < cpu_dai_num; link_next++) {
792		const struct snd_soc_acpi_endpoint *endpoints;
793
794		endpoints = link_next->adr_d->endpoints;
795		if (group_id && (!endpoints->aggregated ||
796				 endpoints->group_id != group_id))
797			continue;
798
799		/* skip the link excluded by this processed group */
800		if (cpu_dai_id[i] != ffs(link_next->mask) - 1)
801			continue;
802
803		ret = create_codec_dai_name(dev, link_next, codecs, codec_idx,
804					    codec_conf, codec_count, codec_conf_index);
805		if (ret < 0)
806			return ret;
807
808		/* check next link to create codec dai in the processed group */
809		i++;
810		codec_idx += link_next->num_adr;
811	}
812
813	/* find codec info to create BE DAI */
814	codec_index = find_codec_info_part(link->adr_d[0].adr);
815	if (codec_index < 0)
816		return codec_index;
817
818	if (codec_info_list[codec_index].ignore_pch_dmic)
819		*ignore_pch_dmic = true;
820
821	cpu_dai_index = *cpu_id;
822	for_each_pcm_streams(stream) {
823		char *name, *cpu_name;
824		int playback, capture;
825		static const char * const sdw_stream_name[] = {
826			"SDW%d-Playback",
827			"SDW%d-Capture",
828		};
829
830		if (!codec_info_list[codec_index].direction[stream])
831			continue;
832
833		/* create stream name according to first link id */
834		name = devm_kasprintf(dev, GFP_KERNEL,
835				      sdw_stream_name[stream], cpu_dai_id[0]);
836		if (!name)
837			return -ENOMEM;
838
839		/*
840		 * generate CPU DAI name base on the sdw link ID and
841		 * PIN ID with offset of 2 according to sdw dai driver.
842		 */
843		for (k = 0; k < cpu_dai_num; k++) {
844			cpu_name = devm_kasprintf(dev, GFP_KERNEL,
845						  "SDW%d Pin%d", cpu_dai_id[k],
846						  j + SDW_INTEL_BIDIR_PDI_BASE);
847			if (!cpu_name)
848				return -ENOMEM;
849
850			if (cpu_dai_index >= sdw_cpu_dai_num) {
851				dev_err(dev, "invalid cpu dai index %d",
852					cpu_dai_index);
853				return -EINVAL;
854			}
855
856			cpus[cpu_dai_index++].dai_name = cpu_name;
857		}
858
859		if (*be_index >= sdw_be_num) {
860			dev_err(dev, " invalid be dai index %d", *be_index);
861			return -EINVAL;
862		}
863
864		if (*cpu_id >= sdw_cpu_dai_num) {
865			dev_err(dev, " invalid cpu dai index %d", *cpu_id);
866			return -EINVAL;
867		}
868
869		playback = (stream == SNDRV_PCM_STREAM_PLAYBACK);
870		capture = (stream == SNDRV_PCM_STREAM_CAPTURE);
871		init_dai_link(dai_links + *be_index, *be_index, name,
872			      playback, capture,
873			      cpus + *cpu_id, cpu_dai_num,
874			      codecs, codec_num,
875			      NULL, &sdw_ops);
876		/*
877		 * SoundWire DAILINKs use 'stream' functions and Bank Switch operations
878		 * based on wait_for_completion(), tag them as 'nonatomic'.
879		 */
880		dai_links[*be_index].nonatomic = true;
881
882		ret = set_codec_init_func(link, dai_links + (*be_index)++,
883					  playback, group_id);
884		if (ret < 0) {
885			dev_err(dev, "failed to init codec %d", codec_index);
886			return ret;
887		}
888
889		*cpu_id += cpu_dai_num;
890		j++;
891	}
892
893	return 0;
894}
895
896/*
897 * DAI link ID of SSP & DMIC & HDMI are based on last
898 * link ID used by sdw link. Since be_id may be changed
899 * in init func of sdw codec, it is not equal to be_id
900 */
901static inline int get_next_be_id(struct snd_soc_dai_link *links,
902				 int be_id)
903{
904	return links[be_id - 1].id + 1;
905}
906
907#define IDISP_CODEC_MASK	0x4
908
909static int sof_card_codec_conf_alloc(struct device *dev,
910				     struct snd_soc_acpi_mach_params *mach_params,
911				     struct snd_soc_codec_conf **codec_conf,
912				     int *codec_conf_count)
913{
914	const struct snd_soc_acpi_link_adr *adr_link;
915	struct snd_soc_codec_conf *c_conf;
916	int num_codecs = 0;
917	int i;
918
919	adr_link = mach_params->links;
920	if (!adr_link)
921		return -EINVAL;
922
923	/* generate DAI links by each sdw link */
924	for (; adr_link->num_adr; adr_link++) {
925		for (i = 0; i < adr_link->num_adr; i++) {
926			if (!adr_link->adr_d[i].name_prefix) {
927				dev_err(dev, "codec 0x%llx does not have a name prefix\n",
928					adr_link->adr_d[i].adr);
929				return -EINVAL;
930			}
931		}
932		num_codecs += adr_link->num_adr;
933	}
934
935	c_conf = devm_kzalloc(dev, num_codecs * sizeof(*c_conf), GFP_KERNEL);
936	if (!c_conf)
937		return -ENOMEM;
938
939	*codec_conf = c_conf;
940	*codec_conf_count = num_codecs;
941
942	return 0;
943}
944
945static int sof_card_dai_links_create(struct device *dev,
946				     struct snd_soc_acpi_mach *mach,
947				     struct snd_soc_card *card)
948{
949	int ssp_num, sdw_be_num = 0, hdmi_num = 0, dmic_num;
950	struct mc_private *ctx = snd_soc_card_get_drvdata(card);
951	struct snd_soc_dai_link_component *idisp_components;
952	struct snd_soc_dai_link_component *ssp_components;
953	struct snd_soc_acpi_mach_params *mach_params;
954	const struct snd_soc_acpi_link_adr *adr_link;
955	struct snd_soc_dai_link_component *cpus;
956	struct snd_soc_codec_conf *codec_conf;
957	bool ignore_pch_dmic = false;
958	int codec_conf_count;
959	int codec_conf_index = 0;
960	bool group_generated[SDW_MAX_GROUPS];
961	int ssp_codec_index, ssp_mask;
962	struct snd_soc_dai_link *links;
963	int num_links, link_id = 0;
964	char *name, *cpu_name;
965	int total_cpu_dai_num;
966	int sdw_cpu_dai_num;
967	int i, j, be_id = 0;
968	int cpu_id = 0;
969	int comp_num;
970	int ret;
971
972	mach_params = &mach->mach_params;
973
974	/* allocate codec conf, will be populated when dailinks are created */
975	ret = sof_card_codec_conf_alloc(dev, mach_params, &codec_conf, &codec_conf_count);
976	if (ret < 0)
977		return ret;
978
979	/* reset amp_num to ensure amp_num++ starts from 0 in each probe */
980	for (i = 0; i < ARRAY_SIZE(codec_info_list); i++)
981		codec_info_list[i].amp_num = 0;
982
983	if (sof_sdw_quirk & SOF_SDW_TGL_HDMI)
984		hdmi_num = SOF_TGL_HDMI_COUNT;
985	else
986		hdmi_num = SOF_PRE_TGL_HDMI_COUNT;
987
988	ssp_mask = SOF_SSP_GET_PORT(sof_sdw_quirk);
989	/*
990	 * on generic tgl platform, I2S or sdw mode is supported
991	 * based on board rework. A ACPI device is registered in
992	 * system only when I2S mode is supported, not sdw mode.
993	 * Here check ACPI ID to confirm I2S is supported.
994	 */
995	ssp_codec_index = find_codec_info_acpi(mach->id);
996	ssp_num = ssp_codec_index >= 0 ? hweight_long(ssp_mask) : 0;
997	comp_num = hdmi_num + ssp_num;
998
999	ret = get_sdw_dailink_info(mach_params->links,
1000				   &sdw_be_num, &sdw_cpu_dai_num);
1001	if (ret < 0) {
1002		dev_err(dev, "failed to get sdw link info %d", ret);
1003		return ret;
1004	}
1005
1006	if (mach_params->codec_mask & IDISP_CODEC_MASK)
1007		ctx->idisp_codec = true;
1008
1009	/* enable dmic01 & dmic16k */
1010	dmic_num = (sof_sdw_quirk & SOF_SDW_PCH_DMIC || mach_params->dmic_num) ? 2 : 0;
1011	comp_num += dmic_num;
1012
1013	dev_dbg(dev, "sdw %d, ssp %d, dmic %d, hdmi %d", sdw_be_num, ssp_num,
1014		dmic_num, ctx->idisp_codec ? hdmi_num : 0);
1015
1016	/* allocate BE dailinks */
1017	num_links = comp_num + sdw_be_num;
1018	links = devm_kcalloc(dev, num_links, sizeof(*links), GFP_KERNEL);
1019
1020	/* allocated CPU DAIs */
1021	total_cpu_dai_num = comp_num + sdw_cpu_dai_num;
1022	cpus = devm_kcalloc(dev, total_cpu_dai_num, sizeof(*cpus),
1023			    GFP_KERNEL);
1024
1025	if (!links || !cpus)
1026		return -ENOMEM;
1027
1028	/* SDW */
1029	if (!sdw_be_num)
1030		goto SSP;
1031
1032	adr_link = mach_params->links;
1033	if (!adr_link)
1034		return -EINVAL;
1035
1036	/*
1037	 * SoundWire Slaves aggregated in the same group may be
1038	 * located on different hardware links. Clear array to indicate
1039	 * CPU DAIs for this group have not been generated.
1040	 */
1041	for (i = 0; i < SDW_MAX_GROUPS; i++)
1042		group_generated[i] = false;
1043
1044	/* generate DAI links by each sdw link */
1045	for (; adr_link->num_adr; adr_link++) {
1046		const struct snd_soc_acpi_endpoint *endpoint;
1047
1048		endpoint = adr_link->adr_d->endpoints;
1049		if (endpoint->aggregated && !endpoint->group_id) {
1050			dev_err(dev, "invalid group id on link %x",
1051				adr_link->mask);
1052			continue;
1053		}
1054
1055		/* this group has been generated */
1056		if (endpoint->aggregated &&
1057		    group_generated[endpoint->group_id])
1058			continue;
1059
1060		ret = create_sdw_dailink(dev, &be_id, links, sdw_be_num,
1061					 sdw_cpu_dai_num, cpus, adr_link,
1062					 &cpu_id, group_generated,
1063					 codec_conf, codec_conf_count,
1064					 &codec_conf_index,
1065					 &ignore_pch_dmic);
1066		if (ret < 0) {
1067			dev_err(dev, "failed to create dai link %d", be_id);
1068			return -ENOMEM;
1069		}
1070	}
1071
1072	/* non-sdw DAI follows sdw DAI */
1073	link_id = be_id;
1074
1075	/* get BE ID for non-sdw DAI */
1076	be_id = get_next_be_id(links, be_id);
1077
1078SSP:
1079	/* SSP */
1080	if (!ssp_num)
1081		goto DMIC;
1082
1083	for (i = 0, j = 0; ssp_mask; i++, ssp_mask >>= 1) {
1084		struct sof_sdw_codec_info *info;
1085		int playback, capture;
1086		char *codec_name;
1087
1088		if (!(ssp_mask & 0x1))
1089			continue;
1090
1091		name = devm_kasprintf(dev, GFP_KERNEL,
1092				      "SSP%d-Codec", i);
1093		if (!name)
1094			return -ENOMEM;
1095
1096		cpu_name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d Pin", i);
1097		if (!cpu_name)
1098			return -ENOMEM;
1099
1100		ssp_components = devm_kzalloc(dev, sizeof(*ssp_components),
1101					      GFP_KERNEL);
1102		if (!ssp_components)
1103			return -ENOMEM;
1104
1105		info = &codec_info_list[ssp_codec_index];
1106		codec_name = devm_kasprintf(dev, GFP_KERNEL, "i2c-%s:0%d",
1107					    info->acpi_id, j++);
1108		if (!codec_name)
1109			return -ENOMEM;
1110
1111		ssp_components->name = codec_name;
1112		ssp_components->dai_name = info->dai_name;
1113		cpus[cpu_id].dai_name = cpu_name;
1114
1115		playback = info->direction[SNDRV_PCM_STREAM_PLAYBACK];
1116		capture = info->direction[SNDRV_PCM_STREAM_CAPTURE];
1117		init_dai_link(links + link_id, be_id, name,
1118			      playback, capture,
1119			      cpus + cpu_id, 1,
1120			      ssp_components, 1,
1121			      NULL, info->ops);
1122
1123		ret = info->init(NULL, links + link_id, info, 0);
1124		if (ret < 0)
1125			return ret;
1126
1127		INC_ID(be_id, cpu_id, link_id);
1128	}
1129
1130DMIC:
1131	/* dmic */
1132	if (dmic_num > 0) {
1133		if (ignore_pch_dmic) {
1134			dev_warn(dev, "Ignoring PCH DMIC\n");
1135			goto HDMI;
1136		}
1137		cpus[cpu_id].dai_name = "DMIC01 Pin";
1138		init_dai_link(links + link_id, be_id, "dmic01",
1139			      0, 1, // DMIC only supports capture
1140			      cpus + cpu_id, 1,
1141			      dmic_component, 1,
1142			      sof_sdw_dmic_init, NULL);
1143		INC_ID(be_id, cpu_id, link_id);
1144
1145		cpus[cpu_id].dai_name = "DMIC16k Pin";
1146		init_dai_link(links + link_id, be_id, "dmic16k",
1147			      0, 1, // DMIC only supports capture
1148			      cpus + cpu_id, 1,
1149			      dmic_component, 1,
1150			      /* don't call sof_sdw_dmic_init() twice */
1151			      NULL, NULL);
1152		INC_ID(be_id, cpu_id, link_id);
1153	}
1154
1155HDMI:
1156	/* HDMI */
1157	if (hdmi_num > 0) {
1158		idisp_components = devm_kcalloc(dev, hdmi_num,
1159						sizeof(*idisp_components),
1160						GFP_KERNEL);
1161		if (!idisp_components)
1162			return -ENOMEM;
1163	}
1164
1165	for (i = 0; i < hdmi_num; i++) {
1166		name = devm_kasprintf(dev, GFP_KERNEL,
1167				      "iDisp%d", i + 1);
1168		if (!name)
1169			return -ENOMEM;
1170
1171		if (ctx->idisp_codec) {
1172			idisp_components[i].name = "ehdaudio0D2";
1173			idisp_components[i].dai_name = devm_kasprintf(dev,
1174								      GFP_KERNEL,
1175								      "intel-hdmi-hifi%d",
1176								      i + 1);
1177			if (!idisp_components[i].dai_name)
1178				return -ENOMEM;
1179		} else {
1180			idisp_components[i].name = "snd-soc-dummy";
1181			idisp_components[i].dai_name = "snd-soc-dummy-dai";
1182		}
1183
1184		cpu_name = devm_kasprintf(dev, GFP_KERNEL,
1185					  "iDisp%d Pin", i + 1);
1186		if (!cpu_name)
1187			return -ENOMEM;
1188
1189		cpus[cpu_id].dai_name = cpu_name;
1190		init_dai_link(links + link_id, be_id, name,
1191			      1, 0, // HDMI only supports playback
1192			      cpus + cpu_id, 1,
1193			      idisp_components + i, 1,
1194			      sof_sdw_hdmi_init, NULL);
1195		INC_ID(be_id, cpu_id, link_id);
1196	}
1197
1198	card->dai_link = links;
1199	card->num_links = num_links;
1200
1201	card->codec_conf = codec_conf;
1202	card->num_configs = codec_conf_count;
1203
1204	return 0;
1205}
1206
1207static int sof_sdw_card_late_probe(struct snd_soc_card *card)
1208{
1209	int i, ret;
1210
1211	for (i = 0; i < ARRAY_SIZE(codec_info_list); i++) {
1212		if (!codec_info_list[i].late_probe)
1213			continue;
1214
1215		ret = codec_info_list[i].codec_card_late_probe(card);
1216		if (ret < 0)
1217			return ret;
1218	}
1219
1220	return sof_sdw_hdmi_card_late_probe(card);
1221}
1222
1223/* SoC card */
1224static const char sdw_card_long_name[] = "Intel Soundwire SOF";
1225
1226static struct snd_soc_card card_sof_sdw = {
1227	.name = "soundwire",
1228	.owner = THIS_MODULE,
1229	.late_probe = sof_sdw_card_late_probe,
1230};
1231
1232static int mc_probe(struct platform_device *pdev)
1233{
1234	struct snd_soc_card *card = &card_sof_sdw;
1235	struct snd_soc_acpi_mach *mach;
1236	struct mc_private *ctx;
1237	int amp_num = 0, i;
1238	int ret;
1239
1240	dev_dbg(&pdev->dev, "Entry %s\n", __func__);
1241
1242	ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
1243	if (!ctx)
1244		return -ENOMEM;
1245
1246	dmi_check_system(sof_sdw_quirk_table);
1247
1248	if (quirk_override != -1) {
1249		dev_info(&pdev->dev, "Overriding quirk 0x%lx => 0x%x\n",
1250			 sof_sdw_quirk, quirk_override);
1251		sof_sdw_quirk = quirk_override;
1252	}
1253	log_quirks(&pdev->dev);
1254
1255	INIT_LIST_HEAD(&ctx->hdmi_pcm_list);
1256
1257	card->dev = &pdev->dev;
1258	snd_soc_card_set_drvdata(card, ctx);
1259
1260	mach = pdev->dev.platform_data;
1261	ret = sof_card_dai_links_create(&pdev->dev, mach,
1262					card);
1263	if (ret < 0)
1264		return ret;
1265
1266	ctx->common_hdmi_codec_drv = mach->mach_params.common_hdmi_codec_drv;
1267
1268	/*
1269	 * the default amp_num is zero for each codec and
1270	 * amp_num will only be increased for active amp
1271	 * codecs on used platform
1272	 */
1273	for (i = 0; i < ARRAY_SIZE(codec_info_list); i++)
1274		amp_num += codec_info_list[i].amp_num;
1275
1276	card->components = devm_kasprintf(card->dev, GFP_KERNEL,
1277					  "cfg-spk:%d cfg-amp:%d",
1278					  (sof_sdw_quirk & SOF_SDW_FOUR_SPK)
1279					  ? 4 : 2, amp_num);
1280	if (!card->components)
1281		return -ENOMEM;
1282
1283	card->long_name = sdw_card_long_name;
1284
1285	/* Register the card */
1286	ret = devm_snd_soc_register_card(&pdev->dev, card);
1287	if (ret) {
1288		dev_err(card->dev, "snd_soc_register_card failed %d\n", ret);
1289		return ret;
1290	}
1291
1292	platform_set_drvdata(pdev, card);
1293
1294	return ret;
1295}
1296
1297static int mc_remove(struct platform_device *pdev)
1298{
1299	struct snd_soc_card *card = platform_get_drvdata(pdev);
1300	struct snd_soc_dai_link *link;
1301	int ret;
1302	int i, j;
1303
1304	for (i = 0; i < ARRAY_SIZE(codec_info_list); i++) {
1305		if (!codec_info_list[i].exit)
1306			continue;
1307		/*
1308		 * We don't need to call .exit function if there is no matched
1309		 * dai link found.
1310		 */
1311		for_each_card_prelinks(card, j, link) {
1312			if (!strcmp(link->codecs[0].dai_name,
1313				    codec_info_list[i].dai_name)) {
1314				ret = codec_info_list[i].exit(&pdev->dev, link);
1315				if (ret)
1316					dev_warn(&pdev->dev,
1317						 "codec exit failed %d\n",
1318						 ret);
1319				break;
1320			}
1321		}
1322	}
1323
1324	return 0;
1325}
1326
1327static struct platform_driver sof_sdw_driver = {
1328	.driver = {
1329		.name = "sof_sdw",
1330		.pm = &snd_soc_pm_ops,
1331	},
1332	.probe = mc_probe,
1333	.remove = mc_remove,
1334};
1335
1336module_platform_driver(sof_sdw_driver);
1337
1338MODULE_DESCRIPTION("ASoC SoundWire Generic Machine driver");
1339MODULE_AUTHOR("Bard Liao <yung-chuan.liao@linux.intel.com>");
1340MODULE_AUTHOR("Rander Wang <rander.wang@linux.intel.com>");
1341MODULE_AUTHOR("Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>");
1342MODULE_LICENSE("GPL v2");
1343MODULE_ALIAS("platform:sof_sdw");
1344