sound fixes for 6.11-rc2
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 qurik -----BEGIN PGP SIGNATURE----- iQJCBAABCAAsFiEEIXTw5fNLNI7mMiVaLtJE4w1nLE8FAmaslGMOHHRpd2FpQHN1 c2UuZGUACgkQLtJE4w1nLE8cXw/+MXYSGLdac8hALz3hxYBqVDo1Lr4NBJar6d8/ 1sngToZa3hb74uwvRctJ/+7bMV6/kIh76cwk+rM8PNSVtidivuTzlcASg8k1DMDq Zqxtba7UE8iC94BInu+GyVRlvYqQ41EwrFNu89oldlffYg+WInLVUe/genMf6NjT PycaFnxsmW1XTBXFqAEJqjNI9VkCyG3/6veOOtKJRPM+yTwjdH2tXGrgdXtVG47e lI10CxT6BFdBG7pmqVejOAMXVSJFKfGpLoyzlIiMr3NR4/qNhzMcQ092qX25CkHu 37MzVXuxlHZFAlk9S28P+ReLRiTc2SdTGDA3zCnkex9kMOF9P8LbMN5gmY7m/6pa Wpq3U5sO/S9peaBtuR1ryGTqkt0jG9aXbRYVuaFMGQu8BVaERj6KEOoYcAeWGxiZ cZHhg4YDqgBExDhU2fPoLwTu1fRlm0RYwW+aiuaXIOXezPfph1M/pTnZR0FhDtd0 BzCSLSaDPV/mWQM8ql8QzHjkHdb3SLhaGiBlQC0U9X5DA1J2KTtpXibG9+tzPQGx 7naXLqswq33gLvWW1SpzhGPODBPRrddFsTRBrg2TlX6cywVmB/ylAl1f6EeZiKyA UWG5lMmXfB4jyDqUJC0mlVl/DQcsRsaChh+NdLeubfaGn8OBPEIDzzHM7GDjPHTQ CXTIxzI= =tMqd -----END PGP SIGNATURE----- Merge tag 'sound-6.11-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound 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
This commit is contained in:
commit
6b779f8a86
@ -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 */
|
||||
|
@ -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
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
/* set up the MIDI2 RPN/NRPN packet data from the parsed info */
|
||||
static void fill_rpn(struct snd_seq_ump_midi2_bank *cc,
|
||||
union snd_ump_midi2_msg *data,
|
||||
unsigned char channel)
|
||||
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 int fill_rpn(struct ump_cvt_to_ump_bank *cc,
|
||||
union snd_ump_midi2_msg *data,
|
||||
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;
|
||||
len--;
|
||||
offset++;
|
||||
xbuf++;
|
||||
len--;
|
||||
if (len > 0 && xbuf[len - 1] == UMP_MIDI1_MSG_SYSEX_END) {
|
||||
status = UMP_SYSEX_STATUS_SINGLE;
|
||||
len--;
|
||||
}
|
||||
} 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;
|
||||
}
|
||||
|
@ -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]);
|
||||
|
@ -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.
|
||||
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);
|
||||
} else {
|
||||
// In process context of ALSA PCM application under acquired lock of
|
||||
// PCM substream.
|
||||
snd_pcm_period_elapsed_under_stream_lock(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);
|
||||
|
@ -191,6 +191,7 @@ struct amdtp_stream {
|
||||
|
||||
/* For a PCM substream processing. */
|
||||
struct snd_pcm_substream *pcm;
|
||||
struct work_struct period_work;
|
||||
snd_pcm_uframes_t pcm_buffer_pointer;
|
||||
unsigned int pcm_period_pointer;
|
||||
unsigned int pcm_frame_multiplier;
|
||||
|
@ -28,7 +28,7 @@
|
||||
#else
|
||||
#define AZX_DCAPS_I915_COMPONENT 0 /* NOP */
|
||||
#endif
|
||||
/* 14 unused */
|
||||
#define AZX_DCAPS_AMD_ALLOC_FIX (1 << 14) /* AMD allocation workaround */
|
||||
#define AZX_DCAPS_CTX_WORKAROUND (1 << 15) /* X-Fi workaround */
|
||||
#define AZX_DCAPS_POSFIX_LPIB (1 << 16) /* Use LPIB as default */
|
||||
#define AZX_DCAPS_AMD_WORKAROUND (1 << 17) /* AMD-specific workaround */
|
||||
|
@ -4955,6 +4955,69 @@ void snd_hda_gen_stream_pm(struct hda_codec *codec, hda_nid_t nid, bool on)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hda_gen_stream_pm);
|
||||
|
||||
/* forcibly mute the speaker output without caching; return true if updated */
|
||||
static bool force_mute_output_path(struct hda_codec *codec, hda_nid_t nid)
|
||||
{
|
||||
if (!nid)
|
||||
return false;
|
||||
if (!nid_has_mute(codec, nid, HDA_OUTPUT))
|
||||
return false; /* no mute, skip */
|
||||
if (snd_hda_codec_amp_read(codec, nid, 0, HDA_OUTPUT, 0) &
|
||||
snd_hda_codec_amp_read(codec, nid, 1, HDA_OUTPUT, 0) &
|
||||
HDA_AMP_MUTE)
|
||||
return false; /* both channels already muted, skip */
|
||||
|
||||
/* direct amp update without caching */
|
||||
snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
|
||||
AC_AMP_SET_OUTPUT | AC_AMP_SET_LEFT |
|
||||
AC_AMP_SET_RIGHT | HDA_AMP_MUTE);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_hda_gen_shutup_speakers - Forcibly mute the speaker outputs
|
||||
* @codec: the HDA codec
|
||||
*
|
||||
* Forcibly mute the speaker outputs, to be called at suspend or shutdown.
|
||||
*
|
||||
* The mute state done by this function isn't cached, hence the original state
|
||||
* will be restored at resume.
|
||||
*
|
||||
* Return true if the mute state has been changed.
|
||||
*/
|
||||
bool snd_hda_gen_shutup_speakers(struct hda_codec *codec)
|
||||
{
|
||||
struct hda_gen_spec *spec = codec->spec;
|
||||
const int *paths;
|
||||
const struct nid_path *path;
|
||||
int i, p, num_paths;
|
||||
bool updated = false;
|
||||
|
||||
/* if already powered off, do nothing */
|
||||
if (!snd_hdac_is_power_on(&codec->core))
|
||||
return false;
|
||||
|
||||
if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT) {
|
||||
paths = spec->out_paths;
|
||||
num_paths = spec->autocfg.line_outs;
|
||||
} else {
|
||||
paths = spec->speaker_paths;
|
||||
num_paths = spec->autocfg.speaker_outs;
|
||||
}
|
||||
|
||||
for (i = 0; i < num_paths; i++) {
|
||||
path = snd_hda_get_path_from_idx(codec, paths[i]);
|
||||
if (!path)
|
||||
continue;
|
||||
for (p = 0; p < path->depth; p++)
|
||||
if (force_mute_output_path(codec, path->path[p]))
|
||||
updated = true;
|
||||
}
|
||||
|
||||
return updated;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hda_gen_shutup_speakers);
|
||||
|
||||
/**
|
||||
* snd_hda_gen_parse_auto_config - Parse the given BIOS configuration and
|
||||
* set up the hda_gen_spec
|
||||
|
@ -353,5 +353,6 @@ int snd_hda_gen_add_mute_led_cdev(struct hda_codec *codec,
|
||||
int snd_hda_gen_add_micmute_led_cdev(struct hda_codec *codec,
|
||||
int (*callback)(struct led_classdev *,
|
||||
enum led_brightness));
|
||||
bool snd_hda_gen_shutup_speakers(struct hda_codec *codec);
|
||||
|
||||
#endif /* __SOUND_HDA_GENERIC_H */
|
||||
|
@ -40,6 +40,7 @@
|
||||
|
||||
#ifdef CONFIG_X86
|
||||
/* for snoop control */
|
||||
#include <linux/dma-map-ops.h>
|
||||
#include <asm/set_memory.h>
|
||||
#include <asm/cpufeature.h>
|
||||
#endif
|
||||
@ -306,7 +307,7 @@ enum {
|
||||
|
||||
/* quirks for ATI HDMI with snoop off */
|
||||
#define AZX_DCAPS_PRESET_ATI_HDMI_NS \
|
||||
(AZX_DCAPS_PRESET_ATI_HDMI | AZX_DCAPS_SNOOP_OFF)
|
||||
(AZX_DCAPS_PRESET_ATI_HDMI | AZX_DCAPS_AMD_ALLOC_FIX)
|
||||
|
||||
/* quirks for AMD SB */
|
||||
#define AZX_DCAPS_PRESET_AMD_SB \
|
||||
@ -1702,6 +1703,13 @@ static void azx_check_snoop_available(struct azx *chip)
|
||||
if (chip->driver_caps & AZX_DCAPS_SNOOP_OFF)
|
||||
snoop = false;
|
||||
|
||||
#ifdef CONFIG_X86
|
||||
/* check the presence of DMA ops (i.e. IOMMU), disable snoop conditionally */
|
||||
if ((chip->driver_caps & AZX_DCAPS_AMD_ALLOC_FIX) &&
|
||||
!get_dma_ops(chip->card->dev))
|
||||
snoop = false;
|
||||
#endif
|
||||
|
||||
chip->snoop = snoop;
|
||||
if (!snoop) {
|
||||
dev_info(chip->card->dev, "Force to non-snoop mode\n");
|
||||
|
@ -21,12 +21,6 @@
|
||||
#include "hda_jack.h"
|
||||
#include "hda_generic.h"
|
||||
|
||||
enum {
|
||||
CX_HEADSET_NOPRESENT = 0,
|
||||
CX_HEADSET_PARTPRESENT,
|
||||
CX_HEADSET_ALLPRESENT,
|
||||
};
|
||||
|
||||
struct conexant_spec {
|
||||
struct hda_gen_spec gen;
|
||||
|
||||
@ -48,7 +42,6 @@ struct conexant_spec {
|
||||
unsigned int gpio_led;
|
||||
unsigned int gpio_mute_led_mask;
|
||||
unsigned int gpio_mic_led_mask;
|
||||
unsigned int headset_present_flag;
|
||||
bool is_cx8070_sn6140;
|
||||
};
|
||||
|
||||
@ -212,6 +205,8 @@ static void cx_auto_shutdown(struct hda_codec *codec)
|
||||
{
|
||||
struct conexant_spec *spec = codec->spec;
|
||||
|
||||
snd_hda_gen_shutup_speakers(codec);
|
||||
|
||||
/* Turn the problematic codec into D3 to avoid spurious noises
|
||||
from the internal speaker during (and after) reboot */
|
||||
cx_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, false);
|
||||
@ -250,48 +245,19 @@ static void cx_process_headset_plugin(struct hda_codec *codec)
|
||||
}
|
||||
}
|
||||
|
||||
static void cx_update_headset_mic_vref(struct hda_codec *codec, unsigned int res)
|
||||
static void cx_update_headset_mic_vref(struct hda_codec *codec, struct hda_jack_callback *event)
|
||||
{
|
||||
unsigned int phone_present, mic_persent, phone_tag, mic_tag;
|
||||
struct conexant_spec *spec = codec->spec;
|
||||
unsigned int mic_present;
|
||||
|
||||
/* In cx8070 and sn6140, the node 16 can only be config to headphone or disabled,
|
||||
* the node 19 can only be config to microphone or disabled.
|
||||
* Check hp&mic tag to process headset pulgin&plugout.
|
||||
*/
|
||||
phone_tag = snd_hda_codec_read(codec, 0x16, 0, AC_VERB_GET_UNSOLICITED_RESPONSE, 0x0);
|
||||
mic_tag = snd_hda_codec_read(codec, 0x19, 0, AC_VERB_GET_UNSOLICITED_RESPONSE, 0x0);
|
||||
if ((phone_tag & (res >> AC_UNSOL_RES_TAG_SHIFT)) ||
|
||||
(mic_tag & (res >> AC_UNSOL_RES_TAG_SHIFT))) {
|
||||
phone_present = snd_hda_codec_read(codec, 0x16, 0, AC_VERB_GET_PIN_SENSE, 0x0);
|
||||
if (!(phone_present & AC_PINSENSE_PRESENCE)) {/* headphone plugout */
|
||||
spec->headset_present_flag = CX_HEADSET_NOPRESENT;
|
||||
mic_present = snd_hda_codec_read(codec, 0x19, 0, AC_VERB_GET_PIN_SENSE, 0x0);
|
||||
if (!(mic_present & AC_PINSENSE_PRESENCE)) /* mic plugout */
|
||||
snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20);
|
||||
return;
|
||||
}
|
||||
if (spec->headset_present_flag == CX_HEADSET_NOPRESENT) {
|
||||
spec->headset_present_flag = CX_HEADSET_PARTPRESENT;
|
||||
} else if (spec->headset_present_flag == CX_HEADSET_PARTPRESENT) {
|
||||
mic_persent = snd_hda_codec_read(codec, 0x19, 0,
|
||||
AC_VERB_GET_PIN_SENSE, 0x0);
|
||||
/* headset is present */
|
||||
if ((phone_present & AC_PINSENSE_PRESENCE) &&
|
||||
(mic_persent & AC_PINSENSE_PRESENCE)) {
|
||||
else
|
||||
cx_process_headset_plugin(codec);
|
||||
spec->headset_present_flag = CX_HEADSET_ALLPRESENT;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void cx_jack_unsol_event(struct hda_codec *codec, unsigned int res)
|
||||
{
|
||||
struct conexant_spec *spec = codec->spec;
|
||||
|
||||
if (spec->is_cx8070_sn6140)
|
||||
cx_update_headset_mic_vref(codec, res);
|
||||
|
||||
snd_hda_jack_unsol_event(codec, res);
|
||||
}
|
||||
|
||||
static int cx_auto_suspend(struct hda_codec *codec)
|
||||
@ -305,7 +271,7 @@ static const struct hda_codec_ops cx_auto_patch_ops = {
|
||||
.build_pcms = snd_hda_gen_build_pcms,
|
||||
.init = cx_auto_init,
|
||||
.free = cx_auto_free,
|
||||
.unsol_event = cx_jack_unsol_event,
|
||||
.unsol_event = snd_hda_jack_unsol_event,
|
||||
.suspend = cx_auto_suspend,
|
||||
.check_power_status = snd_hda_gen_check_power_status,
|
||||
};
|
||||
@ -1163,7 +1129,7 @@ static int patch_conexant_auto(struct hda_codec *codec)
|
||||
case 0x14f11f86:
|
||||
case 0x14f11f87:
|
||||
spec->is_cx8070_sn6140 = true;
|
||||
spec->headset_present_flag = CX_HEADSET_NOPRESENT;
|
||||
snd_hda_jack_detect_enable_callback(codec, 0x19, cx_update_headset_mic_vref);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -9872,6 +9872,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
|
||||
SND_PCI_QUIRK(0x1025, 0x079b, "Acer Aspire V5-573G", ALC282_FIXUP_ASPIRE_V5_PINS),
|
||||
SND_PCI_QUIRK(0x1025, 0x080d, "Acer Aspire V5-122P", ALC269_FIXUP_ASPIRE_HEADSET_MIC),
|
||||
SND_PCI_QUIRK(0x1025, 0x0840, "Acer Aspire E1", ALC269VB_FIXUP_ASPIRE_E1_COEF),
|
||||
SND_PCI_QUIRK(0x1025, 0x100c, "Acer Aspire E5-574G", ALC255_FIXUP_ACER_LIMIT_INT_MIC_BOOST),
|
||||
SND_PCI_QUIRK(0x1025, 0x101c, "Acer Veriton N2510G", ALC269_FIXUP_LIFEBOOK),
|
||||
SND_PCI_QUIRK(0x1025, 0x102b, "Acer Aspire C24-860", ALC286_FIXUP_ACER_AIO_MIC_NO_PRESENCE),
|
||||
SND_PCI_QUIRK(0x1025, 0x1065, "Acer Aspire C20-820", ALC269VC_FIXUP_ACER_HEADSET_MIC),
|
||||
|
@ -244,8 +244,8 @@ static struct snd_pcm_chmap_elem *convert_chmap(int channels, unsigned int bits,
|
||||
SNDRV_CHMAP_FR, /* right front */
|
||||
SNDRV_CHMAP_FC, /* center front */
|
||||
SNDRV_CHMAP_LFE, /* LFE */
|
||||
SNDRV_CHMAP_SL, /* left surround */
|
||||
SNDRV_CHMAP_SR, /* right surround */
|
||||
SNDRV_CHMAP_RL, /* left surround */
|
||||
SNDRV_CHMAP_RR, /* right surround */
|
||||
SNDRV_CHMAP_FLC, /* left of center */
|
||||
SNDRV_CHMAP_FRC, /* right of center */
|
||||
SNDRV_CHMAP_RC, /* surround */
|
||||
|
Loading…
Reference in New Issue
Block a user