Somehow Laurent tricked me into updating a bridge driver. In reality, having updated a driver yourself makes for much better understanding of what is going on. So in order to provide proper review feedback I deciced to give it a spin.
Second iteration of this patchset covers several drivers, and a few panel adjustments.
The general approach for the bridge drivers: - Introduce drm_panel_brigde - Introduce bridge operations - Make connector creation optional
v2: - Updated bus_flags for boe,hv070wsa-100 - Collected r-b, but did not apply patches yet - On the panel side the panel-simple driver gained a default connector type for all the dumb panels that do not include so in their description. With this change panels always provide a connector type, and we have the potential to drop most uses of devm_drm_panel_bridge_add_typed(). - Added conversion of a few more bridge drivers
When bridge drivers no longer create their own connector, the update of the connector state using drm_helper_hpd_irq_event() is also left out. I did not manage to convince myself if this is correct or not. We somehow needs to trigger that the connector state is updated but that should not be the repsonsibility of a bridge driver.
Patches can build but no run-time testing. So both test and review feedback appreciated!
My notes on the status so far - for the bridge drives:
bridge/analogix/analogix-anx6345 todo bridge/analogix/analogix-anx78xx todo bridge/analogix/analogix_dp_core todo
bridge/sii902x todo ? how to deal with outputmode? i2c/tda998x_drv.c todo Needs drm_connector in helper fuctions. In several places. mediatek/mtk_hdmi.c todo Ask: Enric Balletbo i Serra enric.balletbo@collabora.com to do it? omapdrm/dss/dpi.c todo, Laurent? omapdrm/dss/hdmi4.c todo, Laurent? omapdrm/dss/hdmi5.c todo, Laurent? omapdrm/dss/sdi.c todo, Laurent? omapdrm/dss/venc.c todo, Laurent? bridge/nwl-dsi todo, Guido
bridge/adv7511 ongoing, Laurent bridge/simple-bridge ongoing, Laurent bridge/synopsys/dw-hdmi ongoing, Laurent rcar-du/rcar_lvds.c ongoing, Laurent bridge/megachips-stdpxxxx-ge-b850v3-fw ongoing, Sam bridge/nxp-ptn3460 ongoing, Sam bridge/parade-ps8622 ongoing, Sam bridge/tc358764 ongoing, Sam bridge/tc358767 ongoing, Sam bridge/ti-sn65dsi86 ongoing, Sam. How to get bpc without connector? bridge/ti-tpd12s015 ongoing, Sam
bridge/display-connector.c done bridge/lvds-codec done bridge/cdns-dsi done bridge/panel.c done bridge/parade-ps8640 done bridge/sil-sii8620 done bridge/synopsys/dw-mipi-dsi done bridge/tc358768 done bridge/thc63lvd1024 done bridge/ti-tfp410 done
Sam
Sam Ravnborg (21): drm/panel: add connector type to boe,hv070wsa-100 panel drm/panel: panel-simple: add default connector_type drm/bridge: tc358764: drop drm_connector_(un)register drm/bridge: tc358764: add drm_panel_bridge support drm/bridge: tc358764: make connector creation optional drm/bridge: tc358767: add drm_panel_bridge support drm/bridge: tc358767: add detect bridge operation drm/bridge: tc358767: add get_edid bride operation drm/bridge: tc358767: make connector creation optional drm/bridge: ti-tpd12s015: make connector creation optional drm/bridge: parade-ps8622: add drm_panel_bridge support drm/bridge: parade-ps8622: make connector creation optional drm/bridge: megachips: add helper to create connector drm/bridge: megachips: get drm_device from bridge drm/bridge: megachips: enable detect bridge operation drm/bridge: megachips: add get_edid bridge operation drm/bridge: megachips: make connector creation optional drm/bridge: nxp-ptn3460: add drm_panel_bridge support drm/bridge: nxp-ptn3460: add get_modes bridge operation drm/bridge: nxp-ptn3460: make connector creation optional drm/bridge: ti-sn65dsi86: add drm_panel_bridge support
.../drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c | 92 +++++++++++------- drivers/gpu/drm/bridge/nxp-ptn3460.c | 107 +++++++++------------ drivers/gpu/drm/bridge/parade-ps8622.c | 55 ++++------- drivers/gpu/drm/bridge/tc358764.c | 68 ++++--------- drivers/gpu/drm/bridge/tc358767.c | 98 +++++++++++-------- drivers/gpu/drm/bridge/ti-sn65dsi86.c | 27 +++--- drivers/gpu/drm/bridge/ti-tpd12s015.c | 3 - drivers/gpu/drm/panel/panel-simple.c | 13 ++- 8 files changed, 219 insertions(+), 244 deletions(-)
The boe,hv070wsa-100 panel is a LVDS panel. Fix connector type to reflect this.
With this change users of this panel do not have to specify the connector type.
v2: - Add .bus_format (Laurent) - Add .bus_flags
Signed-off-by: Sam Ravnborg sam@ravnborg.org Cc: Laurent Pinchart laurent.pinchart@ideasonboard.com Cc: Thierry Reding thierry.reding@gmail.com Cc: Sam Ravnborg sam@ravnborg.org --- drivers/gpu/drm/panel/panel-simple.c | 3 +++ 1 file changed, 3 insertions(+)
diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index 5e56c68fbd3e..80388706057e 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -1128,6 +1128,9 @@ static const struct panel_desc boe_hv070wsa = { .width = 154, .height = 90, }, + .bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG, + .bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_NEGEDGE, + .connector_type = DRM_MODE_CONNECTOR_LVDS, };
static const struct drm_display_mode boe_nv101wxmn51_modes[] = {
All panels shall report a connector type. panel-simple has a lot of panels with no connector_type, and for these fall back to DPI as the default.
Signed-off-by: Sam Ravnborg sam@ravnborg.org Cc: Thierry Reding thierry.reding@gmail.com Cc: Sam Ravnborg sam@ravnborg.org --- drivers/gpu/drm/panel/panel-simple.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index 80388706057e..13c25ee38dbf 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -451,6 +451,7 @@ static int panel_simple_probe(struct device *dev, const struct panel_desc *desc) struct panel_simple *panel; struct display_timing dt; struct device_node *ddc; + int connector_type; int err;
panel = devm_kzalloc(dev, sizeof(*panel), GFP_KERNEL); @@ -495,8 +496,13 @@ static int panel_simple_probe(struct device *dev, const struct panel_desc *desc) panel_simple_parse_panel_timing_node(dev, panel, &dt); }
- drm_panel_init(&panel->base, dev, &panel_simple_funcs, - desc->connector_type); + /* Default DRM_MODE_CONNECTOR_DPI if no connector_type is set */ + if (desc->connector_type != 0) + connector_type = desc->connector_type; + else + connector_type = DRM_MODE_CONNECTOR_DPI; + + drm_panel_init(&panel->base, dev, &panel_simple_funcs, connector_type);
err = drm_panel_of_backlight(&panel->base); if (err)
Drop drm_connector handling that is not needed:
- drm_dev_register() in the display controller driver takes care of registering connectors. So the call to drm_connector_register() call is not needed in the bridge driver.
- Use of drm_connector_unregister() is only required for drivers that explicit have called drm_dev_register.
- The reference counting using drm_connector_put() is likewise not needed.
Signed-off-by: Sam Ravnborg sam@ravnborg.org Reviewed-by: Laurent Pinchart laurent.pinchart@ideasonboard.com Cc: Andrzej Hajda a.hajda@samsung.com Cc: Neil Armstrong narmstrong@baylibre.com Cc: Laurent Pinchart Laurent.pinchart@ideasonboard.com Cc: Jonas Karlman jonas@kwiboo.se Cc: Jernej Skrabec jernej.skrabec@siol.net --- drivers/gpu/drm/bridge/tc358764.c | 3 --- 1 file changed, 3 deletions(-)
diff --git a/drivers/gpu/drm/bridge/tc358764.c b/drivers/gpu/drm/bridge/tc358764.c index 5ac1430fab04..a277739fab58 100644 --- a/drivers/gpu/drm/bridge/tc358764.c +++ b/drivers/gpu/drm/bridge/tc358764.c @@ -375,7 +375,6 @@ static int tc358764_attach(struct drm_bridge *bridge, drm_connector_attach_encoder(&ctx->connector, bridge->encoder); drm_panel_attach(ctx->panel, &ctx->connector); ctx->connector.funcs->reset(&ctx->connector); - drm_connector_register(&ctx->connector);
return 0; } @@ -384,10 +383,8 @@ static void tc358764_detach(struct drm_bridge *bridge) { struct tc358764 *ctx = bridge_to_tc358764(bridge);
- drm_connector_unregister(&ctx->connector); drm_panel_detach(ctx->panel); ctx->panel = NULL; - drm_connector_put(&ctx->connector); }
static const struct drm_bridge_funcs tc358764_bridge_funcs = {
Prepare the bridge driver for use in a chained setup by replacing direct use of drm_panel with drm_panel_bridge support.
The bridge panel will use the connector type reported by the panel, where the connector for this driver hardcode DRM_MODE_CONNECTOR_LVDS.
Signed-off-by: Sam Ravnborg sam@ravnborg.org Reviewed-by: Laurent Pinchart laurent.pinchart@ideasonboard.com Cc: Andrzej Hajda a.hajda@samsung.com Cc: Neil Armstrong narmstrong@baylibre.com Cc: Laurent Pinchart Laurent.pinchart@ideasonboard.com Cc: Jonas Karlman jonas@kwiboo.se Cc: Jernej Skrabec jernej.skrabec@siol.net --- drivers/gpu/drm/bridge/tc358764.c | 59 +++++++++---------------------- 1 file changed, 16 insertions(+), 43 deletions(-)
diff --git a/drivers/gpu/drm/bridge/tc358764.c b/drivers/gpu/drm/bridge/tc358764.c index a277739fab58..3adb2c191499 100644 --- a/drivers/gpu/drm/bridge/tc358764.c +++ b/drivers/gpu/drm/bridge/tc358764.c @@ -156,7 +156,7 @@ struct tc358764 { struct drm_connector connector; struct regulator_bulk_data supplies[ARRAY_SIZE(tc358764_supplies)]; struct gpio_desc *gpio_reset; - struct drm_panel *panel; + struct drm_bridge *panel_bridge; int error; };
@@ -282,7 +282,7 @@ static int tc358764_get_modes(struct drm_connector *connector) { struct tc358764 *ctx = connector_to_tc358764(connector);
- return drm_panel_get_modes(ctx->panel, connector); + return drm_bridge_get_modes(ctx->panel_bridge, connector); }
static const @@ -298,23 +298,11 @@ static const struct drm_connector_funcs tc358764_connector_funcs = { .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, };
-static void tc358764_disable(struct drm_bridge *bridge) -{ - struct tc358764 *ctx = bridge_to_tc358764(bridge); - int ret = drm_panel_disable(bridge_to_tc358764(bridge)->panel); - - if (ret < 0) - dev_err(ctx->dev, "error disabling panel (%d)\n", ret); -} - static void tc358764_post_disable(struct drm_bridge *bridge) { struct tc358764 *ctx = bridge_to_tc358764(bridge); int ret;
- ret = drm_panel_unprepare(ctx->panel); - if (ret < 0) - dev_err(ctx->dev, "error unpreparing panel (%d)\n", ret); tc358764_reset(ctx); usleep_range(10000, 15000); ret = regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies); @@ -335,18 +323,6 @@ static void tc358764_pre_enable(struct drm_bridge *bridge) ret = tc358764_init(ctx); if (ret < 0) dev_err(ctx->dev, "error initializing bridge (%d)\n", ret); - ret = drm_panel_prepare(ctx->panel); - if (ret < 0) - dev_err(ctx->dev, "error preparing panel (%d)\n", ret); -} - -static void tc358764_enable(struct drm_bridge *bridge) -{ - struct tc358764 *ctx = bridge_to_tc358764(bridge); - int ret = drm_panel_enable(ctx->panel); - - if (ret < 0) - dev_err(ctx->dev, "error enabling panel (%d)\n", ret); }
static int tc358764_attach(struct drm_bridge *bridge, @@ -356,6 +332,11 @@ static int tc358764_attach(struct drm_bridge *bridge, struct drm_device *drm = bridge->dev; int ret;
+ ret = drm_bridge_attach(bridge->encoder, ctx->panel_bridge, + bridge, flags); + if (ret < 0) + return ret; + if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) { DRM_ERROR("Fix bridge driver to make connector optional!"); return -EINVAL; @@ -373,32 +354,21 @@ static int tc358764_attach(struct drm_bridge *bridge, drm_connector_helper_add(&ctx->connector, &tc358764_connector_helper_funcs); drm_connector_attach_encoder(&ctx->connector, bridge->encoder); - drm_panel_attach(ctx->panel, &ctx->connector); ctx->connector.funcs->reset(&ctx->connector);
return 0; }
-static void tc358764_detach(struct drm_bridge *bridge) -{ - struct tc358764 *ctx = bridge_to_tc358764(bridge); - - drm_panel_detach(ctx->panel); - ctx->panel = NULL; -} - static const struct drm_bridge_funcs tc358764_bridge_funcs = { - .disable = tc358764_disable, .post_disable = tc358764_post_disable, - .enable = tc358764_enable, .pre_enable = tc358764_pre_enable, .attach = tc358764_attach, - .detach = tc358764_detach, };
static int tc358764_parse_dt(struct tc358764 *ctx) { struct device *dev = ctx->dev; + struct drm_panel *panel; int ret;
ctx->gpio_reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); @@ -407,12 +377,15 @@ static int tc358764_parse_dt(struct tc358764 *ctx) return PTR_ERR(ctx->gpio_reset); }
- ret = drm_of_find_panel_or_bridge(ctx->dev->of_node, 1, 0, &ctx->panel, - NULL); - if (ret && ret != -EPROBE_DEFER) - dev_err(dev, "cannot find panel (%d)\n", ret); + ret = drm_of_find_panel_or_bridge(dev->of_node, 1, 0, &panel, NULL); + if (ret) + return ret;
- return ret; + ctx->panel_bridge = devm_drm_panel_bridge_add(dev, panel); + if (IS_ERR(ctx->panel_bridge)) + return PTR_ERR(ctx->panel_bridge); + + return 0; }
static int tc358764_configure_regulators(struct tc358764 *ctx)
Hi Sam,
I love your patch! Perhaps something to improve:
[auto build test WARNING on drm-intel/for-linux-next] [also build test WARNING on drm-exynos/exynos-drm-next tegra-drm/drm/tegra/for-next drm-tip/drm-tip linus/master v5.7-rc3 next-20200428] [cannot apply to drm/drm-next] [if your patch is applied to the wrong git tree, please drop us a note to help improve the system. BTW, we also suggest to use '--base' option to specify the base tree in git format-patch, please see https://stackoverflow.com/a/37406982]
url: https://github.com/0day-ci/linux/commits/Sam-Ravnborg/drm-bridge-support-drm... base: git://anongit.freedesktop.org/drm-intel for-linux-next
If you fix the issue, kindly add following tag as appropriate Reported-by: kbuild test robot lkp@intel.com
coccinelle warnings: (new ones prefixed by >>)
drivers/gpu/drm/bridge/tc358764.c:385:1-3: WARNING: PTR_ERR_OR_ZERO can be used
Please review and possibly fold the followup patch.
--- 0-DAY CI Kernel Test Service, Intel Corporation https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
From: kbuild test robot lkp@intel.com
drivers/gpu/drm/bridge/tc358764.c:385:1-3: WARNING: PTR_ERR_OR_ZERO can be used
Use PTR_ERR_OR_ZERO rather than if(IS_ERR(...)) + PTR_ERR
Generated by: scripts/coccinelle/api/ptr_ret.cocci
CC: Sam Ravnborg sam@ravnborg.org Signed-off-by: kbuild test robot lkp@intel.com ---
url: https://github.com/0day-ci/linux/commits/Sam-Ravnborg/drm-bridge-support-drm... base: git://anongit.freedesktop.org/drm-intel for-linux-next
tc358764.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-)
--- a/drivers/gpu/drm/bridge/tc358764.c +++ b/drivers/gpu/drm/bridge/tc358764.c @@ -382,10 +382,7 @@ static int tc358764_parse_dt(struct tc35 return ret;
ctx->panel_bridge = devm_drm_panel_bridge_add(dev, panel); - if (IS_ERR(ctx->panel_bridge)) - return PTR_ERR(ctx->panel_bridge); - - return 0; + return PTR_ERR_OR_ZERO(ctx->panel_bridge); }
static int tc358764_configure_regulators(struct tc358764 *ctx)
Make the connector creation optional to enable usage of the tc358764 bridge with the DRM bridge connector helper.
Signed-off-by: Sam Ravnborg sam@ravnborg.org Reviewed-by: Laurent Pinchart laurent.pinchart@ideasonboard.com Cc: Andrzej Hajda a.hajda@samsung.com Cc: Neil Armstrong narmstrong@baylibre.com Cc: Laurent Pinchart Laurent.pinchart@ideasonboard.com Cc: Jonas Karlman jonas@kwiboo.se Cc: Jernej Skrabec jernej.skrabec@siol.net --- drivers/gpu/drm/bridge/tc358764.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/bridge/tc358764.c b/drivers/gpu/drm/bridge/tc358764.c index 3adb2c191499..4013add64119 100644 --- a/drivers/gpu/drm/bridge/tc358764.c +++ b/drivers/gpu/drm/bridge/tc358764.c @@ -337,10 +337,8 @@ static int tc358764_attach(struct drm_bridge *bridge, if (ret < 0) return ret;
- if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) { - DRM_ERROR("Fix bridge driver to make connector optional!"); - return -EINVAL; - } + if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) + return 0;
ctx->connector.polled = DRM_CONNECTOR_POLL_HPD; ret = drm_connector_init(drm, &ctx->connector,
Prepare the bridge driver for use in a chained setup by replacing direct use of drm_panel with drm_panel_bridge support.
The bridge driver assume the panel is optional. The relevant tests are migrated over to check for the pnale bridge to keep the same functionality.
Note: the bridge panel will use the connector type from the panel.
Signed-off-by: Sam Ravnborg sam@ravnborg.org Cc: Andrzej Hajda a.hajda@samsung.com Cc: Neil Armstrong narmstrong@baylibre.com Cc: Laurent Pinchart Laurent.pinchart@ideasonboard.com Cc: Jonas Karlman jonas@kwiboo.se Cc: Jernej Skrabec jernej.skrabec@siol.net --- drivers/gpu/drm/bridge/tc358767.c | 57 +++++++++++++++---------------- 1 file changed, 27 insertions(+), 30 deletions(-)
diff --git a/drivers/gpu/drm/bridge/tc358767.c b/drivers/gpu/drm/bridge/tc358767.c index e4c0ea03ae3a..51f80ded6235 100644 --- a/drivers/gpu/drm/bridge/tc358767.c +++ b/drivers/gpu/drm/bridge/tc358767.c @@ -244,8 +244,8 @@ struct tc_data { struct drm_dp_aux aux;
struct drm_bridge bridge; + struct drm_bridge *panel_bridge; struct drm_connector connector; - struct drm_panel *panel;
/* link settings */ struct tc_edp_link link; @@ -1236,13 +1236,6 @@ static int tc_stream_disable(struct tc_data *tc) return 0; }
-static void tc_bridge_pre_enable(struct drm_bridge *bridge) -{ - struct tc_data *tc = bridge_to_tc(bridge); - - drm_panel_prepare(tc->panel); -} - static void tc_bridge_enable(struct drm_bridge *bridge) { struct tc_data *tc = bridge_to_tc(bridge); @@ -1266,8 +1259,6 @@ static void tc_bridge_enable(struct drm_bridge *bridge) tc_main_link_disable(tc); return; } - - drm_panel_enable(tc->panel); }
static void tc_bridge_disable(struct drm_bridge *bridge) @@ -1275,8 +1266,6 @@ static void tc_bridge_disable(struct drm_bridge *bridge) struct tc_data *tc = bridge_to_tc(bridge); int ret;
- drm_panel_disable(tc->panel); - ret = tc_stream_disable(tc); if (ret < 0) dev_err(tc->dev, "main link stream stop error: %d\n", ret); @@ -1286,13 +1275,6 @@ static void tc_bridge_disable(struct drm_bridge *bridge) dev_err(tc->dev, "main link disable error: %d\n", ret); }
-static void tc_bridge_post_disable(struct drm_bridge *bridge) -{ - struct tc_data *tc = bridge_to_tc(bridge); - - drm_panel_unprepare(tc->panel); -} - static bool tc_bridge_mode_fixup(struct drm_bridge *bridge, const struct drm_display_mode *mode, struct drm_display_mode *adj) @@ -1347,9 +1329,11 @@ static int tc_connector_get_modes(struct drm_connector *connector) return 0; }
- count = drm_panel_get_modes(tc->panel, connector); - if (count > 0) - return count; + if (tc->panel_bridge) { + count = drm_bridge_get_modes(tc->panel_bridge, connector); + if (count > 0) + return count; + }
edid = drm_get_edid(connector, &tc->aux.ddc);
@@ -1377,7 +1361,7 @@ static enum drm_connector_status tc_connector_detect(struct drm_connector *conne int ret;
if (tc->hpd_pin < 0) { - if (tc->panel) + if (tc->panel_bridge) return connector_status_connected; else return connector_status_unknown; @@ -1412,6 +1396,13 @@ static int tc_bridge_attach(struct drm_bridge *bridge, struct drm_device *drm = bridge->dev; int ret;
+ if (tc->panel_bridge) { + ret = drm_bridge_attach(tc->bridge.encoder, tc->panel_bridge, + &tc->bridge, flags); + if (ret < 0) + return ret; + } + if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) { DRM_ERROR("Fix bridge driver to make connector optional!"); return -EINVAL; @@ -1420,7 +1411,7 @@ static int tc_bridge_attach(struct drm_bridge *bridge, /* Create DP/eDP connector */ drm_connector_helper_add(&tc->connector, &tc_connector_helper_funcs); ret = drm_connector_init(drm, &tc->connector, &tc_connector_funcs, - tc->panel ? DRM_MODE_CONNECTOR_eDP : + tc->panel_bridge ? DRM_MODE_CONNECTOR_eDP : DRM_MODE_CONNECTOR_DisplayPort); if (ret) return ret; @@ -1434,9 +1425,6 @@ static int tc_bridge_attach(struct drm_bridge *bridge, DRM_CONNECTOR_POLL_DISCONNECT; }
- if (tc->panel) - drm_panel_attach(tc->panel, &tc->connector); - drm_display_info_set_bus_formats(&tc->connector.display_info, &bus_format, 1); tc->connector.display_info.bus_flags = @@ -1452,10 +1440,8 @@ static const struct drm_bridge_funcs tc_bridge_funcs = { .attach = tc_bridge_attach, .mode_valid = tc_mode_valid, .mode_set = tc_bridge_mode_set, - .pre_enable = tc_bridge_pre_enable, .enable = tc_bridge_enable, .disable = tc_bridge_disable, - .post_disable = tc_bridge_post_disable, .mode_fixup = tc_bridge_mode_fixup, };
@@ -1546,6 +1532,7 @@ static irqreturn_t tc_irq_handler(int irq, void *arg) static int tc_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct device *dev = &client->dev; + struct drm_panel *panel; struct tc_data *tc; int ret;
@@ -1556,10 +1543,20 @@ static int tc_probe(struct i2c_client *client, const struct i2c_device_id *id) tc->dev = dev;
/* port@2 is the output port */ - ret = drm_of_find_panel_or_bridge(dev->of_node, 2, 0, &tc->panel, NULL); + ret = drm_of_find_panel_or_bridge(dev->of_node, 2, 0, &panel, NULL); if (ret && ret != -ENODEV) return ret;
+ if (panel) { + struct drm_bridge *pbridge; + + pbridge = devm_drm_panel_bridge_add(dev, panel); + if (IS_ERR(pbridge)) + return PTR_ERR(pbridge); + + tc->panel_bridge = pbridge; + } + /* Shut down GPIO is optional */ tc->sd_gpio = devm_gpiod_get_optional(dev, "shutdown", GPIOD_OUT_HIGH); if (IS_ERR(tc->sd_gpio))
Prepare the bridge driver for chained operation by adding support for the detect operation.
Signed-off-by: Sam Ravnborg sam@ravnborg.org Cc: Andrzej Hajda a.hajda@samsung.com Cc: Neil Armstrong narmstrong@baylibre.com Cc: Laurent Pinchart Laurent.pinchart@ideasonboard.com Cc: Jonas Karlman jonas@kwiboo.se Cc: Jernej Skrabec jernej.skrabec@siol.net --- drivers/gpu/drm/bridge/tc358767.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/bridge/tc358767.c b/drivers/gpu/drm/bridge/tc358767.c index 51f80ded6235..211a058c7243 100644 --- a/drivers/gpu/drm/bridge/tc358767.c +++ b/drivers/gpu/drm/bridge/tc358767.c @@ -1352,10 +1352,9 @@ static const struct drm_connector_helper_funcs tc_connector_helper_funcs = { .get_modes = tc_connector_get_modes, };
-static enum drm_connector_status tc_connector_detect(struct drm_connector *connector, - bool force) +static enum drm_connector_status tc_bridge_detect(struct drm_bridge *bridge) { - struct tc_data *tc = connector_to_tc(connector); + struct tc_data *tc = bridge_to_tc(bridge); bool conn; u32 val; int ret; @@ -1379,6 +1378,14 @@ static enum drm_connector_status tc_connector_detect(struct drm_connector *conne return connector_status_disconnected; }
+static enum drm_connector_status +tc_connector_detect(struct drm_connector *connector, bool force) +{ + struct tc_data *tc = connector_to_tc(connector); + + return tc_bridge_detect(&tc->bridge); +} + static const struct drm_connector_funcs tc_connector_funcs = { .detect = tc_connector_detect, .fill_modes = drm_helper_probe_single_connector_modes, @@ -1443,6 +1450,7 @@ static const struct drm_bridge_funcs tc_bridge_funcs = { .enable = tc_bridge_enable, .disable = tc_bridge_disable, .mode_fixup = tc_bridge_mode_fixup, + .detect = tc_bridge_detect, };
static bool tc_readable_reg(struct device *dev, unsigned int reg) @@ -1676,6 +1684,8 @@ static int tc_probe(struct i2c_client *client, const struct i2c_device_id *id) return ret;
tc->bridge.funcs = &tc_bridge_funcs; + tc->bridge.ops = DRM_BRIDGE_OP_DETECT; + tc->bridge.of_node = dev->of_node; drm_bridge_add(&tc->bridge);
Prepare for chained bridge with the addition of get_edid support.
Signed-off-by: Sam Ravnborg sam@ravnborg.org Cc: Andrzej Hajda a.hajda@samsung.com Cc: Neil Armstrong narmstrong@baylibre.com Cc: Laurent Pinchart Laurent.pinchart@ideasonboard.com Cc: Jonas Karlman jonas@kwiboo.se Cc: Jernej Skrabec jernej.skrabec@siol.net --- drivers/gpu/drm/bridge/tc358767.c | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-)
diff --git a/drivers/gpu/drm/bridge/tc358767.c b/drivers/gpu/drm/bridge/tc358767.c index 211a058c7243..42a153bcde64 100644 --- a/drivers/gpu/drm/bridge/tc358767.c +++ b/drivers/gpu/drm/bridge/tc358767.c @@ -1316,6 +1316,20 @@ static void tc_bridge_mode_set(struct drm_bridge *bridge, tc->mode = *mode; }
+static struct edid *tc_get_edid(struct drm_bridge *bridge, + struct drm_connector *connector) +{ + struct tc_data *tc = bridge_to_tc(bridge); + struct edid *edid; + + edid = drm_get_edid(connector, &tc->aux.ddc); + + kfree(tc->edid); + tc->edid = edid; + + return edid; +} + static int tc_connector_get_modes(struct drm_connector *connector) { struct tc_data *tc = connector_to_tc(connector); @@ -1335,12 +1349,7 @@ static int tc_connector_get_modes(struct drm_connector *connector) return count; }
- edid = drm_get_edid(connector, &tc->aux.ddc); - - kfree(tc->edid); - tc->edid = edid; - if (!edid) - return 0; + edid = tc_get_edid(&tc->bridge, connector);
drm_connector_update_edid_property(connector, edid); count = drm_add_edid_modes(connector, edid); @@ -1451,6 +1460,7 @@ static const struct drm_bridge_funcs tc_bridge_funcs = { .disable = tc_bridge_disable, .mode_fixup = tc_bridge_mode_fixup, .detect = tc_bridge_detect, + .get_edid = tc_get_edid, };
static bool tc_readable_reg(struct device *dev, unsigned int reg) @@ -1684,7 +1694,7 @@ static int tc_probe(struct i2c_client *client, const struct i2c_device_id *id) return ret;
tc->bridge.funcs = &tc_bridge_funcs; - tc->bridge.ops = DRM_BRIDGE_OP_DETECT; + tc->bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID;
tc->bridge.of_node = dev->of_node; drm_bridge_add(&tc->bridge);
Display drivers are in the new model expected to create the connector using drm_bridge_connector_init(). Allow users of this bridge driver to support the new model by introducing support for optional connector creation.
Signed-off-by: Sam Ravnborg sam@ravnborg.org Cc: Andrzej Hajda a.hajda@samsung.com Cc: Neil Armstrong narmstrong@baylibre.com Cc: Laurent Pinchart Laurent.pinchart@ideasonboard.com Cc: Jonas Karlman jonas@kwiboo.se Cc: Jernej Skrabec jernej.skrabec@siol.net --- drivers/gpu/drm/bridge/tc358767.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/bridge/tc358767.c b/drivers/gpu/drm/bridge/tc358767.c index 42a153bcde64..991e7d67b692 100644 --- a/drivers/gpu/drm/bridge/tc358767.c +++ b/drivers/gpu/drm/bridge/tc358767.c @@ -1420,8 +1420,7 @@ static int tc_bridge_attach(struct drm_bridge *bridge, }
if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) { - DRM_ERROR("Fix bridge driver to make connector optional!"); - return -EINVAL; + return 0; }
/* Create DP/eDP connector */
The ti-tpd12s015 do not create any connector, so ignore the flags argument, just pass it on to the next bridge in the chain.
Signed-off-by: Sam Ravnborg sam@ravnborg.org Cc: Andrzej Hajda a.hajda@samsung.com Cc: Neil Armstrong narmstrong@baylibre.com Cc: Laurent Pinchart Laurent.pinchart@ideasonboard.com Cc: Jonas Karlman jonas@kwiboo.se Cc: Jernej Skrabec jernej.skrabec@siol.net --- drivers/gpu/drm/bridge/ti-tpd12s015.c | 3 --- 1 file changed, 3 deletions(-)
diff --git a/drivers/gpu/drm/bridge/ti-tpd12s015.c b/drivers/gpu/drm/bridge/ti-tpd12s015.c index 514cbf0eac75..4f1666422ab2 100644 --- a/drivers/gpu/drm/bridge/ti-tpd12s015.c +++ b/drivers/gpu/drm/bridge/ti-tpd12s015.c @@ -43,9 +43,6 @@ static int tpd12s015_attach(struct drm_bridge *bridge, struct tpd12s015_device *tpd = to_tpd12s015(bridge); int ret;
- if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) - return -EINVAL; - ret = drm_bridge_attach(bridge->encoder, tpd->next_bridge, bridge, flags); if (ret < 0)
Prepare the bridge driver for use in a chained setup by replacing direct use of drm_panel with drm_panel_bridge support.
Note: the bridge panel will use the connector type from the panel.
Signed-off-by: Sam Ravnborg sam@ravnborg.org Cc: Andrzej Hajda a.hajda@samsung.com Cc: Neil Armstrong narmstrong@baylibre.com Cc: Laurent Pinchart Laurent.pinchart@ideasonboard.com Cc: Jonas Karlman jonas@kwiboo.se Cc: Jernej Skrabec jernej.skrabec@siol.net --- drivers/gpu/drm/bridge/parade-ps8622.c | 46 ++++++++------------------ 1 file changed, 13 insertions(+), 33 deletions(-)
diff --git a/drivers/gpu/drm/bridge/parade-ps8622.c b/drivers/gpu/drm/bridge/parade-ps8622.c index d789ea2a7fb9..6ab6f60b9091 100644 --- a/drivers/gpu/drm/bridge/parade-ps8622.c +++ b/drivers/gpu/drm/bridge/parade-ps8622.c @@ -45,7 +45,7 @@ struct ps8622_bridge { struct drm_connector connector; struct i2c_client *client; struct drm_bridge bridge; - struct drm_panel *panel; + struct drm_bridge *panel_bridge; struct regulator *v12; struct backlight_device *bl;
@@ -365,11 +365,6 @@ static void ps8622_pre_enable(struct drm_bridge *bridge) DRM_ERROR("fails to enable ps8622->v12"); }
- if (drm_panel_prepare(ps8622->panel)) { - DRM_ERROR("failed to prepare panel\n"); - return; - } - gpiod_set_value(ps8622->gpio_slp, 1);
/* @@ -399,24 +394,8 @@ static void ps8622_pre_enable(struct drm_bridge *bridge) ps8622->enabled = true; }
-static void ps8622_enable(struct drm_bridge *bridge) -{ - struct ps8622_bridge *ps8622 = bridge_to_ps8622(bridge); - - if (drm_panel_enable(ps8622->panel)) { - DRM_ERROR("failed to enable panel\n"); - return; - } -} - static void ps8622_disable(struct drm_bridge *bridge) { - struct ps8622_bridge *ps8622 = bridge_to_ps8622(bridge); - - if (drm_panel_disable(ps8622->panel)) { - DRM_ERROR("failed to disable panel\n"); - return; - } msleep(PS8622_PWMO_END_T12_MS); }
@@ -436,11 +415,6 @@ static void ps8622_post_disable(struct drm_bridge *bridge) */ gpiod_set_value(ps8622->gpio_slp, 0);
- if (drm_panel_unprepare(ps8622->panel)) { - DRM_ERROR("failed to unprepare panel\n"); - return; - } - if (ps8622->v12) regulator_disable(ps8622->v12);
@@ -461,7 +435,7 @@ static int ps8622_get_modes(struct drm_connector *connector)
ps8622 = connector_to_ps8622(connector);
- return drm_panel_get_modes(ps8622->panel, connector); + return drm_bridge_get_modes(ps8622->panel_bridge, connector); }
static const struct drm_connector_helper_funcs ps8622_connector_helper_funcs = { @@ -482,6 +456,9 @@ static int ps8622_attach(struct drm_bridge *bridge, struct ps8622_bridge *ps8622 = bridge_to_ps8622(bridge); int ret;
+ ret = drm_bridge_attach(ps8622->bridge.encoder, ps8622->panel_bridge, + &ps8622->bridge, flags); + if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) { DRM_ERROR("Fix bridge driver to make connector optional!"); return -EINVAL; @@ -505,9 +482,6 @@ static int ps8622_attach(struct drm_bridge *bridge, drm_connector_attach_encoder(&ps8622->connector, bridge->encoder);
- if (ps8622->panel) - drm_panel_attach(ps8622->panel, &ps8622->connector); - drm_helper_hpd_irq_event(ps8622->connector.dev);
return ret; @@ -515,7 +489,6 @@ static int ps8622_attach(struct drm_bridge *bridge,
static const struct drm_bridge_funcs ps8622_bridge_funcs = { .pre_enable = ps8622_pre_enable, - .enable = ps8622_enable, .disable = ps8622_disable, .post_disable = ps8622_post_disable, .attach = ps8622_attach, @@ -533,16 +506,23 @@ static int ps8622_probe(struct i2c_client *client, { struct device *dev = &client->dev; struct ps8622_bridge *ps8622; + struct drm_bridge *pbridge; + struct drm_panel *panel; int ret;
ps8622 = devm_kzalloc(dev, sizeof(*ps8622), GFP_KERNEL); if (!ps8622) return -ENOMEM;
- ret = drm_of_find_panel_or_bridge(dev->of_node, 0, 0, &ps8622->panel, NULL); + ret = drm_of_find_panel_or_bridge(dev->of_node, 0, 0, &panel, NULL); if (ret) return ret;
+ pbridge = devm_drm_panel_bridge_add(dev, panel); + if (IS_ERR(pbridge)) + return PTR_ERR(pbridge); + + ps8622->panel_bridge = pbridge; ps8622->client = client;
ps8622->v12 = devm_regulator_get(dev, "vdd12");
Make the connector creation optional to enable usage of the parade-ps8622 bridge with the DRM bridge connector helper.
This change moves drm_helper_hpd_irq_event() call in the attach function up before the connector creation.
Signed-off-by: Sam Ravnborg sam@ravnborg.org Cc: Andrzej Hajda a.hajda@samsung.com Cc: Neil Armstrong narmstrong@baylibre.com Cc: Laurent Pinchart Laurent.pinchart@ideasonboard.com Cc: Jonas Karlman jonas@kwiboo.se Cc: Jernej Skrabec jernej.skrabec@siol.net --- drivers/gpu/drm/bridge/parade-ps8622.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-)
diff --git a/drivers/gpu/drm/bridge/parade-ps8622.c b/drivers/gpu/drm/bridge/parade-ps8622.c index 6ab6f60b9091..54aa5270d2c9 100644 --- a/drivers/gpu/drm/bridge/parade-ps8622.c +++ b/drivers/gpu/drm/bridge/parade-ps8622.c @@ -459,10 +459,8 @@ static int ps8622_attach(struct drm_bridge *bridge, ret = drm_bridge_attach(ps8622->bridge.encoder, ps8622->panel_bridge, &ps8622->bridge, flags);
- if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) { - DRM_ERROR("Fix bridge driver to make connector optional!"); - return -EINVAL; - } + if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) + return 0;
if (!bridge->encoder) { DRM_ERROR("Parent encoder object not found"); @@ -482,7 +480,7 @@ static int ps8622_attach(struct drm_bridge *bridge, drm_connector_attach_encoder(&ps8622->connector, bridge->encoder);
- drm_helper_hpd_irq_event(ps8622->connector.dev); + drm_helper_hpd_irq_event(ps8622->bridge.dev);
return ret; }
Factor out connector creation to a small helper function.
Signed-off-by: Sam Ravnborg sam@ravnborg.org Cc: Peter Senna Tschudin peter.senna@gmail.com Cc: Martin Donnelly martin.donnelly@ge.com Cc: Martyn Welch martyn.welch@collabora.co.uk Cc: Andrzej Hajda a.hajda@samsung.com Cc: Neil Armstrong narmstrong@baylibre.com Cc: Laurent Pinchart Laurent.pinchart@ideasonboard.com Cc: Jonas Karlman jonas@kwiboo.se Cc: Jernej Skrabec jernej.skrabec@siol.net --- .../bridge/megachips-stdpxxxx-ge-b850v3-fw.c | 47 +++++++++++-------- 1 file changed, 27 insertions(+), 20 deletions(-)
diff --git a/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c b/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c index 6200f12a37e6..258e0525cdcc 100644 --- a/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c +++ b/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c @@ -191,6 +191,32 @@ static const struct drm_connector_funcs ge_b850v3_lvds_connector_funcs = { .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, };
+static int ge_b850v3_lvds_create_connector(struct drm_bridge *bridge) +{ + struct drm_connector *connector = &ge_b850v3_lvds_ptr->connector; + int ret; + + if (!bridge->encoder) { + DRM_ERROR("Parent encoder object not found"); + return -ENODEV; + } + + connector->polled = DRM_CONNECTOR_POLL_HPD; + + drm_connector_helper_add(connector, + &ge_b850v3_lvds_connector_helper_funcs); + + ret = drm_connector_init(bridge->dev, connector, + &ge_b850v3_lvds_connector_funcs, + DRM_MODE_CONNECTOR_DisplayPort); + if (ret) { + DRM_ERROR("Failed to initialize connector with drm\n"); + return ret; + } + + return drm_connector_attach_encoder(connector, bridge->encoder); +} + static irqreturn_t ge_b850v3_lvds_irq_handler(int irq, void *dev_id) { struct i2c_client *stdp4028_i2c @@ -209,7 +235,6 @@ static irqreturn_t ge_b850v3_lvds_irq_handler(int irq, void *dev_id) static int ge_b850v3_lvds_attach(struct drm_bridge *bridge, enum drm_bridge_attach_flags flags) { - struct drm_connector *connector = &ge_b850v3_lvds_ptr->connector; struct i2c_client *stdp4028_i2c = ge_b850v3_lvds_ptr->stdp4028_i2c; int ret; @@ -219,25 +244,7 @@ static int ge_b850v3_lvds_attach(struct drm_bridge *bridge, return -EINVAL; }
- if (!bridge->encoder) { - DRM_ERROR("Parent encoder object not found"); - return -ENODEV; - } - - connector->polled = DRM_CONNECTOR_POLL_HPD; - - drm_connector_helper_add(connector, - &ge_b850v3_lvds_connector_helper_funcs); - - ret = drm_connector_init(bridge->dev, connector, - &ge_b850v3_lvds_connector_funcs, - DRM_MODE_CONNECTOR_DisplayPort); - if (ret) { - DRM_ERROR("Failed to initialize connector with drm\n"); - return ret; - } - - ret = drm_connector_attach_encoder(connector, bridge->encoder); + ret = ge_b850v3_lvds_create_connector(bridge); if (ret) return ret;
Fix so drm_device is read from the bridge. This is a preparation for the connector being optional.
Signed-off-by: Sam Ravnborg sam@ravnborg.org Cc: Peter Senna Tschudin peter.senna@gmail.com Cc: Martin Donnelly martin.donnelly@ge.com Cc: Martyn Welch martyn.welch@collabora.co.uk Cc: Andrzej Hajda a.hajda@samsung.com Cc: Neil Armstrong narmstrong@baylibre.com Cc: Laurent Pinchart Laurent.pinchart@ideasonboard.com Cc: Jonas Karlman jonas@kwiboo.se Cc: Jernej Skrabec jernej.skrabec@siol.net --- drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c b/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c index 258e0525cdcc..cf1dfbc88acf 100644 --- a/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c +++ b/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c @@ -226,8 +226,8 @@ static irqreturn_t ge_b850v3_lvds_irq_handler(int irq, void *dev_id) STDP4028_DPTX_IRQ_STS_REG, STDP4028_DPTX_IRQ_CLEAR);
- if (ge_b850v3_lvds_ptr->connector.dev) - drm_kms_helper_hotplug_event(ge_b850v3_lvds_ptr->connector.dev); + if (ge_b850v3_lvds_ptr->bridge.dev) + drm_kms_helper_hotplug_event(ge_b850v3_lvds_ptr->bridge.dev);
return IRQ_HANDLED; }
To prepare for use in a chained bridge setup enable the detect operation.
Signed-off-by: Sam Ravnborg sam@ravnborg.org Cc: Peter Senna Tschudin peter.senna@gmail.com Cc: Martin Donnelly martin.donnelly@ge.com Cc: Martyn Welch martyn.welch@collabora.co.uk Cc: Andrzej Hajda a.hajda@samsung.com Cc: Neil Armstrong narmstrong@baylibre.com Cc: Laurent Pinchart Laurent.pinchart@ideasonboard.com Cc: Jonas Karlman jonas@kwiboo.se Cc: Jernej Skrabec jernej.skrabec@siol.net --- .../gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c b/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c index cf1dfbc88acf..78a9afe8f063 100644 --- a/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c +++ b/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c @@ -163,8 +163,8 @@ drm_connector_helper_funcs ge_b850v3_lvds_connector_helper_funcs = { .mode_valid = ge_b850v3_lvds_mode_valid, };
-static enum drm_connector_status ge_b850v3_lvds_detect( - struct drm_connector *connector, bool force) +static enum drm_connector_status ge_b850v3_lvds_bridge_detect( + struct drm_bridge *bridge) { struct i2c_client *stdp4028_i2c = ge_b850v3_lvds_ptr->stdp4028_i2c; @@ -182,6 +182,12 @@ static enum drm_connector_status ge_b850v3_lvds_detect( return connector_status_unknown; }
+static enum drm_connector_status ge_b850v3_lvds_detect( + struct drm_connector *connector, bool force) +{ + return ge_b850v3_lvds_bridge_detect(&ge_b850v3_lvds_ptr->bridge); +} + static const struct drm_connector_funcs ge_b850v3_lvds_connector_funcs = { .fill_modes = drm_helper_probe_single_connector_modes, .detect = ge_b850v3_lvds_detect, @@ -263,6 +269,7 @@ static int ge_b850v3_lvds_attach(struct drm_bridge *bridge,
static const struct drm_bridge_funcs ge_b850v3_lvds_funcs = { .attach = ge_b850v3_lvds_attach, + .detect = ge_b850v3_lvds_bridge_detect, };
static int ge_b850v3_lvds_init(struct device *dev) @@ -317,6 +324,7 @@ static int stdp4028_ge_b850v3_fw_probe(struct i2c_client *stdp4028_i2c,
/* drm bridge initialization */ ge_b850v3_lvds_ptr->bridge.funcs = &ge_b850v3_lvds_funcs; + ge_b850v3_lvds_ptr->bridge.ops = DRM_BRIDGE_OP_DETECT; ge_b850v3_lvds_ptr->bridge.of_node = dev->of_node; drm_bridge_add(&ge_b850v3_lvds_ptr->bridge);
To prepare for a chained bridge setup add support for the get_edid bridge operation.
Signed-off-by: Sam Ravnborg sam@ravnborg.org Cc: Peter Senna Tschudin peter.senna@gmail.com Cc: Martin Donnelly martin.donnelly@ge.com Cc: Martyn Welch martyn.welch@collabora.co.uk Cc: Andrzej Hajda a.hajda@samsung.com Cc: Neil Armstrong narmstrong@baylibre.com Cc: Laurent Pinchart Laurent.pinchart@ideasonboard.com Cc: Jonas Karlman jonas@kwiboo.se Cc: Jernej Skrabec jernej.skrabec@siol.net --- .../bridge/megachips-stdpxxxx-ge-b850v3-fw.c | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-)
diff --git a/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c b/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c index 78a9afe8f063..5f06e18f0a61 100644 --- a/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c +++ b/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c @@ -131,21 +131,29 @@ static u8 *stdp2690_get_edid(struct i2c_client *client) return NULL; }
-static int ge_b850v3_lvds_get_modes(struct drm_connector *connector) +static struct edid *ge_b850v3_lvds_get_edid( + struct drm_bridge *bridge, struct drm_connector *connector) { struct i2c_client *client; - int num_modes = 0;
client = ge_b850v3_lvds_ptr->stdp2690_i2c;
kfree(ge_b850v3_lvds_ptr->edid); ge_b850v3_lvds_ptr->edid = (struct edid *)stdp2690_get_edid(client);
- if (ge_b850v3_lvds_ptr->edid) { - drm_connector_update_edid_property(connector, - ge_b850v3_lvds_ptr->edid); - num_modes = drm_add_edid_modes(connector, - ge_b850v3_lvds_ptr->edid); + return ge_b850v3_lvds_ptr->edid; +} + +static int ge_b850v3_lvds_get_modes(struct drm_connector *connector) +{ + struct edid *edid; + int num_modes = 0; + + edid = ge_b850v3_lvds_get_edid(&ge_b850v3_lvds_ptr->bridge, connector); + + if (edid) { + drm_connector_update_edid_property(connector, edid); + num_modes = drm_add_edid_modes(connector, edid); }
return num_modes; @@ -270,6 +278,7 @@ static int ge_b850v3_lvds_attach(struct drm_bridge *bridge, static const struct drm_bridge_funcs ge_b850v3_lvds_funcs = { .attach = ge_b850v3_lvds_attach, .detect = ge_b850v3_lvds_bridge_detect, + .get_edid = ge_b850v3_lvds_get_edid, };
static int ge_b850v3_lvds_init(struct device *dev) @@ -324,7 +333,8 @@ static int stdp4028_ge_b850v3_fw_probe(struct i2c_client *stdp4028_i2c,
/* drm bridge initialization */ ge_b850v3_lvds_ptr->bridge.funcs = &ge_b850v3_lvds_funcs; - ge_b850v3_lvds_ptr->bridge.ops = DRM_BRIDGE_OP_DETECT; + ge_b850v3_lvds_ptr->bridge.ops = DRM_BRIDGE_OP_DETECT | + DRM_BRIDGE_OP_EDID; ge_b850v3_lvds_ptr->bridge.of_node = dev->of_node; drm_bridge_add(&ge_b850v3_lvds_ptr->bridge);
Make the connector creation optional to enable usage of the megachips-stdpxxxx-ge-b850v3-fw bridge with the DRM bridge connector helper.
Signed-off-by: Sam Ravnborg sam@ravnborg.org Cc: Peter Senna Tschudin peter.senna@gmail.com Cc: Martin Donnelly martin.donnelly@ge.com Cc: Martyn Welch martyn.welch@collabora.co.uk Cc: Andrzej Hajda a.hajda@samsung.com Cc: Neil Armstrong narmstrong@baylibre.com Cc: Laurent Pinchart Laurent.pinchart@ideasonboard.com Cc: Jonas Karlman jonas@kwiboo.se Cc: Jernej Skrabec jernej.skrabec@siol.net --- .../drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-)
diff --git a/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c b/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c index 5f06e18f0a61..991417ab35b6 100644 --- a/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c +++ b/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c @@ -251,16 +251,6 @@ static int ge_b850v3_lvds_attach(struct drm_bridge *bridge, { struct i2c_client *stdp4028_i2c = ge_b850v3_lvds_ptr->stdp4028_i2c; - int ret; - - if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) { - DRM_ERROR("Fix bridge driver to make connector optional!"); - return -EINVAL; - } - - ret = ge_b850v3_lvds_create_connector(bridge); - if (ret) - return ret;
/* Configures the bridge to re-enable interrupts after each ack. */ i2c_smbus_write_word_data(stdp4028_i2c, @@ -272,7 +262,10 @@ static int ge_b850v3_lvds_attach(struct drm_bridge *bridge, STDP4028_DPTX_IRQ_EN_REG, STDP4028_DPTX_IRQ_CONFIG);
- return 0; + if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) + return 0; + + return ge_b850v3_lvds_create_connector(bridge); }
static const struct drm_bridge_funcs ge_b850v3_lvds_funcs = {
Prepare the bridge driver for use in a chained setup by replacing direct use of drm_panel with drm_panel_bridge support.
Note: the bridge panel will use the connector type from the panel.
Signed-off-by: Sam Ravnborg sam@ravnborg.org Cc: Andrzej Hajda a.hajda@samsung.com Cc: Neil Armstrong narmstrong@baylibre.com Cc: Laurent Pinchart Laurent.pinchart@ideasonboard.com Cc: Jonas Karlman jonas@kwiboo.se Cc: Jernej Skrabec jernej.skrabec@siol.net --- drivers/gpu/drm/bridge/nxp-ptn3460.c | 51 ++++++++-------------------- 1 file changed, 14 insertions(+), 37 deletions(-)
diff --git a/drivers/gpu/drm/bridge/nxp-ptn3460.c b/drivers/gpu/drm/bridge/nxp-ptn3460.c index 438e566ce0a4..0bd9f0e451b3 100644 --- a/drivers/gpu/drm/bridge/nxp-ptn3460.c +++ b/drivers/gpu/drm/bridge/nxp-ptn3460.c @@ -30,7 +30,7 @@ struct ptn3460_bridge { struct i2c_client *client; struct drm_bridge bridge; struct edid *edid; - struct drm_panel *panel; + struct drm_bridge *panel_bridge; struct gpio_desc *gpio_pd_n; struct gpio_desc *gpio_rst_n; u32 edid_emulation; @@ -127,11 +127,6 @@ static void ptn3460_pre_enable(struct drm_bridge *bridge) usleep_range(10, 20); gpiod_set_value(ptn_bridge->gpio_rst_n, 1);
- if (drm_panel_prepare(ptn_bridge->panel)) { - DRM_ERROR("failed to prepare panel\n"); - return; - } - /* * There's a bug in the PTN chip where it falsely asserts hotplug before * it is fully functional. We're forced to wait for the maximum start up @@ -146,16 +141,6 @@ static void ptn3460_pre_enable(struct drm_bridge *bridge) ptn_bridge->enabled = true; }
-static void ptn3460_enable(struct drm_bridge *bridge) -{ - struct ptn3460_bridge *ptn_bridge = bridge_to_ptn3460(bridge); - - if (drm_panel_enable(ptn_bridge->panel)) { - DRM_ERROR("failed to enable panel\n"); - return; - } -} - static void ptn3460_disable(struct drm_bridge *bridge) { struct ptn3460_bridge *ptn_bridge = bridge_to_ptn3460(bridge); @@ -165,25 +150,10 @@ static void ptn3460_disable(struct drm_bridge *bridge)
ptn_bridge->enabled = false;
- if (drm_panel_disable(ptn_bridge->panel)) { - DRM_ERROR("failed to disable panel\n"); - return; - } - gpiod_set_value(ptn_bridge->gpio_rst_n, 1); gpiod_set_value(ptn_bridge->gpio_pd_n, 0); }
-static void ptn3460_post_disable(struct drm_bridge *bridge) -{ - struct ptn3460_bridge *ptn_bridge = bridge_to_ptn3460(bridge); - - if (drm_panel_unprepare(ptn_bridge->panel)) { - DRM_ERROR("failed to unprepare panel\n"); - return; - } -} - static int ptn3460_get_modes(struct drm_connector *connector) { struct ptn3460_bridge *ptn_bridge; @@ -242,6 +212,11 @@ static int ptn3460_bridge_attach(struct drm_bridge *bridge, struct ptn3460_bridge *ptn_bridge = bridge_to_ptn3460(bridge); int ret;
+ ret = drm_bridge_attach(bridge->encoder, ptn_bridge->panel_bridge, + bridge, flags); + if (ret < 0) + return ret; + if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) { DRM_ERROR("Fix bridge driver to make connector optional!"); return -EINVAL; @@ -265,9 +240,6 @@ static int ptn3460_bridge_attach(struct drm_bridge *bridge, drm_connector_attach_encoder(&ptn_bridge->connector, bridge->encoder);
- if (ptn_bridge->panel) - drm_panel_attach(ptn_bridge->panel, &ptn_bridge->connector); - drm_helper_hpd_irq_event(ptn_bridge->connector.dev);
return ret; @@ -275,9 +247,7 @@ static int ptn3460_bridge_attach(struct drm_bridge *bridge,
static const struct drm_bridge_funcs ptn3460_bridge_funcs = { .pre_enable = ptn3460_pre_enable, - .enable = ptn3460_enable, .disable = ptn3460_disable, - .post_disable = ptn3460_post_disable, .attach = ptn3460_bridge_attach, };
@@ -286,6 +256,8 @@ static int ptn3460_probe(struct i2c_client *client, { struct device *dev = &client->dev; struct ptn3460_bridge *ptn_bridge; + struct drm_bridge *pbridge; + struct drm_panel *panel; int ret;
ptn_bridge = devm_kzalloc(dev, sizeof(*ptn_bridge), GFP_KERNEL); @@ -293,10 +265,15 @@ static int ptn3460_probe(struct i2c_client *client, return -ENOMEM; }
- ret = drm_of_find_panel_or_bridge(dev->of_node, 0, 0, &ptn_bridge->panel, NULL); + ret = drm_of_find_panel_or_bridge(dev->of_node, 0, 0, &panel, NULL); if (ret) return ret;
+ pbridge = devm_drm_panel_bridge_add(dev, panel); + if (IS_ERR(pbridge)) + return PTR_ERR(pbridge); + + ptn_bridge->panel_bridge = pbridge; ptn_bridge->client = client;
ptn_bridge->gpio_pd_n = devm_gpiod_get(&client->dev, "powerdown",
Add the get_modes() bridge operation to prepare for use as a chained bridge. Add helper function that is also used by the connector.
Signed-off-by: Sam Ravnborg sam@ravnborg.org Cc: Andrzej Hajda a.hajda@samsung.com Cc: Neil Armstrong narmstrong@baylibre.com Cc: Laurent Pinchart Laurent.pinchart@ideasonboard.com Cc: Jonas Karlman jonas@kwiboo.se Cc: Jernej Skrabec jernej.skrabec@siol.net --- drivers/gpu/drm/bridge/nxp-ptn3460.c | 52 ++++++++++++++++++---------- 1 file changed, 33 insertions(+), 19 deletions(-)
diff --git a/drivers/gpu/drm/bridge/nxp-ptn3460.c b/drivers/gpu/drm/bridge/nxp-ptn3460.c index 0bd9f0e451b3..e253c185f94c 100644 --- a/drivers/gpu/drm/bridge/nxp-ptn3460.c +++ b/drivers/gpu/drm/bridge/nxp-ptn3460.c @@ -154,17 +154,13 @@ static void ptn3460_disable(struct drm_bridge *bridge) gpiod_set_value(ptn_bridge->gpio_pd_n, 0); }
-static int ptn3460_get_modes(struct drm_connector *connector) +static struct edid *ptn3460_get_edid(struct drm_bridge *bridge, + struct drm_connector *connector) { - struct ptn3460_bridge *ptn_bridge; - u8 *edid; - int ret, num_modes = 0; + struct ptn3460_bridge *ptn_bridge = bridge_to_ptn3460(bridge); bool power_off; - - ptn_bridge = connector_to_ptn3460(connector); - - if (ptn_bridge->edid) - return drm_add_edid_modes(connector, ptn_bridge->edid); + u8 *edid; + int ret;
power_off = !ptn_bridge->enabled; ptn3460_pre_enable(&ptn_bridge->bridge); @@ -172,30 +168,46 @@ static int ptn3460_get_modes(struct drm_connector *connector) edid = kmalloc(EDID_LENGTH, GFP_KERNEL); if (!edid) { DRM_ERROR("Failed to allocate EDID\n"); - return 0; + return NULL; }
ret = ptn3460_read_bytes(ptn_bridge, PTN3460_EDID_ADDR, edid, - EDID_LENGTH); + EDID_LENGTH); if (ret) { kfree(edid); - goto out; + return NULL; }
+ if (power_off) + ptn3460_disable(&ptn_bridge->bridge); + + kfree(ptn_bridge->edid); ptn_bridge->edid = (struct edid *)edid; - drm_connector_update_edid_property(connector, ptn_bridge->edid);
- num_modes = drm_add_edid_modes(connector, ptn_bridge->edid); + return ptn_bridge->edid; +}
-out: - if (power_off) - ptn3460_disable(&ptn_bridge->bridge); +static int ptn3460_connector_get_modes(struct drm_connector *connector) +{ + struct ptn3460_bridge *ptn_bridge; + struct edid *edid; + + ptn_bridge = connector_to_ptn3460(connector); + + if (ptn_bridge->edid) + return drm_add_edid_modes(connector, ptn_bridge->edid); + + edid = ptn3460_get_edid(&ptn_bridge->bridge, connector); + if (!edid) + return 0; + + drm_connector_update_edid_property(connector, edid);
- return num_modes; + return drm_add_edid_modes(connector, edid); }
static const struct drm_connector_helper_funcs ptn3460_connector_helper_funcs = { - .get_modes = ptn3460_get_modes, + .get_modes = ptn3460_connector_get_modes, };
static const struct drm_connector_funcs ptn3460_connector_funcs = { @@ -249,6 +261,7 @@ static const struct drm_bridge_funcs ptn3460_bridge_funcs = { .pre_enable = ptn3460_pre_enable, .disable = ptn3460_disable, .attach = ptn3460_bridge_attach, + .get_edid = ptn3460_get_edid, };
static int ptn3460_probe(struct i2c_client *client, @@ -304,6 +317,7 @@ static int ptn3460_probe(struct i2c_client *client, }
ptn_bridge->bridge.funcs = &ptn3460_bridge_funcs; + ptn_bridge->bridge.ops = DRM_BRIDGE_OP_EDID; ptn_bridge->bridge.of_node = dev->of_node; drm_bridge_add(&ptn_bridge->bridge);
Make the connector creation optional to enable usage of the nxp-ptn3460 bridge with the DRM bridge connector helper.
Signed-off-by: Sam Ravnborg sam@ravnborg.org Cc: Andrzej Hajda a.hajda@samsung.com Cc: Neil Armstrong narmstrong@baylibre.com Cc: Laurent Pinchart Laurent.pinchart@ideasonboard.com Cc: Jonas Karlman jonas@kwiboo.se Cc: Jernej Skrabec jernej.skrabec@siol.net --- drivers/gpu/drm/bridge/nxp-ptn3460.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/bridge/nxp-ptn3460.c b/drivers/gpu/drm/bridge/nxp-ptn3460.c index e253c185f94c..6a65657087f9 100644 --- a/drivers/gpu/drm/bridge/nxp-ptn3460.c +++ b/drivers/gpu/drm/bridge/nxp-ptn3460.c @@ -229,10 +229,8 @@ static int ptn3460_bridge_attach(struct drm_bridge *bridge, if (ret < 0) return ret;
- if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) { - DRM_ERROR("Fix bridge driver to make connector optional!"); - return -EINVAL; - } + if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) + return 0;
if (!bridge->encoder) { DRM_ERROR("Parent encoder object not found");
Prepare the bridge driver for use in a chained setup by replacing direct use of drm_panel with drm_panel_bridge support.
Note: the bridge panel will use the connector type from the panel.
Signed-off-by: Sam Ravnborg sam@ravnborg.org Cc: Andrzej Hajda a.hajda@samsung.com Cc: Neil Armstrong narmstrong@baylibre.com Cc: Laurent Pinchart Laurent.pinchart@ideasonboard.com Cc: Jonas Karlman jonas@kwiboo.se Cc: Jernej Skrabec jernej.skrabec@siol.net --- drivers/gpu/drm/bridge/ti-sn65dsi86.c | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-)
diff --git a/drivers/gpu/drm/bridge/ti-sn65dsi86.c b/drivers/gpu/drm/bridge/ti-sn65dsi86.c index 6ad688b320ae..c306d3069037 100644 --- a/drivers/gpu/drm/bridge/ti-sn65dsi86.c +++ b/drivers/gpu/drm/bridge/ti-sn65dsi86.c @@ -98,7 +98,7 @@ struct ti_sn_bridge { struct device_node *host_node; struct mipi_dsi_device *dsi; struct clk *refclk; - struct drm_panel *panel; + struct drm_bridge *panel_bridge; struct gpio_desc *enable_gpio; struct regulator_bulk_data supplies[SN_REGULATOR_SUPPLY_NUM]; int dp_lanes; @@ -208,7 +208,7 @@ static int ti_sn_bridge_connector_get_modes(struct drm_connector *connector) { struct ti_sn_bridge *pdata = connector_to_ti_sn_bridge(connector);
- return drm_panel_get_modes(pdata->panel, connector); + return drm_bridge_get_modes(pdata->panel_bridge, connector); }
static enum drm_mode_status @@ -340,9 +340,8 @@ static int ti_sn_bridge_attach(struct drm_bridge *bridge, pdata->dsi = dsi;
/* attach panel to bridge */ - drm_panel_attach(pdata->panel, &pdata->connector); - - return 0; + return drm_bridge_attach(bridge->encoder, pdata->panel_bridge, + bridge, flags);
err_dsi_attach: mipi_dsi_device_unregister(dsi); @@ -355,16 +354,12 @@ static void ti_sn_bridge_disable(struct drm_bridge *bridge) { struct ti_sn_bridge *pdata = bridge_to_ti_sn_bridge(bridge);
- drm_panel_disable(pdata->panel); - /* disable video stream */ regmap_update_bits(pdata->regmap, SN_ENH_FRAME_REG, VSTREAM_ENABLE, 0); /* semi auto link training mode OFF */ regmap_write(pdata->regmap, SN_ML_TX_MODE_REG, 0); /* disable DP PLL */ regmap_write(pdata->regmap, SN_PLL_ENABLE_REG, 0); - - drm_panel_unprepare(pdata->panel); }
static u32 ti_sn_bridge_get_dsi_freq(struct ti_sn_bridge *pdata) @@ -731,8 +726,6 @@ static void ti_sn_bridge_enable(struct drm_bridge *bridge) /* enable video stream */ regmap_update_bits(pdata->regmap, SN_ENH_FRAME_REG, VSTREAM_ENABLE, VSTREAM_ENABLE); - - drm_panel_enable(pdata->panel); }
static void ti_sn_bridge_pre_enable(struct drm_bridge *bridge) @@ -762,8 +755,6 @@ static void ti_sn_bridge_pre_enable(struct drm_bridge *bridge) */ regmap_update_bits(pdata->regmap, SN_HPD_DISABLE_REG, HPD_DISABLE, HPD_DISABLE); - - drm_panel_prepare(pdata->panel); }
static void ti_sn_bridge_post_disable(struct drm_bridge *bridge) @@ -878,6 +869,8 @@ static int ti_sn_bridge_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct ti_sn_bridge *pdata; + struct drm_bridge *bridge; + struct drm_panel *panel; int ret;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { @@ -900,12 +893,18 @@ static int ti_sn_bridge_probe(struct i2c_client *client, pdata->dev = &client->dev;
ret = drm_of_find_panel_or_bridge(pdata->dev->of_node, 1, 0, - &pdata->panel, NULL); + &panel, NULL); if (ret) { DRM_ERROR("could not find any panel node\n"); return ret; }
+ bridge = devm_drm_panel_bridge_add(pdata->dev, panel); + if (IS_ERR(bridge)) + return PTR_ERR(bridge); + + pdata->panel_bridge = bridge; + dev_set_drvdata(&client->dev, pdata);
pdata->enable_gpio = devm_gpiod_get(pdata->dev, "enable",
dri-devel@lists.freedesktop.org