Commit 6b779f8a authored by Linus Torvalds's avatar Linus Torvalds

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
parents 29b4a699 478689b5
...@@ -13,6 +13,7 @@ struct ump_cvt_to_ump_bank { ...@@ -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 @@ ...@@ -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 { ...@@ -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 { ...@@ -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, ...@@ -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, ...@@ -789,28 +789,45 @@ static int paf_ev_to_ump_midi2(const struct snd_seq_event *event,
return 1; 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 */ /* 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, 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) { 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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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++;
xbuf++;
}
/* 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; status = UMP_SYSEX_STATUS_SINGLE;
len--; else
}
} else {
if (xbuf[len - 1] == UMP_MIDI1_MSG_SYSEX_END) {
status = UMP_SYSEX_STATUS_END; status = UMP_SYSEX_STATUS_END;
len--; len--;
} else { finished = true;
status = UMP_SYSEX_STATUS_CONTINUE;
}
} }
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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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 @@ ...@@ -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, ...@@ -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); ...@@ -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, ...@@ -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, ...@@ -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) ...@@ -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 { ...@@ -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 @@ ...@@ -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) ...@@ -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, ...@@ -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 @@ ...@@ -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 { ...@@ -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) ...@@ -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 @@ ...@@ -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 { ...@@ -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) ...@@ -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) ...@@ -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 = { ...@@ -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) ...@@ -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[] = { ...@@ -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, ...@@ -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 */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment