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