162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * ALSA SoC using the QUICC Multichannel Controller (QMC)
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright 2022 CS GROUP France
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Author: Herve Codina <herve.codina@bootlin.com>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/dma-mapping.h>
1162306a36Sopenharmony_ci#include <linux/module.h>
1262306a36Sopenharmony_ci#include <linux/of.h>
1362306a36Sopenharmony_ci#include <linux/of_platform.h>
1462306a36Sopenharmony_ci#include <linux/platform_device.h>
1562306a36Sopenharmony_ci#include <linux/slab.h>
1662306a36Sopenharmony_ci#include <soc/fsl/qe/qmc.h>
1762306a36Sopenharmony_ci#include <sound/pcm_params.h>
1862306a36Sopenharmony_ci#include <sound/soc.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_cistruct qmc_dai {
2162306a36Sopenharmony_ci	char *name;
2262306a36Sopenharmony_ci	int id;
2362306a36Sopenharmony_ci	struct device *dev;
2462306a36Sopenharmony_ci	struct qmc_chan *qmc_chan;
2562306a36Sopenharmony_ci	unsigned int nb_tx_ts;
2662306a36Sopenharmony_ci	unsigned int nb_rx_ts;
2762306a36Sopenharmony_ci};
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_cistruct qmc_audio {
3062306a36Sopenharmony_ci	struct device *dev;
3162306a36Sopenharmony_ci	unsigned int num_dais;
3262306a36Sopenharmony_ci	struct qmc_dai *dais;
3362306a36Sopenharmony_ci	struct snd_soc_dai_driver *dai_drivers;
3462306a36Sopenharmony_ci};
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_cistruct qmc_dai_prtd {
3762306a36Sopenharmony_ci	struct qmc_dai *qmc_dai;
3862306a36Sopenharmony_ci	dma_addr_t dma_buffer_start;
3962306a36Sopenharmony_ci	dma_addr_t period_ptr_submitted;
4062306a36Sopenharmony_ci	dma_addr_t period_ptr_ended;
4162306a36Sopenharmony_ci	dma_addr_t dma_buffer_end;
4262306a36Sopenharmony_ci	size_t period_size;
4362306a36Sopenharmony_ci	struct snd_pcm_substream *substream;
4462306a36Sopenharmony_ci};
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_cistatic int qmc_audio_pcm_construct(struct snd_soc_component *component,
4762306a36Sopenharmony_ci				   struct snd_soc_pcm_runtime *rtd)
4862306a36Sopenharmony_ci{
4962306a36Sopenharmony_ci	struct snd_card *card = rtd->card->snd_card;
5062306a36Sopenharmony_ci	int ret;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32));
5362306a36Sopenharmony_ci	if (ret)
5462306a36Sopenharmony_ci		return ret;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV, card->dev,
5762306a36Sopenharmony_ci				       64*1024, 64*1024);
5862306a36Sopenharmony_ci	return 0;
5962306a36Sopenharmony_ci}
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_cistatic int qmc_audio_pcm_hw_params(struct snd_soc_component *component,
6262306a36Sopenharmony_ci				   struct snd_pcm_substream *substream,
6362306a36Sopenharmony_ci				   struct snd_pcm_hw_params *params)
6462306a36Sopenharmony_ci{
6562306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
6662306a36Sopenharmony_ci	struct qmc_dai_prtd *prtd = substream->runtime->private_data;
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	prtd->dma_buffer_start = runtime->dma_addr;
6962306a36Sopenharmony_ci	prtd->dma_buffer_end = runtime->dma_addr + params_buffer_bytes(params);
7062306a36Sopenharmony_ci	prtd->period_size = params_period_bytes(params);
7162306a36Sopenharmony_ci	prtd->period_ptr_submitted = prtd->dma_buffer_start;
7262306a36Sopenharmony_ci	prtd->period_ptr_ended = prtd->dma_buffer_start;
7362306a36Sopenharmony_ci	prtd->substream = substream;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	return 0;
7662306a36Sopenharmony_ci}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_cistatic void qmc_audio_pcm_write_complete(void *context)
7962306a36Sopenharmony_ci{
8062306a36Sopenharmony_ci	struct qmc_dai_prtd *prtd = context;
8162306a36Sopenharmony_ci	int ret;
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	prtd->period_ptr_ended += prtd->period_size;
8462306a36Sopenharmony_ci	if (prtd->period_ptr_ended >= prtd->dma_buffer_end)
8562306a36Sopenharmony_ci		prtd->period_ptr_ended = prtd->dma_buffer_start;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	prtd->period_ptr_submitted += prtd->period_size;
8862306a36Sopenharmony_ci	if (prtd->period_ptr_submitted >= prtd->dma_buffer_end)
8962306a36Sopenharmony_ci		prtd->period_ptr_submitted = prtd->dma_buffer_start;
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	ret = qmc_chan_write_submit(prtd->qmc_dai->qmc_chan,
9262306a36Sopenharmony_ci		prtd->period_ptr_submitted, prtd->period_size,
9362306a36Sopenharmony_ci		qmc_audio_pcm_write_complete, prtd);
9462306a36Sopenharmony_ci	if (ret) {
9562306a36Sopenharmony_ci		dev_err(prtd->qmc_dai->dev, "write_submit failed %d\n",
9662306a36Sopenharmony_ci			ret);
9762306a36Sopenharmony_ci	}
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	snd_pcm_period_elapsed(prtd->substream);
10062306a36Sopenharmony_ci}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_cistatic void qmc_audio_pcm_read_complete(void *context, size_t length)
10362306a36Sopenharmony_ci{
10462306a36Sopenharmony_ci	struct qmc_dai_prtd *prtd = context;
10562306a36Sopenharmony_ci	int ret;
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	if (length != prtd->period_size) {
10862306a36Sopenharmony_ci		dev_err(prtd->qmc_dai->dev, "read complete length = %zu, exp %zu\n",
10962306a36Sopenharmony_ci			length, prtd->period_size);
11062306a36Sopenharmony_ci	}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	prtd->period_ptr_ended += prtd->period_size;
11362306a36Sopenharmony_ci	if (prtd->period_ptr_ended >= prtd->dma_buffer_end)
11462306a36Sopenharmony_ci		prtd->period_ptr_ended = prtd->dma_buffer_start;
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	prtd->period_ptr_submitted += prtd->period_size;
11762306a36Sopenharmony_ci	if (prtd->period_ptr_submitted >= prtd->dma_buffer_end)
11862306a36Sopenharmony_ci		prtd->period_ptr_submitted = prtd->dma_buffer_start;
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	ret = qmc_chan_read_submit(prtd->qmc_dai->qmc_chan,
12162306a36Sopenharmony_ci		prtd->period_ptr_submitted, prtd->period_size,
12262306a36Sopenharmony_ci		qmc_audio_pcm_read_complete, prtd);
12362306a36Sopenharmony_ci	if (ret) {
12462306a36Sopenharmony_ci		dev_err(prtd->qmc_dai->dev, "read_submit failed %d\n",
12562306a36Sopenharmony_ci			ret);
12662306a36Sopenharmony_ci	}
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	snd_pcm_period_elapsed(prtd->substream);
12962306a36Sopenharmony_ci}
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_cistatic int qmc_audio_pcm_trigger(struct snd_soc_component *component,
13262306a36Sopenharmony_ci				 struct snd_pcm_substream *substream, int cmd)
13362306a36Sopenharmony_ci{
13462306a36Sopenharmony_ci	struct qmc_dai_prtd *prtd = substream->runtime->private_data;
13562306a36Sopenharmony_ci	int ret;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	if (!prtd->qmc_dai) {
13862306a36Sopenharmony_ci		dev_err(component->dev, "qmc_dai is not set\n");
13962306a36Sopenharmony_ci		return -EINVAL;
14062306a36Sopenharmony_ci	}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	switch (cmd) {
14362306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
14462306a36Sopenharmony_ci		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
14562306a36Sopenharmony_ci			/* Submit first chunk ... */
14662306a36Sopenharmony_ci			ret = qmc_chan_write_submit(prtd->qmc_dai->qmc_chan,
14762306a36Sopenharmony_ci				prtd->period_ptr_submitted, prtd->period_size,
14862306a36Sopenharmony_ci				qmc_audio_pcm_write_complete, prtd);
14962306a36Sopenharmony_ci			if (ret) {
15062306a36Sopenharmony_ci				dev_err(component->dev, "write_submit failed %d\n",
15162306a36Sopenharmony_ci					ret);
15262306a36Sopenharmony_ci				return ret;
15362306a36Sopenharmony_ci			}
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci			/* ... prepare next one ... */
15662306a36Sopenharmony_ci			prtd->period_ptr_submitted += prtd->period_size;
15762306a36Sopenharmony_ci			if (prtd->period_ptr_submitted >= prtd->dma_buffer_end)
15862306a36Sopenharmony_ci				prtd->period_ptr_submitted = prtd->dma_buffer_start;
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci			/* ... and send it */
16162306a36Sopenharmony_ci			ret = qmc_chan_write_submit(prtd->qmc_dai->qmc_chan,
16262306a36Sopenharmony_ci				prtd->period_ptr_submitted, prtd->period_size,
16362306a36Sopenharmony_ci				qmc_audio_pcm_write_complete, prtd);
16462306a36Sopenharmony_ci			if (ret) {
16562306a36Sopenharmony_ci				dev_err(component->dev, "write_submit failed %d\n",
16662306a36Sopenharmony_ci					ret);
16762306a36Sopenharmony_ci				return ret;
16862306a36Sopenharmony_ci			}
16962306a36Sopenharmony_ci		} else {
17062306a36Sopenharmony_ci			/* Submit first chunk ... */
17162306a36Sopenharmony_ci			ret = qmc_chan_read_submit(prtd->qmc_dai->qmc_chan,
17262306a36Sopenharmony_ci				prtd->period_ptr_submitted, prtd->period_size,
17362306a36Sopenharmony_ci				qmc_audio_pcm_read_complete, prtd);
17462306a36Sopenharmony_ci			if (ret) {
17562306a36Sopenharmony_ci				dev_err(component->dev, "read_submit failed %d\n",
17662306a36Sopenharmony_ci					ret);
17762306a36Sopenharmony_ci				return ret;
17862306a36Sopenharmony_ci			}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci			/* ... prepare next one ... */
18162306a36Sopenharmony_ci			prtd->period_ptr_submitted += prtd->period_size;
18262306a36Sopenharmony_ci			if (prtd->period_ptr_submitted >= prtd->dma_buffer_end)
18362306a36Sopenharmony_ci				prtd->period_ptr_submitted = prtd->dma_buffer_start;
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci			/* ... and send it */
18662306a36Sopenharmony_ci			ret = qmc_chan_read_submit(prtd->qmc_dai->qmc_chan,
18762306a36Sopenharmony_ci				prtd->period_ptr_submitted, prtd->period_size,
18862306a36Sopenharmony_ci				qmc_audio_pcm_read_complete, prtd);
18962306a36Sopenharmony_ci			if (ret) {
19062306a36Sopenharmony_ci				dev_err(component->dev, "write_submit failed %d\n",
19162306a36Sopenharmony_ci					ret);
19262306a36Sopenharmony_ci				return ret;
19362306a36Sopenharmony_ci			}
19462306a36Sopenharmony_ci		}
19562306a36Sopenharmony_ci		break;
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_RESUME:
19862306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
19962306a36Sopenharmony_ci		break;
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
20262306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_SUSPEND:
20362306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
20462306a36Sopenharmony_ci		break;
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	default:
20762306a36Sopenharmony_ci		return -EINVAL;
20862306a36Sopenharmony_ci	}
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	return 0;
21162306a36Sopenharmony_ci}
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_cistatic snd_pcm_uframes_t qmc_audio_pcm_pointer(struct snd_soc_component *component,
21462306a36Sopenharmony_ci					       struct snd_pcm_substream *substream)
21562306a36Sopenharmony_ci{
21662306a36Sopenharmony_ci	struct qmc_dai_prtd *prtd = substream->runtime->private_data;
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	return bytes_to_frames(substream->runtime,
21962306a36Sopenharmony_ci			       prtd->period_ptr_ended - prtd->dma_buffer_start);
22062306a36Sopenharmony_ci}
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_cistatic int qmc_audio_of_xlate_dai_name(struct snd_soc_component *component,
22362306a36Sopenharmony_ci					const struct of_phandle_args *args,
22462306a36Sopenharmony_ci					const char **dai_name)
22562306a36Sopenharmony_ci{
22662306a36Sopenharmony_ci	struct qmc_audio *qmc_audio = dev_get_drvdata(component->dev);
22762306a36Sopenharmony_ci	struct snd_soc_dai_driver *dai_driver;
22862306a36Sopenharmony_ci	int id = args->args[0];
22962306a36Sopenharmony_ci	int i;
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	for (i = 0; i  < qmc_audio->num_dais; i++) {
23262306a36Sopenharmony_ci		dai_driver = qmc_audio->dai_drivers + i;
23362306a36Sopenharmony_ci		if (dai_driver->id == id) {
23462306a36Sopenharmony_ci			*dai_name = dai_driver->name;
23562306a36Sopenharmony_ci			return 0;
23662306a36Sopenharmony_ci		}
23762306a36Sopenharmony_ci	}
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	return -EINVAL;
24062306a36Sopenharmony_ci}
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_cistatic const struct snd_pcm_hardware qmc_audio_pcm_hardware = {
24362306a36Sopenharmony_ci	.info			= SNDRV_PCM_INFO_MMAP |
24462306a36Sopenharmony_ci				  SNDRV_PCM_INFO_MMAP_VALID |
24562306a36Sopenharmony_ci				  SNDRV_PCM_INFO_INTERLEAVED |
24662306a36Sopenharmony_ci				  SNDRV_PCM_INFO_PAUSE,
24762306a36Sopenharmony_ci	.period_bytes_min	= 32,
24862306a36Sopenharmony_ci	.period_bytes_max	= 64*1024,
24962306a36Sopenharmony_ci	.periods_min		= 2,
25062306a36Sopenharmony_ci	.periods_max		= 2*1024,
25162306a36Sopenharmony_ci	.buffer_bytes_max	= 64*1024,
25262306a36Sopenharmony_ci};
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_cistatic int qmc_audio_pcm_open(struct snd_soc_component *component,
25562306a36Sopenharmony_ci			      struct snd_pcm_substream *substream)
25662306a36Sopenharmony_ci{
25762306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
25862306a36Sopenharmony_ci	struct qmc_dai_prtd *prtd;
25962306a36Sopenharmony_ci	int ret;
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	snd_soc_set_runtime_hwparams(substream, &qmc_audio_pcm_hardware);
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	/* ensure that buffer size is a multiple of period size */
26462306a36Sopenharmony_ci	ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
26562306a36Sopenharmony_ci	if (ret < 0)
26662306a36Sopenharmony_ci		return ret;
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	prtd = kzalloc(sizeof(*prtd), GFP_KERNEL);
26962306a36Sopenharmony_ci	if (prtd == NULL)
27062306a36Sopenharmony_ci		return -ENOMEM;
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	runtime->private_data = prtd;
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	return 0;
27562306a36Sopenharmony_ci}
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_cistatic int qmc_audio_pcm_close(struct snd_soc_component *component,
27862306a36Sopenharmony_ci			       struct snd_pcm_substream *substream)
27962306a36Sopenharmony_ci{
28062306a36Sopenharmony_ci	struct qmc_dai_prtd *prtd = substream->runtime->private_data;
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	kfree(prtd);
28362306a36Sopenharmony_ci	return 0;
28462306a36Sopenharmony_ci}
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_cistatic const struct snd_soc_component_driver qmc_audio_soc_platform = {
28762306a36Sopenharmony_ci	.open			= qmc_audio_pcm_open,
28862306a36Sopenharmony_ci	.close			= qmc_audio_pcm_close,
28962306a36Sopenharmony_ci	.hw_params		= qmc_audio_pcm_hw_params,
29062306a36Sopenharmony_ci	.trigger		= qmc_audio_pcm_trigger,
29162306a36Sopenharmony_ci	.pointer		= qmc_audio_pcm_pointer,
29262306a36Sopenharmony_ci	.pcm_construct		= qmc_audio_pcm_construct,
29362306a36Sopenharmony_ci	.of_xlate_dai_name	= qmc_audio_of_xlate_dai_name,
29462306a36Sopenharmony_ci};
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_cistatic unsigned int qmc_dai_get_index(struct snd_soc_dai *dai)
29762306a36Sopenharmony_ci{
29862306a36Sopenharmony_ci	struct qmc_audio *qmc_audio = snd_soc_dai_get_drvdata(dai);
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	return dai->driver - qmc_audio->dai_drivers;
30162306a36Sopenharmony_ci}
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_cistatic struct qmc_dai *qmc_dai_get_data(struct snd_soc_dai *dai)
30462306a36Sopenharmony_ci{
30562306a36Sopenharmony_ci	struct qmc_audio *qmc_audio = snd_soc_dai_get_drvdata(dai);
30662306a36Sopenharmony_ci	unsigned int index;
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	index = qmc_dai_get_index(dai);
30962306a36Sopenharmony_ci	if (index > qmc_audio->num_dais)
31062306a36Sopenharmony_ci		return NULL;
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	return qmc_audio->dais + index;
31362306a36Sopenharmony_ci}
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci/*
31662306a36Sopenharmony_ci * The constraints for format/channel is to match with the number of 8bit
31762306a36Sopenharmony_ci * time-slots available.
31862306a36Sopenharmony_ci */
31962306a36Sopenharmony_cistatic int qmc_dai_hw_rule_channels_by_format(struct qmc_dai *qmc_dai,
32062306a36Sopenharmony_ci					      struct snd_pcm_hw_params *params,
32162306a36Sopenharmony_ci					      unsigned int nb_ts)
32262306a36Sopenharmony_ci{
32362306a36Sopenharmony_ci	struct snd_interval *c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
32462306a36Sopenharmony_ci	snd_pcm_format_t format = params_format(params);
32562306a36Sopenharmony_ci	struct snd_interval ch = {0};
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	switch (snd_pcm_format_physical_width(format)) {
32862306a36Sopenharmony_ci	case 8:
32962306a36Sopenharmony_ci		ch.max = nb_ts;
33062306a36Sopenharmony_ci		break;
33162306a36Sopenharmony_ci	case 16:
33262306a36Sopenharmony_ci		ch.max = nb_ts/2;
33362306a36Sopenharmony_ci		break;
33462306a36Sopenharmony_ci	case 32:
33562306a36Sopenharmony_ci		ch.max = nb_ts/4;
33662306a36Sopenharmony_ci		break;
33762306a36Sopenharmony_ci	case 64:
33862306a36Sopenharmony_ci		ch.max = nb_ts/8;
33962306a36Sopenharmony_ci		break;
34062306a36Sopenharmony_ci	default:
34162306a36Sopenharmony_ci		dev_err(qmc_dai->dev, "format physical width %u not supported\n",
34262306a36Sopenharmony_ci			snd_pcm_format_physical_width(format));
34362306a36Sopenharmony_ci		return -EINVAL;
34462306a36Sopenharmony_ci	}
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	ch.min = ch.max ? 1 : 0;
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	return snd_interval_refine(c, &ch);
34962306a36Sopenharmony_ci}
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_cistatic int qmc_dai_hw_rule_playback_channels_by_format(struct snd_pcm_hw_params *params,
35262306a36Sopenharmony_ci						       struct snd_pcm_hw_rule *rule)
35362306a36Sopenharmony_ci{
35462306a36Sopenharmony_ci	struct qmc_dai *qmc_dai = rule->private;
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	return qmc_dai_hw_rule_channels_by_format(qmc_dai, params, qmc_dai->nb_tx_ts);
35762306a36Sopenharmony_ci}
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_cistatic int qmc_dai_hw_rule_capture_channels_by_format(
36062306a36Sopenharmony_ci			struct snd_pcm_hw_params *params,
36162306a36Sopenharmony_ci			struct snd_pcm_hw_rule *rule)
36262306a36Sopenharmony_ci{
36362306a36Sopenharmony_ci	struct qmc_dai *qmc_dai = rule->private;
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	return qmc_dai_hw_rule_channels_by_format(qmc_dai, params, qmc_dai->nb_rx_ts);
36662306a36Sopenharmony_ci}
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_cistatic int qmc_dai_hw_rule_format_by_channels(struct qmc_dai *qmc_dai,
36962306a36Sopenharmony_ci					      struct snd_pcm_hw_params *params,
37062306a36Sopenharmony_ci					      unsigned int nb_ts)
37162306a36Sopenharmony_ci{
37262306a36Sopenharmony_ci	struct snd_mask *f_old = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
37362306a36Sopenharmony_ci	unsigned int channels = params_channels(params);
37462306a36Sopenharmony_ci	unsigned int slot_width;
37562306a36Sopenharmony_ci	snd_pcm_format_t format;
37662306a36Sopenharmony_ci	struct snd_mask f_new;
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	if (!channels || channels > nb_ts) {
37962306a36Sopenharmony_ci		dev_err(qmc_dai->dev, "channels %u not supported\n",
38062306a36Sopenharmony_ci			nb_ts);
38162306a36Sopenharmony_ci		return -EINVAL;
38262306a36Sopenharmony_ci	}
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	slot_width = (nb_ts / channels) * 8;
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	snd_mask_none(&f_new);
38762306a36Sopenharmony_ci	pcm_for_each_format(format) {
38862306a36Sopenharmony_ci		if (snd_mask_test_format(f_old, format)) {
38962306a36Sopenharmony_ci			if (snd_pcm_format_physical_width(format) <= slot_width)
39062306a36Sopenharmony_ci				snd_mask_set_format(&f_new, format);
39162306a36Sopenharmony_ci		}
39262306a36Sopenharmony_ci	}
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	return snd_mask_refine(f_old, &f_new);
39562306a36Sopenharmony_ci}
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_cistatic int qmc_dai_hw_rule_playback_format_by_channels(
39862306a36Sopenharmony_ci			struct snd_pcm_hw_params *params,
39962306a36Sopenharmony_ci			struct snd_pcm_hw_rule *rule)
40062306a36Sopenharmony_ci{
40162306a36Sopenharmony_ci	struct qmc_dai *qmc_dai = rule->private;
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	return qmc_dai_hw_rule_format_by_channels(qmc_dai, params, qmc_dai->nb_tx_ts);
40462306a36Sopenharmony_ci}
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_cistatic int qmc_dai_hw_rule_capture_format_by_channels(
40762306a36Sopenharmony_ci			struct snd_pcm_hw_params *params,
40862306a36Sopenharmony_ci			struct snd_pcm_hw_rule *rule)
40962306a36Sopenharmony_ci{
41062306a36Sopenharmony_ci	struct qmc_dai *qmc_dai = rule->private;
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci	return qmc_dai_hw_rule_format_by_channels(qmc_dai, params, qmc_dai->nb_rx_ts);
41362306a36Sopenharmony_ci}
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_cistatic int qmc_dai_startup(struct snd_pcm_substream *substream,
41662306a36Sopenharmony_ci			     struct snd_soc_dai *dai)
41762306a36Sopenharmony_ci{
41862306a36Sopenharmony_ci	struct qmc_dai_prtd *prtd = substream->runtime->private_data;
41962306a36Sopenharmony_ci	snd_pcm_hw_rule_func_t hw_rule_channels_by_format;
42062306a36Sopenharmony_ci	snd_pcm_hw_rule_func_t hw_rule_format_by_channels;
42162306a36Sopenharmony_ci	struct qmc_dai *qmc_dai;
42262306a36Sopenharmony_ci	unsigned int frame_bits;
42362306a36Sopenharmony_ci	int ret;
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	qmc_dai = qmc_dai_get_data(dai);
42662306a36Sopenharmony_ci	if (!qmc_dai) {
42762306a36Sopenharmony_ci		dev_err(dai->dev, "Invalid dai\n");
42862306a36Sopenharmony_ci		return -EINVAL;
42962306a36Sopenharmony_ci	}
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	prtd->qmc_dai = qmc_dai;
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
43462306a36Sopenharmony_ci		hw_rule_channels_by_format = qmc_dai_hw_rule_capture_channels_by_format;
43562306a36Sopenharmony_ci		hw_rule_format_by_channels = qmc_dai_hw_rule_capture_format_by_channels;
43662306a36Sopenharmony_ci		frame_bits = qmc_dai->nb_rx_ts * 8;
43762306a36Sopenharmony_ci	} else {
43862306a36Sopenharmony_ci		hw_rule_channels_by_format = qmc_dai_hw_rule_playback_channels_by_format;
43962306a36Sopenharmony_ci		hw_rule_format_by_channels = qmc_dai_hw_rule_playback_format_by_channels;
44062306a36Sopenharmony_ci		frame_bits = qmc_dai->nb_tx_ts * 8;
44162306a36Sopenharmony_ci	}
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	ret = snd_pcm_hw_rule_add(substream->runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
44462306a36Sopenharmony_ci				  hw_rule_channels_by_format, qmc_dai,
44562306a36Sopenharmony_ci				  SNDRV_PCM_HW_PARAM_FORMAT, -1);
44662306a36Sopenharmony_ci	if (ret) {
44762306a36Sopenharmony_ci		dev_err(dai->dev, "Failed to add channels rule (%d)\n", ret);
44862306a36Sopenharmony_ci		return ret;
44962306a36Sopenharmony_ci	}
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	ret = snd_pcm_hw_rule_add(substream->runtime, 0,  SNDRV_PCM_HW_PARAM_FORMAT,
45262306a36Sopenharmony_ci				  hw_rule_format_by_channels, qmc_dai,
45362306a36Sopenharmony_ci				  SNDRV_PCM_HW_PARAM_CHANNELS, -1);
45462306a36Sopenharmony_ci	if (ret) {
45562306a36Sopenharmony_ci		dev_err(dai->dev, "Failed to add format rule (%d)\n", ret);
45662306a36Sopenharmony_ci		return ret;
45762306a36Sopenharmony_ci	}
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	ret = snd_pcm_hw_constraint_single(substream->runtime,
46062306a36Sopenharmony_ci					   SNDRV_PCM_HW_PARAM_FRAME_BITS,
46162306a36Sopenharmony_ci					   frame_bits);
46262306a36Sopenharmony_ci	if (ret < 0) {
46362306a36Sopenharmony_ci		dev_err(dai->dev, "Failed to add frame_bits constraint (%d)\n", ret);
46462306a36Sopenharmony_ci		return ret;
46562306a36Sopenharmony_ci	}
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	return 0;
46862306a36Sopenharmony_ci}
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_cistatic int qmc_dai_hw_params(struct snd_pcm_substream *substream,
47162306a36Sopenharmony_ci			     struct snd_pcm_hw_params *params,
47262306a36Sopenharmony_ci			     struct snd_soc_dai *dai)
47362306a36Sopenharmony_ci{
47462306a36Sopenharmony_ci	struct qmc_chan_param chan_param = {0};
47562306a36Sopenharmony_ci	struct qmc_dai *qmc_dai;
47662306a36Sopenharmony_ci	int ret;
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci	qmc_dai = qmc_dai_get_data(dai);
47962306a36Sopenharmony_ci	if (!qmc_dai) {
48062306a36Sopenharmony_ci		dev_err(dai->dev, "Invalid dai\n");
48162306a36Sopenharmony_ci		return -EINVAL;
48262306a36Sopenharmony_ci	}
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
48562306a36Sopenharmony_ci		chan_param.mode = QMC_TRANSPARENT;
48662306a36Sopenharmony_ci		chan_param.transp.max_rx_buf_size = params_period_bytes(params);
48762306a36Sopenharmony_ci		ret = qmc_chan_set_param(qmc_dai->qmc_chan, &chan_param);
48862306a36Sopenharmony_ci		if (ret) {
48962306a36Sopenharmony_ci			dev_err(dai->dev, "set param failed %d\n",
49062306a36Sopenharmony_ci				ret);
49162306a36Sopenharmony_ci			return ret;
49262306a36Sopenharmony_ci		}
49362306a36Sopenharmony_ci	}
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	return 0;
49662306a36Sopenharmony_ci}
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_cistatic int qmc_dai_trigger(struct snd_pcm_substream *substream, int cmd,
49962306a36Sopenharmony_ci			   struct snd_soc_dai *dai)
50062306a36Sopenharmony_ci{
50162306a36Sopenharmony_ci	struct qmc_dai *qmc_dai;
50262306a36Sopenharmony_ci	int direction;
50362306a36Sopenharmony_ci	int ret;
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci	qmc_dai = qmc_dai_get_data(dai);
50662306a36Sopenharmony_ci	if (!qmc_dai) {
50762306a36Sopenharmony_ci		dev_err(dai->dev, "Invalid dai\n");
50862306a36Sopenharmony_ci		return -EINVAL;
50962306a36Sopenharmony_ci	}
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci	direction = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
51262306a36Sopenharmony_ci		    QMC_CHAN_WRITE : QMC_CHAN_READ;
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	switch (cmd) {
51562306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
51662306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_RESUME:
51762306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
51862306a36Sopenharmony_ci		ret = qmc_chan_start(qmc_dai->qmc_chan, direction);
51962306a36Sopenharmony_ci		if (ret)
52062306a36Sopenharmony_ci			return ret;
52162306a36Sopenharmony_ci		break;
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
52462306a36Sopenharmony_ci		ret = qmc_chan_stop(qmc_dai->qmc_chan, direction);
52562306a36Sopenharmony_ci		if (ret)
52662306a36Sopenharmony_ci			return ret;
52762306a36Sopenharmony_ci		ret = qmc_chan_reset(qmc_dai->qmc_chan, direction);
52862306a36Sopenharmony_ci		if (ret)
52962306a36Sopenharmony_ci			return ret;
53062306a36Sopenharmony_ci		break;
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_SUSPEND:
53362306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
53462306a36Sopenharmony_ci		ret = qmc_chan_stop(qmc_dai->qmc_chan, direction);
53562306a36Sopenharmony_ci		if (ret)
53662306a36Sopenharmony_ci			return ret;
53762306a36Sopenharmony_ci		break;
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci	default:
54062306a36Sopenharmony_ci		return -EINVAL;
54162306a36Sopenharmony_ci	}
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	return 0;
54462306a36Sopenharmony_ci}
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_cistatic const struct snd_soc_dai_ops qmc_dai_ops = {
54762306a36Sopenharmony_ci	.startup	= qmc_dai_startup,
54862306a36Sopenharmony_ci	.trigger	= qmc_dai_trigger,
54962306a36Sopenharmony_ci	.hw_params	= qmc_dai_hw_params,
55062306a36Sopenharmony_ci};
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_cistatic u64 qmc_audio_formats(u8 nb_ts)
55362306a36Sopenharmony_ci{
55462306a36Sopenharmony_ci	unsigned int format_width;
55562306a36Sopenharmony_ci	unsigned int chan_width;
55662306a36Sopenharmony_ci	snd_pcm_format_t format;
55762306a36Sopenharmony_ci	u64 formats_mask;
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	if (!nb_ts)
56062306a36Sopenharmony_ci		return 0;
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci	formats_mask = 0;
56362306a36Sopenharmony_ci	chan_width = nb_ts * 8;
56462306a36Sopenharmony_ci	pcm_for_each_format(format) {
56562306a36Sopenharmony_ci		/*
56662306a36Sopenharmony_ci		 * Support format other than little-endian (ie big-endian or
56762306a36Sopenharmony_ci		 * without endianness such as 8bit formats)
56862306a36Sopenharmony_ci		 */
56962306a36Sopenharmony_ci		if (snd_pcm_format_little_endian(format) == 1)
57062306a36Sopenharmony_ci			continue;
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci		/* Support physical width multiple of 8bit */
57362306a36Sopenharmony_ci		format_width = snd_pcm_format_physical_width(format);
57462306a36Sopenharmony_ci		if (format_width == 0 || format_width % 8)
57562306a36Sopenharmony_ci			continue;
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci		/*
57862306a36Sopenharmony_ci		 * And support physical width that can fit N times in the
57962306a36Sopenharmony_ci		 * channel
58062306a36Sopenharmony_ci		 */
58162306a36Sopenharmony_ci		if (format_width > chan_width || chan_width % format_width)
58262306a36Sopenharmony_ci			continue;
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci		formats_mask |= pcm_format_to_bits(format);
58562306a36Sopenharmony_ci	}
58662306a36Sopenharmony_ci	return formats_mask;
58762306a36Sopenharmony_ci}
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_cistatic int qmc_audio_dai_parse(struct qmc_audio *qmc_audio, struct device_node *np,
59062306a36Sopenharmony_ci	struct qmc_dai *qmc_dai, struct snd_soc_dai_driver *qmc_soc_dai_driver)
59162306a36Sopenharmony_ci{
59262306a36Sopenharmony_ci	struct qmc_chan_info info;
59362306a36Sopenharmony_ci	u32 val;
59462306a36Sopenharmony_ci	int ret;
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci	qmc_dai->dev = qmc_audio->dev;
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci	ret = of_property_read_u32(np, "reg", &val);
59962306a36Sopenharmony_ci	if (ret) {
60062306a36Sopenharmony_ci		dev_err(qmc_audio->dev, "%pOF: failed to read reg\n", np);
60162306a36Sopenharmony_ci		return ret;
60262306a36Sopenharmony_ci	}
60362306a36Sopenharmony_ci	qmc_dai->id = val;
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci	qmc_dai->name = devm_kasprintf(qmc_audio->dev, GFP_KERNEL, "%s.%d",
60662306a36Sopenharmony_ci				       np->parent->name, qmc_dai->id);
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci	qmc_dai->qmc_chan = devm_qmc_chan_get_byphandle(qmc_audio->dev, np,
60962306a36Sopenharmony_ci							"fsl,qmc-chan");
61062306a36Sopenharmony_ci	if (IS_ERR(qmc_dai->qmc_chan)) {
61162306a36Sopenharmony_ci		ret = PTR_ERR(qmc_dai->qmc_chan);
61262306a36Sopenharmony_ci		return dev_err_probe(qmc_audio->dev, ret,
61362306a36Sopenharmony_ci				     "dai %d get QMC channel failed\n", qmc_dai->id);
61462306a36Sopenharmony_ci	}
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci	qmc_soc_dai_driver->id = qmc_dai->id;
61762306a36Sopenharmony_ci	qmc_soc_dai_driver->name = qmc_dai->name;
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci	ret = qmc_chan_get_info(qmc_dai->qmc_chan, &info);
62062306a36Sopenharmony_ci	if (ret) {
62162306a36Sopenharmony_ci		dev_err(qmc_audio->dev, "dai %d get QMC channel info failed %d\n",
62262306a36Sopenharmony_ci			qmc_dai->id, ret);
62362306a36Sopenharmony_ci		return ret;
62462306a36Sopenharmony_ci	}
62562306a36Sopenharmony_ci	dev_info(qmc_audio->dev, "dai %d QMC channel mode %d, nb_tx_ts %u, nb_rx_ts %u\n",
62662306a36Sopenharmony_ci		 qmc_dai->id, info.mode, info.nb_tx_ts, info.nb_rx_ts);
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci	if (info.mode != QMC_TRANSPARENT) {
62962306a36Sopenharmony_ci		dev_err(qmc_audio->dev, "dai %d QMC chan mode %d is not QMC_TRANSPARENT\n",
63062306a36Sopenharmony_ci			qmc_dai->id, info.mode);
63162306a36Sopenharmony_ci		return -EINVAL;
63262306a36Sopenharmony_ci	}
63362306a36Sopenharmony_ci	qmc_dai->nb_tx_ts = info.nb_tx_ts;
63462306a36Sopenharmony_ci	qmc_dai->nb_rx_ts = info.nb_rx_ts;
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci	qmc_soc_dai_driver->playback.channels_min = 0;
63762306a36Sopenharmony_ci	qmc_soc_dai_driver->playback.channels_max = 0;
63862306a36Sopenharmony_ci	if (qmc_dai->nb_tx_ts) {
63962306a36Sopenharmony_ci		qmc_soc_dai_driver->playback.channels_min = 1;
64062306a36Sopenharmony_ci		qmc_soc_dai_driver->playback.channels_max = qmc_dai->nb_tx_ts;
64162306a36Sopenharmony_ci	}
64262306a36Sopenharmony_ci	qmc_soc_dai_driver->playback.formats = qmc_audio_formats(qmc_dai->nb_tx_ts);
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci	qmc_soc_dai_driver->capture.channels_min = 0;
64562306a36Sopenharmony_ci	qmc_soc_dai_driver->capture.channels_max = 0;
64662306a36Sopenharmony_ci	if (qmc_dai->nb_rx_ts) {
64762306a36Sopenharmony_ci		qmc_soc_dai_driver->capture.channels_min = 1;
64862306a36Sopenharmony_ci		qmc_soc_dai_driver->capture.channels_max = qmc_dai->nb_rx_ts;
64962306a36Sopenharmony_ci	}
65062306a36Sopenharmony_ci	qmc_soc_dai_driver->capture.formats = qmc_audio_formats(qmc_dai->nb_rx_ts);
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ci	qmc_soc_dai_driver->playback.rates = snd_pcm_rate_to_rate_bit(info.tx_fs_rate);
65362306a36Sopenharmony_ci	qmc_soc_dai_driver->playback.rate_min = info.tx_fs_rate;
65462306a36Sopenharmony_ci	qmc_soc_dai_driver->playback.rate_max = info.tx_fs_rate;
65562306a36Sopenharmony_ci	qmc_soc_dai_driver->capture.rates = snd_pcm_rate_to_rate_bit(info.rx_fs_rate);
65662306a36Sopenharmony_ci	qmc_soc_dai_driver->capture.rate_min = info.rx_fs_rate;
65762306a36Sopenharmony_ci	qmc_soc_dai_driver->capture.rate_max = info.rx_fs_rate;
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_ci	qmc_soc_dai_driver->ops = &qmc_dai_ops;
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci	return 0;
66262306a36Sopenharmony_ci}
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_cistatic int qmc_audio_probe(struct platform_device *pdev)
66562306a36Sopenharmony_ci{
66662306a36Sopenharmony_ci	struct device_node *np = pdev->dev.of_node;
66762306a36Sopenharmony_ci	struct qmc_audio *qmc_audio;
66862306a36Sopenharmony_ci	struct device_node *child;
66962306a36Sopenharmony_ci	unsigned int i;
67062306a36Sopenharmony_ci	int ret;
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci	qmc_audio = devm_kzalloc(&pdev->dev, sizeof(*qmc_audio), GFP_KERNEL);
67362306a36Sopenharmony_ci	if (!qmc_audio)
67462306a36Sopenharmony_ci		return -ENOMEM;
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci	qmc_audio->dev = &pdev->dev;
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci	qmc_audio->num_dais = of_get_available_child_count(np);
67962306a36Sopenharmony_ci	if (qmc_audio->num_dais) {
68062306a36Sopenharmony_ci		qmc_audio->dais = devm_kcalloc(&pdev->dev, qmc_audio->num_dais,
68162306a36Sopenharmony_ci					       sizeof(*qmc_audio->dais),
68262306a36Sopenharmony_ci					       GFP_KERNEL);
68362306a36Sopenharmony_ci		if (!qmc_audio->dais)
68462306a36Sopenharmony_ci			return -ENOMEM;
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci		qmc_audio->dai_drivers = devm_kcalloc(&pdev->dev, qmc_audio->num_dais,
68762306a36Sopenharmony_ci						      sizeof(*qmc_audio->dai_drivers),
68862306a36Sopenharmony_ci						      GFP_KERNEL);
68962306a36Sopenharmony_ci		if (!qmc_audio->dai_drivers)
69062306a36Sopenharmony_ci			return -ENOMEM;
69162306a36Sopenharmony_ci	}
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci	i = 0;
69462306a36Sopenharmony_ci	for_each_available_child_of_node(np, child) {
69562306a36Sopenharmony_ci		ret = qmc_audio_dai_parse(qmc_audio, child,
69662306a36Sopenharmony_ci					  qmc_audio->dais + i,
69762306a36Sopenharmony_ci					  qmc_audio->dai_drivers + i);
69862306a36Sopenharmony_ci		if (ret) {
69962306a36Sopenharmony_ci			of_node_put(child);
70062306a36Sopenharmony_ci			return ret;
70162306a36Sopenharmony_ci		}
70262306a36Sopenharmony_ci		i++;
70362306a36Sopenharmony_ci	}
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci	platform_set_drvdata(pdev, qmc_audio);
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ci	ret = devm_snd_soc_register_component(qmc_audio->dev,
70962306a36Sopenharmony_ci					      &qmc_audio_soc_platform,
71062306a36Sopenharmony_ci					      qmc_audio->dai_drivers,
71162306a36Sopenharmony_ci					      qmc_audio->num_dais);
71262306a36Sopenharmony_ci	if (ret)
71362306a36Sopenharmony_ci		return ret;
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci	return 0;
71662306a36Sopenharmony_ci}
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_cistatic const struct of_device_id qmc_audio_id_table[] = {
71962306a36Sopenharmony_ci	{ .compatible = "fsl,qmc-audio" },
72062306a36Sopenharmony_ci	{} /* sentinel */
72162306a36Sopenharmony_ci};
72262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, qmc_audio_id_table);
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_cistatic struct platform_driver qmc_audio_driver = {
72562306a36Sopenharmony_ci	.driver = {
72662306a36Sopenharmony_ci		.name = "fsl-qmc-audio",
72762306a36Sopenharmony_ci		.of_match_table = of_match_ptr(qmc_audio_id_table),
72862306a36Sopenharmony_ci	},
72962306a36Sopenharmony_ci	.probe = qmc_audio_probe,
73062306a36Sopenharmony_ci};
73162306a36Sopenharmony_cimodule_platform_driver(qmc_audio_driver);
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_ciMODULE_AUTHOR("Herve Codina <herve.codina@bootlin.com>");
73462306a36Sopenharmony_ciMODULE_DESCRIPTION("CPM/QE QMC audio driver");
73562306a36Sopenharmony_ciMODULE_LICENSE("GPL");
736