Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/drivers/gpu/drm/drm_fb_helper.c
15111 views
1
/*
2
* Copyright (c) 2006-2009 Red Hat Inc.
3
* Copyright (c) 2006-2008 Intel Corporation
4
* Copyright (c) 2007 Dave Airlie <[email protected]>
5
*
6
* DRM framebuffer helper 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
* Dave Airlie <[email protected]>
28
* Jesse Barnes <[email protected]>
29
*/
30
#include <linux/kernel.h>
31
#include <linux/sysrq.h>
32
#include <linux/slab.h>
33
#include <linux/fb.h>
34
#include "drmP.h"
35
#include "drm_crtc.h"
36
#include "drm_fb_helper.h"
37
#include "drm_crtc_helper.h"
38
39
MODULE_AUTHOR("David Airlie, Jesse Barnes");
40
MODULE_DESCRIPTION("DRM KMS helper");
41
MODULE_LICENSE("GPL and additional rights");
42
43
static LIST_HEAD(kernel_fb_helper_list);
44
45
/* simple single crtc case helper function */
46
int drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper)
47
{
48
struct drm_device *dev = fb_helper->dev;
49
struct drm_connector *connector;
50
int i;
51
52
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
53
struct drm_fb_helper_connector *fb_helper_connector;
54
55
fb_helper_connector = kzalloc(sizeof(struct drm_fb_helper_connector), GFP_KERNEL);
56
if (!fb_helper_connector)
57
goto fail;
58
59
fb_helper_connector->connector = connector;
60
fb_helper->connector_info[fb_helper->connector_count++] = fb_helper_connector;
61
}
62
return 0;
63
fail:
64
for (i = 0; i < fb_helper->connector_count; i++) {
65
kfree(fb_helper->connector_info[i]);
66
fb_helper->connector_info[i] = NULL;
67
}
68
fb_helper->connector_count = 0;
69
return -ENOMEM;
70
}
71
EXPORT_SYMBOL(drm_fb_helper_single_add_all_connectors);
72
73
static int drm_fb_helper_parse_command_line(struct drm_fb_helper *fb_helper)
74
{
75
struct drm_fb_helper_connector *fb_helper_conn;
76
int i;
77
78
for (i = 0; i < fb_helper->connector_count; i++) {
79
struct drm_cmdline_mode *mode;
80
struct drm_connector *connector;
81
char *option = NULL;
82
83
fb_helper_conn = fb_helper->connector_info[i];
84
connector = fb_helper_conn->connector;
85
mode = &fb_helper_conn->cmdline_mode;
86
87
/* do something on return - turn off connector maybe */
88
if (fb_get_options(drm_get_connector_name(connector), &option))
89
continue;
90
91
if (drm_mode_parse_command_line_for_connector(option,
92
connector,
93
mode)) {
94
if (mode->force) {
95
const char *s;
96
switch (mode->force) {
97
case DRM_FORCE_OFF: s = "OFF"; break;
98
case DRM_FORCE_ON_DIGITAL: s = "ON - dig"; break;
99
default:
100
case DRM_FORCE_ON: s = "ON"; break;
101
}
102
103
DRM_INFO("forcing %s connector %s\n",
104
drm_get_connector_name(connector), s);
105
connector->force = mode->force;
106
}
107
108
DRM_DEBUG_KMS("cmdline mode for connector %s %dx%d@%dHz%s%s%s\n",
109
drm_get_connector_name(connector),
110
mode->xres, mode->yres,
111
mode->refresh_specified ? mode->refresh : 60,
112
mode->rb ? " reduced blanking" : "",
113
mode->margins ? " with margins" : "",
114
mode->interlace ? " interlaced" : "");
115
}
116
117
}
118
return 0;
119
}
120
121
static void drm_fb_helper_save_lut_atomic(struct drm_crtc *crtc, struct drm_fb_helper *helper)
122
{
123
uint16_t *r_base, *g_base, *b_base;
124
int i;
125
126
r_base = crtc->gamma_store;
127
g_base = r_base + crtc->gamma_size;
128
b_base = g_base + crtc->gamma_size;
129
130
for (i = 0; i < crtc->gamma_size; i++)
131
helper->funcs->gamma_get(crtc, &r_base[i], &g_base[i], &b_base[i], i);
132
}
133
134
static void drm_fb_helper_restore_lut_atomic(struct drm_crtc *crtc)
135
{
136
uint16_t *r_base, *g_base, *b_base;
137
138
r_base = crtc->gamma_store;
139
g_base = r_base + crtc->gamma_size;
140
b_base = g_base + crtc->gamma_size;
141
142
crtc->funcs->gamma_set(crtc, r_base, g_base, b_base, 0, crtc->gamma_size);
143
}
144
145
int drm_fb_helper_debug_enter(struct fb_info *info)
146
{
147
struct drm_fb_helper *helper = info->par;
148
struct drm_crtc_helper_funcs *funcs;
149
int i;
150
151
if (list_empty(&kernel_fb_helper_list))
152
return false;
153
154
list_for_each_entry(helper, &kernel_fb_helper_list, kernel_fb_list) {
155
for (i = 0; i < helper->crtc_count; i++) {
156
struct drm_mode_set *mode_set =
157
&helper->crtc_info[i].mode_set;
158
159
if (!mode_set->crtc->enabled)
160
continue;
161
162
funcs = mode_set->crtc->helper_private;
163
drm_fb_helper_save_lut_atomic(mode_set->crtc, helper);
164
funcs->mode_set_base_atomic(mode_set->crtc,
165
mode_set->fb,
166
mode_set->x,
167
mode_set->y,
168
ENTER_ATOMIC_MODE_SET);
169
}
170
}
171
172
return 0;
173
}
174
EXPORT_SYMBOL(drm_fb_helper_debug_enter);
175
176
/* Find the real fb for a given fb helper CRTC */
177
static struct drm_framebuffer *drm_mode_config_fb(struct drm_crtc *crtc)
178
{
179
struct drm_device *dev = crtc->dev;
180
struct drm_crtc *c;
181
182
list_for_each_entry(c, &dev->mode_config.crtc_list, head) {
183
if (crtc->base.id == c->base.id)
184
return c->fb;
185
}
186
187
return NULL;
188
}
189
190
int drm_fb_helper_debug_leave(struct fb_info *info)
191
{
192
struct drm_fb_helper *helper = info->par;
193
struct drm_crtc *crtc;
194
struct drm_crtc_helper_funcs *funcs;
195
struct drm_framebuffer *fb;
196
int i;
197
198
for (i = 0; i < helper->crtc_count; i++) {
199
struct drm_mode_set *mode_set = &helper->crtc_info[i].mode_set;
200
crtc = mode_set->crtc;
201
funcs = crtc->helper_private;
202
fb = drm_mode_config_fb(crtc);
203
204
if (!crtc->enabled)
205
continue;
206
207
if (!fb) {
208
DRM_ERROR("no fb to restore??\n");
209
continue;
210
}
211
212
drm_fb_helper_restore_lut_atomic(mode_set->crtc);
213
funcs->mode_set_base_atomic(mode_set->crtc, fb, crtc->x,
214
crtc->y, LEAVE_ATOMIC_MODE_SET);
215
}
216
217
return 0;
218
}
219
EXPORT_SYMBOL(drm_fb_helper_debug_leave);
220
221
bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper)
222
{
223
bool error = false;
224
int i, ret;
225
for (i = 0; i < fb_helper->crtc_count; i++) {
226
struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set;
227
ret = drm_crtc_helper_set_config(mode_set);
228
if (ret)
229
error = true;
230
}
231
return error;
232
}
233
EXPORT_SYMBOL(drm_fb_helper_restore_fbdev_mode);
234
235
bool drm_fb_helper_force_kernel_mode(void)
236
{
237
bool ret, error = false;
238
struct drm_fb_helper *helper;
239
240
if (list_empty(&kernel_fb_helper_list))
241
return false;
242
243
list_for_each_entry(helper, &kernel_fb_helper_list, kernel_fb_list) {
244
if (helper->dev->switch_power_state == DRM_SWITCH_POWER_OFF)
245
continue;
246
247
ret = drm_fb_helper_restore_fbdev_mode(helper);
248
if (ret)
249
error = true;
250
}
251
return error;
252
}
253
254
int drm_fb_helper_panic(struct notifier_block *n, unsigned long ununsed,
255
void *panic_str)
256
{
257
printk(KERN_ERR "panic occurred, switching back to text console\n");
258
return drm_fb_helper_force_kernel_mode();
259
return 0;
260
}
261
EXPORT_SYMBOL(drm_fb_helper_panic);
262
263
static struct notifier_block paniced = {
264
.notifier_call = drm_fb_helper_panic,
265
};
266
267
/**
268
* drm_fb_helper_restore - restore the framebuffer console (kernel) config
269
*
270
* Restore's the kernel's fbcon mode, used for lastclose & panic paths.
271
*/
272
void drm_fb_helper_restore(void)
273
{
274
bool ret;
275
ret = drm_fb_helper_force_kernel_mode();
276
if (ret == true)
277
DRM_ERROR("Failed to restore crtc configuration\n");
278
}
279
EXPORT_SYMBOL(drm_fb_helper_restore);
280
281
#ifdef CONFIG_MAGIC_SYSRQ
282
static void drm_fb_helper_restore_work_fn(struct work_struct *ignored)
283
{
284
drm_fb_helper_restore();
285
}
286
static DECLARE_WORK(drm_fb_helper_restore_work, drm_fb_helper_restore_work_fn);
287
288
static void drm_fb_helper_sysrq(int dummy1)
289
{
290
schedule_work(&drm_fb_helper_restore_work);
291
}
292
293
static struct sysrq_key_op sysrq_drm_fb_helper_restore_op = {
294
.handler = drm_fb_helper_sysrq,
295
.help_msg = "force-fb(V)",
296
.action_msg = "Restore framebuffer console",
297
};
298
#else
299
static struct sysrq_key_op sysrq_drm_fb_helper_restore_op = { };
300
#endif
301
302
static void drm_fb_helper_on(struct fb_info *info)
303
{
304
struct drm_fb_helper *fb_helper = info->par;
305
struct drm_device *dev = fb_helper->dev;
306
struct drm_crtc *crtc;
307
struct drm_crtc_helper_funcs *crtc_funcs;
308
struct drm_connector *connector;
309
struct drm_encoder *encoder;
310
int i, j;
311
312
/*
313
* For each CRTC in this fb, turn the crtc on then,
314
* find all associated encoders and turn them on.
315
*/
316
mutex_lock(&dev->mode_config.mutex);
317
for (i = 0; i < fb_helper->crtc_count; i++) {
318
crtc = fb_helper->crtc_info[i].mode_set.crtc;
319
crtc_funcs = crtc->helper_private;
320
321
if (!crtc->enabled)
322
continue;
323
324
crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON);
325
326
/* Walk the connectors & encoders on this fb turning them on */
327
for (j = 0; j < fb_helper->connector_count; j++) {
328
connector = fb_helper->connector_info[j]->connector;
329
connector->dpms = DRM_MODE_DPMS_ON;
330
drm_connector_property_set_value(connector,
331
dev->mode_config.dpms_property,
332
DRM_MODE_DPMS_ON);
333
}
334
/* Found a CRTC on this fb, now find encoders */
335
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
336
if (encoder->crtc == crtc) {
337
struct drm_encoder_helper_funcs *encoder_funcs;
338
339
encoder_funcs = encoder->helper_private;
340
encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON);
341
}
342
}
343
}
344
mutex_unlock(&dev->mode_config.mutex);
345
}
346
347
static void drm_fb_helper_off(struct fb_info *info, int dpms_mode)
348
{
349
struct drm_fb_helper *fb_helper = info->par;
350
struct drm_device *dev = fb_helper->dev;
351
struct drm_crtc *crtc;
352
struct drm_crtc_helper_funcs *crtc_funcs;
353
struct drm_connector *connector;
354
struct drm_encoder *encoder;
355
int i, j;
356
357
/*
358
* For each CRTC in this fb, find all associated encoders
359
* and turn them off, then turn off the CRTC.
360
*/
361
mutex_lock(&dev->mode_config.mutex);
362
for (i = 0; i < fb_helper->crtc_count; i++) {
363
crtc = fb_helper->crtc_info[i].mode_set.crtc;
364
crtc_funcs = crtc->helper_private;
365
366
if (!crtc->enabled)
367
continue;
368
369
/* Walk the connectors on this fb and mark them off */
370
for (j = 0; j < fb_helper->connector_count; j++) {
371
connector = fb_helper->connector_info[j]->connector;
372
connector->dpms = dpms_mode;
373
drm_connector_property_set_value(connector,
374
dev->mode_config.dpms_property,
375
dpms_mode);
376
}
377
/* Found a CRTC on this fb, now find encoders */
378
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
379
if (encoder->crtc == crtc) {
380
struct drm_encoder_helper_funcs *encoder_funcs;
381
382
encoder_funcs = encoder->helper_private;
383
encoder_funcs->dpms(encoder, dpms_mode);
384
}
385
}
386
crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF);
387
}
388
mutex_unlock(&dev->mode_config.mutex);
389
}
390
391
int drm_fb_helper_blank(int blank, struct fb_info *info)
392
{
393
switch (blank) {
394
/* Display: On; HSync: On, VSync: On */
395
case FB_BLANK_UNBLANK:
396
drm_fb_helper_on(info);
397
break;
398
/* Display: Off; HSync: On, VSync: On */
399
case FB_BLANK_NORMAL:
400
drm_fb_helper_off(info, DRM_MODE_DPMS_STANDBY);
401
break;
402
/* Display: Off; HSync: Off, VSync: On */
403
case FB_BLANK_HSYNC_SUSPEND:
404
drm_fb_helper_off(info, DRM_MODE_DPMS_STANDBY);
405
break;
406
/* Display: Off; HSync: On, VSync: Off */
407
case FB_BLANK_VSYNC_SUSPEND:
408
drm_fb_helper_off(info, DRM_MODE_DPMS_SUSPEND);
409
break;
410
/* Display: Off; HSync: Off, VSync: Off */
411
case FB_BLANK_POWERDOWN:
412
drm_fb_helper_off(info, DRM_MODE_DPMS_OFF);
413
break;
414
}
415
return 0;
416
}
417
EXPORT_SYMBOL(drm_fb_helper_blank);
418
419
static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper)
420
{
421
int i;
422
423
for (i = 0; i < helper->connector_count; i++)
424
kfree(helper->connector_info[i]);
425
kfree(helper->connector_info);
426
for (i = 0; i < helper->crtc_count; i++)
427
kfree(helper->crtc_info[i].mode_set.connectors);
428
kfree(helper->crtc_info);
429
}
430
431
int drm_fb_helper_init(struct drm_device *dev,
432
struct drm_fb_helper *fb_helper,
433
int crtc_count, int max_conn_count)
434
{
435
struct drm_crtc *crtc;
436
int ret = 0;
437
int i;
438
439
fb_helper->dev = dev;
440
441
INIT_LIST_HEAD(&fb_helper->kernel_fb_list);
442
443
fb_helper->crtc_info = kcalloc(crtc_count, sizeof(struct drm_fb_helper_crtc), GFP_KERNEL);
444
if (!fb_helper->crtc_info)
445
return -ENOMEM;
446
447
fb_helper->crtc_count = crtc_count;
448
fb_helper->connector_info = kcalloc(dev->mode_config.num_connector, sizeof(struct drm_fb_helper_connector *), GFP_KERNEL);
449
if (!fb_helper->connector_info) {
450
kfree(fb_helper->crtc_info);
451
return -ENOMEM;
452
}
453
fb_helper->connector_count = 0;
454
455
for (i = 0; i < crtc_count; i++) {
456
fb_helper->crtc_info[i].mode_set.connectors =
457
kcalloc(max_conn_count,
458
sizeof(struct drm_connector *),
459
GFP_KERNEL);
460
461
if (!fb_helper->crtc_info[i].mode_set.connectors) {
462
ret = -ENOMEM;
463
goto out_free;
464
}
465
fb_helper->crtc_info[i].mode_set.num_connectors = 0;
466
}
467
468
i = 0;
469
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
470
fb_helper->crtc_info[i].crtc_id = crtc->base.id;
471
fb_helper->crtc_info[i].mode_set.crtc = crtc;
472
i++;
473
}
474
fb_helper->conn_limit = max_conn_count;
475
return 0;
476
out_free:
477
drm_fb_helper_crtc_free(fb_helper);
478
return -ENOMEM;
479
}
480
EXPORT_SYMBOL(drm_fb_helper_init);
481
482
void drm_fb_helper_fini(struct drm_fb_helper *fb_helper)
483
{
484
if (!list_empty(&fb_helper->kernel_fb_list)) {
485
list_del(&fb_helper->kernel_fb_list);
486
if (list_empty(&kernel_fb_helper_list)) {
487
printk(KERN_INFO "drm: unregistered panic notifier\n");
488
atomic_notifier_chain_unregister(&panic_notifier_list,
489
&paniced);
490
unregister_sysrq_key('v', &sysrq_drm_fb_helper_restore_op);
491
}
492
}
493
494
drm_fb_helper_crtc_free(fb_helper);
495
496
}
497
EXPORT_SYMBOL(drm_fb_helper_fini);
498
499
static int setcolreg(struct drm_crtc *crtc, u16 red, u16 green,
500
u16 blue, u16 regno, struct fb_info *info)
501
{
502
struct drm_fb_helper *fb_helper = info->par;
503
struct drm_framebuffer *fb = fb_helper->fb;
504
int pindex;
505
506
if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
507
u32 *palette;
508
u32 value;
509
/* place color in psuedopalette */
510
if (regno > 16)
511
return -EINVAL;
512
palette = (u32 *)info->pseudo_palette;
513
red >>= (16 - info->var.red.length);
514
green >>= (16 - info->var.green.length);
515
blue >>= (16 - info->var.blue.length);
516
value = (red << info->var.red.offset) |
517
(green << info->var.green.offset) |
518
(blue << info->var.blue.offset);
519
if (info->var.transp.length > 0) {
520
u32 mask = (1 << info->var.transp.length) - 1;
521
mask <<= info->var.transp.offset;
522
value |= mask;
523
}
524
palette[regno] = value;
525
return 0;
526
}
527
528
pindex = regno;
529
530
if (fb->bits_per_pixel == 16) {
531
pindex = regno << 3;
532
533
if (fb->depth == 16 && regno > 63)
534
return -EINVAL;
535
if (fb->depth == 15 && regno > 31)
536
return -EINVAL;
537
538
if (fb->depth == 16) {
539
u16 r, g, b;
540
int i;
541
if (regno < 32) {
542
for (i = 0; i < 8; i++)
543
fb_helper->funcs->gamma_set(crtc, red,
544
green, blue, pindex + i);
545
}
546
547
fb_helper->funcs->gamma_get(crtc, &r,
548
&g, &b,
549
pindex >> 1);
550
551
for (i = 0; i < 4; i++)
552
fb_helper->funcs->gamma_set(crtc, r,
553
green, b,
554
(pindex >> 1) + i);
555
}
556
}
557
558
if (fb->depth != 16)
559
fb_helper->funcs->gamma_set(crtc, red, green, blue, pindex);
560
return 0;
561
}
562
563
int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info)
564
{
565
struct drm_fb_helper *fb_helper = info->par;
566
struct drm_crtc_helper_funcs *crtc_funcs;
567
u16 *red, *green, *blue, *transp;
568
struct drm_crtc *crtc;
569
int i, j, rc = 0;
570
int start;
571
572
for (i = 0; i < fb_helper->crtc_count; i++) {
573
crtc = fb_helper->crtc_info[i].mode_set.crtc;
574
crtc_funcs = crtc->helper_private;
575
576
red = cmap->red;
577
green = cmap->green;
578
blue = cmap->blue;
579
transp = cmap->transp;
580
start = cmap->start;
581
582
for (j = 0; j < cmap->len; j++) {
583
u16 hred, hgreen, hblue, htransp = 0xffff;
584
585
hred = *red++;
586
hgreen = *green++;
587
hblue = *blue++;
588
589
if (transp)
590
htransp = *transp++;
591
592
rc = setcolreg(crtc, hred, hgreen, hblue, start++, info);
593
if (rc)
594
return rc;
595
}
596
crtc_funcs->load_lut(crtc);
597
}
598
return rc;
599
}
600
EXPORT_SYMBOL(drm_fb_helper_setcmap);
601
602
int drm_fb_helper_check_var(struct fb_var_screeninfo *var,
603
struct fb_info *info)
604
{
605
struct drm_fb_helper *fb_helper = info->par;
606
struct drm_framebuffer *fb = fb_helper->fb;
607
int depth;
608
609
if (var->pixclock != 0 || in_dbg_master())
610
return -EINVAL;
611
612
/* Need to resize the fb object !!! */
613
if (var->bits_per_pixel > fb->bits_per_pixel || var->xres > fb->width || var->yres > fb->height) {
614
DRM_DEBUG("fb userspace requested width/height/bpp is greater than current fb "
615
"object %dx%d-%d > %dx%d-%d\n", var->xres, var->yres, var->bits_per_pixel,
616
fb->width, fb->height, fb->bits_per_pixel);
617
return -EINVAL;
618
}
619
620
switch (var->bits_per_pixel) {
621
case 16:
622
depth = (var->green.length == 6) ? 16 : 15;
623
break;
624
case 32:
625
depth = (var->transp.length > 0) ? 32 : 24;
626
break;
627
default:
628
depth = var->bits_per_pixel;
629
break;
630
}
631
632
switch (depth) {
633
case 8:
634
var->red.offset = 0;
635
var->green.offset = 0;
636
var->blue.offset = 0;
637
var->red.length = 8;
638
var->green.length = 8;
639
var->blue.length = 8;
640
var->transp.length = 0;
641
var->transp.offset = 0;
642
break;
643
case 15:
644
var->red.offset = 10;
645
var->green.offset = 5;
646
var->blue.offset = 0;
647
var->red.length = 5;
648
var->green.length = 5;
649
var->blue.length = 5;
650
var->transp.length = 1;
651
var->transp.offset = 15;
652
break;
653
case 16:
654
var->red.offset = 11;
655
var->green.offset = 5;
656
var->blue.offset = 0;
657
var->red.length = 5;
658
var->green.length = 6;
659
var->blue.length = 5;
660
var->transp.length = 0;
661
var->transp.offset = 0;
662
break;
663
case 24:
664
var->red.offset = 16;
665
var->green.offset = 8;
666
var->blue.offset = 0;
667
var->red.length = 8;
668
var->green.length = 8;
669
var->blue.length = 8;
670
var->transp.length = 0;
671
var->transp.offset = 0;
672
break;
673
case 32:
674
var->red.offset = 16;
675
var->green.offset = 8;
676
var->blue.offset = 0;
677
var->red.length = 8;
678
var->green.length = 8;
679
var->blue.length = 8;
680
var->transp.length = 8;
681
var->transp.offset = 24;
682
break;
683
default:
684
return -EINVAL;
685
}
686
return 0;
687
}
688
EXPORT_SYMBOL(drm_fb_helper_check_var);
689
690
/* this will let fbcon do the mode init */
691
int drm_fb_helper_set_par(struct fb_info *info)
692
{
693
struct drm_fb_helper *fb_helper = info->par;
694
struct drm_device *dev = fb_helper->dev;
695
struct fb_var_screeninfo *var = &info->var;
696
struct drm_crtc *crtc;
697
int ret;
698
int i;
699
700
if (var->pixclock != 0) {
701
DRM_ERROR("PIXEL CLOCK SET\n");
702
return -EINVAL;
703
}
704
705
mutex_lock(&dev->mode_config.mutex);
706
for (i = 0; i < fb_helper->crtc_count; i++) {
707
crtc = fb_helper->crtc_info[i].mode_set.crtc;
708
ret = crtc->funcs->set_config(&fb_helper->crtc_info[i].mode_set);
709
if (ret) {
710
mutex_unlock(&dev->mode_config.mutex);
711
return ret;
712
}
713
}
714
mutex_unlock(&dev->mode_config.mutex);
715
716
if (fb_helper->delayed_hotplug) {
717
fb_helper->delayed_hotplug = false;
718
drm_fb_helper_hotplug_event(fb_helper);
719
}
720
return 0;
721
}
722
EXPORT_SYMBOL(drm_fb_helper_set_par);
723
724
int drm_fb_helper_pan_display(struct fb_var_screeninfo *var,
725
struct fb_info *info)
726
{
727
struct drm_fb_helper *fb_helper = info->par;
728
struct drm_device *dev = fb_helper->dev;
729
struct drm_mode_set *modeset;
730
struct drm_crtc *crtc;
731
int ret = 0;
732
int i;
733
734
mutex_lock(&dev->mode_config.mutex);
735
for (i = 0; i < fb_helper->crtc_count; i++) {
736
crtc = fb_helper->crtc_info[i].mode_set.crtc;
737
738
modeset = &fb_helper->crtc_info[i].mode_set;
739
740
modeset->x = var->xoffset;
741
modeset->y = var->yoffset;
742
743
if (modeset->num_connectors) {
744
ret = crtc->funcs->set_config(modeset);
745
if (!ret) {
746
info->var.xoffset = var->xoffset;
747
info->var.yoffset = var->yoffset;
748
}
749
}
750
}
751
mutex_unlock(&dev->mode_config.mutex);
752
return ret;
753
}
754
EXPORT_SYMBOL(drm_fb_helper_pan_display);
755
756
int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper,
757
int preferred_bpp)
758
{
759
int new_fb = 0;
760
int crtc_count = 0;
761
int i;
762
struct fb_info *info;
763
struct drm_fb_helper_surface_size sizes;
764
int gamma_size = 0;
765
766
memset(&sizes, 0, sizeof(struct drm_fb_helper_surface_size));
767
sizes.surface_depth = 24;
768
sizes.surface_bpp = 32;
769
sizes.fb_width = (unsigned)-1;
770
sizes.fb_height = (unsigned)-1;
771
772
/* if driver picks 8 or 16 by default use that
773
for both depth/bpp */
774
if (preferred_bpp != sizes.surface_bpp) {
775
sizes.surface_depth = sizes.surface_bpp = preferred_bpp;
776
}
777
/* first up get a count of crtcs now in use and new min/maxes width/heights */
778
for (i = 0; i < fb_helper->connector_count; i++) {
779
struct drm_fb_helper_connector *fb_helper_conn = fb_helper->connector_info[i];
780
struct drm_cmdline_mode *cmdline_mode;
781
782
cmdline_mode = &fb_helper_conn->cmdline_mode;
783
784
if (cmdline_mode->bpp_specified) {
785
switch (cmdline_mode->bpp) {
786
case 8:
787
sizes.surface_depth = sizes.surface_bpp = 8;
788
break;
789
case 15:
790
sizes.surface_depth = 15;
791
sizes.surface_bpp = 16;
792
break;
793
case 16:
794
sizes.surface_depth = sizes.surface_bpp = 16;
795
break;
796
case 24:
797
sizes.surface_depth = sizes.surface_bpp = 24;
798
break;
799
case 32:
800
sizes.surface_depth = 24;
801
sizes.surface_bpp = 32;
802
break;
803
}
804
break;
805
}
806
}
807
808
crtc_count = 0;
809
for (i = 0; i < fb_helper->crtc_count; i++) {
810
struct drm_display_mode *desired_mode;
811
desired_mode = fb_helper->crtc_info[i].desired_mode;
812
813
if (desired_mode) {
814
if (gamma_size == 0)
815
gamma_size = fb_helper->crtc_info[i].mode_set.crtc->gamma_size;
816
if (desired_mode->hdisplay < sizes.fb_width)
817
sizes.fb_width = desired_mode->hdisplay;
818
if (desired_mode->vdisplay < sizes.fb_height)
819
sizes.fb_height = desired_mode->vdisplay;
820
if (desired_mode->hdisplay > sizes.surface_width)
821
sizes.surface_width = desired_mode->hdisplay;
822
if (desired_mode->vdisplay > sizes.surface_height)
823
sizes.surface_height = desired_mode->vdisplay;
824
crtc_count++;
825
}
826
}
827
828
if (crtc_count == 0 || sizes.fb_width == -1 || sizes.fb_height == -1) {
829
/* hmm everyone went away - assume VGA cable just fell out
830
and will come back later. */
831
DRM_INFO("Cannot find any crtc or sizes - going 1024x768\n");
832
sizes.fb_width = sizes.surface_width = 1024;
833
sizes.fb_height = sizes.surface_height = 768;
834
}
835
836
/* push down into drivers */
837
new_fb = (*fb_helper->funcs->fb_probe)(fb_helper, &sizes);
838
if (new_fb < 0)
839
return new_fb;
840
841
info = fb_helper->fbdev;
842
843
/* set the fb pointer */
844
for (i = 0; i < fb_helper->crtc_count; i++) {
845
fb_helper->crtc_info[i].mode_set.fb = fb_helper->fb;
846
}
847
848
if (new_fb) {
849
info->var.pixclock = 0;
850
if (register_framebuffer(info) < 0) {
851
return -EINVAL;
852
}
853
854
printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node,
855
info->fix.id);
856
857
} else {
858
drm_fb_helper_set_par(info);
859
}
860
861
/* Switch back to kernel console on panic */
862
/* multi card linked list maybe */
863
if (list_empty(&kernel_fb_helper_list)) {
864
printk(KERN_INFO "drm: registered panic notifier\n");
865
atomic_notifier_chain_register(&panic_notifier_list,
866
&paniced);
867
register_sysrq_key('v', &sysrq_drm_fb_helper_restore_op);
868
}
869
if (new_fb)
870
list_add(&fb_helper->kernel_fb_list, &kernel_fb_helper_list);
871
872
return 0;
873
}
874
EXPORT_SYMBOL(drm_fb_helper_single_fb_probe);
875
876
void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch,
877
uint32_t depth)
878
{
879
info->fix.type = FB_TYPE_PACKED_PIXELS;
880
info->fix.visual = depth == 8 ? FB_VISUAL_PSEUDOCOLOR :
881
FB_VISUAL_TRUECOLOR;
882
info->fix.mmio_start = 0;
883
info->fix.mmio_len = 0;
884
info->fix.type_aux = 0;
885
info->fix.xpanstep = 1; /* doing it in hw */
886
info->fix.ypanstep = 1; /* doing it in hw */
887
info->fix.ywrapstep = 0;
888
info->fix.accel = FB_ACCEL_NONE;
889
info->fix.type_aux = 0;
890
891
info->fix.line_length = pitch;
892
return;
893
}
894
EXPORT_SYMBOL(drm_fb_helper_fill_fix);
895
896
void drm_fb_helper_fill_var(struct fb_info *info, struct drm_fb_helper *fb_helper,
897
uint32_t fb_width, uint32_t fb_height)
898
{
899
struct drm_framebuffer *fb = fb_helper->fb;
900
info->pseudo_palette = fb_helper->pseudo_palette;
901
info->var.xres_virtual = fb->width;
902
info->var.yres_virtual = fb->height;
903
info->var.bits_per_pixel = fb->bits_per_pixel;
904
info->var.accel_flags = FB_ACCELF_TEXT;
905
info->var.xoffset = 0;
906
info->var.yoffset = 0;
907
info->var.activate = FB_ACTIVATE_NOW;
908
info->var.height = -1;
909
info->var.width = -1;
910
911
switch (fb->depth) {
912
case 8:
913
info->var.red.offset = 0;
914
info->var.green.offset = 0;
915
info->var.blue.offset = 0;
916
info->var.red.length = 8; /* 8bit DAC */
917
info->var.green.length = 8;
918
info->var.blue.length = 8;
919
info->var.transp.offset = 0;
920
info->var.transp.length = 0;
921
break;
922
case 15:
923
info->var.red.offset = 10;
924
info->var.green.offset = 5;
925
info->var.blue.offset = 0;
926
info->var.red.length = 5;
927
info->var.green.length = 5;
928
info->var.blue.length = 5;
929
info->var.transp.offset = 15;
930
info->var.transp.length = 1;
931
break;
932
case 16:
933
info->var.red.offset = 11;
934
info->var.green.offset = 5;
935
info->var.blue.offset = 0;
936
info->var.red.length = 5;
937
info->var.green.length = 6;
938
info->var.blue.length = 5;
939
info->var.transp.offset = 0;
940
break;
941
case 24:
942
info->var.red.offset = 16;
943
info->var.green.offset = 8;
944
info->var.blue.offset = 0;
945
info->var.red.length = 8;
946
info->var.green.length = 8;
947
info->var.blue.length = 8;
948
info->var.transp.offset = 0;
949
info->var.transp.length = 0;
950
break;
951
case 32:
952
info->var.red.offset = 16;
953
info->var.green.offset = 8;
954
info->var.blue.offset = 0;
955
info->var.red.length = 8;
956
info->var.green.length = 8;
957
info->var.blue.length = 8;
958
info->var.transp.offset = 24;
959
info->var.transp.length = 8;
960
break;
961
default:
962
break;
963
}
964
965
info->var.xres = fb_width;
966
info->var.yres = fb_height;
967
}
968
EXPORT_SYMBOL(drm_fb_helper_fill_var);
969
970
static int drm_fb_helper_probe_connector_modes(struct drm_fb_helper *fb_helper,
971
uint32_t maxX,
972
uint32_t maxY)
973
{
974
struct drm_connector *connector;
975
int count = 0;
976
int i;
977
978
for (i = 0; i < fb_helper->connector_count; i++) {
979
connector = fb_helper->connector_info[i]->connector;
980
count += connector->funcs->fill_modes(connector, maxX, maxY);
981
}
982
983
return count;
984
}
985
986
static struct drm_display_mode *drm_has_preferred_mode(struct drm_fb_helper_connector *fb_connector, int width, int height)
987
{
988
struct drm_display_mode *mode;
989
990
list_for_each_entry(mode, &fb_connector->connector->modes, head) {
991
if (drm_mode_width(mode) > width ||
992
drm_mode_height(mode) > height)
993
continue;
994
if (mode->type & DRM_MODE_TYPE_PREFERRED)
995
return mode;
996
}
997
return NULL;
998
}
999
1000
static bool drm_has_cmdline_mode(struct drm_fb_helper_connector *fb_connector)
1001
{
1002
struct drm_cmdline_mode *cmdline_mode;
1003
cmdline_mode = &fb_connector->cmdline_mode;
1004
return cmdline_mode->specified;
1005
}
1006
1007
static struct drm_display_mode *drm_pick_cmdline_mode(struct drm_fb_helper_connector *fb_helper_conn,
1008
int width, int height)
1009
{
1010
struct drm_cmdline_mode *cmdline_mode;
1011
struct drm_display_mode *mode = NULL;
1012
1013
cmdline_mode = &fb_helper_conn->cmdline_mode;
1014
if (cmdline_mode->specified == false)
1015
return mode;
1016
1017
/* attempt to find a matching mode in the list of modes
1018
* we have gotten so far, if not add a CVT mode that conforms
1019
*/
1020
if (cmdline_mode->rb || cmdline_mode->margins)
1021
goto create_mode;
1022
1023
list_for_each_entry(mode, &fb_helper_conn->connector->modes, head) {
1024
/* check width/height */
1025
if (mode->hdisplay != cmdline_mode->xres ||
1026
mode->vdisplay != cmdline_mode->yres)
1027
continue;
1028
1029
if (cmdline_mode->refresh_specified) {
1030
if (mode->vrefresh != cmdline_mode->refresh)
1031
continue;
1032
}
1033
1034
if (cmdline_mode->interlace) {
1035
if (!(mode->flags & DRM_MODE_FLAG_INTERLACE))
1036
continue;
1037
}
1038
return mode;
1039
}
1040
1041
create_mode:
1042
mode = drm_mode_create_from_cmdline_mode(fb_helper_conn->connector->dev,
1043
cmdline_mode);
1044
list_add(&mode->head, &fb_helper_conn->connector->modes);
1045
return mode;
1046
}
1047
1048
static bool drm_connector_enabled(struct drm_connector *connector, bool strict)
1049
{
1050
bool enable;
1051
1052
if (strict) {
1053
enable = connector->status == connector_status_connected;
1054
} else {
1055
enable = connector->status != connector_status_disconnected;
1056
}
1057
return enable;
1058
}
1059
1060
static void drm_enable_connectors(struct drm_fb_helper *fb_helper,
1061
bool *enabled)
1062
{
1063
bool any_enabled = false;
1064
struct drm_connector *connector;
1065
int i = 0;
1066
1067
for (i = 0; i < fb_helper->connector_count; i++) {
1068
connector = fb_helper->connector_info[i]->connector;
1069
enabled[i] = drm_connector_enabled(connector, true);
1070
DRM_DEBUG_KMS("connector %d enabled? %s\n", connector->base.id,
1071
enabled[i] ? "yes" : "no");
1072
any_enabled |= enabled[i];
1073
}
1074
1075
if (any_enabled)
1076
return;
1077
1078
for (i = 0; i < fb_helper->connector_count; i++) {
1079
connector = fb_helper->connector_info[i]->connector;
1080
enabled[i] = drm_connector_enabled(connector, false);
1081
}
1082
}
1083
1084
static bool drm_target_cloned(struct drm_fb_helper *fb_helper,
1085
struct drm_display_mode **modes,
1086
bool *enabled, int width, int height)
1087
{
1088
int count, i, j;
1089
bool can_clone = false;
1090
struct drm_fb_helper_connector *fb_helper_conn;
1091
struct drm_display_mode *dmt_mode, *mode;
1092
1093
/* only contemplate cloning in the single crtc case */
1094
if (fb_helper->crtc_count > 1)
1095
return false;
1096
1097
count = 0;
1098
for (i = 0; i < fb_helper->connector_count; i++) {
1099
if (enabled[i])
1100
count++;
1101
}
1102
1103
/* only contemplate cloning if more than one connector is enabled */
1104
if (count <= 1)
1105
return false;
1106
1107
/* check the command line or if nothing common pick 1024x768 */
1108
can_clone = true;
1109
for (i = 0; i < fb_helper->connector_count; i++) {
1110
if (!enabled[i])
1111
continue;
1112
fb_helper_conn = fb_helper->connector_info[i];
1113
modes[i] = drm_pick_cmdline_mode(fb_helper_conn, width, height);
1114
if (!modes[i]) {
1115
can_clone = false;
1116
break;
1117
}
1118
for (j = 0; j < i; j++) {
1119
if (!enabled[j])
1120
continue;
1121
if (!drm_mode_equal(modes[j], modes[i]))
1122
can_clone = false;
1123
}
1124
}
1125
1126
if (can_clone) {
1127
DRM_DEBUG_KMS("can clone using command line\n");
1128
return true;
1129
}
1130
1131
/* try and find a 1024x768 mode on each connector */
1132
can_clone = true;
1133
dmt_mode = drm_mode_find_dmt(fb_helper->dev, 1024, 768, 60);
1134
1135
for (i = 0; i < fb_helper->connector_count; i++) {
1136
1137
if (!enabled[i])
1138
continue;
1139
1140
fb_helper_conn = fb_helper->connector_info[i];
1141
list_for_each_entry(mode, &fb_helper_conn->connector->modes, head) {
1142
if (drm_mode_equal(mode, dmt_mode))
1143
modes[i] = mode;
1144
}
1145
if (!modes[i])
1146
can_clone = false;
1147
}
1148
1149
if (can_clone) {
1150
DRM_DEBUG_KMS("can clone using 1024x768\n");
1151
return true;
1152
}
1153
DRM_INFO("kms: can't enable cloning when we probably wanted to.\n");
1154
return false;
1155
}
1156
1157
static bool drm_target_preferred(struct drm_fb_helper *fb_helper,
1158
struct drm_display_mode **modes,
1159
bool *enabled, int width, int height)
1160
{
1161
struct drm_fb_helper_connector *fb_helper_conn;
1162
int i;
1163
1164
for (i = 0; i < fb_helper->connector_count; i++) {
1165
fb_helper_conn = fb_helper->connector_info[i];
1166
1167
if (enabled[i] == false)
1168
continue;
1169
1170
DRM_DEBUG_KMS("looking for cmdline mode on connector %d\n",
1171
fb_helper_conn->connector->base.id);
1172
1173
/* got for command line mode first */
1174
modes[i] = drm_pick_cmdline_mode(fb_helper_conn, width, height);
1175
if (!modes[i]) {
1176
DRM_DEBUG_KMS("looking for preferred mode on connector %d\n",
1177
fb_helper_conn->connector->base.id);
1178
modes[i] = drm_has_preferred_mode(fb_helper_conn, width, height);
1179
}
1180
/* No preferred modes, pick one off the list */
1181
if (!modes[i] && !list_empty(&fb_helper_conn->connector->modes)) {
1182
list_for_each_entry(modes[i], &fb_helper_conn->connector->modes, head)
1183
break;
1184
}
1185
DRM_DEBUG_KMS("found mode %s\n", modes[i] ? modes[i]->name :
1186
"none");
1187
}
1188
return true;
1189
}
1190
1191
static int drm_pick_crtcs(struct drm_fb_helper *fb_helper,
1192
struct drm_fb_helper_crtc **best_crtcs,
1193
struct drm_display_mode **modes,
1194
int n, int width, int height)
1195
{
1196
int c, o;
1197
struct drm_device *dev = fb_helper->dev;
1198
struct drm_connector *connector;
1199
struct drm_connector_helper_funcs *connector_funcs;
1200
struct drm_encoder *encoder;
1201
struct drm_fb_helper_crtc *best_crtc;
1202
int my_score, best_score, score;
1203
struct drm_fb_helper_crtc **crtcs, *crtc;
1204
struct drm_fb_helper_connector *fb_helper_conn;
1205
1206
if (n == fb_helper->connector_count)
1207
return 0;
1208
1209
fb_helper_conn = fb_helper->connector_info[n];
1210
connector = fb_helper_conn->connector;
1211
1212
best_crtcs[n] = NULL;
1213
best_crtc = NULL;
1214
best_score = drm_pick_crtcs(fb_helper, best_crtcs, modes, n+1, width, height);
1215
if (modes[n] == NULL)
1216
return best_score;
1217
1218
crtcs = kzalloc(dev->mode_config.num_connector *
1219
sizeof(struct drm_fb_helper_crtc *), GFP_KERNEL);
1220
if (!crtcs)
1221
return best_score;
1222
1223
my_score = 1;
1224
if (connector->status == connector_status_connected)
1225
my_score++;
1226
if (drm_has_cmdline_mode(fb_helper_conn))
1227
my_score++;
1228
if (drm_has_preferred_mode(fb_helper_conn, width, height))
1229
my_score++;
1230
1231
connector_funcs = connector->helper_private;
1232
encoder = connector_funcs->best_encoder(connector);
1233
if (!encoder)
1234
goto out;
1235
1236
/* select a crtc for this connector and then attempt to configure
1237
remaining connectors */
1238
for (c = 0; c < fb_helper->crtc_count; c++) {
1239
crtc = &fb_helper->crtc_info[c];
1240
1241
if ((encoder->possible_crtcs & (1 << c)) == 0) {
1242
continue;
1243
}
1244
1245
for (o = 0; o < n; o++)
1246
if (best_crtcs[o] == crtc)
1247
break;
1248
1249
if (o < n) {
1250
/* ignore cloning unless only a single crtc */
1251
if (fb_helper->crtc_count > 1)
1252
continue;
1253
1254
if (!drm_mode_equal(modes[o], modes[n]))
1255
continue;
1256
}
1257
1258
crtcs[n] = crtc;
1259
memcpy(crtcs, best_crtcs, n * sizeof(struct drm_fb_helper_crtc *));
1260
score = my_score + drm_pick_crtcs(fb_helper, crtcs, modes, n + 1,
1261
width, height);
1262
if (score > best_score) {
1263
best_crtc = crtc;
1264
best_score = score;
1265
memcpy(best_crtcs, crtcs,
1266
dev->mode_config.num_connector *
1267
sizeof(struct drm_fb_helper_crtc *));
1268
}
1269
}
1270
out:
1271
kfree(crtcs);
1272
return best_score;
1273
}
1274
1275
static void drm_setup_crtcs(struct drm_fb_helper *fb_helper)
1276
{
1277
struct drm_device *dev = fb_helper->dev;
1278
struct drm_fb_helper_crtc **crtcs;
1279
struct drm_display_mode **modes;
1280
struct drm_encoder *encoder;
1281
struct drm_mode_set *modeset;
1282
bool *enabled;
1283
int width, height;
1284
int i, ret;
1285
1286
DRM_DEBUG_KMS("\n");
1287
1288
width = dev->mode_config.max_width;
1289
height = dev->mode_config.max_height;
1290
1291
/* clean out all the encoder/crtc combos */
1292
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
1293
encoder->crtc = NULL;
1294
}
1295
1296
crtcs = kcalloc(dev->mode_config.num_connector,
1297
sizeof(struct drm_fb_helper_crtc *), GFP_KERNEL);
1298
modes = kcalloc(dev->mode_config.num_connector,
1299
sizeof(struct drm_display_mode *), GFP_KERNEL);
1300
enabled = kcalloc(dev->mode_config.num_connector,
1301
sizeof(bool), GFP_KERNEL);
1302
1303
drm_enable_connectors(fb_helper, enabled);
1304
1305
ret = drm_target_cloned(fb_helper, modes, enabled, width, height);
1306
if (!ret) {
1307
ret = drm_target_preferred(fb_helper, modes, enabled, width, height);
1308
if (!ret)
1309
DRM_ERROR("Unable to find initial modes\n");
1310
}
1311
1312
DRM_DEBUG_KMS("picking CRTCs for %dx%d config\n", width, height);
1313
1314
drm_pick_crtcs(fb_helper, crtcs, modes, 0, width, height);
1315
1316
/* need to set the modesets up here for use later */
1317
/* fill out the connector<->crtc mappings into the modesets */
1318
for (i = 0; i < fb_helper->crtc_count; i++) {
1319
modeset = &fb_helper->crtc_info[i].mode_set;
1320
modeset->num_connectors = 0;
1321
}
1322
1323
for (i = 0; i < fb_helper->connector_count; i++) {
1324
struct drm_display_mode *mode = modes[i];
1325
struct drm_fb_helper_crtc *fb_crtc = crtcs[i];
1326
modeset = &fb_crtc->mode_set;
1327
1328
if (mode && fb_crtc) {
1329
DRM_DEBUG_KMS("desired mode %s set on crtc %d\n",
1330
mode->name, fb_crtc->mode_set.crtc->base.id);
1331
fb_crtc->desired_mode = mode;
1332
if (modeset->mode)
1333
drm_mode_destroy(dev, modeset->mode);
1334
modeset->mode = drm_mode_duplicate(dev,
1335
fb_crtc->desired_mode);
1336
modeset->connectors[modeset->num_connectors++] = fb_helper->connector_info[i]->connector;
1337
}
1338
}
1339
1340
kfree(crtcs);
1341
kfree(modes);
1342
kfree(enabled);
1343
}
1344
1345
/**
1346
* drm_helper_initial_config - setup a sane initial connector configuration
1347
* @dev: DRM device
1348
*
1349
* LOCKING:
1350
* Called at init time, must take mode config lock.
1351
*
1352
* Scan the CRTCs and connectors and try to put together an initial setup.
1353
* At the moment, this is a cloned configuration across all heads with
1354
* a new framebuffer object as the backing store.
1355
*
1356
* RETURNS:
1357
* Zero if everything went ok, nonzero otherwise.
1358
*/
1359
bool drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper, int bpp_sel)
1360
{
1361
struct drm_device *dev = fb_helper->dev;
1362
int count = 0;
1363
1364
/* disable all the possible outputs/crtcs before entering KMS mode */
1365
drm_helper_disable_unused_functions(fb_helper->dev);
1366
1367
drm_fb_helper_parse_command_line(fb_helper);
1368
1369
count = drm_fb_helper_probe_connector_modes(fb_helper,
1370
dev->mode_config.max_width,
1371
dev->mode_config.max_height);
1372
/*
1373
* we shouldn't end up with no modes here.
1374
*/
1375
if (count == 0) {
1376
printk(KERN_INFO "No connectors reported connected with modes\n");
1377
}
1378
drm_setup_crtcs(fb_helper);
1379
1380
return drm_fb_helper_single_fb_probe(fb_helper, bpp_sel);
1381
}
1382
EXPORT_SYMBOL(drm_fb_helper_initial_config);
1383
1384
/**
1385
* drm_fb_helper_hotplug_event - respond to a hotplug notification by
1386
* probing all the outputs attached to the fb.
1387
* @fb_helper: the drm_fb_helper
1388
*
1389
* LOCKING:
1390
* Called at runtime, must take mode config lock.
1391
*
1392
* Scan the connectors attached to the fb_helper and try to put together a
1393
* setup after *notification of a change in output configuration.
1394
*
1395
* RETURNS:
1396
* 0 on success and a non-zero error code otherwise.
1397
*/
1398
int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper)
1399
{
1400
struct drm_device *dev = fb_helper->dev;
1401
int count = 0;
1402
u32 max_width, max_height, bpp_sel;
1403
bool bound = false, crtcs_bound = false;
1404
struct drm_crtc *crtc;
1405
1406
if (!fb_helper->fb)
1407
return 0;
1408
1409
mutex_lock(&dev->mode_config.mutex);
1410
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
1411
if (crtc->fb)
1412
crtcs_bound = true;
1413
if (crtc->fb == fb_helper->fb)
1414
bound = true;
1415
}
1416
1417
if (!bound && crtcs_bound) {
1418
fb_helper->delayed_hotplug = true;
1419
mutex_unlock(&dev->mode_config.mutex);
1420
return 0;
1421
}
1422
DRM_DEBUG_KMS("\n");
1423
1424
max_width = fb_helper->fb->width;
1425
max_height = fb_helper->fb->height;
1426
bpp_sel = fb_helper->fb->bits_per_pixel;
1427
1428
count = drm_fb_helper_probe_connector_modes(fb_helper, max_width,
1429
max_height);
1430
drm_setup_crtcs(fb_helper);
1431
mutex_unlock(&dev->mode_config.mutex);
1432
1433
return drm_fb_helper_single_fb_probe(fb_helper, bpp_sel);
1434
}
1435
EXPORT_SYMBOL(drm_fb_helper_hotplug_event);
1436
1437
/* The Kconfig DRM_KMS_HELPER selects FRAMEBUFFER_CONSOLE (if !EXPERT)
1438
* but the module doesn't depend on any fb console symbols. At least
1439
* attempt to load fbcon to avoid leaving the system without a usable console.
1440
*/
1441
#if defined(CONFIG_FRAMEBUFFER_CONSOLE_MODULE) && !defined(CONFIG_EXPERT)
1442
static int __init drm_fb_helper_modinit(void)
1443
{
1444
const char *name = "fbcon";
1445
struct module *fbcon;
1446
1447
mutex_lock(&module_mutex);
1448
fbcon = find_module(name);
1449
mutex_unlock(&module_mutex);
1450
1451
if (!fbcon)
1452
request_module_nowait(name);
1453
return 0;
1454
}
1455
1456
module_init(drm_fb_helper_modinit);
1457
#endif
1458
1459