Changes since v1: - Fix null point when dsi next bridge isn't a panel. - "dsi mmsys reset" is implement by https://patchwork.kernel.org/project/linux-mediatek/list/?series=515355
Jitao Shi (3): drm/panel: seperate panel power control from panel prepare/unprepare drm/panel: boe-tv101wum-n16 seperate the panel power control drm/mediatek: fine tune the dsi panel's power sequence
drivers/gpu/drm/bridge/panel.c | 17 +++++ drivers/gpu/drm/drm_panel.c | 38 ++++++++++ drivers/gpu/drm/mediatek/mtk_dsi.c | 29 ++++++-- .../gpu/drm/panel/panel-boe-tv101wum-nl6.c | 72 +++++++++++++------ include/drm/drm_bridge.h | 2 + include/drm/drm_panel.h | 17 +++++ 6 files changed, 148 insertions(+), 27 deletions(-)
Some dsi panels require the dsi lanes keeping low before panel power on. So seperate the panel power control and the communication with panel.
And put the power control in drm_panel_prepare_power and drm_panel_unprepare_power. Put the communication with panel in drm_panel_prepare and drm_panel_unprepare.
Signed-off-by: Jitao Shi jitao.shi@mediatek.com --- drivers/gpu/drm/bridge/panel.c | 17 +++++++++++++++ drivers/gpu/drm/drm_panel.c | 38 ++++++++++++++++++++++++++++++++++ include/drm/drm_bridge.h | 2 ++ include/drm/drm_panel.h | 17 +++++++++++++++ 4 files changed, 74 insertions(+)
diff --git a/drivers/gpu/drm/bridge/panel.c b/drivers/gpu/drm/bridge/panel.c index c916f4b8907e..3a846ac8e24c 100644 --- a/drivers/gpu/drm/bridge/panel.c +++ b/drivers/gpu/drm/bridge/panel.c @@ -137,6 +137,23 @@ static int panel_bridge_get_modes(struct drm_bridge *bridge, return drm_panel_get_modes(panel_bridge->panel, connector); }
+int panel_bridge_prepare_power(struct drm_bridge *bridge) +{ + struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge); + + return drm_panel_prepare_power(panel_bridge->panel); +} +EXPORT_SYMBOL(panel_bridge_prepare_power); + +int panel_bridge_unprepare_power(struct drm_bridge *bridge) +{ + struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge); + + return drm_panel_unprepare_power(panel_bridge->panel); +} +EXPORT_SYMBOL(panel_bridge_unprepare_power); + + static const struct drm_bridge_funcs panel_bridge_bridge_funcs = { .attach = panel_bridge_attach, .detach = panel_bridge_detach, diff --git a/drivers/gpu/drm/drm_panel.c b/drivers/gpu/drm/drm_panel.c index f634371c717a..7bb5185db17d 100644 --- a/drivers/gpu/drm/drm_panel.c +++ b/drivers/gpu/drm/drm_panel.c @@ -115,6 +115,24 @@ int drm_panel_prepare(struct drm_panel *panel) } EXPORT_SYMBOL(drm_panel_prepare);
+/** + * drm_panel_prepare_power - power on a panel's power + * @panel: DRM panel + * + * Calling this function will enable power and deassert any reset signals to + * the panel. + * + * Return: 0 on success or a negative error code on failure. + */ +int drm_panel_prepare_power(struct drm_panel *panel) +{ + if (panel && panel->funcs && panel->funcs->prepare_power) + return panel->funcs->prepare_power(panel); + + return panel ? -ENOSYS : -EINVAL; +} +EXPORT_SYMBOL(drm_panel_prepare_power); + /** * drm_panel_unprepare - power off a panel * @panel: DRM panel @@ -138,6 +156,26 @@ int drm_panel_unprepare(struct drm_panel *panel) } EXPORT_SYMBOL(drm_panel_unprepare);
+/** + * drm_panel_unprepare_power - power off a panel + * @panel: DRM panel + * + * Calling this function will completely power off a panel (assert the panel's + * reset, turn off power supplies, ...). After this function has completed, it + * is usually no longer possible to communicate with the panel until another + * call to drm_panel_prepare_power and drm_panel_prepare(). + * + * Return: 0 on success or a negative error code on failure. + */ +int drm_panel_unprepare_power(struct drm_panel *panel) +{ + if (panel && panel->funcs && panel->funcs->unprepare_power) + return panel->funcs->unprepare_power(panel); + + return panel ? -ENOSYS : -EINVAL; +} +EXPORT_SYMBOL(drm_panel_unprepare_power); + /** * drm_panel_enable - enable a panel * @panel: DRM panel diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h index 2195daa289d2..cc94c9da47d8 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -892,6 +892,8 @@ struct drm_bridge *devm_drm_panel_bridge_add_typed(struct device *dev, struct drm_panel *panel, u32 connector_type); struct drm_connector *drm_panel_bridge_connector(struct drm_bridge *bridge); +int panel_bridge_prepare_power(struct drm_bridge *bridge); +int panel_bridge_unprepare_power(struct drm_bridge *bridge); #endif
#endif diff --git a/include/drm/drm_panel.h b/include/drm/drm_panel.h index 33605c3f0eba..48e83712ad44 100644 --- a/include/drm/drm_panel.h +++ b/include/drm/drm_panel.h @@ -68,6 +68,13 @@ enum drm_panel_orientation; * functionality to enable/disable backlight. */ struct drm_panel_funcs { + /** + * @prepare_power: + * + * Turn on panel power. + */ + int (*prepare_power)(struct drm_panel *panel); + /** * @prepare: * @@ -115,6 +122,13 @@ struct drm_panel_funcs { int (*get_modes)(struct drm_panel *panel, struct drm_connector *connector);
+ /** + * @unprepare_power: + * + * Turn off panel_power. + */ + int (*unprepare_power)(struct drm_panel *panel); + /** * @get_timings: * @@ -180,6 +194,9 @@ void drm_panel_init(struct drm_panel *panel, struct device *dev, void drm_panel_add(struct drm_panel *panel); void drm_panel_remove(struct drm_panel *panel);
+int drm_panel_prepare_power(struct drm_panel *panel); +int drm_panel_unprepare_power(struct drm_panel *panel); + int drm_panel_prepare(struct drm_panel *panel); int drm_panel_unprepare(struct drm_panel *panel);
On Sun, Aug 08, 2021 at 08:52:16PM +0800, Jitao Shi wrote:
Some dsi panels require the dsi lanes keeping low before panel power on. So seperate the panel power control and the communication with panel.
And put the power control in drm_panel_prepare_power and drm_panel_unprepare_power. Put the communication with panel in drm_panel_prepare and drm_panel_unprepare.
Signed-off-by: Jitao Shi jitao.shi@mediatek.com
This solves your problem, but it breaks the panel standard for everyone else since you're not converting them over.
There needs to be more thought here in how to make this all compatible.
Also I think we need very precise spec for how this is supposed to work with DSI panels, and then making sure existing drivers mostly follow this.
Hacking up a shared interface that's used by lots of drivers just to then fix a bug in one user and one implementation is not good. -Daniel
drivers/gpu/drm/bridge/panel.c | 17 +++++++++++++++ drivers/gpu/drm/drm_panel.c | 38 ++++++++++++++++++++++++++++++++++ include/drm/drm_bridge.h | 2 ++ include/drm/drm_panel.h | 17 +++++++++++++++ 4 files changed, 74 insertions(+)
diff --git a/drivers/gpu/drm/bridge/panel.c b/drivers/gpu/drm/bridge/panel.c index c916f4b8907e..3a846ac8e24c 100644 --- a/drivers/gpu/drm/bridge/panel.c +++ b/drivers/gpu/drm/bridge/panel.c @@ -137,6 +137,23 @@ static int panel_bridge_get_modes(struct drm_bridge *bridge, return drm_panel_get_modes(panel_bridge->panel, connector); }
+int panel_bridge_prepare_power(struct drm_bridge *bridge) +{
- struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
- return drm_panel_prepare_power(panel_bridge->panel);
+} +EXPORT_SYMBOL(panel_bridge_prepare_power);
+int panel_bridge_unprepare_power(struct drm_bridge *bridge) +{
struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
return drm_panel_unprepare_power(panel_bridge->panel);
+} +EXPORT_SYMBOL(panel_bridge_unprepare_power);
static const struct drm_bridge_funcs panel_bridge_bridge_funcs = { .attach = panel_bridge_attach, .detach = panel_bridge_detach, diff --git a/drivers/gpu/drm/drm_panel.c b/drivers/gpu/drm/drm_panel.c index f634371c717a..7bb5185db17d 100644 --- a/drivers/gpu/drm/drm_panel.c +++ b/drivers/gpu/drm/drm_panel.c @@ -115,6 +115,24 @@ int drm_panel_prepare(struct drm_panel *panel) } EXPORT_SYMBOL(drm_panel_prepare);
+/**
- drm_panel_prepare_power - power on a panel's power
- @panel: DRM panel
- Calling this function will enable power and deassert any reset signals to
- the panel.
- Return: 0 on success or a negative error code on failure.
- */
+int drm_panel_prepare_power(struct drm_panel *panel) +{
- if (panel && panel->funcs && panel->funcs->prepare_power)
return panel->funcs->prepare_power(panel);
- return panel ? -ENOSYS : -EINVAL;
+} +EXPORT_SYMBOL(drm_panel_prepare_power);
/**
- drm_panel_unprepare - power off a panel
- @panel: DRM panel
@@ -138,6 +156,26 @@ int drm_panel_unprepare(struct drm_panel *panel) } EXPORT_SYMBOL(drm_panel_unprepare);
+/**
- drm_panel_unprepare_power - power off a panel
- @panel: DRM panel
- Calling this function will completely power off a panel (assert the panel's
- reset, turn off power supplies, ...). After this function has completed, it
- is usually no longer possible to communicate with the panel until another
- call to drm_panel_prepare_power and drm_panel_prepare().
- Return: 0 on success or a negative error code on failure.
- */
+int drm_panel_unprepare_power(struct drm_panel *panel) +{
- if (panel && panel->funcs && panel->funcs->unprepare_power)
return panel->funcs->unprepare_power(panel);
- return panel ? -ENOSYS : -EINVAL;
+} +EXPORT_SYMBOL(drm_panel_unprepare_power);
/**
- drm_panel_enable - enable a panel
- @panel: DRM panel
diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h index 2195daa289d2..cc94c9da47d8 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -892,6 +892,8 @@ struct drm_bridge *devm_drm_panel_bridge_add_typed(struct device *dev, struct drm_panel *panel, u32 connector_type); struct drm_connector *drm_panel_bridge_connector(struct drm_bridge *bridge); +int panel_bridge_prepare_power(struct drm_bridge *bridge); +int panel_bridge_unprepare_power(struct drm_bridge *bridge); #endif
#endif diff --git a/include/drm/drm_panel.h b/include/drm/drm_panel.h index 33605c3f0eba..48e83712ad44 100644 --- a/include/drm/drm_panel.h +++ b/include/drm/drm_panel.h @@ -68,6 +68,13 @@ enum drm_panel_orientation;
- functionality to enable/disable backlight.
*/ struct drm_panel_funcs {
- /**
* @prepare_power:
*
* Turn on panel power.
*/
- int (*prepare_power)(struct drm_panel *panel);
- /**
- @prepare:
@@ -115,6 +122,13 @@ struct drm_panel_funcs { int (*get_modes)(struct drm_panel *panel, struct drm_connector *connector);
- /**
* @unprepare_power:
*
* Turn off panel_power.
*/
- int (*unprepare_power)(struct drm_panel *panel);
- /**
- @get_timings:
@@ -180,6 +194,9 @@ void drm_panel_init(struct drm_panel *panel, struct device *dev, void drm_panel_add(struct drm_panel *panel); void drm_panel_remove(struct drm_panel *panel);
+int drm_panel_prepare_power(struct drm_panel *panel); +int drm_panel_unprepare_power(struct drm_panel *panel);
int drm_panel_prepare(struct drm_panel *panel); int drm_panel_unprepare(struct drm_panel *panel);
-- 2.25.1
Seperate the panel power control from prepare/unprepare.
Signed-off-by: Jitao Shi jitao.shi@mediatek.com --- .../gpu/drm/panel/panel-boe-tv101wum-nl6.c | 72 +++++++++++++------ 1 file changed, 50 insertions(+), 22 deletions(-)
diff --git a/drivers/gpu/drm/panel/panel-boe-tv101wum-nl6.c b/drivers/gpu/drm/panel/panel-boe-tv101wum-nl6.c index db9d0b86d542..dc49079a74d1 100644 --- a/drivers/gpu/drm/panel/panel-boe-tv101wum-nl6.c +++ b/drivers/gpu/drm/panel/panel-boe-tv101wum-nl6.c @@ -50,6 +50,7 @@ struct boe_panel { struct regulator *avdd; struct gpio_desc *enable_gpio;
+ bool prepared_power; bool prepared; };
@@ -488,22 +489,13 @@ static int boe_panel_enter_sleep_mode(struct boe_panel *boe) return 0; }
-static int boe_panel_unprepare(struct drm_panel *panel) +static int boe_panel_unprepare_power(struct drm_panel *panel) { struct boe_panel *boe = to_boe_panel(panel); - int ret;
- if (!boe->prepared) + if (!boe->prepared_power) return 0;
- ret = boe_panel_enter_sleep_mode(boe); - if (ret < 0) { - dev_err(panel->dev, "failed to set panel off: %d\n", ret); - return ret; - } - - msleep(150); - if (boe->desc->discharge_on_disable) { regulator_disable(boe->avee); regulator_disable(boe->avdd); @@ -512,6 +504,7 @@ static int boe_panel_unprepare(struct drm_panel *panel) usleep_range(5000, 7000); regulator_disable(boe->pp1800); } else { + msleep(150); gpiod_set_value(boe->enable_gpio, 0); usleep_range(500, 1000); regulator_disable(boe->avee); @@ -520,17 +513,39 @@ static int boe_panel_unprepare(struct drm_panel *panel) regulator_disable(boe->pp1800); }
+ boe->prepared_power = false; + + return 0; +} + +static int boe_panel_unprepare(struct drm_panel *panel) +{ + struct boe_panel *boe = to_boe_panel(panel); + int ret; + + if (!boe->prepared) + return 0; + + if (!boe->desc->discharge_on_disable) { + ret = boe_panel_enter_sleep_mode(boe); + if (ret < 0) { + dev_err(panel->dev, "failed to set panel off: %d\n", + ret); + return ret; + } + } + boe->prepared = false;
return 0; }
-static int boe_panel_prepare(struct drm_panel *panel) +static int boe_panel_prepare_power(struct drm_panel *panel) { struct boe_panel *boe = to_boe_panel(panel); int ret;
- if (boe->prepared) + if (boe->prepared_power) return 0;
gpiod_set_value(boe->enable_gpio, 0); @@ -558,18 +573,10 @@ static int boe_panel_prepare(struct drm_panel *panel) gpiod_set_value(boe->enable_gpio, 1); usleep_range(6000, 10000);
- ret = boe_panel_init_dcs_cmd(boe); - if (ret < 0) { - dev_err(panel->dev, "failed to init panel: %d\n", ret); - goto poweroff; - } - - boe->prepared = true; + boe->prepared_power = true;
return 0;
-poweroff: - regulator_disable(boe->avee); poweroffavdd: regulator_disable(boe->avdd); poweroff1v8: @@ -580,6 +587,25 @@ static int boe_panel_prepare(struct drm_panel *panel) return ret; }
+static int boe_panel_prepare(struct drm_panel *panel) +{ + struct boe_panel *boe = to_boe_panel(panel); + int ret; + + if (boe->prepared) + return 0; + + ret = boe_panel_init_dcs_cmd(boe); + if (ret < 0) { + dev_err(panel->dev, "failed to init panel: %d\n", ret); + return ret; + } + + boe->prepared = true; + + return 0; +} + static int boe_panel_enable(struct drm_panel *panel) { msleep(130); @@ -749,7 +775,9 @@ static int boe_panel_get_modes(struct drm_panel *panel,
static const struct drm_panel_funcs boe_panel_funcs = { .unprepare = boe_panel_unprepare, + .unprepare_power = boe_panel_unprepare_power, .prepare = boe_panel_prepare, + .prepare_power = boe_panel_prepare_power, .enable = boe_panel_enable, .get_modes = boe_panel_get_modes, };
Add the drm_panel_prepare_power and drm_panel_unprepare_power control. Turn on panel power(drm_panel_prepare_power) and control before dsi enable. And then dsi enable, send dcs cmd in drm_panel_prepare, last turn on backlight.
Most dsi panels, have five steps when poweron.
1. turn on dsi signal to LP11 --> dsi host's action 2. turn on the power supplies, --> panel's action 3. send the DCS cmd to panel --> panel's action 4. start send video stream --> dsi host's action 5. turn on backlight. --> panel's action
we put "turn on the power supplies" and "send the DCS cmd to panel" in panel_prepare. And "turn on backlight" in panel_enable.
But some other panels has a special poweron sequence as the following.
1. turn on the power supplies, --> panel's action 2. turn on dsi signal to LP11 --> dsi host's action 3. send the DCS cmd to panel --> panel's action 4. start send video stream --> dsi host's action 5. turn on backlight. --> panel's action
panel's actions are divided into three parts.
So I add a new api "drm_panel_prepare_power/rm_panel_unprepare_power" to control the sequence.
Signed-off-by: Jitao Shi jitao.shi@mediatek.com --- drivers/gpu/drm/mediatek/mtk_dsi.c | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-)
diff --git a/drivers/gpu/drm/mediatek/mtk_dsi.c b/drivers/gpu/drm/mediatek/mtk_dsi.c index ae403c67cbd9..24f89a1dd421 100644 --- a/drivers/gpu/drm/mediatek/mtk_dsi.c +++ b/drivers/gpu/drm/mediatek/mtk_dsi.c @@ -184,6 +184,7 @@ struct mtk_dsi { struct drm_encoder encoder; struct drm_bridge bridge; struct drm_bridge *next_bridge; + struct drm_panel *panel; struct drm_connector *connector; struct phy *phy;
@@ -619,10 +620,18 @@ static int mtk_dsi_poweron(struct mtk_dsi *dsi) dsi->data_rate = DIV_ROUND_UP_ULL(dsi->vm.pixelclock * bit_per_pixel, dsi->lanes);
+ if (dsi->panel) { + ret = panel_bridge_prepare_power(dsi->next_bridge) + if (ret) { + DRM_INFO("can't prepare power the panel\n"); + goto err_refcount; + } + } + ret = clk_set_rate(dsi->hs_clk, dsi->data_rate); if (ret < 0) { dev_err(dev, "Failed to set data rate: %d\n", ret); - goto err_refcount; + goto err_prepare_power; }
phy_power_on(dsi->phy); @@ -665,6 +674,11 @@ static int mtk_dsi_poweron(struct mtk_dsi *dsi) clk_disable_unprepare(dsi->engine_clk); err_phy_power_off: phy_power_off(dsi->phy); +err_prepare_power: + if (dsi->panel) { + if (panel_bridge_unprepare_power(dsi->next_bridge)) + dev_err(dev, "Can't unprepare power the panel\n"); + } err_refcount: dsi->refcount--; return ret; @@ -698,6 +712,12 @@ static void mtk_dsi_poweroff(struct mtk_dsi *dsi) clk_disable_unprepare(dsi->digital_clk);
phy_power_off(dsi->phy); + + if (dsi->panel) { + ret = panel_bridge_unprepare_power(dsi->next_bridge); + if (ret) + dev_err(dev, "Can't unprepare power the panel ret:%d\n", ret); + } }
static void mtk_output_dsi_enable(struct mtk_dsi *dsi) @@ -1001,7 +1021,6 @@ static int mtk_dsi_probe(struct platform_device *pdev) { struct mtk_dsi *dsi; struct device *dev = &pdev->dev; - struct drm_panel *panel; struct resource *regs; int irq_num; int ret; @@ -1019,12 +1038,12 @@ static int mtk_dsi_probe(struct platform_device *pdev) }
ret = drm_of_find_panel_or_bridge(dev->of_node, 0, 0, - &panel, &dsi->next_bridge); + &dsi->panel, &dsi->next_bridge); if (ret) goto err_unregister_host;
- if (panel) { - dsi->next_bridge = devm_drm_panel_bridge_add(dev, panel); + if (dsi->panel) { + dsi->next_bridge = devm_drm_panel_bridge_add(dev, dsi->panel); if (IS_ERR(dsi->next_bridge)) { ret = PTR_ERR(dsi->next_bridge); goto err_unregister_host;
dri-devel@lists.freedesktop.org