diff options
author | Nicolin Chen <nicoleotsuka@gmail.com> | 2014-07-29 06:08:53 -0400 |
---|---|---|
committer | Mark Brown <broonie@linaro.org> | 2014-07-29 14:22:49 -0400 |
commit | 3117bb3109dc223e186302f5dc8ce9ed04adca90 (patch) | |
tree | 35f65b00d2361c37d158f31a8ae1707975aaf1cc /sound/soc | |
parent | 94b912e42829b25d97b6b1f2be66c6aa81ac125f (diff) |
ASoC: fsl_asrc: Add ASRC ASoC CPU DAI and platform drivers
The Asynchronous Sample Rate Converter (ASRC) converts the sampling rate of a
signal associated with an input clock into a signal associated with a different
output clock. The driver currently works as a Front End of DPCM with other Back
Ends DAI links such as ESAI<->CS42888 and SSI<->WM8962 and SAI. It converts the
original sample rate to a common rate supported by Back Ends for playback while
converts the common rate of Back Ends to a desired rate for capture. It has 3
pairs to support three different substreams within totally 10 channels.
Signed-off-by: Nicolin Chen <nicoleotsuka@gmail.com>
Reviewed-by: Varka Bhadram <varkabhadram@gmail.com>
Signed-off-by: Mark Brown <broonie@linaro.org>
Diffstat (limited to 'sound/soc')
-rw-r--r-- | sound/soc/fsl/Kconfig | 9 | ||||
-rw-r--r-- | sound/soc/fsl/Makefile | 2 | ||||
-rw-r--r-- | sound/soc/fsl/fsl_asrc.c | 992 | ||||
-rw-r--r-- | sound/soc/fsl/fsl_asrc.h | 461 | ||||
-rw-r--r-- | sound/soc/fsl/fsl_asrc_dma.c | 386 |
5 files changed, 1850 insertions, 0 deletions
diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index 37933629cbed..56fd32b3a881 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig | |||
@@ -2,6 +2,15 @@ menu "SoC Audio for Freescale CPUs" | |||
2 | 2 | ||
3 | comment "Common SoC Audio options for Freescale CPUs:" | 3 | comment "Common SoC Audio options for Freescale CPUs:" |
4 | 4 | ||
5 | config SND_SOC_FSL_ASRC | ||
6 | tristate "Asynchronous Sample Rate Converter (ASRC) module support" | ||
7 | select REGMAP_MMIO | ||
8 | help | ||
9 | Say Y if you want to add Asynchronous Sample Rate Converter (ASRC) | ||
10 | support for the Freescale CPUs. | ||
11 | This option is only useful for out-of-tree drivers since | ||
12 | in-tree drivers select it automatically. | ||
13 | |||
5 | config SND_SOC_FSL_SAI | 14 | config SND_SOC_FSL_SAI |
6 | tristate "Synchronous Audio Interface (SAI) module support" | 15 | tristate "Synchronous Audio Interface (SAI) module support" |
7 | select REGMAP_MMIO | 16 | select REGMAP_MMIO |
diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile index db254e358c18..9ff59267eac9 100644 --- a/sound/soc/fsl/Makefile +++ b/sound/soc/fsl/Makefile | |||
@@ -11,6 +11,7 @@ snd-soc-p1022-rdk-objs := p1022_rdk.o | |||
11 | obj-$(CONFIG_SND_SOC_P1022_RDK) += snd-soc-p1022-rdk.o | 11 | obj-$(CONFIG_SND_SOC_P1022_RDK) += snd-soc-p1022-rdk.o |
12 | 12 | ||
13 | # Freescale SSI/DMA/SAI/SPDIF Support | 13 | # Freescale SSI/DMA/SAI/SPDIF Support |
14 | snd-soc-fsl-asrc-objs := fsl_asrc.o fsl_asrc_dma.o | ||
14 | snd-soc-fsl-sai-objs := fsl_sai.o | 15 | snd-soc-fsl-sai-objs := fsl_sai.o |
15 | snd-soc-fsl-ssi-y := fsl_ssi.o | 16 | snd-soc-fsl-ssi-y := fsl_ssi.o |
16 | snd-soc-fsl-ssi-$(CONFIG_DEBUG_FS) += fsl_ssi_dbg.o | 17 | snd-soc-fsl-ssi-$(CONFIG_DEBUG_FS) += fsl_ssi_dbg.o |
@@ -18,6 +19,7 @@ snd-soc-fsl-spdif-objs := fsl_spdif.o | |||
18 | snd-soc-fsl-esai-objs := fsl_esai.o | 19 | snd-soc-fsl-esai-objs := fsl_esai.o |
19 | snd-soc-fsl-utils-objs := fsl_utils.o | 20 | snd-soc-fsl-utils-objs := fsl_utils.o |
20 | snd-soc-fsl-dma-objs := fsl_dma.o | 21 | snd-soc-fsl-dma-objs := fsl_dma.o |
22 | obj-$(CONFIG_SND_SOC_FSL_ASRC) += snd-soc-fsl-asrc.o | ||
21 | obj-$(CONFIG_SND_SOC_FSL_SAI) += snd-soc-fsl-sai.o | 23 | obj-$(CONFIG_SND_SOC_FSL_SAI) += snd-soc-fsl-sai.o |
22 | obj-$(CONFIG_SND_SOC_FSL_SSI) += snd-soc-fsl-ssi.o | 24 | obj-$(CONFIG_SND_SOC_FSL_SSI) += snd-soc-fsl-ssi.o |
23 | obj-$(CONFIG_SND_SOC_FSL_SPDIF) += snd-soc-fsl-spdif.o | 25 | obj-$(CONFIG_SND_SOC_FSL_SPDIF) += snd-soc-fsl-spdif.o |
diff --git a/sound/soc/fsl/fsl_asrc.c b/sound/soc/fsl/fsl_asrc.c new file mode 100644 index 000000000000..27a4a7084343 --- /dev/null +++ b/sound/soc/fsl/fsl_asrc.c | |||
@@ -0,0 +1,992 @@ | |||
1 | /* | ||
2 | * Freescale ASRC ALSA SoC Digital Audio Interface (DAI) driver | ||
3 | * | ||
4 | * Copyright (C) 2014 Freescale Semiconductor, Inc. | ||
5 | * | ||
6 | * Author: Nicolin Chen <nicoleotsuka@gmail.com> | ||
7 | * | ||
8 | * This file is licensed under the terms of the GNU General Public License | ||
9 | * version 2. This program is licensed "as is" without any warranty of any | ||
10 | * kind, whether express or implied. | ||
11 | */ | ||
12 | |||
13 | #include <linux/clk.h> | ||
14 | #include <linux/delay.h> | ||
15 | #include <linux/dma-mapping.h> | ||
16 | #include <linux/module.h> | ||
17 | #include <linux/of_platform.h> | ||
18 | #include <linux/platform_data/dma-imx.h> | ||
19 | #include <linux/pm_runtime.h> | ||
20 | #include <sound/dmaengine_pcm.h> | ||
21 | #include <sound/pcm_params.h> | ||
22 | |||
23 | #include "fsl_asrc.h" | ||
24 | |||
25 | #define IDEAL_RATIO_DECIMAL_DEPTH 26 | ||
26 | |||
27 | #define pair_err(fmt, ...) \ | ||
28 | dev_err(&asrc_priv->pdev->dev, "Pair %c: " fmt, 'A' + index, ##__VA_ARGS__) | ||
29 | |||
30 | #define pair_dbg(fmt, ...) \ | ||
31 | dev_dbg(&asrc_priv->pdev->dev, "Pair %c: " fmt, 'A' + index, ##__VA_ARGS__) | ||
32 | |||
33 | /* Sample rates are aligned with that defined in pcm.h file */ | ||
34 | static const u8 process_option[][8][2] = { | ||
35 | /* 32kHz 44.1kHz 48kHz 64kHz 88.2kHz 96kHz 176kHz 192kHz */ | ||
36 | {{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 5512Hz */ | ||
37 | {{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 8kHz */ | ||
38 | {{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 11025Hz */ | ||
39 | {{0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 16kHz */ | ||
40 | {{0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 22050Hz */ | ||
41 | {{0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0},}, /* 32kHz */ | ||
42 | {{0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0},}, /* 44.1kHz */ | ||
43 | {{0, 2}, {0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0},}, /* 48kHz */ | ||
44 | {{1, 2}, {0, 2}, {0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0},}, /* 64kHz */ | ||
45 | {{1, 2}, {1, 2}, {1, 2}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1},}, /* 88.2kHz */ | ||
46 | {{1, 2}, {1, 2}, {1, 2}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1},}, /* 96kHz */ | ||
47 | {{2, 2}, {2, 2}, {2, 2}, {2, 1}, {2, 1}, {2, 1}, {2, 1}, {2, 1},}, /* 176kHz */ | ||
48 | {{2, 2}, {2, 2}, {2, 2}, {2, 1}, {2, 1}, {2, 1}, {2, 1}, {2, 1},}, /* 192kHz */ | ||
49 | }; | ||
50 | |||
51 | /* Corresponding to process_option */ | ||
52 | static int supported_input_rate[] = { | ||
53 | 5512, 8000, 11025, 16000, 22050, 32000, 44100, 48000, 64000, 88200, | ||
54 | 96000, 176400, 192000, | ||
55 | }; | ||
56 | |||
57 | static int supported_asrc_rate[] = { | ||
58 | 32000, 44100, 48000, 64000, 88200, 96000, 176400, 192000, | ||
59 | }; | ||
60 | |||
61 | /** | ||
62 | * The following tables map the relationship between asrc_inclk/asrc_outclk in | ||
63 | * fsl_asrc.h and the registers of ASRCSR | ||
64 | */ | ||
65 | static unsigned char input_clk_map_imx35[] = { | ||
66 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, | ||
67 | }; | ||
68 | |||
69 | static unsigned char output_clk_map_imx35[] = { | ||
70 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, | ||
71 | }; | ||
72 | |||
73 | /* i.MX53 uses the same map for input and output */ | ||
74 | static unsigned char input_clk_map_imx53[] = { | ||
75 | /* 0x0 0x1 0x2 0x3 0x4 0x5 0x6 0x7 0x8 0x9 0xa 0xb 0xc 0xd 0xe 0xf */ | ||
76 | 0x0, 0x1, 0x2, 0x7, 0x4, 0x5, 0x6, 0x3, 0x8, 0x9, 0xa, 0xb, 0xc, 0xf, 0xe, 0xd, | ||
77 | }; | ||
78 | |||
79 | static unsigned char output_clk_map_imx53[] = { | ||
80 | /* 0x0 0x1 0x2 0x3 0x4 0x5 0x6 0x7 0x8 0x9 0xa 0xb 0xc 0xd 0xe 0xf */ | ||
81 | 0x8, 0x9, 0xa, 0x7, 0xc, 0x5, 0x6, 0xb, 0x0, 0x1, 0x2, 0x3, 0x4, 0xf, 0xe, 0xd, | ||
82 | }; | ||
83 | |||
84 | static unsigned char *clk_map[2]; | ||
85 | |||
86 | /** | ||
87 | * Request ASRC pair | ||
88 | * | ||
89 | * It assigns pair by the order of A->C->B because allocation of pair B, | ||
90 | * within range [ANCA, ANCA+ANCB-1], depends on the channels of pair A | ||
91 | * while pair A and pair C are comparatively independent. | ||
92 | */ | ||
93 | static int fsl_asrc_request_pair(int channels, struct fsl_asrc_pair *pair) | ||
94 | { | ||
95 | enum asrc_pair_index index = ASRC_INVALID_PAIR; | ||
96 | struct fsl_asrc *asrc_priv = pair->asrc_priv; | ||
97 | struct device *dev = &asrc_priv->pdev->dev; | ||
98 | unsigned long lock_flags; | ||
99 | int i, ret = 0; | ||
100 | |||
101 | spin_lock_irqsave(&asrc_priv->lock, lock_flags); | ||
102 | |||
103 | for (i = ASRC_PAIR_A; i < ASRC_PAIR_MAX_NUM; i++) { | ||
104 | if (asrc_priv->pair[i] != NULL) | ||
105 | continue; | ||
106 | |||
107 | index = i; | ||
108 | |||
109 | if (i != ASRC_PAIR_B) | ||
110 | break; | ||
111 | } | ||
112 | |||
113 | if (index == ASRC_INVALID_PAIR) { | ||
114 | dev_err(dev, "all pairs are busy now\n"); | ||
115 | ret = -EBUSY; | ||
116 | } else if (asrc_priv->channel_avail < channels) { | ||
117 | dev_err(dev, "can't afford required channels: %d\n", channels); | ||
118 | ret = -EINVAL; | ||
119 | } else { | ||
120 | asrc_priv->channel_avail -= channels; | ||
121 | asrc_priv->pair[index] = pair; | ||
122 | pair->channels = channels; | ||
123 | pair->index = index; | ||
124 | } | ||
125 | |||
126 | spin_unlock_irqrestore(&asrc_priv->lock, lock_flags); | ||
127 | |||
128 | return ret; | ||
129 | } | ||
130 | |||
131 | /** | ||
132 | * Release ASRC pair | ||
133 | * | ||
134 | * It clears the resource from asrc_priv and releases the occupied channels. | ||
135 | */ | ||
136 | static void fsl_asrc_release_pair(struct fsl_asrc_pair *pair) | ||
137 | { | ||
138 | struct fsl_asrc *asrc_priv = pair->asrc_priv; | ||
139 | enum asrc_pair_index index = pair->index; | ||
140 | unsigned long lock_flags; | ||
141 | |||
142 | /* Make sure the pair is disabled */ | ||
143 | regmap_update_bits(asrc_priv->regmap, REG_ASRCTR, | ||
144 | ASRCTR_ASRCEi_MASK(index), 0); | ||
145 | |||
146 | spin_lock_irqsave(&asrc_priv->lock, lock_flags); | ||
147 | |||
148 | asrc_priv->channel_avail += pair->channels; | ||
149 | asrc_priv->pair[index] = NULL; | ||
150 | pair->error = 0; | ||
151 | |||
152 | spin_unlock_irqrestore(&asrc_priv->lock, lock_flags); | ||
153 | } | ||
154 | |||
155 | /** | ||
156 | * Configure input and output thresholds | ||
157 | */ | ||
158 | static void fsl_asrc_set_watermarks(struct fsl_asrc_pair *pair, u32 in, u32 out) | ||
159 | { | ||
160 | struct fsl_asrc *asrc_priv = pair->asrc_priv; | ||
161 | enum asrc_pair_index index = pair->index; | ||
162 | |||
163 | regmap_update_bits(asrc_priv->regmap, REG_ASRMCR(index), | ||
164 | ASRMCRi_EXTTHRSHi_MASK | | ||
165 | ASRMCRi_INFIFO_THRESHOLD_MASK | | ||
166 | ASRMCRi_OUTFIFO_THRESHOLD_MASK, | ||
167 | ASRMCRi_EXTTHRSHi | | ||
168 | ASRMCRi_INFIFO_THRESHOLD(in) | | ||
169 | ASRMCRi_OUTFIFO_THRESHOLD(out)); | ||
170 | } | ||
171 | |||
172 | /** | ||
173 | * Calculate the total divisor between asrck clock rate and sample rate | ||
174 | * | ||
175 | * It follows the formula clk_rate = samplerate * (2 ^ prescaler) * divider | ||
176 | */ | ||
177 | static u32 fsl_asrc_cal_asrck_divisor(struct fsl_asrc_pair *pair, u32 div) | ||
178 | { | ||
179 | u32 ps; | ||
180 | |||
181 | /* Calculate the divisors: prescaler [2^0, 2^7], divder [1, 8] */ | ||
182 | for (ps = 0; div > 8; ps++) | ||
183 | div >>= 1; | ||
184 | |||
185 | return ((div - 1) << ASRCDRi_AxCPi_WIDTH) | ps; | ||
186 | } | ||
187 | |||
188 | /** | ||
189 | * Calculate and set the ratio for Ideal Ratio mode only | ||
190 | * | ||
191 | * The ratio is a 32-bit fixed point value with 26 fractional bits. | ||
192 | */ | ||
193 | static int fsl_asrc_set_ideal_ratio(struct fsl_asrc_pair *pair, | ||
194 | int inrate, int outrate) | ||
195 | { | ||
196 | struct fsl_asrc *asrc_priv = pair->asrc_priv; | ||
197 | enum asrc_pair_index index = pair->index; | ||
198 | unsigned long ratio; | ||
199 | int i; | ||
200 | |||
201 | if (!outrate) { | ||
202 | pair_err("output rate should not be zero\n"); | ||
203 | return -EINVAL; | ||
204 | } | ||
205 | |||
206 | /* Calculate the intergal part of the ratio */ | ||
207 | ratio = (inrate / outrate) << IDEAL_RATIO_DECIMAL_DEPTH; | ||
208 | |||
209 | /* ... and then the 26 depth decimal part */ | ||
210 | inrate %= outrate; | ||
211 | |||
212 | for (i = 1; i <= IDEAL_RATIO_DECIMAL_DEPTH; i++) { | ||
213 | inrate <<= 1; | ||
214 | |||
215 | if (inrate < outrate) | ||
216 | continue; | ||
217 | |||
218 | ratio |= 1 << (IDEAL_RATIO_DECIMAL_DEPTH - i); | ||
219 | inrate -= outrate; | ||
220 | |||
221 | if (!inrate) | ||
222 | break; | ||
223 | } | ||
224 | |||
225 | regmap_write(asrc_priv->regmap, REG_ASRIDRL(index), ratio); | ||
226 | regmap_write(asrc_priv->regmap, REG_ASRIDRH(index), ratio >> 24); | ||
227 | |||
228 | return 0; | ||
229 | } | ||
230 | |||
231 | /** | ||
232 | * Configure the assigned ASRC pair | ||
233 | * | ||
234 | * It configures those ASRC registers according to a configuration instance | ||
235 | * of struct asrc_config which includes in/output sample rate, width, channel | ||
236 | * and clock settings. | ||
237 | */ | ||
238 | static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair) | ||
239 | { | ||
240 | struct asrc_config *config = pair->config; | ||
241 | struct fsl_asrc *asrc_priv = pair->asrc_priv; | ||
242 | enum asrc_pair_index index = pair->index; | ||
243 | u32 inrate = config->input_sample_rate, indiv; | ||
244 | u32 outrate = config->output_sample_rate, outdiv; | ||
245 | bool ideal = config->inclk == INCLK_NONE; | ||
246 | u32 clk_index[2], div[2]; | ||
247 | int in, out, channels; | ||
248 | struct clk *clk; | ||
249 | |||
250 | if (!config) { | ||
251 | pair_err("invalid pair config\n"); | ||
252 | return -EINVAL; | ||
253 | } | ||
254 | |||
255 | /* Validate channels */ | ||
256 | if (config->channel_num < 1 || config->channel_num > 10) { | ||
257 | pair_err("does not support %d channels\n", config->channel_num); | ||
258 | return -EINVAL; | ||
259 | } | ||
260 | |||
261 | /* Validate output width */ | ||
262 | if (config->output_word_width == ASRC_WIDTH_8_BIT) { | ||
263 | pair_err("does not support 8bit width output\n"); | ||
264 | return -EINVAL; | ||
265 | } | ||
266 | |||
267 | /* Validate input and output sample rates */ | ||
268 | for (in = 0; in < ARRAY_SIZE(supported_input_rate); in++) | ||
269 | if (inrate == supported_input_rate[in]) | ||
270 | break; | ||
271 | |||
272 | if (in == ARRAY_SIZE(supported_input_rate)) { | ||
273 | pair_err("unsupported input sample rate: %dHz\n", inrate); | ||
274 | return -EINVAL; | ||
275 | } | ||
276 | |||
277 | for (out = 0; out < ARRAY_SIZE(supported_asrc_rate); out++) | ||
278 | if (outrate == supported_asrc_rate[out]) | ||
279 | break; | ||
280 | |||
281 | if (out == ARRAY_SIZE(supported_asrc_rate)) { | ||
282 | pair_err("unsupported output sample rate: %dHz\n", outrate); | ||
283 | return -EINVAL; | ||
284 | } | ||
285 | |||
286 | /* Validate input and output clock sources */ | ||
287 | clk_index[IN] = clk_map[IN][config->inclk]; | ||
288 | clk_index[OUT] = clk_map[OUT][config->outclk]; | ||
289 | |||
290 | /* We only have output clock for ideal ratio mode */ | ||
291 | clk = asrc_priv->asrck_clk[clk_index[ideal ? OUT : IN]]; | ||
292 | |||
293 | div[IN] = clk_get_rate(clk) / inrate; | ||
294 | if (div[IN] == 0) { | ||
295 | pair_err("failed to support input sample rate %dHz by asrck_%x\n", | ||
296 | inrate, clk_index[ideal ? OUT : IN]); | ||
297 | return -EINVAL; | ||
298 | } | ||
299 | |||
300 | clk = asrc_priv->asrck_clk[clk_index[OUT]]; | ||
301 | |||
302 | /* Use fixed output rate for Ideal Ratio mode (INCLK_NONE) */ | ||
303 | if (ideal) | ||
304 | div[OUT] = clk_get_rate(clk) / IDEAL_RATIO_RATE; | ||
305 | else | ||
306 | div[OUT] = clk_get_rate(clk) / outrate; | ||
307 | |||
308 | if (div[OUT] == 0) { | ||
309 | pair_err("failed to support output sample rate %dHz by asrck_%x\n", | ||
310 | outrate, clk_index[OUT]); | ||
311 | return -EINVAL; | ||
312 | } | ||
313 | |||
314 | /* Set the channel number */ | ||
315 | channels = config->channel_num; | ||
316 | |||
317 | if (asrc_priv->channel_bits < 4) | ||
318 | channels /= 2; | ||
319 | |||
320 | /* Update channels for current pair */ | ||
321 | regmap_update_bits(asrc_priv->regmap, REG_ASRCNCR, | ||
322 | ASRCNCR_ANCi_MASK(index, asrc_priv->channel_bits), | ||
323 | ASRCNCR_ANCi(index, channels, asrc_priv->channel_bits)); | ||
324 | |||
325 | /* Default setting: Automatic selection for processing mode */ | ||
326 | regmap_update_bits(asrc_priv->regmap, REG_ASRCTR, | ||
327 | ASRCTR_ATSi_MASK(index), ASRCTR_ATS(index)); | ||
328 | regmap_update_bits(asrc_priv->regmap, REG_ASRCTR, | ||
329 | ASRCTR_USRi_MASK(index), 0); | ||
330 | |||
331 | /* Set the input and output clock sources */ | ||
332 | regmap_update_bits(asrc_priv->regmap, REG_ASRCSR, | ||
333 | ASRCSR_AICSi_MASK(index) | ASRCSR_AOCSi_MASK(index), | ||
334 | ASRCSR_AICS(index, clk_index[IN]) | | ||
335 | ASRCSR_AOCS(index, clk_index[OUT])); | ||
336 | |||
337 | /* Calculate the input clock divisors */ | ||
338 | indiv = fsl_asrc_cal_asrck_divisor(pair, div[IN]); | ||
339 | outdiv = fsl_asrc_cal_asrck_divisor(pair, div[OUT]); | ||
340 | |||
341 | /* Suppose indiv and outdiv includes prescaler, so add its MASK too */ | ||
342 | regmap_update_bits(asrc_priv->regmap, REG_ASRCDR(index), | ||
343 | ASRCDRi_AOCPi_MASK(index) | ASRCDRi_AICPi_MASK(index) | | ||
344 | ASRCDRi_AOCDi_MASK(index) | ASRCDRi_AICDi_MASK(index), | ||
345 | ASRCDRi_AOCP(index, outdiv) | ASRCDRi_AICP(index, indiv)); | ||
346 | |||
347 | /* Implement word_width configurations */ | ||
348 | regmap_update_bits(asrc_priv->regmap, REG_ASRMCR1(index), | ||
349 | ASRMCR1i_OW16_MASK | ASRMCR1i_IWD_MASK, | ||
350 | ASRMCR1i_OW16(config->output_word_width) | | ||
351 | ASRMCR1i_IWD(config->input_word_width)); | ||
352 | |||
353 | /* Enable BUFFER STALL */ | ||
354 | regmap_update_bits(asrc_priv->regmap, REG_ASRMCR(index), | ||
355 | ASRMCRi_BUFSTALLi_MASK, ASRMCRi_BUFSTALLi); | ||
356 | |||
357 | /* Set default thresholds for input and output FIFO */ | ||
358 | fsl_asrc_set_watermarks(pair, ASRC_INPUTFIFO_THRESHOLD, | ||
359 | ASRC_INPUTFIFO_THRESHOLD); | ||
360 | |||
361 | /* Configure the followings only for Ideal Ratio mode */ | ||
362 | if (!ideal) | ||
363 | return 0; | ||
364 | |||
365 | /* Clear ASTSx bit to use Ideal Ratio mode */ | ||
366 | regmap_update_bits(asrc_priv->regmap, REG_ASRCTR, | ||
367 | ASRCTR_ATSi_MASK(index), 0); | ||
368 | |||
369 | /* Enable Ideal Ratio mode */ | ||
370 | regmap_update_bits(asrc_priv->regmap, REG_ASRCTR, | ||
371 | ASRCTR_IDRi_MASK(index) | ASRCTR_USRi_MASK(index), | ||
372 | ASRCTR_IDR(index) | ASRCTR_USR(index)); | ||
373 | |||
374 | /* Apply configurations for pre- and post-processing */ | ||
375 | regmap_update_bits(asrc_priv->regmap, REG_ASRCFG, | ||
376 | ASRCFG_PREMODi_MASK(index) | ASRCFG_POSTMODi_MASK(index), | ||
377 | ASRCFG_PREMOD(index, process_option[in][out][0]) | | ||
378 | ASRCFG_POSTMOD(index, process_option[in][out][1])); | ||
379 | |||
380 | return fsl_asrc_set_ideal_ratio(pair, inrate, outrate); | ||
381 | } | ||
382 | |||
383 | /** | ||
384 | * Start the assigned ASRC pair | ||
385 | * | ||
386 | * It enables the assigned pair and makes it stopped at the stall level. | ||
387 | */ | ||
388 | static void fsl_asrc_start_pair(struct fsl_asrc_pair *pair) | ||
389 | { | ||
390 | struct fsl_asrc *asrc_priv = pair->asrc_priv; | ||
391 | enum asrc_pair_index index = pair->index; | ||
392 | int reg, retry = 10, i; | ||
393 | |||
394 | /* Enable the current pair */ | ||
395 | regmap_update_bits(asrc_priv->regmap, REG_ASRCTR, | ||
396 | ASRCTR_ASRCEi_MASK(index), ASRCTR_ASRCE(index)); | ||
397 | |||
398 | /* Wait for status of initialization */ | ||
399 | do { | ||
400 | udelay(5); | ||
401 | regmap_read(asrc_priv->regmap, REG_ASRCFG, ®); | ||
402 | reg &= ASRCFG_INIRQi_MASK(index); | ||
403 | } while (!reg && --retry); | ||
404 | |||
405 | /* Make the input fifo to ASRC STALL level */ | ||
406 | regmap_read(asrc_priv->regmap, REG_ASRCNCR, ®); | ||
407 | for (i = 0; i < pair->channels * 4; i++) | ||
408 | regmap_write(asrc_priv->regmap, REG_ASRDI(index), 0); | ||
409 | |||
410 | /* Enable overload interrupt */ | ||
411 | regmap_write(asrc_priv->regmap, REG_ASRIER, ASRIER_AOLIE); | ||
412 | } | ||
413 | |||
414 | /** | ||
415 | * Stop the assigned ASRC pair | ||
416 | */ | ||
417 | static void fsl_asrc_stop_pair(struct fsl_asrc_pair *pair) | ||
418 | { | ||
419 | struct fsl_asrc *asrc_priv = pair->asrc_priv; | ||
420 | enum asrc_pair_index index = pair->index; | ||
421 | |||
422 | /* Stop the current pair */ | ||
423 | regmap_update_bits(asrc_priv->regmap, REG_ASRCTR, | ||
424 | ASRCTR_ASRCEi_MASK(index), 0); | ||
425 | } | ||
426 | |||
427 | /** | ||
428 | * Get DMA channel according to the pair and direction. | ||
429 | */ | ||
430 | struct dma_chan *fsl_asrc_get_dma_channel(struct fsl_asrc_pair *pair, bool dir) | ||
431 | { | ||
432 | struct fsl_asrc *asrc_priv = pair->asrc_priv; | ||
433 | enum asrc_pair_index index = pair->index; | ||
434 | char name[4]; | ||
435 | |||
436 | sprintf(name, "%cx%c", dir == IN ? 'r' : 't', index + 'a'); | ||
437 | |||
438 | return dma_request_slave_channel(&asrc_priv->pdev->dev, name); | ||
439 | } | ||
440 | EXPORT_SYMBOL_GPL(fsl_asrc_get_dma_channel); | ||
441 | |||
442 | static int fsl_asrc_dai_hw_params(struct snd_pcm_substream *substream, | ||
443 | struct snd_pcm_hw_params *params, | ||
444 | struct snd_soc_dai *dai) | ||
445 | { | ||
446 | struct fsl_asrc *asrc_priv = snd_soc_dai_get_drvdata(dai); | ||
447 | int width = snd_pcm_format_width(params_format(params)); | ||
448 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
449 | struct fsl_asrc_pair *pair = runtime->private_data; | ||
450 | unsigned int channels = params_channels(params); | ||
451 | unsigned int rate = params_rate(params); | ||
452 | struct asrc_config config; | ||
453 | int word_width, ret; | ||
454 | |||
455 | ret = fsl_asrc_request_pair(channels, pair); | ||
456 | if (ret) { | ||
457 | dev_err(dai->dev, "fail to request asrc pair\n"); | ||
458 | return ret; | ||
459 | } | ||
460 | |||
461 | pair->config = &config; | ||
462 | |||
463 | if (width == 16) | ||
464 | width = ASRC_WIDTH_16_BIT; | ||
465 | else | ||
466 | width = ASRC_WIDTH_24_BIT; | ||
467 | |||
468 | if (asrc_priv->asrc_width == 16) | ||
469 | word_width = ASRC_WIDTH_16_BIT; | ||
470 | else | ||
471 | word_width = ASRC_WIDTH_24_BIT; | ||
472 | |||
473 | config.pair = pair->index; | ||
474 | config.channel_num = channels; | ||
475 | config.inclk = INCLK_NONE; | ||
476 | config.outclk = OUTCLK_ASRCK1_CLK; | ||
477 | |||
478 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
479 | config.input_word_width = width; | ||
480 | config.output_word_width = word_width; | ||
481 | config.input_sample_rate = rate; | ||
482 | config.output_sample_rate = asrc_priv->asrc_rate; | ||
483 | } else { | ||
484 | config.input_word_width = word_width; | ||
485 | config.output_word_width = width; | ||
486 | config.input_sample_rate = asrc_priv->asrc_rate; | ||
487 | config.output_sample_rate = rate; | ||
488 | } | ||
489 | |||
490 | ret = fsl_asrc_config_pair(pair); | ||
491 | if (ret) { | ||
492 | dev_err(dai->dev, "fail to config asrc pair\n"); | ||
493 | return ret; | ||
494 | } | ||
495 | |||
496 | return 0; | ||
497 | } | ||
498 | |||
499 | static int fsl_asrc_dai_hw_free(struct snd_pcm_substream *substream, | ||
500 | struct snd_soc_dai *dai) | ||
501 | { | ||
502 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
503 | struct fsl_asrc_pair *pair = runtime->private_data; | ||
504 | |||
505 | if (pair) | ||
506 | fsl_asrc_release_pair(pair); | ||
507 | |||
508 | return 0; | ||
509 | } | ||
510 | |||
511 | static int fsl_asrc_dai_trigger(struct snd_pcm_substream *substream, int cmd, | ||
512 | struct snd_soc_dai *dai) | ||
513 | { | ||
514 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
515 | struct fsl_asrc_pair *pair = runtime->private_data; | ||
516 | |||
517 | switch (cmd) { | ||
518 | case SNDRV_PCM_TRIGGER_START: | ||
519 | case SNDRV_PCM_TRIGGER_RESUME: | ||
520 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | ||
521 | fsl_asrc_start_pair(pair); | ||
522 | break; | ||
523 | case SNDRV_PCM_TRIGGER_STOP: | ||
524 | case SNDRV_PCM_TRIGGER_SUSPEND: | ||
525 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | ||
526 | fsl_asrc_stop_pair(pair); | ||
527 | break; | ||
528 | default: | ||
529 | return -EINVAL; | ||
530 | } | ||
531 | |||
532 | return 0; | ||
533 | } | ||
534 | |||
535 | static struct snd_soc_dai_ops fsl_asrc_dai_ops = { | ||
536 | .hw_params = fsl_asrc_dai_hw_params, | ||
537 | .hw_free = fsl_asrc_dai_hw_free, | ||
538 | .trigger = fsl_asrc_dai_trigger, | ||
539 | }; | ||
540 | |||
541 | static int fsl_asrc_dai_probe(struct snd_soc_dai *dai) | ||
542 | { | ||
543 | struct fsl_asrc *asrc_priv = snd_soc_dai_get_drvdata(dai); | ||
544 | |||
545 | snd_soc_dai_init_dma_data(dai, &asrc_priv->dma_params_tx, | ||
546 | &asrc_priv->dma_params_rx); | ||
547 | |||
548 | return 0; | ||
549 | } | ||
550 | |||
551 | #define FSL_ASRC_RATES SNDRV_PCM_RATE_8000_192000 | ||
552 | #define FSL_ASRC_FORMATS (SNDRV_PCM_FMTBIT_S24_LE | \ | ||
553 | SNDRV_PCM_FMTBIT_S16_LE | \ | ||
554 | SNDRV_PCM_FORMAT_S20_3LE) | ||
555 | |||
556 | static struct snd_soc_dai_driver fsl_asrc_dai = { | ||
557 | .probe = fsl_asrc_dai_probe, | ||
558 | .playback = { | ||
559 | .stream_name = "ASRC-Playback", | ||
560 | .channels_min = 1, | ||
561 | .channels_max = 10, | ||
562 | .rates = FSL_ASRC_RATES, | ||
563 | .formats = FSL_ASRC_FORMATS, | ||
564 | }, | ||
565 | .capture = { | ||
566 | .stream_name = "ASRC-Capture", | ||
567 | .channels_min = 1, | ||
568 | .channels_max = 10, | ||
569 | .rates = FSL_ASRC_RATES, | ||
570 | .formats = FSL_ASRC_FORMATS, | ||
571 | }, | ||
572 | .ops = &fsl_asrc_dai_ops, | ||
573 | }; | ||
574 | |||
575 | static const struct snd_soc_component_driver fsl_asrc_component = { | ||
576 | .name = "fsl-asrc-dai", | ||
577 | }; | ||
578 | |||
579 | static bool fsl_asrc_readable_reg(struct device *dev, unsigned int reg) | ||
580 | { | ||
581 | switch (reg) { | ||
582 | case REG_ASRCTR: | ||
583 | case REG_ASRIER: | ||
584 | case REG_ASRCNCR: | ||
585 | case REG_ASRCFG: | ||
586 | case REG_ASRCSR: | ||
587 | case REG_ASRCDR1: | ||
588 | case REG_ASRCDR2: | ||
589 | case REG_ASRSTR: | ||
590 | case REG_ASRPM1: | ||
591 | case REG_ASRPM2: | ||
592 | case REG_ASRPM3: | ||
593 | case REG_ASRPM4: | ||
594 | case REG_ASRPM5: | ||
595 | case REG_ASRTFR1: | ||
596 | case REG_ASRCCR: | ||
597 | case REG_ASRDOA: | ||
598 | case REG_ASRDOB: | ||
599 | case REG_ASRDOC: | ||
600 | case REG_ASRIDRHA: | ||
601 | case REG_ASRIDRLA: | ||
602 | case REG_ASRIDRHB: | ||
603 | case REG_ASRIDRLB: | ||
604 | case REG_ASRIDRHC: | ||
605 | case REG_ASRIDRLC: | ||
606 | case REG_ASR76K: | ||
607 | case REG_ASR56K: | ||
608 | case REG_ASRMCRA: | ||
609 | case REG_ASRFSTA: | ||
610 | case REG_ASRMCRB: | ||
611 | case REG_ASRFSTB: | ||
612 | case REG_ASRMCRC: | ||
613 | case REG_ASRFSTC: | ||
614 | case REG_ASRMCR1A: | ||
615 | case REG_ASRMCR1B: | ||
616 | case REG_ASRMCR1C: | ||
617 | return true; | ||
618 | default: | ||
619 | return false; | ||
620 | } | ||
621 | } | ||
622 | |||
623 | static bool fsl_asrc_volatile_reg(struct device *dev, unsigned int reg) | ||
624 | { | ||
625 | switch (reg) { | ||
626 | case REG_ASRSTR: | ||
627 | case REG_ASRDIA: | ||
628 | case REG_ASRDIB: | ||
629 | case REG_ASRDIC: | ||
630 | case REG_ASRDOA: | ||
631 | case REG_ASRDOB: | ||
632 | case REG_ASRDOC: | ||
633 | case REG_ASRFSTA: | ||
634 | case REG_ASRFSTB: | ||
635 | case REG_ASRFSTC: | ||
636 | case REG_ASRCFG: | ||
637 | return true; | ||
638 | default: | ||
639 | return false; | ||
640 | } | ||
641 | } | ||
642 | |||
643 | static bool fsl_asrc_writeable_reg(struct device *dev, unsigned int reg) | ||
644 | { | ||
645 | switch (reg) { | ||
646 | case REG_ASRCTR: | ||
647 | case REG_ASRIER: | ||
648 | case REG_ASRCNCR: | ||
649 | case REG_ASRCFG: | ||
650 | case REG_ASRCSR: | ||
651 | case REG_ASRCDR1: | ||
652 | case REG_ASRCDR2: | ||
653 | case REG_ASRSTR: | ||
654 | case REG_ASRPM1: | ||
655 | case REG_ASRPM2: | ||
656 | case REG_ASRPM3: | ||
657 | case REG_ASRPM4: | ||
658 | case REG_ASRPM5: | ||
659 | case REG_ASRTFR1: | ||
660 | case REG_ASRCCR: | ||
661 | case REG_ASRDIA: | ||
662 | case REG_ASRDIB: | ||
663 | case REG_ASRDIC: | ||
664 | case REG_ASRIDRHA: | ||
665 | case REG_ASRIDRLA: | ||
666 | case REG_ASRIDRHB: | ||
667 | case REG_ASRIDRLB: | ||
668 | case REG_ASRIDRHC: | ||
669 | case REG_ASRIDRLC: | ||
670 | case REG_ASR76K: | ||
671 | case REG_ASR56K: | ||
672 | case REG_ASRMCRA: | ||
673 | case REG_ASRMCRB: | ||
674 | case REG_ASRMCRC: | ||
675 | case REG_ASRMCR1A: | ||
676 | case REG_ASRMCR1B: | ||
677 | case REG_ASRMCR1C: | ||
678 | return true; | ||
679 | default: | ||
680 | return false; | ||
681 | } | ||
682 | } | ||
683 | |||
684 | static struct regmap_config fsl_asrc_regmap_config = { | ||
685 | .reg_bits = 32, | ||
686 | .reg_stride = 4, | ||
687 | .val_bits = 32, | ||
688 | |||
689 | .max_register = REG_ASRMCR1C, | ||
690 | .readable_reg = fsl_asrc_readable_reg, | ||
691 | .volatile_reg = fsl_asrc_volatile_reg, | ||
692 | .writeable_reg = fsl_asrc_writeable_reg, | ||
693 | .cache_type = REGCACHE_RBTREE, | ||
694 | }; | ||
695 | |||
696 | /** | ||
697 | * Initialize ASRC registers with a default configurations | ||
698 | */ | ||
699 | static int fsl_asrc_init(struct fsl_asrc *asrc_priv) | ||
700 | { | ||
701 | /* Halt ASRC internal FP when input FIFO needs data for pair A, B, C */ | ||
702 | regmap_write(asrc_priv->regmap, REG_ASRCTR, ASRCTR_ASRCEN); | ||
703 | |||
704 | /* Disable interrupt by default */ | ||
705 | regmap_write(asrc_priv->regmap, REG_ASRIER, 0x0); | ||
706 | |||
707 | /* Apply recommended settings for parameters from Reference Manual */ | ||
708 | regmap_write(asrc_priv->regmap, REG_ASRPM1, 0x7fffff); | ||
709 | regmap_write(asrc_priv->regmap, REG_ASRPM2, 0x255555); | ||
710 | regmap_write(asrc_priv->regmap, REG_ASRPM3, 0xff7280); | ||
711 | regmap_write(asrc_priv->regmap, REG_ASRPM4, 0xff7280); | ||
712 | regmap_write(asrc_priv->regmap, REG_ASRPM5, 0xff7280); | ||
713 | |||
714 | /* Base address for task queue FIFO. Set to 0x7C */ | ||
715 | regmap_update_bits(asrc_priv->regmap, REG_ASRTFR1, | ||
716 | ASRTFR1_TF_BASE_MASK, ASRTFR1_TF_BASE(0xfc)); | ||
717 | |||
718 | /* Set the processing clock for 76KHz to 133M */ | ||
719 | regmap_write(asrc_priv->regmap, REG_ASR76K, 0x06D6); | ||
720 | |||
721 | /* Set the processing clock for 56KHz to 133M */ | ||
722 | return regmap_write(asrc_priv->regmap, REG_ASR56K, 0x0947); | ||
723 | } | ||
724 | |||
725 | /** | ||
726 | * Interrupt handler for ASRC | ||
727 | */ | ||
728 | static irqreturn_t fsl_asrc_isr(int irq, void *dev_id) | ||
729 | { | ||
730 | struct fsl_asrc *asrc_priv = (struct fsl_asrc *)dev_id; | ||
731 | struct device *dev = &asrc_priv->pdev->dev; | ||
732 | enum asrc_pair_index index; | ||
733 | u32 status; | ||
734 | |||
735 | regmap_read(asrc_priv->regmap, REG_ASRSTR, &status); | ||
736 | |||
737 | /* Clean overload error */ | ||
738 | regmap_write(asrc_priv->regmap, REG_ASRSTR, ASRSTR_AOLE); | ||
739 | |||
740 | /* | ||
741 | * We here use dev_dbg() for all exceptions because ASRC itself does | ||
742 | * not care if FIFO overflowed or underrun while a warning in the | ||
743 | * interrupt would result a ridged conversion. | ||
744 | */ | ||
745 | for (index = ASRC_PAIR_A; index < ASRC_PAIR_MAX_NUM; index++) { | ||
746 | if (!asrc_priv->pair[index]) | ||
747 | continue; | ||
748 | |||
749 | if (status & ASRSTR_ATQOL) { | ||
750 | asrc_priv->pair[index]->error |= ASRC_TASK_Q_OVERLOAD; | ||
751 | dev_dbg(dev, "ASRC Task Queue FIFO overload\n"); | ||
752 | } | ||
753 | |||
754 | if (status & ASRSTR_AOOL(index)) { | ||
755 | asrc_priv->pair[index]->error |= ASRC_OUTPUT_TASK_OVERLOAD; | ||
756 | pair_dbg("Output Task Overload\n"); | ||
757 | } | ||
758 | |||
759 | if (status & ASRSTR_AIOL(index)) { | ||
760 | asrc_priv->pair[index]->error |= ASRC_INPUT_TASK_OVERLOAD; | ||
761 | pair_dbg("Input Task Overload\n"); | ||
762 | } | ||
763 | |||
764 | if (status & ASRSTR_AODO(index)) { | ||
765 | asrc_priv->pair[index]->error |= ASRC_OUTPUT_BUFFER_OVERFLOW; | ||
766 | pair_dbg("Output Data Buffer has overflowed\n"); | ||
767 | } | ||
768 | |||
769 | if (status & ASRSTR_AIDU(index)) { | ||
770 | asrc_priv->pair[index]->error |= ASRC_INPUT_BUFFER_UNDERRUN; | ||
771 | pair_dbg("Input Data Buffer has underflowed\n"); | ||
772 | } | ||
773 | } | ||
774 | |||
775 | return IRQ_HANDLED; | ||
776 | } | ||
777 | |||
778 | static int fsl_asrc_probe(struct platform_device *pdev) | ||
779 | { | ||
780 | struct device_node *np = pdev->dev.of_node; | ||
781 | struct fsl_asrc *asrc_priv; | ||
782 | struct resource *res; | ||
783 | void __iomem *regs; | ||
784 | int irq, ret, i; | ||
785 | char tmp[16]; | ||
786 | |||
787 | asrc_priv = devm_kzalloc(&pdev->dev, sizeof(*asrc_priv), GFP_KERNEL); | ||
788 | if (!asrc_priv) | ||
789 | return -ENOMEM; | ||
790 | |||
791 | asrc_priv->pdev = pdev; | ||
792 | strcpy(asrc_priv->name, np->name); | ||
793 | |||
794 | /* Get the addresses and IRQ */ | ||
795 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
796 | regs = devm_ioremap_resource(&pdev->dev, res); | ||
797 | if (IS_ERR(regs)) | ||
798 | return PTR_ERR(regs); | ||
799 | |||
800 | asrc_priv->paddr = res->start; | ||
801 | |||
802 | /* Register regmap and let it prepare core clock */ | ||
803 | if (of_property_read_bool(np, "big-endian")) | ||
804 | fsl_asrc_regmap_config.val_format_endian = REGMAP_ENDIAN_BIG; | ||
805 | |||
806 | asrc_priv->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "mem", regs, | ||
807 | &fsl_asrc_regmap_config); | ||
808 | if (IS_ERR(asrc_priv->regmap)) { | ||
809 | dev_err(&pdev->dev, "failed to init regmap\n"); | ||
810 | return PTR_ERR(asrc_priv->regmap); | ||
811 | } | ||
812 | |||
813 | irq = platform_get_irq(pdev, 0); | ||
814 | if (irq < 0) { | ||
815 | dev_err(&pdev->dev, "no irq for node %s\n", np->full_name); | ||
816 | return irq; | ||
817 | } | ||
818 | |||
819 | ret = devm_request_irq(&pdev->dev, irq, fsl_asrc_isr, 0, | ||
820 | asrc_priv->name, asrc_priv); | ||
821 | if (ret) { | ||
822 | dev_err(&pdev->dev, "failed to claim irq %u: %d\n", irq, ret); | ||
823 | return ret; | ||
824 | } | ||
825 | |||
826 | asrc_priv->mem_clk = devm_clk_get(&pdev->dev, "mem"); | ||
827 | if (IS_ERR(asrc_priv->mem_clk)) { | ||
828 | dev_err(&pdev->dev, "failed to get mem clock\n"); | ||
829 | return PTR_ERR(asrc_priv->ipg_clk); | ||
830 | } | ||
831 | |||
832 | asrc_priv->ipg_clk = devm_clk_get(&pdev->dev, "ipg"); | ||
833 | if (IS_ERR(asrc_priv->ipg_clk)) { | ||
834 | dev_err(&pdev->dev, "failed to get ipg clock\n"); | ||
835 | return PTR_ERR(asrc_priv->ipg_clk); | ||
836 | } | ||
837 | |||
838 | for (i = 0; i < ASRC_CLK_MAX_NUM; i++) { | ||
839 | sprintf(tmp, "asrck_%x", i); | ||
840 | asrc_priv->asrck_clk[i] = devm_clk_get(&pdev->dev, tmp); | ||
841 | if (IS_ERR(asrc_priv->asrck_clk[i])) { | ||
842 | dev_err(&pdev->dev, "failed to get %s clock\n", tmp); | ||
843 | return PTR_ERR(asrc_priv->asrck_clk[i]); | ||
844 | } | ||
845 | } | ||
846 | |||
847 | if (of_device_is_compatible(pdev->dev.of_node, "fsl,imx35-asrc")) { | ||
848 | asrc_priv->channel_bits = 3; | ||
849 | clk_map[IN] = input_clk_map_imx35; | ||
850 | clk_map[OUT] = output_clk_map_imx35; | ||
851 | } else { | ||
852 | asrc_priv->channel_bits = 4; | ||
853 | clk_map[IN] = input_clk_map_imx53; | ||
854 | clk_map[OUT] = output_clk_map_imx53; | ||
855 | } | ||
856 | |||
857 | ret = fsl_asrc_init(asrc_priv); | ||
858 | if (ret) { | ||
859 | dev_err(&pdev->dev, "failed to init asrc %d\n", ret); | ||
860 | return -EINVAL; | ||
861 | } | ||
862 | |||
863 | asrc_priv->channel_avail = 10; | ||
864 | |||
865 | ret = of_property_read_u32(np, "fsl,asrc-rate", | ||
866 | &asrc_priv->asrc_rate); | ||
867 | if (ret) { | ||
868 | dev_err(&pdev->dev, "failed to get output rate\n"); | ||
869 | return -EINVAL; | ||
870 | } | ||
871 | |||
872 | ret = of_property_read_u32(np, "fsl,asrc-width", | ||
873 | &asrc_priv->asrc_width); | ||
874 | if (ret) { | ||
875 | dev_err(&pdev->dev, "failed to get output width\n"); | ||
876 | return -EINVAL; | ||
877 | } | ||
878 | |||
879 | if (asrc_priv->asrc_width != 16 && asrc_priv->asrc_width != 24) { | ||
880 | dev_warn(&pdev->dev, "unsupported width, switching to 24bit\n"); | ||
881 | asrc_priv->asrc_width = 24; | ||
882 | } | ||
883 | |||
884 | platform_set_drvdata(pdev, asrc_priv); | ||
885 | pm_runtime_enable(&pdev->dev); | ||
886 | spin_lock_init(&asrc_priv->lock); | ||
887 | |||
888 | ret = devm_snd_soc_register_component(&pdev->dev, &fsl_asrc_component, | ||
889 | &fsl_asrc_dai, 1); | ||
890 | if (ret) { | ||
891 | dev_err(&pdev->dev, "failed to register ASoC DAI\n"); | ||
892 | return ret; | ||
893 | } | ||
894 | |||
895 | ret = devm_snd_soc_register_platform(&pdev->dev, &fsl_asrc_platform); | ||
896 | if (ret) { | ||
897 | dev_err(&pdev->dev, "failed to register ASoC platform\n"); | ||
898 | return ret; | ||
899 | } | ||
900 | |||
901 | dev_info(&pdev->dev, "driver registered\n"); | ||
902 | |||
903 | return 0; | ||
904 | } | ||
905 | |||
906 | #if CONFIG_PM_RUNTIME | ||
907 | static int fsl_asrc_runtime_resume(struct device *dev) | ||
908 | { | ||
909 | struct fsl_asrc *asrc_priv = dev_get_drvdata(dev); | ||
910 | int i; | ||
911 | |||
912 | clk_prepare_enable(asrc_priv->mem_clk); | ||
913 | clk_prepare_enable(asrc_priv->ipg_clk); | ||
914 | for (i = 0; i < ASRC_CLK_MAX_NUM; i++) | ||
915 | clk_prepare_enable(asrc_priv->asrck_clk[i]); | ||
916 | |||
917 | return 0; | ||
918 | } | ||
919 | |||
920 | static int fsl_asrc_runtime_suspend(struct device *dev) | ||
921 | { | ||
922 | struct fsl_asrc *asrc_priv = dev_get_drvdata(dev); | ||
923 | int i; | ||
924 | |||
925 | for (i = 0; i < ASRC_CLK_MAX_NUM; i++) | ||
926 | clk_disable_unprepare(asrc_priv->asrck_clk[i]); | ||
927 | clk_disable_unprepare(asrc_priv->ipg_clk); | ||
928 | clk_disable_unprepare(asrc_priv->mem_clk); | ||
929 | |||
930 | return 0; | ||
931 | } | ||
932 | #endif /* CONFIG_PM_RUNTIME */ | ||
933 | |||
934 | #if CONFIG_PM_SLEEP | ||
935 | static int fsl_asrc_suspend(struct device *dev) | ||
936 | { | ||
937 | struct fsl_asrc *asrc_priv = dev_get_drvdata(dev); | ||
938 | |||
939 | regcache_cache_only(asrc_priv->regmap, true); | ||
940 | regcache_mark_dirty(asrc_priv->regmap); | ||
941 | |||
942 | return 0; | ||
943 | } | ||
944 | |||
945 | static int fsl_asrc_resume(struct device *dev) | ||
946 | { | ||
947 | struct fsl_asrc *asrc_priv = dev_get_drvdata(dev); | ||
948 | u32 asrctr; | ||
949 | |||
950 | /* Stop all pairs provisionally */ | ||
951 | regmap_read(asrc_priv->regmap, REG_ASRCTR, &asrctr); | ||
952 | regmap_update_bits(asrc_priv->regmap, REG_ASRCTR, | ||
953 | ASRCTR_ASRCEi_ALL_MASK, 0); | ||
954 | |||
955 | /* Restore all registers */ | ||
956 | regcache_cache_only(asrc_priv->regmap, false); | ||
957 | regcache_sync(asrc_priv->regmap); | ||
958 | |||
959 | /* Restart enabled pairs */ | ||
960 | regmap_update_bits(asrc_priv->regmap, REG_ASRCTR, | ||
961 | ASRCTR_ASRCEi_ALL_MASK, asrctr); | ||
962 | |||
963 | return 0; | ||
964 | } | ||
965 | #endif /* CONFIG_PM_SLEEP */ | ||
966 | |||
967 | static const struct dev_pm_ops fsl_asrc_pm = { | ||
968 | SET_RUNTIME_PM_OPS(fsl_asrc_runtime_suspend, fsl_asrc_runtime_resume, NULL) | ||
969 | SET_SYSTEM_SLEEP_PM_OPS(fsl_asrc_suspend, fsl_asrc_resume) | ||
970 | }; | ||
971 | |||
972 | static const struct of_device_id fsl_asrc_ids[] = { | ||
973 | { .compatible = "fsl,imx35-asrc", }, | ||
974 | { .compatible = "fsl,imx53-asrc", }, | ||
975 | {} | ||
976 | }; | ||
977 | MODULE_DEVICE_TABLE(of, fsl_asrc_ids); | ||
978 | |||
979 | static struct platform_driver fsl_asrc_driver = { | ||
980 | .probe = fsl_asrc_probe, | ||
981 | .driver = { | ||
982 | .name = "fsl-asrc", | ||
983 | .of_match_table = fsl_asrc_ids, | ||
984 | .pm = &fsl_asrc_pm, | ||
985 | }, | ||
986 | }; | ||
987 | module_platform_driver(fsl_asrc_driver); | ||
988 | |||
989 | MODULE_DESCRIPTION("Freescale ASRC ASoC driver"); | ||
990 | MODULE_AUTHOR("Nicolin Chen <nicoleotsuka@gmail.com>"); | ||
991 | MODULE_ALIAS("platform:fsl-asrc"); | ||
992 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/sound/soc/fsl/fsl_asrc.h b/sound/soc/fsl/fsl_asrc.h new file mode 100644 index 000000000000..a3f211f53c23 --- /dev/null +++ b/sound/soc/fsl/fsl_asrc.h | |||
@@ -0,0 +1,461 @@ | |||
1 | /* | ||
2 | * fsl_asrc.h - Freescale ASRC ALSA SoC header file | ||
3 | * | ||
4 | * Copyright (C) 2014 Freescale Semiconductor, Inc. | ||
5 | * | ||
6 | * Author: Nicolin Chen <nicoleotsuka@gmail.com> | ||
7 | * | ||
8 | * This file is licensed under the terms of the GNU General Public License | ||
9 | * version 2. This program is licensed "as is" without any warranty of any | ||
10 | * kind, whether express or implied. | ||
11 | */ | ||
12 | |||
13 | #ifndef _FSL_ASRC_H | ||
14 | #define _FSL_ASRC_H | ||
15 | |||
16 | #define IN 0 | ||
17 | #define OUT 1 | ||
18 | |||
19 | #define ASRC_DMA_BUFFER_NUM 2 | ||
20 | #define ASRC_INPUTFIFO_THRESHOLD 32 | ||
21 | #define ASRC_OUTPUTFIFO_THRESHOLD 32 | ||
22 | #define ASRC_FIFO_THRESHOLD_MIN 0 | ||
23 | #define ASRC_FIFO_THRESHOLD_MAX 63 | ||
24 | #define ASRC_DMA_BUFFER_SIZE (1024 * 48 * 4) | ||
25 | #define ASRC_MAX_BUFFER_SIZE (1024 * 48) | ||
26 | #define ASRC_OUTPUT_LAST_SAMPLE 8 | ||
27 | |||
28 | #define IDEAL_RATIO_RATE 1000000 | ||
29 | |||
30 | #define REG_ASRCTR 0x00 | ||
31 | #define REG_ASRIER 0x04 | ||
32 | #define REG_ASRCNCR 0x0C | ||
33 | #define REG_ASRCFG 0x10 | ||
34 | #define REG_ASRCSR 0x14 | ||
35 | |||
36 | #define REG_ASRCDR1 0x18 | ||
37 | #define REG_ASRCDR2 0x1C | ||
38 | #define REG_ASRCDR(i) ((i < 2) ? REG_ASRCDR1 : REG_ASRCDR2) | ||
39 | |||
40 | #define REG_ASRSTR 0x20 | ||
41 | #define REG_ASRRA 0x24 | ||
42 | #define REG_ASRRB 0x28 | ||
43 | #define REG_ASRRC 0x2C | ||
44 | #define REG_ASRPM1 0x40 | ||
45 | #define REG_ASRPM2 0x44 | ||
46 | #define REG_ASRPM3 0x48 | ||
47 | #define REG_ASRPM4 0x4C | ||
48 | #define REG_ASRPM5 0x50 | ||
49 | #define REG_ASRTFR1 0x54 | ||
50 | #define REG_ASRCCR 0x5C | ||
51 | |||
52 | #define REG_ASRDIA 0x60 | ||
53 | #define REG_ASRDOA 0x64 | ||
54 | #define REG_ASRDIB 0x68 | ||
55 | #define REG_ASRDOB 0x6C | ||
56 | #define REG_ASRDIC 0x70 | ||
57 | #define REG_ASRDOC 0x74 | ||
58 | #define REG_ASRDI(i) (REG_ASRDIA + (i << 3)) | ||
59 | #define REG_ASRDO(i) (REG_ASRDOA + (i << 3)) | ||
60 | #define REG_ASRDx(x, i) (x == IN ? REG_ASRDI(i) : REG_ASRDO(i)) | ||
61 | |||
62 | #define REG_ASRIDRHA 0x80 | ||
63 | #define REG_ASRIDRLA 0x84 | ||
64 | #define REG_ASRIDRHB 0x88 | ||
65 | #define REG_ASRIDRLB 0x8C | ||
66 | #define REG_ASRIDRHC 0x90 | ||
67 | #define REG_ASRIDRLC 0x94 | ||
68 | #define REG_ASRIDRH(i) (REG_ASRIDRHA + (i << 3)) | ||
69 | #define REG_ASRIDRL(i) (REG_ASRIDRLA + (i << 3)) | ||
70 | |||
71 | #define REG_ASR76K 0x98 | ||
72 | #define REG_ASR56K 0x9C | ||
73 | |||
74 | #define REG_ASRMCRA 0xA0 | ||
75 | #define REG_ASRFSTA 0xA4 | ||
76 | #define REG_ASRMCRB 0xA8 | ||
77 | #define REG_ASRFSTB 0xAC | ||
78 | #define REG_ASRMCRC 0xB0 | ||
79 | #define REG_ASRFSTC 0xB4 | ||
80 | #define REG_ASRMCR(i) (REG_ASRMCRA + (i << 3)) | ||
81 | #define REG_ASRFST(i) (REG_ASRFSTA + (i << 3)) | ||
82 | |||
83 | #define REG_ASRMCR1A 0xC0 | ||
84 | #define REG_ASRMCR1B 0xC4 | ||
85 | #define REG_ASRMCR1C 0xC8 | ||
86 | #define REG_ASRMCR1(i) (REG_ASRMCR1A + (i << 2)) | ||
87 | |||
88 | |||
89 | /* REG0 0x00 REG_ASRCTR */ | ||
90 | #define ASRCTR_ATSi_SHIFT(i) (20 + i) | ||
91 | #define ASRCTR_ATSi_MASK(i) (1 << ASRCTR_ATSi_SHIFT(i)) | ||
92 | #define ASRCTR_ATS(i) (1 << ASRCTR_ATSi_SHIFT(i)) | ||
93 | #define ASRCTR_USRi_SHIFT(i) (14 + (i << 1)) | ||
94 | #define ASRCTR_USRi_MASK(i) (1 << ASRCTR_USRi_SHIFT(i)) | ||
95 | #define ASRCTR_USR(i) (1 << ASRCTR_USRi_SHIFT(i)) | ||
96 | #define ASRCTR_IDRi_SHIFT(i) (13 + (i << 1)) | ||
97 | #define ASRCTR_IDRi_MASK(i) (1 << ASRCTR_IDRi_SHIFT(i)) | ||
98 | #define ASRCTR_IDR(i) (1 << ASRCTR_IDRi_SHIFT(i)) | ||
99 | #define ASRCTR_SRST_SHIFT 4 | ||
100 | #define ASRCTR_SRST_MASK (1 << ASRCTR_SRST_SHIFT) | ||
101 | #define ASRCTR_SRST (1 << ASRCTR_SRST_SHIFT) | ||
102 | #define ASRCTR_ASRCEi_SHIFT(i) (1 + i) | ||
103 | #define ASRCTR_ASRCEi_MASK(i) (1 << ASRCTR_ASRCEi_SHIFT(i)) | ||
104 | #define ASRCTR_ASRCE(i) (1 << ASRCTR_ASRCEi_SHIFT(i)) | ||
105 | #define ASRCTR_ASRCEi_ALL_MASK (0x7 << ASRCTR_ASRCEi_SHIFT(0)) | ||
106 | #define ASRCTR_ASRCEN_SHIFT 0 | ||
107 | #define ASRCTR_ASRCEN_MASK (1 << ASRCTR_ASRCEN_SHIFT) | ||
108 | #define ASRCTR_ASRCEN (1 << ASRCTR_ASRCEN_SHIFT) | ||
109 | |||
110 | /* REG1 0x04 REG_ASRIER */ | ||
111 | #define ASRIER_AFPWE_SHIFT 7 | ||
112 | #define ASRIER_AFPWE_MASK (1 << ASRIER_AFPWE_SHIFT) | ||
113 | #define ASRIER_AFPWE (1 << ASRIER_AFPWE_SHIFT) | ||
114 | #define ASRIER_AOLIE_SHIFT 6 | ||
115 | #define ASRIER_AOLIE_MASK (1 << ASRIER_AOLIE_SHIFT) | ||
116 | #define ASRIER_AOLIE (1 << ASRIER_AOLIE_SHIFT) | ||
117 | #define ASRIER_ADOEi_SHIFT(i) (3 + i) | ||
118 | #define ASRIER_ADOEi_MASK(i) (1 << ASRIER_ADOEi_SHIFT(i)) | ||
119 | #define ASRIER_ADOE(i) (1 << ASRIER_ADOEi_SHIFT(i)) | ||
120 | #define ASRIER_ADIEi_SHIFT(i) (0 + i) | ||
121 | #define ASRIER_ADIEi_MASK(i) (1 << ASRIER_ADIEi_SHIFT(i)) | ||
122 | #define ASRIER_ADIE(i) (1 << ASRIER_ADIEi_SHIFT(i)) | ||
123 | |||
124 | /* REG2 0x0C REG_ASRCNCR */ | ||
125 | #define ASRCNCR_ANCi_SHIFT(i, b) (b * i) | ||
126 | #define ASRCNCR_ANCi_MASK(i, b) (((1 << b) - 1) << ASRCNCR_ANCi_SHIFT(i, b)) | ||
127 | #define ASRCNCR_ANCi(i, v, b) ((v << ASRCNCR_ANCi_SHIFT(i, b)) & ASRCNCR_ANCi_MASK(i, b)) | ||
128 | |||
129 | /* REG3 0x10 REG_ASRCFG */ | ||
130 | #define ASRCFG_INIRQi_SHIFT(i) (21 + i) | ||
131 | #define ASRCFG_INIRQi_MASK(i) (1 << ASRCFG_INIRQi_SHIFT(i)) | ||
132 | #define ASRCFG_INIRQi (1 << ASRCFG_INIRQi_SHIFT(i)) | ||
133 | #define ASRCFG_NDPRi_SHIFT(i) (18 + i) | ||
134 | #define ASRCFG_NDPRi_MASK(i) (1 << ASRCFG_NDPRi_SHIFT(i)) | ||
135 | #define ASRCFG_NDPRi (1 << ASRCFG_NDPRi_SHIFT(i)) | ||
136 | #define ASRCFG_POSTMODi_SHIFT(i) (8 + (i << 2)) | ||
137 | #define ASRCFG_POSTMODi_WIDTH 2 | ||
138 | #define ASRCFG_POSTMODi_MASK(i) (((1 << ASRCFG_POSTMODi_WIDTH) - 1) << ASRCFG_POSTMODi_SHIFT(i)) | ||
139 | #define ASRCFG_POSTMOD(i, v) ((v) << ASRCFG_POSTMODi_SHIFT(i)) | ||
140 | #define ASRCFG_POSTMODi_UP(i) (0 << ASRCFG_POSTMODi_SHIFT(i)) | ||
141 | #define ASRCFG_POSTMODi_DCON(i) (1 << ASRCFG_POSTMODi_SHIFT(i)) | ||
142 | #define ASRCFG_POSTMODi_DOWN(i) (2 << ASRCFG_POSTMODi_SHIFT(i)) | ||
143 | #define ASRCFG_PREMODi_SHIFT(i) (6 + (i << 2)) | ||
144 | #define ASRCFG_PREMODi_WIDTH 2 | ||
145 | #define ASRCFG_PREMODi_MASK(i) (((1 << ASRCFG_PREMODi_WIDTH) - 1) << ASRCFG_PREMODi_SHIFT(i)) | ||
146 | #define ASRCFG_PREMOD(i, v) ((v) << ASRCFG_PREMODi_SHIFT(i)) | ||
147 | #define ASRCFG_PREMODi_UP(i) (0 << ASRCFG_PREMODi_SHIFT(i)) | ||
148 | #define ASRCFG_PREMODi_DCON(i) (1 << ASRCFG_PREMODi_SHIFT(i)) | ||
149 | #define ASRCFG_PREMODi_DOWN(i) (2 << ASRCFG_PREMODi_SHIFT(i)) | ||
150 | #define ASRCFG_PREMODi_BYPASS(i) (3 << ASRCFG_PREMODi_SHIFT(i)) | ||
151 | |||
152 | /* REG4 0x14 REG_ASRCSR */ | ||
153 | #define ASRCSR_AxCSi_WIDTH 4 | ||
154 | #define ASRCSR_AxCSi_MASK ((1 << ASRCSR_AxCSi_WIDTH) - 1) | ||
155 | #define ASRCSR_AOCSi_SHIFT(i) (12 + (i << 2)) | ||
156 | #define ASRCSR_AOCSi_MASK(i) (((1 << ASRCSR_AxCSi_WIDTH) - 1) << ASRCSR_AOCSi_SHIFT(i)) | ||
157 | #define ASRCSR_AOCS(i, v) ((v) << ASRCSR_AOCSi_SHIFT(i)) | ||
158 | #define ASRCSR_AICSi_SHIFT(i) (i << 2) | ||
159 | #define ASRCSR_AICSi_MASK(i) (((1 << ASRCSR_AxCSi_WIDTH) - 1) << ASRCSR_AICSi_SHIFT(i)) | ||
160 | #define ASRCSR_AICS(i, v) ((v) << ASRCSR_AICSi_SHIFT(i)) | ||
161 | |||
162 | /* REG5&6 0x18 & 0x1C REG_ASRCDR1 & ASRCDR2 */ | ||
163 | #define ASRCDRi_AxCPi_WIDTH 3 | ||
164 | #define ASRCDRi_AICPi_SHIFT(i) (0 + (i % 2) * 6) | ||
165 | #define ASRCDRi_AICPi_MASK(i) (((1 << ASRCDRi_AxCPi_WIDTH) - 1) << ASRCDRi_AICPi_SHIFT(i)) | ||
166 | #define ASRCDRi_AICP(i, v) ((v) << ASRCDRi_AICPi_SHIFT(i)) | ||
167 | #define ASRCDRi_AICDi_SHIFT(i) (3 + (i % 2) * 6) | ||
168 | #define ASRCDRi_AICDi_MASK(i) (((1 << ASRCDRi_AxCPi_WIDTH) - 1) << ASRCDRi_AICDi_SHIFT(i)) | ||
169 | #define ASRCDRi_AICD(i, v) ((v) << ASRCDRi_AICDi_SHIFT(i)) | ||
170 | #define ASRCDRi_AOCPi_SHIFT(i) ((i < 2) ? 12 + i * 6 : 6) | ||
171 | #define ASRCDRi_AOCPi_MASK(i) (((1 << ASRCDRi_AxCPi_WIDTH) - 1) << ASRCDRi_AOCPi_SHIFT(i)) | ||
172 | #define ASRCDRi_AOCP(i, v) ((v) << ASRCDRi_AOCPi_SHIFT(i)) | ||
173 | #define ASRCDRi_AOCDi_SHIFT(i) ((i < 2) ? 15 + i * 6 : 9) | ||
174 | #define ASRCDRi_AOCDi_MASK(i) (((1 << ASRCDRi_AxCPi_WIDTH) - 1) << ASRCDRi_AOCDi_SHIFT(i)) | ||
175 | #define ASRCDRi_AOCD(i, v) ((v) << ASRCDRi_AOCDi_SHIFT(i)) | ||
176 | |||
177 | /* REG7 0x20 REG_ASRSTR */ | ||
178 | #define ASRSTR_DSLCNT_SHIFT 21 | ||
179 | #define ASRSTR_DSLCNT_MASK (1 << ASRSTR_DSLCNT_SHIFT) | ||
180 | #define ASRSTR_DSLCNT (1 << ASRSTR_DSLCNT_SHIFT) | ||
181 | #define ASRSTR_ATQOL_SHIFT 20 | ||
182 | #define ASRSTR_ATQOL_MASK (1 << ASRSTR_ATQOL_SHIFT) | ||
183 | #define ASRSTR_ATQOL (1 << ASRSTR_ATQOL_SHIFT) | ||
184 | #define ASRSTR_AOOLi_SHIFT(i) (17 + i) | ||
185 | #define ASRSTR_AOOLi_MASK(i) (1 << ASRSTR_AOOLi_SHIFT(i)) | ||
186 | #define ASRSTR_AOOL(i) (1 << ASRSTR_AOOLi_SHIFT(i)) | ||
187 | #define ASRSTR_AIOLi_SHIFT(i) (14 + i) | ||
188 | #define ASRSTR_AIOLi_MASK(i) (1 << ASRSTR_AIOLi_SHIFT(i)) | ||
189 | #define ASRSTR_AIOL(i) (1 << ASRSTR_AIOLi_SHIFT(i)) | ||
190 | #define ASRSTR_AODOi_SHIFT(i) (11 + i) | ||
191 | #define ASRSTR_AODOi_MASK(i) (1 << ASRSTR_AODOi_SHIFT(i)) | ||
192 | #define ASRSTR_AODO(i) (1 << ASRSTR_AODOi_SHIFT(i)) | ||
193 | #define ASRSTR_AIDUi_SHIFT(i) (8 + i) | ||
194 | #define ASRSTR_AIDUi_MASK(i) (1 << ASRSTR_AIDUi_SHIFT(i)) | ||
195 | #define ASRSTR_AIDU(i) (1 << ASRSTR_AIDUi_SHIFT(i)) | ||
196 | #define ASRSTR_FPWT_SHIFT 7 | ||
197 | #define ASRSTR_FPWT_MASK (1 << ASRSTR_FPWT_SHIFT) | ||
198 | #define ASRSTR_FPWT (1 << ASRSTR_FPWT_SHIFT) | ||
199 | #define ASRSTR_AOLE_SHIFT 6 | ||
200 | #define ASRSTR_AOLE_MASK (1 << ASRSTR_AOLE_SHIFT) | ||
201 | #define ASRSTR_AOLE (1 << ASRSTR_AOLE_SHIFT) | ||
202 | #define ASRSTR_AODEi_SHIFT(i) (3 + i) | ||
203 | #define ASRSTR_AODFi_MASK(i) (1 << ASRSTR_AODEi_SHIFT(i)) | ||
204 | #define ASRSTR_AODF(i) (1 << ASRSTR_AODEi_SHIFT(i)) | ||
205 | #define ASRSTR_AIDEi_SHIFT(i) (0 + i) | ||
206 | #define ASRSTR_AIDEi_MASK(i) (1 << ASRSTR_AIDEi_SHIFT(i)) | ||
207 | #define ASRSTR_AIDE(i) (1 << ASRSTR_AIDEi_SHIFT(i)) | ||
208 | |||
209 | /* REG10 0x54 REG_ASRTFR1 */ | ||
210 | #define ASRTFR1_TF_BASE_WIDTH 7 | ||
211 | #define ASRTFR1_TF_BASE_SHIFT 6 | ||
212 | #define ASRTFR1_TF_BASE_MASK (((1 << ASRTFR1_TF_BASE_WIDTH) - 1) << ASRTFR1_TF_BASE_SHIFT) | ||
213 | #define ASRTFR1_TF_BASE(i) ((i) << ASRTFR1_TF_BASE_SHIFT) | ||
214 | |||
215 | /* | ||
216 | * REG22 0xA0 REG_ASRMCRA | ||
217 | * REG24 0xA8 REG_ASRMCRB | ||
218 | * REG26 0xB0 REG_ASRMCRC | ||
219 | */ | ||
220 | #define ASRMCRi_ZEROBUFi_SHIFT 23 | ||
221 | #define ASRMCRi_ZEROBUFi_MASK (1 << ASRMCRi_ZEROBUFi_SHIFT) | ||
222 | #define ASRMCRi_ZEROBUFi (1 << ASRMCRi_ZEROBUFi_SHIFT) | ||
223 | #define ASRMCRi_EXTTHRSHi_SHIFT 22 | ||
224 | #define ASRMCRi_EXTTHRSHi_MASK (1 << ASRMCRi_EXTTHRSHi_SHIFT) | ||
225 | #define ASRMCRi_EXTTHRSHi (1 << ASRMCRi_EXTTHRSHi_SHIFT) | ||
226 | #define ASRMCRi_BUFSTALLi_SHIFT 21 | ||
227 | #define ASRMCRi_BUFSTALLi_MASK (1 << ASRMCRi_BUFSTALLi_SHIFT) | ||
228 | #define ASRMCRi_BUFSTALLi (1 << ASRMCRi_BUFSTALLi_SHIFT) | ||
229 | #define ASRMCRi_BYPASSPOLYi_SHIFT 20 | ||
230 | #define ASRMCRi_BYPASSPOLYi_MASK (1 << ASRMCRi_BYPASSPOLYi_SHIFT) | ||
231 | #define ASRMCRi_BYPASSPOLYi (1 << ASRMCRi_BYPASSPOLYi_SHIFT) | ||
232 | #define ASRMCRi_OUTFIFO_THRESHOLD_WIDTH 6 | ||
233 | #define ASRMCRi_OUTFIFO_THRESHOLD_SHIFT 12 | ||
234 | #define ASRMCRi_OUTFIFO_THRESHOLD_MASK (((1 << ASRMCRi_OUTFIFO_THRESHOLD_WIDTH) - 1) << ASRMCRi_OUTFIFO_THRESHOLD_SHIFT) | ||
235 | #define ASRMCRi_OUTFIFO_THRESHOLD(v) (((v) << ASRMCRi_OUTFIFO_THRESHOLD_SHIFT) & ASRMCRi_OUTFIFO_THRESHOLD_MASK) | ||
236 | #define ASRMCRi_RSYNIFi_SHIFT 11 | ||
237 | #define ASRMCRi_RSYNIFi_MASK (1 << ASRMCRi_RSYNIFi_SHIFT) | ||
238 | #define ASRMCRi_RSYNIFi (1 << ASRMCRi_RSYNIFi_SHIFT) | ||
239 | #define ASRMCRi_RSYNOFi_SHIFT 10 | ||
240 | #define ASRMCRi_RSYNOFi_MASK (1 << ASRMCRi_RSYNOFi_SHIFT) | ||
241 | #define ASRMCRi_RSYNOFi (1 << ASRMCRi_RSYNOFi_SHIFT) | ||
242 | #define ASRMCRi_INFIFO_THRESHOLD_WIDTH 6 | ||
243 | #define ASRMCRi_INFIFO_THRESHOLD_SHIFT 0 | ||
244 | #define ASRMCRi_INFIFO_THRESHOLD_MASK (((1 << ASRMCRi_INFIFO_THRESHOLD_WIDTH) - 1) << ASRMCRi_INFIFO_THRESHOLD_SHIFT) | ||
245 | #define ASRMCRi_INFIFO_THRESHOLD(v) (((v) << ASRMCRi_INFIFO_THRESHOLD_SHIFT) & ASRMCRi_INFIFO_THRESHOLD_MASK) | ||
246 | |||
247 | /* | ||
248 | * REG23 0xA4 REG_ASRFSTA | ||
249 | * REG25 0xAC REG_ASRFSTB | ||
250 | * REG27 0xB4 REG_ASRFSTC | ||
251 | */ | ||
252 | #define ASRFSTi_OAFi_SHIFT 23 | ||
253 | #define ASRFSTi_OAFi_MASK (1 << ASRFSTi_OAFi_SHIFT) | ||
254 | #define ASRFSTi_OAFi (1 << ASRFSTi_OAFi_SHIFT) | ||
255 | #define ASRFSTi_OUTPUT_FIFO_WIDTH 7 | ||
256 | #define ASRFSTi_OUTPUT_FIFO_SHIFT 12 | ||
257 | #define ASRFSTi_OUTPUT_FIFO_MASK (((1 << ASRFSTi_OUTPUT_FIFO_WIDTH) - 1) << ASRFSTi_OUTPUT_FIFO_SHIFT) | ||
258 | #define ASRFSTi_IAEi_SHIFT 11 | ||
259 | #define ASRFSTi_IAEi_MASK (1 << ASRFSTi_OAFi_SHIFT) | ||
260 | #define ASRFSTi_IAEi (1 << ASRFSTi_OAFi_SHIFT) | ||
261 | #define ASRFSTi_INPUT_FIFO_WIDTH 7 | ||
262 | #define ASRFSTi_INPUT_FIFO_SHIFT 0 | ||
263 | #define ASRFSTi_INPUT_FIFO_MASK ((1 << ASRFSTi_INPUT_FIFO_WIDTH) - 1) | ||
264 | |||
265 | /* REG28 0xC0 & 0xC4 & 0xC8 REG_ASRMCR1i */ | ||
266 | #define ASRMCR1i_IWD_WIDTH 3 | ||
267 | #define ASRMCR1i_IWD_SHIFT 9 | ||
268 | #define ASRMCR1i_IWD_MASK (((1 << ASRMCR1i_IWD_WIDTH) - 1) << ASRMCR1i_IWD_SHIFT) | ||
269 | #define ASRMCR1i_IWD(v) ((v) << ASRMCR1i_IWD_SHIFT) | ||
270 | #define ASRMCR1i_IMSB_SHIFT 8 | ||
271 | #define ASRMCR1i_IMSB_MASK (1 << ASRMCR1i_IMSB_SHIFT) | ||
272 | #define ASRMCR1i_IMSB_MSB (1 << ASRMCR1i_IMSB_SHIFT) | ||
273 | #define ASRMCR1i_IMSB_LSB (0 << ASRMCR1i_IMSB_SHIFT) | ||
274 | #define ASRMCR1i_OMSB_SHIFT 2 | ||
275 | #define ASRMCR1i_OMSB_MASK (1 << ASRMCR1i_OMSB_SHIFT) | ||
276 | #define ASRMCR1i_OMSB_MSB (1 << ASRMCR1i_OMSB_SHIFT) | ||
277 | #define ASRMCR1i_OMSB_LSB (0 << ASRMCR1i_OMSB_SHIFT) | ||
278 | #define ASRMCR1i_OSGN_SHIFT 1 | ||
279 | #define ASRMCR1i_OSGN_MASK (1 << ASRMCR1i_OSGN_SHIFT) | ||
280 | #define ASRMCR1i_OSGN (1 << ASRMCR1i_OSGN_SHIFT) | ||
281 | #define ASRMCR1i_OW16_SHIFT 0 | ||
282 | #define ASRMCR1i_OW16_MASK (1 << ASRMCR1i_OW16_SHIFT) | ||
283 | #define ASRMCR1i_OW16(v) ((v) << ASRMCR1i_OW16_SHIFT) | ||
284 | |||
285 | |||
286 | enum asrc_pair_index { | ||
287 | ASRC_INVALID_PAIR = -1, | ||
288 | ASRC_PAIR_A = 0, | ||
289 | ASRC_PAIR_B = 1, | ||
290 | ASRC_PAIR_C = 2, | ||
291 | }; | ||
292 | |||
293 | #define ASRC_PAIR_MAX_NUM (ASRC_PAIR_C + 1) | ||
294 | |||
295 | enum asrc_inclk { | ||
296 | INCLK_NONE = 0x03, | ||
297 | INCLK_ESAI_RX = 0x00, | ||
298 | INCLK_SSI1_RX = 0x01, | ||
299 | INCLK_SSI2_RX = 0x02, | ||
300 | INCLK_SSI3_RX = 0x07, | ||
301 | INCLK_SPDIF_RX = 0x04, | ||
302 | INCLK_MLB_CLK = 0x05, | ||
303 | INCLK_PAD = 0x06, | ||
304 | INCLK_ESAI_TX = 0x08, | ||
305 | INCLK_SSI1_TX = 0x09, | ||
306 | INCLK_SSI2_TX = 0x0a, | ||
307 | INCLK_SSI3_TX = 0x0b, | ||
308 | INCLK_SPDIF_TX = 0x0c, | ||
309 | INCLK_ASRCK1_CLK = 0x0f, | ||
310 | }; | ||
311 | |||
312 | enum asrc_outclk { | ||
313 | OUTCLK_NONE = 0x03, | ||
314 | OUTCLK_ESAI_TX = 0x00, | ||
315 | OUTCLK_SSI1_TX = 0x01, | ||
316 | OUTCLK_SSI2_TX = 0x02, | ||
317 | OUTCLK_SSI3_TX = 0x07, | ||
318 | OUTCLK_SPDIF_TX = 0x04, | ||
319 | OUTCLK_MLB_CLK = 0x05, | ||
320 | OUTCLK_PAD = 0x06, | ||
321 | OUTCLK_ESAI_RX = 0x08, | ||
322 | OUTCLK_SSI1_RX = 0x09, | ||
323 | OUTCLK_SSI2_RX = 0x0a, | ||
324 | OUTCLK_SSI3_RX = 0x0b, | ||
325 | OUTCLK_SPDIF_RX = 0x0c, | ||
326 | OUTCLK_ASRCK1_CLK = 0x0f, | ||
327 | }; | ||
328 | |||
329 | #define ASRC_CLK_MAX_NUM 16 | ||
330 | |||
331 | enum asrc_word_width { | ||
332 | ASRC_WIDTH_24_BIT = 0, | ||
333 | ASRC_WIDTH_16_BIT = 1, | ||
334 | ASRC_WIDTH_8_BIT = 2, | ||
335 | }; | ||
336 | |||
337 | struct asrc_config { | ||
338 | enum asrc_pair_index pair; | ||
339 | unsigned int channel_num; | ||
340 | unsigned int buffer_num; | ||
341 | unsigned int dma_buffer_size; | ||
342 | unsigned int input_sample_rate; | ||
343 | unsigned int output_sample_rate; | ||
344 | enum asrc_word_width input_word_width; | ||
345 | enum asrc_word_width output_word_width; | ||
346 | enum asrc_inclk inclk; | ||
347 | enum asrc_outclk outclk; | ||
348 | }; | ||
349 | |||
350 | struct asrc_req { | ||
351 | unsigned int chn_num; | ||
352 | enum asrc_pair_index index; | ||
353 | }; | ||
354 | |||
355 | struct asrc_querybuf { | ||
356 | unsigned int buffer_index; | ||
357 | unsigned int input_length; | ||
358 | unsigned int output_length; | ||
359 | unsigned long input_offset; | ||
360 | unsigned long output_offset; | ||
361 | }; | ||
362 | |||
363 | struct asrc_convert_buffer { | ||
364 | void *input_buffer_vaddr; | ||
365 | void *output_buffer_vaddr; | ||
366 | unsigned int input_buffer_length; | ||
367 | unsigned int output_buffer_length; | ||
368 | }; | ||
369 | |||
370 | struct asrc_status_flags { | ||
371 | enum asrc_pair_index index; | ||
372 | unsigned int overload_error; | ||
373 | }; | ||
374 | |||
375 | enum asrc_error_status { | ||
376 | ASRC_TASK_Q_OVERLOAD = 0x01, | ||
377 | ASRC_OUTPUT_TASK_OVERLOAD = 0x02, | ||
378 | ASRC_INPUT_TASK_OVERLOAD = 0x04, | ||
379 | ASRC_OUTPUT_BUFFER_OVERFLOW = 0x08, | ||
380 | ASRC_INPUT_BUFFER_UNDERRUN = 0x10, | ||
381 | }; | ||
382 | |||
383 | struct dma_block { | ||
384 | dma_addr_t dma_paddr; | ||
385 | void *dma_vaddr; | ||
386 | unsigned int length; | ||
387 | }; | ||
388 | |||
389 | /** | ||
390 | * fsl_asrc_pair: ASRC Pair private data | ||
391 | * | ||
392 | * @asrc_priv: pointer to its parent module | ||
393 | * @config: configuration profile | ||
394 | * @error: error record | ||
395 | * @index: pair index (ASRC_PAIR_A, ASRC_PAIR_B, ASRC_PAIR_C) | ||
396 | * @channels: occupied channel number | ||
397 | * @desc: input and output dma descriptors | ||
398 | * @dma_chan: inputer and output DMA channels | ||
399 | * @dma_data: private dma data | ||
400 | * @pos: hardware pointer position | ||
401 | * @private: pair private area | ||
402 | */ | ||
403 | struct fsl_asrc_pair { | ||
404 | struct fsl_asrc *asrc_priv; | ||
405 | struct asrc_config *config; | ||
406 | unsigned int error; | ||
407 | |||
408 | enum asrc_pair_index index; | ||
409 | unsigned int channels; | ||
410 | |||
411 | struct dma_async_tx_descriptor *desc[2]; | ||
412 | struct dma_chan *dma_chan[2]; | ||
413 | struct imx_dma_data dma_data; | ||
414 | unsigned int pos; | ||
415 | |||
416 | void *private; | ||
417 | }; | ||
418 | |||
419 | /** | ||
420 | * fsl_asrc_pair: ASRC private data | ||
421 | * | ||
422 | * @dma_params_rx: DMA parameters for receive channel | ||
423 | * @dma_params_tx: DMA parameters for transmit channel | ||
424 | * @pdev: platform device pointer | ||
425 | * @regmap: regmap handler | ||
426 | * @paddr: physical address to the base address of registers | ||
427 | * @mem_clk: clock source to access register | ||
428 | * @ipg_clk: clock source to drive peripheral | ||
429 | * @asrck_clk: clock sources to driver ASRC internal logic | ||
430 | * @lock: spin lock for resource protection | ||
431 | * @pair: pair pointers | ||
432 | * @channel_bits: width of ASRCNCR register for each pair | ||
433 | * @channel_avail: non-occupied channel numbers | ||
434 | * @asrc_rate: default sample rate for ASoC Back-Ends | ||
435 | * @asrc_width: default sample width for ASoC Back-Ends | ||
436 | * @name: driver name | ||
437 | */ | ||
438 | struct fsl_asrc { | ||
439 | struct snd_dmaengine_dai_dma_data dma_params_rx; | ||
440 | struct snd_dmaengine_dai_dma_data dma_params_tx; | ||
441 | struct platform_device *pdev; | ||
442 | struct regmap *regmap; | ||
443 | unsigned long paddr; | ||
444 | struct clk *mem_clk; | ||
445 | struct clk *ipg_clk; | ||
446 | struct clk *asrck_clk[ASRC_CLK_MAX_NUM]; | ||
447 | spinlock_t lock; | ||
448 | |||
449 | struct fsl_asrc_pair *pair[ASRC_PAIR_MAX_NUM]; | ||
450 | unsigned int channel_bits; | ||
451 | unsigned int channel_avail; | ||
452 | |||
453 | int asrc_rate; | ||
454 | int asrc_width; | ||
455 | |||
456 | char name[32]; | ||
457 | }; | ||
458 | |||
459 | extern struct snd_soc_platform_driver fsl_asrc_platform; | ||
460 | struct dma_chan *fsl_asrc_get_dma_channel(struct fsl_asrc_pair *pair, bool dir); | ||
461 | #endif /* _FSL_ASRC_H */ | ||
diff --git a/sound/soc/fsl/fsl_asrc_dma.c b/sound/soc/fsl/fsl_asrc_dma.c new file mode 100644 index 000000000000..5b1e73e97817 --- /dev/null +++ b/sound/soc/fsl/fsl_asrc_dma.c | |||
@@ -0,0 +1,386 @@ | |||
1 | /* | ||
2 | * Freescale ASRC ALSA SoC Platform (DMA) driver | ||
3 | * | ||
4 | * Copyright (C) 2014 Freescale Semiconductor, Inc. | ||
5 | * | ||
6 | * Author: Nicolin Chen <nicoleotsuka@gmail.com> | ||
7 | * | ||
8 | * This file is licensed under the terms of the GNU General Public License | ||
9 | * version 2. This program is licensed "as is" without any warranty of any | ||
10 | * kind, whether express or implied. | ||
11 | */ | ||
12 | |||
13 | #include <linux/dma-mapping.h> | ||
14 | #include <linux/module.h> | ||
15 | #include <linux/platform_data/dma-imx.h> | ||
16 | #include <sound/dmaengine_pcm.h> | ||
17 | #include <sound/pcm_params.h> | ||
18 | |||
19 | #include "fsl_asrc.h" | ||
20 | |||
21 | #define FSL_ASRC_DMABUF_SIZE (256 * 1024) | ||
22 | |||
23 | static struct snd_pcm_hardware snd_imx_hardware = { | ||
24 | .info = SNDRV_PCM_INFO_INTERLEAVED | | ||
25 | SNDRV_PCM_INFO_BLOCK_TRANSFER | | ||
26 | SNDRV_PCM_INFO_MMAP | | ||
27 | SNDRV_PCM_INFO_MMAP_VALID | | ||
28 | SNDRV_PCM_INFO_PAUSE | | ||
29 | SNDRV_PCM_INFO_RESUME, | ||
30 | .buffer_bytes_max = FSL_ASRC_DMABUF_SIZE, | ||
31 | .period_bytes_min = 128, | ||
32 | .period_bytes_max = 65535, /* Limited by SDMA engine */ | ||
33 | .periods_min = 2, | ||
34 | .periods_max = 255, | ||
35 | .fifo_size = 0, | ||
36 | }; | ||
37 | |||
38 | static bool filter(struct dma_chan *chan, void *param) | ||
39 | { | ||
40 | if (!imx_dma_is_general_purpose(chan)) | ||
41 | return false; | ||
42 | |||
43 | chan->private = param; | ||
44 | |||
45 | return true; | ||
46 | } | ||
47 | |||
48 | static void fsl_asrc_dma_complete(void *arg) | ||
49 | { | ||
50 | struct snd_pcm_substream *substream = arg; | ||
51 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
52 | struct fsl_asrc_pair *pair = runtime->private_data; | ||
53 | |||
54 | pair->pos += snd_pcm_lib_period_bytes(substream); | ||
55 | if (pair->pos >= snd_pcm_lib_buffer_bytes(substream)) | ||
56 | pair->pos = 0; | ||
57 | |||
58 | snd_pcm_period_elapsed(substream); | ||
59 | } | ||
60 | |||
61 | static int fsl_asrc_dma_prepare_and_submit(struct snd_pcm_substream *substream) | ||
62 | { | ||
63 | u8 dir = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? OUT : IN; | ||
64 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
65 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
66 | struct fsl_asrc_pair *pair = runtime->private_data; | ||
67 | struct device *dev = rtd->platform->dev; | ||
68 | unsigned long flags = DMA_CTRL_ACK; | ||
69 | |||
70 | /* Prepare and submit Front-End DMA channel */ | ||
71 | if (!substream->runtime->no_period_wakeup) | ||
72 | flags |= DMA_PREP_INTERRUPT; | ||
73 | |||
74 | pair->pos = 0; | ||
75 | pair->desc[!dir] = dmaengine_prep_dma_cyclic( | ||
76 | pair->dma_chan[!dir], runtime->dma_addr, | ||
77 | snd_pcm_lib_buffer_bytes(substream), | ||
78 | snd_pcm_lib_period_bytes(substream), | ||
79 | dir == OUT ? DMA_TO_DEVICE : DMA_FROM_DEVICE, flags); | ||
80 | if (!pair->desc[!dir]) { | ||
81 | dev_err(dev, "failed to prepare slave DMA for Front-End\n"); | ||
82 | return -ENOMEM; | ||
83 | } | ||
84 | |||
85 | pair->desc[!dir]->callback = fsl_asrc_dma_complete; | ||
86 | pair->desc[!dir]->callback_param = substream; | ||
87 | |||
88 | dmaengine_submit(pair->desc[!dir]); | ||
89 | |||
90 | /* Prepare and submit Back-End DMA channel */ | ||
91 | pair->desc[dir] = dmaengine_prep_dma_cyclic( | ||
92 | pair->dma_chan[dir], 0xffff, 64, 64, DMA_DEV_TO_DEV, 0); | ||
93 | if (!pair->desc[dir]) { | ||
94 | dev_err(dev, "failed to prepare slave DMA for Back-End\n"); | ||
95 | return -ENOMEM; | ||
96 | } | ||
97 | |||
98 | dmaengine_submit(pair->desc[dir]); | ||
99 | |||
100 | return 0; | ||
101 | } | ||
102 | |||
103 | static int fsl_asrc_dma_trigger(struct snd_pcm_substream *substream, int cmd) | ||
104 | { | ||
105 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
106 | struct fsl_asrc_pair *pair = runtime->private_data; | ||
107 | int ret; | ||
108 | |||
109 | switch (cmd) { | ||
110 | case SNDRV_PCM_TRIGGER_START: | ||
111 | case SNDRV_PCM_TRIGGER_RESUME: | ||
112 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | ||
113 | ret = fsl_asrc_dma_prepare_and_submit(substream); | ||
114 | if (ret) | ||
115 | return ret; | ||
116 | dma_async_issue_pending(pair->dma_chan[IN]); | ||
117 | dma_async_issue_pending(pair->dma_chan[OUT]); | ||
118 | break; | ||
119 | case SNDRV_PCM_TRIGGER_STOP: | ||
120 | case SNDRV_PCM_TRIGGER_SUSPEND: | ||
121 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | ||
122 | dmaengine_terminate_all(pair->dma_chan[OUT]); | ||
123 | dmaengine_terminate_all(pair->dma_chan[IN]); | ||
124 | break; | ||
125 | default: | ||
126 | return -EINVAL; | ||
127 | } | ||
128 | |||
129 | return 0; | ||
130 | } | ||
131 | |||
132 | static int fsl_asrc_dma_hw_params(struct snd_pcm_substream *substream, | ||
133 | struct snd_pcm_hw_params *params) | ||
134 | { | ||
135 | enum dma_slave_buswidth buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES; | ||
136 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
137 | bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; | ||
138 | struct snd_dmaengine_dai_dma_data *dma_params_fe = NULL; | ||
139 | struct snd_dmaengine_dai_dma_data *dma_params_be = NULL; | ||
140 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
141 | struct fsl_asrc_pair *pair = runtime->private_data; | ||
142 | struct fsl_asrc *asrc_priv = pair->asrc_priv; | ||
143 | struct dma_slave_config config_fe, config_be; | ||
144 | enum asrc_pair_index index = pair->index; | ||
145 | struct device *dev = rtd->platform->dev; | ||
146 | int stream = substream->stream; | ||
147 | struct imx_dma_data *tmp_data; | ||
148 | struct snd_soc_dpcm *dpcm; | ||
149 | struct dma_chan *tmp_chan; | ||
150 | struct device *dev_be; | ||
151 | u8 dir = tx ? OUT : IN; | ||
152 | dma_cap_mask_t mask; | ||
153 | int ret; | ||
154 | |||
155 | /* Fetch the Back-End dma_data from DPCM */ | ||
156 | list_for_each_entry(dpcm, &rtd->dpcm[stream].be_clients, list_be) { | ||
157 | struct snd_soc_pcm_runtime *be = dpcm->be; | ||
158 | struct snd_pcm_substream *substream_be; | ||
159 | struct snd_soc_dai *dai = be->cpu_dai; | ||
160 | |||
161 | if (dpcm->fe != rtd) | ||
162 | continue; | ||
163 | |||
164 | substream_be = snd_soc_dpcm_get_substream(be, stream); | ||
165 | dma_params_be = snd_soc_dai_get_dma_data(dai, substream_be); | ||
166 | dev_be = dai->dev; | ||
167 | break; | ||
168 | } | ||
169 | |||
170 | if (!dma_params_be) { | ||
171 | dev_err(dev, "failed to get the substream of Back-End\n"); | ||
172 | return -EINVAL; | ||
173 | } | ||
174 | |||
175 | /* Override dma_data of the Front-End and config its dmaengine */ | ||
176 | dma_params_fe = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); | ||
177 | dma_params_fe->addr = asrc_priv->paddr + REG_ASRDx(!dir, index); | ||
178 | dma_params_fe->maxburst = dma_params_be->maxburst; | ||
179 | |||
180 | pair->dma_chan[!dir] = fsl_asrc_get_dma_channel(pair, !dir); | ||
181 | if (!pair->dma_chan[!dir]) { | ||
182 | dev_err(dev, "failed to request DMA channel\n"); | ||
183 | return -EINVAL; | ||
184 | } | ||
185 | |||
186 | memset(&config_fe, 0, sizeof(config_fe)); | ||
187 | ret = snd_dmaengine_pcm_prepare_slave_config(substream, params, &config_fe); | ||
188 | if (ret) { | ||
189 | dev_err(dev, "failed to prepare DMA config for Front-End\n"); | ||
190 | return ret; | ||
191 | } | ||
192 | |||
193 | ret = dmaengine_slave_config(pair->dma_chan[!dir], &config_fe); | ||
194 | if (ret) { | ||
195 | dev_err(dev, "failed to config DMA channel for Front-End\n"); | ||
196 | return ret; | ||
197 | } | ||
198 | |||
199 | /* Request and config DMA channel for Back-End */ | ||
200 | dma_cap_zero(mask); | ||
201 | dma_cap_set(DMA_SLAVE, mask); | ||
202 | dma_cap_set(DMA_CYCLIC, mask); | ||
203 | |||
204 | /* Get DMA request of Back-End */ | ||
205 | tmp_chan = dma_request_slave_channel(dev_be, tx ? "tx" : "rx"); | ||
206 | tmp_data = tmp_chan->private; | ||
207 | pair->dma_data.dma_request = tmp_data->dma_request; | ||
208 | dma_release_channel(tmp_chan); | ||
209 | |||
210 | /* Get DMA request of Front-End */ | ||
211 | tmp_chan = fsl_asrc_get_dma_channel(pair, dir); | ||
212 | tmp_data = tmp_chan->private; | ||
213 | pair->dma_data.dma_request2 = tmp_data->dma_request; | ||
214 | pair->dma_data.peripheral_type = tmp_data->peripheral_type; | ||
215 | pair->dma_data.priority = tmp_data->priority; | ||
216 | dma_release_channel(tmp_chan); | ||
217 | |||
218 | pair->dma_chan[dir] = dma_request_channel(mask, filter, &pair->dma_data); | ||
219 | if (!pair->dma_chan[dir]) { | ||
220 | dev_err(dev, "failed to request DMA channel for Back-End\n"); | ||
221 | return -EINVAL; | ||
222 | } | ||
223 | |||
224 | if (asrc_priv->asrc_width == 16) | ||
225 | buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES; | ||
226 | else | ||
227 | buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES; | ||
228 | |||
229 | config_be.direction = DMA_DEV_TO_DEV; | ||
230 | config_be.src_addr_width = buswidth; | ||
231 | config_be.src_maxburst = dma_params_be->maxburst; | ||
232 | config_be.dst_addr_width = buswidth; | ||
233 | config_be.dst_maxburst = dma_params_be->maxburst; | ||
234 | |||
235 | if (tx) { | ||
236 | config_be.src_addr = asrc_priv->paddr + REG_ASRDO(index); | ||
237 | config_be.dst_addr = dma_params_be->addr; | ||
238 | } else { | ||
239 | config_be.dst_addr = asrc_priv->paddr + REG_ASRDI(index); | ||
240 | config_be.src_addr = dma_params_be->addr; | ||
241 | } | ||
242 | |||
243 | ret = dmaengine_slave_config(pair->dma_chan[dir], &config_be); | ||
244 | if (ret) { | ||
245 | dev_err(dev, "failed to config DMA channel for Back-End\n"); | ||
246 | return ret; | ||
247 | } | ||
248 | |||
249 | snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); | ||
250 | |||
251 | return 0; | ||
252 | } | ||
253 | |||
254 | static int fsl_asrc_dma_hw_free(struct snd_pcm_substream *substream) | ||
255 | { | ||
256 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
257 | struct fsl_asrc_pair *pair = runtime->private_data; | ||
258 | |||
259 | snd_pcm_set_runtime_buffer(substream, NULL); | ||
260 | |||
261 | if (pair->dma_chan[IN]) | ||
262 | dma_release_channel(pair->dma_chan[IN]); | ||
263 | |||
264 | if (pair->dma_chan[OUT]) | ||
265 | dma_release_channel(pair->dma_chan[OUT]); | ||
266 | |||
267 | pair->dma_chan[IN] = NULL; | ||
268 | pair->dma_chan[OUT] = NULL; | ||
269 | |||
270 | return 0; | ||
271 | } | ||
272 | |||
273 | static int fsl_asrc_dma_startup(struct snd_pcm_substream *substream) | ||
274 | { | ||
275 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
276 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
277 | struct device *dev = rtd->platform->dev; | ||
278 | struct fsl_asrc *asrc_priv = dev_get_drvdata(dev); | ||
279 | struct fsl_asrc_pair *pair; | ||
280 | |||
281 | pair = kzalloc(sizeof(struct fsl_asrc_pair), GFP_KERNEL); | ||
282 | if (!pair) { | ||
283 | dev_err(dev, "failed to allocate pair\n"); | ||
284 | return -ENOMEM; | ||
285 | } | ||
286 | |||
287 | pair->asrc_priv = asrc_priv; | ||
288 | |||
289 | runtime->private_data = pair; | ||
290 | |||
291 | snd_pcm_hw_constraint_integer(substream->runtime, | ||
292 | SNDRV_PCM_HW_PARAM_PERIODS); | ||
293 | snd_soc_set_runtime_hwparams(substream, &snd_imx_hardware); | ||
294 | |||
295 | return 0; | ||
296 | } | ||
297 | |||
298 | static int fsl_asrc_dma_shutdown(struct snd_pcm_substream *substream) | ||
299 | { | ||
300 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
301 | struct fsl_asrc_pair *pair = runtime->private_data; | ||
302 | struct fsl_asrc *asrc_priv = pair->asrc_priv; | ||
303 | |||
304 | if (pair && asrc_priv->pair[pair->index] == pair) | ||
305 | asrc_priv->pair[pair->index] = NULL; | ||
306 | |||
307 | kfree(pair); | ||
308 | |||
309 | return 0; | ||
310 | } | ||
311 | |||
312 | static snd_pcm_uframes_t fsl_asrc_dma_pcm_pointer(struct snd_pcm_substream *substream) | ||
313 | { | ||
314 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
315 | struct fsl_asrc_pair *pair = runtime->private_data; | ||
316 | |||
317 | return bytes_to_frames(substream->runtime, pair->pos); | ||
318 | } | ||
319 | |||
320 | static struct snd_pcm_ops fsl_asrc_dma_pcm_ops = { | ||
321 | .ioctl = snd_pcm_lib_ioctl, | ||
322 | .hw_params = fsl_asrc_dma_hw_params, | ||
323 | .hw_free = fsl_asrc_dma_hw_free, | ||
324 | .trigger = fsl_asrc_dma_trigger, | ||
325 | .open = fsl_asrc_dma_startup, | ||
326 | .close = fsl_asrc_dma_shutdown, | ||
327 | .pointer = fsl_asrc_dma_pcm_pointer, | ||
328 | }; | ||
329 | |||
330 | static int fsl_asrc_dma_pcm_new(struct snd_soc_pcm_runtime *rtd) | ||
331 | { | ||
332 | struct snd_card *card = rtd->card->snd_card; | ||
333 | struct snd_pcm_substream *substream; | ||
334 | struct snd_pcm *pcm = rtd->pcm; | ||
335 | int ret, i; | ||
336 | |||
337 | ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32)); | ||
338 | if (ret) { | ||
339 | dev_err(card->dev, "failed to set DMA mask\n"); | ||
340 | return ret; | ||
341 | } | ||
342 | |||
343 | for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_LAST; i++) { | ||
344 | substream = pcm->streams[i].substream; | ||
345 | if (!substream) | ||
346 | continue; | ||
347 | |||
348 | ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, pcm->card->dev, | ||
349 | FSL_ASRC_DMABUF_SIZE, &substream->dma_buffer); | ||
350 | if (ret) { | ||
351 | dev_err(card->dev, "failed to allocate DMA buffer\n"); | ||
352 | goto err; | ||
353 | } | ||
354 | } | ||
355 | |||
356 | return 0; | ||
357 | |||
358 | err: | ||
359 | if (--i == 0 && pcm->streams[i].substream) | ||
360 | snd_dma_free_pages(&pcm->streams[i].substream->dma_buffer); | ||
361 | |||
362 | return ret; | ||
363 | } | ||
364 | |||
365 | static void fsl_asrc_dma_pcm_free(struct snd_pcm *pcm) | ||
366 | { | ||
367 | struct snd_pcm_substream *substream; | ||
368 | int i; | ||
369 | |||
370 | for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_LAST; i++) { | ||
371 | substream = pcm->streams[i].substream; | ||
372 | if (!substream) | ||
373 | continue; | ||
374 | |||
375 | snd_dma_free_pages(&substream->dma_buffer); | ||
376 | substream->dma_buffer.area = NULL; | ||
377 | substream->dma_buffer.addr = 0; | ||
378 | } | ||
379 | } | ||
380 | |||
381 | struct snd_soc_platform_driver fsl_asrc_platform = { | ||
382 | .ops = &fsl_asrc_dma_pcm_ops, | ||
383 | .pcm_new = fsl_asrc_dma_pcm_new, | ||
384 | .pcm_free = fsl_asrc_dma_pcm_free, | ||
385 | }; | ||
386 | EXPORT_SYMBOL_GPL(fsl_asrc_platform); | ||