diff options
Diffstat (limited to 'sound/pci/hda/hda_codec.c')
-rw-r--r-- | sound/pci/hda/hda_codec.c | 519 |
1 files changed, 444 insertions, 75 deletions
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 3c596da2b9b5..8820faf6c9d8 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -487,7 +487,6 @@ int /*__devinit*/ snd_hda_bus_new(struct snd_card *card, { struct hda_bus *bus; int err; - char qname[8]; static struct snd_device_ops dev_ops = { .dev_register = snd_hda_bus_dev_register, .dev_free = snd_hda_bus_dev_free, @@ -517,10 +516,12 @@ int /*__devinit*/ snd_hda_bus_new(struct snd_card *card, mutex_init(&bus->cmd_mutex); INIT_LIST_HEAD(&bus->codec_list); - snprintf(qname, sizeof(qname), "hda%d", card->number); - bus->workq = create_workqueue(qname); + snprintf(bus->workq_name, sizeof(bus->workq_name), + "hd-audio%d", card->number); + bus->workq = create_singlethread_workqueue(bus->workq_name); if (!bus->workq) { - snd_printk(KERN_ERR "cannot create workqueue %s\n", qname); + snd_printk(KERN_ERR "cannot create workqueue %s\n", + bus->workq_name); kfree(bus); return -ENOMEM; } @@ -641,19 +642,21 @@ static int get_codec_name(struct hda_codec *codec) */ static void /*__devinit*/ setup_fg_nodes(struct hda_codec *codec) { - int i, total_nodes; + int i, total_nodes, function_id; hda_nid_t nid; total_nodes = snd_hda_get_sub_nodes(codec, AC_NODE_ROOT, &nid); for (i = 0; i < total_nodes; i++, nid++) { - unsigned int func; - func = snd_hda_param_read(codec, nid, AC_PAR_FUNCTION_TYPE); - switch (func & 0xff) { + function_id = snd_hda_param_read(codec, nid, + AC_PAR_FUNCTION_TYPE) & 0xff; + switch (function_id) { case AC_GRP_AUDIO_FUNCTION: codec->afg = nid; + codec->function_id = function_id; break; case AC_GRP_MODEM_FUNCTION: codec->mfg = nid; + codec->function_id = function_id; break; default: break; @@ -681,11 +684,140 @@ static int read_widget_caps(struct hda_codec *codec, hda_nid_t fg_node) 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, unsigned int record_size); 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 */ @@ -693,6 +825,7 @@ static void snd_hda_codec_free(struct hda_codec *codec) { if (!codec) return; + restore_init_pincfgs(codec); #ifdef CONFIG_SND_HDA_POWER_SAVE cancel_delayed_work(&codec->power_work); flush_workqueue(codec->bus->workq); @@ -711,6 +844,9 @@ static void snd_hda_codec_free(struct hda_codec *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 * @bus: the bus to assign @@ -750,6 +886,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->cmd_cache, sizeof(struct hda_cache_head)); 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) { codec->modelname = kstrdup(codec->bus->modelname, GFP_KERNEL); if (!codec->modelname) { @@ -786,15 +924,18 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr setup_fg_nodes(codec); if (!codec->afg && !codec->mfg) { snd_printdd("hda_codec: no AFG or MFG node found\n"); - snd_hda_codec_free(codec); - return -ENODEV; + err = -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_hda_codec_free(codec); - return -ENOMEM; + goto error; } + err = read_pin_defaults(codec); + if (err < 0) + goto error; if (!codec->subsystem_id) { hda_nid_t nid = codec->afg ? codec->afg : codec->mfg; @@ -805,12 +946,15 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr if (bus->modelname) 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) { err = snd_hda_codec_configure(codec); - if (err < 0) { - snd_hda_codec_free(codec); - return err; - } + if (err < 0) + goto error; } snd_hda_codec_proc_new(codec); @@ -823,6 +967,10 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr if (codecp) *codecp = codec; return 0; + + error: + snd_hda_codec_free(codec); + return err; } EXPORT_SYMBOL_HDA(snd_hda_codec_new); @@ -906,6 +1054,7 @@ EXPORT_SYMBOL_HDA(snd_hda_codec_cleanup_stream); /* FIXME: more better hash key? */ #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_VOL(ch) (1 << (1 + (ch))) @@ -996,6 +1145,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); +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 * if the cache exists, read the cache value. @@ -1119,6 +1283,7 @@ int snd_hda_mixer_amp_volume_info(struct snd_kcontrol *kcontrol, u16 nid = get_amp_nid(kcontrol); u8 chs = get_amp_channels(kcontrol); int dir = get_amp_direction(kcontrol); + unsigned int ofs = get_amp_offset(kcontrol); u32 caps; caps = query_amp_caps(codec, nid, dir); @@ -1130,6 +1295,8 @@ int snd_hda_mixer_amp_volume_info(struct snd_kcontrol *kcontrol, kcontrol->id.name); return -EINVAL; } + if (ofs < caps) + caps -= ofs; uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = chs == 3 ? 2 : 1; uinfo->value.integer.min = 0; @@ -1138,6 +1305,32 @@ int snd_hda_mixer_amp_volume_info(struct snd_kcontrol *kcontrol, } 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, struct snd_ctl_elem_value *ucontrol) { @@ -1146,14 +1339,13 @@ int snd_hda_mixer_amp_volume_get(struct snd_kcontrol *kcontrol, int chs = get_amp_channels(kcontrol); int dir = get_amp_direction(kcontrol); int idx = get_amp_index(kcontrol); + unsigned int ofs = get_amp_offset(kcontrol); long *valp = ucontrol->value.integer.value; if (chs & 1) - *valp++ = snd_hda_codec_amp_read(codec, nid, 0, dir, idx) - & HDA_AMP_VOLMASK; + *valp++ = read_amp_value(codec, nid, 0, dir, idx, ofs); if (chs & 2) - *valp = snd_hda_codec_amp_read(codec, nid, 1, dir, idx) - & HDA_AMP_VOLMASK; + *valp = read_amp_value(codec, nid, 1, dir, idx, ofs); return 0; } EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_volume_get); @@ -1166,18 +1358,17 @@ int snd_hda_mixer_amp_volume_put(struct snd_kcontrol *kcontrol, int chs = get_amp_channels(kcontrol); int dir = get_amp_direction(kcontrol); int idx = get_amp_index(kcontrol); + unsigned int ofs = get_amp_offset(kcontrol); long *valp = ucontrol->value.integer.value; int change = 0; snd_hda_power_up(codec); if (chs & 1) { - change = snd_hda_codec_amp_update(codec, nid, 0, dir, idx, - 0x7f, *valp); + change = update_amp_value(codec, nid, 0, dir, idx, ofs, *valp); valp++; } if (chs & 2) - change |= snd_hda_codec_amp_update(codec, nid, 1, dir, idx, - 0x7f, *valp); + change |= update_amp_value(codec, nid, 1, dir, idx, ofs, *valp); snd_hda_power_down(codec); return change; } @@ -1189,6 +1380,7 @@ int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag, struct hda_codec *codec = snd_kcontrol_chip(kcontrol); hda_nid_t nid = get_amp_nid(kcontrol); int dir = get_amp_direction(kcontrol); + unsigned int ofs = get_amp_offset(kcontrol); u32 caps, val1, val2; if (size < 4 * sizeof(unsigned int)) @@ -1197,6 +1389,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 = (val2 + 1) * 25; val1 = -((caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT); + val1 += ofs; val1 = ((int)val1) * ((int)val2); if (put_user(SNDRV_CTL_TLVT_DB_SCALE, _tlv)) return -EFAULT; @@ -1267,7 +1460,6 @@ int snd_hda_ctl_add(struct hda_codec *codec, struct snd_kcontrol *kctl) } EXPORT_SYMBOL_HDA(snd_hda_ctl_add); -#ifdef CONFIG_SND_HDA_RECONFIG /* Clear all controls assigned to the given codec */ void snd_hda_ctls_clear(struct hda_codec *codec) { @@ -1278,9 +1470,52 @@ void snd_hda_ctls_clear(struct hda_codec *codec) 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 cancel_delayed_work(&codec->power_work); @@ -1290,8 +1525,7 @@ void snd_hda_codec_reset(struct hda_codec *codec) /* relase PCMs */ for (i = 0; i < codec->num_pcms; i++) { if (codec->pcm_info[i].pcm) { - snd_device_free(codec->bus->card, - codec->pcm_info[i].pcm); + snd_device_free(card, codec->pcm_info[i].pcm); clear_bit(codec->pcm_info[i].device, codec->bus->pcm_dev_bits); } @@ -1304,13 +1538,22 @@ void snd_hda_codec_reset(struct hda_codec *codec) free_hda_cache(&codec->cmd_cache); init_hda_cache(&codec->amp_cache, sizeof(struct hda_amp_info)); 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->pcm_info = 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); 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 */ int snd_hda_add_vmaster(struct hda_codec *codec, char *name, @@ -1335,15 +1578,20 @@ int snd_hda_add_vmaster(struct hda_codec *codec, char *name, for (s = slaves; *s; s++) { struct snd_kcontrol *sctl; - - sctl = snd_hda_find_mixer_ctl(codec, *s); - if (!sctl) { - snd_printdd("Cannot find slave %s, skipped\n", *s); - continue; + int i = 0; + for (;;) { + sctl = _snd_hda_find_mixer_ctl(codec, *s, i); + if (!sctl) { + if (!i) + snd_printdd("Cannot find slave %s, " + "skipped\n", *s); + break; + } + err = snd_ctl_add_slave(kctl, sctl); + if (err < 0) + return err; + i++; } - err = snd_ctl_add_slave(kctl, sctl); - if (err < 0) - return err; } return 0; } @@ -1954,6 +2202,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++) { kctl = snd_ctl_new1(dig_mix, codec); + if (!kctl) + return -ENOMEM; kctl->private_value = nid; err = snd_hda_ctl_add(codec, kctl); if (err < 0) @@ -2002,7 +2252,11 @@ int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid, err = bus->ops.command(bus, res); if (!err) { struct hda_cache_head *c; - u32 key = build_cmd_cache_key(nid, verb); + 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); c = get_alloc_hash(&codec->cmd_cache, key); if (c) c->val = parm; @@ -2073,8 +2327,7 @@ static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg, * don't power down the widget if it controls * eapd and EAPD_BTLENABLE is set. */ - pincap = snd_hda_param_read(codec, nid, - AC_PAR_PIN_CAP); + pincap = snd_hda_query_pin_caps(codec, nid); if (pincap & AC_PINCAP_EAPD) { int eapd = snd_hda_codec_read(codec, nid, 0, @@ -2143,6 +2396,7 @@ static void hda_call_codec_resume(struct hda_codec *codec) hda_set_power_state(codec, codec->afg ? codec->afg : codec->mfg, AC_PWRST_D0); + restore_pincfgs(codec); /* restore all current pin configs */ hda_exec_init_verbs(codec); if (codec->patch_ops.resume) codec->patch_ops.resume(codec); @@ -2170,8 +2424,16 @@ int /*__devinit*/ snd_hda_build_controls(struct hda_bus *bus) list_for_each_entry(codec, &bus->codec_list, list) { int err = snd_hda_codec_build_controls(codec); - if (err < 0) - return err; + 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 0; } @@ -2180,19 +2442,12 @@ EXPORT_SYMBOL_HDA(snd_hda_build_controls); int snd_hda_codec_build_controls(struct hda_codec *codec) { 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); /* continue to initialize... */ if (codec->patch_ops.init) err = codec->patch_ops.init(codec); if (!err && codec->patch_ops.build_controls) err = codec->patch_ops.build_controls(codec); - snd_hda_power_down(codec); if (err < 0) return err; return 0; @@ -2305,12 +2560,11 @@ EXPORT_SYMBOL_HDA(snd_hda_calc_stream_format); static int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid, u32 *ratesp, u64 *formatsp, unsigned int *bpsp) { - int i; - unsigned int val, streams; + unsigned int i, val, wcaps; val = 0; - if (nid != codec->afg && - (get_wcaps(codec, nid) & AC_WCAP_FORMAT_OVRD)) { + wcaps = get_wcaps(codec, nid); + if (nid != codec->afg && (wcaps & AC_WCAP_FORMAT_OVRD)) { val = snd_hda_param_read(codec, nid, AC_PAR_PCM); if (val == -1) return -EIO; @@ -2324,15 +2578,20 @@ static int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid, if (val & (1 << i)) 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; } if (formatsp || bpsp) { u64 formats = 0; - unsigned int bps; - unsigned int wcaps; + unsigned int streams, bps; - wcaps = get_wcaps(codec, nid); streams = snd_hda_param_read(codec, nid, AC_PAR_STREAM); if (streams == -1) return -EIO; @@ -2385,6 +2644,15 @@ static int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid, formats |= SNDRV_PCM_FMTBIT_U8; 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) *formatsp = formats; if (bpsp) @@ -2500,12 +2768,16 @@ static int hda_pcm_default_cleanup(struct hda_pcm_stream *hinfo, static int set_pcm_default_values(struct hda_codec *codec, struct hda_pcm_stream *info) { + int err; + /* query support PCM information from the given NID */ 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->formats ? NULL : &info->formats, info->maxbps ? NULL : &info->maxbps); + if (err < 0) + return err; } if (info->ops.open == NULL) info->ops.open = hda_pcm_default_open_close; @@ -2548,13 +2820,10 @@ static int get_empty_pcm_device(struct hda_bus *bus, int type) for (i = 0; i < ARRAY_SIZE(audio_idx); i++) { dev = audio_idx[i]; 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"); - return -EAGAIN; - } - break; + snd_printk(KERN_WARNING "Too many audio devices\n"); + return -EAGAIN; case HDA_PCM_TYPE_SPDIF: case HDA_PCM_TYPE_HDMI: case HDA_PCM_TYPE_MODEM: @@ -2569,6 +2838,7 @@ static int get_empty_pcm_device(struct hda_bus *bus, int type) snd_printk(KERN_WARNING "Invalid PCM type %d\n", type); return -EINVAL; } + ok: set_bit(dev, bus->pcm_dev_bits); return dev; } @@ -2605,24 +2875,36 @@ int snd_hda_codec_build_pcms(struct hda_codec *codec) if (!codec->patch_ops.build_pcms) return 0; err = codec->patch_ops.build_pcms(codec); - if (err < 0) - return err; + 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; + } + } } for (pcm = 0; pcm < codec->num_pcms; pcm++) { struct hda_pcm *cpcm = &codec->pcm_info[pcm]; int dev; if (!cpcm->stream[0].substreams && !cpcm->stream[1].substreams) - return 0; /* no substreams assigned */ + continue; /* no substreams assigned */ if (!cpcm->pcm) { dev = get_empty_pcm_device(codec->bus, cpcm->pcm_type); if (dev < 0) - return 0; + continue; /* no fatal error */ cpcm->device = dev; err = snd_hda_attach_pcm(codec, cpcm); - if (err < 0) - return err; + if (err < 0) { + printk(KERN_ERR "hda_codec: cannot attach " + "PCM stream %d for codec #%d\n", + dev, codec->addr); + continue; /* no fatal error */ + } } } return 0; @@ -2724,6 +3006,67 @@ int snd_hda_check_board_config(struct hda_codec *codec, EXPORT_SYMBOL_HDA(snd_hda_check_board_config); /** + * snd_hda_check_board_codec_sid_config - compare the current codec + subsystem ID with the + config table + + This is important for Gateway notebooks with SB450 HDA Audio + where the vendor ID of the PCI device is: + ATI Technologies Inc SB450 HDA Audio [1002:437b] + and the vendor/subvendor are found only at the codec. + + * @codec: the HDA codec + * @num_configs: number of config enums + * @models: array of model name strings + * @tbl: configuration table, terminated by null entries + * + * Compares the modelname or PCI subsystem id of the current codec with the + * given configuration table. If a matching entry is found, returns its + * config value (supposed to be 0 or positive). + * + * If no entries are matching, the function returns a negative value. + */ +int snd_hda_check_board_codec_sid_config(struct hda_codec *codec, + int num_configs, const char **models, + const struct snd_pci_quirk *tbl) +{ + const struct snd_pci_quirk *q; + + /* Search for codec ID */ + for (q = tbl; q->subvendor; q++) { + unsigned long vendorid = (q->subdevice) | (q->subvendor << 16); + + if (vendorid == codec->subsystem_id) + break; + } + + if (!q->subvendor) + return -1; + + tbl = q; + + if (tbl->value >= 0 && tbl->value < num_configs) { +#ifdef CONFIG_SND_DEBUG_DETECT + char tmp[10]; + const char *model = NULL; + if (models) + model = models[tbl->value]; + if (!model) { + sprintf(tmp, "#%d", tbl->value); + model = tmp; + } + snd_printdd(KERN_INFO "hda_codec: model '%s' is selected " + "for config %x:%x (%s)\n", + model, tbl->subvendor, tbl->subdevice, + (tbl->name ? tbl->name : "Unknown device")); +#endif + return tbl->value; + } + return -1; +} +EXPORT_SYMBOL_HDA(snd_hda_check_board_codec_sid_config); + +/** * snd_hda_add_new_ctls - create controls from the array * @codec: the HDA codec * @knew: the array of struct snd_kcontrol_new @@ -2815,7 +3158,7 @@ void snd_hda_power_down(struct hda_codec *codec) return; if (power_save(codec)) { codec->power_transition = 1; /* avoid reentrance */ - schedule_delayed_work(&codec->power_work, + queue_delayed_work(codec->bus->workq, &codec->power_work, msecs_to_jiffies(power_save(codec) * 1000)); } } @@ -3026,6 +3369,16 @@ int snd_hda_multi_out_dig_prepare(struct hda_codec *codec, } EXPORT_SYMBOL_HDA(snd_hda_multi_out_dig_prepare); +int snd_hda_multi_out_dig_cleanup(struct hda_codec *codec, + struct hda_multi_out *mout) +{ + mutex_lock(&codec->spdif_mutex); + cleanup_dig_out_stream(codec, mout->dig_out_nid); + mutex_unlock(&codec->spdif_mutex); + return 0; +} +EXPORT_SYMBOL_HDA(snd_hda_multi_out_dig_cleanup); + /* * release the digital out */ @@ -3252,8 +3605,7 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec, if (ignore_nids && is_in_nid_list(nid, ignore_nids)) continue; - def_conf = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_CONFIG_DEFAULT, 0); + def_conf = snd_hda_codec_get_pincfg(codec, nid); if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE) continue; loc = get_defcfg_location(def_conf); @@ -3329,10 +3681,22 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec, cfg->input_pins[AUTO_PIN_AUX] = nid; break; 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; case AC_JACK_SPDIF_IN: + case AC_JACK_DIG_OTHER_IN: 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; } } @@ -3438,6 +3802,9 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec, cfg->hp_pins[1], cfg->hp_pins[2], cfg->hp_pins[3], cfg->hp_pins[4]); 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," " cd=0x%x, aux=0x%x\n", cfg->input_pins[AUTO_PIN_MIC], @@ -3446,6 +3813,8 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec, cfg->input_pins[AUTO_PIN_FRONT_LINE], cfg->input_pins[AUTO_PIN_CD], cfg->input_pins[AUTO_PIN_AUX]); + if (cfg->dig_in_pin) + snd_printd(" dig-in=0x%x\n", cfg->dig_in_pin); return 0; } |