return p;
 }
 
+/*
+ * PCM device
+ */
+static void release_pcm(struct kref *kref)
+{
+       struct hda_pcm *pcm = container_of(kref, struct hda_pcm, kref);
+
+       if (pcm->pcm)
+               snd_device_free(pcm->codec->card, pcm->pcm);
+       clear_bit(pcm->device, pcm->codec->bus->pcm_dev_bits);
+       kfree(pcm->name);
+       kfree(pcm);
+}
+
+void snd_hda_codec_pcm_put(struct hda_pcm *pcm)
+{
+       kref_put(&pcm->kref, release_pcm);
+}
+EXPORT_SYMBOL_GPL(snd_hda_codec_pcm_put);
+
+struct hda_pcm *snd_hda_codec_pcm_new(struct hda_codec *codec,
+                                     const char *fmt, ...)
+{
+       struct hda_pcm *pcm;
+       va_list args;
+
+       va_start(args, fmt);
+       pcm = kzalloc(sizeof(*pcm), GFP_KERNEL);
+       if (!pcm)
+               return NULL;
+
+       pcm->codec = codec;
+       kref_init(&pcm->kref);
+       pcm->name = kvasprintf(GFP_KERNEL, fmt, args);
+       if (!pcm->name) {
+               kfree(pcm);
+               return NULL;
+       }
+
+       list_add_tail(&pcm->list, &codec->pcm_list_head);
+       return pcm;
+}
+EXPORT_SYMBOL_GPL(snd_hda_codec_pcm_new);
+
+static void codec_release_pcms(struct hda_codec *codec)
+{
+       struct hda_pcm *pcm, *n;
+
+       list_for_each_entry_safe(pcm, n, &codec->pcm_list_head, list) {
+               list_del_init(&pcm->list);
+               snd_hda_codec_pcm_put(pcm);
+       }
+}
+
 /*
  * codec destructor
  */
        if (!codec)
                return;
        cancel_delayed_work_sync(&codec->jackpoll_work);
+       codec_release_pcms(codec);
        if (device_is_registered(hda_codec_dev(codec)))
                device_del(hda_codec_dev(codec));
        snd_hda_jack_tbl_clear(codec);
        snd_array_init(&codec->jacktbl, sizeof(struct hda_jack_tbl), 16);
        snd_array_init(&codec->verbs, sizeof(struct hda_verb *), 8);
        INIT_LIST_HEAD(&codec->conn_list);
+       INIT_LIST_HEAD(&codec->pcm_list_head);
 
        INIT_DELAYED_WORK(&codec->jackpoll_work, hda_jackpoll_work);
        codec->depop_delay = -1;
                goto err_clear;
 
        list_for_each_entry(codec, &bus->codec_list, list) {
-               int pcm;
-               for (pcm = 0; pcm < codec->num_pcms; pcm++) {
-                       struct hda_pcm *cpcm = &codec->pcm_info[pcm];
+               struct hda_pcm *cpcm;
+               list_for_each_entry(cpcm, &codec->pcm_list_head, list) {
                        if (!cpcm->pcm)
                                continue;
                        if (cpcm->pcm->streams[0].substream_opened ||
 int snd_hda_codec_reset(struct hda_codec *codec)
 {
        struct hda_bus *bus = codec->bus;
-       struct snd_card *card = codec->card;
-       int i;
 
        if (snd_hda_lock_devices(bus) < 0)
                return -EBUSY;
        cancel_delayed_work_sync(&codec->jackpoll_work);
        flush_workqueue(bus->workq);
        snd_hda_ctls_clear(codec);
-       /* release PCMs */
-       for (i = 0; i < codec->num_pcms; i++) {
-               if (codec->pcm_info[i].pcm) {
-                       snd_device_free(card, codec->pcm_info[i].pcm);
-                       clear_bit(codec->pcm_info[i].device,
-                                 bus->pcm_dev_bits);
-               }
-       }
+       codec_release_pcms(codec);
        snd_hda_detach_beep_device(codec);
        if (device_is_registered(hda_codec_dev(codec)))
                device_del(hda_codec_dev(codec));
        snd_array_free(&codec->cvt_setups);
        snd_array_free(&codec->spdif_out);
        snd_array_free(&codec->verbs);
-       codec->num_pcms = 0;
-       codec->pcm_info = NULL;
        codec->preset = NULL;
        codec->slave_dig_outs = NULL;
        codec->spdif_status_reset = 0;
 static int hda_codec_runtime_suspend(struct device *dev)
 {
        struct hda_codec *codec = dev_to_hda_codec(dev);
+       struct hda_pcm *pcm;
        unsigned int state;
-       int i;
 
        cancel_delayed_work_sync(&codec->jackpoll_work);
-       for (i = 0; i < codec->num_pcms; i++)
-               snd_pcm_suspend_all(codec->pcm_info[i].pcm);
+       list_for_each_entry(pcm, &codec->pcm_list_head, list)
+               snd_pcm_suspend_all(pcm->pcm);
        state = hda_call_codec_suspend(codec);
        if (codec->d3_stop_clk && codec->epss && (state & AC_PWRST_CLK_STOP_OK))
                clear_bit(codec->addr, &codec->bus->codec_powered);
  */
 static int add_std_chmaps(struct hda_codec *codec)
 {
-       int i, str, err;
+       struct hda_pcm *pcm;
+       int str, err;
 
-       for (i = 0; i < codec->num_pcms; i++) {
+       list_for_each_entry(pcm, &codec->pcm_list_head, list) {
                for (str = 0; str < 2; str++) {
-                       struct snd_pcm *pcm = codec->pcm_info[i].pcm;
-                       struct hda_pcm_stream *hinfo =
-                               &codec->pcm_info[i].stream[str];
+                       struct hda_pcm_stream *hinfo = &pcm->stream[str];
                        struct snd_pcm_chmap *chmap;
                        const struct snd_pcm_chmap_elem *elem;
 
-                       if (codec->pcm_info[i].own_chmap)
+                       if (pcm->own_chmap)
                                continue;
                        if (!pcm || !hinfo->substreams)
                                continue;
                        elem = hinfo->chmap ? hinfo->chmap : snd_pcm_std_chmaps;
-                       err = snd_pcm_add_chmap_ctls(pcm, str, elem,
+                       err = snd_pcm_add_chmap_ctls(pcm->pcm, str, elem,
                                                     hinfo->channels_max,
                                                     0, &chmap);
                        if (err < 0)
 /* call build_pcms ops of the given codec and set up the default parameters */
 int snd_hda_codec_parse_pcms(struct hda_codec *codec)
 {
-       unsigned int pcm;
+       struct hda_pcm *cpcm;
        int err;
 
-       if (codec->num_pcms)
+       if (!list_empty(&codec->pcm_list_head))
                return 0; /* already parsed */
 
        if (!codec->patch_ops.build_pcms)
                return err;
        }
 
-       for (pcm = 0; pcm < codec->num_pcms; pcm++) {
-               struct hda_pcm *cpcm = &codec->pcm_info[pcm];
+       list_for_each_entry(cpcm, &codec->pcm_list_head, list) {
                int stream;
 
                for (stream = 0; stream < 2; stream++) {
 
                        if (!info->substreams)
                                continue;
-                       if (snd_BUG_ON(!cpcm->name))
-                               return -EINVAL;
                        err = set_pcm_default_values(codec, info);
                        if (err < 0) {
                                codec_warn(codec,
 int snd_hda_codec_build_pcms(struct hda_codec *codec)
 {
        struct hda_bus *bus = codec->bus;
-       unsigned int pcm;
+       struct hda_pcm *cpcm;
        int dev, err;
 
        if (snd_BUG_ON(!bus->ops.attach_pcm))
        }
 
        /* attach a new PCM streams */
-       for (pcm = 0; pcm < codec->num_pcms; pcm++) {
-               struct hda_pcm *cpcm = &codec->pcm_info[pcm];
-
+       list_for_each_entry(cpcm, &codec->pcm_list_head, list) {
                if (cpcm->pcm)
                        continue; /* already attached */
                if (!cpcm->stream[0].substreams && !cpcm->stream[1].substreams)
  *
  * Create PCM information for each codec included in the bus.
  *
- * The build_pcms codec patch is requested to set up codec->num_pcms and
- * codec->pcm_info properly.  The array is referred by the top-level driver
- * to create its PCM instances.
- * The allocated codec->pcm_info should be released in codec->patch_ops.free
- * callback.
+ * The build_pcms codec patch is requested to create and assign new
+ * hda_pcm objects.  The codec is responsible to call snd_hda_codec_pcm_new()
+ * and fills the fields.  Later they are instantiated by this function.
  *
  * At least, substreams, channels_min and channels_max must be filled for
  * each stream.  substreams = 0 indicates that the stream doesn't exist.
 
 #ifndef __SOUND_HDA_CODEC_H
 #define __SOUND_HDA_CODEC_H
 
+#include <linux/kref.h>
 #include <sound/info.h>
 #include <sound/control.h>
 #include <sound/pcm.h>
        int device;             /* device number to assign */
        struct snd_pcm *pcm;    /* assigned PCM instance */
        bool own_chmap;         /* codec driver provides own channel maps */
+       /* private: */
+       struct hda_codec *codec;
+       struct kref kref;
+       struct list_head list;
 };
 
 /* codec information */
        struct hda_codec_ops patch_ops;
 
        /* PCM to create, set by patch_ops.build_pcms callback */
-       unsigned int num_pcms;
-       struct hda_pcm *pcm_info;
+       struct list_head pcm_list_head;
 
        /* codec specific info */
        void *spec;
 int snd_hda_codec_parse_pcms(struct hda_codec *codec);
 int snd_hda_codec_build_pcms(struct hda_codec *codec);
 
+__printf(2, 3)
+struct hda_pcm *snd_hda_codec_pcm_new(struct hda_codec *codec,
+                                     const char *fmt, ...);
+
+static inline void snd_hda_codec_pcm_get(struct hda_pcm *pcm)
+{
+       kref_get(&pcm->kref);
+}
+void snd_hda_codec_pcm_put(struct hda_pcm *pcm);
+
 int snd_hda_codec_prepare(struct hda_codec *codec,
                          struct hda_pcm_stream *hinfo,
                          unsigned int stream,
 
                err = snd_hda_create_dig_out_ctls(codec,
                                                  spec->multiout.dig_out_nid,
                                                  spec->multiout.dig_out_nid,
-                                                 spec->pcm_rec[1].pcm_type);
+                                                 spec->pcm_rec[1]->pcm_type);
                if (err < 0)
                        return err;
                if (!spec->no_analog) {
 int snd_hda_gen_build_pcms(struct hda_codec *codec)
 {
        struct hda_gen_spec *spec = codec->spec;
-       struct hda_pcm *info = spec->pcm_rec;
+       struct hda_pcm *info;
        const struct hda_pcm_stream *p;
        bool have_multi_adcs;
 
-       codec->num_pcms = 1;
-       codec->pcm_info = info;
-
        if (spec->no_analog)
                goto skip_analog;
 
        fill_pcm_stream_name(spec->stream_name_analog,
                             sizeof(spec->stream_name_analog),
                             " Analog", codec->chip_name);
-       info->name = spec->stream_name_analog;
+       info = snd_hda_codec_pcm_new(codec, "%s", spec->stream_name_analog);
+       if (!info)
+               return -ENOMEM;
+       spec->pcm_rec[0] = info;
 
        if (spec->multiout.num_dacs > 0) {
                p = spec->stream_analog_playback;
                fill_pcm_stream_name(spec->stream_name_digital,
                                     sizeof(spec->stream_name_digital),
                                     " Digital", codec->chip_name);
-               codec->num_pcms = 2;
+               info = snd_hda_codec_pcm_new(codec, "%s",
+                                            spec->stream_name_digital);
+               if (!info)
+                       return -ENOMEM;
                codec->slave_dig_outs = spec->multiout.slave_dig_outs;
-               info = spec->pcm_rec + 1;
-               info->name = spec->stream_name_digital;
+               spec->pcm_rec[1] = info;
                if (spec->dig_out_type)
                        info->pcm_type = spec->dig_out_type;
                else
                fill_pcm_stream_name(spec->stream_name_alt_analog,
                                     sizeof(spec->stream_name_alt_analog),
                             " Alt Analog", codec->chip_name);
-               codec->num_pcms = 3;
-               info = spec->pcm_rec + 2;
-               info->name = spec->stream_name_alt_analog;
+               info = snd_hda_codec_pcm_new(codec, "%s",
+                                            spec->stream_name_alt_analog);
+               if (!info)
+                       return -ENOMEM;
+               spec->pcm_rec[2] = info;
                if (spec->alt_dac_nid) {
                        p = spec->stream_analog_alt_playback;
                        if (!p)
 
        int const_channel_count;        /* channel count for all */
 
        /* PCM information */
-       struct hda_pcm pcm_rec[3];      /* used in build_pcms() */
+       struct hda_pcm *pcm_rec[3];     /* used in build_pcms() */
 
        /* dynamic controls, init_verbs and input_mux */
        struct auto_pin_cfg autocfg;
 
 static void print_nid_pcms(struct snd_info_buffer *buffer,
                           struct hda_codec *codec, hda_nid_t nid)
 {
-       int pcm, type;
+       int type;
        struct hda_pcm *cpcm;
-       for (pcm = 0; pcm < codec->num_pcms; pcm++) {
-               cpcm = &codec->pcm_info[pcm];
+
+       list_for_each_entry(cpcm, &codec->pcm_list_head, list) {
                for (type = 0; type < 2; type++) {
                        if (cpcm->stream[type].nid != nid || cpcm->pcm == NULL)
                                continue;
 
        unsigned int num_inputs;
        hda_nid_t shared_mic_nid;
        hda_nid_t shared_out_nid;
-       struct hda_pcm pcm_rec[5]; /* PCM information */
 
        /* chip access */
        struct mutex chipio_mutex; /* chip access mutex */
 static int ca0132_build_pcms(struct hda_codec *codec)
 {
        struct ca0132_spec *spec = codec->spec;
-       struct hda_pcm *info = spec->pcm_rec;
+       struct hda_pcm *info;
 
-       codec->pcm_info = info;
-       codec->num_pcms = 0;
-
-       info->name = "CA0132 Analog";
+       info = snd_hda_codec_pcm_new(codec, "CA0132 Analog");
+       if (!info)
+               return -ENOMEM;
        info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ca0132_pcm_analog_playback;
        info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->dacs[0];
        info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
        info->stream[SNDRV_PCM_STREAM_CAPTURE] = ca0132_pcm_analog_capture;
        info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = 1;
        info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adcs[0];
-       codec->num_pcms++;
 
-       info++;
-       info->name = "CA0132 Analog Mic-In2";
+       info = snd_hda_codec_pcm_new(codec, "CA0132 Analog Mic-In2");
+       if (!info)
+               return -ENOMEM;
        info->stream[SNDRV_PCM_STREAM_CAPTURE] = ca0132_pcm_analog_capture;
        info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = 1;
        info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adcs[1];
-       codec->num_pcms++;
 
-       info++;
-       info->name = "CA0132 What U Hear";
+       info = snd_hda_codec_pcm_new(codec, "CA0132 What U Hear");
+       if (!info)
+               return -ENOMEM;
        info->stream[SNDRV_PCM_STREAM_CAPTURE] = ca0132_pcm_analog_capture;
        info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = 1;
        info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adcs[2];
-       codec->num_pcms++;
 
        if (!spec->dig_out && !spec->dig_in)
                return 0;
 
-       info++;
-       info->name = "CA0132 Digital";
+       info = snd_hda_codec_pcm_new(codec, "CA0132 Digital");
+       if (!info)
+               return -ENOMEM;
        info->pcm_type = HDA_PCM_TYPE_SPDIF;
        if (spec->dig_out) {
                info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
                        ca0132_pcm_digital_capture;
                info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in;
        }
-       codec->num_pcms++;
 
        return 0;
 }
 
        bool non_pcm;
        bool chmap_set;         /* channel-map override by ALSA API? */
        unsigned char chmap[8]; /* ALSA API channel-map */
-       char pcm_name[8];       /* filled in build_pcm callbacks */
 #ifdef CONFIG_PROC_FS
        struct snd_info_entry *proc_entry;
 #endif
 
        int num_pins;
        struct snd_array pins; /* struct hdmi_spec_per_pin */
-       struct snd_array pcm_rec; /* struct hda_pcm */
+       struct hda_pcm *pcm_rec[16];
        unsigned int channels_max; /* max over all cvts */
 
        struct hdmi_eld temp_eld;
        ((struct hdmi_spec_per_pin *)snd_array_elem(&spec->pins, idx))
 #define get_cvt(spec, idx) \
        ((struct hdmi_spec_per_cvt  *)snd_array_elem(&spec->cvts, idx))
-#define get_pcm_rec(spec, idx) \
-       ((struct hda_pcm *)snd_array_elem(&spec->pcm_rec, idx))
+#define get_pcm_rec(spec, idx) ((spec)->pcm_rec[idx])
 
 static int pin_nid_to_pin_index(struct hda_codec *codec, hda_nid_t pin_nid)
 {
                struct hdmi_spec_per_pin *per_pin;
 
                per_pin = get_pin(spec, pin_idx);
-               sprintf(per_pin->pcm_name, "HDMI %d", pin_idx);
-               info = snd_array_new(&spec->pcm_rec);
+               info = snd_hda_codec_pcm_new(codec, "HDMI %d", pin_idx);
                if (!info)
                        return -ENOMEM;
-               info->name = per_pin->pcm_name;
+               spec->pcm_rec[pin_idx] = info;
                info->pcm_type = HDA_PCM_TYPE_HDMI;
                info->own_chmap = true;
 
                /* other pstr fields are set in open */
        }
 
-       codec->num_pcms = spec->num_pins;
-       codec->pcm_info = spec->pcm_rec.list;
-
        return 0;
 }
 
 
        /* add channel maps */
        for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
+               struct hda_pcm *pcm;
                struct snd_pcm_chmap *chmap;
                struct snd_kcontrol *kctl;
                int i;
 
-               if (!codec->pcm_info[pin_idx].pcm)
+               pcm = spec->pcm_rec[pin_idx];
+               if (!pcm || !pcm->pcm)
                        break;
-               err = snd_pcm_add_chmap_ctls(codec->pcm_info[pin_idx].pcm,
+               err = snd_pcm_add_chmap_ctls(pcm->pcm,
                                             SNDRV_PCM_STREAM_PLAYBACK,
                                             NULL, 0, pin_idx, &chmap);
                if (err < 0)
 {
        snd_array_init(&spec->pins, sizeof(struct hdmi_spec_per_pin), nums);
        snd_array_init(&spec->cvts, sizeof(struct hdmi_spec_per_cvt), nums);
-       snd_array_init(&spec->pcm_rec, sizeof(struct hda_pcm), nums);
 }
 
 static void hdmi_array_free(struct hdmi_spec *spec)
 {
        snd_array_free(&spec->pins);
        snd_array_free(&spec->cvts);
-       snd_array_free(&spec->pcm_rec);
 }
 
 static void generic_hdmi_free(struct hda_codec *codec)
        chans = get_wcaps(codec, per_cvt->cvt_nid);
        chans = get_wcaps_channels(chans);
 
-       info = snd_array_new(&spec->pcm_rec);
+       info = snd_hda_codec_pcm_new(codec, "HDMI 0");
        if (!info)
                return -ENOMEM;
-       info->name = get_pin(spec, 0)->pcm_name;
-       sprintf(info->name, "HDMI 0");
+       spec->pcm_rec[0] = info;
        info->pcm_type = HDA_PCM_TYPE_HDMI;
        pstr = &info->stream[SNDRV_PCM_STREAM_PLAYBACK];
        *pstr = spec->pcm_playback;
        if (pstr->channels_max <= 2 && chans && chans <= 16)
                pstr->channels_max = chans;
 
-       codec->num_pcms = 1;
-       codec->pcm_info = info;
-
        return 0;
 }
 
 
 {
        if (action == HDA_FIXUP_ACT_BUILD) {
                struct alc_spec *spec = codec->spec;
-               spec->gen.pcm_rec[0].stream[0].chmap = asus_pcm_2_1_chmaps;
+               spec->gen.pcm_rec[0]->stream[0].chmap = asus_pcm_2_1_chmaps;
        }
 }
 
 
 
 struct si3054_spec {
        unsigned international;
-       struct hda_pcm pcm;
 };
 
 
 
 static int si3054_build_pcms(struct hda_codec *codec)
 {
-       struct si3054_spec *spec = codec->spec;
-       struct hda_pcm *info = &spec->pcm;
-       codec->num_pcms = 1;
-       codec->pcm_info = info;
-       info->name = "Si3054 Modem";
+       struct hda_pcm *info;
+
+       info = snd_hda_codec_pcm_new(codec, "Si3054 Modem");
+       if (!info)
+               return -ENOMEM;
        info->stream[SNDRV_PCM_STREAM_PLAYBACK] = si3054_pcm;
        info->stream[SNDRV_PCM_STREAM_CAPTURE]  = si3054_pcm;
        info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = codec->mfg;
 
         * 24bit samples are used.  Until any workaround is found,
         * disable the 24bit format, so far.
         */
-       for (i = 0; i < codec->num_pcms; i++) {
-               struct hda_pcm *info = &spec->gen.pcm_rec[i];
+       for (i = 0; i < ARRAY_SIZE(spec->gen.pcm_rec); i++) {
+               struct hda_pcm *info = spec->gen.pcm_rec[i];
+               if (!info)
+                       continue;
                if (!info->stream[SNDRV_PCM_STREAM_PLAYBACK].substreams ||
                    info->pcm_type != HDA_PCM_TYPE_AUDIO)
                        continue;