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