• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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