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