Commit 0021b532 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull sound fixes from Takashi Iwai:
 "A collection of small fixes for rc1.

  The only (LOC-wise) dominant change was ASoC Qualcomm fix, but most of
  it was merely a code shuffling.

  Another significant change here is for ALSA PCM core; it received a
  revert and a series of fixes for PCM auto-silencing where it caused a
  regression in the previous PR for rc1.

  Others are all small: ASoC Intel fixes, various quirks for ASoC AMD,
  HD-audio and USB-audio, the continued legacy emu10k1 code cleanup, and
  some documentation updates"

* tag 'sound-fix-6.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: (23 commits)
  ALSA: pcm: use exit controlled loop in snd_pcm_playback_silence()
  ALSA: pcm: simplify top-up mode init in snd_pcm_playback_silence()
  ALSA: pcm: playback silence - move silence variable updates to separate function
  ALSA: pcm: playback silence - remove extra code
  ALSA: pcm: fix playback silence - correct incremental silencing
  ALSA: pcm: fix playback silence - use the actual new_hw_ptr for the threshold mode
  ALSA: pcm: Revert "ALSA: pcm: rewrite snd_pcm_playback_silence()"
  ALSA: hda/realtek: Fix mute and micmute LEDs for an HP laptop
  ALSA: caiaq: input: Add error handling for unsupported input methods in `snd_usb_caiaq_input_init`
  ALSA: usb-audio: Add quirk for Pioneer DDJ-800
  ALSA: hda/realtek: support HP Pavilion Aero 13-be0xxx Mute LED
  ASoC: Intel: soc-acpi-cht: Add quirk for Nextbook Ares 8A tablet
  ASoC: amd: yc: Add Asus VivoBook Pro 14 OLED M6400RC to the quirks list for acp6x
  ASoC: codecs: wcd938x: fix accessing regmap on unattached devices
  ALSA: docs: Fix code block indentation in ALSA driver example
  ALSA: docs: Extend module parameters description
  ALSA: hda/realtek: Add quirk for ASUS UM3402YAR using CS35L41
  ALSA: emu10k1: use more existing defines instead of open-coded numbers
  ASoC: amd: yc: Add ASUS M3402RA into DMI table
  ALSA: hda/realtek: Add quirk for ThinkPad P1 Gen 6
  ...
parents 27e0c846 ee2dd703
Loading
Loading
Loading
Loading
+13 −0
Original line number Diff line number Diff line
@@ -133,6 +133,19 @@ enable
    enable card;
    Default: enabled, for PCI and ISA PnP cards

These options are used for either specifying the order of instances or
controlling enabling and disabling of each one of the devices if there
are multiple devices bound with the same driver. For example, there are
many machines which have two HD-audio controllers (one for HDMI/DP
audio and another for onboard analog). In most cases, the second one is
in primary usage, and people would like to assign it as the first
appearing card. They can do it by specifying "index=1,0" module
parameter, which will swap the assignment slots.

Today, with the sound backend like PulseAudio and PipeWire which
supports dynamic configuration, it's of little use, but that was a
help for static configuration in the past.

Module snd-adlib
----------------

+72 −33
Original line number Diff line number Diff line
@@ -33,6 +33,25 @@
static int fill_silence_frames(struct snd_pcm_substream *substream,
			       snd_pcm_uframes_t off, snd_pcm_uframes_t frames);


static inline void update_silence_vars(struct snd_pcm_runtime *runtime,
				       snd_pcm_uframes_t ptr,
				       snd_pcm_uframes_t new_ptr)
{
	snd_pcm_sframes_t delta;

	delta = new_ptr - ptr;
	if (delta == 0)
		return;
	if (delta < 0)
		delta += runtime->boundary;
	if ((snd_pcm_uframes_t)delta < runtime->silence_filled)
		runtime->silence_filled -= delta;
	else
		runtime->silence_filled = 0;
	runtime->silence_start = new_ptr;
}

/*
 * fill ring buffer with silence
 * runtime->silence_start: starting pointer to silence area
@@ -42,47 +61,67 @@ static int fill_silence_frames(struct snd_pcm_substream *substream,
 *
 * when runtime->silence_size >= runtime->boundary - fill processed area with silence immediately
 */
void snd_pcm_playback_silence(struct snd_pcm_substream *substream)
void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_uframes_t new_hw_ptr)
{
	struct snd_pcm_runtime *runtime = substream->runtime;
	snd_pcm_uframes_t appl_ptr = READ_ONCE(runtime->control->appl_ptr);
	snd_pcm_sframes_t added, hw_avail, frames;
	snd_pcm_uframes_t noise_dist, ofs, transfer;
	snd_pcm_uframes_t frames, ofs, transfer;
	int err;

	added = appl_ptr - runtime->silence_start;
	if (added) {
		if (added < 0)
			added += runtime->boundary;
		if (added < runtime->silence_filled)
			runtime->silence_filled -= added;
		else
			runtime->silence_filled = 0;
		runtime->silence_start = appl_ptr;
	}

	// This will "legitimately" turn negative on underrun, and will be mangled
	// into a huge number by the boundary crossing handling. The initial state
	// might also be not quite sane. The code below MUST account for these cases.
	hw_avail = appl_ptr - runtime->status->hw_ptr;
	if (hw_avail < 0)
		hw_avail += runtime->boundary;

	noise_dist = hw_avail + runtime->silence_filled;
	if (runtime->silence_size < runtime->boundary) {
		frames = runtime->silence_threshold - noise_dist;
		if (frames <= 0)
		snd_pcm_sframes_t noise_dist;
		snd_pcm_uframes_t appl_ptr = READ_ONCE(runtime->control->appl_ptr);
		update_silence_vars(runtime, runtime->silence_start, appl_ptr);
		/* initialization outside pointer updates */
		if (new_hw_ptr == ULONG_MAX)
			new_hw_ptr = runtime->status->hw_ptr;
		/* get hw_avail with the boundary crossing */
		noise_dist = appl_ptr - new_hw_ptr;
		if (noise_dist < 0)
			noise_dist += runtime->boundary;
		/* total noise distance */
		noise_dist += runtime->silence_filled;
		if (noise_dist >= (snd_pcm_sframes_t) runtime->silence_threshold)
			return;
		frames = runtime->silence_threshold - noise_dist;
		if (frames > runtime->silence_size)
			frames = runtime->silence_size;
	} else {
		frames = runtime->buffer_size - noise_dist;
		if (frames <= 0)
			return;
		/*
		 * This filling mode aims at free-running mode (used for example by dmix),
		 * which doesn't update the application pointer.
		 */
		snd_pcm_uframes_t hw_ptr = runtime->status->hw_ptr;
		if (new_hw_ptr == ULONG_MAX) {
			/*
			 * Initialization, fill the whole unused buffer with silence.
			 *
			 * Usually, this is entered while stopped, before data is queued,
			 * so both pointers are expected to be zero.
			 */
			snd_pcm_sframes_t avail = runtime->control->appl_ptr - hw_ptr;
			if (avail < 0)
				avail += runtime->boundary;
			/*
			 * In free-running mode, appl_ptr will be zero even while running,
			 * so we end up with a huge number. There is no useful way to
			 * handle this, so we just clear the whole buffer.
			 */
			runtime->silence_filled = avail > runtime->buffer_size ? 0 : avail;
			runtime->silence_start = hw_ptr;
		} else {
			/* Silence the just played area immediately */
			update_silence_vars(runtime, hw_ptr, new_hw_ptr);
		}
		/*
		 * In this mode, silence_filled actually includes the valid
		 * sample data from the user.
		 */
		frames = runtime->buffer_size - runtime->silence_filled;
	}

	if (snd_BUG_ON(frames > runtime->buffer_size))
		return;
	if (frames == 0)
		return;
	ofs = (runtime->silence_start + runtime->silence_filled) % runtime->buffer_size;
	do {
		transfer = ofs + frames > runtime->buffer_size ? runtime->buffer_size - ofs : frames;
@@ -425,6 +464,10 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
		return 0;
	}

	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
	    runtime->silence_size > 0)
		snd_pcm_playback_silence(substream, new_hw_ptr);

	if (in_interrupt) {
		delta = new_hw_ptr - runtime->hw_ptr_interrupt;
		if (delta < 0)
@@ -442,10 +485,6 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
		runtime->hw_ptr_wrap += runtime->boundary;
	}

	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
	    runtime->silence_size > 0)
		snd_pcm_playback_silence(substream);

	update_audio_tstamp(substream, &curr_tstamp, &audio_tstamp);

	return snd_pcm_update_state(substream, runtime);
+2 −1
Original line number Diff line number Diff line
@@ -29,7 +29,8 @@ int snd_pcm_update_state(struct snd_pcm_substream *substream,
			 struct snd_pcm_runtime *runtime);
int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream);

void snd_pcm_playback_silence(struct snd_pcm_substream *substream);
void snd_pcm_playback_silence(struct snd_pcm_substream *substream,
			      snd_pcm_uframes_t new_hw_ptr);

static inline snd_pcm_uframes_t
snd_pcm_avail(struct snd_pcm_substream *substream)
+3 −3
Original line number Diff line number Diff line
@@ -958,7 +958,7 @@ static int snd_pcm_sw_params(struct snd_pcm_substream *substream,
	if (snd_pcm_running(substream)) {
		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
		    runtime->silence_size > 0)
			snd_pcm_playback_silence(substream);
			snd_pcm_playback_silence(substream, ULONG_MAX);
		err = snd_pcm_update_state(substream, runtime);
	}
	snd_pcm_stream_unlock_irq(substream);
@@ -1455,7 +1455,7 @@ static void snd_pcm_post_start(struct snd_pcm_substream *substream,
	__snd_pcm_set_state(runtime, state);
	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
	    runtime->silence_size > 0)
		snd_pcm_playback_silence(substream);
		snd_pcm_playback_silence(substream, ULONG_MAX);
	snd_pcm_timer_notify(substream, SNDRV_TIMER_EVENT_MSTART);
}

@@ -1916,7 +1916,7 @@ static void snd_pcm_post_reset(struct snd_pcm_substream *substream,
	runtime->control->appl_ptr = runtime->status->hw_ptr;
	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
	    runtime->silence_size > 0)
		snd_pcm_playback_silence(substream);
		snd_pcm_playback_silence(substream, ULONG_MAX);
	snd_pcm_stream_unlock_irq(substream);
}

+8 −7
Original line number Diff line number Diff line
@@ -120,9 +120,9 @@ release_voice(struct snd_emux_voice *vp)
	struct snd_emu10k1 *hw;
	
	hw = vp->hw;
	dcysusv = 0x8000 | (unsigned char)vp->reg.parm.modrelease;
	dcysusv = (unsigned char)vp->reg.parm.modrelease | DCYSUSM_PHASE1_MASK;
	snd_emu10k1_ptr_write(hw, DCYSUSM, vp->ch, dcysusv);
	dcysusv = 0x8000 | (unsigned char)vp->reg.parm.volrelease | DCYSUSV_CHANNELENABLE_MASK;
	dcysusv = (unsigned char)vp->reg.parm.volrelease | DCYSUSV_PHASE1_MASK | DCYSUSV_CHANNELENABLE_MASK;
	snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, dcysusv);
}

@@ -138,7 +138,8 @@ terminate_voice(struct snd_emux_voice *vp)
	if (snd_BUG_ON(!vp))
		return;
	hw = vp->hw;
	snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, 0x807f | DCYSUSV_CHANNELENABLE_MASK);
	snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch,
		DCYSUSV_PHASE1_MASK | DCYSUSV_DECAYTIME_MASK | DCYSUSV_CHANNELENABLE_MASK);
	if (vp->block) {
		struct snd_emu10k1_memblk *emem;
		emem = (struct snd_emu10k1_memblk *)vp->block;
@@ -347,9 +348,9 @@ start_voice(struct snd_emux_voice *vp)
	}

	/* channel to be silent and idle */
	snd_emu10k1_ptr_write(hw, DCYSUSV, ch, 0x0000);
	snd_emu10k1_ptr_write(hw, VTFT, ch, 0x0000FFFF);
	snd_emu10k1_ptr_write(hw, CVCF, ch, 0x0000FFFF);
	snd_emu10k1_ptr_write(hw, DCYSUSV, ch, 0);
	snd_emu10k1_ptr_write(hw, VTFT, ch, VTFT_FILTERTARGET_MASK);
	snd_emu10k1_ptr_write(hw, CVCF, ch, CVCF_CURRENTFILTER_MASK);
	snd_emu10k1_ptr_write(hw, PTRX, ch, 0);
	snd_emu10k1_ptr_write(hw, CPF, ch, 0);

@@ -453,7 +454,7 @@ start_voice(struct snd_emux_voice *vp)
	/* reset volume */
	temp = (unsigned int)vp->vtarget << 16;
	snd_emu10k1_ptr_write(hw, VTFT, ch, temp | vp->ftarget);
	snd_emu10k1_ptr_write(hw, CVCF, ch, temp | 0xff00);
	snd_emu10k1_ptr_write(hw, CVCF, ch, temp | CVCF_CURRENTFILTER_MASK);
	return 0;
}

Loading