Merge branch 'topic/hda' into for-linus

This commit is contained in:
Takashi Iwai 2010-05-20 11:59:52 +02:00
commit 19008bdacb
10 changed files with 504 additions and 140 deletions

View File

@ -204,7 +204,6 @@ generic parser regardless of the codec. Usually the codec-specific
parser is much better than the generic parser (as now). Thus this parser is much better than the generic parser (as now). Thus this
option is more about the debugging purpose. option is more about the debugging purpose.
Speaker and Headphone Output Speaker and Headphone Output
~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
One of the most frequent (and obvious) bugs with HD-audio is the One of the most frequent (and obvious) bugs with HD-audio is the
@ -600,6 +599,9 @@ probing, the proc file is available, so you can get the raw codec
information before modified by the driver. Of course, the driver information before modified by the driver. Of course, the driver
isn't usable with `probe_only=1`. But you can continue the isn't usable with `probe_only=1`. But you can continue the
configuration via hwdep sysfs file if hda-reconfig option is enabled. configuration via hwdep sysfs file if hda-reconfig option is enabled.
Using `probe_only` mask 2 skips the reset of HDA codecs (use
`probe_only=3` as module option). The hwdep interface can be used
to determine the BIOS codec initialization.
hda-verb hda-verb

View File

@ -145,6 +145,7 @@ config SND_HDA_CODEC_NVHDMI
config SND_HDA_CODEC_INTELHDMI config SND_HDA_CODEC_INTELHDMI
bool "Build INTEL HDMI HD-audio codec support" bool "Build INTEL HDMI HD-audio codec support"
select SND_DYNAMIC_MINORS
default y default y
help help
Say Y here to include INTEL HDMI HD-audio codec support in Say Y here to include INTEL HDMI HD-audio codec support in

View File

@ -1209,8 +1209,7 @@ static void free_hda_cache(struct hda_cache_rec *cache)
} }
/* query the hash. allocate an entry if not found. */ /* query the hash. allocate an entry if not found. */
static struct hda_cache_head *get_alloc_hash(struct hda_cache_rec *cache, static struct hda_cache_head *get_hash(struct hda_cache_rec *cache, u32 key)
u32 key)
{ {
u16 idx = key % (u16)ARRAY_SIZE(cache->hash); u16 idx = key % (u16)ARRAY_SIZE(cache->hash);
u16 cur = cache->hash[idx]; u16 cur = cache->hash[idx];
@ -1222,17 +1221,27 @@ static struct hda_cache_head *get_alloc_hash(struct hda_cache_rec *cache,
return info; return info;
cur = info->next; cur = info->next;
} }
return NULL;
}
/* add a new hash entry */ /* query the hash. allocate an entry if not found. */
info = snd_array_new(&cache->buf); static struct hda_cache_head *get_alloc_hash(struct hda_cache_rec *cache,
if (!info) u32 key)
return NULL; {
cur = snd_array_index(&cache->buf, info); struct hda_cache_head *info = get_hash(cache, key);
info->key = key; if (!info) {
info->val = 0; u16 idx, cur;
info->next = cache->hash[idx]; /* add a new hash entry */
cache->hash[idx] = cur; info = snd_array_new(&cache->buf);
if (!info)
return NULL;
cur = snd_array_index(&cache->buf, info);
info->key = key;
info->val = 0;
idx = key % (u16)ARRAY_SIZE(cache->hash);
info->next = cache->hash[idx];
cache->hash[idx] = cur;
}
return info; return info;
} }
@ -1461,6 +1470,8 @@ int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch,
info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, direction, idx)); info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, direction, idx));
if (!info) if (!info)
return 0; return 0;
if (snd_BUG_ON(mask & ~0xff))
mask &= 0xff;
val &= mask; val &= mask;
val |= get_vol_mute(codec, info, nid, ch, direction, idx) & ~mask; val |= get_vol_mute(codec, info, nid, ch, direction, idx) & ~mask;
if (info->vol[ch] == val) if (info->vol[ch] == val)
@ -1486,6 +1497,9 @@ int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid,
int direction, int idx, int mask, int val) int direction, int idx, int mask, int val)
{ {
int ch, ret = 0; int ch, ret = 0;
if (snd_BUG_ON(mask & ~0xff))
mask &= 0xff;
for (ch = 0; ch < 2; ch++) for (ch = 0; ch < 2; ch++)
ret |= snd_hda_codec_amp_update(codec, nid, ch, direction, ret |= snd_hda_codec_amp_update(codec, nid, ch, direction,
idx, mask, val); idx, mask, val);
@ -2716,6 +2730,41 @@ int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid,
} }
EXPORT_SYMBOL_HDA(snd_hda_codec_write_cache); EXPORT_SYMBOL_HDA(snd_hda_codec_write_cache);
/**
* snd_hda_codec_update_cache - check cache and write the cmd only when needed
* @codec: the HDA codec
* @nid: NID to send the command
* @direct: direct flag
* @verb: the verb to send
* @parm: the parameter for the verb
*
* This function works like snd_hda_codec_write_cache(), but it doesn't send
* command if the parameter is already identical with the cached value.
* If not, it sends the command and refreshes the cache.
*
* Returns 0 if successful, or a negative error code.
*/
int snd_hda_codec_update_cache(struct hda_codec *codec, hda_nid_t nid,
int direct, unsigned int verb, unsigned int parm)
{
struct hda_cache_head *c;
u32 key;
/* parm may contain the verb stuff for get/set amp */
verb = verb | (parm >> 8);
parm &= 0xff;
key = build_cmd_cache_key(nid, verb);
mutex_lock(&codec->bus->cmd_mutex);
c = get_hash(&codec->cmd_cache, key);
if (c && c->val == parm) {
mutex_unlock(&codec->bus->cmd_mutex);
return 0;
}
mutex_unlock(&codec->bus->cmd_mutex);
return snd_hda_codec_write_cache(codec, nid, direct, verb, parm);
}
EXPORT_SYMBOL_HDA(snd_hda_codec_update_cache);
/** /**
* snd_hda_codec_resume_cache - Resume the all commands from the cache * snd_hda_codec_resume_cache - Resume the all commands from the cache
* @codec: HD-audio codec * @codec: HD-audio codec
@ -4218,7 +4267,8 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
break; break;
case AC_JACK_MIC_IN: { case AC_JACK_MIC_IN: {
int preferred, alt; int preferred, alt;
if (loc == AC_JACK_LOC_FRONT) { if (loc == AC_JACK_LOC_FRONT ||
(loc & 0x30) == AC_JACK_LOC_INTERNAL) {
preferred = AUTO_PIN_FRONT_MIC; preferred = AUTO_PIN_FRONT_MIC;
alt = AUTO_PIN_MIC; alt = AUTO_PIN_MIC;
} else { } else {

View File

@ -885,9 +885,12 @@ int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid,
int direct, unsigned int verb, unsigned int parm); int direct, unsigned int verb, unsigned int parm);
void snd_hda_sequence_write_cache(struct hda_codec *codec, void snd_hda_sequence_write_cache(struct hda_codec *codec,
const struct hda_verb *seq); const struct hda_verb *seq);
int snd_hda_codec_update_cache(struct hda_codec *codec, hda_nid_t nid,
int direct, unsigned int verb, unsigned int parm);
void snd_hda_codec_resume_cache(struct hda_codec *codec); void snd_hda_codec_resume_cache(struct hda_codec *codec);
#else #else
#define snd_hda_codec_write_cache snd_hda_codec_write #define snd_hda_codec_write_cache snd_hda_codec_write
#define snd_hda_codec_update_cache snd_hda_codec_write
#define snd_hda_sequence_write_cache snd_hda_sequence_write #define snd_hda_sequence_write_cache snd_hda_sequence_write
#endif #endif

View File

@ -84,7 +84,7 @@ module_param_array(bdl_pos_adj, int, NULL, 0644);
MODULE_PARM_DESC(bdl_pos_adj, "BDL position adjustment offset."); MODULE_PARM_DESC(bdl_pos_adj, "BDL position adjustment offset.");
module_param_array(probe_mask, int, NULL, 0444); module_param_array(probe_mask, int, NULL, 0444);
MODULE_PARM_DESC(probe_mask, "Bitmask to probe codecs (default = -1)."); MODULE_PARM_DESC(probe_mask, "Bitmask to probe codecs (default = -1).");
module_param_array(probe_only, bool, NULL, 0444); module_param_array(probe_only, int, NULL, 0444);
MODULE_PARM_DESC(probe_only, "Only probing and no codec initialization."); MODULE_PARM_DESC(probe_only, "Only probing and no codec initialization.");
module_param(single_cmd, bool, 0444); module_param(single_cmd, bool, 0444);
MODULE_PARM_DESC(single_cmd, "Use single command to communicate with codecs " MODULE_PARM_DESC(single_cmd, "Use single command to communicate with codecs "
@ -174,7 +174,7 @@ MODULE_DESCRIPTION("Intel HDA driver");
#define ICH6_GSTS_FSTS (1 << 1) /* flush status */ #define ICH6_GSTS_FSTS (1 << 1) /* flush status */
#define ICH6_REG_INTCTL 0x20 #define ICH6_REG_INTCTL 0x20
#define ICH6_REG_INTSTS 0x24 #define ICH6_REG_INTSTS 0x24
#define ICH6_REG_WALCLK 0x30 #define ICH6_REG_WALLCLK 0x30 /* 24Mhz source */
#define ICH6_REG_SYNC 0x34 #define ICH6_REG_SYNC 0x34
#define ICH6_REG_CORBLBASE 0x40 #define ICH6_REG_CORBLBASE 0x40
#define ICH6_REG_CORBUBASE 0x44 #define ICH6_REG_CORBUBASE 0x44
@ -340,8 +340,8 @@ struct azx_dev {
unsigned int period_bytes; /* size of the period in bytes */ unsigned int period_bytes; /* size of the period in bytes */
unsigned int frags; /* number for period in the play buffer */ unsigned int frags; /* number for period in the play buffer */
unsigned int fifo_size; /* FIFO size */ unsigned int fifo_size; /* FIFO size */
unsigned long start_jiffies; /* start + minimum jiffies */ unsigned long start_wallclk; /* start + minimum wallclk */
unsigned long min_jiffies; /* minimum jiffies before position is valid */ unsigned long period_wallclk; /* wallclk for period */
void __iomem *sd_addr; /* stream descriptor pointer */ void __iomem *sd_addr; /* stream descriptor pointer */
@ -361,7 +361,6 @@ struct azx_dev {
unsigned int opened :1; unsigned int opened :1;
unsigned int running :1; unsigned int running :1;
unsigned int irq_pending :1; unsigned int irq_pending :1;
unsigned int start_flag: 1; /* stream full start flag */
/* /*
* For VIA: * For VIA:
* A flag to ensure DMA position is 0 * A flag to ensure DMA position is 0
@ -425,7 +424,7 @@ struct azx {
struct snd_dma_buffer posbuf; struct snd_dma_buffer posbuf;
/* flags */ /* flags */
int position_fix; int position_fix[2]; /* for both playback/capture streams */
int poll_count; int poll_count;
unsigned int running :1; unsigned int running :1;
unsigned int initialized :1; unsigned int initialized :1;
@ -858,10 +857,13 @@ static void azx_power_notify(struct hda_bus *bus);
#endif #endif
/* reset codec link */ /* reset codec link */
static int azx_reset(struct azx *chip) static int azx_reset(struct azx *chip, int full_reset)
{ {
int count; int count;
if (!full_reset)
goto __skip;
/* clear STATESTS */ /* clear STATESTS */
azx_writeb(chip, STATESTS, STATESTS_INT_MASK); azx_writeb(chip, STATESTS, STATESTS_INT_MASK);
@ -887,6 +889,7 @@ static int azx_reset(struct azx *chip)
/* Brent Chartrand said to wait >= 540us for codecs to initialize */ /* Brent Chartrand said to wait >= 540us for codecs to initialize */
msleep(1); msleep(1);
__skip:
/* check to see if controller is ready */ /* check to see if controller is ready */
if (!azx_readb(chip, GCTL)) { if (!azx_readb(chip, GCTL)) {
snd_printd(SFX "azx_reset: controller not ready!\n"); snd_printd(SFX "azx_reset: controller not ready!\n");
@ -998,13 +1001,13 @@ static void azx_stream_stop(struct azx *chip, struct azx_dev *azx_dev)
/* /*
* reset and start the controller registers * reset and start the controller registers
*/ */
static void azx_init_chip(struct azx *chip) static void azx_init_chip(struct azx *chip, int full_reset)
{ {
if (chip->initialized) if (chip->initialized)
return; return;
/* reset controller */ /* reset controller */
azx_reset(chip); azx_reset(chip, full_reset);
/* initialize interrupts */ /* initialize interrupts */
azx_int_clear(chip); azx_int_clear(chip);
@ -1302,8 +1305,10 @@ static int azx_setup_controller(struct azx *chip, struct azx_dev *azx_dev)
azx_sd_writel(azx_dev, SD_BDLPU, upper_32_bits(azx_dev->bdl.addr)); azx_sd_writel(azx_dev, SD_BDLPU, upper_32_bits(azx_dev->bdl.addr));
/* enable the position buffer */ /* enable the position buffer */
if (chip->position_fix == POS_FIX_POSBUF || if (chip->position_fix[0] == POS_FIX_POSBUF ||
chip->position_fix == POS_FIX_AUTO || chip->position_fix[0] == POS_FIX_AUTO ||
chip->position_fix[1] == POS_FIX_POSBUF ||
chip->position_fix[1] == POS_FIX_AUTO ||
chip->via_dmapos_patch) { chip->via_dmapos_patch) {
if (!(azx_readl(chip, DPLBASE) & ICH6_DPLBASE_ENABLE)) if (!(azx_readl(chip, DPLBASE) & ICH6_DPLBASE_ENABLE))
azx_writel(chip, DPLBASE, azx_writel(chip, DPLBASE,
@ -1348,7 +1353,7 @@ static void azx_bus_reset(struct hda_bus *bus)
bus->in_reset = 1; bus->in_reset = 1;
azx_stop_chip(chip); azx_stop_chip(chip);
azx_init_chip(chip); azx_init_chip(chip, 1);
#ifdef CONFIG_PM #ifdef CONFIG_PM
if (chip->initialized) { if (chip->initialized) {
int i; int i;
@ -1422,7 +1427,7 @@ static int __devinit azx_codec_create(struct azx *chip, const char *model)
* get back to the sanity state. * get back to the sanity state.
*/ */
azx_stop_chip(chip); azx_stop_chip(chip);
azx_init_chip(chip); azx_init_chip(chip, 1);
} }
} }
} }
@ -1670,8 +1675,9 @@ static int azx_pcm_prepare(struct snd_pcm_substream *substream)
return err; return err;
} }
azx_dev->min_jiffies = (runtime->period_size * HZ) / /* wallclk has 24Mhz clock source */
(runtime->rate * 2); azx_dev->period_wallclk = (((runtime->period_size * 24000) /
runtime->rate) * 1000);
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;
@ -1725,14 +1731,15 @@ static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
if (s->pcm->card != substream->pcm->card) if (s->pcm->card != substream->pcm->card)
continue; continue;
azx_dev = get_azx_dev(s); azx_dev = get_azx_dev(s);
if (rstart) { if (start) {
azx_dev->start_flag = 1; azx_dev->start_wallclk = azx_readl(chip, WALLCLK);
azx_dev->start_jiffies = jiffies + azx_dev->min_jiffies; if (!rstart)
} azx_dev->start_wallclk -=
if (start) azx_dev->period_wallclk;
azx_stream_start(chip, azx_dev); azx_stream_start(chip, azx_dev);
else } else {
azx_stream_stop(chip, azx_dev); azx_stream_stop(chip, azx_dev);
}
azx_dev->running = start; azx_dev->running = start;
} }
spin_unlock(&chip->reg_lock); spin_unlock(&chip->reg_lock);
@ -1843,13 +1850,16 @@ static unsigned int azx_get_position(struct azx *chip,
if (chip->via_dmapos_patch) if (chip->via_dmapos_patch)
pos = azx_via_get_position(chip, azx_dev); pos = azx_via_get_position(chip, azx_dev);
else if (chip->position_fix == POS_FIX_POSBUF || else {
chip->position_fix == POS_FIX_AUTO) { int stream = azx_dev->substream->stream;
/* use the position buffer */ if (chip->position_fix[stream] == POS_FIX_POSBUF ||
pos = le32_to_cpu(*azx_dev->posbuf); chip->position_fix[stream] == POS_FIX_AUTO) {
} else { /* use the position buffer */
/* read LPIB */ pos = le32_to_cpu(*azx_dev->posbuf);
pos = azx_sd_readl(azx_dev, SD_LPIB); } else {
/* read LPIB */
pos = azx_sd_readl(azx_dev, SD_LPIB);
}
} }
if (pos >= azx_dev->bufsize) if (pos >= azx_dev->bufsize)
pos = 0; pos = 0;
@ -1876,32 +1886,35 @@ static snd_pcm_uframes_t azx_pcm_pointer(struct snd_pcm_substream *substream)
*/ */
static int azx_position_ok(struct azx *chip, struct azx_dev *azx_dev) static int azx_position_ok(struct azx *chip, struct azx_dev *azx_dev)
{ {
u32 wallclk;
unsigned int pos; unsigned int pos;
int stream;
if (azx_dev->start_flag && wallclk = azx_readl(chip, WALLCLK) - azx_dev->start_wallclk;
time_before_eq(jiffies, azx_dev->start_jiffies)) if (wallclk < (azx_dev->period_wallclk * 2) / 3)
return -1; /* bogus (too early) interrupt */ return -1; /* bogus (too early) interrupt */
azx_dev->start_flag = 0;
stream = azx_dev->substream->stream;
pos = azx_get_position(chip, azx_dev); pos = azx_get_position(chip, azx_dev);
if (chip->position_fix == POS_FIX_AUTO) { if (chip->position_fix[stream] == POS_FIX_AUTO) {
if (!pos) { if (!pos) {
printk(KERN_WARNING printk(KERN_WARNING
"hda-intel: Invalid position buffer, " "hda-intel: Invalid position buffer, "
"using LPIB read method instead.\n"); "using LPIB read method instead.\n");
chip->position_fix = POS_FIX_LPIB; chip->position_fix[stream] = POS_FIX_LPIB;
pos = azx_get_position(chip, azx_dev); pos = azx_get_position(chip, azx_dev);
} else } else
chip->position_fix = POS_FIX_POSBUF; chip->position_fix[stream] = POS_FIX_POSBUF;
} }
if (!bdl_pos_adj[chip->dev_index])
return 1; /* no delayed ack */
if (WARN_ONCE(!azx_dev->period_bytes, if (WARN_ONCE(!azx_dev->period_bytes,
"hda-intel: zero azx_dev->period_bytes")) "hda-intel: zero azx_dev->period_bytes"))
return 0; /* this shouldn't happen! */ return -1; /* this shouldn't happen! */
if (pos % azx_dev->period_bytes > azx_dev->period_bytes / 2) if (wallclk <= azx_dev->period_wallclk &&
return 0; /* NG - it's below the period boundary */ pos % azx_dev->period_bytes > azx_dev->period_bytes / 2)
/* NG - it's below the first next period boundary */
return bdl_pos_adj[chip->dev_index] ? 0 : -1;
azx_dev->start_wallclk = wallclk;
return 1; /* OK, it's fine */ return 1; /* OK, it's fine */
} }
@ -1911,7 +1924,7 @@ static int azx_position_ok(struct azx *chip, struct azx_dev *azx_dev)
static void azx_irq_pending_work(struct work_struct *work) static void azx_irq_pending_work(struct work_struct *work)
{ {
struct azx *chip = container_of(work, struct azx, irq_pending_work); struct azx *chip = container_of(work, struct azx, irq_pending_work);
int i, pending; int i, pending, ok;
if (!chip->irq_pending_warned) { if (!chip->irq_pending_warned) {
printk(KERN_WARNING printk(KERN_WARNING
@ -1930,11 +1943,14 @@ static void azx_irq_pending_work(struct work_struct *work)
!azx_dev->substream || !azx_dev->substream ||
!azx_dev->running) !azx_dev->running)
continue; continue;
if (azx_position_ok(chip, azx_dev)) { ok = azx_position_ok(chip, azx_dev);
if (ok > 0) {
azx_dev->irq_pending = 0; azx_dev->irq_pending = 0;
spin_unlock(&chip->reg_lock); spin_unlock(&chip->reg_lock);
snd_pcm_period_elapsed(azx_dev->substream); snd_pcm_period_elapsed(azx_dev->substream);
spin_lock(&chip->reg_lock); spin_lock(&chip->reg_lock);
} else if (ok < 0) {
pending = 0; /* too early */
} else } else
pending++; pending++;
} }
@ -2112,7 +2128,7 @@ static void azx_power_notify(struct hda_bus *bus)
} }
} }
if (power_on) if (power_on)
azx_init_chip(chip); azx_init_chip(chip, 1);
else if (chip->running && power_save_controller && else if (chip->running && power_save_controller &&
!bus->power_keep_link_on) !bus->power_keep_link_on)
azx_stop_chip(chip); azx_stop_chip(chip);
@ -2182,7 +2198,7 @@ static int azx_resume(struct pci_dev *pci)
azx_init_pci(chip); azx_init_pci(chip);
if (snd_hda_codecs_inuse(chip->bus)) if (snd_hda_codecs_inuse(chip->bus))
azx_init_chip(chip); azx_init_chip(chip, 1);
snd_hda_resume(chip->bus); snd_hda_resume(chip->bus);
snd_power_change_state(card, SNDRV_CTL_POWER_D0); snd_power_change_state(card, SNDRV_CTL_POWER_D0);
@ -2431,7 +2447,8 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
chip->dev_index = dev; chip->dev_index = dev;
INIT_WORK(&chip->irq_pending_work, azx_irq_pending_work); INIT_WORK(&chip->irq_pending_work, azx_irq_pending_work);
chip->position_fix = check_position_fix(chip, position_fix[dev]); chip->position_fix[0] = chip->position_fix[1] =
check_position_fix(chip, position_fix[dev]);
check_probe_mask(chip, dev); check_probe_mask(chip, dev);
chip->single_cmd = single_cmd; chip->single_cmd = single_cmd;
@ -2577,7 +2594,7 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
/* initialize chip */ /* initialize chip */
azx_init_pci(chip); azx_init_pci(chip);
azx_init_chip(chip); azx_init_chip(chip, (probe_only[dev] & 2) == 0);
/* codec detection */ /* codec detection */
if (!chip->codec_mask) { if (!chip->codec_mask) {
@ -2666,7 +2683,7 @@ static int __devinit azx_probe(struct pci_dev *pci,
goto out_free; goto out_free;
} }
#endif #endif
if (!probe_only[dev]) { if ((probe_only[dev] & 1) == 0) {
err = azx_codec_configure(chip); err = azx_codec_configure(chip);
if (err < 0) if (err < 0)
goto out_free; goto out_free;

View File

@ -71,9 +71,10 @@ struct ad198x_spec {
struct hda_input_mux private_imux; struct hda_input_mux private_imux;
hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
unsigned int jack_present :1; unsigned int jack_present: 1;
unsigned int inv_jack_detect:1; /* inverted jack-detection */ unsigned int inv_jack_detect: 1;/* inverted jack-detection */
unsigned int inv_eapd:1; /* inverted EAPD implementation */ unsigned int inv_eapd: 1; /* inverted EAPD implementation */
unsigned int analog_beep: 1; /* analog beep input present */
#ifdef CONFIG_SND_HDA_POWER_SAVE #ifdef CONFIG_SND_HDA_POWER_SAVE
struct hda_loopback_check loopback; struct hda_loopback_check loopback;
@ -165,6 +166,12 @@ static struct snd_kcontrol_new ad_beep_mixer[] = {
{ } /* end */ { } /* end */
}; };
static struct snd_kcontrol_new ad_beep2_mixer[] = {
HDA_CODEC_VOLUME("Digital Beep Playback Volume", 0, 0, HDA_OUTPUT),
HDA_CODEC_MUTE_BEEP("Digital Beep Playback Switch", 0, 0, HDA_OUTPUT),
{ } /* end */
};
#define set_beep_amp(spec, nid, idx, dir) \ #define set_beep_amp(spec, nid, idx, dir) \
((spec)->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir)) /* mono */ ((spec)->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir)) /* mono */
#else #else
@ -203,7 +210,8 @@ static int ad198x_build_controls(struct hda_codec *codec)
#ifdef CONFIG_SND_HDA_INPUT_BEEP #ifdef CONFIG_SND_HDA_INPUT_BEEP
if (spec->beep_amp) { if (spec->beep_amp) {
struct snd_kcontrol_new *knew; struct snd_kcontrol_new *knew;
for (knew = ad_beep_mixer; knew->name; knew++) { knew = spec->analog_beep ? ad_beep2_mixer : ad_beep_mixer;
for ( ; knew->name; knew++) {
struct snd_kcontrol *kctl; struct snd_kcontrol *kctl;
kctl = snd_ctl_new1(knew, codec); kctl = snd_ctl_new1(knew, codec);
if (!kctl) if (!kctl)
@ -3481,6 +3489,8 @@ static struct snd_kcontrol_new ad1984_thinkpad_mixers[] = {
HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT), HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x20, 0x01, HDA_INPUT), HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
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("Beep Playback Volume", 0x20, 0x03, HDA_INPUT),
HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x03, HDA_INPUT),
HDA_CODEC_VOLUME("Docking Mic Playback Volume", 0x20, 0x04, HDA_INPUT), HDA_CODEC_VOLUME("Docking Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
HDA_CODEC_MUTE("Docking Mic Playback Switch", 0x20, 0x04, HDA_INPUT), HDA_CODEC_MUTE("Docking Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT), HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT),
@ -3522,6 +3532,8 @@ static struct hda_verb ad1984_thinkpad_init_verbs[] = {
{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_ZERO}, {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
/* Analog PC Beeper - allow firmware/ACPI beeps */
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3) | 0x1a},
/* 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 */
@ -3654,6 +3666,7 @@ static int patch_ad1984(struct hda_codec *codec)
spec->input_mux = &ad1984_thinkpad_capture_source; spec->input_mux = &ad1984_thinkpad_capture_source;
spec->mixers[0] = ad1984_thinkpad_mixers; spec->mixers[0] = ad1984_thinkpad_mixers;
spec->init_verbs[spec->num_init_verbs++] = ad1984_thinkpad_init_verbs; spec->init_verbs[spec->num_init_verbs++] = ad1984_thinkpad_init_verbs;
spec->analog_beep = 1;
break; break;
case AD1984_DELL_DESKTOP: case AD1984_DELL_DESKTOP:
spec->multiout.dig_out_nid = 0; spec->multiout.dig_out_nid = 0;

View File

@ -115,6 +115,7 @@ struct conexant_spec {
unsigned int port_d_mode; unsigned int port_d_mode;
unsigned int dell_vostro:1; unsigned int dell_vostro:1;
unsigned int ideapad:1; unsigned int ideapad:1;
unsigned int thinkpad:1;
unsigned int ext_mic_present; unsigned int ext_mic_present;
unsigned int recording; unsigned int recording;
@ -1784,6 +1785,7 @@ static struct hda_verb cxt5051_init_verbs[] = {
{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1) | 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}, {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x44},
/* SPDIF route: PCM */ /* SPDIF route: PCM */
{0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
{0x1c, AC_VERB_SET_CONNECT_SEL, 0x0}, {0x1c, AC_VERB_SET_CONNECT_SEL, 0x0},
/* EAPD */ /* EAPD */
{0x1a, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */ {0x1a, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */
@ -1840,6 +1842,7 @@ static struct hda_verb cxt5051_lenovo_x200_init_verbs[] = {
{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1) | 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}, {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x44},
/* SPDIF route: PCM */ /* SPDIF route: PCM */
{0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, /* needed for W500 Advanced Mini Dock 250410 */
{0x1c, AC_VERB_SET_CONNECT_SEL, 0x0}, {0x1c, AC_VERB_SET_CONNECT_SEL, 0x0},
/* EAPD */ /* EAPD */
{0x1a, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */ {0x1a, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */
@ -1911,7 +1914,7 @@ 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_HP_DV6736, /* HP without mic switch */
CXT5051_LENOVO_X200, /* Lenovo X200 laptop */ CXT5051_LENOVO_X200, /* Lenovo X200 laptop, also used for Advanced Mini Dock 250410 */
CXT5051_F700, /* HP Compaq Presario F700 */ CXT5051_F700, /* HP Compaq Presario F700 */
CXT5051_TOSHIBA, /* Toshiba M300 & co */ CXT5051_TOSHIBA, /* Toshiba M300 & co */
CXT5051_MODELS CXT5051_MODELS
@ -2033,6 +2036,9 @@ static void cxt5066_update_speaker(struct hda_codec *codec)
/* Port D (HP/LO) */ /* Port D (HP/LO) */
pinctl = ((spec->hp_present & 2) && spec->cur_eapd) pinctl = ((spec->hp_present & 2) && spec->cur_eapd)
? spec->port_d_mode : 0; ? spec->port_d_mode : 0;
/* Mute if Port A is connected on Thinkpad */
if (spec->thinkpad && (spec->hp_present & 1))
pinctl = 0;
snd_hda_codec_write(codec, 0x1c, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, snd_hda_codec_write(codec, 0x1c, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
pinctl); pinctl);
@ -2213,6 +2219,50 @@ static void cxt5066_ideapad_automic(struct hda_codec *codec)
} }
} }
/* toggle input of built-in digital mic and mic jack appropriately
order is: external mic -> dock mic -> interal mic */
static void cxt5066_thinkpad_automic(struct hda_codec *codec)
{
unsigned int ext_present, dock_present;
static struct hda_verb ext_mic_present[] = {
{0x14, AC_VERB_SET_CONNECT_SEL, 0},
{0x17, AC_VERB_SET_CONNECT_SEL, 1},
{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
{0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
{}
};
static struct hda_verb dock_mic_present[] = {
{0x14, AC_VERB_SET_CONNECT_SEL, 0},
{0x17, AC_VERB_SET_CONNECT_SEL, 0},
{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
{0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
{}
};
static struct hda_verb ext_mic_absent[] = {
{0x14, AC_VERB_SET_CONNECT_SEL, 2},
{0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
{}
};
ext_present = snd_hda_jack_detect(codec, 0x1b);
dock_present = snd_hda_jack_detect(codec, 0x1a);
if (ext_present) {
snd_printdd("CXT5066: external microphone detected\n");
snd_hda_sequence_write(codec, ext_mic_present);
} else if (dock_present) {
snd_printdd("CXT5066: dock microphone detected\n");
snd_hda_sequence_write(codec, dock_mic_present);
} else {
snd_printdd("CXT5066: external microphone absent\n");
snd_hda_sequence_write(codec, ext_mic_absent);
}
}
/* mute internal speaker if HP is plugged */ /* mute internal speaker if HP is plugged */
static void cxt5066_hp_automute(struct hda_codec *codec) static void cxt5066_hp_automute(struct hda_codec *codec)
{ {
@ -2225,7 +2275,8 @@ static void cxt5066_hp_automute(struct hda_codec *codec)
/* Port D */ /* Port D */
portD = snd_hda_jack_detect(codec, 0x1c); portD = snd_hda_jack_detect(codec, 0x1c);
spec->hp_present = !!(portA | portD); spec->hp_present = !!(portA);
spec->hp_present |= portD ? 2 : 0;
snd_printdd("CXT5066: hp automute portA=%x portD=%x present=%d\n", snd_printdd("CXT5066: hp automute portA=%x portD=%x present=%d\n",
portA, portD, spec->hp_present); portA, portD, spec->hp_present);
cxt5066_update_speaker(codec); cxt5066_update_speaker(codec);
@ -2276,6 +2327,20 @@ static void cxt5066_ideapad_event(struct hda_codec *codec, unsigned int res)
} }
} }
/* unsolicited event for jack sensing */
static void cxt5066_thinkpad_event(struct hda_codec *codec, unsigned int res)
{
snd_printdd("CXT5066_thinkpad: unsol event %x (%x)\n", res, res >> 26);
switch (res >> 26) {
case CONEXANT_HP_EVENT:
cxt5066_hp_automute(codec);
break;
case CONEXANT_MIC_EVENT:
cxt5066_thinkpad_automic(codec);
break;
}
}
static const struct hda_input_mux cxt5066_analog_mic_boost = { static const struct hda_input_mux cxt5066_analog_mic_boost = {
.num_items = 5, .num_items = 5,
.items = { .items = {
@ -2294,7 +2359,7 @@ static void cxt5066_set_mic_boost(struct hda_codec *codec)
AC_VERB_SET_AMP_GAIN_MUTE, AC_VERB_SET_AMP_GAIN_MUTE,
AC_AMP_SET_RIGHT | AC_AMP_SET_LEFT | AC_AMP_SET_OUTPUT | AC_AMP_SET_RIGHT | AC_AMP_SET_LEFT | AC_AMP_SET_OUTPUT |
cxt5066_analog_mic_boost.items[spec->mic_boost].index); cxt5066_analog_mic_boost.items[spec->mic_boost].index);
if (spec->ideapad) { if (spec->ideapad || spec->thinkpad) {
/* adjust the internal mic as well...it is not through 0x17 */ /* adjust the internal mic as well...it is not through 0x17 */
snd_hda_codec_write_cache(codec, 0x23, 0, snd_hda_codec_write_cache(codec, 0x23, 0,
AC_VERB_SET_AMP_GAIN_MUTE, AC_VERB_SET_AMP_GAIN_MUTE,
@ -2782,6 +2847,64 @@ static struct hda_verb cxt5066_init_verbs_ideapad[] = {
{ } /* end */ { } /* end */
}; };
static struct hda_verb cxt5066_init_verbs_thinkpad[] = {
{0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* Port F */
{0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* Port E */
/* Port G: internal speakers */
{0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
{0x1f, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */
/* Port A: HP, Amp */
{0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
{0x19, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */
/* Port B: Mic Dock */
{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
/* Port C: Mic */
{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
/* Port D: HP Dock, Amp */
{0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
{0x1c, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */
/* DAC1 */
{0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
/* Node 14 connections: 0x17 0x18 0x23 0x24 0x27 */
{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x50},
{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2) | 0x50},
{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
{0x14, AC_VERB_SET_CONNECT_SEL, 2}, /* default to internal mic */
/* Audio input selector */
{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x2},
{0x17, AC_VERB_SET_CONNECT_SEL, 1}, /* route ext mic */
/* SPDIF route: PCM */
{0x20, AC_VERB_SET_CONNECT_SEL, 0x0},
{0x22, AC_VERB_SET_CONNECT_SEL, 0x0},
{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
{0x22, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
/* internal microphone */
{0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* enable int mic */
/* EAPD */
{0x1d, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */
/* enable unsolicited events for Port A, B, C and D */
{0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_HP_EVENT},
{0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_HP_EVENT},
{0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_MIC_EVENT},
{0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_MIC_EVENT},
{ } /* end */
};
static struct hda_verb cxt5066_init_verbs_portd_lo[] = { static struct hda_verb cxt5066_init_verbs_portd_lo[] = {
{0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
{ } /* end */ { } /* end */
@ -2800,6 +2923,8 @@ static int cxt5066_init(struct hda_codec *codec)
cxt5066_vostro_automic(codec); cxt5066_vostro_automic(codec);
else if (spec->ideapad) else if (spec->ideapad)
cxt5066_ideapad_automic(codec); cxt5066_ideapad_automic(codec);
else if (spec->thinkpad)
cxt5066_thinkpad_automic(codec);
} }
cxt5066_set_mic_boost(codec); cxt5066_set_mic_boost(codec);
return 0; return 0;
@ -2821,20 +2946,22 @@ static int cxt5066_olpc_init(struct hda_codec *codec)
} }
enum { enum {
CXT5066_LAPTOP, /* Laptops w/ EAPD support */ CXT5066_LAPTOP, /* Laptops w/ EAPD support */
CXT5066_DELL_LAPTOP, /* Dell Laptop */ CXT5066_DELL_LAPTOP, /* Dell Laptop */
CXT5066_OLPC_XO_1_5, /* OLPC XO 1.5 */ CXT5066_OLPC_XO_1_5, /* OLPC XO 1.5 */
CXT5066_DELL_VOSTO, /* Dell Vostro 1015i */ CXT5066_DELL_VOSTO, /* Dell Vostro 1015i */
CXT5066_IDEAPAD, /* Lenovo IdeaPad U150 */ CXT5066_IDEAPAD, /* Lenovo IdeaPad U150 */
CXT5066_THINKPAD, /* Lenovo ThinkPad T410s, others? */
CXT5066_MODELS CXT5066_MODELS
}; };
static const char *cxt5066_models[CXT5066_MODELS] = { static const char *cxt5066_models[CXT5066_MODELS] = {
[CXT5066_LAPTOP] = "laptop", [CXT5066_LAPTOP] = "laptop",
[CXT5066_DELL_LAPTOP] = "dell-laptop", [CXT5066_DELL_LAPTOP] = "dell-laptop",
[CXT5066_OLPC_XO_1_5] = "olpc-xo-1_5", [CXT5066_OLPC_XO_1_5] = "olpc-xo-1_5",
[CXT5066_DELL_VOSTO] = "dell-vostro", [CXT5066_DELL_VOSTO] = "dell-vostro",
[CXT5066_IDEAPAD] = "ideapad", [CXT5066_IDEAPAD] = "ideapad",
[CXT5066_THINKPAD] = "thinkpad",
}; };
static struct snd_pci_quirk cxt5066_cfg_tbl[] = { static struct snd_pci_quirk cxt5066_cfg_tbl[] = {
@ -2849,6 +2976,7 @@ static struct snd_pci_quirk cxt5066_cfg_tbl[] = {
SND_PCI_QUIRK(0x1179, 0xffe0, "Toshiba Satellite Pro T130-15F", CXT5066_OLPC_XO_1_5), SND_PCI_QUIRK(0x1179, 0xffe0, "Toshiba Satellite Pro T130-15F", CXT5066_OLPC_XO_1_5),
SND_PCI_QUIRK(0x17aa, 0x21b2, "Thinkpad X100e", CXT5066_IDEAPAD), SND_PCI_QUIRK(0x17aa, 0x21b2, "Thinkpad X100e", CXT5066_IDEAPAD),
SND_PCI_QUIRK(0x17aa, 0x3a0d, "ideapad", CXT5066_IDEAPAD), SND_PCI_QUIRK(0x17aa, 0x3a0d, "ideapad", CXT5066_IDEAPAD),
SND_PCI_QUIRK(0x17aa, 0x215e, "Lenovo Thinkpad", CXT5066_THINKPAD),
{} {}
}; };
@ -2953,6 +3081,22 @@ static int patch_cxt5066(struct hda_codec *codec)
/* no S/PDIF out */ /* no S/PDIF out */
spec->multiout.dig_out_nid = 0; spec->multiout.dig_out_nid = 0;
/* input source automatically selected */
spec->input_mux = NULL;
break;
case CXT5066_THINKPAD:
codec->patch_ops.init = cxt5066_init;
codec->patch_ops.unsol_event = cxt5066_thinkpad_event;
spec->mixers[spec->num_mixers++] = cxt5066_mixer_master;
spec->mixers[spec->num_mixers++] = cxt5066_mixers;
spec->init_verbs[0] = cxt5066_init_verbs_thinkpad;
spec->thinkpad = 1;
spec->port_d_mode = PIN_OUT;
spec->mic_boost = 2; /* default 20dB gain */
/* no S/PDIF out */
spec->multiout.dig_out_nid = 0;
/* input source automatically selected */ /* input source automatically selected */
spec->input_mux = NULL; spec->input_mux = NULL;
break; break;
@ -2975,6 +3119,8 @@ static struct hda_codec_preset snd_hda_preset_conexant[] = {
.patch = patch_cxt5066 }, .patch = patch_cxt5066 },
{ .id = 0x14f15067, .name = "CX20583 (Pebble HSF)", { .id = 0x14f15067, .name = "CX20583 (Pebble HSF)",
.patch = patch_cxt5066 }, .patch = patch_cxt5066 },
{ .id = 0x14f15069, .name = "CX20585",
.patch = patch_cxt5066 },
{} /* terminator */ {} /* terminator */
}; };
@ -2983,6 +3129,7 @@ MODULE_ALIAS("snd-hda-codec-id:14f15047");
MODULE_ALIAS("snd-hda-codec-id:14f15051"); MODULE_ALIAS("snd-hda-codec-id:14f15051");
MODULE_ALIAS("snd-hda-codec-id:14f15066"); MODULE_ALIAS("snd-hda-codec-id:14f15066");
MODULE_ALIAS("snd-hda-codec-id:14f15067"); MODULE_ALIAS("snd-hda-codec-id:14f15067");
MODULE_ALIAS("snd-hda-codec-id:14f15069");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Conexant HD-audio codec"); MODULE_DESCRIPTION("Conexant HD-audio codec");

View File

@ -766,7 +766,7 @@ static int hdmi_add_pin(struct hda_codec *codec, hda_nid_t pin_nid)
if (spec->num_pins >= MAX_HDMI_PINS) { if (spec->num_pins >= MAX_HDMI_PINS) {
snd_printk(KERN_WARNING snd_printk(KERN_WARNING
"HDMI: no space for pin %d\n", pin_nid); "HDMI: no space for pin %d\n", pin_nid);
return -EINVAL; return -E2BIG;
} }
hdmi_present_sense(codec, pin_nid, &spec->sink_eld[spec->num_pins]); hdmi_present_sense(codec, pin_nid, &spec->sink_eld[spec->num_pins]);
@ -788,7 +788,7 @@ static int hdmi_add_cvt(struct hda_codec *codec, hda_nid_t nid)
if (spec->num_cvts >= MAX_HDMI_CVTS) { if (spec->num_cvts >= MAX_HDMI_CVTS) {
snd_printk(KERN_WARNING snd_printk(KERN_WARNING
"HDMI: no space for converter %d\n", nid); "HDMI: no space for converter %d\n", nid);
return -EINVAL; return -E2BIG;
} }
spec->cvt[spec->num_cvts] = nid; spec->cvt[spec->num_cvts] = nid;
@ -820,15 +820,13 @@ static int hdmi_parse_codec(struct hda_codec *codec)
switch (type) { switch (type) {
case AC_WID_AUD_OUT: case AC_WID_AUD_OUT:
if (hdmi_add_cvt(codec, nid) < 0) hdmi_add_cvt(codec, nid);
return -EINVAL;
break; break;
case AC_WID_PIN: case AC_WID_PIN:
caps = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP); caps = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
if (!(caps & (AC_PINCAP_HDMI | AC_PINCAP_DP))) if (!(caps & (AC_PINCAP_HDMI | AC_PINCAP_DP)))
continue; continue;
if (hdmi_add_pin(codec, nid) < 0) hdmi_add_pin(codec, nid);
return -EINVAL;
break; break;
} }
} }

View File

@ -40,7 +40,7 @@
* *
* The HDA correspondence of pipes/ports are converter/pin nodes. * The HDA correspondence of pipes/ports are converter/pin nodes.
*/ */
#define MAX_HDMI_CVTS 2 #define MAX_HDMI_CVTS 3
#define MAX_HDMI_PINS 3 #define MAX_HDMI_PINS 3
#include "patch_hdmi.c" #include "patch_hdmi.c"
@ -48,6 +48,7 @@
static char *intel_hdmi_pcm_names[MAX_HDMI_CVTS] = { static char *intel_hdmi_pcm_names[MAX_HDMI_CVTS] = {
"INTEL HDMI 0", "INTEL HDMI 0",
"INTEL HDMI 1", "INTEL HDMI 1",
"INTEL HDMI 2",
}; };
/* /*
@ -185,14 +186,15 @@ static int patch_intel_hdmi(struct hda_codec *codec)
} }
static struct hda_codec_preset snd_hda_preset_intelhdmi[] = { static struct hda_codec_preset snd_hda_preset_intelhdmi[] = {
{ .id = 0x808629fb, .name = "G45 DEVCL", .patch = patch_intel_hdmi }, { .id = 0x808629fb, .name = "Crestline HDMI", .patch = patch_intel_hdmi },
{ .id = 0x80862801, .name = "G45 DEVBLC", .patch = patch_intel_hdmi }, { .id = 0x80862801, .name = "Bearlake HDMI", .patch = patch_intel_hdmi },
{ .id = 0x80862802, .name = "G45 DEVCTG", .patch = patch_intel_hdmi }, { .id = 0x80862802, .name = "Cantiga HDMI", .patch = patch_intel_hdmi },
{ .id = 0x80862803, .name = "G45 DEVELK", .patch = patch_intel_hdmi }, { .id = 0x80862803, .name = "Eaglelake HDMI", .patch = patch_intel_hdmi },
{ .id = 0x80862804, .name = "G45 DEVIBX", .patch = patch_intel_hdmi }, { .id = 0x80862804, .name = "IbexPeak HDMI", .patch = patch_intel_hdmi },
{ .id = 0x80860054, .name = "Q57 DEVIBX", .patch = patch_intel_hdmi }, { .id = 0x80860054, .name = "IbexPeak HDMI", .patch = patch_intel_hdmi },
{ .id = 0x10951392, .name = "SiI1392 HDMI", .patch = patch_intel_hdmi }, { .id = 0x80862805, .name = "CougarPoint HDMI", .patch = patch_intel_hdmi },
{} /* terminator */ { .id = 0x10951392, .name = "SiI1392 HDMI", .patch = patch_intel_hdmi },
{} /* terminator */
}; };
MODULE_ALIAS("snd-hda-codec-id:808629fb"); MODULE_ALIAS("snd-hda-codec-id:808629fb");
@ -200,6 +202,7 @@ MODULE_ALIAS("snd-hda-codec-id:80862801");
MODULE_ALIAS("snd-hda-codec-id:80862802"); MODULE_ALIAS("snd-hda-codec-id:80862802");
MODULE_ALIAS("snd-hda-codec-id:80862803"); MODULE_ALIAS("snd-hda-codec-id:80862803");
MODULE_ALIAS("snd-hda-codec-id:80862804"); MODULE_ALIAS("snd-hda-codec-id:80862804");
MODULE_ALIAS("snd-hda-codec-id:80862805");
MODULE_ALIAS("snd-hda-codec-id:80860054"); MODULE_ALIAS("snd-hda-codec-id:80860054");
MODULE_ALIAS("snd-hda-codec-id:10951392"); MODULE_ALIAS("snd-hda-codec-id:10951392");

View File

@ -276,6 +276,18 @@ struct alc_mic_route {
#define MUX_IDX_UNDEF ((unsigned char)-1) #define MUX_IDX_UNDEF ((unsigned char)-1)
struct alc_customize_define {
unsigned int sku_cfg;
unsigned char port_connectivity;
unsigned char check_sum;
unsigned char customization;
unsigned char external_amp;
unsigned int enable_pcbeep:1;
unsigned int platform_type:1;
unsigned int swap:1;
unsigned int override:1;
};
struct alc_spec { struct alc_spec {
/* codec parameterization */ /* codec parameterization */
struct snd_kcontrol_new *mixers[5]; /* mixer arrays */ struct snd_kcontrol_new *mixers[5]; /* mixer arrays */
@ -333,6 +345,7 @@ struct alc_spec {
/* dynamic controls, init_verbs and input_mux */ /* dynamic controls, init_verbs and input_mux */
struct auto_pin_cfg autocfg; struct auto_pin_cfg autocfg;
struct alc_customize_define cdefine;
struct snd_array kctls; struct snd_array kctls;
struct hda_input_mux private_imux[3]; struct hda_input_mux private_imux[3];
hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
@ -1248,6 +1261,62 @@ static void alc_init_auto_mic(struct hda_codec *codec)
spec->unsol_event = alc_sku_unsol_event; spec->unsol_event = alc_sku_unsol_event;
} }
static int alc_auto_parse_customize_define(struct hda_codec *codec)
{
unsigned int ass, tmp, i;
unsigned nid = 0;
struct alc_spec *spec = codec->spec;
ass = codec->subsystem_id & 0xffff;
if (ass != codec->bus->pci->subsystem_device && (ass & 1))
goto do_sku;
nid = 0x1d;
if (codec->vendor_id == 0x10ec0260)
nid = 0x17;
ass = snd_hda_codec_get_pincfg(codec, nid);
if (!(ass & 1)) {
printk(KERN_INFO "hda_codec: %s: SKU not ready 0x%08x\n",
codec->chip_name, ass);
return -1;
}
/* check sum */
tmp = 0;
for (i = 1; i < 16; i++) {
if ((ass >> i) & 1)
tmp++;
}
if (((ass >> 16) & 0xf) != tmp)
return -1;
spec->cdefine.port_connectivity = ass >> 30;
spec->cdefine.enable_pcbeep = (ass & 0x100000) >> 20;
spec->cdefine.check_sum = (ass >> 16) & 0xf;
spec->cdefine.customization = ass >> 8;
do_sku:
spec->cdefine.sku_cfg = ass;
spec->cdefine.external_amp = (ass & 0x38) >> 3;
spec->cdefine.platform_type = (ass & 0x4) >> 2;
spec->cdefine.swap = (ass & 0x2) >> 1;
spec->cdefine.override = ass & 0x1;
snd_printd("SKU: Nid=0x%x sku_cfg=0x%08x\n",
nid, spec->cdefine.sku_cfg);
snd_printd("SKU: port_connectivity=0x%x\n",
spec->cdefine.port_connectivity);
snd_printd("SKU: enable_pcbeep=0x%x\n", spec->cdefine.enable_pcbeep);
snd_printd("SKU: check_sum=0x%08x\n", spec->cdefine.check_sum);
snd_printd("SKU: customization=0x%08x\n", spec->cdefine.customization);
snd_printd("SKU: external_amp=0x%x\n", spec->cdefine.external_amp);
snd_printd("SKU: platform_type=0x%x\n", spec->cdefine.platform_type);
snd_printd("SKU: swap=0x%x\n", spec->cdefine.swap);
snd_printd("SKU: override=0x%x\n", spec->cdefine.override);
return 0;
}
/* check subsystem ID and set up device-specific initialization; /* check subsystem ID and set up device-specific initialization;
* return 1 if initialized, 0 if invalid SSID * return 1 if initialized, 0 if invalid SSID
*/ */
@ -3415,6 +3484,10 @@ static int alc_init(struct hda_codec *codec)
if (spec->init_hook) if (spec->init_hook)
spec->init_hook(codec); spec->init_hook(codec);
#ifdef CONFIG_SND_HDA_POWER_SAVE
if (codec->patch_ops.check_power_status)
codec->patch_ops.check_power_status(codec, 0x01);
#endif
return 0; return 0;
} }
@ -3775,6 +3848,10 @@ static int alc_resume(struct hda_codec *codec)
codec->patch_ops.init(codec); codec->patch_ops.init(codec);
snd_hda_codec_resume_amp(codec); snd_hda_codec_resume_amp(codec);
snd_hda_codec_resume_cache(codec); snd_hda_codec_resume_cache(codec);
#ifdef CONFIG_SND_HDA_POWER_SAVE
if (codec->patch_ops.check_power_status)
codec->patch_ops.check_power_status(codec, 0x01);
#endif
return 0; return 0;
} }
#endif #endif
@ -3797,6 +3874,17 @@ static struct hda_codec_ops alc_patch_ops = {
.reboot_notify = alc_shutup, .reboot_notify = alc_shutup,
}; };
/* replace the codec chip_name with the given string */
static int alc_codec_rename(struct hda_codec *codec, const char *name)
{
kfree(codec->chip_name);
codec->chip_name = kstrdup(name, GFP_KERNEL);
if (!codec->chip_name) {
alc_free(codec);
return -ENOMEM;
}
return 0;
}
/* /*
* Test configuration for debugging * Test configuration for debugging
@ -10189,21 +10277,20 @@ static int alc882_auto_create_input_ctls(struct hda_codec *codec,
static void alc882_auto_set_output_and_unmute(struct hda_codec *codec, static void alc882_auto_set_output_and_unmute(struct hda_codec *codec,
hda_nid_t nid, int pin_type, hda_nid_t nid, int pin_type,
int dac_idx) hda_nid_t dac)
{ {
/* set as output */
struct alc_spec *spec = codec->spec;
int idx; int idx;
/* set as output */
alc_set_pin_output(codec, nid, pin_type); alc_set_pin_output(codec, nid, pin_type);
if (dac_idx >= spec->multiout.num_dacs)
return;
if (spec->multiout.dac_nids[dac_idx] == 0x25)
idx = 4;
else
idx = spec->multiout.dac_nids[dac_idx] - 2;
snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, idx);
if (dac == 0x25)
idx = 4;
else if (dac >= 0x02 && dac <= 0x05)
idx = dac - 2;
else
return;
snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, idx);
} }
static void alc882_auto_init_multi_out(struct hda_codec *codec) static void alc882_auto_init_multi_out(struct hda_codec *codec)
@ -10216,22 +10303,29 @@ static void alc882_auto_init_multi_out(struct hda_codec *codec)
int pin_type = get_pin_type(spec->autocfg.line_out_type); int pin_type = get_pin_type(spec->autocfg.line_out_type);
if (nid) if (nid)
alc882_auto_set_output_and_unmute(codec, nid, pin_type, alc882_auto_set_output_and_unmute(codec, nid, pin_type,
i); spec->multiout.dac_nids[i]);
} }
} }
static void alc882_auto_init_hp_out(struct hda_codec *codec) static void alc882_auto_init_hp_out(struct hda_codec *codec)
{ {
struct alc_spec *spec = codec->spec; struct alc_spec *spec = codec->spec;
hda_nid_t pin; hda_nid_t pin, dac;
pin = spec->autocfg.hp_pins[0]; pin = spec->autocfg.hp_pins[0];
if (pin) /* connect to front */ if (pin) {
/* use dac 0 */ dac = spec->multiout.hp_nid;
alc882_auto_set_output_and_unmute(codec, pin, PIN_HP, 0); if (!dac)
dac = spec->multiout.dac_nids[0]; /* to front */
alc882_auto_set_output_and_unmute(codec, pin, PIN_HP, dac);
}
pin = spec->autocfg.speaker_pins[0]; pin = spec->autocfg.speaker_pins[0];
if (pin) if (pin) {
alc882_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0); dac = spec->multiout.extra_out_nid[0];
if (!dac)
dac = spec->multiout.dac_nids[0]; /* to front */
alc882_auto_set_output_and_unmute(codec, pin, PIN_OUT, dac);
}
} }
static void alc882_auto_init_analog_input(struct hda_codec *codec) static void alc882_auto_init_analog_input(struct hda_codec *codec)
@ -10345,6 +10439,10 @@ static int alc882_parse_auto_config(struct hda_codec *codec)
if (err < 0) if (err < 0)
return err; return err;
err = alc880_auto_create_multi_out_ctls(spec, &spec->autocfg); err = alc880_auto_create_multi_out_ctls(spec, &spec->autocfg);
if (err < 0)
return err;
err = alc880_auto_create_extra_out(spec, spec->autocfg.hp_pins[0],
"Headphone");
if (err < 0) if (err < 0)
return err; return err;
err = alc880_auto_create_extra_out(spec, err = alc880_auto_create_extra_out(spec,
@ -10352,10 +10450,6 @@ static int alc882_parse_auto_config(struct hda_codec *codec)
"Speaker"); "Speaker");
if (err < 0) if (err < 0)
return err; return err;
err = alc880_auto_create_extra_out(spec, spec->autocfg.hp_pins[0],
"Headphone");
if (err < 0)
return err;
err = alc882_auto_create_input_ctls(codec, &spec->autocfg); err = alc882_auto_create_input_ctls(codec, &spec->autocfg);
if (err < 0) if (err < 0)
return err; return err;
@ -10425,6 +10519,8 @@ static int patch_alc882(struct hda_codec *codec)
codec->spec = spec; codec->spec = spec;
alc_auto_parse_customize_define(codec);
switch (codec->vendor_id) { switch (codec->vendor_id) {
case 0x10ec0882: case 0x10ec0882:
case 0x10ec0885: case 0x10ec0885:
@ -10484,9 +10580,6 @@ static int patch_alc882(struct hda_codec *codec)
spec->stream_digital_playback = &alc882_pcm_digital_playback; spec->stream_digital_playback = &alc882_pcm_digital_playback;
spec->stream_digital_capture = &alc882_pcm_digital_capture; spec->stream_digital_capture = &alc882_pcm_digital_capture;
if (codec->vendor_id == 0x10ec0888)
spec->init_amp = ALC_INIT_DEFAULT; /* always initialize */
if (!spec->adc_nids && spec->input_mux) { if (!spec->adc_nids && spec->input_mux) {
int i, j; int i, j;
spec->num_adc_nids = 0; spec->num_adc_nids = 0;
@ -10521,7 +10614,9 @@ static int patch_alc882(struct hda_codec *codec)
} }
set_capture_mixer(codec); set_capture_mixer(codec);
set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
if (spec->cdefine.enable_pcbeep)
set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
if (board_config == ALC882_AUTO) if (board_config == ALC882_AUTO)
alc_pick_fixup(codec, alc882_fixup_tbl, alc882_fixups, 0); alc_pick_fixup(codec, alc882_fixup_tbl, alc882_fixups, 0);
@ -12308,6 +12403,7 @@ static int patch_alc262(struct hda_codec *codec)
snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_PROC_COEF, tmp | 0x80); snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_PROC_COEF, tmp | 0x80);
} }
#endif #endif
alc_auto_parse_customize_define(codec);
alc_fix_pll_init(codec, 0x20, 0x0a, 10); alc_fix_pll_init(codec, 0x20, 0x0a, 10);
@ -12386,7 +12482,7 @@ static int patch_alc262(struct hda_codec *codec)
} }
if (!spec->cap_mixer && !spec->no_analog) if (!spec->cap_mixer && !spec->no_analog)
set_capture_mixer(codec); set_capture_mixer(codec);
if (!spec->no_analog) if (!spec->no_analog && spec->cdefine.enable_pcbeep)
set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
spec->vmaster_nid = 0x0c; spec->vmaster_nid = 0x0c;
@ -14005,6 +14101,35 @@ static struct hda_pcm_stream alc269_44k_pcm_analog_capture = {
/* NID is set in alc_build_pcms */ /* NID is set in alc_build_pcms */
}; };
#ifdef CONFIG_SND_HDA_POWER_SAVE
static int alc269_mic2_for_mute_led(struct hda_codec *codec)
{
switch (codec->subsystem_id) {
case 0x103c1586:
return 1;
}
return 0;
}
static int alc269_mic2_mute_check_ps(struct hda_codec *codec, hda_nid_t nid)
{
/* update mute-LED according to the speaker mute state */
if (nid == 0x01 || nid == 0x14) {
int pinval;
if (snd_hda_codec_amp_read(codec, 0x14, 0, HDA_OUTPUT, 0) &
HDA_AMP_MUTE)
pinval = 0x24;
else
pinval = 0x20;
/* mic2 vref pin is used for mute LED control */
snd_hda_codec_update_cache(codec, 0x19, 0,
AC_VERB_SET_PIN_WIDGET_CONTROL,
pinval);
}
return alc_check_power_status(codec, nid);
}
#endif /* CONFIG_SND_HDA_POWER_SAVE */
/* /*
* BIOS auto configuration * BIOS auto configuration
*/ */
@ -14082,7 +14207,7 @@ enum {
ALC269_FIXUP_SONY_VAIO, ALC269_FIXUP_SONY_VAIO,
}; };
const static struct hda_verb alc269_sony_vaio_fixup_verbs[] = { static const struct hda_verb alc269_sony_vaio_fixup_verbs[] = {
{0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREFGRD}, {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREFGRD},
{} {}
}; };
@ -14290,17 +14415,17 @@ static int patch_alc269(struct hda_codec *codec)
codec->spec = spec; codec->spec = spec;
alc_fix_pll_init(codec, 0x20, 0x04, 15); alc_auto_parse_customize_define(codec);
if ((alc_read_coef_idx(codec, 0) & 0x00f0) == 0x0010){ if ((alc_read_coef_idx(codec, 0) & 0x00f0) == 0x0010){
kfree(codec->chip_name); if (codec->bus->pci->subsystem_vendor == 0x1025 &&
codec->chip_name = kstrdup("ALC259", GFP_KERNEL); spec->cdefine.platform_type == 1)
if (!codec->chip_name) { alc_codec_rename(codec, "ALC271X");
alc_free(codec); else
return -ENOMEM; alc_codec_rename(codec, "ALC259");
}
is_alc269vb = 1; is_alc269vb = 1;
} } else
alc_fix_pll_init(codec, 0x20, 0x04, 15);
board_config = snd_hda_check_board_config(codec, ALC269_MODEL_LAST, board_config = snd_hda_check_board_config(codec, ALC269_MODEL_LAST,
alc269_models, alc269_models,
@ -14365,7 +14490,8 @@ static int patch_alc269(struct hda_codec *codec)
if (!spec->cap_mixer) if (!spec->cap_mixer)
set_capture_mixer(codec); set_capture_mixer(codec);
set_beep_amp(spec, 0x0b, 0x04, HDA_INPUT); if (spec->cdefine.enable_pcbeep)
set_beep_amp(spec, 0x0b, 0x04, HDA_INPUT);
if (board_config == ALC269_AUTO) if (board_config == ALC269_AUTO)
alc_pick_fixup(codec, alc269_fixup_tbl, alc269_fixups, 0); alc_pick_fixup(codec, alc269_fixup_tbl, alc269_fixups, 0);
@ -14378,6 +14504,8 @@ static int patch_alc269(struct hda_codec *codec)
#ifdef CONFIG_SND_HDA_POWER_SAVE #ifdef CONFIG_SND_HDA_POWER_SAVE
if (!spec->loopback.amplist) if (!spec->loopback.amplist)
spec->loopback.amplist = alc269_loopbacks; spec->loopback.amplist = alc269_loopbacks;
if (alc269_mic2_for_mute_led(codec))
codec->patch_ops.check_power_status = alc269_mic2_mute_check_ps;
#endif #endif
return 0; return 0;
@ -18525,16 +18653,16 @@ static int patch_alc662(struct hda_codec *codec)
codec->spec = spec; codec->spec = spec;
alc_auto_parse_customize_define(codec);
alc_fix_pll_init(codec, 0x20, 0x04, 15); alc_fix_pll_init(codec, 0x20, 0x04, 15);
if (alc_read_coef_idx(codec, 0)==0x8020){ if (alc_read_coef_idx(codec, 0) == 0x8020)
kfree(codec->chip_name); alc_codec_rename(codec, "ALC661");
codec->chip_name = kstrdup("ALC661", GFP_KERNEL); else if ((alc_read_coef_idx(codec, 0) & (1 << 14)) &&
if (!codec->chip_name) { codec->bus->pci->subsystem_vendor == 0x1025 &&
alc_free(codec); spec->cdefine.platform_type == 1)
return -ENOMEM; alc_codec_rename(codec, "ALC272X");
}
}
board_config = snd_hda_check_board_config(codec, ALC662_MODEL_LAST, board_config = snd_hda_check_board_config(codec, ALC662_MODEL_LAST,
alc662_models, alc662_models,
@ -18584,18 +18712,20 @@ static int patch_alc662(struct hda_codec *codec)
if (!spec->cap_mixer) if (!spec->cap_mixer)
set_capture_mixer(codec); set_capture_mixer(codec);
switch (codec->vendor_id) { if (spec->cdefine.enable_pcbeep) {
case 0x10ec0662: switch (codec->vendor_id) {
set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); case 0x10ec0662:
break; set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
case 0x10ec0272: break;
case 0x10ec0663: case 0x10ec0272:
case 0x10ec0665: case 0x10ec0663:
set_beep_amp(spec, 0x0b, 0x04, HDA_INPUT); case 0x10ec0665:
break; set_beep_amp(spec, 0x0b, 0x04, HDA_INPUT);
case 0x10ec0273: break;
set_beep_amp(spec, 0x0b, 0x03, HDA_INPUT); case 0x10ec0273:
break; set_beep_amp(spec, 0x0b, 0x03, HDA_INPUT);
break;
}
} }
spec->vmaster_nid = 0x02; spec->vmaster_nid = 0x02;