summaryrefslogtreecommitdiffstats
path: root/sound/pci/hda
diff options
context:
space:
mode:
Diffstat (limited to 'sound/pci/hda')
-rw-r--r--sound/pci/hda/Kconfig20
-rw-r--r--sound/pci/hda/hda_auto_parser.c5
-rw-r--r--sound/pci/hda/hda_auto_parser.h10
-rw-r--r--sound/pci/hda/hda_beep.c82
-rw-r--r--sound/pci/hda/hda_beep.h5
-rw-r--r--sound/pci/hda/hda_codec.c118
-rw-r--r--sound/pci/hda/hda_codec.h7
-rw-r--r--sound/pci/hda/hda_intel.c57
-rw-r--r--sound/pci/hda/hda_jack.c102
-rw-r--r--sound/pci/hda/hda_jack.h1
-rw-r--r--sound/pci/hda/hda_local.h4
-rw-r--r--sound/pci/hda/hda_proc.c17
-rw-r--r--sound/pci/hda/patch_analog.c2
-rw-r--r--sound/pci/hda/patch_cirrus.c2
-rw-r--r--sound/pci/hda/patch_conexant.c8
-rw-r--r--sound/pci/hda/patch_hdmi.c310
-rw-r--r--sound/pci/hda/patch_realtek.c356
-rw-r--r--sound/pci/hda/patch_sigmatel.c9
-rw-r--r--sound/pci/hda/patch_via.c2
19 files changed, 750 insertions, 367 deletions
diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig
index 163b6b5de3e..194d625c1f8 100644
--- a/sound/pci/hda/Kconfig
+++ b/sound/pci/hda/Kconfig
@@ -53,15 +53,14 @@ config SND_HDA_INPUT_BEEP
driver. This interface is used to generate digital beeps.
config SND_HDA_INPUT_BEEP_MODE
- int "Digital beep registration mode (0=off, 1=on, 2=mute sw on/off)"
+ int "Digital beep registration mode (0=off, 1=on)"
depends on SND_HDA_INPUT_BEEP=y
default "1"
- range 0 2
+ range 0 1
help
Set 0 to disable the digital beep interface for HD-audio by default.
Set 1 to always enable the digital beep interface for HD-audio by
- default. Set 2 to control the beep device registration to input
- layer using a "Beep Switch" in mixer applications.
+ default.
config SND_HDA_INPUT_JACK
bool "Support jack plugging notification via input layer"
@@ -97,19 +96,6 @@ config SND_HDA_CODEC_REALTEK
snd-hda-codec-realtek.
This module is automatically loaded at probing.
-config SND_HDA_ENABLE_REALTEK_QUIRKS
- bool "Build static quirks for Realtek codecs"
- depends on SND_HDA_CODEC_REALTEK
- default y
- help
- Say Y here to build the static quirks codes for Realtek codecs.
- If you need the "model" preset that the default BIOS auto-parser
- can't handle, turn this option on.
-
- If your device works with model=auto option, basically you don't
- need the quirk code. By turning this off, you can reduce the
- module size quite a lot.
-
config SND_HDA_CODEC_ANALOG
bool "Build Analog Device HD-audio codec support"
default y
diff --git a/sound/pci/hda/hda_auto_parser.c b/sound/pci/hda/hda_auto_parser.c
index 6e9ef3e2509..647218d69f6 100644
--- a/sound/pci/hda/hda_auto_parser.c
+++ b/sound/pci/hda/hda_auto_parser.c
@@ -618,7 +618,6 @@ int snd_hda_gen_add_verbs(struct hda_gen_spec *spec,
const struct hda_verb *list)
{
const struct hda_verb **v;
- snd_array_init(&spec->verbs, sizeof(struct hda_verb *), 8);
v = snd_array_new(&spec->verbs);
if (!v)
return -ENOMEM;
@@ -728,7 +727,7 @@ void snd_hda_pick_fixup(struct hda_codec *codec,
models++;
}
}
- if (id < 0) {
+ if (id < 0 && quirk) {
q = snd_pci_quirk_lookup(codec->bus->pci, quirk);
if (q) {
id = q->value;
@@ -737,7 +736,7 @@ void snd_hda_pick_fixup(struct hda_codec *codec,
#endif
}
}
- if (id < 0) {
+ if (id < 0 && quirk) {
for (q = quirk; q->subvendor; q++) {
unsigned int vendorid =
q->subdevice | (q->subvendor << 16);
diff --git a/sound/pci/hda/hda_auto_parser.h b/sound/pci/hda/hda_auto_parser.h
index 2a7889dfbd1..632ad0ad300 100644
--- a/sound/pci/hda/hda_auto_parser.h
+++ b/sound/pci/hda/hda_auto_parser.h
@@ -157,4 +157,14 @@ void snd_hda_pick_fixup(struct hda_codec *codec,
const struct snd_pci_quirk *quirk,
const struct hda_fixup *fixlist);
+static inline void snd_hda_gen_init(struct hda_gen_spec *spec)
+{
+ snd_array_init(&spec->verbs, sizeof(struct hda_verb *), 8);
+}
+
+static inline void snd_hda_gen_free(struct hda_gen_spec *spec)
+{
+ snd_array_free(&spec->verbs);
+}
+
#endif /* __SOUND_HDA_AUTO_PARSER_H */
diff --git a/sound/pci/hda/hda_beep.c b/sound/pci/hda/hda_beep.c
index 60738e52b8f..0bc2315b181 100644
--- a/sound/pci/hda/hda_beep.c
+++ b/sound/pci/hda/hda_beep.c
@@ -162,50 +162,20 @@ static int snd_hda_do_attach(struct hda_beep *beep)
return 0;
}
-static void snd_hda_do_register(struct work_struct *work)
-{
- struct hda_beep *beep =
- container_of(work, struct hda_beep, register_work);
-
- mutex_lock(&beep->mutex);
- if (beep->enabled && !beep->dev)
- snd_hda_do_attach(beep);
- mutex_unlock(&beep->mutex);
-}
-
-static void snd_hda_do_unregister(struct work_struct *work)
-{
- struct hda_beep *beep =
- container_of(work, struct hda_beep, unregister_work.work);
-
- mutex_lock(&beep->mutex);
- if (!beep->enabled && beep->dev)
- snd_hda_do_detach(beep);
- mutex_unlock(&beep->mutex);
-}
-
int snd_hda_enable_beep_device(struct hda_codec *codec, int enable)
{
struct hda_beep *beep = codec->beep;
- enable = !!enable;
- if (beep == NULL)
+ if (!beep)
return 0;
+ enable = !!enable;
if (beep->enabled != enable) {
beep->enabled = enable;
if (!enable) {
+ cancel_work_sync(&beep->beep_work);
/* turn off beep */
snd_hda_codec_write(beep->codec, beep->nid, 0,
AC_VERB_SET_BEEP_CONTROL, 0);
}
- if (beep->mode == HDA_BEEP_MODE_SWREG) {
- if (enable) {
- cancel_delayed_work(&beep->unregister_work);
- schedule_work(&beep->register_work);
- } else {
- schedule_delayed_work(&beep->unregister_work,
- HZ);
- }
- }
return 1;
}
return 0;
@@ -215,6 +185,7 @@ EXPORT_SYMBOL_HDA(snd_hda_enable_beep_device);
int snd_hda_attach_beep_device(struct hda_codec *codec, int nid)
{
struct hda_beep *beep;
+ int err;
if (!snd_hda_get_bool_hint(codec, "beep"))
return 0; /* disabled explicitly by hints */
@@ -232,21 +203,16 @@ int snd_hda_attach_beep_device(struct hda_codec *codec, int nid)
beep->nid = nid;
beep->codec = codec;
- beep->mode = codec->beep_mode;
codec->beep = beep;
- INIT_WORK(&beep->register_work, &snd_hda_do_register);
- INIT_DELAYED_WORK(&beep->unregister_work, &snd_hda_do_unregister);
INIT_WORK(&beep->beep_work, &snd_hda_generate_beep);
mutex_init(&beep->mutex);
- if (beep->mode == HDA_BEEP_MODE_ON) {
- int err = snd_hda_do_attach(beep);
- if (err < 0) {
- kfree(beep);
- codec->beep = NULL;
- return err;
- }
+ err = snd_hda_do_attach(beep);
+ if (err < 0) {
+ kfree(beep);
+ codec->beep = NULL;
+ return err;
}
return 0;
@@ -257,8 +223,6 @@ void snd_hda_detach_beep_device(struct hda_codec *codec)
{
struct hda_beep *beep = codec->beep;
if (beep) {
- cancel_work_sync(&beep->register_work);
- cancel_delayed_work(&beep->unregister_work);
if (beep->dev)
snd_hda_do_detach(beep);
codec->beep = NULL;
@@ -266,3 +230,31 @@ void snd_hda_detach_beep_device(struct hda_codec *codec)
}
}
EXPORT_SYMBOL_HDA(snd_hda_detach_beep_device);
+
+/* get/put callbacks for beep mute mixer switches */
+int snd_hda_mixer_amp_switch_get_beep(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct hda_beep *beep = codec->beep;
+ if (beep) {
+ ucontrol->value.integer.value[0] =
+ ucontrol->value.integer.value[1] =
+ beep->enabled;
+ return 0;
+ }
+ return snd_hda_mixer_amp_switch_get(kcontrol, ucontrol);
+}
+EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_get_beep);
+
+int snd_hda_mixer_amp_switch_put_beep(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct hda_beep *beep = codec->beep;
+ if (beep)
+ snd_hda_enable_beep_device(codec,
+ *ucontrol->value.integer.value);
+ return snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
+}
+EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_put_beep);
diff --git a/sound/pci/hda/hda_beep.h b/sound/pci/hda/hda_beep.h
index 55f0647458c..4dc6933bc65 100644
--- a/sound/pci/hda/hda_beep.h
+++ b/sound/pci/hda/hda_beep.h
@@ -26,21 +26,16 @@
#define HDA_BEEP_MODE_OFF 0
#define HDA_BEEP_MODE_ON 1
-#define HDA_BEEP_MODE_SWREG 2
/* beep information */
struct hda_beep {
struct input_dev *dev;
struct hda_codec *codec;
- unsigned int mode;
char phys[32];
int tone;
hda_nid_t nid;
unsigned int enabled:1;
- unsigned int request_enable:1;
unsigned int linear_tone:1; /* linear tone for IDT/STAC codec */
- struct work_struct register_work; /* registration work */
- struct delayed_work unregister_work; /* unregistration work */
struct work_struct beep_work; /* scheduled task for beep event */
struct mutex mutex;
};
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index 41ca803a1ff..88a9c20eb7a 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -1184,6 +1184,7 @@ static void snd_hda_codec_free(struct hda_codec *codec)
{
if (!codec)
return;
+ snd_hda_jack_tbl_clear(codec);
restore_init_pincfgs(codec);
#ifdef CONFIG_SND_HDA_POWER_SAVE
cancel_delayed_work(&codec->power_work);
@@ -1192,6 +1193,7 @@ static void snd_hda_codec_free(struct hda_codec *codec)
list_del(&codec->list);
snd_array_free(&codec->mixers);
snd_array_free(&codec->nids);
+ snd_array_free(&codec->cvt_setups);
snd_array_free(&codec->conn_lists);
snd_array_free(&codec->spdif_out);
codec->bus->caddr_tbl[codec->addr] = NULL;
@@ -2333,6 +2335,8 @@ int snd_hda_codec_reset(struct hda_codec *codec)
/* free only driver_pins so that init_pins + user_pins are restored */
snd_array_free(&codec->driver_pins);
restore_pincfgs(codec);
+ snd_array_free(&codec->cvt_setups);
+ snd_array_free(&codec->spdif_out);
codec->num_pcms = 0;
codec->pcm_info = NULL;
codec->preset = NULL;
@@ -2672,25 +2676,6 @@ int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol,
}
EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_put);
-#ifdef CONFIG_SND_HDA_INPUT_BEEP
-/**
- * snd_hda_mixer_amp_switch_put_beep - Put callback for a beep AMP switch
- *
- * This function calls snd_hda_enable_beep_device(), which behaves differently
- * depending on beep_mode option.
- */
-int snd_hda_mixer_amp_switch_put_beep(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- long *valp = ucontrol->value.integer.value;
-
- snd_hda_enable_beep_device(codec, *valp);
- return snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
-}
-EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_put_beep);
-#endif /* CONFIG_SND_HDA_INPUT_BEEP */
-
/*
* bound volume controls
*
@@ -3505,22 +3490,52 @@ void snd_hda_codec_set_power_to_all(struct hda_codec *codec, hda_nid_t fg,
EXPORT_SYMBOL_HDA(snd_hda_codec_set_power_to_all);
/*
+ * supported power states check
+ */
+static bool snd_hda_codec_get_supported_ps(struct hda_codec *codec, hda_nid_t fg,
+ unsigned int power_state)
+{
+ int sup = snd_hda_param_read(codec, fg, AC_PAR_POWER_STATE);
+
+ if (sup < 0)
+ return false;
+ if (sup & power_state)
+ return true;
+ else
+ return false;
+}
+
+/*
* set power state of the codec
*/
static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg,
unsigned int power_state)
{
+ int count;
+ unsigned int state;
+
if (codec->patch_ops.set_power_state) {
codec->patch_ops.set_power_state(codec, fg, power_state);
return;
}
/* this delay seems necessary to avoid click noise at power-down */
- if (power_state == AC_PWRST_D3)
- msleep(100);
- snd_hda_codec_read(codec, fg, 0, AC_VERB_SET_POWER_STATE,
- power_state);
- snd_hda_codec_set_power_to_all(codec, fg, power_state, true);
+ if (power_state == AC_PWRST_D3) {
+ /* transition time less than 10ms for power down */
+ bool epss = snd_hda_codec_get_supported_ps(codec, fg, AC_PWRST_EPSS);
+ msleep(epss ? 10 : 100);
+ }
+
+ /* repeat power states setting at most 10 times*/
+ for (count = 0; count < 10; count++) {
+ snd_hda_codec_read(codec, fg, 0, AC_VERB_SET_POWER_STATE,
+ power_state);
+ snd_hda_codec_set_power_to_all(codec, fg, power_state, true);
+ state = snd_hda_codec_read(codec, fg, 0,
+ AC_VERB_GET_POWER_STATE, 0);
+ if (!(state & AC_PWRST_ERROR))
+ break;
+ }
}
#ifdef CONFIG_SND_HDA_HWDEP
@@ -3541,7 +3556,7 @@ static inline void hda_exec_init_verbs(struct hda_codec *codec) {}
static void hda_call_codec_suspend(struct hda_codec *codec)
{
if (codec->patch_ops.suspend)
- codec->patch_ops.suspend(codec, PMSG_SUSPEND);
+ codec->patch_ops.suspend(codec);
hda_cleanup_all_streams(codec);
hda_set_power_state(codec,
codec->afg ? codec->afg : codec->mfg,
@@ -4393,20 +4408,19 @@ void snd_hda_update_power_acct(struct hda_codec *codec)
codec->power_jiffies += delta;
}
-/**
- * snd_hda_power_up - Power-up the codec
- * @codec: HD-audio codec
- *
- * Increment the power-up counter and power up the hardware really when
- * not turned on yet.
- */
-void snd_hda_power_up(struct hda_codec *codec)
+/* Transition to powered up, if wait_power_down then wait for a pending
+ * transition to D3 to complete. A pending D3 transition is indicated
+ * with power_transition == -1. */
+static void __snd_hda_power_up(struct hda_codec *codec, bool wait_power_down)
{
struct hda_bus *bus = codec->bus;
spin_lock(&codec->power_lock);
codec->power_count++;
- if (codec->power_on || codec->power_transition > 0) {
+ /* Return if power_on or transitioning to power_on, unless currently
+ * powering down. */
+ if ((codec->power_on || codec->power_transition > 0) &&
+ !(wait_power_down && codec->power_transition < 0)) {
spin_unlock(&codec->power_lock);
return;
}
@@ -4415,6 +4429,13 @@ void snd_hda_power_up(struct hda_codec *codec)
cancel_delayed_work_sync(&codec->power_work);
spin_lock(&codec->power_lock);
+ /* If the power down delayed work was cancelled above before starting,
+ * then there is no need to go through power up here.
+ */
+ if (codec->power_on) {
+ spin_unlock(&codec->power_lock);
+ return;
+ }
trace_hda_power_up(codec);
snd_hda_update_power_acct(codec);
codec->power_on = 1;
@@ -4430,8 +4451,37 @@ void snd_hda_power_up(struct hda_codec *codec)
codec->power_transition = 0;
spin_unlock(&codec->power_lock);
}
+
+/**
+ * snd_hda_power_up - Power-up the codec
+ * @codec: HD-audio codec
+ *
+ * Increment the power-up counter and power up the hardware really when
+ * not turned on yet.
+ */
+void snd_hda_power_up(struct hda_codec *codec)
+{
+ __snd_hda_power_up(codec, false);
+}
EXPORT_SYMBOL_HDA(snd_hda_power_up);
+/**
+ * snd_hda_power_up_d3wait - Power-up the codec after waiting for any pending
+ * D3 transition to complete. This differs from snd_hda_power_up() when
+ * power_transition == -1. snd_hda_power_up sees this case as a nop,
+ * snd_hda_power_up_d3wait waits for the D3 transition to complete then powers
+ * back up.
+ * @codec: HD-audio codec
+ *
+ * Cancel any power down operation hapenning on the work queue, then power up.
+ */
+void snd_hda_power_up_d3wait(struct hda_codec *codec)
+{
+ /* This will cancel and wait for pending power_work to complete. */
+ __snd_hda_power_up(codec, true);
+}
+EXPORT_SYMBOL_HDA(snd_hda_power_up_d3wait);
+
#define power_save(codec) \
((codec)->bus->power_save ? *(codec)->bus->power_save : 0)
diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h
index 4fc3960c859..c422d330ca5 100644
--- a/sound/pci/hda/hda_codec.h
+++ b/sound/pci/hda/hda_codec.h
@@ -323,6 +323,9 @@ enum {
#define AC_PWRST_D1 0x01
#define AC_PWRST_D2 0x02
#define AC_PWRST_D3 0x03
+#define AC_PWRST_ERROR (1<<8)
+#define AC_PWRST_CLK_STOP_OK (1<<9)
+#define AC_PWRST_SETTING_RESET (1<<10)
/* Processing capabilies */
#define AC_PCAP_BENIGN (1<<0)
@@ -703,7 +706,7 @@ struct hda_codec_ops {
void (*set_power_state)(struct hda_codec *codec, hda_nid_t fg,
unsigned int power_state);
#ifdef CONFIG_PM
- int (*suspend)(struct hda_codec *codec, pm_message_t state);
+ int (*suspend)(struct hda_codec *codec);
int (*resume)(struct hda_codec *codec);
#endif
#ifdef CONFIG_SND_HDA_POWER_SAVE
@@ -1056,10 +1059,12 @@ const char *snd_hda_get_jack_location(u32 cfg);
*/
#ifdef CONFIG_SND_HDA_POWER_SAVE
void snd_hda_power_up(struct hda_codec *codec);
+void snd_hda_power_up_d3wait(struct hda_codec *codec);
void snd_hda_power_down(struct hda_codec *codec);
void snd_hda_update_power_acct(struct hda_codec *codec);
#else
static inline void snd_hda_power_up(struct hda_codec *codec) {}
+static inline void snd_hda_power_up_d3wait(struct hda_codec *codec) {}
static inline void snd_hda_power_down(struct hda_codec *codec) {}
#endif
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index 02763827dde..c8aced182fd 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -72,7 +72,7 @@ static int enable_msi = -1;
static char *patch[SNDRV_CARDS];
#endif
#ifdef CONFIG_SND_HDA_INPUT_BEEP
-static int beep_mode[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] =
+static bool beep_mode[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] =
CONFIG_SND_HDA_INPUT_BEEP_MODE};
#endif
@@ -103,9 +103,9 @@ module_param_array(patch, charp, NULL, 0444);
MODULE_PARM_DESC(patch, "Patch file for Intel HD audio interface.");
#endif
#ifdef CONFIG_SND_HDA_INPUT_BEEP
-module_param_array(beep_mode, int, NULL, 0444);
+module_param_array(beep_mode, bool, NULL, 0444);
MODULE_PARM_DESC(beep_mode, "Select HDA Beep registration mode "
- "(0=off, 1=on, 2=mute switch on/off) (default=1).");
+ "(0=off, 1=on) (default=1).");
#endif
#ifdef CONFIG_SND_HDA_POWER_SAVE
@@ -151,6 +151,7 @@ MODULE_SUPPORTED_DEVICE("{{Intel, ICH6},"
"{Intel, CPT},"
"{Intel, PPT},"
"{Intel, LPT},"
+ "{Intel, HPT},"
"{Intel, PBG},"
"{Intel, SCH},"
"{ATI, SB450},"
@@ -535,6 +536,7 @@ enum {
#define AZX_DCAPS_BUFSIZE (1 << 21) /* no buffer size alignment */
#define AZX_DCAPS_ALIGN_BUFSIZE (1 << 22) /* buffer size alignment */
#define AZX_DCAPS_4K_BDLE_BOUNDARY (1 << 23) /* BDLE in 4k boundary */
+#define AZX_DCAPS_POSFIX_COMBO (1 << 24) /* Use COMBO as default */
/* quirks for ATI SB / AMD Hudson */
#define AZX_DCAPS_PRESET_ATI_SB \
@@ -1766,7 +1768,7 @@ static int azx_pcm_open(struct snd_pcm_substream *substream)
buff_step);
snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
buff_step);
- snd_hda_power_up(apcm->codec);
+ snd_hda_power_up_d3wait(apcm->codec);
err = hinfo->ops.open(hinfo, apcm->codec, substream);
if (err < 0) {
azx_release_device(azx_dev);
@@ -2403,9 +2405,10 @@ static void azx_power_notify(struct hda_bus *bus)
* power management
*/
-static int azx_suspend(struct pci_dev *pci, pm_message_t state)
+static int azx_suspend(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct pci_dev *pci = to_pci_dev(dev);
+ struct snd_card *card = dev_get_drvdata(dev);
struct azx *chip = card->private_data;
struct azx_pcm *p;
@@ -2424,13 +2427,14 @@ static int azx_suspend(struct pci_dev *pci, pm_message_t state)
pci_disable_msi(chip->pci);
pci_disable_device(pci);
pci_save_state(pci);
- pci_set_power_state(pci, pci_choose_state(pci, state));
+ pci_set_power_state(pci, PCI_D3hot);
return 0;
}
-static int azx_resume(struct pci_dev *pci)
+static int azx_resume(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct pci_dev *pci = to_pci_dev(dev);
+ struct snd_card *card = dev_get_drvdata(dev);
struct azx *chip = card->private_data;
pci_set_power_state(pci, PCI_D0);
@@ -2455,6 +2459,12 @@ static int azx_resume(struct pci_dev *pci)
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
return 0;
}
+static SIMPLE_DEV_PM_OPS(azx_pm, azx_suspend, azx_resume);
+#define AZX_PM_OPS &azx_pm
+#else
+#define azx_suspend(dev)
+#define azx_resume(dev)
+#define AZX_PM_OPS NULL
#endif /* CONFIG_PM */
@@ -2521,13 +2531,13 @@ static void azx_vs_set_state(struct pci_dev *pci,
disabled ? "Disabling" : "Enabling",
pci_name(chip->pci));
if (disabled) {
- azx_suspend(pci, PMSG_FREEZE);
+ azx_suspend(&pci->dev);
chip->disabled = true;
snd_hda_lock_devices(chip->bus);
} else {
snd_hda_unlock_devices(chip->bus);
chip->disabled = false;
- azx_resume(pci);
+ azx_resume(&pci->dev);
}
}
}
@@ -2731,6 +2741,10 @@ static int __devinit check_position_fix(struct azx *chip, int fix)
snd_printd(SFX "Using LPIB position fix\n");
return POS_FIX_LPIB;
}
+ if (chip->driver_caps & AZX_DCAPS_POSFIX_COMBO) {
+ snd_printd(SFX "Using COMBO position fix\n");
+ return POS_FIX_COMBO;
+ }
return POS_FIX_AUTO;
}
@@ -3243,7 +3257,7 @@ static DEFINE_PCI_DEVICE_TABLE(azx_ids) = {
/* CPT */
{ PCI_DEVICE(0x8086, 0x1c20),
.driver_data = AZX_DRIVER_PCH | AZX_DCAPS_SCH_SNOOP |
- AZX_DCAPS_BUFSIZE },
+ AZX_DCAPS_BUFSIZE | AZX_DCAPS_POSFIX_COMBO },
/* PBG */
{ PCI_DEVICE(0x8086, 0x1d20),
.driver_data = AZX_DRIVER_PCH | AZX_DCAPS_SCH_SNOOP |
@@ -3251,11 +3265,15 @@ static DEFINE_PCI_DEVICE_TABLE(azx_ids) = {
/* Panther Point */
{ PCI_DEVICE(0x8086, 0x1e20),
.driver_data = AZX_DRIVER_PCH | AZX_DCAPS_SCH_SNOOP |
- AZX_DCAPS_BUFSIZE},
+ AZX_DCAPS_BUFSIZE | AZX_DCAPS_POSFIX_COMBO },
/* Lynx Point */
{ PCI_DEVICE(0x8086, 0x8c20),
.driver_data = AZX_DRIVER_PCH | AZX_DCAPS_SCH_SNOOP |
- AZX_DCAPS_BUFSIZE},
+ AZX_DCAPS_BUFSIZE | AZX_DCAPS_POSFIX_COMBO },
+ /* Haswell */
+ { PCI_DEVICE(0x8086, 0x0c0c),
+ .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_SCH_SNOOP |
+ AZX_DCAPS_BUFSIZE | AZX_DCAPS_POSFIX_COMBO },
/* SCH */
{ PCI_DEVICE(0x8086, 0x811b),
.driver_data = AZX_DRIVER_SCH | AZX_DCAPS_SCH_SNOOP |
@@ -3341,6 +3359,10 @@ static DEFINE_PCI_DEVICE_TABLE(azx_ids) = {
/* VIA VT8251/VT8237A */
{ PCI_DEVICE(0x1106, 0x3288),
.driver_data = AZX_DRIVER_VIA | AZX_DCAPS_POSFIX_VIA },
+ /* VIA GFX VT7122/VX900 */
+ { PCI_DEVICE(0x1106, 0x9170), .driver_data = AZX_DRIVER_GENERIC },
+ /* VIA GFX VT6122/VX11 */
+ { PCI_DEVICE(0x1106, 0x9140), .driver_data = AZX_DRIVER_GENERIC },
/* SIS966 */
{ PCI_DEVICE(0x1039, 0x7502), .driver_data = AZX_DRIVER_SIS },
/* ULI M5461 */
@@ -3398,10 +3420,9 @@ static struct pci_driver azx_driver = {
.id_table = azx_ids,
.probe = azx_probe,
.remove = __devexit_p(azx_remove),
-#ifdef CONFIG_PM
- .suspend = azx_suspend,
- .resume = azx_resume,
-#endif
+ .driver = {
+ .pm = AZX_PM_OPS,
+ },
};
module_pci_driver(azx_driver);
diff --git a/sound/pci/hda/hda_jack.c b/sound/pci/hda/hda_jack.c
index 2dd1c113a4c..aaccc0236bd 100644
--- a/sound/pci/hda/hda_jack.c
+++ b/sound/pci/hda/hda_jack.c
@@ -127,10 +127,15 @@ void snd_hda_jack_tbl_clear(struct hda_codec *codec)
static void jack_detect_update(struct hda_codec *codec,
struct hda_jack_tbl *jack)
{
- if (jack->jack_dirty || !jack->jack_detect) {
+ if (!jack->jack_dirty)
+ return;
+
+ if (jack->phantom_jack)
+ jack->pin_sense = AC_PINSENSE_PRESENCE;
+ else
jack->pin_sense = read_pin_sense(codec, jack->nid);
- jack->jack_dirty = 0;
- }
+
+ jack->jack_dirty = 0;
}
/**
@@ -264,8 +269,8 @@ static void hda_free_jack_priv(struct snd_jack *jack)
* This assigns a jack-detection kctl to the given pin. The kcontrol
* will have the given name and index.
*/
-int snd_hda_jack_add_kctl(struct hda_codec *codec, hda_nid_t nid,
- const char *name, int idx)
+static int __snd_hda_jack_add_kctl(struct hda_codec *codec, hda_nid_t nid,
+ const char *name, int idx, bool phantom_jack)
{
struct hda_jack_tbl *jack;
struct snd_kcontrol *kctl;
@@ -283,47 +288,81 @@ int snd_hda_jack_add_kctl(struct hda_codec *codec, hda_nid_t nid,
if (err < 0)
return err;
jack->kctl = kctl;
+ jack->phantom_jack = !!phantom_jack;
+
state = snd_hda_jack_detect(codec, nid);
snd_kctl_jack_report(codec->bus->card, kctl, state);
#ifdef CONFIG_SND_HDA_INPUT_JACK
- jack->type = get_input_jack_type(codec, nid);
- err = snd_jack_new(codec->bus->card, name, jack->type, &jack->jack);
- if (err < 0)
- return err;
- jack->jack->private_data = jack;
- jack->jack->private_free = hda_free_jack_priv;
- snd_jack_report(jack->jack, state ? jack->type : 0);
+ if (!phantom_jack) {
+ jack->type = get_input_jack_type(codec, nid);
+ err = snd_jack_new(codec->bus->card, name, jack->type,
+ &jack->jack);
+ if (err < 0)
+ return err;
+ jack->jack->private_data = jack;
+ jack->jack->private_free = hda_free_jack_priv;
+ snd_jack_report(jack->jack, state ? jack->type : 0);
+ }
#endif
return 0;
}
+
+int snd_hda_jack_add_kctl(struct hda_codec *codec, hda_nid_t nid,
+ const char *name, int idx)
+{
+ return __snd_hda_jack_add_kctl(codec, nid, name, idx, false);
+}
EXPORT_SYMBOL_HDA(snd_hda_jack_add_kctl);
+/* get the unique index number for the given kctl name */
+static int get_unique_index(struct hda_codec *codec, const char *name, int idx)
+{
+ struct hda_jack_tbl *jack;
+ int i, len = strlen(name);
+ again:
+ jack = codec->jacktbl.list;
+ for (i = 0; i < codec->jacktbl.used; i++, jack++) {
+ /* jack->kctl.id contains "XXX Jack" name string with index */
+ if (jack->kctl &&
+ !strncmp(name, jack->kctl->id.name, len) &&
+ !strcmp(" Jack", jack->kctl->id.name + len) &&
+ jack->kctl->id.index == idx) {
+ idx++;
+ goto again;
+ }
+ }
+ return idx;
+}
+
static int add_jack_kctl(struct hda_codec *codec, hda_nid_t nid,
- const struct auto_pin_cfg *cfg,
- char *lastname, int *lastidx)
+ const struct auto_pin_cfg *cfg)
{
unsigned int def_conf, conn;
char name[44];
int idx, err;
+ bool phantom_jack;
if (!nid)
return 0;
- if (!is_jack_detectable(codec, nid))
- return 0;
def_conf = snd_hda_codec_get_pincfg(codec, nid);
conn = get_defcfg_connect(def_conf);
- if (conn != AC_JACK_PORT_COMPLEX)
+ if (conn == AC_JACK_PORT_NONE)
return 0;
+ phantom_jack = (conn != AC_JACK_PORT_COMPLEX) ||
+ !is_jack_detectable(codec, nid);
snd_hda_get_pin_label(codec, nid, cfg, name, sizeof(name), &idx);
- if (!strcmp(name, lastname) && idx == *lastidx)
- idx++;
- strncpy(lastname, name, 44);
- *lastidx = idx;
- err = snd_hda_jack_add_kctl(codec, nid, name, idx);
+ if (phantom_jack)
+ /* Example final name: "Internal Mic Phantom Jack" */
+ strncat(name, " Phantom", sizeof(name) - strlen(name) - 1);
+ idx = get_unique_index(codec, name, idx);
+ err = __snd_hda_jack_add_kctl(codec, nid, name, idx, phantom_jack);
if (err < 0)
return err;
- return snd_hda_jack_detect_enable(codec, nid, 0);
+
+ if (!phantom_jack)
+ return snd_hda_jack_detect_enable(codec, nid, 0);
+ return 0;
}
/**
@@ -333,42 +372,41 @@ int snd_hda_jack_add_kctls(struct hda_codec *codec,
const struct auto_pin_cfg *cfg)
{
const hda_nid_t *p;
- int i, err, lastidx = 0;
- char lastname[44] = "";
+ int i, err;
for (i = 0, p = cfg->line_out_pins; i < cfg->line_outs; i++, p++) {
- err = add_jack_kctl(codec, *p, cfg, lastname, &lastidx);
+ err = add_jack_kctl(codec, *p, cfg);
if (err < 0)
return err;
}
for (i = 0, p = cfg->hp_pins; i < cfg->hp_outs; i++, p++) {
if (*p == *cfg->line_out_pins) /* might be duplicated */
break;
- err = add_jack_kctl(codec, *p, cfg, lastname, &lastidx);
+ err = add_jack_kctl(codec, *p, cfg);
if (err < 0)
return err;
}
for (i = 0, p = cfg->speaker_pins; i < cfg->speaker_outs; i++, p++) {
if (*p == *cfg->line_out_pins) /* might be duplicated */
break;
- err = add_jack_kctl(codec, *p, cfg, lastname, &lastidx);
+ err = add_jack_kctl(codec, *p, cfg);
if (err < 0)
return err;
}
for (i = 0; i < cfg->num_inputs; i++) {
- err = add_jack_kctl(codec, cfg->inputs[i].pin, cfg, lastname, &lastidx);
+ err = add_jack_kctl(codec, cfg->inputs[i].pin, cfg);
if (err < 0)
return err;
}
for (i = 0, p = cfg->dig_out_pins; i < cfg->dig_outs; i++, p++) {
- err = add_jack_kctl(codec, *p, cfg, lastname, &lastidx);
+ err = add_jack_kctl(codec, *p, cfg);
if (err < 0)
return err;
}
- err = add_jack_kctl(codec, cfg->dig_in_pin, cfg, lastname, &lastidx);
+ err = add_jack_kctl(codec, cfg->dig_in_pin, cfg);
if (err < 0)
return err;
- err = add_jack_kctl(codec, cfg->mono_out_pin, cfg, lastname, &lastidx);
+ err = add_jack_kctl(codec, cfg->mono_out_pin, cfg);
if (err < 0)
return err;
return 0;
diff --git a/sound/pci/hda/hda_jack.h b/sound/pci/hda/hda_jack.h
index 8ae52465ec5..a9803da633c 100644
--- a/sound/pci/hda/hda_jack.h
+++ b/sound/pci/hda/hda_jack.h
@@ -23,6 +23,7 @@ struct hda_jack_tbl {
unsigned int pin_sense; /* cached pin-sense value */
unsigned int jack_detect:1; /* capable of jack-detection? */
unsigned int jack_dirty:1; /* needs to update? */
+ unsigned int phantom_jack:1; /* a fixed, always present port? */
struct snd_kcontrol *kctl; /* assigned kctl for jack-detection */
#ifdef CONFIG_SND_HDA_INPUT_JACK
int type;
diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h
index 9a096a8e0fc..1b4c12941ba 100644
--- a/sound/pci/hda/hda_local.h
+++ b/sound/pci/hda/hda_local.h
@@ -89,7 +89,7 @@
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \
.subdevice = HDA_SUBDEV_AMP_FLAG, \
.info = snd_hda_mixer_amp_switch_info, \
- .get = snd_hda_mixer_amp_switch_get, \
+ .get = snd_hda_mixer_amp_switch_get_beep, \
.put = snd_hda_mixer_amp_switch_put_beep, \
.private_value = HDA_COMPOSE_AMP_VAL(nid, channel, xindex, direction) }
#else
@@ -121,6 +121,8 @@ int snd_hda_mixer_amp_switch_get(struct snd_kcontrol *kcontrol,
int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
#ifdef CONFIG_SND_HDA_INPUT_BEEP
+int snd_hda_mixer_amp_switch_get_beep(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
int snd_hda_mixer_amp_switch_put_beep(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
#endif
diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c
index e59e2f059b6..7e46258fc70 100644
--- a/sound/pci/hda/hda_proc.c
+++ b/sound/pci/hda/hda_proc.c
@@ -426,10 +426,10 @@ static void print_digital_conv(struct snd_info_buffer *buffer,
static const char *get_pwr_state(u32 state)
{
- static const char * const buf[4] = {
- "D0", "D1", "D2", "D3"
+ static const char * const buf[] = {
+ "D0", "D1", "D2", "D3", "D3cold"
};
- if (state < 4)
+ if (state < ARRAY_SIZE(buf))
return buf[state];
return "UNKNOWN";
}
@@ -451,14 +451,21 @@ static void print_power_state(struct snd_info_buffer *buffer,
int sup = snd_hda_param_read(codec, nid, AC_PAR_POWER_STATE);
int pwr = snd_hda_codec_read(codec, nid, 0,
AC_VERB_GET_POWER_STATE, 0);
- if (sup)
+ if (sup != -1)
snd_iprintf(buffer, " Power states: %s\n",
bits_names(sup, names, ARRAY_SIZE(names)));
- snd_iprintf(buffer, " Power: setting=%s, actual=%s\n",
+ snd_iprintf(buffer, " Power: setting=%s, actual=%s",
get_pwr_state(pwr & AC_PWRST_SETTING),
get_pwr_state((pwr & AC_PWRST_ACTUAL) >>
AC_PWRST_ACTUAL_SHIFT));
+ if (pwr & AC_PWRST_ERROR)
+ snd_iprintf(buffer, ", Error");
+ if (pwr & AC_PWRST_CLK_STOP_OK)
+ snd_iprintf(buffer, ", Clock-stop-OK");
+ if (pwr & AC_PWRST_SETTING_RESET)
+ snd_iprintf(buffer, ", Setting-reset");
+ snd_iprintf(buffer, "\n");
}
static void print_unsol_cap(struct snd_info_buffer *buffer,
diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c
index d8b2d6dee98..0208fa121e5 100644
--- a/sound/pci/hda/patch_analog.c
+++ b/sound/pci/hda/patch_analog.c
@@ -642,7 +642,7 @@ static void ad198x_free(struct hda_codec *codec)
}
#ifdef CONFIG_PM
-static int ad198x_suspend(struct hda_codec *codec, pm_message_t state)
+static int ad198x_suspend(struct hda_codec *codec)
{
ad198x_shutup(codec);
return 0;
diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c
index 9647ed4d792..0c4c1a61b37 100644
--- a/sound/pci/hda/patch_cirrus.c
+++ b/sound/pci/hda/patch_cirrus.c
@@ -1892,7 +1892,7 @@ static int cs421x_parse_auto_config(struct hda_codec *codec)
Manage PDREF, when transitioning to D3hot
(DAC,ADC) -> D3, PDREF=1, AFG->D3
*/
-static int cs421x_suspend(struct hda_codec *codec, pm_message_t state)
+static int cs421x_suspend(struct hda_codec *codec)
{
struct cs_spec *spec = codec->spec;
unsigned int coef;
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c
index 172370b3793..14361184ae1 100644
--- a/sound/pci/hda/patch_conexant.c
+++ b/sound/pci/hda/patch_conexant.c
@@ -445,8 +445,10 @@ static int conexant_init(struct hda_codec *codec)
static void conexant_free(struct hda_codec *codec)
{
+ struct conexant_spec *spec = codec->spec;
+ snd_hda_gen_free(&spec->gen);
snd_hda_detach_beep_device(codec);
- kfree(codec->spec);
+ kfree(spec);
}
static const struct snd_kcontrol_new cxt_capture_mixers[] = {
@@ -552,7 +554,7 @@ static int conexant_build_controls(struct hda_codec *codec)
}
#ifdef CONFIG_SND_HDA_POWER_SAVE
-static int conexant_suspend(struct hda_codec *codec, pm_message_t state)
+static int conexant_suspend(struct hda_codec *codec)
{
snd_hda_shutup_pins(codec);
return 0;
@@ -4466,6 +4468,7 @@ static const struct snd_pci_quirk cxt5066_fixups[] = {
SND_PCI_QUIRK(0x17aa, 0x21ce, "Lenovo T420", CXT_PINCFG_LENOVO_TP410),
SND_PCI_QUIRK(0x17aa, 0x21cf, "Lenovo T520", CXT_PINCFG_LENOVO_TP410),
SND_PCI_QUIRK(0x17aa, 0x3975, "Lenovo U300s", CXT_FIXUP_STEREO_DMIC),
+ SND_PCI_QUIRK(0x17aa, 0x397b, "Lenovo S205", CXT_FIXUP_STEREO_DMIC),
{}
};
@@ -4497,6 +4500,7 @@ static int patch_conexant_auto(struct hda_codec *codec)
if (!spec)
return -ENOMEM;
codec->spec = spec;
+ snd_hda_gen_init(&spec->gen);
switch (codec->vendor_id) {
case 0x14f15045:
diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c
index ad319d4dc32..641408dc28c 100644
--- a/sound/pci/hda/patch_hdmi.c
+++ b/sound/pci/hda/patch_hdmi.c
@@ -85,7 +85,7 @@ struct hdmi_spec {
* Non-generic ATI/NVIDIA specific
*/
struct hda_multi_out multiout;
- const struct hda_pcm_stream *pcm_playback;
+ struct hda_pcm_stream pcm_playback;
};
@@ -787,7 +787,7 @@ static void hdmi_non_intrinsic_event(struct hda_codec *codec, unsigned int res)
int cp_ready = !!(res & AC_UNSOL_RES_CP_READY);
printk(KERN_INFO
- "HDMI CP event: CODEC=%d PIN=%d SUBTAG=0x%x CP_STATE=%d CP_READY=%d\n",
+ "HDMI CP event: CODEC=%d TAG=%d SUBTAG=0x%x CP_STATE=%d CP_READY=%d\n",
codec->addr,
tag,
subtag,
@@ -876,7 +876,6 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo,
struct hdmi_spec_per_pin *per_pin;
struct hdmi_eld *eld;
struct hdmi_spec_per_cvt *per_cvt = NULL;
- int pinctl;
/* Validate hinfo */
pin_idx = hinfo_to_pin_index(spec, hinfo);
@@ -912,11 +911,6 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo,
snd_hda_codec_write(codec, per_pin->pin_nid, 0,
AC_VERB_SET_CONNECT_SEL,
mux_idx);
- pinctl = snd_hda_codec_read(codec, per_pin->pin_nid, 0,
- AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
- snd_hda_codec_write(codec, per_pin->pin_nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- pinctl | PIN_OUT);
snd_hda_spdif_ctls_assign(codec, pin_idx, per_cvt->cvt_nid);
/* Initially set the converter's capabilities */
@@ -1153,11 +1147,17 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
struct hdmi_spec *spec = codec->spec;
int pin_idx = hinfo_to_pin_index(spec, hinfo);
hda_nid_t pin_nid = spec->pins[pin_idx].pin_nid;
+ int pinctl;
hdmi_set_channel_count(codec, cvt_nid, substream->runtime->channels);
hdmi_setup_audio_infoframe(codec, pin_idx, substream);
+ pinctl = snd_hda_codec_read(codec, pin_nid, 0,
+ AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+ snd_hda_codec_write(codec, pin_nid, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, pinctl | PIN_OUT);
+
return hdmi_setup_stream(codec, cvt_nid, pin_nid, stream_tag, format);
}
@@ -1277,23 +1277,34 @@ static int generic_hdmi_build_controls(struct hda_codec *codec)
return 0;
}
-static int generic_hdmi_init(struct hda_codec *codec)
+static int generic_hdmi_init_per_pins(struct hda_codec *codec)
{
struct hdmi_spec *spec = codec->spec;
int pin_idx;
for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
struct hdmi_spec_per_pin *per_pin = &spec->pins[pin_idx];
- hda_nid_t pin_nid = per_pin->pin_nid;
struct hdmi_eld *eld = &per_pin->sink_eld;
- hdmi_init_pin(codec, pin_nid);
- snd_hda_jack_detect_enable(codec, pin_nid, pin_nid);
-
per_pin->codec = codec;
INIT_DELAYED_WORK(&per_pin->work, hdmi_repoll_eld);
snd_hda_eld_proc_new(codec, eld, pin_idx);
}
+ return 0;
+}
+
+static int generic_hdmi_init(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec = codec->spec;
+ int pin_idx;
+
+ for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
+ struct hdmi_spec_per_pin *per_pin = &spec->pins[pin_idx];
+ hda_nid_t pin_nid = per_pin->pin_nid;
+
+ hdmi_init_pin(codec, pin_nid);
+ snd_hda_jack_detect_enable(codec, pin_nid, pin_nid);
+ }
snd_hda_jack_report_sync(codec);
return 0;
}
@@ -1338,6 +1349,7 @@ static int patch_generic_hdmi(struct hda_codec *codec)
return -EINVAL;
}
codec->patch_ops = generic_hdmi_patch_ops;
+ generic_hdmi_init_per_pins(codec);
init_channel_allocations();
@@ -1352,45 +1364,65 @@ static int simple_playback_build_pcms(struct hda_codec *codec)
{
struct hdmi_spec *spec = codec->spec;
struct hda_pcm *info = spec->pcm_rec;
- int i;
+ unsigned int chans;
+ struct hda_pcm_stream *pstr;
- codec->num_pcms = spec->num_cvts;
+ codec->num_pcms = 1;
codec->pcm_info = info;
- for (i = 0; i < codec->num_pcms; i++, info++) {
- unsigned int chans;
- struct hda_pcm_stream *pstr;
-
- chans = get_wcaps(codec, spec->cvts[i].cvt_nid);
- chans = get_wcaps_channels(chans);
+ chans = get_wcaps(codec, spec->cvts[0].cvt_nid);
+ chans = get_wcaps_channels(chans);
- info->name = get_hdmi_pcm_name(i);
- info->pcm_type = HDA_PCM_TYPE_HDMI;
- pstr = &info->stream[SNDRV_PCM_STREAM_PLAYBACK];
- snd_BUG_ON(!spec->pcm_playback);
- *pstr = *spec->pcm_playback;
- pstr->nid = spec->cvts[i].cvt_nid;
- if (pstr->channels_max <= 2 && chans && chans <= 16)
- pstr->channels_max = chans;
- }
+ info->name = get_hdmi_pcm_name(0);
+ info->pcm_type = HDA_PCM_TYPE_HDMI;
+ pstr = &info->stream[SNDRV_PCM_STREAM_PLAYBACK];
+ *pstr = spec->pcm_playback;
+ pstr->nid = spec->cvts[0].cvt_nid;
+ if (pstr->channels_max <= 2 && chans && chans <= 16)
+ pstr->channels_max = chans;
return 0;
}
+/* unsolicited event for jack sensing */
+static void simple_hdmi_unsol_event(struct hda_codec *codec,
+ unsigned int res)
+{
+ snd_hda_jack_set_dirty_all(codec);
+ snd_hda_jack_report_sync(codec);
+}
+
+/* generic_hdmi_build_jack can be used for simple_hdmi, too,
+ * as long as spec->pins[] is set correctly
+ */
+#define simple_hdmi_build_jack generic_hdmi_build_jack
+
static int simple_playback_build_controls(struct hda_codec *codec)
{
struct hdmi_spec *spec = codec->spec;
int err;
- int i;
- for (i = 0; i < codec->num_pcms; i++) {
- err = snd_hda_create_spdif_out_ctls(codec,
- spec->cvts[i].cvt_nid,
- spec->cvts[i].cvt_nid);
- if (err < 0)
- return err;
- }
+ err = snd_hda_create_spdif_out_ctls(codec,
+ spec->cvts[0].cvt_nid,
+ spec->cvts[0].cvt_nid);
+ if (err < 0)
+ return err;
+ return simple_hdmi_build_jack(codec, 0);
+}
+static int simple_playback_init(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec = codec->spec;
+ hda_nid_t pin = spec->pins[0].pin_nid;
+
+ snd_hda_codec_write(codec, pin, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+ /* some codecs require to unmute the pin */
+ if (get_wcaps(codec, pin) & AC_WCAP_OUT_AMP)
+ snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+ AMP_OUT_UNMUTE);
+ snd_hda_jack_detect_enable(codec, pin, pin);
+ snd_hda_jack_report_sync(codec);
return 0;
}
@@ -1418,7 +1450,15 @@ static const hda_nid_t nvhdmi_con_nids_7x[4] = {
0x6, 0x8, 0xa, 0xc,
};
-static const struct hda_verb nvhdmi_basic_init_7x[] = {
+static const struct hda_verb nvhdmi_basic_init_7x_2ch[] = {
+ /* set audio protect on */
+ { 0x1, Nv_VERB_SET_Audio_Protection_On, 0x1},
+ /* enable digital output on pin widget */
+ { 0x5, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
+ {} /* terminator */
+};
+
+static const struct hda_verb nvhdmi_basic_init_7x_8ch[] = {
/* set audio protect on */
{ 0x1, Nv_VERB_SET_Audio_Protection_On, 0x1},
/* enable digital output on pin widget */
@@ -1446,9 +1486,15 @@ static const struct hda_verb nvhdmi_basic_init_7x[] = {
(SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE)
#endif
-static int nvhdmi_7x_init(struct hda_codec *codec)
+static int nvhdmi_7x_init_2ch(struct hda_codec *codec)
{
- snd_hda_sequence_write(codec, nvhdmi_basic_init_7x);
+ snd_hda_sequence_write(codec, nvhdmi_basic_init_7x_2ch);
+ return 0;
+}
+
+static int nvhdmi_7x_init_8ch(struct hda_codec *codec)
+{
+ snd_hda_sequence_write(codec, nvhdmi_basic_init_7x_8ch);
return 0;
}
@@ -1524,6 +1570,50 @@ static int simple_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
stream_tag, format, substream);
}
+static const struct hda_pcm_stream simple_pcm_playback = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+ .ops = {
+ .open = simple_playback_pcm_open,
+ .close = simple_playback_pcm_close,
+ .prepare = simple_playback_pcm_prepare
+ },
+};
+
+static const struct hda_codec_ops simple_hdmi_patch_ops = {
+ .build_controls = simple_playback_build_controls,
+ .build_pcms = simple_playback_build_pcms,
+ .init = simple_playback_init,
+ .free = simple_playback_free,
+ .unsol_event = simple_hdmi_unsol_event,
+};
+
+static int patch_simple_hdmi(struct hda_codec *codec,
+ hda_nid_t cvt_nid, hda_nid_t pin_nid)
+{
+ struct hdmi_spec *spec;
+
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (!spec)
+ return -ENOMEM;
+
+ codec->spec = spec;
+
+ spec->multiout.num_dacs = 0; /* no analog */
+ spec->multiout.max_channels = 2;
+ spec->multiout.dig_out_nid = cvt_nid;
+ spec->num_cvts = 1;
+ spec->num_pins = 1;
+ spec->cvts[0].cvt_nid = cvt_nid;
+ spec->pins[0].pin_nid = pin_nid;
+ spec->pcm_playback = simple_pcm_playback;
+
+ codec->patch_ops = simple_hdmi_patch_ops;
+
+ return 0;
+}
+
static void nvhdmi_8ch_7x_set_info_frame_parameters(struct hda_codec *codec,
int channels)
{
@@ -1696,54 +1786,20 @@ static const struct hda_pcm_stream nvhdmi_pcm_playback_8ch_7x = {
},
};
-static const struct hda_pcm_stream nvhdmi_pcm_playback_2ch = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- .nid = nvhdmi_master_con_nid_7x,
- .rates = SUPPORTED_RATES,
- .maxbps = SUPPORTED_MAXBPS,
- .formats = SUPPORTED_FORMATS,
- .ops = {
- .open = simple_playback_pcm_open,
- .close = simple_playback_pcm_close,
- .prepare = simple_playback_pcm_prepare
- },
-};
-
-static const struct hda_codec_ops nvhdmi_patch_ops_8ch_7x = {
- .build_controls = simple_playback_build_controls,
- .build_pcms = simple_playback_build_pcms,
- .init = nvhdmi_7x_init,
- .free = simple_playback_free,
-};
-
-static const struct hda_codec_ops nvhdmi_patch_ops_2ch = {
- .build_controls = simple_playback_build_controls,
- .build_pcms = simple_playback_build_pcms,
- .init = nvhdmi_7x_init,
- .free = simple_playback_free,
-};
-
static int patch_nvhdmi_2ch(struct hda_codec *codec)
{
struct hdmi_spec *spec;
+ int err = patch_simple_hdmi(codec, nvhdmi_master_con_nid_7x,
+ nvhdmi_master_pin_nid_7x);
+ if (err < 0)
+ return err;
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
-
- codec->spec = spec;
-
- spec->multiout.num_dacs = 0; /* no analog */
- spec->multiout.max_channels = 2;
- spec->multiout.dig_out_nid = nvhdmi_master_con_nid_7x;
- spec->num_cvts = 1;
- spec->cvts[0].cvt_nid = nvhdmi_master_con_nid_7x;
- spec->pcm_playback = &nvhdmi_pcm_playback_2ch;
-
- codec->patch_ops = nvhdmi_patch_ops_2ch;
-
+ codec->patch_ops.init = nvhdmi_7x_init_2ch;
+ /* override the PCM rates, etc, as the codec doesn't give full list */
+ spec = codec->spec;
+ spec->pcm_playback.rates = SUPPORTED_RATES;
+ spec->pcm_playback.maxbps = SUPPORTED_MAXBPS;
+ spec->pcm_playback.formats = SUPPORTED_FORMATS;
return 0;
}
@@ -1751,13 +1807,12 @@ static int patch_nvhdmi_8ch_7x(struct hda_codec *codec)
{
struct hdmi_spec *spec;
int err = patch_nvhdmi_2ch(codec);
-
if (err < 0)
return err;
spec = codec->spec;
spec->multiout.max_channels = 8;
- spec->pcm_playback = &nvhdmi_pcm_playback_8ch_7x;
- codec->patch_ops = nvhdmi_patch_ops_8ch_7x;
+ spec->pcm_playback = nvhdmi_pcm_playback_8ch_7x;
+ codec->patch_ops.init = nvhdmi_7x_init_8ch;
/* Initialize the audio infoframe channel mask and checksum to something
* valid */
@@ -1801,69 +1856,26 @@ static int atihdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
return 0;
}
-static const struct hda_pcm_stream atihdmi_pcm_digital_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- .nid = ATIHDMI_CVT_NID,
- .ops = {
- .open = simple_playback_pcm_open,
- .close = simple_playback_pcm_close,
- .prepare = atihdmi_playback_pcm_prepare
- },
-};
-
-static const struct hda_verb atihdmi_basic_init[] = {
- /* enable digital output on pin widget */
- { 0x03, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- {} /* terminator */
-};
-
-static int atihdmi_init(struct hda_codec *codec)
+static int patch_atihdmi(struct hda_codec *codec)
{
- struct hdmi_spec *spec = codec->spec;
-
- snd_hda_sequence_write(codec, atihdmi_basic_init);
- /* SI codec requires to unmute the pin */
- if (get_wcaps(codec, spec->pins[0].pin_nid) & AC_WCAP_OUT_AMP)
- snd_hda_codec_write(codec, spec->pins[0].pin_nid, 0,
- AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_OUT_UNMUTE);
+ struct hdmi_spec *spec;
+ int err = patch_simple_hdmi(codec, ATIHDMI_CVT_NID, ATIHDMI_PIN_NID);
+ if (err < 0)
+ return err;
+ spec = codec->spec;
+ spec->pcm_playback.ops.prepare = atihdmi_playback_pcm_prepare;
return 0;
}
-static const struct hda_codec_ops atihdmi_patch_ops = {
- .build_controls = simple_playback_build_controls,
- .build_pcms = simple_playback_build_pcms,
- .init = atihdmi_init,
- .free = simple_playback_free,
-};
+/* VIA HDMI Implementation */
+#define VIAHDMI_CVT_NID 0x02 /* audio converter1 */
+#define VIAHDMI_PIN_NID 0x03 /* HDMI output pin1 */
-
-static int patch_atihdmi(struct hda_codec *codec)
+static int patch_via_hdmi(struct hda_codec *codec)
{
- struct hdmi_spec *spec;
-
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
-
- codec->spec = spec;
-
- spec->multiout.num_dacs = 0; /* no analog */
- spec->multiout.max_channels = 2;
- spec->multiout.dig_out_nid = ATIHDMI_CVT_NID;
- spec->num_cvts = 1;
- spec->cvts[0].cvt_nid = ATIHDMI_CVT_NID;
- spec->pins[0].pin_nid = ATIHDMI_PIN_NID;
- spec->pcm_playback = &atihdmi_pcm_digital_playback;
-
- codec->patch_ops = atihdmi_patch_ops;
-
- return 0;
+ return patch_simple_hdmi(codec, VIAHDMI_CVT_NID, VIAHDMI_PIN_NID);
}
-
/*
* patch entries
*/
@@ -1902,8 +1914,13 @@ static const struct hda_codec_preset snd_hda_preset_hdmi[] = {
{ .id = 0x10de0042, .name = "GPU 42 HDMI/DP", .patch = patch_generic_hdmi },
{ .id = 0x10de0043, .name = "GPU 43 HDMI/DP", .patch = patch_generic_hdmi },
{ .id = 0x10de0044, .name = "GPU 44 HDMI/DP", .patch = patch_generic_hdmi },
+{ .id = 0x10de0051, .name = "GPU 51 HDMI/DP", .patch = patch_generic_hdmi },
{ .id = 0x10de0067, .name = "MCP67 HDMI", .patch = patch_nvhdmi_2ch },
{ .id = 0x10de8001, .name = "MCP73 HDMI", .patch = patch_nvhdmi_2ch },
+{ .id = 0x11069f80, .name = "VX900 HDMI/DP", .patch = patch_via_hdmi },
+{ .id = 0x11069f81, .name = "VX900 HDMI/DP", .patch = patch_via_hdmi },
+{ .id = 0x11069f84, .name = "VX11 HDMI/DP", .patch = patch_generic_hdmi },
+{ .id = 0x11069f85, .name = "VX11 HDMI/DP", .patch = patch_generic_hdmi },
{ .id = 0x80860054, .name = "IbexPeak HDMI", .patch = patch_generic_hdmi },
{ .id = 0x80862801, .name = "Bearlake HDMI", .patch = patch_generic_hdmi },
{ .id = 0x80862802, .name = "Cantiga HDMI", .patch = patch_generic_hdmi },
@@ -1911,6 +1928,7 @@ static const struct hda_codec_preset snd_hda_preset_hdmi[] = {
{ .id = 0x80862804, .name = "IbexPeak HDMI", .patch = patch_generic_hdmi },
{ .id = 0x80862805, .name = "CougarPoint HDMI", .patch = patch_generic_hdmi },
{ .id = 0x80862806, .name = "PantherPoint HDMI", .patch = patch_generic_hdmi },
+{ .id = 0x80862807, .name = "Haswell HDMI", .patch = patch_generic_hdmi },
{ .id = 0x80862880, .name = "CedarTrail HDMI", .patch = patch_generic_hdmi },
{ .id = 0x808629fb, .name = "Crestline HDMI", .patch = patch_generic_hdmi },
{} /* terminator */
@@ -1948,8 +1966,13 @@ MODULE_ALIAS("snd-hda-codec-id:10de0041");
MODULE_ALIAS("snd-hda-codec-id:10de0042");
MODULE_ALIAS("snd-hda-codec-id:10de0043");
MODULE_ALIAS("snd-hda-codec-id:10de0044");
+MODULE_ALIAS("snd-hda-codec-id:10de0051");
MODULE_ALIAS("snd-hda-codec-id:10de0067");
MODULE_ALIAS("snd-hda-codec-id:10de8001");
+MODULE_ALIAS("snd-hda-codec-id:11069f80");
+MODULE_ALIAS("snd-hda-codec-id:11069f81");
+MODULE_ALIAS("snd-hda-codec-id:11069f84");
+MODULE_ALIAS("snd-hda-codec-id:11069f85");
MODULE_ALIAS("snd-hda-codec-id:17e80047");
MODULE_ALIAS("snd-hda-codec-id:80860054");
MODULE_ALIAS("snd-hda-codec-id:80862801");
@@ -1958,6 +1981,7 @@ MODULE_ALIAS("snd-hda-codec-id:80862803");
MODULE_ALIAS("snd-hda-codec-id:80862804");
MODULE_ALIAS("snd-hda-codec-id:80862805");
MODULE_ALIAS("snd-hda-codec-id:80862806");
+MODULE_ALIAS("snd-hda-codec-id:80862807");
MODULE_ALIAS("snd-hda-codec-id:80862880");
MODULE_ALIAS("snd-hda-codec-id:808629fb");
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index f8f4906e498..f141395dfee 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -170,10 +170,10 @@ struct alc_spec {
hda_nid_t imux_pins[HDA_MAX_NUM_INPUTS];
unsigned int dyn_adc_idx[HDA_MAX_NUM_INPUTS];
int int_mic_idx, ext_mic_idx, dock_mic_idx; /* for auto-mic */
+ hda_nid_t inv_dmic_pin;
/* hooks */
void (*init_hook)(struct hda_codec *codec);
- void (*unsol_event)(struct hda_codec *codec, unsigned int res);
#ifdef CONFIG_SND_HDA_POWER_SAVE
void (*power_hook)(struct hda_codec *codec);
#endif
@@ -201,6 +201,8 @@ struct alc_spec {
unsigned int vol_in_capsrc:1; /* use capsrc volume (ADC has no vol) */
unsigned int parse_flags; /* passed to snd_hda_parse_pin_defcfg() */
unsigned int shared_mic_hp:1; /* HP/Mic-in sharing */
+ unsigned int inv_dmic_fixup:1; /* has inverted digital-mic workaround */
+ unsigned int inv_dmic_muted:1; /* R-ch of inv d-mic is muted? */
/* auto-mute control */
int automute_mode;
@@ -298,6 +300,39 @@ static inline hda_nid_t get_capsrc(struct alc_spec *spec, int idx)
}
static void call_update_outputs(struct hda_codec *codec);
+static void alc_inv_dmic_sync(struct hda_codec *codec, bool force);
+
+/* for shared I/O, change the pin-control accordingly */
+static void update_shared_mic_hp(struct hda_codec *codec, bool set_as_mic)
+{
+ struct alc_spec *spec = codec->spec;
+ unsigned int val;
+ hda_nid_t pin = spec->autocfg.inputs[1].pin;
+ /* NOTE: this assumes that there are only two inputs, the
+ * first is the real internal mic and the second is HP/mic jack.
+ */
+
+ val = snd_hda_get_default_vref(codec, pin);
+
+ /* This pin does not have vref caps - let's enable vref on pin 0x18
+ instead, as suggested by Realtek */
+ if (val == AC_PINCTL_VREF_HIZ) {
+ const hda_nid_t vref_pin = 0x18;
+ /* Sanity check pin 0x18 */
+ if (get_wcaps_type(get_wcaps(codec, vref_pin)) == AC_WID_PIN &&
+ get_defcfg_connect(snd_hda_codec_get_pincfg(codec, vref_pin)) == AC_JACK_PORT_NONE) {
+ unsigned int vref_val = snd_hda_get_default_vref(codec, vref_pin);
+ if (vref_val != AC_PINCTL_VREF_HIZ)
+ snd_hda_set_pin_ctl(codec, vref_pin, PIN_IN | (set_as_mic ? vref_val : 0));
+ }
+ }
+
+ val = set_as_mic ? val | PIN_IN : PIN_HP;
+ snd_hda_set_pin_ctl(codec, pin, val);
+
+ spec->automute_speaker = !set_as_mic;
+ call_update_outputs(codec);
+}
/* select the given imux item; either unmute exclusively or select the route */
static int alc_mux_select(struct hda_codec *codec, unsigned int adc_idx,
@@ -325,21 +360,8 @@ static int alc_mux_select(struct hda_codec *codec, unsigned int adc_idx,
return 0;
spec->cur_mux[adc_idx] = idx;
- /* for shared I/O, change the pin-control accordingly */
- if (spec->shared_mic_hp) {
- unsigned int val;
- hda_nid_t pin = spec->autocfg.inputs[1].pin;
- /* NOTE: this assumes that there are only two inputs, the
- * first is the real internal mic and the second is HP jack.
- */
- if (spec->cur_mux[adc_idx])
- val = snd_hda_get_default_vref(codec, pin) | PIN_IN;
- else
- val = PIN_HP;
- snd_hda_set_pin_ctl(codec, pin, val);
- spec->automute_speaker = !spec->cur_mux[adc_idx];
- call_update_outputs(codec);
- }
+ if (spec->shared_mic_hp)
+ update_shared_mic_hp(codec, spec->cur_mux[adc_idx]);
if (spec->dyn_adc_switch) {
alc_dyn_adc_pcm_resetup(codec, idx);
@@ -368,6 +390,7 @@ static int alc_mux_select(struct hda_codec *codec, unsigned int adc_idx,
AC_VERB_SET_CONNECT_SEL,
imux->items[idx].index);
}
+ alc_inv_dmic_sync(codec, true);
return 1;
}
@@ -664,7 +687,7 @@ static void alc_update_knob_master(struct hda_codec *codec, hda_nid_t nid)
}
/* unsolicited event for HP jack sensing */
-static void alc_sku_unsol_event(struct hda_codec *codec, unsigned int res)
+static void alc_unsol_event(struct hda_codec *codec, unsigned int res)
{
int action;
@@ -1000,11 +1023,9 @@ static void alc_init_automute(struct hda_codec *codec)
spec->automute_lo = spec->automute_lo_possible;
spec->automute_speaker = spec->automute_speaker_possible;
- if (spec->automute_speaker_possible || spec->automute_lo_possible) {
+ if (spec->automute_speaker_possible || spec->automute_lo_possible)
/* create a control for automute mode */
alc_add_automute_mode_enum(codec);
- spec->unsol_event = alc_sku_unsol_event;
- }
}
/* return the position of NID in the list, or -1 if not found */
@@ -1167,7 +1188,6 @@ static void alc_init_auto_mic(struct hda_codec *codec)
snd_printdd("realtek: Enable auto-mic switch on NID 0x%x/0x%x/0x%x\n",
ext, fixed, dock);
- spec->unsol_event = alc_sku_unsol_event;
}
/* check the availabilities of auto-mute and auto-mic switches */
@@ -1556,14 +1576,14 @@ typedef int (*getput_call_t)(struct snd_kcontrol *kcontrol,
static int alc_cap_getput_caller(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol,
- getput_call_t func, bool check_adc_switch)
+ getput_call_t func, bool is_put)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct alc_spec *spec = codec->spec;
int i, err = 0;
mutex_lock(&codec->control_mutex);
- if (check_adc_switch && spec->dyn_adc_switch) {
+ if (is_put && spec->dyn_adc_switch) {
for (i = 0; i < spec->num_adc_nids; i++) {
kcontrol->private_value =
HDA_COMPOSE_AMP_VAL(spec->adc_nids[i],
@@ -1584,6 +1604,8 @@ static int alc_cap_getput_caller(struct snd_kcontrol *kcontrol,
3, 0, HDA_INPUT);
err = func(kcontrol, ucontrol);
}
+ if (err >= 0 && is_put)
+ alc_inv_dmic_sync(codec, false);
error:
mutex_unlock(&codec->control_mutex);
return err;
@@ -1676,6 +1698,116 @@ DEFINE_CAPMIX_NOSRC(2);
DEFINE_CAPMIX_NOSRC(3);
/*
+ * Inverted digital-mic handling
+ *
+ * First off, it's a bit tricky. The "Inverted Internal Mic Capture Switch"
+ * gives the additional mute only to the right channel of the digital mic
+ * capture stream. This is a workaround for avoiding the almost silence
+ * by summing the stereo stream from some (known to be ForteMedia)
+ * digital mic unit.
+ *
+ * The logic is to call alc_inv_dmic_sync() after each action (possibly)
+ * modifying ADC amp. When the mute flag is set, it mutes the R-channel
+ * without caching so that the cache can still keep the original value.
+ * The cached value is then restored when the flag is set off or any other
+ * than d-mic is used as the current input source.
+ */
+static void alc_inv_dmic_sync(struct hda_codec *codec, bool force)
+{
+ struct alc_spec *spec = codec->spec;
+ int i;
+
+ if (!spec->inv_dmic_fixup)
+ return;
+ if (!spec->inv_dmic_muted && !force)
+ return;
+ for (i = 0; i < spec->num_adc_nids; i++) {
+ int src = spec->dyn_adc_switch ? 0 : i;
+ bool dmic_fixup = false;
+ hda_nid_t nid;
+ int parm, dir, v;
+
+ if (spec->inv_dmic_muted &&
+ spec->imux_pins[spec->cur_mux[src]] == spec->inv_dmic_pin)
+ dmic_fixup = true;
+ if (!dmic_fixup && !force)
+ continue;
+ if (spec->vol_in_capsrc) {
+ nid = spec->capsrc_nids[i];
+ parm = AC_AMP_SET_RIGHT | AC_AMP_SET_OUTPUT;
+ dir = HDA_OUTPUT;
+ } else {
+ nid = spec->adc_nids[i];
+ parm = AC_AMP_SET_RIGHT | AC_AMP_SET_INPUT;
+ dir = HDA_INPUT;
+ }
+ /* we care only right channel */
+ v = snd_hda_codec_amp_read(codec, nid, 1, dir, 0);
+ if (v & 0x80) /* if already muted, we don't need to touch */
+ continue;
+ if (dmic_fixup) /* add mute for d-mic */
+ v |= 0x80;
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+ parm | v);
+ }
+}
+
+static int alc_inv_dmic_sw_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct alc_spec *spec = codec->spec;
+
+ ucontrol->value.integer.value[0] = !spec->inv_dmic_muted;
+ return 0;
+}
+
+static int alc_inv_dmic_sw_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct alc_spec *spec = codec->spec;
+ unsigned int val = !ucontrol->value.integer.value[0];
+
+ if (val == spec->inv_dmic_muted)
+ return 0;
+ spec->inv_dmic_muted = val;
+ alc_inv_dmic_sync(codec, true);
+ return 0;
+}
+
+static const struct snd_kcontrol_new alc_inv_dmic_sw = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .info = snd_ctl_boolean_mono_info,
+ .get = alc_inv_dmic_sw_get,
+ .put = alc_inv_dmic_sw_put,
+};
+
+static int alc_add_inv_dmic_mixer(struct hda_codec *codec, hda_nid_t nid)
+{
+ struct alc_spec *spec = codec->spec;
+ struct snd_kcontrol_new *knew = alc_kcontrol_new(spec);
+ if (!knew)
+ return -ENOMEM;
+ *knew = alc_inv_dmic_sw;
+ knew->name = kstrdup("Inverted Internal Mic Capture Switch", GFP_KERNEL);
+ if (!knew->name)
+ return -ENOMEM;
+ spec->inv_dmic_fixup = 1;
+ spec->inv_dmic_muted = 0;
+ spec->inv_dmic_pin = nid;
+ return 0;
+}
+
+/* typically the digital mic is put at node 0x12 */
+static void alc_fixup_inv_dmic_0x12(struct hda_codec *codec,
+ const struct alc_fixup *fix, int action)
+{
+ if (action == ALC_FIXUP_ACT_PROBE)
+ alc_add_inv_dmic_mixer(codec, 0x12);
+}
+
+/*
* virtual master controls
*/
@@ -1865,13 +1997,31 @@ static int __alc_build_controls(struct hda_codec *codec)
return 0;
}
-static int alc_build_controls(struct hda_codec *codec)
+static int alc_build_jacks(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
+
+ if (spec->shared_mic_hp) {
+ int err;
+ int nid = spec->autocfg.inputs[1].pin;
+ err = snd_hda_jack_add_kctl(codec, nid, "Headphone Mic", 0);
+ if (err < 0)
+ return err;
+ err = snd_hda_jack_detect_enable(codec, nid, 0);
+ if (err < 0)
+ return err;
+ }
+
+ return snd_hda_jack_add_kctls(codec, &spec->autocfg);
+}
+
+static int alc_build_controls(struct hda_codec *codec)
+{
int err = __alc_build_controls(codec);
if (err < 0)
return err;
- err = snd_hda_jack_add_kctls(codec, &spec->autocfg);
+
+ err = alc_build_jacks(codec);
if (err < 0)
return err;
alc_apply_fixup(codec, ALC_FIXUP_ACT_BUILD);
@@ -1908,14 +2058,6 @@ static int alc_init(struct hda_codec *codec)
return 0;
}
-static void alc_unsol_event(struct hda_codec *codec, unsigned int res)
-{
- struct alc_spec *spec = codec->spec;
-
- if (spec->unsol_event)
- spec->unsol_event(codec, res);
-}
-
#ifdef CONFIG_SND_HDA_POWER_SAVE
static int alc_check_power_status(struct hda_codec *codec, hda_nid_t nid)
{
@@ -2289,6 +2431,7 @@ static void alc_free(struct hda_codec *codec)
alc_shutup(codec);
alc_free_kctls(codec);
alc_free_bind_ctls(codec);
+ snd_hda_gen_free(&spec->gen);
kfree(spec);
snd_hda_detach_beep_device(codec);
}
@@ -2299,7 +2442,7 @@ static void alc_power_eapd(struct hda_codec *codec)
alc_auto_setup_eapd(codec, false);
}
-static int alc_suspend(struct hda_codec *codec, pm_message_t state)
+static int alc_suspend(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
alc_shutup(codec);
@@ -2316,6 +2459,7 @@ static int alc_resume(struct hda_codec *codec)
codec->patch_ops.init(codec);
snd_hda_codec_resume_amp(codec);
snd_hda_codec_resume_cache(codec);
+ alc_inv_dmic_sync(codec, true);
hda_call_check_power_status(codec, 0x01);
return 0;
}
@@ -4115,14 +4259,12 @@ static void set_capture_mixer(struct hda_codec *codec)
*/
static void alc_auto_init_std(struct hda_codec *codec)
{
- struct alc_spec *spec = codec->spec;
alc_auto_init_multi_out(codec);
alc_auto_init_extra_out(codec);
alc_auto_init_analog_input(codec);
alc_auto_init_input_src(codec);
alc_auto_init_digital(codec);
- if (spec->unsol_event)
- alc_inithook(codec);
+ alc_inithook(codec);
}
/*
@@ -4253,6 +4395,7 @@ static int alc_alloc_spec(struct hda_codec *codec, hda_nid_t mixer_nid)
return -ENOMEM;
codec->spec = spec;
spec->mixer_nid = mixer_nid;
+ snd_hda_gen_init(&spec->gen);
err = alc_codec_rename_from_preset(codec);
if (err < 0) {
@@ -4722,7 +4865,6 @@ static void alc260_fixup_gpio1_toggle(struct hda_codec *codec,
spec->automute_speaker = 1;
spec->autocfg.hp_pins[0] = 0x0f; /* copy it for automute */
snd_hda_jack_detect_enable(codec, 0x0f, ALC_HP_EVENT);
- spec->unsol_event = alc_sku_unsol_event;
snd_hda_gen_add_verbs(&spec->gen, alc_gpio1_init_verbs);
}
}
@@ -4907,6 +5049,7 @@ enum {
ALC889_FIXUP_DAC_ROUTE,
ALC889_FIXUP_MBP_VREF,
ALC889_FIXUP_IMAC91_VREF,
+ ALC882_FIXUP_INV_DMIC,
};
static void alc889_fixup_coef(struct hda_codec *codec,
@@ -5210,6 +5353,10 @@ static const struct alc_fixup alc882_fixups[] = {
.chained = true,
.chain_id = ALC882_FIXUP_GPIO1,
},
+ [ALC882_FIXUP_INV_DMIC] = {
+ .type = ALC_FIXUP_FUNC,
+ .v.func = alc_fixup_inv_dmic_0x12,
+ },
};
static const struct snd_pci_quirk alc882_fixup_tbl[] = {
@@ -5284,6 +5431,7 @@ static const struct alc_model_fixup alc882_fixup_models[] = {
{.id = ALC882_FIXUP_ACER_ASPIRE_4930G, .name = "acer-aspire-4930g"},
{.id = ALC882_FIXUP_ACER_ASPIRE_8930G, .name = "acer-aspire-8930g"},
{.id = ALC883_FIXUP_ACER_EAPD, .name = "acer-aspire"},
+ {.id = ALC882_FIXUP_INV_DMIC, .name = "inv-dmic"},
{}
};
@@ -5371,6 +5519,7 @@ enum {
ALC262_FIXUP_LENOVO_3000,
ALC262_FIXUP_BENQ,
ALC262_FIXUP_BENQ_T31,
+ ALC262_FIXUP_INV_DMIC,
};
static const struct alc_fixup alc262_fixups[] = {
@@ -5422,6 +5571,10 @@ static const struct alc_fixup alc262_fixups[] = {
{}
}
},
+ [ALC262_FIXUP_INV_DMIC] = {
+ .type = ALC_FIXUP_FUNC,
+ .v.func = alc_fixup_inv_dmic_0x12,
+ },
};
static const struct snd_pci_quirk alc262_fixup_tbl[] = {
@@ -5436,6 +5589,10 @@ static const struct snd_pci_quirk alc262_fixup_tbl[] = {
{}
};
+static const struct alc_model_fixup alc262_fixup_models[] = {
+ {.id = ALC262_FIXUP_INV_DMIC, .name = "inv-dmic"},
+ {}
+};
/*
*/
@@ -5464,7 +5621,8 @@ static int patch_alc262(struct hda_codec *codec)
#endif
alc_fix_pll_init(codec, 0x20, 0x0a, 10);
- alc_pick_fixup(codec, NULL, alc262_fixup_tbl, alc262_fixups);
+ alc_pick_fixup(codec, alc262_fixup_models, alc262_fixup_tbl,
+ alc262_fixups);
alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE);
alc_auto_parse_customize_define(codec);
@@ -5520,6 +5678,22 @@ static const struct hda_verb alc268_beep_init_verbs[] = {
{ }
};
+enum {
+ ALC268_FIXUP_INV_DMIC,
+};
+
+static const struct alc_fixup alc268_fixups[] = {
+ [ALC268_FIXUP_INV_DMIC] = {
+ .type = ALC_FIXUP_FUNC,
+ .v.func = alc_fixup_inv_dmic_0x12,
+ },
+};
+
+static const struct alc_model_fixup alc268_fixup_models[] = {
+ {.id = ALC268_FIXUP_INV_DMIC, .name = "inv-dmic"},
+ {}
+};
+
/*
* BIOS auto configuration
*/
@@ -5551,6 +5725,9 @@ static int patch_alc268(struct hda_codec *codec)
spec = codec->spec;
+ alc_pick_fixup(codec, alc268_fixup_models, NULL, alc268_fixups);
+ alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE);
+
/* automatic parse from the BIOS config */
err = alc268_parse_auto_config(codec);
if (err < 0)
@@ -5580,6 +5757,8 @@ static int patch_alc268(struct hda_codec *codec)
codec->patch_ops = alc_patch_ops;
spec->shutup = alc_eapd_shutup;
+ alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE);
+
return 0;
error:
@@ -5702,6 +5881,15 @@ static int alc269_resume(struct hda_codec *codec)
}
#endif /* CONFIG_PM */
+static void alc269_fixup_pincfg_no_hp_to_lineout(struct hda_codec *codec,
+ const struct alc_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+
+ if (action == ALC_FIXUP_ACT_PRE_PROBE)
+ spec->parse_flags = HDA_PINCFG_NO_HP_FIXUP;
+}
+
static void alc269_fixup_hweq(struct hda_codec *codec,
const struct alc_fixup *fix, int action)
{
@@ -5808,6 +5996,7 @@ static void alc269_fixup_mic2_mute(struct hda_codec *codec,
}
}
+
enum {
ALC269_FIXUP_SONY_VAIO,
ALC275_FIXUP_SONY_VAIO_GPIO2,
@@ -5826,6 +6015,9 @@ enum {
ALC269VB_FIXUP_AMIC,
ALC269VB_FIXUP_DMIC,
ALC269_FIXUP_MIC2_MUTE_LED,
+ ALC269_FIXUP_INV_DMIC,
+ ALC269_FIXUP_LENOVO_DOCK,
+ ALC269_FIXUP_PINCFG_NO_HP_TO_LINEOUT,
};
static const struct alc_fixup alc269_fixups[] = {
@@ -5950,12 +6142,33 @@ static const struct alc_fixup alc269_fixups[] = {
.type = ALC_FIXUP_FUNC,
.v.func = alc269_fixup_mic2_mute,
},
+ [ALC269_FIXUP_INV_DMIC] = {
+ .type = ALC_FIXUP_FUNC,
+ .v.func = alc_fixup_inv_dmic_0x12,
+ },
+ [ALC269_FIXUP_LENOVO_DOCK] = {
+ .type = ALC_FIXUP_PINS,
+ .v.pins = (const struct alc_pincfg[]) {
+ { 0x19, 0x23a11040 }, /* dock mic */
+ { 0x1b, 0x2121103f }, /* dock headphone */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_PINCFG_NO_HP_TO_LINEOUT
+ },
+ [ALC269_FIXUP_PINCFG_NO_HP_TO_LINEOUT] = {
+ .type = ALC_FIXUP_FUNC,
+ .v.func = alc269_fixup_pincfg_no_hp_to_lineout,
+ },
};
static const struct snd_pci_quirk alc269_fixup_tbl[] = {
+ SND_PCI_QUIRK(0x1025, 0x029b, "Acer 1810TZ", ALC269_FIXUP_INV_DMIC),
+ SND_PCI_QUIRK(0x1025, 0x0349, "Acer AOD260", ALC269_FIXUP_INV_DMIC),
SND_PCI_QUIRK(0x103c, 0x1586, "HP", ALC269_FIXUP_MIC2_MUTE_LED),
SND_PCI_QUIRK(0x1043, 0x1427, "Asus Zenbook UX31E", ALC269VB_FIXUP_DMIC),
SND_PCI_QUIRK(0x1043, 0x1a13, "Asus G73Jw", ALC269_FIXUP_ASUS_G73JW),
+ SND_PCI_QUIRK(0x1043, 0x1b13, "Asus U41SV", ALC269_FIXUP_INV_DMIC),
SND_PCI_QUIRK(0x1043, 0x16e3, "ASUS UX50", ALC269_FIXUP_STEREO_DMIC),
SND_PCI_QUIRK(0x1043, 0x831a, "ASUS P901", ALC269_FIXUP_STEREO_DMIC),
SND_PCI_QUIRK(0x1043, 0x834a, "ASUS S101", ALC269_FIXUP_STEREO_DMIC),
@@ -5973,6 +6186,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x17aa, 0x21b8, "Thinkpad Edge 14", ALC269_FIXUP_SKU_IGNORE),
SND_PCI_QUIRK(0x17aa, 0x21ca, "Thinkpad L412", ALC269_FIXUP_SKU_IGNORE),
SND_PCI_QUIRK(0x17aa, 0x21e9, "Thinkpad Edge 15", ALC269_FIXUP_SKU_IGNORE),
+ SND_PCI_QUIRK(0x17aa, 0x2203, "Thinkpad X230 Tablet", ALC269_FIXUP_LENOVO_DOCK),
SND_PCI_QUIRK(0x17aa, 0x3bf8, "Quanta FL1", ALC269_FIXUP_QUANTA_MUTE),
SND_PCI_QUIRK(0x17aa, 0x3bf8, "Lenovo Ideapd", ALC269_FIXUP_PCM_44K),
SND_PCI_QUIRK(0x17aa, 0x9e54, "LENOVO NB", ALC269_FIXUP_LENOVO_EAPD),
@@ -6031,6 +6245,10 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
static const struct alc_model_fixup alc269_fixup_models[] = {
{.id = ALC269_FIXUP_AMIC, .name = "laptop-amic"},
{.id = ALC269_FIXUP_DMIC, .name = "laptop-dmic"},
+ {.id = ALC269_FIXUP_STEREO_DMIC, .name = "alc269-dmic"},
+ {.id = ALC271_FIXUP_DMIC, .name = "alc271-dmic"},
+ {.id = ALC269_FIXUP_INV_DMIC, .name = "inv-dmic"},
+ {.id = ALC269_FIXUP_LENOVO_DOCK, .name = "lenovo-dock"},
{}
};
@@ -6327,12 +6545,6 @@ static const struct snd_pci_quirk alc861vd_fixup_tbl[] = {
{}
};
-static const struct hda_verb alc660vd_eapd_verbs[] = {
- {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2},
- {0x15, AC_VERB_SET_EAPD_BTLENABLE, 2},
- { }
-};
-
/*
*/
static int patch_alc861vd(struct hda_codec *codec)
@@ -6354,11 +6566,6 @@ static int patch_alc861vd(struct hda_codec *codec)
if (err < 0)
goto error;
- if (codec->vendor_id == 0x10ec0660) {
- /* always turn on EAPD */
- snd_hda_gen_add_verbs(&spec->gen, alc660vd_eapd_verbs);
- }
-
if (!spec->no_analog) {
err = snd_hda_attach_beep_device(codec, 0x23);
if (err < 0)
@@ -6441,6 +6648,7 @@ enum {
ALC662_FIXUP_ASUS_MODE8,
ALC662_FIXUP_NO_JACK_DETECT,
ALC662_FIXUP_ZOTAC_Z68,
+ ALC662_FIXUP_INV_DMIC,
};
static const struct alc_fixup alc662_fixups[] = {
@@ -6597,12 +6805,17 @@ static const struct alc_fixup alc662_fixups[] = {
{ }
}
},
+ [ALC662_FIXUP_INV_DMIC] = {
+ .type = ALC_FIXUP_FUNC,
+ .v.func = alc_fixup_inv_dmic_0x12,
+ },
};
static const struct snd_pci_quirk alc662_fixup_tbl[] = {
SND_PCI_QUIRK(0x1019, 0x9087, "ECS", ALC662_FIXUP_ASUS_MODE2),
SND_PCI_QUIRK(0x1025, 0x0308, "Acer Aspire 8942G", ALC662_FIXUP_ASPIRE),
SND_PCI_QUIRK(0x1025, 0x031c, "Gateway NV79", ALC662_FIXUP_SKU_IGNORE),
+ SND_PCI_QUIRK(0x1025, 0x0349, "eMachines eM250", ALC662_FIXUP_INV_DMIC),
SND_PCI_QUIRK(0x1025, 0x038b, "Acer Aspire 8943G", ALC662_FIXUP_ASPIRE),
SND_PCI_QUIRK(0x103c, 0x1632, "HP RP5800", ALC662_FIXUP_HP_RP5800),
SND_PCI_QUIRK(0x1043, 0x8469, "ASUS mobo", ALC662_FIXUP_NO_JACK_DETECT),
@@ -6683,9 +6896,35 @@ static const struct alc_model_fixup alc662_fixup_models[] = {
{.id = ALC662_FIXUP_ASUS_MODE6, .name = "asus-mode6"},
{.id = ALC662_FIXUP_ASUS_MODE7, .name = "asus-mode7"},
{.id = ALC662_FIXUP_ASUS_MODE8, .name = "asus-mode8"},
+ {.id = ALC662_FIXUP_INV_DMIC, .name = "inv-dmic"},
{}
};
+static void alc662_fill_coef(struct hda_codec *codec)
+{
+ int val, coef;
+
+ coef = alc_get_coef0(codec);
+
+ switch (codec->vendor_id) {
+ case 0x10ec0662:
+ if ((coef & 0x00f0) == 0x0030) {
+ val = alc_read_coef_idx(codec, 0x4); /* EAPD Ctrl */
+ alc_write_coef_idx(codec, 0x4, val & ~(1<<10));
+ }
+ break;
+ case 0x10ec0272:
+ case 0x10ec0273:
+ case 0x10ec0663:
+ case 0x10ec0665:
+ case 0x10ec0670:
+ case 0x10ec0671:
+ case 0x10ec0672:
+ val = alc_read_coef_idx(codec, 0xd); /* EAPD Ctrl */
+ alc_write_coef_idx(codec, 0xd, val | (1<<14));
+ break;
+ }
+}
/*
*/
@@ -6705,12 +6944,8 @@ static int patch_alc662(struct hda_codec *codec)
alc_fix_pll_init(codec, 0x20, 0x04, 15);
- if ((alc_get_coef0(codec) & (1 << 14)) &&
- codec->bus->pci->subsystem_vendor == 0x1025 &&
- spec->cdefine.platform_type == 1) {
- if (alc_codec_rename(codec, "ALC272X") < 0)
- goto error;
- }
+ spec->init_hook = alc662_fill_coef;
+ alc662_fill_coef(codec);
alc_pick_fixup(codec, alc662_fixup_models,
alc662_fixup_tbl, alc662_fixups);
@@ -6718,6 +6953,13 @@ static int patch_alc662(struct hda_codec *codec)
alc_auto_parse_customize_define(codec);
+ if ((alc_get_coef0(codec) & (1 << 14)) &&
+ codec->bus->pci->subsystem_vendor == 0x1025 &&
+ spec->cdefine.platform_type == 1) {
+ if (alc_codec_rename(codec, "ALC272X") < 0)
+ goto error;
+ }
+
/* automatic parse from the BIOS config */
err = alc662_parse_auto_config(codec);
if (err < 0)
@@ -6800,6 +7042,8 @@ static const struct hda_codec_preset snd_hda_preset_realtek[] = {
{ .id = 0x10ec0272, .name = "ALC272", .patch = patch_alc662 },
{ .id = 0x10ec0275, .name = "ALC275", .patch = patch_alc269 },
{ .id = 0x10ec0276, .name = "ALC276", .patch = patch_alc269 },
+ { .id = 0x10ec0280, .name = "ALC280", .patch = patch_alc269 },
+ { .id = 0x10ec0282, .name = "ALC282", .patch = patch_alc269 },
{ .id = 0x10ec0861, .rev = 0x100340, .name = "ALC660",
.patch = patch_alc861 },
{ .id = 0x10ec0660, .name = "ALC660-VD", .patch = patch_alc861vd },
diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c
index 7db8228f1b8..a1596a3b171 100644
--- a/sound/pci/hda/patch_sigmatel.c
+++ b/sound/pci/hda/patch_sigmatel.c
@@ -4367,7 +4367,7 @@ static int stac92xx_init(struct hda_codec *codec)
AC_PINCTL_IN_EN);
for (i = 0; i < spec->num_pwrs; i++) {
hda_nid_t nid = spec->pwr_nids[i];
- int pinctl, def_conf;
+ unsigned int pinctl, def_conf;
def_conf = snd_hda_codec_get_pincfg(codec, nid);
def_conf = get_defcfg_connect(def_conf);
@@ -4376,6 +4376,11 @@ static int stac92xx_init(struct hda_codec *codec)
stac_toggle_power_map(codec, nid, 0);
continue;
}
+ if (def_conf == AC_JACK_PORT_FIXED) {
+ /* no need for jack detection for fixed pins */
+ stac_toggle_power_map(codec, nid, 1);
+ continue;
+ }
/* power on when no jack detection is available */
/* or when the VREF is used for controlling LED */
if (!spec->hp_detect ||
@@ -4992,7 +4997,7 @@ static int stac92xx_resume(struct hda_codec *codec)
return 0;
}
-static int stac92xx_suspend(struct hda_codec *codec, pm_message_t state)
+static int stac92xx_suspend(struct hda_codec *codec)
{
stac92xx_shutup(codec);
return 0;
diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c
index 82b368068e0..90645560ed3 100644
--- a/sound/pci/hda/patch_via.c
+++ b/sound/pci/hda/patch_via.c
@@ -1748,7 +1748,7 @@ static void via_unsol_event(struct hda_codec *codec,
}
#ifdef CONFIG_PM
-static int via_suspend(struct hda_codec *codec, pm_message_t state)
+static int via_suspend(struct hda_codec *codec)
{
struct via_spec *spec = codec->spec;
vt1708_stop_hp_work(spec);