diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/gpu/drm/Makefile | 3 | ||||
-rw-r--r-- | drivers/gpu/drm/drm_crtc.c | 2497 | ||||
-rw-r--r-- | drivers/gpu/drm/drm_crtc_helper.c | 822 | ||||
-rw-r--r-- | drivers/gpu/drm/drm_drv.c | 34 | ||||
-rw-r--r-- | drivers/gpu/drm/drm_edid.c | 732 | ||||
-rw-r--r-- | drivers/gpu/drm/drm_fops.c | 22 | ||||
-rw-r--r-- | drivers/gpu/drm/drm_irq.c | 64 | ||||
-rw-r--r-- | drivers/gpu/drm/drm_mm.c | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/drm_modes.c | 576 | ||||
-rw-r--r-- | drivers/gpu/drm/drm_stub.c | 30 | ||||
-rw-r--r-- | drivers/gpu/drm/drm_sysfs.c | 329 | ||||
-rw-r--r-- | drivers/video/console/vgacon.c | 17 |
12 files changed, 5087 insertions, 40 deletions
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 74da99495e21..30022c4a5c12 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile | |||
@@ -9,7 +9,8 @@ drm-y := drm_auth.o drm_bufs.o drm_cache.o \ | |||
9 | drm_drv.o drm_fops.o drm_gem.o drm_ioctl.o drm_irq.o \ | 9 | drm_drv.o drm_fops.o drm_gem.o drm_ioctl.o drm_irq.o \ |
10 | drm_lock.o drm_memory.o drm_proc.o drm_stub.o drm_vm.o \ | 10 | drm_lock.o drm_memory.o drm_proc.o drm_stub.o drm_vm.o \ |
11 | drm_agpsupport.o drm_scatter.o ati_pcigart.o drm_pci.o \ | 11 | drm_agpsupport.o drm_scatter.o ati_pcigart.o drm_pci.o \ |
12 | drm_sysfs.o drm_hashtab.o drm_sman.o drm_mm.o | 12 | drm_sysfs.o drm_hashtab.o drm_sman.o drm_mm.o \ |
13 | drm_crtc.o drm_crtc_helper.o drm_modes.o drm_edid.o | ||
13 | 14 | ||
14 | drm-$(CONFIG_COMPAT) += drm_ioc32.o | 15 | drm-$(CONFIG_COMPAT) += drm_ioc32.o |
15 | 16 | ||
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c new file mode 100644 index 000000000000..2e880240477e --- /dev/null +++ b/drivers/gpu/drm/drm_crtc.c | |||
@@ -0,0 +1,2497 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2006-2008 Intel Corporation | ||
3 | * Copyright (c) 2007 Dave Airlie <airlied@linux.ie> | ||
4 | * Copyright (c) 2008 Red Hat Inc. | ||
5 | * | ||
6 | * DRM core CRTC related functions | ||
7 | * | ||
8 | * Permission to use, copy, modify, distribute, and sell this software and its | ||
9 | * documentation for any purpose is hereby granted without fee, provided that | ||
10 | * the above copyright notice appear in all copies and that both that copyright | ||
11 | * notice and this permission notice appear in supporting documentation, and | ||
12 | * that the name of the copyright holders not be used in advertising or | ||
13 | * publicity pertaining to distribution of the software without specific, | ||
14 | * written prior permission. The copyright holders make no representations | ||
15 | * about the suitability of this software for any purpose. It is provided "as | ||
16 | * is" without express or implied warranty. | ||
17 | * | ||
18 | * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, | ||
19 | * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO | ||
20 | * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR | ||
21 | * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, | ||
22 | * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER | ||
23 | * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE | ||
24 | * OF THIS SOFTWARE. | ||
25 | * | ||
26 | * Authors: | ||
27 | * Keith Packard | ||
28 | * Eric Anholt <eric@anholt.net> | ||
29 | * Dave Airlie <airlied@linux.ie> | ||
30 | * Jesse Barnes <jesse.barnes@intel.com> | ||
31 | */ | ||
32 | #include <linux/list.h> | ||
33 | #include "drm.h" | ||
34 | #include "drmP.h" | ||
35 | #include "drm_crtc.h" | ||
36 | |||
37 | struct drm_prop_enum_list { | ||
38 | int type; | ||
39 | char *name; | ||
40 | }; | ||
41 | |||
42 | /* Avoid boilerplate. I'm tired of typing. */ | ||
43 | #define DRM_ENUM_NAME_FN(fnname, list) \ | ||
44 | char *fnname(int val) \ | ||
45 | { \ | ||
46 | int i; \ | ||
47 | for (i = 0; i < ARRAY_SIZE(list); i++) { \ | ||
48 | if (list[i].type == val) \ | ||
49 | return list[i].name; \ | ||
50 | } \ | ||
51 | return "(unknown)"; \ | ||
52 | } | ||
53 | |||
54 | /* | ||
55 | * Global properties | ||
56 | */ | ||
57 | static struct drm_prop_enum_list drm_dpms_enum_list[] = | ||
58 | { { DRM_MODE_DPMS_ON, "On" }, | ||
59 | { DRM_MODE_DPMS_STANDBY, "Standby" }, | ||
60 | { DRM_MODE_DPMS_SUSPEND, "Suspend" }, | ||
61 | { DRM_MODE_DPMS_OFF, "Off" } | ||
62 | }; | ||
63 | |||
64 | DRM_ENUM_NAME_FN(drm_get_dpms_name, drm_dpms_enum_list) | ||
65 | |||
66 | /* | ||
67 | * Optional properties | ||
68 | */ | ||
69 | static struct drm_prop_enum_list drm_scaling_mode_enum_list[] = | ||
70 | { | ||
71 | { DRM_MODE_SCALE_NON_GPU, "Non-GPU" }, | ||
72 | { DRM_MODE_SCALE_FULLSCREEN, "Fullscreen" }, | ||
73 | { DRM_MODE_SCALE_NO_SCALE, "No scale" }, | ||
74 | { DRM_MODE_SCALE_ASPECT, "Aspect" }, | ||
75 | }; | ||
76 | |||
77 | static struct drm_prop_enum_list drm_dithering_mode_enum_list[] = | ||
78 | { | ||
79 | { DRM_MODE_DITHERING_OFF, "Off" }, | ||
80 | { DRM_MODE_DITHERING_ON, "On" }, | ||
81 | }; | ||
82 | |||
83 | /* | ||
84 | * Non-global properties, but "required" for certain connectors. | ||
85 | */ | ||
86 | static struct drm_prop_enum_list drm_dvi_i_select_enum_list[] = | ||
87 | { | ||
88 | { DRM_MODE_SUBCONNECTOR_Automatic, "Automatic" }, /* DVI-I and TV-out */ | ||
89 | { DRM_MODE_SUBCONNECTOR_DVID, "DVI-D" }, /* DVI-I */ | ||
90 | { DRM_MODE_SUBCONNECTOR_DVIA, "DVI-A" }, /* DVI-I */ | ||
91 | }; | ||
92 | |||
93 | DRM_ENUM_NAME_FN(drm_get_dvi_i_select_name, drm_dvi_i_select_enum_list) | ||
94 | |||
95 | static struct drm_prop_enum_list drm_dvi_i_subconnector_enum_list[] = | ||
96 | { | ||
97 | { DRM_MODE_SUBCONNECTOR_Unknown, "Unknown" }, /* DVI-I and TV-out */ | ||
98 | { DRM_MODE_SUBCONNECTOR_DVID, "DVI-D" }, /* DVI-I */ | ||
99 | { DRM_MODE_SUBCONNECTOR_DVIA, "DVI-A" }, /* DVI-I */ | ||
100 | }; | ||
101 | |||
102 | DRM_ENUM_NAME_FN(drm_get_dvi_i_subconnector_name, | ||
103 | drm_dvi_i_subconnector_enum_list) | ||
104 | |||
105 | static struct drm_prop_enum_list drm_tv_select_enum_list[] = | ||
106 | { | ||
107 | { DRM_MODE_SUBCONNECTOR_Automatic, "Automatic" }, /* DVI-I and TV-out */ | ||
108 | { DRM_MODE_SUBCONNECTOR_Composite, "Composite" }, /* TV-out */ | ||
109 | { DRM_MODE_SUBCONNECTOR_SVIDEO, "SVIDEO" }, /* TV-out */ | ||
110 | { DRM_MODE_SUBCONNECTOR_Component, "Component" }, /* TV-out */ | ||
111 | }; | ||
112 | |||
113 | DRM_ENUM_NAME_FN(drm_get_tv_select_name, drm_tv_select_enum_list) | ||
114 | |||
115 | static struct drm_prop_enum_list drm_tv_subconnector_enum_list[] = | ||
116 | { | ||
117 | { DRM_MODE_SUBCONNECTOR_Unknown, "Unknown" }, /* DVI-I and TV-out */ | ||
118 | { DRM_MODE_SUBCONNECTOR_Composite, "Composite" }, /* TV-out */ | ||
119 | { DRM_MODE_SUBCONNECTOR_SVIDEO, "SVIDEO" }, /* TV-out */ | ||
120 | { DRM_MODE_SUBCONNECTOR_Component, "Component" }, /* TV-out */ | ||
121 | }; | ||
122 | |||
123 | DRM_ENUM_NAME_FN(drm_get_tv_subconnector_name, | ||
124 | drm_tv_subconnector_enum_list) | ||
125 | |||
126 | struct drm_conn_prop_enum_list { | ||
127 | int type; | ||
128 | char *name; | ||
129 | int count; | ||
130 | }; | ||
131 | |||
132 | /* | ||
133 | * Connector and encoder types. | ||
134 | */ | ||
135 | static struct drm_conn_prop_enum_list drm_connector_enum_list[] = | ||
136 | { { DRM_MODE_CONNECTOR_Unknown, "Unknown", 0 }, | ||
137 | { DRM_MODE_CONNECTOR_VGA, "VGA", 0 }, | ||
138 | { DRM_MODE_CONNECTOR_DVII, "DVI-I", 0 }, | ||
139 | { DRM_MODE_CONNECTOR_DVID, "DVI-D", 0 }, | ||
140 | { DRM_MODE_CONNECTOR_DVIA, "DVI-A", 0 }, | ||
141 | { DRM_MODE_CONNECTOR_Composite, "Composite", 0 }, | ||
142 | { DRM_MODE_CONNECTOR_SVIDEO, "SVIDEO", 0 }, | ||
143 | { DRM_MODE_CONNECTOR_LVDS, "LVDS", 0 }, | ||
144 | { DRM_MODE_CONNECTOR_Component, "Component", 0 }, | ||
145 | { DRM_MODE_CONNECTOR_9PinDIN, "9-pin DIN", 0 }, | ||
146 | { DRM_MODE_CONNECTOR_DisplayPort, "DisplayPort", 0 }, | ||
147 | { DRM_MODE_CONNECTOR_HDMIA, "HDMI Type A", 0 }, | ||
148 | { DRM_MODE_CONNECTOR_HDMIB, "HDMI Type B", 0 }, | ||
149 | }; | ||
150 | |||
151 | static struct drm_prop_enum_list drm_encoder_enum_list[] = | ||
152 | { { DRM_MODE_ENCODER_NONE, "None" }, | ||
153 | { DRM_MODE_ENCODER_DAC, "DAC" }, | ||
154 | { DRM_MODE_ENCODER_TMDS, "TMDS" }, | ||
155 | { DRM_MODE_ENCODER_LVDS, "LVDS" }, | ||
156 | { DRM_MODE_ENCODER_TVDAC, "TV" }, | ||
157 | }; | ||
158 | |||
159 | char *drm_get_encoder_name(struct drm_encoder *encoder) | ||
160 | { | ||
161 | static char buf[32]; | ||
162 | |||
163 | snprintf(buf, 32, "%s-%d", | ||
164 | drm_encoder_enum_list[encoder->encoder_type].name, | ||
165 | encoder->base.id); | ||
166 | return buf; | ||
167 | } | ||
168 | |||
169 | char *drm_get_connector_name(struct drm_connector *connector) | ||
170 | { | ||
171 | static char buf[32]; | ||
172 | |||
173 | snprintf(buf, 32, "%s-%d", | ||
174 | drm_connector_enum_list[connector->connector_type].name, | ||
175 | connector->connector_type_id); | ||
176 | return buf; | ||
177 | } | ||
178 | EXPORT_SYMBOL(drm_get_connector_name); | ||
179 | |||
180 | char *drm_get_connector_status_name(enum drm_connector_status status) | ||
181 | { | ||
182 | if (status == connector_status_connected) | ||
183 | return "connected"; | ||
184 | else if (status == connector_status_disconnected) | ||
185 | return "disconnected"; | ||
186 | else | ||
187 | return "unknown"; | ||
188 | } | ||
189 | |||
190 | /** | ||
191 | * drm_mode_object_get - allocate a new identifier | ||
192 | * @dev: DRM device | ||
193 | * @ptr: object pointer, used to generate unique ID | ||
194 | * @type: object type | ||
195 | * | ||
196 | * LOCKING: | ||
197 | * Caller must hold DRM mode_config lock. | ||
198 | * | ||
199 | * Create a unique identifier based on @ptr in @dev's identifier space. Used | ||
200 | * for tracking modes, CRTCs and connectors. | ||
201 | * | ||
202 | * RETURNS: | ||
203 | * New unique (relative to other objects in @dev) integer identifier for the | ||
204 | * object. | ||
205 | */ | ||
206 | static int drm_mode_object_get(struct drm_device *dev, | ||
207 | struct drm_mode_object *obj, uint32_t obj_type) | ||
208 | { | ||
209 | int new_id = 0; | ||
210 | int ret; | ||
211 | |||
212 | WARN(!mutex_is_locked(&dev->mode_config.mutex), | ||
213 | "%s called w/o mode_config lock\n", __FUNCTION__); | ||
214 | again: | ||
215 | if (idr_pre_get(&dev->mode_config.crtc_idr, GFP_KERNEL) == 0) { | ||
216 | DRM_ERROR("Ran out memory getting a mode number\n"); | ||
217 | return -EINVAL; | ||
218 | } | ||
219 | |||
220 | ret = idr_get_new_above(&dev->mode_config.crtc_idr, obj, 1, &new_id); | ||
221 | if (ret == -EAGAIN) | ||
222 | goto again; | ||
223 | |||
224 | obj->id = new_id; | ||
225 | obj->type = obj_type; | ||
226 | return 0; | ||
227 | } | ||
228 | |||
229 | /** | ||
230 | * drm_mode_object_put - free an identifer | ||
231 | * @dev: DRM device | ||
232 | * @id: ID to free | ||
233 | * | ||
234 | * LOCKING: | ||
235 | * Caller must hold DRM mode_config lock. | ||
236 | * | ||
237 | * Free @id from @dev's unique identifier pool. | ||
238 | */ | ||
239 | static void drm_mode_object_put(struct drm_device *dev, | ||
240 | struct drm_mode_object *object) | ||
241 | { | ||
242 | idr_remove(&dev->mode_config.crtc_idr, object->id); | ||
243 | } | ||
244 | |||
245 | void *drm_mode_object_find(struct drm_device *dev, uint32_t id, uint32_t type) | ||
246 | { | ||
247 | struct drm_mode_object *obj; | ||
248 | |||
249 | obj = idr_find(&dev->mode_config.crtc_idr, id); | ||
250 | if (!obj || (obj->type != type) || (obj->id != id)) | ||
251 | return NULL; | ||
252 | |||
253 | return obj; | ||
254 | } | ||
255 | EXPORT_SYMBOL(drm_mode_object_find); | ||
256 | |||
257 | /** | ||
258 | * drm_crtc_from_fb - find the CRTC structure associated with an fb | ||
259 | * @dev: DRM device | ||
260 | * @fb: framebuffer in question | ||
261 | * | ||
262 | * LOCKING: | ||
263 | * Caller must hold mode_config lock. | ||
264 | * | ||
265 | * Find CRTC in the mode_config structure that matches @fb. | ||
266 | * | ||
267 | * RETURNS: | ||
268 | * Pointer to the CRTC or NULL if it wasn't found. | ||
269 | */ | ||
270 | struct drm_crtc *drm_crtc_from_fb(struct drm_device *dev, | ||
271 | struct drm_framebuffer *fb) | ||
272 | { | ||
273 | struct drm_crtc *crtc; | ||
274 | |||
275 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { | ||
276 | if (crtc->fb == fb) | ||
277 | return crtc; | ||
278 | } | ||
279 | return NULL; | ||
280 | } | ||
281 | |||
282 | /** | ||
283 | * drm_framebuffer_init - initialize a framebuffer | ||
284 | * @dev: DRM device | ||
285 | * | ||
286 | * LOCKING: | ||
287 | * Caller must hold mode config lock. | ||
288 | * | ||
289 | * Allocates an ID for the framebuffer's parent mode object, sets its mode | ||
290 | * functions & device file and adds it to the master fd list. | ||
291 | * | ||
292 | * RETURNS: | ||
293 | * Zero on success, error code on falure. | ||
294 | */ | ||
295 | int drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb, | ||
296 | const struct drm_framebuffer_funcs *funcs) | ||
297 | { | ||
298 | int ret; | ||
299 | |||
300 | ret = drm_mode_object_get(dev, &fb->base, DRM_MODE_OBJECT_FB); | ||
301 | if (ret) { | ||
302 | return ret; | ||
303 | } | ||
304 | |||
305 | fb->dev = dev; | ||
306 | fb->funcs = funcs; | ||
307 | dev->mode_config.num_fb++; | ||
308 | list_add(&fb->head, &dev->mode_config.fb_list); | ||
309 | |||
310 | return 0; | ||
311 | } | ||
312 | EXPORT_SYMBOL(drm_framebuffer_init); | ||
313 | |||
314 | /** | ||
315 | * drm_framebuffer_cleanup - remove a framebuffer object | ||
316 | * @fb: framebuffer to remove | ||
317 | * | ||
318 | * LOCKING: | ||
319 | * Caller must hold mode config lock. | ||
320 | * | ||
321 | * Scans all the CRTCs in @dev's mode_config. If they're using @fb, removes | ||
322 | * it, setting it to NULL. | ||
323 | */ | ||
324 | void drm_framebuffer_cleanup(struct drm_framebuffer *fb) | ||
325 | { | ||
326 | struct drm_device *dev = fb->dev; | ||
327 | struct drm_crtc *crtc; | ||
328 | |||
329 | /* remove from any CRTC */ | ||
330 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { | ||
331 | if (crtc->fb == fb) | ||
332 | crtc->fb = NULL; | ||
333 | } | ||
334 | |||
335 | drm_mode_object_put(dev, &fb->base); | ||
336 | list_del(&fb->head); | ||
337 | dev->mode_config.num_fb--; | ||
338 | } | ||
339 | EXPORT_SYMBOL(drm_framebuffer_cleanup); | ||
340 | |||
341 | /** | ||
342 | * drm_crtc_init - Initialise a new CRTC object | ||
343 | * @dev: DRM device | ||
344 | * @crtc: CRTC object to init | ||
345 | * @funcs: callbacks for the new CRTC | ||
346 | * | ||
347 | * LOCKING: | ||
348 | * Caller must hold mode config lock. | ||
349 | * | ||
350 | * Inits a new object created as base part of an driver crtc object. | ||
351 | */ | ||
352 | void drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc, | ||
353 | const struct drm_crtc_funcs *funcs) | ||
354 | { | ||
355 | crtc->dev = dev; | ||
356 | crtc->funcs = funcs; | ||
357 | |||
358 | mutex_lock(&dev->mode_config.mutex); | ||
359 | drm_mode_object_get(dev, &crtc->base, DRM_MODE_OBJECT_CRTC); | ||
360 | |||
361 | list_add_tail(&crtc->head, &dev->mode_config.crtc_list); | ||
362 | dev->mode_config.num_crtc++; | ||
363 | mutex_unlock(&dev->mode_config.mutex); | ||
364 | } | ||
365 | EXPORT_SYMBOL(drm_crtc_init); | ||
366 | |||
367 | /** | ||
368 | * drm_crtc_cleanup - Cleans up the core crtc usage. | ||
369 | * @crtc: CRTC to cleanup | ||
370 | * | ||
371 | * LOCKING: | ||
372 | * Caller must hold mode config lock. | ||
373 | * | ||
374 | * Cleanup @crtc. Removes from drm modesetting space | ||
375 | * does NOT free object, caller does that. | ||
376 | */ | ||
377 | void drm_crtc_cleanup(struct drm_crtc *crtc) | ||
378 | { | ||
379 | struct drm_device *dev = crtc->dev; | ||
380 | |||
381 | if (crtc->gamma_store) { | ||
382 | kfree(crtc->gamma_store); | ||
383 | crtc->gamma_store = NULL; | ||
384 | } | ||
385 | |||
386 | drm_mode_object_put(dev, &crtc->base); | ||
387 | list_del(&crtc->head); | ||
388 | dev->mode_config.num_crtc--; | ||
389 | } | ||
390 | EXPORT_SYMBOL(drm_crtc_cleanup); | ||
391 | |||
392 | /** | ||
393 | * drm_mode_probed_add - add a mode to a connector's probed mode list | ||
394 | * @connector: connector the new mode | ||
395 | * @mode: mode data | ||
396 | * | ||
397 | * LOCKING: | ||
398 | * Caller must hold mode config lock. | ||
399 | * | ||
400 | * Add @mode to @connector's mode list for later use. | ||
401 | */ | ||
402 | void drm_mode_probed_add(struct drm_connector *connector, | ||
403 | struct drm_display_mode *mode) | ||
404 | { | ||
405 | list_add(&mode->head, &connector->probed_modes); | ||
406 | } | ||
407 | EXPORT_SYMBOL(drm_mode_probed_add); | ||
408 | |||
409 | /** | ||
410 | * drm_mode_remove - remove and free a mode | ||
411 | * @connector: connector list to modify | ||
412 | * @mode: mode to remove | ||
413 | * | ||
414 | * LOCKING: | ||
415 | * Caller must hold mode config lock. | ||
416 | * | ||
417 | * Remove @mode from @connector's mode list, then free it. | ||
418 | */ | ||
419 | void drm_mode_remove(struct drm_connector *connector, | ||
420 | struct drm_display_mode *mode) | ||
421 | { | ||
422 | list_del(&mode->head); | ||
423 | kfree(mode); | ||
424 | } | ||
425 | EXPORT_SYMBOL(drm_mode_remove); | ||
426 | |||
427 | /** | ||
428 | * drm_connector_init - Init a preallocated connector | ||
429 | * @dev: DRM device | ||
430 | * @connector: the connector to init | ||
431 | * @funcs: callbacks for this connector | ||
432 | * @name: user visible name of the connector | ||
433 | * | ||
434 | * LOCKING: | ||
435 | * Caller must hold @dev's mode_config lock. | ||
436 | * | ||
437 | * Initialises a preallocated connector. Connectors should be | ||
438 | * subclassed as part of driver connector objects. | ||
439 | */ | ||
440 | void drm_connector_init(struct drm_device *dev, | ||
441 | struct drm_connector *connector, | ||
442 | const struct drm_connector_funcs *funcs, | ||
443 | int connector_type) | ||
444 | { | ||
445 | mutex_lock(&dev->mode_config.mutex); | ||
446 | |||
447 | connector->dev = dev; | ||
448 | connector->funcs = funcs; | ||
449 | drm_mode_object_get(dev, &connector->base, DRM_MODE_OBJECT_CONNECTOR); | ||
450 | connector->connector_type = connector_type; | ||
451 | connector->connector_type_id = | ||
452 | ++drm_connector_enum_list[connector_type].count; /* TODO */ | ||
453 | INIT_LIST_HEAD(&connector->user_modes); | ||
454 | INIT_LIST_HEAD(&connector->probed_modes); | ||
455 | INIT_LIST_HEAD(&connector->modes); | ||
456 | connector->edid_blob_ptr = NULL; | ||
457 | |||
458 | list_add_tail(&connector->head, &dev->mode_config.connector_list); | ||
459 | dev->mode_config.num_connector++; | ||
460 | |||
461 | drm_connector_attach_property(connector, | ||
462 | dev->mode_config.edid_property, 0); | ||
463 | |||
464 | drm_connector_attach_property(connector, | ||
465 | dev->mode_config.dpms_property, 0); | ||
466 | |||
467 | mutex_unlock(&dev->mode_config.mutex); | ||
468 | } | ||
469 | EXPORT_SYMBOL(drm_connector_init); | ||
470 | |||
471 | /** | ||
472 | * drm_connector_cleanup - cleans up an initialised connector | ||
473 | * @connector: connector to cleanup | ||
474 | * | ||
475 | * LOCKING: | ||
476 | * Caller must hold @dev's mode_config lock. | ||
477 | * | ||
478 | * Cleans up the connector but doesn't free the object. | ||
479 | */ | ||
480 | void drm_connector_cleanup(struct drm_connector *connector) | ||
481 | { | ||
482 | struct drm_device *dev = connector->dev; | ||
483 | struct drm_display_mode *mode, *t; | ||
484 | |||
485 | list_for_each_entry_safe(mode, t, &connector->probed_modes, head) | ||
486 | drm_mode_remove(connector, mode); | ||
487 | |||
488 | list_for_each_entry_safe(mode, t, &connector->modes, head) | ||
489 | drm_mode_remove(connector, mode); | ||
490 | |||
491 | list_for_each_entry_safe(mode, t, &connector->user_modes, head) | ||
492 | drm_mode_remove(connector, mode); | ||
493 | |||
494 | mutex_lock(&dev->mode_config.mutex); | ||
495 | drm_mode_object_put(dev, &connector->base); | ||
496 | list_del(&connector->head); | ||
497 | mutex_unlock(&dev->mode_config.mutex); | ||
498 | } | ||
499 | EXPORT_SYMBOL(drm_connector_cleanup); | ||
500 | |||
501 | void drm_encoder_init(struct drm_device *dev, | ||
502 | struct drm_encoder *encoder, | ||
503 | const struct drm_encoder_funcs *funcs, | ||
504 | int encoder_type) | ||
505 | { | ||
506 | mutex_lock(&dev->mode_config.mutex); | ||
507 | |||
508 | encoder->dev = dev; | ||
509 | |||
510 | drm_mode_object_get(dev, &encoder->base, DRM_MODE_OBJECT_ENCODER); | ||
511 | encoder->encoder_type = encoder_type; | ||
512 | encoder->funcs = funcs; | ||
513 | |||
514 | list_add_tail(&encoder->head, &dev->mode_config.encoder_list); | ||
515 | dev->mode_config.num_encoder++; | ||
516 | |||
517 | mutex_unlock(&dev->mode_config.mutex); | ||
518 | } | ||
519 | EXPORT_SYMBOL(drm_encoder_init); | ||
520 | |||
521 | void drm_encoder_cleanup(struct drm_encoder *encoder) | ||
522 | { | ||
523 | struct drm_device *dev = encoder->dev; | ||
524 | mutex_lock(&dev->mode_config.mutex); | ||
525 | drm_mode_object_put(dev, &encoder->base); | ||
526 | list_del(&encoder->head); | ||
527 | mutex_unlock(&dev->mode_config.mutex); | ||
528 | } | ||
529 | EXPORT_SYMBOL(drm_encoder_cleanup); | ||
530 | |||
531 | /** | ||
532 | * drm_mode_create - create a new display mode | ||
533 | * @dev: DRM device | ||
534 | * | ||
535 | * LOCKING: | ||
536 | * Caller must hold DRM mode_config lock. | ||
537 | * | ||
538 | * Create a new drm_display_mode, give it an ID, and return it. | ||
539 | * | ||
540 | * RETURNS: | ||
541 | * Pointer to new mode on success, NULL on error. | ||
542 | */ | ||
543 | struct drm_display_mode *drm_mode_create(struct drm_device *dev) | ||
544 | { | ||
545 | struct drm_display_mode *nmode; | ||
546 | |||
547 | nmode = kzalloc(sizeof(struct drm_display_mode), GFP_KERNEL); | ||
548 | if (!nmode) | ||
549 | return NULL; | ||
550 | |||
551 | drm_mode_object_get(dev, &nmode->base, DRM_MODE_OBJECT_MODE); | ||
552 | return nmode; | ||
553 | } | ||
554 | EXPORT_SYMBOL(drm_mode_create); | ||
555 | |||
556 | /** | ||
557 | * drm_mode_destroy - remove a mode | ||
558 | * @dev: DRM device | ||
559 | * @mode: mode to remove | ||
560 | * | ||
561 | * LOCKING: | ||
562 | * Caller must hold mode config lock. | ||
563 | * | ||
564 | * Free @mode's unique identifier, then free it. | ||
565 | */ | ||
566 | void drm_mode_destroy(struct drm_device *dev, struct drm_display_mode *mode) | ||
567 | { | ||
568 | drm_mode_object_put(dev, &mode->base); | ||
569 | |||
570 | kfree(mode); | ||
571 | } | ||
572 | EXPORT_SYMBOL(drm_mode_destroy); | ||
573 | |||
574 | static int drm_mode_create_standard_connector_properties(struct drm_device *dev) | ||
575 | { | ||
576 | struct drm_property *edid; | ||
577 | struct drm_property *dpms; | ||
578 | int i; | ||
579 | |||
580 | /* | ||
581 | * Standard properties (apply to all connectors) | ||
582 | */ | ||
583 | edid = drm_property_create(dev, DRM_MODE_PROP_BLOB | | ||
584 | DRM_MODE_PROP_IMMUTABLE, | ||
585 | "EDID", 0); | ||
586 | dev->mode_config.edid_property = edid; | ||
587 | |||
588 | dpms = drm_property_create(dev, DRM_MODE_PROP_ENUM, | ||
589 | "DPMS", ARRAY_SIZE(drm_dpms_enum_list)); | ||
590 | for (i = 0; i < ARRAY_SIZE(drm_dpms_enum_list); i++) | ||
591 | drm_property_add_enum(dpms, i, drm_dpms_enum_list[i].type, | ||
592 | drm_dpms_enum_list[i].name); | ||
593 | dev->mode_config.dpms_property = dpms; | ||
594 | |||
595 | return 0; | ||
596 | } | ||
597 | |||
598 | /** | ||
599 | * drm_mode_create_dvi_i_properties - create DVI-I specific connector properties | ||
600 | * @dev: DRM device | ||
601 | * | ||
602 | * Called by a driver the first time a DVI-I connector is made. | ||
603 | */ | ||
604 | int drm_mode_create_dvi_i_properties(struct drm_device *dev) | ||
605 | { | ||
606 | struct drm_property *dvi_i_selector; | ||
607 | struct drm_property *dvi_i_subconnector; | ||
608 | int i; | ||
609 | |||
610 | if (dev->mode_config.dvi_i_select_subconnector_property) | ||
611 | return 0; | ||
612 | |||
613 | dvi_i_selector = | ||
614 | drm_property_create(dev, DRM_MODE_PROP_ENUM, | ||
615 | "select subconnector", | ||
616 | ARRAY_SIZE(drm_dvi_i_select_enum_list)); | ||
617 | for (i = 0; i < ARRAY_SIZE(drm_dvi_i_select_enum_list); i++) | ||
618 | drm_property_add_enum(dvi_i_selector, i, | ||
619 | drm_dvi_i_select_enum_list[i].type, | ||
620 | drm_dvi_i_select_enum_list[i].name); | ||
621 | dev->mode_config.dvi_i_select_subconnector_property = dvi_i_selector; | ||
622 | |||
623 | dvi_i_subconnector = | ||
624 | drm_property_create(dev, DRM_MODE_PROP_ENUM | | ||
625 | DRM_MODE_PROP_IMMUTABLE, | ||
626 | "subconnector", | ||
627 | ARRAY_SIZE(drm_dvi_i_subconnector_enum_list)); | ||
628 | for (i = 0; i < ARRAY_SIZE(drm_dvi_i_subconnector_enum_list); i++) | ||
629 | drm_property_add_enum(dvi_i_subconnector, i, | ||
630 | drm_dvi_i_subconnector_enum_list[i].type, | ||
631 | drm_dvi_i_subconnector_enum_list[i].name); | ||
632 | dev->mode_config.dvi_i_subconnector_property = dvi_i_subconnector; | ||
633 | |||
634 | return 0; | ||
635 | } | ||
636 | EXPORT_SYMBOL(drm_mode_create_dvi_i_properties); | ||
637 | |||
638 | /** | ||
639 | * drm_create_tv_properties - create TV specific connector properties | ||
640 | * @dev: DRM device | ||
641 | * @num_modes: number of different TV formats (modes) supported | ||
642 | * @modes: array of pointers to strings containing name of each format | ||
643 | * | ||
644 | * Called by a driver's TV initialization routine, this function creates | ||
645 | * the TV specific connector properties for a given device. Caller is | ||
646 | * responsible for allocating a list of format names and passing them to | ||
647 | * this routine. | ||
648 | */ | ||
649 | int drm_mode_create_tv_properties(struct drm_device *dev, int num_modes, | ||
650 | char *modes[]) | ||
651 | { | ||
652 | struct drm_property *tv_selector; | ||
653 | struct drm_property *tv_subconnector; | ||
654 | int i; | ||
655 | |||
656 | if (dev->mode_config.tv_select_subconnector_property) | ||
657 | return 0; | ||
658 | |||
659 | /* | ||
660 | * Basic connector properties | ||
661 | */ | ||
662 | tv_selector = drm_property_create(dev, DRM_MODE_PROP_ENUM, | ||
663 | "select subconnector", | ||
664 | ARRAY_SIZE(drm_tv_select_enum_list)); | ||
665 | for (i = 0; i < ARRAY_SIZE(drm_tv_select_enum_list); i++) | ||
666 | drm_property_add_enum(tv_selector, i, | ||
667 | drm_tv_select_enum_list[i].type, | ||
668 | drm_tv_select_enum_list[i].name); | ||
669 | dev->mode_config.tv_select_subconnector_property = tv_selector; | ||
670 | |||
671 | tv_subconnector = | ||
672 | drm_property_create(dev, DRM_MODE_PROP_ENUM | | ||
673 | DRM_MODE_PROP_IMMUTABLE, "subconnector", | ||
674 | ARRAY_SIZE(drm_tv_subconnector_enum_list)); | ||
675 | for (i = 0; i < ARRAY_SIZE(drm_tv_subconnector_enum_list); i++) | ||
676 | drm_property_add_enum(tv_subconnector, i, | ||
677 | drm_tv_subconnector_enum_list[i].type, | ||
678 | drm_tv_subconnector_enum_list[i].name); | ||
679 | dev->mode_config.tv_subconnector_property = tv_subconnector; | ||
680 | |||
681 | /* | ||
682 | * Other, TV specific properties: margins & TV modes. | ||
683 | */ | ||
684 | dev->mode_config.tv_left_margin_property = | ||
685 | drm_property_create(dev, DRM_MODE_PROP_RANGE, | ||
686 | "left margin", 2); | ||
687 | dev->mode_config.tv_left_margin_property->values[0] = 0; | ||
688 | dev->mode_config.tv_left_margin_property->values[1] = 100; | ||
689 | |||
690 | dev->mode_config.tv_right_margin_property = | ||
691 | drm_property_create(dev, DRM_MODE_PROP_RANGE, | ||
692 | "right margin", 2); | ||
693 | dev->mode_config.tv_right_margin_property->values[0] = 0; | ||
694 | dev->mode_config.tv_right_margin_property->values[1] = 100; | ||
695 | |||
696 | dev->mode_config.tv_top_margin_property = | ||
697 | drm_property_create(dev, DRM_MODE_PROP_RANGE, | ||
698 | "top margin", 2); | ||
699 | dev->mode_config.tv_top_margin_property->values[0] = 0; | ||
700 | dev->mode_config.tv_top_margin_property->values[1] = 100; | ||
701 | |||
702 | dev->mode_config.tv_bottom_margin_property = | ||
703 | drm_property_create(dev, DRM_MODE_PROP_RANGE, | ||
704 | "bottom margin", 2); | ||
705 | dev->mode_config.tv_bottom_margin_property->values[0] = 0; | ||
706 | dev->mode_config.tv_bottom_margin_property->values[1] = 100; | ||
707 | |||
708 | dev->mode_config.tv_mode_property = | ||
709 | drm_property_create(dev, DRM_MODE_PROP_ENUM, | ||
710 | "mode", num_modes); | ||
711 | for (i = 0; i < num_modes; i++) | ||
712 | drm_property_add_enum(dev->mode_config.tv_mode_property, i, | ||
713 | i, modes[i]); | ||
714 | |||
715 | return 0; | ||
716 | } | ||
717 | EXPORT_SYMBOL(drm_mode_create_tv_properties); | ||
718 | |||
719 | /** | ||
720 | * drm_mode_create_scaling_mode_property - create scaling mode property | ||
721 | * @dev: DRM device | ||
722 | * | ||
723 | * Called by a driver the first time it's needed, must be attached to desired | ||
724 | * connectors. | ||
725 | */ | ||
726 | int drm_mode_create_scaling_mode_property(struct drm_device *dev) | ||
727 | { | ||
728 | struct drm_property *scaling_mode; | ||
729 | int i; | ||
730 | |||
731 | if (dev->mode_config.scaling_mode_property) | ||
732 | return 0; | ||
733 | |||
734 | scaling_mode = | ||
735 | drm_property_create(dev, DRM_MODE_PROP_ENUM, "scaling mode", | ||
736 | ARRAY_SIZE(drm_scaling_mode_enum_list)); | ||
737 | for (i = 0; i < ARRAY_SIZE(drm_scaling_mode_enum_list); i++) | ||
738 | drm_property_add_enum(scaling_mode, i, | ||
739 | drm_scaling_mode_enum_list[i].type, | ||
740 | drm_scaling_mode_enum_list[i].name); | ||
741 | |||
742 | dev->mode_config.scaling_mode_property = scaling_mode; | ||
743 | |||
744 | return 0; | ||
745 | } | ||
746 | EXPORT_SYMBOL(drm_mode_create_scaling_mode_property); | ||
747 | |||
748 | /** | ||
749 | * drm_mode_create_dithering_property - create dithering property | ||
750 | * @dev: DRM device | ||
751 | * | ||
752 | * Called by a driver the first time it's needed, must be attached to desired | ||
753 | * connectors. | ||
754 | */ | ||
755 | int drm_mode_create_dithering_property(struct drm_device *dev) | ||
756 | { | ||
757 | struct drm_property *dithering_mode; | ||
758 | int i; | ||
759 | |||
760 | if (dev->mode_config.dithering_mode_property) | ||
761 | return 0; | ||
762 | |||
763 | dithering_mode = | ||
764 | drm_property_create(dev, DRM_MODE_PROP_ENUM, "dithering", | ||
765 | ARRAY_SIZE(drm_dithering_mode_enum_list)); | ||
766 | for (i = 0; i < ARRAY_SIZE(drm_dithering_mode_enum_list); i++) | ||
767 | drm_property_add_enum(dithering_mode, i, | ||
768 | drm_dithering_mode_enum_list[i].type, | ||
769 | drm_dithering_mode_enum_list[i].name); | ||
770 | dev->mode_config.dithering_mode_property = dithering_mode; | ||
771 | |||
772 | return 0; | ||
773 | } | ||
774 | EXPORT_SYMBOL(drm_mode_create_dithering_property); | ||
775 | |||
776 | /** | ||
777 | * drm_mode_config_init - initialize DRM mode_configuration structure | ||
778 | * @dev: DRM device | ||
779 | * | ||
780 | * LOCKING: | ||
781 | * None, should happen single threaded at init time. | ||
782 | * | ||
783 | * Initialize @dev's mode_config structure, used for tracking the graphics | ||
784 | * configuration of @dev. | ||
785 | */ | ||
786 | void drm_mode_config_init(struct drm_device *dev) | ||
787 | { | ||
788 | mutex_init(&dev->mode_config.mutex); | ||
789 | INIT_LIST_HEAD(&dev->mode_config.fb_list); | ||
790 | INIT_LIST_HEAD(&dev->mode_config.fb_kernel_list); | ||
791 | INIT_LIST_HEAD(&dev->mode_config.crtc_list); | ||
792 | INIT_LIST_HEAD(&dev->mode_config.connector_list); | ||
793 | INIT_LIST_HEAD(&dev->mode_config.encoder_list); | ||
794 | INIT_LIST_HEAD(&dev->mode_config.property_list); | ||
795 | INIT_LIST_HEAD(&dev->mode_config.property_blob_list); | ||
796 | idr_init(&dev->mode_config.crtc_idr); | ||
797 | |||
798 | mutex_lock(&dev->mode_config.mutex); | ||
799 | drm_mode_create_standard_connector_properties(dev); | ||
800 | mutex_unlock(&dev->mode_config.mutex); | ||
801 | |||
802 | /* Just to be sure */ | ||
803 | dev->mode_config.num_fb = 0; | ||
804 | dev->mode_config.num_connector = 0; | ||
805 | dev->mode_config.num_crtc = 0; | ||
806 | dev->mode_config.num_encoder = 0; | ||
807 | dev->mode_config.hotplug_counter = 0; | ||
808 | } | ||
809 | EXPORT_SYMBOL(drm_mode_config_init); | ||
810 | |||
811 | int drm_mode_group_init(struct drm_device *dev, struct drm_mode_group *group) | ||
812 | { | ||
813 | uint32_t total_objects = 0; | ||
814 | |||
815 | total_objects += dev->mode_config.num_crtc; | ||
816 | total_objects += dev->mode_config.num_connector; | ||
817 | total_objects += dev->mode_config.num_encoder; | ||
818 | |||
819 | if (total_objects == 0) | ||
820 | return -EINVAL; | ||
821 | |||
822 | group->id_list = kzalloc(total_objects * sizeof(uint32_t), GFP_KERNEL); | ||
823 | if (!group->id_list) | ||
824 | return -ENOMEM; | ||
825 | |||
826 | group->num_crtcs = 0; | ||
827 | group->num_connectors = 0; | ||
828 | group->num_encoders = 0; | ||
829 | return 0; | ||
830 | } | ||
831 | |||
832 | int drm_mode_group_init_legacy_group(struct drm_device *dev, | ||
833 | struct drm_mode_group *group) | ||
834 | { | ||
835 | struct drm_crtc *crtc; | ||
836 | struct drm_encoder *encoder; | ||
837 | struct drm_connector *connector; | ||
838 | int ret; | ||
839 | |||
840 | if ((ret = drm_mode_group_init(dev, group))) | ||
841 | return ret; | ||
842 | |||
843 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) | ||
844 | group->id_list[group->num_crtcs++] = crtc->base.id; | ||
845 | |||
846 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) | ||
847 | group->id_list[group->num_crtcs + group->num_encoders++] = | ||
848 | encoder->base.id; | ||
849 | |||
850 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) | ||
851 | group->id_list[group->num_crtcs + group->num_encoders + | ||
852 | group->num_connectors++] = connector->base.id; | ||
853 | |||
854 | return 0; | ||
855 | } | ||
856 | |||
857 | /** | ||
858 | * drm_mode_config_cleanup - free up DRM mode_config info | ||
859 | * @dev: DRM device | ||
860 | * | ||
861 | * LOCKING: | ||
862 | * Caller must hold mode config lock. | ||
863 | * | ||
864 | * Free up all the connectors and CRTCs associated with this DRM device, then | ||
865 | * free up the framebuffers and associated buffer objects. | ||
866 | * | ||
867 | * FIXME: cleanup any dangling user buffer objects too | ||
868 | */ | ||
869 | void drm_mode_config_cleanup(struct drm_device *dev) | ||
870 | { | ||
871 | struct drm_connector *connector, *ot; | ||
872 | struct drm_crtc *crtc, *ct; | ||
873 | struct drm_encoder *encoder, *enct; | ||
874 | struct drm_framebuffer *fb, *fbt; | ||
875 | struct drm_property *property, *pt; | ||
876 | |||
877 | list_for_each_entry_safe(encoder, enct, &dev->mode_config.encoder_list, | ||
878 | head) { | ||
879 | encoder->funcs->destroy(encoder); | ||
880 | } | ||
881 | |||
882 | list_for_each_entry_safe(connector, ot, | ||
883 | &dev->mode_config.connector_list, head) { | ||
884 | connector->funcs->destroy(connector); | ||
885 | } | ||
886 | |||
887 | list_for_each_entry_safe(property, pt, &dev->mode_config.property_list, | ||
888 | head) { | ||
889 | drm_property_destroy(dev, property); | ||
890 | } | ||
891 | |||
892 | list_for_each_entry_safe(fb, fbt, &dev->mode_config.fb_list, head) { | ||
893 | fb->funcs->destroy(fb); | ||
894 | } | ||
895 | |||
896 | list_for_each_entry_safe(crtc, ct, &dev->mode_config.crtc_list, head) { | ||
897 | crtc->funcs->destroy(crtc); | ||
898 | } | ||
899 | |||
900 | } | ||
901 | EXPORT_SYMBOL(drm_mode_config_cleanup); | ||
902 | |||
903 | int drm_mode_hotplug_ioctl(struct drm_device *dev, | ||
904 | void *data, struct drm_file *file_priv) | ||
905 | { | ||
906 | struct drm_mode_hotplug *arg = data; | ||
907 | |||
908 | arg->counter = dev->mode_config.hotplug_counter; | ||
909 | |||
910 | return 0; | ||
911 | } | ||
912 | |||
913 | /** | ||
914 | * drm_crtc_convert_to_umode - convert a drm_display_mode into a modeinfo | ||
915 | * @out: drm_mode_modeinfo struct to return to the user | ||
916 | * @in: drm_display_mode to use | ||
917 | * | ||
918 | * LOCKING: | ||
919 | * None. | ||
920 | * | ||
921 | * Convert a drm_display_mode into a drm_mode_modeinfo structure to return to | ||
922 | * the user. | ||
923 | */ | ||
924 | void drm_crtc_convert_to_umode(struct drm_mode_modeinfo *out, | ||
925 | struct drm_display_mode *in) | ||
926 | { | ||
927 | out->clock = in->clock; | ||
928 | out->hdisplay = in->hdisplay; | ||
929 | out->hsync_start = in->hsync_start; | ||
930 | out->hsync_end = in->hsync_end; | ||
931 | out->htotal = in->htotal; | ||
932 | out->hskew = in->hskew; | ||
933 | out->vdisplay = in->vdisplay; | ||
934 | out->vsync_start = in->vsync_start; | ||
935 | out->vsync_end = in->vsync_end; | ||
936 | out->vtotal = in->vtotal; | ||
937 | out->vscan = in->vscan; | ||
938 | out->vrefresh = in->vrefresh; | ||
939 | out->flags = in->flags; | ||
940 | out->type = in->type; | ||
941 | strncpy(out->name, in->name, DRM_DISPLAY_MODE_LEN); | ||
942 | out->name[DRM_DISPLAY_MODE_LEN-1] = 0; | ||
943 | } | ||
944 | |||
945 | /** | ||
946 | * drm_crtc_convert_to_umode - convert a modeinfo into a drm_display_mode | ||
947 | * @out: drm_display_mode to return to the user | ||
948 | * @in: drm_mode_modeinfo to use | ||
949 | * | ||
950 | * LOCKING: | ||
951 | * None. | ||
952 | * | ||
953 | * Convert a drm_mode_modeinfo into a drm_display_mode structure to return to | ||
954 | * the caller. | ||
955 | */ | ||
956 | void drm_crtc_convert_umode(struct drm_display_mode *out, | ||
957 | struct drm_mode_modeinfo *in) | ||
958 | { | ||
959 | out->clock = in->clock; | ||
960 | out->hdisplay = in->hdisplay; | ||
961 | out->hsync_start = in->hsync_start; | ||
962 | out->hsync_end = in->hsync_end; | ||
963 | out->htotal = in->htotal; | ||
964 | out->hskew = in->hskew; | ||
965 | out->vdisplay = in->vdisplay; | ||
966 | out->vsync_start = in->vsync_start; | ||
967 | out->vsync_end = in->vsync_end; | ||
968 | out->vtotal = in->vtotal; | ||
969 | out->vscan = in->vscan; | ||
970 | out->vrefresh = in->vrefresh; | ||
971 | out->flags = in->flags; | ||
972 | out->type = in->type; | ||
973 | strncpy(out->name, in->name, DRM_DISPLAY_MODE_LEN); | ||
974 | out->name[DRM_DISPLAY_MODE_LEN-1] = 0; | ||
975 | } | ||
976 | |||
977 | /** | ||
978 | * drm_mode_getresources - get graphics configuration | ||
979 | * @inode: inode from the ioctl | ||
980 | * @filp: file * from the ioctl | ||
981 | * @cmd: cmd from ioctl | ||
982 | * @arg: arg from ioctl | ||
983 | * | ||
984 | * LOCKING: | ||
985 | * Takes mode config lock. | ||
986 | * | ||
987 | * Construct a set of configuration description structures and return | ||
988 | * them to the user, including CRTC, connector and framebuffer configuration. | ||
989 | * | ||
990 | * Called by the user via ioctl. | ||
991 | * | ||
992 | * RETURNS: | ||
993 | * Zero on success, errno on failure. | ||
994 | */ | ||
995 | int drm_mode_getresources(struct drm_device *dev, void *data, | ||
996 | struct drm_file *file_priv) | ||
997 | { | ||
998 | struct drm_mode_card_res *card_res = data; | ||
999 | struct list_head *lh; | ||
1000 | struct drm_framebuffer *fb; | ||
1001 | struct drm_connector *connector; | ||
1002 | struct drm_crtc *crtc; | ||
1003 | struct drm_encoder *encoder; | ||
1004 | int ret = 0; | ||
1005 | int connector_count = 0; | ||
1006 | int crtc_count = 0; | ||
1007 | int fb_count = 0; | ||
1008 | int encoder_count = 0; | ||
1009 | int copied = 0, i; | ||
1010 | uint32_t __user *fb_id; | ||
1011 | uint32_t __user *crtc_id; | ||
1012 | uint32_t __user *connector_id; | ||
1013 | uint32_t __user *encoder_id; | ||
1014 | struct drm_mode_group *mode_group; | ||
1015 | |||
1016 | mutex_lock(&dev->mode_config.mutex); | ||
1017 | |||
1018 | /* | ||
1019 | * For the non-control nodes we need to limit the list of resources | ||
1020 | * by IDs in the group list for this node | ||
1021 | */ | ||
1022 | list_for_each(lh, &file_priv->fbs) | ||
1023 | fb_count++; | ||
1024 | |||
1025 | mode_group = &file_priv->master->minor->mode_group; | ||
1026 | if (file_priv->master->minor->type == DRM_MINOR_CONTROL) { | ||
1027 | |||
1028 | list_for_each(lh, &dev->mode_config.crtc_list) | ||
1029 | crtc_count++; | ||
1030 | |||
1031 | list_for_each(lh, &dev->mode_config.connector_list) | ||
1032 | connector_count++; | ||
1033 | |||
1034 | list_for_each(lh, &dev->mode_config.encoder_list) | ||
1035 | encoder_count++; | ||
1036 | } else { | ||
1037 | |||
1038 | crtc_count = mode_group->num_crtcs; | ||
1039 | connector_count = mode_group->num_connectors; | ||
1040 | encoder_count = mode_group->num_encoders; | ||
1041 | } | ||
1042 | |||
1043 | card_res->max_height = dev->mode_config.max_height; | ||
1044 | card_res->min_height = dev->mode_config.min_height; | ||
1045 | card_res->max_width = dev->mode_config.max_width; | ||
1046 | card_res->min_width = dev->mode_config.min_width; | ||
1047 | |||
1048 | /* handle this in 4 parts */ | ||
1049 | /* FBs */ | ||
1050 | if (card_res->count_fbs >= fb_count) { | ||
1051 | copied = 0; | ||
1052 | fb_id = (uint32_t __user *)(unsigned long)card_res->fb_id_ptr; | ||
1053 | list_for_each_entry(fb, &file_priv->fbs, head) { | ||
1054 | if (put_user(fb->base.id, fb_id + copied)) { | ||
1055 | ret = -EFAULT; | ||
1056 | goto out; | ||
1057 | } | ||
1058 | copied++; | ||
1059 | } | ||
1060 | } | ||
1061 | card_res->count_fbs = fb_count; | ||
1062 | |||
1063 | /* CRTCs */ | ||
1064 | if (card_res->count_crtcs >= crtc_count) { | ||
1065 | copied = 0; | ||
1066 | crtc_id = (uint32_t __user *)(unsigned long)card_res->crtc_id_ptr; | ||
1067 | if (file_priv->master->minor->type == DRM_MINOR_CONTROL) { | ||
1068 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, | ||
1069 | head) { | ||
1070 | DRM_DEBUG("CRTC ID is %d\n", crtc->base.id); | ||
1071 | if (put_user(crtc->base.id, crtc_id + copied)) { | ||
1072 | ret = -EFAULT; | ||
1073 | goto out; | ||
1074 | } | ||
1075 | copied++; | ||
1076 | } | ||
1077 | } else { | ||
1078 | for (i = 0; i < mode_group->num_crtcs; i++) { | ||
1079 | if (put_user(mode_group->id_list[i], | ||
1080 | crtc_id + copied)) { | ||
1081 | ret = -EFAULT; | ||
1082 | goto out; | ||
1083 | } | ||
1084 | copied++; | ||
1085 | } | ||
1086 | } | ||
1087 | } | ||
1088 | card_res->count_crtcs = crtc_count; | ||
1089 | |||
1090 | /* Encoders */ | ||
1091 | if (card_res->count_encoders >= encoder_count) { | ||
1092 | copied = 0; | ||
1093 | encoder_id = (uint32_t __user *)(unsigned long)card_res->encoder_id_ptr; | ||
1094 | if (file_priv->master->minor->type == DRM_MINOR_CONTROL) { | ||
1095 | list_for_each_entry(encoder, | ||
1096 | &dev->mode_config.encoder_list, | ||
1097 | head) { | ||
1098 | DRM_DEBUG("ENCODER ID is %d\n", | ||
1099 | encoder->base.id); | ||
1100 | if (put_user(encoder->base.id, encoder_id + | ||
1101 | copied)) { | ||
1102 | ret = -EFAULT; | ||
1103 | goto out; | ||
1104 | } | ||
1105 | copied++; | ||
1106 | } | ||
1107 | } else { | ||
1108 | for (i = mode_group->num_crtcs; i < mode_group->num_crtcs + mode_group->num_encoders; i++) { | ||
1109 | if (put_user(mode_group->id_list[i], | ||
1110 | encoder_id + copied)) { | ||
1111 | ret = -EFAULT; | ||
1112 | goto out; | ||
1113 | } | ||
1114 | copied++; | ||
1115 | } | ||
1116 | |||
1117 | } | ||
1118 | } | ||
1119 | card_res->count_encoders = encoder_count; | ||
1120 | |||
1121 | /* Connectors */ | ||
1122 | if (card_res->count_connectors >= connector_count) { | ||
1123 | copied = 0; | ||
1124 | connector_id = (uint32_t __user *)(unsigned long)card_res->connector_id_ptr; | ||
1125 | if (file_priv->master->minor->type == DRM_MINOR_CONTROL) { | ||
1126 | list_for_each_entry(connector, | ||
1127 | &dev->mode_config.connector_list, | ||
1128 | head) { | ||
1129 | DRM_DEBUG("CONNECTOR ID is %d\n", | ||
1130 | connector->base.id); | ||
1131 | if (put_user(connector->base.id, | ||
1132 | connector_id + copied)) { | ||
1133 | ret = -EFAULT; | ||
1134 | goto out; | ||
1135 | } | ||
1136 | copied++; | ||
1137 | } | ||
1138 | } else { | ||
1139 | int start = mode_group->num_crtcs + | ||
1140 | mode_group->num_encoders; | ||
1141 | for (i = start; i < start + mode_group->num_connectors; i++) { | ||
1142 | if (put_user(mode_group->id_list[i], | ||
1143 | connector_id + copied)) { | ||
1144 | ret = -EFAULT; | ||
1145 | goto out; | ||
1146 | } | ||
1147 | copied++; | ||
1148 | } | ||
1149 | } | ||
1150 | } | ||
1151 | card_res->count_connectors = connector_count; | ||
1152 | |||
1153 | DRM_DEBUG("Counted %d %d %d\n", card_res->count_crtcs, | ||
1154 | card_res->count_connectors, card_res->count_encoders); | ||
1155 | |||
1156 | out: | ||
1157 | mutex_unlock(&dev->mode_config.mutex); | ||
1158 | return ret; | ||
1159 | } | ||
1160 | |||
1161 | /** | ||
1162 | * drm_mode_getcrtc - get CRTC configuration | ||
1163 | * @inode: inode from the ioctl | ||
1164 | * @filp: file * from the ioctl | ||
1165 | * @cmd: cmd from ioctl | ||
1166 | * @arg: arg from ioctl | ||
1167 | * | ||
1168 | * LOCKING: | ||
1169 | * Caller? (FIXME) | ||
1170 | * | ||
1171 | * Construct a CRTC configuration structure to return to the user. | ||
1172 | * | ||
1173 | * Called by the user via ioctl. | ||
1174 | * | ||
1175 | * RETURNS: | ||
1176 | * Zero on success, errno on failure. | ||
1177 | */ | ||
1178 | int drm_mode_getcrtc(struct drm_device *dev, | ||
1179 | void *data, struct drm_file *file_priv) | ||
1180 | { | ||
1181 | struct drm_mode_crtc *crtc_resp = data; | ||
1182 | struct drm_crtc *crtc; | ||
1183 | struct drm_mode_object *obj; | ||
1184 | int ret = 0; | ||
1185 | |||
1186 | mutex_lock(&dev->mode_config.mutex); | ||
1187 | |||
1188 | obj = drm_mode_object_find(dev, crtc_resp->crtc_id, | ||
1189 | DRM_MODE_OBJECT_CRTC); | ||
1190 | if (!obj) { | ||
1191 | ret = -EINVAL; | ||
1192 | goto out; | ||
1193 | } | ||
1194 | crtc = obj_to_crtc(obj); | ||
1195 | |||
1196 | crtc_resp->x = crtc->x; | ||
1197 | crtc_resp->y = crtc->y; | ||
1198 | crtc_resp->gamma_size = crtc->gamma_size; | ||
1199 | if (crtc->fb) | ||
1200 | crtc_resp->fb_id = crtc->fb->base.id; | ||
1201 | else | ||
1202 | crtc_resp->fb_id = 0; | ||
1203 | |||
1204 | if (crtc->enabled) { | ||
1205 | |||
1206 | drm_crtc_convert_to_umode(&crtc_resp->mode, &crtc->mode); | ||
1207 | crtc_resp->mode_valid = 1; | ||
1208 | |||
1209 | } else { | ||
1210 | crtc_resp->mode_valid = 0; | ||
1211 | } | ||
1212 | |||
1213 | out: | ||
1214 | mutex_unlock(&dev->mode_config.mutex); | ||
1215 | return ret; | ||
1216 | } | ||
1217 | |||
1218 | /** | ||
1219 | * drm_mode_getconnector - get connector configuration | ||
1220 | * @inode: inode from the ioctl | ||
1221 | * @filp: file * from the ioctl | ||
1222 | * @cmd: cmd from ioctl | ||
1223 | * @arg: arg from ioctl | ||
1224 | * | ||
1225 | * LOCKING: | ||
1226 | * Caller? (FIXME) | ||
1227 | * | ||
1228 | * Construct a connector configuration structure to return to the user. | ||
1229 | * | ||
1230 | * Called by the user via ioctl. | ||
1231 | * | ||
1232 | * RETURNS: | ||
1233 | * Zero on success, errno on failure. | ||
1234 | */ | ||
1235 | int drm_mode_getconnector(struct drm_device *dev, void *data, | ||
1236 | struct drm_file *file_priv) | ||
1237 | { | ||
1238 | struct drm_mode_get_connector *out_resp = data; | ||
1239 | struct drm_mode_object *obj; | ||
1240 | struct drm_connector *connector; | ||
1241 | struct drm_display_mode *mode; | ||
1242 | int mode_count = 0; | ||
1243 | int props_count = 0; | ||
1244 | int encoders_count = 0; | ||
1245 | int ret = 0; | ||
1246 | int copied = 0; | ||
1247 | int i; | ||
1248 | struct drm_mode_modeinfo u_mode; | ||
1249 | struct drm_mode_modeinfo __user *mode_ptr; | ||
1250 | uint32_t __user *prop_ptr; | ||
1251 | uint64_t __user *prop_values; | ||
1252 | uint32_t __user *encoder_ptr; | ||
1253 | |||
1254 | memset(&u_mode, 0, sizeof(struct drm_mode_modeinfo)); | ||
1255 | |||
1256 | DRM_DEBUG("connector id %d:\n", out_resp->connector_id); | ||
1257 | |||
1258 | mutex_lock(&dev->mode_config.mutex); | ||
1259 | |||
1260 | obj = drm_mode_object_find(dev, out_resp->connector_id, | ||
1261 | DRM_MODE_OBJECT_CONNECTOR); | ||
1262 | if (!obj) { | ||
1263 | ret = -EINVAL; | ||
1264 | goto out; | ||
1265 | } | ||
1266 | connector = obj_to_connector(obj); | ||
1267 | |||
1268 | for (i = 0; i < DRM_CONNECTOR_MAX_PROPERTY; i++) { | ||
1269 | if (connector->property_ids[i] != 0) { | ||
1270 | props_count++; | ||
1271 | } | ||
1272 | } | ||
1273 | |||
1274 | for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { | ||
1275 | if (connector->encoder_ids[i] != 0) { | ||
1276 | encoders_count++; | ||
1277 | } | ||
1278 | } | ||
1279 | |||
1280 | if (out_resp->count_modes == 0) { | ||
1281 | connector->funcs->fill_modes(connector, | ||
1282 | dev->mode_config.max_width, | ||
1283 | dev->mode_config.max_height); | ||
1284 | } | ||
1285 | |||
1286 | /* delayed so we get modes regardless of pre-fill_modes state */ | ||
1287 | list_for_each_entry(mode, &connector->modes, head) | ||
1288 | mode_count++; | ||
1289 | |||
1290 | out_resp->connector_id = connector->base.id; | ||
1291 | out_resp->connector_type = connector->connector_type; | ||
1292 | out_resp->connector_type_id = connector->connector_type_id; | ||
1293 | out_resp->mm_width = connector->display_info.width_mm; | ||
1294 | out_resp->mm_height = connector->display_info.height_mm; | ||
1295 | out_resp->subpixel = connector->display_info.subpixel_order; | ||
1296 | out_resp->connection = connector->status; | ||
1297 | if (connector->encoder) | ||
1298 | out_resp->encoder_id = connector->encoder->base.id; | ||
1299 | else | ||
1300 | out_resp->encoder_id = 0; | ||
1301 | |||
1302 | /* | ||
1303 | * This ioctl is called twice, once to determine how much space is | ||
1304 | * needed, and the 2nd time to fill it. | ||
1305 | */ | ||
1306 | if ((out_resp->count_modes >= mode_count) && mode_count) { | ||
1307 | copied = 0; | ||
1308 | mode_ptr = (struct drm_mode_modeinfo *)(unsigned long)out_resp->modes_ptr; | ||
1309 | list_for_each_entry(mode, &connector->modes, head) { | ||
1310 | drm_crtc_convert_to_umode(&u_mode, mode); | ||
1311 | if (copy_to_user(mode_ptr + copied, | ||
1312 | &u_mode, sizeof(u_mode))) { | ||
1313 | ret = -EFAULT; | ||
1314 | goto out; | ||
1315 | } | ||
1316 | copied++; | ||
1317 | } | ||
1318 | } | ||
1319 | out_resp->count_modes = mode_count; | ||
1320 | |||
1321 | if ((out_resp->count_props >= props_count) && props_count) { | ||
1322 | copied = 0; | ||
1323 | prop_ptr = (uint32_t *)(unsigned long)(out_resp->props_ptr); | ||
1324 | prop_values = (uint64_t *)(unsigned long)(out_resp->prop_values_ptr); | ||
1325 | for (i = 0; i < DRM_CONNECTOR_MAX_PROPERTY; i++) { | ||
1326 | if (connector->property_ids[i] != 0) { | ||
1327 | if (put_user(connector->property_ids[i], | ||
1328 | prop_ptr + copied)) { | ||
1329 | ret = -EFAULT; | ||
1330 | goto out; | ||
1331 | } | ||
1332 | |||
1333 | if (put_user(connector->property_values[i], | ||
1334 | prop_values + copied)) { | ||
1335 | ret = -EFAULT; | ||
1336 | goto out; | ||
1337 | } | ||
1338 | copied++; | ||
1339 | } | ||
1340 | } | ||
1341 | } | ||
1342 | out_resp->count_props = props_count; | ||
1343 | |||
1344 | if ((out_resp->count_encoders >= encoders_count) && encoders_count) { | ||
1345 | copied = 0; | ||
1346 | encoder_ptr = (uint32_t *)(unsigned long)(out_resp->encoders_ptr); | ||
1347 | for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { | ||
1348 | if (connector->encoder_ids[i] != 0) { | ||
1349 | if (put_user(connector->encoder_ids[i], | ||
1350 | encoder_ptr + copied)) { | ||
1351 | ret = -EFAULT; | ||
1352 | goto out; | ||
1353 | } | ||
1354 | copied++; | ||
1355 | } | ||
1356 | } | ||
1357 | } | ||
1358 | out_resp->count_encoders = encoders_count; | ||
1359 | |||
1360 | out: | ||
1361 | mutex_unlock(&dev->mode_config.mutex); | ||
1362 | return ret; | ||
1363 | } | ||
1364 | |||
1365 | int drm_mode_getencoder(struct drm_device *dev, void *data, | ||
1366 | struct drm_file *file_priv) | ||
1367 | { | ||
1368 | struct drm_mode_get_encoder *enc_resp = data; | ||
1369 | struct drm_mode_object *obj; | ||
1370 | struct drm_encoder *encoder; | ||
1371 | int ret = 0; | ||
1372 | |||
1373 | mutex_lock(&dev->mode_config.mutex); | ||
1374 | obj = drm_mode_object_find(dev, enc_resp->encoder_id, | ||
1375 | DRM_MODE_OBJECT_ENCODER); | ||
1376 | if (!obj) { | ||
1377 | ret = -EINVAL; | ||
1378 | goto out; | ||
1379 | } | ||
1380 | encoder = obj_to_encoder(obj); | ||
1381 | |||
1382 | if (encoder->crtc) | ||
1383 | enc_resp->crtc_id = encoder->crtc->base.id; | ||
1384 | else | ||
1385 | enc_resp->crtc_id = 0; | ||
1386 | enc_resp->encoder_type = encoder->encoder_type; | ||
1387 | enc_resp->encoder_id = encoder->base.id; | ||
1388 | enc_resp->possible_crtcs = encoder->possible_crtcs; | ||
1389 | enc_resp->possible_clones = encoder->possible_clones; | ||
1390 | |||
1391 | out: | ||
1392 | mutex_unlock(&dev->mode_config.mutex); | ||
1393 | return ret; | ||
1394 | } | ||
1395 | |||
1396 | /** | ||
1397 | * drm_mode_setcrtc - set CRTC configuration | ||
1398 | * @inode: inode from the ioctl | ||
1399 | * @filp: file * from the ioctl | ||
1400 | * @cmd: cmd from ioctl | ||
1401 | * @arg: arg from ioctl | ||
1402 | * | ||
1403 | * LOCKING: | ||
1404 | * Caller? (FIXME) | ||
1405 | * | ||
1406 | * Build a new CRTC configuration based on user request. | ||
1407 | * | ||
1408 | * Called by the user via ioctl. | ||
1409 | * | ||
1410 | * RETURNS: | ||
1411 | * Zero on success, errno on failure. | ||
1412 | */ | ||
1413 | int drm_mode_setcrtc(struct drm_device *dev, void *data, | ||
1414 | struct drm_file *file_priv) | ||
1415 | { | ||
1416 | struct drm_mode_config *config = &dev->mode_config; | ||
1417 | struct drm_mode_crtc *crtc_req = data; | ||
1418 | struct drm_mode_object *obj; | ||
1419 | struct drm_crtc *crtc, *crtcfb; | ||
1420 | struct drm_connector **connector_set = NULL, *connector; | ||
1421 | struct drm_framebuffer *fb = NULL; | ||
1422 | struct drm_display_mode *mode = NULL; | ||
1423 | struct drm_mode_set set; | ||
1424 | uint32_t __user *set_connectors_ptr; | ||
1425 | int ret = 0; | ||
1426 | int i; | ||
1427 | |||
1428 | mutex_lock(&dev->mode_config.mutex); | ||
1429 | obj = drm_mode_object_find(dev, crtc_req->crtc_id, | ||
1430 | DRM_MODE_OBJECT_CRTC); | ||
1431 | if (!obj) { | ||
1432 | DRM_DEBUG("Unknown CRTC ID %d\n", crtc_req->crtc_id); | ||
1433 | ret = -EINVAL; | ||
1434 | goto out; | ||
1435 | } | ||
1436 | crtc = obj_to_crtc(obj); | ||
1437 | |||
1438 | if (crtc_req->mode_valid) { | ||
1439 | /* If we have a mode we need a framebuffer. */ | ||
1440 | /* If we pass -1, set the mode with the currently bound fb */ | ||
1441 | if (crtc_req->fb_id == -1) { | ||
1442 | list_for_each_entry(crtcfb, | ||
1443 | &dev->mode_config.crtc_list, head) { | ||
1444 | if (crtcfb == crtc) { | ||
1445 | DRM_DEBUG("Using current fb for setmode\n"); | ||
1446 | fb = crtc->fb; | ||
1447 | } | ||
1448 | } | ||
1449 | } else { | ||
1450 | obj = drm_mode_object_find(dev, crtc_req->fb_id, | ||
1451 | DRM_MODE_OBJECT_FB); | ||
1452 | if (!obj) { | ||
1453 | DRM_DEBUG("Unknown FB ID%d\n", crtc_req->fb_id); | ||
1454 | ret = -EINVAL; | ||
1455 | goto out; | ||
1456 | } | ||
1457 | fb = obj_to_fb(obj); | ||
1458 | } | ||
1459 | |||
1460 | mode = drm_mode_create(dev); | ||
1461 | drm_crtc_convert_umode(mode, &crtc_req->mode); | ||
1462 | drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V); | ||
1463 | } | ||
1464 | |||
1465 | if (crtc_req->count_connectors == 0 && mode) { | ||
1466 | DRM_DEBUG("Count connectors is 0 but mode set\n"); | ||
1467 | ret = -EINVAL; | ||
1468 | goto out; | ||
1469 | } | ||
1470 | |||
1471 | if (crtc_req->count_connectors > 0 && !mode && !fb) { | ||
1472 | DRM_DEBUG("Count connectors is %d but no mode or fb set\n", | ||
1473 | crtc_req->count_connectors); | ||
1474 | ret = -EINVAL; | ||
1475 | goto out; | ||
1476 | } | ||
1477 | |||
1478 | if (crtc_req->count_connectors > 0) { | ||
1479 | u32 out_id; | ||
1480 | |||
1481 | /* Avoid unbounded kernel memory allocation */ | ||
1482 | if (crtc_req->count_connectors > config->num_connector) { | ||
1483 | ret = -EINVAL; | ||
1484 | goto out; | ||
1485 | } | ||
1486 | |||
1487 | connector_set = kmalloc(crtc_req->count_connectors * | ||
1488 | sizeof(struct drm_connector *), | ||
1489 | GFP_KERNEL); | ||
1490 | if (!connector_set) { | ||
1491 | ret = -ENOMEM; | ||
1492 | goto out; | ||
1493 | } | ||
1494 | |||
1495 | for (i = 0; i < crtc_req->count_connectors; i++) { | ||
1496 | set_connectors_ptr = (uint32_t *)(unsigned long)crtc_req->set_connectors_ptr; | ||
1497 | if (get_user(out_id, &set_connectors_ptr[i])) { | ||
1498 | ret = -EFAULT; | ||
1499 | goto out; | ||
1500 | } | ||
1501 | |||
1502 | obj = drm_mode_object_find(dev, out_id, | ||
1503 | DRM_MODE_OBJECT_CONNECTOR); | ||
1504 | if (!obj) { | ||
1505 | DRM_DEBUG("Connector id %d unknown\n", out_id); | ||
1506 | ret = -EINVAL; | ||
1507 | goto out; | ||
1508 | } | ||
1509 | connector = obj_to_connector(obj); | ||
1510 | |||
1511 | connector_set[i] = connector; | ||
1512 | } | ||
1513 | } | ||
1514 | |||
1515 | set.crtc = crtc; | ||
1516 | set.x = crtc_req->x; | ||
1517 | set.y = crtc_req->y; | ||
1518 | set.mode = mode; | ||
1519 | set.connectors = connector_set; | ||
1520 | set.num_connectors = crtc_req->count_connectors; | ||
1521 | set.fb =fb; | ||
1522 | ret = crtc->funcs->set_config(&set); | ||
1523 | |||
1524 | out: | ||
1525 | kfree(connector_set); | ||
1526 | mutex_unlock(&dev->mode_config.mutex); | ||
1527 | return ret; | ||
1528 | } | ||
1529 | |||
1530 | int drm_mode_cursor_ioctl(struct drm_device *dev, | ||
1531 | void *data, struct drm_file *file_priv) | ||
1532 | { | ||
1533 | struct drm_mode_cursor *req = data; | ||
1534 | struct drm_mode_object *obj; | ||
1535 | struct drm_crtc *crtc; | ||
1536 | int ret = 0; | ||
1537 | |||
1538 | DRM_DEBUG("\n"); | ||
1539 | |||
1540 | if (!req->flags) { | ||
1541 | DRM_ERROR("no operation set\n"); | ||
1542 | return -EINVAL; | ||
1543 | } | ||
1544 | |||
1545 | mutex_lock(&dev->mode_config.mutex); | ||
1546 | obj = drm_mode_object_find(dev, req->crtc, DRM_MODE_OBJECT_CRTC); | ||
1547 | if (!obj) { | ||
1548 | DRM_DEBUG("Unknown CRTC ID %d\n", req->crtc); | ||
1549 | ret = -EINVAL; | ||
1550 | goto out; | ||
1551 | } | ||
1552 | crtc = obj_to_crtc(obj); | ||
1553 | |||
1554 | if (req->flags & DRM_MODE_CURSOR_BO) { | ||
1555 | if (!crtc->funcs->cursor_set) { | ||
1556 | DRM_ERROR("crtc does not support cursor\n"); | ||
1557 | ret = -ENXIO; | ||
1558 | goto out; | ||
1559 | } | ||
1560 | /* Turns off the cursor if handle is 0 */ | ||
1561 | ret = crtc->funcs->cursor_set(crtc, file_priv, req->handle, | ||
1562 | req->width, req->height); | ||
1563 | } | ||
1564 | |||
1565 | if (req->flags & DRM_MODE_CURSOR_MOVE) { | ||
1566 | if (crtc->funcs->cursor_move) { | ||
1567 | ret = crtc->funcs->cursor_move(crtc, req->x, req->y); | ||
1568 | } else { | ||
1569 | DRM_ERROR("crtc does not support cursor\n"); | ||
1570 | ret = -EFAULT; | ||
1571 | goto out; | ||
1572 | } | ||
1573 | } | ||
1574 | out: | ||
1575 | mutex_unlock(&dev->mode_config.mutex); | ||
1576 | return ret; | ||
1577 | } | ||
1578 | |||
1579 | /** | ||
1580 | * drm_mode_addfb - add an FB to the graphics configuration | ||
1581 | * @inode: inode from the ioctl | ||
1582 | * @filp: file * from the ioctl | ||
1583 | * @cmd: cmd from ioctl | ||
1584 | * @arg: arg from ioctl | ||
1585 | * | ||
1586 | * LOCKING: | ||
1587 | * Takes mode config lock. | ||
1588 | * | ||
1589 | * Add a new FB to the specified CRTC, given a user request. | ||
1590 | * | ||
1591 | * Called by the user via ioctl. | ||
1592 | * | ||
1593 | * RETURNS: | ||
1594 | * Zero on success, errno on failure. | ||
1595 | */ | ||
1596 | int drm_mode_addfb(struct drm_device *dev, | ||
1597 | void *data, struct drm_file *file_priv) | ||
1598 | { | ||
1599 | struct drm_mode_fb_cmd *r = data; | ||
1600 | struct drm_mode_config *config = &dev->mode_config; | ||
1601 | struct drm_framebuffer *fb; | ||
1602 | int ret = 0; | ||
1603 | |||
1604 | if ((config->min_width > r->width) || (r->width > config->max_width)) { | ||
1605 | DRM_ERROR("mode new framebuffer width not within limits\n"); | ||
1606 | return -EINVAL; | ||
1607 | } | ||
1608 | if ((config->min_height > r->height) || (r->height > config->max_height)) { | ||
1609 | DRM_ERROR("mode new framebuffer height not within limits\n"); | ||
1610 | return -EINVAL; | ||
1611 | } | ||
1612 | |||
1613 | mutex_lock(&dev->mode_config.mutex); | ||
1614 | |||
1615 | /* TODO check buffer is sufficently large */ | ||
1616 | /* TODO setup destructor callback */ | ||
1617 | |||
1618 | fb = dev->mode_config.funcs->fb_create(dev, file_priv, r); | ||
1619 | if (!fb) { | ||
1620 | DRM_ERROR("could not create framebuffer\n"); | ||
1621 | ret = -EINVAL; | ||
1622 | goto out; | ||
1623 | } | ||
1624 | |||
1625 | r->buffer_id = fb->base.id; | ||
1626 | list_add(&fb->filp_head, &file_priv->fbs); | ||
1627 | |||
1628 | out: | ||
1629 | mutex_unlock(&dev->mode_config.mutex); | ||
1630 | return ret; | ||
1631 | } | ||
1632 | |||
1633 | /** | ||
1634 | * drm_mode_rmfb - remove an FB from the configuration | ||
1635 | * @inode: inode from the ioctl | ||
1636 | * @filp: file * from the ioctl | ||
1637 | * @cmd: cmd from ioctl | ||
1638 | * @arg: arg from ioctl | ||
1639 | * | ||
1640 | * LOCKING: | ||
1641 | * Takes mode config lock. | ||
1642 | * | ||
1643 | * Remove the FB specified by the user. | ||
1644 | * | ||
1645 | * Called by the user via ioctl. | ||
1646 | * | ||
1647 | * RETURNS: | ||
1648 | * Zero on success, errno on failure. | ||
1649 | */ | ||
1650 | int drm_mode_rmfb(struct drm_device *dev, | ||
1651 | void *data, struct drm_file *file_priv) | ||
1652 | { | ||
1653 | struct drm_mode_object *obj; | ||
1654 | struct drm_framebuffer *fb = NULL; | ||
1655 | struct drm_framebuffer *fbl = NULL; | ||
1656 | uint32_t *id = data; | ||
1657 | int ret = 0; | ||
1658 | int found = 0; | ||
1659 | |||
1660 | mutex_lock(&dev->mode_config.mutex); | ||
1661 | obj = drm_mode_object_find(dev, *id, DRM_MODE_OBJECT_FB); | ||
1662 | /* TODO check that we realy get a framebuffer back. */ | ||
1663 | if (!obj) { | ||
1664 | DRM_ERROR("mode invalid framebuffer id\n"); | ||
1665 | ret = -EINVAL; | ||
1666 | goto out; | ||
1667 | } | ||
1668 | fb = obj_to_fb(obj); | ||
1669 | |||
1670 | list_for_each_entry(fbl, &file_priv->fbs, filp_head) | ||
1671 | if (fb == fbl) | ||
1672 | found = 1; | ||
1673 | |||
1674 | if (!found) { | ||
1675 | DRM_ERROR("tried to remove a fb that we didn't own\n"); | ||
1676 | ret = -EINVAL; | ||
1677 | goto out; | ||
1678 | } | ||
1679 | |||
1680 | /* TODO release all crtc connected to the framebuffer */ | ||
1681 | /* TODO unhock the destructor from the buffer object */ | ||
1682 | |||
1683 | list_del(&fb->filp_head); | ||
1684 | fb->funcs->destroy(fb); | ||
1685 | |||
1686 | out: | ||
1687 | mutex_unlock(&dev->mode_config.mutex); | ||
1688 | return ret; | ||
1689 | } | ||
1690 | |||
1691 | /** | ||
1692 | * drm_mode_getfb - get FB info | ||
1693 | * @inode: inode from the ioctl | ||
1694 | * @filp: file * from the ioctl | ||
1695 | * @cmd: cmd from ioctl | ||
1696 | * @arg: arg from ioctl | ||
1697 | * | ||
1698 | * LOCKING: | ||
1699 | * Caller? (FIXME) | ||
1700 | * | ||
1701 | * Lookup the FB given its ID and return info about it. | ||
1702 | * | ||
1703 | * Called by the user via ioctl. | ||
1704 | * | ||
1705 | * RETURNS: | ||
1706 | * Zero on success, errno on failure. | ||
1707 | */ | ||
1708 | int drm_mode_getfb(struct drm_device *dev, | ||
1709 | void *data, struct drm_file *file_priv) | ||
1710 | { | ||
1711 | struct drm_mode_fb_cmd *r = data; | ||
1712 | struct drm_mode_object *obj; | ||
1713 | struct drm_framebuffer *fb; | ||
1714 | int ret = 0; | ||
1715 | |||
1716 | mutex_lock(&dev->mode_config.mutex); | ||
1717 | obj = drm_mode_object_find(dev, r->buffer_id, DRM_MODE_OBJECT_FB); | ||
1718 | if (!obj) { | ||
1719 | DRM_ERROR("invalid framebuffer id\n"); | ||
1720 | ret = -EINVAL; | ||
1721 | goto out; | ||
1722 | } | ||
1723 | fb = obj_to_fb(obj); | ||
1724 | |||
1725 | r->height = fb->height; | ||
1726 | r->width = fb->width; | ||
1727 | r->depth = fb->depth; | ||
1728 | r->bpp = fb->bits_per_pixel; | ||
1729 | r->pitch = fb->pitch; | ||
1730 | fb->funcs->create_handle(fb, file_priv, &r->handle); | ||
1731 | |||
1732 | out: | ||
1733 | mutex_unlock(&dev->mode_config.mutex); | ||
1734 | return ret; | ||
1735 | } | ||
1736 | |||
1737 | /** | ||
1738 | * drm_fb_release - remove and free the FBs on this file | ||
1739 | * @filp: file * from the ioctl | ||
1740 | * | ||
1741 | * LOCKING: | ||
1742 | * Takes mode config lock. | ||
1743 | * | ||
1744 | * Destroy all the FBs associated with @filp. | ||
1745 | * | ||
1746 | * Called by the user via ioctl. | ||
1747 | * | ||
1748 | * RETURNS: | ||
1749 | * Zero on success, errno on failure. | ||
1750 | */ | ||
1751 | void drm_fb_release(struct file *filp) | ||
1752 | { | ||
1753 | struct drm_file *priv = filp->private_data; | ||
1754 | struct drm_device *dev = priv->minor->dev; | ||
1755 | struct drm_framebuffer *fb, *tfb; | ||
1756 | |||
1757 | mutex_lock(&dev->mode_config.mutex); | ||
1758 | list_for_each_entry_safe(fb, tfb, &priv->fbs, filp_head) { | ||
1759 | list_del(&fb->filp_head); | ||
1760 | fb->funcs->destroy(fb); | ||
1761 | } | ||
1762 | mutex_unlock(&dev->mode_config.mutex); | ||
1763 | } | ||
1764 | |||
1765 | /** | ||
1766 | * drm_mode_attachmode - add a mode to the user mode list | ||
1767 | * @dev: DRM device | ||
1768 | * @connector: connector to add the mode to | ||
1769 | * @mode: mode to add | ||
1770 | * | ||
1771 | * Add @mode to @connector's user mode list. | ||
1772 | */ | ||
1773 | static int drm_mode_attachmode(struct drm_device *dev, | ||
1774 | struct drm_connector *connector, | ||
1775 | struct drm_display_mode *mode) | ||
1776 | { | ||
1777 | int ret = 0; | ||
1778 | |||
1779 | list_add_tail(&mode->head, &connector->user_modes); | ||
1780 | return ret; | ||
1781 | } | ||
1782 | |||
1783 | int drm_mode_attachmode_crtc(struct drm_device *dev, struct drm_crtc *crtc, | ||
1784 | struct drm_display_mode *mode) | ||
1785 | { | ||
1786 | struct drm_connector *connector; | ||
1787 | int ret = 0; | ||
1788 | struct drm_display_mode *dup_mode; | ||
1789 | int need_dup = 0; | ||
1790 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { | ||
1791 | if (!connector->encoder) | ||
1792 | break; | ||
1793 | if (connector->encoder->crtc == crtc) { | ||
1794 | if (need_dup) | ||
1795 | dup_mode = drm_mode_duplicate(dev, mode); | ||
1796 | else | ||
1797 | dup_mode = mode; | ||
1798 | ret = drm_mode_attachmode(dev, connector, dup_mode); | ||
1799 | if (ret) | ||
1800 | return ret; | ||
1801 | need_dup = 1; | ||
1802 | } | ||
1803 | } | ||
1804 | return 0; | ||
1805 | } | ||
1806 | EXPORT_SYMBOL(drm_mode_attachmode_crtc); | ||
1807 | |||
1808 | static int drm_mode_detachmode(struct drm_device *dev, | ||
1809 | struct drm_connector *connector, | ||
1810 | struct drm_display_mode *mode) | ||
1811 | { | ||
1812 | int found = 0; | ||
1813 | int ret = 0; | ||
1814 | struct drm_display_mode *match_mode, *t; | ||
1815 | |||
1816 | list_for_each_entry_safe(match_mode, t, &connector->user_modes, head) { | ||
1817 | if (drm_mode_equal(match_mode, mode)) { | ||
1818 | list_del(&match_mode->head); | ||
1819 | drm_mode_destroy(dev, match_mode); | ||
1820 | found = 1; | ||
1821 | break; | ||
1822 | } | ||
1823 | } | ||
1824 | |||
1825 | if (!found) | ||
1826 | ret = -EINVAL; | ||
1827 | |||
1828 | return ret; | ||
1829 | } | ||
1830 | |||
1831 | int drm_mode_detachmode_crtc(struct drm_device *dev, struct drm_display_mode *mode) | ||
1832 | { | ||
1833 | struct drm_connector *connector; | ||
1834 | |||
1835 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { | ||
1836 | drm_mode_detachmode(dev, connector, mode); | ||
1837 | } | ||
1838 | return 0; | ||
1839 | } | ||
1840 | EXPORT_SYMBOL(drm_mode_detachmode_crtc); | ||
1841 | |||
1842 | /** | ||
1843 | * drm_fb_attachmode - Attach a user mode to an connector | ||
1844 | * @inode: inode from the ioctl | ||
1845 | * @filp: file * from the ioctl | ||
1846 | * @cmd: cmd from ioctl | ||
1847 | * @arg: arg from ioctl | ||
1848 | * | ||
1849 | * This attaches a user specified mode to an connector. | ||
1850 | * Called by the user via ioctl. | ||
1851 | * | ||
1852 | * RETURNS: | ||
1853 | * Zero on success, errno on failure. | ||
1854 | */ | ||
1855 | int drm_mode_attachmode_ioctl(struct drm_device *dev, | ||
1856 | void *data, struct drm_file *file_priv) | ||
1857 | { | ||
1858 | struct drm_mode_mode_cmd *mode_cmd = data; | ||
1859 | struct drm_connector *connector; | ||
1860 | struct drm_display_mode *mode; | ||
1861 | struct drm_mode_object *obj; | ||
1862 | struct drm_mode_modeinfo *umode = &mode_cmd->mode; | ||
1863 | int ret = 0; | ||
1864 | |||
1865 | mutex_lock(&dev->mode_config.mutex); | ||
1866 | |||
1867 | obj = drm_mode_object_find(dev, mode_cmd->connector_id, DRM_MODE_OBJECT_CONNECTOR); | ||
1868 | if (!obj) { | ||
1869 | ret = -EINVAL; | ||
1870 | goto out; | ||
1871 | } | ||
1872 | connector = obj_to_connector(obj); | ||
1873 | |||
1874 | mode = drm_mode_create(dev); | ||
1875 | if (!mode) { | ||
1876 | ret = -ENOMEM; | ||
1877 | goto out; | ||
1878 | } | ||
1879 | |||
1880 | drm_crtc_convert_umode(mode, umode); | ||
1881 | |||
1882 | ret = drm_mode_attachmode(dev, connector, mode); | ||
1883 | out: | ||
1884 | mutex_unlock(&dev->mode_config.mutex); | ||
1885 | return ret; | ||
1886 | } | ||
1887 | |||
1888 | |||
1889 | /** | ||
1890 | * drm_fb_detachmode - Detach a user specified mode from an connector | ||
1891 | * @inode: inode from the ioctl | ||
1892 | * @filp: file * from the ioctl | ||
1893 | * @cmd: cmd from ioctl | ||
1894 | * @arg: arg from ioctl | ||
1895 | * | ||
1896 | * Called by the user via ioctl. | ||
1897 | * | ||
1898 | * RETURNS: | ||
1899 | * Zero on success, errno on failure. | ||
1900 | */ | ||
1901 | int drm_mode_detachmode_ioctl(struct drm_device *dev, | ||
1902 | void *data, struct drm_file *file_priv) | ||
1903 | { | ||
1904 | struct drm_mode_object *obj; | ||
1905 | struct drm_mode_mode_cmd *mode_cmd = data; | ||
1906 | struct drm_connector *connector; | ||
1907 | struct drm_display_mode mode; | ||
1908 | struct drm_mode_modeinfo *umode = &mode_cmd->mode; | ||
1909 | int ret = 0; | ||
1910 | |||
1911 | mutex_lock(&dev->mode_config.mutex); | ||
1912 | |||
1913 | obj = drm_mode_object_find(dev, mode_cmd->connector_id, DRM_MODE_OBJECT_CONNECTOR); | ||
1914 | if (!obj) { | ||
1915 | ret = -EINVAL; | ||
1916 | goto out; | ||
1917 | } | ||
1918 | connector = obj_to_connector(obj); | ||
1919 | |||
1920 | drm_crtc_convert_umode(&mode, umode); | ||
1921 | ret = drm_mode_detachmode(dev, connector, &mode); | ||
1922 | out: | ||
1923 | mutex_unlock(&dev->mode_config.mutex); | ||
1924 | return ret; | ||
1925 | } | ||
1926 | |||
1927 | struct drm_property *drm_property_create(struct drm_device *dev, int flags, | ||
1928 | const char *name, int num_values) | ||
1929 | { | ||
1930 | struct drm_property *property = NULL; | ||
1931 | |||
1932 | property = kzalloc(sizeof(struct drm_property), GFP_KERNEL); | ||
1933 | if (!property) | ||
1934 | return NULL; | ||
1935 | |||
1936 | if (num_values) { | ||
1937 | property->values = kzalloc(sizeof(uint64_t)*num_values, GFP_KERNEL); | ||
1938 | if (!property->values) | ||
1939 | goto fail; | ||
1940 | } | ||
1941 | |||
1942 | drm_mode_object_get(dev, &property->base, DRM_MODE_OBJECT_PROPERTY); | ||
1943 | property->flags = flags; | ||
1944 | property->num_values = num_values; | ||
1945 | INIT_LIST_HEAD(&property->enum_blob_list); | ||
1946 | |||
1947 | if (name) | ||
1948 | strncpy(property->name, name, DRM_PROP_NAME_LEN); | ||
1949 | |||
1950 | list_add_tail(&property->head, &dev->mode_config.property_list); | ||
1951 | return property; | ||
1952 | fail: | ||
1953 | kfree(property); | ||
1954 | return NULL; | ||
1955 | } | ||
1956 | EXPORT_SYMBOL(drm_property_create); | ||
1957 | |||
1958 | int drm_property_add_enum(struct drm_property *property, int index, | ||
1959 | uint64_t value, const char *name) | ||
1960 | { | ||
1961 | struct drm_property_enum *prop_enum; | ||
1962 | |||
1963 | if (!(property->flags & DRM_MODE_PROP_ENUM)) | ||
1964 | return -EINVAL; | ||
1965 | |||
1966 | if (!list_empty(&property->enum_blob_list)) { | ||
1967 | list_for_each_entry(prop_enum, &property->enum_blob_list, head) { | ||
1968 | if (prop_enum->value == value) { | ||
1969 | strncpy(prop_enum->name, name, DRM_PROP_NAME_LEN); | ||
1970 | prop_enum->name[DRM_PROP_NAME_LEN-1] = '\0'; | ||
1971 | return 0; | ||
1972 | } | ||
1973 | } | ||
1974 | } | ||
1975 | |||
1976 | prop_enum = kzalloc(sizeof(struct drm_property_enum), GFP_KERNEL); | ||
1977 | if (!prop_enum) | ||
1978 | return -ENOMEM; | ||
1979 | |||
1980 | strncpy(prop_enum->name, name, DRM_PROP_NAME_LEN); | ||
1981 | prop_enum->name[DRM_PROP_NAME_LEN-1] = '\0'; | ||
1982 | prop_enum->value = value; | ||
1983 | |||
1984 | property->values[index] = value; | ||
1985 | list_add_tail(&prop_enum->head, &property->enum_blob_list); | ||
1986 | return 0; | ||
1987 | } | ||
1988 | EXPORT_SYMBOL(drm_property_add_enum); | ||
1989 | |||
1990 | void drm_property_destroy(struct drm_device *dev, struct drm_property *property) | ||
1991 | { | ||
1992 | struct drm_property_enum *prop_enum, *pt; | ||
1993 | |||
1994 | list_for_each_entry_safe(prop_enum, pt, &property->enum_blob_list, head) { | ||
1995 | list_del(&prop_enum->head); | ||
1996 | kfree(prop_enum); | ||
1997 | } | ||
1998 | |||
1999 | if (property->num_values) | ||
2000 | kfree(property->values); | ||
2001 | drm_mode_object_put(dev, &property->base); | ||
2002 | list_del(&property->head); | ||
2003 | kfree(property); | ||
2004 | } | ||
2005 | EXPORT_SYMBOL(drm_property_destroy); | ||
2006 | |||
2007 | int drm_connector_attach_property(struct drm_connector *connector, | ||
2008 | struct drm_property *property, uint64_t init_val) | ||
2009 | { | ||
2010 | int i; | ||
2011 | |||
2012 | for (i = 0; i < DRM_CONNECTOR_MAX_PROPERTY; i++) { | ||
2013 | if (connector->property_ids[i] == 0) { | ||
2014 | connector->property_ids[i] = property->base.id; | ||
2015 | connector->property_values[i] = init_val; | ||
2016 | break; | ||
2017 | } | ||
2018 | } | ||
2019 | |||
2020 | if (i == DRM_CONNECTOR_MAX_PROPERTY) | ||
2021 | return -EINVAL; | ||
2022 | return 0; | ||
2023 | } | ||
2024 | EXPORT_SYMBOL(drm_connector_attach_property); | ||
2025 | |||
2026 | int drm_connector_property_set_value(struct drm_connector *connector, | ||
2027 | struct drm_property *property, uint64_t value) | ||
2028 | { | ||
2029 | int i; | ||
2030 | |||
2031 | for (i = 0; i < DRM_CONNECTOR_MAX_PROPERTY; i++) { | ||
2032 | if (connector->property_ids[i] == property->base.id) { | ||
2033 | connector->property_values[i] = value; | ||
2034 | break; | ||
2035 | } | ||
2036 | } | ||
2037 | |||
2038 | if (i == DRM_CONNECTOR_MAX_PROPERTY) | ||
2039 | return -EINVAL; | ||
2040 | return 0; | ||
2041 | } | ||
2042 | EXPORT_SYMBOL(drm_connector_property_set_value); | ||
2043 | |||
2044 | int drm_connector_property_get_value(struct drm_connector *connector, | ||
2045 | struct drm_property *property, uint64_t *val) | ||
2046 | { | ||
2047 | int i; | ||
2048 | |||
2049 | for (i = 0; i < DRM_CONNECTOR_MAX_PROPERTY; i++) { | ||
2050 | if (connector->property_ids[i] == property->base.id) { | ||
2051 | *val = connector->property_values[i]; | ||
2052 | break; | ||
2053 | } | ||
2054 | } | ||
2055 | |||
2056 | if (i == DRM_CONNECTOR_MAX_PROPERTY) | ||
2057 | return -EINVAL; | ||
2058 | return 0; | ||
2059 | } | ||
2060 | EXPORT_SYMBOL(drm_connector_property_get_value); | ||
2061 | |||
2062 | int drm_mode_getproperty_ioctl(struct drm_device *dev, | ||
2063 | void *data, struct drm_file *file_priv) | ||
2064 | { | ||
2065 | struct drm_mode_object *obj; | ||
2066 | struct drm_mode_get_property *out_resp = data; | ||
2067 | struct drm_property *property; | ||
2068 | int enum_count = 0; | ||
2069 | int blob_count = 0; | ||
2070 | int value_count = 0; | ||
2071 | int ret = 0, i; | ||
2072 | int copied; | ||
2073 | struct drm_property_enum *prop_enum; | ||
2074 | struct drm_mode_property_enum __user *enum_ptr; | ||
2075 | struct drm_property_blob *prop_blob; | ||
2076 | uint32_t *blob_id_ptr; | ||
2077 | uint64_t __user *values_ptr; | ||
2078 | uint32_t __user *blob_length_ptr; | ||
2079 | |||
2080 | mutex_lock(&dev->mode_config.mutex); | ||
2081 | obj = drm_mode_object_find(dev, out_resp->prop_id, DRM_MODE_OBJECT_PROPERTY); | ||
2082 | if (!obj) { | ||
2083 | ret = -EINVAL; | ||
2084 | goto done; | ||
2085 | } | ||
2086 | property = obj_to_property(obj); | ||
2087 | |||
2088 | if (property->flags & DRM_MODE_PROP_ENUM) { | ||
2089 | list_for_each_entry(prop_enum, &property->enum_blob_list, head) | ||
2090 | enum_count++; | ||
2091 | } else if (property->flags & DRM_MODE_PROP_BLOB) { | ||
2092 | list_for_each_entry(prop_blob, &property->enum_blob_list, head) | ||
2093 | blob_count++; | ||
2094 | } | ||
2095 | |||
2096 | value_count = property->num_values; | ||
2097 | |||
2098 | strncpy(out_resp->name, property->name, DRM_PROP_NAME_LEN); | ||
2099 | out_resp->name[DRM_PROP_NAME_LEN-1] = 0; | ||
2100 | out_resp->flags = property->flags; | ||
2101 | |||
2102 | if ((out_resp->count_values >= value_count) && value_count) { | ||
2103 | values_ptr = (uint64_t *)(unsigned long)out_resp->values_ptr; | ||
2104 | for (i = 0; i < value_count; i++) { | ||
2105 | if (copy_to_user(values_ptr + i, &property->values[i], sizeof(uint64_t))) { | ||
2106 | ret = -EFAULT; | ||
2107 | goto done; | ||
2108 | } | ||
2109 | } | ||
2110 | } | ||
2111 | out_resp->count_values = value_count; | ||
2112 | |||
2113 | if (property->flags & DRM_MODE_PROP_ENUM) { | ||
2114 | if ((out_resp->count_enum_blobs >= enum_count) && enum_count) { | ||
2115 | copied = 0; | ||
2116 | enum_ptr = (struct drm_mode_property_enum *)(unsigned long)out_resp->enum_blob_ptr; | ||
2117 | list_for_each_entry(prop_enum, &property->enum_blob_list, head) { | ||
2118 | |||
2119 | if (copy_to_user(&enum_ptr[copied].value, &prop_enum->value, sizeof(uint64_t))) { | ||
2120 | ret = -EFAULT; | ||
2121 | goto done; | ||
2122 | } | ||
2123 | |||
2124 | if (copy_to_user(&enum_ptr[copied].name, | ||
2125 | &prop_enum->name, DRM_PROP_NAME_LEN)) { | ||
2126 | ret = -EFAULT; | ||
2127 | goto done; | ||
2128 | } | ||
2129 | copied++; | ||
2130 | } | ||
2131 | } | ||
2132 | out_resp->count_enum_blobs = enum_count; | ||
2133 | } | ||
2134 | |||
2135 | if (property->flags & DRM_MODE_PROP_BLOB) { | ||
2136 | if ((out_resp->count_enum_blobs >= blob_count) && blob_count) { | ||
2137 | copied = 0; | ||
2138 | blob_id_ptr = (uint32_t *)(unsigned long)out_resp->enum_blob_ptr; | ||
2139 | blob_length_ptr = (uint32_t *)(unsigned long)out_resp->values_ptr; | ||
2140 | |||
2141 | list_for_each_entry(prop_blob, &property->enum_blob_list, head) { | ||
2142 | if (put_user(prop_blob->base.id, blob_id_ptr + copied)) { | ||
2143 | ret = -EFAULT; | ||
2144 | goto done; | ||
2145 | } | ||
2146 | |||
2147 | if (put_user(prop_blob->length, blob_length_ptr + copied)) { | ||
2148 | ret = -EFAULT; | ||
2149 | goto done; | ||
2150 | } | ||
2151 | |||
2152 | copied++; | ||
2153 | } | ||
2154 | } | ||
2155 | out_resp->count_enum_blobs = blob_count; | ||
2156 | } | ||
2157 | done: | ||
2158 | mutex_unlock(&dev->mode_config.mutex); | ||
2159 | return ret; | ||
2160 | } | ||
2161 | |||
2162 | static struct drm_property_blob *drm_property_create_blob(struct drm_device *dev, int length, | ||
2163 | void *data) | ||
2164 | { | ||
2165 | struct drm_property_blob *blob; | ||
2166 | |||
2167 | if (!length || !data) | ||
2168 | return NULL; | ||
2169 | |||
2170 | blob = kzalloc(sizeof(struct drm_property_blob)+length, GFP_KERNEL); | ||
2171 | if (!blob) | ||
2172 | return NULL; | ||
2173 | |||
2174 | blob->data = (void *)((char *)blob + sizeof(struct drm_property_blob)); | ||
2175 | blob->length = length; | ||
2176 | |||
2177 | memcpy(blob->data, data, length); | ||
2178 | |||
2179 | drm_mode_object_get(dev, &blob->base, DRM_MODE_OBJECT_BLOB); | ||
2180 | |||
2181 | list_add_tail(&blob->head, &dev->mode_config.property_blob_list); | ||
2182 | return blob; | ||
2183 | } | ||
2184 | |||
2185 | static void drm_property_destroy_blob(struct drm_device *dev, | ||
2186 | struct drm_property_blob *blob) | ||
2187 | { | ||
2188 | drm_mode_object_put(dev, &blob->base); | ||
2189 | list_del(&blob->head); | ||
2190 | kfree(blob); | ||
2191 | } | ||
2192 | |||
2193 | int drm_mode_getblob_ioctl(struct drm_device *dev, | ||
2194 | void *data, struct drm_file *file_priv) | ||
2195 | { | ||
2196 | struct drm_mode_object *obj; | ||
2197 | struct drm_mode_get_blob *out_resp = data; | ||
2198 | struct drm_property_blob *blob; | ||
2199 | int ret = 0; | ||
2200 | void *blob_ptr; | ||
2201 | |||
2202 | mutex_lock(&dev->mode_config.mutex); | ||
2203 | obj = drm_mode_object_find(dev, out_resp->blob_id, DRM_MODE_OBJECT_BLOB); | ||
2204 | if (!obj) { | ||
2205 | ret = -EINVAL; | ||
2206 | goto done; | ||
2207 | } | ||
2208 | blob = obj_to_blob(obj); | ||
2209 | |||
2210 | if (out_resp->length == blob->length) { | ||
2211 | blob_ptr = (void *)(unsigned long)out_resp->data; | ||
2212 | if (copy_to_user(blob_ptr, blob->data, blob->length)){ | ||
2213 | ret = -EFAULT; | ||
2214 | goto done; | ||
2215 | } | ||
2216 | } | ||
2217 | out_resp->length = blob->length; | ||
2218 | |||
2219 | done: | ||
2220 | mutex_unlock(&dev->mode_config.mutex); | ||
2221 | return ret; | ||
2222 | } | ||
2223 | |||
2224 | int drm_mode_connector_update_edid_property(struct drm_connector *connector, | ||
2225 | struct edid *edid) | ||
2226 | { | ||
2227 | struct drm_device *dev = connector->dev; | ||
2228 | int ret = 0; | ||
2229 | |||
2230 | if (connector->edid_blob_ptr) | ||
2231 | drm_property_destroy_blob(dev, connector->edid_blob_ptr); | ||
2232 | |||
2233 | /* Delete edid, when there is none. */ | ||
2234 | if (!edid) { | ||
2235 | connector->edid_blob_ptr = NULL; | ||
2236 | ret = drm_connector_property_set_value(connector, dev->mode_config.edid_property, 0); | ||
2237 | return ret; | ||
2238 | } | ||
2239 | |||
2240 | connector->edid_blob_ptr = drm_property_create_blob(connector->dev, 128, edid); | ||
2241 | |||
2242 | ret = drm_connector_property_set_value(connector, | ||
2243 | dev->mode_config.edid_property, | ||
2244 | connector->edid_blob_ptr->base.id); | ||
2245 | |||
2246 | return ret; | ||
2247 | } | ||
2248 | EXPORT_SYMBOL(drm_mode_connector_update_edid_property); | ||
2249 | |||
2250 | int drm_mode_connector_property_set_ioctl(struct drm_device *dev, | ||
2251 | void *data, struct drm_file *file_priv) | ||
2252 | { | ||
2253 | struct drm_mode_connector_set_property *out_resp = data; | ||
2254 | struct drm_mode_object *obj; | ||
2255 | struct drm_property *property; | ||
2256 | struct drm_connector *connector; | ||
2257 | int ret = -EINVAL; | ||
2258 | int i; | ||
2259 | |||
2260 | mutex_lock(&dev->mode_config.mutex); | ||
2261 | |||
2262 | obj = drm_mode_object_find(dev, out_resp->connector_id, DRM_MODE_OBJECT_CONNECTOR); | ||
2263 | if (!obj) { | ||
2264 | goto out; | ||
2265 | } | ||
2266 | connector = obj_to_connector(obj); | ||
2267 | |||
2268 | for (i = 0; i < DRM_CONNECTOR_MAX_PROPERTY; i++) { | ||
2269 | if (connector->property_ids[i] == out_resp->prop_id) | ||
2270 | break; | ||
2271 | } | ||
2272 | |||
2273 | if (i == DRM_CONNECTOR_MAX_PROPERTY) { | ||
2274 | goto out; | ||
2275 | } | ||
2276 | |||
2277 | obj = drm_mode_object_find(dev, out_resp->prop_id, DRM_MODE_OBJECT_PROPERTY); | ||
2278 | if (!obj) { | ||
2279 | goto out; | ||
2280 | } | ||
2281 | property = obj_to_property(obj); | ||
2282 | |||
2283 | if (property->flags & DRM_MODE_PROP_IMMUTABLE) | ||
2284 | goto out; | ||
2285 | |||
2286 | if (property->flags & DRM_MODE_PROP_RANGE) { | ||
2287 | if (out_resp->value < property->values[0]) | ||
2288 | goto out; | ||
2289 | |||
2290 | if (out_resp->value > property->values[1]) | ||
2291 | goto out; | ||
2292 | } else { | ||
2293 | int found = 0; | ||
2294 | for (i = 0; i < property->num_values; i++) { | ||
2295 | if (property->values[i] == out_resp->value) { | ||
2296 | found = 1; | ||
2297 | break; | ||
2298 | } | ||
2299 | } | ||
2300 | if (!found) { | ||
2301 | goto out; | ||
2302 | } | ||
2303 | } | ||
2304 | |||
2305 | if (connector->funcs->set_property) | ||
2306 | ret = connector->funcs->set_property(connector, property, out_resp->value); | ||
2307 | |||
2308 | /* store the property value if succesful */ | ||
2309 | if (!ret) | ||
2310 | drm_connector_property_set_value(connector, property, out_resp->value); | ||
2311 | out: | ||
2312 | mutex_unlock(&dev->mode_config.mutex); | ||
2313 | return ret; | ||
2314 | } | ||
2315 | |||
2316 | |||
2317 | int drm_mode_replacefb(struct drm_device *dev, | ||
2318 | void *data, struct drm_file *file_priv) | ||
2319 | { | ||
2320 | struct drm_mode_fb_cmd *r = data; | ||
2321 | struct drm_mode_object *obj; | ||
2322 | struct drm_framebuffer *fb; | ||
2323 | int found = 0; | ||
2324 | struct drm_framebuffer *fbl = NULL; | ||
2325 | int ret = 0; | ||
2326 | |||
2327 | /* right replace the current bo attached to this fb with a new bo */ | ||
2328 | mutex_lock(&dev->mode_config.mutex); | ||
2329 | obj = drm_mode_object_find(dev, r->buffer_id, DRM_MODE_OBJECT_FB); | ||
2330 | if (!obj) { | ||
2331 | ret = -EINVAL; | ||
2332 | goto out; | ||
2333 | } | ||
2334 | fb = obj_to_fb(obj); | ||
2335 | |||
2336 | list_for_each_entry(fbl, &file_priv->fbs, filp_head) | ||
2337 | if (fb == fbl) | ||
2338 | found = 1; | ||
2339 | |||
2340 | if (!found) { | ||
2341 | DRM_ERROR("tried to replace an fb we didn't own\n"); | ||
2342 | ret = -EINVAL; | ||
2343 | goto out; | ||
2344 | } | ||
2345 | |||
2346 | if (dev->mode_config.funcs->resize_fb) | ||
2347 | ret = dev->mode_config.funcs->resize_fb(dev, file_priv, fb, r); | ||
2348 | else | ||
2349 | ret = -EINVAL; | ||
2350 | out: | ||
2351 | mutex_unlock(&dev->mode_config.mutex); | ||
2352 | return ret; | ||
2353 | |||
2354 | } | ||
2355 | |||
2356 | int drm_mode_connector_attach_encoder(struct drm_connector *connector, | ||
2357 | struct drm_encoder *encoder) | ||
2358 | { | ||
2359 | int i; | ||
2360 | |||
2361 | for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { | ||
2362 | if (connector->encoder_ids[i] == 0) { | ||
2363 | connector->encoder_ids[i] = encoder->base.id; | ||
2364 | return 0; | ||
2365 | } | ||
2366 | } | ||
2367 | return -ENOMEM; | ||
2368 | } | ||
2369 | EXPORT_SYMBOL(drm_mode_connector_attach_encoder); | ||
2370 | |||
2371 | void drm_mode_connector_detach_encoder(struct drm_connector *connector, | ||
2372 | struct drm_encoder *encoder) | ||
2373 | { | ||
2374 | int i; | ||
2375 | for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { | ||
2376 | if (connector->encoder_ids[i] == encoder->base.id) { | ||
2377 | connector->encoder_ids[i] = 0; | ||
2378 | if (connector->encoder == encoder) | ||
2379 | connector->encoder = NULL; | ||
2380 | break; | ||
2381 | } | ||
2382 | } | ||
2383 | } | ||
2384 | EXPORT_SYMBOL(drm_mode_connector_detach_encoder); | ||
2385 | |||
2386 | bool drm_mode_crtc_set_gamma_size(struct drm_crtc *crtc, | ||
2387 | int gamma_size) | ||
2388 | { | ||
2389 | crtc->gamma_size = gamma_size; | ||
2390 | |||
2391 | crtc->gamma_store = kzalloc(gamma_size * sizeof(uint16_t) * 3, GFP_KERNEL); | ||
2392 | if (!crtc->gamma_store) { | ||
2393 | crtc->gamma_size = 0; | ||
2394 | return false; | ||
2395 | } | ||
2396 | |||
2397 | return true; | ||
2398 | } | ||
2399 | EXPORT_SYMBOL(drm_mode_crtc_set_gamma_size); | ||
2400 | |||
2401 | int drm_mode_gamma_set_ioctl(struct drm_device *dev, | ||
2402 | void *data, struct drm_file *file_priv) | ||
2403 | { | ||
2404 | struct drm_mode_crtc_lut *crtc_lut = data; | ||
2405 | struct drm_mode_object *obj; | ||
2406 | struct drm_crtc *crtc; | ||
2407 | void *r_base, *g_base, *b_base; | ||
2408 | int size; | ||
2409 | int ret = 0; | ||
2410 | |||
2411 | mutex_lock(&dev->mode_config.mutex); | ||
2412 | obj = drm_mode_object_find(dev, crtc_lut->crtc_id, DRM_MODE_OBJECT_CRTC); | ||
2413 | if (!obj) { | ||
2414 | ret = -EINVAL; | ||
2415 | goto out; | ||
2416 | } | ||
2417 | crtc = obj_to_crtc(obj); | ||
2418 | |||
2419 | /* memcpy into gamma store */ | ||
2420 | if (crtc_lut->gamma_size != crtc->gamma_size) { | ||
2421 | ret = -EINVAL; | ||
2422 | goto out; | ||
2423 | } | ||
2424 | |||
2425 | size = crtc_lut->gamma_size * (sizeof(uint16_t)); | ||
2426 | r_base = crtc->gamma_store; | ||
2427 | if (copy_from_user(r_base, (void __user *)(unsigned long)crtc_lut->red, size)) { | ||
2428 | ret = -EFAULT; | ||
2429 | goto out; | ||
2430 | } | ||
2431 | |||
2432 | g_base = r_base + size; | ||
2433 | if (copy_from_user(g_base, (void __user *)(unsigned long)crtc_lut->green, size)) { | ||
2434 | ret = -EFAULT; | ||
2435 | goto out; | ||
2436 | } | ||
2437 | |||
2438 | b_base = g_base + size; | ||
2439 | if (copy_from_user(b_base, (void __user *)(unsigned long)crtc_lut->blue, size)) { | ||
2440 | ret = -EFAULT; | ||
2441 | goto out; | ||
2442 | } | ||
2443 | |||
2444 | crtc->funcs->gamma_set(crtc, r_base, g_base, b_base, crtc->gamma_size); | ||
2445 | |||
2446 | out: | ||
2447 | mutex_unlock(&dev->mode_config.mutex); | ||
2448 | return ret; | ||
2449 | |||
2450 | } | ||
2451 | |||
2452 | int drm_mode_gamma_get_ioctl(struct drm_device *dev, | ||
2453 | void *data, struct drm_file *file_priv) | ||
2454 | { | ||
2455 | struct drm_mode_crtc_lut *crtc_lut = data; | ||
2456 | struct drm_mode_object *obj; | ||
2457 | struct drm_crtc *crtc; | ||
2458 | void *r_base, *g_base, *b_base; | ||
2459 | int size; | ||
2460 | int ret = 0; | ||
2461 | |||
2462 | mutex_lock(&dev->mode_config.mutex); | ||
2463 | obj = drm_mode_object_find(dev, crtc_lut->crtc_id, DRM_MODE_OBJECT_CRTC); | ||
2464 | if (!obj) { | ||
2465 | ret = -EINVAL; | ||
2466 | goto out; | ||
2467 | } | ||
2468 | crtc = obj_to_crtc(obj); | ||
2469 | |||
2470 | /* memcpy into gamma store */ | ||
2471 | if (crtc_lut->gamma_size != crtc->gamma_size) { | ||
2472 | ret = -EINVAL; | ||
2473 | goto out; | ||
2474 | } | ||
2475 | |||
2476 | size = crtc_lut->gamma_size * (sizeof(uint16_t)); | ||
2477 | r_base = crtc->gamma_store; | ||
2478 | if (copy_to_user((void __user *)(unsigned long)crtc_lut->red, r_base, size)) { | ||
2479 | ret = -EFAULT; | ||
2480 | goto out; | ||
2481 | } | ||
2482 | |||
2483 | g_base = r_base + size; | ||
2484 | if (copy_to_user((void __user *)(unsigned long)crtc_lut->green, g_base, size)) { | ||
2485 | ret = -EFAULT; | ||
2486 | goto out; | ||
2487 | } | ||
2488 | |||
2489 | b_base = g_base + size; | ||
2490 | if (copy_to_user((void __user *)(unsigned long)crtc_lut->blue, b_base, size)) { | ||
2491 | ret = -EFAULT; | ||
2492 | goto out; | ||
2493 | } | ||
2494 | out: | ||
2495 | mutex_unlock(&dev->mode_config.mutex); | ||
2496 | return ret; | ||
2497 | } | ||
diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c new file mode 100644 index 000000000000..887ed33b0694 --- /dev/null +++ b/drivers/gpu/drm/drm_crtc_helper.c | |||
@@ -0,0 +1,822 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2006-2008 Intel Corporation | ||
3 | * Copyright (c) 2007 Dave Airlie <airlied@linux.ie> | ||
4 | * | ||
5 | * DRM core CRTC related functions | ||
6 | * | ||
7 | * Permission to use, copy, modify, distribute, and sell this software and its | ||
8 | * documentation for any purpose is hereby granted without fee, provided that | ||
9 | * the above copyright notice appear in all copies and that both that copyright | ||
10 | * notice and this permission notice appear in supporting documentation, and | ||
11 | * that the name of the copyright holders not be used in advertising or | ||
12 | * publicity pertaining to distribution of the software without specific, | ||
13 | * written prior permission. The copyright holders make no representations | ||
14 | * about the suitability of this software for any purpose. It is provided "as | ||
15 | * is" without express or implied warranty. | ||
16 | * | ||
17 | * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, | ||
18 | * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO | ||
19 | * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR | ||
20 | * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, | ||
21 | * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER | ||
22 | * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE | ||
23 | * OF THIS SOFTWARE. | ||
24 | * | ||
25 | * Authors: | ||
26 | * Keith Packard | ||
27 | * Eric Anholt <eric@anholt.net> | ||
28 | * Dave Airlie <airlied@linux.ie> | ||
29 | * Jesse Barnes <jesse.barnes@intel.com> | ||
30 | */ | ||
31 | |||
32 | #include "drmP.h" | ||
33 | #include "drm_crtc.h" | ||
34 | #include "drm_crtc_helper.h" | ||
35 | |||
36 | /* | ||
37 | * Detailed mode info for a standard 640x480@60Hz monitor | ||
38 | */ | ||
39 | static struct drm_display_mode std_mode[] = { | ||
40 | { DRM_MODE("640x480", DRM_MODE_TYPE_DEFAULT, 25200, 640, 656, | ||
41 | 752, 800, 0, 480, 490, 492, 525, 0, | ||
42 | DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, | ||
43 | }; | ||
44 | |||
45 | /** | ||
46 | * drm_helper_probe_connector_modes - get complete set of display modes | ||
47 | * @dev: DRM device | ||
48 | * @maxX: max width for modes | ||
49 | * @maxY: max height for modes | ||
50 | * | ||
51 | * LOCKING: | ||
52 | * Caller must hold mode config lock. | ||
53 | * | ||
54 | * Based on @dev's mode_config layout, scan all the connectors and try to detect | ||
55 | * modes on them. Modes will first be added to the connector's probed_modes | ||
56 | * list, then culled (based on validity and the @maxX, @maxY parameters) and | ||
57 | * put into the normal modes list. | ||
58 | * | ||
59 | * Intended to be used either at bootup time or when major configuration | ||
60 | * changes have occurred. | ||
61 | * | ||
62 | * FIXME: take into account monitor limits | ||
63 | */ | ||
64 | void drm_helper_probe_single_connector_modes(struct drm_connector *connector, | ||
65 | uint32_t maxX, uint32_t maxY) | ||
66 | { | ||
67 | struct drm_device *dev = connector->dev; | ||
68 | struct drm_display_mode *mode, *t; | ||
69 | struct drm_connector_helper_funcs *connector_funcs = | ||
70 | connector->helper_private; | ||
71 | int ret; | ||
72 | |||
73 | DRM_DEBUG("%s\n", drm_get_connector_name(connector)); | ||
74 | /* set all modes to the unverified state */ | ||
75 | list_for_each_entry_safe(mode, t, &connector->modes, head) | ||
76 | mode->status = MODE_UNVERIFIED; | ||
77 | |||
78 | connector->status = connector->funcs->detect(connector); | ||
79 | |||
80 | if (connector->status == connector_status_disconnected) { | ||
81 | DRM_DEBUG("%s is disconnected\n", | ||
82 | drm_get_connector_name(connector)); | ||
83 | /* TODO set EDID to NULL */ | ||
84 | return; | ||
85 | } | ||
86 | |||
87 | ret = (*connector_funcs->get_modes)(connector); | ||
88 | |||
89 | if (ret) { | ||
90 | drm_mode_connector_list_update(connector); | ||
91 | } | ||
92 | |||
93 | if (maxX && maxY) | ||
94 | drm_mode_validate_size(dev, &connector->modes, maxX, | ||
95 | maxY, 0); | ||
96 | list_for_each_entry_safe(mode, t, &connector->modes, head) { | ||
97 | if (mode->status == MODE_OK) | ||
98 | mode->status = connector_funcs->mode_valid(connector, | ||
99 | mode); | ||
100 | } | ||
101 | |||
102 | |||
103 | drm_mode_prune_invalid(dev, &connector->modes, true); | ||
104 | |||
105 | if (list_empty(&connector->modes)) { | ||
106 | struct drm_display_mode *stdmode; | ||
107 | |||
108 | DRM_DEBUG("No valid modes on %s\n", | ||
109 | drm_get_connector_name(connector)); | ||
110 | |||
111 | /* Should we do this here ??? | ||
112 | * When no valid EDID modes are available we end up | ||
113 | * here and bailed in the past, now we add a standard | ||
114 | * 640x480@60Hz mode and carry on. | ||
115 | */ | ||
116 | stdmode = drm_mode_duplicate(dev, &std_mode[0]); | ||
117 | drm_mode_probed_add(connector, stdmode); | ||
118 | drm_mode_list_concat(&connector->probed_modes, | ||
119 | &connector->modes); | ||
120 | |||
121 | DRM_DEBUG("Adding standard 640x480 @ 60Hz to %s\n", | ||
122 | drm_get_connector_name(connector)); | ||
123 | } | ||
124 | |||
125 | drm_mode_sort(&connector->modes); | ||
126 | |||
127 | DRM_DEBUG("Probed modes for %s\n", drm_get_connector_name(connector)); | ||
128 | list_for_each_entry_safe(mode, t, &connector->modes, head) { | ||
129 | mode->vrefresh = drm_mode_vrefresh(mode); | ||
130 | |||
131 | drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V); | ||
132 | drm_mode_debug_printmodeline(mode); | ||
133 | } | ||
134 | } | ||
135 | EXPORT_SYMBOL(drm_helper_probe_single_connector_modes); | ||
136 | |||
137 | void drm_helper_probe_connector_modes(struct drm_device *dev, uint32_t maxX, | ||
138 | uint32_t maxY) | ||
139 | { | ||
140 | struct drm_connector *connector; | ||
141 | |||
142 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { | ||
143 | drm_helper_probe_single_connector_modes(connector, maxX, maxY); | ||
144 | } | ||
145 | } | ||
146 | EXPORT_SYMBOL(drm_helper_probe_connector_modes); | ||
147 | |||
148 | |||
149 | /** | ||
150 | * drm_helper_crtc_in_use - check if a given CRTC is in a mode_config | ||
151 | * @crtc: CRTC to check | ||
152 | * | ||
153 | * LOCKING: | ||
154 | * Caller must hold mode config lock. | ||
155 | * | ||
156 | * Walk @crtc's DRM device's mode_config and see if it's in use. | ||
157 | * | ||
158 | * RETURNS: | ||
159 | * True if @crtc is part of the mode_config, false otherwise. | ||
160 | */ | ||
161 | bool drm_helper_crtc_in_use(struct drm_crtc *crtc) | ||
162 | { | ||
163 | struct drm_encoder *encoder; | ||
164 | struct drm_device *dev = crtc->dev; | ||
165 | /* FIXME: Locking around list access? */ | ||
166 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) | ||
167 | if (encoder->crtc == crtc) | ||
168 | return true; | ||
169 | return false; | ||
170 | } | ||
171 | EXPORT_SYMBOL(drm_helper_crtc_in_use); | ||
172 | |||
173 | /** | ||
174 | * drm_disable_unused_functions - disable unused objects | ||
175 | * @dev: DRM device | ||
176 | * | ||
177 | * LOCKING: | ||
178 | * Caller must hold mode config lock. | ||
179 | * | ||
180 | * If an connector or CRTC isn't part of @dev's mode_config, it can be disabled | ||
181 | * by calling its dpms function, which should power it off. | ||
182 | */ | ||
183 | void drm_helper_disable_unused_functions(struct drm_device *dev) | ||
184 | { | ||
185 | struct drm_encoder *encoder; | ||
186 | struct drm_encoder_helper_funcs *encoder_funcs; | ||
187 | struct drm_crtc *crtc; | ||
188 | |||
189 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { | ||
190 | encoder_funcs = encoder->helper_private; | ||
191 | if (!encoder->crtc) | ||
192 | (*encoder_funcs->dpms)(encoder, DRM_MODE_DPMS_OFF); | ||
193 | } | ||
194 | |||
195 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { | ||
196 | struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; | ||
197 | crtc->enabled = drm_helper_crtc_in_use(crtc); | ||
198 | if (!crtc->enabled) { | ||
199 | crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF); | ||
200 | crtc->fb = NULL; | ||
201 | } | ||
202 | } | ||
203 | } | ||
204 | EXPORT_SYMBOL(drm_helper_disable_unused_functions); | ||
205 | |||
206 | static struct drm_display_mode *drm_has_preferred_mode(struct drm_connector *connector, int width, int height) | ||
207 | { | ||
208 | struct drm_display_mode *mode; | ||
209 | |||
210 | list_for_each_entry(mode, &connector->modes, head) { | ||
211 | if (drm_mode_width(mode) > width || | ||
212 | drm_mode_height(mode) > height) | ||
213 | continue; | ||
214 | if (mode->type & DRM_MODE_TYPE_PREFERRED) | ||
215 | return mode; | ||
216 | } | ||
217 | return NULL; | ||
218 | } | ||
219 | |||
220 | static bool drm_connector_enabled(struct drm_connector *connector, bool strict) | ||
221 | { | ||
222 | bool enable; | ||
223 | |||
224 | if (strict) { | ||
225 | enable = connector->status == connector_status_connected; | ||
226 | } else { | ||
227 | enable = connector->status != connector_status_disconnected; | ||
228 | } | ||
229 | return enable; | ||
230 | } | ||
231 | |||
232 | static void drm_enable_connectors(struct drm_device *dev, bool *enabled) | ||
233 | { | ||
234 | bool any_enabled = false; | ||
235 | struct drm_connector *connector; | ||
236 | int i = 0; | ||
237 | |||
238 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { | ||
239 | enabled[i] = drm_connector_enabled(connector, true); | ||
240 | any_enabled |= enabled[i]; | ||
241 | i++; | ||
242 | } | ||
243 | |||
244 | if (any_enabled) | ||
245 | return; | ||
246 | |||
247 | i = 0; | ||
248 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { | ||
249 | enabled[i] = drm_connector_enabled(connector, false); | ||
250 | i++; | ||
251 | } | ||
252 | } | ||
253 | |||
254 | static bool drm_target_preferred(struct drm_device *dev, | ||
255 | struct drm_display_mode **modes, | ||
256 | bool *enabled, int width, int height) | ||
257 | { | ||
258 | struct drm_connector *connector; | ||
259 | int i = 0; | ||
260 | |||
261 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { | ||
262 | |||
263 | if (enabled[i] == false) { | ||
264 | i++; | ||
265 | continue; | ||
266 | } | ||
267 | |||
268 | modes[i] = drm_has_preferred_mode(connector, width, height); | ||
269 | if (!modes[i]) { | ||
270 | list_for_each_entry(modes[i], &connector->modes, head) | ||
271 | break; | ||
272 | } | ||
273 | i++; | ||
274 | } | ||
275 | return true; | ||
276 | } | ||
277 | |||
278 | static int drm_pick_crtcs(struct drm_device *dev, | ||
279 | struct drm_crtc **best_crtcs, | ||
280 | struct drm_display_mode **modes, | ||
281 | int n, int width, int height) | ||
282 | { | ||
283 | int c, o; | ||
284 | struct drm_connector *connector; | ||
285 | struct drm_connector_helper_funcs *connector_funcs; | ||
286 | struct drm_encoder *encoder; | ||
287 | struct drm_crtc *best_crtc; | ||
288 | int my_score, best_score, score; | ||
289 | struct drm_crtc **crtcs, *crtc; | ||
290 | |||
291 | if (n == dev->mode_config.num_connector) | ||
292 | return 0; | ||
293 | c = 0; | ||
294 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { | ||
295 | if (c == n) | ||
296 | break; | ||
297 | c++; | ||
298 | } | ||
299 | |||
300 | best_crtcs[n] = NULL; | ||
301 | best_crtc = NULL; | ||
302 | best_score = drm_pick_crtcs(dev, best_crtcs, modes, n+1, width, height); | ||
303 | if (modes[n] == NULL) | ||
304 | return best_score; | ||
305 | |||
306 | crtcs = kmalloc(dev->mode_config.num_connector * | ||
307 | sizeof(struct drm_crtc *), GFP_KERNEL); | ||
308 | if (!crtcs) | ||
309 | return best_score; | ||
310 | |||
311 | my_score = 1; | ||
312 | if (connector->status == connector_status_connected) | ||
313 | my_score++; | ||
314 | if (drm_has_preferred_mode(connector, width, height)) | ||
315 | my_score++; | ||
316 | |||
317 | connector_funcs = connector->helper_private; | ||
318 | encoder = connector_funcs->best_encoder(connector); | ||
319 | if (!encoder) | ||
320 | goto out; | ||
321 | |||
322 | connector->encoder = encoder; | ||
323 | |||
324 | /* select a crtc for this connector and then attempt to configure | ||
325 | remaining connectors */ | ||
326 | c = 0; | ||
327 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { | ||
328 | |||
329 | if ((connector->encoder->possible_crtcs & (1 << c)) == 0) { | ||
330 | c++; | ||
331 | continue; | ||
332 | } | ||
333 | |||
334 | for (o = 0; o < n; o++) | ||
335 | if (best_crtcs[o] == crtc) | ||
336 | break; | ||
337 | |||
338 | if (o < n) { | ||
339 | /* ignore cloning for now */ | ||
340 | c++; | ||
341 | continue; | ||
342 | } | ||
343 | |||
344 | crtcs[n] = crtc; | ||
345 | memcpy(crtcs, best_crtcs, n * sizeof(struct drm_crtc *)); | ||
346 | score = my_score + drm_pick_crtcs(dev, crtcs, modes, n + 1, | ||
347 | width, height); | ||
348 | if (score > best_score) { | ||
349 | best_crtc = crtc; | ||
350 | best_score = score; | ||
351 | memcpy(best_crtcs, crtcs, | ||
352 | dev->mode_config.num_connector * | ||
353 | sizeof(struct drm_crtc *)); | ||
354 | } | ||
355 | c++; | ||
356 | } | ||
357 | out: | ||
358 | kfree(crtcs); | ||
359 | return best_score; | ||
360 | } | ||
361 | |||
362 | static void drm_setup_crtcs(struct drm_device *dev) | ||
363 | { | ||
364 | struct drm_crtc **crtcs; | ||
365 | struct drm_display_mode **modes; | ||
366 | struct drm_encoder *encoder; | ||
367 | struct drm_connector *connector; | ||
368 | bool *enabled; | ||
369 | int width, height; | ||
370 | int i, ret; | ||
371 | |||
372 | width = dev->mode_config.max_width; | ||
373 | height = dev->mode_config.max_height; | ||
374 | |||
375 | /* clean out all the encoder/crtc combos */ | ||
376 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { | ||
377 | encoder->crtc = NULL; | ||
378 | } | ||
379 | |||
380 | crtcs = kcalloc(dev->mode_config.num_connector, | ||
381 | sizeof(struct drm_crtc *), GFP_KERNEL); | ||
382 | modes = kcalloc(dev->mode_config.num_connector, | ||
383 | sizeof(struct drm_display_mode *), GFP_KERNEL); | ||
384 | enabled = kcalloc(dev->mode_config.num_connector, | ||
385 | sizeof(bool), GFP_KERNEL); | ||
386 | |||
387 | drm_enable_connectors(dev, enabled); | ||
388 | |||
389 | ret = drm_target_preferred(dev, modes, enabled, width, height); | ||
390 | if (!ret) | ||
391 | DRM_ERROR("Unable to find initial modes\n"); | ||
392 | |||
393 | drm_pick_crtcs(dev, crtcs, modes, 0, width, height); | ||
394 | |||
395 | i = 0; | ||
396 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { | ||
397 | struct drm_display_mode *mode = modes[i]; | ||
398 | struct drm_crtc *crtc = crtcs[i]; | ||
399 | |||
400 | if (connector->encoder == NULL) { | ||
401 | i++; | ||
402 | continue; | ||
403 | } | ||
404 | |||
405 | if (mode && crtc) { | ||
406 | crtc->desired_mode = mode; | ||
407 | connector->encoder->crtc = crtc; | ||
408 | } else | ||
409 | connector->encoder->crtc = NULL; | ||
410 | i++; | ||
411 | } | ||
412 | |||
413 | kfree(crtcs); | ||
414 | kfree(modes); | ||
415 | kfree(enabled); | ||
416 | } | ||
417 | /** | ||
418 | * drm_crtc_set_mode - set a mode | ||
419 | * @crtc: CRTC to program | ||
420 | * @mode: mode to use | ||
421 | * @x: width of mode | ||
422 | * @y: height of mode | ||
423 | * | ||
424 | * LOCKING: | ||
425 | * Caller must hold mode config lock. | ||
426 | * | ||
427 | * Try to set @mode on @crtc. Give @crtc and its associated connectors a chance | ||
428 | * to fixup or reject the mode prior to trying to set it. | ||
429 | * | ||
430 | * RETURNS: | ||
431 | * True if the mode was set successfully, or false otherwise. | ||
432 | */ | ||
433 | bool drm_crtc_helper_set_mode(struct drm_crtc *crtc, | ||
434 | struct drm_display_mode *mode, | ||
435 | int x, int y) | ||
436 | { | ||
437 | struct drm_device *dev = crtc->dev; | ||
438 | struct drm_display_mode *adjusted_mode, saved_mode; | ||
439 | struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; | ||
440 | struct drm_encoder_helper_funcs *encoder_funcs; | ||
441 | int saved_x, saved_y; | ||
442 | struct drm_encoder *encoder; | ||
443 | bool ret = true; | ||
444 | |||
445 | adjusted_mode = drm_mode_duplicate(dev, mode); | ||
446 | |||
447 | crtc->enabled = drm_helper_crtc_in_use(crtc); | ||
448 | |||
449 | if (!crtc->enabled) | ||
450 | return true; | ||
451 | |||
452 | saved_mode = crtc->mode; | ||
453 | saved_x = crtc->x; | ||
454 | saved_y = crtc->y; | ||
455 | |||
456 | /* Update crtc values up front so the driver can rely on them for mode | ||
457 | * setting. | ||
458 | */ | ||
459 | crtc->mode = *mode; | ||
460 | crtc->x = x; | ||
461 | crtc->y = y; | ||
462 | |||
463 | if (drm_mode_equal(&saved_mode, &crtc->mode)) { | ||
464 | if (saved_x != crtc->x || saved_y != crtc->y) { | ||
465 | crtc_funcs->mode_set_base(crtc, crtc->x, crtc->y); | ||
466 | goto done; | ||
467 | } | ||
468 | } | ||
469 | |||
470 | /* Pass our mode to the connectors and the CRTC to give them a chance to | ||
471 | * adjust it according to limitations or connector properties, and also | ||
472 | * a chance to reject the mode entirely. | ||
473 | */ | ||
474 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { | ||
475 | |||
476 | if (encoder->crtc != crtc) | ||
477 | continue; | ||
478 | encoder_funcs = encoder->helper_private; | ||
479 | if (!(ret = encoder_funcs->mode_fixup(encoder, mode, | ||
480 | adjusted_mode))) { | ||
481 | goto done; | ||
482 | } | ||
483 | } | ||
484 | |||
485 | if (!(ret = crtc_funcs->mode_fixup(crtc, mode, adjusted_mode))) { | ||
486 | goto done; | ||
487 | } | ||
488 | |||
489 | /* Prepare the encoders and CRTCs before setting the mode. */ | ||
490 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { | ||
491 | |||
492 | if (encoder->crtc != crtc) | ||
493 | continue; | ||
494 | encoder_funcs = encoder->helper_private; | ||
495 | /* Disable the encoders as the first thing we do. */ | ||
496 | encoder_funcs->prepare(encoder); | ||
497 | } | ||
498 | |||
499 | crtc_funcs->prepare(crtc); | ||
500 | |||
501 | /* Set up the DPLL and any encoders state that needs to adjust or depend | ||
502 | * on the DPLL. | ||
503 | */ | ||
504 | crtc_funcs->mode_set(crtc, mode, adjusted_mode, x, y); | ||
505 | |||
506 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { | ||
507 | |||
508 | if (encoder->crtc != crtc) | ||
509 | continue; | ||
510 | |||
511 | DRM_INFO("%s: set mode %s %x\n", drm_get_encoder_name(encoder), | ||
512 | mode->name, mode->base.id); | ||
513 | encoder_funcs = encoder->helper_private; | ||
514 | encoder_funcs->mode_set(encoder, mode, adjusted_mode); | ||
515 | } | ||
516 | |||
517 | /* Now enable the clocks, plane, pipe, and connectors that we set up. */ | ||
518 | crtc_funcs->commit(crtc); | ||
519 | |||
520 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { | ||
521 | |||
522 | if (encoder->crtc != crtc) | ||
523 | continue; | ||
524 | |||
525 | encoder_funcs = encoder->helper_private; | ||
526 | encoder_funcs->commit(encoder); | ||
527 | |||
528 | } | ||
529 | |||
530 | /* XXX free adjustedmode */ | ||
531 | drm_mode_destroy(dev, adjusted_mode); | ||
532 | /* FIXME: add subpixel order */ | ||
533 | done: | ||
534 | if (!ret) { | ||
535 | crtc->mode = saved_mode; | ||
536 | crtc->x = saved_x; | ||
537 | crtc->y = saved_y; | ||
538 | } | ||
539 | |||
540 | return ret; | ||
541 | } | ||
542 | EXPORT_SYMBOL(drm_crtc_helper_set_mode); | ||
543 | |||
544 | |||
545 | /** | ||
546 | * drm_crtc_helper_set_config - set a new config from userspace | ||
547 | * @crtc: CRTC to setup | ||
548 | * @crtc_info: user provided configuration | ||
549 | * @new_mode: new mode to set | ||
550 | * @connector_set: set of connectors for the new config | ||
551 | * @fb: new framebuffer | ||
552 | * | ||
553 | * LOCKING: | ||
554 | * Caller must hold mode config lock. | ||
555 | * | ||
556 | * Setup a new configuration, provided by the user in @crtc_info, and enable | ||
557 | * it. | ||
558 | * | ||
559 | * RETURNS: | ||
560 | * Zero. (FIXME) | ||
561 | */ | ||
562 | int drm_crtc_helper_set_config(struct drm_mode_set *set) | ||
563 | { | ||
564 | struct drm_device *dev; | ||
565 | struct drm_crtc **save_crtcs, *new_crtc; | ||
566 | struct drm_encoder **save_encoders, *new_encoder; | ||
567 | bool save_enabled; | ||
568 | bool changed = false; | ||
569 | bool flip_or_move = false; | ||
570 | struct drm_connector *connector; | ||
571 | int count = 0, ro, fail = 0; | ||
572 | struct drm_crtc_helper_funcs *crtc_funcs; | ||
573 | int ret = 0; | ||
574 | |||
575 | DRM_DEBUG("\n"); | ||
576 | |||
577 | if (!set) | ||
578 | return -EINVAL; | ||
579 | |||
580 | if (!set->crtc) | ||
581 | return -EINVAL; | ||
582 | |||
583 | if (!set->crtc->helper_private) | ||
584 | return -EINVAL; | ||
585 | |||
586 | crtc_funcs = set->crtc->helper_private; | ||
587 | |||
588 | DRM_DEBUG("crtc: %p %d fb: %p connectors: %p num_connectors: %d (x, y) (%i, %i)\n", | ||
589 | set->crtc, set->crtc->base.id, set->fb, set->connectors, | ||
590 | (int)set->num_connectors, set->x, set->y); | ||
591 | |||
592 | dev = set->crtc->dev; | ||
593 | |||
594 | /* save previous config */ | ||
595 | save_enabled = set->crtc->enabled; | ||
596 | |||
597 | /* this is meant to be num_connector not num_crtc */ | ||
598 | save_crtcs = kzalloc(dev->mode_config.num_connector * | ||
599 | sizeof(struct drm_crtc *), GFP_KERNEL); | ||
600 | if (!save_crtcs) | ||
601 | return -ENOMEM; | ||
602 | |||
603 | save_encoders = kzalloc(dev->mode_config.num_connector * | ||
604 | sizeof(struct drm_encoders *), GFP_KERNEL); | ||
605 | if (!save_encoders) { | ||
606 | kfree(save_crtcs); | ||
607 | return -ENOMEM; | ||
608 | } | ||
609 | |||
610 | /* We should be able to check here if the fb has the same properties | ||
611 | * and then just flip_or_move it */ | ||
612 | if (set->crtc->fb != set->fb) { | ||
613 | /* if we have no fb then its a change not a flip */ | ||
614 | if (set->crtc->fb == NULL) | ||
615 | changed = true; | ||
616 | else | ||
617 | flip_or_move = true; | ||
618 | } | ||
619 | |||
620 | if (set->x != set->crtc->x || set->y != set->crtc->y) | ||
621 | flip_or_move = true; | ||
622 | |||
623 | if (set->mode && !drm_mode_equal(set->mode, &set->crtc->mode)) { | ||
624 | DRM_DEBUG("modes are different\n"); | ||
625 | drm_mode_debug_printmodeline(&set->crtc->mode); | ||
626 | drm_mode_debug_printmodeline(set->mode); | ||
627 | changed = true; | ||
628 | } | ||
629 | |||
630 | /* a) traverse passed in connector list and get encoders for them */ | ||
631 | count = 0; | ||
632 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { | ||
633 | struct drm_connector_helper_funcs *connector_funcs = | ||
634 | connector->helper_private; | ||
635 | save_encoders[count++] = connector->encoder; | ||
636 | new_encoder = connector->encoder; | ||
637 | for (ro = 0; ro < set->num_connectors; ro++) { | ||
638 | if (set->connectors[ro] == connector) { | ||
639 | new_encoder = connector_funcs->best_encoder(connector); | ||
640 | /* if we can't get an encoder for a connector | ||
641 | we are setting now - then fail */ | ||
642 | if (new_encoder == NULL) | ||
643 | /* don't break so fail path works correct */ | ||
644 | fail = 1; | ||
645 | break; | ||
646 | } | ||
647 | } | ||
648 | |||
649 | if (new_encoder != connector->encoder) { | ||
650 | changed = true; | ||
651 | connector->encoder = new_encoder; | ||
652 | } | ||
653 | } | ||
654 | |||
655 | if (fail) { | ||
656 | ret = -EINVAL; | ||
657 | goto fail_no_encoder; | ||
658 | } | ||
659 | |||
660 | count = 0; | ||
661 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { | ||
662 | if (!connector->encoder) | ||
663 | continue; | ||
664 | |||
665 | save_crtcs[count++] = connector->encoder->crtc; | ||
666 | |||
667 | if (connector->encoder->crtc == set->crtc) | ||
668 | new_crtc = NULL; | ||
669 | else | ||
670 | new_crtc = connector->encoder->crtc; | ||
671 | |||
672 | for (ro = 0; ro < set->num_connectors; ro++) { | ||
673 | if (set->connectors[ro] == connector) | ||
674 | new_crtc = set->crtc; | ||
675 | } | ||
676 | if (new_crtc != connector->encoder->crtc) { | ||
677 | changed = true; | ||
678 | connector->encoder->crtc = new_crtc; | ||
679 | } | ||
680 | } | ||
681 | |||
682 | /* mode_set_base is not a required function */ | ||
683 | if (flip_or_move && !crtc_funcs->mode_set_base) | ||
684 | changed = true; | ||
685 | |||
686 | if (changed) { | ||
687 | set->crtc->fb = set->fb; | ||
688 | set->crtc->enabled = (set->mode != NULL); | ||
689 | if (set->mode != NULL) { | ||
690 | DRM_DEBUG("attempting to set mode from userspace\n"); | ||
691 | drm_mode_debug_printmodeline(set->mode); | ||
692 | if (!drm_crtc_helper_set_mode(set->crtc, set->mode, | ||
693 | set->x, set->y)) { | ||
694 | ret = -EINVAL; | ||
695 | goto fail_set_mode; | ||
696 | } | ||
697 | /* TODO are these needed? */ | ||
698 | set->crtc->desired_x = set->x; | ||
699 | set->crtc->desired_y = set->y; | ||
700 | set->crtc->desired_mode = set->mode; | ||
701 | } | ||
702 | drm_helper_disable_unused_functions(dev); | ||
703 | } else if (flip_or_move) { | ||
704 | if (set->crtc->fb != set->fb) | ||
705 | set->crtc->fb = set->fb; | ||
706 | crtc_funcs->mode_set_base(set->crtc, set->x, set->y); | ||
707 | } | ||
708 | |||
709 | kfree(save_encoders); | ||
710 | kfree(save_crtcs); | ||
711 | return 0; | ||
712 | |||
713 | fail_set_mode: | ||
714 | set->crtc->enabled = save_enabled; | ||
715 | count = 0; | ||
716 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) | ||
717 | connector->encoder->crtc = save_crtcs[count++]; | ||
718 | fail_no_encoder: | ||
719 | kfree(save_crtcs); | ||
720 | count = 0; | ||
721 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { | ||
722 | connector->encoder = save_encoders[count++]; | ||
723 | } | ||
724 | kfree(save_encoders); | ||
725 | return ret; | ||
726 | } | ||
727 | EXPORT_SYMBOL(drm_crtc_helper_set_config); | ||
728 | |||
729 | bool drm_helper_plugged_event(struct drm_device *dev) | ||
730 | { | ||
731 | DRM_DEBUG("\n"); | ||
732 | |||
733 | drm_helper_probe_connector_modes(dev, dev->mode_config.max_width, | ||
734 | dev->mode_config.max_height); | ||
735 | |||
736 | drm_setup_crtcs(dev); | ||
737 | |||
738 | /* alert the driver fb layer */ | ||
739 | dev->mode_config.funcs->fb_changed(dev); | ||
740 | |||
741 | /* FIXME: send hotplug event */ | ||
742 | return true; | ||
743 | } | ||
744 | /** | ||
745 | * drm_initial_config - setup a sane initial connector configuration | ||
746 | * @dev: DRM device | ||
747 | * @can_grow: this configuration is growable | ||
748 | * | ||
749 | * LOCKING: | ||
750 | * Called at init time, must take mode config lock. | ||
751 | * | ||
752 | * Scan the CRTCs and connectors and try to put together an initial setup. | ||
753 | * At the moment, this is a cloned configuration across all heads with | ||
754 | * a new framebuffer object as the backing store. | ||
755 | * | ||
756 | * RETURNS: | ||
757 | * Zero if everything went ok, nonzero otherwise. | ||
758 | */ | ||
759 | bool drm_helper_initial_config(struct drm_device *dev, bool can_grow) | ||
760 | { | ||
761 | int ret = false; | ||
762 | |||
763 | drm_helper_plugged_event(dev); | ||
764 | return ret; | ||
765 | } | ||
766 | EXPORT_SYMBOL(drm_helper_initial_config); | ||
767 | |||
768 | /** | ||
769 | * drm_hotplug_stage_two | ||
770 | * @dev DRM device | ||
771 | * @connector hotpluged connector | ||
772 | * | ||
773 | * LOCKING. | ||
774 | * Caller must hold mode config lock, function might grab struct lock. | ||
775 | * | ||
776 | * Stage two of a hotplug. | ||
777 | * | ||
778 | * RETURNS: | ||
779 | * Zero on success, errno on failure. | ||
780 | */ | ||
781 | int drm_helper_hotplug_stage_two(struct drm_device *dev) | ||
782 | { | ||
783 | dev->mode_config.hotplug_counter++; | ||
784 | |||
785 | drm_helper_plugged_event(dev); | ||
786 | |||
787 | return 0; | ||
788 | } | ||
789 | EXPORT_SYMBOL(drm_helper_hotplug_stage_two); | ||
790 | |||
791 | int drm_helper_mode_fill_fb_struct(struct drm_framebuffer *fb, | ||
792 | struct drm_mode_fb_cmd *mode_cmd) | ||
793 | { | ||
794 | fb->width = mode_cmd->width; | ||
795 | fb->height = mode_cmd->height; | ||
796 | fb->pitch = mode_cmd->pitch; | ||
797 | fb->bits_per_pixel = mode_cmd->bpp; | ||
798 | fb->depth = mode_cmd->depth; | ||
799 | |||
800 | return 0; | ||
801 | } | ||
802 | EXPORT_SYMBOL(drm_helper_mode_fill_fb_struct); | ||
803 | |||
804 | int drm_helper_resume_force_mode(struct drm_device *dev) | ||
805 | { | ||
806 | struct drm_crtc *crtc; | ||
807 | int ret; | ||
808 | |||
809 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { | ||
810 | |||
811 | if (!crtc->enabled) | ||
812 | continue; | ||
813 | |||
814 | ret = drm_crtc_helper_set_mode(crtc, &crtc->mode, crtc->x, | ||
815 | crtc->y); | ||
816 | |||
817 | if (ret == false) | ||
818 | DRM_ERROR("failed to set mode on crtc %p\n", crtc); | ||
819 | } | ||
820 | return 0; | ||
821 | } | ||
822 | EXPORT_SYMBOL(drm_helper_resume_force_mode); | ||
diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index 98a781375f60..0b9f3164a3b2 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c | |||
@@ -126,6 +126,26 @@ static struct drm_ioctl_desc drm_ioctls[] = { | |||
126 | DRM_IOCTL_DEF(DRM_IOCTL_GEM_CLOSE, drm_gem_close_ioctl, 0), | 126 | DRM_IOCTL_DEF(DRM_IOCTL_GEM_CLOSE, drm_gem_close_ioctl, 0), |
127 | DRM_IOCTL_DEF(DRM_IOCTL_GEM_FLINK, drm_gem_flink_ioctl, DRM_AUTH), | 127 | DRM_IOCTL_DEF(DRM_IOCTL_GEM_FLINK, drm_gem_flink_ioctl, DRM_AUTH), |
128 | DRM_IOCTL_DEF(DRM_IOCTL_GEM_OPEN, drm_gem_open_ioctl, DRM_AUTH), | 128 | DRM_IOCTL_DEF(DRM_IOCTL_GEM_OPEN, drm_gem_open_ioctl, DRM_AUTH), |
129 | |||
130 | DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETRESOURCES, drm_mode_getresources, DRM_MASTER|DRM_CONTROL_ALLOW), | ||
131 | DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETCRTC, drm_mode_getcrtc, DRM_MASTER|DRM_CONTROL_ALLOW), | ||
132 | DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETCONNECTOR, drm_mode_getconnector, DRM_MASTER|DRM_CONTROL_ALLOW), | ||
133 | DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETCRTC, drm_mode_setcrtc, DRM_MASTER|DRM_CONTROL_ALLOW), | ||
134 | DRM_IOCTL_DEF(DRM_IOCTL_MODE_CURSOR, drm_mode_cursor_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW), | ||
135 | DRM_IOCTL_DEF(DRM_IOCTL_MODE_ADDFB, drm_mode_addfb, DRM_MASTER|DRM_CONTROL_ALLOW), | ||
136 | DRM_IOCTL_DEF(DRM_IOCTL_MODE_RMFB, drm_mode_rmfb, DRM_MASTER|DRM_CONTROL_ALLOW), | ||
137 | DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETFB, drm_mode_getfb, DRM_MASTER|DRM_CONTROL_ALLOW), | ||
138 | |||
139 | DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETPROPERTY, drm_mode_connector_property_set_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW), | ||
140 | DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPROPBLOB, drm_mode_getblob_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW), | ||
141 | DRM_IOCTL_DEF(DRM_IOCTL_MODE_ATTACHMODE, drm_mode_attachmode_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW), | ||
142 | DRM_IOCTL_DEF(DRM_IOCTL_MODE_DETACHMODE, drm_mode_detachmode_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW), | ||
143 | DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPROPERTY, drm_mode_getproperty_ioctl, DRM_MASTER | DRM_CONTROL_ALLOW), | ||
144 | |||
145 | DRM_IOCTL_DEF(DRM_IOCTL_MODE_REPLACEFB, drm_mode_replacefb, DRM_MASTER|DRM_ROOT_ONLY|DRM_CONTROL_ALLOW), | ||
146 | DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETENCODER, drm_mode_getencoder, DRM_MASTER|DRM_CONTROL_ALLOW), | ||
147 | DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETGAMMA, drm_mode_gamma_get_ioctl, DRM_MASTER), | ||
148 | DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETGAMMA, drm_mode_gamma_set_ioctl, DRM_MASTER), | ||
129 | }; | 149 | }; |
130 | 150 | ||
131 | #define DRM_CORE_IOCTL_COUNT ARRAY_SIZE( drm_ioctls ) | 151 | #define DRM_CORE_IOCTL_COUNT ARRAY_SIZE( drm_ioctls ) |
@@ -150,7 +170,7 @@ int drm_lastclose(struct drm_device * dev) | |||
150 | dev->driver->lastclose(dev); | 170 | dev->driver->lastclose(dev); |
151 | DRM_DEBUG("driver lastclose completed\n"); | 171 | DRM_DEBUG("driver lastclose completed\n"); |
152 | 172 | ||
153 | if (dev->irq_enabled) | 173 | if (dev->irq_enabled && !drm_core_check_feature(dev, DRIVER_MODESET)) |
154 | drm_irq_uninstall(dev); | 174 | drm_irq_uninstall(dev); |
155 | 175 | ||
156 | mutex_lock(&dev->struct_mutex); | 176 | mutex_lock(&dev->struct_mutex); |
@@ -160,7 +180,8 @@ int drm_lastclose(struct drm_device * dev) | |||
160 | del_timer(&dev->timer); | 180 | del_timer(&dev->timer); |
161 | 181 | ||
162 | /* Clear AGP information */ | 182 | /* Clear AGP information */ |
163 | if (drm_core_has_AGP(dev) && dev->agp) { | 183 | if (drm_core_has_AGP(dev) && dev->agp && |
184 | !drm_core_check_feature(dev, DRIVER_MODESET)) { | ||
164 | struct drm_agp_mem *entry, *tempe; | 185 | struct drm_agp_mem *entry, *tempe; |
165 | 186 | ||
166 | /* Remove AGP resources, but leave dev->agp | 187 | /* Remove AGP resources, but leave dev->agp |
@@ -179,7 +200,8 @@ int drm_lastclose(struct drm_device * dev) | |||
179 | dev->agp->acquired = 0; | 200 | dev->agp->acquired = 0; |
180 | dev->agp->enabled = 0; | 201 | dev->agp->enabled = 0; |
181 | } | 202 | } |
182 | if (drm_core_check_feature(dev, DRIVER_SG) && dev->sg) { | 203 | if (drm_core_check_feature(dev, DRIVER_SG) && dev->sg && |
204 | !drm_core_check_feature(dev, DRIVER_MODESET)) { | ||
183 | drm_sg_cleanup(dev->sg); | 205 | drm_sg_cleanup(dev->sg); |
184 | dev->sg = NULL; | 206 | dev->sg = NULL; |
185 | } | 207 | } |
@@ -206,7 +228,8 @@ int drm_lastclose(struct drm_device * dev) | |||
206 | } | 228 | } |
207 | dev->queue_count = 0; | 229 | dev->queue_count = 0; |
208 | 230 | ||
209 | if (drm_core_check_feature(dev, DRIVER_HAVE_DMA)) | 231 | if (drm_core_check_feature(dev, DRIVER_HAVE_DMA) && |
232 | !drm_core_check_feature(dev, DRIVER_MODESET)) | ||
210 | drm_dma_takedown(dev); | 233 | drm_dma_takedown(dev); |
211 | 234 | ||
212 | dev->dev_mapping = NULL; | 235 | dev->dev_mapping = NULL; |
@@ -307,6 +330,9 @@ static void drm_cleanup(struct drm_device * dev) | |||
307 | drm_ht_remove(&dev->map_hash); | 330 | drm_ht_remove(&dev->map_hash); |
308 | drm_ctxbitmap_cleanup(dev); | 331 | drm_ctxbitmap_cleanup(dev); |
309 | 332 | ||
333 | if (drm_core_check_feature(dev, DRIVER_MODESET)) | ||
334 | drm_put_minor(&dev->control); | ||
335 | |||
310 | if (driver->driver_features & DRIVER_GEM) | 336 | if (driver->driver_features & DRIVER_GEM) |
311 | drm_gem_destroy(dev); | 337 | drm_gem_destroy(dev); |
312 | 338 | ||
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c new file mode 100644 index 000000000000..681753e57dc7 --- /dev/null +++ b/drivers/gpu/drm/drm_edid.c | |||
@@ -0,0 +1,732 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2006 Luc Verhaegen (quirks list) | ||
3 | * Copyright (c) 2007-2008 Intel Corporation | ||
4 | * Jesse Barnes <jesse.barnes@intel.com> | ||
5 | * | ||
6 | * DDC probing routines (drm_ddc_read & drm_do_probe_ddc_edid) originally from | ||
7 | * FB layer. | ||
8 | * Copyright (C) 2006 Dennis Munsie <dmunsie@cecropia.com> | ||
9 | * | ||
10 | * Permission is hereby granted, free of charge, to any person obtaining a | ||
11 | * copy of this software and associated documentation files (the "Software"), | ||
12 | * to deal in the Software without restriction, including without limitation | ||
13 | * the rights to use, copy, modify, merge, publish, distribute, sub license, | ||
14 | * and/or sell copies of the Software, and to permit persons to whom the | ||
15 | * Software is furnished to do so, subject to the following conditions: | ||
16 | * | ||
17 | * The above copyright notice and this permission notice (including the | ||
18 | * next paragraph) shall be included in all copies or substantial portions | ||
19 | * of the Software. | ||
20 | * | ||
21 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
22 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
23 | * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL | ||
24 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
25 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||
26 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | ||
27 | * DEALINGS IN THE SOFTWARE. | ||
28 | */ | ||
29 | #include <linux/kernel.h> | ||
30 | #include <linux/i2c.h> | ||
31 | #include <linux/i2c-algo-bit.h> | ||
32 | #include "drmP.h" | ||
33 | #include "drm_edid.h" | ||
34 | |||
35 | /* | ||
36 | * TODO: | ||
37 | * - support EDID 1.4 (incl. CE blocks) | ||
38 | */ | ||
39 | |||
40 | /* | ||
41 | * EDID blocks out in the wild have a variety of bugs, try to collect | ||
42 | * them here (note that userspace may work around broken monitors first, | ||
43 | * but fixes should make their way here so that the kernel "just works" | ||
44 | * on as many displays as possible). | ||
45 | */ | ||
46 | |||
47 | /* First detailed mode wrong, use largest 60Hz mode */ | ||
48 | #define EDID_QUIRK_PREFER_LARGE_60 (1 << 0) | ||
49 | /* Reported 135MHz pixel clock is too high, needs adjustment */ | ||
50 | #define EDID_QUIRK_135_CLOCK_TOO_HIGH (1 << 1) | ||
51 | /* Prefer the largest mode at 75 Hz */ | ||
52 | #define EDID_QUIRK_PREFER_LARGE_75 (1 << 2) | ||
53 | /* Detail timing is in cm not mm */ | ||
54 | #define EDID_QUIRK_DETAILED_IN_CM (1 << 3) | ||
55 | /* Detailed timing descriptors have bogus size values, so just take the | ||
56 | * maximum size and use that. | ||
57 | */ | ||
58 | #define EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE (1 << 4) | ||
59 | /* Monitor forgot to set the first detailed is preferred bit. */ | ||
60 | #define EDID_QUIRK_FIRST_DETAILED_PREFERRED (1 << 5) | ||
61 | /* use +hsync +vsync for detailed mode */ | ||
62 | #define EDID_QUIRK_DETAILED_SYNC_PP (1 << 6) | ||
63 | |||
64 | static struct edid_quirk { | ||
65 | char *vendor; | ||
66 | int product_id; | ||
67 | u32 quirks; | ||
68 | } edid_quirk_list[] = { | ||
69 | /* Acer AL1706 */ | ||
70 | { "ACR", 44358, EDID_QUIRK_PREFER_LARGE_60 }, | ||
71 | /* Acer F51 */ | ||
72 | { "API", 0x7602, EDID_QUIRK_PREFER_LARGE_60 }, | ||
73 | /* Unknown Acer */ | ||
74 | { "ACR", 2423, EDID_QUIRK_FIRST_DETAILED_PREFERRED }, | ||
75 | |||
76 | /* Belinea 10 15 55 */ | ||
77 | { "MAX", 1516, EDID_QUIRK_PREFER_LARGE_60 }, | ||
78 | { "MAX", 0x77e, EDID_QUIRK_PREFER_LARGE_60 }, | ||
79 | |||
80 | /* Envision Peripherals, Inc. EN-7100e */ | ||
81 | { "EPI", 59264, EDID_QUIRK_135_CLOCK_TOO_HIGH }, | ||
82 | |||
83 | /* Funai Electronics PM36B */ | ||
84 | { "FCM", 13600, EDID_QUIRK_PREFER_LARGE_75 | | ||
85 | EDID_QUIRK_DETAILED_IN_CM }, | ||
86 | |||
87 | /* LG Philips LCD LP154W01-A5 */ | ||
88 | { "LPL", 0, EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE }, | ||
89 | { "LPL", 0x2a00, EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE }, | ||
90 | |||
91 | /* Philips 107p5 CRT */ | ||
92 | { "PHL", 57364, EDID_QUIRK_FIRST_DETAILED_PREFERRED }, | ||
93 | |||
94 | /* Proview AY765C */ | ||
95 | { "PTS", 765, EDID_QUIRK_FIRST_DETAILED_PREFERRED }, | ||
96 | |||
97 | /* Samsung SyncMaster 205BW. Note: irony */ | ||
98 | { "SAM", 541, EDID_QUIRK_DETAILED_SYNC_PP }, | ||
99 | /* Samsung SyncMaster 22[5-6]BW */ | ||
100 | { "SAM", 596, EDID_QUIRK_PREFER_LARGE_60 }, | ||
101 | { "SAM", 638, EDID_QUIRK_PREFER_LARGE_60 }, | ||
102 | }; | ||
103 | |||
104 | |||
105 | /* Valid EDID header has these bytes */ | ||
106 | static u8 edid_header[] = { 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 }; | ||
107 | |||
108 | /** | ||
109 | * edid_is_valid - sanity check EDID data | ||
110 | * @edid: EDID data | ||
111 | * | ||
112 | * Sanity check the EDID block by looking at the header, the version number | ||
113 | * and the checksum. Return 0 if the EDID doesn't check out, or 1 if it's | ||
114 | * valid. | ||
115 | */ | ||
116 | static bool edid_is_valid(struct edid *edid) | ||
117 | { | ||
118 | int i; | ||
119 | u8 csum = 0; | ||
120 | u8 *raw_edid = (u8 *)edid; | ||
121 | |||
122 | if (memcmp(edid->header, edid_header, sizeof(edid_header))) | ||
123 | goto bad; | ||
124 | if (edid->version != 1) { | ||
125 | DRM_ERROR("EDID has major version %d, instead of 1\n", edid->version); | ||
126 | goto bad; | ||
127 | } | ||
128 | if (edid->revision <= 0 || edid->revision > 3) { | ||
129 | DRM_ERROR("EDID has minor version %d, which is not between 0-3\n", edid->revision); | ||
130 | goto bad; | ||
131 | } | ||
132 | |||
133 | for (i = 0; i < EDID_LENGTH; i++) | ||
134 | csum += raw_edid[i]; | ||
135 | if (csum) { | ||
136 | DRM_ERROR("EDID checksum is invalid, remainder is %d\n", csum); | ||
137 | goto bad; | ||
138 | } | ||
139 | |||
140 | return 1; | ||
141 | |||
142 | bad: | ||
143 | if (raw_edid) { | ||
144 | DRM_ERROR("Raw EDID:\n"); | ||
145 | print_hex_dump_bytes(KERN_ERR, DUMP_PREFIX_NONE, raw_edid, EDID_LENGTH); | ||
146 | printk("\n"); | ||
147 | } | ||
148 | return 0; | ||
149 | } | ||
150 | |||
151 | /** | ||
152 | * edid_vendor - match a string against EDID's obfuscated vendor field | ||
153 | * @edid: EDID to match | ||
154 | * @vendor: vendor string | ||
155 | * | ||
156 | * Returns true if @vendor is in @edid, false otherwise | ||
157 | */ | ||
158 | static bool edid_vendor(struct edid *edid, char *vendor) | ||
159 | { | ||
160 | char edid_vendor[3]; | ||
161 | |||
162 | edid_vendor[0] = ((edid->mfg_id[0] & 0x7c) >> 2) + '@'; | ||
163 | edid_vendor[1] = (((edid->mfg_id[0] & 0x3) << 3) | | ||
164 | ((edid->mfg_id[1] & 0xe0) >> 5)) + '@'; | ||
165 | edid_vendor[2] = (edid->mfg_id[2] & 0x1f) + '@'; | ||
166 | |||
167 | return !strncmp(edid_vendor, vendor, 3); | ||
168 | } | ||
169 | |||
170 | /** | ||
171 | * edid_get_quirks - return quirk flags for a given EDID | ||
172 | * @edid: EDID to process | ||
173 | * | ||
174 | * This tells subsequent routines what fixes they need to apply. | ||
175 | */ | ||
176 | static u32 edid_get_quirks(struct edid *edid) | ||
177 | { | ||
178 | struct edid_quirk *quirk; | ||
179 | int i; | ||
180 | |||
181 | for (i = 0; i < ARRAY_SIZE(edid_quirk_list); i++) { | ||
182 | quirk = &edid_quirk_list[i]; | ||
183 | |||
184 | if (edid_vendor(edid, quirk->vendor) && | ||
185 | (EDID_PRODUCT_ID(edid) == quirk->product_id)) | ||
186 | return quirk->quirks; | ||
187 | } | ||
188 | |||
189 | return 0; | ||
190 | } | ||
191 | |||
192 | #define MODE_SIZE(m) ((m)->hdisplay * (m)->vdisplay) | ||
193 | #define MODE_REFRESH_DIFF(m,r) (abs((m)->vrefresh - target_refresh)) | ||
194 | |||
195 | |||
196 | /** | ||
197 | * edid_fixup_preferred - set preferred modes based on quirk list | ||
198 | * @connector: has mode list to fix up | ||
199 | * @quirks: quirks list | ||
200 | * | ||
201 | * Walk the mode list for @connector, clearing the preferred status | ||
202 | * on existing modes and setting it anew for the right mode ala @quirks. | ||
203 | */ | ||
204 | static void edid_fixup_preferred(struct drm_connector *connector, | ||
205 | u32 quirks) | ||
206 | { | ||
207 | struct drm_display_mode *t, *cur_mode, *preferred_mode; | ||
208 | int target_refresh; | ||
209 | |||
210 | if (list_empty(&connector->probed_modes)) | ||
211 | return; | ||
212 | |||
213 | if (quirks & EDID_QUIRK_PREFER_LARGE_60) | ||
214 | target_refresh = 60; | ||
215 | if (quirks & EDID_QUIRK_PREFER_LARGE_75) | ||
216 | target_refresh = 75; | ||
217 | |||
218 | preferred_mode = list_first_entry(&connector->probed_modes, | ||
219 | struct drm_display_mode, head); | ||
220 | |||
221 | list_for_each_entry_safe(cur_mode, t, &connector->probed_modes, head) { | ||
222 | cur_mode->type &= ~DRM_MODE_TYPE_PREFERRED; | ||
223 | |||
224 | if (cur_mode == preferred_mode) | ||
225 | continue; | ||
226 | |||
227 | /* Largest mode is preferred */ | ||
228 | if (MODE_SIZE(cur_mode) > MODE_SIZE(preferred_mode)) | ||
229 | preferred_mode = cur_mode; | ||
230 | |||
231 | /* At a given size, try to get closest to target refresh */ | ||
232 | if ((MODE_SIZE(cur_mode) == MODE_SIZE(preferred_mode)) && | ||
233 | MODE_REFRESH_DIFF(cur_mode, target_refresh) < | ||
234 | MODE_REFRESH_DIFF(preferred_mode, target_refresh)) { | ||
235 | preferred_mode = cur_mode; | ||
236 | } | ||
237 | } | ||
238 | |||
239 | preferred_mode->type |= DRM_MODE_TYPE_PREFERRED; | ||
240 | } | ||
241 | |||
242 | /** | ||
243 | * drm_mode_std - convert standard mode info (width, height, refresh) into mode | ||
244 | * @t: standard timing params | ||
245 | * | ||
246 | * Take the standard timing params (in this case width, aspect, and refresh) | ||
247 | * and convert them into a real mode using CVT. | ||
248 | * | ||
249 | * Punts for now, but should eventually use the FB layer's CVT based mode | ||
250 | * generation code. | ||
251 | */ | ||
252 | struct drm_display_mode *drm_mode_std(struct drm_device *dev, | ||
253 | struct std_timing *t) | ||
254 | { | ||
255 | struct drm_display_mode *mode; | ||
256 | int hsize = t->hsize * 8 + 248, vsize; | ||
257 | |||
258 | mode = drm_mode_create(dev); | ||
259 | if (!mode) | ||
260 | return NULL; | ||
261 | |||
262 | if (t->aspect_ratio == 0) | ||
263 | vsize = (hsize * 10) / 16; | ||
264 | else if (t->aspect_ratio == 1) | ||
265 | vsize = (hsize * 3) / 4; | ||
266 | else if (t->aspect_ratio == 2) | ||
267 | vsize = (hsize * 4) / 5; | ||
268 | else | ||
269 | vsize = (hsize * 9) / 16; | ||
270 | |||
271 | drm_mode_set_name(mode); | ||
272 | |||
273 | return mode; | ||
274 | } | ||
275 | |||
276 | /** | ||
277 | * drm_mode_detailed - create a new mode from an EDID detailed timing section | ||
278 | * @dev: DRM device (needed to create new mode) | ||
279 | * @edid: EDID block | ||
280 | * @timing: EDID detailed timing info | ||
281 | * @quirks: quirks to apply | ||
282 | * | ||
283 | * An EDID detailed timing block contains enough info for us to create and | ||
284 | * return a new struct drm_display_mode. | ||
285 | */ | ||
286 | static struct drm_display_mode *drm_mode_detailed(struct drm_device *dev, | ||
287 | struct edid *edid, | ||
288 | struct detailed_timing *timing, | ||
289 | u32 quirks) | ||
290 | { | ||
291 | struct drm_display_mode *mode; | ||
292 | struct detailed_pixel_timing *pt = &timing->data.pixel_data; | ||
293 | |||
294 | if (pt->stereo) { | ||
295 | printk(KERN_WARNING "stereo mode not supported\n"); | ||
296 | return NULL; | ||
297 | } | ||
298 | if (!pt->separate_sync) { | ||
299 | printk(KERN_WARNING "integrated sync not supported\n"); | ||
300 | return NULL; | ||
301 | } | ||
302 | |||
303 | mode = drm_mode_create(dev); | ||
304 | if (!mode) | ||
305 | return NULL; | ||
306 | |||
307 | mode->type = DRM_MODE_TYPE_DRIVER; | ||
308 | |||
309 | if (quirks & EDID_QUIRK_135_CLOCK_TOO_HIGH) | ||
310 | timing->pixel_clock = 1088; | ||
311 | |||
312 | mode->clock = timing->pixel_clock * 10; | ||
313 | |||
314 | mode->hdisplay = (pt->hactive_hi << 8) | pt->hactive_lo; | ||
315 | mode->hsync_start = mode->hdisplay + ((pt->hsync_offset_hi << 8) | | ||
316 | pt->hsync_offset_lo); | ||
317 | mode->hsync_end = mode->hsync_start + | ||
318 | ((pt->hsync_pulse_width_hi << 8) | | ||
319 | pt->hsync_pulse_width_lo); | ||
320 | mode->htotal = mode->hdisplay + ((pt->hblank_hi << 8) | pt->hblank_lo); | ||
321 | |||
322 | mode->vdisplay = (pt->vactive_hi << 8) | pt->vactive_lo; | ||
323 | mode->vsync_start = mode->vdisplay + ((pt->vsync_offset_hi << 8) | | ||
324 | pt->vsync_offset_lo); | ||
325 | mode->vsync_end = mode->vsync_start + | ||
326 | ((pt->vsync_pulse_width_hi << 8) | | ||
327 | pt->vsync_pulse_width_lo); | ||
328 | mode->vtotal = mode->vdisplay + ((pt->vblank_hi << 8) | pt->vblank_lo); | ||
329 | |||
330 | drm_mode_set_name(mode); | ||
331 | |||
332 | if (pt->interlaced) | ||
333 | mode->flags |= DRM_MODE_FLAG_INTERLACE; | ||
334 | |||
335 | if (quirks & EDID_QUIRK_DETAILED_SYNC_PP) { | ||
336 | pt->hsync_positive = 1; | ||
337 | pt->vsync_positive = 1; | ||
338 | } | ||
339 | |||
340 | mode->flags |= pt->hsync_positive ? DRM_MODE_FLAG_PHSYNC : DRM_MODE_FLAG_NHSYNC; | ||
341 | mode->flags |= pt->vsync_positive ? DRM_MODE_FLAG_PVSYNC : DRM_MODE_FLAG_NVSYNC; | ||
342 | |||
343 | mode->width_mm = pt->width_mm_lo | (pt->width_mm_hi << 8); | ||
344 | mode->height_mm = pt->height_mm_lo | (pt->height_mm_hi << 8); | ||
345 | |||
346 | if (quirks & EDID_QUIRK_DETAILED_IN_CM) { | ||
347 | mode->width_mm *= 10; | ||
348 | mode->height_mm *= 10; | ||
349 | } | ||
350 | |||
351 | if (quirks & EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE) { | ||
352 | mode->width_mm = edid->width_cm * 10; | ||
353 | mode->height_mm = edid->height_cm * 10; | ||
354 | } | ||
355 | |||
356 | return mode; | ||
357 | } | ||
358 | |||
359 | /* | ||
360 | * Detailed mode info for the EDID "established modes" data to use. | ||
361 | */ | ||
362 | static struct drm_display_mode edid_est_modes[] = { | ||
363 | { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 40000, 800, 840, | ||
364 | 968, 1056, 0, 600, 601, 605, 628, 0, | ||
365 | DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 800x600@60Hz */ | ||
366 | { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 36000, 800, 824, | ||
367 | 896, 1024, 0, 600, 601, 603, 625, 0, | ||
368 | DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 800x600@56Hz */ | ||
369 | { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 31500, 640, 656, | ||
370 | 720, 840, 0, 480, 481, 484, 500, 0, | ||
371 | DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@75Hz */ | ||
372 | { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 31500, 640, 664, | ||
373 | 704, 832, 0, 480, 489, 491, 520, 0, | ||
374 | DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@72Hz */ | ||
375 | { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 30240, 640, 704, | ||
376 | 768, 864, 0, 480, 483, 486, 525, 0, | ||
377 | DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@67Hz */ | ||
378 | { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 25200, 640, 656, | ||
379 | 752, 800, 0, 480, 490, 492, 525, 0, | ||
380 | DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@60Hz */ | ||
381 | { DRM_MODE("720x400", DRM_MODE_TYPE_DRIVER, 35500, 720, 738, | ||
382 | 846, 900, 0, 400, 421, 423, 449, 0, | ||
383 | DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 720x400@88Hz */ | ||
384 | { DRM_MODE("720x400", DRM_MODE_TYPE_DRIVER, 28320, 720, 738, | ||
385 | 846, 900, 0, 400, 412, 414, 449, 0, | ||
386 | DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 720x400@70Hz */ | ||
387 | { DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 135000, 1280, 1296, | ||
388 | 1440, 1688, 0, 1024, 1025, 1028, 1066, 0, | ||
389 | DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 1280x1024@75Hz */ | ||
390 | { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 78800, 1024, 1040, | ||
391 | 1136, 1312, 0, 768, 769, 772, 800, 0, | ||
392 | DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 1024x768@75Hz */ | ||
393 | { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 75000, 1024, 1048, | ||
394 | 1184, 1328, 0, 768, 771, 777, 806, 0, | ||
395 | DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 1024x768@70Hz */ | ||
396 | { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 65000, 1024, 1048, | ||
397 | 1184, 1344, 0, 768, 771, 777, 806, 0, | ||
398 | DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 1024x768@60Hz */ | ||
399 | { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER,44900, 1024, 1032, | ||
400 | 1208, 1264, 0, 768, 768, 776, 817, 0, | ||
401 | DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC | DRM_MODE_FLAG_INTERLACE) }, /* 1024x768@43Hz */ | ||
402 | { DRM_MODE("832x624", DRM_MODE_TYPE_DRIVER, 57284, 832, 864, | ||
403 | 928, 1152, 0, 624, 625, 628, 667, 0, | ||
404 | DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 832x624@75Hz */ | ||
405 | { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 49500, 800, 816, | ||
406 | 896, 1056, 0, 600, 601, 604, 625, 0, | ||
407 | DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 800x600@75Hz */ | ||
408 | { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 50000, 800, 856, | ||
409 | 976, 1040, 0, 600, 637, 643, 666, 0, | ||
410 | DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 800x600@72Hz */ | ||
411 | { DRM_MODE("1152x864", DRM_MODE_TYPE_DRIVER, 108000, 1152, 1216, | ||
412 | 1344, 1600, 0, 864, 865, 868, 900, 0, | ||
413 | DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 1152x864@75Hz */ | ||
414 | }; | ||
415 | |||
416 | #define EDID_EST_TIMINGS 16 | ||
417 | #define EDID_STD_TIMINGS 8 | ||
418 | #define EDID_DETAILED_TIMINGS 4 | ||
419 | |||
420 | /** | ||
421 | * add_established_modes - get est. modes from EDID and add them | ||
422 | * @edid: EDID block to scan | ||
423 | * | ||
424 | * Each EDID block contains a bitmap of the supported "established modes" list | ||
425 | * (defined above). Tease them out and add them to the global modes list. | ||
426 | */ | ||
427 | static int add_established_modes(struct drm_connector *connector, struct edid *edid) | ||
428 | { | ||
429 | struct drm_device *dev = connector->dev; | ||
430 | unsigned long est_bits = edid->established_timings.t1 | | ||
431 | (edid->established_timings.t2 << 8) | | ||
432 | ((edid->established_timings.mfg_rsvd & 0x80) << 9); | ||
433 | int i, modes = 0; | ||
434 | |||
435 | for (i = 0; i <= EDID_EST_TIMINGS; i++) | ||
436 | if (est_bits & (1<<i)) { | ||
437 | struct drm_display_mode *newmode; | ||
438 | newmode = drm_mode_duplicate(dev, &edid_est_modes[i]); | ||
439 | if (newmode) { | ||
440 | drm_mode_probed_add(connector, newmode); | ||
441 | modes++; | ||
442 | } | ||
443 | } | ||
444 | |||
445 | return modes; | ||
446 | } | ||
447 | |||
448 | /** | ||
449 | * add_standard_modes - get std. modes from EDID and add them | ||
450 | * @edid: EDID block to scan | ||
451 | * | ||
452 | * Standard modes can be calculated using the CVT standard. Grab them from | ||
453 | * @edid, calculate them, and add them to the list. | ||
454 | */ | ||
455 | static int add_standard_modes(struct drm_connector *connector, struct edid *edid) | ||
456 | { | ||
457 | struct drm_device *dev = connector->dev; | ||
458 | int i, modes = 0; | ||
459 | |||
460 | for (i = 0; i < EDID_STD_TIMINGS; i++) { | ||
461 | struct std_timing *t = &edid->standard_timings[i]; | ||
462 | struct drm_display_mode *newmode; | ||
463 | |||
464 | /* If std timings bytes are 1, 1 it's empty */ | ||
465 | if (t->hsize == 1 && (t->aspect_ratio | t->vfreq) == 1) | ||
466 | continue; | ||
467 | |||
468 | newmode = drm_mode_std(dev, &edid->standard_timings[i]); | ||
469 | if (newmode) { | ||
470 | drm_mode_probed_add(connector, newmode); | ||
471 | modes++; | ||
472 | } | ||
473 | } | ||
474 | |||
475 | return modes; | ||
476 | } | ||
477 | |||
478 | /** | ||
479 | * add_detailed_modes - get detailed mode info from EDID data | ||
480 | * @connector: attached connector | ||
481 | * @edid: EDID block to scan | ||
482 | * @quirks: quirks to apply | ||
483 | * | ||
484 | * Some of the detailed timing sections may contain mode information. Grab | ||
485 | * it and add it to the list. | ||
486 | */ | ||
487 | static int add_detailed_info(struct drm_connector *connector, | ||
488 | struct edid *edid, u32 quirks) | ||
489 | { | ||
490 | struct drm_device *dev = connector->dev; | ||
491 | int i, j, modes = 0; | ||
492 | |||
493 | for (i = 0; i < EDID_DETAILED_TIMINGS; i++) { | ||
494 | struct detailed_timing *timing = &edid->detailed_timings[i]; | ||
495 | struct detailed_non_pixel *data = &timing->data.other_data; | ||
496 | struct drm_display_mode *newmode; | ||
497 | |||
498 | /* EDID up to and including 1.2 may put monitor info here */ | ||
499 | if (edid->version == 1 && edid->revision < 3) | ||
500 | continue; | ||
501 | |||
502 | /* Detailed mode timing */ | ||
503 | if (timing->pixel_clock) { | ||
504 | newmode = drm_mode_detailed(dev, edid, timing, quirks); | ||
505 | if (!newmode) | ||
506 | continue; | ||
507 | |||
508 | /* First detailed mode is preferred */ | ||
509 | if (i == 0 && edid->preferred_timing) | ||
510 | newmode->type |= DRM_MODE_TYPE_PREFERRED; | ||
511 | drm_mode_probed_add(connector, newmode); | ||
512 | |||
513 | modes++; | ||
514 | continue; | ||
515 | } | ||
516 | |||
517 | /* Other timing or info */ | ||
518 | switch (data->type) { | ||
519 | case EDID_DETAIL_MONITOR_SERIAL: | ||
520 | break; | ||
521 | case EDID_DETAIL_MONITOR_STRING: | ||
522 | break; | ||
523 | case EDID_DETAIL_MONITOR_RANGE: | ||
524 | /* Get monitor range data */ | ||
525 | break; | ||
526 | case EDID_DETAIL_MONITOR_NAME: | ||
527 | break; | ||
528 | case EDID_DETAIL_MONITOR_CPDATA: | ||
529 | break; | ||
530 | case EDID_DETAIL_STD_MODES: | ||
531 | /* Five modes per detailed section */ | ||
532 | for (j = 0; j < 5; i++) { | ||
533 | struct std_timing *std; | ||
534 | struct drm_display_mode *newmode; | ||
535 | |||
536 | std = &data->data.timings[j]; | ||
537 | newmode = drm_mode_std(dev, std); | ||
538 | if (newmode) { | ||
539 | drm_mode_probed_add(connector, newmode); | ||
540 | modes++; | ||
541 | } | ||
542 | } | ||
543 | break; | ||
544 | default: | ||
545 | break; | ||
546 | } | ||
547 | } | ||
548 | |||
549 | return modes; | ||
550 | } | ||
551 | |||
552 | #define DDC_ADDR 0x50 | ||
553 | |||
554 | unsigned char *drm_do_probe_ddc_edid(struct i2c_adapter *adapter) | ||
555 | { | ||
556 | unsigned char start = 0x0; | ||
557 | unsigned char *buf = kmalloc(EDID_LENGTH, GFP_KERNEL); | ||
558 | struct i2c_msg msgs[] = { | ||
559 | { | ||
560 | .addr = DDC_ADDR, | ||
561 | .flags = 0, | ||
562 | .len = 1, | ||
563 | .buf = &start, | ||
564 | }, { | ||
565 | .addr = DDC_ADDR, | ||
566 | .flags = I2C_M_RD, | ||
567 | .len = EDID_LENGTH, | ||
568 | .buf = buf, | ||
569 | } | ||
570 | }; | ||
571 | |||
572 | if (!buf) { | ||
573 | dev_warn(&adapter->dev, "unable to allocate memory for EDID " | ||
574 | "block.\n"); | ||
575 | return NULL; | ||
576 | } | ||
577 | |||
578 | if (i2c_transfer(adapter, msgs, 2) == 2) | ||
579 | return buf; | ||
580 | |||
581 | dev_info(&adapter->dev, "unable to read EDID block.\n"); | ||
582 | kfree(buf); | ||
583 | return NULL; | ||
584 | } | ||
585 | EXPORT_SYMBOL(drm_do_probe_ddc_edid); | ||
586 | |||
587 | static unsigned char *drm_ddc_read(struct i2c_adapter *adapter) | ||
588 | { | ||
589 | struct i2c_algo_bit_data *algo_data = adapter->algo_data; | ||
590 | unsigned char *edid = NULL; | ||
591 | int i, j; | ||
592 | |||
593 | algo_data->setscl(algo_data->data, 1); | ||
594 | |||
595 | for (i = 0; i < 1; i++) { | ||
596 | /* For some old monitors we need the | ||
597 | * following process to initialize/stop DDC | ||
598 | */ | ||
599 | algo_data->setsda(algo_data->data, 1); | ||
600 | msleep(13); | ||
601 | |||
602 | algo_data->setscl(algo_data->data, 1); | ||
603 | for (j = 0; j < 5; j++) { | ||
604 | msleep(10); | ||
605 | if (algo_data->getscl(algo_data->data)) | ||
606 | break; | ||
607 | } | ||
608 | if (j == 5) | ||
609 | continue; | ||
610 | |||
611 | algo_data->setsda(algo_data->data, 0); | ||
612 | msleep(15); | ||
613 | algo_data->setscl(algo_data->data, 0); | ||
614 | msleep(15); | ||
615 | algo_data->setsda(algo_data->data, 1); | ||
616 | msleep(15); | ||
617 | |||
618 | /* Do the real work */ | ||
619 | edid = drm_do_probe_ddc_edid(adapter); | ||
620 | algo_data->setsda(algo_data->data, 0); | ||
621 | algo_data->setscl(algo_data->data, 0); | ||
622 | msleep(15); | ||
623 | |||
624 | algo_data->setscl(algo_data->data, 1); | ||
625 | for (j = 0; j < 10; j++) { | ||
626 | msleep(10); | ||
627 | if (algo_data->getscl(algo_data->data)) | ||
628 | break; | ||
629 | } | ||
630 | |||
631 | algo_data->setsda(algo_data->data, 1); | ||
632 | msleep(15); | ||
633 | algo_data->setscl(algo_data->data, 0); | ||
634 | algo_data->setsda(algo_data->data, 0); | ||
635 | if (edid) | ||
636 | break; | ||
637 | } | ||
638 | /* Release the DDC lines when done or the Apple Cinema HD display | ||
639 | * will switch off | ||
640 | */ | ||
641 | algo_data->setsda(algo_data->data, 1); | ||
642 | algo_data->setscl(algo_data->data, 1); | ||
643 | |||
644 | return edid; | ||
645 | } | ||
646 | |||
647 | /** | ||
648 | * drm_get_edid - get EDID data, if available | ||
649 | * @connector: connector we're probing | ||
650 | * @adapter: i2c adapter to use for DDC | ||
651 | * | ||
652 | * Poke the given connector's i2c channel to grab EDID data if possible. | ||
653 | * | ||
654 | * Return edid data or NULL if we couldn't find any. | ||
655 | */ | ||
656 | struct edid *drm_get_edid(struct drm_connector *connector, | ||
657 | struct i2c_adapter *adapter) | ||
658 | { | ||
659 | struct edid *edid; | ||
660 | |||
661 | edid = (struct edid *)drm_ddc_read(adapter); | ||
662 | if (!edid) { | ||
663 | dev_warn(&connector->dev->pdev->dev, "%s: no EDID data\n", | ||
664 | drm_get_connector_name(connector)); | ||
665 | return NULL; | ||
666 | } | ||
667 | if (!edid_is_valid(edid)) { | ||
668 | dev_warn(&connector->dev->pdev->dev, "%s: EDID invalid.\n", | ||
669 | drm_get_connector_name(connector)); | ||
670 | kfree(edid); | ||
671 | return NULL; | ||
672 | } | ||
673 | |||
674 | connector->display_info.raw_edid = (char *)edid; | ||
675 | |||
676 | return edid; | ||
677 | } | ||
678 | EXPORT_SYMBOL(drm_get_edid); | ||
679 | |||
680 | /** | ||
681 | * drm_add_edid_modes - add modes from EDID data, if available | ||
682 | * @connector: connector we're probing | ||
683 | * @edid: edid data | ||
684 | * | ||
685 | * Add the specified modes to the connector's mode list. | ||
686 | * | ||
687 | * Return number of modes added or 0 if we couldn't find any. | ||
688 | */ | ||
689 | int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid) | ||
690 | { | ||
691 | int num_modes = 0; | ||
692 | u32 quirks; | ||
693 | |||
694 | if (edid == NULL) { | ||
695 | return 0; | ||
696 | } | ||
697 | if (!edid_is_valid(edid)) { | ||
698 | dev_warn(&connector->dev->pdev->dev, "%s: EDID invalid.\n", | ||
699 | drm_get_connector_name(connector)); | ||
700 | return 0; | ||
701 | } | ||
702 | |||
703 | quirks = edid_get_quirks(edid); | ||
704 | |||
705 | num_modes += add_established_modes(connector, edid); | ||
706 | num_modes += add_standard_modes(connector, edid); | ||
707 | num_modes += add_detailed_info(connector, edid, quirks); | ||
708 | |||
709 | if (quirks & (EDID_QUIRK_PREFER_LARGE_60 | EDID_QUIRK_PREFER_LARGE_75)) | ||
710 | edid_fixup_preferred(connector, quirks); | ||
711 | |||
712 | connector->display_info.serration_vsync = edid->serration_vsync; | ||
713 | connector->display_info.sync_on_green = edid->sync_on_green; | ||
714 | connector->display_info.composite_sync = edid->composite_sync; | ||
715 | connector->display_info.separate_syncs = edid->separate_syncs; | ||
716 | connector->display_info.blank_to_black = edid->blank_to_black; | ||
717 | connector->display_info.video_level = edid->video_level; | ||
718 | connector->display_info.digital = edid->digital; | ||
719 | connector->display_info.width_mm = edid->width_cm * 10; | ||
720 | connector->display_info.height_mm = edid->height_cm * 10; | ||
721 | connector->display_info.gamma = edid->gamma; | ||
722 | connector->display_info.gtf_supported = edid->default_gtf; | ||
723 | connector->display_info.standard_color = edid->standard_color; | ||
724 | connector->display_info.display_type = edid->display_type; | ||
725 | connector->display_info.active_off_supported = edid->pm_active_off; | ||
726 | connector->display_info.suspend_supported = edid->pm_suspend; | ||
727 | connector->display_info.standby_supported = edid->pm_standby; | ||
728 | connector->display_info.gamma = edid->gamma; | ||
729 | |||
730 | return num_modes; | ||
731 | } | ||
732 | EXPORT_SYMBOL(drm_add_edid_modes); | ||
diff --git a/drivers/gpu/drm/drm_fops.c b/drivers/gpu/drm/drm_fops.c index 3a6c439652a5..3733e36d135e 100644 --- a/drivers/gpu/drm/drm_fops.c +++ b/drivers/gpu/drm/drm_fops.c | |||
@@ -35,7 +35,6 @@ | |||
35 | */ | 35 | */ |
36 | 36 | ||
37 | #include "drmP.h" | 37 | #include "drmP.h" |
38 | #include "drm_sarea.h" | ||
39 | #include <linux/poll.h> | 38 | #include <linux/poll.h> |
40 | #include <linux/smp_lock.h> | 39 | #include <linux/smp_lock.h> |
41 | 40 | ||
@@ -55,10 +54,12 @@ static int drm_setup(struct drm_device * dev) | |||
55 | 54 | ||
56 | atomic_set(&dev->ioctl_count, 0); | 55 | atomic_set(&dev->ioctl_count, 0); |
57 | atomic_set(&dev->vma_count, 0); | 56 | atomic_set(&dev->vma_count, 0); |
58 | dev->buf_use = 0; | ||
59 | atomic_set(&dev->buf_alloc, 0); | ||
60 | 57 | ||
61 | if (drm_core_check_feature(dev, DRIVER_HAVE_DMA)) { | 58 | if (drm_core_check_feature(dev, DRIVER_HAVE_DMA) && |
59 | !drm_core_check_feature(dev, DRIVER_MODESET)) { | ||
60 | dev->buf_use = 0; | ||
61 | atomic_set(&dev->buf_alloc, 0); | ||
62 | |||
62 | i = drm_dma_setup(dev); | 63 | i = drm_dma_setup(dev); |
63 | if (i < 0) | 64 | if (i < 0) |
64 | return i; | 65 | return i; |
@@ -138,14 +139,14 @@ int drm_open(struct inode *inode, struct file *filp) | |||
138 | } | 139 | } |
139 | spin_unlock(&dev->count_lock); | 140 | spin_unlock(&dev->count_lock); |
140 | } | 141 | } |
141 | |||
142 | out: | 142 | out: |
143 | mutex_lock(&dev->struct_mutex); | 143 | mutex_lock(&dev->struct_mutex); |
144 | if (dev->dev_mapping == NULL) | 144 | if (minor->type == DRM_MINOR_LEGACY) { |
145 | dev->dev_mapping = inode->i_mapping; | 145 | BUG_ON((dev->dev_mapping != NULL) && |
146 | else if (dev->dev_mapping != inode->i_mapping) | 146 | (dev->dev_mapping != inode->i_mapping)); |
147 | WARN(1, "dev->dev_mapping not inode mapping (%p expected %p)\n", | 147 | if (dev->dev_mapping == NULL) |
148 | dev->dev_mapping, inode->i_mapping); | 148 | dev->dev_mapping = inode->i_mapping; |
149 | } | ||
149 | mutex_unlock(&dev->struct_mutex); | 150 | mutex_unlock(&dev->struct_mutex); |
150 | 151 | ||
151 | return retcode; | 152 | return retcode; |
@@ -251,6 +252,7 @@ static int drm_open_helper(struct inode *inode, struct file *filp, | |||
251 | priv->lock_count = 0; | 252 | priv->lock_count = 0; |
252 | 253 | ||
253 | INIT_LIST_HEAD(&priv->lhead); | 254 | INIT_LIST_HEAD(&priv->lhead); |
255 | INIT_LIST_HEAD(&priv->fbs); | ||
254 | 256 | ||
255 | if (dev->driver->driver_features & DRIVER_GEM) | 257 | if (dev->driver->driver_features & DRIVER_GEM) |
256 | drm_gem_open(dev, priv); | 258 | drm_gem_open(dev, priv); |
diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c index 1e787f894b3c..1608f8dbfda0 100644 --- a/drivers/gpu/drm/drm_irq.c +++ b/drivers/gpu/drm/drm_irq.c | |||
@@ -305,6 +305,8 @@ int drm_control(struct drm_device *dev, void *data, | |||
305 | case DRM_INST_HANDLER: | 305 | case DRM_INST_HANDLER: |
306 | if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ)) | 306 | if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ)) |
307 | return 0; | 307 | return 0; |
308 | if (drm_core_check_feature(dev, DRIVER_MODESET)) | ||
309 | return 0; | ||
308 | if (dev->if_version < DRM_IF_VERSION(1, 2) && | 310 | if (dev->if_version < DRM_IF_VERSION(1, 2) && |
309 | ctl->irq != dev->pdev->irq) | 311 | ctl->irq != dev->pdev->irq) |
310 | return -EINVAL; | 312 | return -EINVAL; |
@@ -312,6 +314,8 @@ int drm_control(struct drm_device *dev, void *data, | |||
312 | case DRM_UNINST_HANDLER: | 314 | case DRM_UNINST_HANDLER: |
313 | if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ)) | 315 | if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ)) |
314 | return 0; | 316 | return 0; |
317 | if (drm_core_check_feature(dev, DRIVER_MODESET)) | ||
318 | return 0; | ||
315 | return drm_irq_uninstall(dev); | 319 | return drm_irq_uninstall(dev); |
316 | default: | 320 | default: |
317 | return -EINVAL; | 321 | return -EINVAL; |
@@ -427,6 +431,45 @@ void drm_vblank_put(struct drm_device *dev, int crtc) | |||
427 | EXPORT_SYMBOL(drm_vblank_put); | 431 | EXPORT_SYMBOL(drm_vblank_put); |
428 | 432 | ||
429 | /** | 433 | /** |
434 | * drm_vblank_pre_modeset - account for vblanks across mode sets | ||
435 | * @dev: DRM device | ||
436 | * @crtc: CRTC in question | ||
437 | * @post: post or pre mode set? | ||
438 | * | ||
439 | * Account for vblank events across mode setting events, which will likely | ||
440 | * reset the hardware frame counter. | ||
441 | */ | ||
442 | void drm_vblank_pre_modeset(struct drm_device *dev, int crtc) | ||
443 | { | ||
444 | /* | ||
445 | * To avoid all the problems that might happen if interrupts | ||
446 | * were enabled/disabled around or between these calls, we just | ||
447 | * have the kernel take a reference on the CRTC (just once though | ||
448 | * to avoid corrupting the count if multiple, mismatch calls occur), | ||
449 | * so that interrupts remain enabled in the interim. | ||
450 | */ | ||
451 | if (!dev->vblank_inmodeset[crtc]) { | ||
452 | dev->vblank_inmodeset[crtc] = 1; | ||
453 | drm_vblank_get(dev, crtc); | ||
454 | } | ||
455 | } | ||
456 | EXPORT_SYMBOL(drm_vblank_pre_modeset); | ||
457 | |||
458 | void drm_vblank_post_modeset(struct drm_device *dev, int crtc) | ||
459 | { | ||
460 | unsigned long irqflags; | ||
461 | |||
462 | if (dev->vblank_inmodeset[crtc]) { | ||
463 | spin_lock_irqsave(&dev->vbl_lock, irqflags); | ||
464 | dev->vblank_disable_allowed = 1; | ||
465 | dev->vblank_inmodeset[crtc] = 0; | ||
466 | spin_unlock_irqrestore(&dev->vbl_lock, irqflags); | ||
467 | drm_vblank_put(dev, crtc); | ||
468 | } | ||
469 | } | ||
470 | EXPORT_SYMBOL(drm_vblank_post_modeset); | ||
471 | |||
472 | /** | ||
430 | * drm_modeset_ctl - handle vblank event counter changes across mode switch | 473 | * drm_modeset_ctl - handle vblank event counter changes across mode switch |
431 | * @DRM_IOCTL_ARGS: standard ioctl arguments | 474 | * @DRM_IOCTL_ARGS: standard ioctl arguments |
432 | * | 475 | * |
@@ -441,7 +484,6 @@ int drm_modeset_ctl(struct drm_device *dev, void *data, | |||
441 | struct drm_file *file_priv) | 484 | struct drm_file *file_priv) |
442 | { | 485 | { |
443 | struct drm_modeset_ctl *modeset = data; | 486 | struct drm_modeset_ctl *modeset = data; |
444 | unsigned long irqflags; | ||
445 | int crtc, ret = 0; | 487 | int crtc, ret = 0; |
446 | 488 | ||
447 | /* If drm_vblank_init() hasn't been called yet, just no-op */ | 489 | /* If drm_vblank_init() hasn't been called yet, just no-op */ |
@@ -454,28 +496,12 @@ int drm_modeset_ctl(struct drm_device *dev, void *data, | |||
454 | goto out; | 496 | goto out; |
455 | } | 497 | } |
456 | 498 | ||
457 | /* | ||
458 | * To avoid all the problems that might happen if interrupts | ||
459 | * were enabled/disabled around or between these calls, we just | ||
460 | * have the kernel take a reference on the CRTC (just once though | ||
461 | * to avoid corrupting the count if multiple, mismatch calls occur), | ||
462 | * so that interrupts remain enabled in the interim. | ||
463 | */ | ||
464 | switch (modeset->cmd) { | 499 | switch (modeset->cmd) { |
465 | case _DRM_PRE_MODESET: | 500 | case _DRM_PRE_MODESET: |
466 | if (!dev->vblank_inmodeset[crtc]) { | 501 | drm_vblank_pre_modeset(dev, crtc); |
467 | dev->vblank_inmodeset[crtc] = 1; | ||
468 | drm_vblank_get(dev, crtc); | ||
469 | } | ||
470 | break; | 502 | break; |
471 | case _DRM_POST_MODESET: | 503 | case _DRM_POST_MODESET: |
472 | if (dev->vblank_inmodeset[crtc]) { | 504 | drm_vblank_post_modeset(dev, crtc); |
473 | spin_lock_irqsave(&dev->vbl_lock, irqflags); | ||
474 | dev->vblank_disable_allowed = 1; | ||
475 | dev->vblank_inmodeset[crtc] = 0; | ||
476 | spin_unlock_irqrestore(&dev->vbl_lock, irqflags); | ||
477 | drm_vblank_put(dev, crtc); | ||
478 | } | ||
479 | break; | 505 | break; |
480 | default: | 506 | default: |
481 | ret = -EINVAL; | 507 | ret = -EINVAL; |
diff --git a/drivers/gpu/drm/drm_mm.c b/drivers/gpu/drm/drm_mm.c index 217ad7dc7076..367c590ffbba 100644 --- a/drivers/gpu/drm/drm_mm.c +++ b/drivers/gpu/drm/drm_mm.c | |||
@@ -296,3 +296,4 @@ void drm_mm_takedown(struct drm_mm * mm) | |||
296 | 296 | ||
297 | drm_free(entry, sizeof(*entry), DRM_MEM_MM); | 297 | drm_free(entry, sizeof(*entry), DRM_MEM_MM); |
298 | } | 298 | } |
299 | EXPORT_SYMBOL(drm_mm_takedown); | ||
diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c new file mode 100644 index 000000000000..c9b80fdd4630 --- /dev/null +++ b/drivers/gpu/drm/drm_modes.c | |||
@@ -0,0 +1,576 @@ | |||
1 | /* | ||
2 | * The list_sort function is (presumably) licensed under the GPL (see the | ||
3 | * top level "COPYING" file for details). | ||
4 | * | ||
5 | * The remainder of this file is: | ||
6 | * | ||
7 | * Copyright © 1997-2003 by The XFree86 Project, Inc. | ||
8 | * Copyright © 2007 Dave Airlie | ||
9 | * Copyright © 2007-2008 Intel Corporation | ||
10 | * Jesse Barnes <jesse.barnes@intel.com> | ||
11 | * | ||
12 | * Permission is hereby granted, free of charge, to any person obtaining a | ||
13 | * copy of this software and associated documentation files (the "Software"), | ||
14 | * to deal in the Software without restriction, including without limitation | ||
15 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | ||
16 | * and/or sell copies of the Software, and to permit persons to whom the | ||
17 | * Software is furnished to do so, subject to the following conditions: | ||
18 | * | ||
19 | * The above copyright notice and this permission notice shall be included in | ||
20 | * all copies or substantial portions of the Software. | ||
21 | * | ||
22 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
23 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
24 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | ||
25 | * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR | ||
26 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | ||
27 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | ||
28 | * OTHER DEALINGS IN THE SOFTWARE. | ||
29 | * | ||
30 | * Except as contained in this notice, the name of the copyright holder(s) | ||
31 | * and author(s) shall not be used in advertising or otherwise to promote | ||
32 | * the sale, use or other dealings in this Software without prior written | ||
33 | * authorization from the copyright holder(s) and author(s). | ||
34 | */ | ||
35 | |||
36 | #include <linux/list.h> | ||
37 | #include "drmP.h" | ||
38 | #include "drm.h" | ||
39 | #include "drm_crtc.h" | ||
40 | |||
41 | /** | ||
42 | * drm_mode_debug_printmodeline - debug print a mode | ||
43 | * @dev: DRM device | ||
44 | * @mode: mode to print | ||
45 | * | ||
46 | * LOCKING: | ||
47 | * None. | ||
48 | * | ||
49 | * Describe @mode using DRM_DEBUG. | ||
50 | */ | ||
51 | void drm_mode_debug_printmodeline(struct drm_display_mode *mode) | ||
52 | { | ||
53 | DRM_DEBUG("Modeline %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x\n", | ||
54 | mode->base.id, mode->name, mode->vrefresh, mode->clock, | ||
55 | mode->hdisplay, mode->hsync_start, | ||
56 | mode->hsync_end, mode->htotal, | ||
57 | mode->vdisplay, mode->vsync_start, | ||
58 | mode->vsync_end, mode->vtotal, mode->type, mode->flags); | ||
59 | } | ||
60 | EXPORT_SYMBOL(drm_mode_debug_printmodeline); | ||
61 | |||
62 | /** | ||
63 | * drm_mode_set_name - set the name on a mode | ||
64 | * @mode: name will be set in this mode | ||
65 | * | ||
66 | * LOCKING: | ||
67 | * None. | ||
68 | * | ||
69 | * Set the name of @mode to a standard format. | ||
70 | */ | ||
71 | void drm_mode_set_name(struct drm_display_mode *mode) | ||
72 | { | ||
73 | snprintf(mode->name, DRM_DISPLAY_MODE_LEN, "%dx%d", mode->hdisplay, | ||
74 | mode->vdisplay); | ||
75 | } | ||
76 | EXPORT_SYMBOL(drm_mode_set_name); | ||
77 | |||
78 | /** | ||
79 | * drm_mode_list_concat - move modes from one list to another | ||
80 | * @head: source list | ||
81 | * @new: dst list | ||
82 | * | ||
83 | * LOCKING: | ||
84 | * Caller must ensure both lists are locked. | ||
85 | * | ||
86 | * Move all the modes from @head to @new. | ||
87 | */ | ||
88 | void drm_mode_list_concat(struct list_head *head, struct list_head *new) | ||
89 | { | ||
90 | |||
91 | struct list_head *entry, *tmp; | ||
92 | |||
93 | list_for_each_safe(entry, tmp, head) { | ||
94 | list_move_tail(entry, new); | ||
95 | } | ||
96 | } | ||
97 | EXPORT_SYMBOL(drm_mode_list_concat); | ||
98 | |||
99 | /** | ||
100 | * drm_mode_width - get the width of a mode | ||
101 | * @mode: mode | ||
102 | * | ||
103 | * LOCKING: | ||
104 | * None. | ||
105 | * | ||
106 | * Return @mode's width (hdisplay) value. | ||
107 | * | ||
108 | * FIXME: is this needed? | ||
109 | * | ||
110 | * RETURNS: | ||
111 | * @mode->hdisplay | ||
112 | */ | ||
113 | int drm_mode_width(struct drm_display_mode *mode) | ||
114 | { | ||
115 | return mode->hdisplay; | ||
116 | |||
117 | } | ||
118 | EXPORT_SYMBOL(drm_mode_width); | ||
119 | |||
120 | /** | ||
121 | * drm_mode_height - get the height of a mode | ||
122 | * @mode: mode | ||
123 | * | ||
124 | * LOCKING: | ||
125 | * None. | ||
126 | * | ||
127 | * Return @mode's height (vdisplay) value. | ||
128 | * | ||
129 | * FIXME: is this needed? | ||
130 | * | ||
131 | * RETURNS: | ||
132 | * @mode->vdisplay | ||
133 | */ | ||
134 | int drm_mode_height(struct drm_display_mode *mode) | ||
135 | { | ||
136 | return mode->vdisplay; | ||
137 | } | ||
138 | EXPORT_SYMBOL(drm_mode_height); | ||
139 | |||
140 | /** | ||
141 | * drm_mode_vrefresh - get the vrefresh of a mode | ||
142 | * @mode: mode | ||
143 | * | ||
144 | * LOCKING: | ||
145 | * None. | ||
146 | * | ||
147 | * Return @mode's vrefresh rate or calculate it if necessary. | ||
148 | * | ||
149 | * FIXME: why is this needed? shouldn't vrefresh be set already? | ||
150 | * | ||
151 | * RETURNS: | ||
152 | * Vertical refresh rate of @mode x 1000. For precision reasons. | ||
153 | */ | ||
154 | int drm_mode_vrefresh(struct drm_display_mode *mode) | ||
155 | { | ||
156 | int refresh = 0; | ||
157 | unsigned int calc_val; | ||
158 | |||
159 | if (mode->vrefresh > 0) | ||
160 | refresh = mode->vrefresh; | ||
161 | else if (mode->htotal > 0 && mode->vtotal > 0) { | ||
162 | /* work out vrefresh the value will be x1000 */ | ||
163 | calc_val = (mode->clock * 1000); | ||
164 | |||
165 | calc_val /= mode->htotal; | ||
166 | calc_val *= 1000; | ||
167 | calc_val /= mode->vtotal; | ||
168 | |||
169 | refresh = calc_val; | ||
170 | if (mode->flags & DRM_MODE_FLAG_INTERLACE) | ||
171 | refresh *= 2; | ||
172 | if (mode->flags & DRM_MODE_FLAG_DBLSCAN) | ||
173 | refresh /= 2; | ||
174 | if (mode->vscan > 1) | ||
175 | refresh /= mode->vscan; | ||
176 | } | ||
177 | return refresh; | ||
178 | } | ||
179 | EXPORT_SYMBOL(drm_mode_vrefresh); | ||
180 | |||
181 | /** | ||
182 | * drm_mode_set_crtcinfo - set CRTC modesetting parameters | ||
183 | * @p: mode | ||
184 | * @adjust_flags: unused? (FIXME) | ||
185 | * | ||
186 | * LOCKING: | ||
187 | * None. | ||
188 | * | ||
189 | * Setup the CRTC modesetting parameters for @p, adjusting if necessary. | ||
190 | */ | ||
191 | void drm_mode_set_crtcinfo(struct drm_display_mode *p, int adjust_flags) | ||
192 | { | ||
193 | if ((p == NULL) || ((p->type & DRM_MODE_TYPE_CRTC_C) == DRM_MODE_TYPE_BUILTIN)) | ||
194 | return; | ||
195 | |||
196 | p->crtc_hdisplay = p->hdisplay; | ||
197 | p->crtc_hsync_start = p->hsync_start; | ||
198 | p->crtc_hsync_end = p->hsync_end; | ||
199 | p->crtc_htotal = p->htotal; | ||
200 | p->crtc_hskew = p->hskew; | ||
201 | p->crtc_vdisplay = p->vdisplay; | ||
202 | p->crtc_vsync_start = p->vsync_start; | ||
203 | p->crtc_vsync_end = p->vsync_end; | ||
204 | p->crtc_vtotal = p->vtotal; | ||
205 | |||
206 | if (p->flags & DRM_MODE_FLAG_INTERLACE) { | ||
207 | if (adjust_flags & CRTC_INTERLACE_HALVE_V) { | ||
208 | p->crtc_vdisplay /= 2; | ||
209 | p->crtc_vsync_start /= 2; | ||
210 | p->crtc_vsync_end /= 2; | ||
211 | p->crtc_vtotal /= 2; | ||
212 | } | ||
213 | |||
214 | p->crtc_vtotal |= 1; | ||
215 | } | ||
216 | |||
217 | if (p->flags & DRM_MODE_FLAG_DBLSCAN) { | ||
218 | p->crtc_vdisplay *= 2; | ||
219 | p->crtc_vsync_start *= 2; | ||
220 | p->crtc_vsync_end *= 2; | ||
221 | p->crtc_vtotal *= 2; | ||
222 | } | ||
223 | |||
224 | if (p->vscan > 1) { | ||
225 | p->crtc_vdisplay *= p->vscan; | ||
226 | p->crtc_vsync_start *= p->vscan; | ||
227 | p->crtc_vsync_end *= p->vscan; | ||
228 | p->crtc_vtotal *= p->vscan; | ||
229 | } | ||
230 | |||
231 | p->crtc_vblank_start = min(p->crtc_vsync_start, p->crtc_vdisplay); | ||
232 | p->crtc_vblank_end = max(p->crtc_vsync_end, p->crtc_vtotal); | ||
233 | p->crtc_hblank_start = min(p->crtc_hsync_start, p->crtc_hdisplay); | ||
234 | p->crtc_hblank_end = max(p->crtc_hsync_end, p->crtc_htotal); | ||
235 | |||
236 | p->crtc_hadjusted = false; | ||
237 | p->crtc_vadjusted = false; | ||
238 | } | ||
239 | EXPORT_SYMBOL(drm_mode_set_crtcinfo); | ||
240 | |||
241 | |||
242 | /** | ||
243 | * drm_mode_duplicate - allocate and duplicate an existing mode | ||
244 | * @m: mode to duplicate | ||
245 | * | ||
246 | * LOCKING: | ||
247 | * None. | ||
248 | * | ||
249 | * Just allocate a new mode, copy the existing mode into it, and return | ||
250 | * a pointer to it. Used to create new instances of established modes. | ||
251 | */ | ||
252 | struct drm_display_mode *drm_mode_duplicate(struct drm_device *dev, | ||
253 | struct drm_display_mode *mode) | ||
254 | { | ||
255 | struct drm_display_mode *nmode; | ||
256 | int new_id; | ||
257 | |||
258 | nmode = drm_mode_create(dev); | ||
259 | if (!nmode) | ||
260 | return NULL; | ||
261 | |||
262 | new_id = nmode->base.id; | ||
263 | *nmode = *mode; | ||
264 | nmode->base.id = new_id; | ||
265 | INIT_LIST_HEAD(&nmode->head); | ||
266 | return nmode; | ||
267 | } | ||
268 | EXPORT_SYMBOL(drm_mode_duplicate); | ||
269 | |||
270 | /** | ||
271 | * drm_mode_equal - test modes for equality | ||
272 | * @mode1: first mode | ||
273 | * @mode2: second mode | ||
274 | * | ||
275 | * LOCKING: | ||
276 | * None. | ||
277 | * | ||
278 | * Check to see if @mode1 and @mode2 are equivalent. | ||
279 | * | ||
280 | * RETURNS: | ||
281 | * True if the modes are equal, false otherwise. | ||
282 | */ | ||
283 | bool drm_mode_equal(struct drm_display_mode *mode1, struct drm_display_mode *mode2) | ||
284 | { | ||
285 | /* do clock check convert to PICOS so fb modes get matched | ||
286 | * the same */ | ||
287 | if (mode1->clock && mode2->clock) { | ||
288 | if (KHZ2PICOS(mode1->clock) != KHZ2PICOS(mode2->clock)) | ||
289 | return false; | ||
290 | } else if (mode1->clock != mode2->clock) | ||
291 | return false; | ||
292 | |||
293 | if (mode1->hdisplay == mode2->hdisplay && | ||
294 | mode1->hsync_start == mode2->hsync_start && | ||
295 | mode1->hsync_end == mode2->hsync_end && | ||
296 | mode1->htotal == mode2->htotal && | ||
297 | mode1->hskew == mode2->hskew && | ||
298 | mode1->vdisplay == mode2->vdisplay && | ||
299 | mode1->vsync_start == mode2->vsync_start && | ||
300 | mode1->vsync_end == mode2->vsync_end && | ||
301 | mode1->vtotal == mode2->vtotal && | ||
302 | mode1->vscan == mode2->vscan && | ||
303 | mode1->flags == mode2->flags) | ||
304 | return true; | ||
305 | |||
306 | return false; | ||
307 | } | ||
308 | EXPORT_SYMBOL(drm_mode_equal); | ||
309 | |||
310 | /** | ||
311 | * drm_mode_validate_size - make sure modes adhere to size constraints | ||
312 | * @dev: DRM device | ||
313 | * @mode_list: list of modes to check | ||
314 | * @maxX: maximum width | ||
315 | * @maxY: maximum height | ||
316 | * @maxPitch: max pitch | ||
317 | * | ||
318 | * LOCKING: | ||
319 | * Caller must hold a lock protecting @mode_list. | ||
320 | * | ||
321 | * The DRM device (@dev) has size and pitch limits. Here we validate the | ||
322 | * modes we probed for @dev against those limits and set their status as | ||
323 | * necessary. | ||
324 | */ | ||
325 | void drm_mode_validate_size(struct drm_device *dev, | ||
326 | struct list_head *mode_list, | ||
327 | int maxX, int maxY, int maxPitch) | ||
328 | { | ||
329 | struct drm_display_mode *mode; | ||
330 | |||
331 | list_for_each_entry(mode, mode_list, head) { | ||
332 | if (maxPitch > 0 && mode->hdisplay > maxPitch) | ||
333 | mode->status = MODE_BAD_WIDTH; | ||
334 | |||
335 | if (maxX > 0 && mode->hdisplay > maxX) | ||
336 | mode->status = MODE_VIRTUAL_X; | ||
337 | |||
338 | if (maxY > 0 && mode->vdisplay > maxY) | ||
339 | mode->status = MODE_VIRTUAL_Y; | ||
340 | } | ||
341 | } | ||
342 | EXPORT_SYMBOL(drm_mode_validate_size); | ||
343 | |||
344 | /** | ||
345 | * drm_mode_validate_clocks - validate modes against clock limits | ||
346 | * @dev: DRM device | ||
347 | * @mode_list: list of modes to check | ||
348 | * @min: minimum clock rate array | ||
349 | * @max: maximum clock rate array | ||
350 | * @n_ranges: number of clock ranges (size of arrays) | ||
351 | * | ||
352 | * LOCKING: | ||
353 | * Caller must hold a lock protecting @mode_list. | ||
354 | * | ||
355 | * Some code may need to check a mode list against the clock limits of the | ||
356 | * device in question. This function walks the mode list, testing to make | ||
357 | * sure each mode falls within a given range (defined by @min and @max | ||
358 | * arrays) and sets @mode->status as needed. | ||
359 | */ | ||
360 | void drm_mode_validate_clocks(struct drm_device *dev, | ||
361 | struct list_head *mode_list, | ||
362 | int *min, int *max, int n_ranges) | ||
363 | { | ||
364 | struct drm_display_mode *mode; | ||
365 | int i; | ||
366 | |||
367 | list_for_each_entry(mode, mode_list, head) { | ||
368 | bool good = false; | ||
369 | for (i = 0; i < n_ranges; i++) { | ||
370 | if (mode->clock >= min[i] && mode->clock <= max[i]) { | ||
371 | good = true; | ||
372 | break; | ||
373 | } | ||
374 | } | ||
375 | if (!good) | ||
376 | mode->status = MODE_CLOCK_RANGE; | ||
377 | } | ||
378 | } | ||
379 | EXPORT_SYMBOL(drm_mode_validate_clocks); | ||
380 | |||
381 | /** | ||
382 | * drm_mode_prune_invalid - remove invalid modes from mode list | ||
383 | * @dev: DRM device | ||
384 | * @mode_list: list of modes to check | ||
385 | * @verbose: be verbose about it | ||
386 | * | ||
387 | * LOCKING: | ||
388 | * Caller must hold a lock protecting @mode_list. | ||
389 | * | ||
390 | * Once mode list generation is complete, a caller can use this routine to | ||
391 | * remove invalid modes from a mode list. If any of the modes have a | ||
392 | * status other than %MODE_OK, they are removed from @mode_list and freed. | ||
393 | */ | ||
394 | void drm_mode_prune_invalid(struct drm_device *dev, | ||
395 | struct list_head *mode_list, bool verbose) | ||
396 | { | ||
397 | struct drm_display_mode *mode, *t; | ||
398 | |||
399 | list_for_each_entry_safe(mode, t, mode_list, head) { | ||
400 | if (mode->status != MODE_OK) { | ||
401 | list_del(&mode->head); | ||
402 | if (verbose) { | ||
403 | drm_mode_debug_printmodeline(mode); | ||
404 | DRM_DEBUG("Not using %s mode %d\n", mode->name, mode->status); | ||
405 | } | ||
406 | drm_mode_destroy(dev, mode); | ||
407 | } | ||
408 | } | ||
409 | } | ||
410 | EXPORT_SYMBOL(drm_mode_prune_invalid); | ||
411 | |||
412 | /** | ||
413 | * drm_mode_compare - compare modes for favorability | ||
414 | * @lh_a: list_head for first mode | ||
415 | * @lh_b: list_head for second mode | ||
416 | * | ||
417 | * LOCKING: | ||
418 | * None. | ||
419 | * | ||
420 | * Compare two modes, given by @lh_a and @lh_b, returning a value indicating | ||
421 | * which is better. | ||
422 | * | ||
423 | * RETURNS: | ||
424 | * Negative if @lh_a is better than @lh_b, zero if they're equivalent, or | ||
425 | * positive if @lh_b is better than @lh_a. | ||
426 | */ | ||
427 | static int drm_mode_compare(struct list_head *lh_a, struct list_head *lh_b) | ||
428 | { | ||
429 | struct drm_display_mode *a = list_entry(lh_a, struct drm_display_mode, head); | ||
430 | struct drm_display_mode *b = list_entry(lh_b, struct drm_display_mode, head); | ||
431 | int diff; | ||
432 | |||
433 | diff = ((b->type & DRM_MODE_TYPE_PREFERRED) != 0) - | ||
434 | ((a->type & DRM_MODE_TYPE_PREFERRED) != 0); | ||
435 | if (diff) | ||
436 | return diff; | ||
437 | diff = b->hdisplay * b->vdisplay - a->hdisplay * a->vdisplay; | ||
438 | if (diff) | ||
439 | return diff; | ||
440 | diff = b->clock - a->clock; | ||
441 | return diff; | ||
442 | } | ||
443 | |||
444 | /* FIXME: what we don't have a list sort function? */ | ||
445 | /* list sort from Mark J Roberts (mjr@znex.org) */ | ||
446 | void list_sort(struct list_head *head, | ||
447 | int (*cmp)(struct list_head *a, struct list_head *b)) | ||
448 | { | ||
449 | struct list_head *p, *q, *e, *list, *tail, *oldhead; | ||
450 | int insize, nmerges, psize, qsize, i; | ||
451 | |||
452 | list = head->next; | ||
453 | list_del(head); | ||
454 | insize = 1; | ||
455 | for (;;) { | ||
456 | p = oldhead = list; | ||
457 | list = tail = NULL; | ||
458 | nmerges = 0; | ||
459 | |||
460 | while (p) { | ||
461 | nmerges++; | ||
462 | q = p; | ||
463 | psize = 0; | ||
464 | for (i = 0; i < insize; i++) { | ||
465 | psize++; | ||
466 | q = q->next == oldhead ? NULL : q->next; | ||
467 | if (!q) | ||
468 | break; | ||
469 | } | ||
470 | |||
471 | qsize = insize; | ||
472 | while (psize > 0 || (qsize > 0 && q)) { | ||
473 | if (!psize) { | ||
474 | e = q; | ||
475 | q = q->next; | ||
476 | qsize--; | ||
477 | if (q == oldhead) | ||
478 | q = NULL; | ||
479 | } else if (!qsize || !q) { | ||
480 | e = p; | ||
481 | p = p->next; | ||
482 | psize--; | ||
483 | if (p == oldhead) | ||
484 | p = NULL; | ||
485 | } else if (cmp(p, q) <= 0) { | ||
486 | e = p; | ||
487 | p = p->next; | ||
488 | psize--; | ||
489 | if (p == oldhead) | ||
490 | p = NULL; | ||
491 | } else { | ||
492 | e = q; | ||
493 | q = q->next; | ||
494 | qsize--; | ||
495 | if (q == oldhead) | ||
496 | q = NULL; | ||
497 | } | ||
498 | if (tail) | ||
499 | tail->next = e; | ||
500 | else | ||
501 | list = e; | ||
502 | e->prev = tail; | ||
503 | tail = e; | ||
504 | } | ||
505 | p = q; | ||
506 | } | ||
507 | |||
508 | tail->next = list; | ||
509 | list->prev = tail; | ||
510 | |||
511 | if (nmerges <= 1) | ||
512 | break; | ||
513 | |||
514 | insize *= 2; | ||
515 | } | ||
516 | |||
517 | head->next = list; | ||
518 | head->prev = list->prev; | ||
519 | list->prev->next = head; | ||
520 | list->prev = head; | ||
521 | } | ||
522 | |||
523 | /** | ||
524 | * drm_mode_sort - sort mode list | ||
525 | * @mode_list: list to sort | ||
526 | * | ||
527 | * LOCKING: | ||
528 | * Caller must hold a lock protecting @mode_list. | ||
529 | * | ||
530 | * Sort @mode_list by favorability, putting good modes first. | ||
531 | */ | ||
532 | void drm_mode_sort(struct list_head *mode_list) | ||
533 | { | ||
534 | list_sort(mode_list, drm_mode_compare); | ||
535 | } | ||
536 | EXPORT_SYMBOL(drm_mode_sort); | ||
537 | |||
538 | /** | ||
539 | * drm_mode_connector_list_update - update the mode list for the connector | ||
540 | * @connector: the connector to update | ||
541 | * | ||
542 | * LOCKING: | ||
543 | * Caller must hold a lock protecting @mode_list. | ||
544 | * | ||
545 | * This moves the modes from the @connector probed_modes list | ||
546 | * to the actual mode list. It compares the probed mode against the current | ||
547 | * list and only adds different modes. All modes unverified after this point | ||
548 | * will be removed by the prune invalid modes. | ||
549 | */ | ||
550 | void drm_mode_connector_list_update(struct drm_connector *connector) | ||
551 | { | ||
552 | struct drm_display_mode *mode; | ||
553 | struct drm_display_mode *pmode, *pt; | ||
554 | int found_it; | ||
555 | |||
556 | list_for_each_entry_safe(pmode, pt, &connector->probed_modes, | ||
557 | head) { | ||
558 | found_it = 0; | ||
559 | /* go through current modes checking for the new probed mode */ | ||
560 | list_for_each_entry(mode, &connector->modes, head) { | ||
561 | if (drm_mode_equal(pmode, mode)) { | ||
562 | found_it = 1; | ||
563 | /* if equal delete the probed mode */ | ||
564 | mode->status = pmode->status; | ||
565 | list_del(&pmode->head); | ||
566 | drm_mode_destroy(connector->dev, pmode); | ||
567 | break; | ||
568 | } | ||
569 | } | ||
570 | |||
571 | if (!found_it) { | ||
572 | list_move_tail(&pmode->head, &connector->modes); | ||
573 | } | ||
574 | } | ||
575 | } | ||
576 | EXPORT_SYMBOL(drm_mode_connector_list_update); | ||
diff --git a/drivers/gpu/drm/drm_stub.c b/drivers/gpu/drm/drm_stub.c index ea7f9e5d47fa..5ca132afa4f2 100644 --- a/drivers/gpu/drm/drm_stub.c +++ b/drivers/gpu/drm/drm_stub.c | |||
@@ -57,6 +57,14 @@ static int drm_minor_get_id(struct drm_device *dev, int type) | |||
57 | int ret; | 57 | int ret; |
58 | int base = 0, limit = 63; | 58 | int base = 0, limit = 63; |
59 | 59 | ||
60 | if (type == DRM_MINOR_CONTROL) { | ||
61 | base += 64; | ||
62 | limit = base + 127; | ||
63 | } else if (type == DRM_MINOR_RENDER) { | ||
64 | base += 128; | ||
65 | limit = base + 255; | ||
66 | } | ||
67 | |||
60 | again: | 68 | again: |
61 | if (idr_pre_get(&drm_minors_idr, GFP_KERNEL) == 0) { | 69 | if (idr_pre_get(&drm_minors_idr, GFP_KERNEL) == 0) { |
62 | DRM_ERROR("Out of memory expanding drawable idr\n"); | 70 | DRM_ERROR("Out of memory expanding drawable idr\n"); |
@@ -362,12 +370,28 @@ int drm_get_dev(struct pci_dev *pdev, const struct pci_device_id *ent, | |||
362 | printk(KERN_ERR "DRM: Fill_in_dev failed.\n"); | 370 | printk(KERN_ERR "DRM: Fill_in_dev failed.\n"); |
363 | goto err_g2; | 371 | goto err_g2; |
364 | } | 372 | } |
373 | |||
374 | if (drm_core_check_feature(dev, DRIVER_MODESET)) { | ||
375 | ret = drm_get_minor(dev, &dev->control, DRM_MINOR_CONTROL); | ||
376 | if (ret) | ||
377 | goto err_g2; | ||
378 | } | ||
379 | |||
365 | if ((ret = drm_get_minor(dev, &dev->primary, DRM_MINOR_LEGACY))) | 380 | if ((ret = drm_get_minor(dev, &dev->primary, DRM_MINOR_LEGACY))) |
366 | goto err_g2; | 381 | goto err_g3; |
367 | 382 | ||
368 | if (dev->driver->load) | 383 | if (dev->driver->load) { |
369 | if ((ret = dev->driver->load(dev, ent->driver_data))) | 384 | ret = dev->driver->load(dev, ent->driver_data); |
385 | if (ret) | ||
370 | goto err_g3; | 386 | goto err_g3; |
387 | } | ||
388 | |||
389 | /* setup the grouping for the legacy output */ | ||
390 | if (drm_core_check_feature(dev, DRIVER_MODESET)) { | ||
391 | ret = drm_mode_group_init_legacy_group(dev, &dev->primary->mode_group); | ||
392 | if (ret) | ||
393 | goto err_g3; | ||
394 | } | ||
371 | 395 | ||
372 | list_add_tail(&dev->driver_item, &driver->device_list); | 396 | list_add_tail(&dev->driver_item, &driver->device_list); |
373 | 397 | ||
diff --git a/drivers/gpu/drm/drm_sysfs.c b/drivers/gpu/drm/drm_sysfs.c index 1611b9bcbe7f..65d72d094c81 100644 --- a/drivers/gpu/drm/drm_sysfs.c +++ b/drivers/gpu/drm/drm_sysfs.c | |||
@@ -20,6 +20,7 @@ | |||
20 | #include "drmP.h" | 20 | #include "drmP.h" |
21 | 21 | ||
22 | #define to_drm_minor(d) container_of(d, struct drm_minor, kdev) | 22 | #define to_drm_minor(d) container_of(d, struct drm_minor, kdev) |
23 | #define to_drm_connector(d) container_of(d, struct drm_connector, kdev) | ||
23 | 24 | ||
24 | /** | 25 | /** |
25 | * drm_sysfs_suspend - DRM class suspend hook | 26 | * drm_sysfs_suspend - DRM class suspend hook |
@@ -34,7 +35,7 @@ static int drm_sysfs_suspend(struct device *dev, pm_message_t state) | |||
34 | struct drm_minor *drm_minor = to_drm_minor(dev); | 35 | struct drm_minor *drm_minor = to_drm_minor(dev); |
35 | struct drm_device *drm_dev = drm_minor->dev; | 36 | struct drm_device *drm_dev = drm_minor->dev; |
36 | 37 | ||
37 | if (drm_dev->driver->suspend) | 38 | if (drm_minor->type == DRM_MINOR_LEGACY && drm_dev->driver->suspend) |
38 | return drm_dev->driver->suspend(drm_dev, state); | 39 | return drm_dev->driver->suspend(drm_dev, state); |
39 | 40 | ||
40 | return 0; | 41 | return 0; |
@@ -52,7 +53,7 @@ static int drm_sysfs_resume(struct device *dev) | |||
52 | struct drm_minor *drm_minor = to_drm_minor(dev); | 53 | struct drm_minor *drm_minor = to_drm_minor(dev); |
53 | struct drm_device *drm_dev = drm_minor->dev; | 54 | struct drm_device *drm_dev = drm_minor->dev; |
54 | 55 | ||
55 | if (drm_dev->driver->resume) | 56 | if (drm_minor->type == DRM_MINOR_LEGACY && drm_dev->driver->resume) |
56 | return drm_dev->driver->resume(drm_dev); | 57 | return drm_dev->driver->resume(drm_dev); |
57 | 58 | ||
58 | return 0; | 59 | return 0; |
@@ -144,6 +145,323 @@ static void drm_sysfs_device_release(struct device *dev) | |||
144 | return; | 145 | return; |
145 | } | 146 | } |
146 | 147 | ||
148 | /* | ||
149 | * Connector properties | ||
150 | */ | ||
151 | static ssize_t status_show(struct device *device, | ||
152 | struct device_attribute *attr, | ||
153 | char *buf) | ||
154 | { | ||
155 | struct drm_connector *connector = to_drm_connector(device); | ||
156 | enum drm_connector_status status; | ||
157 | |||
158 | status = connector->funcs->detect(connector); | ||
159 | return snprintf(buf, PAGE_SIZE, "%s", | ||
160 | drm_get_connector_status_name(status)); | ||
161 | } | ||
162 | |||
163 | static ssize_t dpms_show(struct device *device, | ||
164 | struct device_attribute *attr, | ||
165 | char *buf) | ||
166 | { | ||
167 | struct drm_connector *connector = to_drm_connector(device); | ||
168 | struct drm_device *dev = connector->dev; | ||
169 | uint64_t dpms_status; | ||
170 | int ret; | ||
171 | |||
172 | ret = drm_connector_property_get_value(connector, | ||
173 | dev->mode_config.dpms_property, | ||
174 | &dpms_status); | ||
175 | if (ret) | ||
176 | return 0; | ||
177 | |||
178 | return snprintf(buf, PAGE_SIZE, "%s", | ||
179 | drm_get_dpms_name((int)dpms_status)); | ||
180 | } | ||
181 | |||
182 | static ssize_t enabled_show(struct device *device, | ||
183 | struct device_attribute *attr, | ||
184 | char *buf) | ||
185 | { | ||
186 | struct drm_connector *connector = to_drm_connector(device); | ||
187 | |||
188 | return snprintf(buf, PAGE_SIZE, connector->encoder ? "enabled" : | ||
189 | "disabled"); | ||
190 | } | ||
191 | |||
192 | static ssize_t edid_show(struct kobject *kobj, struct bin_attribute *attr, | ||
193 | char *buf, loff_t off, size_t count) | ||
194 | { | ||
195 | struct device *connector_dev = container_of(kobj, struct device, kobj); | ||
196 | struct drm_connector *connector = to_drm_connector(connector_dev); | ||
197 | unsigned char *edid; | ||
198 | size_t size; | ||
199 | |||
200 | if (!connector->edid_blob_ptr) | ||
201 | return 0; | ||
202 | |||
203 | edid = connector->edid_blob_ptr->data; | ||
204 | size = connector->edid_blob_ptr->length; | ||
205 | if (!edid) | ||
206 | return 0; | ||
207 | |||
208 | if (off >= size) | ||
209 | return 0; | ||
210 | |||
211 | if (off + count > size) | ||
212 | count = size - off; | ||
213 | memcpy(buf, edid + off, count); | ||
214 | |||
215 | return count; | ||
216 | } | ||
217 | |||
218 | static ssize_t modes_show(struct device *device, | ||
219 | struct device_attribute *attr, | ||
220 | char *buf) | ||
221 | { | ||
222 | struct drm_connector *connector = to_drm_connector(device); | ||
223 | struct drm_display_mode *mode; | ||
224 | int written = 0; | ||
225 | |||
226 | list_for_each_entry(mode, &connector->modes, head) { | ||
227 | written += snprintf(buf + written, PAGE_SIZE - written, "%s\n", | ||
228 | mode->name); | ||
229 | } | ||
230 | |||
231 | return written; | ||
232 | } | ||
233 | |||
234 | static ssize_t subconnector_show(struct device *device, | ||
235 | struct device_attribute *attr, | ||
236 | char *buf) | ||
237 | { | ||
238 | struct drm_connector *connector = to_drm_connector(device); | ||
239 | struct drm_device *dev = connector->dev; | ||
240 | struct drm_property *prop = NULL; | ||
241 | uint64_t subconnector; | ||
242 | int is_tv = 0; | ||
243 | int ret; | ||
244 | |||
245 | switch (connector->connector_type) { | ||
246 | case DRM_MODE_CONNECTOR_DVII: | ||
247 | prop = dev->mode_config.dvi_i_subconnector_property; | ||
248 | break; | ||
249 | case DRM_MODE_CONNECTOR_Composite: | ||
250 | case DRM_MODE_CONNECTOR_SVIDEO: | ||
251 | case DRM_MODE_CONNECTOR_Component: | ||
252 | prop = dev->mode_config.tv_subconnector_property; | ||
253 | is_tv = 1; | ||
254 | break; | ||
255 | default: | ||
256 | DRM_ERROR("Wrong connector type for this property\n"); | ||
257 | return 0; | ||
258 | } | ||
259 | |||
260 | if (!prop) { | ||
261 | DRM_ERROR("Unable to find subconnector property\n"); | ||
262 | return 0; | ||
263 | } | ||
264 | |||
265 | ret = drm_connector_property_get_value(connector, prop, &subconnector); | ||
266 | if (ret) | ||
267 | return 0; | ||
268 | |||
269 | return snprintf(buf, PAGE_SIZE, "%s", is_tv ? | ||
270 | drm_get_tv_subconnector_name((int)subconnector) : | ||
271 | drm_get_dvi_i_subconnector_name((int)subconnector)); | ||
272 | } | ||
273 | |||
274 | static ssize_t select_subconnector_show(struct device *device, | ||
275 | struct device_attribute *attr, | ||
276 | char *buf) | ||
277 | { | ||
278 | struct drm_connector *connector = to_drm_connector(device); | ||
279 | struct drm_device *dev = connector->dev; | ||
280 | struct drm_property *prop = NULL; | ||
281 | uint64_t subconnector; | ||
282 | int is_tv = 0; | ||
283 | int ret; | ||
284 | |||
285 | switch (connector->connector_type) { | ||
286 | case DRM_MODE_CONNECTOR_DVII: | ||
287 | prop = dev->mode_config.dvi_i_select_subconnector_property; | ||
288 | break; | ||
289 | case DRM_MODE_CONNECTOR_Composite: | ||
290 | case DRM_MODE_CONNECTOR_SVIDEO: | ||
291 | case DRM_MODE_CONNECTOR_Component: | ||
292 | prop = dev->mode_config.tv_select_subconnector_property; | ||
293 | is_tv = 1; | ||
294 | break; | ||
295 | default: | ||
296 | DRM_ERROR("Wrong connector type for this property\n"); | ||
297 | return 0; | ||
298 | } | ||
299 | |||
300 | if (!prop) { | ||
301 | DRM_ERROR("Unable to find select subconnector property\n"); | ||
302 | return 0; | ||
303 | } | ||
304 | |||
305 | ret = drm_connector_property_get_value(connector, prop, &subconnector); | ||
306 | if (ret) | ||
307 | return 0; | ||
308 | |||
309 | return snprintf(buf, PAGE_SIZE, "%s", is_tv ? | ||
310 | drm_get_tv_select_name((int)subconnector) : | ||
311 | drm_get_dvi_i_select_name((int)subconnector)); | ||
312 | } | ||
313 | |||
314 | static struct device_attribute connector_attrs[] = { | ||
315 | __ATTR_RO(status), | ||
316 | __ATTR_RO(enabled), | ||
317 | __ATTR_RO(dpms), | ||
318 | __ATTR_RO(modes), | ||
319 | }; | ||
320 | |||
321 | /* These attributes are for both DVI-I connectors and all types of tv-out. */ | ||
322 | static struct device_attribute connector_attrs_opt1[] = { | ||
323 | __ATTR_RO(subconnector), | ||
324 | __ATTR_RO(select_subconnector), | ||
325 | }; | ||
326 | |||
327 | static struct bin_attribute edid_attr = { | ||
328 | .attr.name = "edid", | ||
329 | .size = 128, | ||
330 | .read = edid_show, | ||
331 | }; | ||
332 | |||
333 | /** | ||
334 | * drm_sysfs_connector_add - add an connector to sysfs | ||
335 | * @connector: connector to add | ||
336 | * | ||
337 | * Create an connector device in sysfs, along with its associated connector | ||
338 | * properties (so far, connection status, dpms, mode list & edid) and | ||
339 | * generate a hotplug event so userspace knows there's a new connector | ||
340 | * available. | ||
341 | * | ||
342 | * Note: | ||
343 | * This routine should only be called *once* for each DRM minor registered. | ||
344 | * A second call for an already registered device will trigger the BUG_ON | ||
345 | * below. | ||
346 | */ | ||
347 | int drm_sysfs_connector_add(struct drm_connector *connector) | ||
348 | { | ||
349 | struct drm_device *dev = connector->dev; | ||
350 | int ret = 0, i, j; | ||
351 | |||
352 | /* We shouldn't get called more than once for the same connector */ | ||
353 | BUG_ON(device_is_registered(&connector->kdev)); | ||
354 | |||
355 | connector->kdev.parent = &dev->primary->kdev; | ||
356 | connector->kdev.class = drm_class; | ||
357 | connector->kdev.release = drm_sysfs_device_release; | ||
358 | |||
359 | DRM_DEBUG("adding \"%s\" to sysfs\n", | ||
360 | drm_get_connector_name(connector)); | ||
361 | |||
362 | snprintf(connector->kdev.bus_id, BUS_ID_SIZE, "card%d-%s", | ||
363 | dev->primary->index, drm_get_connector_name(connector)); | ||
364 | ret = device_register(&connector->kdev); | ||
365 | |||
366 | if (ret) { | ||
367 | DRM_ERROR("failed to register connector device: %d\n", ret); | ||
368 | goto out; | ||
369 | } | ||
370 | |||
371 | /* Standard attributes */ | ||
372 | |||
373 | for (i = 0; i < ARRAY_SIZE(connector_attrs); i++) { | ||
374 | ret = device_create_file(&connector->kdev, &connector_attrs[i]); | ||
375 | if (ret) | ||
376 | goto err_out_files; | ||
377 | } | ||
378 | |||
379 | /* Optional attributes */ | ||
380 | /* | ||
381 | * In the long run it maybe a good idea to make one set of | ||
382 | * optionals per connector type. | ||
383 | */ | ||
384 | switch (connector->connector_type) { | ||
385 | case DRM_MODE_CONNECTOR_DVII: | ||
386 | case DRM_MODE_CONNECTOR_Composite: | ||
387 | case DRM_MODE_CONNECTOR_SVIDEO: | ||
388 | case DRM_MODE_CONNECTOR_Component: | ||
389 | for (i = 0; i < ARRAY_SIZE(connector_attrs_opt1); i++) { | ||
390 | ret = device_create_file(&connector->kdev, &connector_attrs_opt1[i]); | ||
391 | if (ret) | ||
392 | goto err_out_files; | ||
393 | } | ||
394 | break; | ||
395 | default: | ||
396 | break; | ||
397 | } | ||
398 | |||
399 | ret = sysfs_create_bin_file(&connector->kdev.kobj, &edid_attr); | ||
400 | if (ret) | ||
401 | goto err_out_files; | ||
402 | |||
403 | /* Let userspace know we have a new connector */ | ||
404 | drm_sysfs_hotplug_event(dev); | ||
405 | |||
406 | return 0; | ||
407 | |||
408 | err_out_files: | ||
409 | if (i > 0) | ||
410 | for (j = 0; j < i; j++) | ||
411 | device_remove_file(&connector->kdev, | ||
412 | &connector_attrs[i]); | ||
413 | device_unregister(&connector->kdev); | ||
414 | |||
415 | out: | ||
416 | return ret; | ||
417 | } | ||
418 | EXPORT_SYMBOL(drm_sysfs_connector_add); | ||
419 | |||
420 | /** | ||
421 | * drm_sysfs_connector_remove - remove an connector device from sysfs | ||
422 | * @connector: connector to remove | ||
423 | * | ||
424 | * Remove @connector and its associated attributes from sysfs. Note that | ||
425 | * the device model core will take care of sending the "remove" uevent | ||
426 | * at this time, so we don't need to do it. | ||
427 | * | ||
428 | * Note: | ||
429 | * This routine should only be called if the connector was previously | ||
430 | * successfully registered. If @connector hasn't been registered yet, | ||
431 | * you'll likely see a panic somewhere deep in sysfs code when called. | ||
432 | */ | ||
433 | void drm_sysfs_connector_remove(struct drm_connector *connector) | ||
434 | { | ||
435 | int i; | ||
436 | |||
437 | DRM_DEBUG("removing \"%s\" from sysfs\n", | ||
438 | drm_get_connector_name(connector)); | ||
439 | |||
440 | for (i = 0; i < ARRAY_SIZE(connector_attrs); i++) | ||
441 | device_remove_file(&connector->kdev, &connector_attrs[i]); | ||
442 | sysfs_remove_bin_file(&connector->kdev.kobj, &edid_attr); | ||
443 | device_unregister(&connector->kdev); | ||
444 | } | ||
445 | EXPORT_SYMBOL(drm_sysfs_connector_remove); | ||
446 | |||
447 | /** | ||
448 | * drm_sysfs_hotplug_event - generate a DRM uevent | ||
449 | * @dev: DRM device | ||
450 | * | ||
451 | * Send a uevent for the DRM device specified by @dev. Currently we only | ||
452 | * set HOTPLUG=1 in the uevent environment, but this could be expanded to | ||
453 | * deal with other types of events. | ||
454 | */ | ||
455 | void drm_sysfs_hotplug_event(struct drm_device *dev) | ||
456 | { | ||
457 | char *event_string = "HOTPLUG=1"; | ||
458 | char *envp[] = { event_string, NULL }; | ||
459 | |||
460 | DRM_DEBUG("generating hotplug event\n"); | ||
461 | |||
462 | kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, envp); | ||
463 | } | ||
464 | |||
147 | /** | 465 | /** |
148 | * drm_sysfs_device_add - adds a class device to sysfs for a character driver | 466 | * drm_sysfs_device_add - adds a class device to sysfs for a character driver |
149 | * @dev: DRM device to be added | 467 | * @dev: DRM device to be added |
@@ -163,7 +481,12 @@ int drm_sysfs_device_add(struct drm_minor *minor) | |||
163 | minor->kdev.class = drm_class; | 481 | minor->kdev.class = drm_class; |
164 | minor->kdev.release = drm_sysfs_device_release; | 482 | minor->kdev.release = drm_sysfs_device_release; |
165 | minor->kdev.devt = minor->device; | 483 | minor->kdev.devt = minor->device; |
166 | minor_str = "card%d"; | 484 | if (minor->type == DRM_MINOR_CONTROL) |
485 | minor_str = "controlD%d"; | ||
486 | else if (minor->type == DRM_MINOR_RENDER) | ||
487 | minor_str = "renderD%d"; | ||
488 | else | ||
489 | minor_str = "card%d"; | ||
167 | 490 | ||
168 | snprintf(minor->kdev.bus_id, BUS_ID_SIZE, minor_str, minor->index); | 491 | snprintf(minor->kdev.bus_id, BUS_ID_SIZE, minor_str, minor->index); |
169 | 492 | ||
diff --git a/drivers/video/console/vgacon.c b/drivers/video/console/vgacon.c index 448d209a0bf2..e6210725b9ab 100644 --- a/drivers/video/console/vgacon.c +++ b/drivers/video/console/vgacon.c | |||
@@ -112,6 +112,23 @@ static int vga_video_font_height; | |||
112 | static int vga_scan_lines __read_mostly; | 112 | static int vga_scan_lines __read_mostly; |
113 | static unsigned int vga_rolled_over; | 113 | static unsigned int vga_rolled_over; |
114 | 114 | ||
115 | int vgacon_text_mode_force = 0; | ||
116 | |||
117 | bool vgacon_text_force(void) | ||
118 | { | ||
119 | return vgacon_text_mode_force ? true : false; | ||
120 | } | ||
121 | EXPORT_SYMBOL(vgacon_text_force); | ||
122 | |||
123 | static int __init text_mode(char *str) | ||
124 | { | ||
125 | vgacon_text_mode_force = 1; | ||
126 | return 1; | ||
127 | } | ||
128 | |||
129 | /* force text mode - used by kernel modesetting */ | ||
130 | __setup("nomodeset", text_mode); | ||
131 | |||
115 | static int __init no_scroll(char *str) | 132 | static int __init no_scroll(char *str) |
116 | { | 133 | { |
117 | /* | 134 | /* |