aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c
diff options
context:
space:
mode:
authorBoris Brezillon <boris.brezillon@free-electrons.com>2016-01-06 04:16:32 -0500
committerBoris Brezillon <boris.brezillon@free-electrons.com>2016-04-14 03:17:27 -0400
commit17a8e03e7e97d3445ec60a16a234149ca51e96fb (patch)
treee67b16dcd56a543ac42b5d6d9fa138e790168885 /drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c
parentaca63b766140f1abf14cbc22ccc5d7c7599b807c (diff)
drm: atmel-hlcdc: rework the output code to support drm bridges
The current output code only supports connection to drm panels. First simplify the drm panel code, and then add support for external drm bridges. Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com> Tested-by: Nicolas Ferre <nicolas.ferre@atmel.com>
Diffstat (limited to 'drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c')
-rw-r--r--drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c193
1 files changed, 112 insertions, 81 deletions
diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c
index 75237b556c58..39802c0539b6 100644
--- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c
+++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c
@@ -34,11 +34,13 @@
34 * @connector: DRM connector 34 * @connector: DRM connector
35 * @encoder: DRM encoder 35 * @encoder: DRM encoder
36 * @dc: pointer to the atmel_hlcdc_dc structure 36 * @dc: pointer to the atmel_hlcdc_dc structure
37 * @panel: panel connected on the RGB output
37 */ 38 */
38struct atmel_hlcdc_rgb_output { 39struct atmel_hlcdc_rgb_output {
39 struct drm_connector connector; 40 struct drm_connector connector;
40 struct drm_encoder encoder; 41 struct drm_encoder encoder;
41 struct atmel_hlcdc_dc *dc; 42 struct atmel_hlcdc_dc *dc;
43 struct drm_panel *panel;
42}; 44};
43 45
44static inline struct atmel_hlcdc_rgb_output * 46static inline struct atmel_hlcdc_rgb_output *
@@ -54,46 +56,31 @@ drm_encoder_to_atmel_hlcdc_rgb_output(struct drm_encoder *encoder)
54 return container_of(encoder, struct atmel_hlcdc_rgb_output, encoder); 56 return container_of(encoder, struct atmel_hlcdc_rgb_output, encoder);
55} 57}
56 58
57/** 59static void atmel_hlcdc_rgb_encoder_enable(struct drm_encoder *encoder)
58 * Atmel HLCDC Panel device structure
59 *
60 * This structure is specialization of the slave device structure to
61 * interface with drm panels.
62 *
63 * @base: base slave device fields
64 * @panel: drm panel attached to this slave device
65 */
66struct atmel_hlcdc_panel {
67 struct atmel_hlcdc_rgb_output base;
68 struct drm_panel *panel;
69};
70
71static inline struct atmel_hlcdc_panel *
72atmel_hlcdc_rgb_output_to_panel(struct atmel_hlcdc_rgb_output *output)
73{
74 return container_of(output, struct atmel_hlcdc_panel, base);
75}
76
77static void atmel_hlcdc_panel_encoder_enable(struct drm_encoder *encoder)
78{ 60{
79 struct atmel_hlcdc_rgb_output *rgb = 61 struct atmel_hlcdc_rgb_output *rgb =
80 drm_encoder_to_atmel_hlcdc_rgb_output(encoder); 62 drm_encoder_to_atmel_hlcdc_rgb_output(encoder);
81 struct atmel_hlcdc_panel *panel = atmel_hlcdc_rgb_output_to_panel(rgb); 63
82 drm_panel_enable(panel->panel); 64 if (rgb->panel) {
65 drm_panel_prepare(rgb->panel);
66 drm_panel_enable(rgb->panel);
67 }
83} 68}
84 69
85static void atmel_hlcdc_panel_encoder_disable(struct drm_encoder *encoder) 70static void atmel_hlcdc_rgb_encoder_disable(struct drm_encoder *encoder)
86{ 71{
87 struct atmel_hlcdc_rgb_output *rgb = 72 struct atmel_hlcdc_rgb_output *rgb =
88 drm_encoder_to_atmel_hlcdc_rgb_output(encoder); 73 drm_encoder_to_atmel_hlcdc_rgb_output(encoder);
89 struct atmel_hlcdc_panel *panel = atmel_hlcdc_rgb_output_to_panel(rgb);
90 74
91 drm_panel_disable(panel->panel); 75 if (rgb->panel) {
76 drm_panel_disable(rgb->panel);
77 drm_panel_unprepare(rgb->panel);
78 }
92} 79}
93 80
94static const struct drm_encoder_helper_funcs atmel_hlcdc_panel_encoder_helper_funcs = { 81static const struct drm_encoder_helper_funcs atmel_hlcdc_panel_encoder_helper_funcs = {
95 .disable = atmel_hlcdc_panel_encoder_disable, 82 .disable = atmel_hlcdc_rgb_encoder_disable,
96 .enable = atmel_hlcdc_panel_encoder_enable, 83 .enable = atmel_hlcdc_rgb_encoder_enable,
97}; 84};
98 85
99static void atmel_hlcdc_rgb_encoder_destroy(struct drm_encoder *encoder) 86static void atmel_hlcdc_rgb_encoder_destroy(struct drm_encoder *encoder)
@@ -110,9 +97,11 @@ static int atmel_hlcdc_panel_get_modes(struct drm_connector *connector)
110{ 97{
111 struct atmel_hlcdc_rgb_output *rgb = 98 struct atmel_hlcdc_rgb_output *rgb =
112 drm_connector_to_atmel_hlcdc_rgb_output(connector); 99 drm_connector_to_atmel_hlcdc_rgb_output(connector);
113 struct atmel_hlcdc_panel *panel = atmel_hlcdc_rgb_output_to_panel(rgb);
114 100
115 return panel->panel->funcs->get_modes(panel->panel); 101 if (rgb->panel)
102 return rgb->panel->funcs->get_modes(rgb->panel);
103
104 return 0;
116} 105}
117 106
118static int atmel_hlcdc_rgb_mode_valid(struct drm_connector *connector, 107static int atmel_hlcdc_rgb_mode_valid(struct drm_connector *connector,
@@ -144,7 +133,13 @@ static const struct drm_connector_helper_funcs atmel_hlcdc_panel_connector_helpe
144static enum drm_connector_status 133static enum drm_connector_status
145atmel_hlcdc_panel_connector_detect(struct drm_connector *connector, bool force) 134atmel_hlcdc_panel_connector_detect(struct drm_connector *connector, bool force)
146{ 135{
147 return connector_status_connected; 136 struct atmel_hlcdc_rgb_output *rgb =
137 drm_connector_to_atmel_hlcdc_rgb_output(connector);
138
139 if (rgb->panel)
140 return connector_status_connected;
141
142 return connector_status_disconnected;
148} 143}
149 144
150static void 145static void
@@ -152,9 +147,10 @@ atmel_hlcdc_panel_connector_destroy(struct drm_connector *connector)
152{ 147{
153 struct atmel_hlcdc_rgb_output *rgb = 148 struct atmel_hlcdc_rgb_output *rgb =
154 drm_connector_to_atmel_hlcdc_rgb_output(connector); 149 drm_connector_to_atmel_hlcdc_rgb_output(connector);
155 struct atmel_hlcdc_panel *panel = atmel_hlcdc_rgb_output_to_panel(rgb);
156 150
157 drm_panel_detach(panel->panel); 151 if (rgb->panel)
152 drm_panel_detach(rgb->panel);
153
158 drm_connector_cleanup(connector); 154 drm_connector_cleanup(connector);
159} 155}
160 156
@@ -168,87 +164,122 @@ static const struct drm_connector_funcs atmel_hlcdc_panel_connector_funcs = {
168 .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, 164 .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
169}; 165};
170 166
171static int atmel_hlcdc_create_panel_output(struct drm_device *dev, 167static int atmel_hlcdc_check_endpoint(struct drm_device *dev,
172 struct of_endpoint *ep) 168 const struct of_endpoint *ep)
173{ 169{
174 struct atmel_hlcdc_dc *dc = dev->dev_private;
175 struct device_node *np; 170 struct device_node *np;
176 struct drm_panel *p = NULL; 171 void *obj;
177 struct atmel_hlcdc_panel *panel;
178 int ret;
179 172
180 np = of_graph_get_remote_port_parent(ep->local_node); 173 np = of_graph_get_remote_port_parent(ep->local_node);
181 if (!np)
182 return -EINVAL;
183 174
184 p = of_drm_find_panel(np); 175 obj = of_drm_find_panel(np);
176 if (!obj)
177 obj = of_drm_find_bridge(np);
178
185 of_node_put(np); 179 of_node_put(np);
186 180
187 if (!p) 181 return obj ? 0 : -EPROBE_DEFER;
188 return -EPROBE_DEFER; 182}
189 183
190 panel = devm_kzalloc(dev->dev, sizeof(*panel), GFP_KERNEL); 184static int atmel_hlcdc_attach_endpoint(struct drm_device *dev,
191 if (!panel) 185 const struct of_endpoint *ep)
192 return -EINVAL; 186{
187 struct atmel_hlcdc_dc *dc = dev->dev_private;
188 struct atmel_hlcdc_rgb_output *output;
189 struct device_node *np;
190 struct drm_panel *panel;
191 struct drm_bridge *bridge;
192 int ret;
193 193
194 output = devm_kzalloc(dev->dev, sizeof(*output), GFP_KERNEL);
195 if (!output)
196 return -EINVAL;
194 197
195 panel->base.dc = dc; 198 output->dc = dc;
196 199
197 drm_encoder_helper_add(&panel->base.encoder, 200 drm_encoder_helper_add(&output->encoder,
198 &atmel_hlcdc_panel_encoder_helper_funcs); 201 &atmel_hlcdc_panel_encoder_helper_funcs);
199 ret = drm_encoder_init(dev, &panel->base.encoder, 202 ret = drm_encoder_init(dev, &output->encoder,
200 &atmel_hlcdc_panel_encoder_funcs, 203 &atmel_hlcdc_panel_encoder_funcs,
201 DRM_MODE_ENCODER_NONE, NULL); 204 DRM_MODE_ENCODER_NONE, NULL);
202 if (ret) 205 if (ret)
203 return ret; 206 return ret;
204 207
205 panel->base.connector.dpms = DRM_MODE_DPMS_OFF; 208 output->encoder.possible_crtcs = 0x1;
206 panel->base.connector.polled = DRM_CONNECTOR_POLL_CONNECT; 209
207 drm_connector_helper_add(&panel->base.connector, 210 np = of_graph_get_remote_port_parent(ep->local_node);
208 &atmel_hlcdc_panel_connector_helper_funcs);
209 ret = drm_connector_init(dev, &panel->base.connector,
210 &atmel_hlcdc_panel_connector_funcs,
211 DRM_MODE_CONNECTOR_Unknown);
212 if (ret)
213 goto err_encoder_cleanup;
214 211
215 drm_mode_connector_attach_encoder(&panel->base.connector, 212 ret = -EPROBE_DEFER;
216 &panel->base.encoder);
217 panel->base.encoder.possible_crtcs = 0x1;
218 213
219 drm_panel_attach(p, &panel->base.connector); 214 panel = of_drm_find_panel(np);
220 panel->panel = p; 215 if (panel) {
216 of_node_put(np);
217 output->connector.dpms = DRM_MODE_DPMS_OFF;
218 output->connector.polled = DRM_CONNECTOR_POLL_CONNECT;
219 drm_connector_helper_add(&output->connector,
220 &atmel_hlcdc_panel_connector_helper_funcs);
221 ret = drm_connector_init(dev, &output->connector,
222 &atmel_hlcdc_panel_connector_funcs,
223 DRM_MODE_CONNECTOR_Unknown);
224 if (ret)
225 goto err_encoder_cleanup;
221 226
222 return 0; 227 drm_mode_connector_attach_encoder(&output->connector,
228 &output->encoder);
229
230 ret = drm_panel_attach(panel, &output->connector);
231 if (ret) {
232 drm_connector_cleanup(&output->connector);
233 goto err_encoder_cleanup;
234 }
235
236 output->panel = panel;
237
238 return 0;
239 }
240
241 bridge = of_drm_find_bridge(np);
242 of_node_put(np);
243
244 if (bridge) {
245 output->encoder.bridge = bridge;
246 bridge->encoder = &output->encoder;
247 ret = drm_bridge_attach(dev, bridge);
248 if (!ret)
249 return 0;
250 }
223 251
224err_encoder_cleanup: 252err_encoder_cleanup:
225 drm_encoder_cleanup(&panel->base.encoder); 253 drm_encoder_cleanup(&output->encoder);
226 254
227 return ret; 255 return ret;
228} 256}
229 257
230int atmel_hlcdc_create_outputs(struct drm_device *dev) 258int atmel_hlcdc_create_outputs(struct drm_device *dev)
231{ 259{
232 struct device_node *port_np, *np; 260 struct device_node *ep_np = NULL;
233 struct of_endpoint ep; 261 struct of_endpoint ep;
234 int ret; 262 int ret;
235 263
236 port_np = of_get_child_by_name(dev->dev->of_node, "port"); 264 for_each_endpoint_of_node(dev->dev->of_node, ep_np) {
237 if (!port_np) 265 ret = of_graph_parse_endpoint(ep_np, &ep);
238 return -EINVAL; 266 if (!ret)
267 ret = atmel_hlcdc_check_endpoint(dev, &ep);
239 268
240 np = of_get_child_by_name(port_np, "endpoint"); 269 of_node_put(ep_np);
241 of_node_put(port_np); 270 if (ret)
271 return ret;
272 }
242 273
243 if (!np) 274 for_each_endpoint_of_node(dev->dev->of_node, ep_np) {
244 return -EINVAL; 275 ret = of_graph_parse_endpoint(ep_np, &ep);
276 if (!ret)
277 ret = atmel_hlcdc_attach_endpoint(dev, &ep);
245 278
246 ret = of_graph_parse_endpoint(np, &ep); 279 of_node_put(ep_np);
247 of_node_put(port_np); 280 if (ret)
281 return ret;
282 }
248 283
249 if (ret) 284 return 0;
250 return ret;
251
252 /* We currently only support panel output */
253 return atmel_hlcdc_create_panel_output(dev, &ep);
254} 285}