Re: [PATCH v2 2/2] drm/sun4i: dw-hdmi: Bit bang CEC on some SoCs

2019-04-12 Thread Hans Verkuil
On 4/1/19 9:15 PM, Jernej Skrabec wrote:
> All DW HDMI controllers used by Allwinner SoCs include CEC controller.
> However, due to additional logic put between CEC controller and pins,
> CEC communication doesn't work well on some of them.
> 
> Based on observations, it seems that only outgoing messages are
> properly transmitted. It's possible that it would still work correctly
> if pins are switched between input and output mode manually in right
> moment. But that's very error prone. It's better and easier just to bit
> bang protocol.
> 
> Enable bit banging just for controller and phy combination found in H3
> and other 40nm SoCs. Other combinations work (H6) or the status is
> unknown (A83T).
> 
> Signed-off-by: Jernej Skrabec 
> ---
>  drivers/gpu/drm/sun4i/Kconfig  | 10 
>  drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h  | 11 
>  drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c | 83 +-
>  3 files changed, 102 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/gpu/drm/sun4i/Kconfig b/drivers/gpu/drm/sun4i/Kconfig
> index 1dbbc3a1b763..7149c72e44c8 100644
> --- a/drivers/gpu/drm/sun4i/Kconfig
> +++ b/drivers/gpu/drm/sun4i/Kconfig
> @@ -60,6 +60,16 @@ config DRM_SUN8I_DW_HDMI
> DesignWare HDMI controller with custom HDMI PHY. If M is
> selected the module will be called sun8i_dw_hdmi.
>  
> +config DRM_SUN8I_DW_HDMI_CEC
> +   bool "Allwinner DesignWare HDMI CEC Support for 40nm SoCs"

I'm not sure mentioning "DesignWare" here is right. The CEC support
(as I understand it) bypasses the DW entirely and is really a sunxi
specific implementation.

> +   depends on DRM_SUN8I_DW_HDMI
> +   select CEC_CORE
> +   select CEC_PIN
> +   help
> +   Choose this option if you have an 40nm Allwinner SoC with
> +   the DesignWare HDMI controller with custom HDMI PHY and
> +   you want to use CEC.
> +
>  config DRM_SUN8I_MIXER
>   tristate "Support for Allwinner Display Engine 2.0 Mixer"
>   default MACH_SUN8I
> diff --git a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h 
> b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h
> index 720c5aa8adc1..49ca001923e3 100644
> --- a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h
> +++ b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h
> @@ -12,6 +12,7 @@
>  #include 
>  #include 
>  #include 
> +#include 
>  
>  #define SUN8I_HDMI_PHY_DBG_CTRL_REG  0x
>  #define SUN8I_HDMI_PHY_DBG_CTRL_PX_LOCK  BIT(0)
> @@ -144,6 +145,13 @@
>  #define SUN8I_HDMI_PHY_ANA_STS_RCAL_MASK GENMASK(5, 0)
>  
>  #define SUN8I_HDMI_PHY_CEC_REG   0x003c
> +#define SUN8I_HDMI_PHY_CEC_PIN_CTRL  BIT(7)
> +/*
> + * Documentation says that this bit is output enable. However,
> + * it seems that this bit is actually output disable.
> + */
> +#define SUN8I_HDMI_PHY_CEC_OUT_DIS   BIT(2)
> +#define SUN8I_HDMI_PHY_CEC_IN_DATA   BIT(1)
>  
>  struct sun8i_hdmi_phy;
>  
> @@ -151,6 +159,7 @@ struct sun8i_hdmi_phy_variant {
>   bool has_phy_clk;
>   bool has_second_pll;
>   unsigned int is_custom_phy : 1;
> + unsigned int bit_bang_cec : 1;
>   const struct dw_hdmi_curr_ctrl *cur_ctr;
>   const struct dw_hdmi_mpll_config *mpll_cfg;
>   const struct dw_hdmi_phy_config *phy_cfg;
> @@ -163,6 +172,8 @@ struct sun8i_hdmi_phy_variant {
>  };
>  
>  struct sun8i_hdmi_phy {
> + struct cec_adapter  *cec_adapter;
> + struct cec_notifier *cec_notifier;
>   struct clk  *clk_bus;
>   struct clk  *clk_mod;
>   struct clk  *clk_phy;
> diff --git a/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c 
> b/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c
> index 66ea3a902e36..8fd6bf91714e 100644
> --- a/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c
> +++ b/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c
> @@ -503,8 +503,9 @@ static void sun8i_hdmi_phy_init_h3(struct sun8i_hdmi_phy 
> *phy)
>   regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_PLL_CFG1_REG,
>  SUN8I_HDMI_PHY_PLL_CFG1_CKIN_SEL_MSK, 0);
>  
> - /* set HW control of CEC pins */
> - regmap_write(phy->regs, SUN8I_HDMI_PHY_CEC_REG, 0);
> + /* manual control of CEC pins */
> + regmap_write(phy->regs, SUN8I_HDMI_PHY_CEC_REG,
> +  SUN8I_HDMI_PHY_CEC_PIN_CTRL);
>  
>   /* read calibration data */
>   regmap_read(phy->regs, SUN8I_HDMI_PHY_ANA_STS_REG, );
> @@ -530,8 +531,49 @@ void sun8i_hdmi_phy_set_ops(struct sun8i_hdmi_phy *phy,
>   plat_data->cur_ctr = variant->cur_ctr;
>   plat_data->phy_config = variant->phy_cfg;
>   }
> + plat_data->disable_cec = phy->variant->bit_bang_cec;
>  }
>  
> +#ifdef CONFIG_DRM_SUN8I_DW_HDMI_CEC
> +static bool sun8i_hdmi_phy_cec_pin_read(struct cec_adapter *adap)
> +{
> + struct sun8i_hdmi_phy *phy = cec_get_drvdata(adap);
> + unsigned int val;
> +
> + regmap_read(phy->regs, SUN8I_HDMI_PHY_CEC_REG, );
> +
> + return val & SUN8I_HDMI_PHY_CEC_IN_DATA;
> 

[PATCH v2 2/2] drm/sun4i: dw-hdmi: Bit bang CEC on some SoCs

2019-04-02 Thread Jernej Skrabec
All DW HDMI controllers used by Allwinner SoCs include CEC controller.
However, due to additional logic put between CEC controller and pins,
CEC communication doesn't work well on some of them.

Based on observations, it seems that only outgoing messages are
properly transmitted. It's possible that it would still work correctly
if pins are switched between input and output mode manually in right
moment. But that's very error prone. It's better and easier just to bit
bang protocol.

Enable bit banging just for controller and phy combination found in H3
and other 40nm SoCs. Other combinations work (H6) or the status is
unknown (A83T).

Signed-off-by: Jernej Skrabec 
---
 drivers/gpu/drm/sun4i/Kconfig  | 10 
 drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h  | 11 
 drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c | 83 +-
 3 files changed, 102 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/sun4i/Kconfig b/drivers/gpu/drm/sun4i/Kconfig
index 1dbbc3a1b763..7149c72e44c8 100644
--- a/drivers/gpu/drm/sun4i/Kconfig
+++ b/drivers/gpu/drm/sun4i/Kconfig
@@ -60,6 +60,16 @@ config DRM_SUN8I_DW_HDMI
  DesignWare HDMI controller with custom HDMI PHY. If M is
  selected the module will be called sun8i_dw_hdmi.
 
+config DRM_SUN8I_DW_HDMI_CEC
+   bool "Allwinner DesignWare HDMI CEC Support for 40nm SoCs"
+   depends on DRM_SUN8I_DW_HDMI
+   select CEC_CORE
+   select CEC_PIN
+   help
+ Choose this option if you have an 40nm Allwinner SoC with
+ the DesignWare HDMI controller with custom HDMI PHY and
+ you want to use CEC.
+
 config DRM_SUN8I_MIXER
tristate "Support for Allwinner Display Engine 2.0 Mixer"
default MACH_SUN8I
diff --git a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h 
b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h
index 720c5aa8adc1..49ca001923e3 100644
--- a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h
+++ b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h
@@ -12,6 +12,7 @@
 #include 
 #include 
 #include 
+#include 
 
 #define SUN8I_HDMI_PHY_DBG_CTRL_REG0x
 #define SUN8I_HDMI_PHY_DBG_CTRL_PX_LOCKBIT(0)
@@ -144,6 +145,13 @@
 #define SUN8I_HDMI_PHY_ANA_STS_RCAL_MASK   GENMASK(5, 0)
 
 #define SUN8I_HDMI_PHY_CEC_REG 0x003c
+#define SUN8I_HDMI_PHY_CEC_PIN_CTRLBIT(7)
+/*
+ * Documentation says that this bit is output enable. However,
+ * it seems that this bit is actually output disable.
+ */
+#define SUN8I_HDMI_PHY_CEC_OUT_DIS BIT(2)
+#define SUN8I_HDMI_PHY_CEC_IN_DATA BIT(1)
 
 struct sun8i_hdmi_phy;
 
@@ -151,6 +159,7 @@ struct sun8i_hdmi_phy_variant {
bool has_phy_clk;
bool has_second_pll;
unsigned int is_custom_phy : 1;
+   unsigned int bit_bang_cec : 1;
const struct dw_hdmi_curr_ctrl *cur_ctr;
const struct dw_hdmi_mpll_config *mpll_cfg;
const struct dw_hdmi_phy_config *phy_cfg;
@@ -163,6 +172,8 @@ struct sun8i_hdmi_phy_variant {
 };
 
 struct sun8i_hdmi_phy {
+   struct cec_adapter  *cec_adapter;
+   struct cec_notifier *cec_notifier;
struct clk  *clk_bus;
struct clk  *clk_mod;
struct clk  *clk_phy;
diff --git a/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c 
b/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c
index 66ea3a902e36..8fd6bf91714e 100644
--- a/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c
+++ b/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c
@@ -503,8 +503,9 @@ static void sun8i_hdmi_phy_init_h3(struct sun8i_hdmi_phy 
*phy)
regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_PLL_CFG1_REG,
   SUN8I_HDMI_PHY_PLL_CFG1_CKIN_SEL_MSK, 0);
 
-   /* set HW control of CEC pins */
-   regmap_write(phy->regs, SUN8I_HDMI_PHY_CEC_REG, 0);
+   /* manual control of CEC pins */
+   regmap_write(phy->regs, SUN8I_HDMI_PHY_CEC_REG,
+SUN8I_HDMI_PHY_CEC_PIN_CTRL);
 
/* read calibration data */
regmap_read(phy->regs, SUN8I_HDMI_PHY_ANA_STS_REG, );
@@ -530,8 +531,49 @@ void sun8i_hdmi_phy_set_ops(struct sun8i_hdmi_phy *phy,
plat_data->cur_ctr = variant->cur_ctr;
plat_data->phy_config = variant->phy_cfg;
}
+   plat_data->disable_cec = phy->variant->bit_bang_cec;
 }
 
+#ifdef CONFIG_DRM_SUN8I_DW_HDMI_CEC
+static bool sun8i_hdmi_phy_cec_pin_read(struct cec_adapter *adap)
+{
+   struct sun8i_hdmi_phy *phy = cec_get_drvdata(adap);
+   unsigned int val;
+
+   regmap_read(phy->regs, SUN8I_HDMI_PHY_CEC_REG, );
+
+   return val & SUN8I_HDMI_PHY_CEC_IN_DATA;
+}
+
+static void sun8i_hdmi_phy_cec_pin_low(struct cec_adapter *adap)
+{
+   struct sun8i_hdmi_phy *phy = cec_get_drvdata(adap);
+
+   /* Start driving the CEC pin low */
+   regmap_write(phy->regs, SUN8I_HDMI_PHY_CEC_REG,
+SUN8I_HDMI_PHY_CEC_PIN_CTRL);
+}
+
+static void sun8i_hdmi_phy_cec_pin_high(struct cec_adapter *adap)
+{
+