All of lore.kernel.org
 help / color / mirror / Atom feed
From: Stefan Mavrodiev <stefan@olimex.com>
To: Maxime Ripard <mripard@kernel.org>, Stefan Mavrodiev <stefan@olimex.com>
Cc: Dan Williams <dan.j.williams@intel.com>,
	Vinod Koul <vkoul@kernel.org>, Chen-Yu Tsai <wens@csie.org>,
	David Airlie <airlied@linux.ie>, Daniel Vetter <daniel@ffwll.ch>,
	open list <linux-kernel@vger.kernel.org>,
	"open list:DMA GENERIC OFFLOAD ENGINE SUBSYSTEM" 
	<dmaengine@vger.kernel.org>,
	"moderated list:ARM/Allwinner sunXi SoC support" 
	<linux-arm-kernel@lists.infradead.org>,
	"open list:DRM DRIVERS FOR ALLWINNER A10" 
	<dri-devel@lists.freedesktop.org>,
	linux-sunxi@googlegroups.com
Subject: Re: [PATCH 2/2] drm: sun4i: hdmi: Add support for sun4i HDMI encoder audio
Date: Wed, 15 Jan 2020 14:23:25 +0200	[thread overview]
Message-ID: <03081515-efd8-f281-947a-29928c8d5923@olimex.com> (raw)
In-Reply-To: <20200115083233.7wedmnkj4ju4eccv@gilmour.lan>

Hi,

On 1/15/20 10:32 AM, Maxime Ripard wrote:
> Hi Stefan,
>
> On Tue, Jan 14, 2020 at 11:04:55AM +0200, Stefan Mavrodiev wrote:
>> On 1/10/20 6:26 PM, Maxime Ripard wrote:
>>> Hi,
>>>
>>> On Fri, Jan 10, 2020 at 04:11:40PM +0200, Stefan Mavrodiev wrote:
>>>> Add HDMI audio support for the sun4i-hdmi encoder, used on
>>>> the older Allwinner chips - A10, A20, A31.
>>>>
>>>> Most of the code is based on the BSP implementation. In it
>>>> dditional formats are supported (S20_3LE and S24_LE), however
>>>> there where some problems with them and only S16_LE is left.
>>>>
>>>> Signed-off-by: Stefan Mavrodiev <stefan@olimex.com>
>>>> ---
>>>>    drivers/gpu/drm/sun4i/Kconfig            |   1 +
>>>>    drivers/gpu/drm/sun4i/Makefile           |   1 +
>>>>    drivers/gpu/drm/sun4i/sun4i_hdmi.h       |  30 ++
>>>>    drivers/gpu/drm/sun4i/sun4i_hdmi_audio.c | 375 +++++++++++++++++++++++
>>>>    drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c   |   4 +
>>>>    5 files changed, 411 insertions(+)
>>>>    create mode 100644 drivers/gpu/drm/sun4i/sun4i_hdmi_audio.c
>>>>
>>>> diff --git a/drivers/gpu/drm/sun4i/Kconfig b/drivers/gpu/drm/sun4i/Kconfig
>>>> index 37e90e42943f..192b732b10cd 100644
>>>> --- a/drivers/gpu/drm/sun4i/Kconfig
>>>> +++ b/drivers/gpu/drm/sun4i/Kconfig
>>>> @@ -19,6 +19,7 @@ if DRM_SUN4I
>>>>    config DRM_SUN4I_HDMI
>>>>           tristate "Allwinner A10 HDMI Controller Support"
>>>>           default DRM_SUN4I
>>>> +       select SND_PCM_ELD
>>>>           help
>>>>    	  Choose this option if you have an Allwinner SoC with an HDMI
>>>>    	  controller.
>>>> diff --git a/drivers/gpu/drm/sun4i/Makefile b/drivers/gpu/drm/sun4i/Makefile
>>>> index 0d04f2447b01..e2d82b451c36 100644
>>>> --- a/drivers/gpu/drm/sun4i/Makefile
>>>> +++ b/drivers/gpu/drm/sun4i/Makefile
>>>> @@ -5,6 +5,7 @@ sun4i-frontend-y		+= sun4i_frontend.o
>>>>    sun4i-drm-y			+= sun4i_drv.o
>>>>    sun4i-drm-y			+= sun4i_framebuffer.o
>>>>
>>>> +sun4i-drm-hdmi-y		+= sun4i_hdmi_audio.o
>>>>    sun4i-drm-hdmi-y		+= sun4i_hdmi_ddc_clk.o
>>>>    sun4i-drm-hdmi-y		+= sun4i_hdmi_enc.o
>>>>    sun4i-drm-hdmi-y		+= sun4i_hdmi_i2c.o
>>>> diff --git a/drivers/gpu/drm/sun4i/sun4i_hdmi.h b/drivers/gpu/drm/sun4i/sun4i_hdmi.h
>>>> index 7ad3f06c127e..456964e681b0 100644
>>>> --- a/drivers/gpu/drm/sun4i/sun4i_hdmi.h
>>>> +++ b/drivers/gpu/drm/sun4i/sun4i_hdmi.h
>>>> @@ -42,7 +42,32 @@
>>>>    #define SUN4I_HDMI_VID_TIMING_POL_VSYNC		BIT(1)
>>>>    #define SUN4I_HDMI_VID_TIMING_POL_HSYNC		BIT(0)
>>>>
>>>> +#define SUN4I_HDMI_AUDIO_CTRL_REG	0x040
>>>> +#define SUN4I_HDMI_AUDIO_CTRL_ENABLE		BIT(31)
>>>> +#define SUN4I_HDMI_AUDIO_CTRL_RESET		BIT(30)
>>>> +
>>>> +#define SUN4I_HDMI_AUDIO_FMT_REG	0x048
>>>> +#define SUN4I_HDMI_AUDIO_FMT_SRC		BIT(31)
>>>> +#define SUN4I_HDMI_AUDIO_FMT_LAYOUT		BIT(3)
>>>> +#define SUN4I_HDMI_AUDIO_FMT_CH_CFG(n)		(n - 1)
>>> There's the issue multiple times in the headers, but you should wrap n
>>> in parentheses to make sure we have no issue with precedence when
>>> calling the macro.
>>>
>>>> +int sun4i_hdmi_audio_create(struct sun4i_hdmi *hdmi)
>>>> +{
>>>> +	struct snd_soc_card *card = &sun4i_hdmi_audio_card;
>>>> +	struct snd_soc_dai_link_component *comp;
>>>> +	struct snd_soc_dai_link *link;
>>>> +	int ret;
>>>> +
>>>> +	ret = devm_snd_dmaengine_pcm_register(hdmi->dev,
>>>> +					      &sun4i_hdmi_audio_pcm_config, 0);
>>>> +	if (ret) {
>>>> +		DRM_ERROR("Could not register PCM\n");
>>>> +		return ret;
>>>> +	}
>>>> +
>>>> +	ret = devm_snd_soc_register_component(hdmi->dev,
>>>> +					      &sun4i_hdmi_audio_component,
>>>> +					      &sun4i_hdmi_audio_dai, 1);
>>>> +	if (ret) {
>>>> +		DRM_ERROR("Could not register DAI\n");
>>>> +		return ret;
>>>> +	}
>>>> +
>>>> +	link = devm_kzalloc(hdmi->dev, sizeof(*link), GFP_KERNEL);
>>>> +	if (!link)
>>>> +		return -ENOMEM;
>>>> +
>>>> +	comp = devm_kzalloc(hdmi->dev, sizeof(*comp) * 3, GFP_KERNEL);
>>>> +	if (!comp)
>>>> +		return -ENOMEM;
>>>> +
>>>> +	link->cpus = &comp[0];
>>>> +	link->codecs = &comp[1];
>>>> +	link->platforms = &comp[2];
>>>> +
>>>> +	link->num_cpus = 1;
>>>> +	link->num_codecs = 1;
>>>> +	link->num_platforms = 1;
>>>> +
>>>> +	link->playback_only = 1;
>>>> +
>>>> +	link->name = "SUN4I-HDMI";
>>>> +	link->stream_name = "SUN4I-HDMI PCM";
>>>> +
>>>> +	link->codecs->name = dev_name(hdmi->dev);
>>>> +	link->codecs->dai_name	= sun4i_hdmi_audio_dai.name;
>>>> +
>>>> +	link->cpus->dai_name = dev_name(hdmi->dev);
>>>> +
>>>> +	link->platforms->name = dev_name(hdmi->dev);
>>>> +
>>>> +	link->dai_fmt = SND_SOC_DAIFMT_I2S;
>>>> +
>>>> +	card->dai_link = link;
>>>> +	card->num_links = 1;
>>>> +	card->dev = hdmi->dev;
>>>> +
>>>> +	snd_soc_card_set_drvdata(card, hdmi);
>>>> +	return devm_snd_soc_register_card(hdmi->dev, card);
>>> Out of curiosity, did you try to remove the module with that patch
>>> applied? IIRC, these functions will overwrite the device drvdata, and
>>> we will try to access them in unbind / remove.
>> Actually I did not. Just tried that and you're right. The module
>> crashes at the unbind call.  I use sun4i_hdmi struct only for
>> regmap. Maybe create separate private structure and copy only
>> regmap?
> I think the issue is that:
>
>    - In bind, we first call dev_set_drvdata on the bound device, with a
>      pointer to struct sun4i_hdmi as the value. The driver_data field
>      in struct device is now a pointer to our instance of struct
>      sun4i_hdmi.
>
>    - In audio create, you then call snd_soc_card_set_drvdata with a
>      pointer to struct sun4i_hdmi as the value. The drvdata field in
>      the struct snd_soc_card is now a pointer to our instance of struct
>      sun4i_hdmi (so far so good).
>
>    - Then you call (devm_)snd_soc_register_card. One of the thing that
>      it will do is call drv_set_drvdata on the card->dev device,
>      setting it to our pointer to the struct snd_soc_card we provided.
>      However, since you set card->dev to the same device than the one
>      initially bound, this means that you just overwrote the struct
>      sun4i_hdmi pointer with a pointer to struct snd_soc_card.
>
>    - The driver will operate properly, since we never really use the
>      driver_data field, in the HDMI driver, except when...
>
>    - At unbind, you retrieve the driver_data field, expecting a struct
>      sun4i_hdmi pointer, except you have a pointer to struct
>      snd_soc_card, and everything explodes.
>
> I think the way to work around that would be to create a new
> (platform_)device for the HDMI audio component, so that ASoC can work
> on that device instead.
>
> This seems to be what dw-hdmi is doing here:
> https://elixir.bootlin.com/linux/latest/source/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c#L2812
>
> (Except that they are also using platform_data, since they have
> multiple drivers, we wouldn't, so we can just lookup sun4i_hdmi using
> the parent's device driver_data).
>
>>>> +}
>>>> diff --git a/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c b/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c
>>>> index a7c4654445c7..79ecd89fb705 100644
>>>> --- a/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c
>>>> +++ b/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c
>>>> @@ -114,6 +114,9 @@ static void sun4i_hdmi_enable(struct drm_encoder *encoder)
>>>>    		val |= SUN4I_HDMI_VID_CTRL_HDMI_MODE;
>>>>
>>>>    	writel(val, hdmi->base + SUN4I_HDMI_VID_CTRL_REG);
>>>> +
>>>> +	if (hdmi->hdmi_audio && sun4i_hdmi_audio_create(hdmi))
>>>> +		DRM_ERROR("Couldn't create the HDMI audio adapter\n");
>>> So you create the audio card each time the display is enabled? I guess
>>> this is to deal with the hotplug?
>> Yes. See below.
>>
>>> I'm not sure this is the right thing to do. If I remember well, the
>>> ELD are here precisely to let userspace know that the display is
>>> plugged (and audio-capable) or not.
>>>
>>> Also, you don't remove that card in the disable, which mean that if
>>> you end up in a situation where you would enable the display, disable
>>> it and then enable it again, you have two audio cards now.
>> There is issue with the hotplug. When inserting the cable, the event
>> is detected and the hdmi encoder is enabled. Thus the card is
>> created. However further removal and insertions are not
>> detected.
> I guess we would need to fix that then?
>
>> This is why I don't remove the card.
>>
>> Also I count on devm_snd_soc_register_card() to release the card.
> I think you should really create the card all the time, and just
> update the ELD to let the userspace know when something has been
> created.
>
> And yeah, we should have a working hotplug, but that's a separate
> story :)

Thank you for the review. Soon I'll prepare v2.

Also I'll check the hotplug issue.

>
> Maxime

Best regards,
Stefan


WARNING: multiple messages have this Message-ID (diff)
From: Stefan Mavrodiev <stefan@olimex.com>
To: Maxime Ripard <mripard@kernel.org>, Stefan Mavrodiev <stefan@olimex.com>
Cc: David Airlie <airlied@linux.ie>,
	linux-sunxi@googlegroups.com, Vinod Koul <vkoul@kernel.org>,
	"open list:DRM DRIVERS FOR ALLWINNER A10"
	<dri-devel@lists.freedesktop.org>,
	open list <linux-kernel@vger.kernel.org>,
	Chen-Yu Tsai <wens@csie.org>, Daniel Vetter <daniel@ffwll.ch>,
	"open list:DMA GENERIC OFFLOAD ENGINE SUBSYSTEM"
	<dmaengine@vger.kernel.org>,
	Dan Williams <dan.j.williams@intel.com>,
	"moderated list:ARM/Allwinner sunXi SoC support"
	<linux-arm-kernel@lists.infradead.org>
Subject: Re: [PATCH 2/2] drm: sun4i: hdmi: Add support for sun4i HDMI encoder audio
Date: Wed, 15 Jan 2020 14:23:25 +0200	[thread overview]
Message-ID: <03081515-efd8-f281-947a-29928c8d5923@olimex.com> (raw)
In-Reply-To: <20200115083233.7wedmnkj4ju4eccv@gilmour.lan>

Hi,

On 1/15/20 10:32 AM, Maxime Ripard wrote:
> Hi Stefan,
>
> On Tue, Jan 14, 2020 at 11:04:55AM +0200, Stefan Mavrodiev wrote:
>> On 1/10/20 6:26 PM, Maxime Ripard wrote:
>>> Hi,
>>>
>>> On Fri, Jan 10, 2020 at 04:11:40PM +0200, Stefan Mavrodiev wrote:
>>>> Add HDMI audio support for the sun4i-hdmi encoder, used on
>>>> the older Allwinner chips - A10, A20, A31.
>>>>
>>>> Most of the code is based on the BSP implementation. In it
>>>> dditional formats are supported (S20_3LE and S24_LE), however
>>>> there where some problems with them and only S16_LE is left.
>>>>
>>>> Signed-off-by: Stefan Mavrodiev <stefan@olimex.com>
>>>> ---
>>>>    drivers/gpu/drm/sun4i/Kconfig            |   1 +
>>>>    drivers/gpu/drm/sun4i/Makefile           |   1 +
>>>>    drivers/gpu/drm/sun4i/sun4i_hdmi.h       |  30 ++
>>>>    drivers/gpu/drm/sun4i/sun4i_hdmi_audio.c | 375 +++++++++++++++++++++++
>>>>    drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c   |   4 +
>>>>    5 files changed, 411 insertions(+)
>>>>    create mode 100644 drivers/gpu/drm/sun4i/sun4i_hdmi_audio.c
>>>>
>>>> diff --git a/drivers/gpu/drm/sun4i/Kconfig b/drivers/gpu/drm/sun4i/Kconfig
>>>> index 37e90e42943f..192b732b10cd 100644
>>>> --- a/drivers/gpu/drm/sun4i/Kconfig
>>>> +++ b/drivers/gpu/drm/sun4i/Kconfig
>>>> @@ -19,6 +19,7 @@ if DRM_SUN4I
>>>>    config DRM_SUN4I_HDMI
>>>>           tristate "Allwinner A10 HDMI Controller Support"
>>>>           default DRM_SUN4I
>>>> +       select SND_PCM_ELD
>>>>           help
>>>>    	  Choose this option if you have an Allwinner SoC with an HDMI
>>>>    	  controller.
>>>> diff --git a/drivers/gpu/drm/sun4i/Makefile b/drivers/gpu/drm/sun4i/Makefile
>>>> index 0d04f2447b01..e2d82b451c36 100644
>>>> --- a/drivers/gpu/drm/sun4i/Makefile
>>>> +++ b/drivers/gpu/drm/sun4i/Makefile
>>>> @@ -5,6 +5,7 @@ sun4i-frontend-y		+= sun4i_frontend.o
>>>>    sun4i-drm-y			+= sun4i_drv.o
>>>>    sun4i-drm-y			+= sun4i_framebuffer.o
>>>>
>>>> +sun4i-drm-hdmi-y		+= sun4i_hdmi_audio.o
>>>>    sun4i-drm-hdmi-y		+= sun4i_hdmi_ddc_clk.o
>>>>    sun4i-drm-hdmi-y		+= sun4i_hdmi_enc.o
>>>>    sun4i-drm-hdmi-y		+= sun4i_hdmi_i2c.o
>>>> diff --git a/drivers/gpu/drm/sun4i/sun4i_hdmi.h b/drivers/gpu/drm/sun4i/sun4i_hdmi.h
>>>> index 7ad3f06c127e..456964e681b0 100644
>>>> --- a/drivers/gpu/drm/sun4i/sun4i_hdmi.h
>>>> +++ b/drivers/gpu/drm/sun4i/sun4i_hdmi.h
>>>> @@ -42,7 +42,32 @@
>>>>    #define SUN4I_HDMI_VID_TIMING_POL_VSYNC		BIT(1)
>>>>    #define SUN4I_HDMI_VID_TIMING_POL_HSYNC		BIT(0)
>>>>
>>>> +#define SUN4I_HDMI_AUDIO_CTRL_REG	0x040
>>>> +#define SUN4I_HDMI_AUDIO_CTRL_ENABLE		BIT(31)
>>>> +#define SUN4I_HDMI_AUDIO_CTRL_RESET		BIT(30)
>>>> +
>>>> +#define SUN4I_HDMI_AUDIO_FMT_REG	0x048
>>>> +#define SUN4I_HDMI_AUDIO_FMT_SRC		BIT(31)
>>>> +#define SUN4I_HDMI_AUDIO_FMT_LAYOUT		BIT(3)
>>>> +#define SUN4I_HDMI_AUDIO_FMT_CH_CFG(n)		(n - 1)
>>> There's the issue multiple times in the headers, but you should wrap n
>>> in parentheses to make sure we have no issue with precedence when
>>> calling the macro.
>>>
>>>> +int sun4i_hdmi_audio_create(struct sun4i_hdmi *hdmi)
>>>> +{
>>>> +	struct snd_soc_card *card = &sun4i_hdmi_audio_card;
>>>> +	struct snd_soc_dai_link_component *comp;
>>>> +	struct snd_soc_dai_link *link;
>>>> +	int ret;
>>>> +
>>>> +	ret = devm_snd_dmaengine_pcm_register(hdmi->dev,
>>>> +					      &sun4i_hdmi_audio_pcm_config, 0);
>>>> +	if (ret) {
>>>> +		DRM_ERROR("Could not register PCM\n");
>>>> +		return ret;
>>>> +	}
>>>> +
>>>> +	ret = devm_snd_soc_register_component(hdmi->dev,
>>>> +					      &sun4i_hdmi_audio_component,
>>>> +					      &sun4i_hdmi_audio_dai, 1);
>>>> +	if (ret) {
>>>> +		DRM_ERROR("Could not register DAI\n");
>>>> +		return ret;
>>>> +	}
>>>> +
>>>> +	link = devm_kzalloc(hdmi->dev, sizeof(*link), GFP_KERNEL);
>>>> +	if (!link)
>>>> +		return -ENOMEM;
>>>> +
>>>> +	comp = devm_kzalloc(hdmi->dev, sizeof(*comp) * 3, GFP_KERNEL);
>>>> +	if (!comp)
>>>> +		return -ENOMEM;
>>>> +
>>>> +	link->cpus = &comp[0];
>>>> +	link->codecs = &comp[1];
>>>> +	link->platforms = &comp[2];
>>>> +
>>>> +	link->num_cpus = 1;
>>>> +	link->num_codecs = 1;
>>>> +	link->num_platforms = 1;
>>>> +
>>>> +	link->playback_only = 1;
>>>> +
>>>> +	link->name = "SUN4I-HDMI";
>>>> +	link->stream_name = "SUN4I-HDMI PCM";
>>>> +
>>>> +	link->codecs->name = dev_name(hdmi->dev);
>>>> +	link->codecs->dai_name	= sun4i_hdmi_audio_dai.name;
>>>> +
>>>> +	link->cpus->dai_name = dev_name(hdmi->dev);
>>>> +
>>>> +	link->platforms->name = dev_name(hdmi->dev);
>>>> +
>>>> +	link->dai_fmt = SND_SOC_DAIFMT_I2S;
>>>> +
>>>> +	card->dai_link = link;
>>>> +	card->num_links = 1;
>>>> +	card->dev = hdmi->dev;
>>>> +
>>>> +	snd_soc_card_set_drvdata(card, hdmi);
>>>> +	return devm_snd_soc_register_card(hdmi->dev, card);
>>> Out of curiosity, did you try to remove the module with that patch
>>> applied? IIRC, these functions will overwrite the device drvdata, and
>>> we will try to access them in unbind / remove.
>> Actually I did not. Just tried that and you're right. The module
>> crashes at the unbind call.  I use sun4i_hdmi struct only for
>> regmap. Maybe create separate private structure and copy only
>> regmap?
> I think the issue is that:
>
>    - In bind, we first call dev_set_drvdata on the bound device, with a
>      pointer to struct sun4i_hdmi as the value. The driver_data field
>      in struct device is now a pointer to our instance of struct
>      sun4i_hdmi.
>
>    - In audio create, you then call snd_soc_card_set_drvdata with a
>      pointer to struct sun4i_hdmi as the value. The drvdata field in
>      the struct snd_soc_card is now a pointer to our instance of struct
>      sun4i_hdmi (so far so good).
>
>    - Then you call (devm_)snd_soc_register_card. One of the thing that
>      it will do is call drv_set_drvdata on the card->dev device,
>      setting it to our pointer to the struct snd_soc_card we provided.
>      However, since you set card->dev to the same device than the one
>      initially bound, this means that you just overwrote the struct
>      sun4i_hdmi pointer with a pointer to struct snd_soc_card.
>
>    - The driver will operate properly, since we never really use the
>      driver_data field, in the HDMI driver, except when...
>
>    - At unbind, you retrieve the driver_data field, expecting a struct
>      sun4i_hdmi pointer, except you have a pointer to struct
>      snd_soc_card, and everything explodes.
>
> I think the way to work around that would be to create a new
> (platform_)device for the HDMI audio component, so that ASoC can work
> on that device instead.
>
> This seems to be what dw-hdmi is doing here:
> https://elixir.bootlin.com/linux/latest/source/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c#L2812
>
> (Except that they are also using platform_data, since they have
> multiple drivers, we wouldn't, so we can just lookup sun4i_hdmi using
> the parent's device driver_data).
>
>>>> +}
>>>> diff --git a/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c b/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c
>>>> index a7c4654445c7..79ecd89fb705 100644
>>>> --- a/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c
>>>> +++ b/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c
>>>> @@ -114,6 +114,9 @@ static void sun4i_hdmi_enable(struct drm_encoder *encoder)
>>>>    		val |= SUN4I_HDMI_VID_CTRL_HDMI_MODE;
>>>>
>>>>    	writel(val, hdmi->base + SUN4I_HDMI_VID_CTRL_REG);
>>>> +
>>>> +	if (hdmi->hdmi_audio && sun4i_hdmi_audio_create(hdmi))
>>>> +		DRM_ERROR("Couldn't create the HDMI audio adapter\n");
>>> So you create the audio card each time the display is enabled? I guess
>>> this is to deal with the hotplug?
>> Yes. See below.
>>
>>> I'm not sure this is the right thing to do. If I remember well, the
>>> ELD are here precisely to let userspace know that the display is
>>> plugged (and audio-capable) or not.
>>>
>>> Also, you don't remove that card in the disable, which mean that if
>>> you end up in a situation where you would enable the display, disable
>>> it and then enable it again, you have two audio cards now.
>> There is issue with the hotplug. When inserting the cable, the event
>> is detected and the hdmi encoder is enabled. Thus the card is
>> created. However further removal and insertions are not
>> detected.
> I guess we would need to fix that then?
>
>> This is why I don't remove the card.
>>
>> Also I count on devm_snd_soc_register_card() to release the card.
> I think you should really create the card all the time, and just
> update the ELD to let the userspace know when something has been
> created.
>
> And yeah, we should have a working hotplug, but that's a separate
> story :)

Thank you for the review. Soon I'll prepare v2.

Also I'll check the hotplug issue.

>
> Maxime

Best regards,
Stefan


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

WARNING: multiple messages have this Message-ID (diff)
From: Stefan Mavrodiev <stefan@olimex.com>
To: Maxime Ripard <mripard@kernel.org>, Stefan Mavrodiev <stefan@olimex.com>
Cc: David Airlie <airlied@linux.ie>,
	linux-sunxi@googlegroups.com, Vinod Koul <vkoul@kernel.org>,
	"open list:DRM DRIVERS FOR ALLWINNER A10"
	<dri-devel@lists.freedesktop.org>,
	open list <linux-kernel@vger.kernel.org>,
	Chen-Yu Tsai <wens@csie.org>,
	"open list:DMA GENERIC OFFLOAD ENGINE SUBSYSTEM"
	<dmaengine@vger.kernel.org>,
	Dan Williams <dan.j.williams@intel.com>,
	"moderated list:ARM/Allwinner sunXi SoC support"
	<linux-arm-kernel@lists.infradead.org>
Subject: Re: [PATCH 2/2] drm: sun4i: hdmi: Add support for sun4i HDMI encoder audio
Date: Wed, 15 Jan 2020 14:23:25 +0200	[thread overview]
Message-ID: <03081515-efd8-f281-947a-29928c8d5923@olimex.com> (raw)
In-Reply-To: <20200115083233.7wedmnkj4ju4eccv@gilmour.lan>

Hi,

On 1/15/20 10:32 AM, Maxime Ripard wrote:
> Hi Stefan,
>
> On Tue, Jan 14, 2020 at 11:04:55AM +0200, Stefan Mavrodiev wrote:
>> On 1/10/20 6:26 PM, Maxime Ripard wrote:
>>> Hi,
>>>
>>> On Fri, Jan 10, 2020 at 04:11:40PM +0200, Stefan Mavrodiev wrote:
>>>> Add HDMI audio support for the sun4i-hdmi encoder, used on
>>>> the older Allwinner chips - A10, A20, A31.
>>>>
>>>> Most of the code is based on the BSP implementation. In it
>>>> dditional formats are supported (S20_3LE and S24_LE), however
>>>> there where some problems with them and only S16_LE is left.
>>>>
>>>> Signed-off-by: Stefan Mavrodiev <stefan@olimex.com>
>>>> ---
>>>>    drivers/gpu/drm/sun4i/Kconfig            |   1 +
>>>>    drivers/gpu/drm/sun4i/Makefile           |   1 +
>>>>    drivers/gpu/drm/sun4i/sun4i_hdmi.h       |  30 ++
>>>>    drivers/gpu/drm/sun4i/sun4i_hdmi_audio.c | 375 +++++++++++++++++++++++
>>>>    drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c   |   4 +
>>>>    5 files changed, 411 insertions(+)
>>>>    create mode 100644 drivers/gpu/drm/sun4i/sun4i_hdmi_audio.c
>>>>
>>>> diff --git a/drivers/gpu/drm/sun4i/Kconfig b/drivers/gpu/drm/sun4i/Kconfig
>>>> index 37e90e42943f..192b732b10cd 100644
>>>> --- a/drivers/gpu/drm/sun4i/Kconfig
>>>> +++ b/drivers/gpu/drm/sun4i/Kconfig
>>>> @@ -19,6 +19,7 @@ if DRM_SUN4I
>>>>    config DRM_SUN4I_HDMI
>>>>           tristate "Allwinner A10 HDMI Controller Support"
>>>>           default DRM_SUN4I
>>>> +       select SND_PCM_ELD
>>>>           help
>>>>    	  Choose this option if you have an Allwinner SoC with an HDMI
>>>>    	  controller.
>>>> diff --git a/drivers/gpu/drm/sun4i/Makefile b/drivers/gpu/drm/sun4i/Makefile
>>>> index 0d04f2447b01..e2d82b451c36 100644
>>>> --- a/drivers/gpu/drm/sun4i/Makefile
>>>> +++ b/drivers/gpu/drm/sun4i/Makefile
>>>> @@ -5,6 +5,7 @@ sun4i-frontend-y		+= sun4i_frontend.o
>>>>    sun4i-drm-y			+= sun4i_drv.o
>>>>    sun4i-drm-y			+= sun4i_framebuffer.o
>>>>
>>>> +sun4i-drm-hdmi-y		+= sun4i_hdmi_audio.o
>>>>    sun4i-drm-hdmi-y		+= sun4i_hdmi_ddc_clk.o
>>>>    sun4i-drm-hdmi-y		+= sun4i_hdmi_enc.o
>>>>    sun4i-drm-hdmi-y		+= sun4i_hdmi_i2c.o
>>>> diff --git a/drivers/gpu/drm/sun4i/sun4i_hdmi.h b/drivers/gpu/drm/sun4i/sun4i_hdmi.h
>>>> index 7ad3f06c127e..456964e681b0 100644
>>>> --- a/drivers/gpu/drm/sun4i/sun4i_hdmi.h
>>>> +++ b/drivers/gpu/drm/sun4i/sun4i_hdmi.h
>>>> @@ -42,7 +42,32 @@
>>>>    #define SUN4I_HDMI_VID_TIMING_POL_VSYNC		BIT(1)
>>>>    #define SUN4I_HDMI_VID_TIMING_POL_HSYNC		BIT(0)
>>>>
>>>> +#define SUN4I_HDMI_AUDIO_CTRL_REG	0x040
>>>> +#define SUN4I_HDMI_AUDIO_CTRL_ENABLE		BIT(31)
>>>> +#define SUN4I_HDMI_AUDIO_CTRL_RESET		BIT(30)
>>>> +
>>>> +#define SUN4I_HDMI_AUDIO_FMT_REG	0x048
>>>> +#define SUN4I_HDMI_AUDIO_FMT_SRC		BIT(31)
>>>> +#define SUN4I_HDMI_AUDIO_FMT_LAYOUT		BIT(3)
>>>> +#define SUN4I_HDMI_AUDIO_FMT_CH_CFG(n)		(n - 1)
>>> There's the issue multiple times in the headers, but you should wrap n
>>> in parentheses to make sure we have no issue with precedence when
>>> calling the macro.
>>>
>>>> +int sun4i_hdmi_audio_create(struct sun4i_hdmi *hdmi)
>>>> +{
>>>> +	struct snd_soc_card *card = &sun4i_hdmi_audio_card;
>>>> +	struct snd_soc_dai_link_component *comp;
>>>> +	struct snd_soc_dai_link *link;
>>>> +	int ret;
>>>> +
>>>> +	ret = devm_snd_dmaengine_pcm_register(hdmi->dev,
>>>> +					      &sun4i_hdmi_audio_pcm_config, 0);
>>>> +	if (ret) {
>>>> +		DRM_ERROR("Could not register PCM\n");
>>>> +		return ret;
>>>> +	}
>>>> +
>>>> +	ret = devm_snd_soc_register_component(hdmi->dev,
>>>> +					      &sun4i_hdmi_audio_component,
>>>> +					      &sun4i_hdmi_audio_dai, 1);
>>>> +	if (ret) {
>>>> +		DRM_ERROR("Could not register DAI\n");
>>>> +		return ret;
>>>> +	}
>>>> +
>>>> +	link = devm_kzalloc(hdmi->dev, sizeof(*link), GFP_KERNEL);
>>>> +	if (!link)
>>>> +		return -ENOMEM;
>>>> +
>>>> +	comp = devm_kzalloc(hdmi->dev, sizeof(*comp) * 3, GFP_KERNEL);
>>>> +	if (!comp)
>>>> +		return -ENOMEM;
>>>> +
>>>> +	link->cpus = &comp[0];
>>>> +	link->codecs = &comp[1];
>>>> +	link->platforms = &comp[2];
>>>> +
>>>> +	link->num_cpus = 1;
>>>> +	link->num_codecs = 1;
>>>> +	link->num_platforms = 1;
>>>> +
>>>> +	link->playback_only = 1;
>>>> +
>>>> +	link->name = "SUN4I-HDMI";
>>>> +	link->stream_name = "SUN4I-HDMI PCM";
>>>> +
>>>> +	link->codecs->name = dev_name(hdmi->dev);
>>>> +	link->codecs->dai_name	= sun4i_hdmi_audio_dai.name;
>>>> +
>>>> +	link->cpus->dai_name = dev_name(hdmi->dev);
>>>> +
>>>> +	link->platforms->name = dev_name(hdmi->dev);
>>>> +
>>>> +	link->dai_fmt = SND_SOC_DAIFMT_I2S;
>>>> +
>>>> +	card->dai_link = link;
>>>> +	card->num_links = 1;
>>>> +	card->dev = hdmi->dev;
>>>> +
>>>> +	snd_soc_card_set_drvdata(card, hdmi);
>>>> +	return devm_snd_soc_register_card(hdmi->dev, card);
>>> Out of curiosity, did you try to remove the module with that patch
>>> applied? IIRC, these functions will overwrite the device drvdata, and
>>> we will try to access them in unbind / remove.
>> Actually I did not. Just tried that and you're right. The module
>> crashes at the unbind call.  I use sun4i_hdmi struct only for
>> regmap. Maybe create separate private structure and copy only
>> regmap?
> I think the issue is that:
>
>    - In bind, we first call dev_set_drvdata on the bound device, with a
>      pointer to struct sun4i_hdmi as the value. The driver_data field
>      in struct device is now a pointer to our instance of struct
>      sun4i_hdmi.
>
>    - In audio create, you then call snd_soc_card_set_drvdata with a
>      pointer to struct sun4i_hdmi as the value. The drvdata field in
>      the struct snd_soc_card is now a pointer to our instance of struct
>      sun4i_hdmi (so far so good).
>
>    - Then you call (devm_)snd_soc_register_card. One of the thing that
>      it will do is call drv_set_drvdata on the card->dev device,
>      setting it to our pointer to the struct snd_soc_card we provided.
>      However, since you set card->dev to the same device than the one
>      initially bound, this means that you just overwrote the struct
>      sun4i_hdmi pointer with a pointer to struct snd_soc_card.
>
>    - The driver will operate properly, since we never really use the
>      driver_data field, in the HDMI driver, except when...
>
>    - At unbind, you retrieve the driver_data field, expecting a struct
>      sun4i_hdmi pointer, except you have a pointer to struct
>      snd_soc_card, and everything explodes.
>
> I think the way to work around that would be to create a new
> (platform_)device for the HDMI audio component, so that ASoC can work
> on that device instead.
>
> This seems to be what dw-hdmi is doing here:
> https://elixir.bootlin.com/linux/latest/source/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c#L2812
>
> (Except that they are also using platform_data, since they have
> multiple drivers, we wouldn't, so we can just lookup sun4i_hdmi using
> the parent's device driver_data).
>
>>>> +}
>>>> diff --git a/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c b/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c
>>>> index a7c4654445c7..79ecd89fb705 100644
>>>> --- a/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c
>>>> +++ b/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c
>>>> @@ -114,6 +114,9 @@ static void sun4i_hdmi_enable(struct drm_encoder *encoder)
>>>>    		val |= SUN4I_HDMI_VID_CTRL_HDMI_MODE;
>>>>
>>>>    	writel(val, hdmi->base + SUN4I_HDMI_VID_CTRL_REG);
>>>> +
>>>> +	if (hdmi->hdmi_audio && sun4i_hdmi_audio_create(hdmi))
>>>> +		DRM_ERROR("Couldn't create the HDMI audio adapter\n");
>>> So you create the audio card each time the display is enabled? I guess
>>> this is to deal with the hotplug?
>> Yes. See below.
>>
>>> I'm not sure this is the right thing to do. If I remember well, the
>>> ELD are here precisely to let userspace know that the display is
>>> plugged (and audio-capable) or not.
>>>
>>> Also, you don't remove that card in the disable, which mean that if
>>> you end up in a situation where you would enable the display, disable
>>> it and then enable it again, you have two audio cards now.
>> There is issue with the hotplug. When inserting the cable, the event
>> is detected and the hdmi encoder is enabled. Thus the card is
>> created. However further removal and insertions are not
>> detected.
> I guess we would need to fix that then?
>
>> This is why I don't remove the card.
>>
>> Also I count on devm_snd_soc_register_card() to release the card.
> I think you should really create the card all the time, and just
> update the ELD to let the userspace know when something has been
> created.
>
> And yeah, we should have a working hotplug, but that's a separate
> story :)

Thank you for the review. Soon I'll prepare v2.

Also I'll check the hotplug issue.

>
> Maxime

Best regards,
Stefan

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

  reply	other threads:[~2020-01-15 12:23 UTC|newest]

Thread overview: 42+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-01-10 14:11 [PATCH 0/2] Add support for sun4i HDMI audio Stefan Mavrodiev
2020-01-10 14:11 ` Stefan Mavrodiev
2020-01-10 14:11 ` Stefan Mavrodiev
2020-01-10 14:11 ` [PATCH 1/2] dmaengine: sun4i: Add support for cyclic requests with dedicated DMA Stefan Mavrodiev
2020-01-10 14:11   ` Stefan Mavrodiev
2020-01-10 14:11   ` Stefan Mavrodiev
2020-01-10 16:18   ` Maxime Ripard
2020-01-10 16:18     ` Maxime Ripard
2020-01-10 16:18     ` Maxime Ripard
2020-01-15 12:31   ` Vinod Koul
2020-01-15 12:31     ` Vinod Koul
2020-01-15 12:31     ` Vinod Koul
2020-01-15 17:07     ` Maxime Ripard
2020-01-15 17:07       ` Maxime Ripard
2020-01-15 17:07       ` Maxime Ripard
2020-01-21  8:35       ` Vinod Koul
2020-01-21  8:35         ` Vinod Koul
2020-01-21  8:35         ` Vinod Koul
2020-01-21 11:37         ` Stefan Mavrodiev
2020-01-21 11:37           ` Stefan Mavrodiev
2020-01-21 11:37           ` Stefan Mavrodiev
2020-01-21 12:14           ` Vinod Koul
2020-01-21 12:14             ` Vinod Koul
2020-01-21 12:14             ` Vinod Koul
2020-01-10 14:11 ` [PATCH 2/2] drm: sun4i: hdmi: Add support for sun4i HDMI encoder audio Stefan Mavrodiev
2020-01-10 14:11   ` Stefan Mavrodiev
2020-01-10 14:11   ` Stefan Mavrodiev
2020-01-10 16:26   ` Maxime Ripard
2020-01-10 16:26     ` Maxime Ripard
2020-01-10 16:26     ` Maxime Ripard
2020-01-14  9:04     ` Stefan Mavrodiev
2020-01-14  9:04       ` Stefan Mavrodiev
2020-01-14  9:04       ` Stefan Mavrodiev
2020-01-15  8:32       ` Maxime Ripard
2020-01-15  8:32         ` Maxime Ripard
2020-01-15  8:32         ` Maxime Ripard
2020-01-15 12:23         ` Stefan Mavrodiev [this message]
2020-01-15 12:23           ` Stefan Mavrodiev
2020-01-15 12:23           ` Stefan Mavrodiev
2020-01-10 16:30   ` [linux-sunxi] " Chen-Yu Tsai
2020-01-10 16:30     ` Chen-Yu Tsai
2020-01-10 16:30     ` Chen-Yu Tsai

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=03081515-efd8-f281-947a-29928c8d5923@olimex.com \
    --to=stefan@olimex.com \
    --cc=airlied@linux.ie \
    --cc=dan.j.williams@intel.com \
    --cc=daniel@ffwll.ch \
    --cc=dmaengine@vger.kernel.org \
    --cc=dri-devel@lists.freedesktop.org \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-sunxi@googlegroups.com \
    --cc=mripard@kernel.org \
    --cc=vkoul@kernel.org \
    --cc=wens@csie.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.