aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu
diff options
context:
space:
mode:
authorEric Anholt <eric@anholt.net>2017-06-02 16:25:14 -0400
committerArchit Taneja <architt@codeaurora.org>2017-06-05 04:30:40 -0400
commit13dfc0540a575b47b2d640b093ac16e9e09474f6 (patch)
treecafdf5725be58809db3bcbacdbb6639f8ab03125 /drivers/gpu
parentb0febde779fd5d2e3e6f83843a828726117fe0a7 (diff)
drm/bridge: Refactor out the panel wrapper from the lvds-encoder bridge.
Many DRM drivers have common code to make a stub connector implementation that wraps a drm_panel. By wrapping the panel in a DRM bridge, all of the connector code (including calls during encoder enable/disable) goes away. v2: Fix build with CONFIG_DRM=m, drop "dev" argument that should just be the panel's dev, move kerneldoc up a level and document _remove(). v3: Fix another breakage with CONFIG_DRM=m, fix breakage with CONFIG_OF=n, move protos under CONFIG_DRM_PANEL_BRIDGE, wrap a line. Signed-off-by: Eric Anholt <eric@anholt.net> Acked-by: Daniel Vetter <daniel.vetter@ffwll.ch> (v1) Reviewed-by: Boris Brezillon <boris.brezillon@free-electrons.com> (v2) Acked-by: Archit Taneja <architt@codeaurora.org> (v2) Signed-off-by: Archit Taneja <architt@codeaurora.org> Link: http://patchwork.freedesktop.org/patch/msgid/20170602202514.11900-1-eric@anholt.net
Diffstat (limited to 'drivers/gpu')
-rw-r--r--drivers/gpu/drm/Makefile1
-rw-r--r--drivers/gpu/drm/bridge/Kconfig11
-rw-r--r--drivers/gpu/drm/bridge/lvds-encoder.c157
-rw-r--r--drivers/gpu/drm/bridge/panel.c200
4 files changed, 228 insertions, 141 deletions
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index acc88942c2e5..dc69175255b1 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -24,6 +24,7 @@ drm-$(CONFIG_COMPAT) += drm_ioc32.o
24drm-$(CONFIG_DRM_GEM_CMA_HELPER) += drm_gem_cma_helper.o 24drm-$(CONFIG_DRM_GEM_CMA_HELPER) += drm_gem_cma_helper.o
25drm-$(CONFIG_PCI) += ati_pcigart.o 25drm-$(CONFIG_PCI) += ati_pcigart.o
26drm-$(CONFIG_DRM_PANEL) += drm_panel.o 26drm-$(CONFIG_DRM_PANEL) += drm_panel.o
27drm-$(CONFIG_DRM_PANEL_BRIDGE) += bridge/panel.o
27drm-$(CONFIG_OF) += drm_of.o 28drm-$(CONFIG_OF) += drm_of.o
28drm-$(CONFIG_AGP) += drm_agpsupport.o 29drm-$(CONFIG_AGP) += drm_agpsupport.o
29drm-$(CONFIG_DEBUG_FS) += drm_debugfs.o drm_debugfs_crc.o 30drm-$(CONFIG_DEBUG_FS) += drm_debugfs.o drm_debugfs_crc.o
diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
index f6968d3b4b41..adf9ae0e0b7c 100644
--- a/drivers/gpu/drm/bridge/Kconfig
+++ b/drivers/gpu/drm/bridge/Kconfig
@@ -4,6 +4,14 @@ config DRM_BRIDGE
4 help 4 help
5 Bridge registration and lookup framework. 5 Bridge registration and lookup framework.
6 6
7config DRM_PANEL_BRIDGE
8 def_bool y
9 depends on DRM_BRIDGE
10 depends on DRM_KMS_HELPER
11 select DRM_PANEL
12 help
13 DRM bridge wrapper of DRM panels
14
7menu "Display Interface Bridges" 15menu "Display Interface Bridges"
8 depends on DRM && DRM_BRIDGE 16 depends on DRM && DRM_BRIDGE
9 17
@@ -27,8 +35,7 @@ config DRM_DUMB_VGA_DAC
27config DRM_LVDS_ENCODER 35config DRM_LVDS_ENCODER
28 tristate "Transparent parallel to LVDS encoder support" 36 tristate "Transparent parallel to LVDS encoder support"
29 depends on OF 37 depends on OF
30 select DRM_KMS_HELPER 38 select DRM_PANEL_BRIDGE
31 select DRM_PANEL
32 help 39 help
33 Support for transparent parallel to LVDS encoders that don't require 40 Support for transparent parallel to LVDS encoders that don't require
34 any configuration. 41 any configuration.
diff --git a/drivers/gpu/drm/bridge/lvds-encoder.c b/drivers/gpu/drm/bridge/lvds-encoder.c
index f1f67a279426..0903ba574f61 100644
--- a/drivers/gpu/drm/bridge/lvds-encoder.c
+++ b/drivers/gpu/drm/bridge/lvds-encoder.c
@@ -8,144 +8,18 @@
8 */ 8 */
9 9
10#include <drm/drmP.h> 10#include <drm/drmP.h>
11#include <drm/drm_atomic_helper.h> 11#include <drm/drm_bridge.h>
12#include <drm/drm_connector.h>
13#include <drm/drm_crtc_helper.h>
14#include <drm/drm_encoder.h>
15#include <drm/drm_modeset_helper_vtables.h>
16#include <drm/drm_panel.h> 12#include <drm/drm_panel.h>
17 13
18#include <linux/of_graph.h> 14#include <linux/of_graph.h>
19 15
20struct lvds_encoder {
21 struct device *dev;
22
23 struct drm_bridge bridge;
24 struct drm_connector connector;
25 struct drm_panel *panel;
26};
27
28static inline struct lvds_encoder *
29drm_bridge_to_lvds_encoder(struct drm_bridge *bridge)
30{
31 return container_of(bridge, struct lvds_encoder, bridge);
32}
33
34static inline struct lvds_encoder *
35drm_connector_to_lvds_encoder(struct drm_connector *connector)
36{
37 return container_of(connector, struct lvds_encoder, connector);
38}
39
40static int lvds_connector_get_modes(struct drm_connector *connector)
41{
42 struct lvds_encoder *lvds = drm_connector_to_lvds_encoder(connector);
43
44 return drm_panel_get_modes(lvds->panel);
45}
46
47static const struct drm_connector_helper_funcs lvds_connector_helper_funcs = {
48 .get_modes = lvds_connector_get_modes,
49};
50
51static const struct drm_connector_funcs lvds_connector_funcs = {
52 .dpms = drm_atomic_helper_connector_dpms,
53 .reset = drm_atomic_helper_connector_reset,
54 .fill_modes = drm_helper_probe_single_connector_modes,
55 .destroy = drm_connector_cleanup,
56 .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
57 .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
58};
59
60static int lvds_encoder_attach(struct drm_bridge *bridge)
61{
62 struct lvds_encoder *lvds = drm_bridge_to_lvds_encoder(bridge);
63 struct drm_connector *connector = &lvds->connector;
64 int ret;
65
66 if (!bridge->encoder) {
67 DRM_ERROR("Missing encoder\n");
68 return -ENODEV;
69 }
70
71 drm_connector_helper_add(connector, &lvds_connector_helper_funcs);
72
73 ret = drm_connector_init(bridge->dev, connector, &lvds_connector_funcs,
74 DRM_MODE_CONNECTOR_LVDS);
75 if (ret) {
76 DRM_ERROR("Failed to initialize connector\n");
77 return ret;
78 }
79
80 drm_mode_connector_attach_encoder(&lvds->connector, bridge->encoder);
81
82 ret = drm_panel_attach(lvds->panel, &lvds->connector);
83 if (ret < 0)
84 return ret;
85
86 return 0;
87}
88
89static void lvds_encoder_detach(struct drm_bridge *bridge)
90{
91 struct lvds_encoder *lvds = drm_bridge_to_lvds_encoder(bridge);
92
93 drm_panel_detach(lvds->panel);
94}
95
96static void lvds_encoder_pre_enable(struct drm_bridge *bridge)
97{
98 struct lvds_encoder *lvds = drm_bridge_to_lvds_encoder(bridge);
99
100 drm_panel_prepare(lvds->panel);
101}
102
103static void lvds_encoder_enable(struct drm_bridge *bridge)
104{
105 struct lvds_encoder *lvds = drm_bridge_to_lvds_encoder(bridge);
106
107 drm_panel_enable(lvds->panel);
108}
109
110static void lvds_encoder_disable(struct drm_bridge *bridge)
111{
112 struct lvds_encoder *lvds = drm_bridge_to_lvds_encoder(bridge);
113
114 drm_panel_disable(lvds->panel);
115}
116
117static void lvds_encoder_post_disable(struct drm_bridge *bridge)
118{
119 struct lvds_encoder *lvds = drm_bridge_to_lvds_encoder(bridge);
120
121 drm_panel_unprepare(lvds->panel);
122}
123
124static const struct drm_bridge_funcs lvds_encoder_bridge_funcs = {
125 .attach = lvds_encoder_attach,
126 .detach = lvds_encoder_detach,
127 .pre_enable = lvds_encoder_pre_enable,
128 .enable = lvds_encoder_enable,
129 .disable = lvds_encoder_disable,
130 .post_disable = lvds_encoder_post_disable,
131};
132
133static int lvds_encoder_probe(struct platform_device *pdev) 16static int lvds_encoder_probe(struct platform_device *pdev)
134{ 17{
135 struct lvds_encoder *lvds;
136 struct device_node *port; 18 struct device_node *port;
137 struct device_node *endpoint; 19 struct device_node *endpoint;
138 struct device_node *panel; 20 struct device_node *panel_node;
139 21 struct drm_panel *panel;
140 lvds = devm_kzalloc(&pdev->dev, sizeof(*lvds), GFP_KERNEL); 22 struct drm_bridge *bridge;
141 if (!lvds)
142 return -ENOMEM;
143
144 lvds->dev = &pdev->dev;
145 platform_set_drvdata(pdev, lvds);
146
147 lvds->bridge.funcs = &lvds_encoder_bridge_funcs;
148 lvds->bridge.of_node = pdev->dev.of_node;
149 23
150 /* Locate the panel DT node. */ 24 /* Locate the panel DT node. */
151 port = of_graph_get_port_by_id(pdev->dev.of_node, 1); 25 port = of_graph_get_port_by_id(pdev->dev.of_node, 1);
@@ -161,29 +35,34 @@ static int lvds_encoder_probe(struct platform_device *pdev)
161 return -ENXIO; 35 return -ENXIO;
162 } 36 }
163 37
164 panel = of_graph_get_remote_port_parent(endpoint); 38 panel_node = of_graph_get_remote_port_parent(endpoint);
165 of_node_put(endpoint); 39 of_node_put(endpoint);
166 if (!panel) { 40 if (!panel_node) {
167 dev_dbg(&pdev->dev, "no remote endpoint for port 1\n"); 41 dev_dbg(&pdev->dev, "no remote endpoint for port 1\n");
168 return -ENXIO; 42 return -ENXIO;
169 } 43 }
170 44
171 lvds->panel = of_drm_find_panel(panel); 45 panel = of_drm_find_panel(panel_node);
172 of_node_put(panel); 46 of_node_put(panel_node);
173 if (!lvds->panel) { 47 if (!panel) {
174 dev_dbg(&pdev->dev, "panel not found, deferring probe\n"); 48 dev_dbg(&pdev->dev, "panel not found, deferring probe\n");
175 return -EPROBE_DEFER; 49 return -EPROBE_DEFER;
176 } 50 }
177 51
178 /* Register the bridge. */ 52 bridge = drm_panel_bridge_add(panel, DRM_MODE_CONNECTOR_LVDS);
179 return drm_bridge_add(&lvds->bridge); 53 if (IS_ERR(bridge))
54 return PTR_ERR(bridge);
55
56 platform_set_drvdata(pdev, bridge);
57
58 return 0;
180} 59}
181 60
182static int lvds_encoder_remove(struct platform_device *pdev) 61static int lvds_encoder_remove(struct platform_device *pdev)
183{ 62{
184 struct lvds_encoder *encoder = platform_get_drvdata(pdev); 63 struct drm_bridge *bridge = platform_get_drvdata(pdev);
185 64
186 drm_bridge_remove(&encoder->bridge); 65 drm_bridge_remove(bridge);
187 66
188 return 0; 67 return 0;
189} 68}
diff --git a/drivers/gpu/drm/bridge/panel.c b/drivers/gpu/drm/bridge/panel.c
new file mode 100644
index 000000000000..99f9a4beb859
--- /dev/null
+++ b/drivers/gpu/drm/bridge/panel.c
@@ -0,0 +1,200 @@
1/*
2 * Copyright (C) 2016 Laurent Pinchart <laurent.pinchart@ideasonboard.com>
3 * Copyright (C) 2017 Broadcom
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation; either version 2 of
8 * the License, or (at your option) any later version.
9 */
10
11#include <drm/drmP.h>
12#include <drm/drm_panel.h>
13#include <drm/drm_atomic_helper.h>
14#include <drm/drm_connector.h>
15#include <drm/drm_crtc_helper.h>
16#include <drm/drm_encoder.h>
17#include <drm/drm_modeset_helper_vtables.h>
18#include <drm/drm_panel.h>
19
20struct panel_bridge {
21 struct drm_bridge bridge;
22 struct drm_connector connector;
23 struct drm_panel *panel;
24 u32 connector_type;
25};
26
27static inline struct panel_bridge *
28drm_bridge_to_panel_bridge(struct drm_bridge *bridge)
29{
30 return container_of(bridge, struct panel_bridge, bridge);
31}
32
33static inline struct panel_bridge *
34drm_connector_to_panel_bridge(struct drm_connector *connector)
35{
36 return container_of(connector, struct panel_bridge, connector);
37}
38
39static int panel_bridge_connector_get_modes(struct drm_connector *connector)
40{
41 struct panel_bridge *panel_bridge =
42 drm_connector_to_panel_bridge(connector);
43
44 return drm_panel_get_modes(panel_bridge->panel);
45}
46
47static const struct drm_connector_helper_funcs
48panel_bridge_connector_helper_funcs = {
49 .get_modes = panel_bridge_connector_get_modes,
50};
51
52static const struct drm_connector_funcs panel_bridge_connector_funcs = {
53 .dpms = drm_atomic_helper_connector_dpms,
54 .reset = drm_atomic_helper_connector_reset,
55 .fill_modes = drm_helper_probe_single_connector_modes,
56 .destroy = drm_connector_cleanup,
57 .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
58 .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
59};
60
61static int panel_bridge_attach(struct drm_bridge *bridge)
62{
63 struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
64 struct drm_connector *connector = &panel_bridge->connector;
65 int ret;
66
67 if (!bridge->encoder) {
68 DRM_ERROR("Missing encoder\n");
69 return -ENODEV;
70 }
71
72 drm_connector_helper_add(connector,
73 &panel_bridge_connector_helper_funcs);
74
75 ret = drm_connector_init(bridge->dev, connector,
76 &panel_bridge_connector_funcs,
77 panel_bridge->connector_type);
78 if (ret) {
79 DRM_ERROR("Failed to initialize connector\n");
80 return ret;
81 }
82
83 drm_mode_connector_attach_encoder(&panel_bridge->connector,
84 bridge->encoder);
85
86 ret = drm_panel_attach(panel_bridge->panel, &panel_bridge->connector);
87 if (ret < 0)
88 return ret;
89
90 return 0;
91}
92
93static void panel_bridge_detach(struct drm_bridge *bridge)
94{
95 struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
96
97 drm_panel_detach(panel_bridge->panel);
98}
99
100static void panel_bridge_pre_enable(struct drm_bridge *bridge)
101{
102 struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
103
104 drm_panel_prepare(panel_bridge->panel);
105}
106
107static void panel_bridge_enable(struct drm_bridge *bridge)
108{
109 struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
110
111 drm_panel_enable(panel_bridge->panel);
112}
113
114static void panel_bridge_disable(struct drm_bridge *bridge)
115{
116 struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
117
118 drm_panel_disable(panel_bridge->panel);
119}
120
121static void panel_bridge_post_disable(struct drm_bridge *bridge)
122{
123 struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
124
125 drm_panel_unprepare(panel_bridge->panel);
126}
127
128static const struct drm_bridge_funcs panel_bridge_bridge_funcs = {
129 .attach = panel_bridge_attach,
130 .detach = panel_bridge_detach,
131 .pre_enable = panel_bridge_pre_enable,
132 .enable = panel_bridge_enable,
133 .disable = panel_bridge_disable,
134 .post_disable = panel_bridge_post_disable,
135};
136
137/**
138 * drm_panel_bridge_add - Creates a drm_bridge and drm_connector that
139 * just calls the appropriate functions from drm_panel.
140 *
141 * @panel: The drm_panel being wrapped. Must be non-NULL.
142 * @connector_type: The DRM_MODE_CONNECTOR_* for the connector to be
143 * created.
144 *
145 * For drivers converting from directly using drm_panel: The expected
146 * usage pattern is that during either encoder module probe or DSI
147 * host attach, a drm_panel will be looked up through
148 * drm_of_find_panel_or_bridge(). drm_panel_bridge_add() is used to
149 * wrap that panel in the new bridge, and the result can then be
150 * passed to drm_bridge_attach(). The drm_panel_prepare() and related
151 * functions can be dropped from the encoder driver (they're now
152 * called by the KMS helpers before calling into the encoder), along
153 * with connector creation. When done with the bridge,
154 * drm_bridge_detach() should be called as normal, then
155 * drm_panel_bridge_remove() to free it.
156 */
157struct drm_bridge *drm_panel_bridge_add(struct drm_panel *panel,
158 u32 connector_type)
159{
160 struct panel_bridge *panel_bridge;
161 int ret;
162
163 if (!panel)
164 return ERR_PTR(EINVAL);
165
166 panel_bridge = devm_kzalloc(panel->dev, sizeof(*panel_bridge),
167 GFP_KERNEL);
168 if (!panel_bridge)
169 return ERR_PTR(-ENOMEM);
170
171 panel_bridge->connector_type = connector_type;
172 panel_bridge->panel = panel;
173
174 panel_bridge->bridge.funcs = &panel_bridge_bridge_funcs;
175#ifdef CONFIG_OF
176 panel_bridge->bridge.of_node = panel->dev->of_node;
177#endif
178
179 ret = drm_bridge_add(&panel_bridge->bridge);
180 if (ret)
181 return ERR_PTR(ret);
182
183 return &panel_bridge->bridge;
184}
185EXPORT_SYMBOL(drm_panel_bridge_add);
186
187/**
188 * drm_panel_bridge_remove - Unregisters and frees a drm_bridge
189 * created by drm_panel_bridge_add().
190 *
191 * @bridge: The drm_bridge being freed.
192 */
193void drm_panel_bridge_remove(struct drm_bridge *bridge)
194{
195 struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
196
197 drm_bridge_remove(bridge);
198 devm_kfree(panel_bridge->panel->dev, bridge);
199}
200EXPORT_SYMBOL(drm_panel_bridge_remove);