[PATCH v3 10/14] drm/sun4i: hdmi: Add support for controller hardware variants

2017-09-29 Thread Chen-Yu Tsai
The HDMI controller found in earlier Allwinner SoCs have slight
differences between the A10, A10s, and the A31:

  - Need different initial values for the PLL related registers

  - Different behavior of the DDC and TMDS clocks

  - Different register layout for the DDC portion

  - Separate DDC parent clock on the A31

  - Explicit reset control

For the A31, the HDMI TMDS clock has a different value offset for
the divider. The HDMI DDC block is different from the one in the
other SoCs. As far as the DDC clock goes, it has no pre-divider,
as it is clocked from a slower parent clock, not the TMDS clock.
The divider offset from the register value is different. And the
clock control register is at a different offset.

A new variant data structure is created to store pointers to the
above functions, structures, and the different initial values.
Another flag notates whether there is a separate DDC parent clock.
If not, the TMDS clock is passed to the DDC clock create function,
as before.

Regmap fields are used to deal with the different register layout
of the DDC block.

Signed-off-by: Chen-Yu Tsai 
Acked-by: Maxime Ripard 
---
 drivers/gpu/drm/sun4i/sun4i_hdmi.h  |  72 +
 drivers/gpu/drm/sun4i/sun4i_hdmi_ddc_clk.c  |  38 +++--
 drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c  | 112 +++---
 drivers/gpu/drm/sun4i/sun4i_hdmi_i2c.c  | 227 
 drivers/gpu/drm/sun4i/sun4i_hdmi_tmds_clk.c |  17 ++-
 5 files changed, 369 insertions(+), 97 deletions(-)

diff --git a/drivers/gpu/drm/sun4i/sun4i_hdmi.h 
b/drivers/gpu/drm/sun4i/sun4i_hdmi.h
index b95512ec8eb6..b1124a8f9f05 100644
--- a/drivers/gpu/drm/sun4i/sun4i_hdmi.h
+++ b/drivers/gpu/drm/sun4i/sun4i_hdmi.h
@@ -14,6 +14,7 @@
 
 #include 
 #include 
+#include 
 
 #include 
 
@@ -157,6 +158,55 @@ enum sun4i_hdmi_pkt_type {
SUN4I_HDMI_PKT_END = 15,
 };
 
+struct sun4i_hdmi_variant {
+   bool has_ddc_parent_clk;
+   bool has_reset_control;
+
+   u32 pad_ctrl0_init_val;
+   u32 pad_ctrl1_init_val;
+   u32 pll_ctrl_init_val;
+
+   struct reg_field ddc_clk_reg;
+   u8 ddc_clk_pre_divider;
+   u8 ddc_clk_m_offset;
+
+   u8 tmds_clk_div_offset;
+
+   /* Register fields for I2C adapter */
+   struct reg_fieldfield_ddc_en;
+   struct reg_fieldfield_ddc_start;
+   struct reg_fieldfield_ddc_reset;
+   struct reg_fieldfield_ddc_addr_reg;
+   struct reg_fieldfield_ddc_slave_addr;
+   struct reg_fieldfield_ddc_int_mask;
+   struct reg_fieldfield_ddc_int_status;
+   struct reg_fieldfield_ddc_fifo_clear;
+   struct reg_fieldfield_ddc_fifo_rx_thres;
+   struct reg_fieldfield_ddc_fifo_tx_thres;
+   struct reg_fieldfield_ddc_byte_count;
+   struct reg_fieldfield_ddc_cmd;
+   struct reg_fieldfield_ddc_sda_en;
+   struct reg_fieldfield_ddc_sck_en;
+
+   /* DDC FIFO register offset */
+   u32 ddc_fifo_reg;
+
+   /*
+* DDC FIFO threshold boundary conditions
+*
+* This is used to cope with the threshold boundary condition
+* being slightly different on sun5i and sun6i.
+*
+* On sun5i the threshold is exclusive, i.e. does not include,
+* the value of the threshold. ( > for RX; < for TX )
+* On sun6i the threshold is inclusive, i.e. includes, the
+* value of the threshold. ( >= for RX; <= for TX )
+*/
+   boolddc_fifo_thres_incl;
+
+   boolddc_fifo_has_dir;
+};
+
 struct sun4i_hdmi {
struct drm_connectorconnector;
struct drm_encoder  encoder;
@@ -165,9 +215,13 @@ struct sun4i_hdmi {
void __iomem*base;
struct regmap   *regmap;
 
+   /* Reset control */
+   struct reset_control*reset;
+
/* Parent clocks */
struct clk  *bus_clk;
struct clk  *mod_clk;
+   struct clk  *ddc_parent_clk;
struct clk  *pll0_clk;
struct clk  *pll1_clk;
 
@@ -177,10 +231,28 @@ struct sun4i_hdmi {
 
struct i2c_adapter  *i2c;
 
+   /* Regmap fields for I2C adapter */
+   struct regmap_field *field_ddc_en;
+   struct regmap_field *field_ddc_start;
+   struct regmap_field *field_ddc_reset;
+   struct regmap_field *field_ddc_addr_reg;
+   struct regmap_field *field_ddc_slave_addr;
+   struct regmap_field *field_ddc_int_mask;
+   struct regmap_field *field_ddc_int_status;
+   struct regmap_field *field_ddc_fifo_clear;
+   struct regmap_field *field_ddc_fifo_rx_thres;
+   struct regmap_field *field_ddc_fifo_tx_thres;
+   struct regmap_field *field_ddc_byte_count;
+   struct 

[PATCH v3 10/14] drm/sun4i: hdmi: Add support for controller hardware variants

2017-09-29 Thread Chen-Yu Tsai
The HDMI controller found in earlier Allwinner SoCs have slight
differences between the A10, A10s, and the A31:

  - Need different initial values for the PLL related registers

  - Different behavior of the DDC and TMDS clocks

  - Different register layout for the DDC portion

  - Separate DDC parent clock on the A31

  - Explicit reset control

For the A31, the HDMI TMDS clock has a different value offset for
the divider. The HDMI DDC block is different from the one in the
other SoCs. As far as the DDC clock goes, it has no pre-divider,
as it is clocked from a slower parent clock, not the TMDS clock.
The divider offset from the register value is different. And the
clock control register is at a different offset.

A new variant data structure is created to store pointers to the
above functions, structures, and the different initial values.
Another flag notates whether there is a separate DDC parent clock.
If not, the TMDS clock is passed to the DDC clock create function,
as before.

Regmap fields are used to deal with the different register layout
of the DDC block.

Signed-off-by: Chen-Yu Tsai 
Acked-by: Maxime Ripard 
---
 drivers/gpu/drm/sun4i/sun4i_hdmi.h  |  72 +
 drivers/gpu/drm/sun4i/sun4i_hdmi_ddc_clk.c  |  38 +++--
 drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c  | 112 +++---
 drivers/gpu/drm/sun4i/sun4i_hdmi_i2c.c  | 227 
 drivers/gpu/drm/sun4i/sun4i_hdmi_tmds_clk.c |  17 ++-
 5 files changed, 369 insertions(+), 97 deletions(-)

diff --git a/drivers/gpu/drm/sun4i/sun4i_hdmi.h 
b/drivers/gpu/drm/sun4i/sun4i_hdmi.h
index b95512ec8eb6..b1124a8f9f05 100644
--- a/drivers/gpu/drm/sun4i/sun4i_hdmi.h
+++ b/drivers/gpu/drm/sun4i/sun4i_hdmi.h
@@ -14,6 +14,7 @@
 
 #include 
 #include 
+#include 
 
 #include 
 
@@ -157,6 +158,55 @@ enum sun4i_hdmi_pkt_type {
SUN4I_HDMI_PKT_END = 15,
 };
 
+struct sun4i_hdmi_variant {
+   bool has_ddc_parent_clk;
+   bool has_reset_control;
+
+   u32 pad_ctrl0_init_val;
+   u32 pad_ctrl1_init_val;
+   u32 pll_ctrl_init_val;
+
+   struct reg_field ddc_clk_reg;
+   u8 ddc_clk_pre_divider;
+   u8 ddc_clk_m_offset;
+
+   u8 tmds_clk_div_offset;
+
+   /* Register fields for I2C adapter */
+   struct reg_fieldfield_ddc_en;
+   struct reg_fieldfield_ddc_start;
+   struct reg_fieldfield_ddc_reset;
+   struct reg_fieldfield_ddc_addr_reg;
+   struct reg_fieldfield_ddc_slave_addr;
+   struct reg_fieldfield_ddc_int_mask;
+   struct reg_fieldfield_ddc_int_status;
+   struct reg_fieldfield_ddc_fifo_clear;
+   struct reg_fieldfield_ddc_fifo_rx_thres;
+   struct reg_fieldfield_ddc_fifo_tx_thres;
+   struct reg_fieldfield_ddc_byte_count;
+   struct reg_fieldfield_ddc_cmd;
+   struct reg_fieldfield_ddc_sda_en;
+   struct reg_fieldfield_ddc_sck_en;
+
+   /* DDC FIFO register offset */
+   u32 ddc_fifo_reg;
+
+   /*
+* DDC FIFO threshold boundary conditions
+*
+* This is used to cope with the threshold boundary condition
+* being slightly different on sun5i and sun6i.
+*
+* On sun5i the threshold is exclusive, i.e. does not include,
+* the value of the threshold. ( > for RX; < for TX )
+* On sun6i the threshold is inclusive, i.e. includes, the
+* value of the threshold. ( >= for RX; <= for TX )
+*/
+   boolddc_fifo_thres_incl;
+
+   boolddc_fifo_has_dir;
+};
+
 struct sun4i_hdmi {
struct drm_connectorconnector;
struct drm_encoder  encoder;
@@ -165,9 +215,13 @@ struct sun4i_hdmi {
void __iomem*base;
struct regmap   *regmap;
 
+   /* Reset control */
+   struct reset_control*reset;
+
/* Parent clocks */
struct clk  *bus_clk;
struct clk  *mod_clk;
+   struct clk  *ddc_parent_clk;
struct clk  *pll0_clk;
struct clk  *pll1_clk;
 
@@ -177,10 +231,28 @@ struct sun4i_hdmi {
 
struct i2c_adapter  *i2c;
 
+   /* Regmap fields for I2C adapter */
+   struct regmap_field *field_ddc_en;
+   struct regmap_field *field_ddc_start;
+   struct regmap_field *field_ddc_reset;
+   struct regmap_field *field_ddc_addr_reg;
+   struct regmap_field *field_ddc_slave_addr;
+   struct regmap_field *field_ddc_int_mask;
+   struct regmap_field *field_ddc_int_status;
+   struct regmap_field *field_ddc_fifo_clear;
+   struct regmap_field *field_ddc_fifo_rx_thres;
+   struct regmap_field *field_ddc_fifo_tx_thres;
+   struct regmap_field *field_ddc_byte_count;
+   struct regmap_field *field_ddc_cmd;
+   struct regmap_field