diff options
author | Peter Ujfalusi <peter.ujfalusi@ti.com> | 2018-11-08 02:29:58 -0500 |
---|---|---|
committer | Mark Brown <broonie@kernel.org> | 2018-11-13 12:50:24 -0500 |
commit | 9c34d023dc35a4b3d005d94db512742dfcdc8f82 (patch) | |
tree | f99b065aff9521ad4614c94f31c1752cf2d53f8a | |
parent | be51c576e8495fd2cffbababad6de7e0a0a562ba (diff) |
ASoC: omap-mcbsp: Re-arrange files for core McBSP and Sidetone function split
The mcbsp.c was copied a while back from arch/arm/plat-omap/mcbsp.c and it
contained a mix of McBSP and McBSP sidetone functions.
Create new file structure with the following split:
omap-mcbsp.c - McBSP related functions
omap-mcbsp-st.c - McBSP sidetone functionality
omap-mcbsp-priv.h - Private header for internal use
omap-mcbsp.h - Header for user drivers
I have tried to do the code move with minimal code change, cleanup patches
can be based on the new structure.
Signed-off-by: Peter Ujfalusi <peter.ujfalusi@ti.com>
Acked-by: Jarkko Nikula <jarkko.nikula@bitmer.com>
Tested-by: Jarkko Nikula <jarkko.nikula@bitmer.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
-rw-r--r-- | sound/soc/omap/Makefile | 2 | ||||
-rw-r--r-- | sound/soc/omap/mcbsp.c | 1094 | ||||
-rw-r--r-- | sound/soc/omap/omap-mcbsp-priv.h (renamed from sound/soc/omap/mcbsp.h) | 126 | ||||
-rw-r--r-- | sound/soc/omap/omap-mcbsp-st.c | 516 | ||||
-rw-r--r-- | sound/soc/omap/omap-mcbsp.c | 838 | ||||
-rw-r--r-- | sound/soc/omap/omap-mcbsp.h | 8 |
6 files changed, 1269 insertions, 1315 deletions
diff --git a/sound/soc/omap/Makefile b/sound/soc/omap/Makefile index af50cdd6eea9..d005338dd13c 100644 --- a/sound/soc/omap/Makefile +++ b/sound/soc/omap/Makefile | |||
@@ -2,7 +2,7 @@ | |||
2 | # OMAP Platform Support | 2 | # OMAP Platform Support |
3 | snd-soc-sdma-objs := sdma-pcm.o | 3 | snd-soc-sdma-objs := sdma-pcm.o |
4 | snd-soc-omap-dmic-objs := omap-dmic.o | 4 | snd-soc-omap-dmic-objs := omap-dmic.o |
5 | snd-soc-omap-mcbsp-objs := omap-mcbsp.o mcbsp.o | 5 | snd-soc-omap-mcbsp-objs := omap-mcbsp.o omap-mcbsp-st.o |
6 | snd-soc-omap-mcpdm-objs := omap-mcpdm.o | 6 | snd-soc-omap-mcpdm-objs := omap-mcpdm.o |
7 | snd-soc-omap-hdmi-audio-objs := omap-hdmi-audio.o | 7 | snd-soc-omap-hdmi-audio-objs := omap-hdmi-audio.o |
8 | 8 | ||
diff --git a/sound/soc/omap/mcbsp.c b/sound/soc/omap/mcbsp.c deleted file mode 100644 index b19168f5c110..000000000000 --- a/sound/soc/omap/mcbsp.c +++ /dev/null | |||
@@ -1,1094 +0,0 @@ | |||
1 | /* | ||
2 | * sound/soc/omap/mcbsp.c | ||
3 | * | ||
4 | * Copyright (C) 2004 Nokia Corporation | ||
5 | * Author: Samuel Ortiz <samuel.ortiz@nokia.com> | ||
6 | * | ||
7 | * Contact: Jarkko Nikula <jarkko.nikula@bitmer.com> | ||
8 | * Peter Ujfalusi <peter.ujfalusi@ti.com> | ||
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 | ||
12 | * published by the Free Software Foundation. | ||
13 | * | ||
14 | * Multichannel mode not supported. | ||
15 | */ | ||
16 | |||
17 | #include <linux/module.h> | ||
18 | #include <linux/init.h> | ||
19 | #include <linux/device.h> | ||
20 | #include <linux/platform_device.h> | ||
21 | #include <linux/interrupt.h> | ||
22 | #include <linux/err.h> | ||
23 | #include <linux/clk.h> | ||
24 | #include <linux/delay.h> | ||
25 | #include <linux/io.h> | ||
26 | #include <linux/slab.h> | ||
27 | #include <linux/pm_runtime.h> | ||
28 | |||
29 | #include <linux/platform_data/asoc-ti-mcbsp.h> | ||
30 | |||
31 | #include "mcbsp.h" | ||
32 | |||
33 | static void omap_mcbsp_write(struct omap_mcbsp *mcbsp, u16 reg, u32 val) | ||
34 | { | ||
35 | void __iomem *addr = mcbsp->io_base + reg * mcbsp->pdata->reg_step; | ||
36 | |||
37 | if (mcbsp->pdata->reg_size == 2) { | ||
38 | ((u16 *)mcbsp->reg_cache)[reg] = (u16)val; | ||
39 | writew_relaxed((u16)val, addr); | ||
40 | } else { | ||
41 | ((u32 *)mcbsp->reg_cache)[reg] = val; | ||
42 | writel_relaxed(val, addr); | ||
43 | } | ||
44 | } | ||
45 | |||
46 | static int omap_mcbsp_read(struct omap_mcbsp *mcbsp, u16 reg, bool from_cache) | ||
47 | { | ||
48 | void __iomem *addr = mcbsp->io_base + reg * mcbsp->pdata->reg_step; | ||
49 | |||
50 | if (mcbsp->pdata->reg_size == 2) { | ||
51 | return !from_cache ? readw_relaxed(addr) : | ||
52 | ((u16 *)mcbsp->reg_cache)[reg]; | ||
53 | } else { | ||
54 | return !from_cache ? readl_relaxed(addr) : | ||
55 | ((u32 *)mcbsp->reg_cache)[reg]; | ||
56 | } | ||
57 | } | ||
58 | |||
59 | static void omap_mcbsp_st_write(struct omap_mcbsp *mcbsp, u16 reg, u32 val) | ||
60 | { | ||
61 | writel_relaxed(val, mcbsp->st_data->io_base_st + reg); | ||
62 | } | ||
63 | |||
64 | static int omap_mcbsp_st_read(struct omap_mcbsp *mcbsp, u16 reg) | ||
65 | { | ||
66 | return readl_relaxed(mcbsp->st_data->io_base_st + reg); | ||
67 | } | ||
68 | |||
69 | #define MCBSP_READ(mcbsp, reg) \ | ||
70 | omap_mcbsp_read(mcbsp, OMAP_MCBSP_REG_##reg, 0) | ||
71 | #define MCBSP_WRITE(mcbsp, reg, val) \ | ||
72 | omap_mcbsp_write(mcbsp, OMAP_MCBSP_REG_##reg, val) | ||
73 | #define MCBSP_READ_CACHE(mcbsp, reg) \ | ||
74 | omap_mcbsp_read(mcbsp, OMAP_MCBSP_REG_##reg, 1) | ||
75 | |||
76 | #define MCBSP_ST_READ(mcbsp, reg) \ | ||
77 | omap_mcbsp_st_read(mcbsp, OMAP_ST_REG_##reg) | ||
78 | #define MCBSP_ST_WRITE(mcbsp, reg, val) \ | ||
79 | omap_mcbsp_st_write(mcbsp, OMAP_ST_REG_##reg, val) | ||
80 | |||
81 | static void omap_mcbsp_dump_reg(struct omap_mcbsp *mcbsp) | ||
82 | { | ||
83 | dev_dbg(mcbsp->dev, "**** McBSP%d regs ****\n", mcbsp->id); | ||
84 | dev_dbg(mcbsp->dev, "DRR2: 0x%04x\n", | ||
85 | MCBSP_READ(mcbsp, DRR2)); | ||
86 | dev_dbg(mcbsp->dev, "DRR1: 0x%04x\n", | ||
87 | MCBSP_READ(mcbsp, DRR1)); | ||
88 | dev_dbg(mcbsp->dev, "DXR2: 0x%04x\n", | ||
89 | MCBSP_READ(mcbsp, DXR2)); | ||
90 | dev_dbg(mcbsp->dev, "DXR1: 0x%04x\n", | ||
91 | MCBSP_READ(mcbsp, DXR1)); | ||
92 | dev_dbg(mcbsp->dev, "SPCR2: 0x%04x\n", | ||
93 | MCBSP_READ(mcbsp, SPCR2)); | ||
94 | dev_dbg(mcbsp->dev, "SPCR1: 0x%04x\n", | ||
95 | MCBSP_READ(mcbsp, SPCR1)); | ||
96 | dev_dbg(mcbsp->dev, "RCR2: 0x%04x\n", | ||
97 | MCBSP_READ(mcbsp, RCR2)); | ||
98 | dev_dbg(mcbsp->dev, "RCR1: 0x%04x\n", | ||
99 | MCBSP_READ(mcbsp, RCR1)); | ||
100 | dev_dbg(mcbsp->dev, "XCR2: 0x%04x\n", | ||
101 | MCBSP_READ(mcbsp, XCR2)); | ||
102 | dev_dbg(mcbsp->dev, "XCR1: 0x%04x\n", | ||
103 | MCBSP_READ(mcbsp, XCR1)); | ||
104 | dev_dbg(mcbsp->dev, "SRGR2: 0x%04x\n", | ||
105 | MCBSP_READ(mcbsp, SRGR2)); | ||
106 | dev_dbg(mcbsp->dev, "SRGR1: 0x%04x\n", | ||
107 | MCBSP_READ(mcbsp, SRGR1)); | ||
108 | dev_dbg(mcbsp->dev, "PCR0: 0x%04x\n", | ||
109 | MCBSP_READ(mcbsp, PCR0)); | ||
110 | dev_dbg(mcbsp->dev, "***********************\n"); | ||
111 | } | ||
112 | |||
113 | static irqreturn_t omap_mcbsp_irq_handler(int irq, void *data) | ||
114 | { | ||
115 | struct omap_mcbsp *mcbsp = data; | ||
116 | u16 irqst; | ||
117 | |||
118 | irqst = MCBSP_READ(mcbsp, IRQST); | ||
119 | dev_dbg(mcbsp->dev, "IRQ callback : 0x%x\n", irqst); | ||
120 | |||
121 | if (irqst & RSYNCERREN) | ||
122 | dev_err(mcbsp->dev, "RX Frame Sync Error!\n"); | ||
123 | if (irqst & RFSREN) | ||
124 | dev_dbg(mcbsp->dev, "RX Frame Sync\n"); | ||
125 | if (irqst & REOFEN) | ||
126 | dev_dbg(mcbsp->dev, "RX End Of Frame\n"); | ||
127 | if (irqst & RRDYEN) | ||
128 | dev_dbg(mcbsp->dev, "RX Buffer Threshold Reached\n"); | ||
129 | if (irqst & RUNDFLEN) | ||
130 | dev_err(mcbsp->dev, "RX Buffer Underflow!\n"); | ||
131 | if (irqst & ROVFLEN) | ||
132 | dev_err(mcbsp->dev, "RX Buffer Overflow!\n"); | ||
133 | |||
134 | if (irqst & XSYNCERREN) | ||
135 | dev_err(mcbsp->dev, "TX Frame Sync Error!\n"); | ||
136 | if (irqst & XFSXEN) | ||
137 | dev_dbg(mcbsp->dev, "TX Frame Sync\n"); | ||
138 | if (irqst & XEOFEN) | ||
139 | dev_dbg(mcbsp->dev, "TX End Of Frame\n"); | ||
140 | if (irqst & XRDYEN) | ||
141 | dev_dbg(mcbsp->dev, "TX Buffer threshold Reached\n"); | ||
142 | if (irqst & XUNDFLEN) | ||
143 | dev_err(mcbsp->dev, "TX Buffer Underflow!\n"); | ||
144 | if (irqst & XOVFLEN) | ||
145 | dev_err(mcbsp->dev, "TX Buffer Overflow!\n"); | ||
146 | if (irqst & XEMPTYEOFEN) | ||
147 | dev_dbg(mcbsp->dev, "TX Buffer empty at end of frame\n"); | ||
148 | |||
149 | MCBSP_WRITE(mcbsp, IRQST, irqst); | ||
150 | |||
151 | return IRQ_HANDLED; | ||
152 | } | ||
153 | |||
154 | static irqreturn_t omap_mcbsp_tx_irq_handler(int irq, void *data) | ||
155 | { | ||
156 | struct omap_mcbsp *mcbsp = data; | ||
157 | u16 irqst_spcr2; | ||
158 | |||
159 | irqst_spcr2 = MCBSP_READ(mcbsp, SPCR2); | ||
160 | dev_dbg(mcbsp->dev, "TX IRQ callback : 0x%x\n", irqst_spcr2); | ||
161 | |||
162 | if (irqst_spcr2 & XSYNC_ERR) { | ||
163 | dev_err(mcbsp->dev, "TX Frame Sync Error! : 0x%x\n", | ||
164 | irqst_spcr2); | ||
165 | /* Writing zero to XSYNC_ERR clears the IRQ */ | ||
166 | MCBSP_WRITE(mcbsp, SPCR2, MCBSP_READ_CACHE(mcbsp, SPCR2)); | ||
167 | } | ||
168 | |||
169 | return IRQ_HANDLED; | ||
170 | } | ||
171 | |||
172 | static irqreturn_t omap_mcbsp_rx_irq_handler(int irq, void *data) | ||
173 | { | ||
174 | struct omap_mcbsp *mcbsp = data; | ||
175 | u16 irqst_spcr1; | ||
176 | |||
177 | irqst_spcr1 = MCBSP_READ(mcbsp, SPCR1); | ||
178 | dev_dbg(mcbsp->dev, "RX IRQ callback : 0x%x\n", irqst_spcr1); | ||
179 | |||
180 | if (irqst_spcr1 & RSYNC_ERR) { | ||
181 | dev_err(mcbsp->dev, "RX Frame Sync Error! : 0x%x\n", | ||
182 | irqst_spcr1); | ||
183 | /* Writing zero to RSYNC_ERR clears the IRQ */ | ||
184 | MCBSP_WRITE(mcbsp, SPCR1, MCBSP_READ_CACHE(mcbsp, SPCR1)); | ||
185 | } | ||
186 | |||
187 | return IRQ_HANDLED; | ||
188 | } | ||
189 | |||
190 | /* | ||
191 | * omap_mcbsp_config simply write a config to the | ||
192 | * appropriate McBSP. | ||
193 | * You either call this function or set the McBSP registers | ||
194 | * by yourself before calling omap_mcbsp_start(). | ||
195 | */ | ||
196 | void omap_mcbsp_config(struct omap_mcbsp *mcbsp, | ||
197 | const struct omap_mcbsp_reg_cfg *config) | ||
198 | { | ||
199 | dev_dbg(mcbsp->dev, "Configuring McBSP%d phys_base: 0x%08lx\n", | ||
200 | mcbsp->id, mcbsp->phys_base); | ||
201 | |||
202 | /* We write the given config */ | ||
203 | MCBSP_WRITE(mcbsp, SPCR2, config->spcr2); | ||
204 | MCBSP_WRITE(mcbsp, SPCR1, config->spcr1); | ||
205 | MCBSP_WRITE(mcbsp, RCR2, config->rcr2); | ||
206 | MCBSP_WRITE(mcbsp, RCR1, config->rcr1); | ||
207 | MCBSP_WRITE(mcbsp, XCR2, config->xcr2); | ||
208 | MCBSP_WRITE(mcbsp, XCR1, config->xcr1); | ||
209 | MCBSP_WRITE(mcbsp, SRGR2, config->srgr2); | ||
210 | MCBSP_WRITE(mcbsp, SRGR1, config->srgr1); | ||
211 | MCBSP_WRITE(mcbsp, MCR2, config->mcr2); | ||
212 | MCBSP_WRITE(mcbsp, MCR1, config->mcr1); | ||
213 | MCBSP_WRITE(mcbsp, PCR0, config->pcr0); | ||
214 | if (mcbsp->pdata->has_ccr) { | ||
215 | MCBSP_WRITE(mcbsp, XCCR, config->xccr); | ||
216 | MCBSP_WRITE(mcbsp, RCCR, config->rccr); | ||
217 | } | ||
218 | /* Enable wakeup behavior */ | ||
219 | if (mcbsp->pdata->has_wakeup) | ||
220 | MCBSP_WRITE(mcbsp, WAKEUPEN, XRDYEN | RRDYEN); | ||
221 | |||
222 | /* Enable TX/RX sync error interrupts by default */ | ||
223 | if (mcbsp->irq) | ||
224 | MCBSP_WRITE(mcbsp, IRQEN, RSYNCERREN | XSYNCERREN | | ||
225 | RUNDFLEN | ROVFLEN | XUNDFLEN | XOVFLEN); | ||
226 | } | ||
227 | |||
228 | /** | ||
229 | * omap_mcbsp_dma_reg_params - returns the address of mcbsp data register | ||
230 | * @mcbsp: omap_mcbsp struct for the McBSP instance | ||
231 | * @stream: Stream direction (playback/capture) | ||
232 | * | ||
233 | * Returns the address of mcbsp data transmit register or data receive register | ||
234 | * to be used by DMA for transferring/receiving data | ||
235 | */ | ||
236 | static int omap_mcbsp_dma_reg_params(struct omap_mcbsp *mcbsp, | ||
237 | unsigned int stream) | ||
238 | { | ||
239 | int data_reg; | ||
240 | |||
241 | if (stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
242 | if (mcbsp->pdata->reg_size == 2) | ||
243 | data_reg = OMAP_MCBSP_REG_DXR1; | ||
244 | else | ||
245 | data_reg = OMAP_MCBSP_REG_DXR; | ||
246 | } else { | ||
247 | if (mcbsp->pdata->reg_size == 2) | ||
248 | data_reg = OMAP_MCBSP_REG_DRR1; | ||
249 | else | ||
250 | data_reg = OMAP_MCBSP_REG_DRR; | ||
251 | } | ||
252 | |||
253 | return mcbsp->phys_dma_base + data_reg * mcbsp->pdata->reg_step; | ||
254 | } | ||
255 | |||
256 | static void omap_st_on(struct omap_mcbsp *mcbsp) | ||
257 | { | ||
258 | unsigned int w; | ||
259 | |||
260 | if (mcbsp->pdata->force_ick_on) | ||
261 | mcbsp->pdata->force_ick_on(mcbsp->st_data->mcbsp_iclk, true); | ||
262 | |||
263 | /* Disable Sidetone clock auto-gating for normal operation */ | ||
264 | w = MCBSP_ST_READ(mcbsp, SYSCONFIG); | ||
265 | MCBSP_ST_WRITE(mcbsp, SYSCONFIG, w & ~(ST_AUTOIDLE)); | ||
266 | |||
267 | /* Enable McBSP Sidetone */ | ||
268 | w = MCBSP_READ(mcbsp, SSELCR); | ||
269 | MCBSP_WRITE(mcbsp, SSELCR, w | SIDETONEEN); | ||
270 | |||
271 | /* Enable Sidetone from Sidetone Core */ | ||
272 | w = MCBSP_ST_READ(mcbsp, SSELCR); | ||
273 | MCBSP_ST_WRITE(mcbsp, SSELCR, w | ST_SIDETONEEN); | ||
274 | } | ||
275 | |||
276 | static void omap_st_off(struct omap_mcbsp *mcbsp) | ||
277 | { | ||
278 | unsigned int w; | ||
279 | |||
280 | w = MCBSP_ST_READ(mcbsp, SSELCR); | ||
281 | MCBSP_ST_WRITE(mcbsp, SSELCR, w & ~(ST_SIDETONEEN)); | ||
282 | |||
283 | w = MCBSP_READ(mcbsp, SSELCR); | ||
284 | MCBSP_WRITE(mcbsp, SSELCR, w & ~(SIDETONEEN)); | ||
285 | |||
286 | /* Enable Sidetone clock auto-gating to reduce power consumption */ | ||
287 | w = MCBSP_ST_READ(mcbsp, SYSCONFIG); | ||
288 | MCBSP_ST_WRITE(mcbsp, SYSCONFIG, w | ST_AUTOIDLE); | ||
289 | |||
290 | if (mcbsp->pdata->force_ick_on) | ||
291 | mcbsp->pdata->force_ick_on(mcbsp->st_data->mcbsp_iclk, false); | ||
292 | } | ||
293 | |||
294 | static void omap_st_fir_write(struct omap_mcbsp *mcbsp, s16 *fir) | ||
295 | { | ||
296 | u16 val, i; | ||
297 | |||
298 | val = MCBSP_ST_READ(mcbsp, SSELCR); | ||
299 | |||
300 | if (val & ST_COEFFWREN) | ||
301 | MCBSP_ST_WRITE(mcbsp, SSELCR, val & ~(ST_COEFFWREN)); | ||
302 | |||
303 | MCBSP_ST_WRITE(mcbsp, SSELCR, val | ST_COEFFWREN); | ||
304 | |||
305 | for (i = 0; i < 128; i++) | ||
306 | MCBSP_ST_WRITE(mcbsp, SFIRCR, fir[i]); | ||
307 | |||
308 | i = 0; | ||
309 | |||
310 | val = MCBSP_ST_READ(mcbsp, SSELCR); | ||
311 | while (!(val & ST_COEFFWRDONE) && (++i < 1000)) | ||
312 | val = MCBSP_ST_READ(mcbsp, SSELCR); | ||
313 | |||
314 | MCBSP_ST_WRITE(mcbsp, SSELCR, val & ~(ST_COEFFWREN)); | ||
315 | |||
316 | if (i == 1000) | ||
317 | dev_err(mcbsp->dev, "McBSP FIR load error!\n"); | ||
318 | } | ||
319 | |||
320 | static void omap_st_chgain(struct omap_mcbsp *mcbsp) | ||
321 | { | ||
322 | u16 w; | ||
323 | struct omap_mcbsp_st_data *st_data = mcbsp->st_data; | ||
324 | |||
325 | w = MCBSP_ST_READ(mcbsp, SSELCR); | ||
326 | |||
327 | MCBSP_ST_WRITE(mcbsp, SGAINCR, ST_CH0GAIN(st_data->ch0gain) | \ | ||
328 | ST_CH1GAIN(st_data->ch1gain)); | ||
329 | } | ||
330 | |||
331 | int omap_st_set_chgain(struct omap_mcbsp *mcbsp, int channel, s16 chgain) | ||
332 | { | ||
333 | struct omap_mcbsp_st_data *st_data = mcbsp->st_data; | ||
334 | int ret = 0; | ||
335 | |||
336 | if (!st_data) | ||
337 | return -ENOENT; | ||
338 | |||
339 | spin_lock_irq(&mcbsp->lock); | ||
340 | if (channel == 0) | ||
341 | st_data->ch0gain = chgain; | ||
342 | else if (channel == 1) | ||
343 | st_data->ch1gain = chgain; | ||
344 | else | ||
345 | ret = -EINVAL; | ||
346 | |||
347 | if (st_data->enabled) | ||
348 | omap_st_chgain(mcbsp); | ||
349 | spin_unlock_irq(&mcbsp->lock); | ||
350 | |||
351 | return ret; | ||
352 | } | ||
353 | |||
354 | int omap_st_get_chgain(struct omap_mcbsp *mcbsp, int channel, s16 *chgain) | ||
355 | { | ||
356 | struct omap_mcbsp_st_data *st_data = mcbsp->st_data; | ||
357 | int ret = 0; | ||
358 | |||
359 | if (!st_data) | ||
360 | return -ENOENT; | ||
361 | |||
362 | spin_lock_irq(&mcbsp->lock); | ||
363 | if (channel == 0) | ||
364 | *chgain = st_data->ch0gain; | ||
365 | else if (channel == 1) | ||
366 | *chgain = st_data->ch1gain; | ||
367 | else | ||
368 | ret = -EINVAL; | ||
369 | spin_unlock_irq(&mcbsp->lock); | ||
370 | |||
371 | return ret; | ||
372 | } | ||
373 | |||
374 | static int omap_st_start(struct omap_mcbsp *mcbsp) | ||
375 | { | ||
376 | struct omap_mcbsp_st_data *st_data = mcbsp->st_data; | ||
377 | |||
378 | if (st_data->enabled && !st_data->running) { | ||
379 | omap_st_fir_write(mcbsp, st_data->taps); | ||
380 | omap_st_chgain(mcbsp); | ||
381 | |||
382 | if (!mcbsp->free) { | ||
383 | omap_st_on(mcbsp); | ||
384 | st_data->running = 1; | ||
385 | } | ||
386 | } | ||
387 | |||
388 | return 0; | ||
389 | } | ||
390 | |||
391 | int omap_st_enable(struct omap_mcbsp *mcbsp) | ||
392 | { | ||
393 | struct omap_mcbsp_st_data *st_data = mcbsp->st_data; | ||
394 | |||
395 | if (!st_data) | ||
396 | return -ENODEV; | ||
397 | |||
398 | spin_lock_irq(&mcbsp->lock); | ||
399 | st_data->enabled = 1; | ||
400 | omap_st_start(mcbsp); | ||
401 | spin_unlock_irq(&mcbsp->lock); | ||
402 | |||
403 | return 0; | ||
404 | } | ||
405 | |||
406 | static int omap_st_stop(struct omap_mcbsp *mcbsp) | ||
407 | { | ||
408 | struct omap_mcbsp_st_data *st_data = mcbsp->st_data; | ||
409 | |||
410 | if (st_data->running) { | ||
411 | if (!mcbsp->free) { | ||
412 | omap_st_off(mcbsp); | ||
413 | st_data->running = 0; | ||
414 | } | ||
415 | } | ||
416 | |||
417 | return 0; | ||
418 | } | ||
419 | |||
420 | int omap_st_disable(struct omap_mcbsp *mcbsp) | ||
421 | { | ||
422 | struct omap_mcbsp_st_data *st_data = mcbsp->st_data; | ||
423 | int ret = 0; | ||
424 | |||
425 | if (!st_data) | ||
426 | return -ENODEV; | ||
427 | |||
428 | spin_lock_irq(&mcbsp->lock); | ||
429 | omap_st_stop(mcbsp); | ||
430 | st_data->enabled = 0; | ||
431 | spin_unlock_irq(&mcbsp->lock); | ||
432 | |||
433 | return ret; | ||
434 | } | ||
435 | |||
436 | int omap_st_is_enabled(struct omap_mcbsp *mcbsp) | ||
437 | { | ||
438 | struct omap_mcbsp_st_data *st_data = mcbsp->st_data; | ||
439 | |||
440 | if (!st_data) | ||
441 | return -ENODEV; | ||
442 | |||
443 | return st_data->enabled; | ||
444 | } | ||
445 | |||
446 | /* | ||
447 | * omap_mcbsp_set_rx_threshold configures the transmit threshold in words. | ||
448 | * The threshold parameter is 1 based, and it is converted (threshold - 1) | ||
449 | * for the THRSH2 register. | ||
450 | */ | ||
451 | void omap_mcbsp_set_tx_threshold(struct omap_mcbsp *mcbsp, u16 threshold) | ||
452 | { | ||
453 | if (threshold && threshold <= mcbsp->max_tx_thres) | ||
454 | MCBSP_WRITE(mcbsp, THRSH2, threshold - 1); | ||
455 | } | ||
456 | |||
457 | /* | ||
458 | * omap_mcbsp_set_rx_threshold configures the receive threshold in words. | ||
459 | * The threshold parameter is 1 based, and it is converted (threshold - 1) | ||
460 | * for the THRSH1 register. | ||
461 | */ | ||
462 | void omap_mcbsp_set_rx_threshold(struct omap_mcbsp *mcbsp, u16 threshold) | ||
463 | { | ||
464 | if (threshold && threshold <= mcbsp->max_rx_thres) | ||
465 | MCBSP_WRITE(mcbsp, THRSH1, threshold - 1); | ||
466 | } | ||
467 | |||
468 | /* | ||
469 | * omap_mcbsp_get_tx_delay returns the number of used slots in the McBSP FIFO | ||
470 | */ | ||
471 | u16 omap_mcbsp_get_tx_delay(struct omap_mcbsp *mcbsp) | ||
472 | { | ||
473 | u16 buffstat; | ||
474 | |||
475 | /* Returns the number of free locations in the buffer */ | ||
476 | buffstat = MCBSP_READ(mcbsp, XBUFFSTAT); | ||
477 | |||
478 | /* Number of slots are different in McBSP ports */ | ||
479 | return mcbsp->pdata->buffer_size - buffstat; | ||
480 | } | ||
481 | |||
482 | /* | ||
483 | * omap_mcbsp_get_rx_delay returns the number of free slots in the McBSP FIFO | ||
484 | * to reach the threshold value (when the DMA will be triggered to read it) | ||
485 | */ | ||
486 | u16 omap_mcbsp_get_rx_delay(struct omap_mcbsp *mcbsp) | ||
487 | { | ||
488 | u16 buffstat, threshold; | ||
489 | |||
490 | /* Returns the number of used locations in the buffer */ | ||
491 | buffstat = MCBSP_READ(mcbsp, RBUFFSTAT); | ||
492 | /* RX threshold */ | ||
493 | threshold = MCBSP_READ(mcbsp, THRSH1); | ||
494 | |||
495 | /* Return the number of location till we reach the threshold limit */ | ||
496 | if (threshold <= buffstat) | ||
497 | return 0; | ||
498 | else | ||
499 | return threshold - buffstat; | ||
500 | } | ||
501 | |||
502 | int omap_mcbsp_request(struct omap_mcbsp *mcbsp) | ||
503 | { | ||
504 | void *reg_cache; | ||
505 | int err; | ||
506 | |||
507 | reg_cache = kzalloc(mcbsp->reg_cache_size, GFP_KERNEL); | ||
508 | if (!reg_cache) { | ||
509 | return -ENOMEM; | ||
510 | } | ||
511 | |||
512 | spin_lock(&mcbsp->lock); | ||
513 | if (!mcbsp->free) { | ||
514 | dev_err(mcbsp->dev, "McBSP%d is currently in use\n", | ||
515 | mcbsp->id); | ||
516 | err = -EBUSY; | ||
517 | goto err_kfree; | ||
518 | } | ||
519 | |||
520 | mcbsp->free = false; | ||
521 | mcbsp->reg_cache = reg_cache; | ||
522 | spin_unlock(&mcbsp->lock); | ||
523 | |||
524 | if (mcbsp->pdata && mcbsp->pdata->ops && mcbsp->pdata->ops->request) | ||
525 | mcbsp->pdata->ops->request(mcbsp->id - 1); | ||
526 | |||
527 | /* | ||
528 | * Make sure that transmitter, receiver and sample-rate generator are | ||
529 | * not running before activating IRQs. | ||
530 | */ | ||
531 | MCBSP_WRITE(mcbsp, SPCR1, 0); | ||
532 | MCBSP_WRITE(mcbsp, SPCR2, 0); | ||
533 | |||
534 | if (mcbsp->irq) { | ||
535 | err = request_irq(mcbsp->irq, omap_mcbsp_irq_handler, 0, | ||
536 | "McBSP", (void *)mcbsp); | ||
537 | if (err != 0) { | ||
538 | dev_err(mcbsp->dev, "Unable to request IRQ\n"); | ||
539 | goto err_clk_disable; | ||
540 | } | ||
541 | } else { | ||
542 | err = request_irq(mcbsp->tx_irq, omap_mcbsp_tx_irq_handler, 0, | ||
543 | "McBSP TX", (void *)mcbsp); | ||
544 | if (err != 0) { | ||
545 | dev_err(mcbsp->dev, "Unable to request TX IRQ\n"); | ||
546 | goto err_clk_disable; | ||
547 | } | ||
548 | |||
549 | err = request_irq(mcbsp->rx_irq, omap_mcbsp_rx_irq_handler, 0, | ||
550 | "McBSP RX", (void *)mcbsp); | ||
551 | if (err != 0) { | ||
552 | dev_err(mcbsp->dev, "Unable to request RX IRQ\n"); | ||
553 | goto err_free_irq; | ||
554 | } | ||
555 | } | ||
556 | |||
557 | return 0; | ||
558 | err_free_irq: | ||
559 | free_irq(mcbsp->tx_irq, (void *)mcbsp); | ||
560 | err_clk_disable: | ||
561 | if (mcbsp->pdata && mcbsp->pdata->ops && mcbsp->pdata->ops->free) | ||
562 | mcbsp->pdata->ops->free(mcbsp->id - 1); | ||
563 | |||
564 | /* Disable wakeup behavior */ | ||
565 | if (mcbsp->pdata->has_wakeup) | ||
566 | MCBSP_WRITE(mcbsp, WAKEUPEN, 0); | ||
567 | |||
568 | spin_lock(&mcbsp->lock); | ||
569 | mcbsp->free = true; | ||
570 | mcbsp->reg_cache = NULL; | ||
571 | err_kfree: | ||
572 | spin_unlock(&mcbsp->lock); | ||
573 | kfree(reg_cache); | ||
574 | |||
575 | return err; | ||
576 | } | ||
577 | |||
578 | void omap_mcbsp_free(struct omap_mcbsp *mcbsp) | ||
579 | { | ||
580 | void *reg_cache; | ||
581 | |||
582 | if (mcbsp->pdata && mcbsp->pdata->ops && mcbsp->pdata->ops->free) | ||
583 | mcbsp->pdata->ops->free(mcbsp->id - 1); | ||
584 | |||
585 | /* Disable wakeup behavior */ | ||
586 | if (mcbsp->pdata->has_wakeup) | ||
587 | MCBSP_WRITE(mcbsp, WAKEUPEN, 0); | ||
588 | |||
589 | /* Disable interrupt requests */ | ||
590 | if (mcbsp->irq) | ||
591 | MCBSP_WRITE(mcbsp, IRQEN, 0); | ||
592 | |||
593 | if (mcbsp->irq) { | ||
594 | free_irq(mcbsp->irq, (void *)mcbsp); | ||
595 | } else { | ||
596 | free_irq(mcbsp->rx_irq, (void *)mcbsp); | ||
597 | free_irq(mcbsp->tx_irq, (void *)mcbsp); | ||
598 | } | ||
599 | |||
600 | reg_cache = mcbsp->reg_cache; | ||
601 | |||
602 | /* | ||
603 | * Select CLKS source from internal source unconditionally before | ||
604 | * marking the McBSP port as free. | ||
605 | * If the external clock source via MCBSP_CLKS pin has been selected the | ||
606 | * system will refuse to enter idle if the CLKS pin source is not reset | ||
607 | * back to internal source. | ||
608 | */ | ||
609 | if (!mcbsp_omap1()) | ||
610 | omap2_mcbsp_set_clks_src(mcbsp, MCBSP_CLKS_PRCM_SRC); | ||
611 | |||
612 | spin_lock(&mcbsp->lock); | ||
613 | if (mcbsp->free) | ||
614 | dev_err(mcbsp->dev, "McBSP%d was not reserved\n", mcbsp->id); | ||
615 | else | ||
616 | mcbsp->free = true; | ||
617 | mcbsp->reg_cache = NULL; | ||
618 | spin_unlock(&mcbsp->lock); | ||
619 | |||
620 | kfree(reg_cache); | ||
621 | } | ||
622 | |||
623 | /* | ||
624 | * Here we start the McBSP, by enabling transmitter, receiver or both. | ||
625 | * If no transmitter or receiver is active prior calling, then sample-rate | ||
626 | * generator and frame sync are started. | ||
627 | */ | ||
628 | void omap_mcbsp_start(struct omap_mcbsp *mcbsp, int stream) | ||
629 | { | ||
630 | int tx = (stream == SNDRV_PCM_STREAM_PLAYBACK); | ||
631 | int rx = !tx; | ||
632 | int enable_srg = 0; | ||
633 | u16 w; | ||
634 | |||
635 | if (mcbsp->st_data) | ||
636 | omap_st_start(mcbsp); | ||
637 | |||
638 | /* Only enable SRG, if McBSP is master */ | ||
639 | w = MCBSP_READ_CACHE(mcbsp, PCR0); | ||
640 | if (w & (FSXM | FSRM | CLKXM | CLKRM)) | ||
641 | enable_srg = !((MCBSP_READ_CACHE(mcbsp, SPCR2) | | ||
642 | MCBSP_READ_CACHE(mcbsp, SPCR1)) & 1); | ||
643 | |||
644 | if (enable_srg) { | ||
645 | /* Start the sample generator */ | ||
646 | w = MCBSP_READ_CACHE(mcbsp, SPCR2); | ||
647 | MCBSP_WRITE(mcbsp, SPCR2, w | (1 << 6)); | ||
648 | } | ||
649 | |||
650 | /* Enable transmitter and receiver */ | ||
651 | tx &= 1; | ||
652 | w = MCBSP_READ_CACHE(mcbsp, SPCR2); | ||
653 | MCBSP_WRITE(mcbsp, SPCR2, w | tx); | ||
654 | |||
655 | rx &= 1; | ||
656 | w = MCBSP_READ_CACHE(mcbsp, SPCR1); | ||
657 | MCBSP_WRITE(mcbsp, SPCR1, w | rx); | ||
658 | |||
659 | /* | ||
660 | * Worst case: CLKSRG*2 = 8000khz: (1/8000) * 2 * 2 usec | ||
661 | * REVISIT: 100us may give enough time for two CLKSRG, however | ||
662 | * due to some unknown PM related, clock gating etc. reason it | ||
663 | * is now at 500us. | ||
664 | */ | ||
665 | udelay(500); | ||
666 | |||
667 | if (enable_srg) { | ||
668 | /* Start frame sync */ | ||
669 | w = MCBSP_READ_CACHE(mcbsp, SPCR2); | ||
670 | MCBSP_WRITE(mcbsp, SPCR2, w | (1 << 7)); | ||
671 | } | ||
672 | |||
673 | if (mcbsp->pdata->has_ccr) { | ||
674 | /* Release the transmitter and receiver */ | ||
675 | w = MCBSP_READ_CACHE(mcbsp, XCCR); | ||
676 | w &= ~(tx ? XDISABLE : 0); | ||
677 | MCBSP_WRITE(mcbsp, XCCR, w); | ||
678 | w = MCBSP_READ_CACHE(mcbsp, RCCR); | ||
679 | w &= ~(rx ? RDISABLE : 0); | ||
680 | MCBSP_WRITE(mcbsp, RCCR, w); | ||
681 | } | ||
682 | |||
683 | /* Dump McBSP Regs */ | ||
684 | omap_mcbsp_dump_reg(mcbsp); | ||
685 | } | ||
686 | |||
687 | void omap_mcbsp_stop(struct omap_mcbsp *mcbsp, int stream) | ||
688 | { | ||
689 | int tx = (stream == SNDRV_PCM_STREAM_PLAYBACK); | ||
690 | int rx = !tx; | ||
691 | int idle; | ||
692 | u16 w; | ||
693 | |||
694 | /* Reset transmitter */ | ||
695 | tx &= 1; | ||
696 | if (mcbsp->pdata->has_ccr) { | ||
697 | w = MCBSP_READ_CACHE(mcbsp, XCCR); | ||
698 | w |= (tx ? XDISABLE : 0); | ||
699 | MCBSP_WRITE(mcbsp, XCCR, w); | ||
700 | } | ||
701 | w = MCBSP_READ_CACHE(mcbsp, SPCR2); | ||
702 | MCBSP_WRITE(mcbsp, SPCR2, w & ~tx); | ||
703 | |||
704 | /* Reset receiver */ | ||
705 | rx &= 1; | ||
706 | if (mcbsp->pdata->has_ccr) { | ||
707 | w = MCBSP_READ_CACHE(mcbsp, RCCR); | ||
708 | w |= (rx ? RDISABLE : 0); | ||
709 | MCBSP_WRITE(mcbsp, RCCR, w); | ||
710 | } | ||
711 | w = MCBSP_READ_CACHE(mcbsp, SPCR1); | ||
712 | MCBSP_WRITE(mcbsp, SPCR1, w & ~rx); | ||
713 | |||
714 | idle = !((MCBSP_READ_CACHE(mcbsp, SPCR2) | | ||
715 | MCBSP_READ_CACHE(mcbsp, SPCR1)) & 1); | ||
716 | |||
717 | if (idle) { | ||
718 | /* Reset the sample rate generator */ | ||
719 | w = MCBSP_READ_CACHE(mcbsp, SPCR2); | ||
720 | MCBSP_WRITE(mcbsp, SPCR2, w & ~(1 << 6)); | ||
721 | } | ||
722 | |||
723 | if (mcbsp->st_data) | ||
724 | omap_st_stop(mcbsp); | ||
725 | } | ||
726 | |||
727 | int omap2_mcbsp_set_clks_src(struct omap_mcbsp *mcbsp, u8 fck_src_id) | ||
728 | { | ||
729 | struct clk *fck_src; | ||
730 | const char *src; | ||
731 | int r; | ||
732 | |||
733 | if (fck_src_id == MCBSP_CLKS_PAD_SRC) | ||
734 | src = "pad_fck"; | ||
735 | else if (fck_src_id == MCBSP_CLKS_PRCM_SRC) | ||
736 | src = "prcm_fck"; | ||
737 | else | ||
738 | return -EINVAL; | ||
739 | |||
740 | fck_src = clk_get(mcbsp->dev, src); | ||
741 | if (IS_ERR(fck_src)) { | ||
742 | dev_err(mcbsp->dev, "CLKS: could not clk_get() %s\n", src); | ||
743 | return -EINVAL; | ||
744 | } | ||
745 | |||
746 | pm_runtime_put_sync(mcbsp->dev); | ||
747 | |||
748 | r = clk_set_parent(mcbsp->fclk, fck_src); | ||
749 | if (r) { | ||
750 | dev_err(mcbsp->dev, "CLKS: could not clk_set_parent() to %s\n", | ||
751 | src); | ||
752 | clk_put(fck_src); | ||
753 | return r; | ||
754 | } | ||
755 | |||
756 | pm_runtime_get_sync(mcbsp->dev); | ||
757 | |||
758 | clk_put(fck_src); | ||
759 | |||
760 | return 0; | ||
761 | |||
762 | } | ||
763 | |||
764 | #define max_thres(m) (mcbsp->pdata->buffer_size) | ||
765 | #define valid_threshold(m, val) ((val) <= max_thres(m)) | ||
766 | #define THRESHOLD_PROP_BUILDER(prop) \ | ||
767 | static ssize_t prop##_show(struct device *dev, \ | ||
768 | struct device_attribute *attr, char *buf) \ | ||
769 | { \ | ||
770 | struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); \ | ||
771 | \ | ||
772 | return sprintf(buf, "%u\n", mcbsp->prop); \ | ||
773 | } \ | ||
774 | \ | ||
775 | static ssize_t prop##_store(struct device *dev, \ | ||
776 | struct device_attribute *attr, \ | ||
777 | const char *buf, size_t size) \ | ||
778 | { \ | ||
779 | struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); \ | ||
780 | unsigned long val; \ | ||
781 | int status; \ | ||
782 | \ | ||
783 | status = kstrtoul(buf, 0, &val); \ | ||
784 | if (status) \ | ||
785 | return status; \ | ||
786 | \ | ||
787 | if (!valid_threshold(mcbsp, val)) \ | ||
788 | return -EDOM; \ | ||
789 | \ | ||
790 | mcbsp->prop = val; \ | ||
791 | return size; \ | ||
792 | } \ | ||
793 | \ | ||
794 | static DEVICE_ATTR(prop, 0644, prop##_show, prop##_store); | ||
795 | |||
796 | THRESHOLD_PROP_BUILDER(max_tx_thres); | ||
797 | THRESHOLD_PROP_BUILDER(max_rx_thres); | ||
798 | |||
799 | static const char *dma_op_modes[] = { | ||
800 | "element", "threshold", | ||
801 | }; | ||
802 | |||
803 | static ssize_t dma_op_mode_show(struct device *dev, | ||
804 | struct device_attribute *attr, char *buf) | ||
805 | { | ||
806 | struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); | ||
807 | int dma_op_mode, i = 0; | ||
808 | ssize_t len = 0; | ||
809 | const char * const *s; | ||
810 | |||
811 | dma_op_mode = mcbsp->dma_op_mode; | ||
812 | |||
813 | for (s = &dma_op_modes[i]; i < ARRAY_SIZE(dma_op_modes); s++, i++) { | ||
814 | if (dma_op_mode == i) | ||
815 | len += sprintf(buf + len, "[%s] ", *s); | ||
816 | else | ||
817 | len += sprintf(buf + len, "%s ", *s); | ||
818 | } | ||
819 | len += sprintf(buf + len, "\n"); | ||
820 | |||
821 | return len; | ||
822 | } | ||
823 | |||
824 | static ssize_t dma_op_mode_store(struct device *dev, | ||
825 | struct device_attribute *attr, | ||
826 | const char *buf, size_t size) | ||
827 | { | ||
828 | struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); | ||
829 | int i; | ||
830 | |||
831 | i = sysfs_match_string(dma_op_modes, buf); | ||
832 | if (i < 0) | ||
833 | return i; | ||
834 | |||
835 | spin_lock_irq(&mcbsp->lock); | ||
836 | if (!mcbsp->free) { | ||
837 | size = -EBUSY; | ||
838 | goto unlock; | ||
839 | } | ||
840 | mcbsp->dma_op_mode = i; | ||
841 | |||
842 | unlock: | ||
843 | spin_unlock_irq(&mcbsp->lock); | ||
844 | |||
845 | return size; | ||
846 | } | ||
847 | |||
848 | static DEVICE_ATTR_RW(dma_op_mode); | ||
849 | |||
850 | static const struct attribute *additional_attrs[] = { | ||
851 | &dev_attr_max_tx_thres.attr, | ||
852 | &dev_attr_max_rx_thres.attr, | ||
853 | &dev_attr_dma_op_mode.attr, | ||
854 | NULL, | ||
855 | }; | ||
856 | |||
857 | static const struct attribute_group additional_attr_group = { | ||
858 | .attrs = (struct attribute **)additional_attrs, | ||
859 | }; | ||
860 | |||
861 | static ssize_t st_taps_show(struct device *dev, | ||
862 | struct device_attribute *attr, char *buf) | ||
863 | { | ||
864 | struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); | ||
865 | struct omap_mcbsp_st_data *st_data = mcbsp->st_data; | ||
866 | ssize_t status = 0; | ||
867 | int i; | ||
868 | |||
869 | spin_lock_irq(&mcbsp->lock); | ||
870 | for (i = 0; i < st_data->nr_taps; i++) | ||
871 | status += sprintf(&buf[status], (i ? ", %d" : "%d"), | ||
872 | st_data->taps[i]); | ||
873 | if (i) | ||
874 | status += sprintf(&buf[status], "\n"); | ||
875 | spin_unlock_irq(&mcbsp->lock); | ||
876 | |||
877 | return status; | ||
878 | } | ||
879 | |||
880 | static ssize_t st_taps_store(struct device *dev, | ||
881 | struct device_attribute *attr, | ||
882 | const char *buf, size_t size) | ||
883 | { | ||
884 | struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); | ||
885 | struct omap_mcbsp_st_data *st_data = mcbsp->st_data; | ||
886 | int val, tmp, status, i = 0; | ||
887 | |||
888 | spin_lock_irq(&mcbsp->lock); | ||
889 | memset(st_data->taps, 0, sizeof(st_data->taps)); | ||
890 | st_data->nr_taps = 0; | ||
891 | |||
892 | do { | ||
893 | status = sscanf(buf, "%d%n", &val, &tmp); | ||
894 | if (status < 0 || status == 0) { | ||
895 | size = -EINVAL; | ||
896 | goto out; | ||
897 | } | ||
898 | if (val < -32768 || val > 32767) { | ||
899 | size = -EINVAL; | ||
900 | goto out; | ||
901 | } | ||
902 | st_data->taps[i++] = val; | ||
903 | buf += tmp; | ||
904 | if (*buf != ',') | ||
905 | break; | ||
906 | buf++; | ||
907 | } while (1); | ||
908 | |||
909 | st_data->nr_taps = i; | ||
910 | |||
911 | out: | ||
912 | spin_unlock_irq(&mcbsp->lock); | ||
913 | |||
914 | return size; | ||
915 | } | ||
916 | |||
917 | static DEVICE_ATTR_RW(st_taps); | ||
918 | |||
919 | static const struct attribute *sidetone_attrs[] = { | ||
920 | &dev_attr_st_taps.attr, | ||
921 | NULL, | ||
922 | }; | ||
923 | |||
924 | static const struct attribute_group sidetone_attr_group = { | ||
925 | .attrs = (struct attribute **)sidetone_attrs, | ||
926 | }; | ||
927 | |||
928 | static int omap_st_add(struct omap_mcbsp *mcbsp, struct resource *res) | ||
929 | { | ||
930 | struct omap_mcbsp_st_data *st_data; | ||
931 | int err; | ||
932 | |||
933 | st_data = devm_kzalloc(mcbsp->dev, sizeof(*mcbsp->st_data), GFP_KERNEL); | ||
934 | if (!st_data) | ||
935 | return -ENOMEM; | ||
936 | |||
937 | st_data->mcbsp_iclk = clk_get(mcbsp->dev, "ick"); | ||
938 | if (IS_ERR(st_data->mcbsp_iclk)) { | ||
939 | dev_warn(mcbsp->dev, | ||
940 | "Failed to get ick, sidetone might be broken\n"); | ||
941 | st_data->mcbsp_iclk = NULL; | ||
942 | } | ||
943 | |||
944 | st_data->io_base_st = devm_ioremap(mcbsp->dev, res->start, | ||
945 | resource_size(res)); | ||
946 | if (!st_data->io_base_st) | ||
947 | return -ENOMEM; | ||
948 | |||
949 | err = sysfs_create_group(&mcbsp->dev->kobj, &sidetone_attr_group); | ||
950 | if (err) | ||
951 | return err; | ||
952 | |||
953 | mcbsp->st_data = st_data; | ||
954 | return 0; | ||
955 | } | ||
956 | |||
957 | /* | ||
958 | * McBSP1 and McBSP3 are directly mapped on 1610 and 1510. | ||
959 | * 730 has only 2 McBSP, and both of them are MPU peripherals. | ||
960 | */ | ||
961 | int omap_mcbsp_init(struct platform_device *pdev) | ||
962 | { | ||
963 | struct omap_mcbsp *mcbsp = platform_get_drvdata(pdev); | ||
964 | struct resource *res; | ||
965 | int ret = 0; | ||
966 | |||
967 | spin_lock_init(&mcbsp->lock); | ||
968 | mcbsp->free = true; | ||
969 | |||
970 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mpu"); | ||
971 | if (!res) | ||
972 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
973 | |||
974 | mcbsp->io_base = devm_ioremap_resource(&pdev->dev, res); | ||
975 | if (IS_ERR(mcbsp->io_base)) | ||
976 | return PTR_ERR(mcbsp->io_base); | ||
977 | |||
978 | mcbsp->phys_base = res->start; | ||
979 | mcbsp->reg_cache_size = resource_size(res); | ||
980 | |||
981 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dma"); | ||
982 | if (!res) | ||
983 | mcbsp->phys_dma_base = mcbsp->phys_base; | ||
984 | else | ||
985 | mcbsp->phys_dma_base = res->start; | ||
986 | |||
987 | /* | ||
988 | * OMAP1, 2 uses two interrupt lines: TX, RX | ||
989 | * OMAP2430, OMAP3 SoC have combined IRQ line as well. | ||
990 | * OMAP4 and newer SoC only have the combined IRQ line. | ||
991 | * Use the combined IRQ if available since it gives better debugging | ||
992 | * possibilities. | ||
993 | */ | ||
994 | mcbsp->irq = platform_get_irq_byname(pdev, "common"); | ||
995 | if (mcbsp->irq == -ENXIO) { | ||
996 | mcbsp->tx_irq = platform_get_irq_byname(pdev, "tx"); | ||
997 | |||
998 | if (mcbsp->tx_irq == -ENXIO) { | ||
999 | mcbsp->irq = platform_get_irq(pdev, 0); | ||
1000 | mcbsp->tx_irq = 0; | ||
1001 | } else { | ||
1002 | mcbsp->rx_irq = platform_get_irq_byname(pdev, "rx"); | ||
1003 | mcbsp->irq = 0; | ||
1004 | } | ||
1005 | } | ||
1006 | |||
1007 | if (!pdev->dev.of_node) { | ||
1008 | res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "tx"); | ||
1009 | if (!res) { | ||
1010 | dev_err(&pdev->dev, "invalid tx DMA channel\n"); | ||
1011 | return -ENODEV; | ||
1012 | } | ||
1013 | mcbsp->dma_req[0] = res->start; | ||
1014 | mcbsp->dma_data[0].filter_data = &mcbsp->dma_req[0]; | ||
1015 | |||
1016 | res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "rx"); | ||
1017 | if (!res) { | ||
1018 | dev_err(&pdev->dev, "invalid rx DMA channel\n"); | ||
1019 | return -ENODEV; | ||
1020 | } | ||
1021 | mcbsp->dma_req[1] = res->start; | ||
1022 | mcbsp->dma_data[1].filter_data = &mcbsp->dma_req[1]; | ||
1023 | } else { | ||
1024 | mcbsp->dma_data[0].filter_data = "tx"; | ||
1025 | mcbsp->dma_data[1].filter_data = "rx"; | ||
1026 | } | ||
1027 | |||
1028 | mcbsp->dma_data[0].addr = omap_mcbsp_dma_reg_params(mcbsp, | ||
1029 | SNDRV_PCM_STREAM_PLAYBACK); | ||
1030 | mcbsp->dma_data[1].addr = omap_mcbsp_dma_reg_params(mcbsp, | ||
1031 | SNDRV_PCM_STREAM_CAPTURE); | ||
1032 | |||
1033 | mcbsp->fclk = clk_get(&pdev->dev, "fck"); | ||
1034 | if (IS_ERR(mcbsp->fclk)) { | ||
1035 | ret = PTR_ERR(mcbsp->fclk); | ||
1036 | dev_err(mcbsp->dev, "unable to get fck: %d\n", ret); | ||
1037 | return ret; | ||
1038 | } | ||
1039 | |||
1040 | mcbsp->dma_op_mode = MCBSP_DMA_MODE_ELEMENT; | ||
1041 | if (mcbsp->pdata->buffer_size) { | ||
1042 | /* | ||
1043 | * Initially configure the maximum thresholds to a safe value. | ||
1044 | * The McBSP FIFO usage with these values should not go under | ||
1045 | * 16 locations. | ||
1046 | * If the whole FIFO without safety buffer is used, than there | ||
1047 | * is a possibility that the DMA will be not able to push the | ||
1048 | * new data on time, causing channel shifts in runtime. | ||
1049 | */ | ||
1050 | mcbsp->max_tx_thres = max_thres(mcbsp) - 0x10; | ||
1051 | mcbsp->max_rx_thres = max_thres(mcbsp) - 0x10; | ||
1052 | |||
1053 | ret = sysfs_create_group(&mcbsp->dev->kobj, | ||
1054 | &additional_attr_group); | ||
1055 | if (ret) { | ||
1056 | dev_err(mcbsp->dev, | ||
1057 | "Unable to create additional controls\n"); | ||
1058 | goto err_thres; | ||
1059 | } | ||
1060 | } else { | ||
1061 | mcbsp->max_tx_thres = -EINVAL; | ||
1062 | mcbsp->max_rx_thres = -EINVAL; | ||
1063 | } | ||
1064 | |||
1065 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sidetone"); | ||
1066 | if (res) { | ||
1067 | ret = omap_st_add(mcbsp, res); | ||
1068 | if (ret) { | ||
1069 | dev_err(mcbsp->dev, | ||
1070 | "Unable to create sidetone controls\n"); | ||
1071 | goto err_st; | ||
1072 | } | ||
1073 | } | ||
1074 | |||
1075 | return 0; | ||
1076 | |||
1077 | err_st: | ||
1078 | if (mcbsp->pdata->buffer_size) | ||
1079 | sysfs_remove_group(&mcbsp->dev->kobj, &additional_attr_group); | ||
1080 | err_thres: | ||
1081 | clk_put(mcbsp->fclk); | ||
1082 | return ret; | ||
1083 | } | ||
1084 | |||
1085 | void omap_mcbsp_cleanup(struct omap_mcbsp *mcbsp) | ||
1086 | { | ||
1087 | if (mcbsp->pdata->buffer_size) | ||
1088 | sysfs_remove_group(&mcbsp->dev->kobj, &additional_attr_group); | ||
1089 | |||
1090 | if (mcbsp->st_data) { | ||
1091 | sysfs_remove_group(&mcbsp->dev->kobj, &sidetone_attr_group); | ||
1092 | clk_put(mcbsp->st_data->mcbsp_iclk); | ||
1093 | } | ||
1094 | } | ||
diff --git a/sound/soc/omap/mcbsp.h b/sound/soc/omap/omap-mcbsp-priv.h index 92472c6ef358..7865cda4bf0a 100644 --- a/sound/soc/omap/mcbsp.h +++ b/sound/soc/omap/omap-mcbsp-priv.h | |||
@@ -1,28 +1,15 @@ | |||
1 | /* SPDX-License-Identifier: GPL-2.0 */ | ||
1 | /* | 2 | /* |
2 | * sound/soc/omap/mcbsp.h | ||
3 | * | ||
4 | * OMAP Multi-Channel Buffered Serial Port | 3 | * OMAP Multi-Channel Buffered Serial Port |
5 | * | 4 | * |
6 | * Contact: Jarkko Nikula <jarkko.nikula@bitmer.com> | 5 | * Contact: Jarkko Nikula <jarkko.nikula@bitmer.com> |
7 | * Peter Ujfalusi <peter.ujfalusi@ti.com> | 6 | * Peter Ujfalusi <peter.ujfalusi@ti.com> |
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 as published by | ||
11 | * the Free Software Foundation; either version 2 of the License, or | ||
12 | * (at your option) any later version. | ||
13 | * | ||
14 | * This program is distributed in the hope that it will be useful, | ||
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
17 | * GNU General Public License for more details. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License | ||
20 | * along with this program; if not, write to the Free Software | ||
21 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
22 | * | ||
23 | */ | 7 | */ |
24 | #ifndef __ASOC_MCBSP_H | 8 | |
25 | #define __ASOC_MCBSP_H | 9 | #ifndef __OMAP_MCBSP_PRIV_H__ |
10 | #define __OMAP_MCBSP_PRIV_H__ | ||
11 | |||
12 | #include <linux/platform_data/asoc-ti-mcbsp.h> | ||
26 | 13 | ||
27 | #ifdef CONFIG_ARCH_OMAP1 | 14 | #ifdef CONFIG_ARCH_OMAP1 |
28 | #define mcbsp_omap1() 1 | 15 | #define mcbsp_omap1() 1 |
@@ -30,8 +17,6 @@ | |||
30 | #define mcbsp_omap1() 0 | 17 | #define mcbsp_omap1() 0 |
31 | #endif | 18 | #endif |
32 | 19 | ||
33 | #include <sound/dmaengine_pcm.h> | ||
34 | |||
35 | /* McBSP register numbers. Register address offset = num * reg_step */ | 20 | /* McBSP register numbers. Register address offset = num * reg_step */ |
36 | enum { | 21 | enum { |
37 | /* Common registers */ | 22 | /* Common registers */ |
@@ -85,15 +70,6 @@ enum { | |||
85 | OMAP_MCBSP_REG_SSELCR, | 70 | OMAP_MCBSP_REG_SSELCR, |
86 | }; | 71 | }; |
87 | 72 | ||
88 | /* OMAP3 sidetone control registers */ | ||
89 | #define OMAP_ST_REG_REV 0x00 | ||
90 | #define OMAP_ST_REG_SYSCONFIG 0x10 | ||
91 | #define OMAP_ST_REG_IRQSTATUS 0x18 | ||
92 | #define OMAP_ST_REG_IRQENABLE 0x1C | ||
93 | #define OMAP_ST_REG_SGAINCR 0x24 | ||
94 | #define OMAP_ST_REG_SFIRCR 0x28 | ||
95 | #define OMAP_ST_REG_SSELCR 0x2C | ||
96 | |||
97 | /************************** McBSP SPCR1 bit definitions ***********************/ | 73 | /************************** McBSP SPCR1 bit definitions ***********************/ |
98 | #define RRST BIT(0) | 74 | #define RRST BIT(0) |
99 | #define RRDY BIT(1) | 75 | #define RRDY BIT(1) |
@@ -202,24 +178,6 @@ enum { | |||
202 | #define SIDLEMODE(value) (((value) & 0x3) << 3) | 178 | #define SIDLEMODE(value) (((value) & 0x3) << 3) |
203 | #define CLOCKACTIVITY(value) (((value) & 0x3) << 8) | 179 | #define CLOCKACTIVITY(value) (((value) & 0x3) << 8) |
204 | 180 | ||
205 | /********************** McBSP SSELCR bit definitions ***********************/ | ||
206 | #define SIDETONEEN BIT(10) | ||
207 | |||
208 | /********************** McBSP Sidetone SYSCONFIG bit definitions ***********/ | ||
209 | #define ST_AUTOIDLE BIT(0) | ||
210 | |||
211 | /********************** McBSP Sidetone SGAINCR bit definitions *************/ | ||
212 | #define ST_CH0GAIN(value) ((value) & 0xffff) /* Bits 0:15 */ | ||
213 | #define ST_CH1GAIN(value) (((value) & 0xffff) << 16) /* Bits 16:31 */ | ||
214 | |||
215 | /********************** McBSP Sidetone SFIRCR bit definitions **************/ | ||
216 | #define ST_FIRCOEFF(value) ((value) & 0xffff) /* Bits 0:15 */ | ||
217 | |||
218 | /********************** McBSP Sidetone SSELCR bit definitions **************/ | ||
219 | #define ST_SIDETONEEN BIT(0) | ||
220 | #define ST_COEFFWREN BIT(1) | ||
221 | #define ST_COEFFWRDONE BIT(2) | ||
222 | |||
223 | /********************** McBSP DMA operating modes **************************/ | 181 | /********************** McBSP DMA operating modes **************************/ |
224 | #define MCBSP_DMA_MODE_ELEMENT 0 | 182 | #define MCBSP_DMA_MODE_ELEMENT 0 |
225 | #define MCBSP_DMA_MODE_THRESHOLD 1 | 183 | #define MCBSP_DMA_MODE_THRESHOLD 1 |
@@ -278,16 +236,7 @@ struct omap_mcbsp_reg_cfg { | |||
278 | u16 rccr; | 236 | u16 rccr; |
279 | }; | 237 | }; |
280 | 238 | ||
281 | struct omap_mcbsp_st_data { | 239 | struct omap_mcbsp_st_data; |
282 | void __iomem *io_base_st; | ||
283 | struct clk *mcbsp_iclk; | ||
284 | bool running; | ||
285 | bool enabled; | ||
286 | s16 taps[128]; /* Sidetone filter coefficients */ | ||
287 | int nr_taps; /* Number of filter coefficients in use */ | ||
288 | s16 ch0gain; | ||
289 | s16 ch1gain; | ||
290 | }; | ||
291 | 240 | ||
292 | struct omap_mcbsp { | 241 | struct omap_mcbsp { |
293 | struct device *dev; | 242 | struct device *dev; |
@@ -330,29 +279,46 @@ struct omap_mcbsp { | |||
330 | struct pm_qos_request pm_qos_req; | 279 | struct pm_qos_request pm_qos_req; |
331 | }; | 280 | }; |
332 | 281 | ||
333 | void omap_mcbsp_config(struct omap_mcbsp *mcbsp, | 282 | static inline void omap_mcbsp_write(struct omap_mcbsp *mcbsp, u16 reg, u32 val) |
334 | const struct omap_mcbsp_reg_cfg *config); | 283 | { |
335 | void omap_mcbsp_set_tx_threshold(struct omap_mcbsp *mcbsp, u16 threshold); | 284 | void __iomem *addr = mcbsp->io_base + reg * mcbsp->pdata->reg_step; |
336 | void omap_mcbsp_set_rx_threshold(struct omap_mcbsp *mcbsp, u16 threshold); | 285 | |
337 | u16 omap_mcbsp_get_tx_delay(struct omap_mcbsp *mcbsp); | 286 | if (mcbsp->pdata->reg_size == 2) { |
338 | u16 omap_mcbsp_get_rx_delay(struct omap_mcbsp *mcbsp); | 287 | ((u16 *)mcbsp->reg_cache)[reg] = (u16)val; |
339 | int omap_mcbsp_get_dma_op_mode(struct omap_mcbsp *mcbsp); | 288 | writew_relaxed((u16)val, addr); |
340 | int omap_mcbsp_request(struct omap_mcbsp *mcbsp); | 289 | } else { |
341 | void omap_mcbsp_free(struct omap_mcbsp *mcbsp); | 290 | ((u32 *)mcbsp->reg_cache)[reg] = val; |
342 | void omap_mcbsp_start(struct omap_mcbsp *mcbsp, int stream); | 291 | writel_relaxed(val, addr); |
343 | void omap_mcbsp_stop(struct omap_mcbsp *mcbsp, int stream); | 292 | } |
344 | 293 | } | |
345 | /* McBSP functional clock source changing function */ | 294 | |
346 | int omap2_mcbsp_set_clks_src(struct omap_mcbsp *mcbsp, u8 fck_src_id); | 295 | static inline int omap_mcbsp_read(struct omap_mcbsp *mcbsp, u16 reg, |
296 | bool from_cache) | ||
297 | { | ||
298 | void __iomem *addr = mcbsp->io_base + reg * mcbsp->pdata->reg_step; | ||
299 | |||
300 | if (mcbsp->pdata->reg_size == 2) { | ||
301 | return !from_cache ? readw_relaxed(addr) : | ||
302 | ((u16 *)mcbsp->reg_cache)[reg]; | ||
303 | } else { | ||
304 | return !from_cache ? readl_relaxed(addr) : | ||
305 | ((u32 *)mcbsp->reg_cache)[reg]; | ||
306 | } | ||
307 | } | ||
308 | |||
309 | #define MCBSP_READ(mcbsp, reg) \ | ||
310 | omap_mcbsp_read(mcbsp, OMAP_MCBSP_REG_##reg, 0) | ||
311 | #define MCBSP_WRITE(mcbsp, reg, val) \ | ||
312 | omap_mcbsp_write(mcbsp, OMAP_MCBSP_REG_##reg, val) | ||
313 | #define MCBSP_READ_CACHE(mcbsp, reg) \ | ||
314 | omap_mcbsp_read(mcbsp, OMAP_MCBSP_REG_##reg, 1) | ||
315 | |||
347 | 316 | ||
348 | /* Sidetone specific API */ | 317 | /* Sidetone specific API */ |
349 | int omap_st_set_chgain(struct omap_mcbsp *mcbsp, int channel, s16 chgain); | 318 | int omap_mcbsp_st_init(struct platform_device *pdev); |
350 | int omap_st_get_chgain(struct omap_mcbsp *mcbsp, int channel, s16 *chgain); | 319 | void omap_mcbsp_st_cleanup(struct platform_device *pdev); |
351 | int omap_st_enable(struct omap_mcbsp *mcbsp); | ||
352 | int omap_st_disable(struct omap_mcbsp *mcbsp); | ||
353 | int omap_st_is_enabled(struct omap_mcbsp *mcbsp); | ||
354 | 320 | ||
355 | int omap_mcbsp_init(struct platform_device *pdev); | 321 | int omap_mcbsp_st_start(struct omap_mcbsp *mcbsp); |
356 | void omap_mcbsp_cleanup(struct omap_mcbsp *mcbsp); | 322 | int omap_mcbsp_st_stop(struct omap_mcbsp *mcbsp); |
357 | 323 | ||
358 | #endif /* __ASOC_MCBSP_H */ | 324 | #endif /* __OMAP_MCBSP_PRIV_H__ */ |
diff --git a/sound/soc/omap/omap-mcbsp-st.c b/sound/soc/omap/omap-mcbsp-st.c new file mode 100644 index 000000000000..1a3fe854e856 --- /dev/null +++ b/sound/soc/omap/omap-mcbsp-st.c | |||
@@ -0,0 +1,516 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* | ||
3 | * McBSP Sidetone support | ||
4 | * | ||
5 | * Copyright (C) 2004 Nokia Corporation | ||
6 | * Author: Samuel Ortiz <samuel.ortiz@nokia.com> | ||
7 | * | ||
8 | * Contact: Jarkko Nikula <jarkko.nikula@bitmer.com> | ||
9 | * Peter Ujfalusi <peter.ujfalusi@ti.com> | ||
10 | */ | ||
11 | |||
12 | #include <linux/module.h> | ||
13 | #include <linux/init.h> | ||
14 | #include <linux/device.h> | ||
15 | #include <linux/platform_device.h> | ||
16 | #include <linux/interrupt.h> | ||
17 | #include <linux/err.h> | ||
18 | #include <linux/clk.h> | ||
19 | #include <linux/delay.h> | ||
20 | #include <linux/io.h> | ||
21 | #include <linux/slab.h> | ||
22 | #include <linux/pm_runtime.h> | ||
23 | |||
24 | #include "omap-mcbsp.h" | ||
25 | #include "omap-mcbsp-priv.h" | ||
26 | |||
27 | /* OMAP3 sidetone control registers */ | ||
28 | #define OMAP_ST_REG_REV 0x00 | ||
29 | #define OMAP_ST_REG_SYSCONFIG 0x10 | ||
30 | #define OMAP_ST_REG_IRQSTATUS 0x18 | ||
31 | #define OMAP_ST_REG_IRQENABLE 0x1C | ||
32 | #define OMAP_ST_REG_SGAINCR 0x24 | ||
33 | #define OMAP_ST_REG_SFIRCR 0x28 | ||
34 | #define OMAP_ST_REG_SSELCR 0x2C | ||
35 | |||
36 | /********************** McBSP SSELCR bit definitions ***********************/ | ||
37 | #define SIDETONEEN BIT(10) | ||
38 | |||
39 | /********************** McBSP Sidetone SYSCONFIG bit definitions ***********/ | ||
40 | #define ST_AUTOIDLE BIT(0) | ||
41 | |||
42 | /********************** McBSP Sidetone SGAINCR bit definitions *************/ | ||
43 | #define ST_CH0GAIN(value) ((value) & 0xffff) /* Bits 0:15 */ | ||
44 | #define ST_CH1GAIN(value) (((value) & 0xffff) << 16) /* Bits 16:31 */ | ||
45 | |||
46 | /********************** McBSP Sidetone SFIRCR bit definitions **************/ | ||
47 | #define ST_FIRCOEFF(value) ((value) & 0xffff) /* Bits 0:15 */ | ||
48 | |||
49 | /********************** McBSP Sidetone SSELCR bit definitions **************/ | ||
50 | #define ST_SIDETONEEN BIT(0) | ||
51 | #define ST_COEFFWREN BIT(1) | ||
52 | #define ST_COEFFWRDONE BIT(2) | ||
53 | |||
54 | struct omap_mcbsp_st_data { | ||
55 | void __iomem *io_base_st; | ||
56 | struct clk *mcbsp_iclk; | ||
57 | bool running; | ||
58 | bool enabled; | ||
59 | s16 taps[128]; /* Sidetone filter coefficients */ | ||
60 | int nr_taps; /* Number of filter coefficients in use */ | ||
61 | s16 ch0gain; | ||
62 | s16 ch1gain; | ||
63 | }; | ||
64 | |||
65 | static void omap_mcbsp_st_write(struct omap_mcbsp *mcbsp, u16 reg, u32 val) | ||
66 | { | ||
67 | writel_relaxed(val, mcbsp->st_data->io_base_st + reg); | ||
68 | } | ||
69 | |||
70 | static int omap_mcbsp_st_read(struct omap_mcbsp *mcbsp, u16 reg) | ||
71 | { | ||
72 | return readl_relaxed(mcbsp->st_data->io_base_st + reg); | ||
73 | } | ||
74 | |||
75 | #define MCBSP_ST_READ(mcbsp, reg) omap_mcbsp_st_read(mcbsp, OMAP_ST_REG_##reg) | ||
76 | #define MCBSP_ST_WRITE(mcbsp, reg, val) \ | ||
77 | omap_mcbsp_st_write(mcbsp, OMAP_ST_REG_##reg, val) | ||
78 | |||
79 | static void omap_mcbsp_st_on(struct omap_mcbsp *mcbsp) | ||
80 | { | ||
81 | unsigned int w; | ||
82 | |||
83 | if (mcbsp->pdata->force_ick_on) | ||
84 | mcbsp->pdata->force_ick_on(mcbsp->st_data->mcbsp_iclk, true); | ||
85 | |||
86 | /* Disable Sidetone clock auto-gating for normal operation */ | ||
87 | w = MCBSP_ST_READ(mcbsp, SYSCONFIG); | ||
88 | MCBSP_ST_WRITE(mcbsp, SYSCONFIG, w & ~(ST_AUTOIDLE)); | ||
89 | |||
90 | /* Enable McBSP Sidetone */ | ||
91 | w = MCBSP_READ(mcbsp, SSELCR); | ||
92 | MCBSP_WRITE(mcbsp, SSELCR, w | SIDETONEEN); | ||
93 | |||
94 | /* Enable Sidetone from Sidetone Core */ | ||
95 | w = MCBSP_ST_READ(mcbsp, SSELCR); | ||
96 | MCBSP_ST_WRITE(mcbsp, SSELCR, w | ST_SIDETONEEN); | ||
97 | } | ||
98 | |||
99 | static void omap_mcbsp_st_off(struct omap_mcbsp *mcbsp) | ||
100 | { | ||
101 | unsigned int w; | ||
102 | |||
103 | w = MCBSP_ST_READ(mcbsp, SSELCR); | ||
104 | MCBSP_ST_WRITE(mcbsp, SSELCR, w & ~(ST_SIDETONEEN)); | ||
105 | |||
106 | w = MCBSP_READ(mcbsp, SSELCR); | ||
107 | MCBSP_WRITE(mcbsp, SSELCR, w & ~(SIDETONEEN)); | ||
108 | |||
109 | /* Enable Sidetone clock auto-gating to reduce power consumption */ | ||
110 | w = MCBSP_ST_READ(mcbsp, SYSCONFIG); | ||
111 | MCBSP_ST_WRITE(mcbsp, SYSCONFIG, w | ST_AUTOIDLE); | ||
112 | |||
113 | if (mcbsp->pdata->force_ick_on) | ||
114 | mcbsp->pdata->force_ick_on(mcbsp->st_data->mcbsp_iclk, false); | ||
115 | } | ||
116 | |||
117 | static void omap_mcbsp_st_fir_write(struct omap_mcbsp *mcbsp, s16 *fir) | ||
118 | { | ||
119 | u16 val, i; | ||
120 | |||
121 | val = MCBSP_ST_READ(mcbsp, SSELCR); | ||
122 | |||
123 | if (val & ST_COEFFWREN) | ||
124 | MCBSP_ST_WRITE(mcbsp, SSELCR, val & ~(ST_COEFFWREN)); | ||
125 | |||
126 | MCBSP_ST_WRITE(mcbsp, SSELCR, val | ST_COEFFWREN); | ||
127 | |||
128 | for (i = 0; i < 128; i++) | ||
129 | MCBSP_ST_WRITE(mcbsp, SFIRCR, fir[i]); | ||
130 | |||
131 | i = 0; | ||
132 | |||
133 | val = MCBSP_ST_READ(mcbsp, SSELCR); | ||
134 | while (!(val & ST_COEFFWRDONE) && (++i < 1000)) | ||
135 | val = MCBSP_ST_READ(mcbsp, SSELCR); | ||
136 | |||
137 | MCBSP_ST_WRITE(mcbsp, SSELCR, val & ~(ST_COEFFWREN)); | ||
138 | |||
139 | if (i == 1000) | ||
140 | dev_err(mcbsp->dev, "McBSP FIR load error!\n"); | ||
141 | } | ||
142 | |||
143 | static void omap_mcbsp_st_chgain(struct omap_mcbsp *mcbsp) | ||
144 | { | ||
145 | u16 w; | ||
146 | struct omap_mcbsp_st_data *st_data = mcbsp->st_data; | ||
147 | |||
148 | w = MCBSP_ST_READ(mcbsp, SSELCR); | ||
149 | |||
150 | MCBSP_ST_WRITE(mcbsp, SGAINCR, ST_CH0GAIN(st_data->ch0gain) | | ||
151 | ST_CH1GAIN(st_data->ch1gain)); | ||
152 | } | ||
153 | |||
154 | static int omap_mcbsp_st_set_chgain(struct omap_mcbsp *mcbsp, int channel, | ||
155 | s16 chgain) | ||
156 | { | ||
157 | struct omap_mcbsp_st_data *st_data = mcbsp->st_data; | ||
158 | int ret = 0; | ||
159 | |||
160 | if (!st_data) | ||
161 | return -ENOENT; | ||
162 | |||
163 | spin_lock_irq(&mcbsp->lock); | ||
164 | if (channel == 0) | ||
165 | st_data->ch0gain = chgain; | ||
166 | else if (channel == 1) | ||
167 | st_data->ch1gain = chgain; | ||
168 | else | ||
169 | ret = -EINVAL; | ||
170 | |||
171 | if (st_data->enabled) | ||
172 | omap_mcbsp_st_chgain(mcbsp); | ||
173 | spin_unlock_irq(&mcbsp->lock); | ||
174 | |||
175 | return ret; | ||
176 | } | ||
177 | |||
178 | static int omap_mcbsp_st_get_chgain(struct omap_mcbsp *mcbsp, int channel, | ||
179 | s16 *chgain) | ||
180 | { | ||
181 | struct omap_mcbsp_st_data *st_data = mcbsp->st_data; | ||
182 | int ret = 0; | ||
183 | |||
184 | if (!st_data) | ||
185 | return -ENOENT; | ||
186 | |||
187 | spin_lock_irq(&mcbsp->lock); | ||
188 | if (channel == 0) | ||
189 | *chgain = st_data->ch0gain; | ||
190 | else if (channel == 1) | ||
191 | *chgain = st_data->ch1gain; | ||
192 | else | ||
193 | ret = -EINVAL; | ||
194 | spin_unlock_irq(&mcbsp->lock); | ||
195 | |||
196 | return ret; | ||
197 | } | ||
198 | |||
199 | static int omap_mcbsp_st_enable(struct omap_mcbsp *mcbsp) | ||
200 | { | ||
201 | struct omap_mcbsp_st_data *st_data = mcbsp->st_data; | ||
202 | |||
203 | if (!st_data) | ||
204 | return -ENODEV; | ||
205 | |||
206 | spin_lock_irq(&mcbsp->lock); | ||
207 | st_data->enabled = 1; | ||
208 | omap_mcbsp_st_start(mcbsp); | ||
209 | spin_unlock_irq(&mcbsp->lock); | ||
210 | |||
211 | return 0; | ||
212 | } | ||
213 | |||
214 | static int omap_mcbsp_st_disable(struct omap_mcbsp *mcbsp) | ||
215 | { | ||
216 | struct omap_mcbsp_st_data *st_data = mcbsp->st_data; | ||
217 | int ret = 0; | ||
218 | |||
219 | if (!st_data) | ||
220 | return -ENODEV; | ||
221 | |||
222 | spin_lock_irq(&mcbsp->lock); | ||
223 | omap_mcbsp_st_stop(mcbsp); | ||
224 | st_data->enabled = 0; | ||
225 | spin_unlock_irq(&mcbsp->lock); | ||
226 | |||
227 | return ret; | ||
228 | } | ||
229 | |||
230 | static int omap_mcbsp_st_is_enabled(struct omap_mcbsp *mcbsp) | ||
231 | { | ||
232 | struct omap_mcbsp_st_data *st_data = mcbsp->st_data; | ||
233 | |||
234 | if (!st_data) | ||
235 | return -ENODEV; | ||
236 | |||
237 | return st_data->enabled; | ||
238 | } | ||
239 | |||
240 | static ssize_t st_taps_show(struct device *dev, | ||
241 | struct device_attribute *attr, char *buf) | ||
242 | { | ||
243 | struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); | ||
244 | struct omap_mcbsp_st_data *st_data = mcbsp->st_data; | ||
245 | ssize_t status = 0; | ||
246 | int i; | ||
247 | |||
248 | spin_lock_irq(&mcbsp->lock); | ||
249 | for (i = 0; i < st_data->nr_taps; i++) | ||
250 | status += sprintf(&buf[status], (i ? ", %d" : "%d"), | ||
251 | st_data->taps[i]); | ||
252 | if (i) | ||
253 | status += sprintf(&buf[status], "\n"); | ||
254 | spin_unlock_irq(&mcbsp->lock); | ||
255 | |||
256 | return status; | ||
257 | } | ||
258 | |||
259 | static ssize_t st_taps_store(struct device *dev, | ||
260 | struct device_attribute *attr, | ||
261 | const char *buf, size_t size) | ||
262 | { | ||
263 | struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); | ||
264 | struct omap_mcbsp_st_data *st_data = mcbsp->st_data; | ||
265 | int val, tmp, status, i = 0; | ||
266 | |||
267 | spin_lock_irq(&mcbsp->lock); | ||
268 | memset(st_data->taps, 0, sizeof(st_data->taps)); | ||
269 | st_data->nr_taps = 0; | ||
270 | |||
271 | do { | ||
272 | status = sscanf(buf, "%d%n", &val, &tmp); | ||
273 | if (status < 0 || status == 0) { | ||
274 | size = -EINVAL; | ||
275 | goto out; | ||
276 | } | ||
277 | if (val < -32768 || val > 32767) { | ||
278 | size = -EINVAL; | ||
279 | goto out; | ||
280 | } | ||
281 | st_data->taps[i++] = val; | ||
282 | buf += tmp; | ||
283 | if (*buf != ',') | ||
284 | break; | ||
285 | buf++; | ||
286 | } while (1); | ||
287 | |||
288 | st_data->nr_taps = i; | ||
289 | |||
290 | out: | ||
291 | spin_unlock_irq(&mcbsp->lock); | ||
292 | |||
293 | return size; | ||
294 | } | ||
295 | |||
296 | static DEVICE_ATTR_RW(st_taps); | ||
297 | |||
298 | static const struct attribute *sidetone_attrs[] = { | ||
299 | &dev_attr_st_taps.attr, | ||
300 | NULL, | ||
301 | }; | ||
302 | |||
303 | static const struct attribute_group sidetone_attr_group = { | ||
304 | .attrs = (struct attribute **)sidetone_attrs, | ||
305 | }; | ||
306 | |||
307 | int omap_mcbsp_st_start(struct omap_mcbsp *mcbsp) | ||
308 | { | ||
309 | struct omap_mcbsp_st_data *st_data = mcbsp->st_data; | ||
310 | |||
311 | if (st_data->enabled && !st_data->running) { | ||
312 | omap_mcbsp_st_fir_write(mcbsp, st_data->taps); | ||
313 | omap_mcbsp_st_chgain(mcbsp); | ||
314 | |||
315 | if (!mcbsp->free) { | ||
316 | omap_mcbsp_st_on(mcbsp); | ||
317 | st_data->running = 1; | ||
318 | } | ||
319 | } | ||
320 | |||
321 | return 0; | ||
322 | } | ||
323 | |||
324 | int omap_mcbsp_st_stop(struct omap_mcbsp *mcbsp) | ||
325 | { | ||
326 | struct omap_mcbsp_st_data *st_data = mcbsp->st_data; | ||
327 | |||
328 | if (st_data->running) { | ||
329 | if (!mcbsp->free) { | ||
330 | omap_mcbsp_st_off(mcbsp); | ||
331 | st_data->running = 0; | ||
332 | } | ||
333 | } | ||
334 | |||
335 | return 0; | ||
336 | } | ||
337 | |||
338 | int omap_mcbsp_st_init(struct platform_device *pdev) | ||
339 | { | ||
340 | struct omap_mcbsp *mcbsp = platform_get_drvdata(pdev); | ||
341 | struct omap_mcbsp_st_data *st_data; | ||
342 | struct resource *res; | ||
343 | int ret; | ||
344 | |||
345 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sidetone"); | ||
346 | if (!res) | ||
347 | return 0; | ||
348 | |||
349 | st_data = devm_kzalloc(mcbsp->dev, sizeof(*mcbsp->st_data), GFP_KERNEL); | ||
350 | if (!st_data) | ||
351 | return -ENOMEM; | ||
352 | |||
353 | st_data->mcbsp_iclk = clk_get(mcbsp->dev, "ick"); | ||
354 | if (IS_ERR(st_data->mcbsp_iclk)) { | ||
355 | dev_warn(mcbsp->dev, | ||
356 | "Failed to get ick, sidetone might be broken\n"); | ||
357 | st_data->mcbsp_iclk = NULL; | ||
358 | } | ||
359 | |||
360 | st_data->io_base_st = devm_ioremap(mcbsp->dev, res->start, | ||
361 | resource_size(res)); | ||
362 | if (!st_data->io_base_st) | ||
363 | return -ENOMEM; | ||
364 | |||
365 | ret = sysfs_create_group(&mcbsp->dev->kobj, &sidetone_attr_group); | ||
366 | if (ret) | ||
367 | return ret; | ||
368 | |||
369 | mcbsp->st_data = st_data; | ||
370 | |||
371 | return 0; | ||
372 | } | ||
373 | |||
374 | void omap_mcbsp_st_cleanup(struct platform_device *pdev) | ||
375 | { | ||
376 | struct omap_mcbsp *mcbsp = platform_get_drvdata(pdev); | ||
377 | |||
378 | if (mcbsp->st_data) { | ||
379 | sysfs_remove_group(&mcbsp->dev->kobj, &sidetone_attr_group); | ||
380 | clk_put(mcbsp->st_data->mcbsp_iclk); | ||
381 | } | ||
382 | } | ||
383 | |||
384 | static int omap_mcbsp_st_info_volsw(struct snd_kcontrol *kcontrol, | ||
385 | struct snd_ctl_elem_info *uinfo) | ||
386 | { | ||
387 | struct soc_mixer_control *mc = | ||
388 | (struct soc_mixer_control *)kcontrol->private_value; | ||
389 | int max = mc->max; | ||
390 | int min = mc->min; | ||
391 | |||
392 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
393 | uinfo->count = 1; | ||
394 | uinfo->value.integer.min = min; | ||
395 | uinfo->value.integer.max = max; | ||
396 | return 0; | ||
397 | } | ||
398 | |||
399 | #define OMAP_MCBSP_ST_CHANNEL_VOLUME(channel) \ | ||
400 | static int \ | ||
401 | omap_mcbsp_set_st_ch##channel##_volume(struct snd_kcontrol *kc, \ | ||
402 | struct snd_ctl_elem_value *uc) \ | ||
403 | { \ | ||
404 | struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kc); \ | ||
405 | struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); \ | ||
406 | struct soc_mixer_control *mc = \ | ||
407 | (struct soc_mixer_control *)kc->private_value; \ | ||
408 | int max = mc->max; \ | ||
409 | int min = mc->min; \ | ||
410 | int val = uc->value.integer.value[0]; \ | ||
411 | \ | ||
412 | if (val < min || val > max) \ | ||
413 | return -EINVAL; \ | ||
414 | \ | ||
415 | /* OMAP McBSP implementation uses index values 0..4 */ \ | ||
416 | return omap_mcbsp_st_set_chgain(mcbsp, channel, val); \ | ||
417 | } \ | ||
418 | \ | ||
419 | static int \ | ||
420 | omap_mcbsp_get_st_ch##channel##_volume(struct snd_kcontrol *kc, \ | ||
421 | struct snd_ctl_elem_value *uc) \ | ||
422 | { \ | ||
423 | struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kc); \ | ||
424 | struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); \ | ||
425 | s16 chgain; \ | ||
426 | \ | ||
427 | if (omap_mcbsp_st_get_chgain(mcbsp, channel, &chgain)) \ | ||
428 | return -EAGAIN; \ | ||
429 | \ | ||
430 | uc->value.integer.value[0] = chgain; \ | ||
431 | return 0; \ | ||
432 | } | ||
433 | |||
434 | OMAP_MCBSP_ST_CHANNEL_VOLUME(0) | ||
435 | OMAP_MCBSP_ST_CHANNEL_VOLUME(1) | ||
436 | |||
437 | static int omap_mcbsp_st_put_mode(struct snd_kcontrol *kcontrol, | ||
438 | struct snd_ctl_elem_value *ucontrol) | ||
439 | { | ||
440 | struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); | ||
441 | struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); | ||
442 | u8 value = ucontrol->value.integer.value[0]; | ||
443 | |||
444 | if (value == omap_mcbsp_st_is_enabled(mcbsp)) | ||
445 | return 0; | ||
446 | |||
447 | if (value) | ||
448 | omap_mcbsp_st_enable(mcbsp); | ||
449 | else | ||
450 | omap_mcbsp_st_disable(mcbsp); | ||
451 | |||
452 | return 1; | ||
453 | } | ||
454 | |||
455 | static int omap_mcbsp_st_get_mode(struct snd_kcontrol *kcontrol, | ||
456 | struct snd_ctl_elem_value *ucontrol) | ||
457 | { | ||
458 | struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); | ||
459 | struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); | ||
460 | |||
461 | ucontrol->value.integer.value[0] = omap_mcbsp_st_is_enabled(mcbsp); | ||
462 | return 0; | ||
463 | } | ||
464 | |||
465 | #define OMAP_MCBSP_SOC_SINGLE_S16_EXT(xname, xmin, xmax, \ | ||
466 | xhandler_get, xhandler_put) \ | ||
467 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ | ||
468 | .info = omap_mcbsp_st_info_volsw, \ | ||
469 | .get = xhandler_get, .put = xhandler_put, \ | ||
470 | .private_value = (unsigned long)&(struct soc_mixer_control) \ | ||
471 | {.min = xmin, .max = xmax} } | ||
472 | |||
473 | #define OMAP_MCBSP_ST_CONTROLS(port) \ | ||
474 | static const struct snd_kcontrol_new omap_mcbsp##port##_st_controls[] = { \ | ||
475 | SOC_SINGLE_EXT("McBSP" #port " Sidetone Switch", 1, 0, 1, 0, \ | ||
476 | omap_mcbsp_st_get_mode, omap_mcbsp_st_put_mode), \ | ||
477 | OMAP_MCBSP_SOC_SINGLE_S16_EXT("McBSP" #port " Sidetone Channel 0 Volume", \ | ||
478 | -32768, 32767, \ | ||
479 | omap_mcbsp_get_st_ch0_volume, \ | ||
480 | omap_mcbsp_set_st_ch0_volume), \ | ||
481 | OMAP_MCBSP_SOC_SINGLE_S16_EXT("McBSP" #port " Sidetone Channel 1 Volume", \ | ||
482 | -32768, 32767, \ | ||
483 | omap_mcbsp_get_st_ch1_volume, \ | ||
484 | omap_mcbsp_set_st_ch1_volume), \ | ||
485 | } | ||
486 | |||
487 | OMAP_MCBSP_ST_CONTROLS(2); | ||
488 | OMAP_MCBSP_ST_CONTROLS(3); | ||
489 | |||
490 | int omap_mcbsp_st_add_controls(struct snd_soc_pcm_runtime *rtd, int port_id) | ||
491 | { | ||
492 | struct snd_soc_dai *cpu_dai = rtd->cpu_dai; | ||
493 | struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); | ||
494 | |||
495 | if (!mcbsp->st_data) { | ||
496 | dev_warn(mcbsp->dev, "No sidetone data for port\n"); | ||
497 | return 0; | ||
498 | } | ||
499 | |||
500 | switch (port_id) { | ||
501 | case 2: /* McBSP 2 */ | ||
502 | return snd_soc_add_dai_controls(cpu_dai, | ||
503 | omap_mcbsp2_st_controls, | ||
504 | ARRAY_SIZE(omap_mcbsp2_st_controls)); | ||
505 | case 3: /* McBSP 3 */ | ||
506 | return snd_soc_add_dai_controls(cpu_dai, | ||
507 | omap_mcbsp3_st_controls, | ||
508 | ARRAY_SIZE(omap_mcbsp3_st_controls)); | ||
509 | default: | ||
510 | dev_err(mcbsp->dev, "Port %d not supported\n", port_id); | ||
511 | break; | ||
512 | } | ||
513 | |||
514 | return -EINVAL; | ||
515 | } | ||
516 | EXPORT_SYMBOL_GPL(omap_mcbsp_st_add_controls); | ||
diff --git a/sound/soc/omap/omap-mcbsp.c b/sound/soc/omap/omap-mcbsp.c index 69a6b8ad6d42..e48fad11a0cc 100644 --- a/sound/soc/omap/omap-mcbsp.c +++ b/sound/soc/omap/omap-mcbsp.c | |||
@@ -35,21 +35,12 @@ | |||
35 | #include <sound/soc.h> | 35 | #include <sound/soc.h> |
36 | #include <sound/dmaengine_pcm.h> | 36 | #include <sound/dmaengine_pcm.h> |
37 | 37 | ||
38 | #include <linux/platform_data/asoc-ti-mcbsp.h> | 38 | #include "omap-mcbsp-priv.h" |
39 | #include "mcbsp.h" | ||
40 | #include "omap-mcbsp.h" | 39 | #include "omap-mcbsp.h" |
41 | #include "sdma-pcm.h" | 40 | #include "sdma-pcm.h" |
42 | 41 | ||
43 | #define OMAP_MCBSP_RATES (SNDRV_PCM_RATE_8000_96000) | 42 | #define OMAP_MCBSP_RATES (SNDRV_PCM_RATE_8000_96000) |
44 | 43 | ||
45 | #define OMAP_MCBSP_SOC_SINGLE_S16_EXT(xname, xmin, xmax, \ | ||
46 | xhandler_get, xhandler_put) \ | ||
47 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ | ||
48 | .info = omap_mcbsp_st_info_volsw, \ | ||
49 | .get = xhandler_get, .put = xhandler_put, \ | ||
50 | .private_value = (unsigned long) &(struct soc_mixer_control) \ | ||
51 | {.min = xmin, .max = xmax} } | ||
52 | |||
53 | enum { | 44 | enum { |
54 | OMAP_MCBSP_WORD_8 = 0, | 45 | OMAP_MCBSP_WORD_8 = 0, |
55 | OMAP_MCBSP_WORD_12, | 46 | OMAP_MCBSP_WORD_12, |
@@ -59,6 +50,702 @@ enum { | |||
59 | OMAP_MCBSP_WORD_32, | 50 | OMAP_MCBSP_WORD_32, |
60 | }; | 51 | }; |
61 | 52 | ||
53 | static void omap_mcbsp_dump_reg(struct omap_mcbsp *mcbsp) | ||
54 | { | ||
55 | dev_dbg(mcbsp->dev, "**** McBSP%d regs ****\n", mcbsp->id); | ||
56 | dev_dbg(mcbsp->dev, "DRR2: 0x%04x\n", MCBSP_READ(mcbsp, DRR2)); | ||
57 | dev_dbg(mcbsp->dev, "DRR1: 0x%04x\n", MCBSP_READ(mcbsp, DRR1)); | ||
58 | dev_dbg(mcbsp->dev, "DXR2: 0x%04x\n", MCBSP_READ(mcbsp, DXR2)); | ||
59 | dev_dbg(mcbsp->dev, "DXR1: 0x%04x\n", MCBSP_READ(mcbsp, DXR1)); | ||
60 | dev_dbg(mcbsp->dev, "SPCR2: 0x%04x\n", MCBSP_READ(mcbsp, SPCR2)); | ||
61 | dev_dbg(mcbsp->dev, "SPCR1: 0x%04x\n", MCBSP_READ(mcbsp, SPCR1)); | ||
62 | dev_dbg(mcbsp->dev, "RCR2: 0x%04x\n", MCBSP_READ(mcbsp, RCR2)); | ||
63 | dev_dbg(mcbsp->dev, "RCR1: 0x%04x\n", MCBSP_READ(mcbsp, RCR1)); | ||
64 | dev_dbg(mcbsp->dev, "XCR2: 0x%04x\n", MCBSP_READ(mcbsp, XCR2)); | ||
65 | dev_dbg(mcbsp->dev, "XCR1: 0x%04x\n", MCBSP_READ(mcbsp, XCR1)); | ||
66 | dev_dbg(mcbsp->dev, "SRGR2: 0x%04x\n", MCBSP_READ(mcbsp, SRGR2)); | ||
67 | dev_dbg(mcbsp->dev, "SRGR1: 0x%04x\n", MCBSP_READ(mcbsp, SRGR1)); | ||
68 | dev_dbg(mcbsp->dev, "PCR0: 0x%04x\n", MCBSP_READ(mcbsp, PCR0)); | ||
69 | dev_dbg(mcbsp->dev, "***********************\n"); | ||
70 | } | ||
71 | |||
72 | static int omap2_mcbsp_set_clks_src(struct omap_mcbsp *mcbsp, u8 fck_src_id) | ||
73 | { | ||
74 | struct clk *fck_src; | ||
75 | const char *src; | ||
76 | int r; | ||
77 | |||
78 | if (fck_src_id == MCBSP_CLKS_PAD_SRC) | ||
79 | src = "pad_fck"; | ||
80 | else if (fck_src_id == MCBSP_CLKS_PRCM_SRC) | ||
81 | src = "prcm_fck"; | ||
82 | else | ||
83 | return -EINVAL; | ||
84 | |||
85 | fck_src = clk_get(mcbsp->dev, src); | ||
86 | if (IS_ERR(fck_src)) { | ||
87 | dev_err(mcbsp->dev, "CLKS: could not clk_get() %s\n", src); | ||
88 | return -EINVAL; | ||
89 | } | ||
90 | |||
91 | pm_runtime_put_sync(mcbsp->dev); | ||
92 | |||
93 | r = clk_set_parent(mcbsp->fclk, fck_src); | ||
94 | if (r) { | ||
95 | dev_err(mcbsp->dev, "CLKS: could not clk_set_parent() to %s\n", | ||
96 | src); | ||
97 | clk_put(fck_src); | ||
98 | return r; | ||
99 | } | ||
100 | |||
101 | pm_runtime_get_sync(mcbsp->dev); | ||
102 | |||
103 | clk_put(fck_src); | ||
104 | |||
105 | return 0; | ||
106 | } | ||
107 | |||
108 | static irqreturn_t omap_mcbsp_irq_handler(int irq, void *data) | ||
109 | { | ||
110 | struct omap_mcbsp *mcbsp = data; | ||
111 | u16 irqst; | ||
112 | |||
113 | irqst = MCBSP_READ(mcbsp, IRQST); | ||
114 | dev_dbg(mcbsp->dev, "IRQ callback : 0x%x\n", irqst); | ||
115 | |||
116 | if (irqst & RSYNCERREN) | ||
117 | dev_err(mcbsp->dev, "RX Frame Sync Error!\n"); | ||
118 | if (irqst & RFSREN) | ||
119 | dev_dbg(mcbsp->dev, "RX Frame Sync\n"); | ||
120 | if (irqst & REOFEN) | ||
121 | dev_dbg(mcbsp->dev, "RX End Of Frame\n"); | ||
122 | if (irqst & RRDYEN) | ||
123 | dev_dbg(mcbsp->dev, "RX Buffer Threshold Reached\n"); | ||
124 | if (irqst & RUNDFLEN) | ||
125 | dev_err(mcbsp->dev, "RX Buffer Underflow!\n"); | ||
126 | if (irqst & ROVFLEN) | ||
127 | dev_err(mcbsp->dev, "RX Buffer Overflow!\n"); | ||
128 | |||
129 | if (irqst & XSYNCERREN) | ||
130 | dev_err(mcbsp->dev, "TX Frame Sync Error!\n"); | ||
131 | if (irqst & XFSXEN) | ||
132 | dev_dbg(mcbsp->dev, "TX Frame Sync\n"); | ||
133 | if (irqst & XEOFEN) | ||
134 | dev_dbg(mcbsp->dev, "TX End Of Frame\n"); | ||
135 | if (irqst & XRDYEN) | ||
136 | dev_dbg(mcbsp->dev, "TX Buffer threshold Reached\n"); | ||
137 | if (irqst & XUNDFLEN) | ||
138 | dev_err(mcbsp->dev, "TX Buffer Underflow!\n"); | ||
139 | if (irqst & XOVFLEN) | ||
140 | dev_err(mcbsp->dev, "TX Buffer Overflow!\n"); | ||
141 | if (irqst & XEMPTYEOFEN) | ||
142 | dev_dbg(mcbsp->dev, "TX Buffer empty at end of frame\n"); | ||
143 | |||
144 | MCBSP_WRITE(mcbsp, IRQST, irqst); | ||
145 | |||
146 | return IRQ_HANDLED; | ||
147 | } | ||
148 | |||
149 | static irqreturn_t omap_mcbsp_tx_irq_handler(int irq, void *data) | ||
150 | { | ||
151 | struct omap_mcbsp *mcbsp = data; | ||
152 | u16 irqst_spcr2; | ||
153 | |||
154 | irqst_spcr2 = MCBSP_READ(mcbsp, SPCR2); | ||
155 | dev_dbg(mcbsp->dev, "TX IRQ callback : 0x%x\n", irqst_spcr2); | ||
156 | |||
157 | if (irqst_spcr2 & XSYNC_ERR) { | ||
158 | dev_err(mcbsp->dev, "TX Frame Sync Error! : 0x%x\n", | ||
159 | irqst_spcr2); | ||
160 | /* Writing zero to XSYNC_ERR clears the IRQ */ | ||
161 | MCBSP_WRITE(mcbsp, SPCR2, MCBSP_READ_CACHE(mcbsp, SPCR2)); | ||
162 | } | ||
163 | |||
164 | return IRQ_HANDLED; | ||
165 | } | ||
166 | |||
167 | static irqreturn_t omap_mcbsp_rx_irq_handler(int irq, void *data) | ||
168 | { | ||
169 | struct omap_mcbsp *mcbsp = data; | ||
170 | u16 irqst_spcr1; | ||
171 | |||
172 | irqst_spcr1 = MCBSP_READ(mcbsp, SPCR1); | ||
173 | dev_dbg(mcbsp->dev, "RX IRQ callback : 0x%x\n", irqst_spcr1); | ||
174 | |||
175 | if (irqst_spcr1 & RSYNC_ERR) { | ||
176 | dev_err(mcbsp->dev, "RX Frame Sync Error! : 0x%x\n", | ||
177 | irqst_spcr1); | ||
178 | /* Writing zero to RSYNC_ERR clears the IRQ */ | ||
179 | MCBSP_WRITE(mcbsp, SPCR1, MCBSP_READ_CACHE(mcbsp, SPCR1)); | ||
180 | } | ||
181 | |||
182 | return IRQ_HANDLED; | ||
183 | } | ||
184 | |||
185 | /* | ||
186 | * omap_mcbsp_config simply write a config to the | ||
187 | * appropriate McBSP. | ||
188 | * You either call this function or set the McBSP registers | ||
189 | * by yourself before calling omap_mcbsp_start(). | ||
190 | */ | ||
191 | static void omap_mcbsp_config(struct omap_mcbsp *mcbsp, | ||
192 | const struct omap_mcbsp_reg_cfg *config) | ||
193 | { | ||
194 | dev_dbg(mcbsp->dev, "Configuring McBSP%d phys_base: 0x%08lx\n", | ||
195 | mcbsp->id, mcbsp->phys_base); | ||
196 | |||
197 | /* We write the given config */ | ||
198 | MCBSP_WRITE(mcbsp, SPCR2, config->spcr2); | ||
199 | MCBSP_WRITE(mcbsp, SPCR1, config->spcr1); | ||
200 | MCBSP_WRITE(mcbsp, RCR2, config->rcr2); | ||
201 | MCBSP_WRITE(mcbsp, RCR1, config->rcr1); | ||
202 | MCBSP_WRITE(mcbsp, XCR2, config->xcr2); | ||
203 | MCBSP_WRITE(mcbsp, XCR1, config->xcr1); | ||
204 | MCBSP_WRITE(mcbsp, SRGR2, config->srgr2); | ||
205 | MCBSP_WRITE(mcbsp, SRGR1, config->srgr1); | ||
206 | MCBSP_WRITE(mcbsp, MCR2, config->mcr2); | ||
207 | MCBSP_WRITE(mcbsp, MCR1, config->mcr1); | ||
208 | MCBSP_WRITE(mcbsp, PCR0, config->pcr0); | ||
209 | if (mcbsp->pdata->has_ccr) { | ||
210 | MCBSP_WRITE(mcbsp, XCCR, config->xccr); | ||
211 | MCBSP_WRITE(mcbsp, RCCR, config->rccr); | ||
212 | } | ||
213 | /* Enable wakeup behavior */ | ||
214 | if (mcbsp->pdata->has_wakeup) | ||
215 | MCBSP_WRITE(mcbsp, WAKEUPEN, XRDYEN | RRDYEN); | ||
216 | |||
217 | /* Enable TX/RX sync error interrupts by default */ | ||
218 | if (mcbsp->irq) | ||
219 | MCBSP_WRITE(mcbsp, IRQEN, RSYNCERREN | XSYNCERREN | | ||
220 | RUNDFLEN | ROVFLEN | XUNDFLEN | XOVFLEN); | ||
221 | } | ||
222 | |||
223 | /** | ||
224 | * omap_mcbsp_dma_reg_params - returns the address of mcbsp data register | ||
225 | * @mcbsp: omap_mcbsp struct for the McBSP instance | ||
226 | * @stream: Stream direction (playback/capture) | ||
227 | * | ||
228 | * Returns the address of mcbsp data transmit register or data receive register | ||
229 | * to be used by DMA for transferring/receiving data | ||
230 | */ | ||
231 | static int omap_mcbsp_dma_reg_params(struct omap_mcbsp *mcbsp, | ||
232 | unsigned int stream) | ||
233 | { | ||
234 | int data_reg; | ||
235 | |||
236 | if (stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
237 | if (mcbsp->pdata->reg_size == 2) | ||
238 | data_reg = OMAP_MCBSP_REG_DXR1; | ||
239 | else | ||
240 | data_reg = OMAP_MCBSP_REG_DXR; | ||
241 | } else { | ||
242 | if (mcbsp->pdata->reg_size == 2) | ||
243 | data_reg = OMAP_MCBSP_REG_DRR1; | ||
244 | else | ||
245 | data_reg = OMAP_MCBSP_REG_DRR; | ||
246 | } | ||
247 | |||
248 | return mcbsp->phys_dma_base + data_reg * mcbsp->pdata->reg_step; | ||
249 | } | ||
250 | |||
251 | /* | ||
252 | * omap_mcbsp_set_rx_threshold configures the transmit threshold in words. | ||
253 | * The threshold parameter is 1 based, and it is converted (threshold - 1) | ||
254 | * for the THRSH2 register. | ||
255 | */ | ||
256 | static void omap_mcbsp_set_tx_threshold(struct omap_mcbsp *mcbsp, u16 threshold) | ||
257 | { | ||
258 | if (threshold && threshold <= mcbsp->max_tx_thres) | ||
259 | MCBSP_WRITE(mcbsp, THRSH2, threshold - 1); | ||
260 | } | ||
261 | |||
262 | /* | ||
263 | * omap_mcbsp_set_rx_threshold configures the receive threshold in words. | ||
264 | * The threshold parameter is 1 based, and it is converted (threshold - 1) | ||
265 | * for the THRSH1 register. | ||
266 | */ | ||
267 | static void omap_mcbsp_set_rx_threshold(struct omap_mcbsp *mcbsp, u16 threshold) | ||
268 | { | ||
269 | if (threshold && threshold <= mcbsp->max_rx_thres) | ||
270 | MCBSP_WRITE(mcbsp, THRSH1, threshold - 1); | ||
271 | } | ||
272 | |||
273 | /* | ||
274 | * omap_mcbsp_get_tx_delay returns the number of used slots in the McBSP FIFO | ||
275 | */ | ||
276 | static u16 omap_mcbsp_get_tx_delay(struct omap_mcbsp *mcbsp) | ||
277 | { | ||
278 | u16 buffstat; | ||
279 | |||
280 | /* Returns the number of free locations in the buffer */ | ||
281 | buffstat = MCBSP_READ(mcbsp, XBUFFSTAT); | ||
282 | |||
283 | /* Number of slots are different in McBSP ports */ | ||
284 | return mcbsp->pdata->buffer_size - buffstat; | ||
285 | } | ||
286 | |||
287 | /* | ||
288 | * omap_mcbsp_get_rx_delay returns the number of free slots in the McBSP FIFO | ||
289 | * to reach the threshold value (when the DMA will be triggered to read it) | ||
290 | */ | ||
291 | static u16 omap_mcbsp_get_rx_delay(struct omap_mcbsp *mcbsp) | ||
292 | { | ||
293 | u16 buffstat, threshold; | ||
294 | |||
295 | /* Returns the number of used locations in the buffer */ | ||
296 | buffstat = MCBSP_READ(mcbsp, RBUFFSTAT); | ||
297 | /* RX threshold */ | ||
298 | threshold = MCBSP_READ(mcbsp, THRSH1); | ||
299 | |||
300 | /* Return the number of location till we reach the threshold limit */ | ||
301 | if (threshold <= buffstat) | ||
302 | return 0; | ||
303 | else | ||
304 | return threshold - buffstat; | ||
305 | } | ||
306 | |||
307 | static int omap_mcbsp_request(struct omap_mcbsp *mcbsp) | ||
308 | { | ||
309 | void *reg_cache; | ||
310 | int err; | ||
311 | |||
312 | reg_cache = kzalloc(mcbsp->reg_cache_size, GFP_KERNEL); | ||
313 | if (!reg_cache) | ||
314 | return -ENOMEM; | ||
315 | |||
316 | spin_lock(&mcbsp->lock); | ||
317 | if (!mcbsp->free) { | ||
318 | dev_err(mcbsp->dev, "McBSP%d is currently in use\n", mcbsp->id); | ||
319 | err = -EBUSY; | ||
320 | goto err_kfree; | ||
321 | } | ||
322 | |||
323 | mcbsp->free = false; | ||
324 | mcbsp->reg_cache = reg_cache; | ||
325 | spin_unlock(&mcbsp->lock); | ||
326 | |||
327 | if (mcbsp->pdata && mcbsp->pdata->ops && mcbsp->pdata->ops->request) | ||
328 | mcbsp->pdata->ops->request(mcbsp->id - 1); | ||
329 | |||
330 | /* | ||
331 | * Make sure that transmitter, receiver and sample-rate generator are | ||
332 | * not running before activating IRQs. | ||
333 | */ | ||
334 | MCBSP_WRITE(mcbsp, SPCR1, 0); | ||
335 | MCBSP_WRITE(mcbsp, SPCR2, 0); | ||
336 | |||
337 | if (mcbsp->irq) { | ||
338 | err = request_irq(mcbsp->irq, omap_mcbsp_irq_handler, 0, | ||
339 | "McBSP", (void *)mcbsp); | ||
340 | if (err != 0) { | ||
341 | dev_err(mcbsp->dev, "Unable to request IRQ\n"); | ||
342 | goto err_clk_disable; | ||
343 | } | ||
344 | } else { | ||
345 | err = request_irq(mcbsp->tx_irq, omap_mcbsp_tx_irq_handler, 0, | ||
346 | "McBSP TX", (void *)mcbsp); | ||
347 | if (err != 0) { | ||
348 | dev_err(mcbsp->dev, "Unable to request TX IRQ\n"); | ||
349 | goto err_clk_disable; | ||
350 | } | ||
351 | |||
352 | err = request_irq(mcbsp->rx_irq, omap_mcbsp_rx_irq_handler, 0, | ||
353 | "McBSP RX", (void *)mcbsp); | ||
354 | if (err != 0) { | ||
355 | dev_err(mcbsp->dev, "Unable to request RX IRQ\n"); | ||
356 | goto err_free_irq; | ||
357 | } | ||
358 | } | ||
359 | |||
360 | return 0; | ||
361 | err_free_irq: | ||
362 | free_irq(mcbsp->tx_irq, (void *)mcbsp); | ||
363 | err_clk_disable: | ||
364 | if (mcbsp->pdata && mcbsp->pdata->ops && mcbsp->pdata->ops->free) | ||
365 | mcbsp->pdata->ops->free(mcbsp->id - 1); | ||
366 | |||
367 | /* Disable wakeup behavior */ | ||
368 | if (mcbsp->pdata->has_wakeup) | ||
369 | MCBSP_WRITE(mcbsp, WAKEUPEN, 0); | ||
370 | |||
371 | spin_lock(&mcbsp->lock); | ||
372 | mcbsp->free = true; | ||
373 | mcbsp->reg_cache = NULL; | ||
374 | err_kfree: | ||
375 | spin_unlock(&mcbsp->lock); | ||
376 | kfree(reg_cache); | ||
377 | |||
378 | return err; | ||
379 | } | ||
380 | |||
381 | static void omap_mcbsp_free(struct omap_mcbsp *mcbsp) | ||
382 | { | ||
383 | void *reg_cache; | ||
384 | |||
385 | if (mcbsp->pdata && mcbsp->pdata->ops && mcbsp->pdata->ops->free) | ||
386 | mcbsp->pdata->ops->free(mcbsp->id - 1); | ||
387 | |||
388 | /* Disable wakeup behavior */ | ||
389 | if (mcbsp->pdata->has_wakeup) | ||
390 | MCBSP_WRITE(mcbsp, WAKEUPEN, 0); | ||
391 | |||
392 | /* Disable interrupt requests */ | ||
393 | if (mcbsp->irq) | ||
394 | MCBSP_WRITE(mcbsp, IRQEN, 0); | ||
395 | |||
396 | if (mcbsp->irq) { | ||
397 | free_irq(mcbsp->irq, (void *)mcbsp); | ||
398 | } else { | ||
399 | free_irq(mcbsp->rx_irq, (void *)mcbsp); | ||
400 | free_irq(mcbsp->tx_irq, (void *)mcbsp); | ||
401 | } | ||
402 | |||
403 | reg_cache = mcbsp->reg_cache; | ||
404 | |||
405 | /* | ||
406 | * Select CLKS source from internal source unconditionally before | ||
407 | * marking the McBSP port as free. | ||
408 | * If the external clock source via MCBSP_CLKS pin has been selected the | ||
409 | * system will refuse to enter idle if the CLKS pin source is not reset | ||
410 | * back to internal source. | ||
411 | */ | ||
412 | if (!mcbsp_omap1()) | ||
413 | omap2_mcbsp_set_clks_src(mcbsp, MCBSP_CLKS_PRCM_SRC); | ||
414 | |||
415 | spin_lock(&mcbsp->lock); | ||
416 | if (mcbsp->free) | ||
417 | dev_err(mcbsp->dev, "McBSP%d was not reserved\n", mcbsp->id); | ||
418 | else | ||
419 | mcbsp->free = true; | ||
420 | mcbsp->reg_cache = NULL; | ||
421 | spin_unlock(&mcbsp->lock); | ||
422 | |||
423 | kfree(reg_cache); | ||
424 | } | ||
425 | |||
426 | /* | ||
427 | * Here we start the McBSP, by enabling transmitter, receiver or both. | ||
428 | * If no transmitter or receiver is active prior calling, then sample-rate | ||
429 | * generator and frame sync are started. | ||
430 | */ | ||
431 | static void omap_mcbsp_start(struct omap_mcbsp *mcbsp, int stream) | ||
432 | { | ||
433 | int tx = (stream == SNDRV_PCM_STREAM_PLAYBACK); | ||
434 | int rx = !tx; | ||
435 | int enable_srg = 0; | ||
436 | u16 w; | ||
437 | |||
438 | if (mcbsp->st_data) | ||
439 | omap_mcbsp_st_start(mcbsp); | ||
440 | |||
441 | /* Only enable SRG, if McBSP is master */ | ||
442 | w = MCBSP_READ_CACHE(mcbsp, PCR0); | ||
443 | if (w & (FSXM | FSRM | CLKXM | CLKRM)) | ||
444 | enable_srg = !((MCBSP_READ_CACHE(mcbsp, SPCR2) | | ||
445 | MCBSP_READ_CACHE(mcbsp, SPCR1)) & 1); | ||
446 | |||
447 | if (enable_srg) { | ||
448 | /* Start the sample generator */ | ||
449 | w = MCBSP_READ_CACHE(mcbsp, SPCR2); | ||
450 | MCBSP_WRITE(mcbsp, SPCR2, w | (1 << 6)); | ||
451 | } | ||
452 | |||
453 | /* Enable transmitter and receiver */ | ||
454 | tx &= 1; | ||
455 | w = MCBSP_READ_CACHE(mcbsp, SPCR2); | ||
456 | MCBSP_WRITE(mcbsp, SPCR2, w | tx); | ||
457 | |||
458 | rx &= 1; | ||
459 | w = MCBSP_READ_CACHE(mcbsp, SPCR1); | ||
460 | MCBSP_WRITE(mcbsp, SPCR1, w | rx); | ||
461 | |||
462 | /* | ||
463 | * Worst case: CLKSRG*2 = 8000khz: (1/8000) * 2 * 2 usec | ||
464 | * REVISIT: 100us may give enough time for two CLKSRG, however | ||
465 | * due to some unknown PM related, clock gating etc. reason it | ||
466 | * is now at 500us. | ||
467 | */ | ||
468 | udelay(500); | ||
469 | |||
470 | if (enable_srg) { | ||
471 | /* Start frame sync */ | ||
472 | w = MCBSP_READ_CACHE(mcbsp, SPCR2); | ||
473 | MCBSP_WRITE(mcbsp, SPCR2, w | (1 << 7)); | ||
474 | } | ||
475 | |||
476 | if (mcbsp->pdata->has_ccr) { | ||
477 | /* Release the transmitter and receiver */ | ||
478 | w = MCBSP_READ_CACHE(mcbsp, XCCR); | ||
479 | w &= ~(tx ? XDISABLE : 0); | ||
480 | MCBSP_WRITE(mcbsp, XCCR, w); | ||
481 | w = MCBSP_READ_CACHE(mcbsp, RCCR); | ||
482 | w &= ~(rx ? RDISABLE : 0); | ||
483 | MCBSP_WRITE(mcbsp, RCCR, w); | ||
484 | } | ||
485 | |||
486 | /* Dump McBSP Regs */ | ||
487 | omap_mcbsp_dump_reg(mcbsp); | ||
488 | } | ||
489 | |||
490 | static void omap_mcbsp_stop(struct omap_mcbsp *mcbsp, int stream) | ||
491 | { | ||
492 | int tx = (stream == SNDRV_PCM_STREAM_PLAYBACK); | ||
493 | int rx = !tx; | ||
494 | int idle; | ||
495 | u16 w; | ||
496 | |||
497 | /* Reset transmitter */ | ||
498 | tx &= 1; | ||
499 | if (mcbsp->pdata->has_ccr) { | ||
500 | w = MCBSP_READ_CACHE(mcbsp, XCCR); | ||
501 | w |= (tx ? XDISABLE : 0); | ||
502 | MCBSP_WRITE(mcbsp, XCCR, w); | ||
503 | } | ||
504 | w = MCBSP_READ_CACHE(mcbsp, SPCR2); | ||
505 | MCBSP_WRITE(mcbsp, SPCR2, w & ~tx); | ||
506 | |||
507 | /* Reset receiver */ | ||
508 | rx &= 1; | ||
509 | if (mcbsp->pdata->has_ccr) { | ||
510 | w = MCBSP_READ_CACHE(mcbsp, RCCR); | ||
511 | w |= (rx ? RDISABLE : 0); | ||
512 | MCBSP_WRITE(mcbsp, RCCR, w); | ||
513 | } | ||
514 | w = MCBSP_READ_CACHE(mcbsp, SPCR1); | ||
515 | MCBSP_WRITE(mcbsp, SPCR1, w & ~rx); | ||
516 | |||
517 | idle = !((MCBSP_READ_CACHE(mcbsp, SPCR2) | | ||
518 | MCBSP_READ_CACHE(mcbsp, SPCR1)) & 1); | ||
519 | |||
520 | if (idle) { | ||
521 | /* Reset the sample rate generator */ | ||
522 | w = MCBSP_READ_CACHE(mcbsp, SPCR2); | ||
523 | MCBSP_WRITE(mcbsp, SPCR2, w & ~(1 << 6)); | ||
524 | } | ||
525 | |||
526 | if (mcbsp->st_data) | ||
527 | omap_mcbsp_st_stop(mcbsp); | ||
528 | } | ||
529 | |||
530 | #define max_thres(m) (mcbsp->pdata->buffer_size) | ||
531 | #define valid_threshold(m, val) ((val) <= max_thres(m)) | ||
532 | #define THRESHOLD_PROP_BUILDER(prop) \ | ||
533 | static ssize_t prop##_show(struct device *dev, \ | ||
534 | struct device_attribute *attr, char *buf) \ | ||
535 | { \ | ||
536 | struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); \ | ||
537 | \ | ||
538 | return sprintf(buf, "%u\n", mcbsp->prop); \ | ||
539 | } \ | ||
540 | \ | ||
541 | static ssize_t prop##_store(struct device *dev, \ | ||
542 | struct device_attribute *attr, \ | ||
543 | const char *buf, size_t size) \ | ||
544 | { \ | ||
545 | struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); \ | ||
546 | unsigned long val; \ | ||
547 | int status; \ | ||
548 | \ | ||
549 | status = kstrtoul(buf, 0, &val); \ | ||
550 | if (status) \ | ||
551 | return status; \ | ||
552 | \ | ||
553 | if (!valid_threshold(mcbsp, val)) \ | ||
554 | return -EDOM; \ | ||
555 | \ | ||
556 | mcbsp->prop = val; \ | ||
557 | return size; \ | ||
558 | } \ | ||
559 | \ | ||
560 | static DEVICE_ATTR(prop, 0644, prop##_show, prop##_store) | ||
561 | |||
562 | THRESHOLD_PROP_BUILDER(max_tx_thres); | ||
563 | THRESHOLD_PROP_BUILDER(max_rx_thres); | ||
564 | |||
565 | static const char * const dma_op_modes[] = { | ||
566 | "element", "threshold", | ||
567 | }; | ||
568 | |||
569 | static ssize_t dma_op_mode_show(struct device *dev, | ||
570 | struct device_attribute *attr, char *buf) | ||
571 | { | ||
572 | struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); | ||
573 | int dma_op_mode, i = 0; | ||
574 | ssize_t len = 0; | ||
575 | const char * const *s; | ||
576 | |||
577 | dma_op_mode = mcbsp->dma_op_mode; | ||
578 | |||
579 | for (s = &dma_op_modes[i]; i < ARRAY_SIZE(dma_op_modes); s++, i++) { | ||
580 | if (dma_op_mode == i) | ||
581 | len += sprintf(buf + len, "[%s] ", *s); | ||
582 | else | ||
583 | len += sprintf(buf + len, "%s ", *s); | ||
584 | } | ||
585 | len += sprintf(buf + len, "\n"); | ||
586 | |||
587 | return len; | ||
588 | } | ||
589 | |||
590 | static ssize_t dma_op_mode_store(struct device *dev, | ||
591 | struct device_attribute *attr, const char *buf, | ||
592 | size_t size) | ||
593 | { | ||
594 | struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); | ||
595 | int i; | ||
596 | |||
597 | i = sysfs_match_string(dma_op_modes, buf); | ||
598 | if (i < 0) | ||
599 | return i; | ||
600 | |||
601 | spin_lock_irq(&mcbsp->lock); | ||
602 | if (!mcbsp->free) { | ||
603 | size = -EBUSY; | ||
604 | goto unlock; | ||
605 | } | ||
606 | mcbsp->dma_op_mode = i; | ||
607 | |||
608 | unlock: | ||
609 | spin_unlock_irq(&mcbsp->lock); | ||
610 | |||
611 | return size; | ||
612 | } | ||
613 | |||
614 | static DEVICE_ATTR_RW(dma_op_mode); | ||
615 | |||
616 | static const struct attribute *additional_attrs[] = { | ||
617 | &dev_attr_max_tx_thres.attr, | ||
618 | &dev_attr_max_rx_thres.attr, | ||
619 | &dev_attr_dma_op_mode.attr, | ||
620 | NULL, | ||
621 | }; | ||
622 | |||
623 | static const struct attribute_group additional_attr_group = { | ||
624 | .attrs = (struct attribute **)additional_attrs, | ||
625 | }; | ||
626 | |||
627 | /* | ||
628 | * McBSP1 and McBSP3 are directly mapped on 1610 and 1510. | ||
629 | * 730 has only 2 McBSP, and both of them are MPU peripherals. | ||
630 | */ | ||
631 | static int omap_mcbsp_init(struct platform_device *pdev) | ||
632 | { | ||
633 | struct omap_mcbsp *mcbsp = platform_get_drvdata(pdev); | ||
634 | struct resource *res; | ||
635 | int ret = 0; | ||
636 | |||
637 | spin_lock_init(&mcbsp->lock); | ||
638 | mcbsp->free = true; | ||
639 | |||
640 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mpu"); | ||
641 | if (!res) | ||
642 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
643 | |||
644 | mcbsp->io_base = devm_ioremap_resource(&pdev->dev, res); | ||
645 | if (IS_ERR(mcbsp->io_base)) | ||
646 | return PTR_ERR(mcbsp->io_base); | ||
647 | |||
648 | mcbsp->phys_base = res->start; | ||
649 | mcbsp->reg_cache_size = resource_size(res); | ||
650 | |||
651 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dma"); | ||
652 | if (!res) | ||
653 | mcbsp->phys_dma_base = mcbsp->phys_base; | ||
654 | else | ||
655 | mcbsp->phys_dma_base = res->start; | ||
656 | |||
657 | /* | ||
658 | * OMAP1, 2 uses two interrupt lines: TX, RX | ||
659 | * OMAP2430, OMAP3 SoC have combined IRQ line as well. | ||
660 | * OMAP4 and newer SoC only have the combined IRQ line. | ||
661 | * Use the combined IRQ if available since it gives better debugging | ||
662 | * possibilities. | ||
663 | */ | ||
664 | mcbsp->irq = platform_get_irq_byname(pdev, "common"); | ||
665 | if (mcbsp->irq == -ENXIO) { | ||
666 | mcbsp->tx_irq = platform_get_irq_byname(pdev, "tx"); | ||
667 | |||
668 | if (mcbsp->tx_irq == -ENXIO) { | ||
669 | mcbsp->irq = platform_get_irq(pdev, 0); | ||
670 | mcbsp->tx_irq = 0; | ||
671 | } else { | ||
672 | mcbsp->rx_irq = platform_get_irq_byname(pdev, "rx"); | ||
673 | mcbsp->irq = 0; | ||
674 | } | ||
675 | } | ||
676 | |||
677 | if (!pdev->dev.of_node) { | ||
678 | res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "tx"); | ||
679 | if (!res) { | ||
680 | dev_err(&pdev->dev, "invalid tx DMA channel\n"); | ||
681 | return -ENODEV; | ||
682 | } | ||
683 | mcbsp->dma_req[0] = res->start; | ||
684 | mcbsp->dma_data[0].filter_data = &mcbsp->dma_req[0]; | ||
685 | |||
686 | res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "rx"); | ||
687 | if (!res) { | ||
688 | dev_err(&pdev->dev, "invalid rx DMA channel\n"); | ||
689 | return -ENODEV; | ||
690 | } | ||
691 | mcbsp->dma_req[1] = res->start; | ||
692 | mcbsp->dma_data[1].filter_data = &mcbsp->dma_req[1]; | ||
693 | } else { | ||
694 | mcbsp->dma_data[0].filter_data = "tx"; | ||
695 | mcbsp->dma_data[1].filter_data = "rx"; | ||
696 | } | ||
697 | |||
698 | mcbsp->dma_data[0].addr = omap_mcbsp_dma_reg_params(mcbsp, | ||
699 | SNDRV_PCM_STREAM_PLAYBACK); | ||
700 | mcbsp->dma_data[1].addr = omap_mcbsp_dma_reg_params(mcbsp, | ||
701 | SNDRV_PCM_STREAM_CAPTURE); | ||
702 | |||
703 | mcbsp->fclk = clk_get(&pdev->dev, "fck"); | ||
704 | if (IS_ERR(mcbsp->fclk)) { | ||
705 | ret = PTR_ERR(mcbsp->fclk); | ||
706 | dev_err(mcbsp->dev, "unable to get fck: %d\n", ret); | ||
707 | return ret; | ||
708 | } | ||
709 | |||
710 | mcbsp->dma_op_mode = MCBSP_DMA_MODE_ELEMENT; | ||
711 | if (mcbsp->pdata->buffer_size) { | ||
712 | /* | ||
713 | * Initially configure the maximum thresholds to a safe value. | ||
714 | * The McBSP FIFO usage with these values should not go under | ||
715 | * 16 locations. | ||
716 | * If the whole FIFO without safety buffer is used, than there | ||
717 | * is a possibility that the DMA will be not able to push the | ||
718 | * new data on time, causing channel shifts in runtime. | ||
719 | */ | ||
720 | mcbsp->max_tx_thres = max_thres(mcbsp) - 0x10; | ||
721 | mcbsp->max_rx_thres = max_thres(mcbsp) - 0x10; | ||
722 | |||
723 | ret = sysfs_create_group(&mcbsp->dev->kobj, | ||
724 | &additional_attr_group); | ||
725 | if (ret) { | ||
726 | dev_err(mcbsp->dev, | ||
727 | "Unable to create additional controls\n"); | ||
728 | goto err_thres; | ||
729 | } | ||
730 | } else { | ||
731 | mcbsp->max_tx_thres = -EINVAL; | ||
732 | mcbsp->max_rx_thres = -EINVAL; | ||
733 | } | ||
734 | |||
735 | ret = omap_mcbsp_st_init(pdev); | ||
736 | if (ret) | ||
737 | goto err_st; | ||
738 | |||
739 | return 0; | ||
740 | |||
741 | err_st: | ||
742 | if (mcbsp->pdata->buffer_size) | ||
743 | sysfs_remove_group(&mcbsp->dev->kobj, &additional_attr_group); | ||
744 | err_thres: | ||
745 | clk_put(mcbsp->fclk); | ||
746 | return ret; | ||
747 | } | ||
748 | |||
62 | /* | 749 | /* |
63 | * Stream DMA parameters. DMA request line and port address are set runtime | 750 | * Stream DMA parameters. DMA request line and port address are set runtime |
64 | * since they are different between OMAP1 and later OMAPs | 751 | * since they are different between OMAP1 and later OMAPs |
@@ -656,132 +1343,6 @@ static const struct snd_soc_component_driver omap_mcbsp_component = { | |||
656 | .name = "omap-mcbsp", | 1343 | .name = "omap-mcbsp", |
657 | }; | 1344 | }; |
658 | 1345 | ||
659 | static int omap_mcbsp_st_info_volsw(struct snd_kcontrol *kcontrol, | ||
660 | struct snd_ctl_elem_info *uinfo) | ||
661 | { | ||
662 | struct soc_mixer_control *mc = | ||
663 | (struct soc_mixer_control *)kcontrol->private_value; | ||
664 | int max = mc->max; | ||
665 | int min = mc->min; | ||
666 | |||
667 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
668 | uinfo->count = 1; | ||
669 | uinfo->value.integer.min = min; | ||
670 | uinfo->value.integer.max = max; | ||
671 | return 0; | ||
672 | } | ||
673 | |||
674 | #define OMAP_MCBSP_ST_CHANNEL_VOLUME(channel) \ | ||
675 | static int \ | ||
676 | omap_mcbsp_set_st_ch##channel##_volume(struct snd_kcontrol *kc, \ | ||
677 | struct snd_ctl_elem_value *uc) \ | ||
678 | { \ | ||
679 | struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kc); \ | ||
680 | struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); \ | ||
681 | struct soc_mixer_control *mc = \ | ||
682 | (struct soc_mixer_control *)kc->private_value; \ | ||
683 | int max = mc->max; \ | ||
684 | int min = mc->min; \ | ||
685 | int val = uc->value.integer.value[0]; \ | ||
686 | \ | ||
687 | if (val < min || val > max) \ | ||
688 | return -EINVAL; \ | ||
689 | \ | ||
690 | /* OMAP McBSP implementation uses index values 0..4 */ \ | ||
691 | return omap_st_set_chgain(mcbsp, channel, val); \ | ||
692 | } \ | ||
693 | \ | ||
694 | static int \ | ||
695 | omap_mcbsp_get_st_ch##channel##_volume(struct snd_kcontrol *kc, \ | ||
696 | struct snd_ctl_elem_value *uc) \ | ||
697 | { \ | ||
698 | struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kc); \ | ||
699 | struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); \ | ||
700 | s16 chgain; \ | ||
701 | \ | ||
702 | if (omap_st_get_chgain(mcbsp, channel, &chgain)) \ | ||
703 | return -EAGAIN; \ | ||
704 | \ | ||
705 | uc->value.integer.value[0] = chgain; \ | ||
706 | return 0; \ | ||
707 | } | ||
708 | |||
709 | OMAP_MCBSP_ST_CHANNEL_VOLUME(0) | ||
710 | OMAP_MCBSP_ST_CHANNEL_VOLUME(1) | ||
711 | |||
712 | static int omap_mcbsp_st_put_mode(struct snd_kcontrol *kcontrol, | ||
713 | struct snd_ctl_elem_value *ucontrol) | ||
714 | { | ||
715 | struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); | ||
716 | struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); | ||
717 | u8 value = ucontrol->value.integer.value[0]; | ||
718 | |||
719 | if (value == omap_st_is_enabled(mcbsp)) | ||
720 | return 0; | ||
721 | |||
722 | if (value) | ||
723 | omap_st_enable(mcbsp); | ||
724 | else | ||
725 | omap_st_disable(mcbsp); | ||
726 | |||
727 | return 1; | ||
728 | } | ||
729 | |||
730 | static int omap_mcbsp_st_get_mode(struct snd_kcontrol *kcontrol, | ||
731 | struct snd_ctl_elem_value *ucontrol) | ||
732 | { | ||
733 | struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); | ||
734 | struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); | ||
735 | |||
736 | ucontrol->value.integer.value[0] = omap_st_is_enabled(mcbsp); | ||
737 | return 0; | ||
738 | } | ||
739 | |||
740 | #define OMAP_MCBSP_ST_CONTROLS(port) \ | ||
741 | static const struct snd_kcontrol_new omap_mcbsp##port##_st_controls[] = { \ | ||
742 | SOC_SINGLE_EXT("McBSP" #port " Sidetone Switch", 1, 0, 1, 0, \ | ||
743 | omap_mcbsp_st_get_mode, omap_mcbsp_st_put_mode), \ | ||
744 | OMAP_MCBSP_SOC_SINGLE_S16_EXT("McBSP" #port " Sidetone Channel 0 Volume", \ | ||
745 | -32768, 32767, \ | ||
746 | omap_mcbsp_get_st_ch0_volume, \ | ||
747 | omap_mcbsp_set_st_ch0_volume), \ | ||
748 | OMAP_MCBSP_SOC_SINGLE_S16_EXT("McBSP" #port " Sidetone Channel 1 Volume", \ | ||
749 | -32768, 32767, \ | ||
750 | omap_mcbsp_get_st_ch1_volume, \ | ||
751 | omap_mcbsp_set_st_ch1_volume), \ | ||
752 | } | ||
753 | |||
754 | OMAP_MCBSP_ST_CONTROLS(2); | ||
755 | OMAP_MCBSP_ST_CONTROLS(3); | ||
756 | |||
757 | int omap_mcbsp_st_add_controls(struct snd_soc_pcm_runtime *rtd, int port_id) | ||
758 | { | ||
759 | struct snd_soc_dai *cpu_dai = rtd->cpu_dai; | ||
760 | struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); | ||
761 | |||
762 | if (!mcbsp->st_data) { | ||
763 | dev_warn(mcbsp->dev, "No sidetone data for port\n"); | ||
764 | return 0; | ||
765 | } | ||
766 | |||
767 | switch (port_id) { | ||
768 | case 2: /* McBSP 2 */ | ||
769 | return snd_soc_add_dai_controls(cpu_dai, | ||
770 | omap_mcbsp2_st_controls, | ||
771 | ARRAY_SIZE(omap_mcbsp2_st_controls)); | ||
772 | case 3: /* McBSP 3 */ | ||
773 | return snd_soc_add_dai_controls(cpu_dai, | ||
774 | omap_mcbsp3_st_controls, | ||
775 | ARRAY_SIZE(omap_mcbsp3_st_controls)); | ||
776 | default: | ||
777 | dev_err(mcbsp->dev, "Port %d not supported\n", port_id); | ||
778 | break; | ||
779 | } | ||
780 | |||
781 | return -EINVAL; | ||
782 | } | ||
783 | EXPORT_SYMBOL_GPL(omap_mcbsp_st_add_controls); | ||
784 | |||
785 | static struct omap_mcbsp_platform_data omap2420_pdata = { | 1346 | static struct omap_mcbsp_platform_data omap2420_pdata = { |
786 | .reg_step = 4, | 1347 | .reg_step = 4, |
787 | .reg_size = 2, | 1348 | .reg_size = 2, |
@@ -893,7 +1454,10 @@ static int asoc_mcbsp_remove(struct platform_device *pdev) | |||
893 | if (pm_qos_request_active(&mcbsp->pm_qos_req)) | 1454 | if (pm_qos_request_active(&mcbsp->pm_qos_req)) |
894 | pm_qos_remove_request(&mcbsp->pm_qos_req); | 1455 | pm_qos_remove_request(&mcbsp->pm_qos_req); |
895 | 1456 | ||
896 | omap_mcbsp_cleanup(mcbsp); | 1457 | if (mcbsp->pdata->buffer_size) |
1458 | sysfs_remove_group(&mcbsp->dev->kobj, &additional_attr_group); | ||
1459 | |||
1460 | omap_mcbsp_st_cleanup(pdev); | ||
897 | 1461 | ||
898 | clk_put(mcbsp->fclk); | 1462 | clk_put(mcbsp->fclk); |
899 | 1463 | ||
diff --git a/sound/soc/omap/omap-mcbsp.h b/sound/soc/omap/omap-mcbsp.h index 2e3369c27be3..7911d24898c9 100644 --- a/sound/soc/omap/omap-mcbsp.h +++ b/sound/soc/omap/omap-mcbsp.h | |||
@@ -22,8 +22,10 @@ | |||
22 | * | 22 | * |
23 | */ | 23 | */ |
24 | 24 | ||
25 | #ifndef __OMAP_I2S_H__ | 25 | #ifndef __OMAP_MCBSP_H__ |
26 | #define __OMAP_I2S_H__ | 26 | #define __OMAP_MCBSP_H__ |
27 | |||
28 | #include <sound/dmaengine_pcm.h> | ||
27 | 29 | ||
28 | /* Source clocks for McBSP sample rate generator */ | 30 | /* Source clocks for McBSP sample rate generator */ |
29 | enum omap_mcbsp_clksrg_clk { | 31 | enum omap_mcbsp_clksrg_clk { |
@@ -41,4 +43,4 @@ enum omap_mcbsp_div { | |||
41 | 43 | ||
42 | int omap_mcbsp_st_add_controls(struct snd_soc_pcm_runtime *rtd, int port_id); | 44 | int omap_mcbsp_st_add_controls(struct snd_soc_pcm_runtime *rtd, int port_id); |
43 | 45 | ||
44 | #endif | 46 | #endif /* __OMAP_MCBSP_H__ */ |