blob: a6327cc086c0e0139e5527359fb68289eb6097f2 [file] [log] [blame]
Rajeev Kumar3a9cf8e2012-06-21 15:54:51 +05301/*
2 * ALSA SoC Synopsys I2S Audio Layer
3 *
Rajeev Kumar22a4adf2013-06-11 09:29:08 +05304 * sound/soc/dwc/designware_i2s.c
Rajeev Kumar3a9cf8e2012-06-21 15:54:51 +05305 *
6 * Copyright (C) 2010 ST Microelectronics
Rajeev Kumar9a302c32014-09-05 16:47:04 +05307 * Rajeev Kumar <rajeevkumar.linux@gmail.com>
Rajeev Kumar3a9cf8e2012-06-21 15:54:51 +05308 *
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
Andrew Jacksonb226efe2014-12-30 10:55:45 +000057/*
58 * Component parameter register fields - define the I2S block's
59 * configuration.
60 */
61#define COMP1_TX_WORDSIZE_3(r) (((r) & GENMASK(27, 25)) >> 25)
62#define COMP1_TX_WORDSIZE_2(r) (((r) & GENMASK(24, 22)) >> 22)
63#define COMP1_TX_WORDSIZE_1(r) (((r) & GENMASK(21, 19)) >> 19)
64#define COMP1_TX_WORDSIZE_0(r) (((r) & GENMASK(18, 16)) >> 16)
65#define COMP1_TX_CHANNELS(r) (((r) & GENMASK(10, 9)) >> 9)
66#define COMP1_RX_CHANNELS(r) (((r) & GENMASK(8, 7)) >> 7)
67#define COMP1_RX_ENABLED(r) (((r) & BIT(6)) >> 6)
68#define COMP1_TX_ENABLED(r) (((r) & BIT(5)) >> 5)
69#define COMP1_MODE_EN(r) (((r) & BIT(4)) >> 4)
70#define COMP1_FIFO_DEPTH_GLOBAL(r) (((r) & GENMASK(3, 2)) >> 2)
71#define COMP1_APB_DATA_WIDTH(r) (((r) & GENMASK(1, 0)) >> 0)
72
73#define COMP2_RX_WORDSIZE_3(r) (((r) & GENMASK(12, 10)) >> 10)
74#define COMP2_RX_WORDSIZE_2(r) (((r) & GENMASK(9, 7)) >> 7)
75#define COMP2_RX_WORDSIZE_1(r) (((r) & GENMASK(5, 3)) >> 3)
76#define COMP2_RX_WORDSIZE_0(r) (((r) & GENMASK(2, 0)) >> 0)
77
78/* Number of entries in WORDSIZE and DATA_WIDTH parameter registers */
79#define COMP_MAX_WORDSIZE (1 << 3)
80#define COMP_MAX_DATA_WIDTH (1 << 2)
81
Rajeev Kumar3a9cf8e2012-06-21 15:54:51 +053082#define MAX_CHANNEL_NUM 8
83#define MIN_CHANNEL_NUM 2
84
85struct dw_i2s_dev {
86 void __iomem *i2s_base;
87 struct clk *clk;
88 int active;
89 unsigned int capability;
90 struct device *dev;
91
92 /* data related to DMA transfers b/w i2s and DMAC */
93 struct i2s_dma_data play_dma_data;
94 struct i2s_dma_data capture_dma_data;
95 struct i2s_clk_config_data config;
96 int (*i2s_clk_cfg)(struct i2s_clk_config_data *config);
97};
98
Mark Brown6b4a21b2012-06-28 13:11:47 +010099static inline void i2s_write_reg(void __iomem *io_base, int reg, u32 val)
Rajeev Kumar3a9cf8e2012-06-21 15:54:51 +0530100{
101 writel(val, io_base + reg);
102}
103
Mark Brown6b4a21b2012-06-28 13:11:47 +0100104static inline u32 i2s_read_reg(void __iomem *io_base, int reg)
Rajeev Kumar3a9cf8e2012-06-21 15:54:51 +0530105{
106 return readl(io_base + reg);
107}
108
109static inline void i2s_disable_channels(struct dw_i2s_dev *dev, u32 stream)
110{
111 u32 i = 0;
112
113 if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
114 for (i = 0; i < 4; i++)
115 i2s_write_reg(dev->i2s_base, TER(i), 0);
116 } else {
117 for (i = 0; i < 4; i++)
118 i2s_write_reg(dev->i2s_base, RER(i), 0);
119 }
120}
121
122static inline void i2s_clear_irqs(struct dw_i2s_dev *dev, u32 stream)
123{
124 u32 i = 0;
125
126 if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
127 for (i = 0; i < 4; i++)
128 i2s_write_reg(dev->i2s_base, TOR(i), 0);
129 } else {
130 for (i = 0; i < 4; i++)
131 i2s_write_reg(dev->i2s_base, ROR(i), 0);
132 }
133}
134
Mark Brown1520ffd2012-07-04 19:04:11 +0100135static void i2s_start(struct dw_i2s_dev *dev,
136 struct snd_pcm_substream *substream)
Rajeev Kumar3a9cf8e2012-06-21 15:54:51 +0530137{
138
139 i2s_write_reg(dev->i2s_base, IER, 1);
140
141 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
142 i2s_write_reg(dev->i2s_base, ITER, 1);
143 else
144 i2s_write_reg(dev->i2s_base, IRER, 1);
145
146 i2s_write_reg(dev->i2s_base, CER, 1);
147}
148
149static void i2s_stop(struct dw_i2s_dev *dev,
150 struct snd_pcm_substream *substream)
151{
152 u32 i = 0, irq;
153
154 i2s_clear_irqs(dev, substream->stream);
155 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
156 i2s_write_reg(dev->i2s_base, ITER, 0);
157
158 for (i = 0; i < 4; i++) {
159 irq = i2s_read_reg(dev->i2s_base, IMR(i));
160 i2s_write_reg(dev->i2s_base, IMR(i), irq | 0x30);
161 }
162 } else {
163 i2s_write_reg(dev->i2s_base, IRER, 0);
164
165 for (i = 0; i < 4; i++) {
166 irq = i2s_read_reg(dev->i2s_base, IMR(i));
167 i2s_write_reg(dev->i2s_base, IMR(i), irq | 0x03);
168 }
169 }
170
171 if (!dev->active) {
172 i2s_write_reg(dev->i2s_base, CER, 0);
173 i2s_write_reg(dev->i2s_base, IER, 0);
174 }
175}
176
177static int dw_i2s_startup(struct snd_pcm_substream *substream,
178 struct snd_soc_dai *cpu_dai)
179{
180 struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(cpu_dai);
181 struct i2s_dma_data *dma_data = NULL;
182
183 if (!(dev->capability & DWC_I2S_RECORD) &&
184 (substream->stream == SNDRV_PCM_STREAM_CAPTURE))
185 return -EINVAL;
186
187 if (!(dev->capability & DWC_I2S_PLAY) &&
188 (substream->stream == SNDRV_PCM_STREAM_PLAYBACK))
189 return -EINVAL;
190
191 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
192 dma_data = &dev->play_dma_data;
193 else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
194 dma_data = &dev->capture_dma_data;
195
196 snd_soc_dai_set_dma_data(cpu_dai, substream, (void *)dma_data);
197
198 return 0;
199}
200
201static int dw_i2s_hw_params(struct snd_pcm_substream *substream,
202 struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
203{
204 struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
205 struct i2s_clk_config_data *config = &dev->config;
206 u32 ccr, xfer_resolution, ch_reg, irq;
207 int ret;
208
209 switch (params_format(params)) {
210 case SNDRV_PCM_FORMAT_S16_LE:
211 config->data_width = 16;
212 ccr = 0x00;
213 xfer_resolution = 0x02;
214 break;
215
216 case SNDRV_PCM_FORMAT_S24_LE:
217 config->data_width = 24;
218 ccr = 0x08;
219 xfer_resolution = 0x04;
220 break;
221
222 case SNDRV_PCM_FORMAT_S32_LE:
223 config->data_width = 32;
224 ccr = 0x10;
225 xfer_resolution = 0x05;
226 break;
227
228 default:
229 dev_err(dev->dev, "designware-i2s: unsuppted PCM fmt");
230 return -EINVAL;
231 }
232
233 config->chan_nr = params_channels(params);
234
235 switch (config->chan_nr) {
236 case EIGHT_CHANNEL_SUPPORT:
237 ch_reg = 3;
Dan Carpenter0099d242013-01-25 09:43:43 +0300238 break;
Rajeev Kumar3a9cf8e2012-06-21 15:54:51 +0530239 case SIX_CHANNEL_SUPPORT:
240 ch_reg = 2;
Dan Carpenter0099d242013-01-25 09:43:43 +0300241 break;
Rajeev Kumar3a9cf8e2012-06-21 15:54:51 +0530242 case FOUR_CHANNEL_SUPPORT:
243 ch_reg = 1;
Dan Carpenter0099d242013-01-25 09:43:43 +0300244 break;
Rajeev Kumar3a9cf8e2012-06-21 15:54:51 +0530245 case TWO_CHANNEL_SUPPORT:
246 ch_reg = 0;
247 break;
248 default:
249 dev_err(dev->dev, "channel not supported\n");
Dan Carpenter0099d242013-01-25 09:43:43 +0300250 return -EINVAL;
Rajeev Kumar3a9cf8e2012-06-21 15:54:51 +0530251 }
252
253 i2s_disable_channels(dev, substream->stream);
254
255 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
256 i2s_write_reg(dev->i2s_base, TCR(ch_reg), xfer_resolution);
257 i2s_write_reg(dev->i2s_base, TFCR(ch_reg), 0x02);
258 irq = i2s_read_reg(dev->i2s_base, IMR(ch_reg));
259 i2s_write_reg(dev->i2s_base, IMR(ch_reg), irq & ~0x30);
260 i2s_write_reg(dev->i2s_base, TER(ch_reg), 1);
261 } else {
262 i2s_write_reg(dev->i2s_base, RCR(ch_reg), xfer_resolution);
263 i2s_write_reg(dev->i2s_base, RFCR(ch_reg), 0x07);
264 irq = i2s_read_reg(dev->i2s_base, IMR(ch_reg));
265 i2s_write_reg(dev->i2s_base, IMR(ch_reg), irq & ~0x03);
266 i2s_write_reg(dev->i2s_base, RER(ch_reg), 1);
267 }
268
269 i2s_write_reg(dev->i2s_base, CCR, ccr);
270
271 config->sample_rate = params_rate(params);
272
273 if (!dev->i2s_clk_cfg)
274 return -EINVAL;
275
276 ret = dev->i2s_clk_cfg(config);
277 if (ret < 0) {
278 dev_err(dev->dev, "runtime audio clk config fail\n");
279 return ret;
280 }
281
282 return 0;
283}
284
285static void dw_i2s_shutdown(struct snd_pcm_substream *substream,
286 struct snd_soc_dai *dai)
287{
288 snd_soc_dai_set_dma_data(dai, substream, NULL);
289}
290
291static int dw_i2s_trigger(struct snd_pcm_substream *substream,
292 int cmd, struct snd_soc_dai *dai)
293{
294 struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
295 int ret = 0;
296
297 switch (cmd) {
298 case SNDRV_PCM_TRIGGER_START:
299 case SNDRV_PCM_TRIGGER_RESUME:
300 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
301 dev->active++;
302 i2s_start(dev, substream);
303 break;
304
305 case SNDRV_PCM_TRIGGER_STOP:
306 case SNDRV_PCM_TRIGGER_SUSPEND:
307 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
308 dev->active--;
309 i2s_stop(dev, substream);
310 break;
311 default:
312 ret = -EINVAL;
313 break;
314 }
315 return ret;
316}
317
318static struct snd_soc_dai_ops dw_i2s_dai_ops = {
319 .startup = dw_i2s_startup,
320 .shutdown = dw_i2s_shutdown,
321 .hw_params = dw_i2s_hw_params,
322 .trigger = dw_i2s_trigger,
323};
324
Kuninori Morimoto92eaa322013-03-21 03:31:30 -0700325static const struct snd_soc_component_driver dw_i2s_component = {
326 .name = "dw-i2s",
327};
328
Rajeev Kumar3a9cf8e2012-06-21 15:54:51 +0530329#ifdef CONFIG_PM
330
331static int dw_i2s_suspend(struct snd_soc_dai *dai)
332{
333 struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
334
335 clk_disable(dev->clk);
336 return 0;
337}
338
339static int dw_i2s_resume(struct snd_soc_dai *dai)
340{
341 struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
342
343 clk_enable(dev->clk);
344 return 0;
345}
346
347#else
348#define dw_i2s_suspend NULL
349#define dw_i2s_resume NULL
350#endif
351
Andrew Jacksonb226efe2014-12-30 10:55:45 +0000352/*
353 * The following tables allow a direct lookup of various parameters
354 * defined in the I2S block's configuration in terms of sound system
355 * parameters. Each table is sized to the number of entries possible
356 * according to the number of configuration bits describing an I2S
357 * block parameter.
358 */
359
360/* Width of (DMA) bus */
361static const u32 bus_widths[COMP_MAX_DATA_WIDTH] = {
362 DMA_SLAVE_BUSWIDTH_1_BYTE,
363 DMA_SLAVE_BUSWIDTH_2_BYTES,
364 DMA_SLAVE_BUSWIDTH_4_BYTES,
365 DMA_SLAVE_BUSWIDTH_UNDEFINED
366};
367
368/* PCM format to support channel resolution */
369static const u32 formats[COMP_MAX_WORDSIZE] = {
370 SNDRV_PCM_FMTBIT_S16_LE,
371 SNDRV_PCM_FMTBIT_S16_LE,
372 SNDRV_PCM_FMTBIT_S24_LE,
373 SNDRV_PCM_FMTBIT_S24_LE,
374 SNDRV_PCM_FMTBIT_S32_LE,
375 0,
376 0,
377 0
378};
379
380static int dw_configure_dai_by_pd(struct dw_i2s_dev *dev,
Andrew Jacksonafa86032014-12-19 16:18:07 +0000381 struct snd_soc_dai_driver *dw_i2s_dai,
382 struct resource *res,
383 const struct i2s_platform_data *pdata)
384{
Andrew Jacksonb226efe2014-12-30 10:55:45 +0000385 /*
386 * Read component parameter registers to extract
387 * the I2S block's configuration.
388 */
389 u32 comp1 = i2s_read_reg(dev->i2s_base, I2S_COMP_PARAM_1);
390 u32 comp2 = i2s_read_reg(dev->i2s_base, I2S_COMP_PARAM_2);
391 u32 idx = COMP1_APB_DATA_WIDTH(comp1);
392
393 if (WARN_ON(idx >= ARRAY_SIZE(bus_widths)))
394 return -EINVAL;
395
Andrew Jacksonafa86032014-12-19 16:18:07 +0000396 /* Set DMA slaves info */
397
398 dev->play_dma_data.data = pdata->play_dma_data;
399 dev->capture_dma_data.data = pdata->capture_dma_data;
400 dev->play_dma_data.addr = res->start + I2S_TXDMA;
401 dev->capture_dma_data.addr = res->start + I2S_RXDMA;
402 dev->play_dma_data.max_burst = 16;
403 dev->capture_dma_data.max_burst = 16;
Andrew Jacksonb226efe2014-12-30 10:55:45 +0000404 dev->play_dma_data.addr_width = bus_widths[idx];
405 dev->capture_dma_data.addr_width = bus_widths[idx];
Andrew Jacksonafa86032014-12-19 16:18:07 +0000406 dev->play_dma_data.filter = pdata->filter;
407 dev->capture_dma_data.filter = pdata->filter;
408
Andrew Jacksonb226efe2014-12-30 10:55:45 +0000409 if (COMP1_TX_ENABLED(comp1)) {
Andrew Jacksonafa86032014-12-19 16:18:07 +0000410 dev_dbg(dev->dev, " designware: play supported\n");
Andrew Jacksonb226efe2014-12-30 10:55:45 +0000411 idx = COMP1_TX_WORDSIZE_0(comp1);
412 if (WARN_ON(idx >= ARRAY_SIZE(formats)))
413 return -EINVAL;
Andrew Jacksonafa86032014-12-19 16:18:07 +0000414 dw_i2s_dai->playback.channels_min = MIN_CHANNEL_NUM;
Andrew Jacksonb226efe2014-12-30 10:55:45 +0000415 dw_i2s_dai->playback.channels_max =
416 1 << (COMP1_TX_CHANNELS(comp1) + 1);
417 dw_i2s_dai->playback.formats = formats[idx];
Andrew Jacksonafa86032014-12-19 16:18:07 +0000418 dw_i2s_dai->playback.rates = pdata->snd_rates;
419 }
420
Andrew Jacksonb226efe2014-12-30 10:55:45 +0000421 if (COMP1_RX_ENABLED(comp1)) {
Andrew Jacksonafa86032014-12-19 16:18:07 +0000422 dev_dbg(dev->dev, "designware: record supported\n");
Andrew Jacksonb226efe2014-12-30 10:55:45 +0000423 idx = COMP2_RX_WORDSIZE_0(comp2);
424 if (WARN_ON(idx >= ARRAY_SIZE(formats)))
425 return -EINVAL;
Andrew Jacksonafa86032014-12-19 16:18:07 +0000426 dw_i2s_dai->capture.channels_min = MIN_CHANNEL_NUM;
Andrew Jacksonb226efe2014-12-30 10:55:45 +0000427 dw_i2s_dai->capture.channels_max =
428 1 << (COMP1_RX_CHANNELS(comp1) + 1);
429 dw_i2s_dai->capture.formats = formats[idx];
Andrew Jacksonafa86032014-12-19 16:18:07 +0000430 dw_i2s_dai->capture.rates = pdata->snd_rates;
431 }
Andrew Jacksonb226efe2014-12-30 10:55:45 +0000432
433 return 0;
Andrew Jacksonafa86032014-12-19 16:18:07 +0000434}
435
Rajeev Kumar3a9cf8e2012-06-21 15:54:51 +0530436static int dw_i2s_probe(struct platform_device *pdev)
437{
438 const struct i2s_platform_data *pdata = pdev->dev.platform_data;
439 struct dw_i2s_dev *dev;
440 struct resource *res;
441 int ret;
Rajeev Kumar3a9cf8e2012-06-21 15:54:51 +0530442 struct snd_soc_dai_driver *dw_i2s_dai;
443
444 if (!pdata) {
445 dev_err(&pdev->dev, "Invalid platform data\n");
446 return -EINVAL;
447 }
448
Rajeev Kumar3a9cf8e2012-06-21 15:54:51 +0530449 dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
450 if (!dev) {
451 dev_warn(&pdev->dev, "kzalloc fail\n");
452 return -ENOMEM;
453 }
454
Andrew Jacksonb163be42014-12-03 16:38:46 +0000455 dw_i2s_dai = devm_kzalloc(&pdev->dev, sizeof(*dw_i2s_dai), GFP_KERNEL);
Andrew Jacksonbe334652014-12-12 09:25:00 +0000456 if (!dw_i2s_dai)
Rajeev Kumar3a9cf8e2012-06-21 15:54:51 +0530457 return -ENOMEM;
Rajeev Kumar3a9cf8e2012-06-21 15:54:51 +0530458
Andrew Jacksonb163be42014-12-03 16:38:46 +0000459 dw_i2s_dai->ops = &dw_i2s_dai_ops;
460 dw_i2s_dai->suspend = dw_i2s_suspend;
461 dw_i2s_dai->resume = dw_i2s_resume;
462
463 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
Andrew Jacksonb163be42014-12-03 16:38:46 +0000464 dev->i2s_base = devm_ioremap_resource(&pdev->dev, res);
Andrew Jacksonbe334652014-12-12 09:25:00 +0000465 if (IS_ERR(dev->i2s_base))
Andrew Jacksonb163be42014-12-03 16:38:46 +0000466 return PTR_ERR(dev->i2s_base);
Andrew Jacksonb163be42014-12-03 16:38:46 +0000467
Andrew Jacksonafa86032014-12-19 16:18:07 +0000468 dev->dev = &pdev->dev;
Andrew Jacksonb226efe2014-12-30 10:55:45 +0000469 ret = dw_configure_dai_by_pd(dev, dw_i2s_dai, res, pdata);
470 if (ret < 0)
471 return ret;
Andrew Jacksonafa86032014-12-19 16:18:07 +0000472
473 dev->capability = pdata->cap;
Rajeev Kumar3a9cf8e2012-06-21 15:54:51 +0530474 dev->i2s_clk_cfg = pdata->i2s_clk_cfg;
Andrew Jacksona56257c62014-12-30 10:55:43 +0000475 dev->clk = devm_clk_get(&pdev->dev, NULL);
Rajeev Kumar3a9cf8e2012-06-21 15:54:51 +0530476 if (IS_ERR(dev->clk))
477 return PTR_ERR(dev->clk);
478
Andrew Jackson3a192722014-12-30 10:55:44 +0000479 ret = clk_prepare_enable(dev->clk);
Rajeev Kumar3a9cf8e2012-06-21 15:54:51 +0530480 if (ret < 0)
Andrew Jacksona56257c62014-12-30 10:55:43 +0000481 return ret;
Rajeev Kumar3a9cf8e2012-06-21 15:54:51 +0530482
Rajeev Kumar3a9cf8e2012-06-21 15:54:51 +0530483 dev_set_drvdata(&pdev->dev, dev);
Kuninori Morimoto92eaa322013-03-21 03:31:30 -0700484 ret = snd_soc_register_component(&pdev->dev, &dw_i2s_component,
485 dw_i2s_dai, 1);
Rajeev Kumar3a9cf8e2012-06-21 15:54:51 +0530486 if (ret != 0) {
487 dev_err(&pdev->dev, "not able to register dai\n");
Fabio Estevame925a6b2013-08-26 09:25:15 -0300488 goto err_clk_disable;
Rajeev Kumar3a9cf8e2012-06-21 15:54:51 +0530489 }
490
491 return 0;
492
Rajeev Kumar3a9cf8e2012-06-21 15:54:51 +0530493err_clk_disable:
Andrew Jackson3a192722014-12-30 10:55:44 +0000494 clk_disable_unprepare(dev->clk);
Rajeev Kumar3a9cf8e2012-06-21 15:54:51 +0530495 return ret;
496}
497
498static int dw_i2s_remove(struct platform_device *pdev)
499{
Andrew Jackson3a192722014-12-30 10:55:44 +0000500 struct dw_i2s_dev *dev = dev_get_drvdata(&pdev->dev);
501
Kuninori Morimoto92eaa322013-03-21 03:31:30 -0700502 snd_soc_unregister_component(&pdev->dev);
Andrew Jackson3a192722014-12-30 10:55:44 +0000503 clk_disable_unprepare(dev->clk);
Rajeev Kumar3a9cf8e2012-06-21 15:54:51 +0530504
Rajeev Kumar3a9cf8e2012-06-21 15:54:51 +0530505 return 0;
506}
507
508static struct platform_driver dw_i2s_driver = {
509 .probe = dw_i2s_probe,
510 .remove = dw_i2s_remove,
511 .driver = {
512 .name = "designware-i2s",
Rajeev Kumar3a9cf8e2012-06-21 15:54:51 +0530513 },
514};
515
516module_platform_driver(dw_i2s_driver);
517
Rajeev Kumarb794dbc2014-09-09 12:27:19 +0530518MODULE_AUTHOR("Rajeev Kumar <rajeevkumar.linux@gmail.com>");
Rajeev Kumar3a9cf8e2012-06-21 15:54:51 +0530519MODULE_DESCRIPTION("DESIGNWARE I2S SoC Interface");
520MODULE_LICENSE("GPL");
521MODULE_ALIAS("platform:designware_i2s");