diff options
Diffstat (limited to 'sound/soc/codecs/hdac_hdmi.c')
-rw-r--r-- | sound/soc/codecs/hdac_hdmi.c | 204 |
1 files changed, 173 insertions, 31 deletions
diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c index f26b77faed59..e1a7f0b0c0f3 100644 --- a/sound/soc/codecs/hdac_hdmi.c +++ b/sound/soc/codecs/hdac_hdmi.c @@ -9,12 +9,14 @@ * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + #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> @@ -107,6 +109,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 { @@ -434,23 +437,28 @@ static int hdac_hdmi_setup_audio_infoframe(struct hdac_device *hdev, 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_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; - dev_dbg(&hdev->dev, "%s: strm_tag: %d\n", __func__, tx_mask); + hstream = (struct hdac_stream *)stream; + + 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; } @@ -461,13 +469,14 @@ static int hdac_hdmi_set_hw_params(struct snd_pcm_substream *substream, struct hdac_hdmi_priv *hdmi = snd_soc_dai_get_drvdata(dai); struct hdac_hdmi_dai_port_map *dai_map; struct hdac_hdmi_pcm *pcm; + unsigned int bits; int format; dai_map = &hdmi->dai_map[dai->id]; - 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) @@ -521,7 +530,7 @@ static struct hdac_hdmi_port *hdac_hdmi_get_port_from_cvt( 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) { @@ -663,6 +672,7 @@ hdac_hdmi_query_cvt_params(struct hdac_device *hdev, struct hdac_hdmi_cvt *cvt) err = snd_hdac_query_supported_pcm(hdev, cvt->nid, &cvt->params.rates, &cvt->params.formats, + NULL, &cvt->params.maxbps); if (err < 0) dev_err(&hdev->dev, @@ -711,7 +721,7 @@ static struct hdac_hdmi_pcm *hdac_hdmi_get_pcm(struct hdac_device *hdev, struct hdac_hdmi_port *port) { struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); - struct hdac_hdmi_pcm *pcm = NULL; + struct hdac_hdmi_pcm *pcm; struct hdac_hdmi_port *p; list_for_each_entry(pcm, &hdmi->pcm_list, head) { @@ -898,7 +908,7 @@ static int hdac_hdmi_set_pin_port_mux(struct snd_kcontrol *kcontrol, struct hdac_hdmi_port *port = w->priv; struct hdac_device *hdev = dev_to_hdac_dev(dapm->dev); struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); - struct hdac_hdmi_pcm *pcm = NULL; + 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); @@ -1248,6 +1258,7 @@ static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin, struct hdac_hdmi_pcm *pcm; int size = 0; int port_id = -1; + bool eld_valid, eld_changed; if (!hdmi) return; @@ -1273,6 +1284,8 @@ static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin, size = -EINVAL; } + eld_valid = port->eld.eld_valid; + if (size > 0) { port->eld.eld_valid = true; port->eld.eld_size = size; @@ -1281,6 +1294,8 @@ static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin, port->eld.eld_size = 0; } + eld_changed = (eld_valid != port->eld.eld_valid); + pcm = hdac_hdmi_get_pcm(hdev, port); if (!port->eld.monitor_present || !port->eld.eld_valid) { @@ -1313,6 +1328,12 @@ static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin, } 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_device *hdev, @@ -1411,11 +1432,127 @@ static void hdac_hdmi_skl_enable_dp12(struct hdac_device *hdev) } +static int hdac_hdmi_eld_ctl_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct hdac_hdmi_priv *hdmi = snd_soc_component_get_drvdata(component); + struct hdac_hdmi_pcm *pcm; + struct hdac_hdmi_port *port; + struct hdac_hdmi_eld *eld; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; + uinfo->count = 0; + + pcm = get_hdmi_pcm_from_id(hdmi, kcontrol->id.device); + if (!pcm) { + dev_dbg(component->dev, "%s: no pcm, device %d\n", __func__, + kcontrol->id.device); + return 0; + } + + if (list_empty(&pcm->port_list)) { + dev_dbg(component->dev, "%s: empty port list, device %d\n", + __func__, kcontrol->id.device); + return 0; + } + + mutex_lock(&hdmi->pin_mutex); + + list_for_each_entry(port, &pcm->port_list, head) { + eld = &port->eld; + + if (eld->eld_valid) { + uinfo->count = eld->eld_size; + break; + } + } + + mutex_unlock(&hdmi->pin_mutex); + + return 0; +} + +static int hdac_hdmi_eld_ctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct hdac_hdmi_priv *hdmi = snd_soc_component_get_drvdata(component); + struct hdac_hdmi_pcm *pcm; + struct hdac_hdmi_port *port; + struct hdac_hdmi_eld *eld; + + memset(ucontrol->value.bytes.data, 0, sizeof(ucontrol->value.bytes.data)); + + pcm = get_hdmi_pcm_from_id(hdmi, kcontrol->id.device); + if (!pcm) { + dev_dbg(component->dev, "%s: no pcm, device %d\n", __func__, + kcontrol->id.device); + return 0; + } + + if (list_empty(&pcm->port_list)) { + dev_dbg(component->dev, "%s: empty port list, device %d\n", + __func__, kcontrol->id.device); + return 0; + } + + mutex_lock(&hdmi->pin_mutex); + + list_for_each_entry(port, &pcm->port_list, head) { + eld = &port->eld; + + if (!eld->eld_valid) + continue; + + if (eld->eld_size > ARRAY_SIZE(ucontrol->value.bytes.data) || + eld->eld_size > ELD_MAX_SIZE) { + mutex_unlock(&hdmi->pin_mutex); + + dev_err(component->dev, "%s: buffer too small, device %d eld_size %d\n", + __func__, kcontrol->id.device, eld->eld_size); + snd_BUG(); + return -EINVAL; + } + + memcpy(ucontrol->value.bytes.data, eld->eld_buffer, + eld->eld_size); + break; + } + + mutex_unlock(&hdmi->pin_mutex); + + return 0; +} + +static int hdac_hdmi_create_eld_ctl(struct snd_soc_component *component, struct hdac_hdmi_pcm *pcm) +{ + struct snd_kcontrol *kctl; + struct snd_kcontrol_new hdmi_eld_ctl = { + .access = SNDRV_CTL_ELEM_ACCESS_READ | + SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "ELD", + .info = hdac_hdmi_eld_ctl_info, + .get = hdac_hdmi_eld_ctl_get, + .device = pcm->pcm_id, + }; + + /* add ELD ctl with the device number corresponding to the PCM stream */ + kctl = snd_ctl_new1(&hdmi_eld_ctl, component); + if (!kctl) + return -ENOMEM; + + pcm->eld_ctl = kctl; + + return snd_ctl_add(component->card->snd_card, kctl); +} + 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, }; /* @@ -1443,7 +1580,7 @@ static int hdac_hdmi_create_dais(struct hdac_device *hdev, list_for_each_entry(cvt, &hdmi->cvt_list, head) { ret = snd_hdac_query_supported_pcm(hdev, cvt->nid, - &rates, &formats, &bps); + &rates, &formats, NULL, &bps); if (ret) return ret; @@ -1564,7 +1701,7 @@ static void hdac_hdmi_eld_notify_cb(void *aptr, int port, int pipe) { struct hdac_device *hdev = aptr; struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); - struct hdac_hdmi_pin *pin = NULL; + struct hdac_hdmi_pin *pin; struct hdac_hdmi_port *hport = NULL; struct snd_soc_component *component = hdmi->component; int i; @@ -1637,7 +1774,6 @@ static int create_fill_jack_kcontrols(struct snd_soc_card *card, { 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 hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); @@ -1651,14 +1787,14 @@ static int create_fill_jack_kcontrols(struct snd_soc_card *card, 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(component->dev, xname, GFP_KERNEL); + name = devm_kasprintf(component->dev, GFP_KERNEL, + "hif%d-%d Jack", + pin->nid, pin->ports[j].id); if (!name) return -ENOMEM; - snprintf(kc_name, sizeof(kc_name), "%s Switch", xname); - kc[i].name = devm_kstrdup(component->dev, kc_name, - GFP_KERNEL); + + kc[i].name = devm_kasprintf(component->dev, GFP_KERNEL, + "%s Switch", name); if (!kc[i].name) return -ENOMEM; @@ -1784,6 +1920,15 @@ int hdac_hdmi_jack_init(struct snd_soc_dai *dai, int device, } } + /* add control for ELD Bytes */ + err = hdac_hdmi_create_eld_ctl(component, pcm); + if (err < 0) { + dev_err(&hdev->dev, + "eld control add failed with err: %d for pcm: %d\n", + err, device); + return err; + } + list_add_tail(&pcm->head, &hdmi->pcm_list); return 0; @@ -1820,7 +1965,7 @@ static int hdmi_codec_probe(struct snd_soc_component *component) struct hdac_device *hdev = hdmi->hdev; struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); - struct hdac_ext_link *hlink = NULL; + struct hdac_ext_link *hlink; int ret; hdmi->component = component; @@ -1829,7 +1974,7 @@ static int hdmi_codec_probe(struct snd_soc_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(hdev->bus, dev_name(&hdev->dev)); + hlink = snd_hdac_ext_bus_get_hlink_by_name(hdev->bus, dev_name(&hdev->dev)); if (!hlink) { dev_err(&hdev->dev, "hdac link not found\n"); return -EIO; @@ -1920,7 +2065,6 @@ static const struct snd_soc_component_driver hdmi_hda_codec = { .remove = hdmi_codec_remove, .use_pmdown_time = 1, .endianness = 1, - .non_legacy_dai_naming = 1, }; static void hdac_hdmi_get_chmap(struct hdac_device *hdev, int pcm_idx, @@ -2007,7 +2151,7 @@ static int hdac_hdmi_dev_probe(struct hdac_device *hdev) 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(hdev->bus, dev_name(&hdev->dev)); + hlink = snd_hdac_ext_bus_get_hlink_by_name(hdev->bus, dev_name(&hdev->dev)); if (!hlink) { dev_err(&hdev->dev, "hdac link not found\n"); return -EIO; @@ -2089,7 +2233,7 @@ static int hdac_hdmi_runtime_suspend(struct device *dev) { struct hdac_device *hdev = dev_to_hdac_dev(dev); struct hdac_bus *bus = hdev->bus; - struct hdac_ext_link *hlink = NULL; + struct hdac_ext_link *hlink; dev_dbg(dev, "Enter: %s\n", __func__); @@ -2097,8 +2241,6 @@ static int hdac_hdmi_runtime_suspend(struct device *dev) if (!bus) return 0; - clear_dapm_works(hdev); - /* * Power down afg. * codec_read is preferred over codec_write to set the power state. @@ -2109,7 +2251,7 @@ static int hdac_hdmi_runtime_suspend(struct device *dev) snd_hdac_codec_read(hdev, hdev->afg, 0, AC_VERB_SET_POWER_STATE, AC_PWRST_D3); - hlink = snd_hdac_ext_bus_get_link(bus, 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; @@ -2127,7 +2269,7 @@ static int hdac_hdmi_runtime_resume(struct device *dev) { struct hdac_device *hdev = dev_to_hdac_dev(dev); struct hdac_bus *bus = hdev->bus; - struct hdac_ext_link *hlink = NULL; + struct hdac_ext_link *hlink; dev_dbg(dev, "Enter: %s\n", __func__); @@ -2135,7 +2277,7 @@ static int hdac_hdmi_runtime_resume(struct device *dev) if (!bus) return 0; - hlink = snd_hdac_ext_bus_get_link(bus, 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; |