ASoC: wm5102: Remove unused platform data header
[deliverable/linux.git] / sound / soc / dwc / designware_i2s.c
CommitLineData
3a9cf8ef
RK
1/*
2 * ALSA SoC Synopsys I2S Audio Layer
3 *
4 * sound/soc/spear/designware_i2s.c
5 *
6 * Copyright (C) 2010 ST Microelectronics
7 * Rajeev Kumar <rajeev-dlh.kumar@st.com>
8 *
9 * This file is licensed under the terms of the GNU General Public
10 * License version 2. This program is licensed "as is" without any
11 * warranty of any kind, whether express or implied.
12 */
13
14#include <linux/clk.h>
15#include <linux/device.h>
16#include <linux/init.h>
17#include <linux/io.h>
18#include <linux/interrupt.h>
19#include <linux/module.h>
20#include <linux/slab.h>
21#include <sound/designware_i2s.h>
22#include <sound/pcm.h>
23#include <sound/pcm_params.h>
24#include <sound/soc.h>
25
26/* common register for all channel */
27#define IER 0x000
28#define IRER 0x004
29#define ITER 0x008
30#define CER 0x00C
31#define CCR 0x010
32#define RXFFR 0x014
33#define TXFFR 0x018
34
35/* I2STxRxRegisters for all channels */
36#define LRBR_LTHR(x) (0x40 * x + 0x020)
37#define RRBR_RTHR(x) (0x40 * x + 0x024)
38#define RER(x) (0x40 * x + 0x028)
39#define TER(x) (0x40 * x + 0x02C)
40#define RCR(x) (0x40 * x + 0x030)
41#define TCR(x) (0x40 * x + 0x034)
42#define ISR(x) (0x40 * x + 0x038)
43#define IMR(x) (0x40 * x + 0x03C)
44#define ROR(x) (0x40 * x + 0x040)
45#define TOR(x) (0x40 * x + 0x044)
46#define RFCR(x) (0x40 * x + 0x048)
47#define TFCR(x) (0x40 * x + 0x04C)
48#define RFF(x) (0x40 * x + 0x050)
49#define TFF(x) (0x40 * x + 0x054)
50
51/* I2SCOMPRegisters */
52#define I2S_COMP_PARAM_2 0x01F0
53#define I2S_COMP_PARAM_1 0x01F4
54#define I2S_COMP_VERSION 0x01F8
55#define I2S_COMP_TYPE 0x01FC
56
57#define MAX_CHANNEL_NUM 8
58#define MIN_CHANNEL_NUM 2
59
60struct dw_i2s_dev {
61 void __iomem *i2s_base;
62 struct clk *clk;
63 int active;
64 unsigned int capability;
65 struct device *dev;
66
67 /* data related to DMA transfers b/w i2s and DMAC */
68 struct i2s_dma_data play_dma_data;
69 struct i2s_dma_data capture_dma_data;
70 struct i2s_clk_config_data config;
71 int (*i2s_clk_cfg)(struct i2s_clk_config_data *config);
72};
73
74static inline void i2s_write_reg(void *io_base, int reg, u32 val)
75{
76 writel(val, io_base + reg);
77}
78
79static inline u32 i2s_read_reg(void *io_base, int reg)
80{
81 return readl(io_base + reg);
82}
83
84static inline void i2s_disable_channels(struct dw_i2s_dev *dev, u32 stream)
85{
86 u32 i = 0;
87
88 if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
89 for (i = 0; i < 4; i++)
90 i2s_write_reg(dev->i2s_base, TER(i), 0);
91 } else {
92 for (i = 0; i < 4; i++)
93 i2s_write_reg(dev->i2s_base, RER(i), 0);
94 }
95}
96
97static inline void i2s_clear_irqs(struct dw_i2s_dev *dev, u32 stream)
98{
99 u32 i = 0;
100
101 if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
102 for (i = 0; i < 4; i++)
103 i2s_write_reg(dev->i2s_base, TOR(i), 0);
104 } else {
105 for (i = 0; i < 4; i++)
106 i2s_write_reg(dev->i2s_base, ROR(i), 0);
107 }
108}
109
110void i2s_start(struct dw_i2s_dev *dev, struct snd_pcm_substream *substream)
111{
112
113 i2s_write_reg(dev->i2s_base, IER, 1);
114
115 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
116 i2s_write_reg(dev->i2s_base, ITER, 1);
117 else
118 i2s_write_reg(dev->i2s_base, IRER, 1);
119
120 i2s_write_reg(dev->i2s_base, CER, 1);
121}
122
123static void i2s_stop(struct dw_i2s_dev *dev,
124 struct snd_pcm_substream *substream)
125{
126 u32 i = 0, irq;
127
128 i2s_clear_irqs(dev, substream->stream);
129 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
130 i2s_write_reg(dev->i2s_base, ITER, 0);
131
132 for (i = 0; i < 4; i++) {
133 irq = i2s_read_reg(dev->i2s_base, IMR(i));
134 i2s_write_reg(dev->i2s_base, IMR(i), irq | 0x30);
135 }
136 } else {
137 i2s_write_reg(dev->i2s_base, IRER, 0);
138
139 for (i = 0; i < 4; i++) {
140 irq = i2s_read_reg(dev->i2s_base, IMR(i));
141 i2s_write_reg(dev->i2s_base, IMR(i), irq | 0x03);
142 }
143 }
144
145 if (!dev->active) {
146 i2s_write_reg(dev->i2s_base, CER, 0);
147 i2s_write_reg(dev->i2s_base, IER, 0);
148 }
149}
150
151static int dw_i2s_startup(struct snd_pcm_substream *substream,
152 struct snd_soc_dai *cpu_dai)
153{
154 struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(cpu_dai);
155 struct i2s_dma_data *dma_data = NULL;
156
157 if (!(dev->capability & DWC_I2S_RECORD) &&
158 (substream->stream == SNDRV_PCM_STREAM_CAPTURE))
159 return -EINVAL;
160
161 if (!(dev->capability & DWC_I2S_PLAY) &&
162 (substream->stream == SNDRV_PCM_STREAM_PLAYBACK))
163 return -EINVAL;
164
165 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
166 dma_data = &dev->play_dma_data;
167 else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
168 dma_data = &dev->capture_dma_data;
169
170 snd_soc_dai_set_dma_data(cpu_dai, substream, (void *)dma_data);
171
172 return 0;
173}
174
175static int dw_i2s_hw_params(struct snd_pcm_substream *substream,
176 struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
177{
178 struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
179 struct i2s_clk_config_data *config = &dev->config;
180 u32 ccr, xfer_resolution, ch_reg, irq;
181 int ret;
182
183 switch (params_format(params)) {
184 case SNDRV_PCM_FORMAT_S16_LE:
185 config->data_width = 16;
186 ccr = 0x00;
187 xfer_resolution = 0x02;
188 break;
189
190 case SNDRV_PCM_FORMAT_S24_LE:
191 config->data_width = 24;
192 ccr = 0x08;
193 xfer_resolution = 0x04;
194 break;
195
196 case SNDRV_PCM_FORMAT_S32_LE:
197 config->data_width = 32;
198 ccr = 0x10;
199 xfer_resolution = 0x05;
200 break;
201
202 default:
203 dev_err(dev->dev, "designware-i2s: unsuppted PCM fmt");
204 return -EINVAL;
205 }
206
207 config->chan_nr = params_channels(params);
208
209 switch (config->chan_nr) {
210 case EIGHT_CHANNEL_SUPPORT:
211 ch_reg = 3;
212 case SIX_CHANNEL_SUPPORT:
213 ch_reg = 2;
214 case FOUR_CHANNEL_SUPPORT:
215 ch_reg = 1;
216 case TWO_CHANNEL_SUPPORT:
217 ch_reg = 0;
218 break;
219 default:
220 dev_err(dev->dev, "channel not supported\n");
221 }
222
223 i2s_disable_channels(dev, substream->stream);
224
225 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
226 i2s_write_reg(dev->i2s_base, TCR(ch_reg), xfer_resolution);
227 i2s_write_reg(dev->i2s_base, TFCR(ch_reg), 0x02);
228 irq = i2s_read_reg(dev->i2s_base, IMR(ch_reg));
229 i2s_write_reg(dev->i2s_base, IMR(ch_reg), irq & ~0x30);
230 i2s_write_reg(dev->i2s_base, TER(ch_reg), 1);
231 } else {
232 i2s_write_reg(dev->i2s_base, RCR(ch_reg), xfer_resolution);
233 i2s_write_reg(dev->i2s_base, RFCR(ch_reg), 0x07);
234 irq = i2s_read_reg(dev->i2s_base, IMR(ch_reg));
235 i2s_write_reg(dev->i2s_base, IMR(ch_reg), irq & ~0x03);
236 i2s_write_reg(dev->i2s_base, RER(ch_reg), 1);
237 }
238
239 i2s_write_reg(dev->i2s_base, CCR, ccr);
240
241 config->sample_rate = params_rate(params);
242
243 if (!dev->i2s_clk_cfg)
244 return -EINVAL;
245
246 ret = dev->i2s_clk_cfg(config);
247 if (ret < 0) {
248 dev_err(dev->dev, "runtime audio clk config fail\n");
249 return ret;
250 }
251
252 return 0;
253}
254
255static void dw_i2s_shutdown(struct snd_pcm_substream *substream,
256 struct snd_soc_dai *dai)
257{
258 snd_soc_dai_set_dma_data(dai, substream, NULL);
259}
260
261static int dw_i2s_trigger(struct snd_pcm_substream *substream,
262 int cmd, struct snd_soc_dai *dai)
263{
264 struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
265 int ret = 0;
266
267 switch (cmd) {
268 case SNDRV_PCM_TRIGGER_START:
269 case SNDRV_PCM_TRIGGER_RESUME:
270 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
271 dev->active++;
272 i2s_start(dev, substream);
273 break;
274
275 case SNDRV_PCM_TRIGGER_STOP:
276 case SNDRV_PCM_TRIGGER_SUSPEND:
277 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
278 dev->active--;
279 i2s_stop(dev, substream);
280 break;
281 default:
282 ret = -EINVAL;
283 break;
284 }
285 return ret;
286}
287
288static struct snd_soc_dai_ops dw_i2s_dai_ops = {
289 .startup = dw_i2s_startup,
290 .shutdown = dw_i2s_shutdown,
291 .hw_params = dw_i2s_hw_params,
292 .trigger = dw_i2s_trigger,
293};
294
295#ifdef CONFIG_PM
296
297static int dw_i2s_suspend(struct snd_soc_dai *dai)
298{
299 struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
300
301 clk_disable(dev->clk);
302 return 0;
303}
304
305static int dw_i2s_resume(struct snd_soc_dai *dai)
306{
307 struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
308
309 clk_enable(dev->clk);
310 return 0;
311}
312
313#else
314#define dw_i2s_suspend NULL
315#define dw_i2s_resume NULL
316#endif
317
318static int dw_i2s_probe(struct platform_device *pdev)
319{
320 const struct i2s_platform_data *pdata = pdev->dev.platform_data;
321 struct dw_i2s_dev *dev;
322 struct resource *res;
323 int ret;
324 unsigned int cap;
325 struct snd_soc_dai_driver *dw_i2s_dai;
326
327 if (!pdata) {
328 dev_err(&pdev->dev, "Invalid platform data\n");
329 return -EINVAL;
330 }
331
332 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
333 if (!res) {
334 dev_err(&pdev->dev, "no i2s resource defined\n");
335 return -ENODEV;
336 }
337
338 if (!devm_request_mem_region(&pdev->dev, res->start,
339 resource_size(res), pdev->name)) {
340 dev_err(&pdev->dev, "i2s region already claimed\n");
341 return -EBUSY;
342 }
343
344 dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
345 if (!dev) {
346 dev_warn(&pdev->dev, "kzalloc fail\n");
347 return -ENOMEM;
348 }
349
350 dev->i2s_base = devm_ioremap(&pdev->dev, res->start,
351 resource_size(res));
352 if (!dev->i2s_base) {
353 dev_err(&pdev->dev, "ioremap fail for i2s_region\n");
354 return -ENOMEM;
355 }
356
357 cap = pdata->cap;
358 dev->capability = cap;
359 dev->i2s_clk_cfg = pdata->i2s_clk_cfg;
360
361 /* Set DMA slaves info */
362
363 dev->play_dma_data.data = pdata->play_dma_data;
364 dev->capture_dma_data.data = pdata->capture_dma_data;
365 dev->play_dma_data.addr = res->start + I2S_TXDMA;
366 dev->capture_dma_data.addr = res->start + I2S_RXDMA;
367 dev->play_dma_data.max_burst = 16;
368 dev->capture_dma_data.max_burst = 16;
369 dev->play_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
370 dev->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
371 dev->play_dma_data.filter = pdata->filter;
372 dev->capture_dma_data.filter = pdata->filter;
373
374 dev->clk = clk_get(&pdev->dev, NULL);
375 if (IS_ERR(dev->clk))
376 return PTR_ERR(dev->clk);
377
378 ret = clk_enable(dev->clk);
379 if (ret < 0)
380 goto err_clk_put;
381
382 dw_i2s_dai = devm_kzalloc(&pdev->dev, sizeof(*dw_i2s_dai), GFP_KERNEL);
383 if (!dw_i2s_dai) {
384 dev_err(&pdev->dev, "mem allocation failed for dai driver\n");
385 ret = -ENOMEM;
386 goto err_clk_disable;
387 }
388
389 if (cap & DWC_I2S_PLAY) {
390 dev_dbg(&pdev->dev, " SPEAr: play supported\n");
391 dw_i2s_dai->playback.channels_min = MIN_CHANNEL_NUM;
392 dw_i2s_dai->playback.channels_max = pdata->channel;
393 dw_i2s_dai->playback.formats = pdata->snd_fmts;
394 dw_i2s_dai->playback.rates = pdata->snd_rates;
395 }
396
397 if (cap & DWC_I2S_RECORD) {
398 dev_dbg(&pdev->dev, "SPEAr: record supported\n");
399 dw_i2s_dai->capture.channels_min = MIN_CHANNEL_NUM;
400 dw_i2s_dai->capture.channels_max = pdata->channel;
401 dw_i2s_dai->capture.formats = pdata->snd_fmts;
402 dw_i2s_dai->capture.rates = pdata->snd_rates;
403 }
404
405 dw_i2s_dai->ops = &dw_i2s_dai_ops;
406 dw_i2s_dai->suspend = dw_i2s_suspend;
407 dw_i2s_dai->resume = dw_i2s_resume;
408
409 dev->dev = &pdev->dev;
410 dev_set_drvdata(&pdev->dev, dev);
411 ret = snd_soc_register_dai(&pdev->dev, dw_i2s_dai);
412 if (ret != 0) {
413 dev_err(&pdev->dev, "not able to register dai\n");
414 goto err_set_drvdata;
415 }
416
417 return 0;
418
419err_set_drvdata:
420 dev_set_drvdata(&pdev->dev, NULL);
421err_clk_disable:
422 clk_disable(dev->clk);
423err_clk_put:
424 clk_put(dev->clk);
425 return ret;
426}
427
428static int dw_i2s_remove(struct platform_device *pdev)
429{
430 struct dw_i2s_dev *dev = dev_get_drvdata(&pdev->dev);
431
432 snd_soc_unregister_dai(&pdev->dev);
433 dev_set_drvdata(&pdev->dev, NULL);
434
435 clk_put(dev->clk);
436
437 return 0;
438}
439
440static struct platform_driver dw_i2s_driver = {
441 .probe = dw_i2s_probe,
442 .remove = dw_i2s_remove,
443 .driver = {
444 .name = "designware-i2s",
445 .owner = THIS_MODULE,
446 },
447};
448
449module_platform_driver(dw_i2s_driver);
450
451MODULE_AUTHOR("Rajeev Kumar <rajeev-dlh.kumar@st.com>");
452MODULE_DESCRIPTION("DESIGNWARE I2S SoC Interface");
453MODULE_LICENSE("GPL");
454MODULE_ALIAS("platform:designware_i2s");
This page took 0.059952 seconds and 5 git commands to generate.