drm_fb_helper.c 38.6 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/*
 * Copyright (c) 2006-2009 Red Hat Inc.
 * Copyright (c) 2006-2008 Intel Corporation
 * Copyright (c) 2007 Dave Airlie <airlied@linux.ie>
 *
 * DRM framebuffer helper functions
 *
 * Permission to use, copy, modify, distribute, and sell this software and its
 * documentation for any purpose is hereby granted without fee, provided that
 * the above copyright notice appear in all copies and that both that copyright
 * notice and this permission notice appear in supporting documentation, and
 * that the name of the copyright holders not be used in advertising or
 * publicity pertaining to distribution of the software without specific,
 * written prior permission.  The copyright holders make no representations
 * about the suitability of this software for any purpose.  It is provided "as
 * is" without express or implied warranty.
 *
 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
 * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
 * OF THIS SOFTWARE.
 *
 * Authors:
 *      Dave Airlie <airlied@linux.ie>
 *      Jesse Barnes <jesse.barnes@intel.com>
 */
30
#include <linux/kernel.h>
31
#include <linux/sysrq.h>
32
#include <linux/slab.h>
33
#include <linux/fb.h>
34
#include <linux/module.h>
35
36
37
38
39
#include "drmP.h"
#include "drm_crtc.h"
#include "drm_fb_helper.h"
#include "drm_crtc_helper.h"

40
41
42
43
MODULE_AUTHOR("David Airlie, Jesse Barnes");
MODULE_DESCRIPTION("DRM KMS helper");
MODULE_LICENSE("GPL and additional rights");

44
45
static LIST_HEAD(kernel_fb_helper_list);

46
47
/* simple single crtc case helper function */
int drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper)
48
{
49
50
51
	struct drm_device *dev = fb_helper->dev;
	struct drm_connector *connector;
	int i;
52

53
54
55
56
57
58
	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
		struct drm_fb_helper_connector *fb_helper_connector;

		fb_helper_connector = kzalloc(sizeof(struct drm_fb_helper_connector), GFP_KERNEL);
		if (!fb_helper_connector)
			goto fail;
59

60
61
62
		fb_helper_connector->connector = connector;
		fb_helper->connector_info[fb_helper->connector_count++] = fb_helper_connector;
	}
63
	return 0;
64
65
66
67
68
69
70
fail:
	for (i = 0; i < fb_helper->connector_count; i++) {
		kfree(fb_helper->connector_info[i]);
		fb_helper->connector_info[i] = NULL;
	}
	fb_helper->connector_count = 0;
	return -ENOMEM;
71
}
72
EXPORT_SYMBOL(drm_fb_helper_single_add_all_connectors);
73

74
static int drm_fb_helper_parse_command_line(struct drm_fb_helper *fb_helper)
75
{
76
77
	struct drm_fb_helper_connector *fb_helper_conn;
	int i;
78

79
	for (i = 0; i < fb_helper->connector_count; i++) {
80
81
		struct drm_cmdline_mode *mode;
		struct drm_connector *connector;
82
83
		char *option = NULL;

84
		fb_helper_conn = fb_helper->connector_info[i];
85
86
		connector = fb_helper_conn->connector;
		mode = &fb_helper_conn->cmdline_mode;
87

88
		/* do something on return - turn off connector maybe */
89
		if (fb_get_options(drm_get_connector_name(connector), &option))
90
91
			continue;

92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
		if (drm_mode_parse_command_line_for_connector(option,
							      connector,
							      mode)) {
			if (mode->force) {
				const char *s;
				switch (mode->force) {
				case DRM_FORCE_OFF: s = "OFF"; break;
				case DRM_FORCE_ON_DIGITAL: s = "ON - dig"; break;
				default:
				case DRM_FORCE_ON: s = "ON"; break;
				}

				DRM_INFO("forcing %s connector %s\n",
					 drm_get_connector_name(connector), s);
				connector->force = mode->force;
			}

			DRM_DEBUG_KMS("cmdline mode for connector %s %dx%d@%dHz%s%s%s\n",
				      drm_get_connector_name(connector),
				      mode->xres, mode->yres,
				      mode->refresh_specified ? mode->refresh : 60,
				      mode->rb ? " reduced blanking" : "",
				      mode->margins ? " with margins" : "",
				      mode->interlace ?  " interlaced" : "");
		}

118
119
120
121
	}
	return 0;
}

122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
static void drm_fb_helper_save_lut_atomic(struct drm_crtc *crtc, struct drm_fb_helper *helper)
{
	uint16_t *r_base, *g_base, *b_base;
	int i;

	r_base = crtc->gamma_store;
	g_base = r_base + crtc->gamma_size;
	b_base = g_base + crtc->gamma_size;

	for (i = 0; i < crtc->gamma_size; i++)
		helper->funcs->gamma_get(crtc, &r_base[i], &g_base[i], &b_base[i], i);
}

static void drm_fb_helper_restore_lut_atomic(struct drm_crtc *crtc)
{
	uint16_t *r_base, *g_base, *b_base;

	r_base = crtc->gamma_store;
	g_base = r_base + crtc->gamma_size;
	b_base = g_base + crtc->gamma_size;

	crtc->funcs->gamma_set(crtc, r_base, g_base, b_base, 0, crtc->gamma_size);
}

Jesse Barnes's avatar
Jesse Barnes committed
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
int drm_fb_helper_debug_enter(struct fb_info *info)
{
	struct drm_fb_helper *helper = info->par;
	struct drm_crtc_helper_funcs *funcs;
	int i;

	if (list_empty(&kernel_fb_helper_list))
		return false;

	list_for_each_entry(helper, &kernel_fb_helper_list, kernel_fb_list) {
		for (i = 0; i < helper->crtc_count; i++) {
			struct drm_mode_set *mode_set =
				&helper->crtc_info[i].mode_set;

			if (!mode_set->crtc->enabled)
				continue;

			funcs =	mode_set->crtc->helper_private;
164
			drm_fb_helper_save_lut_atomic(mode_set->crtc, helper);
Jesse Barnes's avatar
Jesse Barnes committed
165
166
167
			funcs->mode_set_base_atomic(mode_set->crtc,
						    mode_set->fb,
						    mode_set->x,
168
						    mode_set->y,
169
						    ENTER_ATOMIC_MODE_SET);
Jesse Barnes's avatar
Jesse Barnes committed
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
		}
	}

	return 0;
}
EXPORT_SYMBOL(drm_fb_helper_debug_enter);

/* Find the real fb for a given fb helper CRTC */
static struct drm_framebuffer *drm_mode_config_fb(struct drm_crtc *crtc)
{
	struct drm_device *dev = crtc->dev;
	struct drm_crtc *c;

	list_for_each_entry(c, &dev->mode_config.crtc_list, head) {
		if (crtc->base.id == c->base.id)
			return c->fb;
	}

	return NULL;
}

int drm_fb_helper_debug_leave(struct fb_info *info)
{
	struct drm_fb_helper *helper = info->par;
	struct drm_crtc *crtc;
	struct drm_crtc_helper_funcs *funcs;
	struct drm_framebuffer *fb;
	int i;

	for (i = 0; i < helper->crtc_count; i++) {
		struct drm_mode_set *mode_set = &helper->crtc_info[i].mode_set;
		crtc = mode_set->crtc;
		funcs = crtc->helper_private;
		fb = drm_mode_config_fb(crtc);

		if (!crtc->enabled)
			continue;

		if (!fb) {
			DRM_ERROR("no fb to restore??\n");
			continue;
		}

213
		drm_fb_helper_restore_lut_atomic(mode_set->crtc);
Jesse Barnes's avatar
Jesse Barnes committed
214
		funcs->mode_set_base_atomic(mode_set->crtc, fb, crtc->x,
215
					    crtc->y, LEAVE_ATOMIC_MODE_SET);
Jesse Barnes's avatar
Jesse Barnes committed
216
217
218
219
220
221
	}

	return 0;
}
EXPORT_SYMBOL(drm_fb_helper_debug_leave);

222
223
224
225
226
227
228
229
230
231
232
233
234
235
bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper)
{
	bool error = false;
	int i, ret;
	for (i = 0; i < fb_helper->crtc_count; i++) {
		struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set;
		ret = drm_crtc_helper_set_config(mode_set);
		if (ret)
			error = true;
	}
	return error;
}
EXPORT_SYMBOL(drm_fb_helper_restore_fbdev_mode);

236
237
238
239
240
241
242
243
244
bool drm_fb_helper_force_kernel_mode(void)
{
	bool ret, error = false;
	struct drm_fb_helper *helper;

	if (list_empty(&kernel_fb_helper_list))
		return false;

	list_for_each_entry(helper, &kernel_fb_helper_list, kernel_fb_list) {
245
246
247
248
249
250
		if (helper->dev->switch_power_state == DRM_SWITCH_POWER_OFF)
			continue;

		ret = drm_fb_helper_restore_fbdev_mode(helper);
		if (ret)
			error = true;
251
252
253
254
255
256
257
	}
	return error;
}

int drm_fb_helper_panic(struct notifier_block *n, unsigned long ununsed,
			void *panic_str)
{
258
259
260
261
262
263
264
	/*
	 * It's a waste of time and effort to switch back to text console
	 * if the kernel should reboot before panic messages can be seen.
	 */
	if (panic_timeout < 0)
		return 0;

265
	printk(KERN_ERR "panic occurred, switching back to text console\n");
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
	return drm_fb_helper_force_kernel_mode();
}
EXPORT_SYMBOL(drm_fb_helper_panic);

static struct notifier_block paniced = {
	.notifier_call = drm_fb_helper_panic,
};

/**
 * drm_fb_helper_restore - restore the framebuffer console (kernel) config
 *
 * Restore's the kernel's fbcon mode, used for lastclose & panic paths.
 */
void drm_fb_helper_restore(void)
{
	bool ret;
	ret = drm_fb_helper_force_kernel_mode();
	if (ret == true)
		DRM_ERROR("Failed to restore crtc configuration\n");
}
EXPORT_SYMBOL(drm_fb_helper_restore);

288
#ifdef CONFIG_MAGIC_SYSRQ
289
290
291
292
293
294
static void drm_fb_helper_restore_work_fn(struct work_struct *ignored)
{
	drm_fb_helper_restore();
}
static DECLARE_WORK(drm_fb_helper_restore_work, drm_fb_helper_restore_work_fn);

295
static void drm_fb_helper_sysrq(int dummy1)
296
297
298
299
300
301
302
303
304
{
	schedule_work(&drm_fb_helper_restore_work);
}

static struct sysrq_key_op sysrq_drm_fb_helper_restore_op = {
	.handler = drm_fb_helper_sysrq,
	.help_msg = "force-fb(V)",
	.action_msg = "Restore framebuffer console",
};
305
306
#else
static struct sysrq_key_op sysrq_drm_fb_helper_restore_op = { };
307
#endif
308
309
310
311
312
313

static void drm_fb_helper_on(struct fb_info *info)
{
	struct drm_fb_helper *fb_helper = info->par;
	struct drm_device *dev = fb_helper->dev;
	struct drm_crtc *crtc;
314
	struct drm_crtc_helper_funcs *crtc_funcs;
315
	struct drm_connector *connector;
316
	struct drm_encoder *encoder;
317
	int i, j;
318
319
320
321
322

	/*
	 * For each CRTC in this fb, turn the crtc on then,
	 * find all associated encoders and turn them on.
	 */
323
	mutex_lock(&dev->mode_config.mutex);
324
	for (i = 0; i < fb_helper->crtc_count; i++) {
325
326
		crtc = fb_helper->crtc_info[i].mode_set.crtc;
		crtc_funcs = crtc->helper_private;
327

328
329
330
331
		if (!crtc->enabled)
			continue;

		crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON);
332

333
334
335
336
337
338
339
340
		/* Walk the connectors & encoders on this fb turning them on */
		for (j = 0; j < fb_helper->connector_count; j++) {
			connector = fb_helper->connector_info[j]->connector;
			connector->dpms = DRM_MODE_DPMS_ON;
			drm_connector_property_set_value(connector,
							 dev->mode_config.dpms_property,
							 DRM_MODE_DPMS_ON);
		}
341
342
343
344
		/* Found a CRTC on this fb, now find encoders */
		list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
			if (encoder->crtc == crtc) {
				struct drm_encoder_helper_funcs *encoder_funcs;
345

346
347
				encoder_funcs = encoder->helper_private;
				encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON);
348
349
350
			}
		}
	}
351
	mutex_unlock(&dev->mode_config.mutex);
352
353
354
355
356
357
358
}

static void drm_fb_helper_off(struct fb_info *info, int dpms_mode)
{
	struct drm_fb_helper *fb_helper = info->par;
	struct drm_device *dev = fb_helper->dev;
	struct drm_crtc *crtc;
359
	struct drm_crtc_helper_funcs *crtc_funcs;
360
	struct drm_connector *connector;
361
	struct drm_encoder *encoder;
362
	int i, j;
363
364
365
366
367

	/*
	 * For each CRTC in this fb, find all associated encoders
	 * and turn them off, then turn off the CRTC.
	 */
368
	mutex_lock(&dev->mode_config.mutex);
369
	for (i = 0; i < fb_helper->crtc_count; i++) {
370
371
		crtc = fb_helper->crtc_info[i].mode_set.crtc;
		crtc_funcs = crtc->helper_private;
372

373
374
		if (!crtc->enabled)
			continue;
375

376
377
378
379
380
381
382
383
		/* Walk the connectors on this fb and mark them off */
		for (j = 0; j < fb_helper->connector_count; j++) {
			connector = fb_helper->connector_info[j]->connector;
			connector->dpms = dpms_mode;
			drm_connector_property_set_value(connector,
							 dev->mode_config.dpms_property,
							 dpms_mode);
		}
384
385
386
387
		/* Found a CRTC on this fb, now find encoders */
		list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
			if (encoder->crtc == crtc) {
				struct drm_encoder_helper_funcs *encoder_funcs;
388

389
390
				encoder_funcs = encoder->helper_private;
				encoder_funcs->dpms(encoder, dpms_mode);
391
			}
392
		}
393
		crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF);
394
	}
395
	mutex_unlock(&dev->mode_config.mutex);
396
397
398
399
400
}

int drm_fb_helper_blank(int blank, struct fb_info *info)
{
	switch (blank) {
401
	/* Display: On; HSync: On, VSync: On */
402
403
404
	case FB_BLANK_UNBLANK:
		drm_fb_helper_on(info);
		break;
405
	/* Display: Off; HSync: On, VSync: On */
406
	case FB_BLANK_NORMAL:
407
		drm_fb_helper_off(info, DRM_MODE_DPMS_STANDBY);
408
		break;
409
	/* Display: Off; HSync: Off, VSync: On */
410
411
412
	case FB_BLANK_HSYNC_SUSPEND:
		drm_fb_helper_off(info, DRM_MODE_DPMS_STANDBY);
		break;
413
	/* Display: Off; HSync: On, VSync: Off */
414
415
416
	case FB_BLANK_VSYNC_SUSPEND:
		drm_fb_helper_off(info, DRM_MODE_DPMS_SUSPEND);
		break;
417
	/* Display: Off; HSync: Off, VSync: Off */
418
419
420
421
422
423
424
425
426
427
428
429
	case FB_BLANK_POWERDOWN:
		drm_fb_helper_off(info, DRM_MODE_DPMS_OFF);
		break;
	}
	return 0;
}
EXPORT_SYMBOL(drm_fb_helper_blank);

static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper)
{
	int i;

430
431
432
	for (i = 0; i < helper->connector_count; i++)
		kfree(helper->connector_info[i]);
	kfree(helper->connector_info);
433
434
435
436
437
	for (i = 0; i < helper->crtc_count; i++)
		kfree(helper->crtc_info[i].mode_set.connectors);
	kfree(helper->crtc_info);
}

438
439
int drm_fb_helper_init(struct drm_device *dev,
		       struct drm_fb_helper *fb_helper,
440
		       int crtc_count, int max_conn_count)
441
442
443
444
445
{
	struct drm_crtc *crtc;
	int ret = 0;
	int i;

446
447
448
449
450
451
	fb_helper->dev = dev;

	INIT_LIST_HEAD(&fb_helper->kernel_fb_list);

	fb_helper->crtc_info = kcalloc(crtc_count, sizeof(struct drm_fb_helper_crtc), GFP_KERNEL);
	if (!fb_helper->crtc_info)
452
453
		return -ENOMEM;

454
455
456
457
	fb_helper->crtc_count = crtc_count;
	fb_helper->connector_info = kcalloc(dev->mode_config.num_connector, sizeof(struct drm_fb_helper_connector *), GFP_KERNEL);
	if (!fb_helper->connector_info) {
		kfree(fb_helper->crtc_info);
458
459
		return -ENOMEM;
	}
460
	fb_helper->connector_count = 0;
461
462

	for (i = 0; i < crtc_count; i++) {
463
		fb_helper->crtc_info[i].mode_set.connectors =
464
465
466
467
			kcalloc(max_conn_count,
				sizeof(struct drm_connector *),
				GFP_KERNEL);

468
		if (!fb_helper->crtc_info[i].mode_set.connectors) {
469
470
471
			ret = -ENOMEM;
			goto out_free;
		}
472
		fb_helper->crtc_info[i].mode_set.num_connectors = 0;
473
474
475
476
	}

	i = 0;
	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
477
478
		fb_helper->crtc_info[i].crtc_id = crtc->base.id;
		fb_helper->crtc_info[i].mode_set.crtc = crtc;
479
480
		i++;
	}
481
	fb_helper->conn_limit = max_conn_count;
482
483
	return 0;
out_free:
484
	drm_fb_helper_crtc_free(fb_helper);
485
486
	return -ENOMEM;
}
487
488
489
490
491
492
493
EXPORT_SYMBOL(drm_fb_helper_init);

void drm_fb_helper_fini(struct drm_fb_helper *fb_helper)
{
	if (!list_empty(&fb_helper->kernel_fb_list)) {
		list_del(&fb_helper->kernel_fb_list);
		if (list_empty(&kernel_fb_helper_list)) {
494
			printk(KERN_INFO "drm: unregistered panic notifier\n");
495
496
497
498
499
500
501
502
503
504
			atomic_notifier_chain_unregister(&panic_notifier_list,
							 &paniced);
			unregister_sysrq_key('v', &sysrq_drm_fb_helper_restore_op);
		}
	}

	drm_fb_helper_crtc_free(fb_helper);

}
EXPORT_SYMBOL(drm_fb_helper_fini);
505

506
static int setcolreg(struct drm_crtc *crtc, u16 red, u16 green,
507
508
509
510
511
512
		     u16 blue, u16 regno, struct fb_info *info)
{
	struct drm_fb_helper *fb_helper = info->par;
	struct drm_framebuffer *fb = fb_helper->fb;
	int pindex;

513
514
515
516
517
518
519
520
521
522
523
524
525
	if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
		u32 *palette;
		u32 value;
		/* place color in psuedopalette */
		if (regno > 16)
			return -EINVAL;
		palette = (u32 *)info->pseudo_palette;
		red >>= (16 - info->var.red.length);
		green >>= (16 - info->var.green.length);
		blue >>= (16 - info->var.blue.length);
		value = (red << info->var.red.offset) |
			(green << info->var.green.offset) |
			(blue << info->var.blue.offset);
526
527
528
529
530
		if (info->var.transp.length > 0) {
			u32 mask = (1 << info->var.transp.length) - 1;
			mask <<= info->var.transp.offset;
			value |= mask;
		}
531
532
533
534
		palette[regno] = value;
		return 0;
	}

535
536
537
538
539
540
	pindex = regno;

	if (fb->bits_per_pixel == 16) {
		pindex = regno << 3;

		if (fb->depth == 16 && regno > 63)
541
			return -EINVAL;
542
		if (fb->depth == 15 && regno > 31)
543
			return -EINVAL;
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566

		if (fb->depth == 16) {
			u16 r, g, b;
			int i;
			if (regno < 32) {
				for (i = 0; i < 8; i++)
					fb_helper->funcs->gamma_set(crtc, red,
						green, blue, pindex + i);
			}

			fb_helper->funcs->gamma_get(crtc, &r,
						    &g, &b,
						    pindex >> 1);

			for (i = 0; i < 4; i++)
				fb_helper->funcs->gamma_set(crtc, r,
							    green, b,
							    (pindex >> 1) + i);
		}
	}

	if (fb->depth != 16)
		fb_helper->funcs->gamma_set(crtc, red, green, blue, pindex);
567
	return 0;
568
569
}

570
571
572
int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info)
{
	struct drm_fb_helper *fb_helper = info->par;
573
	struct drm_crtc_helper_funcs *crtc_funcs;
574
575
	u16 *red, *green, *blue, *transp;
	struct drm_crtc *crtc;
roel's avatar
roel committed
576
	int i, j, rc = 0;
577
578
	int start;

579
580
581
	for (i = 0; i < fb_helper->crtc_count; i++) {
		crtc = fb_helper->crtc_info[i].mode_set.crtc;
		crtc_funcs = crtc->helper_private;
582
583
584
585
586
587
588

		red = cmap->red;
		green = cmap->green;
		blue = cmap->blue;
		transp = cmap->transp;
		start = cmap->start;

roel's avatar
roel committed
589
		for (j = 0; j < cmap->len; j++) {
590
591
592
593
594
595
596
597
598
			u16 hred, hgreen, hblue, htransp = 0xffff;

			hred = *red++;
			hgreen = *green++;
			hblue = *blue++;

			if (transp)
				htransp = *transp++;

599
600
601
			rc = setcolreg(crtc, hred, hgreen, hblue, start++, info);
			if (rc)
				return rc;
602
603
604
605
606
607
608
		}
		crtc_funcs->load_lut(crtc);
	}
	return rc;
}
EXPORT_SYMBOL(drm_fb_helper_setcmap);

609
610
611
612
613
614
615
int drm_fb_helper_check_var(struct fb_var_screeninfo *var,
			    struct fb_info *info)
{
	struct drm_fb_helper *fb_helper = info->par;
	struct drm_framebuffer *fb = fb_helper->fb;
	int depth;

616
	if (var->pixclock != 0 || in_dbg_master())
617
618
619
		return -EINVAL;

	/* Need to resize the fb object !!! */
620
621
622
623
	if (var->bits_per_pixel > fb->bits_per_pixel || var->xres > fb->width || var->yres > fb->height) {
		DRM_DEBUG("fb userspace requested width/height/bpp is greater than current fb "
			  "object %dx%d-%d > %dx%d-%d\n", var->xres, var->yres, var->bits_per_pixel,
			  fb->width, fb->height, fb->bits_per_pixel);
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
		return -EINVAL;
	}

	switch (var->bits_per_pixel) {
	case 16:
		depth = (var->green.length == 6) ? 16 : 15;
		break;
	case 32:
		depth = (var->transp.length > 0) ? 32 : 24;
		break;
	default:
		depth = var->bits_per_pixel;
		break;
	}

	switch (depth) {
	case 8:
		var->red.offset = 0;
		var->green.offset = 0;
		var->blue.offset = 0;
		var->red.length = 8;
		var->green.length = 8;
		var->blue.length = 8;
		var->transp.length = 0;
		var->transp.offset = 0;
		break;
	case 15:
		var->red.offset = 10;
		var->green.offset = 5;
		var->blue.offset = 0;
		var->red.length = 5;
		var->green.length = 5;
		var->blue.length = 5;
		var->transp.length = 1;
		var->transp.offset = 15;
		break;
	case 16:
		var->red.offset = 11;
		var->green.offset = 5;
		var->blue.offset = 0;
		var->red.length = 5;
		var->green.length = 6;
		var->blue.length = 5;
		var->transp.length = 0;
		var->transp.offset = 0;
		break;
	case 24:
		var->red.offset = 16;
		var->green.offset = 8;
		var->blue.offset = 0;
		var->red.length = 8;
		var->green.length = 8;
		var->blue.length = 8;
		var->transp.length = 0;
		var->transp.offset = 0;
		break;
	case 32:
		var->red.offset = 16;
		var->green.offset = 8;
		var->blue.offset = 0;
		var->red.length = 8;
		var->green.length = 8;
		var->blue.length = 8;
		var->transp.length = 8;
		var->transp.offset = 24;
		break;
	default:
		return -EINVAL;
	}
	return 0;
}
EXPORT_SYMBOL(drm_fb_helper_check_var);

/* this will let fbcon do the mode init */
int drm_fb_helper_set_par(struct fb_info *info)
{
	struct drm_fb_helper *fb_helper = info->par;
	struct drm_device *dev = fb_helper->dev;
	struct fb_var_screeninfo *var = &info->var;
	struct drm_crtc *crtc;
	int ret;
	int i;

707
	if (var->pixclock != 0) {
708
		DRM_ERROR("PIXEL CLOCK SET\n");
709
710
711
		return -EINVAL;
	}

712
713
714
	mutex_lock(&dev->mode_config.mutex);
	for (i = 0; i < fb_helper->crtc_count; i++) {
		crtc = fb_helper->crtc_info[i].mode_set.crtc;
715
716
		ret = crtc->funcs->set_config(&fb_helper->crtc_info[i].mode_set);
		if (ret) {
717
			mutex_unlock(&dev->mode_config.mutex);
718
			return ret;
719
720
		}
	}
721
	mutex_unlock(&dev->mode_config.mutex);
722
723
724

	if (fb_helper->delayed_hotplug) {
		fb_helper->delayed_hotplug = false;
725
		drm_fb_helper_hotplug_event(fb_helper);
726
	}
727
728
729
730
731
732
733
734
735
736
737
738
739
740
	return 0;
}
EXPORT_SYMBOL(drm_fb_helper_set_par);

int drm_fb_helper_pan_display(struct fb_var_screeninfo *var,
			      struct fb_info *info)
{
	struct drm_fb_helper *fb_helper = info->par;
	struct drm_device *dev = fb_helper->dev;
	struct drm_mode_set *modeset;
	struct drm_crtc *crtc;
	int ret = 0;
	int i;

741
742
743
	mutex_lock(&dev->mode_config.mutex);
	for (i = 0; i < fb_helper->crtc_count; i++) {
		crtc = fb_helper->crtc_info[i].mode_set.crtc;
744
745
746
747
748
749
750
751
752
753
754
755
756
757

		modeset = &fb_helper->crtc_info[i].mode_set;

		modeset->x = var->xoffset;
		modeset->y = var->yoffset;

		if (modeset->num_connectors) {
			ret = crtc->funcs->set_config(modeset);
			if (!ret) {
				info->var.xoffset = var->xoffset;
				info->var.yoffset = var->yoffset;
			}
		}
	}
758
	mutex_unlock(&dev->mode_config.mutex);
759
760
761
762
	return ret;
}
EXPORT_SYMBOL(drm_fb_helper_pan_display);

763
764
int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper,
				  int preferred_bpp)
765
766
767
{
	int new_fb = 0;
	int crtc_count = 0;
768
	int i;
769
	struct fb_info *info;
770
	struct drm_fb_helper_surface_size sizes;
771
	int gamma_size = 0;
772
773
774
775
776
777

	memset(&sizes, 0, sizeof(struct drm_fb_helper_surface_size));
	sizes.surface_depth = 24;
	sizes.surface_bpp = 32;
	sizes.fb_width = (unsigned)-1;
	sizes.fb_height = (unsigned)-1;
778

779
780
	/* if driver picks 8 or 16 by default use that
	   for both depth/bpp */
781
782
	if (preferred_bpp != sizes.surface_bpp) {
		sizes.surface_depth = sizes.surface_bpp = preferred_bpp;
783
	}
784
	/* first up get a count of crtcs now in use and new min/maxes width/heights */
785
786
	for (i = 0; i < fb_helper->connector_count; i++) {
		struct drm_fb_helper_connector *fb_helper_conn = fb_helper->connector_info[i];
787
		struct drm_cmdline_mode *cmdline_mode;
788

789
		cmdline_mode = &fb_helper_conn->cmdline_mode;
790
791
792
793

		if (cmdline_mode->bpp_specified) {
			switch (cmdline_mode->bpp) {
			case 8:
794
				sizes.surface_depth = sizes.surface_bpp = 8;
795
796
				break;
			case 15:
797
798
				sizes.surface_depth = 15;
				sizes.surface_bpp = 16;
799
800
				break;
			case 16:
801
				sizes.surface_depth = sizes.surface_bpp = 16;
802
803
				break;
			case 24:
804
				sizes.surface_depth = sizes.surface_bpp = 24;
805
806
				break;
			case 32:
807
808
				sizes.surface_depth = 24;
				sizes.surface_bpp = 32;
809
810
811
812
813
814
				break;
			}
			break;
		}
	}

815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
	crtc_count = 0;
	for (i = 0; i < fb_helper->crtc_count; i++) {
		struct drm_display_mode *desired_mode;
		desired_mode = fb_helper->crtc_info[i].desired_mode;

		if (desired_mode) {
			if (gamma_size == 0)
				gamma_size = fb_helper->crtc_info[i].mode_set.crtc->gamma_size;
			if (desired_mode->hdisplay < sizes.fb_width)
				sizes.fb_width = desired_mode->hdisplay;
			if (desired_mode->vdisplay < sizes.fb_height)
				sizes.fb_height = desired_mode->vdisplay;
			if (desired_mode->hdisplay > sizes.surface_width)
				sizes.surface_width = desired_mode->hdisplay;
			if (desired_mode->vdisplay > sizes.surface_height)
				sizes.surface_height = desired_mode->vdisplay;
831
832
833
834
			crtc_count++;
		}
	}

835
	if (crtc_count == 0 || sizes.fb_width == -1 || sizes.fb_height == -1) {
836
837
		/* hmm everyone went away - assume VGA cable just fell out
		   and will come back later. */
838
		DRM_INFO("Cannot find any crtc or sizes - going 1024x768\n");
839
840
		sizes.fb_width = sizes.surface_width = 1024;
		sizes.fb_height = sizes.surface_height = 768;
841
842
	}

843
	/* push down into drivers */
844
	new_fb = (*fb_helper->funcs->fb_probe)(fb_helper, &sizes);
845
846
	if (new_fb < 0)
		return new_fb;
847

848
	info = fb_helper->fbdev;
849

850
851
852
	/* set the fb pointer */
	for (i = 0; i < fb_helper->crtc_count; i++) {
		fb_helper->crtc_info[i].mode_set.fb = fb_helper->fb;
853
854
855
	}

	if (new_fb) {
856
		info->var.pixclock = 0;
857
		if (register_framebuffer(info) < 0) {
858
			return -EINVAL;
859
		}
860
861
862
863

		printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node,
		       info->fix.id);

864
865
866
867
868
869
870
	} else {
		drm_fb_helper_set_par(info);
	}

	/* Switch back to kernel console on panic */
	/* multi card linked list maybe */
	if (list_empty(&kernel_fb_helper_list)) {
871
		printk(KERN_INFO "drm: registered panic notifier\n");
872
873
874
875
		atomic_notifier_chain_register(&panic_notifier_list,
					       &paniced);
		register_sysrq_key('v', &sysrq_drm_fb_helper_restore_op);
	}
876
877
878
	if (new_fb)
		list_add(&fb_helper->kernel_fb_list, &kernel_fb_helper_list);

879
880
881
882
	return 0;
}
EXPORT_SYMBOL(drm_fb_helper_single_fb_probe);

883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch,
			    uint32_t depth)
{
	info->fix.type = FB_TYPE_PACKED_PIXELS;
	info->fix.visual = depth == 8 ? FB_VISUAL_PSEUDOCOLOR :
		FB_VISUAL_TRUECOLOR;
	info->fix.mmio_start = 0;
	info->fix.mmio_len = 0;
	info->fix.type_aux = 0;
	info->fix.xpanstep = 1; /* doing it in hw */
	info->fix.ypanstep = 1; /* doing it in hw */
	info->fix.ywrapstep = 0;
	info->fix.accel = FB_ACCEL_NONE;
	info->fix.type_aux = 0;

	info->fix.line_length = pitch;
	return;
}
EXPORT_SYMBOL(drm_fb_helper_fill_fix);

903
void drm_fb_helper_fill_var(struct fb_info *info, struct drm_fb_helper *fb_helper,
904
905
			    uint32_t fb_width, uint32_t fb_height)
{
906
907
	struct drm_framebuffer *fb = fb_helper->fb;
	info->pseudo_palette = fb_helper->pseudo_palette;
908
909
910
	info->var.xres_virtual = fb->width;
	info->var.yres_virtual = fb->height;
	info->var.bits_per_pixel = fb->bits_per_pixel;
911
	info->var.accel_flags = FB_ACCELF_TEXT;
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
	info->var.xoffset = 0;
	info->var.yoffset = 0;
	info->var.activate = FB_ACTIVATE_NOW;
	info->var.height = -1;
	info->var.width = -1;

	switch (fb->depth) {
	case 8:
		info->var.red.offset = 0;
		info->var.green.offset = 0;
		info->var.blue.offset = 0;
		info->var.red.length = 8; /* 8bit DAC */
		info->var.green.length = 8;
		info->var.blue.length = 8;
		info->var.transp.offset = 0;
		info->var.transp.length = 0;
		break;
	case 15:
		info->var.red.offset = 10;
		info->var.green.offset = 5;
		info->var.blue.offset = 0;
		info->var.red.length = 5;
		info->var.green.length = 5;
		info->var.blue.length = 5;
		info->var.transp.offset = 15;
		info->var.transp.length = 1;
		break;
	case 16:
		info->var.red.offset = 11;
		info->var.green.offset = 5;
		info->var.blue.offset = 0;
		info->var.red.length = 5;
		info->var.green.length = 6;
		info->var.blue.length = 5;
		info->var.transp.offset = 0;
		break;
	case 24:
		info->var.red.offset = 16;
		info->var.green.offset = 8;
		info->var.blue.offset = 0;
		info->var.red.length = 8;
		info->var.green.length = 8;
		info->var.blue.length = 8;
		info->var.transp.offset = 0;
		info->var.transp.length = 0;
		break;
	case 32:
		info->var.red.offset = 16;
		info->var.green.offset = 8;
		info->var.blue.offset = 0;
		info->var.red.length = 8;
		info->var.green.length = 8;
		info->var.blue.length = 8;
		info->var.transp.offset = 24;
		info->var.transp.length = 8;
		break;
	default:
		break;
	}

	info->var.xres = fb_width;
	info->var.yres = fb_height;
}
EXPORT_SYMBOL(drm_fb_helper_fill_var);
976

977
978
979
static int drm_fb_helper_probe_connector_modes(struct drm_fb_helper *fb_helper,
					       uint32_t maxX,
					       uint32_t maxY)
980
981
982
{
	struct drm_connector *connector;
	int count = 0;
983
	int i;
984

985
986
	for (i = 0; i < fb_helper->connector_count; i++) {
		connector = fb_helper->connector_info[i]->connector;
987
988
989
990
991
992
		count += connector->funcs->fill_modes(connector, maxX, maxY);
	}

	return count;
}

993
static struct drm_display_mode *drm_has_preferred_mode(struct drm_fb_helper_connector *fb_connector, int width, int height)
994
995
996
{
	struct drm_display_mode *mode;

997
	list_for_each_entry(mode, &fb_connector->connector->modes, head) {
998
999
1000
		if (drm_mode_width(mode) > width ||
		    drm_mode_height(mode) > height)
			continue;
For faster browsing, not all history is shown. View entire blame