diff --git a/include/sound/hdmi_chmap.h b/include/sound/hdmi_chmap.h
index 32087f1..c7702ef9 100644
--- a/include/sound/hdmi_chmap.h
+++ b/include/sound/hdmi_chmap.h
@@ -30,6 +30,12 @@ struct hdmi_chmap_ops {
 
 	/* check that the user-given chmap is supported */
 	int (*chmap_validate)(int ca, int channels, unsigned char *chmap);
+
+	void (*get_chmap)(struct hdac_device *hdac, int pcm_idx,
+					unsigned char *chmap);
+	void (*set_chmap)(struct hdac_device *hdac, int pcm_idx,
+			unsigned char *chmap, int prepared);
+	bool (*is_monitor_connected)(struct hdac_device *hdac, int pcm_idx);
 };
 
 struct hdmi_chmap {
diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c
index fd5ae6f..ed51d0d 100644
--- a/sound/pci/hda/patch_hdmi.c
+++ b/sound/pci/hda/patch_hdmi.c
@@ -100,8 +100,6 @@ struct hdmi_spec_per_pin {
 #endif
 };
 
-struct cea_channel_speaker_allocation;
-
 /* operations used by generic code that can be overridden by patches */
 struct hdmi_ops {
 	int (*pin_get_eld)(struct hda_codec *codec, hda_nid_t pin_nid,
@@ -2350,9 +2348,7 @@ static int hdmi_chmap_ctl_info(struct snd_kcontrol *kcontrol,
 			       struct snd_ctl_elem_info *uinfo)
 {
 	struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
-	struct hda_codec *codec = info->private_data;
-	struct hdmi_spec *spec = codec->spec;
-	struct hdmi_chmap *chmap = &spec->chmap;
+	struct hdmi_chmap *chmap = info->private_data;
 
 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
 	uinfo->count = chmap->channels_max;
@@ -2389,13 +2385,49 @@ static void hdmi_cea_alloc_to_tlv_chmap(struct cea_channel_speaker_allocation *c
 	WARN_ON(count != channels);
 }
 
+static void hdmi_get_chmap(struct hdac_device *hdac, int pcm_idx,
+					unsigned char *chmap)
+{
+	struct hda_codec *codec = container_of(hdac, struct hda_codec, core);
+	struct hdmi_spec *spec = codec->spec;
+	struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx);
+
+	/* chmap is already set to 0 in caller */
+	if (!per_pin)
+		return;
+
+	memcpy(chmap, per_pin->chmap, ARRAY_SIZE(per_pin->chmap));
+}
+
+static void hdmi_set_chmap(struct hdac_device *hdac, int pcm_idx,
+				unsigned char *chmap, int prepared)
+{
+	struct hda_codec *codec = container_of(hdac, struct hda_codec, core);
+	struct hdmi_spec *spec = codec->spec;
+	struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx);
+
+	mutex_lock(&per_pin->lock);
+	per_pin->chmap_set = true;
+	memcpy(per_pin->chmap, chmap, ARRAY_SIZE(per_pin->chmap));
+	if (prepared)
+		hdmi_setup_audio_infoframe(codec, per_pin, per_pin->non_pcm);
+	mutex_unlock(&per_pin->lock);
+}
+
+static bool is_hdmi_monitor_connected(struct hdac_device *hdac, int pcm_idx)
+{
+	struct hda_codec *codec = container_of(hdac, struct hda_codec, core);
+	struct hdmi_spec *spec = codec->spec;
+	struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx);
+
+	return per_pin ? true:false;
+}
+
 static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag,
 			      unsigned int size, unsigned int __user *tlv)
 {
 	struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
-	struct hda_codec *codec = info->private_data;
-	struct hdmi_spec *spec = codec->spec;
-	struct hdmi_chmap *chmap = &spec->chmap;
+	struct hdmi_chmap *chmap = info->private_data;
 	unsigned int __user *dst;
 	int chs, count = 0;
 
@@ -2444,21 +2476,17 @@ static int hdmi_chmap_ctl_get(struct snd_kcontrol *kcontrol,
 			      struct snd_ctl_elem_value *ucontrol)
 {
 	struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
-	struct hda_codec *codec = info->private_data;
-	struct hdmi_spec *spec = codec->spec;
-	struct hdmi_chmap *chmap = &spec->chmap;
+	struct hdmi_chmap *chmap = info->private_data;
 	int pcm_idx = kcontrol->private_value;
-	struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx);
+	unsigned char pcm_chmap[8];
 	int i;
 
-	if (!per_pin) {
-		for (i = 0; i < chmap->channels_max; i++)
-			ucontrol->value.integer.value[i] = 0;
-		return 0;
-	}
+	memset(pcm_chmap, 0, sizeof(pcm_chmap));
+	chmap->ops.get_chmap(chmap->hdac, pcm_idx, pcm_chmap);
+
+	for (i = 0; i < sizeof(chmap); i++)
+		ucontrol->value.integer.value[i] = pcm_chmap[i];
 
-	for (i = 0; i < ARRAY_SIZE(per_pin->chmap); i++)
-		ucontrol->value.integer.value[i] = per_pin->chmap[i];
 	return 0;
 }
 
@@ -2466,20 +2494,17 @@ static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol,
 			      struct snd_ctl_elem_value *ucontrol)
 {
 	struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
-	struct hda_codec *codec = info->private_data;
-	struct hdmi_spec *spec = codec->spec;
-	struct hdmi_chmap *hchmap = &spec->chmap;
+	struct hdmi_chmap *hchmap = info->private_data;
 	int pcm_idx = kcontrol->private_value;
-	struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx);
 	unsigned int ctl_idx;
 	struct snd_pcm_substream *substream;
-	unsigned char chmap[8];
+	unsigned char chmap[8], per_pin_chmap[8];
 	int i, err, ca, prepared = 0;
 
 	/* No monitor is connected in dyn_pcm_assign.
 	 * It's invalid to setup the chmap
 	 */
-	if (!per_pin)
+	if (!hchmap->ops.is_monitor_connected(hchmap->hdac, pcm_idx))
 		return 0;
 
 	ctl_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
@@ -2499,7 +2524,9 @@ static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol,
 	memset(chmap, 0, sizeof(chmap));
 	for (i = 0; i < ARRAY_SIZE(chmap); i++)
 		chmap[i] = ucontrol->value.integer.value[i];
-	if (!memcmp(chmap, per_pin->chmap, sizeof(chmap)))
+
+	hchmap->ops.get_chmap(hchmap->hdac, pcm_idx, per_pin_chmap);
+	if (!memcmp(chmap, per_pin_chmap, sizeof(chmap)))
 		return 0;
 	ca = hdmi_manual_channel_allocation(ARRAY_SIZE(chmap), chmap);
 	if (ca < 0)
@@ -2509,12 +2536,8 @@ static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol,
 		if (err)
 			return err;
 	}
-	mutex_lock(&per_pin->lock);
-	per_pin->chmap_set = true;
-	memcpy(per_pin->chmap, chmap, sizeof(chmap));
-	if (prepared)
-		hdmi_setup_audio_infoframe(codec, per_pin, per_pin->non_pcm);
-	mutex_unlock(&per_pin->lock);
+
+	hchmap->ops.set_chmap(hchmap->hdac, pcm_idx, chmap, prepared);
 
 	return 0;
 }
@@ -2672,7 +2695,7 @@ static int generic_hdmi_build_controls(struct hda_codec *codec)
 		if (err < 0)
 			return err;
 		/* override handlers */
-		chmap->private_data = codec;
+		chmap->private_data = &spec->chmap;
 		kctl = chmap->kctl;
 		for (i = 0; i < kctl->count; i++)
 			kctl->vd[i].access |= SNDRV_CTL_ELEM_ACCESS_WRITE;
@@ -2801,9 +2824,11 @@ static const struct hdmi_ops generic_standard_hdmi_ops = {
 static const struct hdmi_chmap_ops chmap_ops = {
 	.chmap_cea_alloc_validate_get_type	= hdmi_chmap_cea_alloc_validate_get_type,
 	.cea_alloc_to_tlv_chmap			= hdmi_cea_alloc_to_tlv_chmap,
+	.get_chmap				= hdmi_get_chmap,
+	.set_chmap				= hdmi_set_chmap,
+	.is_monitor_connected			= is_hdmi_monitor_connected,
 };
 
-
 static void intel_haswell_fixup_connect_list(struct hda_codec *codec,
 					     hda_nid_t nid)
 {
@@ -3526,6 +3551,9 @@ static int patch_nvhdmi(struct hda_codec *codec)
 	spec->chmap.ops.chmap_cea_alloc_validate_get_type =
 		nvhdmi_chmap_cea_alloc_validate_get_type;
 	spec->chmap.ops.chmap_validate = nvhdmi_chmap_validate;
+	spec->chmap.ops.get_chmap = hdmi_get_chmap;
+	spec->chmap.ops.set_chmap = hdmi_set_chmap;
+	spec->chmap.ops.is_monitor_connected = is_hdmi_monitor_connected;
 
 	return 0;
 }
@@ -4039,6 +4067,10 @@ static int patch_atihdmi(struct hda_codec *codec)
 		spec->chmap.ops.cea_alloc_to_tlv_chmap =
 				atihdmi_paired_cea_alloc_to_tlv_chmap;
 		spec->chmap.ops.chmap_validate = atihdmi_paired_chmap_validate;
+		spec->chmap.ops.get_chmap = hdmi_get_chmap;
+		spec->chmap.ops.set_chmap = hdmi_set_chmap;
+		spec->chmap.ops.is_monitor_connected =
+				is_hdmi_monitor_connected;
 	}
 
 	/* ATI/AMD converters do not advertise all of their capabilities */
