xref: /linux/sound/soc/img/img-i2s-in.c (revision b6ebbac51bedf9e98e837688bc838f400196da5e)
1 /*
2  * IMG I2S input controller driver
3  *
4  * Copyright (C) 2015 Imagination Technologies Ltd.
5  *
6  * Author: Damien Horsley <Damien.Horsley@imgtec.com>
7  *
8  * This program is free software; you can redistribute it and/or modify it
9  * under the terms and conditions of the GNU General Public License,
10  * version 2, as published by the Free Software Foundation.
11  */
12 
13 #include <linux/clk.h>
14 #include <linux/init.h>
15 #include <linux/kernel.h>
16 #include <linux/module.h>
17 #include <linux/of.h>
18 #include <linux/platform_device.h>
19 #include <linux/reset.h>
20 
21 #include <sound/core.h>
22 #include <sound/dmaengine_pcm.h>
23 #include <sound/initval.h>
24 #include <sound/pcm.h>
25 #include <sound/pcm_params.h>
26 #include <sound/soc.h>
27 
28 #define IMG_I2S_IN_RX_FIFO			0x0
29 
30 #define IMG_I2S_IN_CTL				0x4
31 #define IMG_I2S_IN_CTL_ACTIVE_CHAN_MASK		0xfffffffc
32 #define IMG_I2S_IN_CTL_ACTIVE_CH_SHIFT		2
33 #define IMG_I2S_IN_CTL_16PACK_MASK		BIT(1)
34 #define IMG_I2S_IN_CTL_ME_MASK			BIT(0)
35 
36 #define IMG_I2S_IN_CH_CTL			0x4
37 #define IMG_I2S_IN_CH_CTL_CCDEL_MASK		0x38000
38 #define IMG_I2S_IN_CH_CTL_CCDEL_SHIFT		15
39 #define IMG_I2S_IN_CH_CTL_FEN_MASK		BIT(14)
40 #define IMG_I2S_IN_CH_CTL_FMODE_MASK		BIT(13)
41 #define IMG_I2S_IN_CH_CTL_16PACK_MASK		BIT(12)
42 #define IMG_I2S_IN_CH_CTL_JUST_MASK		BIT(10)
43 #define IMG_I2S_IN_CH_CTL_PACKH_MASK		BIT(9)
44 #define IMG_I2S_IN_CH_CTL_CLK_TRANS_MASK	BIT(8)
45 #define IMG_I2S_IN_CH_CTL_BLKP_MASK		BIT(7)
46 #define IMG_I2S_IN_CH_CTL_FIFO_FLUSH_MASK	BIT(6)
47 #define IMG_I2S_IN_CH_CTL_LRD_MASK		BIT(3)
48 #define IMG_I2S_IN_CH_CTL_FW_MASK		BIT(2)
49 #define IMG_I2S_IN_CH_CTL_SW_MASK		BIT(1)
50 #define IMG_I2S_IN_CH_CTL_ME_MASK		BIT(0)
51 
52 #define IMG_I2S_IN_CH_STRIDE			0x20
53 
54 struct img_i2s_in {
55 	void __iomem *base;
56 	struct clk *clk_sys;
57 	struct snd_dmaengine_dai_dma_data dma_data;
58 	struct device *dev;
59 	unsigned int max_i2s_chan;
60 	void __iomem *channel_base;
61 	unsigned int active_channels;
62 	struct snd_soc_dai_driver dai_driver;
63 };
64 
65 static inline void img_i2s_in_writel(struct img_i2s_in *i2s, u32 val, u32 reg)
66 {
67 	writel(val, i2s->base + reg);
68 }
69 
70 static inline u32 img_i2s_in_readl(struct img_i2s_in *i2s, u32 reg)
71 {
72 	return readl(i2s->base + reg);
73 }
74 
75 static inline void img_i2s_in_ch_writel(struct img_i2s_in *i2s, u32 chan,
76 					u32 val, u32 reg)
77 {
78 	writel(val, i2s->channel_base + (chan * IMG_I2S_IN_CH_STRIDE) + reg);
79 }
80 
81 static inline u32 img_i2s_in_ch_readl(struct img_i2s_in *i2s, u32 chan,
82 					u32 reg)
83 {
84 	return readl(i2s->channel_base + (chan * IMG_I2S_IN_CH_STRIDE) + reg);
85 }
86 
87 static inline void img_i2s_in_ch_disable(struct img_i2s_in *i2s, u32 chan)
88 {
89 	u32 reg;
90 
91 	reg = img_i2s_in_ch_readl(i2s, chan, IMG_I2S_IN_CH_CTL);
92 	reg &= ~IMG_I2S_IN_CH_CTL_ME_MASK;
93 	img_i2s_in_ch_writel(i2s, chan, reg, IMG_I2S_IN_CH_CTL);
94 }
95 
96 static inline void img_i2s_in_ch_enable(struct img_i2s_in *i2s, u32 chan)
97 {
98 	u32 reg;
99 
100 	reg = img_i2s_in_ch_readl(i2s, chan, IMG_I2S_IN_CH_CTL);
101 	reg |= IMG_I2S_IN_CH_CTL_ME_MASK;
102 	img_i2s_in_ch_writel(i2s, chan, reg, IMG_I2S_IN_CH_CTL);
103 }
104 
105 static inline void img_i2s_in_disable(struct img_i2s_in *i2s)
106 {
107 	u32 reg;
108 
109 	reg = img_i2s_in_readl(i2s, IMG_I2S_IN_CTL);
110 	reg &= ~IMG_I2S_IN_CTL_ME_MASK;
111 	img_i2s_in_writel(i2s, reg, IMG_I2S_IN_CTL);
112 }
113 
114 static inline void img_i2s_in_enable(struct img_i2s_in *i2s)
115 {
116 	u32 reg;
117 
118 	reg = img_i2s_in_readl(i2s, IMG_I2S_IN_CTL);
119 	reg |= IMG_I2S_IN_CTL_ME_MASK;
120 	img_i2s_in_writel(i2s, reg, IMG_I2S_IN_CTL);
121 }
122 
123 static inline void img_i2s_in_flush(struct img_i2s_in *i2s)
124 {
125 	int i;
126 	u32 reg;
127 
128 	for (i = 0; i < i2s->active_channels; i++) {
129 		reg = img_i2s_in_ch_readl(i2s, i, IMG_I2S_IN_CH_CTL);
130 		reg |= IMG_I2S_IN_CH_CTL_FIFO_FLUSH_MASK;
131 		img_i2s_in_ch_writel(i2s, i, reg, IMG_I2S_IN_CH_CTL);
132 		reg &= ~IMG_I2S_IN_CH_CTL_FIFO_FLUSH_MASK;
133 		img_i2s_in_ch_writel(i2s, i, reg, IMG_I2S_IN_CH_CTL);
134 	}
135 }
136 
137 static int img_i2s_in_trigger(struct snd_pcm_substream *substream, int cmd,
138 	struct snd_soc_dai *dai)
139 {
140 	struct img_i2s_in *i2s = snd_soc_dai_get_drvdata(dai);
141 
142 	switch (cmd) {
143 	case SNDRV_PCM_TRIGGER_START:
144 	case SNDRV_PCM_TRIGGER_RESUME:
145 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
146 		img_i2s_in_enable(i2s);
147 		break;
148 
149 	case SNDRV_PCM_TRIGGER_STOP:
150 	case SNDRV_PCM_TRIGGER_SUSPEND:
151 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
152 		img_i2s_in_disable(i2s);
153 		break;
154 	default:
155 		return -EINVAL;
156 	}
157 
158 	return 0;
159 }
160 
161 static int img_i2s_in_check_rate(struct img_i2s_in *i2s,
162 		unsigned int sample_rate, unsigned int frame_size,
163 		unsigned int *bclk_filter_enable,
164 		unsigned int *bclk_filter_value)
165 {
166 	unsigned int bclk_freq, cur_freq;
167 
168 	bclk_freq = sample_rate * frame_size;
169 
170 	cur_freq = clk_get_rate(i2s->clk_sys);
171 
172 	if (cur_freq >= bclk_freq * 8) {
173 		*bclk_filter_enable = 1;
174 		*bclk_filter_value = 0;
175 	} else if (cur_freq >= bclk_freq * 7) {
176 		*bclk_filter_enable = 1;
177 		*bclk_filter_value = 1;
178 	} else if (cur_freq >= bclk_freq * 6) {
179 		*bclk_filter_enable = 0;
180 		*bclk_filter_value = 0;
181 	} else {
182 		dev_err(i2s->dev,
183 			"Sys clock rate %u insufficient for sample rate %u\n",
184 			cur_freq, sample_rate);
185 		return -EINVAL;
186 	}
187 
188 	return 0;
189 }
190 
191 static int img_i2s_in_hw_params(struct snd_pcm_substream *substream,
192 	struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
193 {
194 	struct img_i2s_in *i2s = snd_soc_dai_get_drvdata(dai);
195 	unsigned int rate, channels, i2s_channels, frame_size;
196 	unsigned int bclk_filter_enable, bclk_filter_value;
197 	int i, ret = 0;
198 	u32 reg, control_mask, chan_control_mask;
199 	u32 control_set = 0, chan_control_set = 0;
200 	snd_pcm_format_t format;
201 
202 	rate = params_rate(params);
203 	format = params_format(params);
204 	channels = params_channels(params);
205 	i2s_channels = channels / 2;
206 
207 	switch (format) {
208 	case SNDRV_PCM_FORMAT_S32_LE:
209 		frame_size = 64;
210 		chan_control_set |= IMG_I2S_IN_CH_CTL_SW_MASK;
211 		chan_control_set |= IMG_I2S_IN_CH_CTL_FW_MASK;
212 		chan_control_set |= IMG_I2S_IN_CH_CTL_PACKH_MASK;
213 		break;
214 	case SNDRV_PCM_FORMAT_S24_LE:
215 		frame_size = 64;
216 		chan_control_set |= IMG_I2S_IN_CH_CTL_SW_MASK;
217 		chan_control_set |= IMG_I2S_IN_CH_CTL_FW_MASK;
218 		break;
219 	case SNDRV_PCM_FORMAT_S16_LE:
220 		frame_size = 32;
221 		control_set |= IMG_I2S_IN_CTL_16PACK_MASK;
222 		chan_control_set |= IMG_I2S_IN_CH_CTL_16PACK_MASK;
223 		break;
224 	default:
225 		return -EINVAL;
226 	}
227 
228 	if ((channels < 2) ||
229 	    (channels > (i2s->max_i2s_chan * 2)) ||
230 	    (channels % 2))
231 		return -EINVAL;
232 
233 	control_set |= ((i2s_channels - 1) << IMG_I2S_IN_CTL_ACTIVE_CH_SHIFT);
234 
235 	ret = img_i2s_in_check_rate(i2s, rate, frame_size,
236 			&bclk_filter_enable, &bclk_filter_value);
237 	if (ret < 0)
238 		return ret;
239 
240 	if (bclk_filter_enable)
241 		chan_control_set |= IMG_I2S_IN_CH_CTL_FEN_MASK;
242 
243 	if (bclk_filter_value)
244 		chan_control_set |= IMG_I2S_IN_CH_CTL_FMODE_MASK;
245 
246 	control_mask = IMG_I2S_IN_CTL_16PACK_MASK |
247 		       IMG_I2S_IN_CTL_ACTIVE_CHAN_MASK;
248 
249 	chan_control_mask = IMG_I2S_IN_CH_CTL_16PACK_MASK |
250 			    IMG_I2S_IN_CH_CTL_FEN_MASK |
251 			    IMG_I2S_IN_CH_CTL_FMODE_MASK |
252 			    IMG_I2S_IN_CH_CTL_SW_MASK |
253 			    IMG_I2S_IN_CH_CTL_FW_MASK |
254 			    IMG_I2S_IN_CH_CTL_PACKH_MASK;
255 
256 	reg = img_i2s_in_readl(i2s, IMG_I2S_IN_CTL);
257 	reg = (reg & ~control_mask) | control_set;
258 	img_i2s_in_writel(i2s, reg, IMG_I2S_IN_CTL);
259 
260 	for (i = 0; i < i2s->active_channels; i++)
261 		img_i2s_in_ch_disable(i2s, i);
262 
263 	for (i = 0; i < i2s->max_i2s_chan; i++) {
264 		reg = img_i2s_in_ch_readl(i2s, i, IMG_I2S_IN_CH_CTL);
265 		reg = (reg & ~chan_control_mask) | chan_control_set;
266 		img_i2s_in_ch_writel(i2s, i, reg, IMG_I2S_IN_CH_CTL);
267 	}
268 
269 	i2s->active_channels = i2s_channels;
270 
271 	img_i2s_in_flush(i2s);
272 
273 	for (i = 0; i < i2s->active_channels; i++)
274 		img_i2s_in_ch_enable(i2s, i);
275 
276 	return 0;
277 }
278 
279 static int img_i2s_in_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
280 {
281 	struct img_i2s_in *i2s = snd_soc_dai_get_drvdata(dai);
282 	int i;
283 	u32 chan_control_mask, lrd_set = 0, blkp_set = 0, chan_control_set = 0;
284 	u32 reg;
285 
286 	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
287 	case SND_SOC_DAIFMT_NB_NF:
288 		lrd_set |= IMG_I2S_IN_CH_CTL_LRD_MASK;
289 		break;
290 	case SND_SOC_DAIFMT_NB_IF:
291 		break;
292 	case SND_SOC_DAIFMT_IB_NF:
293 		lrd_set |= IMG_I2S_IN_CH_CTL_LRD_MASK;
294 		blkp_set |= IMG_I2S_IN_CH_CTL_BLKP_MASK;
295 		break;
296 	case SND_SOC_DAIFMT_IB_IF:
297 		blkp_set |= IMG_I2S_IN_CH_CTL_BLKP_MASK;
298 		break;
299 	default:
300 		return -EINVAL;
301 	}
302 
303 	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
304 	case SND_SOC_DAIFMT_I2S:
305 		chan_control_set |= IMG_I2S_IN_CH_CTL_CLK_TRANS_MASK;
306 		break;
307 	case SND_SOC_DAIFMT_LEFT_J:
308 		break;
309 	default:
310 		return -EINVAL;
311 	}
312 
313 	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
314 	case SND_SOC_DAIFMT_CBM_CFM:
315 		break;
316 	default:
317 		return -EINVAL;
318 	}
319 
320 	chan_control_mask = IMG_I2S_IN_CH_CTL_CLK_TRANS_MASK;
321 
322 	for (i = 0; i < i2s->active_channels; i++)
323 		img_i2s_in_ch_disable(i2s, i);
324 
325 	/*
326 	 * BLKP and LRD must be set during separate register writes
327 	 */
328 	for (i = 0; i < i2s->max_i2s_chan; i++) {
329 		reg = img_i2s_in_ch_readl(i2s, i, IMG_I2S_IN_CH_CTL);
330 		reg = (reg & ~chan_control_mask) | chan_control_set;
331 		img_i2s_in_ch_writel(i2s, i, reg, IMG_I2S_IN_CH_CTL);
332 		reg = (reg & ~IMG_I2S_IN_CH_CTL_BLKP_MASK) | blkp_set;
333 		img_i2s_in_ch_writel(i2s, i, reg, IMG_I2S_IN_CH_CTL);
334 		reg = (reg & ~IMG_I2S_IN_CH_CTL_LRD_MASK) | lrd_set;
335 		img_i2s_in_ch_writel(i2s, i, reg, IMG_I2S_IN_CH_CTL);
336 	}
337 
338 	for (i = 0; i < i2s->active_channels; i++)
339 		img_i2s_in_ch_enable(i2s, i);
340 
341 	return 0;
342 }
343 
344 static const struct snd_soc_dai_ops img_i2s_in_dai_ops = {
345 	.trigger = img_i2s_in_trigger,
346 	.hw_params = img_i2s_in_hw_params,
347 	.set_fmt = img_i2s_in_set_fmt
348 };
349 
350 static int img_i2s_in_dai_probe(struct snd_soc_dai *dai)
351 {
352 	struct img_i2s_in *i2s = snd_soc_dai_get_drvdata(dai);
353 
354 	snd_soc_dai_init_dma_data(dai, NULL, &i2s->dma_data);
355 
356 	return 0;
357 }
358 
359 static const struct snd_soc_component_driver img_i2s_in_component = {
360 	.name = "img-i2s-in"
361 };
362 
363 static int img_i2s_in_dma_prepare_slave_config(struct snd_pcm_substream *st,
364 	struct snd_pcm_hw_params *params, struct dma_slave_config *sc)
365 {
366 	unsigned int i2s_channels = params_channels(params) / 2;
367 	struct snd_soc_pcm_runtime *rtd = st->private_data;
368 	struct snd_dmaengine_dai_dma_data *dma_data;
369 	int ret;
370 
371 	dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, st);
372 
373 	ret = snd_hwparams_to_dma_slave_config(st, params, sc);
374 	if (ret)
375 		return ret;
376 
377 	sc->src_addr = dma_data->addr;
378 	sc->src_addr_width = dma_data->addr_width;
379 	sc->src_maxburst = 4 * i2s_channels;
380 
381 	return 0;
382 }
383 
384 static const struct snd_dmaengine_pcm_config img_i2s_in_dma_config = {
385 	.prepare_slave_config = img_i2s_in_dma_prepare_slave_config
386 };
387 
388 static int img_i2s_in_probe(struct platform_device *pdev)
389 {
390 	struct img_i2s_in *i2s;
391 	struct resource *res;
392 	void __iomem *base;
393 	int ret, i;
394 	struct reset_control *rst;
395 	unsigned int max_i2s_chan_pow_2;
396 	struct device *dev = &pdev->dev;
397 
398 	i2s = devm_kzalloc(dev, sizeof(*i2s), GFP_KERNEL);
399 	if (!i2s)
400 		return -ENOMEM;
401 
402 	platform_set_drvdata(pdev, i2s);
403 
404 	i2s->dev = dev;
405 
406 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
407 	base = devm_ioremap_resource(dev, res);
408 	if (IS_ERR(base))
409 		return PTR_ERR(base);
410 
411 	i2s->base = base;
412 
413 	if (of_property_read_u32(pdev->dev.of_node, "img,i2s-channels",
414 			&i2s->max_i2s_chan)) {
415 		dev_err(dev, "No img,i2s-channels property\n");
416 		return -EINVAL;
417 	}
418 
419 	max_i2s_chan_pow_2 = 1 << get_count_order(i2s->max_i2s_chan);
420 
421 	i2s->channel_base = base + (max_i2s_chan_pow_2 * 0x20);
422 
423 	i2s->clk_sys = devm_clk_get(dev, "sys");
424 	if (IS_ERR(i2s->clk_sys)) {
425 		if (PTR_ERR(i2s->clk_sys) != -EPROBE_DEFER)
426 			dev_err(dev, "Failed to acquire clock 'sys'\n");
427 		return PTR_ERR(i2s->clk_sys);
428 	}
429 
430 	ret = clk_prepare_enable(i2s->clk_sys);
431 	if (ret)
432 		return ret;
433 
434 	i2s->active_channels = 1;
435 	i2s->dma_data.addr = res->start + IMG_I2S_IN_RX_FIFO;
436 	i2s->dma_data.addr_width = 4;
437 
438 	i2s->dai_driver.probe = img_i2s_in_dai_probe;
439 	i2s->dai_driver.capture.channels_min = 2;
440 	i2s->dai_driver.capture.channels_max = i2s->max_i2s_chan * 2;
441 	i2s->dai_driver.capture.rates = SNDRV_PCM_RATE_8000_192000;
442 	i2s->dai_driver.capture.formats = SNDRV_PCM_FMTBIT_S32_LE |
443 		SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE;
444 	i2s->dai_driver.ops = &img_i2s_in_dai_ops;
445 
446 	rst = devm_reset_control_get(dev, "rst");
447 	if (IS_ERR(rst)) {
448 		if (PTR_ERR(rst) == -EPROBE_DEFER) {
449 			ret = -EPROBE_DEFER;
450 			goto err_clk_disable;
451 		}
452 
453 		dev_dbg(dev, "No top level reset found\n");
454 
455 		img_i2s_in_disable(i2s);
456 
457 		for (i = 0; i < i2s->max_i2s_chan; i++)
458 			img_i2s_in_ch_disable(i2s, i);
459 	} else {
460 		reset_control_assert(rst);
461 		reset_control_deassert(rst);
462 	}
463 
464 	img_i2s_in_writel(i2s, 0, IMG_I2S_IN_CTL);
465 
466 	for (i = 0; i < i2s->max_i2s_chan; i++)
467 		img_i2s_in_ch_writel(i2s, i,
468 			(4 << IMG_I2S_IN_CH_CTL_CCDEL_SHIFT) |
469 			IMG_I2S_IN_CH_CTL_JUST_MASK |
470 			IMG_I2S_IN_CH_CTL_FW_MASK, IMG_I2S_IN_CH_CTL);
471 
472 	ret = devm_snd_soc_register_component(dev, &img_i2s_in_component,
473 						&i2s->dai_driver, 1);
474 	if (ret)
475 		goto err_clk_disable;
476 
477 	ret = devm_snd_dmaengine_pcm_register(dev, &img_i2s_in_dma_config, 0);
478 	if (ret)
479 		goto err_clk_disable;
480 
481 	return 0;
482 
483 err_clk_disable:
484 	clk_disable_unprepare(i2s->clk_sys);
485 
486 	return ret;
487 }
488 
489 static int img_i2s_in_dev_remove(struct platform_device *pdev)
490 {
491 	struct img_i2s_in *i2s = platform_get_drvdata(pdev);
492 
493 	clk_disable_unprepare(i2s->clk_sys);
494 
495 	return 0;
496 }
497 
498 static const struct of_device_id img_i2s_in_of_match[] = {
499 	{ .compatible = "img,i2s-in" },
500 	{}
501 };
502 MODULE_DEVICE_TABLE(of, img_i2s_in_of_match);
503 
504 static struct platform_driver img_i2s_in_driver = {
505 	.driver = {
506 		.name = "img-i2s-in",
507 		.of_match_table = img_i2s_in_of_match
508 	},
509 	.probe = img_i2s_in_probe,
510 	.remove = img_i2s_in_dev_remove
511 };
512 module_platform_driver(img_i2s_in_driver);
513 
514 MODULE_AUTHOR("Damien Horsley <Damien.Horsley@imgtec.com>");
515 MODULE_DESCRIPTION("IMG I2S Input Driver");
516 MODULE_LICENSE("GPL v2");
517