The RK3588 SoC family integrates the newer Synopsys DesignWare HDMI 2.1
Quad-Pixel (QP) TX controller IP and a HDMI/eDP TX Combo PHY based on a
Samsung IP block.
Add just the basic support for now, i.e. RGB output up to 4K@60Hz,
without audio, CEC or any of the HDMI 2.1 specific features.
Co-developed-by: Algea Cao <algea.cao@rock-chips.com>
Signed-off-by: Algea Cao <algea.cao@rock-chips.com>
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
---
drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 261 +++++++++++++++++++++++++++-
1 file changed, 253 insertions(+), 8 deletions(-)
diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index ca6728a43159..48a777fe4214 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -29,8 +29,8 @@
#define RK3288_GRF_SOC_CON6 0x025C
#define RK3288_HDMI_LCDC_SEL BIT(4)
-#define RK3328_GRF_SOC_CON2 0x0408
+#define RK3328_GRF_SOC_CON2 0x0408
#define RK3328_HDMI_SDAIN_MSK BIT(11)
#define RK3328_HDMI_SCLIN_MSK BIT(10)
#define RK3328_HDMI_HPD_IOE BIT(2)
@@ -54,6 +54,21 @@
#define RK3568_HDMI_SDAIN_MSK BIT(15)
#define RK3568_HDMI_SCLIN_MSK BIT(14)
+#define RK3588_GRF_SOC_CON2 0x0308
+#define RK3588_HDMI0_HPD_INT_MSK BIT(13)
+#define RK3588_HDMI0_HPD_INT_CLR BIT(12)
+#define RK3588_GRF_SOC_CON7 0x031c
+#define RK3588_SET_HPD_PATH_MASK (0x3 << 12)
+#define RK3588_GRF_SOC_STATUS1 0x0384
+#define RK3588_HDMI0_LEVEL_INT BIT(16)
+#define RK3588_GRF_VO1_CON3 0x000c
+#define RK3588_SCLIN_MASK BIT(9)
+#define RK3588_SDAIN_MASK BIT(10)
+#define RK3588_MODE_MASK BIT(11)
+#define RK3588_I2S_SEL_MASK BIT(13)
+#define RK3588_GRF_VO1_CON9 0x0024
+#define RK3588_HDMI0_GRANT_SEL BIT(10)
+
#define HIWORD_UPDATE(val, mask) (val | (mask) << 16)
/**
@@ -71,6 +86,7 @@ struct rockchip_hdmi_chip_data {
struct rockchip_hdmi {
struct device *dev;
struct regmap *regmap;
+ struct regmap *vo1_regmap;
struct rockchip_encoder encoder;
const struct rockchip_hdmi_chip_data *chip_data;
const struct dw_hdmi_plat_data *plat_data;
@@ -78,6 +94,10 @@ struct rockchip_hdmi {
struct clk *grf_clk;
struct dw_hdmi *hdmi;
struct phy *phy;
+
+ bool is_hdmi_qp;
+ struct gpio_desc *qp_enable_gpio;
+ struct delayed_work qp_hpd_work;
};
static struct rockchip_hdmi *to_rockchip_hdmi(struct drm_encoder *encoder)
@@ -206,8 +226,12 @@ static const struct dw_hdmi_phy_config rockchip_phy_config[] = {
static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi)
{
+ static const char * const qp_clk_names[] = {
+ "pclk", "hdp", "earc", "aud", "hclk_vo1",
+ };
struct device_node *np = hdmi->dev->of_node;
- int ret;
+ struct clk *qp_clk;
+ int ret, i;
hdmi->regmap = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
if (IS_ERR(hdmi->regmap)) {
@@ -234,6 +258,34 @@ static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi)
return ret;
}
+ if (hdmi->is_hdmi_qp) {
+ hdmi->vo1_regmap = syscon_regmap_lookup_by_phandle(np, "rockchip,vo1_grf");
+ if (IS_ERR(hdmi->vo1_regmap)) {
+ drm_err(hdmi, "Unable to get rockchip,vo1_grf\n");
+ return PTR_ERR(hdmi->vo1_regmap);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(qp_clk_names); i++) {
+ qp_clk = devm_clk_get_optional_enabled(hdmi->dev, qp_clk_names[i]);
+
+ if (IS_ERR(qp_clk)) {
+ ret = PTR_ERR(qp_clk);
+ if (ret != -EPROBE_DEFER)
+ drm_err(hdmi, "failed to get %s clock: %d\n",
+ qp_clk_names[i], ret);
+ return ret;
+ }
+ }
+
+ hdmi->qp_enable_gpio = devm_gpiod_get_optional(hdmi->dev, "enable",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(hdmi->qp_enable_gpio)) {
+ ret = PTR_ERR(hdmi->qp_enable_gpio);
+ drm_err(hdmi, "failed to request enable GPIO: %d\n", ret);
+ return ret;
+ }
+ }
+
ret = devm_regulator_get_enable(hdmi->dev, "avdd-0v9");
if (ret)
return ret;
@@ -303,8 +355,32 @@ static void dw_hdmi_rockchip_encoder_mode_set(struct drm_encoder *encoder,
static void dw_hdmi_rockchip_encoder_enable(struct drm_encoder *encoder)
{
struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder);
+ struct drm_crtc *crtc = encoder->crtc;
u32 val;
- int ret;
+ int ret, rate;
+
+ if (hdmi->is_hdmi_qp) {
+ /* Unconditionally switch to TMDS as FRL is not yet supported */
+ gpiod_set_value(hdmi->qp_enable_gpio, 1);
+
+ if (crtc && crtc->state) {
+ clk_set_rate(hdmi->ref_clk,
+ crtc->state->adjusted_mode.crtc_clock * 1000);
+ /*
+ * 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
+ */
+ if (hdmi->phy) {
+ rate = crtc->state->mode.clock * 10;
+ phy_set_bus_width(hdmi->phy, rate);
+ drm_dbg(hdmi, "%s set bus_width=%u\n",
+ __func__, rate);
+ }
+ }
+ }
if (hdmi->chip_data->lcdsel_grf_reg < 0)
return;
@@ -356,6 +432,9 @@ static int dw_hdmi_rockchip_genphy_init(struct dw_hdmi *dw_hdmi, void *data,
{
struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
+ if (hdmi->is_hdmi_qp)
+ dw_hdmi_set_high_tmds_clock_ratio(dw_hdmi, display);
+
return phy_power_on(hdmi->phy);
}
@@ -430,6 +509,29 @@ static void dw_hdmi_rk3328_setup_hpd(struct dw_hdmi *dw_hdmi, void *data)
RK3328_HDMI_HPD_IOE));
}
+static enum drm_connector_status
+dw_hdmi_rk3588_read_hpd(struct dw_hdmi *dw_hdmi, void *data)
+{
+ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
+ u32 val;
+
+ regmap_read(hdmi->regmap, RK3588_GRF_SOC_STATUS1, &val);
+
+ return val & RK3588_HDMI0_LEVEL_INT ?
+ connector_status_connected : connector_status_disconnected;
+}
+
+static void dw_hdmi_rk3588_setup_hpd(struct dw_hdmi *dw_hdmi, void *data)
+{
+ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
+
+ regmap_write(hdmi->regmap,
+ RK3588_GRF_SOC_CON2,
+ HIWORD_UPDATE(RK3588_HDMI0_HPD_INT_CLR,
+ RK3588_HDMI0_HPD_INT_CLR |
+ RK3588_HDMI0_HPD_INT_MSK));
+}
+
static const struct dw_hdmi_phy_ops rk3228_hdmi_phy_ops = {
.init = dw_hdmi_rockchip_genphy_init,
.disable = dw_hdmi_rockchip_genphy_disable,
@@ -513,6 +615,82 @@ static const struct dw_hdmi_plat_data rk3568_hdmi_drv_data = {
.use_drm_infoframe = true,
};
+static const struct dw_hdmi_phy_ops rk3588_hdmi_phy_ops = {
+ .init = dw_hdmi_rockchip_genphy_init,
+ .disable = dw_hdmi_rockchip_genphy_disable,
+ .read_hpd = dw_hdmi_rk3588_read_hpd,
+ .setup_hpd = dw_hdmi_rk3588_setup_hpd,
+};
+
+struct rockchip_hdmi_chip_data rk3588_chip_data = {
+ .lcdsel_grf_reg = -1,
+};
+
+static const struct dw_hdmi_plat_data rk3588_hdmi_drv_data = {
+ .phy_data = &rk3588_chip_data,
+ .phy_ops = &rk3588_hdmi_phy_ops,
+ .phy_name = "samsung_hdptx_phy",
+ .phy_force_vendor = true,
+ .use_drm_infoframe = true,
+ .is_hdmi_qp = true,
+};
+
+static void dw_hdmi_rk3588_hpd_work(struct work_struct *p_work)
+{
+ struct rockchip_hdmi *hdmi = container_of(p_work, struct rockchip_hdmi,
+ qp_hpd_work.work);
+
+ struct drm_device *drm = hdmi->encoder.encoder.dev;
+ bool changed;
+
+ if (drm) {
+ changed = drm_helper_hpd_irq_event(drm);
+ if (changed)
+ drm_dbg(hdmi, "connector status changed\n");
+ }
+}
+
+static irqreturn_t dw_hdmi_rk3588_hardirq(int irq, void *dev_id)
+{
+ struct rockchip_hdmi *hdmi = dev_id;
+ u32 intr_stat, val;
+
+ regmap_read(hdmi->regmap, RK3588_GRF_SOC_STATUS1, &intr_stat);
+
+ if (intr_stat) {
+ val = HIWORD_UPDATE(RK3588_HDMI0_HPD_INT_MSK,
+ RK3588_HDMI0_HPD_INT_MSK);
+ regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val);
+ return IRQ_WAKE_THREAD;
+ }
+
+ return IRQ_NONE;
+}
+
+static irqreturn_t dw_hdmi_rk3588_irq(int irq, void *dev_id)
+{
+ struct rockchip_hdmi *hdmi = dev_id;
+ u32 intr_stat, val;
+ int debounce_ms;
+
+ regmap_read(hdmi->regmap, RK3588_GRF_SOC_STATUS1, &intr_stat);
+ if (!intr_stat)
+ return IRQ_NONE;
+
+ val = HIWORD_UPDATE(RK3588_HDMI0_HPD_INT_CLR,
+ RK3588_HDMI0_HPD_INT_CLR);
+ regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val);
+
+ debounce_ms = intr_stat & RK3588_HDMI0_LEVEL_INT ? 150 : 20;
+ mod_delayed_work(system_wq, &hdmi->qp_hpd_work,
+ msecs_to_jiffies(debounce_ms));
+
+ val |= HIWORD_UPDATE(0, RK3588_HDMI0_HPD_INT_MSK);
+ regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val);
+
+ return IRQ_HANDLED;
+}
+
static const struct of_device_id dw_hdmi_rockchip_dt_ids[] = {
{ .compatible = "rockchip,rk3228-dw-hdmi",
.data = &rk3228_hdmi_drv_data
@@ -529,6 +707,9 @@ static const struct of_device_id dw_hdmi_rockchip_dt_ids[] = {
{ .compatible = "rockchip,rk3568-dw-hdmi",
.data = &rk3568_hdmi_drv_data
},
+ { .compatible = "rockchip,rk3588-dw-hdmi",
+ .data = &rk3588_hdmi_drv_data
+ },
{},
};
MODULE_DEVICE_TABLE(of, dw_hdmi_rockchip_dt_ids);
@@ -542,7 +723,8 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
struct drm_device *drm = data;
struct drm_encoder *encoder;
struct rockchip_hdmi *hdmi;
- int ret;
+ int ret, irq;
+ u32 val;
if (!pdev->dev.of_node)
return -ENODEV;
@@ -553,13 +735,14 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
match = of_match_node(dw_hdmi_rockchip_dt_ids, pdev->dev.of_node);
plat_data = devm_kmemdup(&pdev->dev, match->data,
- sizeof(*plat_data), GFP_KERNEL);
+ sizeof(*plat_data), GFP_KERNEL);
if (!plat_data)
return -ENOMEM;
hdmi->dev = &pdev->dev;
hdmi->plat_data = plat_data;
hdmi->chip_data = plat_data->phy_data;
+ hdmi->is_hdmi_qp = plat_data->is_hdmi_qp;
plat_data->phy_data = hdmi;
plat_data->priv_data = hdmi;
encoder = &hdmi->encoder.encoder;
@@ -598,6 +781,37 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
RK3568_HDMI_SCLIN_MSK,
RK3568_HDMI_SDAIN_MSK |
RK3568_HDMI_SCLIN_MSK));
+ } else if (hdmi->is_hdmi_qp) {
+ val = HIWORD_UPDATE(RK3588_SCLIN_MASK, RK3588_SCLIN_MASK) |
+ HIWORD_UPDATE(RK3588_SDAIN_MASK, RK3588_SDAIN_MASK) |
+ HIWORD_UPDATE(RK3588_MODE_MASK, RK3588_MODE_MASK) |
+ HIWORD_UPDATE(RK3588_I2S_SEL_MASK, RK3588_I2S_SEL_MASK);
+ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON3, val);
+
+ val = HIWORD_UPDATE(RK3588_SET_HPD_PATH_MASK,
+ RK3588_SET_HPD_PATH_MASK);
+ regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON7, val);
+
+ val = HIWORD_UPDATE(RK3588_HDMI0_GRANT_SEL,
+ RK3588_HDMI0_GRANT_SEL);
+ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON9, val);
+
+ val = HIWORD_UPDATE(RK3588_HDMI0_HPD_INT_MSK, RK3588_HDMI0_HPD_INT_MSK);
+ regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val);
+
+ INIT_DELAYED_WORK(&hdmi->qp_hpd_work, dw_hdmi_rk3588_hpd_work);
+
+ irq = platform_get_irq(pdev, 4);
+ if (irq < 0)
+ return irq;
+
+ ret = devm_request_threaded_irq(hdmi->dev, irq,
+ dw_hdmi_rk3588_hardirq,
+ dw_hdmi_rk3588_irq,
+ IRQF_SHARED, "dw-hdmi-qp-hpd",
+ hdmi);
+ if (ret)
+ return ret;
}
drm_encoder_helper_add(encoder, &dw_hdmi_rockchip_encoder_helper_funcs);
@@ -605,7 +819,10 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
platform_set_drvdata(pdev, hdmi);
- hdmi->hdmi = dw_hdmi_bind(pdev, encoder, plat_data);
+ if (hdmi->is_hdmi_qp)
+ hdmi->hdmi = dw_hdmi_qp_bind(pdev, encoder, plat_data);
+ else
+ hdmi->hdmi = dw_hdmi_bind(pdev, encoder, plat_data);
/*
* If dw_hdmi_bind() fails we'll never call dw_hdmi_unbind(),
@@ -629,7 +846,13 @@ static void dw_hdmi_rockchip_unbind(struct device *dev, struct device *master,
{
struct rockchip_hdmi *hdmi = dev_get_drvdata(dev);
- dw_hdmi_unbind(hdmi->hdmi);
+ if (hdmi->is_hdmi_qp) {
+ cancel_delayed_work_sync(&hdmi->qp_hpd_work);
+ dw_hdmi_qp_unbind(hdmi->hdmi);
+ } else {
+ dw_hdmi_unbind(hdmi->hdmi);
+ }
+
drm_encoder_cleanup(&hdmi->encoder.encoder);
}
@@ -651,8 +874,30 @@ static void dw_hdmi_rockchip_remove(struct platform_device *pdev)
static int __maybe_unused dw_hdmi_rockchip_resume(struct device *dev)
{
struct rockchip_hdmi *hdmi = dev_get_drvdata(dev);
+ u32 val;
+
+ if (hdmi->is_hdmi_qp) {
+ val = HIWORD_UPDATE(RK3588_SCLIN_MASK, RK3588_SCLIN_MASK) |
+ HIWORD_UPDATE(RK3588_SDAIN_MASK, RK3588_SDAIN_MASK) |
+ HIWORD_UPDATE(RK3588_MODE_MASK, RK3588_MODE_MASK) |
+ HIWORD_UPDATE(RK3588_I2S_SEL_MASK, RK3588_I2S_SEL_MASK);
+ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON3, val);
- dw_hdmi_resume(hdmi->hdmi);
+ val = HIWORD_UPDATE(RK3588_SET_HPD_PATH_MASK,
+ RK3588_SET_HPD_PATH_MASK);
+ regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON7, val);
+
+ val = HIWORD_UPDATE(RK3588_HDMI0_GRANT_SEL,
+ RK3588_HDMI0_GRANT_SEL);
+ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON9, val);
+
+ dw_hdmi_qp_resume(dev, hdmi->hdmi);
+
+ if (hdmi->encoder.encoder.dev)
+ drm_helper_hpd_irq_event(hdmi->encoder.encoder.dev);
+ } else {
+ dw_hdmi_resume(hdmi->hdmi);
+ }
return 0;
}
--
2.45.0
Hi Cristian,
kernel test robot noticed the following build errors:
[auto build test ERROR on 1613e604df0cd359cf2a7fbd9be7a0bcfacfabd0]
url: https://github.com/intel-lab-lkp/linux/commits/Cristian-Ciocaltea/drm-bridge-dw-hdmi-Simplify-clock-handling/20240601-211531
base: 1613e604df0cd359cf2a7fbd9be7a0bcfacfabd0
patch link: https://lore.kernel.org/r/20240601-b4-rk3588-bridge-upstream-v1-14-f6203753232b%40collabora.com
patch subject: [PATCH 14/14] drm/rockchip: dw_hdmi: Add basic RK3588 support
config: arm-defconfig (https://download.01.org/0day-ci/archive/20240602/202406020122.7LHytbS3-lkp@intel.com/config)
compiler: clang version 14.0.6 (https://github.com/llvm/llvm-project f28c006a5895fc0e329fe15fead81e37457cb1d1)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20240602/202406020122.7LHytbS3-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202406020122.7LHytbS3-lkp@intel.com/
All errors (new ones prefixed by >>):
>> drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c:280:26: error: implicit declaration of function 'devm_gpiod_get_optional' is invalid in C99 [-Werror,-Wimplicit-function-declaration]
hdmi->qp_enable_gpio = devm_gpiod_get_optional(hdmi->dev, "enable",
^
drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c:280:26: note: did you mean 'devm_clk_get_optional'?
include/linux/clk.h:597:13: note: 'devm_clk_get_optional' declared here
struct clk *devm_clk_get_optional(struct device *dev, const char *id);
^
>> drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c:281:15: error: use of undeclared identifier 'GPIOD_OUT_HIGH'
GPIOD_OUT_HIGH);
^
>> drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c:364:3: error: implicit declaration of function 'gpiod_set_value' is invalid in C99 [-Werror,-Wimplicit-function-declaration]
gpiod_set_value(hdmi->qp_enable_gpio, 1);
^
3 errors generated.
vim +/devm_gpiod_get_optional +280 drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
226
227 static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi)
228 {
229 static const char * const qp_clk_names[] = {
230 "pclk", "hdp", "earc", "aud", "hclk_vo1",
231 };
232 struct device_node *np = hdmi->dev->of_node;
233 struct clk *qp_clk;
234 int ret, i;
235
236 hdmi->regmap = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
237 if (IS_ERR(hdmi->regmap)) {
238 drm_err(hdmi, "Unable to get rockchip,grf\n");
239 return PTR_ERR(hdmi->regmap);
240 }
241
242 hdmi->ref_clk = devm_clk_get_optional_enabled(hdmi->dev, "ref");
243 if (!hdmi->ref_clk)
244 hdmi->ref_clk = devm_clk_get_optional_enabled(hdmi->dev, "vpll");
245
246 if (IS_ERR(hdmi->ref_clk)) {
247 ret = PTR_ERR(hdmi->ref_clk);
248 if (ret != -EPROBE_DEFER)
249 drm_err(hdmi, "failed to get reference clock\n");
250 return ret;
251 }
252
253 hdmi->grf_clk = devm_clk_get_optional(hdmi->dev, "grf");
254 if (IS_ERR(hdmi->grf_clk)) {
255 ret = PTR_ERR(hdmi->grf_clk);
256 if (ret != -EPROBE_DEFER)
257 drm_err(hdmi, "failed to get grf clock\n");
258 return ret;
259 }
260
261 if (hdmi->is_hdmi_qp) {
262 hdmi->vo1_regmap = syscon_regmap_lookup_by_phandle(np, "rockchip,vo1_grf");
263 if (IS_ERR(hdmi->vo1_regmap)) {
264 drm_err(hdmi, "Unable to get rockchip,vo1_grf\n");
265 return PTR_ERR(hdmi->vo1_regmap);
266 }
267
268 for (i = 0; i < ARRAY_SIZE(qp_clk_names); i++) {
269 qp_clk = devm_clk_get_optional_enabled(hdmi->dev, qp_clk_names[i]);
270
271 if (IS_ERR(qp_clk)) {
272 ret = PTR_ERR(qp_clk);
273 if (ret != -EPROBE_DEFER)
274 drm_err(hdmi, "failed to get %s clock: %d\n",
275 qp_clk_names[i], ret);
276 return ret;
277 }
278 }
279
> 280 hdmi->qp_enable_gpio = devm_gpiod_get_optional(hdmi->dev, "enable",
> 281 GPIOD_OUT_HIGH);
282 if (IS_ERR(hdmi->qp_enable_gpio)) {
283 ret = PTR_ERR(hdmi->qp_enable_gpio);
284 drm_err(hdmi, "failed to request enable GPIO: %d\n", ret);
285 return ret;
286 }
287 }
288
289 ret = devm_regulator_get_enable(hdmi->dev, "avdd-0v9");
290 if (ret)
291 return ret;
292
293 ret = devm_regulator_get_enable(hdmi->dev, "avdd-1v8");
294
295 return ret;
296 }
297
298 static enum drm_mode_status
299 dw_hdmi_rockchip_mode_valid(struct dw_hdmi *dw_hdmi, void *data,
300 const struct drm_display_info *info,
301 const struct drm_display_mode *mode)
302 {
303 struct rockchip_hdmi *hdmi = data;
304 const struct dw_hdmi_mpll_config *mpll_cfg = rockchip_mpll_cfg;
305 int pclk = mode->clock * 1000;
306 bool exact_match = hdmi->plat_data->phy_force_vendor;
307 int i;
308
309 if (hdmi->ref_clk) {
310 int rpclk = clk_round_rate(hdmi->ref_clk, pclk);
311
312 if (abs(rpclk - pclk) > pclk / 1000)
313 return MODE_NOCLOCK;
314 }
315
316 for (i = 0; mpll_cfg[i].mpixelclock != (~0UL); i++) {
317 /*
318 * For vendor specific phys force an exact match of the pixelclock
319 * to preserve the original behaviour of the driver.
320 */
321 if (exact_match && pclk == mpll_cfg[i].mpixelclock)
322 return MODE_OK;
323 /*
324 * The Synopsys phy can work with pixelclocks up to the value given
325 * in the corresponding mpll_cfg entry.
326 */
327 if (!exact_match && pclk <= mpll_cfg[i].mpixelclock)
328 return MODE_OK;
329 }
330
331 return MODE_BAD;
332 }
333
334 static void dw_hdmi_rockchip_encoder_disable(struct drm_encoder *encoder)
335 {
336 }
337
338 static bool
339 dw_hdmi_rockchip_encoder_mode_fixup(struct drm_encoder *encoder,
340 const struct drm_display_mode *mode,
341 struct drm_display_mode *adj_mode)
342 {
343 return true;
344 }
345
346 static void dw_hdmi_rockchip_encoder_mode_set(struct drm_encoder *encoder,
347 struct drm_display_mode *mode,
348 struct drm_display_mode *adj_mode)
349 {
350 struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder);
351
352 clk_set_rate(hdmi->ref_clk, adj_mode->clock * 1000);
353 }
354
355 static void dw_hdmi_rockchip_encoder_enable(struct drm_encoder *encoder)
356 {
357 struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder);
358 struct drm_crtc *crtc = encoder->crtc;
359 u32 val;
360 int ret, rate;
361
362 if (hdmi->is_hdmi_qp) {
363 /* Unconditionally switch to TMDS as FRL is not yet supported */
> 364 gpiod_set_value(hdmi->qp_enable_gpio, 1);
365
366 if (crtc && crtc->state) {
367 clk_set_rate(hdmi->ref_clk,
368 crtc->state->adjusted_mode.crtc_clock * 1000);
369 /*
370 * FIXME: Temporary workaround to pass pixel clock rate
371 * to the PHY driver until phy_configure_opts_hdmi
372 * becomes available in the PHY API. See also the related
373 * comment in rk_hdptx_phy_power_on() from
374 * drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
375 */
376 if (hdmi->phy) {
377 rate = crtc->state->mode.clock * 10;
378 phy_set_bus_width(hdmi->phy, rate);
379 drm_dbg(hdmi, "%s set bus_width=%u\n",
380 __func__, rate);
381 }
382 }
383 }
384
385 if (hdmi->chip_data->lcdsel_grf_reg < 0)
386 return;
387
388 ret = drm_of_encoder_active_endpoint_id(hdmi->dev->of_node, encoder);
389 if (ret)
390 val = hdmi->chip_data->lcdsel_lit;
391 else
392 val = hdmi->chip_data->lcdsel_big;
393
394 ret = clk_prepare_enable(hdmi->grf_clk);
395 if (ret < 0) {
396 drm_err(hdmi, "failed to enable grfclk %d\n", ret);
397 return;
398 }
399
400 ret = regmap_write(hdmi->regmap, hdmi->chip_data->lcdsel_grf_reg, val);
401 if (ret != 0)
402 drm_err(hdmi, "Could not write to GRF: %d\n", ret);
403
404 clk_disable_unprepare(hdmi->grf_clk);
405 drm_dbg(hdmi, "vop %s output to hdmi\n", ret ? "LIT" : "BIG");
406 }
407
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
Hi Cristian,
kernel test robot noticed the following build errors:
[auto build test ERROR on 1613e604df0cd359cf2a7fbd9be7a0bcfacfabd0]
url: https://github.com/intel-lab-lkp/linux/commits/Cristian-Ciocaltea/drm-bridge-dw-hdmi-Simplify-clock-handling/20240601-211531
base: 1613e604df0cd359cf2a7fbd9be7a0bcfacfabd0
patch link: https://lore.kernel.org/r/20240601-b4-rk3588-bridge-upstream-v1-14-f6203753232b%40collabora.com
patch subject: [PATCH 14/14] drm/rockchip: dw_hdmi: Add basic RK3588 support
config: i386-buildonly-randconfig-002-20240601 (https://download.01.org/0day-ci/archive/20240601/202406012207.Yjbj56eg-lkp@intel.com/config)
compiler: gcc-13 (Ubuntu 13.2.0-4ubuntu3) 13.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20240601/202406012207.Yjbj56eg-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202406012207.Yjbj56eg-lkp@intel.com/
All errors (new ones prefixed by >>):
drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c: In function 'rockchip_hdmi_parse_dt':
>> drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c:280:40: error: implicit declaration of function 'devm_gpiod_get_optional'; did you mean 'devm_clk_get_optional'? [-Werror=implicit-function-declaration]
280 | hdmi->qp_enable_gpio = devm_gpiod_get_optional(hdmi->dev, "enable",
| ^~~~~~~~~~~~~~~~~~~~~~~
| devm_clk_get_optional
>> drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c:281:64: error: 'GPIOD_OUT_HIGH' undeclared (first use in this function)
281 | GPIOD_OUT_HIGH);
| ^~~~~~~~~~~~~~
drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c:281:64: note: each undeclared identifier is reported only once for each function it appears in
drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c: In function 'dw_hdmi_rockchip_encoder_enable':
>> drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c:364:17: error: implicit declaration of function 'gpiod_set_value' [-Werror=implicit-function-declaration]
364 | gpiod_set_value(hdmi->qp_enable_gpio, 1);
| ^~~~~~~~~~~~~~~
cc1: some warnings being treated as errors
vim +280 drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
226
227 static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi)
228 {
229 static const char * const qp_clk_names[] = {
230 "pclk", "hdp", "earc", "aud", "hclk_vo1",
231 };
232 struct device_node *np = hdmi->dev->of_node;
233 struct clk *qp_clk;
234 int ret, i;
235
236 hdmi->regmap = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
237 if (IS_ERR(hdmi->regmap)) {
238 drm_err(hdmi, "Unable to get rockchip,grf\n");
239 return PTR_ERR(hdmi->regmap);
240 }
241
242 hdmi->ref_clk = devm_clk_get_optional_enabled(hdmi->dev, "ref");
243 if (!hdmi->ref_clk)
244 hdmi->ref_clk = devm_clk_get_optional_enabled(hdmi->dev, "vpll");
245
246 if (IS_ERR(hdmi->ref_clk)) {
247 ret = PTR_ERR(hdmi->ref_clk);
248 if (ret != -EPROBE_DEFER)
249 drm_err(hdmi, "failed to get reference clock\n");
250 return ret;
251 }
252
253 hdmi->grf_clk = devm_clk_get_optional(hdmi->dev, "grf");
254 if (IS_ERR(hdmi->grf_clk)) {
255 ret = PTR_ERR(hdmi->grf_clk);
256 if (ret != -EPROBE_DEFER)
257 drm_err(hdmi, "failed to get grf clock\n");
258 return ret;
259 }
260
261 if (hdmi->is_hdmi_qp) {
262 hdmi->vo1_regmap = syscon_regmap_lookup_by_phandle(np, "rockchip,vo1_grf");
263 if (IS_ERR(hdmi->vo1_regmap)) {
264 drm_err(hdmi, "Unable to get rockchip,vo1_grf\n");
265 return PTR_ERR(hdmi->vo1_regmap);
266 }
267
268 for (i = 0; i < ARRAY_SIZE(qp_clk_names); i++) {
269 qp_clk = devm_clk_get_optional_enabled(hdmi->dev, qp_clk_names[i]);
270
271 if (IS_ERR(qp_clk)) {
272 ret = PTR_ERR(qp_clk);
273 if (ret != -EPROBE_DEFER)
274 drm_err(hdmi, "failed to get %s clock: %d\n",
275 qp_clk_names[i], ret);
276 return ret;
277 }
278 }
279
> 280 hdmi->qp_enable_gpio = devm_gpiod_get_optional(hdmi->dev, "enable",
> 281 GPIOD_OUT_HIGH);
282 if (IS_ERR(hdmi->qp_enable_gpio)) {
283 ret = PTR_ERR(hdmi->qp_enable_gpio);
284 drm_err(hdmi, "failed to request enable GPIO: %d\n", ret);
285 return ret;
286 }
287 }
288
289 ret = devm_regulator_get_enable(hdmi->dev, "avdd-0v9");
290 if (ret)
291 return ret;
292
293 ret = devm_regulator_get_enable(hdmi->dev, "avdd-1v8");
294
295 return ret;
296 }
297
298 static enum drm_mode_status
299 dw_hdmi_rockchip_mode_valid(struct dw_hdmi *dw_hdmi, void *data,
300 const struct drm_display_info *info,
301 const struct drm_display_mode *mode)
302 {
303 struct rockchip_hdmi *hdmi = data;
304 const struct dw_hdmi_mpll_config *mpll_cfg = rockchip_mpll_cfg;
305 int pclk = mode->clock * 1000;
306 bool exact_match = hdmi->plat_data->phy_force_vendor;
307 int i;
308
309 if (hdmi->ref_clk) {
310 int rpclk = clk_round_rate(hdmi->ref_clk, pclk);
311
312 if (abs(rpclk - pclk) > pclk / 1000)
313 return MODE_NOCLOCK;
314 }
315
316 for (i = 0; mpll_cfg[i].mpixelclock != (~0UL); i++) {
317 /*
318 * For vendor specific phys force an exact match of the pixelclock
319 * to preserve the original behaviour of the driver.
320 */
321 if (exact_match && pclk == mpll_cfg[i].mpixelclock)
322 return MODE_OK;
323 /*
324 * The Synopsys phy can work with pixelclocks up to the value given
325 * in the corresponding mpll_cfg entry.
326 */
327 if (!exact_match && pclk <= mpll_cfg[i].mpixelclock)
328 return MODE_OK;
329 }
330
331 return MODE_BAD;
332 }
333
334 static void dw_hdmi_rockchip_encoder_disable(struct drm_encoder *encoder)
335 {
336 }
337
338 static bool
339 dw_hdmi_rockchip_encoder_mode_fixup(struct drm_encoder *encoder,
340 const struct drm_display_mode *mode,
341 struct drm_display_mode *adj_mode)
342 {
343 return true;
344 }
345
346 static void dw_hdmi_rockchip_encoder_mode_set(struct drm_encoder *encoder,
347 struct drm_display_mode *mode,
348 struct drm_display_mode *adj_mode)
349 {
350 struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder);
351
352 clk_set_rate(hdmi->ref_clk, adj_mode->clock * 1000);
353 }
354
355 static void dw_hdmi_rockchip_encoder_enable(struct drm_encoder *encoder)
356 {
357 struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder);
358 struct drm_crtc *crtc = encoder->crtc;
359 u32 val;
360 int ret, rate;
361
362 if (hdmi->is_hdmi_qp) {
363 /* Unconditionally switch to TMDS as FRL is not yet supported */
> 364 gpiod_set_value(hdmi->qp_enable_gpio, 1);
365
366 if (crtc && crtc->state) {
367 clk_set_rate(hdmi->ref_clk,
368 crtc->state->adjusted_mode.crtc_clock * 1000);
369 /*
370 * FIXME: Temporary workaround to pass pixel clock rate
371 * to the PHY driver until phy_configure_opts_hdmi
372 * becomes available in the PHY API. See also the related
373 * comment in rk_hdptx_phy_power_on() from
374 * drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
375 */
376 if (hdmi->phy) {
377 rate = crtc->state->mode.clock * 10;
378 phy_set_bus_width(hdmi->phy, rate);
379 drm_dbg(hdmi, "%s set bus_width=%u\n",
380 __func__, rate);
381 }
382 }
383 }
384
385 if (hdmi->chip_data->lcdsel_grf_reg < 0)
386 return;
387
388 ret = drm_of_encoder_active_endpoint_id(hdmi->dev->of_node, encoder);
389 if (ret)
390 val = hdmi->chip_data->lcdsel_lit;
391 else
392 val = hdmi->chip_data->lcdsel_big;
393
394 ret = clk_prepare_enable(hdmi->grf_clk);
395 if (ret < 0) {
396 drm_err(hdmi, "failed to enable grfclk %d\n", ret);
397 return;
398 }
399
400 ret = regmap_write(hdmi->regmap, hdmi->chip_data->lcdsel_grf_reg, val);
401 if (ret != 0)
402 drm_err(hdmi, "Could not write to GRF: %d\n", ret);
403
404 clk_disable_unprepare(hdmi->grf_clk);
405 drm_dbg(hdmi, "vop %s output to hdmi\n", ret ? "LIT" : "BIG");
406 }
407
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
© 2016 - 2026 Red Hat, Inc.