Commit 6b779f8a authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull sound fixes from Takashi Iwai:
 "A small collection of fixes:

   - Revert of FireWire changes that caused a long-time regression

   - Another long-time regression fix for AMD HDMI

   - MIDI2 UMP fixes

   - HD-audio Conexant codec fixes and a quirk"

* tag 'sound-6.11-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound:
  ALSA: hda: Conditionally use snooping for AMD HDMI
  ALSA: usb-audio: Correct surround channels in UAC1 channel map
  ALSA: seq: ump: Explicitly reset RPN with Null RPN
  ALSA: seq: ump: Transmit RPN/NRPN message at each MSB/LSB data reception
  ALSA: seq: ump: Use the common RPN/bank conversion context
  ALSA: ump: Explicitly reset RPN with Null RPN
  ALSA: ump: Transmit RPN/NRPN message at each MSB/LSB data reception
  Revert "ALSA: firewire-lib: operate for period elapse event in process context"
  Revert "ALSA: firewire-lib: obsolete workqueue for period update"
  ALSA: hda/realtek: Add quirk for Acer Aspire E5-574G
  ALSA: seq: ump: Optimize conversions from SysEx to UMP
  ALSA: hda/conexant: Mute speakers at suspend / shutdown
  ALSA: hda/generic: Add a helper to mute speakers at suspend/shutdown
  ALSA: hda: conexant: Fix headset auto detect fail in the polling mode
parents 29b4a699 478689b5
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -13,6 +13,7 @@ struct ump_cvt_to_ump_bank {
	unsigned char cc_nrpn_msb, cc_nrpn_lsb;
	unsigned char cc_data_msb, cc_data_lsb;
	unsigned char cc_bank_msb, cc_bank_lsb;
	bool cc_data_msb_set, cc_data_lsb_set;
};

/* context for converting from MIDI1 byte stream to UMP packet */
+2 −12
Original line number Diff line number Diff line
@@ -7,6 +7,7 @@
#define __SND_SEQ_PORTS_H

#include <sound/seq_kernel.h>
#include <sound/ump_convert.h>
#include "seq_lock.h"

/* list of 'exported' ports */
@@ -42,17 +43,6 @@ struct snd_seq_port_subs_info {
	int (*close)(void *private_data, struct snd_seq_port_subscribe *info);
};

/* context for converting from legacy control event to UMP packet */
struct snd_seq_ump_midi2_bank {
	bool rpn_set;
	bool nrpn_set;
	bool bank_set;
	unsigned char cc_rpn_msb, cc_rpn_lsb;
	unsigned char cc_nrpn_msb, cc_nrpn_lsb;
	unsigned char cc_data_msb, cc_data_lsb;
	unsigned char cc_bank_msb, cc_bank_lsb;
};

struct snd_seq_client_port {

	struct snd_seq_addr addr;	/* client/port number */
@@ -88,7 +78,7 @@ struct snd_seq_client_port {
	unsigned char ump_group;

#if IS_ENABLED(CONFIG_SND_SEQ_UMP)
	struct snd_seq_ump_midi2_bank midi2_bank[16]; /* per channel */
	struct ump_cvt_to_ump_bank midi2_bank[16]; /* per channel */
#endif
};

+83 −49
Original line number Diff line number Diff line
@@ -368,7 +368,7 @@ static int cvt_ump_midi1_to_midi2(struct snd_seq_client *dest,
	struct snd_seq_ump_event ev_cvt;
	const union snd_ump_midi1_msg *midi1 = (const union snd_ump_midi1_msg *)event->ump;
	union snd_ump_midi2_msg *midi2 = (union snd_ump_midi2_msg *)ev_cvt.ump;
	struct snd_seq_ump_midi2_bank *cc;
	struct ump_cvt_to_ump_bank *cc;

	ev_cvt = *event;
	memset(&ev_cvt.ump, 0, sizeof(ev_cvt.ump));
@@ -789,28 +789,45 @@ static int paf_ev_to_ump_midi2(const struct snd_seq_event *event,
	return 1;
}

static void reset_rpn(struct ump_cvt_to_ump_bank *cc)
{
	cc->rpn_set = 0;
	cc->nrpn_set = 0;
	cc->cc_rpn_msb = cc->cc_rpn_lsb = 0;
	cc->cc_data_msb = cc->cc_data_lsb = 0;
	cc->cc_data_msb_set = cc->cc_data_lsb_set = 0;
}

/* set up the MIDI2 RPN/NRPN packet data from the parsed info */
static void fill_rpn(struct snd_seq_ump_midi2_bank *cc,
static int fill_rpn(struct ump_cvt_to_ump_bank *cc,
		    union snd_ump_midi2_msg *data,
		     unsigned char channel)
		    unsigned char channel,
		    bool flush)
{
	if (!(cc->cc_data_lsb_set || cc->cc_data_msb_set))
		return 0; // skip
	/* when not flushing, wait for complete data set */
	if (!flush && (!cc->cc_data_lsb_set || !cc->cc_data_msb_set))
		return 0; // skip

	if (cc->rpn_set) {
		data->rpn.status = UMP_MSG_STATUS_RPN;
		data->rpn.bank = cc->cc_rpn_msb;
		data->rpn.index = cc->cc_rpn_lsb;
		cc->rpn_set = 0;
		cc->cc_rpn_msb = cc->cc_rpn_lsb = 0;
	} else {
	} else if (cc->nrpn_set) {
		data->rpn.status = UMP_MSG_STATUS_NRPN;
		data->rpn.bank = cc->cc_nrpn_msb;
		data->rpn.index = cc->cc_nrpn_lsb;
		cc->nrpn_set = 0;
		cc->cc_nrpn_msb = cc->cc_nrpn_lsb = 0;
	} else {
		return 0; // skip
	}

	data->rpn.data = upscale_14_to_32bit((cc->cc_data_msb << 7) |
					     cc->cc_data_lsb);
	data->rpn.channel = channel;
	cc->cc_data_msb = cc->cc_data_lsb = 0;

	reset_rpn(cc);
	return 1;
}

/* convert CC event to MIDI 2.0 UMP */
@@ -822,29 +839,39 @@ static int cc_ev_to_ump_midi2(const struct snd_seq_event *event,
	unsigned char channel = event->data.control.channel & 0x0f;
	unsigned char index = event->data.control.param & 0x7f;
	unsigned char val = event->data.control.value & 0x7f;
	struct snd_seq_ump_midi2_bank *cc = &dest_port->midi2_bank[channel];
	struct ump_cvt_to_ump_bank *cc = &dest_port->midi2_bank[channel];
	int ret;

	/* process special CC's (bank/rpn/nrpn) */
	switch (index) {
	case UMP_CC_RPN_MSB:
		ret = fill_rpn(cc, data, channel, true);
		cc->rpn_set = 1;
		cc->cc_rpn_msb = val;
		return 0; // skip
		if (cc->cc_rpn_msb == 0x7f && cc->cc_rpn_lsb == 0x7f)
			reset_rpn(cc);
		return ret;
	case UMP_CC_RPN_LSB:
		ret = fill_rpn(cc, data, channel, true);
		cc->rpn_set = 1;
		cc->cc_rpn_lsb = val;
		return 0; // skip
		if (cc->cc_rpn_msb == 0x7f && cc->cc_rpn_lsb == 0x7f)
			reset_rpn(cc);
		return ret;
	case UMP_CC_NRPN_MSB:
		ret = fill_rpn(cc, data, channel, true);
		cc->nrpn_set = 1;
		cc->cc_nrpn_msb = val;
		return 0; // skip
		return ret;
	case UMP_CC_NRPN_LSB:
		ret = fill_rpn(cc, data, channel, true);
		cc->nrpn_set = 1;
		cc->cc_nrpn_lsb = val;
		return 0; // skip
		return ret;
	case UMP_CC_DATA:
		cc->cc_data_msb_set = 1;
		cc->cc_data_msb = val;
		return 0; // skip
		return fill_rpn(cc, data, channel, false);
	case UMP_CC_BANK_SELECT:
		cc->bank_set = 1;
		cc->cc_bank_msb = val;
@@ -854,11 +881,9 @@ static int cc_ev_to_ump_midi2(const struct snd_seq_event *event,
		cc->cc_bank_lsb = val;
		return 0; // skip
	case UMP_CC_DATA_LSB:
		cc->cc_data_lsb_set = 1;
		cc->cc_data_lsb = val;
		if (!(cc->rpn_set || cc->nrpn_set))
			return 0; // skip
		fill_rpn(cc, data, channel);
		return 1;
		return fill_rpn(cc, data, channel, false);
	}

	data->cc.status = status;
@@ -887,7 +912,7 @@ static int pgm_ev_to_ump_midi2(const struct snd_seq_event *event,
			       unsigned char status)
{
	unsigned char channel = event->data.control.channel & 0x0f;
	struct snd_seq_ump_midi2_bank *cc = &dest_port->midi2_bank[channel];
	struct ump_cvt_to_ump_bank *cc = &dest_port->midi2_bank[channel];

	data->pg.status = status;
	data->pg.channel = channel;
@@ -924,8 +949,9 @@ static int ctrl14_ev_to_ump_midi2(const struct snd_seq_event *event,
{
	unsigned char channel = event->data.control.channel & 0x0f;
	unsigned char index = event->data.control.param & 0x7f;
	struct snd_seq_ump_midi2_bank *cc = &dest_port->midi2_bank[channel];
	struct ump_cvt_to_ump_bank *cc = &dest_port->midi2_bank[channel];
	unsigned char msb, lsb;
	int ret;

	msb = (event->data.control.value >> 7) & 0x7f;
	lsb = event->data.control.value & 0x7f;
@@ -939,28 +965,27 @@ static int ctrl14_ev_to_ump_midi2(const struct snd_seq_event *event,
		cc->cc_bank_lsb = lsb;
		return 0; // skip
	case UMP_CC_RPN_MSB:
		cc->cc_rpn_msb = msb;
		fallthrough;
	case UMP_CC_RPN_LSB:
		cc->rpn_set = 1;
		ret = fill_rpn(cc, data, channel, true);
		cc->cc_rpn_msb = msb;
		cc->cc_rpn_lsb = lsb;
		return 0; // skip
		cc->rpn_set = 1;
		if (cc->cc_rpn_msb == 0x7f && cc->cc_rpn_lsb == 0x7f)
			reset_rpn(cc);
		return ret;
	case UMP_CC_NRPN_MSB:
		cc->cc_nrpn_msb = msb;
		fallthrough;
	case UMP_CC_NRPN_LSB:
		ret = fill_rpn(cc, data, channel, true);
		cc->cc_nrpn_msb = msb;
		cc->nrpn_set = 1;
		cc->cc_nrpn_lsb = lsb;
		return 0; // skip
		return ret;
	case UMP_CC_DATA:
		cc->cc_data_msb = msb;
		fallthrough;
	case UMP_CC_DATA_LSB:
		cc->cc_data_msb_set = cc->cc_data_lsb_set = 1;
		cc->cc_data_msb = msb;
		cc->cc_data_lsb = lsb;
		if (!(cc->rpn_set || cc->nrpn_set))
			return 0; // skip
		fill_rpn(cc, data, channel);
		return 1;
		return fill_rpn(cc, data, channel, false);
	}

	data->cc.status = UMP_MSG_STATUS_CC;
@@ -1192,44 +1217,53 @@ static int cvt_sysex_to_ump(struct snd_seq_client *dest,
{
	struct snd_seq_ump_event ev_cvt;
	unsigned char status;
	u8 buf[6], *xbuf;
	u8 buf[8], *xbuf;
	int offset = 0;
	int len, err;
	bool finished = false;

	if (!snd_seq_ev_is_variable(event))
		return 0;

	setup_ump_event(&ev_cvt, event);
	for (;;) {
	while (!finished) {
		len = snd_seq_expand_var_event_at(event, sizeof(buf), buf, offset);
		if (len <= 0)
			break;
		if (WARN_ON(len > 6))
		if (WARN_ON(len > sizeof(buf)))
			break;
		offset += len;

		xbuf = buf;
		status = UMP_SYSEX_STATUS_CONTINUE;
		/* truncate the sysex start-marker */
		if (*xbuf == UMP_MIDI1_MSG_SYSEX_START) {
			status = UMP_SYSEX_STATUS_START;
			xbuf++;
			len--;
			if (len > 0 && xbuf[len - 1] == UMP_MIDI1_MSG_SYSEX_END) {
				status = UMP_SYSEX_STATUS_SINGLE;
			len--;
			offset++;
			xbuf++;
		}
		} else {
			if (xbuf[len - 1] == UMP_MIDI1_MSG_SYSEX_END) {

		/* if the last of this packet or the 1st byte of the next packet
		 * is the end-marker, finish the transfer with this packet
		 */
		if (len > 0 && len < 8 &&
		    xbuf[len - 1] == UMP_MIDI1_MSG_SYSEX_END) {
			if (status == UMP_SYSEX_STATUS_START)
				status = UMP_SYSEX_STATUS_SINGLE;
			else
				status = UMP_SYSEX_STATUS_END;
			len--;
			} else {
				status = UMP_SYSEX_STATUS_CONTINUE;
			}
			finished = true;
		}

		len = min(len, 6);
		fill_sysex7_ump(dest_port, ev_cvt.ump, status, xbuf, len);
		err = __snd_seq_deliver_single_event(dest, dest_port,
						     (struct snd_seq_event *)&ev_cvt,
						     atomic, hop);
		if (err < 0)
			return err;
		offset += len;
	}
	return 0;
}
+42 −18
Original line number Diff line number Diff line
@@ -287,25 +287,42 @@ static int cvt_legacy_system_to_ump(struct ump_cvt_to_ump *cvt,
	return 4;
}

static void fill_rpn(struct ump_cvt_to_ump_bank *cc,
		     union snd_ump_midi2_msg *midi2)
static void reset_rpn(struct ump_cvt_to_ump_bank *cc)
{
	cc->rpn_set = 0;
	cc->nrpn_set = 0;
	cc->cc_rpn_msb = cc->cc_rpn_lsb = 0;
	cc->cc_data_msb = cc->cc_data_lsb = 0;
	cc->cc_data_msb_set = cc->cc_data_lsb_set = 0;
}

static int fill_rpn(struct ump_cvt_to_ump_bank *cc,
		    union snd_ump_midi2_msg *midi2,
		    bool flush)
{
	if (!(cc->cc_data_lsb_set || cc->cc_data_msb_set))
		return 0; // skip
	/* when not flushing, wait for complete data set */
	if (!flush && (!cc->cc_data_lsb_set || !cc->cc_data_msb_set))
		return 0; // skip

	if (cc->rpn_set) {
		midi2->rpn.status = UMP_MSG_STATUS_RPN;
		midi2->rpn.bank = cc->cc_rpn_msb;
		midi2->rpn.index = cc->cc_rpn_lsb;
		cc->rpn_set = 0;
		cc->cc_rpn_msb = cc->cc_rpn_lsb = 0;
	} else {
	} else if (cc->nrpn_set) {
		midi2->rpn.status = UMP_MSG_STATUS_NRPN;
		midi2->rpn.bank = cc->cc_nrpn_msb;
		midi2->rpn.index = cc->cc_nrpn_lsb;
		cc->nrpn_set = 0;
		cc->cc_nrpn_msb = cc->cc_nrpn_lsb = 0;
	} else {
		return 0; // skip
	}

	midi2->rpn.data = upscale_14_to_32bit((cc->cc_data_msb << 7) |
					      cc->cc_data_lsb);
	cc->cc_data_msb = cc->cc_data_lsb = 0;

	reset_rpn(cc);
	return 1;
}

/* convert to a MIDI 1.0 Channel Voice message */
@@ -318,6 +335,7 @@ static int cvt_legacy_cmd_to_ump(struct ump_cvt_to_ump *cvt,
	struct ump_cvt_to_ump_bank *cc;
	union snd_ump_midi2_msg *midi2 = (union snd_ump_midi2_msg *)data;
	unsigned char status, channel;
	int ret;

	BUILD_BUG_ON(sizeof(union snd_ump_midi1_msg) != 4);
	BUILD_BUG_ON(sizeof(union snd_ump_midi2_msg) != 8);
@@ -358,24 +376,33 @@ static int cvt_legacy_cmd_to_ump(struct ump_cvt_to_ump *cvt,
	case UMP_MSG_STATUS_CC:
		switch (buf[1]) {
		case UMP_CC_RPN_MSB:
			ret = fill_rpn(cc, midi2, true);
			cc->rpn_set = 1;
			cc->cc_rpn_msb = buf[2];
			return 0; // skip
			if (cc->cc_rpn_msb == 0x7f && cc->cc_rpn_lsb == 0x7f)
				reset_rpn(cc);
			return ret;
		case UMP_CC_RPN_LSB:
			ret = fill_rpn(cc, midi2, true);
			cc->rpn_set = 1;
			cc->cc_rpn_lsb = buf[2];
			return 0; // skip
			if (cc->cc_rpn_msb == 0x7f && cc->cc_rpn_lsb == 0x7f)
				reset_rpn(cc);
			return ret;
		case UMP_CC_NRPN_MSB:
			ret = fill_rpn(cc, midi2, true);
			cc->nrpn_set = 1;
			cc->cc_nrpn_msb = buf[2];
			return 0; // skip
			return ret;
		case UMP_CC_NRPN_LSB:
			ret = fill_rpn(cc, midi2, true);
			cc->nrpn_set = 1;
			cc->cc_nrpn_lsb = buf[2];
			return 0; // skip
			return ret;
		case UMP_CC_DATA:
			cc->cc_data_msb_set = 1;
			cc->cc_data_msb = buf[2];
			return 0; // skip
			return fill_rpn(cc, midi2, false);
		case UMP_CC_BANK_SELECT:
			cc->bank_set = 1;
			cc->cc_bank_msb = buf[2];
@@ -385,12 +412,9 @@ static int cvt_legacy_cmd_to_ump(struct ump_cvt_to_ump *cvt,
			cc->cc_bank_lsb = buf[2];
			return 0; // skip
		case UMP_CC_DATA_LSB:
			cc->cc_data_lsb_set = 1;
			cc->cc_data_lsb = buf[2];
			if (cc->rpn_set || cc->nrpn_set)
				fill_rpn(cc, midi2);
			else
				return 0; // skip
			break;
			return fill_rpn(cc, midi2, false);
		default:
			midi2->cc.index = buf[1];
			midi2->cc.data = upscale_7_to_32bit(buf[2]);
+24 −14
Original line number Diff line number Diff line
@@ -77,6 +77,8 @@
// overrun. Actual device can skip more, then this module stops the packet streaming.
#define IR_JUMBO_PAYLOAD_MAX_SKIP_CYCLES	5

static void pcm_period_work(struct work_struct *work);

/**
 * amdtp_stream_init - initialize an AMDTP stream structure
 * @s: the AMDTP stream to initialize
@@ -105,6 +107,7 @@ int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit,
	s->flags = flags;
	s->context = ERR_PTR(-1);
	mutex_init(&s->mutex);
	INIT_WORK(&s->period_work, pcm_period_work);
	s->packet_index = 0;

	init_waitqueue_head(&s->ready_wait);
@@ -347,6 +350,7 @@ EXPORT_SYMBOL(amdtp_stream_get_max_payload);
 */
void amdtp_stream_pcm_prepare(struct amdtp_stream *s)
{
	cancel_work_sync(&s->period_work);
	s->pcm_buffer_pointer = 0;
	s->pcm_period_pointer = 0;
}
@@ -611,17 +615,19 @@ static void update_pcm_pointers(struct amdtp_stream *s,
		// The program in user process should periodically check the status of intermediate
		// buffer associated to PCM substream to process PCM frames in the buffer, instead
		// of receiving notification of period elapsed by poll wait.
		if (!pcm->runtime->no_period_wakeup) {
			if (in_softirq()) {
				// In software IRQ context for 1394 OHCI.
				snd_pcm_period_elapsed(pcm);
			} else {
				// In process context of ALSA PCM application under acquired lock of
				// PCM substream.
				snd_pcm_period_elapsed_under_stream_lock(pcm);
			}
		if (!pcm->runtime->no_period_wakeup)
			queue_work(system_highpri_wq, &s->period_work);
	}
}

static void pcm_period_work(struct work_struct *work)
{
	struct amdtp_stream *s = container_of(work, struct amdtp_stream,
					      period_work);
	struct snd_pcm_substream *pcm = READ_ONCE(s->pcm);

	if (pcm)
		snd_pcm_period_elapsed(pcm);
}

static int queue_packet(struct amdtp_stream *s, struct fw_iso_packet *params,
@@ -1849,11 +1855,14 @@ unsigned long amdtp_domain_stream_pcm_pointer(struct amdtp_domain *d,
{
	struct amdtp_stream *irq_target = d->irq_target;

	// Process isochronous packets queued till recent isochronous cycle to handle PCM frames.
	if (irq_target && amdtp_stream_running(irq_target)) {
		// In software IRQ context, the call causes dead-lock to disable the tasklet
		// synchronously.
		if (!in_softirq())
		// use wq to prevent AB/BA deadlock competition for
		// substream lock:
		// fw_iso_context_flush_completions() acquires
		// lock by ohci_flush_iso_completions(),
		// amdtp-stream process_rx_packets() attempts to
		// acquire same lock by snd_pcm_elapsed()
		if (current_work() != &s->period_work)
			fw_iso_context_flush_completions(irq_target->context);
	}

@@ -1909,6 +1918,7 @@ static void amdtp_stream_stop(struct amdtp_stream *s)
		return;
	}

	cancel_work_sync(&s->period_work);
	fw_iso_context_stop(s->context);
	fw_iso_context_destroy(s->context);
	s->context = ERR_PTR(-1);
Loading