162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * hdac_hdmi.c - ASoc HDA-HDMI codec driver for Intel platforms 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2014-2015 Intel Corp 662306a36Sopenharmony_ci * Author: Samreen Nilofer <samreen.nilofer@intel.com> 762306a36Sopenharmony_ci * Subhransu S. Prusty <subhransu.s.prusty@intel.com> 862306a36Sopenharmony_ci * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <linux/init.h> 1462306a36Sopenharmony_ci#include <linux/delay.h> 1562306a36Sopenharmony_ci#include <linux/module.h> 1662306a36Sopenharmony_ci#include <linux/pm_runtime.h> 1762306a36Sopenharmony_ci#include <linux/hdmi.h> 1862306a36Sopenharmony_ci#include <drm/drm_edid.h> 1962306a36Sopenharmony_ci#include <sound/pcm_params.h> 2062306a36Sopenharmony_ci#include <sound/jack.h> 2162306a36Sopenharmony_ci#include <sound/soc.h> 2262306a36Sopenharmony_ci#include <sound/hdaudio_ext.h> 2362306a36Sopenharmony_ci#include <sound/hda_i915.h> 2462306a36Sopenharmony_ci#include <sound/pcm_drm_eld.h> 2562306a36Sopenharmony_ci#include <sound/hda_chmap.h> 2662306a36Sopenharmony_ci#include "../../hda/local.h" 2762306a36Sopenharmony_ci#include "hdac_hdmi.h" 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#define NAME_SIZE 32 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#define AMP_OUT_MUTE 0xb080 3262306a36Sopenharmony_ci#define AMP_OUT_UNMUTE 0xb000 3362306a36Sopenharmony_ci#define PIN_OUT (AC_PINCTL_OUT_EN) 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#define HDA_MAX_CONNECTIONS 32 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#define HDA_MAX_CVTS 3 3862306a36Sopenharmony_ci#define HDA_MAX_PORTS 3 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci#define ELD_MAX_SIZE 256 4162306a36Sopenharmony_ci#define ELD_FIXED_BYTES 20 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci#define ELD_VER_CEA_861D 2 4462306a36Sopenharmony_ci#define ELD_VER_PARTIAL 31 4562306a36Sopenharmony_ci#define ELD_MAX_MNL 16 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistruct hdac_hdmi_cvt_params { 4862306a36Sopenharmony_ci unsigned int channels_min; 4962306a36Sopenharmony_ci unsigned int channels_max; 5062306a36Sopenharmony_ci u32 rates; 5162306a36Sopenharmony_ci u64 formats; 5262306a36Sopenharmony_ci unsigned int maxbps; 5362306a36Sopenharmony_ci}; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistruct hdac_hdmi_cvt { 5662306a36Sopenharmony_ci struct list_head head; 5762306a36Sopenharmony_ci hda_nid_t nid; 5862306a36Sopenharmony_ci const char *name; 5962306a36Sopenharmony_ci struct hdac_hdmi_cvt_params params; 6062306a36Sopenharmony_ci}; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci/* Currently only spk_alloc, more to be added */ 6362306a36Sopenharmony_cistruct hdac_hdmi_parsed_eld { 6462306a36Sopenharmony_ci u8 spk_alloc; 6562306a36Sopenharmony_ci}; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cistruct hdac_hdmi_eld { 6862306a36Sopenharmony_ci bool monitor_present; 6962306a36Sopenharmony_ci bool eld_valid; 7062306a36Sopenharmony_ci int eld_size; 7162306a36Sopenharmony_ci char eld_buffer[ELD_MAX_SIZE]; 7262306a36Sopenharmony_ci struct hdac_hdmi_parsed_eld info; 7362306a36Sopenharmony_ci}; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistruct hdac_hdmi_pin { 7662306a36Sopenharmony_ci struct list_head head; 7762306a36Sopenharmony_ci hda_nid_t nid; 7862306a36Sopenharmony_ci bool mst_capable; 7962306a36Sopenharmony_ci struct hdac_hdmi_port *ports; 8062306a36Sopenharmony_ci int num_ports; 8162306a36Sopenharmony_ci struct hdac_device *hdev; 8262306a36Sopenharmony_ci}; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_cistruct hdac_hdmi_port { 8562306a36Sopenharmony_ci struct list_head head; 8662306a36Sopenharmony_ci int id; 8762306a36Sopenharmony_ci struct hdac_hdmi_pin *pin; 8862306a36Sopenharmony_ci int num_mux_nids; 8962306a36Sopenharmony_ci hda_nid_t mux_nids[HDA_MAX_CONNECTIONS]; 9062306a36Sopenharmony_ci struct hdac_hdmi_eld eld; 9162306a36Sopenharmony_ci const char *jack_pin; 9262306a36Sopenharmony_ci bool is_connect; 9362306a36Sopenharmony_ci struct snd_soc_dapm_context *dapm; 9462306a36Sopenharmony_ci const char *output_pin; 9562306a36Sopenharmony_ci struct work_struct dapm_work; 9662306a36Sopenharmony_ci}; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cistruct hdac_hdmi_pcm { 9962306a36Sopenharmony_ci struct list_head head; 10062306a36Sopenharmony_ci int pcm_id; 10162306a36Sopenharmony_ci struct list_head port_list; 10262306a36Sopenharmony_ci struct hdac_hdmi_cvt *cvt; 10362306a36Sopenharmony_ci struct snd_soc_jack *jack; 10462306a36Sopenharmony_ci int stream_tag; 10562306a36Sopenharmony_ci int channels; 10662306a36Sopenharmony_ci int format; 10762306a36Sopenharmony_ci bool chmap_set; 10862306a36Sopenharmony_ci unsigned char chmap[8]; /* ALSA API channel-map */ 10962306a36Sopenharmony_ci struct mutex lock; 11062306a36Sopenharmony_ci int jack_event; 11162306a36Sopenharmony_ci struct snd_kcontrol *eld_ctl; 11262306a36Sopenharmony_ci}; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_cistruct hdac_hdmi_dai_port_map { 11562306a36Sopenharmony_ci int dai_id; 11662306a36Sopenharmony_ci struct hdac_hdmi_port *port; 11762306a36Sopenharmony_ci struct hdac_hdmi_cvt *cvt; 11862306a36Sopenharmony_ci}; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_cistruct hdac_hdmi_drv_data { 12162306a36Sopenharmony_ci unsigned int vendor_nid; 12262306a36Sopenharmony_ci}; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_cistruct hdac_hdmi_priv { 12562306a36Sopenharmony_ci struct hdac_device *hdev; 12662306a36Sopenharmony_ci struct snd_soc_component *component; 12762306a36Sopenharmony_ci struct snd_card *card; 12862306a36Sopenharmony_ci struct hdac_hdmi_dai_port_map dai_map[HDA_MAX_CVTS]; 12962306a36Sopenharmony_ci struct list_head pin_list; 13062306a36Sopenharmony_ci struct list_head cvt_list; 13162306a36Sopenharmony_ci struct list_head pcm_list; 13262306a36Sopenharmony_ci int num_pin; 13362306a36Sopenharmony_ci int num_cvt; 13462306a36Sopenharmony_ci int num_ports; 13562306a36Sopenharmony_ci struct mutex pin_mutex; 13662306a36Sopenharmony_ci struct hdac_chmap chmap; 13762306a36Sopenharmony_ci struct hdac_hdmi_drv_data *drv_data; 13862306a36Sopenharmony_ci struct snd_soc_dai_driver *dai_drv; 13962306a36Sopenharmony_ci}; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci#define hdev_to_hdmi_priv(_hdev) dev_get_drvdata(&(_hdev)->dev) 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_cistatic struct hdac_hdmi_pcm * 14462306a36Sopenharmony_cihdac_hdmi_get_pcm_from_cvt(struct hdac_hdmi_priv *hdmi, 14562306a36Sopenharmony_ci struct hdac_hdmi_cvt *cvt) 14662306a36Sopenharmony_ci{ 14762306a36Sopenharmony_ci struct hdac_hdmi_pcm *pcm; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci list_for_each_entry(pcm, &hdmi->pcm_list, head) { 15062306a36Sopenharmony_ci if (pcm->cvt == cvt) 15162306a36Sopenharmony_ci return pcm; 15262306a36Sopenharmony_ci } 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci return NULL; 15562306a36Sopenharmony_ci} 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_cistatic void hdac_hdmi_jack_report(struct hdac_hdmi_pcm *pcm, 15862306a36Sopenharmony_ci struct hdac_hdmi_port *port, bool is_connect) 15962306a36Sopenharmony_ci{ 16062306a36Sopenharmony_ci struct hdac_device *hdev = port->pin->hdev; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci port->is_connect = is_connect; 16362306a36Sopenharmony_ci if (is_connect) { 16462306a36Sopenharmony_ci /* 16562306a36Sopenharmony_ci * Report Jack connect event when a device is connected 16662306a36Sopenharmony_ci * for the first time where same PCM is attached to multiple 16762306a36Sopenharmony_ci * ports. 16862306a36Sopenharmony_ci */ 16962306a36Sopenharmony_ci if (pcm->jack_event == 0) { 17062306a36Sopenharmony_ci dev_dbg(&hdev->dev, 17162306a36Sopenharmony_ci "jack report for pcm=%d\n", 17262306a36Sopenharmony_ci pcm->pcm_id); 17362306a36Sopenharmony_ci snd_soc_jack_report(pcm->jack, SND_JACK_AVOUT, 17462306a36Sopenharmony_ci SND_JACK_AVOUT); 17562306a36Sopenharmony_ci } 17662306a36Sopenharmony_ci pcm->jack_event++; 17762306a36Sopenharmony_ci } else { 17862306a36Sopenharmony_ci /* 17962306a36Sopenharmony_ci * Report Jack disconnect event when a device is disconnected 18062306a36Sopenharmony_ci * is the only last connected device when same PCM is attached 18162306a36Sopenharmony_ci * to multiple ports. 18262306a36Sopenharmony_ci */ 18362306a36Sopenharmony_ci if (pcm->jack_event == 1) 18462306a36Sopenharmony_ci snd_soc_jack_report(pcm->jack, 0, SND_JACK_AVOUT); 18562306a36Sopenharmony_ci if (pcm->jack_event > 0) 18662306a36Sopenharmony_ci pcm->jack_event--; 18762306a36Sopenharmony_ci } 18862306a36Sopenharmony_ci} 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_cistatic void hdac_hdmi_port_dapm_update(struct hdac_hdmi_port *port) 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci if (port->is_connect) 19362306a36Sopenharmony_ci snd_soc_dapm_enable_pin(port->dapm, port->jack_pin); 19462306a36Sopenharmony_ci else 19562306a36Sopenharmony_ci snd_soc_dapm_disable_pin(port->dapm, port->jack_pin); 19662306a36Sopenharmony_ci snd_soc_dapm_sync(port->dapm); 19762306a36Sopenharmony_ci} 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_cistatic void hdac_hdmi_jack_dapm_work(struct work_struct *work) 20062306a36Sopenharmony_ci{ 20162306a36Sopenharmony_ci struct hdac_hdmi_port *port; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci port = container_of(work, struct hdac_hdmi_port, dapm_work); 20462306a36Sopenharmony_ci hdac_hdmi_port_dapm_update(port); 20562306a36Sopenharmony_ci} 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_cistatic void hdac_hdmi_jack_report_sync(struct hdac_hdmi_pcm *pcm, 20862306a36Sopenharmony_ci struct hdac_hdmi_port *port, bool is_connect) 20962306a36Sopenharmony_ci{ 21062306a36Sopenharmony_ci hdac_hdmi_jack_report(pcm, port, is_connect); 21162306a36Sopenharmony_ci hdac_hdmi_port_dapm_update(port); 21262306a36Sopenharmony_ci} 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci/* MST supported verbs */ 21562306a36Sopenharmony_ci/* 21662306a36Sopenharmony_ci * Get the no devices that can be connected to a port on the Pin widget. 21762306a36Sopenharmony_ci */ 21862306a36Sopenharmony_cistatic int hdac_hdmi_get_port_len(struct hdac_device *hdev, hda_nid_t nid) 21962306a36Sopenharmony_ci{ 22062306a36Sopenharmony_ci unsigned int caps; 22162306a36Sopenharmony_ci unsigned int type, param; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci caps = get_wcaps(hdev, nid); 22462306a36Sopenharmony_ci type = get_wcaps_type(caps); 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci if (!(caps & AC_WCAP_DIGITAL) || (type != AC_WID_PIN)) 22762306a36Sopenharmony_ci return 0; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci param = snd_hdac_read_parm_uncached(hdev, nid, AC_PAR_DEVLIST_LEN); 23062306a36Sopenharmony_ci if (param == -1) 23162306a36Sopenharmony_ci return param; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci return param & AC_DEV_LIST_LEN_MASK; 23462306a36Sopenharmony_ci} 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci/* 23762306a36Sopenharmony_ci * Get the port entry select on the pin. Return the port entry 23862306a36Sopenharmony_ci * id selected on the pin. Return 0 means the first port entry 23962306a36Sopenharmony_ci * is selected or MST is not supported. 24062306a36Sopenharmony_ci */ 24162306a36Sopenharmony_cistatic int hdac_hdmi_port_select_get(struct hdac_device *hdev, 24262306a36Sopenharmony_ci struct hdac_hdmi_port *port) 24362306a36Sopenharmony_ci{ 24462306a36Sopenharmony_ci return snd_hdac_codec_read(hdev, port->pin->nid, 24562306a36Sopenharmony_ci 0, AC_VERB_GET_DEVICE_SEL, 0); 24662306a36Sopenharmony_ci} 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci/* 24962306a36Sopenharmony_ci * Sets the selected port entry for the configuring Pin widget verb. 25062306a36Sopenharmony_ci * returns error if port set is not equal to port get otherwise success 25162306a36Sopenharmony_ci */ 25262306a36Sopenharmony_cistatic int hdac_hdmi_port_select_set(struct hdac_device *hdev, 25362306a36Sopenharmony_ci struct hdac_hdmi_port *port) 25462306a36Sopenharmony_ci{ 25562306a36Sopenharmony_ci int num_ports; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci if (!port->pin->mst_capable) 25862306a36Sopenharmony_ci return 0; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci /* AC_PAR_DEVLIST_LEN is 0 based. */ 26162306a36Sopenharmony_ci num_ports = hdac_hdmi_get_port_len(hdev, port->pin->nid); 26262306a36Sopenharmony_ci if (num_ports < 0) 26362306a36Sopenharmony_ci return -EIO; 26462306a36Sopenharmony_ci /* 26562306a36Sopenharmony_ci * Device List Length is a 0 based integer value indicating the 26662306a36Sopenharmony_ci * number of sink device that a MST Pin Widget can support. 26762306a36Sopenharmony_ci */ 26862306a36Sopenharmony_ci if (num_ports + 1 < port->id) 26962306a36Sopenharmony_ci return 0; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci snd_hdac_codec_write(hdev, port->pin->nid, 0, 27262306a36Sopenharmony_ci AC_VERB_SET_DEVICE_SEL, port->id); 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci if (port->id != hdac_hdmi_port_select_get(hdev, port)) 27562306a36Sopenharmony_ci return -EIO; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci dev_dbg(&hdev->dev, "Selected the port=%d\n", port->id); 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci return 0; 28062306a36Sopenharmony_ci} 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_cistatic struct hdac_hdmi_pcm *get_hdmi_pcm_from_id(struct hdac_hdmi_priv *hdmi, 28362306a36Sopenharmony_ci int pcm_idx) 28462306a36Sopenharmony_ci{ 28562306a36Sopenharmony_ci struct hdac_hdmi_pcm *pcm; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci list_for_each_entry(pcm, &hdmi->pcm_list, head) { 28862306a36Sopenharmony_ci if (pcm->pcm_id == pcm_idx) 28962306a36Sopenharmony_ci return pcm; 29062306a36Sopenharmony_ci } 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci return NULL; 29362306a36Sopenharmony_ci} 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_cistatic unsigned int sad_format(const u8 *sad) 29662306a36Sopenharmony_ci{ 29762306a36Sopenharmony_ci return ((sad[0] >> 0x3) & 0x1f); 29862306a36Sopenharmony_ci} 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_cistatic unsigned int sad_sample_bits_lpcm(const u8 *sad) 30162306a36Sopenharmony_ci{ 30262306a36Sopenharmony_ci return (sad[2] & 7); 30362306a36Sopenharmony_ci} 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_cistatic int hdac_hdmi_eld_limit_formats(struct snd_pcm_runtime *runtime, 30662306a36Sopenharmony_ci void *eld) 30762306a36Sopenharmony_ci{ 30862306a36Sopenharmony_ci u64 formats = SNDRV_PCM_FMTBIT_S16; 30962306a36Sopenharmony_ci int i; 31062306a36Sopenharmony_ci const u8 *sad, *eld_buf = eld; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci sad = drm_eld_sad(eld_buf); 31362306a36Sopenharmony_ci if (!sad) 31462306a36Sopenharmony_ci goto format_constraint; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci for (i = drm_eld_sad_count(eld_buf); i > 0; i--, sad += 3) { 31762306a36Sopenharmony_ci if (sad_format(sad) == 1) { /* AUDIO_CODING_TYPE_LPCM */ 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci /* 32062306a36Sopenharmony_ci * the controller support 20 and 24 bits in 32 bit 32162306a36Sopenharmony_ci * container so we set S32 32262306a36Sopenharmony_ci */ 32362306a36Sopenharmony_ci if (sad_sample_bits_lpcm(sad) & 0x6) 32462306a36Sopenharmony_ci formats |= SNDRV_PCM_FMTBIT_S32; 32562306a36Sopenharmony_ci } 32662306a36Sopenharmony_ci } 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ciformat_constraint: 32962306a36Sopenharmony_ci return snd_pcm_hw_constraint_mask64(runtime, SNDRV_PCM_HW_PARAM_FORMAT, 33062306a36Sopenharmony_ci formats); 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci} 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_cistatic void 33562306a36Sopenharmony_cihdac_hdmi_set_dip_index(struct hdac_device *hdev, hda_nid_t pin_nid, 33662306a36Sopenharmony_ci int packet_index, int byte_index) 33762306a36Sopenharmony_ci{ 33862306a36Sopenharmony_ci int val; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci val = (packet_index << 5) | (byte_index & 0x1f); 34162306a36Sopenharmony_ci snd_hdac_codec_write(hdev, pin_nid, 0, AC_VERB_SET_HDMI_DIP_INDEX, val); 34262306a36Sopenharmony_ci} 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_cistruct dp_audio_infoframe { 34562306a36Sopenharmony_ci u8 type; /* 0x84 */ 34662306a36Sopenharmony_ci u8 len; /* 0x1b */ 34762306a36Sopenharmony_ci u8 ver; /* 0x11 << 2 */ 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci u8 CC02_CT47; /* match with HDMI infoframe from this on */ 35062306a36Sopenharmony_ci u8 SS01_SF24; 35162306a36Sopenharmony_ci u8 CXT04; 35262306a36Sopenharmony_ci u8 CA; 35362306a36Sopenharmony_ci u8 LFEPBL01_LSV36_DM_INH7; 35462306a36Sopenharmony_ci}; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_cistatic int hdac_hdmi_setup_audio_infoframe(struct hdac_device *hdev, 35762306a36Sopenharmony_ci struct hdac_hdmi_pcm *pcm, struct hdac_hdmi_port *port) 35862306a36Sopenharmony_ci{ 35962306a36Sopenharmony_ci uint8_t buffer[HDMI_INFOFRAME_HEADER_SIZE + HDMI_AUDIO_INFOFRAME_SIZE]; 36062306a36Sopenharmony_ci struct hdmi_audio_infoframe frame; 36162306a36Sopenharmony_ci struct hdac_hdmi_pin *pin = port->pin; 36262306a36Sopenharmony_ci struct dp_audio_infoframe dp_ai; 36362306a36Sopenharmony_ci struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); 36462306a36Sopenharmony_ci struct hdac_hdmi_cvt *cvt = pcm->cvt; 36562306a36Sopenharmony_ci u8 *dip; 36662306a36Sopenharmony_ci int ret; 36762306a36Sopenharmony_ci int i; 36862306a36Sopenharmony_ci const u8 *eld_buf; 36962306a36Sopenharmony_ci u8 conn_type; 37062306a36Sopenharmony_ci int channels, ca; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci ca = snd_hdac_channel_allocation(hdev, port->eld.info.spk_alloc, 37362306a36Sopenharmony_ci pcm->channels, pcm->chmap_set, true, pcm->chmap); 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci channels = snd_hdac_get_active_channels(ca); 37662306a36Sopenharmony_ci hdmi->chmap.ops.set_channel_count(hdev, cvt->nid, channels); 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci snd_hdac_setup_channel_mapping(&hdmi->chmap, pin->nid, false, ca, 37962306a36Sopenharmony_ci pcm->channels, pcm->chmap, pcm->chmap_set); 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci eld_buf = port->eld.eld_buffer; 38262306a36Sopenharmony_ci conn_type = drm_eld_get_conn_type(eld_buf); 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci switch (conn_type) { 38562306a36Sopenharmony_ci case DRM_ELD_CONN_TYPE_HDMI: 38662306a36Sopenharmony_ci hdmi_audio_infoframe_init(&frame); 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci frame.channels = channels; 38962306a36Sopenharmony_ci frame.channel_allocation = ca; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci ret = hdmi_audio_infoframe_pack(&frame, buffer, sizeof(buffer)); 39262306a36Sopenharmony_ci if (ret < 0) 39362306a36Sopenharmony_ci return ret; 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci break; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci case DRM_ELD_CONN_TYPE_DP: 39862306a36Sopenharmony_ci memset(&dp_ai, 0, sizeof(dp_ai)); 39962306a36Sopenharmony_ci dp_ai.type = 0x84; 40062306a36Sopenharmony_ci dp_ai.len = 0x1b; 40162306a36Sopenharmony_ci dp_ai.ver = 0x11 << 2; 40262306a36Sopenharmony_ci dp_ai.CC02_CT47 = channels - 1; 40362306a36Sopenharmony_ci dp_ai.CA = ca; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci dip = (u8 *)&dp_ai; 40662306a36Sopenharmony_ci break; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci default: 40962306a36Sopenharmony_ci dev_err(&hdev->dev, "Invalid connection type: %d\n", conn_type); 41062306a36Sopenharmony_ci return -EIO; 41162306a36Sopenharmony_ci } 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci /* stop infoframe transmission */ 41462306a36Sopenharmony_ci hdac_hdmi_set_dip_index(hdev, pin->nid, 0x0, 0x0); 41562306a36Sopenharmony_ci snd_hdac_codec_write(hdev, pin->nid, 0, 41662306a36Sopenharmony_ci AC_VERB_SET_HDMI_DIP_XMIT, AC_DIPXMIT_DISABLE); 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci /* Fill infoframe. Index auto-incremented */ 42062306a36Sopenharmony_ci hdac_hdmi_set_dip_index(hdev, pin->nid, 0x0, 0x0); 42162306a36Sopenharmony_ci if (conn_type == DRM_ELD_CONN_TYPE_HDMI) { 42262306a36Sopenharmony_ci for (i = 0; i < sizeof(buffer); i++) 42362306a36Sopenharmony_ci snd_hdac_codec_write(hdev, pin->nid, 0, 42462306a36Sopenharmony_ci AC_VERB_SET_HDMI_DIP_DATA, buffer[i]); 42562306a36Sopenharmony_ci } else { 42662306a36Sopenharmony_ci for (i = 0; i < sizeof(dp_ai); i++) 42762306a36Sopenharmony_ci snd_hdac_codec_write(hdev, pin->nid, 0, 42862306a36Sopenharmony_ci AC_VERB_SET_HDMI_DIP_DATA, dip[i]); 42962306a36Sopenharmony_ci } 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci /* Start infoframe */ 43262306a36Sopenharmony_ci hdac_hdmi_set_dip_index(hdev, pin->nid, 0x0, 0x0); 43362306a36Sopenharmony_ci snd_hdac_codec_write(hdev, pin->nid, 0, 43462306a36Sopenharmony_ci AC_VERB_SET_HDMI_DIP_XMIT, AC_DIPXMIT_BEST); 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci return 0; 43762306a36Sopenharmony_ci} 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_cistatic int hdac_hdmi_set_stream(struct snd_soc_dai *dai, 44062306a36Sopenharmony_ci void *stream, int direction) 44162306a36Sopenharmony_ci{ 44262306a36Sopenharmony_ci struct hdac_hdmi_priv *hdmi = snd_soc_dai_get_drvdata(dai); 44362306a36Sopenharmony_ci struct hdac_device *hdev = hdmi->hdev; 44462306a36Sopenharmony_ci struct hdac_hdmi_dai_port_map *dai_map; 44562306a36Sopenharmony_ci struct hdac_hdmi_pcm *pcm; 44662306a36Sopenharmony_ci struct hdac_stream *hstream; 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci if (!stream) 44962306a36Sopenharmony_ci return -EINVAL; 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci hstream = (struct hdac_stream *)stream; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci dev_dbg(&hdev->dev, "%s: strm_tag: %d\n", __func__, hstream->stream_tag); 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci dai_map = &hdmi->dai_map[dai->id]; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci pcm = hdac_hdmi_get_pcm_from_cvt(hdmi, dai_map->cvt); 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci if (pcm) 46062306a36Sopenharmony_ci pcm->stream_tag = (hstream->stream_tag << 4); 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci return 0; 46362306a36Sopenharmony_ci} 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_cistatic int hdac_hdmi_set_hw_params(struct snd_pcm_substream *substream, 46662306a36Sopenharmony_ci struct snd_pcm_hw_params *hparams, struct snd_soc_dai *dai) 46762306a36Sopenharmony_ci{ 46862306a36Sopenharmony_ci struct hdac_hdmi_priv *hdmi = snd_soc_dai_get_drvdata(dai); 46962306a36Sopenharmony_ci struct hdac_hdmi_dai_port_map *dai_map; 47062306a36Sopenharmony_ci struct hdac_hdmi_pcm *pcm; 47162306a36Sopenharmony_ci int format; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci dai_map = &hdmi->dai_map[dai->id]; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci format = snd_hdac_calc_stream_format(params_rate(hparams), 47662306a36Sopenharmony_ci params_channels(hparams), params_format(hparams), 47762306a36Sopenharmony_ci dai->driver->playback.sig_bits, 0); 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci pcm = hdac_hdmi_get_pcm_from_cvt(hdmi, dai_map->cvt); 48062306a36Sopenharmony_ci if (!pcm) 48162306a36Sopenharmony_ci return -EIO; 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci pcm->format = format; 48462306a36Sopenharmony_ci pcm->channels = params_channels(hparams); 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci return 0; 48762306a36Sopenharmony_ci} 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_cistatic int hdac_hdmi_query_port_connlist(struct hdac_device *hdev, 49062306a36Sopenharmony_ci struct hdac_hdmi_pin *pin, 49162306a36Sopenharmony_ci struct hdac_hdmi_port *port) 49262306a36Sopenharmony_ci{ 49362306a36Sopenharmony_ci if (!(get_wcaps(hdev, pin->nid) & AC_WCAP_CONN_LIST)) { 49462306a36Sopenharmony_ci dev_warn(&hdev->dev, 49562306a36Sopenharmony_ci "HDMI: pin %d wcaps %#x does not support connection list\n", 49662306a36Sopenharmony_ci pin->nid, get_wcaps(hdev, pin->nid)); 49762306a36Sopenharmony_ci return -EINVAL; 49862306a36Sopenharmony_ci } 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci if (hdac_hdmi_port_select_set(hdev, port) < 0) 50162306a36Sopenharmony_ci return -EIO; 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci port->num_mux_nids = snd_hdac_get_connections(hdev, pin->nid, 50462306a36Sopenharmony_ci port->mux_nids, HDA_MAX_CONNECTIONS); 50562306a36Sopenharmony_ci if (port->num_mux_nids == 0) 50662306a36Sopenharmony_ci dev_warn(&hdev->dev, 50762306a36Sopenharmony_ci "No connections found for pin:port %d:%d\n", 50862306a36Sopenharmony_ci pin->nid, port->id); 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci dev_dbg(&hdev->dev, "num_mux_nids %d for pin:port %d:%d\n", 51162306a36Sopenharmony_ci port->num_mux_nids, pin->nid, port->id); 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci return port->num_mux_nids; 51462306a36Sopenharmony_ci} 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci/* 51762306a36Sopenharmony_ci * Query pcm list and return port to which stream is routed. 51862306a36Sopenharmony_ci * 51962306a36Sopenharmony_ci * Also query connection list of the pin, to validate the cvt to port map. 52062306a36Sopenharmony_ci * 52162306a36Sopenharmony_ci * Same stream rendering to multiple ports simultaneously can be done 52262306a36Sopenharmony_ci * possibly, but not supported for now in driver. So return the first port 52362306a36Sopenharmony_ci * connected. 52462306a36Sopenharmony_ci */ 52562306a36Sopenharmony_cistatic struct hdac_hdmi_port *hdac_hdmi_get_port_from_cvt( 52662306a36Sopenharmony_ci struct hdac_device *hdev, 52762306a36Sopenharmony_ci struct hdac_hdmi_priv *hdmi, 52862306a36Sopenharmony_ci struct hdac_hdmi_cvt *cvt) 52962306a36Sopenharmony_ci{ 53062306a36Sopenharmony_ci struct hdac_hdmi_pcm *pcm; 53162306a36Sopenharmony_ci struct hdac_hdmi_port *port; 53262306a36Sopenharmony_ci int ret, i; 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci list_for_each_entry(pcm, &hdmi->pcm_list, head) { 53562306a36Sopenharmony_ci if (pcm->cvt == cvt) { 53662306a36Sopenharmony_ci if (list_empty(&pcm->port_list)) 53762306a36Sopenharmony_ci continue; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci list_for_each_entry(port, &pcm->port_list, head) { 54062306a36Sopenharmony_ci mutex_lock(&pcm->lock); 54162306a36Sopenharmony_ci ret = hdac_hdmi_query_port_connlist(hdev, 54262306a36Sopenharmony_ci port->pin, port); 54362306a36Sopenharmony_ci mutex_unlock(&pcm->lock); 54462306a36Sopenharmony_ci if (ret < 0) 54562306a36Sopenharmony_ci continue; 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci for (i = 0; i < port->num_mux_nids; i++) { 54862306a36Sopenharmony_ci if (port->mux_nids[i] == cvt->nid && 54962306a36Sopenharmony_ci port->eld.monitor_present && 55062306a36Sopenharmony_ci port->eld.eld_valid) 55162306a36Sopenharmony_ci return port; 55262306a36Sopenharmony_ci } 55362306a36Sopenharmony_ci } 55462306a36Sopenharmony_ci } 55562306a36Sopenharmony_ci } 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci return NULL; 55862306a36Sopenharmony_ci} 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci/* 56162306a36Sopenharmony_ci * Go through all converters and ensure connection is set to 56262306a36Sopenharmony_ci * the correct pin as set via kcontrols. 56362306a36Sopenharmony_ci */ 56462306a36Sopenharmony_cistatic void hdac_hdmi_verify_connect_sel_all_pins(struct hdac_device *hdev) 56562306a36Sopenharmony_ci{ 56662306a36Sopenharmony_ci struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); 56762306a36Sopenharmony_ci struct hdac_hdmi_port *port; 56862306a36Sopenharmony_ci struct hdac_hdmi_cvt *cvt; 56962306a36Sopenharmony_ci int cvt_idx = 0; 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci list_for_each_entry(cvt, &hdmi->cvt_list, head) { 57262306a36Sopenharmony_ci port = hdac_hdmi_get_port_from_cvt(hdev, hdmi, cvt); 57362306a36Sopenharmony_ci if (port && port->pin) { 57462306a36Sopenharmony_ci snd_hdac_codec_write(hdev, port->pin->nid, 0, 57562306a36Sopenharmony_ci AC_VERB_SET_CONNECT_SEL, cvt_idx); 57662306a36Sopenharmony_ci dev_dbg(&hdev->dev, "%s: %s set connect %d -> %d\n", 57762306a36Sopenharmony_ci __func__, cvt->name, port->pin->nid, cvt_idx); 57862306a36Sopenharmony_ci } 57962306a36Sopenharmony_ci ++cvt_idx; 58062306a36Sopenharmony_ci } 58162306a36Sopenharmony_ci} 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci/* 58462306a36Sopenharmony_ci * This tries to get a valid pin and set the HW constraints based on the 58562306a36Sopenharmony_ci * ELD. Even if a valid pin is not found return success so that device open 58662306a36Sopenharmony_ci * doesn't fail. 58762306a36Sopenharmony_ci */ 58862306a36Sopenharmony_cistatic int hdac_hdmi_pcm_open(struct snd_pcm_substream *substream, 58962306a36Sopenharmony_ci struct snd_soc_dai *dai) 59062306a36Sopenharmony_ci{ 59162306a36Sopenharmony_ci struct hdac_hdmi_priv *hdmi = snd_soc_dai_get_drvdata(dai); 59262306a36Sopenharmony_ci struct hdac_device *hdev = hdmi->hdev; 59362306a36Sopenharmony_ci struct hdac_hdmi_dai_port_map *dai_map; 59462306a36Sopenharmony_ci struct hdac_hdmi_cvt *cvt; 59562306a36Sopenharmony_ci struct hdac_hdmi_port *port; 59662306a36Sopenharmony_ci int ret; 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci dai_map = &hdmi->dai_map[dai->id]; 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci cvt = dai_map->cvt; 60162306a36Sopenharmony_ci port = hdac_hdmi_get_port_from_cvt(hdev, hdmi, cvt); 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci /* 60462306a36Sopenharmony_ci * To make PA and other userland happy. 60562306a36Sopenharmony_ci * userland scans devices so returning error does not help. 60662306a36Sopenharmony_ci */ 60762306a36Sopenharmony_ci if (!port) 60862306a36Sopenharmony_ci return 0; 60962306a36Sopenharmony_ci if ((!port->eld.monitor_present) || 61062306a36Sopenharmony_ci (!port->eld.eld_valid)) { 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci dev_warn(&hdev->dev, 61362306a36Sopenharmony_ci "Failed: present?:%d ELD valid?:%d pin:port: %d:%d\n", 61462306a36Sopenharmony_ci port->eld.monitor_present, port->eld.eld_valid, 61562306a36Sopenharmony_ci port->pin->nid, port->id); 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci return 0; 61862306a36Sopenharmony_ci } 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci dai_map->port = port; 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci ret = hdac_hdmi_eld_limit_formats(substream->runtime, 62362306a36Sopenharmony_ci port->eld.eld_buffer); 62462306a36Sopenharmony_ci if (ret < 0) 62562306a36Sopenharmony_ci return ret; 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci return snd_pcm_hw_constraint_eld(substream->runtime, 62862306a36Sopenharmony_ci port->eld.eld_buffer); 62962306a36Sopenharmony_ci} 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_cistatic void hdac_hdmi_pcm_close(struct snd_pcm_substream *substream, 63262306a36Sopenharmony_ci struct snd_soc_dai *dai) 63362306a36Sopenharmony_ci{ 63462306a36Sopenharmony_ci struct hdac_hdmi_priv *hdmi = snd_soc_dai_get_drvdata(dai); 63562306a36Sopenharmony_ci struct hdac_hdmi_dai_port_map *dai_map; 63662306a36Sopenharmony_ci struct hdac_hdmi_pcm *pcm; 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci dai_map = &hdmi->dai_map[dai->id]; 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci pcm = hdac_hdmi_get_pcm_from_cvt(hdmi, dai_map->cvt); 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci if (pcm) { 64362306a36Sopenharmony_ci mutex_lock(&pcm->lock); 64462306a36Sopenharmony_ci pcm->chmap_set = false; 64562306a36Sopenharmony_ci memset(pcm->chmap, 0, sizeof(pcm->chmap)); 64662306a36Sopenharmony_ci pcm->channels = 0; 64762306a36Sopenharmony_ci mutex_unlock(&pcm->lock); 64862306a36Sopenharmony_ci } 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci if (dai_map->port) 65162306a36Sopenharmony_ci dai_map->port = NULL; 65262306a36Sopenharmony_ci} 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_cistatic int 65562306a36Sopenharmony_cihdac_hdmi_query_cvt_params(struct hdac_device *hdev, struct hdac_hdmi_cvt *cvt) 65662306a36Sopenharmony_ci{ 65762306a36Sopenharmony_ci unsigned int chans; 65862306a36Sopenharmony_ci struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); 65962306a36Sopenharmony_ci int err; 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci chans = get_wcaps(hdev, cvt->nid); 66262306a36Sopenharmony_ci chans = get_wcaps_channels(chans); 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci cvt->params.channels_min = 2; 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci cvt->params.channels_max = chans; 66762306a36Sopenharmony_ci if (chans > hdmi->chmap.channels_max) 66862306a36Sopenharmony_ci hdmi->chmap.channels_max = chans; 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci err = snd_hdac_query_supported_pcm(hdev, cvt->nid, 67162306a36Sopenharmony_ci &cvt->params.rates, 67262306a36Sopenharmony_ci &cvt->params.formats, 67362306a36Sopenharmony_ci &cvt->params.maxbps); 67462306a36Sopenharmony_ci if (err < 0) 67562306a36Sopenharmony_ci dev_err(&hdev->dev, 67662306a36Sopenharmony_ci "Failed to query pcm params for nid %d: %d\n", 67762306a36Sopenharmony_ci cvt->nid, err); 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci return err; 68062306a36Sopenharmony_ci} 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_cistatic int hdac_hdmi_fill_widget_info(struct device *dev, 68362306a36Sopenharmony_ci struct snd_soc_dapm_widget *w, enum snd_soc_dapm_type id, 68462306a36Sopenharmony_ci void *priv, const char *wname, const char *stream, 68562306a36Sopenharmony_ci struct snd_kcontrol_new *wc, int numkc, 68662306a36Sopenharmony_ci int (*event)(struct snd_soc_dapm_widget *, 68762306a36Sopenharmony_ci struct snd_kcontrol *, int), unsigned short event_flags) 68862306a36Sopenharmony_ci{ 68962306a36Sopenharmony_ci w->id = id; 69062306a36Sopenharmony_ci w->name = devm_kstrdup(dev, wname, GFP_KERNEL); 69162306a36Sopenharmony_ci if (!w->name) 69262306a36Sopenharmony_ci return -ENOMEM; 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci w->sname = stream; 69562306a36Sopenharmony_ci w->reg = SND_SOC_NOPM; 69662306a36Sopenharmony_ci w->shift = 0; 69762306a36Sopenharmony_ci w->kcontrol_news = wc; 69862306a36Sopenharmony_ci w->num_kcontrols = numkc; 69962306a36Sopenharmony_ci w->priv = priv; 70062306a36Sopenharmony_ci w->event = event; 70162306a36Sopenharmony_ci w->event_flags = event_flags; 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci return 0; 70462306a36Sopenharmony_ci} 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_cistatic void hdac_hdmi_fill_route(struct snd_soc_dapm_route *route, 70762306a36Sopenharmony_ci const char *sink, const char *control, const char *src, 70862306a36Sopenharmony_ci int (*handler)(struct snd_soc_dapm_widget *src, 70962306a36Sopenharmony_ci struct snd_soc_dapm_widget *sink)) 71062306a36Sopenharmony_ci{ 71162306a36Sopenharmony_ci route->sink = sink; 71262306a36Sopenharmony_ci route->source = src; 71362306a36Sopenharmony_ci route->control = control; 71462306a36Sopenharmony_ci route->connected = handler; 71562306a36Sopenharmony_ci} 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_cistatic struct hdac_hdmi_pcm *hdac_hdmi_get_pcm(struct hdac_device *hdev, 71862306a36Sopenharmony_ci struct hdac_hdmi_port *port) 71962306a36Sopenharmony_ci{ 72062306a36Sopenharmony_ci struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); 72162306a36Sopenharmony_ci struct hdac_hdmi_pcm *pcm; 72262306a36Sopenharmony_ci struct hdac_hdmi_port *p; 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci list_for_each_entry(pcm, &hdmi->pcm_list, head) { 72562306a36Sopenharmony_ci if (list_empty(&pcm->port_list)) 72662306a36Sopenharmony_ci continue; 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci list_for_each_entry(p, &pcm->port_list, head) { 72962306a36Sopenharmony_ci if (p->id == port->id && port->pin == p->pin) 73062306a36Sopenharmony_ci return pcm; 73162306a36Sopenharmony_ci } 73262306a36Sopenharmony_ci } 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci return NULL; 73562306a36Sopenharmony_ci} 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_cistatic void hdac_hdmi_set_power_state(struct hdac_device *hdev, 73862306a36Sopenharmony_ci hda_nid_t nid, unsigned int pwr_state) 73962306a36Sopenharmony_ci{ 74062306a36Sopenharmony_ci int count; 74162306a36Sopenharmony_ci unsigned int state; 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci if (get_wcaps(hdev, nid) & AC_WCAP_POWER) { 74462306a36Sopenharmony_ci if (!snd_hdac_check_power_state(hdev, nid, pwr_state)) { 74562306a36Sopenharmony_ci for (count = 0; count < 10; count++) { 74662306a36Sopenharmony_ci snd_hdac_codec_read(hdev, nid, 0, 74762306a36Sopenharmony_ci AC_VERB_SET_POWER_STATE, 74862306a36Sopenharmony_ci pwr_state); 74962306a36Sopenharmony_ci state = snd_hdac_sync_power_state(hdev, 75062306a36Sopenharmony_ci nid, pwr_state); 75162306a36Sopenharmony_ci if (!(state & AC_PWRST_ERROR)) 75262306a36Sopenharmony_ci break; 75362306a36Sopenharmony_ci } 75462306a36Sopenharmony_ci } 75562306a36Sopenharmony_ci } 75662306a36Sopenharmony_ci} 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_cistatic void hdac_hdmi_set_amp(struct hdac_device *hdev, 75962306a36Sopenharmony_ci hda_nid_t nid, int val) 76062306a36Sopenharmony_ci{ 76162306a36Sopenharmony_ci if (get_wcaps(hdev, nid) & AC_WCAP_OUT_AMP) 76262306a36Sopenharmony_ci snd_hdac_codec_write(hdev, nid, 0, 76362306a36Sopenharmony_ci AC_VERB_SET_AMP_GAIN_MUTE, val); 76462306a36Sopenharmony_ci} 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_cistatic int hdac_hdmi_pin_output_widget_event(struct snd_soc_dapm_widget *w, 76862306a36Sopenharmony_ci struct snd_kcontrol *kc, int event) 76962306a36Sopenharmony_ci{ 77062306a36Sopenharmony_ci struct hdac_hdmi_port *port = w->priv; 77162306a36Sopenharmony_ci struct hdac_device *hdev = dev_to_hdac_dev(w->dapm->dev); 77262306a36Sopenharmony_ci struct hdac_hdmi_pcm *pcm; 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci dev_dbg(&hdev->dev, "%s: widget: %s event: %x\n", 77562306a36Sopenharmony_ci __func__, w->name, event); 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci pcm = hdac_hdmi_get_pcm(hdev, port); 77862306a36Sopenharmony_ci if (!pcm) 77962306a36Sopenharmony_ci return -EIO; 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci /* set the device if pin is mst_capable */ 78262306a36Sopenharmony_ci if (hdac_hdmi_port_select_set(hdev, port) < 0) 78362306a36Sopenharmony_ci return -EIO; 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci switch (event) { 78662306a36Sopenharmony_ci case SND_SOC_DAPM_PRE_PMU: 78762306a36Sopenharmony_ci hdac_hdmi_set_power_state(hdev, port->pin->nid, AC_PWRST_D0); 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci /* Enable out path for this pin widget */ 79062306a36Sopenharmony_ci snd_hdac_codec_write(hdev, port->pin->nid, 0, 79162306a36Sopenharmony_ci AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci hdac_hdmi_set_amp(hdev, port->pin->nid, AMP_OUT_UNMUTE); 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci return hdac_hdmi_setup_audio_infoframe(hdev, pcm, port); 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci case SND_SOC_DAPM_POST_PMD: 79862306a36Sopenharmony_ci hdac_hdmi_set_amp(hdev, port->pin->nid, AMP_OUT_MUTE); 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci /* Disable out path for this pin widget */ 80162306a36Sopenharmony_ci snd_hdac_codec_write(hdev, port->pin->nid, 0, 80262306a36Sopenharmony_ci AC_VERB_SET_PIN_WIDGET_CONTROL, 0); 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci hdac_hdmi_set_power_state(hdev, port->pin->nid, AC_PWRST_D3); 80562306a36Sopenharmony_ci break; 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci } 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci return 0; 81062306a36Sopenharmony_ci} 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_cistatic int hdac_hdmi_cvt_output_widget_event(struct snd_soc_dapm_widget *w, 81362306a36Sopenharmony_ci struct snd_kcontrol *kc, int event) 81462306a36Sopenharmony_ci{ 81562306a36Sopenharmony_ci struct hdac_hdmi_cvt *cvt = w->priv; 81662306a36Sopenharmony_ci struct hdac_device *hdev = dev_to_hdac_dev(w->dapm->dev); 81762306a36Sopenharmony_ci struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); 81862306a36Sopenharmony_ci struct hdac_hdmi_pcm *pcm; 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci dev_dbg(&hdev->dev, "%s: widget: %s event: %x\n", 82162306a36Sopenharmony_ci __func__, w->name, event); 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci pcm = hdac_hdmi_get_pcm_from_cvt(hdmi, cvt); 82462306a36Sopenharmony_ci if (!pcm) 82562306a36Sopenharmony_ci return -EIO; 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci switch (event) { 82862306a36Sopenharmony_ci case SND_SOC_DAPM_PRE_PMU: 82962306a36Sopenharmony_ci hdac_hdmi_set_power_state(hdev, cvt->nid, AC_PWRST_D0); 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci /* Enable transmission */ 83262306a36Sopenharmony_ci snd_hdac_codec_write(hdev, cvt->nid, 0, 83362306a36Sopenharmony_ci AC_VERB_SET_DIGI_CONVERT_1, 1); 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci /* Category Code (CC) to zero */ 83662306a36Sopenharmony_ci snd_hdac_codec_write(hdev, cvt->nid, 0, 83762306a36Sopenharmony_ci AC_VERB_SET_DIGI_CONVERT_2, 0); 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci snd_hdac_codec_write(hdev, cvt->nid, 0, 84062306a36Sopenharmony_ci AC_VERB_SET_CHANNEL_STREAMID, pcm->stream_tag); 84162306a36Sopenharmony_ci snd_hdac_codec_write(hdev, cvt->nid, 0, 84262306a36Sopenharmony_ci AC_VERB_SET_STREAM_FORMAT, pcm->format); 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci /* 84562306a36Sopenharmony_ci * The connection indices are shared by all converters and 84662306a36Sopenharmony_ci * may interfere with each other. Ensure correct 84762306a36Sopenharmony_ci * routing for all converters at stream start. 84862306a36Sopenharmony_ci */ 84962306a36Sopenharmony_ci hdac_hdmi_verify_connect_sel_all_pins(hdev); 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci break; 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci case SND_SOC_DAPM_POST_PMD: 85462306a36Sopenharmony_ci snd_hdac_codec_write(hdev, cvt->nid, 0, 85562306a36Sopenharmony_ci AC_VERB_SET_CHANNEL_STREAMID, 0); 85662306a36Sopenharmony_ci snd_hdac_codec_write(hdev, cvt->nid, 0, 85762306a36Sopenharmony_ci AC_VERB_SET_STREAM_FORMAT, 0); 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci hdac_hdmi_set_power_state(hdev, cvt->nid, AC_PWRST_D3); 86062306a36Sopenharmony_ci break; 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci } 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci return 0; 86562306a36Sopenharmony_ci} 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_cistatic int hdac_hdmi_pin_mux_widget_event(struct snd_soc_dapm_widget *w, 86862306a36Sopenharmony_ci struct snd_kcontrol *kc, int event) 86962306a36Sopenharmony_ci{ 87062306a36Sopenharmony_ci struct hdac_hdmi_port *port = w->priv; 87162306a36Sopenharmony_ci struct hdac_device *hdev = dev_to_hdac_dev(w->dapm->dev); 87262306a36Sopenharmony_ci int mux_idx; 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ci dev_dbg(&hdev->dev, "%s: widget: %s event: %x\n", 87562306a36Sopenharmony_ci __func__, w->name, event); 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci if (!kc) 87862306a36Sopenharmony_ci kc = w->kcontrols[0]; 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci mux_idx = dapm_kcontrol_get_value(kc); 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci /* set the device if pin is mst_capable */ 88362306a36Sopenharmony_ci if (hdac_hdmi_port_select_set(hdev, port) < 0) 88462306a36Sopenharmony_ci return -EIO; 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ci if (mux_idx > 0) { 88762306a36Sopenharmony_ci snd_hdac_codec_write(hdev, port->pin->nid, 0, 88862306a36Sopenharmony_ci AC_VERB_SET_CONNECT_SEL, (mux_idx - 1)); 88962306a36Sopenharmony_ci } 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci return 0; 89262306a36Sopenharmony_ci} 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci/* 89562306a36Sopenharmony_ci * Based on user selection, map the PINs with the PCMs. 89662306a36Sopenharmony_ci */ 89762306a36Sopenharmony_cistatic int hdac_hdmi_set_pin_port_mux(struct snd_kcontrol *kcontrol, 89862306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 89962306a36Sopenharmony_ci{ 90062306a36Sopenharmony_ci int ret; 90162306a36Sopenharmony_ci struct hdac_hdmi_port *p, *p_next; 90262306a36Sopenharmony_ci struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; 90362306a36Sopenharmony_ci struct snd_soc_dapm_widget *w = snd_soc_dapm_kcontrol_widget(kcontrol); 90462306a36Sopenharmony_ci struct snd_soc_dapm_context *dapm = w->dapm; 90562306a36Sopenharmony_ci struct hdac_hdmi_port *port = w->priv; 90662306a36Sopenharmony_ci struct hdac_device *hdev = dev_to_hdac_dev(dapm->dev); 90762306a36Sopenharmony_ci struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); 90862306a36Sopenharmony_ci struct hdac_hdmi_pcm *pcm; 90962306a36Sopenharmony_ci const char *cvt_name = e->texts[ucontrol->value.enumerated.item[0]]; 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_ci ret = snd_soc_dapm_put_enum_double(kcontrol, ucontrol); 91262306a36Sopenharmony_ci if (ret < 0) 91362306a36Sopenharmony_ci return ret; 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci if (port == NULL) 91662306a36Sopenharmony_ci return -EINVAL; 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci mutex_lock(&hdmi->pin_mutex); 91962306a36Sopenharmony_ci list_for_each_entry(pcm, &hdmi->pcm_list, head) { 92062306a36Sopenharmony_ci if (list_empty(&pcm->port_list)) 92162306a36Sopenharmony_ci continue; 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_ci list_for_each_entry_safe(p, p_next, &pcm->port_list, head) { 92462306a36Sopenharmony_ci if (p == port && p->id == port->id && 92562306a36Sopenharmony_ci p->pin == port->pin) { 92662306a36Sopenharmony_ci hdac_hdmi_jack_report_sync(pcm, port, false); 92762306a36Sopenharmony_ci list_del(&p->head); 92862306a36Sopenharmony_ci } 92962306a36Sopenharmony_ci } 93062306a36Sopenharmony_ci } 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci /* 93362306a36Sopenharmony_ci * Jack status is not reported during device probe as the 93462306a36Sopenharmony_ci * PCMs are not registered by then. So report it here. 93562306a36Sopenharmony_ci */ 93662306a36Sopenharmony_ci list_for_each_entry(pcm, &hdmi->pcm_list, head) { 93762306a36Sopenharmony_ci if (!strcmp(cvt_name, pcm->cvt->name)) { 93862306a36Sopenharmony_ci list_add_tail(&port->head, &pcm->port_list); 93962306a36Sopenharmony_ci if (port->eld.monitor_present && port->eld.eld_valid) { 94062306a36Sopenharmony_ci hdac_hdmi_jack_report_sync(pcm, port, true); 94162306a36Sopenharmony_ci mutex_unlock(&hdmi->pin_mutex); 94262306a36Sopenharmony_ci return ret; 94362306a36Sopenharmony_ci } 94462306a36Sopenharmony_ci } 94562306a36Sopenharmony_ci } 94662306a36Sopenharmony_ci mutex_unlock(&hdmi->pin_mutex); 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci return ret; 94962306a36Sopenharmony_ci} 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci/* 95262306a36Sopenharmony_ci * Ideally the Mux inputs should be based on the num_muxs enumerated, but 95362306a36Sopenharmony_ci * the display driver seem to be programming the connection list for the pin 95462306a36Sopenharmony_ci * widget runtime. 95562306a36Sopenharmony_ci * 95662306a36Sopenharmony_ci * So programming all the possible inputs for the mux, the user has to take 95762306a36Sopenharmony_ci * care of selecting the right one and leaving all other inputs selected to 95862306a36Sopenharmony_ci * "NONE" 95962306a36Sopenharmony_ci */ 96062306a36Sopenharmony_cistatic int hdac_hdmi_create_pin_port_muxs(struct hdac_device *hdev, 96162306a36Sopenharmony_ci struct hdac_hdmi_port *port, 96262306a36Sopenharmony_ci struct snd_soc_dapm_widget *widget, 96362306a36Sopenharmony_ci const char *widget_name) 96462306a36Sopenharmony_ci{ 96562306a36Sopenharmony_ci struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); 96662306a36Sopenharmony_ci struct hdac_hdmi_pin *pin = port->pin; 96762306a36Sopenharmony_ci struct snd_kcontrol_new *kc; 96862306a36Sopenharmony_ci struct hdac_hdmi_cvt *cvt; 96962306a36Sopenharmony_ci struct soc_enum *se; 97062306a36Sopenharmony_ci char kc_name[NAME_SIZE]; 97162306a36Sopenharmony_ci char mux_items[NAME_SIZE]; 97262306a36Sopenharmony_ci /* To hold inputs to the Pin mux */ 97362306a36Sopenharmony_ci char *items[HDA_MAX_CONNECTIONS]; 97462306a36Sopenharmony_ci int i = 0; 97562306a36Sopenharmony_ci int num_items = hdmi->num_cvt + 1; 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_ci kc = devm_kzalloc(&hdev->dev, sizeof(*kc), GFP_KERNEL); 97862306a36Sopenharmony_ci if (!kc) 97962306a36Sopenharmony_ci return -ENOMEM; 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_ci se = devm_kzalloc(&hdev->dev, sizeof(*se), GFP_KERNEL); 98262306a36Sopenharmony_ci if (!se) 98362306a36Sopenharmony_ci return -ENOMEM; 98462306a36Sopenharmony_ci 98562306a36Sopenharmony_ci snprintf(kc_name, NAME_SIZE, "Pin %d port %d Input", 98662306a36Sopenharmony_ci pin->nid, port->id); 98762306a36Sopenharmony_ci kc->name = devm_kstrdup(&hdev->dev, kc_name, GFP_KERNEL); 98862306a36Sopenharmony_ci if (!kc->name) 98962306a36Sopenharmony_ci return -ENOMEM; 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci kc->private_value = (long)se; 99262306a36Sopenharmony_ci kc->iface = SNDRV_CTL_ELEM_IFACE_MIXER; 99362306a36Sopenharmony_ci kc->access = 0; 99462306a36Sopenharmony_ci kc->info = snd_soc_info_enum_double; 99562306a36Sopenharmony_ci kc->put = hdac_hdmi_set_pin_port_mux; 99662306a36Sopenharmony_ci kc->get = snd_soc_dapm_get_enum_double; 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_ci se->reg = SND_SOC_NOPM; 99962306a36Sopenharmony_ci 100062306a36Sopenharmony_ci /* enum texts: ["NONE", "cvt #", "cvt #", ...] */ 100162306a36Sopenharmony_ci se->items = num_items; 100262306a36Sopenharmony_ci se->mask = roundup_pow_of_two(se->items) - 1; 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_ci sprintf(mux_items, "NONE"); 100562306a36Sopenharmony_ci items[i] = devm_kstrdup(&hdev->dev, mux_items, GFP_KERNEL); 100662306a36Sopenharmony_ci if (!items[i]) 100762306a36Sopenharmony_ci return -ENOMEM; 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ci list_for_each_entry(cvt, &hdmi->cvt_list, head) { 101062306a36Sopenharmony_ci i++; 101162306a36Sopenharmony_ci sprintf(mux_items, "cvt %d", cvt->nid); 101262306a36Sopenharmony_ci items[i] = devm_kstrdup(&hdev->dev, mux_items, GFP_KERNEL); 101362306a36Sopenharmony_ci if (!items[i]) 101462306a36Sopenharmony_ci return -ENOMEM; 101562306a36Sopenharmony_ci } 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_ci se->texts = devm_kmemdup(&hdev->dev, items, 101862306a36Sopenharmony_ci (num_items * sizeof(char *)), GFP_KERNEL); 101962306a36Sopenharmony_ci if (!se->texts) 102062306a36Sopenharmony_ci return -ENOMEM; 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_ci return hdac_hdmi_fill_widget_info(&hdev->dev, widget, 102362306a36Sopenharmony_ci snd_soc_dapm_mux, port, widget_name, NULL, kc, 1, 102462306a36Sopenharmony_ci hdac_hdmi_pin_mux_widget_event, 102562306a36Sopenharmony_ci SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_REG); 102662306a36Sopenharmony_ci} 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_ci/* Add cvt <- input <- mux route map */ 102962306a36Sopenharmony_cistatic void hdac_hdmi_add_pinmux_cvt_route(struct hdac_device *hdev, 103062306a36Sopenharmony_ci struct snd_soc_dapm_widget *widgets, 103162306a36Sopenharmony_ci struct snd_soc_dapm_route *route, int rindex) 103262306a36Sopenharmony_ci{ 103362306a36Sopenharmony_ci struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); 103462306a36Sopenharmony_ci const struct snd_kcontrol_new *kc; 103562306a36Sopenharmony_ci struct soc_enum *se; 103662306a36Sopenharmony_ci int mux_index = hdmi->num_cvt + hdmi->num_ports; 103762306a36Sopenharmony_ci int i, j; 103862306a36Sopenharmony_ci 103962306a36Sopenharmony_ci for (i = 0; i < hdmi->num_ports; i++) { 104062306a36Sopenharmony_ci kc = widgets[mux_index].kcontrol_news; 104162306a36Sopenharmony_ci se = (struct soc_enum *)kc->private_value; 104262306a36Sopenharmony_ci for (j = 0; j < hdmi->num_cvt; j++) { 104362306a36Sopenharmony_ci hdac_hdmi_fill_route(&route[rindex], 104462306a36Sopenharmony_ci widgets[mux_index].name, 104562306a36Sopenharmony_ci se->texts[j + 1], 104662306a36Sopenharmony_ci widgets[j].name, NULL); 104762306a36Sopenharmony_ci 104862306a36Sopenharmony_ci rindex++; 104962306a36Sopenharmony_ci } 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_ci mux_index++; 105262306a36Sopenharmony_ci } 105362306a36Sopenharmony_ci} 105462306a36Sopenharmony_ci 105562306a36Sopenharmony_ci/* 105662306a36Sopenharmony_ci * Widgets are added in the below sequence 105762306a36Sopenharmony_ci * Converter widgets for num converters enumerated 105862306a36Sopenharmony_ci * Pin-port widgets for num ports for Pins enumerated 105962306a36Sopenharmony_ci * Pin-port mux widgets to represent connenction list of pin widget 106062306a36Sopenharmony_ci * 106162306a36Sopenharmony_ci * For each port, one Mux and One output widget is added 106262306a36Sopenharmony_ci * Total widgets elements = num_cvt + (num_ports * 2); 106362306a36Sopenharmony_ci * 106462306a36Sopenharmony_ci * Routes are added as below: 106562306a36Sopenharmony_ci * pin-port mux -> pin (based on num_ports) 106662306a36Sopenharmony_ci * cvt -> "Input sel control" -> pin-port_mux 106762306a36Sopenharmony_ci * 106862306a36Sopenharmony_ci * Total route elements: 106962306a36Sopenharmony_ci * num_ports + (pin_muxes * num_cvt) 107062306a36Sopenharmony_ci */ 107162306a36Sopenharmony_cistatic int create_fill_widget_route_map(struct snd_soc_dapm_context *dapm) 107262306a36Sopenharmony_ci{ 107362306a36Sopenharmony_ci struct snd_soc_dapm_widget *widgets; 107462306a36Sopenharmony_ci struct snd_soc_dapm_route *route; 107562306a36Sopenharmony_ci struct hdac_device *hdev = dev_to_hdac_dev(dapm->dev); 107662306a36Sopenharmony_ci struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); 107762306a36Sopenharmony_ci struct snd_soc_dai_driver *dai_drv = hdmi->dai_drv; 107862306a36Sopenharmony_ci char widget_name[NAME_SIZE]; 107962306a36Sopenharmony_ci struct hdac_hdmi_cvt *cvt; 108062306a36Sopenharmony_ci struct hdac_hdmi_pin *pin; 108162306a36Sopenharmony_ci int ret, i = 0, num_routes = 0, j; 108262306a36Sopenharmony_ci 108362306a36Sopenharmony_ci if (list_empty(&hdmi->cvt_list) || list_empty(&hdmi->pin_list)) 108462306a36Sopenharmony_ci return -EINVAL; 108562306a36Sopenharmony_ci 108662306a36Sopenharmony_ci widgets = devm_kzalloc(dapm->dev, (sizeof(*widgets) * 108762306a36Sopenharmony_ci ((2 * hdmi->num_ports) + hdmi->num_cvt)), 108862306a36Sopenharmony_ci GFP_KERNEL); 108962306a36Sopenharmony_ci 109062306a36Sopenharmony_ci if (!widgets) 109162306a36Sopenharmony_ci return -ENOMEM; 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_ci /* DAPM widgets to represent each converter widget */ 109462306a36Sopenharmony_ci list_for_each_entry(cvt, &hdmi->cvt_list, head) { 109562306a36Sopenharmony_ci sprintf(widget_name, "Converter %d", cvt->nid); 109662306a36Sopenharmony_ci ret = hdac_hdmi_fill_widget_info(dapm->dev, &widgets[i], 109762306a36Sopenharmony_ci snd_soc_dapm_aif_in, cvt, 109862306a36Sopenharmony_ci widget_name, dai_drv[i].playback.stream_name, NULL, 0, 109962306a36Sopenharmony_ci hdac_hdmi_cvt_output_widget_event, 110062306a36Sopenharmony_ci SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD); 110162306a36Sopenharmony_ci if (ret < 0) 110262306a36Sopenharmony_ci return ret; 110362306a36Sopenharmony_ci i++; 110462306a36Sopenharmony_ci } 110562306a36Sopenharmony_ci 110662306a36Sopenharmony_ci list_for_each_entry(pin, &hdmi->pin_list, head) { 110762306a36Sopenharmony_ci for (j = 0; j < pin->num_ports; j++) { 110862306a36Sopenharmony_ci sprintf(widget_name, "hif%d-%d Output", 110962306a36Sopenharmony_ci pin->nid, pin->ports[j].id); 111062306a36Sopenharmony_ci ret = hdac_hdmi_fill_widget_info(dapm->dev, &widgets[i], 111162306a36Sopenharmony_ci snd_soc_dapm_output, &pin->ports[j], 111262306a36Sopenharmony_ci widget_name, NULL, NULL, 0, 111362306a36Sopenharmony_ci hdac_hdmi_pin_output_widget_event, 111462306a36Sopenharmony_ci SND_SOC_DAPM_PRE_PMU | 111562306a36Sopenharmony_ci SND_SOC_DAPM_POST_PMD); 111662306a36Sopenharmony_ci if (ret < 0) 111762306a36Sopenharmony_ci return ret; 111862306a36Sopenharmony_ci pin->ports[j].output_pin = widgets[i].name; 111962306a36Sopenharmony_ci i++; 112062306a36Sopenharmony_ci } 112162306a36Sopenharmony_ci } 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_ci /* DAPM widgets to represent the connection list to pin widget */ 112462306a36Sopenharmony_ci list_for_each_entry(pin, &hdmi->pin_list, head) { 112562306a36Sopenharmony_ci for (j = 0; j < pin->num_ports; j++) { 112662306a36Sopenharmony_ci sprintf(widget_name, "Pin%d-Port%d Mux", 112762306a36Sopenharmony_ci pin->nid, pin->ports[j].id); 112862306a36Sopenharmony_ci ret = hdac_hdmi_create_pin_port_muxs(hdev, 112962306a36Sopenharmony_ci &pin->ports[j], &widgets[i], 113062306a36Sopenharmony_ci widget_name); 113162306a36Sopenharmony_ci if (ret < 0) 113262306a36Sopenharmony_ci return ret; 113362306a36Sopenharmony_ci i++; 113462306a36Sopenharmony_ci 113562306a36Sopenharmony_ci /* For cvt to pin_mux mapping */ 113662306a36Sopenharmony_ci num_routes += hdmi->num_cvt; 113762306a36Sopenharmony_ci 113862306a36Sopenharmony_ci /* For pin_mux to pin mapping */ 113962306a36Sopenharmony_ci num_routes++; 114062306a36Sopenharmony_ci } 114162306a36Sopenharmony_ci } 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_ci route = devm_kzalloc(dapm->dev, (sizeof(*route) * num_routes), 114462306a36Sopenharmony_ci GFP_KERNEL); 114562306a36Sopenharmony_ci if (!route) 114662306a36Sopenharmony_ci return -ENOMEM; 114762306a36Sopenharmony_ci 114862306a36Sopenharmony_ci i = 0; 114962306a36Sopenharmony_ci /* Add pin <- NULL <- mux route map */ 115062306a36Sopenharmony_ci list_for_each_entry(pin, &hdmi->pin_list, head) { 115162306a36Sopenharmony_ci for (j = 0; j < pin->num_ports; j++) { 115262306a36Sopenharmony_ci int sink_index = i + hdmi->num_cvt; 115362306a36Sopenharmony_ci int src_index = sink_index + pin->num_ports * 115462306a36Sopenharmony_ci hdmi->num_pin; 115562306a36Sopenharmony_ci 115662306a36Sopenharmony_ci hdac_hdmi_fill_route(&route[i], 115762306a36Sopenharmony_ci widgets[sink_index].name, NULL, 115862306a36Sopenharmony_ci widgets[src_index].name, NULL); 115962306a36Sopenharmony_ci i++; 116062306a36Sopenharmony_ci } 116162306a36Sopenharmony_ci } 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_ci hdac_hdmi_add_pinmux_cvt_route(hdev, widgets, route, i); 116462306a36Sopenharmony_ci 116562306a36Sopenharmony_ci snd_soc_dapm_new_controls(dapm, widgets, 116662306a36Sopenharmony_ci ((2 * hdmi->num_ports) + hdmi->num_cvt)); 116762306a36Sopenharmony_ci 116862306a36Sopenharmony_ci snd_soc_dapm_add_routes(dapm, route, num_routes); 116962306a36Sopenharmony_ci snd_soc_dapm_new_widgets(dapm->card); 117062306a36Sopenharmony_ci 117162306a36Sopenharmony_ci return 0; 117262306a36Sopenharmony_ci 117362306a36Sopenharmony_ci} 117462306a36Sopenharmony_ci 117562306a36Sopenharmony_cistatic int hdac_hdmi_init_dai_map(struct hdac_device *hdev) 117662306a36Sopenharmony_ci{ 117762306a36Sopenharmony_ci struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); 117862306a36Sopenharmony_ci struct hdac_hdmi_dai_port_map *dai_map; 117962306a36Sopenharmony_ci struct hdac_hdmi_cvt *cvt; 118062306a36Sopenharmony_ci int dai_id = 0; 118162306a36Sopenharmony_ci 118262306a36Sopenharmony_ci if (list_empty(&hdmi->cvt_list)) 118362306a36Sopenharmony_ci return -EINVAL; 118462306a36Sopenharmony_ci 118562306a36Sopenharmony_ci list_for_each_entry(cvt, &hdmi->cvt_list, head) { 118662306a36Sopenharmony_ci dai_map = &hdmi->dai_map[dai_id]; 118762306a36Sopenharmony_ci dai_map->dai_id = dai_id; 118862306a36Sopenharmony_ci dai_map->cvt = cvt; 118962306a36Sopenharmony_ci 119062306a36Sopenharmony_ci dai_id++; 119162306a36Sopenharmony_ci 119262306a36Sopenharmony_ci if (dai_id == HDA_MAX_CVTS) { 119362306a36Sopenharmony_ci dev_warn(&hdev->dev, 119462306a36Sopenharmony_ci "Max dais supported: %d\n", dai_id); 119562306a36Sopenharmony_ci break; 119662306a36Sopenharmony_ci } 119762306a36Sopenharmony_ci } 119862306a36Sopenharmony_ci 119962306a36Sopenharmony_ci return 0; 120062306a36Sopenharmony_ci} 120162306a36Sopenharmony_ci 120262306a36Sopenharmony_cistatic int hdac_hdmi_add_cvt(struct hdac_device *hdev, hda_nid_t nid) 120362306a36Sopenharmony_ci{ 120462306a36Sopenharmony_ci struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); 120562306a36Sopenharmony_ci struct hdac_hdmi_cvt *cvt; 120662306a36Sopenharmony_ci char name[NAME_SIZE]; 120762306a36Sopenharmony_ci 120862306a36Sopenharmony_ci cvt = devm_kzalloc(&hdev->dev, sizeof(*cvt), GFP_KERNEL); 120962306a36Sopenharmony_ci if (!cvt) 121062306a36Sopenharmony_ci return -ENOMEM; 121162306a36Sopenharmony_ci 121262306a36Sopenharmony_ci cvt->nid = nid; 121362306a36Sopenharmony_ci sprintf(name, "cvt %d", cvt->nid); 121462306a36Sopenharmony_ci cvt->name = devm_kstrdup(&hdev->dev, name, GFP_KERNEL); 121562306a36Sopenharmony_ci if (!cvt->name) 121662306a36Sopenharmony_ci return -ENOMEM; 121762306a36Sopenharmony_ci 121862306a36Sopenharmony_ci list_add_tail(&cvt->head, &hdmi->cvt_list); 121962306a36Sopenharmony_ci hdmi->num_cvt++; 122062306a36Sopenharmony_ci 122162306a36Sopenharmony_ci return hdac_hdmi_query_cvt_params(hdev, cvt); 122262306a36Sopenharmony_ci} 122362306a36Sopenharmony_ci 122462306a36Sopenharmony_cistatic int hdac_hdmi_parse_eld(struct hdac_device *hdev, 122562306a36Sopenharmony_ci struct hdac_hdmi_port *port) 122662306a36Sopenharmony_ci{ 122762306a36Sopenharmony_ci unsigned int ver, mnl; 122862306a36Sopenharmony_ci 122962306a36Sopenharmony_ci ver = (port->eld.eld_buffer[DRM_ELD_VER] & DRM_ELD_VER_MASK) 123062306a36Sopenharmony_ci >> DRM_ELD_VER_SHIFT; 123162306a36Sopenharmony_ci 123262306a36Sopenharmony_ci if (ver != ELD_VER_CEA_861D && ver != ELD_VER_PARTIAL) { 123362306a36Sopenharmony_ci dev_err(&hdev->dev, "HDMI: Unknown ELD version %d\n", ver); 123462306a36Sopenharmony_ci return -EINVAL; 123562306a36Sopenharmony_ci } 123662306a36Sopenharmony_ci 123762306a36Sopenharmony_ci mnl = (port->eld.eld_buffer[DRM_ELD_CEA_EDID_VER_MNL] & 123862306a36Sopenharmony_ci DRM_ELD_MNL_MASK) >> DRM_ELD_MNL_SHIFT; 123962306a36Sopenharmony_ci 124062306a36Sopenharmony_ci if (mnl > ELD_MAX_MNL) { 124162306a36Sopenharmony_ci dev_err(&hdev->dev, "HDMI: MNL Invalid %d\n", mnl); 124262306a36Sopenharmony_ci return -EINVAL; 124362306a36Sopenharmony_ci } 124462306a36Sopenharmony_ci 124562306a36Sopenharmony_ci port->eld.info.spk_alloc = port->eld.eld_buffer[DRM_ELD_SPEAKER]; 124662306a36Sopenharmony_ci 124762306a36Sopenharmony_ci return 0; 124862306a36Sopenharmony_ci} 124962306a36Sopenharmony_ci 125062306a36Sopenharmony_cistatic void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin, 125162306a36Sopenharmony_ci struct hdac_hdmi_port *port) 125262306a36Sopenharmony_ci{ 125362306a36Sopenharmony_ci struct hdac_device *hdev = pin->hdev; 125462306a36Sopenharmony_ci struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); 125562306a36Sopenharmony_ci struct hdac_hdmi_pcm *pcm; 125662306a36Sopenharmony_ci int size = 0; 125762306a36Sopenharmony_ci int port_id = -1; 125862306a36Sopenharmony_ci bool eld_valid, eld_changed; 125962306a36Sopenharmony_ci 126062306a36Sopenharmony_ci if (!hdmi) 126162306a36Sopenharmony_ci return; 126262306a36Sopenharmony_ci 126362306a36Sopenharmony_ci /* 126462306a36Sopenharmony_ci * In case of non MST pin, get_eld info API expectes port 126562306a36Sopenharmony_ci * to be -1. 126662306a36Sopenharmony_ci */ 126762306a36Sopenharmony_ci mutex_lock(&hdmi->pin_mutex); 126862306a36Sopenharmony_ci port->eld.monitor_present = false; 126962306a36Sopenharmony_ci 127062306a36Sopenharmony_ci if (pin->mst_capable) 127162306a36Sopenharmony_ci port_id = port->id; 127262306a36Sopenharmony_ci 127362306a36Sopenharmony_ci size = snd_hdac_acomp_get_eld(hdev, pin->nid, port_id, 127462306a36Sopenharmony_ci &port->eld.monitor_present, 127562306a36Sopenharmony_ci port->eld.eld_buffer, 127662306a36Sopenharmony_ci ELD_MAX_SIZE); 127762306a36Sopenharmony_ci 127862306a36Sopenharmony_ci if (size > 0) { 127962306a36Sopenharmony_ci size = min(size, ELD_MAX_SIZE); 128062306a36Sopenharmony_ci if (hdac_hdmi_parse_eld(hdev, port) < 0) 128162306a36Sopenharmony_ci size = -EINVAL; 128262306a36Sopenharmony_ci } 128362306a36Sopenharmony_ci 128462306a36Sopenharmony_ci eld_valid = port->eld.eld_valid; 128562306a36Sopenharmony_ci 128662306a36Sopenharmony_ci if (size > 0) { 128762306a36Sopenharmony_ci port->eld.eld_valid = true; 128862306a36Sopenharmony_ci port->eld.eld_size = size; 128962306a36Sopenharmony_ci } else { 129062306a36Sopenharmony_ci port->eld.eld_valid = false; 129162306a36Sopenharmony_ci port->eld.eld_size = 0; 129262306a36Sopenharmony_ci } 129362306a36Sopenharmony_ci 129462306a36Sopenharmony_ci eld_changed = (eld_valid != port->eld.eld_valid); 129562306a36Sopenharmony_ci 129662306a36Sopenharmony_ci pcm = hdac_hdmi_get_pcm(hdev, port); 129762306a36Sopenharmony_ci 129862306a36Sopenharmony_ci if (!port->eld.monitor_present || !port->eld.eld_valid) { 129962306a36Sopenharmony_ci 130062306a36Sopenharmony_ci dev_err(&hdev->dev, "%s: disconnect for pin:port %d:%d\n", 130162306a36Sopenharmony_ci __func__, pin->nid, port->id); 130262306a36Sopenharmony_ci 130362306a36Sopenharmony_ci /* 130462306a36Sopenharmony_ci * PCMs are not registered during device probe, so don't 130562306a36Sopenharmony_ci * report jack here. It will be done in usermode mux 130662306a36Sopenharmony_ci * control select. 130762306a36Sopenharmony_ci */ 130862306a36Sopenharmony_ci if (pcm) { 130962306a36Sopenharmony_ci hdac_hdmi_jack_report(pcm, port, false); 131062306a36Sopenharmony_ci schedule_work(&port->dapm_work); 131162306a36Sopenharmony_ci } 131262306a36Sopenharmony_ci 131362306a36Sopenharmony_ci mutex_unlock(&hdmi->pin_mutex); 131462306a36Sopenharmony_ci return; 131562306a36Sopenharmony_ci } 131662306a36Sopenharmony_ci 131762306a36Sopenharmony_ci if (port->eld.monitor_present && port->eld.eld_valid) { 131862306a36Sopenharmony_ci if (pcm) { 131962306a36Sopenharmony_ci hdac_hdmi_jack_report(pcm, port, true); 132062306a36Sopenharmony_ci schedule_work(&port->dapm_work); 132162306a36Sopenharmony_ci } 132262306a36Sopenharmony_ci 132362306a36Sopenharmony_ci print_hex_dump_debug("ELD: ", DUMP_PREFIX_OFFSET, 16, 1, 132462306a36Sopenharmony_ci port->eld.eld_buffer, port->eld.eld_size, false); 132562306a36Sopenharmony_ci 132662306a36Sopenharmony_ci } 132762306a36Sopenharmony_ci mutex_unlock(&hdmi->pin_mutex); 132862306a36Sopenharmony_ci 132962306a36Sopenharmony_ci if (eld_changed && pcm) 133062306a36Sopenharmony_ci snd_ctl_notify(hdmi->card, 133162306a36Sopenharmony_ci SNDRV_CTL_EVENT_MASK_VALUE | 133262306a36Sopenharmony_ci SNDRV_CTL_EVENT_MASK_INFO, 133362306a36Sopenharmony_ci &pcm->eld_ctl->id); 133462306a36Sopenharmony_ci} 133562306a36Sopenharmony_ci 133662306a36Sopenharmony_cistatic int hdac_hdmi_add_ports(struct hdac_device *hdev, 133762306a36Sopenharmony_ci struct hdac_hdmi_pin *pin) 133862306a36Sopenharmony_ci{ 133962306a36Sopenharmony_ci struct hdac_hdmi_port *ports; 134062306a36Sopenharmony_ci int max_ports = HDA_MAX_PORTS; 134162306a36Sopenharmony_ci int i; 134262306a36Sopenharmony_ci 134362306a36Sopenharmony_ci /* 134462306a36Sopenharmony_ci * FIXME: max_port may vary for each platform, so pass this as 134562306a36Sopenharmony_ci * as driver data or query from i915 interface when this API is 134662306a36Sopenharmony_ci * implemented. 134762306a36Sopenharmony_ci */ 134862306a36Sopenharmony_ci 134962306a36Sopenharmony_ci ports = devm_kcalloc(&hdev->dev, max_ports, sizeof(*ports), GFP_KERNEL); 135062306a36Sopenharmony_ci if (!ports) 135162306a36Sopenharmony_ci return -ENOMEM; 135262306a36Sopenharmony_ci 135362306a36Sopenharmony_ci for (i = 0; i < max_ports; i++) { 135462306a36Sopenharmony_ci ports[i].id = i; 135562306a36Sopenharmony_ci ports[i].pin = pin; 135662306a36Sopenharmony_ci INIT_WORK(&ports[i].dapm_work, hdac_hdmi_jack_dapm_work); 135762306a36Sopenharmony_ci } 135862306a36Sopenharmony_ci pin->ports = ports; 135962306a36Sopenharmony_ci pin->num_ports = max_ports; 136062306a36Sopenharmony_ci return 0; 136162306a36Sopenharmony_ci} 136262306a36Sopenharmony_ci 136362306a36Sopenharmony_cistatic int hdac_hdmi_add_pin(struct hdac_device *hdev, hda_nid_t nid) 136462306a36Sopenharmony_ci{ 136562306a36Sopenharmony_ci struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); 136662306a36Sopenharmony_ci struct hdac_hdmi_pin *pin; 136762306a36Sopenharmony_ci int ret; 136862306a36Sopenharmony_ci 136962306a36Sopenharmony_ci pin = devm_kzalloc(&hdev->dev, sizeof(*pin), GFP_KERNEL); 137062306a36Sopenharmony_ci if (!pin) 137162306a36Sopenharmony_ci return -ENOMEM; 137262306a36Sopenharmony_ci 137362306a36Sopenharmony_ci pin->nid = nid; 137462306a36Sopenharmony_ci pin->mst_capable = false; 137562306a36Sopenharmony_ci pin->hdev = hdev; 137662306a36Sopenharmony_ci ret = hdac_hdmi_add_ports(hdev, pin); 137762306a36Sopenharmony_ci if (ret < 0) 137862306a36Sopenharmony_ci return ret; 137962306a36Sopenharmony_ci 138062306a36Sopenharmony_ci list_add_tail(&pin->head, &hdmi->pin_list); 138162306a36Sopenharmony_ci hdmi->num_pin++; 138262306a36Sopenharmony_ci hdmi->num_ports += pin->num_ports; 138362306a36Sopenharmony_ci 138462306a36Sopenharmony_ci return 0; 138562306a36Sopenharmony_ci} 138662306a36Sopenharmony_ci 138762306a36Sopenharmony_ci#define INTEL_VENDOR_NID 0x08 138862306a36Sopenharmony_ci#define INTEL_GLK_VENDOR_NID 0x0b 138962306a36Sopenharmony_ci#define INTEL_GET_VENDOR_VERB 0xf81 139062306a36Sopenharmony_ci#define INTEL_SET_VENDOR_VERB 0x781 139162306a36Sopenharmony_ci#define INTEL_EN_DP12 0x02 /* enable DP 1.2 features */ 139262306a36Sopenharmony_ci#define INTEL_EN_ALL_PIN_CVTS 0x01 /* enable 2nd & 3rd pins and convertors */ 139362306a36Sopenharmony_ci 139462306a36Sopenharmony_cistatic void hdac_hdmi_skl_enable_all_pins(struct hdac_device *hdev) 139562306a36Sopenharmony_ci{ 139662306a36Sopenharmony_ci unsigned int vendor_param; 139762306a36Sopenharmony_ci struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); 139862306a36Sopenharmony_ci unsigned int vendor_nid = hdmi->drv_data->vendor_nid; 139962306a36Sopenharmony_ci 140062306a36Sopenharmony_ci vendor_param = snd_hdac_codec_read(hdev, vendor_nid, 0, 140162306a36Sopenharmony_ci INTEL_GET_VENDOR_VERB, 0); 140262306a36Sopenharmony_ci if (vendor_param == -1 || vendor_param & INTEL_EN_ALL_PIN_CVTS) 140362306a36Sopenharmony_ci return; 140462306a36Sopenharmony_ci 140562306a36Sopenharmony_ci vendor_param |= INTEL_EN_ALL_PIN_CVTS; 140662306a36Sopenharmony_ci vendor_param = snd_hdac_codec_read(hdev, vendor_nid, 0, 140762306a36Sopenharmony_ci INTEL_SET_VENDOR_VERB, vendor_param); 140862306a36Sopenharmony_ci if (vendor_param == -1) 140962306a36Sopenharmony_ci return; 141062306a36Sopenharmony_ci} 141162306a36Sopenharmony_ci 141262306a36Sopenharmony_cistatic void hdac_hdmi_skl_enable_dp12(struct hdac_device *hdev) 141362306a36Sopenharmony_ci{ 141462306a36Sopenharmony_ci unsigned int vendor_param; 141562306a36Sopenharmony_ci struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); 141662306a36Sopenharmony_ci unsigned int vendor_nid = hdmi->drv_data->vendor_nid; 141762306a36Sopenharmony_ci 141862306a36Sopenharmony_ci vendor_param = snd_hdac_codec_read(hdev, vendor_nid, 0, 141962306a36Sopenharmony_ci INTEL_GET_VENDOR_VERB, 0); 142062306a36Sopenharmony_ci if (vendor_param == -1 || vendor_param & INTEL_EN_DP12) 142162306a36Sopenharmony_ci return; 142262306a36Sopenharmony_ci 142362306a36Sopenharmony_ci /* enable DP1.2 mode */ 142462306a36Sopenharmony_ci vendor_param |= INTEL_EN_DP12; 142562306a36Sopenharmony_ci vendor_param = snd_hdac_codec_read(hdev, vendor_nid, 0, 142662306a36Sopenharmony_ci INTEL_SET_VENDOR_VERB, vendor_param); 142762306a36Sopenharmony_ci if (vendor_param == -1) 142862306a36Sopenharmony_ci return; 142962306a36Sopenharmony_ci 143062306a36Sopenharmony_ci} 143162306a36Sopenharmony_ci 143262306a36Sopenharmony_cistatic int hdac_hdmi_eld_ctl_info(struct snd_kcontrol *kcontrol, 143362306a36Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 143462306a36Sopenharmony_ci{ 143562306a36Sopenharmony_ci struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); 143662306a36Sopenharmony_ci struct hdac_hdmi_priv *hdmi = snd_soc_component_get_drvdata(component); 143762306a36Sopenharmony_ci struct hdac_hdmi_pcm *pcm; 143862306a36Sopenharmony_ci struct hdac_hdmi_port *port; 143962306a36Sopenharmony_ci struct hdac_hdmi_eld *eld; 144062306a36Sopenharmony_ci 144162306a36Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; 144262306a36Sopenharmony_ci uinfo->count = 0; 144362306a36Sopenharmony_ci 144462306a36Sopenharmony_ci pcm = get_hdmi_pcm_from_id(hdmi, kcontrol->id.device); 144562306a36Sopenharmony_ci if (!pcm) { 144662306a36Sopenharmony_ci dev_dbg(component->dev, "%s: no pcm, device %d\n", __func__, 144762306a36Sopenharmony_ci kcontrol->id.device); 144862306a36Sopenharmony_ci return 0; 144962306a36Sopenharmony_ci } 145062306a36Sopenharmony_ci 145162306a36Sopenharmony_ci if (list_empty(&pcm->port_list)) { 145262306a36Sopenharmony_ci dev_dbg(component->dev, "%s: empty port list, device %d\n", 145362306a36Sopenharmony_ci __func__, kcontrol->id.device); 145462306a36Sopenharmony_ci return 0; 145562306a36Sopenharmony_ci } 145662306a36Sopenharmony_ci 145762306a36Sopenharmony_ci mutex_lock(&hdmi->pin_mutex); 145862306a36Sopenharmony_ci 145962306a36Sopenharmony_ci list_for_each_entry(port, &pcm->port_list, head) { 146062306a36Sopenharmony_ci eld = &port->eld; 146162306a36Sopenharmony_ci 146262306a36Sopenharmony_ci if (eld->eld_valid) { 146362306a36Sopenharmony_ci uinfo->count = eld->eld_size; 146462306a36Sopenharmony_ci break; 146562306a36Sopenharmony_ci } 146662306a36Sopenharmony_ci } 146762306a36Sopenharmony_ci 146862306a36Sopenharmony_ci mutex_unlock(&hdmi->pin_mutex); 146962306a36Sopenharmony_ci 147062306a36Sopenharmony_ci return 0; 147162306a36Sopenharmony_ci} 147262306a36Sopenharmony_ci 147362306a36Sopenharmony_cistatic int hdac_hdmi_eld_ctl_get(struct snd_kcontrol *kcontrol, 147462306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 147562306a36Sopenharmony_ci{ 147662306a36Sopenharmony_ci struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); 147762306a36Sopenharmony_ci struct hdac_hdmi_priv *hdmi = snd_soc_component_get_drvdata(component); 147862306a36Sopenharmony_ci struct hdac_hdmi_pcm *pcm; 147962306a36Sopenharmony_ci struct hdac_hdmi_port *port; 148062306a36Sopenharmony_ci struct hdac_hdmi_eld *eld; 148162306a36Sopenharmony_ci 148262306a36Sopenharmony_ci memset(ucontrol->value.bytes.data, 0, sizeof(ucontrol->value.bytes.data)); 148362306a36Sopenharmony_ci 148462306a36Sopenharmony_ci pcm = get_hdmi_pcm_from_id(hdmi, kcontrol->id.device); 148562306a36Sopenharmony_ci if (!pcm) { 148662306a36Sopenharmony_ci dev_dbg(component->dev, "%s: no pcm, device %d\n", __func__, 148762306a36Sopenharmony_ci kcontrol->id.device); 148862306a36Sopenharmony_ci return 0; 148962306a36Sopenharmony_ci } 149062306a36Sopenharmony_ci 149162306a36Sopenharmony_ci if (list_empty(&pcm->port_list)) { 149262306a36Sopenharmony_ci dev_dbg(component->dev, "%s: empty port list, device %d\n", 149362306a36Sopenharmony_ci __func__, kcontrol->id.device); 149462306a36Sopenharmony_ci return 0; 149562306a36Sopenharmony_ci } 149662306a36Sopenharmony_ci 149762306a36Sopenharmony_ci mutex_lock(&hdmi->pin_mutex); 149862306a36Sopenharmony_ci 149962306a36Sopenharmony_ci list_for_each_entry(port, &pcm->port_list, head) { 150062306a36Sopenharmony_ci eld = &port->eld; 150162306a36Sopenharmony_ci 150262306a36Sopenharmony_ci if (!eld->eld_valid) 150362306a36Sopenharmony_ci continue; 150462306a36Sopenharmony_ci 150562306a36Sopenharmony_ci if (eld->eld_size > ARRAY_SIZE(ucontrol->value.bytes.data) || 150662306a36Sopenharmony_ci eld->eld_size > ELD_MAX_SIZE) { 150762306a36Sopenharmony_ci mutex_unlock(&hdmi->pin_mutex); 150862306a36Sopenharmony_ci 150962306a36Sopenharmony_ci dev_err(component->dev, "%s: buffer too small, device %d eld_size %d\n", 151062306a36Sopenharmony_ci __func__, kcontrol->id.device, eld->eld_size); 151162306a36Sopenharmony_ci snd_BUG(); 151262306a36Sopenharmony_ci return -EINVAL; 151362306a36Sopenharmony_ci } 151462306a36Sopenharmony_ci 151562306a36Sopenharmony_ci memcpy(ucontrol->value.bytes.data, eld->eld_buffer, 151662306a36Sopenharmony_ci eld->eld_size); 151762306a36Sopenharmony_ci break; 151862306a36Sopenharmony_ci } 151962306a36Sopenharmony_ci 152062306a36Sopenharmony_ci mutex_unlock(&hdmi->pin_mutex); 152162306a36Sopenharmony_ci 152262306a36Sopenharmony_ci return 0; 152362306a36Sopenharmony_ci} 152462306a36Sopenharmony_ci 152562306a36Sopenharmony_cistatic int hdac_hdmi_create_eld_ctl(struct snd_soc_component *component, struct hdac_hdmi_pcm *pcm) 152662306a36Sopenharmony_ci{ 152762306a36Sopenharmony_ci struct snd_kcontrol *kctl; 152862306a36Sopenharmony_ci struct snd_kcontrol_new hdmi_eld_ctl = { 152962306a36Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READ | 153062306a36Sopenharmony_ci SNDRV_CTL_ELEM_ACCESS_VOLATILE, 153162306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_PCM, 153262306a36Sopenharmony_ci .name = "ELD", 153362306a36Sopenharmony_ci .info = hdac_hdmi_eld_ctl_info, 153462306a36Sopenharmony_ci .get = hdac_hdmi_eld_ctl_get, 153562306a36Sopenharmony_ci .device = pcm->pcm_id, 153662306a36Sopenharmony_ci }; 153762306a36Sopenharmony_ci 153862306a36Sopenharmony_ci /* add ELD ctl with the device number corresponding to the PCM stream */ 153962306a36Sopenharmony_ci kctl = snd_ctl_new1(&hdmi_eld_ctl, component); 154062306a36Sopenharmony_ci if (!kctl) 154162306a36Sopenharmony_ci return -ENOMEM; 154262306a36Sopenharmony_ci 154362306a36Sopenharmony_ci pcm->eld_ctl = kctl; 154462306a36Sopenharmony_ci 154562306a36Sopenharmony_ci return snd_ctl_add(component->card->snd_card, kctl); 154662306a36Sopenharmony_ci} 154762306a36Sopenharmony_ci 154862306a36Sopenharmony_cistatic const struct snd_soc_dai_ops hdmi_dai_ops = { 154962306a36Sopenharmony_ci .startup = hdac_hdmi_pcm_open, 155062306a36Sopenharmony_ci .shutdown = hdac_hdmi_pcm_close, 155162306a36Sopenharmony_ci .hw_params = hdac_hdmi_set_hw_params, 155262306a36Sopenharmony_ci .set_stream = hdac_hdmi_set_stream, 155362306a36Sopenharmony_ci}; 155462306a36Sopenharmony_ci 155562306a36Sopenharmony_ci/* 155662306a36Sopenharmony_ci * Each converter can support a stream independently. So a dai is created 155762306a36Sopenharmony_ci * based on the number of converter queried. 155862306a36Sopenharmony_ci */ 155962306a36Sopenharmony_cistatic int hdac_hdmi_create_dais(struct hdac_device *hdev, 156062306a36Sopenharmony_ci struct snd_soc_dai_driver **dais, 156162306a36Sopenharmony_ci struct hdac_hdmi_priv *hdmi, int num_dais) 156262306a36Sopenharmony_ci{ 156362306a36Sopenharmony_ci struct snd_soc_dai_driver *hdmi_dais; 156462306a36Sopenharmony_ci struct hdac_hdmi_cvt *cvt; 156562306a36Sopenharmony_ci char name[NAME_SIZE], dai_name[NAME_SIZE]; 156662306a36Sopenharmony_ci int i = 0; 156762306a36Sopenharmony_ci u32 rates, bps; 156862306a36Sopenharmony_ci unsigned int rate_max = 384000, rate_min = 8000; 156962306a36Sopenharmony_ci u64 formats; 157062306a36Sopenharmony_ci int ret; 157162306a36Sopenharmony_ci 157262306a36Sopenharmony_ci hdmi_dais = devm_kzalloc(&hdev->dev, 157362306a36Sopenharmony_ci (sizeof(*hdmi_dais) * num_dais), 157462306a36Sopenharmony_ci GFP_KERNEL); 157562306a36Sopenharmony_ci if (!hdmi_dais) 157662306a36Sopenharmony_ci return -ENOMEM; 157762306a36Sopenharmony_ci 157862306a36Sopenharmony_ci list_for_each_entry(cvt, &hdmi->cvt_list, head) { 157962306a36Sopenharmony_ci ret = snd_hdac_query_supported_pcm(hdev, cvt->nid, 158062306a36Sopenharmony_ci &rates, &formats, &bps); 158162306a36Sopenharmony_ci if (ret) 158262306a36Sopenharmony_ci return ret; 158362306a36Sopenharmony_ci 158462306a36Sopenharmony_ci /* Filter out 44.1, 88.2 and 176.4Khz */ 158562306a36Sopenharmony_ci rates &= ~(SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_88200 | 158662306a36Sopenharmony_ci SNDRV_PCM_RATE_176400); 158762306a36Sopenharmony_ci if (!rates) 158862306a36Sopenharmony_ci return -EINVAL; 158962306a36Sopenharmony_ci 159062306a36Sopenharmony_ci sprintf(dai_name, "intel-hdmi-hifi%d", i+1); 159162306a36Sopenharmony_ci hdmi_dais[i].name = devm_kstrdup(&hdev->dev, 159262306a36Sopenharmony_ci dai_name, GFP_KERNEL); 159362306a36Sopenharmony_ci 159462306a36Sopenharmony_ci if (!hdmi_dais[i].name) 159562306a36Sopenharmony_ci return -ENOMEM; 159662306a36Sopenharmony_ci 159762306a36Sopenharmony_ci snprintf(name, sizeof(name), "hifi%d", i+1); 159862306a36Sopenharmony_ci hdmi_dais[i].playback.stream_name = 159962306a36Sopenharmony_ci devm_kstrdup(&hdev->dev, name, GFP_KERNEL); 160062306a36Sopenharmony_ci if (!hdmi_dais[i].playback.stream_name) 160162306a36Sopenharmony_ci return -ENOMEM; 160262306a36Sopenharmony_ci 160362306a36Sopenharmony_ci /* 160462306a36Sopenharmony_ci * Set caps based on capability queried from the converter. 160562306a36Sopenharmony_ci * It will be constrained runtime based on ELD queried. 160662306a36Sopenharmony_ci */ 160762306a36Sopenharmony_ci hdmi_dais[i].playback.formats = formats; 160862306a36Sopenharmony_ci hdmi_dais[i].playback.rates = rates; 160962306a36Sopenharmony_ci hdmi_dais[i].playback.rate_max = rate_max; 161062306a36Sopenharmony_ci hdmi_dais[i].playback.rate_min = rate_min; 161162306a36Sopenharmony_ci hdmi_dais[i].playback.channels_min = 2; 161262306a36Sopenharmony_ci hdmi_dais[i].playback.channels_max = 2; 161362306a36Sopenharmony_ci hdmi_dais[i].playback.sig_bits = bps; 161462306a36Sopenharmony_ci hdmi_dais[i].ops = &hdmi_dai_ops; 161562306a36Sopenharmony_ci i++; 161662306a36Sopenharmony_ci } 161762306a36Sopenharmony_ci 161862306a36Sopenharmony_ci *dais = hdmi_dais; 161962306a36Sopenharmony_ci hdmi->dai_drv = hdmi_dais; 162062306a36Sopenharmony_ci 162162306a36Sopenharmony_ci return 0; 162262306a36Sopenharmony_ci} 162362306a36Sopenharmony_ci 162462306a36Sopenharmony_ci/* 162562306a36Sopenharmony_ci * Parse all nodes and store the cvt/pin nids in array 162662306a36Sopenharmony_ci * Add one time initialization for pin and cvt widgets 162762306a36Sopenharmony_ci */ 162862306a36Sopenharmony_cistatic int hdac_hdmi_parse_and_map_nid(struct hdac_device *hdev, 162962306a36Sopenharmony_ci struct snd_soc_dai_driver **dais, int *num_dais) 163062306a36Sopenharmony_ci{ 163162306a36Sopenharmony_ci hda_nid_t nid; 163262306a36Sopenharmony_ci int i, num_nodes; 163362306a36Sopenharmony_ci struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); 163462306a36Sopenharmony_ci int ret; 163562306a36Sopenharmony_ci 163662306a36Sopenharmony_ci hdac_hdmi_skl_enable_all_pins(hdev); 163762306a36Sopenharmony_ci hdac_hdmi_skl_enable_dp12(hdev); 163862306a36Sopenharmony_ci 163962306a36Sopenharmony_ci num_nodes = snd_hdac_get_sub_nodes(hdev, hdev->afg, &nid); 164062306a36Sopenharmony_ci if (!nid || num_nodes <= 0) { 164162306a36Sopenharmony_ci dev_warn(&hdev->dev, "HDMI: failed to get afg sub nodes\n"); 164262306a36Sopenharmony_ci return -EINVAL; 164362306a36Sopenharmony_ci } 164462306a36Sopenharmony_ci 164562306a36Sopenharmony_ci for (i = 0; i < num_nodes; i++, nid++) { 164662306a36Sopenharmony_ci unsigned int caps; 164762306a36Sopenharmony_ci unsigned int type; 164862306a36Sopenharmony_ci 164962306a36Sopenharmony_ci caps = get_wcaps(hdev, nid); 165062306a36Sopenharmony_ci type = get_wcaps_type(caps); 165162306a36Sopenharmony_ci 165262306a36Sopenharmony_ci if (!(caps & AC_WCAP_DIGITAL)) 165362306a36Sopenharmony_ci continue; 165462306a36Sopenharmony_ci 165562306a36Sopenharmony_ci switch (type) { 165662306a36Sopenharmony_ci 165762306a36Sopenharmony_ci case AC_WID_AUD_OUT: 165862306a36Sopenharmony_ci ret = hdac_hdmi_add_cvt(hdev, nid); 165962306a36Sopenharmony_ci if (ret < 0) 166062306a36Sopenharmony_ci return ret; 166162306a36Sopenharmony_ci break; 166262306a36Sopenharmony_ci 166362306a36Sopenharmony_ci case AC_WID_PIN: 166462306a36Sopenharmony_ci ret = hdac_hdmi_add_pin(hdev, nid); 166562306a36Sopenharmony_ci if (ret < 0) 166662306a36Sopenharmony_ci return ret; 166762306a36Sopenharmony_ci break; 166862306a36Sopenharmony_ci } 166962306a36Sopenharmony_ci } 167062306a36Sopenharmony_ci 167162306a36Sopenharmony_ci if (!hdmi->num_pin || !hdmi->num_cvt) { 167262306a36Sopenharmony_ci ret = -EIO; 167362306a36Sopenharmony_ci dev_err(&hdev->dev, "Bad pin/cvt setup in %s\n", __func__); 167462306a36Sopenharmony_ci return ret; 167562306a36Sopenharmony_ci } 167662306a36Sopenharmony_ci 167762306a36Sopenharmony_ci ret = hdac_hdmi_create_dais(hdev, dais, hdmi, hdmi->num_cvt); 167862306a36Sopenharmony_ci if (ret) { 167962306a36Sopenharmony_ci dev_err(&hdev->dev, "Failed to create dais with err: %d\n", 168062306a36Sopenharmony_ci ret); 168162306a36Sopenharmony_ci return ret; 168262306a36Sopenharmony_ci } 168362306a36Sopenharmony_ci 168462306a36Sopenharmony_ci *num_dais = hdmi->num_cvt; 168562306a36Sopenharmony_ci ret = hdac_hdmi_init_dai_map(hdev); 168662306a36Sopenharmony_ci if (ret < 0) 168762306a36Sopenharmony_ci dev_err(&hdev->dev, "Failed to init DAI map with err: %d\n", 168862306a36Sopenharmony_ci ret); 168962306a36Sopenharmony_ci return ret; 169062306a36Sopenharmony_ci} 169162306a36Sopenharmony_ci 169262306a36Sopenharmony_cistatic int hdac_hdmi_pin2port(void *aptr, int pin) 169362306a36Sopenharmony_ci{ 169462306a36Sopenharmony_ci return pin - 4; /* map NID 0x05 -> port #1 */ 169562306a36Sopenharmony_ci} 169662306a36Sopenharmony_ci 169762306a36Sopenharmony_cistatic void hdac_hdmi_eld_notify_cb(void *aptr, int port, int pipe) 169862306a36Sopenharmony_ci{ 169962306a36Sopenharmony_ci struct hdac_device *hdev = aptr; 170062306a36Sopenharmony_ci struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); 170162306a36Sopenharmony_ci struct hdac_hdmi_pin *pin; 170262306a36Sopenharmony_ci struct hdac_hdmi_port *hport = NULL; 170362306a36Sopenharmony_ci struct snd_soc_component *component = hdmi->component; 170462306a36Sopenharmony_ci int i; 170562306a36Sopenharmony_ci 170662306a36Sopenharmony_ci /* Don't know how this mapping is derived */ 170762306a36Sopenharmony_ci hda_nid_t pin_nid = port + 0x04; 170862306a36Sopenharmony_ci 170962306a36Sopenharmony_ci dev_dbg(&hdev->dev, "%s: for pin:%d port=%d\n", __func__, 171062306a36Sopenharmony_ci pin_nid, pipe); 171162306a36Sopenharmony_ci 171262306a36Sopenharmony_ci /* 171362306a36Sopenharmony_ci * skip notification during system suspend (but not in runtime PM); 171462306a36Sopenharmony_ci * the state will be updated at resume. Also since the ELD and 171562306a36Sopenharmony_ci * connection states are updated in anyway at the end of the resume, 171662306a36Sopenharmony_ci * we can skip it when received during PM process. 171762306a36Sopenharmony_ci */ 171862306a36Sopenharmony_ci if (snd_power_get_state(component->card->snd_card) != 171962306a36Sopenharmony_ci SNDRV_CTL_POWER_D0) 172062306a36Sopenharmony_ci return; 172162306a36Sopenharmony_ci 172262306a36Sopenharmony_ci if (atomic_read(&hdev->in_pm)) 172362306a36Sopenharmony_ci return; 172462306a36Sopenharmony_ci 172562306a36Sopenharmony_ci list_for_each_entry(pin, &hdmi->pin_list, head) { 172662306a36Sopenharmony_ci if (pin->nid != pin_nid) 172762306a36Sopenharmony_ci continue; 172862306a36Sopenharmony_ci 172962306a36Sopenharmony_ci /* In case of non MST pin, pipe is -1 */ 173062306a36Sopenharmony_ci if (pipe == -1) { 173162306a36Sopenharmony_ci pin->mst_capable = false; 173262306a36Sopenharmony_ci /* if not MST, default is port[0] */ 173362306a36Sopenharmony_ci hport = &pin->ports[0]; 173462306a36Sopenharmony_ci } else { 173562306a36Sopenharmony_ci for (i = 0; i < pin->num_ports; i++) { 173662306a36Sopenharmony_ci pin->mst_capable = true; 173762306a36Sopenharmony_ci if (pin->ports[i].id == pipe) { 173862306a36Sopenharmony_ci hport = &pin->ports[i]; 173962306a36Sopenharmony_ci break; 174062306a36Sopenharmony_ci } 174162306a36Sopenharmony_ci } 174262306a36Sopenharmony_ci } 174362306a36Sopenharmony_ci 174462306a36Sopenharmony_ci if (hport) 174562306a36Sopenharmony_ci hdac_hdmi_present_sense(pin, hport); 174662306a36Sopenharmony_ci } 174762306a36Sopenharmony_ci 174862306a36Sopenharmony_ci} 174962306a36Sopenharmony_ci 175062306a36Sopenharmony_cistatic struct drm_audio_component_audio_ops aops = { 175162306a36Sopenharmony_ci .pin2port = hdac_hdmi_pin2port, 175262306a36Sopenharmony_ci .pin_eld_notify = hdac_hdmi_eld_notify_cb, 175362306a36Sopenharmony_ci}; 175462306a36Sopenharmony_ci 175562306a36Sopenharmony_cistatic struct snd_pcm *hdac_hdmi_get_pcm_from_id(struct snd_soc_card *card, 175662306a36Sopenharmony_ci int device) 175762306a36Sopenharmony_ci{ 175862306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd; 175962306a36Sopenharmony_ci 176062306a36Sopenharmony_ci for_each_card_rtds(card, rtd) { 176162306a36Sopenharmony_ci if (rtd->pcm && (rtd->pcm->device == device)) 176262306a36Sopenharmony_ci return rtd->pcm; 176362306a36Sopenharmony_ci } 176462306a36Sopenharmony_ci 176562306a36Sopenharmony_ci return NULL; 176662306a36Sopenharmony_ci} 176762306a36Sopenharmony_ci 176862306a36Sopenharmony_ci/* create jack pin kcontrols */ 176962306a36Sopenharmony_cistatic int create_fill_jack_kcontrols(struct snd_soc_card *card, 177062306a36Sopenharmony_ci struct hdac_device *hdev) 177162306a36Sopenharmony_ci{ 177262306a36Sopenharmony_ci struct hdac_hdmi_pin *pin; 177362306a36Sopenharmony_ci struct snd_kcontrol_new *kc; 177462306a36Sopenharmony_ci char kc_name[NAME_SIZE], xname[NAME_SIZE]; 177562306a36Sopenharmony_ci char *name; 177662306a36Sopenharmony_ci int i = 0, j; 177762306a36Sopenharmony_ci struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); 177862306a36Sopenharmony_ci struct snd_soc_component *component = hdmi->component; 177962306a36Sopenharmony_ci 178062306a36Sopenharmony_ci kc = devm_kcalloc(component->dev, hdmi->num_ports, 178162306a36Sopenharmony_ci sizeof(*kc), GFP_KERNEL); 178262306a36Sopenharmony_ci 178362306a36Sopenharmony_ci if (!kc) 178462306a36Sopenharmony_ci return -ENOMEM; 178562306a36Sopenharmony_ci 178662306a36Sopenharmony_ci list_for_each_entry(pin, &hdmi->pin_list, head) { 178762306a36Sopenharmony_ci for (j = 0; j < pin->num_ports; j++) { 178862306a36Sopenharmony_ci snprintf(xname, sizeof(xname), "hif%d-%d Jack", 178962306a36Sopenharmony_ci pin->nid, pin->ports[j].id); 179062306a36Sopenharmony_ci name = devm_kstrdup(component->dev, xname, GFP_KERNEL); 179162306a36Sopenharmony_ci if (!name) 179262306a36Sopenharmony_ci return -ENOMEM; 179362306a36Sopenharmony_ci snprintf(kc_name, sizeof(kc_name), "%s Switch", xname); 179462306a36Sopenharmony_ci kc[i].name = devm_kstrdup(component->dev, kc_name, 179562306a36Sopenharmony_ci GFP_KERNEL); 179662306a36Sopenharmony_ci if (!kc[i].name) 179762306a36Sopenharmony_ci return -ENOMEM; 179862306a36Sopenharmony_ci 179962306a36Sopenharmony_ci kc[i].private_value = (unsigned long)name; 180062306a36Sopenharmony_ci kc[i].iface = SNDRV_CTL_ELEM_IFACE_MIXER; 180162306a36Sopenharmony_ci kc[i].access = 0; 180262306a36Sopenharmony_ci kc[i].info = snd_soc_dapm_info_pin_switch; 180362306a36Sopenharmony_ci kc[i].put = snd_soc_dapm_put_pin_switch; 180462306a36Sopenharmony_ci kc[i].get = snd_soc_dapm_get_pin_switch; 180562306a36Sopenharmony_ci i++; 180662306a36Sopenharmony_ci } 180762306a36Sopenharmony_ci } 180862306a36Sopenharmony_ci 180962306a36Sopenharmony_ci return snd_soc_add_card_controls(card, kc, i); 181062306a36Sopenharmony_ci} 181162306a36Sopenharmony_ci 181262306a36Sopenharmony_ciint hdac_hdmi_jack_port_init(struct snd_soc_component *component, 181362306a36Sopenharmony_ci struct snd_soc_dapm_context *dapm) 181462306a36Sopenharmony_ci{ 181562306a36Sopenharmony_ci struct hdac_hdmi_priv *hdmi = snd_soc_component_get_drvdata(component); 181662306a36Sopenharmony_ci struct hdac_device *hdev = hdmi->hdev; 181762306a36Sopenharmony_ci struct hdac_hdmi_pin *pin; 181862306a36Sopenharmony_ci struct snd_soc_dapm_widget *widgets; 181962306a36Sopenharmony_ci struct snd_soc_dapm_route *route; 182062306a36Sopenharmony_ci char w_name[NAME_SIZE]; 182162306a36Sopenharmony_ci int i = 0, j, ret; 182262306a36Sopenharmony_ci 182362306a36Sopenharmony_ci widgets = devm_kcalloc(dapm->dev, hdmi->num_ports, 182462306a36Sopenharmony_ci sizeof(*widgets), GFP_KERNEL); 182562306a36Sopenharmony_ci 182662306a36Sopenharmony_ci if (!widgets) 182762306a36Sopenharmony_ci return -ENOMEM; 182862306a36Sopenharmony_ci 182962306a36Sopenharmony_ci route = devm_kcalloc(dapm->dev, hdmi->num_ports, 183062306a36Sopenharmony_ci sizeof(*route), GFP_KERNEL); 183162306a36Sopenharmony_ci if (!route) 183262306a36Sopenharmony_ci return -ENOMEM; 183362306a36Sopenharmony_ci 183462306a36Sopenharmony_ci /* create Jack DAPM widget */ 183562306a36Sopenharmony_ci list_for_each_entry(pin, &hdmi->pin_list, head) { 183662306a36Sopenharmony_ci for (j = 0; j < pin->num_ports; j++) { 183762306a36Sopenharmony_ci snprintf(w_name, sizeof(w_name), "hif%d-%d Jack", 183862306a36Sopenharmony_ci pin->nid, pin->ports[j].id); 183962306a36Sopenharmony_ci 184062306a36Sopenharmony_ci ret = hdac_hdmi_fill_widget_info(dapm->dev, &widgets[i], 184162306a36Sopenharmony_ci snd_soc_dapm_spk, NULL, 184262306a36Sopenharmony_ci w_name, NULL, NULL, 0, NULL, 0); 184362306a36Sopenharmony_ci if (ret < 0) 184462306a36Sopenharmony_ci return ret; 184562306a36Sopenharmony_ci 184662306a36Sopenharmony_ci pin->ports[j].jack_pin = widgets[i].name; 184762306a36Sopenharmony_ci pin->ports[j].dapm = dapm; 184862306a36Sopenharmony_ci 184962306a36Sopenharmony_ci /* add to route from Jack widget to output */ 185062306a36Sopenharmony_ci hdac_hdmi_fill_route(&route[i], pin->ports[j].jack_pin, 185162306a36Sopenharmony_ci NULL, pin->ports[j].output_pin, NULL); 185262306a36Sopenharmony_ci 185362306a36Sopenharmony_ci i++; 185462306a36Sopenharmony_ci } 185562306a36Sopenharmony_ci } 185662306a36Sopenharmony_ci 185762306a36Sopenharmony_ci /* Add Route from Jack widget to the output widget */ 185862306a36Sopenharmony_ci ret = snd_soc_dapm_new_controls(dapm, widgets, hdmi->num_ports); 185962306a36Sopenharmony_ci if (ret < 0) 186062306a36Sopenharmony_ci return ret; 186162306a36Sopenharmony_ci 186262306a36Sopenharmony_ci ret = snd_soc_dapm_add_routes(dapm, route, hdmi->num_ports); 186362306a36Sopenharmony_ci if (ret < 0) 186462306a36Sopenharmony_ci return ret; 186562306a36Sopenharmony_ci 186662306a36Sopenharmony_ci ret = snd_soc_dapm_new_widgets(dapm->card); 186762306a36Sopenharmony_ci if (ret < 0) 186862306a36Sopenharmony_ci return ret; 186962306a36Sopenharmony_ci 187062306a36Sopenharmony_ci /* Add Jack Pin switch Kcontrol */ 187162306a36Sopenharmony_ci ret = create_fill_jack_kcontrols(dapm->card, hdev); 187262306a36Sopenharmony_ci 187362306a36Sopenharmony_ci if (ret < 0) 187462306a36Sopenharmony_ci return ret; 187562306a36Sopenharmony_ci 187662306a36Sopenharmony_ci /* default set the Jack Pin switch to OFF */ 187762306a36Sopenharmony_ci list_for_each_entry(pin, &hdmi->pin_list, head) { 187862306a36Sopenharmony_ci for (j = 0; j < pin->num_ports; j++) 187962306a36Sopenharmony_ci snd_soc_dapm_disable_pin(pin->ports[j].dapm, 188062306a36Sopenharmony_ci pin->ports[j].jack_pin); 188162306a36Sopenharmony_ci } 188262306a36Sopenharmony_ci 188362306a36Sopenharmony_ci return 0; 188462306a36Sopenharmony_ci} 188562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(hdac_hdmi_jack_port_init); 188662306a36Sopenharmony_ci 188762306a36Sopenharmony_ciint hdac_hdmi_jack_init(struct snd_soc_dai *dai, int device, 188862306a36Sopenharmony_ci struct snd_soc_jack *jack) 188962306a36Sopenharmony_ci{ 189062306a36Sopenharmony_ci struct snd_soc_component *component = dai->component; 189162306a36Sopenharmony_ci struct hdac_hdmi_priv *hdmi = snd_soc_component_get_drvdata(component); 189262306a36Sopenharmony_ci struct hdac_device *hdev = hdmi->hdev; 189362306a36Sopenharmony_ci struct hdac_hdmi_pcm *pcm; 189462306a36Sopenharmony_ci struct snd_pcm *snd_pcm; 189562306a36Sopenharmony_ci int err; 189662306a36Sopenharmony_ci 189762306a36Sopenharmony_ci /* 189862306a36Sopenharmony_ci * this is a new PCM device, create new pcm and 189962306a36Sopenharmony_ci * add to the pcm list 190062306a36Sopenharmony_ci */ 190162306a36Sopenharmony_ci pcm = devm_kzalloc(&hdev->dev, sizeof(*pcm), GFP_KERNEL); 190262306a36Sopenharmony_ci if (!pcm) 190362306a36Sopenharmony_ci return -ENOMEM; 190462306a36Sopenharmony_ci pcm->pcm_id = device; 190562306a36Sopenharmony_ci pcm->cvt = hdmi->dai_map[dai->id].cvt; 190662306a36Sopenharmony_ci pcm->jack_event = 0; 190762306a36Sopenharmony_ci pcm->jack = jack; 190862306a36Sopenharmony_ci mutex_init(&pcm->lock); 190962306a36Sopenharmony_ci INIT_LIST_HEAD(&pcm->port_list); 191062306a36Sopenharmony_ci snd_pcm = hdac_hdmi_get_pcm_from_id(dai->component->card, device); 191162306a36Sopenharmony_ci if (snd_pcm) { 191262306a36Sopenharmony_ci err = snd_hdac_add_chmap_ctls(snd_pcm, device, &hdmi->chmap); 191362306a36Sopenharmony_ci if (err < 0) { 191462306a36Sopenharmony_ci dev_err(&hdev->dev, 191562306a36Sopenharmony_ci "chmap control add failed with err: %d for pcm: %d\n", 191662306a36Sopenharmony_ci err, device); 191762306a36Sopenharmony_ci return err; 191862306a36Sopenharmony_ci } 191962306a36Sopenharmony_ci } 192062306a36Sopenharmony_ci 192162306a36Sopenharmony_ci /* add control for ELD Bytes */ 192262306a36Sopenharmony_ci err = hdac_hdmi_create_eld_ctl(component, pcm); 192362306a36Sopenharmony_ci if (err < 0) { 192462306a36Sopenharmony_ci dev_err(&hdev->dev, 192562306a36Sopenharmony_ci "eld control add failed with err: %d for pcm: %d\n", 192662306a36Sopenharmony_ci err, device); 192762306a36Sopenharmony_ci return err; 192862306a36Sopenharmony_ci } 192962306a36Sopenharmony_ci 193062306a36Sopenharmony_ci list_add_tail(&pcm->head, &hdmi->pcm_list); 193162306a36Sopenharmony_ci 193262306a36Sopenharmony_ci return 0; 193362306a36Sopenharmony_ci} 193462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(hdac_hdmi_jack_init); 193562306a36Sopenharmony_ci 193662306a36Sopenharmony_cistatic void hdac_hdmi_present_sense_all_pins(struct hdac_device *hdev, 193762306a36Sopenharmony_ci struct hdac_hdmi_priv *hdmi, bool detect_pin_caps) 193862306a36Sopenharmony_ci{ 193962306a36Sopenharmony_ci int i; 194062306a36Sopenharmony_ci struct hdac_hdmi_pin *pin; 194162306a36Sopenharmony_ci 194262306a36Sopenharmony_ci list_for_each_entry(pin, &hdmi->pin_list, head) { 194362306a36Sopenharmony_ci if (detect_pin_caps) { 194462306a36Sopenharmony_ci 194562306a36Sopenharmony_ci if (hdac_hdmi_get_port_len(hdev, pin->nid) == 0) 194662306a36Sopenharmony_ci pin->mst_capable = false; 194762306a36Sopenharmony_ci else 194862306a36Sopenharmony_ci pin->mst_capable = true; 194962306a36Sopenharmony_ci } 195062306a36Sopenharmony_ci 195162306a36Sopenharmony_ci for (i = 0; i < pin->num_ports; i++) { 195262306a36Sopenharmony_ci if (!pin->mst_capable && i > 0) 195362306a36Sopenharmony_ci continue; 195462306a36Sopenharmony_ci 195562306a36Sopenharmony_ci hdac_hdmi_present_sense(pin, &pin->ports[i]); 195662306a36Sopenharmony_ci } 195762306a36Sopenharmony_ci } 195862306a36Sopenharmony_ci} 195962306a36Sopenharmony_ci 196062306a36Sopenharmony_cistatic int hdmi_codec_probe(struct snd_soc_component *component) 196162306a36Sopenharmony_ci{ 196262306a36Sopenharmony_ci struct hdac_hdmi_priv *hdmi = snd_soc_component_get_drvdata(component); 196362306a36Sopenharmony_ci struct hdac_device *hdev = hdmi->hdev; 196462306a36Sopenharmony_ci struct snd_soc_dapm_context *dapm = 196562306a36Sopenharmony_ci snd_soc_component_get_dapm(component); 196662306a36Sopenharmony_ci struct hdac_ext_link *hlink; 196762306a36Sopenharmony_ci int ret; 196862306a36Sopenharmony_ci 196962306a36Sopenharmony_ci hdmi->component = component; 197062306a36Sopenharmony_ci 197162306a36Sopenharmony_ci /* 197262306a36Sopenharmony_ci * hold the ref while we probe, also no need to drop the ref on 197362306a36Sopenharmony_ci * exit, we call pm_runtime_suspend() so that will do for us 197462306a36Sopenharmony_ci */ 197562306a36Sopenharmony_ci hlink = snd_hdac_ext_bus_get_hlink_by_name(hdev->bus, dev_name(&hdev->dev)); 197662306a36Sopenharmony_ci if (!hlink) { 197762306a36Sopenharmony_ci dev_err(&hdev->dev, "hdac link not found\n"); 197862306a36Sopenharmony_ci return -EIO; 197962306a36Sopenharmony_ci } 198062306a36Sopenharmony_ci 198162306a36Sopenharmony_ci snd_hdac_ext_bus_link_get(hdev->bus, hlink); 198262306a36Sopenharmony_ci 198362306a36Sopenharmony_ci ret = create_fill_widget_route_map(dapm); 198462306a36Sopenharmony_ci if (ret < 0) 198562306a36Sopenharmony_ci return ret; 198662306a36Sopenharmony_ci 198762306a36Sopenharmony_ci aops.audio_ptr = hdev; 198862306a36Sopenharmony_ci ret = snd_hdac_acomp_register_notifier(hdev->bus, &aops); 198962306a36Sopenharmony_ci if (ret < 0) { 199062306a36Sopenharmony_ci dev_err(&hdev->dev, "notifier register failed: err: %d\n", ret); 199162306a36Sopenharmony_ci return ret; 199262306a36Sopenharmony_ci } 199362306a36Sopenharmony_ci 199462306a36Sopenharmony_ci hdac_hdmi_present_sense_all_pins(hdev, hdmi, true); 199562306a36Sopenharmony_ci /* Imp: Store the card pointer in hda_codec */ 199662306a36Sopenharmony_ci hdmi->card = dapm->card->snd_card; 199762306a36Sopenharmony_ci 199862306a36Sopenharmony_ci /* 199962306a36Sopenharmony_ci * Setup a device_link between card device and HDMI codec device. 200062306a36Sopenharmony_ci * The card device is the consumer and the HDMI codec device is 200162306a36Sopenharmony_ci * the supplier. With this setting, we can make sure that the audio 200262306a36Sopenharmony_ci * domain in display power will be always turned on before operating 200362306a36Sopenharmony_ci * on the HDMI audio codec registers. 200462306a36Sopenharmony_ci * Let's use the flag DL_FLAG_AUTOREMOVE_CONSUMER. This can make 200562306a36Sopenharmony_ci * sure the device link is freed when the machine driver is removed. 200662306a36Sopenharmony_ci */ 200762306a36Sopenharmony_ci device_link_add(component->card->dev, &hdev->dev, DL_FLAG_RPM_ACTIVE | 200862306a36Sopenharmony_ci DL_FLAG_AUTOREMOVE_CONSUMER); 200962306a36Sopenharmony_ci /* 201062306a36Sopenharmony_ci * hdac_device core already sets the state to active and calls 201162306a36Sopenharmony_ci * get_noresume. So enable runtime and set the device to suspend. 201262306a36Sopenharmony_ci */ 201362306a36Sopenharmony_ci pm_runtime_enable(&hdev->dev); 201462306a36Sopenharmony_ci pm_runtime_put(&hdev->dev); 201562306a36Sopenharmony_ci pm_runtime_suspend(&hdev->dev); 201662306a36Sopenharmony_ci 201762306a36Sopenharmony_ci return 0; 201862306a36Sopenharmony_ci} 201962306a36Sopenharmony_ci 202062306a36Sopenharmony_cistatic void hdmi_codec_remove(struct snd_soc_component *component) 202162306a36Sopenharmony_ci{ 202262306a36Sopenharmony_ci struct hdac_hdmi_priv *hdmi = snd_soc_component_get_drvdata(component); 202362306a36Sopenharmony_ci struct hdac_device *hdev = hdmi->hdev; 202462306a36Sopenharmony_ci int ret; 202562306a36Sopenharmony_ci 202662306a36Sopenharmony_ci ret = snd_hdac_acomp_register_notifier(hdev->bus, NULL); 202762306a36Sopenharmony_ci if (ret < 0) 202862306a36Sopenharmony_ci dev_err(&hdev->dev, "notifier unregister failed: err: %d\n", 202962306a36Sopenharmony_ci ret); 203062306a36Sopenharmony_ci 203162306a36Sopenharmony_ci pm_runtime_disable(&hdev->dev); 203262306a36Sopenharmony_ci} 203362306a36Sopenharmony_ci 203462306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 203562306a36Sopenharmony_cistatic int hdmi_codec_resume(struct device *dev) 203662306a36Sopenharmony_ci{ 203762306a36Sopenharmony_ci struct hdac_device *hdev = dev_to_hdac_dev(dev); 203862306a36Sopenharmony_ci struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); 203962306a36Sopenharmony_ci int ret; 204062306a36Sopenharmony_ci 204162306a36Sopenharmony_ci ret = pm_runtime_force_resume(dev); 204262306a36Sopenharmony_ci if (ret < 0) 204362306a36Sopenharmony_ci return ret; 204462306a36Sopenharmony_ci /* 204562306a36Sopenharmony_ci * As the ELD notify callback request is not entertained while the 204662306a36Sopenharmony_ci * device is in suspend state. Need to manually check detection of 204762306a36Sopenharmony_ci * all pins here. pin capablity change is not support, so use the 204862306a36Sopenharmony_ci * already set pin caps. 204962306a36Sopenharmony_ci * 205062306a36Sopenharmony_ci * NOTE: this is safe to call even if the codec doesn't actually resume. 205162306a36Sopenharmony_ci * The pin check involves only with DRM audio component hooks, so it 205262306a36Sopenharmony_ci * works even if the HD-audio side is still dreaming peacefully. 205362306a36Sopenharmony_ci */ 205462306a36Sopenharmony_ci hdac_hdmi_present_sense_all_pins(hdev, hdmi, false); 205562306a36Sopenharmony_ci return 0; 205662306a36Sopenharmony_ci} 205762306a36Sopenharmony_ci#else 205862306a36Sopenharmony_ci#define hdmi_codec_resume NULL 205962306a36Sopenharmony_ci#endif 206062306a36Sopenharmony_ci 206162306a36Sopenharmony_cistatic const struct snd_soc_component_driver hdmi_hda_codec = { 206262306a36Sopenharmony_ci .probe = hdmi_codec_probe, 206362306a36Sopenharmony_ci .remove = hdmi_codec_remove, 206462306a36Sopenharmony_ci .use_pmdown_time = 1, 206562306a36Sopenharmony_ci .endianness = 1, 206662306a36Sopenharmony_ci}; 206762306a36Sopenharmony_ci 206862306a36Sopenharmony_cistatic void hdac_hdmi_get_chmap(struct hdac_device *hdev, int pcm_idx, 206962306a36Sopenharmony_ci unsigned char *chmap) 207062306a36Sopenharmony_ci{ 207162306a36Sopenharmony_ci struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); 207262306a36Sopenharmony_ci struct hdac_hdmi_pcm *pcm = get_hdmi_pcm_from_id(hdmi, pcm_idx); 207362306a36Sopenharmony_ci 207462306a36Sopenharmony_ci memcpy(chmap, pcm->chmap, ARRAY_SIZE(pcm->chmap)); 207562306a36Sopenharmony_ci} 207662306a36Sopenharmony_ci 207762306a36Sopenharmony_cistatic void hdac_hdmi_set_chmap(struct hdac_device *hdev, int pcm_idx, 207862306a36Sopenharmony_ci unsigned char *chmap, int prepared) 207962306a36Sopenharmony_ci{ 208062306a36Sopenharmony_ci struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); 208162306a36Sopenharmony_ci struct hdac_hdmi_pcm *pcm = get_hdmi_pcm_from_id(hdmi, pcm_idx); 208262306a36Sopenharmony_ci struct hdac_hdmi_port *port; 208362306a36Sopenharmony_ci 208462306a36Sopenharmony_ci if (!pcm) 208562306a36Sopenharmony_ci return; 208662306a36Sopenharmony_ci 208762306a36Sopenharmony_ci if (list_empty(&pcm->port_list)) 208862306a36Sopenharmony_ci return; 208962306a36Sopenharmony_ci 209062306a36Sopenharmony_ci mutex_lock(&pcm->lock); 209162306a36Sopenharmony_ci pcm->chmap_set = true; 209262306a36Sopenharmony_ci memcpy(pcm->chmap, chmap, ARRAY_SIZE(pcm->chmap)); 209362306a36Sopenharmony_ci list_for_each_entry(port, &pcm->port_list, head) 209462306a36Sopenharmony_ci if (prepared) 209562306a36Sopenharmony_ci hdac_hdmi_setup_audio_infoframe(hdev, pcm, port); 209662306a36Sopenharmony_ci mutex_unlock(&pcm->lock); 209762306a36Sopenharmony_ci} 209862306a36Sopenharmony_ci 209962306a36Sopenharmony_cistatic bool is_hdac_hdmi_pcm_attached(struct hdac_device *hdev, int pcm_idx) 210062306a36Sopenharmony_ci{ 210162306a36Sopenharmony_ci struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); 210262306a36Sopenharmony_ci struct hdac_hdmi_pcm *pcm = get_hdmi_pcm_from_id(hdmi, pcm_idx); 210362306a36Sopenharmony_ci 210462306a36Sopenharmony_ci if (!pcm) 210562306a36Sopenharmony_ci return false; 210662306a36Sopenharmony_ci 210762306a36Sopenharmony_ci if (list_empty(&pcm->port_list)) 210862306a36Sopenharmony_ci return false; 210962306a36Sopenharmony_ci 211062306a36Sopenharmony_ci return true; 211162306a36Sopenharmony_ci} 211262306a36Sopenharmony_ci 211362306a36Sopenharmony_cistatic int hdac_hdmi_get_spk_alloc(struct hdac_device *hdev, int pcm_idx) 211462306a36Sopenharmony_ci{ 211562306a36Sopenharmony_ci struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); 211662306a36Sopenharmony_ci struct hdac_hdmi_pcm *pcm = get_hdmi_pcm_from_id(hdmi, pcm_idx); 211762306a36Sopenharmony_ci struct hdac_hdmi_port *port; 211862306a36Sopenharmony_ci 211962306a36Sopenharmony_ci if (!pcm) 212062306a36Sopenharmony_ci return 0; 212162306a36Sopenharmony_ci 212262306a36Sopenharmony_ci if (list_empty(&pcm->port_list)) 212362306a36Sopenharmony_ci return 0; 212462306a36Sopenharmony_ci 212562306a36Sopenharmony_ci port = list_first_entry(&pcm->port_list, struct hdac_hdmi_port, head); 212662306a36Sopenharmony_ci 212762306a36Sopenharmony_ci if (!port || !port->eld.eld_valid) 212862306a36Sopenharmony_ci return 0; 212962306a36Sopenharmony_ci 213062306a36Sopenharmony_ci return port->eld.info.spk_alloc; 213162306a36Sopenharmony_ci} 213262306a36Sopenharmony_ci 213362306a36Sopenharmony_cistatic struct hdac_hdmi_drv_data intel_glk_drv_data = { 213462306a36Sopenharmony_ci .vendor_nid = INTEL_GLK_VENDOR_NID, 213562306a36Sopenharmony_ci}; 213662306a36Sopenharmony_ci 213762306a36Sopenharmony_cistatic struct hdac_hdmi_drv_data intel_drv_data = { 213862306a36Sopenharmony_ci .vendor_nid = INTEL_VENDOR_NID, 213962306a36Sopenharmony_ci}; 214062306a36Sopenharmony_ci 214162306a36Sopenharmony_cistatic int hdac_hdmi_dev_probe(struct hdac_device *hdev) 214262306a36Sopenharmony_ci{ 214362306a36Sopenharmony_ci struct hdac_hdmi_priv *hdmi_priv; 214462306a36Sopenharmony_ci struct snd_soc_dai_driver *hdmi_dais = NULL; 214562306a36Sopenharmony_ci struct hdac_ext_link *hlink; 214662306a36Sopenharmony_ci int num_dais = 0; 214762306a36Sopenharmony_ci int ret; 214862306a36Sopenharmony_ci struct hdac_driver *hdrv = drv_to_hdac_driver(hdev->dev.driver); 214962306a36Sopenharmony_ci const struct hda_device_id *hdac_id = hdac_get_device_id(hdev, hdrv); 215062306a36Sopenharmony_ci 215162306a36Sopenharmony_ci /* hold the ref while we probe */ 215262306a36Sopenharmony_ci hlink = snd_hdac_ext_bus_get_hlink_by_name(hdev->bus, dev_name(&hdev->dev)); 215362306a36Sopenharmony_ci if (!hlink) { 215462306a36Sopenharmony_ci dev_err(&hdev->dev, "hdac link not found\n"); 215562306a36Sopenharmony_ci return -EIO; 215662306a36Sopenharmony_ci } 215762306a36Sopenharmony_ci 215862306a36Sopenharmony_ci snd_hdac_ext_bus_link_get(hdev->bus, hlink); 215962306a36Sopenharmony_ci 216062306a36Sopenharmony_ci hdmi_priv = devm_kzalloc(&hdev->dev, sizeof(*hdmi_priv), GFP_KERNEL); 216162306a36Sopenharmony_ci if (hdmi_priv == NULL) 216262306a36Sopenharmony_ci return -ENOMEM; 216362306a36Sopenharmony_ci 216462306a36Sopenharmony_ci snd_hdac_register_chmap_ops(hdev, &hdmi_priv->chmap); 216562306a36Sopenharmony_ci hdmi_priv->chmap.ops.get_chmap = hdac_hdmi_get_chmap; 216662306a36Sopenharmony_ci hdmi_priv->chmap.ops.set_chmap = hdac_hdmi_set_chmap; 216762306a36Sopenharmony_ci hdmi_priv->chmap.ops.is_pcm_attached = is_hdac_hdmi_pcm_attached; 216862306a36Sopenharmony_ci hdmi_priv->chmap.ops.get_spk_alloc = hdac_hdmi_get_spk_alloc; 216962306a36Sopenharmony_ci hdmi_priv->hdev = hdev; 217062306a36Sopenharmony_ci 217162306a36Sopenharmony_ci if (!hdac_id) 217262306a36Sopenharmony_ci return -ENODEV; 217362306a36Sopenharmony_ci 217462306a36Sopenharmony_ci if (hdac_id->driver_data) 217562306a36Sopenharmony_ci hdmi_priv->drv_data = 217662306a36Sopenharmony_ci (struct hdac_hdmi_drv_data *)hdac_id->driver_data; 217762306a36Sopenharmony_ci else 217862306a36Sopenharmony_ci hdmi_priv->drv_data = &intel_drv_data; 217962306a36Sopenharmony_ci 218062306a36Sopenharmony_ci dev_set_drvdata(&hdev->dev, hdmi_priv); 218162306a36Sopenharmony_ci 218262306a36Sopenharmony_ci INIT_LIST_HEAD(&hdmi_priv->pin_list); 218362306a36Sopenharmony_ci INIT_LIST_HEAD(&hdmi_priv->cvt_list); 218462306a36Sopenharmony_ci INIT_LIST_HEAD(&hdmi_priv->pcm_list); 218562306a36Sopenharmony_ci mutex_init(&hdmi_priv->pin_mutex); 218662306a36Sopenharmony_ci 218762306a36Sopenharmony_ci /* 218862306a36Sopenharmony_ci * Turned off in the runtime_suspend during the first explicit 218962306a36Sopenharmony_ci * pm_runtime_suspend call. 219062306a36Sopenharmony_ci */ 219162306a36Sopenharmony_ci snd_hdac_display_power(hdev->bus, hdev->addr, true); 219262306a36Sopenharmony_ci 219362306a36Sopenharmony_ci ret = hdac_hdmi_parse_and_map_nid(hdev, &hdmi_dais, &num_dais); 219462306a36Sopenharmony_ci if (ret < 0) { 219562306a36Sopenharmony_ci dev_err(&hdev->dev, 219662306a36Sopenharmony_ci "Failed in parse and map nid with err: %d\n", ret); 219762306a36Sopenharmony_ci return ret; 219862306a36Sopenharmony_ci } 219962306a36Sopenharmony_ci snd_hdac_refresh_widgets(hdev); 220062306a36Sopenharmony_ci 220162306a36Sopenharmony_ci /* ASoC specific initialization */ 220262306a36Sopenharmony_ci ret = devm_snd_soc_register_component(&hdev->dev, &hdmi_hda_codec, 220362306a36Sopenharmony_ci hdmi_dais, num_dais); 220462306a36Sopenharmony_ci 220562306a36Sopenharmony_ci snd_hdac_ext_bus_link_put(hdev->bus, hlink); 220662306a36Sopenharmony_ci 220762306a36Sopenharmony_ci return ret; 220862306a36Sopenharmony_ci} 220962306a36Sopenharmony_ci 221062306a36Sopenharmony_cistatic void clear_dapm_works(struct hdac_device *hdev) 221162306a36Sopenharmony_ci{ 221262306a36Sopenharmony_ci struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); 221362306a36Sopenharmony_ci struct hdac_hdmi_pin *pin; 221462306a36Sopenharmony_ci int i; 221562306a36Sopenharmony_ci 221662306a36Sopenharmony_ci list_for_each_entry(pin, &hdmi->pin_list, head) 221762306a36Sopenharmony_ci for (i = 0; i < pin->num_ports; i++) 221862306a36Sopenharmony_ci cancel_work_sync(&pin->ports[i].dapm_work); 221962306a36Sopenharmony_ci} 222062306a36Sopenharmony_ci 222162306a36Sopenharmony_cistatic int hdac_hdmi_dev_remove(struct hdac_device *hdev) 222262306a36Sopenharmony_ci{ 222362306a36Sopenharmony_ci clear_dapm_works(hdev); 222462306a36Sopenharmony_ci snd_hdac_display_power(hdev->bus, hdev->addr, false); 222562306a36Sopenharmony_ci 222662306a36Sopenharmony_ci return 0; 222762306a36Sopenharmony_ci} 222862306a36Sopenharmony_ci 222962306a36Sopenharmony_ci#ifdef CONFIG_PM 223062306a36Sopenharmony_cistatic int hdac_hdmi_runtime_suspend(struct device *dev) 223162306a36Sopenharmony_ci{ 223262306a36Sopenharmony_ci struct hdac_device *hdev = dev_to_hdac_dev(dev); 223362306a36Sopenharmony_ci struct hdac_bus *bus = hdev->bus; 223462306a36Sopenharmony_ci struct hdac_ext_link *hlink; 223562306a36Sopenharmony_ci 223662306a36Sopenharmony_ci dev_dbg(dev, "Enter: %s\n", __func__); 223762306a36Sopenharmony_ci 223862306a36Sopenharmony_ci /* controller may not have been initialized for the first time */ 223962306a36Sopenharmony_ci if (!bus) 224062306a36Sopenharmony_ci return 0; 224162306a36Sopenharmony_ci 224262306a36Sopenharmony_ci /* 224362306a36Sopenharmony_ci * Power down afg. 224462306a36Sopenharmony_ci * codec_read is preferred over codec_write to set the power state. 224562306a36Sopenharmony_ci * This way verb is send to set the power state and response 224662306a36Sopenharmony_ci * is received. So setting power state is ensured without using loop 224762306a36Sopenharmony_ci * to read the state. 224862306a36Sopenharmony_ci */ 224962306a36Sopenharmony_ci snd_hdac_codec_read(hdev, hdev->afg, 0, AC_VERB_SET_POWER_STATE, 225062306a36Sopenharmony_ci AC_PWRST_D3); 225162306a36Sopenharmony_ci 225262306a36Sopenharmony_ci hlink = snd_hdac_ext_bus_get_hlink_by_name(bus, dev_name(dev)); 225362306a36Sopenharmony_ci if (!hlink) { 225462306a36Sopenharmony_ci dev_err(dev, "hdac link not found\n"); 225562306a36Sopenharmony_ci return -EIO; 225662306a36Sopenharmony_ci } 225762306a36Sopenharmony_ci 225862306a36Sopenharmony_ci snd_hdac_codec_link_down(hdev); 225962306a36Sopenharmony_ci snd_hdac_ext_bus_link_put(bus, hlink); 226062306a36Sopenharmony_ci 226162306a36Sopenharmony_ci snd_hdac_display_power(bus, hdev->addr, false); 226262306a36Sopenharmony_ci 226362306a36Sopenharmony_ci return 0; 226462306a36Sopenharmony_ci} 226562306a36Sopenharmony_ci 226662306a36Sopenharmony_cistatic int hdac_hdmi_runtime_resume(struct device *dev) 226762306a36Sopenharmony_ci{ 226862306a36Sopenharmony_ci struct hdac_device *hdev = dev_to_hdac_dev(dev); 226962306a36Sopenharmony_ci struct hdac_bus *bus = hdev->bus; 227062306a36Sopenharmony_ci struct hdac_ext_link *hlink; 227162306a36Sopenharmony_ci 227262306a36Sopenharmony_ci dev_dbg(dev, "Enter: %s\n", __func__); 227362306a36Sopenharmony_ci 227462306a36Sopenharmony_ci /* controller may not have been initialized for the first time */ 227562306a36Sopenharmony_ci if (!bus) 227662306a36Sopenharmony_ci return 0; 227762306a36Sopenharmony_ci 227862306a36Sopenharmony_ci hlink = snd_hdac_ext_bus_get_hlink_by_name(bus, dev_name(dev)); 227962306a36Sopenharmony_ci if (!hlink) { 228062306a36Sopenharmony_ci dev_err(dev, "hdac link not found\n"); 228162306a36Sopenharmony_ci return -EIO; 228262306a36Sopenharmony_ci } 228362306a36Sopenharmony_ci 228462306a36Sopenharmony_ci snd_hdac_ext_bus_link_get(bus, hlink); 228562306a36Sopenharmony_ci snd_hdac_codec_link_up(hdev); 228662306a36Sopenharmony_ci 228762306a36Sopenharmony_ci snd_hdac_display_power(bus, hdev->addr, true); 228862306a36Sopenharmony_ci 228962306a36Sopenharmony_ci hdac_hdmi_skl_enable_all_pins(hdev); 229062306a36Sopenharmony_ci hdac_hdmi_skl_enable_dp12(hdev); 229162306a36Sopenharmony_ci 229262306a36Sopenharmony_ci /* Power up afg */ 229362306a36Sopenharmony_ci snd_hdac_codec_read(hdev, hdev->afg, 0, AC_VERB_SET_POWER_STATE, 229462306a36Sopenharmony_ci AC_PWRST_D0); 229562306a36Sopenharmony_ci 229662306a36Sopenharmony_ci return 0; 229762306a36Sopenharmony_ci} 229862306a36Sopenharmony_ci#else 229962306a36Sopenharmony_ci#define hdac_hdmi_runtime_suspend NULL 230062306a36Sopenharmony_ci#define hdac_hdmi_runtime_resume NULL 230162306a36Sopenharmony_ci#endif 230262306a36Sopenharmony_ci 230362306a36Sopenharmony_cistatic const struct dev_pm_ops hdac_hdmi_pm = { 230462306a36Sopenharmony_ci SET_RUNTIME_PM_OPS(hdac_hdmi_runtime_suspend, hdac_hdmi_runtime_resume, NULL) 230562306a36Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, hdmi_codec_resume) 230662306a36Sopenharmony_ci}; 230762306a36Sopenharmony_ci 230862306a36Sopenharmony_cistatic const struct hda_device_id hdmi_list[] = { 230962306a36Sopenharmony_ci HDA_CODEC_EXT_ENTRY(0x80862809, 0x100000, "Skylake HDMI", 0), 231062306a36Sopenharmony_ci HDA_CODEC_EXT_ENTRY(0x8086280a, 0x100000, "Broxton HDMI", 0), 231162306a36Sopenharmony_ci HDA_CODEC_EXT_ENTRY(0x8086280b, 0x100000, "Kabylake HDMI", 0), 231262306a36Sopenharmony_ci HDA_CODEC_EXT_ENTRY(0x8086280c, 0x100000, "Cannonlake HDMI", 231362306a36Sopenharmony_ci &intel_glk_drv_data), 231462306a36Sopenharmony_ci HDA_CODEC_EXT_ENTRY(0x8086280d, 0x100000, "Geminilake HDMI", 231562306a36Sopenharmony_ci &intel_glk_drv_data), 231662306a36Sopenharmony_ci {} 231762306a36Sopenharmony_ci}; 231862306a36Sopenharmony_ci 231962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(hdaudio, hdmi_list); 232062306a36Sopenharmony_ci 232162306a36Sopenharmony_cistatic struct hdac_driver hdmi_driver = { 232262306a36Sopenharmony_ci .driver = { 232362306a36Sopenharmony_ci .name = "HDMI HDA Codec", 232462306a36Sopenharmony_ci .pm = &hdac_hdmi_pm, 232562306a36Sopenharmony_ci }, 232662306a36Sopenharmony_ci .id_table = hdmi_list, 232762306a36Sopenharmony_ci .probe = hdac_hdmi_dev_probe, 232862306a36Sopenharmony_ci .remove = hdac_hdmi_dev_remove, 232962306a36Sopenharmony_ci}; 233062306a36Sopenharmony_ci 233162306a36Sopenharmony_cistatic int __init hdmi_init(void) 233262306a36Sopenharmony_ci{ 233362306a36Sopenharmony_ci return snd_hda_ext_driver_register(&hdmi_driver); 233462306a36Sopenharmony_ci} 233562306a36Sopenharmony_ci 233662306a36Sopenharmony_cistatic void __exit hdmi_exit(void) 233762306a36Sopenharmony_ci{ 233862306a36Sopenharmony_ci snd_hda_ext_driver_unregister(&hdmi_driver); 233962306a36Sopenharmony_ci} 234062306a36Sopenharmony_ci 234162306a36Sopenharmony_cimodule_init(hdmi_init); 234262306a36Sopenharmony_cimodule_exit(hdmi_exit); 234362306a36Sopenharmony_ci 234462306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 234562306a36Sopenharmony_ciMODULE_DESCRIPTION("HDMI HD codec"); 234662306a36Sopenharmony_ciMODULE_AUTHOR("Samreen Nilofer<samreen.nilofer@intel.com>"); 234762306a36Sopenharmony_ciMODULE_AUTHOR("Subhransu S. Prusty<subhransu.s.prusty@intel.com>"); 2348