blob: 0389203f85608edfababa699086278d4c51ccb69 [file] [log] [blame]
Damien.Horsley14b947d2015-11-04 14:40:48 +00001/*
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
54struct 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
65static inline void img_i2s_in_writel(struct img_i2s_in *i2s, u32 val, u32 reg)
66{
67 writel(val, i2s->base + reg);
68}
69
70static inline u32 img_i2s_in_readl(struct img_i2s_in *i2s, u32 reg)
71{
72 return readl(i2s->base + reg);
73}
74
75static 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
81static 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
87static 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
96static 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
105static 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
114static 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
123static 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
137static 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
161static 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
191static 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
279static 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
344static 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
350static 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
359static const struct snd_soc_component_driver img_i2s_in_component = {
360 .name = "img-i2s-in"
361};
362
363static 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
384static const struct snd_dmaengine_pcm_config img_i2s_in_dma_config = {
385 .prepare_slave_config = img_i2s_in_dma_prepare_slave_config
386};
387
388static 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
483err_clk_disable:
484 clk_disable_unprepare(i2s->clk_sys);
485
486 return ret;
487}
488
489static 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
498static const struct of_device_id img_i2s_in_of_match[] = {
499 { .compatible = "img,i2s-in" },
500 {}
501};
502MODULE_DEVICE_TABLE(of, img_i2s_in_of_match);
503
504static 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};
512module_platform_driver(img_i2s_in_driver);
513
514MODULE_AUTHOR("Damien Horsley <Damien.Horsley@imgtec.com>");
515MODULE_DESCRIPTION("IMG I2S Input Driver");
516MODULE_LICENSE("GPL v2");