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/dma-buf.h>
34#include <linux/kernel.h>
35#include <linux/sysrq.h>
36#include <linux/slab.h>
37#include <linux/module.h>
38#include <drm/drmP.h>
39#include <drm/drm_crtc.h>
40#include <drm/drm_fb_helper.h>
41#include <drm/drm_crtc_helper.h>
42#include <drm/drm_atomic.h>
43#include <drm/drm_atomic_helper.h>
44
45#include "drm_crtc_internal.h"
46#include "drm_crtc_helper_internal.h"
47
48static bool drm_fbdev_emulation = true;
49module_param_named(fbdev_emulation, drm_fbdev_emulation, bool, 0600);
50MODULE_PARM_DESC(fbdev_emulation,
51 "Enable legacy fbdev emulation [default=true]");
52
53static int drm_fbdev_overalloc = CONFIG_DRM_FBDEV_OVERALLOC;
54module_param(drm_fbdev_overalloc, int, 0444);
55MODULE_PARM_DESC(drm_fbdev_overalloc,
56 "Overallocation of the fbdev buffer (%) [default="
57 __MODULE_STRING(CONFIG_DRM_FBDEV_OVERALLOC) "]");
58
59
60
61
62
63
64
65
66
67
68
69
70
71#if IS_ENABLED(CONFIG_DRM_FBDEV_LEAK_PHYS_SMEM)
72static bool drm_leak_fbdev_smem = false;
73module_param_unsafe(drm_leak_fbdev_smem, bool, 0600);
74MODULE_PARM_DESC(drm_leak_fbdev_smem,
75 "Allow unsafe leaking fbdev physical smem address [default=false]");
76#endif
77
78static LIST_HEAD(kernel_fb_helper_list);
79static DEFINE_MUTEX(kernel_fb_helper_lock);
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137#define drm_fb_helper_for_each_connector(fbh, i__) \
138 for (({ lockdep_assert_held(&(fbh)->lock); }), \
139 i__ = 0; i__ < (fbh)->connector_count; i__++)
140
141static int __drm_fb_helper_add_one_connector(struct drm_fb_helper *fb_helper,
142 struct drm_connector *connector)
143{
144 struct drm_fb_helper_connector *fb_conn;
145 struct drm_fb_helper_connector **temp;
146 unsigned int count;
147
148 if (!drm_fbdev_emulation)
149 return 0;
150
151 lockdep_assert_held(&fb_helper->lock);
152
153 count = fb_helper->connector_count + 1;
154
155 if (count > fb_helper->connector_info_alloc_count) {
156 size_t size = count * sizeof(fb_conn);
157
158 temp = krealloc(fb_helper->connector_info, size, GFP_KERNEL);
159 if (!temp)
160 return -ENOMEM;
161
162 fb_helper->connector_info_alloc_count = count;
163 fb_helper->connector_info = temp;
164 }
165
166 fb_conn = kzalloc(sizeof(*fb_conn), GFP_KERNEL);
167 if (!fb_conn)
168 return -ENOMEM;
169
170 drm_connector_get(connector);
171 fb_conn->connector = connector;
172 fb_helper->connector_info[fb_helper->connector_count++] = fb_conn;
173
174 return 0;
175}
176
177int drm_fb_helper_add_one_connector(struct drm_fb_helper *fb_helper,
178 struct drm_connector *connector)
179{
180 int err;
181
182 if (!fb_helper)
183 return 0;
184
185 mutex_lock(&fb_helper->lock);
186 err = __drm_fb_helper_add_one_connector(fb_helper, connector);
187 mutex_unlock(&fb_helper->lock);
188
189 return err;
190}
191EXPORT_SYMBOL(drm_fb_helper_add_one_connector);
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207int drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper)
208{
209 struct drm_device *dev;
210 struct drm_connector *connector;
211 struct drm_connector_list_iter conn_iter;
212 int i, ret = 0;
213
214 if (!drm_fbdev_emulation || !fb_helper)
215 return 0;
216
217 dev = fb_helper->dev;
218
219 mutex_lock(&fb_helper->lock);
220 drm_connector_list_iter_begin(dev, &conn_iter);
221 drm_for_each_connector_iter(connector, &conn_iter) {
222 if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK)
223 continue;
224
225 ret = __drm_fb_helper_add_one_connector(fb_helper, connector);
226 if (ret)
227 goto fail;
228 }
229 goto out;
230
231fail:
232 drm_fb_helper_for_each_connector(fb_helper, i) {
233 struct drm_fb_helper_connector *fb_helper_connector =
234 fb_helper->connector_info[i];
235
236 drm_connector_put(fb_helper_connector->connector);
237
238 kfree(fb_helper_connector);
239 fb_helper->connector_info[i] = NULL;
240 }
241 fb_helper->connector_count = 0;
242out:
243 drm_connector_list_iter_end(&conn_iter);
244 mutex_unlock(&fb_helper->lock);
245
246 return ret;
247}
248EXPORT_SYMBOL(drm_fb_helper_single_add_all_connectors);
249
250static int __drm_fb_helper_remove_one_connector(struct drm_fb_helper *fb_helper,
251 struct drm_connector *connector)
252{
253 struct drm_fb_helper_connector *fb_helper_connector;
254 int i, j;
255
256 if (!drm_fbdev_emulation)
257 return 0;
258
259 lockdep_assert_held(&fb_helper->lock);
260
261 drm_fb_helper_for_each_connector(fb_helper, i) {
262 if (fb_helper->connector_info[i]->connector == connector)
263 break;
264 }
265
266 if (i == fb_helper->connector_count)
267 return -EINVAL;
268 fb_helper_connector = fb_helper->connector_info[i];
269 drm_connector_put(fb_helper_connector->connector);
270
271 for (j = i + 1; j < fb_helper->connector_count; j++)
272 fb_helper->connector_info[j - 1] = fb_helper->connector_info[j];
273
274 fb_helper->connector_count--;
275 kfree(fb_helper_connector);
276
277 return 0;
278}
279
280int drm_fb_helper_remove_one_connector(struct drm_fb_helper *fb_helper,
281 struct drm_connector *connector)
282{
283 int err;
284
285 if (!fb_helper)
286 return 0;
287
288 mutex_lock(&fb_helper->lock);
289 err = __drm_fb_helper_remove_one_connector(fb_helper, connector);
290 mutex_unlock(&fb_helper->lock);
291
292 return err;
293}
294EXPORT_SYMBOL(drm_fb_helper_remove_one_connector);
295
296static void drm_fb_helper_restore_lut_atomic(struct drm_crtc *crtc)
297{
298 uint16_t *r_base, *g_base, *b_base;
299
300 if (crtc->funcs->gamma_set == NULL)
301 return;
302
303 r_base = crtc->gamma_store;
304 g_base = r_base + crtc->gamma_size;
305 b_base = g_base + crtc->gamma_size;
306
307 crtc->funcs->gamma_set(crtc, r_base, g_base, b_base,
308 crtc->gamma_size, NULL);
309}
310
311
312
313
314
315int drm_fb_helper_debug_enter(struct fb_info *info)
316{
317 struct drm_fb_helper *helper = info->par;
318 const struct drm_crtc_helper_funcs *funcs;
319 int i;
320
321 list_for_each_entry(helper, &kernel_fb_helper_list, kernel_fb_list) {
322 for (i = 0; i < helper->crtc_count; i++) {
323 struct drm_mode_set *mode_set =
324 &helper->crtc_info[i].mode_set;
325
326 if (!mode_set->crtc->enabled)
327 continue;
328
329 funcs = mode_set->crtc->helper_private;
330 if (funcs->mode_set_base_atomic == NULL)
331 continue;
332
333 if (drm_drv_uses_atomic_modeset(mode_set->crtc->dev))
334 continue;
335
336 funcs->mode_set_base_atomic(mode_set->crtc,
337 mode_set->fb,
338 mode_set->x,
339 mode_set->y,
340 ENTER_ATOMIC_MODE_SET);
341 }
342 }
343
344 return 0;
345}
346EXPORT_SYMBOL(drm_fb_helper_debug_enter);
347
348
349
350
351
352int drm_fb_helper_debug_leave(struct fb_info *info)
353{
354 struct drm_fb_helper *helper = info->par;
355 struct drm_crtc *crtc;
356 const struct drm_crtc_helper_funcs *funcs;
357 struct drm_framebuffer *fb;
358 int i;
359
360 for (i = 0; i < helper->crtc_count; i++) {
361 struct drm_mode_set *mode_set = &helper->crtc_info[i].mode_set;
362
363 crtc = mode_set->crtc;
364 if (drm_drv_uses_atomic_modeset(crtc->dev))
365 continue;
366
367 funcs = crtc->helper_private;
368 fb = crtc->primary->fb;
369
370 if (!crtc->enabled)
371 continue;
372
373 if (!fb) {
374 DRM_ERROR("no fb to restore??\n");
375 continue;
376 }
377
378 if (funcs->mode_set_base_atomic == NULL)
379 continue;
380
381 drm_fb_helper_restore_lut_atomic(mode_set->crtc);
382 funcs->mode_set_base_atomic(mode_set->crtc, fb, crtc->x,
383 crtc->y, LEAVE_ATOMIC_MODE_SET);
384 }
385
386 return 0;
387}
388EXPORT_SYMBOL(drm_fb_helper_debug_leave);
389
390static int restore_fbdev_mode_atomic(struct drm_fb_helper *fb_helper, bool active)
391{
392 struct drm_device *dev = fb_helper->dev;
393 struct drm_plane_state *plane_state;
394 struct drm_plane *plane;
395 struct drm_atomic_state *state;
396 int i, ret;
397 struct drm_modeset_acquire_ctx ctx;
398
399 drm_modeset_acquire_init(&ctx, 0);
400
401 state = drm_atomic_state_alloc(dev);
402 if (!state) {
403 ret = -ENOMEM;
404 goto out_ctx;
405 }
406
407 state->acquire_ctx = &ctx;
408retry:
409 drm_for_each_plane(plane, dev) {
410 plane_state = drm_atomic_get_plane_state(state, plane);
411 if (IS_ERR(plane_state)) {
412 ret = PTR_ERR(plane_state);
413 goto out_state;
414 }
415
416 plane_state->rotation = DRM_MODE_ROTATE_0;
417
418
419 if (plane->type == DRM_PLANE_TYPE_PRIMARY)
420 continue;
421
422 ret = __drm_atomic_helper_disable_plane(plane, plane_state);
423 if (ret != 0)
424 goto out_state;
425 }
426
427 for (i = 0; i < fb_helper->crtc_count; i++) {
428 struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set;
429 struct drm_plane *primary = mode_set->crtc->primary;
430
431
432 plane_state = drm_atomic_get_new_plane_state(state, primary);
433 plane_state->rotation = fb_helper->crtc_info[i].rotation;
434
435 ret = __drm_atomic_helper_set_config(mode_set, state);
436 if (ret != 0)
437 goto out_state;
438
439
440
441
442
443 if (!active) {
444 struct drm_crtc *crtc = mode_set->crtc;
445 struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
446
447 crtc_state->active = false;
448 }
449 }
450
451 ret = drm_atomic_commit(state);
452
453out_state:
454 if (ret == -EDEADLK)
455 goto backoff;
456
457 drm_atomic_state_put(state);
458out_ctx:
459 drm_modeset_drop_locks(&ctx);
460 drm_modeset_acquire_fini(&ctx);
461
462 return ret;
463
464backoff:
465 drm_atomic_state_clear(state);
466 drm_modeset_backoff(&ctx);
467
468 goto retry;
469}
470
471static int restore_fbdev_mode_legacy(struct drm_fb_helper *fb_helper)
472{
473 struct drm_device *dev = fb_helper->dev;
474 struct drm_plane *plane;
475 int i, ret = 0;
476
477 drm_modeset_lock_all(fb_helper->dev);
478 drm_for_each_plane(plane, dev) {
479 if (plane->type != DRM_PLANE_TYPE_PRIMARY)
480 drm_plane_force_disable(plane);
481
482 if (plane->rotation_property)
483 drm_mode_plane_set_obj_prop(plane,
484 plane->rotation_property,
485 DRM_MODE_ROTATE_0);
486 }
487
488 for (i = 0; i < fb_helper->crtc_count; i++) {
489 struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set;
490 struct drm_crtc *crtc = mode_set->crtc;
491
492 if (crtc->funcs->cursor_set2) {
493 ret = crtc->funcs->cursor_set2(crtc, NULL, 0, 0, 0, 0, 0);
494 if (ret)
495 goto out;
496 } else if (crtc->funcs->cursor_set) {
497 ret = crtc->funcs->cursor_set(crtc, NULL, 0, 0, 0);
498 if (ret)
499 goto out;
500 }
501
502 ret = drm_mode_set_config_internal(mode_set);
503 if (ret)
504 goto out;
505 }
506out:
507 drm_modeset_unlock_all(fb_helper->dev);
508
509 return ret;
510}
511
512static int restore_fbdev_mode(struct drm_fb_helper *fb_helper)
513{
514 struct drm_device *dev = fb_helper->dev;
515
516 if (drm_drv_uses_atomic_modeset(dev))
517 return restore_fbdev_mode_atomic(fb_helper, true);
518 else
519 return restore_fbdev_mode_legacy(fb_helper);
520}
521
522
523
524
525
526
527
528
529
530
531
532
533int drm_fb_helper_restore_fbdev_mode_unlocked(struct drm_fb_helper *fb_helper)
534{
535 bool do_delayed;
536 int ret;
537
538 if (!drm_fbdev_emulation || !fb_helper)
539 return -ENODEV;
540
541 if (READ_ONCE(fb_helper->deferred_setup))
542 return 0;
543
544 mutex_lock(&fb_helper->lock);
545 ret = restore_fbdev_mode(fb_helper);
546
547 do_delayed = fb_helper->delayed_hotplug;
548 if (do_delayed)
549 fb_helper->delayed_hotplug = false;
550 mutex_unlock(&fb_helper->lock);
551
552 if (do_delayed)
553 drm_fb_helper_hotplug_event(fb_helper);
554
555 return ret;
556}
557EXPORT_SYMBOL(drm_fb_helper_restore_fbdev_mode_unlocked);
558
559static bool drm_fb_helper_is_bound(struct drm_fb_helper *fb_helper)
560{
561 struct drm_device *dev = fb_helper->dev;
562 struct drm_crtc *crtc;
563 int bound = 0, crtcs_bound = 0;
564
565
566
567
568
569 if (READ_ONCE(dev->master))
570 return false;
571
572 drm_for_each_crtc(crtc, dev) {
573 drm_modeset_lock(&crtc->mutex, NULL);
574 if (crtc->primary->fb)
575 crtcs_bound++;
576 if (crtc->primary->fb == fb_helper->fb)
577 bound++;
578 drm_modeset_unlock(&crtc->mutex);
579 }
580
581 if (bound < crtcs_bound)
582 return false;
583
584 return true;
585}
586
587#ifdef CONFIG_MAGIC_SYSRQ
588
589
590
591
592static bool drm_fb_helper_force_kernel_mode(void)
593{
594 bool ret, error = false;
595 struct drm_fb_helper *helper;
596
597 if (list_empty(&kernel_fb_helper_list))
598 return false;
599
600 list_for_each_entry(helper, &kernel_fb_helper_list, kernel_fb_list) {
601 struct drm_device *dev = helper->dev;
602
603 if (dev->switch_power_state == DRM_SWITCH_POWER_OFF)
604 continue;
605
606 mutex_lock(&helper->lock);
607 ret = restore_fbdev_mode(helper);
608 if (ret)
609 error = true;
610 mutex_unlock(&helper->lock);
611 }
612 return error;
613}
614
615static void drm_fb_helper_restore_work_fn(struct work_struct *ignored)
616{
617 bool ret;
618
619 ret = drm_fb_helper_force_kernel_mode();
620 if (ret == true)
621 DRM_ERROR("Failed to restore crtc configuration\n");
622}
623static DECLARE_WORK(drm_fb_helper_restore_work, drm_fb_helper_restore_work_fn);
624
625static void drm_fb_helper_sysrq(int dummy1)
626{
627 schedule_work(&drm_fb_helper_restore_work);
628}
629
630static struct sysrq_key_op sysrq_drm_fb_helper_restore_op = {
631 .handler = drm_fb_helper_sysrq,
632 .help_msg = "force-fb(V)",
633 .action_msg = "Restore framebuffer console",
634};
635#else
636static struct sysrq_key_op sysrq_drm_fb_helper_restore_op = { };
637#endif
638
639static void dpms_legacy(struct drm_fb_helper *fb_helper, int dpms_mode)
640{
641 struct drm_device *dev = fb_helper->dev;
642 struct drm_connector *connector;
643 struct drm_mode_set *modeset;
644 int i, j;
645
646 drm_modeset_lock_all(dev);
647 for (i = 0; i < fb_helper->crtc_count; i++) {
648 modeset = &fb_helper->crtc_info[i].mode_set;
649
650 if (!modeset->crtc->enabled)
651 continue;
652
653 for (j = 0; j < modeset->num_connectors; j++) {
654 connector = modeset->connectors[j];
655 connector->funcs->dpms(connector, dpms_mode);
656 drm_object_property_set_value(&connector->base,
657 dev->mode_config.dpms_property, dpms_mode);
658 }
659 }
660 drm_modeset_unlock_all(dev);
661}
662
663static void drm_fb_helper_dpms(struct fb_info *info, int dpms_mode)
664{
665 struct drm_fb_helper *fb_helper = info->par;
666
667
668
669
670 mutex_lock(&fb_helper->lock);
671 if (!drm_fb_helper_is_bound(fb_helper)) {
672 mutex_unlock(&fb_helper->lock);
673 return;
674 }
675
676 if (drm_drv_uses_atomic_modeset(fb_helper->dev))
677 restore_fbdev_mode_atomic(fb_helper, dpms_mode == DRM_MODE_DPMS_ON);
678 else
679 dpms_legacy(fb_helper, dpms_mode);
680 mutex_unlock(&fb_helper->lock);
681}
682
683
684
685
686
687
688int drm_fb_helper_blank(int blank, struct fb_info *info)
689{
690 if (oops_in_progress)
691 return -EBUSY;
692
693 switch (blank) {
694
695 case FB_BLANK_UNBLANK:
696 drm_fb_helper_dpms(info, DRM_MODE_DPMS_ON);
697 break;
698
699 case FB_BLANK_NORMAL:
700 drm_fb_helper_dpms(info, DRM_MODE_DPMS_STANDBY);
701 break;
702
703 case FB_BLANK_HSYNC_SUSPEND:
704 drm_fb_helper_dpms(info, DRM_MODE_DPMS_STANDBY);
705 break;
706
707 case FB_BLANK_VSYNC_SUSPEND:
708 drm_fb_helper_dpms(info, DRM_MODE_DPMS_SUSPEND);
709 break;
710
711 case FB_BLANK_POWERDOWN:
712 drm_fb_helper_dpms(info, DRM_MODE_DPMS_OFF);
713 break;
714 }
715 return 0;
716}
717EXPORT_SYMBOL(drm_fb_helper_blank);
718
719static void drm_fb_helper_modeset_release(struct drm_fb_helper *helper,
720 struct drm_mode_set *modeset)
721{
722 int i;
723
724 for (i = 0; i < modeset->num_connectors; i++) {
725 drm_connector_put(modeset->connectors[i]);
726 modeset->connectors[i] = NULL;
727 }
728 modeset->num_connectors = 0;
729
730 drm_mode_destroy(helper->dev, modeset->mode);
731 modeset->mode = NULL;
732
733
734 modeset->fb = NULL;
735}
736
737static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper)
738{
739 int i;
740
741 for (i = 0; i < helper->connector_count; i++) {
742 drm_connector_put(helper->connector_info[i]->connector);
743 kfree(helper->connector_info[i]);
744 }
745 kfree(helper->connector_info);
746
747 for (i = 0; i < helper->crtc_count; i++) {
748 struct drm_mode_set *modeset = &helper->crtc_info[i].mode_set;
749
750 drm_fb_helper_modeset_release(helper, modeset);
751 kfree(modeset->connectors);
752 }
753 kfree(helper->crtc_info);
754}
755
756static void drm_fb_helper_resume_worker(struct work_struct *work)
757{
758 struct drm_fb_helper *helper = container_of(work, struct drm_fb_helper,
759 resume_work);
760
761 console_lock();
762 fb_set_suspend(helper->fbdev, 0);
763 console_unlock();
764}
765
766static void drm_fb_helper_dirty_blit_real(struct drm_fb_helper *fb_helper,
767 struct drm_clip_rect *clip)
768{
769 struct drm_framebuffer *fb = fb_helper->fb;
770 unsigned int cpp = drm_format_plane_cpp(fb->format->format, 0);
771 size_t offset = clip->y1 * fb->pitches[0] + clip->x1 * cpp;
772 void *src = fb_helper->fbdev->screen_buffer + offset;
773 void *dst = fb_helper->buffer->vaddr + offset;
774 size_t len = (clip->x2 - clip->x1) * cpp;
775 unsigned int y;
776
777 for (y = clip->y1; y < clip->y2; y++) {
778 memcpy(dst, src, len);
779 src += fb->pitches[0];
780 dst += fb->pitches[0];
781 }
782}
783
784static void drm_fb_helper_dirty_work(struct work_struct *work)
785{
786 struct drm_fb_helper *helper = container_of(work, struct drm_fb_helper,
787 dirty_work);
788 struct drm_clip_rect *clip = &helper->dirty_clip;
789 struct drm_clip_rect clip_copy;
790 unsigned long flags;
791
792 spin_lock_irqsave(&helper->dirty_lock, flags);
793 clip_copy = *clip;
794 clip->x1 = clip->y1 = ~0;
795 clip->x2 = clip->y2 = 0;
796 spin_unlock_irqrestore(&helper->dirty_lock, flags);
797
798
799 if (clip_copy.x1 < clip_copy.x2 && clip_copy.y1 < clip_copy.y2) {
800
801 if (helper->buffer)
802 drm_fb_helper_dirty_blit_real(helper, &clip_copy);
803 helper->fb->funcs->dirty(helper->fb, NULL, 0, 0, &clip_copy, 1);
804 }
805}
806
807
808
809
810
811
812
813
814
815
816void drm_fb_helper_prepare(struct drm_device *dev, struct drm_fb_helper *helper,
817 const struct drm_fb_helper_funcs *funcs)
818{
819 INIT_LIST_HEAD(&helper->kernel_fb_list);
820 spin_lock_init(&helper->dirty_lock);
821 INIT_WORK(&helper->resume_work, drm_fb_helper_resume_worker);
822 INIT_WORK(&helper->dirty_work, drm_fb_helper_dirty_work);
823 helper->dirty_clip.x1 = helper->dirty_clip.y1 = ~0;
824 mutex_init(&helper->lock);
825 helper->funcs = funcs;
826 helper->dev = dev;
827}
828EXPORT_SYMBOL(drm_fb_helper_prepare);
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846int drm_fb_helper_init(struct drm_device *dev,
847 struct drm_fb_helper *fb_helper,
848 int max_conn_count)
849{
850 struct drm_crtc *crtc;
851 struct drm_mode_config *config = &dev->mode_config;
852 int i;
853
854 if (!drm_fbdev_emulation) {
855 dev->fb_helper = fb_helper;
856 return 0;
857 }
858
859 if (!max_conn_count)
860 return -EINVAL;
861
862 fb_helper->crtc_info = kcalloc(config->num_crtc, sizeof(struct drm_fb_helper_crtc), GFP_KERNEL);
863 if (!fb_helper->crtc_info)
864 return -ENOMEM;
865
866 fb_helper->crtc_count = config->num_crtc;
867 fb_helper->connector_info = kcalloc(dev->mode_config.num_connector, sizeof(struct drm_fb_helper_connector *), GFP_KERNEL);
868 if (!fb_helper->connector_info) {
869 kfree(fb_helper->crtc_info);
870 return -ENOMEM;
871 }
872 fb_helper->connector_info_alloc_count = dev->mode_config.num_connector;
873 fb_helper->connector_count = 0;
874
875 for (i = 0; i < fb_helper->crtc_count; i++) {
876 fb_helper->crtc_info[i].mode_set.connectors =
877 kcalloc(max_conn_count,
878 sizeof(struct drm_connector *),
879 GFP_KERNEL);
880
881 if (!fb_helper->crtc_info[i].mode_set.connectors)
882 goto out_free;
883 fb_helper->crtc_info[i].mode_set.num_connectors = 0;
884 fb_helper->crtc_info[i].rotation = DRM_MODE_ROTATE_0;
885 }
886
887 i = 0;
888 drm_for_each_crtc(crtc, dev) {
889 fb_helper->crtc_info[i].mode_set.crtc = crtc;
890 i++;
891 }
892
893 dev->fb_helper = fb_helper;
894
895 return 0;
896out_free:
897 drm_fb_helper_crtc_free(fb_helper);
898 return -ENOMEM;
899}
900EXPORT_SYMBOL(drm_fb_helper_init);
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915struct fb_info *drm_fb_helper_alloc_fbi(struct drm_fb_helper *fb_helper)
916{
917 struct device *dev = fb_helper->dev->dev;
918 struct fb_info *info;
919 int ret;
920
921 info = framebuffer_alloc(0, dev);
922 if (!info)
923 return ERR_PTR(-ENOMEM);
924
925 ret = fb_alloc_cmap(&info->cmap, 256, 0);
926 if (ret)
927 goto err_release;
928
929 info->apertures = alloc_apertures(1);
930 if (!info->apertures) {
931 ret = -ENOMEM;
932 goto err_free_cmap;
933 }
934
935 fb_helper->fbdev = info;
936 info->skip_vt_switch = true;
937
938 return info;
939
940err_free_cmap:
941 fb_dealloc_cmap(&info->cmap);
942err_release:
943 framebuffer_release(info);
944 return ERR_PTR(ret);
945}
946EXPORT_SYMBOL(drm_fb_helper_alloc_fbi);
947
948
949
950
951
952
953
954
955
956void drm_fb_helper_unregister_fbi(struct drm_fb_helper *fb_helper)
957{
958 if (fb_helper && fb_helper->fbdev)
959 unregister_framebuffer(fb_helper->fbdev);
960}
961EXPORT_SYMBOL(drm_fb_helper_unregister_fbi);
962
963
964
965
966
967
968
969
970void drm_fb_helper_fini(struct drm_fb_helper *fb_helper)
971{
972 struct fb_info *info;
973
974 if (!fb_helper)
975 return;
976
977 fb_helper->dev->fb_helper = NULL;
978
979 if (!drm_fbdev_emulation)
980 return;
981
982 cancel_work_sync(&fb_helper->resume_work);
983 cancel_work_sync(&fb_helper->dirty_work);
984
985 info = fb_helper->fbdev;
986 if (info) {
987 if (info->cmap.len)
988 fb_dealloc_cmap(&info->cmap);
989 framebuffer_release(info);
990 }
991 fb_helper->fbdev = NULL;
992
993 mutex_lock(&kernel_fb_helper_lock);
994 if (!list_empty(&fb_helper->kernel_fb_list)) {
995 list_del(&fb_helper->kernel_fb_list);
996 if (list_empty(&kernel_fb_helper_list))
997 unregister_sysrq_key('v', &sysrq_drm_fb_helper_restore_op);
998 }
999 mutex_unlock(&kernel_fb_helper_lock);
1000
1001 mutex_destroy(&fb_helper->lock);
1002 drm_fb_helper_crtc_free(fb_helper);
1003
1004}
1005EXPORT_SYMBOL(drm_fb_helper_fini);
1006
1007
1008
1009
1010
1011
1012
1013void drm_fb_helper_unlink_fbi(struct drm_fb_helper *fb_helper)
1014{
1015 if (fb_helper && fb_helper->fbdev)
1016 unlink_framebuffer(fb_helper->fbdev);
1017}
1018EXPORT_SYMBOL(drm_fb_helper_unlink_fbi);
1019
1020static void drm_fb_helper_dirty(struct fb_info *info, u32 x, u32 y,
1021 u32 width, u32 height)
1022{
1023 struct drm_fb_helper *helper = info->par;
1024 struct drm_clip_rect *clip = &helper->dirty_clip;
1025 unsigned long flags;
1026
1027 if (!helper->fb->funcs->dirty)
1028 return;
1029
1030 spin_lock_irqsave(&helper->dirty_lock, flags);
1031 clip->x1 = min_t(u32, clip->x1, x);
1032 clip->y1 = min_t(u32, clip->y1, y);
1033 clip->x2 = max_t(u32, clip->x2, x + width);
1034 clip->y2 = max_t(u32, clip->y2, y + height);
1035 spin_unlock_irqrestore(&helper->dirty_lock, flags);
1036
1037 schedule_work(&helper->dirty_work);
1038}
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048void drm_fb_helper_deferred_io(struct fb_info *info,
1049 struct list_head *pagelist)
1050{
1051 unsigned long start, end, min, max;
1052 struct page *page;
1053 u32 y1, y2;
1054
1055 min = ULONG_MAX;
1056 max = 0;
1057 list_for_each_entry(page, pagelist, lru) {
1058 start = page->index << PAGE_SHIFT;
1059 end = start + PAGE_SIZE - 1;
1060 min = min(min, start);
1061 max = max(max, end);
1062 }
1063
1064 if (min < max) {
1065 y1 = min / info->fix.line_length;
1066 y2 = min_t(u32, DIV_ROUND_UP(max, info->fix.line_length),
1067 info->var.yres);
1068 drm_fb_helper_dirty(info, 0, y1, info->var.xres, y2 - y1);
1069 }
1070}
1071EXPORT_SYMBOL(drm_fb_helper_deferred_io);
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089int drm_fb_helper_defio_init(struct drm_fb_helper *fb_helper)
1090{
1091 struct fb_info *info = fb_helper->fbdev;
1092 struct fb_deferred_io *fbdefio;
1093 struct fb_ops *fbops;
1094
1095 fbdefio = kzalloc(sizeof(*fbdefio), GFP_KERNEL);
1096 fbops = kzalloc(sizeof(*fbops), GFP_KERNEL);
1097 if (!fbdefio || !fbops) {
1098 kfree(fbdefio);
1099 kfree(fbops);
1100 return -ENOMEM;
1101 }
1102
1103 info->fbdefio = fbdefio;
1104 fbdefio->delay = msecs_to_jiffies(50);
1105 fbdefio->deferred_io = drm_fb_helper_deferred_io;
1106
1107 *fbops = *info->fbops;
1108 info->fbops = fbops;
1109
1110 fb_deferred_io_init(info);
1111
1112 return 0;
1113}
1114EXPORT_SYMBOL(drm_fb_helper_defio_init);
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125ssize_t drm_fb_helper_sys_read(struct fb_info *info, char __user *buf,
1126 size_t count, loff_t *ppos)
1127{
1128 return fb_sys_read(info, buf, count, ppos);
1129}
1130EXPORT_SYMBOL(drm_fb_helper_sys_read);
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141ssize_t drm_fb_helper_sys_write(struct fb_info *info, const char __user *buf,
1142 size_t count, loff_t *ppos)
1143{
1144 ssize_t ret;
1145
1146 ret = fb_sys_write(info, buf, count, ppos);
1147 if (ret > 0)
1148 drm_fb_helper_dirty(info, 0, 0, info->var.xres,
1149 info->var.yres);
1150
1151 return ret;
1152}
1153EXPORT_SYMBOL(drm_fb_helper_sys_write);
1154
1155
1156
1157
1158
1159
1160
1161
1162void drm_fb_helper_sys_fillrect(struct fb_info *info,
1163 const struct fb_fillrect *rect)
1164{
1165 sys_fillrect(info, rect);
1166 drm_fb_helper_dirty(info, rect->dx, rect->dy,
1167 rect->width, rect->height);
1168}
1169EXPORT_SYMBOL(drm_fb_helper_sys_fillrect);
1170
1171
1172
1173
1174
1175
1176
1177
1178void drm_fb_helper_sys_copyarea(struct fb_info *info,
1179 const struct fb_copyarea *area)
1180{
1181 sys_copyarea(info, area);
1182 drm_fb_helper_dirty(info, area->dx, area->dy,
1183 area->width, area->height);
1184}
1185EXPORT_SYMBOL(drm_fb_helper_sys_copyarea);
1186
1187
1188
1189
1190
1191
1192
1193
1194void drm_fb_helper_sys_imageblit(struct fb_info *info,
1195 const struct fb_image *image)
1196{
1197 sys_imageblit(info, image);
1198 drm_fb_helper_dirty(info, image->dx, image->dy,
1199 image->width, image->height);
1200}
1201EXPORT_SYMBOL(drm_fb_helper_sys_imageblit);
1202
1203
1204
1205
1206
1207
1208
1209
1210void drm_fb_helper_cfb_fillrect(struct fb_info *info,
1211 const struct fb_fillrect *rect)
1212{
1213 cfb_fillrect(info, rect);
1214 drm_fb_helper_dirty(info, rect->dx, rect->dy,
1215 rect->width, rect->height);
1216}
1217EXPORT_SYMBOL(drm_fb_helper_cfb_fillrect);
1218
1219
1220
1221
1222
1223
1224
1225
1226void drm_fb_helper_cfb_copyarea(struct fb_info *info,
1227 const struct fb_copyarea *area)
1228{
1229 cfb_copyarea(info, area);
1230 drm_fb_helper_dirty(info, area->dx, area->dy,
1231 area->width, area->height);
1232}
1233EXPORT_SYMBOL(drm_fb_helper_cfb_copyarea);
1234
1235
1236
1237
1238
1239
1240
1241
1242void drm_fb_helper_cfb_imageblit(struct fb_info *info,
1243 const struct fb_image *image)
1244{
1245 cfb_imageblit(info, image);
1246 drm_fb_helper_dirty(info, image->dx, image->dy,
1247 image->width, image->height);
1248}
1249EXPORT_SYMBOL(drm_fb_helper_cfb_imageblit);
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260void drm_fb_helper_set_suspend(struct drm_fb_helper *fb_helper, bool suspend)
1261{
1262 if (fb_helper && fb_helper->fbdev)
1263 fb_set_suspend(fb_helper->fbdev, suspend);
1264}
1265EXPORT_SYMBOL(drm_fb_helper_set_suspend);
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283void drm_fb_helper_set_suspend_unlocked(struct drm_fb_helper *fb_helper,
1284 bool suspend)
1285{
1286 if (!fb_helper || !fb_helper->fbdev)
1287 return;
1288
1289
1290 flush_work(&fb_helper->resume_work);
1291
1292 if (suspend) {
1293 if (fb_helper->fbdev->state != FBINFO_STATE_RUNNING)
1294 return;
1295
1296 console_lock();
1297
1298 } else {
1299 if (fb_helper->fbdev->state == FBINFO_STATE_RUNNING)
1300 return;
1301
1302 if (!console_trylock()) {
1303 schedule_work(&fb_helper->resume_work);
1304 return;
1305 }
1306 }
1307
1308 fb_set_suspend(fb_helper->fbdev, suspend);
1309 console_unlock();
1310}
1311EXPORT_SYMBOL(drm_fb_helper_set_suspend_unlocked);
1312
1313static int setcmap_pseudo_palette(struct fb_cmap *cmap, struct fb_info *info)
1314{
1315 u32 *palette = (u32 *)info->pseudo_palette;
1316 int i;
1317
1318 if (cmap->start + cmap->len > 16)
1319 return -EINVAL;
1320
1321 for (i = 0; i < cmap->len; ++i) {
1322 u16 red = cmap->red[i];
1323 u16 green = cmap->green[i];
1324 u16 blue = cmap->blue[i];
1325 u32 value;
1326
1327 red >>= 16 - info->var.red.length;
1328 green >>= 16 - info->var.green.length;
1329 blue >>= 16 - info->var.blue.length;
1330 value = (red << info->var.red.offset) |
1331 (green << info->var.green.offset) |
1332 (blue << info->var.blue.offset);
1333 if (info->var.transp.length > 0) {
1334 u32 mask = (1 << info->var.transp.length) - 1;
1335
1336 mask <<= info->var.transp.offset;
1337 value |= mask;
1338 }
1339 palette[cmap->start + i] = value;
1340 }
1341
1342 return 0;
1343}
1344
1345static int setcmap_legacy(struct fb_cmap *cmap, struct fb_info *info)
1346{
1347 struct drm_fb_helper *fb_helper = info->par;
1348 struct drm_crtc *crtc;
1349 u16 *r, *g, *b;
1350 int i, ret = 0;
1351
1352 drm_modeset_lock_all(fb_helper->dev);
1353 for (i = 0; i < fb_helper->crtc_count; i++) {
1354 crtc = fb_helper->crtc_info[i].mode_set.crtc;
1355 if (!crtc->funcs->gamma_set || !crtc->gamma_size)
1356 return -EINVAL;
1357
1358 if (cmap->start + cmap->len > crtc->gamma_size)
1359 return -EINVAL;
1360
1361 r = crtc->gamma_store;
1362 g = r + crtc->gamma_size;
1363 b = g + crtc->gamma_size;
1364
1365 memcpy(r + cmap->start, cmap->red, cmap->len * sizeof(*r));
1366 memcpy(g + cmap->start, cmap->green, cmap->len * sizeof(*g));
1367 memcpy(b + cmap->start, cmap->blue, cmap->len * sizeof(*b));
1368
1369 ret = crtc->funcs->gamma_set(crtc, r, g, b,
1370 crtc->gamma_size, NULL);
1371 if (ret)
1372 return ret;
1373 }
1374 drm_modeset_unlock_all(fb_helper->dev);
1375
1376 return ret;
1377}
1378
1379static struct drm_property_blob *setcmap_new_gamma_lut(struct drm_crtc *crtc,
1380 struct fb_cmap *cmap)
1381{
1382 struct drm_device *dev = crtc->dev;
1383 struct drm_property_blob *gamma_lut;
1384 struct drm_color_lut *lut;
1385 int size = crtc->gamma_size;
1386 int i;
1387
1388 if (!size || cmap->start + cmap->len > size)
1389 return ERR_PTR(-EINVAL);
1390
1391 gamma_lut = drm_property_create_blob(dev, sizeof(*lut) * size, NULL);
1392 if (IS_ERR(gamma_lut))
1393 return gamma_lut;
1394
1395 lut = gamma_lut->data;
1396 if (cmap->start || cmap->len != size) {
1397 u16 *r = crtc->gamma_store;
1398 u16 *g = r + crtc->gamma_size;
1399 u16 *b = g + crtc->gamma_size;
1400
1401 for (i = 0; i < cmap->start; i++) {
1402 lut[i].red = r[i];
1403 lut[i].green = g[i];
1404 lut[i].blue = b[i];
1405 }
1406 for (i = cmap->start + cmap->len; i < size; i++) {
1407 lut[i].red = r[i];
1408 lut[i].green = g[i];
1409 lut[i].blue = b[i];
1410 }
1411 }
1412
1413 for (i = 0; i < cmap->len; i++) {
1414 lut[cmap->start + i].red = cmap->red[i];
1415 lut[cmap->start + i].green = cmap->green[i];
1416 lut[cmap->start + i].blue = cmap->blue[i];
1417 }
1418
1419 return gamma_lut;
1420}
1421
1422static int setcmap_atomic(struct fb_cmap *cmap, struct fb_info *info)
1423{
1424 struct drm_fb_helper *fb_helper = info->par;
1425 struct drm_device *dev = fb_helper->dev;
1426 struct drm_property_blob *gamma_lut = NULL;
1427 struct drm_modeset_acquire_ctx ctx;
1428 struct drm_crtc_state *crtc_state;
1429 struct drm_atomic_state *state;
1430 struct drm_crtc *crtc;
1431 u16 *r, *g, *b;
1432 int i, ret = 0;
1433 bool replaced;
1434
1435 drm_modeset_acquire_init(&ctx, 0);
1436
1437 state = drm_atomic_state_alloc(dev);
1438 if (!state) {
1439 ret = -ENOMEM;
1440 goto out_ctx;
1441 }
1442
1443 state->acquire_ctx = &ctx;
1444retry:
1445 for (i = 0; i < fb_helper->crtc_count; i++) {
1446 crtc = fb_helper->crtc_info[i].mode_set.crtc;
1447
1448 if (!gamma_lut)
1449 gamma_lut = setcmap_new_gamma_lut(crtc, cmap);
1450 if (IS_ERR(gamma_lut)) {
1451 ret = PTR_ERR(gamma_lut);
1452 gamma_lut = NULL;
1453 goto out_state;
1454 }
1455
1456 crtc_state = drm_atomic_get_crtc_state(state, crtc);
1457 if (IS_ERR(crtc_state)) {
1458 ret = PTR_ERR(crtc_state);
1459 goto out_state;
1460 }
1461
1462 replaced = drm_property_replace_blob(&crtc_state->degamma_lut,
1463 NULL);
1464 replaced |= drm_property_replace_blob(&crtc_state->ctm, NULL);
1465 replaced |= drm_property_replace_blob(&crtc_state->gamma_lut,
1466 gamma_lut);
1467 crtc_state->color_mgmt_changed |= replaced;
1468 }
1469
1470 ret = drm_atomic_commit(state);
1471 if (ret)
1472 goto out_state;
1473
1474 for (i = 0; i < fb_helper->crtc_count; i++) {
1475 crtc = fb_helper->crtc_info[i].mode_set.crtc;
1476
1477 r = crtc->gamma_store;
1478 g = r + crtc->gamma_size;
1479 b = g + crtc->gamma_size;
1480
1481 memcpy(r + cmap->start, cmap->red, cmap->len * sizeof(*r));
1482 memcpy(g + cmap->start, cmap->green, cmap->len * sizeof(*g));
1483 memcpy(b + cmap->start, cmap->blue, cmap->len * sizeof(*b));
1484 }
1485
1486out_state:
1487 if (ret == -EDEADLK)
1488 goto backoff;
1489
1490 drm_property_blob_put(gamma_lut);
1491 drm_atomic_state_put(state);
1492out_ctx:
1493 drm_modeset_drop_locks(&ctx);
1494 drm_modeset_acquire_fini(&ctx);
1495
1496 return ret;
1497
1498backoff:
1499 drm_atomic_state_clear(state);
1500 drm_modeset_backoff(&ctx);
1501 goto retry;
1502}
1503
1504
1505
1506
1507
1508
1509int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info)
1510{
1511 struct drm_fb_helper *fb_helper = info->par;
1512 int ret;
1513
1514 if (oops_in_progress)
1515 return -EBUSY;
1516
1517 mutex_lock(&fb_helper->lock);
1518
1519 if (!drm_fb_helper_is_bound(fb_helper)) {
1520 ret = -EBUSY;
1521 goto out;
1522 }
1523
1524 if (info->fix.visual == FB_VISUAL_TRUECOLOR)
1525 ret = setcmap_pseudo_palette(cmap, info);
1526 else if (drm_drv_uses_atomic_modeset(fb_helper->dev))
1527 ret = setcmap_atomic(cmap, info);
1528 else
1529 ret = setcmap_legacy(cmap, info);
1530
1531out:
1532 mutex_unlock(&fb_helper->lock);
1533
1534 return ret;
1535}
1536EXPORT_SYMBOL(drm_fb_helper_setcmap);
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547int drm_fb_helper_ioctl(struct fb_info *info, unsigned int cmd,
1548 unsigned long arg)
1549{
1550 struct drm_fb_helper *fb_helper = info->par;
1551 struct drm_mode_set *mode_set;
1552 struct drm_crtc *crtc;
1553 int ret = 0;
1554
1555 mutex_lock(&fb_helper->lock);
1556 if (!drm_fb_helper_is_bound(fb_helper)) {
1557 ret = -EBUSY;
1558 goto unlock;
1559 }
1560
1561 switch (cmd) {
1562 case FBIO_WAITFORVSYNC:
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579 mode_set = &fb_helper->crtc_info[0].mode_set;
1580 crtc = mode_set->crtc;
1581
1582
1583
1584
1585
1586
1587 ret = drm_crtc_vblank_get(crtc);
1588 if (!ret) {
1589 drm_crtc_wait_one_vblank(crtc);
1590 drm_crtc_vblank_put(crtc);
1591 }
1592
1593 ret = 0;
1594 goto unlock;
1595 default:
1596 ret = -ENOTTY;
1597 }
1598
1599unlock:
1600 mutex_unlock(&fb_helper->lock);
1601 return ret;
1602}
1603EXPORT_SYMBOL(drm_fb_helper_ioctl);
1604
1605static bool drm_fb_pixel_format_equal(const struct fb_var_screeninfo *var_1,
1606 const struct fb_var_screeninfo *var_2)
1607{
1608 return var_1->bits_per_pixel == var_2->bits_per_pixel &&
1609 var_1->grayscale == var_2->grayscale &&
1610 var_1->red.offset == var_2->red.offset &&
1611 var_1->red.length == var_2->red.length &&
1612 var_1->red.msb_right == var_2->red.msb_right &&
1613 var_1->green.offset == var_2->green.offset &&
1614 var_1->green.length == var_2->green.length &&
1615 var_1->green.msb_right == var_2->green.msb_right &&
1616 var_1->blue.offset == var_2->blue.offset &&
1617 var_1->blue.length == var_2->blue.length &&
1618 var_1->blue.msb_right == var_2->blue.msb_right &&
1619 var_1->transp.offset == var_2->transp.offset &&
1620 var_1->transp.length == var_2->transp.length &&
1621 var_1->transp.msb_right == var_2->transp.msb_right;
1622}
1623
1624static void drm_fb_helper_fill_pixel_fmt(struct fb_var_screeninfo *var,
1625 u8 depth)
1626{
1627 switch (depth) {
1628 case 8:
1629 var->red.offset = 0;
1630 var->green.offset = 0;
1631 var->blue.offset = 0;
1632 var->red.length = 8;
1633 var->green.length = 8;
1634 var->blue.length = 8;
1635 var->transp.offset = 0;
1636 var->transp.length = 0;
1637 break;
1638 case 15:
1639 var->red.offset = 10;
1640 var->green.offset = 5;
1641 var->blue.offset = 0;
1642 var->red.length = 5;
1643 var->green.length = 5;
1644 var->blue.length = 5;
1645 var->transp.offset = 15;
1646 var->transp.length = 1;
1647 break;
1648 case 16:
1649 var->red.offset = 11;
1650 var->green.offset = 5;
1651 var->blue.offset = 0;
1652 var->red.length = 5;
1653 var->green.length = 6;
1654 var->blue.length = 5;
1655 var->transp.offset = 0;
1656 break;
1657 case 24:
1658 var->red.offset = 16;
1659 var->green.offset = 8;
1660 var->blue.offset = 0;
1661 var->red.length = 8;
1662 var->green.length = 8;
1663 var->blue.length = 8;
1664 var->transp.offset = 0;
1665 var->transp.length = 0;
1666 break;
1667 case 32:
1668 var->red.offset = 16;
1669 var->green.offset = 8;
1670 var->blue.offset = 0;
1671 var->red.length = 8;
1672 var->green.length = 8;
1673 var->blue.length = 8;
1674 var->transp.offset = 24;
1675 var->transp.length = 8;
1676 break;
1677 default:
1678 break;
1679 }
1680}
1681
1682
1683
1684
1685
1686
1687int drm_fb_helper_check_var(struct fb_var_screeninfo *var,
1688 struct fb_info *info)
1689{
1690 struct drm_fb_helper *fb_helper = info->par;
1691 struct drm_framebuffer *fb = fb_helper->fb;
1692
1693 if (in_dbg_master())
1694 return -EINVAL;
1695
1696 if (var->pixclock != 0) {
1697 DRM_DEBUG("fbdev emulation doesn't support changing the pixel clock, value of pixclock is ignored\n");
1698 var->pixclock = 0;
1699 }
1700
1701 if ((drm_format_info_block_width(fb->format, 0) > 1) ||
1702 (drm_format_info_block_height(fb->format, 0) > 1))
1703 return -EINVAL;
1704
1705
1706
1707
1708
1709 if (var->bits_per_pixel != fb->format->cpp[0] * 8 ||
1710 var->xres > fb->width || var->yres > fb->height ||
1711 var->xres_virtual > fb->width || var->yres_virtual > fb->height) {
1712 DRM_DEBUG("fb requested width/height/bpp can't fit in current fb "
1713 "request %dx%d-%d (virtual %dx%d) > %dx%d-%d\n",
1714 var->xres, var->yres, var->bits_per_pixel,
1715 var->xres_virtual, var->yres_virtual,
1716 fb->width, fb->height, fb->format->cpp[0] * 8);
1717 return -EINVAL;
1718 }
1719
1720
1721
1722
1723
1724
1725 if (!var->red.offset && !var->green.offset &&
1726 !var->blue.offset && !var->transp.offset &&
1727 !var->red.length && !var->green.length &&
1728 !var->blue.length && !var->transp.length &&
1729 !var->red.msb_right && !var->green.msb_right &&
1730 !var->blue.msb_right && !var->transp.msb_right) {
1731 drm_fb_helper_fill_pixel_fmt(var, fb->format->depth);
1732 }
1733
1734
1735
1736
1737
1738 if (!drm_fb_pixel_format_equal(var, &info->var)) {
1739 DRM_DEBUG("fbdev emulation doesn't support changing the pixel format\n");
1740 return -EINVAL;
1741 }
1742
1743 return 0;
1744}
1745EXPORT_SYMBOL(drm_fb_helper_check_var);
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755int drm_fb_helper_set_par(struct fb_info *info)
1756{
1757 struct drm_fb_helper *fb_helper = info->par;
1758 struct fb_var_screeninfo *var = &info->var;
1759
1760 if (oops_in_progress)
1761 return -EBUSY;
1762
1763 if (var->pixclock != 0) {
1764 DRM_ERROR("PIXEL CLOCK SET\n");
1765 return -EINVAL;
1766 }
1767
1768 drm_fb_helper_restore_fbdev_mode_unlocked(fb_helper);
1769
1770 return 0;
1771}
1772EXPORT_SYMBOL(drm_fb_helper_set_par);
1773
1774static void pan_set(struct drm_fb_helper *fb_helper, int x, int y)
1775{
1776 int i;
1777
1778 for (i = 0; i < fb_helper->crtc_count; i++) {
1779 struct drm_mode_set *mode_set;
1780
1781 mode_set = &fb_helper->crtc_info[i].mode_set;
1782
1783 mode_set->x = x;
1784 mode_set->y = y;
1785 }
1786}
1787
1788static int pan_display_atomic(struct fb_var_screeninfo *var,
1789 struct fb_info *info)
1790{
1791 struct drm_fb_helper *fb_helper = info->par;
1792 int ret;
1793
1794 pan_set(fb_helper, var->xoffset, var->yoffset);
1795
1796 ret = restore_fbdev_mode_atomic(fb_helper, true);
1797 if (!ret) {
1798 info->var.xoffset = var->xoffset;
1799 info->var.yoffset = var->yoffset;
1800 } else
1801 pan_set(fb_helper, info->var.xoffset, info->var.yoffset);
1802
1803 return ret;
1804}
1805
1806static int pan_display_legacy(struct fb_var_screeninfo *var,
1807 struct fb_info *info)
1808{
1809 struct drm_fb_helper *fb_helper = info->par;
1810 struct drm_mode_set *modeset;
1811 int ret = 0;
1812 int i;
1813
1814 drm_modeset_lock_all(fb_helper->dev);
1815 for (i = 0; i < fb_helper->crtc_count; i++) {
1816 modeset = &fb_helper->crtc_info[i].mode_set;
1817
1818 modeset->x = var->xoffset;
1819 modeset->y = var->yoffset;
1820
1821 if (modeset->num_connectors) {
1822 ret = drm_mode_set_config_internal(modeset);
1823 if (!ret) {
1824 info->var.xoffset = var->xoffset;
1825 info->var.yoffset = var->yoffset;
1826 }
1827 }
1828 }
1829 drm_modeset_unlock_all(fb_helper->dev);
1830
1831 return ret;
1832}
1833
1834
1835
1836
1837
1838
1839int drm_fb_helper_pan_display(struct fb_var_screeninfo *var,
1840 struct fb_info *info)
1841{
1842 struct drm_fb_helper *fb_helper = info->par;
1843 struct drm_device *dev = fb_helper->dev;
1844 int ret;
1845
1846 if (oops_in_progress)
1847 return -EBUSY;
1848
1849 mutex_lock(&fb_helper->lock);
1850 if (!drm_fb_helper_is_bound(fb_helper)) {
1851 mutex_unlock(&fb_helper->lock);
1852 return -EBUSY;
1853 }
1854
1855 if (drm_drv_uses_atomic_modeset(dev))
1856 ret = pan_display_atomic(var, info);
1857 else
1858 ret = pan_display_legacy(var, info);
1859 mutex_unlock(&fb_helper->lock);
1860
1861 return ret;
1862}
1863EXPORT_SYMBOL(drm_fb_helper_pan_display);
1864
1865
1866
1867
1868
1869static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper,
1870 int preferred_bpp)
1871{
1872 int ret = 0;
1873 int crtc_count = 0;
1874 int i;
1875 struct drm_fb_helper_surface_size sizes;
1876 int best_depth = 0;
1877
1878 memset(&sizes, 0, sizeof(struct drm_fb_helper_surface_size));
1879 sizes.surface_depth = 24;
1880 sizes.surface_bpp = 32;
1881 sizes.fb_width = (u32)-1;
1882 sizes.fb_height = (u32)-1;
1883
1884
1885
1886
1887
1888 if (preferred_bpp != sizes.surface_bpp)
1889 sizes.surface_depth = sizes.surface_bpp = preferred_bpp;
1890
1891 drm_fb_helper_for_each_connector(fb_helper, i) {
1892 struct drm_fb_helper_connector *fb_helper_conn = fb_helper->connector_info[i];
1893 struct drm_cmdline_mode *cmdline_mode;
1894
1895 cmdline_mode = &fb_helper_conn->connector->cmdline_mode;
1896
1897 if (cmdline_mode->bpp_specified) {
1898 switch (cmdline_mode->bpp) {
1899 case 8:
1900 sizes.surface_depth = sizes.surface_bpp = 8;
1901 break;
1902 case 15:
1903 sizes.surface_depth = 15;
1904 sizes.surface_bpp = 16;
1905 break;
1906 case 16:
1907 sizes.surface_depth = sizes.surface_bpp = 16;
1908 break;
1909 case 24:
1910 sizes.surface_depth = sizes.surface_bpp = 24;
1911 break;
1912 case 32:
1913 sizes.surface_depth = 24;
1914 sizes.surface_bpp = 32;
1915 break;
1916 }
1917 break;
1918 }
1919 }
1920
1921
1922
1923
1924
1925
1926 for (i = 0; i < fb_helper->crtc_count; i++) {
1927 struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set;
1928 struct drm_crtc *crtc = mode_set->crtc;
1929 struct drm_plane *plane = crtc->primary;
1930 int j;
1931
1932 DRM_DEBUG("test CRTC %d primary plane\n", i);
1933
1934 for (j = 0; j < plane->format_count; j++) {
1935 const struct drm_format_info *fmt;
1936
1937 fmt = drm_format_info(plane->format_types[j]);
1938
1939
1940
1941
1942
1943
1944
1945
1946 if (fmt->depth == 0)
1947 continue;
1948
1949
1950 if (fmt->depth == sizes.surface_depth) {
1951 best_depth = fmt->depth;
1952 break;
1953 }
1954
1955
1956 if (fmt->depth > sizes.surface_depth)
1957 continue;
1958
1959
1960 if (fmt->depth > best_depth)
1961 best_depth = fmt->depth;
1962 }
1963 }
1964 if (sizes.surface_depth != best_depth && best_depth) {
1965 DRM_INFO("requested bpp %d, scaled depth down to %d",
1966 sizes.surface_bpp, best_depth);
1967 sizes.surface_depth = best_depth;
1968 }
1969
1970
1971 crtc_count = 0;
1972 for (i = 0; i < fb_helper->crtc_count; i++) {
1973 struct drm_display_mode *desired_mode;
1974 struct drm_mode_set *mode_set;
1975 int x, y, j;
1976
1977
1978
1979
1980 bool lastv = true, lasth = true;
1981
1982 desired_mode = fb_helper->crtc_info[i].desired_mode;
1983 mode_set = &fb_helper->crtc_info[i].mode_set;
1984
1985 if (!desired_mode)
1986 continue;
1987
1988 crtc_count++;
1989
1990 x = fb_helper->crtc_info[i].x;
1991 y = fb_helper->crtc_info[i].y;
1992
1993 sizes.surface_width = max_t(u32, desired_mode->hdisplay + x, sizes.surface_width);
1994 sizes.surface_height = max_t(u32, desired_mode->vdisplay + y, sizes.surface_height);
1995
1996 for (j = 0; j < mode_set->num_connectors; j++) {
1997 struct drm_connector *connector = mode_set->connectors[j];
1998
1999 if (connector->has_tile) {
2000 lasth = (connector->tile_h_loc == (connector->num_h_tile - 1));
2001 lastv = (connector->tile_v_loc == (connector->num_v_tile - 1));
2002
2003 break;
2004 }
2005 }
2006
2007 if (lasth)
2008 sizes.fb_width = min_t(u32, desired_mode->hdisplay + x, sizes.fb_width);
2009 if (lastv)
2010 sizes.fb_height = min_t(u32, desired_mode->vdisplay + y, sizes.fb_height);
2011 }
2012
2013 if (crtc_count == 0 || sizes.fb_width == -1 || sizes.fb_height == -1) {
2014 DRM_INFO("Cannot find any crtc or sizes\n");
2015
2016
2017 if (!fb_helper->deferred_setup && !READ_ONCE(fb_helper->dev->master))
2018 restore_fbdev_mode(fb_helper);
2019 return -EAGAIN;
2020 }
2021
2022
2023 sizes.surface_height *= drm_fbdev_overalloc;
2024 sizes.surface_height /= 100;
2025
2026
2027 ret = (*fb_helper->funcs->fb_probe)(fb_helper, &sizes);
2028 if (ret < 0)
2029 return ret;
2030
2031 strcpy(fb_helper->fb->comm, "[fbcon]");
2032 return 0;
2033}
2034
2035static void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch,
2036 uint32_t depth)
2037{
2038 info->fix.type = FB_TYPE_PACKED_PIXELS;
2039 info->fix.visual = depth == 8 ? FB_VISUAL_PSEUDOCOLOR :
2040 FB_VISUAL_TRUECOLOR;
2041 info->fix.mmio_start = 0;
2042 info->fix.mmio_len = 0;
2043 info->fix.type_aux = 0;
2044 info->fix.xpanstep = 1;
2045 info->fix.ypanstep = 1;
2046 info->fix.ywrapstep = 0;
2047 info->fix.accel = FB_ACCEL_NONE;
2048
2049 info->fix.line_length = pitch;
2050}
2051
2052static void drm_fb_helper_fill_var(struct fb_info *info,
2053 struct drm_fb_helper *fb_helper,
2054 uint32_t fb_width, uint32_t fb_height)
2055{
2056 struct drm_framebuffer *fb = fb_helper->fb;
2057
2058 WARN_ON((drm_format_info_block_width(fb->format, 0) > 1) ||
2059 (drm_format_info_block_height(fb->format, 0) > 1));
2060 info->pseudo_palette = fb_helper->pseudo_palette;
2061 info->var.xres_virtual = fb->width;
2062 info->var.yres_virtual = fb->height;
2063 info->var.bits_per_pixel = fb->format->cpp[0] * 8;
2064 info->var.accel_flags = FB_ACCELF_TEXT;
2065 info->var.xoffset = 0;
2066 info->var.yoffset = 0;
2067 info->var.activate = FB_ACTIVATE_NOW;
2068
2069 drm_fb_helper_fill_pixel_fmt(&info->var, fb->format->depth);
2070
2071 info->var.xres = fb_width;
2072 info->var.yres = fb_height;
2073}
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088void drm_fb_helper_fill_info(struct fb_info *info,
2089 struct drm_fb_helper *fb_helper,
2090 struct drm_fb_helper_surface_size *sizes)
2091{
2092 struct drm_framebuffer *fb = fb_helper->fb;
2093
2094 drm_fb_helper_fill_fix(info, fb->pitches[0], fb->format->depth);
2095 drm_fb_helper_fill_var(info, fb_helper,
2096 sizes->fb_width, sizes->fb_height);
2097
2098 info->par = fb_helper;
2099 snprintf(info->fix.id, sizeof(info->fix.id), "%sdrmfb",
2100 fb_helper->dev->driver->name);
2101
2102}
2103EXPORT_SYMBOL(drm_fb_helper_fill_info);
2104
2105static int drm_fb_helper_probe_connector_modes(struct drm_fb_helper *fb_helper,
2106 uint32_t maxX,
2107 uint32_t maxY)
2108{
2109 struct drm_connector *connector;
2110 int i, count = 0;
2111
2112 drm_fb_helper_for_each_connector(fb_helper, i) {
2113 connector = fb_helper->connector_info[i]->connector;
2114 count += connector->funcs->fill_modes(connector, maxX, maxY);
2115 }
2116
2117 return count;
2118}
2119
2120struct drm_display_mode *drm_has_preferred_mode(struct drm_fb_helper_connector *fb_connector, int width, int height)
2121{
2122 struct drm_display_mode *mode;
2123
2124 list_for_each_entry(mode, &fb_connector->connector->modes, head) {
2125 if (mode->hdisplay > width ||
2126 mode->vdisplay > height)
2127 continue;
2128 if (mode->type & DRM_MODE_TYPE_PREFERRED)
2129 return mode;
2130 }
2131 return NULL;
2132}
2133EXPORT_SYMBOL(drm_has_preferred_mode);
2134
2135static bool drm_has_cmdline_mode(struct drm_fb_helper_connector *fb_connector)
2136{
2137 return fb_connector->connector->cmdline_mode.specified;
2138}
2139
2140struct drm_display_mode *drm_pick_cmdline_mode(struct drm_fb_helper_connector *fb_helper_conn)
2141{
2142 struct drm_cmdline_mode *cmdline_mode;
2143 struct drm_display_mode *mode;
2144 bool prefer_non_interlace;
2145
2146 cmdline_mode = &fb_helper_conn->connector->cmdline_mode;
2147 if (cmdline_mode->specified == false)
2148 return NULL;
2149
2150
2151
2152
2153 if (cmdline_mode->rb || cmdline_mode->margins)
2154 goto create_mode;
2155
2156 prefer_non_interlace = !cmdline_mode->interlace;
2157again:
2158 list_for_each_entry(mode, &fb_helper_conn->connector->modes, head) {
2159
2160 if (mode->hdisplay != cmdline_mode->xres ||
2161 mode->vdisplay != cmdline_mode->yres)
2162 continue;
2163
2164 if (cmdline_mode->refresh_specified) {
2165 if (mode->vrefresh != cmdline_mode->refresh)
2166 continue;
2167 }
2168
2169 if (cmdline_mode->interlace) {
2170 if (!(mode->flags & DRM_MODE_FLAG_INTERLACE))
2171 continue;
2172 } else if (prefer_non_interlace) {
2173 if (mode->flags & DRM_MODE_FLAG_INTERLACE)
2174 continue;
2175 }
2176 return mode;
2177 }
2178
2179 if (prefer_non_interlace) {
2180 prefer_non_interlace = false;
2181 goto again;
2182 }
2183
2184create_mode:
2185 mode = drm_mode_create_from_cmdline_mode(fb_helper_conn->connector->dev,
2186 cmdline_mode);
2187 list_add(&mode->head, &fb_helper_conn->connector->modes);
2188 return mode;
2189}
2190EXPORT_SYMBOL(drm_pick_cmdline_mode);
2191
2192static bool drm_connector_enabled(struct drm_connector *connector, bool strict)
2193{
2194 bool enable;
2195
2196 if (connector->display_info.non_desktop)
2197 return false;
2198
2199 if (strict)
2200 enable = connector->status == connector_status_connected;
2201 else
2202 enable = connector->status != connector_status_disconnected;
2203
2204 return enable;
2205}
2206
2207static void drm_enable_connectors(struct drm_fb_helper *fb_helper,
2208 bool *enabled)
2209{
2210 bool any_enabled = false;
2211 struct drm_connector *connector;
2212 int i = 0;
2213
2214 drm_fb_helper_for_each_connector(fb_helper, i) {
2215 connector = fb_helper->connector_info[i]->connector;
2216 enabled[i] = drm_connector_enabled(connector, true);
2217 DRM_DEBUG_KMS("connector %d enabled? %s\n", connector->base.id,
2218 connector->display_info.non_desktop ? "non desktop" : enabled[i] ? "yes" : "no");
2219
2220 any_enabled |= enabled[i];
2221 }
2222
2223 if (any_enabled)
2224 return;
2225
2226 drm_fb_helper_for_each_connector(fb_helper, i) {
2227 connector = fb_helper->connector_info[i]->connector;
2228 enabled[i] = drm_connector_enabled(connector, false);
2229 }
2230}
2231
2232static bool drm_target_cloned(struct drm_fb_helper *fb_helper,
2233 struct drm_display_mode **modes,
2234 struct drm_fb_offset *offsets,
2235 bool *enabled, int width, int height)
2236{
2237 int count, i, j;
2238 bool can_clone = false;
2239 struct drm_fb_helper_connector *fb_helper_conn;
2240 struct drm_display_mode *dmt_mode, *mode;
2241
2242
2243 if (fb_helper->crtc_count > 1)
2244 return false;
2245
2246 count = 0;
2247 drm_fb_helper_for_each_connector(fb_helper, i) {
2248 if (enabled[i])
2249 count++;
2250 }
2251
2252
2253 if (count <= 1)
2254 return false;
2255
2256
2257 can_clone = true;
2258 drm_fb_helper_for_each_connector(fb_helper, i) {
2259 if (!enabled[i])
2260 continue;
2261 fb_helper_conn = fb_helper->connector_info[i];
2262 modes[i] = drm_pick_cmdline_mode(fb_helper_conn);
2263 if (!modes[i]) {
2264 can_clone = false;
2265 break;
2266 }
2267 for (j = 0; j < i; j++) {
2268 if (!enabled[j])
2269 continue;
2270 if (!drm_mode_match(modes[j], modes[i],
2271 DRM_MODE_MATCH_TIMINGS |
2272 DRM_MODE_MATCH_CLOCK |
2273 DRM_MODE_MATCH_FLAGS |
2274 DRM_MODE_MATCH_3D_FLAGS))
2275 can_clone = false;
2276 }
2277 }
2278
2279 if (can_clone) {
2280 DRM_DEBUG_KMS("can clone using command line\n");
2281 return true;
2282 }
2283
2284
2285 can_clone = true;
2286 dmt_mode = drm_mode_find_dmt(fb_helper->dev, 1024, 768, 60, false);
2287
2288 drm_fb_helper_for_each_connector(fb_helper, i) {
2289 if (!enabled[i])
2290 continue;
2291
2292 fb_helper_conn = fb_helper->connector_info[i];
2293 list_for_each_entry(mode, &fb_helper_conn->connector->modes, head) {
2294 if (drm_mode_match(mode, dmt_mode,
2295 DRM_MODE_MATCH_TIMINGS |
2296 DRM_MODE_MATCH_CLOCK |
2297 DRM_MODE_MATCH_FLAGS |
2298 DRM_MODE_MATCH_3D_FLAGS))
2299 modes[i] = mode;
2300 }
2301 if (!modes[i])
2302 can_clone = false;
2303 }
2304
2305 if (can_clone) {
2306 DRM_DEBUG_KMS("can clone using 1024x768\n");
2307 return true;
2308 }
2309 DRM_INFO("kms: can't enable cloning when we probably wanted to.\n");
2310 return false;
2311}
2312
2313static int drm_get_tile_offsets(struct drm_fb_helper *fb_helper,
2314 struct drm_display_mode **modes,
2315 struct drm_fb_offset *offsets,
2316 int idx,
2317 int h_idx, int v_idx)
2318{
2319 struct drm_fb_helper_connector *fb_helper_conn;
2320 int i;
2321 int hoffset = 0, voffset = 0;
2322
2323 drm_fb_helper_for_each_connector(fb_helper, i) {
2324 fb_helper_conn = fb_helper->connector_info[i];
2325 if (!fb_helper_conn->connector->has_tile)
2326 continue;
2327
2328 if (!modes[i] && (h_idx || v_idx)) {
2329 DRM_DEBUG_KMS("no modes for connector tiled %d %d\n", i,
2330 fb_helper_conn->connector->base.id);
2331 continue;
2332 }
2333 if (fb_helper_conn->connector->tile_h_loc < h_idx)
2334 hoffset += modes[i]->hdisplay;
2335
2336 if (fb_helper_conn->connector->tile_v_loc < v_idx)
2337 voffset += modes[i]->vdisplay;
2338 }
2339 offsets[idx].x = hoffset;
2340 offsets[idx].y = voffset;
2341 DRM_DEBUG_KMS("returned %d %d for %d %d\n", hoffset, voffset, h_idx, v_idx);
2342 return 0;
2343}
2344
2345static bool drm_target_preferred(struct drm_fb_helper *fb_helper,
2346 struct drm_display_mode **modes,
2347 struct drm_fb_offset *offsets,
2348 bool *enabled, int width, int height)
2349{
2350 struct drm_fb_helper_connector *fb_helper_conn;
2351 const u64 mask = BIT_ULL(fb_helper->connector_count) - 1;
2352 u64 conn_configured = 0;
2353 int tile_pass = 0;
2354 int i;
2355
2356retry:
2357 drm_fb_helper_for_each_connector(fb_helper, i) {
2358 fb_helper_conn = fb_helper->connector_info[i];
2359
2360 if (conn_configured & BIT_ULL(i))
2361 continue;
2362
2363 if (enabled[i] == false) {
2364 conn_configured |= BIT_ULL(i);
2365 continue;
2366 }
2367
2368
2369 if (tile_pass == 0 && fb_helper_conn->connector->has_tile)
2370 continue;
2371
2372 if (tile_pass == 1) {
2373 if (fb_helper_conn->connector->tile_h_loc != 0 ||
2374 fb_helper_conn->connector->tile_v_loc != 0)
2375 continue;
2376
2377 } else {
2378 if (fb_helper_conn->connector->tile_h_loc != tile_pass - 1 &&
2379 fb_helper_conn->connector->tile_v_loc != tile_pass - 1)
2380
2381 continue;
2382
2383
2384
2385
2386
2387 drm_get_tile_offsets(fb_helper, modes, offsets,
2388 i, fb_helper_conn->connector->tile_h_loc, fb_helper_conn->connector->tile_v_loc);
2389 }
2390 DRM_DEBUG_KMS("looking for cmdline mode on connector %d\n",
2391 fb_helper_conn->connector->base.id);
2392
2393
2394 modes[i] = drm_pick_cmdline_mode(fb_helper_conn);
2395 if (!modes[i]) {
2396 DRM_DEBUG_KMS("looking for preferred mode on connector %d %d\n",
2397 fb_helper_conn->connector->base.id, fb_helper_conn->connector->tile_group ? fb_helper_conn->connector->tile_group->id : 0);
2398 modes[i] = drm_has_preferred_mode(fb_helper_conn, width, height);
2399 }
2400
2401 if (!modes[i] && !list_empty(&fb_helper_conn->connector->modes)) {
2402 list_for_each_entry(modes[i], &fb_helper_conn->connector->modes, head)
2403 break;
2404 }
2405 DRM_DEBUG_KMS("found mode %s\n", modes[i] ? modes[i]->name :
2406 "none");
2407 conn_configured |= BIT_ULL(i);
2408 }
2409
2410 if ((conn_configured & mask) != mask) {
2411 tile_pass++;
2412 goto retry;
2413 }
2414 return true;
2415}
2416
2417static bool connector_has_possible_crtc(struct drm_connector *connector,
2418 struct drm_crtc *crtc)
2419{
2420 struct drm_encoder *encoder;
2421 int i;
2422
2423 drm_connector_for_each_possible_encoder(connector, encoder, i) {
2424 if (encoder->possible_crtcs & drm_crtc_mask(crtc))
2425 return true;
2426 }
2427
2428 return false;
2429}
2430
2431static int drm_pick_crtcs(struct drm_fb_helper *fb_helper,
2432 struct drm_fb_helper_crtc **best_crtcs,
2433 struct drm_display_mode **modes,
2434 int n, int width, int height)
2435{
2436 int c, o;
2437 struct drm_connector *connector;
2438 int my_score, best_score, score;
2439 struct drm_fb_helper_crtc **crtcs, *crtc;
2440 struct drm_fb_helper_connector *fb_helper_conn;
2441
2442 if (n == fb_helper->connector_count)
2443 return 0;
2444
2445 fb_helper_conn = fb_helper->connector_info[n];
2446 connector = fb_helper_conn->connector;
2447
2448 best_crtcs[n] = NULL;
2449 best_score = drm_pick_crtcs(fb_helper, best_crtcs, modes, n+1, width, height);
2450 if (modes[n] == NULL)
2451 return best_score;
2452
2453 crtcs = kcalloc(fb_helper->connector_count,
2454 sizeof(struct drm_fb_helper_crtc *), GFP_KERNEL);
2455 if (!crtcs)
2456 return best_score;
2457
2458 my_score = 1;
2459 if (connector->status == connector_status_connected)
2460 my_score++;
2461 if (drm_has_cmdline_mode(fb_helper_conn))
2462 my_score++;
2463 if (drm_has_preferred_mode(fb_helper_conn, width, height))
2464 my_score++;
2465
2466
2467
2468
2469
2470 for (c = 0; c < fb_helper->crtc_count; c++) {
2471 crtc = &fb_helper->crtc_info[c];
2472
2473 if (!connector_has_possible_crtc(connector,
2474 crtc->mode_set.crtc))
2475 continue;
2476
2477 for (o = 0; o < n; o++)
2478 if (best_crtcs[o] == crtc)
2479 break;
2480
2481 if (o < n) {
2482
2483 if (fb_helper->crtc_count > 1)
2484 continue;
2485
2486 if (!drm_mode_equal(modes[o], modes[n]))
2487 continue;
2488 }
2489
2490 crtcs[n] = crtc;
2491 memcpy(crtcs, best_crtcs, n * sizeof(struct drm_fb_helper_crtc *));
2492 score = my_score + drm_pick_crtcs(fb_helper, crtcs, modes, n + 1,
2493 width, height);
2494 if (score > best_score) {
2495 best_score = score;
2496 memcpy(best_crtcs, crtcs,
2497 fb_helper->connector_count *
2498 sizeof(struct drm_fb_helper_crtc *));
2499 }
2500 }
2501
2502 kfree(crtcs);
2503 return best_score;
2504}
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515static void drm_setup_crtc_rotation(struct drm_fb_helper *fb_helper,
2516 struct drm_fb_helper_crtc *fb_crtc,
2517 struct drm_connector *connector)
2518{
2519 struct drm_plane *plane = fb_crtc->mode_set.crtc->primary;
2520 uint64_t valid_mask = 0;
2521 int i, rotation;
2522
2523 fb_crtc->rotation = DRM_MODE_ROTATE_0;
2524
2525 switch (connector->display_info.panel_orientation) {
2526 case DRM_MODE_PANEL_ORIENTATION_BOTTOM_UP:
2527 rotation = DRM_MODE_ROTATE_180;
2528 break;
2529 case DRM_MODE_PANEL_ORIENTATION_LEFT_UP:
2530 rotation = DRM_MODE_ROTATE_90;
2531 break;
2532 case DRM_MODE_PANEL_ORIENTATION_RIGHT_UP:
2533 rotation = DRM_MODE_ROTATE_270;
2534 break;
2535 default:
2536 rotation = DRM_MODE_ROTATE_0;
2537 }
2538
2539
2540
2541
2542
2543
2544 if (rotation != DRM_MODE_ROTATE_180 || !plane->rotation_property) {
2545 fb_helper->sw_rotations |= rotation;
2546 return;
2547 }
2548
2549 for (i = 0; i < plane->rotation_property->num_values; i++)
2550 valid_mask |= (1ULL << plane->rotation_property->values[i]);
2551
2552 if (!(rotation & valid_mask)) {
2553 fb_helper->sw_rotations |= rotation;
2554 return;
2555 }
2556
2557 fb_crtc->rotation = rotation;
2558
2559 fb_helper->sw_rotations |= DRM_MODE_ROTATE_0;
2560}
2561
2562static struct drm_fb_helper_crtc *
2563drm_fb_helper_crtc(struct drm_fb_helper *fb_helper, struct drm_crtc *crtc)
2564{
2565 int i;
2566
2567 for (i = 0; i < fb_helper->crtc_count; i++)
2568 if (fb_helper->crtc_info[i].mode_set.crtc == crtc)
2569 return &fb_helper->crtc_info[i];
2570
2571 return NULL;
2572}
2573
2574
2575static bool drm_fb_helper_firmware_config(struct drm_fb_helper *fb_helper,
2576 struct drm_fb_helper_crtc **crtcs,
2577 struct drm_display_mode **modes,
2578 struct drm_fb_offset *offsets,
2579 bool *enabled, int width, int height)
2580{
2581 struct drm_device *dev = fb_helper->dev;
2582 unsigned int count = min(fb_helper->connector_count, BITS_PER_LONG);
2583 unsigned long conn_configured, conn_seq, mask;
2584 int i, j;
2585 bool *save_enabled;
2586 bool fallback = true, ret = true;
2587 int num_connectors_enabled = 0;
2588 int num_connectors_detected = 0;
2589 struct drm_modeset_acquire_ctx ctx;
2590
2591 if (!drm_drv_uses_atomic_modeset(dev))
2592 return false;
2593
2594 save_enabled = kcalloc(count, sizeof(bool), GFP_KERNEL);
2595 if (!save_enabled)
2596 return false;
2597
2598 drm_modeset_acquire_init(&ctx, 0);
2599
2600 while (drm_modeset_lock_all_ctx(dev, &ctx) != 0)
2601 drm_modeset_backoff(&ctx);
2602
2603 memcpy(save_enabled, enabled, count);
2604 mask = GENMASK(count - 1, 0);
2605 conn_configured = 0;
2606retry:
2607 conn_seq = conn_configured;
2608 for (i = 0; i < count; i++) {
2609 struct drm_fb_helper_connector *fb_conn;
2610 struct drm_connector *connector;
2611 struct drm_encoder *encoder;
2612 struct drm_fb_helper_crtc *new_crtc;
2613
2614 fb_conn = fb_helper->connector_info[i];
2615 connector = fb_conn->connector;
2616
2617 if (conn_configured & BIT(i))
2618 continue;
2619
2620 if (conn_seq == 0 && !connector->has_tile)
2621 continue;
2622
2623 if (connector->status == connector_status_connected)
2624 num_connectors_detected++;
2625
2626 if (!enabled[i]) {
2627 DRM_DEBUG_KMS("connector %s not enabled, skipping\n",
2628 connector->name);
2629 conn_configured |= BIT(i);
2630 continue;
2631 }
2632
2633 if (connector->force == DRM_FORCE_OFF) {
2634 DRM_DEBUG_KMS("connector %s is disabled by user, skipping\n",
2635 connector->name);
2636 enabled[i] = false;
2637 continue;
2638 }
2639
2640 encoder = connector->state->best_encoder;
2641 if (!encoder || WARN_ON(!connector->state->crtc)) {
2642 if (connector->force > DRM_FORCE_OFF)
2643 goto bail;
2644
2645 DRM_DEBUG_KMS("connector %s has no encoder or crtc, skipping\n",
2646 connector->name);
2647 enabled[i] = false;
2648 conn_configured |= BIT(i);
2649 continue;
2650 }
2651
2652 num_connectors_enabled++;
2653
2654 new_crtc = drm_fb_helper_crtc(fb_helper, connector->state->crtc);
2655
2656
2657
2658
2659
2660
2661 for (j = 0; j < count; j++) {
2662 if (crtcs[j] == new_crtc) {
2663 DRM_DEBUG_KMS("fallback: cloned configuration\n");
2664 goto bail;
2665 }
2666 }
2667
2668 DRM_DEBUG_KMS("looking for cmdline mode on connector %s\n",
2669 connector->name);
2670
2671
2672 modes[i] = drm_pick_cmdline_mode(fb_conn);
2673
2674
2675 if (!modes[i]) {
2676 DRM_DEBUG_KMS("looking for preferred mode on connector %s %d\n",
2677 connector->name, connector->has_tile);
2678 modes[i] = drm_has_preferred_mode(fb_conn, width,
2679 height);
2680 }
2681
2682
2683 if (!modes[i] && !list_empty(&connector->modes)) {
2684 DRM_DEBUG_KMS("using first mode listed on connector %s\n",
2685 connector->name);
2686 modes[i] = list_first_entry(&connector->modes,
2687 struct drm_display_mode,
2688 head);
2689 }
2690
2691
2692 if (!modes[i]) {
2693
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704 DRM_DEBUG_KMS("looking for current mode on connector %s\n",
2705 connector->name);
2706 modes[i] = &connector->state->crtc->mode;
2707 }
2708 crtcs[i] = new_crtc;
2709
2710 DRM_DEBUG_KMS("connector %s on [CRTC:%d:%s]: %dx%d%s\n",
2711 connector->name,
2712 connector->state->crtc->base.id,
2713 connector->state->crtc->name,
2714 modes[i]->hdisplay, modes[i]->vdisplay,
2715 modes[i]->flags & DRM_MODE_FLAG_INTERLACE ? "i" : "");
2716
2717 fallback = false;
2718 conn_configured |= BIT(i);
2719 }
2720
2721 if ((conn_configured & mask) != mask && conn_configured != conn_seq)
2722 goto retry;
2723
2724
2725
2726
2727
2728
2729 if (num_connectors_enabled != num_connectors_detected &&
2730 num_connectors_enabled < dev->mode_config.num_crtc) {
2731 DRM_DEBUG_KMS("fallback: Not all outputs enabled\n");
2732 DRM_DEBUG_KMS("Enabled: %i, detected: %i\n", num_connectors_enabled,
2733 num_connectors_detected);
2734 fallback = true;
2735 }
2736
2737 if (fallback) {
2738bail:
2739 DRM_DEBUG_KMS("Not using firmware configuration\n");
2740 memcpy(enabled, save_enabled, count);
2741 ret = false;
2742 }
2743
2744 drm_modeset_drop_locks(&ctx);
2745 drm_modeset_acquire_fini(&ctx);
2746
2747 kfree(save_enabled);
2748 return ret;
2749}
2750
2751static void drm_setup_crtcs(struct drm_fb_helper *fb_helper,
2752 u32 width, u32 height)
2753{
2754 struct drm_device *dev = fb_helper->dev;
2755 struct drm_fb_helper_crtc **crtcs;
2756 struct drm_display_mode **modes;
2757 struct drm_fb_offset *offsets;
2758 bool *enabled;
2759 int i;
2760
2761 DRM_DEBUG_KMS("\n");
2762
2763 lockdep_assert_held(&fb_helper->lock);
2764
2765 crtcs = kcalloc(fb_helper->connector_count,
2766 sizeof(struct drm_fb_helper_crtc *), GFP_KERNEL);
2767 modes = kcalloc(fb_helper->connector_count,
2768 sizeof(struct drm_display_mode *), GFP_KERNEL);
2769 offsets = kcalloc(fb_helper->connector_count,
2770 sizeof(struct drm_fb_offset), GFP_KERNEL);
2771 enabled = kcalloc(fb_helper->connector_count,
2772 sizeof(bool), GFP_KERNEL);
2773 if (!crtcs || !modes || !enabled || !offsets) {
2774 DRM_ERROR("Memory allocation failed\n");
2775 goto out;
2776 }
2777
2778 mutex_lock(&fb_helper->dev->mode_config.mutex);
2779 if (drm_fb_helper_probe_connector_modes(fb_helper, width, height) == 0)
2780 DRM_DEBUG_KMS("No connectors reported connected with modes\n");
2781 drm_enable_connectors(fb_helper, enabled);
2782
2783 if (!drm_fb_helper_firmware_config(fb_helper, crtcs, modes, offsets,
2784 enabled, width, height)) {
2785 memset(modes, 0, fb_helper->connector_count*sizeof(modes[0]));
2786 memset(crtcs, 0, fb_helper->connector_count*sizeof(crtcs[0]));
2787 memset(offsets, 0, fb_helper->connector_count*sizeof(offsets[0]));
2788
2789 if (!drm_target_cloned(fb_helper, modes, offsets,
2790 enabled, width, height) &&
2791 !drm_target_preferred(fb_helper, modes, offsets,
2792 enabled, width, height))
2793 DRM_ERROR("Unable to find initial modes\n");
2794
2795 DRM_DEBUG_KMS("picking CRTCs for %dx%d config\n",
2796 width, height);
2797
2798 drm_pick_crtcs(fb_helper, crtcs, modes, 0, width, height);
2799 }
2800 mutex_unlock(&fb_helper->dev->mode_config.mutex);
2801
2802
2803
2804 for (i = 0; i < fb_helper->crtc_count; i++)
2805 drm_fb_helper_modeset_release(fb_helper,
2806 &fb_helper->crtc_info[i].mode_set);
2807
2808 fb_helper->sw_rotations = 0;
2809 drm_fb_helper_for_each_connector(fb_helper, i) {
2810 struct drm_display_mode *mode = modes[i];
2811 struct drm_fb_helper_crtc *fb_crtc = crtcs[i];
2812 struct drm_fb_offset *offset = &offsets[i];
2813
2814 if (mode && fb_crtc) {
2815 struct drm_mode_set *modeset = &fb_crtc->mode_set;
2816 struct drm_connector *connector =
2817 fb_helper->connector_info[i]->connector;
2818
2819 DRM_DEBUG_KMS("desired mode %s set on crtc %d (%d,%d)\n",
2820 mode->name, fb_crtc->mode_set.crtc->base.id, offset->x, offset->y);
2821
2822 fb_crtc->desired_mode = mode;
2823 fb_crtc->x = offset->x;
2824 fb_crtc->y = offset->y;
2825 modeset->mode = drm_mode_duplicate(dev,
2826 fb_crtc->desired_mode);
2827 drm_connector_get(connector);
2828 drm_setup_crtc_rotation(fb_helper, fb_crtc, connector);
2829 modeset->connectors[modeset->num_connectors++] = connector;
2830 modeset->x = offset->x;
2831 modeset->y = offset->y;
2832 }
2833 }
2834out:
2835 kfree(crtcs);
2836 kfree(modes);
2837 kfree(offsets);
2838 kfree(enabled);
2839}
2840
2841
2842
2843
2844
2845
2846
2847
2848static void drm_setup_crtcs_fb(struct drm_fb_helper *fb_helper)
2849{
2850 struct fb_info *info = fb_helper->fbdev;
2851 int i;
2852
2853 for (i = 0; i < fb_helper->crtc_count; i++)
2854 if (fb_helper->crtc_info[i].mode_set.num_connectors)
2855 fb_helper->crtc_info[i].mode_set.fb = fb_helper->fb;
2856
2857 mutex_lock(&fb_helper->dev->mode_config.mutex);
2858 drm_fb_helper_for_each_connector(fb_helper, i) {
2859 struct drm_connector *connector =
2860 fb_helper->connector_info[i]->connector;
2861
2862
2863 if (connector->status == connector_status_connected) {
2864 info->var.width = connector->display_info.width_mm;
2865 info->var.height = connector->display_info.height_mm;
2866 break;
2867 }
2868 }
2869 mutex_unlock(&fb_helper->dev->mode_config.mutex);
2870
2871 switch (fb_helper->sw_rotations) {
2872 case DRM_MODE_ROTATE_0:
2873 info->fbcon_rotate_hint = FB_ROTATE_UR;
2874 break;
2875 case DRM_MODE_ROTATE_90:
2876 info->fbcon_rotate_hint = FB_ROTATE_CCW;
2877 break;
2878 case DRM_MODE_ROTATE_180:
2879 info->fbcon_rotate_hint = FB_ROTATE_UD;
2880 break;
2881 case DRM_MODE_ROTATE_270:
2882 info->fbcon_rotate_hint = FB_ROTATE_CW;
2883 break;
2884 default:
2885
2886
2887
2888
2889
2890 info->fbcon_rotate_hint = FB_ROTATE_UR;
2891 }
2892}
2893
2894
2895static int
2896__drm_fb_helper_initial_config_and_unlock(struct drm_fb_helper *fb_helper,
2897 int bpp_sel)
2898{
2899 struct drm_device *dev = fb_helper->dev;
2900 struct fb_info *info;
2901 unsigned int width, height;
2902 int ret;
2903
2904 width = dev->mode_config.max_width;
2905 height = dev->mode_config.max_height;
2906
2907 drm_setup_crtcs(fb_helper, width, height);
2908 ret = drm_fb_helper_single_fb_probe(fb_helper, bpp_sel);
2909 if (ret < 0) {
2910 if (ret == -EAGAIN) {
2911 fb_helper->preferred_bpp = bpp_sel;
2912 fb_helper->deferred_setup = true;
2913 ret = 0;
2914 }
2915 mutex_unlock(&fb_helper->lock);
2916
2917 return ret;
2918 }
2919 drm_setup_crtcs_fb(fb_helper);
2920
2921 fb_helper->deferred_setup = false;
2922
2923 info = fb_helper->fbdev;
2924 info->var.pixclock = 0;
2925
2926#if IS_ENABLED(CONFIG_DRM_FBDEV_LEAK_PHYS_SMEM)
2927 if (!drm_leak_fbdev_smem)
2928#endif
2929
2930 info->flags |= FBINFO_HIDE_SMEM_START;
2931
2932
2933
2934
2935 mutex_unlock(&fb_helper->lock);
2936
2937 ret = register_framebuffer(info);
2938 if (ret < 0)
2939 return ret;
2940
2941 dev_info(dev->dev, "fb%d: %s frame buffer device\n",
2942 info->node, info->fix.id);
2943
2944 mutex_lock(&kernel_fb_helper_lock);
2945 if (list_empty(&kernel_fb_helper_list))
2946 register_sysrq_key('v', &sysrq_drm_fb_helper_restore_op);
2947
2948 list_add(&fb_helper->kernel_fb_list, &kernel_fb_helper_list);
2949 mutex_unlock(&kernel_fb_helper_lock);
2950
2951 return 0;
2952}
2953
2954
2955
2956
2957
2958
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
2969
2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
2983
2984
2985
2986
2987
2988
2989
2990
2991
2992
2993
2994
2995int drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper, int bpp_sel)
2996{
2997 int ret;
2998
2999 if (!drm_fbdev_emulation)
3000 return 0;
3001
3002 mutex_lock(&fb_helper->lock);
3003 ret = __drm_fb_helper_initial_config_and_unlock(fb_helper, bpp_sel);
3004
3005 return ret;
3006}
3007EXPORT_SYMBOL(drm_fb_helper_initial_config);
3008
3009
3010
3011
3012
3013
3014
3015
3016
3017
3018
3019
3020
3021
3022
3023
3024
3025
3026
3027
3028
3029
3030int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper)
3031{
3032 int err = 0;
3033
3034 if (!drm_fbdev_emulation || !fb_helper)
3035 return 0;
3036
3037 mutex_lock(&fb_helper->lock);
3038 if (fb_helper->deferred_setup) {
3039 err = __drm_fb_helper_initial_config_and_unlock(fb_helper,
3040 fb_helper->preferred_bpp);
3041 return err;
3042 }
3043
3044 if (!fb_helper->fb || !drm_fb_helper_is_bound(fb_helper)) {
3045 fb_helper->delayed_hotplug = true;
3046 mutex_unlock(&fb_helper->lock);
3047 return err;
3048 }
3049
3050 DRM_DEBUG_KMS("\n");
3051
3052 drm_setup_crtcs(fb_helper, fb_helper->fb->width, fb_helper->fb->height);
3053 drm_setup_crtcs_fb(fb_helper);
3054 mutex_unlock(&fb_helper->lock);
3055
3056 drm_fb_helper_set_par(fb_helper->fbdev);
3057
3058 return 0;
3059}
3060EXPORT_SYMBOL(drm_fb_helper_hotplug_event);
3061
3062
3063
3064
3065
3066
3067
3068
3069
3070
3071
3072
3073
3074
3075
3076
3077
3078
3079
3080
3081
3082
3083
3084
3085int drm_fb_helper_fbdev_setup(struct drm_device *dev,
3086 struct drm_fb_helper *fb_helper,
3087 const struct drm_fb_helper_funcs *funcs,
3088 unsigned int preferred_bpp,
3089 unsigned int max_conn_count)
3090{
3091 int ret;
3092
3093 if (!preferred_bpp)
3094 preferred_bpp = dev->mode_config.preferred_depth;
3095 if (!preferred_bpp)
3096 preferred_bpp = 32;
3097
3098 if (!max_conn_count)
3099 max_conn_count = dev->mode_config.num_connector;
3100 if (!max_conn_count) {
3101 DRM_DEV_ERROR(dev->dev, "fbdev: No connectors\n");
3102 return -EINVAL;
3103 }
3104
3105 drm_fb_helper_prepare(dev, fb_helper, funcs);
3106
3107 ret = drm_fb_helper_init(dev, fb_helper, max_conn_count);
3108 if (ret < 0) {
3109 DRM_DEV_ERROR(dev->dev, "fbdev: Failed to initialize (ret=%d)\n", ret);
3110 return ret;
3111 }
3112
3113 ret = drm_fb_helper_single_add_all_connectors(fb_helper);
3114 if (ret < 0) {
3115 DRM_DEV_ERROR(dev->dev, "fbdev: Failed to add connectors (ret=%d)\n", ret);
3116 goto err_drm_fb_helper_fini;
3117 }
3118
3119 if (!drm_drv_uses_atomic_modeset(dev))
3120 drm_helper_disable_unused_functions(dev);
3121
3122 ret = drm_fb_helper_initial_config(fb_helper, preferred_bpp);
3123 if (ret < 0) {
3124 DRM_DEV_ERROR(dev->dev, "fbdev: Failed to set configuration (ret=%d)\n", ret);
3125 goto err_drm_fb_helper_fini;
3126 }
3127
3128 return 0;
3129
3130err_drm_fb_helper_fini:
3131 drm_fb_helper_fbdev_teardown(dev);
3132
3133 return ret;
3134}
3135EXPORT_SYMBOL(drm_fb_helper_fbdev_setup);
3136
3137
3138
3139
3140
3141
3142
3143
3144
3145
3146
3147
3148
3149
3150
3151
3152void drm_fb_helper_fbdev_teardown(struct drm_device *dev)
3153{
3154 struct drm_fb_helper *fb_helper = dev->fb_helper;
3155 struct fb_ops *fbops = NULL;
3156
3157 if (!fb_helper)
3158 return;
3159
3160
3161 if (fb_helper->fbdev && fb_helper->fbdev->dev)
3162 drm_fb_helper_unregister_fbi(fb_helper);
3163
3164 if (fb_helper->fbdev && fb_helper->fbdev->fbdefio) {
3165 fb_deferred_io_cleanup(fb_helper->fbdev);
3166 kfree(fb_helper->fbdev->fbdefio);
3167 fbops = fb_helper->fbdev->fbops;
3168 }
3169
3170 drm_fb_helper_fini(fb_helper);
3171 kfree(fbops);
3172
3173 if (fb_helper->fb)
3174 drm_framebuffer_remove(fb_helper->fb);
3175}
3176EXPORT_SYMBOL(drm_fb_helper_fbdev_teardown);
3177
3178
3179
3180
3181
3182
3183
3184
3185void drm_fb_helper_lastclose(struct drm_device *dev)
3186{
3187 drm_fb_helper_restore_fbdev_mode_unlocked(dev->fb_helper);
3188}
3189EXPORT_SYMBOL(drm_fb_helper_lastclose);
3190
3191
3192
3193
3194
3195
3196
3197
3198
3199
3200void drm_fb_helper_output_poll_changed(struct drm_device *dev)
3201{
3202 drm_fb_helper_hotplug_event(dev->fb_helper);
3203}
3204EXPORT_SYMBOL(drm_fb_helper_output_poll_changed);
3205
3206
3207static int drm_fbdev_fb_open(struct fb_info *info, int user)
3208{
3209 struct drm_fb_helper *fb_helper = info->par;
3210
3211
3212 if (user && !try_module_get(fb_helper->dev->driver->fops->owner))
3213 return -ENODEV;
3214
3215 return 0;
3216}
3217
3218static int drm_fbdev_fb_release(struct fb_info *info, int user)
3219{
3220 struct drm_fb_helper *fb_helper = info->par;
3221
3222 if (user)
3223 module_put(fb_helper->dev->driver->fops->owner);
3224
3225 return 0;
3226}
3227
3228static void drm_fbdev_cleanup(struct drm_fb_helper *fb_helper)
3229{
3230 struct fb_info *fbi = fb_helper->fbdev;
3231 struct fb_ops *fbops = NULL;
3232 void *shadow = NULL;
3233
3234 if (!fb_helper->dev)
3235 return;
3236
3237 if (fbi && fbi->fbdefio) {
3238 fb_deferred_io_cleanup(fbi);
3239 shadow = fbi->screen_buffer;
3240 fbops = fbi->fbops;
3241 }
3242
3243 drm_fb_helper_fini(fb_helper);
3244
3245 if (shadow) {
3246 vfree(shadow);
3247 kfree(fbops);
3248 }
3249
3250 drm_client_framebuffer_delete(fb_helper->buffer);
3251}
3252
3253static void drm_fbdev_release(struct drm_fb_helper *fb_helper)
3254{
3255 drm_fbdev_cleanup(fb_helper);
3256 drm_client_release(&fb_helper->client);
3257 kfree(fb_helper);
3258}
3259
3260
3261
3262
3263
3264static void drm_fbdev_fb_destroy(struct fb_info *info)
3265{
3266 drm_fbdev_release(info->par);
3267}
3268
3269static int drm_fbdev_fb_mmap(struct fb_info *info, struct vm_area_struct *vma)
3270{
3271 struct drm_fb_helper *fb_helper = info->par;
3272
3273 if (fb_helper->dev->driver->gem_prime_mmap)
3274 return fb_helper->dev->driver->gem_prime_mmap(fb_helper->buffer->gem, vma);
3275 else
3276 return -ENODEV;
3277}
3278
3279static struct fb_ops drm_fbdev_fb_ops = {
3280 .owner = THIS_MODULE,
3281 DRM_FB_HELPER_DEFAULT_OPS,
3282 .fb_open = drm_fbdev_fb_open,
3283 .fb_release = drm_fbdev_fb_release,
3284 .fb_destroy = drm_fbdev_fb_destroy,
3285 .fb_mmap = drm_fbdev_fb_mmap,
3286 .fb_read = drm_fb_helper_sys_read,
3287 .fb_write = drm_fb_helper_sys_write,
3288 .fb_fillrect = drm_fb_helper_sys_fillrect,
3289 .fb_copyarea = drm_fb_helper_sys_copyarea,
3290 .fb_imageblit = drm_fb_helper_sys_imageblit,
3291};
3292
3293static struct fb_deferred_io drm_fbdev_defio = {
3294 .delay = HZ / 20,
3295 .deferred_io = drm_fb_helper_deferred_io,
3296};
3297
3298
3299
3300
3301
3302
3303
3304
3305
3306
3307
3308
3309
3310
3311int drm_fb_helper_generic_probe(struct drm_fb_helper *fb_helper,
3312 struct drm_fb_helper_surface_size *sizes)
3313{
3314 struct drm_client_dev *client = &fb_helper->client;
3315 struct drm_client_buffer *buffer;
3316 struct drm_framebuffer *fb;
3317 struct fb_info *fbi;
3318 u32 format;
3319
3320 DRM_DEBUG_KMS("surface width(%d), height(%d) and bpp(%d)\n",
3321 sizes->surface_width, sizes->surface_height,
3322 sizes->surface_bpp);
3323
3324 format = drm_mode_legacy_fb_format(sizes->surface_bpp, sizes->surface_depth);
3325 buffer = drm_client_framebuffer_create(client, sizes->surface_width,
3326 sizes->surface_height, format);
3327 if (IS_ERR(buffer))
3328 return PTR_ERR(buffer);
3329
3330 fb_helper->buffer = buffer;
3331 fb_helper->fb = buffer->fb;
3332 fb = buffer->fb;
3333
3334 fbi = drm_fb_helper_alloc_fbi(fb_helper);
3335 if (IS_ERR(fbi))
3336 return PTR_ERR(fbi);
3337
3338 fbi->fbops = &drm_fbdev_fb_ops;
3339 fbi->screen_size = fb->height * fb->pitches[0];
3340 fbi->fix.smem_len = fbi->screen_size;
3341 fbi->screen_buffer = buffer->vaddr;
3342
3343#if IS_ENABLED(CONFIG_DRM_FBDEV_LEAK_PHYS_SMEM)
3344 if (drm_leak_fbdev_smem && fbi->fix.smem_start == 0)
3345 fbi->fix.smem_start =
3346 page_to_phys(virt_to_page(fbi->screen_buffer));
3347#endif
3348 drm_fb_helper_fill_info(fbi, fb_helper, sizes);
3349
3350 if (fb->funcs->dirty) {
3351 struct fb_ops *fbops;
3352 void *shadow;
3353
3354
3355
3356
3357
3358 fbops = kzalloc(sizeof(*fbops), GFP_KERNEL);
3359 shadow = vzalloc(fbi->screen_size);
3360 if (!fbops || !shadow) {
3361 kfree(fbops);
3362 vfree(shadow);
3363 return -ENOMEM;
3364 }
3365
3366 *fbops = *fbi->fbops;
3367 fbi->fbops = fbops;
3368 fbi->screen_buffer = shadow;
3369 fbi->fbdefio = &drm_fbdev_defio;
3370
3371 fb_deferred_io_init(fbi);
3372 }
3373
3374 return 0;
3375}
3376EXPORT_SYMBOL(drm_fb_helper_generic_probe);
3377
3378static const struct drm_fb_helper_funcs drm_fb_helper_generic_funcs = {
3379 .fb_probe = drm_fb_helper_generic_probe,
3380};
3381
3382static void drm_fbdev_client_unregister(struct drm_client_dev *client)
3383{
3384 struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client);
3385
3386 if (fb_helper->fbdev)
3387
3388 drm_fb_helper_unregister_fbi(fb_helper);
3389 else
3390 drm_fbdev_release(fb_helper);
3391}
3392
3393static int drm_fbdev_client_restore(struct drm_client_dev *client)
3394{
3395 drm_fb_helper_lastclose(client->dev);
3396
3397 return 0;
3398}
3399
3400static int drm_fbdev_client_hotplug(struct drm_client_dev *client)
3401{
3402 struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client);
3403 struct drm_device *dev = client->dev;
3404 int ret;
3405
3406
3407 if (!fb_helper->dev && fb_helper->funcs)
3408 return 0;
3409
3410 if (dev->fb_helper)
3411 return drm_fb_helper_hotplug_event(dev->fb_helper);
3412
3413 if (!dev->mode_config.num_connector) {
3414 DRM_DEV_DEBUG(dev->dev, "No connectors found, will not create framebuffer!\n");
3415 return 0;
3416 }
3417
3418 drm_fb_helper_prepare(dev, fb_helper, &drm_fb_helper_generic_funcs);
3419
3420 ret = drm_fb_helper_init(dev, fb_helper, dev->mode_config.num_connector);
3421 if (ret)
3422 goto err;
3423
3424 ret = drm_fb_helper_single_add_all_connectors(fb_helper);
3425 if (ret)
3426 goto err_cleanup;
3427
3428 if (!drm_drv_uses_atomic_modeset(dev))
3429 drm_helper_disable_unused_functions(dev);
3430
3431 ret = drm_fb_helper_initial_config(fb_helper, fb_helper->preferred_bpp);
3432 if (ret)
3433 goto err_cleanup;
3434
3435 return 0;
3436
3437err_cleanup:
3438 drm_fbdev_cleanup(fb_helper);
3439err:
3440 fb_helper->dev = NULL;
3441 fb_helper->fbdev = NULL;
3442
3443 DRM_DEV_ERROR(dev->dev, "fbdev: Failed to setup generic emulation (ret=%d)\n", ret);
3444
3445 return ret;
3446}
3447
3448static const struct drm_client_funcs drm_fbdev_client_funcs = {
3449 .owner = THIS_MODULE,
3450 .unregister = drm_fbdev_client_unregister,
3451 .restore = drm_fbdev_client_restore,
3452 .hotplug = drm_fbdev_client_hotplug,
3453};
3454
3455
3456
3457
3458
3459
3460
3461
3462
3463
3464
3465
3466
3467
3468
3469
3470
3471
3472
3473
3474
3475
3476
3477
3478
3479
3480
3481int drm_fbdev_generic_setup(struct drm_device *dev, unsigned int preferred_bpp)
3482{
3483 struct drm_fb_helper *fb_helper;
3484 int ret;
3485
3486 WARN(dev->fb_helper, "fb_helper is already set!\n");
3487
3488 if (!drm_fbdev_emulation)
3489 return 0;
3490
3491 fb_helper = kzalloc(sizeof(*fb_helper), GFP_KERNEL);
3492 if (!fb_helper)
3493 return -ENOMEM;
3494
3495 ret = drm_client_init(dev, &fb_helper->client, "fbdev", &drm_fbdev_client_funcs);
3496 if (ret) {
3497 kfree(fb_helper);
3498 DRM_DEV_ERROR(dev->dev, "Failed to register client: %d\n", ret);
3499 return ret;
3500 }
3501
3502 if (!preferred_bpp)
3503 preferred_bpp = dev->mode_config.preferred_depth;
3504 if (!preferred_bpp)
3505 preferred_bpp = 32;
3506 fb_helper->preferred_bpp = preferred_bpp;
3507
3508 ret = drm_fbdev_client_hotplug(&fb_helper->client);
3509 if (ret)
3510 DRM_DEV_DEBUG(dev->dev, "client hotplug ret=%d\n", ret);
3511
3512 drm_client_register(&fb_helper->client);
3513
3514 return 0;
3515}
3516EXPORT_SYMBOL(drm_fbdev_generic_setup);
3517
3518
3519
3520
3521
3522int __init drm_fb_helper_modinit(void)
3523{
3524#if defined(CONFIG_FRAMEBUFFER_CONSOLE_MODULE) && !defined(CONFIG_EXPERT)
3525 const char name[] = "fbcon";
3526 struct module *fbcon;
3527
3528 mutex_lock(&module_mutex);
3529 fbcon = find_module(name);
3530 mutex_unlock(&module_mutex);
3531
3532 if (!fbcon)
3533 request_module_nowait(name);
3534#endif
3535 return 0;
3536}
3537EXPORT_SYMBOL(drm_fb_helper_modinit);
3538