Subversion Repositories Kolibri OS

Rev

Rev 1179 | Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
1123 serge 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 
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 
37
#include "drmP.h"
38
#include "drm.h"
39
#include "drm_crtc.h"
40
 
41
#define DRM_MODESET_DEBUG	"drm_mode"
42
/**
43
 * drm_mode_debug_printmodeline - debug print a mode
44
 * @dev: DRM device
45
 * @mode: mode to print
46
 *
47
 * LOCKING:
48
 * None.
49
 *
50
 * Describe @mode using DRM_DEBUG.
51
 */
52
void drm_mode_debug_printmodeline(struct drm_display_mode *mode)
53
{
54
	DRM_DEBUG_MODE(DRM_MODESET_DEBUG,
55
		"Modeline %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x\n",
56
		mode->base.id, mode->name, mode->vrefresh, mode->clock,
57
		mode->hdisplay, mode->hsync_start,
58
		mode->hsync_end, mode->htotal,
59
		mode->vdisplay, mode->vsync_start,
60
		mode->vsync_end, mode->vtotal, mode->type, mode->flags);
61
}
62
EXPORT_SYMBOL(drm_mode_debug_printmodeline);
63
 
64
/**
65
 * drm_mode_set_name - set the name on a mode
66
 * @mode: name will be set in this mode
67
 *
68
 * LOCKING:
69
 * None.
70
 *
71
 * Set the name of @mode to a standard format.
72
 */
73
void drm_mode_set_name(struct drm_display_mode *mode)
74
{
75
	snprintf(mode->name, DRM_DISPLAY_MODE_LEN, "%dx%d", mode->hdisplay,
76
		 mode->vdisplay);
77
}
78
EXPORT_SYMBOL(drm_mode_set_name);
79
 
80
/**
81
 * drm_mode_list_concat - move modes from one list to another
82
 * @head: source list
83
 * @new: dst list
84
 *
85
 * LOCKING:
86
 * Caller must ensure both lists are locked.
87
 *
88
 * Move all the modes from @head to @new.
89
 */
90
void drm_mode_list_concat(struct list_head *head, struct list_head *new)
91
{
92
 
93
	struct list_head *entry, *tmp;
94
 
95
	list_for_each_safe(entry, tmp, head) {
96
		list_move_tail(entry, new);
97
	}
98
}
99
EXPORT_SYMBOL(drm_mode_list_concat);
100
 
101
/**
102
 * drm_mode_width - get the width of a mode
103
 * @mode: mode
104
 *
105
 * LOCKING:
106
 * None.
107
 *
108
 * Return @mode's width (hdisplay) value.
109
 *
110
 * FIXME: is this needed?
111
 *
112
 * RETURNS:
113
 * @mode->hdisplay
114
 */
115
int drm_mode_width(struct drm_display_mode *mode)
116
{
117
	return mode->hdisplay;
118
 
119
}
120
EXPORT_SYMBOL(drm_mode_width);
121
 
122
/**
123
 * drm_mode_height - get the height of a mode
124
 * @mode: mode
125
 *
126
 * LOCKING:
127
 * None.
128
 *
129
 * Return @mode's height (vdisplay) value.
130
 *
131
 * FIXME: is this needed?
132
 *
133
 * RETURNS:
134
 * @mode->vdisplay
135
 */
136
int drm_mode_height(struct drm_display_mode *mode)
137
{
138
	return mode->vdisplay;
139
}
140
EXPORT_SYMBOL(drm_mode_height);
141
 
142
/**
143
 * drm_mode_vrefresh - get the vrefresh of a mode
144
 * @mode: mode
145
 *
146
 * LOCKING:
147
 * None.
148
 *
149
 * Return @mode's vrefresh rate or calculate it if necessary.
150
 *
151
 * FIXME: why is this needed?  shouldn't vrefresh be set already?
152
 *
153
 * RETURNS:
154
 * Vertical refresh rate of @mode x 1000. For precision reasons.
155
 */
156
int drm_mode_vrefresh(struct drm_display_mode *mode)
157
{
158
	int refresh = 0;
159
	unsigned int calc_val;
160
 
161
	if (mode->vrefresh > 0)
162
		refresh = mode->vrefresh;
163
	else if (mode->htotal > 0 && mode->vtotal > 0) {
164
		/* work out vrefresh the value will be x1000 */
165
		calc_val = (mode->clock * 1000);
166
 
167
		calc_val /= mode->htotal;
168
		calc_val *= 1000;
169
		calc_val /= mode->vtotal;
170
 
171
		refresh = calc_val;
172
		if (mode->flags & DRM_MODE_FLAG_INTERLACE)
173
			refresh *= 2;
174
		if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
175
			refresh /= 2;
176
		if (mode->vscan > 1)
177
			refresh /= mode->vscan;
178
	}
179
	return refresh;
180
}
181
EXPORT_SYMBOL(drm_mode_vrefresh);
182
 
183
/**
184
 * drm_mode_set_crtcinfo - set CRTC modesetting parameters
185
 * @p: mode
186
 * @adjust_flags: unused? (FIXME)
187
 *
188
 * LOCKING:
189
 * None.
190
 *
191
 * Setup the CRTC modesetting parameters for @p, adjusting if necessary.
192
 */
193
void drm_mode_set_crtcinfo(struct drm_display_mode *p, int adjust_flags)
194
{
195
	if ((p == NULL) || ((p->type & DRM_MODE_TYPE_CRTC_C) == DRM_MODE_TYPE_BUILTIN))
196
		return;
197
 
198
	p->crtc_hdisplay = p->hdisplay;
199
	p->crtc_hsync_start = p->hsync_start;
200
	p->crtc_hsync_end = p->hsync_end;
201
	p->crtc_htotal = p->htotal;
202
	p->crtc_hskew = p->hskew;
203
	p->crtc_vdisplay = p->vdisplay;
204
	p->crtc_vsync_start = p->vsync_start;
205
	p->crtc_vsync_end = p->vsync_end;
206
	p->crtc_vtotal = p->vtotal;
207
 
208
	if (p->flags & DRM_MODE_FLAG_INTERLACE) {
209
		if (adjust_flags & CRTC_INTERLACE_HALVE_V) {
210
			p->crtc_vdisplay /= 2;
211
			p->crtc_vsync_start /= 2;
212
			p->crtc_vsync_end /= 2;
213
			p->crtc_vtotal /= 2;
214
		}
215
 
216
		p->crtc_vtotal |= 1;
217
	}
218
 
219
	if (p->flags & DRM_MODE_FLAG_DBLSCAN) {
220
		p->crtc_vdisplay *= 2;
221
		p->crtc_vsync_start *= 2;
222
		p->crtc_vsync_end *= 2;
223
		p->crtc_vtotal *= 2;
224
	}
225
 
226
	if (p->vscan > 1) {
227
		p->crtc_vdisplay *= p->vscan;
228
		p->crtc_vsync_start *= p->vscan;
229
		p->crtc_vsync_end *= p->vscan;
230
		p->crtc_vtotal *= p->vscan;
231
	}
232
 
233
	p->crtc_vblank_start = min(p->crtc_vsync_start, p->crtc_vdisplay);
234
	p->crtc_vblank_end = max(p->crtc_vsync_end, p->crtc_vtotal);
235
	p->crtc_hblank_start = min(p->crtc_hsync_start, p->crtc_hdisplay);
236
	p->crtc_hblank_end = max(p->crtc_hsync_end, p->crtc_htotal);
237
 
238
	p->crtc_hadjusted = false;
239
	p->crtc_vadjusted = false;
240
}
241
EXPORT_SYMBOL(drm_mode_set_crtcinfo);
242
 
243
 
244
/**
245
 * drm_mode_duplicate - allocate and duplicate an existing mode
246
 * @m: mode to duplicate
247
 *
248
 * LOCKING:
249
 * None.
250
 *
251
 * Just allocate a new mode, copy the existing mode into it, and return
252
 * a pointer to it.  Used to create new instances of established modes.
253
 */
254
struct drm_display_mode *drm_mode_duplicate(struct drm_device *dev,
255
					    struct drm_display_mode *mode)
256
{
257
	struct drm_display_mode *nmode;
258
	int new_id;
259
 
260
	nmode = drm_mode_create(dev);
261
	if (!nmode)
262
		return NULL;
263
 
264
	new_id = nmode->base.id;
265
	*nmode = *mode;
266
	nmode->base.id = new_id;
267
	INIT_LIST_HEAD(&nmode->head);
268
	return nmode;
269
}
270
EXPORT_SYMBOL(drm_mode_duplicate);
271
 
272
/**
273
 * drm_mode_equal - test modes for equality
274
 * @mode1: first mode
275
 * @mode2: second mode
276
 *
277
 * LOCKING:
278
 * None.
279
 *
280
 * Check to see if @mode1 and @mode2 are equivalent.
281
 *
282
 * RETURNS:
283
 * True if the modes are equal, false otherwise.
284
 */
285
bool drm_mode_equal(struct drm_display_mode *mode1, struct drm_display_mode *mode2)
286
{
287
	/* do clock check convert to PICOS so fb modes get matched
288
	 * the same */
289
	if (mode1->clock && mode2->clock) {
290
		if (KHZ2PICOS(mode1->clock) != KHZ2PICOS(mode2->clock))
291
			return false;
292
	} else if (mode1->clock != mode2->clock)
293
		return false;
294
 
295
	if (mode1->hdisplay == mode2->hdisplay &&
296
	    mode1->hsync_start == mode2->hsync_start &&
297
	    mode1->hsync_end == mode2->hsync_end &&
298
	    mode1->htotal == mode2->htotal &&
299
	    mode1->hskew == mode2->hskew &&
300
	    mode1->vdisplay == mode2->vdisplay &&
301
	    mode1->vsync_start == mode2->vsync_start &&
302
	    mode1->vsync_end == mode2->vsync_end &&
303
	    mode1->vtotal == mode2->vtotal &&
304
	    mode1->vscan == mode2->vscan &&
305
	    mode1->flags == mode2->flags)
306
		return true;
307
 
308
	return false;
309
}
310
EXPORT_SYMBOL(drm_mode_equal);
311
 
312
/**
313
 * drm_mode_validate_size - make sure modes adhere to size constraints
314
 * @dev: DRM device
315
 * @mode_list: list of modes to check
316
 * @maxX: maximum width
317
 * @maxY: maximum height
318
 * @maxPitch: max pitch
319
 *
320
 * LOCKING:
321
 * Caller must hold a lock protecting @mode_list.
322
 *
323
 * The DRM device (@dev) has size and pitch limits.  Here we validate the
324
 * modes we probed for @dev against those limits and set their status as
325
 * necessary.
326
 */
327
void drm_mode_validate_size(struct drm_device *dev,
328
			    struct list_head *mode_list,
329
			    int maxX, int maxY, int maxPitch)
330
{
331
	struct drm_display_mode *mode;
332
 
333
	list_for_each_entry(mode, mode_list, head) {
334
		if (maxPitch > 0 && mode->hdisplay > maxPitch)
335
			mode->status = MODE_BAD_WIDTH;
336
 
337
		if (maxX > 0 && mode->hdisplay > maxX)
338
			mode->status = MODE_VIRTUAL_X;
339
 
340
		if (maxY > 0 && mode->vdisplay > maxY)
341
			mode->status = MODE_VIRTUAL_Y;
342
	}
343
}
344
EXPORT_SYMBOL(drm_mode_validate_size);
345
 
346
/**
347
 * drm_mode_validate_clocks - validate modes against clock limits
348
 * @dev: DRM device
349
 * @mode_list: list of modes to check
350
 * @min: minimum clock rate array
351
 * @max: maximum clock rate array
352
 * @n_ranges: number of clock ranges (size of arrays)
353
 *
354
 * LOCKING:
355
 * Caller must hold a lock protecting @mode_list.
356
 *
357
 * Some code may need to check a mode list against the clock limits of the
358
 * device in question.  This function walks the mode list, testing to make
359
 * sure each mode falls within a given range (defined by @min and @max
360
 * arrays) and sets @mode->status as needed.
361
 */
362
void drm_mode_validate_clocks(struct drm_device *dev,
363
			      struct list_head *mode_list,
364
			      int *min, int *max, int n_ranges)
365
{
366
	struct drm_display_mode *mode;
367
	int i;
368
 
369
	list_for_each_entry(mode, mode_list, head) {
370
		bool good = false;
371
		for (i = 0; i < n_ranges; i++) {
372
			if (mode->clock >= min[i] && mode->clock <= max[i]) {
373
				good = true;
374
				break;
375
			}
376
		}
377
		if (!good)
378
			mode->status = MODE_CLOCK_RANGE;
379
	}
380
}
381
EXPORT_SYMBOL(drm_mode_validate_clocks);
382
 
383
/**
384
 * drm_mode_prune_invalid - remove invalid modes from mode list
385
 * @dev: DRM device
386
 * @mode_list: list of modes to check
387
 * @verbose: be verbose about it
388
 *
389
 * LOCKING:
390
 * Caller must hold a lock protecting @mode_list.
391
 *
392
 * Once mode list generation is complete, a caller can use this routine to
393
 * remove invalid modes from a mode list.  If any of the modes have a
394
 * status other than %MODE_OK, they are removed from @mode_list and freed.
395
 */
396
void drm_mode_prune_invalid(struct drm_device *dev,
397
			    struct list_head *mode_list, bool verbose)
398
{
399
	struct drm_display_mode *mode, *t;
400
 
401
	list_for_each_entry_safe(mode, t, mode_list, head) {
402
		if (mode->status != MODE_OK) {
403
			list_del(&mode->head);
404
			if (verbose) {
405
				drm_mode_debug_printmodeline(mode);
406
				DRM_DEBUG_MODE(DRM_MODESET_DEBUG,
407
					"Not using %s mode %d\n",
408
					mode->name, mode->status);
409
			}
410
			drm_mode_destroy(dev, mode);
411
		}
412
	}
413
}
414
EXPORT_SYMBOL(drm_mode_prune_invalid);
415
 
416
/**
417
 * drm_mode_compare - compare modes for favorability
418
 * @lh_a: list_head for first mode
419
 * @lh_b: list_head for second mode
420
 *
421
 * LOCKING:
422
 * None.
423
 *
424
 * Compare two modes, given by @lh_a and @lh_b, returning a value indicating
425
 * which is better.
426
 *
427
 * RETURNS:
428
 * Negative if @lh_a is better than @lh_b, zero if they're equivalent, or
429
 * positive if @lh_b is better than @lh_a.
430
 */
431
static int drm_mode_compare(struct list_head *lh_a, struct list_head *lh_b)
432
{
433
	struct drm_display_mode *a = list_entry(lh_a, struct drm_display_mode, head);
434
	struct drm_display_mode *b = list_entry(lh_b, struct drm_display_mode, head);
435
	int diff;
436
 
437
	diff = ((b->type & DRM_MODE_TYPE_PREFERRED) != 0) -
438
		((a->type & DRM_MODE_TYPE_PREFERRED) != 0);
439
	if (diff)
440
		return diff;
441
	diff = b->hdisplay * b->vdisplay - a->hdisplay * a->vdisplay;
442
	if (diff)
443
		return diff;
444
	diff = b->clock - a->clock;
445
	return diff;
446
}
447
 
448
/* FIXME: what we don't have a list sort function? */
449
/* list sort from Mark J Roberts (mjr@znex.org) */
450
void list_sort(struct list_head *head,
451
	       int (*cmp)(struct list_head *a, struct list_head *b))
452
{
453
	struct list_head *p, *q, *e, *list, *tail, *oldhead;
454
	int insize, nmerges, psize, qsize, i;
455
 
456
	list = head->next;
457
	list_del(head);
458
	insize = 1;
459
	for (;;) {
460
		p = oldhead = list;
461
		list = tail = NULL;
462
		nmerges = 0;
463
 
464
		while (p) {
465
			nmerges++;
466
			q = p;
467
			psize = 0;
468
			for (i = 0; i < insize; i++) {
469
				psize++;
470
				q = q->next == oldhead ? NULL : q->next;
471
				if (!q)
472
					break;
473
			}
474
 
475
			qsize = insize;
476
			while (psize > 0 || (qsize > 0 && q)) {
477
				if (!psize) {
478
					e = q;
479
					q = q->next;
480
					qsize--;
481
					if (q == oldhead)
482
						q = NULL;
483
				} else if (!qsize || !q) {
484
					e = p;
485
					p = p->next;
486
					psize--;
487
					if (p == oldhead)
488
						p = NULL;
489
				} else if (cmp(p, q) <= 0) {
490
					e = p;
491
					p = p->next;
492
					psize--;
493
					if (p == oldhead)
494
						p = NULL;
495
				} else {
496
					e = q;
497
					q = q->next;
498
					qsize--;
499
					if (q == oldhead)
500
						q = NULL;
501
				}
502
				if (tail)
503
					tail->next = e;
504
				else
505
					list = e;
506
				e->prev = tail;
507
				tail = e;
508
			}
509
			p = q;
510
		}
511
 
512
		tail->next = list;
513
		list->prev = tail;
514
 
515
		if (nmerges <= 1)
516
			break;
517
 
518
		insize *= 2;
519
	}
520
 
521
	head->next = list;
522
	head->prev = list->prev;
523
	list->prev->next = head;
524
	list->prev = head;
525
}
526
 
527
/**
528
 * drm_mode_sort - sort mode list
529
 * @mode_list: list to sort
530
 *
531
 * LOCKING:
532
 * Caller must hold a lock protecting @mode_list.
533
 *
534
 * Sort @mode_list by favorability, putting good modes first.
535
 */
536
void drm_mode_sort(struct list_head *mode_list)
537
{
538
	list_sort(mode_list, drm_mode_compare);
539
}
540
EXPORT_SYMBOL(drm_mode_sort);
541
 
542
/**
543
 * drm_mode_connector_list_update - update the mode list for the connector
544
 * @connector: the connector to update
545
 *
546
 * LOCKING:
547
 * Caller must hold a lock protecting @mode_list.
548
 *
549
 * This moves the modes from the @connector probed_modes list
550
 * to the actual mode list. It compares the probed mode against the current
551
 * list and only adds different modes. All modes unverified after this point
552
 * will be removed by the prune invalid modes.
553
 */
554
void drm_mode_connector_list_update(struct drm_connector *connector)
555
{
556
	struct drm_display_mode *mode;
557
	struct drm_display_mode *pmode, *pt;
558
	int found_it;
559
 
560
	list_for_each_entry_safe(pmode, pt, &connector->probed_modes,
561
				 head) {
562
		found_it = 0;
563
		/* go through current modes checking for the new probed mode */
564
		list_for_each_entry(mode, &connector->modes, head) {
565
			if (drm_mode_equal(pmode, mode)) {
566
				found_it = 1;
567
				/* if equal delete the probed mode */
568
				mode->status = pmode->status;
569
				list_del(&pmode->head);
570
				drm_mode_destroy(connector->dev, pmode);
571
				break;
572
			}
573
		}
574
 
575
		if (!found_it) {
576
			list_move_tail(&pmode->head, &connector->modes);
577
		}
578
	}
579
}
580
EXPORT_SYMBOL(drm_mode_connector_list_update);