Linux Audio

Check our new training course

Loading...
Note: File does not exist in v6.8.
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2//
  3// Author: Kevin Wells <kevin.wells@nxp.com>
  4//
  5// Copyright (C) 2008 NXP Semiconductors
  6// Copyright 2023 Timesys Corporation <piotr.wojtaszczyk@timesys.com>
  7
  8#include <linux/init.h>
  9#include <linux/module.h>
 10#include <linux/interrupt.h>
 11#include <linux/device.h>
 12#include <linux/delay.h>
 13#include <linux/clk.h>
 14#include <linux/io.h>
 15
 16#include <sound/core.h>
 17#include <sound/pcm.h>
 18#include <sound/pcm_params.h>
 19#include <sound/dmaengine_pcm.h>
 20#include <sound/initval.h>
 21#include <sound/soc.h>
 22
 23#include "lpc3xxx-i2s.h"
 24
 25#define I2S_PLAYBACK_FLAG 0x1
 26#define I2S_CAPTURE_FLAG 0x2
 27
 28#define LPC3XXX_I2S_RATES ( \
 29	SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | \
 30	SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
 31	SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000)
 32
 33#define LPC3XXX_I2S_FORMATS ( \
 34	SNDRV_PCM_FMTBIT_S8 | \
 35	SNDRV_PCM_FMTBIT_S16_LE | \
 36	SNDRV_PCM_FMTBIT_S32_LE)
 37
 38static void __lpc3xxx_find_clkdiv(u32 *clkx, u32 *clky, int freq, int xbytes, u32 clkrate)
 39{
 40	u32 i2srate;
 41	u32 idxx, idyy;
 42	u32 diff, trate, baseclk;
 43
 44	/* Adjust rate for sample size (bits) and 2 channels and offset for
 45	 * divider in clock output
 46	 */
 47	i2srate = (freq / 100) * 2 * (8 * xbytes);
 48	i2srate = i2srate << 1;
 49	clkrate = clkrate / 100;
 50	baseclk = clkrate;
 51	*clkx = 1;
 52	*clky = 1;
 53
 54	/* Find the best divider */
 55	*clkx = *clky = 0;
 56	diff = ~0;
 57	for (idxx = 1; idxx < 0xFF; idxx++) {
 58		for (idyy = 1; idyy < 0xFF; idyy++) {
 59			trate = (baseclk * idxx) / idyy;
 60			if (abs(trate - i2srate) < diff) {
 61				diff = abs(trate - i2srate);
 62				*clkx = idxx;
 63				*clky = idyy;
 64			}
 65		}
 66	}
 67}
 68
 69static int lpc3xxx_i2s_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai)
 70{
 71	struct lpc3xxx_i2s_info *i2s_info_p = snd_soc_dai_get_drvdata(cpu_dai);
 72	struct device *dev = i2s_info_p->dev;
 73	u32 flag;
 74	int ret = 0;
 75
 76	guard(mutex)(&i2s_info_p->lock);
 77
 78	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
 79		flag = I2S_PLAYBACK_FLAG;
 80	else
 81		flag = I2S_CAPTURE_FLAG;
 82
 83	if (flag & i2s_info_p->streams_in_use) {
 84		dev_warn(dev, "I2S channel is busy\n");
 85		ret = -EBUSY;
 86		return ret;
 87	}
 88
 89	if (i2s_info_p->streams_in_use == 0) {
 90		ret = clk_prepare_enable(i2s_info_p->clk);
 91		if (ret) {
 92			dev_err(dev, "Can't enable clock, err=%d\n", ret);
 93			return ret;
 94		}
 95	}
 96
 97	i2s_info_p->streams_in_use |= flag;
 98	return 0;
 99}
100
101static void lpc3xxx_i2s_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai)
102{
103	struct lpc3xxx_i2s_info *i2s_info_p = snd_soc_dai_get_drvdata(cpu_dai);
104	struct regmap *regs = i2s_info_p->regs;
105	const u32 stop_bits = (LPC3XXX_I2S_RESET | LPC3XXX_I2S_STOP);
106	u32 flag;
107
108	guard(mutex)(&i2s_info_p->lock);
109
110	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
111		flag = I2S_PLAYBACK_FLAG;
112		regmap_write(regs, LPC3XXX_REG_I2S_TX_RATE, 0);
113		regmap_update_bits(regs, LPC3XXX_REG_I2S_DAO, stop_bits, stop_bits);
114	} else {
115		flag = I2S_CAPTURE_FLAG;
116		regmap_write(regs, LPC3XXX_REG_I2S_RX_RATE, 0);
117		regmap_update_bits(regs, LPC3XXX_REG_I2S_DAI, stop_bits, stop_bits);
118	}
119	i2s_info_p->streams_in_use &= ~flag;
120
121	if (i2s_info_p->streams_in_use == 0)
122		clk_disable_unprepare(i2s_info_p->clk);
123}
124
125static int lpc3xxx_i2s_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
126				      int clk_id, unsigned int freq, int dir)
127{
128	struct lpc3xxx_i2s_info *i2s_info_p = snd_soc_dai_get_drvdata(cpu_dai);
129
130	/* Will use in HW params later */
131	i2s_info_p->freq = freq;
132
133	return 0;
134}
135
136static int lpc3xxx_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
137{
138	struct lpc3xxx_i2s_info *i2s_info_p = snd_soc_dai_get_drvdata(cpu_dai);
139	struct device *dev = i2s_info_p->dev;
140
141	if ((fmt & SND_SOC_DAIFMT_FORMAT_MASK) != SND_SOC_DAIFMT_I2S) {
142		dev_warn(dev, "unsupported bus format %d\n", fmt);
143		return -EINVAL;
144	}
145
146	if ((fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) != SND_SOC_DAIFMT_BP_FP) {
147		dev_warn(dev, "unsupported clock provider %d\n", fmt);
148		return -EINVAL;
149	}
150
151	return 0;
152}
153
154static int lpc3xxx_i2s_hw_params(struct snd_pcm_substream *substream,
155				 struct snd_pcm_hw_params *params,
156				 struct snd_soc_dai *cpu_dai)
157{
158	struct lpc3xxx_i2s_info *i2s_info_p = snd_soc_dai_get_drvdata(cpu_dai);
159	struct device *dev = i2s_info_p->dev;
160	struct regmap *regs = i2s_info_p->regs;
161	int xfersize;
162	u32 tmp, clkx, clky;
163
164	tmp = LPC3XXX_I2S_RESET | LPC3XXX_I2S_STOP;
165	switch (params_format(params)) {
166	case SNDRV_PCM_FORMAT_S8:
167		tmp |= LPC3XXX_I2S_WW8 | LPC3XXX_I2S_WS_HP(LPC3XXX_I2S_WW8_HP);
168		xfersize = 1;
169		break;
170
171	case SNDRV_PCM_FORMAT_S16_LE:
172		tmp |= LPC3XXX_I2S_WW16 | LPC3XXX_I2S_WS_HP(LPC3XXX_I2S_WW16_HP);
173		xfersize = 2;
174		break;
175
176	case SNDRV_PCM_FORMAT_S32_LE:
177		tmp |= LPC3XXX_I2S_WW32 | LPC3XXX_I2S_WS_HP(LPC3XXX_I2S_WW32_HP);
178		xfersize = 4;
179		break;
180
181	default:
182		dev_warn(dev, "Unsupported audio data format %d\n", params_format(params));
183		return -EINVAL;
184	}
185
186	if (params_channels(params) == 1)
187		tmp |= LPC3XXX_I2S_MONO;
188
189	__lpc3xxx_find_clkdiv(&clkx, &clky, i2s_info_p->freq, xfersize, i2s_info_p->clkrate);
190
191	dev_dbg(dev, "Stream                : %s\n", snd_pcm_direction_name(substream->stream));
192	dev_dbg(dev, "Desired clock rate    : %d\n", i2s_info_p->freq);
193	dev_dbg(dev, "Base clock rate       : %d\n", i2s_info_p->clkrate);
194	dev_dbg(dev, "Transfer size (bytes) : %d\n", xfersize);
195	dev_dbg(dev, "Clock divider (x)     : %d\n", clkx);
196	dev_dbg(dev, "Clock divider (y)     : %d\n", clky);
197	dev_dbg(dev, "Channels              : %d\n", params_channels(params));
198	dev_dbg(dev, "Data format           : %s\n", "I2S");
199
200	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
201		regmap_write(regs, LPC3XXX_REG_I2S_DMA1,
202			     LPC3XXX_I2S_DMA1_TX_EN | LPC3XXX_I2S_DMA0_TX_DEPTH(4));
203		regmap_write(regs, LPC3XXX_REG_I2S_TX_RATE, (clkx << 8) | clky);
204		regmap_write(regs, LPC3XXX_REG_I2S_DAO, tmp);
205	} else {
206		regmap_write(regs, LPC3XXX_REG_I2S_DMA0,
207			     LPC3XXX_I2S_DMA0_RX_EN | LPC3XXX_I2S_DMA1_RX_DEPTH(4));
208		regmap_write(regs, LPC3XXX_REG_I2S_RX_RATE, (clkx << 8) | clky);
209		regmap_write(regs, LPC3XXX_REG_I2S_DAI, tmp);
210	}
211
212	return 0;
213}
214
215static int lpc3xxx_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
216			       struct snd_soc_dai *cpu_dai)
217{
218	struct lpc3xxx_i2s_info *i2s_info_p = snd_soc_dai_get_drvdata(cpu_dai);
219	struct regmap *regs = i2s_info_p->regs;
220	int ret = 0;
221
222	switch (cmd) {
223	case SNDRV_PCM_TRIGGER_STOP:
224	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
225	case SNDRV_PCM_TRIGGER_SUSPEND:
226		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
227			regmap_update_bits(regs, LPC3XXX_REG_I2S_DAO,
228					   LPC3XXX_I2S_STOP, LPC3XXX_I2S_STOP);
229		else
230			regmap_update_bits(regs, LPC3XXX_REG_I2S_DAI,
231					   LPC3XXX_I2S_STOP, LPC3XXX_I2S_STOP);
232		break;
233
234	case SNDRV_PCM_TRIGGER_START:
235	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
236	case SNDRV_PCM_TRIGGER_RESUME:
237		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
238			regmap_update_bits(regs, LPC3XXX_REG_I2S_DAO,
239					   (LPC3XXX_I2S_RESET | LPC3XXX_I2S_STOP), 0);
240		else
241			regmap_update_bits(regs, LPC3XXX_REG_I2S_DAI,
242					   (LPC3XXX_I2S_RESET | LPC3XXX_I2S_STOP), 0);
243		break;
244	default:
245		ret = -EINVAL;
246	}
247
248	return ret;
249}
250
251static int lpc3xxx_i2s_dai_probe(struct snd_soc_dai *dai)
252{
253	struct lpc3xxx_i2s_info *i2s_info_p = snd_soc_dai_get_drvdata(dai);
254
255	snd_soc_dai_init_dma_data(dai, &i2s_info_p->playback_dma_config,
256				  &i2s_info_p->capture_dma_config);
257	return 0;
258}
259
260static const struct snd_soc_dai_ops lpc3xxx_i2s_dai_ops = {
261	.probe	= lpc3xxx_i2s_dai_probe,
262	.startup = lpc3xxx_i2s_startup,
263	.shutdown = lpc3xxx_i2s_shutdown,
264	.trigger = lpc3xxx_i2s_trigger,
265	.hw_params = lpc3xxx_i2s_hw_params,
266	.set_sysclk = lpc3xxx_i2s_set_dai_sysclk,
267	.set_fmt = lpc3xxx_i2s_set_dai_fmt,
268};
269
270static struct snd_soc_dai_driver lpc3xxx_i2s_dai_driver = {
271	.playback = {
272		.channels_min = 1,
273		.channels_max = 2,
274		.rates = LPC3XXX_I2S_RATES,
275		.formats = LPC3XXX_I2S_FORMATS,
276		},
277	.capture = {
278		.channels_min = 1,
279		.channels_max = 2,
280		.rates = LPC3XXX_I2S_RATES,
281		.formats = LPC3XXX_I2S_FORMATS,
282		},
283	.ops = &lpc3xxx_i2s_dai_ops,
284	.symmetric_rate = 1,
285	.symmetric_channels = 1,
286	.symmetric_sample_bits = 1,
287};
288
289static const struct snd_soc_component_driver lpc32xx_i2s_component = {
290	.name = "lpc32xx-i2s",
291	.legacy_dai_naming = 1,
292};
293
294static const struct regmap_config lpc32xx_i2s_regconfig = {
295	.reg_bits = 32,
296	.reg_stride = 4,
297	.val_bits = 32,
298	.max_register = LPC3XXX_REG_I2S_RX_RATE,
299};
300
301static int lpc32xx_i2s_probe(struct platform_device *pdev)
302{
303	struct device *dev = &pdev->dev;
304	struct lpc3xxx_i2s_info *i2s_info_p;
305	struct resource *res;
306	void __iomem *iomem;
307	int ret;
308
309	i2s_info_p = devm_kzalloc(dev, sizeof(*i2s_info_p), GFP_KERNEL);
310	if (!i2s_info_p)
311		return -ENOMEM;
312
313	platform_set_drvdata(pdev, i2s_info_p);
314	i2s_info_p->dev = dev;
315
316	iomem = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
317	if (IS_ERR(iomem))
318		return dev_err_probe(dev, PTR_ERR(iomem), "Can't map registers\n");
319
320	i2s_info_p->regs = devm_regmap_init_mmio(dev, iomem, &lpc32xx_i2s_regconfig);
321	if (IS_ERR(i2s_info_p->regs))
322		return dev_err_probe(dev, PTR_ERR(i2s_info_p->regs),
323				     "failed to init register map: %pe\n", i2s_info_p->regs);
324
325	i2s_info_p->clk = devm_clk_get(dev, NULL);
326	if (IS_ERR(i2s_info_p->clk))
327		return dev_err_probe(dev, PTR_ERR(i2s_info_p->clk), "Can't get clock\n");
328
329	i2s_info_p->clkrate = clk_get_rate(i2s_info_p->clk);
330	if (i2s_info_p->clkrate == 0)
331		return dev_err_probe(dev, -EINVAL, "Invalid returned clock rate\n");
332
333	mutex_init(&i2s_info_p->lock);
334
335	ret = devm_snd_soc_register_component(dev, &lpc32xx_i2s_component,
336					      &lpc3xxx_i2s_dai_driver, 1);
337	if (ret)
338		return dev_err_probe(dev, ret, "Can't register cpu_dai component\n");
339
340	i2s_info_p->playback_dma_config.addr = (dma_addr_t)(res->start + LPC3XXX_REG_I2S_TX_FIFO);
341	i2s_info_p->playback_dma_config.maxburst = 4;
342
343	i2s_info_p->capture_dma_config.addr = (dma_addr_t)(res->start + LPC3XXX_REG_I2S_RX_FIFO);
344	i2s_info_p->capture_dma_config.maxburst = 4;
345
346	ret = lpc3xxx_pcm_register(pdev);
347	if (ret)
348		return dev_err_probe(dev, ret, "Can't register pcm component\n");
349
350	return 0;
351}
352
353static const struct of_device_id lpc32xx_i2s_match[] = {
354	{ .compatible = "nxp,lpc3220-i2s" },
355	{},
356};
357MODULE_DEVICE_TABLE(of, lpc32xx_i2s_match);
358
359static struct platform_driver lpc32xx_i2s_driver = {
360	.probe = lpc32xx_i2s_probe,
361	.driver		= {
362		.name	= "lpc3xxx-i2s",
363		.of_match_table = lpc32xx_i2s_match,
364	},
365};
366
367module_platform_driver(lpc32xx_i2s_driver);
368
369MODULE_AUTHOR("Kevin Wells <kevin.wells@nxp.com>");
370MODULE_AUTHOR("Piotr Wojtaszczyk <piotr.wojtaszczyk@timesys.com>");
371MODULE_DESCRIPTION("ASoC LPC3XXX I2S interface");
372MODULE_LICENSE("GPL");