Blame view

drivers/gpu/drm/exynos/exynos_drm_dpi.c 7.44 KB
14b6873a0   Andrzej Hajda   drm/exynos: resto...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
  /*
   * Exynos DRM Parallel output support.
   *
   * Copyright (c) 2014 Samsung Electronics Co., Ltd
   *
   * Contacts: Andrzej Hajda <a.hajda@samsung.com>
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License version 2 as
   * published by the Free Software Foundation.
  */
  
  #include <drm/drmP.h>
  #include <drm/drm_crtc_helper.h>
  #include <drm/drm_panel.h>
  
  #include <linux/regulator/consumer.h>
  
  #include <video/of_videomode.h>
  #include <video/videomode.h>
  
  #include "exynos_drm_drv.h"
  
  struct exynos_dpi {
  	struct device *dev;
  	struct device_node *panel_node;
  
  	struct drm_panel *panel;
  	struct drm_connector connector;
  	struct drm_encoder *encoder;
  
  	struct videomode *vm;
  	int dpms_mode;
  };
  
  #define connector_to_dpi(c) container_of(c, struct exynos_dpi, connector)
  
  static enum drm_connector_status
  exynos_dpi_detect(struct drm_connector *connector, bool force)
  {
  	struct exynos_dpi *ctx = connector_to_dpi(connector);
aaa51b13f   Tomasz Figa   drm/exynos: dpi: ...
42
  	if (ctx->panel && !ctx->panel->connector)
000cc9204   Andrzej Hajda   drm/exynos: separ...
43
  		drm_panel_attach(ctx->panel, &ctx->connector);
14b6873a0   Andrzej Hajda   drm/exynos: resto...
44

000cc9204   Andrzej Hajda   drm/exynos: separ...
45
  	return connector_status_connected;
14b6873a0   Andrzej Hajda   drm/exynos: resto...
46
47
48
49
  }
  
  static void exynos_dpi_connector_destroy(struct drm_connector *connector)
  {
34ea3d386   Thomas Wood   drm: add register...
50
  	drm_connector_unregister(connector);
14b6873a0   Andrzej Hajda   drm/exynos: resto...
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
  	drm_connector_cleanup(connector);
  }
  
  static struct drm_connector_funcs exynos_dpi_connector_funcs = {
  	.dpms = drm_helper_connector_dpms,
  	.detect = exynos_dpi_detect,
  	.fill_modes = drm_helper_probe_single_connector_modes,
  	.destroy = exynos_dpi_connector_destroy,
  };
  
  static int exynos_dpi_get_modes(struct drm_connector *connector)
  {
  	struct exynos_dpi *ctx = connector_to_dpi(connector);
  
  	/* fimd timings gets precedence over panel modes */
  	if (ctx->vm) {
  		struct drm_display_mode *mode;
  
  		mode = drm_mode_create(connector->dev);
  		if (!mode) {
  			DRM_ERROR("failed to create a new display mode
  ");
  			return 0;
  		}
  		drm_display_mode_from_videomode(ctx->vm, mode);
  		mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
  		drm_mode_probed_add(connector, mode);
  		return 1;
  	}
  
  	if (ctx->panel)
  		return ctx->panel->funcs->get_modes(ctx->panel);
  
  	return 0;
  }
14b6873a0   Andrzej Hajda   drm/exynos: resto...
86
87
88
89
90
91
92
93
94
95
  static struct drm_encoder *
  exynos_dpi_best_encoder(struct drm_connector *connector)
  {
  	struct exynos_dpi *ctx = connector_to_dpi(connector);
  
  	return ctx->encoder;
  }
  
  static struct drm_connector_helper_funcs exynos_dpi_connector_helper_funcs = {
  	.get_modes = exynos_dpi_get_modes,
14b6873a0   Andrzej Hajda   drm/exynos: resto...
96
97
98
99
100
101
102
103
104
105
106
  	.best_encoder = exynos_dpi_best_encoder,
  };
  
  static int exynos_dpi_create_connector(struct exynos_drm_display *display,
  				       struct drm_encoder *encoder)
  {
  	struct exynos_dpi *ctx = display->ctx;
  	struct drm_connector *connector = &ctx->connector;
  	int ret;
  
  	ctx->encoder = encoder;
ae08fe6c1   Inki Dae   drm/exynos: dpi: ...
107
  	connector->polled = DRM_CONNECTOR_POLL_HPD;
14b6873a0   Andrzej Hajda   drm/exynos: resto...
108
109
110
111
112
113
114
115
116
117
118
  
  	ret = drm_connector_init(encoder->dev, connector,
  				 &exynos_dpi_connector_funcs,
  				 DRM_MODE_CONNECTOR_VGA);
  	if (ret) {
  		DRM_ERROR("failed to initialize connector with drm
  ");
  		return ret;
  	}
  
  	drm_connector_helper_add(connector, &exynos_dpi_connector_helper_funcs);
34ea3d386   Thomas Wood   drm: add register...
119
  	drm_connector_register(connector);
14b6873a0   Andrzej Hajda   drm/exynos: resto...
120
121
122
123
124
125
126
  	drm_mode_connector_attach_encoder(connector, encoder);
  
  	return 0;
  }
  
  static void exynos_dpi_poweron(struct exynos_dpi *ctx)
  {
39bbde9cc   Ajay Kumar   drm/exynos: dpi: ...
127
128
  	if (ctx->panel) {
  		drm_panel_prepare(ctx->panel);
14b6873a0   Andrzej Hajda   drm/exynos: resto...
129
  		drm_panel_enable(ctx->panel);
39bbde9cc   Ajay Kumar   drm/exynos: dpi: ...
130
  	}
14b6873a0   Andrzej Hajda   drm/exynos: resto...
131
132
133
134
  }
  
  static void exynos_dpi_poweroff(struct exynos_dpi *ctx)
  {
39bbde9cc   Ajay Kumar   drm/exynos: dpi: ...
135
  	if (ctx->panel) {
14b6873a0   Andrzej Hajda   drm/exynos: resto...
136
  		drm_panel_disable(ctx->panel);
39bbde9cc   Ajay Kumar   drm/exynos: dpi: ...
137
138
  		drm_panel_unprepare(ctx->panel);
  	}
14b6873a0   Andrzej Hajda   drm/exynos: resto...
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
  }
  
  static void exynos_dpi_dpms(struct exynos_drm_display *display, int mode)
  {
  	struct exynos_dpi *ctx = display->ctx;
  
  	switch (mode) {
  	case DRM_MODE_DPMS_ON:
  		if (ctx->dpms_mode != DRM_MODE_DPMS_ON)
  				exynos_dpi_poweron(ctx);
  			break;
  	case DRM_MODE_DPMS_STANDBY:
  	case DRM_MODE_DPMS_SUSPEND:
  	case DRM_MODE_DPMS_OFF:
  		if (ctx->dpms_mode == DRM_MODE_DPMS_ON)
  			exynos_dpi_poweroff(ctx);
  		break;
  	default:
  		break;
10d9b4ed3   Damien Lespiau   drm: Remove spuri...
158
  	}
14b6873a0   Andrzej Hajda   drm/exynos: resto...
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
  	ctx->dpms_mode = mode;
  }
  
  static struct exynos_drm_display_ops exynos_dpi_display_ops = {
  	.create_connector = exynos_dpi_create_connector,
  	.dpms = exynos_dpi_dpms
  };
  
  static struct exynos_drm_display exynos_dpi_display = {
  	.type = EXYNOS_DISPLAY_TYPE_LCD,
  	.ops = &exynos_dpi_display_ops,
  };
  
  /* of_* functions will be removed after merge of of_graph patches */
  static struct device_node *
  of_get_child_by_name_reg(struct device_node *parent, const char *name, u32 reg)
  {
  	struct device_node *np;
  
  	for_each_child_of_node(parent, np) {
  		u32 r;
  
  		if (!np->name || of_node_cmp(np->name, name))
  			continue;
  
  		if (of_property_read_u32(np, "reg", &r) < 0)
  			r = 0;
  
  		if (reg == r)
  			break;
  	}
  
  	return np;
  }
  
  static struct device_node *of_graph_get_port_by_reg(struct device_node *parent,
  						    u32 reg)
  {
  	struct device_node *ports, *port;
  
  	ports = of_get_child_by_name(parent, "ports");
  	if (ports)
  		parent = ports;
  
  	port = of_get_child_by_name_reg(parent, "port", reg);
  
  	of_node_put(ports);
  
  	return port;
  }
  
  static struct device_node *
  of_graph_get_endpoint_by_reg(struct device_node *port, u32 reg)
  {
  	return of_get_child_by_name_reg(port, "endpoint", reg);
  }
  
  static struct device_node *
  of_graph_get_remote_port_parent(const struct device_node *node)
  {
  	struct device_node *np;
  	unsigned int depth;
  
  	np = of_parse_phandle(node, "remote-endpoint", 0);
  
  	/* Walk 3 levels up only if there is 'ports' node. */
  	for (depth = 3; depth && np; depth--) {
  		np = of_get_next_parent(np);
  		if (depth == 2 && of_node_cmp(np->name, "ports"))
  			break;
  	}
  	return np;
  }
  
  enum {
  	FIMD_PORT_IN0,
  	FIMD_PORT_IN1,
  	FIMD_PORT_IN2,
  	FIMD_PORT_RGB,
  	FIMD_PORT_WRB,
  };
f46ed1abe   Sachin Kamat   drm/exynos: Stati...
240
  static struct device_node *exynos_dpi_of_find_panel_node(struct device *dev)
14b6873a0   Andrzej Hajda   drm/exynos: resto...
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
  {
  	struct device_node *np, *ep;
  
  	np = of_graph_get_port_by_reg(dev->of_node, FIMD_PORT_RGB);
  	if (!np)
  		return NULL;
  
  	ep = of_graph_get_endpoint_by_reg(np, 0);
  	of_node_put(np);
  	if (!ep)
  		return NULL;
  
  	np = of_graph_get_remote_port_parent(ep);
  	of_node_put(ep);
  
  	return np;
  }
  
  static int exynos_dpi_parse_dt(struct exynos_dpi *ctx)
  {
  	struct device *dev = ctx->dev;
  	struct device_node *dn = dev->of_node;
  	struct device_node *np;
  
  	ctx->panel_node = exynos_dpi_of_find_panel_node(dev);
  
  	np = of_get_child_by_name(dn, "display-timings");
  	if (np) {
  		struct videomode *vm;
  		int ret;
  
  		of_node_put(np);
  
  		vm = devm_kzalloc(dev, sizeof(*ctx->vm), GFP_KERNEL);
  		if (!vm)
  			return -ENOMEM;
  
  		ret = of_get_videomode(dn, vm, 0);
000cc9204   Andrzej Hajda   drm/exynos: separ...
279
280
  		if (ret < 0) {
  			devm_kfree(dev, vm);
14b6873a0   Andrzej Hajda   drm/exynos: resto...
281
  			return ret;
000cc9204   Andrzej Hajda   drm/exynos: separ...
282
  		}
14b6873a0   Andrzej Hajda   drm/exynos: resto...
283
284
285
286
287
288
289
290
291
292
293
  
  		ctx->vm = vm;
  
  		return 0;
  	}
  
  	if (!ctx->panel_node)
  		return -EINVAL;
  
  	return 0;
  }
000cc9204   Andrzej Hajda   drm/exynos: separ...
294
  struct exynos_drm_display *exynos_dpi_probe(struct device *dev)
14b6873a0   Andrzej Hajda   drm/exynos: resto...
295
296
297
  {
  	struct exynos_dpi *ctx;
  	int ret;
df5225bc9   Inki Dae   drm/exynos: consi...
298
299
300
301
302
  	ret = exynos_drm_component_add(dev,
  					EXYNOS_DEVICE_TYPE_CONNECTOR,
  					exynos_dpi_display.type);
  	if (ret)
  		return ERR_PTR(ret);
14b6873a0   Andrzej Hajda   drm/exynos: resto...
303
304
  	ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
  	if (!ctx)
df5225bc9   Inki Dae   drm/exynos: consi...
305
  		goto err_del_component;
14b6873a0   Andrzej Hajda   drm/exynos: resto...
306
307
308
309
310
311
  
  	ctx->dev = dev;
  	exynos_dpi_display.ctx = ctx;
  	ctx->dpms_mode = DRM_MODE_DPMS_OFF;
  
  	ret = exynos_dpi_parse_dt(ctx);
000cc9204   Andrzej Hajda   drm/exynos: separ...
312
313
  	if (ret < 0) {
  		devm_kfree(dev, ctx);
df5225bc9   Inki Dae   drm/exynos: consi...
314
  		goto err_del_component;
000cc9204   Andrzej Hajda   drm/exynos: separ...
315
316
317
318
  	}
  
  	if (ctx->panel_node) {
  		ctx->panel = of_drm_find_panel(ctx->panel_node);
df5225bc9   Inki Dae   drm/exynos: consi...
319
320
321
  		if (!ctx->panel) {
  			exynos_drm_component_del(dev,
  						EXYNOS_DEVICE_TYPE_CONNECTOR);
000cc9204   Andrzej Hajda   drm/exynos: separ...
322
  			return ERR_PTR(-EPROBE_DEFER);
df5225bc9   Inki Dae   drm/exynos: consi...
323
  		}
000cc9204   Andrzej Hajda   drm/exynos: separ...
324
  	}
14b6873a0   Andrzej Hajda   drm/exynos: resto...
325

000cc9204   Andrzej Hajda   drm/exynos: separ...
326
  	return &exynos_dpi_display;
df5225bc9   Inki Dae   drm/exynos: consi...
327
328
329
330
331
  
  err_del_component:
  	exynos_drm_component_del(dev, EXYNOS_DEVICE_TYPE_CONNECTOR);
  
  	return NULL;
14b6873a0   Andrzej Hajda   drm/exynos: resto...
332
  }
000cc9204   Andrzej Hajda   drm/exynos: separ...
333
  int exynos_dpi_remove(struct device *dev)
14b6873a0   Andrzej Hajda   drm/exynos: resto...
334
  {
f37cd5e80   Inki Dae   drm/exynos: add c...
335
  	struct exynos_dpi *ctx = exynos_dpi_display.ctx;
14b6873a0   Andrzej Hajda   drm/exynos: resto...
336
  	exynos_dpi_dpms(&exynos_dpi_display, DRM_MODE_DPMS_OFF);
90eac8972   Andrzej Hajda   drm/exynos/dpi: u...
337

90eac8972   Andrzej Hajda   drm/exynos/dpi: u...
338
339
  	if (ctx->panel)
  		drm_panel_detach(ctx->panel);
14b6873a0   Andrzej Hajda   drm/exynos: resto...
340

df5225bc9   Inki Dae   drm/exynos: consi...
341
  	exynos_drm_component_del(dev, EXYNOS_DEVICE_TYPE_CONNECTOR);
14b6873a0   Andrzej Hajda   drm/exynos: resto...
342
343
  	return 0;
  }