diff options
Diffstat (limited to 'sound/soc/codecs/hdac_hdmi.c')
-rw-r--r-- | sound/soc/codecs/hdac_hdmi.c | 167 |
1 files changed, 151 insertions, 16 deletions
diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c index e6558475e006..cb23650ad522 100644 --- a/sound/soc/codecs/hdac_hdmi.c +++ b/sound/soc/codecs/hdac_hdmi.c @@ -9,6 +9,7 @@ * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + #include <linux/init.h> #include <linux/delay.h> #include <linux/module.h> @@ -107,6 +108,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 { @@ -142,14 +144,14 @@ 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, @@ -521,7 +523,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) { @@ -711,7 +713,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 +900,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 +1250,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 +1276,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 +1286,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 +1320,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,6 +1424,122 @@ 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, @@ -1564,7 +1693,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; @@ -1784,6 +1913,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 +1958,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; @@ -1920,7 +2058,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, @@ -1998,11 +2135,11 @@ static struct hdac_hdmi_drv_data intel_drv_data = { static int hdac_hdmi_dev_probe(struct hdac_device *hdev) { - struct hdac_hdmi_priv *hdmi_priv = NULL; + 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); @@ -2089,7 +2226,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 +2234,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. @@ -2127,7 +2262,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__); |