blob: 3b4835a1bd23ee494683a45e67e493e15b83bf1f [file] [log] [blame]
Jassi Brar5033f432010-11-22 15:37:25 +09001/* sound/soc/samsung/i2s.c
Jassi Brar1c7ac012010-11-22 15:36:59 +09002 *
3 * ALSA SoC Audio Layer - Samsung I2S Controller driver
4 *
5 * Copyright (c) 2010 Samsung Electronics Co. Ltd.
Jaswinder Singhdf8ad332012-02-25 16:24:36 +05306 * Jaswinder Singh <jassisinghbrar@gmail.com>
Jassi Brar1c7ac012010-11-22 15:36:59 +09007 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11 */
12
13#include <linux/delay.h>
14#include <linux/slab.h>
15#include <linux/clk.h>
16#include <linux/io.h>
Paul Gortmakerda155d52011-07-15 12:38:28 -040017#include <linux/module.h>
Padmavathi Venna40476f62013-01-18 17:17:01 +053018#include <linux/of.h>
19#include <linux/of_gpio.h>
Mark Brownc5cf4db2011-12-08 16:45:03 +080020#include <linux/pm_runtime.h>
Jassi Brar1c7ac012010-11-22 15:36:59 +090021
Jassi Brar1c7ac012010-11-22 15:36:59 +090022#include <sound/soc.h>
Seungwhan Youn0378b6a2011-01-11 07:26:06 +090023#include <sound/pcm_params.h>
Jassi Brar1c7ac012010-11-22 15:36:59 +090024
Padmavathi Venna40476f62013-01-18 17:17:01 +053025#include <mach/dma.h>
26
Arnd Bergmann436d42c2012-08-24 15:22:12 +020027#include <linux/platform_data/asoc-s3c.h>
Jassi Brar1c7ac012010-11-22 15:36:59 +090028
29#include "dma.h"
Sangbeom Kim61100f42011-07-20 17:07:12 +090030#include "idma.h"
Jassi Brar1c7ac012010-11-22 15:36:59 +090031#include "i2s.h"
Sangbeom Kim172a4532011-06-20 16:36:18 +090032#include "i2s-regs.h"
Jassi Brar1c7ac012010-11-22 15:36:59 +090033
34#define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t)
35
Padmavathi Venna7c62eeb2013-01-18 17:17:00 +053036enum samsung_dai_type {
37 TYPE_PRI,
38 TYPE_SEC,
39};
40
Padmavathi Venna40476f62013-01-18 17:17:01 +053041struct samsung_i2s_dai_data {
42 int dai_type;
Padmavathi Venna7da493e2013-08-12 15:19:51 +053043 u32 quirks;
Padmavathi Venna40476f62013-01-18 17:17:01 +053044};
45
Jassi Brar1c7ac012010-11-22 15:36:59 +090046struct i2s_dai {
47 /* Platform device for this DAI */
48 struct platform_device *pdev;
49 /* IOREMAP'd SFRs */
50 void __iomem *addr;
51 /* Physical base address of SFRs */
52 u32 base;
53 /* Rate of RCLK source clock */
54 unsigned long rclk_srcrate;
55 /* Frame Clock */
56 unsigned frmclk;
57 /*
58 * Specifically requested RCLK,BCLK by MACHINE Driver.
59 * 0 indicates CPU driver is free to choose any value.
60 */
61 unsigned rfs, bfs;
62 /* I2S Controller's core clock */
63 struct clk *clk;
64 /* Clock for generating I2S signals */
65 struct clk *op_clk;
Jassi Brar1c7ac012010-11-22 15:36:59 +090066 /* Pointer to the Primary_Fifo if this is Sec_Fifo, NULL otherwise */
67 struct i2s_dai *pri_dai;
68 /* Pointer to the Secondary_Fifo if it has one, NULL otherwise */
69 struct i2s_dai *sec_dai;
70#define DAI_OPENED (1 << 0) /* Dai is opened */
71#define DAI_MANAGER (1 << 1) /* Dai is the manager */
72 unsigned mode;
73 /* Driver for this DAI */
74 struct snd_soc_dai_driver i2s_dai_drv;
75 /* DMA parameters */
76 struct s3c_dma_params dma_playback;
77 struct s3c_dma_params dma_capture;
Sangbeom Kim61100f42011-07-20 17:07:12 +090078 struct s3c_dma_params idma_playback;
Jassi Brar1c7ac012010-11-22 15:36:59 +090079 u32 quirks;
80 u32 suspend_i2smod;
81 u32 suspend_i2scon;
82 u32 suspend_i2spsr;
Padmavathi Venna40476f62013-01-18 17:17:01 +053083 unsigned long gpios[7]; /* i2s gpio line numbers */
Jassi Brar1c7ac012010-11-22 15:36:59 +090084};
85
86/* Lock for cross i/f checks */
87static DEFINE_SPINLOCK(lock);
88
89/* If this is the 'overlay' stereo DAI */
90static inline bool is_secondary(struct i2s_dai *i2s)
91{
92 return i2s->pri_dai ? true : false;
93}
94
95/* If operating in SoC-Slave mode */
96static inline bool is_slave(struct i2s_dai *i2s)
97{
98 return (readl(i2s->addr + I2SMOD) & MOD_SLAVE) ? true : false;
99}
100
101/* If this interface of the controller is transmitting data */
102static inline bool tx_active(struct i2s_dai *i2s)
103{
104 u32 active;
105
106 if (!i2s)
107 return false;
108
Sangbeom Kim33195502011-06-10 10:36:54 +0900109 active = readl(i2s->addr + I2SCON);
Jassi Brar1c7ac012010-11-22 15:36:59 +0900110
111 if (is_secondary(i2s))
112 active &= CON_TXSDMA_ACTIVE;
113 else
114 active &= CON_TXDMA_ACTIVE;
115
116 return active ? true : false;
117}
118
119/* If the other interface of the controller is transmitting data */
120static inline bool other_tx_active(struct i2s_dai *i2s)
121{
122 struct i2s_dai *other = i2s->pri_dai ? : i2s->sec_dai;
123
124 return tx_active(other);
125}
126
127/* If any interface of the controller is transmitting data */
128static inline bool any_tx_active(struct i2s_dai *i2s)
129{
130 return tx_active(i2s) || other_tx_active(i2s);
131}
132
133/* If this interface of the controller is receiving data */
134static inline bool rx_active(struct i2s_dai *i2s)
135{
136 u32 active;
137
138 if (!i2s)
139 return false;
140
Sangbeom Kim33195502011-06-10 10:36:54 +0900141 active = readl(i2s->addr + I2SCON) & CON_RXDMA_ACTIVE;
Jassi Brar1c7ac012010-11-22 15:36:59 +0900142
143 return active ? true : false;
144}
145
146/* If the other interface of the controller is receiving data */
147static inline bool other_rx_active(struct i2s_dai *i2s)
148{
149 struct i2s_dai *other = i2s->pri_dai ? : i2s->sec_dai;
150
151 return rx_active(other);
152}
153
154/* If any interface of the controller is receiving data */
155static inline bool any_rx_active(struct i2s_dai *i2s)
156{
157 return rx_active(i2s) || other_rx_active(i2s);
158}
159
160/* If the other DAI is transmitting or receiving data */
161static inline bool other_active(struct i2s_dai *i2s)
162{
163 return other_rx_active(i2s) || other_tx_active(i2s);
164}
165
166/* If this DAI is transmitting or receiving data */
167static inline bool this_active(struct i2s_dai *i2s)
168{
169 return tx_active(i2s) || rx_active(i2s);
170}
171
172/* If the controller is active anyway */
173static inline bool any_active(struct i2s_dai *i2s)
174{
175 return this_active(i2s) || other_active(i2s);
176}
177
178static inline struct i2s_dai *to_info(struct snd_soc_dai *dai)
179{
180 return snd_soc_dai_get_drvdata(dai);
181}
182
183static inline bool is_opened(struct i2s_dai *i2s)
184{
185 if (i2s && (i2s->mode & DAI_OPENED))
186 return true;
187 else
188 return false;
189}
190
191static inline bool is_manager(struct i2s_dai *i2s)
192{
193 if (is_opened(i2s) && (i2s->mode & DAI_MANAGER))
194 return true;
195 else
196 return false;
197}
198
199/* Read RCLK of I2S (in multiples of LRCLK) */
200static inline unsigned get_rfs(struct i2s_dai *i2s)
201{
Padmavathi Vennab60be4a2013-07-26 19:06:48 +0530202 u32 rfs = (readl(i2s->addr + I2SMOD) >> MOD_RCLK_SHIFT);
203 rfs &= MOD_RCLK_MASK;
Jassi Brar1c7ac012010-11-22 15:36:59 +0900204
205 switch (rfs) {
206 case 3: return 768;
207 case 2: return 384;
208 case 1: return 512;
209 default: return 256;
210 }
211}
212
213/* Write RCLK of I2S (in multiples of LRCLK) */
214static inline void set_rfs(struct i2s_dai *i2s, unsigned rfs)
215{
216 u32 mod = readl(i2s->addr + I2SMOD);
Padmavathi Vennab60be4a2013-07-26 19:06:48 +0530217 int rfs_shift = MOD_RCLK_SHIFT;
Jassi Brar1c7ac012010-11-22 15:36:59 +0900218
Padmavathi Vennab60be4a2013-07-26 19:06:48 +0530219 mod &= ~(MOD_RCLK_MASK << rfs_shift);
Jassi Brar1c7ac012010-11-22 15:36:59 +0900220
221 switch (rfs) {
222 case 768:
Padmavathi Vennab60be4a2013-07-26 19:06:48 +0530223 mod |= (MOD_RCLK_768FS << rfs_shift);
Jassi Brar1c7ac012010-11-22 15:36:59 +0900224 break;
225 case 512:
Padmavathi Vennab60be4a2013-07-26 19:06:48 +0530226 mod |= (MOD_RCLK_512FS << rfs_shift);
Jassi Brar1c7ac012010-11-22 15:36:59 +0900227 break;
228 case 384:
Padmavathi Vennab60be4a2013-07-26 19:06:48 +0530229 mod |= (MOD_RCLK_384FS << rfs_shift);
Jassi Brar1c7ac012010-11-22 15:36:59 +0900230 break;
231 default:
Padmavathi Vennab60be4a2013-07-26 19:06:48 +0530232 mod |= (MOD_RCLK_256FS << rfs_shift);
Jassi Brar1c7ac012010-11-22 15:36:59 +0900233 break;
234 }
235
236 writel(mod, i2s->addr + I2SMOD);
237}
238
239/* Read Bit-Clock of I2S (in multiples of LRCLK) */
240static inline unsigned get_bfs(struct i2s_dai *i2s)
241{
Padmavathi Vennab60be4a2013-07-26 19:06:48 +0530242 u32 bfs = readl(i2s->addr + I2SMOD) >> MOD_BCLK_SHIFT;
243 bfs &= MOD_BCLK_MASK;
Jassi Brar1c7ac012010-11-22 15:36:59 +0900244
245 switch (bfs) {
246 case 3: return 24;
247 case 2: return 16;
248 case 1: return 48;
249 default: return 32;
250 }
251}
252
253/* Write Bit-Clock of I2S (in multiples of LRCLK) */
254static inline void set_bfs(struct i2s_dai *i2s, unsigned bfs)
255{
256 u32 mod = readl(i2s->addr + I2SMOD);
Padmavathi Vennab60be4a2013-07-26 19:06:48 +0530257 int bfs_shift = MOD_BCLK_SHIFT;
Jassi Brar1c7ac012010-11-22 15:36:59 +0900258
Padmavathi Vennab60be4a2013-07-26 19:06:48 +0530259 mod &= ~(MOD_BCLK_MASK << bfs_shift);
Jassi Brar1c7ac012010-11-22 15:36:59 +0900260
261 switch (bfs) {
262 case 48:
Padmavathi Vennab60be4a2013-07-26 19:06:48 +0530263 mod |= (MOD_BCLK_48FS << bfs_shift);
Jassi Brar1c7ac012010-11-22 15:36:59 +0900264 break;
265 case 32:
Padmavathi Vennab60be4a2013-07-26 19:06:48 +0530266 mod |= (MOD_BCLK_32FS << bfs_shift);
Jassi Brar1c7ac012010-11-22 15:36:59 +0900267 break;
268 case 24:
Padmavathi Vennab60be4a2013-07-26 19:06:48 +0530269 mod |= (MOD_BCLK_24FS << bfs_shift);
Jassi Brar1c7ac012010-11-22 15:36:59 +0900270 break;
271 case 16:
Padmavathi Vennab60be4a2013-07-26 19:06:48 +0530272 mod |= (MOD_BCLK_16FS << bfs_shift);
Jassi Brar1c7ac012010-11-22 15:36:59 +0900273 break;
274 default:
275 dev_err(&i2s->pdev->dev, "Wrong BCLK Divider!\n");
276 return;
277 }
278
279 writel(mod, i2s->addr + I2SMOD);
280}
281
282/* Sample-Size */
283static inline int get_blc(struct i2s_dai *i2s)
284{
285 int blc = readl(i2s->addr + I2SMOD);
286
287 blc = (blc >> 13) & 0x3;
288
289 switch (blc) {
290 case 2: return 24;
291 case 1: return 8;
292 default: return 16;
293 }
294}
295
296/* TX Channel Control */
297static void i2s_txctrl(struct i2s_dai *i2s, int on)
298{
299 void __iomem *addr = i2s->addr;
300 u32 con = readl(addr + I2SCON);
301 u32 mod = readl(addr + I2SMOD) & ~MOD_MASK;
302
303 if (on) {
304 con |= CON_ACTIVE;
305 con &= ~CON_TXCH_PAUSE;
306
307 if (is_secondary(i2s)) {
308 con |= CON_TXSDMA_ACTIVE;
309 con &= ~CON_TXSDMA_PAUSE;
310 } else {
311 con |= CON_TXDMA_ACTIVE;
312 con &= ~CON_TXDMA_PAUSE;
313 }
314
315 if (any_rx_active(i2s))
316 mod |= MOD_TXRX;
317 else
318 mod |= MOD_TXONLY;
319 } else {
320 if (is_secondary(i2s)) {
321 con |= CON_TXSDMA_PAUSE;
322 con &= ~CON_TXSDMA_ACTIVE;
323 } else {
324 con |= CON_TXDMA_PAUSE;
325 con &= ~CON_TXDMA_ACTIVE;
326 }
327
328 if (other_tx_active(i2s)) {
329 writel(con, addr + I2SCON);
330 return;
331 }
332
333 con |= CON_TXCH_PAUSE;
334
335 if (any_rx_active(i2s))
336 mod |= MOD_RXONLY;
337 else
338 con &= ~CON_ACTIVE;
339 }
340
341 writel(mod, addr + I2SMOD);
342 writel(con, addr + I2SCON);
343}
344
345/* RX Channel Control */
346static void i2s_rxctrl(struct i2s_dai *i2s, int on)
347{
348 void __iomem *addr = i2s->addr;
349 u32 con = readl(addr + I2SCON);
350 u32 mod = readl(addr + I2SMOD) & ~MOD_MASK;
351
352 if (on) {
353 con |= CON_RXDMA_ACTIVE | CON_ACTIVE;
354 con &= ~(CON_RXDMA_PAUSE | CON_RXCH_PAUSE);
355
356 if (any_tx_active(i2s))
357 mod |= MOD_TXRX;
358 else
359 mod |= MOD_RXONLY;
360 } else {
361 con |= CON_RXDMA_PAUSE | CON_RXCH_PAUSE;
362 con &= ~CON_RXDMA_ACTIVE;
363
364 if (any_tx_active(i2s))
365 mod |= MOD_TXONLY;
366 else
367 con &= ~CON_ACTIVE;
368 }
369
370 writel(mod, addr + I2SMOD);
371 writel(con, addr + I2SCON);
372}
373
374/* Flush FIFO of an interface */
375static inline void i2s_fifo(struct i2s_dai *i2s, u32 flush)
376{
377 void __iomem *fic;
378 u32 val;
379
380 if (!i2s)
381 return;
382
383 if (is_secondary(i2s))
384 fic = i2s->addr + I2SFICS;
385 else
386 fic = i2s->addr + I2SFIC;
387
388 /* Flush the FIFO */
389 writel(readl(fic) | flush, fic);
390
391 /* Be patient */
392 val = msecs_to_loops(1) / 1000; /* 1 usec */
393 while (--val)
394 cpu_relax();
395
396 writel(readl(fic) & ~flush, fic);
397}
398
399static int i2s_set_sysclk(struct snd_soc_dai *dai,
400 int clk_id, unsigned int rfs, int dir)
401{
402 struct i2s_dai *i2s = to_info(dai);
403 struct i2s_dai *other = i2s->pri_dai ? : i2s->sec_dai;
404 u32 mod = readl(i2s->addr + I2SMOD);
405
406 switch (clk_id) {
407 case SAMSUNG_I2S_CDCLK:
408 /* Shouldn't matter in GATING(CLOCK_IN) mode */
409 if (dir == SND_SOC_CLOCK_IN)
410 rfs = 0;
411
412 if ((rfs && other->rfs && (other->rfs != rfs)) ||
413 (any_active(i2s) &&
414 (((dir == SND_SOC_CLOCK_IN)
415 && !(mod & MOD_CDCLKCON)) ||
416 ((dir == SND_SOC_CLOCK_OUT)
417 && (mod & MOD_CDCLKCON))))) {
418 dev_err(&i2s->pdev->dev,
419 "%s:%d Other DAI busy\n", __func__, __LINE__);
420 return -EAGAIN;
421 }
422
423 if (dir == SND_SOC_CLOCK_IN)
424 mod |= MOD_CDCLKCON;
425 else
426 mod &= ~MOD_CDCLKCON;
427
428 i2s->rfs = rfs;
429 break;
430
431 case SAMSUNG_I2S_RCLKSRC_0: /* clock corrsponding to IISMOD[10] := 0 */
432 case SAMSUNG_I2S_RCLKSRC_1: /* clock corrsponding to IISMOD[10] := 1 */
433 if ((i2s->quirks & QUIRK_NO_MUXPSR)
434 || (clk_id == SAMSUNG_I2S_RCLKSRC_0))
435 clk_id = 0;
436 else
437 clk_id = 1;
438
439 if (!any_active(i2s)) {
440 if (i2s->op_clk) {
441 if ((clk_id && !(mod & MOD_IMS_SYSMUX)) ||
442 (!clk_id && (mod & MOD_IMS_SYSMUX))) {
Thomas Abraham98614cf2012-10-03 08:46:58 +0900443 clk_disable_unprepare(i2s->op_clk);
Jassi Brar1c7ac012010-11-22 15:36:59 +0900444 clk_put(i2s->op_clk);
445 } else {
Jassi Brar6ce534a2010-12-20 11:05:46 +0900446 i2s->rclk_srcrate =
447 clk_get_rate(i2s->op_clk);
Jassi Brar1c7ac012010-11-22 15:36:59 +0900448 return 0;
449 }
450 }
451
Padmavathi Venna1974a042012-11-28 16:17:48 +0530452 if (clk_id)
453 i2s->op_clk = clk_get(&i2s->pdev->dev,
454 "i2s_opclk1");
455 else
456 i2s->op_clk = clk_get(&i2s->pdev->dev,
457 "i2s_opclk0");
Thomas Abraham98614cf2012-10-03 08:46:58 +0900458 clk_prepare_enable(i2s->op_clk);
Jassi Brar1c7ac012010-11-22 15:36:59 +0900459 i2s->rclk_srcrate = clk_get_rate(i2s->op_clk);
460
461 /* Over-ride the other's */
462 if (other) {
463 other->op_clk = i2s->op_clk;
464 other->rclk_srcrate = i2s->rclk_srcrate;
465 }
466 } else if ((!clk_id && (mod & MOD_IMS_SYSMUX))
467 || (clk_id && !(mod & MOD_IMS_SYSMUX))) {
468 dev_err(&i2s->pdev->dev,
469 "%s:%d Other DAI busy\n", __func__, __LINE__);
470 return -EAGAIN;
471 } else {
472 /* Call can't be on the active DAI */
473 i2s->op_clk = other->op_clk;
474 i2s->rclk_srcrate = other->rclk_srcrate;
475 return 0;
476 }
477
478 if (clk_id == 0)
479 mod &= ~MOD_IMS_SYSMUX;
480 else
481 mod |= MOD_IMS_SYSMUX;
482 break;
483
484 default:
485 dev_err(&i2s->pdev->dev, "We don't serve that!\n");
486 return -EINVAL;
487 }
488
489 writel(mod, i2s->addr + I2SMOD);
490
491 return 0;
492}
493
494static int i2s_set_fmt(struct snd_soc_dai *dai,
495 unsigned int fmt)
496{
497 struct i2s_dai *i2s = to_info(dai);
498 u32 mod = readl(i2s->addr + I2SMOD);
Padmavathi Vennab60be4a2013-07-26 19:06:48 +0530499 int lrp_shift = MOD_LRP_SHIFT, sdf_shift = MOD_SDF_SHIFT;
500 int sdf_mask, lrp_rlow;
Jassi Brar1c7ac012010-11-22 15:36:59 +0900501 u32 tmp = 0;
502
Padmavathi Vennab60be4a2013-07-26 19:06:48 +0530503 sdf_mask = MOD_SDF_MASK << sdf_shift;
504 lrp_rlow = MOD_LR_RLOW << lrp_shift;
505
Jassi Brar1c7ac012010-11-22 15:36:59 +0900506 /* Format is priority */
507 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
508 case SND_SOC_DAIFMT_RIGHT_J:
Padmavathi Vennab60be4a2013-07-26 19:06:48 +0530509 tmp |= lrp_rlow;
510 tmp |= (MOD_SDF_MSB << sdf_shift);
Jassi Brar1c7ac012010-11-22 15:36:59 +0900511 break;
512 case SND_SOC_DAIFMT_LEFT_J:
Padmavathi Vennab60be4a2013-07-26 19:06:48 +0530513 tmp |= lrp_rlow;
514 tmp |= (MOD_SDF_LSB << sdf_shift);
Jassi Brar1c7ac012010-11-22 15:36:59 +0900515 break;
516 case SND_SOC_DAIFMT_I2S:
Padmavathi Vennab60be4a2013-07-26 19:06:48 +0530517 tmp |= (MOD_SDF_IIS << sdf_shift);
Jassi Brar1c7ac012010-11-22 15:36:59 +0900518 break;
519 default:
520 dev_err(&i2s->pdev->dev, "Format not supported\n");
521 return -EINVAL;
522 }
523
524 /*
525 * INV flag is relative to the FORMAT flag - if set it simply
526 * flips the polarity specified by the Standard
527 */
528 switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
529 case SND_SOC_DAIFMT_NB_NF:
530 break;
531 case SND_SOC_DAIFMT_NB_IF:
Padmavathi Vennab60be4a2013-07-26 19:06:48 +0530532 if (tmp & lrp_rlow)
533 tmp &= ~lrp_rlow;
Jassi Brar1c7ac012010-11-22 15:36:59 +0900534 else
Padmavathi Vennab60be4a2013-07-26 19:06:48 +0530535 tmp |= lrp_rlow;
Jassi Brar1c7ac012010-11-22 15:36:59 +0900536 break;
537 default:
538 dev_err(&i2s->pdev->dev, "Polarity not supported\n");
539 return -EINVAL;
540 }
541
542 switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
543 case SND_SOC_DAIFMT_CBM_CFM:
544 tmp |= MOD_SLAVE;
545 break;
546 case SND_SOC_DAIFMT_CBS_CFS:
547 /* Set default source clock in Master mode */
548 if (i2s->rclk_srcrate == 0)
549 i2s_set_sysclk(dai, SAMSUNG_I2S_RCLKSRC_0,
550 0, SND_SOC_CLOCK_IN);
551 break;
552 default:
553 dev_err(&i2s->pdev->dev, "master/slave format not supported\n");
554 return -EINVAL;
555 }
556
Padmavathi Vennab60be4a2013-07-26 19:06:48 +0530557 /*
558 * Don't change the I2S mode if any controller is active on this
559 * channel.
560 */
Jassi Brar1c7ac012010-11-22 15:36:59 +0900561 if (any_active(i2s) &&
Padmavathi Vennab60be4a2013-07-26 19:06:48 +0530562 ((mod & (sdf_mask | lrp_rlow | MOD_SLAVE)) != tmp)) {
Jassi Brar1c7ac012010-11-22 15:36:59 +0900563 dev_err(&i2s->pdev->dev,
564 "%s:%d Other DAI busy\n", __func__, __LINE__);
565 return -EAGAIN;
566 }
567
Padmavathi Vennab60be4a2013-07-26 19:06:48 +0530568 mod &= ~(sdf_mask | lrp_rlow | MOD_SLAVE);
Jassi Brar1c7ac012010-11-22 15:36:59 +0900569 mod |= tmp;
570 writel(mod, i2s->addr + I2SMOD);
571
572 return 0;
573}
574
575static int i2s_hw_params(struct snd_pcm_substream *substream,
576 struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
577{
578 struct i2s_dai *i2s = to_info(dai);
579 u32 mod = readl(i2s->addr + I2SMOD);
580
581 if (!is_secondary(i2s))
582 mod &= ~(MOD_DC2_EN | MOD_DC1_EN);
583
584 switch (params_channels(params)) {
585 case 6:
586 mod |= MOD_DC2_EN;
587 case 4:
588 mod |= MOD_DC1_EN;
589 break;
590 case 2:
Sangsu Park588fb702012-03-16 15:40:53 +0900591 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
592 i2s->dma_playback.dma_size = 4;
593 else
594 i2s->dma_capture.dma_size = 4;
595 break;
596 case 1:
597 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
598 i2s->dma_playback.dma_size = 2;
599 else
600 i2s->dma_capture.dma_size = 2;
601
Jassi Brar1c7ac012010-11-22 15:36:59 +0900602 break;
603 default:
604 dev_err(&i2s->pdev->dev, "%d channels not supported\n",
605 params_channels(params));
606 return -EINVAL;
607 }
608
609 if (is_secondary(i2s))
610 mod &= ~MOD_BLCS_MASK;
611 else
612 mod &= ~MOD_BLCP_MASK;
613
614 if (is_manager(i2s))
615 mod &= ~MOD_BLC_MASK;
616
617 switch (params_format(params)) {
618 case SNDRV_PCM_FORMAT_S8:
619 if (is_secondary(i2s))
620 mod |= MOD_BLCS_8BIT;
621 else
622 mod |= MOD_BLCP_8BIT;
623 if (is_manager(i2s))
624 mod |= MOD_BLC_8BIT;
625 break;
626 case SNDRV_PCM_FORMAT_S16_LE:
627 if (is_secondary(i2s))
628 mod |= MOD_BLCS_16BIT;
629 else
630 mod |= MOD_BLCP_16BIT;
631 if (is_manager(i2s))
632 mod |= MOD_BLC_16BIT;
633 break;
634 case SNDRV_PCM_FORMAT_S24_LE:
635 if (is_secondary(i2s))
636 mod |= MOD_BLCS_24BIT;
637 else
638 mod |= MOD_BLCP_24BIT;
639 if (is_manager(i2s))
640 mod |= MOD_BLC_24BIT;
641 break;
642 default:
643 dev_err(&i2s->pdev->dev, "Format(%d) not supported\n",
644 params_format(params));
645 return -EINVAL;
646 }
647 writel(mod, i2s->addr + I2SMOD);
648
649 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
650 snd_soc_dai_set_dma_data(dai, substream,
651 (void *)&i2s->dma_playback);
652 else
653 snd_soc_dai_set_dma_data(dai, substream,
654 (void *)&i2s->dma_capture);
655
656 i2s->frmclk = params_rate(params);
657
658 return 0;
659}
660
661/* We set constraints on the substream acc to the version of I2S */
662static int i2s_startup(struct snd_pcm_substream *substream,
663 struct snd_soc_dai *dai)
664{
665 struct i2s_dai *i2s = to_info(dai);
666 struct i2s_dai *other = i2s->pri_dai ? : i2s->sec_dai;
667 unsigned long flags;
668
669 spin_lock_irqsave(&lock, flags);
670
671 i2s->mode |= DAI_OPENED;
672
673 if (is_manager(other))
674 i2s->mode &= ~DAI_MANAGER;
675 else
676 i2s->mode |= DAI_MANAGER;
677
678 /* Enforce set_sysclk in Master mode */
679 i2s->rclk_srcrate = 0;
680
Padmavathi Venna2d778282013-01-24 18:05:31 +0530681 if (!any_active(i2s) && (i2s->quirks & QUIRK_NEED_RSTCLR))
682 writel(CON_RSTCLR, i2s->addr + I2SCON);
683
Jassi Brar1c7ac012010-11-22 15:36:59 +0900684 spin_unlock_irqrestore(&lock, flags);
685
686 return 0;
687}
688
689static void i2s_shutdown(struct snd_pcm_substream *substream,
690 struct snd_soc_dai *dai)
691{
692 struct i2s_dai *i2s = to_info(dai);
693 struct i2s_dai *other = i2s->pri_dai ? : i2s->sec_dai;
694 unsigned long flags;
695
696 spin_lock_irqsave(&lock, flags);
697
698 i2s->mode &= ~DAI_OPENED;
699 i2s->mode &= ~DAI_MANAGER;
700
701 if (is_opened(other))
702 other->mode |= DAI_MANAGER;
703
704 /* Reset any constraint on RFS and BFS */
705 i2s->rfs = 0;
706 i2s->bfs = 0;
707
708 spin_unlock_irqrestore(&lock, flags);
709
710 /* Gate CDCLK by default */
711 if (!is_opened(other))
712 i2s_set_sysclk(dai, SAMSUNG_I2S_CDCLK,
713 0, SND_SOC_CLOCK_IN);
714}
715
716static int config_setup(struct i2s_dai *i2s)
717{
718 struct i2s_dai *other = i2s->pri_dai ? : i2s->sec_dai;
719 unsigned rfs, bfs, blc;
720 u32 psr;
721
722 blc = get_blc(i2s);
723
724 bfs = i2s->bfs;
725
726 if (!bfs && other)
727 bfs = other->bfs;
728
729 /* Select least possible multiple(2) if no constraint set */
730 if (!bfs)
731 bfs = blc * 2;
732
733 rfs = i2s->rfs;
734
735 if (!rfs && other)
736 rfs = other->rfs;
737
738 if ((rfs == 256 || rfs == 512) && (blc == 24)) {
739 dev_err(&i2s->pdev->dev,
740 "%d-RFS not supported for 24-blc\n", rfs);
741 return -EINVAL;
742 }
743
744 if (!rfs) {
745 if (bfs == 16 || bfs == 32)
746 rfs = 256;
747 else
748 rfs = 384;
749 }
750
751 /* If already setup and running */
752 if (any_active(i2s) && (get_rfs(i2s) != rfs || get_bfs(i2s) != bfs)) {
753 dev_err(&i2s->pdev->dev,
754 "%s:%d Other DAI busy\n", __func__, __LINE__);
755 return -EAGAIN;
756 }
757
758 /* Don't bother RFS, BFS & PSR in Slave mode */
759 if (is_slave(i2s))
760 return 0;
761
762 set_bfs(i2s, bfs);
763 set_rfs(i2s, rfs);
764
765 if (!(i2s->quirks & QUIRK_NO_MUXPSR)) {
766 psr = i2s->rclk_srcrate / i2s->frmclk / rfs;
767 writel(((psr - 1) << 8) | PSR_PSREN, i2s->addr + I2SPSR);
768 dev_dbg(&i2s->pdev->dev,
769 "RCLK_SRC=%luHz PSR=%u, RCLK=%dfs, BCLK=%dfs\n",
770 i2s->rclk_srcrate, psr, rfs, bfs);
771 }
772
773 return 0;
774}
775
776static int i2s_trigger(struct snd_pcm_substream *substream,
777 int cmd, struct snd_soc_dai *dai)
778{
779 int capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE);
780 struct snd_soc_pcm_runtime *rtd = substream->private_data;
781 struct i2s_dai *i2s = to_info(rtd->cpu_dai);
782 unsigned long flags;
783
784 switch (cmd) {
785 case SNDRV_PCM_TRIGGER_START:
786 case SNDRV_PCM_TRIGGER_RESUME:
787 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
788 local_irq_save(flags);
789
Jassi Brar1c7ac012010-11-22 15:36:59 +0900790 if (config_setup(i2s)) {
791 local_irq_restore(flags);
792 return -EINVAL;
793 }
794
795 if (capture)
796 i2s_rxctrl(i2s, 1);
797 else
798 i2s_txctrl(i2s, 1);
799
800 local_irq_restore(flags);
801 break;
802 case SNDRV_PCM_TRIGGER_STOP:
803 case SNDRV_PCM_TRIGGER_SUSPEND:
804 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
805 local_irq_save(flags);
806
Jassi Brarc90887f2012-02-25 16:42:34 +0530807 if (capture) {
Jassi Brar1c7ac012010-11-22 15:36:59 +0900808 i2s_rxctrl(i2s, 0);
Jassi Brar775bc972010-12-20 11:05:47 +0900809 i2s_fifo(i2s, FIC_RXFLUSH);
Jassi Brarc90887f2012-02-25 16:42:34 +0530810 } else {
811 i2s_txctrl(i2s, 0);
Jassi Brar775bc972010-12-20 11:05:47 +0900812 i2s_fifo(i2s, FIC_TXFLUSH);
Jassi Brarc90887f2012-02-25 16:42:34 +0530813 }
Jassi Brar775bc972010-12-20 11:05:47 +0900814
Jassi Brar1c7ac012010-11-22 15:36:59 +0900815 local_irq_restore(flags);
816 break;
817 }
818
819 return 0;
820}
821
822static int i2s_set_clkdiv(struct snd_soc_dai *dai,
823 int div_id, int div)
824{
825 struct i2s_dai *i2s = to_info(dai);
826 struct i2s_dai *other = i2s->pri_dai ? : i2s->sec_dai;
827
828 switch (div_id) {
829 case SAMSUNG_I2S_DIV_BCLK:
830 if ((any_active(i2s) && div && (get_bfs(i2s) != div))
831 || (other && other->bfs && (other->bfs != div))) {
832 dev_err(&i2s->pdev->dev,
833 "%s:%d Other DAI busy\n", __func__, __LINE__);
834 return -EAGAIN;
835 }
836 i2s->bfs = div;
837 break;
838 default:
839 dev_err(&i2s->pdev->dev,
840 "Invalid clock divider(%d)\n", div_id);
841 return -EINVAL;
842 }
843
844 return 0;
845}
846
847static snd_pcm_sframes_t
848i2s_delay(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
849{
850 struct i2s_dai *i2s = to_info(dai);
851 u32 reg = readl(i2s->addr + I2SFIC);
852 snd_pcm_sframes_t delay;
853
854 if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
855 delay = FIC_RXCOUNT(reg);
856 else if (is_secondary(i2s))
857 delay = FICS_TXCOUNT(readl(i2s->addr + I2SFICS));
858 else
859 delay = FIC_TXCOUNT(reg);
860
861 return delay;
862}
863
864#ifdef CONFIG_PM
865static int i2s_suspend(struct snd_soc_dai *dai)
866{
867 struct i2s_dai *i2s = to_info(dai);
868
869 if (dai->active) {
870 i2s->suspend_i2smod = readl(i2s->addr + I2SMOD);
871 i2s->suspend_i2scon = readl(i2s->addr + I2SCON);
872 i2s->suspend_i2spsr = readl(i2s->addr + I2SPSR);
873 }
874
875 return 0;
876}
877
878static int i2s_resume(struct snd_soc_dai *dai)
879{
880 struct i2s_dai *i2s = to_info(dai);
881
882 if (dai->active) {
883 writel(i2s->suspend_i2scon, i2s->addr + I2SCON);
884 writel(i2s->suspend_i2smod, i2s->addr + I2SMOD);
885 writel(i2s->suspend_i2spsr, i2s->addr + I2SPSR);
886 }
887
888 return 0;
889}
890#else
891#define i2s_suspend NULL
892#define i2s_resume NULL
893#endif
894
895static int samsung_i2s_dai_probe(struct snd_soc_dai *dai)
896{
897 struct i2s_dai *i2s = to_info(dai);
898 struct i2s_dai *other = i2s->pri_dai ? : i2s->sec_dai;
899
900 if (other && other->clk) /* If this is probe on secondary */
901 goto probe_exit;
902
903 i2s->addr = ioremap(i2s->base, 0x100);
904 if (i2s->addr == NULL) {
905 dev_err(&i2s->pdev->dev, "cannot ioremap registers\n");
906 return -ENXIO;
907 }
908
909 i2s->clk = clk_get(&i2s->pdev->dev, "iis");
910 if (IS_ERR(i2s->clk)) {
911 dev_err(&i2s->pdev->dev, "failed to get i2s_clock\n");
912 iounmap(i2s->addr);
913 return -ENOENT;
914 }
Thomas Abraham98614cf2012-10-03 08:46:58 +0900915 clk_prepare_enable(i2s->clk);
Jassi Brar1c7ac012010-11-22 15:36:59 +0900916
917 if (other) {
918 other->addr = i2s->addr;
919 other->clk = i2s->clk;
920 }
921
922 if (i2s->quirks & QUIRK_NEED_RSTCLR)
923 writel(CON_RSTCLR, i2s->addr + I2SCON);
924
Sangbeom Kim61100f42011-07-20 17:07:12 +0900925 if (i2s->quirks & QUIRK_SEC_DAI)
Mark Brown9b8f5692011-11-27 21:35:40 +0000926 idma_reg_addr_init(i2s->addr,
Sangbeom Kim61100f42011-07-20 17:07:12 +0900927 i2s->sec_dai->idma_playback.dma_addr);
928
Jassi Brar1c7ac012010-11-22 15:36:59 +0900929probe_exit:
930 /* Reset any constraint on RFS and BFS */
931 i2s->rfs = 0;
932 i2s->bfs = 0;
933 i2s_txctrl(i2s, 0);
934 i2s_rxctrl(i2s, 0);
935 i2s_fifo(i2s, FIC_TXFLUSH);
936 i2s_fifo(other, FIC_TXFLUSH);
937 i2s_fifo(i2s, FIC_RXFLUSH);
938
939 /* Gate CDCLK by default */
940 if (!is_opened(other))
941 i2s_set_sysclk(dai, SAMSUNG_I2S_CDCLK,
942 0, SND_SOC_CLOCK_IN);
943
944 return 0;
945}
946
947static int samsung_i2s_dai_remove(struct snd_soc_dai *dai)
948{
949 struct i2s_dai *i2s = snd_soc_dai_get_drvdata(dai);
950 struct i2s_dai *other = i2s->pri_dai ? : i2s->sec_dai;
951
952 if (!other || !other->clk) {
953
954 if (i2s->quirks & QUIRK_NEED_RSTCLR)
955 writel(0, i2s->addr + I2SCON);
956
Thomas Abraham98614cf2012-10-03 08:46:58 +0900957 clk_disable_unprepare(i2s->clk);
Jassi Brar1c7ac012010-11-22 15:36:59 +0900958 clk_put(i2s->clk);
959
960 iounmap(i2s->addr);
961 }
962
963 i2s->clk = NULL;
964
965 return 0;
966}
967
Lars-Peter Clausen85e76522011-11-23 11:40:40 +0100968static const struct snd_soc_dai_ops samsung_i2s_dai_ops = {
Jassi Brar1c7ac012010-11-22 15:36:59 +0900969 .trigger = i2s_trigger,
970 .hw_params = i2s_hw_params,
971 .set_fmt = i2s_set_fmt,
972 .set_clkdiv = i2s_set_clkdiv,
973 .set_sysclk = i2s_set_sysclk,
974 .startup = i2s_startup,
975 .shutdown = i2s_shutdown,
976 .delay = i2s_delay,
977};
978
Kuninori Morimoto4b828532013-03-21 03:35:55 -0700979static const struct snd_soc_component_driver samsung_i2s_component = {
980 .name = "samsung-i2s",
981};
982
Jassi Brar1c7ac012010-11-22 15:36:59 +0900983#define SAMSUNG_I2S_RATES SNDRV_PCM_RATE_8000_96000
984
985#define SAMSUNG_I2S_FMTS (SNDRV_PCM_FMTBIT_S8 | \
986 SNDRV_PCM_FMTBIT_S16_LE | \
987 SNDRV_PCM_FMTBIT_S24_LE)
988
Bill Pembertonfdca21a2012-12-07 09:26:15 -0500989static struct i2s_dai *i2s_alloc_dai(struct platform_device *pdev, bool sec)
Jassi Brar1c7ac012010-11-22 15:36:59 +0900990{
991 struct i2s_dai *i2s;
Prathyush Kc6f9b1e2013-04-02 16:53:02 +0530992 int ret;
Jassi Brar1c7ac012010-11-22 15:36:59 +0900993
Mark Brownb960ce72011-12-03 20:30:37 +0000994 i2s = devm_kzalloc(&pdev->dev, sizeof(struct i2s_dai), GFP_KERNEL);
Jassi Brar1c7ac012010-11-22 15:36:59 +0900995 if (i2s == NULL)
996 return NULL;
997
998 i2s->pdev = pdev;
999 i2s->pri_dai = NULL;
1000 i2s->sec_dai = NULL;
1001 i2s->i2s_dai_drv.symmetric_rates = 1;
1002 i2s->i2s_dai_drv.probe = samsung_i2s_dai_probe;
1003 i2s->i2s_dai_drv.remove = samsung_i2s_dai_remove;
1004 i2s->i2s_dai_drv.ops = &samsung_i2s_dai_ops;
1005 i2s->i2s_dai_drv.suspend = i2s_suspend;
1006 i2s->i2s_dai_drv.resume = i2s_resume;
1007 i2s->i2s_dai_drv.playback.channels_min = 2;
1008 i2s->i2s_dai_drv.playback.channels_max = 2;
1009 i2s->i2s_dai_drv.playback.rates = SAMSUNG_I2S_RATES;
1010 i2s->i2s_dai_drv.playback.formats = SAMSUNG_I2S_FMTS;
1011
1012 if (!sec) {
Sangsu Park588fb702012-03-16 15:40:53 +09001013 i2s->i2s_dai_drv.capture.channels_min = 1;
Jassi Brar1c7ac012010-11-22 15:36:59 +09001014 i2s->i2s_dai_drv.capture.channels_max = 2;
1015 i2s->i2s_dai_drv.capture.rates = SAMSUNG_I2S_RATES;
1016 i2s->i2s_dai_drv.capture.formats = SAMSUNG_I2S_FMTS;
Prathyush Kc6f9b1e2013-04-02 16:53:02 +05301017 dev_set_drvdata(&i2s->pdev->dev, i2s);
Jassi Brar1c7ac012010-11-22 15:36:59 +09001018 } else { /* Create a new platform_device for Secondary */
Prathyush Kc6f9b1e2013-04-02 16:53:02 +05301019 i2s->pdev = platform_device_alloc("samsung-i2s-sec", -1);
Mark Brownb960ce72011-12-03 20:30:37 +00001020 if (IS_ERR(i2s->pdev))
Jassi Brar1c7ac012010-11-22 15:36:59 +09001021 return NULL;
Jassi Brar1c7ac012010-11-22 15:36:59 +09001022
Mark Brown2f6f0ff2013-08-01 11:02:47 +01001023 i2s->pdev->dev.parent = &pdev->dev;
1024
Prathyush Kc6f9b1e2013-04-02 16:53:02 +05301025 platform_set_drvdata(i2s->pdev, i2s);
1026 ret = platform_device_add(i2s->pdev);
1027 if (ret < 0)
1028 return NULL;
1029 }
Jassi Brar1c7ac012010-11-22 15:36:59 +09001030
1031 return i2s;
1032}
1033
Padmavathi Venna40476f62013-01-18 17:17:01 +05301034static const struct of_device_id exynos_i2s_match[];
1035
Padmavathi Venna7da493e2013-08-12 15:19:51 +05301036static inline const struct samsung_i2s_dai_data *samsung_i2s_get_driver_data(
1037 struct platform_device *pdev)
Padmavathi Venna7c62eeb2013-01-18 17:17:00 +05301038{
Padmavathi Venna40476f62013-01-18 17:17:01 +05301039#ifdef CONFIG_OF
Padmavathi Venna40476f62013-01-18 17:17:01 +05301040 if (pdev->dev.of_node) {
1041 const struct of_device_id *match;
1042 match = of_match_node(exynos_i2s_match, pdev->dev.of_node);
Padmavathi Venna7da493e2013-08-12 15:19:51 +05301043 return match->data;
Padmavathi Venna40476f62013-01-18 17:17:01 +05301044 } else
1045#endif
Padmavathi Venna7da493e2013-08-12 15:19:51 +05301046 return (struct samsung_i2s_dai_data *)
1047 platform_get_device_id(pdev)->driver_data;
Padmavathi Venna7c62eeb2013-01-18 17:17:00 +05301048}
1049
R. Chandrasekar5b1d3c32013-01-30 17:41:04 +05301050#ifdef CONFIG_PM_RUNTIME
1051static int i2s_runtime_suspend(struct device *dev)
1052{
1053 struct i2s_dai *i2s = dev_get_drvdata(dev);
1054
1055 clk_disable_unprepare(i2s->clk);
1056
1057 return 0;
1058}
1059
1060static int i2s_runtime_resume(struct device *dev)
1061{
1062 struct i2s_dai *i2s = dev_get_drvdata(dev);
1063
1064 clk_prepare_enable(i2s->clk);
1065
1066 return 0;
1067}
1068#endif /* CONFIG_PM_RUNTIME */
1069
Bill Pembertonfdca21a2012-12-07 09:26:15 -05001070static int samsung_i2s_probe(struct platform_device *pdev)
Jassi Brar1c7ac012010-11-22 15:36:59 +09001071{
Jassi Brar1c7ac012010-11-22 15:36:59 +09001072 struct i2s_dai *pri_dai, *sec_dai = NULL;
Padmavathi Venna40476f62013-01-18 17:17:01 +05301073 struct s3c_audio_pdata *i2s_pdata = pdev->dev.platform_data;
1074 struct samsung_i2s *i2s_cfg = NULL;
Jassi Brar1c7ac012010-11-22 15:36:59 +09001075 struct resource *res;
Padmavathi Venna40476f62013-01-18 17:17:01 +05301076 u32 regs_base, quirks = 0, idma_addr = 0;
1077 struct device_node *np = pdev->dev.of_node;
Padmavathi Venna7da493e2013-08-12 15:19:51 +05301078 const struct samsung_i2s_dai_data *i2s_dai_data;
Jassi Brar1c7ac012010-11-22 15:36:59 +09001079 int ret = 0;
1080
1081 /* Call during Seconday interface registration */
Padmavathi Venna7da493e2013-08-12 15:19:51 +05301082 i2s_dai_data = samsung_i2s_get_driver_data(pdev);
Padmavathi Venna7c62eeb2013-01-18 17:17:00 +05301083
Padmavathi Venna7da493e2013-08-12 15:19:51 +05301084 if (i2s_dai_data->dai_type == TYPE_SEC) {
Jassi Brar1c7ac012010-11-22 15:36:59 +09001085 sec_dai = dev_get_drvdata(&pdev->dev);
Prathyush Ka9b977e2013-04-02 16:53:01 +05301086 if (!sec_dai) {
1087 dev_err(&pdev->dev, "Unable to get drvdata\n");
1088 return -EFAULT;
1089 }
Kuninori Morimoto4b828532013-03-21 03:35:55 -07001090 snd_soc_register_component(&sec_dai->pdev->dev,
1091 &samsung_i2s_component,
1092 &sec_dai->i2s_dai_drv, 1);
Padmavathi Vennaa08485d82012-12-07 13:59:21 +05301093 asoc_dma_platform_register(&pdev->dev);
Jassi Brar1c7ac012010-11-22 15:36:59 +09001094 return 0;
1095 }
1096
Padmavathi Venna40476f62013-01-18 17:17:01 +05301097 pri_dai = i2s_alloc_dai(pdev, false);
1098 if (!pri_dai) {
1099 dev_err(&pdev->dev, "Unable to alloc I2S_pri\n");
1100 return -ENOMEM;
Jassi Brar1c7ac012010-11-22 15:36:59 +09001101 }
1102
Padmavathi Venna40476f62013-01-18 17:17:01 +05301103 if (!np) {
1104 res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
1105 if (!res) {
1106 dev_err(&pdev->dev,
1107 "Unable to get I2S-TX dma resource\n");
1108 return -ENXIO;
1109 }
1110 pri_dai->dma_playback.channel = res->start;
Jassi Brar1c7ac012010-11-22 15:36:59 +09001111
Padmavathi Venna40476f62013-01-18 17:17:01 +05301112 res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
1113 if (!res) {
1114 dev_err(&pdev->dev,
1115 "Unable to get I2S-RX dma resource\n");
1116 return -ENXIO;
1117 }
1118 pri_dai->dma_capture.channel = res->start;
Jassi Brar1c7ac012010-11-22 15:36:59 +09001119
Padmavathi Venna40476f62013-01-18 17:17:01 +05301120 if (i2s_pdata == NULL) {
1121 dev_err(&pdev->dev, "Can't work without s3c_audio_pdata\n");
1122 return -EINVAL;
1123 }
1124
1125 if (&i2s_pdata->type)
1126 i2s_cfg = &i2s_pdata->type.i2s;
1127
1128 if (i2s_cfg) {
1129 quirks = i2s_cfg->quirks;
1130 idma_addr = i2s_cfg->idma_addr;
1131 }
1132 } else {
Padmavathi Venna7da493e2013-08-12 15:19:51 +05301133 quirks = i2s_dai_data->quirks;
Padmavathi Venna40476f62013-01-18 17:17:01 +05301134 if (of_property_read_u32(np, "samsung,idma-addr",
1135 &idma_addr)) {
1136 if (quirks & QUIRK_SEC_DAI) {
1137 dev_err(&pdev->dev, "idma address is not"\
1138 "specified");
1139 return -EINVAL;
1140 }
1141 }
1142 }
Jassi Brar1c7ac012010-11-22 15:36:59 +09001143
1144 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
1145 if (!res) {
1146 dev_err(&pdev->dev, "Unable to get I2S SFR address\n");
1147 return -ENXIO;
1148 }
1149
1150 if (!request_mem_region(res->start, resource_size(res),
1151 "samsung-i2s")) {
1152 dev_err(&pdev->dev, "Unable to request SFR region\n");
1153 return -EBUSY;
1154 }
1155 regs_base = res->start;
1156
Jassi Brar1c7ac012010-11-22 15:36:59 +09001157 pri_dai->dma_playback.dma_addr = regs_base + I2STXD;
1158 pri_dai->dma_capture.dma_addr = regs_base + I2SRXD;
1159 pri_dai->dma_playback.client =
1160 (struct s3c2410_dma_client *)&pri_dai->dma_playback;
Padmavathi Venna40476f62013-01-18 17:17:01 +05301161 pri_dai->dma_playback.ch_name = "tx";
Jassi Brar1c7ac012010-11-22 15:36:59 +09001162 pri_dai->dma_capture.client =
1163 (struct s3c2410_dma_client *)&pri_dai->dma_capture;
Padmavathi Venna40476f62013-01-18 17:17:01 +05301164 pri_dai->dma_capture.ch_name = "rx";
Jassi Brar1c7ac012010-11-22 15:36:59 +09001165 pri_dai->dma_playback.dma_size = 4;
1166 pri_dai->dma_capture.dma_size = 4;
1167 pri_dai->base = regs_base;
1168 pri_dai->quirks = quirks;
1169
1170 if (quirks & QUIRK_PRI_6CHAN)
1171 pri_dai->i2s_dai_drv.playback.channels_max = 6;
1172
1173 if (quirks & QUIRK_SEC_DAI) {
1174 sec_dai = i2s_alloc_dai(pdev, true);
1175 if (!sec_dai) {
1176 dev_err(&pdev->dev, "Unable to alloc I2S_sec\n");
1177 ret = -ENOMEM;
Mark Brownb960ce72011-12-03 20:30:37 +00001178 goto err;
Jassi Brar1c7ac012010-11-22 15:36:59 +09001179 }
1180 sec_dai->dma_playback.dma_addr = regs_base + I2STXDS;
1181 sec_dai->dma_playback.client =
1182 (struct s3c2410_dma_client *)&sec_dai->dma_playback;
Padmavathi Venna40476f62013-01-18 17:17:01 +05301183 sec_dai->dma_playback.ch_name = "tx-sec";
1184
1185 if (!np) {
1186 res = platform_get_resource(pdev, IORESOURCE_DMA, 2);
1187 if (res)
1188 sec_dai->dma_playback.channel = res->start;
1189 }
1190
Jassi Brar1c7ac012010-11-22 15:36:59 +09001191 sec_dai->dma_playback.dma_size = 4;
1192 sec_dai->base = regs_base;
1193 sec_dai->quirks = quirks;
Padmavathi Venna40476f62013-01-18 17:17:01 +05301194 sec_dai->idma_playback.dma_addr = idma_addr;
Jassi Brar1c7ac012010-11-22 15:36:59 +09001195 sec_dai->pri_dai = pri_dai;
1196 pri_dai->sec_dai = sec_dai;
1197 }
1198
Mark Brown0429ffe2013-07-02 13:10:28 +01001199 if (i2s_pdata && i2s_pdata->cfg_gpio && i2s_pdata->cfg_gpio(pdev)) {
1200 dev_err(&pdev->dev, "Unable to configure gpio\n");
1201 ret = -EINVAL;
1202 goto err;
Jassi Brar1c7ac012010-11-22 15:36:59 +09001203 }
1204
Kuninori Morimoto4b828532013-03-21 03:35:55 -07001205 snd_soc_register_component(&pri_dai->pdev->dev, &samsung_i2s_component,
1206 &pri_dai->i2s_dai_drv, 1);
Jassi Brar1c7ac012010-11-22 15:36:59 +09001207
Mark Brownc5cf4db2011-12-08 16:45:03 +08001208 pm_runtime_enable(&pdev->dev);
1209
Padmavathi Vennaa08485d82012-12-07 13:59:21 +05301210 asoc_dma_platform_register(&pdev->dev);
1211
Jassi Brar1c7ac012010-11-22 15:36:59 +09001212 return 0;
Mark Brownb960ce72011-12-03 20:30:37 +00001213err:
Jassi Brar1c7ac012010-11-22 15:36:59 +09001214 release_mem_region(regs_base, resource_size(res));
1215
1216 return ret;
1217}
1218
Bill Pembertonfdca21a2012-12-07 09:26:15 -05001219static int samsung_i2s_remove(struct platform_device *pdev)
Jassi Brar1c7ac012010-11-22 15:36:59 +09001220{
1221 struct i2s_dai *i2s, *other;
Mark Brownc5cf4db2011-12-08 16:45:03 +08001222 struct resource *res;
Jassi Brar1c7ac012010-11-22 15:36:59 +09001223
1224 i2s = dev_get_drvdata(&pdev->dev);
1225 other = i2s->pri_dai ? : i2s->sec_dai;
1226
1227 if (other) {
1228 other->pri_dai = NULL;
1229 other->sec_dai = NULL;
1230 } else {
Mark Brownc5cf4db2011-12-08 16:45:03 +08001231 pm_runtime_disable(&pdev->dev);
Jassi Brar1c7ac012010-11-22 15:36:59 +09001232 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
1233 if (res)
1234 release_mem_region(res->start, resource_size(res));
1235 }
1236
1237 i2s->pri_dai = NULL;
1238 i2s->sec_dai = NULL;
1239
Padmavathi Vennaa08485d82012-12-07 13:59:21 +05301240 asoc_dma_platform_unregister(&pdev->dev);
Kuninori Morimoto4b828532013-03-21 03:35:55 -07001241 snd_soc_unregister_component(&pdev->dev);
Jassi Brar1c7ac012010-11-22 15:36:59 +09001242
1243 return 0;
1244}
1245
Padmavathi Venna7da493e2013-08-12 15:19:51 +05301246static const struct samsung_i2s_dai_data i2sv3_dai_type = {
1247 .dai_type = TYPE_PRI,
1248 .quirks = QUIRK_NO_MUXPSR,
1249};
1250
1251static const struct samsung_i2s_dai_data i2sv5_dai_type = {
1252 .dai_type = TYPE_PRI,
1253 .quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR,
1254};
1255
1256static const struct samsung_i2s_dai_data samsung_dai_type_pri = {
1257 .dai_type = TYPE_PRI,
1258};
1259
1260static const struct samsung_i2s_dai_data samsung_dai_type_sec = {
1261 .dai_type = TYPE_SEC,
1262};
1263
Padmavathi Venna7c62eeb2013-01-18 17:17:00 +05301264static struct platform_device_id samsung_i2s_driver_ids[] = {
1265 {
1266 .name = "samsung-i2s",
Padmavathi Venna7da493e2013-08-12 15:19:51 +05301267 .driver_data = (kernel_ulong_t)&samsung_dai_type_pri,
Padmavathi Venna7c62eeb2013-01-18 17:17:00 +05301268 }, {
1269 .name = "samsung-i2s-sec",
Padmavathi Venna7da493e2013-08-12 15:19:51 +05301270 .driver_data = (kernel_ulong_t)&samsung_dai_type_sec,
Padmavathi Venna7c62eeb2013-01-18 17:17:00 +05301271 },
1272 {},
1273};
Arnd Bergmann2af19552013-04-11 02:05:03 +02001274MODULE_DEVICE_TABLE(platform, samsung_i2s_driver_ids);
Padmavathi Venna7c62eeb2013-01-18 17:17:00 +05301275
Padmavathi Venna40476f62013-01-18 17:17:01 +05301276#ifdef CONFIG_OF
Padmavathi Venna40476f62013-01-18 17:17:01 +05301277static const struct of_device_id exynos_i2s_match[] = {
Padmavathi Venna7da493e2013-08-12 15:19:51 +05301278 {
1279 .compatible = "samsung,s3c6410-i2s",
1280 .data = &i2sv3_dai_type,
1281 }, {
1282 .compatible = "samsung,s5pv210-i2s",
1283 .data = &i2sv5_dai_type,
Padmavathi Venna40476f62013-01-18 17:17:01 +05301284 },
1285 {},
1286};
1287MODULE_DEVICE_TABLE(of, exynos_i2s_match);
1288#endif
1289
R. Chandrasekar5b1d3c32013-01-30 17:41:04 +05301290static const struct dev_pm_ops samsung_i2s_pm = {
1291 SET_RUNTIME_PM_OPS(i2s_runtime_suspend,
1292 i2s_runtime_resume, NULL)
1293};
1294
Jassi Brar1c7ac012010-11-22 15:36:59 +09001295static struct platform_driver samsung_i2s_driver = {
1296 .probe = samsung_i2s_probe,
Bill Pembertonfdca21a2012-12-07 09:26:15 -05001297 .remove = samsung_i2s_remove,
Padmavathi Venna7c62eeb2013-01-18 17:17:00 +05301298 .id_table = samsung_i2s_driver_ids,
Jassi Brar1c7ac012010-11-22 15:36:59 +09001299 .driver = {
1300 .name = "samsung-i2s",
1301 .owner = THIS_MODULE,
Padmavathi Venna40476f62013-01-18 17:17:01 +05301302 .of_match_table = of_match_ptr(exynos_i2s_match),
R. Chandrasekar5b1d3c32013-01-30 17:41:04 +05301303 .pm = &samsung_i2s_pm,
Jassi Brar1c7ac012010-11-22 15:36:59 +09001304 },
1305};
1306
Mark Browne00c3f52011-11-23 15:20:13 +00001307module_platform_driver(samsung_i2s_driver);
Jassi Brar1c7ac012010-11-22 15:36:59 +09001308
1309/* Module information */
Jaswinder Singhdf8ad332012-02-25 16:24:36 +05301310MODULE_AUTHOR("Jaswinder Singh, <jassisinghbrar@gmail.com>");
Jassi Brar1c7ac012010-11-22 15:36:59 +09001311MODULE_DESCRIPTION("Samsung I2S Interface");
1312MODULE_ALIAS("platform:samsung-i2s");
1313MODULE_LICENSE("GPL");