]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blobdiff - sound/pci/hda/hda_codec.c
ALSA: hda - Split codec->name to vendor and chip name strings
[mirror_ubuntu-bionic-kernel.git] / sound / pci / hda / hda_codec.c
index a4e5e59521157d8598553bc989b5e40e84b19a86..77385de01744fe96fd1b062ee4343106e2a179d0 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);
@@ -613,7 +623,10 @@ static int get_codec_name(struct hda_codec *codec)
        const struct hda_vendor_id *c;
        const char *vendor = NULL;
        u16 vendor_id = codec->vendor_id >> 16;
-       char tmp[16], name[32];
+       char tmp[16];
+
+       if (codec->vendor_name)
+               goto get_chip_name;
 
        for (c = hda_vendor_ids; c->id; c++) {
                if (c->id == vendor_id) {
@@ -625,14 +638,21 @@ static int get_codec_name(struct hda_codec *codec)
                sprintf(tmp, "Generic %04x", vendor_id);
                vendor = tmp;
        }
+       codec->vendor_name = kstrdup(vendor, GFP_KERNEL);
+       if (!codec->vendor_name)
+               return -ENOMEM;
+
+ get_chip_name:
+       if (codec->chip_name)
+               return 0;
+
        if (codec->preset && codec->preset->name)
-               snprintf(name, sizeof(name), "%s %s", vendor,
-                        codec->preset->name);
-       else
-               snprintf(name, sizeof(name), "%s ID %x", vendor,
-                        codec->vendor_id & 0xffff);
-       codec->name = kstrdup(name, GFP_KERNEL);
-       if (!codec->name)
+               codec->chip_name = kstrdup(codec->preset->name, GFP_KERNEL);
+       else {
+               sprintf(tmp, "ID %x", codec->vendor_id & 0xffff);
+               codec->chip_name = kstrdup(tmp, GFP_KERNEL);
+       }
+       if (!codec->chip_name)
                return -ENOMEM;
        return 0;
 }
@@ -642,19 +662,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++) {
-               codec->function_id = snd_hda_param_read(codec, nid,
+               function_id = snd_hda_param_read(codec, nid,
                                                AC_PAR_FUNCTION_TYPE) & 0xff;
-               switch (codec->function_id) {
+               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;
@@ -836,7 +858,8 @@ static void snd_hda_codec_free(struct hda_codec *codec)
        module_put(codec->owner);
        free_hda_cache(&codec->amp_cache);
        free_hda_cache(&codec->cmd_cache);
-       kfree(codec->name);
+       kfree(codec->vendor_name);
+       kfree(codec->chip_name);
        kfree(codec->modelname);
        kfree(codec->wcaps);
        kfree(codec);
@@ -977,15 +1000,16 @@ int snd_hda_codec_configure(struct hda_codec *codec)
        int err;
 
        codec->preset = find_codec_preset(codec);
-       if (!codec->name) {
+       if (!codec->vendor_name || !codec->chip_name) {
                err = get_codec_name(codec);
                if (err < 0)
                        return err;
        }
        /* audio codec should override the mixer name */
        if (codec->afg || !*codec->bus->card->mixername)
-               strlcpy(codec->bus->card->mixername, codec->name,
-                       sizeof(codec->bus->card->mixername));
+               snprintf(codec->bus->card->mixername,
+                        sizeof(codec->bus->card->mixername),
+                        "%s %s", codec->vendor_name, codec->chip_name);
 
        if (is_generic_config(codec)) {
                err = snd_hda_parse_generic_codec(codec);
@@ -1053,6 +1077,8 @@ 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)))
 
@@ -1143,19 +1169,32 @@ 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)
+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, HDA_HASH_PINCAP_KEY(nid));
+       info = get_alloc_amp_hash(codec, key);
        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;
+               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);
 
 /*
@@ -1430,6 +1469,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);
 }
@@ -2250,7 +2291,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;
@@ -2315,7 +2360,8 @@ static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg,
                if (wcaps & AC_WCAP_POWER) {
                        unsigned int wid_type = (wcaps & AC_WCAP_TYPE) >>
                                AC_WCAP_TYPE_SHIFT;
-                       if (wid_type == AC_WID_PIN) {
+                       if (power_state == AC_PWRST_D3 &&
+                           wid_type == AC_WID_PIN) {
                                unsigned int pincap;
                                /*
                                 * don't power down the widget if it controls
@@ -2327,7 +2373,7 @@ static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg,
                                                nid, 0,
                                                AC_VERB_GET_EAPD_BTLENABLE, 0);
                                        eapd &= 0x02;
-                                       if (power_state == AC_PWRST_D3 && eapd)
+                                       if (eapd)
                                                continue;
                                }
                        }
@@ -2538,6 +2584,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
@@ -2556,15 +2637,8 @@ static int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid,
 {
        unsigned int i, val, wcaps;
 
-       val = 0;
        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;
-       }
-       if (!val)
-               val = snd_hda_param_read(codec, codec->afg, AC_PAR_PCM);
+       val = query_pcm_param(codec, nid);
 
        if (ratesp) {
                u32 rates = 0;
@@ -2586,15 +2660,9 @@ static int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid,
                u64 formats = 0;
                unsigned int streams, bps;
 
-               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) {
@@ -2668,17 +2736,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++)
@@ -2690,12 +2750,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) {