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