Merge branch 'topic/hda-cache' into topic/hda
[safe/jmp/linux-2.6] / sound / pci / hda / hda_codec.c
index 04cb125..b649033 100644 (file)
@@ -48,6 +48,7 @@ static struct hda_vendor_id hda_vendor_ids[] = {
        { 0x1095, "Silicon Image" },
        { 0x10de, "Nvidia" },
        { 0x10ec, "Realtek" },
+       { 0x1102, "Creative" },
        { 0x1106, "VIA" },
        { 0x111d, "IDT" },
        { 0x11c1, "LSI" },
@@ -174,14 +175,23 @@ unsigned int snd_hda_codec_read(struct hda_codec *codec, hda_nid_t nid,
                                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);
@@ -642,19 +652,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;
@@ -842,6 +854,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
@@ -941,6 +956,11 @@ 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)
@@ -1044,6 +1064,9 @@ 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 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)))
 
@@ -1134,6 +1157,34 @@ int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
 }
 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.
@@ -1406,6 +1457,8 @@ _snd_hda_find_mixer_ctl(struct hda_codec *codec,
        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);
 }
@@ -1552,15 +1605,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;
 }
@@ -2221,7 +2279,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;
@@ -2292,8 +2354,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,
@@ -2408,19 +2469,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;
@@ -2517,6 +2571,41 @@ unsigned int snd_hda_calc_stream_format(unsigned int rate,
 }
 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
@@ -2533,18 +2622,10 @@ 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)) {
-               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;
@@ -2552,24 +2633,23 @@ 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)
+               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) {
@@ -2613,6 +2693,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)
@@ -2634,17 +2723,9 @@ int snd_hda_is_supported_format(struct hda_codec *codec, hda_nid_t nid,
        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++)
@@ -2656,12 +2737,8 @@ int snd_hda_is_supported_format(struct hda_codec *codec, hda_nid_t nid,
        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) {
@@ -2728,12 +2805,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;