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 {
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;
}
static void reset_rpn(struct ump_cvt_to_ump_bank *cc)
{
cc->rpn_set = 0;
cc->nrpn_set = 0;
cc->cc_rpn_msb = cc->cc_rpn_lsb = 0;
cc->cc_data_msb = cc->cc_data_lsb = 0;
cc->cc_data_msb_set = cc->cc_data_lsb_set = 0;
}
/* set up the MIDI2 RPN/NRPN packet data from the parsed info */
static void fill_rpn(struct snd_seq_ump_midi2_bank *cc,
union snd_ump_midi2_msg *data,
unsigned char channel)
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;
xbuf++;
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;
len--;
}
} else {
if (xbuf[len - 1] == UMP_MIDI1_MSG_SYSEX_END) {
else
status = UMP_SYSEX_STATUS_END;
len--;
} else {
status = UMP_SYSEX_STATUS_CONTINUE;
}
len--;
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,19 +615,21 @@ static void update_pcm_pointers(struct amdtp_stream *s,
// The program in user process should periodically check the status of intermediate
// buffer associated to PCM substream to process PCM frames in the buffer, instead
// of receiving notification of period elapsed by poll wait.
if (!pcm->runtime->no_period_wakeup) {
if (in_softirq()) {
// In software IRQ context for 1394 OHCI.
snd_pcm_period_elapsed(pcm);
} else {
// In process context of ALSA PCM application under acquired lock of
// PCM substream.
snd_pcm_period_elapsed_under_stream_lock(pcm);
}
}
if (!pcm->runtime->no_period_wakeup)
queue_work(system_highpri_wq, &s->period_work);
}
}
static void pcm_period_work(struct work_struct *work)
{
struct amdtp_stream *s = container_of(work, struct amdtp_stream,
period_work);
struct snd_pcm_substream *pcm = READ_ONCE(s->pcm);
if (pcm)
snd_pcm_period_elapsed(pcm);
}
static int queue_packet(struct amdtp_stream *s, struct fw_iso_packet *params,
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;
// 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;
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);
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);
else
cx_process_headset_plugin(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,
.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 */
......
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