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