162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * HDMI Channel map support helpers 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/module.h> 762306a36Sopenharmony_ci#include <sound/control.h> 862306a36Sopenharmony_ci#include <sound/tlv.h> 962306a36Sopenharmony_ci#include <sound/hda_chmap.h> 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci/* 1262306a36Sopenharmony_ci * CEA speaker placement: 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * FLH FCH FRH 1562306a36Sopenharmony_ci * FLW FL FLC FC FRC FR FRW 1662306a36Sopenharmony_ci * 1762306a36Sopenharmony_ci * LFE 1862306a36Sopenharmony_ci * TC 1962306a36Sopenharmony_ci * 2062306a36Sopenharmony_ci * RL RLC RC RRC RR 2162306a36Sopenharmony_ci * 2262306a36Sopenharmony_ci * The Left/Right Surround channel _notions_ LS/RS in SMPTE 320M corresponds to 2362306a36Sopenharmony_ci * CEA RL/RR; The SMPTE channel _assignment_ C/LFE is swapped to CEA LFE/FC. 2462306a36Sopenharmony_ci */ 2562306a36Sopenharmony_cienum cea_speaker_placement { 2662306a36Sopenharmony_ci FL = (1 << 0), /* Front Left */ 2762306a36Sopenharmony_ci FC = (1 << 1), /* Front Center */ 2862306a36Sopenharmony_ci FR = (1 << 2), /* Front Right */ 2962306a36Sopenharmony_ci FLC = (1 << 3), /* Front Left Center */ 3062306a36Sopenharmony_ci FRC = (1 << 4), /* Front Right Center */ 3162306a36Sopenharmony_ci RL = (1 << 5), /* Rear Left */ 3262306a36Sopenharmony_ci RC = (1 << 6), /* Rear Center */ 3362306a36Sopenharmony_ci RR = (1 << 7), /* Rear Right */ 3462306a36Sopenharmony_ci RLC = (1 << 8), /* Rear Left Center */ 3562306a36Sopenharmony_ci RRC = (1 << 9), /* Rear Right Center */ 3662306a36Sopenharmony_ci LFE = (1 << 10), /* Low Frequency Effect */ 3762306a36Sopenharmony_ci FLW = (1 << 11), /* Front Left Wide */ 3862306a36Sopenharmony_ci FRW = (1 << 12), /* Front Right Wide */ 3962306a36Sopenharmony_ci FLH = (1 << 13), /* Front Left High */ 4062306a36Sopenharmony_ci FCH = (1 << 14), /* Front Center High */ 4162306a36Sopenharmony_ci FRH = (1 << 15), /* Front Right High */ 4262306a36Sopenharmony_ci TC = (1 << 16), /* Top Center */ 4362306a36Sopenharmony_ci}; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistatic const char * const cea_speaker_allocation_names[] = { 4662306a36Sopenharmony_ci /* 0 */ "FL/FR", 4762306a36Sopenharmony_ci /* 1 */ "LFE", 4862306a36Sopenharmony_ci /* 2 */ "FC", 4962306a36Sopenharmony_ci /* 3 */ "RL/RR", 5062306a36Sopenharmony_ci /* 4 */ "RC", 5162306a36Sopenharmony_ci /* 5 */ "FLC/FRC", 5262306a36Sopenharmony_ci /* 6 */ "RLC/RRC", 5362306a36Sopenharmony_ci /* 7 */ "FLW/FRW", 5462306a36Sopenharmony_ci /* 8 */ "FLH/FRH", 5562306a36Sopenharmony_ci /* 9 */ "TC", 5662306a36Sopenharmony_ci /* 10 */ "FCH", 5762306a36Sopenharmony_ci}; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci/* 6062306a36Sopenharmony_ci * ELD SA bits in the CEA Speaker Allocation data block 6162306a36Sopenharmony_ci */ 6262306a36Sopenharmony_cistatic const int eld_speaker_allocation_bits[] = { 6362306a36Sopenharmony_ci [0] = FL | FR, 6462306a36Sopenharmony_ci [1] = LFE, 6562306a36Sopenharmony_ci [2] = FC, 6662306a36Sopenharmony_ci [3] = RL | RR, 6762306a36Sopenharmony_ci [4] = RC, 6862306a36Sopenharmony_ci [5] = FLC | FRC, 6962306a36Sopenharmony_ci [6] = RLC | RRC, 7062306a36Sopenharmony_ci /* the following are not defined in ELD yet */ 7162306a36Sopenharmony_ci [7] = FLW | FRW, 7262306a36Sopenharmony_ci [8] = FLH | FRH, 7362306a36Sopenharmony_ci [9] = TC, 7462306a36Sopenharmony_ci [10] = FCH, 7562306a36Sopenharmony_ci}; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci/* 7862306a36Sopenharmony_ci * ALSA sequence is: 7962306a36Sopenharmony_ci * 8062306a36Sopenharmony_ci * surround40 surround41 surround50 surround51 surround71 8162306a36Sopenharmony_ci * ch0 front left = = = = 8262306a36Sopenharmony_ci * ch1 front right = = = = 8362306a36Sopenharmony_ci * ch2 rear left = = = = 8462306a36Sopenharmony_ci * ch3 rear right = = = = 8562306a36Sopenharmony_ci * ch4 LFE center center center 8662306a36Sopenharmony_ci * ch5 LFE LFE 8762306a36Sopenharmony_ci * ch6 side left 8862306a36Sopenharmony_ci * ch7 side right 8962306a36Sopenharmony_ci * 9062306a36Sopenharmony_ci * surround71 = {FL, FR, RLC, RRC, FC, LFE, RL, RR} 9162306a36Sopenharmony_ci */ 9262306a36Sopenharmony_cistatic int hdmi_channel_mapping[0x32][8] = { 9362306a36Sopenharmony_ci /* stereo */ 9462306a36Sopenharmony_ci [0x00] = { 0x00, 0x11, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7 }, 9562306a36Sopenharmony_ci /* 2.1 */ 9662306a36Sopenharmony_ci [0x01] = { 0x00, 0x11, 0x22, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7 }, 9762306a36Sopenharmony_ci /* Dolby Surround */ 9862306a36Sopenharmony_ci [0x02] = { 0x00, 0x11, 0x23, 0xf2, 0xf4, 0xf5, 0xf6, 0xf7 }, 9962306a36Sopenharmony_ci /* surround40 */ 10062306a36Sopenharmony_ci [0x08] = { 0x00, 0x11, 0x24, 0x35, 0xf3, 0xf2, 0xf6, 0xf7 }, 10162306a36Sopenharmony_ci /* 4ch */ 10262306a36Sopenharmony_ci [0x03] = { 0x00, 0x11, 0x23, 0x32, 0x44, 0xf5, 0xf6, 0xf7 }, 10362306a36Sopenharmony_ci /* surround41 */ 10462306a36Sopenharmony_ci [0x09] = { 0x00, 0x11, 0x24, 0x35, 0x42, 0xf3, 0xf6, 0xf7 }, 10562306a36Sopenharmony_ci /* surround50 */ 10662306a36Sopenharmony_ci [0x0a] = { 0x00, 0x11, 0x24, 0x35, 0x43, 0xf2, 0xf6, 0xf7 }, 10762306a36Sopenharmony_ci /* surround51 */ 10862306a36Sopenharmony_ci [0x0b] = { 0x00, 0x11, 0x24, 0x35, 0x43, 0x52, 0xf6, 0xf7 }, 10962306a36Sopenharmony_ci /* 7.1 */ 11062306a36Sopenharmony_ci [0x13] = { 0x00, 0x11, 0x26, 0x37, 0x43, 0x52, 0x64, 0x75 }, 11162306a36Sopenharmony_ci}; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci/* 11462306a36Sopenharmony_ci * This is an ordered list! 11562306a36Sopenharmony_ci * 11662306a36Sopenharmony_ci * The preceding ones have better chances to be selected by 11762306a36Sopenharmony_ci * hdmi_channel_allocation(). 11862306a36Sopenharmony_ci */ 11962306a36Sopenharmony_cistatic struct hdac_cea_channel_speaker_allocation channel_allocations[] = { 12062306a36Sopenharmony_ci/* channel: 7 6 5 4 3 2 1 0 */ 12162306a36Sopenharmony_ci{ .ca_index = 0x00, .speakers = { 0, 0, 0, 0, 0, 0, FR, FL } }, 12262306a36Sopenharmony_ci /* 2.1 */ 12362306a36Sopenharmony_ci{ .ca_index = 0x01, .speakers = { 0, 0, 0, 0, 0, LFE, FR, FL } }, 12462306a36Sopenharmony_ci /* Dolby Surround */ 12562306a36Sopenharmony_ci{ .ca_index = 0x02, .speakers = { 0, 0, 0, 0, FC, 0, FR, FL } }, 12662306a36Sopenharmony_ci /* surround40 */ 12762306a36Sopenharmony_ci{ .ca_index = 0x08, .speakers = { 0, 0, RR, RL, 0, 0, FR, FL } }, 12862306a36Sopenharmony_ci /* surround41 */ 12962306a36Sopenharmony_ci{ .ca_index = 0x09, .speakers = { 0, 0, RR, RL, 0, LFE, FR, FL } }, 13062306a36Sopenharmony_ci /* surround50 */ 13162306a36Sopenharmony_ci{ .ca_index = 0x0a, .speakers = { 0, 0, RR, RL, FC, 0, FR, FL } }, 13262306a36Sopenharmony_ci /* surround51 */ 13362306a36Sopenharmony_ci{ .ca_index = 0x0b, .speakers = { 0, 0, RR, RL, FC, LFE, FR, FL } }, 13462306a36Sopenharmony_ci /* 6.1 */ 13562306a36Sopenharmony_ci{ .ca_index = 0x0f, .speakers = { 0, RC, RR, RL, FC, LFE, FR, FL } }, 13662306a36Sopenharmony_ci /* surround71 */ 13762306a36Sopenharmony_ci{ .ca_index = 0x13, .speakers = { RRC, RLC, RR, RL, FC, LFE, FR, FL } }, 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci{ .ca_index = 0x03, .speakers = { 0, 0, 0, 0, FC, LFE, FR, FL } }, 14062306a36Sopenharmony_ci{ .ca_index = 0x04, .speakers = { 0, 0, 0, RC, 0, 0, FR, FL } }, 14162306a36Sopenharmony_ci{ .ca_index = 0x05, .speakers = { 0, 0, 0, RC, 0, LFE, FR, FL } }, 14262306a36Sopenharmony_ci{ .ca_index = 0x06, .speakers = { 0, 0, 0, RC, FC, 0, FR, FL } }, 14362306a36Sopenharmony_ci{ .ca_index = 0x07, .speakers = { 0, 0, 0, RC, FC, LFE, FR, FL } }, 14462306a36Sopenharmony_ci{ .ca_index = 0x0c, .speakers = { 0, RC, RR, RL, 0, 0, FR, FL } }, 14562306a36Sopenharmony_ci{ .ca_index = 0x0d, .speakers = { 0, RC, RR, RL, 0, LFE, FR, FL } }, 14662306a36Sopenharmony_ci{ .ca_index = 0x0e, .speakers = { 0, RC, RR, RL, FC, 0, FR, FL } }, 14762306a36Sopenharmony_ci{ .ca_index = 0x10, .speakers = { RRC, RLC, RR, RL, 0, 0, FR, FL } }, 14862306a36Sopenharmony_ci{ .ca_index = 0x11, .speakers = { RRC, RLC, RR, RL, 0, LFE, FR, FL } }, 14962306a36Sopenharmony_ci{ .ca_index = 0x12, .speakers = { RRC, RLC, RR, RL, FC, 0, FR, FL } }, 15062306a36Sopenharmony_ci{ .ca_index = 0x14, .speakers = { FRC, FLC, 0, 0, 0, 0, FR, FL } }, 15162306a36Sopenharmony_ci{ .ca_index = 0x15, .speakers = { FRC, FLC, 0, 0, 0, LFE, FR, FL } }, 15262306a36Sopenharmony_ci{ .ca_index = 0x16, .speakers = { FRC, FLC, 0, 0, FC, 0, FR, FL } }, 15362306a36Sopenharmony_ci{ .ca_index = 0x17, .speakers = { FRC, FLC, 0, 0, FC, LFE, FR, FL } }, 15462306a36Sopenharmony_ci{ .ca_index = 0x18, .speakers = { FRC, FLC, 0, RC, 0, 0, FR, FL } }, 15562306a36Sopenharmony_ci{ .ca_index = 0x19, .speakers = { FRC, FLC, 0, RC, 0, LFE, FR, FL } }, 15662306a36Sopenharmony_ci{ .ca_index = 0x1a, .speakers = { FRC, FLC, 0, RC, FC, 0, FR, FL } }, 15762306a36Sopenharmony_ci{ .ca_index = 0x1b, .speakers = { FRC, FLC, 0, RC, FC, LFE, FR, FL } }, 15862306a36Sopenharmony_ci{ .ca_index = 0x1c, .speakers = { FRC, FLC, RR, RL, 0, 0, FR, FL } }, 15962306a36Sopenharmony_ci{ .ca_index = 0x1d, .speakers = { FRC, FLC, RR, RL, 0, LFE, FR, FL } }, 16062306a36Sopenharmony_ci{ .ca_index = 0x1e, .speakers = { FRC, FLC, RR, RL, FC, 0, FR, FL } }, 16162306a36Sopenharmony_ci{ .ca_index = 0x1f, .speakers = { FRC, FLC, RR, RL, FC, LFE, FR, FL } }, 16262306a36Sopenharmony_ci{ .ca_index = 0x20, .speakers = { 0, FCH, RR, RL, FC, 0, FR, FL } }, 16362306a36Sopenharmony_ci{ .ca_index = 0x21, .speakers = { 0, FCH, RR, RL, FC, LFE, FR, FL } }, 16462306a36Sopenharmony_ci{ .ca_index = 0x22, .speakers = { TC, 0, RR, RL, FC, 0, FR, FL } }, 16562306a36Sopenharmony_ci{ .ca_index = 0x23, .speakers = { TC, 0, RR, RL, FC, LFE, FR, FL } }, 16662306a36Sopenharmony_ci{ .ca_index = 0x24, .speakers = { FRH, FLH, RR, RL, 0, 0, FR, FL } }, 16762306a36Sopenharmony_ci{ .ca_index = 0x25, .speakers = { FRH, FLH, RR, RL, 0, LFE, FR, FL } }, 16862306a36Sopenharmony_ci{ .ca_index = 0x26, .speakers = { FRW, FLW, RR, RL, 0, 0, FR, FL } }, 16962306a36Sopenharmony_ci{ .ca_index = 0x27, .speakers = { FRW, FLW, RR, RL, 0, LFE, FR, FL } }, 17062306a36Sopenharmony_ci{ .ca_index = 0x28, .speakers = { TC, RC, RR, RL, FC, 0, FR, FL } }, 17162306a36Sopenharmony_ci{ .ca_index = 0x29, .speakers = { TC, RC, RR, RL, FC, LFE, FR, FL } }, 17262306a36Sopenharmony_ci{ .ca_index = 0x2a, .speakers = { FCH, RC, RR, RL, FC, 0, FR, FL } }, 17362306a36Sopenharmony_ci{ .ca_index = 0x2b, .speakers = { FCH, RC, RR, RL, FC, LFE, FR, FL } }, 17462306a36Sopenharmony_ci{ .ca_index = 0x2c, .speakers = { TC, FCH, RR, RL, FC, 0, FR, FL } }, 17562306a36Sopenharmony_ci{ .ca_index = 0x2d, .speakers = { TC, FCH, RR, RL, FC, LFE, FR, FL } }, 17662306a36Sopenharmony_ci{ .ca_index = 0x2e, .speakers = { FRH, FLH, RR, RL, FC, 0, FR, FL } }, 17762306a36Sopenharmony_ci{ .ca_index = 0x2f, .speakers = { FRH, FLH, RR, RL, FC, LFE, FR, FL } }, 17862306a36Sopenharmony_ci{ .ca_index = 0x30, .speakers = { FRW, FLW, RR, RL, FC, 0, FR, FL } }, 17962306a36Sopenharmony_ci{ .ca_index = 0x31, .speakers = { FRW, FLW, RR, RL, FC, LFE, FR, FL } }, 18062306a36Sopenharmony_ci}; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_cistatic int hdmi_pin_set_slot_channel(struct hdac_device *codec, 18362306a36Sopenharmony_ci hda_nid_t pin_nid, int asp_slot, int channel) 18462306a36Sopenharmony_ci{ 18562306a36Sopenharmony_ci return snd_hdac_codec_write(codec, pin_nid, 0, 18662306a36Sopenharmony_ci AC_VERB_SET_HDMI_CHAN_SLOT, 18762306a36Sopenharmony_ci (channel << 4) | asp_slot); 18862306a36Sopenharmony_ci} 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_cistatic int hdmi_pin_get_slot_channel(struct hdac_device *codec, 19162306a36Sopenharmony_ci hda_nid_t pin_nid, int asp_slot) 19262306a36Sopenharmony_ci{ 19362306a36Sopenharmony_ci return (snd_hdac_codec_read(codec, pin_nid, 0, 19462306a36Sopenharmony_ci AC_VERB_GET_HDMI_CHAN_SLOT, 19562306a36Sopenharmony_ci asp_slot) & 0xf0) >> 4; 19662306a36Sopenharmony_ci} 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_cistatic int hdmi_get_channel_count(struct hdac_device *codec, hda_nid_t cvt_nid) 19962306a36Sopenharmony_ci{ 20062306a36Sopenharmony_ci return 1 + snd_hdac_codec_read(codec, cvt_nid, 0, 20162306a36Sopenharmony_ci AC_VERB_GET_CVT_CHAN_COUNT, 0); 20262306a36Sopenharmony_ci} 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_cistatic void hdmi_set_channel_count(struct hdac_device *codec, 20562306a36Sopenharmony_ci hda_nid_t cvt_nid, int chs) 20662306a36Sopenharmony_ci{ 20762306a36Sopenharmony_ci if (chs != hdmi_get_channel_count(codec, cvt_nid)) 20862306a36Sopenharmony_ci snd_hdac_codec_write(codec, cvt_nid, 0, 20962306a36Sopenharmony_ci AC_VERB_SET_CVT_CHAN_COUNT, chs - 1); 21062306a36Sopenharmony_ci} 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci/* 21362306a36Sopenharmony_ci * Channel mapping routines 21462306a36Sopenharmony_ci */ 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci/* 21762306a36Sopenharmony_ci * Compute derived values in channel_allocations[]. 21862306a36Sopenharmony_ci */ 21962306a36Sopenharmony_cistatic void init_channel_allocations(void) 22062306a36Sopenharmony_ci{ 22162306a36Sopenharmony_ci int i, j; 22262306a36Sopenharmony_ci struct hdac_cea_channel_speaker_allocation *p; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { 22562306a36Sopenharmony_ci p = channel_allocations + i; 22662306a36Sopenharmony_ci p->channels = 0; 22762306a36Sopenharmony_ci p->spk_mask = 0; 22862306a36Sopenharmony_ci for (j = 0; j < ARRAY_SIZE(p->speakers); j++) 22962306a36Sopenharmony_ci if (p->speakers[j]) { 23062306a36Sopenharmony_ci p->channels++; 23162306a36Sopenharmony_ci p->spk_mask |= p->speakers[j]; 23262306a36Sopenharmony_ci } 23362306a36Sopenharmony_ci } 23462306a36Sopenharmony_ci} 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_cistatic int get_channel_allocation_order(int ca) 23762306a36Sopenharmony_ci{ 23862306a36Sopenharmony_ci int i; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { 24162306a36Sopenharmony_ci if (channel_allocations[i].ca_index == ca) 24262306a36Sopenharmony_ci break; 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci return i; 24562306a36Sopenharmony_ci} 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_civoid snd_hdac_print_channel_allocation(int spk_alloc, char *buf, int buflen) 24862306a36Sopenharmony_ci{ 24962306a36Sopenharmony_ci int i, j; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci for (i = 0, j = 0; i < ARRAY_SIZE(cea_speaker_allocation_names); i++) { 25262306a36Sopenharmony_ci if (spk_alloc & (1 << i)) 25362306a36Sopenharmony_ci j += scnprintf(buf + j, buflen - j, " %s", 25462306a36Sopenharmony_ci cea_speaker_allocation_names[i]); 25562306a36Sopenharmony_ci } 25662306a36Sopenharmony_ci buf[j] = '\0'; /* necessary when j == 0 */ 25762306a36Sopenharmony_ci} 25862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hdac_print_channel_allocation); 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci/* 26162306a36Sopenharmony_ci * The transformation takes two steps: 26262306a36Sopenharmony_ci * 26362306a36Sopenharmony_ci * eld->spk_alloc => (eld_speaker_allocation_bits[]) => spk_mask 26462306a36Sopenharmony_ci * spk_mask => (channel_allocations[]) => ai->CA 26562306a36Sopenharmony_ci * 26662306a36Sopenharmony_ci * TODO: it could select the wrong CA from multiple candidates. 26762306a36Sopenharmony_ci*/ 26862306a36Sopenharmony_cistatic int hdmi_channel_allocation_spk_alloc_blk(struct hdac_device *codec, 26962306a36Sopenharmony_ci int spk_alloc, int channels) 27062306a36Sopenharmony_ci{ 27162306a36Sopenharmony_ci int i; 27262306a36Sopenharmony_ci int ca = 0; 27362306a36Sopenharmony_ci int spk_mask = 0; 27462306a36Sopenharmony_ci char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE]; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci /* 27762306a36Sopenharmony_ci * CA defaults to 0 for basic stereo audio 27862306a36Sopenharmony_ci */ 27962306a36Sopenharmony_ci if (channels <= 2) 28062306a36Sopenharmony_ci return 0; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci /* 28362306a36Sopenharmony_ci * expand ELD's speaker allocation mask 28462306a36Sopenharmony_ci * 28562306a36Sopenharmony_ci * ELD tells the speaker mask in a compact(paired) form, 28662306a36Sopenharmony_ci * expand ELD's notions to match the ones used by Audio InfoFrame. 28762306a36Sopenharmony_ci */ 28862306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(eld_speaker_allocation_bits); i++) { 28962306a36Sopenharmony_ci if (spk_alloc & (1 << i)) 29062306a36Sopenharmony_ci spk_mask |= eld_speaker_allocation_bits[i]; 29162306a36Sopenharmony_ci } 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci /* search for the first working match in the CA table */ 29462306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { 29562306a36Sopenharmony_ci if (channels == channel_allocations[i].channels && 29662306a36Sopenharmony_ci (spk_mask & channel_allocations[i].spk_mask) == 29762306a36Sopenharmony_ci channel_allocations[i].spk_mask) { 29862306a36Sopenharmony_ci ca = channel_allocations[i].ca_index; 29962306a36Sopenharmony_ci break; 30062306a36Sopenharmony_ci } 30162306a36Sopenharmony_ci } 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci if (!ca) { 30462306a36Sopenharmony_ci /* 30562306a36Sopenharmony_ci * if there was no match, select the regular ALSA channel 30662306a36Sopenharmony_ci * allocation with the matching number of channels 30762306a36Sopenharmony_ci */ 30862306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { 30962306a36Sopenharmony_ci if (channels == channel_allocations[i].channels) { 31062306a36Sopenharmony_ci ca = channel_allocations[i].ca_index; 31162306a36Sopenharmony_ci break; 31262306a36Sopenharmony_ci } 31362306a36Sopenharmony_ci } 31462306a36Sopenharmony_ci } 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci snd_hdac_print_channel_allocation(spk_alloc, buf, sizeof(buf)); 31762306a36Sopenharmony_ci dev_dbg(&codec->dev, "HDMI: select CA 0x%x for %d-channel allocation: %s\n", 31862306a36Sopenharmony_ci ca, channels, buf); 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci return ca; 32162306a36Sopenharmony_ci} 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_cistatic void hdmi_debug_channel_mapping(struct hdac_chmap *chmap, 32462306a36Sopenharmony_ci hda_nid_t pin_nid) 32562306a36Sopenharmony_ci{ 32662306a36Sopenharmony_ci#ifdef CONFIG_SND_DEBUG_VERBOSE 32762306a36Sopenharmony_ci int i; 32862306a36Sopenharmony_ci int channel; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci for (i = 0; i < 8; i++) { 33162306a36Sopenharmony_ci channel = chmap->ops.pin_get_slot_channel( 33262306a36Sopenharmony_ci chmap->hdac, pin_nid, i); 33362306a36Sopenharmony_ci dev_dbg(&chmap->hdac->dev, "HDMI: ASP channel %d => slot %d\n", 33462306a36Sopenharmony_ci channel, i); 33562306a36Sopenharmony_ci } 33662306a36Sopenharmony_ci#endif 33762306a36Sopenharmony_ci} 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_cistatic void hdmi_std_setup_channel_mapping(struct hdac_chmap *chmap, 34062306a36Sopenharmony_ci hda_nid_t pin_nid, 34162306a36Sopenharmony_ci bool non_pcm, 34262306a36Sopenharmony_ci int ca) 34362306a36Sopenharmony_ci{ 34462306a36Sopenharmony_ci struct hdac_cea_channel_speaker_allocation *ch_alloc; 34562306a36Sopenharmony_ci int i; 34662306a36Sopenharmony_ci int err; 34762306a36Sopenharmony_ci int order; 34862306a36Sopenharmony_ci int non_pcm_mapping[8]; 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci order = get_channel_allocation_order(ca); 35162306a36Sopenharmony_ci ch_alloc = &channel_allocations[order]; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci if (hdmi_channel_mapping[ca][1] == 0) { 35462306a36Sopenharmony_ci int hdmi_slot = 0; 35562306a36Sopenharmony_ci /* fill actual channel mappings in ALSA channel (i) order */ 35662306a36Sopenharmony_ci for (i = 0; i < ch_alloc->channels; i++) { 35762306a36Sopenharmony_ci while (!WARN_ON(hdmi_slot >= 8) && 35862306a36Sopenharmony_ci !ch_alloc->speakers[7 - hdmi_slot]) 35962306a36Sopenharmony_ci hdmi_slot++; /* skip zero slots */ 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci hdmi_channel_mapping[ca][i] = (i << 4) | hdmi_slot++; 36262306a36Sopenharmony_ci } 36362306a36Sopenharmony_ci /* fill the rest of the slots with ALSA channel 0xf */ 36462306a36Sopenharmony_ci for (hdmi_slot = 0; hdmi_slot < 8; hdmi_slot++) 36562306a36Sopenharmony_ci if (!ch_alloc->speakers[7 - hdmi_slot]) 36662306a36Sopenharmony_ci hdmi_channel_mapping[ca][i++] = (0xf << 4) | hdmi_slot; 36762306a36Sopenharmony_ci } 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci if (non_pcm) { 37062306a36Sopenharmony_ci for (i = 0; i < ch_alloc->channels; i++) 37162306a36Sopenharmony_ci non_pcm_mapping[i] = (i << 4) | i; 37262306a36Sopenharmony_ci for (; i < 8; i++) 37362306a36Sopenharmony_ci non_pcm_mapping[i] = (0xf << 4) | i; 37462306a36Sopenharmony_ci } 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci for (i = 0; i < 8; i++) { 37762306a36Sopenharmony_ci int slotsetup = non_pcm ? non_pcm_mapping[i] : hdmi_channel_mapping[ca][i]; 37862306a36Sopenharmony_ci int hdmi_slot = slotsetup & 0x0f; 37962306a36Sopenharmony_ci int channel = (slotsetup & 0xf0) >> 4; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci err = chmap->ops.pin_set_slot_channel(chmap->hdac, 38262306a36Sopenharmony_ci pin_nid, hdmi_slot, channel); 38362306a36Sopenharmony_ci if (err) { 38462306a36Sopenharmony_ci dev_dbg(&chmap->hdac->dev, "HDMI: channel mapping failed\n"); 38562306a36Sopenharmony_ci break; 38662306a36Sopenharmony_ci } 38762306a36Sopenharmony_ci } 38862306a36Sopenharmony_ci} 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_cistruct channel_map_table { 39162306a36Sopenharmony_ci unsigned char map; /* ALSA API channel map position */ 39262306a36Sopenharmony_ci int spk_mask; /* speaker position bit mask */ 39362306a36Sopenharmony_ci}; 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_cistatic struct channel_map_table map_tables[] = { 39662306a36Sopenharmony_ci { SNDRV_CHMAP_FL, FL }, 39762306a36Sopenharmony_ci { SNDRV_CHMAP_FR, FR }, 39862306a36Sopenharmony_ci { SNDRV_CHMAP_RL, RL }, 39962306a36Sopenharmony_ci { SNDRV_CHMAP_RR, RR }, 40062306a36Sopenharmony_ci { SNDRV_CHMAP_LFE, LFE }, 40162306a36Sopenharmony_ci { SNDRV_CHMAP_FC, FC }, 40262306a36Sopenharmony_ci { SNDRV_CHMAP_RLC, RLC }, 40362306a36Sopenharmony_ci { SNDRV_CHMAP_RRC, RRC }, 40462306a36Sopenharmony_ci { SNDRV_CHMAP_RC, RC }, 40562306a36Sopenharmony_ci { SNDRV_CHMAP_FLC, FLC }, 40662306a36Sopenharmony_ci { SNDRV_CHMAP_FRC, FRC }, 40762306a36Sopenharmony_ci { SNDRV_CHMAP_TFL, FLH }, 40862306a36Sopenharmony_ci { SNDRV_CHMAP_TFR, FRH }, 40962306a36Sopenharmony_ci { SNDRV_CHMAP_FLW, FLW }, 41062306a36Sopenharmony_ci { SNDRV_CHMAP_FRW, FRW }, 41162306a36Sopenharmony_ci { SNDRV_CHMAP_TC, TC }, 41262306a36Sopenharmony_ci { SNDRV_CHMAP_TFC, FCH }, 41362306a36Sopenharmony_ci {} /* terminator */ 41462306a36Sopenharmony_ci}; 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci/* from ALSA API channel position to speaker bit mask */ 41762306a36Sopenharmony_ciint snd_hdac_chmap_to_spk_mask(unsigned char c) 41862306a36Sopenharmony_ci{ 41962306a36Sopenharmony_ci struct channel_map_table *t = map_tables; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci for (; t->map; t++) { 42262306a36Sopenharmony_ci if (t->map == c) 42362306a36Sopenharmony_ci return t->spk_mask; 42462306a36Sopenharmony_ci } 42562306a36Sopenharmony_ci return 0; 42662306a36Sopenharmony_ci} 42762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hdac_chmap_to_spk_mask); 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci/* from ALSA API channel position to CEA slot */ 43062306a36Sopenharmony_cistatic int to_cea_slot(int ordered_ca, unsigned char pos) 43162306a36Sopenharmony_ci{ 43262306a36Sopenharmony_ci int mask = snd_hdac_chmap_to_spk_mask(pos); 43362306a36Sopenharmony_ci int i; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci /* Add sanity check to pass klockwork check. 43662306a36Sopenharmony_ci * This should never happen. 43762306a36Sopenharmony_ci */ 43862306a36Sopenharmony_ci if (ordered_ca >= ARRAY_SIZE(channel_allocations)) 43962306a36Sopenharmony_ci return -1; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci if (mask) { 44262306a36Sopenharmony_ci for (i = 0; i < 8; i++) { 44362306a36Sopenharmony_ci if (channel_allocations[ordered_ca].speakers[7 - i] == mask) 44462306a36Sopenharmony_ci return i; 44562306a36Sopenharmony_ci } 44662306a36Sopenharmony_ci } 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci return -1; 44962306a36Sopenharmony_ci} 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci/* from speaker bit mask to ALSA API channel position */ 45262306a36Sopenharmony_ciint snd_hdac_spk_to_chmap(int spk) 45362306a36Sopenharmony_ci{ 45462306a36Sopenharmony_ci struct channel_map_table *t = map_tables; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci for (; t->map; t++) { 45762306a36Sopenharmony_ci if (t->spk_mask == spk) 45862306a36Sopenharmony_ci return t->map; 45962306a36Sopenharmony_ci } 46062306a36Sopenharmony_ci return 0; 46162306a36Sopenharmony_ci} 46262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hdac_spk_to_chmap); 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci/* from CEA slot to ALSA API channel position */ 46562306a36Sopenharmony_cistatic int from_cea_slot(int ordered_ca, unsigned char slot) 46662306a36Sopenharmony_ci{ 46762306a36Sopenharmony_ci int mask; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci /* Add sanity check to pass klockwork check. 47062306a36Sopenharmony_ci * This should never happen. 47162306a36Sopenharmony_ci */ 47262306a36Sopenharmony_ci if (slot >= 8) 47362306a36Sopenharmony_ci return 0; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci mask = channel_allocations[ordered_ca].speakers[7 - slot]; 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci return snd_hdac_spk_to_chmap(mask); 47862306a36Sopenharmony_ci} 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci/* get the CA index corresponding to the given ALSA API channel map */ 48162306a36Sopenharmony_cistatic int hdmi_manual_channel_allocation(int chs, unsigned char *map) 48262306a36Sopenharmony_ci{ 48362306a36Sopenharmony_ci int i, spks = 0, spk_mask = 0; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci for (i = 0; i < chs; i++) { 48662306a36Sopenharmony_ci int mask = snd_hdac_chmap_to_spk_mask(map[i]); 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci if (mask) { 48962306a36Sopenharmony_ci spk_mask |= mask; 49062306a36Sopenharmony_ci spks++; 49162306a36Sopenharmony_ci } 49262306a36Sopenharmony_ci } 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { 49562306a36Sopenharmony_ci if ((chs == channel_allocations[i].channels || 49662306a36Sopenharmony_ci spks == channel_allocations[i].channels) && 49762306a36Sopenharmony_ci (spk_mask & channel_allocations[i].spk_mask) == 49862306a36Sopenharmony_ci channel_allocations[i].spk_mask) 49962306a36Sopenharmony_ci return channel_allocations[i].ca_index; 50062306a36Sopenharmony_ci } 50162306a36Sopenharmony_ci return -1; 50262306a36Sopenharmony_ci} 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci/* set up the channel slots for the given ALSA API channel map */ 50562306a36Sopenharmony_cistatic int hdmi_manual_setup_channel_mapping(struct hdac_chmap *chmap, 50662306a36Sopenharmony_ci hda_nid_t pin_nid, 50762306a36Sopenharmony_ci int chs, unsigned char *map, 50862306a36Sopenharmony_ci int ca) 50962306a36Sopenharmony_ci{ 51062306a36Sopenharmony_ci int ordered_ca = get_channel_allocation_order(ca); 51162306a36Sopenharmony_ci int alsa_pos, hdmi_slot; 51262306a36Sopenharmony_ci int assignments[8] = {[0 ... 7] = 0xf}; 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci for (alsa_pos = 0; alsa_pos < chs; alsa_pos++) { 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci hdmi_slot = to_cea_slot(ordered_ca, map[alsa_pos]); 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci if (hdmi_slot < 0) 51962306a36Sopenharmony_ci continue; /* unassigned channel */ 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci assignments[hdmi_slot] = alsa_pos; 52262306a36Sopenharmony_ci } 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci for (hdmi_slot = 0; hdmi_slot < 8; hdmi_slot++) { 52562306a36Sopenharmony_ci int err; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci err = chmap->ops.pin_set_slot_channel(chmap->hdac, 52862306a36Sopenharmony_ci pin_nid, hdmi_slot, assignments[hdmi_slot]); 52962306a36Sopenharmony_ci if (err) 53062306a36Sopenharmony_ci return -EINVAL; 53162306a36Sopenharmony_ci } 53262306a36Sopenharmony_ci return 0; 53362306a36Sopenharmony_ci} 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci/* store ALSA API channel map from the current default map */ 53662306a36Sopenharmony_cistatic void hdmi_setup_fake_chmap(unsigned char *map, int ca) 53762306a36Sopenharmony_ci{ 53862306a36Sopenharmony_ci int i; 53962306a36Sopenharmony_ci int ordered_ca = get_channel_allocation_order(ca); 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci for (i = 0; i < 8; i++) { 54262306a36Sopenharmony_ci if (ordered_ca < ARRAY_SIZE(channel_allocations) && 54362306a36Sopenharmony_ci i < channel_allocations[ordered_ca].channels) 54462306a36Sopenharmony_ci map[i] = from_cea_slot(ordered_ca, hdmi_channel_mapping[ca][i] & 0x0f); 54562306a36Sopenharmony_ci else 54662306a36Sopenharmony_ci map[i] = 0; 54762306a36Sopenharmony_ci } 54862306a36Sopenharmony_ci} 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_civoid snd_hdac_setup_channel_mapping(struct hdac_chmap *chmap, 55162306a36Sopenharmony_ci hda_nid_t pin_nid, bool non_pcm, int ca, 55262306a36Sopenharmony_ci int channels, unsigned char *map, 55362306a36Sopenharmony_ci bool chmap_set) 55462306a36Sopenharmony_ci{ 55562306a36Sopenharmony_ci if (!non_pcm && chmap_set) { 55662306a36Sopenharmony_ci hdmi_manual_setup_channel_mapping(chmap, pin_nid, 55762306a36Sopenharmony_ci channels, map, ca); 55862306a36Sopenharmony_ci } else { 55962306a36Sopenharmony_ci hdmi_std_setup_channel_mapping(chmap, pin_nid, non_pcm, ca); 56062306a36Sopenharmony_ci hdmi_setup_fake_chmap(map, ca); 56162306a36Sopenharmony_ci } 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci hdmi_debug_channel_mapping(chmap, pin_nid); 56462306a36Sopenharmony_ci} 56562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hdac_setup_channel_mapping); 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ciint snd_hdac_get_active_channels(int ca) 56862306a36Sopenharmony_ci{ 56962306a36Sopenharmony_ci int ordered_ca = get_channel_allocation_order(ca); 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci /* Add sanity check to pass klockwork check. 57262306a36Sopenharmony_ci * This should never happen. 57362306a36Sopenharmony_ci */ 57462306a36Sopenharmony_ci if (ordered_ca >= ARRAY_SIZE(channel_allocations)) 57562306a36Sopenharmony_ci ordered_ca = 0; 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci return channel_allocations[ordered_ca].channels; 57862306a36Sopenharmony_ci} 57962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hdac_get_active_channels); 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_cistruct hdac_cea_channel_speaker_allocation *snd_hdac_get_ch_alloc_from_ca(int ca) 58262306a36Sopenharmony_ci{ 58362306a36Sopenharmony_ci return &channel_allocations[get_channel_allocation_order(ca)]; 58462306a36Sopenharmony_ci} 58562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hdac_get_ch_alloc_from_ca); 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ciint snd_hdac_channel_allocation(struct hdac_device *hdac, int spk_alloc, 58862306a36Sopenharmony_ci int channels, bool chmap_set, bool non_pcm, unsigned char *map) 58962306a36Sopenharmony_ci{ 59062306a36Sopenharmony_ci int ca; 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci if (!non_pcm && chmap_set) 59362306a36Sopenharmony_ci ca = hdmi_manual_channel_allocation(channels, map); 59462306a36Sopenharmony_ci else 59562306a36Sopenharmony_ci ca = hdmi_channel_allocation_spk_alloc_blk(hdac, 59662306a36Sopenharmony_ci spk_alloc, channels); 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci if (ca < 0) 59962306a36Sopenharmony_ci ca = 0; 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci return ca; 60262306a36Sopenharmony_ci} 60362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hdac_channel_allocation); 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci/* 60662306a36Sopenharmony_ci * ALSA API channel-map control callbacks 60762306a36Sopenharmony_ci */ 60862306a36Sopenharmony_cistatic int hdmi_chmap_ctl_info(struct snd_kcontrol *kcontrol, 60962306a36Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 61062306a36Sopenharmony_ci{ 61162306a36Sopenharmony_ci struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); 61262306a36Sopenharmony_ci struct hdac_chmap *chmap = info->private_data; 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 61562306a36Sopenharmony_ci uinfo->count = chmap->channels_max; 61662306a36Sopenharmony_ci uinfo->value.integer.min = 0; 61762306a36Sopenharmony_ci uinfo->value.integer.max = SNDRV_CHMAP_LAST; 61862306a36Sopenharmony_ci return 0; 61962306a36Sopenharmony_ci} 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_cistatic int hdmi_chmap_cea_alloc_validate_get_type(struct hdac_chmap *chmap, 62262306a36Sopenharmony_ci struct hdac_cea_channel_speaker_allocation *cap, int channels) 62362306a36Sopenharmony_ci{ 62462306a36Sopenharmony_ci /* If the speaker allocation matches the channel count, it is OK.*/ 62562306a36Sopenharmony_ci if (cap->channels != channels) 62662306a36Sopenharmony_ci return -1; 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci /* all channels are remappable freely */ 62962306a36Sopenharmony_ci return SNDRV_CTL_TLVT_CHMAP_VAR; 63062306a36Sopenharmony_ci} 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_cistatic void hdmi_cea_alloc_to_tlv_chmap(struct hdac_chmap *hchmap, 63362306a36Sopenharmony_ci struct hdac_cea_channel_speaker_allocation *cap, 63462306a36Sopenharmony_ci unsigned int *chmap, int channels) 63562306a36Sopenharmony_ci{ 63662306a36Sopenharmony_ci int count = 0; 63762306a36Sopenharmony_ci int c; 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci for (c = 7; c >= 0; c--) { 64062306a36Sopenharmony_ci int spk = cap->speakers[c]; 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci if (!spk) 64362306a36Sopenharmony_ci continue; 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci chmap[count++] = snd_hdac_spk_to_chmap(spk); 64662306a36Sopenharmony_ci } 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci WARN_ON(count != channels); 64962306a36Sopenharmony_ci} 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_cistatic int spk_mask_from_spk_alloc(int spk_alloc) 65262306a36Sopenharmony_ci{ 65362306a36Sopenharmony_ci int i; 65462306a36Sopenharmony_ci int spk_mask = eld_speaker_allocation_bits[0]; 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(eld_speaker_allocation_bits); i++) { 65762306a36Sopenharmony_ci if (spk_alloc & (1 << i)) 65862306a36Sopenharmony_ci spk_mask |= eld_speaker_allocation_bits[i]; 65962306a36Sopenharmony_ci } 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci return spk_mask; 66262306a36Sopenharmony_ci} 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_cistatic int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag, 66562306a36Sopenharmony_ci unsigned int size, unsigned int __user *tlv) 66662306a36Sopenharmony_ci{ 66762306a36Sopenharmony_ci struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); 66862306a36Sopenharmony_ci struct hdac_chmap *chmap = info->private_data; 66962306a36Sopenharmony_ci int pcm_idx = kcontrol->private_value; 67062306a36Sopenharmony_ci unsigned int __user *dst; 67162306a36Sopenharmony_ci int chs, count = 0; 67262306a36Sopenharmony_ci unsigned long max_chs; 67362306a36Sopenharmony_ci int type; 67462306a36Sopenharmony_ci int spk_alloc, spk_mask; 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci if (size < 8) 67762306a36Sopenharmony_ci return -ENOMEM; 67862306a36Sopenharmony_ci if (put_user(SNDRV_CTL_TLVT_CONTAINER, tlv)) 67962306a36Sopenharmony_ci return -EFAULT; 68062306a36Sopenharmony_ci size -= 8; 68162306a36Sopenharmony_ci dst = tlv + 2; 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci spk_alloc = chmap->ops.get_spk_alloc(chmap->hdac, pcm_idx); 68462306a36Sopenharmony_ci spk_mask = spk_mask_from_spk_alloc(spk_alloc); 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci max_chs = hweight_long(spk_mask); 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci for (chs = 2; chs <= max_chs; chs++) { 68962306a36Sopenharmony_ci int i; 69062306a36Sopenharmony_ci struct hdac_cea_channel_speaker_allocation *cap; 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci cap = channel_allocations; 69362306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(channel_allocations); i++, cap++) { 69462306a36Sopenharmony_ci int chs_bytes = chs * 4; 69562306a36Sopenharmony_ci unsigned int tlv_chmap[8]; 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci if (cap->channels != chs) 69862306a36Sopenharmony_ci continue; 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci if (!(cap->spk_mask == (spk_mask & cap->spk_mask))) 70162306a36Sopenharmony_ci continue; 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci type = chmap->ops.chmap_cea_alloc_validate_get_type( 70462306a36Sopenharmony_ci chmap, cap, chs); 70562306a36Sopenharmony_ci if (type < 0) 70662306a36Sopenharmony_ci return -ENODEV; 70762306a36Sopenharmony_ci if (size < 8) 70862306a36Sopenharmony_ci return -ENOMEM; 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci if (put_user(type, dst) || 71162306a36Sopenharmony_ci put_user(chs_bytes, dst + 1)) 71262306a36Sopenharmony_ci return -EFAULT; 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci dst += 2; 71562306a36Sopenharmony_ci size -= 8; 71662306a36Sopenharmony_ci count += 8; 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci if (size < chs_bytes) 71962306a36Sopenharmony_ci return -ENOMEM; 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci size -= chs_bytes; 72262306a36Sopenharmony_ci count += chs_bytes; 72362306a36Sopenharmony_ci chmap->ops.cea_alloc_to_tlv_chmap(chmap, cap, 72462306a36Sopenharmony_ci tlv_chmap, chs); 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci if (copy_to_user(dst, tlv_chmap, chs_bytes)) 72762306a36Sopenharmony_ci return -EFAULT; 72862306a36Sopenharmony_ci dst += chs; 72962306a36Sopenharmony_ci } 73062306a36Sopenharmony_ci } 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci if (put_user(count, tlv + 1)) 73362306a36Sopenharmony_ci return -EFAULT; 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci return 0; 73662306a36Sopenharmony_ci} 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_cistatic int hdmi_chmap_ctl_get(struct snd_kcontrol *kcontrol, 73962306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 74062306a36Sopenharmony_ci{ 74162306a36Sopenharmony_ci struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); 74262306a36Sopenharmony_ci struct hdac_chmap *chmap = info->private_data; 74362306a36Sopenharmony_ci int pcm_idx = kcontrol->private_value; 74462306a36Sopenharmony_ci unsigned char pcm_chmap[8]; 74562306a36Sopenharmony_ci int i; 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci memset(pcm_chmap, 0, sizeof(pcm_chmap)); 74862306a36Sopenharmony_ci chmap->ops.get_chmap(chmap->hdac, pcm_idx, pcm_chmap); 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(pcm_chmap); i++) 75162306a36Sopenharmony_ci ucontrol->value.integer.value[i] = pcm_chmap[i]; 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci return 0; 75462306a36Sopenharmony_ci} 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_cistatic int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol, 75762306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 75862306a36Sopenharmony_ci{ 75962306a36Sopenharmony_ci struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); 76062306a36Sopenharmony_ci struct hdac_chmap *hchmap = info->private_data; 76162306a36Sopenharmony_ci int pcm_idx = kcontrol->private_value; 76262306a36Sopenharmony_ci unsigned int ctl_idx; 76362306a36Sopenharmony_ci struct snd_pcm_substream *substream; 76462306a36Sopenharmony_ci unsigned char chmap[8], per_pin_chmap[8]; 76562306a36Sopenharmony_ci int i, err, ca, prepared = 0; 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci /* No monitor is connected in dyn_pcm_assign. 76862306a36Sopenharmony_ci * It's invalid to setup the chmap 76962306a36Sopenharmony_ci */ 77062306a36Sopenharmony_ci if (!hchmap->ops.is_pcm_attached(hchmap->hdac, pcm_idx)) 77162306a36Sopenharmony_ci return 0; 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci ctl_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); 77462306a36Sopenharmony_ci substream = snd_pcm_chmap_substream(info, ctl_idx); 77562306a36Sopenharmony_ci if (!substream || !substream->runtime) 77662306a36Sopenharmony_ci return 0; /* just for avoiding error from alsactl restore */ 77762306a36Sopenharmony_ci switch (substream->runtime->state) { 77862306a36Sopenharmony_ci case SNDRV_PCM_STATE_OPEN: 77962306a36Sopenharmony_ci case SNDRV_PCM_STATE_SETUP: 78062306a36Sopenharmony_ci break; 78162306a36Sopenharmony_ci case SNDRV_PCM_STATE_PREPARED: 78262306a36Sopenharmony_ci prepared = 1; 78362306a36Sopenharmony_ci break; 78462306a36Sopenharmony_ci default: 78562306a36Sopenharmony_ci return -EBUSY; 78662306a36Sopenharmony_ci } 78762306a36Sopenharmony_ci memset(chmap, 0, sizeof(chmap)); 78862306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(chmap); i++) 78962306a36Sopenharmony_ci chmap[i] = ucontrol->value.integer.value[i]; 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci hchmap->ops.get_chmap(hchmap->hdac, pcm_idx, per_pin_chmap); 79262306a36Sopenharmony_ci if (!memcmp(chmap, per_pin_chmap, sizeof(chmap))) 79362306a36Sopenharmony_ci return 0; 79462306a36Sopenharmony_ci ca = hdmi_manual_channel_allocation(ARRAY_SIZE(chmap), chmap); 79562306a36Sopenharmony_ci if (ca < 0) 79662306a36Sopenharmony_ci return -EINVAL; 79762306a36Sopenharmony_ci if (hchmap->ops.chmap_validate) { 79862306a36Sopenharmony_ci err = hchmap->ops.chmap_validate(hchmap, ca, 79962306a36Sopenharmony_ci ARRAY_SIZE(chmap), chmap); 80062306a36Sopenharmony_ci if (err) 80162306a36Sopenharmony_ci return err; 80262306a36Sopenharmony_ci } 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci hchmap->ops.set_chmap(hchmap->hdac, pcm_idx, chmap, prepared); 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci return 0; 80762306a36Sopenharmony_ci} 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_cistatic const struct hdac_chmap_ops chmap_ops = { 81062306a36Sopenharmony_ci .chmap_cea_alloc_validate_get_type = hdmi_chmap_cea_alloc_validate_get_type, 81162306a36Sopenharmony_ci .cea_alloc_to_tlv_chmap = hdmi_cea_alloc_to_tlv_chmap, 81262306a36Sopenharmony_ci .pin_get_slot_channel = hdmi_pin_get_slot_channel, 81362306a36Sopenharmony_ci .pin_set_slot_channel = hdmi_pin_set_slot_channel, 81462306a36Sopenharmony_ci .set_channel_count = hdmi_set_channel_count, 81562306a36Sopenharmony_ci}; 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_civoid snd_hdac_register_chmap_ops(struct hdac_device *hdac, 81862306a36Sopenharmony_ci struct hdac_chmap *chmap) 81962306a36Sopenharmony_ci{ 82062306a36Sopenharmony_ci chmap->ops = chmap_ops; 82162306a36Sopenharmony_ci chmap->hdac = hdac; 82262306a36Sopenharmony_ci init_channel_allocations(); 82362306a36Sopenharmony_ci} 82462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hdac_register_chmap_ops); 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ciint snd_hdac_add_chmap_ctls(struct snd_pcm *pcm, int pcm_idx, 82762306a36Sopenharmony_ci struct hdac_chmap *hchmap) 82862306a36Sopenharmony_ci{ 82962306a36Sopenharmony_ci struct snd_pcm_chmap *chmap; 83062306a36Sopenharmony_ci struct snd_kcontrol *kctl; 83162306a36Sopenharmony_ci int err, i; 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci err = snd_pcm_add_chmap_ctls(pcm, 83462306a36Sopenharmony_ci SNDRV_PCM_STREAM_PLAYBACK, 83562306a36Sopenharmony_ci NULL, 0, pcm_idx, &chmap); 83662306a36Sopenharmony_ci if (err < 0) 83762306a36Sopenharmony_ci return err; 83862306a36Sopenharmony_ci /* override handlers */ 83962306a36Sopenharmony_ci chmap->private_data = hchmap; 84062306a36Sopenharmony_ci kctl = chmap->kctl; 84162306a36Sopenharmony_ci for (i = 0; i < kctl->count; i++) 84262306a36Sopenharmony_ci kctl->vd[i].access |= SNDRV_CTL_ELEM_ACCESS_WRITE; 84362306a36Sopenharmony_ci kctl->info = hdmi_chmap_ctl_info; 84462306a36Sopenharmony_ci kctl->get = hdmi_chmap_ctl_get; 84562306a36Sopenharmony_ci kctl->put = hdmi_chmap_ctl_put; 84662306a36Sopenharmony_ci kctl->tlv.c = hdmi_chmap_ctl_tlv; 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci return 0; 84962306a36Sopenharmony_ci} 85062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hdac_add_chmap_ctls); 851