From 9df731a176aab8e03b984b681b1bea01ccff6644 Mon Sep 17 00:00:00 2001 From: hc <hc@nodka.com> Date: Mon, 06 Nov 2023 07:23:06 +0000 Subject: [PATCH] rk3568 rt uboot init --- kernel/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 334 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 files changed, 320 insertions(+), 14 deletions(-) diff --git a/kernel/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/kernel/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c index 25cf927..27efb38 100644 --- a/kernel/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c +++ b/kernel/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c @@ -8,8 +8,10 @@ */ #include <linux/clk.h> +#include <linux/gpio/consumer.h> #include <linux/mfd/syscon.h> #include <linux/module.h> +#include <linux/of_gpio.h> #include <linux/platform_device.h> #include <linux/phy/phy.h> #include <linux/regmap.h> @@ -57,6 +59,22 @@ #define RK3399_GRF_SOC_CON20 0x6250 #define RK3399_HDMI_LCDC_SEL BIT(6) +#define RK3528_VO_GRF_HDMI_MASK 0x60014 +#define RK3528_HDMI_SNKDET_SEL BIT(6) +#define RK3528_HDMI_SNKDET BIT(5) +#define RK3528_HDMI_CECIN_MSK BIT(2) +#define RK3528_HDMI_SDAIN_MSK BIT(1) +#define RK3528_HDMI_SCLIN_MSK BIT(0) + +#define RK3528PMU_GRF_SOC_CON6 0x70018 +#define RK3528_HDMI_SDA5V_GRF BIT(6) +#define RK3528_HDMI_SCL5V_GRF BIT(5) +#define RK3528_HDMI_CEC5V_GRF BIT(4) +#define RK3528_HDMI_HPD5V_GRF BIT(3) + +#define RK3528_GPIO_SWPORT_DR_L 0x0000 +#define RK3528_GPIO0_A2_DR BIT(2) + #define RK3568_GRF_VO_CON1 0x0364 #define RK3568_HDMI_SDAIN_MSK BIT(15) #define RK3568_HDMI_SCLIN_MSK BIT(14) @@ -103,12 +121,14 @@ struct rockchip_hdmi { struct device *dev; struct regmap *regmap; + void __iomem *gpio_base; struct drm_encoder encoder; const struct rockchip_hdmi_chip_data *chip_data; struct clk *phyref_clk; struct clk *grf_clk; struct clk *hclk_vio; struct clk *hclk_vop; + struct clk *dclk_vop; struct dw_hdmi *hdmi; struct phy *phy; @@ -117,12 +137,14 @@ bool unsupported_deep_color; bool skip_check_420_mode; bool mode_changed; + bool hpd_wake_en; u8 force_output; u8 id; unsigned long bus_format; unsigned long output_bus_format; unsigned long enc_out_encoding; + unsigned long prev_bus_format; int color_changed; struct drm_property *color_depth_property; @@ -143,6 +165,12 @@ unsigned int phy_bus_width; enum drm_hdmi_output_type hdmi_output; struct rockchip_drm_sub_dev sub_dev; + + struct gpio_desc *hpd_gpiod; + struct pinctrl *p; + struct pinctrl_state *idle_state; + struct pinctrl_state *default_state; + int hpd_irq; }; #define to_rockchip_hdmi(x) container_of(x, struct rockchip_hdmi, x) @@ -460,6 +488,37 @@ { ~0UL, 0x0000, 0x0000, 0x0000}, }; +static int hdmi_bus_fmt_color_depth(unsigned int bus_format) +{ + switch (bus_format) { + case MEDIA_BUS_FMT_RGB888_1X24: + case MEDIA_BUS_FMT_YUV8_1X24: + case MEDIA_BUS_FMT_UYVY8_1X16: + case MEDIA_BUS_FMT_UYYVYY8_0_5X24: + return 8; + + case MEDIA_BUS_FMT_RGB101010_1X30: + case MEDIA_BUS_FMT_YUV10_1X30: + case MEDIA_BUS_FMT_UYVY10_1X20: + case MEDIA_BUS_FMT_UYYVYY10_0_5X30: + return 10; + + case MEDIA_BUS_FMT_RGB121212_1X36: + case MEDIA_BUS_FMT_YUV12_1X36: + case MEDIA_BUS_FMT_UYVY12_1X24: + case MEDIA_BUS_FMT_UYYVYY12_0_5X36: + return 12; + + case MEDIA_BUS_FMT_RGB161616_1X48: + case MEDIA_BUS_FMT_YUV16_1X48: + case MEDIA_BUS_FMT_UYYVYY16_0_5X48: + return 16; + + default: + return 0; + } +} + static int rockchip_hdmi_update_phy_table(struct rockchip_hdmi *hdmi, u32 *config, int phy_table_size) @@ -482,6 +541,55 @@ } return 0; +} + +static irqreturn_t rockchip_hdmi_hpd_irq_handler(int irq, void *arg) +{ + u32 val; + struct rockchip_hdmi *hdmi = arg; + + val = gpiod_get_value(hdmi->hpd_gpiod); + if (val) { + val = HIWORD_UPDATE(RK3528_HDMI_SNKDET, RK3528_HDMI_SNKDET); + if (hdmi->hdmi && hdmi->hpd_wake_en && hdmi->hpd_gpiod) + dw_hdmi_set_hpd_wake(hdmi->hdmi); + } else { + val = HIWORD_UPDATE(0, RK3528_HDMI_SNKDET); + } + regmap_write(hdmi->regmap, RK3528_VO_GRF_HDMI_MASK, val); + + return IRQ_HANDLED; +} + +static void dw_hdmi_rk3528_gpio_hpd_init(struct rockchip_hdmi *hdmi) +{ + u32 val; + + if (hdmi->hpd_gpiod) { + /* gpio0_a2's input enable is controlled by gpio output data bit */ + val = HIWORD_UPDATE(RK3528_GPIO0_A2_DR, RK3528_GPIO0_A2_DR); + writel(val, hdmi->gpio_base + RK3528_GPIO_SWPORT_DR_L); + + val = HIWORD_UPDATE(RK3528_HDMI_SNKDET_SEL | RK3528_HDMI_SDAIN_MSK | + RK3528_HDMI_SCLIN_MSK, + RK3528_HDMI_SNKDET_SEL | RK3528_HDMI_SDAIN_MSK | + RK3528_HDMI_SCLIN_MSK); + } else { + val = HIWORD_UPDATE(RK3528_HDMI_SDAIN_MSK | RK3528_HDMI_SCLIN_MSK, + RK3528_HDMI_SDAIN_MSK | RK3528_HDMI_SCLIN_MSK); + } + + regmap_write(hdmi->regmap, RK3528_VO_GRF_HDMI_MASK, val); + + val = gpiod_get_value(hdmi->hpd_gpiod); + if (val) { + val = HIWORD_UPDATE(RK3528_HDMI_SNKDET, RK3528_HDMI_SNKDET); + if (hdmi->hdmi && hdmi->hpd_wake_en && hdmi->hpd_gpiod) + dw_hdmi_set_hpd_wake(hdmi->hdmi); + } else { + val = HIWORD_UPDATE(0, RK3528_HDMI_SNKDET); + } + regmap_write(hdmi->regmap, RK3528_VO_GRF_HDMI_MASK, val); } static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi) @@ -539,6 +647,12 @@ return PTR_ERR(hdmi->hclk_vop); } + hdmi->dclk_vop = devm_clk_get_optional(hdmi->dev, "dclk_vop"); + if (IS_ERR(hdmi->dclk_vop)) { + dev_err(hdmi->dev, "failed to get dclk_vop\n"); + return PTR_ERR(hdmi->dclk_vop); + } + ret = of_property_read_u32(np, "max-tmdsclk", &hdmi->max_tmdsclk); if (ret != -EINVAL && ret < 0) { @@ -576,6 +690,70 @@ kfree(phy_config); } else { dev_dbg(hdmi->dev, "use default hdmi phy table\n"); + } + + hdmi->hpd_gpiod = devm_gpiod_get_optional(hdmi->dev, "hpd", GPIOD_IN); + + if (IS_ERR(hdmi->hpd_gpiod)) { + dev_err(hdmi->dev, "error getting HDP GPIO: %ld\n", + PTR_ERR(hdmi->hpd_gpiod)); + return PTR_ERR(hdmi->hpd_gpiod); + } + + if (hdmi->hpd_gpiod) { + struct resource *res; + struct platform_device *pdev = to_platform_device(hdmi->dev); + + /* gpio interrupt reflects hpd status */ + hdmi->hpd_irq = gpiod_to_irq(hdmi->hpd_gpiod); + if (hdmi->hpd_irq < 0) + return -EINVAL; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!res) { + DRM_DEV_ERROR(hdmi->dev, "failed to get gpio regs\n"); + return -EINVAL; + } + + hdmi->gpio_base = devm_ioremap(hdmi->dev, res->start, resource_size(res)); + if (IS_ERR(hdmi->gpio_base)) { + DRM_DEV_ERROR(hdmi->dev, "Unable to get gpio ioregmap\n"); + return PTR_ERR(hdmi->gpio_base); + } + + dw_hdmi_rk3528_gpio_hpd_init(hdmi); + ret = devm_request_threaded_irq(hdmi->dev, hdmi->hpd_irq, NULL, + rockchip_hdmi_hpd_irq_handler, + IRQF_TRIGGER_RISING | + IRQF_TRIGGER_FALLING | + IRQF_ONESHOT, + "hdmi-hpd", hdmi); + if (ret) { + dev_err(hdmi->dev, "failed to request hpd IRQ: %d\n", ret); + return ret; + } + + hdmi->hpd_wake_en = device_property_read_bool(hdmi->dev, "hpd-wake-up"); + if (hdmi->hpd_wake_en) + enable_irq_wake(hdmi->hpd_irq); + } + + hdmi->p = devm_pinctrl_get(hdmi->dev); + if (IS_ERR(hdmi->p)) { + dev_err(hdmi->dev, "could not get pinctrl\n"); + return PTR_ERR(hdmi->p); + } + + hdmi->idle_state = pinctrl_lookup_state(hdmi->p, "idle"); + if (IS_ERR(hdmi->idle_state)) { + dev_dbg(hdmi->dev, "idle state is not defined\n"); + return 0; + } + + hdmi->default_state = pinctrl_lookup_state(hdmi->p, "default"); + if (IS_ERR(hdmi->default_state)) { + dev_err(hdmi->dev, "could not find default state\n"); + return PTR_ERR(hdmi->default_state); } return 0; @@ -670,6 +848,9 @@ struct drm_crtc *crtc = encoder->crtc; struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc->state); + if (WARN_ON(!crtc || !crtc->state)) + return; + if (!hdmi->mode_changed) s->output_if &= ~VOP_OUTPUT_IF_HDMI0; /* @@ -754,6 +935,7 @@ unsigned int color_depth; bool support_dc = false; bool sink_is_hdmi = dw_hdmi_get_output_whether_hdmi(hdmi->hdmi); + bool yuv422_out = false; int max_tmds_clock = info->max_tmds_clock; int output_eotf; @@ -829,22 +1011,27 @@ *eotf = output_eotf; } - if ((*eotf > TRADITIONAL_GAMMA_HDR && - conn_state->connector->hdr_sink_metadata.hdmi_type1.eotf & - BIT(*eotf)) || (hdmi->colorimetry == - RK_HDMI_COLORIMETRY_BT2020 && info->hdmi.colorimetry & - (BIT(6) | BIT(7)))) + /* bt2020 sdr/hdr output */ + if (hdmi->colorimetry == RK_HDMI_COLORIMETRY_BT2020 && + info->hdmi.colorimetry & (BIT(6) | BIT(7))) { *enc_out_encoding = V4L2_YCBCR_ENC_BT2020; - else if ((vic == 6) || (vic == 7) || (vic == 21) || (vic == 22) || - (vic == 2) || (vic == 3) || (vic == 17) || (vic == 18)) - *enc_out_encoding = V4L2_YCBCR_ENC_601; - else + yuv422_out = true; + /* bt709 hdr output */ + } else if (hdmi->colorimetry != RK_HDMI_COLORIMETRY_BT2020 && + (conn_state->connector->hdr_sink_metadata.hdmi_type1.eotf & BIT(*eotf) && + *eotf > TRADITIONAL_GAMMA_HDR)) { *enc_out_encoding = V4L2_YCBCR_ENC_709; + yuv422_out = true; + } else if ((vic == 6) || (vic == 7) || (vic == 21) || (vic == 22) || + (vic == 2) || (vic == 3) || (vic == 17) || (vic == 18)) { + *enc_out_encoding = V4L2_YCBCR_ENC_601; + } else { + *enc_out_encoding = V4L2_YCBCR_ENC_709; + } - if (*enc_out_encoding == V4L2_YCBCR_ENC_BT2020) { - /* BT2020 require color depth at lest 10bit */ - color_depth = 10; - /* We prefer use YCbCr422 to send 10bit */ + if ((yuv422_out || hdmi->hdmi_output == DRM_HDMI_OUTPUT_YCBCR_HQ) && + color_depth == 10 && hdmi_bus_fmt_color_depth(hdmi->prev_bus_format) == 8) { + /* We prefer use YCbCr422 to send hdr 10bit */ if (info->color_formats & DRM_COLOR_FORMAT_YCRCB422) *color_format = DRM_HDMI_OUTPUT_YCBCR422; } @@ -1063,6 +1250,14 @@ return hdmi->hdr_panel_blob_ptr; } +static void dw_hdmi_rockchip_update_color_format(struct drm_connector_state *conn_state, + void *data) +{ + struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; + + dw_hdmi_rockchip_check_color(conn_state, hdmi); +} + static bool dw_hdmi_rockchip_get_color_changed(void *data) { @@ -1072,6 +1267,63 @@ if (hdmi->color_changed) ret = true; hdmi->color_changed = 0; + + return ret; +} + +static bool +dw_hdmi_rockchip_check_hdr_color_change(struct drm_connector_state *conn_state, + void *data) +{ + struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; + + if (!conn_state || !data) + return false; + + if (dw_hdmi_rockchip_check_color(conn_state, hdmi)) + return true; + + return false; +} + +static void dw_hdmi_rockchip_set_prev_bus_format(void *data, unsigned long bus_format) +{ + struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; + + hdmi->prev_bus_format = bus_format; +} + +static void dw_hdmi_rockchip_set_ddc_io(void *data, bool enable) +{ + struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; + + if (!hdmi->p || !hdmi->idle_state || !hdmi->default_state) + return; + + if (!enable) { + if (pinctrl_select_state(hdmi->p, hdmi->idle_state)) + dev_err(hdmi->dev, "could not select idle state\n"); + } else { + if (pinctrl_select_state(hdmi->p, hdmi->default_state)) + dev_err(hdmi->dev, "could not select default state\n"); + } +} + +static int dw_hdmi_rockchip_dclk_set(void *data, bool enable, int vp_id) +{ + struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; + int ret = 0; + + if (!hdmi->dclk_vop) + return 0; + + if (enable) { + ret = clk_prepare_enable(hdmi->dclk_vop); + if (ret < 0) + dev_err(hdmi->dev, "failed to enable dclk_vop\n"); + } else { + clk_disable_unprepare(hdmi->dclk_vop); + } return ret; } @@ -1158,6 +1410,7 @@ } hdmi->bus_format = color; + hdmi->prev_bus_format = color; if (hdmi->hdmi_output == DRM_HDMI_OUTPUT_YCBCR422) { if (hdmi->colordepth == 12) @@ -1581,6 +1834,20 @@ .setup_hpd = dw_hdmi_rk3328_setup_hpd, }; +static enum drm_connector_status +dw_hdmi_rk3528_read_hpd(struct dw_hdmi *dw_hdmi, void *data) +{ + return dw_hdmi_phy_read_hpd(dw_hdmi, data); +} + +static const struct dw_hdmi_phy_ops rk3528_hdmi_phy_ops = { + .init = dw_hdmi_rockchip_genphy_init, + .disable = dw_hdmi_rockchip_genphy_disable, + .read_hpd = dw_hdmi_rk3528_read_hpd, + .update_hpd = dw_hdmi_phy_update_hpd, + .setup_hpd = dw_hdmi_phy_setup_hpd, +}; + static struct rockchip_hdmi_chip_data rk3328_chip_data = { .lcdsel_grf_reg = -1, }; @@ -1627,6 +1894,22 @@ .ycbcr_420_allowed = true, }; +static struct rockchip_hdmi_chip_data rk3528_chip_data = { + .lcdsel_grf_reg = -1, +}; + +static const struct dw_hdmi_plat_data rk3528_hdmi_drv_data = { + .mode_valid = dw_hdmi_rockchip_mode_valid, + .mpll_cfg = rockchip_mpll_cfg, + .cur_ctr = rockchip_cur_ctr, + .phy_config = rockchip_phy_config, + .phy_data = &rk3528_chip_data, + .phy_ops = &rk3528_hdmi_phy_ops, + .phy_name = "inno_dw_hdmi_phy2", + .phy_force_vendor = true, + .ycbcr_420_allowed = true, +}; + static struct rockchip_hdmi_chip_data rk3568_chip_data = { .lcdsel_grf_reg = -1, .ddc_en_reg = RK3568_GRF_VO_CON1, @@ -1658,6 +1941,9 @@ }, { .compatible = "rockchip,rk3399-dw-hdmi", .data = &rk3399_hdmi_drv_data + }, + { .compatible = "rockchip,rk3528-dw-hdmi", + .data = &rk3528_hdmi_drv_data }, { .compatible = "rockchip,rk3568-dw-hdmi", .data = &rk3568_hdmi_drv_data @@ -1713,6 +1999,16 @@ dw_hdmi_rockchip_get_hdr_blob; plat_data->get_color_changed = dw_hdmi_rockchip_get_color_changed; + plat_data->update_color_format = + dw_hdmi_rockchip_update_color_format; + plat_data->check_hdr_color_change = + dw_hdmi_rockchip_check_hdr_color_change; + plat_data->set_prev_bus_format = + dw_hdmi_rockchip_set_prev_bus_format; + plat_data->set_ddc_io = + dw_hdmi_rockchip_set_ddc_io; + plat_data->dclk_set = + dw_hdmi_rockchip_dclk_set; plat_data->property_ops = &dw_hdmi_rockchip_property_ops; encoder = &hdmi->encoder; @@ -1780,7 +2076,6 @@ platform_set_drvdata(pdev, hdmi); hdmi->hdmi = dw_hdmi_bind(pdev, encoder, plat_data); - /* * If dw_hdmi_bind() fails we'll never call dw_hdmi_unbind(), * which would have called the encoder cleanup. Do it manually. @@ -1833,6 +2128,11 @@ if (!hdmi) return; + if (hdmi->hpd_gpiod) { + disable_irq(hdmi->hpd_irq); + if (hdmi->hpd_wake_en) + disable_irq_wake(hdmi->hpd_irq); + } dw_hdmi_suspend(&pdev->dev, hdmi->hdmi); pm_runtime_put_sync(&pdev->dev); } @@ -1849,6 +2149,8 @@ { struct rockchip_hdmi *hdmi = dev_get_drvdata(dev); + if (hdmi->hpd_gpiod) + disable_irq(hdmi->hpd_irq); dw_hdmi_suspend(dev, hdmi->hdmi); pm_runtime_put_sync(dev); @@ -1859,6 +2161,10 @@ { struct rockchip_hdmi *hdmi = dev_get_drvdata(dev); + if (hdmi->hpd_gpiod) { + dw_hdmi_rk3528_gpio_hpd_init(hdmi); + enable_irq(hdmi->hpd_irq); + } pm_runtime_get_sync(dev); dw_hdmi_resume(dev, hdmi->hdmi); -- Gitblit v1.6.2