Re: [PATCH v4] drm: exynos: Add driver for HDMI audio interface

2017-10-23 Thread Inki Dae
Merged.

Thanks,
Inki Dae

2017년 10월 23일 21:49에 Sylwester Nawrocki 이(가) 쓴 글:
> The hdmi-codec interface added in this patch is required to properly
> support HDMI audio. Currently the audio part of the SoC internal
> HDMI transmitter is configured with fixed values, which makes HDMI
> audio working by chance, only on boards having an external audio
> codec connected in parallel with the HDMI audio transmitter's input
> I2S interface.
> 
> Signed-off-by: Sylwester Nawrocki 
> Reviewed-by: Andrzej Hajda 
> 
> ---
> Changes since v3:
>  - removed unregister_audio_device() function, added missing
>audio platform device unregistration in probe(),
>  - 'enable' field in struct hdmi_audio replace with 'mute',
>  - audio infoframe initialization moved from the bind()
>callback to probe().
> 
> Changes since v2:
>  - u8 replaced with bool type,
>  - the  HDMI codec iec.status data used directly for setting up
>the HDMI controller registers rather than using hard coded
>constants,
>  - constants used for configuring the HDMI_AUI_CON register
>instead of plain numbers,
>  - if/IS_ERR/return replaced with PTR_ERR_OR_ZERO().
> 
> Changes since v1:
>  - rebased onto v4.14-rc1 and adapted locking
> 
> Changes since RFC version:
>  - fixed hdmi-codec locking issue
>  - added a comment documenting struct hdmi_contex::mutex
> ---
>  drivers/gpu/drm/exynos/Kconfig   |   1 +
>  drivers/gpu/drm/exynos/exynos_hdmi.c | 250 
> ++-
>  drivers/gpu/drm/exynos/regs-hdmi.h   |   8 +-
>  3 files changed, 194 insertions(+), 65 deletions(-)
> 
> diff --git a/drivers/gpu/drm/exynos/Kconfig b/drivers/gpu/drm/exynos/Kconfig
> index 305dc3d4ff77..5a7c9d8abd6b 100644
> --- a/drivers/gpu/drm/exynos/Kconfig
> +++ b/drivers/gpu/drm/exynos/Kconfig
> @@ -3,6 +3,7 @@ config DRM_EXYNOS
>   depends on OF && DRM && (ARCH_S3C64XX || ARCH_EXYNOS || 
> ARCH_MULTIPLATFORM)
>   select DRM_KMS_HELPER
>   select VIDEOMODE_HELPERS
> + select SND_SOC_HDMI_CODEC if SND_SOC
>   help
> Choose this option if you have a Samsung SoC EXYNOS chipset.
> If M is selected the module will be called exynosdrm.
> diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c 
> b/drivers/gpu/drm/exynos/exynos_hdmi.c
> index 1309b1c9e074..82d1b7e2febe 100644
> --- a/drivers/gpu/drm/exynos/exynos_hdmi.c
> +++ b/drivers/gpu/drm/exynos/exynos_hdmi.c
> @@ -40,7 +40,7 @@
>  #include 
>  #include 
>  #include 
> -
> +#include 
>  #include 
> 
>  #include 
> @@ -111,12 +111,18 @@ struct hdmi_driver_data {
>   struct string_array_spec clk_muxes;
>  };
> 
> +struct hdmi_audio {
> + struct platform_device  *pdev;
> + struct hdmi_audio_infoframe infoframe;
> + struct hdmi_codec_paramsparams;
> + boolmute;
> +};
> +
>  struct hdmi_context {
>   struct drm_encoder  encoder;
>   struct device   *dev;
>   struct drm_device   *drm_dev;
>   struct drm_connectorconnector;
> - boolpowered;
>   booldvi_mode;
>   struct delayed_work hotplug_work;
>   struct cec_notifier *notifier;
> @@ -136,6 +142,11 @@ struct hdmi_context {
>   struct regulator*reg_hdmi_en;
>   struct exynos_drm_clk   phy_clk;
>   struct drm_bridge   *bridge;
> +
> + /* mutex protecting subsequent fields below */
> + struct mutexmutex;
> + struct hdmi_audio   audio;
> + boolpowered;
>  };
> 
>  static inline struct hdmi_context *encoder_to_hdmi(struct drm_encoder *e)
> @@ -776,6 +787,22 @@ static int hdmi_clk_set_parents(struct hdmi_context 
> *hdata, bool to_phy)
>   return ret;
>  }
> 
> +static int hdmi_audio_infoframe_apply(struct hdmi_context *hdata)
> +{
> + struct hdmi_audio_infoframe *infoframe = &hdata->audio.infoframe;
> + u8 buf[HDMI_INFOFRAME_SIZE(AUDIO)];
> + int len;
> +
> + len = hdmi_audio_infoframe_pack(infoframe, buf, sizeof(buf));
> + if (len < 0)
> + return len;
> +
> + hdmi_reg_writeb(hdata, HDMI_AUI_CON, HDMI_AUI_CON_EVERY_VSYNC);
> + hdmi_reg_write_buf(hdata, HDMI_AUI_HEADER0, buf, len);
> +
> + return 0;
> +}
> +
>  static void hdmi_reg_infoframes(struct hdmi_context *hdata)
>  {
>   struct drm_display_mode *m = &hdata->encoder.crtc->state->mode;
> @@ -812,15 +839,7 @@ static void hdmi_reg_infoframes(struct hdmi_context 
> *hdata)
>   hdmi_reg_write_buf(hdata, HDMI_VSI_DATA(0), buf + 3, ret - 3);
>   }
> 
> - ret = hdmi_audio_infoframe_init(&frm.audio);
> - if (!ret) {
> - frm.audio.channels = 2;
> - ret = hdmi_audio_infoframe_pack(&frm.audio, buf, sizeof(buf));
> - }
> - if (ret > 0) {
> - hdmi_reg_writeb(hdata, HDMI_AUI_CON, HDMI_AUI_CON_EVERY_VSYNC);
> -   

[PATCH v4] drm: exynos: Add driver for HDMI audio interface

2017-10-23 Thread Sylwester Nawrocki
The hdmi-codec interface added in this patch is required to properly
support HDMI audio. Currently the audio part of the SoC internal
HDMI transmitter is configured with fixed values, which makes HDMI
audio working by chance, only on boards having an external audio
codec connected in parallel with the HDMI audio transmitter's input
I2S interface.

Signed-off-by: Sylwester Nawrocki 
Reviewed-by: Andrzej Hajda 

---
Changes since v3:
 - removed unregister_audio_device() function, added missing
   audio platform device unregistration in probe(),
 - 'enable' field in struct hdmi_audio replace with 'mute',
 - audio infoframe initialization moved from the bind()
   callback to probe().

Changes since v2:
 - u8 replaced with bool type,
 - the  HDMI codec iec.status data used directly for setting up
   the HDMI controller registers rather than using hard coded
   constants,
 - constants used for configuring the HDMI_AUI_CON register
   instead of plain numbers,
 - if/IS_ERR/return replaced with PTR_ERR_OR_ZERO().

Changes since v1:
 - rebased onto v4.14-rc1 and adapted locking

Changes since RFC version:
 - fixed hdmi-codec locking issue
 - added a comment documenting struct hdmi_contex::mutex
---
 drivers/gpu/drm/exynos/Kconfig   |   1 +
 drivers/gpu/drm/exynos/exynos_hdmi.c | 250 ++-
 drivers/gpu/drm/exynos/regs-hdmi.h   |   8 +-
 3 files changed, 194 insertions(+), 65 deletions(-)

diff --git a/drivers/gpu/drm/exynos/Kconfig b/drivers/gpu/drm/exynos/Kconfig
index 305dc3d4ff77..5a7c9d8abd6b 100644
--- a/drivers/gpu/drm/exynos/Kconfig
+++ b/drivers/gpu/drm/exynos/Kconfig
@@ -3,6 +3,7 @@ config DRM_EXYNOS
depends on OF && DRM && (ARCH_S3C64XX || ARCH_EXYNOS || 
ARCH_MULTIPLATFORM)
select DRM_KMS_HELPER
select VIDEOMODE_HELPERS
+   select SND_SOC_HDMI_CODEC if SND_SOC
help
  Choose this option if you have a Samsung SoC EXYNOS chipset.
  If M is selected the module will be called exynosdrm.
diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c 
b/drivers/gpu/drm/exynos/exynos_hdmi.c
index 1309b1c9e074..82d1b7e2febe 100644
--- a/drivers/gpu/drm/exynos/exynos_hdmi.c
+++ b/drivers/gpu/drm/exynos/exynos_hdmi.c
@@ -40,7 +40,7 @@
 #include 
 #include 
 #include 
-
+#include 
 #include 

 #include 
@@ -111,12 +111,18 @@ struct hdmi_driver_data {
struct string_array_spec clk_muxes;
 };

+struct hdmi_audio {
+   struct platform_device  *pdev;
+   struct hdmi_audio_infoframe infoframe;
+   struct hdmi_codec_paramsparams;
+   boolmute;
+};
+
 struct hdmi_context {
struct drm_encoder  encoder;
struct device   *dev;
struct drm_device   *drm_dev;
struct drm_connectorconnector;
-   boolpowered;
booldvi_mode;
struct delayed_work hotplug_work;
struct cec_notifier *notifier;
@@ -136,6 +142,11 @@ struct hdmi_context {
struct regulator*reg_hdmi_en;
struct exynos_drm_clk   phy_clk;
struct drm_bridge   *bridge;
+
+   /* mutex protecting subsequent fields below */
+   struct mutexmutex;
+   struct hdmi_audio   audio;
+   boolpowered;
 };

 static inline struct hdmi_context *encoder_to_hdmi(struct drm_encoder *e)
@@ -776,6 +787,22 @@ static int hdmi_clk_set_parents(struct hdmi_context 
*hdata, bool to_phy)
return ret;
 }

+static int hdmi_audio_infoframe_apply(struct hdmi_context *hdata)
+{
+   struct hdmi_audio_infoframe *infoframe = &hdata->audio.infoframe;
+   u8 buf[HDMI_INFOFRAME_SIZE(AUDIO)];
+   int len;
+
+   len = hdmi_audio_infoframe_pack(infoframe, buf, sizeof(buf));
+   if (len < 0)
+   return len;
+
+   hdmi_reg_writeb(hdata, HDMI_AUI_CON, HDMI_AUI_CON_EVERY_VSYNC);
+   hdmi_reg_write_buf(hdata, HDMI_AUI_HEADER0, buf, len);
+
+   return 0;
+}
+
 static void hdmi_reg_infoframes(struct hdmi_context *hdata)
 {
struct drm_display_mode *m = &hdata->encoder.crtc->state->mode;
@@ -812,15 +839,7 @@ static void hdmi_reg_infoframes(struct hdmi_context *hdata)
hdmi_reg_write_buf(hdata, HDMI_VSI_DATA(0), buf + 3, ret - 3);
}

-   ret = hdmi_audio_infoframe_init(&frm.audio);
-   if (!ret) {
-   frm.audio.channels = 2;
-   ret = hdmi_audio_infoframe_pack(&frm.audio, buf, sizeof(buf));
-   }
-   if (ret > 0) {
-   hdmi_reg_writeb(hdata, HDMI_AUI_CON, HDMI_AUI_CON_EVERY_VSYNC);
-   hdmi_reg_write_buf(hdata, HDMI_AUI_HEADER0, buf, ret);
-   }
+   hdmi_audio_infoframe_apply(hdata);
 }

 static enum drm_connector_status hdmi_detect(struct drm_connector *connector,
@@ -1010,23 +1029,18 @@ static void hdmi_reg_acr(struc