mirror of
https://github.com/LibreELEC/LibreELEC.tv
synced 2025-09-24 19:46:01 +07:00
233 lines
7.4 KiB
Diff
233 lines
7.4 KiB
Diff
From 2dae3de105101f59c449419add1b1b1f04beb69b Mon Sep 17 00:00:00 2001
|
|
From: Jonas Karlman <jonas@kwiboo.se>
|
|
Date: Fri, 20 Dec 2019 08:12:42 +0000
|
|
Subject: [PATCH 12/59] WIP/1000: drm/rockchip: dw-hdmi: add bridge and switch
|
|
to drm_bridge_funcs
|
|
|
|
Switch the dw-hdmi driver to drm_bridge_funcs by implementing
|
|
a new local bridge, connecting it to the dw-hdmi bridge.
|
|
|
|
Also enable bridge format negotiation by implementing
|
|
atomic_get_input_bus_fmts and support for 8-bit RGB 4:4:4.
|
|
|
|
Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
|
|
---
|
|
drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 118 ++++++++++++++------
|
|
1 file changed, 81 insertions(+), 37 deletions(-)
|
|
|
|
diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
|
|
index c7841c7d5daf..a50218412fb0 100644
|
|
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
|
|
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
|
|
@@ -5,6 +5,7 @@
|
|
|
|
#include <linux/clk.h>
|
|
#include <linux/mfd/syscon.h>
|
|
+#include <linux/media-bus-format.h>
|
|
#include <linux/module.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/phy/phy.h>
|
|
@@ -73,6 +74,7 @@ struct rockchip_hdmi_chip_data {
|
|
struct rockchip_hdmi {
|
|
struct device *dev;
|
|
struct regmap *regmap;
|
|
+ struct drm_bridge bridge;
|
|
struct rockchip_encoder encoder;
|
|
const struct rockchip_hdmi_chip_data *chip_data;
|
|
const struct dw_hdmi_plat_data *plat_data;
|
|
@@ -83,11 +85,9 @@ struct rockchip_hdmi {
|
|
struct phy *phy;
|
|
};
|
|
|
|
-static struct rockchip_hdmi *to_rockchip_hdmi(struct drm_encoder *encoder)
|
|
+static struct rockchip_hdmi *to_rockchip_hdmi(struct drm_bridge *bridge)
|
|
{
|
|
- struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
|
|
-
|
|
- return container_of(rkencoder, struct rockchip_hdmi, encoder);
|
|
+ return container_of(bridge, struct rockchip_hdmi, bridge);
|
|
}
|
|
|
|
static const struct dw_hdmi_mpll_config rockchip_mpll_cfg[] = {
|
|
@@ -343,31 +343,21 @@ dw_hdmi_rockchip_mode_valid(struct dw_hdmi *dw_hdmi, void *data,
|
|
|
|
return MODE_OK;
|
|
}
|
|
-
|
|
-static void dw_hdmi_rockchip_encoder_disable(struct drm_encoder *encoder)
|
|
+static void
|
|
+dw_hdmi_rockchip_bridge_mode_set(struct drm_bridge *bridge,
|
|
+ const struct drm_display_mode *mode,
|
|
+ const struct drm_display_mode *adjusted_mode)
|
|
{
|
|
-}
|
|
+ struct rockchip_hdmi *hdmi = to_rockchip_hdmi(bridge);
|
|
|
|
-static bool
|
|
-dw_hdmi_rockchip_encoder_mode_fixup(struct drm_encoder *encoder,
|
|
- const struct drm_display_mode *mode,
|
|
- struct drm_display_mode *adj_mode)
|
|
-{
|
|
- return true;
|
|
+ clk_set_rate(hdmi->ref_clk, adjusted_mode->clock * 1000);
|
|
}
|
|
|
|
-static void dw_hdmi_rockchip_encoder_mode_set(struct drm_encoder *encoder,
|
|
- struct drm_display_mode *mode,
|
|
- struct drm_display_mode *adj_mode)
|
|
+static void dw_hdmi_rockchip_bridge_enable(struct drm_bridge *bridge)
|
|
{
|
|
- struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder);
|
|
+ struct rockchip_hdmi *hdmi = to_rockchip_hdmi(bridge);
|
|
+ struct drm_encoder *encoder = bridge->encoder;
|
|
|
|
- clk_set_rate(hdmi->ref_clk, adj_mode->clock * 1000);
|
|
-}
|
|
-
|
|
-static void dw_hdmi_rockchip_encoder_enable(struct drm_encoder *encoder)
|
|
-{
|
|
- struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder);
|
|
u32 val;
|
|
int ret;
|
|
|
|
@@ -394,10 +384,21 @@ static void dw_hdmi_rockchip_encoder_enable(struct drm_encoder *encoder)
|
|
dev_dbg(hdmi->dev, "vop %s output to hdmi\n", ret ? "LIT" : "BIG");
|
|
}
|
|
|
|
+static bool is_rgb(u32 format)
|
|
+{
|
|
+ switch (format) {
|
|
+ case MEDIA_BUS_FMT_RGB888_1X24:
|
|
+ return true;
|
|
+ default:
|
|
+ return false;
|
|
+ }
|
|
+}
|
|
+
|
|
static int
|
|
-dw_hdmi_rockchip_encoder_atomic_check(struct drm_encoder *encoder,
|
|
- struct drm_crtc_state *crtc_state,
|
|
- struct drm_connector_state *conn_state)
|
|
+dw_hdmi_rockchip_bridge_atomic_check(struct drm_bridge *bridge,
|
|
+ struct drm_bridge_state *bridge_state,
|
|
+ struct drm_crtc_state *crtc_state,
|
|
+ struct drm_connector_state *conn_state)
|
|
{
|
|
struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state);
|
|
|
|
@@ -407,12 +408,38 @@ dw_hdmi_rockchip_encoder_atomic_check(struct drm_encoder *encoder,
|
|
return 0;
|
|
}
|
|
|
|
-static const struct drm_encoder_helper_funcs dw_hdmi_rockchip_encoder_helper_funcs = {
|
|
- .mode_fixup = dw_hdmi_rockchip_encoder_mode_fixup,
|
|
- .mode_set = dw_hdmi_rockchip_encoder_mode_set,
|
|
- .enable = dw_hdmi_rockchip_encoder_enable,
|
|
- .disable = dw_hdmi_rockchip_encoder_disable,
|
|
- .atomic_check = dw_hdmi_rockchip_encoder_atomic_check,
|
|
+static u32 *dw_hdmi_rockchip_get_input_bus_fmts(struct drm_bridge *bridge,
|
|
+ struct drm_bridge_state *bridge_state,
|
|
+ struct drm_crtc_state *crtc_state,
|
|
+ struct drm_connector_state *conn_state,
|
|
+ u32 output_fmt,
|
|
+ unsigned int *num_input_fmts)
|
|
+{
|
|
+ u32 *input_fmt;
|
|
+
|
|
+ *num_input_fmts = 0;
|
|
+
|
|
+ if (!is_rgb(output_fmt))
|
|
+ return NULL;
|
|
+
|
|
+ input_fmt = kzalloc(sizeof(*input_fmt), GFP_KERNEL);
|
|
+ if (!input_fmt)
|
|
+ return NULL;
|
|
+
|
|
+ *num_input_fmts = 1;
|
|
+ *input_fmt = output_fmt;
|
|
+
|
|
+ return input_fmt;
|
|
+}
|
|
+
|
|
+static const struct drm_bridge_funcs dw_hdmi_rockchip_bridge_funcs = {
|
|
+ .mode_set = dw_hdmi_rockchip_bridge_mode_set,
|
|
+ .enable = dw_hdmi_rockchip_bridge_enable,
|
|
+ .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
|
|
+ .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
|
|
+ .atomic_get_input_bus_fmts = dw_hdmi_rockchip_get_input_bus_fmts,
|
|
+ .atomic_check = dw_hdmi_rockchip_bridge_atomic_check,
|
|
+ .atomic_reset = drm_atomic_helper_bridge_reset,
|
|
};
|
|
|
|
static int dw_hdmi_rockchip_genphy_init(struct dw_hdmi *dw_hdmi, void *data,
|
|
@@ -616,6 +643,7 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
|
|
struct dw_hdmi_plat_data *plat_data;
|
|
const struct of_device_id *match;
|
|
struct drm_device *drm = data;
|
|
+ struct drm_bridge *next_bridge;
|
|
struct drm_encoder *encoder;
|
|
struct rockchip_hdmi *hdmi;
|
|
int ret;
|
|
@@ -685,20 +713,21 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
|
|
RK3568_HDMI_SCLIN_MSK));
|
|
}
|
|
|
|
- drm_encoder_helper_add(encoder, &dw_hdmi_rockchip_encoder_helper_funcs);
|
|
-
|
|
ret = drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_TMDS);
|
|
if (ret) {
|
|
DRM_DEV_ERROR(hdmi->dev, "Failed to init encoder: %d\n", ret);
|
|
goto err_disable_clk;
|
|
}
|
|
|
|
+ hdmi->bridge.funcs = &dw_hdmi_rockchip_bridge_funcs;
|
|
+ drm_bridge_attach(encoder, &hdmi->bridge, NULL, 0);
|
|
+
|
|
platform_set_drvdata(pdev, hdmi);
|
|
|
|
- hdmi->hdmi = dw_hdmi_bind(pdev, encoder, plat_data);
|
|
+ hdmi->hdmi = dw_hdmi_probe(pdev, plat_data);
|
|
|
|
/*
|
|
- * If dw_hdmi_bind() fails we'll never call dw_hdmi_unbind(),
|
|
+ * If dw_hdmi_probe() fails we'll never call dw_hdmi_unbind(),
|
|
* which would have called the encoder cleanup. Do it manually.
|
|
*/
|
|
if (IS_ERR(hdmi->hdmi)) {
|
|
@@ -706,8 +735,23 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
|
|
goto err_bind;
|
|
}
|
|
|
|
+ next_bridge = of_drm_find_bridge(pdev->dev.of_node);
|
|
+ if (!next_bridge) {
|
|
+ ret = -EPROBE_DEFER;
|
|
+ goto err_dw_hdmi_remove;
|
|
+ }
|
|
+
|
|
+ ret = drm_bridge_attach(encoder, next_bridge, &hdmi->bridge, 0);
|
|
+ if (ret) {
|
|
+ if (ret != -EPROBE_DEFER)
|
|
+ DRM_DEV_ERROR(hdmi->dev, "Failed to attach dw-hdmi bridge: %d\n", ret);
|
|
+ goto err_dw_hdmi_remove;
|
|
+ }
|
|
+
|
|
return 0;
|
|
|
|
+err_dw_hdmi_remove:
|
|
+ dw_hdmi_remove(hdmi->hdmi);
|
|
err_bind:
|
|
drm_encoder_cleanup(encoder);
|
|
err_disable_clk:
|
|
@@ -719,7 +763,7 @@ 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);
|
|
+ dw_hdmi_remove(hdmi->hdmi);
|
|
drm_encoder_cleanup(&hdmi->encoder.encoder);
|
|
}
|
|
|
|
--
|
|
2.34.1
|
|
|