diff options
author | Ola Lilja <ola.o.lilja@stericsson.com> | 2012-05-08 09:57:18 -0400 |
---|---|---|
committer | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2012-05-09 07:52:59 -0400 |
commit | 3592b7f69a5438812381ff0aacdea1725ffa1c1c (patch) | |
tree | b24451a32b623d2d1ce71031bdc9033195129fec /sound/soc/ux500 | |
parent | 41a41eaca48946a69bb0a1f6e053b9e4f1458e15 (diff) |
ASoC: Ux500: Add MSP I2S-driver
Add driver for running I2S with the MSP-block.
Signed-off-by: Ola Lilja <ola.o.lilja@stericsson.com>
[Fixed trailing whitespace -- broonie]
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Diffstat (limited to 'sound/soc/ux500')
-rw-r--r-- | sound/soc/ux500/Kconfig | 15 | ||||
-rw-r--r-- | sound/soc/ux500/Makefile | 4 | ||||
-rw-r--r-- | sound/soc/ux500/ux500_msp_dai.c | 843 | ||||
-rw-r--r-- | sound/soc/ux500/ux500_msp_dai.h | 79 | ||||
-rw-r--r-- | sound/soc/ux500/ux500_msp_i2s.c | 742 | ||||
-rw-r--r-- | sound/soc/ux500/ux500_msp_i2s.h | 553 |
6 files changed, 2236 insertions, 0 deletions
diff --git a/sound/soc/ux500/Kconfig b/sound/soc/ux500/Kconfig new file mode 100644 index 000000000000..ac852347c5dc --- /dev/null +++ b/sound/soc/ux500/Kconfig | |||
@@ -0,0 +1,15 @@ | |||
1 | # | ||
2 | # Ux500 SoC audio configuration | ||
3 | # | ||
4 | menuconfig SND_SOC_UX500 | ||
5 | tristate "SoC Audio support for Ux500 platform" | ||
6 | depends on SND_SOC | ||
7 | help | ||
8 | Say Y if you want to enable ASoC-support for | ||
9 | any of the Ux500 platforms (e.g. U8500). | ||
10 | |||
11 | config SND_SOC_UX500_PLAT_MSP_I2S | ||
12 | tristate "Platform - MSP (I2S)" | ||
13 | depends on SND_SOC_UX500 | ||
14 | help | ||
15 | Say Y if you want to enable the Ux500 MSP I2S-driver. | ||
diff --git a/sound/soc/ux500/Makefile b/sound/soc/ux500/Makefile new file mode 100644 index 000000000000..19974c5a2ea1 --- /dev/null +++ b/sound/soc/ux500/Makefile | |||
@@ -0,0 +1,4 @@ | |||
1 | # Ux500 Platform Support | ||
2 | |||
3 | snd-soc-ux500-plat-msp-i2s-objs := ux500_msp_dai.o ux500_msp_i2s.o | ||
4 | obj-$(CONFIG_SND_SOC_UX500_PLAT_MSP_I2S) += snd-soc-ux500-plat-msp-i2s.o | ||
diff --git a/sound/soc/ux500/ux500_msp_dai.c b/sound/soc/ux500/ux500_msp_dai.c new file mode 100644 index 000000000000..93c6c40e724c --- /dev/null +++ b/sound/soc/ux500/ux500_msp_dai.c | |||
@@ -0,0 +1,843 @@ | |||
1 | /* | ||
2 | * Copyright (C) ST-Ericsson SA 2012 | ||
3 | * | ||
4 | * Author: Ola Lilja <ola.o.lilja@stericsson.com>, | ||
5 | * Roger Nilsson <roger.xr.nilsson@stericsson.com> | ||
6 | * for ST-Ericsson. | ||
7 | * | ||
8 | * License terms: | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License version 2 as published | ||
12 | * by the Free Software Foundation. | ||
13 | */ | ||
14 | |||
15 | #include <linux/module.h> | ||
16 | #include <linux/slab.h> | ||
17 | #include <linux/bitops.h> | ||
18 | #include <linux/platform_device.h> | ||
19 | #include <linux/clk.h> | ||
20 | #include <linux/regulator/consumer.h> | ||
21 | #include <linux/mfd/dbx500-prcmu.h> | ||
22 | |||
23 | #include <mach/hardware.h> | ||
24 | #include <mach/board-mop500-msp.h> | ||
25 | |||
26 | #include <sound/soc.h> | ||
27 | #include <sound/soc-dai.h> | ||
28 | |||
29 | #include "ux500_msp_i2s.h" | ||
30 | #include "ux500_msp_dai.h" | ||
31 | |||
32 | static int setup_pcm_multichan(struct snd_soc_dai *dai, | ||
33 | struct ux500_msp_config *msp_config) | ||
34 | { | ||
35 | struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev); | ||
36 | struct msp_multichannel_config *multi = | ||
37 | &msp_config->multichannel_config; | ||
38 | |||
39 | if (drvdata->slots > 1) { | ||
40 | msp_config->multichannel_configured = 1; | ||
41 | |||
42 | multi->tx_multichannel_enable = true; | ||
43 | multi->rx_multichannel_enable = true; | ||
44 | multi->rx_comparison_enable_mode = MSP_COMPARISON_DISABLED; | ||
45 | |||
46 | multi->tx_channel_0_enable = drvdata->tx_mask; | ||
47 | multi->tx_channel_1_enable = 0; | ||
48 | multi->tx_channel_2_enable = 0; | ||
49 | multi->tx_channel_3_enable = 0; | ||
50 | |||
51 | multi->rx_channel_0_enable = drvdata->rx_mask; | ||
52 | multi->rx_channel_1_enable = 0; | ||
53 | multi->rx_channel_2_enable = 0; | ||
54 | multi->rx_channel_3_enable = 0; | ||
55 | |||
56 | dev_dbg(dai->dev, | ||
57 | "%s: Multichannel enabled. Slots: %d, TX: %u, RX: %u\n", | ||
58 | __func__, drvdata->slots, multi->tx_channel_0_enable, | ||
59 | multi->rx_channel_0_enable); | ||
60 | } | ||
61 | |||
62 | return 0; | ||
63 | } | ||
64 | |||
65 | static int setup_frameper(struct snd_soc_dai *dai, unsigned int rate, | ||
66 | struct msp_protdesc *prot_desc) | ||
67 | { | ||
68 | struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev); | ||
69 | |||
70 | switch (drvdata->slots) { | ||
71 | case 1: | ||
72 | switch (rate) { | ||
73 | case 8000: | ||
74 | prot_desc->frame_period = | ||
75 | FRAME_PER_SINGLE_SLOT_8_KHZ; | ||
76 | break; | ||
77 | |||
78 | case 16000: | ||
79 | prot_desc->frame_period = | ||
80 | FRAME_PER_SINGLE_SLOT_16_KHZ; | ||
81 | break; | ||
82 | |||
83 | case 44100: | ||
84 | prot_desc->frame_period = | ||
85 | FRAME_PER_SINGLE_SLOT_44_1_KHZ; | ||
86 | break; | ||
87 | |||
88 | case 48000: | ||
89 | prot_desc->frame_period = | ||
90 | FRAME_PER_SINGLE_SLOT_48_KHZ; | ||
91 | break; | ||
92 | |||
93 | default: | ||
94 | dev_err(dai->dev, | ||
95 | "%s: Error: Unsupported sample-rate (freq = %d)!\n", | ||
96 | __func__, rate); | ||
97 | return -EINVAL; | ||
98 | } | ||
99 | break; | ||
100 | |||
101 | case 2: | ||
102 | prot_desc->frame_period = FRAME_PER_2_SLOTS; | ||
103 | break; | ||
104 | |||
105 | case 8: | ||
106 | prot_desc->frame_period = FRAME_PER_8_SLOTS; | ||
107 | break; | ||
108 | |||
109 | case 16: | ||
110 | prot_desc->frame_period = FRAME_PER_16_SLOTS; | ||
111 | break; | ||
112 | default: | ||
113 | dev_err(dai->dev, | ||
114 | "%s: Error: Unsupported slot-count (slots = %d)!\n", | ||
115 | __func__, drvdata->slots); | ||
116 | return -EINVAL; | ||
117 | } | ||
118 | |||
119 | prot_desc->clocks_per_frame = | ||
120 | prot_desc->frame_period+1; | ||
121 | |||
122 | dev_dbg(dai->dev, "%s: Clocks per frame: %u\n", | ||
123 | __func__, | ||
124 | prot_desc->clocks_per_frame); | ||
125 | |||
126 | return 0; | ||
127 | } | ||
128 | |||
129 | static int setup_pcm_framing(struct snd_soc_dai *dai, unsigned int rate, | ||
130 | struct msp_protdesc *prot_desc) | ||
131 | { | ||
132 | struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev); | ||
133 | |||
134 | u32 frame_length = MSP_FRAME_LEN_1; | ||
135 | prot_desc->frame_width = 0; | ||
136 | |||
137 | switch (drvdata->slots) { | ||
138 | case 1: | ||
139 | frame_length = MSP_FRAME_LEN_1; | ||
140 | break; | ||
141 | |||
142 | case 2: | ||
143 | frame_length = MSP_FRAME_LEN_2; | ||
144 | break; | ||
145 | |||
146 | case 8: | ||
147 | frame_length = MSP_FRAME_LEN_8; | ||
148 | break; | ||
149 | |||
150 | case 16: | ||
151 | frame_length = MSP_FRAME_LEN_16; | ||
152 | break; | ||
153 | default: | ||
154 | dev_err(dai->dev, | ||
155 | "%s: Error: Unsupported slot-count (slots = %d)!\n", | ||
156 | __func__, drvdata->slots); | ||
157 | return -EINVAL; | ||
158 | } | ||
159 | |||
160 | prot_desc->tx_frame_len_1 = frame_length; | ||
161 | prot_desc->rx_frame_len_1 = frame_length; | ||
162 | prot_desc->tx_frame_len_2 = frame_length; | ||
163 | prot_desc->rx_frame_len_2 = frame_length; | ||
164 | |||
165 | prot_desc->tx_elem_len_1 = MSP_ELEM_LEN_16; | ||
166 | prot_desc->rx_elem_len_1 = MSP_ELEM_LEN_16; | ||
167 | prot_desc->tx_elem_len_2 = MSP_ELEM_LEN_16; | ||
168 | prot_desc->rx_elem_len_2 = MSP_ELEM_LEN_16; | ||
169 | |||
170 | return setup_frameper(dai, rate, prot_desc); | ||
171 | } | ||
172 | |||
173 | static int setup_clocking(struct snd_soc_dai *dai, | ||
174 | unsigned int fmt, | ||
175 | struct ux500_msp_config *msp_config) | ||
176 | { | ||
177 | switch (fmt & SND_SOC_DAIFMT_INV_MASK) { | ||
178 | case SND_SOC_DAIFMT_NB_NF: | ||
179 | break; | ||
180 | |||
181 | case SND_SOC_DAIFMT_NB_IF: | ||
182 | msp_config->tx_fsync_pol ^= 1 << TFSPOL_SHIFT; | ||
183 | msp_config->rx_fsync_pol ^= 1 << RFSPOL_SHIFT; | ||
184 | |||
185 | break; | ||
186 | |||
187 | default: | ||
188 | dev_err(dai->dev, | ||
189 | "%s: Error: Unsopported inversion (fmt = 0x%x)!\n", | ||
190 | __func__, fmt); | ||
191 | |||
192 | return -EINVAL; | ||
193 | } | ||
194 | |||
195 | switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { | ||
196 | case SND_SOC_DAIFMT_CBM_CFM: | ||
197 | dev_dbg(dai->dev, "%s: Codec is master.\n", __func__); | ||
198 | |||
199 | msp_config->iodelay = 0x20; | ||
200 | msp_config->rx_fsync_sel = 0; | ||
201 | msp_config->tx_fsync_sel = 1 << TFSSEL_SHIFT; | ||
202 | msp_config->tx_clk_sel = 0; | ||
203 | msp_config->rx_clk_sel = 0; | ||
204 | msp_config->srg_clk_sel = 0x2 << SCKSEL_SHIFT; | ||
205 | |||
206 | break; | ||
207 | |||
208 | case SND_SOC_DAIFMT_CBS_CFS: | ||
209 | dev_dbg(dai->dev, "%s: Codec is slave.\n", __func__); | ||
210 | |||
211 | msp_config->tx_clk_sel = TX_CLK_SEL_SRG; | ||
212 | msp_config->tx_fsync_sel = TX_SYNC_SRG_PROG; | ||
213 | msp_config->rx_clk_sel = RX_CLK_SEL_SRG; | ||
214 | msp_config->rx_fsync_sel = RX_SYNC_SRG; | ||
215 | msp_config->srg_clk_sel = 1 << SCKSEL_SHIFT; | ||
216 | |||
217 | break; | ||
218 | |||
219 | default: | ||
220 | dev_err(dai->dev, "%s: Error: Unsopported master (fmt = 0x%x)!\n", | ||
221 | __func__, fmt); | ||
222 | |||
223 | return -EINVAL; | ||
224 | } | ||
225 | |||
226 | return 0; | ||
227 | } | ||
228 | |||
229 | static int setup_pcm_protdesc(struct snd_soc_dai *dai, | ||
230 | unsigned int fmt, | ||
231 | struct msp_protdesc *prot_desc) | ||
232 | { | ||
233 | prot_desc->rx_phase_mode = MSP_SINGLE_PHASE; | ||
234 | prot_desc->tx_phase_mode = MSP_SINGLE_PHASE; | ||
235 | prot_desc->rx_phase2_start_mode = MSP_PHASE2_START_MODE_IMEDIATE; | ||
236 | prot_desc->tx_phase2_start_mode = MSP_PHASE2_START_MODE_IMEDIATE; | ||
237 | prot_desc->rx_byte_order = MSP_BTF_MS_BIT_FIRST; | ||
238 | prot_desc->tx_byte_order = MSP_BTF_MS_BIT_FIRST; | ||
239 | prot_desc->tx_fsync_pol = MSP_FSYNC_POL(MSP_FSYNC_POL_ACT_HI); | ||
240 | prot_desc->rx_fsync_pol = MSP_FSYNC_POL_ACT_HI << RFSPOL_SHIFT; | ||
241 | |||
242 | if ((fmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_DSP_A) { | ||
243 | dev_dbg(dai->dev, "%s: DSP_A.\n", __func__); | ||
244 | prot_desc->rx_clk_pol = MSP_RISING_EDGE; | ||
245 | prot_desc->tx_clk_pol = MSP_FALLING_EDGE; | ||
246 | |||
247 | prot_desc->rx_data_delay = MSP_DELAY_1; | ||
248 | prot_desc->tx_data_delay = MSP_DELAY_1; | ||
249 | } else { | ||
250 | dev_dbg(dai->dev, "%s: DSP_B.\n", __func__); | ||
251 | prot_desc->rx_clk_pol = MSP_FALLING_EDGE; | ||
252 | prot_desc->tx_clk_pol = MSP_RISING_EDGE; | ||
253 | |||
254 | prot_desc->rx_data_delay = MSP_DELAY_0; | ||
255 | prot_desc->tx_data_delay = MSP_DELAY_0; | ||
256 | } | ||
257 | |||
258 | prot_desc->rx_half_word_swap = MSP_SWAP_NONE; | ||
259 | prot_desc->tx_half_word_swap = MSP_SWAP_NONE; | ||
260 | prot_desc->compression_mode = MSP_COMPRESS_MODE_LINEAR; | ||
261 | prot_desc->expansion_mode = MSP_EXPAND_MODE_LINEAR; | ||
262 | prot_desc->frame_sync_ignore = MSP_FSYNC_IGNORE; | ||
263 | |||
264 | return 0; | ||
265 | } | ||
266 | |||
267 | static int setup_i2s_protdesc(struct msp_protdesc *prot_desc) | ||
268 | { | ||
269 | prot_desc->rx_phase_mode = MSP_DUAL_PHASE; | ||
270 | prot_desc->tx_phase_mode = MSP_DUAL_PHASE; | ||
271 | prot_desc->rx_phase2_start_mode = MSP_PHASE2_START_MODE_FSYNC; | ||
272 | prot_desc->tx_phase2_start_mode = MSP_PHASE2_START_MODE_FSYNC; | ||
273 | prot_desc->rx_byte_order = MSP_BTF_MS_BIT_FIRST; | ||
274 | prot_desc->tx_byte_order = MSP_BTF_MS_BIT_FIRST; | ||
275 | prot_desc->tx_fsync_pol = MSP_FSYNC_POL(MSP_FSYNC_POL_ACT_LO); | ||
276 | prot_desc->rx_fsync_pol = MSP_FSYNC_POL_ACT_LO << RFSPOL_SHIFT; | ||
277 | |||
278 | prot_desc->rx_frame_len_1 = MSP_FRAME_LEN_1; | ||
279 | prot_desc->rx_frame_len_2 = MSP_FRAME_LEN_1; | ||
280 | prot_desc->tx_frame_len_1 = MSP_FRAME_LEN_1; | ||
281 | prot_desc->tx_frame_len_2 = MSP_FRAME_LEN_1; | ||
282 | prot_desc->rx_elem_len_1 = MSP_ELEM_LEN_16; | ||
283 | prot_desc->rx_elem_len_2 = MSP_ELEM_LEN_16; | ||
284 | prot_desc->tx_elem_len_1 = MSP_ELEM_LEN_16; | ||
285 | prot_desc->tx_elem_len_2 = MSP_ELEM_LEN_16; | ||
286 | |||
287 | prot_desc->rx_clk_pol = MSP_RISING_EDGE; | ||
288 | prot_desc->tx_clk_pol = MSP_FALLING_EDGE; | ||
289 | |||
290 | prot_desc->rx_data_delay = MSP_DELAY_0; | ||
291 | prot_desc->tx_data_delay = MSP_DELAY_0; | ||
292 | |||
293 | prot_desc->tx_half_word_swap = MSP_SWAP_NONE; | ||
294 | prot_desc->rx_half_word_swap = MSP_SWAP_NONE; | ||
295 | prot_desc->compression_mode = MSP_COMPRESS_MODE_LINEAR; | ||
296 | prot_desc->expansion_mode = MSP_EXPAND_MODE_LINEAR; | ||
297 | prot_desc->frame_sync_ignore = MSP_FSYNC_IGNORE; | ||
298 | |||
299 | return 0; | ||
300 | } | ||
301 | |||
302 | static int setup_msp_config(struct snd_pcm_substream *substream, | ||
303 | struct snd_soc_dai *dai, | ||
304 | struct ux500_msp_config *msp_config) | ||
305 | { | ||
306 | struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev); | ||
307 | struct msp_protdesc *prot_desc = &msp_config->protdesc; | ||
308 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
309 | unsigned int fmt = drvdata->fmt; | ||
310 | int ret; | ||
311 | |||
312 | memset(msp_config, 0, sizeof(*msp_config)); | ||
313 | |||
314 | msp_config->f_inputclk = drvdata->master_clk; | ||
315 | |||
316 | msp_config->tx_fifo_config = TX_FIFO_ENABLE; | ||
317 | msp_config->rx_fifo_config = RX_FIFO_ENABLE; | ||
318 | msp_config->def_elem_len = 1; | ||
319 | msp_config->direction = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? | ||
320 | MSP_DIR_TX : MSP_DIR_RX; | ||
321 | msp_config->data_size = MSP_DATA_BITS_32; | ||
322 | msp_config->frame_freq = runtime->rate; | ||
323 | |||
324 | dev_dbg(dai->dev, "%s: f_inputclk = %u, frame_freq = %u.\n", | ||
325 | __func__, msp_config->f_inputclk, msp_config->frame_freq); | ||
326 | /* To avoid division by zero */ | ||
327 | prot_desc->clocks_per_frame = 1; | ||
328 | |||
329 | dev_dbg(dai->dev, "%s: rate: %u, channels: %d.\n", __func__, | ||
330 | runtime->rate, runtime->channels); | ||
331 | switch (fmt & | ||
332 | (SND_SOC_DAIFMT_FORMAT_MASK | SND_SOC_DAIFMT_MASTER_MASK)) { | ||
333 | case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS: | ||
334 | dev_dbg(dai->dev, "%s: SND_SOC_DAIFMT_I2S.\n", __func__); | ||
335 | |||
336 | msp_config->default_protdesc = 1; | ||
337 | msp_config->protocol = MSP_I2S_PROTOCOL; | ||
338 | break; | ||
339 | |||
340 | case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM: | ||
341 | dev_dbg(dai->dev, "%s: SND_SOC_DAIFMT_I2S.\n", __func__); | ||
342 | |||
343 | msp_config->data_size = MSP_DATA_BITS_16; | ||
344 | msp_config->protocol = MSP_I2S_PROTOCOL; | ||
345 | |||
346 | ret = setup_i2s_protdesc(prot_desc); | ||
347 | if (ret < 0) | ||
348 | return ret; | ||
349 | |||
350 | break; | ||
351 | |||
352 | case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBS_CFS: | ||
353 | case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBM_CFM: | ||
354 | case SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBS_CFS: | ||
355 | case SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBM_CFM: | ||
356 | dev_dbg(dai->dev, "%s: PCM format.\n", __func__); | ||
357 | |||
358 | msp_config->data_size = MSP_DATA_BITS_16; | ||
359 | msp_config->protocol = MSP_PCM_PROTOCOL; | ||
360 | |||
361 | ret = setup_pcm_protdesc(dai, fmt, prot_desc); | ||
362 | if (ret < 0) | ||
363 | return ret; | ||
364 | |||
365 | ret = setup_pcm_multichan(dai, msp_config); | ||
366 | if (ret < 0) | ||
367 | return ret; | ||
368 | |||
369 | ret = setup_pcm_framing(dai, runtime->rate, prot_desc); | ||
370 | if (ret < 0) | ||
371 | return ret; | ||
372 | |||
373 | break; | ||
374 | |||
375 | default: | ||
376 | dev_err(dai->dev, "%s: Error: Unsopported format (%d)!\n", | ||
377 | __func__, fmt); | ||
378 | return -EINVAL; | ||
379 | } | ||
380 | |||
381 | return setup_clocking(dai, fmt, msp_config); | ||
382 | } | ||
383 | |||
384 | static int ux500_msp_dai_startup(struct snd_pcm_substream *substream, | ||
385 | struct snd_soc_dai *dai) | ||
386 | { | ||
387 | int ret = 0; | ||
388 | struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev); | ||
389 | |||
390 | dev_dbg(dai->dev, "%s: MSP %d (%s): Enter.\n", __func__, dai->id, | ||
391 | snd_pcm_stream_str(substream)); | ||
392 | |||
393 | /* Enable regulator */ | ||
394 | ret = regulator_enable(drvdata->reg_vape); | ||
395 | if (ret != 0) { | ||
396 | dev_err(drvdata->msp->dev, | ||
397 | "%s: Failed to enable regulator!\n", __func__); | ||
398 | return ret; | ||
399 | } | ||
400 | |||
401 | /* Enable clock */ | ||
402 | dev_dbg(dai->dev, "%s: Enabling MSP-clock.\n", __func__); | ||
403 | clk_enable(drvdata->clk); | ||
404 | |||
405 | return 0; | ||
406 | } | ||
407 | |||
408 | static void ux500_msp_dai_shutdown(struct snd_pcm_substream *substream, | ||
409 | struct snd_soc_dai *dai) | ||
410 | { | ||
411 | int ret; | ||
412 | struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev); | ||
413 | bool is_playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK); | ||
414 | |||
415 | dev_dbg(dai->dev, "%s: MSP %d (%s): Enter.\n", __func__, dai->id, | ||
416 | snd_pcm_stream_str(substream)); | ||
417 | |||
418 | if (drvdata->vape_opp_constraint == 1) { | ||
419 | prcmu_qos_update_requirement(PRCMU_QOS_APE_OPP, | ||
420 | "ux500_msp_i2s", 50); | ||
421 | drvdata->vape_opp_constraint = 0; | ||
422 | } | ||
423 | |||
424 | if (ux500_msp_i2s_close(drvdata->msp, | ||
425 | is_playback ? MSP_DIR_TX : MSP_DIR_RX)) { | ||
426 | dev_err(dai->dev, | ||
427 | "%s: Error: MSP %d (%s): Unable to close i2s.\n", | ||
428 | __func__, dai->id, snd_pcm_stream_str(substream)); | ||
429 | } | ||
430 | |||
431 | /* Disable clock */ | ||
432 | clk_disable(drvdata->clk); | ||
433 | |||
434 | /* Disable regulator */ | ||
435 | ret = regulator_disable(drvdata->reg_vape); | ||
436 | if (ret < 0) | ||
437 | dev_err(dai->dev, | ||
438 | "%s: ERROR: Failed to disable regulator (%d)!\n", | ||
439 | __func__, ret); | ||
440 | } | ||
441 | |||
442 | static int ux500_msp_dai_prepare(struct snd_pcm_substream *substream, | ||
443 | struct snd_soc_dai *dai) | ||
444 | { | ||
445 | int ret = 0; | ||
446 | struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev); | ||
447 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
448 | struct ux500_msp_config msp_config; | ||
449 | |||
450 | dev_dbg(dai->dev, "%s: MSP %d (%s): Enter (rate = %d).\n", __func__, | ||
451 | dai->id, snd_pcm_stream_str(substream), runtime->rate); | ||
452 | |||
453 | setup_msp_config(substream, dai, &msp_config); | ||
454 | |||
455 | ret = ux500_msp_i2s_open(drvdata->msp, &msp_config); | ||
456 | if (ret < 0) { | ||
457 | dev_err(dai->dev, "%s: Error: msp_setup failed (ret = %d)!\n", | ||
458 | __func__, ret); | ||
459 | return ret; | ||
460 | } | ||
461 | |||
462 | /* Set OPP-level */ | ||
463 | if ((drvdata->fmt & SND_SOC_DAIFMT_MASTER_MASK) && | ||
464 | (drvdata->msp->f_bitclk > 19200000)) { | ||
465 | /* If the bit-clock is higher than 19.2MHz, Vape should be | ||
466 | * run in 100% OPP. Only when bit-clock is used (MSP master) */ | ||
467 | prcmu_qos_update_requirement(PRCMU_QOS_APE_OPP, | ||
468 | "ux500-msp-i2s", 100); | ||
469 | drvdata->vape_opp_constraint = 1; | ||
470 | } else { | ||
471 | prcmu_qos_update_requirement(PRCMU_QOS_APE_OPP, | ||
472 | "ux500-msp-i2s", 50); | ||
473 | drvdata->vape_opp_constraint = 0; | ||
474 | } | ||
475 | |||
476 | return ret; | ||
477 | } | ||
478 | |||
479 | static int ux500_msp_dai_hw_params(struct snd_pcm_substream *substream, | ||
480 | struct snd_pcm_hw_params *params, | ||
481 | struct snd_soc_dai *dai) | ||
482 | { | ||
483 | unsigned int mask, slots_active; | ||
484 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
485 | struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev); | ||
486 | |||
487 | dev_dbg(dai->dev, "%s: MSP %d (%s): Enter.\n", | ||
488 | __func__, dai->id, snd_pcm_stream_str(substream)); | ||
489 | |||
490 | switch (drvdata->fmt & SND_SOC_DAIFMT_FORMAT_MASK) { | ||
491 | case SND_SOC_DAIFMT_I2S: | ||
492 | snd_pcm_hw_constraint_minmax(runtime, | ||
493 | SNDRV_PCM_HW_PARAM_CHANNELS, | ||
494 | 1, 2); | ||
495 | break; | ||
496 | |||
497 | case SND_SOC_DAIFMT_DSP_B: | ||
498 | case SND_SOC_DAIFMT_DSP_A: | ||
499 | mask = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? | ||
500 | drvdata->tx_mask : | ||
501 | drvdata->rx_mask; | ||
502 | |||
503 | slots_active = hweight32(mask); | ||
504 | dev_dbg(dai->dev, "TDM-slots active: %d", slots_active); | ||
505 | |||
506 | snd_pcm_hw_constraint_minmax(runtime, | ||
507 | SNDRV_PCM_HW_PARAM_CHANNELS, | ||
508 | slots_active, slots_active); | ||
509 | break; | ||
510 | |||
511 | default: | ||
512 | dev_err(dai->dev, | ||
513 | "%s: Error: Unsupported protocol (fmt = 0x%x)!\n", | ||
514 | __func__, drvdata->fmt); | ||
515 | return -EINVAL; | ||
516 | } | ||
517 | |||
518 | return 0; | ||
519 | } | ||
520 | |||
521 | static int ux500_msp_dai_set_dai_fmt(struct snd_soc_dai *dai, | ||
522 | unsigned int fmt) | ||
523 | { | ||
524 | struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev); | ||
525 | |||
526 | dev_dbg(dai->dev, "%s: MSP %d: Enter.\n", __func__, dai->id); | ||
527 | |||
528 | switch (fmt & (SND_SOC_DAIFMT_FORMAT_MASK | | ||
529 | SND_SOC_DAIFMT_MASTER_MASK)) { | ||
530 | case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS: | ||
531 | case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM: | ||
532 | case SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBS_CFS: | ||
533 | case SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBM_CFM: | ||
534 | case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBS_CFS: | ||
535 | case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBM_CFM: | ||
536 | break; | ||
537 | |||
538 | default: | ||
539 | dev_err(dai->dev, | ||
540 | "%s: Error: Unsupported protocol/master (fmt = 0x%x)!\n", | ||
541 | __func__, drvdata->fmt); | ||
542 | return -EINVAL; | ||
543 | } | ||
544 | |||
545 | switch (fmt & SND_SOC_DAIFMT_INV_MASK) { | ||
546 | case SND_SOC_DAIFMT_NB_NF: | ||
547 | case SND_SOC_DAIFMT_NB_IF: | ||
548 | case SND_SOC_DAIFMT_IB_IF: | ||
549 | break; | ||
550 | |||
551 | default: | ||
552 | dev_err(dai->dev, | ||
553 | "%s: Error: Unsupported inversion (fmt = 0x%x)!\n", | ||
554 | __func__, drvdata->fmt); | ||
555 | return -EINVAL; | ||
556 | } | ||
557 | |||
558 | drvdata->fmt = fmt; | ||
559 | return 0; | ||
560 | } | ||
561 | |||
562 | static int ux500_msp_dai_set_tdm_slot(struct snd_soc_dai *dai, | ||
563 | unsigned int tx_mask, | ||
564 | unsigned int rx_mask, | ||
565 | int slots, int slot_width) | ||
566 | { | ||
567 | struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev); | ||
568 | unsigned int cap; | ||
569 | |||
570 | switch (slots) { | ||
571 | case 1: | ||
572 | cap = 0x01; | ||
573 | break; | ||
574 | case 2: | ||
575 | cap = 0x03; | ||
576 | break; | ||
577 | case 8: | ||
578 | cap = 0xFF; | ||
579 | break; | ||
580 | case 16: | ||
581 | cap = 0xFFFF; | ||
582 | break; | ||
583 | default: | ||
584 | dev_err(dai->dev, "%s: Error: Unsupported slot-count (%d)!\n", | ||
585 | __func__, slots); | ||
586 | return -EINVAL; | ||
587 | } | ||
588 | drvdata->slots = slots; | ||
589 | |||
590 | if (!(slot_width == 16)) { | ||
591 | dev_err(dai->dev, "%s: Error: Unsupported slot-width (%d)!\n", | ||
592 | __func__, slot_width); | ||
593 | return -EINVAL; | ||
594 | } | ||
595 | drvdata->slot_width = slot_width; | ||
596 | |||
597 | drvdata->tx_mask = tx_mask & cap; | ||
598 | drvdata->rx_mask = rx_mask & cap; | ||
599 | |||
600 | return 0; | ||
601 | } | ||
602 | |||
603 | static int ux500_msp_dai_set_dai_sysclk(struct snd_soc_dai *dai, | ||
604 | int clk_id, unsigned int freq, int dir) | ||
605 | { | ||
606 | struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev); | ||
607 | |||
608 | dev_dbg(dai->dev, "%s: MSP %d: Enter. clk-id: %d, freq: %u.\n", | ||
609 | __func__, dai->id, clk_id, freq); | ||
610 | |||
611 | switch (clk_id) { | ||
612 | case UX500_MSP_MASTER_CLOCK: | ||
613 | drvdata->master_clk = freq; | ||
614 | break; | ||
615 | |||
616 | default: | ||
617 | dev_err(dai->dev, "%s: MSP %d: Invalid clk-id (%d)!\n", | ||
618 | __func__, dai->id, clk_id); | ||
619 | return -EINVAL; | ||
620 | } | ||
621 | |||
622 | return 0; | ||
623 | } | ||
624 | |||
625 | static int ux500_msp_dai_trigger(struct snd_pcm_substream *substream, | ||
626 | int cmd, struct snd_soc_dai *dai) | ||
627 | { | ||
628 | int ret = 0; | ||
629 | struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev); | ||
630 | |||
631 | dev_dbg(dai->dev, "%s: MSP %d (%s): Enter (msp->id = %d, cmd = %d).\n", | ||
632 | __func__, dai->id, snd_pcm_stream_str(substream), | ||
633 | (int)drvdata->msp->id, cmd); | ||
634 | |||
635 | ret = ux500_msp_i2s_trigger(drvdata->msp, cmd, substream->stream); | ||
636 | |||
637 | return ret; | ||
638 | } | ||
639 | |||
640 | static int ux500_msp_dai_probe(struct snd_soc_dai *dai) | ||
641 | { | ||
642 | struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev); | ||
643 | |||
644 | drvdata->playback_dma_data.dma_cfg = drvdata->msp->dma_cfg_tx; | ||
645 | drvdata->capture_dma_data.dma_cfg = drvdata->msp->dma_cfg_rx; | ||
646 | |||
647 | dai->playback_dma_data = &drvdata->playback_dma_data; | ||
648 | dai->capture_dma_data = &drvdata->capture_dma_data; | ||
649 | |||
650 | drvdata->playback_dma_data.data_size = drvdata->slot_width; | ||
651 | drvdata->capture_dma_data.data_size = drvdata->slot_width; | ||
652 | |||
653 | return 0; | ||
654 | } | ||
655 | |||
656 | static struct snd_soc_dai_ops ux500_msp_dai_ops[] = { | ||
657 | { | ||
658 | .set_sysclk = ux500_msp_dai_set_dai_sysclk, | ||
659 | .set_fmt = ux500_msp_dai_set_dai_fmt, | ||
660 | .set_tdm_slot = ux500_msp_dai_set_tdm_slot, | ||
661 | .startup = ux500_msp_dai_startup, | ||
662 | .shutdown = ux500_msp_dai_shutdown, | ||
663 | .prepare = ux500_msp_dai_prepare, | ||
664 | .trigger = ux500_msp_dai_trigger, | ||
665 | .hw_params = ux500_msp_dai_hw_params, | ||
666 | } | ||
667 | }; | ||
668 | |||
669 | static struct snd_soc_dai_driver ux500_msp_dai_drv[UX500_NBR_OF_DAI] = { | ||
670 | { | ||
671 | .name = "ux500-msp-i2s.0", | ||
672 | .probe = ux500_msp_dai_probe, | ||
673 | .id = 0, | ||
674 | .suspend = NULL, | ||
675 | .resume = NULL, | ||
676 | .playback = { | ||
677 | .channels_min = UX500_MSP_MIN_CHANNELS, | ||
678 | .channels_max = UX500_MSP_MAX_CHANNELS, | ||
679 | .rates = UX500_I2S_RATES, | ||
680 | .formats = UX500_I2S_FORMATS, | ||
681 | }, | ||
682 | .capture = { | ||
683 | .channels_min = UX500_MSP_MIN_CHANNELS, | ||
684 | .channels_max = UX500_MSP_MAX_CHANNELS, | ||
685 | .rates = UX500_I2S_RATES, | ||
686 | .formats = UX500_I2S_FORMATS, | ||
687 | }, | ||
688 | .ops = ux500_msp_dai_ops, | ||
689 | }, | ||
690 | { | ||
691 | .name = "ux500-msp-i2s.1", | ||
692 | .probe = ux500_msp_dai_probe, | ||
693 | .id = 1, | ||
694 | .suspend = NULL, | ||
695 | .resume = NULL, | ||
696 | .playback = { | ||
697 | .channels_min = UX500_MSP_MIN_CHANNELS, | ||
698 | .channels_max = UX500_MSP_MAX_CHANNELS, | ||
699 | .rates = UX500_I2S_RATES, | ||
700 | .formats = UX500_I2S_FORMATS, | ||
701 | }, | ||
702 | .capture = { | ||
703 | .channels_min = UX500_MSP_MIN_CHANNELS, | ||
704 | .channels_max = UX500_MSP_MAX_CHANNELS, | ||
705 | .rates = UX500_I2S_RATES, | ||
706 | .formats = UX500_I2S_FORMATS, | ||
707 | }, | ||
708 | .ops = ux500_msp_dai_ops, | ||
709 | }, | ||
710 | { | ||
711 | .name = "ux500-msp-i2s.2", | ||
712 | .id = 2, | ||
713 | .probe = ux500_msp_dai_probe, | ||
714 | .suspend = NULL, | ||
715 | .resume = NULL, | ||
716 | .playback = { | ||
717 | .channels_min = UX500_MSP_MIN_CHANNELS, | ||
718 | .channels_max = UX500_MSP_MAX_CHANNELS, | ||
719 | .rates = UX500_I2S_RATES, | ||
720 | .formats = UX500_I2S_FORMATS, | ||
721 | }, | ||
722 | .capture = { | ||
723 | .channels_min = UX500_MSP_MIN_CHANNELS, | ||
724 | .channels_max = UX500_MSP_MAX_CHANNELS, | ||
725 | .rates = UX500_I2S_RATES, | ||
726 | .formats = UX500_I2S_FORMATS, | ||
727 | }, | ||
728 | .ops = ux500_msp_dai_ops, | ||
729 | }, | ||
730 | { | ||
731 | .name = "ux500-msp-i2s.3", | ||
732 | .probe = ux500_msp_dai_probe, | ||
733 | .id = 3, | ||
734 | .suspend = NULL, | ||
735 | .resume = NULL, | ||
736 | .playback = { | ||
737 | .channels_min = UX500_MSP_MIN_CHANNELS, | ||
738 | .channels_max = UX500_MSP_MAX_CHANNELS, | ||
739 | .rates = UX500_I2S_RATES, | ||
740 | .formats = UX500_I2S_FORMATS, | ||
741 | }, | ||
742 | .capture = { | ||
743 | .channels_min = UX500_MSP_MIN_CHANNELS, | ||
744 | .channels_max = UX500_MSP_MAX_CHANNELS, | ||
745 | .rates = UX500_I2S_RATES, | ||
746 | .formats = UX500_I2S_FORMATS, | ||
747 | }, | ||
748 | .ops = ux500_msp_dai_ops, | ||
749 | }, | ||
750 | }; | ||
751 | |||
752 | static int __devinit ux500_msp_drv_probe(struct platform_device *pdev) | ||
753 | { | ||
754 | struct ux500_msp_i2s_drvdata *drvdata; | ||
755 | int ret = 0; | ||
756 | |||
757 | dev_dbg(&pdev->dev, "%s: Enter (pdev->name = %s).\n", __func__, | ||
758 | pdev->name); | ||
759 | |||
760 | drvdata = devm_kzalloc(&pdev->dev, | ||
761 | sizeof(struct ux500_msp_i2s_drvdata), | ||
762 | GFP_KERNEL); | ||
763 | drvdata->fmt = 0; | ||
764 | drvdata->slots = 1; | ||
765 | drvdata->tx_mask = 0x01; | ||
766 | drvdata->rx_mask = 0x01; | ||
767 | drvdata->slot_width = 16; | ||
768 | drvdata->master_clk = MSP_INPUT_FREQ_APB; | ||
769 | |||
770 | drvdata->reg_vape = devm_regulator_get(&pdev->dev, "v-ape"); | ||
771 | if (IS_ERR(drvdata->reg_vape)) { | ||
772 | ret = (int)PTR_ERR(drvdata->reg_vape); | ||
773 | dev_err(&pdev->dev, | ||
774 | "%s: ERROR: Failed to get Vape supply (%d)!\n", | ||
775 | __func__, ret); | ||
776 | return ret; | ||
777 | } | ||
778 | prcmu_qos_add_requirement(PRCMU_QOS_APE_OPP, (char *)pdev->name, 50); | ||
779 | |||
780 | drvdata->clk = clk_get(&pdev->dev, NULL); | ||
781 | if (IS_ERR(drvdata->clk)) { | ||
782 | ret = (int)PTR_ERR(drvdata->clk); | ||
783 | dev_err(&pdev->dev, "%s: ERROR: clk_get failed (%d)!\n", | ||
784 | __func__, ret); | ||
785 | goto err_clk; | ||
786 | } | ||
787 | |||
788 | ret = ux500_msp_i2s_init_msp(pdev, &drvdata->msp, | ||
789 | pdev->dev.platform_data); | ||
790 | if (!drvdata->msp) { | ||
791 | dev_err(&pdev->dev, | ||
792 | "%s: ERROR: Failed to init MSP-struct (%d)!", | ||
793 | __func__, ret); | ||
794 | goto err_init_msp; | ||
795 | } | ||
796 | dev_set_drvdata(&pdev->dev, drvdata); | ||
797 | |||
798 | ret = snd_soc_register_dai(&pdev->dev, | ||
799 | &ux500_msp_dai_drv[drvdata->msp->id]); | ||
800 | if (ret < 0) { | ||
801 | dev_err(&pdev->dev, "Error: %s: Failed to register MSP%d!\n", | ||
802 | __func__, drvdata->msp->id); | ||
803 | goto err_init_msp; | ||
804 | } | ||
805 | |||
806 | return 0; | ||
807 | |||
808 | err_init_msp: | ||
809 | clk_put(drvdata->clk); | ||
810 | |||
811 | err_clk: | ||
812 | devm_regulator_put(drvdata->reg_vape); | ||
813 | |||
814 | return ret; | ||
815 | } | ||
816 | |||
817 | static int __devexit ux500_msp_drv_remove(struct platform_device *pdev) | ||
818 | { | ||
819 | struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(&pdev->dev); | ||
820 | |||
821 | snd_soc_unregister_dais(&pdev->dev, ARRAY_SIZE(ux500_msp_dai_drv)); | ||
822 | |||
823 | devm_regulator_put(drvdata->reg_vape); | ||
824 | prcmu_qos_remove_requirement(PRCMU_QOS_APE_OPP, "ux500_msp_i2s"); | ||
825 | |||
826 | clk_put(drvdata->clk); | ||
827 | |||
828 | ux500_msp_i2s_cleanup_msp(pdev, drvdata->msp); | ||
829 | |||
830 | return 0; | ||
831 | } | ||
832 | |||
833 | static struct platform_driver msp_i2s_driver = { | ||
834 | .driver = { | ||
835 | .name = "ux500-msp-i2s", | ||
836 | .owner = THIS_MODULE, | ||
837 | }, | ||
838 | .probe = ux500_msp_drv_probe, | ||
839 | .remove = ux500_msp_drv_remove, | ||
840 | }; | ||
841 | module_platform_driver(msp_i2s_driver); | ||
842 | |||
843 | MODULE_LICENSE("GPLv2"); | ||
diff --git a/sound/soc/ux500/ux500_msp_dai.h b/sound/soc/ux500/ux500_msp_dai.h new file mode 100644 index 000000000000..98202a34a5dd --- /dev/null +++ b/sound/soc/ux500/ux500_msp_dai.h | |||
@@ -0,0 +1,79 @@ | |||
1 | /* | ||
2 | * Copyright (C) ST-Ericsson SA 2012 | ||
3 | * | ||
4 | * Author: Ola Lilja <ola.o.lilja@stericsson.com>, | ||
5 | * Roger Nilsson <roger.xr.nilsson@stericsson.com> | ||
6 | * for ST-Ericsson. | ||
7 | * | ||
8 | * License terms: | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License version 2 as published | ||
12 | * by the Free Software Foundation. | ||
13 | */ | ||
14 | |||
15 | #ifndef UX500_msp_dai_H | ||
16 | #define UX500_msp_dai_H | ||
17 | |||
18 | #include <linux/types.h> | ||
19 | #include <linux/spinlock.h> | ||
20 | |||
21 | #include "ux500_msp_i2s.h" | ||
22 | |||
23 | #define UX500_NBR_OF_DAI 4 | ||
24 | |||
25 | #define UX500_I2S_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | \ | ||
26 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000) | ||
27 | |||
28 | #define UX500_I2S_FORMATS (SNDRV_PCM_FMTBIT_S16_LE) | ||
29 | |||
30 | #define FRAME_PER_SINGLE_SLOT_8_KHZ 31 | ||
31 | #define FRAME_PER_SINGLE_SLOT_16_KHZ 124 | ||
32 | #define FRAME_PER_SINGLE_SLOT_44_1_KHZ 63 | ||
33 | #define FRAME_PER_SINGLE_SLOT_48_KHZ 49 | ||
34 | #define FRAME_PER_2_SLOTS 31 | ||
35 | #define FRAME_PER_8_SLOTS 138 | ||
36 | #define FRAME_PER_16_SLOTS 277 | ||
37 | |||
38 | #ifndef CONFIG_SND_SOC_UX500_AB5500 | ||
39 | #define UX500_MSP_INTERNAL_CLOCK_FREQ 40000000 | ||
40 | #define UX500_MSP1_INTERNAL_CLOCK_FREQ UX500_MSP_INTERNAL_CLOCK_FREQ | ||
41 | #else | ||
42 | #define UX500_MSP_INTERNAL_CLOCK_FREQ 13000000 | ||
43 | #define UX500_MSP1_INTERNAL_CLOCK_FREQ (UX500_MSP_INTERNAL_CLOCK_FREQ * 2) | ||
44 | #endif | ||
45 | |||
46 | #define UX500_MSP_MIN_CHANNELS 1 | ||
47 | #define UX500_MSP_MAX_CHANNELS 8 | ||
48 | |||
49 | #define PLAYBACK_CONFIGURED 1 | ||
50 | #define CAPTURE_CONFIGURED 2 | ||
51 | |||
52 | enum ux500_msp_clock_id { | ||
53 | UX500_MSP_MASTER_CLOCK, | ||
54 | }; | ||
55 | |||
56 | struct ux500_msp_i2s_drvdata { | ||
57 | struct ux500_msp *msp; | ||
58 | struct regulator *reg_vape; | ||
59 | struct ux500_msp_dma_params playback_dma_data; | ||
60 | struct ux500_msp_dma_params capture_dma_data; | ||
61 | unsigned int fmt; | ||
62 | unsigned int tx_mask; | ||
63 | unsigned int rx_mask; | ||
64 | int slots; | ||
65 | int slot_width; | ||
66 | u8 configured; | ||
67 | int data_delay; | ||
68 | |||
69 | /* Clocks */ | ||
70 | unsigned int master_clk; | ||
71 | struct clk *clk; | ||
72 | |||
73 | /* Regulators */ | ||
74 | int vape_opp_constraint; | ||
75 | }; | ||
76 | |||
77 | int ux500_msp_dai_set_data_delay(struct snd_soc_dai *dai, int delay); | ||
78 | |||
79 | #endif | ||
diff --git a/sound/soc/ux500/ux500_msp_i2s.c b/sound/soc/ux500/ux500_msp_i2s.c new file mode 100644 index 000000000000..496dec10c96e --- /dev/null +++ b/sound/soc/ux500/ux500_msp_i2s.c | |||
@@ -0,0 +1,742 @@ | |||
1 | /* | ||
2 | * Copyright (C) ST-Ericsson SA 2012 | ||
3 | * | ||
4 | * Author: Ola Lilja <ola.o.lilja@stericsson.com>, | ||
5 | * Roger Nilsson <roger.xr.nilsson@stericsson.com>, | ||
6 | * Sandeep Kaushik <sandeep.kaushik@st.com> | ||
7 | * for ST-Ericsson. | ||
8 | * | ||
9 | * License terms: | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License version 2 as published | ||
13 | * by the Free Software Foundation. | ||
14 | */ | ||
15 | |||
16 | #include <linux/module.h> | ||
17 | #include <linux/platform_device.h> | ||
18 | #include <linux/delay.h> | ||
19 | #include <linux/slab.h> | ||
20 | |||
21 | #include <mach/hardware.h> | ||
22 | #include <mach/board-mop500-msp.h> | ||
23 | |||
24 | #include <sound/soc.h> | ||
25 | |||
26 | #include "ux500_msp_i2s.h" | ||
27 | |||
28 | /* Protocol desciptors */ | ||
29 | static const struct msp_protdesc prot_descs[] = { | ||
30 | { /* I2S */ | ||
31 | MSP_SINGLE_PHASE, | ||
32 | MSP_SINGLE_PHASE, | ||
33 | MSP_PHASE2_START_MODE_IMEDIATE, | ||
34 | MSP_PHASE2_START_MODE_IMEDIATE, | ||
35 | MSP_BTF_MS_BIT_FIRST, | ||
36 | MSP_BTF_MS_BIT_FIRST, | ||
37 | MSP_FRAME_LEN_1, | ||
38 | MSP_FRAME_LEN_1, | ||
39 | MSP_FRAME_LEN_1, | ||
40 | MSP_FRAME_LEN_1, | ||
41 | MSP_ELEM_LEN_32, | ||
42 | MSP_ELEM_LEN_32, | ||
43 | MSP_ELEM_LEN_32, | ||
44 | MSP_ELEM_LEN_32, | ||
45 | MSP_DELAY_1, | ||
46 | MSP_DELAY_1, | ||
47 | MSP_RISING_EDGE, | ||
48 | MSP_FALLING_EDGE, | ||
49 | MSP_FSYNC_POL_ACT_LO, | ||
50 | MSP_FSYNC_POL_ACT_LO, | ||
51 | MSP_SWAP_NONE, | ||
52 | MSP_SWAP_NONE, | ||
53 | MSP_COMPRESS_MODE_LINEAR, | ||
54 | MSP_EXPAND_MODE_LINEAR, | ||
55 | MSP_FSYNC_IGNORE, | ||
56 | 31, | ||
57 | 15, | ||
58 | 32, | ||
59 | }, { /* PCM */ | ||
60 | MSP_DUAL_PHASE, | ||
61 | MSP_DUAL_PHASE, | ||
62 | MSP_PHASE2_START_MODE_FSYNC, | ||
63 | MSP_PHASE2_START_MODE_FSYNC, | ||
64 | MSP_BTF_MS_BIT_FIRST, | ||
65 | MSP_BTF_MS_BIT_FIRST, | ||
66 | MSP_FRAME_LEN_1, | ||
67 | MSP_FRAME_LEN_1, | ||
68 | MSP_FRAME_LEN_1, | ||
69 | MSP_FRAME_LEN_1, | ||
70 | MSP_ELEM_LEN_16, | ||
71 | MSP_ELEM_LEN_16, | ||
72 | MSP_ELEM_LEN_16, | ||
73 | MSP_ELEM_LEN_16, | ||
74 | MSP_DELAY_0, | ||
75 | MSP_DELAY_0, | ||
76 | MSP_RISING_EDGE, | ||
77 | MSP_FALLING_EDGE, | ||
78 | MSP_FSYNC_POL_ACT_HI, | ||
79 | MSP_FSYNC_POL_ACT_HI, | ||
80 | MSP_SWAP_NONE, | ||
81 | MSP_SWAP_NONE, | ||
82 | MSP_COMPRESS_MODE_LINEAR, | ||
83 | MSP_EXPAND_MODE_LINEAR, | ||
84 | MSP_FSYNC_IGNORE, | ||
85 | 255, | ||
86 | 0, | ||
87 | 256, | ||
88 | }, { /* Companded PCM */ | ||
89 | MSP_SINGLE_PHASE, | ||
90 | MSP_SINGLE_PHASE, | ||
91 | MSP_PHASE2_START_MODE_FSYNC, | ||
92 | MSP_PHASE2_START_MODE_FSYNC, | ||
93 | MSP_BTF_MS_BIT_FIRST, | ||
94 | MSP_BTF_MS_BIT_FIRST, | ||
95 | MSP_FRAME_LEN_1, | ||
96 | MSP_FRAME_LEN_1, | ||
97 | MSP_FRAME_LEN_1, | ||
98 | MSP_FRAME_LEN_1, | ||
99 | MSP_ELEM_LEN_8, | ||
100 | MSP_ELEM_LEN_8, | ||
101 | MSP_ELEM_LEN_8, | ||
102 | MSP_ELEM_LEN_8, | ||
103 | MSP_DELAY_0, | ||
104 | MSP_DELAY_0, | ||
105 | MSP_RISING_EDGE, | ||
106 | MSP_RISING_EDGE, | ||
107 | MSP_FSYNC_POL_ACT_HI, | ||
108 | MSP_FSYNC_POL_ACT_HI, | ||
109 | MSP_SWAP_NONE, | ||
110 | MSP_SWAP_NONE, | ||
111 | MSP_COMPRESS_MODE_LINEAR, | ||
112 | MSP_EXPAND_MODE_LINEAR, | ||
113 | MSP_FSYNC_IGNORE, | ||
114 | 255, | ||
115 | 0, | ||
116 | 256, | ||
117 | }, | ||
118 | }; | ||
119 | |||
120 | static void set_prot_desc_tx(struct ux500_msp *msp, | ||
121 | struct msp_protdesc *protdesc, | ||
122 | enum msp_data_size data_size) | ||
123 | { | ||
124 | u32 temp_reg = 0; | ||
125 | |||
126 | temp_reg |= MSP_P2_ENABLE_BIT(protdesc->tx_phase_mode); | ||
127 | temp_reg |= MSP_P2_START_MODE_BIT(protdesc->tx_phase2_start_mode); | ||
128 | temp_reg |= MSP_P1_FRAME_LEN_BITS(protdesc->tx_frame_len_1); | ||
129 | temp_reg |= MSP_P2_FRAME_LEN_BITS(protdesc->tx_frame_len_2); | ||
130 | if (msp->def_elem_len) { | ||
131 | temp_reg |= MSP_P1_ELEM_LEN_BITS(protdesc->tx_elem_len_1); | ||
132 | temp_reg |= MSP_P2_ELEM_LEN_BITS(protdesc->tx_elem_len_2); | ||
133 | } else { | ||
134 | temp_reg |= MSP_P1_ELEM_LEN_BITS(data_size); | ||
135 | temp_reg |= MSP_P2_ELEM_LEN_BITS(data_size); | ||
136 | } | ||
137 | temp_reg |= MSP_DATA_DELAY_BITS(protdesc->tx_data_delay); | ||
138 | temp_reg |= MSP_SET_ENDIANNES_BIT(protdesc->tx_byte_order); | ||
139 | temp_reg |= MSP_FSYNC_POL(protdesc->tx_fsync_pol); | ||
140 | temp_reg |= MSP_DATA_WORD_SWAP(protdesc->tx_half_word_swap); | ||
141 | temp_reg |= MSP_SET_COMPANDING_MODE(protdesc->compression_mode); | ||
142 | temp_reg |= MSP_SET_FSYNC_IGNORE(protdesc->frame_sync_ignore); | ||
143 | |||
144 | writel(temp_reg, msp->registers + MSP_TCF); | ||
145 | } | ||
146 | |||
147 | static void set_prot_desc_rx(struct ux500_msp *msp, | ||
148 | struct msp_protdesc *protdesc, | ||
149 | enum msp_data_size data_size) | ||
150 | { | ||
151 | u32 temp_reg = 0; | ||
152 | |||
153 | temp_reg |= MSP_P2_ENABLE_BIT(protdesc->rx_phase_mode); | ||
154 | temp_reg |= MSP_P2_START_MODE_BIT(protdesc->rx_phase2_start_mode); | ||
155 | temp_reg |= MSP_P1_FRAME_LEN_BITS(protdesc->rx_frame_len_1); | ||
156 | temp_reg |= MSP_P2_FRAME_LEN_BITS(protdesc->rx_frame_len_2); | ||
157 | if (msp->def_elem_len) { | ||
158 | temp_reg |= MSP_P1_ELEM_LEN_BITS(protdesc->rx_elem_len_1); | ||
159 | temp_reg |= MSP_P2_ELEM_LEN_BITS(protdesc->rx_elem_len_2); | ||
160 | } else { | ||
161 | temp_reg |= MSP_P1_ELEM_LEN_BITS(data_size); | ||
162 | temp_reg |= MSP_P2_ELEM_LEN_BITS(data_size); | ||
163 | } | ||
164 | |||
165 | temp_reg |= MSP_DATA_DELAY_BITS(protdesc->rx_data_delay); | ||
166 | temp_reg |= MSP_SET_ENDIANNES_BIT(protdesc->rx_byte_order); | ||
167 | temp_reg |= MSP_FSYNC_POL(protdesc->rx_fsync_pol); | ||
168 | temp_reg |= MSP_DATA_WORD_SWAP(protdesc->rx_half_word_swap); | ||
169 | temp_reg |= MSP_SET_COMPANDING_MODE(protdesc->expansion_mode); | ||
170 | temp_reg |= MSP_SET_FSYNC_IGNORE(protdesc->frame_sync_ignore); | ||
171 | |||
172 | writel(temp_reg, msp->registers + MSP_RCF); | ||
173 | } | ||
174 | |||
175 | static int configure_protocol(struct ux500_msp *msp, | ||
176 | struct ux500_msp_config *config) | ||
177 | { | ||
178 | struct msp_protdesc *protdesc; | ||
179 | enum msp_data_size data_size; | ||
180 | u32 temp_reg = 0; | ||
181 | |||
182 | data_size = config->data_size; | ||
183 | msp->def_elem_len = config->def_elem_len; | ||
184 | if (config->default_protdesc == 1) { | ||
185 | if (config->protocol >= MSP_INVALID_PROTOCOL) { | ||
186 | dev_err(msp->dev, "%s: ERROR: Invalid protocol!\n", | ||
187 | __func__); | ||
188 | return -EINVAL; | ||
189 | } | ||
190 | protdesc = | ||
191 | (struct msp_protdesc *)&prot_descs[config->protocol]; | ||
192 | } else { | ||
193 | protdesc = (struct msp_protdesc *)&config->protdesc; | ||
194 | } | ||
195 | |||
196 | if (data_size < MSP_DATA_BITS_DEFAULT || data_size > MSP_DATA_BITS_32) { | ||
197 | dev_err(msp->dev, | ||
198 | "%s: ERROR: Invalid data-size requested (data_size = %d)!\n", | ||
199 | __func__, data_size); | ||
200 | return -EINVAL; | ||
201 | } | ||
202 | |||
203 | if (config->direction & MSP_DIR_TX) | ||
204 | set_prot_desc_tx(msp, protdesc, data_size); | ||
205 | if (config->direction & MSP_DIR_RX) | ||
206 | set_prot_desc_rx(msp, protdesc, data_size); | ||
207 | |||
208 | /* The code below should not be separated. */ | ||
209 | temp_reg = readl(msp->registers + MSP_GCR) & ~TX_CLK_POL_RISING; | ||
210 | temp_reg |= MSP_TX_CLKPOL_BIT(~protdesc->tx_clk_pol); | ||
211 | writel(temp_reg, msp->registers + MSP_GCR); | ||
212 | temp_reg = readl(msp->registers + MSP_GCR) & ~RX_CLK_POL_RISING; | ||
213 | temp_reg |= MSP_RX_CLKPOL_BIT(protdesc->rx_clk_pol); | ||
214 | writel(temp_reg, msp->registers + MSP_GCR); | ||
215 | |||
216 | return 0; | ||
217 | } | ||
218 | |||
219 | static int setup_bitclk(struct ux500_msp *msp, struct ux500_msp_config *config) | ||
220 | { | ||
221 | u32 reg_val_GCR; | ||
222 | u32 frame_per = 0; | ||
223 | u32 sck_div = 0; | ||
224 | u32 frame_width = 0; | ||
225 | u32 temp_reg = 0; | ||
226 | struct msp_protdesc *protdesc = NULL; | ||
227 | |||
228 | reg_val_GCR = readl(msp->registers + MSP_GCR); | ||
229 | writel(reg_val_GCR & ~SRG_ENABLE, msp->registers + MSP_GCR); | ||
230 | |||
231 | if (config->default_protdesc) | ||
232 | protdesc = | ||
233 | (struct msp_protdesc *)&prot_descs[config->protocol]; | ||
234 | else | ||
235 | protdesc = (struct msp_protdesc *)&config->protdesc; | ||
236 | |||
237 | switch (config->protocol) { | ||
238 | case MSP_PCM_PROTOCOL: | ||
239 | case MSP_PCM_COMPAND_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 | case MSP_I2S_PROTOCOL: | ||
246 | frame_width = protdesc->frame_width; | ||
247 | sck_div = config->f_inputclk / (config->frame_freq * | ||
248 | (protdesc->clocks_per_frame)); | ||
249 | frame_per = protdesc->frame_period; | ||
250 | break; | ||
251 | default: | ||
252 | dev_err(msp->dev, "%s: ERROR: Unknown protocol (%d)!\n", | ||
253 | __func__, | ||
254 | config->protocol); | ||
255 | return -EINVAL; | ||
256 | } | ||
257 | |||
258 | temp_reg = (sck_div - 1) & SCK_DIV_MASK; | ||
259 | temp_reg |= FRAME_WIDTH_BITS(frame_width); | ||
260 | temp_reg |= FRAME_PERIOD_BITS(frame_per); | ||
261 | writel(temp_reg, msp->registers + MSP_SRG); | ||
262 | |||
263 | msp->f_bitclk = (config->f_inputclk)/(sck_div + 1); | ||
264 | |||
265 | /* Enable bit-clock */ | ||
266 | udelay(100); | ||
267 | reg_val_GCR = readl(msp->registers + MSP_GCR); | ||
268 | writel(reg_val_GCR | SRG_ENABLE, msp->registers + MSP_GCR); | ||
269 | udelay(100); | ||
270 | |||
271 | return 0; | ||
272 | } | ||
273 | |||
274 | static int configure_multichannel(struct ux500_msp *msp, | ||
275 | struct ux500_msp_config *config) | ||
276 | { | ||
277 | struct msp_protdesc *protdesc; | ||
278 | struct msp_multichannel_config *mcfg; | ||
279 | u32 reg_val_MCR; | ||
280 | |||
281 | if (config->default_protdesc == 1) { | ||
282 | if (config->protocol >= MSP_INVALID_PROTOCOL) { | ||
283 | dev_err(msp->dev, | ||
284 | "%s: ERROR: Invalid protocol (%d)!\n", | ||
285 | __func__, config->protocol); | ||
286 | return -EINVAL; | ||
287 | } | ||
288 | protdesc = (struct msp_protdesc *) | ||
289 | &prot_descs[config->protocol]; | ||
290 | } else { | ||
291 | protdesc = (struct msp_protdesc *)&config->protdesc; | ||
292 | } | ||
293 | |||
294 | mcfg = &config->multichannel_config; | ||
295 | if (mcfg->tx_multichannel_enable) { | ||
296 | if (protdesc->tx_phase_mode == MSP_SINGLE_PHASE) { | ||
297 | reg_val_MCR = readl(msp->registers + MSP_MCR); | ||
298 | writel(reg_val_MCR | (mcfg->tx_multichannel_enable ? | ||
299 | 1 << TMCEN_BIT : 0), | ||
300 | msp->registers + MSP_MCR); | ||
301 | writel(mcfg->tx_channel_0_enable, | ||
302 | msp->registers + MSP_TCE0); | ||
303 | writel(mcfg->tx_channel_1_enable, | ||
304 | msp->registers + MSP_TCE1); | ||
305 | writel(mcfg->tx_channel_2_enable, | ||
306 | msp->registers + MSP_TCE2); | ||
307 | writel(mcfg->tx_channel_3_enable, | ||
308 | msp->registers + MSP_TCE3); | ||
309 | } else { | ||
310 | dev_err(msp->dev, | ||
311 | "%s: ERROR: Only single-phase supported (TX-mode: %d)!\n", | ||
312 | __func__, protdesc->tx_phase_mode); | ||
313 | return -EINVAL; | ||
314 | } | ||
315 | } | ||
316 | if (mcfg->rx_multichannel_enable) { | ||
317 | if (protdesc->rx_phase_mode == MSP_SINGLE_PHASE) { | ||
318 | reg_val_MCR = readl(msp->registers + MSP_MCR); | ||
319 | writel(reg_val_MCR | (mcfg->rx_multichannel_enable ? | ||
320 | 1 << RMCEN_BIT : 0), | ||
321 | msp->registers + MSP_MCR); | ||
322 | writel(mcfg->rx_channel_0_enable, | ||
323 | msp->registers + MSP_RCE0); | ||
324 | writel(mcfg->rx_channel_1_enable, | ||
325 | msp->registers + MSP_RCE1); | ||
326 | writel(mcfg->rx_channel_2_enable, | ||
327 | msp->registers + MSP_RCE2); | ||
328 | writel(mcfg->rx_channel_3_enable, | ||
329 | msp->registers + MSP_RCE3); | ||
330 | } else { | ||
331 | dev_err(msp->dev, | ||
332 | "%s: ERROR: Only single-phase supported (RX-mode: %d)!\n", | ||
333 | __func__, protdesc->rx_phase_mode); | ||
334 | return -EINVAL; | ||
335 | } | ||
336 | if (mcfg->rx_comparison_enable_mode) { | ||
337 | reg_val_MCR = readl(msp->registers + MSP_MCR); | ||
338 | writel(reg_val_MCR | | ||
339 | (mcfg->rx_comparison_enable_mode << RCMPM_BIT), | ||
340 | msp->registers + MSP_MCR); | ||
341 | |||
342 | writel(mcfg->comparison_mask, | ||
343 | msp->registers + MSP_RCM); | ||
344 | writel(mcfg->comparison_value, | ||
345 | msp->registers + MSP_RCV); | ||
346 | |||
347 | } | ||
348 | } | ||
349 | |||
350 | return 0; | ||
351 | } | ||
352 | |||
353 | static int enable_msp(struct ux500_msp *msp, struct ux500_msp_config *config) | ||
354 | { | ||
355 | int status = 0; | ||
356 | u32 reg_val_DMACR, reg_val_GCR; | ||
357 | |||
358 | /* Check msp state whether in RUN or CONFIGURED Mode */ | ||
359 | if ((msp->msp_state == MSP_STATE_IDLE) && (msp->plat_init)) { | ||
360 | status = msp->plat_init(); | ||
361 | if (status) { | ||
362 | dev_err(msp->dev, "%s: ERROR: Failed to init MSP (%d)!\n", | ||
363 | __func__, status); | ||
364 | return status; | ||
365 | } | ||
366 | } | ||
367 | |||
368 | /* Configure msp with protocol dependent settings */ | ||
369 | configure_protocol(msp, config); | ||
370 | setup_bitclk(msp, config); | ||
371 | if (config->multichannel_configured == 1) { | ||
372 | status = configure_multichannel(msp, config); | ||
373 | if (status) | ||
374 | dev_warn(msp->dev, | ||
375 | "%s: WARN: configure_multichannel failed (%d)!\n", | ||
376 | __func__, status); | ||
377 | } | ||
378 | |||
379 | /* Make sure the correct DMA-directions are configured */ | ||
380 | if ((config->direction & MSP_DIR_RX) && (!msp->dma_cfg_rx)) { | ||
381 | dev_err(msp->dev, "%s: ERROR: MSP RX-mode is not configured!", | ||
382 | __func__); | ||
383 | return -EINVAL; | ||
384 | } | ||
385 | if ((config->direction == MSP_DIR_TX) && (!msp->dma_cfg_tx)) { | ||
386 | dev_err(msp->dev, "%s: ERROR: MSP TX-mode is not configured!", | ||
387 | __func__); | ||
388 | return -EINVAL; | ||
389 | } | ||
390 | |||
391 | reg_val_DMACR = readl(msp->registers + MSP_DMACR); | ||
392 | if (config->direction & MSP_DIR_RX) | ||
393 | reg_val_DMACR |= RX_DMA_ENABLE; | ||
394 | if (config->direction & MSP_DIR_TX) | ||
395 | reg_val_DMACR |= TX_DMA_ENABLE; | ||
396 | writel(reg_val_DMACR, msp->registers + MSP_DMACR); | ||
397 | |||
398 | writel(config->iodelay, msp->registers + MSP_IODLY); | ||
399 | |||
400 | /* Enable frame generation logic */ | ||
401 | reg_val_GCR = readl(msp->registers + MSP_GCR); | ||
402 | writel(reg_val_GCR | FRAME_GEN_ENABLE, msp->registers + MSP_GCR); | ||
403 | |||
404 | return status; | ||
405 | } | ||
406 | |||
407 | static void flush_fifo_rx(struct ux500_msp *msp) | ||
408 | { | ||
409 | u32 reg_val_DR, reg_val_GCR, reg_val_FLR; | ||
410 | u32 limit = 32; | ||
411 | |||
412 | reg_val_GCR = readl(msp->registers + MSP_GCR); | ||
413 | writel(reg_val_GCR | RX_ENABLE, msp->registers + MSP_GCR); | ||
414 | |||
415 | reg_val_FLR = readl(msp->registers + MSP_FLR); | ||
416 | while (!(reg_val_FLR & RX_FIFO_EMPTY) && limit--) { | ||
417 | reg_val_DR = readl(msp->registers + MSP_DR); | ||
418 | reg_val_FLR = readl(msp->registers + MSP_FLR); | ||
419 | } | ||
420 | |||
421 | writel(reg_val_GCR, msp->registers + MSP_GCR); | ||
422 | } | ||
423 | |||
424 | static void flush_fifo_tx(struct ux500_msp *msp) | ||
425 | { | ||
426 | u32 reg_val_TSTDR, reg_val_GCR, reg_val_FLR; | ||
427 | u32 limit = 32; | ||
428 | |||
429 | reg_val_GCR = readl(msp->registers + MSP_GCR); | ||
430 | writel(reg_val_GCR | TX_ENABLE, msp->registers + MSP_GCR); | ||
431 | writel(MSP_ITCR_ITEN | MSP_ITCR_TESTFIFO, msp->registers + MSP_ITCR); | ||
432 | |||
433 | reg_val_FLR = readl(msp->registers + MSP_FLR); | ||
434 | while (!(reg_val_FLR & TX_FIFO_EMPTY) && limit--) { | ||
435 | reg_val_TSTDR = readl(msp->registers + MSP_TSTDR); | ||
436 | reg_val_FLR = readl(msp->registers + MSP_FLR); | ||
437 | } | ||
438 | writel(0x0, msp->registers + MSP_ITCR); | ||
439 | writel(reg_val_GCR, msp->registers + MSP_GCR); | ||
440 | } | ||
441 | |||
442 | int ux500_msp_i2s_open(struct ux500_msp *msp, | ||
443 | struct ux500_msp_config *config) | ||
444 | { | ||
445 | u32 old_reg, new_reg, mask; | ||
446 | int res; | ||
447 | unsigned int tx_sel, rx_sel, tx_busy, rx_busy; | ||
448 | |||
449 | if (in_interrupt()) { | ||
450 | dev_err(msp->dev, | ||
451 | "%s: ERROR: Open called in interrupt context!\n", | ||
452 | __func__); | ||
453 | return -1; | ||
454 | } | ||
455 | |||
456 | tx_sel = (config->direction & MSP_DIR_TX) > 0; | ||
457 | rx_sel = (config->direction & MSP_DIR_RX) > 0; | ||
458 | if (!tx_sel && !rx_sel) { | ||
459 | dev_err(msp->dev, "%s: Error: No direction selected!\n", | ||
460 | __func__); | ||
461 | return -EINVAL; | ||
462 | } | ||
463 | |||
464 | tx_busy = (msp->dir_busy & MSP_DIR_TX) > 0; | ||
465 | rx_busy = (msp->dir_busy & MSP_DIR_RX) > 0; | ||
466 | if (tx_busy && tx_sel) { | ||
467 | dev_err(msp->dev, "%s: Error: TX is in use!\n", __func__); | ||
468 | return -EBUSY; | ||
469 | } | ||
470 | if (rx_busy && rx_sel) { | ||
471 | dev_err(msp->dev, "%s: Error: RX is in use!\n", __func__); | ||
472 | return -EBUSY; | ||
473 | } | ||
474 | |||
475 | msp->dir_busy |= (tx_sel ? MSP_DIR_TX : 0) | (rx_sel ? MSP_DIR_RX : 0); | ||
476 | |||
477 | /* First do the global config register */ | ||
478 | mask = RX_CLK_SEL_MASK | TX_CLK_SEL_MASK | RX_FSYNC_MASK | | ||
479 | TX_FSYNC_MASK | RX_SYNC_SEL_MASK | TX_SYNC_SEL_MASK | | ||
480 | RX_FIFO_ENABLE_MASK | TX_FIFO_ENABLE_MASK | SRG_CLK_SEL_MASK | | ||
481 | LOOPBACK_MASK | TX_EXTRA_DELAY_MASK; | ||
482 | |||
483 | new_reg = (config->tx_clk_sel | config->rx_clk_sel | | ||
484 | config->rx_fsync_pol | config->tx_fsync_pol | | ||
485 | config->rx_fsync_sel | config->tx_fsync_sel | | ||
486 | config->rx_fifo_config | config->tx_fifo_config | | ||
487 | config->srg_clk_sel | config->loopback_enable | | ||
488 | config->tx_data_enable); | ||
489 | |||
490 | old_reg = readl(msp->registers + MSP_GCR); | ||
491 | old_reg &= ~mask; | ||
492 | new_reg |= old_reg; | ||
493 | writel(new_reg, msp->registers + MSP_GCR); | ||
494 | |||
495 | res = enable_msp(msp, config); | ||
496 | if (res < 0) { | ||
497 | dev_err(msp->dev, "%s: ERROR: enable_msp failed (%d)!\n", | ||
498 | __func__, res); | ||
499 | return -EBUSY; | ||
500 | } | ||
501 | if (config->loopback_enable & 0x80) | ||
502 | msp->loopback_enable = 1; | ||
503 | |||
504 | /* Flush FIFOs */ | ||
505 | flush_fifo_tx(msp); | ||
506 | flush_fifo_rx(msp); | ||
507 | |||
508 | msp->msp_state = MSP_STATE_CONFIGURED; | ||
509 | return 0; | ||
510 | } | ||
511 | |||
512 | static void disable_msp_rx(struct ux500_msp *msp) | ||
513 | { | ||
514 | u32 reg_val_GCR, reg_val_DMACR, reg_val_IMSC; | ||
515 | |||
516 | reg_val_GCR = readl(msp->registers + MSP_GCR); | ||
517 | writel(reg_val_GCR & ~RX_ENABLE, msp->registers + MSP_GCR); | ||
518 | reg_val_DMACR = readl(msp->registers + MSP_DMACR); | ||
519 | writel(reg_val_DMACR & ~RX_DMA_ENABLE, msp->registers + MSP_DMACR); | ||
520 | reg_val_IMSC = readl(msp->registers + MSP_IMSC); | ||
521 | writel(reg_val_IMSC & | ||
522 | ~(RX_SERVICE_INT | RX_OVERRUN_ERROR_INT), | ||
523 | msp->registers + MSP_IMSC); | ||
524 | |||
525 | msp->dir_busy &= ~MSP_DIR_RX; | ||
526 | } | ||
527 | |||
528 | static void disable_msp_tx(struct ux500_msp *msp) | ||
529 | { | ||
530 | u32 reg_val_GCR, reg_val_DMACR, reg_val_IMSC; | ||
531 | |||
532 | reg_val_GCR = readl(msp->registers + MSP_GCR); | ||
533 | writel(reg_val_GCR & ~TX_ENABLE, msp->registers + MSP_GCR); | ||
534 | reg_val_DMACR = readl(msp->registers + MSP_DMACR); | ||
535 | writel(reg_val_DMACR & ~TX_DMA_ENABLE, msp->registers + MSP_DMACR); | ||
536 | reg_val_IMSC = readl(msp->registers + MSP_IMSC); | ||
537 | writel(reg_val_IMSC & | ||
538 | ~(TX_SERVICE_INT | TX_UNDERRUN_ERR_INT), | ||
539 | msp->registers + MSP_IMSC); | ||
540 | |||
541 | msp->dir_busy &= ~MSP_DIR_TX; | ||
542 | } | ||
543 | |||
544 | static int disable_msp(struct ux500_msp *msp, unsigned int dir) | ||
545 | { | ||
546 | u32 reg_val_GCR; | ||
547 | int status = 0; | ||
548 | unsigned int disable_tx, disable_rx; | ||
549 | |||
550 | reg_val_GCR = readl(msp->registers + MSP_GCR); | ||
551 | disable_tx = dir & MSP_DIR_TX; | ||
552 | disable_rx = dir & MSP_DIR_TX; | ||
553 | if (disable_tx && disable_rx) { | ||
554 | reg_val_GCR = readl(msp->registers + MSP_GCR); | ||
555 | writel(reg_val_GCR | LOOPBACK_MASK, | ||
556 | msp->registers + MSP_GCR); | ||
557 | |||
558 | /* Flush TX-FIFO */ | ||
559 | flush_fifo_tx(msp); | ||
560 | |||
561 | /* Disable TX-channel */ | ||
562 | writel((readl(msp->registers + MSP_GCR) & | ||
563 | (~TX_ENABLE)), msp->registers + MSP_GCR); | ||
564 | |||
565 | /* Flush RX-FIFO */ | ||
566 | flush_fifo_rx(msp); | ||
567 | |||
568 | /* Disable Loopback and Receive channel */ | ||
569 | writel((readl(msp->registers + MSP_GCR) & | ||
570 | (~(RX_ENABLE | LOOPBACK_MASK))), | ||
571 | msp->registers + MSP_GCR); | ||
572 | |||
573 | disable_msp_tx(msp); | ||
574 | disable_msp_rx(msp); | ||
575 | } else if (disable_tx) | ||
576 | disable_msp_tx(msp); | ||
577 | else if (disable_rx) | ||
578 | disable_msp_rx(msp); | ||
579 | |||
580 | return status; | ||
581 | } | ||
582 | |||
583 | int ux500_msp_i2s_trigger(struct ux500_msp *msp, int cmd, int direction) | ||
584 | { | ||
585 | u32 reg_val_GCR, enable_bit; | ||
586 | |||
587 | if (msp->msp_state == MSP_STATE_IDLE) { | ||
588 | dev_err(msp->dev, "%s: ERROR: MSP is not configured!\n", | ||
589 | __func__); | ||
590 | return -EINVAL; | ||
591 | } | ||
592 | |||
593 | switch (cmd) { | ||
594 | case SNDRV_PCM_TRIGGER_START: | ||
595 | case SNDRV_PCM_TRIGGER_RESUME: | ||
596 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | ||
597 | if (direction == SNDRV_PCM_STREAM_PLAYBACK) | ||
598 | enable_bit = TX_ENABLE; | ||
599 | else | ||
600 | enable_bit = RX_ENABLE; | ||
601 | reg_val_GCR = readl(msp->registers + MSP_GCR); | ||
602 | writel(reg_val_GCR | enable_bit, msp->registers + MSP_GCR); | ||
603 | break; | ||
604 | |||
605 | case SNDRV_PCM_TRIGGER_STOP: | ||
606 | case SNDRV_PCM_TRIGGER_SUSPEND: | ||
607 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | ||
608 | if (direction == SNDRV_PCM_STREAM_PLAYBACK) | ||
609 | disable_msp_tx(msp); | ||
610 | else | ||
611 | disable_msp_rx(msp); | ||
612 | break; | ||
613 | default: | ||
614 | return -EINVAL; | ||
615 | break; | ||
616 | } | ||
617 | |||
618 | return 0; | ||
619 | } | ||
620 | |||
621 | int ux500_msp_i2s_close(struct ux500_msp *msp, unsigned int dir) | ||
622 | { | ||
623 | int status = 0; | ||
624 | |||
625 | dev_dbg(msp->dev, "%s: Enter (dir = 0x%01x).\n", __func__, dir); | ||
626 | |||
627 | status = disable_msp(msp, dir); | ||
628 | if (msp->dir_busy == 0) { | ||
629 | /* disable sample rate and frame generators */ | ||
630 | msp->msp_state = MSP_STATE_IDLE; | ||
631 | writel((readl(msp->registers + MSP_GCR) & | ||
632 | (~(FRAME_GEN_ENABLE | SRG_ENABLE))), | ||
633 | msp->registers + MSP_GCR); | ||
634 | if (msp->plat_exit) | ||
635 | status = msp->plat_exit(); | ||
636 | if (status) | ||
637 | dev_warn(msp->dev, | ||
638 | "%s: WARN: ux500_msp_i2s_exit failed (%d)!\n", | ||
639 | __func__, status); | ||
640 | writel(0, msp->registers + MSP_GCR); | ||
641 | writel(0, msp->registers + MSP_TCF); | ||
642 | writel(0, msp->registers + MSP_RCF); | ||
643 | writel(0, msp->registers + MSP_DMACR); | ||
644 | writel(0, msp->registers + MSP_SRG); | ||
645 | writel(0, msp->registers + MSP_MCR); | ||
646 | writel(0, msp->registers + MSP_RCM); | ||
647 | writel(0, msp->registers + MSP_RCV); | ||
648 | writel(0, msp->registers + MSP_TCE0); | ||
649 | writel(0, msp->registers + MSP_TCE1); | ||
650 | writel(0, msp->registers + MSP_TCE2); | ||
651 | writel(0, msp->registers + MSP_TCE3); | ||
652 | writel(0, msp->registers + MSP_RCE0); | ||
653 | writel(0, msp->registers + MSP_RCE1); | ||
654 | writel(0, msp->registers + MSP_RCE2); | ||
655 | writel(0, msp->registers + MSP_RCE3); | ||
656 | } | ||
657 | |||
658 | return status; | ||
659 | |||
660 | } | ||
661 | |||
662 | int ux500_msp_i2s_init_msp(struct platform_device *pdev, | ||
663 | struct ux500_msp **msp_p, | ||
664 | struct msp_i2s_platform_data *platform_data) | ||
665 | { | ||
666 | int ret = 0; | ||
667 | struct resource *res = NULL; | ||
668 | struct i2s_controller *i2s_cont; | ||
669 | struct ux500_msp *msp; | ||
670 | |||
671 | dev_dbg(&pdev->dev, "%s: Enter (name: %s, id: %d).\n", __func__, | ||
672 | pdev->name, platform_data->id); | ||
673 | |||
674 | *msp_p = devm_kzalloc(&pdev->dev, sizeof(struct ux500_msp), GFP_KERNEL); | ||
675 | msp = *msp_p; | ||
676 | |||
677 | msp->id = platform_data->id; | ||
678 | msp->dev = &pdev->dev; | ||
679 | msp->plat_init = platform_data->msp_i2s_init; | ||
680 | msp->plat_exit = platform_data->msp_i2s_exit; | ||
681 | msp->dma_cfg_rx = platform_data->msp_i2s_dma_rx; | ||
682 | msp->dma_cfg_tx = platform_data->msp_i2s_dma_tx; | ||
683 | |||
684 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
685 | if (res == NULL) { | ||
686 | dev_err(&pdev->dev, "%s: ERROR: Unable to get resource!\n", | ||
687 | __func__); | ||
688 | ret = -ENOMEM; | ||
689 | goto err_res; | ||
690 | } | ||
691 | |||
692 | msp->registers = ioremap(res->start, (res->end - res->start + 1)); | ||
693 | if (msp->registers == NULL) { | ||
694 | dev_err(&pdev->dev, "%s: ERROR: ioremap failed!\n", __func__); | ||
695 | ret = -ENOMEM; | ||
696 | goto err_res; | ||
697 | } | ||
698 | |||
699 | msp->msp_state = MSP_STATE_IDLE; | ||
700 | msp->loopback_enable = 0; | ||
701 | |||
702 | /* I2S-controller is allocated and added in I2S controller class. */ | ||
703 | i2s_cont = devm_kzalloc(&pdev->dev, sizeof(*i2s_cont), GFP_KERNEL); | ||
704 | if (!i2s_cont) { | ||
705 | dev_err(&pdev->dev, | ||
706 | "%s: ERROR: Failed to allocate I2S-controller!\n", | ||
707 | __func__); | ||
708 | goto err_i2s_cont; | ||
709 | } | ||
710 | i2s_cont->dev.parent = &pdev->dev; | ||
711 | i2s_cont->data = (void *)msp; | ||
712 | i2s_cont->id = (s16)msp->id; | ||
713 | snprintf(i2s_cont->name, sizeof(i2s_cont->name), "ux500-msp-i2s.%04x", | ||
714 | msp->id); | ||
715 | dev_dbg(&pdev->dev, "I2S device-name: '%s'\n", i2s_cont->name); | ||
716 | msp->i2s_cont = i2s_cont; | ||
717 | |||
718 | return 0; | ||
719 | |||
720 | err_i2s_cont: | ||
721 | iounmap(msp->registers); | ||
722 | |||
723 | err_res: | ||
724 | devm_kfree(&pdev->dev, msp); | ||
725 | |||
726 | return ret; | ||
727 | } | ||
728 | |||
729 | void ux500_msp_i2s_cleanup_msp(struct platform_device *pdev, | ||
730 | struct ux500_msp *msp) | ||
731 | { | ||
732 | dev_dbg(msp->dev, "%s: Enter (id = %d).\n", __func__, msp->id); | ||
733 | |||
734 | device_unregister(&msp->i2s_cont->dev); | ||
735 | devm_kfree(&pdev->dev, msp->i2s_cont); | ||
736 | |||
737 | iounmap(msp->registers); | ||
738 | |||
739 | devm_kfree(&pdev->dev, msp); | ||
740 | } | ||
741 | |||
742 | MODULE_LICENSE("GPLv2"); | ||
diff --git a/sound/soc/ux500/ux500_msp_i2s.h b/sound/soc/ux500/ux500_msp_i2s.h new file mode 100644 index 000000000000..7f71b4a0d4bc --- /dev/null +++ b/sound/soc/ux500/ux500_msp_i2s.h | |||
@@ -0,0 +1,553 @@ | |||
1 | /* | ||
2 | * Copyright (C) ST-Ericsson SA 2012 | ||
3 | * | ||
4 | * Author: Ola Lilja <ola.o.lilja@stericsson.com>, | ||
5 | * for ST-Ericsson. | ||
6 | * | ||
7 | * License terms: | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License version 2 as published | ||
11 | * by the Free Software Foundation. | ||
12 | */ | ||
13 | |||
14 | |||
15 | #ifndef UX500_MSP_I2S_H | ||
16 | #define UX500_MSP_I2S_H | ||
17 | |||
18 | #include <linux/platform_device.h> | ||
19 | |||
20 | #include <mach/board-mop500-msp.h> | ||
21 | |||
22 | #define MSP_INPUT_FREQ_APB 48000000 | ||
23 | |||
24 | /*** Stereo mode. Used for APB data accesses as 16 bits accesses (mono), | ||
25 | * 32 bits accesses (stereo). | ||
26 | ***/ | ||
27 | enum msp_stereo_mode { | ||
28 | MSP_MONO, | ||
29 | MSP_STEREO | ||
30 | }; | ||
31 | |||
32 | /* Direction (Transmit/Receive mode) */ | ||
33 | enum msp_direction { | ||
34 | MSP_TX = 1, | ||
35 | MSP_RX = 2 | ||
36 | }; | ||
37 | |||
38 | /* Transmit and receive configuration register */ | ||
39 | #define MSP_BIG_ENDIAN 0x00000000 | ||
40 | #define MSP_LITTLE_ENDIAN 0x00001000 | ||
41 | #define MSP_UNEXPECTED_FS_ABORT 0x00000000 | ||
42 | #define MSP_UNEXPECTED_FS_IGNORE 0x00008000 | ||
43 | #define MSP_NON_MODE_BIT_MASK 0x00009000 | ||
44 | |||
45 | /* Global configuration register */ | ||
46 | #define RX_ENABLE 0x00000001 | ||
47 | #define RX_FIFO_ENABLE 0x00000002 | ||
48 | #define RX_SYNC_SRG 0x00000010 | ||
49 | #define RX_CLK_POL_RISING 0x00000020 | ||
50 | #define RX_CLK_SEL_SRG 0x00000040 | ||
51 | #define TX_ENABLE 0x00000100 | ||
52 | #define TX_FIFO_ENABLE 0x00000200 | ||
53 | #define TX_SYNC_SRG_PROG 0x00001800 | ||
54 | #define TX_SYNC_SRG_AUTO 0x00001000 | ||
55 | #define TX_CLK_POL_RISING 0x00002000 | ||
56 | #define TX_CLK_SEL_SRG 0x00004000 | ||
57 | #define TX_EXTRA_DELAY_ENABLE 0x00008000 | ||
58 | #define SRG_ENABLE 0x00010000 | ||
59 | #define FRAME_GEN_ENABLE 0x00100000 | ||
60 | #define SRG_CLK_SEL_APB 0x00000000 | ||
61 | #define RX_FIFO_SYNC_HI 0x00000000 | ||
62 | #define TX_FIFO_SYNC_HI 0x00000000 | ||
63 | #define SPI_CLK_MODE_NORMAL 0x00000000 | ||
64 | |||
65 | #define MSP_FRAME_SIZE_AUTO -1 | ||
66 | |||
67 | #define MSP_DR 0x00 | ||
68 | #define MSP_GCR 0x04 | ||
69 | #define MSP_TCF 0x08 | ||
70 | #define MSP_RCF 0x0c | ||
71 | #define MSP_SRG 0x10 | ||
72 | #define MSP_FLR 0x14 | ||
73 | #define MSP_DMACR 0x18 | ||
74 | |||
75 | #define MSP_IMSC 0x20 | ||
76 | #define MSP_RIS 0x24 | ||
77 | #define MSP_MIS 0x28 | ||
78 | #define MSP_ICR 0x2c | ||
79 | #define MSP_MCR 0x30 | ||
80 | #define MSP_RCV 0x34 | ||
81 | #define MSP_RCM 0x38 | ||
82 | |||
83 | #define MSP_TCE0 0x40 | ||
84 | #define MSP_TCE1 0x44 | ||
85 | #define MSP_TCE2 0x48 | ||
86 | #define MSP_TCE3 0x4c | ||
87 | |||
88 | #define MSP_RCE0 0x60 | ||
89 | #define MSP_RCE1 0x64 | ||
90 | #define MSP_RCE2 0x68 | ||
91 | #define MSP_RCE3 0x6c | ||
92 | #define MSP_IODLY 0x70 | ||
93 | |||
94 | #define MSP_ITCR 0x80 | ||
95 | #define MSP_ITIP 0x84 | ||
96 | #define MSP_ITOP 0x88 | ||
97 | #define MSP_TSTDR 0x8c | ||
98 | |||
99 | #define MSP_PID0 0xfe0 | ||
100 | #define MSP_PID1 0xfe4 | ||
101 | #define MSP_PID2 0xfe8 | ||
102 | #define MSP_PID3 0xfec | ||
103 | |||
104 | #define MSP_CID0 0xff0 | ||
105 | #define MSP_CID1 0xff4 | ||
106 | #define MSP_CID2 0xff8 | ||
107 | #define MSP_CID3 0xffc | ||
108 | |||
109 | /* Protocol dependant parameters list */ | ||
110 | #define RX_ENABLE_MASK BIT(0) | ||
111 | #define RX_FIFO_ENABLE_MASK BIT(1) | ||
112 | #define RX_FSYNC_MASK BIT(2) | ||
113 | #define DIRECT_COMPANDING_MASK BIT(3) | ||
114 | #define RX_SYNC_SEL_MASK BIT(4) | ||
115 | #define RX_CLK_POL_MASK BIT(5) | ||
116 | #define RX_CLK_SEL_MASK BIT(6) | ||
117 | #define LOOPBACK_MASK BIT(7) | ||
118 | #define TX_ENABLE_MASK BIT(8) | ||
119 | #define TX_FIFO_ENABLE_MASK BIT(9) | ||
120 | #define TX_FSYNC_MASK BIT(10) | ||
121 | #define TX_MSP_TDR_TSR BIT(11) | ||
122 | #define TX_SYNC_SEL_MASK (BIT(12) | BIT(11)) | ||
123 | #define TX_CLK_POL_MASK BIT(13) | ||
124 | #define TX_CLK_SEL_MASK BIT(14) | ||
125 | #define TX_EXTRA_DELAY_MASK BIT(15) | ||
126 | #define SRG_ENABLE_MASK BIT(16) | ||
127 | #define SRG_CLK_POL_MASK BIT(17) | ||
128 | #define SRG_CLK_SEL_MASK (BIT(19) | BIT(18)) | ||
129 | #define FRAME_GEN_EN_MASK BIT(20) | ||
130 | #define SPI_CLK_MODE_MASK (BIT(22) | BIT(21)) | ||
131 | #define SPI_BURST_MODE_MASK BIT(23) | ||
132 | |||
133 | #define RXEN_SHIFT 0 | ||
134 | #define RFFEN_SHIFT 1 | ||
135 | #define RFSPOL_SHIFT 2 | ||
136 | #define DCM_SHIFT 3 | ||
137 | #define RFSSEL_SHIFT 4 | ||
138 | #define RCKPOL_SHIFT 5 | ||
139 | #define RCKSEL_SHIFT 6 | ||
140 | #define LBM_SHIFT 7 | ||
141 | #define TXEN_SHIFT 8 | ||
142 | #define TFFEN_SHIFT 9 | ||
143 | #define TFSPOL_SHIFT 10 | ||
144 | #define TFSSEL_SHIFT 11 | ||
145 | #define TCKPOL_SHIFT 13 | ||
146 | #define TCKSEL_SHIFT 14 | ||
147 | #define TXDDL_SHIFT 15 | ||
148 | #define SGEN_SHIFT 16 | ||
149 | #define SCKPOL_SHIFT 17 | ||
150 | #define SCKSEL_SHIFT 18 | ||
151 | #define FGEN_SHIFT 20 | ||
152 | #define SPICKM_SHIFT 21 | ||
153 | #define TBSWAP_SHIFT 28 | ||
154 | |||
155 | #define RCKPOL_MASK BIT(0) | ||
156 | #define TCKPOL_MASK BIT(0) | ||
157 | #define SPICKM_MASK (BIT(1) | BIT(0)) | ||
158 | #define MSP_RX_CLKPOL_BIT(n) ((n & RCKPOL_MASK) << RCKPOL_SHIFT) | ||
159 | #define MSP_TX_CLKPOL_BIT(n) ((n & TCKPOL_MASK) << TCKPOL_SHIFT) | ||
160 | |||
161 | #define P1ELEN_SHIFT 0 | ||
162 | #define P1FLEN_SHIFT 3 | ||
163 | #define DTYP_SHIFT 10 | ||
164 | #define ENDN_SHIFT 12 | ||
165 | #define DDLY_SHIFT 13 | ||
166 | #define FSIG_SHIFT 15 | ||
167 | #define P2ELEN_SHIFT 16 | ||
168 | #define P2FLEN_SHIFT 19 | ||
169 | #define P2SM_SHIFT 26 | ||
170 | #define P2EN_SHIFT 27 | ||
171 | #define FSYNC_SHIFT 15 | ||
172 | |||
173 | #define P1ELEN_MASK 0x00000007 | ||
174 | #define P2ELEN_MASK 0x00070000 | ||
175 | #define P1FLEN_MASK 0x00000378 | ||
176 | #define P2FLEN_MASK 0x03780000 | ||
177 | #define DDLY_MASK 0x00003000 | ||
178 | #define DTYP_MASK 0x00000600 | ||
179 | #define P2SM_MASK 0x04000000 | ||
180 | #define P2EN_MASK 0x08000000 | ||
181 | #define ENDN_MASK 0x00001000 | ||
182 | #define TFSPOL_MASK 0x00000400 | ||
183 | #define TBSWAP_MASK 0x30000000 | ||
184 | #define COMPANDING_MODE_MASK 0x00000c00 | ||
185 | #define FSYNC_MASK 0x00008000 | ||
186 | |||
187 | #define MSP_P1_ELEM_LEN_BITS(n) (n & P1ELEN_MASK) | ||
188 | #define MSP_P2_ELEM_LEN_BITS(n) (((n) << P2ELEN_SHIFT) & P2ELEN_MASK) | ||
189 | #define MSP_P1_FRAME_LEN_BITS(n) (((n) << P1FLEN_SHIFT) & P1FLEN_MASK) | ||
190 | #define MSP_P2_FRAME_LEN_BITS(n) (((n) << P2FLEN_SHIFT) & P2FLEN_MASK) | ||
191 | #define MSP_DATA_DELAY_BITS(n) (((n) << DDLY_SHIFT) & DDLY_MASK) | ||
192 | #define MSP_DATA_TYPE_BITS(n) (((n) << DTYP_SHIFT) & DTYP_MASK) | ||
193 | #define MSP_P2_START_MODE_BIT(n) ((n << P2SM_SHIFT) & P2SM_MASK) | ||
194 | #define MSP_P2_ENABLE_BIT(n) ((n << P2EN_SHIFT) & P2EN_MASK) | ||
195 | #define MSP_SET_ENDIANNES_BIT(n) ((n << ENDN_SHIFT) & ENDN_MASK) | ||
196 | #define MSP_FSYNC_POL(n) ((n << TFSPOL_SHIFT) & TFSPOL_MASK) | ||
197 | #define MSP_DATA_WORD_SWAP(n) ((n << TBSWAP_SHIFT) & TBSWAP_MASK) | ||
198 | #define MSP_SET_COMPANDING_MODE(n) ((n << DTYP_SHIFT) & \ | ||
199 | COMPANDING_MODE_MASK) | ||
200 | #define MSP_SET_FSYNC_IGNORE(n) ((n << FSYNC_SHIFT) & FSYNC_MASK) | ||
201 | |||
202 | /* Flag register */ | ||
203 | #define RX_BUSY BIT(0) | ||
204 | #define RX_FIFO_EMPTY BIT(1) | ||
205 | #define RX_FIFO_FULL BIT(2) | ||
206 | #define TX_BUSY BIT(3) | ||
207 | #define TX_FIFO_EMPTY BIT(4) | ||
208 | #define TX_FIFO_FULL BIT(5) | ||
209 | |||
210 | #define RBUSY_SHIFT 0 | ||
211 | #define RFE_SHIFT 1 | ||
212 | #define RFU_SHIFT 2 | ||
213 | #define TBUSY_SHIFT 3 | ||
214 | #define TFE_SHIFT 4 | ||
215 | #define TFU_SHIFT 5 | ||
216 | |||
217 | /* Multichannel control register */ | ||
218 | #define RMCEN_SHIFT 0 | ||
219 | #define RMCSF_SHIFT 1 | ||
220 | #define RCMPM_SHIFT 3 | ||
221 | #define TMCEN_SHIFT 5 | ||
222 | #define TNCSF_SHIFT 6 | ||
223 | |||
224 | /* Sample rate generator register */ | ||
225 | #define SCKDIV_SHIFT 0 | ||
226 | #define FRWID_SHIFT 10 | ||
227 | #define FRPER_SHIFT 16 | ||
228 | |||
229 | #define SCK_DIV_MASK 0x0000003FF | ||
230 | #define FRAME_WIDTH_BITS(n) (((n) << FRWID_SHIFT) & 0x0000FC00) | ||
231 | #define FRAME_PERIOD_BITS(n) (((n) << FRPER_SHIFT) & 0x1FFF0000) | ||
232 | |||
233 | /* DMA controller register */ | ||
234 | #define RX_DMA_ENABLE BIT(0) | ||
235 | #define TX_DMA_ENABLE BIT(1) | ||
236 | |||
237 | #define RDMAE_SHIFT 0 | ||
238 | #define TDMAE_SHIFT 1 | ||
239 | |||
240 | /* Interrupt Register */ | ||
241 | #define RX_SERVICE_INT BIT(0) | ||
242 | #define RX_OVERRUN_ERROR_INT BIT(1) | ||
243 | #define RX_FSYNC_ERR_INT BIT(2) | ||
244 | #define RX_FSYNC_INT BIT(3) | ||
245 | #define TX_SERVICE_INT BIT(4) | ||
246 | #define TX_UNDERRUN_ERR_INT BIT(5) | ||
247 | #define TX_FSYNC_ERR_INT BIT(6) | ||
248 | #define TX_FSYNC_INT BIT(7) | ||
249 | #define ALL_INT 0x000000ff | ||
250 | |||
251 | /* MSP test control register */ | ||
252 | #define MSP_ITCR_ITEN BIT(0) | ||
253 | #define MSP_ITCR_TESTFIFO BIT(1) | ||
254 | |||
255 | #define RMCEN_BIT 0 | ||
256 | #define RMCSF_BIT 1 | ||
257 | #define RCMPM_BIT 3 | ||
258 | #define TMCEN_BIT 5 | ||
259 | #define TNCSF_BIT 6 | ||
260 | |||
261 | /* Single or dual phase mode */ | ||
262 | enum msp_phase_mode { | ||
263 | MSP_SINGLE_PHASE, | ||
264 | MSP_DUAL_PHASE | ||
265 | }; | ||
266 | |||
267 | /* Frame length */ | ||
268 | enum msp_frame_length { | ||
269 | MSP_FRAME_LEN_1 = 0, | ||
270 | MSP_FRAME_LEN_2 = 1, | ||
271 | MSP_FRAME_LEN_4 = 3, | ||
272 | MSP_FRAME_LEN_8 = 7, | ||
273 | MSP_FRAME_LEN_12 = 11, | ||
274 | MSP_FRAME_LEN_16 = 15, | ||
275 | MSP_FRAME_LEN_20 = 19, | ||
276 | MSP_FRAME_LEN_32 = 31, | ||
277 | MSP_FRAME_LEN_48 = 47, | ||
278 | MSP_FRAME_LEN_64 = 63 | ||
279 | }; | ||
280 | |||
281 | /* Element length */ | ||
282 | enum msp_elem_length { | ||
283 | MSP_ELEM_LEN_8 = 0, | ||
284 | MSP_ELEM_LEN_10 = 1, | ||
285 | MSP_ELEM_LEN_12 = 2, | ||
286 | MSP_ELEM_LEN_14 = 3, | ||
287 | MSP_ELEM_LEN_16 = 4, | ||
288 | MSP_ELEM_LEN_20 = 5, | ||
289 | MSP_ELEM_LEN_24 = 6, | ||
290 | MSP_ELEM_LEN_32 = 7 | ||
291 | }; | ||
292 | |||
293 | enum msp_data_xfer_width { | ||
294 | MSP_DATA_TRANSFER_WIDTH_BYTE, | ||
295 | MSP_DATA_TRANSFER_WIDTH_HALFWORD, | ||
296 | MSP_DATA_TRANSFER_WIDTH_WORD | ||
297 | }; | ||
298 | |||
299 | enum msp_frame_sync { | ||
300 | MSP_FSYNC_UNIGNORE = 0, | ||
301 | MSP_FSYNC_IGNORE = 1, | ||
302 | }; | ||
303 | |||
304 | enum msp_phase2_start_mode { | ||
305 | MSP_PHASE2_START_MODE_IMEDIATE, | ||
306 | MSP_PHASE2_START_MODE_FSYNC | ||
307 | }; | ||
308 | |||
309 | enum msp_btf { | ||
310 | MSP_BTF_MS_BIT_FIRST = 0, | ||
311 | MSP_BTF_LS_BIT_FIRST = 1 | ||
312 | }; | ||
313 | |||
314 | enum msp_fsync_pol { | ||
315 | MSP_FSYNC_POL_ACT_HI = 0, | ||
316 | MSP_FSYNC_POL_ACT_LO = 1 | ||
317 | }; | ||
318 | |||
319 | /* Data delay (in bit clock cycles) */ | ||
320 | enum msp_delay { | ||
321 | MSP_DELAY_0 = 0, | ||
322 | MSP_DELAY_1 = 1, | ||
323 | MSP_DELAY_2 = 2, | ||
324 | MSP_DELAY_3 = 3 | ||
325 | }; | ||
326 | |||
327 | /* Configurations of clocks (transmit, receive or sample rate generator) */ | ||
328 | enum msp_edge { | ||
329 | MSP_FALLING_EDGE = 0, | ||
330 | MSP_RISING_EDGE = 1, | ||
331 | }; | ||
332 | |||
333 | enum msp_hws { | ||
334 | MSP_SWAP_NONE = 0, | ||
335 | MSP_SWAP_BYTE_PER_WORD = 1, | ||
336 | MSP_SWAP_BYTE_PER_HALF_WORD = 2, | ||
337 | MSP_SWAP_HALF_WORD_PER_WORD = 3 | ||
338 | }; | ||
339 | |||
340 | enum msp_compress_mode { | ||
341 | MSP_COMPRESS_MODE_LINEAR = 0, | ||
342 | MSP_COMPRESS_MODE_MU_LAW = 2, | ||
343 | MSP_COMPRESS_MODE_A_LAW = 3 | ||
344 | }; | ||
345 | |||
346 | enum msp_spi_burst_mode { | ||
347 | MSP_SPI_BURST_MODE_DISABLE = 0, | ||
348 | MSP_SPI_BURST_MODE_ENABLE = 1 | ||
349 | }; | ||
350 | |||
351 | enum msp_expand_mode { | ||
352 | MSP_EXPAND_MODE_LINEAR = 0, | ||
353 | MSP_EXPAND_MODE_LINEAR_SIGNED = 1, | ||
354 | MSP_EXPAND_MODE_MU_LAW = 2, | ||
355 | MSP_EXPAND_MODE_A_LAW = 3 | ||
356 | }; | ||
357 | |||
358 | #define MSP_FRAME_PERIOD_IN_MONO_MODE 256 | ||
359 | #define MSP_FRAME_PERIOD_IN_STEREO_MODE 32 | ||
360 | #define MSP_FRAME_WIDTH_IN_STEREO_MODE 16 | ||
361 | |||
362 | enum msp_protocol { | ||
363 | MSP_I2S_PROTOCOL, | ||
364 | MSP_PCM_PROTOCOL, | ||
365 | MSP_PCM_COMPAND_PROTOCOL, | ||
366 | MSP_INVALID_PROTOCOL | ||
367 | }; | ||
368 | |||
369 | /* | ||
370 | * No of registers to backup during | ||
371 | * suspend resume | ||
372 | */ | ||
373 | #define MAX_MSP_BACKUP_REGS 36 | ||
374 | |||
375 | enum enum_i2s_controller { | ||
376 | MSP_0_I2S_CONTROLLER = 0, | ||
377 | MSP_1_I2S_CONTROLLER, | ||
378 | MSP_2_I2S_CONTROLLER, | ||
379 | MSP_3_I2S_CONTROLLER, | ||
380 | }; | ||
381 | |||
382 | enum i2s_direction_t { | ||
383 | MSP_DIR_TX = 0x01, | ||
384 | MSP_DIR_RX = 0x02, | ||
385 | }; | ||
386 | |||
387 | enum msp_data_size { | ||
388 | MSP_DATA_BITS_DEFAULT = -1, | ||
389 | MSP_DATA_BITS_8 = 0x00, | ||
390 | MSP_DATA_BITS_10, | ||
391 | MSP_DATA_BITS_12, | ||
392 | MSP_DATA_BITS_14, | ||
393 | MSP_DATA_BITS_16, | ||
394 | MSP_DATA_BITS_20, | ||
395 | MSP_DATA_BITS_24, | ||
396 | MSP_DATA_BITS_32, | ||
397 | }; | ||
398 | |||
399 | enum msp_state { | ||
400 | MSP_STATE_IDLE = 0, | ||
401 | MSP_STATE_CONFIGURED = 1, | ||
402 | MSP_STATE_RUNNING = 2, | ||
403 | }; | ||
404 | |||
405 | enum msp_rx_comparison_enable_mode { | ||
406 | MSP_COMPARISON_DISABLED = 0, | ||
407 | MSP_COMPARISON_NONEQUAL_ENABLED = 2, | ||
408 | MSP_COMPARISON_EQUAL_ENABLED = 3 | ||
409 | }; | ||
410 | |||
411 | struct msp_multichannel_config { | ||
412 | bool rx_multichannel_enable; | ||
413 | bool tx_multichannel_enable; | ||
414 | enum msp_rx_comparison_enable_mode rx_comparison_enable_mode; | ||
415 | u8 padding; | ||
416 | u32 comparison_value; | ||
417 | u32 comparison_mask; | ||
418 | u32 rx_channel_0_enable; | ||
419 | u32 rx_channel_1_enable; | ||
420 | u32 rx_channel_2_enable; | ||
421 | u32 rx_channel_3_enable; | ||
422 | u32 tx_channel_0_enable; | ||
423 | u32 tx_channel_1_enable; | ||
424 | u32 tx_channel_2_enable; | ||
425 | u32 tx_channel_3_enable; | ||
426 | }; | ||
427 | |||
428 | struct msp_protdesc { | ||
429 | u32 rx_phase_mode; | ||
430 | u32 tx_phase_mode; | ||
431 | u32 rx_phase2_start_mode; | ||
432 | u32 tx_phase2_start_mode; | ||
433 | u32 rx_byte_order; | ||
434 | u32 tx_byte_order; | ||
435 | u32 rx_frame_len_1; | ||
436 | u32 rx_frame_len_2; | ||
437 | u32 tx_frame_len_1; | ||
438 | u32 tx_frame_len_2; | ||
439 | u32 rx_elem_len_1; | ||
440 | u32 rx_elem_len_2; | ||
441 | u32 tx_elem_len_1; | ||
442 | u32 tx_elem_len_2; | ||
443 | u32 rx_data_delay; | ||
444 | u32 tx_data_delay; | ||
445 | u32 rx_clk_pol; | ||
446 | u32 tx_clk_pol; | ||
447 | u32 rx_fsync_pol; | ||
448 | u32 tx_fsync_pol; | ||
449 | u32 rx_half_word_swap; | ||
450 | u32 tx_half_word_swap; | ||
451 | u32 compression_mode; | ||
452 | u32 expansion_mode; | ||
453 | u32 frame_sync_ignore; | ||
454 | u32 frame_period; | ||
455 | u32 frame_width; | ||
456 | u32 clocks_per_frame; | ||
457 | }; | ||
458 | |||
459 | struct i2s_message { | ||
460 | enum i2s_direction_t i2s_direction; | ||
461 | void *txdata; | ||
462 | void *rxdata; | ||
463 | size_t txbytes; | ||
464 | size_t rxbytes; | ||
465 | int dma_flag; | ||
466 | int tx_offset; | ||
467 | int rx_offset; | ||
468 | bool cyclic_dma; | ||
469 | dma_addr_t buf_addr; | ||
470 | size_t buf_len; | ||
471 | size_t period_len; | ||
472 | }; | ||
473 | |||
474 | struct i2s_controller { | ||
475 | struct module *owner; | ||
476 | unsigned int id; | ||
477 | unsigned int class; | ||
478 | const struct i2s_algorithm *algo; /* the algorithm to access the bus */ | ||
479 | void *data; | ||
480 | struct mutex bus_lock; | ||
481 | struct device dev; /* the controller device */ | ||
482 | char name[48]; | ||
483 | }; | ||
484 | |||
485 | struct ux500_msp_config { | ||
486 | unsigned int f_inputclk; | ||
487 | unsigned int rx_clk_sel; | ||
488 | unsigned int tx_clk_sel; | ||
489 | unsigned int srg_clk_sel; | ||
490 | unsigned int rx_fsync_pol; | ||
491 | unsigned int tx_fsync_pol; | ||
492 | unsigned int rx_fsync_sel; | ||
493 | unsigned int tx_fsync_sel; | ||
494 | unsigned int rx_fifo_config; | ||
495 | unsigned int tx_fifo_config; | ||
496 | unsigned int spi_clk_mode; | ||
497 | unsigned int spi_burst_mode; | ||
498 | unsigned int loopback_enable; | ||
499 | unsigned int tx_data_enable; | ||
500 | unsigned int default_protdesc; | ||
501 | struct msp_protdesc protdesc; | ||
502 | int multichannel_configured; | ||
503 | struct msp_multichannel_config multichannel_config; | ||
504 | unsigned int direction; | ||
505 | unsigned int protocol; | ||
506 | unsigned int frame_freq; | ||
507 | unsigned int frame_size; | ||
508 | enum msp_data_size data_size; | ||
509 | unsigned int def_elem_len; | ||
510 | unsigned int iodelay; | ||
511 | void (*handler) (void *data); | ||
512 | void *tx_callback_data; | ||
513 | void *rx_callback_data; | ||
514 | }; | ||
515 | |||
516 | struct ux500_msp { | ||
517 | enum enum_i2s_controller id; | ||
518 | void __iomem *registers; | ||
519 | struct device *dev; | ||
520 | struct i2s_controller *i2s_cont; | ||
521 | struct stedma40_chan_cfg *dma_cfg_rx; | ||
522 | struct stedma40_chan_cfg *dma_cfg_tx; | ||
523 | struct dma_chan *tx_pipeid; | ||
524 | struct dma_chan *rx_pipeid; | ||
525 | enum msp_state msp_state; | ||
526 | int (*transfer) (struct ux500_msp *msp, struct i2s_message *message); | ||
527 | int (*plat_init) (void); | ||
528 | int (*plat_exit) (void); | ||
529 | struct timer_list notify_timer; | ||
530 | int def_elem_len; | ||
531 | unsigned int dir_busy; | ||
532 | int loopback_enable; | ||
533 | u32 backup_regs[MAX_MSP_BACKUP_REGS]; | ||
534 | unsigned int f_bitclk; | ||
535 | }; | ||
536 | |||
537 | struct ux500_msp_dma_params { | ||
538 | unsigned int data_size; | ||
539 | struct stedma40_chan_cfg *dma_cfg; | ||
540 | }; | ||
541 | |||
542 | int ux500_msp_i2s_init_msp(struct platform_device *pdev, | ||
543 | struct ux500_msp **msp_p, | ||
544 | struct msp_i2s_platform_data *platform_data); | ||
545 | void ux500_msp_i2s_cleanup_msp(struct platform_device *pdev, | ||
546 | struct ux500_msp *msp); | ||
547 | int ux500_msp_i2s_open(struct ux500_msp *msp, struct ux500_msp_config *config); | ||
548 | int ux500_msp_i2s_close(struct ux500_msp *msp, | ||
549 | unsigned int dir); | ||
550 | int ux500_msp_i2s_trigger(struct ux500_msp *msp, int cmd, | ||
551 | int direction); | ||
552 | |||
553 | #endif | ||