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 <eric@anholt.net>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
Link: https://patchwork.freedesktop.org/patch/msgid/20170908124709.4758-3-linus.walleij@linaro.org
This commit is contained in:
Linus Walleij 2017-09-08 14:47:05 +02:00
parent bcaf9ff425
commit 001485d525
6 changed files with 49 additions and 168 deletions

View File

@ -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.

View File

@ -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

View File

@ -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 <airlied@linux.ie>
* 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 <linux/amba/clcd-regs.h>
#include <linux/version.h>
#include <linux/shmem_fs.h>
#include <linux/dma-buf.h>
#include <drm/drmP.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_of.h>
#include <drm/drm_panel.h>
#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;
}

View File

@ -21,7 +21,6 @@
#include <linux/of_graph.h>
#include <drm/drmP.h>
#include <drm/drm_panel.h>
#include <drm/drm_gem_cma_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_fb_cma_helper.h>
@ -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;

View File

@ -21,21 +21,22 @@
#include <drm/drm_gem.h>
#include <drm/drm_simple_kms_helper.h>
#include <drm/drm_connector.h>
#include <drm/drm_encoder.h>
#include <drm/drm_panel.h>
#include <drm/drm_bridge.h>
#include <linux/clk-provider.h>
#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_ */

View File

@ -68,6 +68,9 @@
#include <drm/drm_gem_cma_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_fb_cma_helper.h>
#include <drm/drm_of.h>
#include <drm/drm_bridge.h>
#include <drm/drm_panel.h>
#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);