Linux Audio

Check our new training course

Loading...
v6.13.7
  1// SPDX-License-Identifier: (GPL-2.0 OR MIT)
  2//
  3// Copyright (c) 2018 BayLibre, SAS.
  4// Author: Jerome Brunet <jbrunet@baylibre.com>
  5
  6#include <linux/module.h>
  7#include <linux/of_platform.h>
  8#include <sound/soc.h>
  9#include <sound/soc-dai.h>
 10
 11#include "axg-tdm.h"
 12#include "meson-card.h"
 13
 14struct axg_dai_link_tdm_mask {
 15	u32 tx;
 16	u32 rx;
 17};
 18
 19struct axg_dai_link_tdm_data {
 20	unsigned int mclk_fs;
 21	unsigned int slots;
 22	unsigned int slot_width;
 23	u32 *tx_mask;
 24	u32 *rx_mask;
 25	struct axg_dai_link_tdm_mask *codec_masks;
 26};
 27
 28/*
 29 * Base params for the codec to codec links
 30 * Those will be over-written by the CPU side of the link
 31 */
 32static const struct snd_soc_pcm_stream codec_params = {
 33	.formats = SNDRV_PCM_FMTBIT_S24_LE,
 34	.rate_min = 5525,
 35	.rate_max = 192000,
 36	.channels_min = 1,
 37	.channels_max = 8,
 38};
 39
 40static int axg_card_tdm_be_hw_params(struct snd_pcm_substream *substream,
 41				     struct snd_pcm_hw_params *params)
 42{
 43	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
 44	struct meson_card *priv = snd_soc_card_get_drvdata(rtd->card);
 45	struct axg_dai_link_tdm_data *be =
 46		(struct axg_dai_link_tdm_data *)priv->link_data[rtd->id];
 47
 48	return meson_card_i2s_set_sysclk(substream, params, be->mclk_fs);
 49}
 50
 51static const struct snd_soc_ops axg_card_tdm_be_ops = {
 52	.hw_params = axg_card_tdm_be_hw_params,
 53};
 54
 55static int axg_card_tdm_dai_init(struct snd_soc_pcm_runtime *rtd)
 56{
 57	struct meson_card *priv = snd_soc_card_get_drvdata(rtd->card);
 58	struct axg_dai_link_tdm_data *be =
 59		(struct axg_dai_link_tdm_data *)priv->link_data[rtd->id];
 60	struct snd_soc_dai *codec_dai;
 61	int ret, i;
 62
 63	for_each_rtd_codec_dais(rtd, i, codec_dai) {
 64		ret = snd_soc_dai_set_tdm_slot(codec_dai,
 65					       be->codec_masks[i].tx,
 66					       be->codec_masks[i].rx,
 67					       be->slots, be->slot_width);
 68		if (ret && ret != -ENOTSUPP) {
 69			dev_err(codec_dai->dev,
 70				"setting tdm link slots failed\n");
 71			return ret;
 72		}
 73	}
 74
 75	ret = axg_tdm_set_tdm_slots(snd_soc_rtd_to_cpu(rtd, 0), be->tx_mask, be->rx_mask,
 76				    be->slots, be->slot_width);
 77	if (ret) {
 78		dev_err(snd_soc_rtd_to_cpu(rtd, 0)->dev, "setting tdm link slots failed\n");
 79		return ret;
 80	}
 81
 82	return 0;
 83}
 84
 85static int axg_card_tdm_dai_lb_init(struct snd_soc_pcm_runtime *rtd)
 86{
 87	struct meson_card *priv = snd_soc_card_get_drvdata(rtd->card);
 88	struct axg_dai_link_tdm_data *be =
 89		(struct axg_dai_link_tdm_data *)priv->link_data[rtd->id];
 90	int ret;
 91
 92	/* The loopback rx_mask is the pad tx_mask */
 93	ret = axg_tdm_set_tdm_slots(snd_soc_rtd_to_cpu(rtd, 0), NULL, be->tx_mask,
 94				    be->slots, be->slot_width);
 95	if (ret) {
 96		dev_err(snd_soc_rtd_to_cpu(rtd, 0)->dev, "setting tdm link slots failed\n");
 97		return ret;
 98	}
 99
100	return 0;
101}
102
103static int axg_card_add_tdm_loopback(struct snd_soc_card *card,
104				     int *index)
105{
106	struct meson_card *priv = snd_soc_card_get_drvdata(card);
107	struct snd_soc_dai_link *pad;
108	struct snd_soc_dai_link *lb;
109	struct snd_soc_dai_link_component *dlc;
110	int ret;
111
112	/* extend links */
113	ret = meson_card_reallocate_links(card, card->num_links + 1);
114	if (ret)
115		return ret;
116
117	pad = &card->dai_link[*index];
118	lb = &card->dai_link[*index + 1];
119
120	lb->name = devm_kasprintf(card->dev, GFP_KERNEL, "%s-lb", pad->name);
121	if (!lb->name)
122		return -ENOMEM;
123
124	dlc = devm_kzalloc(card->dev, sizeof(*dlc), GFP_KERNEL);
125	if (!dlc)
126		return -ENOMEM;
127
128	lb->cpus = dlc;
129	lb->codecs = &snd_soc_dummy_dlc;
130	lb->num_cpus = 1;
131	lb->num_codecs = 1;
132
133	lb->stream_name = lb->name;
134	lb->cpus->of_node = pad->cpus->of_node;
135	lb->cpus->dai_name = "TDM Loopback";
136	lb->capture_only = 1;
137	lb->no_pcm = 1;
138	lb->ops = &axg_card_tdm_be_ops;
139	lb->init = axg_card_tdm_dai_lb_init;
140
141	/* Provide the same link data to the loopback */
142	priv->link_data[*index + 1] = priv->link_data[*index];
143
144	/*
145	 * axg_card_clean_references() will iterate over this link,
146	 * make sure the node count is balanced
147	 */
148	of_node_get(lb->cpus->of_node);
149
150	/* Let add_links continue where it should */
151	*index += 1;
152
153	return 0;
154}
155
156static int axg_card_parse_cpu_tdm_slots(struct snd_soc_card *card,
157					struct snd_soc_dai_link *link,
158					struct device_node *node,
159					struct axg_dai_link_tdm_data *be)
160{
161	char propname[32];
162	u32 tx, rx;
163	int i;
164
165	be->tx_mask = devm_kcalloc(card->dev, AXG_TDM_NUM_LANES,
166				   sizeof(*be->tx_mask), GFP_KERNEL);
167	be->rx_mask = devm_kcalloc(card->dev, AXG_TDM_NUM_LANES,
168				   sizeof(*be->rx_mask), GFP_KERNEL);
169	if (!be->tx_mask || !be->rx_mask)
170		return -ENOMEM;
171
172	for (i = 0, tx = 0; i < AXG_TDM_NUM_LANES; i++) {
173		snprintf(propname, 32, "dai-tdm-slot-tx-mask-%d", i);
174		snd_soc_of_get_slot_mask(node, propname, &be->tx_mask[i]);
175		tx = max(tx, be->tx_mask[i]);
176	}
177
178	/* Disable playback is the interface has no tx slots */
179	if (!tx)
180		link->capture_only = 1;
181
182	for (i = 0, rx = 0; i < AXG_TDM_NUM_LANES; i++) {
183		snprintf(propname, 32, "dai-tdm-slot-rx-mask-%d", i);
184		snd_soc_of_get_slot_mask(node, propname, &be->rx_mask[i]);
185		rx = max(rx, be->rx_mask[i]);
186	}
187
188	/* Disable capture is the interface has no rx slots */
189	if (!rx)
190		link->playback_only = 1;
191
192	/* ... but the interface should at least have one direction */
193	if (!tx && !rx) {
194		dev_err(card->dev, "tdm link has no cpu slots\n");
195		return -EINVAL;
196	}
197
198	of_property_read_u32(node, "dai-tdm-slot-num", &be->slots);
199	if (!be->slots) {
200		/*
201		 * If the slot number is not provided, set it such as it
202		 * accommodates the largest mask
203		 */
204		be->slots = fls(max(tx, rx));
205	} else if (be->slots < fls(max(tx, rx)) || be->slots > 32) {
206		/*
207		 * Error if the slots can't accommodate the largest mask or
208		 * if it is just too big
209		 */
210		dev_err(card->dev, "bad slot number\n");
211		return -EINVAL;
212	}
213
214	of_property_read_u32(node, "dai-tdm-slot-width", &be->slot_width);
215
216	return 0;
217}
218
219static int axg_card_parse_codecs_masks(struct snd_soc_card *card,
220				       struct snd_soc_dai_link *link,
221				       struct device_node *node,
222				       struct axg_dai_link_tdm_data *be)
223{
224	struct axg_dai_link_tdm_mask *codec_mask;
225	struct device_node *np;
226
227	codec_mask = devm_kcalloc(card->dev, link->num_codecs,
228				  sizeof(*codec_mask), GFP_KERNEL);
229	if (!codec_mask)
230		return -ENOMEM;
231
232	be->codec_masks = codec_mask;
233
234	for_each_child_of_node(node, np) {
235		snd_soc_of_get_slot_mask(np, "dai-tdm-slot-rx-mask",
236					 &codec_mask->rx);
237		snd_soc_of_get_slot_mask(np, "dai-tdm-slot-tx-mask",
238					 &codec_mask->tx);
239
240		codec_mask++;
241	}
242
243	return 0;
244}
245
246static int axg_card_parse_tdm(struct snd_soc_card *card,
247			      struct device_node *node,
248			      int *index)
249{
250	struct meson_card *priv = snd_soc_card_get_drvdata(card);
251	struct snd_soc_dai_link *link = &card->dai_link[*index];
252	struct axg_dai_link_tdm_data *be;
253	int ret;
254
255	/* Allocate tdm link parameters */
256	be = devm_kzalloc(card->dev, sizeof(*be), GFP_KERNEL);
257	if (!be)
258		return -ENOMEM;
259	priv->link_data[*index] = be;
260
261	/* Setup tdm link */
262	link->ops = &axg_card_tdm_be_ops;
263	link->init = axg_card_tdm_dai_init;
264	link->dai_fmt = meson_card_parse_daifmt(node, link->cpus->of_node);
265
266	of_property_read_u32(node, "mclk-fs", &be->mclk_fs);
267
268	ret = axg_card_parse_cpu_tdm_slots(card, link, node, be);
269	if (ret) {
270		dev_err(card->dev, "error parsing tdm link slots\n");
271		return ret;
272	}
273
274	ret = axg_card_parse_codecs_masks(card, link, node, be);
275	if (ret)
276		return ret;
277
278	/* Add loopback if the pad dai has playback */
279	if (!link->capture_only) {
280		ret = axg_card_add_tdm_loopback(card, index);
281		if (ret)
282			return ret;
283	}
284
285	return 0;
286}
287
288static int axg_card_cpu_is_capture_fe(struct device_node *np)
289{
290	return of_device_is_compatible(np, DT_PREFIX "axg-toddr");
291}
292
293static int axg_card_cpu_is_playback_fe(struct device_node *np)
294{
295	return of_device_is_compatible(np, DT_PREFIX "axg-frddr");
296}
297
298static int axg_card_cpu_is_tdm_iface(struct device_node *np)
299{
300	return of_device_is_compatible(np, DT_PREFIX "axg-tdm-iface");
301}
302
303static int axg_card_cpu_is_codec(struct device_node *np)
304{
305	return of_device_is_compatible(np, DT_PREFIX "g12a-tohdmitx") ||
306		of_device_is_compatible(np, DT_PREFIX "g12a-toacodec");
307}
308
309static int axg_card_add_link(struct snd_soc_card *card, struct device_node *np,
310			     int *index)
311{
312	struct snd_soc_dai_link *dai_link = &card->dai_link[*index];
313	struct snd_soc_dai_link_component *cpu;
314	int ret;
315
316	cpu = devm_kzalloc(card->dev, sizeof(*cpu), GFP_KERNEL);
317	if (!cpu)
318		return -ENOMEM;
319
320	dai_link->cpus = cpu;
321	dai_link->num_cpus = 1;
322	dai_link->nonatomic = true;
323
324	ret = meson_card_parse_dai(card, np, dai_link->cpus);
325	if (ret)
326		return ret;
327
328	if (axg_card_cpu_is_playback_fe(dai_link->cpus->of_node))
329		return meson_card_set_fe_link(card, dai_link, np, true);
330	else if (axg_card_cpu_is_capture_fe(dai_link->cpus->of_node))
331		return meson_card_set_fe_link(card, dai_link, np, false);
332
333
334	ret = meson_card_set_be_link(card, dai_link, np);
335	if (ret)
336		return ret;
337
338	if (axg_card_cpu_is_codec(dai_link->cpus->of_node)) {
339		dai_link->c2c_params = &codec_params;
340		dai_link->num_c2c_params = 1;
341	} else {
342		dai_link->no_pcm = 1;
 
343		if (axg_card_cpu_is_tdm_iface(dai_link->cpus->of_node))
344			ret = axg_card_parse_tdm(card, np, index);
345	}
346
347	return ret;
348}
349
350static const struct meson_card_match_data axg_card_match_data = {
351	.add_link = axg_card_add_link,
352};
353
354static const struct of_device_id axg_card_of_match[] = {
355	{
356		.compatible = "amlogic,axg-sound-card",
357		.data = &axg_card_match_data,
358	}, {}
359};
360MODULE_DEVICE_TABLE(of, axg_card_of_match);
361
362static struct platform_driver axg_card_pdrv = {
363	.probe = meson_card_probe,
364	.remove = meson_card_remove,
365	.driver = {
366		.name = "axg-sound-card",
367		.of_match_table = axg_card_of_match,
368	},
369};
370module_platform_driver(axg_card_pdrv);
371
372MODULE_DESCRIPTION("Amlogic AXG ALSA machine driver");
373MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
374MODULE_LICENSE("GPL v2");
v6.9.4
  1// SPDX-License-Identifier: (GPL-2.0 OR MIT)
  2//
  3// Copyright (c) 2018 BayLibre, SAS.
  4// Author: Jerome Brunet <jbrunet@baylibre.com>
  5
  6#include <linux/module.h>
  7#include <linux/of_platform.h>
  8#include <sound/soc.h>
  9#include <sound/soc-dai.h>
 10
 11#include "axg-tdm.h"
 12#include "meson-card.h"
 13
 14struct axg_dai_link_tdm_mask {
 15	u32 tx;
 16	u32 rx;
 17};
 18
 19struct axg_dai_link_tdm_data {
 20	unsigned int mclk_fs;
 21	unsigned int slots;
 22	unsigned int slot_width;
 23	u32 *tx_mask;
 24	u32 *rx_mask;
 25	struct axg_dai_link_tdm_mask *codec_masks;
 26};
 27
 28/*
 29 * Base params for the codec to codec links
 30 * Those will be over-written by the CPU side of the link
 31 */
 32static const struct snd_soc_pcm_stream codec_params = {
 33	.formats = SNDRV_PCM_FMTBIT_S24_LE,
 34	.rate_min = 5525,
 35	.rate_max = 192000,
 36	.channels_min = 1,
 37	.channels_max = 8,
 38};
 39
 40static int axg_card_tdm_be_hw_params(struct snd_pcm_substream *substream,
 41				     struct snd_pcm_hw_params *params)
 42{
 43	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
 44	struct meson_card *priv = snd_soc_card_get_drvdata(rtd->card);
 45	struct axg_dai_link_tdm_data *be =
 46		(struct axg_dai_link_tdm_data *)priv->link_data[rtd->num];
 47
 48	return meson_card_i2s_set_sysclk(substream, params, be->mclk_fs);
 49}
 50
 51static const struct snd_soc_ops axg_card_tdm_be_ops = {
 52	.hw_params = axg_card_tdm_be_hw_params,
 53};
 54
 55static int axg_card_tdm_dai_init(struct snd_soc_pcm_runtime *rtd)
 56{
 57	struct meson_card *priv = snd_soc_card_get_drvdata(rtd->card);
 58	struct axg_dai_link_tdm_data *be =
 59		(struct axg_dai_link_tdm_data *)priv->link_data[rtd->num];
 60	struct snd_soc_dai *codec_dai;
 61	int ret, i;
 62
 63	for_each_rtd_codec_dais(rtd, i, codec_dai) {
 64		ret = snd_soc_dai_set_tdm_slot(codec_dai,
 65					       be->codec_masks[i].tx,
 66					       be->codec_masks[i].rx,
 67					       be->slots, be->slot_width);
 68		if (ret && ret != -ENOTSUPP) {
 69			dev_err(codec_dai->dev,
 70				"setting tdm link slots failed\n");
 71			return ret;
 72		}
 73	}
 74
 75	ret = axg_tdm_set_tdm_slots(snd_soc_rtd_to_cpu(rtd, 0), be->tx_mask, be->rx_mask,
 76				    be->slots, be->slot_width);
 77	if (ret) {
 78		dev_err(snd_soc_rtd_to_cpu(rtd, 0)->dev, "setting tdm link slots failed\n");
 79		return ret;
 80	}
 81
 82	return 0;
 83}
 84
 85static int axg_card_tdm_dai_lb_init(struct snd_soc_pcm_runtime *rtd)
 86{
 87	struct meson_card *priv = snd_soc_card_get_drvdata(rtd->card);
 88	struct axg_dai_link_tdm_data *be =
 89		(struct axg_dai_link_tdm_data *)priv->link_data[rtd->num];
 90	int ret;
 91
 92	/* The loopback rx_mask is the pad tx_mask */
 93	ret = axg_tdm_set_tdm_slots(snd_soc_rtd_to_cpu(rtd, 0), NULL, be->tx_mask,
 94				    be->slots, be->slot_width);
 95	if (ret) {
 96		dev_err(snd_soc_rtd_to_cpu(rtd, 0)->dev, "setting tdm link slots failed\n");
 97		return ret;
 98	}
 99
100	return 0;
101}
102
103static int axg_card_add_tdm_loopback(struct snd_soc_card *card,
104				     int *index)
105{
106	struct meson_card *priv = snd_soc_card_get_drvdata(card);
107	struct snd_soc_dai_link *pad = &card->dai_link[*index];
108	struct snd_soc_dai_link *lb;
109	struct snd_soc_dai_link_component *dlc;
110	int ret;
111
112	/* extend links */
113	ret = meson_card_reallocate_links(card, card->num_links + 1);
114	if (ret)
115		return ret;
116
 
117	lb = &card->dai_link[*index + 1];
118
119	lb->name = devm_kasprintf(card->dev, GFP_KERNEL, "%s-lb", pad->name);
120	if (!lb->name)
121		return -ENOMEM;
122
123	dlc = devm_kzalloc(card->dev, sizeof(*dlc), GFP_KERNEL);
124	if (!dlc)
125		return -ENOMEM;
126
127	lb->cpus = dlc;
128	lb->codecs = &snd_soc_dummy_dlc;
129	lb->num_cpus = 1;
130	lb->num_codecs = 1;
131
132	lb->stream_name = lb->name;
133	lb->cpus->of_node = pad->cpus->of_node;
134	lb->cpus->dai_name = "TDM Loopback";
135	lb->dpcm_capture = 1;
136	lb->no_pcm = 1;
137	lb->ops = &axg_card_tdm_be_ops;
138	lb->init = axg_card_tdm_dai_lb_init;
139
140	/* Provide the same link data to the loopback */
141	priv->link_data[*index + 1] = priv->link_data[*index];
142
143	/*
144	 * axg_card_clean_references() will iterate over this link,
145	 * make sure the node count is balanced
146	 */
147	of_node_get(lb->cpus->of_node);
148
149	/* Let add_links continue where it should */
150	*index += 1;
151
152	return 0;
153}
154
155static int axg_card_parse_cpu_tdm_slots(struct snd_soc_card *card,
156					struct snd_soc_dai_link *link,
157					struct device_node *node,
158					struct axg_dai_link_tdm_data *be)
159{
160	char propname[32];
161	u32 tx, rx;
162	int i;
163
164	be->tx_mask = devm_kcalloc(card->dev, AXG_TDM_NUM_LANES,
165				   sizeof(*be->tx_mask), GFP_KERNEL);
166	be->rx_mask = devm_kcalloc(card->dev, AXG_TDM_NUM_LANES,
167				   sizeof(*be->rx_mask), GFP_KERNEL);
168	if (!be->tx_mask || !be->rx_mask)
169		return -ENOMEM;
170
171	for (i = 0, tx = 0; i < AXG_TDM_NUM_LANES; i++) {
172		snprintf(propname, 32, "dai-tdm-slot-tx-mask-%d", i);
173		snd_soc_of_get_slot_mask(node, propname, &be->tx_mask[i]);
174		tx = max(tx, be->tx_mask[i]);
175	}
176
177	/* Disable playback is the interface has no tx slots */
178	if (!tx)
179		link->dpcm_playback = 0;
180
181	for (i = 0, rx = 0; i < AXG_TDM_NUM_LANES; i++) {
182		snprintf(propname, 32, "dai-tdm-slot-rx-mask-%d", i);
183		snd_soc_of_get_slot_mask(node, propname, &be->rx_mask[i]);
184		rx = max(rx, be->rx_mask[i]);
185	}
186
187	/* Disable capture is the interface has no rx slots */
188	if (!rx)
189		link->dpcm_capture = 0;
190
191	/* ... but the interface should at least have one of them */
192	if (!tx && !rx) {
193		dev_err(card->dev, "tdm link has no cpu slots\n");
194		return -EINVAL;
195	}
196
197	of_property_read_u32(node, "dai-tdm-slot-num", &be->slots);
198	if (!be->slots) {
199		/*
200		 * If the slot number is not provided, set it such as it
201		 * accommodates the largest mask
202		 */
203		be->slots = fls(max(tx, rx));
204	} else if (be->slots < fls(max(tx, rx)) || be->slots > 32) {
205		/*
206		 * Error if the slots can't accommodate the largest mask or
207		 * if it is just too big
208		 */
209		dev_err(card->dev, "bad slot number\n");
210		return -EINVAL;
211	}
212
213	of_property_read_u32(node, "dai-tdm-slot-width", &be->slot_width);
214
215	return 0;
216}
217
218static int axg_card_parse_codecs_masks(struct snd_soc_card *card,
219				       struct snd_soc_dai_link *link,
220				       struct device_node *node,
221				       struct axg_dai_link_tdm_data *be)
222{
223	struct axg_dai_link_tdm_mask *codec_mask;
224	struct device_node *np;
225
226	codec_mask = devm_kcalloc(card->dev, link->num_codecs,
227				  sizeof(*codec_mask), GFP_KERNEL);
228	if (!codec_mask)
229		return -ENOMEM;
230
231	be->codec_masks = codec_mask;
232
233	for_each_child_of_node(node, np) {
234		snd_soc_of_get_slot_mask(np, "dai-tdm-slot-rx-mask",
235					 &codec_mask->rx);
236		snd_soc_of_get_slot_mask(np, "dai-tdm-slot-tx-mask",
237					 &codec_mask->tx);
238
239		codec_mask++;
240	}
241
242	return 0;
243}
244
245static int axg_card_parse_tdm(struct snd_soc_card *card,
246			      struct device_node *node,
247			      int *index)
248{
249	struct meson_card *priv = snd_soc_card_get_drvdata(card);
250	struct snd_soc_dai_link *link = &card->dai_link[*index];
251	struct axg_dai_link_tdm_data *be;
252	int ret;
253
254	/* Allocate tdm link parameters */
255	be = devm_kzalloc(card->dev, sizeof(*be), GFP_KERNEL);
256	if (!be)
257		return -ENOMEM;
258	priv->link_data[*index] = be;
259
260	/* Setup tdm link */
261	link->ops = &axg_card_tdm_be_ops;
262	link->init = axg_card_tdm_dai_init;
263	link->dai_fmt = meson_card_parse_daifmt(node, link->cpus->of_node);
264
265	of_property_read_u32(node, "mclk-fs", &be->mclk_fs);
266
267	ret = axg_card_parse_cpu_tdm_slots(card, link, node, be);
268	if (ret) {
269		dev_err(card->dev, "error parsing tdm link slots\n");
270		return ret;
271	}
272
273	ret = axg_card_parse_codecs_masks(card, link, node, be);
274	if (ret)
275		return ret;
276
277	/* Add loopback if the pad dai has playback */
278	if (link->dpcm_playback) {
279		ret = axg_card_add_tdm_loopback(card, index);
280		if (ret)
281			return ret;
282	}
283
284	return 0;
285}
286
287static int axg_card_cpu_is_capture_fe(struct device_node *np)
288{
289	return of_device_is_compatible(np, DT_PREFIX "axg-toddr");
290}
291
292static int axg_card_cpu_is_playback_fe(struct device_node *np)
293{
294	return of_device_is_compatible(np, DT_PREFIX "axg-frddr");
295}
296
297static int axg_card_cpu_is_tdm_iface(struct device_node *np)
298{
299	return of_device_is_compatible(np, DT_PREFIX "axg-tdm-iface");
300}
301
302static int axg_card_cpu_is_codec(struct device_node *np)
303{
304	return of_device_is_compatible(np, DT_PREFIX "g12a-tohdmitx") ||
305		of_device_is_compatible(np, DT_PREFIX "g12a-toacodec");
306}
307
308static int axg_card_add_link(struct snd_soc_card *card, struct device_node *np,
309			     int *index)
310{
311	struct snd_soc_dai_link *dai_link = &card->dai_link[*index];
312	struct snd_soc_dai_link_component *cpu;
313	int ret;
314
315	cpu = devm_kzalloc(card->dev, sizeof(*cpu), GFP_KERNEL);
316	if (!cpu)
317		return -ENOMEM;
318
319	dai_link->cpus = cpu;
320	dai_link->num_cpus = 1;
321	dai_link->nonatomic = true;
322
323	ret = meson_card_parse_dai(card, np, dai_link->cpus);
324	if (ret)
325		return ret;
326
327	if (axg_card_cpu_is_playback_fe(dai_link->cpus->of_node))
328		return meson_card_set_fe_link(card, dai_link, np, true);
329	else if (axg_card_cpu_is_capture_fe(dai_link->cpus->of_node))
330		return meson_card_set_fe_link(card, dai_link, np, false);
331
332
333	ret = meson_card_set_be_link(card, dai_link, np);
334	if (ret)
335		return ret;
336
337	if (axg_card_cpu_is_codec(dai_link->cpus->of_node)) {
338		dai_link->c2c_params = &codec_params;
339		dai_link->num_c2c_params = 1;
340	} else {
341		dai_link->no_pcm = 1;
342		snd_soc_dai_link_set_capabilities(dai_link);
343		if (axg_card_cpu_is_tdm_iface(dai_link->cpus->of_node))
344			ret = axg_card_parse_tdm(card, np, index);
345	}
346
347	return ret;
348}
349
350static const struct meson_card_match_data axg_card_match_data = {
351	.add_link = axg_card_add_link,
352};
353
354static const struct of_device_id axg_card_of_match[] = {
355	{
356		.compatible = "amlogic,axg-sound-card",
357		.data = &axg_card_match_data,
358	}, {}
359};
360MODULE_DEVICE_TABLE(of, axg_card_of_match);
361
362static struct platform_driver axg_card_pdrv = {
363	.probe = meson_card_probe,
364	.remove_new = meson_card_remove,
365	.driver = {
366		.name = "axg-sound-card",
367		.of_match_table = axg_card_of_match,
368	},
369};
370module_platform_driver(axg_card_pdrv);
371
372MODULE_DESCRIPTION("Amlogic AXG ALSA machine driver");
373MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
374MODULE_LICENSE("GPL v2");