1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (C) ST-Ericsson SA 2012 |
4 | * |
5 | * Author: Ola Lilja <ola.o.lilja@stericsson.com>, |
6 | * Roger Nilsson <roger.xr.nilsson@stericsson.com>, |
7 | * Sandeep Kaushik <sandeep.kaushik@st.com> |
8 | * for ST-Ericsson. |
9 | */ |
10 | |
11 | #include <linux/module.h> |
12 | #include <linux/platform_device.h> |
13 | #include <linux/delay.h> |
14 | #include <linux/slab.h> |
15 | #include <linux/io.h> |
16 | #include <linux/of.h> |
17 | |
18 | #include <sound/soc.h> |
19 | |
20 | #include "ux500_msp_i2s.h" |
21 | |
22 | /* Protocol desciptors */ |
23 | static const struct msp_protdesc prot_descs[] = { |
24 | { /* I2S */ |
25 | MSP_SINGLE_PHASE, |
26 | MSP_SINGLE_PHASE, |
27 | MSP_PHASE2_START_MODE_IMEDIATE, |
28 | MSP_PHASE2_START_MODE_IMEDIATE, |
29 | MSP_BTF_MS_BIT_FIRST, |
30 | MSP_BTF_MS_BIT_FIRST, |
31 | MSP_FRAME_LEN_1, |
32 | MSP_FRAME_LEN_1, |
33 | MSP_FRAME_LEN_1, |
34 | MSP_FRAME_LEN_1, |
35 | MSP_ELEM_LEN_32, |
36 | MSP_ELEM_LEN_32, |
37 | MSP_ELEM_LEN_32, |
38 | MSP_ELEM_LEN_32, |
39 | MSP_DELAY_1, |
40 | MSP_DELAY_1, |
41 | MSP_RISING_EDGE, |
42 | MSP_FALLING_EDGE, |
43 | MSP_FSYNC_POL_ACT_LO, |
44 | MSP_FSYNC_POL_ACT_LO, |
45 | MSP_SWAP_NONE, |
46 | MSP_SWAP_NONE, |
47 | MSP_COMPRESS_MODE_LINEAR, |
48 | MSP_EXPAND_MODE_LINEAR, |
49 | MSP_FSYNC_IGNORE, |
50 | 31, |
51 | 15, |
52 | 32, |
53 | }, { /* PCM */ |
54 | MSP_DUAL_PHASE, |
55 | MSP_DUAL_PHASE, |
56 | MSP_PHASE2_START_MODE_FSYNC, |
57 | MSP_PHASE2_START_MODE_FSYNC, |
58 | MSP_BTF_MS_BIT_FIRST, |
59 | MSP_BTF_MS_BIT_FIRST, |
60 | MSP_FRAME_LEN_1, |
61 | MSP_FRAME_LEN_1, |
62 | MSP_FRAME_LEN_1, |
63 | MSP_FRAME_LEN_1, |
64 | MSP_ELEM_LEN_16, |
65 | MSP_ELEM_LEN_16, |
66 | MSP_ELEM_LEN_16, |
67 | MSP_ELEM_LEN_16, |
68 | MSP_DELAY_0, |
69 | MSP_DELAY_0, |
70 | MSP_RISING_EDGE, |
71 | MSP_FALLING_EDGE, |
72 | MSP_FSYNC_POL_ACT_HI, |
73 | MSP_FSYNC_POL_ACT_HI, |
74 | MSP_SWAP_NONE, |
75 | MSP_SWAP_NONE, |
76 | MSP_COMPRESS_MODE_LINEAR, |
77 | MSP_EXPAND_MODE_LINEAR, |
78 | MSP_FSYNC_IGNORE, |
79 | 255, |
80 | 0, |
81 | 256, |
82 | }, { /* Companded PCM */ |
83 | MSP_SINGLE_PHASE, |
84 | MSP_SINGLE_PHASE, |
85 | MSP_PHASE2_START_MODE_FSYNC, |
86 | MSP_PHASE2_START_MODE_FSYNC, |
87 | MSP_BTF_MS_BIT_FIRST, |
88 | MSP_BTF_MS_BIT_FIRST, |
89 | MSP_FRAME_LEN_1, |
90 | MSP_FRAME_LEN_1, |
91 | MSP_FRAME_LEN_1, |
92 | MSP_FRAME_LEN_1, |
93 | MSP_ELEM_LEN_8, |
94 | MSP_ELEM_LEN_8, |
95 | MSP_ELEM_LEN_8, |
96 | MSP_ELEM_LEN_8, |
97 | MSP_DELAY_0, |
98 | MSP_DELAY_0, |
99 | MSP_RISING_EDGE, |
100 | MSP_RISING_EDGE, |
101 | MSP_FSYNC_POL_ACT_HI, |
102 | MSP_FSYNC_POL_ACT_HI, |
103 | MSP_SWAP_NONE, |
104 | MSP_SWAP_NONE, |
105 | MSP_COMPRESS_MODE_LINEAR, |
106 | MSP_EXPAND_MODE_LINEAR, |
107 | MSP_FSYNC_IGNORE, |
108 | 255, |
109 | 0, |
110 | 256, |
111 | }, |
112 | }; |
113 | |
114 | static void set_prot_desc_tx(struct ux500_msp *msp, |
115 | struct msp_protdesc *protdesc, |
116 | enum msp_data_size data_size) |
117 | { |
118 | u32 temp_reg = 0; |
119 | |
120 | temp_reg |= MSP_P2_ENABLE_BIT(protdesc->tx_phase_mode); |
121 | temp_reg |= MSP_P2_START_MODE_BIT(protdesc->tx_phase2_start_mode); |
122 | temp_reg |= MSP_P1_FRAME_LEN_BITS(protdesc->tx_frame_len_1); |
123 | temp_reg |= MSP_P2_FRAME_LEN_BITS(protdesc->tx_frame_len_2); |
124 | if (msp->def_elem_len) { |
125 | temp_reg |= MSP_P1_ELEM_LEN_BITS(protdesc->tx_elem_len_1); |
126 | temp_reg |= MSP_P2_ELEM_LEN_BITS(protdesc->tx_elem_len_2); |
127 | } else { |
128 | temp_reg |= MSP_P1_ELEM_LEN_BITS(data_size); |
129 | temp_reg |= MSP_P2_ELEM_LEN_BITS(data_size); |
130 | } |
131 | temp_reg |= MSP_DATA_DELAY_BITS(protdesc->tx_data_delay); |
132 | temp_reg |= MSP_SET_ENDIANNES_BIT(protdesc->tx_byte_order); |
133 | temp_reg |= MSP_FSYNC_POL(protdesc->tx_fsync_pol); |
134 | temp_reg |= MSP_DATA_WORD_SWAP(protdesc->tx_half_word_swap); |
135 | temp_reg |= MSP_SET_COMPANDING_MODE(protdesc->compression_mode); |
136 | temp_reg |= MSP_SET_FSYNC_IGNORE(protdesc->frame_sync_ignore); |
137 | |
138 | writel(val: temp_reg, addr: msp->registers + MSP_TCF); |
139 | } |
140 | |
141 | static void set_prot_desc_rx(struct ux500_msp *msp, |
142 | struct msp_protdesc *protdesc, |
143 | enum msp_data_size data_size) |
144 | { |
145 | u32 temp_reg = 0; |
146 | |
147 | temp_reg |= MSP_P2_ENABLE_BIT(protdesc->rx_phase_mode); |
148 | temp_reg |= MSP_P2_START_MODE_BIT(protdesc->rx_phase2_start_mode); |
149 | temp_reg |= MSP_P1_FRAME_LEN_BITS(protdesc->rx_frame_len_1); |
150 | temp_reg |= MSP_P2_FRAME_LEN_BITS(protdesc->rx_frame_len_2); |
151 | if (msp->def_elem_len) { |
152 | temp_reg |= MSP_P1_ELEM_LEN_BITS(protdesc->rx_elem_len_1); |
153 | temp_reg |= MSP_P2_ELEM_LEN_BITS(protdesc->rx_elem_len_2); |
154 | } else { |
155 | temp_reg |= MSP_P1_ELEM_LEN_BITS(data_size); |
156 | temp_reg |= MSP_P2_ELEM_LEN_BITS(data_size); |
157 | } |
158 | |
159 | temp_reg |= MSP_DATA_DELAY_BITS(protdesc->rx_data_delay); |
160 | temp_reg |= MSP_SET_ENDIANNES_BIT(protdesc->rx_byte_order); |
161 | temp_reg |= MSP_FSYNC_POL(protdesc->rx_fsync_pol); |
162 | temp_reg |= MSP_DATA_WORD_SWAP(protdesc->rx_half_word_swap); |
163 | temp_reg |= MSP_SET_COMPANDING_MODE(protdesc->expansion_mode); |
164 | temp_reg |= MSP_SET_FSYNC_IGNORE(protdesc->frame_sync_ignore); |
165 | |
166 | writel(val: temp_reg, addr: msp->registers + MSP_RCF); |
167 | } |
168 | |
169 | static int configure_protocol(struct ux500_msp *msp, |
170 | struct ux500_msp_config *config) |
171 | { |
172 | struct msp_protdesc *protdesc; |
173 | enum msp_data_size data_size; |
174 | u32 temp_reg = 0; |
175 | |
176 | data_size = config->data_size; |
177 | msp->def_elem_len = config->def_elem_len; |
178 | if (config->default_protdesc == 1) { |
179 | if (config->protocol >= MSP_INVALID_PROTOCOL) { |
180 | dev_err(msp->dev, "%s: ERROR: Invalid protocol!\n" , |
181 | __func__); |
182 | return -EINVAL; |
183 | } |
184 | protdesc = |
185 | (struct msp_protdesc *)&prot_descs[config->protocol]; |
186 | } else { |
187 | protdesc = (struct msp_protdesc *)&config->protdesc; |
188 | } |
189 | |
190 | if (data_size < MSP_DATA_BITS_DEFAULT || data_size > MSP_DATA_BITS_32) { |
191 | dev_err(msp->dev, |
192 | "%s: ERROR: Invalid data-size requested (data_size = %d)!\n" , |
193 | __func__, data_size); |
194 | return -EINVAL; |
195 | } |
196 | |
197 | if (config->direction & MSP_DIR_TX) |
198 | set_prot_desc_tx(msp, protdesc, data_size); |
199 | if (config->direction & MSP_DIR_RX) |
200 | set_prot_desc_rx(msp, protdesc, data_size); |
201 | |
202 | /* The code below should not be separated. */ |
203 | temp_reg = readl(addr: msp->registers + MSP_GCR) & ~TX_CLK_POL_RISING; |
204 | temp_reg |= MSP_TX_CLKPOL_BIT(~protdesc->tx_clk_pol); |
205 | writel(val: temp_reg, addr: msp->registers + MSP_GCR); |
206 | temp_reg = readl(addr: msp->registers + MSP_GCR) & ~RX_CLK_POL_RISING; |
207 | temp_reg |= MSP_RX_CLKPOL_BIT(protdesc->rx_clk_pol); |
208 | writel(val: temp_reg, addr: msp->registers + MSP_GCR); |
209 | |
210 | return 0; |
211 | } |
212 | |
213 | static int setup_bitclk(struct ux500_msp *msp, struct ux500_msp_config *config) |
214 | { |
215 | u32 reg_val_GCR; |
216 | u32 frame_per = 0; |
217 | u32 sck_div = 0; |
218 | u32 frame_width = 0; |
219 | u32 temp_reg = 0; |
220 | struct msp_protdesc *protdesc = NULL; |
221 | |
222 | reg_val_GCR = readl(addr: msp->registers + MSP_GCR); |
223 | writel(val: reg_val_GCR & ~SRG_ENABLE, addr: msp->registers + MSP_GCR); |
224 | |
225 | if (config->default_protdesc) |
226 | protdesc = |
227 | (struct msp_protdesc *)&prot_descs[config->protocol]; |
228 | else |
229 | protdesc = (struct msp_protdesc *)&config->protdesc; |
230 | |
231 | switch (config->protocol) { |
232 | case MSP_PCM_PROTOCOL: |
233 | case MSP_PCM_COMPAND_PROTOCOL: |
234 | frame_width = protdesc->frame_width; |
235 | sck_div = config->f_inputclk / (config->frame_freq * |
236 | (protdesc->clocks_per_frame)); |
237 | frame_per = protdesc->frame_period; |
238 | break; |
239 | case MSP_I2S_PROTOCOL: |
240 | frame_width = protdesc->frame_width; |
241 | sck_div = config->f_inputclk / (config->frame_freq * |
242 | (protdesc->clocks_per_frame)); |
243 | frame_per = protdesc->frame_period; |
244 | break; |
245 | default: |
246 | dev_err(msp->dev, "%s: ERROR: Unknown protocol (%d)!\n" , |
247 | __func__, |
248 | config->protocol); |
249 | return -EINVAL; |
250 | } |
251 | |
252 | temp_reg = (sck_div - 1) & SCK_DIV_MASK; |
253 | temp_reg |= FRAME_WIDTH_BITS(frame_width); |
254 | temp_reg |= FRAME_PERIOD_BITS(frame_per); |
255 | writel(val: temp_reg, addr: msp->registers + MSP_SRG); |
256 | |
257 | msp->f_bitclk = (config->f_inputclk)/(sck_div + 1); |
258 | |
259 | /* Enable bit-clock */ |
260 | udelay(100); |
261 | reg_val_GCR = readl(addr: msp->registers + MSP_GCR); |
262 | writel(val: reg_val_GCR | SRG_ENABLE, addr: msp->registers + MSP_GCR); |
263 | udelay(100); |
264 | |
265 | return 0; |
266 | } |
267 | |
268 | static int configure_multichannel(struct ux500_msp *msp, |
269 | struct ux500_msp_config *config) |
270 | { |
271 | struct msp_protdesc *protdesc; |
272 | struct msp_multichannel_config *mcfg; |
273 | u32 reg_val_MCR; |
274 | |
275 | if (config->default_protdesc == 1) { |
276 | if (config->protocol >= MSP_INVALID_PROTOCOL) { |
277 | dev_err(msp->dev, |
278 | "%s: ERROR: Invalid protocol (%d)!\n" , |
279 | __func__, config->protocol); |
280 | return -EINVAL; |
281 | } |
282 | protdesc = (struct msp_protdesc *) |
283 | &prot_descs[config->protocol]; |
284 | } else { |
285 | protdesc = (struct msp_protdesc *)&config->protdesc; |
286 | } |
287 | |
288 | mcfg = &config->multichannel_config; |
289 | if (mcfg->tx_multichannel_enable) { |
290 | if (protdesc->tx_phase_mode == MSP_SINGLE_PHASE) { |
291 | reg_val_MCR = readl(addr: msp->registers + MSP_MCR); |
292 | writel(val: reg_val_MCR | (mcfg->tx_multichannel_enable ? |
293 | 1 << TMCEN_BIT : 0), |
294 | addr: msp->registers + MSP_MCR); |
295 | writel(val: mcfg->tx_channel_0_enable, |
296 | addr: msp->registers + MSP_TCE0); |
297 | writel(val: mcfg->tx_channel_1_enable, |
298 | addr: msp->registers + MSP_TCE1); |
299 | writel(val: mcfg->tx_channel_2_enable, |
300 | addr: msp->registers + MSP_TCE2); |
301 | writel(val: mcfg->tx_channel_3_enable, |
302 | addr: msp->registers + MSP_TCE3); |
303 | } else { |
304 | dev_err(msp->dev, |
305 | "%s: ERROR: Only single-phase supported (TX-mode: %d)!\n" , |
306 | __func__, protdesc->tx_phase_mode); |
307 | return -EINVAL; |
308 | } |
309 | } |
310 | if (mcfg->rx_multichannel_enable) { |
311 | if (protdesc->rx_phase_mode == MSP_SINGLE_PHASE) { |
312 | reg_val_MCR = readl(addr: msp->registers + MSP_MCR); |
313 | writel(val: reg_val_MCR | (mcfg->rx_multichannel_enable ? |
314 | 1 << RMCEN_BIT : 0), |
315 | addr: msp->registers + MSP_MCR); |
316 | writel(val: mcfg->rx_channel_0_enable, |
317 | addr: msp->registers + MSP_RCE0); |
318 | writel(val: mcfg->rx_channel_1_enable, |
319 | addr: msp->registers + MSP_RCE1); |
320 | writel(val: mcfg->rx_channel_2_enable, |
321 | addr: msp->registers + MSP_RCE2); |
322 | writel(val: mcfg->rx_channel_3_enable, |
323 | addr: msp->registers + MSP_RCE3); |
324 | } else { |
325 | dev_err(msp->dev, |
326 | "%s: ERROR: Only single-phase supported (RX-mode: %d)!\n" , |
327 | __func__, protdesc->rx_phase_mode); |
328 | return -EINVAL; |
329 | } |
330 | if (mcfg->rx_comparison_enable_mode) { |
331 | reg_val_MCR = readl(addr: msp->registers + MSP_MCR); |
332 | writel(val: reg_val_MCR | |
333 | (mcfg->rx_comparison_enable_mode << RCMPM_BIT), |
334 | addr: msp->registers + MSP_MCR); |
335 | |
336 | writel(val: mcfg->comparison_mask, |
337 | addr: msp->registers + MSP_RCM); |
338 | writel(val: mcfg->comparison_value, |
339 | addr: msp->registers + MSP_RCV); |
340 | |
341 | } |
342 | } |
343 | |
344 | return 0; |
345 | } |
346 | |
347 | static int enable_msp(struct ux500_msp *msp, struct ux500_msp_config *config) |
348 | { |
349 | int status = 0; |
350 | u32 reg_val_DMACR, reg_val_GCR; |
351 | |
352 | /* Configure msp with protocol dependent settings */ |
353 | configure_protocol(msp, config); |
354 | setup_bitclk(msp, config); |
355 | if (config->multichannel_configured == 1) { |
356 | status = configure_multichannel(msp, config); |
357 | if (status) |
358 | dev_warn(msp->dev, |
359 | "%s: WARN: configure_multichannel failed (%d)!\n" , |
360 | __func__, status); |
361 | } |
362 | |
363 | reg_val_DMACR = readl(addr: msp->registers + MSP_DMACR); |
364 | if (config->direction & MSP_DIR_RX) |
365 | reg_val_DMACR |= RX_DMA_ENABLE; |
366 | if (config->direction & MSP_DIR_TX) |
367 | reg_val_DMACR |= TX_DMA_ENABLE; |
368 | writel(val: reg_val_DMACR, addr: msp->registers + MSP_DMACR); |
369 | |
370 | writel(val: config->iodelay, addr: msp->registers + MSP_IODLY); |
371 | |
372 | /* Enable frame generation logic */ |
373 | reg_val_GCR = readl(addr: msp->registers + MSP_GCR); |
374 | writel(val: reg_val_GCR | FRAME_GEN_ENABLE, addr: msp->registers + MSP_GCR); |
375 | |
376 | return status; |
377 | } |
378 | |
379 | static void flush_fifo_rx(struct ux500_msp *msp) |
380 | { |
381 | u32 reg_val_GCR, reg_val_FLR; |
382 | u32 limit = 32; |
383 | |
384 | reg_val_GCR = readl(addr: msp->registers + MSP_GCR); |
385 | writel(val: reg_val_GCR | RX_ENABLE, addr: msp->registers + MSP_GCR); |
386 | |
387 | reg_val_FLR = readl(addr: msp->registers + MSP_FLR); |
388 | while (!(reg_val_FLR & RX_FIFO_EMPTY) && limit--) { |
389 | readl(addr: msp->registers + MSP_DR); |
390 | reg_val_FLR = readl(addr: msp->registers + MSP_FLR); |
391 | } |
392 | |
393 | writel(val: reg_val_GCR, addr: msp->registers + MSP_GCR); |
394 | } |
395 | |
396 | static void flush_fifo_tx(struct ux500_msp *msp) |
397 | { |
398 | u32 reg_val_GCR, reg_val_FLR; |
399 | u32 limit = 32; |
400 | |
401 | reg_val_GCR = readl(addr: msp->registers + MSP_GCR); |
402 | writel(val: reg_val_GCR | TX_ENABLE, addr: msp->registers + MSP_GCR); |
403 | writel(MSP_ITCR_ITEN | MSP_ITCR_TESTFIFO, addr: msp->registers + MSP_ITCR); |
404 | |
405 | reg_val_FLR = readl(addr: msp->registers + MSP_FLR); |
406 | while (!(reg_val_FLR & TX_FIFO_EMPTY) && limit--) { |
407 | readl(addr: msp->registers + MSP_TSTDR); |
408 | reg_val_FLR = readl(addr: msp->registers + MSP_FLR); |
409 | } |
410 | writel(val: 0x0, addr: msp->registers + MSP_ITCR); |
411 | writel(val: reg_val_GCR, addr: msp->registers + MSP_GCR); |
412 | } |
413 | |
414 | int ux500_msp_i2s_open(struct ux500_msp *msp, |
415 | struct ux500_msp_config *config) |
416 | { |
417 | u32 old_reg, new_reg, mask; |
418 | int res; |
419 | unsigned int tx_sel, rx_sel, tx_busy, rx_busy; |
420 | |
421 | if (in_interrupt()) { |
422 | dev_err(msp->dev, |
423 | "%s: ERROR: Open called in interrupt context!\n" , |
424 | __func__); |
425 | return -1; |
426 | } |
427 | |
428 | tx_sel = (config->direction & MSP_DIR_TX) > 0; |
429 | rx_sel = (config->direction & MSP_DIR_RX) > 0; |
430 | if (!tx_sel && !rx_sel) { |
431 | dev_err(msp->dev, "%s: Error: No direction selected!\n" , |
432 | __func__); |
433 | return -EINVAL; |
434 | } |
435 | |
436 | tx_busy = (msp->dir_busy & MSP_DIR_TX) > 0; |
437 | rx_busy = (msp->dir_busy & MSP_DIR_RX) > 0; |
438 | if (tx_busy && tx_sel) { |
439 | dev_err(msp->dev, "%s: Error: TX is in use!\n" , __func__); |
440 | return -EBUSY; |
441 | } |
442 | if (rx_busy && rx_sel) { |
443 | dev_err(msp->dev, "%s: Error: RX is in use!\n" , __func__); |
444 | return -EBUSY; |
445 | } |
446 | |
447 | msp->dir_busy |= (tx_sel ? MSP_DIR_TX : 0) | (rx_sel ? MSP_DIR_RX : 0); |
448 | |
449 | /* First do the global config register */ |
450 | mask = RX_CLK_SEL_MASK | TX_CLK_SEL_MASK | RX_FSYNC_MASK | |
451 | TX_FSYNC_MASK | RX_SYNC_SEL_MASK | TX_SYNC_SEL_MASK | |
452 | RX_FIFO_ENABLE_MASK | TX_FIFO_ENABLE_MASK | SRG_CLK_SEL_MASK | |
453 | LOOPBACK_MASK | TX_EXTRA_DELAY_MASK; |
454 | |
455 | new_reg = (config->tx_clk_sel | config->rx_clk_sel | |
456 | config->rx_fsync_pol | config->tx_fsync_pol | |
457 | config->rx_fsync_sel | config->tx_fsync_sel | |
458 | config->rx_fifo_config | config->tx_fifo_config | |
459 | config->srg_clk_sel | config->loopback_enable | |
460 | config->tx_data_enable); |
461 | |
462 | old_reg = readl(addr: msp->registers + MSP_GCR); |
463 | old_reg &= ~mask; |
464 | new_reg |= old_reg; |
465 | writel(val: new_reg, addr: msp->registers + MSP_GCR); |
466 | |
467 | res = enable_msp(msp, config); |
468 | if (res < 0) { |
469 | dev_err(msp->dev, "%s: ERROR: enable_msp failed (%d)!\n" , |
470 | __func__, res); |
471 | return -EBUSY; |
472 | } |
473 | if (config->loopback_enable & 0x80) |
474 | msp->loopback_enable = 1; |
475 | |
476 | /* Flush FIFOs */ |
477 | flush_fifo_tx(msp); |
478 | flush_fifo_rx(msp); |
479 | |
480 | msp->msp_state = MSP_STATE_CONFIGURED; |
481 | return 0; |
482 | } |
483 | |
484 | static void disable_msp_rx(struct ux500_msp *msp) |
485 | { |
486 | u32 reg_val_GCR, reg_val_DMACR, reg_val_IMSC; |
487 | |
488 | reg_val_GCR = readl(addr: msp->registers + MSP_GCR); |
489 | writel(val: reg_val_GCR & ~RX_ENABLE, addr: msp->registers + MSP_GCR); |
490 | reg_val_DMACR = readl(addr: msp->registers + MSP_DMACR); |
491 | writel(val: reg_val_DMACR & ~RX_DMA_ENABLE, addr: msp->registers + MSP_DMACR); |
492 | reg_val_IMSC = readl(addr: msp->registers + MSP_IMSC); |
493 | writel(val: reg_val_IMSC & |
494 | ~(RX_SERVICE_INT | RX_OVERRUN_ERROR_INT), |
495 | addr: msp->registers + MSP_IMSC); |
496 | |
497 | msp->dir_busy &= ~MSP_DIR_RX; |
498 | } |
499 | |
500 | static void disable_msp_tx(struct ux500_msp *msp) |
501 | { |
502 | u32 reg_val_GCR, reg_val_DMACR, reg_val_IMSC; |
503 | |
504 | reg_val_GCR = readl(addr: msp->registers + MSP_GCR); |
505 | writel(val: reg_val_GCR & ~TX_ENABLE, addr: msp->registers + MSP_GCR); |
506 | reg_val_DMACR = readl(addr: msp->registers + MSP_DMACR); |
507 | writel(val: reg_val_DMACR & ~TX_DMA_ENABLE, addr: msp->registers + MSP_DMACR); |
508 | reg_val_IMSC = readl(addr: msp->registers + MSP_IMSC); |
509 | writel(val: reg_val_IMSC & |
510 | ~(TX_SERVICE_INT | TX_UNDERRUN_ERR_INT), |
511 | addr: msp->registers + MSP_IMSC); |
512 | |
513 | msp->dir_busy &= ~MSP_DIR_TX; |
514 | } |
515 | |
516 | static int disable_msp(struct ux500_msp *msp, unsigned int dir) |
517 | { |
518 | u32 reg_val_GCR; |
519 | unsigned int disable_tx, disable_rx; |
520 | |
521 | reg_val_GCR = readl(addr: msp->registers + MSP_GCR); |
522 | disable_tx = dir & MSP_DIR_TX; |
523 | disable_rx = dir & MSP_DIR_TX; |
524 | if (disable_tx && disable_rx) { |
525 | reg_val_GCR = readl(addr: msp->registers + MSP_GCR); |
526 | writel(val: reg_val_GCR | LOOPBACK_MASK, |
527 | addr: msp->registers + MSP_GCR); |
528 | |
529 | /* Flush TX-FIFO */ |
530 | flush_fifo_tx(msp); |
531 | |
532 | /* Disable TX-channel */ |
533 | writel(val: (readl(addr: msp->registers + MSP_GCR) & |
534 | (~TX_ENABLE)), addr: msp->registers + MSP_GCR); |
535 | |
536 | /* Flush RX-FIFO */ |
537 | flush_fifo_rx(msp); |
538 | |
539 | /* Disable Loopback and Receive channel */ |
540 | writel(val: (readl(addr: msp->registers + MSP_GCR) & |
541 | (~(RX_ENABLE | LOOPBACK_MASK))), |
542 | addr: msp->registers + MSP_GCR); |
543 | |
544 | disable_msp_tx(msp); |
545 | disable_msp_rx(msp); |
546 | } else if (disable_tx) |
547 | disable_msp_tx(msp); |
548 | else if (disable_rx) |
549 | disable_msp_rx(msp); |
550 | |
551 | return 0; |
552 | } |
553 | |
554 | int ux500_msp_i2s_trigger(struct ux500_msp *msp, int cmd, int direction) |
555 | { |
556 | u32 reg_val_GCR, enable_bit; |
557 | |
558 | if (msp->msp_state == MSP_STATE_IDLE) { |
559 | dev_err(msp->dev, "%s: ERROR: MSP is not configured!\n" , |
560 | __func__); |
561 | return -EINVAL; |
562 | } |
563 | |
564 | switch (cmd) { |
565 | case SNDRV_PCM_TRIGGER_START: |
566 | case SNDRV_PCM_TRIGGER_RESUME: |
567 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: |
568 | if (direction == SNDRV_PCM_STREAM_PLAYBACK) |
569 | enable_bit = TX_ENABLE; |
570 | else |
571 | enable_bit = RX_ENABLE; |
572 | reg_val_GCR = readl(addr: msp->registers + MSP_GCR); |
573 | writel(val: reg_val_GCR | enable_bit, addr: msp->registers + MSP_GCR); |
574 | break; |
575 | |
576 | case SNDRV_PCM_TRIGGER_STOP: |
577 | case SNDRV_PCM_TRIGGER_SUSPEND: |
578 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: |
579 | if (direction == SNDRV_PCM_STREAM_PLAYBACK) |
580 | disable_msp_tx(msp); |
581 | else |
582 | disable_msp_rx(msp); |
583 | break; |
584 | default: |
585 | return -EINVAL; |
586 | } |
587 | |
588 | return 0; |
589 | } |
590 | |
591 | int ux500_msp_i2s_close(struct ux500_msp *msp, unsigned int dir) |
592 | { |
593 | int status = 0; |
594 | |
595 | dev_dbg(msp->dev, "%s: Enter (dir = 0x%01x).\n" , __func__, dir); |
596 | |
597 | status = disable_msp(msp, dir); |
598 | if (msp->dir_busy == 0) { |
599 | /* disable sample rate and frame generators */ |
600 | msp->msp_state = MSP_STATE_IDLE; |
601 | writel(val: (readl(addr: msp->registers + MSP_GCR) & |
602 | (~(FRAME_GEN_ENABLE | SRG_ENABLE))), |
603 | addr: msp->registers + MSP_GCR); |
604 | |
605 | writel(val: 0, addr: msp->registers + MSP_GCR); |
606 | writel(val: 0, addr: msp->registers + MSP_TCF); |
607 | writel(val: 0, addr: msp->registers + MSP_RCF); |
608 | writel(val: 0, addr: msp->registers + MSP_DMACR); |
609 | writel(val: 0, addr: msp->registers + MSP_SRG); |
610 | writel(val: 0, addr: msp->registers + MSP_MCR); |
611 | writel(val: 0, addr: msp->registers + MSP_RCM); |
612 | writel(val: 0, addr: msp->registers + MSP_RCV); |
613 | writel(val: 0, addr: msp->registers + MSP_TCE0); |
614 | writel(val: 0, addr: msp->registers + MSP_TCE1); |
615 | writel(val: 0, addr: msp->registers + MSP_TCE2); |
616 | writel(val: 0, addr: msp->registers + MSP_TCE3); |
617 | writel(val: 0, addr: msp->registers + MSP_RCE0); |
618 | writel(val: 0, addr: msp->registers + MSP_RCE1); |
619 | writel(val: 0, addr: msp->registers + MSP_RCE2); |
620 | writel(val: 0, addr: msp->registers + MSP_RCE3); |
621 | } |
622 | |
623 | return status; |
624 | |
625 | } |
626 | |
627 | int ux500_msp_i2s_init_msp(struct platform_device *pdev, |
628 | struct ux500_msp **msp_p) |
629 | { |
630 | struct resource *res = NULL; |
631 | struct ux500_msp *msp; |
632 | |
633 | *msp_p = devm_kzalloc(dev: &pdev->dev, size: sizeof(struct ux500_msp), GFP_KERNEL); |
634 | msp = *msp_p; |
635 | if (!msp) |
636 | return -ENOMEM; |
637 | |
638 | msp->dev = &pdev->dev; |
639 | |
640 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
641 | if (res == NULL) { |
642 | dev_err(&pdev->dev, "%s: ERROR: Unable to get resource!\n" , |
643 | __func__); |
644 | return -ENOMEM; |
645 | } |
646 | |
647 | msp->tx_rx_addr = res->start + MSP_DR; |
648 | msp->registers = devm_ioremap(dev: &pdev->dev, offset: res->start, |
649 | size: resource_size(res)); |
650 | if (msp->registers == NULL) { |
651 | dev_err(&pdev->dev, "%s: ERROR: ioremap failed!\n" , __func__); |
652 | return -ENOMEM; |
653 | } |
654 | |
655 | msp->msp_state = MSP_STATE_IDLE; |
656 | msp->loopback_enable = 0; |
657 | |
658 | return 0; |
659 | } |
660 | |
661 | void ux500_msp_i2s_cleanup_msp(struct platform_device *pdev, |
662 | struct ux500_msp *msp) |
663 | { |
664 | dev_dbg(msp->dev, "%s: Enter (id = %d).\n" , __func__, msp->id); |
665 | } |
666 | |
667 | MODULE_LICENSE("GPL v2" ); |
668 | |