This series is based on exynos-drm-next branch of Inki Dae's tree at: git://git.kernel.org/pub/scm/linux/kernel/git/daeinki/drm-exynos.git
I have tested this after adding few DT changes for exynos5250-snow, exynos5420-peach-pit and exynos5800-peach-pi boards.
The V4 series of this particular patchset was also tested by: Rahul Sharma rahul.sharma@samsung.com Javier Martinez Canillas javier@dowhile0.org
Changes since V2: -- Address comments from Jingoo Han for ps8622 driver -- Address comments from Daniel, Rob and Thierry regarding bridge chaining -- Address comments from Thierry regarding the names for new drm_panel functions
Changes since V3: -- Remove hotplug based initialization of exynos_dp -- Make exynos_dp work directly with drm_panel, remove dependency on panel_binder -- Minor cleanups in panel_binder and panel_lvds driver
Changes since V4: -- Use gpiod interface for panel-lvds and ps8622 drivers. -- Address comments from Javier. -- Fix compilation issues when PANEL_BINDER is selected as module. -- Split Documentation patches from driver patches. -- Rebase on top of the tree.
Changes since V5: -- Modify bridge drivers to support driver model. -- Drop the concept of bridge chain(sincle there are no 2 real bridges) Hence drop bridge-panel_binder layer. -- Drop panel-lvds driver and accomodate the required changes in panel-simple driver. -- Use gpiod interface in ptn3460 driver. -- Address all comments by Thierry Reding for V5 series. -- Address comments from Sean Paul for exynos_dp_commit issue.
Ajay Kumar (6): [PATCH V6 1/8] drm/panel: Add prepare, unprepare and get_modes routines [PATCH V6 2/8] drm/panel: Add support for prepare and unprepare routines [PATCH V6 3/8] drm/panel: simple: Add support for auo_b133htn01 panel [PATCH V6 4/8] drm/exynos: Move DP setup into commit() [PATCH V6 5/8] drm/exynos: dp: Modify driver to support drm_panel [PATCH V6 6/8] drm/bridge: Modify drm_bridge core to support driver model
Sean Paul (1): [PATCH V6 7/8] drm/bridge: Add i2c based driver for ptn3460 bridge
Vincent Palatin (1): [PATCH V6 8/8] drm/bridge: Add i2c based driver for ps8622/ps8625 bridge
.../devicetree/bindings/drm/bridge/ptn3460.txt | 27 - .../devicetree/bindings/panel/auo,b133htn01.txt | 7 + .../devicetree/bindings/vendor-prefixes.txt | 1 + .../devicetree/bindings/video/bridge/ps8622.txt | 19 + .../devicetree/bindings/video/bridge/ptn3460.txt | 27 + drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/bridge/Kconfig | 32 +- drivers/gpu/drm/bridge/Makefile | 1 + drivers/gpu/drm/bridge/ps8622.c | 602 ++++++++++++++++++++ drivers/gpu/drm/bridge/ptn3460.c | 212 ++++--- drivers/gpu/drm/drm_bridge.c | 89 +++ drivers/gpu/drm/drm_crtc.c | 9 +- drivers/gpu/drm/exynos/Kconfig | 4 +- drivers/gpu/drm/exynos/exynos_dp_core.c | 165 ++++-- drivers/gpu/drm/exynos/exynos_dp_core.h | 4 +- drivers/gpu/drm/exynos/exynos_drm_dpi.c | 8 +- drivers/gpu/drm/exynos/exynos_drm_dsi.c | 7 + drivers/gpu/drm/msm/hdmi/hdmi_bridge.c | 3 +- drivers/gpu/drm/panel/panel-ld9040.c | 18 +- drivers/gpu/drm/panel/panel-s6e8aa0.c | 18 +- drivers/gpu/drm/panel/panel-simple.c | 96 +++- drivers/gpu/drm/tegra/output.c | 2 + include/drm/bridge/ptn3460.h | 37 -- include/drm/drm_crtc.h | 16 +- include/drm/drm_panel.h | 26 + 25 files changed, 1206 insertions(+), 225 deletions(-) delete mode 100644 Documentation/devicetree/bindings/drm/bridge/ptn3460.txt create mode 100644 Documentation/devicetree/bindings/panel/auo,b133htn01.txt create mode 100644 Documentation/devicetree/bindings/video/bridge/ps8622.txt create mode 100644 Documentation/devicetree/bindings/video/bridge/ptn3460.txt create mode 100644 drivers/gpu/drm/bridge/ps8622.c create mode 100644 drivers/gpu/drm/drm_bridge.c delete mode 100644 include/drm/bridge/ptn3460.h
Most of the panels need an init sequence as mentioned below: -- poweron LCD unit/LCD_EN -- start video data -- poweron LED unit/BACKLIGHT_EN And, a de-init sequence as mentioned below: -- poweroff LED unit/BACKLIGHT_EN -- stop video data -- poweroff LCD unit/LCD_EN With existing callbacks for drm panel, we cannot accomodate such panels, since only two callbacks, i.e "panel_enable" and panel_disable are supported.
This patch adds: -- "prepare" callback which can be called before the actual video data is on, and then call the "enable" callback after the video data is available.
-- "unprepare" callback which can be called after the video data is off, and use "disable" callback to do something before switching off the video data.
Now, we can easily map the above scenario as shown below: poweron LCD unit/LCD_EN = "prepare" callback poweron LED unit/BACKLIGHT_EN = "enable" callback poweroff LED unit/BACKLIGHT_EN = "disable" callback poweroff LCD unit/LCD_EN = "unprepare" callback
Also, a helper function for get_modes is added.
Signed-off-by: Ajay Kumar ajaykumar.rs@samsung.com --- include/drm/drm_panel.h | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+)
diff --git a/include/drm/drm_panel.h b/include/drm/drm_panel.h index c2ab77a..efc63cc 100644 --- a/include/drm/drm_panel.h +++ b/include/drm/drm_panel.h @@ -31,7 +31,9 @@ struct drm_device; struct drm_panel;
struct drm_panel_funcs { + int (*unprepare)(struct drm_panel *panel); int (*disable)(struct drm_panel *panel); + int (*prepare)(struct drm_panel *panel); int (*enable)(struct drm_panel *panel); int (*get_modes)(struct drm_panel *panel); }; @@ -46,6 +48,14 @@ struct drm_panel { struct list_head list; };
+static inline int drm_panel_unprepare(struct drm_panel *panel) +{ + if (panel && panel->funcs && panel->funcs->unprepare) + return panel->funcs->unprepare(panel); + + return panel ? -ENOSYS : -EINVAL; +} + static inline int drm_panel_disable(struct drm_panel *panel) { if (panel && panel->funcs && panel->funcs->disable) @@ -54,6 +64,14 @@ static inline int drm_panel_disable(struct drm_panel *panel) return panel ? -ENOSYS : -EINVAL; }
+static inline int drm_panel_prepare(struct drm_panel *panel) +{ + if (panel && panel->funcs && panel->funcs->prepare) + return panel->funcs->prepare(panel); + + return panel ? -ENOSYS : -EINVAL; +} + static inline int drm_panel_enable(struct drm_panel *panel) { if (panel && panel->funcs && panel->funcs->enable) @@ -62,6 +80,14 @@ static inline int drm_panel_enable(struct drm_panel *panel) return panel ? -ENOSYS : -EINVAL; }
+static inline int drm_panel_get_modes(struct drm_panel *panel) +{ + if (panel && panel->funcs && panel->funcs->get_modes) + return panel->funcs->get_modes(panel); + + return 0; +} + void drm_panel_init(struct drm_panel *panel);
int drm_panel_add(struct drm_panel *panel);
On Sat, Jul 26, 2014 at 12:52:03AM +0530, Ajay Kumar wrote:
Most of the panels need an init sequence as mentioned below: -- poweron LCD unit/LCD_EN -- start video data -- poweron LED unit/BACKLIGHT_EN And, a de-init sequence as mentioned below: -- poweroff LED unit/BACKLIGHT_EN -- stop video data -- poweroff LCD unit/LCD_EN With existing callbacks for drm panel, we cannot accomodate such panels, since only two callbacks, i.e "panel_enable" and panel_disable are supported.
This patch adds: -- "prepare" callback which can be called before the actual video data is on, and then call the "enable" callback after the video data is available.
-- "unprepare" callback which can be called after the video data is off, and use "disable" callback to do something before switching off the video data.
Now, we can easily map the above scenario as shown below: poweron LCD unit/LCD_EN = "prepare" callback poweron LED unit/BACKLIGHT_EN = "enable" callback poweroff LED unit/BACKLIGHT_EN = "disable" callback poweroff LCD unit/LCD_EN = "unprepare" callback
Also, a helper function for get_modes is added.
Signed-off-by: Ajay Kumar ajaykumar.rs@samsung.com
include/drm/drm_panel.h | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+)
I had already decided to apply an earlier version that you posted (I think the only difference was that it didn't have the inline implementation for drm_panel_get_modes() yet), reworded the commit message a little (to be less specific about LED, LCD and backlight units) and added some kerneldoc for struct drm_panel_funcs.
What I propose is to merge that patch (I'll hopefully push it today so that it shows up in tomorrow's linux-next) and then apply a separate patch to add drm_panel_get_modes().
Do you have any objections to that?
Thierry
On Wed, Jul 30, 2014 at 3:30 PM, Thierry Reding thierry.reding@gmail.com wrote:
On Sat, Jul 26, 2014 at 12:52:03AM +0530, Ajay Kumar wrote:
Most of the panels need an init sequence as mentioned below: -- poweron LCD unit/LCD_EN -- start video data -- poweron LED unit/BACKLIGHT_EN And, a de-init sequence as mentioned below: -- poweroff LED unit/BACKLIGHT_EN -- stop video data -- poweroff LCD unit/LCD_EN With existing callbacks for drm panel, we cannot accomodate such panels, since only two callbacks, i.e "panel_enable" and panel_disable are supported.
This patch adds: -- "prepare" callback which can be called before the actual video data is on, and then call the "enable" callback after the video data is available.
-- "unprepare" callback which can be called after the video data is off, and use "disable" callback to do something before switching off the video data.
Now, we can easily map the above scenario as shown below: poweron LCD unit/LCD_EN = "prepare" callback poweron LED unit/BACKLIGHT_EN = "enable" callback poweroff LED unit/BACKLIGHT_EN = "disable" callback poweroff LCD unit/LCD_EN = "unprepare" callback
Also, a helper function for get_modes is added.
Signed-off-by: Ajay Kumar ajaykumar.rs@samsung.com
include/drm/drm_panel.h | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+)
I had already decided to apply an earlier version that you posted (I think the only difference was that it didn't have the inline implementation for drm_panel_get_modes() yet), reworded the commit message a little (to be less specific about LED, LCD and backlight units) and added some kerneldoc for struct drm_panel_funcs.
What I propose is to merge that patch (I'll hopefully push it today so that it shows up in tomorrow's linux-next) and then apply a separate patch to add drm_panel_get_modes().
Do you have any objections to that?
I am okay with that. drm_panel_get_modes() will come as a separate patch.
Ajay
Now that we have 2 new callbacks(prepare and unprepare) for drm_panel, make changes in all the drm drivers which use the drm_panel framework to support the new callbacks.
Signed-off-by: Ajay Kumar ajaykumar.rs@samsung.com --- drivers/gpu/drm/exynos/exynos_drm_dpi.c | 8 +++-- drivers/gpu/drm/exynos/exynos_drm_dsi.c | 7 +++++ drivers/gpu/drm/panel/panel-ld9040.c | 18 +++++++++-- drivers/gpu/drm/panel/panel-s6e8aa0.c | 18 +++++++++-- drivers/gpu/drm/panel/panel-simple.c | 51 ++++++++++++++++++++++++------- drivers/gpu/drm/tegra/output.c | 2 ++ 6 files changed, 85 insertions(+), 19 deletions(-)
diff --git a/drivers/gpu/drm/exynos/exynos_drm_dpi.c b/drivers/gpu/drm/exynos/exynos_drm_dpi.c index 3aa1c7e..fa08f05 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_dpi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_dpi.c @@ -125,14 +125,18 @@ static int exynos_dpi_create_connector(struct exynos_drm_display *display,
static void exynos_dpi_poweron(struct exynos_dpi *ctx) { - if (ctx->panel) + if (ctx->panel) { + drm_panel_prepare(ctx->panel); drm_panel_enable(ctx->panel); + } }
static void exynos_dpi_poweroff(struct exynos_dpi *ctx) { - if (ctx->panel) + if (ctx->panel) { drm_panel_disable(ctx->panel); + drm_panel_unprepare(ctx->panel); + } }
static void exynos_dpi_dpms(struct exynos_drm_display *display, int mode) diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c index 5e78d45..2f58e45 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c @@ -1333,6 +1333,12 @@ static int exynos_dsi_enable(struct exynos_dsi *dsi) if (ret < 0) return ret;
+ ret = drm_panel_prepare(dsi->panel); + if (ret < 0) { + exynos_dsi_poweroff(dsi); + return ret; + } + ret = drm_panel_enable(dsi->panel); if (ret < 0) { exynos_dsi_poweroff(dsi); @@ -1354,6 +1360,7 @@ static void exynos_dsi_disable(struct exynos_dsi *dsi)
exynos_dsi_set_display_enable(dsi, false); drm_panel_disable(dsi->panel); + drm_panel_unprepare(dsi->panel); exynos_dsi_poweroff(dsi);
dsi->state &= ~DSIM_STATE_ENABLED; diff --git a/drivers/gpu/drm/panel/panel-ld9040.c b/drivers/gpu/drm/panel/panel-ld9040.c index db1601f..046d9fe 100644 --- a/drivers/gpu/drm/panel/panel-ld9040.c +++ b/drivers/gpu/drm/panel/panel-ld9040.c @@ -214,7 +214,7 @@ static int ld9040_power_off(struct ld9040 *ctx) return regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies); }
-static int ld9040_disable(struct drm_panel *panel) +static int ld9040_unprepare(struct drm_panel *panel) { struct ld9040 *ctx = panel_to_ld9040(panel);
@@ -228,7 +228,12 @@ static int ld9040_disable(struct drm_panel *panel) return ld9040_power_off(ctx); }
-static int ld9040_enable(struct drm_panel *panel) +static int ld9040_disable(struct drm_panel *panel) +{ + return 0; +} + +static int ld9040_prepare(struct drm_panel *panel) { struct ld9040 *ctx = panel_to_ld9040(panel); int ret; @@ -242,11 +247,16 @@ static int ld9040_enable(struct drm_panel *panel) ret = ld9040_clear_error(ctx);
if (ret < 0) - ld9040_disable(panel); + ld9040_unprepare(panel);
return ret; }
+static int ld9040_enable(struct drm_panel *panel) +{ + return 0; +} + static int ld9040_get_modes(struct drm_panel *panel) { struct drm_connector *connector = panel->connector; @@ -272,7 +282,9 @@ static int ld9040_get_modes(struct drm_panel *panel) }
static const struct drm_panel_funcs ld9040_drm_funcs = { + .unprepare = ld9040_unprepare, .disable = ld9040_disable, + .prepare = ld9040_prepare, .enable = ld9040_enable, .get_modes = ld9040_get_modes, }; diff --git a/drivers/gpu/drm/panel/panel-s6e8aa0.c b/drivers/gpu/drm/panel/panel-s6e8aa0.c index 06e57a2..51c657a 100644 --- a/drivers/gpu/drm/panel/panel-s6e8aa0.c +++ b/drivers/gpu/drm/panel/panel-s6e8aa0.c @@ -887,7 +887,7 @@ static int s6e8aa0_power_off(struct s6e8aa0 *ctx) return regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies); }
-static int s6e8aa0_disable(struct drm_panel *panel) +static int s6e8aa0_unprepare(struct drm_panel *panel) { struct s6e8aa0 *ctx = panel_to_s6e8aa0(panel);
@@ -900,7 +900,12 @@ static int s6e8aa0_disable(struct drm_panel *panel) return s6e8aa0_power_off(ctx); }
-static int s6e8aa0_enable(struct drm_panel *panel) +static int s6e8aa0_disable(struct drm_panel *panel) +{ + return 0; +} + +static int s6e8aa0_prepare(struct drm_panel *panel) { struct s6e8aa0 *ctx = panel_to_s6e8aa0(panel); int ret; @@ -913,11 +918,16 @@ static int s6e8aa0_enable(struct drm_panel *panel) ret = ctx->error;
if (ret < 0) - s6e8aa0_disable(panel); + s6e8aa0_unprepare(panel);
return ret; }
+static int s6e8aa0_enable(struct drm_panel *panel) +{ + return 0; +} + static int s6e8aa0_get_modes(struct drm_panel *panel) { struct drm_connector *connector = panel->connector; @@ -943,7 +953,9 @@ static int s6e8aa0_get_modes(struct drm_panel *panel) }
static const struct drm_panel_funcs s6e8aa0_drm_funcs = { + .unprepare = s6e8aa0_unprepare, .disable = s6e8aa0_disable, + .prepare = s6e8aa0_prepare, .enable = s6e8aa0_enable, .get_modes = s6e8aa0_get_modes, }; diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index a251361..fb0cfe2 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -45,7 +45,8 @@ struct panel_desc {
struct panel_simple { struct drm_panel base; - bool enabled; + bool panel_enabled; + bool backlight_enabled;
const struct panel_desc *desc;
@@ -93,11 +94,28 @@ static int panel_simple_get_fixed_modes(struct panel_simple *panel) return num; }
+static int panel_simple_unprepare(struct drm_panel *panel) +{ + struct panel_simple *p = to_panel_simple(panel); + + if (!p->panel_enabled) + return 0; + + if (p->enable_gpio) + gpiod_set_value_cansleep(p->enable_gpio, 0); + + regulator_disable(p->supply); + + p->panel_enabled = false; + + return 0; +} + static int panel_simple_disable(struct drm_panel *panel) { struct panel_simple *p = to_panel_simple(panel);
- if (!p->enabled) + if (!p->backlight_enabled) return 0;
if (p->backlight) { @@ -105,21 +123,17 @@ static int panel_simple_disable(struct drm_panel *panel) backlight_update_status(p->backlight); }
- if (p->enable_gpio) - gpiod_set_value_cansleep(p->enable_gpio, 0); - - regulator_disable(p->supply); - p->enabled = false; + p->backlight_enabled = false;
return 0; }
-static int panel_simple_enable(struct drm_panel *panel) +static int panel_simple_prepare(struct drm_panel *panel) { struct panel_simple *p = to_panel_simple(panel); int err;
- if (p->enabled) + if (p->panel_enabled) return 0;
err = regulator_enable(p->supply); @@ -131,12 +145,24 @@ static int panel_simple_enable(struct drm_panel *panel) if (p->enable_gpio) gpiod_set_value_cansleep(p->enable_gpio, 1);
+ p->panel_enabled = true; + + return 0; +} + +static int panel_simple_enable(struct drm_panel *panel) +{ + struct panel_simple *p = to_panel_simple(panel); + + if (p->backlight_enabled) + return 0; + if (p->backlight) { p->backlight->props.power = FB_BLANK_UNBLANK; backlight_update_status(p->backlight); }
- p->enabled = true; + p->backlight_enabled = true;
return 0; } @@ -164,6 +190,8 @@ static int panel_simple_get_modes(struct drm_panel *panel)
static const struct drm_panel_funcs panel_simple_funcs = { .disable = panel_simple_disable, + .unprepare = panel_simple_unprepare, + .prepare = panel_simple_prepare, .enable = panel_simple_enable, .get_modes = panel_simple_get_modes, }; @@ -178,7 +206,8 @@ static int panel_simple_probe(struct device *dev, const struct panel_desc *desc) if (!panel) return -ENOMEM;
- panel->enabled = false; + panel->panel_enabled = false; + panel->backlight_enabled = false; panel->desc = desc;
panel->supply = devm_regulator_get(dev, "power"); diff --git a/drivers/gpu/drm/tegra/output.c b/drivers/gpu/drm/tegra/output.c index 446837e..b574ee6 100644 --- a/drivers/gpu/drm/tegra/output.c +++ b/drivers/gpu/drm/tegra/output.c @@ -139,9 +139,11 @@ static void tegra_encoder_dpms(struct drm_encoder *encoder, int mode)
if (mode != DRM_MODE_DPMS_ON) { drm_panel_disable(panel); + drm_panel_unprepare(panel); tegra_output_disable(output); } else { tegra_output_enable(output); + drm_panel_prepare(panel); drm_panel_enable(panel); } }
On Sat, Jul 26, 2014 at 12:52:04AM +0530, Ajay Kumar wrote:
Now that we have 2 new callbacks(prepare and unprepare) for drm_panel, make changes in all the drm drivers which use the drm_panel framework to support the new callbacks.
Signed-off-by: Ajay Kumar ajaykumar.rs@samsung.com
drivers/gpu/drm/exynos/exynos_drm_dpi.c | 8 +++-- drivers/gpu/drm/exynos/exynos_drm_dsi.c | 7 +++++ drivers/gpu/drm/panel/panel-ld9040.c | 18 +++++++++-- drivers/gpu/drm/panel/panel-s6e8aa0.c | 18 +++++++++-- drivers/gpu/drm/panel/panel-simple.c | 51 ++++++++++++++++++++++++------- drivers/gpu/drm/tegra/output.c | 2 ++ 6 files changed, 85 insertions(+), 19 deletions(-)
I'd prefer for this to be split up into patches per panel and per display driver. The reason is that if this patch is merged as-is, then if it's ever determined to cause a regression it'll be difficult to find out which change exactly caused this.
But to not break bisectability you'll need to be careful in how you break up the patches. I think the following should work:
- for each panel driver, implement dummy .prepare() and .unprepare() that return 0 - for each display driver, make use of drm_panel_prepare() and drm_panel_unprepare() - for each panel driver, properly implement .prepare() and .unprepare() (presumably by moving code out of .enable() and .disable(), respectively)
I have a couple more comments below about the ordering of the .prepare() vs. .enable() and .disable() vs. .unprepare() calls.
diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c index 5e78d45..2f58e45 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c @@ -1333,6 +1333,12 @@ static int exynos_dsi_enable(struct exynos_dsi *dsi) if (ret < 0) return ret;
- ret = drm_panel_prepare(dsi->panel);
- if (ret < 0) {
exynos_dsi_poweroff(dsi);
return ret;
- }
- ret = drm_panel_enable(dsi->panel); if (ret < 0) { exynos_dsi_poweroff(dsi);
@@ -1354,6 +1360,7 @@ static void exynos_dsi_disable(struct exynos_dsi *dsi)
exynos_dsi_set_display_enable(dsi, false); drm_panel_disable(dsi->panel);
- drm_panel_unprepare(dsi->panel); exynos_dsi_poweroff(dsi);
I don't know Exynos very well, so this may be completely wrong, but should disable_panel_prepare() be called much earlier than drm_panel_enable() and drm_panel_unprepare() much later than drm_panel_disable()? With the above the result is still the same, so you'll get the same glitches as without their separation.
Without knowing exactly what Exynos does in the above, I'd expect the correct sequence to be something like this:
ret = exynos_dsi_power_on(dsi); if (ret < 0) return ret;
ret = drm_panel_prepare(dsi->panel); if (ret < 0) { exynos_dsi_poweroff(dsi); return ret; }
exynos_dsi_set_display_mode(dsi); exynos_dsi_set_display_enable(dsi, true);
ret = drm_panel_enable(dsi->panel); if (ret < 0) { /* perhaps undo exynos_dsi_set_display_enable() here? */ exynos_dsi_poweroff(dsi); return ret; }
And for disable:
drm_panel_disable(dsi->panel); exynos_dsi_set_display_enable(dsi, false); drm_panel_unprepare(dsi->panel); exynos_dsi_poweroff(dsi);
Perhaps I should quote the kerneldoc that I added for drm_panel_funcs to underline why I think this is important:
/** * struct drm_panel_funcs - perform operations on a given panel * @disable: disable panel (turn off back light, etc.) * @unprepare: turn off panel * @prepare: turn on panel and perform set up * @enable: enable panel (turn on back light, etc.) * @get_modes: add modes to the connector that the panel is attached to and * return the number of modes added * * The .prepare() function is typically called before the display controller * starts to transmit video data. Panel drivers can use this to turn the panel * on and wait for it to become ready. If additional configuration is required * (via a control bus such as I2C, SPI or DSI for example) this is a good time * to do that. * * After the display controller has started transmitting video data, it's safe * to call the .enable() function. This will typically enable the backlight to * make the image on screen visible. Some panels require a certain amount of * time or frames before the image is displayed. This function is responsible * for taking this into account before enabling the backlight to avoid visual * glitches. * * Before stopping video transmission from the display controller it can be * necessary to turn off the panel to avoid visual glitches. This is done in * the .disable() function. Analogously to .enable() this typically involves * turning off the backlight and waiting for some time to make sure no image * is visible on the panel. It is then safe for the display controller to * cease transmission of video data. * * To save power when no video data is transmitted, a driver can power down * the panel. This is the job of the .unprepare() function. */
As you see, .prepare() should do whatever is necessary to make the panel accept a stream of video data, then the display driver can start sending video data and call .enable() to make the transmitted data visible.
Analogously .disable() should turn off the display, so that the user can no longer see what's going on, then the display controller can cease transmission of video data (and since the panel is disabled this should no longer cause visual glitches) and then call .unprepare() to turn the panel off.
I know that this isn't immediately relevant just yet because currently the backlight will already turn on by default, but like we discussed earlier I have ideas on how to properly fix it. As a matter of fact I'll go and send out another mail about that when I'm through this series.
static const struct drm_panel_funcs ld9040_drm_funcs = {
- .unprepare = ld9040_unprepare, .disable = ld9040_disable,
- .prepare = ld9040_prepare, .enable = ld9040_enable, .get_modes = ld9040_get_modes,
};
The patch I applied for .prepare() and .unprepare() I've reordered the functions slightly to give a more natural sequence. .disable() is now first, while .unprepare() is next, since that's the sequence that they should be called in.
Patches for the panel drivers should follow this same ordering.
diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index a251361..fb0cfe2 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -45,7 +45,8 @@ struct panel_desc {
struct panel_simple { struct drm_panel base;
- bool enabled;
- bool panel_enabled;
- bool backlight_enabled;
Perhaps this should rather be:
bool prepared; bool enabled;
?
diff --git a/drivers/gpu/drm/tegra/output.c b/drivers/gpu/drm/tegra/output.c index 446837e..b574ee6 100644 --- a/drivers/gpu/drm/tegra/output.c +++ b/drivers/gpu/drm/tegra/output.c @@ -139,9 +139,11 @@ static void tegra_encoder_dpms(struct drm_encoder *encoder, int mode)
if (mode != DRM_MODE_DPMS_ON) { drm_panel_disable(panel);
tegra_output_disable(output);drm_panel_unprepare(panel);
Similarly to my comments for the Exynos drivers, this should be:
drm_panel_disable(panel); tegra_output_disable(output); drm_panel_unprepare(panel);
} else { tegra_output_enable(output);
drm_panel_enable(panel); }drm_panel_prepare(panel);
and
drm_panel_prepare(panel); tegra_output_enable(output); drm_panel_enable(panel);
Thierry
On Wed, Jul 30, 2014 at 4:02 PM, Thierry Reding thierry.reding@gmail.com wrote:
On Sat, Jul 26, 2014 at 12:52:04AM +0530, Ajay Kumar wrote:
Now that we have 2 new callbacks(prepare and unprepare) for drm_panel, make changes in all the drm drivers which use the drm_panel framework to support the new callbacks.
Signed-off-by: Ajay Kumar ajaykumar.rs@samsung.com
drivers/gpu/drm/exynos/exynos_drm_dpi.c | 8 +++-- drivers/gpu/drm/exynos/exynos_drm_dsi.c | 7 +++++ drivers/gpu/drm/panel/panel-ld9040.c | 18 +++++++++-- drivers/gpu/drm/panel/panel-s6e8aa0.c | 18 +++++++++-- drivers/gpu/drm/panel/panel-simple.c | 51 ++++++++++++++++++++++++------- drivers/gpu/drm/tegra/output.c | 2 ++ 6 files changed, 85 insertions(+), 19 deletions(-)
I'd prefer for this to be split up into patches per panel and per display driver. The reason is that if this patch is merged as-is, then if it's ever determined to cause a regression it'll be difficult to find out which change exactly caused this.
But to not break bisectability you'll need to be careful in how you break up the patches. I think the following should work:
- for each panel driver, implement dummy .prepare() and .unprepare() that return 0 - for each display driver, make use of drm_panel_prepare() and drm_panel_unprepare() - for each panel driver, properly implement .prepare() and .unprepare() (presumably by moving code out of .enable() and .disable(), respectively)
I will try this. With this approach, compilation should not fail.
I have a couple more comments below about the ordering of the .prepare() vs. .enable() and .disable() vs. .unprepare() calls.
diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c index 5e78d45..2f58e45 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c @@ -1333,6 +1333,12 @@ static int exynos_dsi_enable(struct exynos_dsi *dsi) if (ret < 0) return ret;
ret = drm_panel_prepare(dsi->panel);
if (ret < 0) {
exynos_dsi_poweroff(dsi);
return ret;
}
ret = drm_panel_enable(dsi->panel); if (ret < 0) { exynos_dsi_poweroff(dsi);
@@ -1354,6 +1360,7 @@ static void exynos_dsi_disable(struct exynos_dsi *dsi)
exynos_dsi_set_display_enable(dsi, false); drm_panel_disable(dsi->panel);
drm_panel_unprepare(dsi->panel); exynos_dsi_poweroff(dsi);
I don't know Exynos very well, so this may be completely wrong, but should disable_panel_prepare() be called much earlier than drm_panel_enable() and drm_panel_unprepare() much later than drm_panel_disable()? With the above the result is still the same, so you'll get the same glitches as without their separation.
Actually, I have not worked on DSI panels. And till now, these DSI panels have been working with just the enable/disable callback. So, I thought it might not really cause a glitch/garbage on the screen. I just placed the new panel calls so that basic working will not be broken. It would be great if someone can test this on exynos boards with DSI panels :( Inki/Andrej?
Anyways, now there is a kerneldoc which explains all these calls, I will rearrange the panel calls.
Without knowing exactly what Exynos does in the above, I'd expect the correct sequence to be something like this:
ret = exynos_dsi_power_on(dsi); if (ret < 0) return ret; ret = drm_panel_prepare(dsi->panel); if (ret < 0) { exynos_dsi_poweroff(dsi); return ret; } exynos_dsi_set_display_mode(dsi); exynos_dsi_set_display_enable(dsi, true); ret = drm_panel_enable(dsi->panel); if (ret < 0) { /* perhaps undo exynos_dsi_set_display_enable() here? */ exynos_dsi_poweroff(dsi); return ret; }
And for disable:
drm_panel_disable(dsi->panel); exynos_dsi_set_display_enable(dsi, false); drm_panel_unprepare(dsi->panel); exynos_dsi_poweroff(dsi);
Perhaps I should quote the kerneldoc that I added for drm_panel_funcs to underline why I think this is important:
/**
- struct drm_panel_funcs - perform operations on a given panel
- @disable: disable panel (turn off back light, etc.)
- @unprepare: turn off panel
- @prepare: turn on panel and perform set up
- @enable: enable panel (turn on back light, etc.)
- @get_modes: add modes to the connector that the panel is attached to and
- return the number of modes added
- The .prepare() function is typically called before the display controller
- starts to transmit video data. Panel drivers can use this to turn the panel
- on and wait for it to become ready. If additional configuration is required
- (via a control bus such as I2C, SPI or DSI for example) this is a good time
- to do that.
- After the display controller has started transmitting video data, it's safe
- to call the .enable() function. This will typically enable the backlight to
- make the image on screen visible. Some panels require a certain amount of
- time or frames before the image is displayed. This function is responsible
- for taking this into account before enabling the backlight to avoid visual
- glitches.
- Before stopping video transmission from the display controller it can be
- necessary to turn off the panel to avoid visual glitches. This is done in
- the .disable() function. Analogously to .enable() this typically involves
- turning off the backlight and waiting for some time to make sure no image
- is visible on the panel. It is then safe for the display controller to
- cease transmission of video data.
- To save power when no video data is transmitted, a driver can power down
- the panel. This is the job of the .unprepare() function.
*/
As you see, .prepare() should do whatever is necessary to make the panel accept a stream of video data, then the display driver can start sending video data and call .enable() to make the transmitted data visible.
Analogously .disable() should turn off the display, so that the user can no longer see what's going on, then the display controller can cease transmission of video data (and since the panel is disabled this should no longer cause visual glitches) and then call .unprepare() to turn the panel off.
I know that this isn't immediately relevant just yet because currently the backlight will already turn on by default, but like we discussed earlier I have ideas on how to properly fix it. As a matter of fact I'll go and send out another mail about that when I'm through this series.
static const struct drm_panel_funcs ld9040_drm_funcs = {
.unprepare = ld9040_unprepare, .disable = ld9040_disable,
.prepare = ld9040_prepare, .enable = ld9040_enable, .get_modes = ld9040_get_modes,
};
The patch I applied for .prepare() and .unprepare() I've reordered the functions slightly to give a more natural sequence. .disable() is now first, while .unprepare() is next, since that's the sequence that they should be called in.
Patches for the panel drivers should follow this same ordering.
Ok. I will follow the same order for all panel drivers.
diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index a251361..fb0cfe2 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -45,7 +45,8 @@ struct panel_desc {
struct panel_simple { struct drm_panel base;
bool enabled;
bool panel_enabled;
bool backlight_enabled;
Perhaps this should rather be:
bool prepared; bool enabled;
More generic. Will change it!
Ajay
diff --git a/drivers/gpu/drm/tegra/output.c b/drivers/gpu/drm/tegra/output.c index 446837e..b574ee6 100644 --- a/drivers/gpu/drm/tegra/output.c +++ b/drivers/gpu/drm/tegra/output.c @@ -139,9 +139,11 @@ static void tegra_encoder_dpms(struct drm_encoder *encoder, int mode)
if (mode != DRM_MODE_DPMS_ON) { drm_panel_disable(panel);
drm_panel_unprepare(panel); tegra_output_disable(output);
Similarly to my comments for the Exynos drivers, this should be:
drm_panel_disable(panel); tegra_output_disable(output); drm_panel_unprepare(panel);
} else { tegra_output_enable(output);
drm_panel_prepare(panel); drm_panel_enable(panel); }
and
drm_panel_prepare(panel); tegra_output_enable(output); drm_panel_enable(panel);
Thierry
Add panel_desc structure for auo_b133htn01 eDP panel.
Also, modify the panel_simple routines to support timing_parameter delays if mentioned in the panel_desc structure.
Signed-off-by: Ajay Kumar ajaykumar.rs@samsung.com --- .../devicetree/bindings/panel/auo,b133htn01.txt | 7 +++ drivers/gpu/drm/panel/panel-simple.c | 47 ++++++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 Documentation/devicetree/bindings/panel/auo,b133htn01.txt
diff --git a/Documentation/devicetree/bindings/panel/auo,b133htn01.txt b/Documentation/devicetree/bindings/panel/auo,b133htn01.txt new file mode 100644 index 0000000..302226b --- /dev/null +++ b/Documentation/devicetree/bindings/panel/auo,b133htn01.txt @@ -0,0 +1,7 @@ +AU Optronics Corporation 13.3" FHD (1920x1080) color TFT-LCD panel + +Required properties: +- compatible: should be "auo,b133htn01" + +This binding is compatible with the simple-panel binding, which is specified +in simple-panel.txt in this directory. diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index fb0cfe2..cbbb1b8 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -41,6 +41,13 @@ struct panel_desc { unsigned int width; unsigned int height; } size; + + struct { + unsigned int prepare_stage_delay; + unsigned int enable_stage_delay; + unsigned int disable_stage_delay; + unsigned int unprepare_stage_delay; + } timing_parameter; };
struct panel_simple { @@ -105,6 +112,8 @@ static int panel_simple_unprepare(struct drm_panel *panel) gpiod_set_value_cansleep(p->enable_gpio, 0);
regulator_disable(p->supply); + if (p->desc) + msleep(p->desc->timing_parameter.unprepare_stage_delay);
p->panel_enabled = false;
@@ -123,6 +132,9 @@ static int panel_simple_disable(struct drm_panel *panel) backlight_update_status(p->backlight); }
+ if (p->desc) + msleep(p->desc->timing_parameter.disable_stage_delay); + p->backlight_enabled = false;
return 0; @@ -142,6 +154,9 @@ static int panel_simple_prepare(struct drm_panel *panel) return err; }
+ if (p->desc) + msleep(p->desc->timing_parameter.prepare_stage_delay); + if (p->enable_gpio) gpiod_set_value_cansleep(p->enable_gpio, 1);
@@ -157,6 +172,8 @@ static int panel_simple_enable(struct drm_panel *panel) if (p->backlight_enabled) return 0;
+ if (p->desc) + msleep(p->desc->timing_parameter.enable_stage_delay); if (p->backlight) { p->backlight->props.power = FB_BLANK_UNBLANK; backlight_update_status(p->backlight); @@ -342,6 +359,33 @@ static const struct panel_desc auo_b133xtn01 = { }, };
+static const struct drm_display_mode auo_b133htn01_mode = { + .clock = 150660, + .hdisplay = 1920, + .hsync_start = 1920 + 172, + .hsync_end = 1920 + 172 + 80, + .htotal = 1920 + 172 + 80 + 60, + .vdisplay = 1080, + .vsync_start = 1080 + 25, + .vsync_end = 1080 + 25 + 10, + .vtotal = 1080 + 25 + 10 + 10, + .vrefresh = 60, +}; + +static const struct panel_desc auo_b133htn01 = { + .modes = &auo_b133htn01_mode, + .num_modes = 1, + .size = { + .width = 293, + .height = 165, + }, + .timing_parameter = { + .prepare_stage_delay = 105, + .enable_stage_delay = 20, + .prepare_stage_delay = 50, + }, +}; + static const struct drm_display_mode chunghwa_claa101wa01a_mode = { .clock = 72070, .hdisplay = 1366, @@ -481,6 +525,9 @@ static const struct of_device_id platform_of_match[] = { .compatible = "auo,b101aw03", .data = &auo_b101aw03, }, { + .compatible = "auo,b133htn01", + .data = &auo_b133htn01, + }, { .compatible = "auo,b133xtn01", .data = &auo_b133xtn01, }, {
On Sat, Jul 26, 2014 at 12:52:05AM +0530, Ajay Kumar wrote:
Add panel_desc structure for auo_b133htn01 eDP panel.
Also, modify the panel_simple routines to support timing_parameter delays if mentioned in the panel_desc structure.
Signed-off-by: Ajay Kumar ajaykumar.rs@samsung.com
.../devicetree/bindings/panel/auo,b133htn01.txt | 7 +++ drivers/gpu/drm/panel/panel-simple.c | 47 ++++++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 Documentation/devicetree/bindings/panel/auo,b133htn01.txt
I think this should be two patches, one which adds the delay parameters and another which adds support for the new panel.
diff --git a/Documentation/devicetree/bindings/panel/auo,b133htn01.txt b/Documentation/devicetree/bindings/panel/auo,b133htn01.txt new file mode 100644 index 0000000..302226b --- /dev/null +++ b/Documentation/devicetree/bindings/panel/auo,b133htn01.txt @@ -0,0 +1,7 @@ +AU Optronics Corporation 13.3" FHD (1920x1080) color TFT-LCD panel
+Required properties: +- compatible: should be "auo,b133htn01"
+This binding is compatible with the simple-panel binding, which is specified +in simple-panel.txt in this directory. diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index fb0cfe2..cbbb1b8 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -41,6 +41,13 @@ struct panel_desc { unsigned int width; unsigned int height; } size;
- struct {
unsigned int prepare_stage_delay;
unsigned int enable_stage_delay;
unsigned int disable_stage_delay;
unsigned int unprepare_stage_delay;
- } timing_parameter;
These are overly long in my opinion, how about:
struct { unsigned int prepare; unsigned int enable; unsigned int disable; unsigned int unprepare; } delay;
? Oh, and this should probably mention that it's in milliseconds. On that note, do you think we'll ever need microsecond resolution? I don't think I've ever seen a panel datasheet mentioning that kind of granularity.
struct panel_simple { @@ -105,6 +112,8 @@ static int panel_simple_unprepare(struct drm_panel *panel) gpiod_set_value_cansleep(p->enable_gpio, 0);
regulator_disable(p->supply);
- if (p->desc)
Should have a blank line between "regulator_disable(...)" and "if ...". Also it's not useful to check for p->desc, really, since it's a bug if that is NULL.
However I think it would be good to check for the delay being set, like so:
if (p->desc->delay.unprepare) msleep(p->desc->delay.unprepare);
I'm not sure about the placement of the delay here, see below for more.
@@ -142,6 +154,9 @@ static int panel_simple_prepare(struct drm_panel *panel) return err; }
- if (p->desc)
msleep(p->desc->timing_parameter.prepare_stage_delay);
- if (p->enable_gpio) gpiod_set_value_cansleep(p->enable_gpio, 1);
Should the delay not be *after* all steps in prepare have been performed? That way drivers can use it to specify the time that a panel needs to "internally" become ready after they've been power up (for example).
@@ -157,6 +172,8 @@ static int panel_simple_enable(struct drm_panel *panel) if (p->backlight_enabled) return 0;
- if (p->desc)
if (p->backlight) { p->backlight->props.power = FB_BLANK_UNBLANK; backlight_update_status(p->backlight);msleep(p->desc->timing_parameter.enable_stage_delay);
I think here the delay makes sense where it is because it allows the panel driver to wait for a given amount of time (after video data has been sent by the display controller) until the first "good" frame is visible.
In general I think it would be good to have a description of these in the struct panel_desc structure, something like this perhaps:
/** * @prepare: the time (in milliseconds) that it takes for the panel * to become ready and start receiving video data * @enable: the time (in milliseconds) that it takes for the panel * to display the first valid frame after starting to * receive video data * @disable: the time (in milliseconds) that it takes for the panel * to turn the display off (no content is visible) * @unprepare: ??? */ struct { unsigned int prepare; unsigned int enable; unsigned int disable; unsigned int unprepare; } delay;
For prepare and enable delays this would mean that they should take effect at the very end of the .prepare() and .enable() functions, respectively. For disable in means that it should be at the end (for example to take into account the time it takes for backlight to completely turn off). For unprepare I have no idea what we would need it for. And the new panel that you're adding in this patch doesn't use it either, so perhaps it can just be left out (for now)?
+static const struct panel_desc auo_b133htn01 = {
- .modes = &auo_b133htn01_mode,
- .num_modes = 1,
- .size = {
.width = 293,
.height = 165,
- },
- .timing_parameter = {
.prepare_stage_delay = 105,
.enable_stage_delay = 20,
.prepare_stage_delay = 50,
I take it that this last one was supposed to be .enable_stage_delay since you've already set up .prepare_stage_delay.
Thierry
On Wed, Jul 30, 2014 at 4:21 PM, Thierry Reding thierry.reding@gmail.com wrote:
On Sat, Jul 26, 2014 at 12:52:05AM +0530, Ajay Kumar wrote:
Add panel_desc structure for auo_b133htn01 eDP panel.
Also, modify the panel_simple routines to support timing_parameter delays if mentioned in the panel_desc structure.
Signed-off-by: Ajay Kumar ajaykumar.rs@samsung.com
.../devicetree/bindings/panel/auo,b133htn01.txt | 7 +++ drivers/gpu/drm/panel/panel-simple.c | 47 ++++++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 Documentation/devicetree/bindings/panel/auo,b133htn01.txt
I think this should be two patches, one which adds the delay parameters and another which adds support for the new panel.
Ok. Will split it.
diff --git a/Documentation/devicetree/bindings/panel/auo,b133htn01.txt b/Documentation/devicetree/bindings/panel/auo,b133htn01.txt new file mode 100644 index 0000000..302226b --- /dev/null +++ b/Documentation/devicetree/bindings/panel/auo,b133htn01.txt @@ -0,0 +1,7 @@ +AU Optronics Corporation 13.3" FHD (1920x1080) color TFT-LCD panel
+Required properties: +- compatible: should be "auo,b133htn01"
+This binding is compatible with the simple-panel binding, which is specified +in simple-panel.txt in this directory. diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index fb0cfe2..cbbb1b8 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -41,6 +41,13 @@ struct panel_desc { unsigned int width; unsigned int height; } size;
struct {
unsigned int prepare_stage_delay;
unsigned int enable_stage_delay;
unsigned int disable_stage_delay;
unsigned int unprepare_stage_delay;
} timing_parameter;
These are overly long in my opinion, how about:
struct { unsigned int prepare; unsigned int enable; unsigned int disable; unsigned int unprepare; } delay;
Ok, will use this.
? Oh, and this should probably mention that it's in milliseconds. On that note, do you think we'll ever need microsecond resolution? I don't think I've ever seen a panel datasheet mentioning that kind of granularity.
Nope. All in milliseconds.
struct panel_simple { @@ -105,6 +112,8 @@ static int panel_simple_unprepare(struct drm_panel *panel) gpiod_set_value_cansleep(p->enable_gpio, 0);
regulator_disable(p->supply);
if (p->desc)
Should have a blank line between "regulator_disable(...)" and "if ...". Also it's not useful to check for p->desc, really, since it's a bug if that is NULL.
Well, I added this check because I used just the "simple-panel" compatible for panel node on snow. This check won't be needed anymore.
However I think it would be good to check for the delay being set, like so:
if (p->desc->delay.unprepare) msleep(p->desc->delay.unprepare);
Ok, will change it.
I'm not sure about the placement of the delay here, see below for more.
@@ -142,6 +154,9 @@ static int panel_simple_prepare(struct drm_panel *panel) return err; }
if (p->desc)
msleep(p->desc->timing_parameter.prepare_stage_delay);
if (p->enable_gpio) gpiod_set_value_cansleep(p->enable_gpio, 1);
Should the delay not be *after* all steps in prepare have been performed? That way drivers can use it to specify the time that a panel needs to "internally" become ready after they've been power up (for example).
Right. I will move that delay down.
@@ -157,6 +172,8 @@ static int panel_simple_enable(struct drm_panel *panel) if (p->backlight_enabled) return 0;
if (p->desc)
msleep(p->desc->timing_parameter.enable_stage_delay); if (p->backlight) { p->backlight->props.power = FB_BLANK_UNBLANK; backlight_update_status(p->backlight);
I think here the delay makes sense where it is because it allows the panel driver to wait for a given amount of time (after video data has been sent by the display controller) until the first "good" frame is visible.
Exactly!
In general I think it would be good to have a description of these in the struct panel_desc structure, something like this perhaps:
/** * @prepare: the time (in milliseconds) that it takes for the panel * to become ready and start receiving video data * @enable: the time (in milliseconds) that it takes for the panel * to display the first valid frame after starting to * receive video data * @disable: the time (in milliseconds) that it takes for the panel * to turn the display off (no content is visible) * @unprepare: the time (in milliseconds) that it takes for the panel
to power down itself completely.
*/ struct { unsigned int prepare; unsigned int enable; unsigned int disable; unsigned int unprepare; } delay;
For prepare and enable delays this would mean that they should take effect at the very end of the .prepare() and .enable() functions, respectively. For disable in means that it should be at the end (for example to take into account the time it takes for backlight to completely turn off). For unprepare I have no idea what we would need it for. And the new panel that you're adding in this patch doesn't use it either, so perhaps it can just be left out (for now)?
Actually, there was a typo. That should have been .unprepare_stage_delay = 50. This is needed because panels need some delay before powering them on again. As in, assume you are doing a test to turn on/off display continuously, Then, the delay between (N - 1)th cycle poweroff to Nth cycle poweron should be at least 500ms. That's what the datasheet says! And, somehow 50ms works fine for me.
Ajay
+static const struct panel_desc auo_b133htn01 = {
.modes = &auo_b133htn01_mode,
.num_modes = 1,
.size = {
.width = 293,
.height = 165,
},
.timing_parameter = {
.prepare_stage_delay = 105,
.enable_stage_delay = 20,
.prepare_stage_delay = 50,
I take it that this last one was supposed to be .enable_stage_delay since you've already set up .prepare_stage_delay.
Thierry
On Wed, Jul 30, 2014 at 05:02:11PM +0530, Ajay kumar wrote:
On Wed, Jul 30, 2014 at 4:21 PM, Thierry Reding thierry.reding@gmail.com wrote:
On Sat, Jul 26, 2014 at 12:52:05AM +0530, Ajay Kumar wrote:
Add panel_desc structure for auo_b133htn01 eDP panel.
Also, modify the panel_simple routines to support timing_parameter delays if mentioned in the panel_desc structure.
Signed-off-by: Ajay Kumar ajaykumar.rs@samsung.com
.../devicetree/bindings/panel/auo,b133htn01.txt | 7 +++ drivers/gpu/drm/panel/panel-simple.c | 47 ++++++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 Documentation/devicetree/bindings/panel/auo,b133htn01.txt
I think this should be two patches, one which adds the delay parameters and another which adds support for the new panel.
Ok. Will split it.
I was going to propose that I split this up myself so you no longer have to worry about it. I can easily apply the changes we discussed. But if you prefer to keep it in one series that works for me too.
In general I think it would be good to have a description of these in the struct panel_desc structure, something like this perhaps:
/** * @prepare: the time (in milliseconds) that it takes for the panel * to become ready and start receiving video data * @enable: the time (in milliseconds) that it takes for the panel * to display the first valid frame after starting to * receive video data * @disable: the time (in milliseconds) that it takes for the panel * to turn the display off (no content is visible) * @unprepare: the time (in milliseconds) that it takes for the panel
to power down itself completely.
*/ struct { unsigned int prepare; unsigned int enable; unsigned int disable; unsigned int unprepare; } delay;
For prepare and enable delays this would mean that they should take effect at the very end of the .prepare() and .enable() functions, respectively. For disable in means that it should be at the end (for example to take into account the time it takes for backlight to completely turn off). For unprepare I have no idea what we would need it for. And the new panel that you're adding in this patch doesn't use it either, so perhaps it can just be left out (for now)?
Actually, there was a typo. That should have been .unprepare_stage_delay = 50. This is needed because panels need some delay before powering them on again. As in, assume you are doing a test to turn on/off display continuously, Then, the delay between (N - 1)th cycle poweroff to Nth cycle poweron should be at least 500ms. That's what the datasheet says! And, somehow 50ms works fine for me.
Okay, that makes sense then.
Thierry
On Wed, Jul 30, 2014 at 7:00 PM, Thierry Reding thierry.reding@gmail.com wrote:
On Wed, Jul 30, 2014 at 05:02:11PM +0530, Ajay kumar wrote:
On Wed, Jul 30, 2014 at 4:21 PM, Thierry Reding thierry.reding@gmail.com wrote:
On Sat, Jul 26, 2014 at 12:52:05AM +0530, Ajay Kumar wrote:
Add panel_desc structure for auo_b133htn01 eDP panel.
Also, modify the panel_simple routines to support timing_parameter delays if mentioned in the panel_desc structure.
Signed-off-by: Ajay Kumar ajaykumar.rs@samsung.com
.../devicetree/bindings/panel/auo,b133htn01.txt | 7 +++ drivers/gpu/drm/panel/panel-simple.c | 47 ++++++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 Documentation/devicetree/bindings/panel/auo,b133htn01.txt
I think this should be two patches, one which adds the delay parameters and another which adds support for the new panel.
Ok. Will split it.
I was going to propose that I split this up myself so you no longer have to worry about it. I can easily apply the changes we discussed. But if you prefer to keep it in one series that works for me too.
Well, you can only take in the "delay" structure as of now.
Ajay
In general I think it would be good to have a description of these in the struct panel_desc structure, something like this perhaps:
/** * @prepare: the time (in milliseconds) that it takes for the panel * to become ready and start receiving video data * @enable: the time (in milliseconds) that it takes for the panel * to display the first valid frame after starting to * receive video data * @disable: the time (in milliseconds) that it takes for the panel * to turn the display off (no content is visible) * @unprepare: the time (in milliseconds) that it takes for the panel
to power down itself completely.
*/ struct { unsigned int prepare; unsigned int enable; unsigned int disable; unsigned int unprepare; } delay;
For prepare and enable delays this would mean that they should take effect at the very end of the .prepare() and .enable() functions, respectively. For disable in means that it should be at the end (for example to take into account the time it takes for backlight to completely turn off). For unprepare I have no idea what we would need it for. And the new panel that you're adding in this patch doesn't use it either, so perhaps it can just be left out (for now)?
Actually, there was a typo. That should have been .unprepare_stage_delay = 50. This is needed because panels need some delay before powering them on again. As in, assume you are doing a test to turn on/off display continuously, Then, the delay between (N - 1)th cycle poweroff to Nth cycle poweron should be at least 500ms. That's what the datasheet says! And, somehow 50ms works fine for me.
Okay, that makes sense then.
Thierry
This patch moves the DP training and video enable from the hotplug handler into commit().
Signed-off-by: Sean Paul seanpaul@chromium.org Signed-off-by: Ajay Kumar ajaykumar.rs@samsung.com --- drivers/gpu/drm/exynos/exynos_dp_core.c | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-)
diff --git a/drivers/gpu/drm/exynos/exynos_dp_core.c b/drivers/gpu/drm/exynos/exynos_dp_core.c index 845d766..4b6ad95 100644 --- a/drivers/gpu/drm/exynos/exynos_dp_core.c +++ b/drivers/gpu/drm/exynos/exynos_dp_core.c @@ -875,10 +875,18 @@ static irqreturn_t exynos_dp_irq_handler(int irq, void *arg) static void exynos_dp_hotplug(struct work_struct *work) { struct exynos_dp_device *dp; - int ret;
dp = container_of(work, struct exynos_dp_device, hotplug_work);
+ if (dp->drm_dev) + drm_helper_hpd_irq_event(dp->drm_dev); +} + +static void exynos_dp_commit(struct exynos_drm_display *display) +{ + struct exynos_dp_device *dp = display->ctx; + int ret; + ret = exynos_dp_detect_hpd(dp); if (ret) { /* Cable has been disconnected, we're done */ @@ -1050,8 +1058,10 @@ static void exynos_dp_phy_exit(struct exynos_dp_device *dp) } }
-static void exynos_dp_poweron(struct exynos_dp_device *dp) +static void exynos_dp_poweron(struct exynos_drm_display *display) { + struct exynos_dp_device *dp = display->ctx; + if (dp->dpms_mode == DRM_MODE_DPMS_ON) return;
@@ -1059,10 +1069,13 @@ static void exynos_dp_poweron(struct exynos_dp_device *dp) exynos_dp_phy_init(dp); exynos_dp_init_dp(dp); enable_irq(dp->irq); + exynos_dp_commit(display); }
-static void exynos_dp_poweroff(struct exynos_dp_device *dp) +static void exynos_dp_poweroff(struct exynos_drm_display *display) { + struct exynos_dp_device *dp = display->ctx; + if (dp->dpms_mode != DRM_MODE_DPMS_ON) return;
@@ -1078,12 +1091,12 @@ static void exynos_dp_dpms(struct exynos_drm_display *display, int mode)
switch (mode) { case DRM_MODE_DPMS_ON: - exynos_dp_poweron(dp); + exynos_dp_poweron(display); break; case DRM_MODE_DPMS_STANDBY: case DRM_MODE_DPMS_SUSPEND: case DRM_MODE_DPMS_OFF: - exynos_dp_poweroff(dp); + exynos_dp_poweroff(display); break; default: break; @@ -1094,6 +1107,7 @@ static void exynos_dp_dpms(struct exynos_drm_display *display, int mode) static struct exynos_drm_display_ops exynos_dp_display_ops = { .create_connector = exynos_dp_create_connector, .dpms = exynos_dp_dpms, + .commit = exynos_dp_commit, };
static struct exynos_drm_display exynos_dp_display = {
On Sat, Jul 26, 2014 at 12:52:06AM +0530, Ajay Kumar wrote:
This patch moves the DP training and video enable from the hotplug handler into commit().
I don't think I can comment on this one, but perhaps the commit message should describe why it's doing this rather than just parroting what the code does.
Thierry
On Wed, Jul 30, 2014 at 4:22 PM, Thierry Reding thierry.reding@gmail.com wrote:
On Sat, Jul 26, 2014 at 12:52:06AM +0530, Ajay Kumar wrote:
This patch moves the DP training and video enable from the hotplug handler into commit().
I don't think I can comment on this one, but perhaps the commit message should describe why it's doing this rather than just parroting what the code does.
Actually, this patch adds the missing commit callback for dp encoder. I will add proper commit message.
Ajay
Add drm_panel controls to support powerup/down of the eDP panel, if one is present at the sink side.
Signed-off-by: Ajay Kumar ajaykumar.rs@samsung.com --- drivers/gpu/drm/exynos/Kconfig | 1 + drivers/gpu/drm/exynos/exynos_dp_core.c | 88 ++++++++++++++++++++++++------- drivers/gpu/drm/exynos/exynos_dp_core.h | 3 +- 3 files changed, 71 insertions(+), 21 deletions(-)
diff --git a/drivers/gpu/drm/exynos/Kconfig b/drivers/gpu/drm/exynos/Kconfig index 9ba1aae..7f9f6f9 100644 --- a/drivers/gpu/drm/exynos/Kconfig +++ b/drivers/gpu/drm/exynos/Kconfig @@ -53,6 +53,7 @@ config DRM_EXYNOS_DP bool "EXYNOS DRM DP driver support" depends on DRM_EXYNOS_FIMD && ARCH_EXYNOS && (DRM_PTN3460=n || DRM_PTN3460=y || DRM_PTN3460=DRM_EXYNOS) default DRM_EXYNOS + select DRM_PANEL help This enables support for DP device.
diff --git a/drivers/gpu/drm/exynos/exynos_dp_core.c b/drivers/gpu/drm/exynos/exynos_dp_core.c index 4b6ad95..49356cc 100644 --- a/drivers/gpu/drm/exynos/exynos_dp_core.c +++ b/drivers/gpu/drm/exynos/exynos_dp_core.c @@ -16,7 +16,6 @@ #include <linux/clk.h> #include <linux/io.h> #include <linux/interrupt.h> -#include <linux/delay.h> #include <linux/of.h> #include <linux/of_gpio.h> #include <linux/gpio.h> @@ -28,6 +27,7 @@ #include <drm/drmP.h> #include <drm/drm_crtc.h> #include <drm/drm_crtc_helper.h> +#include <drm/drm_panel.h> #include <drm/bridge/ptn3460.h>
#include "exynos_drm_drv.h" @@ -41,7 +41,7 @@ struct bridge_init { struct device_node *node; };
-static int exynos_dp_init_dp(struct exynos_dp_device *dp) +static void exynos_dp_init_dp(struct exynos_dp_device *dp) { exynos_dp_reset(dp);
@@ -58,8 +58,6 @@ static int exynos_dp_init_dp(struct exynos_dp_device *dp)
exynos_dp_init_hpd(dp); exynos_dp_init_aux(dp); - - return 0; }
static int exynos_dp_detect_hpd(struct exynos_dp_device *dp) @@ -887,6 +885,12 @@ static void exynos_dp_commit(struct exynos_drm_display *display) struct exynos_dp_device *dp = display->ctx; int ret;
+ /* Keep backlight disabled while we configure video */ + if (dp->panel) { + if (drm_panel_disable(dp->panel)) + DRM_ERROR("failed to disable panel backlight\n"); + } + ret = exynos_dp_detect_hpd(dp); if (ret) { /* Cable has been disconnected, we're done */ @@ -917,6 +921,12 @@ static void exynos_dp_commit(struct exynos_drm_display *display) ret = exynos_dp_config_video(dp); if (ret) dev_err(dp->dev, "unable to config video\n"); + + /* Safe to turn on backlight now */ + if (dp->panel) { + if (drm_panel_enable(dp->panel)) + DRM_ERROR("failed to enable panel backlight\n"); + } }
static enum drm_connector_status exynos_dp_detect( @@ -941,15 +951,18 @@ static int exynos_dp_get_modes(struct drm_connector *connector) struct exynos_dp_device *dp = ctx_from_connector(connector); struct drm_display_mode *mode;
+ if (dp->panel) + return drm_panel_get_modes(dp->panel); + mode = drm_mode_create(connector->dev); if (!mode) { DRM_ERROR("failed to create a new display mode.\n"); return 0; }
- drm_display_mode_from_videomode(&dp->panel.vm, mode); - mode->width_mm = dp->panel.width_mm; - mode->height_mm = dp->panel.height_mm; + drm_display_mode_from_videomode(&dp->priv.vm, mode); + mode->width_mm = dp->priv.width_mm; + mode->height_mm = dp->priv.height_mm; connector->display_info.width_mm = mode->width_mm; connector->display_info.height_mm = mode->height_mm;
@@ -1029,7 +1042,10 @@ static int exynos_dp_create_connector(struct exynos_drm_display *display, drm_connector_register(connector); drm_mode_connector_attach_encoder(connector, encoder);
- return 0; + if (dp->panel) + ret = drm_panel_attach(dp->panel, &dp->connector); + + return ret; }
static void exynos_dp_phy_init(struct exynos_dp_device *dp) @@ -1065,6 +1081,13 @@ static void exynos_dp_poweron(struct exynos_drm_display *display) if (dp->dpms_mode == DRM_MODE_DPMS_ON) return;
+ if (dp->panel) { + if (drm_panel_prepare(dp->panel)) { + DRM_ERROR("failed to powerup the panel\n"); + return; + } + } + clk_prepare_enable(dp->clock); exynos_dp_phy_init(dp); exynos_dp_init_dp(dp); @@ -1079,10 +1102,22 @@ static void exynos_dp_poweroff(struct exynos_drm_display *display) if (dp->dpms_mode != DRM_MODE_DPMS_ON) return;
+ if (dp->panel) { + if (drm_panel_disable(dp->panel)) { + DRM_ERROR("failed to disable panel backlight\n"); + return; + } + } + disable_irq(dp->irq); flush_work(&dp->hotplug_work); exynos_dp_phy_exit(dp); clk_disable_unprepare(dp->clock); + + if (dp->panel) { + if (drm_panel_unprepare(dp->panel)) + DRM_ERROR("failed to powerdown the panel\n"); + } }
static void exynos_dp_dpms(struct exynos_drm_display *display, int mode) @@ -1215,7 +1250,7 @@ static int exynos_dp_dt_parse_panel(struct exynos_dp_device *dp) { int ret;
- ret = of_get_videomode(dp->dev->of_node, &dp->panel.vm, + ret = of_get_videomode(dp->dev->of_node, &dp->priv.vm, OF_USE_NATIVE_MODE); if (ret) { DRM_ERROR("failed: of_get_videomode() : %d\n", ret); @@ -1229,16 +1264,10 @@ static int exynos_dp_bind(struct device *dev, struct device *master, void *data) struct platform_device *pdev = to_platform_device(dev); struct drm_device *drm_dev = data; struct resource *res; - struct exynos_dp_device *dp; + struct exynos_dp_device *dp = exynos_dp_display.ctx; unsigned int irq_flags; - int ret = 0;
- dp = devm_kzalloc(&pdev->dev, sizeof(struct exynos_dp_device), - GFP_KERNEL); - if (!dp) - return -ENOMEM; - dp->dev = &pdev->dev; dp->dpms_mode = DRM_MODE_DPMS_OFF;
@@ -1250,9 +1279,11 @@ static int exynos_dp_bind(struct device *dev, struct device *master, void *data) if (ret) return ret;
- ret = exynos_dp_dt_parse_panel(dp); - if (ret) - return ret; + if (!dp->panel) { + ret = exynos_dp_dt_parse_panel(dp); + if (ret) + return ret; + }
dp->clock = devm_clk_get(&pdev->dev, "dp"); if (IS_ERR(dp->clock)) { @@ -1312,7 +1343,6 @@ static int exynos_dp_bind(struct device *dev, struct device *master, void *data) disable_irq(dp->irq);
dp->drm_dev = drm_dev; - exynos_dp_display.ctx = dp;
platform_set_drvdata(pdev, &exynos_dp_display);
@@ -1339,6 +1369,9 @@ static const struct component_ops exynos_dp_ops = {
static int exynos_dp_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; + struct device_node *panel_node; + struct exynos_dp_device *dp; int ret;
ret = exynos_drm_component_add(&pdev->dev, EXYNOS_DEVICE_TYPE_CONNECTOR, @@ -1346,6 +1379,21 @@ static int exynos_dp_probe(struct platform_device *pdev) if (ret) return ret;
+ dp = devm_kzalloc(&pdev->dev, sizeof(struct exynos_dp_device), + GFP_KERNEL); + if (!dp) + return -ENOMEM; + + panel_node = of_parse_phandle(dev->of_node, "panel", 0); + if (panel_node) { + dp->panel = of_drm_find_panel(panel_node); + of_node_put(panel_node); + if (!dp->panel) + return -EPROBE_DEFER; + } + + exynos_dp_display.ctx = dp; + ret = component_add(&pdev->dev, &exynos_dp_ops); if (ret) exynos_drm_component_del(&pdev->dev, diff --git a/drivers/gpu/drm/exynos/exynos_dp_core.h b/drivers/gpu/drm/exynos/exynos_dp_core.h index 02cc4f9..a1aee69 100644 --- a/drivers/gpu/drm/exynos/exynos_dp_core.h +++ b/drivers/gpu/drm/exynos/exynos_dp_core.h @@ -149,6 +149,7 @@ struct exynos_dp_device { struct drm_device *drm_dev; struct drm_connector connector; struct drm_encoder *encoder; + struct drm_panel *panel; struct clk *clock; unsigned int irq; void __iomem *reg_base; @@ -162,7 +163,7 @@ struct exynos_dp_device { int dpms_mode; int hpd_gpio;
- struct exynos_drm_panel_info panel; + struct exynos_drm_panel_info priv; };
/* exynos_dp_reg.c */
On Sat, Jul 26, 2014 at 12:52:07AM +0530, Ajay Kumar wrote: [...]
diff --git a/drivers/gpu/drm/exynos/exynos_dp_core.c b/drivers/gpu/drm/exynos/exynos_dp_core.c
[...]
@@ -887,6 +885,12 @@ static void exynos_dp_commit(struct exynos_drm_display *display) struct exynos_dp_device *dp = display->ctx; int ret;
- /* Keep backlight disabled while we configure video */
- if (dp->panel) {
if (drm_panel_disable(dp->panel))
DRM_ERROR("failed to disable panel backlight\n");
- }
drm_panel_disable() already gracefully handles the dp->panel == NULL case, so no need to check for it explicitly. But perhaps you do that only because panel support is optional and you want to avoid the error message if it isn't set up. In that case it's probably fine to leave this as-is.
But you should change the comment and error message, since you don't know what exactly drm_panel_disable() does.
And it might be useful to print the error code while at it, it might help save some debugging time in the future.
The same comments apply to most of the remainder of the file, but it looks good to me otherwise.
Thierry
This patch tries to seperate drm_bridge implementation into 2 parts, a drm part and a non_drm part.
A set of helper functions are defined in this patch to make bridge driver probe independent of the drm flow.
The bridge devices register themselves on a lookup table when they get probed by calling "drm_bridge_add_for_lookup".
The parent encoder driver waits till the bridge is available in the lookup table(by calling "of_drm_find_bridge") and then continues with its initialization.
The encoder driver should call "drm_bridge_attach_encoder" to pass on the drm_device and the encoder pointers to the bridge object.
Now that the drm_device pointer is available, the encoder then calls "bridge->funcs->post_encoder_init" to allow the bridge to continue registering itself with the drm core.
Also, non driver model based ptn3460 driver is removed in this patch.
Signed-off-by: Ajay Kumar ajaykumar.rs@samsung.com --- .../devicetree/bindings/drm/bridge/ptn3460.txt | 27 -- drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/bridge/Kconfig | 12 +- drivers/gpu/drm/bridge/Makefile | 2 - drivers/gpu/drm/bridge/ptn3460.c | 343 -------------------- drivers/gpu/drm/drm_bridge.c | 89 +++++ drivers/gpu/drm/drm_crtc.c | 9 +- drivers/gpu/drm/exynos/Kconfig | 3 +- drivers/gpu/drm/exynos/exynos_dp_core.c | 57 ++-- drivers/gpu/drm/exynos/exynos_dp_core.h | 1 + drivers/gpu/drm/msm/hdmi/hdmi_bridge.c | 3 +- include/drm/bridge/ptn3460.h | 37 --- include/drm/drm_crtc.h | 16 +- 13 files changed, 147 insertions(+), 453 deletions(-) delete mode 100644 Documentation/devicetree/bindings/drm/bridge/ptn3460.txt delete mode 100644 drivers/gpu/drm/bridge/ptn3460.c create mode 100644 drivers/gpu/drm/drm_bridge.c delete mode 100644 include/drm/bridge/ptn3460.h
diff --git a/Documentation/devicetree/bindings/drm/bridge/ptn3460.txt b/Documentation/devicetree/bindings/drm/bridge/ptn3460.txt deleted file mode 100644 index 52b93b2..0000000 --- a/Documentation/devicetree/bindings/drm/bridge/ptn3460.txt +++ /dev/null @@ -1,27 +0,0 @@ -ptn3460 bridge bindings - -Required properties: - - compatible: "nxp,ptn3460" - - reg: i2c address of the bridge - - powerdown-gpio: OF device-tree gpio specification - - reset-gpio: OF device-tree gpio specification - - edid-emulation: The EDID emulation entry to use - +-------+------------+------------------+ - | Value | Resolution | Description | - | 0 | 1024x768 | NXP Generic | - | 1 | 1920x1080 | NXP Generic | - | 2 | 1920x1080 | NXP Generic | - | 3 | 1600x900 | Samsung LTM200KT | - | 4 | 1920x1080 | Samsung LTM230HT | - | 5 | 1366x768 | NXP Generic | - | 6 | 1600x900 | ChiMei M215HGE | - +-------+------------+------------------+ - -Example: - lvds-bridge@20 { - compatible = "nxp,ptn3460"; - reg = <0x20>; - powerdown-gpio = <&gpy2 5 1 0 0>; - reset-gpio = <&gpx1 5 1 0 0>; - edid-emulation = <5>; - }; diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index af9a609..14a8b45 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -20,6 +20,7 @@ drm-$(CONFIG_COMPAT) += drm_ioc32.o drm-$(CONFIG_DRM_GEM_CMA_HELPER) += drm_gem_cma_helper.o drm-$(CONFIG_PCI) += ati_pcigart.o drm-$(CONFIG_DRM_PANEL) += drm_panel.o +drm-$(CONFIG_DRM_BRIDGE) += drm_bridge.o
drm-usb-y := drm_usb.o
diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig index 884923f..1e2f96c 100644 --- a/drivers/gpu/drm/bridge/Kconfig +++ b/drivers/gpu/drm/bridge/Kconfig @@ -1,5 +1,9 @@ -config DRM_PTN3460 - tristate "PTN3460 DP/LVDS bridge" +config DRM_BRIDGE + tristate depends on DRM - select DRM_KMS_HELPER - ---help--- + help + Bridge registration and lookup framework. + +menu "bridge chips" + depends on DRM_BRIDGE +endmenu diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile index b4733e1..be16eca 100644 --- a/drivers/gpu/drm/bridge/Makefile +++ b/drivers/gpu/drm/bridge/Makefile @@ -1,3 +1 @@ ccflags-y := -Iinclude/drm - -obj-$(CONFIG_DRM_PTN3460) += ptn3460.o diff --git a/drivers/gpu/drm/bridge/ptn3460.c b/drivers/gpu/drm/bridge/ptn3460.c deleted file mode 100644 index d466696..0000000 --- a/drivers/gpu/drm/bridge/ptn3460.c +++ /dev/null @@ -1,343 +0,0 @@ -/* - * NXP PTN3460 DP/LVDS bridge driver - * - * Copyright (C) 2013 Google, Inc. - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include <linux/module.h> -#include <linux/of.h> -#include <linux/of_gpio.h> -#include <linux/i2c.h> -#include <linux/gpio.h> -#include <linux/delay.h> - -#include "drmP.h" -#include "drm_edid.h" -#include "drm_crtc.h" -#include "drm_crtc_helper.h" - -#include "bridge/ptn3460.h" - -#define PTN3460_EDID_ADDR 0x0 -#define PTN3460_EDID_EMULATION_ADDR 0x84 -#define PTN3460_EDID_ENABLE_EMULATION 0 -#define PTN3460_EDID_EMULATION_SELECTION 1 -#define PTN3460_EDID_SRAM_LOAD_ADDR 0x85 - -struct ptn3460_bridge { - struct drm_connector connector; - struct i2c_client *client; - struct drm_encoder *encoder; - struct drm_bridge *bridge; - struct edid *edid; - int gpio_pd_n; - int gpio_rst_n; - u32 edid_emulation; - bool enabled; -}; - -static int ptn3460_read_bytes(struct ptn3460_bridge *ptn_bridge, char addr, - u8 *buf, int len) -{ - int ret; - - ret = i2c_master_send(ptn_bridge->client, &addr, 1); - if (ret <= 0) { - DRM_ERROR("Failed to send i2c command, ret=%d\n", ret); - return ret; - } - - ret = i2c_master_recv(ptn_bridge->client, buf, len); - if (ret <= 0) { - DRM_ERROR("Failed to recv i2c data, ret=%d\n", ret); - return ret; - } - - return 0; -} - -static int ptn3460_write_byte(struct ptn3460_bridge *ptn_bridge, char addr, - char val) -{ - int ret; - char buf[2]; - - buf[0] = addr; - buf[1] = val; - - ret = i2c_master_send(ptn_bridge->client, buf, ARRAY_SIZE(buf)); - if (ret <= 0) { - DRM_ERROR("Failed to send i2c command, ret=%d\n", ret); - return ret; - } - - return 0; -} - -static int ptn3460_select_edid(struct ptn3460_bridge *ptn_bridge) -{ - int ret; - char val; - - /* Load the selected edid into SRAM (accessed at PTN3460_EDID_ADDR) */ - ret = ptn3460_write_byte(ptn_bridge, PTN3460_EDID_SRAM_LOAD_ADDR, - ptn_bridge->edid_emulation); - if (ret) { - DRM_ERROR("Failed to transfer edid to sram, ret=%d\n", ret); - return ret; - } - - /* Enable EDID emulation and select the desired EDID */ - val = 1 << PTN3460_EDID_ENABLE_EMULATION | - ptn_bridge->edid_emulation << PTN3460_EDID_EMULATION_SELECTION; - - ret = ptn3460_write_byte(ptn_bridge, PTN3460_EDID_EMULATION_ADDR, val); - if (ret) { - DRM_ERROR("Failed to write edid value, ret=%d\n", ret); - return ret; - } - - return 0; -} - -static void ptn3460_pre_enable(struct drm_bridge *bridge) -{ - struct ptn3460_bridge *ptn_bridge = bridge->driver_private; - int ret; - - if (ptn_bridge->enabled) - return; - - if (gpio_is_valid(ptn_bridge->gpio_pd_n)) - gpio_set_value(ptn_bridge->gpio_pd_n, 1); - - if (gpio_is_valid(ptn_bridge->gpio_rst_n)) { - gpio_set_value(ptn_bridge->gpio_rst_n, 0); - udelay(10); - gpio_set_value(ptn_bridge->gpio_rst_n, 1); - } - - /* - * 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 - * time specified in the chip's datasheet to make sure we're really up. - */ - msleep(90); - - ret = ptn3460_select_edid(ptn_bridge); - if (ret) - DRM_ERROR("Select edid failed ret=%d\n", ret); - - ptn_bridge->enabled = true; -} - -static void ptn3460_enable(struct drm_bridge *bridge) -{ -} - -static void ptn3460_disable(struct drm_bridge *bridge) -{ - struct ptn3460_bridge *ptn_bridge = bridge->driver_private; - - if (!ptn_bridge->enabled) - return; - - ptn_bridge->enabled = false; - - if (gpio_is_valid(ptn_bridge->gpio_rst_n)) - gpio_set_value(ptn_bridge->gpio_rst_n, 1); - - if (gpio_is_valid(ptn_bridge->gpio_pd_n)) - gpio_set_value(ptn_bridge->gpio_pd_n, 0); -} - -static void ptn3460_post_disable(struct drm_bridge *bridge) -{ -} - -void ptn3460_bridge_destroy(struct drm_bridge *bridge) -{ - struct ptn3460_bridge *ptn_bridge = bridge->driver_private; - - drm_bridge_cleanup(bridge); - if (gpio_is_valid(ptn_bridge->gpio_pd_n)) - gpio_free(ptn_bridge->gpio_pd_n); - if (gpio_is_valid(ptn_bridge->gpio_rst_n)) - gpio_free(ptn_bridge->gpio_rst_n); - /* Nothing else to free, we've got devm allocated memory */ -} - -struct drm_bridge_funcs ptn3460_bridge_funcs = { - .pre_enable = ptn3460_pre_enable, - .enable = ptn3460_enable, - .disable = ptn3460_disable, - .post_disable = ptn3460_post_disable, - .destroy = ptn3460_bridge_destroy, -}; - -int ptn3460_get_modes(struct drm_connector *connector) -{ - struct ptn3460_bridge *ptn_bridge; - u8 *edid; - int ret, num_modes; - bool power_off; - - ptn_bridge = container_of(connector, struct ptn3460_bridge, connector); - - if (ptn_bridge->edid) - return drm_add_edid_modes(connector, ptn_bridge->edid); - - power_off = !ptn_bridge->enabled; - ptn3460_pre_enable(ptn_bridge->bridge); - - edid = kmalloc(EDID_LENGTH, GFP_KERNEL); - if (!edid) { - DRM_ERROR("Failed to allocate edid\n"); - return 0; - } - - ret = ptn3460_read_bytes(ptn_bridge, PTN3460_EDID_ADDR, edid, - EDID_LENGTH); - if (ret) { - kfree(edid); - num_modes = 0; - goto out; - } - - ptn_bridge->edid = (struct edid *)edid; - drm_mode_connector_update_edid_property(connector, ptn_bridge->edid); - - num_modes = drm_add_edid_modes(connector, ptn_bridge->edid); - -out: - if (power_off) - ptn3460_disable(ptn_bridge->bridge); - - return num_modes; -} - -struct drm_encoder *ptn3460_best_encoder(struct drm_connector *connector) -{ - struct ptn3460_bridge *ptn_bridge; - - ptn_bridge = container_of(connector, struct ptn3460_bridge, connector); - - return ptn_bridge->encoder; -} - -struct drm_connector_helper_funcs ptn3460_connector_helper_funcs = { - .get_modes = ptn3460_get_modes, - .best_encoder = ptn3460_best_encoder, -}; - -enum drm_connector_status ptn3460_detect(struct drm_connector *connector, - bool force) -{ - return connector_status_connected; -} - -void ptn3460_connector_destroy(struct drm_connector *connector) -{ - drm_connector_cleanup(connector); -} - -struct drm_connector_funcs ptn3460_connector_funcs = { - .dpms = drm_helper_connector_dpms, - .fill_modes = drm_helper_probe_single_connector_modes, - .detect = ptn3460_detect, - .destroy = ptn3460_connector_destroy, -}; - -int ptn3460_init(struct drm_device *dev, struct drm_encoder *encoder, - struct i2c_client *client, struct device_node *node) -{ - int ret; - struct drm_bridge *bridge; - struct ptn3460_bridge *ptn_bridge; - - bridge = devm_kzalloc(dev->dev, sizeof(*bridge), GFP_KERNEL); - if (!bridge) { - DRM_ERROR("Failed to allocate drm bridge\n"); - return -ENOMEM; - } - - ptn_bridge = devm_kzalloc(dev->dev, sizeof(*ptn_bridge), GFP_KERNEL); - if (!ptn_bridge) { - DRM_ERROR("Failed to allocate ptn bridge\n"); - return -ENOMEM; - } - - ptn_bridge->client = client; - ptn_bridge->encoder = encoder; - ptn_bridge->bridge = bridge; - ptn_bridge->gpio_pd_n = of_get_named_gpio(node, "powerdown-gpio", 0); - if (gpio_is_valid(ptn_bridge->gpio_pd_n)) { - ret = gpio_request_one(ptn_bridge->gpio_pd_n, - GPIOF_OUT_INIT_HIGH, "PTN3460_PD_N"); - if (ret) { - DRM_ERROR("Request powerdown-gpio failed (%d)\n", ret); - return ret; - } - } - - ptn_bridge->gpio_rst_n = of_get_named_gpio(node, "reset-gpio", 0); - if (gpio_is_valid(ptn_bridge->gpio_rst_n)) { - /* - * Request the reset pin low to avoid the bridge being - * initialized prematurely - */ - ret = gpio_request_one(ptn_bridge->gpio_rst_n, - GPIOF_OUT_INIT_LOW, "PTN3460_RST_N"); - if (ret) { - DRM_ERROR("Request reset-gpio failed (%d)\n", ret); - gpio_free(ptn_bridge->gpio_pd_n); - return ret; - } - } - - ret = of_property_read_u32(node, "edid-emulation", - &ptn_bridge->edid_emulation); - if (ret) { - DRM_ERROR("Can't read edid emulation value\n"); - goto err; - } - - ret = drm_bridge_init(dev, bridge, &ptn3460_bridge_funcs); - if (ret) { - DRM_ERROR("Failed to initialize bridge with drm\n"); - goto err; - } - - bridge->driver_private = ptn_bridge; - encoder->bridge = bridge; - - ret = drm_connector_init(dev, &ptn_bridge->connector, - &ptn3460_connector_funcs, DRM_MODE_CONNECTOR_LVDS); - if (ret) { - DRM_ERROR("Failed to initialize connector with drm\n"); - goto err; - } - drm_connector_helper_add(&ptn_bridge->connector, - &ptn3460_connector_helper_funcs); - drm_connector_register(&ptn_bridge->connector); - drm_mode_connector_attach_encoder(&ptn_bridge->connector, encoder); - - return 0; - -err: - if (gpio_is_valid(ptn_bridge->gpio_pd_n)) - gpio_free(ptn_bridge->gpio_pd_n); - if (gpio_is_valid(ptn_bridge->gpio_rst_n)) - gpio_free(ptn_bridge->gpio_rst_n); - return ret; -} -EXPORT_SYMBOL(ptn3460_init); diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c new file mode 100644 index 0000000..84645e6 --- /dev/null +++ b/drivers/gpu/drm/drm_bridge.c @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sub license, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include <linux/err.h> +#include <linux/module.h> + +#include <drm/drm_crtc.h> + +static DEFINE_MUTEX(bridge_lock); +static LIST_HEAD(bridge_lookup); + +int drm_bridge_add_for_lookup(struct drm_bridge *bridge) +{ + mutex_lock(&bridge_lock); + list_add_tail(&bridge->head, &bridge_lookup); + mutex_unlock(&bridge_lock); + + return 0; +} +EXPORT_SYMBOL(drm_bridge_add_for_lookup); + +void drm_bridge_remove_from_lookup(struct drm_bridge *bridge) +{ + mutex_lock(&bridge_lock); + list_del_init(&bridge->head); + mutex_unlock(&bridge_lock); +} +EXPORT_SYMBOL(drm_bridge_remove_from_lookup); + +int drm_bridge_attach_encoder(struct drm_bridge *bridge, + struct drm_encoder *encoder) +{ + if (!bridge || !encoder) + return -EINVAL; + + if (bridge->encoder) + return -EBUSY; + + encoder->bridge = bridge; + bridge->encoder = encoder; + bridge->drm_dev = encoder->dev; + + return 0; +} +EXPORT_SYMBOL(drm_bridge_attach_encoder); + +#ifdef CONFIG_OF +struct drm_bridge *of_drm_find_bridge(struct device_node *np) +{ + struct drm_bridge *bridge; + + mutex_lock(&bridge_lock); + + list_for_each_entry(bridge, &bridge_lookup, head) { + if (bridge->dev->of_node == np) { + mutex_unlock(&bridge_lock); + return bridge; + } + } + + mutex_unlock(&bridge_lock); + return NULL; +} +EXPORT_SYMBOL(of_drm_find_bridge); +#endif + +MODULE_AUTHOR("Ajay Kumar ajaykumar.rs@samsung.com"); +MODULE_DESCRIPTION("DRM bridge infrastructure"); +MODULE_LICENSE("GPL and additional rights"); diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 1ccf5cb..436e75d 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -993,7 +993,6 @@ EXPORT_SYMBOL(drm_connector_unplug_all); * drm_bridge_init - initialize a drm transcoder/bridge * @dev: drm device * @bridge: transcoder/bridge to set up - * @funcs: bridge function table * * Initialises a preallocated bridge. Bridges should be * subclassed as part of driver connector objects. @@ -1001,8 +1000,7 @@ EXPORT_SYMBOL(drm_connector_unplug_all); * Returns: * Zero on success, error code on failure. */ -int drm_bridge_init(struct drm_device *dev, struct drm_bridge *bridge, - const struct drm_bridge_funcs *funcs) +int drm_bridge_init(struct drm_device *dev, struct drm_bridge *bridge) { int ret;
@@ -1012,8 +1010,7 @@ int drm_bridge_init(struct drm_device *dev, struct drm_bridge *bridge, if (ret) goto out;
- bridge->dev = dev; - bridge->funcs = funcs; + bridge->drm_dev = dev;
list_add_tail(&bridge->head, &dev->mode_config.bridge_list); dev->mode_config.num_bridge++; @@ -1032,7 +1029,7 @@ EXPORT_SYMBOL(drm_bridge_init); */ void drm_bridge_cleanup(struct drm_bridge *bridge) { - struct drm_device *dev = bridge->dev; + struct drm_device *dev = bridge->drm_dev;
drm_modeset_lock_all(dev); drm_mode_object_put(dev, &bridge->base); diff --git a/drivers/gpu/drm/exynos/Kconfig b/drivers/gpu/drm/exynos/Kconfig index 7f9f6f9..bdef294 100644 --- a/drivers/gpu/drm/exynos/Kconfig +++ b/drivers/gpu/drm/exynos/Kconfig @@ -51,9 +51,10 @@ config DRM_EXYNOS_DSI
config DRM_EXYNOS_DP bool "EXYNOS DRM DP driver support" - depends on DRM_EXYNOS_FIMD && ARCH_EXYNOS && (DRM_PTN3460=n || DRM_PTN3460=y || DRM_PTN3460=DRM_EXYNOS) + depends on DRM_EXYNOS_FIMD && ARCH_EXYNOS default DRM_EXYNOS select DRM_PANEL + select DRM_BRIDGE help This enables support for DP device.
diff --git a/drivers/gpu/drm/exynos/exynos_dp_core.c b/drivers/gpu/drm/exynos/exynos_dp_core.c index 49356cc..055a9e1 100644 --- a/drivers/gpu/drm/exynos/exynos_dp_core.c +++ b/drivers/gpu/drm/exynos/exynos_dp_core.c @@ -28,7 +28,6 @@ #include <drm/drm_crtc.h> #include <drm/drm_crtc_helper.h> #include <drm/drm_panel.h> -#include <drm/bridge/ptn3460.h>
#include "exynos_drm_drv.h" #include "exynos_dp_core.h" @@ -986,33 +985,23 @@ static struct drm_connector_helper_funcs exynos_dp_connector_helper_funcs = { .best_encoder = exynos_dp_best_encoder, };
-static bool find_bridge(const char *compat, struct bridge_init *bridge) +static int exynos_drm_attach_lcd_bridge(struct exynos_dp_device *dp, + struct drm_encoder *encoder) { - bridge->client = NULL; - bridge->node = of_find_compatible_node(NULL, NULL, compat); - if (!bridge->node) - return false; - - bridge->client = of_find_i2c_device_by_node(bridge->node); - if (!bridge->client) - return false; - - return true; -} - -/* returns the number of bridges attached */ -static int exynos_drm_attach_lcd_bridge(struct drm_device *dev, - struct drm_encoder *encoder) -{ - struct bridge_init bridge; int ret;
- if (find_bridge("nxp,ptn3460", &bridge)) { - ret = ptn3460_init(dev, encoder, bridge.client, bridge.node); - if (!ret) - return 1; + dp->bridge->connector_polled = DRM_CONNECTOR_POLL_HPD; + ret = drm_bridge_attach_encoder(dp->bridge, encoder); + if (ret) { + DRM_ERROR("Failed to attach bridge to encoder\n"); + return ret; } - return 0; + + /* Allow the bridge to continue with rest of initialization */ + if (dp->bridge->funcs && dp->bridge->funcs->post_encoder_init) + return dp->bridge->funcs->post_encoder_init(dp->bridge); + + return -ENODEV; }
static int exynos_dp_create_connector(struct exynos_drm_display *display, @@ -1025,9 +1014,11 @@ static int exynos_dp_create_connector(struct exynos_drm_display *display, dp->encoder = encoder;
/* Pre-empt DP connector creation if there's a bridge */ - ret = exynos_drm_attach_lcd_bridge(dp->drm_dev, encoder); - if (ret) - return 0; + if (dp->bridge) { + ret = exynos_drm_attach_lcd_bridge(dp, encoder); + if (!ret) + return 0; + }
connector->polled = DRM_CONNECTOR_POLL_HPD;
@@ -1279,7 +1270,7 @@ static int exynos_dp_bind(struct device *dev, struct device *master, void *data) if (ret) return ret;
- if (!dp->panel) { + if (!dp->panel && !dp->bridge) { ret = exynos_dp_dt_parse_panel(dp); if (ret) return ret; @@ -1370,7 +1361,7 @@ static const struct component_ops exynos_dp_ops = { static int exynos_dp_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct device_node *panel_node; + struct device_node *panel_node, *bridge_node; struct exynos_dp_device *dp; int ret;
@@ -1392,6 +1383,14 @@ static int exynos_dp_probe(struct platform_device *pdev) return -EPROBE_DEFER; }
+ bridge_node = of_parse_phandle(dev->of_node, "bridge", 0); + if (bridge_node) { + dp->bridge = of_drm_find_bridge(bridge_node); + of_node_put(bridge_node); + if (!dp->bridge) + return -EPROBE_DEFER; + } + exynos_dp_display.ctx = dp;
ret = component_add(&pdev->dev, &exynos_dp_ops); diff --git a/drivers/gpu/drm/exynos/exynos_dp_core.h b/drivers/gpu/drm/exynos/exynos_dp_core.h index a1aee69..3b0ba93 100644 --- a/drivers/gpu/drm/exynos/exynos_dp_core.h +++ b/drivers/gpu/drm/exynos/exynos_dp_core.h @@ -150,6 +150,7 @@ struct exynos_dp_device { struct drm_connector connector; struct drm_encoder *encoder; struct drm_panel *panel; + struct drm_bridge *bridge; struct clk *clock; unsigned int irq; void __iomem *reg_base; diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c b/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c index f6cf745..0309539 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c @@ -221,8 +221,9 @@ struct drm_bridge *hdmi_bridge_init(struct hdmi *hdmi) hdmi_bridge->hdmi = hdmi_reference(hdmi);
bridge = &hdmi_bridge->base; + bridge->funcs = &hdmi_bridge_funcs;
- drm_bridge_init(hdmi->dev, bridge, &hdmi_bridge_funcs); + drm_bridge_init(hdmi->dev, bridge);
return bridge;
diff --git a/include/drm/bridge/ptn3460.h b/include/drm/bridge/ptn3460.h deleted file mode 100644 index ff62344..0000000 --- a/include/drm/bridge/ptn3460.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2013 Google, Inc. - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef _DRM_BRIDGE_PTN3460_H_ -#define _DRM_BRIDGE_PTN3460_H_ - -struct drm_device; -struct drm_encoder; -struct i2c_client; -struct device_node; - -#if defined(CONFIG_DRM_PTN3460) || defined(CONFIG_DRM_PTN3460_MODULE) - -int ptn3460_init(struct drm_device *dev, struct drm_encoder *encoder, - struct i2c_client *client, struct device_node *node); -#else - -static inline int ptn3460_init(struct drm_device *dev, - struct drm_encoder *encoder, struct i2c_client *client, - struct device_node *node) -{ - return 0; -} - -#endif - -#endif diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index e529b68..e5a41ad 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -619,6 +619,7 @@ struct drm_plane {
/** * drm_bridge_funcs - drm_bridge control functions + * @post_encoder_init: called by the parent encoder * @mode_fixup: Try to fixup (or reject entirely) proposed mode for this bridge * @disable: Called right before encoder prepare, disables the bridge * @post_disable: Called right after encoder prepare, for lockstepped disable @@ -628,6 +629,7 @@ struct drm_plane { * @destroy: make object go away */ struct drm_bridge_funcs { + int (*post_encoder_init)(struct drm_bridge *bridge); bool (*mode_fixup)(struct drm_bridge *bridge, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode); @@ -648,15 +650,19 @@ struct drm_bridge_funcs { * @base: base mode object * @funcs: control functions * @driver_private: pointer to the bridge driver's internal context + * @connector_polled: polled flag needed for registering connector */ struct drm_bridge { - struct drm_device *dev; + struct device *dev; + struct drm_device *drm_dev; + struct drm_encoder *encoder; struct list_head head;
struct drm_mode_object base;
const struct drm_bridge_funcs *funcs; void *driver_private; + int connector_polled; };
/** @@ -895,8 +901,12 @@ extern void drm_connector_cleanup(struct drm_connector *connector); /* helper to unplug all connectors from sysfs for device */ extern void drm_connector_unplug_all(struct drm_device *dev);
-extern int drm_bridge_init(struct drm_device *dev, struct drm_bridge *bridge, - const struct drm_bridge_funcs *funcs); +extern int drm_bridge_add_for_lookup(struct drm_bridge *bridge); +extern void drm_bridge_remove_from_lookup(struct drm_bridge *bridge); +extern struct drm_bridge *of_drm_find_bridge(struct device_node *np); +extern int drm_bridge_attach_encoder(struct drm_bridge *bridge, + struct drm_encoder *encoder); +extern int drm_bridge_init(struct drm_device *dev, struct drm_bridge *bridge); extern void drm_bridge_cleanup(struct drm_bridge *bridge);
extern int drm_encoder_init(struct drm_device *dev,
On Sat, Jul 26, 2014 at 12:52:08AM +0530, Ajay Kumar wrote:
This patch tries to seperate drm_bridge implementation into 2 parts, a drm part and a non_drm part.
A set of helper functions are defined in this patch to make bridge driver probe independent of the drm flow.
The bridge devices register themselves on a lookup table when they get probed by calling "drm_bridge_add_for_lookup".
The parent encoder driver waits till the bridge is available in the lookup table(by calling "of_drm_find_bridge") and then continues with its initialization.
The encoder driver should call "drm_bridge_attach_encoder" to pass on the drm_device and the encoder pointers to the bridge object.
Now that the drm_device pointer is available, the encoder then calls "bridge->funcs->post_encoder_init" to allow the bridge to continue registering itself with the drm core.
Also, non driver model based ptn3460 driver is removed in this patch.
Why is it removed in this patch? Can't you do this incrementally rather than remove the driver in this patch and add it again later? If you do it this way then we'll always have this one commit where devices that have a ptn3460 don't work, so it becomes impossible to bisect across this commit.
diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
[...]
+int drm_bridge_add_for_lookup(struct drm_bridge *bridge) +{
- mutex_lock(&bridge_lock);
- list_add_tail(&bridge->head, &bridge_lookup);
- mutex_unlock(&bridge_lock);
- return 0;
+} +EXPORT_SYMBOL(drm_bridge_add_for_lookup);
+void drm_bridge_remove_from_lookup(struct drm_bridge *bridge) +{
- mutex_lock(&bridge_lock);
- list_del_init(&bridge->head);
- mutex_unlock(&bridge_lock);
+} +EXPORT_SYMBOL(drm_bridge_remove_from_lookup);
The "_for_lookup" and "_from_lookup" suffixes aren't useful in my opinion.
+int drm_bridge_attach_encoder(struct drm_bridge *bridge,
struct drm_encoder *encoder)
And this could simply be "drm_bridge_attach()" since we'll only ever want to attach it to encoders.
+{
- if (!bridge || !encoder)
return -EINVAL;
- if (bridge->encoder)
return -EBUSY;
- encoder->bridge = bridge;
- bridge->encoder = encoder;
- bridge->drm_dev = encoder->dev;
Should this function perhaps call the bridge's ->post_encoder_init()? And it should probably call drm_bridge_init() too, since the DRM device is now available.
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
[...]
Maybe since you're introducing a new drm_bridge.c file above already it would make sense to move out existing drm_bridge related code in a preparatory patch?
Maybe Sean or Rob can comment on whether there was a specific reason to include it in drm_crtc.c in the first place.
@@ -1012,8 +1010,7 @@ int drm_bridge_init(struct drm_device *dev, struct drm_bridge *bridge, if (ret) goto out;
- bridge->dev = dev;
- bridge->funcs = funcs;
- bridge->drm_dev = dev;
This sets ->drm_dev, but it was already set in drm_bridge_attach(), so I think that's one more argument to call this function when attaching.
diff --git a/drivers/gpu/drm/exynos/exynos_dp_core.c b/drivers/gpu/drm/exynos/exynos_dp_core.c
[...]
@@ -1370,7 +1361,7 @@ static const struct component_ops exynos_dp_ops = { static int exynos_dp_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev;
- struct device_node *panel_node;
- struct device_node *panel_node, *bridge_node;
Nit: I don't think you'll need two variables here, since once you've obtained the real panel or bridge objects you no longer need the OF nodes.
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index e529b68..e5a41ad 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -619,6 +619,7 @@ struct drm_plane {
/**
- drm_bridge_funcs - drm_bridge control functions
- @post_encoder_init: called by the parent encoder
Maybe rename this to "attach" to make it more obvious when exactly it's called?
- @mode_fixup: Try to fixup (or reject entirely) proposed mode for this bridge
- @disable: Called right before encoder prepare, disables the bridge
- @post_disable: Called right after encoder prepare, for lockstepped disable
@@ -628,6 +629,7 @@ struct drm_plane {
- @destroy: make object go away
*/ struct drm_bridge_funcs {
- int (*post_encoder_init)(struct drm_bridge *bridge); bool (*mode_fixup)(struct drm_bridge *bridge, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode);
@@ -648,15 +650,19 @@ struct drm_bridge_funcs {
- @base: base mode object
- @funcs: control functions
- @driver_private: pointer to the bridge driver's internal context
- @connector_polled: polled flag needed for registering connector
Can you explain why this new field is needed? It seems like a completely unrelated change.
Thierry
On Wed, Jul 30, 2014 at 4:49 PM, Thierry Reding thierry.reding@gmail.com wrote:
On Sat, Jul 26, 2014 at 12:52:08AM +0530, Ajay Kumar wrote:
This patch tries to seperate drm_bridge implementation into 2 parts, a drm part and a non_drm part.
A set of helper functions are defined in this patch to make bridge driver probe independent of the drm flow.
The bridge devices register themselves on a lookup table when they get probed by calling "drm_bridge_add_for_lookup".
The parent encoder driver waits till the bridge is available in the lookup table(by calling "of_drm_find_bridge") and then continues with its initialization.
The encoder driver should call "drm_bridge_attach_encoder" to pass on the drm_device and the encoder pointers to the bridge object.
Now that the drm_device pointer is available, the encoder then calls "bridge->funcs->post_encoder_init" to allow the bridge to continue registering itself with the drm core.
Also, non driver model based ptn3460 driver is removed in this patch.
Why is it removed in this patch? Can't you do this incrementally rather than remove the driver in this patch and add it again later? If you do it this way then we'll always have this one commit where devices that have a ptn3460 don't work, so it becomes impossible to bisect across this commit.
Ok. I will try to modify ptn3460 to support driver model in this patch itself. And then, adding panel support, converting it to gpiod interface and other cleanups should follow.
diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
[...]
+int drm_bridge_add_for_lookup(struct drm_bridge *bridge) +{
mutex_lock(&bridge_lock);
list_add_tail(&bridge->head, &bridge_lookup);
mutex_unlock(&bridge_lock);
return 0;
+} +EXPORT_SYMBOL(drm_bridge_add_for_lookup);
+void drm_bridge_remove_from_lookup(struct drm_bridge *bridge) +{
mutex_lock(&bridge_lock);
list_del_init(&bridge->head);
mutex_unlock(&bridge_lock);
+} +EXPORT_SYMBOL(drm_bridge_remove_from_lookup);
The "_for_lookup" and "_from_lookup" suffixes aren't useful in my opinion.
Ok. I will just remove the suffix.
+int drm_bridge_attach_encoder(struct drm_bridge *bridge,
struct drm_encoder *encoder)
And this could simply be "drm_bridge_attach()" since we'll only ever want to attach it to encoders.
Right.
+{
if (!bridge || !encoder)
return -EINVAL;
if (bridge->encoder)
return -EBUSY;
encoder->bridge = bridge;
bridge->encoder = encoder;
bridge->drm_dev = encoder->dev;
Should this function perhaps call the bridge's ->post_encoder_init()? And it should probably call drm_bridge_init() too, since the DRM device is now available.
This will cleanup some code in both the drivers. I will change it.
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
[...]
Maybe since you're introducing a new drm_bridge.c file above already it would make sense to move out existing drm_bridge related code in a preparatory patch?
Maybe Sean or Rob can comment on whether there was a specific reason to include it in drm_crtc.c in the first place.
@@ -1012,8 +1010,7 @@ int drm_bridge_init(struct drm_device *dev, struct drm_bridge *bridge, if (ret) goto out;
bridge->dev = dev;
bridge->funcs = funcs;
bridge->drm_dev = dev;
This sets ->drm_dev, but it was already set in drm_bridge_attach(), so I think that's one more argument to call this function when attaching.
Point accepted.
diff --git a/drivers/gpu/drm/exynos/exynos_dp_core.c b/drivers/gpu/drm/exynos/exynos_dp_core.c
[...]
@@ -1370,7 +1361,7 @@ static const struct component_ops exynos_dp_ops = { static int exynos_dp_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev;
struct device_node *panel_node;
struct device_node *panel_node, *bridge_node;
Nit: I don't think you'll need two variables here, since once you've obtained the real panel or bridge objects you no longer need the OF nodes.
Right.
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index e529b68..e5a41ad 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -619,6 +619,7 @@ struct drm_plane {
/**
- drm_bridge_funcs - drm_bridge control functions
- @post_encoder_init: called by the parent encoder
Maybe rename this to "attach" to make it more obvious when exactly it's called?
"post_encoder_attach"?
- @mode_fixup: Try to fixup (or reject entirely) proposed mode for this bridge
- @disable: Called right before encoder prepare, disables the bridge
- @post_disable: Called right after encoder prepare, for lockstepped disable
@@ -628,6 +629,7 @@ struct drm_plane {
- @destroy: make object go away
*/ struct drm_bridge_funcs {
int (*post_encoder_init)(struct drm_bridge *bridge); bool (*mode_fixup)(struct drm_bridge *bridge, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode);
@@ -648,15 +650,19 @@ struct drm_bridge_funcs {
- @base: base mode object
- @funcs: control functions
- @driver_private: pointer to the bridge driver's internal context
- @connector_polled: polled flag needed for registering connector
Can you explain why this new field is needed? It seems like a completely unrelated change.
How do I select this flag for the bridge chip? Assume if I did select DRM_CONNECTOR_POLL_HPD, where to call drm_helper_hpd_irq_event in the driver? Is post_encoder_init a right place?
Without the polled flag, I get display very late. Please throw some light on this!
Ajay
On Wed, Jul 30, 2014 at 08:01:44PM +0530, Ajay kumar wrote:
On Wed, Jul 30, 2014 at 4:49 PM, Thierry Reding thierry.reding@gmail.com wrote:
On Sat, Jul 26, 2014 at 12:52:08AM +0530, Ajay Kumar wrote:
This patch tries to seperate drm_bridge implementation into 2 parts, a drm part and a non_drm part.
A set of helper functions are defined in this patch to make bridge driver probe independent of the drm flow.
The bridge devices register themselves on a lookup table when they get probed by calling "drm_bridge_add_for_lookup".
The parent encoder driver waits till the bridge is available in the lookup table(by calling "of_drm_find_bridge") and then continues with its initialization.
The encoder driver should call "drm_bridge_attach_encoder" to pass on the drm_device and the encoder pointers to the bridge object.
Now that the drm_device pointer is available, the encoder then calls "bridge->funcs->post_encoder_init" to allow the bridge to continue registering itself with the drm core.
Also, non driver model based ptn3460 driver is removed in this patch.
Why is it removed in this patch? Can't you do this incrementally rather than remove the driver in this patch and add it again later? If you do it this way then we'll always have this one commit where devices that have a ptn3460 don't work, so it becomes impossible to bisect across this commit.
Ok. I will try to modify ptn3460 to support driver model in this patch itself. And then, adding panel support, converting it to gpiod interface and other cleanups should follow.
I think it should even be possible to do this in more separate steps. For example you could add the new bridge infrastructure without touching any of the existing drivers (so that they are completely unaffected by the changes) and then start converting one by one.
For some of the changes this may be difficult (such as the dev -> drm_dev rename to make room for the new struct device *dev). But that could for example be done in a preparatory patch that first renames the field, so that the "infrastructure" patch can add the new field without renaming any fields and therefore needing changes to drivers directly.
The goal of that whole exercise is to allow display drivers to keep working with the existing API (ptn3460_init()) while we convert the bridge drivers to register with the new framework. Then we can more safely convert each display driver individually to make use of the new framework and once all drivers have been converted the old API can simply be removed.
That way there should be no impact on existing functionality at any point.
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
[...]
Maybe since you're introducing a new drm_bridge.c file above already it would make sense to move out existing drm_bridge related code in a preparatory patch?
Maybe Sean or Rob can comment on whether there was a specific reason to include it in drm_crtc.c in the first place.
@@ -1012,8 +1010,7 @@ int drm_bridge_init(struct drm_device *dev, struct drm_bridge *bridge, if (ret) goto out;
bridge->dev = dev;
bridge->funcs = funcs;
bridge->drm_dev = dev;
This sets ->drm_dev, but it was already set in drm_bridge_attach(), so I think that's one more argument to call this function when attaching.
Point accepted.
I forgot to mention earlier. drm_dev seems redundant to me. I'd go with just "drm".
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index e529b68..e5a41ad 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -619,6 +619,7 @@ struct drm_plane {
/**
- drm_bridge_funcs - drm_bridge control functions
- @post_encoder_init: called by the parent encoder
Maybe rename this to "attach" to make it more obvious when exactly it's called?
"post_encoder_attach"?
"post_encoder_" doesn't contain much information, or even ambiguous information. What does "post" "encoder" mean? A bridge is always attached to an encoder, so "encoder" can be dropped. Now "post" has implications as to the time when it is called, but does it mean after the encoder has been initialized, or after the encoder has been removed? Simply "attach" means it's called by the parent encoder to initialize the bridge once it's been attached to an encoder. So obviously it's after the encoder has been initialized. "attach" has all he information required. Any prefix is redundant in my opinion and removing prefixes gives shorter names and reduces the number of keypresses.
- @mode_fixup: Try to fixup (or reject entirely) proposed mode for this bridge
- @disable: Called right before encoder prepare, disables the bridge
- @post_disable: Called right after encoder prepare, for lockstepped disable
@@ -628,6 +629,7 @@ struct drm_plane {
- @destroy: make object go away
*/ struct drm_bridge_funcs {
int (*post_encoder_init)(struct drm_bridge *bridge); bool (*mode_fixup)(struct drm_bridge *bridge, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode);
@@ -648,15 +650,19 @@ struct drm_bridge_funcs {
- @base: base mode object
- @funcs: control functions
- @driver_private: pointer to the bridge driver's internal context
- @connector_polled: polled flag needed for registering connector
Can you explain why this new field is needed? It seems like a completely unrelated change.
How do I select this flag for the bridge chip? Assume if I did select DRM_CONNECTOR_POLL_HPD, where to call drm_helper_hpd_irq_event in the driver? Is post_encoder_init a right place?
Without the polled flag, I get display very late. Please throw some light on this!
I just don't understand why it's necessary to implement this field in the drm_bridge. Every bridge driver will already implement a connector, in which case it can simply set the connector's .polled field, can't it?
It seems like the only reason you have it in drm_bridge is so that the encoder driver can set it. But I don't see why it should be doing that. The polled state is a property of the connector, and the encoder driver doesn't know anything about it. So if the bridge has a way to detect HPD then it should be setting up the connector to properly report it. For example if the bridge has an input pin to detect it, then it could use a GPIO to receive interrupts and call drm_helper_hpd_irq_event() in the interrupt handler.
Perhaps you can explain the exact setup where you need this (or point me at the code since I can't seem to find the relevant location) so that I can gain a better understanding.
Thierry
On Wed, Jul 30, 2014 at 8:38 PM, Thierry Reding thierry.reding@gmail.com wrote:
On Wed, Jul 30, 2014 at 08:01:44PM +0530, Ajay kumar wrote:
On Wed, Jul 30, 2014 at 4:49 PM, Thierry Reding thierry.reding@gmail.com wrote:
On Sat, Jul 26, 2014 at 12:52:08AM +0530, Ajay Kumar wrote:
This patch tries to seperate drm_bridge implementation into 2 parts, a drm part and a non_drm part.
A set of helper functions are defined in this patch to make bridge driver probe independent of the drm flow.
The bridge devices register themselves on a lookup table when they get probed by calling "drm_bridge_add_for_lookup".
The parent encoder driver waits till the bridge is available in the lookup table(by calling "of_drm_find_bridge") and then continues with its initialization.
The encoder driver should call "drm_bridge_attach_encoder" to pass on the drm_device and the encoder pointers to the bridge object.
Now that the drm_device pointer is available, the encoder then calls "bridge->funcs->post_encoder_init" to allow the bridge to continue registering itself with the drm core.
Also, non driver model based ptn3460 driver is removed in this patch.
Why is it removed in this patch? Can't you do this incrementally rather than remove the driver in this patch and add it again later? If you do it this way then we'll always have this one commit where devices that have a ptn3460 don't work, so it becomes impossible to bisect across this commit.
Ok. I will try to modify ptn3460 to support driver model in this patch itself. And then, adding panel support, converting it to gpiod interface and other cleanups should follow.
I think it should even be possible to do this in more separate steps. For example you could add the new bridge infrastructure without touching any of the existing drivers (so that they are completely unaffected by the changes) and then start converting one by one.
For some of the changes this may be difficult (such as the dev -> drm_dev rename to make room for the new struct device *dev). But that could for example be done in a preparatory patch that first renames the field, so that the "infrastructure" patch can add the new field without renaming any fields and therefore needing changes to drivers directly.
The goal of that whole exercise is to allow display drivers to keep working with the existing API (ptn3460_init()) while we convert the bridge drivers to register with the new framework. Then we can more safely convert each display driver individually to make use of the new framework and once all drivers have been converted the old API can simply be removed.
That way there should be no impact on existing functionality at any point.
As of now only exynos_dp uses ptn3460_init. And, also only 2 drivers use drm_bridge_init. It should be really easy to bisect if something goes wrong. Still, I will try to divide it so that each patch contains minimal change.
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
[...]
Maybe since you're introducing a new drm_bridge.c file above already it would make sense to move out existing drm_bridge related code in a preparatory patch?
Maybe Sean or Rob can comment on whether there was a specific reason to include it in drm_crtc.c in the first place.
@@ -1012,8 +1010,7 @@ int drm_bridge_init(struct drm_device *dev, struct drm_bridge *bridge, if (ret) goto out;
bridge->dev = dev;
bridge->funcs = funcs;
bridge->drm_dev = dev;
This sets ->drm_dev, but it was already set in drm_bridge_attach(), so I think that's one more argument to call this function when attaching.
Point accepted.
I forgot to mention earlier. drm_dev seems redundant to me. I'd go with just "drm".
Ok.
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index e529b68..e5a41ad 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -619,6 +619,7 @@ struct drm_plane {
/**
- drm_bridge_funcs - drm_bridge control functions
- @post_encoder_init: called by the parent encoder
Maybe rename this to "attach" to make it more obvious when exactly it's called?
"post_encoder_attach"?
"post_encoder_" doesn't contain much information, or even ambiguous information. What does "post" "encoder" mean? A bridge is always attached to an encoder, so "encoder" can be dropped. Now "post" has implications as to the time when it is called, but does it mean after the encoder has been initialized, or after the encoder has been removed? Simply "attach" means it's called by the parent encoder to initialize the bridge once it's been attached to an encoder. So obviously it's after the encoder has been initialized. "attach" has all he information required. Any prefix is redundant in my opinion and removing prefixes gives shorter names and reduces the number of keypresses.
Finally, what name it should have?
- @mode_fixup: Try to fixup (or reject entirely) proposed mode for this bridge
- @disable: Called right before encoder prepare, disables the bridge
- @post_disable: Called right after encoder prepare, for lockstepped disable
@@ -628,6 +629,7 @@ struct drm_plane {
- @destroy: make object go away
*/ struct drm_bridge_funcs {
int (*post_encoder_init)(struct drm_bridge *bridge); bool (*mode_fixup)(struct drm_bridge *bridge, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode);
@@ -648,15 +650,19 @@ struct drm_bridge_funcs {
- @base: base mode object
- @funcs: control functions
- @driver_private: pointer to the bridge driver's internal context
- @connector_polled: polled flag needed for registering connector
Can you explain why this new field is needed? It seems like a completely unrelated change.
How do I select this flag for the bridge chip? Assume if I did select DRM_CONNECTOR_POLL_HPD, where to call drm_helper_hpd_irq_event in the driver? Is post_encoder_init a right place?
Without the polled flag, I get display very late. Please throw some light on this!
I just don't understand why it's necessary to implement this field in the drm_bridge. Every bridge driver will already implement a connector, in which case it can simply set the connector's .polled field, can't it?
It seems like the only reason you have it in drm_bridge is so that the encoder driver can set it. But I don't see why it should be doing that. The polled state is a property of the connector, and the encoder driver doesn't know anything about it. So if the bridge has a way to detect HPD then it should be setting up the connector to properly report it. For example if the bridge has an input pin to detect it, then it could use a GPIO to receive interrupts and call drm_helper_hpd_irq_event() in the interrupt handler.
Hmm. Are we allowed to call drm_helper_hpd_irq_event() the way DSI panels use it? Like the last step in panel probe? For bridges, it will be in post_encoder_init!
Perhaps you can explain the exact setup where you need this (or point me at the code since I can't seem to find the relevant location) so that I can gain a better understanding.
I can see bridge getting detected only when I set polled member of bridge connector to DRM_CONNECTOR_POLL_HPD, because exynos_drm also calls drm_helper_hpd_irq_event() to force detect all connectors at the end of drm_load.
If I don't set the polled member, I see bridge getting detected after quite sometime.
Ajay
On Wed, Jul 30, 2014 at 09:33:28PM +0530, Ajay kumar wrote:
On Wed, Jul 30, 2014 at 8:38 PM, Thierry Reding thierry.reding@gmail.com wrote:
[...]
I think it should even be possible to do this in more separate steps. For example you could add the new bridge infrastructure without touching any of the existing drivers (so that they are completely unaffected by the changes) and then start converting one by one.
For some of the changes this may be difficult (such as the dev -> drm_dev rename to make room for the new struct device *dev). But that could for example be done in a preparatory patch that first renames the field, so that the "infrastructure" patch can add the new field without renaming any fields and therefore needing changes to drivers directly.
The goal of that whole exercise is to allow display drivers to keep working with the existing API (ptn3460_init()) while we convert the bridge drivers to register with the new framework. Then we can more safely convert each display driver individually to make use of the new framework and once all drivers have been converted the old API can simply be removed.
That way there should be no impact on existing functionality at any point.
As of now only exynos_dp uses ptn3460_init. And, also only 2 drivers use drm_bridge_init. It should be really easy to bisect if something goes wrong. Still, I will try to divide it so that each patch contains minimal change.
Thanks.
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index e529b68..e5a41ad 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -619,6 +619,7 @@ struct drm_plane {
/**
- drm_bridge_funcs - drm_bridge control functions
- @post_encoder_init: called by the parent encoder
Maybe rename this to "attach" to make it more obvious when exactly it's called?
"post_encoder_attach"?
"post_encoder_" doesn't contain much information, or even ambiguous information. What does "post" "encoder" mean? A bridge is always attached to an encoder, so "encoder" can be dropped. Now "post" has implications as to the time when it is called, but does it mean after the encoder has been initialized, or after the encoder has been removed? Simply "attach" means it's called by the parent encoder to initialize the bridge once it's been attached to an encoder. So obviously it's after the encoder has been initialized. "attach" has all he information required. Any prefix is redundant in my opinion and removing prefixes gives shorter names and reduces the number of keypresses.
Finally, what name it should have?
I originally proposed "attach" as a more concise name and I still think that's the best alternative.
- @mode_fixup: Try to fixup (or reject entirely) proposed mode for this bridge
- @disable: Called right before encoder prepare, disables the bridge
- @post_disable: Called right after encoder prepare, for lockstepped disable
@@ -628,6 +629,7 @@ struct drm_plane {
- @destroy: make object go away
*/ struct drm_bridge_funcs {
int (*post_encoder_init)(struct drm_bridge *bridge); bool (*mode_fixup)(struct drm_bridge *bridge, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode);
@@ -648,15 +650,19 @@ struct drm_bridge_funcs {
- @base: base mode object
- @funcs: control functions
- @driver_private: pointer to the bridge driver's internal context
- @connector_polled: polled flag needed for registering connector
Can you explain why this new field is needed? It seems like a completely unrelated change.
How do I select this flag for the bridge chip? Assume if I did select DRM_CONNECTOR_POLL_HPD, where to call drm_helper_hpd_irq_event in the driver? Is post_encoder_init a right place?
Without the polled flag, I get display very late. Please throw some light on this!
I just don't understand why it's necessary to implement this field in the drm_bridge. Every bridge driver will already implement a connector, in which case it can simply set the connector's .polled field, can't it?
It seems like the only reason you have it in drm_bridge is so that the encoder driver can set it. But I don't see why it should be doing that. The polled state is a property of the connector, and the encoder driver doesn't know anything about it. So if the bridge has a way to detect HPD then it should be setting up the connector to properly report it. For example if the bridge has an input pin to detect it, then it could use a GPIO to receive interrupts and call drm_helper_hpd_irq_event() in the interrupt handler.
Hmm. Are we allowed to call drm_helper_hpd_irq_event() the way DSI panels use it? Like the last step in panel probe? For bridges, it will be in post_encoder_init!
drm_helper_hpd_irq_event() should only be called when a hotplug event is detected. For all other cases detection should already happen when DRM initializes.
I see that on Tegra we call drm_helper_hpd_irq_event() in the DSI host's ->attach(), but I don't remember why that's there and I don't see why it would be necessary either. I'll try to remove it and see if things still work without.
Perhaps you can explain the exact setup where you need this (or point me at the code since I can't seem to find the relevant location) so that I can gain a better understanding.
I can see bridge getting detected only when I set polled member of bridge connector to DRM_CONNECTOR_POLL_HPD, because exynos_drm also calls drm_helper_hpd_irq_event() to force detect all connectors at the end of drm_load.
That shouldn't be necessary. DRM automatically force detects all outputs (at least if you use drm_helper_probe_single_connector_modes(), which seems to be the case for Exynos).
Thierry
Hello Ajay,
On Thu, Jul 31, 2014 at 12:58 PM, Thierry Reding thierry.reding@gmail.com wrote:
On Wed, Jul 30, 2014 at 09:33:28PM +0530, Ajay kumar wrote:
On Wed, Jul 30, 2014 at 8:38 PM, Thierry Reding thierry.reding@gmail.com wrote:
[...]
I think it should even be possible to do this in more separate steps. For example you could add the new bridge infrastructure without touching any of the existing drivers (so that they are completely unaffected by the changes) and then start converting one by one.
For some of the changes this may be difficult (such as the dev -> drm_dev rename to make room for the new struct device *dev). But that could for example be done in a preparatory patch that first renames the field, so that the "infrastructure" patch can add the new field without renaming any fields and therefore needing changes to drivers directly.
The goal of that whole exercise is to allow display drivers to keep working with the existing API (ptn3460_init()) while we convert the bridge drivers to register with the new framework. Then we can more safely convert each display driver individually to make use of the new framework and once all drivers have been converted the old API can simply be removed.
That way there should be no impact on existing functionality at any point.
As of now only exynos_dp uses ptn3460_init. And, also only 2 drivers use drm_bridge_init. It should be really easy to bisect if something goes wrong. Still, I will try to divide it so that each patch contains minimal change.
Thanks.
Do you plan to address Thierry's concerns and re-spin this patch?
Same question for patches:
"drm/bridge: Add i2c based driver for ptn3460 bridge" "drm/bridge: Add i2c based driver for ps8622/ps8625 bridge"
Thanks a lot and best regards, Javier
Hi Javier,
On Sat, Aug 23, 2014 at 5:03 AM, Javier Martinez Canillas javier@dowhile0.org wrote:
Hello Ajay,
On Thu, Jul 31, 2014 at 12:58 PM, Thierry Reding thierry.reding@gmail.com wrote:
On Wed, Jul 30, 2014 at 09:33:28PM +0530, Ajay kumar wrote:
On Wed, Jul 30, 2014 at 8:38 PM, Thierry Reding thierry.reding@gmail.com wrote:
[...]
I think it should even be possible to do this in more separate steps. For example you could add the new bridge infrastructure without touching any of the existing drivers (so that they are completely unaffected by the changes) and then start converting one by one.
For some of the changes this may be difficult (such as the dev -> drm_dev rename to make room for the new struct device *dev). But that could for example be done in a preparatory patch that first renames the field, so that the "infrastructure" patch can add the new field without renaming any fields and therefore needing changes to drivers directly.
The goal of that whole exercise is to allow display drivers to keep working with the existing API (ptn3460_init()) while we convert the bridge drivers to register with the new framework. Then we can more safely convert each display driver individually to make use of the new framework and once all drivers have been converted the old API can simply be removed.
That way there should be no impact on existing functionality at any point.
As of now only exynos_dp uses ptn3460_init. And, also only 2 drivers use drm_bridge_init. It should be really easy to bisect if something goes wrong. Still, I will try to divide it so that each patch contains minimal change.
Thanks.
Do you plan to address Thierry's concerns and re-spin this patch?
Same question for patches:
"drm/bridge: Add i2c based driver for ptn3460 bridge" "drm/bridge: Add i2c based driver for ps8622/ps8625 bridge"
Yes, I will. I was caught up with some other work. I will be sending a version ASAP.
Ajay
Hello Ajay,
On Mon, Aug 25, 2014 at 8:11 AM, Ajay kumar ajaynumb@gmail.com wrote:
Do you plan to address Thierry's concerns and re-spin this patch?
Same question for patches:
"drm/bridge: Add i2c based driver for ptn3460 bridge" "drm/bridge: Add i2c based driver for ps8622/ps8625 bridge"
Yes, I will. I was caught up with some other work. I will be sending a version ASAP.
Great, glad to know that you will be doing a re-spin!
Ajay
Thanks a lot and best regards, Javier
Hi Ajay,
Thank you for the patch.
I think we're moving in the right direction, but we're not there yet.
On Saturday 26 July 2014 00:52:08 Ajay Kumar wrote:
This patch tries to seperate drm_bridge implementation into 2 parts, a drm part and a non_drm part.
A set of helper functions are defined in this patch to make bridge driver probe independent of the drm flow.
The bridge devices register themselves on a lookup table when they get probed by calling "drm_bridge_add_for_lookup".
The parent encoder driver waits till the bridge is available in the lookup table(by calling "of_drm_find_bridge") and then continues with its initialization.
Before the introduction of the component framework I would have said this is the way to go. Now, I think bridges should register themselves as components, and the DRM master driver should use the component framework to get a reference to the bridges it needs.
The encoder driver should call "drm_bridge_attach_encoder" to pass on the drm_device and the encoder pointers to the bridge object.
Now that the drm_device pointer is available, the encoder then calls "bridge->funcs->post_encoder_init" to allow the bridge to continue registering itself with the drm core.
This is what really bothers me with DRM bridge.
The framework assumes that a bridge will always bridge an encoder and a connector. Beside lacking support for chained bridges, this creates an artificial split between bridges and encoders by modeling the same components using drm_encoder or drm_bridge depending on their position in the video output pipeline.
I would like to see drm_bridge becoming more self-centric, removing the awareness of the upstream encoder and downstream connector. I'll give this a try, but it will conflict with this patch, so I'd like to share opinions and coordinate efforts sooner than later if possible.
Also, non driver model based ptn3460 driver is removed in this patch.
Signed-off-by: Ajay Kumar ajaykumar.rs@samsung.com
.../devicetree/bindings/drm/bridge/ptn3460.txt | 27 -- drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/bridge/Kconfig | 12 +- drivers/gpu/drm/bridge/Makefile | 2 - drivers/gpu/drm/bridge/ptn3460.c | 343 ----------------- drivers/gpu/drm/drm_bridge.c | 89 +++++ drivers/gpu/drm/drm_crtc.c | 9 +- drivers/gpu/drm/exynos/Kconfig | 3 +- drivers/gpu/drm/exynos/exynos_dp_core.c | 57 ++-- drivers/gpu/drm/exynos/exynos_dp_core.h | 1 + drivers/gpu/drm/msm/hdmi/hdmi_bridge.c | 3 +- include/drm/bridge/ptn3460.h | 37 --- include/drm/drm_crtc.h | 16 +- 13 files changed, 147 insertions(+), 453 deletions(-) delete mode 100644 Documentation/devicetree/bindings/drm/bridge/ptn3460.txt delete mode 100644 drivers/gpu/drm/bridge/ptn3460.c create mode 100644 drivers/gpu/drm/drm_bridge.c delete mode 100644 include/drm/bridge/ptn3460.h
Hi Laurent,
On Mon, Sep 15, 2014 at 11:07 PM, Laurent Pinchart laurent.pinchart@ideasonboard.com wrote:
Hi Ajay,
Thank you for the patch.
I think we're moving in the right direction, but we're not there yet.
On Saturday 26 July 2014 00:52:08 Ajay Kumar wrote:
This patch tries to seperate drm_bridge implementation into 2 parts, a drm part and a non_drm part.
A set of helper functions are defined in this patch to make bridge driver probe independent of the drm flow.
The bridge devices register themselves on a lookup table when they get probed by calling "drm_bridge_add_for_lookup".
The parent encoder driver waits till the bridge is available in the lookup table(by calling "of_drm_find_bridge") and then continues with its initialization.
Before the introduction of the component framework I would have said this is the way to go. Now, I think bridges should register themselves as components, and the DRM master driver should use the component framework to get a reference to the bridges it needs.
Well, I have modified the bridge framework exactly the way Thierry wanted it to be, I mean the same way the current panel framework is. And, I don't think there is a problem with that. What problem are you facing with current bridge implementation? What is the advantage of using the component framework to register bridges?
The encoder driver should call "drm_bridge_attach_encoder" to pass on the drm_device and the encoder pointers to the bridge object.
Now that the drm_device pointer is available, the encoder then calls "bridge->funcs->post_encoder_init" to allow the bridge to continue registering itself with the drm core.
This is what really bothers me with DRM bridge.
The framework assumes that a bridge will always bridge an encoder and a connector. Beside lacking support for chained bridges, this creates an artificial split between bridges and encoders by modeling the same components using drm_encoder or drm_bridge depending on their position in the video output pipeline.
I would like to see drm_bridge becoming more self-centric, removing the awareness of the upstream encoder and downstream connector. I'll give this a try, but it will conflict with this patch, so I'd like to share opinions and coordinate efforts sooner than later if possible.
I am not really able to understand how you want "drm_bridge" to be. As of now, there are many platforms using drm_bridge and they don't have a problem with current implementation. Regarding chained bridges: Can't you add this once my patchset is merged? As an additional feature?
To be honest, I have spent quite sometime for working on this patchset. All I started with was to add drm_panel support to drm_bridge. When I sent the first patchset for that, Daniel, Rob and Thierry raised a concern that current bridge framework itself is not proper and hence they asked me to fix that first. And we have reached till here based on their comments only.
Without this patchset, you cannot bring an X server based display on snow and peach_pit. Also, day by day the number of platforms using drm_bridge is increasing. And, I don't really see a problem with the current approach(which is exactly the same way panel framework is). And, I am no decision maker here. I would expect the top guys to comment!
Ajay
Also, non driver model based ptn3460 driver is removed in this patch.
Signed-off-by: Ajay Kumar ajaykumar.rs@samsung.com
.../devicetree/bindings/drm/bridge/ptn3460.txt | 27 -- drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/bridge/Kconfig | 12 +- drivers/gpu/drm/bridge/Makefile | 2 - drivers/gpu/drm/bridge/ptn3460.c | 343 ----------------- drivers/gpu/drm/drm_bridge.c | 89 +++++ drivers/gpu/drm/drm_crtc.c | 9 +- drivers/gpu/drm/exynos/Kconfig | 3 +- drivers/gpu/drm/exynos/exynos_dp_core.c | 57 ++-- drivers/gpu/drm/exynos/exynos_dp_core.h | 1 + drivers/gpu/drm/msm/hdmi/hdmi_bridge.c | 3 +- include/drm/bridge/ptn3460.h | 37 --- include/drm/drm_crtc.h | 16 +- 13 files changed, 147 insertions(+), 453 deletions(-) delete mode 100644 Documentation/devicetree/bindings/drm/bridge/ptn3460.txt delete mode 100644 drivers/gpu/drm/bridge/ptn3460.c create mode 100644 drivers/gpu/drm/drm_bridge.c delete mode 100644 include/drm/bridge/ptn3460.h
-- Regards,
Laurent Pinchart
Before the introduction of the component framework I would have said this is the way to go. Now, I think bridges should register themselves as components, and the DRM master driver should use the component framework to get a reference to the bridges it needs.
Well, I have modified the bridge framework exactly the way Thierry wanted it to be, I mean the same way the current panel framework is. And, I don't think there is a problem with that. What problem are you facing with current bridge implementation? What is the advantage of using the component framework to register bridges?
At this point I'd rather merge something that keep iterating out of tree, if this enables current hw to work and is better than what we have we should merge it, and if we can get interest in using the component framework then we should look at that as a separate problem.
Dave.
Hi Ajay,
On Wednesday 17 September 2014 14:37:30 Ajay kumar wrote:
On Mon, Sep 15, 2014 at 11:07 PM, Laurent Pinchart wrote:
Hi Ajay,
Thank you for the patch.
I think we're moving in the right direction, but we're not there yet.
On Saturday 26 July 2014 00:52:08 Ajay Kumar wrote:
This patch tries to seperate drm_bridge implementation into 2 parts, a drm part and a non_drm part.
A set of helper functions are defined in this patch to make bridge driver probe independent of the drm flow.
The bridge devices register themselves on a lookup table when they get probed by calling "drm_bridge_add_for_lookup".
The parent encoder driver waits till the bridge is available in the lookup table(by calling "of_drm_find_bridge") and then continues with its initialization.
Before the introduction of the component framework I would have said this is the way to go. Now, I think bridges should register themselves as components, and the DRM master driver should use the component framework to get a reference to the bridges it needs.
Well, I have modified the bridge framework exactly the way Thierry wanted it to be, I mean the same way the current panel framework is. And, I don't think there is a problem with that. What problem are you facing with current bridge implementation? What is the advantage of using the component framework to register bridges?
There are several advantages.
- The component framework has been designed with this exact problem in mind, piecing multiple components into a display device. This patch set introduces yet another framework, without any compelling reason as far as I can see. Today DRM drivers already need to use three different frameworks (component, I2C slave encoder and panel), and we're adding a fourth one to make the mess even messier. This is really a headlong rush, we need to stop and fix the design mistakes.
- The component framework solves the probe ordering problem. Bridges can use deferred probing, but when a bridge requires a resources (such as a clock for instance) provided by the display controller, this will break.
The encoder driver should call "drm_bridge_attach_encoder" to pass on the drm_device and the encoder pointers to the bridge object.
Now that the drm_device pointer is available, the encoder then calls "bridge->funcs->post_encoder_init" to allow the bridge to continue registering itself with the drm core.
This is what really bothers me with DRM bridge.
The framework assumes that a bridge will always bridge an encoder and a connector. Beside lacking support for chained bridges, this creates an artificial split between bridges and encoders by modeling the same components using drm_encoder or drm_bridge depending on their position in the video output pipeline.
I would like to see drm_bridge becoming more self-centric, removing the awareness of the upstream encoder and downstream connector. I'll give this a try, but it will conflict with this patch, so I'd like to share opinions and coordinate efforts sooner than later if possible.
I am not really able to understand how you want "drm_bridge" to be. As of now, there are many platforms using drm_bridge and they don't have a problem with current implementation. Regarding chained bridges: Can't you add this once my patchset is merged? As an additional feature?
Yes, as I mentioned in another e-mail this can be fixed later. I want to start discussing it though.
To be honest, I have spent quite sometime for working on this patchset. All I started with was to add drm_panel support to drm_bridge. When I sent the first patchset for that, Daniel, Rob and Thierry raised a concern that current bridge framework itself is not proper and hence they asked me to fix that first. And we have reached till here based on their comments only.
Without this patchset, you cannot bring an X server based display on snow and peach_pit. Also, day by day the number of platforms using drm_bridge is increasing.
That's exactly why I'd like to use the component framework now, as the conversion will become more complex as time goes by.
And, I don't really see a problem with the current approach(which is exactly the same way panel framework is). And, I am no decision maker here. I would expect the top guys to comment!
On Wed, Sep 17, 2014 at 2:57 PM, Laurent Pinchart laurent.pinchart@ideasonboard.com wrote:
Hi Ajay,
On Wednesday 17 September 2014 14:37:30 Ajay kumar wrote:
On Mon, Sep 15, 2014 at 11:07 PM, Laurent Pinchart wrote:
Hi Ajay,
Thank you for the patch.
I think we're moving in the right direction, but we're not there yet.
On Saturday 26 July 2014 00:52:08 Ajay Kumar wrote:
This patch tries to seperate drm_bridge implementation into 2 parts, a drm part and a non_drm part.
A set of helper functions are defined in this patch to make bridge driver probe independent of the drm flow.
The bridge devices register themselves on a lookup table when they get probed by calling "drm_bridge_add_for_lookup".
The parent encoder driver waits till the bridge is available in the lookup table(by calling "of_drm_find_bridge") and then continues with its initialization.
Before the introduction of the component framework I would have said this is the way to go. Now, I think bridges should register themselves as components, and the DRM master driver should use the component framework to get a reference to the bridges it needs.
Well, I have modified the bridge framework exactly the way Thierry wanted it to be, I mean the same way the current panel framework is. And, I don't think there is a problem with that. What problem are you facing with current bridge implementation? What is the advantage of using the component framework to register bridges?
There are several advantages.
- The component framework has been designed with this exact problem in mind,
piecing multiple components into a display device. This patch set introduces yet another framework, without any compelling reason as far as I can see.
Without this bridge registration framework, there is no way you can pass on a drm_device pointer to the bridge driver. That is why we added a lookup framework.
Today DRM drivers already need to use three different frameworks (component, I2C slave encoder and panel), and we're adding a fourth one to make the mess even messier. This is really a headlong rush, we need to stop and fix the design mistakes.
- The component framework solves the probe ordering problem. Bridges can use
deferred probing, but when a bridge requires a resources (such as a clock for instance) provided by the display controller, this will break.
This is exactly the way sti drm uses bridge I think. It uses component framework to wait till the master device initializes and then passes on a drm_device pointer to the component devices. But please know that it is feasible in case of sti, because the bridge they use must be a embedded chip on the SOC, but not a third party device. And, the case which you mentioned(clock instance need to be passed to bridge driver) happens only in the case of bridges embedded on the SOC, but not a third party device. So, you are always allowed to use component framework for that.
But, assume the bridge chip is a third party device(ex: ptn3460 or ps8622) which sits on an i2c bus. In that case, your approach poses the foll problems: The way master and components are binded varies from platform to platform. i.e the way exynos consolidates and adds the components is very much different the way msm/sti/armada does the same! So, when one needs to use a third party device as a bridge, they will end up hacking up their drm layer to support this third party device.
With my approach, only the corresponding encoder driver needs to know about the bridge, that too just the phandle to bridge node. The platform specific drm layer can still be unaware of the bridge and stuff.
With your approach, the way we would specify the bridge node will change from platform to platform resulting in non-uniformity. Also, the platform specific drm layer needs to be aware of the bridge.
And, I assume these are the reasons why drm_panel doesn't use component framework. Because all the panels are often third party and hence can be reused across platforms, and so are ptn3460/ps8622.
The encoder driver should call "drm_bridge_attach_encoder" to pass on the drm_device and the encoder pointers to the bridge object.
Now that the drm_device pointer is available, the encoder then calls "bridge->funcs->post_encoder_init" to allow the bridge to continue registering itself with the drm core.
This is what really bothers me with DRM bridge.
The framework assumes that a bridge will always bridge an encoder and a connector. Beside lacking support for chained bridges, this creates an artificial split between bridges and encoders by modeling the same components using drm_encoder or drm_bridge depending on their position in the video output pipeline.
I would like to see drm_bridge becoming more self-centric, removing the awareness of the upstream encoder and downstream connector. I'll give this a try, but it will conflict with this patch, so I'd like to share opinions and coordinate efforts sooner than later if possible.
I am not really able to understand how you want "drm_bridge" to be. As of now, there are many platforms using drm_bridge and they don't have a problem with current implementation. Regarding chained bridges: Can't you add this once my patchset is merged? As an additional feature?
Yes, as I mentioned in another e-mail this can be fixed later. I want to start discussing it though.
To be honest, I have spent quite sometime for working on this patchset. All I started with was to add drm_panel support to drm_bridge. When I sent the first patchset for that, Daniel, Rob and Thierry raised a concern that current bridge framework itself is not proper and hence they asked me to fix that first. And we have reached till here based on their comments only.
Without this patchset, you cannot bring an X server based display on snow and peach_pit. Also, day by day the number of platforms using drm_bridge is increasing.
That's exactly why I'd like to use the component framework now, as the conversion will become more complex as time goes by.
As I have explained above, using component framework for third party bridges is a bad idea. And all other private bridges, already use the component framework(ex: sti). So, ideally this point should not be a blocker for this patchset to get in. :)
Ajay
On Wed, Sep 17, 2014 at 12:27:13PM +0300, Laurent Pinchart wrote:
Hi Ajay,
On Wednesday 17 September 2014 14:37:30 Ajay kumar wrote:
On Mon, Sep 15, 2014 at 11:07 PM, Laurent Pinchart wrote:
Hi Ajay,
Thank you for the patch.
I think we're moving in the right direction, but we're not there yet.
On Saturday 26 July 2014 00:52:08 Ajay Kumar wrote:
This patch tries to seperate drm_bridge implementation into 2 parts, a drm part and a non_drm part.
A set of helper functions are defined in this patch to make bridge driver probe independent of the drm flow.
The bridge devices register themselves on a lookup table when they get probed by calling "drm_bridge_add_for_lookup".
The parent encoder driver waits till the bridge is available in the lookup table(by calling "of_drm_find_bridge") and then continues with its initialization.
Before the introduction of the component framework I would have said this is the way to go. Now, I think bridges should register themselves as components, and the DRM master driver should use the component framework to get a reference to the bridges it needs.
Well, I have modified the bridge framework exactly the way Thierry wanted it to be, I mean the same way the current panel framework is. And, I don't think there is a problem with that. What problem are you facing with current bridge implementation? What is the advantage of using the component framework to register bridges?
There are several advantages.
- The component framework has been designed with this exact problem in mind,
piecing multiple components into a display device.
No. Component framework was designed with multi-device drivers in mind. That is, drivers that need to combine two or more platform devices into a single logical device. Typically that includes display controllers and encoders (in various looks) for DRM.
Panels and bridges are in my opinion different because they are outside of the DRM driver. They aren't part of the device complex that an SoC provides. They represent hardware that is external to the SoC and the DRM driver and can be shared across SoCs.
Forcing panels and bridges to register as components will require all drivers to implement master/component support solely for accessing this external hardware.
What you're suggesting is like saying that clocks or regulators should register as components so that their users can get them that way. In fact by that argument everything that's referenced by phandle would need to register as component (PHYs, LEDs, GPIOs, I2C controllers, ...).
This patch set introduces yet another framework, without any compelling reason as far as I can see. Today DRM drivers already need to use three different frameworks (component, I2C slave encoder and panel), and we're adding a fourth one to make the mess even messier.
Panel and bridge aren't really frameworks. Rather they are a simple registry to allow drivers to register panels and bridges and display drivers to look them up.
This is really a headlong rush, we need to stop and fix the design mistakes.
Can you point out specific design mistakes? I don't see any, but I'm obviously biased.
- The component framework solves the probe ordering problem. Bridges can use
deferred probing, but when a bridge requires a resources (such as a clock for instance) provided by the display controller, this will break.
Panel and bridges can support deferred probing without the component framework just fine.
Without this patchset, you cannot bring an X server based display on snow and peach_pit. Also, day by day the number of platforms using drm_bridge is increasing.
That's exactly why I'd like to use the component framework now, as the conversion will become more complex as time goes by.
No it won't. If we ever do decide that component framework is a better fit then the conversion may be more work but it would still be largely mechanical.
Thierry
Hi Thierry,
On Monday 22 September 2014 09:40:38 Thierry Reding wrote:
On Wed, Sep 17, 2014 at 12:27:13PM +0300, Laurent Pinchart wrote:
On Wednesday 17 September 2014 14:37:30 Ajay kumar wrote:
On Mon, Sep 15, 2014 at 11:07 PM, Laurent Pinchart wrote:
Hi Ajay,
Thank you for the patch.
I think we're moving in the right direction, but we're not there yet.
On Saturday 26 July 2014 00:52:08 Ajay Kumar wrote:
This patch tries to seperate drm_bridge implementation into 2 parts, a drm part and a non_drm part.
A set of helper functions are defined in this patch to make bridge driver probe independent of the drm flow.
The bridge devices register themselves on a lookup table when they get probed by calling "drm_bridge_add_for_lookup".
The parent encoder driver waits till the bridge is available in the lookup table(by calling "of_drm_find_bridge") and then continues with its initialization.
Before the introduction of the component framework I would have said this is the way to go. Now, I think bridges should register themselves as components, and the DRM master driver should use the component framework to get a reference to the bridges it needs.
Well, I have modified the bridge framework exactly the way Thierry wanted it to be, I mean the same way the current panel framework is. And, I don't think there is a problem with that. What problem are you facing with current bridge implementation? What is the advantage of using the component framework to register bridges?
There are several advantages.
- The component framework has been designed with this exact problem in
mind, piecing multiple components into a display device.
No. Component framework was designed with multi-device drivers in mind. That is, drivers that need to combine two or more platform devices into a single logical device. Typically that includes display controllers and encoders (in various looks) for DRM.
I disagree. AFAIK the component framework was designed to easily combine multiple devices into a single logical device, regardless of which bus each device is connected to. That's what makes the component framework useful : it allows master drivers to build logical devices from heterogeneous components without having to use one API per bus and/or component type. If the only goal had been to combine platform devices on an SoC, simpler device-specific solutions would likely have been used instead.
Panels and bridges are in my opinion different because they are outside of the DRM driver. They aren't part of the device complex that an SoC provides. They represent hardware that is external to the SoC and the DRM driver and can be shared across SoCs.
They represent hardware external to the SoC, but internal to the logical DRM device.
Forcing panels and bridges to register as components will require all drivers to implement master/component support solely for accessing this external hardware.
What you're suggesting is like saying that clocks or regulators should register as components so that their users can get them that way. In fact by that argument everything that's referenced by phandle would need to register as component (PHYs, LEDs, GPIOs, I2C controllers, ...).
No, that's very different. The device you list are clearly external resources, while the bridges and panels are components part of a logical display device.
This patch set introduces yet another framework, without any compelling reason as far as I can see. Today DRM drivers already need to use three different frameworks (component, I2C slave encoder and panel), and we're adding a fourth oneto make the mess even messier.
Panel and bridge aren't really frameworks. Rather they are a simple registry to allow drivers to register panels and bridges and display drivers to look them up.
Regardless of how you call them, we have three interfaces.
This is really a headlong rush, we need to stop and fix the design mistakes.
Can you point out specific design mistakes? I don't see any, but I'm obviously biased.
The slave encoder / bridge split is what I consider a design mistake. Those two interfaces serve the same purpose, they should really be merged.
- The component framework solves the probe ordering problem. Bridges can
use deferred probing, but when a bridge requires a resources (such as a clock for instance) provided by the display controller, this will break.
Panel and bridges can support deferred probing without the component framework just fine.
Not if the bridge requires a clock provided by the display controller, in which case there's a dependency loop.
Without this patchset, you cannot bring an X server based display on snow and peach_pit. Also, day by day the number of platforms using drm_bridge is increasing.
That's exactly why I'd like to use the component framework now, as the conversion will become more complex as time goes by.
No it won't. If we ever do decide that component framework is a better fit then the conversion may be more work but it would still be largely mechanical.
Are you volunteering to perform the conversion ? :-)
On Tue, Sep 23, 2014 at 03:29:13AM +0300, Laurent Pinchart wrote:
Hi Thierry,
On Monday 22 September 2014 09:40:38 Thierry Reding wrote:
On Wed, Sep 17, 2014 at 12:27:13PM +0300, Laurent Pinchart wrote:
On Wednesday 17 September 2014 14:37:30 Ajay kumar wrote:
On Mon, Sep 15, 2014 at 11:07 PM, Laurent Pinchart wrote:
Hi Ajay,
Thank you for the patch.
I think we're moving in the right direction, but we're not there yet.
On Saturday 26 July 2014 00:52:08 Ajay Kumar wrote:
This patch tries to seperate drm_bridge implementation into 2 parts, a drm part and a non_drm part.
A set of helper functions are defined in this patch to make bridge driver probe independent of the drm flow.
The bridge devices register themselves on a lookup table when they get probed by calling "drm_bridge_add_for_lookup".
The parent encoder driver waits till the bridge is available in the lookup table(by calling "of_drm_find_bridge") and then continues with its initialization.
Before the introduction of the component framework I would have said this is the way to go. Now, I think bridges should register themselves as components, and the DRM master driver should use the component framework to get a reference to the bridges it needs.
Well, I have modified the bridge framework exactly the way Thierry wanted it to be, I mean the same way the current panel framework is. And, I don't think there is a problem with that. What problem are you facing with current bridge implementation? What is the advantage of using the component framework to register bridges?
There are several advantages.
- The component framework has been designed with this exact problem in
mind, piecing multiple components into a display device.
No. Component framework was designed with multi-device drivers in mind. That is, drivers that need to combine two or more platform devices into a single logical device. Typically that includes display controllers and encoders (in various looks) for DRM.
I disagree. AFAIK the component framework was designed to easily combine multiple devices into a single logical device, regardless of which bus each device is connected to. That's what makes the component framework useful : it allows master drivers to build logical devices from heterogeneous components without having to use one API per bus and/or component type.
But this doesn't work really well once you leave the SoC. For component/ master to work you need to usually (i.e. using DT) have access to a device tree node for each of the devices so that you can create a list of needed devices. Once you leave the SoC, the number of combinations that you can have becomes non-deterministic. A driver that wants to pull this off would need to more or less manually look up phandles and traverse from SoC via bridges to panels to find all the devices that make up the logical device.
If the only goal
had been to combine platform devices on an SoC, simpler device-specific solutions would likely have been used instead.
I think one of the goals was to replace any of the simpler device- specific solutions with a generic one.
But one of the issues with component/master is that it's viral. That is it requires users to register as master themselves in order to pull in the components. While that makes sense for on-SoC devices I think it's a mistake to use it for external hardware like bridges and panels that can be shared across SoCs. If we make component/master mandatory for bridges and panels, then we also force every driver that wants to use them to implement component/master support.
Furthermore I did try a while back to convert the Tegra DRM driver to use component/master and couldn't make it work. When I proposed patches to enhance the API so that it would work for Tegra I got silence on one side and "just keep using what you currently have" on the other side. So in other words since I can't use component/master on Tegra it means that whatever driver gets added that registers a component can't be used on Tegra either.
Panels and bridges are in my opinion different because they are outside of the DRM driver. They aren't part of the device complex that an SoC provides. They represent hardware that is external to the SoC and the DRM driver and can be shared across SoCs.
They represent hardware external to the SoC, but internal to the logical DRM device.
That depends largely on how you look at it. From my point of view the logical DRM device stops at the connector (well I think it actually stops somewhat earlier already, with connector only being the handle through which the outputs are configured).
Panels are, at least theoretically, hotpluggable. That is, if you have a device that has both a panel and HDMI but you never use HDMI, then you should be able to just unload the panel driver but keep the DRM device running for HDMI. If you use component/master that's impossible because as soon as the panel component goes away the complete DRM device goes away.
Forcing panels and bridges to register as components will require all drivers to implement master/component support solely for accessing this external hardware.
What you're suggesting is like saying that clocks or regulators should register as components so that their users can get them that way. In fact by that argument everything that's referenced by phandle would need to register as component (PHYs, LEDs, GPIOs, I2C controllers, ...).
No, that's very different. The device you list are clearly external resources, while the bridges and panels are components part of a logical display device.
Like I said above, I consider bridges and panels external resources, too. I can easily imagine (actually I don't have to because it was recently discussed for a project) setups where some board defines a standard header that display modules can be connected to and hence whatever bridges or panels are attached can change at runtime.
This patch set introduces yet another framework, without any compelling reason as far as I can see. Today DRM drivers already need to use three different frameworks (component, I2C slave encoder and panel), and we're adding a fourth oneto make the mess even messier.
Panel and bridge aren't really frameworks. Rather they are a simple registry to allow drivers to register panels and bridges and display drivers to look them up.
Regardless of how you call them, we have three interfaces.
We need an interface to control the bridges and panels anyway. component and master only gives you a generic way to handle the registration and initialization.
Adding a drm_*_lookup() function to the interface to retrieve a resource isn't as bloated as you make it out to be. Implementing component/master is much more complicated for (in this case) too little advantage.
This is really a headlong rush, we need to stop and fix the design mistakes.
Can you point out specific design mistakes? I don't see any, but I'm obviously biased.
The slave encoder / bridge split is what I consider a design mistake. Those two interfaces serve the same purpose, they should really be merged.
Agreed.
- The component framework solves the probe ordering problem. Bridges can
use deferred probing, but when a bridge requires a resources (such as a clock for instance) provided by the display controller, this will break.
Panel and bridges can support deferred probing without the component framework just fine.
Not if the bridge requires a clock provided by the display controller, in which case there's a dependency loop.
I don't see how component/master would solve that differently than the current proposal for DRM bridge (or the existing DRM panel for that matter).
Without this patchset, you cannot bring an X server based display on snow and peach_pit. Also, day by day the number of platforms using drm_bridge is increasing.
That's exactly why I'd like to use the component framework now, as the conversion will become more complex as time goes by.
No it won't. If we ever do decide that component framework is a better fit then the conversion may be more work but it would still be largely mechanical.
Are you volunteering to perform the conversion ? :-)
No. I'm still convinced that we won't need it. Less work for everyone. =)
Thierry
From: Sean Paul seanpaul@chromium.org
This patch adds ptn3460 as module_i2c_driver.
Signed-off-by: Sean Paul seanpaul@chromium.org Signed-off-by: Ajay Kumar ajaykumar.rs@samsung.com --- .../devicetree/bindings/video/bridge/ptn3460.txt | 27 ++ drivers/gpu/drm/bridge/Kconfig | 10 + drivers/gpu/drm/bridge/Makefile | 2 + drivers/gpu/drm/bridge/ptn3460.c | 405 ++++++++++++++++++++ 4 files changed, 444 insertions(+) create mode 100644 Documentation/devicetree/bindings/video/bridge/ptn3460.txt create mode 100644 drivers/gpu/drm/bridge/ptn3460.c
diff --git a/Documentation/devicetree/bindings/video/bridge/ptn3460.txt b/Documentation/devicetree/bindings/video/bridge/ptn3460.txt new file mode 100644 index 0000000..03366c4 --- /dev/null +++ b/Documentation/devicetree/bindings/video/bridge/ptn3460.txt @@ -0,0 +1,27 @@ +ptn3460 bridge bindings + +Required properties: + - compatible: "nxp,ptn3460" + - reg: i2c address of the bridge + - powerdown-gpios: OF device-tree gpio specification + - reset-gpios: OF device-tree gpio specification + - edid-emulation: The EDID emulation entry to use + +-------+------------+------------------+ + | Value | Resolution | Description | + | 0 | 1024x768 | NXP Generic | + | 1 | 1920x1080 | NXP Generic | + | 2 | 1920x1080 | NXP Generic | + | 3 | 1600x900 | Samsung LTM200KT | + | 4 | 1920x1080 | Samsung LTM230HT | + | 5 | 1366x768 | NXP Generic | + | 6 | 1600x900 | ChiMei M215HGE | + +-------+------------+------------------+ + +Example: + lvds-bridge@20 { + compatible = "nxp,ptn3460"; + reg = <0x20>; + powerdown-gpios = <&gpy2 5 1 0 0>; + reset-gpios = <&gpx1 5 1 0 0>; + edid-emulation = <5>; + }; diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig index 1e2f96c..0b12d16 100644 --- a/drivers/gpu/drm/bridge/Kconfig +++ b/drivers/gpu/drm/bridge/Kconfig @@ -6,4 +6,14 @@ config DRM_BRIDGE
menu "bridge chips" depends on DRM_BRIDGE + +config DRM_PTN3460 + tristate "NXP ptn3460 eDP/LVDS bridge" + depends on DRM && DRM_BRIDGE + depends on OF + depends on I2C + select DRM_PANEL + help + ptn3460 eDP-LVDS bridge chip driver. + endmenu diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile index be16eca..b4733e1 100644 --- a/drivers/gpu/drm/bridge/Makefile +++ b/drivers/gpu/drm/bridge/Makefile @@ -1 +1,3 @@ ccflags-y := -Iinclude/drm + +obj-$(CONFIG_DRM_PTN3460) += ptn3460.o diff --git a/drivers/gpu/drm/bridge/ptn3460.c b/drivers/gpu/drm/bridge/ptn3460.c new file mode 100644 index 0000000..f41302a --- /dev/null +++ b/drivers/gpu/drm/bridge/ptn3460.c @@ -0,0 +1,405 @@ +/* + * NXP PTN3460 DP/LVDS bridge driver + * + * Copyright (C) 2013 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_gpio.h> +#include <linux/i2c.h> +#include <linux/gpio.h> +#include <linux/delay.h> +#include <drm/drm_panel.h> + +#include "drmP.h" +#include "drm_edid.h" +#include "drm_crtc.h" +#include "drm_crtc_helper.h" + +#define PTN3460_EDID_ADDR 0x0 +#define PTN3460_EDID_EMULATION_ADDR 0x84 +#define PTN3460_EDID_ENABLE_EMULATION 0 +#define PTN3460_EDID_EMULATION_SELECTION 1 +#define PTN3460_EDID_SRAM_LOAD_ADDR 0x85 + +struct ptn3460_bridge { + struct drm_connector connector; + struct i2c_client *client; + struct drm_bridge *bridge; + struct drm_panel *panel; + struct edid *edid; + struct gpio_desc *gpio_pd_n; + struct gpio_desc *gpio_rst_n; + u32 edid_emulation; + bool enabled; +}; + +static int ptn3460_read_bytes(struct ptn3460_bridge *ptn_bridge, char addr, + u8 *buf, int len) +{ + int ret; + + ret = i2c_master_send(ptn_bridge->client, &addr, 1); + if (ret <= 0) { + DRM_ERROR("Failed to send i2c command, ret=%d\n", ret); + return ret; + } + + ret = i2c_master_recv(ptn_bridge->client, buf, len); + if (ret <= 0) { + DRM_ERROR("Failed to recv i2c data, ret=%d\n", ret); + return ret; + } + + return 0; +} + +static int ptn3460_write_byte(struct ptn3460_bridge *ptn_bridge, char addr, + char val) +{ + int ret; + char buf[2]; + + buf[0] = addr; + buf[1] = val; + + ret = i2c_master_send(ptn_bridge->client, buf, ARRAY_SIZE(buf)); + if (ret <= 0) { + DRM_ERROR("Failed to send i2c command, ret=%d\n", ret); + return ret; + } + + return 0; +} + +static int ptn3460_select_edid(struct ptn3460_bridge *ptn_bridge) +{ + int ret; + char val; + + /* Load the selected edid into SRAM (accessed at PTN3460_EDID_ADDR) */ + ret = ptn3460_write_byte(ptn_bridge, PTN3460_EDID_SRAM_LOAD_ADDR, + ptn_bridge->edid_emulation); + if (ret) { + DRM_ERROR("Failed to transfer edid to sram, ret=%d\n", ret); + return ret; + } + + /* Enable EDID emulation and select the desired EDID */ + val = 1 << PTN3460_EDID_ENABLE_EMULATION | + ptn_bridge->edid_emulation << PTN3460_EDID_EMULATION_SELECTION; + + ret = ptn3460_write_byte(ptn_bridge, PTN3460_EDID_EMULATION_ADDR, val); + if (ret) { + DRM_ERROR("Failed to write edid value, ret=%d\n", ret); + return ret; + } + + return 0; +} + +static void ptn3460_pre_enable(struct drm_bridge *bridge) +{ + struct ptn3460_bridge *ptn_bridge = bridge->driver_private; + int ret; + + if (ptn_bridge->enabled) + return; + + gpiod_set_value(ptn_bridge->gpio_pd_n, 1); + + gpiod_set_value(ptn_bridge->gpio_rst_n, 0); + udelay(10); + gpiod_set_value(ptn_bridge->gpio_rst_n, 1); + + drm_panel_prepare(ptn_bridge->panel); + + /* + * 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 + * time specified in the chip's datasheet to make sure we're really up. + */ + msleep(90); + + ret = ptn3460_select_edid(ptn_bridge); + if (ret) + DRM_ERROR("Select edid failed ret=%d\n", ret); + + ptn_bridge->enabled = true; +} + +static void ptn3460_enable(struct drm_bridge *bridge) +{ + struct ptn3460_bridge *ptn_bridge = bridge->driver_private; + + drm_panel_enable(ptn_bridge->panel); +} + +static void ptn3460_disable(struct drm_bridge *bridge) +{ + struct ptn3460_bridge *ptn_bridge = bridge->driver_private; + + if (!ptn_bridge->enabled) + return; + + ptn_bridge->enabled = false; + + drm_panel_disable(ptn_bridge->panel); + + 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->driver_private; + + drm_panel_unprepare(ptn_bridge->panel); +} + +void ptn3460_bridge_destroy(struct drm_bridge *bridge) +{ + drm_bridge_cleanup(bridge); +} + +int ptn3460_get_modes(struct drm_connector *connector) +{ + struct ptn3460_bridge *ptn_bridge; + u8 *edid; + int ret, num_modes; + bool power_off; + + ptn_bridge = container_of(connector, struct ptn3460_bridge, connector); + + if (ptn_bridge->edid) + return drm_add_edid_modes(connector, ptn_bridge->edid); + + power_off = !ptn_bridge->enabled; + ptn3460_pre_enable(ptn_bridge->bridge); + + edid = kmalloc(EDID_LENGTH, GFP_KERNEL); + if (!edid) { + DRM_ERROR("Failed to allocate edid\n"); + return 0; + } + + ret = ptn3460_read_bytes(ptn_bridge, PTN3460_EDID_ADDR, edid, + EDID_LENGTH); + if (ret) { + kfree(edid); + num_modes = 0; + goto out; + } + + ptn_bridge->edid = (struct edid *)edid; + drm_mode_connector_update_edid_property(connector, ptn_bridge->edid); + + num_modes = drm_add_edid_modes(connector, ptn_bridge->edid); + +out: + if (power_off) + ptn3460_disable(ptn_bridge->bridge); + + return num_modes; +} + +struct drm_encoder *ptn3460_best_encoder(struct drm_connector *connector) +{ + struct ptn3460_bridge *ptn_bridge; + + ptn_bridge = container_of(connector, struct ptn3460_bridge, connector); + + return ptn_bridge->bridge->encoder; +} + +struct drm_connector_helper_funcs ptn3460_connector_helper_funcs = { + .get_modes = ptn3460_get_modes, + .best_encoder = ptn3460_best_encoder, +}; + +enum drm_connector_status ptn3460_detect(struct drm_connector *connector, + bool force) +{ + return connector_status_connected; +} + +void ptn3460_connector_destroy(struct drm_connector *connector) +{ + drm_connector_cleanup(connector); +} + +struct drm_connector_funcs ptn3460_connector_funcs = { + .dpms = drm_helper_connector_dpms, + .fill_modes = drm_helper_probe_single_connector_modes, + .detect = ptn3460_detect, + .destroy = ptn3460_connector_destroy, +}; + +int ptn3460_post_encoder_init(struct drm_bridge *bridge) +{ + struct ptn3460_bridge *ptn_bridge = bridge->driver_private; + int ret; + + /* bridge is attached to encoder. + * safe to remove it from the bridge_lookup table. + */ + drm_bridge_remove_from_lookup(bridge); + + ret = drm_bridge_init(bridge->drm_dev, bridge); + if (ret) { + DRM_ERROR("Failed to initialize bridge with drm\n"); + return ret; + } + + /* connector implementation */ + ptn_bridge->connector.polled = bridge->connector_polled; + + ret = drm_connector_init(bridge->drm_dev, &ptn_bridge->connector, + &ptn3460_connector_funcs, DRM_MODE_CONNECTOR_LVDS); + if (ret) { + DRM_ERROR("Failed to initialize connector with drm\n"); + return ret; + } + drm_connector_helper_add(&ptn_bridge->connector, + &ptn3460_connector_helper_funcs); + drm_connector_register(&ptn_bridge->connector); + drm_mode_connector_attach_encoder(&ptn_bridge->connector, + bridge->encoder); + + if (ptn_bridge->panel) + drm_panel_attach(ptn_bridge->panel, &ptn_bridge->connector); + + return ret; +} + +struct drm_bridge_funcs ptn3460_bridge_funcs = { + .post_encoder_init = ptn3460_post_encoder_init, + .pre_enable = ptn3460_pre_enable, + .enable = ptn3460_enable, + .disable = ptn3460_disable, + .post_disable = ptn3460_post_disable, + .destroy = ptn3460_bridge_destroy, +}; + +static int ptn3460_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &(client->dev); + struct device_node *panel_node; + struct drm_bridge *bridge; + struct ptn3460_bridge *ptn_bridge; + int ret; + + bridge = devm_kzalloc(dev, sizeof(*bridge), GFP_KERNEL); + if (!bridge) { + DRM_ERROR("Failed to allocate drm bridge\n"); + return -ENOMEM; + } + + ptn_bridge = devm_kzalloc(dev, sizeof(*ptn_bridge), GFP_KERNEL); + if (!ptn_bridge) { + DRM_ERROR("Failed to allocate ptn bridge\n"); + return -ENOMEM; + } + + panel_node = of_parse_phandle(dev->of_node, "panel", 0); + if (panel_node) { + ptn_bridge->panel = of_drm_find_panel(panel_node); + of_node_put(panel_node); + if (!ptn_bridge->panel) + return -EPROBE_DEFER; + } + + bridge->dev = dev; + bridge->driver_private = ptn_bridge; + bridge->funcs = &ptn3460_bridge_funcs; + + ptn_bridge->client = client; + ptn_bridge->bridge = bridge; + + ptn_bridge->gpio_pd_n = devm_gpiod_get(&client->dev, "powerdown"); + if (IS_ERR(ptn_bridge->gpio_pd_n)) { + ret = PTR_ERR(ptn_bridge->gpio_pd_n); + DRM_ERROR("cannot get gpio_pd_n %d\n", ret); + return ret; + } else { + ret = gpiod_direction_output(ptn_bridge->gpio_pd_n, 1); + if (ret) { + DRM_ERROR("cannot configure gpio_pd_n\n"); + return ret; + } + } + + ptn_bridge->gpio_rst_n = devm_gpiod_get(&client->dev, "reset"); + if (IS_ERR(ptn_bridge->gpio_rst_n)) { + ret = PTR_ERR(ptn_bridge->gpio_rst_n); + DRM_ERROR("cannot get gpio_rst_n %d\n", ret); + return ret; + } else { + /* + * Request the reset pin low to avoid the bridge being + * initialized prematurely + */ + ret = gpiod_direction_output(ptn_bridge->gpio_rst_n, 0); + if (ret) { + DRM_ERROR("cannot configure gpio_pd_n\n"); + return ret; + } + } + + ret = of_property_read_u32(dev->of_node, "edid-emulation", + &ptn_bridge->edid_emulation); + if (ret) { + DRM_ERROR("Can't read edid emulation value\n"); + return -ENODEV; + } + + i2c_set_clientdata(client, ptn_bridge); + + drm_bridge_add_for_lookup(bridge); + + return 0; +} + +static int ptn3460_remove(struct i2c_client *client) +{ + return 0; +} + +static const struct i2c_device_id ptn3460_i2c_table[] = { + {"nxp,ptn3460", 0}, + {}, +}; +MODULE_DEVICE_TABLE(i2c, ptn3460_i2c_table); + +static const struct of_device_id ptn3460_match[] = { + { .compatible = "nxp,ptn3460" }, + {}, +}; +MODULE_DEVICE_TABLE(of, ptn3460_match); + +struct i2c_driver ptn3460_driver = { + .id_table = ptn3460_i2c_table, + .probe = ptn3460_probe, + .remove = ptn3460_remove, + .driver = { + .name = "nxp,ptn3460", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(ptn3460_match), + }, +}; +module_i2c_driver(ptn3460_driver); + +MODULE_AUTHOR("Sean Paul seanpaul@chromium.org"); +MODULE_DESCRIPTION("NXP ptn3460 eDP-LVDS converter driver"); +MODULE_LICENSE("GPL");
On Sat, Jul 26, 2014 at 12:52:09AM +0530, Ajay Kumar wrote: [...]
diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig index 1e2f96c..0b12d16 100644 --- a/drivers/gpu/drm/bridge/Kconfig +++ b/drivers/gpu/drm/bridge/Kconfig @@ -6,4 +6,14 @@ config DRM_BRIDGE
menu "bridge chips" depends on DRM_BRIDGE
+config DRM_PTN3460
- tristate "NXP ptn3460 eDP/LVDS bridge"
- depends on DRM && DRM_BRIDGE
I don't think you need these two dependencies any longer since they are implicit in the "bridge chips" menu.
diff --git a/drivers/gpu/drm/bridge/ptn3460.c b/drivers/gpu/drm/bridge/ptn3460.c
[...]
+#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_gpio.h> +#include <linux/i2c.h> +#include <linux/gpio.h> +#include <linux/delay.h> +#include <drm/drm_panel.h>
These should be ordered alphabetically, but they are already this way in the original driver, so the reordering can be a separate patch.
+struct ptn3460_bridge {
- struct drm_connector connector;
- struct i2c_client *client;
- struct drm_bridge *bridge;
I think it would be much more natural for ptn3460_bridge to embed struct drm_bridge rather than store a pointer to it.
- struct drm_panel *panel;
- struct edid *edid;
- struct gpio_desc *gpio_pd_n;
- struct gpio_desc *gpio_rst_n;
- u32 edid_emulation;
- bool enabled;
+};
+static int ptn3460_read_bytes(struct ptn3460_bridge *ptn_bridge, char addr,
u8 *buf, int len)
+{
- int ret;
- ret = i2c_master_send(ptn_bridge->client, &addr, 1);
- if (ret <= 0) {
DRM_ERROR("Failed to send i2c command, ret=%d\n", ret);
return ret;
- }
- ret = i2c_master_recv(ptn_bridge->client, buf, len);
- if (ret <= 0) {
DRM_ERROR("Failed to recv i2c data, ret=%d\n", ret);
return ret;
- }
This isn't introduced by this patch, but doesn't this require locking so that this is an atomic transaction?
Perhaps it could be rewritten using i2c_smbus_read_block_data()?
+static int ptn3460_write_byte(struct ptn3460_bridge *ptn_bridge, char addr,
char val)
+{
- int ret;
- char buf[2];
- buf[0] = addr;
- buf[1] = val;
- ret = i2c_master_send(ptn_bridge->client, buf, ARRAY_SIZE(buf));
- if (ret <= 0) {
DRM_ERROR("Failed to send i2c command, ret=%d\n", ret);
return ret;
- }
- return 0;
+}
Same here, this looks like it could be i2c_smbus_write_byte_data().
+static int ptn3460_select_edid(struct ptn3460_bridge *ptn_bridge) +{
- int ret;
- char val;
- /* Load the selected edid into SRAM (accessed at PTN3460_EDID_ADDR) */
- ret = ptn3460_write_byte(ptn_bridge, PTN3460_EDID_SRAM_LOAD_ADDR,
ptn_bridge->edid_emulation);
- if (ret) {
DRM_ERROR("Failed to transfer edid to sram, ret=%d\n", ret);
return ret;
- }
- /* Enable EDID emulation and select the desired EDID */
- val = 1 << PTN3460_EDID_ENABLE_EMULATION |
ptn_bridge->edid_emulation << PTN3460_EDID_EMULATION_SELECTION;
- ret = ptn3460_write_byte(ptn_bridge, PTN3460_EDID_EMULATION_ADDR, val);
- if (ret) {
DRM_ERROR("Failed to write edid value, ret=%d\n", ret);
return ret;
- }
- return 0;
+}
s/edid/EDID/ in the above (and below, too), but again the original driver had this, so it can be a separate patch.
+static void ptn3460_pre_enable(struct drm_bridge *bridge) +{
- struct ptn3460_bridge *ptn_bridge = bridge->driver_private;
If you embed drm_bridge within ptn3460_bridge then you can use the much more canonical container_of() macro (or rather a driver-specific inline function that wraps it) and no longer need the drm_bridge.driver_private field.
- int ret;
- if (ptn_bridge->enabled)
return;
- gpiod_set_value(ptn_bridge->gpio_pd_n, 1);
- gpiod_set_value(ptn_bridge->gpio_rst_n, 0);
- udelay(10);
This shouldn't be using udelay(), usleep_range(10, 20) (or similar) would be better. Again, can be a separate patch.
- gpiod_set_value(ptn_bridge->gpio_rst_n, 1);
It also seems like you've converted to using the gpiod_*() API, but the driver previously used gpio_is_valid() to check that both PD and RST pins had valid GPIOs associated with them. The device tree binding said that they are required, though.
So this patch actually does the right thing by making them non-optional but it also changes behaviour from the original. Like I said earlier, I would very much prefer that this conversion be split into separate patches rather than one patch that removes the old driver and a second patch that adds a new one. It makes it really difficult to tell what's really changing, breaks bisectability and generally makes our lives miserable.
- drm_panel_prepare(ptn_bridge->panel);
This should check for errors.
+static void ptn3460_enable(struct drm_bridge *bridge) +{
- struct ptn3460_bridge *ptn_bridge = bridge->driver_private;
- drm_panel_enable(ptn_bridge->panel);
Should check for errors as well.
+int ptn3460_get_modes(struct drm_connector *connector)
static? There seem to be quite a few functions that can be locally scoped. Again, this seems to be the case in the original driver as but it should definitely be fixed at some point.
+{
- struct ptn3460_bridge *ptn_bridge;
- u8 *edid;
- int ret, num_modes;
- bool power_off;
- ptn_bridge = container_of(connector, struct ptn3460_bridge, connector);
- if (ptn_bridge->edid)
return drm_add_edid_modes(connector, ptn_bridge->edid);
- power_off = !ptn_bridge->enabled;
- ptn3460_pre_enable(ptn_bridge->bridge);
- edid = kmalloc(EDID_LENGTH, GFP_KERNEL);
- if (!edid) {
DRM_ERROR("Failed to allocate edid\n");
return 0;
- }
- ret = ptn3460_read_bytes(ptn_bridge, PTN3460_EDID_ADDR, edid,
EDID_LENGTH);
- if (ret) {
kfree(edid);
num_modes = 0;
Maybe instead of doing this here you can initialize the variable when you declare it? It's always been that way, so can be a separate patch, too.
+struct drm_encoder *ptn3460_best_encoder(struct drm_connector *connector) +{
- struct ptn3460_bridge *ptn_bridge;
- ptn_bridge = container_of(connector, struct ptn3460_bridge, connector);
You use this long construct a couple of times, so it's useful to introduce a helper, such as this:
static inline struct ptn3460_bridge * connector_to_ptn3460(struct drm_connector *connector) { return container_of(connector, struct ptn3460_bridge, connector); }
+int ptn3460_post_encoder_init(struct drm_bridge *bridge) +{
- struct ptn3460_bridge *ptn_bridge = bridge->driver_private;
- int ret;
- /* bridge is attached to encoder.
* safe to remove it from the bridge_lookup table.
*/
- drm_bridge_remove_from_lookup(bridge);
No, you should never do this. First, you're not adding it back to the registry when the bridge is detached, so unloading and reloading the display driver will fail. Second there should never be a need to remove it from the registry as long as the driver itself is loaded. If you're concerned about a single bridge being used multiple times, there's already code to handle that in your previous patch:
int drm_bridge_attach_encoder(...) { ...
if (bridge->encoder) return -EBUSY;
... }
Generally the registry should contain a list of bridges that have been registered, whether they're used or not is irrelevant.
- ret = drm_bridge_init(bridge->drm_dev, bridge);
- if (ret) {
DRM_ERROR("Failed to initialize bridge with drm\n");
return ret;
- }
- /* connector implementation */
- ptn_bridge->connector.polled = bridge->connector_polled;
Why does this need to be handed from bridge to connector? You implement both the connector and the bridge in this driver, so can't you directly set ptn_bridge->connector.polled as appropriate?
- ret = drm_connector_init(bridge->drm_dev, &ptn_bridge->connector,
&ptn3460_connector_funcs, DRM_MODE_CONNECTOR_LVDS);
- if (ret) {
DRM_ERROR("Failed to initialize connector with drm\n");
return ret;
- }
- drm_connector_helper_add(&ptn_bridge->connector,
&ptn3460_connector_helper_funcs);
- drm_connector_register(&ptn_bridge->connector);
- drm_mode_connector_attach_encoder(&ptn_bridge->connector,
bridge->encoder);
- if (ptn_bridge->panel)
drm_panel_attach(ptn_bridge->panel, &ptn_bridge->connector);
- return ret;
+}
I'm thinking that eventually we'll want to register the connector only when a panel is attached to the bridge. This will only become important when we implement bridge chaining because if you instantiate a connector for each bridge then you'll get a list of connectors for the DRM device representing the output of each bridge rather than just the final one that goes to the display.
+static int ptn3460_probe(struct i2c_client *client,
const struct i2c_device_id *id)
+{
- struct device *dev = &(client->dev);
No need for the parentheses here.
- struct device_node *panel_node;
- struct drm_bridge *bridge;
- struct ptn3460_bridge *ptn_bridge;
- int ret;
- bridge = devm_kzalloc(dev, sizeof(*bridge), GFP_KERNEL);
- if (!bridge) {
DRM_ERROR("Failed to allocate drm bridge\n");
return -ENOMEM;
- }
- ptn_bridge = devm_kzalloc(dev, sizeof(*ptn_bridge), GFP_KERNEL);
- if (!ptn_bridge) {
DRM_ERROR("Failed to allocate ptn bridge\n");
return -ENOMEM;
- }
No need for error messages on allocation failures. The allocator will already complain itself.
Also I think it's usually better to use the dev_*() functions to print messages, especially given that at this stage we're not even hooked up to DRM in the first place.
So in general I try to use DRM_*() functions only from DRM-specific callbacks (or functions called from them) and dev_*() otherwise.
+static int ptn3460_remove(struct i2c_client *client) +{
- return 0;
+}
This function should remove the bridge from the lookup table by calling drm_bridge_remove().
+static const struct i2c_device_id ptn3460_i2c_table[] = {
- {"nxp,ptn3460", 0},
- {},
+}; +MODULE_DEVICE_TABLE(i2c, ptn3460_i2c_table);
+static const struct of_device_id ptn3460_match[] = {
- { .compatible = "nxp,ptn3460" },
- {},
+}; +MODULE_DEVICE_TABLE(of, ptn3460_match);
+struct i2c_driver ptn3460_driver = {
Is there a reason why this can't be static?
- .id_table = ptn3460_i2c_table,
- .probe = ptn3460_probe,
- .remove = ptn3460_remove,
- .driver = {
.name = "nxp,ptn3460",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(ptn3460_match),
You don't need of_match_ptr() here since you already depend on OF in Kconfig, therefore of_match_ptr(x) will always evaluate to x.
- },
+}; +module_i2c_driver(ptn3460_driver);
+MODULE_AUTHOR("Sean Paul seanpaul@chromium.org"); +MODULE_DESCRIPTION("NXP ptn3460 eDP-LVDS converter driver"); +MODULE_LICENSE("GPL");
This should be "GPL v2".
Thierry
On Wed, Jul 30, 2014 at 5:35 PM, Thierry Reding thierry.reding@gmail.com wrote:
On Sat, Jul 26, 2014 at 12:52:09AM +0530, Ajay Kumar wrote: [...]
diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig index 1e2f96c..0b12d16 100644 --- a/drivers/gpu/drm/bridge/Kconfig +++ b/drivers/gpu/drm/bridge/Kconfig @@ -6,4 +6,14 @@ config DRM_BRIDGE
menu "bridge chips" depends on DRM_BRIDGE
+config DRM_PTN3460
tristate "NXP ptn3460 eDP/LVDS bridge"
depends on DRM && DRM_BRIDGE
I don't think you need these two dependencies any longer since they are implicit in the "bridge chips" menu.
Ok.
diff --git a/drivers/gpu/drm/bridge/ptn3460.c b/drivers/gpu/drm/bridge/ptn3460.c
[...]
+#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_gpio.h> +#include <linux/i2c.h> +#include <linux/gpio.h> +#include <linux/delay.h> +#include <drm/drm_panel.h>
These should be ordered alphabetically, but they are already this way in the original driver, so the reordering can be a separate patch.
This can be done later.
+struct ptn3460_bridge {
struct drm_connector connector;
struct i2c_client *client;
struct drm_bridge *bridge;
I think it would be much more natural for ptn3460_bridge to embed struct drm_bridge rather than store a pointer to it.
Right. As you said, we can eliminate driver_private and use container_of.
struct drm_panel *panel;
struct edid *edid;
struct gpio_desc *gpio_pd_n;
struct gpio_desc *gpio_rst_n;
u32 edid_emulation;
bool enabled;
+};
+static int ptn3460_read_bytes(struct ptn3460_bridge *ptn_bridge, char addr,
u8 *buf, int len)
+{
int ret;
ret = i2c_master_send(ptn_bridge->client, &addr, 1);
if (ret <= 0) {
DRM_ERROR("Failed to send i2c command, ret=%d\n", ret);
return ret;
}
ret = i2c_master_recv(ptn_bridge->client, buf, len);
if (ret <= 0) {
DRM_ERROR("Failed to recv i2c data, ret=%d\n", ret);
return ret;
}
This isn't introduced by this patch, but doesn't this require locking so that this is an atomic transaction?
Perhaps it could be rewritten using i2c_smbus_read_block_data()?
Well, I am not quite aware of i2c functions. I will have a look into it though.
+static int ptn3460_write_byte(struct ptn3460_bridge *ptn_bridge, char addr,
char val)
+{
int ret;
char buf[2];
buf[0] = addr;
buf[1] = val;
ret = i2c_master_send(ptn_bridge->client, buf, ARRAY_SIZE(buf));
if (ret <= 0) {
DRM_ERROR("Failed to send i2c command, ret=%d\n", ret);
return ret;
}
return 0;
+}
Same here, this looks like it could be i2c_smbus_write_byte_data().
+static int ptn3460_select_edid(struct ptn3460_bridge *ptn_bridge) +{
int ret;
char val;
/* Load the selected edid into SRAM (accessed at PTN3460_EDID_ADDR) */
ret = ptn3460_write_byte(ptn_bridge, PTN3460_EDID_SRAM_LOAD_ADDR,
ptn_bridge->edid_emulation);
if (ret) {
DRM_ERROR("Failed to transfer edid to sram, ret=%d\n", ret);
return ret;
}
/* Enable EDID emulation and select the desired EDID */
val = 1 << PTN3460_EDID_ENABLE_EMULATION |
ptn_bridge->edid_emulation << PTN3460_EDID_EMULATION_SELECTION;
ret = ptn3460_write_byte(ptn_bridge, PTN3460_EDID_EMULATION_ADDR, val);
if (ret) {
DRM_ERROR("Failed to write edid value, ret=%d\n", ret);
return ret;
}
return 0;
+}
s/edid/EDID/ in the above (and below, too), but again the original driver had this, so it can be a separate patch.
This can be done later.
+static void ptn3460_pre_enable(struct drm_bridge *bridge) +{
struct ptn3460_bridge *ptn_bridge = bridge->driver_private;
If you embed drm_bridge within ptn3460_bridge then you can use the much more canonical container_of() macro (or rather a driver-specific inline function that wraps it) and no longer need the drm_bridge.driver_private field.
Agreed.
int ret;
if (ptn_bridge->enabled)
return;
gpiod_set_value(ptn_bridge->gpio_pd_n, 1);
gpiod_set_value(ptn_bridge->gpio_rst_n, 0);
udelay(10);
This shouldn't be using udelay(), usleep_range(10, 20) (or similar) would be better. Again, can be a separate patch.
gpiod_set_value(ptn_bridge->gpio_rst_n, 1);
It also seems like you've converted to using the gpiod_*() API, but the driver previously used gpio_is_valid() to check that both PD and RST pins had valid GPIOs associated with them. The device tree binding said that they are required, though.
So this patch actually does the right thing by making them non-optional but it also changes behaviour from the original. Like I said earlier, I would very much prefer that this conversion be split into separate patches rather than one patch that removes the old driver and a second patch that adds a new one. It makes it really difficult to tell what's really changing, breaks bisectability and generally makes our lives miserable.
Ok. I will add these as incremental changes.
drm_panel_prepare(ptn_bridge->panel);
This should check for errors.
Ok.
+static void ptn3460_enable(struct drm_bridge *bridge) +{
struct ptn3460_bridge *ptn_bridge = bridge->driver_private;
drm_panel_enable(ptn_bridge->panel);
Should check for errors as well.
Ok.
+int ptn3460_get_modes(struct drm_connector *connector)
static? There seem to be quite a few functions that can be locally scoped. Again, this seems to be the case in the original driver as but it should definitely be fixed at some point.
Ok.
+{
struct ptn3460_bridge *ptn_bridge;
u8 *edid;
int ret, num_modes;
bool power_off;
ptn_bridge = container_of(connector, struct ptn3460_bridge, connector);
if (ptn_bridge->edid)
return drm_add_edid_modes(connector, ptn_bridge->edid);
power_off = !ptn_bridge->enabled;
ptn3460_pre_enable(ptn_bridge->bridge);
edid = kmalloc(EDID_LENGTH, GFP_KERNEL);
if (!edid) {
DRM_ERROR("Failed to allocate edid\n");
return 0;
}
ret = ptn3460_read_bytes(ptn_bridge, PTN3460_EDID_ADDR, edid,
EDID_LENGTH);
if (ret) {
kfree(edid);
num_modes = 0;
Maybe instead of doing this here you can initialize the variable when you declare it? It's always been that way, so can be a separate patch, too.
Ok.
+struct drm_encoder *ptn3460_best_encoder(struct drm_connector *connector) +{
struct ptn3460_bridge *ptn_bridge;
ptn_bridge = container_of(connector, struct ptn3460_bridge, connector);
You use this long construct a couple of times, so it's useful to introduce a helper, such as this:
static inline struct ptn3460_bridge * connector_to_ptn3460(struct drm_connector *connector) { return container_of(connector, struct ptn3460_bridge, connector); }
Ok, will use this.
+int ptn3460_post_encoder_init(struct drm_bridge *bridge) +{
struct ptn3460_bridge *ptn_bridge = bridge->driver_private;
int ret;
/* bridge is attached to encoder.
* safe to remove it from the bridge_lookup table.
*/
drm_bridge_remove_from_lookup(bridge);
No, you should never do this. First, you're not adding it back to the registry when the bridge is detached, so unloading and reloading the display driver will fail. Second there should never be a need to remove it from the registry as long as the driver itself is loaded. If you're concerned about a single bridge being used multiple times, there's already code to handle that in your previous patch:
int drm_bridge_attach_encoder(...) { ... if (bridge->encoder) return -EBUSY; ... }
Generally the registry should contain a list of bridges that have been registered, whether they're used or not is irrelevant.
I was just wondering if it is ok to have a node in two independent lists? bridge_lookup_table and the other mode_config.bridge_list?
ret = drm_bridge_init(bridge->drm_dev, bridge);
if (ret) {
DRM_ERROR("Failed to initialize bridge with drm\n");
return ret;
}
/* connector implementation */
ptn_bridge->connector.polled = bridge->connector_polled;
Why does this need to be handed from bridge to connector? You implement both the connector and the bridge in this driver, so can't you directly set ptn_bridge->connector.polled as appropriate?
As explained for the previous patch, how to choose the polled flag?
ret = drm_connector_init(bridge->drm_dev, &ptn_bridge->connector,
&ptn3460_connector_funcs, DRM_MODE_CONNECTOR_LVDS);
if (ret) {
DRM_ERROR("Failed to initialize connector with drm\n");
return ret;
}
drm_connector_helper_add(&ptn_bridge->connector,
&ptn3460_connector_helper_funcs);
drm_connector_register(&ptn_bridge->connector);
drm_mode_connector_attach_encoder(&ptn_bridge->connector,
bridge->encoder);
if (ptn_bridge->panel)
drm_panel_attach(ptn_bridge->panel, &ptn_bridge->connector);
return ret;
+}
I'm thinking that eventually we'll want to register the connector only when a panel is attached to the bridge. This will only become important when we implement bridge chaining because if you instantiate a connector for each bridge then you'll get a list of connectors for the DRM device representing the output of each bridge rather than just the final one that goes to the display.
So, do not initialize connector if there is no panel? and, get_modes via panel instead of doing it by edid-emulation?
+static int ptn3460_probe(struct i2c_client *client,
const struct i2c_device_id *id)
+{
struct device *dev = &(client->dev);
No need for the parentheses here.
Ok.
struct device_node *panel_node;
struct drm_bridge *bridge;
struct ptn3460_bridge *ptn_bridge;
int ret;
bridge = devm_kzalloc(dev, sizeof(*bridge), GFP_KERNEL);
if (!bridge) {
DRM_ERROR("Failed to allocate drm bridge\n");
return -ENOMEM;
}
ptn_bridge = devm_kzalloc(dev, sizeof(*ptn_bridge), GFP_KERNEL);
if (!ptn_bridge) {
DRM_ERROR("Failed to allocate ptn bridge\n");
return -ENOMEM;
}
No need for error messages on allocation failures. The allocator will already complain itself.
Also I think it's usually better to use the dev_*() functions to print messages, especially given that at this stage we're not even hooked up to DRM in the first place.
So in general I try to use DRM_*() functions only from DRM-specific callbacks (or functions called from them) and dev_*() otherwise.
Ok, will fix them.
+static int ptn3460_remove(struct i2c_client *client) +{
return 0;
+}
This function should remove the bridge from the lookup table by calling drm_bridge_remove().
Just one doubt, already asked above.
+static const struct i2c_device_id ptn3460_i2c_table[] = {
{"nxp,ptn3460", 0},
{},
+}; +MODULE_DEVICE_TABLE(i2c, ptn3460_i2c_table);
+static const struct of_device_id ptn3460_match[] = {
{ .compatible = "nxp,ptn3460" },
{},
+}; +MODULE_DEVICE_TABLE(of, ptn3460_match);
+struct i2c_driver ptn3460_driver = {
Is there a reason why this can't be static?
Will make it static.
.id_table = ptn3460_i2c_table,
.probe = ptn3460_probe,
.remove = ptn3460_remove,
.driver = {
.name = "nxp,ptn3460",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(ptn3460_match),
You don't need of_match_ptr() here since you already depend on OF in Kconfig, therefore of_match_ptr(x) will always evaluate to x.
Ok, will fix it.
},
+}; +module_i2c_driver(ptn3460_driver);
+MODULE_AUTHOR("Sean Paul seanpaul@chromium.org"); +MODULE_DESCRIPTION("NXP ptn3460 eDP-LVDS converter driver"); +MODULE_LICENSE("GPL");
This should be "GPL v2".
Ok. Will fix it.
Ajay
On Wed, Jul 30, 2014 at 08:46:44PM +0530, Ajay kumar wrote:
On Wed, Jul 30, 2014 at 5:35 PM, Thierry Reding thierry.reding@gmail.com wrote:
On Sat, Jul 26, 2014 at 12:52:09AM +0530, Ajay Kumar wrote:
[...]
+int ptn3460_post_encoder_init(struct drm_bridge *bridge) +{
struct ptn3460_bridge *ptn_bridge = bridge->driver_private;
int ret;
/* bridge is attached to encoder.
* safe to remove it from the bridge_lookup table.
*/
drm_bridge_remove_from_lookup(bridge);
No, you should never do this. First, you're not adding it back to the registry when the bridge is detached, so unloading and reloading the display driver will fail. Second there should never be a need to remove it from the registry as long as the driver itself is loaded. If you're concerned about a single bridge being used multiple times, there's already code to handle that in your previous patch:
int drm_bridge_attach_encoder(...) { ... if (bridge->encoder) return -EBUSY; ... }
Generally the registry should contain a list of bridges that have been registered, whether they're used or not is irrelevant.
I was just wondering if it is ok to have a node in two independent lists? bridge_lookup_table and the other mode_config.bridge_list?
Oh, it reuses the head field for the registry. I hadn't noticed before. No, you certainly can't have the same node in two lists. Honestly I don't quite understand why there was a need to expose drm_bridge as a drm_mode_object in the first place since it's never exported to userspace.
So I think that perhaps we could simply get rid of the base field and not tie in drm_bridge objects with the DRM object as we currently do. But until Sean or Rob comment on this it might be better to simply add another struct list_head field for the registry. That way both can coexist and we can independently still decide to remove the base and head fields if they're no longer needed.
ret = drm_connector_init(bridge->drm_dev, &ptn_bridge->connector,
&ptn3460_connector_funcs, DRM_MODE_CONNECTOR_LVDS);
if (ret) {
DRM_ERROR("Failed to initialize connector with drm\n");
return ret;
}
drm_connector_helper_add(&ptn_bridge->connector,
&ptn3460_connector_helper_funcs);
drm_connector_register(&ptn_bridge->connector);
drm_mode_connector_attach_encoder(&ptn_bridge->connector,
bridge->encoder);
if (ptn_bridge->panel)
drm_panel_attach(ptn_bridge->panel, &ptn_bridge->connector);
return ret;
+}
I'm thinking that eventually we'll want to register the connector only when a panel is attached to the bridge. This will only become important when we implement bridge chaining because if you instantiate a connector for each bridge then you'll get a list of connectors for the DRM device representing the output of each bridge rather than just the final one that goes to the display.
So, do not initialize connector if there is no panel? and, get_modes via panel instead of doing it by edid-emulation?
If there's no panel, then there's nothing to connect the connector to, so it's unneeded. Also if you have chained bridges, then each bridge would expose a connector and it would become impossible to choose the correct one. So only the final bridge in the chain should instantiate the connector.
.get_modes() still needs to be done from the bridge because that is the most closely connected to the display controller and therefore dictates the timing that the display controller needs to generate.
Querying the panel's .get_modes() might be useful to figure out which emulation mode to use in the bridge.
Thierry
On Wed, Jul 30, 2014 at 9:10 PM, Thierry Reding thierry.reding@gmail.com wrote:
On Wed, Jul 30, 2014 at 08:46:44PM +0530, Ajay kumar wrote:
On Wed, Jul 30, 2014 at 5:35 PM, Thierry Reding thierry.reding@gmail.com wrote:
On Sat, Jul 26, 2014 at 12:52:09AM +0530, Ajay Kumar wrote:
[...]
+int ptn3460_post_encoder_init(struct drm_bridge *bridge) +{
struct ptn3460_bridge *ptn_bridge = bridge->driver_private;
int ret;
/* bridge is attached to encoder.
* safe to remove it from the bridge_lookup table.
*/
drm_bridge_remove_from_lookup(bridge);
No, you should never do this. First, you're not adding it back to the registry when the bridge is detached, so unloading and reloading the display driver will fail. Second there should never be a need to remove it from the registry as long as the driver itself is loaded. If you're concerned about a single bridge being used multiple times, there's already code to handle that in your previous patch:
int drm_bridge_attach_encoder(...) { ... if (bridge->encoder) return -EBUSY; ... }
Generally the registry should contain a list of bridges that have been registered, whether they're used or not is irrelevant.
I was just wondering if it is ok to have a node in two independent lists? bridge_lookup_table and the other mode_config.bridge_list?
Oh, it reuses the head field for the registry. I hadn't noticed before. No, you certainly can't have the same node in two lists. Honestly I don't quite understand why there was a need to expose drm_bridge as a drm_mode_object in the first place since it's never exported to userspace.
So I think that perhaps we could simply get rid of the base field and not tie in drm_bridge objects with the DRM object as we currently do. But until Sean or Rob comment on this it might be better to simply add another struct list_head field for the registry. That way both can coexist and we can independently still decide to remove the base and head fields if they're no longer needed.
Ok. What shall I name the new list_head?
ret = drm_connector_init(bridge->drm_dev, &ptn_bridge->connector,
&ptn3460_connector_funcs, DRM_MODE_CONNECTOR_LVDS);
if (ret) {
DRM_ERROR("Failed to initialize connector with drm\n");
return ret;
}
drm_connector_helper_add(&ptn_bridge->connector,
&ptn3460_connector_helper_funcs);
drm_connector_register(&ptn_bridge->connector);
drm_mode_connector_attach_encoder(&ptn_bridge->connector,
bridge->encoder);
if (ptn_bridge->panel)
drm_panel_attach(ptn_bridge->panel, &ptn_bridge->connector);
return ret;
+}
I'm thinking that eventually we'll want to register the connector only when a panel is attached to the bridge. This will only become important when we implement bridge chaining because if you instantiate a connector for each bridge then you'll get a list of connectors for the DRM device representing the output of each bridge rather than just the final one that goes to the display.
So, do not initialize connector if there is no panel? and, get_modes via panel instead of doing it by edid-emulation?
If there's no panel, then there's nothing to connect the connector to, so it's unneeded. Also if you have chained bridges, then each bridge would expose a connector and it would become impossible to choose the correct one. So only the final bridge in the chain should instantiate the connector.
Since there is only a single bridge when it comes to ptn3460/ps8622, lets not talk about chaining of bridges now. And, I agree that if there is no panel, then no need to register connector.
.get_modes() still needs to be done from the bridge because that is the most closely connected to the display controller and therefore dictates the timing that the display controller needs to generate.
Querying the panel's .get_modes() might be useful to figure out which emulation mode to use in the bridge.
But, get_modes from panel returns me only the no_of_modes but not the actual mode structure. How do I compare the list of supported emulation modes?
Ajay
On Wed, Jul 30, 2014 at 09:44:32PM +0530, Ajay kumar wrote:
On Wed, Jul 30, 2014 at 9:10 PM, Thierry Reding thierry.reding@gmail.com wrote:
On Wed, Jul 30, 2014 at 08:46:44PM +0530, Ajay kumar wrote:
On Wed, Jul 30, 2014 at 5:35 PM, Thierry Reding thierry.reding@gmail.com wrote:
On Sat, Jul 26, 2014 at 12:52:09AM +0530, Ajay Kumar wrote:
[...]
+int ptn3460_post_encoder_init(struct drm_bridge *bridge) +{
struct ptn3460_bridge *ptn_bridge = bridge->driver_private;
int ret;
/* bridge is attached to encoder.
* safe to remove it from the bridge_lookup table.
*/
drm_bridge_remove_from_lookup(bridge);
No, you should never do this. First, you're not adding it back to the registry when the bridge is detached, so unloading and reloading the display driver will fail. Second there should never be a need to remove it from the registry as long as the driver itself is loaded. If you're concerned about a single bridge being used multiple times, there's already code to handle that in your previous patch:
int drm_bridge_attach_encoder(...) { ... if (bridge->encoder) return -EBUSY; ... }
Generally the registry should contain a list of bridges that have been registered, whether they're used or not is irrelevant.
I was just wondering if it is ok to have a node in two independent lists? bridge_lookup_table and the other mode_config.bridge_list?
Oh, it reuses the head field for the registry. I hadn't noticed before. No, you certainly can't have the same node in two lists. Honestly I don't quite understand why there was a need to expose drm_bridge as a drm_mode_object in the first place since it's never exported to userspace.
So I think that perhaps we could simply get rid of the base field and not tie in drm_bridge objects with the DRM object as we currently do. But until Sean or Rob comment on this it might be better to simply add another struct list_head field for the registry. That way both can coexist and we can independently still decide to remove the base and head fields if they're no longer needed.
Ok. What shall I name the new list_head?
"list" would be a good choice in my opinion.
.get_modes() still needs to be done from the bridge because that is the most closely connected to the display controller and therefore dictates the timing that the display controller needs to generate.
Querying the panel's .get_modes() might be useful to figure out which emulation mode to use in the bridge.
But, get_modes from panel returns me only the no_of_modes but not the actual mode structure. How do I compare the list of supported emulation modes?
You could iterate over the connector's probed_modes list which should contain all the modes that the panel reported (after .get_modes() was called).
Thierry
From: Vincent Palatin vpalatin@chromium.org
This patch adds drm_bridge driver for parade DisplayPort to LVDS bridge chip.
Signed-off-by: Vincent Palatin vpalatin@chromium.org Signed-off-by: Andrew Bresticker abrestic@chromium.org Signed-off-by: Sean Paul seanpaul@chromium.org Signed-off-by: Rahul Sharma rahul.sharma@samsung.com Signed-off-by: Ajay Kumar ajaykumar.rs@samsung.com --- .../devicetree/bindings/vendor-prefixes.txt | 1 + .../devicetree/bindings/video/bridge/ps8622.txt | 19 + drivers/gpu/drm/bridge/Kconfig | 10 + drivers/gpu/drm/bridge/Makefile | 1 + drivers/gpu/drm/bridge/ps8622.c | 602 ++++++++++++++++++++ 5 files changed, 633 insertions(+) create mode 100644 Documentation/devicetree/bindings/video/bridge/ps8622.txt create mode 100644 drivers/gpu/drm/bridge/ps8622.c
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt index 46a311e..b4a99cc 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.txt +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt @@ -96,6 +96,7 @@ nxp NXP Semiconductors onnn ON Semiconductor Corp. opencores OpenCores.org panasonic Panasonic Corporation +parade Parade Technologies Inc. phytec PHYTEC Messtechnik GmbH picochip Picochip Ltd plathome Plat'Home Co., Ltd. diff --git a/Documentation/devicetree/bindings/video/bridge/ps8622.txt b/Documentation/devicetree/bindings/video/bridge/ps8622.txt new file mode 100644 index 0000000..fdeafb2 --- /dev/null +++ b/Documentation/devicetree/bindings/video/bridge/ps8622.txt @@ -0,0 +1,19 @@ +ps8622-bridge bindings + +Required properties: + - compatible: "parade,ps8622" or "parade,ps8625" + - reg: first i2c address of the bridge + - sleep-gpios: OF device-tree gpio specification + - reset-gpios: OF device-tree gpio specification + +Optional properties: + - lane-count: number of DP lanes to use + +Example: + ps8622-bridge@48 { + compatible = "parade,ps8622"; + reg = <0x48>; + sleep-gpios = <&gpc3 6 1 0 0>; + reset-gpios = <&gpc3 1 1 0 0>; + lane-count = <1> + }; diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig index 0b12d16..d73e474 100644 --- a/drivers/gpu/drm/bridge/Kconfig +++ b/drivers/gpu/drm/bridge/Kconfig @@ -16,4 +16,14 @@ config DRM_PTN3460 help ptn3460 eDP-LVDS bridge chip driver.
+config DRM_PS8622 + tristate "Parade eDP/LVDS bridge" + depends on DRM && DRM_BRIDGE + depends on OF + depends on I2C + select DRM_PANEL + select BACKLIGHT_LCD_SUPPORT + select BACKLIGHT_CLASS_DEVICE + help + parade eDP-LVDS bridge chip driver. endmenu diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile index b4733e1..d1b5daa 100644 --- a/drivers/gpu/drm/bridge/Makefile +++ b/drivers/gpu/drm/bridge/Makefile @@ -1,3 +1,4 @@ ccflags-y := -Iinclude/drm
obj-$(CONFIG_DRM_PTN3460) += ptn3460.o +obj-$(CONFIG_DRM_PS8622) += ps8622.o diff --git a/drivers/gpu/drm/bridge/ps8622.c b/drivers/gpu/drm/bridge/ps8622.c new file mode 100644 index 0000000..ec60fcf --- /dev/null +++ b/drivers/gpu/drm/bridge/ps8622.c @@ -0,0 +1,602 @@ +/* + * Parade PS8622 eDP/LVDS bridge driver + * + * Copyright (C) 2014 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/module.h> +#include <linux/backlight.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/fb.h> +#include <linux/gpio.h> +#include <linux/i2c.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/pm.h> +#include <linux/regulator/consumer.h> + +#include <drm/drm_panel.h> + +#include "drmP.h" +#include "drm_crtc.h" +#include "drm_crtc_helper.h" + +struct ps8622_bridge { + struct drm_connector connector; + struct i2c_client *client; + struct drm_bridge *bridge; + struct drm_panel *panel; + struct regulator *v12; + struct backlight_device *bl; + struct mutex enable_mutex; + + struct gpio_desc *gpio_slp_n; + struct gpio_desc *gpio_rst_n; + + u8 max_lane_count; + u8 lane_count; + + bool enabled; +}; + +struct ps8622_device_data { + u8 max_lane_count; +}; + +static const struct ps8622_device_data ps8622_data = { + .max_lane_count = 1, +}; + +static const struct ps8622_device_data ps8625_data = { + .max_lane_count = 2, +}; + +/* Brightness scale on the Parade chip */ +#define PS8622_MAX_BRIGHTNESS 0xff + +/* Timings taken from the version 1.7 datasheet for the PS8622/PS8625 */ +#define PS8622_POWER_RISE_T1_MIN_US 10 +#define PS8622_POWER_RISE_T1_MAX_US 10000 +#define PS8622_RST_HIGH_T2_MIN_US 3000 +#define PS8622_RST_HIGH_T2_MAX_US 30000 +#define PS8622_PWMO_END_T12_MS 200 +#define PS8622_POWER_FALL_T16_MAX_US 10000 +#define PS8622_POWER_OFF_T17_MS 500 + +#if ((PS8622_RST_HIGH_T2_MIN_US + PS8622_POWER_RISE_T1_MAX_US) > \ + (PS8622_RST_HIGH_T2_MAX_US + PS8622_POWER_RISE_T1_MIN_US)) +#error "T2.min + T1.max must be less than T2.max + T1.min" +#endif + +static int ps8622_set(struct i2c_client *client, u8 page, u8 reg, u8 val) +{ + int ret; + struct i2c_adapter *adap = client->adapter; + struct i2c_msg msg; + u8 data[] = {reg, val}; + + msg.addr = client->addr + page; + msg.flags = 0; + msg.len = sizeof(data); + msg.buf = data; + + ret = i2c_transfer(adap, &msg, 1); + if (ret != 1) + pr_warn("PS8622 I2C write (0x%02x,0x%02x,0x%02x) failed: %d\n", + client->addr + page, reg, val, ret); + return !(ret == 1); +} + +static int ps8622_send_config(struct ps8622_bridge *ps_bridge) +{ + struct i2c_client *cl = ps_bridge->client; + int err = 0; + + /* wait 20ms after power ON */ + usleep_range(20000, 30000); + + err |= ps8622_set(cl, 0x02, 0xa1, 0x01); /* HPD low */ + /* SW setting */ + err |= ps8622_set(cl, 0x04, 0x14, 0x01); /* [1:0] SW output 1.2V voltage + * is lower to 96% */ + /* RCO SS setting */ + err |= ps8622_set(cl, 0x04, 0xe3, 0x20); /* [5:4] = b01 0.5%, b10 1%, + * b11 1.5% */ + err |= ps8622_set(cl, 0x04, 0xe2, 0x80); /* [7] RCO SS enable */ + /* RPHY Setting */ + err |= ps8622_set(cl, 0x04, 0x8a, 0x0c); /* [3:2] CDR tune wait cycle + * before measure for fine tune + * b00: 1us b01: 0.5us b10:2us + * b11: 4us */ + err |= ps8622_set(cl, 0x04, 0x89, 0x08); /* [3] RFD always on */ + err |= ps8622_set(cl, 0x04, 0x71, 0x2d); /* CTN lock in/out: + * 20000ppm/80000ppm. + * Lock out 2 times. */ + /* 2.7G CDR settings */ + err |= ps8622_set(cl, 0x04, 0x7d, 0x07); /* NOF=40LSB for HBR CDR + * setting */ + err |= ps8622_set(cl, 0x04, 0x7b, 0x00); /* [1:0] Fmin=+4bands */ + err |= ps8622_set(cl, 0x04, 0x7a, 0xfd); /* [7:5] DCO_FTRNG=+-40% */ + /* 1.62G CDR settings */ + err |= ps8622_set(cl, 0x04, 0xc0, 0x12); /* [5:2]NOF=64LSB [1:0]DCO + * scale is 2/5 */ + err |= ps8622_set(cl, 0x04, 0xc1, 0x92); /* Gitune=-37% */ + err |= ps8622_set(cl, 0x04, 0xc2, 0x1c); /* Fbstep=100% */ + err |= ps8622_set(cl, 0x04, 0x32, 0x80); /* [7] LOS signal disable */ + /* RPIO Setting */ + err |= ps8622_set(cl, 0x04, 0x00, 0xb0); /* [7:4] LVDS driver bias + * current : 75% (250mV swing) + * */ + err |= ps8622_set(cl, 0x04, 0x15, 0x40); /* [7:6] Right-bar GPIO output + * strength is 8mA */ + /* EQ Training State Machine Setting */ + err |= ps8622_set(cl, 0x04, 0x54, 0x10); /* RCO calibration start */ + /* Logic, needs more than 10 I2C command */ + err |= ps8622_set(cl, 0x01, 0x02, 0x80 | ps_bridge->max_lane_count); + /* [4:0] MAX_LANE_COUNT set to + * max supported lanes */ + err |= ps8622_set(cl, 0x01, 0x21, 0x80 | ps_bridge->lane_count); + /* [4:0] LANE_COUNT_SET set to + * chosen lane count */ + err |= ps8622_set(cl, 0x00, 0x52, 0x20); + err |= ps8622_set(cl, 0x00, 0xf1, 0x03); /* HPD CP toggle enable */ + err |= ps8622_set(cl, 0x00, 0x62, 0x41); + err |= ps8622_set(cl, 0x00, 0xf6, 0x01); /* Counter number, add 1ms + * counter delay */ + err |= ps8622_set(cl, 0x00, 0x77, 0x06); /* [6]PWM function control by + * DPCD0040f[7], default is PWM + * block always works. */ + err |= ps8622_set(cl, 0x00, 0x4c, 0x04); /* 04h Adjust VTotal tolerance + * to fix the 30Hz no display + * issue */ + err |= ps8622_set(cl, 0x01, 0xc0, 0x00); /* DPCD00400='h00, Parade OUI = + * 'h001cf8 */ + err |= ps8622_set(cl, 0x01, 0xc1, 0x1c); /* DPCD00401='h1c */ + err |= ps8622_set(cl, 0x01, 0xc2, 0xf8); /* DPCD00402='hf8 */ + err |= ps8622_set(cl, 0x01, 0xc3, 0x44); /* DPCD403~408 = ASCII code + * D2SLV5='h4432534c5635 */ + err |= ps8622_set(cl, 0x01, 0xc4, 0x32); /* DPCD404 */ + err |= ps8622_set(cl, 0x01, 0xc5, 0x53); /* DPCD405 */ + err |= ps8622_set(cl, 0x01, 0xc6, 0x4c); /* DPCD406 */ + err |= ps8622_set(cl, 0x01, 0xc7, 0x56); /* DPCD407 */ + err |= ps8622_set(cl, 0x01, 0xc8, 0x35); /* DPCD408 */ + err |= ps8622_set(cl, 0x01, 0xca, 0x01); /* DPCD40A, Initial Code major + * revision '01' */ + err |= ps8622_set(cl, 0x01, 0xcb, 0x05); /* DPCD40B, Initial Code minor + * revision '05' */ + if (ps_bridge->bl) { + err |= ps8622_set(cl, 0x01, 0xa5, 0xa0); + /* DPCD720, internal PWM */ + err |= ps8622_set(cl, 0x01, 0xa7, + ps_bridge->bl->props.brightness); + /* FFh for 100% brightness, + * 0h for 0% brightness */ + } else { + err |= ps8622_set(cl, 0x01, 0xa5, 0x80); + /* DPCD720, external PWM */ + } + err |= ps8622_set(cl, 0x01, 0xcc, 0x13); /* Set LVDS output as 6bit-VESA + * mapping, single LVDS channel + * */ + err |= ps8622_set(cl, 0x02, 0xb1, 0x20); /* Enable SSC set by register + * */ + err |= ps8622_set(cl, 0x04, 0x10, 0x16); /* Set SSC enabled and +/-1% + * central spreading */ + /* Logic end */ + err |= ps8622_set(cl, 0x04, 0x59, 0x60); /* MPU Clock source: LC => RCO + * */ + err |= ps8622_set(cl, 0x04, 0x54, 0x14); /* LC -> RCO */ + err |= ps8622_set(cl, 0x02, 0xa1, 0x91); /* HPD high */ + + return err ? -EIO : 0; +} + +static int ps8622_backlight_update(struct backlight_device *bl) +{ + struct ps8622_bridge *ps_bridge = dev_get_drvdata(&bl->dev); + int ret, brightness = bl->props.brightness; + + if (bl->props.power != FB_BLANK_UNBLANK || + bl->props.state & (BL_CORE_SUSPENDED | BL_CORE_FBBLANK)) + brightness = 0; + + mutex_lock(&ps_bridge->enable_mutex); + + if (!ps_bridge->enabled) { + ret = -EINVAL; + goto out; + } + + ret = ps8622_set(ps_bridge->client, 0x01, 0xa7, brightness); + +out: + mutex_unlock(&ps_bridge->enable_mutex); + return ret; +} + +static int ps8622_backlight_get(struct backlight_device *bl) +{ + return bl->props.brightness; +} + +static const struct backlight_ops ps8622_backlight_ops = { + .update_status = ps8622_backlight_update, + .get_brightness = ps8622_backlight_get, +}; + +static void ps8622_pre_enable(struct drm_bridge *bridge) +{ + struct ps8622_bridge *ps_bridge = bridge->driver_private; + int ret; + + mutex_lock(&ps_bridge->enable_mutex); + if (ps_bridge->enabled) + goto out; + + gpiod_set_value(ps_bridge->gpio_rst_n, 0); + + if (ps_bridge->v12) { + ret = regulator_enable(ps_bridge->v12); + if (ret) + DRM_ERROR("fails to enable ps_bridge->v12"); + } + + drm_panel_prepare(ps_bridge->panel); + + gpiod_set_value(ps_bridge->gpio_slp_n, 1); + + /* + * T1 is the range of time that it takes for the power to rise after we + * enable the lcd fet. T2 is the range of time in which the data sheet + * specifies we should deassert the reset pin. + * + * If it takes T1.max for the power to rise, we need to wait atleast + * T2.min before deasserting the reset pin. If it takes T1.min for the + * power to rise, we need to wait at most T2.max before deasserting the + * reset pin. + */ + usleep_range(PS8622_RST_HIGH_T2_MIN_US + PS8622_POWER_RISE_T1_MAX_US, + PS8622_RST_HIGH_T2_MAX_US + PS8622_POWER_RISE_T1_MIN_US); + + gpiod_set_value(ps_bridge->gpio_rst_n, 1); + + ret = ps8622_send_config(ps_bridge); + if (ret) + DRM_ERROR("Failed to send config to bridge (%d)\n", ret); + + ps_bridge->enabled = true; + +out: + mutex_unlock(&ps_bridge->enable_mutex); +} + +static void ps8622_enable(struct drm_bridge *bridge) +{ + struct ps8622_bridge *ps_bridge = bridge->driver_private; + + mutex_lock(&ps_bridge->enable_mutex); + drm_panel_enable(ps_bridge->panel); + mutex_unlock(&ps_bridge->enable_mutex); +} + +static void ps8622_disable(struct drm_bridge *bridge) +{ + struct ps8622_bridge *ps_bridge = bridge->driver_private; + + mutex_lock(&ps_bridge->enable_mutex); + + if (!ps_bridge->enabled) + goto out; + + ps_bridge->enabled = false; + + drm_panel_disable(ps_bridge->panel); + msleep(PS8622_PWMO_END_T12_MS); + + /* + * This doesn't matter if the regulators are turned off, but something + * else might keep them on. In that case, we want to assert the slp gpio + * to lower power. + */ + gpiod_set_value(ps_bridge->gpio_slp_n, 0); + + if (ps_bridge->v12) + regulator_disable(ps_bridge->v12); + + /* + * Sleep for at least the amount of time that it takes the power rail to + * fall to prevent asserting the rst gpio from doing anything. + */ + usleep_range(PS8622_POWER_FALL_T16_MAX_US, + 2 * PS8622_POWER_FALL_T16_MAX_US); + gpiod_set_value(ps_bridge->gpio_rst_n, 0); + + msleep(PS8622_POWER_OFF_T17_MS); + +out: + mutex_unlock(&ps_bridge->enable_mutex); +} + +static void ps8622_post_disable(struct drm_bridge *bridge) +{ + struct ps8622_bridge *ps_bridge = bridge->driver_private; + + drm_panel_unprepare(ps_bridge->panel); +} + +static void ps8622_destroy(struct drm_bridge *bridge) +{ + drm_bridge_cleanup(bridge); +} + +static int ps8622_get_modes(struct drm_connector *connector) +{ + struct ps8622_bridge *ps_bridge; + + ps_bridge = container_of(connector, struct ps8622_bridge, connector); + + return drm_panel_get_modes(ps_bridge->panel); +} + +static struct drm_encoder *ps8622_best_encoder(struct drm_connector *connector) +{ + struct ps8622_bridge *ps_bridge; + + ps_bridge = container_of(connector, struct ps8622_bridge, connector); + + return ps_bridge->bridge->encoder; +} + +static const struct drm_connector_helper_funcs ps8622_connector_helper_funcs = { + .get_modes = ps8622_get_modes, + .best_encoder = ps8622_best_encoder, +}; + +static enum drm_connector_status ps8622_detect(struct drm_connector *connector, + bool force) +{ + return connector_status_connected; +} + +static void ps8622_connector_destroy(struct drm_connector *connector) +{ + drm_connector_cleanup(connector); +} + +static const struct drm_connector_funcs ps8622_connector_funcs = { + .dpms = drm_helper_connector_dpms, + .fill_modes = drm_helper_probe_single_connector_modes, + .detect = ps8622_detect, + .destroy = ps8622_connector_destroy, +}; + +int ps8622_post_encoder_init(struct drm_bridge *bridge) +{ + struct ps8622_bridge *ps_bridge = bridge->driver_private; + int ret; + + /* bridge is attached to encoder. + * safe to remove it from the bridge_lookup table. + */ + drm_bridge_remove_from_lookup(bridge); + + ret = drm_bridge_init(bridge->drm_dev, bridge); + if (ret) { + DRM_ERROR("Failed to initialize bridge with drm\n"); + return ret; + } + + /* connector implementation */ + ps_bridge->connector.polled = bridge->connector_polled; + + ret = drm_connector_init(bridge->drm_dev, &ps_bridge->connector, + &ps8622_connector_funcs, DRM_MODE_CONNECTOR_LVDS); + if (ret) { + DRM_ERROR("Failed to initialize connector with drm\n"); + return ret; + } + drm_connector_helper_add(&ps_bridge->connector, + &ps8622_connector_helper_funcs); + drm_connector_register(&ps_bridge->connector); + drm_mode_connector_attach_encoder(&ps_bridge->connector, + bridge->encoder); + + if (ps_bridge->panel) + drm_panel_attach(ps_bridge->panel, &ps_bridge->connector); + + return ret; +} + +static const struct drm_bridge_funcs ps8622_bridge_funcs = { + .post_encoder_init = ps8622_post_encoder_init, + .pre_enable = ps8622_pre_enable, + .enable = ps8622_enable, + .disable = ps8622_disable, + .post_disable = ps8622_post_disable, + .destroy = ps8622_destroy, +}; + +static const struct of_device_id ps8622_devices[] = { + { + .compatible = "parade,ps8622", + .data = &ps8622_data, + }, { + .compatible = "parade,ps8625", + .data = &ps8625_data, + }, { + /* end node */ + } +}; +MODULE_DEVICE_TABLE(of, ps8622_devices); + +static inline struct ps8622_device_data *get_device_data(struct device *dev) +{ + const struct of_device_id *match = + of_match_device(ps8622_devices, dev); + + return (struct ps8622_device_data *)match->data; +} + +static int ps8622_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &(client->dev); + struct device_node *panel_node, *backlight_node; + struct drm_bridge *bridge; + struct backlight_device *backlight_dev; + struct ps8622_bridge *ps_bridge; + const struct ps8622_device_data *device_data; + int ret; + + bridge = devm_kzalloc(dev, sizeof(*bridge), GFP_KERNEL); + if (!bridge) { + DRM_ERROR("Failed to allocate drm bridge\n"); + return -ENOMEM; + } + + ps_bridge = devm_kzalloc(dev, sizeof(*ps_bridge), GFP_KERNEL); + if (!ps_bridge) { + DRM_ERROR("could not allocate ps bridge\n"); + return -ENOMEM; + } + + panel_node = of_parse_phandle(dev->of_node, "panel", 0); + if (panel_node) { + ps_bridge->panel = of_drm_find_panel(panel_node); + of_node_put(panel_node); + if (!ps_bridge->panel) + return -EPROBE_DEFER; + } + + backlight_dev = NULL; + backlight_node = of_parse_phandle(dev->of_node, "backlight", 0); + if (backlight_node) { + backlight_dev = of_find_backlight_by_node(backlight_node); + of_node_put(backlight_node); + if (!backlight_dev) + return -EPROBE_DEFER; + } + + mutex_init(&ps_bridge->enable_mutex); + + bridge->dev = dev; + bridge->driver_private = ps_bridge; + bridge->funcs = &ps8622_bridge_funcs; + + ps_bridge->client = client; + ps_bridge->bridge = bridge; + + device_data = get_device_data(dev); + + ps_bridge->v12 = devm_regulator_get(&client->dev, "vdd_bridge"); + if (IS_ERR(ps_bridge->v12)) { + DRM_INFO("no 1.2v regulator found for PS8622\n"); + ps_bridge->v12 = NULL; + } + + ps_bridge->gpio_slp_n = devm_gpiod_get(&client->dev, "sleep"); + if (IS_ERR(ps_bridge->gpio_slp_n)) { + ret = PTR_ERR(ps_bridge->gpio_slp_n); + DRM_ERROR("cannot get gpio_slp_n %d\n", ret); + goto err_client; + } else { + ret = gpiod_direction_output(ps_bridge->gpio_slp_n, 1); + if (ret) { + DRM_ERROR("cannot configure gpio_slp_n\n"); + goto err_client; + } + } + + ps_bridge->gpio_rst_n = devm_gpiod_get(&client->dev, "reset"); + if (IS_ERR(ps_bridge->gpio_rst_n)) { + ret = PTR_ERR(ps_bridge->gpio_rst_n); + DRM_ERROR("cannot get gpio_rst_n %d\n", ret); + goto err_client; + } else { + /* + * Assert the reset pin high to avoid the bridge being + * initialized prematurely + */ + ret = gpiod_direction_output(ps_bridge->gpio_rst_n, 1); + if (ret) { + DRM_ERROR("cannot configure gpio_slp_n\n"); + goto err_client; + } + } + + ps_bridge->max_lane_count = device_data->max_lane_count; + + if (of_property_read_u8(dev->of_node, "lane-count", + &ps_bridge->lane_count)) + ps_bridge->lane_count = ps_bridge->max_lane_count; + else if (ps_bridge->lane_count > ps_bridge->max_lane_count) { + DRM_INFO("lane-count property is too high for DP bridge\n"); + ps_bridge->lane_count = ps_bridge->max_lane_count; + } + + if (!backlight_dev) { + ps_bridge->bl = backlight_device_register("ps8622-backlight", + dev, ps_bridge, &ps8622_backlight_ops, + NULL); + if (IS_ERR(ps_bridge->bl)) { + DRM_ERROR("failed to register backlight\n"); + ret = PTR_ERR(ps_bridge->bl); + ps_bridge->bl = NULL; + goto err_client; + } + ps_bridge->bl->props.max_brightness = PS8622_MAX_BRIGHTNESS; + ps_bridge->bl->props.brightness = PS8622_MAX_BRIGHTNESS; + } + + i2c_set_clientdata(client, ps_bridge); + + drm_bridge_add_for_lookup(bridge); + + return 0; + +err_client: + DRM_ERROR("device probe failed : %d\n", ret); + return ret; +} + +static int ps8622_remove(struct i2c_client *client) +{ + struct ps8622_bridge *ps_bridge = i2c_get_clientdata(client); + + if (ps_bridge->bl) + backlight_device_unregister(ps_bridge->bl); + + return 0; +} + +static const struct i2c_device_id ps8622_i2c_table[] = { + {"parade", 0}, + {}, +}; +MODULE_DEVICE_TABLE(i2c, ps8622_i2c_table); + +struct i2c_driver ps8622_driver = { + .id_table = ps8622_i2c_table, + .probe = ps8622_probe, + .remove = ps8622_remove, + .driver = { + .name = "parade", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(ps8622_devices), + }, +}; +module_i2c_driver(ps8622_driver); + +MODULE_AUTHOR("Vincent Palatin vpalatin@chromium.org"); +MODULE_DESCRIPTION("Parade ps8622 eDP-LVDS converter driver"); +MODULE_LICENSE("GPL");
Am 25.07.2014 21:22, schrieb Ajay Kumar:
From: Vincent Palatin vpalatin@chromium.org
This patch adds drm_bridge driver for parade DisplayPort to LVDS bridge chip.
Signed-off-by: Vincent Palatin vpalatin@chromium.org Signed-off-by: Andrew Bresticker abrestic@chromium.org Signed-off-by: Sean Paul seanpaul@chromium.org Signed-off-by: Rahul Sharma rahul.sharma@samsung.com Signed-off-by: Ajay Kumar ajaykumar.rs@samsung.com
.../devicetree/bindings/vendor-prefixes.txt | 1 + .../devicetree/bindings/video/bridge/ps8622.txt | 19 + drivers/gpu/drm/bridge/Kconfig | 10 + drivers/gpu/drm/bridge/Makefile | 1 + drivers/gpu/drm/bridge/ps8622.c | 602 ++++++++++++++++++++ 5 files changed, 633 insertions(+) create mode 100644 Documentation/devicetree/bindings/video/bridge/ps8622.txt create mode 100644 drivers/gpu/drm/bridge/ps8622.c
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt index 46a311e..b4a99cc 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.txt +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt @@ -96,6 +96,7 @@ nxp NXP Semiconductors onnn ON Semiconductor Corp. opencores OpenCores.org panasonic Panasonic Corporation +parade Parade Technologies Inc. phytec PHYTEC Messtechnik GmbH picochip Picochip Ltd plathome Plat'Home Co., Ltd. diff --git a/Documentation/devicetree/bindings/video/bridge/ps8622.txt b/Documentation/devicetree/bindings/video/bridge/ps8622.txt new file mode 100644 index 0000000..fdeafb2 --- /dev/null +++ b/Documentation/devicetree/bindings/video/bridge/ps8622.txt @@ -0,0 +1,19 @@ +ps8622-bridge bindings
+Required properties:
- compatible: "parade,ps8622" or "parade,ps8625"
- reg: first i2c address of the bridge
- sleep-gpios: OF device-tree gpio specification
- reset-gpios: OF device-tree gpio specification
+Optional properties:
- lane-count: number of DP lanes to use
+Example:
- ps8622-bridge@48 {
Nit: Shouldn't this be lvds-bridge like in 7/8 or something else not derived from the specific model? Applies to the DT series as well.
compatible = "parade,ps8622";
reg = <0x48>;
sleep-gpios = <&gpc3 6 1 0 0>;
reset-gpios = <&gpc3 1 1 0 0>;
lane-count = <1>
- };
[...]
diff --git a/drivers/gpu/drm/bridge/ps8622.c b/drivers/gpu/drm/bridge/ps8622.c new file mode 100644 index 0000000..ec60fcf --- /dev/null +++ b/drivers/gpu/drm/bridge/ps8622.c @@ -0,0 +1,602 @@ +/*
- Parade PS8622 eDP/LVDS bridge driver
- Copyright (C) 2014 Google, Inc.
- This software is licensed under the terms of the GNU General Public
- License version 2, as published by the Free Software Foundation, and
- may be copied, distributed, and modified under those terms.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- */
[...]
+MODULE_AUTHOR("Vincent Palatin vpalatin@chromium.org"); +MODULE_DESCRIPTION("Parade ps8622 eDP-LVDS converter driver"); +MODULE_LICENSE("GPL");
"GPL v2"?
Regards, Andreas
Hi Andreas,
On Tue, Jul 29, 2014 at 4:59 PM, Andreas Färber afaerber@suse.de wrote:
Am 25.07.2014 21:22, schrieb Ajay Kumar:
From: Vincent Palatin vpalatin@chromium.org
This patch adds drm_bridge driver for parade DisplayPort to LVDS bridge chip.
Signed-off-by: Vincent Palatin vpalatin@chromium.org Signed-off-by: Andrew Bresticker abrestic@chromium.org Signed-off-by: Sean Paul seanpaul@chromium.org Signed-off-by: Rahul Sharma rahul.sharma@samsung.com Signed-off-by: Ajay Kumar ajaykumar.rs@samsung.com
.../devicetree/bindings/vendor-prefixes.txt | 1 + .../devicetree/bindings/video/bridge/ps8622.txt | 19 + drivers/gpu/drm/bridge/Kconfig | 10 + drivers/gpu/drm/bridge/Makefile | 1 + drivers/gpu/drm/bridge/ps8622.c | 602 ++++++++++++++++++++ 5 files changed, 633 insertions(+) create mode 100644 Documentation/devicetree/bindings/video/bridge/ps8622.txt create mode 100644 drivers/gpu/drm/bridge/ps8622.c
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt index 46a311e..b4a99cc 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.txt +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt @@ -96,6 +96,7 @@ nxp NXP Semiconductors onnn ON Semiconductor Corp. opencores OpenCores.org panasonic Panasonic Corporation +parade Parade Technologies Inc. phytec PHYTEC Messtechnik GmbH picochip Picochip Ltd plathome Plat'Home Co., Ltd. diff --git a/Documentation/devicetree/bindings/video/bridge/ps8622.txt b/Documentation/devicetree/bindings/video/bridge/ps8622.txt new file mode 100644 index 0000000..fdeafb2 --- /dev/null +++ b/Documentation/devicetree/bindings/video/bridge/ps8622.txt @@ -0,0 +1,19 @@ +ps8622-bridge bindings
+Required properties:
- compatible: "parade,ps8622" or "parade,ps8625"
- reg: first i2c address of the bridge
- sleep-gpios: OF device-tree gpio specification
- reset-gpios: OF device-tree gpio specification
+Optional properties:
- lane-count: number of DP lanes to use
+Example:
ps8622-bridge@48 {
Nit: Shouldn't this be lvds-bridge like in 7/8 or something else not derived from the specific model? Applies to the DT series as well.
Right. I will fix this while sending the next series.
compatible = "parade,ps8622";
reg = <0x48>;
sleep-gpios = <&gpc3 6 1 0 0>;
reset-gpios = <&gpc3 1 1 0 0>;
lane-count = <1>
};
[...]
diff --git a/drivers/gpu/drm/bridge/ps8622.c b/drivers/gpu/drm/bridge/ps8622.c new file mode 100644 index 0000000..ec60fcf --- /dev/null +++ b/drivers/gpu/drm/bridge/ps8622.c @@ -0,0 +1,602 @@ +/*
- Parade PS8622 eDP/LVDS bridge driver
- Copyright (C) 2014 Google, Inc.
- This software is licensed under the terms of the GNU General Public
- License version 2, as published by the Free Software Foundation, and
- may be copied, distributed, and modified under those terms.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- */
[...]
+MODULE_AUTHOR("Vincent Palatin vpalatin@chromium.org"); +MODULE_DESCRIPTION("Parade ps8622 eDP-LVDS converter driver"); +MODULE_LICENSE("GPL");
"GPL v2"?
Ok. Will change it.
Regards, Ajay
On Sat, Jul 26, 2014 at 12:52:10AM +0530, Ajay Kumar wrote: [...]
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt index 46a311e..b4a99cc 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.txt +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt @@ -96,6 +96,7 @@ nxp NXP Semiconductors onnn ON Semiconductor Corp. opencores OpenCores.org panasonic Panasonic Corporation +parade Parade Technologies Inc. phytec PHYTEC Messtechnik GmbH picochip Picochip Ltd plathome Plat'Home Co., Ltd.
I think it's a good idea to turn this into a separate patch to avoid conflicts.
diff --git a/Documentation/devicetree/bindings/video/bridge/ps8622.txt b/Documentation/devicetree/bindings/video/bridge/ps8622.txt new file mode 100644 index 0000000..fdeafb2 --- /dev/null +++ b/Documentation/devicetree/bindings/video/bridge/ps8622.txt @@ -0,0 +1,19 @@ +ps8622-bridge bindings
+Required properties:
- compatible: "parade,ps8622" or "parade,ps8625"
- reg: first i2c address of the bridge
I'm not sure what the deal is with devices that use more than one I2C addresses. It would be good to hear from the device tree maintainers on how to handle those. Technically each address is a separate device. This is also mirrored in the Linux kernel's implementation of the I2C bus, where it looks wrong to access more than a single address (since the core only marks one of them used). So technically it's valid for userspace to access any but the first "page" of this device.
- sleep-gpios: OF device-tree gpio specification
- reset-gpios: OF device-tree gpio specification
This should explain what these GPIOs are used for.
+Optional properties:
- lane-count: number of DP lanes to use
+Example:
- ps8622-bridge@48 {
compatible = "parade,ps8622";
reg = <0x48>;
sleep-gpios = <&gpc3 6 1 0 0>;
reset-gpios = <&gpc3 1 1 0 0>;
lane-count = <1>
- };
Same here. It's usually best to make this a patch separate from the driver. Not because of conflicts, but because it makes it easier for DT reviewers to find the relevant pieces to look at.
diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig index 0b12d16..d73e474 100644 --- a/drivers/gpu/drm/bridge/Kconfig +++ b/drivers/gpu/drm/bridge/Kconfig @@ -16,4 +16,14 @@ config DRM_PTN3460 help ptn3460 eDP-LVDS bridge chip driver.
+config DRM_PS8622
- tristate "Parade eDP/LVDS bridge"
- depends on DRM && DRM_BRIDGE
Aagin, these are unnecessary.
diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile index b4733e1..d1b5daa 100644 --- a/drivers/gpu/drm/bridge/Makefile +++ b/drivers/gpu/drm/bridge/Makefile @@ -1,3 +1,4 @@ ccflags-y := -Iinclude/drm
obj-$(CONFIG_DRM_PTN3460) += ptn3460.o +obj-$(CONFIG_DRM_PS8622) += ps8622.o
Please keep these sorted alphabetically.
diff --git a/drivers/gpu/drm/bridge/ps8622.c b/drivers/gpu/drm/bridge/ps8622.c
[...]
+#include <linux/module.h> +#include <linux/backlight.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/fb.h> +#include <linux/gpio.h> +#include <linux/i2c.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/pm.h> +#include <linux/regulator/consumer.h>
These too.
+struct ps8622_bridge {
- struct drm_connector connector;
- struct i2c_client *client;
- struct drm_bridge *bridge;
Should be embedded.
- struct drm_panel *panel;
- struct regulator *v12;
- struct backlight_device *bl;
- struct mutex enable_mutex;
I don't quite understand what this protects. Can you explain?
+struct ps8622_device_data {
- u8 max_lane_count;
+};
+static const struct ps8622_device_data ps8622_data = {
- .max_lane_count = 1,
+};
+static const struct ps8622_device_data ps8625_data = {
- .max_lane_count = 2,
+};
+/* Brightness scale on the Parade chip */ +#define PS8622_MAX_BRIGHTNESS 0xff
+/* Timings taken from the version 1.7 datasheet for the PS8622/PS8625 */ +#define PS8622_POWER_RISE_T1_MIN_US 10 +#define PS8622_POWER_RISE_T1_MAX_US 10000 +#define PS8622_RST_HIGH_T2_MIN_US 3000 +#define PS8622_RST_HIGH_T2_MAX_US 30000 +#define PS8622_PWMO_END_T12_MS 200 +#define PS8622_POWER_FALL_T16_MAX_US 10000 +#define PS8622_POWER_OFF_T17_MS 500
+#if ((PS8622_RST_HIGH_T2_MIN_US + PS8622_POWER_RISE_T1_MAX_US) > \
- (PS8622_RST_HIGH_T2_MAX_US + PS8622_POWER_RISE_T1_MIN_US))
+#error "T2.min + T1.max must be less than T2.max + T1.min" +#endif
+static int ps8622_set(struct i2c_client *client, u8 page, u8 reg, u8 val) +{
- int ret;
- struct i2c_adapter *adap = client->adapter;
- struct i2c_msg msg;
- u8 data[] = {reg, val};
- msg.addr = client->addr + page;
- msg.flags = 0;
- msg.len = sizeof(data);
- msg.buf = data;
- ret = i2c_transfer(adap, &msg, 1);
- if (ret != 1)
pr_warn("PS8622 I2C write (0x%02x,0x%02x,0x%02x) failed: %d\n",
client->addr + page, reg, val, ret);
- return !(ret == 1);
+}
+static int ps8622_send_config(struct ps8622_bridge *ps_bridge) +{
- struct i2c_client *cl = ps_bridge->client;
- int err = 0;
- /* wait 20ms after power ON */
- usleep_range(20000, 30000);
It's unusual to do this here. The function doesn't know when it's being called. It could theoretically be called at any point. Better to move the sleep to after where the device is powered on.
- err |= ps8622_set(cl, 0x02, 0xa1, 0x01); /* HPD low */
- /* SW setting */
- err |= ps8622_set(cl, 0x04, 0x14, 0x01); /* [1:0] SW output 1.2V voltage
* is lower to 96% */
I'm not at all a fan of this kind of error chaining because you loose all context about specific errors. Also if there's a systematic error you'll want to abort as early as possible. If the device doesn't respond to the first command sent, then it likely won't respond to the next either. No need to keep trying in that case.
- /* RCO SS setting */
- err |= ps8622_set(cl, 0x04, 0xe3, 0x20); /* [5:4] = b01 0.5%, b10 1%,
* b11 1.5% */
Also I find it very hard to follow these calls. Since you have enough information about what each of these registers does, can't you provide symbolic constants?
It would probably also help to separate these into logical groups (or individual function calls if there are no meaningful groups). That way you'll get automatic documentation via function names and this function would look a lot less cluttered.
+static int ps8622_backlight_get(struct backlight_device *bl) +{
- return bl->props.brightness;
+}
Backlight devices no longer need this kind of trivial implementation. It is what the backlight core will do by default if the driver doesn't implement .get_brightness().
+static void ps8622_pre_enable(struct drm_bridge *bridge) +{
- struct ps8622_bridge *ps_bridge = bridge->driver_private;
- int ret;
- mutex_lock(&ps_bridge->enable_mutex);
- if (ps_bridge->enabled)
goto out;
- gpiod_set_value(ps_bridge->gpio_rst_n, 0);
- if (ps_bridge->v12) {
ret = regulator_enable(ps_bridge->v12);
if (ret)
DRM_ERROR("fails to enable ps_bridge->v12");
- }
- drm_panel_prepare(ps_bridge->panel);
- gpiod_set_value(ps_bridge->gpio_slp_n, 1);
This uses high-active logic for the sleep pin, so shouldn't it rather be named "gpio_slp"?
- /*
* T1 is the range of time that it takes for the power to rise after we
* enable the lcd fet.
Is that the time it takes for the FET's power to rise? In that case it would be a property of the FET, not the bridge.
T2 is the range of time in which the data sheet
* specifies we should deassert the reset pin.
*
* If it takes T1.max for the power to rise, we need to wait atleast
* T2.min before deasserting the reset pin. If it takes T1.min for the
* power to rise, we need to wait at most T2.max before deasserting the
* reset pin.
*/
Note that to my knowledge none of the sleep functions give you any guarantees about the maximum amount of time they'll sleep, only the minimum. So there's no such thing as "wait at most" in Linux.
- usleep_range(PS8622_RST_HIGH_T2_MIN_US + PS8622_POWER_RISE_T1_MAX_US,
PS8622_RST_HIGH_T2_MAX_US + PS8622_POWER_RISE_T1_MIN_US);
- gpiod_set_value(ps_bridge->gpio_rst_n, 1);
This also uses high-active logic.
- ret = ps8622_send_config(ps_bridge);
- if (ret)
DRM_ERROR("Failed to send config to bridge (%d)\n", ret);
Isn't this a fatal error? What will be the consequences if this doesn't succeed? I guess it doesn't matter much either way since the API can't propagate errors at this point anyway...
+static void ps8622_enable(struct drm_bridge *bridge) +{
- struct ps8622_bridge *ps_bridge = bridge->driver_private;
- mutex_lock(&ps_bridge->enable_mutex);
- drm_panel_enable(ps_bridge->panel);
- mutex_unlock(&ps_bridge->enable_mutex);
What's the enable_mutex protecting here?
+static void ps8622_disable(struct drm_bridge *bridge) +{
- struct ps8622_bridge *ps_bridge = bridge->driver_private;
- mutex_lock(&ps_bridge->enable_mutex);
- if (!ps_bridge->enabled)
goto out;
- ps_bridge->enabled = false;
- drm_panel_disable(ps_bridge->panel);
- msleep(PS8622_PWMO_END_T12_MS);
- /*
* This doesn't matter if the regulators are turned off, but something
* else might keep them on. In that case, we want to assert the slp gpio
* to lower power.
*/
- gpiod_set_value(ps_bridge->gpio_slp_n, 0);
- if (ps_bridge->v12)
regulator_disable(ps_bridge->v12);
- /*
* Sleep for at least the amount of time that it takes the power rail to
* fall to prevent asserting the rst gpio from doing anything.
*/
- usleep_range(PS8622_POWER_FALL_T16_MAX_US,
2 * PS8622_POWER_FALL_T16_MAX_US);
- gpiod_set_value(ps_bridge->gpio_rst_n, 0);
Does this even matter? Why would it be bad if the reset was asserted before power went away? Isn't the end result that the chip will be off and reset to an initial state the next time it's power up anyway?
+static enum drm_connector_status ps8622_detect(struct drm_connector *connector,
bool force)
Alignment here is slightly odd. We usually follow (in DRM and many other parts of the kernel) the convention of aligning arguments on subsequent lines with the first argument on the first line.
+int ps8622_post_encoder_init(struct drm_bridge *bridge) +{
- struct ps8622_bridge *ps_bridge = bridge->driver_private;
- int ret;
- /* bridge is attached to encoder.
* safe to remove it from the bridge_lookup table.
*/
- drm_bridge_remove_from_lookup(bridge);
Like I said for the ptn3460 driver, this should not be here.
- ret = drm_bridge_init(bridge->drm_dev, bridge);
- if (ret) {
DRM_ERROR("Failed to initialize bridge with drm\n");
return ret;
- }
And this should probably happen somewhere else to avoid having to do it for every driver.
+static const struct of_device_id ps8622_devices[] = {
- {
.compatible = "parade,ps8622",
.data = &ps8622_data,
- }, {
.compatible = "parade,ps8625",
.data = &ps8625_data,
- }, {
The above uses a mix of spaces and tabs for padding (tabs between .data and =). Single spaces will do.
/* end node */
Nit: it's not a node, it's an entry or element.
- }
+}; +MODULE_DEVICE_TABLE(of, ps8622_devices);
+static inline struct ps8622_device_data *get_device_data(struct device *dev) +{
- const struct of_device_id *match =
of_match_device(ps8622_devices, dev);
- return (struct ps8622_device_data *)match->data;
+}
Please drop this. First of all, match->data is void *, so there's no need for the explicit casting. Second, match->data is also const, so you're effectively casting that away.
+static int ps8622_probe(struct i2c_client *client,
const struct i2c_device_id *id)
+{
- struct device *dev = &(client->dev);
No need for the parentheses here.
- struct device_node *panel_node, *backlight_node;
- struct drm_bridge *bridge;
- struct backlight_device *backlight_dev;
- struct ps8622_bridge *ps_bridge;
Have you considered naming this simply "ps" to make it shorter? There's some weird formatting below just because this is a very long variable name.
- const struct ps8622_device_data *device_data;
- int ret;
It's just as easy (and shorter) to do this here:
const struct ps8622_device_data *data; const struct of_device_id *match;
match = of_match_device(ps8622_devices, dev); data = match->data;
- bridge = devm_kzalloc(dev, sizeof(*bridge), GFP_KERNEL);
- if (!bridge) {
DRM_ERROR("Failed to allocate drm bridge\n");
return -ENOMEM;
- }
- ps_bridge = devm_kzalloc(dev, sizeof(*ps_bridge), GFP_KERNEL);
- if (!ps_bridge) {
DRM_ERROR("could not allocate ps bridge\n");
return -ENOMEM;
- }
- panel_node = of_parse_phandle(dev->of_node, "panel", 0);
- if (panel_node) {
ps_bridge->panel = of_drm_find_panel(panel_node);
of_node_put(panel_node);
if (!ps_bridge->panel)
return -EPROBE_DEFER;
- }
- backlight_dev = NULL;
It's more idiomatic to initialize these when you declare them to avoid sprinkling them across the code like this.
- backlight_node = of_parse_phandle(dev->of_node, "backlight", 0);
- if (backlight_node) {
backlight_dev = of_find_backlight_by_node(backlight_node);
of_node_put(backlight_node);
if (!backlight_dev)
return -EPROBE_DEFER;
It seems like in this case you aren't storing the backlight anywhere. Is there a reason for that? Is it because the same backlight is used by the panel driver and it does the controlling from there? If so it might be better to use a boolean property here rather than a phandle to another backlight device. Using a phandle usually signal that you do want to use the device in some way.
- }
- mutex_init(&ps_bridge->enable_mutex);
- bridge->dev = dev;
- bridge->driver_private = ps_bridge;
- bridge->funcs = &ps8622_bridge_funcs;
- ps_bridge->client = client;
- ps_bridge->bridge = bridge;
- device_data = get_device_data(dev);
- ps_bridge->v12 = devm_regulator_get(&client->dev, "vdd_bridge");
vdd_bridge is not a valid name for a regulator. This will translate to vdd_bridge-supply for a DT property and you shouldn't be using underscores for them. Also this property isn't documented in the binding.
- if (IS_ERR(ps_bridge->v12)) {
DRM_INFO("no 1.2v regulator found for PS8622\n");
ps_bridge->v12 = NULL;
- }
Does this ever happen? As far as I know the regulator core will give you a dummy regulator when booting using DT, so errors returned here will always be real errors that you likely won't want to ignore.
- ps_bridge->gpio_slp_n = devm_gpiod_get(&client->dev, "sleep");
- if (IS_ERR(ps_bridge->gpio_slp_n)) {
ret = PTR_ERR(ps_bridge->gpio_slp_n);
DRM_ERROR("cannot get gpio_slp_n %d\n", ret);
goto err_client;
- } else {
ret = gpiod_direction_output(ps_bridge->gpio_slp_n, 1);
if (ret) {
DRM_ERROR("cannot configure gpio_slp_n\n");
goto err_client;
}
- }
- ps_bridge->gpio_rst_n = devm_gpiod_get(&client->dev, "reset");
- if (IS_ERR(ps_bridge->gpio_rst_n)) {
ret = PTR_ERR(ps_bridge->gpio_rst_n);
DRM_ERROR("cannot get gpio_rst_n %d\n", ret);
goto err_client;
- } else {
/*
* Assert the reset pin high to avoid the bridge being
* initialized prematurely
*/
ret = gpiod_direction_output(ps_bridge->gpio_rst_n, 1);
if (ret) {
DRM_ERROR("cannot configure gpio_slp_n\n");
goto err_client;
}
- }
Just as a side-note, there's an API under discussion that will allow gpio_desc's to be configured at the same time as they are requested. This driver should probably convert to that API.
- ps_bridge->max_lane_count = device_data->max_lane_count;
- if (of_property_read_u8(dev->of_node, "lane-count",
&ps_bridge->lane_count))
ps_bridge->lane_count = ps_bridge->max_lane_count;
- else if (ps_bridge->lane_count > ps_bridge->max_lane_count) {
DRM_INFO("lane-count property is too high for DP bridge\n");
ps_bridge->lane_count = ps_bridge->max_lane_count;
- }
Perhaps this should clarify that you're falling back to max_lane_count.
- if (!backlight_dev) {
ps_bridge->bl = backlight_device_register("ps8622-backlight",
dev, ps_bridge, &ps8622_backlight_ops,
NULL);
if (IS_ERR(ps_bridge->bl)) {
DRM_ERROR("failed to register backlight\n");
ret = PTR_ERR(ps_bridge->bl);
ps_bridge->bl = NULL;
No need to set this to NULL since you'll return with an error and ps_bridge will be freed.
goto err_client;
}
ps_bridge->bl->props.max_brightness = PS8622_MAX_BRIGHTNESS;
ps_bridge->bl->props.brightness = PS8622_MAX_BRIGHTNESS;
Perhaps you want to initialize bl->props.power to FB_BLANK_POWERDOWN at this point. The reason is that if you don't then the backlight will be on by default (FB_BLANK_UNBLANK == 0). Many backlight drivers do call backlight_update_status() after registering with the core to set the initial state. It looks like you're not doing that here, but it might be better to still set this to reflect what the initial state should be.
I'm assuming you want it disabled to avoid visual glitches when you turn on the display. Of course if the bootloader already turned it on and initialized the display, then you'll get flickering... but I guess that's a problem to solve another day.
- }
- i2c_set_clientdata(client, ps_bridge);
- drm_bridge_add_for_lookup(bridge);
- return 0;
+err_client:
- DRM_ERROR("device probe failed : %d\n", ret);
No need for this. The driver core typically tells you already.
+static int ps8622_remove(struct i2c_client *client) +{
- struct ps8622_bridge *ps_bridge = i2c_get_clientdata(client);
- if (ps_bridge->bl)
backlight_device_unregister(ps_bridge->bl);
- return 0;
+}
You'll also want to call drm_bridge_remove() here.
+static const struct i2c_device_id ps8622_i2c_table[] = {
- {"parade", 0},
- {},
+}; +MODULE_DEVICE_TABLE(i2c, ps8622_i2c_table);
This table doesn't look right. Shouldn't that be:
{ "ps8622", 0 }, { "ps8625", 0 },
? And perhaps use the .driver_data field to refer to the device data like for the of_device_id table?
+struct i2c_driver ps8622_driver = {
- .id_table = ps8622_i2c_table,
- .probe = ps8622_probe,
- .remove = ps8622_remove,
- .driver = {
.name = "parade",
That's an awfully generic name. I'd suggest "ps8622".
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(ps8622_devices),
As for ptn3460, this has a hard dependency on OF, so of_match_ptr() isn't useful.
- },
+}; +module_i2c_driver(ps8622_driver);
+MODULE_AUTHOR("Vincent Palatin vpalatin@chromium.org"); +MODULE_DESCRIPTION("Parade ps8622 eDP-LVDS converter driver"); +MODULE_LICENSE("GPL");
"GPL v2"
Thierry
Hi Ajay,
Am 25.07.2014 21:22, schrieb Ajay Kumar:
This series is based on exynos-drm-next branch of Inki Dae's tree at: git://git.kernel.org/pub/scm/linux/kernel/git/daeinki/drm-exynos.git
I have tested this after adding few DT changes for exynos5250-snow, exynos5420-peach-pit and exynos5800-peach-pi boards.
I'm trying to test this with a modified exynos5250-spring DT based off kgene's for-next branch due to DT, and I run into the following:
CC drivers/gpu/drm/bridge/ptn3460.o drivers/gpu/drm/bridge/ptn3460.c: In function ‘ptn3460_post_encoder_init’: drivers/gpu/drm/bridge/ptn3460.c:275:2: error: implicit declaration of function ‘drm_connector_register’ [-Werror=implicit-function-declaration] drm_connector_register(&ptn_bridge->connector); ^ cc1: some warnings being treated as errors scripts/Makefile.build:257: recipe for target 'drivers/gpu/drm/bridge/ptn3460.o' failed make[4]: *** [drivers/gpu/drm/bridge/ptn3460.o] Error 1 scripts/Makefile.build:404: recipe for target 'drivers/gpu/drm/bridge' failed make[3]: *** [drivers/gpu/drm/bridge] Error 2 make[3]: *** Warte auf noch nicht beendete Prozesse... scripts/Makefile.build:404: recipe for target 'drivers/gpu/drm' failed make[2]: *** [drivers/gpu/drm] Error 2 scripts/Makefile.build:404: recipe for target 'drivers/gpu' failed make[1]: *** [drivers/gpu] Error 2 Makefile:899: recipe for target 'drivers' failed make: *** [drivers] Error 2
Any hint which prerequisite I'm missing? Didn't spot it in Inki's tree, and it must be new since v4.
Thanks, Andreas
Hi Andreas,
On 7/27/14, Andreas Färber afaerber@suse.de wrote:
Hi Ajay,
Am 25.07.2014 21:22, schrieb Ajay Kumar:
This series is based on exynos-drm-next branch of Inki Dae's tree at: git://git.kernel.org/pub/scm/linux/kernel/git/daeinki/drm-exynos.git
I have tested this after adding few DT changes for exynos5250-snow, exynos5420-peach-pit and exynos5800-peach-pi boards.
I'm trying to test this with a modified exynos5250-spring DT based off kgene's for-next branch due to DT, and I run into the following:
CC drivers/gpu/drm/bridge/ptn3460.o drivers/gpu/drm/bridge/ptn3460.c: In function ‘ptn3460_post_encoder_init’: drivers/gpu/drm/bridge/ptn3460.c:275:2: error: implicit declaration of function ‘drm_connector_register’ [-Werror=implicit-function-declaration] drm_connector_register(&ptn_bridge->connector); ^
Hope this might help: http://www.spinics.net/lists/dri-devel/msg60578.html
Switch back to drm_sysfs_connector_add if your kernel doesn't have this patch.
Ajay
cc1: some warnings being treated as errors scripts/Makefile.build:257: recipe for target 'drivers/gpu/drm/bridge/ptn3460.o' failed make[4]: *** [drivers/gpu/drm/bridge/ptn3460.o] Error 1 scripts/Makefile.build:404: recipe for target 'drivers/gpu/drm/bridge' failed make[3]: *** [drivers/gpu/drm/bridge] Error 2 make[3]: *** Warte auf noch nicht beendete Prozesse... scripts/Makefile.build:404: recipe for target 'drivers/gpu/drm' failed make[2]: *** [drivers/gpu/drm] Error 2 scripts/Makefile.build:404: recipe for target 'drivers/gpu' failed make[1]: *** [drivers/gpu] Error 2 Makefile:899: recipe for target 'drivers' failed make: *** [drivers] Error 2
Any hint which prerequisite I'm missing? Didn't spot it in Inki's tree, and it must be new since v4.
Thanks, Andreas
-- SUSE LINUX Products GmbH, Maxfeldstr. 5, 90409 Nürnberg, Germany GF: Jeff Hawn, Jennifer Guild, Felix Imendörffer; HRB 16746 AG Nürnberg
Hi Ajay,
Am 28.07.2014 08:13, schrieb Ajay kumar:
On 7/27/14, Andreas Färber afaerber@suse.de wrote:
Am 25.07.2014 21:22, schrieb Ajay Kumar:
This series is based on exynos-drm-next branch of Inki Dae's tree at: git://git.kernel.org/pub/scm/linux/kernel/git/daeinki/drm-exynos.git
I have tested this after adding few DT changes for exynos5250-snow, exynos5420-peach-pit and exynos5800-peach-pi boards.
I'm trying to test this with a modified exynos5250-spring DT based off kgene's for-next branch due to DT, and I run into the following:
CC drivers/gpu/drm/bridge/ptn3460.o drivers/gpu/drm/bridge/ptn3460.c: In function ‘ptn3460_post_encoder_init’: drivers/gpu/drm/bridge/ptn3460.c:275:2: error: implicit declaration of function ‘drm_connector_register’ [-Werror=implicit-function-declaration] drm_connector_register(&ptn_bridge->connector); ^
Hope this might help: http://www.spinics.net/lists/dri-devel/msg60578.html
That fixed my build, thanks.
Unfortunately the most I got on Spring with attached DT was a blank screen with a white horizontal line in the middle.
Do I need to specify a specific panel model for Spring?
For testing I re-applied your iommu patches (which btw fail now for 5420 due to disp_pd) but didn't know what to do about your panel-lvds regulator patch now that it's gone.
If I don't apply this series, then commenting out the dp-controller node gets me a working display with simplefb as before.
Regards, Andreas
On Tue, Jul 29, 2014 at 01:21:48PM +0200, Andreas Färber wrote:
Hi Ajay,
Am 28.07.2014 08:13, schrieb Ajay kumar:
On 7/27/14, Andreas Färber afaerber@suse.de wrote:
Am 25.07.2014 21:22, schrieb Ajay Kumar:
This series is based on exynos-drm-next branch of Inki Dae's tree at: git://git.kernel.org/pub/scm/linux/kernel/git/daeinki/drm-exynos.git
I have tested this after adding few DT changes for exynos5250-snow, exynos5420-peach-pit and exynos5800-peach-pi boards.
I'm trying to test this with a modified exynos5250-spring DT based off kgene's for-next branch due to DT, and I run into the following:
CC drivers/gpu/drm/bridge/ptn3460.o drivers/gpu/drm/bridge/ptn3460.c: In function ‘ptn3460_post_encoder_init’: drivers/gpu/drm/bridge/ptn3460.c:275:2: error: implicit declaration of function ‘drm_connector_register’ [-Werror=implicit-function-declaration] drm_connector_register(&ptn_bridge->connector); ^
Hope this might help: http://www.spinics.net/lists/dri-devel/msg60578.html
That fixed my build, thanks.
Unfortunately the most I got on Spring with attached DT was a blank screen with a white horizontal line in the middle.
Do I need to specify a specific panel model for Spring?
For testing I re-applied your iommu patches (which btw fail now for 5420 due to disp_pd) but didn't know what to do about your panel-lvds regulator patch now that it's gone.
If I don't apply this series, then commenting out the dp-controller node gets me a working display with simplefb as before.
Regards, Andreas
-- SUSE LINUX Products GmbH, Maxfeldstr. 5, 90409 Nürnberg, Germany GF: Jeff Hawn, Jennifer Guild, Felix Imendörffer; HRB 16746 AG Nürnberg
From 9172a26a8f0d0f0d170bd27e1c150ad204d8086a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20F=C3=A4rber?= afaerber@suse.de Date: Sun, 27 Jul 2014 21:58:06 +0200 Subject: [PATCH] ARM: dts: exynos5250: Add eDP/LVDS bridge to Spring MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit
Signed-off-by: Ajay Kumar ajaykumar.rs@samsung.com [AF: Redone for v6] Signed-off-by: Andreas F??rber afaerber@suse.de
arch/arm/boot/dts/exynos5250-spring.dts | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-)
diff --git a/arch/arm/boot/dts/exynos5250-spring.dts b/arch/arm/boot/dts/exynos5250-spring.dts index 687dfab86bc8..517b1ff2bfdf 100644 --- a/arch/arm/boot/dts/exynos5250-spring.dts +++ b/arch/arm/boot/dts/exynos5250-spring.dts @@ -64,10 +64,14 @@ vdd_pll-supply = <&s5m_ldo8_reg>; };
- panel: panel {
compatible = "simple-panel";
- };
You can't do this. "simple-panel" isn't a valid panel model. It should probably be removed from the platform_of_match table in the driver.
Thierry
Am 29.07.2014 13:36, schrieb Thierry Reding:
On Tue, Jul 29, 2014 at 01:21:48PM +0200, Andreas Färber wrote:
Hi Ajay,
Am 28.07.2014 08:13, schrieb Ajay kumar:
On 7/27/14, Andreas Färber afaerber@suse.de wrote:
Am 25.07.2014 21:22, schrieb Ajay Kumar:
This series is based on exynos-drm-next branch of Inki Dae's tree at: git://git.kernel.org/pub/scm/linux/kernel/git/daeinki/drm-exynos.git
I have tested this after adding few DT changes for exynos5250-snow, exynos5420-peach-pit and exynos5800-peach-pi boards.
I'm trying to test this with a modified exynos5250-spring DT
[...]
Unfortunately the most I got on Spring with attached DT was a blank screen with a white horizontal line in the middle.
Do I need to specify a specific panel model for Spring?
[...]
From 9172a26a8f0d0f0d170bd27e1c150ad204d8086a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20F=C3=A4rber?= afaerber@suse.de Date: Sun, 27 Jul 2014 21:58:06 +0200 Subject: [PATCH] ARM: dts: exynos5250: Add eDP/LVDS bridge to Spring MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit
Signed-off-by: Ajay Kumar ajaykumar.rs@samsung.com [AF: Redone for v6] Signed-off-by: Andreas F??rber afaerber@suse.de
arch/arm/boot/dts/exynos5250-spring.dts | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-)
diff --git a/arch/arm/boot/dts/exynos5250-spring.dts b/arch/arm/boot/dts/exynos5250-spring.dts index 687dfab86bc8..517b1ff2bfdf 100644 --- a/arch/arm/boot/dts/exynos5250-spring.dts +++ b/arch/arm/boot/dts/exynos5250-spring.dts @@ -64,10 +64,14 @@ vdd_pll-supply = <&s5m_ldo8_reg>; };
- panel: panel {
compatible = "simple-panel";
- };
You can't do this. "simple-panel" isn't a valid panel model. It should probably be removed from the platform_of_match table in the driver.
Okay, that means the Snow DT is wrong, too: https://patchwork.kernel.org/patch/4625441/
And the others specify it as fallback: https://patchwork.kernel.org/patch/4625461/ https://patchwork.kernel.org/patch/4625451/
Cheers, Andreas
On Tue, Jul 29, 2014 at 01:42:09PM +0200, Andreas Färber wrote:
Am 29.07.2014 13:36, schrieb Thierry Reding:
On Tue, Jul 29, 2014 at 01:21:48PM +0200, Andreas Färber wrote:
Hi Ajay,
Am 28.07.2014 08:13, schrieb Ajay kumar:
On 7/27/14, Andreas Färber afaerber@suse.de wrote:
Am 25.07.2014 21:22, schrieb Ajay Kumar:
This series is based on exynos-drm-next branch of Inki Dae's tree at: git://git.kernel.org/pub/scm/linux/kernel/git/daeinki/drm-exynos.git
I have tested this after adding few DT changes for exynos5250-snow, exynos5420-peach-pit and exynos5800-peach-pi boards.
I'm trying to test this with a modified exynos5250-spring DT
[...]
Unfortunately the most I got on Spring with attached DT was a blank screen with a white horizontal line in the middle.
Do I need to specify a specific panel model for Spring?
[...]
From 9172a26a8f0d0f0d170bd27e1c150ad204d8086a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20F=C3=A4rber?= afaerber@suse.de Date: Sun, 27 Jul 2014 21:58:06 +0200 Subject: [PATCH] ARM: dts: exynos5250: Add eDP/LVDS bridge to Spring MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit
Signed-off-by: Ajay Kumar ajaykumar.rs@samsung.com [AF: Redone for v6] Signed-off-by: Andreas F??rber afaerber@suse.de
arch/arm/boot/dts/exynos5250-spring.dts | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-)
diff --git a/arch/arm/boot/dts/exynos5250-spring.dts b/arch/arm/boot/dts/exynos5250-spring.dts index 687dfab86bc8..517b1ff2bfdf 100644 --- a/arch/arm/boot/dts/exynos5250-spring.dts +++ b/arch/arm/boot/dts/exynos5250-spring.dts @@ -64,10 +64,14 @@ vdd_pll-supply = <&s5m_ldo8_reg>; };
- panel: panel {
compatible = "simple-panel";
- };
You can't do this. "simple-panel" isn't a valid panel model. It should probably be removed from the platform_of_match table in the driver.
Okay, that means the Snow DT is wrong, too: https://patchwork.kernel.org/patch/4625441/
And the others specify it as fallback: https://patchwork.kernel.org/patch/4625461/ https://patchwork.kernel.org/patch/4625451/
A quick grep shows that many (all?) devices that use DRM panels provide simple-panel as fallback. That's probably fine as long as they also do provide the specific model. But given that simple-panel does not have a mode or physical size, I don't think even that makes sense.
Any of the DTS files in the tree I have that list simple-panel as a fallback are Tegra, so I'll go write a patch that removes the fallback. I can't think of a reason why it would ever be needed or meaningful.
Thierry
Hi Thierry,
On Tue, Jul 29, 2014 at 5:17 PM, Thierry Reding thierry.reding@gmail.com wrote:
On Tue, Jul 29, 2014 at 01:42:09PM +0200, Andreas Färber wrote:
Am 29.07.2014 13:36, schrieb Thierry Reding:
On Tue, Jul 29, 2014 at 01:21:48PM +0200, Andreas Färber wrote:
Hi Ajay,
Am 28.07.2014 08:13, schrieb Ajay kumar:
On 7/27/14, Andreas Färber afaerber@suse.de wrote:
Am 25.07.2014 21:22, schrieb Ajay Kumar: > This series is based on exynos-drm-next branch of Inki Dae's tree at: > git://git.kernel.org/pub/scm/linux/kernel/git/daeinki/drm-exynos.git > > I have tested this after adding few DT changes for exynos5250-snow, > exynos5420-peach-pit and exynos5800-peach-pi boards.
I'm trying to test this with a modified exynos5250-spring DT
[...]
Unfortunately the most I got on Spring with attached DT was a blank screen with a white horizontal line in the middle.
Do I need to specify a specific panel model for Spring?
[...]
From 9172a26a8f0d0f0d170bd27e1c150ad204d8086a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20F=C3=A4rber?= afaerber@suse.de Date: Sun, 27 Jul 2014 21:58:06 +0200 Subject: [PATCH] ARM: dts: exynos5250: Add eDP/LVDS bridge to Spring MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit
Signed-off-by: Ajay Kumar ajaykumar.rs@samsung.com [AF: Redone for v6] Signed-off-by: Andreas F??rber afaerber@suse.de
arch/arm/boot/dts/exynos5250-spring.dts | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-)
diff --git a/arch/arm/boot/dts/exynos5250-spring.dts b/arch/arm/boot/dts/exynos5250-spring.dts index 687dfab86bc8..517b1ff2bfdf 100644 --- a/arch/arm/boot/dts/exynos5250-spring.dts +++ b/arch/arm/boot/dts/exynos5250-spring.dts @@ -64,10 +64,14 @@ vdd_pll-supply = <&s5m_ldo8_reg>; };
- panel: panel {
compatible = "simple-panel";
- };
You can't do this. "simple-panel" isn't a valid panel model. It should probably be removed from the platform_of_match table in the driver.
Okay, that means the Snow DT is wrong, too: https://patchwork.kernel.org/patch/4625441/
And the others specify it as fallback: https://patchwork.kernel.org/patch/4625461/ https://patchwork.kernel.org/patch/4625451/
A quick grep shows that many (all?) devices that use DRM panels provide simple-panel as fallback. That's probably fine as long as they also do provide the specific model. But given that simple-panel does not have a mode or physical size, I don't think even that makes sense.
On snow, the bridge chip provides the display mode instead of the panel. That is why display was working for me.
Any of the DTS files in the tree I have that list simple-panel as a fallback are Tegra, so I'll go write a patch that removes the fallback. I can't think of a reason why it would ever be needed or meaningful.
Ok. I will resend DT patches removing "simple-panel" fallback.
Regards, Ajay
On Wed, Jul 30, 2014 at 11:54:00AM +0530, Ajay kumar wrote:
Hi Thierry,
On Tue, Jul 29, 2014 at 5:17 PM, Thierry Reding thierry.reding@gmail.com wrote:
On Tue, Jul 29, 2014 at 01:42:09PM +0200, Andreas Färber wrote:
Am 29.07.2014 13:36, schrieb Thierry Reding:
On Tue, Jul 29, 2014 at 01:21:48PM +0200, Andreas Färber wrote:
Hi Ajay,
Am 28.07.2014 08:13, schrieb Ajay kumar:
On 7/27/14, Andreas Färber afaerber@suse.de wrote: > Am 25.07.2014 21:22, schrieb Ajay Kumar: >> This series is based on exynos-drm-next branch of Inki Dae's tree at: >> git://git.kernel.org/pub/scm/linux/kernel/git/daeinki/drm-exynos.git >> >> I have tested this after adding few DT changes for exynos5250-snow, >> exynos5420-peach-pit and exynos5800-peach-pi boards. > > I'm trying to test this with a modified exynos5250-spring DT
[...]
Unfortunately the most I got on Spring with attached DT was a blank screen with a white horizontal line in the middle.
Do I need to specify a specific panel model for Spring?
[...]
From 9172a26a8f0d0f0d170bd27e1c150ad204d8086a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20F=C3=A4rber?= afaerber@suse.de Date: Sun, 27 Jul 2014 21:58:06 +0200 Subject: [PATCH] ARM: dts: exynos5250: Add eDP/LVDS bridge to Spring MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit
Signed-off-by: Ajay Kumar ajaykumar.rs@samsung.com [AF: Redone for v6] Signed-off-by: Andreas F??rber afaerber@suse.de
arch/arm/boot/dts/exynos5250-spring.dts | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-)
diff --git a/arch/arm/boot/dts/exynos5250-spring.dts b/arch/arm/boot/dts/exynos5250-spring.dts index 687dfab86bc8..517b1ff2bfdf 100644 --- a/arch/arm/boot/dts/exynos5250-spring.dts +++ b/arch/arm/boot/dts/exynos5250-spring.dts @@ -64,10 +64,14 @@ vdd_pll-supply = <&s5m_ldo8_reg>; };
- panel: panel {
compatible = "simple-panel";
- };
You can't do this. "simple-panel" isn't a valid panel model. It should probably be removed from the platform_of_match table in the driver.
Okay, that means the Snow DT is wrong, too: https://patchwork.kernel.org/patch/4625441/
And the others specify it as fallback: https://patchwork.kernel.org/patch/4625461/ https://patchwork.kernel.org/patch/4625451/
A quick grep shows that many (all?) devices that use DRM panels provide simple-panel as fallback. That's probably fine as long as they also do provide the specific model. But given that simple-panel does not have a mode or physical size, I don't think even that makes sense.
On snow, the bridge chip provides the display mode instead of the panel. That is why display was working for me.
Okay, I suppose under some circumstances that might make sense. Although it's still always the panel that dictates the display timings, so the panel node needs to have a panel model specific compatible value with a matching entry in the panel-simple driver so that it can even be used in setups without a bridge.
One other thing: how does the bridge know which mode to drive? I suspect that it can drive more than one mode? Can it freely be configured or does it have a predefined set of modes? If the latter, then according to what you said above there needs to be a way to configure the bridge (via DT?) so that it reports the mode matching the panel. I wonder if that should be handled completely in code, so that for example a bridge has a panel attached it can use the panel's .get_modes() and select a matching mode among the set that it supports.
Thierry
On Wed, Jul 30, 2014 at 3:10 PM, Thierry Reding thierry.reding@gmail.com wrote:
On Wed, Jul 30, 2014 at 11:54:00AM +0530, Ajay kumar wrote:
Hi Thierry,
On Tue, Jul 29, 2014 at 5:17 PM, Thierry Reding thierry.reding@gmail.com wrote:
On Tue, Jul 29, 2014 at 01:42:09PM +0200, Andreas Färber wrote:
Am 29.07.2014 13:36, schrieb Thierry Reding:
On Tue, Jul 29, 2014 at 01:21:48PM +0200, Andreas Färber wrote:
Hi Ajay,
Am 28.07.2014 08:13, schrieb Ajay kumar: > On 7/27/14, Andreas Färber afaerber@suse.de wrote: >> Am 25.07.2014 21:22, schrieb Ajay Kumar: >>> This series is based on exynos-drm-next branch of Inki Dae's tree at: >>> git://git.kernel.org/pub/scm/linux/kernel/git/daeinki/drm-exynos.git >>> >>> I have tested this after adding few DT changes for exynos5250-snow, >>> exynos5420-peach-pit and exynos5800-peach-pi boards. >> >> I'm trying to test this with a modified exynos5250-spring DT
[...]
Unfortunately the most I got on Spring with attached DT was a blank screen with a white horizontal line in the middle.
Do I need to specify a specific panel model for Spring?
[...]
From 9172a26a8f0d0f0d170bd27e1c150ad204d8086a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20F=C3=A4rber?= afaerber@suse.de Date: Sun, 27 Jul 2014 21:58:06 +0200 Subject: [PATCH] ARM: dts: exynos5250: Add eDP/LVDS bridge to Spring MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit
Signed-off-by: Ajay Kumar ajaykumar.rs@samsung.com [AF: Redone for v6] Signed-off-by: Andreas F??rber afaerber@suse.de
arch/arm/boot/dts/exynos5250-spring.dts | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-)
diff --git a/arch/arm/boot/dts/exynos5250-spring.dts b/arch/arm/boot/dts/exynos5250-spring.dts index 687dfab86bc8..517b1ff2bfdf 100644 --- a/arch/arm/boot/dts/exynos5250-spring.dts +++ b/arch/arm/boot/dts/exynos5250-spring.dts @@ -64,10 +64,14 @@ vdd_pll-supply = <&s5m_ldo8_reg>; };
- panel: panel {
compatible = "simple-panel";
- };
You can't do this. "simple-panel" isn't a valid panel model. It should probably be removed from the platform_of_match table in the driver.
Okay, that means the Snow DT is wrong, too: https://patchwork.kernel.org/patch/4625441/
And the others specify it as fallback: https://patchwork.kernel.org/patch/4625461/ https://patchwork.kernel.org/patch/4625451/
A quick grep shows that many (all?) devices that use DRM panels provide simple-panel as fallback. That's probably fine as long as they also do provide the specific model. But given that simple-panel does not have a mode or physical size, I don't think even that makes sense.
On snow, the bridge chip provides the display mode instead of the panel. That is why display was working for me.
Okay, I suppose under some circumstances that might make sense. Although it's still always the panel that dictates the display timings, so the panel node needs to have a panel model specific compatible value with a matching entry in the panel-simple driver so that it can even be used in setups without a bridge.
Well, I am okay with adding the compatible value for specific panel model because "simple-panel" alone cannot provide width/height of the panel.
One other thing: how does the bridge know which mode to drive? I suspect that it can drive more than one mode? Can it freely be configured or does it have a predefined set of modes? If the latter, then according to what you said above there needs to be a way to configure the bridge (via DT?) so that it reports the mode matching the panel. I wonder if that should be handled completely in code, so that for example a bridge has a panel attached it can use the panel's .get_modes() and select a matching mode among the set that it supports.
ptn3460 supports a standard list of "edid-emulation" ids. As of now, we receive that as a DT entry. And, these are the list of emulation ids supported:
| Value | Resolution | Description | 0 | 1024x768 | NXP Generic | 1 | 1920x1080 | NXP Generic | 2 | 1920x1080 | NXP Generic | 3 | 1600x900 | Samsung LTM200KT | 4 | 1920x1080 | Samsung LTM230HT | 5 | 1366x768 | NXP Generic | 6 | 1600x900 | ChiMei M215HGE
As you can see, the same resolutions have different emulator ids. May be, it depends on panel vendor also. I am really not sure if we can do this. For snow(which has 1366x768 panel), we set edid-emulation as 5.
Ajay
On Wed, Jul 30, 2014 at 03:54:13PM +0530, Ajay kumar wrote:
On Wed, Jul 30, 2014 at 3:10 PM, Thierry Reding thierry.reding@gmail.com wrote:
On Wed, Jul 30, 2014 at 11:54:00AM +0530, Ajay kumar wrote:
Hi Thierry,
On Tue, Jul 29, 2014 at 5:17 PM, Thierry Reding thierry.reding@gmail.com wrote:
On Tue, Jul 29, 2014 at 01:42:09PM +0200, Andreas Färber wrote:
Am 29.07.2014 13:36, schrieb Thierry Reding:
On Tue, Jul 29, 2014 at 01:21:48PM +0200, Andreas Färber wrote: > Hi Ajay, > > Am 28.07.2014 08:13, schrieb Ajay kumar: >> On 7/27/14, Andreas Färber afaerber@suse.de wrote: >>> Am 25.07.2014 21:22, schrieb Ajay Kumar: >>>> This series is based on exynos-drm-next branch of Inki Dae's tree at: >>>> git://git.kernel.org/pub/scm/linux/kernel/git/daeinki/drm-exynos.git >>>> >>>> I have tested this after adding few DT changes for exynos5250-snow, >>>> exynos5420-peach-pit and exynos5800-peach-pi boards. >>> >>> I'm trying to test this with a modified exynos5250-spring DT
[...]
> Unfortunately the most I got on Spring with attached DT was a blank > screen with a white horizontal line in the middle. > > Do I need to specify a specific panel model for Spring?
[...]
> From 9172a26a8f0d0f0d170bd27e1c150ad204d8086a Mon Sep 17 00:00:00 2001 > From: =?UTF-8?q?Andreas=20F=C3=A4rber?= afaerber@suse.de > Date: Sun, 27 Jul 2014 21:58:06 +0200 > Subject: [PATCH] ARM: dts: exynos5250: Add eDP/LVDS bridge to Spring > MIME-Version: 1.0 > Content-Type: text/plain; charset=UTF-8 > Content-Transfer-Encoding: 8bit > > Signed-off-by: Ajay Kumar ajaykumar.rs@samsung.com > [AF: Redone for v6] > Signed-off-by: Andreas F??rber afaerber@suse.de > --- > arch/arm/boot/dts/exynos5250-spring.dts | 32 +++++++++++++++++++++++++++++++- > 1 file changed, 31 insertions(+), 1 deletion(-) > > diff --git a/arch/arm/boot/dts/exynos5250-spring.dts b/arch/arm/boot/dts/exynos5250-spring.dts > index 687dfab86bc8..517b1ff2bfdf 100644 > --- a/arch/arm/boot/dts/exynos5250-spring.dts > +++ b/arch/arm/boot/dts/exynos5250-spring.dts > @@ -64,10 +64,14 @@ > vdd_pll-supply = <&s5m_ldo8_reg>; > }; > > + panel: panel { > + compatible = "simple-panel"; > + };
You can't do this. "simple-panel" isn't a valid panel model. It should probably be removed from the platform_of_match table in the driver.
Okay, that means the Snow DT is wrong, too: https://patchwork.kernel.org/patch/4625441/
And the others specify it as fallback: https://patchwork.kernel.org/patch/4625461/ https://patchwork.kernel.org/patch/4625451/
A quick grep shows that many (all?) devices that use DRM panels provide simple-panel as fallback. That's probably fine as long as they also do provide the specific model. But given that simple-panel does not have a mode or physical size, I don't think even that makes sense.
On snow, the bridge chip provides the display mode instead of the panel. That is why display was working for me.
Okay, I suppose under some circumstances that might make sense. Although it's still always the panel that dictates the display timings, so the panel node needs to have a panel model specific compatible value with a matching entry in the panel-simple driver so that it can even be used in setups without a bridge.
Well, I am okay with adding the compatible value for specific panel model because "simple-panel" alone cannot provide width/height of the panel.
One other thing: how does the bridge know which mode to drive? I suspect that it can drive more than one mode? Can it freely be configured or does it have a predefined set of modes? If the latter, then according to what you said above there needs to be a way to configure the bridge (via DT?) so that it reports the mode matching the panel. I wonder if that should be handled completely in code, so that for example a bridge has a panel attached it can use the panel's .get_modes() and select a matching mode among the set that it supports.
ptn3460 supports a standard list of "edid-emulation" ids. As of now, we receive that as a DT entry. And, these are the list of emulation ids supported:
| Value | Resolution | Description | 0 | 1024x768 | NXP Generic | 1 | 1920x1080 | NXP Generic | 2 | 1920x1080 | NXP Generic | 3 | 1600x900 | Samsung LTM200KT | 4 | 1920x1080 | Samsung LTM230HT | 5 | 1366x768 | NXP Generic | 6 | 1600x900 | ChiMei M215HGE
As you can see, the same resolutions have different emulator ids. May be, it depends on panel vendor also. I am really not sure if we can do this. For snow(which has 1366x768 panel), we set edid-emulation as 5.
Well, modes 1, 2 and 4 as well as modes 3 and 6 must differ in some ways, otherwise there wouldn't be much point in using different IDs for them. You could try to match on more than just the active horizontal and vertical resolution.
The reason behind this is that it would allow us to keep the device tree content to a minimum and determine the proper emulation ID at runtime. But if it's too difficult to implement I won't object to keeping the edid-emulation property in DT. It just means that the device tree will contain some duplicate information that needs to be kept in sync.
Thierry
Hi Thierry,
On Wednesday 30 July 2014 11:40:54 Thierry Reding wrote:
On Wed, Jul 30, 2014 at 11:54:00AM +0530, Ajay kumar wrote:
On Tue, Jul 29, 2014 at 5:17 PM, Thierry Reding wrote:
On Tue, Jul 29, 2014 at 01:42:09PM +0200, Andreas Färber wrote:
Am 29.07.2014 13:36, schrieb Thierry Reding:
On Tue, Jul 29, 2014 at 01:21:48PM +0200, Andreas Färber wrote:
Am 28.07.2014 08:13, schrieb Ajay kumar: > On 7/27/14, Andreas Färber afaerber@suse.de wrote: >> Am 25.07.2014 21:22, schrieb Ajay Kumar: >>> This series is based on exynos-drm-next branch of Inki Dae's tree >>> at: >>> git://git.kernel.org/pub/scm/linux/kernel/git/daeinki/drm-exynos. >>> git >>> >>> I have tested this after adding few DT changes for >>> exynos5250-snow, >>> exynos5420-peach-pit and exynos5800-peach-pi boards. >> >> I'm trying to test this with a modified exynos5250-spring DT
[...]
Unfortunately the most I got on Spring with attached DT was a blank screen with a white horizontal line in the middle.
Do I need to specify a specific panel model for Spring?
[...]
From 9172a26a8f0d0f0d170bd27e1c150ad204d8086a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20F=C3=A4rber?= afaerber@suse.de Date: Sun, 27 Jul 2014 21:58:06 +0200 Subject: [PATCH] ARM: dts: exynos5250: Add eDP/LVDS bridge to Spring MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit
Signed-off-by: Ajay Kumar ajaykumar.rs@samsung.com [AF: Redone for v6] Signed-off-by: Andreas F??rber afaerber@suse.de
arch/arm/boot/dts/exynos5250-spring.dts | 32 +++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-)
diff --git a/arch/arm/boot/dts/exynos5250-spring.dts b/arch/arm/boot/dts/exynos5250-spring.dts index 687dfab86bc8..517b1ff2bfdf 100644 --- a/arch/arm/boot/dts/exynos5250-spring.dts +++ b/arch/arm/boot/dts/exynos5250-spring.dts @@ -64,10 +64,14 @@ vdd_pll-supply = <&s5m_ldo8_reg>; };
- panel: panel {
compatible = "simple-panel";
- };
You can't do this. "simple-panel" isn't a valid panel model. It should probably be removed from the platform_of_match table in the driver.
Okay, that means the Snow DT is wrong, too: https://patchwork.kernel.org/patch/4625441/
And the others specify it as fallback: https://patchwork.kernel.org/patch/4625461/ https://patchwork.kernel.org/patch/4625451/
A quick grep shows that many (all?) devices that use DRM panels provide simple-panel as fallback. That's probably fine as long as they also do provide the specific model. But given that simple-panel does not have a mode or physical size, I don't think even that makes sense.
On snow, the bridge chip provides the display mode instead of the panel. That is why display was working for me.
Okay, I suppose under some circumstances that might make sense. Although it's still always the panel that dictates the display timings, so the panel node needs to have a panel model specific compatible value with a matching entry in the panel-simple driver so that it can even be used in setups without a bridge.
One other thing: how does the bridge know which mode to drive? I suspect that it can drive more than one mode? Can it freely be configured or does it have a predefined set of modes? If the latter, then according to what you said above there needs to be a way to configure the bridge (via DT?) so that it reports the mode matching the panel. I wonder if that should be handled completely in code, so that for example a bridge has a panel attached it can use the panel's .get_modes() and select a matching mode among the set that it supports.
Yes, pretty please :-) I don't think it would be a good idea to duplicate mode information in the bridge DT node, as that's not a property of the bridge. Querying the mode at runtime is in my opinion a much better option, and would also allow switching between different modes at runtime when that makes sense.
Now, I'm not sure whether it should be the bridge driver querying the panel driver directly, or the display controller driver doing it and then configuring the bridge accordingly. The latter seems more generic to me and doesn't rely on the assumption that the bridge output will always be directly connected to a panel.
Hi Laurent,
Please find the latest series here: http://www.spinics.net/lists/dri-devel/msg66740.html
On Wed, Sep 17, 2014 at 3:23 PM, Laurent Pinchart laurent.pinchart@ideasonboard.com wrote:
Hi Thierry,
On Wednesday 30 July 2014 11:40:54 Thierry Reding wrote:
On Wed, Jul 30, 2014 at 11:54:00AM +0530, Ajay kumar wrote:
On Tue, Jul 29, 2014 at 5:17 PM, Thierry Reding wrote:
On Tue, Jul 29, 2014 at 01:42:09PM +0200, Andreas Färber wrote:
Am 29.07.2014 13:36, schrieb Thierry Reding:
On Tue, Jul 29, 2014 at 01:21:48PM +0200, Andreas Färber wrote: > Am 28.07.2014 08:13, schrieb Ajay kumar: >> On 7/27/14, Andreas Färber afaerber@suse.de wrote: >>> Am 25.07.2014 21:22, schrieb Ajay Kumar: >>>> This series is based on exynos-drm-next branch of Inki Dae's tree >>>> at: >>>> git://git.kernel.org/pub/scm/linux/kernel/git/daeinki/drm-exynos. >>>> git >>>> >>>> I have tested this after adding few DT changes for >>>> exynos5250-snow, >>>> exynos5420-peach-pit and exynos5800-peach-pi boards. >>> >>> I'm trying to test this with a modified exynos5250-spring DT
[...]
> Unfortunately the most I got on Spring with attached DT was a blank > screen with a white horizontal line in the middle. > > Do I need to specify a specific panel model for Spring?
[...]
> From 9172a26a8f0d0f0d170bd27e1c150ad204d8086a Mon Sep 17 00:00:00 > 2001 > From: =?UTF-8?q?Andreas=20F=C3=A4rber?= afaerber@suse.de > Date: Sun, 27 Jul 2014 21:58:06 +0200 > Subject: [PATCH] ARM: dts: exynos5250: Add eDP/LVDS bridge to Spring > MIME-Version: 1.0 > Content-Type: text/plain; charset=UTF-8 > Content-Transfer-Encoding: 8bit > > Signed-off-by: Ajay Kumar ajaykumar.rs@samsung.com > [AF: Redone for v6] > Signed-off-by: Andreas F??rber afaerber@suse.de > --- > > arch/arm/boot/dts/exynos5250-spring.dts | 32 +++++++++++++++++++++- > 1 file changed, 31 insertions(+), 1 deletion(-) > > diff --git a/arch/arm/boot/dts/exynos5250-spring.dts > b/arch/arm/boot/dts/exynos5250-spring.dts index > 687dfab86bc8..517b1ff2bfdf 100644 > --- a/arch/arm/boot/dts/exynos5250-spring.dts > +++ b/arch/arm/boot/dts/exynos5250-spring.dts > @@ -64,10 +64,14 @@ > vdd_pll-supply = <&s5m_ldo8_reg>; > }; > > + panel: panel { > + compatible = "simple-panel"; > + };
You can't do this. "simple-panel" isn't a valid panel model. It should probably be removed from the platform_of_match table in the driver.
Okay, that means the Snow DT is wrong, too: https://patchwork.kernel.org/patch/4625441/
And the others specify it as fallback: https://patchwork.kernel.org/patch/4625461/ https://patchwork.kernel.org/patch/4625451/
A quick grep shows that many (all?) devices that use DRM panels provide simple-panel as fallback. That's probably fine as long as they also do provide the specific model. But given that simple-panel does not have a mode or physical size, I don't think even that makes sense.
On snow, the bridge chip provides the display mode instead of the panel. That is why display was working for me.
Okay, I suppose under some circumstances that might make sense. Although it's still always the panel that dictates the display timings, so the panel node needs to have a panel model specific compatible value with a matching entry in the panel-simple driver so that it can even be used in setups without a bridge.
One other thing: how does the bridge know which mode to drive? I suspect that it can drive more than one mode? Can it freely be configured or does it have a predefined set of modes? If the latter, then according to what you said above there needs to be a way to configure the bridge (via DT?) so that it reports the mode matching the panel. I wonder if that should be handled completely in code, so that for example a bridge has a panel attached it can use the panel's .get_modes() and select a matching mode among the set that it supports.
Yes, pretty please :-) I don't think it would be a good idea to duplicate mode information in the bridge DT node, as that's not a property of the bridge. Querying the mode at runtime is in my opinion a much better option, and would also allow switching between different modes at runtime when that makes sense.
Now, I'm not sure whether it should be the bridge driver querying the panel driver directly, or the display controller driver doing it and then configuring the bridge accordingly. The latter seems more generic to me and doesn't rely on the assumption that the bridge output will always be directly connected to a panel.
-- Regards,
Laurent Pinchart
Hi Ajay,
On Wednesday 17 September 2014 15:43:04 Ajay kumar wrote:
Hi Laurent,
Please find the latest series here: http://www.spinics.net/lists/dri-devel/msg66740.html
Thank you. My comment was meant to be general though, not just for your patch series.
On Wed, Sep 17, 2014 at 3:23 PM, Laurent Pinchart wrote:
On Wednesday 30 July 2014 11:40:54 Thierry Reding wrote:
[snip]
One other thing: how does the bridge know which mode to drive? I suspect that it can drive more than one mode? Can it freely be configured or does it have a predefined set of modes? If the latter, then according to what you said above there needs to be a way to configure the bridge (via DT?) so that it reports the mode matching the panel. I wonder if that should be handled completely in code, so that for example a bridge has a panel attached it can use the panel's .get_modes() and select a matching mode among the set that it supports.
Yes, pretty please :-) I don't think it would be a good idea to duplicate mode information in the bridge DT node, as that's not a property of the bridge. Querying the mode at runtime is in my opinion a much better option, and would also allow switching between different modes at runtime when that makes sense.
Now, I'm not sure whether it should be the bridge driver querying the panel driver directly, or the display controller driver doing it and then configuring the bridge accordingly. The latter seems more generic to me and doesn't rely on the assumption that the bridge output will always be directly connected to a panel.
On Tue, Jul 29, 2014 at 01:36:46PM +0200, Thierry Reding wrote:
On Tue, Jul 29, 2014 at 01:21:48PM +0200, Andreas Färber wrote:
Hi Ajay,
Am 28.07.2014 08:13, schrieb Ajay kumar:
On 7/27/14, Andreas Färber afaerber@suse.de wrote:
Am 25.07.2014 21:22, schrieb Ajay Kumar:
This series is based on exynos-drm-next branch of Inki Dae's tree at: git://git.kernel.org/pub/scm/linux/kernel/git/daeinki/drm-exynos.git
I have tested this after adding few DT changes for exynos5250-snow, exynos5420-peach-pit and exynos5800-peach-pi boards.
I'm trying to test this with a modified exynos5250-spring DT based off kgene's for-next branch due to DT, and I run into the following:
CC drivers/gpu/drm/bridge/ptn3460.o drivers/gpu/drm/bridge/ptn3460.c: In function ‘ptn3460_post_encoder_init’: drivers/gpu/drm/bridge/ptn3460.c:275:2: error: implicit declaration of function ‘drm_connector_register’ [-Werror=implicit-function-declaration] drm_connector_register(&ptn_bridge->connector); ^
Hope this might help: http://www.spinics.net/lists/dri-devel/msg60578.html
That fixed my build, thanks.
Unfortunately the most I got on Spring with attached DT was a blank screen with a white horizontal line in the middle.
Do I need to specify a specific panel model for Spring?
For testing I re-applied your iommu patches (which btw fail now for 5420 due to disp_pd) but didn't know what to do about your panel-lvds regulator patch now that it's gone.
If I don't apply this series, then commenting out the dp-controller node gets me a working display with simplefb as before.
Regards, Andreas
-- SUSE LINUX Products GmbH, Maxfeldstr. 5, 90409 Nürnberg, Germany GF: Jeff Hawn, Jennifer Guild, Felix Imendörffer; HRB 16746 AG Nürnberg
From 9172a26a8f0d0f0d170bd27e1c150ad204d8086a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20F=C3=A4rber?= afaerber@suse.de Date: Sun, 27 Jul 2014 21:58:06 +0200 Subject: [PATCH] ARM: dts: exynos5250: Add eDP/LVDS bridge to Spring MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit
Signed-off-by: Ajay Kumar ajaykumar.rs@samsung.com [AF: Redone for v6] Signed-off-by: Andreas F??rber afaerber@suse.de
arch/arm/boot/dts/exynos5250-spring.dts | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-)
diff --git a/arch/arm/boot/dts/exynos5250-spring.dts b/arch/arm/boot/dts/exynos5250-spring.dts index 687dfab86bc8..517b1ff2bfdf 100644 --- a/arch/arm/boot/dts/exynos5250-spring.dts +++ b/arch/arm/boot/dts/exynos5250-spring.dts @@ -64,10 +64,14 @@ vdd_pll-supply = <&s5m_ldo8_reg>; };
- panel: panel {
compatible = "simple-panel";
- };
You can't do this. "simple-panel" isn't a valid panel model. It should probably be removed from the platform_of_match table in the driver.
Done.
Thierry
Hi Andreas,
On Tue, Jul 29, 2014 at 4:51 PM, Andreas Färber afaerber@suse.de wrote:
Hi Ajay,
Am 28.07.2014 08:13, schrieb Ajay kumar:
On 7/27/14, Andreas Färber afaerber@suse.de wrote:
Am 25.07.2014 21:22, schrieb Ajay Kumar:
This series is based on exynos-drm-next branch of Inki Dae's tree at: git://git.kernel.org/pub/scm/linux/kernel/git/daeinki/drm-exynos.git
I have tested this after adding few DT changes for exynos5250-snow, exynos5420-peach-pit and exynos5800-peach-pi boards.
I'm trying to test this with a modified exynos5250-spring DT based off kgene's for-next branch due to DT, and I run into the following:
CC drivers/gpu/drm/bridge/ptn3460.o drivers/gpu/drm/bridge/ptn3460.c: In function ‘ptn3460_post_encoder_init’: drivers/gpu/drm/bridge/ptn3460.c:275:2: error: implicit declaration of function ‘drm_connector_register’ [-Werror=implicit-function-declaration] drm_connector_register(&ptn_bridge->connector); ^
Hope this might help: http://www.spinics.net/lists/dri-devel/msg60578.html
That fixed my build, thanks.
Unfortunately the most I got on Spring with attached DT was a blank screen with a white horizontal line in the middle.
Then, I think bridge chip is working fine. You just need to configure the proper mode for FIMD. You can see backlight also, right?
Do I need to specify a specific panel model for Spring?
Yes! Try using "chunghwa,claa101wb01" as compatible string for panel node.
For testing I re-applied your iommu patches (which btw fail now for 5420 due to disp_pd) but didn't know what to do about your panel-lvds regulator patch now that it's gone.
Ignore that regulator patch.
Also, please attach the bootlog if possible after trying this.
Regards, Ajay
Hi Ajay,
Am 30.07.2014 08:21, schrieb Ajay kumar:
On Tue, Jul 29, 2014 at 4:51 PM, Andreas Färber afaerber@suse.de wrote:
Am 28.07.2014 08:13, schrieb Ajay kumar:
On 7/27/14, Andreas Färber afaerber@suse.de wrote:
Am 25.07.2014 21:22, schrieb Ajay Kumar:
This series is based on exynos-drm-next branch of Inki Dae's tree at: git://git.kernel.org/pub/scm/linux/kernel/git/daeinki/drm-exynos.git
I have tested this after adding few DT changes for exynos5250-snow, exynos5420-peach-pit and exynos5800-peach-pi boards.
I'm trying to test this with a modified exynos5250-spring DT based off kgene's for-next branch due to DT, and I run into the following:
Unfortunately the most I got on Spring with attached DT was a blank screen with a white horizontal line in the middle.
Then, I think bridge chip is working fine. You just need to configure the proper mode for FIMD. You can see backlight also, right?
Do I need to specify a specific panel model for Spring?
Yes! Try using "chunghwa,claa101wb01" as compatible string for panel node.
With just your v6 applied plus updated DT patch (attached) [1], I see backlight and a black screen (no white line any more). dmesg attached.
For testing I re-applied your iommu patches (which btw fail now for 5420 due to disp_pd) but didn't know what to do about your panel-lvds regulator patch now that it's gone.
Ignore that regulator patch.
Also, please attach the bootlog if possible after trying this.
If I further apply the IOMMU patches [2], I get no backlight nor USB and thus can't obtain a boot log.
Regards, Andreas
[1] https://github.com/afaerber/linux/commits/spring-next [2] https://github.com/afaerber/linux/commits/spring-bridge.v6
P.S. Note that your Snow DT patch will conflict with my Snow cleanups, shuffling some nodes around: https://patchwork.kernel.org/patch/4649471/
Andreas,
On Thu, Jul 31, 2014 at 1:02 AM, Andreas Färber afaerber@suse.de wrote:
Hi Ajay,
Am 30.07.2014 08:21, schrieb Ajay kumar:
On Tue, Jul 29, 2014 at 4:51 PM, Andreas Färber afaerber@suse.de wrote:
Am 28.07.2014 08:13, schrieb Ajay kumar:
On 7/27/14, Andreas Färber afaerber@suse.de wrote:
Am 25.07.2014 21:22, schrieb Ajay Kumar:
This series is based on exynos-drm-next branch of Inki Dae's tree at: git://git.kernel.org/pub/scm/linux/kernel/git/daeinki/drm-exynos.git
I have tested this after adding few DT changes for exynos5250-snow, exynos5420-peach-pit and exynos5800-peach-pi boards.
I'm trying to test this with a modified exynos5250-spring DT based off kgene's for-next branch due to DT, and I run into the following:
Unfortunately the most I got on Spring with attached DT was a blank screen with a white horizontal line in the middle.
Then, I think bridge chip is working fine. You just need to configure the proper mode for FIMD. You can see backlight also, right?
Do I need to specify a specific panel model for Spring?
Yes! Try using "chunghwa,claa101wb01" as compatible string for panel node.
With just your v6 applied plus updated DT patch (attached) [1], I see backlight and a black screen (no white line any more). dmesg attached.
I can see penguin's also!
For testing I re-applied your iommu patches (which btw fail now for 5420 due to disp_pd) but didn't know what to do about your panel-lvds regulator patch now that it's gone.
Ignore that regulator patch.
Also, please attach the bootlog if possible after trying this.
If I further apply the IOMMU patches [2], I get no backlight nor USB and thus can't obtain a boot log.
Regards, Andreas
[1] https://github.com/afaerber/linux/commits/spring-next [2] https://github.com/afaerber/linux/commits/spring-bridge.v6
With just the spring-bridge.v6 branch of your own tree, I am able to see bootup logo on Skate(a variant of spring which also contains ps8622). I have tried both exynos_defconfig and multi_v7_defconfig. I enable DRM, EXYNOS DRM, BRIDGE CHIPS, IOMMU, EXYNOS IOMMU in configs.
Even in your bootlogs, I can see DP getting probed. And, you say backlight is also visible. That means entire display path should be fine. Its just that you should start writing to the buffer. Have you enabled boot logos?
Ajay
P.S. Note that your Snow DT patch will conflict with my Snow cleanups, shuffling some nodes around: https://patchwork.kernel.org/patch/4649471/
-- SUSE LINUX Products GmbH, Maxfeldstr. 5, 90409 Nürnberg, Germany GF: Jeff Hawn, Jennifer Guild, Felix Imendörffer; HRB 16746 AG Nürnberg
Ajay,
Am 31.07.2014 10:38, schrieb Ajay kumar:
On Thu, Jul 31, 2014 at 1:02 AM, Andreas Färber afaerber@suse.de wrote:
Am 30.07.2014 08:21, schrieb Ajay kumar:
On Tue, Jul 29, 2014 at 4:51 PM, Andreas Färber afaerber@suse.de wrote:
Am 28.07.2014 08:13, schrieb Ajay kumar:
On 7/27/14, Andreas Färber afaerber@suse.de wrote:
Am 25.07.2014 21:22, schrieb Ajay Kumar: > This series is based on exynos-drm-next branch of Inki Dae's tree at: > git://git.kernel.org/pub/scm/linux/kernel/git/daeinki/drm-exynos.git > > I have tested this after adding few DT changes for exynos5250-snow, > exynos5420-peach-pit and exynos5800-peach-pi boards.
I'm trying to test this with a modified exynos5250-spring DT based off kgene's for-next branch due to DT, and I run into the following:
Unfortunately the most I got on Spring with attached DT was a blank screen with a white horizontal line in the middle.
Then, I think bridge chip is working fine. You just need to configure the proper mode for FIMD. You can see backlight also, right?
Do I need to specify a specific panel model for Spring?
Yes! Try using "chunghwa,claa101wb01" as compatible string for panel node.
With just your v6 applied plus updated DT patch (attached) [1], I see backlight and a black screen (no white line any more). dmesg attached.
I can see penguin's also!
See below...
For testing I re-applied your iommu patches (which btw fail now for 5420 due to disp_pd) but didn't know what to do about your panel-lvds regulator patch now that it's gone.
Ignore that regulator patch.
Also, please attach the bootlog if possible after trying this.
If I further apply the IOMMU patches [2], I get no backlight nor USB and thus can't obtain a boot log.
Regards, Andreas
(I updated the branch meantime, so what I meant was spring-bridge.v6~2)
[2] https://github.com/afaerber/linux/commits/spring-bridge.v6
With just the spring-bridge.v6 branch of your own tree, I am able to see bootup logo on Skate(a variant of spring which also contains ps8622). I have tried both exynos_defconfig and multi_v7_defconfig. I enable DRM, EXYNOS DRM, BRIDGE CHIPS, IOMMU, EXYNOS IOMMU in configs.
Even in your bootlogs, I can see DP getting probed. And, you say backlight is also visible. That means entire display path should be fine. Its just that you should start writing to the buffer. Have you enabled boot logos?
Let me clarify: U-Boot uses the display [*], so it is powered and I see penguins initially. Then, when drm gets initialized, the screen goes black and no longer prints kernel messages or systemd output or X11 gdm login screen.
Since drm stuff is the only variance here and it works with simplefb, surely something prints to some buffer!
Andreas
[*] https://github.com/afaerber/u-boot/commits/spring
On Thu, Jul 31, 2014 at 2:27 PM, Andreas Färber afaerber@suse.de wrote:
Ajay,
Am 31.07.2014 10:38, schrieb Ajay kumar:
On Thu, Jul 31, 2014 at 1:02 AM, Andreas Färber afaerber@suse.de wrote:
Am 30.07.2014 08:21, schrieb Ajay kumar:
On Tue, Jul 29, 2014 at 4:51 PM, Andreas Färber afaerber@suse.de wrote:
Am 28.07.2014 08:13, schrieb Ajay kumar:
On 7/27/14, Andreas Färber afaerber@suse.de wrote: > Am 25.07.2014 21:22, schrieb Ajay Kumar: >> This series is based on exynos-drm-next branch of Inki Dae's tree at: >> git://git.kernel.org/pub/scm/linux/kernel/git/daeinki/drm-exynos.git >> >> I have tested this after adding few DT changes for exynos5250-snow, >> exynos5420-peach-pit and exynos5800-peach-pi boards. > > I'm trying to test this with a modified exynos5250-spring DT based off > kgene's for-next branch due to DT, and I run into the following:
Unfortunately the most I got on Spring with attached DT was a blank screen with a white horizontal line in the middle.
Then, I think bridge chip is working fine. You just need to configure the proper mode for FIMD. You can see backlight also, right?
Do I need to specify a specific panel model for Spring?
Yes! Try using "chunghwa,claa101wb01" as compatible string for panel node.
With just your v6 applied plus updated DT patch (attached) [1], I see backlight and a black screen (no white line any more). dmesg attached.
I can see penguin's also!
See below...
For testing I re-applied your iommu patches (which btw fail now for 5420 due to disp_pd) but didn't know what to do about your panel-lvds regulator patch now that it's gone.
Ignore that regulator patch.
Also, please attach the bootlog if possible after trying this.
If I further apply the IOMMU patches [2], I get no backlight nor USB and thus can't obtain a boot log.
Regards, Andreas
(I updated the branch meantime, so what I meant was spring-bridge.v6~2)
[2] https://github.com/afaerber/linux/commits/spring-bridge.v6
With just the spring-bridge.v6 branch of your own tree, I am able to see bootup logo on Skate(a variant of spring which also contains ps8622). I have tried both exynos_defconfig and multi_v7_defconfig. I enable DRM, EXYNOS DRM, BRIDGE CHIPS, IOMMU, EXYNOS IOMMU in configs.
Even in your bootlogs, I can see DP getting probed. And, you say backlight is also visible. That means entire display path should be fine. Its just that you should start writing to the buffer. Have you enabled boot logos?
Let me clarify: U-Boot uses the display [*], so it is powered and I see penguins initially. Then, when drm gets initialized, the screen goes black and no longer prints kernel messages or systemd output or X11 gdm login screen.
Since drm stuff is the only variance here and it works with simplefb, surely something prints to some buffer!
Well, I cannot really help you with this. I think the ui process/X server is getting terminated for some reason. That might be some issue induced by the platform/drm lib. This cannot be an issue induced by the bridge chip series, definitely not! I am able to run modetest, and also able to see random data on display by doing "cat /dev/urandom > /dev/fb0"
Ajay
On Thu, Jul 31, 2014 at 10:57:55AM +0200, Andreas Färber wrote:
Am 31.07.2014 10:38, schrieb Ajay kumar:
[...]
With just the spring-bridge.v6 branch of your own tree, I am able to see bootup logo on Skate(a variant of spring which also contains ps8622). I have tried both exynos_defconfig and multi_v7_defconfig. I enable DRM, EXYNOS DRM, BRIDGE CHIPS, IOMMU, EXYNOS IOMMU in configs.
Even in your bootlogs, I can see DP getting probed. And, you say backlight is also visible. That means entire display path should be fine. Its just that you should start writing to the buffer. Have you enabled boot logos?
Let me clarify: U-Boot uses the display [*], so it is powered and I see penguins initially. Then, when drm gets initialized, the screen goes black and no longer prints kernel messages or systemd output or X11 gdm login screen.
Who's displaying the penguins? If you're referring to the Linux boot logo then it shouldn't be displayed at all until after DRM has been initialized (and the framebuffer console been set up).
Since drm stuff is the only variance here and it works with simplefb, surely something prints to some buffer!
If you have something like simplefb enabled in addition to a DRM driver, then perhaps the DRM driver isn't properly taking over the framebuffer console.
Thierry
Am 31.07.2014 12:23, schrieb Thierry Reding:
On Thu, Jul 31, 2014 at 10:57:55AM +0200, Andreas Färber wrote:
Am 31.07.2014 10:38, schrieb Ajay kumar:
[...]
With just the spring-bridge.v6 branch of your own tree, I am able to see bootup logo on Skate(a variant of spring which also contains ps8622). I have tried both exynos_defconfig and multi_v7_defconfig. I enable DRM, EXYNOS DRM, BRIDGE CHIPS, IOMMU, EXYNOS IOMMU in configs.
Even in your bootlogs, I can see DP getting probed. And, you say backlight is also visible. That means entire display path should be fine. Its just that you should start writing to the buffer. Have you enabled boot logos?
Let me clarify: U-Boot uses the display [*], so it is powered and I see penguins initially. Then, when drm gets initialized, the screen goes black and no longer prints kernel messages or systemd output or X11 gdm login screen.
Who's displaying the penguins? If you're referring to the Linux boot logo then it shouldn't be displayed at all until after DRM has been initialized (and the framebuffer console been set up).
Yes, I'm referring to the default boot logo, but before the boot messages (console=tty1) indicate that [drm] driver is being initialized.
Since drm stuff is the only variance here and it works with simplefb, surely something prints to some buffer!
If you have something like simplefb enabled in addition to a DRM driver, then perhaps the DRM driver isn't properly taking over the framebuffer console.
Okay, that's worth a try. Around v4 of this series it was not a problem.
Thanks, Andreas
Am 31.07.2014 12:23, schrieb Thierry Reding:
On Thu, Jul 31, 2014 at 10:57:55AM +0200, Andreas Färber wrote:
Am 31.07.2014 10:38, schrieb Ajay kumar:
With just the spring-bridge.v6 branch of your own tree, I am able to see bootup logo on Skate(a variant of spring which also contains ps8622). I have tried both exynos_defconfig and multi_v7_defconfig. I enable DRM, EXYNOS DRM, BRIDGE CHIPS, IOMMU, EXYNOS IOMMU in configs.
[...]
If you have something like simplefb enabled in addition to a DRM driver, then perhaps the DRM driver isn't properly taking over the framebuffer console.
So, with simplefb reverted in U-Boot and ...
* with just the v6 applied (...~2), I get only a black screen from Linux, no penguins, but the backlight seems on. System comes up okay, ssh available, and nothing stands out in dmesg.
* with the two iommu patches on top, something breaks badly, no backlight, no USB/network, system inaccessible.
I.e. U-Boot had no noticeable impact on these symptoms.
* with the iommu patches, but dp-controller, ps8622, panel commented out, I do get backlight but USB/network not working, system inaccessible.
With simplefb support in U-Boot and with just v6 applied, but dp-controller, ps8622, panel commented out, things work as well as before, i.e. this series has no bad side effects. Note that I never claimed Ajay's series were broken, just reported that things regressed for me from v4, which may well be DT-related.
The iommu patches interfere with my USB somehow (none or not all devices powered; with v4, plugging my wifi dongle led to oops but ethernet dongle worked, so not entirely new symptom), which is bad since my rootfs is on USB. The internal SDIO-connected Wifi is not enabled yet, so USB based network is my only alternative to a working display once we reach userspace.
Regards, Andreas
Andreas,
On Thu, Jul 31, 2014 at 7:52 PM, Andreas Färber afaerber@suse.de wrote:
Am 31.07.2014 12:23, schrieb Thierry Reding:
On Thu, Jul 31, 2014 at 10:57:55AM +0200, Andreas Färber wrote:
Am 31.07.2014 10:38, schrieb Ajay kumar:
With just the spring-bridge.v6 branch of your own tree, I am able to see bootup logo on Skate(a variant of spring which also contains ps8622). I have tried both exynos_defconfig and multi_v7_defconfig. I enable DRM, EXYNOS DRM, BRIDGE CHIPS, IOMMU, EXYNOS IOMMU in configs.
[...]
If you have something like simplefb enabled in addition to a DRM driver, then perhaps the DRM driver isn't properly taking over the framebuffer console.
So, with simplefb reverted in U-Boot and ...
- with just the v6 applied (...~2), I get only a black screen from
Linux, no penguins, but the backlight seems on. System comes up okay, ssh available, and nothing stands out in dmesg.
- with the two iommu patches on top, something breaks badly, no
backlight, no USB/network, system inaccessible. I.e. U-Boot had no noticeable impact on these symptoms.
- with the iommu patches, but dp-controller, ps8622, panel commented
out, I do get backlight but USB/network not working, system inaccessible.
With simplefb support in U-Boot and with just v6 applied, but dp-controller, ps8622, panel commented out, things work as well as before, i.e. this series has no bad side effects. Note that I never claimed Ajay's series were broken, just reported that things regressed for me from v4, which may well be DT-related.
The iommu patches interfere with my USB somehow (none or not all devices powered; with v4, plugging my wifi dongle led to oops but ethernet dongle worked, so not entirely new symptom), which is bad since my rootfs is on USB. The internal SDIO-connected Wifi is not enabled yet, so USB based network is my only alternative to a working display once we reach userspace.
Well, there are 2 variants here: 1) Bootloader 2) config
Type of the bootloader should not matter unless its switching on FET1 and FET6 of tps65090 for you.
But, config can be different! I have attached a config i used to get display with your latest spring-bridge.v6. Uncomment the DT nodes for DP and bridge chip and you should be able to get display and also the login.
I think that you have not selected all the configs needed for IOMMU to work properly. When I deselected few IOMMU configs, I could see system crashing when FIMD + DP path was being initialized. May be, that is why your USB stops working. Even I have root file system on USB drive.
Just give a try with this config and let me know your observation. If you still observe the same behavior, may be I need to flash new bootloader onto my board and try. :(
Ajay
Hi Ajay,
Am 01.08.2014 09:02, schrieb Ajay kumar:
On Thu, Jul 31, 2014 at 7:52 PM, Andreas Färber afaerber@suse.de wrote:
So, with simplefb reverted in U-Boot and ...
- with just the v6 applied (...~2), I get only a black screen from
Linux, no penguins, but the backlight seems on. System comes up okay, ssh available, and nothing stands out in dmesg.
- with the two iommu patches on top, something breaks badly, no
backlight, no USB/network, system inaccessible. I.e. U-Boot had no noticeable impact on these symptoms.
- with the iommu patches, but dp-controller, ps8622, panel commented
out, I do get backlight but USB/network not working, system inaccessible.
With simplefb support in U-Boot and with just v6 applied, but dp-controller, ps8622, panel commented out, things work as well as before, i.e. this series has no bad side effects. Note that I never claimed Ajay's series were broken, just reported that things regressed for me from v4, which may well be DT-related.
The iommu patches interfere with my USB somehow (none or not all devices powered; with v4, plugging my wifi dongle led to oops but ethernet dongle worked, so not entirely new symptom), which is bad since my rootfs is on USB. The internal SDIO-connected Wifi is not enabled yet, so USB based network is my only alternative to a working display once we reach userspace.
Well, there are 2 variants here:
- Bootloader
- config
Type of the bootloader should not matter unless its switching on FET1 and FET6 of tps65090 for you.
But, config can be different! I have attached a config i used to get display with your latest spring-bridge.v6. Uncomment the DT nodes for DP and bridge chip and you should be able to get display and also the login.
I think that you have not selected all the configs needed for IOMMU to work properly. When I deselected few IOMMU configs, I could see system crashing when FIMD + DP path was being initialized. May be, that is why your USB stops working. Even I have root file system on USB drive.
Find attached a diff between our configs. The following stand out:
* I have LPAE enabled * you don't have DMA enabled * I have CMA enabled (like in the new defconfigs) * I have the arch timer disabled (which you suggested earlier for delay) * I have more devices enabled (SPI, PWM, cpufreq, watchdog, ...)
Andreas
Ajay,
Am 01.08.2014 09:02, schrieb Ajay kumar:
On Thu, Jul 31, 2014 at 7:52 PM, Andreas Färber afaerber@suse.de wrote:
Am 31.07.2014 12:23, schrieb Thierry Reding:
On Thu, Jul 31, 2014 at 10:57:55AM +0200, Andreas Färber wrote:
Am 31.07.2014 10:38, schrieb Ajay kumar:
With just the spring-bridge.v6 branch of your own tree, I am able to see bootup logo on Skate(a variant of spring which also contains ps8622). I have tried both exynos_defconfig and multi_v7_defconfig. I enable DRM, EXYNOS DRM, BRIDGE CHIPS, IOMMU, EXYNOS IOMMU in configs.
[...]
If you have something like simplefb enabled in addition to a DRM driver, then perhaps the DRM driver isn't properly taking over the framebuffer console.
So, with simplefb reverted in U-Boot and ...
- with just the v6 applied (...~2), I get only a black screen from
Linux, no penguins, but the backlight seems on. System comes up okay, ssh available, and nothing stands out in dmesg.
- with the two iommu patches on top, something breaks badly, no
backlight, no USB/network, system inaccessible. I.e. U-Boot had no noticeable impact on these symptoms.
- with the iommu patches, but dp-controller, ps8622, panel commented
out, I do get backlight but USB/network not working, system inaccessible.
With simplefb support in U-Boot and with just v6 applied, but dp-controller, ps8622, panel commented out, things work as well as before, i.e. this series has no bad side effects. Note that I never claimed Ajay's series were broken, just reported that things regressed for me from v4, which may well be DT-related.
The iommu patches interfere with my USB somehow (none or not all devices powered; with v4, plugging my wifi dongle led to oops but ethernet dongle worked, so not entirely new symptom), which is bad since my rootfs is on USB. The internal SDIO-connected Wifi is not enabled yet, so USB based network is my only alternative to a working display once we reach userspace.
Well, there are 2 variants here:
- Bootloader
- config
Unfortunately your config doesn't work for me either, on my latest spring-bridge.v6 branch.
Type of the bootloader should not matter unless its switching on FET1 and FET6 of tps65090 for you.
It does switch them on, if I'm reading correctly:
https://github.com/afaerber/u-boot/blob/spring/board/samsung/smdk5250/smdk52...
https://github.com/afaerber/u-boot/blob/spring/board/samsung/smdk5250/smdk52...
Another observation I made is that sometimes in my testing (didn't spot a pattern yet) after reboot or power-off/power-on the initial white screen with the warning did not come up (no backlight), but Ctrl+U worked fine and chain-loaded nv u-boot came up on screen okay.
Andreas
On Sat, Jul 26, 2014 at 12:52:02AM +0530, Ajay Kumar wrote:
This series is based on exynos-drm-next branch of Inki Dae's tree at: git://git.kernel.org/pub/scm/linux/kernel/git/daeinki/drm-exynos.git
I have tested this after adding few DT changes for exynos5250-snow, exynos5420-peach-pit and exynos5800-peach-pi boards.
Hi Ajay,
I very much like where this is going. There are a few rough edges still, but generally this is pretty much what I had imagined. Thanks for bearing with me.
Generally I think it would be safer to restructure some of the patches a little so that they can be more easily applied and keep bisectability. I'll go into more detail in the individual patches.
Thierry
dri-devel@lists.freedesktop.org