summaryrefslogtreecommitdiff
path: root/sound
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2010-01-13 08:32:53 +0100
committerTakashi Iwai <tiwai@suse.de>2010-01-13 08:32:53 +0100
commit47e91348459901c30cc1bb4897e62ced21ca243a (patch)
treeebc855a2f3686726fb68b6b173af54ee75b44c61 /sound
parent6b98515a620592636d2f8e0d3e2942d1cb4847ec (diff)
parented69c6a8eef679f2783848ed624897a937a434ac (diff)
Merge branch 'devel' of git.alsa-project.org:alsa-kernel into topic/misc
Diffstat (limited to 'sound')
-rw-r--r--sound/core/pcm_lib.c8
-rw-r--r--sound/pci/hda/hda_codec.c48
-rw-r--r--sound/pci/hda/hda_codec.h1
-rw-r--r--sound/pci/hda/hda_hwdep.c7
-rw-r--r--sound/pci/hda/hda_intel.c51
-rw-r--r--sound/pci/hda/hda_local.h2
-rw-r--r--sound/pci/hda/patch_analog.c71
-rw-r--r--sound/pci/hda/patch_cirrus.c12
-rw-r--r--sound/pci/hda/patch_cmedia.c3
-rw-r--r--sound/pci/hda/patch_conexant.c382
-rw-r--r--sound/pci/hda/patch_realtek.c73
-rw-r--r--sound/pci/hda/patch_sigmatel.c113
-rw-r--r--sound/pci/hda/patch_via.c3
13 files changed, 591 insertions, 183 deletions
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c
index 0403a7d55f0c..720019560794 100644
--- a/sound/core/pcm_lib.c
+++ b/sound/core/pcm_lib.c
@@ -394,6 +394,7 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
+ HZ/100);
/* move new_hw_ptr according jiffies not pos variable */
new_hw_ptr = old_hw_ptr;
+ hw_base = delta;
/* use loop to avoid checks for delta overflows */
/* the delta value is small or zero in most cases */
while (delta > 0) {
@@ -403,8 +404,6 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
delta--;
}
/* align hw_base to buffer_size */
- hw_base = new_hw_ptr - (new_hw_ptr % runtime->buffer_size);
- delta = 0;
hw_ptr_error(substream,
"hw_ptr skipping! %s"
"(pos=%ld, delta=%ld, period=%ld, "
@@ -412,9 +411,12 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
in_interrupt ? "[Q] " : "",
(long)pos, (long)hdelta,
(long)runtime->period_size, jdelta,
- ((hdelta * HZ) / runtime->rate), delta,
+ ((hdelta * HZ) / runtime->rate), hw_base,
(unsigned long)old_hw_ptr,
(unsigned long)new_hw_ptr);
+ /* reset values to proper state */
+ delta = 0;
+ hw_base = new_hw_ptr - (new_hw_ptr % runtime->buffer_size);
}
no_jiffies_check:
if (delta > runtime->period_size + runtime->period_size / 2) {
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index d2f10b1c3a8a..d02ea8926e7e 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -824,6 +824,9 @@ int snd_hda_add_pincfg(struct hda_codec *codec, struct snd_array *list,
struct hda_pincfg *pin;
unsigned int oldcfg;
+ if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_PIN)
+ return -EINVAL;
+
oldcfg = snd_hda_codec_get_pincfg(codec, nid);
pin = look_up_pincfg(codec, list, nid);
if (!pin) {
@@ -899,6 +902,25 @@ static void restore_pincfgs(struct hda_codec *codec)
}
}
+/**
+ * snd_hda_shutup_pins - Shut up all pins
+ * @codec: the HDA codec
+ *
+ * Clear all pin controls to shup up before suspend for avoiding click noise.
+ * The controls aren't cached so that they can be resumed properly.
+ */
+void snd_hda_shutup_pins(struct hda_codec *codec)
+{
+ int i;
+ for (i = 0; i < codec->init_pins.used; i++) {
+ struct hda_pincfg *pin = snd_array_elem(&codec->init_pins, i);
+ /* use read here for syncing after issuing each verb */
+ snd_hda_codec_read(codec, pin->nid, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, 0);
+ }
+}
+EXPORT_SYMBOL_HDA(snd_hda_shutup_pins);
+
static void init_hda_cache(struct hda_cache_rec *cache,
unsigned int record_size);
static void free_hda_cache(struct hda_cache_rec *cache);
@@ -3537,32 +3559,6 @@ int snd_hda_add_new_ctls(struct hda_codec *codec, struct snd_kcontrol_new *knew)
}
EXPORT_SYMBOL_HDA(snd_hda_add_new_ctls);
-/**
- * snd_hda_add_nids - assign nids to controls from the array
- * @codec: the HDA codec
- * @kctl: struct snd_kcontrol
- * @index: index to kctl
- * @nids: the array of hda_nid_t
- * @size: count of hda_nid_t items
- *
- * This helper function assigns NIDs in the given array to a control element.
- *
- * Returns 0 if successful, or a negative error code.
- */
-int snd_hda_add_nids(struct hda_codec *codec, struct snd_kcontrol *kctl,
- unsigned int index, hda_nid_t *nids, unsigned int size)
-{
- int err;
-
- for ( ; size > 0; size--, nids++) {
- err = snd_hda_add_nid(codec, kctl, index, *nids);
- if (err < 0)
- return err;
- }
- return 0;
-}
-EXPORT_SYMBOL_HDA(snd_hda_add_nids);
-
#ifdef CONFIG_SND_HDA_POWER_SAVE
static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg,
unsigned int power_state);
diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h
index 0d08ad5bd898..11c4aa8ee996 100644
--- a/sound/pci/hda/hda_codec.h
+++ b/sound/pci/hda/hda_codec.h
@@ -898,6 +898,7 @@ int snd_hda_codec_set_pincfg(struct hda_codec *codec, hda_nid_t nid,
unsigned int cfg);
int snd_hda_add_pincfg(struct hda_codec *codec, struct snd_array *list,
hda_nid_t nid, unsigned int cfg); /* for hwdep */
+void snd_hda_shutup_pins(struct hda_codec *codec);
/*
* Mixer
diff --git a/sound/pci/hda/hda_hwdep.c b/sound/pci/hda/hda_hwdep.c
index 40ccb419b6e9..b36919c0d363 100644
--- a/sound/pci/hda/hda_hwdep.c
+++ b/sound/pci/hda/hda_hwdep.c
@@ -293,8 +293,11 @@ static ssize_t type##_store(struct device *dev, \
{ \
struct snd_hwdep *hwdep = dev_get_drvdata(dev); \
struct hda_codec *codec = hwdep->private_data; \
- char *after; \
- codec->type = simple_strtoul(buf, &after, 0); \
+ unsigned long val; \
+ int err = strict_strtoul(buf, 0, &val); \
+ if (err < 0) \
+ return err; \
+ codec->type = val; \
return count; \
}
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index ff8ad46cc50e..1f516e668d88 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -356,6 +356,7 @@ struct azx_dev {
*/
unsigned char stream_tag; /* assigned stream */
unsigned char index; /* stream index */
+ int device; /* last device number assigned to */
unsigned int opened :1;
unsigned int running :1;
@@ -1441,10 +1442,13 @@ static int __devinit azx_codec_configure(struct azx *chip)
*/
/* assign a stream for the PCM */
-static inline struct azx_dev *azx_assign_device(struct azx *chip, int stream)
+static inline struct azx_dev *
+azx_assign_device(struct azx *chip, struct snd_pcm_substream *substream)
{
int dev, i, nums;
- if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ struct azx_dev *res = NULL;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
dev = chip->playback_index_offset;
nums = chip->playback_streams;
} else {
@@ -1453,10 +1457,15 @@ static inline struct azx_dev *azx_assign_device(struct azx *chip, int stream)
}
for (i = 0; i < nums; i++, dev++)
if (!chip->azx_dev[dev].opened) {
- chip->azx_dev[dev].opened = 1;
- return &chip->azx_dev[dev];
+ res = &chip->azx_dev[dev];
+ if (res->device == substream->pcm->device)
+ break;
}
- return NULL;
+ if (res) {
+ res->opened = 1;
+ res->device = substream->pcm->device;
+ }
+ return res;
}
/* release the assigned stream */
@@ -1505,7 +1514,7 @@ static int azx_pcm_open(struct snd_pcm_substream *substream)
int err;
mutex_lock(&chip->open_mutex);
- azx_dev = azx_assign_device(chip, substream->stream);
+ azx_dev = azx_assign_device(chip, substream);
if (azx_dev == NULL) {
mutex_unlock(&chip->open_mutex);
return -EBUSY;
@@ -2695,32 +2704,10 @@ static struct pci_device_id azx_ids[] = {
/* ULI M5461 */
{ PCI_DEVICE(0x10b9, 0x5461), .driver_data = AZX_DRIVER_ULI },
/* NVIDIA MCP */
- { PCI_DEVICE(0x10de, 0x026c), .driver_data = AZX_DRIVER_NVIDIA },
- { PCI_DEVICE(0x10de, 0x0371), .driver_data = AZX_DRIVER_NVIDIA },
- { PCI_DEVICE(0x10de, 0x03e4), .driver_data = AZX_DRIVER_NVIDIA },
- { PCI_DEVICE(0x10de, 0x03f0), .driver_data = AZX_DRIVER_NVIDIA },
- { PCI_DEVICE(0x10de, 0x044a), .driver_data = AZX_DRIVER_NVIDIA },
- { PCI_DEVICE(0x10de, 0x044b), .driver_data = AZX_DRIVER_NVIDIA },
- { PCI_DEVICE(0x10de, 0x055c), .driver_data = AZX_DRIVER_NVIDIA },
- { PCI_DEVICE(0x10de, 0x055d), .driver_data = AZX_DRIVER_NVIDIA },
- { PCI_DEVICE(0x10de, 0x0590), .driver_data = AZX_DRIVER_NVIDIA },
- { PCI_DEVICE(0x10de, 0x0774), .driver_data = AZX_DRIVER_NVIDIA },
- { PCI_DEVICE(0x10de, 0x0775), .driver_data = AZX_DRIVER_NVIDIA },
- { PCI_DEVICE(0x10de, 0x0776), .driver_data = AZX_DRIVER_NVIDIA },
- { PCI_DEVICE(0x10de, 0x0777), .driver_data = AZX_DRIVER_NVIDIA },
- { PCI_DEVICE(0x10de, 0x07fc), .driver_data = AZX_DRIVER_NVIDIA },
- { PCI_DEVICE(0x10de, 0x07fd), .driver_data = AZX_DRIVER_NVIDIA },
- { PCI_DEVICE(0x10de, 0x0ac0), .driver_data = AZX_DRIVER_NVIDIA },
- { PCI_DEVICE(0x10de, 0x0ac1), .driver_data = AZX_DRIVER_NVIDIA },
- { PCI_DEVICE(0x10de, 0x0ac2), .driver_data = AZX_DRIVER_NVIDIA },
- { PCI_DEVICE(0x10de, 0x0ac3), .driver_data = AZX_DRIVER_NVIDIA },
- { PCI_DEVICE(0x10de, 0x0be2), .driver_data = AZX_DRIVER_NVIDIA },
- { PCI_DEVICE(0x10de, 0x0be3), .driver_data = AZX_DRIVER_NVIDIA },
- { PCI_DEVICE(0x10de, 0x0be4), .driver_data = AZX_DRIVER_NVIDIA },
- { PCI_DEVICE(0x10de, 0x0d94), .driver_data = AZX_DRIVER_NVIDIA },
- { PCI_DEVICE(0x10de, 0x0d95), .driver_data = AZX_DRIVER_NVIDIA },
- { PCI_DEVICE(0x10de, 0x0d96), .driver_data = AZX_DRIVER_NVIDIA },
- { PCI_DEVICE(0x10de, 0x0d97), .driver_data = AZX_DRIVER_NVIDIA },
+ { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID),
+ .class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8,
+ .class_mask = 0xffffff,
+ .driver_data = AZX_DRIVER_NVIDIA },
/* Teradici */
{ PCI_DEVICE(0x6549, 0x1200), .driver_data = AZX_DRIVER_TERA },
/* Creative X-Fi (CA0110-IBG) */
diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h
index d505d052972e..7cee364976ff 100644
--- a/sound/pci/hda/hda_local.h
+++ b/sound/pci/hda/hda_local.h
@@ -343,8 +343,6 @@ int snd_hda_check_board_codec_sid_config(struct hda_codec *codec,
const struct snd_pci_quirk *tbl);
int snd_hda_add_new_ctls(struct hda_codec *codec,
struct snd_kcontrol_new *knew);
-int snd_hda_add_nids(struct hda_codec *codec, struct snd_kcontrol *kctl,
- unsigned int index, hda_nid_t *nids, unsigned int size);
/*
* unsolicited event handler
diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c
index 92b72d4f3984..cecd3c108990 100644
--- a/sound/pci/hda/patch_analog.c
+++ b/sound/pci/hda/patch_analog.c
@@ -244,8 +244,7 @@ static int ad198x_build_controls(struct hda_codec *codec)
if (!kctl)
kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
for (i = 0; kctl && i < kctl->count; i++) {
- err = snd_hda_add_nids(codec, kctl, i, spec->capsrc_nids,
- spec->input_mux->num_items);
+ err = snd_hda_add_nid(codec, kctl, i, spec->capsrc_nids[i]);
if (err < 0)
return err;
}
@@ -442,6 +441,11 @@ static int ad198x_build_pcms(struct hda_codec *codec)
return 0;
}
+static inline void ad198x_shutup(struct hda_codec *codec)
+{
+ snd_hda_shutup_pins(codec);
+}
+
static void ad198x_free_kctls(struct hda_codec *codec)
{
struct ad198x_spec *spec = codec->spec;
@@ -455,6 +459,46 @@ static void ad198x_free_kctls(struct hda_codec *codec)
snd_array_free(&spec->kctls);
}
+static void ad198x_power_eapd_write(struct hda_codec *codec, hda_nid_t front,
+ hda_nid_t hp)
+{
+ struct ad198x_spec *spec = codec->spec;
+ snd_hda_codec_write(codec, front, 0, AC_VERB_SET_EAPD_BTLENABLE,
+ !spec->inv_eapd ? 0x00 : 0x02);
+ snd_hda_codec_write(codec, hp, 0, AC_VERB_SET_EAPD_BTLENABLE,
+ !spec->inv_eapd ? 0x00 : 0x02);
+}
+
+static void ad198x_power_eapd(struct hda_codec *codec)
+{
+ /* We currently only handle front, HP */
+ switch (codec->vendor_id) {
+ case 0x11d41882:
+ case 0x11d4882a:
+ case 0x11d41884:
+ case 0x11d41984:
+ case 0x11d41883:
+ case 0x11d4184a:
+ case 0x11d4194a:
+ case 0x11d4194b:
+ ad198x_power_eapd_write(codec, 0x12, 0x11);
+ break;
+ case 0x11d41981:
+ case 0x11d41983:
+ ad198x_power_eapd_write(codec, 0x05, 0x06);
+ break;
+ case 0x11d41986:
+ ad198x_power_eapd_write(codec, 0x1b, 0x1a);
+ break;
+ case 0x11d41988:
+ case 0x11d4198b:
+ case 0x11d4989a:
+ case 0x11d4989b:
+ ad198x_power_eapd_write(codec, 0x29, 0x22);
+ break;
+ }
+}
+
static void ad198x_free(struct hda_codec *codec)
{
struct ad198x_spec *spec = codec->spec;
@@ -462,11 +506,29 @@ static void ad198x_free(struct hda_codec *codec)
if (!spec)
return;
+ ad198x_shutup(codec);
ad198x_free_kctls(codec);
kfree(spec);
snd_hda_detach_beep_device(codec);
}
+#ifdef SND_HDA_NEEDS_RESUME
+static int ad198x_suspend(struct hda_codec *codec, pm_message_t state)
+{
+ ad198x_shutup(codec);
+ ad198x_power_eapd(codec);
+ return 0;
+}
+
+static int ad198x_resume(struct hda_codec *codec)
+{
+ ad198x_init(codec);
+ snd_hda_codec_resume_amp(codec);
+ snd_hda_codec_resume_cache(codec);
+ return 0;
+}
+#endif
+
static struct hda_codec_ops ad198x_patch_ops = {
.build_controls = ad198x_build_controls,
.build_pcms = ad198x_build_pcms,
@@ -475,6 +537,11 @@ static struct hda_codec_ops ad198x_patch_ops = {
#ifdef CONFIG_SND_HDA_POWER_SAVE
.check_power_status = ad198x_check_power_status,
#endif
+#ifdef SND_HDA_NEEDS_RESUME
+ .suspend = ad198x_suspend,
+ .resume = ad198x_resume,
+#endif
+ .reboot_notify = ad198x_shutup,
};
diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c
index 093cfbb55e9e..7de782a5b8f4 100644
--- a/sound/pci/hda/patch_cirrus.c
+++ b/sound/pci/hda/patch_cirrus.c
@@ -753,6 +753,7 @@ static int build_input(struct hda_codec *codec)
spec->capture_bind[1] = make_bind_capture(codec, &snd_hda_bind_vol);
for (i = 0; i < 2; i++) {
struct snd_kcontrol *kctl;
+ int n;
if (!spec->capture_bind[i])
return -ENOMEM;
kctl = snd_ctl_new1(&cs_capture_ctls[i], codec);
@@ -762,10 +763,13 @@ static int build_input(struct hda_codec *codec)
err = snd_hda_ctl_add(codec, 0, kctl);
if (err < 0)
return err;
- err = snd_hda_add_nids(codec, kctl, 0, spec->adc_nid,
- spec->num_inputs);
- if (err < 0)
- return err;
+ for (n = 0; n < AUTO_PIN_LAST; n++) {
+ if (!spec->adc_nid[n])
+ continue;
+ err = snd_hda_add_nid(codec, kctl, 0, spec->adc_nid[i]);
+ if (err < 0)
+ return err;
+ }
}
if (spec->num_inputs > 1 && !spec->mic_detect) {
diff --git a/sound/pci/hda/patch_cmedia.c b/sound/pci/hda/patch_cmedia.c
index cc1c22370a60..ff60908f4554 100644
--- a/sound/pci/hda/patch_cmedia.c
+++ b/sound/pci/hda/patch_cmedia.c
@@ -345,8 +345,7 @@ static int cmi9880_build_controls(struct hda_codec *codec)
/* assign Capture Source enums to NID */
kctl = snd_hda_find_mixer_ctl(codec, "Capture Source");
for (i = 0; kctl && i < kctl->count; i++) {
- err = snd_hda_add_nids(codec, kctl, i, spec->adc_nids,
- spec->input_mux->num_items);
+ err = snd_hda_add_nid(codec, kctl, i, spec->adc_nids[i]);
if (err < 0)
return err;
}
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c
index 947785f43b28..685015a53292 100644
--- a/sound/pci/hda/patch_conexant.c
+++ b/sound/pci/hda/patch_conexant.c
@@ -111,8 +111,22 @@ struct conexant_spec {
unsigned int dell_automute;
unsigned int port_d_mode;
- unsigned char ext_mic_bias;
unsigned int dell_vostro;
+
+ unsigned int ext_mic_present;
+ unsigned int recording;
+ void (*capture_prepare)(struct hda_codec *codec);
+ void (*capture_cleanup)(struct hda_codec *codec);
+
+ /* OLPC XO-1.5 supports DC input mode (e.g. for use with analog sensors)
+ * through the microphone jack.
+ * When the user enables this through a mixer switch, both internal and
+ * external microphones are disabled. Gain is fixed at 0dB. In this mode,
+ * we also allow the bias to be configured through a separate mixer
+ * control. */
+ unsigned int dc_enable;
+ unsigned int dc_input_bias; /* offset into cxt5066_olpc_dc_bias */
+ unsigned int mic_boost; /* offset into cxt5066_analog_mic_boost */
};
static int conexant_playback_pcm_open(struct hda_pcm_stream *hinfo,
@@ -185,6 +199,8 @@ static int conexant_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
struct snd_pcm_substream *substream)
{
struct conexant_spec *spec = codec->spec;
+ if (spec->capture_prepare)
+ spec->capture_prepare(codec);
snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
stream_tag, 0, format);
return 0;
@@ -196,6 +212,8 @@ static int conexant_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
{
struct conexant_spec *spec = codec->spec;
snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
+ if (spec->capture_cleanup)
+ spec->capture_cleanup(codec);
return 0;
}
@@ -1723,6 +1741,22 @@ static struct snd_kcontrol_new cxt5051_hp_dv6736_mixers[] = {
{}
};
+static struct snd_kcontrol_new cxt5051_f700_mixers[] = {
+ HDA_CODEC_VOLUME("Mic Volume", 0x14, 0x01, HDA_INPUT),
+ HDA_CODEC_MUTE("Mic Switch", 0x14, 0x01, HDA_INPUT),
+ HDA_CODEC_VOLUME("Master Playback Volume", 0x10, 0x00, HDA_OUTPUT),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Master Playback Switch",
+ .info = cxt_eapd_info,
+ .get = cxt_eapd_get,
+ .put = cxt5051_hp_master_sw_put,
+ .private_value = 0x1a,
+ },
+
+ {}
+};
+
static struct hda_verb cxt5051_init_verbs[] = {
/* Line in, Mic */
{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03},
@@ -1813,6 +1847,32 @@ static struct hda_verb cxt5051_lenovo_x200_init_verbs[] = {
{ } /* end */
};
+static struct hda_verb cxt5051_f700_init_verbs[] = {
+ /* Line in, Mic */
+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1) | 0x03},
+ {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+ {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0},
+ {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0},
+ /* SPK */
+ {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ {0x1a, AC_VERB_SET_CONNECT_SEL, 0x00},
+ /* HP, Amp */
+ {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+ {0x16, AC_VERB_SET_CONNECT_SEL, 0x00},
+ /* DAC1 */
+ {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ /* Record selector: Int mic */
+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1) | 0x44},
+ {0x14, AC_VERB_SET_CONNECT_SEL, 0x1},
+ /* SPDIF route: PCM */
+ {0x1c, AC_VERB_SET_CONNECT_SEL, 0x0},
+ /* EAPD */
+ {0x1a, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */
+ {0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CONEXANT_HP_EVENT},
+ {0x17, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CXT5051_PORTB_EVENT},
+ { } /* end */
+};
+
/* initialize jack-sensing, too */
static int cxt5051_init(struct hda_codec *codec)
{
@@ -1832,6 +1892,7 @@ enum {
CXT5051_HP, /* no docking */
CXT5051_HP_DV6736, /* HP without mic switch */
CXT5051_LENOVO_X200, /* Lenovo X200 laptop */
+ CXT5051_F700, /* HP Compaq Presario F700 */
CXT5051_MODELS
};
@@ -1840,6 +1901,7 @@ static const char *cxt5051_models[CXT5051_MODELS] = {
[CXT5051_HP] = "hp",
[CXT5051_HP_DV6736] = "hp-dv6736",
[CXT5051_LENOVO_X200] = "lenovo-x200",
+ [CXT5051_F700] = "hp 700"
};
static struct snd_pci_quirk cxt5051_cfg_tbl[] = {
@@ -1849,6 +1911,7 @@ static struct snd_pci_quirk cxt5051_cfg_tbl[] = {
CXT5051_LAPTOP),
SND_PCI_QUIRK(0x14f1, 0x5051, "HP Spartan 1.1", CXT5051_HP),
SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo X200", CXT5051_LENOVO_X200),
+ SND_PCI_QUIRK(0x103c, 0x30ea, "Compaq Presario F700", CXT5051_F700),
{}
};
@@ -1899,6 +1962,11 @@ static int patch_cxt5051(struct hda_codec *codec)
case CXT5051_LENOVO_X200:
spec->init_verbs[0] = cxt5051_lenovo_x200_init_verbs;
break;
+ case CXT5051_F700:
+ spec->init_verbs[0] = cxt5051_f700_init_verbs;
+ spec->mixers[0] = cxt5051_f700_mixers;
+ spec->no_auto_mic = 1;
+ break;
}
return 0;
@@ -1966,53 +2034,97 @@ static int cxt5066_hp_master_sw_put(struct snd_kcontrol *kcontrol,
return 1;
}
-/* toggle input of built-in and mic jack appropriately */
-static void cxt5066_automic(struct hda_codec *codec)
+static const struct hda_input_mux cxt5066_olpc_dc_bias = {
+ .num_items = 3,
+ .items = {
+ { "Off", PIN_IN },
+ { "50%", PIN_VREF50 },
+ { "80%", PIN_VREF80 },
+ },
+};
+
+static int cxt5066_set_olpc_dc_bias(struct hda_codec *codec)
{
struct conexant_spec *spec = codec->spec;
- struct hda_verb ext_mic_present[] = {
- /* enable external mic, port B */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, spec->ext_mic_bias},
+ /* Even though port F is the DC input, the bias is controlled on port B.
+ * we also leave that port as an active input (but unselected) in DC mode
+ * just in case that is necessary to make the bias setting take effect. */
+ return snd_hda_codec_write_cache(codec, 0x1a, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL,
+ cxt5066_olpc_dc_bias.items[spec->dc_input_bias].index);
+}
- /* switch to external mic input */
- {0x17, AC_VERB_SET_CONNECT_SEL, 0},
+/* OLPC defers mic widget control until when capture is started because the
+ * microphone LED comes on as soon as these settings are put in place. if we
+ * did this before recording, it would give the false indication that recording
+ * is happening when it is not. */
+static void cxt5066_olpc_select_mic(struct hda_codec *codec)
+{
+ struct conexant_spec *spec = codec->spec;
+ if (!spec->recording)
+ return;
- /* disable internal mic, port C */
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
- {}
- };
- static struct hda_verb ext_mic_absent[] = {
- /* enable internal mic, port C */
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+ if (spec->dc_enable) {
+ /* in DC mode we ignore presence detection and just use the jack
+ * through our special DC port */
+ const struct hda_verb enable_dc_mode[] = {
+ /* disble internal mic, port C */
+ {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+
+ /* enable DC capture, port F */
+ {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+ {},
+ };
+
+ snd_hda_sequence_write(codec, enable_dc_mode);
+ /* port B input disabled (and bias set) through the following call */
+ cxt5066_set_olpc_dc_bias(codec);
+ return;
+ }
- /* switch to internal mic input */
- {0x17, AC_VERB_SET_CONNECT_SEL, 1},
+ /* disable DC (port F) */
+ snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0);
- /* disable external mic, port B */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
- {}
- };
+ /* external mic, port B */
+ snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+ spec->ext_mic_present ? CXT5066_OLPC_EXT_MIC_BIAS : 0);
+
+ /* internal mic, port C */
+ snd_hda_codec_write(codec, 0x1b, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+ spec->ext_mic_present ? 0 : PIN_VREF80);
+}
+
+/* toggle input of built-in and mic jack appropriately */
+static void cxt5066_olpc_automic(struct hda_codec *codec)
+{
+ struct conexant_spec *spec = codec->spec;
unsigned int present;
- present = snd_hda_jack_detect(codec, 0x1a);
- if (present) {
+ if (spec->dc_enable) /* don't do presence detection in DC mode */
+ return;
+
+ present = snd_hda_codec_read(codec, 0x1a, 0,
+ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+ if (present)
snd_printdd("CXT5066: external microphone detected\n");
- snd_hda_sequence_write(codec, ext_mic_present);
- } else {
+ else
snd_printdd("CXT5066: external microphone absent\n");
- snd_hda_sequence_write(codec, ext_mic_absent);
- }
+
+ snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_CONNECT_SEL,
+ present ? 0 : 1);
+ spec->ext_mic_present = !!present;
+
+ cxt5066_olpc_select_mic(codec);
}
/* toggle input of built-in digital mic and mic jack appropriately */
static void cxt5066_vostro_automic(struct hda_codec *codec)
{
- struct conexant_spec *spec = codec->spec;
unsigned int present;
struct hda_verb ext_mic_present[] = {
/* enable external mic, port B */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, spec->ext_mic_bias},
+ {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
/* switch to external mic input */
{0x17, AC_VERB_SET_CONNECT_SEL, 0},
@@ -2063,15 +2175,18 @@ static void cxt5066_hp_automute(struct hda_codec *codec)
}
/* unsolicited event for jack sensing */
-static void cxt5066_unsol_event(struct hda_codec *codec, unsigned int res)
+static void cxt5066_olpc_unsol_event(struct hda_codec *codec, unsigned int res)
{
+ struct conexant_spec *spec = codec->spec;
snd_printdd("CXT5066: unsol event %x (%x)\n", res, res >> 26);
switch (res >> 26) {
case CONEXANT_HP_EVENT:
cxt5066_hp_automute(codec);
break;
case CONEXANT_MIC_EVENT:
- cxt5066_automic(codec);
+ /* ignore mic events in DC mode; we're always using the jack */
+ if (!spec->dc_enable)
+ cxt5066_olpc_automic(codec);
break;
}
}
@@ -2101,6 +2216,15 @@ static const struct hda_input_mux cxt5066_analog_mic_boost = {
},
};
+static int cxt5066_set_mic_boost(struct hda_codec *codec)
+{
+ struct conexant_spec *spec = codec->spec;
+ return snd_hda_codec_write_cache(codec, 0x17, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE,
+ AC_AMP_SET_RIGHT | AC_AMP_SET_LEFT | AC_AMP_SET_OUTPUT |
+ cxt5066_analog_mic_boost.items[spec->mic_boost].index);
+}
+
static int cxt5066_mic_boost_mux_enum_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
@@ -2111,15 +2235,8 @@ static int cxt5066_mic_boost_mux_enum_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- int val;
- hda_nid_t nid = kcontrol->private_value & 0xff;
- int inout = (kcontrol->private_value & 0x100) ?
- AC_AMP_GET_INPUT : AC_AMP_GET_OUTPUT;
-
- val = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_AMP_GAIN_MUTE, inout);
-
- ucontrol->value.enumerated.item[0] = val & AC_AMP_GAIN;
+ struct conexant_spec *spec = codec->spec;
+ ucontrol->value.enumerated.item[0] = spec->mic_boost;
return 0;
}
@@ -2127,26 +2244,132 @@ static int cxt5066_mic_boost_mux_enum_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct conexant_spec *spec = codec->spec;
const struct hda_input_mux *imux = &cxt5066_analog_mic_boost;
unsigned int idx;
- hda_nid_t nid = kcontrol->private_value & 0xff;
- int inout = (kcontrol->private_value & 0x100) ?
- AC_AMP_SET_INPUT : AC_AMP_SET_OUTPUT;
+ idx = ucontrol->value.enumerated.item[0];
+ if (idx >= imux->num_items)
+ idx = imux->num_items - 1;
+
+ spec->mic_boost = idx;
+ if (!spec->dc_enable)
+ cxt5066_set_mic_boost(codec);
+ return 1;
+}
+
+static void cxt5066_enable_dc(struct hda_codec *codec)
+{
+ const struct hda_verb enable_dc_mode[] = {
+ /* disable gain */
+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+
+ /* switch to DC input */
+ {0x17, AC_VERB_SET_CONNECT_SEL, 3},
+ {}
+ };
+
+ /* configure as input source */
+ snd_hda_sequence_write(codec, enable_dc_mode);
+ cxt5066_olpc_select_mic(codec); /* also sets configured bias */
+}
+
+static void cxt5066_disable_dc(struct hda_codec *codec)
+{
+ /* reconfigure input source */
+ cxt5066_set_mic_boost(codec);
+ /* automic also selects the right mic if we're recording */
+ cxt5066_olpc_automic(codec);
+}
+
+static int cxt5066_olpc_dc_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct conexant_spec *spec = codec->spec;
+ ucontrol->value.integer.value[0] = spec->dc_enable;
+ return 0;
+}
+
+static int cxt5066_olpc_dc_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct conexant_spec *spec = codec->spec;
+ int dc_enable = !!ucontrol->value.integer.value[0];
- if (!imux->num_items)
+ if (dc_enable == spec->dc_enable)
return 0;
+
+ spec->dc_enable = dc_enable;
+ if (dc_enable)
+ cxt5066_enable_dc(codec);
+ else
+ cxt5066_disable_dc(codec);
+
+ return 1;
+}
+
+static int cxt5066_olpc_dc_bias_enum_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ return snd_hda_input_mux_info(&cxt5066_olpc_dc_bias, uinfo);
+}
+
+static int cxt5066_olpc_dc_bias_enum_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct conexant_spec *spec = codec->spec;
+ ucontrol->value.enumerated.item[0] = spec->dc_input_bias;
+ return 0;
+}
+
+static int cxt5066_olpc_dc_bias_enum_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct conexant_spec *spec = codec->spec;
+ const struct hda_input_mux *imux = &cxt5066_analog_mic_boost;
+ unsigned int idx;
+
idx = ucontrol->value.enumerated.item[0];
if (idx >= imux->num_items)
idx = imux->num_items - 1;
- snd_hda_codec_write_cache(codec, nid, 0,
- AC_VERB_SET_AMP_GAIN_MUTE,
- AC_AMP_SET_RIGHT | AC_AMP_SET_LEFT | inout |
- imux->items[idx].index);
-
+ spec->dc_input_bias = idx;
+ if (spec->dc_enable)
+ cxt5066_set_olpc_dc_bias(codec);
return 1;
}
+static void cxt5066_olpc_capture_prepare(struct hda_codec *codec)
+{
+ struct conexant_spec *spec = codec->spec;
+ /* mark as recording and configure the microphone widget so that the
+ * recording LED comes on. */
+ spec->recording = 1;
+ cxt5066_olpc_select_mic(codec);
+}
+
+static void cxt5066_olpc_capture_cleanup(struct hda_codec *codec)
+{
+ struct conexant_spec *spec = codec->spec;
+ const struct hda_verb disable_mics[] = {
+ /* disable external mic, port B */
+ {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+
+ /* disble internal mic, port C */
+ {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+
+ /* disable DC capture, port F */
+ {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+ {},
+ };
+
+ snd_hda_sequence_write(codec, disable_mics);
+ spec->recording = 0;
+}
+
static struct hda_input_mux cxt5066_capture_source = {
.num_items = 4,
.items = {
@@ -2199,6 +2422,24 @@ static struct snd_kcontrol_new cxt5066_mixer_master_olpc[] = {
{}
};
+static struct snd_kcontrol_new cxt5066_mixer_olpc_dc[] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "DC Mode Enable Switch",
+ .info = snd_ctl_boolean_mono_info,
+ .get = cxt5066_olpc_dc_get,
+ .put = cxt5066_olpc_dc_put,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "DC Input Bias Enum",
+ .info = cxt5066_olpc_dc_bias_enum_info,
+ .get = cxt5066_olpc_dc_bias_enum_get,
+ .put = cxt5066_olpc_dc_bias_enum_put,
+ },
+ {}
+};
+
static struct snd_kcontrol_new cxt5066_mixers[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -2211,11 +2452,10 @@ static struct snd_kcontrol_new cxt5066_mixers[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Ext Mic Boost Capture Enum",
+ .name = "Analog Mic Boost Capture Enum",
.info = cxt5066_mic_boost_mux_enum_info,
.get = cxt5066_mic_boost_mux_enum_get,
.put = cxt5066_mic_boost_mux_enum_put,
- .private_value = 0x17,
},
HDA_BIND_VOL("Capture Volume", &cxt5066_bind_capture_vol_others),
@@ -2297,10 +2537,10 @@ static struct hda_verb cxt5066_init_verbs_olpc[] = {
{0x19, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */
/* Port B: external microphone */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, CXT5066_OLPC_EXT_MIC_BIAS},
+ {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
/* Port C: internal microphone */
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+ {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
/* Port D: unused */
{0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
@@ -2309,7 +2549,7 @@ static struct hda_verb cxt5066_init_verbs_olpc[] = {
{0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
{0x1d, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */
- /* Port F: unused */
+ /* Port F: external DC input through microphone port */
{0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
/* Port G: internal speakers */
@@ -2429,8 +2669,22 @@ static int cxt5066_init(struct hda_codec *codec)
cxt5066_hp_automute(codec);
if (spec->dell_vostro)
cxt5066_vostro_automic(codec);
- else
- cxt5066_automic(codec);
+ }
+ cxt5066_set_mic_boost(codec);
+ return 0;
+}
+
+static int cxt5066_olpc_init(struct hda_codec *codec)
+{
+ struct conexant_spec *spec = codec->spec;
+ snd_printdd("CXT5066: init\n");
+ conexant_init(codec);
+ cxt5066_hp_automute(codec);
+ if (!spec->dc_enable) {
+ cxt5066_set_mic_boost(codec);
+ cxt5066_olpc_automic(codec);
+ } else {
+ cxt5066_enable_dc(codec);
}
return 0;
}
@@ -2471,7 +2725,7 @@ static int patch_cxt5066(struct hda_codec *codec)
codec->spec = spec;
codec->patch_ops = conexant_patch_ops;
- codec->patch_ops.init = cxt5066_init;
+ codec->patch_ops.init = conexant_init;
spec->dell_automute = 0;
spec->multiout.max_channels = 2;
@@ -2484,7 +2738,6 @@ static int patch_cxt5066(struct hda_codec *codec)
spec->input_mux = &cxt5066_capture_source;
spec->port_d_mode = PIN_HP;
- spec->ext_mic_bias = PIN_VREF80;
spec->num_init_verbs = 1;
spec->init_verbs[0] = cxt5066_init_verbs;
@@ -2511,20 +2764,28 @@ static int patch_cxt5066(struct hda_codec *codec)
spec->dell_automute = 1;
break;
case CXT5066_OLPC_XO_1_5:
- codec->patch_ops.unsol_event = cxt5066_unsol_event;
+ codec->patch_ops.init = cxt5066_olpc_init;
+ codec->patch_ops.unsol_event = cxt5066_olpc_unsol_event;
spec->init_verbs[0] = cxt5066_init_verbs_olpc;
spec->mixers[spec->num_mixers++] = cxt5066_mixer_master_olpc;
+ spec->mixers[spec->num_mixers++] = cxt5066_mixer_olpc_dc;
spec->mixers[spec->num_mixers++] = cxt5066_mixers;
spec->port_d_mode = 0;
- spec->ext_mic_bias = CXT5066_OLPC_EXT_MIC_BIAS;
+ spec->mic_boost = 3; /* default 30dB gain */
/* no S/PDIF out */
spec->multiout.dig_out_nid = 0;
/* input source automatically selected */
spec->input_mux = NULL;
+
+ /* our capture hooks which allow us to turn on the microphone LED
+ * at the right time */
+ spec->capture_prepare = cxt5066_olpc_capture_prepare;
+ spec->capture_cleanup = cxt5066_olpc_capture_cleanup;
break;
case CXT5066_DELL_VOSTO:
+ codec->patch_ops.init = cxt5066_init;
codec->patch_ops.unsol_event = cxt5066_vostro_event;
spec->init_verbs[0] = cxt5066_init_verbs_vostro;
spec->mixers[spec->num_mixers++] = cxt5066_mixer_master_olpc;
@@ -2532,6 +2793,7 @@ static int patch_cxt5066(struct hda_codec *codec)
spec->mixers[spec->num_mixers++] = cxt5066_vostro_mixers;
spec->port_d_mode = 0;
spec->dell_vostro = 1;
+ spec->mic_boost = 3; /* default 30dB gain */
snd_hda_attach_beep_device(codec, 0x13);
/* no S/PDIF out */
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index aeb23ef6afe5..141ff446104a 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -338,7 +338,7 @@ struct alc_spec {
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, int power);
+ void (*power_hook)(struct hda_codec *codec);
#endif
/* for pin sensing */
@@ -391,7 +391,7 @@ struct alc_config_preset {
void (*init_hook)(struct hda_codec *);
#ifdef CONFIG_SND_HDA_POWER_SAVE
struct hda_amp_list *loopbacks;
- void (*power_hook)(struct hda_codec *codec, int power);
+ void (*power_hook)(struct hda_codec *codec);
#endif
};
@@ -1835,16 +1835,6 @@ static void alc889_acer_aspire_8930g_setup(struct hda_codec *codec)
spec->autocfg.speaker_pins[2] = 0x1b;
}
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static void alc889_power_eapd(struct hda_codec *codec, int power)
-{
- snd_hda_codec_write(codec, 0x14, 0,
- AC_VERB_SET_EAPD_BTLENABLE, power ? 2 : 0);
- snd_hda_codec_write(codec, 0x15, 0,
- AC_VERB_SET_EAPD_BTLENABLE, power ? 2 : 0);
-}
-#endif
-
/*
* ALC880 3-stack model
*
@@ -2548,8 +2538,10 @@ static int alc_build_controls(struct hda_codec *codec)
if (!kctl)
kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
for (i = 0; kctl && i < kctl->count; i++) {
- err = snd_hda_add_nids(codec, kctl, i, spec->capsrc_nids,
- spec->input_mux->num_items);
+ hda_nid_t *nids = spec->capsrc_nids;
+ if (!nids)
+ nids = spec->adc_nids;
+ err = snd_hda_add_nid(codec, kctl, i, nids[i]);
if (err < 0)
return err;
}
@@ -3691,6 +3683,11 @@ static int alc_build_pcms(struct hda_codec *codec)
return 0;
}
+static inline void alc_shutup(struct hda_codec *codec)
+{
+ snd_hda_shutup_pins(codec);
+}
+
static void alc_free_kctls(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
@@ -3711,17 +3708,47 @@ static void alc_free(struct hda_codec *codec)
if (!spec)
return;
+ alc_shutup(codec);
alc_free_kctls(codec);
kfree(spec);
snd_hda_detach_beep_device(codec);
}
#ifdef CONFIG_SND_HDA_POWER_SAVE
+static void alc_power_eapd(struct hda_codec *codec)
+{
+ /* We currently only handle front, HP */
+ switch (codec->vendor_id) {
+ case 0x10ec0260:
+ snd_hda_codec_write(codec, 0x0f, 0,
+ AC_VERB_SET_EAPD_BTLENABLE, 0x00);
+ snd_hda_codec_write(codec, 0x10, 0,
+ AC_VERB_SET_EAPD_BTLENABLE, 0x00);
+ break;
+ case 0x10ec0262:
+ case 0x10ec0267:
+ case 0x10ec0268:
+ case 0x10ec0269:
+ case 0x10ec0272:
+ case 0x10ec0660:
+ case 0x10ec0662:
+ case 0x10ec0663:
+ case 0x10ec0862:
+ case 0x10ec0889:
+ snd_hda_codec_write(codec, 0x14, 0,
+ AC_VERB_SET_EAPD_BTLENABLE, 0x00);
+ snd_hda_codec_write(codec, 0x15, 0,
+ AC_VERB_SET_EAPD_BTLENABLE, 0x00);
+ break;
+ }
+}
+
static int alc_suspend(struct hda_codec *codec, pm_message_t state)
{
struct alc_spec *spec = codec->spec;
+ alc_shutup(codec);
if (spec && spec->power_hook)
- spec->power_hook(codec, 0);
+ spec->power_hook(codec);
return 0;
}
#endif
@@ -3729,16 +3756,9 @@ static int alc_suspend(struct hda_codec *codec, pm_message_t state)
#ifdef SND_HDA_NEEDS_RESUME
static int alc_resume(struct hda_codec *codec)
{
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- struct alc_spec *spec = codec->spec;
-#endif
codec->patch_ops.init(codec);
snd_hda_codec_resume_amp(codec);
snd_hda_codec_resume_cache(codec);
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- if (spec && spec->power_hook)
- spec->power_hook(codec, 1);
-#endif
return 0;
}
#endif
@@ -3758,6 +3778,7 @@ static struct hda_codec_ops alc_patch_ops = {
.suspend = alc_suspend,
.check_power_status = alc_check_power_status,
#endif
+ .reboot_notify = alc_shutup,
};
@@ -9538,7 +9559,7 @@ static struct alc_config_preset alc882_presets[] = {
.setup = alc889_acer_aspire_8930g_setup,
.init_hook = alc_automute_amp,
#ifdef CONFIG_SND_HDA_POWER_SAVE
- .power_hook = alc889_power_eapd,
+ .power_hook = alc_power_eapd,
#endif
},
[ALC888_ACER_ASPIRE_7730G] = {
@@ -14975,9 +14996,13 @@ static int patch_alc861(struct hda_codec *codec)
spec->vmaster_nid = 0x03;
codec->patch_ops = alc_patch_ops;
- if (board_config == ALC861_AUTO)
+ if (board_config == ALC861_AUTO) {
spec->init_hook = alc861_auto_init;
#ifdef CONFIG_SND_HDA_POWER_SAVE
+ spec->power_hook = alc_power_eapd;
+#endif
+ }
+#ifdef CONFIG_SND_HDA_POWER_SAVE
if (!spec->loopback.amplist)
spec->loopback.amplist = alc861_loopbacks;
#endif
diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c
index 74d5d333ed6c..e28c810bc00c 100644
--- a/sound/pci/hda/patch_sigmatel.c
+++ b/sound/pci/hda/patch_sigmatel.c
@@ -4159,34 +4159,52 @@ static void stac92xx_power_down(struct hda_codec *codec)
static void stac_toggle_power_map(struct hda_codec *codec, hda_nid_t nid,
int enable);
+static inline int get_int_hint(struct hda_codec *codec, const char *key,
+ int *valp)
+{
+ const char *p;
+ p = snd_hda_get_hint(codec, key);
+ if (p) {
+ unsigned long val;
+ if (!strict_strtoul(p, 0, &val)) {
+ *valp = val;
+ return 1;
+ }
+ }
+ return 0;
+}
+
/* override some hints from the hwdep entry */
static void stac_store_hints(struct hda_codec *codec)
{
struct sigmatel_spec *spec = codec->spec;
- const char *p;
int val;
val = snd_hda_get_bool_hint(codec, "hp_detect");
if (val >= 0)
spec->hp_detect = val;
- p = snd_hda_get_hint(codec, "gpio_mask");
- if (p) {
- spec->gpio_mask = simple_strtoul(p, NULL, 0);
+ if (get_int_hint(codec, "gpio_mask", &spec->gpio_mask)) {
spec->eapd_mask = spec->gpio_dir = spec->gpio_data =
spec->gpio_mask;
}
- p = snd_hda_get_hint(codec, "gpio_dir");
- if (p)
- spec->gpio_dir = simple_strtoul(p, NULL, 0) & spec->gpio_mask;
- p = snd_hda_get_hint(codec, "gpio_data");
- if (p)
- spec->gpio_data = simple_strtoul(p, NULL, 0) & spec->gpio_mask;
- p = snd_hda_get_hint(codec, "eapd_mask");
- if (p)
- spec->eapd_mask = simple_strtoul(p, NULL, 0) & spec->gpio_mask;
+ if (get_int_hint(codec, "gpio_dir", &spec->gpio_dir))
+ spec->gpio_mask &= spec->gpio_mask;
+ if (get_int_hint(codec, "gpio_data", &spec->gpio_data))
+ spec->gpio_dir &= spec->gpio_mask;
+ if (get_int_hint(codec, "eapd_mask", &spec->eapd_mask))
+ spec->eapd_mask &= spec->gpio_mask;
+ if (get_int_hint(codec, "gpio_mute", &spec->gpio_mute))
+ spec->gpio_mute &= spec->gpio_mask;
val = snd_hda_get_bool_hint(codec, "eapd_switch");
if (val >= 0)
spec->eapd_switch = val;
+ get_int_hint(codec, "gpio_led_polarity", &spec->gpio_led_polarity);
+ if (get_int_hint(codec, "gpio_led", &spec->gpio_led)) {
+ spec->gpio_mask |= spec->gpio_led;
+ spec->gpio_dir |= spec->gpio_led;
+ if (spec->gpio_led_polarity)
+ spec->gpio_data |= spec->gpio_led;
+ }
}
static int stac92xx_init(struct hda_codec *codec)
@@ -4371,18 +4389,8 @@ static void stac92xx_free_kctls(struct hda_codec *codec)
static void stac92xx_shutup(struct hda_codec *codec)
{
struct sigmatel_spec *spec = codec->spec;
- int i;
- hda_nid_t nid;
- /* reset each pin before powering down DAC/ADC to avoid click noise */
- nid = codec->start_nid;
- for (i = 0; i < codec->num_nodes; i++, nid++) {
- unsigned int wcaps = get_wcaps(codec, nid);
- unsigned int wid_type = get_wcaps_type(wcaps);
- if (wid_type == AC_WID_PIN)
- snd_hda_codec_read(codec, nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, 0);
- }
+ snd_hda_shutup_pins(codec);
if (spec->eapd_mask)
stac_gpio_set(codec, spec->gpio_mask,
@@ -5406,6 +5414,54 @@ static int stac92hd71bxx_connected_smuxes(struct hda_codec *codec,
return 0;
}
+/* HP dv7 bass switch - GPIO5 */
+#define stac_hp_bass_gpio_info snd_ctl_boolean_mono_info
+static int stac_hp_bass_gpio_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct sigmatel_spec *spec = codec->spec;
+ ucontrol->value.integer.value[0] = !!(spec->gpio_data & 0x20);
+ return 0;
+}
+
+static int stac_hp_bass_gpio_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct sigmatel_spec *spec = codec->spec;
+ unsigned int gpio_data;
+
+ gpio_data = (spec->gpio_data & ~0x20) |
+ (ucontrol->value.integer.value[0] ? 0x20 : 0);
+ if (gpio_data == spec->gpio_data)
+ return 0;
+ spec->gpio_data = gpio_data;
+ stac_gpio_set(codec, spec->gpio_mask, spec->gpio_dir, spec->gpio_data);
+ return 1;
+}
+
+static struct snd_kcontrol_new stac_hp_bass_sw_ctrl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .info = stac_hp_bass_gpio_info,
+ .get = stac_hp_bass_gpio_get,
+ .put = stac_hp_bass_gpio_put,
+};
+
+static int stac_add_hp_bass_switch(struct hda_codec *codec)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ if (!stac_control_new(spec, &stac_hp_bass_sw_ctrl,
+ "Bass Speaker Playback Switch", 0))
+ return -ENOMEM;
+
+ spec->gpio_mask |= 0x20;
+ spec->gpio_dir |= 0x20;
+ spec->gpio_data |= 0x20;
+ return 0;
+}
+
static int patch_stac92hd71bxx(struct hda_codec *codec)
{
struct sigmatel_spec *spec;
@@ -5646,6 +5702,15 @@ again:
return err;
}
+ /* enable bass on HP dv7 */
+ if (spec->board_config == STAC_HP_DV5) {
+ unsigned int cap;
+ cap = snd_hda_param_read(codec, 0x1, AC_PAR_GPIO_CAP);
+ cap &= AC_GPIO_IO_COUNT;
+ if (cap >= 6)
+ stac_add_hp_bass_switch(codec);
+ }
+
codec->proc_widget_hook = stac92hd7x_proc_hook;
return 0;
diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c
index de4839e46762..9ddc37300f6b 100644
--- a/sound/pci/hda/patch_via.c
+++ b/sound/pci/hda/patch_via.c
@@ -1907,8 +1907,7 @@ static int via_build_controls(struct hda_codec *codec)
/* assign Capture Source enums to NID */
kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
for (i = 0; kctl && i < kctl->count; i++) {
- err = snd_hda_add_nids(codec, kctl, i, spec->mux_nids,
- spec->input_mux->num_items);
+ err = snd_hda_add_nid(codec, kctl, i, spec->mux_nids[i]);
if (err < 0)
return err;
}