1 /*sunxi_drm_fbdev.c
2 *
3 * Copyright (C) 2022 Allwinnertech Co., Ltd.
4 * Authors: zhengwanyu <zhengwanyu@allwinnertech.com>
5 * Authors: hongyaobin <hongyaobin@allwinnertech.com>
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation; either version 2 of the License, or (at your
10 * option) any later version.
11 */
12
13 #include <drm/drm_crtc_helper.h>
14 #include <drm/drm_fb_helper.h>
15 #include <drm/drm_fourcc.h>
16
17 #include "sunxi_drm_drv.h"
18 #include "sunxi_drm_connector.h"
19 #include "sunxi_drm_encoder.h"
20 #include "sunxi_drm_crtc.h"
21 #include "sunxi_drm_fbdev.h"
22 #include "sunxi_drm_gem.h"
23 #include "sunxi_drm_iommu.h"
24
25 struct __fb_addr_para {
26 uintptr_t fb_paddr;
27 int fb_size;
28 };
29
30 struct __fb_addr_para g_fb_addr;
31
32 struct sunxi_drm_fbdev *sunxi_drmfbdev[FBDEV_MAX_NUM];
33
sunxi_drm_fbdev_set_fbdev(struct sunxi_drm_fbdev * sunxi_fbdev,int index)34 static void sunxi_drm_fbdev_set_fbdev(struct sunxi_drm_fbdev *sunxi_fbdev, int index)
35 {
36 if (index < FBDEV_MAX_NUM)
37 sunxi_drmfbdev[index] = sunxi_fbdev;
38 }
39
sunxi_drm_fbdev_get_fbdev(int index)40 struct sunxi_drm_fbdev *sunxi_drm_fbdev_get_fbdev(int index)
41 {
42 if (index < FBDEV_MAX_NUM)
43 return sunxi_drmfbdev[index];
44 return NULL;
45 }
46
sunxi_drm_fbdev_mmap(struct fb_info * info,struct vm_area_struct * vma)47 static int sunxi_drm_fbdev_mmap(struct fb_info *info, struct vm_area_struct *vma)
48 {
49 struct drm_fb_helper *helper = info->par;
50 //@TODO:fix it, now just support 1 fbdev
51 struct sunxi_drm_fbdev *sunxi_fbd = sunxi_drm_fbdev_get_fbdev(0);
52 struct sunxi_drm_gem_object *sunxi_gem_obj = sunxi_fbd->sunxi_fb->obj[0];
53 unsigned long vm_size;
54 int ret;
55
56 vma->vm_flags |= VM_IO | VM_DONTEXPAND | VM_DONTDUMP;
57
58 vm_size = vma->vm_end - vma->vm_start;
59
60 if (vm_size > sunxi_gem_obj->size)
61 return -EINVAL;
62
63 ret = dma_mmap_attrs(sunxi_gem_obj->base.dev->dev, vma, sunxi_gem_obj->vaddr,
64 sunxi_gem_obj->dma_addr, sunxi_gem_obj->size,
65 sunxi_gem_obj->dma_attrs);
66 if (ret < 0) {
67 DRM_ERROR("failed to mmap.\n");
68 return ret;
69 }
70
71 return 0;
72 }
73
74 /*static int sunxi_drm_fbdev_helper_set_par(struct fb_info *info)
75 {
76 int i, ret;
77 struct drm_mode_set *modeset;
78 struct drm_framebuffer *fb;
79 struct drm_fb_helper *fb_helper = info->par;
80 struct fb_var_screeninfo *var = &info->var;
81
82 ret = drm_fb_helper_set_par(info);
83 if (ret < 0) {
84 DRM_ERROR("drm_fb_helper_set_par failed\n");
85 return ret;
86 }
87
88 for (i = 0; i < fb_helper->crtc_count; i++) {
89 modeset = &fb_helper->crtc_info[i].mode_set;
90 if (!modeset->fb || !modeset->crtc)
91 continue;
92 fb = modeset->fb;
93 fb->width = var->xres;
94 fb->height = var->yres * 2;
95
96 switch (var->bits_per_pixel) {
97 case 16:
98 fb->depth = (var->green.length == 6) ? 16 : 15;
99 break;
100 case 32:
101 fb->depth = (var->transp.length > 0) ? 32 : 24;
102 break;
103 default:
104 fb->depth = var->bits_per_pixel;
105 break;
106 }
107
108 fb->bits_per_pixel = var->bits_per_pixel;
109 fb->pitches[0] = var->xres * var->bits_per_pixel / 8;
110 fb->offsets[0] = 0;
111 fb->pixel_format = drm_mode_legacy_fb_format(
112 fb->bits_per_pixel, fb->depth);
113 }
114
115 return 0;
116 }*/
117
118 /*for other device in kernel like gpu to invoke*/
sunxi_get_fb_addr_para(struct __fb_addr_para * fb_addr_para)119 void sunxi_get_fb_addr_para(struct __fb_addr_para *fb_addr_para)
120 {
121 if (fb_addr_para) {
122 fb_addr_para->fb_paddr = g_fb_addr.fb_paddr;
123 fb_addr_para->fb_size = g_fb_addr.fb_size;
124 }
125 }
126 EXPORT_SYMBOL(g_fb_addr);
127 EXPORT_SYMBOL(sunxi_get_fb_addr_para);
128
129 static struct fb_ops sunxi_drm_fbdev_ops = {
130 .owner = THIS_MODULE,
131 DRM_FB_HELPER_DEFAULT_OPS,
132 .fb_mmap = sunxi_drm_fbdev_mmap,
133 .fb_fillrect = drm_fb_helper_cfb_fillrect,
134 .fb_copyarea = drm_fb_helper_cfb_copyarea,
135 .fb_imageblit = drm_fb_helper_cfb_imageblit,
136 };
137
138 /*
139 * sunxi_drm_fbdev_create_with_funcs() - create fbdev, with_funcs means that
140 * with funcs for drm_framebuffer instant
141 * 1.create a gem with a dma-buf
142 * 2.create a fb_info
143 * 3.create a drm_framebuffer with one plane
144 */
sunxi_drm_fbdev_create_with_funcs(struct drm_fb_helper * helper,struct drm_fb_helper_surface_size * sizes,const struct drm_framebuffer_funcs * funcs)145 int sunxi_drm_fbdev_create_with_funcs(struct drm_fb_helper *helper,
146 struct drm_fb_helper_surface_size *sizes,
147 const struct drm_framebuffer_funcs *funcs)
148 {
149 struct sunxi_drm_fbdev *sunxi_fbdev = to_sunxi_fbdev(helper);
150 struct drm_mode_fb_cmd2 mode_cmd = { 0 };
151 struct drm_device *dev = helper->dev;
152 struct sunxi_drm_gem_object *obj;
153 struct drm_framebuffer *fb;
154 unsigned int bytes_per_pixel;
155 unsigned long offset;
156 struct fb_info *fbi;
157 size_t size;
158 unsigned int flags;
159 int ret;
160
161 #ifndef CONFIG_DRM_FBDEV_EMULATION_DUAL_DISPLAY_WITH_DIFFERENT_BUFFER
162 /* Force to make the fbdev's sizes decided by primary connector,
163 * the bootlogo's sizes are equal to the primary connector's
164 */
165 sizes->surface_width = sunxi_drm_get_init_width();
166 sizes->surface_height = sunxi_drm_get_init_height();
167 sizes->fb_width = sunxi_drm_get_init_width();
168 sizes->fb_height = sunxi_drm_get_init_height();
169 sizes->surface_bpp = sunxi_drm_get_init_bpp();
170 #endif
171
172 DRM_INFO("surface width(%d), height(%d) and bpp(%d) depth(%d)\n",
173 sizes->surface_width, sizes->surface_height,
174 sizes->surface_bpp, sizes->surface_depth);
175 DRM_INFO("fb width(%d) height(%d)\n", sizes->fb_width, sizes->fb_height);
176
177 bytes_per_pixel = DIV_ROUND_UP(sizes->surface_bpp, 8);
178
179 mode_cmd.width = sizes->surface_width;
180 mode_cmd.height = sizes->surface_height * 2;
181
182 mode_cmd.pitches[0] = sizes->surface_width * bytes_per_pixel;
183 mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
184 sizes->surface_depth);
185
186 size = mode_cmd.pitches[0] * mode_cmd.height;
187 DRM_INFO("size = %d\n", size);
188
189 if (is_drm_iommu_supported(dev))
190 flags = SUNXI_BO_NONCONTIG | SUNXI_BO_WC;
191 else
192 flags = SUNXI_BO_CONTIG | SUNXI_BO_WC;
193
194 obj = sunxi_drm_gem_create(dev, flags, size);
195 if (IS_ERR(obj))
196 return PTR_ERR(obj);
197
198 fbi = drm_fb_helper_alloc_fbi(helper);
199 if (IS_ERR(fbi)) {
200 ret = PTR_ERR(fbi);
201 goto err_gem_free_object;
202 }
203
204 sunxi_fbdev->sunxi_fb = sunxi_drm_fb_alloc(dev, &mode_cmd, &obj, 1,
205 FBDEV_CREATE_FB, funcs);
206 if (IS_ERR(sunxi_fbdev->sunxi_fb)) {
207 dev_err(dev->dev, "Failed to allocate DRM framebuffer.\n");
208 ret = PTR_ERR(sunxi_fbdev->sunxi_fb);
209 goto err_gem_free_object;
210 }
211
212 fb = &sunxi_fbdev->sunxi_fb->fb;
213 helper->fb = fb;
214
215 fbi->par = helper;
216 fbi->flags = FBINFO_FLAG_DEFAULT;
217 fbi->fbops = &sunxi_drm_fbdev_ops;
218
219 drm_fb_helper_fill_info(fbi, helper, sizes);
220
221 offset = fbi->var.xoffset * bytes_per_pixel;
222 offset += fbi->var.yoffset * fb->pitches[0];
223
224 dev->mode_config.fb_base = (resource_size_t)obj->dma_addr;
225 fbi->screen_base = obj->vaddr + offset;
226 fbi->fix.smem_start = (unsigned long)(obj->dma_addr + offset);
227 fbi->screen_size = size;
228 fbi->fix.smem_len = size;
229
230 /*for other device in kernel like gpu to invoke*/
231 g_fb_addr.fb_paddr = fbi->fix.smem_start;
232 g_fb_addr.fb_size = fbi->fix.smem_len;
233
234 return 0;
235
236 err_gem_free_object:
237 drm_gem_object_put(&obj->base);
238 return ret;
239 }
240
sunxi_drm_fbdev_create(struct drm_fb_helper * helper,struct drm_fb_helper_surface_size * sizes)241 static int sunxi_drm_fbdev_create(struct drm_fb_helper *helper,
242 struct drm_fb_helper_surface_size *sizes)
243 {
244 return sunxi_drm_fbdev_create_with_funcs(helper, sizes,
245 sunxi_drm_fb_get_funcs());
246 }
247
248 /* For booting:
249 * in this func, you can set the default attachments
250 * amount crtc/encoder/connector during drm kernel init.
251 * it is useful for smoot booting
252 *
253 * @crtcs: *crtcs[i] means which crtc connector[i] will attached to
254 * @modes: *modes[i] means which mode connector[i] will set
255 * @offsets: offsets[i] means x/y offset of fb used by connector[i]
256 * @enabled: enabled[i] means if connector[i] is enabled
257 */
258 #if 0
259 static bool sunxi_drm_fbdev_initial_config(struct drm_fb_helper *fb_helper,
260 struct drm_fb_helper_crtc **crtcs,
261 struct drm_display_mode **modes,
262 struct drm_fb_offset *offsets,
263 bool *enabled, int width, int height)
264 {
265 int i, j;
266
267 struct drm_connector *conn;
268 struct sunxi_drm_connector *sunxi_conn;
269 struct drm_display_mode *mode_tmp;
270 const struct drm_connector_helper_funcs *conn_funcs;
271
272 struct drm_encoder *enc;
273 struct sunxi_drm_encoder *sunxi_enc;
274
275 struct drm_crtc *crtc = NULL;
276 struct sunxi_drm_crtc *sunxi_crtc;
277
278 struct drm_device *dev;
279 bool find = false;
280
281 for (i = 0; i < fb_helper->connector_count; i++) {
282 find = false;
283 if (!enabled[i])
284 continue;
285
286 offsets[i].x = 0;
287 offsets[i].y = 0;
288
289 /*find the preferred display_mode of the connector*/
290 /*preferred mode: the mode set by user space*/
291 conn = fb_helper->connector_info[i]->connector;
292 sunxi_conn = to_sunxi_connector(conn);
293
294 list_for_each_entry(mode_tmp, &conn->modes, head) {
295 if (mode_tmp->type & DRM_MODE_TYPE_USERDEF) {
296 modes[i] = mode_tmp;
297 find = true;
298 DRM_INFO("[FBDEV]Find the userdef_mode[%s]:"
299 "%dx%d%c@%d\n",
300 mode_tmp->name,
301 mode_tmp->hdisplay, mode_tmp->vdisplay,
302 (mode_tmp->flags
303 & DRM_MODE_FLAG_INTERLACE) ? 'I' : 'P',
304 (mode_tmp->clock * 1000)
305 / (mode_tmp->htotal * mode_tmp->vtotal));
306 break;
307 }
308 }
309
310 /*if there is NO preferred mode, use default mode*/
311 /*default mode: the mode set in DTS*/
312 if (!find) {
313 list_for_each_entry(mode_tmp, &conn->modes, head) {
314 if (mode_tmp->type & DRM_MODE_TYPE_DEFAULT) {
315 modes[i] = mode_tmp;
316 find = true;
317 DRM_INFO("[FBDEV]Find the default_mode[%s]:"
318 "%dx%d%c@%d\n",
319 mode_tmp->name,
320 mode_tmp->hdisplay,
321 mode_tmp->vdisplay,
322 (mode_tmp->flags
323 & DRM_MODE_FLAG_INTERLACE) ? 'I' : 'P',
324 (mode_tmp->clock * 1000)
325 / (mode_tmp->htotal * mode_tmp->vtotal));
326 break;
327 }
328 }
329 }
330
331 if (!find) {
332 list_for_each_entry(mode_tmp, &conn->modes, head) {
333 if (mode_tmp->type & DRM_MODE_TYPE_PREFERRED) {
334 modes[i] = mode_tmp;
335 find = true;
336 DRM_INFO("[FBDEV]Find the preferred_mode[%s]:"
337 "%dx%d%c@%d\n",
338 mode_tmp->name,
339 mode_tmp->hdisplay, mode_tmp->vdisplay,
340 (mode_tmp->flags
341 & DRM_MODE_FLAG_INTERLACE) ? 'I' : 'P',
342 (mode_tmp->clock * 1000)
343 / (mode_tmp->htotal * mode_tmp->vtotal));
344 break;
345 }
346 }
347 }
348
349 /*find the best encoder for the connector*/
350 conn_funcs = conn->helper_private;
351 enc = conn_funcs->best_encoder(conn);
352 if (!enc)
353 continue;
354 conn->encoder = enc;
355 sunxi_enc = to_sunxi_encoder(enc);
356 DRM_INFO("[SUNXI-FBDEV]: connector(%d)--->encoder(%d)\n",
357 i, sunxi_enc->encoder_id);
358
359 /*find the best ertc for the connector*/
360 for (j = 0; j < fb_helper->crtc_count; j++) {
361 if (!(enc->possible_crtcs & (1 << i)))
362 continue;
363
364 crtc = fb_helper->crtc_info[j].mode_set.crtc;
365 if (!crtc)
366 continue;
367 sunxi_crtc = to_sunxi_crtc(crtc);
368 dev = crtc->dev;
369
370 /*this crtc has been attached to this encoder*/
371 if (enc->crtc == crtc) {
372 crtcs[i] = &fb_helper->crtc_info[j];
373 enc->crtc = crtc;
374 DRM_INFO("[SUNXI-FBDEV]:"
375 "encoder(%d)--->crtc(%d)\n",
376 sunxi_enc->encoder_id,
377 sunxi_crtc->crtc_id);
378 break;
379
380 /*this crtc has NOT been attached to this encoder*/
381 } else {
382 /*this crtc has NOT been attached to any encoder*/
383 if (!sunxi_crtc->is_in_use(sunxi_crtc)) {
384 crtcs[i] = &fb_helper->crtc_info[j];
385 enc->crtc = crtc;
386 DRM_INFO("[SUNXI-FBDEV]:"
387 "encoder(%d)--->crtc(%d)\n",
388 sunxi_enc->encoder_id,
389 sunxi_crtc->crtc_id);
390 break;
391 }
392 }
393 }
394
395 }
396
397 for (i = 0; i < fb_helper->connector_count; i++) {
398 if (modes[i] && crtcs[i])
399 return true;
400 }
401
402 DRM_INFO("[WARN]:There is NO suitable crtc/encoder "
403 "and display_mode for the connected connectors\n");
404 return true;
405 }
406 #endif
407
408 static const struct drm_fb_helper_funcs sunxi_drm_fb_helper_funcs = {
409 //.initial_config = sunxi_drm_fbdev_initial_config,
410 .fb_probe = sunxi_drm_fbdev_create,
411 };
412
413 #ifdef CONFIG_DRM_FBDEV_EMULATION_DUAL_DISPLAY_WITH_DIFFERENT_BUFFER
sunxi_drm_fb_helper_modeset_release(struct drm_fb_helper * helper,struct drm_mode_set * modeset)414 static void sunxi_drm_fb_helper_modeset_release(struct drm_fb_helper *helper,
415 struct drm_mode_set *modeset)
416 {
417 int i;
418
419 for (i = 0; i < modeset->num_connectors; i++) {
420 drm_connector_unreference(modeset->connectors[i]);
421 modeset->connectors[i] = NULL;
422 }
423 modeset->num_connectors = 0;
424
425 drm_mode_destroy(helper->dev, modeset->mode);
426 modeset->mode = NULL;
427
428 /* FIXME should hold a ref? */
429 modeset->fb = NULL;
430 }
431
sunxi_drm_fb_helper_crtc_free(struct drm_fb_helper * helper)432 static void sunxi_drm_fb_helper_crtc_free(struct drm_fb_helper *helper)
433 {
434 int i;
435
436 for (i = 0; i < helper->connector_count; i++) {
437 drm_connector_unreference(helper->connector_info[i]->connector);
438 kfree(helper->connector_info[i]);
439 }
440 kfree(helper->connector_info);
441
442 for (i = 0; i < helper->crtc_count; i++) {
443 struct drm_mode_set *modeset = &helper->crtc_info[i].mode_set;
444
445 sunxi_drm_fb_helper_modeset_release(helper, modeset);
446 kfree(modeset->connectors);
447 }
448 kfree(helper->crtc_info);
449 }
450
451
452 /*This function is created from drm_fb_help_init()*/
sunxi_drm_fb_helper_init(struct drm_device * dev,struct drm_fb_helper * fb_helper,int index)453 static int sunxi_drm_fb_helper_init(struct drm_device *dev,
454 struct drm_fb_helper *fb_helper, int index)
455 {
456 struct drm_crtc *crtc;
457
458 fb_helper->crtc_info = kcalloc(1, sizeof(struct drm_fb_helper_crtc), GFP_KERNEL);
459 if (!fb_helper->crtc_info)
460 return -ENOMEM;
461
462 fb_helper->crtc_count = 1;
463 fb_helper->connector_info = kcalloc(1, sizeof(struct drm_fb_helper_connector *), GFP_KERNEL);
464 if (!fb_helper->connector_info) {
465 kfree(fb_helper->crtc_info);
466 return -ENOMEM;
467 }
468 fb_helper->connector_info_alloc_count = 1;
469 fb_helper->connector_count = 0;
470
471 fb_helper->crtc_info[0].mode_set.connectors =
472 kcalloc(1, sizeof(struct drm_connector *), GFP_KERNEL);
473
474 if (!fb_helper->crtc_info[0].mode_set.connectors)
475 goto out_free;
476 fb_helper->crtc_info[0].mode_set.num_connectors = 0;
477
478 drm_for_each_crtc(crtc, dev) {
479 if (crtc->index == index)
480 fb_helper->crtc_info[0].mode_set.crtc = crtc;
481 }
482
483 return 0;
484 out_free:
485 sunxi_drm_fb_helper_crtc_free(fb_helper);
486 return -ENOMEM;
487 }
488 #endif
489
sunxi_drm_fbdev_init_with_funcs(struct drm_device * dev,unsigned int preferred_bpp,unsigned int num_crtc,const struct drm_fb_helper_funcs * funcs,int index)490 struct sunxi_drm_fbdev *sunxi_drm_fbdev_init_with_funcs(struct drm_device *dev,
491
492 unsigned int preferred_bpp, unsigned int num_crtc,
493 const struct drm_fb_helper_funcs *funcs, int index)
494 {
495 struct sunxi_drm_fbdev *sunxi_fbdev;
496 struct drm_fb_helper *helper;
497 int ret;
498 #ifdef CONFIG_DRM_FBDEV_EMULATION_DUAL_DISPLAY_WITH_DIFFERENT_BUFFER
499 struct sunxi_drm_connector *sconn;
500 #endif
501
502 sunxi_fbdev = kzalloc(sizeof(*sunxi_fbdev), GFP_KERNEL);
503 if (!sunxi_fbdev) {
504 dev_err(dev->dev, "Failed to allocate drm fbdev.\n");
505 return ERR_PTR(-ENOMEM);
506 }
507
508 helper = &sunxi_fbdev->fb_helper;
509
510 drm_fb_helper_prepare(dev, helper, funcs);
511
512 #ifdef CONFIG_DRM_FBDEV_EMULATION_DUAL_DISPLAY_WITH_DIFFERENT_BUFFER
513 /*init fb_helper and set crtc for this fb_helper*/
514 ret = sunxi_drm_fb_helper_init(dev, helper, index);
515 if (ret < 0) {
516 dev_err(dev->dev, "Failed to initialize drm fb helper.\n");
517 goto err_free;
518 }
519
520 /*set connector for this fb_helper*/
521 sconn = sunxi_drm_connector_get_connector(index);
522 if (!sconn) {
523 dev_err(dev->dev, "NO sunxi connector for create fbdev\n");
524 goto err_free;
525 }
526 #else
527 ret = drm_fb_helper_init(dev, helper);
528 if (ret < 0) {
529 dev_err(dev->dev, "Failed to initialize drm fb helper.\n");
530 goto err_free;
531 }
532 #endif
533
534 ret = drm_fb_helper_initial_config(helper, preferred_bpp);
535 if (ret < 0) {
536 dev_err(dev->dev, "Failed to set initial hw configuration.\n");
537 goto err_drm_fb_helper_fini;
538 }
539
540 return sunxi_fbdev;
541
542 err_drm_fb_helper_fini:
543 drm_fb_helper_fini(helper);
544 err_free:
545 kfree(sunxi_fbdev);
546
547 return ERR_PTR(ret);
548 }
549
sunxi_drm_fbdev_init(struct drm_device * dev)550 int sunxi_drm_fbdev_init(struct drm_device *dev)
551 {
552 struct sunxi_drm_fbdev *sunxi_fbdev;
553 int num_crtc, num_connector, bpp;
554 #ifdef CONFIG_DRM_FBDEV_EMULATION_DUAL_DISPLAY_WITH_DIFFERENT_BUFFER
555 int i;
556 #endif
557
558 num_crtc = dev->mode_config.num_crtc;
559 num_connector = dev->mode_config.num_connector;
560
561 #if defined(CONFIG_DRM_FBDEV_EMULATION_DUAL_DISPLAY_WITH_SAME_BUFFER) \
562 || defined(CONFIG_DRM_FBDEV_EMULATION_DUAL_DISPLAY_WITH_DIFFERENT_BUFFER)
563 if (num_connector != 2) {
564 DRM_ERROR("use dual-display, "
565 "please check your sys_config.fex to set dual-display\n");
566 return -1;
567 }
568
569 #else
570 if (num_connector != 1) {
571 DRM_ERROR("NOT use dual-display, num_connector:%d\n",
572 num_connector);
573 DRM_ERROR("please check your sys_config.fex to set one-display\n");
574 return -1;
575 }
576
577 #endif
578
579 bpp = 32;
580
581 /*decide bpp by bootlogo, or bootlogo can NOT be copy to fbdev correctly*/
582 if (sunxi_drm_is_need_smooth_boot())
583 bpp = sunxi_drm_get_init_bpp();
584
585 /* create /dev/fb0 and /dev/fb1
586 * /dev/fb0: crtc0--->connector0
587 * /dev/fb1: crtc1--->connector1
588 */
589 #ifdef CONFIG_DRM_FBDEV_EMULATION_DUAL_DISPLAY_WITH_DIFFERENT_BUFFER
590 for (i = 0; i < FBDEV_MAX_NUM; i++) {
591 sunxi_fbdev = sunxi_drm_fbdev_init_with_funcs(dev, bpp,
592 1, &sunxi_drm_fb_helper_funcs, i);
593 if (!sunxi_fbdev) {
594 DRM_ERROR("sunxi_drm_fbdev_init failed\n");
595 return -1;
596 }
597
598 sunxi_drm_fbdev_set_fbdev(sunxi_fbdev, i);
599 }
600 #else
601 sunxi_fbdev = sunxi_drm_fbdev_init_with_funcs(dev, bpp,
602 num_crtc, &sunxi_drm_fb_helper_funcs, 0);
603 if (!sunxi_fbdev) {
604 DRM_ERROR("sunxi_drm_fbdev_init failed\n");
605 return -1;
606 }
607
608 sunxi_drm_fbdev_set_fbdev(sunxi_fbdev, 0);
609 #endif
610
611 return 0;
612 }
613
sunxi_drm_fbdev_exit(struct drm_device * dev)614 void sunxi_drm_fbdev_exit(struct drm_device *dev)
615 {
616 int i = 0;
617 struct sunxi_drm_fbdev *sunxi_fbdev;
618
619 for (i = 0; i < FBDEV_MAX_NUM; i++) {
620 sunxi_fbdev = sunxi_drm_fbdev_get_fbdev(i);
621 if (!sunxi_fbdev)
622 return;
623 drm_fb_helper_unregister_fbi(&sunxi_fbdev->fb_helper);
624
625 if (sunxi_fbdev->sunxi_fb) {
626 drm_framebuffer_unregister_private(&sunxi_fbdev->sunxi_fb->fb);
627 sunxi_drm_fb_destroy(&sunxi_fbdev->sunxi_fb->fb);
628 }
629
630 drm_fb_helper_fini(&sunxi_fbdev->fb_helper);
631 kfree(sunxi_fbdev);
632 sunxi_drm_fbdev_set_fbdev(NULL, i);
633 }
634 }
635
sunxi_drm_fbdev_output_poll_changed(struct drm_device * dev)636 void sunxi_drm_fbdev_output_poll_changed(struct drm_device *dev)
637 {
638 struct sunxi_drm_fbdev *sunxi_fbdev;
639
640 #ifdef CONFIG_DRM_FBDEV_EMULATION_DUAL_DISPLAY_WITH_DIFFERENT_BUFFER
641 int i;
642
643 for (i = 0; i < FBDEV_MAX_NUM; i++) {
644 sunxi_fbdev = sunxi_drm_fbdev_get_fbdev(i);
645
646 drm_fb_helper_hotplug_event(&sunxi_fbdev->fb_helper);
647 }
648 #else
649 sunxi_fbdev = sunxi_drm_fbdev_get_fbdev(0);
650
651 drm_fb_helper_hotplug_event(&sunxi_fbdev->fb_helper);
652 #endif
653 }
654