mirror of
https://github.com/Ysurac/openmptcprouter.git
synced 2025-02-13 20:01:55 +00:00
511 lines
17 KiB
Diff
511 lines
17 KiB
Diff
From 52483e09534ae00ca4e01643bbce2cd2fe48c266 Mon Sep 17 00:00:00 2001
|
|
From: popcornmix <popcornmix@gmail.com>
|
|
Date: Mon, 20 Apr 2020 18:00:38 +0100
|
|
Subject: [PATCH 443/552] 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(+)
|
|
|
|
diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c
|
|
index 2f3a7674902e..16a2e214a1b3 100644
|
|
--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
|
|
+++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
|
|
@@ -50,6 +50,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"
|
|
@@ -98,6 +99,311 @@
|
|
|
|
#define HDMI_14_MAX_TMDS_CLK (340 * 1000 * 1000)
|
|
|
|
+#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 bool vc4_hdmi_mode_needs_scrambling(const struct drm_display_mode *mode)
|
|
{
|
|
return (mode->clock * 1000) > HDMI_14_MAX_TMDS_CLK;
|
|
@@ -487,6 +793,9 @@ static void vc4_hdmi_set_audio_infoframe(struct drm_encoder *encoder)
|
|
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);
|
|
}
|
|
|
|
@@ -1250,6 +1559,10 @@ static int vc4_hdmi_audio_startup(struct snd_pcm_substream *substream,
|
|
if (ret)
|
|
return ret;
|
|
|
|
+ /* Select chmap supported */
|
|
+ vc4_hdmi->audio.max_channels = 8;
|
|
+ hdmi_codec_eld_chmap(vc4_hdmi);
|
|
+
|
|
return 0;
|
|
}
|
|
|
|
@@ -1336,6 +1649,7 @@ static int vc4_hdmi_audio_prepare(struct snd_pcm_substream *substream,
|
|
u32 channel_map;
|
|
u32 mai_audio_format;
|
|
u32 mai_sample_rate;
|
|
+ int idx;
|
|
|
|
if (substream != vc4_hdmi->audio.substream)
|
|
return -EINVAL;
|
|
@@ -1398,6 +1712,14 @@ static int vc4_hdmi_audio_prepare(struct snd_pcm_substream *substream,
|
|
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;
|
|
+ }
|
|
+
|
|
vc4_hdmi_set_audio_infoframe(encoder);
|
|
|
|
return 0;
|
|
@@ -1516,6 +1838,89 @@ static int vc4_spdif_mask_get(struct snd_kcontrol *kcontrol,
|
|
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 |
|
|
@@ -1538,6 +1943,16 @@ static const struct snd_kcontrol_new vc4_hdmi_audio_controls[] = {
|
|
.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[] = {
|
|
diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.h b/drivers/gpu/drm/vc4/vc4_hdmi.h
|
|
index 07fa6007a60c..4d38098b6172 100644
|
|
--- a/drivers/gpu/drm/vc4/vc4_hdmi.h
|
|
+++ b/drivers/gpu/drm/vc4/vc4_hdmi.h
|
|
@@ -119,6 +119,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. */
|
|
--
|
|
2.33.0
|
|
|