1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
31
32#include <linux/kernel.h>
33#include <linux/sysrq.h>
34#include <linux/slab.h>
35#include <linux/fb.h>
36#include <linux/module.h>
37#include <drm/drmP.h>
38#include <drm/drm_crtc.h>
39#include <drm/drm_fb_helper.h>
40#include <drm/drm_crtc_helper.h>
41#include <drm/drm_atomic.h>
42#include <drm/drm_atomic_helper.h>
43
44static bool drm_fbdev_emulation = true;
45module_param_named(fbdev_emulation, drm_fbdev_emulation, bool, 0600);
46MODULE_PARM_DESC(fbdev_emulation,
47 "Enable legacy fbdev emulation [default=true]");
48
49static LIST_HEAD(kernel_fb_helper_list);
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103int drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper)
104{
105 struct drm_device *dev = fb_helper->dev;
106 struct drm_connector *connector;
107 int i, ret;
108
109 if (!drm_fbdev_emulation)
110 return 0;
111
112 mutex_lock(&dev->mode_config.mutex);
113 drm_for_each_connector(connector, dev) {
114 ret = drm_fb_helper_add_one_connector(fb_helper, connector);
115
116 if (ret)
117 goto fail;
118 }
119 mutex_unlock(&dev->mode_config.mutex);
120 return 0;
121fail:
122 for (i = 0; i < fb_helper->connector_count; i++) {
123 kfree(fb_helper->connector_info[i]);
124 fb_helper->connector_info[i] = NULL;
125 }
126 fb_helper->connector_count = 0;
127 mutex_unlock(&dev->mode_config.mutex);
128
129 return ret;
130}
131EXPORT_SYMBOL(drm_fb_helper_single_add_all_connectors);
132
133int drm_fb_helper_add_one_connector(struct drm_fb_helper *fb_helper, struct drm_connector *connector)
134{
135 struct drm_fb_helper_connector **temp;
136 struct drm_fb_helper_connector *fb_helper_connector;
137
138 if (!drm_fbdev_emulation)
139 return 0;
140
141 WARN_ON(!mutex_is_locked(&fb_helper->dev->mode_config.mutex));
142 if (fb_helper->connector_count + 1 > fb_helper->connector_info_alloc_count) {
143 temp = krealloc(fb_helper->connector_info, sizeof(struct drm_fb_helper_connector *) * (fb_helper->connector_count + 1), GFP_KERNEL);
144 if (!temp)
145 return -ENOMEM;
146
147 fb_helper->connector_info_alloc_count = fb_helper->connector_count + 1;
148 fb_helper->connector_info = temp;
149 }
150
151
152 fb_helper_connector = kzalloc(sizeof(struct drm_fb_helper_connector), GFP_KERNEL);
153 if (!fb_helper_connector)
154 return -ENOMEM;
155
156 fb_helper_connector->connector = connector;
157 fb_helper->connector_info[fb_helper->connector_count++] = fb_helper_connector;
158 return 0;
159}
160EXPORT_SYMBOL(drm_fb_helper_add_one_connector);
161
162static void remove_from_modeset(struct drm_mode_set *set,
163 struct drm_connector *connector)
164{
165 int i, j;
166
167 for (i = 0; i < set->num_connectors; i++) {
168 if (set->connectors[i] == connector)
169 break;
170 }
171
172 if (i == set->num_connectors)
173 return;
174
175 for (j = i + 1; j < set->num_connectors; j++) {
176 set->connectors[j - 1] = set->connectors[j];
177 }
178 set->num_connectors--;
179
180
181
182
183 if (set->num_connectors == 0) {
184 set->fb = NULL;
185 drm_mode_destroy(connector->dev, set->mode);
186 set->mode = NULL;
187 }
188}
189
190int drm_fb_helper_remove_one_connector(struct drm_fb_helper *fb_helper,
191 struct drm_connector *connector)
192{
193 struct drm_fb_helper_connector *fb_helper_connector;
194 int i, j;
195
196 if (!drm_fbdev_emulation)
197 return 0;
198
199 WARN_ON(!mutex_is_locked(&fb_helper->dev->mode_config.mutex));
200
201 for (i = 0; i < fb_helper->connector_count; i++) {
202 if (fb_helper->connector_info[i]->connector == connector)
203 break;
204 }
205
206 if (i == fb_helper->connector_count)
207 return -EINVAL;
208 fb_helper_connector = fb_helper->connector_info[i];
209
210 for (j = i + 1; j < fb_helper->connector_count; j++) {
211 fb_helper->connector_info[j - 1] = fb_helper->connector_info[j];
212 }
213 fb_helper->connector_count--;
214 kfree(fb_helper_connector);
215
216
217 for (i = 0; i < fb_helper->crtc_count; i++)
218 remove_from_modeset(&fb_helper->crtc_info[i].mode_set, connector);
219
220 return 0;
221}
222EXPORT_SYMBOL(drm_fb_helper_remove_one_connector);
223
224static void drm_fb_helper_save_lut_atomic(struct drm_crtc *crtc, struct drm_fb_helper *helper)
225{
226 uint16_t *r_base, *g_base, *b_base;
227 int i;
228
229 if (helper->funcs->gamma_get == NULL)
230 return;
231
232 r_base = crtc->gamma_store;
233 g_base = r_base + crtc->gamma_size;
234 b_base = g_base + crtc->gamma_size;
235
236 for (i = 0; i < crtc->gamma_size; i++)
237 helper->funcs->gamma_get(crtc, &r_base[i], &g_base[i], &b_base[i], i);
238}
239
240static void drm_fb_helper_restore_lut_atomic(struct drm_crtc *crtc)
241{
242 uint16_t *r_base, *g_base, *b_base;
243
244 if (crtc->funcs->gamma_set == NULL)
245 return;
246
247 r_base = crtc->gamma_store;
248 g_base = r_base + crtc->gamma_size;
249 b_base = g_base + crtc->gamma_size;
250
251 crtc->funcs->gamma_set(crtc, r_base, g_base, b_base, 0, crtc->gamma_size);
252}
253
254
255
256
257
258int drm_fb_helper_debug_enter(struct fb_info *info)
259{
260 struct drm_fb_helper *helper = info->par;
261 const struct drm_crtc_helper_funcs *funcs;
262 int i;
263
264 list_for_each_entry(helper, &kernel_fb_helper_list, kernel_fb_list) {
265 for (i = 0; i < helper->crtc_count; i++) {
266 struct drm_mode_set *mode_set =
267 &helper->crtc_info[i].mode_set;
268
269 if (!mode_set->crtc->enabled)
270 continue;
271
272 funcs = mode_set->crtc->helper_private;
273 drm_fb_helper_save_lut_atomic(mode_set->crtc, helper);
274 funcs->mode_set_base_atomic(mode_set->crtc,
275 mode_set->fb,
276 mode_set->x,
277 mode_set->y,
278 ENTER_ATOMIC_MODE_SET);
279 }
280 }
281
282 return 0;
283}
284EXPORT_SYMBOL(drm_fb_helper_debug_enter);
285
286
287static struct drm_framebuffer *drm_mode_config_fb(struct drm_crtc *crtc)
288{
289 struct drm_device *dev = crtc->dev;
290 struct drm_crtc *c;
291
292 drm_for_each_crtc(c, dev) {
293 if (crtc->base.id == c->base.id)
294 return c->primary->fb;
295 }
296
297 return NULL;
298}
299
300
301
302
303
304int drm_fb_helper_debug_leave(struct fb_info *info)
305{
306 struct drm_fb_helper *helper = info->par;
307 struct drm_crtc *crtc;
308 const struct drm_crtc_helper_funcs *funcs;
309 struct drm_framebuffer *fb;
310 int i;
311
312 for (i = 0; i < helper->crtc_count; i++) {
313 struct drm_mode_set *mode_set = &helper->crtc_info[i].mode_set;
314 crtc = mode_set->crtc;
315 funcs = crtc->helper_private;
316 fb = drm_mode_config_fb(crtc);
317
318 if (!crtc->enabled)
319 continue;
320
321 if (!fb) {
322 DRM_ERROR("no fb to restore??\n");
323 continue;
324 }
325
326 drm_fb_helper_restore_lut_atomic(mode_set->crtc);
327 funcs->mode_set_base_atomic(mode_set->crtc, fb, crtc->x,
328 crtc->y, LEAVE_ATOMIC_MODE_SET);
329 }
330
331 return 0;
332}
333EXPORT_SYMBOL(drm_fb_helper_debug_leave);
334
335static int restore_fbdev_mode_atomic(struct drm_fb_helper *fb_helper)
336{
337 struct drm_device *dev = fb_helper->dev;
338 struct drm_plane *plane;
339 struct drm_atomic_state *state;
340 int i, ret;
341 unsigned plane_mask;
342
343 state = drm_atomic_state_alloc(dev);
344 if (!state)
345 return -ENOMEM;
346
347 state->acquire_ctx = dev->mode_config.acquire_ctx;
348retry:
349 plane_mask = 0;
350 drm_for_each_plane(plane, dev) {
351 struct drm_plane_state *plane_state;
352
353 plane_state = drm_atomic_get_plane_state(state, plane);
354 if (IS_ERR(plane_state)) {
355 ret = PTR_ERR(plane_state);
356 goto fail;
357 }
358
359 plane_state->rotation = BIT(DRM_ROTATE_0);
360
361 plane->old_fb = plane->fb;
362 plane_mask |= 1 << drm_plane_index(plane);
363
364
365 if (plane->type == DRM_PLANE_TYPE_PRIMARY)
366 continue;
367
368 ret = __drm_atomic_helper_disable_plane(plane, plane_state);
369 if (ret != 0)
370 goto fail;
371 }
372
373 for(i = 0; i < fb_helper->crtc_count; i++) {
374 struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set;
375
376 ret = __drm_atomic_helper_set_config(mode_set, state);
377 if (ret != 0)
378 goto fail;
379 }
380
381 ret = drm_atomic_commit(state);
382
383fail:
384 drm_atomic_clean_old_fb(dev, plane_mask, ret);
385
386 if (ret == -EDEADLK)
387 goto backoff;
388
389 if (ret != 0)
390 drm_atomic_state_free(state);
391
392 return ret;
393
394backoff:
395 drm_atomic_state_clear(state);
396 drm_atomic_legacy_backoff(state);
397
398 goto retry;
399}
400
401static int restore_fbdev_mode(struct drm_fb_helper *fb_helper)
402{
403 struct drm_device *dev = fb_helper->dev;
404 struct drm_plane *plane;
405 int i;
406
407 drm_warn_on_modeset_not_all_locked(dev);
408
409 if (fb_helper->atomic)
410 return restore_fbdev_mode_atomic(fb_helper);
411
412 drm_for_each_plane(plane, dev) {
413 if (plane->type != DRM_PLANE_TYPE_PRIMARY)
414 drm_plane_force_disable(plane);
415
416 if (dev->mode_config.rotation_property) {
417 drm_mode_plane_set_obj_prop(plane,
418 dev->mode_config.rotation_property,
419 BIT(DRM_ROTATE_0));
420 }
421 }
422
423 for (i = 0; i < fb_helper->crtc_count; i++) {
424 struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set;
425 struct drm_crtc *crtc = mode_set->crtc;
426 int ret;
427
428 if (crtc->funcs->cursor_set2) {
429 ret = crtc->funcs->cursor_set2(crtc, NULL, 0, 0, 0, 0, 0);
430 if (ret)
431 return ret;
432 } else if (crtc->funcs->cursor_set) {
433 ret = crtc->funcs->cursor_set(crtc, NULL, 0, 0, 0);
434 if (ret)
435 return ret;
436 }
437
438 ret = drm_mode_set_config_internal(mode_set);
439 if (ret)
440 return ret;
441 }
442
443 return 0;
444}
445
446
447
448
449
450
451
452
453
454
455
456
457int drm_fb_helper_restore_fbdev_mode_unlocked(struct drm_fb_helper *fb_helper)
458{
459 struct drm_device *dev = fb_helper->dev;
460 bool do_delayed;
461 int ret;
462
463 if (!drm_fbdev_emulation)
464 return -ENODEV;
465
466 drm_modeset_lock_all(dev);
467 ret = restore_fbdev_mode(fb_helper);
468
469 do_delayed = fb_helper->delayed_hotplug;
470 if (do_delayed)
471 fb_helper->delayed_hotplug = false;
472 drm_modeset_unlock_all(dev);
473
474 if (do_delayed)
475 drm_fb_helper_hotplug_event(fb_helper);
476 return ret;
477}
478EXPORT_SYMBOL(drm_fb_helper_restore_fbdev_mode_unlocked);
479
480static bool drm_fb_helper_is_bound(struct drm_fb_helper *fb_helper)
481{
482 struct drm_device *dev = fb_helper->dev;
483 struct drm_crtc *crtc;
484 int bound = 0, crtcs_bound = 0;
485
486
487
488 if (dev->primary->master)
489 return false;
490
491 drm_for_each_crtc(crtc, dev) {
492 if (crtc->primary->fb)
493 crtcs_bound++;
494 if (crtc->primary->fb == fb_helper->fb)
495 bound++;
496 }
497
498 if (bound < crtcs_bound)
499 return false;
500
501 return true;
502}
503
504#ifdef CONFIG_MAGIC_SYSRQ
505
506
507
508
509static bool drm_fb_helper_force_kernel_mode(void)
510{
511 bool ret, error = false;
512 struct drm_fb_helper *helper;
513
514 if (list_empty(&kernel_fb_helper_list))
515 return false;
516
517 list_for_each_entry(helper, &kernel_fb_helper_list, kernel_fb_list) {
518 struct drm_device *dev = helper->dev;
519
520 if (dev->switch_power_state == DRM_SWITCH_POWER_OFF)
521 continue;
522
523 drm_modeset_lock_all(dev);
524 ret = restore_fbdev_mode(helper);
525 if (ret)
526 error = true;
527 drm_modeset_unlock_all(dev);
528 }
529 return error;
530}
531
532static void drm_fb_helper_restore_work_fn(struct work_struct *ignored)
533{
534 bool ret;
535 ret = drm_fb_helper_force_kernel_mode();
536 if (ret == true)
537 DRM_ERROR("Failed to restore crtc configuration\n");
538}
539static DECLARE_WORK(drm_fb_helper_restore_work, drm_fb_helper_restore_work_fn);
540
541static void drm_fb_helper_sysrq(int dummy1)
542{
543 schedule_work(&drm_fb_helper_restore_work);
544}
545
546static struct sysrq_key_op sysrq_drm_fb_helper_restore_op = {
547 .handler = drm_fb_helper_sysrq,
548 .help_msg = "force-fb(V)",
549 .action_msg = "Restore framebuffer console",
550};
551#else
552static struct sysrq_key_op sysrq_drm_fb_helper_restore_op = { };
553#endif
554
555static void drm_fb_helper_dpms(struct fb_info *info, int dpms_mode)
556{
557 struct drm_fb_helper *fb_helper = info->par;
558 struct drm_device *dev = fb_helper->dev;
559 struct drm_crtc *crtc;
560 struct drm_connector *connector;
561 int i, j;
562
563
564
565
566 drm_modeset_lock_all(dev);
567 if (!drm_fb_helper_is_bound(fb_helper)) {
568 drm_modeset_unlock_all(dev);
569 return;
570 }
571
572 for (i = 0; i < fb_helper->crtc_count; i++) {
573 crtc = fb_helper->crtc_info[i].mode_set.crtc;
574
575 if (!crtc->enabled)
576 continue;
577
578
579 for (j = 0; j < fb_helper->connector_count; j++) {
580 connector = fb_helper->connector_info[j]->connector;
581 connector->funcs->dpms(connector, dpms_mode);
582 drm_object_property_set_value(&connector->base,
583 dev->mode_config.dpms_property, dpms_mode);
584 }
585 }
586 drm_modeset_unlock_all(dev);
587}
588
589
590
591
592
593
594int drm_fb_helper_blank(int blank, struct fb_info *info)
595{
596 if (oops_in_progress)
597 return -EBUSY;
598
599 switch (blank) {
600
601 case FB_BLANK_UNBLANK:
602 drm_fb_helper_dpms(info, DRM_MODE_DPMS_ON);
603 break;
604
605 case FB_BLANK_NORMAL:
606 drm_fb_helper_dpms(info, DRM_MODE_DPMS_STANDBY);
607 break;
608
609 case FB_BLANK_HSYNC_SUSPEND:
610 drm_fb_helper_dpms(info, DRM_MODE_DPMS_STANDBY);
611 break;
612
613 case FB_BLANK_VSYNC_SUSPEND:
614 drm_fb_helper_dpms(info, DRM_MODE_DPMS_SUSPEND);
615 break;
616
617 case FB_BLANK_POWERDOWN:
618 drm_fb_helper_dpms(info, DRM_MODE_DPMS_OFF);
619 break;
620 }
621 return 0;
622}
623EXPORT_SYMBOL(drm_fb_helper_blank);
624
625static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper)
626{
627 int i;
628
629 for (i = 0; i < helper->connector_count; i++)
630 kfree(helper->connector_info[i]);
631 kfree(helper->connector_info);
632 for (i = 0; i < helper->crtc_count; i++) {
633 kfree(helper->crtc_info[i].mode_set.connectors);
634 if (helper->crtc_info[i].mode_set.mode)
635 drm_mode_destroy(helper->dev, helper->crtc_info[i].mode_set.mode);
636 }
637 kfree(helper->crtc_info);
638}
639
640
641
642
643
644
645
646
647
648
649void drm_fb_helper_prepare(struct drm_device *dev, struct drm_fb_helper *helper,
650 const struct drm_fb_helper_funcs *funcs)
651{
652 INIT_LIST_HEAD(&helper->kernel_fb_list);
653 helper->funcs = funcs;
654 helper->dev = dev;
655}
656EXPORT_SYMBOL(drm_fb_helper_prepare);
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675int drm_fb_helper_init(struct drm_device *dev,
676 struct drm_fb_helper *fb_helper,
677 int crtc_count, int max_conn_count)
678{
679 struct drm_crtc *crtc;
680 int i;
681
682 if (!drm_fbdev_emulation)
683 return 0;
684
685 if (!max_conn_count)
686 return -EINVAL;
687
688 fb_helper->crtc_info = kcalloc(crtc_count, sizeof(struct drm_fb_helper_crtc), GFP_KERNEL);
689 if (!fb_helper->crtc_info)
690 return -ENOMEM;
691
692 fb_helper->crtc_count = crtc_count;
693 fb_helper->connector_info = kcalloc(dev->mode_config.num_connector, sizeof(struct drm_fb_helper_connector *), GFP_KERNEL);
694 if (!fb_helper->connector_info) {
695 kfree(fb_helper->crtc_info);
696 return -ENOMEM;
697 }
698 fb_helper->connector_info_alloc_count = dev->mode_config.num_connector;
699 fb_helper->connector_count = 0;
700
701 for (i = 0; i < crtc_count; i++) {
702 fb_helper->crtc_info[i].mode_set.connectors =
703 kcalloc(max_conn_count,
704 sizeof(struct drm_connector *),
705 GFP_KERNEL);
706
707 if (!fb_helper->crtc_info[i].mode_set.connectors)
708 goto out_free;
709 fb_helper->crtc_info[i].mode_set.num_connectors = 0;
710 }
711
712 i = 0;
713 drm_for_each_crtc(crtc, dev) {
714 fb_helper->crtc_info[i].mode_set.crtc = crtc;
715 i++;
716 }
717
718 fb_helper->atomic = !!drm_core_check_feature(dev, DRIVER_ATOMIC);
719
720 return 0;
721out_free:
722 drm_fb_helper_crtc_free(fb_helper);
723 return -ENOMEM;
724}
725EXPORT_SYMBOL(drm_fb_helper_init);
726
727
728
729
730
731
732
733
734
735
736
737
738struct fb_info *drm_fb_helper_alloc_fbi(struct drm_fb_helper *fb_helper)
739{
740 struct device *dev = fb_helper->dev->dev;
741 struct fb_info *info;
742 int ret;
743
744 info = framebuffer_alloc(0, dev);
745 if (!info)
746 return ERR_PTR(-ENOMEM);
747
748 ret = fb_alloc_cmap(&info->cmap, 256, 0);
749 if (ret)
750 goto err_release;
751
752 info->apertures = alloc_apertures(1);
753 if (!info->apertures) {
754 ret = -ENOMEM;
755 goto err_free_cmap;
756 }
757
758 fb_helper->fbdev = info;
759
760 return info;
761
762err_free_cmap:
763 fb_dealloc_cmap(&info->cmap);
764err_release:
765 framebuffer_release(info);
766 return ERR_PTR(ret);
767}
768EXPORT_SYMBOL(drm_fb_helper_alloc_fbi);
769
770
771
772
773
774
775
776
777void drm_fb_helper_unregister_fbi(struct drm_fb_helper *fb_helper)
778{
779 if (fb_helper && fb_helper->fbdev)
780 unregister_framebuffer(fb_helper->fbdev);
781}
782EXPORT_SYMBOL(drm_fb_helper_unregister_fbi);
783
784
785
786
787
788
789
790
791void drm_fb_helper_release_fbi(struct drm_fb_helper *fb_helper)
792{
793 if (fb_helper) {
794 struct fb_info *info = fb_helper->fbdev;
795
796 if (info) {
797 if (info->cmap.len)
798 fb_dealloc_cmap(&info->cmap);
799 framebuffer_release(info);
800 }
801
802 fb_helper->fbdev = NULL;
803 }
804}
805EXPORT_SYMBOL(drm_fb_helper_release_fbi);
806
807void drm_fb_helper_fini(struct drm_fb_helper *fb_helper)
808{
809 if (!drm_fbdev_emulation)
810 return;
811
812 if (!list_empty(&fb_helper->kernel_fb_list)) {
813 list_del(&fb_helper->kernel_fb_list);
814 if (list_empty(&kernel_fb_helper_list)) {
815 unregister_sysrq_key('v', &sysrq_drm_fb_helper_restore_op);
816 }
817 }
818
819 drm_fb_helper_crtc_free(fb_helper);
820
821}
822EXPORT_SYMBOL(drm_fb_helper_fini);
823
824
825
826
827
828
829
830void drm_fb_helper_unlink_fbi(struct drm_fb_helper *fb_helper)
831{
832 if (fb_helper && fb_helper->fbdev)
833 unlink_framebuffer(fb_helper->fbdev);
834}
835EXPORT_SYMBOL(drm_fb_helper_unlink_fbi);
836
837
838
839
840
841
842
843
844
845
846ssize_t drm_fb_helper_sys_read(struct fb_info *info, char __user *buf,
847 size_t count, loff_t *ppos)
848{
849 return fb_sys_read(info, buf, count, ppos);
850}
851EXPORT_SYMBOL(drm_fb_helper_sys_read);
852
853
854
855
856
857
858
859
860
861
862ssize_t drm_fb_helper_sys_write(struct fb_info *info, const char __user *buf,
863 size_t count, loff_t *ppos)
864{
865 return fb_sys_write(info, buf, count, ppos);
866}
867EXPORT_SYMBOL(drm_fb_helper_sys_write);
868
869
870
871
872
873
874
875
876void drm_fb_helper_sys_fillrect(struct fb_info *info,
877 const struct fb_fillrect *rect)
878{
879 sys_fillrect(info, rect);
880}
881EXPORT_SYMBOL(drm_fb_helper_sys_fillrect);
882
883
884
885
886
887
888
889
890void drm_fb_helper_sys_copyarea(struct fb_info *info,
891 const struct fb_copyarea *area)
892{
893 sys_copyarea(info, area);
894}
895EXPORT_SYMBOL(drm_fb_helper_sys_copyarea);
896
897
898
899
900
901
902
903
904void drm_fb_helper_sys_imageblit(struct fb_info *info,
905 const struct fb_image *image)
906{
907 sys_imageblit(info, image);
908}
909EXPORT_SYMBOL(drm_fb_helper_sys_imageblit);
910
911
912
913
914
915
916
917
918void drm_fb_helper_cfb_fillrect(struct fb_info *info,
919 const struct fb_fillrect *rect)
920{
921 cfb_fillrect(info, rect);
922}
923EXPORT_SYMBOL(drm_fb_helper_cfb_fillrect);
924
925
926
927
928
929
930
931
932void drm_fb_helper_cfb_copyarea(struct fb_info *info,
933 const struct fb_copyarea *area)
934{
935 cfb_copyarea(info, area);
936}
937EXPORT_SYMBOL(drm_fb_helper_cfb_copyarea);
938
939
940
941
942
943
944
945
946void drm_fb_helper_cfb_imageblit(struct fb_info *info,
947 const struct fb_image *image)
948{
949 cfb_imageblit(info, image);
950}
951EXPORT_SYMBOL(drm_fb_helper_cfb_imageblit);
952
953
954
955
956
957
958
959
960void drm_fb_helper_set_suspend(struct drm_fb_helper *fb_helper, int state)
961{
962 if (fb_helper && fb_helper->fbdev)
963 fb_set_suspend(fb_helper->fbdev, state);
964}
965EXPORT_SYMBOL(drm_fb_helper_set_suspend);
966
967static int setcolreg(struct drm_crtc *crtc, u16 red, u16 green,
968 u16 blue, u16 regno, struct fb_info *info)
969{
970 struct drm_fb_helper *fb_helper = info->par;
971 struct drm_framebuffer *fb = fb_helper->fb;
972 int pindex;
973
974 if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
975 u32 *palette;
976 u32 value;
977
978 if (regno > 16)
979 return -EINVAL;
980 palette = (u32 *)info->pseudo_palette;
981 red >>= (16 - info->var.red.length);
982 green >>= (16 - info->var.green.length);
983 blue >>= (16 - info->var.blue.length);
984 value = (red << info->var.red.offset) |
985 (green << info->var.green.offset) |
986 (blue << info->var.blue.offset);
987 if (info->var.transp.length > 0) {
988 u32 mask = (1 << info->var.transp.length) - 1;
989 mask <<= info->var.transp.offset;
990 value |= mask;
991 }
992 palette[regno] = value;
993 return 0;
994 }
995
996
997
998
999
1000 if (WARN_ON(!fb_helper->funcs->gamma_set ||
1001 !fb_helper->funcs->gamma_get))
1002 return -EINVAL;
1003
1004 pindex = regno;
1005
1006 if (fb->bits_per_pixel == 16) {
1007 pindex = regno << 3;
1008
1009 if (fb->depth == 16 && regno > 63)
1010 return -EINVAL;
1011 if (fb->depth == 15 && regno > 31)
1012 return -EINVAL;
1013
1014 if (fb->depth == 16) {
1015 u16 r, g, b;
1016 int i;
1017 if (regno < 32) {
1018 for (i = 0; i < 8; i++)
1019 fb_helper->funcs->gamma_set(crtc, red,
1020 green, blue, pindex + i);
1021 }
1022
1023 fb_helper->funcs->gamma_get(crtc, &r,
1024 &g, &b,
1025 pindex >> 1);
1026
1027 for (i = 0; i < 4; i++)
1028 fb_helper->funcs->gamma_set(crtc, r,
1029 green, b,
1030 (pindex >> 1) + i);
1031 }
1032 }
1033
1034 if (fb->depth != 16)
1035 fb_helper->funcs->gamma_set(crtc, red, green, blue, pindex);
1036 return 0;
1037}
1038
1039
1040
1041
1042
1043
1044int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info)
1045{
1046 struct drm_fb_helper *fb_helper = info->par;
1047 struct drm_device *dev = fb_helper->dev;
1048 const struct drm_crtc_helper_funcs *crtc_funcs;
1049 u16 *red, *green, *blue, *transp;
1050 struct drm_crtc *crtc;
1051 int i, j, rc = 0;
1052 int start;
1053
1054 if (oops_in_progress)
1055 return -EBUSY;
1056
1057 drm_modeset_lock_all(dev);
1058 if (!drm_fb_helper_is_bound(fb_helper)) {
1059 drm_modeset_unlock_all(dev);
1060 return -EBUSY;
1061 }
1062
1063 for (i = 0; i < fb_helper->crtc_count; i++) {
1064 crtc = fb_helper->crtc_info[i].mode_set.crtc;
1065 crtc_funcs = crtc->helper_private;
1066
1067 red = cmap->red;
1068 green = cmap->green;
1069 blue = cmap->blue;
1070 transp = cmap->transp;
1071 start = cmap->start;
1072
1073 for (j = 0; j < cmap->len; j++) {
1074 u16 hred, hgreen, hblue, htransp = 0xffff;
1075
1076 hred = *red++;
1077 hgreen = *green++;
1078 hblue = *blue++;
1079
1080 if (transp)
1081 htransp = *transp++;
1082
1083 rc = setcolreg(crtc, hred, hgreen, hblue, start++, info);
1084 if (rc)
1085 goto out;
1086 }
1087 if (crtc_funcs->load_lut)
1088 crtc_funcs->load_lut(crtc);
1089 }
1090 out:
1091 drm_modeset_unlock_all(dev);
1092 return rc;
1093}
1094EXPORT_SYMBOL(drm_fb_helper_setcmap);
1095
1096
1097
1098
1099
1100
1101int drm_fb_helper_check_var(struct fb_var_screeninfo *var,
1102 struct fb_info *info)
1103{
1104 struct drm_fb_helper *fb_helper = info->par;
1105 struct drm_framebuffer *fb = fb_helper->fb;
1106 int depth;
1107
1108 if (var->pixclock != 0 || in_dbg_master())
1109 return -EINVAL;
1110
1111
1112 if (var->bits_per_pixel > fb->bits_per_pixel ||
1113 var->xres > fb->width || var->yres > fb->height ||
1114 var->xres_virtual > fb->width || var->yres_virtual > fb->height) {
1115 DRM_DEBUG("fb userspace requested width/height/bpp is greater than current fb "
1116 "request %dx%d-%d (virtual %dx%d) > %dx%d-%d\n",
1117 var->xres, var->yres, var->bits_per_pixel,
1118 var->xres_virtual, var->yres_virtual,
1119 fb->width, fb->height, fb->bits_per_pixel);
1120 return -EINVAL;
1121 }
1122
1123 switch (var->bits_per_pixel) {
1124 case 16:
1125 depth = (var->green.length == 6) ? 16 : 15;
1126 break;
1127 case 32:
1128 depth = (var->transp.length > 0) ? 32 : 24;
1129 break;
1130 default:
1131 depth = var->bits_per_pixel;
1132 break;
1133 }
1134
1135 switch (depth) {
1136 case 8:
1137 var->red.offset = 0;
1138 var->green.offset = 0;
1139 var->blue.offset = 0;
1140 var->red.length = 8;
1141 var->green.length = 8;
1142 var->blue.length = 8;
1143 var->transp.length = 0;
1144 var->transp.offset = 0;
1145 break;
1146 case 15:
1147 var->red.offset = 10;
1148 var->green.offset = 5;
1149 var->blue.offset = 0;
1150 var->red.length = 5;
1151 var->green.length = 5;
1152 var->blue.length = 5;
1153 var->transp.length = 1;
1154 var->transp.offset = 15;
1155 break;
1156 case 16:
1157 var->red.offset = 11;
1158 var->green.offset = 5;
1159 var->blue.offset = 0;
1160 var->red.length = 5;
1161 var->green.length = 6;
1162 var->blue.length = 5;
1163 var->transp.length = 0;
1164 var->transp.offset = 0;
1165 break;
1166 case 24:
1167 var->red.offset = 16;
1168 var->green.offset = 8;
1169 var->blue.offset = 0;
1170 var->red.length = 8;
1171 var->green.length = 8;
1172 var->blue.length = 8;
1173 var->transp.length = 0;
1174 var->transp.offset = 0;
1175 break;
1176 case 32:
1177 var->red.offset = 16;
1178 var->green.offset = 8;
1179 var->blue.offset = 0;
1180 var->red.length = 8;
1181 var->green.length = 8;
1182 var->blue.length = 8;
1183 var->transp.length = 8;
1184 var->transp.offset = 24;
1185 break;
1186 default:
1187 return -EINVAL;
1188 }
1189 return 0;
1190}
1191EXPORT_SYMBOL(drm_fb_helper_check_var);
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201int drm_fb_helper_set_par(struct fb_info *info)
1202{
1203 struct drm_fb_helper *fb_helper = info->par;
1204 struct fb_var_screeninfo *var = &info->var;
1205
1206 if (oops_in_progress)
1207 return -EBUSY;
1208
1209 if (var->pixclock != 0) {
1210 DRM_ERROR("PIXEL CLOCK SET\n");
1211 return -EINVAL;
1212 }
1213
1214 drm_fb_helper_restore_fbdev_mode_unlocked(fb_helper);
1215
1216 return 0;
1217}
1218EXPORT_SYMBOL(drm_fb_helper_set_par);
1219
1220static int pan_display_atomic(struct fb_var_screeninfo *var,
1221 struct fb_info *info)
1222{
1223 struct drm_fb_helper *fb_helper = info->par;
1224 struct drm_device *dev = fb_helper->dev;
1225 struct drm_atomic_state *state;
1226 struct drm_plane *plane;
1227 int i, ret;
1228 unsigned plane_mask;
1229
1230 state = drm_atomic_state_alloc(dev);
1231 if (!state)
1232 return -ENOMEM;
1233
1234 state->acquire_ctx = dev->mode_config.acquire_ctx;
1235retry:
1236 plane_mask = 0;
1237 for(i = 0; i < fb_helper->crtc_count; i++) {
1238 struct drm_mode_set *mode_set;
1239
1240 mode_set = &fb_helper->crtc_info[i].mode_set;
1241
1242 mode_set->x = var->xoffset;
1243 mode_set->y = var->yoffset;
1244
1245 ret = __drm_atomic_helper_set_config(mode_set, state);
1246 if (ret != 0)
1247 goto fail;
1248
1249 plane = mode_set->crtc->primary;
1250 plane_mask |= (1 << drm_plane_index(plane));
1251 plane->old_fb = plane->fb;
1252 }
1253
1254 ret = drm_atomic_commit(state);
1255 if (ret != 0)
1256 goto fail;
1257
1258 info->var.xoffset = var->xoffset;
1259 info->var.yoffset = var->yoffset;
1260
1261
1262fail:
1263 drm_atomic_clean_old_fb(dev, plane_mask, ret);
1264
1265 if (ret == -EDEADLK)
1266 goto backoff;
1267
1268 if (ret != 0)
1269 drm_atomic_state_free(state);
1270
1271 return ret;
1272
1273backoff:
1274 drm_atomic_state_clear(state);
1275 drm_atomic_legacy_backoff(state);
1276
1277 goto retry;
1278}
1279
1280
1281
1282
1283
1284
1285int drm_fb_helper_pan_display(struct fb_var_screeninfo *var,
1286 struct fb_info *info)
1287{
1288 struct drm_fb_helper *fb_helper = info->par;
1289 struct drm_device *dev = fb_helper->dev;
1290 struct drm_mode_set *modeset;
1291 int ret = 0;
1292 int i;
1293
1294 if (oops_in_progress)
1295 return -EBUSY;
1296
1297 drm_modeset_lock_all(dev);
1298 if (!drm_fb_helper_is_bound(fb_helper)) {
1299 drm_modeset_unlock_all(dev);
1300 return -EBUSY;
1301 }
1302
1303 if (fb_helper->atomic) {
1304 ret = pan_display_atomic(var, info);
1305 goto unlock;
1306 }
1307
1308 for (i = 0; i < fb_helper->crtc_count; i++) {
1309 modeset = &fb_helper->crtc_info[i].mode_set;
1310
1311 modeset->x = var->xoffset;
1312 modeset->y = var->yoffset;
1313
1314 if (modeset->num_connectors) {
1315 ret = drm_mode_set_config_internal(modeset);
1316 if (!ret) {
1317 info->var.xoffset = var->xoffset;
1318 info->var.yoffset = var->yoffset;
1319 }
1320 }
1321 }
1322unlock:
1323 drm_modeset_unlock_all(dev);
1324 return ret;
1325}
1326EXPORT_SYMBOL(drm_fb_helper_pan_display);
1327
1328
1329
1330
1331
1332
1333static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper,
1334 int preferred_bpp)
1335{
1336 int ret = 0;
1337 int crtc_count = 0;
1338 int i;
1339 struct fb_info *info;
1340 struct drm_fb_helper_surface_size sizes;
1341 int gamma_size = 0;
1342
1343 memset(&sizes, 0, sizeof(struct drm_fb_helper_surface_size));
1344 sizes.surface_depth = 24;
1345 sizes.surface_bpp = 32;
1346 sizes.fb_width = (unsigned)-1;
1347 sizes.fb_height = (unsigned)-1;
1348
1349
1350
1351 if (preferred_bpp != sizes.surface_bpp)
1352 sizes.surface_depth = sizes.surface_bpp = preferred_bpp;
1353
1354
1355 for (i = 0; i < fb_helper->connector_count; i++) {
1356 struct drm_fb_helper_connector *fb_helper_conn = fb_helper->connector_info[i];
1357 struct drm_cmdline_mode *cmdline_mode;
1358
1359 cmdline_mode = &fb_helper_conn->connector->cmdline_mode;
1360
1361 if (cmdline_mode->bpp_specified) {
1362 switch (cmdline_mode->bpp) {
1363 case 8:
1364 sizes.surface_depth = sizes.surface_bpp = 8;
1365 break;
1366 case 15:
1367 sizes.surface_depth = 15;
1368 sizes.surface_bpp = 16;
1369 break;
1370 case 16:
1371 sizes.surface_depth = sizes.surface_bpp = 16;
1372 break;
1373 case 24:
1374 sizes.surface_depth = sizes.surface_bpp = 24;
1375 break;
1376 case 32:
1377 sizes.surface_depth = 24;
1378 sizes.surface_bpp = 32;
1379 break;
1380 }
1381 break;
1382 }
1383 }
1384
1385 crtc_count = 0;
1386 for (i = 0; i < fb_helper->crtc_count; i++) {
1387 struct drm_display_mode *desired_mode;
1388 struct drm_mode_set *mode_set;
1389 int x, y, j;
1390
1391
1392
1393
1394 bool lastv = true, lasth = true;
1395
1396 desired_mode = fb_helper->crtc_info[i].desired_mode;
1397 mode_set = &fb_helper->crtc_info[i].mode_set;
1398
1399 if (!desired_mode)
1400 continue;
1401
1402 crtc_count++;
1403
1404 x = fb_helper->crtc_info[i].x;
1405 y = fb_helper->crtc_info[i].y;
1406
1407 if (gamma_size == 0)
1408 gamma_size = fb_helper->crtc_info[i].mode_set.crtc->gamma_size;
1409
1410 sizes.surface_width = max_t(u32, desired_mode->hdisplay + x, sizes.surface_width);
1411 sizes.surface_height = max_t(u32, desired_mode->vdisplay + y, sizes.surface_height);
1412
1413 for (j = 0; j < mode_set->num_connectors; j++) {
1414 struct drm_connector *connector = mode_set->connectors[j];
1415 if (connector->has_tile) {
1416 lasth = (connector->tile_h_loc == (connector->num_h_tile - 1));
1417 lastv = (connector->tile_v_loc == (connector->num_v_tile - 1));
1418
1419 break;
1420 }
1421 }
1422
1423 if (lasth)
1424 sizes.fb_width = min_t(u32, desired_mode->hdisplay + x, sizes.fb_width);
1425 if (lastv)
1426 sizes.fb_height = min_t(u32, desired_mode->vdisplay + y, sizes.fb_height);
1427 }
1428
1429 if (crtc_count == 0 || sizes.fb_width == -1 || sizes.fb_height == -1) {
1430
1431
1432 DRM_INFO("Cannot find any crtc or sizes - going 1024x768\n");
1433 sizes.fb_width = sizes.surface_width = 1024;
1434 sizes.fb_height = sizes.surface_height = 768;
1435 }
1436
1437
1438 ret = (*fb_helper->funcs->fb_probe)(fb_helper, &sizes);
1439 if (ret < 0)
1440 return ret;
1441
1442 info = fb_helper->fbdev;
1443
1444
1445
1446
1447
1448
1449
1450
1451 for (i = 0; i < fb_helper->crtc_count; i++)
1452 if (fb_helper->crtc_info[i].mode_set.num_connectors)
1453 fb_helper->crtc_info[i].mode_set.fb = fb_helper->fb;
1454
1455
1456 info->var.pixclock = 0;
1457 if (register_framebuffer(info) < 0)
1458 return -EINVAL;
1459
1460 dev_info(fb_helper->dev->dev, "fb%d: %s frame buffer device\n",
1461 info->node, info->fix.id);
1462
1463 if (list_empty(&kernel_fb_helper_list)) {
1464 register_sysrq_key('v', &sysrq_drm_fb_helper_restore_op);
1465 }
1466
1467 list_add(&fb_helper->kernel_fb_list, &kernel_fb_helper_list);
1468
1469 return 0;
1470}
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch,
1486 uint32_t depth)
1487{
1488 info->fix.type = FB_TYPE_PACKED_PIXELS;
1489 info->fix.visual = depth == 8 ? FB_VISUAL_PSEUDOCOLOR :
1490 FB_VISUAL_TRUECOLOR;
1491 info->fix.mmio_start = 0;
1492 info->fix.mmio_len = 0;
1493 info->fix.type_aux = 0;
1494 info->fix.xpanstep = 1;
1495 info->fix.ypanstep = 1;
1496 info->fix.ywrapstep = 0;
1497 info->fix.accel = FB_ACCEL_NONE;
1498
1499 info->fix.line_length = pitch;
1500 return;
1501}
1502EXPORT_SYMBOL(drm_fb_helper_fill_fix);
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518void drm_fb_helper_fill_var(struct fb_info *info, struct drm_fb_helper *fb_helper,
1519 uint32_t fb_width, uint32_t fb_height)
1520{
1521 struct drm_framebuffer *fb = fb_helper->fb;
1522 info->pseudo_palette = fb_helper->pseudo_palette;
1523 info->var.xres_virtual = fb->width;
1524 info->var.yres_virtual = fb->height;
1525 info->var.bits_per_pixel = fb->bits_per_pixel;
1526 info->var.accel_flags = FB_ACCELF_TEXT;
1527 info->var.xoffset = 0;
1528 info->var.yoffset = 0;
1529 info->var.activate = FB_ACTIVATE_NOW;
1530 info->var.height = -1;
1531 info->var.width = -1;
1532
1533 switch (fb->depth) {
1534 case 8:
1535 info->var.red.offset = 0;
1536 info->var.green.offset = 0;
1537 info->var.blue.offset = 0;
1538 info->var.red.length = 8;
1539 info->var.green.length = 8;
1540 info->var.blue.length = 8;
1541 info->var.transp.offset = 0;
1542 info->var.transp.length = 0;
1543 break;
1544 case 15:
1545 info->var.red.offset = 10;
1546 info->var.green.offset = 5;
1547 info->var.blue.offset = 0;
1548 info->var.red.length = 5;
1549 info->var.green.length = 5;
1550 info->var.blue.length = 5;
1551 info->var.transp.offset = 15;
1552 info->var.transp.length = 1;
1553 break;
1554 case 16:
1555 info->var.red.offset = 11;
1556 info->var.green.offset = 5;
1557 info->var.blue.offset = 0;
1558 info->var.red.length = 5;
1559 info->var.green.length = 6;
1560 info->var.blue.length = 5;
1561 info->var.transp.offset = 0;
1562 break;
1563 case 24:
1564 info->var.red.offset = 16;
1565 info->var.green.offset = 8;
1566 info->var.blue.offset = 0;
1567 info->var.red.length = 8;
1568 info->var.green.length = 8;
1569 info->var.blue.length = 8;
1570 info->var.transp.offset = 0;
1571 info->var.transp.length = 0;
1572 break;
1573 case 32:
1574 info->var.red.offset = 16;
1575 info->var.green.offset = 8;
1576 info->var.blue.offset = 0;
1577 info->var.red.length = 8;
1578 info->var.green.length = 8;
1579 info->var.blue.length = 8;
1580 info->var.transp.offset = 24;
1581 info->var.transp.length = 8;
1582 break;
1583 default:
1584 break;
1585 }
1586
1587 info->var.xres = fb_width;
1588 info->var.yres = fb_height;
1589}
1590EXPORT_SYMBOL(drm_fb_helper_fill_var);
1591
1592static int drm_fb_helper_probe_connector_modes(struct drm_fb_helper *fb_helper,
1593 uint32_t maxX,
1594 uint32_t maxY)
1595{
1596 struct drm_connector *connector;
1597 int count = 0;
1598 int i;
1599
1600 for (i = 0; i < fb_helper->connector_count; i++) {
1601 connector = fb_helper->connector_info[i]->connector;
1602 count += connector->funcs->fill_modes(connector, maxX, maxY);
1603 }
1604
1605 return count;
1606}
1607
1608struct drm_display_mode *drm_has_preferred_mode(struct drm_fb_helper_connector *fb_connector, int width, int height)
1609{
1610 struct drm_display_mode *mode;
1611
1612 list_for_each_entry(mode, &fb_connector->connector->modes, head) {
1613 if (mode->hdisplay > width ||
1614 mode->vdisplay > height)
1615 continue;
1616 if (mode->type & DRM_MODE_TYPE_PREFERRED)
1617 return mode;
1618 }
1619 return NULL;
1620}
1621EXPORT_SYMBOL(drm_has_preferred_mode);
1622
1623static bool drm_has_cmdline_mode(struct drm_fb_helper_connector *fb_connector)
1624{
1625 return fb_connector->connector->cmdline_mode.specified;
1626}
1627
1628struct drm_display_mode *drm_pick_cmdline_mode(struct drm_fb_helper_connector *fb_helper_conn,
1629 int width, int height)
1630{
1631 struct drm_cmdline_mode *cmdline_mode;
1632 struct drm_display_mode *mode;
1633 bool prefer_non_interlace;
1634
1635 cmdline_mode = &fb_helper_conn->connector->cmdline_mode;
1636 if (cmdline_mode->specified == false)
1637 return NULL;
1638
1639
1640
1641
1642 if (cmdline_mode->rb || cmdline_mode->margins)
1643 goto create_mode;
1644
1645 prefer_non_interlace = !cmdline_mode->interlace;
1646again:
1647 list_for_each_entry(mode, &fb_helper_conn->connector->modes, head) {
1648
1649 if (mode->hdisplay != cmdline_mode->xres ||
1650 mode->vdisplay != cmdline_mode->yres)
1651 continue;
1652
1653 if (cmdline_mode->refresh_specified) {
1654 if (mode->vrefresh != cmdline_mode->refresh)
1655 continue;
1656 }
1657
1658 if (cmdline_mode->interlace) {
1659 if (!(mode->flags & DRM_MODE_FLAG_INTERLACE))
1660 continue;
1661 } else if (prefer_non_interlace) {
1662 if (mode->flags & DRM_MODE_FLAG_INTERLACE)
1663 continue;
1664 }
1665 return mode;
1666 }
1667
1668 if (prefer_non_interlace) {
1669 prefer_non_interlace = false;
1670 goto again;
1671 }
1672
1673create_mode:
1674 mode = drm_mode_create_from_cmdline_mode(fb_helper_conn->connector->dev,
1675 cmdline_mode);
1676 list_add(&mode->head, &fb_helper_conn->connector->modes);
1677 return mode;
1678}
1679EXPORT_SYMBOL(drm_pick_cmdline_mode);
1680
1681static bool drm_connector_enabled(struct drm_connector *connector, bool strict)
1682{
1683 bool enable;
1684
1685 if (strict)
1686 enable = connector->status == connector_status_connected;
1687 else
1688 enable = connector->status != connector_status_disconnected;
1689
1690 return enable;
1691}
1692
1693static void drm_enable_connectors(struct drm_fb_helper *fb_helper,
1694 bool *enabled)
1695{
1696 bool any_enabled = false;
1697 struct drm_connector *connector;
1698 int i = 0;
1699
1700 for (i = 0; i < fb_helper->connector_count; i++) {
1701 connector = fb_helper->connector_info[i]->connector;
1702 enabled[i] = drm_connector_enabled(connector, true);
1703 DRM_DEBUG_KMS("connector %d enabled? %s\n", connector->base.id,
1704 enabled[i] ? "yes" : "no");
1705 any_enabled |= enabled[i];
1706 }
1707
1708 if (any_enabled)
1709 return;
1710
1711 for (i = 0; i < fb_helper->connector_count; i++) {
1712 connector = fb_helper->connector_info[i]->connector;
1713 enabled[i] = drm_connector_enabled(connector, false);
1714 }
1715}
1716
1717static bool drm_target_cloned(struct drm_fb_helper *fb_helper,
1718 struct drm_display_mode **modes,
1719 struct drm_fb_offset *offsets,
1720 bool *enabled, int width, int height)
1721{
1722 int count, i, j;
1723 bool can_clone = false;
1724 struct drm_fb_helper_connector *fb_helper_conn;
1725 struct drm_display_mode *dmt_mode, *mode;
1726
1727
1728 if (fb_helper->crtc_count > 1)
1729 return false;
1730
1731 count = 0;
1732 for (i = 0; i < fb_helper->connector_count; i++) {
1733 if (enabled[i])
1734 count++;
1735 }
1736
1737
1738 if (count <= 1)
1739 return false;
1740
1741
1742 can_clone = true;
1743 for (i = 0; i < fb_helper->connector_count; i++) {
1744 if (!enabled[i])
1745 continue;
1746 fb_helper_conn = fb_helper->connector_info[i];
1747 modes[i] = drm_pick_cmdline_mode(fb_helper_conn, width, height);
1748 if (!modes[i]) {
1749 can_clone = false;
1750 break;
1751 }
1752 for (j = 0; j < i; j++) {
1753 if (!enabled[j])
1754 continue;
1755 if (!drm_mode_equal(modes[j], modes[i]))
1756 can_clone = false;
1757 }
1758 }
1759
1760 if (can_clone) {
1761 DRM_DEBUG_KMS("can clone using command line\n");
1762 return true;
1763 }
1764
1765
1766 can_clone = true;
1767 dmt_mode = drm_mode_find_dmt(fb_helper->dev, 1024, 768, 60, false);
1768
1769 for (i = 0; i < fb_helper->connector_count; i++) {
1770
1771 if (!enabled[i])
1772 continue;
1773
1774 fb_helper_conn = fb_helper->connector_info[i];
1775 list_for_each_entry(mode, &fb_helper_conn->connector->modes, head) {
1776 if (drm_mode_equal(mode, dmt_mode))
1777 modes[i] = mode;
1778 }
1779 if (!modes[i])
1780 can_clone = false;
1781 }
1782
1783 if (can_clone) {
1784 DRM_DEBUG_KMS("can clone using 1024x768\n");
1785 return true;
1786 }
1787 DRM_INFO("kms: can't enable cloning when we probably wanted to.\n");
1788 return false;
1789}
1790
1791static int drm_get_tile_offsets(struct drm_fb_helper *fb_helper,
1792 struct drm_display_mode **modes,
1793 struct drm_fb_offset *offsets,
1794 int idx,
1795 int h_idx, int v_idx)
1796{
1797 struct drm_fb_helper_connector *fb_helper_conn;
1798 int i;
1799 int hoffset = 0, voffset = 0;
1800
1801 for (i = 0; i < fb_helper->connector_count; i++) {
1802 fb_helper_conn = fb_helper->connector_info[i];
1803 if (!fb_helper_conn->connector->has_tile)
1804 continue;
1805
1806 if (!modes[i] && (h_idx || v_idx)) {
1807 DRM_DEBUG_KMS("no modes for connector tiled %d %d\n", i,
1808 fb_helper_conn->connector->base.id);
1809 continue;
1810 }
1811 if (fb_helper_conn->connector->tile_h_loc < h_idx)
1812 hoffset += modes[i]->hdisplay;
1813
1814 if (fb_helper_conn->connector->tile_v_loc < v_idx)
1815 voffset += modes[i]->vdisplay;
1816 }
1817 offsets[idx].x = hoffset;
1818 offsets[idx].y = voffset;
1819 DRM_DEBUG_KMS("returned %d %d for %d %d\n", hoffset, voffset, h_idx, v_idx);
1820 return 0;
1821}
1822
1823static bool drm_target_preferred(struct drm_fb_helper *fb_helper,
1824 struct drm_display_mode **modes,
1825 struct drm_fb_offset *offsets,
1826 bool *enabled, int width, int height)
1827{
1828 struct drm_fb_helper_connector *fb_helper_conn;
1829 int i;
1830 uint64_t conn_configured = 0, mask;
1831 int tile_pass = 0;
1832 mask = (1 << fb_helper->connector_count) - 1;
1833retry:
1834 for (i = 0; i < fb_helper->connector_count; i++) {
1835 fb_helper_conn = fb_helper->connector_info[i];
1836
1837 if (conn_configured & (1 << i))
1838 continue;
1839
1840 if (enabled[i] == false) {
1841 conn_configured |= (1 << i);
1842 continue;
1843 }
1844
1845
1846 if (tile_pass == 0 && fb_helper_conn->connector->has_tile)
1847 continue;
1848
1849 if (tile_pass == 1) {
1850 if (fb_helper_conn->connector->tile_h_loc != 0 ||
1851 fb_helper_conn->connector->tile_v_loc != 0)
1852 continue;
1853
1854 } else {
1855 if (fb_helper_conn->connector->tile_h_loc != tile_pass -1 &&
1856 fb_helper_conn->connector->tile_v_loc != tile_pass - 1)
1857
1858 continue;
1859
1860
1861
1862 drm_get_tile_offsets(fb_helper, modes, offsets,
1863 i, fb_helper_conn->connector->tile_h_loc, fb_helper_conn->connector->tile_v_loc);
1864 }
1865 DRM_DEBUG_KMS("looking for cmdline mode on connector %d\n",
1866 fb_helper_conn->connector->base.id);
1867
1868
1869 modes[i] = drm_pick_cmdline_mode(fb_helper_conn, width, height);
1870 if (!modes[i]) {
1871 DRM_DEBUG_KMS("looking for preferred mode on connector %d %d\n",
1872 fb_helper_conn->connector->base.id, fb_helper_conn->connector->tile_group ? fb_helper_conn->connector->tile_group->id : 0);
1873 modes[i] = drm_has_preferred_mode(fb_helper_conn, width, height);
1874 }
1875
1876 if (!modes[i] && !list_empty(&fb_helper_conn->connector->modes)) {
1877 list_for_each_entry(modes[i], &fb_helper_conn->connector->modes, head)
1878 break;
1879 }
1880 DRM_DEBUG_KMS("found mode %s\n", modes[i] ? modes[i]->name :
1881 "none");
1882 conn_configured |= (1 << i);
1883 }
1884
1885 if ((conn_configured & mask) != mask) {
1886 tile_pass++;
1887 goto retry;
1888 }
1889 return true;
1890}
1891
1892static int drm_pick_crtcs(struct drm_fb_helper *fb_helper,
1893 struct drm_fb_helper_crtc **best_crtcs,
1894 struct drm_display_mode **modes,
1895 int n, int width, int height)
1896{
1897 int c, o;
1898 struct drm_device *dev = fb_helper->dev;
1899 struct drm_connector *connector;
1900 const struct drm_connector_helper_funcs *connector_funcs;
1901 struct drm_encoder *encoder;
1902 int my_score, best_score, score;
1903 struct drm_fb_helper_crtc **crtcs, *crtc;
1904 struct drm_fb_helper_connector *fb_helper_conn;
1905
1906 if (n == fb_helper->connector_count)
1907 return 0;
1908
1909 fb_helper_conn = fb_helper->connector_info[n];
1910 connector = fb_helper_conn->connector;
1911
1912 best_crtcs[n] = NULL;
1913 best_score = drm_pick_crtcs(fb_helper, best_crtcs, modes, n+1, width, height);
1914 if (modes[n] == NULL)
1915 return best_score;
1916
1917 crtcs = kzalloc(dev->mode_config.num_connector *
1918 sizeof(struct drm_fb_helper_crtc *), GFP_KERNEL);
1919 if (!crtcs)
1920 return best_score;
1921
1922 my_score = 1;
1923 if (connector->status == connector_status_connected)
1924 my_score++;
1925 if (drm_has_cmdline_mode(fb_helper_conn))
1926 my_score++;
1927 if (drm_has_preferred_mode(fb_helper_conn, width, height))
1928 my_score++;
1929
1930 connector_funcs = connector->helper_private;
1931 encoder = connector_funcs->best_encoder(connector);
1932 if (!encoder)
1933 goto out;
1934
1935
1936
1937 for (c = 0; c < fb_helper->crtc_count; c++) {
1938 crtc = &fb_helper->crtc_info[c];
1939
1940 if ((encoder->possible_crtcs & (1 << c)) == 0)
1941 continue;
1942
1943 for (o = 0; o < n; o++)
1944 if (best_crtcs[o] == crtc)
1945 break;
1946
1947 if (o < n) {
1948
1949 if (fb_helper->crtc_count > 1)
1950 continue;
1951
1952 if (!drm_mode_equal(modes[o], modes[n]))
1953 continue;
1954 }
1955
1956 crtcs[n] = crtc;
1957 memcpy(crtcs, best_crtcs, n * sizeof(struct drm_fb_helper_crtc *));
1958 score = my_score + drm_pick_crtcs(fb_helper, crtcs, modes, n + 1,
1959 width, height);
1960 if (score > best_score) {
1961 best_score = score;
1962 memcpy(best_crtcs, crtcs,
1963 dev->mode_config.num_connector *
1964 sizeof(struct drm_fb_helper_crtc *));
1965 }
1966 }
1967out:
1968 kfree(crtcs);
1969 return best_score;
1970}
1971
1972static void drm_setup_crtcs(struct drm_fb_helper *fb_helper)
1973{
1974 struct drm_device *dev = fb_helper->dev;
1975 struct drm_fb_helper_crtc **crtcs;
1976 struct drm_display_mode **modes;
1977 struct drm_fb_offset *offsets;
1978 struct drm_mode_set *modeset;
1979 bool *enabled;
1980 int width, height;
1981 int i;
1982
1983 DRM_DEBUG_KMS("\n");
1984
1985 width = dev->mode_config.max_width;
1986 height = dev->mode_config.max_height;
1987
1988 crtcs = kcalloc(fb_helper->connector_count,
1989 sizeof(struct drm_fb_helper_crtc *), GFP_KERNEL);
1990 modes = kcalloc(fb_helper->connector_count,
1991 sizeof(struct drm_display_mode *), GFP_KERNEL);
1992 offsets = kcalloc(fb_helper->connector_count,
1993 sizeof(struct drm_fb_offset), GFP_KERNEL);
1994 enabled = kcalloc(fb_helper->connector_count,
1995 sizeof(bool), GFP_KERNEL);
1996 if (!crtcs || !modes || !enabled || !offsets) {
1997 DRM_ERROR("Memory allocation failed\n");
1998 goto out;
1999 }
2000
2001
2002 drm_enable_connectors(fb_helper, enabled);
2003
2004 if (!(fb_helper->funcs->initial_config &&
2005 fb_helper->funcs->initial_config(fb_helper, crtcs, modes,
2006 offsets,
2007 enabled, width, height))) {
2008 memset(modes, 0, fb_helper->connector_count*sizeof(modes[0]));
2009 memset(crtcs, 0, fb_helper->connector_count*sizeof(crtcs[0]));
2010 memset(offsets, 0, fb_helper->connector_count*sizeof(offsets[0]));
2011
2012 if (!drm_target_cloned(fb_helper, modes, offsets,
2013 enabled, width, height) &&
2014 !drm_target_preferred(fb_helper, modes, offsets,
2015 enabled, width, height))
2016 DRM_ERROR("Unable to find initial modes\n");
2017
2018 DRM_DEBUG_KMS("picking CRTCs for %dx%d config\n",
2019 width, height);
2020
2021 drm_pick_crtcs(fb_helper, crtcs, modes, 0, width, height);
2022 }
2023
2024
2025
2026 for (i = 0; i < fb_helper->crtc_count; i++) {
2027 modeset = &fb_helper->crtc_info[i].mode_set;
2028 modeset->num_connectors = 0;
2029 modeset->fb = NULL;
2030 }
2031
2032 for (i = 0; i < fb_helper->connector_count; i++) {
2033 struct drm_display_mode *mode = modes[i];
2034 struct drm_fb_helper_crtc *fb_crtc = crtcs[i];
2035 struct drm_fb_offset *offset = &offsets[i];
2036 modeset = &fb_crtc->mode_set;
2037
2038 if (mode && fb_crtc) {
2039 DRM_DEBUG_KMS("desired mode %s set on crtc %d (%d,%d)\n",
2040 mode->name, fb_crtc->mode_set.crtc->base.id, offset->x, offset->y);
2041 fb_crtc->desired_mode = mode;
2042 fb_crtc->x = offset->x;
2043 fb_crtc->y = offset->y;
2044 if (modeset->mode)
2045 drm_mode_destroy(dev, modeset->mode);
2046 modeset->mode = drm_mode_duplicate(dev,
2047 fb_crtc->desired_mode);
2048 modeset->connectors[modeset->num_connectors++] = fb_helper->connector_info[i]->connector;
2049 modeset->fb = fb_helper->fb;
2050 modeset->x = offset->x;
2051 modeset->y = offset->y;
2052 }
2053 }
2054
2055
2056 for (i = 0; i < fb_helper->crtc_count; i++) {
2057 modeset = &fb_helper->crtc_info[i].mode_set;
2058 if (modeset->num_connectors == 0) {
2059 BUG_ON(modeset->fb);
2060 if (modeset->mode)
2061 drm_mode_destroy(dev, modeset->mode);
2062 modeset->mode = NULL;
2063 }
2064 }
2065out:
2066 kfree(crtcs);
2067 kfree(modes);
2068 kfree(offsets);
2069 kfree(enabled);
2070}
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114int drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper, int bpp_sel)
2115{
2116 struct drm_device *dev = fb_helper->dev;
2117 int count = 0;
2118
2119 if (!drm_fbdev_emulation)
2120 return 0;
2121
2122 mutex_lock(&dev->mode_config.mutex);
2123 count = drm_fb_helper_probe_connector_modes(fb_helper,
2124 dev->mode_config.max_width,
2125 dev->mode_config.max_height);
2126 mutex_unlock(&dev->mode_config.mutex);
2127
2128
2129
2130 if (count == 0)
2131 dev_info(fb_helper->dev->dev, "No connectors reported connected with modes\n");
2132
2133 drm_setup_crtcs(fb_helper);
2134
2135 return drm_fb_helper_single_fb_probe(fb_helper, bpp_sel);
2136}
2137EXPORT_SYMBOL(drm_fb_helper_initial_config);
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper)
2161{
2162 struct drm_device *dev = fb_helper->dev;
2163 u32 max_width, max_height;
2164
2165 if (!drm_fbdev_emulation)
2166 return 0;
2167
2168 mutex_lock(&fb_helper->dev->mode_config.mutex);
2169 if (!fb_helper->fb || !drm_fb_helper_is_bound(fb_helper)) {
2170 fb_helper->delayed_hotplug = true;
2171 mutex_unlock(&fb_helper->dev->mode_config.mutex);
2172 return 0;
2173 }
2174 DRM_DEBUG_KMS("\n");
2175
2176 max_width = fb_helper->fb->width;
2177 max_height = fb_helper->fb->height;
2178
2179 drm_fb_helper_probe_connector_modes(fb_helper, max_width, max_height);
2180 mutex_unlock(&fb_helper->dev->mode_config.mutex);
2181
2182 drm_modeset_lock_all(dev);
2183 drm_setup_crtcs(fb_helper);
2184 drm_modeset_unlock_all(dev);
2185 drm_fb_helper_set_par(fb_helper->fbdev);
2186
2187 return 0;
2188}
2189EXPORT_SYMBOL(drm_fb_helper_hotplug_event);
2190
2191
2192
2193
2194
2195int __init drm_fb_helper_modinit(void)
2196{
2197#if defined(CONFIG_FRAMEBUFFER_CONSOLE_MODULE) && !defined(CONFIG_EXPERT)
2198 const char *name = "fbcon";
2199 struct module *fbcon;
2200
2201 mutex_lock(&module_mutex);
2202 fbcon = find_module(name);
2203 mutex_unlock(&module_mutex);
2204
2205 if (!fbcon)
2206 request_module_nowait(name);
2207#endif
2208 return 0;
2209}
2210EXPORT_SYMBOL(drm_fb_helper_modinit);
2211