diff options
author | Takashi Iwai <tiwai@suse.de> | 2017-02-07 08:05:46 +0100 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2017-02-10 10:21:42 +0100 |
commit | 8d48c0163d1ab0f56a18ebe9898a34ebcfdfaa1a (patch) | |
tree | f58c097b7d324f5a886f774bf394e7cfd28c3373 /sound/x86 | |
parent | a9ebdd0ef261fb9838af2821c09d165f6a14789b (diff) | |
download | lwn-8d48c0163d1ab0f56a18ebe9898a34ebcfdfaa1a.tar.gz lwn-8d48c0163d1ab0f56a18ebe9898a34ebcfdfaa1a.zip |
ALSA: x86: Allow single period PCM operation
This is an implementation of PCM streaming with only 1 period.
Since the hardware requires the refresh of BDs after each BD
processing finishes, we'd need at least two BDs. The trick is that
both BDs point to the same content: the address of the PCM buffer
head, and the whole buffer size. Then it loops over to the whole
buffer again after it finished once.
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/x86')
-rw-r--r-- | sound/x86/intel_hdmi_audio.c | 14 | ||||
-rw-r--r-- | sound/x86/intel_hdmi_lpe_audio.h | 2 |
2 files changed, 13 insertions, 3 deletions
diff --git a/sound/x86/intel_hdmi_audio.c b/sound/x86/intel_hdmi_audio.c index 73a662d20201..d2136498defe 100644 --- a/sound/x86/intel_hdmi_audio.c +++ b/sound/x86/intel_hdmi_audio.c @@ -822,6 +822,11 @@ static int had_prog_n(u32 aud_samp_freq, u32 *n_param, * * For nperiods < 4, the remaining BDs out of 4 are marked as invalid, so that * the hardware skips those BDs in the loop. + * + * An exceptional setup is the case with nperiods=1. Since we have to update + * BDs after finishing one BD processing, we'd need at least two BDs, where + * both BDs point to the same content, the same address, the same size of the + * whole PCM buffer. */ #define AUD_BUF_ADDR(x) (AUD_BUF_A_ADDR + (x) * HAD_REG_WIDTH) @@ -864,6 +869,8 @@ static void had_init_ringbuf(struct snd_pcm_substream *substream, num_periods = runtime->periods; intelhaddata->num_bds = min(num_periods, HAD_NUM_OF_RING_BUFS); + /* set the minimum 2 BDs for num_periods=1 */ + intelhaddata->num_bds = max(intelhaddata->num_bds, 2U); intelhaddata->period_bytes = frames_to_bytes(runtime, runtime->period_size); WARN_ON(intelhaddata->period_bytes & 0x3f); @@ -873,7 +880,7 @@ static void had_init_ringbuf(struct snd_pcm_substream *substream, intelhaddata->pcmbuf_filled = 0; for (i = 0; i < HAD_NUM_OF_RING_BUFS; i++) { - if (i < num_periods) + if (i < intelhaddata->num_bds) had_prog_bd(substream, intelhaddata); else /* invalidate the rest */ had_invalidate_bd(intelhaddata, i); @@ -1255,7 +1262,10 @@ static snd_pcm_uframes_t had_pcm_pointer(struct snd_pcm_substream *substream) len = had_process_ringbuf(substream, intelhaddata); if (len < 0) return SNDRV_PCM_POS_XRUN; - return bytes_to_frames(substream->runtime, len); + len = bytes_to_frames(substream->runtime, len); + /* wrapping may happen when periods=1 */ + len %= substream->runtime->buffer_size; + return len; } /* diff --git a/sound/x86/intel_hdmi_lpe_audio.h b/sound/x86/intel_hdmi_lpe_audio.h index 97bbca19333a..477e5153307c 100644 --- a/sound/x86/intel_hdmi_lpe_audio.h +++ b/sound/x86/intel_hdmi_lpe_audio.h @@ -31,7 +31,7 @@ #define HAD_MAX_BUFFER ((1024 * 1024 - 1) & ~0x3f) #define HAD_DEFAULT_BUFFER (600 * 1024) /* default prealloc size */ #define HAD_MAX_PERIODS 256 /* arbitrary, but should suffice */ -#define HAD_MIN_PERIODS 2 +#define HAD_MIN_PERIODS 1 #define HAD_MAX_PERIOD_BYTES ((HAD_MAX_BUFFER / HAD_MIN_PERIODS) & ~0x3f) #define HAD_MIN_PERIOD_BYTES 1024 /* might be smaller */ #define HAD_FIFO_SIZE 0 /* fifo not being used */ |