ALSA: hda - Allow unlimited pins and converters in patch_hdmi.c
authorTakashi Iwai <tiwai@suse.de>
Wed, 13 Mar 2013 13:40:31 +0000 (14:40 +0100)
committerTakashi Iwai <tiwai@suse.de>
Wed, 13 Mar 2013 17:07:04 +0000 (18:07 +0100)
Use the dynamic array allocations for pins, converters and PCM arrays
instead of the fixed size arrays.  The modern HDMI codecs get more and
more pins, and we don't know the sensitive limit.

Most of the patch are spent for the straight conversions from the
fixed array access to snd_array helpers.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
sound/pci/hda/patch_hdmi.c

index 78e1827..3e1159d 100644 (file)
@@ -44,16 +44,6 @@ static bool static_hdmi_pcm;
 module_param(static_hdmi_pcm, bool, 0644);
 MODULE_PARM_DESC(static_hdmi_pcm, "Don't restrict PCM parameters per ELD info");
 
-/*
- * The HDMI/DisplayPort configuration can be highly dynamic. A graphics device
- * could support N independent pipes, each of them can be connected to one or
- * more ports (DVI, HDMI or DisplayPort).
- *
- * The HDA correspondence of pipes/ports are converter/pin nodes.
- */
-#define MAX_HDMI_CVTS  8
-#define MAX_HDMI_PINS  8
-
 struct hdmi_spec_per_cvt {
        hda_nid_t cvt_nid;
        int assigned;
@@ -80,16 +70,17 @@ struct hdmi_spec_per_pin {
        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 */
 };
 
 struct hdmi_spec {
        int num_cvts;
-       struct hdmi_spec_per_cvt cvts[MAX_HDMI_CVTS];
-       hda_nid_t cvt_nids[MAX_HDMI_CVTS];
+       struct snd_array cvts; /* struct hdmi_spec_per_cvt */
+       hda_nid_t cvt_nids[4]; /* only for haswell fix */
 
        int num_pins;
-       struct hdmi_spec_per_pin pins[MAX_HDMI_PINS];
-       struct hda_pcm pcm_rec[MAX_HDMI_PINS];
+       struct snd_array pins; /* struct hdmi_spec_per_pin */
+       struct snd_array pcm_rec; /* struct hda_pcm */
        unsigned int channels_max; /* max over all cvts */
 
        struct hdmi_eld temp_eld;
@@ -304,12 +295,19 @@ static struct cea_channel_speaker_allocation channel_allocations[] = {
  * HDMI routines
  */
 
+#define get_pin(spec, idx) \
+       ((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))
+
 static int pin_nid_to_pin_index(struct hdmi_spec *spec, hda_nid_t pin_nid)
 {
        int pin_idx;
 
        for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++)
-               if (spec->pins[pin_idx].pin_nid == pin_nid)
+               if (get_pin(spec, pin_idx)->pin_nid == pin_nid)
                        return pin_idx;
 
        snd_printk(KERN_WARNING "HDMI: pin nid %d not registered\n", pin_nid);
@@ -322,7 +320,7 @@ static int hinfo_to_pin_index(struct hdmi_spec *spec,
        int pin_idx;
 
        for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++)
-               if (&spec->pcm_rec[pin_idx].stream[0] == hinfo)
+               if (get_pcm_rec(spec, pin_idx)->stream == hinfo)
                        return pin_idx;
 
        snd_printk(KERN_WARNING "HDMI: hinfo %p not registered\n", hinfo);
@@ -334,7 +332,7 @@ static int cvt_nid_to_cvt_index(struct hdmi_spec *spec, hda_nid_t cvt_nid)
        int cvt_idx;
 
        for (cvt_idx = 0; cvt_idx < spec->num_cvts; cvt_idx++)
-               if (spec->cvts[cvt_idx].cvt_nid == cvt_nid)
+               if (get_cvt(spec, cvt_idx)->cvt_nid == cvt_nid)
                        return cvt_idx;
 
        snd_printk(KERN_WARNING "HDMI: cvt nid %d not registered\n", cvt_nid);
@@ -352,7 +350,7 @@ static int hdmi_eld_ctl_info(struct snd_kcontrol *kcontrol,
        uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
 
        pin_idx = kcontrol->private_value;
-       eld = &spec->pins[pin_idx].sink_eld;
+       eld = &get_pin(spec, pin_idx)->sink_eld;
 
        mutex_lock(&eld->lock);
        uinfo->count = eld->eld_valid ? eld->eld_size : 0;
@@ -370,7 +368,7 @@ static int hdmi_eld_ctl_get(struct snd_kcontrol *kcontrol,
        int pin_idx;
 
        pin_idx = kcontrol->private_value;
-       eld = &spec->pins[pin_idx].sink_eld;
+       eld = &get_pin(spec, pin_idx)->sink_eld;
 
        mutex_lock(&eld->lock);
        if (eld->eld_size > ARRAY_SIZE(ucontrol->value.bytes.data)) {
@@ -410,11 +408,11 @@ static int hdmi_create_eld_ctl(struct hda_codec *codec, int pin_idx,
        kctl->private_value = pin_idx;
        kctl->id.device = device;
 
-       err = snd_hda_ctl_add(codec, spec->pins[pin_idx].pin_nid, kctl);
+       err = snd_hda_ctl_add(codec, get_pin(spec, pin_idx)->pin_nid, kctl);
        if (err < 0)
                return err;
 
-       spec->pins[pin_idx].eld_ctl = kctl;
+       get_pin(spec, pin_idx)->eld_ctl = kctl;
        return 0;
 }
 
@@ -875,14 +873,14 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, int pin_idx,
                                       struct snd_pcm_substream *substream)
 {
        struct hdmi_spec *spec = codec->spec;
-       struct hdmi_spec_per_pin *per_pin = &spec->pins[pin_idx];
+       struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
        hda_nid_t pin_nid = per_pin->pin_nid;
        int channels = substream->runtime->channels;
        struct hdmi_eld *eld;
        int ca;
        union audio_infoframe ai;
 
-       eld = &spec->pins[pin_idx].sink_eld;
+       eld = &per_pin->sink_eld;
        if (!eld->monitor_present)
                return;
 
@@ -977,7 +975,7 @@ static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res)
        if (pin_idx < 0)
                return;
 
-       hdmi_present_sense(&spec->pins[pin_idx], 1);
+       hdmi_present_sense(get_pin(spec, pin_idx), 1);
        snd_hda_jack_report_sync(codec);
 }
 
@@ -1083,12 +1081,12 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo,
        pin_idx = hinfo_to_pin_index(spec, hinfo);
        if (snd_BUG_ON(pin_idx < 0))
                return -EINVAL;
-       per_pin = &spec->pins[pin_idx];
+       per_pin = get_pin(spec, pin_idx);
        eld = &per_pin->sink_eld;
 
        /* Dynamically assign converter to stream */
        for (cvt_idx = 0; cvt_idx < spec->num_cvts; cvt_idx++) {
-               per_cvt = &spec->cvts[cvt_idx];
+               per_cvt = get_cvt(spec, cvt_idx);
 
                /* Must not already be assigned */
                if (per_cvt->assigned)
@@ -1151,7 +1149,7 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo,
 static int hdmi_read_pin_conn(struct hda_codec *codec, int pin_idx)
 {
        struct hdmi_spec *spec = codec->spec;
-       struct hdmi_spec_per_pin *per_pin = &spec->pins[pin_idx];
+       struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
        hda_nid_t pin_nid = per_pin->pin_nid;
 
        if (!(get_wcaps(codec, pin_nid) & AC_WCAP_CONN_LIST)) {
@@ -1275,14 +1273,13 @@ static int hdmi_add_pin(struct hda_codec *codec, hda_nid_t pin_nid)
        if (get_defcfg_connect(config) == AC_JACK_PORT_NONE)
                return 0;
 
-       if (snd_BUG_ON(spec->num_pins >= MAX_HDMI_PINS))
-               return -E2BIG;
-
        if (codec->vendor_id == 0x80862807)
                intel_haswell_fixup_connect_list(codec, pin_nid);
 
        pin_idx = spec->num_pins;
-       per_pin = &spec->pins[pin_idx];
+       per_pin = snd_array_new(&spec->pins);
+       if (!per_pin)
+               return -ENOMEM;
 
        per_pin->pin_nid = pin_nid;
        per_pin->non_pcm = false;
@@ -1299,19 +1296,16 @@ static int hdmi_add_pin(struct hda_codec *codec, hda_nid_t pin_nid)
 static int hdmi_add_cvt(struct hda_codec *codec, hda_nid_t cvt_nid)
 {
        struct hdmi_spec *spec = codec->spec;
-       int cvt_idx;
        struct hdmi_spec_per_cvt *per_cvt;
        unsigned int chans;
        int err;
 
-       if (snd_BUG_ON(spec->num_cvts >= MAX_HDMI_CVTS))
-               return -E2BIG;
-
        chans = get_wcaps(codec, cvt_nid);
        chans = get_wcaps_channels(chans);
 
-       cvt_idx = spec->num_cvts;
-       per_cvt = &spec->cvts[cvt_idx];
+       per_cvt = snd_array_new(&spec->cvts);
+       if (!per_cvt)
+               return -ENOMEM;
 
        per_cvt->cvt_nid = cvt_nid;
        per_cvt->channels_min = 2;
@@ -1328,7 +1322,9 @@ static int hdmi_add_cvt(struct hda_codec *codec, hda_nid_t cvt_nid)
        if (err < 0)
                return err;
 
-       spec->cvt_nids[spec->num_cvts++] = cvt_nid;
+       if (spec->num_cvts < ARRAY_SIZE(spec->cvt_nids))
+               spec->cvt_nids[spec->num_cvts] = cvt_nid;
+       spec->num_cvts++;
 
        return 0;
 }
@@ -1384,13 +1380,6 @@ static int hdmi_parse_codec(struct hda_codec *codec)
 
 /*
  */
-static char *get_hdmi_pcm_name(int idx)
-{
-       static char names[MAX_HDMI_PINS][8];
-       sprintf(&names[idx][0], "HDMI %d", idx);
-       return &names[idx][0];
-}
-
 static bool check_non_pcm_per_cvt(struct hda_codec *codec, hda_nid_t cvt_nid)
 {
        struct hda_spdif_out *spdif;
@@ -1417,7 +1406,7 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
        hda_nid_t cvt_nid = hinfo->nid;
        struct hdmi_spec *spec = codec->spec;
        int pin_idx = hinfo_to_pin_index(spec, hinfo);
-       hda_nid_t pin_nid = spec->pins[pin_idx].pin_nid;
+       hda_nid_t pin_nid = get_pin(spec, pin_idx)->pin_nid;
        bool non_pcm;
 
        non_pcm = check_non_pcm_per_cvt(codec, cvt_nid);
@@ -1450,7 +1439,7 @@ static int hdmi_pcm_close(struct hda_pcm_stream *hinfo,
                cvt_idx = cvt_nid_to_cvt_index(spec, hinfo->nid);
                if (snd_BUG_ON(cvt_idx < 0))
                        return -EINVAL;
-               per_cvt = &spec->cvts[cvt_idx];
+               per_cvt = get_cvt(spec, cvt_idx);
 
                snd_BUG_ON(!per_cvt->assigned);
                per_cvt->assigned = 0;
@@ -1459,7 +1448,7 @@ static int hdmi_pcm_close(struct hda_pcm_stream *hinfo,
                pin_idx = hinfo_to_pin_index(spec, hinfo);
                if (snd_BUG_ON(pin_idx < 0))
                        return -EINVAL;
-               per_pin = &spec->pins[pin_idx];
+               per_pin = get_pin(spec, pin_idx);
 
                snd_hda_spdif_ctls_unassign(codec, pin_idx);
                per_pin->chmap_set = false;
@@ -1553,7 +1542,7 @@ static int hdmi_chmap_ctl_get(struct snd_kcontrol *kcontrol,
        struct hda_codec *codec = info->private_data;
        struct hdmi_spec *spec = codec->spec;
        int pin_idx = kcontrol->private_value;
-       struct hdmi_spec_per_pin *per_pin = &spec->pins[pin_idx];
+       struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
        int i;
 
        for (i = 0; i < ARRAY_SIZE(per_pin->chmap); i++)
@@ -1568,7 +1557,7 @@ static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol,
        struct hda_codec *codec = info->private_data;
        struct hdmi_spec *spec = codec->spec;
        int pin_idx = kcontrol->private_value;
-       struct hdmi_spec_per_pin *per_pin = &spec->pins[pin_idx];
+       struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
        unsigned int ctl_idx;
        struct snd_pcm_substream *substream;
        unsigned char chmap[8];
@@ -1613,9 +1602,14 @@ static int generic_hdmi_build_pcms(struct hda_codec *codec)
        for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
                struct hda_pcm *info;
                struct hda_pcm_stream *pstr;
-
-               info = &spec->pcm_rec[pin_idx];
-               info->name = get_hdmi_pcm_name(pin_idx);
+               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);
+               if (!info)
+                       return -ENOMEM;
+               info->name = per_pin->pcm_name;
                info->pcm_type = HDA_PCM_TYPE_HDMI;
                info->own_chmap = true;
 
@@ -1626,7 +1620,7 @@ static int generic_hdmi_build_pcms(struct hda_codec *codec)
        }
 
        codec->num_pcms = spec->num_pins;
-       codec->pcm_info = spec->pcm_rec;
+       codec->pcm_info = spec->pcm_rec.list;
 
        return 0;
 }
@@ -1635,8 +1629,8 @@ static int generic_hdmi_build_jack(struct hda_codec *codec, int pin_idx)
 {
        char hdmi_str[32] = "HDMI/DP";
        struct hdmi_spec *spec = codec->spec;
-       struct hdmi_spec_per_pin *per_pin = &spec->pins[pin_idx];
-       int pcmdev = spec->pcm_rec[pin_idx].device;
+       struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
+       int pcmdev = get_pcm_rec(spec, pin_idx)->device;
 
        if (pcmdev > 0)
                sprintf(hdmi_str + strlen(hdmi_str), ",pcm=%d", pcmdev);
@@ -1654,7 +1648,7 @@ static int generic_hdmi_build_controls(struct hda_codec *codec)
        int pin_idx;
 
        for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
-               struct hdmi_spec_per_pin *per_pin = &spec->pins[pin_idx];
+               struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
 
                err = generic_hdmi_build_jack(codec, pin_idx);
                if (err < 0)
@@ -1669,9 +1663,8 @@ static int generic_hdmi_build_controls(struct hda_codec *codec)
                snd_hda_spdif_ctls_unassign(codec, pin_idx);
 
                /* add control for ELD Bytes */
-               err = hdmi_create_eld_ctl(codec,
-                                       pin_idx,
-                                       spec->pcm_rec[pin_idx].device);
+               err = hdmi_create_eld_ctl(codec, pin_idx,
+                                         get_pcm_rec(spec, pin_idx)->device);
 
                if (err < 0)
                        return err;
@@ -1709,7 +1702,7 @@ static int generic_hdmi_init_per_pins(struct hda_codec *codec)
        int pin_idx;
 
        for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
-               struct hdmi_spec_per_pin *per_pin = &spec->pins[pin_idx];
+               struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
                struct hdmi_eld *eld = &per_pin->sink_eld;
 
                per_pin->codec = codec;
@@ -1726,7 +1719,7 @@ static int generic_hdmi_init(struct hda_codec *codec)
        int pin_idx;
 
        for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
-               struct hdmi_spec_per_pin *per_pin = &spec->pins[pin_idx];
+               struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
                hda_nid_t pin_nid = per_pin->pin_nid;
 
                hdmi_init_pin(codec, pin_nid);
@@ -1735,13 +1728,27 @@ static int generic_hdmi_init(struct hda_codec *codec)
        return 0;
 }
 
+static void hdmi_array_init(struct hdmi_spec *spec, int nums)
+{
+       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)
 {
        struct hdmi_spec *spec = codec->spec;
        int pin_idx;
 
        for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
-               struct hdmi_spec_per_pin *per_pin = &spec->pins[pin_idx];
+               struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
                struct hdmi_eld *eld = &per_pin->sink_eld;
 
                cancel_delayed_work(&per_pin->work);
@@ -1749,6 +1756,7 @@ static void generic_hdmi_free(struct hda_codec *codec)
        }
 
        flush_workqueue(codec->bus->workq);
+       hdmi_array_free(spec);
        kfree(spec);
 }
 
@@ -1775,6 +1783,7 @@ static void intel_haswell_fixup_connect_list(struct hda_codec *codec,
 
        /* override pins connection list */
        snd_printdd("hdmi: haswell: override pin connection 0x%x\n", nid);
+       nconns = max(spec->num_cvts, 4);
        snd_hda_override_conn_list(codec, nid, spec->num_cvts, spec->cvt_nids);
 }
 
@@ -1855,6 +1864,7 @@ static int patch_generic_hdmi(struct hda_codec *codec)
                return -ENOMEM;
 
        codec->spec = spec;
+       hdmi_array_init(spec, 4);
 
        snd_hda_pick_fixup(codec, hdmi_models, hdmi_fixup_tbl, hdmi_fixups);
        snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
@@ -1882,24 +1892,30 @@ static int patch_generic_hdmi(struct hda_codec *codec)
 static int simple_playback_build_pcms(struct hda_codec *codec)
 {
        struct hdmi_spec *spec = codec->spec;
-       struct hda_pcm *info = spec->pcm_rec;
+       struct hda_pcm *info;
        unsigned int chans;
        struct hda_pcm_stream *pstr;
+       struct hdmi_spec_per_cvt *per_cvt;
 
-       codec->num_pcms = 1;
-       codec->pcm_info = info;
-
-       chans = get_wcaps(codec, spec->cvts[0].cvt_nid);
+       per_cvt = get_cvt(spec, 0);
+       chans = get_wcaps(codec, per_cvt->cvt_nid);
        chans = get_wcaps_channels(chans);
 
-       info->name = get_hdmi_pcm_name(0);
+       info = snd_array_new(&spec->pcm_rec);
+       if (!info)
+               return -ENOMEM;
+       info->name = get_pin(spec, 0)->pcm_name;
+       sprintf(info->name, "HDMI 0");
        info->pcm_type = HDA_PCM_TYPE_HDMI;
        pstr = &info->stream[SNDRV_PCM_STREAM_PLAYBACK];
        *pstr = spec->pcm_playback;
-       pstr->nid = spec->cvts[0].cvt_nid;
+       pstr->nid = per_cvt->cvt_nid;
        if (pstr->channels_max <= 2 && chans && chans <= 16)
                pstr->channels_max = chans;
 
+       codec->num_pcms = 1;
+       codec->pcm_info = info;
+
        return 0;
 }
 
@@ -1919,11 +1935,12 @@ static void simple_hdmi_unsol_event(struct hda_codec *codec,
 static int simple_playback_build_controls(struct hda_codec *codec)
 {
        struct hdmi_spec *spec = codec->spec;
+       struct hdmi_spec_per_cvt *per_cvt;
        int err;
 
-       err = snd_hda_create_spdif_out_ctls(codec,
-                                           spec->cvts[0].cvt_nid,
-                                           spec->cvts[0].cvt_nid);
+       per_cvt = get_cvt(spec, 0);
+       err = snd_hda_create_spdif_out_ctls(codec, per_cvt->cvt_nid,
+                                           per_cvt->cvt_nid);
        if (err < 0)
                return err;
        return simple_hdmi_build_jack(codec, 0);
@@ -1932,7 +1949,8 @@ static int simple_playback_build_controls(struct hda_codec *codec)
 static int simple_playback_init(struct hda_codec *codec)
 {
        struct hdmi_spec *spec = codec->spec;
-       hda_nid_t pin = spec->pins[0].pin_nid;
+       struct hdmi_spec_per_pin *per_pin = get_pin(spec, 0);
+       hda_nid_t pin = per_pin->pin_nid;
 
        snd_hda_codec_write(codec, pin, 0,
                            AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
@@ -1948,6 +1966,7 @@ static void simple_playback_free(struct hda_codec *codec)
 {
        struct hdmi_spec *spec = codec->spec;
 
+       hdmi_array_free(spec);
        kfree(spec);
 }
 
@@ -2111,20 +2130,29 @@ static int patch_simple_hdmi(struct hda_codec *codec,
                             hda_nid_t cvt_nid, hda_nid_t pin_nid)
 {
        struct hdmi_spec *spec;
+       struct hdmi_spec_per_cvt *per_cvt;
+       struct hdmi_spec_per_pin *per_pin;
 
        spec = kzalloc(sizeof(*spec), GFP_KERNEL);
        if (!spec)
                return -ENOMEM;
 
        codec->spec = spec;
+       hdmi_array_init(spec, 1);
 
        spec->multiout.num_dacs = 0;  /* no analog */
        spec->multiout.max_channels = 2;
        spec->multiout.dig_out_nid = cvt_nid;
        spec->num_cvts = 1;
        spec->num_pins = 1;
-       spec->cvts[0].cvt_nid = cvt_nid;
-       spec->pins[0].pin_nid = pin_nid;
+       per_pin = snd_array_new(&spec->pins);
+       per_cvt = snd_array_new(&spec->cvts);
+       if (!per_pin || !per_cvt) {
+               simple_playback_free(codec);
+               return -ENOMEM;
+       }
+       per_cvt->cvt_nid = cvt_nid;
+       per_pin->pin_nid = pin_nid;
        spec->pcm_playback = simple_pcm_playback;
 
        codec->patch_ops = simple_hdmi_patch_ops;
@@ -2201,9 +2229,11 @@ static int nvhdmi_8ch_7x_pcm_prepare(struct hda_pcm_stream *hinfo,
        int i;
        struct hdmi_spec *spec = codec->spec;
        struct hda_spdif_out *spdif;
+       struct hdmi_spec_per_cvt *per_cvt;
 
        mutex_lock(&codec->spdif_mutex);
-       spdif = snd_hda_spdif_out_of_nid(codec, spec->cvts[0].cvt_nid);
+       per_cvt = get_cvt(spec, 0);
+       spdif = snd_hda_spdif_out_of_nid(codec, per_cvt->cvt_nid);
 
        chs = substream->runtime->channels;
 
@@ -2325,13 +2355,17 @@ static int nvhdmi_7x_8ch_build_pcms(struct hda_codec *codec)
 {
        struct hdmi_spec *spec = codec->spec;
        int err = simple_playback_build_pcms(codec);
-       spec->pcm_rec[0].own_chmap = true;
+       if (!err) {
+               struct hda_pcm *info = get_pcm_rec(spec, 0);
+               info->own_chmap = true;
+       }
        return err;
 }
 
 static int nvhdmi_7x_8ch_build_controls(struct hda_codec *codec)
 {
        struct hdmi_spec *spec = codec->spec;
+       struct hda_pcm *info;
        struct snd_pcm_chmap *chmap;
        int err;
 
@@ -2340,7 +2374,8 @@ static int nvhdmi_7x_8ch_build_controls(struct hda_codec *codec)
                return err;
 
        /* add channel maps */
-       err = snd_pcm_add_chmap_ctls(spec->pcm_rec[0].pcm,
+       info = get_pcm_rec(spec, 0);
+       err = snd_pcm_add_chmap_ctls(info->pcm,
                                     SNDRV_PCM_STREAM_PLAYBACK,
                                     snd_pcm_alt_chmaps, 8, 0, &chmap);
        if (err < 0)
@@ -2395,6 +2430,7 @@ static int atihdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
                                        struct snd_pcm_substream *substream)
 {
        struct hdmi_spec *spec = codec->spec;
+       struct hdmi_spec_per_cvt *per_cvt = get_cvt(spec, 0);
        int chans = substream->runtime->channels;
        int i, err;
 
@@ -2402,11 +2438,11 @@ static int atihdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
                                          substream);
        if (err < 0)
                return err;
-       snd_hda_codec_write(codec, spec->cvts[0].cvt_nid, 0,
+       snd_hda_codec_write(codec, per_cvt->cvt_nid, 0,
                            AC_VERB_SET_CVT_CHAN_COUNT, chans - 1);
        /* FIXME: XXX */
        for (i = 0; i < chans; i++) {
-               snd_hda_codec_write(codec, spec->cvts[0].cvt_nid, 0,
+               snd_hda_codec_write(codec, per_cvt->cvt_nid, 0,
                                    AC_VERB_SET_HDMI_CHAN_SLOT,
                                    (i << 4) | i);
        }