aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPadmavathi Venna <padma.v@samsung.com>2013-08-12 05:49:52 -0400
committerMark Brown <broonie@linaro.org>2013-08-13 08:44:09 -0400
commit4ca0c0d4784fa82d68733f7793e3487023e12282 (patch)
treef5dfe6ed90617527f19fe399e4af8265a9074cf4
parent7da493e9229c737c399886f57996f6bfd4454e21 (diff)
ASoC: Samsung: I2S: Modify the I2S driver to support I2S on Exynos5420
Exynos5420 added support for I2S TDM mode. For this, there are some register changes in the I2S controller. This patch adds the relevant register changes to support I2S in normal mode. This patch adds a quirk for TDM mode and if TDM mode is present all the relevent changes will be applied. Signed-off-by: Padmavathi Venna <padma.v@samsung.com> Reviewed-by: Tomasz Figa <t.figa@samsung.com> Signed-off-by: Mark Brown <broonie@linaro.org>
-rw-r--r--Documentation/devicetree/bindings/sound/samsung-i2s.txt4
-rw-r--r--include/linux/platform_data/asoc-s3c.h1
-rw-r--r--sound/soc/samsung/i2s-regs.h15
-rw-r--r--sound/soc/samsung/i2s.c81
4 files changed, 93 insertions, 8 deletions
diff --git a/Documentation/devicetree/bindings/sound/samsung-i2s.txt b/Documentation/devicetree/bindings/sound/samsung-i2s.txt
index 25a0024d1b0a..7386d444ada1 100644
--- a/Documentation/devicetree/bindings/sound/samsung-i2s.txt
+++ b/Documentation/devicetree/bindings/sound/samsung-i2s.txt
@@ -6,6 +6,10 @@ 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
10 secondary fifo, s/w reset control, internal mux for root clk src and
11 TDM support. TDM (Time division multiplexing) is to allow transfer of
12 multiple channel audio data on single data line.
9 13
10- reg: physical base address of the controller and length of memory mapped 14- reg: physical base address of the controller and length of memory mapped
11 region. 15 region.
diff --git a/include/linux/platform_data/asoc-s3c.h b/include/linux/platform_data/asoc-s3c.h
index 88272591a895..9efc04dd255a 100644
--- a/include/linux/platform_data/asoc-s3c.h
+++ b/include/linux/platform_data/asoc-s3c.h
@@ -36,6 +36,7 @@ struct samsung_i2s {
36 */ 36 */
37#define QUIRK_NO_MUXPSR (1 << 2) 37#define QUIRK_NO_MUXPSR (1 << 2)
38#define QUIRK_NEED_RSTCLR (1 << 3) 38#define QUIRK_NEED_RSTCLR (1 << 3)
39#define QUIRK_SUPPORTS_TDM (1 << 4)
39 /* Quirks of the I2S controller */ 40 /* Quirks of the I2S controller */
40 u32 quirks; 41 u32 quirks;
41 dma_addr_t idma_addr; 42 dma_addr_t idma_addr;
diff --git a/sound/soc/samsung/i2s-regs.h b/sound/soc/samsung/i2s-regs.h
index 30513b7ede3a..821a50231002 100644
--- a/sound/soc/samsung/i2s-regs.h
+++ b/sound/soc/samsung/i2s-regs.h
@@ -31,6 +31,10 @@
31#define I2SLVL1ADDR 0x34 31#define I2SLVL1ADDR 0x34
32#define I2SLVL2ADDR 0x38 32#define I2SLVL2ADDR 0x38
33#define I2SLVL3ADDR 0x3c 33#define I2SLVL3ADDR 0x3c
34#define I2SSTR1 0x40
35#define I2SVER 0x44
36#define I2SFIC2 0x48
37#define I2STDM 0x4c
34 38
35#define CON_RSTCLR (1 << 31) 39#define CON_RSTCLR (1 << 31)
36#define CON_FRXOFSTATUS (1 << 26) 40#define CON_FRXOFSTATUS (1 << 26)
@@ -117,6 +121,17 @@
117#define MOD_BCLK_MASK 3 121#define MOD_BCLK_MASK 3
118#define MOD_8BIT (1 << 0) 122#define MOD_8BIT (1 << 0)
119 123
124#define EXYNOS5420_MOD_LRP_SHIFT 15
125#define EXYNOS5420_MOD_SDF_SHIFT 6
126#define EXYNOS5420_MOD_RCLK_SHIFT 4
127#define EXYNOS5420_MOD_BCLK_SHIFT 0
128#define EXYNOS5420_MOD_BCLK_64FS 4
129#define EXYNOS5420_MOD_BCLK_96FS 5
130#define EXYNOS5420_MOD_BCLK_128FS 6
131#define EXYNOS5420_MOD_BCLK_192FS 7
132#define EXYNOS5420_MOD_BCLK_256FS 8
133#define EXYNOS5420_MOD_BCLK_MASK 0xf
134
120#define MOD_CDCLKCON (1 << 12) 135#define MOD_CDCLKCON (1 << 12)
121 136
122#define PSR_PSREN (1 << 15) 137#define PSR_PSREN (1 << 15)
diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c
index 3b4835a1bd23..dd995a7ab55c 100644
--- a/sound/soc/samsung/i2s.c
+++ b/sound/soc/samsung/i2s.c
@@ -199,7 +199,12 @@ static inline bool is_manager(struct i2s_dai *i2s)
199/* Read RCLK of I2S (in multiples of LRCLK) */ 199/* Read RCLK of I2S (in multiples of LRCLK) */
200static inline unsigned get_rfs(struct i2s_dai *i2s) 200static inline unsigned get_rfs(struct i2s_dai *i2s)
201{ 201{
202 u32 rfs = (readl(i2s->addr + I2SMOD) >> MOD_RCLK_SHIFT); 202 u32 rfs;
203
204 if (i2s->quirks & QUIRK_SUPPORTS_TDM)
205 rfs = readl(i2s->addr + I2SMOD) >> EXYNOS5420_MOD_RCLK_SHIFT;
206 else
207 rfs = (readl(i2s->addr + I2SMOD) >> MOD_RCLK_SHIFT);
203 rfs &= MOD_RCLK_MASK; 208 rfs &= MOD_RCLK_MASK;
204 209
205 switch (rfs) { 210 switch (rfs) {
@@ -214,8 +219,12 @@ static inline unsigned get_rfs(struct i2s_dai *i2s)
214static inline void set_rfs(struct i2s_dai *i2s, unsigned rfs) 219static inline void set_rfs(struct i2s_dai *i2s, unsigned rfs)
215{ 220{
216 u32 mod = readl(i2s->addr + I2SMOD); 221 u32 mod = readl(i2s->addr + I2SMOD);
217 int rfs_shift = MOD_RCLK_SHIFT; 222 int rfs_shift;
218 223
224 if (i2s->quirks & QUIRK_SUPPORTS_TDM)
225 rfs_shift = EXYNOS5420_MOD_RCLK_SHIFT;
226 else
227 rfs_shift = MOD_RCLK_SHIFT;
219 mod &= ~(MOD_RCLK_MASK << rfs_shift); 228 mod &= ~(MOD_RCLK_MASK << rfs_shift);
220 229
221 switch (rfs) { 230 switch (rfs) {
@@ -239,10 +248,22 @@ static inline void set_rfs(struct i2s_dai *i2s, unsigned rfs)
239/* Read Bit-Clock of I2S (in multiples of LRCLK) */ 248/* Read Bit-Clock of I2S (in multiples of LRCLK) */
240static inline unsigned get_bfs(struct i2s_dai *i2s) 249static inline unsigned get_bfs(struct i2s_dai *i2s)
241{ 250{
242 u32 bfs = readl(i2s->addr + I2SMOD) >> MOD_BCLK_SHIFT; 251 u32 bfs;
243 bfs &= MOD_BCLK_MASK; 252
253 if (i2s->quirks & QUIRK_SUPPORTS_TDM) {
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 }
244 260
245 switch (bfs) { 261 switch (bfs) {
262 case 8: return 256;
263 case 7: return 192;
264 case 6: return 128;
265 case 5: return 96;
266 case 4: return 64;
246 case 3: return 24; 267 case 3: return 24;
247 case 2: return 16; 268 case 2: return 16;
248 case 1: return 48; 269 case 1: return 48;
@@ -254,9 +275,22 @@ static inline unsigned get_bfs(struct i2s_dai *i2s)
254static inline void set_bfs(struct i2s_dai *i2s, unsigned bfs) 275static inline void set_bfs(struct i2s_dai *i2s, unsigned bfs)
255{ 276{
256 u32 mod = readl(i2s->addr + I2SMOD); 277 u32 mod = readl(i2s->addr + I2SMOD);
257 int bfs_shift = MOD_BCLK_SHIFT; 278 int bfs_shift;
279 int tdm = i2s->quirks & QUIRK_SUPPORTS_TDM;
258 280
259 mod &= ~(MOD_BCLK_MASK << bfs_shift); 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
289 /* Non-TDM I2S controllers do not support BCLK > 48 * FS */
290 if (!tdm && bfs > 48) {
291 dev_err(&i2s->pdev->dev, "Unsupported BCLK divider\n");
292 return;
293 }
260 294
261 switch (bfs) { 295 switch (bfs) {
262 case 48: 296 case 48:
@@ -271,6 +305,21 @@ static inline void set_bfs(struct i2s_dai *i2s, unsigned bfs)
271 case 16: 305 case 16:
272 mod |= (MOD_BCLK_16FS << bfs_shift); 306 mod |= (MOD_BCLK_16FS << bfs_shift);
273 break; 307 break;
308 case 64:
309 mod |= (EXYNOS5420_MOD_BCLK_64FS << bfs_shift);
310 break;
311 case 96:
312 mod |= (EXYNOS5420_MOD_BCLK_96FS << bfs_shift);
313 break;
314 case 128:
315 mod |= (EXYNOS5420_MOD_BCLK_128FS << bfs_shift);
316 break;
317 case 192:
318 mod |= (EXYNOS5420_MOD_BCLK_192FS << bfs_shift);
319 break;
320 case 256:
321 mod |= (EXYNOS5420_MOD_BCLK_256FS << bfs_shift);
322 break;
274 default: 323 default:
275 dev_err(&i2s->pdev->dev, "Wrong BCLK Divider!\n"); 324 dev_err(&i2s->pdev->dev, "Wrong BCLK Divider!\n");
276 return; 325 return;
@@ -496,10 +545,17 @@ static int i2s_set_fmt(struct snd_soc_dai *dai,
496{ 545{
497 struct i2s_dai *i2s = to_info(dai); 546 struct i2s_dai *i2s = to_info(dai);
498 u32 mod = readl(i2s->addr + I2SMOD); 547 u32 mod = readl(i2s->addr + I2SMOD);
499 int lrp_shift = MOD_LRP_SHIFT, sdf_shift = MOD_SDF_SHIFT; 548 int lrp_shift, sdf_shift, sdf_mask, lrp_rlow;
500 int sdf_mask, lrp_rlow;
501 u32 tmp = 0; 549 u32 tmp = 0;
502 550
551 if (i2s->quirks & QUIRK_SUPPORTS_TDM) {
552 lrp_shift = EXYNOS5420_MOD_LRP_SHIFT;
553 sdf_shift = EXYNOS5420_MOD_SDF_SHIFT;
554 } else {
555 lrp_shift = MOD_LRP_SHIFT;
556 sdf_shift = MOD_SDF_SHIFT;
557 }
558
503 sdf_mask = MOD_SDF_MASK << sdf_shift; 559 sdf_mask = MOD_SDF_MASK << sdf_shift;
504 lrp_rlow = MOD_LR_RLOW << lrp_shift; 560 lrp_rlow = MOD_LR_RLOW << lrp_shift;
505 561
@@ -1253,6 +1309,12 @@ static const struct samsung_i2s_dai_data i2sv5_dai_type = {
1253 .quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR, 1309 .quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR,
1254}; 1310};
1255 1311
1312static const struct samsung_i2s_dai_data i2sv6_dai_type = {
1313 .dai_type = TYPE_PRI,
1314 .quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR |
1315 QUIRK_SUPPORTS_TDM,
1316};
1317
1256static const struct samsung_i2s_dai_data samsung_dai_type_pri = { 1318static const struct samsung_i2s_dai_data samsung_dai_type_pri = {
1257 .dai_type = TYPE_PRI, 1319 .dai_type = TYPE_PRI,
1258}; 1320};
@@ -1281,6 +1343,9 @@ static const struct of_device_id exynos_i2s_match[] = {
1281 }, { 1343 }, {
1282 .compatible = "samsung,s5pv210-i2s", 1344 .compatible = "samsung,s5pv210-i2s",
1283 .data = &i2sv5_dai_type, 1345 .data = &i2sv5_dai_type,
1346 }, {
1347 .compatible = "samsung,exynos5420-i2s",
1348 .data = &i2sv6_dai_type,
1284 }, 1349 },
1285 {}, 1350 {},
1286}; 1351};