]>
Commit | Line | Data |
---|---|---|
511ed5fd RS |
1 | /* |
2 | * Copyright (C) 2012 Samsung Electronics | |
3 | * R. Chandrasekar <rcsekar@samsung.com> | |
4 | * | |
1a459660 | 5 | * SPDX-License-Identifier: GPL-2.0+ |
511ed5fd RS |
6 | */ |
7 | ||
8 | #include <asm/arch/clk.h> | |
9 | #include <asm/arch/pinmux.h> | |
10 | #include <asm/arch/i2s-regs.h> | |
11 | #include <asm/io.h> | |
12 | #include <common.h> | |
13 | #include <sound.h> | |
14 | #include <i2s.h> | |
15 | ||
16 | #define FIC_TX2COUNT(x) (((x) >> 24) & 0xf) | |
17 | #define FIC_TX1COUNT(x) (((x) >> 16) & 0xf) | |
18 | #define FIC_TXCOUNT(x) (((x) >> 8) & 0xf) | |
19 | #define FIC_RXCOUNT(x) (((x) >> 0) & 0xf) | |
20 | #define FICS_TXCOUNT(x) (((x) >> 8) & 0x7f) | |
21 | ||
22 | #define TIMEOUT_I2S_TX 100 /* i2s transfer timeout */ | |
23 | ||
24 | /* | |
25 | * Sets the frame size for I2S LR clock | |
26 | * | |
27 | * @param i2s_reg i2s regiter address | |
28 | * @param rfs Frame Size | |
29 | */ | |
30 | static void i2s_set_lr_framesize(struct i2s_reg *i2s_reg, unsigned int rfs) | |
31 | { | |
32 | unsigned int mod = readl(&i2s_reg->mod); | |
33 | ||
34 | mod &= ~MOD_RCLK_MASK; | |
35 | ||
36 | switch (rfs) { | |
37 | case 768: | |
38 | mod |= MOD_RCLK_768FS; | |
39 | break; | |
40 | case 512: | |
41 | mod |= MOD_RCLK_512FS; | |
42 | break; | |
43 | case 384: | |
44 | mod |= MOD_RCLK_384FS; | |
45 | break; | |
46 | default: | |
47 | mod |= MOD_RCLK_256FS; | |
48 | break; | |
49 | } | |
50 | ||
51 | writel(mod, &i2s_reg->mod); | |
52 | } | |
53 | ||
54 | /* | |
55 | * Sets the i2s transfer control | |
56 | * | |
57 | * @param i2s_reg i2s regiter address | |
58 | * @param on 1 enable tx , 0 disable tx transfer | |
59 | */ | |
60 | static void i2s_txctrl(struct i2s_reg *i2s_reg, int on) | |
61 | { | |
62 | unsigned int con = readl(&i2s_reg->con); | |
63 | unsigned int mod = readl(&i2s_reg->mod) & ~MOD_MASK; | |
64 | ||
65 | if (on) { | |
66 | con |= CON_ACTIVE; | |
67 | con &= ~CON_TXCH_PAUSE; | |
511ed5fd | 68 | } else { |
511ed5fd RS |
69 | con |= CON_TXCH_PAUSE; |
70 | con &= ~CON_ACTIVE; | |
71 | } | |
72 | ||
73 | writel(mod, &i2s_reg->mod); | |
74 | writel(con, &i2s_reg->con); | |
75 | } | |
76 | ||
77 | /* | |
78 | * set the bit clock frame size (in multiples of LRCLK) | |
79 | * | |
80 | * @param i2s_reg i2s regiter address | |
81 | * @param bfs bit Frame Size | |
82 | */ | |
83 | static void i2s_set_bitclk_framesize(struct i2s_reg *i2s_reg, unsigned bfs) | |
84 | { | |
85 | unsigned int mod = readl(&i2s_reg->mod); | |
86 | ||
87 | mod &= ~MOD_BCLK_MASK; | |
88 | ||
89 | switch (bfs) { | |
90 | case 48: | |
91 | mod |= MOD_BCLK_48FS; | |
92 | break; | |
93 | case 32: | |
94 | mod |= MOD_BCLK_32FS; | |
95 | break; | |
96 | case 24: | |
97 | mod |= MOD_BCLK_24FS; | |
98 | break; | |
99 | case 16: | |
100 | mod |= MOD_BCLK_16FS; | |
101 | break; | |
102 | default: | |
103 | return; | |
104 | } | |
105 | writel(mod, &i2s_reg->mod); | |
106 | } | |
107 | ||
108 | /* | |
109 | * flushes the i2stx fifo | |
110 | * | |
111 | * @param i2s_reg i2s regiter address | |
112 | * @param flush Tx fifo flush command (0x00 - do not flush | |
113 | * 0x80 - flush tx fifo) | |
114 | */ | |
115 | void i2s_fifo(struct i2s_reg *i2s_reg, unsigned int flush) | |
116 | { | |
117 | /* Flush the FIFO */ | |
118 | setbits_le32(&i2s_reg->fic, flush); | |
119 | clrbits_le32(&i2s_reg->fic, flush); | |
120 | } | |
121 | ||
122 | /* | |
123 | * Set System Clock direction | |
124 | * | |
125 | * @param i2s_reg i2s regiter address | |
126 | * @param dir Clock direction | |
127 | * | |
128 | * @return int value 0 for success, -1 in case of error | |
129 | */ | |
130 | int i2s_set_sysclk_dir(struct i2s_reg *i2s_reg, int dir) | |
131 | { | |
132 | unsigned int mod = readl(&i2s_reg->mod); | |
133 | ||
134 | if (dir == SND_SOC_CLOCK_IN) | |
135 | mod |= MOD_CDCLKCON; | |
136 | else | |
137 | mod &= ~MOD_CDCLKCON; | |
138 | ||
139 | writel(mod, &i2s_reg->mod); | |
140 | ||
141 | return 0; | |
142 | } | |
143 | ||
144 | /* | |
145 | * Sets I2S Clcok format | |
146 | * | |
147 | * @param fmt i2s clock properties | |
148 | * @param i2s_reg i2s regiter address | |
149 | * | |
150 | * @return int value 0 for success, -1 in case of error | |
151 | */ | |
152 | int i2s_set_fmt(struct i2s_reg *i2s_reg, unsigned int fmt) | |
153 | { | |
154 | unsigned int mod = readl(&i2s_reg->mod); | |
155 | unsigned int tmp = 0; | |
156 | unsigned int ret = 0; | |
157 | ||
158 | /* Format is priority */ | |
159 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { | |
160 | case SND_SOC_DAIFMT_RIGHT_J: | |
161 | tmp |= MOD_LR_RLOW; | |
162 | tmp |= MOD_SDF_MSB; | |
163 | break; | |
164 | case SND_SOC_DAIFMT_LEFT_J: | |
165 | tmp |= MOD_LR_RLOW; | |
166 | tmp |= MOD_SDF_LSB; | |
167 | break; | |
168 | case SND_SOC_DAIFMT_I2S: | |
169 | tmp |= MOD_SDF_IIS; | |
170 | break; | |
171 | default: | |
172 | debug("%s: Invalid format priority [0x%x]\n", __func__, | |
3dd22a37 | 173 | (fmt & SND_SOC_DAIFMT_FORMAT_MASK)); |
511ed5fd RS |
174 | return -1; |
175 | } | |
176 | ||
177 | /* | |
178 | * INV flag is relative to the FORMAT flag - if set it simply | |
179 | * flips the polarity specified by the Standard | |
180 | */ | |
181 | switch (fmt & SND_SOC_DAIFMT_INV_MASK) { | |
182 | case SND_SOC_DAIFMT_NB_NF: | |
183 | break; | |
184 | case SND_SOC_DAIFMT_NB_IF: | |
185 | if (tmp & MOD_LR_RLOW) | |
186 | tmp &= ~MOD_LR_RLOW; | |
187 | else | |
188 | tmp |= MOD_LR_RLOW; | |
189 | break; | |
190 | default: | |
191 | debug("%s: Invalid clock ploarity input [0x%x]\n", __func__, | |
3dd22a37 | 192 | (fmt & SND_SOC_DAIFMT_INV_MASK)); |
511ed5fd RS |
193 | return -1; |
194 | } | |
195 | ||
196 | switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { | |
197 | case SND_SOC_DAIFMT_CBS_CFS: | |
198 | tmp |= MOD_SLAVE; | |
199 | break; | |
200 | case SND_SOC_DAIFMT_CBM_CFM: | |
201 | /* Set default source clock in Master mode */ | |
202 | ret = i2s_set_sysclk_dir(i2s_reg, SND_SOC_CLOCK_OUT); | |
203 | if (ret != 0) { | |
204 | debug("%s:set i2s clock direction failed\n", __func__); | |
205 | return -1; | |
206 | } | |
207 | break; | |
208 | default: | |
209 | debug("%s: Invalid master selection [0x%x]\n", __func__, | |
3dd22a37 | 210 | (fmt & SND_SOC_DAIFMT_MASTER_MASK)); |
511ed5fd RS |
211 | return -1; |
212 | } | |
213 | ||
214 | mod &= ~(MOD_SDF_MASK | MOD_LR_RLOW | MOD_SLAVE); | |
215 | mod |= tmp; | |
216 | writel(mod, &i2s_reg->mod); | |
217 | ||
218 | return 0; | |
219 | } | |
220 | ||
221 | /* | |
222 | * Sets the sample width in bits | |
223 | * | |
224 | * @param blc samplewidth (size of sample in bits) | |
225 | * @param i2s_reg i2s regiter address | |
226 | * | |
227 | * @return int value 0 for success, -1 in case of error | |
228 | */ | |
229 | int i2s_set_samplesize(struct i2s_reg *i2s_reg, unsigned int blc) | |
230 | { | |
231 | unsigned int mod = readl(&i2s_reg->mod); | |
232 | ||
233 | mod &= ~MOD_BLCP_MASK; | |
234 | mod &= ~MOD_BLC_MASK; | |
235 | ||
236 | switch (blc) { | |
237 | case 8: | |
238 | mod |= MOD_BLCP_8BIT; | |
239 | mod |= MOD_BLC_8BIT; | |
240 | break; | |
241 | case 16: | |
242 | mod |= MOD_BLCP_16BIT; | |
243 | mod |= MOD_BLC_16BIT; | |
244 | break; | |
245 | case 24: | |
246 | mod |= MOD_BLCP_24BIT; | |
247 | mod |= MOD_BLC_24BIT; | |
248 | break; | |
249 | default: | |
250 | debug("%s: Invalid sample size input [0x%x]\n", | |
3dd22a37 | 251 | __func__, blc); |
511ed5fd RS |
252 | return -1; |
253 | } | |
254 | writel(mod, &i2s_reg->mod); | |
255 | ||
256 | return 0; | |
257 | } | |
258 | ||
259 | int i2s_transfer_tx_data(struct i2stx_info *pi2s_tx, unsigned int *data, | |
260 | unsigned long data_size) | |
261 | { | |
262 | int i; | |
263 | int start; | |
264 | struct i2s_reg *i2s_reg = | |
265 | (struct i2s_reg *)pi2s_tx->base_address; | |
266 | ||
267 | if (data_size < FIFO_LENGTH) { | |
268 | debug("%s : Invalid data size\n", __func__); | |
269 | return -1; /* invalid pcm data size */ | |
270 | } | |
271 | ||
272 | /* fill the tx buffer before stating the tx transmit */ | |
273 | for (i = 0; i < FIFO_LENGTH; i++) | |
274 | writel(*data++, &i2s_reg->txd); | |
275 | ||
276 | data_size -= FIFO_LENGTH; | |
277 | i2s_txctrl(i2s_reg, I2S_TX_ON); | |
278 | ||
279 | while (data_size > 0) { | |
280 | start = get_timer(0); | |
281 | if (!(CON_TXFIFO_FULL & (readl(&i2s_reg->con)))) { | |
282 | writel(*data++, &i2s_reg->txd); | |
283 | data_size--; | |
284 | } else { | |
285 | if (get_timer(start) > TIMEOUT_I2S_TX) { | |
286 | i2s_txctrl(i2s_reg, I2S_TX_OFF); | |
287 | debug("%s: I2S Transfer Timeout\n", __func__); | |
288 | return -1; | |
289 | } | |
290 | } | |
291 | } | |
292 | i2s_txctrl(i2s_reg, I2S_TX_OFF); | |
293 | ||
294 | return 0; | |
295 | } | |
296 | ||
297 | int i2s_tx_init(struct i2stx_info *pi2s_tx) | |
298 | { | |
299 | int ret; | |
300 | struct i2s_reg *i2s_reg = | |
301 | (struct i2s_reg *)pi2s_tx->base_address; | |
5fb5b155 DKM |
302 | if (pi2s_tx->id == 0) { |
303 | /* Initialize GPIO for I2S-0 */ | |
304 | exynos_pinmux_config(PERIPH_ID_I2S0, 0); | |
305 | ||
306 | /* Set EPLL Clock */ | |
307 | ret = set_epll_clk(pi2s_tx->samplingrate * pi2s_tx->rfs * 4); | |
308 | } else if (pi2s_tx->id == 1) { | |
309 | /* Initialize GPIO for I2S-1 */ | |
310 | exynos_pinmux_config(PERIPH_ID_I2S1, 0); | |
311 | ||
312 | /* Set EPLL Clock */ | |
313 | ret = set_epll_clk(pi2s_tx->audio_pll_clk); | |
314 | } else { | |
315 | debug("%s: unsupported i2s-%d bus\n", __func__, pi2s_tx->id); | |
316 | return -1; | |
317 | } | |
511ed5fd | 318 | |
511ed5fd | 319 | if (ret != 0) { |
5fb5b155 | 320 | debug("%s: epll clock set rate failed\n", __func__); |
511ed5fd RS |
321 | return -1; |
322 | } | |
323 | ||
5fb5b155 | 324 | /* Select Clk Source for Audio 0 or 1 */ |
3dd22a37 DKM |
325 | ret = set_i2s_clk_source(pi2s_tx->id); |
326 | if (ret == -1) { | |
327 | debug("%s: unsupported clock for i2s-%d\n", __func__, | |
328 | pi2s_tx->id); | |
329 | return -1; | |
330 | } | |
511ed5fd | 331 | |
5fb5b155 DKM |
332 | if (pi2s_tx->id == 0) { |
333 | /*Reset the i2s module */ | |
334 | writel(CON_RESET, &i2s_reg->con); | |
335 | ||
336 | writel(MOD_OP_CLK | MOD_RCLKSRC, &i2s_reg->mod); | |
337 | /* set i2s prescaler */ | |
338 | writel(PSREN | PSVAL, &i2s_reg->psr); | |
339 | } else { | |
340 | /* Set Prescaler to get MCLK */ | |
341 | ret = set_i2s_clk_prescaler(pi2s_tx->audio_pll_clk, | |
342 | (pi2s_tx->samplingrate * (pi2s_tx->rfs)), | |
343 | pi2s_tx->id); | |
344 | } | |
3dd22a37 DKM |
345 | if (ret == -1) { |
346 | debug("%s: unsupported prescalar for i2s-%d\n", __func__, | |
347 | pi2s_tx->id); | |
348 | return -1; | |
349 | } | |
511ed5fd RS |
350 | |
351 | /* Configure I2s format */ | |
352 | ret = i2s_set_fmt(i2s_reg, (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | | |
5fb5b155 | 353 | SND_SOC_DAIFMT_CBM_CFM)); |
511ed5fd RS |
354 | if (ret == 0) { |
355 | i2s_set_lr_framesize(i2s_reg, pi2s_tx->rfs); | |
356 | ret = i2s_set_samplesize(i2s_reg, pi2s_tx->bitspersample); | |
357 | if (ret != 0) { | |
358 | debug("%s:set sample rate failed\n", __func__); | |
359 | return -1; | |
360 | } | |
361 | ||
362 | i2s_set_bitclk_framesize(i2s_reg, pi2s_tx->bfs); | |
363 | /* disable i2s transfer flag and flush the fifo */ | |
364 | i2s_txctrl(i2s_reg, I2S_TX_OFF); | |
365 | i2s_fifo(i2s_reg, FIC_TXFLUSH); | |
366 | } else { | |
367 | debug("%s: failed\n", __func__); | |
368 | } | |
369 | ||
370 | return ret; | |
371 | } |