{ 0x1095, "Silicon Image" },
{ 0x10de, "Nvidia" },
{ 0x10ec, "Realtek" },
+ { 0x1102, "Creative" },
{ 0x1106, "VIA" },
{ 0x111d, "IDT" },
{ 0x11c1, "LSI" },
unsigned int verb, unsigned int parm)
{
struct hda_bus *bus = codec->bus;
- unsigned int res;
+ unsigned int cmd, res;
+ int repeated = 0;
- res = make_codec_cmd(codec, nid, direct, verb, parm);
+ cmd = make_codec_cmd(codec, nid, direct, verb, parm);
snd_hda_power_up(codec);
mutex_lock(&bus->cmd_mutex);
- if (!bus->ops.command(bus, res))
+ again:
+ if (!bus->ops.command(bus, cmd)) {
res = bus->ops.get_response(bus);
- else
+ if (res == -1 && bus->rirb_error) {
+ if (repeated++ < 1) {
+ snd_printd(KERN_WARNING "hda_codec: "
+ "Trying verb 0x%08x again\n", cmd);
+ goto again;
+ }
+ }
+ } else
res = (unsigned int)-1;
mutex_unlock(&bus->cmd_mutex);
snd_hda_power_down(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;
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
*/
{
if (!codec)
return;
+ restore_init_pincfgs(codec);
#ifdef CONFIG_SND_HDA_POWER_SAVE
cancel_delayed_work(&codec->power_work);
flush_workqueue(codec->bus->workq);
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
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) {
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;
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);
if (codecp)
*codecp = codec;
return 0;
+
+ error:
+ snd_hda_codec_free(codec);
+ return err;
}
EXPORT_SYMBOL_HDA(snd_hda_codec_new);
/* 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 HDA_HASH_PARPCM_KEY(nid) (u32)((nid) + (0x03 << 24))
+#define HDA_HASH_PARSTR_KEY(nid) (u32)((nid) + (0x04 << 24))
#define INFO_AMP_CAPS (1<<0)
#define INFO_AMP_VOL(ch) (1 << (1 + (ch)))
}
EXPORT_SYMBOL_HDA(snd_hda_override_amp_caps);
+static unsigned int
+query_caps_hash(struct hda_codec *codec, hda_nid_t nid, u32 key,
+ unsigned int (*func)(struct hda_codec *, hda_nid_t))
+{
+ struct hda_amp_info *info;
+
+ info = get_alloc_amp_hash(codec, key);
+ if (!info)
+ return 0;
+ if (!info->head.val) {
+ info->head.val |= INFO_AMP_CAPS;
+ info->amp_caps = func(codec, nid);
+ }
+ return info->amp_caps;
+}
+
+static unsigned int read_pin_cap(struct hda_codec *codec, hda_nid_t nid)
+{
+ return snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
+}
+
+u32 snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t nid)
+{
+ return query_caps_hash(codec, nid, HDA_HASH_PINCAP_KEY(nid),
+ read_pin_cap);
+}
+EXPORT_SYMBOL_HDA(snd_hda_query_pin_caps);
+
/*
* read the current volume to info
* if the cache exists, read the cache value.
memset(&id, 0, sizeof(id));
id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
id.index = idx;
+ if (snd_BUG_ON(strlen(name) >= sizeof(id.name)))
+ return NULL;
strcpy(id.name, name);
return snd_ctl_find_id(codec->bus->card, &id);
}
}
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)
{
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);
/* 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);
}
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,
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;
}
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;
* 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,
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);
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;
}
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;
}
EXPORT_SYMBOL_HDA(snd_hda_calc_stream_format);
+static unsigned int get_pcm_param(struct hda_codec *codec, hda_nid_t nid)
+{
+ unsigned int val = 0;
+ if (nid != codec->afg &&
+ (get_wcaps(codec, nid) & AC_WCAP_FORMAT_OVRD))
+ val = snd_hda_param_read(codec, nid, AC_PAR_PCM);
+ if (!val || val == -1)
+ val = snd_hda_param_read(codec, codec->afg, AC_PAR_PCM);
+ if (!val || val == -1)
+ return 0;
+ return val;
+}
+
+static unsigned int query_pcm_param(struct hda_codec *codec, hda_nid_t nid)
+{
+ return query_caps_hash(codec, nid, HDA_HASH_PARPCM_KEY(nid),
+ get_pcm_param);
+}
+
+static unsigned int get_stream_param(struct hda_codec *codec, hda_nid_t nid)
+{
+ unsigned int streams = snd_hda_param_read(codec, nid, AC_PAR_STREAM);
+ if (!streams || streams == -1)
+ streams = snd_hda_param_read(codec, codec->afg, AC_PAR_STREAM);
+ if (!streams || streams == -1)
+ return 0;
+ return streams;
+}
+
+static unsigned int query_stream_param(struct hda_codec *codec, hda_nid_t nid)
+{
+ return query_caps_hash(codec, nid, HDA_HASH_PARSTR_KEY(nid),
+ get_stream_param);
+}
+
/**
* snd_hda_query_supported_pcm - query the supported PCM rates and formats
* @codec: the HDA codec
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)) {
- val = snd_hda_param_read(codec, nid, AC_PAR_PCM);
- if (val == -1)
- return -EIO;
- }
- if (!val)
- val = snd_hda_param_read(codec, codec->afg, AC_PAR_PCM);
+ wcaps = get_wcaps(codec, nid);
+ val = query_pcm_param(codec, nid);
if (ratesp) {
u32 rates = 0;
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)
+ streams = query_stream_param(codec, nid);
+ if (!streams)
return -EIO;
- if (!streams) {
- streams = snd_hda_param_read(codec, codec->afg,
- AC_PAR_STREAM);
- if (streams == -1)
- return -EIO;
- }
bps = 0;
if (streams & AC_SUPFMT_PCM) {
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)
int i;
unsigned int val = 0, rate, stream;
- if (nid != codec->afg &&
- (get_wcaps(codec, nid) & AC_WCAP_FORMAT_OVRD)) {
- val = snd_hda_param_read(codec, nid, AC_PAR_PCM);
- if (val == -1)
- return 0;
- }
- if (!val) {
- val = snd_hda_param_read(codec, codec->afg, AC_PAR_PCM);
- if (val == -1)
- return 0;
- }
+ val = query_pcm_param(codec, nid);
+ if (!val)
+ return 0;
rate = format & 0xff00;
for (i = 0; i < AC_PAR_PCM_RATE_BITS; i++)
if (i >= AC_PAR_PCM_RATE_BITS)
return 0;
- stream = snd_hda_param_read(codec, nid, AC_PAR_STREAM);
- if (stream == -1)
- return 0;
- if (!stream && nid != codec->afg)
- stream = snd_hda_param_read(codec, codec->afg, AC_PAR_STREAM);
- if (!stream || stream == -1)
+ stream = query_stream_param(codec, nid);
+ if (!stream)
return 0;
if (stream & AC_SUPFMT_PCM) {
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;
for (i = 0; i < ARRAY_SIZE(audio_idx); i++) {
dev = audio_idx[i];
if (!test_bit(dev, bus->pcm_dev_bits))
- break;
- }
- if (i >= ARRAY_SIZE(audio_idx)) {
- snd_printk(KERN_WARNING "Too many audio devices\n");
- return -EAGAIN;
+ goto ok;
}
- 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:
snd_printk(KERN_WARNING "Invalid PCM type %d\n", type);
return -EINVAL;
}
+ ok:
set_bit(dev, bus->pcm_dev_bits);
return dev;
}
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];
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;
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);