xref: /linux/sound/soc/spacemit/k1_i2s.c (revision cc4adab164b772a34b3340d644b7c4728498581e)
1fce21744STroy Mitchell // SPDX-License-Identifier: GPL-2.0
2fce21744STroy Mitchell /* Copyright (c) 2025 Troy Mitchell <troy.mitchell@linux.spacemit.com> */
3fce21744STroy Mitchell 
4fce21744STroy Mitchell #include <linux/bitfield.h>
5fce21744STroy Mitchell #include <linux/clk.h>
6fce21744STroy Mitchell #include <linux/reset.h>
7fce21744STroy Mitchell #include <sound/dmaengine_pcm.h>
8fce21744STroy Mitchell #include <sound/pcm.h>
9fce21744STroy Mitchell #include <sound/pcm_params.h>
10fce21744STroy Mitchell 
11fce21744STroy Mitchell #define SSCR			0x00	/* SPI/I2S top control register */
12fce21744STroy Mitchell #define SSFCR			0x04	/* SPI/I2S FIFO control register */
13fce21744STroy Mitchell #define SSINTEN			0x08	/* SPI/I2S interrupt enable register */
14fce21744STroy Mitchell #define SSDATR			0x10	/* SPI/I2S data register */
15fce21744STroy Mitchell #define SSPSP			0x18	/* SPI/I2S programmable serial protocol control register */
16fce21744STroy Mitchell #define SSRWT			0x24	/* SPI/I2S root control register */
17fce21744STroy Mitchell 
18fce21744STroy Mitchell /* SPI/I2S Work data size, register bits value 0~31 indicated data size 1~32 bits */
19fce21744STroy Mitchell #define SSCR_FIELD_DSS		GENMASK(9, 5)
20fce21744STroy Mitchell #define SSCR_DW_8BYTE		FIELD_PREP(SSCR_FIELD_DSS, 0x7)
21fce21744STroy Mitchell #define SSCR_DW_16BYTE		FIELD_PREP(SSCR_FIELD_DSS, 0xf)
22fce21744STroy Mitchell #define SSCR_DW_18BYTE		FIELD_PREP(SSCR_FIELD_DSS, 0x11)
23fce21744STroy Mitchell #define SSCR_DW_32BYTE		FIELD_PREP(SSCR_FIELD_DSS, 0x1f)
24fce21744STroy Mitchell 
25fce21744STroy Mitchell #define SSCR_SSE		BIT(0)		/* SPI/I2S Enable */
26fce21744STroy Mitchell #define SSCR_FRF_PSP		GENMASK(2, 1)	/* Frame Format*/
27fce21744STroy Mitchell #define SSCR_TRAIL		BIT(13)		/* Trailing Byte */
28fce21744STroy Mitchell 
29fce21744STroy Mitchell #define SSFCR_FIELD_TFT		GENMASK(3, 0)   /* TXFIFO Trigger Threshold */
30fce21744STroy Mitchell #define SSFCR_FIELD_RFT		GENMASK(8, 5)   /* RXFIFO Trigger Threshold */
31fce21744STroy Mitchell #define SSFCR_TSRE		BIT(10)		/* Transmit Service Request Enable */
32fce21744STroy Mitchell #define SSFCR_RSRE		BIT(11)		/* Receive Service Request Enable */
33fce21744STroy Mitchell 
34fce21744STroy Mitchell #define SSPSP_FSRT		BIT(3)		/* Frame Sync Relative Timing Bit */
35fce21744STroy Mitchell #define SSPSP_SFRMP		BIT(4)		/* Serial Frame Polarity */
36fce21744STroy Mitchell #define SSPSP_FIELD_SFRMWDTH	GENMASK(17, 12)	/* Serial Frame Width field  */
37fce21744STroy Mitchell 
38fce21744STroy Mitchell #define SSRWT_RWOT		BIT(0)		/* Receive Without Transmit */
39fce21744STroy Mitchell 
40fce21744STroy Mitchell #define SPACEMIT_PCM_RATES	(SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | \
41fce21744STroy Mitchell 				SNDRV_PCM_RATE_48000)
42fce21744STroy Mitchell #define SPACEMIT_PCM_FORMATS	(SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE)
43fce21744STroy Mitchell 
44fce21744STroy Mitchell #define SPACEMIT_I2S_PERIOD_SIZE 1024
45fce21744STroy Mitchell 
46fce21744STroy Mitchell struct spacemit_i2s_dev {
47fce21744STroy Mitchell 	struct device *dev;
48fce21744STroy Mitchell 
49fce21744STroy Mitchell 	void __iomem *base;
50fce21744STroy Mitchell 
51fce21744STroy Mitchell 	struct reset_control *reset;
52fce21744STroy Mitchell 
53fce21744STroy Mitchell 	struct clk *sysclk;
54fce21744STroy Mitchell 	struct clk *bclk;
55fce21744STroy Mitchell 	struct clk *sspa_clk;
56fce21744STroy Mitchell 
57fce21744STroy Mitchell 	struct snd_dmaengine_dai_dma_data capture_dma_data;
58fce21744STroy Mitchell 	struct snd_dmaengine_dai_dma_data playback_dma_data;
59fce21744STroy Mitchell 
60fce21744STroy Mitchell 	bool has_capture;
61fce21744STroy Mitchell 	bool has_playback;
62fce21744STroy Mitchell 
63fce21744STroy Mitchell 	int dai_fmt;
64fce21744STroy Mitchell 
65fce21744STroy Mitchell 	int started_count;
66fce21744STroy Mitchell };
67fce21744STroy Mitchell 
68fce21744STroy Mitchell static const struct snd_pcm_hardware spacemit_pcm_hardware = {
69fce21744STroy Mitchell 	.info		  = SNDRV_PCM_INFO_INTERLEAVED |
70fce21744STroy Mitchell 			    SNDRV_PCM_INFO_BATCH,
71fce21744STroy Mitchell 	.formats          = SPACEMIT_PCM_FORMATS,
72fce21744STroy Mitchell 	.rates		  = SPACEMIT_PCM_RATES,
73fce21744STroy Mitchell 	.rate_min         = SNDRV_PCM_RATE_8000,
74fce21744STroy Mitchell 	.rate_max         = SNDRV_PCM_RATE_192000,
75fce21744STroy Mitchell 	.channels_min     = 1,
76fce21744STroy Mitchell 	.channels_max     = 2,
77fce21744STroy Mitchell 	.buffer_bytes_max = SPACEMIT_I2S_PERIOD_SIZE * 4 * 4,
78fce21744STroy Mitchell 	.period_bytes_min = SPACEMIT_I2S_PERIOD_SIZE * 2,
79fce21744STroy Mitchell 	.period_bytes_max = SPACEMIT_I2S_PERIOD_SIZE * 4,
80fce21744STroy Mitchell 	.periods_min	  = 2,
81fce21744STroy Mitchell 	.periods_max	  = 4,
82fce21744STroy Mitchell };
83fce21744STroy Mitchell 
84fce21744STroy Mitchell static const struct snd_dmaengine_pcm_config spacemit_dmaengine_pcm_config = {
85fce21744STroy Mitchell 	.pcm_hardware = &spacemit_pcm_hardware,
86fce21744STroy Mitchell 	.prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
87fce21744STroy Mitchell 	.chan_names = {"tx", "rx"},
88fce21744STroy Mitchell 	.prealloc_buffer_size = 32 * 1024,
89fce21744STroy Mitchell };
90fce21744STroy Mitchell 
91fce21744STroy Mitchell static void spacemit_i2s_init(struct spacemit_i2s_dev *i2s)
92fce21744STroy Mitchell {
93fce21744STroy Mitchell 	u32 sscr_val, sspsp_val, ssfcr_val, ssrwt_val;
94fce21744STroy Mitchell 
95fce21744STroy Mitchell 	sscr_val = SSCR_TRAIL | SSCR_FRF_PSP;
96fce21744STroy Mitchell 	ssfcr_val = FIELD_PREP(SSFCR_FIELD_TFT, 5) |
97fce21744STroy Mitchell 		    FIELD_PREP(SSFCR_FIELD_RFT, 5) |
98fce21744STroy Mitchell 		    SSFCR_RSRE | SSFCR_TSRE;
99fce21744STroy Mitchell 	ssrwt_val = SSRWT_RWOT;
100fce21744STroy Mitchell 	sspsp_val = SSPSP_SFRMP;
101fce21744STroy Mitchell 
102fce21744STroy Mitchell 	writel(sscr_val, i2s->base + SSCR);
103fce21744STroy Mitchell 	writel(ssfcr_val, i2s->base + SSFCR);
104fce21744STroy Mitchell 	writel(sspsp_val, i2s->base + SSPSP);
105fce21744STroy Mitchell 	writel(ssrwt_val, i2s->base + SSRWT);
106fce21744STroy Mitchell 	writel(0, i2s->base + SSINTEN);
107fce21744STroy Mitchell }
108fce21744STroy Mitchell 
109fce21744STroy Mitchell static int spacemit_i2s_hw_params(struct snd_pcm_substream *substream,
110fce21744STroy Mitchell 				  struct snd_pcm_hw_params *params,
111fce21744STroy Mitchell 				  struct snd_soc_dai *dai)
112fce21744STroy Mitchell {
113fce21744STroy Mitchell 	struct spacemit_i2s_dev *i2s = snd_soc_dai_get_drvdata(dai);
114fce21744STroy Mitchell 	struct snd_dmaengine_dai_dma_data *dma_data;
115fce21744STroy Mitchell 	u32 data_width, data_bits;
116fce21744STroy Mitchell 	unsigned long bclk_rate;
117fce21744STroy Mitchell 	u32 val;
118fce21744STroy Mitchell 	int ret;
119fce21744STroy Mitchell 
120fce21744STroy Mitchell 	val = readl(i2s->base + SSCR);
121fce21744STroy Mitchell 	if (val & SSCR_SSE)
122fce21744STroy Mitchell 		return 0;
123fce21744STroy Mitchell 
124fce21744STroy Mitchell 	dma_data = &i2s->playback_dma_data;
125fce21744STroy Mitchell 
126fce21744STroy Mitchell 	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
127fce21744STroy Mitchell 		dma_data = &i2s->capture_dma_data;
128fce21744STroy Mitchell 
129fce21744STroy Mitchell 	switch (params_format(params)) {
130fce21744STroy Mitchell 	case SNDRV_PCM_FORMAT_S8:
131fce21744STroy Mitchell 		data_bits = 8;
132fce21744STroy Mitchell 		data_width = SSCR_DW_8BYTE;
133fce21744STroy Mitchell 		dma_data->maxburst = 8;
134fce21744STroy Mitchell 		dma_data->addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
135fce21744STroy Mitchell 		break;
136fce21744STroy Mitchell 	case SNDRV_PCM_FORMAT_S16_LE:
137fce21744STroy Mitchell 		data_bits = 16;
138fce21744STroy Mitchell 		data_width = SSCR_DW_16BYTE;
139fce21744STroy Mitchell 		dma_data->maxburst = 16;
140fce21744STroy Mitchell 		dma_data->addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
141fce21744STroy Mitchell 		break;
142fce21744STroy Mitchell 	case SNDRV_PCM_FORMAT_S32_LE:
143fce21744STroy Mitchell 		data_bits = 32;
144fce21744STroy Mitchell 		data_width = SSCR_DW_32BYTE;
145fce21744STroy Mitchell 		dma_data->maxburst = 32;
146fce21744STroy Mitchell 		dma_data->addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
147fce21744STroy Mitchell 		break;
148fce21744STroy Mitchell 	default:
149fce21744STroy Mitchell 		dev_dbg(i2s->dev, "unexpected data width type");
150fce21744STroy Mitchell 		return -EINVAL;
151fce21744STroy Mitchell 	}
152fce21744STroy Mitchell 
153fce21744STroy Mitchell 	switch (i2s->dai_fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
154fce21744STroy Mitchell 	case SND_SOC_DAIFMT_I2S:
155fce21744STroy Mitchell 		if (data_bits == 16) {
156fce21744STroy Mitchell 			data_width = SSCR_DW_32BYTE;
157fce21744STroy Mitchell 			dma_data->maxburst = 32;
158fce21744STroy Mitchell 			dma_data->addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
159fce21744STroy Mitchell 		}
160fce21744STroy Mitchell 
161fce21744STroy Mitchell 		snd_pcm_hw_constraint_minmax(substream->runtime,
162fce21744STroy Mitchell 					     SNDRV_PCM_HW_PARAM_CHANNELS,
163fce21744STroy Mitchell 					     1, 2);
164fce21744STroy Mitchell 		snd_pcm_hw_constraint_mask64(substream->runtime,
165fce21744STroy Mitchell 					     SNDRV_PCM_HW_PARAM_FORMAT,
166fce21744STroy Mitchell 					     SNDRV_PCM_FMTBIT_S16_LE);
167fce21744STroy Mitchell 		break;
168fce21744STroy Mitchell 	case SND_SOC_DAIFMT_DSP_A:
169fce21744STroy Mitchell 	case SND_SOC_DAIFMT_DSP_B:
170fce21744STroy Mitchell 		snd_pcm_hw_constraint_minmax(substream->runtime,
171fce21744STroy Mitchell 					     SNDRV_PCM_HW_PARAM_CHANNELS,
172fce21744STroy Mitchell 					     1, 1);
173fce21744STroy Mitchell 		snd_pcm_hw_constraint_mask64(substream->runtime,
174fce21744STroy Mitchell 					     SNDRV_PCM_HW_PARAM_FORMAT,
175fce21744STroy Mitchell 					     SNDRV_PCM_FMTBIT_S32_LE);
176fce21744STroy Mitchell 		break;
177fce21744STroy Mitchell 	default:
178fce21744STroy Mitchell 		dev_dbg(i2s->dev, "unexpected format type");
179fce21744STroy Mitchell 		return -EINVAL;
180fce21744STroy Mitchell 
181fce21744STroy Mitchell 	}
182fce21744STroy Mitchell 
183fce21744STroy Mitchell 	val = readl(i2s->base + SSCR);
184fce21744STroy Mitchell 	val &= ~SSCR_DW_32BYTE;
185fce21744STroy Mitchell 	val |= data_width;
186fce21744STroy Mitchell 	writel(val, i2s->base + SSCR);
187fce21744STroy Mitchell 
188fce21744STroy Mitchell 	bclk_rate = params_channels(params) *
189fce21744STroy Mitchell 		    params_rate(params) *
190fce21744STroy Mitchell 		    data_bits;
191fce21744STroy Mitchell 
192fce21744STroy Mitchell 	ret = clk_set_rate(i2s->bclk, bclk_rate);
193fce21744STroy Mitchell 	if (ret)
194fce21744STroy Mitchell 		return ret;
195fce21744STroy Mitchell 
196fce21744STroy Mitchell 	return clk_set_rate(i2s->sspa_clk, bclk_rate);
197fce21744STroy Mitchell }
198fce21744STroy Mitchell 
199fce21744STroy Mitchell static int spacemit_i2s_set_sysclk(struct snd_soc_dai *cpu_dai, int clk_id,
200fce21744STroy Mitchell 				   unsigned int freq, int dir)
201fce21744STroy Mitchell {
202fce21744STroy Mitchell 	struct spacemit_i2s_dev *i2s = dev_get_drvdata(cpu_dai->dev);
203fce21744STroy Mitchell 
204fce21744STroy Mitchell 	if (freq == 0)
205fce21744STroy Mitchell 		return 0;
206fce21744STroy Mitchell 
207fce21744STroy Mitchell 	return clk_set_rate(i2s->sysclk, freq);
208fce21744STroy Mitchell }
209fce21744STroy Mitchell 
210fce21744STroy Mitchell static int spacemit_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
211fce21744STroy Mitchell 				unsigned int fmt)
212fce21744STroy Mitchell {
213fce21744STroy Mitchell 	struct spacemit_i2s_dev *i2s = dev_get_drvdata(cpu_dai->dev);
214fce21744STroy Mitchell 	u32 sspsp_val;
215fce21744STroy Mitchell 
216fce21744STroy Mitchell 	sspsp_val = readl(i2s->base + SSPSP);
217fce21744STroy Mitchell 	sspsp_val &= ~SSPSP_FIELD_SFRMWDTH;
218fce21744STroy Mitchell 	sspsp_val |= SSPSP_FSRT;
219fce21744STroy Mitchell 
220fce21744STroy Mitchell 	i2s->dai_fmt = fmt;
221fce21744STroy Mitchell 
222fce21744STroy Mitchell 	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
223fce21744STroy Mitchell 	case SND_SOC_DAIFMT_I2S:
224fce21744STroy Mitchell 		sspsp_val |= FIELD_PREP(SSPSP_FIELD_SFRMWDTH, 0x10);
225fce21744STroy Mitchell 		break;
226fce21744STroy Mitchell 	case SND_SOC_DAIFMT_DSP_B:
227fce21744STroy Mitchell 		/* DSP_B: next frame asserted after previous frame end, so clear FSRT */
228fce21744STroy Mitchell 		sspsp_val &= ~SSPSP_FSRT;
229fce21744STroy Mitchell 		fallthrough;
230fce21744STroy Mitchell 	case SND_SOC_DAIFMT_DSP_A:
231fce21744STroy Mitchell 		sspsp_val |= FIELD_PREP(SSPSP_FIELD_SFRMWDTH, 0x1);
232fce21744STroy Mitchell 		break;
233fce21744STroy Mitchell 	default:
234fce21744STroy Mitchell 		dev_dbg(i2s->dev, "unexpected format type");
235fce21744STroy Mitchell 		return -EINVAL;
236fce21744STroy Mitchell 	}
237fce21744STroy Mitchell 
238fce21744STroy Mitchell 	writel(sspsp_val, i2s->base + SSPSP);
239fce21744STroy Mitchell 
240fce21744STroy Mitchell 	return 0;
241fce21744STroy Mitchell }
242fce21744STroy Mitchell 
243fce21744STroy Mitchell static int spacemit_i2s_trigger(struct snd_pcm_substream *substream,
244fce21744STroy Mitchell 				int cmd, struct snd_soc_dai *dai)
245fce21744STroy Mitchell {
246fce21744STroy Mitchell 	struct spacemit_i2s_dev *i2s = snd_soc_dai_get_drvdata(dai);
247fce21744STroy Mitchell 	u32 val;
248fce21744STroy Mitchell 
249fce21744STroy Mitchell 	switch (cmd) {
250fce21744STroy Mitchell 	case SNDRV_PCM_TRIGGER_START:
251fce21744STroy Mitchell 	case SNDRV_PCM_TRIGGER_RESUME:
252fce21744STroy Mitchell 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
253fce21744STroy Mitchell 		if (!i2s->started_count) {
254fce21744STroy Mitchell 			val = readl(i2s->base + SSCR);
255fce21744STroy Mitchell 			val |= SSCR_SSE;
256fce21744STroy Mitchell 			writel(val, i2s->base + SSCR);
257fce21744STroy Mitchell 		}
258fce21744STroy Mitchell 		i2s->started_count++;
259fce21744STroy Mitchell 		break;
260fce21744STroy Mitchell 	case SNDRV_PCM_TRIGGER_STOP:
261fce21744STroy Mitchell 	case SNDRV_PCM_TRIGGER_SUSPEND:
262fce21744STroy Mitchell 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
263fce21744STroy Mitchell 		if (i2s->started_count)
264fce21744STroy Mitchell 			i2s->started_count--;
265fce21744STroy Mitchell 
266fce21744STroy Mitchell 		if (!i2s->started_count) {
267fce21744STroy Mitchell 			val = readl(i2s->base + SSCR);
268fce21744STroy Mitchell 			val &= ~SSCR_SSE;
269fce21744STroy Mitchell 			writel(val, i2s->base + SSCR);
270fce21744STroy Mitchell 		}
271fce21744STroy Mitchell 		break;
272fce21744STroy Mitchell 	default:
273fce21744STroy Mitchell 		return -EINVAL;
274fce21744STroy Mitchell 	}
275fce21744STroy Mitchell 
276fce21744STroy Mitchell 	return 0;
277fce21744STroy Mitchell }
278fce21744STroy Mitchell 
279fce21744STroy Mitchell static int spacemit_i2s_dai_probe(struct snd_soc_dai *dai)
280fce21744STroy Mitchell {
281fce21744STroy Mitchell 	struct spacemit_i2s_dev *i2s = snd_soc_dai_get_drvdata(dai);
282fce21744STroy Mitchell 
283fce21744STroy Mitchell 	snd_soc_dai_init_dma_data(dai,
284fce21744STroy Mitchell 				  i2s->has_playback ? &i2s->playback_dma_data : NULL,
285fce21744STroy Mitchell 				  i2s->has_capture ? &i2s->capture_dma_data : NULL);
286fce21744STroy Mitchell 
287fce21744STroy Mitchell 	reset_control_deassert(i2s->reset);
288fce21744STroy Mitchell 
289fce21744STroy Mitchell 	spacemit_i2s_init(i2s);
290fce21744STroy Mitchell 
291fce21744STroy Mitchell 	return 0;
292fce21744STroy Mitchell }
293fce21744STroy Mitchell 
294fce21744STroy Mitchell static int spacemit_i2s_dai_remove(struct snd_soc_dai *dai)
295fce21744STroy Mitchell {
296fce21744STroy Mitchell 	struct spacemit_i2s_dev *i2s = snd_soc_dai_get_drvdata(dai);
297fce21744STroy Mitchell 
298fce21744STroy Mitchell 	reset_control_assert(i2s->reset);
299fce21744STroy Mitchell 
300fce21744STroy Mitchell 	return 0;
301fce21744STroy Mitchell }
302fce21744STroy Mitchell 
303fce21744STroy Mitchell static const struct snd_soc_dai_ops spacemit_i2s_dai_ops = {
304fce21744STroy Mitchell 	.probe = spacemit_i2s_dai_probe,
305fce21744STroy Mitchell 	.remove = spacemit_i2s_dai_remove,
306fce21744STroy Mitchell 	.hw_params = spacemit_i2s_hw_params,
307fce21744STroy Mitchell 	.set_sysclk = spacemit_i2s_set_sysclk,
308fce21744STroy Mitchell 	.set_fmt = spacemit_i2s_set_fmt,
309fce21744STroy Mitchell 	.trigger = spacemit_i2s_trigger,
310fce21744STroy Mitchell };
311fce21744STroy Mitchell 
312fce21744STroy Mitchell static struct snd_soc_dai_driver spacemit_i2s_dai = {
313fce21744STroy Mitchell 	.ops = &spacemit_i2s_dai_ops,
314fce21744STroy Mitchell 	.playback = {
315fce21744STroy Mitchell 		.channels_min = 1,
316fce21744STroy Mitchell 		.channels_max = 2,
317fce21744STroy Mitchell 		.rates = SPACEMIT_PCM_RATES,
318fce21744STroy Mitchell 		.rate_min = SNDRV_PCM_RATE_8000,
319fce21744STroy Mitchell 		.rate_max = SNDRV_PCM_RATE_48000,
320fce21744STroy Mitchell 		.formats = SPACEMIT_PCM_FORMATS,
321fce21744STroy Mitchell 	},
322fce21744STroy Mitchell 	.capture = {
323fce21744STroy Mitchell 		.channels_min = 1,
324fce21744STroy Mitchell 		.channels_max = 2,
325fce21744STroy Mitchell 		.rates = SPACEMIT_PCM_RATES,
326fce21744STroy Mitchell 		.rate_min = SNDRV_PCM_RATE_8000,
327fce21744STroy Mitchell 		.rate_max = SNDRV_PCM_RATE_48000,
328fce21744STroy Mitchell 		.formats = SPACEMIT_PCM_FORMATS,
329fce21744STroy Mitchell 	},
330fce21744STroy Mitchell 	.symmetric_rate = 1,
331fce21744STroy Mitchell };
332fce21744STroy Mitchell 
333fce21744STroy Mitchell static int spacemit_i2s_init_dai(struct spacemit_i2s_dev *i2s,
334fce21744STroy Mitchell 				 struct snd_soc_dai_driver **dp,
335fce21744STroy Mitchell 				 dma_addr_t addr)
336fce21744STroy Mitchell {
337fce21744STroy Mitchell 	struct device_node *node = i2s->dev->of_node;
338fce21744STroy Mitchell 	struct snd_soc_dai_driver *dai;
339fce21744STroy Mitchell 	struct property *dma_names;
340fce21744STroy Mitchell 	const char *dma_name;
341fce21744STroy Mitchell 
342fce21744STroy Mitchell 	of_property_for_each_string(node, "dma-names", dma_names, dma_name) {
343fce21744STroy Mitchell 		if (!strcmp(dma_name, "tx"))
344fce21744STroy Mitchell 			i2s->has_playback = true;
345fce21744STroy Mitchell 		if (!strcmp(dma_name, "rx"))
346fce21744STroy Mitchell 			i2s->has_capture = true;
347fce21744STroy Mitchell 	}
348fce21744STroy Mitchell 
349fce21744STroy Mitchell 	dai = devm_kmemdup(i2s->dev, &spacemit_i2s_dai,
350fce21744STroy Mitchell 			   sizeof(*dai), GFP_KERNEL);
351fce21744STroy Mitchell 	if (!dai)
352fce21744STroy Mitchell 		return -ENOMEM;
353fce21744STroy Mitchell 
354fce21744STroy Mitchell 	if (i2s->has_playback) {
355fce21744STroy Mitchell 		dai->playback.stream_name = "Playback";
356fce21744STroy Mitchell 		dai->playback.channels_min = 1;
357fce21744STroy Mitchell 		dai->playback.channels_max = 2;
358fce21744STroy Mitchell 		dai->playback.rates = SPACEMIT_PCM_RATES;
359fce21744STroy Mitchell 		dai->playback.formats = SPACEMIT_PCM_FORMATS;
360fce21744STroy Mitchell 
361fce21744STroy Mitchell 		i2s->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
362fce21744STroy Mitchell 		i2s->playback_dma_data.maxburst = 32;
363fce21744STroy Mitchell 		i2s->playback_dma_data.addr = addr;
364fce21744STroy Mitchell 	}
365fce21744STroy Mitchell 
366fce21744STroy Mitchell 	if (i2s->has_capture) {
367fce21744STroy Mitchell 		dai->capture.stream_name = "Capture";
368fce21744STroy Mitchell 		dai->capture.channels_min = 1;
369fce21744STroy Mitchell 		dai->capture.channels_max = 2;
370fce21744STroy Mitchell 		dai->capture.rates = SPACEMIT_PCM_RATES;
371fce21744STroy Mitchell 		dai->capture.formats = SPACEMIT_PCM_FORMATS;
372fce21744STroy Mitchell 
373fce21744STroy Mitchell 		i2s->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
374fce21744STroy Mitchell 		i2s->capture_dma_data.maxburst = 32;
375fce21744STroy Mitchell 		i2s->capture_dma_data.addr = addr;
376fce21744STroy Mitchell 	}
377fce21744STroy Mitchell 
378fce21744STroy Mitchell 	if (dp)
379fce21744STroy Mitchell 		*dp = dai;
380fce21744STroy Mitchell 
381fce21744STroy Mitchell 	return 0;
382fce21744STroy Mitchell }
383fce21744STroy Mitchell 
384fce21744STroy Mitchell static const struct snd_soc_component_driver spacemit_i2s_component = {
385fce21744STroy Mitchell 	.name = "i2s-k1",
386fce21744STroy Mitchell 	.legacy_dai_naming = 1,
387fce21744STroy Mitchell };
388fce21744STroy Mitchell 
389fce21744STroy Mitchell static int spacemit_i2s_probe(struct platform_device *pdev)
390fce21744STroy Mitchell {
391fce21744STroy Mitchell 	struct snd_soc_dai_driver *dai;
392fce21744STroy Mitchell 	struct spacemit_i2s_dev *i2s;
393fce21744STroy Mitchell 	struct resource *res;
394fce21744STroy Mitchell 	struct clk *clk;
395fce21744STroy Mitchell 	int ret;
396fce21744STroy Mitchell 
397fce21744STroy Mitchell 	i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL);
398fce21744STroy Mitchell 	if (!i2s)
399fce21744STroy Mitchell 		return -ENOMEM;
400fce21744STroy Mitchell 
401fce21744STroy Mitchell 	i2s->dev = &pdev->dev;
402fce21744STroy Mitchell 
403fce21744STroy Mitchell 	i2s->sysclk = devm_clk_get_enabled(i2s->dev, "sysclk");
404fce21744STroy Mitchell 	if (IS_ERR(i2s->sysclk))
405fce21744STroy Mitchell 		return dev_err_probe(i2s->dev, PTR_ERR(i2s->sysclk),
406fce21744STroy Mitchell 				     "failed to enable sysbase clock\n");
407fce21744STroy Mitchell 
408fce21744STroy Mitchell 	i2s->bclk = devm_clk_get_enabled(i2s->dev, "bclk");
409fce21744STroy Mitchell 	if (IS_ERR(i2s->bclk))
410fce21744STroy Mitchell 		return dev_err_probe(i2s->dev, PTR_ERR(i2s->bclk), "failed to enable bit clock\n");
411fce21744STroy Mitchell 
412fce21744STroy Mitchell 	clk = devm_clk_get_enabled(i2s->dev, "sspa_bus");
413fce21744STroy Mitchell 	if (IS_ERR(clk))
414fce21744STroy Mitchell 		return dev_err_probe(i2s->dev, PTR_ERR(clk), "failed to enable sspa_bus clock\n");
415fce21744STroy Mitchell 
416fce21744STroy Mitchell 	i2s->sspa_clk = devm_clk_get_enabled(i2s->dev, "sspa");
417*e32c4025SGoko Mell 	if (IS_ERR(i2s->sspa_clk))
418*e32c4025SGoko Mell 		return dev_err_probe(i2s->dev, PTR_ERR(i2s->sspa_clk),
419*e32c4025SGoko Mell 				     "failed to enable sspa clock\n");
420fce21744STroy Mitchell 
421fce21744STroy Mitchell 	i2s->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
422fce21744STroy Mitchell 	if (IS_ERR(i2s->base))
423fce21744STroy Mitchell 		return dev_err_probe(i2s->dev, PTR_ERR(i2s->base), "failed to map registers\n");
424fce21744STroy Mitchell 
425fce21744STroy Mitchell 	i2s->reset = devm_reset_control_get_exclusive(&pdev->dev, NULL);
426fce21744STroy Mitchell 	if (IS_ERR(i2s->reset))
427fce21744STroy Mitchell 		return dev_err_probe(i2s->dev, PTR_ERR(i2s->reset),
428fce21744STroy Mitchell 				     "failed to get reset control");
429fce21744STroy Mitchell 
430fce21744STroy Mitchell 	dev_set_drvdata(i2s->dev, i2s);
431fce21744STroy Mitchell 
432f034c16aSTroy Mitchell 	ret = spacemit_i2s_init_dai(i2s, &dai, res->start + SSDATR);
433f034c16aSTroy Mitchell 	if (ret)
434f034c16aSTroy Mitchell 		return ret;
435fce21744STroy Mitchell 
436fce21744STroy Mitchell 	ret = devm_snd_soc_register_component(i2s->dev,
437fce21744STroy Mitchell 					      &spacemit_i2s_component,
438fce21744STroy Mitchell 					      dai, 1);
439fce21744STroy Mitchell 	if (ret)
440fce21744STroy Mitchell 		return dev_err_probe(i2s->dev, ret, "failed to register component");
441fce21744STroy Mitchell 
442fce21744STroy Mitchell 	return devm_snd_dmaengine_pcm_register(&pdev->dev, &spacemit_dmaengine_pcm_config, 0);
443fce21744STroy Mitchell }
444fce21744STroy Mitchell 
445fce21744STroy Mitchell static const struct of_device_id spacemit_i2s_of_match[] = {
446fce21744STroy Mitchell 	{ .compatible = "spacemit,k1-i2s", },
447fce21744STroy Mitchell 	{ /* sentinel */ }
448fce21744STroy Mitchell };
449fce21744STroy Mitchell MODULE_DEVICE_TABLE(of, spacemit_i2s_of_match);
450fce21744STroy Mitchell 
451fce21744STroy Mitchell static struct platform_driver spacemit_i2s_driver = {
452fce21744STroy Mitchell 	.probe = spacemit_i2s_probe,
453fce21744STroy Mitchell 	.driver = {
454fce21744STroy Mitchell 		.name = "i2s-k1",
455fce21744STroy Mitchell 		.of_match_table = spacemit_i2s_of_match,
456fce21744STroy Mitchell 	},
457fce21744STroy Mitchell };
458fce21744STroy Mitchell module_platform_driver(spacemit_i2s_driver);
459fce21744STroy Mitchell 
460fce21744STroy Mitchell MODULE_LICENSE("GPL");
461fce21744STroy Mitchell MODULE_DESCRIPTION("I2S bus driver for SpacemiT K1 SoC");
462