162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * ALSA SoC codec for HDMI encoder drivers 462306a36Sopenharmony_ci * Copyright (C) 2015 Texas Instruments Incorporated - https://www.ti.com/ 562306a36Sopenharmony_ci * Author: Jyri Sarha <jsarha@ti.com> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci#include <linux/module.h> 862306a36Sopenharmony_ci#include <linux/string.h> 962306a36Sopenharmony_ci#include <sound/core.h> 1062306a36Sopenharmony_ci#include <sound/jack.h> 1162306a36Sopenharmony_ci#include <sound/pcm.h> 1262306a36Sopenharmony_ci#include <sound/pcm_params.h> 1362306a36Sopenharmony_ci#include <sound/soc.h> 1462306a36Sopenharmony_ci#include <sound/tlv.h> 1562306a36Sopenharmony_ci#include <sound/pcm_drm_eld.h> 1662306a36Sopenharmony_ci#include <sound/hdmi-codec.h> 1762306a36Sopenharmony_ci#include <sound/pcm_iec958.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include <drm/drm_crtc.h> /* This is only to get MAX_ELD_BYTES */ 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#define HDMI_CODEC_CHMAP_IDX_UNKNOWN -1 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci/* 2462306a36Sopenharmony_ci * CEA speaker placement for HDMI 1.4: 2562306a36Sopenharmony_ci * 2662306a36Sopenharmony_ci * FL FLC FC FRC FR FRW 2762306a36Sopenharmony_ci * 2862306a36Sopenharmony_ci * LFE 2962306a36Sopenharmony_ci * 3062306a36Sopenharmony_ci * RL RLC RC RRC RR 3162306a36Sopenharmony_ci * 3262306a36Sopenharmony_ci * Speaker placement has to be extended to support HDMI 2.0 3362306a36Sopenharmony_ci */ 3462306a36Sopenharmony_cienum hdmi_codec_cea_spk_placement { 3562306a36Sopenharmony_ci FL = BIT(0), /* Front Left */ 3662306a36Sopenharmony_ci FC = BIT(1), /* Front Center */ 3762306a36Sopenharmony_ci FR = BIT(2), /* Front Right */ 3862306a36Sopenharmony_ci FLC = BIT(3), /* Front Left Center */ 3962306a36Sopenharmony_ci FRC = BIT(4), /* Front Right Center */ 4062306a36Sopenharmony_ci RL = BIT(5), /* Rear Left */ 4162306a36Sopenharmony_ci RC = BIT(6), /* Rear Center */ 4262306a36Sopenharmony_ci RR = BIT(7), /* Rear Right */ 4362306a36Sopenharmony_ci RLC = BIT(8), /* Rear Left Center */ 4462306a36Sopenharmony_ci RRC = BIT(9), /* Rear Right Center */ 4562306a36Sopenharmony_ci LFE = BIT(10), /* Low Frequency Effect */ 4662306a36Sopenharmony_ci}; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci/* 4962306a36Sopenharmony_ci * cea Speaker allocation structure 5062306a36Sopenharmony_ci */ 5162306a36Sopenharmony_cistruct hdmi_codec_cea_spk_alloc { 5262306a36Sopenharmony_ci const int ca_id; 5362306a36Sopenharmony_ci unsigned int n_ch; 5462306a36Sopenharmony_ci unsigned long mask; 5562306a36Sopenharmony_ci}; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci/* Channel maps stereo HDMI */ 5862306a36Sopenharmony_cistatic const struct snd_pcm_chmap_elem hdmi_codec_stereo_chmaps[] = { 5962306a36Sopenharmony_ci { .channels = 2, 6062306a36Sopenharmony_ci .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR } }, 6162306a36Sopenharmony_ci { } 6262306a36Sopenharmony_ci}; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci/* Channel maps for multi-channel playbacks, up to 8 n_ch */ 6562306a36Sopenharmony_cistatic const struct snd_pcm_chmap_elem hdmi_codec_8ch_chmaps[] = { 6662306a36Sopenharmony_ci { .channels = 2, /* CA_ID 0x00 */ 6762306a36Sopenharmony_ci .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR } }, 6862306a36Sopenharmony_ci { .channels = 4, /* CA_ID 0x01 */ 6962306a36Sopenharmony_ci .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, 7062306a36Sopenharmony_ci SNDRV_CHMAP_NA } }, 7162306a36Sopenharmony_ci { .channels = 4, /* CA_ID 0x02 */ 7262306a36Sopenharmony_ci .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, 7362306a36Sopenharmony_ci SNDRV_CHMAP_FC } }, 7462306a36Sopenharmony_ci { .channels = 4, /* CA_ID 0x03 */ 7562306a36Sopenharmony_ci .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, 7662306a36Sopenharmony_ci SNDRV_CHMAP_FC } }, 7762306a36Sopenharmony_ci { .channels = 6, /* CA_ID 0x04 */ 7862306a36Sopenharmony_ci .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, 7962306a36Sopenharmony_ci SNDRV_CHMAP_NA, SNDRV_CHMAP_RC, SNDRV_CHMAP_NA } }, 8062306a36Sopenharmony_ci { .channels = 6, /* CA_ID 0x05 */ 8162306a36Sopenharmony_ci .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, 8262306a36Sopenharmony_ci SNDRV_CHMAP_NA, SNDRV_CHMAP_RC, SNDRV_CHMAP_NA } }, 8362306a36Sopenharmony_ci { .channels = 6, /* CA_ID 0x06 */ 8462306a36Sopenharmony_ci .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, 8562306a36Sopenharmony_ci SNDRV_CHMAP_FC, SNDRV_CHMAP_RC, SNDRV_CHMAP_NA } }, 8662306a36Sopenharmony_ci { .channels = 6, /* CA_ID 0x07 */ 8762306a36Sopenharmony_ci .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, 8862306a36Sopenharmony_ci SNDRV_CHMAP_FC, SNDRV_CHMAP_RC, SNDRV_CHMAP_NA } }, 8962306a36Sopenharmony_ci { .channels = 6, /* CA_ID 0x08 */ 9062306a36Sopenharmony_ci .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, 9162306a36Sopenharmony_ci SNDRV_CHMAP_NA, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } }, 9262306a36Sopenharmony_ci { .channels = 6, /* CA_ID 0x09 */ 9362306a36Sopenharmony_ci .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, 9462306a36Sopenharmony_ci SNDRV_CHMAP_NA, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } }, 9562306a36Sopenharmony_ci { .channels = 6, /* CA_ID 0x0A */ 9662306a36Sopenharmony_ci .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, 9762306a36Sopenharmony_ci SNDRV_CHMAP_FC, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } }, 9862306a36Sopenharmony_ci { .channels = 6, /* CA_ID 0x0B */ 9962306a36Sopenharmony_ci .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, 10062306a36Sopenharmony_ci SNDRV_CHMAP_FC, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } }, 10162306a36Sopenharmony_ci { .channels = 8, /* CA_ID 0x0C */ 10262306a36Sopenharmony_ci .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, 10362306a36Sopenharmony_ci SNDRV_CHMAP_NA, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR, 10462306a36Sopenharmony_ci SNDRV_CHMAP_RC, SNDRV_CHMAP_NA } }, 10562306a36Sopenharmony_ci { .channels = 8, /* CA_ID 0x0D */ 10662306a36Sopenharmony_ci .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, 10762306a36Sopenharmony_ci SNDRV_CHMAP_NA, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR, 10862306a36Sopenharmony_ci SNDRV_CHMAP_RC, SNDRV_CHMAP_NA } }, 10962306a36Sopenharmony_ci { .channels = 8, /* CA_ID 0x0E */ 11062306a36Sopenharmony_ci .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, 11162306a36Sopenharmony_ci SNDRV_CHMAP_FC, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR, 11262306a36Sopenharmony_ci SNDRV_CHMAP_RC, SNDRV_CHMAP_NA } }, 11362306a36Sopenharmony_ci { .channels = 8, /* CA_ID 0x0F */ 11462306a36Sopenharmony_ci .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, 11562306a36Sopenharmony_ci SNDRV_CHMAP_FC, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR, 11662306a36Sopenharmony_ci SNDRV_CHMAP_RC, SNDRV_CHMAP_NA } }, 11762306a36Sopenharmony_ci { .channels = 8, /* CA_ID 0x10 */ 11862306a36Sopenharmony_ci .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, 11962306a36Sopenharmony_ci SNDRV_CHMAP_NA, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR, 12062306a36Sopenharmony_ci SNDRV_CHMAP_RLC, SNDRV_CHMAP_RRC } }, 12162306a36Sopenharmony_ci { .channels = 8, /* CA_ID 0x11 */ 12262306a36Sopenharmony_ci .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, 12362306a36Sopenharmony_ci SNDRV_CHMAP_NA, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR, 12462306a36Sopenharmony_ci SNDRV_CHMAP_RLC, SNDRV_CHMAP_RRC } }, 12562306a36Sopenharmony_ci { .channels = 8, /* CA_ID 0x12 */ 12662306a36Sopenharmony_ci .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, 12762306a36Sopenharmony_ci SNDRV_CHMAP_FC, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR, 12862306a36Sopenharmony_ci SNDRV_CHMAP_RLC, SNDRV_CHMAP_RRC } }, 12962306a36Sopenharmony_ci { .channels = 8, /* CA_ID 0x13 */ 13062306a36Sopenharmony_ci .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, 13162306a36Sopenharmony_ci SNDRV_CHMAP_FC, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR, 13262306a36Sopenharmony_ci SNDRV_CHMAP_RLC, SNDRV_CHMAP_RRC } }, 13362306a36Sopenharmony_ci { .channels = 8, /* CA_ID 0x14 */ 13462306a36Sopenharmony_ci .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, 13562306a36Sopenharmony_ci SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, 13662306a36Sopenharmony_ci SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } }, 13762306a36Sopenharmony_ci { .channels = 8, /* CA_ID 0x15 */ 13862306a36Sopenharmony_ci .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, 13962306a36Sopenharmony_ci SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, 14062306a36Sopenharmony_ci SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } }, 14162306a36Sopenharmony_ci { .channels = 8, /* CA_ID 0x16 */ 14262306a36Sopenharmony_ci .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, 14362306a36Sopenharmony_ci SNDRV_CHMAP_FC, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, 14462306a36Sopenharmony_ci SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } }, 14562306a36Sopenharmony_ci { .channels = 8, /* CA_ID 0x17 */ 14662306a36Sopenharmony_ci .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, 14762306a36Sopenharmony_ci SNDRV_CHMAP_FC, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, 14862306a36Sopenharmony_ci SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } }, 14962306a36Sopenharmony_ci { .channels = 8, /* CA_ID 0x18 */ 15062306a36Sopenharmony_ci .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, 15162306a36Sopenharmony_ci SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, 15262306a36Sopenharmony_ci SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } }, 15362306a36Sopenharmony_ci { .channels = 8, /* CA_ID 0x19 */ 15462306a36Sopenharmony_ci .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, 15562306a36Sopenharmony_ci SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, 15662306a36Sopenharmony_ci SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } }, 15762306a36Sopenharmony_ci { .channels = 8, /* CA_ID 0x1A */ 15862306a36Sopenharmony_ci .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, 15962306a36Sopenharmony_ci SNDRV_CHMAP_FC, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, 16062306a36Sopenharmony_ci SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } }, 16162306a36Sopenharmony_ci { .channels = 8, /* CA_ID 0x1B */ 16262306a36Sopenharmony_ci .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, 16362306a36Sopenharmony_ci SNDRV_CHMAP_FC, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, 16462306a36Sopenharmony_ci SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } }, 16562306a36Sopenharmony_ci { .channels = 8, /* CA_ID 0x1C */ 16662306a36Sopenharmony_ci .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, 16762306a36Sopenharmony_ci SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, 16862306a36Sopenharmony_ci SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } }, 16962306a36Sopenharmony_ci { .channels = 8, /* CA_ID 0x1D */ 17062306a36Sopenharmony_ci .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, 17162306a36Sopenharmony_ci SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, 17262306a36Sopenharmony_ci SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } }, 17362306a36Sopenharmony_ci { .channels = 8, /* CA_ID 0x1E */ 17462306a36Sopenharmony_ci .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, 17562306a36Sopenharmony_ci SNDRV_CHMAP_FC, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, 17662306a36Sopenharmony_ci SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } }, 17762306a36Sopenharmony_ci { .channels = 8, /* CA_ID 0x1F */ 17862306a36Sopenharmony_ci .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, 17962306a36Sopenharmony_ci SNDRV_CHMAP_FC, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, 18062306a36Sopenharmony_ci SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } }, 18162306a36Sopenharmony_ci { } 18262306a36Sopenharmony_ci}; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci/* 18562306a36Sopenharmony_ci * hdmi_codec_channel_alloc: speaker configuration available for CEA 18662306a36Sopenharmony_ci * 18762306a36Sopenharmony_ci * This is an ordered list that must match with hdmi_codec_8ch_chmaps struct 18862306a36Sopenharmony_ci * The preceding ones have better chances to be selected by 18962306a36Sopenharmony_ci * hdmi_codec_get_ch_alloc_table_idx(). 19062306a36Sopenharmony_ci */ 19162306a36Sopenharmony_cistatic const struct hdmi_codec_cea_spk_alloc hdmi_codec_channel_alloc[] = { 19262306a36Sopenharmony_ci { .ca_id = 0x00, .n_ch = 2, 19362306a36Sopenharmony_ci .mask = FL | FR}, 19462306a36Sopenharmony_ci /* 2.1 */ 19562306a36Sopenharmony_ci { .ca_id = 0x01, .n_ch = 4, 19662306a36Sopenharmony_ci .mask = FL | FR | LFE}, 19762306a36Sopenharmony_ci /* Dolby Surround */ 19862306a36Sopenharmony_ci { .ca_id = 0x02, .n_ch = 4, 19962306a36Sopenharmony_ci .mask = FL | FR | FC }, 20062306a36Sopenharmony_ci /* surround51 */ 20162306a36Sopenharmony_ci { .ca_id = 0x0b, .n_ch = 6, 20262306a36Sopenharmony_ci .mask = FL | FR | LFE | FC | RL | RR}, 20362306a36Sopenharmony_ci /* surround40 */ 20462306a36Sopenharmony_ci { .ca_id = 0x08, .n_ch = 6, 20562306a36Sopenharmony_ci .mask = FL | FR | RL | RR }, 20662306a36Sopenharmony_ci /* surround41 */ 20762306a36Sopenharmony_ci { .ca_id = 0x09, .n_ch = 6, 20862306a36Sopenharmony_ci .mask = FL | FR | LFE | RL | RR }, 20962306a36Sopenharmony_ci /* surround50 */ 21062306a36Sopenharmony_ci { .ca_id = 0x0a, .n_ch = 6, 21162306a36Sopenharmony_ci .mask = FL | FR | FC | RL | RR }, 21262306a36Sopenharmony_ci /* 6.1 */ 21362306a36Sopenharmony_ci { .ca_id = 0x0f, .n_ch = 8, 21462306a36Sopenharmony_ci .mask = FL | FR | LFE | FC | RL | RR | RC }, 21562306a36Sopenharmony_ci /* surround71 */ 21662306a36Sopenharmony_ci { .ca_id = 0x13, .n_ch = 8, 21762306a36Sopenharmony_ci .mask = FL | FR | LFE | FC | RL | RR | RLC | RRC }, 21862306a36Sopenharmony_ci /* others */ 21962306a36Sopenharmony_ci { .ca_id = 0x03, .n_ch = 8, 22062306a36Sopenharmony_ci .mask = FL | FR | LFE | FC }, 22162306a36Sopenharmony_ci { .ca_id = 0x04, .n_ch = 8, 22262306a36Sopenharmony_ci .mask = FL | FR | RC}, 22362306a36Sopenharmony_ci { .ca_id = 0x05, .n_ch = 8, 22462306a36Sopenharmony_ci .mask = FL | FR | LFE | RC }, 22562306a36Sopenharmony_ci { .ca_id = 0x06, .n_ch = 8, 22662306a36Sopenharmony_ci .mask = FL | FR | FC | RC }, 22762306a36Sopenharmony_ci { .ca_id = 0x07, .n_ch = 8, 22862306a36Sopenharmony_ci .mask = FL | FR | LFE | FC | RC }, 22962306a36Sopenharmony_ci { .ca_id = 0x0c, .n_ch = 8, 23062306a36Sopenharmony_ci .mask = FL | FR | RC | RL | RR }, 23162306a36Sopenharmony_ci { .ca_id = 0x0d, .n_ch = 8, 23262306a36Sopenharmony_ci .mask = FL | FR | LFE | RL | RR | RC }, 23362306a36Sopenharmony_ci { .ca_id = 0x0e, .n_ch = 8, 23462306a36Sopenharmony_ci .mask = FL | FR | FC | RL | RR | RC }, 23562306a36Sopenharmony_ci { .ca_id = 0x10, .n_ch = 8, 23662306a36Sopenharmony_ci .mask = FL | FR | RL | RR | RLC | RRC }, 23762306a36Sopenharmony_ci { .ca_id = 0x11, .n_ch = 8, 23862306a36Sopenharmony_ci .mask = FL | FR | LFE | RL | RR | RLC | RRC }, 23962306a36Sopenharmony_ci { .ca_id = 0x12, .n_ch = 8, 24062306a36Sopenharmony_ci .mask = FL | FR | FC | RL | RR | RLC | RRC }, 24162306a36Sopenharmony_ci { .ca_id = 0x14, .n_ch = 8, 24262306a36Sopenharmony_ci .mask = FL | FR | FLC | FRC }, 24362306a36Sopenharmony_ci { .ca_id = 0x15, .n_ch = 8, 24462306a36Sopenharmony_ci .mask = FL | FR | LFE | FLC | FRC }, 24562306a36Sopenharmony_ci { .ca_id = 0x16, .n_ch = 8, 24662306a36Sopenharmony_ci .mask = FL | FR | FC | FLC | FRC }, 24762306a36Sopenharmony_ci { .ca_id = 0x17, .n_ch = 8, 24862306a36Sopenharmony_ci .mask = FL | FR | LFE | FC | FLC | FRC }, 24962306a36Sopenharmony_ci { .ca_id = 0x18, .n_ch = 8, 25062306a36Sopenharmony_ci .mask = FL | FR | RC | FLC | FRC }, 25162306a36Sopenharmony_ci { .ca_id = 0x19, .n_ch = 8, 25262306a36Sopenharmony_ci .mask = FL | FR | LFE | RC | FLC | FRC }, 25362306a36Sopenharmony_ci { .ca_id = 0x1a, .n_ch = 8, 25462306a36Sopenharmony_ci .mask = FL | FR | RC | FC | FLC | FRC }, 25562306a36Sopenharmony_ci { .ca_id = 0x1b, .n_ch = 8, 25662306a36Sopenharmony_ci .mask = FL | FR | LFE | RC | FC | FLC | FRC }, 25762306a36Sopenharmony_ci { .ca_id = 0x1c, .n_ch = 8, 25862306a36Sopenharmony_ci .mask = FL | FR | RL | RR | FLC | FRC }, 25962306a36Sopenharmony_ci { .ca_id = 0x1d, .n_ch = 8, 26062306a36Sopenharmony_ci .mask = FL | FR | LFE | RL | RR | FLC | FRC }, 26162306a36Sopenharmony_ci { .ca_id = 0x1e, .n_ch = 8, 26262306a36Sopenharmony_ci .mask = FL | FR | FC | RL | RR | FLC | FRC }, 26362306a36Sopenharmony_ci { .ca_id = 0x1f, .n_ch = 8, 26462306a36Sopenharmony_ci .mask = FL | FR | LFE | FC | RL | RR | FLC | FRC }, 26562306a36Sopenharmony_ci}; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_cistruct hdmi_codec_priv { 26862306a36Sopenharmony_ci struct hdmi_codec_pdata hcd; 26962306a36Sopenharmony_ci uint8_t eld[MAX_ELD_BYTES]; 27062306a36Sopenharmony_ci struct snd_pcm_chmap *chmap_info; 27162306a36Sopenharmony_ci unsigned int chmap_idx; 27262306a36Sopenharmony_ci struct mutex lock; 27362306a36Sopenharmony_ci bool busy; 27462306a36Sopenharmony_ci struct snd_soc_jack *jack; 27562306a36Sopenharmony_ci unsigned int jack_status; 27662306a36Sopenharmony_ci u8 iec_status[AES_IEC958_STATUS_SIZE]; 27762306a36Sopenharmony_ci}; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_cistatic const struct snd_soc_dapm_widget hdmi_widgets[] = { 28062306a36Sopenharmony_ci SND_SOC_DAPM_OUTPUT("TX"), 28162306a36Sopenharmony_ci SND_SOC_DAPM_OUTPUT("RX"), 28262306a36Sopenharmony_ci}; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_cienum { 28562306a36Sopenharmony_ci DAI_ID_I2S = 0, 28662306a36Sopenharmony_ci DAI_ID_SPDIF, 28762306a36Sopenharmony_ci}; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_cistatic int hdmi_eld_ctl_info(struct snd_kcontrol *kcontrol, 29062306a36Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 29162306a36Sopenharmony_ci{ 29262306a36Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; 29362306a36Sopenharmony_ci uinfo->count = sizeof_field(struct hdmi_codec_priv, eld); 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci return 0; 29662306a36Sopenharmony_ci} 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_cistatic int hdmi_eld_ctl_get(struct snd_kcontrol *kcontrol, 29962306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 30062306a36Sopenharmony_ci{ 30162306a36Sopenharmony_ci struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); 30262306a36Sopenharmony_ci struct hdmi_codec_priv *hcp = snd_soc_component_get_drvdata(component); 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci memcpy(ucontrol->value.bytes.data, hcp->eld, sizeof(hcp->eld)); 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci return 0; 30762306a36Sopenharmony_ci} 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_cistatic unsigned long hdmi_codec_spk_mask_from_alloc(int spk_alloc) 31062306a36Sopenharmony_ci{ 31162306a36Sopenharmony_ci int i; 31262306a36Sopenharmony_ci static const unsigned long hdmi_codec_eld_spk_alloc_bits[] = { 31362306a36Sopenharmony_ci [0] = FL | FR, [1] = LFE, [2] = FC, [3] = RL | RR, 31462306a36Sopenharmony_ci [4] = RC, [5] = FLC | FRC, [6] = RLC | RRC, 31562306a36Sopenharmony_ci }; 31662306a36Sopenharmony_ci unsigned long spk_mask = 0; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(hdmi_codec_eld_spk_alloc_bits); i++) { 31962306a36Sopenharmony_ci if (spk_alloc & (1 << i)) 32062306a36Sopenharmony_ci spk_mask |= hdmi_codec_eld_spk_alloc_bits[i]; 32162306a36Sopenharmony_ci } 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci return spk_mask; 32462306a36Sopenharmony_ci} 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_cistatic void hdmi_codec_eld_chmap(struct hdmi_codec_priv *hcp) 32762306a36Sopenharmony_ci{ 32862306a36Sopenharmony_ci u8 spk_alloc; 32962306a36Sopenharmony_ci unsigned long spk_mask; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci spk_alloc = drm_eld_get_spk_alloc(hcp->eld); 33262306a36Sopenharmony_ci spk_mask = hdmi_codec_spk_mask_from_alloc(spk_alloc); 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci /* Detect if only stereo supported, else return 8 channels mappings */ 33562306a36Sopenharmony_ci if ((spk_mask & ~(FL | FR)) && hcp->chmap_info->max_channels > 2) 33662306a36Sopenharmony_ci hcp->chmap_info->chmap = hdmi_codec_8ch_chmaps; 33762306a36Sopenharmony_ci else 33862306a36Sopenharmony_ci hcp->chmap_info->chmap = hdmi_codec_stereo_chmaps; 33962306a36Sopenharmony_ci} 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_cistatic int hdmi_codec_get_ch_alloc_table_idx(struct hdmi_codec_priv *hcp, 34262306a36Sopenharmony_ci unsigned char channels) 34362306a36Sopenharmony_ci{ 34462306a36Sopenharmony_ci int i; 34562306a36Sopenharmony_ci u8 spk_alloc; 34662306a36Sopenharmony_ci unsigned long spk_mask; 34762306a36Sopenharmony_ci const struct hdmi_codec_cea_spk_alloc *cap = hdmi_codec_channel_alloc; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci spk_alloc = drm_eld_get_spk_alloc(hcp->eld); 35062306a36Sopenharmony_ci spk_mask = hdmi_codec_spk_mask_from_alloc(spk_alloc); 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(hdmi_codec_channel_alloc); i++, cap++) { 35362306a36Sopenharmony_ci /* If spk_alloc == 0, HDMI is unplugged return stereo config*/ 35462306a36Sopenharmony_ci if (!spk_alloc && cap->ca_id == 0) 35562306a36Sopenharmony_ci return i; 35662306a36Sopenharmony_ci if (cap->n_ch != channels) 35762306a36Sopenharmony_ci continue; 35862306a36Sopenharmony_ci if (!(cap->mask == (spk_mask & cap->mask))) 35962306a36Sopenharmony_ci continue; 36062306a36Sopenharmony_ci return i; 36162306a36Sopenharmony_ci } 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci return -EINVAL; 36462306a36Sopenharmony_ci} 36562306a36Sopenharmony_cistatic int hdmi_codec_chmap_ctl_get(struct snd_kcontrol *kcontrol, 36662306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 36762306a36Sopenharmony_ci{ 36862306a36Sopenharmony_ci unsigned const char *map; 36962306a36Sopenharmony_ci unsigned int i; 37062306a36Sopenharmony_ci struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); 37162306a36Sopenharmony_ci struct hdmi_codec_priv *hcp = info->private_data; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci map = info->chmap[hcp->chmap_idx].map; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci for (i = 0; i < info->max_channels; i++) { 37662306a36Sopenharmony_ci if (hcp->chmap_idx == HDMI_CODEC_CHMAP_IDX_UNKNOWN) 37762306a36Sopenharmony_ci ucontrol->value.integer.value[i] = 0; 37862306a36Sopenharmony_ci else 37962306a36Sopenharmony_ci ucontrol->value.integer.value[i] = map[i]; 38062306a36Sopenharmony_ci } 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci return 0; 38362306a36Sopenharmony_ci} 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_cistatic int hdmi_codec_iec958_info(struct snd_kcontrol *kcontrol, 38662306a36Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 38762306a36Sopenharmony_ci{ 38862306a36Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; 38962306a36Sopenharmony_ci uinfo->count = 1; 39062306a36Sopenharmony_ci return 0; 39162306a36Sopenharmony_ci} 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_cistatic int hdmi_codec_iec958_default_get(struct snd_kcontrol *kcontrol, 39462306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 39562306a36Sopenharmony_ci{ 39662306a36Sopenharmony_ci struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); 39762306a36Sopenharmony_ci struct hdmi_codec_priv *hcp = snd_soc_component_get_drvdata(component); 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci memcpy(ucontrol->value.iec958.status, hcp->iec_status, 40062306a36Sopenharmony_ci sizeof(hcp->iec_status)); 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci return 0; 40362306a36Sopenharmony_ci} 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_cistatic int hdmi_codec_iec958_default_put(struct snd_kcontrol *kcontrol, 40662306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 40762306a36Sopenharmony_ci{ 40862306a36Sopenharmony_ci struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); 40962306a36Sopenharmony_ci struct hdmi_codec_priv *hcp = snd_soc_component_get_drvdata(component); 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci memcpy(hcp->iec_status, ucontrol->value.iec958.status, 41262306a36Sopenharmony_ci sizeof(hcp->iec_status)); 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci return 0; 41562306a36Sopenharmony_ci} 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_cistatic int hdmi_codec_iec958_mask_get(struct snd_kcontrol *kcontrol, 41862306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 41962306a36Sopenharmony_ci{ 42062306a36Sopenharmony_ci memset(ucontrol->value.iec958.status, 0xff, 42162306a36Sopenharmony_ci sizeof_field(struct hdmi_codec_priv, iec_status)); 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci return 0; 42462306a36Sopenharmony_ci} 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_cistatic int hdmi_codec_startup(struct snd_pcm_substream *substream, 42762306a36Sopenharmony_ci struct snd_soc_dai *dai) 42862306a36Sopenharmony_ci{ 42962306a36Sopenharmony_ci struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai); 43062306a36Sopenharmony_ci bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; 43162306a36Sopenharmony_ci bool has_capture = !hcp->hcd.no_i2s_capture; 43262306a36Sopenharmony_ci bool has_playback = !hcp->hcd.no_i2s_playback; 43362306a36Sopenharmony_ci int ret = 0; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci if (!((has_playback && tx) || (has_capture && !tx))) 43662306a36Sopenharmony_ci return 0; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci mutex_lock(&hcp->lock); 43962306a36Sopenharmony_ci if (hcp->busy) { 44062306a36Sopenharmony_ci dev_err(dai->dev, "Only one simultaneous stream supported!\n"); 44162306a36Sopenharmony_ci mutex_unlock(&hcp->lock); 44262306a36Sopenharmony_ci return -EINVAL; 44362306a36Sopenharmony_ci } 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci if (hcp->hcd.ops->audio_startup) { 44662306a36Sopenharmony_ci ret = hcp->hcd.ops->audio_startup(dai->dev->parent, hcp->hcd.data); 44762306a36Sopenharmony_ci if (ret) 44862306a36Sopenharmony_ci goto err; 44962306a36Sopenharmony_ci } 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci if (tx && hcp->hcd.ops->get_eld) { 45262306a36Sopenharmony_ci ret = hcp->hcd.ops->get_eld(dai->dev->parent, hcp->hcd.data, 45362306a36Sopenharmony_ci hcp->eld, sizeof(hcp->eld)); 45462306a36Sopenharmony_ci if (ret) 45562306a36Sopenharmony_ci goto err; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci ret = snd_pcm_hw_constraint_eld(substream->runtime, hcp->eld); 45862306a36Sopenharmony_ci if (ret) 45962306a36Sopenharmony_ci goto err; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci /* Select chmap supported */ 46262306a36Sopenharmony_ci hdmi_codec_eld_chmap(hcp); 46362306a36Sopenharmony_ci } 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci hcp->busy = true; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_cierr: 46862306a36Sopenharmony_ci mutex_unlock(&hcp->lock); 46962306a36Sopenharmony_ci return ret; 47062306a36Sopenharmony_ci} 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_cistatic void hdmi_codec_shutdown(struct snd_pcm_substream *substream, 47362306a36Sopenharmony_ci struct snd_soc_dai *dai) 47462306a36Sopenharmony_ci{ 47562306a36Sopenharmony_ci struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai); 47662306a36Sopenharmony_ci bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; 47762306a36Sopenharmony_ci bool has_capture = !hcp->hcd.no_i2s_capture; 47862306a36Sopenharmony_ci bool has_playback = !hcp->hcd.no_i2s_playback; 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci if (!((has_playback && tx) || (has_capture && !tx))) 48162306a36Sopenharmony_ci return; 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci hcp->chmap_idx = HDMI_CODEC_CHMAP_IDX_UNKNOWN; 48462306a36Sopenharmony_ci hcp->hcd.ops->audio_shutdown(dai->dev->parent, hcp->hcd.data); 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci mutex_lock(&hcp->lock); 48762306a36Sopenharmony_ci hcp->busy = false; 48862306a36Sopenharmony_ci mutex_unlock(&hcp->lock); 48962306a36Sopenharmony_ci} 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_cistatic int hdmi_codec_fill_codec_params(struct snd_soc_dai *dai, 49262306a36Sopenharmony_ci unsigned int sample_width, 49362306a36Sopenharmony_ci unsigned int sample_rate, 49462306a36Sopenharmony_ci unsigned int channels, 49562306a36Sopenharmony_ci struct hdmi_codec_params *hp) 49662306a36Sopenharmony_ci{ 49762306a36Sopenharmony_ci struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai); 49862306a36Sopenharmony_ci int idx = HDMI_CODEC_CHMAP_IDX_UNKNOWN; 49962306a36Sopenharmony_ci u8 ca_id = 0; 50062306a36Sopenharmony_ci bool pcm_audio = !(hcp->iec_status[0] & IEC958_AES0_NONAUDIO); 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci if (pcm_audio) { 50362306a36Sopenharmony_ci /* Select a channel allocation that matches with ELD and pcm channels */ 50462306a36Sopenharmony_ci idx = hdmi_codec_get_ch_alloc_table_idx(hcp, channels); 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci if (idx < 0) { 50762306a36Sopenharmony_ci dev_err(dai->dev, "Not able to map channels to speakers (%d)\n", 50862306a36Sopenharmony_ci idx); 50962306a36Sopenharmony_ci hcp->chmap_idx = HDMI_CODEC_CHMAP_IDX_UNKNOWN; 51062306a36Sopenharmony_ci return idx; 51162306a36Sopenharmony_ci } 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci ca_id = hdmi_codec_channel_alloc[idx].ca_id; 51462306a36Sopenharmony_ci } 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci memset(hp, 0, sizeof(*hp)); 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci hdmi_audio_infoframe_init(&hp->cea); 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci if (pcm_audio) 52162306a36Sopenharmony_ci hp->cea.channels = channels; 52262306a36Sopenharmony_ci else 52362306a36Sopenharmony_ci hp->cea.channels = 0; 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci hp->cea.coding_type = HDMI_AUDIO_CODING_TYPE_STREAM; 52662306a36Sopenharmony_ci hp->cea.sample_size = HDMI_AUDIO_SAMPLE_SIZE_STREAM; 52762306a36Sopenharmony_ci hp->cea.sample_frequency = HDMI_AUDIO_SAMPLE_FREQUENCY_STREAM; 52862306a36Sopenharmony_ci hp->cea.channel_allocation = ca_id; 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci hp->sample_width = sample_width; 53162306a36Sopenharmony_ci hp->sample_rate = sample_rate; 53262306a36Sopenharmony_ci hp->channels = channels; 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci if (pcm_audio) 53562306a36Sopenharmony_ci hcp->chmap_idx = ca_id; 53662306a36Sopenharmony_ci else 53762306a36Sopenharmony_ci hcp->chmap_idx = HDMI_CODEC_CHMAP_IDX_UNKNOWN; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci return 0; 54062306a36Sopenharmony_ci} 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_cistatic int hdmi_codec_hw_params(struct snd_pcm_substream *substream, 54362306a36Sopenharmony_ci struct snd_pcm_hw_params *params, 54462306a36Sopenharmony_ci struct snd_soc_dai *dai) 54562306a36Sopenharmony_ci{ 54662306a36Sopenharmony_ci struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai); 54762306a36Sopenharmony_ci struct hdmi_codec_daifmt *cf = snd_soc_dai_dma_data_get_playback(dai); 54862306a36Sopenharmony_ci struct hdmi_codec_params hp = { 54962306a36Sopenharmony_ci .iec = { 55062306a36Sopenharmony_ci .status = { 0 }, 55162306a36Sopenharmony_ci .subcode = { 0 }, 55262306a36Sopenharmony_ci .pad = 0, 55362306a36Sopenharmony_ci .dig_subframe = { 0 }, 55462306a36Sopenharmony_ci } 55562306a36Sopenharmony_ci }; 55662306a36Sopenharmony_ci int ret; 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci if (!hcp->hcd.ops->hw_params) 55962306a36Sopenharmony_ci return 0; 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci dev_dbg(dai->dev, "%s() width %d rate %d channels %d\n", __func__, 56262306a36Sopenharmony_ci params_width(params), params_rate(params), 56362306a36Sopenharmony_ci params_channels(params)); 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci ret = hdmi_codec_fill_codec_params(dai, 56662306a36Sopenharmony_ci params_width(params), 56762306a36Sopenharmony_ci params_rate(params), 56862306a36Sopenharmony_ci params_channels(params), 56962306a36Sopenharmony_ci &hp); 57062306a36Sopenharmony_ci if (ret < 0) 57162306a36Sopenharmony_ci return ret; 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci memcpy(hp.iec.status, hcp->iec_status, sizeof(hp.iec.status)); 57462306a36Sopenharmony_ci ret = snd_pcm_fill_iec958_consumer_hw_params(params, hp.iec.status, 57562306a36Sopenharmony_ci sizeof(hp.iec.status)); 57662306a36Sopenharmony_ci if (ret < 0) { 57762306a36Sopenharmony_ci dev_err(dai->dev, "Creating IEC958 channel status failed %d\n", 57862306a36Sopenharmony_ci ret); 57962306a36Sopenharmony_ci return ret; 58062306a36Sopenharmony_ci } 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci cf->bit_fmt = params_format(params); 58362306a36Sopenharmony_ci return hcp->hcd.ops->hw_params(dai->dev->parent, hcp->hcd.data, 58462306a36Sopenharmony_ci cf, &hp); 58562306a36Sopenharmony_ci} 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_cistatic int hdmi_codec_prepare(struct snd_pcm_substream *substream, 58862306a36Sopenharmony_ci struct snd_soc_dai *dai) 58962306a36Sopenharmony_ci{ 59062306a36Sopenharmony_ci struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai); 59162306a36Sopenharmony_ci struct hdmi_codec_daifmt *cf = snd_soc_dai_dma_data_get_playback(dai); 59262306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 59362306a36Sopenharmony_ci unsigned int channels = runtime->channels; 59462306a36Sopenharmony_ci unsigned int width = snd_pcm_format_width(runtime->format); 59562306a36Sopenharmony_ci unsigned int rate = runtime->rate; 59662306a36Sopenharmony_ci struct hdmi_codec_params hp; 59762306a36Sopenharmony_ci int ret; 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci if (!hcp->hcd.ops->prepare) 60062306a36Sopenharmony_ci return 0; 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci dev_dbg(dai->dev, "%s() width %d rate %d channels %d\n", __func__, 60362306a36Sopenharmony_ci width, rate, channels); 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci ret = hdmi_codec_fill_codec_params(dai, width, rate, channels, &hp); 60662306a36Sopenharmony_ci if (ret < 0) 60762306a36Sopenharmony_ci return ret; 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci memcpy(hp.iec.status, hcp->iec_status, sizeof(hp.iec.status)); 61062306a36Sopenharmony_ci ret = snd_pcm_fill_iec958_consumer(runtime, hp.iec.status, 61162306a36Sopenharmony_ci sizeof(hp.iec.status)); 61262306a36Sopenharmony_ci if (ret < 0) { 61362306a36Sopenharmony_ci dev_err(dai->dev, "Creating IEC958 channel status failed %d\n", 61462306a36Sopenharmony_ci ret); 61562306a36Sopenharmony_ci return ret; 61662306a36Sopenharmony_ci } 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci cf->bit_fmt = runtime->format; 61962306a36Sopenharmony_ci return hcp->hcd.ops->prepare(dai->dev->parent, hcp->hcd.data, 62062306a36Sopenharmony_ci cf, &hp); 62162306a36Sopenharmony_ci} 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_cistatic int hdmi_codec_i2s_set_fmt(struct snd_soc_dai *dai, 62462306a36Sopenharmony_ci unsigned int fmt) 62562306a36Sopenharmony_ci{ 62662306a36Sopenharmony_ci struct hdmi_codec_daifmt *cf = snd_soc_dai_dma_data_get_playback(dai); 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci /* Reset daifmt */ 62962306a36Sopenharmony_ci memset(cf, 0, sizeof(*cf)); 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { 63262306a36Sopenharmony_ci case SND_SOC_DAIFMT_CBP_CFP: 63362306a36Sopenharmony_ci cf->bit_clk_provider = 1; 63462306a36Sopenharmony_ci cf->frame_clk_provider = 1; 63562306a36Sopenharmony_ci break; 63662306a36Sopenharmony_ci case SND_SOC_DAIFMT_CBC_CFP: 63762306a36Sopenharmony_ci cf->frame_clk_provider = 1; 63862306a36Sopenharmony_ci break; 63962306a36Sopenharmony_ci case SND_SOC_DAIFMT_CBP_CFC: 64062306a36Sopenharmony_ci cf->bit_clk_provider = 1; 64162306a36Sopenharmony_ci break; 64262306a36Sopenharmony_ci case SND_SOC_DAIFMT_CBC_CFC: 64362306a36Sopenharmony_ci break; 64462306a36Sopenharmony_ci default: 64562306a36Sopenharmony_ci return -EINVAL; 64662306a36Sopenharmony_ci } 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_INV_MASK) { 64962306a36Sopenharmony_ci case SND_SOC_DAIFMT_NB_NF: 65062306a36Sopenharmony_ci break; 65162306a36Sopenharmony_ci case SND_SOC_DAIFMT_NB_IF: 65262306a36Sopenharmony_ci cf->frame_clk_inv = 1; 65362306a36Sopenharmony_ci break; 65462306a36Sopenharmony_ci case SND_SOC_DAIFMT_IB_NF: 65562306a36Sopenharmony_ci cf->bit_clk_inv = 1; 65662306a36Sopenharmony_ci break; 65762306a36Sopenharmony_ci case SND_SOC_DAIFMT_IB_IF: 65862306a36Sopenharmony_ci cf->frame_clk_inv = 1; 65962306a36Sopenharmony_ci cf->bit_clk_inv = 1; 66062306a36Sopenharmony_ci break; 66162306a36Sopenharmony_ci } 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 66462306a36Sopenharmony_ci case SND_SOC_DAIFMT_I2S: 66562306a36Sopenharmony_ci cf->fmt = HDMI_I2S; 66662306a36Sopenharmony_ci break; 66762306a36Sopenharmony_ci case SND_SOC_DAIFMT_DSP_A: 66862306a36Sopenharmony_ci cf->fmt = HDMI_DSP_A; 66962306a36Sopenharmony_ci break; 67062306a36Sopenharmony_ci case SND_SOC_DAIFMT_DSP_B: 67162306a36Sopenharmony_ci cf->fmt = HDMI_DSP_B; 67262306a36Sopenharmony_ci break; 67362306a36Sopenharmony_ci case SND_SOC_DAIFMT_RIGHT_J: 67462306a36Sopenharmony_ci cf->fmt = HDMI_RIGHT_J; 67562306a36Sopenharmony_ci break; 67662306a36Sopenharmony_ci case SND_SOC_DAIFMT_LEFT_J: 67762306a36Sopenharmony_ci cf->fmt = HDMI_LEFT_J; 67862306a36Sopenharmony_ci break; 67962306a36Sopenharmony_ci case SND_SOC_DAIFMT_AC97: 68062306a36Sopenharmony_ci cf->fmt = HDMI_AC97; 68162306a36Sopenharmony_ci break; 68262306a36Sopenharmony_ci default: 68362306a36Sopenharmony_ci dev_err(dai->dev, "Invalid DAI interface format\n"); 68462306a36Sopenharmony_ci return -EINVAL; 68562306a36Sopenharmony_ci } 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci return 0; 68862306a36Sopenharmony_ci} 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_cistatic int hdmi_codec_mute(struct snd_soc_dai *dai, int mute, int direction) 69162306a36Sopenharmony_ci{ 69262306a36Sopenharmony_ci struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai); 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci /* 69562306a36Sopenharmony_ci * ignore if direction was CAPTURE 69662306a36Sopenharmony_ci * and it had .no_capture_mute flag 69762306a36Sopenharmony_ci * see 69862306a36Sopenharmony_ci * snd_soc_dai_digital_mute() 69962306a36Sopenharmony_ci */ 70062306a36Sopenharmony_ci if (hcp->hcd.ops->mute_stream && 70162306a36Sopenharmony_ci (direction == SNDRV_PCM_STREAM_PLAYBACK || 70262306a36Sopenharmony_ci !hcp->hcd.ops->no_capture_mute)) 70362306a36Sopenharmony_ci return hcp->hcd.ops->mute_stream(dai->dev->parent, 70462306a36Sopenharmony_ci hcp->hcd.data, 70562306a36Sopenharmony_ci mute, direction); 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci return -ENOTSUPP; 70862306a36Sopenharmony_ci} 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci/* 71162306a36Sopenharmony_ci * This driver can select all SND_SOC_DAIFMT_CBx_CFx, 71262306a36Sopenharmony_ci * but need to be selected from Sound Card, not be auto selected. 71362306a36Sopenharmony_ci * Because it might be used from other driver. 71462306a36Sopenharmony_ci * For example, 71562306a36Sopenharmony_ci * ${LINUX}/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c 71662306a36Sopenharmony_ci */ 71762306a36Sopenharmony_cistatic u64 hdmi_codec_formats = 71862306a36Sopenharmony_ci SND_SOC_POSSIBLE_DAIFMT_NB_NF | 71962306a36Sopenharmony_ci SND_SOC_POSSIBLE_DAIFMT_NB_IF | 72062306a36Sopenharmony_ci SND_SOC_POSSIBLE_DAIFMT_IB_NF | 72162306a36Sopenharmony_ci SND_SOC_POSSIBLE_DAIFMT_IB_IF | 72262306a36Sopenharmony_ci SND_SOC_POSSIBLE_DAIFMT_I2S | 72362306a36Sopenharmony_ci SND_SOC_POSSIBLE_DAIFMT_DSP_A | 72462306a36Sopenharmony_ci SND_SOC_POSSIBLE_DAIFMT_DSP_B | 72562306a36Sopenharmony_ci SND_SOC_POSSIBLE_DAIFMT_RIGHT_J | 72662306a36Sopenharmony_ci SND_SOC_POSSIBLE_DAIFMT_LEFT_J | 72762306a36Sopenharmony_ci SND_SOC_POSSIBLE_DAIFMT_AC97; 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci#define HDMI_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\ 73062306a36Sopenharmony_ci SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |\ 73162306a36Sopenharmony_ci SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |\ 73262306a36Sopenharmony_ci SNDRV_PCM_RATE_192000) 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci#define SPDIF_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ 73562306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_LE) 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci/* 73862306a36Sopenharmony_ci * This list is only for formats allowed on the I2S bus. So there is 73962306a36Sopenharmony_ci * some formats listed that are not supported by HDMI interface. For 74062306a36Sopenharmony_ci * instance allowing the 32-bit formats enables 24-precision with CPU 74162306a36Sopenharmony_ci * DAIs that do not support 24-bit formats. If the extra formats cause 74262306a36Sopenharmony_ci * problems, we should add the video side driver an option to disable 74362306a36Sopenharmony_ci * them. 74462306a36Sopenharmony_ci */ 74562306a36Sopenharmony_ci#define I2S_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ 74662306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_LE |\ 74762306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE) 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_cistatic struct snd_kcontrol_new hdmi_codec_controls[] = { 75062306a36Sopenharmony_ci { 75162306a36Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READ, 75262306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_PCM, 75362306a36Sopenharmony_ci .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, MASK), 75462306a36Sopenharmony_ci .info = hdmi_codec_iec958_info, 75562306a36Sopenharmony_ci .get = hdmi_codec_iec958_mask_get, 75662306a36Sopenharmony_ci }, 75762306a36Sopenharmony_ci { 75862306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_PCM, 75962306a36Sopenharmony_ci .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT), 76062306a36Sopenharmony_ci .info = hdmi_codec_iec958_info, 76162306a36Sopenharmony_ci .get = hdmi_codec_iec958_default_get, 76262306a36Sopenharmony_ci .put = hdmi_codec_iec958_default_put, 76362306a36Sopenharmony_ci }, 76462306a36Sopenharmony_ci { 76562306a36Sopenharmony_ci .access = (SNDRV_CTL_ELEM_ACCESS_READ | 76662306a36Sopenharmony_ci SNDRV_CTL_ELEM_ACCESS_VOLATILE), 76762306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_PCM, 76862306a36Sopenharmony_ci .name = "ELD", 76962306a36Sopenharmony_ci .info = hdmi_eld_ctl_info, 77062306a36Sopenharmony_ci .get = hdmi_eld_ctl_get, 77162306a36Sopenharmony_ci }, 77262306a36Sopenharmony_ci}; 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_cistatic int hdmi_codec_pcm_new(struct snd_soc_pcm_runtime *rtd, 77562306a36Sopenharmony_ci struct snd_soc_dai *dai) 77662306a36Sopenharmony_ci{ 77762306a36Sopenharmony_ci struct snd_soc_dai_driver *drv = dai->driver; 77862306a36Sopenharmony_ci struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai); 77962306a36Sopenharmony_ci unsigned int i; 78062306a36Sopenharmony_ci int ret; 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci ret = snd_pcm_add_chmap_ctls(rtd->pcm, SNDRV_PCM_STREAM_PLAYBACK, 78362306a36Sopenharmony_ci NULL, drv->playback.channels_max, 0, 78462306a36Sopenharmony_ci &hcp->chmap_info); 78562306a36Sopenharmony_ci if (ret < 0) 78662306a36Sopenharmony_ci return ret; 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci /* override handlers */ 78962306a36Sopenharmony_ci hcp->chmap_info->private_data = hcp; 79062306a36Sopenharmony_ci hcp->chmap_info->kctl->get = hdmi_codec_chmap_ctl_get; 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci /* default chmap supported is stereo */ 79362306a36Sopenharmony_ci hcp->chmap_info->chmap = hdmi_codec_stereo_chmaps; 79462306a36Sopenharmony_ci hcp->chmap_idx = HDMI_CODEC_CHMAP_IDX_UNKNOWN; 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(hdmi_codec_controls); i++) { 79762306a36Sopenharmony_ci struct snd_kcontrol *kctl; 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci /* add ELD ctl with the device number corresponding to the PCM stream */ 80062306a36Sopenharmony_ci kctl = snd_ctl_new1(&hdmi_codec_controls[i], dai->component); 80162306a36Sopenharmony_ci if (!kctl) 80262306a36Sopenharmony_ci return -ENOMEM; 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci kctl->id.device = rtd->pcm->device; 80562306a36Sopenharmony_ci ret = snd_ctl_add(rtd->card->snd_card, kctl); 80662306a36Sopenharmony_ci if (ret < 0) 80762306a36Sopenharmony_ci return ret; 80862306a36Sopenharmony_ci } 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci return 0; 81162306a36Sopenharmony_ci} 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_cistatic int hdmi_dai_probe(struct snd_soc_dai *dai) 81462306a36Sopenharmony_ci{ 81562306a36Sopenharmony_ci struct snd_soc_dapm_context *dapm; 81662306a36Sopenharmony_ci struct hdmi_codec_daifmt *daifmt; 81762306a36Sopenharmony_ci struct snd_soc_dapm_route route[] = { 81862306a36Sopenharmony_ci { 81962306a36Sopenharmony_ci .sink = "TX", 82062306a36Sopenharmony_ci .source = dai->driver->playback.stream_name, 82162306a36Sopenharmony_ci }, 82262306a36Sopenharmony_ci { 82362306a36Sopenharmony_ci .sink = dai->driver->capture.stream_name, 82462306a36Sopenharmony_ci .source = "RX", 82562306a36Sopenharmony_ci }, 82662306a36Sopenharmony_ci }; 82762306a36Sopenharmony_ci int ret, i; 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci dapm = snd_soc_component_get_dapm(dai->component); 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci /* One of the directions might be omitted for unidirectional DAIs */ 83262306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(route); i++) { 83362306a36Sopenharmony_ci if (!route[i].source || !route[i].sink) 83462306a36Sopenharmony_ci continue; 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci ret = snd_soc_dapm_add_routes(dapm, &route[i], 1); 83762306a36Sopenharmony_ci if (ret) 83862306a36Sopenharmony_ci return ret; 83962306a36Sopenharmony_ci } 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci daifmt = devm_kzalloc(dai->dev, sizeof(*daifmt), GFP_KERNEL); 84262306a36Sopenharmony_ci if (!daifmt) 84362306a36Sopenharmony_ci return -ENOMEM; 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci snd_soc_dai_dma_data_set_playback(dai, daifmt); 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci return 0; 84862306a36Sopenharmony_ci} 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_cistatic void hdmi_codec_jack_report(struct hdmi_codec_priv *hcp, 85162306a36Sopenharmony_ci unsigned int jack_status) 85262306a36Sopenharmony_ci{ 85362306a36Sopenharmony_ci if (jack_status != hcp->jack_status) { 85462306a36Sopenharmony_ci if (hcp->jack) 85562306a36Sopenharmony_ci snd_soc_jack_report(hcp->jack, jack_status, SND_JACK_LINEOUT); 85662306a36Sopenharmony_ci hcp->jack_status = jack_status; 85762306a36Sopenharmony_ci } 85862306a36Sopenharmony_ci} 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_cistatic void plugged_cb(struct device *dev, bool plugged) 86162306a36Sopenharmony_ci{ 86262306a36Sopenharmony_ci struct hdmi_codec_priv *hcp = dev_get_drvdata(dev); 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci if (plugged) { 86562306a36Sopenharmony_ci if (hcp->hcd.ops->get_eld) { 86662306a36Sopenharmony_ci hcp->hcd.ops->get_eld(dev->parent, hcp->hcd.data, 86762306a36Sopenharmony_ci hcp->eld, sizeof(hcp->eld)); 86862306a36Sopenharmony_ci } 86962306a36Sopenharmony_ci hdmi_codec_jack_report(hcp, SND_JACK_LINEOUT); 87062306a36Sopenharmony_ci } else { 87162306a36Sopenharmony_ci hdmi_codec_jack_report(hcp, 0); 87262306a36Sopenharmony_ci memset(hcp->eld, 0, sizeof(hcp->eld)); 87362306a36Sopenharmony_ci } 87462306a36Sopenharmony_ci} 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_cistatic int hdmi_codec_set_jack(struct snd_soc_component *component, 87762306a36Sopenharmony_ci struct snd_soc_jack *jack, 87862306a36Sopenharmony_ci void *data) 87962306a36Sopenharmony_ci{ 88062306a36Sopenharmony_ci struct hdmi_codec_priv *hcp = snd_soc_component_get_drvdata(component); 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci if (hcp->hcd.ops->hook_plugged_cb) { 88362306a36Sopenharmony_ci hcp->jack = jack; 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_ci /* 88662306a36Sopenharmony_ci * Report the initial jack status which may have been provided 88762306a36Sopenharmony_ci * by the parent hdmi driver while the hpd hook was registered. 88862306a36Sopenharmony_ci */ 88962306a36Sopenharmony_ci snd_soc_jack_report(jack, hcp->jack_status, SND_JACK_LINEOUT); 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci return 0; 89262306a36Sopenharmony_ci } 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci return -ENOTSUPP; 89562306a36Sopenharmony_ci} 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_cistatic int hdmi_dai_spdif_probe(struct snd_soc_dai *dai) 89862306a36Sopenharmony_ci{ 89962306a36Sopenharmony_ci struct hdmi_codec_daifmt *cf; 90062306a36Sopenharmony_ci int ret; 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_ci ret = hdmi_dai_probe(dai); 90362306a36Sopenharmony_ci if (ret) 90462306a36Sopenharmony_ci return ret; 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_ci cf = snd_soc_dai_dma_data_get_playback(dai); 90762306a36Sopenharmony_ci cf->fmt = HDMI_SPDIF; 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_ci return 0; 91062306a36Sopenharmony_ci} 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_cistatic const struct snd_soc_dai_ops hdmi_codec_i2s_dai_ops = { 91362306a36Sopenharmony_ci .probe = hdmi_dai_probe, 91462306a36Sopenharmony_ci .startup = hdmi_codec_startup, 91562306a36Sopenharmony_ci .shutdown = hdmi_codec_shutdown, 91662306a36Sopenharmony_ci .hw_params = hdmi_codec_hw_params, 91762306a36Sopenharmony_ci .prepare = hdmi_codec_prepare, 91862306a36Sopenharmony_ci .set_fmt = hdmi_codec_i2s_set_fmt, 91962306a36Sopenharmony_ci .mute_stream = hdmi_codec_mute, 92062306a36Sopenharmony_ci .pcm_new = hdmi_codec_pcm_new, 92162306a36Sopenharmony_ci .auto_selectable_formats = &hdmi_codec_formats, 92262306a36Sopenharmony_ci .num_auto_selectable_formats = 1, 92362306a36Sopenharmony_ci}; 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_cistatic const struct snd_soc_dai_ops hdmi_codec_spdif_dai_ops = { 92662306a36Sopenharmony_ci .probe = hdmi_dai_spdif_probe, 92762306a36Sopenharmony_ci .startup = hdmi_codec_startup, 92862306a36Sopenharmony_ci .shutdown = hdmi_codec_shutdown, 92962306a36Sopenharmony_ci .hw_params = hdmi_codec_hw_params, 93062306a36Sopenharmony_ci .mute_stream = hdmi_codec_mute, 93162306a36Sopenharmony_ci .pcm_new = hdmi_codec_pcm_new, 93262306a36Sopenharmony_ci}; 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_cistatic const struct snd_soc_dai_driver hdmi_i2s_dai = { 93562306a36Sopenharmony_ci .name = "i2s-hifi", 93662306a36Sopenharmony_ci .id = DAI_ID_I2S, 93762306a36Sopenharmony_ci .playback = { 93862306a36Sopenharmony_ci .stream_name = "I2S Playback", 93962306a36Sopenharmony_ci .channels_min = 2, 94062306a36Sopenharmony_ci .channels_max = 8, 94162306a36Sopenharmony_ci .rates = HDMI_RATES, 94262306a36Sopenharmony_ci .formats = I2S_FORMATS, 94362306a36Sopenharmony_ci .sig_bits = 24, 94462306a36Sopenharmony_ci }, 94562306a36Sopenharmony_ci .capture = { 94662306a36Sopenharmony_ci .stream_name = "Capture", 94762306a36Sopenharmony_ci .channels_min = 2, 94862306a36Sopenharmony_ci .channels_max = 8, 94962306a36Sopenharmony_ci .rates = HDMI_RATES, 95062306a36Sopenharmony_ci .formats = I2S_FORMATS, 95162306a36Sopenharmony_ci .sig_bits = 24, 95262306a36Sopenharmony_ci }, 95362306a36Sopenharmony_ci .ops = &hdmi_codec_i2s_dai_ops, 95462306a36Sopenharmony_ci}; 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_cistatic const struct snd_soc_dai_driver hdmi_spdif_dai = { 95762306a36Sopenharmony_ci .name = "spdif-hifi", 95862306a36Sopenharmony_ci .id = DAI_ID_SPDIF, 95962306a36Sopenharmony_ci .playback = { 96062306a36Sopenharmony_ci .stream_name = "SPDIF Playback", 96162306a36Sopenharmony_ci .channels_min = 2, 96262306a36Sopenharmony_ci .channels_max = 2, 96362306a36Sopenharmony_ci .rates = HDMI_RATES, 96462306a36Sopenharmony_ci .formats = SPDIF_FORMATS, 96562306a36Sopenharmony_ci }, 96662306a36Sopenharmony_ci .capture = { 96762306a36Sopenharmony_ci .stream_name = "Capture", 96862306a36Sopenharmony_ci .channels_min = 2, 96962306a36Sopenharmony_ci .channels_max = 2, 97062306a36Sopenharmony_ci .rates = HDMI_RATES, 97162306a36Sopenharmony_ci .formats = SPDIF_FORMATS, 97262306a36Sopenharmony_ci }, 97362306a36Sopenharmony_ci .ops = &hdmi_codec_spdif_dai_ops, 97462306a36Sopenharmony_ci}; 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_cistatic int hdmi_of_xlate_dai_id(struct snd_soc_component *component, 97762306a36Sopenharmony_ci struct device_node *endpoint) 97862306a36Sopenharmony_ci{ 97962306a36Sopenharmony_ci struct hdmi_codec_priv *hcp = snd_soc_component_get_drvdata(component); 98062306a36Sopenharmony_ci int ret = -ENOTSUPP; /* see snd_soc_get_dai_id() */ 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci if (hcp->hcd.ops->get_dai_id) 98362306a36Sopenharmony_ci ret = hcp->hcd.ops->get_dai_id(component, endpoint); 98462306a36Sopenharmony_ci 98562306a36Sopenharmony_ci return ret; 98662306a36Sopenharmony_ci} 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_cistatic int hdmi_probe(struct snd_soc_component *component) 98962306a36Sopenharmony_ci{ 99062306a36Sopenharmony_ci struct hdmi_codec_priv *hcp = snd_soc_component_get_drvdata(component); 99162306a36Sopenharmony_ci int ret = 0; 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_ci if (hcp->hcd.ops->hook_plugged_cb) { 99462306a36Sopenharmony_ci ret = hcp->hcd.ops->hook_plugged_cb(component->dev->parent, 99562306a36Sopenharmony_ci hcp->hcd.data, 99662306a36Sopenharmony_ci plugged_cb, 99762306a36Sopenharmony_ci component->dev); 99862306a36Sopenharmony_ci } 99962306a36Sopenharmony_ci 100062306a36Sopenharmony_ci return ret; 100162306a36Sopenharmony_ci} 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_cistatic void hdmi_remove(struct snd_soc_component *component) 100462306a36Sopenharmony_ci{ 100562306a36Sopenharmony_ci struct hdmi_codec_priv *hcp = snd_soc_component_get_drvdata(component); 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_ci if (hcp->hcd.ops->hook_plugged_cb) 100862306a36Sopenharmony_ci hcp->hcd.ops->hook_plugged_cb(component->dev->parent, 100962306a36Sopenharmony_ci hcp->hcd.data, NULL, NULL); 101062306a36Sopenharmony_ci} 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_cistatic const struct snd_soc_component_driver hdmi_driver = { 101362306a36Sopenharmony_ci .probe = hdmi_probe, 101462306a36Sopenharmony_ci .remove = hdmi_remove, 101562306a36Sopenharmony_ci .dapm_widgets = hdmi_widgets, 101662306a36Sopenharmony_ci .num_dapm_widgets = ARRAY_SIZE(hdmi_widgets), 101762306a36Sopenharmony_ci .of_xlate_dai_id = hdmi_of_xlate_dai_id, 101862306a36Sopenharmony_ci .idle_bias_on = 1, 101962306a36Sopenharmony_ci .use_pmdown_time = 1, 102062306a36Sopenharmony_ci .endianness = 1, 102162306a36Sopenharmony_ci .set_jack = hdmi_codec_set_jack, 102262306a36Sopenharmony_ci}; 102362306a36Sopenharmony_ci 102462306a36Sopenharmony_cistatic int hdmi_codec_probe(struct platform_device *pdev) 102562306a36Sopenharmony_ci{ 102662306a36Sopenharmony_ci struct hdmi_codec_pdata *hcd = pdev->dev.platform_data; 102762306a36Sopenharmony_ci struct snd_soc_dai_driver *daidrv; 102862306a36Sopenharmony_ci struct device *dev = &pdev->dev; 102962306a36Sopenharmony_ci struct hdmi_codec_priv *hcp; 103062306a36Sopenharmony_ci int dai_count, i = 0; 103162306a36Sopenharmony_ci int ret; 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_ci if (!hcd) { 103462306a36Sopenharmony_ci dev_err(dev, "%s: No platform data\n", __func__); 103562306a36Sopenharmony_ci return -EINVAL; 103662306a36Sopenharmony_ci } 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_ci dai_count = hcd->i2s + hcd->spdif; 103962306a36Sopenharmony_ci if (dai_count < 1 || !hcd->ops || 104062306a36Sopenharmony_ci (!hcd->ops->hw_params && !hcd->ops->prepare) || 104162306a36Sopenharmony_ci !hcd->ops->audio_shutdown) { 104262306a36Sopenharmony_ci dev_err(dev, "%s: Invalid parameters\n", __func__); 104362306a36Sopenharmony_ci return -EINVAL; 104462306a36Sopenharmony_ci } 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_ci hcp = devm_kzalloc(dev, sizeof(*hcp), GFP_KERNEL); 104762306a36Sopenharmony_ci if (!hcp) 104862306a36Sopenharmony_ci return -ENOMEM; 104962306a36Sopenharmony_ci 105062306a36Sopenharmony_ci hcp->hcd = *hcd; 105162306a36Sopenharmony_ci mutex_init(&hcp->lock); 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_ci ret = snd_pcm_create_iec958_consumer_default(hcp->iec_status, 105462306a36Sopenharmony_ci sizeof(hcp->iec_status)); 105562306a36Sopenharmony_ci if (ret < 0) 105662306a36Sopenharmony_ci return ret; 105762306a36Sopenharmony_ci 105862306a36Sopenharmony_ci daidrv = devm_kcalloc(dev, dai_count, sizeof(*daidrv), GFP_KERNEL); 105962306a36Sopenharmony_ci if (!daidrv) 106062306a36Sopenharmony_ci return -ENOMEM; 106162306a36Sopenharmony_ci 106262306a36Sopenharmony_ci if (hcd->i2s) { 106362306a36Sopenharmony_ci daidrv[i] = hdmi_i2s_dai; 106462306a36Sopenharmony_ci daidrv[i].playback.channels_max = hcd->max_i2s_channels; 106562306a36Sopenharmony_ci if (hcd->no_i2s_playback) 106662306a36Sopenharmony_ci memset(&daidrv[i].playback, 0, 106762306a36Sopenharmony_ci sizeof(daidrv[i].playback)); 106862306a36Sopenharmony_ci if (hcd->no_i2s_capture) 106962306a36Sopenharmony_ci memset(&daidrv[i].capture, 0, 107062306a36Sopenharmony_ci sizeof(daidrv[i].capture)); 107162306a36Sopenharmony_ci i++; 107262306a36Sopenharmony_ci } 107362306a36Sopenharmony_ci 107462306a36Sopenharmony_ci if (hcd->spdif) { 107562306a36Sopenharmony_ci daidrv[i] = hdmi_spdif_dai; 107662306a36Sopenharmony_ci if (hcd->no_spdif_playback) 107762306a36Sopenharmony_ci memset(&daidrv[i].playback, 0, 107862306a36Sopenharmony_ci sizeof(daidrv[i].playback)); 107962306a36Sopenharmony_ci if (hcd->no_spdif_capture) 108062306a36Sopenharmony_ci memset(&daidrv[i].capture, 0, 108162306a36Sopenharmony_ci sizeof(daidrv[i].capture)); 108262306a36Sopenharmony_ci } 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_ci dev_set_drvdata(dev, hcp); 108562306a36Sopenharmony_ci 108662306a36Sopenharmony_ci ret = devm_snd_soc_register_component(dev, &hdmi_driver, daidrv, 108762306a36Sopenharmony_ci dai_count); 108862306a36Sopenharmony_ci if (ret) { 108962306a36Sopenharmony_ci dev_err(dev, "%s: snd_soc_register_component() failed (%d)\n", 109062306a36Sopenharmony_ci __func__, ret); 109162306a36Sopenharmony_ci return ret; 109262306a36Sopenharmony_ci } 109362306a36Sopenharmony_ci return 0; 109462306a36Sopenharmony_ci} 109562306a36Sopenharmony_ci 109662306a36Sopenharmony_cistatic struct platform_driver hdmi_codec_driver = { 109762306a36Sopenharmony_ci .driver = { 109862306a36Sopenharmony_ci .name = HDMI_CODEC_DRV_NAME, 109962306a36Sopenharmony_ci }, 110062306a36Sopenharmony_ci .probe = hdmi_codec_probe, 110162306a36Sopenharmony_ci}; 110262306a36Sopenharmony_ci 110362306a36Sopenharmony_cimodule_platform_driver(hdmi_codec_driver); 110462306a36Sopenharmony_ci 110562306a36Sopenharmony_ciMODULE_AUTHOR("Jyri Sarha <jsarha@ti.com>"); 110662306a36Sopenharmony_ciMODULE_DESCRIPTION("HDMI Audio Codec Driver"); 110762306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 110862306a36Sopenharmony_ciMODULE_ALIAS("platform:" HDMI_CODEC_DRV_NAME); 1109