diff mbox

[1/3] drm: bridge: add DesignWare HDMI I2S audio support

Message ID 8737o3xqab.wl%kuninori.morimoto.gx@renesas.com (mailing list archive)
State New, archived
Headers show

Commit Message

Kuninori Morimoto June 24, 2016, 2:40 a.m. UTC
From: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>

Current dw-hdmi is supporting sound via AHB bus, but it has
I2S audio feature too. This patch adds I2S audio support to dw-hdmi.
This HDMI I2S is supported by using ALSA SoC common HDMI encoder
driver.

Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
---
 drivers/gpu/drm/bridge/Kconfig             |   8 ++
 drivers/gpu/drm/bridge/Makefile            |   1 +
 drivers/gpu/drm/bridge/dw-hdmi-audio.h     |   7 ++
 drivers/gpu/drm/bridge/dw-hdmi-i2s-audio.c | 123 +++++++++++++++++++++++++++++
 drivers/gpu/drm/bridge/dw-hdmi.c           |  22 +++++-
 drivers/gpu/drm/bridge/dw-hdmi.h           |  21 +++++
 6 files changed, 180 insertions(+), 2 deletions(-)
 create mode 100644 drivers/gpu/drm/bridge/dw-hdmi-i2s-audio.c

Comments

Yakir Yang June 29, 2016, 9:50 a.m. UTC | #1
Kuninori,

On 06/24/2016 10:40 AM, Kuninori Morimoto wrote:
> From: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
>
> Current dw-hdmi is supporting sound via AHB bus, but it has
> I2S audio feature too. This patch adds I2S audio support to dw-hdmi.
> This HDMI I2S is supported by using ALSA SoC common HDMI encoder
> driver.
>
> Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
> ---
>   drivers/gpu/drm/bridge/Kconfig             |   8 ++
>   drivers/gpu/drm/bridge/Makefile            |   1 +
>   drivers/gpu/drm/bridge/dw-hdmi-audio.h     |   7 ++
>   drivers/gpu/drm/bridge/dw-hdmi-i2s-audio.c | 123 +++++++++++++++++++++++++++++
>   drivers/gpu/drm/bridge/dw-hdmi.c           |  22 +++++-
>   drivers/gpu/drm/bridge/dw-hdmi.h           |  21 +++++
>   6 files changed, 180 insertions(+), 2 deletions(-)
>   create mode 100644 drivers/gpu/drm/bridge/dw-hdmi-i2s-audio.c
>
> diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
> index 8f7423f..8e2a22d 100644
> --- a/drivers/gpu/drm/bridge/Kconfig
> +++ b/drivers/gpu/drm/bridge/Kconfig
> @@ -32,6 +32,14 @@ config DRM_DW_HDMI_AHB_AUDIO
>   	  Designware HDMI block.  This is used in conjunction with
>   	  the i.MX6 HDMI driver.
>   
> +config DRM_DW_HDMI_I2S_AUDIO
> +	tristate "Synopsis Designware I2S Audio interface"
> +	depends on DRM_DW_HDMI
> +	select SND_SOC_HDMI_CODEC
> +	help
> +	  Support the I2S Audio interface which is part of the Synopsis
> +	  Designware HDMI block.
> +
>   config DRM_NXP_PTN3460
>   	tristate "NXP PTN3460 DP/LVDS bridge"
>   	depends on OF
> diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile
> index 96b13b3..1af92ad 100644
> --- a/drivers/gpu/drm/bridge/Makefile
> +++ b/drivers/gpu/drm/bridge/Makefile
> @@ -3,6 +3,7 @@ ccflags-y := -Iinclude/drm
>   obj-$(CONFIG_DRM_ANALOGIX_ANX78XX) += analogix-anx78xx.o
>   obj-$(CONFIG_DRM_DW_HDMI) += dw-hdmi.o
>   obj-$(CONFIG_DRM_DW_HDMI_AHB_AUDIO) += dw-hdmi-ahb-audio.o
> +obj-$(CONFIG_DRM_DW_HDMI_I2S_AUDIO) += dw-hdmi-i2s-audio.o
>   obj-$(CONFIG_DRM_NXP_PTN3460) += nxp-ptn3460.o
>   obj-$(CONFIG_DRM_PARADE_PS8622) += parade-ps8622.o
>   obj-$(CONFIG_DRM_ANALOGIX_DP) += analogix/
> diff --git a/drivers/gpu/drm/bridge/dw-hdmi-audio.h b/drivers/gpu/drm/bridge/dw-hdmi-audio.h
> index 91f631b..fd1f745 100644
> --- a/drivers/gpu/drm/bridge/dw-hdmi-audio.h
> +++ b/drivers/gpu/drm/bridge/dw-hdmi-audio.h
> @@ -11,4 +11,11 @@ struct dw_hdmi_audio_data {
>   	u8 *eld;
>   };
>   
> +struct dw_hdmi_i2s_audio_data {
> +	struct dw_hdmi *hdmi;
> +
> +	void (*write)(struct dw_hdmi *hdmi, u8 val, int offset);
> +	u8 (*read)(struct dw_hdmi *hdmi, int offset);
> +};
> +
>   #endif
> diff --git a/drivers/gpu/drm/bridge/dw-hdmi-i2s-audio.c b/drivers/gpu/drm/bridge/dw-hdmi-i2s-audio.c
> new file mode 100644
> index 0000000..df1519c
> --- /dev/null
> +++ b/drivers/gpu/drm/bridge/dw-hdmi-i2s-audio.c
> @@ -0,0 +1,123 @@
> +/*
> + * dw-hdmi-i2s-audio.c
> + *
> + * Copyright (c) 2016 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +#include <drm/bridge/dw_hdmi.h>
> +
> +#include <sound/hdmi-codec.h>
> +
> +#include "dw-hdmi.h"
> +#include "dw-hdmi-audio.h"
> +
> +#define DRIVER_NAME "dw-hdmi-i2s-audio"
> +
> +static inline void hdmi_write(struct dw_hdmi_i2s_audio_data *audio, u8 val, int offset)
> +{
> +	struct dw_hdmi *hdmi = audio->hdmi;
> +
> +	audio->write(hdmi, val, offset);
> +}
> +
> +static inline u8 hdmi_read(struct dw_hdmi_i2s_audio_data *audio, int offset)
> +{
> +	struct dw_hdmi *hdmi = audio->hdmi;
> +
> +	return audio->read(hdmi, offset);
> +}
> +
> +static int dw_hdmi_i2s_hw_params(struct device *dev, void *data,
> +				 struct hdmi_codec_daifmt *fmt,
> +				 struct hdmi_codec_params *hparms)
> +{
> +	struct dw_hdmi_i2s_audio_data *audio = data;
> +	struct dw_hdmi *hdmi = audio->hdmi;
> +	u8 conf0 = 0;
> +	u8 conf1 = 0;
> +	u8 inputclkfs = 0;
> +
> +	/* it cares I2S only */
> +	if ((fmt->fmt != HDMI_I2S) ||
> +	    (fmt->bit_clk_master | fmt->frame_clk_master)) {
> +		dev_err(dev, "unsupported format/settings\n");
> +		return -EINVAL;
> +	}
> +
> +	inputclkfs	= HDMI_AUD_INPUTCLKFS_64FS;
> +	conf0		= HDMI_AUD_CONF0_I2S_ALL_ENABLE;
> +
> +	switch(hparms->sample_width) {
> +	case 16:
> +		conf1 = HDMI_AUD_CONF1_WIDTH_16;
> +		break;
> +	case 24:
> +	case 32:
> +		conf1 = HDMI_AUD_CONF1_WIDTH_24;
> +		break;
> +	}
> +
> +	dw_hdmi_set_sample_rate(hdmi, hparms->sample_rate);
> +
> +	hdmi_write(audio, inputclkfs, HDMI_AUD_INPUTCLKFS);
> +	hdmi_write(audio, conf0, HDMI_AUD_CONF0);
> +	hdmi_write(audio, conf1, HDMI_AUD_CONF1);
> +
> +	dw_hdmi_audio_enable(hdmi);
> +
> +	return 0;
> +}
> +
> +static void dw_hdmi_i2s_audio_shutdown(struct device *dev, void *data)
> +{
> +	struct dw_hdmi_i2s_audio_data *audio = data;
> +	struct dw_hdmi *hdmi = audio->hdmi;
> +
> +	dw_hdmi_audio_disable(hdmi);
> +
> +	hdmi_write(audio, HDMI_AUD_CONF0_SW_RESET, HDMI_AUD_CONF0);
> +}
> +
> +static struct hdmi_codec_ops dw_hdmi_i2s_ops = {
> +	.hw_params	= dw_hdmi_i2s_hw_params,
> +	.audio_shutdown	= dw_hdmi_i2s_audio_shutdown,
> +};
> +
> +static int snd_dw_hdmi_probe(struct platform_device *pdev)
> +{
> +	struct dw_hdmi_i2s_audio_data *audio = pdev->dev.platform_data;
> +	struct platform_device_info pdevinfo;
> +	struct hdmi_codec_pdata pdata;
> +
> +	pdata.ops		= &dw_hdmi_i2s_ops;
> +	pdata.i2s		= 1;
> +	pdata.max_i2s_channels	= 6;
> +	pdata.data		= audio;
> +
> +	memset(&pdevinfo, 0, sizeof(pdevinfo));
> +	pdevinfo.parent		= pdev->dev.parent;
> +	pdevinfo.id		= PLATFORM_DEVID_AUTO;
> +	pdevinfo.name		= HDMI_CODEC_DRV_NAME;
> +	pdevinfo.data		= &pdata;
> +	pdevinfo.size_data	= sizeof(pdata);
> +	pdevinfo.dma_mask	= DMA_BIT_MASK(32);
> +
> +	return IS_ERR_OR_NULL(platform_device_register_full(&pdevinfo));
> +}
> +
> +static struct platform_driver snd_dw_hdmi_driver = {
> +	.probe	= snd_dw_hdmi_probe,
> +	.driver	= {
> +		.name = DRIVER_NAME,
> +		.owner = THIS_MODULE,
> +	},
> +};
> +module_platform_driver(snd_dw_hdmi_driver);
> +

I don't think you need a separate driver for dw-hdmi i2s-audio, you can 
just place then into the main dw-hdmi driver (just like dw-hdmi ahb-audio).

And then you don't need to add an external point into hdmi-codec for 
storing the 'struct dw_hdmi_i2s_audio_data'.

Cheers,
- Yakir

> +MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
> +MODULE_DESCRIPTION("Synopsis Designware HDMI I2S ALSA SoC interface");
> +MODULE_LICENSE("GPL v2");
> +MODULE_ALIAS("platform:" DRIVER_NAME);
> diff --git a/drivers/gpu/drm/bridge/dw-hdmi.c b/drivers/gpu/drm/bridge/dw-hdmi.c
> index 55e73e8..e9ba59e 100644
> --- a/drivers/gpu/drm/bridge/dw-hdmi.c
> +++ b/drivers/gpu/drm/bridge/dw-hdmi.c
> @@ -2013,10 +2013,11 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
>   	struct device_node *np = dev->of_node;
>   	struct platform_device_info pdevinfo;
>   	struct device_node *ddc_node;
> -	struct dw_hdmi_audio_data audio;
>   	struct dw_hdmi *hdmi;
>   	int ret;
>   	u32 val = 1;
> +	u8 config0;
> +	u8 config1;
>   
>   	hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL);
>   	if (!hdmi)
> @@ -2185,7 +2186,12 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
>   	pdevinfo.parent = dev;
>   	pdevinfo.id = PLATFORM_DEVID_AUTO;
>   
> -	if (hdmi_readb(hdmi, HDMI_CONFIG1_ID) & HDMI_CONFIG1_AHB) {
> +	config0 = hdmi_readb(hdmi, HDMI_CONFIG0_ID);
> +	config1 = hdmi_readb(hdmi, HDMI_CONFIG1_ID);
> +
> +	if (config1 & HDMI_CONFIG1_AHB) {
> +		struct dw_hdmi_audio_data audio;
> +
>   		audio.phys = iores->start;
>   		audio.base = hdmi->regs;
>   		audio.irq = irq;
> @@ -2197,6 +2203,18 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
>   		pdevinfo.size_data = sizeof(audio);
>   		pdevinfo.dma_mask = DMA_BIT_MASK(32);
>   		hdmi->audio = platform_device_register_full(&pdevinfo);
> +	} else if (config0 & HDMI_CONFIG0_I2S) {
> +		struct dw_hdmi_i2s_audio_data audio;
> +
> +		audio.hdmi	= hdmi;
> +		audio.write	= hdmi_writeb;
> +		audio.read	= hdmi_readb;
> +
> +		pdevinfo.name = "dw-hdmi-i2s-audio";
> +		pdevinfo.data = &audio;
> +		pdevinfo.size_data = sizeof(audio);
> +		pdevinfo.dma_mask = DMA_BIT_MASK(32);
> +		hdmi->audio = platform_device_register_full(&pdevinfo);
>   	}
>   
>   	/* Unmute I2CM interrupts and reset HDMI DDC I2C master controller */
> diff --git a/drivers/gpu/drm/bridge/dw-hdmi.h b/drivers/gpu/drm/bridge/dw-hdmi.h
> index fc9a560..c8bdbf2 100644
> --- a/drivers/gpu/drm/bridge/dw-hdmi.h
> +++ b/drivers/gpu/drm/bridge/dw-hdmi.h
> @@ -545,6 +545,10 @@
>   #define HDMI_I2CM_FS_SCL_LCNT_0_ADDR            0x7E12
>   
>   enum {
> +
> +/* CONFIG0_ID field values */
> +	HDMI_CONFIG0_I2S = 0x10,
> +
>   /* CONFIG1_ID field values */
>   	HDMI_CONFIG1_AHB = 0x01,
>   
> @@ -887,6 +891,17 @@ enum {
>   	HDMI_PHY_I2CM_CTLINT_ADDR_ARBITRATION_POL = 0x08,
>   	HDMI_PHY_I2CM_CTLINT_ADDR_ARBITRATION_MASK = 0x04,
>   
> +/* AUD_CONF0 field values */
> +	HDMI_AUD_CONF0_SW_RESET = 0x80,
> +	HDMI_AUD_CONF0_I2S_ALL_ENABLE = 0x2F,
> +
> +/* AUD_CONF1 field values */
> +	HDMI_AUD_CONF1_MODE_I2S = 0x00,
> +	HDMI_AUD_CONF1_MODE_RIGHT_J = 0x02,
> +	HDMI_AUD_CONF1_MODE_LEFT_J = 0x04,
> +	HDMI_AUD_CONF1_WIDTH_16 = 0x10,
> +	HDMI_AUD_CONF1_WIDTH_24 = 0x18,
> +
>   /* AUD_CTS3 field values */
>   	HDMI_AUD_CTS3_N_SHIFT_OFFSET = 5,
>   	HDMI_AUD_CTS3_N_SHIFT_MASK = 0xe0,
> @@ -901,6 +916,12 @@ enum {
>   	HDMI_AUD_CTS3_CTS_MANUAL = 0x10,
>   	HDMI_AUD_CTS3_AUDCTS19_16_MASK = 0x0f,
>   
> +/* HDMI_AUD_INPUTCLKFS field values */
> +	HDMI_AUD_INPUTCLKFS_128FS = 0,
> +	HDMI_AUD_INPUTCLKFS_256FS = 1,
> +	HDMI_AUD_INPUTCLKFS_512FS = 2,
> +	HDMI_AUD_INPUTCLKFS_64FS = 4,
> +
>   /* AHB_DMA_CONF0 field values */
>   	HDMI_AHB_DMA_CONF0_SW_FIFO_RST_OFFSET = 7,
>   	HDMI_AHB_DMA_CONF0_SW_FIFO_RST_MASK = 0x80,
Jose Abreu July 28, 2016, 4:05 p.m. UTC | #2
Hi,


On 24-06-2016 03:40, Kuninori Morimoto wrote:
> From: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
>
> Current dw-hdmi is supporting sound via AHB bus, but it has
> I2S audio feature too. This patch adds I2S audio support to dw-hdmi.
> This HDMI I2S is supported by using ALSA SoC common HDMI encoder
> driver.
>
> Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
> ---
>

I just tested this patch and everything seems ok. Should I give
my tested-by?

Best regards,
Jose Miguel Abreu
Kuninori Morimoto Aug. 1, 2016, 4:57 a.m. UTC | #3
Hi Jose
Cc: Mark, Thierry, Daniel

> > From: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
> >
> > Current dw-hdmi is supporting sound via AHB bus, but it has
> > I2S audio feature too. This patch adds I2S audio support to dw-hdmi.
> > This HDMI I2S is supported by using ALSA SoC common HDMI encoder
> > driver.
> >
> > Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
> > ---
> >
> 
> I just tested this patch and everything seems ok. Should I give
> my tested-by?

Thank you for your test. I'm happy if it could have it.

Mark, Thierry, Daniel
I wonder who can be maintainer for this patch ??

Best regards
---
Kuninori Morimoto
Jose Abreu Aug. 1, 2016, 7:45 a.m. UTC | #4
Hi,

On 01-08-2016 05:57, Kuninori Morimoto wrote:
> Hi Jose
> Cc: Mark, Thierry, Daniel
>
>>> From: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
>>>
>>> Current dw-hdmi is supporting sound via AHB bus, but it has
>>> I2S audio feature too. This patch adds I2S audio support to dw-hdmi.
>>> This HDMI I2S is supported by using ALSA SoC common HDMI encoder
>>> driver.
>>>
>>> Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
>>> ---
>>>
>> I just tested this patch and everything seems ok. Should I give
>> my tested-by?
> Thank you for your test. I'm happy if it could have it.
>
> Mark, Thierry, Daniel
> I wonder who can be maintainer for this patch ??
>
> Best regards
> ---
> Kuninori Morimoto

Tested-by: Jose Abreu <joabreu@synopsys.com>

Best regards,
Jose Miguel Abreu
Russell King (Oracle) Aug. 1, 2016, 2:39 p.m. UTC | #5
On Fri, Jun 24, 2016 at 11:40:44AM +0900, Kuninori Morimoto wrote:
> +static int snd_dw_hdmi_probe(struct platform_device *pdev)
> +{
> +	struct dw_hdmi_i2s_audio_data *audio = pdev->dev.platform_data;
> +	struct platform_device_info pdevinfo;
> +	struct hdmi_codec_pdata pdata;
> +
> +	pdata.ops		= &dw_hdmi_i2s_ops;
> +	pdata.i2s		= 1;
> +	pdata.max_i2s_channels	= 6;
> +	pdata.data		= audio;
> +
> +	memset(&pdevinfo, 0, sizeof(pdevinfo));
> +	pdevinfo.parent		= pdev->dev.parent;
> +	pdevinfo.id		= PLATFORM_DEVID_AUTO;
> +	pdevinfo.name		= HDMI_CODEC_DRV_NAME;
> +	pdevinfo.data		= &pdata;
> +	pdevinfo.size_data	= sizeof(pdata);
> +	pdevinfo.dma_mask	= DMA_BIT_MASK(32);
> +
> +	return IS_ERR_OR_NULL(platform_device_register_full(&pdevinfo));

This is certainly wrong.  You're returning a 0/1 value rather than
an error code when platform_device_register_full() fails.
Mark Brown Aug. 1, 2016, 5:45 p.m. UTC | #6
On Mon, Aug 01, 2016 at 04:57:07AM +0000, Kuninori Morimoto wrote:

> Mark, Thierry, Daniel
> I wonder who can be maintainer for this patch ??

It's a DRM patch so I'd expect someone in the DRM subsystem.
Kuninori Morimoto Aug. 2, 2016, 12:46 a.m. UTC | #7
Hi Russell

> > +static int snd_dw_hdmi_probe(struct platform_device *pdev)
> > +{
> > +	struct dw_hdmi_i2s_audio_data *audio = pdev->dev.platform_data;
> > +	struct platform_device_info pdevinfo;
> > +	struct hdmi_codec_pdata pdata;
> > +
> > +	pdata.ops		= &dw_hdmi_i2s_ops;
> > +	pdata.i2s		= 1;
> > +	pdata.max_i2s_channels	= 6;
> > +	pdata.data		= audio;
> > +
> > +	memset(&pdevinfo, 0, sizeof(pdevinfo));
> > +	pdevinfo.parent		= pdev->dev.parent;
> > +	pdevinfo.id		= PLATFORM_DEVID_AUTO;
> > +	pdevinfo.name		= HDMI_CODEC_DRV_NAME;
> > +	pdevinfo.data		= &pdata;
> > +	pdevinfo.size_data	= sizeof(pdata);
> > +	pdevinfo.dma_mask	= DMA_BIT_MASK(32);
> > +
> > +	return IS_ERR_OR_NULL(platform_device_register_full(&pdevinfo));
> 
> This is certainly wrong.  You're returning a 0/1 value rather than
> an error code when platform_device_register_full() fails.

Thanks.
I forgot why I added this ??
I will fix it on v2 patch
Kuninori Morimoto Aug. 2, 2016, 12:47 a.m. UTC | #8
Hi Mark

> > Mark, Thierry, Daniel
> > I wonder who can be maintainer for this patch ??
> 
> It's a DRM patch so I'd expect someone in the DRM subsystem.

OK, I see.
But, I will keep Cc to you for this patch-set.
Daniel Vetter Aug. 2, 2016, 1:14 p.m. UTC | #9
On Tue, Aug 02, 2016 at 12:47:46AM +0000, Kuninori Morimoto wrote:
> 
> Hi Mark
> 
> > > Mark, Thierry, Daniel
> > > I wonder who can be maintainer for this patch ??
> > 
> > It's a DRM patch so I'd expect someone in the DRM subsystem.
> 
> OK, I see.
> But, I will keep Cc to you for this patch-set.

Archit Tajena is the maintainer of last resort for anything related to
drm_bridge.
-Daniel
Kuninori Morimoto Aug. 3, 2016, 1:29 a.m. UTC | #10
Hi Daniel

> > > > Mark, Thierry, Daniel
> > > > I wonder who can be maintainer for this patch ??
> > > 
> > > It's a DRM patch so I'd expect someone in the DRM subsystem.
> > 
> > OK, I see.
> > But, I will keep Cc to you for this patch-set.
> 
> Archit Tajena is the maintainer of last resort for anything related to
> drm_bridge.

Thank you.
I will re-post these patches with To: Archit
Mark Brown Aug. 5, 2016, 11:13 a.m. UTC | #11
On Tue, Aug 02, 2016 at 03:14:57PM +0200, Daniel Vetter wrote:

> Archit Tajena is the maintainer of last resort for anything related to
> drm_bridge.

That's Archit Taneja <architt@codeaurora.org> (note spelling of surname)
for anyone else who's confused.  Can we get him listed in MAINTAINERS
please so it's easier for people to work this out and send patches to
him?
diff mbox

Patch

diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
index 8f7423f..8e2a22d 100644
--- a/drivers/gpu/drm/bridge/Kconfig
+++ b/drivers/gpu/drm/bridge/Kconfig
@@ -32,6 +32,14 @@  config DRM_DW_HDMI_AHB_AUDIO
 	  Designware HDMI block.  This is used in conjunction with
 	  the i.MX6 HDMI driver.
 
+config DRM_DW_HDMI_I2S_AUDIO
+	tristate "Synopsis Designware I2S Audio interface"
+	depends on DRM_DW_HDMI
+	select SND_SOC_HDMI_CODEC
+	help
+	  Support the I2S Audio interface which is part of the Synopsis
+	  Designware HDMI block.
+
 config DRM_NXP_PTN3460
 	tristate "NXP PTN3460 DP/LVDS bridge"
 	depends on OF
diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile
index 96b13b3..1af92ad 100644
--- a/drivers/gpu/drm/bridge/Makefile
+++ b/drivers/gpu/drm/bridge/Makefile
@@ -3,6 +3,7 @@  ccflags-y := -Iinclude/drm
 obj-$(CONFIG_DRM_ANALOGIX_ANX78XX) += analogix-anx78xx.o
 obj-$(CONFIG_DRM_DW_HDMI) += dw-hdmi.o
 obj-$(CONFIG_DRM_DW_HDMI_AHB_AUDIO) += dw-hdmi-ahb-audio.o
+obj-$(CONFIG_DRM_DW_HDMI_I2S_AUDIO) += dw-hdmi-i2s-audio.o
 obj-$(CONFIG_DRM_NXP_PTN3460) += nxp-ptn3460.o
 obj-$(CONFIG_DRM_PARADE_PS8622) += parade-ps8622.o
 obj-$(CONFIG_DRM_ANALOGIX_DP) += analogix/
diff --git a/drivers/gpu/drm/bridge/dw-hdmi-audio.h b/drivers/gpu/drm/bridge/dw-hdmi-audio.h
index 91f631b..fd1f745 100644
--- a/drivers/gpu/drm/bridge/dw-hdmi-audio.h
+++ b/drivers/gpu/drm/bridge/dw-hdmi-audio.h
@@ -11,4 +11,11 @@  struct dw_hdmi_audio_data {
 	u8 *eld;
 };
 
+struct dw_hdmi_i2s_audio_data {
+	struct dw_hdmi *hdmi;
+
+	void (*write)(struct dw_hdmi *hdmi, u8 val, int offset);
+	u8 (*read)(struct dw_hdmi *hdmi, int offset);
+};
+
 #endif
diff --git a/drivers/gpu/drm/bridge/dw-hdmi-i2s-audio.c b/drivers/gpu/drm/bridge/dw-hdmi-i2s-audio.c
new file mode 100644
index 0000000..df1519c
--- /dev/null
+++ b/drivers/gpu/drm/bridge/dw-hdmi-i2s-audio.c
@@ -0,0 +1,123 @@ 
+/*
+ * dw-hdmi-i2s-audio.c
+ *
+ * Copyright (c) 2016 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <drm/bridge/dw_hdmi.h>
+
+#include <sound/hdmi-codec.h>
+
+#include "dw-hdmi.h"
+#include "dw-hdmi-audio.h"
+
+#define DRIVER_NAME "dw-hdmi-i2s-audio"
+
+static inline void hdmi_write(struct dw_hdmi_i2s_audio_data *audio, u8 val, int offset)
+{
+	struct dw_hdmi *hdmi = audio->hdmi;
+
+	audio->write(hdmi, val, offset);
+}
+
+static inline u8 hdmi_read(struct dw_hdmi_i2s_audio_data *audio, int offset)
+{
+	struct dw_hdmi *hdmi = audio->hdmi;
+
+	return audio->read(hdmi, offset);
+}
+
+static int dw_hdmi_i2s_hw_params(struct device *dev, void *data,
+				 struct hdmi_codec_daifmt *fmt,
+				 struct hdmi_codec_params *hparms)
+{
+	struct dw_hdmi_i2s_audio_data *audio = data;
+	struct dw_hdmi *hdmi = audio->hdmi;
+	u8 conf0 = 0;
+	u8 conf1 = 0;
+	u8 inputclkfs = 0;
+
+	/* it cares I2S only */
+	if ((fmt->fmt != HDMI_I2S) ||
+	    (fmt->bit_clk_master | fmt->frame_clk_master)) {
+		dev_err(dev, "unsupported format/settings\n");
+		return -EINVAL;
+	}
+
+	inputclkfs	= HDMI_AUD_INPUTCLKFS_64FS;
+	conf0		= HDMI_AUD_CONF0_I2S_ALL_ENABLE;
+
+	switch(hparms->sample_width) {
+	case 16:
+		conf1 = HDMI_AUD_CONF1_WIDTH_16;
+		break;
+	case 24:
+	case 32:
+		conf1 = HDMI_AUD_CONF1_WIDTH_24;
+		break;
+	}
+
+	dw_hdmi_set_sample_rate(hdmi, hparms->sample_rate);
+
+	hdmi_write(audio, inputclkfs, HDMI_AUD_INPUTCLKFS);
+	hdmi_write(audio, conf0, HDMI_AUD_CONF0);
+	hdmi_write(audio, conf1, HDMI_AUD_CONF1);
+
+	dw_hdmi_audio_enable(hdmi);
+
+	return 0;
+}
+
+static void dw_hdmi_i2s_audio_shutdown(struct device *dev, void *data)
+{
+	struct dw_hdmi_i2s_audio_data *audio = data;
+	struct dw_hdmi *hdmi = audio->hdmi;
+
+	dw_hdmi_audio_disable(hdmi);
+
+	hdmi_write(audio, HDMI_AUD_CONF0_SW_RESET, HDMI_AUD_CONF0);
+}
+
+static struct hdmi_codec_ops dw_hdmi_i2s_ops = {
+	.hw_params	= dw_hdmi_i2s_hw_params,
+	.audio_shutdown	= dw_hdmi_i2s_audio_shutdown,
+};
+
+static int snd_dw_hdmi_probe(struct platform_device *pdev)
+{
+	struct dw_hdmi_i2s_audio_data *audio = pdev->dev.platform_data;
+	struct platform_device_info pdevinfo;
+	struct hdmi_codec_pdata pdata;
+
+	pdata.ops		= &dw_hdmi_i2s_ops;
+	pdata.i2s		= 1;
+	pdata.max_i2s_channels	= 6;
+	pdata.data		= audio;
+
+	memset(&pdevinfo, 0, sizeof(pdevinfo));
+	pdevinfo.parent		= pdev->dev.parent;
+	pdevinfo.id		= PLATFORM_DEVID_AUTO;
+	pdevinfo.name		= HDMI_CODEC_DRV_NAME;
+	pdevinfo.data		= &pdata;
+	pdevinfo.size_data	= sizeof(pdata);
+	pdevinfo.dma_mask	= DMA_BIT_MASK(32);
+
+	return IS_ERR_OR_NULL(platform_device_register_full(&pdevinfo));
+}
+
+static struct platform_driver snd_dw_hdmi_driver = {
+	.probe	= snd_dw_hdmi_probe,
+	.driver	= {
+		.name = DRIVER_NAME,
+		.owner = THIS_MODULE,
+	},
+};
+module_platform_driver(snd_dw_hdmi_driver);
+
+MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
+MODULE_DESCRIPTION("Synopsis Designware HDMI I2S ALSA SoC interface");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/drivers/gpu/drm/bridge/dw-hdmi.c b/drivers/gpu/drm/bridge/dw-hdmi.c
index 55e73e8..e9ba59e 100644
--- a/drivers/gpu/drm/bridge/dw-hdmi.c
+++ b/drivers/gpu/drm/bridge/dw-hdmi.c
@@ -2013,10 +2013,11 @@  int dw_hdmi_bind(struct device *dev, struct device *master,
 	struct device_node *np = dev->of_node;
 	struct platform_device_info pdevinfo;
 	struct device_node *ddc_node;
-	struct dw_hdmi_audio_data audio;
 	struct dw_hdmi *hdmi;
 	int ret;
 	u32 val = 1;
+	u8 config0;
+	u8 config1;
 
 	hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL);
 	if (!hdmi)
@@ -2185,7 +2186,12 @@  int dw_hdmi_bind(struct device *dev, struct device *master,
 	pdevinfo.parent = dev;
 	pdevinfo.id = PLATFORM_DEVID_AUTO;
 
-	if (hdmi_readb(hdmi, HDMI_CONFIG1_ID) & HDMI_CONFIG1_AHB) {
+	config0 = hdmi_readb(hdmi, HDMI_CONFIG0_ID);
+	config1 = hdmi_readb(hdmi, HDMI_CONFIG1_ID);
+
+	if (config1 & HDMI_CONFIG1_AHB) {
+		struct dw_hdmi_audio_data audio;
+
 		audio.phys = iores->start;
 		audio.base = hdmi->regs;
 		audio.irq = irq;
@@ -2197,6 +2203,18 @@  int dw_hdmi_bind(struct device *dev, struct device *master,
 		pdevinfo.size_data = sizeof(audio);
 		pdevinfo.dma_mask = DMA_BIT_MASK(32);
 		hdmi->audio = platform_device_register_full(&pdevinfo);
+	} else if (config0 & HDMI_CONFIG0_I2S) {
+		struct dw_hdmi_i2s_audio_data audio;
+
+		audio.hdmi	= hdmi;
+		audio.write	= hdmi_writeb;
+		audio.read	= hdmi_readb;
+
+		pdevinfo.name = "dw-hdmi-i2s-audio";
+		pdevinfo.data = &audio;
+		pdevinfo.size_data = sizeof(audio);
+		pdevinfo.dma_mask = DMA_BIT_MASK(32);
+		hdmi->audio = platform_device_register_full(&pdevinfo);
 	}
 
 	/* Unmute I2CM interrupts and reset HDMI DDC I2C master controller */
diff --git a/drivers/gpu/drm/bridge/dw-hdmi.h b/drivers/gpu/drm/bridge/dw-hdmi.h
index fc9a560..c8bdbf2 100644
--- a/drivers/gpu/drm/bridge/dw-hdmi.h
+++ b/drivers/gpu/drm/bridge/dw-hdmi.h
@@ -545,6 +545,10 @@ 
 #define HDMI_I2CM_FS_SCL_LCNT_0_ADDR            0x7E12
 
 enum {
+
+/* CONFIG0_ID field values */
+	HDMI_CONFIG0_I2S = 0x10,
+
 /* CONFIG1_ID field values */
 	HDMI_CONFIG1_AHB = 0x01,
 
@@ -887,6 +891,17 @@  enum {
 	HDMI_PHY_I2CM_CTLINT_ADDR_ARBITRATION_POL = 0x08,
 	HDMI_PHY_I2CM_CTLINT_ADDR_ARBITRATION_MASK = 0x04,
 
+/* AUD_CONF0 field values */
+	HDMI_AUD_CONF0_SW_RESET = 0x80,
+	HDMI_AUD_CONF0_I2S_ALL_ENABLE = 0x2F,
+
+/* AUD_CONF1 field values */
+	HDMI_AUD_CONF1_MODE_I2S = 0x00,
+	HDMI_AUD_CONF1_MODE_RIGHT_J = 0x02,
+	HDMI_AUD_CONF1_MODE_LEFT_J = 0x04,
+	HDMI_AUD_CONF1_WIDTH_16 = 0x10,
+	HDMI_AUD_CONF1_WIDTH_24 = 0x18,
+
 /* AUD_CTS3 field values */
 	HDMI_AUD_CTS3_N_SHIFT_OFFSET = 5,
 	HDMI_AUD_CTS3_N_SHIFT_MASK = 0xe0,
@@ -901,6 +916,12 @@  enum {
 	HDMI_AUD_CTS3_CTS_MANUAL = 0x10,
 	HDMI_AUD_CTS3_AUDCTS19_16_MASK = 0x0f,
 
+/* HDMI_AUD_INPUTCLKFS field values */
+	HDMI_AUD_INPUTCLKFS_128FS = 0,
+	HDMI_AUD_INPUTCLKFS_256FS = 1,
+	HDMI_AUD_INPUTCLKFS_512FS = 2,
+	HDMI_AUD_INPUTCLKFS_64FS = 4,
+
 /* AHB_DMA_CONF0 field values */
 	HDMI_AHB_DMA_CONF0_SW_FIFO_RST_OFFSET = 7,
 	HDMI_AHB_DMA_CONF0_SW_FIFO_RST_MASK = 0x80,