Commit 6fd90310 authored by Jernej Skrabec's avatar Jernej Skrabec Committed by Maxime Ripard

drm/sun4i: Add support for variants to DW HDMI PHY

There are multiple variants of DW HDMI PHYs in Allwinner SoCs. While
some things like clock and reset setup are the same, PHY configuration
differs a lot.

Split out code which is PHY specific to separate functions and create
a structure which holds pointers to those functions.
Signed-off-by: default avatarJernej Skrabec <jernej.skrabec@siol.net>
Signed-off-by: default avatarMaxime Ripard <maxime.ripard@bootlin.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20180301213442.16677-11-jernej.skrabec@siol.net
parent e420ccd6
...@@ -12,11 +12,23 @@ ...@@ -12,11 +12,23 @@
#include <linux/regmap.h> #include <linux/regmap.h>
#include <linux/reset.h> #include <linux/reset.h>
struct sun8i_hdmi_phy;
struct sun8i_hdmi_phy_variant {
void (*phy_init)(struct sun8i_hdmi_phy *phy);
void (*phy_disable)(struct dw_hdmi *hdmi,
struct sun8i_hdmi_phy *phy);
int (*phy_config)(struct dw_hdmi *hdmi,
struct sun8i_hdmi_phy *phy,
unsigned int clk_rate);
};
struct sun8i_hdmi_phy { struct sun8i_hdmi_phy {
struct clk *clk_bus; struct clk *clk_bus;
struct clk *clk_mod; struct clk *clk_mod;
struct regmap *regs; struct regmap *regs;
struct reset_control *rst_phy; struct reset_control *rst_phy;
struct sun8i_hdmi_phy_variant *variant;
}; };
struct sun8i_dw_hdmi { struct sun8i_dw_hdmi {
......
...@@ -30,21 +30,10 @@ ...@@ -30,21 +30,10 @@
*/ */
#define I2C_ADDR 0x69 #define I2C_ADDR 0x69
static int sun8i_hdmi_phy_config(struct dw_hdmi *hdmi, void *data, static int sun8i_hdmi_phy_config_a83t(struct dw_hdmi *hdmi,
struct drm_display_mode *mode) struct sun8i_hdmi_phy *phy,
unsigned int clk_rate)
{ {
struct sun8i_hdmi_phy *phy = (struct sun8i_hdmi_phy *)data;
u32 val = 0;
if (mode->flags & DRM_MODE_FLAG_NHSYNC)
val |= SUN8I_HDMI_PHY_DBG_CTRL_POL_NHSYNC;
if (mode->flags & DRM_MODE_FLAG_NVSYNC)
val |= SUN8I_HDMI_PHY_DBG_CTRL_POL_NVSYNC;
regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_DBG_CTRL_REG,
SUN8I_HDMI_PHY_DBG_CTRL_POL_MASK, val);
regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_REXT_CTRL_REG, regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_REXT_CTRL_REG,
SUN8I_HDMI_PHY_REXT_CTRL_REXT_EN, SUN8I_HDMI_PHY_REXT_CTRL_REXT_EN,
SUN8I_HDMI_PHY_REXT_CTRL_REXT_EN); SUN8I_HDMI_PHY_REXT_CTRL_REXT_EN);
...@@ -64,21 +53,21 @@ static int sun8i_hdmi_phy_config(struct dw_hdmi *hdmi, void *data, ...@@ -64,21 +53,21 @@ static int sun8i_hdmi_phy_config(struct dw_hdmi *hdmi, void *data,
* release any documentation, explanation of this values can * release any documentation, explanation of this values can
* be found in i.MX 6Dual/6Quad Reference Manual. * be found in i.MX 6Dual/6Quad Reference Manual.
*/ */
if (mode->crtc_clock <= 27000) { if (clk_rate <= 27000000) {
dw_hdmi_phy_i2c_write(hdmi, 0x01e0, 0x06); dw_hdmi_phy_i2c_write(hdmi, 0x01e0, 0x06);
dw_hdmi_phy_i2c_write(hdmi, 0x0000, 0x15); dw_hdmi_phy_i2c_write(hdmi, 0x0000, 0x15);
dw_hdmi_phy_i2c_write(hdmi, 0x08da, 0x10); dw_hdmi_phy_i2c_write(hdmi, 0x08da, 0x10);
dw_hdmi_phy_i2c_write(hdmi, 0x0007, 0x19); dw_hdmi_phy_i2c_write(hdmi, 0x0007, 0x19);
dw_hdmi_phy_i2c_write(hdmi, 0x0318, 0x0e); dw_hdmi_phy_i2c_write(hdmi, 0x0318, 0x0e);
dw_hdmi_phy_i2c_write(hdmi, 0x8009, 0x09); dw_hdmi_phy_i2c_write(hdmi, 0x8009, 0x09);
} else if (mode->crtc_clock <= 74250) { } else if (clk_rate <= 74250000) {
dw_hdmi_phy_i2c_write(hdmi, 0x0540, 0x06); dw_hdmi_phy_i2c_write(hdmi, 0x0540, 0x06);
dw_hdmi_phy_i2c_write(hdmi, 0x0005, 0x15); dw_hdmi_phy_i2c_write(hdmi, 0x0005, 0x15);
dw_hdmi_phy_i2c_write(hdmi, 0x0000, 0x10); dw_hdmi_phy_i2c_write(hdmi, 0x0000, 0x10);
dw_hdmi_phy_i2c_write(hdmi, 0x0007, 0x19); dw_hdmi_phy_i2c_write(hdmi, 0x0007, 0x19);
dw_hdmi_phy_i2c_write(hdmi, 0x02b5, 0x0e); dw_hdmi_phy_i2c_write(hdmi, 0x02b5, 0x0e);
dw_hdmi_phy_i2c_write(hdmi, 0x8009, 0x09); dw_hdmi_phy_i2c_write(hdmi, 0x8009, 0x09);
} else if (mode->crtc_clock <= 148500) { } else if (clk_rate <= 148500000) {
dw_hdmi_phy_i2c_write(hdmi, 0x04a0, 0x06); dw_hdmi_phy_i2c_write(hdmi, 0x04a0, 0x06);
dw_hdmi_phy_i2c_write(hdmi, 0x000a, 0x15); dw_hdmi_phy_i2c_write(hdmi, 0x000a, 0x15);
dw_hdmi_phy_i2c_write(hdmi, 0x0000, 0x10); dw_hdmi_phy_i2c_write(hdmi, 0x0000, 0x10);
...@@ -103,10 +92,27 @@ static int sun8i_hdmi_phy_config(struct dw_hdmi *hdmi, void *data, ...@@ -103,10 +92,27 @@ static int sun8i_hdmi_phy_config(struct dw_hdmi *hdmi, void *data,
return 0; return 0;
}; };
static void sun8i_hdmi_phy_disable(struct dw_hdmi *hdmi, void *data) static int sun8i_hdmi_phy_config(struct dw_hdmi *hdmi, void *data,
struct drm_display_mode *mode)
{ {
struct sun8i_hdmi_phy *phy = (struct sun8i_hdmi_phy *)data; struct sun8i_hdmi_phy *phy = (struct sun8i_hdmi_phy *)data;
u32 val = 0;
if (mode->flags & DRM_MODE_FLAG_NHSYNC)
val |= SUN8I_HDMI_PHY_DBG_CTRL_POL_NHSYNC;
if (mode->flags & DRM_MODE_FLAG_NVSYNC)
val |= SUN8I_HDMI_PHY_DBG_CTRL_POL_NVSYNC;
regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_DBG_CTRL_REG,
SUN8I_HDMI_PHY_DBG_CTRL_POL_MASK, val);
return phy->variant->phy_config(hdmi, phy, mode->crtc_clock * 1000);
};
static void sun8i_hdmi_phy_disable_a83t(struct dw_hdmi *hdmi,
struct sun8i_hdmi_phy *phy)
{
dw_hdmi_phy_gen2_txpwron(hdmi, 0); dw_hdmi_phy_gen2_txpwron(hdmi, 0);
dw_hdmi_phy_gen2_pddq(hdmi, 1); dw_hdmi_phy_gen2_pddq(hdmi, 1);
...@@ -114,6 +120,13 @@ static void sun8i_hdmi_phy_disable(struct dw_hdmi *hdmi, void *data) ...@@ -114,6 +120,13 @@ static void sun8i_hdmi_phy_disable(struct dw_hdmi *hdmi, void *data)
SUN8I_HDMI_PHY_REXT_CTRL_REXT_EN, 0); SUN8I_HDMI_PHY_REXT_CTRL_REXT_EN, 0);
} }
static void sun8i_hdmi_phy_disable(struct dw_hdmi *hdmi, void *data)
{
struct sun8i_hdmi_phy *phy = (struct sun8i_hdmi_phy *)data;
phy->variant->phy_disable(hdmi, phy);
}
static const struct dw_hdmi_phy_ops sun8i_hdmi_phy_ops = { static const struct dw_hdmi_phy_ops sun8i_hdmi_phy_ops = {
.init = &sun8i_hdmi_phy_config, .init = &sun8i_hdmi_phy_config,
.disable = &sun8i_hdmi_phy_disable, .disable = &sun8i_hdmi_phy_disable,
...@@ -122,16 +135,8 @@ static const struct dw_hdmi_phy_ops sun8i_hdmi_phy_ops = { ...@@ -122,16 +135,8 @@ static const struct dw_hdmi_phy_ops sun8i_hdmi_phy_ops = {
.setup_hpd = &dw_hdmi_phy_setup_hpd, .setup_hpd = &dw_hdmi_phy_setup_hpd,
}; };
void sun8i_hdmi_phy_init(struct sun8i_hdmi_phy *phy) static void sun8i_hdmi_phy_init_a83t(struct sun8i_hdmi_phy *phy)
{ {
/* enable read access to HDMI controller */
regmap_write(phy->regs, SUN8I_HDMI_PHY_READ_EN_REG,
SUN8I_HDMI_PHY_READ_EN_MAGIC);
/* unscramble register offsets */
regmap_write(phy->regs, SUN8I_HDMI_PHY_UNSCRAMBLE_REG,
SUN8I_HDMI_PHY_UNSCRAMBLE_MAGIC);
regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_DBG_CTRL_REG, regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_DBG_CTRL_REG,
SUN8I_HDMI_PHY_DBG_CTRL_PX_LOCK, SUN8I_HDMI_PHY_DBG_CTRL_PX_LOCK,
SUN8I_HDMI_PHY_DBG_CTRL_PX_LOCK); SUN8I_HDMI_PHY_DBG_CTRL_PX_LOCK);
...@@ -145,6 +150,19 @@ void sun8i_hdmi_phy_init(struct sun8i_hdmi_phy *phy) ...@@ -145,6 +150,19 @@ void sun8i_hdmi_phy_init(struct sun8i_hdmi_phy *phy)
SUN8I_HDMI_PHY_DBG_CTRL_ADDR(I2C_ADDR)); SUN8I_HDMI_PHY_DBG_CTRL_ADDR(I2C_ADDR));
} }
void sun8i_hdmi_phy_init(struct sun8i_hdmi_phy *phy)
{
/* enable read access to HDMI controller */
regmap_write(phy->regs, SUN8I_HDMI_PHY_READ_EN_REG,
SUN8I_HDMI_PHY_READ_EN_MAGIC);
/* unscramble register offsets */
regmap_write(phy->regs, SUN8I_HDMI_PHY_UNSCRAMBLE_REG,
SUN8I_HDMI_PHY_UNSCRAMBLE_MAGIC);
phy->variant->phy_init(phy);
}
const struct dw_hdmi_phy_ops *sun8i_hdmi_phy_get_ops(void) const struct dw_hdmi_phy_ops *sun8i_hdmi_phy_get_ops(void)
{ {
return &sun8i_hdmi_phy_ops; return &sun8i_hdmi_phy_ops;
...@@ -158,20 +176,31 @@ static struct regmap_config sun8i_hdmi_phy_regmap_config = { ...@@ -158,20 +176,31 @@ static struct regmap_config sun8i_hdmi_phy_regmap_config = {
.name = "phy" .name = "phy"
}; };
static const struct sun8i_hdmi_phy_variant sun8i_a83t_hdmi_phy = {
.phy_init = &sun8i_hdmi_phy_init_a83t,
.phy_disable = &sun8i_hdmi_phy_disable_a83t,
.phy_config = &sun8i_hdmi_phy_config_a83t,
};
static const struct of_device_id sun8i_hdmi_phy_of_table[] = { static const struct of_device_id sun8i_hdmi_phy_of_table[] = {
{ .compatible = "allwinner,sun8i-a83t-hdmi-phy" }, {
.compatible = "allwinner,sun8i-a83t-hdmi-phy",
.data = &sun8i_a83t_hdmi_phy,
},
{ /* sentinel */ } { /* sentinel */ }
}; };
int sun8i_hdmi_phy_probe(struct sun8i_dw_hdmi *hdmi, struct device_node *node) int sun8i_hdmi_phy_probe(struct sun8i_dw_hdmi *hdmi, struct device_node *node)
{ {
const struct of_device_id *match;
struct device *dev = hdmi->dev; struct device *dev = hdmi->dev;
struct sun8i_hdmi_phy *phy; struct sun8i_hdmi_phy *phy;
struct resource res; struct resource res;
void __iomem *regs; void __iomem *regs;
int ret; int ret;
if (!of_match_node(sun8i_hdmi_phy_of_table, node)) { match = of_match_node(sun8i_hdmi_phy_of_table, node);
if (!match) {
dev_err(dev, "Incompatible HDMI PHY\n"); dev_err(dev, "Incompatible HDMI PHY\n");
return -EINVAL; return -EINVAL;
} }
...@@ -180,6 +209,8 @@ int sun8i_hdmi_phy_probe(struct sun8i_dw_hdmi *hdmi, struct device_node *node) ...@@ -180,6 +209,8 @@ int sun8i_hdmi_phy_probe(struct sun8i_dw_hdmi *hdmi, struct device_node *node)
if (!phy) if (!phy)
return -ENOMEM; return -ENOMEM;
phy->variant = (struct sun8i_hdmi_phy_variant *)match->data;
ret = of_address_to_resource(node, 0, &res); ret = of_address_to_resource(node, 0, &res);
if (ret) { if (ret) {
dev_err(dev, "phy: Couldn't get our resources\n"); dev_err(dev, "phy: Couldn't get our resources\n");
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment