[alsa-devel] [PATCH 4/4] ALSA: x86: Refactor PCM process engine

Pierre-Louis Bossart pierre-louis.bossart at linux.intel.com
Fri Feb 3 20:47:55 CET 2017


On 2/3/17 10:44 AM, Takashi Iwai wrote:
> This is again a big rewrite of the driver; now it touches the code to
> process PCM stream transfers.
>
> The most fundamental change is that now the driver supports more than
> four periods.  Instead of keeping the same index between the ring
> buffers (from A to D) and the PCM buffer periods, now we keep
> difference indices for both.  Also, for the cases with less periods
> than four, we track the head index, too.  That is, we now have four
> indices: ringbuf_head, pcm_head, ringbuf_filled, and pcm_filled.

Well that's not completely right. The DMA can only generate an interrupt 
once the buffer you submit was played, and with 4 descriptors you can't 
have more than 4 points where interrupts are generated.  If you program 
different values in different descriptors then the notion of periodic 
hardware interrupts will be lost.

>
> By this flexibility, we can use even dmix, which requires 16 periods
> as default.
>
> The buffer size could be up to 20bit, so it's set to that value.  But
> the pre-allocation is limited to 128k as default.  It's because the
> chip requires the continuous pages (unfortunately no-SG possible),
> thus we don't want too much continuous pages.

No, that's not true. You need contiguous regions for each descriptor, 
but the entire buffer doesn't need to be contiguous.

>
> Signed-off-by: Takashi Iwai <tiwai at suse.de>
> ---
>  sound/x86/intel_hdmi_audio.c     | 510 ++++++++++++++-------------------------
>  sound/x86/intel_hdmi_audio.h     |  23 +-
>  sound/x86/intel_hdmi_lpe_audio.h |  26 +-
>  3 files changed, 204 insertions(+), 355 deletions(-)
>
> diff --git a/sound/x86/intel_hdmi_audio.c b/sound/x86/intel_hdmi_audio.c
> index c503f0de3975..2c3dcdcb43f5 100644
> --- a/sound/x86/intel_hdmi_audio.c
> +++ b/sound/x86/intel_hdmi_audio.c
> @@ -619,82 +619,6 @@ static void snd_intelhad_prog_dip(struct snd_pcm_substream *substream,
>  	had_write_register(intelhaddata, AUD_CNTL_ST, ctrl_state.regval);
>  }
>
> -/*
> - * Programs buffer address and length registers
> - * This function programs ring buffer address and length into registers.
> - */
> -static int snd_intelhad_prog_buffer(struct snd_pcm_substream *substream,
> -				    struct snd_intelhad *intelhaddata,
> -				    int start, int end)
> -{
> -	u32 ring_buf_addr, ring_buf_size, period_bytes;
> -	u8 i, num_periods;
> -
> -	ring_buf_addr = substream->runtime->dma_addr;
> -	ring_buf_size = snd_pcm_lib_buffer_bytes(substream);
> -	intelhaddata->stream_info.ring_buf_size = ring_buf_size;
> -	period_bytes = frames_to_bytes(substream->runtime,
> -				substream->runtime->period_size);
> -	num_periods = substream->runtime->periods;
> -
> -	/*
> -	 * buffer addr should  be 64 byte aligned, period bytes
> -	 * will be used to calculate addr offset
> -	 */
> -	period_bytes &= ~0x3F;
> -
> -	/* Hardware supports MAX_PERIODS buffers */
> -	if (end >= HAD_MAX_PERIODS)
> -		return -EINVAL;
> -
> -	for (i = start; i <= end; i++) {
> -		/* Program the buf registers with addr and len */
> -		intelhaddata->buf_info[i].buf_addr = ring_buf_addr +
> -							 (i * period_bytes);
> -		if (i < num_periods-1)
> -			intelhaddata->buf_info[i].buf_size = period_bytes;
> -		else
> -			intelhaddata->buf_info[i].buf_size = ring_buf_size -
> -							(i * period_bytes);
> -
> -		had_write_register(intelhaddata,
> -				   AUD_BUF_A_ADDR + (i * HAD_REG_WIDTH),
> -					intelhaddata->buf_info[i].buf_addr |
> -					BIT(0) | BIT(1));
> -		had_write_register(intelhaddata,
> -				   AUD_BUF_A_LENGTH + (i * HAD_REG_WIDTH),
> -					period_bytes);
> -		intelhaddata->buf_info[i].is_valid = true;
> -	}
> -	dev_dbg(intelhaddata->dev, "%s:buf[%d-%d] addr=%#x  and size=%d\n",
> -		__func__, start, end,
> -		intelhaddata->buf_info[start].buf_addr,
> -		intelhaddata->buf_info[start].buf_size);
> -	intelhaddata->valid_buf_cnt = num_periods;
> -	return 0;
> -}
> -
> -static int snd_intelhad_read_len(struct snd_intelhad *intelhaddata)
> -{
> -	int i, retval = 0;
> -	u32 len[4];
> -
> -	for (i = 0; i < 4 ; i++) {
> -		had_read_register(intelhaddata,
> -				  AUD_BUF_A_LENGTH + (i * HAD_REG_WIDTH),
> -				  &len[i]);
> -		if (!len[i])
> -			retval++;
> -	}
> -	if (retval != 1) {
> -		for (i = 0; i < 4 ; i++)
> -			dev_dbg(intelhaddata->dev, "buf[%d] size=%d\n",
> -				i, len[i]);
> -	}
> -
> -	return retval;
> -}
> -
>  static int had_calculate_maud_value(u32 aud_samp_freq, u32 link_rate)
>  {
>  	u32 maud_val;
> @@ -882,34 +806,192 @@ static int snd_intelhad_prog_n(u32 aud_samp_freq, u32 *n_param,
>  	return 0;
>  }
>
> +/*
> + * PCM ring buffer handling
> + */
> +
> +#define AUD_BUF_ADDR(x)		(AUD_BUF_A_ADDR + (x) * HAD_REG_WIDTH)
> +#define AUD_BUF_LEN(x)		(AUD_BUF_A_LENGTH + (x) * HAD_REG_WIDTH)
> +
> +/* Set up a ring buffer at the "filled" position */
> +static void had_prog_ringbuf(struct snd_pcm_substream *substream,
> +			     struct snd_intelhad *intelhaddata)
> +{
> +	int idx = intelhaddata->ringbuf_filled;
> +	int ofs = intelhaddata->pcmbuf_filled * intelhaddata->period_bytes;
> +	u32 addr = substream->runtime->dma_addr + ofs;
> +
> +	addr |= AUD_BUF_VALID | AUD_BUF_INTR_EN;
> +	had_write_register(intelhaddata, AUD_BUF_ADDR(idx), addr);
> +	had_write_register(intelhaddata, AUD_BUF_LEN(idx),
> +			   intelhaddata->period_bytes);
> +
> +	/* advance the indices to the next */
> +	intelhaddata->ringbuf_filled++;
> +	intelhaddata->ringbuf_filled %= HAD_NUM_RINGBUFS;
> +	intelhaddata->pcmbuf_filled++;
> +	intelhaddata->pcmbuf_filled %= substream->runtime->periods;
> +}
> +
> +/* invalidate a ring buffer with the given index */
> +static void had_invalidate_ringbuf(struct snd_intelhad *intelhaddata,
> +				   int idx)
> +{
> +	had_write_register(intelhaddata, AUD_BUF_ADDR(idx), 0);
> +	had_write_register(intelhaddata, AUD_BUF_LEN(idx), 0);
> +}
> +
> +/* Initial programming of ring buffers */
> +static void had_init_ringbufs(struct snd_pcm_substream *substream,
> +			      struct snd_intelhad *intelhaddata)
> +{
> +	struct snd_pcm_runtime *runtime = substream->runtime;
> +	int i, period_bytes, num_periods;
> +
> +	num_periods = runtime->periods;
> +	intelhaddata->period_bytes = period_bytes =
> +		frames_to_bytes(runtime, runtime->period_size);
> +	WARN_ON(period_bytes & 0x3f);
> +
> +	intelhaddata->ringbuf_head = 0;
> +	intelhaddata->pcmbuf_head = 0;
> +	intelhaddata->ringbuf_filled = 0;
> +	intelhaddata->pcmbuf_filled = 0;
> +
> +	for (i = 0; i < HAD_NUM_RINGBUFS; i++) {
> +		if (i < num_periods)
> +			had_prog_ringbuf(substream, intelhaddata);
> +		else /* invalidate the rest */
> +			had_invalidate_ringbuf(intelhaddata, i);
> +	}
> +}
> +
> +/* process a ring buf, advance to the next */
> +static void had_advance_ringbuf(struct snd_pcm_substream *substream,
> +				struct snd_intelhad *intelhaddata)
> +{
> +	int num_periods = substream->runtime->periods;
> +
> +	/* reprogram the next buffer */
> +	had_prog_ringbuf(substream, intelhaddata);
> +
> +	/* invalidate the processed buf for shorter PCM buffer */
> +	if (num_periods < HAD_NUM_RINGBUFS)
> +		had_invalidate_ringbuf(intelhaddata,
> +				       intelhaddata->ringbuf_head);
> +
> +	/* proceed to next */
> +	intelhaddata->ringbuf_head++;
> +	intelhaddata->ringbuf_head %= HAD_NUM_RINGBUFS;
> +	intelhaddata->pcmbuf_head++;
> +	intelhaddata->pcmbuf_head %= num_periods;
> +}
> +
> +/* process the current ring buffer(s);
> + * returns the current PCM buffer byte position, or -EPIPE for underrun.
> + */
> +static int had_process_ringbufs(struct snd_pcm_substream *substream,
> +				struct snd_intelhad *intelhaddata)
> +{
> +	int len, processed;
> +	unsigned long flags;
> +
> +	processed = 0;
> +	spin_lock_irqsave(&intelhaddata->had_spinlock, flags);
> +	for (;;) {
> +		/* get the remaining bytes on the buffer */
> +		had_read_register(intelhaddata,
> +				  AUD_BUF_LEN(intelhaddata->ringbuf_head),
> +				  &len);
> +		if (len < 0 || len > intelhaddata->period_bytes) {
> +			dev_dbg(intelhaddata->dev, "Invalid buf length %d\n",
> +				len);
> +			len = -EPIPE;
> +			goto out;
> +		}
> +
> +		if (len > 0) /* OK, this is the current buffer */
> +			break;
> +
> +		/* len=0 => already empty, check the next buffer */
> +		if (++processed >= HAD_NUM_RINGBUFS) {
> +			len = -EPIPE; /* all empty? - report underrun */
> +			goto out;
> +		}
> +		had_advance_ringbuf(substream, intelhaddata);
> +	}
> +
> +	len = intelhaddata->period_bytes - len;
> +	len += intelhaddata->period_bytes * intelhaddata->pcmbuf_head;
> + out:
> +	spin_unlock_irqrestore(&intelhaddata->had_spinlock, flags);
> +	return len;
> +}
> +
> +/* called from irq handler */
> +static void had_process_buffer_done(struct snd_intelhad *intelhaddata)
> +{
> +	struct snd_pcm_substream *substream;
> +
> +	if (!intelhaddata->connected)
> +		return; /* disconnected? - bail out */
> +
> +	substream = had_substream_get(intelhaddata);
> +	if (!substream)
> +		return; /* no stream? - bail out */
> +
> +	/* process or stop the stream */
> +	if (had_process_ringbufs(substream, intelhaddata) < 0)
> +		snd_pcm_stop_xrun(substream);
> +	else
> +		snd_pcm_period_elapsed(substream);
> +
> +	had_substream_put(intelhaddata);
> +}
> +
>  #define MAX_CNT			0xFF
>
> -static void snd_intelhad_handle_underrun(struct snd_intelhad *intelhaddata)
> +/*
> + * The interrupt status 'sticky' bits might not be cleared by
> + * setting '1' to that bit once...
> + */
> +static void wait_clear_underrun_bit(struct snd_intelhad *intelhaddata)
>  {
> -	u32 hdmi_status = 0, i = 0;
> +	int i;
> +	u32 val;
> +
> +	for (i = 0; i < MAX_CNT; i++) {
> +		/* clear bit30, 31 AUD_HDMI_STATUS */
> +		had_read_register(intelhaddata, AUD_HDMI_STATUS, &val);
> +		if (!(val & AUD_CONFIG_MASK_UNDERRUN))
> +			return;
> +		had_write_register(intelhaddata, AUD_HDMI_STATUS, val);
> +	}
> +	dev_err(intelhaddata->dev, "Unable to clear UNDERRUN bits\n");
> +}
> +
> +/* called from irq handler */
> +static void had_process_buffer_underrun(struct snd_intelhad *intelhaddata)
> +{
> +	struct snd_pcm_substream *substream;
>
>  	/* Handle Underrun interrupt within Audio Unit */
>  	had_write_register(intelhaddata, AUD_CONFIG, 0);
>  	/* Reset buffer pointers */
>  	had_write_register(intelhaddata, AUD_HDMI_STATUS, 1);
>  	had_write_register(intelhaddata, AUD_HDMI_STATUS, 0);
> -	/*
> -	 * The interrupt status 'sticky' bits might not be cleared by
> -	 * setting '1' to that bit once...
> -	 */
> -	do { /* clear bit30, 31 AUD_HDMI_STATUS */
> -		had_read_register(intelhaddata, AUD_HDMI_STATUS,
> -				  &hdmi_status);
> -		dev_dbg(intelhaddata->dev, "HDMI status =0x%x\n", hdmi_status);
> -		if (hdmi_status & AUD_CONFIG_MASK_UNDERRUN) {
> -			i++;
> -			had_write_register(intelhaddata,
> -					   AUD_HDMI_STATUS, hdmi_status);
> -		} else
> -			break;
> -	} while (i < MAX_CNT);
> -	if (i >= MAX_CNT)
> -		dev_err(intelhaddata->dev, "Unable to clear UNDERRUN bits\n");
> +
> +	wait_clear_underrun_bit(intelhaddata);
> +
> +	if (!intelhaddata->connected)
> +		return; /* disconnected? - bail out */
> +
> +	/* Report UNDERRUN error to above layers */
> +	substream = had_substream_get(intelhaddata);
> +	if (substream) {
> +		snd_pcm_stop_xrun(substream);
> +		had_substream_put(intelhaddata);
> +	}
>  }
>
>  /*
> @@ -955,11 +1037,6 @@ static int snd_intelhad_open(struct snd_pcm_substream *substream)
>  	intelhaddata->stream_info.substream_refcount++;
>  	spin_unlock_irq(&intelhaddata->had_spinlock);
>
> -	/* these are cleared in prepare callback, but just to be sure */
> -	intelhaddata->curr_buf = 0;
> -	intelhaddata->underrun_count = 0;
> -	intelhaddata->stream_info.buffer_rendered = 0;
> -
>  	return retval;
>   error:
>  	pm_runtime_put(intelhaddata->dev);
> @@ -1123,10 +1200,6 @@ static int snd_intelhad_pcm_prepare(struct snd_pcm_substream *substream)
>  	dev_dbg(intelhaddata->dev, "rate=%d\n", runtime->rate);
>  	dev_dbg(intelhaddata->dev, "channels=%d\n", runtime->channels);
>
> -	intelhaddata->curr_buf = 0;
> -	intelhaddata->underrun_count = 0;
> -	intelhaddata->stream_info.buffer_rendered = 0;
> -
>  	/* Get N value in KHz */
>  	disp_samp_freq = intelhaddata->tmds_clock_speed;
>
> @@ -1150,8 +1223,7 @@ static int snd_intelhad_pcm_prepare(struct snd_pcm_substream *substream)
>  	retval = snd_intelhad_audio_ctrl(substream, intelhaddata);
>
>  	/* Prog buffer address */
> -	retval = snd_intelhad_prog_buffer(substream, intelhaddata,
> -			HAD_BUF_TYPE_A, HAD_BUF_TYPE_D);
> +	had_init_ringbufs(substream, intelhaddata);
>
>  	/*
>  	 * Program channel mapping in following order:
> @@ -1171,48 +1243,17 @@ static snd_pcm_uframes_t
>  snd_intelhad_pcm_pointer(struct snd_pcm_substream *substream)
>  {
>  	struct snd_intelhad *intelhaddata;
> -	u32 bytes_rendered = 0;
> -	u32 t;
> -	int buf_id;
> +	int len;
>
>  	intelhaddata = snd_pcm_substream_chip(substream);
>
>  	if (!intelhaddata->connected)
>  		return SNDRV_PCM_POS_XRUN;
>
> -	/* Use a hw register to calculate sub-period position reports.
> -	 * This makes PulseAudio happier.
> -	 */
> -
> -	buf_id = intelhaddata->curr_buf % 4;
> -	had_read_register(intelhaddata,
> -			  AUD_BUF_A_LENGTH + (buf_id * HAD_REG_WIDTH), &t);
> -
> -	if ((t == 0) || (t == ((u32)-1L))) {
> -		intelhaddata->underrun_count++;
> -		dev_dbg(intelhaddata->dev,
> -			"discovered buffer done for buf %d, count = %d\n",
> -			 buf_id, intelhaddata->underrun_count);
> -
> -		if (intelhaddata->underrun_count > (HAD_MIN_PERIODS/2)) {
> -			dev_dbg(intelhaddata->dev,
> -				"assume audio_codec_reset, underrun = %d - do xrun\n",
> -				 intelhaddata->underrun_count);
> -			return SNDRV_PCM_POS_XRUN;
> -		}
> -	} else {
> -		/* Reset Counter */
> -		intelhaddata->underrun_count = 0;
> -	}
> -
> -	t = intelhaddata->buf_info[buf_id].buf_size - t;
> -
> -	if (intelhaddata->stream_info.buffer_rendered)
> -		div_u64_rem(intelhaddata->stream_info.buffer_rendered,
> -			intelhaddata->stream_info.ring_buf_size,
> -			&(bytes_rendered));
> -
> -	return bytes_to_frames(substream->runtime, bytes_rendered + t);
> +	len = had_process_ringbufs(substream, intelhaddata);
> +	if (len < 0)
> +		return SNDRV_PCM_POS_XRUN;
> +	return bytes_to_frames(substream->runtime, len);
>  }
>
>  /*
> @@ -1283,179 +1324,9 @@ static int hdmi_audio_mode_change(struct snd_intelhad *intelhaddata)
>  	return retval;
>  }
>
> -static inline int had_chk_intrmiss(struct snd_intelhad *intelhaddata,
> -		enum intel_had_aud_buf_type buf_id)
> -{
> -	int i, intr_count = 0;
> -	enum intel_had_aud_buf_type buff_done;
> -	u32 buf_size, buf_addr;
> -
> -	buff_done = buf_id;
> -
> -	intr_count = snd_intelhad_read_len(intelhaddata);
> -	if (intr_count > 1) {
> -		/* In case of active playback */
> -		dev_err(intelhaddata->dev,
> -			"Driver detected %d missed buffer done interrupt(s)\n",
> -			(intr_count - 1));
> -		if (intr_count > 3)
> -			return intr_count;
> -
> -		buf_id += (intr_count - 1);
> -		/* Reprogram registers*/
> -		for (i = buff_done; i < buf_id; i++) {
> -			int j = i % 4;
> -
> -			buf_size = intelhaddata->buf_info[j].buf_size;
> -			buf_addr = intelhaddata->buf_info[j].buf_addr;
> -			had_write_register(intelhaddata,
> -					   AUD_BUF_A_LENGTH +
> -					   (j * HAD_REG_WIDTH), buf_size);
> -			had_write_register(intelhaddata,
> -					   AUD_BUF_A_ADDR+(j * HAD_REG_WIDTH),
> -					   (buf_addr | BIT(0) | BIT(1)));
> -		}
> -		buf_id = buf_id % 4;
> -		intelhaddata->buff_done = buf_id;
> -	}
> -
> -	return intr_count;
> -}
> -
> -/* called from irq handler */
> -static int had_process_buffer_done(struct snd_intelhad *intelhaddata)
> -{
> -	u32 len = 1;
> -	enum intel_had_aud_buf_type buf_id;
> -	enum intel_had_aud_buf_type buff_done;
> -	struct pcm_stream_info *stream;
> -	struct snd_pcm_substream *substream;
> -	u32 buf_size;
> -	int intr_count;
> -	unsigned long flags;
> -
> -	stream = &intelhaddata->stream_info;
> -	intr_count = 1;
> -
> -	spin_lock_irqsave(&intelhaddata->had_spinlock, flags);
> -	if (!intelhaddata->connected) {
> -		spin_unlock_irqrestore(&intelhaddata->had_spinlock, flags);
> -		dev_dbg(intelhaddata->dev,
> -			"%s:Device already disconnected\n", __func__);
> -		return 0;
> -	}
> -	buf_id = intelhaddata->curr_buf;
> -	intelhaddata->buff_done = buf_id;
> -	buff_done = intelhaddata->buff_done;
> -	buf_size = intelhaddata->buf_info[buf_id].buf_size;
> -
> -	/* Every debug statement has an implication
> -	 * of ~5msec. Thus, avoid having >3 debug statements
> -	 * for each buffer_done handling.
> -	 */
> -
> -	/* Check for any intr_miss in case of active playback */
> -	if (stream->running) {
> -		intr_count = had_chk_intrmiss(intelhaddata, buf_id);
> -		if (!intr_count || (intr_count > 3)) {
> -			spin_unlock_irqrestore(&intelhaddata->had_spinlock,
> -					       flags);
> -			dev_err(intelhaddata->dev,
> -				"HAD SW state in non-recoverable mode\n");
> -			return 0;
> -		}
> -		buf_id += (intr_count - 1);
> -		buf_id = buf_id % 4;
> -	}
> -
> -	intelhaddata->buf_info[buf_id].is_valid = true;
> -	if (intelhaddata->valid_buf_cnt-1 == buf_id) {
> -		if (stream->running)
> -			intelhaddata->curr_buf = HAD_BUF_TYPE_A;
> -	} else
> -		intelhaddata->curr_buf = buf_id + 1;
> -
> -	spin_unlock_irqrestore(&intelhaddata->had_spinlock, flags);
> -
> -	if (!intelhaddata->connected) {
> -		dev_dbg(intelhaddata->dev, "HDMI cable plugged-out\n");
> -		return 0;
> -	}
> -
> -	/* Reprogram the registers with addr and length */
> -	had_write_register(intelhaddata,
> -			   AUD_BUF_A_LENGTH + (buf_id * HAD_REG_WIDTH),
> -			   buf_size);
> -	had_write_register(intelhaddata,
> -			   AUD_BUF_A_ADDR + (buf_id * HAD_REG_WIDTH),
> -			   intelhaddata->buf_info[buf_id].buf_addr |
> -			   BIT(0) | BIT(1));
> -
> -	had_read_register(intelhaddata,
> -			  AUD_BUF_A_LENGTH + (buf_id * HAD_REG_WIDTH),
> -			  &len);
> -	dev_dbg(intelhaddata->dev, "%s:Enabled buf[%d]\n", __func__, buf_id);
> -
> -	/* In case of actual data,
> -	 * report buffer_done to above ALSA layer
> -	 */
> -	substream = had_substream_get(intelhaddata);
> -	if (substream) {
> -		buf_size = intelhaddata->buf_info[buf_id].buf_size;
> -		intelhaddata->stream_info.buffer_rendered +=
> -			(intr_count * buf_size);
> -		snd_pcm_period_elapsed(substream);
> -		had_substream_put(intelhaddata);
> -	}
> -
> -	return 0;
> -}
> -
> -/* called from irq handler */
> -static int had_process_buffer_underrun(struct snd_intelhad *intelhaddata)
> -{
> -	enum intel_had_aud_buf_type buf_id;
> -	struct pcm_stream_info *stream;
> -	struct snd_pcm_substream *substream;
> -	unsigned long flags;
> -	int connected;
> -
> -	stream = &intelhaddata->stream_info;
> -
> -	spin_lock_irqsave(&intelhaddata->had_spinlock, flags);
> -	buf_id = intelhaddata->curr_buf;
> -	intelhaddata->buff_done = buf_id;
> -	connected = intelhaddata->connected;
> -	if (stream->running)
> -		intelhaddata->curr_buf = HAD_BUF_TYPE_A;
> -
> -	spin_unlock_irqrestore(&intelhaddata->had_spinlock, flags);
> -
> -	dev_dbg(intelhaddata->dev, "Enter:%s buf_id=%d, stream_running=%d\n",
> -			__func__, buf_id, stream->running);
> -
> -	snd_intelhad_handle_underrun(intelhaddata);
> -
> -	if (!connected) {
> -		dev_dbg(intelhaddata->dev,
> -			"%s:Device already disconnected\n", __func__);
> -		return 0;
> -	}
> -
> -	/* Report UNDERRUN error to above layers */
> -	substream = had_substream_get(intelhaddata);
> -	if (substream) {
> -		snd_pcm_stop_xrun(substream);
> -		had_substream_put(intelhaddata);
> -	}
> -
> -	return 0;
> -}
> -
>  /* process hot plug, called from wq with mutex locked */
>  static void had_process_hot_plug(struct snd_intelhad *intelhaddata)
>  {
> -	enum intel_had_aud_buf_type buf_id;
>  	struct snd_pcm_substream *substream;
>
>  	spin_lock_irq(&intelhaddata->had_spinlock);
> @@ -1465,17 +1336,12 @@ static void had_process_hot_plug(struct snd_intelhad *intelhaddata)
>  		return;
>  	}
>
> -	buf_id = intelhaddata->curr_buf;
> -	intelhaddata->buff_done = buf_id;
>  	intelhaddata->connected = true;
>  	dev_dbg(intelhaddata->dev,
>  		"%s @ %d:DEBUG PLUG/UNPLUG : HAD_DRV_CONNECTED\n",
>  			__func__, __LINE__);
>  	spin_unlock_irq(&intelhaddata->had_spinlock);
>
> -	dev_dbg(intelhaddata->dev, "Processing HOT_PLUG, buf_id = %d\n",
> -		buf_id);
> -
>  	/* Safety check */
>  	substream = had_substream_get(intelhaddata);
>  	if (substream) {
> @@ -1492,11 +1358,8 @@ static void had_process_hot_plug(struct snd_intelhad *intelhaddata)
>  /* process hot unplug, called from wq with mutex locked */
>  static void had_process_hot_unplug(struct snd_intelhad *intelhaddata)
>  {
> -	enum intel_had_aud_buf_type buf_id;
>  	struct snd_pcm_substream *substream;
>
> -	buf_id = intelhaddata->curr_buf;
> -
>  	substream = had_substream_get(intelhaddata);
>
>  	spin_lock_irq(&intelhaddata->had_spinlock);
> @@ -1868,13 +1731,12 @@ static int hdmi_lpe_audio_probe(struct platform_device *pdev)
>  	dma_set_mask(&pdev->dev, DMA_BIT_MASK(32));
>  	dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
>
> -	/* allocate dma pages for ALSA stream operations
> -	 * memory allocated is based on size, not max value
> -	 * thus using same argument for max & size
> +	/* allocate dma pages;
> +	 * try to allocate 128k buffer as default which is large enough
>  	 */
>  	snd_pcm_lib_preallocate_pages_for_all(pcm,
>  			SNDRV_DMA_TYPE_DEV, NULL,
> -			HAD_MAX_BUFFER, HAD_MAX_BUFFER);
> +			128 * 1024, HAD_MAX_BUFFER);
>
>  	/* create controls */
>  	for (i = 0; i < ARRAY_SIZE(had_controls); i++) {
> diff --git a/sound/x86/intel_hdmi_audio.h b/sound/x86/intel_hdmi_audio.h
> index 9f713a8a88bc..6178b09801e7 100644
> --- a/sound/x86/intel_hdmi_audio.h
> +++ b/sound/x86/intel_hdmi_audio.h
> @@ -64,24 +64,15 @@
>
>  struct pcm_stream_info {
>  	struct snd_pcm_substream *substream;
> -	u64		buffer_rendered;
> -	u32		ring_buf_size;
>  	int substream_refcount;
>  	bool running;
>  };
>
> -struct ring_buf_info {
> -	u32	buf_addr;
> -	u32	buf_size;
> -	u8	is_valid;
> -};
> -
>  /*
>   * struct snd_intelhad - intelhad driver structure
>   *
>   * @card: ptr to hold card details
>   * @connected: the monitor connection status
> - * @buf_info: ring buffer info
>   * @stream_info: stream information
>   * @eld: holds ELD info
>   * @curr_buf: pointer to hold current active ring buf
> @@ -91,26 +82,28 @@ struct ring_buf_info {
>   * @buff_done: id of current buffer done intr
>   * @dev: platoform device handle
>   * @chmap: holds channel map info
> - * @underrun_count: PCM stream underrun counter
>   */
>  struct snd_intelhad {
>  	struct snd_card	*card;
>  	bool		connected;
> -	struct		ring_buf_info buf_info[HAD_NUM_OF_RING_BUFS];
>  	struct		pcm_stream_info stream_info;
>  	unsigned char	eld[HDMI_MAX_ELD_BYTES];
>  	bool dp_output;
> -	enum		intel_had_aud_buf_type curr_buf;
> -	int		valid_buf_cnt;
>  	unsigned int	aes_bits;
>  	spinlock_t had_spinlock;
> -	enum		intel_had_aud_buf_type buff_done;
>  	struct device *dev;
>  	struct snd_pcm_chmap *chmap;
> -	int underrun_count;
>  	int tmds_clock_speed;
>  	int link_rate;
>
> +	/* positions (indices) for ring buffer [A-D] */
> +	unsigned int ringbuf_head;	/* being processed */
> +	unsigned int ringbuf_filled;	/* to be filled */
> +	/* positions (indices) for PCM buffer */
> +	unsigned int pcmbuf_head;	/* being processed */
> +	unsigned int pcmbuf_filled;	/* to be filled */
> +	unsigned int period_bytes;	/* PCM period size in bytes */
> +
>  	/* internal stuff */
>  	int irq;
>  	void __iomem *mmio_start;
> diff --git a/sound/x86/intel_hdmi_lpe_audio.h b/sound/x86/intel_hdmi_lpe_audio.h
> index be9783910a3a..612714eecaff 100644
> --- a/sound/x86/intel_hdmi_lpe_audio.h
> +++ b/sound/x86/intel_hdmi_lpe_audio.h
> @@ -26,15 +26,14 @@
>  #define HAD_MAX_DEVICES		1
>  #define HAD_MIN_CHANNEL		2
>  #define HAD_MAX_CHANNEL		8
> -#define HAD_NUM_OF_RING_BUFS	4
> +#define HAD_NUM_RINGBUFS	4
>
> -/* Assume 192KHz, 8channel, 25msec period */
> -#define HAD_MAX_BUFFER		(600*1024)
> -#define HAD_MIN_BUFFER		(32*1024)
> -#define HAD_MAX_PERIODS		4
> -#define HAD_MIN_PERIODS		4
> -#define HAD_MAX_PERIOD_BYTES	(HAD_MAX_BUFFER/HAD_MIN_PERIODS)
> -#define HAD_MIN_PERIOD_BYTES	256
> +/* max 20bit address, aligned to 64 */
> +#define HAD_MAX_BUFFER		((1024 * 1024 - 1) & ~0x3f)
> +#define HAD_MAX_PERIODS		256	/* arbitrary, but should suffice */
> +#define HAD_MIN_PERIODS		2
> +#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 */
>  #define MAX_SPEAKERS		8
>
> @@ -82,14 +81,6 @@
>  /* Naud Value */
>  #define DP_NAUD_VAL					32768
>
> -/* enum intel_had_aud_buf_type - HDMI controller ring buffer types */
> -enum intel_had_aud_buf_type {
> -	HAD_BUF_TYPE_A = 0,
> -	HAD_BUF_TYPE_B = 1,
> -	HAD_BUF_TYPE_C = 2,
> -	HAD_BUF_TYPE_D = 3,
> -};
> -
>  /* HDMI Controller register offsets - audio domain common */
>  /* Base address for below regs = 0x65000 */
>  enum hdmi_ctrl_reg_offset_common {
> @@ -274,6 +265,9 @@ union aud_buf_addr {
>  	u32 regval;
>  };
>
> +#define AUD_BUF_VALID		(1U << 0)
> +#define AUD_BUF_INTR_EN		(1U << 1)
> +
>  /* Length of Audio Buffer */
>  union aud_buf_len {
>  	struct {
>



More information about the Alsa-devel mailing list