diff options
Diffstat (limited to 'drivers/gpu/drm/rockchip/inno_hdmi.c')
-rw-r--r-- | drivers/gpu/drm/rockchip/inno_hdmi.c | 59 |
1 files changed, 43 insertions, 16 deletions
diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.c b/drivers/gpu/drm/rockchip/inno_hdmi.c index 898d90155057..db4b4038e51d 100644 --- a/drivers/gpu/drm/rockchip/inno_hdmi.c +++ b/drivers/gpu/drm/rockchip/inno_hdmi.c @@ -10,10 +10,12 @@ #include <linux/delay.h> #include <linux/err.h> #include <linux/hdmi.h> +#include <linux/mfd/syscon.h> #include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/mutex.h> #include <linux/platform_device.h> +#include <linux/regmap.h> #include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> @@ -29,8 +31,19 @@ #include "inno_hdmi.h" +#define HIWORD_UPDATE(val, mask) ((val) | (mask) << 16) + #define INNO_HDMI_MIN_TMDS_CLOCK 25000000U +#define RK3036_GRF_SOC_CON2 0x148 +#define RK3036_HDMI_PHSYNC BIT(4) +#define RK3036_HDMI_PVSYNC BIT(5) + +enum inno_hdmi_dev_type { + RK3036_HDMI, + RK3128_HDMI, +}; + struct inno_hdmi_phy_config { unsigned long pixelclock; u8 pre_emphasis; @@ -38,6 +51,7 @@ struct inno_hdmi_phy_config { }; struct inno_hdmi_variant { + enum inno_hdmi_dev_type dev_type; struct inno_hdmi_phy_config *phy_configs; struct inno_hdmi_phy_config *default_phy_config; }; @@ -58,6 +72,7 @@ struct inno_hdmi { struct clk *pclk; struct clk *refclk; void __iomem *regs; + struct regmap *grf; struct drm_connector connector; struct rockchip_encoder encoder; @@ -374,7 +389,15 @@ static int inno_hdmi_config_video_csc(struct inno_hdmi *hdmi) static int inno_hdmi_config_video_timing(struct inno_hdmi *hdmi, struct drm_display_mode *mode) { - int value; + int value, psync; + + if (hdmi->variant->dev_type == RK3036_HDMI) { + psync = mode->flags & DRM_MODE_FLAG_PHSYNC ? RK3036_HDMI_PHSYNC : 0; + value = HIWORD_UPDATE(psync, RK3036_HDMI_PHSYNC); + psync = mode->flags & DRM_MODE_FLAG_PVSYNC ? RK3036_HDMI_PVSYNC : 0; + value |= HIWORD_UPDATE(psync, RK3036_HDMI_PVSYNC); + regmap_write(hdmi->grf, RK3036_GRF_SOC_CON2, value); + } /* Set detail external video timing polarity and interlace mode */ value = v_EXTERANL_VIDEO(1); @@ -471,7 +494,7 @@ static int inno_hdmi_setup(struct inno_hdmi *hdmi, } static enum drm_mode_status inno_hdmi_display_mode_valid(struct inno_hdmi *hdmi, - struct drm_display_mode *mode) + const struct drm_display_mode *mode) { unsigned long mpixelclk, max_tolerance; long rounded_refclk; @@ -577,7 +600,7 @@ static int inno_hdmi_connector_get_modes(struct drm_connector *connector) static enum drm_mode_status inno_hdmi_connector_mode_valid(struct drm_connector *connector, - struct drm_display_mode *mode) + const struct drm_display_mode *mode) { struct inno_hdmi *hdmi = connector_to_inno_hdmi(connector); @@ -885,32 +908,34 @@ static int inno_hdmi_bind(struct device *dev, struct device *master, return PTR_ERR(hdmi->regs); hdmi->pclk = devm_clk_get(hdmi->dev, "pclk"); - if (IS_ERR(hdmi->pclk)) { - DRM_DEV_ERROR(hdmi->dev, "Unable to get HDMI pclk clk\n"); - return PTR_ERR(hdmi->pclk); - } + if (IS_ERR(hdmi->pclk)) + return dev_err_probe(dev, PTR_ERR(hdmi->pclk), "Unable to get HDMI pclk\n"); ret = clk_prepare_enable(hdmi->pclk); - if (ret) { - DRM_DEV_ERROR(hdmi->dev, - "Cannot enable HDMI pclk clock: %d\n", ret); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "Cannot enable HDMI pclk: %d\n", ret); hdmi->refclk = devm_clk_get_optional(hdmi->dev, "ref"); if (IS_ERR(hdmi->refclk)) { - DRM_DEV_ERROR(hdmi->dev, "Unable to get HDMI reference clock\n"); - ret = PTR_ERR(hdmi->refclk); + ret = dev_err_probe(dev, PTR_ERR(hdmi->refclk), "Unable to get HDMI refclk\n"); goto err_disable_pclk; } ret = clk_prepare_enable(hdmi->refclk); if (ret) { - DRM_DEV_ERROR(hdmi->dev, - "Cannot enable HDMI reference clock: %d\n", ret); + ret = dev_err_probe(dev, ret, "Cannot enable HDMI refclk: %d\n", ret); goto err_disable_pclk; } + if (hdmi->variant->dev_type == RK3036_HDMI) { + hdmi->grf = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,grf"); + if (IS_ERR(hdmi->grf)) { + ret = dev_err_probe(dev, PTR_ERR(hdmi->grf), + "Unable to get rockchip,grf\n"); + goto err_disable_clk; + } + } + irq = platform_get_irq(pdev, 0); if (irq < 0) { ret = irq; @@ -995,11 +1020,13 @@ static void inno_hdmi_remove(struct platform_device *pdev) } static const struct inno_hdmi_variant rk3036_inno_hdmi_variant = { + .dev_type = RK3036_HDMI, .phy_configs = rk3036_hdmi_phy_configs, .default_phy_config = &rk3036_hdmi_phy_configs[1], }; static const struct inno_hdmi_variant rk3128_inno_hdmi_variant = { + .dev_type = RK3128_HDMI, .phy_configs = rk3128_hdmi_phy_configs, .default_phy_config = &rk3128_hdmi_phy_configs[1], }; |