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 changed files with 244 additions and 145 deletions
|
@ -13,6 +13,7 @@ struct ump_cvt_to_ump_bank {
|
||||||
unsigned char cc_nrpn_msb, cc_nrpn_lsb;
|
unsigned char cc_nrpn_msb, cc_nrpn_lsb;
|
||||||
unsigned char cc_data_msb, cc_data_lsb;
|
unsigned char cc_data_msb, cc_data_lsb;
|
||||||
unsigned char cc_bank_msb, cc_bank_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 */
|
/* context for converting from MIDI1 byte stream to UMP packet */
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#define __SND_SEQ_PORTS_H
|
#define __SND_SEQ_PORTS_H
|
||||||
|
|
||||||
#include <sound/seq_kernel.h>
|
#include <sound/seq_kernel.h>
|
||||||
|
#include <sound/ump_convert.h>
|
||||||
#include "seq_lock.h"
|
#include "seq_lock.h"
|
||||||
|
|
||||||
/* list of 'exported' ports */
|
/* 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);
|
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_client_port {
|
||||||
|
|
||||||
struct snd_seq_addr addr; /* client/port number */
|
struct snd_seq_addr addr; /* client/port number */
|
||||||
|
@ -88,7 +78,7 @@ struct snd_seq_client_port {
|
||||||
unsigned char ump_group;
|
unsigned char ump_group;
|
||||||
|
|
||||||
#if IS_ENABLED(CONFIG_SND_SEQ_UMP)
|
#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
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -368,7 +368,7 @@ static int cvt_ump_midi1_to_midi2(struct snd_seq_client *dest,
|
||||||
struct snd_seq_ump_event ev_cvt;
|
struct snd_seq_ump_event ev_cvt;
|
||||||
const union snd_ump_midi1_msg *midi1 = (const union snd_ump_midi1_msg *)event->ump;
|
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;
|
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;
|
ev_cvt = *event;
|
||||||
memset(&ev_cvt.ump, 0, sizeof(ev_cvt.ump));
|
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;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* set up the MIDI2 RPN/NRPN packet data from the parsed info */
|
static void reset_rpn(struct ump_cvt_to_ump_bank *cc)
|
||||||
static void fill_rpn(struct snd_seq_ump_midi2_bank *cc,
|
|
||||||
union snd_ump_midi2_msg *data,
|
|
||||||
unsigned char channel)
|
|
||||||
{
|
{
|
||||||
|
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) {
|
if (cc->rpn_set) {
|
||||||
data->rpn.status = UMP_MSG_STATUS_RPN;
|
data->rpn.status = UMP_MSG_STATUS_RPN;
|
||||||
data->rpn.bank = cc->cc_rpn_msb;
|
data->rpn.bank = cc->cc_rpn_msb;
|
||||||
data->rpn.index = cc->cc_rpn_lsb;
|
data->rpn.index = cc->cc_rpn_lsb;
|
||||||
cc->rpn_set = 0;
|
} else if (cc->nrpn_set) {
|
||||||
cc->cc_rpn_msb = cc->cc_rpn_lsb = 0;
|
|
||||||
} else {
|
|
||||||
data->rpn.status = UMP_MSG_STATUS_NRPN;
|
data->rpn.status = UMP_MSG_STATUS_NRPN;
|
||||||
data->rpn.bank = cc->cc_nrpn_msb;
|
data->rpn.bank = cc->cc_nrpn_msb;
|
||||||
data->rpn.index = cc->cc_nrpn_lsb;
|
data->rpn.index = cc->cc_nrpn_lsb;
|
||||||
cc->nrpn_set = 0;
|
} else {
|
||||||
cc->cc_nrpn_msb = cc->cc_nrpn_lsb = 0;
|
return 0; // skip
|
||||||
}
|
}
|
||||||
|
|
||||||
data->rpn.data = upscale_14_to_32bit((cc->cc_data_msb << 7) |
|
data->rpn.data = upscale_14_to_32bit((cc->cc_data_msb << 7) |
|
||||||
cc->cc_data_lsb);
|
cc->cc_data_lsb);
|
||||||
data->rpn.channel = channel;
|
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 */
|
/* 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 channel = event->data.control.channel & 0x0f;
|
||||||
unsigned char index = event->data.control.param & 0x7f;
|
unsigned char index = event->data.control.param & 0x7f;
|
||||||
unsigned char val = event->data.control.value & 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) */
|
/* process special CC's (bank/rpn/nrpn) */
|
||||||
switch (index) {
|
switch (index) {
|
||||||
case UMP_CC_RPN_MSB:
|
case UMP_CC_RPN_MSB:
|
||||||
|
ret = fill_rpn(cc, data, channel, true);
|
||||||
cc->rpn_set = 1;
|
cc->rpn_set = 1;
|
||||||
cc->cc_rpn_msb = val;
|
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:
|
case UMP_CC_RPN_LSB:
|
||||||
|
ret = fill_rpn(cc, data, channel, true);
|
||||||
cc->rpn_set = 1;
|
cc->rpn_set = 1;
|
||||||
cc->cc_rpn_lsb = val;
|
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:
|
case UMP_CC_NRPN_MSB:
|
||||||
|
ret = fill_rpn(cc, data, channel, true);
|
||||||
cc->nrpn_set = 1;
|
cc->nrpn_set = 1;
|
||||||
cc->cc_nrpn_msb = val;
|
cc->cc_nrpn_msb = val;
|
||||||
return 0; // skip
|
return ret;
|
||||||
case UMP_CC_NRPN_LSB:
|
case UMP_CC_NRPN_LSB:
|
||||||
|
ret = fill_rpn(cc, data, channel, true);
|
||||||
cc->nrpn_set = 1;
|
cc->nrpn_set = 1;
|
||||||
cc->cc_nrpn_lsb = val;
|
cc->cc_nrpn_lsb = val;
|
||||||
return 0; // skip
|
return ret;
|
||||||
case UMP_CC_DATA:
|
case UMP_CC_DATA:
|
||||||
|
cc->cc_data_msb_set = 1;
|
||||||
cc->cc_data_msb = val;
|
cc->cc_data_msb = val;
|
||||||
return 0; // skip
|
return fill_rpn(cc, data, channel, false);
|
||||||
case UMP_CC_BANK_SELECT:
|
case UMP_CC_BANK_SELECT:
|
||||||
cc->bank_set = 1;
|
cc->bank_set = 1;
|
||||||
cc->cc_bank_msb = val;
|
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;
|
cc->cc_bank_lsb = val;
|
||||||
return 0; // skip
|
return 0; // skip
|
||||||
case UMP_CC_DATA_LSB:
|
case UMP_CC_DATA_LSB:
|
||||||
|
cc->cc_data_lsb_set = 1;
|
||||||
cc->cc_data_lsb = val;
|
cc->cc_data_lsb = val;
|
||||||
if (!(cc->rpn_set || cc->nrpn_set))
|
return fill_rpn(cc, data, channel, false);
|
||||||
return 0; // skip
|
|
||||||
fill_rpn(cc, data, channel);
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
data->cc.status = status;
|
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 status)
|
||||||
{
|
{
|
||||||
unsigned char channel = event->data.control.channel & 0x0f;
|
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.status = status;
|
||||||
data->pg.channel = channel;
|
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 channel = event->data.control.channel & 0x0f;
|
||||||
unsigned char index = event->data.control.param & 0x7f;
|
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;
|
unsigned char msb, lsb;
|
||||||
|
int ret;
|
||||||
|
|
||||||
msb = (event->data.control.value >> 7) & 0x7f;
|
msb = (event->data.control.value >> 7) & 0x7f;
|
||||||
lsb = event->data.control.value & 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;
|
cc->cc_bank_lsb = lsb;
|
||||||
return 0; // skip
|
return 0; // skip
|
||||||
case UMP_CC_RPN_MSB:
|
case UMP_CC_RPN_MSB:
|
||||||
cc->cc_rpn_msb = msb;
|
|
||||||
fallthrough;
|
|
||||||
case UMP_CC_RPN_LSB:
|
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;
|
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:
|
case UMP_CC_NRPN_MSB:
|
||||||
cc->cc_nrpn_msb = msb;
|
|
||||||
fallthrough;
|
|
||||||
case UMP_CC_NRPN_LSB:
|
case UMP_CC_NRPN_LSB:
|
||||||
|
ret = fill_rpn(cc, data, channel, true);
|
||||||
|
cc->cc_nrpn_msb = msb;
|
||||||
cc->nrpn_set = 1;
|
cc->nrpn_set = 1;
|
||||||
cc->cc_nrpn_lsb = lsb;
|
cc->cc_nrpn_lsb = lsb;
|
||||||
return 0; // skip
|
return ret;
|
||||||
case UMP_CC_DATA:
|
case UMP_CC_DATA:
|
||||||
cc->cc_data_msb = msb;
|
|
||||||
fallthrough;
|
|
||||||
case UMP_CC_DATA_LSB:
|
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;
|
cc->cc_data_lsb = lsb;
|
||||||
if (!(cc->rpn_set || cc->nrpn_set))
|
return fill_rpn(cc, data, channel, false);
|
||||||
return 0; // skip
|
|
||||||
fill_rpn(cc, data, channel);
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
data->cc.status = UMP_MSG_STATUS_CC;
|
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;
|
struct snd_seq_ump_event ev_cvt;
|
||||||
unsigned char status;
|
unsigned char status;
|
||||||
u8 buf[6], *xbuf;
|
u8 buf[8], *xbuf;
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
int len, err;
|
int len, err;
|
||||||
|
bool finished = false;
|
||||||
|
|
||||||
if (!snd_seq_ev_is_variable(event))
|
if (!snd_seq_ev_is_variable(event))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
setup_ump_event(&ev_cvt, event);
|
setup_ump_event(&ev_cvt, event);
|
||||||
for (;;) {
|
while (!finished) {
|
||||||
len = snd_seq_expand_var_event_at(event, sizeof(buf), buf, offset);
|
len = snd_seq_expand_var_event_at(event, sizeof(buf), buf, offset);
|
||||||
if (len <= 0)
|
if (len <= 0)
|
||||||
break;
|
break;
|
||||||
if (WARN_ON(len > 6))
|
if (WARN_ON(len > sizeof(buf)))
|
||||||
break;
|
break;
|
||||||
offset += len;
|
|
||||||
xbuf = buf;
|
xbuf = buf;
|
||||||
|
status = UMP_SYSEX_STATUS_CONTINUE;
|
||||||
|
/* truncate the sysex start-marker */
|
||||||
if (*xbuf == UMP_MIDI1_MSG_SYSEX_START) {
|
if (*xbuf == UMP_MIDI1_MSG_SYSEX_START) {
|
||||||
status = UMP_SYSEX_STATUS_START;
|
status = UMP_SYSEX_STATUS_START;
|
||||||
xbuf++;
|
|
||||||
len--;
|
len--;
|
||||||
if (len > 0 && xbuf[len - 1] == UMP_MIDI1_MSG_SYSEX_END) {
|
offset++;
|
||||||
status = UMP_SYSEX_STATUS_SINGLE;
|
xbuf++;
|
||||||
len--;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (xbuf[len - 1] == UMP_MIDI1_MSG_SYSEX_END) {
|
|
||||||
status = UMP_SYSEX_STATUS_END;
|
|
||||||
len--;
|
|
||||||
} else {
|
|
||||||
status = UMP_SYSEX_STATUS_CONTINUE;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 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--;
|
||||||
|
finished = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
len = min(len, 6);
|
||||||
fill_sysex7_ump(dest_port, ev_cvt.ump, status, xbuf, len);
|
fill_sysex7_ump(dest_port, ev_cvt.ump, status, xbuf, len);
|
||||||
err = __snd_seq_deliver_single_event(dest, dest_port,
|
err = __snd_seq_deliver_single_event(dest, dest_port,
|
||||||
(struct snd_seq_event *)&ev_cvt,
|
(struct snd_seq_event *)&ev_cvt,
|
||||||
atomic, hop);
|
atomic, hop);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return err;
|
return err;
|
||||||
|
offset += len;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -287,25 +287,42 @@ static int cvt_legacy_system_to_ump(struct ump_cvt_to_ump *cvt,
|
||||||
return 4;
|
return 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void fill_rpn(struct ump_cvt_to_ump_bank *cc,
|
static void reset_rpn(struct ump_cvt_to_ump_bank *cc)
|
||||||
union snd_ump_midi2_msg *midi2)
|
|
||||||
{
|
{
|
||||||
|
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) {
|
if (cc->rpn_set) {
|
||||||
midi2->rpn.status = UMP_MSG_STATUS_RPN;
|
midi2->rpn.status = UMP_MSG_STATUS_RPN;
|
||||||
midi2->rpn.bank = cc->cc_rpn_msb;
|
midi2->rpn.bank = cc->cc_rpn_msb;
|
||||||
midi2->rpn.index = cc->cc_rpn_lsb;
|
midi2->rpn.index = cc->cc_rpn_lsb;
|
||||||
cc->rpn_set = 0;
|
} else if (cc->nrpn_set) {
|
||||||
cc->cc_rpn_msb = cc->cc_rpn_lsb = 0;
|
|
||||||
} else {
|
|
||||||
midi2->rpn.status = UMP_MSG_STATUS_NRPN;
|
midi2->rpn.status = UMP_MSG_STATUS_NRPN;
|
||||||
midi2->rpn.bank = cc->cc_nrpn_msb;
|
midi2->rpn.bank = cc->cc_nrpn_msb;
|
||||||
midi2->rpn.index = cc->cc_nrpn_lsb;
|
midi2->rpn.index = cc->cc_nrpn_lsb;
|
||||||
cc->nrpn_set = 0;
|
} else {
|
||||||
cc->cc_nrpn_msb = cc->cc_nrpn_lsb = 0;
|
return 0; // skip
|
||||||
}
|
}
|
||||||
|
|
||||||
midi2->rpn.data = upscale_14_to_32bit((cc->cc_data_msb << 7) |
|
midi2->rpn.data = upscale_14_to_32bit((cc->cc_data_msb << 7) |
|
||||||
cc->cc_data_lsb);
|
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 */
|
/* 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;
|
struct ump_cvt_to_ump_bank *cc;
|
||||||
union snd_ump_midi2_msg *midi2 = (union snd_ump_midi2_msg *)data;
|
union snd_ump_midi2_msg *midi2 = (union snd_ump_midi2_msg *)data;
|
||||||
unsigned char status, channel;
|
unsigned char status, channel;
|
||||||
|
int ret;
|
||||||
|
|
||||||
BUILD_BUG_ON(sizeof(union snd_ump_midi1_msg) != 4);
|
BUILD_BUG_ON(sizeof(union snd_ump_midi1_msg) != 4);
|
||||||
BUILD_BUG_ON(sizeof(union snd_ump_midi2_msg) != 8);
|
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:
|
case UMP_MSG_STATUS_CC:
|
||||||
switch (buf[1]) {
|
switch (buf[1]) {
|
||||||
case UMP_CC_RPN_MSB:
|
case UMP_CC_RPN_MSB:
|
||||||
|
ret = fill_rpn(cc, midi2, true);
|
||||||
cc->rpn_set = 1;
|
cc->rpn_set = 1;
|
||||||
cc->cc_rpn_msb = buf[2];
|
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:
|
case UMP_CC_RPN_LSB:
|
||||||
|
ret = fill_rpn(cc, midi2, true);
|
||||||
cc->rpn_set = 1;
|
cc->rpn_set = 1;
|
||||||
cc->cc_rpn_lsb = buf[2];
|
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:
|
case UMP_CC_NRPN_MSB:
|
||||||
|
ret = fill_rpn(cc, midi2, true);
|
||||||
cc->nrpn_set = 1;
|
cc->nrpn_set = 1;
|
||||||
cc->cc_nrpn_msb = buf[2];
|
cc->cc_nrpn_msb = buf[2];
|
||||||
return 0; // skip
|
return ret;
|
||||||
case UMP_CC_NRPN_LSB:
|
case UMP_CC_NRPN_LSB:
|
||||||
|
ret = fill_rpn(cc, midi2, true);
|
||||||
cc->nrpn_set = 1;
|
cc->nrpn_set = 1;
|
||||||
cc->cc_nrpn_lsb = buf[2];
|
cc->cc_nrpn_lsb = buf[2];
|
||||||
return 0; // skip
|
return ret;
|
||||||
case UMP_CC_DATA:
|
case UMP_CC_DATA:
|
||||||
|
cc->cc_data_msb_set = 1;
|
||||||
cc->cc_data_msb = buf[2];
|
cc->cc_data_msb = buf[2];
|
||||||
return 0; // skip
|
return fill_rpn(cc, midi2, false);
|
||||||
case UMP_CC_BANK_SELECT:
|
case UMP_CC_BANK_SELECT:
|
||||||
cc->bank_set = 1;
|
cc->bank_set = 1;
|
||||||
cc->cc_bank_msb = buf[2];
|
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];
|
cc->cc_bank_lsb = buf[2];
|
||||||
return 0; // skip
|
return 0; // skip
|
||||||
case UMP_CC_DATA_LSB:
|
case UMP_CC_DATA_LSB:
|
||||||
|
cc->cc_data_lsb_set = 1;
|
||||||
cc->cc_data_lsb = buf[2];
|
cc->cc_data_lsb = buf[2];
|
||||||
if (cc->rpn_set || cc->nrpn_set)
|
return fill_rpn(cc, midi2, false);
|
||||||
fill_rpn(cc, midi2);
|
|
||||||
else
|
|
||||||
return 0; // skip
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
midi2->cc.index = buf[1];
|
midi2->cc.index = buf[1];
|
||||||
midi2->cc.data = upscale_7_to_32bit(buf[2]);
|
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.
|
// overrun. Actual device can skip more, then this module stops the packet streaming.
|
||||||
#define IR_JUMBO_PAYLOAD_MAX_SKIP_CYCLES 5
|
#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
|
* amdtp_stream_init - initialize an AMDTP stream structure
|
||||||
* @s: the AMDTP stream to initialize
|
* @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->flags = flags;
|
||||||
s->context = ERR_PTR(-1);
|
s->context = ERR_PTR(-1);
|
||||||
mutex_init(&s->mutex);
|
mutex_init(&s->mutex);
|
||||||
|
INIT_WORK(&s->period_work, pcm_period_work);
|
||||||
s->packet_index = 0;
|
s->packet_index = 0;
|
||||||
|
|
||||||
init_waitqueue_head(&s->ready_wait);
|
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)
|
void amdtp_stream_pcm_prepare(struct amdtp_stream *s)
|
||||||
{
|
{
|
||||||
|
cancel_work_sync(&s->period_work);
|
||||||
s->pcm_buffer_pointer = 0;
|
s->pcm_buffer_pointer = 0;
|
||||||
s->pcm_period_pointer = 0;
|
s->pcm_period_pointer = 0;
|
||||||
}
|
}
|
||||||
|
@ -611,19 +615,21 @@ static void update_pcm_pointers(struct amdtp_stream *s,
|
||||||
// The program in user process should periodically check the status of intermediate
|
// 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
|
// buffer associated to PCM substream to process PCM frames in the buffer, instead
|
||||||
// of receiving notification of period elapsed by poll wait.
|
// of receiving notification of period elapsed by poll wait.
|
||||||
if (!pcm->runtime->no_period_wakeup) {
|
if (!pcm->runtime->no_period_wakeup)
|
||||||
if (in_softirq()) {
|
queue_work(system_highpri_wq, &s->period_work);
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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,
|
static int queue_packet(struct amdtp_stream *s, struct fw_iso_packet *params,
|
||||||
bool sched_irq)
|
bool sched_irq)
|
||||||
{
|
{
|
||||||
|
@ -1849,11 +1855,14 @@ unsigned long amdtp_domain_stream_pcm_pointer(struct amdtp_domain *d,
|
||||||
{
|
{
|
||||||
struct amdtp_stream *irq_target = d->irq_target;
|
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)) {
|
if (irq_target && amdtp_stream_running(irq_target)) {
|
||||||
// In software IRQ context, the call causes dead-lock to disable the tasklet
|
// use wq to prevent AB/BA deadlock competition for
|
||||||
// synchronously.
|
// substream lock:
|
||||||
if (!in_softirq())
|
// 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);
|
fw_iso_context_flush_completions(irq_target->context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1909,6 +1918,7 @@ static void amdtp_stream_stop(struct amdtp_stream *s)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cancel_work_sync(&s->period_work);
|
||||||
fw_iso_context_stop(s->context);
|
fw_iso_context_stop(s->context);
|
||||||
fw_iso_context_destroy(s->context);
|
fw_iso_context_destroy(s->context);
|
||||||
s->context = ERR_PTR(-1);
|
s->context = ERR_PTR(-1);
|
||||||
|
|
|
@ -191,6 +191,7 @@ struct amdtp_stream {
|
||||||
|
|
||||||
/* For a PCM substream processing. */
|
/* For a PCM substream processing. */
|
||||||
struct snd_pcm_substream *pcm;
|
struct snd_pcm_substream *pcm;
|
||||||
|
struct work_struct period_work;
|
||||||
snd_pcm_uframes_t pcm_buffer_pointer;
|
snd_pcm_uframes_t pcm_buffer_pointer;
|
||||||
unsigned int pcm_period_pointer;
|
unsigned int pcm_period_pointer;
|
||||||
unsigned int pcm_frame_multiplier;
|
unsigned int pcm_frame_multiplier;
|
||||||
|
|
|
@ -28,7 +28,7 @@
|
||||||
#else
|
#else
|
||||||
#define AZX_DCAPS_I915_COMPONENT 0 /* NOP */
|
#define AZX_DCAPS_I915_COMPONENT 0 /* NOP */
|
||||||
#endif
|
#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_CTX_WORKAROUND (1 << 15) /* X-Fi workaround */
|
||||||
#define AZX_DCAPS_POSFIX_LPIB (1 << 16) /* Use LPIB as default */
|
#define AZX_DCAPS_POSFIX_LPIB (1 << 16) /* Use LPIB as default */
|
||||||
#define AZX_DCAPS_AMD_WORKAROUND (1 << 17) /* AMD-specific workaround */
|
#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);
|
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
|
* snd_hda_gen_parse_auto_config - Parse the given BIOS configuration and
|
||||||
* set up the hda_gen_spec
|
* 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 snd_hda_gen_add_micmute_led_cdev(struct hda_codec *codec,
|
||||||
int (*callback)(struct led_classdev *,
|
int (*callback)(struct led_classdev *,
|
||||||
enum led_brightness));
|
enum led_brightness));
|
||||||
|
bool snd_hda_gen_shutup_speakers(struct hda_codec *codec);
|
||||||
|
|
||||||
#endif /* __SOUND_HDA_GENERIC_H */
|
#endif /* __SOUND_HDA_GENERIC_H */
|
||||||
|
|
|
@ -40,6 +40,7 @@
|
||||||
|
|
||||||
#ifdef CONFIG_X86
|
#ifdef CONFIG_X86
|
||||||
/* for snoop control */
|
/* for snoop control */
|
||||||
|
#include <linux/dma-map-ops.h>
|
||||||
#include <asm/set_memory.h>
|
#include <asm/set_memory.h>
|
||||||
#include <asm/cpufeature.h>
|
#include <asm/cpufeature.h>
|
||||||
#endif
|
#endif
|
||||||
|
@ -306,7 +307,7 @@ enum {
|
||||||
|
|
||||||
/* quirks for ATI HDMI with snoop off */
|
/* quirks for ATI HDMI with snoop off */
|
||||||
#define AZX_DCAPS_PRESET_ATI_HDMI_NS \
|
#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 */
|
/* quirks for AMD SB */
|
||||||
#define AZX_DCAPS_PRESET_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)
|
if (chip->driver_caps & AZX_DCAPS_SNOOP_OFF)
|
||||||
snoop = false;
|
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;
|
chip->snoop = snoop;
|
||||||
if (!snoop) {
|
if (!snoop) {
|
||||||
dev_info(chip->card->dev, "Force to non-snoop mode\n");
|
dev_info(chip->card->dev, "Force to non-snoop mode\n");
|
||||||
|
|
|
@ -21,12 +21,6 @@
|
||||||
#include "hda_jack.h"
|
#include "hda_jack.h"
|
||||||
#include "hda_generic.h"
|
#include "hda_generic.h"
|
||||||
|
|
||||||
enum {
|
|
||||||
CX_HEADSET_NOPRESENT = 0,
|
|
||||||
CX_HEADSET_PARTPRESENT,
|
|
||||||
CX_HEADSET_ALLPRESENT,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct conexant_spec {
|
struct conexant_spec {
|
||||||
struct hda_gen_spec gen;
|
struct hda_gen_spec gen;
|
||||||
|
|
||||||
|
@ -48,7 +42,6 @@ struct conexant_spec {
|
||||||
unsigned int gpio_led;
|
unsigned int gpio_led;
|
||||||
unsigned int gpio_mute_led_mask;
|
unsigned int gpio_mute_led_mask;
|
||||||
unsigned int gpio_mic_led_mask;
|
unsigned int gpio_mic_led_mask;
|
||||||
unsigned int headset_present_flag;
|
|
||||||
bool is_cx8070_sn6140;
|
bool is_cx8070_sn6140;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -212,6 +205,8 @@ static void cx_auto_shutdown(struct hda_codec *codec)
|
||||||
{
|
{
|
||||||
struct conexant_spec *spec = codec->spec;
|
struct conexant_spec *spec = codec->spec;
|
||||||
|
|
||||||
|
snd_hda_gen_shutup_speakers(codec);
|
||||||
|
|
||||||
/* Turn the problematic codec into D3 to avoid spurious noises
|
/* Turn the problematic codec into D3 to avoid spurious noises
|
||||||
from the internal speaker during (and after) reboot */
|
from the internal speaker during (and after) reboot */
|
||||||
cx_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, false);
|
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;
|
unsigned int mic_present;
|
||||||
struct conexant_spec *spec = codec->spec;
|
|
||||||
|
|
||||||
/* In cx8070 and sn6140, the node 16 can only be config to headphone or disabled,
|
/* 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.
|
* the node 19 can only be config to microphone or disabled.
|
||||||
* Check hp&mic tag to process headset pulgin&plugout.
|
* 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_present = snd_hda_codec_read(codec, 0x19, 0, AC_VERB_GET_PIN_SENSE, 0x0);
|
||||||
mic_tag = snd_hda_codec_read(codec, 0x19, 0, AC_VERB_GET_UNSOLICITED_RESPONSE, 0x0);
|
if (!(mic_present & AC_PINSENSE_PRESENCE)) /* mic plugout */
|
||||||
if ((phone_tag & (res >> AC_UNSOL_RES_TAG_SHIFT)) ||
|
snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20);
|
||||||
(mic_tag & (res >> AC_UNSOL_RES_TAG_SHIFT))) {
|
else
|
||||||
phone_present = snd_hda_codec_read(codec, 0x16, 0, AC_VERB_GET_PIN_SENSE, 0x0);
|
cx_process_headset_plugin(codec);
|
||||||
if (!(phone_present & AC_PINSENSE_PRESENCE)) {/* headphone plugout */
|
|
||||||
spec->headset_present_flag = CX_HEADSET_NOPRESENT;
|
|
||||||
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)) {
|
|
||||||
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)
|
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,
|
.build_pcms = snd_hda_gen_build_pcms,
|
||||||
.init = cx_auto_init,
|
.init = cx_auto_init,
|
||||||
.free = cx_auto_free,
|
.free = cx_auto_free,
|
||||||
.unsol_event = cx_jack_unsol_event,
|
.unsol_event = snd_hda_jack_unsol_event,
|
||||||
.suspend = cx_auto_suspend,
|
.suspend = cx_auto_suspend,
|
||||||
.check_power_status = snd_hda_gen_check_power_status,
|
.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 0x14f11f86:
|
||||||
case 0x14f11f87:
|
case 0x14f11f87:
|
||||||
spec->is_cx8070_sn6140 = true;
|
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;
|
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, 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, 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, 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, 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, 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),
|
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_FR, /* right front */
|
||||||
SNDRV_CHMAP_FC, /* center front */
|
SNDRV_CHMAP_FC, /* center front */
|
||||||
SNDRV_CHMAP_LFE, /* LFE */
|
SNDRV_CHMAP_LFE, /* LFE */
|
||||||
SNDRV_CHMAP_SL, /* left surround */
|
SNDRV_CHMAP_RL, /* left surround */
|
||||||
SNDRV_CHMAP_SR, /* right surround */
|
SNDRV_CHMAP_RR, /* right surround */
|
||||||
SNDRV_CHMAP_FLC, /* left of center */
|
SNDRV_CHMAP_FLC, /* left of center */
|
||||||
SNDRV_CHMAP_FRC, /* right of center */
|
SNDRV_CHMAP_FRC, /* right of center */
|
||||||
SNDRV_CHMAP_RC, /* surround */
|
SNDRV_CHMAP_RC, /* surround */
|
||||||
|
|
Loading…
Reference in a new issue