[PATCH 1/2 v2] ASoC: hdmi-codec: enable multi probe for same device

From: Kuninori Morimoto
Date: Tue Aug 02 2016 - 01:55:36 EST



From: Kuninori Morimoto <kuninori.morimoto.gx@xxxxxxxxxxx>

hdmi-codec driver is common HDMI sound driver,
but it doesn't care about multi sound ports.
For example, hdmi-codec driver is supporting 1 I2S and 1 SPDIF ports,
so, we can't use this driver if HDMI has 2 or more I2S ports.

And we would like to use multi detection.
For example, DesignWare HDMI driver is providing dw_hdmi_bind() to
DRM/KMS driver, and it will setups HDMI video/sound.
Note is that it will be called under for_each loop of ports.

int dw_hdmi_bind(xxx)
{
/* register hdmi-codec driver here */
}

static int xxx_probe(struct platform_device *pdev)
{
for_each_xxx(xx) {
...
dw_hdmi_bind(xxx);
...
}
}

This case, dw_hdmi_bind() would like to use hdmi-codec,
and it will be called multiple times for each ports.

Here, ASoC's CPU/Codec/Card bind will checks each "of_node" on DT,
and hdmi-codec driver is assuming its parent device for it.
But it doesn't care about case.
Thus, ASoC never detect correct sound card in this case.

To solve this issue, this patch checks each parent device,
and names "hdmi-hifi.x" in order to each ports.
And uses struct snd_soc_component_driver :: of_xlate_dai_name
for snd_soc_get_dai_name().

Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@xxxxxxxxxxx>
---
sound/soc/codecs/hdmi-codec.c | 67 +++++++++++++++++++++++++++++++++++++++++--
1 file changed, 64 insertions(+), 3 deletions(-)

diff --git a/sound/soc/codecs/hdmi-codec.c b/sound/soc/codecs/hdmi-codec.c
index f27d115..f64ecaa 100644
--- a/sound/soc/codecs/hdmi-codec.c
+++ b/sound/soc/codecs/hdmi-codec.c
@@ -24,6 +24,15 @@

#include <drm/drm_crtc.h> /* This is only to get MAX_ELD_BYTES */

+struct hdmi_device {
+ struct device *dev;
+ struct list_head list;
+ int cnt;
+};
+#define pos_to_hdmi_device(pos) container_of((pos), struct hdmi_device, list)
+LIST_HEAD(hdmi_device_list);
+
+#define DAI_NAME_SIZE 16
struct hdmi_codec_priv {
struct hdmi_codec_pdata hcd;
struct snd_soc_dai_driver *daidrv;
@@ -320,7 +329,6 @@ static const struct snd_soc_dai_ops hdmi_dai_ops = {
SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE)

static struct snd_soc_dai_driver hdmi_i2s_dai = {
- .name = "i2s-hifi",
.id = DAI_ID_I2S,
.playback = {
.stream_name = "Playback",
@@ -334,7 +342,6 @@ static struct snd_soc_dai_driver hdmi_i2s_dai = {
};

static const struct snd_soc_dai_driver hdmi_spdif_dai = {
- .name = "spdif-hifi",
.id = DAI_ID_SPDIF,
.playback = {
.stream_name = "Playback",
@@ -346,6 +353,27 @@ static const struct snd_soc_dai_driver hdmi_spdif_dai = {
.ops = &hdmi_dai_ops,
};

+static char hdmi_dai_name[][DAI_NAME_SIZE] = {
+ "hdmi-hifi.0",
+ "hdmi-hifi.1",
+ "hdmi-hifi.2",
+ "hdmi-hifi.3",
+};
+
+static int hdmi_of_xlate_dai_name(struct snd_soc_component *component,
+ struct of_phandle_args *args,
+ const char **dai_name)
+{
+ int id = args->args[0];
+
+ if (id < ARRAY_SIZE(hdmi_dai_name)) {
+ *dai_name = hdmi_dai_name[id];
+ return 0;
+ }
+
+ return -EAGAIN;
+}
+
static struct snd_soc_codec_driver hdmi_codec = {
.controls = hdmi_controls,
.num_controls = ARRAY_SIZE(hdmi_controls),
@@ -353,6 +381,9 @@ static struct snd_soc_codec_driver hdmi_codec = {
.num_dapm_widgets = ARRAY_SIZE(hdmi_widgets),
.dapm_routes = hdmi_routes,
.num_dapm_routes = ARRAY_SIZE(hdmi_routes),
+ .component_driver = {
+ .of_xlate_dai_name = hdmi_of_xlate_dai_name,
+ },
};

static int hdmi_codec_probe(struct platform_device *pdev)
@@ -360,6 +391,8 @@ static int hdmi_codec_probe(struct platform_device *pdev)
struct hdmi_codec_pdata *hcd = pdev->dev.platform_data;
struct device *dev = &pdev->dev;
struct hdmi_codec_priv *hcp;
+ struct hdmi_device *hd;
+ struct list_head *pos;
int dai_count, i = 0;
int ret;

@@ -381,6 +414,31 @@ static int hdmi_codec_probe(struct platform_device *pdev)
if (!hcp)
return -ENOMEM;

+ hd = NULL;
+ list_for_each(pos, &hdmi_device_list) {
+ struct hdmi_device *tmp = pos_to_hdmi_device(pos);
+
+ if (tmp->dev == dev->parent) {
+ hd = tmp;
+ break;
+ }
+ }
+
+ if (!hd) {
+ hd = devm_kzalloc(dev, sizeof(*hd), GFP_KERNEL);
+ if (!hd)
+ return -ENOMEM;
+
+ hd->dev = dev->parent;
+
+ list_add_tail(&hd->list, &hdmi_device_list);
+ }
+
+ if (hd->cnt >= ARRAY_SIZE(hdmi_dai_name)) {
+ dev_err(dev, "too many hdmi codec are deteced\n");
+ return -EINVAL;
+ }
+
hcp->hcd = *hcd;
mutex_init(&hcp->current_stream_lock);

@@ -393,11 +451,14 @@ static int hdmi_codec_probe(struct platform_device *pdev)
hcp->daidrv[i] = hdmi_i2s_dai;
hcp->daidrv[i].playback.channels_max =
hcd->max_i2s_channels;
+ hcp->daidrv[i].name = hdmi_dai_name[hd->cnt++];
i++;
}

- if (hcd->spdif)
+ if (hcd->spdif) {
hcp->daidrv[i] = hdmi_spdif_dai;
+ hcp->daidrv[i].name = hdmi_dai_name[hd->cnt++];
+ }

ret = snd_soc_register_codec(dev, &hdmi_codec, hcp->daidrv,
dai_count);
--
1.9.1