Received: by 2002:a05:6a10:a0d1:0:0:0:0 with SMTP id j17csp3148804pxa; Tue, 18 Aug 2020 07:50:30 -0700 (PDT) X-Google-Smtp-Source: ABdhPJyAgBLIA4ywmtfgKWDSZ79yVMsDnsDVA/AhqRZ/KO1JlOVHPQ/AfilMqMHf98G10U4AT5Be X-Received: by 2002:a50:fb14:: with SMTP id d20mr19470868edq.191.1597762230322; Tue, 18 Aug 2020 07:50:30 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1597762230; cv=none; d=google.com; s=arc-20160816; b=TxGtooEeuu6nGpKVcjA+uh4lqV6p4oIr+9+JXjy81D9dLMppDw1LG5yUb1l0AgqGJV qYzFp0v0EBY5ELL19ilgE1rHfymDyeWPcdsGhYzbOSO4r2asVTy6NBJ2Qw/gmrfusmFE kzDGoUCfnAAFldhULJTwPP2isavvnaQJmH7Qc9BKw0oFgAdEYML8L8/RUfQNhpZBjzyr oe5fcNUvYa9dSbWXDV//JYHBGfiOnoPKewQRuTQ7xziOLFuaOnJeuTlX7pA2LXIr3HPr roJcSb4BsRL41QIcnQ+ZTtRZF1ZdkFNr4xUPkHhcsWFdfT2O9uQcdWPGCVJKeUB/BFTk xjHg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:in-reply-to:message-id:date :subject:cc:to:from:ironport-sdr:ironport-sdr; bh=SQJ5oEtYcoH9fJVir/jz+eYihIL5rFZmQVzj0pN/1dk=; b=TCOLF8Zmboqz9HqfPI2eUquJ1y2JprjysOT+4uc48IuFwBKz34N03ZHiYD82VLy1xU l5vCMnTc2+fDOhVugJe0QMIWIb3+ZJ78ao9g9YhG9LdmcXW/hfwEGPrTxV8NftxcdRI2 DF395mZfvLnMuD6Xcsi2SUEAvIPK8NaAD4OtrDjMhcue54HQ+g+Ee+7wgTBPgiACV4/k aywbNr4XT2BYnsiUN3hTvsEU7X3AVfgQj6Au7TC1iMLhYQVPZqw50nKFhV/gIESDYs2A kMF8e54jCy3W5efhSwQGx2JDJyO+mHYz9qzsbcOJRxyCiiqoVqUEE6L1t6Wa+udmg7n0 gYXA== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=intel.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id w1si12995223ejc.440.2020.08.18.07.50.05; Tue, 18 Aug 2020 07:50:30 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) client-ip=23.128.96.18; Authentication-Results: mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=intel.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727783AbgHROsj (ORCPT + 99 others); Tue, 18 Aug 2020 10:48:39 -0400 Received: from mga11.intel.com ([192.55.52.93]:28962 "EHLO mga11.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726803AbgHROsi (ORCPT ); Tue, 18 Aug 2020 10:48:38 -0400 IronPort-SDR: 7tBoIHbIJfPASeUy5ALqOQYLf3E2gS4pJ8Fj2AWFGjpGIBKG1SJbQO4R05VxAIk8brOBAd1Mhc VfcuYp8khU4w== X-IronPort-AV: E=McAfee;i="6000,8403,9716"; a="152545745" X-IronPort-AV: E=Sophos;i="5.76,327,1592895600"; d="scan'208";a="152545745" X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from fmsmga003.fm.intel.com ([10.253.24.29]) by fmsmga102.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 18 Aug 2020 07:48:37 -0700 IronPort-SDR: j3lzUZxUs/HATozfdFB66r1N6Z+nQv6tj1Q0Dm6QaLVGDGp2gtIymda8Hc9mj0LD5Z51BBbt/E Z/y1JkdV0PuA== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.76,327,1592895600"; d="scan'208";a="334390588" Received: from brentlu-desk0.itwn.intel.com ([10.5.253.11]) by FMSMGA003.fm.intel.com with ESMTP; 18 Aug 2020 07:48:35 -0700 From: Brent Lu To: alsa-devel@alsa-project.org Cc: Liam Girdwood , Mark Brown , Jaroslav Kysela , Takashi Iwai , Pierre-Louis Bossart , Brent Lu , =?UTF-8?q?Amadeusz=20S=C5=82awi=C5=84ski?= , Guennadi Liakhovetski , Kai Vehmanen , linux-kernel@vger.kernel.org Subject: [PATCH v3] ASoC: hdac_hdmi: support 'ELD' mixer Date: Tue, 18 Aug 2020 22:36:32 +0800 Message-Id: <20200818143632.16539-1-brent.lu@intel.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20200818004413.12852-1-brent.lu@intel.com> References: <20200818004413.12852-1-brent.lu@intel.com> Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Add a binary mixer 'ELD' to each HDMI PCM device so user-space can read the ELD data of external HDMI display. If same PCM is connected to multiple receivers, the mixer returns the ELD data for the first one found in the port list and ignore the rest. Thie behavior seems iffy but is aligned with the user-space interface exposed by patch_hdmi.c. Also the pcm-to-many routing is nor really supported by the driver now. Signed-off-by: Brent Lu --- sound/soc/codecs/hdac_hdmi.c | 138 +++++++++++++++++++++++++++++++++++ 1 file changed, 138 insertions(+) diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c index f26b77faed59..869d1547ae5d 100644 --- a/sound/soc/codecs/hdac_hdmi.c +++ b/sound/soc/codecs/hdac_hdmi.c @@ -9,6 +9,7 @@ * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + #include #include #include @@ -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 { @@ -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, ARRAY_SIZE(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, @@ -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; -- 2.17.1