Message ID | 1554194132-2177-1-git-send-email-libin.yang@intel.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | [RFC,V2] ASoC: hdac_hdmi: add device PM dependency | expand |
Hi all, Or if we can assume that consumer will definitely call hdac_hdmi_device_link_del() itself, the patch can be simplified. I attach the simplified version of the patch. I'm not sure which is better. diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c index 5eeb0fe..346e8e5 100644 --- a/sound/soc/codecs/hdac_hdmi.c +++ b/sound/soc/codecs/hdac_hdmi.c @@ -1792,6 +1792,49 @@ int hdac_hdmi_jack_init(struct snd_soc_dai *dai, int device, } EXPORT_SYMBOL_GPL(hdac_hdmi_jack_init); +/** + * hdac_hdmi_device_link_add - add a link between consumer and hdev device + * @consumer: Consumer end of the link. + * @component: The hdmi codec component. + * @flags: Link flags. + * + * If there is a consumer of the hdev device, which means a consumer device + * operation depends on the hdev device power status, the consumer device + * driver should call this function. It supports several consumers depending + * on the same hdmi codec hdev device. + * + * Return: pointer of device_link, or NULL if fails to create device_link + */ +struct device_link * +hdac_hdmi_device_link_add(struct device *consumer, + struct snd_soc_component *component, + u32 flags) +{ + struct hdac_hdmi_priv *hdmi = snd_soc_component_get_drvdata(component); + struct hdac_device *hdev = hdmi->hdev; + + return device_link_add(consumer, &hdev->dev, flags); +} +EXPORT_SYMBOL_GPL(hdac_hdmi_device_link_add); + +/** + * hdac_hdmi_device_link_del - del the link between consumer and hdev device + * @consumer: Consumer end of the link. + * @component: The hdmi codec component. + * + * If the consumer doesn't need the dependency any longer, the consumer device + * driver should call this function to release the device_link + */ +void hdac_hdmi_device_link_del(struct device *consumer, + struct snd_soc_component *component) +{ + struct hdac_hdmi_priv *hdmi = snd_soc_component_get_drvdata(component); + struct hdac_device *hdev = hdmi->hdev; + + device_link_remove(consumer, &hdev->dev); +} +EXPORT_SYMBOL_GPL(hdac_hdmi_device_link_del); + static void hdac_hdmi_present_sense_all_pins(struct hdac_device *hdev, struct hdac_hdmi_priv *hdmi, bool detect_pin_caps) { diff --git a/sound/soc/codecs/hdac_hdmi.h b/sound/soc/codecs/hdac_hdmi.h index 4fa2fc9..e877167 100644 --- a/sound/soc/codecs/hdac_hdmi.h +++ b/sound/soc/codecs/hdac_hdmi.h @@ -7,4 +7,11 @@ int hdac_hdmi_jack_init(struct snd_soc_dai *dai, int pcm, int hdac_hdmi_jack_port_init(struct snd_soc_component *component, struct snd_soc_dapm_context *dapm); + +struct device_link * +hdac_hdmi_device_link_add(struct device *consumer, + struct snd_soc_component *component, + u32 flags); +void hdac_hdmi_device_link_del(struct device *consumer, + struct snd_soc_component *component); #endif /* __HDAC_HDMI_H__ */ -- 2.7.4 Regards, Libin >-----Original Message----- >From: Yang, Libin >Sent: Tuesday, April 2, 2019 4:36 PM >To: alsa-devel@alsa-project.org; tiwai@suse.de; broonie@kernel.org; pierre- >louis.bossart@linux.intel.com >Cc: Yang, Libin <libin.yang@intel.com> >Subject: [alsa-devel] [RFC PATCH V2] ASoC: hdac_hdmi: add device PM >dependency > >From: Libin Yang <libin.yang@intel.com> > >HDMI audio codec register operation depends on the display audio power >domain. The hdmi audio codec dapm event callback must be called after >display audio power is turned on. > >Add hdac_hdmi_device_link_add/del() in HDMI audio codec driver. The >customer audio driver, such as cAVS/SST and SOF audio driver, can call the >function to set/remove the audio power dependency. > >Signed-off-by: Libin Yang <libin.yang@intel.com> >--- > sound/soc/codecs/hdac_hdmi.c | 86 >++++++++++++++++++++++++++++++++++++++++++++ > sound/soc/codecs/hdac_hdmi.h | 7 ++++ > 2 files changed, 93 insertions(+) > >diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c >index 5eeb0fe..7490219 100644 >--- a/sound/soc/codecs/hdac_hdmi.c >+++ b/sound/soc/codecs/hdac_hdmi.c >@@ -133,6 +133,12 @@ struct hdac_hdmi_drv_data { > int port_num; > }; > >+/* each supplier - consumer pair has one instance of this structure */ >+struct hdac_hdmi_dev_link { >+ struct list_head head; >+ struct device_link *link; >+}; >+ > struct hdac_hdmi_priv { > struct hdac_device *hdev; > struct snd_soc_component *component; >@@ -141,10 +147,12 @@ struct hdac_hdmi_priv { > struct list_head pin_list; > struct list_head cvt_list; > struct list_head pcm_list; >+ struct list_head link_list; /* supplier - consumer pairs */ > int num_pin; > int num_cvt; > int num_ports; > struct mutex pin_mutex; >+ struct mutex link_mutex; > struct hdac_chmap chmap; > struct hdac_hdmi_drv_data *drv_data; > struct snd_soc_dai_driver *dai_drv; >@@ -1792,6 +1800,72 @@ int hdac_hdmi_jack_init(struct snd_soc_dai *dai, >int device, } EXPORT_SYMBOL_GPL(hdac_hdmi_jack_init); > >+/** >+ * hdac_hdmi_device_link_add - add a link between consumer and hdev >+device >+ * @consumer: Consumer end of the link. >+ * @component: The hdmi codec component. >+ * @flags: Link flags. >+ * >+ * If there is a consumer of the hdev device, which means a consumer >+device >+ * operation depends on the hdev device power status, the consumer >+device >+ * driver should call this function. It supports several consumers >+depending >+ * on the same hdmi codec hdev device. >+ * >+ * Return: pointer of device_link, or NULL if fails to create >+device_link */ struct device_link * hdac_hdmi_device_link_add(struct >+device *consumer, >+ struct snd_soc_component *component, >+ u32 flags) >+{ >+ struct hdac_hdmi_priv *hdmi = >snd_soc_component_get_drvdata(component); >+ struct hdac_device *hdev = hdmi->hdev; >+ struct hdac_hdmi_dev_link *dev_link; >+ >+ dev_link = devm_kzalloc(&hdev->dev, sizeof(*dev_link), GFP_KERNEL); >+ if (!dev_link) >+ return NULL; >+ >+ dev_link->link = device_link_add(consumer, &hdev->dev, flags); >+ >+ mutex_lock(&hdmi->link_mutex); >+ if (dev_link->link) >+ list_add(&dev_link->head, &hdmi->link_list); >+ mutex_unlock(&hdmi->link_mutex); >+ >+ return dev_link->link; >+} >+EXPORT_SYMBOL_GPL(hdac_hdmi_device_link_add); >+ >+/** >+ * hdac_hdmi_device_link_del - del the link between consumer and hdev >+device >+ * @consumer: Consumer end of the link. >+ * @component: The hdmi codec component. >+ * >+ * If the consumer doesn't need the dependency any longer, the consumer >+device >+ * driver should call this function to release the device_link */ void >+hdac_hdmi_device_link_del(struct device *consumer, >+ struct snd_soc_component *component) { >+ struct hdac_hdmi_priv *hdmi = >snd_soc_component_get_drvdata(component); >+ struct hdac_device *hdev = hdmi->hdev; >+ struct hdac_hdmi_dev_link *dev_link, *tmp; >+ >+ mutex_lock(&hdmi->link_mutex); >+ list_for_each_entry_safe(dev_link, tmp, &hdmi->link_list, head) { >+ if (dev_link->link->consumer == consumer && >+ dev_link->link->supplier == &hdev->dev) { >+ device_link_del(dev_link->link); >+ list_del(&dev_link->head); >+ devm_kfree(&hdev->dev, dev_link); >+ return; >+ } >+ } >+ mutex_unlock(&hdmi->link_mutex); >+} >+EXPORT_SYMBOL_GPL(hdac_hdmi_device_link_del); >+ > static void hdac_hdmi_present_sense_all_pins(struct hdac_device *hdev, > struct hdac_hdmi_priv *hdmi, bool detect_pin_caps) >{ @@ -2031,7 +2105,9 @@ static int hdac_hdmi_dev_probe(struct >hdac_device *hdev) > INIT_LIST_HEAD(&hdmi_priv->pin_list); > INIT_LIST_HEAD(&hdmi_priv->cvt_list); > INIT_LIST_HEAD(&hdmi_priv->pcm_list); >+ INIT_LIST_HEAD(&hdmi_priv->link_list); > mutex_init(&hdmi_priv->pin_mutex); >+ mutex_init(&hdmi_priv->link_mutex); > > /* > * Turned off in the runtime_suspend during the first explicit @@ - >2058,8 +2134,18 @@ static int hdac_hdmi_dev_probe(struct hdac_device >*hdev) > > static int hdac_hdmi_dev_remove(struct hdac_device *hdev) { >+ struct hdac_hdmi_dev_link *dev_link, *tmp; >+ struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); >+ > snd_hdac_display_power(hdev->bus, hdev->addr, false); > >+ mutex_unlock(&hdmi->link_mutex); >+ list_for_each_entry_safe(dev_link, tmp, &hdmi->link_list, head) { >+ device_link_del(dev_link->link); >+ list_del(&dev_link->head); >+ } >+ mutex_unlock(&hdmi->link_mutex); >+ > return 0; > } > >diff --git a/sound/soc/codecs/hdac_hdmi.h b/sound/soc/codecs/hdac_hdmi.h >index 4fa2fc9..e877167 100644 >--- a/sound/soc/codecs/hdac_hdmi.h >+++ b/sound/soc/codecs/hdac_hdmi.h >@@ -7,4 +7,11 @@ int hdac_hdmi_jack_init(struct snd_soc_dai *dai, int pcm, > > int hdac_hdmi_jack_port_init(struct snd_soc_component *component, > struct snd_soc_dapm_context *dapm); >+ >+struct device_link * >+hdac_hdmi_device_link_add(struct device *consumer, >+ struct snd_soc_component *component, >+ u32 flags); >+void hdac_hdmi_device_link_del(struct device *consumer, >+ struct snd_soc_component *component); > #endif /* __HDAC_HDMI_H__ */ >-- >2.7.4
Hi all, After second thought, maybe we can call device_link_add/remove() in the consumer driver? Regards, Libin >-----Original Message----- >From: Yang, Libin >Sent: Wednesday, April 3, 2019 10:01 AM >To: alsa-devel@alsa-project.org; tiwai@suse.de; broonie@kernel.org; pierre- >louis.bossart@linux.intel.com >Subject: RE: [alsa-devel] [RFC PATCH V2] ASoC: hdac_hdmi: add device PM >dependency > >Hi all, > >Or if we can assume that consumer will definitely call >hdac_hdmi_device_link_del() itself, the patch can be simplified. > >I attach the simplified version of the patch. I'm not sure which is better. > >diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c >index 5eeb0fe..346e8e5 100644 >--- a/sound/soc/codecs/hdac_hdmi.c >+++ b/sound/soc/codecs/hdac_hdmi.c >@@ -1792,6 +1792,49 @@ int hdac_hdmi_jack_init(struct snd_soc_dai *dai, >int device, } EXPORT_SYMBOL_GPL(hdac_hdmi_jack_init); > >+/** >+ * hdac_hdmi_device_link_add - add a link between consumer and hdev >+device >+ * @consumer: Consumer end of the link. >+ * @component: The hdmi codec component. >+ * @flags: Link flags. >+ * >+ * If there is a consumer of the hdev device, which means a consumer >+device >+ * operation depends on the hdev device power status, the consumer >+device >+ * driver should call this function. It supports several consumers >+depending >+ * on the same hdmi codec hdev device. >+ * >+ * Return: pointer of device_link, or NULL if fails to create >+device_link */ struct device_link * hdac_hdmi_device_link_add(struct >+device *consumer, >+ struct snd_soc_component *component, >+ u32 flags) >+{ >+ struct hdac_hdmi_priv *hdmi = >snd_soc_component_get_drvdata(component); >+ struct hdac_device *hdev = hdmi->hdev; >+ >+ return device_link_add(consumer, &hdev->dev, flags); } >+EXPORT_SYMBOL_GPL(hdac_hdmi_device_link_add); >+ >+/** >+ * hdac_hdmi_device_link_del - del the link between consumer and hdev >+device >+ * @consumer: Consumer end of the link. >+ * @component: The hdmi codec component. >+ * >+ * If the consumer doesn't need the dependency any longer, the consumer >+device >+ * driver should call this function to release the device_link */ void >+hdac_hdmi_device_link_del(struct device *consumer, >+ struct snd_soc_component *component) { >+ struct hdac_hdmi_priv *hdmi = >snd_soc_component_get_drvdata(component); >+ struct hdac_device *hdev = hdmi->hdev; >+ >+ device_link_remove(consumer, &hdev->dev); } >+EXPORT_SYMBOL_GPL(hdac_hdmi_device_link_del); >+ > static void hdac_hdmi_present_sense_all_pins(struct hdac_device *hdev, > struct hdac_hdmi_priv *hdmi, bool detect_pin_caps) { diff --git >a/sound/soc/codecs/hdac_hdmi.h b/sound/soc/codecs/hdac_hdmi.h index >4fa2fc9..e877167 100644 >--- a/sound/soc/codecs/hdac_hdmi.h >+++ b/sound/soc/codecs/hdac_hdmi.h >@@ -7,4 +7,11 @@ int hdac_hdmi_jack_init(struct snd_soc_dai *dai, int pcm, > > int hdac_hdmi_jack_port_init(struct snd_soc_component *component, > struct snd_soc_dapm_context *dapm); >+ >+struct device_link * >+hdac_hdmi_device_link_add(struct device *consumer, >+ struct snd_soc_component *component, >+ u32 flags); >+void hdac_hdmi_device_link_del(struct device *consumer, >+ struct snd_soc_component *component); > #endif /* __HDAC_HDMI_H__ */ >-- >2.7.4 > >Regards, >Libin > > >>-----Original Message----- >>From: Yang, Libin >>Sent: Tuesday, April 2, 2019 4:36 PM >>To: alsa-devel@alsa-project.org; tiwai@suse.de; broonie@kernel.org; >>pierre- louis.bossart@linux.intel.com >>Cc: Yang, Libin <libin.yang@intel.com> >>Subject: [alsa-devel] [RFC PATCH V2] ASoC: hdac_hdmi: add device PM >>dependency >> >>From: Libin Yang <libin.yang@intel.com> >> >>HDMI audio codec register operation depends on the display audio power >>domain. The hdmi audio codec dapm event callback must be called after >>display audio power is turned on. >> >>Add hdac_hdmi_device_link_add/del() in HDMI audio codec driver. The >>customer audio driver, such as cAVS/SST and SOF audio driver, can call >>the function to set/remove the audio power dependency. >> >>Signed-off-by: Libin Yang <libin.yang@intel.com> >>--- >> sound/soc/codecs/hdac_hdmi.c | 86 >>++++++++++++++++++++++++++++++++++++++++++++ >> sound/soc/codecs/hdac_hdmi.h | 7 ++++ >> 2 files changed, 93 insertions(+) >> >>diff --git a/sound/soc/codecs/hdac_hdmi.c >>b/sound/soc/codecs/hdac_hdmi.c index 5eeb0fe..7490219 100644 >>--- a/sound/soc/codecs/hdac_hdmi.c >>+++ b/sound/soc/codecs/hdac_hdmi.c >>@@ -133,6 +133,12 @@ struct hdac_hdmi_drv_data { >> int port_num; >> }; >> >>+/* each supplier - consumer pair has one instance of this structure */ >>+struct hdac_hdmi_dev_link { >>+ struct list_head head; >>+ struct device_link *link; >>+}; >>+ >> struct hdac_hdmi_priv { >> struct hdac_device *hdev; >> struct snd_soc_component *component; >>@@ -141,10 +147,12 @@ struct hdac_hdmi_priv { >> struct list_head pin_list; >> struct list_head cvt_list; >> struct list_head pcm_list; >>+ struct list_head link_list; /* supplier - consumer pairs */ >> int num_pin; >> int num_cvt; >> int num_ports; >> struct mutex pin_mutex; >>+ struct mutex link_mutex; >> struct hdac_chmap chmap; >> struct hdac_hdmi_drv_data *drv_data; >> struct snd_soc_dai_driver *dai_drv; >>@@ -1792,6 +1800,72 @@ int hdac_hdmi_jack_init(struct snd_soc_dai *dai, >>int device, } EXPORT_SYMBOL_GPL(hdac_hdmi_jack_init); >> >>+/** >>+ * hdac_hdmi_device_link_add - add a link between consumer and hdev >>+device >>+ * @consumer: Consumer end of the link. >>+ * @component: The hdmi codec component. >>+ * @flags: Link flags. >>+ * >>+ * If there is a consumer of the hdev device, which means a consumer >>+device >>+ * operation depends on the hdev device power status, the consumer >>+device >>+ * driver should call this function. It supports several consumers >>+depending >>+ * on the same hdmi codec hdev device. >>+ * >>+ * Return: pointer of device_link, or NULL if fails to create >>+device_link */ struct device_link * hdac_hdmi_device_link_add(struct >>+device *consumer, >>+ struct snd_soc_component *component, >>+ u32 flags) >>+{ >>+ struct hdac_hdmi_priv *hdmi = >>snd_soc_component_get_drvdata(component); >>+ struct hdac_device *hdev = hdmi->hdev; >>+ struct hdac_hdmi_dev_link *dev_link; >>+ >>+ dev_link = devm_kzalloc(&hdev->dev, sizeof(*dev_link), GFP_KERNEL); >>+ if (!dev_link) >>+ return NULL; >>+ >>+ dev_link->link = device_link_add(consumer, &hdev->dev, flags); >>+ >>+ mutex_lock(&hdmi->link_mutex); >>+ if (dev_link->link) >>+ list_add(&dev_link->head, &hdmi->link_list); >>+ mutex_unlock(&hdmi->link_mutex); >>+ >>+ return dev_link->link; >>+} >>+EXPORT_SYMBOL_GPL(hdac_hdmi_device_link_add); >>+ >>+/** >>+ * hdac_hdmi_device_link_del - del the link between consumer and hdev >>+device >>+ * @consumer: Consumer end of the link. >>+ * @component: The hdmi codec component. >>+ * >>+ * If the consumer doesn't need the dependency any longer, the >>+consumer device >>+ * driver should call this function to release the device_link */ >>+void hdac_hdmi_device_link_del(struct device *consumer, >>+ struct snd_soc_component *component) { >>+ struct hdac_hdmi_priv *hdmi = >>snd_soc_component_get_drvdata(component); >>+ struct hdac_device *hdev = hdmi->hdev; >>+ struct hdac_hdmi_dev_link *dev_link, *tmp; >>+ >>+ mutex_lock(&hdmi->link_mutex); >>+ list_for_each_entry_safe(dev_link, tmp, &hdmi->link_list, head) { >>+ if (dev_link->link->consumer == consumer && >>+ dev_link->link->supplier == &hdev->dev) { >>+ device_link_del(dev_link->link); >>+ list_del(&dev_link->head); >>+ devm_kfree(&hdev->dev, dev_link); >>+ return; >>+ } >>+ } >>+ mutex_unlock(&hdmi->link_mutex); >>+} >>+EXPORT_SYMBOL_GPL(hdac_hdmi_device_link_del); >>+ >> static void hdac_hdmi_present_sense_all_pins(struct hdac_device *hdev, >> struct hdac_hdmi_priv *hdmi, bool detect_pin_caps) >{ @@ -2031,7 >>+2105,9 @@ static int hdac_hdmi_dev_probe(struct hdac_device *hdev) >> INIT_LIST_HEAD(&hdmi_priv->pin_list); >> INIT_LIST_HEAD(&hdmi_priv->cvt_list); >> INIT_LIST_HEAD(&hdmi_priv->pcm_list); >>+ INIT_LIST_HEAD(&hdmi_priv->link_list); >> mutex_init(&hdmi_priv->pin_mutex); >>+ mutex_init(&hdmi_priv->link_mutex); >> >> /* >> * Turned off in the runtime_suspend during the first explicit @@ - >>2058,8 +2134,18 @@ static int hdac_hdmi_dev_probe(struct hdac_device >>*hdev) >> >> static int hdac_hdmi_dev_remove(struct hdac_device *hdev) { >>+ struct hdac_hdmi_dev_link *dev_link, *tmp; >>+ struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); >>+ >> snd_hdac_display_power(hdev->bus, hdev->addr, false); >> >>+ mutex_unlock(&hdmi->link_mutex); >>+ list_for_each_entry_safe(dev_link, tmp, &hdmi->link_list, head) { >>+ device_link_del(dev_link->link); >>+ list_del(&dev_link->head); >>+ } >>+ mutex_unlock(&hdmi->link_mutex); >>+ >> return 0; >> } >> >>diff --git a/sound/soc/codecs/hdac_hdmi.h >>b/sound/soc/codecs/hdac_hdmi.h index 4fa2fc9..e877167 100644 >>--- a/sound/soc/codecs/hdac_hdmi.h >>+++ b/sound/soc/codecs/hdac_hdmi.h >>@@ -7,4 +7,11 @@ int hdac_hdmi_jack_init(struct snd_soc_dai *dai, int >>pcm, >> >> int hdac_hdmi_jack_port_init(struct snd_soc_component *component, >> struct snd_soc_dapm_context *dapm); >>+ >>+struct device_link * >>+hdac_hdmi_device_link_add(struct device *consumer, >>+ struct snd_soc_component *component, >>+ u32 flags); >>+void hdac_hdmi_device_link_del(struct device *consumer, >>+ struct snd_soc_component *component); >> #endif /* __HDAC_HDMI_H__ */ >>-- >>2.7.4
diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c index 5eeb0fe..7490219 100644 --- a/sound/soc/codecs/hdac_hdmi.c +++ b/sound/soc/codecs/hdac_hdmi.c @@ -133,6 +133,12 @@ struct hdac_hdmi_drv_data { int port_num; }; +/* each supplier - consumer pair has one instance of this structure */ +struct hdac_hdmi_dev_link { + struct list_head head; + struct device_link *link; +}; + struct hdac_hdmi_priv { struct hdac_device *hdev; struct snd_soc_component *component; @@ -141,10 +147,12 @@ struct hdac_hdmi_priv { struct list_head pin_list; struct list_head cvt_list; struct list_head pcm_list; + struct list_head link_list; /* supplier - consumer pairs */ int num_pin; int num_cvt; int num_ports; struct mutex pin_mutex; + struct mutex link_mutex; struct hdac_chmap chmap; struct hdac_hdmi_drv_data *drv_data; struct snd_soc_dai_driver *dai_drv; @@ -1792,6 +1800,72 @@ int hdac_hdmi_jack_init(struct snd_soc_dai *dai, int device, } EXPORT_SYMBOL_GPL(hdac_hdmi_jack_init); +/** + * hdac_hdmi_device_link_add - add a link between consumer and hdev device + * @consumer: Consumer end of the link. + * @component: The hdmi codec component. + * @flags: Link flags. + * + * If there is a consumer of the hdev device, which means a consumer device + * operation depends on the hdev device power status, the consumer device + * driver should call this function. It supports several consumers depending + * on the same hdmi codec hdev device. + * + * Return: pointer of device_link, or NULL if fails to create device_link + */ +struct device_link * +hdac_hdmi_device_link_add(struct device *consumer, + struct snd_soc_component *component, + u32 flags) +{ + struct hdac_hdmi_priv *hdmi = snd_soc_component_get_drvdata(component); + struct hdac_device *hdev = hdmi->hdev; + struct hdac_hdmi_dev_link *dev_link; + + dev_link = devm_kzalloc(&hdev->dev, sizeof(*dev_link), GFP_KERNEL); + if (!dev_link) + return NULL; + + dev_link->link = device_link_add(consumer, &hdev->dev, flags); + + mutex_lock(&hdmi->link_mutex); + if (dev_link->link) + list_add(&dev_link->head, &hdmi->link_list); + mutex_unlock(&hdmi->link_mutex); + + return dev_link->link; +} +EXPORT_SYMBOL_GPL(hdac_hdmi_device_link_add); + +/** + * hdac_hdmi_device_link_del - del the link between consumer and hdev device + * @consumer: Consumer end of the link. + * @component: The hdmi codec component. + * + * If the consumer doesn't need the dependency any longer, the consumer device + * driver should call this function to release the device_link + */ +void hdac_hdmi_device_link_del(struct device *consumer, + struct snd_soc_component *component) +{ + struct hdac_hdmi_priv *hdmi = snd_soc_component_get_drvdata(component); + struct hdac_device *hdev = hdmi->hdev; + struct hdac_hdmi_dev_link *dev_link, *tmp; + + mutex_lock(&hdmi->link_mutex); + list_for_each_entry_safe(dev_link, tmp, &hdmi->link_list, head) { + if (dev_link->link->consumer == consumer && + dev_link->link->supplier == &hdev->dev) { + device_link_del(dev_link->link); + list_del(&dev_link->head); + devm_kfree(&hdev->dev, dev_link); + return; + } + } + mutex_unlock(&hdmi->link_mutex); +} +EXPORT_SYMBOL_GPL(hdac_hdmi_device_link_del); + static void hdac_hdmi_present_sense_all_pins(struct hdac_device *hdev, struct hdac_hdmi_priv *hdmi, bool detect_pin_caps) { @@ -2031,7 +2105,9 @@ static int hdac_hdmi_dev_probe(struct hdac_device *hdev) INIT_LIST_HEAD(&hdmi_priv->pin_list); INIT_LIST_HEAD(&hdmi_priv->cvt_list); INIT_LIST_HEAD(&hdmi_priv->pcm_list); + INIT_LIST_HEAD(&hdmi_priv->link_list); mutex_init(&hdmi_priv->pin_mutex); + mutex_init(&hdmi_priv->link_mutex); /* * Turned off in the runtime_suspend during the first explicit @@ -2058,8 +2134,18 @@ static int hdac_hdmi_dev_probe(struct hdac_device *hdev) static int hdac_hdmi_dev_remove(struct hdac_device *hdev) { + struct hdac_hdmi_dev_link *dev_link, *tmp; + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); + snd_hdac_display_power(hdev->bus, hdev->addr, false); + mutex_unlock(&hdmi->link_mutex); + list_for_each_entry_safe(dev_link, tmp, &hdmi->link_list, head) { + device_link_del(dev_link->link); + list_del(&dev_link->head); + } + mutex_unlock(&hdmi->link_mutex); + return 0; } diff --git a/sound/soc/codecs/hdac_hdmi.h b/sound/soc/codecs/hdac_hdmi.h index 4fa2fc9..e877167 100644 --- a/sound/soc/codecs/hdac_hdmi.h +++ b/sound/soc/codecs/hdac_hdmi.h @@ -7,4 +7,11 @@ int hdac_hdmi_jack_init(struct snd_soc_dai *dai, int pcm, int hdac_hdmi_jack_port_init(struct snd_soc_component *component, struct snd_soc_dapm_context *dapm); + +struct device_link * +hdac_hdmi_device_link_add(struct device *consumer, + struct snd_soc_component *component, + u32 flags); +void hdac_hdmi_device_link_del(struct device *consumer, + struct snd_soc_component *component); #endif /* __HDAC_HDMI_H__ */