ALSA: x86: Allow single period PCM operation
authorTakashi Iwai <tiwai@suse.de>
Tue, 7 Feb 2017 07:05:46 +0000 (08:05 +0100)
committerTakashi Iwai <tiwai@suse.de>
Fri, 10 Feb 2017 09:21:42 +0000 (10:21 +0100)
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>
sound/x86/intel_hdmi_audio.c
sound/x86/intel_hdmi_lpe_audio.h

index 73a662d20201d21cbb9885c9220b57d9ef07cf85..d2136498defe02601fc2556ab4b99fa840583fe9 100644 (file)
@@ -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;
 }
 
 /*
index 97bbca19333ac4f148cc42df9716ed4613758daa..477e5153307c6ec5654f856961979c6674c936a0 100644 (file)
@@ -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 */