Because some RK chips use inno hdmi phy, such as RK3328, we add inno hdmi phy ops.
Signed-off-by: Algea Cao <algea....@rock-chips.com> --- drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 110 +++++++++++++++++++++++++++- 1 file changed, 107 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c index 09c77f9..7658b2f 100644 --- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c +++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c @@ -12,7 +12,7 @@ #include <linux/module.h> #include <linux/platform_device.h> #include <linux/regmap.h> - +#include <linux/phy/phy.h> #include <drm/drm_of.h> #include <drm/drmP.h> #include <drm/drm_crtc_helper.h> @@ -26,6 +26,18 @@ #define RK3288_HDMI_LCDC_SEL BIT(4) #define RK3399_GRF_SOC_CON20 0x6250 #define RK3399_HDMI_LCDC_SEL BIT(6) +#define RK3328_GRF_SOC_CON2 0x0408 +#define RK3328_DDC_MASK_EN ((3 << 10) | (3 << (10 + 16))) +#define RK3328_GRF_SOC_CON3 0x040c +#define RK3328_IO_CTRL_BY_HDMI (0xf0000000 | BIT(13) | BIT(12)) +#define RK3328_GRF_SOC_CON4 0x0410 +#define RK3328_IO_3V_DOMAIN (7 << (9 + 16)) +#define RK3328_IO_5V_DOMAIN ((7 << 9) | (3 << (9 + 16))) +#define RK3328_HPD_3V (BIT(8 + 16) | BIT(13 + 16)) +#define RK3228_GRF_SOC_CON2 0x0408 +#define RK3228_DDC_MASK_EN ((3 << 13) | (3 << (13 + 16))) +#define RK3228_GRF_SOC_CON6 0x0418 +#define RK3228_IO_3V_DOMAIN ((7 << 4) | (7 << (4 + 16))) #define HIWORD_UPDATE(val, mask) (val | (mask) << 16) @@ -49,10 +61,82 @@ struct rockchip_hdmi { enum dw_hdmi_devtype dev_type; struct clk *vpll_clk; struct clk *grf_clk; + struct phy *phy; }; #define to_rockchip_hdmi(x) container_of(x, struct rockchip_hdmi, x) +static int +inno_dw_hdmi_phy_init(struct dw_hdmi *dw_hdmi, void *data, + struct drm_display_mode *mode) +{ + struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; + + return phy_power_on(hdmi->phy); +} + +static void inno_dw_hdmi_phy_disable(struct dw_hdmi *dw_hdmi, void *data) +{ + struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; + + phy_power_off(hdmi->phy); +} + +static enum drm_connector_status +inno_dw_hdmi_phy_read_hpd(struct dw_hdmi *dw_hdmi, void *data) +{ + struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; + enum drm_connector_status status; + + status = dw_hdmi_phy_read_hpd(dw_hdmi, data); + + if (hdmi->dev_type == RK3228_HDMI) + return status; + + if (status == connector_status_connected) + regmap_write(hdmi->regmap, + RK3328_GRF_SOC_CON4, + RK3328_IO_5V_DOMAIN); + else + regmap_write(hdmi->regmap, + RK3328_GRF_SOC_CON4, + RK3328_IO_3V_DOMAIN); + return status; +} + +static int inno_dw_hdmi_init(struct rockchip_hdmi *hdmi) +{ + int ret; + + ret = clk_prepare_enable(hdmi->grf_clk); + if (ret < 0) { + dev_err(hdmi->dev, "failed to enable grfclk %d\n", ret); + return -EPROBE_DEFER; + } + if (hdmi->dev_type == RK3328_HDMI) { + /* Map HPD pin to 3V io */ + regmap_write(hdmi->regmap, + RK3328_GRF_SOC_CON4, + RK3328_IO_3V_DOMAIN | + RK3328_HPD_3V); + /* Map ddc pin to 5V io */ + regmap_write(hdmi->regmap, + RK3328_GRF_SOC_CON3, + RK3328_IO_CTRL_BY_HDMI); + regmap_write(hdmi->regmap, + RK3328_GRF_SOC_CON2, + RK3328_DDC_MASK_EN | + BIT(18)); + } else { + regmap_write(hdmi->regmap, RK3228_GRF_SOC_CON2, + RK3228_DDC_MASK_EN); + regmap_write(hdmi->regmap, RK3228_GRF_SOC_CON6, + RK3228_IO_3V_DOMAIN); + } + clk_disable_unprepare(hdmi->grf_clk); + return 0; +} + static const struct dw_hdmi_mpll_config rockchip_mpll_cfg[] = { { 27000000, { @@ -294,6 +378,12 @@ static const struct drm_encoder_helper_funcs dw_hdmi_rockchip_encoder_helper_fun .atomic_check = dw_hdmi_rockchip_encoder_atomic_check, }; +static const struct dw_hdmi_phy_ops inno_dw_hdmi_phy_ops = { + .init = inno_dw_hdmi_phy_init, + .disable = inno_dw_hdmi_phy_disable, + .read_hpd = inno_dw_hdmi_phy_read_hpd, +}; + static struct rockchip_hdmi_chip_data rk3288_chip_data = { .lcdsel_grf_reg = RK3288_GRF_SOC_CON6, .lcdsel_big = HIWORD_UPDATE(0, RK3288_HDMI_LCDC_SEL), @@ -317,6 +407,8 @@ static struct rockchip_hdmi_chip_data rk3399_chip_data = { static const struct dw_hdmi_plat_data rk3328_hdmi_drv_data = { .mode_valid = dw_hdmi_rockchip_mode_valid, + .phy_ops = &inno_dw_hdmi_phy_ops, + .phy_name = "inno_dw_hdmi_phy2", .dev_type = RK3328_HDMI, }; static const struct dw_hdmi_plat_data rk3399_hdmi_drv_data = { @@ -346,7 +438,7 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master, void *data) { struct platform_device *pdev = to_platform_device(dev); - const struct dw_hdmi_plat_data *plat_data; + struct dw_hdmi_plat_data *plat_data; const struct of_device_id *match; struct drm_device *drm = data; struct drm_encoder *encoder; @@ -361,7 +453,7 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master, return -ENOMEM; match = of_match_node(dw_hdmi_rockchip_dt_ids, pdev->dev.of_node); - plat_data = match->data; + plat_data = (struct dw_hdmi_plat_data *)match->data; hdmi->dev = &pdev->dev; hdmi->chip_data = plat_data->phy_data; hdmi->dev_type = plat_data->dev_type; @@ -383,6 +475,18 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master, return ret; } + if (hdmi->dev_type == RK3328_HDMI || hdmi->dev_type == RK3228_HDMI) { + hdmi->phy = devm_phy_get(dev, "hdmi_phy"); + if (IS_ERR(hdmi->phy)) { + ret = PTR_ERR(hdmi->phy); + dev_err(dev, "failed to get phy: %d\n", ret); + return ret; + } + plat_data->phy_data = hdmi; + ret = inno_dw_hdmi_init(hdmi); + if (ret < 0) + return ret; + } drm_encoder_helper_add(encoder, &dw_hdmi_rockchip_encoder_helper_funcs); drm_encoder_init(drm, encoder, &dw_hdmi_rockchip_encoder_funcs, DRM_MODE_ENCODER_TMDS, NULL); -- 2.7.4