diff options
-rw-r--r-- | Documentation/devicetree/bindings/sound/samsung-i2s.txt | 15 | ||||
-rw-r--r-- | sound/soc/samsung/Kconfig | 2 | ||||
-rw-r--r-- | sound/soc/samsung/i2s-regs.h | 10 | ||||
-rw-r--r-- | sound/soc/samsung/i2s.c | 218 |
4 files changed, 174 insertions, 71 deletions
diff --git a/Documentation/devicetree/bindings/sound/samsung-i2s.txt b/Documentation/devicetree/bindings/sound/samsung-i2s.txt index 7386d444ada1..d188296bb6ec 100644 --- a/Documentation/devicetree/bindings/sound/samsung-i2s.txt +++ b/Documentation/devicetree/bindings/sound/samsung-i2s.txt | |||
@@ -6,10 +6,17 @@ Required SoC Specific Properties: | |||
6 | - samsung,s3c6410-i2s: for 8/16/24bit stereo I2S. | 6 | - samsung,s3c6410-i2s: for 8/16/24bit stereo I2S. |
7 | - samsung,s5pv210-i2s: for 8/16/24bit multichannel(5.1) I2S with | 7 | - samsung,s5pv210-i2s: for 8/16/24bit multichannel(5.1) I2S with |
8 | secondary fifo, s/w reset control and internal mux for root clk src. | 8 | secondary fifo, s/w reset control and internal mux for root clk src. |
9 | - samsung,exynos5420-i2s: for 8/16/24bit multichannel(7.1) I2S with | 9 | - samsung,exynos5420-i2s: for 8/16/24bit multichannel(5.1) I2S for |
10 | secondary fifo, s/w reset control, internal mux for root clk src and | 10 | playback, sterio channel capture, secondary fifo using internal |
11 | TDM support. TDM (Time division multiplexing) is to allow transfer of | 11 | or external dma, s/w reset control, internal mux for root clk src |
12 | multiple channel audio data on single data line. | 12 | and 7.1 channel TDM support for playback. TDM (Time division multiplexing) |
13 | is to allow transfer of multiple channel audio data on single data line. | ||
14 | - samsung,exynos7-i2s: with all the available features of exynos5 i2s, | ||
15 | exynos7 I2S has 7.1 channel TDM support for capture, secondary fifo | ||
16 | with only external dma and more no.of root clk sampling frequencies. | ||
17 | - samsung,exynos7-i2s1: I2S1 on previous samsung platforms supports | ||
18 | stereo channels. exynos7 i2s1 upgraded to 5.1 multichannel with | ||
19 | slightly modified bit offsets. | ||
13 | 20 | ||
14 | - reg: physical base address of the controller and length of memory mapped | 21 | - reg: physical base address of the controller and length of memory mapped |
15 | region. | 22 | region. |
diff --git a/sound/soc/samsung/Kconfig b/sound/soc/samsung/Kconfig index 55a38697443d..e0e737faadd9 100644 --- a/sound/soc/samsung/Kconfig +++ b/sound/soc/samsung/Kconfig | |||
@@ -1,6 +1,6 @@ | |||
1 | config SND_SOC_SAMSUNG | 1 | config SND_SOC_SAMSUNG |
2 | tristate "ASoC support for Samsung" | 2 | tristate "ASoC support for Samsung" |
3 | depends on PLAT_SAMSUNG | 3 | depends on (PLAT_SAMSUNG || ARCH_EXYNOS) |
4 | depends on S3C64XX_PL080 || !ARCH_S3C64XX | 4 | depends on S3C64XX_PL080 || !ARCH_S3C64XX |
5 | depends on S3C24XX_DMAC || !ARCH_S3C24XX | 5 | depends on S3C24XX_DMAC || !ARCH_S3C24XX |
6 | select SND_SOC_GENERIC_DMAENGINE_PCM | 6 | select SND_SOC_GENERIC_DMAENGINE_PCM |
diff --git a/sound/soc/samsung/i2s-regs.h b/sound/soc/samsung/i2s-regs.h index 821a50231002..9170c311d66e 100644 --- a/sound/soc/samsung/i2s-regs.h +++ b/sound/soc/samsung/i2s-regs.h | |||
@@ -33,8 +33,9 @@ | |||
33 | #define I2SLVL3ADDR 0x3c | 33 | #define I2SLVL3ADDR 0x3c |
34 | #define I2SSTR1 0x40 | 34 | #define I2SSTR1 0x40 |
35 | #define I2SVER 0x44 | 35 | #define I2SVER 0x44 |
36 | #define I2SFIC2 0x48 | 36 | #define I2SFIC1 0x48 |
37 | #define I2STDM 0x4c | 37 | #define I2STDM 0x4c |
38 | #define I2SFSTA 0x50 | ||
38 | 39 | ||
39 | #define CON_RSTCLR (1 << 31) | 40 | #define CON_RSTCLR (1 << 31) |
40 | #define CON_FRXOFSTATUS (1 << 26) | 41 | #define CON_FRXOFSTATUS (1 << 26) |
@@ -93,8 +94,6 @@ | |||
93 | #define MOD_BLC_24BIT (2 << 13) | 94 | #define MOD_BLC_24BIT (2 << 13) |
94 | #define MOD_BLC_MASK (3 << 13) | 95 | #define MOD_BLC_MASK (3 << 13) |
95 | 96 | ||
96 | #define MOD_IMS_SYSMUX (1 << 10) | ||
97 | #define MOD_SLAVE (1 << 11) | ||
98 | #define MOD_TXONLY (0 << 8) | 97 | #define MOD_TXONLY (0 << 8) |
99 | #define MOD_RXONLY (1 << 8) | 98 | #define MOD_RXONLY (1 << 8) |
100 | #define MOD_TXRX (2 << 8) | 99 | #define MOD_TXRX (2 << 8) |
@@ -132,7 +131,10 @@ | |||
132 | #define EXYNOS5420_MOD_BCLK_256FS 8 | 131 | #define EXYNOS5420_MOD_BCLK_256FS 8 |
133 | #define EXYNOS5420_MOD_BCLK_MASK 0xf | 132 | #define EXYNOS5420_MOD_BCLK_MASK 0xf |
134 | 133 | ||
135 | #define MOD_CDCLKCON (1 << 12) | 134 | #define EXYNOS7_MOD_RCLK_64FS 4 |
135 | #define EXYNOS7_MOD_RCLK_128FS 5 | ||
136 | #define EXYNOS7_MOD_RCLK_96FS 6 | ||
137 | #define EXYNOS7_MOD_RCLK_192FS 7 | ||
136 | 138 | ||
137 | #define PSR_PSREN (1 << 15) | 139 | #define PSR_PSREN (1 << 15) |
138 | 140 | ||
diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index 38b9a524cc9f..947352d00ddf 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c | |||
@@ -36,9 +36,24 @@ enum samsung_dai_type { | |||
36 | TYPE_SEC, | 36 | TYPE_SEC, |
37 | }; | 37 | }; |
38 | 38 | ||
39 | struct samsung_i2s_variant_regs { | ||
40 | unsigned int bfs_off; | ||
41 | unsigned int rfs_off; | ||
42 | unsigned int sdf_off; | ||
43 | unsigned int txr_off; | ||
44 | unsigned int rclksrc_off; | ||
45 | unsigned int mss_off; | ||
46 | unsigned int cdclkcon_off; | ||
47 | unsigned int lrp_off; | ||
48 | unsigned int bfs_mask; | ||
49 | unsigned int rfs_mask; | ||
50 | unsigned int ftx0cnt_off; | ||
51 | }; | ||
52 | |||
39 | struct samsung_i2s_dai_data { | 53 | struct samsung_i2s_dai_data { |
40 | int dai_type; | 54 | int dai_type; |
41 | u32 quirks; | 55 | u32 quirks; |
56 | const struct samsung_i2s_variant_regs *i2s_variant_regs; | ||
42 | }; | 57 | }; |
43 | 58 | ||
44 | struct i2s_dai { | 59 | struct i2s_dai { |
@@ -81,6 +96,7 @@ struct i2s_dai { | |||
81 | u32 suspend_i2scon; | 96 | u32 suspend_i2scon; |
82 | u32 suspend_i2spsr; | 97 | u32 suspend_i2spsr; |
83 | unsigned long gpios[7]; /* i2s gpio line numbers */ | 98 | unsigned long gpios[7]; /* i2s gpio line numbers */ |
99 | const struct samsung_i2s_variant_regs *variant_regs; | ||
84 | }; | 100 | }; |
85 | 101 | ||
86 | /* Lock for cross i/f checks */ | 102 | /* Lock for cross i/f checks */ |
@@ -95,7 +111,8 @@ static inline bool is_secondary(struct i2s_dai *i2s) | |||
95 | /* If operating in SoC-Slave mode */ | 111 | /* If operating in SoC-Slave mode */ |
96 | static inline bool is_slave(struct i2s_dai *i2s) | 112 | static inline bool is_slave(struct i2s_dai *i2s) |
97 | { | 113 | { |
98 | return (readl(i2s->addr + I2SMOD) & MOD_SLAVE) ? true : false; | 114 | u32 mod = readl(i2s->addr + I2SMOD); |
115 | return (mod & (1 << i2s->variant_regs->mss_off)) ? true : false; | ||
99 | } | 116 | } |
100 | 117 | ||
101 | /* If this interface of the controller is transmitting data */ | 118 | /* If this interface of the controller is transmitting data */ |
@@ -200,14 +217,14 @@ static inline bool is_manager(struct i2s_dai *i2s) | |||
200 | static inline unsigned get_rfs(struct i2s_dai *i2s) | 217 | static inline unsigned get_rfs(struct i2s_dai *i2s) |
201 | { | 218 | { |
202 | u32 rfs; | 219 | u32 rfs; |
203 | 220 | rfs = readl(i2s->addr + I2SMOD) >> i2s->variant_regs->rfs_off; | |
204 | if (i2s->quirks & QUIRK_SUPPORTS_TDM) | 221 | rfs &= i2s->variant_regs->rfs_mask; |
205 | rfs = readl(i2s->addr + I2SMOD) >> EXYNOS5420_MOD_RCLK_SHIFT; | ||
206 | else | ||
207 | rfs = (readl(i2s->addr + I2SMOD) >> MOD_RCLK_SHIFT); | ||
208 | rfs &= MOD_RCLK_MASK; | ||
209 | 222 | ||
210 | switch (rfs) { | 223 | switch (rfs) { |
224 | case 7: return 192; | ||
225 | case 6: return 96; | ||
226 | case 5: return 128; | ||
227 | case 4: return 64; | ||
211 | case 3: return 768; | 228 | case 3: return 768; |
212 | case 2: return 384; | 229 | case 2: return 384; |
213 | case 1: return 512; | 230 | case 1: return 512; |
@@ -219,15 +236,23 @@ static inline unsigned get_rfs(struct i2s_dai *i2s) | |||
219 | static inline void set_rfs(struct i2s_dai *i2s, unsigned rfs) | 236 | static inline void set_rfs(struct i2s_dai *i2s, unsigned rfs) |
220 | { | 237 | { |
221 | u32 mod = readl(i2s->addr + I2SMOD); | 238 | u32 mod = readl(i2s->addr + I2SMOD); |
222 | int rfs_shift; | 239 | int rfs_shift = i2s->variant_regs->rfs_off; |
223 | 240 | ||
224 | if (i2s->quirks & QUIRK_SUPPORTS_TDM) | 241 | mod &= ~(i2s->variant_regs->rfs_mask << rfs_shift); |
225 | rfs_shift = EXYNOS5420_MOD_RCLK_SHIFT; | ||
226 | else | ||
227 | rfs_shift = MOD_RCLK_SHIFT; | ||
228 | mod &= ~(MOD_RCLK_MASK << rfs_shift); | ||
229 | 242 | ||
230 | switch (rfs) { | 243 | switch (rfs) { |
244 | case 192: | ||
245 | mod |= (EXYNOS7_MOD_RCLK_192FS << rfs_shift); | ||
246 | break; | ||
247 | case 96: | ||
248 | mod |= (EXYNOS7_MOD_RCLK_96FS << rfs_shift); | ||
249 | break; | ||
250 | case 128: | ||
251 | mod |= (EXYNOS7_MOD_RCLK_128FS << rfs_shift); | ||
252 | break; | ||
253 | case 64: | ||
254 | mod |= (EXYNOS7_MOD_RCLK_64FS << rfs_shift); | ||
255 | break; | ||
231 | case 768: | 256 | case 768: |
232 | mod |= (MOD_RCLK_768FS << rfs_shift); | 257 | mod |= (MOD_RCLK_768FS << rfs_shift); |
233 | break; | 258 | break; |
@@ -249,14 +274,8 @@ static inline void set_rfs(struct i2s_dai *i2s, unsigned rfs) | |||
249 | static inline unsigned get_bfs(struct i2s_dai *i2s) | 274 | static inline unsigned get_bfs(struct i2s_dai *i2s) |
250 | { | 275 | { |
251 | u32 bfs; | 276 | u32 bfs; |
252 | 277 | bfs = readl(i2s->addr + I2SMOD) >> i2s->variant_regs->bfs_off; | |
253 | if (i2s->quirks & QUIRK_SUPPORTS_TDM) { | 278 | bfs &= i2s->variant_regs->bfs_mask; |
254 | bfs = readl(i2s->addr + I2SMOD) >> EXYNOS5420_MOD_BCLK_SHIFT; | ||
255 | bfs &= EXYNOS5420_MOD_BCLK_MASK; | ||
256 | } else { | ||
257 | bfs = readl(i2s->addr + I2SMOD) >> MOD_BCLK_SHIFT; | ||
258 | bfs &= MOD_BCLK_MASK; | ||
259 | } | ||
260 | 279 | ||
261 | switch (bfs) { | 280 | switch (bfs) { |
262 | case 8: return 256; | 281 | case 8: return 256; |
@@ -275,16 +294,8 @@ static inline unsigned get_bfs(struct i2s_dai *i2s) | |||
275 | static inline void set_bfs(struct i2s_dai *i2s, unsigned bfs) | 294 | static inline void set_bfs(struct i2s_dai *i2s, unsigned bfs) |
276 | { | 295 | { |
277 | u32 mod = readl(i2s->addr + I2SMOD); | 296 | u32 mod = readl(i2s->addr + I2SMOD); |
278 | int bfs_shift; | ||
279 | int tdm = i2s->quirks & QUIRK_SUPPORTS_TDM; | 297 | int tdm = i2s->quirks & QUIRK_SUPPORTS_TDM; |
280 | 298 | int bfs_shift = i2s->variant_regs->bfs_off; | |
281 | if (i2s->quirks & QUIRK_SUPPORTS_TDM) { | ||
282 | bfs_shift = EXYNOS5420_MOD_BCLK_SHIFT; | ||
283 | mod &= ~(EXYNOS5420_MOD_BCLK_MASK << bfs_shift); | ||
284 | } else { | ||
285 | bfs_shift = MOD_BCLK_SHIFT; | ||
286 | mod &= ~(MOD_BCLK_MASK << bfs_shift); | ||
287 | } | ||
288 | 299 | ||
289 | /* Non-TDM I2S controllers do not support BCLK > 48 * FS */ | 300 | /* Non-TDM I2S controllers do not support BCLK > 48 * FS */ |
290 | if (!tdm && bfs > 48) { | 301 | if (!tdm && bfs > 48) { |
@@ -292,6 +303,8 @@ static inline void set_bfs(struct i2s_dai *i2s, unsigned bfs) | |||
292 | return; | 303 | return; |
293 | } | 304 | } |
294 | 305 | ||
306 | mod &= ~(i2s->variant_regs->bfs_mask << bfs_shift); | ||
307 | |||
295 | switch (bfs) { | 308 | switch (bfs) { |
296 | case 48: | 309 | case 48: |
297 | mod |= (MOD_BCLK_48FS << bfs_shift); | 310 | mod |= (MOD_BCLK_48FS << bfs_shift); |
@@ -346,8 +359,9 @@ static inline int get_blc(struct i2s_dai *i2s) | |||
346 | static void i2s_txctrl(struct i2s_dai *i2s, int on) | 359 | static void i2s_txctrl(struct i2s_dai *i2s, int on) |
347 | { | 360 | { |
348 | void __iomem *addr = i2s->addr; | 361 | void __iomem *addr = i2s->addr; |
362 | int txr_off = i2s->variant_regs->txr_off; | ||
349 | u32 con = readl(addr + I2SCON); | 363 | u32 con = readl(addr + I2SCON); |
350 | u32 mod = readl(addr + I2SMOD) & ~MOD_MASK; | 364 | u32 mod = readl(addr + I2SMOD) & ~(3 << txr_off); |
351 | 365 | ||
352 | if (on) { | 366 | if (on) { |
353 | con |= CON_ACTIVE; | 367 | con |= CON_ACTIVE; |
@@ -362,9 +376,9 @@ static void i2s_txctrl(struct i2s_dai *i2s, int on) | |||
362 | } | 376 | } |
363 | 377 | ||
364 | if (any_rx_active(i2s)) | 378 | if (any_rx_active(i2s)) |
365 | mod |= MOD_TXRX; | 379 | mod |= 2 << txr_off; |
366 | else | 380 | else |
367 | mod |= MOD_TXONLY; | 381 | mod |= 0 << txr_off; |
368 | } else { | 382 | } else { |
369 | if (is_secondary(i2s)) { | 383 | if (is_secondary(i2s)) { |
370 | con |= CON_TXSDMA_PAUSE; | 384 | con |= CON_TXSDMA_PAUSE; |
@@ -382,7 +396,7 @@ static void i2s_txctrl(struct i2s_dai *i2s, int on) | |||
382 | con |= CON_TXCH_PAUSE; | 396 | con |= CON_TXCH_PAUSE; |
383 | 397 | ||
384 | if (any_rx_active(i2s)) | 398 | if (any_rx_active(i2s)) |
385 | mod |= MOD_RXONLY; | 399 | mod |= 1 << txr_off; |
386 | else | 400 | else |
387 | con &= ~CON_ACTIVE; | 401 | con &= ~CON_ACTIVE; |
388 | } | 402 | } |
@@ -395,23 +409,24 @@ static void i2s_txctrl(struct i2s_dai *i2s, int on) | |||
395 | static void i2s_rxctrl(struct i2s_dai *i2s, int on) | 409 | static void i2s_rxctrl(struct i2s_dai *i2s, int on) |
396 | { | 410 | { |
397 | void __iomem *addr = i2s->addr; | 411 | void __iomem *addr = i2s->addr; |
412 | int txr_off = i2s->variant_regs->txr_off; | ||
398 | u32 con = readl(addr + I2SCON); | 413 | u32 con = readl(addr + I2SCON); |
399 | u32 mod = readl(addr + I2SMOD) & ~MOD_MASK; | 414 | u32 mod = readl(addr + I2SMOD) & ~(3 << txr_off); |
400 | 415 | ||
401 | if (on) { | 416 | if (on) { |
402 | con |= CON_RXDMA_ACTIVE | CON_ACTIVE; | 417 | con |= CON_RXDMA_ACTIVE | CON_ACTIVE; |
403 | con &= ~(CON_RXDMA_PAUSE | CON_RXCH_PAUSE); | 418 | con &= ~(CON_RXDMA_PAUSE | CON_RXCH_PAUSE); |
404 | 419 | ||
405 | if (any_tx_active(i2s)) | 420 | if (any_tx_active(i2s)) |
406 | mod |= MOD_TXRX; | 421 | mod |= 2 << txr_off; |
407 | else | 422 | else |
408 | mod |= MOD_RXONLY; | 423 | mod |= 1 << txr_off; |
409 | } else { | 424 | } else { |
410 | con |= CON_RXDMA_PAUSE | CON_RXCH_PAUSE; | 425 | con |= CON_RXDMA_PAUSE | CON_RXCH_PAUSE; |
411 | con &= ~CON_RXDMA_ACTIVE; | 426 | con &= ~CON_RXDMA_ACTIVE; |
412 | 427 | ||
413 | if (any_tx_active(i2s)) | 428 | if (any_tx_active(i2s)) |
414 | mod |= MOD_TXONLY; | 429 | mod |= 0 << txr_off; |
415 | else | 430 | else |
416 | con &= ~CON_ACTIVE; | 431 | con &= ~CON_ACTIVE; |
417 | } | 432 | } |
@@ -451,6 +466,9 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, | |||
451 | struct i2s_dai *i2s = to_info(dai); | 466 | struct i2s_dai *i2s = to_info(dai); |
452 | struct i2s_dai *other = i2s->pri_dai ? : i2s->sec_dai; | 467 | struct i2s_dai *other = i2s->pri_dai ? : i2s->sec_dai; |
453 | u32 mod = readl(i2s->addr + I2SMOD); | 468 | u32 mod = readl(i2s->addr + I2SMOD); |
469 | const struct samsung_i2s_variant_regs *i2s_regs = i2s->variant_regs; | ||
470 | unsigned int cdcon_mask = 1 << i2s_regs->cdclkcon_off; | ||
471 | unsigned int rsrc_mask = 1 << i2s_regs->rclksrc_off; | ||
454 | 472 | ||
455 | switch (clk_id) { | 473 | switch (clk_id) { |
456 | case SAMSUNG_I2S_OPCLK: | 474 | case SAMSUNG_I2S_OPCLK: |
@@ -465,18 +483,18 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, | |||
465 | if ((rfs && other && other->rfs && (other->rfs != rfs)) || | 483 | if ((rfs && other && other->rfs && (other->rfs != rfs)) || |
466 | (any_active(i2s) && | 484 | (any_active(i2s) && |
467 | (((dir == SND_SOC_CLOCK_IN) | 485 | (((dir == SND_SOC_CLOCK_IN) |
468 | && !(mod & MOD_CDCLKCON)) || | 486 | && !(mod & cdcon_mask)) || |
469 | ((dir == SND_SOC_CLOCK_OUT) | 487 | ((dir == SND_SOC_CLOCK_OUT) |
470 | && (mod & MOD_CDCLKCON))))) { | 488 | && (mod & cdcon_mask))))) { |
471 | dev_err(&i2s->pdev->dev, | 489 | dev_err(&i2s->pdev->dev, |
472 | "%s:%d Other DAI busy\n", __func__, __LINE__); | 490 | "%s:%d Other DAI busy\n", __func__, __LINE__); |
473 | return -EAGAIN; | 491 | return -EAGAIN; |
474 | } | 492 | } |
475 | 493 | ||
476 | if (dir == SND_SOC_CLOCK_IN) | 494 | if (dir == SND_SOC_CLOCK_IN) |
477 | mod |= MOD_CDCLKCON; | 495 | mod |= 1 << i2s_regs->cdclkcon_off; |
478 | else | 496 | else |
479 | mod &= ~MOD_CDCLKCON; | 497 | mod &= 0 << i2s_regs->cdclkcon_off; |
480 | 498 | ||
481 | i2s->rfs = rfs; | 499 | i2s->rfs = rfs; |
482 | break; | 500 | break; |
@@ -491,8 +509,8 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, | |||
491 | 509 | ||
492 | if (!any_active(i2s)) { | 510 | if (!any_active(i2s)) { |
493 | if (i2s->op_clk && !IS_ERR(i2s->op_clk)) { | 511 | if (i2s->op_clk && !IS_ERR(i2s->op_clk)) { |
494 | if ((clk_id && !(mod & MOD_IMS_SYSMUX)) || | 512 | if ((clk_id && !(mod & rsrc_mask)) || |
495 | (!clk_id && (mod & MOD_IMS_SYSMUX))) { | 513 | (!clk_id && (mod & rsrc_mask))) { |
496 | clk_disable_unprepare(i2s->op_clk); | 514 | clk_disable_unprepare(i2s->op_clk); |
497 | clk_put(i2s->op_clk); | 515 | clk_put(i2s->op_clk); |
498 | } else { | 516 | } else { |
@@ -520,8 +538,8 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, | |||
520 | other->op_clk = i2s->op_clk; | 538 | other->op_clk = i2s->op_clk; |
521 | other->rclk_srcrate = i2s->rclk_srcrate; | 539 | other->rclk_srcrate = i2s->rclk_srcrate; |
522 | } | 540 | } |
523 | } else if ((!clk_id && (mod & MOD_IMS_SYSMUX)) | 541 | } else if ((!clk_id && (mod & rsrc_mask)) |
524 | || (clk_id && !(mod & MOD_IMS_SYSMUX))) { | 542 | || (clk_id && !(mod & rsrc_mask))) { |
525 | dev_err(&i2s->pdev->dev, | 543 | dev_err(&i2s->pdev->dev, |
526 | "%s:%d Other DAI busy\n", __func__, __LINE__); | 544 | "%s:%d Other DAI busy\n", __func__, __LINE__); |
527 | return -EAGAIN; | 545 | return -EAGAIN; |
@@ -533,10 +551,9 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, | |||
533 | } | 551 | } |
534 | 552 | ||
535 | if (clk_id == 0) | 553 | if (clk_id == 0) |
536 | mod &= ~MOD_IMS_SYSMUX; | 554 | mod &= 0 << i2s_regs->rclksrc_off; |
537 | else | 555 | else |
538 | mod |= MOD_IMS_SYSMUX; | 556 | mod |= 1 << i2s_regs->rclksrc_off; |
539 | break; | ||
540 | 557 | ||
541 | default: | 558 | default: |
542 | dev_err(&i2s->pdev->dev, "We don't serve that!\n"); | 559 | dev_err(&i2s->pdev->dev, "We don't serve that!\n"); |
@@ -553,16 +570,12 @@ static int i2s_set_fmt(struct snd_soc_dai *dai, | |||
553 | { | 570 | { |
554 | struct i2s_dai *i2s = to_info(dai); | 571 | struct i2s_dai *i2s = to_info(dai); |
555 | u32 mod = readl(i2s->addr + I2SMOD); | 572 | u32 mod = readl(i2s->addr + I2SMOD); |
556 | int lrp_shift, sdf_shift, sdf_mask, lrp_rlow; | 573 | int lrp_shift, sdf_shift, sdf_mask, lrp_rlow, mod_slave; |
557 | u32 tmp = 0; | 574 | u32 tmp = 0; |
558 | 575 | ||
559 | if (i2s->quirks & QUIRK_SUPPORTS_TDM) { | 576 | lrp_shift = i2s->variant_regs->lrp_off; |
560 | lrp_shift = EXYNOS5420_MOD_LRP_SHIFT; | 577 | sdf_shift = i2s->variant_regs->sdf_off; |
561 | sdf_shift = EXYNOS5420_MOD_SDF_SHIFT; | 578 | mod_slave = 1 << i2s->variant_regs->mss_off; |
562 | } else { | ||
563 | lrp_shift = MOD_LRP_SHIFT; | ||
564 | sdf_shift = MOD_SDF_SHIFT; | ||
565 | } | ||
566 | 579 | ||
567 | sdf_mask = MOD_SDF_MASK << sdf_shift; | 580 | sdf_mask = MOD_SDF_MASK << sdf_shift; |
568 | lrp_rlow = MOD_LR_RLOW << lrp_shift; | 581 | lrp_rlow = MOD_LR_RLOW << lrp_shift; |
@@ -605,7 +618,7 @@ static int i2s_set_fmt(struct snd_soc_dai *dai, | |||
605 | 618 | ||
606 | switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { | 619 | switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { |
607 | case SND_SOC_DAIFMT_CBM_CFM: | 620 | case SND_SOC_DAIFMT_CBM_CFM: |
608 | tmp |= MOD_SLAVE; | 621 | tmp |= mod_slave; |
609 | break; | 622 | break; |
610 | case SND_SOC_DAIFMT_CBS_CFS: | 623 | case SND_SOC_DAIFMT_CBS_CFS: |
611 | /* Set default source clock in Master mode */ | 624 | /* Set default source clock in Master mode */ |
@@ -623,13 +636,13 @@ static int i2s_set_fmt(struct snd_soc_dai *dai, | |||
623 | * channel. | 636 | * channel. |
624 | */ | 637 | */ |
625 | if (any_active(i2s) && | 638 | if (any_active(i2s) && |
626 | ((mod & (sdf_mask | lrp_rlow | MOD_SLAVE)) != tmp)) { | 639 | ((mod & (sdf_mask | lrp_rlow | mod_slave)) != tmp)) { |
627 | dev_err(&i2s->pdev->dev, | 640 | dev_err(&i2s->pdev->dev, |
628 | "%s:%d Other DAI busy\n", __func__, __LINE__); | 641 | "%s:%d Other DAI busy\n", __func__, __LINE__); |
629 | return -EAGAIN; | 642 | return -EAGAIN; |
630 | } | 643 | } |
631 | 644 | ||
632 | mod &= ~(sdf_mask | lrp_rlow | MOD_SLAVE); | 645 | mod &= ~(sdf_mask | lrp_rlow | mod_slave); |
633 | mod |= tmp; | 646 | mod |= tmp; |
634 | writel(mod, i2s->addr + I2SMOD); | 647 | writel(mod, i2s->addr + I2SMOD); |
635 | 648 | ||
@@ -751,6 +764,7 @@ static void i2s_shutdown(struct snd_pcm_substream *substream, | |||
751 | struct i2s_dai *i2s = to_info(dai); | 764 | struct i2s_dai *i2s = to_info(dai); |
752 | struct i2s_dai *other = i2s->pri_dai ? : i2s->sec_dai; | 765 | struct i2s_dai *other = i2s->pri_dai ? : i2s->sec_dai; |
753 | unsigned long flags; | 766 | unsigned long flags; |
767 | const struct samsung_i2s_variant_regs *i2s_regs = i2s->variant_regs; | ||
754 | 768 | ||
755 | spin_lock_irqsave(&lock, flags); | 769 | spin_lock_irqsave(&lock, flags); |
756 | 770 | ||
@@ -761,7 +775,7 @@ static void i2s_shutdown(struct snd_pcm_substream *substream, | |||
761 | other->mode |= DAI_MANAGER; | 775 | other->mode |= DAI_MANAGER; |
762 | } else { | 776 | } else { |
763 | u32 mod = readl(i2s->addr + I2SMOD); | 777 | u32 mod = readl(i2s->addr + I2SMOD); |
764 | i2s->cdclk_out = !(mod & MOD_CDCLKCON); | 778 | i2s->cdclk_out = !(mod & (1 << i2s_regs->cdclkcon_off)); |
765 | if (other) | 779 | if (other) |
766 | other->cdclk_out = i2s->cdclk_out; | 780 | other->cdclk_out = i2s->cdclk_out; |
767 | } | 781 | } |
@@ -914,13 +928,14 @@ i2s_delay(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) | |||
914 | struct i2s_dai *i2s = to_info(dai); | 928 | struct i2s_dai *i2s = to_info(dai); |
915 | u32 reg = readl(i2s->addr + I2SFIC); | 929 | u32 reg = readl(i2s->addr + I2SFIC); |
916 | snd_pcm_sframes_t delay; | 930 | snd_pcm_sframes_t delay; |
931 | const struct samsung_i2s_variant_regs *i2s_regs = i2s->variant_regs; | ||
917 | 932 | ||
918 | if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) | 933 | if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) |
919 | delay = FIC_RXCOUNT(reg); | 934 | delay = FIC_RXCOUNT(reg); |
920 | else if (is_secondary(i2s)) | 935 | else if (is_secondary(i2s)) |
921 | delay = FICS_TXCOUNT(readl(i2s->addr + I2SFICS)); | 936 | delay = FICS_TXCOUNT(readl(i2s->addr + I2SFICS)); |
922 | else | 937 | else |
923 | delay = FIC_TXCOUNT(reg); | 938 | delay = (reg >> i2s_regs->ftx0cnt_off) & 0x7f; |
924 | 939 | ||
925 | return delay; | 940 | return delay; |
926 | } | 941 | } |
@@ -1227,6 +1242,7 @@ static int samsung_i2s_probe(struct platform_device *pdev) | |||
1227 | pri_dai->dma_capture.dma_size = 4; | 1242 | pri_dai->dma_capture.dma_size = 4; |
1228 | pri_dai->base = regs_base; | 1243 | pri_dai->base = regs_base; |
1229 | pri_dai->quirks = quirks; | 1244 | pri_dai->quirks = quirks; |
1245 | pri_dai->variant_regs = i2s_dai_data->i2s_variant_regs; | ||
1230 | 1246 | ||
1231 | if (quirks & QUIRK_PRI_6CHAN) | 1247 | if (quirks & QUIRK_PRI_6CHAN) |
1232 | pri_dai->i2s_dai_drv.playback.channels_max = 6; | 1248 | pri_dai->i2s_dai_drv.playback.channels_max = 6; |
@@ -1301,21 +1317,93 @@ static int samsung_i2s_remove(struct platform_device *pdev) | |||
1301 | return 0; | 1317 | return 0; |
1302 | } | 1318 | } |
1303 | 1319 | ||
1320 | static const struct samsung_i2s_variant_regs i2sv3_regs = { | ||
1321 | .bfs_off = 1, | ||
1322 | .rfs_off = 3, | ||
1323 | .sdf_off = 5, | ||
1324 | .txr_off = 8, | ||
1325 | .rclksrc_off = 10, | ||
1326 | .mss_off = 11, | ||
1327 | .cdclkcon_off = 12, | ||
1328 | .lrp_off = 7, | ||
1329 | .bfs_mask = 0x3, | ||
1330 | .rfs_mask = 0x3, | ||
1331 | .ftx0cnt_off = 8, | ||
1332 | }; | ||
1333 | |||
1334 | static const struct samsung_i2s_variant_regs i2sv6_regs = { | ||
1335 | .bfs_off = 0, | ||
1336 | .rfs_off = 4, | ||
1337 | .sdf_off = 6, | ||
1338 | .txr_off = 8, | ||
1339 | .rclksrc_off = 10, | ||
1340 | .mss_off = 11, | ||
1341 | .cdclkcon_off = 12, | ||
1342 | .lrp_off = 15, | ||
1343 | .bfs_mask = 0xf, | ||
1344 | .rfs_mask = 0x3, | ||
1345 | .ftx0cnt_off = 8, | ||
1346 | }; | ||
1347 | |||
1348 | static const struct samsung_i2s_variant_regs i2sv7_regs = { | ||
1349 | .bfs_off = 0, | ||
1350 | .rfs_off = 4, | ||
1351 | .sdf_off = 7, | ||
1352 | .txr_off = 9, | ||
1353 | .rclksrc_off = 11, | ||
1354 | .mss_off = 12, | ||
1355 | .cdclkcon_off = 22, | ||
1356 | .lrp_off = 15, | ||
1357 | .bfs_mask = 0xf, | ||
1358 | .rfs_mask = 0x7, | ||
1359 | .ftx0cnt_off = 0, | ||
1360 | }; | ||
1361 | |||
1362 | static const struct samsung_i2s_variant_regs i2sv5_i2s1_regs = { | ||
1363 | .bfs_off = 0, | ||
1364 | .rfs_off = 3, | ||
1365 | .sdf_off = 6, | ||
1366 | .txr_off = 8, | ||
1367 | .rclksrc_off = 10, | ||
1368 | .mss_off = 11, | ||
1369 | .cdclkcon_off = 12, | ||
1370 | .lrp_off = 15, | ||
1371 | .bfs_mask = 0x7, | ||
1372 | .rfs_mask = 0x7, | ||
1373 | .ftx0cnt_off = 8, | ||
1374 | }; | ||
1375 | |||
1304 | static const struct samsung_i2s_dai_data i2sv3_dai_type = { | 1376 | static const struct samsung_i2s_dai_data i2sv3_dai_type = { |
1305 | .dai_type = TYPE_PRI, | 1377 | .dai_type = TYPE_PRI, |
1306 | .quirks = QUIRK_NO_MUXPSR, | 1378 | .quirks = QUIRK_NO_MUXPSR, |
1379 | .i2s_variant_regs = &i2sv3_regs, | ||
1307 | }; | 1380 | }; |
1308 | 1381 | ||
1309 | static const struct samsung_i2s_dai_data i2sv5_dai_type = { | 1382 | static const struct samsung_i2s_dai_data i2sv5_dai_type = { |
1310 | .dai_type = TYPE_PRI, | 1383 | .dai_type = TYPE_PRI, |
1311 | .quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR | | 1384 | .quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR | |
1312 | QUIRK_SUPPORTS_IDMA, | 1385 | QUIRK_SUPPORTS_IDMA, |
1386 | .i2s_variant_regs = &i2sv3_regs, | ||
1313 | }; | 1387 | }; |
1314 | 1388 | ||
1315 | static const struct samsung_i2s_dai_data i2sv6_dai_type = { | 1389 | static const struct samsung_i2s_dai_data i2sv6_dai_type = { |
1316 | .dai_type = TYPE_PRI, | 1390 | .dai_type = TYPE_PRI, |
1317 | .quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR | | 1391 | .quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR | |
1318 | QUIRK_SUPPORTS_TDM | QUIRK_SUPPORTS_IDMA, | 1392 | QUIRK_SUPPORTS_TDM | QUIRK_SUPPORTS_IDMA, |
1393 | .i2s_variant_regs = &i2sv6_regs, | ||
1394 | }; | ||
1395 | |||
1396 | static const struct samsung_i2s_dai_data i2sv7_dai_type = { | ||
1397 | .dai_type = TYPE_PRI, | ||
1398 | .quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR | | ||
1399 | QUIRK_SUPPORTS_TDM, | ||
1400 | .i2s_variant_regs = &i2sv7_regs, | ||
1401 | }; | ||
1402 | |||
1403 | static const struct samsung_i2s_dai_data i2sv5_dai_type_i2s1 = { | ||
1404 | .dai_type = TYPE_PRI, | ||
1405 | .quirks = QUIRK_PRI_6CHAN | QUIRK_NEED_RSTCLR, | ||
1406 | .i2s_variant_regs = &i2sv5_i2s1_regs, | ||
1319 | }; | 1407 | }; |
1320 | 1408 | ||
1321 | static const struct samsung_i2s_dai_data samsung_dai_type_pri = { | 1409 | static const struct samsung_i2s_dai_data samsung_dai_type_pri = { |
@@ -1349,6 +1437,12 @@ static const struct of_device_id exynos_i2s_match[] = { | |||
1349 | }, { | 1437 | }, { |
1350 | .compatible = "samsung,exynos5420-i2s", | 1438 | .compatible = "samsung,exynos5420-i2s", |
1351 | .data = &i2sv6_dai_type, | 1439 | .data = &i2sv6_dai_type, |
1440 | }, { | ||
1441 | .compatible = "samsung,exynos7-i2s", | ||
1442 | .data = &i2sv7_dai_type, | ||
1443 | }, { | ||
1444 | .compatible = "samsung,exynos7-i2s1", | ||
1445 | .data = &i2sv5_dai_type_i2s1, | ||
1352 | }, | 1446 | }, |
1353 | {}, | 1447 | {}, |
1354 | }; | 1448 | }; |