From 48074fceaa26f8c08f00fe77b940b971a1482cd2 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 8 Sep 2017 14:47:03 +0200 Subject: drm/pl111: Cleanup local header file The header file contains prototypes for two nonexisting functions. Get rid of them. Reviewed-by: Eric Anholt Signed-off-by: Linus Walleij Link: https://patchwork.freedesktop.org/patch/msgid/20170908124709.4758-1-linus.walleij@linaro.org --- drivers/gpu/drm/pl111/pl111_drm.h | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers/gpu/drm/pl111') diff --git a/drivers/gpu/drm/pl111/pl111_drm.h b/drivers/gpu/drm/pl111/pl111_drm.h index 5c685bfc8fdc..a97f303f6833 100644 --- a/drivers/gpu/drm/pl111/pl111_drm.h +++ b/drivers/gpu/drm/pl111/pl111_drm.h @@ -58,10 +58,6 @@ int pl111_enable_vblank(struct drm_device *drm, unsigned int crtc); void pl111_disable_vblank(struct drm_device *drm, unsigned int crtc); irqreturn_t pl111_irq(int irq, void *data); int pl111_connector_init(struct drm_device *dev); -int pl111_encoder_init(struct drm_device *dev); -int pl111_dumb_create(struct drm_file *file_priv, - struct drm_device *dev, - struct drm_mode_create_dumb *args); int pl111_debugfs_init(struct drm_minor *minor); #endif /* _PL111_DRM_H_ */ -- cgit From bcaf9ff425cc2dc9a16373f9d9e36354e2f09a52 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 8 Sep 2017 14:47:04 +0200 Subject: drm/pl111: Add all registers to debugfs This adds all the main control registers to the debugfs register file. This was helpful for my debugging so it will likely help others as well. Reviewed-by: Eric Anholt Signed-off-by: Linus Walleij Link: https://patchwork.freedesktop.org/patch/msgid/20170908124709.4758-2-linus.walleij@linaro.org --- drivers/gpu/drm/pl111/pl111_debugfs.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/gpu/drm/pl111') diff --git a/drivers/gpu/drm/pl111/pl111_debugfs.c b/drivers/gpu/drm/pl111/pl111_debugfs.c index 0d9dee199b2c..7ddc7e3b9e7d 100644 --- a/drivers/gpu/drm/pl111/pl111_debugfs.c +++ b/drivers/gpu/drm/pl111/pl111_debugfs.c @@ -22,8 +22,14 @@ static const struct { REGDEF(CLCD_TIM2), REGDEF(CLCD_TIM3), REGDEF(CLCD_UBAS), + REGDEF(CLCD_LBAS), REGDEF(CLCD_PL111_CNTL), REGDEF(CLCD_PL111_IENB), + REGDEF(CLCD_PL111_RIS), + REGDEF(CLCD_PL111_MIS), + REGDEF(CLCD_PL111_ICR), + REGDEF(CLCD_PL111_UCUR), + REGDEF(CLCD_PL111_LCUR), }; int pl111_debugfs_regs(struct seq_file *m, void *unused) -- cgit From 001485d5255cb17e99aa9e3712e43865b92d6adc Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 8 Sep 2017 14:47:05 +0200 Subject: drm/pl111: Replace custom connector with panel bridge This replaces the custom connector in the PL111 with the panel bridge helper. This works nicely for all standard panels, but since there are several PL11x-based systems that will need to use the dumb VGA connector bridge we use drm_of_find_panel_or_bridge() and make some headroom for dealing with bridges that are not panels as well, and drop a TODO in the code. Reviewed-by: Eric Anholt Signed-off-by: Linus Walleij Link: https://patchwork.freedesktop.org/patch/msgid/20170908124709.4758-3-linus.walleij@linaro.org --- drivers/gpu/drm/pl111/Kconfig | 3 +- drivers/gpu/drm/pl111/Makefile | 3 +- drivers/gpu/drm/pl111/pl111_connector.c | 126 -------------------------------- drivers/gpu/drm/pl111/pl111_display.c | 13 +--- drivers/gpu/drm/pl111/pl111_drm.h | 17 ++--- drivers/gpu/drm/pl111/pl111_drv.c | 55 +++++++++----- 6 files changed, 49 insertions(+), 168 deletions(-) delete mode 100644 drivers/gpu/drm/pl111/pl111_connector.c (limited to 'drivers/gpu/drm/pl111') diff --git a/drivers/gpu/drm/pl111/Kconfig b/drivers/gpu/drm/pl111/Kconfig index bbfba87cd1a8..e5e2abd66491 100644 --- a/drivers/gpu/drm/pl111/Kconfig +++ b/drivers/gpu/drm/pl111/Kconfig @@ -6,7 +6,8 @@ config DRM_PL111 select DRM_KMS_HELPER select DRM_KMS_CMA_HELPER select DRM_GEM_CMA_HELPER - select DRM_PANEL + select DRM_BRIDGE + select DRM_PANEL_BRIDGE select VT_HW_CONSOLE_BINDING if FRAMEBUFFER_CONSOLE help Choose this option for DRM support for the PL111 CLCD controller. diff --git a/drivers/gpu/drm/pl111/Makefile b/drivers/gpu/drm/pl111/Makefile index 59483d610ef5..c5f8f9684848 100644 --- a/drivers/gpu/drm/pl111/Makefile +++ b/drivers/gpu/drm/pl111/Makefile @@ -1,5 +1,4 @@ -pl111_drm-y += pl111_connector.o \ - pl111_display.o \ +pl111_drm-y += pl111_display.o \ pl111_drv.o pl111_drm-$(CONFIG_DEBUG_FS) += pl111_debugfs.o diff --git a/drivers/gpu/drm/pl111/pl111_connector.c b/drivers/gpu/drm/pl111/pl111_connector.c deleted file mode 100644 index d335f9a29ce4..000000000000 --- a/drivers/gpu/drm/pl111/pl111_connector.c +++ /dev/null @@ -1,126 +0,0 @@ -/* - * (C) COPYRIGHT 2012-2013 ARM Limited. All rights reserved. - * - * Parts of this file were based on sources as follows: - * - * Copyright (c) 2006-2008 Intel Corporation - * Copyright (c) 2007 Dave Airlie - * Copyright (C) 2011 Texas Instruments - * - * This program is free software and is provided to you under the terms of the - * GNU General Public License version 2 as published by the Free Software - * Foundation, and any use by you of this program is subject to the terms of - * such GNU licence. - * - */ - -/** - * pl111_drm_connector.c - * Implementation of the connector functions for PL111 DRM - */ -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "pl111_drm.h" - -static void pl111_connector_destroy(struct drm_connector *connector) -{ - struct pl111_drm_connector *pl111_connector = - to_pl111_connector(connector); - - if (pl111_connector->panel) - drm_panel_detach(pl111_connector->panel); - - drm_connector_unregister(connector); - drm_connector_cleanup(connector); -} - -static enum drm_connector_status pl111_connector_detect(struct drm_connector - *connector, bool force) -{ - struct pl111_drm_connector *pl111_connector = - to_pl111_connector(connector); - - return (pl111_connector->panel ? - connector_status_connected : - connector_status_disconnected); -} - -static int pl111_connector_helper_get_modes(struct drm_connector *connector) -{ - struct pl111_drm_connector *pl111_connector = - to_pl111_connector(connector); - - if (!pl111_connector->panel) - return 0; - - return drm_panel_get_modes(pl111_connector->panel); -} - -const struct drm_connector_funcs connector_funcs = { - .fill_modes = drm_helper_probe_single_connector_modes, - .destroy = pl111_connector_destroy, - .detect = pl111_connector_detect, - .reset = drm_atomic_helper_connector_reset, - .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, - .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, -}; - -const struct drm_connector_helper_funcs connector_helper_funcs = { - .get_modes = pl111_connector_helper_get_modes, -}; - -/* Walks the OF graph to find the panel node and then asks DRM to look - * up the panel. - */ -static struct drm_panel *pl111_get_panel(struct device *dev) -{ - struct device_node *endpoint, *panel_node; - struct device_node *np = dev->of_node; - struct drm_panel *panel; - - endpoint = of_graph_get_next_endpoint(np, NULL); - if (!endpoint) { - dev_err(dev, "no endpoint to fetch panel\n"); - return NULL; - } - - /* don't proceed if we have an endpoint but no panel_node tied to it */ - panel_node = of_graph_get_remote_port_parent(endpoint); - of_node_put(endpoint); - if (!panel_node) { - dev_err(dev, "no valid panel node\n"); - return NULL; - } - - panel = of_drm_find_panel(panel_node); - of_node_put(panel_node); - - return panel; -} - -int pl111_connector_init(struct drm_device *dev) -{ - struct pl111_drm_dev_private *priv = dev->dev_private; - struct pl111_drm_connector *pl111_connector = &priv->connector; - struct drm_connector *connector = &pl111_connector->connector; - - drm_connector_init(dev, connector, &connector_funcs, - DRM_MODE_CONNECTOR_DPI); - drm_connector_helper_add(connector, &connector_helper_funcs); - - pl111_connector->panel = pl111_get_panel(dev->dev); - if (pl111_connector->panel) - drm_panel_attach(pl111_connector->panel, connector); - - return 0; -} - diff --git a/drivers/gpu/drm/pl111/pl111_display.c b/drivers/gpu/drm/pl111/pl111_display.c index b58c988d9da0..9caf50d130f4 100644 --- a/drivers/gpu/drm/pl111/pl111_display.c +++ b/drivers/gpu/drm/pl111/pl111_display.c @@ -21,7 +21,6 @@ #include #include -#include #include #include #include @@ -94,7 +93,7 @@ static void pl111_display_enable(struct drm_simple_display_pipe *pipe, struct pl111_drm_dev_private *priv = drm->dev_private; const struct drm_display_mode *mode = &cstate->mode; struct drm_framebuffer *fb = plane->state->fb; - struct drm_connector *connector = &priv->connector.connector; + struct drm_connector *connector = priv->connector; u32 cntl; u32 ppl, hsw, hfp, hbp; u32 lpp, vsw, vfp, vbp; @@ -156,8 +155,6 @@ static void pl111_display_enable(struct drm_simple_display_pipe *pipe, writel(0, priv->regs + CLCD_TIM3); - drm_panel_prepare(priv->connector.panel); - /* Enable and Power Up */ cntl = CNTL_LCDEN | CNTL_LCDTFT | CNTL_LCDPWR | CNTL_LCDVCOMP(1); @@ -204,8 +201,6 @@ static void pl111_display_enable(struct drm_simple_display_pipe *pipe, writel(cntl, priv->regs + CLCD_PL111_CNTL); - drm_panel_enable(priv->connector.panel); - drm_crtc_vblank_on(crtc); } @@ -217,13 +212,9 @@ void pl111_display_disable(struct drm_simple_display_pipe *pipe) drm_crtc_vblank_off(crtc); - drm_panel_disable(priv->connector.panel); - /* Disable and Power Down */ writel(0, priv->regs + CLCD_PL111_CNTL); - drm_panel_unprepare(priv->connector.panel); - clk_disable_unprepare(priv->clk); } @@ -458,7 +449,7 @@ int pl111_display_init(struct drm_device *drm) ret = drm_simple_display_pipe_init(drm, &priv->pipe, &pl111_display_funcs, formats, ARRAY_SIZE(formats), - NULL, &priv->connector.connector); + NULL, priv->connector); if (ret) return ret; diff --git a/drivers/gpu/drm/pl111/pl111_drm.h b/drivers/gpu/drm/pl111/pl111_drm.h index a97f303f6833..000534d85b43 100644 --- a/drivers/gpu/drm/pl111/pl111_drm.h +++ b/drivers/gpu/drm/pl111/pl111_drm.h @@ -21,21 +21,22 @@ #include #include +#include +#include +#include +#include #include #define CLCD_IRQ_NEXTBASE_UPDATE BIT(2) struct drm_minor; -struct pl111_drm_connector { - struct drm_connector connector; - struct drm_panel *panel; -}; - struct pl111_drm_dev_private { struct drm_device *drm; - struct pl111_drm_connector connector; + struct drm_connector *connector; + struct drm_panel *panel; + struct drm_bridge *bridge; struct drm_simple_display_pipe pipe; struct drm_fbdev_cma *fbdev; @@ -50,14 +51,10 @@ struct pl111_drm_dev_private { spinlock_t tim2_lock; }; -#define to_pl111_connector(x) \ - container_of(x, struct pl111_drm_connector, connector) - int pl111_display_init(struct drm_device *dev); int pl111_enable_vblank(struct drm_device *drm, unsigned int crtc); void pl111_disable_vblank(struct drm_device *drm, unsigned int crtc); irqreturn_t pl111_irq(int irq, void *data); -int pl111_connector_init(struct drm_device *dev); int pl111_debugfs_init(struct drm_minor *minor); #endif /* _PL111_DRM_H_ */ diff --git a/drivers/gpu/drm/pl111/pl111_drv.c b/drivers/gpu/drm/pl111/pl111_drv.c index 581c452cede1..7dae687a8df4 100644 --- a/drivers/gpu/drm/pl111/pl111_drv.c +++ b/drivers/gpu/drm/pl111/pl111_drv.c @@ -68,6 +68,9 @@ #include #include #include +#include +#include +#include #include "pl111_drm.h" @@ -83,6 +86,8 @@ static int pl111_modeset_init(struct drm_device *dev) { struct drm_mode_config *mode_config; struct pl111_drm_dev_private *priv = dev->dev_private; + struct drm_panel *panel; + struct drm_bridge *bridge; int ret = 0; drm_mode_config_init(dev); @@ -93,34 +98,43 @@ static int pl111_modeset_init(struct drm_device *dev) mode_config->min_height = 1; mode_config->max_height = 768; - ret = pl111_connector_init(dev); - if (ret) { - dev_err(dev->dev, "Failed to create pl111_drm_connector\n"); - goto out_config; - } - - /* Don't actually attach if we didn't find a drm_panel - * attached to us. This will allow a kernel to include both - * the fbdev pl111 driver and this one, and choose between - * them based on which subsystem has support for the panel. - */ - if (!priv->connector.panel) { - dev_info(dev->dev, - "Disabling due to lack of DRM panel device.\n"); - ret = -ENODEV; - goto out_config; + ret = drm_of_find_panel_or_bridge(dev->dev->of_node, + 0, 0, &panel, &bridge); + if (ret && ret != -ENODEV) + return ret; + if (panel) { + bridge = drm_panel_bridge_add(panel, + DRM_MODE_CONNECTOR_Unknown); + if (IS_ERR(bridge)) { + ret = PTR_ERR(bridge); + goto out_config; + } + /* + * TODO: when we are using a different bridge than a panel + * (such as a dumb VGA connector) we need to devise a different + * method to get the connector out of the bridge. + */ } ret = pl111_display_init(dev); if (ret != 0) { dev_err(dev->dev, "Failed to init display\n"); - goto out_config; + goto out_bridge; } + ret = drm_simple_display_pipe_attach_bridge(&priv->pipe, + bridge); + if (ret) + return ret; + + priv->bridge = bridge; + priv->panel = panel; + priv->connector = panel->connector; + ret = drm_vblank_init(dev, 1); if (ret != 0) { dev_err(dev->dev, "Failed to init vblank\n"); - goto out_config; + goto out_bridge; } drm_mode_config_reset(dev); @@ -132,6 +146,9 @@ static int pl111_modeset_init(struct drm_device *dev) goto finish; +out_bridge: + if (panel) + drm_panel_bridge_remove(bridge); out_config: drm_mode_config_cleanup(dev); finish: @@ -236,6 +253,8 @@ static int pl111_amba_remove(struct amba_device *amba_dev) drm_dev_unregister(drm); if (priv->fbdev) drm_fbdev_cma_fini(priv->fbdev); + if (priv->panel) + drm_panel_bridge_remove(priv->bridge); drm_mode_config_cleanup(drm); drm_dev_unref(drm); -- cgit From fa83306cd104d24793a9a833f7d8a4c74c81809a Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 8 Sep 2017 14:47:06 +0200 Subject: drm/pl111: Enable PL110 variant We detect and enable the use of the PL110 variant, an earlier incarnation of PL111. The only real difference is that the control and interrupt enable registers have swapped place. The Versatile AB and Versatile PB have a variant inbetween PL110 and PL111, it is PL110 but they have already swapped the two registers so those two need a bit of special handling. Reviewed-by: Eric Anholt Signed-off-by: Linus Walleij Link: https://patchwork.freedesktop.org/patch/msgid/20170908124709.4758-4-linus.walleij@linaro.org --- drivers/gpu/drm/pl111/pl111_display.c | 30 ++++-------- drivers/gpu/drm/pl111/pl111_drm.h | 17 +++++++ drivers/gpu/drm/pl111/pl111_drv.c | 86 +++++++++++++++++++++++++++++++++-- 3 files changed, 108 insertions(+), 25 deletions(-) (limited to 'drivers/gpu/drm/pl111') diff --git a/drivers/gpu/drm/pl111/pl111_display.c b/drivers/gpu/drm/pl111/pl111_display.c index 9caf50d130f4..c08706be4b7e 100644 --- a/drivers/gpu/drm/pl111/pl111_display.c +++ b/drivers/gpu/drm/pl111/pl111_display.c @@ -199,7 +199,7 @@ static void pl111_display_enable(struct drm_simple_display_pipe *pipe, break; } - writel(cntl, priv->regs + CLCD_PL111_CNTL); + writel(cntl, priv->regs + priv->ctrl); drm_crtc_vblank_on(crtc); } @@ -213,7 +213,7 @@ void pl111_display_disable(struct drm_simple_display_pipe *pipe) drm_crtc_vblank_off(crtc); /* Disable and Power Down */ - writel(0, priv->regs + CLCD_PL111_CNTL); + writel(0, priv->regs + priv->ctrl); clk_disable_unprepare(priv->clk); } @@ -251,7 +251,7 @@ int pl111_enable_vblank(struct drm_device *drm, unsigned int crtc) { struct pl111_drm_dev_private *priv = drm->dev_private; - writel(CLCD_IRQ_NEXTBASE_UPDATE, priv->regs + CLCD_PL111_IENB); + writel(CLCD_IRQ_NEXTBASE_UPDATE, priv->regs + priv->ienb); return 0; } @@ -260,7 +260,7 @@ void pl111_disable_vblank(struct drm_device *drm, unsigned int crtc) { struct pl111_drm_dev_private *priv = drm->dev_private; - writel(0, priv->regs + CLCD_PL111_IENB); + writel(0, priv->regs + priv->ienb); } static int pl111_display_prepare_fb(struct drm_simple_display_pipe *pipe, @@ -404,22 +404,6 @@ int pl111_display_init(struct drm_device *drm) struct device_node *endpoint; u32 tft_r0b0g0[3]; int ret; - static const u32 formats[] = { - DRM_FORMAT_ABGR8888, - DRM_FORMAT_XBGR8888, - DRM_FORMAT_ARGB8888, - DRM_FORMAT_XRGB8888, - DRM_FORMAT_BGR565, - DRM_FORMAT_RGB565, - DRM_FORMAT_ABGR1555, - DRM_FORMAT_XBGR1555, - DRM_FORMAT_ARGB1555, - DRM_FORMAT_XRGB1555, - DRM_FORMAT_ABGR4444, - DRM_FORMAT_XBGR4444, - DRM_FORMAT_ARGB4444, - DRM_FORMAT_XRGB4444, - }; endpoint = of_graph_get_next_endpoint(dev->of_node, NULL); if (!endpoint) @@ -448,8 +432,10 @@ int pl111_display_init(struct drm_device *drm) ret = drm_simple_display_pipe_init(drm, &priv->pipe, &pl111_display_funcs, - formats, ARRAY_SIZE(formats), - NULL, priv->connector); + priv->variant->formats, + priv->variant->nformats, + NULL, + priv->connector); if (ret) return ret; diff --git a/drivers/gpu/drm/pl111/pl111_drm.h b/drivers/gpu/drm/pl111/pl111_drm.h index 000534d85b43..b380a7b4fb58 100644 --- a/drivers/gpu/drm/pl111/pl111_drm.h +++ b/drivers/gpu/drm/pl111/pl111_drm.h @@ -31,6 +31,20 @@ struct drm_minor; +/** + * struct pl111_variant_data - encodes IP differences + * @name: the name of this variant + * @is_pl110: this is the early PL110 variant + * @formats: array of supported pixel formats on this variant + * @nformats: the length of the array of supported pixel formats + */ +struct pl111_variant_data { + const char *name; + bool is_pl110; + const u32 *formats; + unsigned int nformats; +}; + struct pl111_drm_dev_private { struct drm_device *drm; @@ -41,6 +55,8 @@ struct pl111_drm_dev_private { struct drm_fbdev_cma *fbdev; void *regs; + u32 ienb; + u32 ctrl; /* The pixel clock (a reference to our clock divider off of CLCDCLK). */ struct clk *clk; /* pl111's internal clock divider. */ @@ -49,6 +65,7 @@ struct pl111_drm_dev_private { * subsystem and pl111_display_enable(). */ spinlock_t tim2_lock; + const struct pl111_variant_data *variant; }; int pl111_display_init(struct drm_device *dev); diff --git a/drivers/gpu/drm/pl111/pl111_drv.c b/drivers/gpu/drm/pl111/pl111_drv.c index 7dae687a8df4..7a47db066a78 100644 --- a/drivers/gpu/drm/pl111/pl111_drv.c +++ b/drivers/gpu/drm/pl111/pl111_drv.c @@ -200,6 +200,7 @@ static int pl111_amba_probe(struct amba_device *amba_dev, { struct device *dev = &amba_dev->dev; struct pl111_drm_dev_private *priv; + struct pl111_variant_data *variant = id->data; struct drm_device *drm; int ret; @@ -213,6 +214,33 @@ static int pl111_amba_probe(struct amba_device *amba_dev, amba_set_drvdata(amba_dev, drm); priv->drm = drm; drm->dev_private = priv; + priv->variant = variant; + + /* + * The PL110 and PL111 variants have two registers + * swapped: interrupt enable and control. For this reason + * we use offsets that we can change per variant. + */ + if (variant->is_pl110) { + /* + * The ARM Versatile boards are even more special: + * their PrimeCell ID say they are PL110 but the + * control and interrupt enable registers are anyway + * swapped to the PL111 order so they are not following + * the PL110 datasheet. + */ + if (of_machine_is_compatible("arm,versatile-ab") || + of_machine_is_compatible("arm,versatile-pb")) { + priv->ienb = CLCD_PL111_IENB; + priv->ctrl = CLCD_PL111_CNTL; + } else { + priv->ienb = CLCD_PL110_IENB; + priv->ctrl = CLCD_PL110_CNTL; + } + } else { + priv->ienb = CLCD_PL111_IENB; + priv->ctrl = CLCD_PL111_CNTL; + } priv->regs = devm_ioremap_resource(dev, &amba_dev->res); if (IS_ERR(priv->regs)) { @@ -221,10 +249,10 @@ static int pl111_amba_probe(struct amba_device *amba_dev, } /* turn off interrupts before requesting the irq */ - writel(0, priv->regs + CLCD_PL111_IENB); + writel(0, priv->regs + priv->ienb); ret = devm_request_irq(dev, amba_dev->irq[0], pl111_irq, 0, - "pl111", priv); + variant->name, priv); if (ret != 0) { dev_err(dev, "%s failed irq %d\n", __func__, ret); return ret; @@ -261,10 +289,62 @@ static int pl111_amba_remove(struct amba_device *amba_dev) return 0; } -static struct amba_id pl111_id_table[] = { +/* + * This variant exist in early versions like the ARM Integrator + * and this version lacks the 565 and 444 pixel formats. + */ +static const u32 pl110_pixel_formats[] = { + DRM_FORMAT_ABGR8888, + DRM_FORMAT_XBGR8888, + DRM_FORMAT_ARGB8888, + DRM_FORMAT_XRGB8888, + DRM_FORMAT_ABGR1555, + DRM_FORMAT_XBGR1555, + DRM_FORMAT_ARGB1555, + DRM_FORMAT_XRGB1555, +}; + +static const struct pl111_variant_data pl110_variant = { + .name = "PL110", + .is_pl110 = true, + .formats = pl110_pixel_formats, + .nformats = ARRAY_SIZE(pl110_pixel_formats), +}; + +/* RealView, Versatile Express etc use this modern variant */ +static const u32 pl111_pixel_formats[] = { + DRM_FORMAT_ABGR8888, + DRM_FORMAT_XBGR8888, + DRM_FORMAT_ARGB8888, + DRM_FORMAT_XRGB8888, + DRM_FORMAT_BGR565, + DRM_FORMAT_RGB565, + DRM_FORMAT_ABGR1555, + DRM_FORMAT_XBGR1555, + DRM_FORMAT_ARGB1555, + DRM_FORMAT_XRGB1555, + DRM_FORMAT_ABGR4444, + DRM_FORMAT_XBGR4444, + DRM_FORMAT_ARGB4444, + DRM_FORMAT_XRGB4444, +}; + +static const struct pl111_variant_data pl111_variant = { + .name = "PL111", + .formats = pl111_pixel_formats, + .nformats = ARRAY_SIZE(pl111_pixel_formats), +}; + +static const struct amba_id pl111_id_table[] = { + { + .id = 0x00041110, + .mask = 0x000fffff, + .data = (void*)&pl110_variant, + }, { .id = 0x00041111, .mask = 0x000fffff, + .data = (void*)&pl111_variant, }, {0, 0}, }; -- cgit From 52289a07a09d131a0981966aa8f2ab2a252a3c53 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 8 Sep 2017 14:47:07 +0200 Subject: drm/pl111: Insert delay before powering up PL11x The old codebase has a delay between enabling and powering up the PL11x. According to the manual for PL110, ARM DDI 0161E page 1-5 and the PL111 manual ARM DDI 0293C page 1-6, the power sequence should be such that once Vdd is stable (which we assume it is at boot) LCDEN is enabled first and then CLPOWER should be enabled "after the signals have stabilized" and this is said to be display-dependent. The old codebase uses 20ms. Reviewed-by: Eric Anholt Signed-off-by: Linus Walleij Link: https://patchwork.freedesktop.org/patch/msgid/20170908124709.4758-5-linus.walleij@linaro.org --- drivers/gpu/drm/pl111/pl111_display.c | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) (limited to 'drivers/gpu/drm/pl111') diff --git a/drivers/gpu/drm/pl111/pl111_display.c b/drivers/gpu/drm/pl111/pl111_display.c index c08706be4b7e..3091fb4559cf 100644 --- a/drivers/gpu/drm/pl111/pl111_display.c +++ b/drivers/gpu/drm/pl111/pl111_display.c @@ -155,8 +155,8 @@ static void pl111_display_enable(struct drm_simple_display_pipe *pipe, writel(0, priv->regs + CLCD_TIM3); - /* Enable and Power Up */ - cntl = CNTL_LCDEN | CNTL_LCDTFT | CNTL_LCDPWR | CNTL_LCDVCOMP(1); + /* Hard-code TFT panel */ + cntl = CNTL_LCDEN | CNTL_LCDTFT | CNTL_LCDVCOMP(1); /* Note that the the hardware's format reader takes 'r' from * the low bit, while DRM formats list channels from high bit @@ -199,6 +199,17 @@ static void pl111_display_enable(struct drm_simple_display_pipe *pipe, break; } + /* Power sequence: first enable and chill */ + writel(cntl, priv->regs + priv->ctrl); + + /* + * We expect this delay to stabilize the contrast + * voltage Vee as stipulated by the manual + */ + msleep(20); + + /* Power Up */ + cntl |= CNTL_LCDPWR; writel(cntl, priv->regs + priv->ctrl); drm_crtc_vblank_on(crtc); @@ -209,10 +220,24 @@ void pl111_display_disable(struct drm_simple_display_pipe *pipe) struct drm_crtc *crtc = &pipe->crtc; struct drm_device *drm = crtc->dev; struct pl111_drm_dev_private *priv = drm->dev_private; + u32 cntl; drm_crtc_vblank_off(crtc); - /* Disable and Power Down */ + /* Power Down */ + cntl = readl(priv->regs + priv->ctrl); + if (cntl & CNTL_LCDPWR) { + cntl &= ~CNTL_LCDPWR; + writel(cntl, priv->regs + priv->ctrl); + } + + /* + * We expect this delay to stabilize the contrast voltage Vee as + * stipulated by the manual + */ + msleep(20); + + /* Disable */ writel(0, priv->regs + priv->ctrl); clk_disable_unprepare(priv->clk); -- cgit From ad86ebe71cba5fa06e1a96e41769c793b498fa75 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 8 Sep 2017 14:47:08 +0200 Subject: drm/pl111: Add optional variant display en/disable callbacks The silcon and components around the PL111 may require some variants to perform special set-up of the display. Add two callbacks to manage this. Reviewed-by: Eric Anholt Signed-off-by: Linus Walleij Link: https://patchwork.freedesktop.org/patch/msgid/20170908124709.4758-6-linus.walleij@linaro.org --- drivers/gpu/drm/pl111/pl111_display.c | 6 ++++++ drivers/gpu/drm/pl111/pl111_drm.h | 3 +++ 2 files changed, 9 insertions(+) (limited to 'drivers/gpu/drm/pl111') diff --git a/drivers/gpu/drm/pl111/pl111_display.c b/drivers/gpu/drm/pl111/pl111_display.c index 3091fb4559cf..29148ff380d8 100644 --- a/drivers/gpu/drm/pl111/pl111_display.c +++ b/drivers/gpu/drm/pl111/pl111_display.c @@ -208,6 +208,9 @@ static void pl111_display_enable(struct drm_simple_display_pipe *pipe, */ msleep(20); + if (priv->variant_display_enable) + priv->variant_display_enable(drm, fb->format->format); + /* Power Up */ cntl |= CNTL_LCDPWR; writel(cntl, priv->regs + priv->ctrl); @@ -237,6 +240,9 @@ void pl111_display_disable(struct drm_simple_display_pipe *pipe) */ msleep(20); + if (priv->variant_display_disable) + priv->variant_display_disable(drm); + /* Disable */ writel(0, priv->regs + priv->ctrl); diff --git a/drivers/gpu/drm/pl111/pl111_drm.h b/drivers/gpu/drm/pl111/pl111_drm.h index b380a7b4fb58..440f53ebee8c 100644 --- a/drivers/gpu/drm/pl111/pl111_drm.h +++ b/drivers/gpu/drm/pl111/pl111_drm.h @@ -26,6 +26,7 @@ #include #include #include +#include #define CLCD_IRQ_NEXTBASE_UPDATE BIT(2) @@ -66,6 +67,8 @@ struct pl111_drm_dev_private { */ spinlock_t tim2_lock; const struct pl111_variant_data *variant; + void (*variant_display_enable) (struct drm_device *drm, u32 format); + void (*variant_display_disable) (struct drm_device *drm); }; int pl111_display_init(struct drm_device *dev); -- cgit From 72fe915ac5655a84210adbe358425e40b9ada5cb Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 8 Sep 2017 14:47:09 +0200 Subject: drm/pl111: Add handling of Versatile platforms The ARM reference designs in the Versatile family: Integrator, Versatile and RealView can make use of the new DRM driver as well. We just need to create a bit of platform-specific code for them that we isolate to its own file. Reviewed-by: Eric Anholt Signed-off-by: Linus Walleij Link: https://patchwork.freedesktop.org/patch/msgid/20170908124709.4758-7-linus.walleij@linaro.org --- drivers/gpu/drm/pl111/Makefile | 1 + drivers/gpu/drm/pl111/pl111_display.c | 7 - drivers/gpu/drm/pl111/pl111_drv.c | 8 +- drivers/gpu/drm/pl111/pl111_versatile.c | 270 ++++++++++++++++++++++++++++++++ drivers/gpu/drm/pl111/pl111_versatile.h | 9 ++ 5 files changed, 285 insertions(+), 10 deletions(-) create mode 100644 drivers/gpu/drm/pl111/pl111_versatile.c create mode 100644 drivers/gpu/drm/pl111/pl111_versatile.h (limited to 'drivers/gpu/drm/pl111') diff --git a/drivers/gpu/drm/pl111/Makefile b/drivers/gpu/drm/pl111/Makefile index c5f8f9684848..fce1453a93e1 100644 --- a/drivers/gpu/drm/pl111/Makefile +++ b/drivers/gpu/drm/pl111/Makefile @@ -1,4 +1,5 @@ pl111_drm-y += pl111_display.o \ + pl111_versatile.o \ pl111_drv.o pl111_drm-$(CONFIG_DEBUG_FS) += pl111_debugfs.o diff --git a/drivers/gpu/drm/pl111/pl111_display.c b/drivers/gpu/drm/pl111/pl111_display.c index 29148ff380d8..06c4bf756b69 100644 --- a/drivers/gpu/drm/pl111/pl111_display.c +++ b/drivers/gpu/drm/pl111/pl111_display.c @@ -450,13 +450,6 @@ int pl111_display_init(struct drm_device *drm) } of_node_put(endpoint); - if (tft_r0b0g0[0] != 0 || - tft_r0b0g0[1] != 8 || - tft_r0b0g0[2] != 16) { - dev_err(dev, "arm,pl11x,tft-r0g0b0-pads != [0,8,16] not yet supported\n"); - return -EINVAL; - } - ret = pl111_init_clock_divider(drm); if (ret) return ret; diff --git a/drivers/gpu/drm/pl111/pl111_drv.c b/drivers/gpu/drm/pl111/pl111_drv.c index 7a47db066a78..201d57d5cb54 100644 --- a/drivers/gpu/drm/pl111/pl111_drv.c +++ b/drivers/gpu/drm/pl111/pl111_drv.c @@ -41,9 +41,6 @@ * - Fix race between setting plane base address and getting IRQ for * vsync firing the pageflip completion. * - * - Expose the correct set of formats we can support based on the - * "arm,pl11x,tft-r0g0b0-pads" DT property. - * * - Use the "max-memory-bandwidth" DT property to filter the * supported formats. * @@ -73,6 +70,7 @@ #include #include "pl111_drm.h" +#include "pl111_versatile.h" #define DRIVER_DESC "DRM module for PL111" @@ -258,6 +256,10 @@ static int pl111_amba_probe(struct amba_device *amba_dev, return ret; } + ret = pl111_versatile_init(dev, priv); + if (ret) + goto dev_unref; + ret = pl111_modeset_init(drm); if (ret != 0) goto dev_unref; diff --git a/drivers/gpu/drm/pl111/pl111_versatile.c b/drivers/gpu/drm/pl111/pl111_versatile.c new file mode 100644 index 000000000000..97d4af6925a3 --- /dev/null +++ b/drivers/gpu/drm/pl111/pl111_versatile.c @@ -0,0 +1,270 @@ +#include +#include +#include +#include +#include +#include +#include +#include "pl111_versatile.h" +#include "pl111_drm.h" + +static struct regmap *versatile_syscon_map; + +/* + * We detect the different syscon types from the compatible strings. + */ +enum versatile_clcd { + INTEGRATOR_CLCD_CM, + VERSATILE_CLCD, + REALVIEW_CLCD_EB, + REALVIEW_CLCD_PB1176, + REALVIEW_CLCD_PB11MP, + REALVIEW_CLCD_PBA8, + REALVIEW_CLCD_PBX, +}; + +static const struct of_device_id versatile_clcd_of_match[] = { + { + .compatible = "arm,core-module-integrator", + .data = (void *)INTEGRATOR_CLCD_CM, + }, + { + .compatible = "arm,versatile-sysreg", + .data = (void *)VERSATILE_CLCD, + }, + { + .compatible = "arm,realview-eb-syscon", + .data = (void *)REALVIEW_CLCD_EB, + }, + { + .compatible = "arm,realview-pb1176-syscon", + .data = (void *)REALVIEW_CLCD_PB1176, + }, + { + .compatible = "arm,realview-pb11mp-syscon", + .data = (void *)REALVIEW_CLCD_PB11MP, + }, + { + .compatible = "arm,realview-pba8-syscon", + .data = (void *)REALVIEW_CLCD_PBA8, + }, + { + .compatible = "arm,realview-pbx-syscon", + .data = (void *)REALVIEW_CLCD_PBX, + }, + {}, +}; + +/* + * Core module CLCD control on the Integrator/CP, bits + * 8 thru 19 of the CM_CONTROL register controls a bunch + * of CLCD settings. + */ +#define INTEGRATOR_HDR_CTRL_OFFSET 0x0C +#define INTEGRATOR_CLCD_LCDBIASEN BIT(8) +#define INTEGRATOR_CLCD_LCDBIASUP BIT(9) +#define INTEGRATOR_CLCD_LCDBIASDN BIT(10) +/* Bits 11,12,13 controls the LCD type */ +#define INTEGRATOR_CLCD_LCDMUX_MASK (BIT(11)|BIT(12)|BIT(13)) +#define INTEGRATOR_CLCD_LCDMUX_LCD24 BIT(11) +#define INTEGRATOR_CLCD_LCDMUX_VGA565 BIT(12) +#define INTEGRATOR_CLCD_LCDMUX_SHARP (BIT(11)|BIT(12)) +#define INTEGRATOR_CLCD_LCDMUX_VGA555 BIT(13) +#define INTEGRATOR_CLCD_LCDMUX_VGA24 (BIT(11)|BIT(12)|BIT(13)) +#define INTEGRATOR_CLCD_LCD0_EN BIT(14) +#define INTEGRATOR_CLCD_LCD1_EN BIT(15) +/* R/L flip on Sharp */ +#define INTEGRATOR_CLCD_LCD_STATIC1 BIT(16) +/* U/D flip on Sharp */ +#define INTEGRATOR_CLCD_LCD_STATIC2 BIT(17) +/* No connection on Sharp */ +#define INTEGRATOR_CLCD_LCD_STATIC BIT(18) +/* 0 = 24bit VGA, 1 = 18bit VGA */ +#define INTEGRATOR_CLCD_LCD_N24BITEN BIT(19) + +#define INTEGRATOR_CLCD_MASK (INTEGRATOR_CLCD_LCDBIASEN | \ + INTEGRATOR_CLCD_LCDBIASUP | \ + INTEGRATOR_CLCD_LCDBIASDN | \ + INTEGRATOR_CLCD_LCDMUX_MASK | \ + INTEGRATOR_CLCD_LCD0_EN | \ + INTEGRATOR_CLCD_LCD1_EN | \ + INTEGRATOR_CLCD_LCD_STATIC1 | \ + INTEGRATOR_CLCD_LCD_STATIC2 | \ + INTEGRATOR_CLCD_LCD_STATIC | \ + INTEGRATOR_CLCD_LCD_N24BITEN) + +static void pl111_integrator_enable(struct drm_device *drm, u32 format) +{ + u32 val; + + dev_info(drm->dev, "enable Integrator CLCD connectors\n"); + + /* FIXME: really needed? */ + val = INTEGRATOR_CLCD_LCD_STATIC1 | INTEGRATOR_CLCD_LCD_STATIC2 | + INTEGRATOR_CLCD_LCD0_EN | INTEGRATOR_CLCD_LCD1_EN; + + switch (format) { + case DRM_FORMAT_XBGR8888: + case DRM_FORMAT_XRGB8888: + break; + case DRM_FORMAT_BGR565: + case DRM_FORMAT_RGB565: + /* truecolor RGB565 */ + val |= INTEGRATOR_CLCD_LCDMUX_VGA565; + break; + case DRM_FORMAT_XBGR1555: + case DRM_FORMAT_XRGB1555: + /* Pseudocolor, RGB555, BGR555 */ + val |= INTEGRATOR_CLCD_LCDMUX_VGA555; + break; + default: + dev_err(drm->dev, "unhandled format on Integrator 0x%08x\n", + format); + break; + } + + regmap_update_bits(versatile_syscon_map, + INTEGRATOR_HDR_CTRL_OFFSET, + INTEGRATOR_CLCD_MASK, + val); +} + +/* + * This configuration register in the Versatile and RealView + * family is uniformly present but appears more and more + * unutilized starting with the RealView series. + */ +#define SYS_CLCD 0x50 +#define SYS_CLCD_MODE_MASK (BIT(0)|BIT(1)) +#define SYS_CLCD_MODE_888 0 +#define SYS_CLCD_MODE_5551 BIT(0) +#define SYS_CLCD_MODE_565_R_LSB BIT(1) +#define SYS_CLCD_MODE_565_B_LSB (BIT(0)|BIT(1)) +#define SYS_CLCD_CONNECTOR_MASK (BIT(2)|BIT(3)|BIT(4)|BIT(5)) +#define SYS_CLCD_NLCDIOON BIT(2) +#define SYS_CLCD_VDDPOSSWITCH BIT(3) +#define SYS_CLCD_PWR3V5SWITCH BIT(4) +#define SYS_CLCD_VDDNEGSWITCH BIT(5) + +static void pl111_versatile_disable(struct drm_device *drm) +{ + dev_info(drm->dev, "disable Versatile CLCD connectors\n"); + regmap_update_bits(versatile_syscon_map, + SYS_CLCD, + SYS_CLCD_CONNECTOR_MASK, + 0); +} + +static void pl111_versatile_enable(struct drm_device *drm, u32 format) +{ + u32 val = 0; + + dev_info(drm->dev, "enable Versatile CLCD connectors\n"); + + switch (format) { + case DRM_FORMAT_ABGR8888: + case DRM_FORMAT_XBGR8888: + case DRM_FORMAT_ARGB8888: + case DRM_FORMAT_XRGB8888: + val |= SYS_CLCD_MODE_888; + break; + case DRM_FORMAT_BGR565: + val |= SYS_CLCD_MODE_565_R_LSB; + break; + case DRM_FORMAT_RGB565: + val |= SYS_CLCD_MODE_565_B_LSB; + break; + case DRM_FORMAT_ABGR1555: + case DRM_FORMAT_XBGR1555: + case DRM_FORMAT_ARGB1555: + case DRM_FORMAT_XRGB1555: + val |= SYS_CLCD_MODE_5551; + break; + default: + dev_err(drm->dev, "unhandled format on Versatile 0x%08x\n", + format); + break; + } + + /* Set up the MUX */ + regmap_update_bits(versatile_syscon_map, + SYS_CLCD, + SYS_CLCD_MODE_MASK, + val); + + /* Then enable the display */ + regmap_update_bits(versatile_syscon_map, + SYS_CLCD, + SYS_CLCD_CONNECTOR_MASK, + SYS_CLCD_NLCDIOON | SYS_CLCD_PWR3V5SWITCH); +} + +static void pl111_realview_clcd_disable(struct drm_device *drm) +{ + dev_info(drm->dev, "disable RealView CLCD connectors\n"); + regmap_update_bits(versatile_syscon_map, + SYS_CLCD, + SYS_CLCD_CONNECTOR_MASK, + 0); +} + +static void pl111_realview_clcd_enable(struct drm_device *drm, u32 format) +{ + dev_info(drm->dev, "enable RealView CLCD connectors\n"); + regmap_update_bits(versatile_syscon_map, + SYS_CLCD, + SYS_CLCD_CONNECTOR_MASK, + SYS_CLCD_NLCDIOON | SYS_CLCD_PWR3V5SWITCH); +} + +int pl111_versatile_init(struct device *dev, struct pl111_drm_dev_private *priv) +{ + const struct of_device_id *clcd_id; + enum versatile_clcd versatile_clcd_type; + struct device_node *np; + struct regmap *map; + + np = of_find_matching_node_and_match(NULL, versatile_clcd_of_match, + &clcd_id); + if (!np) { + /* Non-ARM reference designs, just bail out */ + return 0; + } + versatile_clcd_type = (enum versatile_clcd)clcd_id->data; + + map = syscon_node_to_regmap(np); + if (IS_ERR(map)) { + dev_err(dev, "no Versatile syscon regmap\n"); + return PTR_ERR(map); + } + + switch (versatile_clcd_type) { + case INTEGRATOR_CLCD_CM: + versatile_syscon_map = map; + priv->variant_display_enable = pl111_integrator_enable; + dev_info(dev, "set up callbacks for Integrator PL110\n"); + break; + case VERSATILE_CLCD: + versatile_syscon_map = map; + priv->variant_display_enable = pl111_versatile_enable; + priv->variant_display_disable = pl111_versatile_disable; + dev_info(dev, "set up callbacks for Versatile PL110+\n"); + break; + case REALVIEW_CLCD_EB: + case REALVIEW_CLCD_PB1176: + case REALVIEW_CLCD_PB11MP: + case REALVIEW_CLCD_PBA8: + case REALVIEW_CLCD_PBX: + versatile_syscon_map = map; + priv->variant_display_enable = pl111_realview_clcd_enable; + priv->variant_display_disable = pl111_realview_clcd_disable; + dev_info(dev, "set up callbacks for RealView PL111\n"); + break; + default: + dev_info(dev, "unknown Versatile system controller\n"); + break; + } + + return 0; +} +EXPORT_SYMBOL_GPL(pl111_versatile_init); diff --git a/drivers/gpu/drm/pl111/pl111_versatile.h b/drivers/gpu/drm/pl111/pl111_versatile.h new file mode 100644 index 000000000000..41aa6d969dc6 --- /dev/null +++ b/drivers/gpu/drm/pl111/pl111_versatile.h @@ -0,0 +1,9 @@ +#include +#include "pl111_drm.h" + +#ifndef PL111_VERSATILE_H +#define PL111_VERSATILE_H + +int pl111_versatile_init(struct device *dev, struct pl111_drm_dev_private *priv); + +#endif -- cgit