diff options
Diffstat (limited to 'target/linux/bcm27xx/patches-5.4/950-0920-vc4-Report-channel-mapping-back-to-userspace.patch')
-rw-r--r-- | target/linux/bcm27xx/patches-5.4/950-0920-vc4-Report-channel-mapping-back-to-userspace.patch | 504 |
1 files changed, 0 insertions, 504 deletions
diff --git a/target/linux/bcm27xx/patches-5.4/950-0920-vc4-Report-channel-mapping-back-to-userspace.patch b/target/linux/bcm27xx/patches-5.4/950-0920-vc4-Report-channel-mapping-back-to-userspace.patch deleted file mode 100644 index 8c4e1012bf..0000000000 --- a/target/linux/bcm27xx/patches-5.4/950-0920-vc4-Report-channel-mapping-back-to-userspace.patch +++ /dev/null @@ -1,504 +0,0 @@ -From 058328afcdd3d5c9531cfac8b980d0c0db75856f Mon Sep 17 00:00:00 2001 -From: popcornmix <popcornmix@gmail.com> -Date: Mon, 20 Apr 2020 18:00:38 +0100 -Subject: [PATCH] vc4: Report channel mapping back to userspace - -This follows logic in hdmi-codec.c to use speaker layout -from ELD to choose a suitable speaker mapping based on -number of channels requested and signal that in audio -infoframe and report this back to userspace. - -This allows apps like speaker-test and kodi to get the -output to the right speakers. - -Signed-off-by: Dom Cobley <popcornmix@gmail.com> ---- - drivers/gpu/drm/vc4/vc4_hdmi.c | 415 +++++++++++++++++++++++++++++++++ - drivers/gpu/drm/vc4/vc4_hdmi.h | 3 + - 2 files changed, 418 insertions(+) - ---- a/drivers/gpu/drm/vc4/vc4_hdmi.c -+++ b/drivers/gpu/drm/vc4/vc4_hdmi.c -@@ -48,6 +48,7 @@ - #include <sound/pcm_drm_eld.h> - #include <sound/pcm_params.h> - #include <sound/soc.h> -+#include <sound/tlv.h> - #include "media/cec.h" - #include "vc4_drv.h" - #include "vc4_hdmi.h" -@@ -82,6 +83,311 @@ - #define CEC_CLOCK_FREQ 40000 - #define VC4_HSM_CLOCK 163682864 - -+#define HDMI_CODEC_CHMAP_IDX_UNKNOWN -1 -+ -+/* -+ * CEA speaker placement for HDMI 1.4: -+ * -+ * FL FLC FC FRC FR FRW -+ * -+ * LFE -+ * -+ * RL RLC RC RRC RR -+ * -+ * Speaker placement has to be extended to support HDMI 2.0 -+ */ -+enum hdmi_codec_cea_spk_placement { -+ FL = BIT(0), /* Front Left */ -+ FC = BIT(1), /* Front Center */ -+ FR = BIT(2), /* Front Right */ -+ FLC = BIT(3), /* Front Left Center */ -+ FRC = BIT(4), /* Front Right Center */ -+ RL = BIT(5), /* Rear Left */ -+ RC = BIT(6), /* Rear Center */ -+ RR = BIT(7), /* Rear Right */ -+ RLC = BIT(8), /* Rear Left Center */ -+ RRC = BIT(9), /* Rear Right Center */ -+ LFE = BIT(10), /* Low Frequency Effect */ -+}; -+ -+/* -+ * cea Speaker allocation structure -+ */ -+struct hdmi_codec_cea_spk_alloc { -+ const int ca_id; -+ unsigned int n_ch; -+ unsigned long mask; -+}; -+ -+/* Channel maps stereo HDMI */ -+static const struct snd_pcm_chmap_elem hdmi_codec_stereo_chmaps[] = { -+ { .channels = 2, -+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR } }, -+ { } -+}; -+ -+/* Channel maps for multi-channel playbacks, up to 8 n_ch */ -+static const struct snd_pcm_chmap_elem hdmi_codec_8ch_chmaps[] = { -+ { .channels = 2, /* CA_ID 0x00 */ -+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR } }, -+ { .channels = 4, /* CA_ID 0x01 */ -+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, -+ SNDRV_CHMAP_NA } }, -+ { .channels = 4, /* CA_ID 0x02 */ -+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, -+ SNDRV_CHMAP_FC } }, -+ { .channels = 4, /* CA_ID 0x03 */ -+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, -+ SNDRV_CHMAP_FC } }, -+ { .channels = 6, /* CA_ID 0x04 */ -+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, -+ SNDRV_CHMAP_NA, SNDRV_CHMAP_RC, SNDRV_CHMAP_NA } }, -+ { .channels = 6, /* CA_ID 0x05 */ -+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, -+ SNDRV_CHMAP_NA, SNDRV_CHMAP_RC, SNDRV_CHMAP_NA } }, -+ { .channels = 6, /* CA_ID 0x06 */ -+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, -+ SNDRV_CHMAP_FC, SNDRV_CHMAP_RC, SNDRV_CHMAP_NA } }, -+ { .channels = 6, /* CA_ID 0x07 */ -+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, -+ SNDRV_CHMAP_FC, SNDRV_CHMAP_RC, SNDRV_CHMAP_NA } }, -+ { .channels = 6, /* CA_ID 0x08 */ -+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, -+ SNDRV_CHMAP_NA, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } }, -+ { .channels = 6, /* CA_ID 0x09 */ -+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, -+ SNDRV_CHMAP_NA, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } }, -+ { .channels = 6, /* CA_ID 0x0A */ -+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, -+ SNDRV_CHMAP_FC, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } }, -+ { .channels = 6, /* CA_ID 0x0B */ -+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, -+ SNDRV_CHMAP_FC, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } }, -+ { .channels = 8, /* CA_ID 0x0C */ -+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, -+ SNDRV_CHMAP_NA, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR, -+ SNDRV_CHMAP_RC, SNDRV_CHMAP_NA } }, -+ { .channels = 8, /* CA_ID 0x0D */ -+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, -+ SNDRV_CHMAP_NA, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR, -+ SNDRV_CHMAP_RC, SNDRV_CHMAP_NA } }, -+ { .channels = 8, /* CA_ID 0x0E */ -+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, -+ SNDRV_CHMAP_FC, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR, -+ SNDRV_CHMAP_RC, SNDRV_CHMAP_NA } }, -+ { .channels = 8, /* CA_ID 0x0F */ -+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, -+ SNDRV_CHMAP_FC, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR, -+ SNDRV_CHMAP_RC, SNDRV_CHMAP_NA } }, -+ { .channels = 8, /* CA_ID 0x10 */ -+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, -+ SNDRV_CHMAP_NA, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR, -+ SNDRV_CHMAP_RLC, SNDRV_CHMAP_RRC } }, -+ { .channels = 8, /* CA_ID 0x11 */ -+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, -+ SNDRV_CHMAP_NA, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR, -+ SNDRV_CHMAP_RLC, SNDRV_CHMAP_RRC } }, -+ { .channels = 8, /* CA_ID 0x12 */ -+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, -+ SNDRV_CHMAP_FC, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR, -+ SNDRV_CHMAP_RLC, SNDRV_CHMAP_RRC } }, -+ { .channels = 8, /* CA_ID 0x13 */ -+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, -+ SNDRV_CHMAP_FC, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR, -+ SNDRV_CHMAP_RLC, SNDRV_CHMAP_RRC } }, -+ { .channels = 8, /* CA_ID 0x14 */ -+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, -+ SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, -+ SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } }, -+ { .channels = 8, /* CA_ID 0x15 */ -+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, -+ SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, -+ SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } }, -+ { .channels = 8, /* CA_ID 0x16 */ -+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, -+ SNDRV_CHMAP_FC, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, -+ SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } }, -+ { .channels = 8, /* CA_ID 0x17 */ -+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, -+ SNDRV_CHMAP_FC, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, -+ SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } }, -+ { .channels = 8, /* CA_ID 0x18 */ -+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, -+ SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, -+ SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } }, -+ { .channels = 8, /* CA_ID 0x19 */ -+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, -+ SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, -+ SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } }, -+ { .channels = 8, /* CA_ID 0x1A */ -+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, -+ SNDRV_CHMAP_FC, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, -+ SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } }, -+ { .channels = 8, /* CA_ID 0x1B */ -+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, -+ SNDRV_CHMAP_FC, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, -+ SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } }, -+ { .channels = 8, /* CA_ID 0x1C */ -+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, -+ SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, -+ SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } }, -+ { .channels = 8, /* CA_ID 0x1D */ -+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, -+ SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, -+ SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } }, -+ { .channels = 8, /* CA_ID 0x1E */ -+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, -+ SNDRV_CHMAP_FC, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, -+ SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } }, -+ { .channels = 8, /* CA_ID 0x1F */ -+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, -+ SNDRV_CHMAP_FC, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, -+ SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } }, -+ { } -+}; -+ -+/* -+ * hdmi_codec_channel_alloc: speaker configuration available for CEA -+ * -+ * This is an ordered list that must match with hdmi_codec_8ch_chmaps struct -+ * The preceding ones have better chances to be selected by -+ * hdmi_codec_get_ch_alloc_table_idx(). -+ */ -+static const struct hdmi_codec_cea_spk_alloc hdmi_codec_channel_alloc[] = { -+ { .ca_id = 0x00, .n_ch = 2, -+ .mask = FL | FR}, -+ /* 2.1 */ -+ { .ca_id = 0x01, .n_ch = 4, -+ .mask = FL | FR | LFE}, -+ /* Dolby Surround */ -+ { .ca_id = 0x02, .n_ch = 4, -+ .mask = FL | FR | FC }, -+ /* surround51 */ -+ { .ca_id = 0x0b, .n_ch = 6, -+ .mask = FL | FR | LFE | FC | RL | RR}, -+ /* surround40 */ -+ { .ca_id = 0x08, .n_ch = 6, -+ .mask = FL | FR | RL | RR }, -+ /* surround41 */ -+ { .ca_id = 0x09, .n_ch = 6, -+ .mask = FL | FR | LFE | RL | RR }, -+ /* surround50 */ -+ { .ca_id = 0x0a, .n_ch = 6, -+ .mask = FL | FR | FC | RL | RR }, -+ /* 6.1 */ -+ { .ca_id = 0x0f, .n_ch = 8, -+ .mask = FL | FR | LFE | FC | RL | RR | RC }, -+ /* surround71 */ -+ { .ca_id = 0x13, .n_ch = 8, -+ .mask = FL | FR | LFE | FC | RL | RR | RLC | RRC }, -+ /* others */ -+ { .ca_id = 0x03, .n_ch = 8, -+ .mask = FL | FR | LFE | FC }, -+ { .ca_id = 0x04, .n_ch = 8, -+ .mask = FL | FR | RC}, -+ { .ca_id = 0x05, .n_ch = 8, -+ .mask = FL | FR | LFE | RC }, -+ { .ca_id = 0x06, .n_ch = 8, -+ .mask = FL | FR | FC | RC }, -+ { .ca_id = 0x07, .n_ch = 8, -+ .mask = FL | FR | LFE | FC | RC }, -+ { .ca_id = 0x0c, .n_ch = 8, -+ .mask = FL | FR | RC | RL | RR }, -+ { .ca_id = 0x0d, .n_ch = 8, -+ .mask = FL | FR | LFE | RL | RR | RC }, -+ { .ca_id = 0x0e, .n_ch = 8, -+ .mask = FL | FR | FC | RL | RR | RC }, -+ { .ca_id = 0x10, .n_ch = 8, -+ .mask = FL | FR | RL | RR | RLC | RRC }, -+ { .ca_id = 0x11, .n_ch = 8, -+ .mask = FL | FR | LFE | RL | RR | RLC | RRC }, -+ { .ca_id = 0x12, .n_ch = 8, -+ .mask = FL | FR | FC | RL | RR | RLC | RRC }, -+ { .ca_id = 0x14, .n_ch = 8, -+ .mask = FL | FR | FLC | FRC }, -+ { .ca_id = 0x15, .n_ch = 8, -+ .mask = FL | FR | LFE | FLC | FRC }, -+ { .ca_id = 0x16, .n_ch = 8, -+ .mask = FL | FR | FC | FLC | FRC }, -+ { .ca_id = 0x17, .n_ch = 8, -+ .mask = FL | FR | LFE | FC | FLC | FRC }, -+ { .ca_id = 0x18, .n_ch = 8, -+ .mask = FL | FR | RC | FLC | FRC }, -+ { .ca_id = 0x19, .n_ch = 8, -+ .mask = FL | FR | LFE | RC | FLC | FRC }, -+ { .ca_id = 0x1a, .n_ch = 8, -+ .mask = FL | FR | RC | FC | FLC | FRC }, -+ { .ca_id = 0x1b, .n_ch = 8, -+ .mask = FL | FR | LFE | RC | FC | FLC | FRC }, -+ { .ca_id = 0x1c, .n_ch = 8, -+ .mask = FL | FR | RL | RR | FLC | FRC }, -+ { .ca_id = 0x1d, .n_ch = 8, -+ .mask = FL | FR | LFE | RL | RR | FLC | FRC }, -+ { .ca_id = 0x1e, .n_ch = 8, -+ .mask = FL | FR | FC | RL | RR | FLC | FRC }, -+ { .ca_id = 0x1f, .n_ch = 8, -+ .mask = FL | FR | LFE | FC | RL | RR | FLC | FRC }, -+}; -+ -+static unsigned long hdmi_codec_spk_mask_from_alloc(int spk_alloc) -+{ -+ int i; -+ static const unsigned long hdmi_codec_eld_spk_alloc_bits[] = { -+ [0] = FL | FR, [1] = LFE, [2] = FC, [3] = RL | RR, -+ [4] = RC, [5] = FLC | FRC, [6] = RLC | RRC, -+ }; -+ unsigned long spk_mask = 0; -+ -+ for (i = 0; i < ARRAY_SIZE(hdmi_codec_eld_spk_alloc_bits); i++) { -+ if (spk_alloc & (1 << i)) -+ spk_mask |= hdmi_codec_eld_spk_alloc_bits[i]; -+ } -+ -+ return spk_mask; -+} -+ -+static int hdmi_codec_get_ch_alloc_table_idx(struct vc4_hdmi *vc4_hdmi, -+ unsigned char channels) -+{ -+ struct drm_connector *connector = &vc4_hdmi->connector; -+ int i; -+ u8 spk_alloc; -+ unsigned long spk_mask; -+ const struct hdmi_codec_cea_spk_alloc *cap = hdmi_codec_channel_alloc; -+ -+ spk_alloc = drm_eld_get_spk_alloc(connector->eld); -+ spk_mask = hdmi_codec_spk_mask_from_alloc(spk_alloc); -+ -+ for (i = 0; i < ARRAY_SIZE(hdmi_codec_channel_alloc); i++, cap++) { -+ /* If spk_alloc == 0, HDMI is unplugged return stereo config*/ -+ if (!spk_alloc && cap->ca_id == 0) -+ return i; -+ if (cap->n_ch != channels) -+ continue; -+ if (!(cap->mask == (spk_mask & cap->mask))) -+ continue; -+ return i; -+ } -+ -+ return -EINVAL; -+} -+ -+static void hdmi_codec_eld_chmap(struct vc4_hdmi *vc4_hdmi) -+{ -+ struct drm_connector *connector = &vc4_hdmi->connector; -+ u8 spk_alloc; -+ unsigned long spk_mask; -+ -+ spk_alloc = drm_eld_get_spk_alloc(connector->eld); -+ spk_mask = hdmi_codec_spk_mask_from_alloc(spk_alloc); -+ -+ /* Detect if only stereo supported, else return 8 channels mappings */ -+ if ((spk_mask & ~(FL | FR))) -+ vc4_hdmi->audio.chmap = hdmi_codec_8ch_chmaps; -+ else -+ vc4_hdmi->audio.chmap = hdmi_codec_stereo_chmaps; -+} -+ - static int vc4_hdmi_debugfs_regs(struct seq_file *m, void *unused) - { - struct drm_info_node *node = (struct drm_info_node *)m->private; -@@ -350,6 +656,9 @@ static void vc4_hdmi_set_audio_infoframe - frame.audio.sample_size = HDMI_AUDIO_SAMPLE_SIZE_STREAM; - frame.audio.channels = vc4_hdmi->audio.channels; - -+ /* Select a channel allocation that matches with ELD and pcm channels */ -+ frame.audio.channel_allocation = vc4_hdmi->audio.chmap_idx; -+ - vc4_hdmi_write_infoframe(encoder, &frame); - } - -@@ -881,6 +1190,10 @@ static int vc4_hdmi_audio_startup(struct - if (ret) - return ret; - -+ /* Select chmap supported */ -+ vc4_hdmi->audio.max_channels = 8; -+ hdmi_codec_eld_chmap(vc4_hdmi); -+ - return 0; - } - -@@ -967,6 +1280,7 @@ static int vc4_hdmi_audio_prepare(struct - u32 channel_map; - u32 mai_audio_format; - u32 mai_sample_rate; -+ int idx; - - if (substream != vc4_hdmi->audio.substream) - return -EINVAL; -@@ -1027,6 +1341,14 @@ static int vc4_hdmi_audio_prepare(struct - HDMI_WRITE(HDMI_AUDIO_PACKET_CONFIG, audio_packet_config); - vc4_hdmi_set_n_cts(vc4_hdmi); - -+ idx = hdmi_codec_get_ch_alloc_table_idx(vc4_hdmi, vc4_hdmi->audio.channels); -+ if (idx < 0) { -+ DRM_ERROR("Not able to map channels to speakers (%d)\n", idx); -+ vc4_hdmi->audio.chmap_idx = HDMI_CODEC_CHMAP_IDX_UNKNOWN; -+ } else { -+ vc4_hdmi->audio.chmap_idx = hdmi_codec_channel_alloc[idx].ca_id; -+ } -+ - return 0; - } - -@@ -1145,6 +1467,89 @@ static int vc4_spdif_mask_get(struct snd - return 0; - } - -+/* -+ * ALSA API channel-map control callbacks -+ */ -+static int vc4_chmap_ctl_info(struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_info *uinfo) -+{ -+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); -+ struct vc4_hdmi *vc4_hdmi = snd_component_to_hdmi(component); -+ -+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; -+ uinfo->count = vc4_hdmi->audio.max_channels; -+ uinfo->value.integer.min = 0; -+ uinfo->value.integer.max = SNDRV_CHMAP_LAST; -+ -+ return 0; -+} -+ -+static int vc4_chmap_ctl_get(struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_value *ucontrol) -+{ -+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); -+ struct vc4_hdmi *vc4_hdmi = snd_component_to_hdmi(component); -+ unsigned const char *map; -+ unsigned int i; -+ -+ if (!vc4_hdmi->audio.chmap) -+ return -EINVAL; -+ -+ map = vc4_hdmi->audio.chmap[vc4_hdmi->audio.chmap_idx].map; -+ -+ for (i = 0; i < vc4_hdmi->audio.max_channels; i++) { -+ if (vc4_hdmi->audio.chmap_idx == HDMI_CODEC_CHMAP_IDX_UNKNOWN) -+ ucontrol->value.integer.value[i] = 0; -+ else -+ ucontrol->value.integer.value[i] = map[i]; -+ } -+ return 0; -+} -+ -+static int vc4_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag, -+ unsigned int size, unsigned int __user *tlv) -+{ -+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); -+ struct vc4_hdmi *vc4_hdmi = snd_component_to_hdmi(component); -+ const struct snd_pcm_chmap_elem *map; -+ unsigned int __user *dst; -+ int c, count = 0; -+ -+ if (!vc4_hdmi->audio.chmap) -+ return -EINVAL; -+ if (size < 8) -+ return -ENOMEM; -+ if (put_user(SNDRV_CTL_TLVT_CONTAINER, tlv)) -+ return -EFAULT; -+ size -= 8; -+ dst = tlv + 2; -+ for (map = vc4_hdmi->audio.chmap; map->channels; map++) { -+ int chs_bytes = map->channels * 4; -+ //if (!valid_chmap_channels(info, map->channels)) -+ // continue; -+ if (size < 8) -+ return -ENOMEM; -+ if (put_user(SNDRV_CTL_TLVT_CHMAP_FIXED, dst) || -+ put_user(chs_bytes, dst + 1)) -+ return -EFAULT; -+ dst += 2; -+ size -= 8; -+ count += 8; -+ if (size < chs_bytes) -+ return -ENOMEM; -+ size -= chs_bytes; -+ count += chs_bytes; -+ for (c = 0; c < map->channels; c++) { -+ if (put_user(map->map[c], dst)) -+ return -EFAULT; -+ dst++; -+ } -+ } -+ if (put_user(count, tlv + 1)) -+ return -EFAULT; -+ return 0; -+} -+ - static const struct snd_kcontrol_new vc4_hdmi_audio_controls[] = { - { - .access = SNDRV_CTL_ELEM_ACCESS_READ | -@@ -1167,6 +1572,16 @@ static const struct snd_kcontrol_new vc4 - .info = vc4_spdif_info, - .get = vc4_spdif_mask_get, - }, -+ { -+ .access = SNDRV_CTL_ELEM_ACCESS_READ | -+ SNDRV_CTL_ELEM_ACCESS_TLV_READ | -+ SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK, -+ .iface = SNDRV_CTL_ELEM_IFACE_PCM, -+ .name = "Playback Channel Map", -+ .info = vc4_chmap_ctl_info, -+ .get = vc4_chmap_ctl_get, -+ .tlv.c = vc4_chmap_ctl_tlv, -+ }, - }; - - static const struct snd_soc_dapm_widget vc4_hdmi_audio_widgets[] = { ---- a/drivers/gpu/drm/vc4/vc4_hdmi.h -+++ b/drivers/gpu/drm/vc4/vc4_hdmi.h -@@ -117,6 +117,9 @@ struct vc4_hdmi_audio { - bool streaming; - - unsigned char iec_status[4]; -+ const struct snd_pcm_chmap_elem *chmap; -+ unsigned int chmap_idx; -+ unsigned int max_channels; - }; - - /* General HDMI hardware state. */ |