Merge branch 'topic/hda' into for-linus

This commit is contained in:
Takashi Iwai 2009-03-24 00:36:09 +01:00
commit e7bfbb0215
19 changed files with 2616 additions and 1479 deletions

View file

@ -741,6 +741,9 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
model - force the model name model - force the model name
position_fix - Fix DMA pointer (0 = auto, 1 = use LPIB, 2 = POSBUF) position_fix - Fix DMA pointer (0 = auto, 1 = use LPIB, 2 = POSBUF)
probe_mask - Bitmask to probe codecs (default = -1, meaning all slots) probe_mask - Bitmask to probe codecs (default = -1, meaning all slots)
When the bit 8 (0x100) is set, the lower 8 bits are used
as the "fixed" codec slots; i.e. the driver probes the
slots regardless what hardware reports back
probe_only - Only probing and no codec initialization (default=off); probe_only - Only probing and no codec initialization (default=off);
Useful to check the initial codec status for debugging Useful to check the initial codec status for debugging
bdl_pos_adj - Specifies the DMA IRQ timing delay in samples. bdl_pos_adj - Specifies the DMA IRQ timing delay in samples.

View file

@ -56,6 +56,7 @@ ALC262
sony-assamd Sony ASSAMD sony-assamd Sony ASSAMD
toshiba-s06 Toshiba S06 toshiba-s06 Toshiba S06
toshiba-rx1 Toshiba RX1 toshiba-rx1 Toshiba RX1
tyan Tyan Thunder n6650W (S2915-E)
ultra Samsung Q1 Ultra Vista model ultra Samsung Q1 Ultra Vista model
lenovo-3000 Lenovo 3000 y410 lenovo-3000 Lenovo 3000 y410
nec NEC Versa S9100 nec NEC Versa S9100
@ -261,6 +262,8 @@ Conexant 5051
============= =============
laptop Basic Laptop config (default) laptop Basic Laptop config (default)
hp HP Spartan laptop hp HP Spartan laptop
hp-dv6736 HP dv6736
lenovo-x200 Lenovo X200 laptop
STAC9200 STAC9200
======== ========
@ -278,6 +281,7 @@ STAC9200
gateway-m4 Gateway laptops with EAPD control gateway-m4 Gateway laptops with EAPD control
gateway-m4-2 Gateway laptops with EAPD control gateway-m4-2 Gateway laptops with EAPD control
panasonic Panasonic CF-74 panasonic Panasonic CF-74
auto BIOS setup (default)
STAC9205/9254 STAC9205/9254
============= =============
@ -285,6 +289,8 @@ STAC9205/9254
dell-m42 Dell (unknown) dell-m42 Dell (unknown)
dell-m43 Dell Precision dell-m43 Dell Precision
dell-m44 Dell Inspiron dell-m44 Dell Inspiron
eapd Keep EAPD on (e.g. Gateway T1616)
auto BIOS setup (default)
STAC9220/9221 STAC9220/9221
============= =============
@ -308,6 +314,7 @@ STAC9220/9221
dell-d82 Dell (unknown) dell-d82 Dell (unknown)
dell-m81 Dell (unknown) dell-m81 Dell (unknown)
dell-m82 Dell XPS M1210 dell-m82 Dell XPS M1210
auto BIOS setup (default)
STAC9202/9250/9251 STAC9202/9250/9251
================== ==================
@ -319,6 +326,7 @@ STAC9202/9250/9251
m3 Some Gateway MX series laptops m3 Some Gateway MX series laptops
m5 Some Gateway MX series laptops (MP6954) m5 Some Gateway MX series laptops (MP6954)
m6 Some Gateway NX series laptops m6 Some Gateway NX series laptops
auto BIOS setup (default)
STAC9227/9228/9229/927x STAC9227/9228/9229/927x
======================= =======================
@ -328,6 +336,7 @@ STAC9227/9228/9229/927x
5stack D965 5stack + SPDIF 5stack D965 5stack + SPDIF
dell-3stack Dell Dimension E520 dell-3stack Dell Dimension E520
dell-bios Fixes with Dell BIOS setup dell-bios Fixes with Dell BIOS setup
auto BIOS setup (default)
STAC92HD71B* STAC92HD71B*
============ ============
@ -335,7 +344,10 @@ STAC92HD71B*
dell-m4-1 Dell desktops dell-m4-1 Dell desktops
dell-m4-2 Dell desktops dell-m4-2 Dell desktops
dell-m4-3 Dell desktops dell-m4-3 Dell desktops
hp-m4 HP dv laptops hp-m4 HP mini 1000
hp-dv5 HP dv series
hp-hdx HP HDX series
auto BIOS setup (default)
STAC92HD73* STAC92HD73*
=========== ===========
@ -345,13 +357,16 @@ STAC92HD73*
dell-m6-dmic Dell desktops/laptops with digital mics dell-m6-dmic Dell desktops/laptops with digital mics
dell-m6 Dell desktops/laptops with both type of mics dell-m6 Dell desktops/laptops with both type of mics
dell-eq Dell desktops/laptops dell-eq Dell desktops/laptops
auto BIOS setup (default)
STAC92HD83* STAC92HD83*
=========== ===========
ref Reference board ref Reference board
mic-ref Reference board with power managment for ports mic-ref Reference board with power managment for ports
dell-s14 Dell laptop
auto BIOS setup (default)
STAC9872 STAC9872
======== ========
vaio Setup for VAIO FE550G/SZ110 vaio VAIO laptop without SPDIF
vaio-ar Setup for VAIO AR auto BIOS setup (default)

View file

@ -109,6 +109,13 @@ slot, pass `probe_mask=1`. For the first and the third slots, pass
Since 2.6.29 kernel, the driver has a more robust probing method, so Since 2.6.29 kernel, the driver has a more robust probing method, so
this error might happen rarely, though. this error might happen rarely, though.
On a machine with a broken BIOS, sometimes you need to force the
driver to probe the codec slots the hardware doesn't report for use.
In such a case, turn the bit 8 (0x100) of `probe_mask` option on.
Then the rest 8 bits are passed as the codec slots to probe
unconditionally. For example, `probe_mask=0x103` will force to probe
the codec slots 0 and 1 no matter what the hardware reports.
Interrupt Handling Interrupt Handling
~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~
@ -358,10 +365,26 @@ modelname::
to this file. to this file.
init_verbs:: init_verbs::
The extra verbs to execute at initialization. You can add a verb by The extra verbs to execute at initialization. You can add a verb by
writing to this file. Pass tree numbers, nid, verb and parameter. writing to this file. Pass three numbers: nid, verb and parameter
(separated with a space).
hints:: hints::
Shows hint strings for codec parsers for any use. Right now it's Shows / stores hint strings for codec parsers for any use.
not used. Its format is `key = value`. For example, passing `hp_detect = yes`
to IDT/STAC codec parser will result in the disablement of the
headphone detection.
init_pin_configs::
Shows the initial pin default config values set by BIOS.
driver_pin_configs::
Shows the pin default values set by the codec parser explicitly.
This doesn't show all pin values but only the changed values by
the parser. That is, if the parser doesn't change the pin default
config values by itself, this will contain nothing.
user_pin_configs::
Shows the pin default config values to override the BIOS setup.
Writing this (with two numbers, NID and value) appends the new
value. The given will be used instead of the initial BIOS value at
the next reconfiguration time. Note that this config will override
even the driver pin configs, too.
reconfig:: reconfig::
Triggers the codec re-configuration. When any value is written to Triggers the codec re-configuration. When any value is written to
this file, the driver re-initialize and parses the codec tree this file, the driver re-initialize and parses the codec tree
@ -371,6 +394,14 @@ clear::
Resets the codec, removes the mixer elements and PCM stuff of the Resets the codec, removes the mixer elements and PCM stuff of the
specified codec, and clear all init verbs and hints. specified codec, and clear all init verbs and hints.
For example, when you want to change the pin default configuration
value of the pin widget 0x14 to 0x9993013f, and let the driver
re-configure based on that state, run like below:
------------------------------------------------------------------------
# echo 0x14 0x9993013f > /sys/class/sound/hwC0D0/user_pin_configs
# echo 1 > /sys/class/sound/hwC0D0/reconfig
------------------------------------------------------------------------
Power-Saving Power-Saving
~~~~~~~~~~~~ ~~~~~~~~~~~~
@ -461,6 +492,16 @@ run with `--no-upload` option, and attach the generated file.
There are some other useful options. See `--help` option output for There are some other useful options. See `--help` option output for
details. details.
When a probe error occurs or when the driver obviously assigns a
mismatched model, it'd be helpful to load the driver with
`probe_only=1` option (at best after the cold reboot) and run
alsa-info at this state. With this option, the driver won't configure
the mixer and PCM but just tries to probe the codec slot. After
probing, the proc file is available, so you can get the raw codec
information before modified by the driver. Of course, the driver
isn't usable with `probe_only=1`. But you can continue the
configuration via hwdep sysfs file if hda-reconfig option is enabled.
hda-verb hda-verb
~~~~~~~~ ~~~~~~~~

View file

@ -2112,6 +2112,8 @@
#define PCI_DEVICE_ID_MELLANOX_SINAI_OLD 0x5e8c #define PCI_DEVICE_ID_MELLANOX_SINAI_OLD 0x5e8c
#define PCI_DEVICE_ID_MELLANOX_SINAI 0x6274 #define PCI_DEVICE_ID_MELLANOX_SINAI 0x6274
#define PCI_VENDOR_ID_DFI 0x15bd
#define PCI_VENDOR_ID_QUICKNET 0x15e2 #define PCI_VENDOR_ID_QUICKNET 0x15e2
#define PCI_DEVICE_ID_QUICKNET_XJ 0x0500 #define PCI_DEVICE_ID_QUICKNET_XJ 0x0500

View file

@ -138,6 +138,7 @@ void snd_hda_detach_beep_device(struct hda_codec *codec)
input_unregister_device(beep->dev); input_unregister_device(beep->dev);
kfree(beep); kfree(beep);
codec->beep = NULL;
} }
} }
EXPORT_SYMBOL_HDA(snd_hda_detach_beep_device); EXPORT_SYMBOL_HDA(snd_hda_detach_beep_device);

View file

@ -39,7 +39,7 @@ struct hda_beep {
int snd_hda_attach_beep_device(struct hda_codec *codec, int nid); int snd_hda_attach_beep_device(struct hda_codec *codec, int nid);
void snd_hda_detach_beep_device(struct hda_codec *codec); void snd_hda_detach_beep_device(struct hda_codec *codec);
#else #else
#define snd_hda_attach_beep_device(...) #define snd_hda_attach_beep_device(...) 0
#define snd_hda_detach_beep_device(...) #define snd_hda_detach_beep_device(...)
#endif #endif
#endif #endif

View file

@ -647,9 +647,9 @@ static void /*__devinit*/ setup_fg_nodes(struct hda_codec *codec)
total_nodes = snd_hda_get_sub_nodes(codec, AC_NODE_ROOT, &nid); total_nodes = snd_hda_get_sub_nodes(codec, AC_NODE_ROOT, &nid);
for (i = 0; i < total_nodes; i++, nid++) { for (i = 0; i < total_nodes; i++, nid++) {
unsigned int func; codec->function_id = snd_hda_param_read(codec, nid,
func = snd_hda_param_read(codec, nid, AC_PAR_FUNCTION_TYPE); AC_PAR_FUNCTION_TYPE) & 0xff;
switch (func & 0xff) { switch (codec->function_id) {
case AC_GRP_AUDIO_FUNCTION: case AC_GRP_AUDIO_FUNCTION:
codec->afg = nid; codec->afg = nid;
break; break;
@ -682,11 +682,140 @@ static int read_widget_caps(struct hda_codec *codec, hda_nid_t fg_node)
return 0; return 0;
} }
/* read all pin default configurations and save codec->init_pins */
static int read_pin_defaults(struct hda_codec *codec)
{
int i;
hda_nid_t nid = codec->start_nid;
for (i = 0; i < codec->num_nodes; i++, nid++) {
struct hda_pincfg *pin;
unsigned int wcaps = get_wcaps(codec, nid);
unsigned int wid_type = (wcaps & AC_WCAP_TYPE) >>
AC_WCAP_TYPE_SHIFT;
if (wid_type != AC_WID_PIN)
continue;
pin = snd_array_new(&codec->init_pins);
if (!pin)
return -ENOMEM;
pin->nid = nid;
pin->cfg = snd_hda_codec_read(codec, nid, 0,
AC_VERB_GET_CONFIG_DEFAULT, 0);
}
return 0;
}
/* look up the given pin config list and return the item matching with NID */
static struct hda_pincfg *look_up_pincfg(struct hda_codec *codec,
struct snd_array *array,
hda_nid_t nid)
{
int i;
for (i = 0; i < array->used; i++) {
struct hda_pincfg *pin = snd_array_elem(array, i);
if (pin->nid == nid)
return pin;
}
return NULL;
}
/* write a config value for the given NID */
static void set_pincfg(struct hda_codec *codec, hda_nid_t nid,
unsigned int cfg)
{
int i;
for (i = 0; i < 4; i++) {
snd_hda_codec_write(codec, nid, 0,
AC_VERB_SET_CONFIG_DEFAULT_BYTES_0 + i,
cfg & 0xff);
cfg >>= 8;
}
}
/* set the current pin config value for the given NID.
* the value is cached, and read via snd_hda_codec_get_pincfg()
*/
int snd_hda_add_pincfg(struct hda_codec *codec, struct snd_array *list,
hda_nid_t nid, unsigned int cfg)
{
struct hda_pincfg *pin;
unsigned int oldcfg;
oldcfg = snd_hda_codec_get_pincfg(codec, nid);
pin = look_up_pincfg(codec, list, nid);
if (!pin) {
pin = snd_array_new(list);
if (!pin)
return -ENOMEM;
pin->nid = nid;
}
pin->cfg = cfg;
/* change only when needed; e.g. if the pincfg is already present
* in user_pins[], don't write it
*/
cfg = snd_hda_codec_get_pincfg(codec, nid);
if (oldcfg != cfg)
set_pincfg(codec, nid, cfg);
return 0;
}
int snd_hda_codec_set_pincfg(struct hda_codec *codec,
hda_nid_t nid, unsigned int cfg)
{
return snd_hda_add_pincfg(codec, &codec->driver_pins, nid, cfg);
}
EXPORT_SYMBOL_HDA(snd_hda_codec_set_pincfg);
/* get the current pin config value of the given pin NID */
unsigned int snd_hda_codec_get_pincfg(struct hda_codec *codec, hda_nid_t nid)
{
struct hda_pincfg *pin;
#ifdef CONFIG_SND_HDA_HWDEP
pin = look_up_pincfg(codec, &codec->user_pins, nid);
if (pin)
return pin->cfg;
#endif
pin = look_up_pincfg(codec, &codec->driver_pins, nid);
if (pin)
return pin->cfg;
pin = look_up_pincfg(codec, &codec->init_pins, nid);
if (pin)
return pin->cfg;
return 0;
}
EXPORT_SYMBOL_HDA(snd_hda_codec_get_pincfg);
/* restore all current pin configs */
static void restore_pincfgs(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);
set_pincfg(codec, pin->nid,
snd_hda_codec_get_pincfg(codec, pin->nid));
}
}
static void init_hda_cache(struct hda_cache_rec *cache, static void init_hda_cache(struct hda_cache_rec *cache,
unsigned int record_size); unsigned int record_size);
static void free_hda_cache(struct hda_cache_rec *cache); static void free_hda_cache(struct hda_cache_rec *cache);
/* restore the initial pin cfgs and release all pincfg lists */
static void restore_init_pincfgs(struct hda_codec *codec)
{
/* first free driver_pins and user_pins, then call restore_pincfg
* so that only the values in init_pins are restored
*/
snd_array_free(&codec->driver_pins);
#ifdef CONFIG_SND_HDA_HWDEP
snd_array_free(&codec->user_pins);
#endif
restore_pincfgs(codec);
snd_array_free(&codec->init_pins);
}
/* /*
* codec destructor * codec destructor
*/ */
@ -694,6 +823,7 @@ static void snd_hda_codec_free(struct hda_codec *codec)
{ {
if (!codec) if (!codec)
return; return;
restore_init_pincfgs(codec);
#ifdef CONFIG_SND_HDA_POWER_SAVE #ifdef CONFIG_SND_HDA_POWER_SAVE
cancel_delayed_work(&codec->power_work); cancel_delayed_work(&codec->power_work);
flush_workqueue(codec->bus->workq); flush_workqueue(codec->bus->workq);
@ -712,6 +842,9 @@ static void snd_hda_codec_free(struct hda_codec *codec)
kfree(codec); kfree(codec);
} }
static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg,
unsigned int power_state);
/** /**
* snd_hda_codec_new - create a HDA codec * snd_hda_codec_new - create a HDA codec
* @bus: the bus to assign * @bus: the bus to assign
@ -751,6 +884,8 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr
init_hda_cache(&codec->amp_cache, sizeof(struct hda_amp_info)); init_hda_cache(&codec->amp_cache, sizeof(struct hda_amp_info));
init_hda_cache(&codec->cmd_cache, sizeof(struct hda_cache_head)); init_hda_cache(&codec->cmd_cache, sizeof(struct hda_cache_head));
snd_array_init(&codec->mixers, sizeof(struct snd_kcontrol *), 32); snd_array_init(&codec->mixers, sizeof(struct snd_kcontrol *), 32);
snd_array_init(&codec->init_pins, sizeof(struct hda_pincfg), 16);
snd_array_init(&codec->driver_pins, sizeof(struct hda_pincfg), 16);
if (codec->bus->modelname) { if (codec->bus->modelname) {
codec->modelname = kstrdup(codec->bus->modelname, GFP_KERNEL); codec->modelname = kstrdup(codec->bus->modelname, GFP_KERNEL);
if (!codec->modelname) { if (!codec->modelname) {
@ -787,15 +922,18 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr
setup_fg_nodes(codec); setup_fg_nodes(codec);
if (!codec->afg && !codec->mfg) { if (!codec->afg && !codec->mfg) {
snd_printdd("hda_codec: no AFG or MFG node found\n"); snd_printdd("hda_codec: no AFG or MFG node found\n");
snd_hda_codec_free(codec); err = -ENODEV;
return -ENODEV; goto error;
} }
if (read_widget_caps(codec, codec->afg ? codec->afg : codec->mfg) < 0) { err = read_widget_caps(codec, codec->afg ? codec->afg : codec->mfg);
if (err < 0) {
snd_printk(KERN_ERR "hda_codec: cannot malloc\n"); snd_printk(KERN_ERR "hda_codec: cannot malloc\n");
snd_hda_codec_free(codec); goto error;
return -ENOMEM;
} }
err = read_pin_defaults(codec);
if (err < 0)
goto error;
if (!codec->subsystem_id) { if (!codec->subsystem_id) {
hda_nid_t nid = codec->afg ? codec->afg : codec->mfg; hda_nid_t nid = codec->afg ? codec->afg : codec->mfg;
@ -806,12 +944,15 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr
if (bus->modelname) if (bus->modelname)
codec->modelname = kstrdup(bus->modelname, GFP_KERNEL); codec->modelname = kstrdup(bus->modelname, GFP_KERNEL);
/* power-up all before initialization */
hda_set_power_state(codec,
codec->afg ? codec->afg : codec->mfg,
AC_PWRST_D0);
if (do_init) { if (do_init) {
err = snd_hda_codec_configure(codec); err = snd_hda_codec_configure(codec);
if (err < 0) { if (err < 0)
snd_hda_codec_free(codec); goto error;
return err;
}
} }
snd_hda_codec_proc_new(codec); snd_hda_codec_proc_new(codec);
@ -824,6 +965,10 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr
if (codecp) if (codecp)
*codecp = codec; *codecp = codec;
return 0; return 0;
error:
snd_hda_codec_free(codec);
return err;
} }
EXPORT_SYMBOL_HDA(snd_hda_codec_new); EXPORT_SYMBOL_HDA(snd_hda_codec_new);
@ -907,6 +1052,7 @@ EXPORT_SYMBOL_HDA(snd_hda_codec_cleanup_stream);
/* FIXME: more better hash key? */ /* FIXME: more better hash key? */
#define HDA_HASH_KEY(nid,dir,idx) (u32)((nid) + ((idx) << 16) + ((dir) << 24)) #define HDA_HASH_KEY(nid,dir,idx) (u32)((nid) + ((idx) << 16) + ((dir) << 24))
#define HDA_HASH_PINCAP_KEY(nid) (u32)((nid) + (0x02 << 24))
#define INFO_AMP_CAPS (1<<0) #define INFO_AMP_CAPS (1<<0)
#define INFO_AMP_VOL(ch) (1 << (1 + (ch))) #define INFO_AMP_VOL(ch) (1 << (1 + (ch)))
@ -997,6 +1143,21 @@ int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
} }
EXPORT_SYMBOL_HDA(snd_hda_override_amp_caps); EXPORT_SYMBOL_HDA(snd_hda_override_amp_caps);
u32 snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t nid)
{
struct hda_amp_info *info;
info = get_alloc_amp_hash(codec, HDA_HASH_PINCAP_KEY(nid));
if (!info)
return 0;
if (!info->head.val) {
info->amp_caps = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
info->head.val |= INFO_AMP_CAPS;
}
return info->amp_caps;
}
EXPORT_SYMBOL_HDA(snd_hda_query_pin_caps);
/* /*
* read the current volume to info * read the current volume to info
* if the cache exists, read the cache value. * if the cache exists, read the cache value.
@ -1120,6 +1281,7 @@ int snd_hda_mixer_amp_volume_info(struct snd_kcontrol *kcontrol,
u16 nid = get_amp_nid(kcontrol); u16 nid = get_amp_nid(kcontrol);
u8 chs = get_amp_channels(kcontrol); u8 chs = get_amp_channels(kcontrol);
int dir = get_amp_direction(kcontrol); int dir = get_amp_direction(kcontrol);
unsigned int ofs = get_amp_offset(kcontrol);
u32 caps; u32 caps;
caps = query_amp_caps(codec, nid, dir); caps = query_amp_caps(codec, nid, dir);
@ -1131,6 +1293,8 @@ int snd_hda_mixer_amp_volume_info(struct snd_kcontrol *kcontrol,
kcontrol->id.name); kcontrol->id.name);
return -EINVAL; return -EINVAL;
} }
if (ofs < caps)
caps -= ofs;
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = chs == 3 ? 2 : 1; uinfo->count = chs == 3 ? 2 : 1;
uinfo->value.integer.min = 0; uinfo->value.integer.min = 0;
@ -1139,6 +1303,32 @@ int snd_hda_mixer_amp_volume_info(struct snd_kcontrol *kcontrol,
} }
EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_volume_info); EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_volume_info);
static inline unsigned int
read_amp_value(struct hda_codec *codec, hda_nid_t nid,
int ch, int dir, int idx, unsigned int ofs)
{
unsigned int val;
val = snd_hda_codec_amp_read(codec, nid, ch, dir, idx);
val &= HDA_AMP_VOLMASK;
if (val >= ofs)
val -= ofs;
else
val = 0;
return val;
}
static inline int
update_amp_value(struct hda_codec *codec, hda_nid_t nid,
int ch, int dir, int idx, unsigned int ofs,
unsigned int val)
{
if (val > 0)
val += ofs;
return snd_hda_codec_amp_update(codec, nid, ch, dir, idx,
HDA_AMP_VOLMASK, val);
}
int snd_hda_mixer_amp_volume_get(struct snd_kcontrol *kcontrol, int snd_hda_mixer_amp_volume_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol) struct snd_ctl_elem_value *ucontrol)
{ {
@ -1147,14 +1337,13 @@ int snd_hda_mixer_amp_volume_get(struct snd_kcontrol *kcontrol,
int chs = get_amp_channels(kcontrol); int chs = get_amp_channels(kcontrol);
int dir = get_amp_direction(kcontrol); int dir = get_amp_direction(kcontrol);
int idx = get_amp_index(kcontrol); int idx = get_amp_index(kcontrol);
unsigned int ofs = get_amp_offset(kcontrol);
long *valp = ucontrol->value.integer.value; long *valp = ucontrol->value.integer.value;
if (chs & 1) if (chs & 1)
*valp++ = snd_hda_codec_amp_read(codec, nid, 0, dir, idx) *valp++ = read_amp_value(codec, nid, 0, dir, idx, ofs);
& HDA_AMP_VOLMASK;
if (chs & 2) if (chs & 2)
*valp = snd_hda_codec_amp_read(codec, nid, 1, dir, idx) *valp = read_amp_value(codec, nid, 1, dir, idx, ofs);
& HDA_AMP_VOLMASK;
return 0; return 0;
} }
EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_volume_get); EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_volume_get);
@ -1167,18 +1356,17 @@ int snd_hda_mixer_amp_volume_put(struct snd_kcontrol *kcontrol,
int chs = get_amp_channels(kcontrol); int chs = get_amp_channels(kcontrol);
int dir = get_amp_direction(kcontrol); int dir = get_amp_direction(kcontrol);
int idx = get_amp_index(kcontrol); int idx = get_amp_index(kcontrol);
unsigned int ofs = get_amp_offset(kcontrol);
long *valp = ucontrol->value.integer.value; long *valp = ucontrol->value.integer.value;
int change = 0; int change = 0;
snd_hda_power_up(codec); snd_hda_power_up(codec);
if (chs & 1) { if (chs & 1) {
change = snd_hda_codec_amp_update(codec, nid, 0, dir, idx, change = update_amp_value(codec, nid, 0, dir, idx, ofs, *valp);
0x7f, *valp);
valp++; valp++;
} }
if (chs & 2) if (chs & 2)
change |= snd_hda_codec_amp_update(codec, nid, 1, dir, idx, change |= update_amp_value(codec, nid, 1, dir, idx, ofs, *valp);
0x7f, *valp);
snd_hda_power_down(codec); snd_hda_power_down(codec);
return change; return change;
} }
@ -1190,6 +1378,7 @@ int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag,
struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
hda_nid_t nid = get_amp_nid(kcontrol); hda_nid_t nid = get_amp_nid(kcontrol);
int dir = get_amp_direction(kcontrol); int dir = get_amp_direction(kcontrol);
unsigned int ofs = get_amp_offset(kcontrol);
u32 caps, val1, val2; u32 caps, val1, val2;
if (size < 4 * sizeof(unsigned int)) if (size < 4 * sizeof(unsigned int))
@ -1198,6 +1387,7 @@ int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag,
val2 = (caps & AC_AMPCAP_STEP_SIZE) >> AC_AMPCAP_STEP_SIZE_SHIFT; val2 = (caps & AC_AMPCAP_STEP_SIZE) >> AC_AMPCAP_STEP_SIZE_SHIFT;
val2 = (val2 + 1) * 25; val2 = (val2 + 1) * 25;
val1 = -((caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT); val1 = -((caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT);
val1 += ofs;
val1 = ((int)val1) * ((int)val2); val1 = ((int)val1) * ((int)val2);
if (put_user(SNDRV_CTL_TLVT_DB_SCALE, _tlv)) if (put_user(SNDRV_CTL_TLVT_DB_SCALE, _tlv))
return -EFAULT; return -EFAULT;
@ -1268,7 +1458,6 @@ int snd_hda_ctl_add(struct hda_codec *codec, struct snd_kcontrol *kctl)
} }
EXPORT_SYMBOL_HDA(snd_hda_ctl_add); EXPORT_SYMBOL_HDA(snd_hda_ctl_add);
#ifdef CONFIG_SND_HDA_RECONFIG
/* Clear all controls assigned to the given codec */ /* Clear all controls assigned to the given codec */
void snd_hda_ctls_clear(struct hda_codec *codec) void snd_hda_ctls_clear(struct hda_codec *codec)
{ {
@ -1279,9 +1468,52 @@ void snd_hda_ctls_clear(struct hda_codec *codec)
snd_array_free(&codec->mixers); snd_array_free(&codec->mixers);
} }
void snd_hda_codec_reset(struct hda_codec *codec) /* pseudo device locking
* toggle card->shutdown to allow/disallow the device access (as a hack)
*/
static int hda_lock_devices(struct snd_card *card)
{ {
int i; spin_lock(&card->files_lock);
if (card->shutdown) {
spin_unlock(&card->files_lock);
return -EINVAL;
}
card->shutdown = 1;
spin_unlock(&card->files_lock);
return 0;
}
static void hda_unlock_devices(struct snd_card *card)
{
spin_lock(&card->files_lock);
card->shutdown = 0;
spin_unlock(&card->files_lock);
}
int snd_hda_codec_reset(struct hda_codec *codec)
{
struct snd_card *card = codec->bus->card;
int i, pcm;
if (hda_lock_devices(card) < 0)
return -EBUSY;
/* check whether the codec isn't used by any mixer or PCM streams */
if (!list_empty(&card->ctl_files)) {
hda_unlock_devices(card);
return -EBUSY;
}
for (pcm = 0; pcm < codec->num_pcms; pcm++) {
struct hda_pcm *cpcm = &codec->pcm_info[pcm];
if (!cpcm->pcm)
continue;
if (cpcm->pcm->streams[0].substream_opened ||
cpcm->pcm->streams[1].substream_opened) {
hda_unlock_devices(card);
return -EBUSY;
}
}
/* OK, let it free */
#ifdef CONFIG_SND_HDA_POWER_SAVE #ifdef CONFIG_SND_HDA_POWER_SAVE
cancel_delayed_work(&codec->power_work); cancel_delayed_work(&codec->power_work);
@ -1291,8 +1523,7 @@ void snd_hda_codec_reset(struct hda_codec *codec)
/* relase PCMs */ /* relase PCMs */
for (i = 0; i < codec->num_pcms; i++) { for (i = 0; i < codec->num_pcms; i++) {
if (codec->pcm_info[i].pcm) { if (codec->pcm_info[i].pcm) {
snd_device_free(codec->bus->card, snd_device_free(card, codec->pcm_info[i].pcm);
codec->pcm_info[i].pcm);
clear_bit(codec->pcm_info[i].device, clear_bit(codec->pcm_info[i].device,
codec->bus->pcm_dev_bits); codec->bus->pcm_dev_bits);
} }
@ -1305,13 +1536,22 @@ void snd_hda_codec_reset(struct hda_codec *codec)
free_hda_cache(&codec->cmd_cache); free_hda_cache(&codec->cmd_cache);
init_hda_cache(&codec->amp_cache, sizeof(struct hda_amp_info)); init_hda_cache(&codec->amp_cache, sizeof(struct hda_amp_info));
init_hda_cache(&codec->cmd_cache, sizeof(struct hda_cache_head)); init_hda_cache(&codec->cmd_cache, sizeof(struct hda_cache_head));
/* free only driver_pins so that init_pins + user_pins are restored */
snd_array_free(&codec->driver_pins);
restore_pincfgs(codec);
codec->num_pcms = 0; codec->num_pcms = 0;
codec->pcm_info = NULL; codec->pcm_info = NULL;
codec->preset = NULL; codec->preset = NULL;
memset(&codec->patch_ops, 0, sizeof(codec->patch_ops));
codec->slave_dig_outs = NULL;
codec->spdif_status_reset = 0;
module_put(codec->owner); module_put(codec->owner);
codec->owner = NULL; codec->owner = NULL;
/* allow device access again */
hda_unlock_devices(card);
return 0;
} }
#endif /* CONFIG_SND_HDA_RECONFIG */
/* create a virtual master control and add slaves */ /* create a virtual master control and add slaves */
int snd_hda_add_vmaster(struct hda_codec *codec, char *name, int snd_hda_add_vmaster(struct hda_codec *codec, char *name,
@ -1336,15 +1576,20 @@ int snd_hda_add_vmaster(struct hda_codec *codec, char *name,
for (s = slaves; *s; s++) { for (s = slaves; *s; s++) {
struct snd_kcontrol *sctl; struct snd_kcontrol *sctl;
int i = 0;
sctl = snd_hda_find_mixer_ctl(codec, *s); for (;;) {
sctl = _snd_hda_find_mixer_ctl(codec, *s, i);
if (!sctl) { if (!sctl) {
snd_printdd("Cannot find slave %s, skipped\n", *s); if (!i)
continue; snd_printdd("Cannot find slave %s, "
"skipped\n", *s);
break;
} }
err = snd_ctl_add_slave(kctl, sctl); err = snd_ctl_add_slave(kctl, sctl);
if (err < 0) if (err < 0)
return err; return err;
i++;
}
} }
return 0; return 0;
} }
@ -1955,6 +2200,8 @@ int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid)
} }
for (dig_mix = dig_in_ctls; dig_mix->name; dig_mix++) { for (dig_mix = dig_in_ctls; dig_mix->name; dig_mix++) {
kctl = snd_ctl_new1(dig_mix, codec); kctl = snd_ctl_new1(dig_mix, codec);
if (!kctl)
return -ENOMEM;
kctl->private_value = nid; kctl->private_value = nid;
err = snd_hda_ctl_add(codec, kctl); err = snd_hda_ctl_add(codec, kctl);
if (err < 0) if (err < 0)
@ -2074,8 +2321,7 @@ static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg,
* don't power down the widget if it controls * don't power down the widget if it controls
* eapd and EAPD_BTLENABLE is set. * eapd and EAPD_BTLENABLE is set.
*/ */
pincap = snd_hda_param_read(codec, nid, pincap = snd_hda_query_pin_caps(codec, nid);
AC_PAR_PIN_CAP);
if (pincap & AC_PINCAP_EAPD) { if (pincap & AC_PINCAP_EAPD) {
int eapd = snd_hda_codec_read(codec, int eapd = snd_hda_codec_read(codec,
nid, 0, nid, 0,
@ -2144,6 +2390,7 @@ static void hda_call_codec_resume(struct hda_codec *codec)
hda_set_power_state(codec, hda_set_power_state(codec,
codec->afg ? codec->afg : codec->mfg, codec->afg ? codec->afg : codec->mfg,
AC_PWRST_D0); AC_PWRST_D0);
restore_pincfgs(codec); /* restore all current pin configs */
hda_exec_init_verbs(codec); hda_exec_init_verbs(codec);
if (codec->patch_ops.resume) if (codec->patch_ops.resume)
codec->patch_ops.resume(codec); codec->patch_ops.resume(codec);
@ -2171,9 +2418,17 @@ int /*__devinit*/ snd_hda_build_controls(struct hda_bus *bus)
list_for_each_entry(codec, &bus->codec_list, list) { list_for_each_entry(codec, &bus->codec_list, list) {
int err = snd_hda_codec_build_controls(codec); int err = snd_hda_codec_build_controls(codec);
if (err < 0) if (err < 0) {
printk(KERN_ERR "hda_codec: cannot build controls"
"for #%d (error %d)\n", codec->addr, err);
err = snd_hda_codec_reset(codec);
if (err < 0) {
printk(KERN_ERR
"hda_codec: cannot revert codec\n");
return err; return err;
} }
}
}
return 0; return 0;
} }
EXPORT_SYMBOL_HDA(snd_hda_build_controls); EXPORT_SYMBOL_HDA(snd_hda_build_controls);
@ -2181,19 +2436,12 @@ EXPORT_SYMBOL_HDA(snd_hda_build_controls);
int snd_hda_codec_build_controls(struct hda_codec *codec) int snd_hda_codec_build_controls(struct hda_codec *codec)
{ {
int err = 0; int err = 0;
/* fake as if already powered-on */
hda_keep_power_on(codec);
/* then fire up */
hda_set_power_state(codec,
codec->afg ? codec->afg : codec->mfg,
AC_PWRST_D0);
hda_exec_init_verbs(codec); hda_exec_init_verbs(codec);
/* continue to initialize... */ /* continue to initialize... */
if (codec->patch_ops.init) if (codec->patch_ops.init)
err = codec->patch_ops.init(codec); err = codec->patch_ops.init(codec);
if (!err && codec->patch_ops.build_controls) if (!err && codec->patch_ops.build_controls)
err = codec->patch_ops.build_controls(codec); err = codec->patch_ops.build_controls(codec);
snd_hda_power_down(codec);
if (err < 0) if (err < 0)
return err; return err;
return 0; return 0;
@ -2306,12 +2554,11 @@ EXPORT_SYMBOL_HDA(snd_hda_calc_stream_format);
static int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid, static int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid,
u32 *ratesp, u64 *formatsp, unsigned int *bpsp) u32 *ratesp, u64 *formatsp, unsigned int *bpsp)
{ {
int i; unsigned int i, val, wcaps;
unsigned int val, streams;
val = 0; val = 0;
if (nid != codec->afg && wcaps = get_wcaps(codec, nid);
(get_wcaps(codec, nid) & AC_WCAP_FORMAT_OVRD)) { if (nid != codec->afg && (wcaps & AC_WCAP_FORMAT_OVRD)) {
val = snd_hda_param_read(codec, nid, AC_PAR_PCM); val = snd_hda_param_read(codec, nid, AC_PAR_PCM);
if (val == -1) if (val == -1)
return -EIO; return -EIO;
@ -2325,15 +2572,20 @@ static int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid,
if (val & (1 << i)) if (val & (1 << i))
rates |= rate_bits[i].alsa_bits; rates |= rate_bits[i].alsa_bits;
} }
if (rates == 0) {
snd_printk(KERN_ERR "hda_codec: rates == 0 "
"(nid=0x%x, val=0x%x, ovrd=%i)\n",
nid, val,
(wcaps & AC_WCAP_FORMAT_OVRD) ? 1 : 0);
return -EIO;
}
*ratesp = rates; *ratesp = rates;
} }
if (formatsp || bpsp) { if (formatsp || bpsp) {
u64 formats = 0; u64 formats = 0;
unsigned int bps; unsigned int streams, bps;
unsigned int wcaps;
wcaps = get_wcaps(codec, nid);
streams = snd_hda_param_read(codec, nid, AC_PAR_STREAM); streams = snd_hda_param_read(codec, nid, AC_PAR_STREAM);
if (streams == -1) if (streams == -1)
return -EIO; return -EIO;
@ -2386,6 +2638,15 @@ static int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid,
formats |= SNDRV_PCM_FMTBIT_U8; formats |= SNDRV_PCM_FMTBIT_U8;
bps = 8; bps = 8;
} }
if (formats == 0) {
snd_printk(KERN_ERR "hda_codec: formats == 0 "
"(nid=0x%x, val=0x%x, ovrd=%i, "
"streams=0x%x)\n",
nid, val,
(wcaps & AC_WCAP_FORMAT_OVRD) ? 1 : 0,
streams);
return -EIO;
}
if (formatsp) if (formatsp)
*formatsp = formats; *formatsp = formats;
if (bpsp) if (bpsp)
@ -2501,12 +2762,16 @@ static int hda_pcm_default_cleanup(struct hda_pcm_stream *hinfo,
static int set_pcm_default_values(struct hda_codec *codec, static int set_pcm_default_values(struct hda_codec *codec,
struct hda_pcm_stream *info) struct hda_pcm_stream *info)
{ {
int err;
/* query support PCM information from the given NID */ /* query support PCM information from the given NID */
if (info->nid && (!info->rates || !info->formats)) { if (info->nid && (!info->rates || !info->formats)) {
snd_hda_query_supported_pcm(codec, info->nid, err = snd_hda_query_supported_pcm(codec, info->nid,
info->rates ? NULL : &info->rates, info->rates ? NULL : &info->rates,
info->formats ? NULL : &info->formats, info->formats ? NULL : &info->formats,
info->maxbps ? NULL : &info->maxbps); info->maxbps ? NULL : &info->maxbps);
if (err < 0)
return err;
} }
if (info->ops.open == NULL) if (info->ops.open == NULL)
info->ops.open = hda_pcm_default_open_close; info->ops.open = hda_pcm_default_open_close;
@ -2549,13 +2814,10 @@ static int get_empty_pcm_device(struct hda_bus *bus, int type)
for (i = 0; i < ARRAY_SIZE(audio_idx); i++) { for (i = 0; i < ARRAY_SIZE(audio_idx); i++) {
dev = audio_idx[i]; dev = audio_idx[i];
if (!test_bit(dev, bus->pcm_dev_bits)) if (!test_bit(dev, bus->pcm_dev_bits))
break; goto ok;
} }
if (i >= ARRAY_SIZE(audio_idx)) {
snd_printk(KERN_WARNING "Too many audio devices\n"); snd_printk(KERN_WARNING "Too many audio devices\n");
return -EAGAIN; return -EAGAIN;
}
break;
case HDA_PCM_TYPE_SPDIF: case HDA_PCM_TYPE_SPDIF:
case HDA_PCM_TYPE_HDMI: case HDA_PCM_TYPE_HDMI:
case HDA_PCM_TYPE_MODEM: case HDA_PCM_TYPE_MODEM:
@ -2570,6 +2832,7 @@ static int get_empty_pcm_device(struct hda_bus *bus, int type)
snd_printk(KERN_WARNING "Invalid PCM type %d\n", type); snd_printk(KERN_WARNING "Invalid PCM type %d\n", type);
return -EINVAL; return -EINVAL;
} }
ok:
set_bit(dev, bus->pcm_dev_bits); set_bit(dev, bus->pcm_dev_bits);
return dev; return dev;
} }
@ -2606,24 +2869,36 @@ int snd_hda_codec_build_pcms(struct hda_codec *codec)
if (!codec->patch_ops.build_pcms) if (!codec->patch_ops.build_pcms)
return 0; return 0;
err = codec->patch_ops.build_pcms(codec); err = codec->patch_ops.build_pcms(codec);
if (err < 0) if (err < 0) {
printk(KERN_ERR "hda_codec: cannot build PCMs"
"for #%d (error %d)\n", codec->addr, err);
err = snd_hda_codec_reset(codec);
if (err < 0) {
printk(KERN_ERR
"hda_codec: cannot revert codec\n");
return err; return err;
} }
}
}
for (pcm = 0; pcm < codec->num_pcms; pcm++) { for (pcm = 0; pcm < codec->num_pcms; pcm++) {
struct hda_pcm *cpcm = &codec->pcm_info[pcm]; struct hda_pcm *cpcm = &codec->pcm_info[pcm];
int dev; int dev;
if (!cpcm->stream[0].substreams && !cpcm->stream[1].substreams) if (!cpcm->stream[0].substreams && !cpcm->stream[1].substreams)
return 0; /* no substreams assigned */ continue; /* no substreams assigned */
if (!cpcm->pcm) { if (!cpcm->pcm) {
dev = get_empty_pcm_device(codec->bus, cpcm->pcm_type); dev = get_empty_pcm_device(codec->bus, cpcm->pcm_type);
if (dev < 0) if (dev < 0)
return 0; continue; /* no fatal error */
cpcm->device = dev; cpcm->device = dev;
err = snd_hda_attach_pcm(codec, cpcm); err = snd_hda_attach_pcm(codec, cpcm);
if (err < 0) if (err < 0) {
return err; printk(KERN_ERR "hda_codec: cannot attach "
"PCM stream %d for codec #%d\n",
dev, codec->addr);
continue; /* no fatal error */
}
} }
} }
return 0; return 0;
@ -3324,8 +3599,7 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
if (ignore_nids && is_in_nid_list(nid, ignore_nids)) if (ignore_nids && is_in_nid_list(nid, ignore_nids))
continue; continue;
def_conf = snd_hda_codec_read(codec, nid, 0, def_conf = snd_hda_codec_get_pincfg(codec, nid);
AC_VERB_GET_CONFIG_DEFAULT, 0);
if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE) if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE)
continue; continue;
loc = get_defcfg_location(def_conf); loc = get_defcfg_location(def_conf);
@ -3401,10 +3675,22 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
cfg->input_pins[AUTO_PIN_AUX] = nid; cfg->input_pins[AUTO_PIN_AUX] = nid;
break; break;
case AC_JACK_SPDIF_OUT: case AC_JACK_SPDIF_OUT:
cfg->dig_out_pin = nid; case AC_JACK_DIG_OTHER_OUT:
if (cfg->dig_outs >= ARRAY_SIZE(cfg->dig_out_pins))
continue;
cfg->dig_out_pins[cfg->dig_outs] = nid;
cfg->dig_out_type[cfg->dig_outs] =
(loc == AC_JACK_LOC_HDMI) ?
HDA_PCM_TYPE_HDMI : HDA_PCM_TYPE_SPDIF;
cfg->dig_outs++;
break; break;
case AC_JACK_SPDIF_IN: case AC_JACK_SPDIF_IN:
case AC_JACK_DIG_OTHER_IN:
cfg->dig_in_pin = nid; cfg->dig_in_pin = nid;
if (loc == AC_JACK_LOC_HDMI)
cfg->dig_in_type = HDA_PCM_TYPE_HDMI;
else
cfg->dig_in_type = HDA_PCM_TYPE_SPDIF;
break; break;
} }
} }
@ -3510,6 +3796,9 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
cfg->hp_pins[1], cfg->hp_pins[2], cfg->hp_pins[1], cfg->hp_pins[2],
cfg->hp_pins[3], cfg->hp_pins[4]); cfg->hp_pins[3], cfg->hp_pins[4]);
snd_printd(" mono: mono_out=0x%x\n", cfg->mono_out_pin); snd_printd(" mono: mono_out=0x%x\n", cfg->mono_out_pin);
if (cfg->dig_outs)
snd_printd(" dig-out=0x%x/0x%x\n",
cfg->dig_out_pins[0], cfg->dig_out_pins[1]);
snd_printd(" inputs: mic=0x%x, fmic=0x%x, line=0x%x, fline=0x%x," snd_printd(" inputs: mic=0x%x, fmic=0x%x, line=0x%x, fline=0x%x,"
" cd=0x%x, aux=0x%x\n", " cd=0x%x, aux=0x%x\n",
cfg->input_pins[AUTO_PIN_MIC], cfg->input_pins[AUTO_PIN_MIC],
@ -3518,6 +3807,8 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
cfg->input_pins[AUTO_PIN_FRONT_LINE], cfg->input_pins[AUTO_PIN_FRONT_LINE],
cfg->input_pins[AUTO_PIN_CD], cfg->input_pins[AUTO_PIN_CD],
cfg->input_pins[AUTO_PIN_AUX]); cfg->input_pins[AUTO_PIN_AUX]);
if (cfg->dig_in_pin)
snd_printd(" dig-in=0x%x\n", cfg->dig_in_pin);
return 0; return 0;
} }

View file

@ -739,6 +739,7 @@ struct hda_codec {
hda_nid_t mfg; /* MFG node id */ hda_nid_t mfg; /* MFG node id */
/* ids */ /* ids */
u32 function_id;
u32 vendor_id; u32 vendor_id;
u32 subsystem_id; u32 subsystem_id;
u32 revision_id; u32 revision_id;
@ -778,11 +779,14 @@ struct hda_codec {
unsigned short spdif_ctls; /* SPDIF control bits */ unsigned short spdif_ctls; /* SPDIF control bits */
unsigned int spdif_in_enable; /* SPDIF input enable? */ unsigned int spdif_in_enable; /* SPDIF input enable? */
hda_nid_t *slave_dig_outs; /* optional digital out slave widgets */ hda_nid_t *slave_dig_outs; /* optional digital out slave widgets */
struct snd_array init_pins; /* initial (BIOS) pin configurations */
struct snd_array driver_pins; /* pin configs set by codec parser */
#ifdef CONFIG_SND_HDA_HWDEP #ifdef CONFIG_SND_HDA_HWDEP
struct snd_hwdep *hwdep; /* assigned hwdep device */ struct snd_hwdep *hwdep; /* assigned hwdep device */
struct snd_array init_verbs; /* additional init verbs */ struct snd_array init_verbs; /* additional init verbs */
struct snd_array hints; /* additional hints */ struct snd_array hints; /* additional hints */
struct snd_array user_pins; /* default pin configs to override */
#endif #endif
/* misc flags */ /* misc flags */
@ -790,6 +794,9 @@ struct hda_codec {
* status change * status change
* (e.g. Realtek codecs) * (e.g. Realtek codecs)
*/ */
unsigned int pin_amp_workaround:1; /* pin out-amp takes index
* (e.g. Conexant codecs)
*/
#ifdef CONFIG_SND_HDA_POWER_SAVE #ifdef CONFIG_SND_HDA_POWER_SAVE
unsigned int power_on :1; /* current (global) power-state */ unsigned int power_on :1; /* current (global) power-state */
unsigned int power_transition :1; /* power-state in transition */ unsigned int power_transition :1; /* power-state in transition */
@ -855,6 +862,18 @@ void snd_hda_codec_resume_cache(struct hda_codec *codec);
#define snd_hda_sequence_write_cache snd_hda_sequence_write #define snd_hda_sequence_write_cache snd_hda_sequence_write
#endif #endif
/* the struct for codec->pin_configs */
struct hda_pincfg {
hda_nid_t nid;
unsigned int cfg;
};
unsigned int snd_hda_codec_get_pincfg(struct hda_codec *codec, hda_nid_t nid);
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 */
/* /*
* Mixer * Mixer
*/ */

View file

@ -144,9 +144,9 @@ static int add_new_node(struct hda_codec *codec, struct hda_gspec *spec, hda_nid
node->type = (node->wid_caps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; node->type = (node->wid_caps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
if (node->type == AC_WID_PIN) { if (node->type == AC_WID_PIN) {
node->pin_caps = snd_hda_param_read(codec, node->nid, AC_PAR_PIN_CAP); node->pin_caps = snd_hda_query_pin_caps(codec, node->nid);
node->pin_ctl = snd_hda_codec_read(codec, node->nid, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0); node->pin_ctl = snd_hda_codec_read(codec, node->nid, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
node->def_cfg = snd_hda_codec_read(codec, node->nid, 0, AC_VERB_GET_CONFIG_DEFAULT, 0); node->def_cfg = snd_hda_codec_get_pincfg(codec, node->nid);
} }
if (node->wid_caps & AC_WCAP_OUT_AMP) { if (node->wid_caps & AC_WCAP_OUT_AMP) {

View file

@ -30,6 +30,12 @@
#include <sound/hda_hwdep.h> #include <sound/hda_hwdep.h>
#include <sound/minors.h> #include <sound/minors.h>
/* hint string pair */
struct hda_hint {
const char *key;
const char *val; /* contained in the same alloc as key */
};
/* /*
* write/read an out-of-bound verb * write/read an out-of-bound verb
*/ */
@ -99,16 +105,17 @@ static int hda_hwdep_open(struct snd_hwdep *hw, struct file *file)
static void clear_hwdep_elements(struct hda_codec *codec) static void clear_hwdep_elements(struct hda_codec *codec)
{ {
char **head;
int i; int i;
/* clear init verbs */ /* clear init verbs */
snd_array_free(&codec->init_verbs); snd_array_free(&codec->init_verbs);
/* clear hints */ /* clear hints */
head = codec->hints.list; for (i = 0; i < codec->hints.used; i++) {
for (i = 0; i < codec->hints.used; i++, head++) struct hda_hint *hint = snd_array_elem(&codec->hints, i);
kfree(*head); kfree(hint->key); /* we don't need to free hint->val */
}
snd_array_free(&codec->hints); snd_array_free(&codec->hints);
snd_array_free(&codec->user_pins);
} }
static void hwdep_free(struct snd_hwdep *hwdep) static void hwdep_free(struct snd_hwdep *hwdep)
@ -140,7 +147,8 @@ int /*__devinit*/ snd_hda_create_hwdep(struct hda_codec *codec)
#endif #endif
snd_array_init(&codec->init_verbs, sizeof(struct hda_verb), 32); snd_array_init(&codec->init_verbs, sizeof(struct hda_verb), 32);
snd_array_init(&codec->hints, sizeof(char *), 32); snd_array_init(&codec->hints, sizeof(struct hda_hint), 32);
snd_array_init(&codec->user_pins, sizeof(struct hda_pincfg), 16);
return 0; return 0;
} }
@ -153,7 +161,13 @@ int /*__devinit*/ snd_hda_create_hwdep(struct hda_codec *codec)
static int clear_codec(struct hda_codec *codec) static int clear_codec(struct hda_codec *codec)
{ {
snd_hda_codec_reset(codec); int err;
err = snd_hda_codec_reset(codec);
if (err < 0) {
snd_printk(KERN_ERR "The codec is being used, can't free.\n");
return err;
}
clear_hwdep_elements(codec); clear_hwdep_elements(codec);
return 0; return 0;
} }
@ -162,20 +176,29 @@ static int reconfig_codec(struct hda_codec *codec)
{ {
int err; int err;
snd_hda_power_up(codec);
snd_printk(KERN_INFO "hda-codec: reconfiguring\n"); snd_printk(KERN_INFO "hda-codec: reconfiguring\n");
snd_hda_codec_reset(codec); err = snd_hda_codec_reset(codec);
if (err < 0) {
snd_printk(KERN_ERR
"The codec is being used, can't reconfigure.\n");
goto error;
}
err = snd_hda_codec_configure(codec); err = snd_hda_codec_configure(codec);
if (err < 0) if (err < 0)
return err; goto error;
/* rebuild PCMs */ /* rebuild PCMs */
err = snd_hda_codec_build_pcms(codec); err = snd_hda_codec_build_pcms(codec);
if (err < 0) if (err < 0)
return err; goto error;
/* rebuild mixers */ /* rebuild mixers */
err = snd_hda_codec_build_controls(codec); err = snd_hda_codec_build_controls(codec);
if (err < 0) if (err < 0)
goto error;
err = snd_card_register(codec->bus->card);
error:
snd_hda_power_down(codec);
return err; return err;
return snd_card_register(codec->bus->card);
} }
/* /*
@ -271,6 +294,22 @@ static ssize_t type##_store(struct device *dev, \
CODEC_ACTION_STORE(reconfig); CODEC_ACTION_STORE(reconfig);
CODEC_ACTION_STORE(clear); CODEC_ACTION_STORE(clear);
static ssize_t init_verbs_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct snd_hwdep *hwdep = dev_get_drvdata(dev);
struct hda_codec *codec = hwdep->private_data;
int i, len = 0;
for (i = 0; i < codec->init_verbs.used; i++) {
struct hda_verb *v = snd_array_elem(&codec->init_verbs, i);
len += snprintf(buf + len, PAGE_SIZE - len,
"0x%02x 0x%03x 0x%04x\n",
v->nid, v->verb, v->param);
}
return len;
}
static ssize_t init_verbs_store(struct device *dev, static ssize_t init_verbs_store(struct device *dev,
struct device_attribute *attr, struct device_attribute *attr,
const char *buf, size_t count) const char *buf, size_t count)
@ -293,26 +332,157 @@ static ssize_t init_verbs_store(struct device *dev,
return count; return count;
} }
static ssize_t hints_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct snd_hwdep *hwdep = dev_get_drvdata(dev);
struct hda_codec *codec = hwdep->private_data;
int i, len = 0;
for (i = 0; i < codec->hints.used; i++) {
struct hda_hint *hint = snd_array_elem(&codec->hints, i);
len += snprintf(buf + len, PAGE_SIZE - len,
"%s = %s\n", hint->key, hint->val);
}
return len;
}
static struct hda_hint *get_hint(struct hda_codec *codec, const char *key)
{
int i;
for (i = 0; i < codec->hints.used; i++) {
struct hda_hint *hint = snd_array_elem(&codec->hints, i);
if (!strcmp(hint->key, key))
return hint;
}
return NULL;
}
static void remove_trail_spaces(char *str)
{
char *p;
if (!*str)
return;
p = str + strlen(str) - 1;
for (; isspace(*p); p--) {
*p = 0;
if (p == str)
return;
}
}
#define MAX_HINTS 1024
static ssize_t hints_store(struct device *dev, static ssize_t hints_store(struct device *dev,
struct device_attribute *attr, struct device_attribute *attr,
const char *buf, size_t count) const char *buf, size_t count)
{ {
struct snd_hwdep *hwdep = dev_get_drvdata(dev); struct snd_hwdep *hwdep = dev_get_drvdata(dev);
struct hda_codec *codec = hwdep->private_data; struct hda_codec *codec = hwdep->private_data;
char *p; char *key, *val;
char **hint; struct hda_hint *hint;
if (!*buf || isspace(*buf) || *buf == '#' || *buf == '\n') while (isspace(*buf))
buf++;
if (!*buf || *buf == '#' || *buf == '\n')
return count; return count;
p = kstrndup_noeol(buf, 1024); if (*buf == '=')
if (!p) return -EINVAL;
key = kstrndup_noeol(buf, 1024);
if (!key)
return -ENOMEM; return -ENOMEM;
/* extract key and val */
val = strchr(key, '=');
if (!val) {
kfree(key);
return -EINVAL;
}
*val++ = 0;
while (isspace(*val))
val++;
remove_trail_spaces(key);
remove_trail_spaces(val);
hint = get_hint(codec, key);
if (hint) {
/* replace */
kfree(hint->key);
hint->key = key;
hint->val = val;
return count;
}
/* allocate a new hint entry */
if (codec->hints.used >= MAX_HINTS)
hint = NULL;
else
hint = snd_array_new(&codec->hints); hint = snd_array_new(&codec->hints);
if (!hint) { if (!hint) {
kfree(p); kfree(key);
return -ENOMEM; return -ENOMEM;
} }
*hint = p; hint->key = key;
hint->val = val;
return count;
}
static ssize_t pin_configs_show(struct hda_codec *codec,
struct snd_array *list,
char *buf)
{
int i, len = 0;
for (i = 0; i < list->used; i++) {
struct hda_pincfg *pin = snd_array_elem(list, i);
len += sprintf(buf + len, "0x%02x 0x%08x\n",
pin->nid, pin->cfg);
}
return len;
}
static ssize_t init_pin_configs_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct snd_hwdep *hwdep = dev_get_drvdata(dev);
struct hda_codec *codec = hwdep->private_data;
return pin_configs_show(codec, &codec->init_pins, buf);
}
static ssize_t user_pin_configs_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct snd_hwdep *hwdep = dev_get_drvdata(dev);
struct hda_codec *codec = hwdep->private_data;
return pin_configs_show(codec, &codec->user_pins, buf);
}
static ssize_t driver_pin_configs_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct snd_hwdep *hwdep = dev_get_drvdata(dev);
struct hda_codec *codec = hwdep->private_data;
return pin_configs_show(codec, &codec->driver_pins, buf);
}
#define MAX_PIN_CONFIGS 32
static ssize_t user_pin_configs_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct snd_hwdep *hwdep = dev_get_drvdata(dev);
struct hda_codec *codec = hwdep->private_data;
int nid, cfg;
int err;
if (sscanf(buf, "%i %i", &nid, &cfg) != 2)
return -EINVAL;
if (!nid)
return -EINVAL;
err = snd_hda_add_pincfg(codec, &codec->user_pins, nid, cfg);
if (err < 0)
return err;
return count; return count;
} }
@ -331,8 +501,11 @@ static struct device_attribute codec_attrs[] = {
CODEC_ATTR_RO(mfg), CODEC_ATTR_RO(mfg),
CODEC_ATTR_RW(name), CODEC_ATTR_RW(name),
CODEC_ATTR_RW(modelname), CODEC_ATTR_RW(modelname),
CODEC_ATTR_WO(init_verbs), CODEC_ATTR_RW(init_verbs),
CODEC_ATTR_WO(hints), CODEC_ATTR_RW(hints),
CODEC_ATTR_RO(init_pin_configs),
CODEC_ATTR_RW(user_pin_configs),
CODEC_ATTR_RO(driver_pin_configs),
CODEC_ATTR_WO(reconfig), CODEC_ATTR_WO(reconfig),
CODEC_ATTR_WO(clear), CODEC_ATTR_WO(clear),
}; };
@ -351,4 +524,29 @@ int snd_hda_hwdep_add_sysfs(struct hda_codec *codec)
return 0; return 0;
} }
/*
* Look for hint string
*/
const char *snd_hda_get_hint(struct hda_codec *codec, const char *key)
{
struct hda_hint *hint = get_hint(codec, key);
return hint ? hint->val : NULL;
}
EXPORT_SYMBOL_HDA(snd_hda_get_hint);
int snd_hda_get_bool_hint(struct hda_codec *codec, const char *key)
{
const char *p = snd_hda_get_hint(codec, key);
if (!p || !*p)
return -ENOENT;
switch (toupper(*p)) {
case 'T': /* true */
case 'Y': /* yes */
case '1':
return 1;
}
return 0;
}
EXPORT_SYMBOL_HDA(snd_hda_get_bool_hint);
#endif /* CONFIG_SND_HDA_RECONFIG */ #endif /* CONFIG_SND_HDA_RECONFIG */

View file

@ -381,6 +381,7 @@ struct azx {
/* HD codec */ /* HD codec */
unsigned short codec_mask; unsigned short codec_mask;
int codec_probe_mask; /* copied from probe_mask option */
struct hda_bus *bus; struct hda_bus *bus;
/* CORB/RIRB */ /* CORB/RIRB */
@ -858,13 +859,18 @@ static void azx_stream_start(struct azx *chip, struct azx_dev *azx_dev)
SD_CTL_DMA_START | SD_INT_MASK); SD_CTL_DMA_START | SD_INT_MASK);
} }
/* stop a stream */
static void azx_stream_stop(struct azx *chip, struct azx_dev *azx_dev)
{
/* stop DMA */ /* stop DMA */
static void azx_stream_clear(struct azx *chip, struct azx_dev *azx_dev)
{
azx_sd_writeb(azx_dev, SD_CTL, azx_sd_readb(azx_dev, SD_CTL) & azx_sd_writeb(azx_dev, SD_CTL, azx_sd_readb(azx_dev, SD_CTL) &
~(SD_CTL_DMA_START | SD_INT_MASK)); ~(SD_CTL_DMA_START | SD_INT_MASK));
azx_sd_writeb(azx_dev, SD_STS, SD_INT_MASK); /* to be sure */ azx_sd_writeb(azx_dev, SD_STS, SD_INT_MASK); /* to be sure */
}
/* stop a stream */
static void azx_stream_stop(struct azx *chip, struct azx_dev *azx_dev)
{
azx_stream_clear(chip, azx_dev);
/* disable SIE */ /* disable SIE */
azx_writeb(chip, INTCTL, azx_writeb(chip, INTCTL,
azx_readb(chip, INTCTL) & ~(1 << azx_dev->index)); azx_readb(chip, INTCTL) & ~(1 << azx_dev->index));
@ -1075,8 +1081,7 @@ static int azx_setup_periods(struct azx *chip,
azx_sd_writel(azx_dev, SD_BDLPL, 0); azx_sd_writel(azx_dev, SD_BDLPL, 0);
azx_sd_writel(azx_dev, SD_BDLPU, 0); azx_sd_writel(azx_dev, SD_BDLPU, 0);
period_bytes = snd_pcm_lib_period_bytes(substream); period_bytes = azx_dev->period_bytes;
azx_dev->period_bytes = period_bytes;
periods = azx_dev->bufsize / period_bytes; periods = azx_dev->bufsize / period_bytes;
/* program the initial BDL entries */ /* program the initial BDL entries */
@ -1123,24 +1128,17 @@ static int azx_setup_periods(struct azx *chip,
error: error:
snd_printk(KERN_ERR "Too many BDL entries: buffer=%d, period=%d\n", snd_printk(KERN_ERR "Too many BDL entries: buffer=%d, period=%d\n",
azx_dev->bufsize, period_bytes); azx_dev->bufsize, period_bytes);
/* reset */
azx_sd_writel(azx_dev, SD_BDLPL, 0);
azx_sd_writel(azx_dev, SD_BDLPU, 0);
return -EINVAL; return -EINVAL;
} }
/* /* reset stream */
* set up the SD for streaming static void azx_stream_reset(struct azx *chip, struct azx_dev *azx_dev)
*/
static int azx_setup_controller(struct azx *chip, struct azx_dev *azx_dev)
{ {
unsigned char val; unsigned char val;
int timeout; int timeout;
/* make sure the run bit is zero for SD */ azx_stream_clear(chip, azx_dev);
azx_sd_writeb(azx_dev, SD_CTL, azx_sd_readb(azx_dev, SD_CTL) &
~SD_CTL_DMA_START);
/* reset stream */
azx_sd_writeb(azx_dev, SD_CTL, azx_sd_readb(azx_dev, SD_CTL) | azx_sd_writeb(azx_dev, SD_CTL, azx_sd_readb(azx_dev, SD_CTL) |
SD_CTL_STREAM_RESET); SD_CTL_STREAM_RESET);
udelay(3); udelay(3);
@ -1157,7 +1155,15 @@ static int azx_setup_controller(struct azx *chip, struct azx_dev *azx_dev)
while (((val = azx_sd_readb(azx_dev, SD_CTL)) & SD_CTL_STREAM_RESET) && while (((val = azx_sd_readb(azx_dev, SD_CTL)) & SD_CTL_STREAM_RESET) &&
--timeout) --timeout)
; ;
}
/*
* set up the SD for streaming
*/
static int azx_setup_controller(struct azx *chip, struct azx_dev *azx_dev)
{
/* make sure the run bit is zero for SD */
azx_stream_clear(chip, azx_dev);
/* program the stream_tag */ /* program the stream_tag */
azx_sd_writel(azx_dev, SD_CTL, azx_sd_writel(azx_dev, SD_CTL,
(azx_sd_readl(azx_dev, SD_CTL) & ~SD_CTL_STREAM_TAG_MASK)| (azx_sd_readl(azx_dev, SD_CTL) & ~SD_CTL_STREAM_TAG_MASK)|
@ -1228,7 +1234,6 @@ static unsigned int azx_max_codecs[AZX_NUM_DRIVERS] __devinitdata = {
}; };
static int __devinit azx_codec_create(struct azx *chip, const char *model, static int __devinit azx_codec_create(struct azx *chip, const char *model,
unsigned int codec_probe_mask,
int no_init) int no_init)
{ {
struct hda_bus_template bus_temp; struct hda_bus_template bus_temp;
@ -1261,7 +1266,7 @@ static int __devinit azx_codec_create(struct azx *chip, const char *model,
/* First try to probe all given codec slots */ /* First try to probe all given codec slots */
for (c = 0; c < max_slots; c++) { for (c = 0; c < max_slots; c++) {
if ((chip->codec_mask & (1 << c)) & codec_probe_mask) { if ((chip->codec_mask & (1 << c)) & chip->codec_probe_mask) {
if (probe_codec(chip, c) < 0) { if (probe_codec(chip, c) < 0) {
/* Some BIOSen give you wrong codec addresses /* Some BIOSen give you wrong codec addresses
* that don't exist * that don't exist
@ -1285,7 +1290,7 @@ static int __devinit azx_codec_create(struct azx *chip, const char *model,
/* Then create codec instances */ /* Then create codec instances */
for (c = 0; c < max_slots; c++) { for (c = 0; c < max_slots; c++) {
if ((chip->codec_mask & (1 << c)) & codec_probe_mask) { if ((chip->codec_mask & (1 << c)) & chip->codec_probe_mask) {
struct hda_codec *codec; struct hda_codec *codec;
err = snd_hda_codec_new(chip->bus, c, !no_init, &codec); err = snd_hda_codec_new(chip->bus, c, !no_init, &codec);
if (err < 0) if (err < 0)
@ -1403,6 +1408,8 @@ static int azx_pcm_open(struct snd_pcm_substream *substream)
runtime->private_data = azx_dev; runtime->private_data = azx_dev;
snd_pcm_set_sync(substream); snd_pcm_set_sync(substream);
mutex_unlock(&chip->open_mutex); mutex_unlock(&chip->open_mutex);
azx_stream_reset(chip, azx_dev);
return 0; return 0;
} }
@ -1429,6 +1436,11 @@ static int azx_pcm_close(struct snd_pcm_substream *substream)
static int azx_pcm_hw_params(struct snd_pcm_substream *substream, static int azx_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params) struct snd_pcm_hw_params *hw_params)
{ {
struct azx_dev *azx_dev = get_azx_dev(substream);
azx_dev->bufsize = 0;
azx_dev->period_bytes = 0;
azx_dev->format_val = 0;
return snd_pcm_lib_malloc_pages(substream, return snd_pcm_lib_malloc_pages(substream,
params_buffer_bytes(hw_params)); params_buffer_bytes(hw_params));
} }
@ -1443,6 +1455,9 @@ static int azx_pcm_hw_free(struct snd_pcm_substream *substream)
azx_sd_writel(azx_dev, SD_BDLPL, 0); azx_sd_writel(azx_dev, SD_BDLPL, 0);
azx_sd_writel(azx_dev, SD_BDLPU, 0); azx_sd_writel(azx_dev, SD_BDLPU, 0);
azx_sd_writel(azx_dev, SD_CTL, 0); azx_sd_writel(azx_dev, SD_CTL, 0);
azx_dev->bufsize = 0;
azx_dev->period_bytes = 0;
azx_dev->format_val = 0;
hinfo->ops.cleanup(hinfo, apcm->codec, substream); hinfo->ops.cleanup(hinfo, apcm->codec, substream);
@ -1456,23 +1471,37 @@ static int azx_pcm_prepare(struct snd_pcm_substream *substream)
struct azx_dev *azx_dev = get_azx_dev(substream); struct azx_dev *azx_dev = get_azx_dev(substream);
struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream]; struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream];
struct snd_pcm_runtime *runtime = substream->runtime; struct snd_pcm_runtime *runtime = substream->runtime;
unsigned int bufsize, period_bytes, format_val;
int err;
azx_dev->bufsize = snd_pcm_lib_buffer_bytes(substream); format_val = snd_hda_calc_stream_format(runtime->rate,
azx_dev->format_val = snd_hda_calc_stream_format(runtime->rate,
runtime->channels, runtime->channels,
runtime->format, runtime->format,
hinfo->maxbps); hinfo->maxbps);
if (!azx_dev->format_val) { if (!format_val) {
snd_printk(KERN_ERR SFX snd_printk(KERN_ERR SFX
"invalid format_val, rate=%d, ch=%d, format=%d\n", "invalid format_val, rate=%d, ch=%d, format=%d\n",
runtime->rate, runtime->channels, runtime->format); runtime->rate, runtime->channels, runtime->format);
return -EINVAL; return -EINVAL;
} }
bufsize = snd_pcm_lib_buffer_bytes(substream);
period_bytes = snd_pcm_lib_period_bytes(substream);
snd_printdd("azx_pcm_prepare: bufsize=0x%x, format=0x%x\n", snd_printdd("azx_pcm_prepare: bufsize=0x%x, format=0x%x\n",
azx_dev->bufsize, azx_dev->format_val); bufsize, format_val);
if (azx_setup_periods(chip, substream, azx_dev) < 0)
return -EINVAL; if (bufsize != azx_dev->bufsize ||
period_bytes != azx_dev->period_bytes ||
format_val != azx_dev->format_val) {
azx_dev->bufsize = bufsize;
azx_dev->period_bytes = period_bytes;
azx_dev->format_val = format_val;
err = azx_setup_periods(chip, substream, azx_dev);
if (err < 0)
return err;
}
azx_setup_controller(chip, azx_dev); azx_setup_controller(chip, azx_dev);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
azx_dev->fifo_size = azx_sd_readw(azx_dev, SD_FIFOSIZE) + 1; azx_dev->fifo_size = azx_sd_readw(azx_dev, SD_FIFOSIZE) + 1;
@ -2100,25 +2129,36 @@ static struct snd_pci_quirk probe_mask_list[] __devinitdata = {
SND_PCI_QUIRK(0x1028, 0x20ac, "Dell Studio Desktop", 0x01), SND_PCI_QUIRK(0x1028, 0x20ac, "Dell Studio Desktop", 0x01),
/* including bogus ALC268 in slot#2 that conflicts with ALC888 */ /* including bogus ALC268 in slot#2 that conflicts with ALC888 */
SND_PCI_QUIRK(0x17c0, 0x4085, "Medion MD96630", 0x01), SND_PCI_QUIRK(0x17c0, 0x4085, "Medion MD96630", 0x01),
/* conflict of ALC268 in slot#3 (digital I/O); a temporary fix */ /* forced codec slots */
SND_PCI_QUIRK(0x1179, 0xff00, "Toshiba laptop", 0x03), SND_PCI_QUIRK(0x1046, 0x1262, "ASUS W5F", 0x103),
{} {}
}; };
#define AZX_FORCE_CODEC_MASK 0x100
static void __devinit check_probe_mask(struct azx *chip, int dev) static void __devinit check_probe_mask(struct azx *chip, int dev)
{ {
const struct snd_pci_quirk *q; const struct snd_pci_quirk *q;
if (probe_mask[dev] == -1) { chip->codec_probe_mask = probe_mask[dev];
if (chip->codec_probe_mask == -1) {
q = snd_pci_quirk_lookup(chip->pci, probe_mask_list); q = snd_pci_quirk_lookup(chip->pci, probe_mask_list);
if (q) { if (q) {
printk(KERN_INFO printk(KERN_INFO
"hda_intel: probe_mask set to 0x%x " "hda_intel: probe_mask set to 0x%x "
"for device %04x:%04x\n", "for device %04x:%04x\n",
q->value, q->subvendor, q->subdevice); q->value, q->subvendor, q->subdevice);
probe_mask[dev] = q->value; chip->codec_probe_mask = q->value;
} }
} }
/* check forced option */
if (chip->codec_probe_mask != -1 &&
(chip->codec_probe_mask & AZX_FORCE_CODEC_MASK)) {
chip->codec_mask = chip->codec_probe_mask & 0xff;
printk(KERN_INFO "hda_intel: codec_mask forced to 0x%x\n",
chip->codec_mask);
}
} }
@ -2359,8 +2399,7 @@ static int __devinit azx_probe(struct pci_dev *pci,
card->private_data = chip; card->private_data = chip;
/* create codec instances */ /* create codec instances */
err = azx_codec_create(chip, model[dev], probe_mask[dev], err = azx_codec_create(chip, model[dev], probe_only[dev]);
probe_only[dev]);
if (err < 0) if (err < 0)
goto out_free; goto out_free;
@ -2457,10 +2496,10 @@ static struct pci_device_id azx_ids[] = {
{ PCI_DEVICE(0x10de, 0x0ac1), .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, 0x0ac2), .driver_data = AZX_DRIVER_NVIDIA },
{ PCI_DEVICE(0x10de, 0x0ac3), .driver_data = AZX_DRIVER_NVIDIA }, { PCI_DEVICE(0x10de, 0x0ac3), .driver_data = AZX_DRIVER_NVIDIA },
{ PCI_DEVICE(0x10de, 0x0bd4), .driver_data = AZX_DRIVER_NVIDIA }, { PCI_DEVICE(0x10de, 0x0d94), .driver_data = AZX_DRIVER_NVIDIA },
{ PCI_DEVICE(0x10de, 0x0bd5), .driver_data = AZX_DRIVER_NVIDIA }, { PCI_DEVICE(0x10de, 0x0d95), .driver_data = AZX_DRIVER_NVIDIA },
{ PCI_DEVICE(0x10de, 0x0bd6), .driver_data = AZX_DRIVER_NVIDIA }, { PCI_DEVICE(0x10de, 0x0d96), .driver_data = AZX_DRIVER_NVIDIA },
{ PCI_DEVICE(0x10de, 0x0bd7), .driver_data = AZX_DRIVER_NVIDIA }, { PCI_DEVICE(0x10de, 0x0d97), .driver_data = AZX_DRIVER_NVIDIA },
/* Teradici */ /* Teradici */
{ PCI_DEVICE(0x6549, 0x1200), .driver_data = AZX_DRIVER_TERA }, { PCI_DEVICE(0x6549, 0x1200), .driver_data = AZX_DRIVER_TERA },
/* AMD Generic, PCI class code and Vendor ID for HD Audio */ /* AMD Generic, PCI class code and Vendor ID for HD Audio */

View file

@ -26,8 +26,10 @@
/* /*
* for mixer controls * for mixer controls
*/ */
#define HDA_COMPOSE_AMP_VAL_OFS(nid,chs,idx,dir,ofs) \
((nid) | ((chs)<<16) | ((dir)<<18) | ((idx)<<19) | ((ofs)<<23))
#define HDA_COMPOSE_AMP_VAL(nid,chs,idx,dir) \ #define HDA_COMPOSE_AMP_VAL(nid,chs,idx,dir) \
((nid) | ((chs)<<16) | ((dir)<<18) | ((idx)<<19)) HDA_COMPOSE_AMP_VAL_OFS(nid, chs, idx, dir, 0)
/* mono volume with index (index=0,1,...) (channel=1,2) */ /* mono volume with index (index=0,1,...) (channel=1,2) */
#define HDA_CODEC_VOLUME_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \ #define HDA_CODEC_VOLUME_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \
@ -96,7 +98,7 @@ struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec,
const char *name); const char *name);
int snd_hda_add_vmaster(struct hda_codec *codec, char *name, int snd_hda_add_vmaster(struct hda_codec *codec, char *name,
unsigned int *tlv, const char **slaves); unsigned int *tlv, const char **slaves);
void snd_hda_codec_reset(struct hda_codec *codec); int snd_hda_codec_reset(struct hda_codec *codec);
int snd_hda_codec_configure(struct hda_codec *codec); int snd_hda_codec_configure(struct hda_codec *codec);
/* amp value bits */ /* amp value bits */
@ -134,7 +136,7 @@ extern struct hda_ctl_ops snd_hda_bind_sw; /* for bind-switch */
struct hda_bind_ctls { struct hda_bind_ctls {
struct hda_ctl_ops *ops; struct hda_ctl_ops *ops;
long values[]; unsigned long values[];
}; };
int snd_hda_mixer_bind_ctls_info(struct snd_kcontrol *kcontrol, int snd_hda_mixer_bind_ctls_info(struct snd_kcontrol *kcontrol,
@ -227,6 +229,7 @@ struct hda_multi_out {
hda_nid_t hp_nid; /* optional DAC for HP, 0 when not exists */ hda_nid_t hp_nid; /* optional DAC for HP, 0 when not exists */
hda_nid_t extra_out_nid[3]; /* optional DACs, 0 when not exists */ hda_nid_t extra_out_nid[3]; /* optional DACs, 0 when not exists */
hda_nid_t dig_out_nid; /* digital out audio widget */ hda_nid_t dig_out_nid; /* digital out audio widget */
hda_nid_t *slave_dig_outs;
int max_channels; /* currently supported analog channels */ int max_channels; /* currently supported analog channels */
int dig_out_used; /* current usage of digital out (HDA_DIG_XXX) */ int dig_out_used; /* current usage of digital out (HDA_DIG_XXX) */
int no_share_stream; /* don't share a stream with multiple pins */ int no_share_stream; /* don't share a stream with multiple pins */
@ -354,9 +357,12 @@ struct auto_pin_cfg {
int line_out_type; /* AUTO_PIN_XXX_OUT */ int line_out_type; /* AUTO_PIN_XXX_OUT */
hda_nid_t hp_pins[AUTO_CFG_MAX_OUTS]; hda_nid_t hp_pins[AUTO_CFG_MAX_OUTS];
hda_nid_t input_pins[AUTO_PIN_LAST]; hda_nid_t input_pins[AUTO_PIN_LAST];
hda_nid_t dig_out_pin; int dig_outs;
hda_nid_t dig_out_pins[2];
hda_nid_t dig_in_pin; hda_nid_t dig_in_pin;
hda_nid_t mono_out_pin; hda_nid_t mono_out_pin;
int dig_out_type[2]; /* HDA_PCM_TYPE_XXX */
int dig_in_type; /* HDA_PCM_TYPE_XXX */
}; };
#define get_defcfg_connect(cfg) \ #define get_defcfg_connect(cfg) \
@ -405,6 +411,7 @@ static inline u32 get_wcaps(struct hda_codec *codec, hda_nid_t nid)
u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction); u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction);
int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir, int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
unsigned int caps); unsigned int caps);
u32 snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t nid);
int snd_hda_ctl_add(struct hda_codec *codec, struct snd_kcontrol *kctl); int snd_hda_ctl_add(struct hda_codec *codec, struct snd_kcontrol *kctl);
void snd_hda_ctls_clear(struct hda_codec *codec); void snd_hda_ctls_clear(struct hda_codec *codec);
@ -427,6 +434,23 @@ static inline int snd_hda_hwdep_add_sysfs(struct hda_codec *codec)
} }
#endif #endif
#ifdef CONFIG_SND_HDA_RECONFIG
const char *snd_hda_get_hint(struct hda_codec *codec, const char *key);
int snd_hda_get_bool_hint(struct hda_codec *codec, const char *key);
#else
static inline
const char *snd_hda_get_hint(struct hda_codec *codec, const char *key)
{
return NULL;
}
static inline
int snd_hda_get_bool_hint(struct hda_codec *codec, const char *key)
{
return -ENOENT;
}
#endif
/* /*
* power-management * power-management
*/ */
@ -458,6 +482,7 @@ int snd_hda_check_amp_list_power(struct hda_codec *codec,
#define get_amp_channels(kc) (((kc)->private_value >> 16) & 0x3) #define get_amp_channels(kc) (((kc)->private_value >> 16) & 0x3)
#define get_amp_direction(kc) (((kc)->private_value >> 18) & 0x1) #define get_amp_direction(kc) (((kc)->private_value >> 18) & 0x1)
#define get_amp_index(kc) (((kc)->private_value >> 19) & 0xf) #define get_amp_index(kc) (((kc)->private_value >> 19) & 0xf)
#define get_amp_offset(kc) (((kc)->private_value >> 23) & 0x3f)
/* /*
* CEA Short Audio Descriptor data * CEA Short Audio Descriptor data

View file

@ -399,8 +399,10 @@ static void print_conn_list(struct snd_info_buffer *buffer,
{ {
int c, curr = -1; int c, curr = -1;
if (conn_len > 1 && wid_type != AC_WID_AUD_MIX && if (conn_len > 1 &&
wid_type != AC_WID_VOL_KNB) wid_type != AC_WID_AUD_MIX &&
wid_type != AC_WID_VOL_KNB &&
wid_type != AC_WID_POWER)
curr = snd_hda_codec_read(codec, nid, 0, curr = snd_hda_codec_read(codec, nid, 0,
AC_VERB_GET_CONNECT_SEL, 0); AC_VERB_GET_CONNECT_SEL, 0);
snd_iprintf(buffer, " Connection: %d\n", conn_len); snd_iprintf(buffer, " Connection: %d\n", conn_len);
@ -467,8 +469,9 @@ static void print_codec_info(struct snd_info_entry *entry,
snd_iprintf(buffer, "Codec: %s\n", snd_iprintf(buffer, "Codec: %s\n",
codec->name ? codec->name : "Not Set"); codec->name ? codec->name : "Not Set");
snd_iprintf(buffer, "Address: %d\n", codec->addr); snd_iprintf(buffer, "Address: %d\n", codec->addr);
snd_iprintf(buffer, "Vendor Id: 0x%x\n", codec->vendor_id); snd_iprintf(buffer, "Function Id: 0x%x\n", codec->function_id);
snd_iprintf(buffer, "Subsystem Id: 0x%x\n", codec->subsystem_id); snd_iprintf(buffer, "Vendor Id: 0x%08x\n", codec->vendor_id);
snd_iprintf(buffer, "Subsystem Id: 0x%08x\n", codec->subsystem_id);
snd_iprintf(buffer, "Revision Id: 0x%x\n", codec->revision_id); snd_iprintf(buffer, "Revision Id: 0x%x\n", codec->revision_id);
if (codec->mfg) if (codec->mfg)
@ -554,6 +557,12 @@ static void print_codec_info(struct snd_info_entry *entry,
snd_iprintf(buffer, " Amp-Out caps: "); snd_iprintf(buffer, " Amp-Out caps: ");
print_amp_caps(buffer, codec, nid, HDA_OUTPUT); print_amp_caps(buffer, codec, nid, HDA_OUTPUT);
snd_iprintf(buffer, " Amp-Out vals: "); snd_iprintf(buffer, " Amp-Out vals: ");
if (wid_type == AC_WID_PIN &&
codec->pin_amp_workaround)
print_amp_vals(buffer, codec, nid, HDA_OUTPUT,
wid_caps & AC_WCAP_STEREO,
conn_len);
else
print_amp_vals(buffer, codec, nid, HDA_OUTPUT, print_amp_vals(buffer, codec, nid, HDA_OUTPUT,
wid_caps & AC_WCAP_STEREO, 1); wid_caps & AC_WCAP_STEREO, 1);
} }

View file

@ -27,11 +27,12 @@
#include <sound/core.h> #include <sound/core.h>
#include "hda_codec.h" #include "hda_codec.h"
#include "hda_local.h" #include "hda_local.h"
#include "hda_beep.h"
struct ad198x_spec { struct ad198x_spec {
struct snd_kcontrol_new *mixers[5]; struct snd_kcontrol_new *mixers[5];
int num_mixers; int num_mixers;
unsigned int beep_amp; /* beep amp value, set via set_beep_amp() */
const struct hda_verb *init_verbs[5]; /* initialization verbs const struct hda_verb *init_verbs[5]; /* initialization verbs
* don't forget NULL termination! * don't forget NULL termination!
*/ */
@ -154,6 +155,16 @@ static const char *ad_slave_sws[] = {
static void ad198x_free_kctls(struct hda_codec *codec); static void ad198x_free_kctls(struct hda_codec *codec);
/* additional beep mixers; the actual parameters are overwritten at build */
static struct snd_kcontrol_new ad_beep_mixer[] = {
HDA_CODEC_VOLUME("Beep Playback Volume", 0, 0, HDA_OUTPUT),
HDA_CODEC_MUTE("Beep Playback Switch", 0, 0, HDA_OUTPUT),
{ } /* end */
};
#define set_beep_amp(spec, nid, idx, dir) \
((spec)->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir)) /* mono */
static int ad198x_build_controls(struct hda_codec *codec) static int ad198x_build_controls(struct hda_codec *codec)
{ {
struct ad198x_spec *spec = codec->spec; struct ad198x_spec *spec = codec->spec;
@ -181,6 +192,21 @@ static int ad198x_build_controls(struct hda_codec *codec)
return err; return err;
} }
/* create beep controls if needed */
if (spec->beep_amp) {
struct snd_kcontrol_new *knew;
for (knew = ad_beep_mixer; knew->name; knew++) {
struct snd_kcontrol *kctl;
kctl = snd_ctl_new1(knew, codec);
if (!kctl)
return -ENOMEM;
kctl->private_value = spec->beep_amp;
err = snd_hda_ctl_add(codec, kctl);
if (err < 0)
return err;
}
}
/* if we have no master control, let's create it */ /* if we have no master control, let's create it */
if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) { if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
unsigned int vmaster_tlv[4]; unsigned int vmaster_tlv[4];
@ -406,7 +432,8 @@ static void ad198x_free(struct hda_codec *codec)
return; return;
ad198x_free_kctls(codec); ad198x_free_kctls(codec);
kfree(codec->spec); kfree(spec);
snd_hda_detach_beep_device(codec);
} }
static struct hda_codec_ops ad198x_patch_ops = { static struct hda_codec_ops ad198x_patch_ops = {
@ -545,8 +572,6 @@ static struct snd_kcontrol_new ad1986a_mixers[] = {
HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT), HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x18, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x18, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME("Mono Playback Volume", 0x1e, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME("Mono Playback Volume", 0x1e, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE("Mono Playback Switch", 0x1e, 0x0, HDA_OUTPUT), HDA_CODEC_MUTE("Mono Playback Switch", 0x1e, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
@ -610,8 +635,7 @@ static struct snd_kcontrol_new ad1986a_laptop_mixers[] = {
HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT), HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT),
/* HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x18, 0x0, HDA_OUTPUT), /*
HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x18, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME("Mono Playback Volume", 0x1e, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME("Mono Playback Volume", 0x1e, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE("Mono Playback Switch", 0x1e, 0x0, HDA_OUTPUT), */ HDA_CODEC_MUTE("Mono Playback Switch", 0x1e, 0x0, HDA_OUTPUT), */
HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
@ -809,8 +833,6 @@ static struct snd_kcontrol_new ad1986a_laptop_automute_mixers[] = {
HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT), HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME("Beep Playback Volume", 0x18, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE("Beep Playback Switch", 0x18, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT), HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
{ {
@ -1002,10 +1024,8 @@ static struct snd_pci_quirk ad1986a_cfg_tbl[] = {
SND_PCI_QUIRK(0x1179, 0xff40, "Toshiba", AD1986A_LAPTOP_EAPD), SND_PCI_QUIRK(0x1179, 0xff40, "Toshiba", AD1986A_LAPTOP_EAPD),
SND_PCI_QUIRK(0x144d, 0xb03c, "Samsung R55", AD1986A_3STACK), SND_PCI_QUIRK(0x144d, 0xb03c, "Samsung R55", AD1986A_3STACK),
SND_PCI_QUIRK(0x144d, 0xc01e, "FSC V2060", AD1986A_LAPTOP), SND_PCI_QUIRK(0x144d, 0xc01e, "FSC V2060", AD1986A_LAPTOP),
SND_PCI_QUIRK(0x144d, 0xc023, "Samsung X60", AD1986A_SAMSUNG),
SND_PCI_QUIRK(0x144d, 0xc024, "Samsung R65", AD1986A_SAMSUNG),
SND_PCI_QUIRK(0x144d, 0xc026, "Samsung X11", AD1986A_SAMSUNG),
SND_PCI_QUIRK(0x144d, 0xc027, "Samsung Q1", AD1986A_ULTRA), SND_PCI_QUIRK(0x144d, 0xc027, "Samsung Q1", AD1986A_ULTRA),
SND_PCI_QUIRK_MASK(0x144d, 0xff00, 0xc000, "Samsung", AD1986A_SAMSUNG),
SND_PCI_QUIRK(0x144d, 0xc504, "Samsung Q35", AD1986A_3STACK), SND_PCI_QUIRK(0x144d, 0xc504, "Samsung Q35", AD1986A_3STACK),
SND_PCI_QUIRK(0x17aa, 0x1011, "Lenovo M55", AD1986A_LAPTOP), SND_PCI_QUIRK(0x17aa, 0x1011, "Lenovo M55", AD1986A_LAPTOP),
SND_PCI_QUIRK(0x17aa, 0x1017, "Lenovo A60", AD1986A_3STACK), SND_PCI_QUIRK(0x17aa, 0x1017, "Lenovo A60", AD1986A_3STACK),
@ -1027,15 +1047,14 @@ static struct hda_amp_list ad1986a_loopbacks[] = {
static int is_jack_available(struct hda_codec *codec, hda_nid_t nid) static int is_jack_available(struct hda_codec *codec, hda_nid_t nid)
{ {
unsigned int conf = snd_hda_codec_read(codec, nid, 0, unsigned int conf = snd_hda_codec_get_pincfg(codec, nid);
AC_VERB_GET_CONFIG_DEFAULT, 0);
return get_defcfg_connect(conf) != AC_JACK_PORT_NONE; return get_defcfg_connect(conf) != AC_JACK_PORT_NONE;
} }
static int patch_ad1986a(struct hda_codec *codec) static int patch_ad1986a(struct hda_codec *codec)
{ {
struct ad198x_spec *spec; struct ad198x_spec *spec;
int board_config; int err, board_config;
spec = kzalloc(sizeof(*spec), GFP_KERNEL); spec = kzalloc(sizeof(*spec), GFP_KERNEL);
if (spec == NULL) if (spec == NULL)
@ -1043,6 +1062,13 @@ static int patch_ad1986a(struct hda_codec *codec)
codec->spec = spec; codec->spec = spec;
err = snd_hda_attach_beep_device(codec, 0x19);
if (err < 0) {
ad198x_free(codec);
return err;
}
set_beep_amp(spec, 0x18, 0, HDA_OUTPUT);
spec->multiout.max_channels = 6; spec->multiout.max_channels = 6;
spec->multiout.num_dacs = ARRAY_SIZE(ad1986a_dac_nids); spec->multiout.num_dacs = ARRAY_SIZE(ad1986a_dac_nids);
spec->multiout.dac_nids = ad1986a_dac_nids; spec->multiout.dac_nids = ad1986a_dac_nids;
@ -1222,8 +1248,6 @@ static struct snd_kcontrol_new ad1983_mixers[] = {
HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT), HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME("Line Playback Volume", 0x13, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME("Line Playback Volume", 0x13, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE("Line Playback Switch", 0x13, 0x0, HDA_OUTPUT), HDA_CODEC_MUTE("Line Playback Switch", 0x13, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME_MONO("PC Speaker Playback Volume", 0x10, 1, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE_MONO("PC Speaker Playback Switch", 0x10, 1, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME("Mic Boost", 0x0c, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME("Mic Boost", 0x0c, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT), HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
@ -1294,6 +1318,7 @@ static struct hda_amp_list ad1983_loopbacks[] = {
static int patch_ad1983(struct hda_codec *codec) static int patch_ad1983(struct hda_codec *codec)
{ {
struct ad198x_spec *spec; struct ad198x_spec *spec;
int err;
spec = kzalloc(sizeof(*spec), GFP_KERNEL); spec = kzalloc(sizeof(*spec), GFP_KERNEL);
if (spec == NULL) if (spec == NULL)
@ -1301,6 +1326,13 @@ static int patch_ad1983(struct hda_codec *codec)
codec->spec = spec; codec->spec = spec;
err = snd_hda_attach_beep_device(codec, 0x10);
if (err < 0) {
ad198x_free(codec);
return err;
}
set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
spec->multiout.max_channels = 2; spec->multiout.max_channels = 2;
spec->multiout.num_dacs = ARRAY_SIZE(ad1983_dac_nids); spec->multiout.num_dacs = ARRAY_SIZE(ad1983_dac_nids);
spec->multiout.dac_nids = ad1983_dac_nids; spec->multiout.dac_nids = ad1983_dac_nids;
@ -1370,8 +1402,6 @@ static struct snd_kcontrol_new ad1981_mixers[] = {
HDA_CODEC_MUTE("Mic Playback Switch", 0x1c, 0x0, HDA_OUTPUT), HDA_CODEC_MUTE("Mic Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT), HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME_MONO("PC Speaker Playback Volume", 0x0d, 1, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE_MONO("PC Speaker Playback Switch", 0x0d, 1, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME("Front Mic Boost", 0x08, 0x0, HDA_INPUT), HDA_CODEC_VOLUME("Front Mic Boost", 0x08, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME("Mic Boost", 0x18, 0x0, HDA_INPUT), HDA_CODEC_VOLUME("Mic Boost", 0x18, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
@ -1416,8 +1446,8 @@ static struct hda_verb ad1981_init_verbs[] = {
{0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
{0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
/* Mic boost: 0dB */ /* Mic boost: 0dB */
{0x08, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
{0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
/* Record selector: Front mic */ /* Record selector: Front mic */
{0x15, AC_VERB_SET_CONNECT_SEL, 0x0}, {0x15, AC_VERB_SET_CONNECT_SEL, 0x0},
{0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
@ -1682,10 +1712,10 @@ static struct snd_pci_quirk ad1981_cfg_tbl[] = {
SND_PCI_QUIRK(0x1014, 0x0597, "Lenovo Z60", AD1981_THINKPAD), SND_PCI_QUIRK(0x1014, 0x0597, "Lenovo Z60", AD1981_THINKPAD),
SND_PCI_QUIRK(0x1014, 0x05b7, "Lenovo Z60m", AD1981_THINKPAD), SND_PCI_QUIRK(0x1014, 0x05b7, "Lenovo Z60m", AD1981_THINKPAD),
/* All HP models */ /* All HP models */
SND_PCI_QUIRK(0x103c, 0, "HP nx", AD1981_HP), SND_PCI_QUIRK_VENDOR(0x103c, "HP nx", AD1981_HP),
SND_PCI_QUIRK(0x1179, 0x0001, "Toshiba U205", AD1981_TOSHIBA), SND_PCI_QUIRK(0x1179, 0x0001, "Toshiba U205", AD1981_TOSHIBA),
/* Lenovo Thinkpad T60/X60/Z6xx */ /* Lenovo Thinkpad T60/X60/Z6xx */
SND_PCI_QUIRK(0x17aa, 0, "Lenovo Thinkpad", AD1981_THINKPAD), SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo Thinkpad", AD1981_THINKPAD),
/* HP nx6320 (reversed SSID, H/W bug) */ /* HP nx6320 (reversed SSID, H/W bug) */
SND_PCI_QUIRK(0x30b0, 0x103c, "HP nx6320", AD1981_HP), SND_PCI_QUIRK(0x30b0, 0x103c, "HP nx6320", AD1981_HP),
{} {}
@ -1694,7 +1724,7 @@ static struct snd_pci_quirk ad1981_cfg_tbl[] = {
static int patch_ad1981(struct hda_codec *codec) static int patch_ad1981(struct hda_codec *codec)
{ {
struct ad198x_spec *spec; struct ad198x_spec *spec;
int board_config; int err, board_config;
spec = kzalloc(sizeof(*spec), GFP_KERNEL); spec = kzalloc(sizeof(*spec), GFP_KERNEL);
if (spec == NULL) if (spec == NULL)
@ -1702,6 +1732,13 @@ static int patch_ad1981(struct hda_codec *codec)
codec->spec = spec; codec->spec = spec;
err = snd_hda_attach_beep_device(codec, 0x10);
if (err < 0) {
ad198x_free(codec);
return err;
}
set_beep_amp(spec, 0x0d, 0, HDA_OUTPUT);
spec->multiout.max_channels = 2; spec->multiout.max_channels = 2;
spec->multiout.num_dacs = ARRAY_SIZE(ad1981_dac_nids); spec->multiout.num_dacs = ARRAY_SIZE(ad1981_dac_nids);
spec->multiout.dac_nids = ad1981_dac_nids; spec->multiout.dac_nids = ad1981_dac_nids;
@ -1988,9 +2025,6 @@ static struct snd_kcontrol_new ad1988_6stack_mixers2[] = {
HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x4, HDA_INPUT), HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x4, HDA_INPUT),
HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x4, HDA_INPUT), HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x4, HDA_INPUT),
HDA_CODEC_VOLUME("Beep Playback Volume", 0x10, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE("Beep Playback Switch", 0x10, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT), HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
@ -2034,9 +2068,6 @@ static struct snd_kcontrol_new ad1988_3stack_mixers2[] = {
HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x4, HDA_INPUT), HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x4, HDA_INPUT),
HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x4, HDA_INPUT), HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x4, HDA_INPUT),
HDA_CODEC_VOLUME("Beep Playback Volume", 0x10, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE("Beep Playback Switch", 0x10, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT), HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
@ -2066,9 +2097,6 @@ static struct snd_kcontrol_new ad1988_laptop_mixers[] = {
HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT), HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT), HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
HDA_CODEC_VOLUME("Beep Playback Volume", 0x10, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE("Beep Playback Switch", 0x10, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT), HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
@ -2297,10 +2325,6 @@ static struct hda_verb ad1988_capture_init_verbs[] = {
{0x0c, AC_VERB_SET_CONNECT_SEL, 0x1}, {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
{0x0d, AC_VERB_SET_CONNECT_SEL, 0x1}, {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
{0x0e, AC_VERB_SET_CONNECT_SEL, 0x1}, {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
/* ADCs; muted */
{0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
{0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
{ } { }
}; };
@ -2408,10 +2432,6 @@ static struct hda_verb ad1988_3stack_init_verbs[] = {
{0x0c, AC_VERB_SET_CONNECT_SEL, 0x1}, {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
{0x0d, AC_VERB_SET_CONNECT_SEL, 0x1}, {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
{0x0e, AC_VERB_SET_CONNECT_SEL, 0x1}, {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
/* ADCs; muted */
{0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
{0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
/* Analog Mix output amp */ /* Analog Mix output amp */
{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */ {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
{ } { }
@ -2483,10 +2503,6 @@ static struct hda_verb ad1988_laptop_init_verbs[] = {
{0x0c, AC_VERB_SET_CONNECT_SEL, 0x1}, {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
{0x0d, AC_VERB_SET_CONNECT_SEL, 0x1}, {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
{0x0e, AC_VERB_SET_CONNECT_SEL, 0x1}, {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
/* ADCs; muted */
{0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
{0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
/* Analog Mix output amp */ /* Analog Mix output amp */
{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */ {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
{ } { }
@ -2890,7 +2906,7 @@ static int ad1988_parse_auto_config(struct hda_codec *codec)
spec->multiout.max_channels = spec->multiout.num_dacs * 2; spec->multiout.max_channels = spec->multiout.num_dacs * 2;
if (spec->autocfg.dig_out_pin) if (spec->autocfg.dig_outs)
spec->multiout.dig_out_nid = AD1988_SPDIF_OUT; spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
if (spec->autocfg.dig_in_pin) if (spec->autocfg.dig_in_pin)
spec->dig_in_nid = AD1988_SPDIF_IN; spec->dig_in_nid = AD1988_SPDIF_IN;
@ -2940,7 +2956,7 @@ static struct snd_pci_quirk ad1988_cfg_tbl[] = {
static int patch_ad1988(struct hda_codec *codec) static int patch_ad1988(struct hda_codec *codec)
{ {
struct ad198x_spec *spec; struct ad198x_spec *spec;
int board_config; int err, board_config;
spec = kzalloc(sizeof(*spec), GFP_KERNEL); spec = kzalloc(sizeof(*spec), GFP_KERNEL);
if (spec == NULL) if (spec == NULL)
@ -2960,7 +2976,7 @@ static int patch_ad1988(struct hda_codec *codec)
if (board_config == AD1988_AUTO) { if (board_config == AD1988_AUTO) {
/* automatic parse from the BIOS config */ /* automatic parse from the BIOS config */
int err = ad1988_parse_auto_config(codec); err = ad1988_parse_auto_config(codec);
if (err < 0) { if (err < 0) {
ad198x_free(codec); ad198x_free(codec);
return err; return err;
@ -2970,6 +2986,13 @@ static int patch_ad1988(struct hda_codec *codec)
} }
} }
err = snd_hda_attach_beep_device(codec, 0x10);
if (err < 0) {
ad198x_free(codec);
return err;
}
set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
switch (board_config) { switch (board_config) {
case AD1988_6STACK: case AD1988_6STACK:
case AD1988_6STACK_DIG: case AD1988_6STACK_DIG:
@ -3126,12 +3149,6 @@ static struct snd_kcontrol_new ad1884_base_mixers[] = {
HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT), HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x02, HDA_INPUT), HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x02, HDA_INPUT),
HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT), HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT),
/*
HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x20, 0x03, HDA_INPUT),
HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x20, 0x03, HDA_INPUT),
HDA_CODEC_VOLUME("Digital Beep Playback Volume", 0x10, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE("Digital Beep Playback Switch", 0x10, 0x0, HDA_OUTPUT),
*/
HDA_CODEC_VOLUME("Mic Boost", 0x15, 0x0, HDA_INPUT), HDA_CODEC_VOLUME("Mic Boost", 0x15, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT), HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
@ -3204,10 +3221,10 @@ static struct hda_verb ad1884_init_verbs[] = {
{0x0e, AC_VERB_SET_CONNECT_SEL, 0x1}, {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
/* Port-B (front mic) pin */ /* Port-B (front mic) pin */
{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
/* Port-C (rear mic) pin */ /* Port-C (rear mic) pin */
{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
/* Analog mixer; mute as default */ /* Analog mixer; mute as default */
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
@ -3240,7 +3257,7 @@ static const char *ad1884_slave_vols[] = {
"CD Playback Volume", "CD Playback Volume",
"Internal Mic Playback Volume", "Internal Mic Playback Volume",
"Docking Mic Playback Volume" "Docking Mic Playback Volume"
"Beep Playback Volume", /* "Beep Playback Volume", */
"IEC958 Playback Volume", "IEC958 Playback Volume",
NULL NULL
}; };
@ -3248,6 +3265,7 @@ static const char *ad1884_slave_vols[] = {
static int patch_ad1884(struct hda_codec *codec) static int patch_ad1884(struct hda_codec *codec)
{ {
struct ad198x_spec *spec; struct ad198x_spec *spec;
int err;
spec = kzalloc(sizeof(*spec), GFP_KERNEL); spec = kzalloc(sizeof(*spec), GFP_KERNEL);
if (spec == NULL) if (spec == NULL)
@ -3255,6 +3273,13 @@ static int patch_ad1884(struct hda_codec *codec)
codec->spec = spec; codec->spec = spec;
err = snd_hda_attach_beep_device(codec, 0x10);
if (err < 0) {
ad198x_free(codec);
return err;
}
set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
spec->multiout.max_channels = 2; spec->multiout.max_channels = 2;
spec->multiout.num_dacs = ARRAY_SIZE(ad1884_dac_nids); spec->multiout.num_dacs = ARRAY_SIZE(ad1884_dac_nids);
spec->multiout.dac_nids = ad1884_dac_nids; spec->multiout.dac_nids = ad1884_dac_nids;
@ -3321,8 +3346,6 @@ static struct snd_kcontrol_new ad1984_thinkpad_mixers[] = {
HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT), HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME("Internal Mic Boost", 0x15, 0x0, HDA_INPUT), HDA_CODEC_VOLUME("Internal Mic Boost", 0x15, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME("Docking Mic Boost", 0x25, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME("Docking Mic Boost", 0x25, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x03, HDA_INPUT),
HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x03, HDA_INPUT),
HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT), HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
@ -3358,7 +3381,7 @@ static struct hda_verb ad1984_thinkpad_init_verbs[] = {
{0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
/* docking mic boost */ /* docking mic boost */
{0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
/* Analog mixer - docking mic; mute as default */ /* Analog mixer - docking mic; mute as default */
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
/* enable EAPD bit */ /* enable EAPD bit */
@ -3379,10 +3402,6 @@ static struct snd_kcontrol_new ad1984_dell_desktop_mixers[] = {
HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT), HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
HDA_CODEC_VOLUME("Line-In Playback Volume", 0x20, 0x01, HDA_INPUT), HDA_CODEC_VOLUME("Line-In Playback Volume", 0x20, 0x01, HDA_INPUT),
HDA_CODEC_MUTE("Line-In Playback Switch", 0x20, 0x01, HDA_INPUT), HDA_CODEC_MUTE("Line-In Playback Switch", 0x20, 0x01, HDA_INPUT),
/*
HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x20, 0x03, HDA_INPUT),
HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x20, 0x03, HDA_INPUT),
*/
HDA_CODEC_VOLUME("Line-In Boost", 0x15, 0x0, HDA_INPUT), HDA_CODEC_VOLUME("Line-In Boost", 0x15, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT), HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
@ -3468,7 +3487,7 @@ static const char *ad1984_models[AD1984_MODELS] = {
static struct snd_pci_quirk ad1984_cfg_tbl[] = { static struct snd_pci_quirk ad1984_cfg_tbl[] = {
/* Lenovo Thinkpad T61/X61 */ /* Lenovo Thinkpad T61/X61 */
SND_PCI_QUIRK(0x17aa, 0, "Lenovo Thinkpad", AD1984_THINKPAD), SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo Thinkpad", AD1984_THINKPAD),
SND_PCI_QUIRK(0x1028, 0x0214, "Dell T3400", AD1984_DELL_DESKTOP), SND_PCI_QUIRK(0x1028, 0x0214, "Dell T3400", AD1984_DELL_DESKTOP),
{} {}
}; };
@ -3561,8 +3580,6 @@ static struct snd_kcontrol_new ad1884a_base_mixers[] = {
HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x04, HDA_INPUT), HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x02, HDA_INPUT), HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x02, HDA_INPUT),
HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT), HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT),
HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x03, HDA_INPUT),
HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x03, HDA_INPUT),
HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT), HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME("Line Boost", 0x15, 0x0, HDA_INPUT), HDA_CODEC_VOLUME("Line Boost", 0x15, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME("Mic Boost", 0x25, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME("Mic Boost", 0x25, 0x0, HDA_OUTPUT),
@ -3622,10 +3639,10 @@ static struct hda_verb ad1884a_init_verbs[] = {
{0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
/* Port-B (front mic) pin */ /* Port-B (front mic) pin */
{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
/* Port-C (rear line-in) pin */ /* Port-C (rear line-in) pin */
{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
/* Port-E (rear mic) pin */ /* Port-E (rear mic) pin */
{0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
@ -3695,8 +3712,6 @@ static struct snd_kcontrol_new ad1884a_laptop_mixers[] = {
HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x20, 0x01, HDA_INPUT), HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
HDA_CODEC_VOLUME("Dock Mic Playback Volume", 0x20, 0x04, HDA_INPUT), HDA_CODEC_VOLUME("Dock Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
HDA_CODEC_MUTE("Dock Mic Playback Switch", 0x20, 0x04, HDA_INPUT), HDA_CODEC_MUTE("Dock Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x03, HDA_INPUT),
HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x03, HDA_INPUT),
HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT), HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME("Internal Mic Boost", 0x15, 0x0, HDA_INPUT), HDA_CODEC_VOLUME("Internal Mic Boost", 0x15, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME("Dock Mic Boost", 0x25, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME("Dock Mic Boost", 0x25, 0x0, HDA_OUTPUT),
@ -3724,8 +3739,6 @@ static struct snd_kcontrol_new ad1884a_mobile_mixers[] = {
HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT), HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT), HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT), HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x03, HDA_INPUT),
HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x03, HDA_INPUT),
HDA_CODEC_VOLUME("Mic Capture Volume", 0x14, 0x0, HDA_INPUT), HDA_CODEC_VOLUME("Mic Capture Volume", 0x14, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME("Internal Mic Capture Volume", 0x15, 0x0, HDA_INPUT), HDA_CODEC_VOLUME("Internal Mic Capture Volume", 0x15, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
@ -3836,8 +3849,6 @@ static struct snd_kcontrol_new ad1984a_thinkpad_mixers[] = {
HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT), HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT), HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT), HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x03, HDA_INPUT),
HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x03, HDA_INPUT),
HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT), HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME("Internal Mic Boost", 0x17, 0x0, HDA_INPUT), HDA_CODEC_VOLUME("Internal Mic Boost", 0x17, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
@ -3911,9 +3922,9 @@ static struct snd_pci_quirk ad1884a_cfg_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x3030, "HP", AD1884A_MOBILE), SND_PCI_QUIRK(0x103c, 0x3030, "HP", AD1884A_MOBILE),
SND_PCI_QUIRK(0x103c, 0x3037, "HP 2230s", AD1884A_LAPTOP), SND_PCI_QUIRK(0x103c, 0x3037, "HP 2230s", AD1884A_LAPTOP),
SND_PCI_QUIRK(0x103c, 0x3056, "HP", AD1884A_MOBILE), SND_PCI_QUIRK(0x103c, 0x3056, "HP", AD1884A_MOBILE),
SND_PCI_QUIRK(0x103c, 0x30e6, "HP 6730b", AD1884A_LAPTOP), SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x3070, "HP", AD1884A_MOBILE),
SND_PCI_QUIRK(0x103c, 0x30e7, "HP EliteBook 8530p", AD1884A_LAPTOP), SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x30e0, "HP laptop", AD1884A_LAPTOP),
SND_PCI_QUIRK(0x103c, 0x3614, "HP 6730s", AD1884A_LAPTOP), SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x3600, "HP laptop", AD1884A_LAPTOP),
SND_PCI_QUIRK(0x17aa, 0x20ac, "Thinkpad X300", AD1884A_THINKPAD), SND_PCI_QUIRK(0x17aa, 0x20ac, "Thinkpad X300", AD1884A_THINKPAD),
{} {}
}; };
@ -3921,7 +3932,7 @@ static struct snd_pci_quirk ad1884a_cfg_tbl[] = {
static int patch_ad1884a(struct hda_codec *codec) static int patch_ad1884a(struct hda_codec *codec)
{ {
struct ad198x_spec *spec; struct ad198x_spec *spec;
int board_config; int err, board_config;
spec = kzalloc(sizeof(*spec), GFP_KERNEL); spec = kzalloc(sizeof(*spec), GFP_KERNEL);
if (spec == NULL) if (spec == NULL)
@ -3929,6 +3940,13 @@ static int patch_ad1884a(struct hda_codec *codec)
codec->spec = spec; codec->spec = spec;
err = snd_hda_attach_beep_device(codec, 0x10);
if (err < 0) {
ad198x_free(codec);
return err;
}
set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
spec->multiout.max_channels = 2; spec->multiout.max_channels = 2;
spec->multiout.num_dacs = ARRAY_SIZE(ad1884a_dac_nids); spec->multiout.num_dacs = ARRAY_SIZE(ad1884a_dac_nids);
spec->multiout.dac_nids = ad1884a_dac_nids; spec->multiout.dac_nids = ad1884a_dac_nids;
@ -3966,6 +3984,14 @@ static int patch_ad1884a(struct hda_codec *codec)
spec->multiout.dig_out_nid = 0; spec->multiout.dig_out_nid = 0;
codec->patch_ops.unsol_event = ad1884a_hp_unsol_event; codec->patch_ops.unsol_event = ad1884a_hp_unsol_event;
codec->patch_ops.init = ad1884a_hp_init; codec->patch_ops.init = ad1884a_hp_init;
/* set the upper-limit for mixer amp to 0dB for avoiding the
* possible damage by overloading
*/
snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT,
(0x17 << AC_AMPCAP_OFFSET_SHIFT) |
(0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
(0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
(1 << AC_AMPCAP_MUTE_SHIFT));
break; break;
case AD1884A_THINKPAD: case AD1884A_THINKPAD:
spec->mixers[0] = ad1984a_thinkpad_mixers; spec->mixers[0] = ad1984a_thinkpad_mixers;
@ -4083,8 +4109,6 @@ static struct snd_kcontrol_new ad1882_loopback_mixers[] = {
HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x04, HDA_INPUT), HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x04, HDA_INPUT),
HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x06, HDA_INPUT), HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x06, HDA_INPUT),
HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x06, HDA_INPUT), HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x06, HDA_INPUT),
HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x07, HDA_INPUT),
HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x07, HDA_INPUT),
{ } /* end */ { } /* end */
}; };
@ -4097,8 +4121,6 @@ static struct snd_kcontrol_new ad1882a_loopback_mixers[] = {
HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x01, HDA_INPUT), HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x01, HDA_INPUT),
HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x06, HDA_INPUT), HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x06, HDA_INPUT),
HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x06, HDA_INPUT), HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x06, HDA_INPUT),
HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x07, HDA_INPUT),
HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x07, HDA_INPUT),
HDA_CODEC_VOLUME("Digital Mic Boost", 0x1f, 0x0, HDA_INPUT), HDA_CODEC_VOLUME("Digital Mic Boost", 0x1f, 0x0, HDA_INPUT),
{ } /* end */ { } /* end */
}; };
@ -4257,7 +4279,7 @@ static const char *ad1882_models[AD1986A_MODELS] = {
static int patch_ad1882(struct hda_codec *codec) static int patch_ad1882(struct hda_codec *codec)
{ {
struct ad198x_spec *spec; struct ad198x_spec *spec;
int board_config; int err, board_config;
spec = kzalloc(sizeof(*spec), GFP_KERNEL); spec = kzalloc(sizeof(*spec), GFP_KERNEL);
if (spec == NULL) if (spec == NULL)
@ -4265,6 +4287,13 @@ static int patch_ad1882(struct hda_codec *codec)
codec->spec = spec; codec->spec = spec;
err = snd_hda_attach_beep_device(codec, 0x10);
if (err < 0) {
ad198x_free(codec);
return err;
}
set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
spec->multiout.max_channels = 6; spec->multiout.max_channels = 6;
spec->multiout.num_dacs = 3; spec->multiout.num_dacs = 3;
spec->multiout.dac_nids = ad1882_dac_nids; spec->multiout.dac_nids = ad1882_dac_nids;

View file

@ -680,13 +680,13 @@ static int patch_cmi9880(struct hda_codec *codec)
struct auto_pin_cfg cfg; struct auto_pin_cfg cfg;
/* collect pin default configuration */ /* collect pin default configuration */
port_e = snd_hda_codec_read(codec, 0x0f, 0, AC_VERB_GET_CONFIG_DEFAULT, 0); port_e = snd_hda_codec_get_pincfg(codec, 0x0f);
port_f = snd_hda_codec_read(codec, 0x10, 0, AC_VERB_GET_CONFIG_DEFAULT, 0); port_f = snd_hda_codec_get_pincfg(codec, 0x10);
spec->front_panel = 1; spec->front_panel = 1;
if (get_defcfg_connect(port_e) == AC_JACK_PORT_NONE || if (get_defcfg_connect(port_e) == AC_JACK_PORT_NONE ||
get_defcfg_connect(port_f) == AC_JACK_PORT_NONE) { get_defcfg_connect(port_f) == AC_JACK_PORT_NONE) {
port_g = snd_hda_codec_read(codec, 0x1f, 0, AC_VERB_GET_CONFIG_DEFAULT, 0); port_g = snd_hda_codec_get_pincfg(codec, 0x1f);
port_h = snd_hda_codec_read(codec, 0x20, 0, AC_VERB_GET_CONFIG_DEFAULT, 0); port_h = snd_hda_codec_get_pincfg(codec, 0x20);
spec->channel_modes = cmi9880_channel_modes; spec->channel_modes = cmi9880_channel_modes;
/* no front panel */ /* no front panel */
if (get_defcfg_connect(port_g) == AC_JACK_PORT_NONE || if (get_defcfg_connect(port_g) == AC_JACK_PORT_NONE ||
@ -703,8 +703,8 @@ static int patch_cmi9880(struct hda_codec *codec)
spec->multiout.max_channels = cmi9880_channel_modes[0].channels; spec->multiout.max_channels = cmi9880_channel_modes[0].channels;
} else { } else {
spec->input_mux = &cmi9880_basic_mux; spec->input_mux = &cmi9880_basic_mux;
port_spdifi = snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONFIG_DEFAULT, 0); port_spdifi = snd_hda_codec_get_pincfg(codec, 0x13);
port_spdifo = snd_hda_codec_read(codec, 0x12, 0, AC_VERB_GET_CONFIG_DEFAULT, 0); port_spdifo = snd_hda_codec_get_pincfg(codec, 0x12);
if (get_defcfg_connect(port_spdifo) != AC_JACK_PORT_NONE) if (get_defcfg_connect(port_spdifo) != AC_JACK_PORT_NONE)
spec->multiout.dig_out_nid = CMI_DIG_OUT_NID; spec->multiout.dig_out_nid = CMI_DIG_OUT_NID;
if (get_defcfg_connect(port_spdifi) != AC_JACK_PORT_NONE) if (get_defcfg_connect(port_spdifi) != AC_JACK_PORT_NONE)

View file

@ -58,6 +58,7 @@ struct conexant_spec {
struct snd_kcontrol_new *mixers[5]; struct snd_kcontrol_new *mixers[5];
int num_mixers; int num_mixers;
hda_nid_t vmaster_nid;
const struct hda_verb *init_verbs[5]; /* initialization verbs const struct hda_verb *init_verbs[5]; /* initialization verbs
* don't forget NULL * don't forget NULL
@ -72,6 +73,7 @@ struct conexant_spec {
*/ */
unsigned int cur_eapd; unsigned int cur_eapd;
unsigned int hp_present; unsigned int hp_present;
unsigned int no_auto_mic;
unsigned int need_dac_fix; unsigned int need_dac_fix;
/* capture */ /* capture */
@ -461,6 +463,29 @@ static void conexant_free(struct hda_codec *codec)
kfree(codec->spec); kfree(codec->spec);
} }
static struct snd_kcontrol_new cxt_capture_mixers[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Capture Source",
.info = conexant_mux_enum_info,
.get = conexant_mux_enum_get,
.put = conexant_mux_enum_put
},
{}
};
static const char *slave_vols[] = {
"Headphone Playback Volume",
"Speaker Playback Volume",
NULL
};
static const char *slave_sws[] = {
"Headphone Playback Switch",
"Speaker Playback Switch",
NULL
};
static int conexant_build_controls(struct hda_codec *codec) static int conexant_build_controls(struct hda_codec *codec)
{ {
struct conexant_spec *spec = codec->spec; struct conexant_spec *spec = codec->spec;
@ -488,6 +513,32 @@ static int conexant_build_controls(struct hda_codec *codec)
if (err < 0) if (err < 0)
return err; return err;
} }
/* if we have no master control, let's create it */
if (spec->vmaster_nid &&
!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
unsigned int vmaster_tlv[4];
snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid,
HDA_OUTPUT, vmaster_tlv);
err = snd_hda_add_vmaster(codec, "Master Playback Volume",
vmaster_tlv, slave_vols);
if (err < 0)
return err;
}
if (spec->vmaster_nid &&
!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
err = snd_hda_add_vmaster(codec, "Master Playback Switch",
NULL, slave_sws);
if (err < 0)
return err;
}
if (spec->input_mux) {
err = snd_hda_add_new_ctls(codec, cxt_capture_mixers);
if (err < 0)
return err;
}
return 0; return 0;
} }
@ -719,13 +770,6 @@ static void cxt5045_hp_unsol_event(struct hda_codec *codec,
} }
static struct snd_kcontrol_new cxt5045_mixers[] = { static struct snd_kcontrol_new cxt5045_mixers[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Capture Source",
.info = conexant_mux_enum_info,
.get = conexant_mux_enum_get,
.put = conexant_mux_enum_put
},
HDA_CODEC_VOLUME("Int Mic Capture Volume", 0x1a, 0x01, HDA_INPUT), HDA_CODEC_VOLUME("Int Mic Capture Volume", 0x1a, 0x01, HDA_INPUT),
HDA_CODEC_MUTE("Int Mic Capture Switch", 0x1a, 0x01, HDA_INPUT), HDA_CODEC_MUTE("Int Mic Capture Switch", 0x1a, 0x01, HDA_INPUT),
HDA_CODEC_VOLUME("Ext Mic Capture Volume", 0x1a, 0x02, HDA_INPUT), HDA_CODEC_VOLUME("Ext Mic Capture Volume", 0x1a, 0x02, HDA_INPUT),
@ -759,13 +803,6 @@ static struct snd_kcontrol_new cxt5045_benq_mixers[] = {
}; };
static struct snd_kcontrol_new cxt5045_mixers_hp530[] = { static struct snd_kcontrol_new cxt5045_mixers_hp530[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Capture Source",
.info = conexant_mux_enum_info,
.get = conexant_mux_enum_get,
.put = conexant_mux_enum_put
},
HDA_CODEC_VOLUME("Int Mic Capture Volume", 0x1a, 0x02, HDA_INPUT), HDA_CODEC_VOLUME("Int Mic Capture Volume", 0x1a, 0x02, HDA_INPUT),
HDA_CODEC_MUTE("Int Mic Capture Switch", 0x1a, 0x02, HDA_INPUT), HDA_CODEC_MUTE("Int Mic Capture Switch", 0x1a, 0x02, HDA_INPUT),
HDA_CODEC_VOLUME("Ext Mic Capture Volume", 0x1a, 0x01, HDA_INPUT), HDA_CODEC_VOLUME("Ext Mic Capture Volume", 0x1a, 0x01, HDA_INPUT),
@ -1002,15 +1039,9 @@ static const char *cxt5045_models[CXT5045_MODELS] = {
}; };
static struct snd_pci_quirk cxt5045_cfg_tbl[] = { static struct snd_pci_quirk cxt5045_cfg_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x30a5, "HP", CXT5045_LAPTOP_HPSENSE),
SND_PCI_QUIRK(0x103c, 0x30b2, "HP DV Series", CXT5045_LAPTOP_HPSENSE),
SND_PCI_QUIRK(0x103c, 0x30b5, "HP DV2120", CXT5045_LAPTOP_HPSENSE),
SND_PCI_QUIRK(0x103c, 0x30b7, "HP DV6000Z", CXT5045_LAPTOP_HPSENSE),
SND_PCI_QUIRK(0x103c, 0x30bb, "HP DV8000", CXT5045_LAPTOP_HPSENSE),
SND_PCI_QUIRK(0x103c, 0x30cd, "HP DV Series", CXT5045_LAPTOP_HPSENSE),
SND_PCI_QUIRK(0x103c, 0x30cf, "HP DV9533EG", CXT5045_LAPTOP_HPSENSE),
SND_PCI_QUIRK(0x103c, 0x30d5, "HP 530", CXT5045_LAPTOP_HP530), SND_PCI_QUIRK(0x103c, 0x30d5, "HP 530", CXT5045_LAPTOP_HP530),
SND_PCI_QUIRK(0x103c, 0x30d9, "HP Spartan", CXT5045_LAPTOP_HPSENSE), SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x3000, "HP DV Series",
CXT5045_LAPTOP_HPSENSE),
SND_PCI_QUIRK(0x1179, 0xff31, "Toshiba P105", CXT5045_LAPTOP_MICSENSE), SND_PCI_QUIRK(0x1179, 0xff31, "Toshiba P105", CXT5045_LAPTOP_MICSENSE),
SND_PCI_QUIRK(0x152d, 0x0753, "Benq R55E", CXT5045_BENQ), SND_PCI_QUIRK(0x152d, 0x0753, "Benq R55E", CXT5045_BENQ),
SND_PCI_QUIRK(0x1734, 0x10ad, "Fujitsu Si1520", CXT5045_LAPTOP_MICSENSE), SND_PCI_QUIRK(0x1734, 0x10ad, "Fujitsu Si1520", CXT5045_LAPTOP_MICSENSE),
@ -1020,8 +1051,8 @@ static struct snd_pci_quirk cxt5045_cfg_tbl[] = {
SND_PCI_QUIRK(0x1509, 0x1e40, "FIC", CXT5045_LAPTOP_HPMICSENSE), SND_PCI_QUIRK(0x1509, 0x1e40, "FIC", CXT5045_LAPTOP_HPMICSENSE),
SND_PCI_QUIRK(0x1509, 0x2f05, "FIC", CXT5045_LAPTOP_HPMICSENSE), SND_PCI_QUIRK(0x1509, 0x2f05, "FIC", CXT5045_LAPTOP_HPMICSENSE),
SND_PCI_QUIRK(0x1509, 0x2f06, "FIC", CXT5045_LAPTOP_HPMICSENSE), SND_PCI_QUIRK(0x1509, 0x2f06, "FIC", CXT5045_LAPTOP_HPMICSENSE),
SND_PCI_QUIRK(0x1631, 0xc106, "Packard Bell", CXT5045_LAPTOP_HPMICSENSE), SND_PCI_QUIRK_MASK(0x1631, 0xff00, 0xc100, "Packard Bell",
SND_PCI_QUIRK(0x1631, 0xc107, "Packard Bell", CXT5045_LAPTOP_HPMICSENSE), CXT5045_LAPTOP_HPMICSENSE),
SND_PCI_QUIRK(0x8086, 0x2111, "Conexant Reference board", CXT5045_LAPTOP_HPSENSE), SND_PCI_QUIRK(0x8086, 0x2111, "Conexant Reference board", CXT5045_LAPTOP_HPSENSE),
{} {}
}; };
@ -1035,6 +1066,7 @@ static int patch_cxt5045(struct hda_codec *codec)
if (!spec) if (!spec)
return -ENOMEM; return -ENOMEM;
codec->spec = spec; codec->spec = spec;
codec->pin_amp_workaround = 1;
spec->multiout.max_channels = 2; spec->multiout.max_channels = 2;
spec->multiout.num_dacs = ARRAY_SIZE(cxt5045_dac_nids); spec->multiout.num_dacs = ARRAY_SIZE(cxt5045_dac_nids);
@ -1134,7 +1166,7 @@ static int patch_cxt5045(struct hda_codec *codec)
/* Conexant 5047 specific */ /* Conexant 5047 specific */
#define CXT5047_SPDIF_OUT 0x11 #define CXT5047_SPDIF_OUT 0x11
static hda_nid_t cxt5047_dac_nids[2] = { 0x10, 0x1c }; static hda_nid_t cxt5047_dac_nids[1] = { 0x10 }; /* 0x1c */
static hda_nid_t cxt5047_adc_nids[1] = { 0x12 }; static hda_nid_t cxt5047_adc_nids[1] = { 0x12 };
static hda_nid_t cxt5047_capsrc_nids[1] = { 0x1a }; static hda_nid_t cxt5047_capsrc_nids[1] = { 0x1a };
@ -1142,20 +1174,6 @@ static struct hda_channel_mode cxt5047_modes[1] = {
{ 2, NULL }, { 2, NULL },
}; };
static struct hda_input_mux cxt5047_capture_source = {
.num_items = 1,
.items = {
{ "Mic", 0x2 },
}
};
static struct hda_input_mux cxt5047_hp_capture_source = {
.num_items = 1,
.items = {
{ "ExtMic", 0x2 },
}
};
static struct hda_input_mux cxt5047_toshiba_capture_source = { static struct hda_input_mux cxt5047_toshiba_capture_source = {
.num_items = 2, .num_items = 2,
.items = { .items = {
@ -1179,7 +1197,11 @@ static int cxt5047_hp_master_sw_put(struct snd_kcontrol *kcontrol,
* the headphone jack * the headphone jack
*/ */
bits = (!spec->hp_present && spec->cur_eapd) ? 0 : HDA_AMP_MUTE; bits = (!spec->hp_present && spec->cur_eapd) ? 0 : HDA_AMP_MUTE;
snd_hda_codec_amp_stereo(codec, 0x1d, HDA_OUTPUT, 0, /* NOTE: Conexat codec needs the index for *OUTPUT* amp of
* pin widgets unlike other codecs. In this case, we need to
* set index 0x01 for the volume from the mixer amp 0x19.
*/
snd_hda_codec_amp_stereo(codec, 0x1d, HDA_OUTPUT, 0x01,
HDA_AMP_MUTE, bits); HDA_AMP_MUTE, bits);
bits = spec->cur_eapd ? 0 : HDA_AMP_MUTE; bits = spec->cur_eapd ? 0 : HDA_AMP_MUTE;
snd_hda_codec_amp_stereo(codec, 0x13, HDA_OUTPUT, 0, snd_hda_codec_amp_stereo(codec, 0x13, HDA_OUTPUT, 0,
@ -1187,16 +1209,6 @@ static int cxt5047_hp_master_sw_put(struct snd_kcontrol *kcontrol,
return 1; return 1;
} }
/* bind volumes of both NID 0x13 (Headphones) and 0x1d (Speakers) */
static struct hda_bind_ctls cxt5047_bind_master_vol = {
.ops = &snd_hda_bind_vol,
.values = {
HDA_COMPOSE_AMP_VAL(0x13, 3, 0, HDA_OUTPUT),
HDA_COMPOSE_AMP_VAL(0x1d, 3, 0, HDA_OUTPUT),
0
},
};
/* mute internal speaker if HP is plugged */ /* mute internal speaker if HP is plugged */
static void cxt5047_hp_automute(struct hda_codec *codec) static void cxt5047_hp_automute(struct hda_codec *codec)
{ {
@ -1207,27 +1219,8 @@ static void cxt5047_hp_automute(struct hda_codec *codec)
AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
bits = (spec->hp_present || !spec->cur_eapd) ? HDA_AMP_MUTE : 0; bits = (spec->hp_present || !spec->cur_eapd) ? HDA_AMP_MUTE : 0;
snd_hda_codec_amp_stereo(codec, 0x1d, HDA_OUTPUT, 0, /* See the note in cxt5047_hp_master_sw_put */
HDA_AMP_MUTE, bits); snd_hda_codec_amp_stereo(codec, 0x1d, HDA_OUTPUT, 0x01,
/* Mute/Unmute PCM 2 for good measure - some systems need this */
snd_hda_codec_amp_stereo(codec, 0x1c, HDA_OUTPUT, 0,
HDA_AMP_MUTE, bits);
}
/* mute internal speaker if HP is plugged */
static void cxt5047_hp2_automute(struct hda_codec *codec)
{
struct conexant_spec *spec = codec->spec;
unsigned int bits;
spec->hp_present = snd_hda_codec_read(codec, 0x13, 0,
AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
bits = spec->hp_present ? HDA_AMP_MUTE : 0;
snd_hda_codec_amp_stereo(codec, 0x1d, HDA_OUTPUT, 0,
HDA_AMP_MUTE, bits);
/* Mute/Unmute PCM 2 for good measure - some systems need this */
snd_hda_codec_amp_stereo(codec, 0x1c, HDA_OUTPUT, 0,
HDA_AMP_MUTE, bits); HDA_AMP_MUTE, bits);
} }
@ -1268,90 +1261,35 @@ static void cxt5047_hp_unsol_event(struct hda_codec *codec,
} }
} }
/* unsolicited event for HP jack sensing - non-EAPD systems */ static struct snd_kcontrol_new cxt5047_base_mixers[] = {
static void cxt5047_hp2_unsol_event(struct hda_codec *codec, HDA_CODEC_VOLUME("Mic Playback Volume", 0x19, 0x02, HDA_INPUT),
unsigned int res) HDA_CODEC_MUTE("Mic Playback Switch", 0x19, 0x02, HDA_INPUT),
{ HDA_CODEC_VOLUME("Mic Boost", 0x1a, 0x0, HDA_OUTPUT),
res >>= 26;
switch (res) {
case CONEXANT_HP_EVENT:
cxt5047_hp2_automute(codec);
break;
case CONEXANT_MIC_EVENT:
cxt5047_hp_automic(codec);
break;
}
}
static struct snd_kcontrol_new cxt5047_mixers[] = {
HDA_CODEC_VOLUME("Mic Bypass Capture Volume", 0x19, 0x02, HDA_INPUT),
HDA_CODEC_MUTE("Mic Bypass Capture Switch", 0x19, 0x02, HDA_INPUT),
HDA_CODEC_VOLUME("Mic Gain Volume", 0x1a, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE("Mic Gain Switch", 0x1a, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x03, HDA_INPUT), HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x03, HDA_INPUT),
HDA_CODEC_MUTE("Capture Switch", 0x12, 0x03, HDA_INPUT), HDA_CODEC_MUTE("Capture Switch", 0x12, 0x03, HDA_INPUT),
HDA_CODEC_VOLUME("PCM Volume", 0x10, 0x00, HDA_OUTPUT), HDA_CODEC_VOLUME("PCM Volume", 0x10, 0x00, HDA_OUTPUT),
HDA_CODEC_MUTE("PCM Switch", 0x10, 0x00, HDA_OUTPUT), HDA_CODEC_MUTE("PCM Switch", 0x10, 0x00, HDA_OUTPUT),
HDA_CODEC_VOLUME("PCM-2 Volume", 0x1c, 0x00, HDA_OUTPUT), {
HDA_CODEC_MUTE("PCM-2 Switch", 0x1c, 0x00, HDA_OUTPUT), .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
HDA_CODEC_VOLUME("Speaker Playback Volume", 0x1d, 0x00, HDA_OUTPUT), .name = "Master Playback Switch",
HDA_CODEC_MUTE("Speaker Playback Switch", 0x1d, 0x00, HDA_OUTPUT), .info = cxt_eapd_info,
.get = cxt_eapd_get,
.put = cxt5047_hp_master_sw_put,
.private_value = 0x13,
},
{}
};
static struct snd_kcontrol_new cxt5047_hp_spk_mixers[] = {
/* See the note in cxt5047_hp_master_sw_put */
HDA_CODEC_VOLUME("Speaker Playback Volume", 0x1d, 0x01, HDA_OUTPUT),
HDA_CODEC_VOLUME("Headphone Playback Volume", 0x13, 0x00, HDA_OUTPUT), HDA_CODEC_VOLUME("Headphone Playback Volume", 0x13, 0x00, HDA_OUTPUT),
HDA_CODEC_MUTE("Headphone Playback Switch", 0x13, 0x00, HDA_OUTPUT),
{} {}
}; };
static struct snd_kcontrol_new cxt5047_toshiba_mixers[] = { static struct snd_kcontrol_new cxt5047_hp_only_mixers[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Capture Source",
.info = conexant_mux_enum_info,
.get = conexant_mux_enum_get,
.put = conexant_mux_enum_put
},
HDA_CODEC_VOLUME("Mic Bypass Capture Volume", 0x19, 0x02, HDA_INPUT),
HDA_CODEC_MUTE("Mic Bypass Capture Switch", 0x19, 0x02, HDA_INPUT),
HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x03, HDA_INPUT),
HDA_CODEC_MUTE("Capture Switch", 0x12, 0x03, HDA_INPUT),
HDA_CODEC_VOLUME("PCM Volume", 0x10, 0x00, HDA_OUTPUT),
HDA_CODEC_MUTE("PCM Switch", 0x10, 0x00, HDA_OUTPUT),
HDA_BIND_VOL("Master Playback Volume", &cxt5047_bind_master_vol),
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Master Playback Switch",
.info = cxt_eapd_info,
.get = cxt_eapd_get,
.put = cxt5047_hp_master_sw_put,
.private_value = 0x13,
},
{}
};
static struct snd_kcontrol_new cxt5047_hp_mixers[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Capture Source",
.info = conexant_mux_enum_info,
.get = conexant_mux_enum_get,
.put = conexant_mux_enum_put
},
HDA_CODEC_VOLUME("Mic Bypass Capture Volume", 0x19, 0x02, HDA_INPUT),
HDA_CODEC_MUTE("Mic Bypass Capture Switch", 0x19,0x02,HDA_INPUT),
HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x03, HDA_INPUT),
HDA_CODEC_MUTE("Capture Switch", 0x12, 0x03, HDA_INPUT),
HDA_CODEC_VOLUME("PCM Volume", 0x10, 0x00, HDA_OUTPUT),
HDA_CODEC_MUTE("PCM Switch", 0x10, 0x00, HDA_OUTPUT),
HDA_CODEC_VOLUME("Master Playback Volume", 0x13, 0x00, HDA_OUTPUT), HDA_CODEC_VOLUME("Master Playback Volume", 0x13, 0x00, HDA_OUTPUT),
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Master Playback Switch",
.info = cxt_eapd_info,
.get = cxt_eapd_get,
.put = cxt5047_hp_master_sw_put,
.private_value = 0x13,
},
{ } /* end */ { } /* end */
}; };
@ -1362,8 +1300,8 @@ static struct hda_verb cxt5047_init_verbs[] = {
{0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN|AC_PINCTL_VREF_50 }, {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN|AC_PINCTL_VREF_50 },
/* HP, Speaker */ /* HP, Speaker */
{0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP }, {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP },
{0x13, AC_VERB_SET_CONNECT_SEL,0x1}, {0x13, AC_VERB_SET_CONNECT_SEL, 0x0}, /* mixer(0x19) */
{0x1d, AC_VERB_SET_CONNECT_SEL,0x0}, {0x1d, AC_VERB_SET_CONNECT_SEL, 0x1}, /* mixer(0x19) */
/* Record selector: Mic */ /* Record selector: Mic */
{0x12, AC_VERB_SET_CONNECT_SEL,0x03}, {0x12, AC_VERB_SET_CONNECT_SEL,0x03},
{0x19, AC_VERB_SET_AMP_GAIN_MUTE, {0x19, AC_VERB_SET_AMP_GAIN_MUTE,
@ -1383,30 +1321,7 @@ static struct hda_verb cxt5047_init_verbs[] = {
/* configuration for Toshiba Laptops */ /* configuration for Toshiba Laptops */
static struct hda_verb cxt5047_toshiba_init_verbs[] = { static struct hda_verb cxt5047_toshiba_init_verbs[] = {
{0x13, AC_VERB_SET_EAPD_BTLENABLE, 0x0 }, /* default on */ {0x13, AC_VERB_SET_EAPD_BTLENABLE, 0x0}, /* default off */
/* pin sensing on HP and Mic jacks */
{0x13, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_HP_EVENT},
{0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_MIC_EVENT},
/* Speaker routing */
{0x1d, AC_VERB_SET_CONNECT_SEL,0x1},
{}
};
/* configuration for HP Laptops */
static struct hda_verb cxt5047_hp_init_verbs[] = {
/* pin sensing on HP jack */
{0x13, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_HP_EVENT},
/* 0x13 is actually shared by both HP and speaker;
* setting the connection to 0 (=0x19) makes the master volume control
* working mysteriouslly...
*/
{0x13, AC_VERB_SET_CONNECT_SEL, 0x0},
/* Record selector: Ext Mic */
{0x12, AC_VERB_SET_CONNECT_SEL,0x03},
{0x19, AC_VERB_SET_AMP_GAIN_MUTE,
AC_AMP_SET_INPUT|AC_AMP_SET_RIGHT|AC_AMP_SET_LEFT|0x17},
/* Speaker routing */
{0x1d, AC_VERB_SET_CONNECT_SEL,0x1},
{} {}
}; };
@ -1571,11 +1486,9 @@ static const char *cxt5047_models[CXT5047_MODELS] = {
}; };
static struct snd_pci_quirk cxt5047_cfg_tbl[] = { static struct snd_pci_quirk cxt5047_cfg_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x30a0, "HP DV1000", CXT5047_LAPTOP),
SND_PCI_QUIRK(0x103c, 0x30a5, "HP DV5200T/DV8000T", CXT5047_LAPTOP_HP), SND_PCI_QUIRK(0x103c, 0x30a5, "HP DV5200T/DV8000T", CXT5047_LAPTOP_HP),
SND_PCI_QUIRK(0x103c, 0x30b2, "HP DV2000T/DV3000T", CXT5047_LAPTOP), SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x3000, "HP DV Series",
SND_PCI_QUIRK(0x103c, 0x30b5, "HP DV2000Z", CXT5047_LAPTOP), CXT5047_LAPTOP),
SND_PCI_QUIRK(0x103c, 0x30cf, "HP DV6700", CXT5047_LAPTOP),
SND_PCI_QUIRK(0x1179, 0xff31, "Toshiba P100", CXT5047_LAPTOP_EAPD), SND_PCI_QUIRK(0x1179, 0xff31, "Toshiba P100", CXT5047_LAPTOP_EAPD),
{} {}
}; };
@ -1589,6 +1502,7 @@ static int patch_cxt5047(struct hda_codec *codec)
if (!spec) if (!spec)
return -ENOMEM; return -ENOMEM;
codec->spec = spec; codec->spec = spec;
codec->pin_amp_workaround = 1;
spec->multiout.max_channels = 2; spec->multiout.max_channels = 2;
spec->multiout.num_dacs = ARRAY_SIZE(cxt5047_dac_nids); spec->multiout.num_dacs = ARRAY_SIZE(cxt5047_dac_nids);
@ -1597,9 +1511,8 @@ static int patch_cxt5047(struct hda_codec *codec)
spec->num_adc_nids = 1; spec->num_adc_nids = 1;
spec->adc_nids = cxt5047_adc_nids; spec->adc_nids = cxt5047_adc_nids;
spec->capsrc_nids = cxt5047_capsrc_nids; spec->capsrc_nids = cxt5047_capsrc_nids;
spec->input_mux = &cxt5047_capture_source;
spec->num_mixers = 1; spec->num_mixers = 1;
spec->mixers[0] = cxt5047_mixers; spec->mixers[0] = cxt5047_base_mixers;
spec->num_init_verbs = 1; spec->num_init_verbs = 1;
spec->init_verbs[0] = cxt5047_init_verbs; spec->init_verbs[0] = cxt5047_init_verbs;
spec->spdif_route = 0; spec->spdif_route = 0;
@ -1613,21 +1526,22 @@ static int patch_cxt5047(struct hda_codec *codec)
cxt5047_cfg_tbl); cxt5047_cfg_tbl);
switch (board_config) { switch (board_config) {
case CXT5047_LAPTOP: case CXT5047_LAPTOP:
codec->patch_ops.unsol_event = cxt5047_hp2_unsol_event; spec->num_mixers = 2;
spec->mixers[1] = cxt5047_hp_spk_mixers;
codec->patch_ops.unsol_event = cxt5047_hp_unsol_event;
break; break;
case CXT5047_LAPTOP_HP: case CXT5047_LAPTOP_HP:
spec->input_mux = &cxt5047_hp_capture_source; spec->num_mixers = 2;
spec->num_init_verbs = 2; spec->mixers[1] = cxt5047_hp_only_mixers;
spec->init_verbs[1] = cxt5047_hp_init_verbs;
spec->mixers[0] = cxt5047_hp_mixers;
codec->patch_ops.unsol_event = cxt5047_hp_unsol_event; codec->patch_ops.unsol_event = cxt5047_hp_unsol_event;
codec->patch_ops.init = cxt5047_hp_init; codec->patch_ops.init = cxt5047_hp_init;
break; break;
case CXT5047_LAPTOP_EAPD: case CXT5047_LAPTOP_EAPD:
spec->input_mux = &cxt5047_toshiba_capture_source; spec->input_mux = &cxt5047_toshiba_capture_source;
spec->num_mixers = 2;
spec->mixers[1] = cxt5047_hp_spk_mixers;
spec->num_init_verbs = 2; spec->num_init_verbs = 2;
spec->init_verbs[1] = cxt5047_toshiba_init_verbs; spec->init_verbs[1] = cxt5047_toshiba_init_verbs;
spec->mixers[0] = cxt5047_toshiba_mixers;
codec->patch_ops.unsol_event = cxt5047_hp_unsol_event; codec->patch_ops.unsol_event = cxt5047_hp_unsol_event;
break; break;
#ifdef CONFIG_SND_DEBUG #ifdef CONFIG_SND_DEBUG
@ -1638,6 +1552,7 @@ static int patch_cxt5047(struct hda_codec *codec)
codec->patch_ops.unsol_event = cxt5047_hp_unsol_event; codec->patch_ops.unsol_event = cxt5047_hp_unsol_event;
#endif #endif
} }
spec->vmaster_nid = 0x13;
return 0; return 0;
} }
@ -1673,8 +1588,11 @@ static int cxt5051_hp_master_sw_put(struct snd_kcontrol *kcontrol,
/* toggle input of built-in and mic jack appropriately */ /* toggle input of built-in and mic jack appropriately */
static void cxt5051_portb_automic(struct hda_codec *codec) static void cxt5051_portb_automic(struct hda_codec *codec)
{ {
struct conexant_spec *spec = codec->spec;
unsigned int present; unsigned int present;
if (spec->no_auto_mic)
return;
present = snd_hda_codec_read(codec, 0x17, 0, present = snd_hda_codec_read(codec, 0x17, 0,
AC_VERB_GET_PIN_SENSE, 0) & AC_VERB_GET_PIN_SENSE, 0) &
AC_PINSENSE_PRESENCE; AC_PINSENSE_PRESENCE;
@ -1690,6 +1608,8 @@ static void cxt5051_portc_automic(struct hda_codec *codec)
unsigned int present; unsigned int present;
hda_nid_t new_adc; hda_nid_t new_adc;
if (spec->no_auto_mic)
return;
present = snd_hda_codec_read(codec, 0x18, 0, present = snd_hda_codec_read(codec, 0x18, 0,
AC_VERB_GET_PIN_SENSE, 0) & AC_VERB_GET_PIN_SENSE, 0) &
AC_PINSENSE_PRESENCE; AC_PINSENSE_PRESENCE;
@ -1776,6 +1696,22 @@ static struct snd_kcontrol_new cxt5051_hp_mixers[] = {
{} {}
}; };
static struct snd_kcontrol_new cxt5051_hp_dv6736_mixers[] = {
HDA_CODEC_VOLUME("Mic Volume", 0x14, 0x00, HDA_INPUT),
HDA_CODEC_MUTE("Mic Switch", 0x14, 0x00, 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[] = { static struct hda_verb cxt5051_init_verbs[] = {
/* Line in, Mic */ /* Line in, Mic */
{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03}, {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03},
@ -1806,6 +1742,66 @@ static struct hda_verb cxt5051_init_verbs[] = {
{ } /* end */ { } /* end */
}; };
static struct hda_verb cxt5051_hp_dv6736_init_verbs[] = {
/* Line in, Mic */
{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 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 */
};
static struct hda_verb cxt5051_lenovo_x200_init_verbs[] = {
/* Line in, Mic */
{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03},
{0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03},
{0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
{0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
{0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03},
/* 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},
/* Docking HP */
{0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
{0x19, 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(0) | 0x44},
{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1) | 0x44},
{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x44},
/* 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},
{0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CXT5051_PORTC_EVENT},
{0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CONEXANT_HP_EVENT},
{ } /* end */
};
/* initialize jack-sensing, too */ /* initialize jack-sensing, too */
static int cxt5051_init(struct hda_codec *codec) static int cxt5051_init(struct hda_codec *codec)
{ {
@ -1823,18 +1819,24 @@ static int cxt5051_init(struct hda_codec *codec)
enum { enum {
CXT5051_LAPTOP, /* Laptops w/ EAPD support */ CXT5051_LAPTOP, /* Laptops w/ EAPD support */
CXT5051_HP, /* no docking */ CXT5051_HP, /* no docking */
CXT5051_HP_DV6736, /* HP without mic switch */
CXT5051_LENOVO_X200, /* Lenovo X200 laptop */
CXT5051_MODELS CXT5051_MODELS
}; };
static const char *cxt5051_models[CXT5051_MODELS] = { static const char *cxt5051_models[CXT5051_MODELS] = {
[CXT5051_LAPTOP] = "laptop", [CXT5051_LAPTOP] = "laptop",
[CXT5051_HP] = "hp", [CXT5051_HP] = "hp",
[CXT5051_HP_DV6736] = "hp-dv6736",
[CXT5051_LENOVO_X200] = "lenovo-x200",
}; };
static struct snd_pci_quirk cxt5051_cfg_tbl[] = { static struct snd_pci_quirk cxt5051_cfg_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x30cf, "HP DV6736", CXT5051_HP_DV6736),
SND_PCI_QUIRK(0x14f1, 0x0101, "Conexant Reference board", SND_PCI_QUIRK(0x14f1, 0x0101, "Conexant Reference board",
CXT5051_LAPTOP), CXT5051_LAPTOP),
SND_PCI_QUIRK(0x14f1, 0x5051, "HP Spartan 1.1", CXT5051_HP), SND_PCI_QUIRK(0x14f1, 0x5051, "HP Spartan 1.1", CXT5051_HP),
SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo X200", CXT5051_LENOVO_X200),
{} {}
}; };
@ -1847,6 +1849,7 @@ static int patch_cxt5051(struct hda_codec *codec)
if (!spec) if (!spec)
return -ENOMEM; return -ENOMEM;
codec->spec = spec; codec->spec = spec;
codec->pin_amp_workaround = 1;
codec->patch_ops = conexant_patch_ops; codec->patch_ops = conexant_patch_ops;
codec->patch_ops.init = cxt5051_init; codec->patch_ops.init = cxt5051_init;
@ -1867,17 +1870,22 @@ static int patch_cxt5051(struct hda_codec *codec)
spec->cur_adc = 0; spec->cur_adc = 0;
spec->cur_adc_idx = 0; spec->cur_adc_idx = 0;
codec->patch_ops.unsol_event = cxt5051_hp_unsol_event;
board_config = snd_hda_check_board_config(codec, CXT5051_MODELS, board_config = snd_hda_check_board_config(codec, CXT5051_MODELS,
cxt5051_models, cxt5051_models,
cxt5051_cfg_tbl); cxt5051_cfg_tbl);
switch (board_config) { switch (board_config) {
case CXT5051_HP: case CXT5051_HP:
codec->patch_ops.unsol_event = cxt5051_hp_unsol_event;
spec->mixers[0] = cxt5051_hp_mixers; spec->mixers[0] = cxt5051_hp_mixers;
break; break;
default: case CXT5051_HP_DV6736:
case CXT5051_LAPTOP: spec->init_verbs[0] = cxt5051_hp_dv6736_init_verbs;
codec->patch_ops.unsol_event = cxt5051_hp_unsol_event; spec->mixers[0] = cxt5051_hp_dv6736_mixers;
spec->no_auto_mic = 1;
break;
case CXT5051_LENOVO_X200:
spec->init_verbs[0] = cxt5051_lenovo_x200_init_verbs;
break; break;
} }

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1308,16 +1308,13 @@ static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
unsigned int def_conf; unsigned int def_conf;
unsigned char seqassoc; unsigned char seqassoc;
def_conf = snd_hda_codec_read(codec, nid, 0, def_conf = snd_hda_codec_get_pincfg(codec, nid);
AC_VERB_GET_CONFIG_DEFAULT, 0);
seqassoc = (unsigned char) get_defcfg_association(def_conf); seqassoc = (unsigned char) get_defcfg_association(def_conf);
seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf); seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf);
if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE) { if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE) {
if (seqassoc == 0xff) { if (seqassoc == 0xff) {
def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30)); def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
snd_hda_codec_write(codec, nid, 0, snd_hda_codec_set_pincfg(codec, nid, def_conf);
AC_VERB_SET_CONFIG_DEFAULT_BYTES_3,
def_conf >> 24);
} }
} }
@ -1354,7 +1351,7 @@ static int vt1708_parse_auto_config(struct hda_codec *codec)
spec->multiout.max_channels = spec->multiout.num_dacs * 2; spec->multiout.max_channels = spec->multiout.num_dacs * 2;
if (spec->autocfg.dig_out_pin) if (spec->autocfg.dig_outs)
spec->multiout.dig_out_nid = VT1708_DIGOUT_NID; spec->multiout.dig_out_nid = VT1708_DIGOUT_NID;
if (spec->autocfg.dig_in_pin) if (spec->autocfg.dig_in_pin)
spec->dig_in_nid = VT1708_DIGIN_NID; spec->dig_in_nid = VT1708_DIGIN_NID;
@ -1827,7 +1824,7 @@ static int vt1709_parse_auto_config(struct hda_codec *codec)
spec->multiout.max_channels = spec->multiout.num_dacs * 2; spec->multiout.max_channels = spec->multiout.num_dacs * 2;
if (spec->autocfg.dig_out_pin) if (spec->autocfg.dig_outs)
spec->multiout.dig_out_nid = VT1709_DIGOUT_NID; spec->multiout.dig_out_nid = VT1709_DIGOUT_NID;
if (spec->autocfg.dig_in_pin) if (spec->autocfg.dig_in_pin)
spec->dig_in_nid = VT1709_DIGIN_NID; spec->dig_in_nid = VT1709_DIGIN_NID;
@ -2371,7 +2368,7 @@ static int vt1708B_parse_auto_config(struct hda_codec *codec)
spec->multiout.max_channels = spec->multiout.num_dacs * 2; spec->multiout.max_channels = spec->multiout.num_dacs * 2;
if (spec->autocfg.dig_out_pin) if (spec->autocfg.dig_outs)
spec->multiout.dig_out_nid = VT1708B_DIGOUT_NID; spec->multiout.dig_out_nid = VT1708B_DIGOUT_NID;
if (spec->autocfg.dig_in_pin) if (spec->autocfg.dig_in_pin)
spec->dig_in_nid = VT1708B_DIGIN_NID; spec->dig_in_nid = VT1708B_DIGIN_NID;
@ -2836,7 +2833,7 @@ static int vt1708S_parse_auto_config(struct hda_codec *codec)
spec->multiout.max_channels = spec->multiout.num_dacs * 2; spec->multiout.max_channels = spec->multiout.num_dacs * 2;
if (spec->autocfg.dig_out_pin) if (spec->autocfg.dig_outs)
spec->multiout.dig_out_nid = VT1708S_DIGOUT_NID; spec->multiout.dig_out_nid = VT1708S_DIGOUT_NID;
spec->extra_dig_out_nid = 0x15; spec->extra_dig_out_nid = 0x15;
@ -3155,7 +3152,7 @@ static int vt1702_parse_auto_config(struct hda_codec *codec)
spec->multiout.max_channels = spec->multiout.num_dacs * 2; spec->multiout.max_channels = spec->multiout.num_dacs * 2;
if (spec->autocfg.dig_out_pin) if (spec->autocfg.dig_outs)
spec->multiout.dig_out_nid = VT1702_DIGOUT_NID; spec->multiout.dig_out_nid = VT1702_DIGOUT_NID;
spec->extra_dig_out_nid = 0x1B; spec->extra_dig_out_nid = 0x1B;