brintos

brintos / linux-shallow public Read only

0
0
Text · 24.6 KiB · 7b27604 Raw
869 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * HDMI Channel map support helpers4 */5 6#include <linux/module.h>7#include <sound/control.h>8#include <sound/tlv.h>9#include <sound/hda_chmap.h>10 11/*12 * CEA speaker placement:13 *14 *        FLH       FCH        FRH15 *  FLW    FL  FLC   FC   FRC   FR   FRW16 *17 *                                  LFE18 *                     TC19 *20 *          RL  RLC   RC   RRC   RR21 *22 * The Left/Right Surround channel _notions_ LS/RS in SMPTE 320M corresponds to23 * CEA RL/RR; The SMPTE channel _assignment_ C/LFE is swapped to CEA LFE/FC.24 */25enum cea_speaker_placement {26	FL  = (1 <<  0),	/* Front Left           */27	FC  = (1 <<  1),	/* Front Center         */28	FR  = (1 <<  2),	/* Front Right          */29	FLC = (1 <<  3),	/* Front Left Center    */30	FRC = (1 <<  4),	/* Front Right Center   */31	RL  = (1 <<  5),	/* Rear Left            */32	RC  = (1 <<  6),	/* Rear Center          */33	RR  = (1 <<  7),	/* Rear Right           */34	RLC = (1 <<  8),	/* Rear Left Center     */35	RRC = (1 <<  9),	/* Rear Right Center    */36	LFE = (1 << 10),	/* Low Frequency Effect */37	FLW = (1 << 11),	/* Front Left Wide      */38	FRW = (1 << 12),	/* Front Right Wide     */39	FLH = (1 << 13),	/* Front Left High      */40	FCH = (1 << 14),	/* Front Center High    */41	FRH = (1 << 15),	/* Front Right High     */42	TC  = (1 << 16),	/* Top Center           */43};44 45static const char * const cea_speaker_allocation_names[] = {46	/*  0 */ "FL/FR",47	/*  1 */ "LFE",48	/*  2 */ "FC",49	/*  3 */ "RL/RR",50	/*  4 */ "RC",51	/*  5 */ "FLC/FRC",52	/*  6 */ "RLC/RRC",53	/*  7 */ "FLW/FRW",54	/*  8 */ "FLH/FRH",55	/*  9 */ "TC",56	/* 10 */ "FCH",57};58 59/*60 * ELD SA bits in the CEA Speaker Allocation data block61 */62static const int eld_speaker_allocation_bits[] = {63	[0] = FL | FR,64	[1] = LFE,65	[2] = FC,66	[3] = RL | RR,67	[4] = RC,68	[5] = FLC | FRC,69	[6] = RLC | RRC,70	/* the following are not defined in ELD yet */71	[7] = FLW | FRW,72	[8] = FLH | FRH,73	[9] = TC,74	[10] = FCH,75};76 77/*78 * ALSA sequence is:79 *80 *       surround40   surround41   surround50   surround51   surround7181 * ch0   front left   =            =            =            =82 * ch1   front right  =            =            =            =83 * ch2   rear left    =            =            =            =84 * ch3   rear right   =            =            =            =85 * ch4                LFE          center       center       center86 * ch5                                          LFE          LFE87 * ch6                                                       side left88 * ch7                                                       side right89 *90 * surround71 = {FL, FR, RLC, RRC, FC, LFE, RL, RR}91 */92static int hdmi_channel_mapping[0x32][8] = {93	/* stereo */94	[0x00] = { 0x00, 0x11, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7 },95	/* 2.1 */96	[0x01] = { 0x00, 0x11, 0x22, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7 },97	/* Dolby Surround */98	[0x02] = { 0x00, 0x11, 0x23, 0xf2, 0xf4, 0xf5, 0xf6, 0xf7 },99	/* surround40 */100	[0x08] = { 0x00, 0x11, 0x24, 0x35, 0xf3, 0xf2, 0xf6, 0xf7 },101	/* 4ch */102	[0x03] = { 0x00, 0x11, 0x23, 0x32, 0x44, 0xf5, 0xf6, 0xf7 },103	/* surround41 */104	[0x09] = { 0x00, 0x11, 0x24, 0x35, 0x42, 0xf3, 0xf6, 0xf7 },105	/* surround50 */106	[0x0a] = { 0x00, 0x11, 0x24, 0x35, 0x43, 0xf2, 0xf6, 0xf7 },107	/* surround51 */108	[0x0b] = { 0x00, 0x11, 0x24, 0x35, 0x43, 0x52, 0xf6, 0xf7 },109	/* 7.1 */110	[0x13] = { 0x00, 0x11, 0x26, 0x37, 0x43, 0x52, 0x64, 0x75 },111};112 113/*114 * This is an ordered list!115 *116 * The preceding ones have better chances to be selected by117 * hdmi_channel_allocation().118 */119static struct hdac_cea_channel_speaker_allocation channel_allocations[] = {120/*			  channel:   7     6    5    4    3     2    1    0  */121{ .ca_index = 0x00,  .speakers = {   0,    0,   0,   0,   0,    0,  FR,  FL } },122				 /* 2.1 */123{ .ca_index = 0x01,  .speakers = {   0,    0,   0,   0,   0,  LFE,  FR,  FL } },124				 /* Dolby Surround */125{ .ca_index = 0x02,  .speakers = {   0,    0,   0,   0,  FC,    0,  FR,  FL } },126				 /* surround40 */127{ .ca_index = 0x08,  .speakers = {   0,    0,  RR,  RL,   0,    0,  FR,  FL } },128				 /* surround41 */129{ .ca_index = 0x09,  .speakers = {   0,    0,  RR,  RL,   0,  LFE,  FR,  FL } },130				 /* surround50 */131{ .ca_index = 0x0a,  .speakers = {   0,    0,  RR,  RL,  FC,    0,  FR,  FL } },132				 /* surround51 */133{ .ca_index = 0x0b,  .speakers = {   0,    0,  RR,  RL,  FC,  LFE,  FR,  FL } },134				 /* 6.1 */135{ .ca_index = 0x0f,  .speakers = {   0,   RC,  RR,  RL,  FC,  LFE,  FR,  FL } },136				 /* surround71 */137{ .ca_index = 0x13,  .speakers = { RRC,  RLC,  RR,  RL,  FC,  LFE,  FR,  FL } },138 139{ .ca_index = 0x03,  .speakers = {   0,    0,   0,   0,  FC,  LFE,  FR,  FL } },140{ .ca_index = 0x04,  .speakers = {   0,    0,   0,  RC,   0,    0,  FR,  FL } },141{ .ca_index = 0x05,  .speakers = {   0,    0,   0,  RC,   0,  LFE,  FR,  FL } },142{ .ca_index = 0x06,  .speakers = {   0,    0,   0,  RC,  FC,    0,  FR,  FL } },143{ .ca_index = 0x07,  .speakers = {   0,    0,   0,  RC,  FC,  LFE,  FR,  FL } },144{ .ca_index = 0x0c,  .speakers = {   0,   RC,  RR,  RL,   0,    0,  FR,  FL } },145{ .ca_index = 0x0d,  .speakers = {   0,   RC,  RR,  RL,   0,  LFE,  FR,  FL } },146{ .ca_index = 0x0e,  .speakers = {   0,   RC,  RR,  RL,  FC,    0,  FR,  FL } },147{ .ca_index = 0x10,  .speakers = { RRC,  RLC,  RR,  RL,   0,    0,  FR,  FL } },148{ .ca_index = 0x11,  .speakers = { RRC,  RLC,  RR,  RL,   0,  LFE,  FR,  FL } },149{ .ca_index = 0x12,  .speakers = { RRC,  RLC,  RR,  RL,  FC,    0,  FR,  FL } },150{ .ca_index = 0x14,  .speakers = { FRC,  FLC,   0,   0,   0,    0,  FR,  FL } },151{ .ca_index = 0x15,  .speakers = { FRC,  FLC,   0,   0,   0,  LFE,  FR,  FL } },152{ .ca_index = 0x16,  .speakers = { FRC,  FLC,   0,   0,  FC,    0,  FR,  FL } },153{ .ca_index = 0x17,  .speakers = { FRC,  FLC,   0,   0,  FC,  LFE,  FR,  FL } },154{ .ca_index = 0x18,  .speakers = { FRC,  FLC,   0,  RC,   0,    0,  FR,  FL } },155{ .ca_index = 0x19,  .speakers = { FRC,  FLC,   0,  RC,   0,  LFE,  FR,  FL } },156{ .ca_index = 0x1a,  .speakers = { FRC,  FLC,   0,  RC,  FC,    0,  FR,  FL } },157{ .ca_index = 0x1b,  .speakers = { FRC,  FLC,   0,  RC,  FC,  LFE,  FR,  FL } },158{ .ca_index = 0x1c,  .speakers = { FRC,  FLC,  RR,  RL,   0,    0,  FR,  FL } },159{ .ca_index = 0x1d,  .speakers = { FRC,  FLC,  RR,  RL,   0,  LFE,  FR,  FL } },160{ .ca_index = 0x1e,  .speakers = { FRC,  FLC,  RR,  RL,  FC,    0,  FR,  FL } },161{ .ca_index = 0x1f,  .speakers = { FRC,  FLC,  RR,  RL,  FC,  LFE,  FR,  FL } },162{ .ca_index = 0x20,  .speakers = {   0,  FCH,  RR,  RL,  FC,    0,  FR,  FL } },163{ .ca_index = 0x21,  .speakers = {   0,  FCH,  RR,  RL,  FC,  LFE,  FR,  FL } },164{ .ca_index = 0x22,  .speakers = {  TC,    0,  RR,  RL,  FC,    0,  FR,  FL } },165{ .ca_index = 0x23,  .speakers = {  TC,    0,  RR,  RL,  FC,  LFE,  FR,  FL } },166{ .ca_index = 0x24,  .speakers = { FRH,  FLH,  RR,  RL,   0,    0,  FR,  FL } },167{ .ca_index = 0x25,  .speakers = { FRH,  FLH,  RR,  RL,   0,  LFE,  FR,  FL } },168{ .ca_index = 0x26,  .speakers = { FRW,  FLW,  RR,  RL,   0,    0,  FR,  FL } },169{ .ca_index = 0x27,  .speakers = { FRW,  FLW,  RR,  RL,   0,  LFE,  FR,  FL } },170{ .ca_index = 0x28,  .speakers = {  TC,   RC,  RR,  RL,  FC,    0,  FR,  FL } },171{ .ca_index = 0x29,  .speakers = {  TC,   RC,  RR,  RL,  FC,  LFE,  FR,  FL } },172{ .ca_index = 0x2a,  .speakers = { FCH,   RC,  RR,  RL,  FC,    0,  FR,  FL } },173{ .ca_index = 0x2b,  .speakers = { FCH,   RC,  RR,  RL,  FC,  LFE,  FR,  FL } },174{ .ca_index = 0x2c,  .speakers = {  TC,  FCH,  RR,  RL,  FC,    0,  FR,  FL } },175{ .ca_index = 0x2d,  .speakers = {  TC,  FCH,  RR,  RL,  FC,  LFE,  FR,  FL } },176{ .ca_index = 0x2e,  .speakers = { FRH,  FLH,  RR,  RL,  FC,    0,  FR,  FL } },177{ .ca_index = 0x2f,  .speakers = { FRH,  FLH,  RR,  RL,  FC,  LFE,  FR,  FL } },178{ .ca_index = 0x30,  .speakers = { FRW,  FLW,  RR,  RL,  FC,    0,  FR,  FL } },179{ .ca_index = 0x31,  .speakers = { FRW,  FLW,  RR,  RL,  FC,  LFE,  FR,  FL } },180};181 182static int hdmi_pin_set_slot_channel(struct hdac_device *codec,183		hda_nid_t pin_nid, int asp_slot, int channel)184{185	return snd_hdac_codec_write(codec, pin_nid, 0,186				AC_VERB_SET_HDMI_CHAN_SLOT,187				(channel << 4) | asp_slot);188}189 190static int hdmi_pin_get_slot_channel(struct hdac_device *codec,191			hda_nid_t pin_nid, int asp_slot)192{193	return (snd_hdac_codec_read(codec, pin_nid, 0,194				   AC_VERB_GET_HDMI_CHAN_SLOT,195				   asp_slot) & 0xf0) >> 4;196}197 198static int hdmi_get_channel_count(struct hdac_device *codec, hda_nid_t cvt_nid)199{200	return 1 + snd_hdac_codec_read(codec, cvt_nid, 0,201					AC_VERB_GET_CVT_CHAN_COUNT, 0);202}203 204static void hdmi_set_channel_count(struct hdac_device *codec,205				   hda_nid_t cvt_nid, int chs)206{207	if (chs != hdmi_get_channel_count(codec, cvt_nid))208		snd_hdac_codec_write(codec, cvt_nid, 0,209				    AC_VERB_SET_CVT_CHAN_COUNT, chs - 1);210}211 212/*213 * Channel mapping routines214 */215 216/*217 * Compute derived values in channel_allocations[].218 */219static void init_channel_allocations(void)220{221	int i, j;222	struct hdac_cea_channel_speaker_allocation *p;223 224	for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {225		p = channel_allocations + i;226		p->channels = 0;227		p->spk_mask = 0;228		for (j = 0; j < ARRAY_SIZE(p->speakers); j++)229			if (p->speakers[j]) {230				p->channels++;231				p->spk_mask |= p->speakers[j];232			}233	}234}235 236static int get_channel_allocation_order(int ca)237{238	int i;239 240	for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {241		if (channel_allocations[i].ca_index == ca)242			break;243	}244	return i;245}246 247void snd_hdac_print_channel_allocation(int spk_alloc, char *buf, int buflen)248{249	int i, j;250 251	for (i = 0, j = 0; i < ARRAY_SIZE(cea_speaker_allocation_names); i++) {252		if (spk_alloc & (1 << i))253			j += scnprintf(buf + j, buflen - j,  " %s",254					cea_speaker_allocation_names[i]);255	}256	buf[j] = '\0';	/* necessary when j == 0 */257}258EXPORT_SYMBOL_GPL(snd_hdac_print_channel_allocation);259 260/*261 * The transformation takes two steps:262 *263 *	eld->spk_alloc => (eld_speaker_allocation_bits[]) => spk_mask264 *	      spk_mask => (channel_allocations[])         => ai->CA265 *266 * TODO: it could select the wrong CA from multiple candidates.267*/268static int hdmi_channel_allocation_spk_alloc_blk(struct hdac_device *codec,269				   int spk_alloc, int channels)270{271	int i;272	int ca = 0;273	int spk_mask = 0;274	char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE];275 276	/*277	 * CA defaults to 0 for basic stereo audio278	 */279	if (channels <= 2)280		return 0;281 282	/*283	 * expand ELD's speaker allocation mask284	 *285	 * ELD tells the speaker mask in a compact(paired) form,286	 * expand ELD's notions to match the ones used by Audio InfoFrame.287	 */288	for (i = 0; i < ARRAY_SIZE(eld_speaker_allocation_bits); i++) {289		if (spk_alloc & (1 << i))290			spk_mask |= eld_speaker_allocation_bits[i];291	}292 293	/* search for the first working match in the CA table */294	for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {295		if (channels == channel_allocations[i].channels &&296		    (spk_mask & channel_allocations[i].spk_mask) ==297				channel_allocations[i].spk_mask) {298			ca = channel_allocations[i].ca_index;299			break;300		}301	}302 303	if (!ca) {304		/*305		 * if there was no match, select the regular ALSA channel306		 * allocation with the matching number of channels307		 */308		for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {309			if (channels == channel_allocations[i].channels) {310				ca = channel_allocations[i].ca_index;311				break;312			}313		}314	}315 316	snd_hdac_print_channel_allocation(spk_alloc, buf, sizeof(buf));317	dev_dbg(&codec->dev, "HDMI: select CA 0x%x for %d-channel allocation: %s\n",318		    ca, channels, buf);319 320	return ca;321}322 323static void hdmi_debug_channel_mapping(struct hdac_chmap *chmap,324				       hda_nid_t pin_nid)325{326#ifdef CONFIG_SND_DEBUG_VERBOSE327	int i;328	int channel;329 330	for (i = 0; i < 8; i++) {331		channel = chmap->ops.pin_get_slot_channel(332				chmap->hdac, pin_nid, i);333		dev_dbg(&chmap->hdac->dev, "HDMI: ASP channel %d => slot %d\n",334						channel, i);335	}336#endif337}338 339static void hdmi_std_setup_channel_mapping(struct hdac_chmap *chmap,340				       hda_nid_t pin_nid,341				       bool non_pcm,342				       int ca)343{344	struct hdac_cea_channel_speaker_allocation *ch_alloc;345	int i;346	int err;347	int order;348	int non_pcm_mapping[8];349 350	order = get_channel_allocation_order(ca);351	ch_alloc = &channel_allocations[order];352 353	if (hdmi_channel_mapping[ca][1] == 0) {354		int hdmi_slot = 0;355		/* fill actual channel mappings in ALSA channel (i) order */356		for (i = 0; i < ch_alloc->channels; i++) {357			while (!WARN_ON(hdmi_slot >= 8) &&358			       !ch_alloc->speakers[7 - hdmi_slot])359				hdmi_slot++; /* skip zero slots */360 361			hdmi_channel_mapping[ca][i] = (i << 4) | hdmi_slot++;362		}363		/* fill the rest of the slots with ALSA channel 0xf */364		for (hdmi_slot = 0; hdmi_slot < 8; hdmi_slot++)365			if (!ch_alloc->speakers[7 - hdmi_slot])366				hdmi_channel_mapping[ca][i++] = (0xf << 4) | hdmi_slot;367	}368 369	if (non_pcm) {370		for (i = 0; i < ch_alloc->channels; i++)371			non_pcm_mapping[i] = (i << 4) | i;372		for (; i < 8; i++)373			non_pcm_mapping[i] = (0xf << 4) | i;374	}375 376	for (i = 0; i < 8; i++) {377		int slotsetup = non_pcm ? non_pcm_mapping[i] : hdmi_channel_mapping[ca][i];378		int hdmi_slot = slotsetup & 0x0f;379		int channel = (slotsetup & 0xf0) >> 4;380 381		err = chmap->ops.pin_set_slot_channel(chmap->hdac,382				pin_nid, hdmi_slot, channel);383		if (err) {384			dev_dbg(&chmap->hdac->dev, "HDMI: channel mapping failed\n");385			break;386		}387	}388}389 390struct channel_map_table {391	unsigned char map;		/* ALSA API channel map position */392	int spk_mask;			/* speaker position bit mask */393};394 395static struct channel_map_table map_tables[] = {396	{ SNDRV_CHMAP_FL,	FL },397	{ SNDRV_CHMAP_FR,	FR },398	{ SNDRV_CHMAP_RL,	RL },399	{ SNDRV_CHMAP_RR,	RR },400	{ SNDRV_CHMAP_LFE,	LFE },401	{ SNDRV_CHMAP_FC,	FC },402	{ SNDRV_CHMAP_RLC,	RLC },403	{ SNDRV_CHMAP_RRC,	RRC },404	{ SNDRV_CHMAP_RC,	RC },405	{ SNDRV_CHMAP_FLC,	FLC },406	{ SNDRV_CHMAP_FRC,	FRC },407	{ SNDRV_CHMAP_TFL,	FLH },408	{ SNDRV_CHMAP_TFR,	FRH },409	{ SNDRV_CHMAP_FLW,	FLW },410	{ SNDRV_CHMAP_FRW,	FRW },411	{ SNDRV_CHMAP_TC,	TC },412	{ SNDRV_CHMAP_TFC,	FCH },413	{} /* terminator */414};415 416/* from ALSA API channel position to speaker bit mask */417int snd_hdac_chmap_to_spk_mask(unsigned char c)418{419	struct channel_map_table *t = map_tables;420 421	for (; t->map; t++) {422		if (t->map == c)423			return t->spk_mask;424	}425	return 0;426}427EXPORT_SYMBOL_GPL(snd_hdac_chmap_to_spk_mask);428 429/* from ALSA API channel position to CEA slot */430static int to_cea_slot(int ordered_ca, unsigned char pos)431{432	int mask = snd_hdac_chmap_to_spk_mask(pos);433	int i;434 435	/* Add sanity check to pass klockwork check.436	 * This should never happen.437	 */438	if (ordered_ca >= ARRAY_SIZE(channel_allocations))439		return -1;440 441	if (mask) {442		for (i = 0; i < 8; i++) {443			if (channel_allocations[ordered_ca].speakers[7 - i] == mask)444				return i;445		}446	}447 448	return -1;449}450 451/* from speaker bit mask to ALSA API channel position */452int snd_hdac_spk_to_chmap(int spk)453{454	struct channel_map_table *t = map_tables;455 456	for (; t->map; t++) {457		if (t->spk_mask == spk)458			return t->map;459	}460	return 0;461}462EXPORT_SYMBOL_GPL(snd_hdac_spk_to_chmap);463 464/* from CEA slot to ALSA API channel position */465static int from_cea_slot(int ordered_ca, unsigned char slot)466{467	int mask;468 469	/* Add sanity check to pass klockwork check.470	 * This should never happen.471	 */472	if (slot >= 8)473		return 0;474 475	mask = channel_allocations[ordered_ca].speakers[7 - slot];476 477	return snd_hdac_spk_to_chmap(mask);478}479 480/* get the CA index corresponding to the given ALSA API channel map */481static int hdmi_manual_channel_allocation(int chs, unsigned char *map)482{483	int i, spks = 0, spk_mask = 0;484 485	for (i = 0; i < chs; i++) {486		int mask = snd_hdac_chmap_to_spk_mask(map[i]);487 488		if (mask) {489			spk_mask |= mask;490			spks++;491		}492	}493 494	for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {495		if ((chs == channel_allocations[i].channels ||496		     spks == channel_allocations[i].channels) &&497		    (spk_mask & channel_allocations[i].spk_mask) ==498				channel_allocations[i].spk_mask)499			return channel_allocations[i].ca_index;500	}501	return -1;502}503 504/* set up the channel slots for the given ALSA API channel map */505static int hdmi_manual_setup_channel_mapping(struct hdac_chmap *chmap,506					     hda_nid_t pin_nid,507					     int chs, unsigned char *map,508					     int ca)509{510	int ordered_ca = get_channel_allocation_order(ca);511	int alsa_pos, hdmi_slot;512	int assignments[8] = {[0 ... 7] = 0xf};513 514	for (alsa_pos = 0; alsa_pos < chs; alsa_pos++) {515 516		hdmi_slot = to_cea_slot(ordered_ca, map[alsa_pos]);517 518		if (hdmi_slot < 0)519			continue; /* unassigned channel */520 521		assignments[hdmi_slot] = alsa_pos;522	}523 524	for (hdmi_slot = 0; hdmi_slot < 8; hdmi_slot++) {525		int err;526 527		err = chmap->ops.pin_set_slot_channel(chmap->hdac,528				pin_nid, hdmi_slot, assignments[hdmi_slot]);529		if (err)530			return -EINVAL;531	}532	return 0;533}534 535/* store ALSA API channel map from the current default map */536static void hdmi_setup_fake_chmap(unsigned char *map, int ca)537{538	int i;539	int ordered_ca = get_channel_allocation_order(ca);540 541	for (i = 0; i < 8; i++) {542		if (ordered_ca < ARRAY_SIZE(channel_allocations) &&543		    i < channel_allocations[ordered_ca].channels)544			map[i] = from_cea_slot(ordered_ca, hdmi_channel_mapping[ca][i] & 0x0f);545		else546			map[i] = 0;547	}548}549 550void snd_hdac_setup_channel_mapping(struct hdac_chmap *chmap,551				       hda_nid_t pin_nid, bool non_pcm, int ca,552				       int channels, unsigned char *map,553				       bool chmap_set)554{555	if (!non_pcm && chmap_set) {556		hdmi_manual_setup_channel_mapping(chmap, pin_nid,557						  channels, map, ca);558	} else {559		hdmi_std_setup_channel_mapping(chmap, pin_nid, non_pcm, ca);560		hdmi_setup_fake_chmap(map, ca);561	}562 563	hdmi_debug_channel_mapping(chmap, pin_nid);564}565EXPORT_SYMBOL_GPL(snd_hdac_setup_channel_mapping);566 567int snd_hdac_get_active_channels(int ca)568{569	int ordered_ca = get_channel_allocation_order(ca);570 571	/* Add sanity check to pass klockwork check.572	 * This should never happen.573	 */574	if (ordered_ca >= ARRAY_SIZE(channel_allocations))575		ordered_ca = 0;576 577	return channel_allocations[ordered_ca].channels;578}579EXPORT_SYMBOL_GPL(snd_hdac_get_active_channels);580 581struct hdac_cea_channel_speaker_allocation *snd_hdac_get_ch_alloc_from_ca(int ca)582{583	return &channel_allocations[get_channel_allocation_order(ca)];584}585EXPORT_SYMBOL_GPL(snd_hdac_get_ch_alloc_from_ca);586 587int snd_hdac_channel_allocation(struct hdac_device *hdac, int spk_alloc,588		int channels, bool chmap_set, bool non_pcm, unsigned char *map)589{590	int ca;591 592	if (!non_pcm && chmap_set)593		ca = hdmi_manual_channel_allocation(channels, map);594	else595		ca = hdmi_channel_allocation_spk_alloc_blk(hdac,596					spk_alloc, channels);597 598	if (ca < 0)599		ca = 0;600 601	return ca;602}603EXPORT_SYMBOL_GPL(snd_hdac_channel_allocation);604 605/*606 * ALSA API channel-map control callbacks607 */608static int hdmi_chmap_ctl_info(struct snd_kcontrol *kcontrol,609			       struct snd_ctl_elem_info *uinfo)610{611	struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);612	struct hdac_chmap *chmap = info->private_data;613 614	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;615	uinfo->count = chmap->channels_max;616	uinfo->value.integer.min = 0;617	uinfo->value.integer.max = SNDRV_CHMAP_LAST;618	return 0;619}620 621static int hdmi_chmap_cea_alloc_validate_get_type(struct hdac_chmap *chmap,622		struct hdac_cea_channel_speaker_allocation *cap, int channels)623{624	/* If the speaker allocation matches the channel count, it is OK.*/625	if (cap->channels != channels)626		return -1;627 628	/* all channels are remappable freely */629	return SNDRV_CTL_TLVT_CHMAP_VAR;630}631 632static void hdmi_cea_alloc_to_tlv_chmap(struct hdac_chmap *hchmap,633		struct hdac_cea_channel_speaker_allocation *cap,634		unsigned int *chmap, int channels)635{636	int count = 0;637	int c;638 639	for (c = 7; c >= 0; c--) {640		int spk = cap->speakers[c];641 642		if (!spk)643			continue;644 645		chmap[count++] = snd_hdac_spk_to_chmap(spk);646	}647 648	WARN_ON(count != channels);649}650 651static int spk_mask_from_spk_alloc(int spk_alloc)652{653	int i;654	int spk_mask = eld_speaker_allocation_bits[0];655 656	for (i = 0; i < ARRAY_SIZE(eld_speaker_allocation_bits); i++) {657		if (spk_alloc & (1 << i))658			spk_mask |= eld_speaker_allocation_bits[i];659	}660 661	return spk_mask;662}663 664static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag,665			      unsigned int size, unsigned int __user *tlv)666{667	struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);668	struct hdac_chmap *chmap = info->private_data;669	int pcm_idx = kcontrol->private_value;670	unsigned int __user *dst;671	int chs, count = 0;672	unsigned long max_chs;673	int type;674	int spk_alloc, spk_mask;675 676	if (size < 8)677		return -ENOMEM;678	if (put_user(SNDRV_CTL_TLVT_CONTAINER, tlv))679		return -EFAULT;680	size -= 8;681	dst = tlv + 2;682 683	spk_alloc = chmap->ops.get_spk_alloc(chmap->hdac, pcm_idx);684	spk_mask = spk_mask_from_spk_alloc(spk_alloc);685 686	max_chs = hweight_long(spk_mask);687 688	for (chs = 2; chs <= max_chs; chs++) {689		int i;690		struct hdac_cea_channel_speaker_allocation *cap;691 692		cap = channel_allocations;693		for (i = 0; i < ARRAY_SIZE(channel_allocations); i++, cap++) {694			int chs_bytes = chs * 4;695			unsigned int tlv_chmap[8];696 697			if (cap->channels != chs)698				continue;699 700			if (!(cap->spk_mask == (spk_mask & cap->spk_mask)))701				continue;702 703			type = chmap->ops.chmap_cea_alloc_validate_get_type(704							chmap, cap, chs);705			if (type < 0)706				return -ENODEV;707			if (size < 8)708				return -ENOMEM;709 710			if (put_user(type, dst) ||711			    put_user(chs_bytes, dst + 1))712				return -EFAULT;713 714			dst += 2;715			size -= 8;716			count += 8;717 718			if (size < chs_bytes)719				return -ENOMEM;720 721			size -= chs_bytes;722			count += chs_bytes;723			chmap->ops.cea_alloc_to_tlv_chmap(chmap, cap,724						tlv_chmap, chs);725 726			if (copy_to_user(dst, tlv_chmap, chs_bytes))727				return -EFAULT;728			dst += chs;729		}730	}731 732	if (put_user(count, tlv + 1))733		return -EFAULT;734 735	return 0;736}737 738static int hdmi_chmap_ctl_get(struct snd_kcontrol *kcontrol,739			      struct snd_ctl_elem_value *ucontrol)740{741	struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);742	struct hdac_chmap *chmap = info->private_data;743	int pcm_idx = kcontrol->private_value;744	unsigned char pcm_chmap[8];745	int i;746 747	memset(pcm_chmap, 0, sizeof(pcm_chmap));748	chmap->ops.get_chmap(chmap->hdac, pcm_idx, pcm_chmap);749 750	for (i = 0; i < ARRAY_SIZE(pcm_chmap); i++)751		ucontrol->value.integer.value[i] = pcm_chmap[i];752 753	return 0;754}755 756/* a simple sanity check for input values to chmap kcontrol */757static int chmap_value_check(struct hdac_chmap *hchmap,758			     const struct snd_ctl_elem_value *ucontrol)759{760	int i;761 762	for (i = 0; i < hchmap->channels_max; i++) {763		if (ucontrol->value.integer.value[i] < 0 ||764		    ucontrol->value.integer.value[i] > SNDRV_CHMAP_LAST)765			return -EINVAL;766	}767	return 0;768}769 770static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol,771			      struct snd_ctl_elem_value *ucontrol)772{773	struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);774	struct hdac_chmap *hchmap = info->private_data;775	int pcm_idx = kcontrol->private_value;776	unsigned int ctl_idx;777	struct snd_pcm_substream *substream;778	unsigned char chmap[8], per_pin_chmap[8];779	int i, err, ca, prepared = 0;780 781	err = chmap_value_check(hchmap, ucontrol);782	if (err < 0)783		return err;784 785	/* No monitor is connected in dyn_pcm_assign.786	 * It's invalid to setup the chmap787	 */788	if (!hchmap->ops.is_pcm_attached(hchmap->hdac, pcm_idx))789		return 0;790 791	ctl_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);792	substream = snd_pcm_chmap_substream(info, ctl_idx);793	if (!substream || !substream->runtime)794		return 0; /* just for avoiding error from alsactl restore */795	switch (substream->runtime->state) {796	case SNDRV_PCM_STATE_OPEN:797	case SNDRV_PCM_STATE_SETUP:798		break;799	case SNDRV_PCM_STATE_PREPARED:800		prepared = 1;801		break;802	default:803		return -EBUSY;804	}805	memset(chmap, 0, sizeof(chmap));806	for (i = 0; i < ARRAY_SIZE(chmap); i++)807		chmap[i] = ucontrol->value.integer.value[i];808 809	hchmap->ops.get_chmap(hchmap->hdac, pcm_idx, per_pin_chmap);810	if (!memcmp(chmap, per_pin_chmap, sizeof(chmap)))811		return 0;812	ca = hdmi_manual_channel_allocation(ARRAY_SIZE(chmap), chmap);813	if (ca < 0)814		return -EINVAL;815	if (hchmap->ops.chmap_validate) {816		err = hchmap->ops.chmap_validate(hchmap, ca,817				ARRAY_SIZE(chmap), chmap);818		if (err)819			return err;820	}821 822	hchmap->ops.set_chmap(hchmap->hdac, pcm_idx, chmap, prepared);823 824	return 0;825}826 827static const struct hdac_chmap_ops chmap_ops = {828	.chmap_cea_alloc_validate_get_type	= hdmi_chmap_cea_alloc_validate_get_type,829	.cea_alloc_to_tlv_chmap			= hdmi_cea_alloc_to_tlv_chmap,830	.pin_get_slot_channel			= hdmi_pin_get_slot_channel,831	.pin_set_slot_channel			= hdmi_pin_set_slot_channel,832	.set_channel_count			= hdmi_set_channel_count,833};834 835void snd_hdac_register_chmap_ops(struct hdac_device *hdac,836				struct hdac_chmap *chmap)837{838	chmap->ops = chmap_ops;839	chmap->hdac = hdac;840	init_channel_allocations();841}842EXPORT_SYMBOL_GPL(snd_hdac_register_chmap_ops);843 844int snd_hdac_add_chmap_ctls(struct snd_pcm *pcm, int pcm_idx,845				struct hdac_chmap *hchmap)846{847	struct snd_pcm_chmap *chmap;848	struct snd_kcontrol *kctl;849	int err, i;850 851	err = snd_pcm_add_chmap_ctls(pcm,852				     SNDRV_PCM_STREAM_PLAYBACK,853				     NULL, 0, pcm_idx, &chmap);854	if (err < 0)855		return err;856	/* override handlers */857	chmap->private_data = hchmap;858	kctl = chmap->kctl;859	for (i = 0; i < kctl->count; i++)860		kctl->vd[i].access |= SNDRV_CTL_ELEM_ACCESS_WRITE;861	kctl->info = hdmi_chmap_ctl_info;862	kctl->get = hdmi_chmap_ctl_get;863	kctl->put = hdmi_chmap_ctl_put;864	kctl->tlv.c = hdmi_chmap_ctl_tlv;865 866	return 0;867}868EXPORT_SYMBOL_GPL(snd_hdac_add_chmap_ctls);869