File Index
Files and directories
Sign in
FILE

k1_i2s.c

File preview with syntax highlighting.

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