aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/bcm27xx/patches-5.4/950-0920-vc4-Report-channel-mapping-back-to-userspace.patch
diff options
context:
space:
mode:
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.patch504
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. */