diff options
author | Misael Lopez Cruz <misael.lopez@ti.com> | 2014-11-12 09:38:05 -0500 |
---|---|---|
committer | Mark Brown <broonie@kernel.org> | 2014-11-12 09:55:00 -0500 |
commit | a7a3324a602cd7ebabfb7f5990006ec4f3d6449f (patch) | |
tree | f77c68c09dec608db66c5e16b9010bd56359d4f4 | |
parent | 18a4f55756ac945bd89d1fe63dafe36462ecba86 (diff) |
ASoC: davinci-mcasp: Add overrun/underrun event handling
An underrun (playback) event occurs when the serializer transfer
data from the XRBUF buffer to the XRSR shift register, but the
XRBUF hasn't been filled. Similarly, the overrun (capture) event
occurs when data from the XRSR shift register is transferred to
the XRBUF but it hasn't been read yet.
These events are handled as XRUN events that cause the pcm to stop.
The stream has to be explicitly restarted by the userspace which
ensures that after stopping/starting McASP the data transfer is
aligned with DMA. The other possibility was to internally stop and
start McASP without DMA even knowing about it.
Signed-off-by: Misael Lopez Cruz <misael.lopez@ti.com>
Signed-off-by: Peter Ujfalusi <peter.ujfalusi@ti.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
-rw-r--r-- | Documentation/devicetree/bindings/sound/davinci-mcasp-audio.txt | 2 | ||||
-rw-r--r-- | sound/soc/davinci/davinci-mcasp.c | 124 | ||||
-rw-r--r-- | sound/soc/davinci/davinci-mcasp.h | 11 |
3 files changed, 136 insertions, 1 deletions
diff --git a/Documentation/devicetree/bindings/sound/davinci-mcasp-audio.txt b/Documentation/devicetree/bindings/sound/davinci-mcasp-audio.txt index 60ca07996458..46bc9829c71a 100644 --- a/Documentation/devicetree/bindings/sound/davinci-mcasp-audio.txt +++ b/Documentation/devicetree/bindings/sound/davinci-mcasp-audio.txt | |||
@@ -32,7 +32,7 @@ Optional properties: | |||
32 | - rx-num-evt : FIFO levels. | 32 | - rx-num-evt : FIFO levels. |
33 | - sram-size-playback : size of sram to be allocated during playback | 33 | - sram-size-playback : size of sram to be allocated during playback |
34 | - sram-size-capture : size of sram to be allocated during capture | 34 | - sram-size-capture : size of sram to be allocated during capture |
35 | - interrupts : Interrupt numbers for McASP, currently not used by the driver | 35 | - interrupts : Interrupt numbers for McASP |
36 | - interrupt-names : Known interrupt names are "tx" and "rx" | 36 | - interrupt-names : Known interrupt names are "tx" and "rx" |
37 | - pinctrl-0: Should specify pin control group used for this controller. | 37 | - pinctrl-0: Should specify pin control group used for this controller. |
38 | - pinctrl-names: Should contain only one value - "default", for more details | 38 | - pinctrl-names: Should contain only one value - "default", for more details |
diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index a9822c7bbb0b..e460f97c514e 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c | |||
@@ -70,6 +70,7 @@ struct davinci_mcasp { | |||
70 | void __iomem *base; | 70 | void __iomem *base; |
71 | u32 fifo_base; | 71 | u32 fifo_base; |
72 | struct device *dev; | 72 | struct device *dev; |
73 | struct snd_pcm_substream *substreams[2]; | ||
73 | 74 | ||
74 | /* McASP specific data */ | 75 | /* McASP specific data */ |
75 | int tdm_slots; | 76 | int tdm_slots; |
@@ -80,6 +81,7 @@ struct davinci_mcasp { | |||
80 | u8 bclk_div; | 81 | u8 bclk_div; |
81 | u16 bclk_lrclk_ratio; | 82 | u16 bclk_lrclk_ratio; |
82 | int streams; | 83 | int streams; |
84 | u32 irq_request[2]; | ||
83 | 85 | ||
84 | int sysclk_freq; | 86 | int sysclk_freq; |
85 | bool bclk_master; | 87 | bool bclk_master; |
@@ -185,6 +187,10 @@ static void mcasp_start_rx(struct davinci_mcasp *mcasp) | |||
185 | mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXFSRST); | 187 | mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXFSRST); |
186 | if (mcasp_is_synchronous(mcasp)) | 188 | if (mcasp_is_synchronous(mcasp)) |
187 | mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXFSRST); | 189 | mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXFSRST); |
190 | |||
191 | /* enable receive IRQs */ | ||
192 | mcasp_set_bits(mcasp, DAVINCI_MCASP_EVTCTLR_REG, | ||
193 | mcasp->irq_request[SNDRV_PCM_STREAM_CAPTURE]); | ||
188 | } | 194 | } |
189 | 195 | ||
190 | static void mcasp_start_tx(struct davinci_mcasp *mcasp) | 196 | static void mcasp_start_tx(struct davinci_mcasp *mcasp) |
@@ -214,6 +220,10 @@ static void mcasp_start_tx(struct davinci_mcasp *mcasp) | |||
214 | mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXSMRST); | 220 | mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXSMRST); |
215 | /* Release Frame Sync generator */ | 221 | /* Release Frame Sync generator */ |
216 | mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXFSRST); | 222 | mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXFSRST); |
223 | |||
224 | /* enable transmit IRQs */ | ||
225 | mcasp_set_bits(mcasp, DAVINCI_MCASP_EVTCTLX_REG, | ||
226 | mcasp->irq_request[SNDRV_PCM_STREAM_PLAYBACK]); | ||
217 | } | 227 | } |
218 | 228 | ||
219 | static void davinci_mcasp_start(struct davinci_mcasp *mcasp, int stream) | 229 | static void davinci_mcasp_start(struct davinci_mcasp *mcasp, int stream) |
@@ -228,6 +238,10 @@ static void davinci_mcasp_start(struct davinci_mcasp *mcasp, int stream) | |||
228 | 238 | ||
229 | static void mcasp_stop_rx(struct davinci_mcasp *mcasp) | 239 | static void mcasp_stop_rx(struct davinci_mcasp *mcasp) |
230 | { | 240 | { |
241 | /* disable IRQ sources */ | ||
242 | mcasp_clr_bits(mcasp, DAVINCI_MCASP_EVTCTLR_REG, | ||
243 | mcasp->irq_request[SNDRV_PCM_STREAM_CAPTURE]); | ||
244 | |||
231 | /* | 245 | /* |
232 | * In synchronous mode stop the TX clocks if no other stream is | 246 | * In synchronous mode stop the TX clocks if no other stream is |
233 | * running | 247 | * running |
@@ -249,6 +263,10 @@ static void mcasp_stop_tx(struct davinci_mcasp *mcasp) | |||
249 | { | 263 | { |
250 | u32 val = 0; | 264 | u32 val = 0; |
251 | 265 | ||
266 | /* disable IRQ sources */ | ||
267 | mcasp_clr_bits(mcasp, DAVINCI_MCASP_EVTCTLX_REG, | ||
268 | mcasp->irq_request[SNDRV_PCM_STREAM_PLAYBACK]); | ||
269 | |||
252 | /* | 270 | /* |
253 | * In synchronous mode keep TX clocks running if the capture stream is | 271 | * In synchronous mode keep TX clocks running if the capture stream is |
254 | * still running. | 272 | * still running. |
@@ -276,6 +294,76 @@ static void davinci_mcasp_stop(struct davinci_mcasp *mcasp, int stream) | |||
276 | mcasp_stop_rx(mcasp); | 294 | mcasp_stop_rx(mcasp); |
277 | } | 295 | } |
278 | 296 | ||
297 | static irqreturn_t davinci_mcasp_tx_irq_handler(int irq, void *data) | ||
298 | { | ||
299 | struct davinci_mcasp *mcasp = (struct davinci_mcasp *)data; | ||
300 | struct snd_pcm_substream *substream; | ||
301 | u32 irq_mask = mcasp->irq_request[SNDRV_PCM_STREAM_PLAYBACK]; | ||
302 | u32 handled_mask = 0; | ||
303 | u32 stat; | ||
304 | |||
305 | stat = mcasp_get_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG); | ||
306 | if (stat & XUNDRN & irq_mask) { | ||
307 | dev_warn(mcasp->dev, "Transmit buffer underflow\n"); | ||
308 | handled_mask |= XUNDRN; | ||
309 | |||
310 | substream = mcasp->substreams[SNDRV_PCM_STREAM_PLAYBACK]; | ||
311 | if (substream) { | ||
312 | snd_pcm_stream_lock_irq(substream); | ||
313 | if (snd_pcm_running(substream)) | ||
314 | snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); | ||
315 | snd_pcm_stream_unlock_irq(substream); | ||
316 | } | ||
317 | } | ||
318 | |||
319 | if (!handled_mask) | ||
320 | dev_warn(mcasp->dev, "unhandled tx event. txstat: 0x%08x\n", | ||
321 | stat); | ||
322 | |||
323 | if (stat & XRERR) | ||
324 | handled_mask |= XRERR; | ||
325 | |||
326 | /* Ack the handled event only */ | ||
327 | mcasp_set_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG, handled_mask); | ||
328 | |||
329 | return IRQ_RETVAL(handled_mask); | ||
330 | } | ||
331 | |||
332 | static irqreturn_t davinci_mcasp_rx_irq_handler(int irq, void *data) | ||
333 | { | ||
334 | struct davinci_mcasp *mcasp = (struct davinci_mcasp *)data; | ||
335 | struct snd_pcm_substream *substream; | ||
336 | u32 irq_mask = mcasp->irq_request[SNDRV_PCM_STREAM_CAPTURE]; | ||
337 | u32 handled_mask = 0; | ||
338 | u32 stat; | ||
339 | |||
340 | stat = mcasp_get_reg(mcasp, DAVINCI_MCASP_RXSTAT_REG); | ||
341 | if (stat & ROVRN & irq_mask) { | ||
342 | dev_warn(mcasp->dev, "Receive buffer overflow\n"); | ||
343 | handled_mask |= ROVRN; | ||
344 | |||
345 | substream = mcasp->substreams[SNDRV_PCM_STREAM_CAPTURE]; | ||
346 | if (substream) { | ||
347 | snd_pcm_stream_lock_irq(substream); | ||
348 | if (snd_pcm_running(substream)) | ||
349 | snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); | ||
350 | snd_pcm_stream_unlock_irq(substream); | ||
351 | } | ||
352 | } | ||
353 | |||
354 | if (!handled_mask) | ||
355 | dev_warn(mcasp->dev, "unhandled rx event. rxstat: 0x%08x\n", | ||
356 | stat); | ||
357 | |||
358 | if (stat & XRERR) | ||
359 | handled_mask |= XRERR; | ||
360 | |||
361 | /* Ack the handled event only */ | ||
362 | mcasp_set_reg(mcasp, DAVINCI_MCASP_RXSTAT_REG, handled_mask); | ||
363 | |||
364 | return IRQ_RETVAL(handled_mask); | ||
365 | } | ||
366 | |||
279 | static int davinci_mcasp_set_dai_fmt(struct snd_soc_dai *cpu_dai, | 367 | static int davinci_mcasp_set_dai_fmt(struct snd_soc_dai *cpu_dai, |
280 | unsigned int fmt) | 368 | unsigned int fmt) |
281 | { | 369 | { |
@@ -869,6 +957,8 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream, | |||
869 | u32 max_channels = 0; | 957 | u32 max_channels = 0; |
870 | int i, dir; | 958 | int i, dir; |
871 | 959 | ||
960 | mcasp->substreams[substream->stream] = substream; | ||
961 | |||
872 | if (mcasp->op_mode == DAVINCI_MCASP_DIT_MODE) | 962 | if (mcasp->op_mode == DAVINCI_MCASP_DIT_MODE) |
873 | return 0; | 963 | return 0; |
874 | 964 | ||
@@ -907,6 +997,8 @@ static void davinci_mcasp_shutdown(struct snd_pcm_substream *substream, | |||
907 | { | 997 | { |
908 | struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(cpu_dai); | 998 | struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(cpu_dai); |
909 | 999 | ||
1000 | mcasp->substreams[substream->stream] = NULL; | ||
1001 | |||
910 | if (mcasp->op_mode == DAVINCI_MCASP_DIT_MODE) | 1002 | if (mcasp->op_mode == DAVINCI_MCASP_DIT_MODE) |
911 | return; | 1003 | return; |
912 | 1004 | ||
@@ -1256,6 +1348,8 @@ static int davinci_mcasp_probe(struct platform_device *pdev) | |||
1256 | struct resource *mem, *ioarea, *res, *dat; | 1348 | struct resource *mem, *ioarea, *res, *dat; |
1257 | struct davinci_mcasp_pdata *pdata; | 1349 | struct davinci_mcasp_pdata *pdata; |
1258 | struct davinci_mcasp *mcasp; | 1350 | struct davinci_mcasp *mcasp; |
1351 | char *irq_name; | ||
1352 | int irq; | ||
1259 | int ret; | 1353 | int ret; |
1260 | 1354 | ||
1261 | if (!pdev->dev.platform_data && !pdev->dev.of_node) { | 1355 | if (!pdev->dev.platform_data && !pdev->dev.of_node) { |
@@ -1336,6 +1430,36 @@ static int davinci_mcasp_probe(struct platform_device *pdev) | |||
1336 | 1430 | ||
1337 | mcasp->dev = &pdev->dev; | 1431 | mcasp->dev = &pdev->dev; |
1338 | 1432 | ||
1433 | irq = platform_get_irq_byname(pdev, "rx"); | ||
1434 | if (irq >= 0) { | ||
1435 | irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s_rx\n", | ||
1436 | dev_name(&pdev->dev)); | ||
1437 | ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, | ||
1438 | davinci_mcasp_rx_irq_handler, | ||
1439 | IRQF_ONESHOT, irq_name, mcasp); | ||
1440 | if (ret) { | ||
1441 | dev_err(&pdev->dev, "RX IRQ request failed\n"); | ||
1442 | goto err; | ||
1443 | } | ||
1444 | |||
1445 | mcasp->irq_request[SNDRV_PCM_STREAM_CAPTURE] = ROVRN; | ||
1446 | } | ||
1447 | |||
1448 | irq = platform_get_irq_byname(pdev, "tx"); | ||
1449 | if (irq >= 0) { | ||
1450 | irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s_tx\n", | ||
1451 | dev_name(&pdev->dev)); | ||
1452 | ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, | ||
1453 | davinci_mcasp_tx_irq_handler, | ||
1454 | IRQF_ONESHOT, irq_name, mcasp); | ||
1455 | if (ret) { | ||
1456 | dev_err(&pdev->dev, "TX IRQ request failed\n"); | ||
1457 | goto err; | ||
1458 | } | ||
1459 | |||
1460 | mcasp->irq_request[SNDRV_PCM_STREAM_PLAYBACK] = XUNDRN; | ||
1461 | } | ||
1462 | |||
1339 | dat = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dat"); | 1463 | dat = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dat"); |
1340 | if (dat) | 1464 | if (dat) |
1341 | mcasp->dat_port = true; | 1465 | mcasp->dat_port = true; |
diff --git a/sound/soc/davinci/davinci-mcasp.h b/sound/soc/davinci/davinci-mcasp.h index 9737108f0305..79dc511180bf 100644 --- a/sound/soc/davinci/davinci-mcasp.h +++ b/sound/soc/davinci/davinci-mcasp.h | |||
@@ -256,6 +256,7 @@ | |||
256 | * DAVINCI_MCASP_TXSTAT_REG - Transmitter Status Register Bits | 256 | * DAVINCI_MCASP_TXSTAT_REG - Transmitter Status Register Bits |
257 | * DAVINCI_MCASP_RXSTAT_REG - Receiver Status Register Bits | 257 | * DAVINCI_MCASP_RXSTAT_REG - Receiver Status Register Bits |
258 | */ | 258 | */ |
259 | #define XRERR BIT(8) /* Transmit/Receive error */ | ||
259 | #define XRDATA BIT(5) /* Transmit/Receive data ready */ | 260 | #define XRDATA BIT(5) /* Transmit/Receive data ready */ |
260 | 261 | ||
261 | /* | 262 | /* |
@@ -285,6 +286,16 @@ | |||
285 | #define TXDATADMADIS BIT(0) | 286 | #define TXDATADMADIS BIT(0) |
286 | 287 | ||
287 | /* | 288 | /* |
289 | * DAVINCI_MCASP_EVTCTLR_REG - Receiver Interrupt Control Register Bits | ||
290 | */ | ||
291 | #define ROVRN BIT(0) | ||
292 | |||
293 | /* | ||
294 | * DAVINCI_MCASP_EVTCTLX_REG - Transmitter Interrupt Control Register Bits | ||
295 | */ | ||
296 | #define XUNDRN BIT(0) | ||
297 | |||
298 | /* | ||
288 | * DAVINCI_MCASP_W[R]FIFOCTL - Write/Read FIFO Control Register bits | 299 | * DAVINCI_MCASP_W[R]FIFOCTL - Write/Read FIFO Control Register bits |
289 | */ | 300 | */ |
290 | #define FIFO_ENABLE BIT(16) | 301 | #define FIFO_ENABLE BIT(16) |