diff options
Diffstat (limited to 'sound/soc/codecs/hdac_hdmi.c')
| -rw-r--r-- | sound/soc/codecs/hdac_hdmi.c | 1094 |
1 files changed, 489 insertions, 605 deletions
diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c index bc2e74ff3b2d..2652fcf2a3a3 100644 --- a/sound/soc/codecs/hdac_hdmi.c +++ b/sound/soc/codecs/hdac_hdmi.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * hdac_hdmi.c - ASoc HDA-HDMI codec driver for Intel platforms * @@ -6,23 +7,16 @@ * Subhransu S. Prusty <subhransu.s.prusty@intel.com> * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + #include <linux/init.h> #include <linux/delay.h> #include <linux/module.h> #include <linux/pm_runtime.h> #include <linux/hdmi.h> #include <drm/drm_edid.h> +#include <drm/drm_eld.h> #include <sound/pcm_params.h> #include <sound/jack.h> #include <sound/soc.h> @@ -30,8 +24,6 @@ #include <sound/hda_i915.h> #include <sound/pcm_drm_eld.h> #include <sound/hda_chmap.h> -#include "../../hda/local.h" -#include "hdac_hdmi.h" #define NAME_SIZE 32 @@ -85,7 +77,7 @@ struct hdac_hdmi_pin { bool mst_capable; struct hdac_hdmi_port *ports; int num_ports; - struct hdac_ext_device *edev; + struct hdac_device *hdev; }; struct hdac_hdmi_port { @@ -96,8 +88,10 @@ struct hdac_hdmi_port { hda_nid_t mux_nids[HDA_MAX_CONNECTIONS]; struct hdac_hdmi_eld eld; const char *jack_pin; + bool is_connect; struct snd_soc_dapm_context *dapm; const char *output_pin; + struct work_struct dapm_work; }; struct hdac_hdmi_pcm { @@ -113,6 +107,7 @@ struct hdac_hdmi_pcm { unsigned char chmap[8]; /* ALSA API channel-map */ struct mutex lock; int jack_event; + struct snd_kcontrol *eld_ctl; }; struct hdac_hdmi_dai_port_map { @@ -121,7 +116,14 @@ struct hdac_hdmi_dai_port_map { struct hdac_hdmi_cvt *cvt; }; +struct hdac_hdmi_drv_data { + unsigned int vendor_nid; +}; + struct hdac_hdmi_priv { + struct hdac_device *hdev; + struct snd_soc_component *component; + struct snd_card *card; struct hdac_hdmi_dai_port_map dai_map[HDA_MAX_CVTS]; struct list_head pin_list; struct list_head cvt_list; @@ -131,32 +133,32 @@ struct hdac_hdmi_priv { int num_ports; struct mutex pin_mutex; struct hdac_chmap chmap; + struct hdac_hdmi_drv_data *drv_data; + struct snd_soc_dai_driver *dai_drv; }; +#define hdev_to_hdmi_priv(_hdev) dev_get_drvdata(&(_hdev)->dev) + static struct hdac_hdmi_pcm * hdac_hdmi_get_pcm_from_cvt(struct hdac_hdmi_priv *hdmi, struct hdac_hdmi_cvt *cvt) { - struct hdac_hdmi_pcm *pcm = NULL; + struct hdac_hdmi_pcm *pcm; list_for_each_entry(pcm, &hdmi->pcm_list, head) { if (pcm->cvt == cvt) - break; + return pcm; } - return pcm; + return NULL; } static void hdac_hdmi_jack_report(struct hdac_hdmi_pcm *pcm, struct hdac_hdmi_port *port, bool is_connect) { - struct hdac_ext_device *edev = port->pin->edev; - - if (is_connect) - snd_soc_dapm_enable_pin(port->dapm, port->jack_pin); - else - snd_soc_dapm_disable_pin(port->dapm, port->jack_pin); + struct hdac_device *hdev = port->pin->hdev; + port->is_connect = is_connect; if (is_connect) { /* * Report Jack connect event when a device is connected @@ -164,7 +166,7 @@ static void hdac_hdmi_jack_report(struct hdac_hdmi_pcm *pcm, * ports. */ if (pcm->jack_event == 0) { - dev_dbg(&edev->hdac.dev, + dev_dbg(&hdev->dev, "jack report for pcm=%d\n", pcm->pcm_id); snd_soc_jack_report(pcm->jack, SND_JACK_AVOUT, @@ -182,27 +184,48 @@ static void hdac_hdmi_jack_report(struct hdac_hdmi_pcm *pcm, if (pcm->jack_event > 0) pcm->jack_event--; } +} +static void hdac_hdmi_port_dapm_update(struct hdac_hdmi_port *port) +{ + if (port->is_connect) + snd_soc_dapm_enable_pin(port->dapm, port->jack_pin); + else + snd_soc_dapm_disable_pin(port->dapm, port->jack_pin); snd_soc_dapm_sync(port->dapm); } +static void hdac_hdmi_jack_dapm_work(struct work_struct *work) +{ + struct hdac_hdmi_port *port; + + port = container_of(work, struct hdac_hdmi_port, dapm_work); + hdac_hdmi_port_dapm_update(port); +} + +static void hdac_hdmi_jack_report_sync(struct hdac_hdmi_pcm *pcm, + struct hdac_hdmi_port *port, bool is_connect) +{ + hdac_hdmi_jack_report(pcm, port, is_connect); + hdac_hdmi_port_dapm_update(port); +} + /* MST supported verbs */ /* * Get the no devices that can be connected to a port on the Pin widget. */ -static int hdac_hdmi_get_port_len(struct hdac_ext_device *hdac, hda_nid_t nid) +static int hdac_hdmi_get_port_len(struct hdac_device *hdev, hda_nid_t nid) { unsigned int caps; unsigned int type, param; - caps = get_wcaps(&hdac->hdac, nid); - type = get_wcaps_type(caps); + caps = snd_hdac_get_wcaps(hdev, nid); + type = snd_hdac_get_wcaps_type(caps); if (!(caps & AC_WCAP_DIGITAL) || (type != AC_WID_PIN)) return 0; - param = snd_hdac_read_parm_uncached(&hdac->hdac, nid, - AC_PAR_DEVLIST_LEN); + param = snd_hdac_read_parm_uncached(hdev, nid, AC_PAR_DEVLIST_LEN); if (param == -1) return param; @@ -214,10 +237,10 @@ static int hdac_hdmi_get_port_len(struct hdac_ext_device *hdac, hda_nid_t nid) * id selected on the pin. Return 0 means the first port entry * is selected or MST is not supported. */ -static int hdac_hdmi_port_select_get(struct hdac_ext_device *hdac, +static int hdac_hdmi_port_select_get(struct hdac_device *hdev, struct hdac_hdmi_port *port) { - return snd_hdac_codec_read(&hdac->hdac, port->pin->nid, + return snd_hdac_codec_read(hdev, port->pin->nid, 0, AC_VERB_GET_DEVICE_SEL, 0); } @@ -225,7 +248,7 @@ static int hdac_hdmi_port_select_get(struct hdac_ext_device *hdac, * Sets the selected port entry for the configuring Pin widget verb. * returns error if port set is not equal to port get otherwise success */ -static int hdac_hdmi_port_select_set(struct hdac_ext_device *hdac, +static int hdac_hdmi_port_select_set(struct hdac_device *hdev, struct hdac_hdmi_port *port) { int num_ports; @@ -234,8 +257,7 @@ static int hdac_hdmi_port_select_set(struct hdac_ext_device *hdac, return 0; /* AC_PAR_DEVLIST_LEN is 0 based. */ - num_ports = hdac_hdmi_get_port_len(hdac, port->pin->nid); - + num_ports = hdac_hdmi_get_port_len(hdev, port->pin->nid); if (num_ports < 0) return -EIO; /* @@ -245,13 +267,13 @@ static int hdac_hdmi_port_select_set(struct hdac_ext_device *hdac, if (num_ports + 1 < port->id) return 0; - snd_hdac_codec_write(&hdac->hdac, port->pin->nid, 0, + snd_hdac_codec_write(hdev, port->pin->nid, 0, AC_VERB_SET_DEVICE_SEL, port->id); - if (port->id != hdac_hdmi_port_select_get(hdac, port)) + if (port->id != hdac_hdmi_port_select_get(hdev, port)) return -EIO; - dev_dbg(&hdac->hdac.dev, "Selected the port=%d\n", port->id); + dev_dbg(&hdev->dev, "Selected the port=%d\n", port->id); return 0; } @@ -269,13 +291,6 @@ static struct hdac_hdmi_pcm *get_hdmi_pcm_from_id(struct hdac_hdmi_priv *hdmi, return NULL; } -static inline struct hdac_ext_device *to_hda_ext_device(struct device *dev) -{ - struct hdac_device *hdac = dev_to_hdac_dev(dev); - - return to_ehdac_device(hdac); -} - static unsigned int sad_format(const u8 *sad) { return ((sad[0] >> 0x3) & 0x1f); @@ -316,15 +331,13 @@ format_constraint: } static void -hdac_hdmi_set_dip_index(struct hdac_ext_device *hdac, hda_nid_t pin_nid, +hdac_hdmi_set_dip_index(struct hdac_device *hdev, hda_nid_t pin_nid, int packet_index, int byte_index) { int val; val = (packet_index << 5) | (byte_index & 0x1f); - - snd_hdac_codec_write(&hdac->hdac, pin_nid, 0, - AC_VERB_SET_HDMI_DIP_INDEX, val); + snd_hdac_codec_write(hdev, pin_nid, 0, AC_VERB_SET_HDMI_DIP_INDEX, val); } struct dp_audio_infoframe { @@ -339,14 +352,14 @@ struct dp_audio_infoframe { u8 LFEPBL01_LSV36_DM_INH7; }; -static int hdac_hdmi_setup_audio_infoframe(struct hdac_ext_device *hdac, +static int hdac_hdmi_setup_audio_infoframe(struct hdac_device *hdev, struct hdac_hdmi_pcm *pcm, struct hdac_hdmi_port *port) { uint8_t buffer[HDMI_INFOFRAME_HEADER_SIZE + HDMI_AUDIO_INFOFRAME_SIZE]; struct hdmi_audio_infoframe frame; struct hdac_hdmi_pin *pin = port->pin; struct dp_audio_infoframe dp_ai; - struct hdac_hdmi_priv *hdmi = hdac->private_data; + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); struct hdac_hdmi_cvt *cvt = pcm->cvt; u8 *dip; int ret; @@ -355,11 +368,11 @@ static int hdac_hdmi_setup_audio_infoframe(struct hdac_ext_device *hdac, u8 conn_type; int channels, ca; - ca = snd_hdac_channel_allocation(&hdac->hdac, port->eld.info.spk_alloc, + ca = snd_hdac_channel_allocation(hdev, port->eld.info.spk_alloc, pcm->channels, pcm->chmap_set, true, pcm->chmap); channels = snd_hdac_get_active_channels(ca); - hdmi->chmap.ops.set_channel_count(&hdac->hdac, cvt->nid, channels); + hdmi->chmap.ops.set_channel_count(hdev, cvt->nid, channels); snd_hdac_setup_channel_mapping(&hdmi->chmap, pin->nid, false, ca, pcm->channels, pcm->chmap, pcm->chmap_set); @@ -392,54 +405,58 @@ static int hdac_hdmi_setup_audio_infoframe(struct hdac_ext_device *hdac, break; default: - dev_err(&hdac->hdac.dev, "Invalid connection type: %d\n", - conn_type); + dev_err(&hdev->dev, "Invalid connection type: %d\n", conn_type); return -EIO; } /* stop infoframe transmission */ - hdac_hdmi_set_dip_index(hdac, pin->nid, 0x0, 0x0); - snd_hdac_codec_write(&hdac->hdac, pin->nid, 0, + hdac_hdmi_set_dip_index(hdev, pin->nid, 0x0, 0x0); + snd_hdac_codec_write(hdev, pin->nid, 0, AC_VERB_SET_HDMI_DIP_XMIT, AC_DIPXMIT_DISABLE); /* Fill infoframe. Index auto-incremented */ - hdac_hdmi_set_dip_index(hdac, pin->nid, 0x0, 0x0); + hdac_hdmi_set_dip_index(hdev, pin->nid, 0x0, 0x0); if (conn_type == DRM_ELD_CONN_TYPE_HDMI) { for (i = 0; i < sizeof(buffer); i++) - snd_hdac_codec_write(&hdac->hdac, pin->nid, 0, + snd_hdac_codec_write(hdev, pin->nid, 0, AC_VERB_SET_HDMI_DIP_DATA, buffer[i]); } else { for (i = 0; i < sizeof(dp_ai); i++) - snd_hdac_codec_write(&hdac->hdac, pin->nid, 0, + snd_hdac_codec_write(hdev, pin->nid, 0, AC_VERB_SET_HDMI_DIP_DATA, dip[i]); } /* Start infoframe */ - hdac_hdmi_set_dip_index(hdac, pin->nid, 0x0, 0x0); - snd_hdac_codec_write(&hdac->hdac, pin->nid, 0, + hdac_hdmi_set_dip_index(hdev, pin->nid, 0x0, 0x0); + snd_hdac_codec_write(hdev, pin->nid, 0, AC_VERB_SET_HDMI_DIP_XMIT, AC_DIPXMIT_BEST); return 0; } -static int hdac_hdmi_set_tdm_slot(struct snd_soc_dai *dai, - unsigned int tx_mask, unsigned int rx_mask, - int slots, int slot_width) +static int hdac_hdmi_set_stream(struct snd_soc_dai *dai, + void *stream, int direction) { - struct hdac_ext_device *edev = snd_soc_dai_get_drvdata(dai); - struct hdac_hdmi_priv *hdmi = edev->private_data; + struct hdac_hdmi_priv *hdmi = snd_soc_dai_get_drvdata(dai); + struct hdac_device *hdev = hdmi->hdev; struct hdac_hdmi_dai_port_map *dai_map; struct hdac_hdmi_pcm *pcm; + struct hdac_stream *hstream; + + if (!stream) + return -EINVAL; + + hstream = (struct hdac_stream *)stream; - dev_dbg(&edev->hdac.dev, "%s: strm_tag: %d\n", __func__, tx_mask); + dev_dbg(&hdev->dev, "%s: strm_tag: %d\n", __func__, hstream->stream_tag); dai_map = &hdmi->dai_map[dai->id]; pcm = hdac_hdmi_get_pcm_from_cvt(hdmi, dai_map->cvt); if (pcm) - pcm->stream_tag = (tx_mask << 4); + pcm->stream_tag = (hstream->stream_tag << 4); return 0; } @@ -447,29 +464,17 @@ static int hdac_hdmi_set_tdm_slot(struct snd_soc_dai *dai, static int hdac_hdmi_set_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hparams, struct snd_soc_dai *dai) { - struct hdac_ext_device *hdac = snd_soc_dai_get_drvdata(dai); - struct hdac_hdmi_priv *hdmi = hdac->private_data; + struct hdac_hdmi_priv *hdmi = snd_soc_dai_get_drvdata(dai); struct hdac_hdmi_dai_port_map *dai_map; - struct hdac_hdmi_port *port; struct hdac_hdmi_pcm *pcm; + unsigned int bits; int format; dai_map = &hdmi->dai_map[dai->id]; - port = dai_map->port; - if (!port) - return -ENODEV; - - if ((!port->eld.monitor_present) || (!port->eld.eld_valid)) { - dev_err(&hdac->hdac.dev, - "device is not configured for this pin:port%d:%d\n", - port->pin->nid, port->id); - return -ENODEV; - } - - format = snd_hdac_calc_stream_format(params_rate(hparams), - params_channels(hparams), params_format(hparams), - dai->driver->playback.sig_bits, 0); + bits = snd_hdac_stream_format_bits(params_format(hparams), SNDRV_PCM_SUBFORMAT_STD, + dai->driver->playback.sig_bits); + format = snd_hdac_stream_format(params_channels(hparams), bits, params_rate(hparams)); pcm = hdac_hdmi_get_pcm_from_cvt(hdmi, dai_map->cvt); if (!pcm) @@ -481,28 +486,28 @@ static int hdac_hdmi_set_hw_params(struct snd_pcm_substream *substream, return 0; } -static int hdac_hdmi_query_port_connlist(struct hdac_ext_device *hdac, +static int hdac_hdmi_query_port_connlist(struct hdac_device *hdev, struct hdac_hdmi_pin *pin, struct hdac_hdmi_port *port) { - if (!(get_wcaps(&hdac->hdac, pin->nid) & AC_WCAP_CONN_LIST)) { - dev_warn(&hdac->hdac.dev, + if (!(snd_hdac_get_wcaps(hdev, pin->nid) & AC_WCAP_CONN_LIST)) { + dev_warn(&hdev->dev, "HDMI: pin %d wcaps %#x does not support connection list\n", - pin->nid, get_wcaps(&hdac->hdac, pin->nid)); + pin->nid, snd_hdac_get_wcaps(hdev, pin->nid)); return -EINVAL; } - if (hdac_hdmi_port_select_set(hdac, port) < 0) + if (hdac_hdmi_port_select_set(hdev, port) < 0) return -EIO; - port->num_mux_nids = snd_hdac_get_connections(&hdac->hdac, pin->nid, + port->num_mux_nids = snd_hdac_get_connections(hdev, pin->nid, port->mux_nids, HDA_MAX_CONNECTIONS); if (port->num_mux_nids == 0) - dev_warn(&hdac->hdac.dev, + dev_warn(&hdev->dev, "No connections found for pin:port %d:%d\n", pin->nid, port->id); - dev_dbg(&hdac->hdac.dev, "num_mux_nids %d for pin:port %d:%d\n", + dev_dbg(&hdev->dev, "num_mux_nids %d for pin:port %d:%d\n", port->num_mux_nids, pin->nid, port->id); return port->num_mux_nids; @@ -518,12 +523,12 @@ static int hdac_hdmi_query_port_connlist(struct hdac_ext_device *hdac, * connected. */ static struct hdac_hdmi_port *hdac_hdmi_get_port_from_cvt( - struct hdac_ext_device *edev, + struct hdac_device *hdev, struct hdac_hdmi_priv *hdmi, struct hdac_hdmi_cvt *cvt) { struct hdac_hdmi_pcm *pcm; - struct hdac_hdmi_port *port = NULL; + struct hdac_hdmi_port *port; int ret, i; list_for_each_entry(pcm, &hdmi->pcm_list, head) { @@ -533,7 +538,7 @@ static struct hdac_hdmi_port *hdac_hdmi_get_port_from_cvt( list_for_each_entry(port, &pcm->port_list, head) { mutex_lock(&pcm->lock); - ret = hdac_hdmi_query_port_connlist(edev, + ret = hdac_hdmi_query_port_connlist(hdev, port->pin, port); mutex_unlock(&pcm->lock); if (ret < 0) @@ -553,6 +558,29 @@ static struct hdac_hdmi_port *hdac_hdmi_get_port_from_cvt( } /* + * Go through all converters and ensure connection is set to + * the correct pin as set via kcontrols. + */ +static void hdac_hdmi_verify_connect_sel_all_pins(struct hdac_device *hdev) +{ + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); + struct hdac_hdmi_port *port; + struct hdac_hdmi_cvt *cvt; + int cvt_idx = 0; + + list_for_each_entry(cvt, &hdmi->cvt_list, head) { + port = hdac_hdmi_get_port_from_cvt(hdev, hdmi, cvt); + if (port && port->pin) { + snd_hdac_codec_write(hdev, port->pin->nid, 0, + AC_VERB_SET_CONNECT_SEL, cvt_idx); + dev_dbg(&hdev->dev, "%s: %s set connect %d -> %d\n", + __func__, cvt->name, port->pin->nid, cvt_idx); + } + ++cvt_idx; + } +} + +/* * This tries to get a valid pin and set the HW constraints based on the * ELD. Even if a valid pin is not found return success so that device open * doesn't fail. @@ -560,8 +588,8 @@ static struct hdac_hdmi_port *hdac_hdmi_get_port_from_cvt( static int hdac_hdmi_pcm_open(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - struct hdac_ext_device *hdac = snd_soc_dai_get_drvdata(dai); - struct hdac_hdmi_priv *hdmi = hdac->private_data; + struct hdac_hdmi_priv *hdmi = snd_soc_dai_get_drvdata(dai); + struct hdac_device *hdev = hdmi->hdev; struct hdac_hdmi_dai_port_map *dai_map; struct hdac_hdmi_cvt *cvt; struct hdac_hdmi_port *port; @@ -570,7 +598,7 @@ static int hdac_hdmi_pcm_open(struct snd_pcm_substream *substream, dai_map = &hdmi->dai_map[dai->id]; cvt = dai_map->cvt; - port = hdac_hdmi_get_port_from_cvt(hdac, hdmi, cvt); + port = hdac_hdmi_get_port_from_cvt(hdev, hdmi, cvt); /* * To make PA and other userland happy. @@ -581,7 +609,7 @@ static int hdac_hdmi_pcm_open(struct snd_pcm_substream *substream, if ((!port->eld.monitor_present) || (!port->eld.eld_valid)) { - dev_warn(&hdac->hdac.dev, + dev_warn(&hdev->dev, "Failed: present?:%d ELD valid?:%d pin:port: %d:%d\n", port->eld.monitor_present, port->eld.eld_valid, port->pin->nid, port->id); @@ -603,8 +631,7 @@ static int hdac_hdmi_pcm_open(struct snd_pcm_substream *substream, static void hdac_hdmi_pcm_close(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - struct hdac_ext_device *hdac = snd_soc_dai_get_drvdata(dai); - struct hdac_hdmi_priv *hdmi = hdac->private_data; + struct hdac_hdmi_priv *hdmi = snd_soc_dai_get_drvdata(dai); struct hdac_hdmi_dai_port_map *dai_map; struct hdac_hdmi_pcm *pcm; @@ -625,15 +652,14 @@ static void hdac_hdmi_pcm_close(struct snd_pcm_substream *substream, } static int -hdac_hdmi_query_cvt_params(struct hdac_device *hdac, struct hdac_hdmi_cvt *cvt) +hdac_hdmi_query_cvt_params(struct hdac_device *hdev, struct hdac_hdmi_cvt *cvt) { unsigned int chans; - struct hdac_ext_device *edev = to_ehdac_device(hdac); - struct hdac_hdmi_priv *hdmi = edev->private_data; + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); int err; - chans = get_wcaps(hdac, cvt->nid); - chans = get_wcaps_channels(chans); + chans = snd_hdac_get_wcaps(hdev, cvt->nid); + chans = snd_hdac_get_wcaps_channels(chans); cvt->params.channels_min = 2; @@ -641,12 +667,13 @@ hdac_hdmi_query_cvt_params(struct hdac_device *hdac, struct hdac_hdmi_cvt *cvt) if (chans > hdmi->chmap.channels_max) hdmi->chmap.channels_max = chans; - err = snd_hdac_query_supported_pcm(hdac, cvt->nid, + err = snd_hdac_query_supported_pcm(hdev, cvt->nid, &cvt->params.rates, &cvt->params.formats, + NULL, &cvt->params.maxbps); if (err < 0) - dev_err(&hdac->dev, + dev_err(&hdev->dev, "Failed to query pcm params for nid %d: %d\n", cvt->nid, err); @@ -688,11 +715,11 @@ static void hdac_hdmi_fill_route(struct snd_soc_dapm_route *route, route->connected = handler; } -static struct hdac_hdmi_pcm *hdac_hdmi_get_pcm(struct hdac_ext_device *edev, +static struct hdac_hdmi_pcm *hdac_hdmi_get_pcm(struct hdac_device *hdev, struct hdac_hdmi_port *port) { - struct hdac_hdmi_priv *hdmi = edev->private_data; - struct hdac_hdmi_pcm *pcm = NULL; + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); + struct hdac_hdmi_pcm *pcm; struct hdac_hdmi_port *p; list_for_each_entry(pcm, &hdmi->pcm_list, head) { @@ -708,21 +735,32 @@ static struct hdac_hdmi_pcm *hdac_hdmi_get_pcm(struct hdac_ext_device *edev, return NULL; } -static void hdac_hdmi_set_power_state(struct hdac_ext_device *edev, +static void hdac_hdmi_set_power_state(struct hdac_device *hdev, hda_nid_t nid, unsigned int pwr_state) { - if (get_wcaps(&edev->hdac, nid) & AC_WCAP_POWER) { - if (!snd_hdac_check_power_state(&edev->hdac, nid, pwr_state)) - snd_hdac_codec_write(&edev->hdac, nid, 0, - AC_VERB_SET_POWER_STATE, pwr_state); + int count; + unsigned int state; + + if (snd_hdac_get_wcaps(hdev, nid) & AC_WCAP_POWER) { + if (!snd_hdac_check_power_state(hdev, nid, pwr_state)) { + for (count = 0; count < 10; count++) { + snd_hdac_codec_read(hdev, nid, 0, + AC_VERB_SET_POWER_STATE, + pwr_state); + state = snd_hdac_sync_power_state(hdev, + nid, pwr_state); + if (!(state & AC_PWRST_ERROR)) + break; + } + } } } -static void hdac_hdmi_set_amp(struct hdac_ext_device *edev, +static void hdac_hdmi_set_amp(struct hdac_device *hdev, hda_nid_t nid, int val) { - if (get_wcaps(&edev->hdac, nid) & AC_WCAP_OUT_AMP) - snd_hdac_codec_write(&edev->hdac, nid, 0, + if (snd_hdac_get_wcaps(hdev, nid) & AC_WCAP_OUT_AMP) + snd_hdac_codec_write(hdev, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, val); } @@ -731,40 +769,41 @@ static int hdac_hdmi_pin_output_widget_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kc, int event) { struct hdac_hdmi_port *port = w->priv; - struct hdac_ext_device *edev = to_hda_ext_device(w->dapm->dev); + struct device *dev = snd_soc_dapm_to_dev(w->dapm); + struct hdac_device *hdev = dev_to_hdac_dev(dev); struct hdac_hdmi_pcm *pcm; - dev_dbg(&edev->hdac.dev, "%s: widget: %s event: %x\n", + dev_dbg(&hdev->dev, "%s: widget: %s event: %x\n", __func__, w->name, event); - pcm = hdac_hdmi_get_pcm(edev, port); + pcm = hdac_hdmi_get_pcm(hdev, port); if (!pcm) return -EIO; /* set the device if pin is mst_capable */ - if (hdac_hdmi_port_select_set(edev, port) < 0) + if (hdac_hdmi_port_select_set(hdev, port) < 0) return -EIO; switch (event) { case SND_SOC_DAPM_PRE_PMU: - hdac_hdmi_set_power_state(edev, port->pin->nid, AC_PWRST_D0); + hdac_hdmi_set_power_state(hdev, port->pin->nid, AC_PWRST_D0); /* Enable out path for this pin widget */ - snd_hdac_codec_write(&edev->hdac, port->pin->nid, 0, + snd_hdac_codec_write(hdev, port->pin->nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); - hdac_hdmi_set_amp(edev, port->pin->nid, AMP_OUT_UNMUTE); + hdac_hdmi_set_amp(hdev, port->pin->nid, AMP_OUT_UNMUTE); - return hdac_hdmi_setup_audio_infoframe(edev, pcm, port); + return hdac_hdmi_setup_audio_infoframe(hdev, pcm, port); case SND_SOC_DAPM_POST_PMD: - hdac_hdmi_set_amp(edev, port->pin->nid, AMP_OUT_MUTE); + hdac_hdmi_set_amp(hdev, port->pin->nid, AMP_OUT_MUTE); /* Disable out path for this pin widget */ - snd_hdac_codec_write(&edev->hdac, port->pin->nid, 0, + snd_hdac_codec_write(hdev, port->pin->nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0); - hdac_hdmi_set_power_state(edev, port->pin->nid, AC_PWRST_D3); + hdac_hdmi_set_power_state(hdev, port->pin->nid, AC_PWRST_D3); break; } @@ -776,11 +815,12 @@ static int hdac_hdmi_cvt_output_widget_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kc, int event) { struct hdac_hdmi_cvt *cvt = w->priv; - struct hdac_ext_device *edev = to_hda_ext_device(w->dapm->dev); - struct hdac_hdmi_priv *hdmi = edev->private_data; + struct device *dev = snd_soc_dapm_to_dev(w->dapm); + struct hdac_device *hdev = dev_to_hdac_dev(dev); + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); struct hdac_hdmi_pcm *pcm; - dev_dbg(&edev->hdac.dev, "%s: widget: %s event: %x\n", + dev_dbg(&hdev->dev, "%s: widget: %s event: %x\n", __func__, w->name, event); pcm = hdac_hdmi_get_pcm_from_cvt(hdmi, cvt); @@ -789,29 +829,37 @@ static int hdac_hdmi_cvt_output_widget_event(struct snd_soc_dapm_widget *w, switch (event) { case SND_SOC_DAPM_PRE_PMU: - hdac_hdmi_set_power_state(edev, cvt->nid, AC_PWRST_D0); + hdac_hdmi_set_power_state(hdev, cvt->nid, AC_PWRST_D0); /* Enable transmission */ - snd_hdac_codec_write(&edev->hdac, cvt->nid, 0, + snd_hdac_codec_write(hdev, cvt->nid, 0, AC_VERB_SET_DIGI_CONVERT_1, 1); /* Category Code (CC) to zero */ - snd_hdac_codec_write(&edev->hdac, cvt->nid, 0, + snd_hdac_codec_write(hdev, cvt->nid, 0, AC_VERB_SET_DIGI_CONVERT_2, 0); - snd_hdac_codec_write(&edev->hdac, cvt->nid, 0, + snd_hdac_codec_write(hdev, cvt->nid, 0, AC_VERB_SET_CHANNEL_STREAMID, pcm->stream_tag); - snd_hdac_codec_write(&edev->hdac, cvt->nid, 0, + snd_hdac_codec_write(hdev, cvt->nid, 0, AC_VERB_SET_STREAM_FORMAT, pcm->format); + + /* + * The connection indices are shared by all converters and + * may interfere with each other. Ensure correct + * routing for all converters at stream start. + */ + hdac_hdmi_verify_connect_sel_all_pins(hdev); + break; case SND_SOC_DAPM_POST_PMD: - snd_hdac_codec_write(&edev->hdac, cvt->nid, 0, + snd_hdac_codec_write(hdev, cvt->nid, 0, AC_VERB_SET_CHANNEL_STREAMID, 0); - snd_hdac_codec_write(&edev->hdac, cvt->nid, 0, + snd_hdac_codec_write(hdev, cvt->nid, 0, AC_VERB_SET_STREAM_FORMAT, 0); - hdac_hdmi_set_power_state(edev, cvt->nid, AC_PWRST_D3); + hdac_hdmi_set_power_state(hdev, cvt->nid, AC_PWRST_D3); break; } @@ -823,23 +871,24 @@ static int hdac_hdmi_pin_mux_widget_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kc, int event) { struct hdac_hdmi_port *port = w->priv; - struct hdac_ext_device *edev = to_hda_ext_device(w->dapm->dev); + struct device *dev = snd_soc_dapm_to_dev(w->dapm); + struct hdac_device *hdev = dev_to_hdac_dev(dev); int mux_idx; - dev_dbg(&edev->hdac.dev, "%s: widget: %s event: %x\n", + dev_dbg(&hdev->dev, "%s: widget: %s event: %x\n", __func__, w->name, event); if (!kc) kc = w->kcontrols[0]; - mux_idx = dapm_kcontrol_get_value(kc); + mux_idx = snd_soc_dapm_kcontrol_get_value(kc); /* set the device if pin is mst_capable */ - if (hdac_hdmi_port_select_set(edev, port) < 0) + if (hdac_hdmi_port_select_set(hdev, port) < 0) return -EIO; if (mux_idx > 0) { - snd_hdac_codec_write(&edev->hdac, port->pin->nid, 0, + snd_hdac_codec_write(hdev, port->pin->nid, 0, AC_VERB_SET_CONNECT_SEL, (mux_idx - 1)); } @@ -855,12 +904,13 @@ static int hdac_hdmi_set_pin_port_mux(struct snd_kcontrol *kcontrol, int ret; struct hdac_hdmi_port *p, *p_next; struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; - struct snd_soc_dapm_widget *w = snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_dapm_widget *w = snd_soc_dapm_kcontrol_to_widget(kcontrol); struct snd_soc_dapm_context *dapm = w->dapm; + struct device *dev = snd_soc_dapm_to_dev(dapm); struct hdac_hdmi_port *port = w->priv; - struct hdac_ext_device *edev = to_hda_ext_device(dapm->dev); - struct hdac_hdmi_priv *hdmi = edev->private_data; - struct hdac_hdmi_pcm *pcm = NULL; + struct hdac_device *hdev = dev_to_hdac_dev(dev); + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); + struct hdac_hdmi_pcm *pcm; const char *cvt_name = e->texts[ucontrol->value.enumerated.item[0]]; ret = snd_soc_dapm_put_enum_double(kcontrol, ucontrol); @@ -878,7 +928,7 @@ static int hdac_hdmi_set_pin_port_mux(struct snd_kcontrol *kcontrol, list_for_each_entry_safe(p, p_next, &pcm->port_list, head) { if (p == port && p->id == port->id && p->pin == port->pin) { - hdac_hdmi_jack_report(pcm, port, false); + hdac_hdmi_jack_report_sync(pcm, port, false); list_del(&p->head); } } @@ -892,7 +942,7 @@ static int hdac_hdmi_set_pin_port_mux(struct snd_kcontrol *kcontrol, if (!strcmp(cvt_name, pcm->cvt->name)) { list_add_tail(&port->head, &pcm->port_list); if (port->eld.monitor_present && port->eld.eld_valid) { - hdac_hdmi_jack_report(pcm, port, true); + hdac_hdmi_jack_report_sync(pcm, port, true); mutex_unlock(&hdmi->pin_mutex); return ret; } @@ -912,12 +962,12 @@ static int hdac_hdmi_set_pin_port_mux(struct snd_kcontrol *kcontrol, * care of selecting the right one and leaving all other inputs selected to * "NONE" */ -static int hdac_hdmi_create_pin_port_muxs(struct hdac_ext_device *edev, +static int hdac_hdmi_create_pin_port_muxs(struct hdac_device *hdev, struct hdac_hdmi_port *port, struct snd_soc_dapm_widget *widget, const char *widget_name) { - struct hdac_hdmi_priv *hdmi = edev->private_data; + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); struct hdac_hdmi_pin *pin = port->pin; struct snd_kcontrol_new *kc; struct hdac_hdmi_cvt *cvt; @@ -929,16 +979,17 @@ static int hdac_hdmi_create_pin_port_muxs(struct hdac_ext_device *edev, int i = 0; int num_items = hdmi->num_cvt + 1; - kc = devm_kzalloc(&edev->hdac.dev, sizeof(*kc), GFP_KERNEL); + kc = devm_kzalloc(&hdev->dev, sizeof(*kc), GFP_KERNEL); if (!kc) return -ENOMEM; - se = devm_kzalloc(&edev->hdac.dev, sizeof(*se), GFP_KERNEL); + se = devm_kzalloc(&hdev->dev, sizeof(*se), GFP_KERNEL); if (!se) return -ENOMEM; - sprintf(kc_name, "Pin %d port %d Input", pin->nid, port->id); - kc->name = devm_kstrdup(&edev->hdac.dev, kc_name, GFP_KERNEL); + snprintf(kc_name, NAME_SIZE, "Pin %d port %d Input", + pin->nid, port->id); + kc->name = devm_kstrdup(&hdev->dev, kc_name, GFP_KERNEL); if (!kc->name) return -ENOMEM; @@ -956,35 +1007,34 @@ static int hdac_hdmi_create_pin_port_muxs(struct hdac_ext_device *edev, se->mask = roundup_pow_of_two(se->items) - 1; sprintf(mux_items, "NONE"); - items[i] = devm_kstrdup(&edev->hdac.dev, mux_items, GFP_KERNEL); + items[i] = devm_kstrdup(&hdev->dev, mux_items, GFP_KERNEL); if (!items[i]) return -ENOMEM; list_for_each_entry(cvt, &hdmi->cvt_list, head) { i++; sprintf(mux_items, "cvt %d", cvt->nid); - items[i] = devm_kstrdup(&edev->hdac.dev, mux_items, GFP_KERNEL); + items[i] = devm_kstrdup(&hdev->dev, mux_items, GFP_KERNEL); if (!items[i]) return -ENOMEM; } - se->texts = devm_kmemdup(&edev->hdac.dev, items, - (num_items * sizeof(char *)), GFP_KERNEL); + se->texts = devm_kmemdup_array(&hdev->dev, items, num_items, sizeof(items[0]), GFP_KERNEL); if (!se->texts) return -ENOMEM; - return hdac_hdmi_fill_widget_info(&edev->hdac.dev, widget, + return hdac_hdmi_fill_widget_info(&hdev->dev, widget, snd_soc_dapm_mux, port, widget_name, NULL, kc, 1, hdac_hdmi_pin_mux_widget_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_REG); } /* Add cvt <- input <- mux route map */ -static void hdac_hdmi_add_pinmux_cvt_route(struct hdac_ext_device *edev, +static void hdac_hdmi_add_pinmux_cvt_route(struct hdac_device *hdev, struct snd_soc_dapm_widget *widgets, struct snd_soc_dapm_route *route, int rindex) { - struct hdac_hdmi_priv *hdmi = edev->private_data; + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); const struct snd_kcontrol_new *kc; struct soc_enum *se; int mux_index = hdmi->num_cvt + hdmi->num_ports; @@ -1024,11 +1074,13 @@ static void hdac_hdmi_add_pinmux_cvt_route(struct hdac_ext_device *edev, */ static int create_fill_widget_route_map(struct snd_soc_dapm_context *dapm) { + struct device *dev = snd_soc_dapm_to_dev(dapm); + struct snd_soc_card *card = snd_soc_dapm_to_card(dapm); struct snd_soc_dapm_widget *widgets; struct snd_soc_dapm_route *route; - struct hdac_ext_device *edev = to_hda_ext_device(dapm->dev); - struct hdac_hdmi_priv *hdmi = edev->private_data; - struct snd_soc_dai_driver *dai_drv = dapm->component->dai_drv; + struct hdac_device *hdev = dev_to_hdac_dev(dev); + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); + struct snd_soc_dai_driver *dai_drv = hdmi->dai_drv; char widget_name[NAME_SIZE]; struct hdac_hdmi_cvt *cvt; struct hdac_hdmi_pin *pin; @@ -1037,7 +1089,7 @@ static int create_fill_widget_route_map(struct snd_soc_dapm_context *dapm) if (list_empty(&hdmi->cvt_list) || list_empty(&hdmi->pin_list)) return -EINVAL; - widgets = devm_kzalloc(dapm->dev, (sizeof(*widgets) * + widgets = devm_kzalloc(dev, (sizeof(*widgets) * ((2 * hdmi->num_ports) + hdmi->num_cvt)), GFP_KERNEL); @@ -1047,7 +1099,7 @@ static int create_fill_widget_route_map(struct snd_soc_dapm_context *dapm) /* DAPM widgets to represent each converter widget */ list_for_each_entry(cvt, &hdmi->cvt_list, head) { sprintf(widget_name, "Converter %d", cvt->nid); - ret = hdac_hdmi_fill_widget_info(dapm->dev, &widgets[i], + ret = hdac_hdmi_fill_widget_info(dev, &widgets[i], snd_soc_dapm_aif_in, cvt, widget_name, dai_drv[i].playback.stream_name, NULL, 0, hdac_hdmi_cvt_output_widget_event, @@ -1061,7 +1113,7 @@ static int create_fill_widget_route_map(struct snd_soc_dapm_context *dapm) for (j = 0; j < pin->num_ports; j++) { sprintf(widget_name, "hif%d-%d Output", pin->nid, pin->ports[j].id); - ret = hdac_hdmi_fill_widget_info(dapm->dev, &widgets[i], + ret = hdac_hdmi_fill_widget_info(dev, &widgets[i], snd_soc_dapm_output, &pin->ports[j], widget_name, NULL, NULL, 0, hdac_hdmi_pin_output_widget_event, @@ -1079,7 +1131,7 @@ static int create_fill_widget_route_map(struct snd_soc_dapm_context *dapm) for (j = 0; j < pin->num_ports; j++) { sprintf(widget_name, "Pin%d-Port%d Mux", pin->nid, pin->ports[j].id); - ret = hdac_hdmi_create_pin_port_muxs(edev, + ret = hdac_hdmi_create_pin_port_muxs(hdev, &pin->ports[j], &widgets[i], widget_name); if (ret < 0) @@ -1094,7 +1146,7 @@ static int create_fill_widget_route_map(struct snd_soc_dapm_context *dapm) } } - route = devm_kzalloc(dapm->dev, (sizeof(*route) * num_routes), + route = devm_kzalloc(dev, (sizeof(*route) * num_routes), GFP_KERNEL); if (!route) return -ENOMEM; @@ -1114,21 +1166,21 @@ static int create_fill_widget_route_map(struct snd_soc_dapm_context *dapm) } } - hdac_hdmi_add_pinmux_cvt_route(edev, widgets, route, i); + hdac_hdmi_add_pinmux_cvt_route(hdev, widgets, route, i); snd_soc_dapm_new_controls(dapm, widgets, ((2 * hdmi->num_ports) + hdmi->num_cvt)); snd_soc_dapm_add_routes(dapm, route, num_routes); - snd_soc_dapm_new_widgets(dapm->card); + snd_soc_dapm_new_widgets(card); return 0; } -static int hdac_hdmi_init_dai_map(struct hdac_ext_device *edev) +static int hdac_hdmi_init_dai_map(struct hdac_device *hdev) { - struct hdac_hdmi_priv *hdmi = edev->private_data; + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); struct hdac_hdmi_dai_port_map *dai_map; struct hdac_hdmi_cvt *cvt; int dai_id = 0; @@ -1144,7 +1196,7 @@ static int hdac_hdmi_init_dai_map(struct hdac_ext_device *edev) dai_id++; if (dai_id == HDA_MAX_CVTS) { - dev_warn(&edev->hdac.dev, + dev_warn(&hdev->dev, "Max dais supported: %d\n", dai_id); break; } @@ -1153,27 +1205,29 @@ static int hdac_hdmi_init_dai_map(struct hdac_ext_device *edev) return 0; } -static int hdac_hdmi_add_cvt(struct hdac_ext_device *edev, hda_nid_t nid) +static int hdac_hdmi_add_cvt(struct hdac_device *hdev, hda_nid_t nid) { - struct hdac_hdmi_priv *hdmi = edev->private_data; + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); struct hdac_hdmi_cvt *cvt; char name[NAME_SIZE]; - cvt = kzalloc(sizeof(*cvt), GFP_KERNEL); + cvt = devm_kzalloc(&hdev->dev, sizeof(*cvt), GFP_KERNEL); if (!cvt) return -ENOMEM; cvt->nid = nid; sprintf(name, "cvt %d", cvt->nid); - cvt->name = kstrdup(name, GFP_KERNEL); + cvt->name = devm_kstrdup(&hdev->dev, name, GFP_KERNEL); + if (!cvt->name) + return -ENOMEM; list_add_tail(&cvt->head, &hdmi->cvt_list); hdmi->num_cvt++; - return hdac_hdmi_query_cvt_params(&edev->hdac, cvt); + return hdac_hdmi_query_cvt_params(hdev, cvt); } -static int hdac_hdmi_parse_eld(struct hdac_ext_device *edev, +static int hdac_hdmi_parse_eld(struct hdac_device *hdev, struct hdac_hdmi_port *port) { unsigned int ver, mnl; @@ -1182,7 +1236,8 @@ static int hdac_hdmi_parse_eld(struct hdac_ext_device *edev, >> DRM_ELD_VER_SHIFT; if (ver != ELD_VER_CEA_861D && ver != ELD_VER_PARTIAL) { - dev_err(&edev->hdac.dev, "HDMI: Unknown ELD version %d\n", ver); + dev_err_ratelimited(&hdev->dev, + "HDMI: Unknown ELD version %d\n", ver); return -EINVAL; } @@ -1190,7 +1245,8 @@ static int hdac_hdmi_parse_eld(struct hdac_ext_device *edev, DRM_ELD_MNL_MASK) >> DRM_ELD_MNL_SHIFT; if (mnl > ELD_MAX_MNL) { - dev_err(&edev->hdac.dev, "HDMI: MNL Invalid %d\n", mnl); + dev_err_ratelimited(&hdev->dev, + "HDMI: MNL Invalid %d\n", mnl); return -EINVAL; } @@ -1202,11 +1258,12 @@ static int hdac_hdmi_parse_eld(struct hdac_ext_device *edev, static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin, struct hdac_hdmi_port *port) { - struct hdac_ext_device *edev = pin->edev; - struct hdac_hdmi_priv *hdmi = edev->private_data; + struct hdac_device *hdev = pin->hdev; + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); struct hdac_hdmi_pcm *pcm; int size = 0; int port_id = -1; + bool eld_valid, eld_changed; if (!hdmi) return; @@ -1221,17 +1278,19 @@ static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin, if (pin->mst_capable) port_id = port->id; - size = snd_hdac_acomp_get_eld(&edev->hdac, pin->nid, port_id, + size = snd_hdac_acomp_get_eld(hdev, pin->nid, port_id, &port->eld.monitor_present, port->eld.eld_buffer, ELD_MAX_SIZE); if (size > 0) { size = min(size, ELD_MAX_SIZE); - if (hdac_hdmi_parse_eld(edev, port) < 0) + if (hdac_hdmi_parse_eld(hdev, port) < 0) size = -EINVAL; } + eld_valid = port->eld.eld_valid; + if (size > 0) { port->eld.eld_valid = true; port->eld.eld_size = size; @@ -1240,38 +1299,50 @@ static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin, port->eld.eld_size = 0; } - pcm = hdac_hdmi_get_pcm(edev, port); + eld_changed = (eld_valid != port->eld.eld_valid); + + pcm = hdac_hdmi_get_pcm(hdev, port); if (!port->eld.monitor_present || !port->eld.eld_valid) { - dev_err(&edev->hdac.dev, "%s: disconnect for pin:port %d:%d\n", - __func__, pin->nid, port->id); + dev_dbg(&hdev->dev, "%s: disconnect for pin:port %d:%d\n", + __func__, pin->nid, port->id); /* * PCMs are not registered during device probe, so don't * report jack here. It will be done in usermode mux * control select. */ - if (pcm) + if (pcm) { hdac_hdmi_jack_report(pcm, port, false); + schedule_work(&port->dapm_work); + } mutex_unlock(&hdmi->pin_mutex); return; } if (port->eld.monitor_present && port->eld.eld_valid) { - if (pcm) + if (pcm) { hdac_hdmi_jack_report(pcm, port, true); + schedule_work(&port->dapm_work); + } print_hex_dump_debug("ELD: ", DUMP_PREFIX_OFFSET, 16, 1, port->eld.eld_buffer, port->eld.eld_size, false); } mutex_unlock(&hdmi->pin_mutex); + + if (eld_changed && pcm) + snd_ctl_notify(hdmi->card, + SNDRV_CTL_EVENT_MASK_VALUE | + SNDRV_CTL_EVENT_MASK_INFO, + &pcm->eld_ctl->id); } -static int hdac_hdmi_add_ports(struct hdac_hdmi_priv *hdmi, - struct hdac_hdmi_pin *pin) +static int hdac_hdmi_add_ports(struct hdac_device *hdev, + struct hdac_hdmi_pin *pin) { struct hdac_hdmi_port *ports; int max_ports = HDA_MAX_PORTS; @@ -1283,33 +1354,34 @@ static int hdac_hdmi_add_ports(struct hdac_hdmi_priv *hdmi, * implemented. */ - ports = kcalloc(max_ports, sizeof(*ports), GFP_KERNEL); + ports = devm_kcalloc(&hdev->dev, max_ports, sizeof(*ports), GFP_KERNEL); if (!ports) return -ENOMEM; for (i = 0; i < max_ports; i++) { ports[i].id = i; ports[i].pin = pin; + INIT_WORK(&ports[i].dapm_work, hdac_hdmi_jack_dapm_work); } pin->ports = ports; pin->num_ports = max_ports; return 0; } -static int hdac_hdmi_add_pin(struct hdac_ext_device *edev, hda_nid_t nid) +static int hdac_hdmi_add_pin(struct hdac_device *hdev, hda_nid_t nid) { - struct hdac_hdmi_priv *hdmi = edev->private_data; + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); struct hdac_hdmi_pin *pin; int ret; - pin = kzalloc(sizeof(*pin), GFP_KERNEL); + pin = devm_kzalloc(&hdev->dev, sizeof(*pin), GFP_KERNEL); if (!pin) return -ENOMEM; pin->nid = nid; pin->mst_capable = false; - pin->edev = edev; - ret = hdac_hdmi_add_ports(hdmi, pin); + pin->hdev = hdev; + ret = hdac_hdmi_add_ports(hdev, pin); if (ret < 0) return ret; @@ -1321,57 +1393,62 @@ static int hdac_hdmi_add_pin(struct hdac_ext_device *edev, hda_nid_t nid) } #define INTEL_VENDOR_NID 0x08 +#define INTEL_GLK_VENDOR_NID 0x0b #define INTEL_GET_VENDOR_VERB 0xf81 #define INTEL_SET_VENDOR_VERB 0x781 #define INTEL_EN_DP12 0x02 /* enable DP 1.2 features */ #define INTEL_EN_ALL_PIN_CVTS 0x01 /* enable 2nd & 3rd pins and convertors */ -static void hdac_hdmi_skl_enable_all_pins(struct hdac_device *hdac) +static void hdac_hdmi_skl_enable_all_pins(struct hdac_device *hdev) { unsigned int vendor_param; + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); + unsigned int vendor_nid = hdmi->drv_data->vendor_nid; - vendor_param = snd_hdac_codec_read(hdac, INTEL_VENDOR_NID, 0, + vendor_param = snd_hdac_codec_read(hdev, vendor_nid, 0, INTEL_GET_VENDOR_VERB, 0); if (vendor_param == -1 || vendor_param & INTEL_EN_ALL_PIN_CVTS) return; vendor_param |= INTEL_EN_ALL_PIN_CVTS; - vendor_param = snd_hdac_codec_read(hdac, INTEL_VENDOR_NID, 0, + vendor_param = snd_hdac_codec_read(hdev, vendor_nid, 0, INTEL_SET_VENDOR_VERB, vendor_param); if (vendor_param == -1) return; } -static void hdac_hdmi_skl_enable_dp12(struct hdac_device *hdac) +static void hdac_hdmi_skl_enable_dp12(struct hdac_device *hdev) { unsigned int vendor_param; + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); + unsigned int vendor_nid = hdmi->drv_data->vendor_nid; - vendor_param = snd_hdac_codec_read(hdac, INTEL_VENDOR_NID, 0, + vendor_param = snd_hdac_codec_read(hdev, vendor_nid, 0, INTEL_GET_VENDOR_VERB, 0); if (vendor_param == -1 || vendor_param & INTEL_EN_DP12) return; /* enable DP1.2 mode */ vendor_param |= INTEL_EN_DP12; - vendor_param = snd_hdac_codec_read(hdac, INTEL_VENDOR_NID, 0, + vendor_param = snd_hdac_codec_read(hdev, vendor_nid, 0, INTEL_SET_VENDOR_VERB, vendor_param); if (vendor_param == -1) return; } -static struct snd_soc_dai_ops hdmi_dai_ops = { +static const struct snd_soc_dai_ops hdmi_dai_ops = { .startup = hdac_hdmi_pcm_open, .shutdown = hdac_hdmi_pcm_close, .hw_params = hdac_hdmi_set_hw_params, - .set_tdm_slot = hdac_hdmi_set_tdm_slot, + .set_stream = hdac_hdmi_set_stream, }; /* * Each converter can support a stream independently. So a dai is created * based on the number of converter queried. */ -static int hdac_hdmi_create_dais(struct hdac_device *hdac, +static int hdac_hdmi_create_dais(struct hdac_device *hdev, struct snd_soc_dai_driver **dais, struct hdac_hdmi_priv *hdmi, int num_dais) { @@ -1384,20 +1461,26 @@ static int hdac_hdmi_create_dais(struct hdac_device *hdac, u64 formats; int ret; - hdmi_dais = devm_kzalloc(&hdac->dev, + hdmi_dais = devm_kzalloc(&hdev->dev, (sizeof(*hdmi_dais) * num_dais), GFP_KERNEL); if (!hdmi_dais) return -ENOMEM; list_for_each_entry(cvt, &hdmi->cvt_list, head) { - ret = snd_hdac_query_supported_pcm(hdac, cvt->nid, - &rates, &formats, &bps); + ret = snd_hdac_query_supported_pcm(hdev, cvt->nid, + &rates, &formats, NULL, &bps); if (ret) return ret; + /* Filter out 44.1, 88.2 and 176.4Khz */ + rates &= ~(SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_176400); + if (!rates) + return -EINVAL; + sprintf(dai_name, "intel-hdmi-hifi%d", i+1); - hdmi_dais[i].name = devm_kstrdup(&hdac->dev, + hdmi_dais[i].name = devm_kstrdup(&hdev->dev, dai_name, GFP_KERNEL); if (!hdmi_dais[i].name) @@ -1405,7 +1488,7 @@ static int hdac_hdmi_create_dais(struct hdac_device *hdac, snprintf(name, sizeof(name), "hifi%d", i+1); hdmi_dais[i].playback.stream_name = - devm_kstrdup(&hdac->dev, name, GFP_KERNEL); + devm_kstrdup(&hdev->dev, name, GFP_KERNEL); if (!hdmi_dais[i].playback.stream_name) return -ENOMEM; @@ -1425,6 +1508,7 @@ static int hdac_hdmi_create_dais(struct hdac_device *hdac, } *dais = hdmi_dais; + hdmi->dai_drv = hdmi_dais; return 0; } @@ -1433,33 +1517,29 @@ static int hdac_hdmi_create_dais(struct hdac_device *hdac, * Parse all nodes and store the cvt/pin nids in array * Add one time initialization for pin and cvt widgets */ -static int hdac_hdmi_parse_and_map_nid(struct hdac_ext_device *edev, +static int hdac_hdmi_parse_and_map_nid(struct hdac_device *hdev, struct snd_soc_dai_driver **dais, int *num_dais) { hda_nid_t nid; int i, num_nodes; - struct hdac_device *hdac = &edev->hdac; - struct hdac_hdmi_priv *hdmi = edev->private_data; + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); int ret; - hdac_hdmi_skl_enable_all_pins(hdac); - hdac_hdmi_skl_enable_dp12(hdac); + hdac_hdmi_skl_enable_all_pins(hdev); + hdac_hdmi_skl_enable_dp12(hdev); - num_nodes = snd_hdac_get_sub_nodes(hdac, hdac->afg, &nid); + num_nodes = snd_hdac_get_sub_nodes(hdev, hdev->afg, &nid); if (!nid || num_nodes <= 0) { - dev_warn(&hdac->dev, "HDMI: failed to get afg sub nodes\n"); + dev_warn(&hdev->dev, "HDMI: failed to get afg sub nodes\n"); return -EINVAL; } - hdac->num_nodes = num_nodes; - hdac->start_nid = nid; - - for (i = 0; i < hdac->num_nodes; i++, nid++) { + for (i = 0; i < num_nodes; i++, nid++) { unsigned int caps; unsigned int type; - caps = get_wcaps(hdac, nid); - type = get_wcaps_type(caps); + caps = snd_hdac_get_wcaps(hdev, nid); + type = snd_hdac_get_wcaps_type(caps); if (!(caps & AC_WCAP_DIGITAL)) continue; @@ -1467,49 +1547,58 @@ static int hdac_hdmi_parse_and_map_nid(struct hdac_ext_device *edev, switch (type) { case AC_WID_AUD_OUT: - ret = hdac_hdmi_add_cvt(edev, nid); + ret = hdac_hdmi_add_cvt(hdev, nid); if (ret < 0) return ret; break; case AC_WID_PIN: - ret = hdac_hdmi_add_pin(edev, nid); + ret = hdac_hdmi_add_pin(hdev, nid); if (ret < 0) return ret; break; } } - hdac->end_nid = nid; - - if (!hdmi->num_pin || !hdmi->num_cvt) - return -EIO; + if (!hdmi->num_pin || !hdmi->num_cvt) { + ret = -EIO; + dev_err(&hdev->dev, "Bad pin/cvt setup in %s\n", __func__); + return ret; + } - ret = hdac_hdmi_create_dais(hdac, dais, hdmi, hdmi->num_cvt); + ret = hdac_hdmi_create_dais(hdev, dais, hdmi, hdmi->num_cvt); if (ret) { - dev_err(&hdac->dev, "Failed to create dais with err: %d\n", - ret); + dev_err(&hdev->dev, "Failed to create dais with err: %d\n", + ret); return ret; } *num_dais = hdmi->num_cvt; + ret = hdac_hdmi_init_dai_map(hdev); + if (ret < 0) + dev_err(&hdev->dev, "Failed to init DAI map with err: %d\n", + ret); + return ret; +} - return hdac_hdmi_init_dai_map(edev); +static int hdac_hdmi_pin2port(void *aptr, int pin) +{ + return pin - 4; /* map NID 0x05 -> port #1 */ } static void hdac_hdmi_eld_notify_cb(void *aptr, int port, int pipe) { - struct hdac_ext_device *edev = aptr; - struct hdac_hdmi_priv *hdmi = edev->private_data; - struct hdac_hdmi_pin *pin = NULL; + struct hdac_device *hdev = aptr; + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); + struct hdac_hdmi_pin *pin; struct hdac_hdmi_port *hport = NULL; - struct snd_soc_codec *codec = edev->scodec; + struct snd_soc_component *component = hdmi->component; int i; /* Don't know how this mapping is derived */ hda_nid_t pin_nid = port + 0x04; - dev_dbg(&edev->hdac.dev, "%s: for pin:%d port=%d\n", __func__, + dev_dbg(&hdev->dev, "%s: for pin:%d port=%d\n", __func__, pin_nid, pipe); /* @@ -1518,11 +1607,11 @@ static void hdac_hdmi_eld_notify_cb(void *aptr, int port, int pipe) * connection states are updated in anyway at the end of the resume, * we can skip it when received during PM process. */ - if (snd_power_get_state(codec->component.card->snd_card) != + if (snd_power_get_state(component->card->snd_card) != SNDRV_CTL_POWER_D0) return; - if (atomic_read(&edev->hdac.in_pm)) + if (atomic_read(&hdev->in_pm)) return; list_for_each_entry(pin, &hdmi->pin_list, head) { @@ -1550,184 +1639,12 @@ static void hdac_hdmi_eld_notify_cb(void *aptr, int port, int pipe) } -static struct i915_audio_component_audio_ops aops = { +static struct drm_audio_component_audio_ops aops = { + .pin2port = hdac_hdmi_pin2port, .pin_eld_notify = hdac_hdmi_eld_notify_cb, }; -static struct snd_pcm *hdac_hdmi_get_pcm_from_id(struct snd_soc_card *card, - int device) -{ - struct snd_soc_pcm_runtime *rtd; - - list_for_each_entry(rtd, &card->rtd_list, list) { - if (rtd->pcm && (rtd->pcm->device == device)) - return rtd->pcm; - } - - return NULL; -} - -/* create jack pin kcontrols */ -static int create_fill_jack_kcontrols(struct snd_soc_card *card, - struct hdac_ext_device *edev) -{ - struct hdac_hdmi_pin *pin; - struct snd_kcontrol_new *kc; - char kc_name[NAME_SIZE], xname[NAME_SIZE]; - char *name; - int i = 0, j; - struct snd_soc_codec *codec = edev->scodec; - struct hdac_hdmi_priv *hdmi = edev->private_data; - - kc = devm_kcalloc(codec->dev, hdmi->num_ports, - sizeof(*kc), GFP_KERNEL); - - if (!kc) - return -ENOMEM; - - list_for_each_entry(pin, &hdmi->pin_list, head) { - for (j = 0; j < pin->num_ports; j++) { - snprintf(xname, sizeof(xname), "hif%d-%d Jack", - pin->nid, pin->ports[j].id); - name = devm_kstrdup(codec->dev, xname, GFP_KERNEL); - if (!name) - return -ENOMEM; - snprintf(kc_name, sizeof(kc_name), "%s Switch", xname); - kc[i].name = devm_kstrdup(codec->dev, kc_name, - GFP_KERNEL); - if (!kc[i].name) - return -ENOMEM; - - kc[i].private_value = (unsigned long)name; - kc[i].iface = SNDRV_CTL_ELEM_IFACE_MIXER; - kc[i].access = 0; - kc[i].info = snd_soc_dapm_info_pin_switch; - kc[i].put = snd_soc_dapm_put_pin_switch; - kc[i].get = snd_soc_dapm_get_pin_switch; - i++; - } - } - - return snd_soc_add_card_controls(card, kc, i); -} - -int hdac_hdmi_jack_port_init(struct snd_soc_codec *codec, - struct snd_soc_dapm_context *dapm) -{ - struct hdac_ext_device *edev = snd_soc_codec_get_drvdata(codec); - struct hdac_hdmi_priv *hdmi = edev->private_data; - struct hdac_hdmi_pin *pin; - struct snd_soc_dapm_widget *widgets; - struct snd_soc_dapm_route *route; - char w_name[NAME_SIZE]; - int i = 0, j, ret; - - widgets = devm_kcalloc(dapm->dev, hdmi->num_ports, - sizeof(*widgets), GFP_KERNEL); - - if (!widgets) - return -ENOMEM; - - route = devm_kcalloc(dapm->dev, hdmi->num_ports, - sizeof(*route), GFP_KERNEL); - if (!route) - return -ENOMEM; - - /* create Jack DAPM widget */ - list_for_each_entry(pin, &hdmi->pin_list, head) { - for (j = 0; j < pin->num_ports; j++) { - snprintf(w_name, sizeof(w_name), "hif%d-%d Jack", - pin->nid, pin->ports[j].id); - - ret = hdac_hdmi_fill_widget_info(dapm->dev, &widgets[i], - snd_soc_dapm_spk, NULL, - w_name, NULL, NULL, 0, NULL, 0); - if (ret < 0) - return ret; - - pin->ports[j].jack_pin = widgets[i].name; - pin->ports[j].dapm = dapm; - - /* add to route from Jack widget to output */ - hdac_hdmi_fill_route(&route[i], pin->ports[j].jack_pin, - NULL, pin->ports[j].output_pin, NULL); - - i++; - } - } - - /* Add Route from Jack widget to the output widget */ - ret = snd_soc_dapm_new_controls(dapm, widgets, hdmi->num_ports); - if (ret < 0) - return ret; - - ret = snd_soc_dapm_add_routes(dapm, route, hdmi->num_ports); - if (ret < 0) - return ret; - - ret = snd_soc_dapm_new_widgets(dapm->card); - if (ret < 0) - return ret; - - /* Add Jack Pin switch Kcontrol */ - ret = create_fill_jack_kcontrols(dapm->card, edev); - - if (ret < 0) - return ret; - - /* default set the Jack Pin switch to OFF */ - list_for_each_entry(pin, &hdmi->pin_list, head) { - for (j = 0; j < pin->num_ports; j++) - snd_soc_dapm_disable_pin(pin->ports[j].dapm, - pin->ports[j].jack_pin); - } - - return 0; -} -EXPORT_SYMBOL_GPL(hdac_hdmi_jack_port_init); - -int hdac_hdmi_jack_init(struct snd_soc_dai *dai, int device, - struct snd_soc_jack *jack) -{ - struct snd_soc_codec *codec = dai->codec; - struct hdac_ext_device *edev = snd_soc_codec_get_drvdata(codec); - struct hdac_hdmi_priv *hdmi = edev->private_data; - struct hdac_hdmi_pcm *pcm; - struct snd_pcm *snd_pcm; - int err; - - /* - * this is a new PCM device, create new pcm and - * add to the pcm list - */ - pcm = kzalloc(sizeof(*pcm), GFP_KERNEL); - if (!pcm) - return -ENOMEM; - pcm->pcm_id = device; - pcm->cvt = hdmi->dai_map[dai->id].cvt; - pcm->jack_event = 0; - pcm->jack = jack; - mutex_init(&pcm->lock); - INIT_LIST_HEAD(&pcm->port_list); - snd_pcm = hdac_hdmi_get_pcm_from_id(dai->component->card, device); - if (snd_pcm) { - err = snd_hdac_add_chmap_ctls(snd_pcm, device, &hdmi->chmap); - if (err < 0) { - dev_err(&edev->hdac.dev, - "chmap control add failed with err: %d for pcm: %d\n", - err, device); - kfree(pcm); - return err; - } - } - - list_add_tail(&pcm->head, &hdmi->pcm_list); - - return 0; -} -EXPORT_SYMBOL_GPL(hdac_hdmi_jack_init); - -static void hdac_hdmi_present_sense_all_pins(struct hdac_ext_device *edev, +static void hdac_hdmi_present_sense_all_pins(struct hdac_device *hdev, struct hdac_hdmi_priv *hdmi, bool detect_pin_caps) { int i; @@ -1736,7 +1653,7 @@ static void hdac_hdmi_present_sense_all_pins(struct hdac_ext_device *edev, list_for_each_entry(pin, &hdmi->pin_list, head) { if (detect_pin_caps) { - if (hdac_hdmi_get_port_len(edev, pin->nid) == 0) + if (hdac_hdmi_get_port_len(hdev, pin->nid) == 0) pin->mst_capable = false; else pin->mst_capable = true; @@ -1751,137 +1668,128 @@ static void hdac_hdmi_present_sense_all_pins(struct hdac_ext_device *edev, } } -static int hdmi_codec_probe(struct snd_soc_codec *codec) +static int hdmi_codec_probe(struct snd_soc_component *component) { - struct hdac_ext_device *edev = snd_soc_codec_get_drvdata(codec); - struct hdac_hdmi_priv *hdmi = edev->private_data; - struct snd_soc_dapm_context *dapm = - snd_soc_component_get_dapm(&codec->component); - struct hdac_ext_link *hlink = NULL; + struct hdac_hdmi_priv *hdmi = snd_soc_component_get_drvdata(component); + struct hdac_device *hdev = hdmi->hdev; + struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component); + struct hdac_ext_link *hlink; int ret; - edev->scodec = codec; + hdmi->component = component; /* * hold the ref while we probe, also no need to drop the ref on * exit, we call pm_runtime_suspend() so that will do for us */ - hlink = snd_hdac_ext_bus_get_link(edev->ebus, dev_name(&edev->hdac.dev)); + hlink = snd_hdac_ext_bus_get_hlink_by_name(hdev->bus, dev_name(&hdev->dev)); if (!hlink) { - dev_err(&edev->hdac.dev, "hdac link not found\n"); + dev_err(&hdev->dev, "hdac link not found\n"); return -EIO; } - snd_hdac_ext_bus_link_get(edev->ebus, hlink); + snd_hdac_ext_bus_link_get(hdev->bus, hlink); ret = create_fill_widget_route_map(dapm); if (ret < 0) return ret; - aops.audio_ptr = edev; - ret = snd_hdac_i915_register_notifier(&aops); + aops.audio_ptr = hdev; + ret = snd_hdac_acomp_register_notifier(hdev->bus, &aops); if (ret < 0) { - dev_err(&edev->hdac.dev, "notifier register failed: err: %d\n", - ret); + dev_err(&hdev->dev, "notifier register failed: err: %d\n", ret); return ret; } - hdac_hdmi_present_sense_all_pins(edev, hdmi, true); + hdac_hdmi_present_sense_all_pins(hdev, hdmi, true); /* Imp: Store the card pointer in hda_codec */ - edev->card = dapm->card->snd_card; + hdmi->card = component->card->snd_card; /* + * Setup a device_link between card device and HDMI codec device. + * The card device is the consumer and the HDMI codec device is + * the supplier. With this setting, we can make sure that the audio + * domain in display power will be always turned on before operating + * on the HDMI audio codec registers. + * Let's use the flag DL_FLAG_AUTOREMOVE_CONSUMER. This can make + * sure the device link is freed when the machine driver is removed. + */ + device_link_add(component->card->dev, &hdev->dev, DL_FLAG_RPM_ACTIVE | + DL_FLAG_AUTOREMOVE_CONSUMER); + /* * hdac_device core already sets the state to active and calls * get_noresume. So enable runtime and set the device to suspend. */ - pm_runtime_enable(&edev->hdac.dev); - pm_runtime_put(&edev->hdac.dev); - pm_runtime_suspend(&edev->hdac.dev); - - return 0; -} - -static int hdmi_codec_remove(struct snd_soc_codec *codec) -{ - struct hdac_ext_device *edev = snd_soc_codec_get_drvdata(codec); + pm_runtime_enable(&hdev->dev); + pm_runtime_put(&hdev->dev); + pm_runtime_suspend(&hdev->dev); - pm_runtime_disable(&edev->hdac.dev); return 0; } -#ifdef CONFIG_PM -static int hdmi_codec_prepare(struct device *dev) +static void hdmi_codec_remove(struct snd_soc_component *component) { - struct hdac_ext_device *edev = to_hda_ext_device(dev); - struct hdac_device *hdac = &edev->hdac; - - pm_runtime_get_sync(&edev->hdac.dev); + struct hdac_hdmi_priv *hdmi = snd_soc_component_get_drvdata(component); + struct hdac_device *hdev = hdmi->hdev; + int ret; - /* - * Power down afg. - * codec_read is preferred over codec_write to set the power state. - * This way verb is send to set the power state and response - * is received. So setting power state is ensured without using loop - * to read the state. - */ - snd_hdac_codec_read(hdac, hdac->afg, 0, AC_VERB_SET_POWER_STATE, - AC_PWRST_D3); + ret = snd_hdac_acomp_register_notifier(hdev->bus, NULL); + if (ret < 0) + dev_err(&hdev->dev, "notifier unregister failed: err: %d\n", + ret); - return 0; + pm_runtime_disable(&hdev->dev); } -static void hdmi_codec_complete(struct device *dev) +static int hdmi_codec_resume(struct device *dev) { - struct hdac_ext_device *edev = to_hda_ext_device(dev); - struct hdac_hdmi_priv *hdmi = edev->private_data; - struct hdac_device *hdac = &edev->hdac; - - /* Power up afg */ - snd_hdac_codec_read(hdac, hdac->afg, 0, AC_VERB_SET_POWER_STATE, - AC_PWRST_D0); - - hdac_hdmi_skl_enable_all_pins(&edev->hdac); - hdac_hdmi_skl_enable_dp12(&edev->hdac); + struct hdac_device *hdev = dev_to_hdac_dev(dev); + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); + int ret; + ret = pm_runtime_force_resume(dev); + if (ret < 0) + return ret; /* * As the ELD notify callback request is not entertained while the * device is in suspend state. Need to manually check detection of * all pins here. pin capablity change is not support, so use the * already set pin caps. + * + * NOTE: this is safe to call even if the codec doesn't actually resume. + * The pin check involves only with DRM audio component hooks, so it + * works even if the HD-audio side is still dreaming peacefully. */ - hdac_hdmi_present_sense_all_pins(edev, hdmi, false); - - pm_runtime_put_sync(&edev->hdac.dev); + hdac_hdmi_present_sense_all_pins(hdev, hdmi, false); + return 0; } -#else -#define hdmi_codec_prepare NULL -#define hdmi_codec_complete NULL -#endif - -static struct snd_soc_codec_driver hdmi_hda_codec = { - .probe = hdmi_codec_probe, - .remove = hdmi_codec_remove, - .idle_bias_off = true, + +static const struct snd_soc_component_driver hdmi_hda_codec = { + .probe = hdmi_codec_probe, + .remove = hdmi_codec_remove, + .use_pmdown_time = 1, + .endianness = 1, }; -static void hdac_hdmi_get_chmap(struct hdac_device *hdac, int pcm_idx, +static void hdac_hdmi_get_chmap(struct hdac_device *hdev, int pcm_idx, unsigned char *chmap) { - struct hdac_ext_device *edev = to_ehdac_device(hdac); - struct hdac_hdmi_priv *hdmi = edev->private_data; + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); struct hdac_hdmi_pcm *pcm = get_hdmi_pcm_from_id(hdmi, pcm_idx); memcpy(chmap, pcm->chmap, ARRAY_SIZE(pcm->chmap)); } -static void hdac_hdmi_set_chmap(struct hdac_device *hdac, int pcm_idx, +static void hdac_hdmi_set_chmap(struct hdac_device *hdev, int pcm_idx, unsigned char *chmap, int prepared) { - struct hdac_ext_device *edev = to_ehdac_device(hdac); - struct hdac_hdmi_priv *hdmi = edev->private_data; + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); struct hdac_hdmi_pcm *pcm = get_hdmi_pcm_from_id(hdmi, pcm_idx); struct hdac_hdmi_port *port; + if (!pcm) + return; + if (list_empty(&pcm->port_list)) return; @@ -1890,73 +1798,92 @@ static void hdac_hdmi_set_chmap(struct hdac_device *hdac, int pcm_idx, memcpy(pcm->chmap, chmap, ARRAY_SIZE(pcm->chmap)); list_for_each_entry(port, &pcm->port_list, head) if (prepared) - hdac_hdmi_setup_audio_infoframe(edev, pcm, port); + hdac_hdmi_setup_audio_infoframe(hdev, pcm, port); mutex_unlock(&pcm->lock); } -static bool is_hdac_hdmi_pcm_attached(struct hdac_device *hdac, int pcm_idx) +static bool is_hdac_hdmi_pcm_attached(struct hdac_device *hdev, int pcm_idx) { - struct hdac_ext_device *edev = to_ehdac_device(hdac); - struct hdac_hdmi_priv *hdmi = edev->private_data; + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); struct hdac_hdmi_pcm *pcm = get_hdmi_pcm_from_id(hdmi, pcm_idx); + if (!pcm) + return false; + if (list_empty(&pcm->port_list)) return false; return true; } -static int hdac_hdmi_get_spk_alloc(struct hdac_device *hdac, int pcm_idx) +static int hdac_hdmi_get_spk_alloc(struct hdac_device *hdev, int pcm_idx) { - struct hdac_ext_device *edev = to_ehdac_device(hdac); - struct hdac_hdmi_priv *hdmi = edev->private_data; + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); struct hdac_hdmi_pcm *pcm = get_hdmi_pcm_from_id(hdmi, pcm_idx); struct hdac_hdmi_port *port; + if (!pcm) + return 0; + if (list_empty(&pcm->port_list)) return 0; port = list_first_entry(&pcm->port_list, struct hdac_hdmi_port, head); - if (!port) - return 0; - if (!port || !port->eld.eld_valid) return 0; return port->eld.info.spk_alloc; } -static int hdac_hdmi_dev_probe(struct hdac_ext_device *edev) +static struct hdac_hdmi_drv_data intel_glk_drv_data = { + .vendor_nid = INTEL_GLK_VENDOR_NID, +}; + +static struct hdac_hdmi_drv_data intel_drv_data = { + .vendor_nid = INTEL_VENDOR_NID, +}; + +static int hdac_hdmi_dev_probe(struct hdac_device *hdev) { - struct hdac_device *codec = &edev->hdac; struct hdac_hdmi_priv *hdmi_priv; struct snd_soc_dai_driver *hdmi_dais = NULL; - struct hdac_ext_link *hlink = NULL; + struct hdac_ext_link *hlink; int num_dais = 0; - int ret = 0; + int ret; + struct hdac_driver *hdrv = drv_to_hdac_driver(hdev->dev.driver); + const struct hda_device_id *hdac_id = hdac_get_device_id(hdev, hdrv); /* hold the ref while we probe */ - hlink = snd_hdac_ext_bus_get_link(edev->ebus, dev_name(&edev->hdac.dev)); + hlink = snd_hdac_ext_bus_get_hlink_by_name(hdev->bus, dev_name(&hdev->dev)); if (!hlink) { - dev_err(&edev->hdac.dev, "hdac link not found\n"); + dev_err(&hdev->dev, "hdac link not found\n"); return -EIO; } - snd_hdac_ext_bus_link_get(edev->ebus, hlink); + snd_hdac_ext_bus_link_get(hdev->bus, hlink); - hdmi_priv = devm_kzalloc(&codec->dev, sizeof(*hdmi_priv), GFP_KERNEL); + hdmi_priv = devm_kzalloc(&hdev->dev, sizeof(*hdmi_priv), GFP_KERNEL); if (hdmi_priv == NULL) return -ENOMEM; - edev->private_data = hdmi_priv; - snd_hdac_register_chmap_ops(codec, &hdmi_priv->chmap); + snd_hdac_register_chmap_ops(hdev, &hdmi_priv->chmap); hdmi_priv->chmap.ops.get_chmap = hdac_hdmi_get_chmap; hdmi_priv->chmap.ops.set_chmap = hdac_hdmi_set_chmap; hdmi_priv->chmap.ops.is_pcm_attached = is_hdac_hdmi_pcm_attached; hdmi_priv->chmap.ops.get_spk_alloc = hdac_hdmi_get_spk_alloc; + hdmi_priv->hdev = hdev; + + if (!hdac_id) + return -ENODEV; + + if (hdac_id->driver_data) + hdmi_priv->drv_data = + (struct hdac_hdmi_drv_data *)hdac_id->driver_data; + else + hdmi_priv->drv_data = &intel_drv_data; - dev_set_drvdata(&codec->dev, edev); + dev_set_drvdata(&hdev->dev, hdmi_priv); INIT_LIST_HEAD(&hdmi_priv->pin_list); INIT_LIST_HEAD(&hdmi_priv->cvt_list); @@ -1967,80 +1894,49 @@ static int hdac_hdmi_dev_probe(struct hdac_ext_device *edev) * Turned off in the runtime_suspend during the first explicit * pm_runtime_suspend call. */ - ret = snd_hdac_display_power(edev->hdac.bus, true); - if (ret < 0) { - dev_err(&edev->hdac.dev, - "Cannot turn on display power on i915 err: %d\n", - ret); - return ret; - } + snd_hdac_display_power(hdev->bus, hdev->addr, true); - ret = hdac_hdmi_parse_and_map_nid(edev, &hdmi_dais, &num_dais); + ret = hdac_hdmi_parse_and_map_nid(hdev, &hdmi_dais, &num_dais); if (ret < 0) { - dev_err(&codec->dev, + dev_err(&hdev->dev, "Failed in parse and map nid with err: %d\n", ret); return ret; } + snd_hdac_refresh_widgets(hdev); /* ASoC specific initialization */ - ret = snd_soc_register_codec(&codec->dev, &hdmi_hda_codec, + ret = devm_snd_soc_register_component(&hdev->dev, &hdmi_hda_codec, hdmi_dais, num_dais); - snd_hdac_ext_bus_link_put(edev->ebus, hlink); + snd_hdac_ext_bus_link_put(hdev->bus, hlink); return ret; } -static int hdac_hdmi_dev_remove(struct hdac_ext_device *edev) +static void clear_dapm_works(struct hdac_device *hdev) { - struct hdac_hdmi_priv *hdmi = edev->private_data; - struct hdac_hdmi_pin *pin, *pin_next; - struct hdac_hdmi_cvt *cvt, *cvt_next; - struct hdac_hdmi_pcm *pcm, *pcm_next; - struct hdac_hdmi_port *port, *port_next; + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); + struct hdac_hdmi_pin *pin; int i; - snd_soc_unregister_codec(&edev->hdac.dev); - - list_for_each_entry_safe(pcm, pcm_next, &hdmi->pcm_list, head) { - pcm->cvt = NULL; - if (list_empty(&pcm->port_list)) - continue; - - list_for_each_entry_safe(port, port_next, - &pcm->port_list, head) - list_del(&port->head); - - list_del(&pcm->head); - kfree(pcm); - } - - list_for_each_entry_safe(cvt, cvt_next, &hdmi->cvt_list, head) { - list_del(&cvt->head); - kfree(cvt->name); - kfree(cvt); - } - - list_for_each_entry_safe(pin, pin_next, &hdmi->pin_list, head) { + list_for_each_entry(pin, &hdmi->pin_list, head) for (i = 0; i < pin->num_ports; i++) - pin->ports[i].pin = NULL; - kfree(pin->ports); - list_del(&pin->head); - kfree(pin); - } + cancel_work_sync(&pin->ports[i].dapm_work); +} + +static int hdac_hdmi_dev_remove(struct hdac_device *hdev) +{ + clear_dapm_works(hdev); + snd_hdac_display_power(hdev->bus, hdev->addr, false); return 0; } -#ifdef CONFIG_PM static int hdac_hdmi_runtime_suspend(struct device *dev) { - struct hdac_ext_device *edev = to_hda_ext_device(dev); - struct hdac_device *hdac = &edev->hdac; - struct hdac_bus *bus = hdac->bus; - struct hdac_ext_bus *ebus = hbus_to_ebus(bus); - struct hdac_ext_link *hlink = NULL; - int err; + struct hdac_device *hdev = dev_to_hdac_dev(dev); + struct hdac_bus *bus = hdev->bus; + struct hdac_ext_link *hlink; dev_dbg(dev, "Enter: %s\n", __func__); @@ -2055,33 +1951,28 @@ static int hdac_hdmi_runtime_suspend(struct device *dev) * is received. So setting power state is ensured without using loop * to read the state. */ - snd_hdac_codec_read(hdac, hdac->afg, 0, AC_VERB_SET_POWER_STATE, + snd_hdac_codec_read(hdev, hdev->afg, 0, AC_VERB_SET_POWER_STATE, AC_PWRST_D3); - err = snd_hdac_display_power(bus, false); - if (err < 0) { - dev_err(bus->dev, "Cannot turn on display power on i915\n"); - return err; - } - hlink = snd_hdac_ext_bus_get_link(ebus, dev_name(dev)); + hlink = snd_hdac_ext_bus_get_hlink_by_name(bus, dev_name(dev)); if (!hlink) { dev_err(dev, "hdac link not found\n"); return -EIO; } - snd_hdac_ext_bus_link_put(ebus, hlink); + snd_hdac_codec_link_down(hdev); + snd_hdac_ext_bus_link_put(bus, hlink); + + snd_hdac_display_power(bus, hdev->addr, false); return 0; } static int hdac_hdmi_runtime_resume(struct device *dev) { - struct hdac_ext_device *edev = to_hda_ext_device(dev); - struct hdac_device *hdac = &edev->hdac; - struct hdac_bus *bus = hdac->bus; - struct hdac_ext_bus *ebus = hbus_to_ebus(bus); - struct hdac_ext_link *hlink = NULL; - int err; + struct hdac_device *hdev = dev_to_hdac_dev(dev); + struct hdac_bus *bus = hdev->bus; + struct hdac_ext_link *hlink; dev_dbg(dev, "Enter: %s\n", __func__); @@ -2089,58 +1980,51 @@ static int hdac_hdmi_runtime_resume(struct device *dev) if (!bus) return 0; - hlink = snd_hdac_ext_bus_get_link(ebus, dev_name(dev)); + hlink = snd_hdac_ext_bus_get_hlink_by_name(bus, dev_name(dev)); if (!hlink) { dev_err(dev, "hdac link not found\n"); return -EIO; } - snd_hdac_ext_bus_link_get(ebus, hlink); + snd_hdac_ext_bus_link_get(bus, hlink); + snd_hdac_codec_link_up(hdev); - err = snd_hdac_display_power(bus, true); - if (err < 0) { - dev_err(bus->dev, "Cannot turn on display power on i915\n"); - return err; - } + snd_hdac_display_power(bus, hdev->addr, true); - hdac_hdmi_skl_enable_all_pins(&edev->hdac); - hdac_hdmi_skl_enable_dp12(&edev->hdac); + hdac_hdmi_skl_enable_all_pins(hdev); + hdac_hdmi_skl_enable_dp12(hdev); /* Power up afg */ - snd_hdac_codec_read(hdac, hdac->afg, 0, AC_VERB_SET_POWER_STATE, + snd_hdac_codec_read(hdev, hdev->afg, 0, AC_VERB_SET_POWER_STATE, AC_PWRST_D0); return 0; } -#else -#define hdac_hdmi_runtime_suspend NULL -#define hdac_hdmi_runtime_resume NULL -#endif static const struct dev_pm_ops hdac_hdmi_pm = { - SET_RUNTIME_PM_OPS(hdac_hdmi_runtime_suspend, hdac_hdmi_runtime_resume, NULL) - .prepare = hdmi_codec_prepare, - .complete = hdmi_codec_complete, + RUNTIME_PM_OPS(hdac_hdmi_runtime_suspend, hdac_hdmi_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, hdmi_codec_resume) }; static const struct hda_device_id hdmi_list[] = { HDA_CODEC_EXT_ENTRY(0x80862809, 0x100000, "Skylake HDMI", 0), HDA_CODEC_EXT_ENTRY(0x8086280a, 0x100000, "Broxton HDMI", 0), HDA_CODEC_EXT_ENTRY(0x8086280b, 0x100000, "Kabylake HDMI", 0), - HDA_CODEC_EXT_ENTRY(0x8086280d, 0x100000, "Geminilake HDMI", 0), + HDA_CODEC_EXT_ENTRY(0x8086280c, 0x100000, "Cannonlake HDMI", + &intel_glk_drv_data), + HDA_CODEC_EXT_ENTRY(0x8086280d, 0x100000, "Geminilake HDMI", + &intel_glk_drv_data), {} }; MODULE_DEVICE_TABLE(hdaudio, hdmi_list); -static struct hdac_ext_driver hdmi_driver = { - . hdac = { - .driver = { - .name = "HDMI HDA Codec", - .pm = &hdac_hdmi_pm, - }, - .id_table = hdmi_list, +static struct hdac_driver hdmi_driver = { + .driver = { + .name = "HDMI HDA Codec", + .pm = pm_ptr(&hdac_hdmi_pm), }, + .id_table = hdmi_list, .probe = hdac_hdmi_dev_probe, .remove = hdac_hdmi_dev_remove, }; |
