aboutsummaryrefslogtreecommitdiffstats
path: root/sound
diff options
context:
space:
mode:
authorLibin Yang <libin.yang@intel.com>2015-02-09 21:02:47 -0500
committerTakashi Iwai <tiwai@suse.de>2015-02-11 03:58:15 -0500
commit1b006996b6c44d9d95462e382921954756cec99b (patch)
tree72852960cce0fcf464671d851823734b7e421b07 /sound
parent3271cb22838882bad86f6f2405b29fa7925a08e8 (diff)
ASoC: Intel: Clean data after SST fw fetch
The BDW audio firmware DSP manages the DMA and the DMA cannot be stopped exactly at the end of the playback stream. This means stale samples may be played at PCM stop unless the driver copies silence to the subsequent periods. Signed-off-by: Libin Yang <libin.yang@intel.com> Reviewed-by: Liam Girdwood <liam.r.girdwood@linux.intel.com> Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound')
-rw-r--r--sound/soc/intel/sst-haswell-ipc.c28
-rw-r--r--sound/soc/intel/sst-haswell-ipc.h9
-rw-r--r--sound/soc/intel/sst-haswell-pcm.c65
3 files changed, 102 insertions, 0 deletions
diff --git a/sound/soc/intel/sst-haswell-ipc.c b/sound/soc/intel/sst-haswell-ipc.c
index 0ab1309ef274..394af5684c05 100644
--- a/sound/soc/intel/sst-haswell-ipc.c
+++ b/sound/soc/intel/sst-haswell-ipc.c
@@ -31,6 +31,7 @@
31#include <linux/dma-mapping.h> 31#include <linux/dma-mapping.h>
32#include <linux/debugfs.h> 32#include <linux/debugfs.h>
33#include <linux/pm_runtime.h> 33#include <linux/pm_runtime.h>
34#include <sound/asound.h>
34 35
35#include "sst-haswell-ipc.h" 36#include "sst-haswell-ipc.h"
36#include "sst-dsp.h" 37#include "sst-dsp.h"
@@ -242,6 +243,9 @@ struct sst_hsw_stream {
242 u32 (*notify_position)(struct sst_hsw_stream *stream, void *data); 243 u32 (*notify_position)(struct sst_hsw_stream *stream, void *data);
243 void *pdata; 244 void *pdata;
244 245
246 /* record the fw read position when playback */
247 snd_pcm_uframes_t old_position;
248 bool play_silence;
245 struct list_head node; 249 struct list_head node;
246}; 250};
247 251
@@ -1347,6 +1351,30 @@ int sst_hsw_stream_commit(struct sst_hsw *hsw, struct sst_hsw_stream *stream)
1347 return 0; 1351 return 0;
1348} 1352}
1349 1353
1354snd_pcm_uframes_t sst_hsw_stream_get_old_position(struct sst_hsw *hsw,
1355 struct sst_hsw_stream *stream)
1356{
1357 return stream->old_position;
1358}
1359
1360void sst_hsw_stream_set_old_position(struct sst_hsw *hsw,
1361 struct sst_hsw_stream *stream, snd_pcm_uframes_t val)
1362{
1363 stream->old_position = val;
1364}
1365
1366bool sst_hsw_stream_get_silence_start(struct sst_hsw *hsw,
1367 struct sst_hsw_stream *stream)
1368{
1369 return stream->play_silence;
1370}
1371
1372void sst_hsw_stream_set_silence_start(struct sst_hsw *hsw,
1373 struct sst_hsw_stream *stream, bool val)
1374{
1375 stream->play_silence = val;
1376}
1377
1350/* Stream Information - these calls could be inline but we want the IPC 1378/* Stream Information - these calls could be inline but we want the IPC
1351 ABI to be opaque to client PCM drivers to cope with any future ABI changes */ 1379 ABI to be opaque to client PCM drivers to cope with any future ABI changes */
1352int sst_hsw_mixer_get_info(struct sst_hsw *hsw) 1380int sst_hsw_mixer_get_info(struct sst_hsw *hsw)
diff --git a/sound/soc/intel/sst-haswell-ipc.h b/sound/soc/intel/sst-haswell-ipc.h
index c1ad901342f2..858096041cb1 100644
--- a/sound/soc/intel/sst-haswell-ipc.h
+++ b/sound/soc/intel/sst-haswell-ipc.h
@@ -20,6 +20,7 @@
20#include <linux/types.h> 20#include <linux/types.h>
21#include <linux/kernel.h> 21#include <linux/kernel.h>
22#include <linux/platform_device.h> 22#include <linux/platform_device.h>
23#include <sound/asound.h>
23 24
24#define SST_HSW_NO_CHANNELS 4 25#define SST_HSW_NO_CHANNELS 4
25#define SST_HSW_MAX_DX_REGIONS 14 26#define SST_HSW_MAX_DX_REGIONS 14
@@ -425,6 +426,14 @@ int sst_hsw_stream_set_pmemory_info(struct sst_hsw *hsw,
425 struct sst_hsw_stream *stream, u32 offset, u32 size); 426 struct sst_hsw_stream *stream, u32 offset, u32 size);
426int sst_hsw_stream_set_smemory_info(struct sst_hsw *hsw, 427int sst_hsw_stream_set_smemory_info(struct sst_hsw *hsw,
427 struct sst_hsw_stream *stream, u32 offset, u32 size); 428 struct sst_hsw_stream *stream, u32 offset, u32 size);
429snd_pcm_uframes_t sst_hsw_stream_get_old_position(struct sst_hsw *hsw,
430 struct sst_hsw_stream *stream);
431void sst_hsw_stream_set_old_position(struct sst_hsw *hsw,
432 struct sst_hsw_stream *stream, snd_pcm_uframes_t val);
433bool sst_hsw_stream_get_silence_start(struct sst_hsw *hsw,
434 struct sst_hsw_stream *stream);
435void sst_hsw_stream_set_silence_start(struct sst_hsw *hsw,
436 struct sst_hsw_stream *stream, bool val);
428int sst_hsw_mixer_get_info(struct sst_hsw *hsw); 437int sst_hsw_mixer_get_info(struct sst_hsw *hsw);
429 438
430/* Stream ALSA trigger operations */ 439/* Stream ALSA trigger operations */
diff --git a/sound/soc/intel/sst-haswell-pcm.c b/sound/soc/intel/sst-haswell-pcm.c
index 78fa01be57f2..d6fa9d5514e1 100644
--- a/sound/soc/intel/sst-haswell-pcm.c
+++ b/sound/soc/intel/sst-haswell-pcm.c
@@ -36,6 +36,11 @@
36#define HSW_PCM_COUNT 6 36#define HSW_PCM_COUNT 6
37#define HSW_VOLUME_MAX 0x7FFFFFFF /* 0dB */ 37#define HSW_VOLUME_MAX 0x7FFFFFFF /* 0dB */
38 38
39#define SST_OLD_POSITION(d, r, o) ((d) + \
40 frames_to_bytes(r, o))
41#define SST_SAMPLES(r, x) (bytes_to_samples(r, \
42 frames_to_bytes(r, (x))))
43
39/* simple volume table */ 44/* simple volume table */
40static const u32 volume_map[] = { 45static const u32 volume_map[] = {
41 HSW_VOLUME_MAX >> 30, 46 HSW_VOLUME_MAX >> 30,
@@ -577,23 +582,34 @@ static int hsw_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
577 struct hsw_priv_data *pdata = 582 struct hsw_priv_data *pdata =
578 snd_soc_platform_get_drvdata(rtd->platform); 583 snd_soc_platform_get_drvdata(rtd->platform);
579 struct hsw_pcm_data *pcm_data; 584 struct hsw_pcm_data *pcm_data;
585 struct sst_hsw_stream *sst_stream;
580 struct sst_hsw *hsw = pdata->hsw; 586 struct sst_hsw *hsw = pdata->hsw;
587 struct snd_pcm_runtime *runtime = substream->runtime;
588 snd_pcm_uframes_t pos;
581 int dai; 589 int dai;
582 590
583 dai = mod_map[rtd->cpu_dai->id].dai_id; 591 dai = mod_map[rtd->cpu_dai->id].dai_id;
584 pcm_data = &pdata->pcm[dai][substream->stream]; 592 pcm_data = &pdata->pcm[dai][substream->stream];
593 sst_stream = pcm_data->stream;
585 594
586 switch (cmd) { 595 switch (cmd) {
587 case SNDRV_PCM_TRIGGER_START: 596 case SNDRV_PCM_TRIGGER_START:
588 case SNDRV_PCM_TRIGGER_RESUME: 597 case SNDRV_PCM_TRIGGER_RESUME:
589 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 598 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
599 sst_hsw_stream_set_silence_start(hsw, sst_stream, false);
590 sst_hsw_stream_resume(hsw, pcm_data->stream, 0); 600 sst_hsw_stream_resume(hsw, pcm_data->stream, 0);
591 break; 601 break;
592 case SNDRV_PCM_TRIGGER_STOP: 602 case SNDRV_PCM_TRIGGER_STOP:
593 case SNDRV_PCM_TRIGGER_SUSPEND: 603 case SNDRV_PCM_TRIGGER_SUSPEND:
594 case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 604 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
605 sst_hsw_stream_set_silence_start(hsw, sst_stream, false);
595 sst_hsw_stream_pause(hsw, pcm_data->stream, 0); 606 sst_hsw_stream_pause(hsw, pcm_data->stream, 0);
596 break; 607 break;
608 case SNDRV_PCM_TRIGGER_DRAIN:
609 pos = runtime->control->appl_ptr % runtime->buffer_size;
610 sst_hsw_stream_set_old_position(hsw, pcm_data->stream, pos);
611 sst_hsw_stream_set_silence_start(hsw, sst_stream, true);
612 break;
597 default: 613 default:
598 break; 614 break;
599 } 615 }
@@ -607,13 +623,62 @@ static u32 hsw_notify_pointer(struct sst_hsw_stream *stream, void *data)
607 struct snd_pcm_substream *substream = pcm_data->substream; 623 struct snd_pcm_substream *substream = pcm_data->substream;
608 struct snd_pcm_runtime *runtime = substream->runtime; 624 struct snd_pcm_runtime *runtime = substream->runtime;
609 struct snd_soc_pcm_runtime *rtd = substream->private_data; 625 struct snd_soc_pcm_runtime *rtd = substream->private_data;
626 struct hsw_priv_data *pdata =
627 snd_soc_platform_get_drvdata(rtd->platform);
628 struct sst_hsw *hsw = pdata->hsw;
610 u32 pos; 629 u32 pos;
630 snd_pcm_uframes_t position = bytes_to_frames(runtime,
631 sst_hsw_get_dsp_position(hsw, pcm_data->stream));
632 unsigned char *dma_area = runtime->dma_area;
633 snd_pcm_uframes_t dma_frames =
634 bytes_to_frames(runtime, runtime->dma_bytes);
635 snd_pcm_uframes_t old_position;
636 ssize_t samples;
611 637
612 pos = frames_to_bytes(runtime, 638 pos = frames_to_bytes(runtime,
613 (runtime->control->appl_ptr % runtime->buffer_size)); 639 (runtime->control->appl_ptr % runtime->buffer_size));
614 640
615 dev_vdbg(rtd->dev, "PCM: App pointer %d bytes\n", pos); 641 dev_vdbg(rtd->dev, "PCM: App pointer %d bytes\n", pos);
616 642
643 /* SST fw don't know where to stop dma
644 * So, SST driver need to clean the data which has been consumed
645 */
646 if (dma_area == NULL || dma_frames <= 0
647 || (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
648 || !sst_hsw_stream_get_silence_start(hsw, stream)) {
649 snd_pcm_period_elapsed(substream);
650 return pos;
651 }
652
653 old_position = sst_hsw_stream_get_old_position(hsw, stream);
654 if (position > old_position) {
655 if (position < dma_frames) {
656 samples = SST_SAMPLES(runtime, position - old_position);
657 snd_pcm_format_set_silence(runtime->format,
658 SST_OLD_POSITION(dma_area,
659 runtime, old_position),
660 samples);
661 } else
662 dev_err(rtd->dev, "PCM: position is wrong\n");
663 } else {
664 if (old_position < dma_frames) {
665 samples = SST_SAMPLES(runtime,
666 dma_frames - old_position);
667 snd_pcm_format_set_silence(runtime->format,
668 SST_OLD_POSITION(dma_area,
669 runtime, old_position),
670 samples);
671 } else
672 dev_err(rtd->dev, "PCM: dma_bytes is wrong\n");
673 if (position < dma_frames) {
674 samples = SST_SAMPLES(runtime, position);
675 snd_pcm_format_set_silence(runtime->format,
676 dma_area, samples);
677 } else
678 dev_err(rtd->dev, "PCM: position is wrong\n");
679 }
680 sst_hsw_stream_set_old_position(hsw, stream, position);
681
617 /* let alsa know we have play a period */ 682 /* let alsa know we have play a period */
618 snd_pcm_period_elapsed(substream); 683 snd_pcm_period_elapsed(substream);
619 return pos; 684 return pos;