]>
Commit | Line | Data |
---|---|---|
83d290c5 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
511ed5fd RS |
2 | /* |
3 | * Copyright (C) 2012 Samsung Electronics | |
4 | * R. Chandrasekar <[email protected]> | |
511ed5fd RS |
5 | */ |
6 | ||
a1efd49e | 7 | #include <common.h> |
d6cadd59 | 8 | #include <dm.h> |
a1efd49e | 9 | #include <i2s.h> |
f7ae49fc | 10 | #include <log.h> |
a1efd49e | 11 | #include <sound.h> |
511ed5fd RS |
12 | #include <asm/arch/clk.h> |
13 | #include <asm/arch/pinmux.h> | |
14 | #include <asm/arch/i2s-regs.h> | |
15 | #include <asm/io.h> | |
511ed5fd RS |
16 | |
17 | #define FIC_TX2COUNT(x) (((x) >> 24) & 0xf) | |
18 | #define FIC_TX1COUNT(x) (((x) >> 16) & 0xf) | |
19 | #define FIC_TXCOUNT(x) (((x) >> 8) & 0xf) | |
20 | #define FIC_RXCOUNT(x) (((x) >> 0) & 0xf) | |
21 | #define FICS_TXCOUNT(x) (((x) >> 8) & 0x7f) | |
22 | ||
23 | #define TIMEOUT_I2S_TX 100 /* i2s transfer timeout */ | |
24 | ||
25 | /* | |
26 | * Sets the frame size for I2S LR clock | |
27 | * | |
4a68a60e | 28 | * @param i2s_reg i2s register address |
511ed5fd RS |
29 | * @param rfs Frame Size |
30 | */ | |
31 | static void i2s_set_lr_framesize(struct i2s_reg *i2s_reg, unsigned int rfs) | |
32 | { | |
33 | unsigned int mod = readl(&i2s_reg->mod); | |
34 | ||
35 | mod &= ~MOD_RCLK_MASK; | |
36 | ||
37 | switch (rfs) { | |
38 | case 768: | |
39 | mod |= MOD_RCLK_768FS; | |
40 | break; | |
41 | case 512: | |
42 | mod |= MOD_RCLK_512FS; | |
43 | break; | |
44 | case 384: | |
45 | mod |= MOD_RCLK_384FS; | |
46 | break; | |
47 | default: | |
48 | mod |= MOD_RCLK_256FS; | |
49 | break; | |
50 | } | |
51 | ||
52 | writel(mod, &i2s_reg->mod); | |
53 | } | |
54 | ||
55 | /* | |
56 | * Sets the i2s transfer control | |
57 | * | |
4a68a60e | 58 | * @param i2s_reg i2s register address |
511ed5fd RS |
59 | * @param on 1 enable tx , 0 disable tx transfer |
60 | */ | |
61 | static void i2s_txctrl(struct i2s_reg *i2s_reg, int on) | |
62 | { | |
63 | unsigned int con = readl(&i2s_reg->con); | |
64 | unsigned int mod = readl(&i2s_reg->mod) & ~MOD_MASK; | |
65 | ||
66 | if (on) { | |
67 | con |= CON_ACTIVE; | |
68 | con &= ~CON_TXCH_PAUSE; | |
511ed5fd | 69 | } else { |
511ed5fd RS |
70 | con |= CON_TXCH_PAUSE; |
71 | con &= ~CON_ACTIVE; | |
72 | } | |
73 | ||
74 | writel(mod, &i2s_reg->mod); | |
75 | writel(con, &i2s_reg->con); | |
76 | } | |
77 | ||
78 | /* | |
79 | * set the bit clock frame size (in multiples of LRCLK) | |
80 | * | |
4a68a60e | 81 | * @param i2s_reg i2s register address |
511ed5fd RS |
82 | * @param bfs bit Frame Size |
83 | */ | |
84 | static void i2s_set_bitclk_framesize(struct i2s_reg *i2s_reg, unsigned bfs) | |
85 | { | |
86 | unsigned int mod = readl(&i2s_reg->mod); | |
87 | ||
88 | mod &= ~MOD_BCLK_MASK; | |
89 | ||
90 | switch (bfs) { | |
91 | case 48: | |
92 | mod |= MOD_BCLK_48FS; | |
93 | break; | |
94 | case 32: | |
95 | mod |= MOD_BCLK_32FS; | |
96 | break; | |
97 | case 24: | |
98 | mod |= MOD_BCLK_24FS; | |
99 | break; | |
100 | case 16: | |
101 | mod |= MOD_BCLK_16FS; | |
102 | break; | |
103 | default: | |
104 | return; | |
105 | } | |
106 | writel(mod, &i2s_reg->mod); | |
107 | } | |
108 | ||
109 | /* | |
110 | * flushes the i2stx fifo | |
111 | * | |
4a68a60e | 112 | * @param i2s_reg i2s register address |
511ed5fd RS |
113 | * @param flush Tx fifo flush command (0x00 - do not flush |
114 | * 0x80 - flush tx fifo) | |
115 | */ | |
eb133502 | 116 | static void i2s_fifo(struct i2s_reg *i2s_reg, unsigned int flush) |
511ed5fd RS |
117 | { |
118 | /* Flush the FIFO */ | |
119 | setbits_le32(&i2s_reg->fic, flush); | |
120 | clrbits_le32(&i2s_reg->fic, flush); | |
121 | } | |
122 | ||
123 | /* | |
124 | * Set System Clock direction | |
125 | * | |
4a68a60e | 126 | * @param i2s_reg i2s register address |
511ed5fd RS |
127 | * @param dir Clock direction |
128 | * | |
185f812c | 129 | * Return: int value 0 for success, -1 in case of error |
511ed5fd | 130 | */ |
eb133502 | 131 | static int i2s_set_sysclk_dir(struct i2s_reg *i2s_reg, int dir) |
511ed5fd RS |
132 | { |
133 | unsigned int mod = readl(&i2s_reg->mod); | |
134 | ||
135 | if (dir == SND_SOC_CLOCK_IN) | |
136 | mod |= MOD_CDCLKCON; | |
137 | else | |
138 | mod &= ~MOD_CDCLKCON; | |
139 | ||
140 | writel(mod, &i2s_reg->mod); | |
141 | ||
142 | return 0; | |
143 | } | |
144 | ||
145 | /* | |
146 | * Sets I2S Clcok format | |
147 | * | |
148 | * @param fmt i2s clock properties | |
4a68a60e | 149 | * @param i2s_reg i2s register address |
511ed5fd | 150 | * |
185f812c | 151 | * Return: int value 0 for success, -1 in case of error |
511ed5fd | 152 | */ |
eb133502 | 153 | static int i2s_set_fmt(struct i2s_reg *i2s_reg, unsigned int fmt) |
511ed5fd RS |
154 | { |
155 | unsigned int mod = readl(&i2s_reg->mod); | |
156 | unsigned int tmp = 0; | |
157 | unsigned int ret = 0; | |
158 | ||
159 | /* Format is priority */ | |
160 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { | |
161 | case SND_SOC_DAIFMT_RIGHT_J: | |
162 | tmp |= MOD_LR_RLOW; | |
163 | tmp |= MOD_SDF_MSB; | |
164 | break; | |
165 | case SND_SOC_DAIFMT_LEFT_J: | |
166 | tmp |= MOD_LR_RLOW; | |
167 | tmp |= MOD_SDF_LSB; | |
168 | break; | |
169 | case SND_SOC_DAIFMT_I2S: | |
170 | tmp |= MOD_SDF_IIS; | |
171 | break; | |
172 | default: | |
173 | debug("%s: Invalid format priority [0x%x]\n", __func__, | |
3dd22a37 | 174 | (fmt & SND_SOC_DAIFMT_FORMAT_MASK)); |
a1efd49e | 175 | return -ERANGE; |
511ed5fd RS |
176 | } |
177 | ||
178 | /* | |
179 | * INV flag is relative to the FORMAT flag - if set it simply | |
180 | * flips the polarity specified by the Standard | |
181 | */ | |
182 | switch (fmt & SND_SOC_DAIFMT_INV_MASK) { | |
183 | case SND_SOC_DAIFMT_NB_NF: | |
184 | break; | |
185 | case SND_SOC_DAIFMT_NB_IF: | |
186 | if (tmp & MOD_LR_RLOW) | |
187 | tmp &= ~MOD_LR_RLOW; | |
188 | else | |
189 | tmp |= MOD_LR_RLOW; | |
190 | break; | |
191 | default: | |
192 | debug("%s: Invalid clock ploarity input [0x%x]\n", __func__, | |
3dd22a37 | 193 | (fmt & SND_SOC_DAIFMT_INV_MASK)); |
a1efd49e | 194 | return -ERANGE; |
511ed5fd RS |
195 | } |
196 | ||
197 | switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { | |
198 | case SND_SOC_DAIFMT_CBS_CFS: | |
199 | tmp |= MOD_SLAVE; | |
200 | break; | |
201 | case SND_SOC_DAIFMT_CBM_CFM: | |
202 | /* Set default source clock in Master mode */ | |
203 | ret = i2s_set_sysclk_dir(i2s_reg, SND_SOC_CLOCK_OUT); | |
204 | if (ret != 0) { | |
205 | debug("%s:set i2s clock direction failed\n", __func__); | |
a1efd49e | 206 | return ret; |
511ed5fd RS |
207 | } |
208 | break; | |
209 | default: | |
210 | debug("%s: Invalid master selection [0x%x]\n", __func__, | |
3dd22a37 | 211 | (fmt & SND_SOC_DAIFMT_MASTER_MASK)); |
a1efd49e | 212 | return -ERANGE; |
511ed5fd RS |
213 | } |
214 | ||
215 | mod &= ~(MOD_SDF_MASK | MOD_LR_RLOW | MOD_SLAVE); | |
216 | mod |= tmp; | |
217 | writel(mod, &i2s_reg->mod); | |
218 | ||
219 | return 0; | |
220 | } | |
221 | ||
222 | /* | |
223 | * Sets the sample width in bits | |
224 | * | |
225 | * @param blc samplewidth (size of sample in bits) | |
4a68a60e | 226 | * @param i2s_reg i2s register address |
511ed5fd | 227 | * |
185f812c | 228 | * Return: int value 0 for success, -1 in case of error |
511ed5fd | 229 | */ |
eb133502 | 230 | static int i2s_set_samplesize(struct i2s_reg *i2s_reg, unsigned int blc) |
511ed5fd RS |
231 | { |
232 | unsigned int mod = readl(&i2s_reg->mod); | |
233 | ||
234 | mod &= ~MOD_BLCP_MASK; | |
235 | mod &= ~MOD_BLC_MASK; | |
236 | ||
237 | switch (blc) { | |
238 | case 8: | |
239 | mod |= MOD_BLCP_8BIT; | |
240 | mod |= MOD_BLC_8BIT; | |
241 | break; | |
242 | case 16: | |
243 | mod |= MOD_BLCP_16BIT; | |
244 | mod |= MOD_BLC_16BIT; | |
245 | break; | |
246 | case 24: | |
247 | mod |= MOD_BLCP_24BIT; | |
248 | mod |= MOD_BLC_24BIT; | |
249 | break; | |
250 | default: | |
251 | debug("%s: Invalid sample size input [0x%x]\n", | |
3dd22a37 | 252 | __func__, blc); |
a1efd49e | 253 | return -ERANGE; |
511ed5fd RS |
254 | } |
255 | writel(mod, &i2s_reg->mod); | |
256 | ||
257 | return 0; | |
258 | } | |
259 | ||
d6cadd59 SG |
260 | int i2s_transfer_tx_data(struct i2s_uc_priv *pi2s_tx, void *data, |
261 | uint data_size) | |
511ed5fd | 262 | { |
d6cadd59 SG |
263 | struct i2s_reg *i2s_reg = (struct i2s_reg *)pi2s_tx->base_address; |
264 | u32 *ptr; | |
511ed5fd RS |
265 | int i; |
266 | int start; | |
511ed5fd RS |
267 | |
268 | if (data_size < FIFO_LENGTH) { | |
269 | debug("%s : Invalid data size\n", __func__); | |
a1efd49e | 270 | return -ENODATA; /* invalid pcm data size */ |
511ed5fd RS |
271 | } |
272 | ||
273 | /* fill the tx buffer before stating the tx transmit */ | |
d6cadd59 SG |
274 | for (i = 0, ptr = data; i < FIFO_LENGTH; i++) |
275 | writel(*ptr++, &i2s_reg->txd); | |
511ed5fd | 276 | |
d6cadd59 | 277 | data_size -= sizeof(*ptr) * FIFO_LENGTH; |
511ed5fd RS |
278 | i2s_txctrl(i2s_reg, I2S_TX_ON); |
279 | ||
280 | while (data_size > 0) { | |
281 | start = get_timer(0); | |
282 | if (!(CON_TXFIFO_FULL & (readl(&i2s_reg->con)))) { | |
d6cadd59 SG |
283 | writel(*ptr++, &i2s_reg->txd); |
284 | data_size -= sizeof(*ptr); | |
511ed5fd RS |
285 | } else { |
286 | if (get_timer(start) > TIMEOUT_I2S_TX) { | |
287 | i2s_txctrl(i2s_reg, I2S_TX_OFF); | |
288 | debug("%s: I2S Transfer Timeout\n", __func__); | |
a1efd49e | 289 | return -ETIMEDOUT; |
511ed5fd RS |
290 | } |
291 | } | |
292 | } | |
293 | i2s_txctrl(i2s_reg, I2S_TX_OFF); | |
294 | ||
295 | return 0; | |
296 | } | |
297 | ||
4a68a60e | 298 | static int i2s_tx_init(struct i2s_uc_priv *pi2s_tx) |
511ed5fd RS |
299 | { |
300 | int ret; | |
d6cadd59 SG |
301 | struct i2s_reg *i2s_reg = (struct i2s_reg *)pi2s_tx->base_address; |
302 | ||
5fb5b155 DKM |
303 | if (pi2s_tx->id == 0) { |
304 | /* Initialize GPIO for I2S-0 */ | |
305 | exynos_pinmux_config(PERIPH_ID_I2S0, 0); | |
306 | ||
307 | /* Set EPLL Clock */ | |
308 | ret = set_epll_clk(pi2s_tx->samplingrate * pi2s_tx->rfs * 4); | |
309 | } else if (pi2s_tx->id == 1) { | |
310 | /* Initialize GPIO for I2S-1 */ | |
311 | exynos_pinmux_config(PERIPH_ID_I2S1, 0); | |
312 | ||
313 | /* Set EPLL Clock */ | |
314 | ret = set_epll_clk(pi2s_tx->audio_pll_clk); | |
315 | } else { | |
316 | debug("%s: unsupported i2s-%d bus\n", __func__, pi2s_tx->id); | |
a1efd49e | 317 | return -ERANGE; |
5fb5b155 | 318 | } |
511ed5fd | 319 | |
a1efd49e | 320 | if (ret) { |
5fb5b155 | 321 | debug("%s: epll clock set rate failed\n", __func__); |
a1efd49e | 322 | return ret; |
511ed5fd RS |
323 | } |
324 | ||
5fb5b155 | 325 | /* Select Clk Source for Audio 0 or 1 */ |
3dd22a37 | 326 | ret = set_i2s_clk_source(pi2s_tx->id); |
a1efd49e | 327 | if (ret) { |
3dd22a37 DKM |
328 | debug("%s: unsupported clock for i2s-%d\n", __func__, |
329 | pi2s_tx->id); | |
a1efd49e | 330 | return ret; |
3dd22a37 | 331 | } |
511ed5fd | 332 | |
5fb5b155 DKM |
333 | if (pi2s_tx->id == 0) { |
334 | /*Reset the i2s module */ | |
335 | writel(CON_RESET, &i2s_reg->con); | |
336 | ||
337 | writel(MOD_OP_CLK | MOD_RCLKSRC, &i2s_reg->mod); | |
338 | /* set i2s prescaler */ | |
339 | writel(PSREN | PSVAL, &i2s_reg->psr); | |
340 | } else { | |
341 | /* Set Prescaler to get MCLK */ | |
342 | ret = set_i2s_clk_prescaler(pi2s_tx->audio_pll_clk, | |
343 | (pi2s_tx->samplingrate * (pi2s_tx->rfs)), | |
344 | pi2s_tx->id); | |
345 | } | |
a1efd49e | 346 | if (ret) { |
3dd22a37 DKM |
347 | debug("%s: unsupported prescalar for i2s-%d\n", __func__, |
348 | pi2s_tx->id); | |
a1efd49e | 349 | return ret; |
3dd22a37 | 350 | } |
511ed5fd RS |
351 | |
352 | /* Configure I2s format */ | |
d6cadd59 SG |
353 | ret = i2s_set_fmt(i2s_reg, SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | |
354 | SND_SOC_DAIFMT_CBM_CFM); | |
511ed5fd RS |
355 | if (ret == 0) { |
356 | i2s_set_lr_framesize(i2s_reg, pi2s_tx->rfs); | |
357 | ret = i2s_set_samplesize(i2s_reg, pi2s_tx->bitspersample); | |
358 | if (ret != 0) { | |
359 | debug("%s:set sample rate failed\n", __func__); | |
a1efd49e | 360 | return ret; |
511ed5fd RS |
361 | } |
362 | ||
363 | i2s_set_bitclk_framesize(i2s_reg, pi2s_tx->bfs); | |
364 | /* disable i2s transfer flag and flush the fifo */ | |
365 | i2s_txctrl(i2s_reg, I2S_TX_OFF); | |
366 | i2s_fifo(i2s_reg, FIC_TXFLUSH); | |
367 | } else { | |
368 | debug("%s: failed\n", __func__); | |
369 | } | |
370 | ||
371 | return ret; | |
372 | } | |
d6cadd59 SG |
373 | |
374 | static int samsung_i2s_tx_data(struct udevice *dev, void *data, uint data_size) | |
375 | { | |
376 | struct i2s_uc_priv *priv = dev_get_uclass_priv(dev); | |
377 | ||
378 | return i2s_transfer_tx_data(priv, data, data_size); | |
379 | } | |
380 | ||
381 | static int samsung_i2s_probe(struct udevice *dev) | |
382 | { | |
383 | struct i2s_uc_priv *priv = dev_get_uclass_priv(dev); | |
384 | ||
385 | return i2s_tx_init(priv); | |
386 | } | |
387 | ||
d1998a9f | 388 | static int samsung_i2s_of_to_plat(struct udevice *dev) |
d6cadd59 SG |
389 | { |
390 | struct i2s_uc_priv *priv = dev_get_uclass_priv(dev); | |
391 | ulong base; | |
392 | ||
393 | /* | |
394 | * Get the pre-defined sound specific values from FDT. | |
395 | * All of these are expected to be correct otherwise | |
396 | * wrong register values in i2s setup parameters | |
397 | * may result in no sound play. | |
398 | */ | |
399 | base = dev_read_addr(dev); | |
400 | if (base == FDT_ADDR_T_NONE) { | |
401 | debug("%s: Missing i2s base\n", __func__); | |
402 | return -EINVAL; | |
403 | } | |
404 | priv->base_address = base; | |
405 | ||
406 | if (dev_read_u32u(dev, "samsung,i2s-epll-clock-frequency", | |
407 | &priv->audio_pll_clk)) | |
408 | goto err; | |
409 | debug("audio_pll_clk = %d\n", priv->audio_pll_clk); | |
410 | if (dev_read_u32u(dev, "samsung,i2s-sampling-rate", | |
411 | &priv->samplingrate)) | |
412 | goto err; | |
413 | debug("samplingrate = %d\n", priv->samplingrate); | |
414 | if (dev_read_u32u(dev, "samsung,i2s-bits-per-sample", | |
415 | &priv->bitspersample)) | |
416 | goto err; | |
417 | debug("bitspersample = %d\n", priv->bitspersample); | |
418 | if (dev_read_u32u(dev, "samsung,i2s-channels", &priv->channels)) | |
419 | goto err; | |
420 | debug("channels = %d\n", priv->channels); | |
421 | if (dev_read_u32u(dev, "samsung,i2s-lr-clk-framesize", &priv->rfs)) | |
422 | goto err; | |
423 | debug("rfs = %d\n", priv->rfs); | |
424 | if (dev_read_u32u(dev, "samsung,i2s-bit-clk-framesize", &priv->bfs)) | |
425 | goto err; | |
426 | debug("bfs = %d\n", priv->bfs); | |
427 | ||
428 | if (dev_read_u32u(dev, "samsung,i2s-id", &priv->id)) | |
429 | goto err; | |
430 | debug("id = %d\n", priv->id); | |
431 | ||
432 | return 0; | |
433 | ||
434 | err: | |
435 | debug("fail to get sound i2s node properties\n"); | |
436 | ||
437 | return -EINVAL; | |
438 | } | |
439 | ||
440 | static const struct i2s_ops samsung_i2s_ops = { | |
441 | .tx_data = samsung_i2s_tx_data, | |
442 | }; | |
443 | ||
444 | static const struct udevice_id samsung_i2s_ids[] = { | |
445 | { .compatible = "samsung,s5pv210-i2s" }, | |
446 | { } | |
447 | }; | |
448 | ||
449 | U_BOOT_DRIVER(samsung_i2s) = { | |
450 | .name = "samsung_i2s", | |
451 | .id = UCLASS_I2S, | |
452 | .of_match = samsung_i2s_ids, | |
453 | .probe = samsung_i2s_probe, | |
d1998a9f | 454 | .of_to_plat = samsung_i2s_of_to_plat, |
d6cadd59 SG |
455 | .ops = &samsung_i2s_ops, |
456 | }; |