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