diff options
Diffstat (limited to 'drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c')
| -rw-r--r-- | drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c | 195 |
1 files changed, 119 insertions, 76 deletions
diff --git a/drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c index ed6e8f036f4b..c9fe6aa3e3e3 100644 --- a/drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c +++ b/drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c @@ -14,6 +14,7 @@ #include <linux/module.h> #include <linux/platform_device.h> #include <linux/phy/phy.h> +#include <linux/phy/phy-hdmi.h> #include <linux/regmap.h> #include <linux/workqueue.h> @@ -38,21 +39,16 @@ #define RK3576_HDMI_HDCP14_MEM_EN BIT(15) #define RK3576_VO0_GRF_SOC_CON8 0x0020 -#define RK3576_COLOR_FORMAT_MASK (0xf << 4) -#define RK3576_COLOR_DEPTH_MASK (0xf << 8) -#define RK3576_RGB (0 << 4) -#define RK3576_YUV422 (0x1 << 4) -#define RK3576_YUV444 (0x2 << 4) -#define RK3576_YUV420 (0x3 << 4) -#define RK3576_8BPC (0x0 << 8) -#define RK3576_10BPC (0x6 << 8) +#define RK3576_COLOR_DEPTH_MASK GENMASK(11, 8) +#define RK3576_8BPC 0x0 +#define RK3576_10BPC 0x6 +#define RK3576_COLOR_FORMAT_MASK GENMASK(7, 4) +#define RK3576_RGB 0x9 +#define RK3576_YUV422 0x1 +#define RK3576_YUV444 0x2 +#define RK3576_YUV420 0x3 #define RK3576_CECIN_MASK BIT(3) -#define RK3576_VO0_GRF_SOC_CON12 0x0030 -#define RK3576_GRF_OSDA_DLYN (0xf << 12) -#define RK3576_GRF_OSDA_DIV (0x7f << 1) -#define RK3576_GRF_OSDA_DLY_EN BIT(0) - #define RK3576_VO0_GRF_SOC_CON14 0x0038 #define RK3576_I2S_SEL_MASK BIT(0) #define RK3576_SPDIF_SEL_MASK BIT(1) @@ -74,6 +70,12 @@ #define RK3588_HDMI1_LEVEL_INT BIT(24) #define RK3588_GRF_VO1_CON3 0x000c #define RK3588_GRF_VO1_CON6 0x0018 +#define RK3588_COLOR_DEPTH_MASK GENMASK(7, 4) +#define RK3588_8BPC 0x0 +#define RK3588_10BPC 0x6 +#define RK3588_COLOR_FORMAT_MASK GENMASK(3, 0) +#define RK3588_RGB 0x0 +#define RK3588_YUV420 0x3 #define RK3588_SCLIN_MASK BIT(9) #define RK3588_SDAIN_MASK BIT(10) #define RK3588_MODE_MASK BIT(11) @@ -92,14 +94,16 @@ struct rockchip_hdmi_qp { struct rockchip_encoder encoder; struct dw_hdmi_qp *hdmi; struct phy *phy; - struct gpio_desc *enable_gpio; + struct gpio_desc *frl_enable_gpio; struct delayed_work hpd_work; int port_id; const struct rockchip_hdmi_qp_ctrl_ops *ctrl_ops; + unsigned long long tmds_char_rate; }; struct rockchip_hdmi_qp_ctrl_ops { void (*io_init)(struct rockchip_hdmi_qp *hdmi); + void (*enc_init)(struct rockchip_hdmi_qp *hdmi, struct rockchip_crtc_state *state); irqreturn_t (*irq_callback)(int irq, void *dev_id); irqreturn_t (*hardirq_callback)(int irq, void *dev_id); }; @@ -115,23 +119,15 @@ static void dw_hdmi_qp_rockchip_encoder_enable(struct drm_encoder *encoder) { struct rockchip_hdmi_qp *hdmi = to_rockchip_hdmi_qp(encoder); struct drm_crtc *crtc = encoder->crtc; - unsigned long long rate; /* Unconditionally switch to TMDS as FRL is not yet supported */ - gpiod_set_value(hdmi->enable_gpio, 1); - - if (crtc && crtc->state) { - rate = drm_hdmi_compute_mode_clock(&crtc->state->adjusted_mode, - 8, HDMI_COLORSPACE_RGB); - /* - * FIXME: Temporary workaround to pass pixel clock rate - * to the PHY driver until phy_configure_opts_hdmi - * becomes available in the PHY API. See also the related - * comment in rk_hdptx_phy_power_on() from - * drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c - */ - phy_set_bus_width(hdmi->phy, div_u64(rate, 100)); - } + gpiod_set_value(hdmi->frl_enable_gpio, 0); + + if (!crtc || !crtc->state) + return; + + if (hdmi->ctrl_ops->enc_init) + hdmi->ctrl_ops->enc_init(hdmi, to_rockchip_crtc_state(crtc->state)); } static int @@ -139,12 +135,29 @@ dw_hdmi_qp_rockchip_encoder_atomic_check(struct drm_encoder *encoder, struct drm_crtc_state *crtc_state, struct drm_connector_state *conn_state) { + struct rockchip_hdmi_qp *hdmi = to_rockchip_hdmi_qp(encoder); struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state); + union phy_configure_opts phy_cfg = {}; + int ret; + + if (hdmi->tmds_char_rate == conn_state->hdmi.tmds_char_rate && + s->output_bpc == conn_state->hdmi.output_bpc) + return 0; + + phy_cfg.hdmi.tmds_char_rate = conn_state->hdmi.tmds_char_rate; + phy_cfg.hdmi.bpc = conn_state->hdmi.output_bpc; + + ret = phy_configure(hdmi->phy, &phy_cfg); + if (!ret) { + hdmi->tmds_char_rate = conn_state->hdmi.tmds_char_rate; + s->output_mode = ROCKCHIP_OUT_MODE_AAAA; + s->output_type = DRM_MODE_CONNECTOR_HDMIA; + s->output_bpc = conn_state->hdmi.output_bpc; + } else { + dev_err(hdmi->dev, "Failed to configure phy: %d\n", ret); + } - s->output_mode = ROCKCHIP_OUT_MODE_AAAA; - s->output_type = DRM_MODE_CONNECTOR_HDMIA; - - return 0; + return ret; } static const struct @@ -375,15 +388,45 @@ static void dw_hdmi_qp_rk3588_io_init(struct rockchip_hdmi_qp *hdmi) regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val); } +static void dw_hdmi_qp_rk3576_enc_init(struct rockchip_hdmi_qp *hdmi, + struct rockchip_crtc_state *state) +{ + u32 val; + + if (state->output_bpc == 10) + val = FIELD_PREP_WM16(RK3576_COLOR_DEPTH_MASK, RK3576_10BPC); + else + val = FIELD_PREP_WM16(RK3576_COLOR_DEPTH_MASK, RK3576_8BPC); + + regmap_write(hdmi->vo_regmap, RK3576_VO0_GRF_SOC_CON8, val); +} + +static void dw_hdmi_qp_rk3588_enc_init(struct rockchip_hdmi_qp *hdmi, + struct rockchip_crtc_state *state) +{ + u32 val; + + if (state->output_bpc == 10) + val = FIELD_PREP_WM16(RK3588_COLOR_DEPTH_MASK, RK3588_10BPC); + else + val = FIELD_PREP_WM16(RK3588_COLOR_DEPTH_MASK, RK3588_8BPC); + + regmap_write(hdmi->vo_regmap, + hdmi->port_id ? RK3588_GRF_VO1_CON6 : RK3588_GRF_VO1_CON3, + val); +} + static const struct rockchip_hdmi_qp_ctrl_ops rk3576_hdmi_ctrl_ops = { .io_init = dw_hdmi_qp_rk3576_io_init, - .irq_callback = dw_hdmi_qp_rk3576_irq, + .enc_init = dw_hdmi_qp_rk3576_enc_init, + .irq_callback = dw_hdmi_qp_rk3576_irq, .hardirq_callback = dw_hdmi_qp_rk3576_hardirq, }; static const struct rockchip_hdmi_qp_ctrl_ops rk3588_hdmi_ctrl_ops = { .io_init = dw_hdmi_qp_rk3588_io_init, - .irq_callback = dw_hdmi_qp_rk3588_irq, + .enc_init = dw_hdmi_qp_rk3588_enc_init, + .irq_callback = dw_hdmi_qp_rk3588_irq, .hardirq_callback = dw_hdmi_qp_rk3588_hardirq, }; @@ -429,14 +472,15 @@ static int dw_hdmi_qp_rockchip_bind(struct device *dev, struct device *master, void *data) { struct platform_device *pdev = to_platform_device(dev); + struct dw_hdmi_qp_plat_data plat_data = {}; const struct rockchip_hdmi_qp_cfg *cfg; - struct dw_hdmi_qp_plat_data plat_data; struct drm_device *drm = data; struct drm_connector *connector; struct drm_encoder *encoder; struct rockchip_hdmi_qp *hdmi; struct resource *res; struct clk_bulk_data *clks; + struct clk *ref_clk; int ret, irq, i; if (!pdev->dev.of_node) @@ -455,10 +499,8 @@ static int dw_hdmi_qp_rockchip_bind(struct device *dev, struct device *master, return -ENODEV; if (!cfg->ctrl_ops || !cfg->ctrl_ops->io_init || - !cfg->ctrl_ops->irq_callback || !cfg->ctrl_ops->hardirq_callback) { - dev_err(dev, "Missing platform ctrl ops\n"); - return -ENODEV; - } + !cfg->ctrl_ops->irq_callback || !cfg->ctrl_ops->hardirq_callback) + return dev_err_probe(dev, -ENODEV, "Missing platform ctrl ops\n"); hdmi->ctrl_ops = cfg->ctrl_ops; hdmi->dev = &pdev->dev; @@ -471,13 +513,13 @@ static int dw_hdmi_qp_rockchip_bind(struct device *dev, struct device *master, break; } } - if (hdmi->port_id < 0) { - dev_err(hdmi->dev, "Failed to match HDMI port ID\n"); - return hdmi->port_id; - } + if (hdmi->port_id < 0) + return dev_err_probe(hdmi->dev, hdmi->port_id, + "Failed to match HDMI port ID\n"); plat_data.phy_ops = cfg->phy_ops; plat_data.phy_data = hdmi; + plat_data.max_bpc = 10; encoder = &hdmi->encoder.encoder; encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node); @@ -495,39 +537,38 @@ static int dw_hdmi_qp_rockchip_bind(struct device *dev, struct device *master, hdmi->regmap = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,grf"); - if (IS_ERR(hdmi->regmap)) { - dev_err(hdmi->dev, "Unable to get rockchip,grf\n"); - return PTR_ERR(hdmi->regmap); - } + if (IS_ERR(hdmi->regmap)) + return dev_err_probe(hdmi->dev, PTR_ERR(hdmi->regmap), + "Unable to get rockchip,grf\n"); hdmi->vo_regmap = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,vo-grf"); - if (IS_ERR(hdmi->vo_regmap)) { - dev_err(hdmi->dev, "Unable to get rockchip,vo-grf\n"); - return PTR_ERR(hdmi->vo_regmap); - } + if (IS_ERR(hdmi->vo_regmap)) + return dev_err_probe(hdmi->dev, PTR_ERR(hdmi->vo_regmap), + "Unable to get rockchip,vo-grf\n"); ret = devm_clk_bulk_get_all_enabled(hdmi->dev, &clks); - if (ret < 0) { - dev_err(hdmi->dev, "Failed to get clocks: %d\n", ret); - return ret; - } + if (ret < 0) + return dev_err_probe(hdmi->dev, ret, "Failed to get clocks\n"); - hdmi->enable_gpio = devm_gpiod_get_optional(hdmi->dev, "enable", - GPIOD_OUT_HIGH); - if (IS_ERR(hdmi->enable_gpio)) { - ret = PTR_ERR(hdmi->enable_gpio); - dev_err(hdmi->dev, "Failed to request enable GPIO: %d\n", ret); - return ret; - } + ref_clk = clk_get(hdmi->dev, "ref"); + if (IS_ERR(ref_clk)) + return dev_err_probe(hdmi->dev, PTR_ERR(ref_clk), + "Failed to get ref clock\n"); + + plat_data.ref_clk_rate = clk_get_rate(ref_clk); + clk_put(ref_clk); + + hdmi->frl_enable_gpio = devm_gpiod_get_optional(hdmi->dev, "frl-enable", + GPIOD_OUT_LOW); + if (IS_ERR(hdmi->frl_enable_gpio)) + return dev_err_probe(hdmi->dev, PTR_ERR(hdmi->frl_enable_gpio), + "Failed to request FRL enable GPIO\n"); hdmi->phy = devm_of_phy_get_by_index(dev, dev->of_node, 0); - if (IS_ERR(hdmi->phy)) { - ret = PTR_ERR(hdmi->phy); - if (ret != -EPROBE_DEFER) - dev_err(hdmi->dev, "failed to get phy: %d\n", ret); - return ret; - } + if (IS_ERR(hdmi->phy)) + return dev_err_probe(hdmi->dev, PTR_ERR(hdmi->phy), + "Failed to get phy\n"); cfg->ctrl_ops->io_init(hdmi); @@ -537,6 +578,10 @@ static int dw_hdmi_qp_rockchip_bind(struct device *dev, struct device *master, if (plat_data.main_irq < 0) return plat_data.main_irq; + plat_data.cec_irq = platform_get_irq_byname(pdev, "cec"); + if (plat_data.cec_irq < 0) + return plat_data.cec_irq; + irq = platform_get_irq_byname(pdev, "hpd"); if (irq < 0) return irq; @@ -556,17 +601,15 @@ static int dw_hdmi_qp_rockchip_bind(struct device *dev, struct device *master, hdmi->hdmi = dw_hdmi_qp_bind(pdev, encoder, &plat_data); if (IS_ERR(hdmi->hdmi)) { - ret = PTR_ERR(hdmi->hdmi); drm_encoder_cleanup(encoder); - return ret; + return dev_err_probe(hdmi->dev, PTR_ERR(hdmi->hdmi), + "Failed to bind dw-hdmi-qp"); } connector = drm_bridge_connector_init(drm, encoder); - if (IS_ERR(connector)) { - ret = PTR_ERR(connector); - dev_err(hdmi->dev, "failed to init bridge connector: %d\n", ret); - return ret; - } + if (IS_ERR(connector)) + return dev_err_probe(hdmi->dev, PTR_ERR(connector), + "Failed to init bridge connector\n"); return drm_connector_attach_encoder(connector, encoder); } |
