mirror of
				git://git.openwrt.org/openwrt/openwrt.git
				synced 2025-10-30 21:44:27 -04:00 
			
		
		
		
	bcm2708: boot tested on RPi B+ v1.2 bcm2709: boot tested on RPi 3B v1.2 and RPi 4B v1.1 4G bcm2710: boot tested on RPi 3B v1.2 bcm2711: boot tested on RPi 4B v1.1 4G Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
		
			
				
	
	
		
			505 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			505 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
| 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. */
 |