2 * Copyright (C) 2013 Google, Inc.
4 * This software is licensed under the terms of the GNU General Public
5 * License version 2, as published by the Free Software Foundation, and
6 * may be copied, distributed, and modified under those terms.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
15 #include <linux/vmalloc.h>
17 #include <video/adf.h>
18 #include <video/adf_client.h>
19 #include <video/adf_fbdev.h>
20 #include <video/adf_format.h>
24 struct adf_fbdev_format {
37 static const struct adf_fbdev_format format_table[] = {
38 {DRM_FORMAT_RGB332, 8, 3, 3, 2, 0, 5, 2, 0, 0},
39 {DRM_FORMAT_BGR233, 8, 3, 3, 2, 0, 0, 3, 5, 0},
41 {DRM_FORMAT_XRGB4444, 16, 4, 4, 4, 0, 8, 4, 0, 0},
42 {DRM_FORMAT_XBGR4444, 16, 4, 4, 4, 0, 0, 4, 8, 0},
43 {DRM_FORMAT_RGBX4444, 16, 4, 4, 4, 0, 12, 8, 4, 0},
44 {DRM_FORMAT_BGRX4444, 16, 4, 4, 4, 0, 0, 4, 8, 0},
46 {DRM_FORMAT_ARGB4444, 16, 4, 4, 4, 4, 8, 4, 0, 12},
47 {DRM_FORMAT_ABGR4444, 16, 4, 4, 4, 4, 0, 4, 8, 12},
48 {DRM_FORMAT_RGBA4444, 16, 4, 4, 4, 4, 12, 8, 4, 0},
49 {DRM_FORMAT_BGRA4444, 16, 4, 4, 4, 4, 0, 4, 8, 0},
51 {DRM_FORMAT_XRGB1555, 16, 5, 5, 5, 0, 10, 5, 0, 0},
52 {DRM_FORMAT_XBGR1555, 16, 5, 5, 5, 0, 0, 5, 10, 0},
53 {DRM_FORMAT_RGBX5551, 16, 5, 5, 5, 0, 11, 6, 1, 0},
54 {DRM_FORMAT_BGRX5551, 16, 5, 5, 5, 0, 1, 6, 11, 0},
56 {DRM_FORMAT_ARGB1555, 16, 5, 5, 5, 1, 10, 5, 0, 15},
57 {DRM_FORMAT_ABGR1555, 16, 5, 5, 5, 1, 0, 5, 10, 15},
58 {DRM_FORMAT_RGBA5551, 16, 5, 5, 5, 1, 11, 6, 1, 0},
59 {DRM_FORMAT_BGRA5551, 16, 5, 5, 5, 1, 1, 6, 11, 0},
61 {DRM_FORMAT_RGB565, 16, 5, 6, 5, 0, 11, 5, 0, 0},
62 {DRM_FORMAT_BGR565, 16, 5, 6, 5, 0, 0, 5, 11, 0},
64 {DRM_FORMAT_RGB888, 24, 8, 8, 8, 0, 16, 8, 0, 0},
65 {DRM_FORMAT_BGR888, 24, 8, 8, 8, 0, 0, 8, 16, 0},
67 {DRM_FORMAT_XRGB8888, 32, 8, 8, 8, 0, 16, 8, 0, 0},
68 {DRM_FORMAT_XBGR8888, 32, 8, 8, 8, 0, 0, 8, 16, 0},
69 {DRM_FORMAT_RGBX8888, 32, 8, 8, 8, 0, 24, 16, 8, 0},
70 {DRM_FORMAT_BGRX8888, 32, 8, 8, 8, 0, 8, 16, 24, 0},
72 {DRM_FORMAT_ARGB8888, 32, 8, 8, 8, 8, 16, 8, 0, 24},
73 {DRM_FORMAT_ABGR8888, 32, 8, 8, 8, 8, 0, 8, 16, 24},
74 {DRM_FORMAT_RGBA8888, 32, 8, 8, 8, 8, 24, 16, 8, 0},
75 {DRM_FORMAT_BGRA8888, 32, 8, 8, 8, 8, 8, 16, 24, 0},
77 {DRM_FORMAT_XRGB2101010, 32, 10, 10, 10, 0, 20, 10, 0, 0},
78 {DRM_FORMAT_XBGR2101010, 32, 10, 10, 10, 0, 0, 10, 20, 0},
79 {DRM_FORMAT_RGBX1010102, 32, 10, 10, 10, 0, 22, 12, 2, 0},
80 {DRM_FORMAT_BGRX1010102, 32, 10, 10, 10, 0, 2, 12, 22, 0},
82 {DRM_FORMAT_ARGB2101010, 32, 10, 10, 10, 2, 20, 10, 0, 30},
83 {DRM_FORMAT_ABGR2101010, 32, 10, 10, 10, 2, 0, 10, 20, 30},
84 {DRM_FORMAT_RGBA1010102, 32, 10, 10, 10, 2, 22, 12, 2, 0},
85 {DRM_FORMAT_BGRA1010102, 32, 10, 10, 10, 2, 2, 12, 22, 0},
88 static u32 drm_fourcc_from_fb_var(struct fb_var_screeninfo *var)
91 for (i = 0; i < ARRAY_SIZE(format_table); i++) {
92 const struct adf_fbdev_format *f = &format_table[i];
93 if (var->red.length == f->r_length &&
94 var->red.offset == f->r_offset &&
95 var->green.length == f->g_length &&
96 var->green.offset == f->g_offset &&
97 var->blue.length == f->b_length &&
98 var->blue.offset == f->b_offset &&
99 var->transp.length == f->a_length &&
100 (var->transp.length == 0 ||
101 var->transp.offset == f->a_offset))
108 static const struct adf_fbdev_format *fbdev_format_info(u32 format)
111 for (i = 0; i < ARRAY_SIZE(format_table); i++) {
112 const struct adf_fbdev_format *f = &format_table[i];
113 if (f->fourcc == format)
120 void adf_modeinfo_to_fb_videomode(const struct drm_mode_modeinfo *mode,
121 struct fb_videomode *vmode)
123 memset(vmode, 0, sizeof(*vmode));
125 vmode->refresh = mode->vrefresh;
127 vmode->xres = mode->hdisplay;
128 vmode->yres = mode->vdisplay;
130 vmode->pixclock = mode->clock ? KHZ2PICOS(mode->clock) : 0;
131 vmode->left_margin = mode->htotal - mode->hsync_end;
132 vmode->right_margin = mode->hsync_start - mode->hdisplay;
133 vmode->upper_margin = mode->vtotal - mode->vsync_end;
134 vmode->lower_margin = mode->vsync_start - mode->vdisplay;
135 vmode->hsync_len = mode->hsync_end - mode->hsync_start;
136 vmode->vsync_len = mode->vsync_end - mode->vsync_start;
139 if (mode->flags & DRM_MODE_FLAG_PHSYNC)
140 vmode->sync |= FB_SYNC_HOR_HIGH_ACT;
141 if (mode->flags & DRM_MODE_FLAG_PVSYNC)
142 vmode->sync |= FB_SYNC_VERT_HIGH_ACT;
143 if (mode->flags & DRM_MODE_FLAG_PCSYNC)
144 vmode->sync |= FB_SYNC_COMP_HIGH_ACT;
145 if (mode->flags & DRM_MODE_FLAG_BCAST)
146 vmode->sync |= FB_SYNC_BROADCAST;
149 if (mode->flags & DRM_MODE_FLAG_INTERLACE)
150 vmode->vmode |= FB_VMODE_INTERLACED;
151 if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
152 vmode->vmode |= FB_VMODE_DOUBLE;
154 EXPORT_SYMBOL(adf_modeinfo_to_fb_videomode);
156 void adf_modeinfo_from_fb_videomode(const struct fb_videomode *vmode,
157 struct drm_mode_modeinfo *mode)
159 memset(mode, 0, sizeof(*mode));
161 mode->hdisplay = vmode->xres;
162 mode->hsync_start = mode->hdisplay + vmode->right_margin;
163 mode->hsync_end = mode->hsync_start + vmode->hsync_len;
164 mode->htotal = mode->hsync_end + vmode->left_margin;
166 mode->vdisplay = vmode->yres;
167 mode->vsync_start = mode->vdisplay + vmode->lower_margin;
168 mode->vsync_end = mode->vsync_start + vmode->vsync_len;
169 mode->vtotal = mode->vsync_end + vmode->upper_margin;
171 mode->clock = vmode->pixclock ? PICOS2KHZ(vmode->pixclock) : 0;
174 if (vmode->sync & FB_SYNC_HOR_HIGH_ACT)
175 mode->flags |= DRM_MODE_FLAG_PHSYNC;
176 if (vmode->sync & FB_SYNC_VERT_HIGH_ACT)
177 mode->flags |= DRM_MODE_FLAG_PVSYNC;
178 if (vmode->sync & FB_SYNC_COMP_HIGH_ACT)
179 mode->flags |= DRM_MODE_FLAG_PCSYNC;
180 if (vmode->sync & FB_SYNC_BROADCAST)
181 mode->flags |= DRM_MODE_FLAG_BCAST;
182 if (vmode->vmode & FB_VMODE_INTERLACED)
183 mode->flags |= DRM_MODE_FLAG_INTERLACE;
184 if (vmode->vmode & FB_VMODE_DOUBLE)
185 mode->flags |= DRM_MODE_FLAG_DBLSCAN;
188 mode->vrefresh = vmode->refresh;
190 adf_modeinfo_set_vrefresh(mode);
193 strlcpy(mode->name, vmode->name, sizeof(mode->name));
195 adf_modeinfo_set_name(mode);
197 EXPORT_SYMBOL(adf_modeinfo_from_fb_videomode);
199 static int adf_fbdev_post(struct adf_fbdev *fbdev)
201 struct adf_buffer buf;
202 struct sync_fence *complete_fence;
205 memset(&buf, 0, sizeof(buf));
206 buf.overlay_engine = fbdev->eng;
207 buf.w = fbdev->info->var.xres;
208 buf.h = fbdev->info->var.yres;
209 buf.format = fbdev->format;
210 buf.dma_bufs[0] = fbdev->dma_buf;
211 buf.offset[0] = fbdev->offset +
212 fbdev->info->var.yoffset * fbdev->pitch +
213 fbdev->info->var.xoffset *
214 (fbdev->info->var.bits_per_pixel / 8);
215 buf.pitch[0] = fbdev->pitch;
218 complete_fence = adf_interface_simple_post(fbdev->intf, &buf);
219 if (IS_ERR(complete_fence)) {
220 ret = PTR_ERR(complete_fence);
224 sync_fence_put(complete_fence);
229 static const u16 vga_palette[][3] = {
230 {0x0000, 0x0000, 0x0000},
231 {0x0000, 0x0000, 0xAAAA},
232 {0x0000, 0xAAAA, 0x0000},
233 {0x0000, 0xAAAA, 0xAAAA},
234 {0xAAAA, 0x0000, 0x0000},
235 {0xAAAA, 0x0000, 0xAAAA},
236 {0xAAAA, 0x5555, 0x0000},
237 {0xAAAA, 0xAAAA, 0xAAAA},
238 {0x5555, 0x5555, 0x5555},
239 {0x5555, 0x5555, 0xFFFF},
240 {0x5555, 0xFFFF, 0x5555},
241 {0x5555, 0xFFFF, 0xFFFF},
242 {0xFFFF, 0x5555, 0x5555},
243 {0xFFFF, 0x5555, 0xFFFF},
244 {0xFFFF, 0xFFFF, 0x5555},
245 {0xFFFF, 0xFFFF, 0xFFFF},
248 static int adf_fb_alloc(struct adf_fbdev *fbdev)
252 ret = adf_interface_simple_buffer_alloc(fbdev->intf,
253 fbdev->default_xres_virtual,
254 fbdev->default_yres_virtual,
255 fbdev->default_format,
256 &fbdev->dma_buf, &fbdev->offset, &fbdev->pitch);
258 dev_err(fbdev->info->dev, "allocating fb failed: %d\n", ret);
262 fbdev->vaddr = dma_buf_vmap(fbdev->dma_buf);
265 dev_err(fbdev->info->dev, "vmapping fb failed\n");
268 fbdev->info->fix.line_length = fbdev->pitch;
269 fbdev->info->var.xres_virtual = fbdev->default_xres_virtual;
270 fbdev->info->var.yres_virtual = fbdev->default_yres_virtual;
271 fbdev->info->fix.smem_len = fbdev->dma_buf->size;
272 fbdev->info->screen_base = fbdev->vaddr;
277 dma_buf_put(fbdev->dma_buf);
281 static void adf_fb_destroy(struct adf_fbdev *fbdev)
283 dma_buf_vunmap(fbdev->dma_buf, fbdev->vaddr);
284 dma_buf_put(fbdev->dma_buf);
287 static void adf_fbdev_set_format(struct adf_fbdev *fbdev, u32 format)
290 const struct adf_fbdev_format *info = fbdev_format_info(format);
291 for (i = 0; i < ARRAY_SIZE(vga_palette); i++) {
292 u16 r = vga_palette[i][0];
293 u16 g = vga_palette[i][1];
294 u16 b = vga_palette[i][2];
296 r >>= (16 - info->r_length);
297 g >>= (16 - info->g_length);
298 b >>= (16 - info->b_length);
300 fbdev->pseudo_palette[i] =
301 (r << info->r_offset) |
302 (g << info->g_offset) |
303 (b << info->b_offset);
305 if (info->a_length) {
306 u16 a = BIT(info->a_length) - 1;
307 fbdev->pseudo_palette[i] |= (a << info->a_offset);
311 fbdev->info->var.bits_per_pixel = adf_format_bpp(format);
312 fbdev->info->var.red.length = info->r_length;
313 fbdev->info->var.red.offset = info->r_offset;
314 fbdev->info->var.green.length = info->g_length;
315 fbdev->info->var.green.offset = info->g_offset;
316 fbdev->info->var.blue.length = info->b_length;
317 fbdev->info->var.blue.offset = info->b_offset;
318 fbdev->info->var.transp.length = info->a_length;
319 fbdev->info->var.transp.offset = info->a_offset;
320 fbdev->format = format;
323 static void adf_fbdev_fill_modelist(struct adf_fbdev *fbdev)
325 struct drm_mode_modeinfo *modelist;
326 struct fb_videomode fbmode;
330 n_modes = adf_interface_modelist(fbdev->intf, NULL, 0);
331 modelist = kzalloc(sizeof(modelist[0]) * n_modes, GFP_KERNEL);
333 dev_warn(fbdev->info->dev, "allocating new modelist failed; keeping old modelist\n");
336 adf_interface_modelist(fbdev->intf, modelist, n_modes);
338 fb_destroy_modelist(&fbdev->info->modelist);
340 for (i = 0; i < n_modes; i++) {
341 adf_modeinfo_to_fb_videomode(&modelist[i], &fbmode);
342 ret = fb_add_videomode(&fbmode, &fbdev->info->modelist);
344 dev_warn(fbdev->info->dev, "adding mode %s to modelist failed: %d\n",
345 modelist[i].name, ret);
352 * adf_fbdev_open - default implementation of fbdev open op
354 int adf_fbdev_open(struct fb_info *info, int user)
356 struct adf_fbdev *fbdev = info->par;
359 mutex_lock(&fbdev->refcount_lock);
361 if (unlikely(fbdev->refcount == UINT_MAX)) {
366 if (!fbdev->refcount) {
367 struct drm_mode_modeinfo mode;
368 struct fb_videomode fbmode;
369 struct adf_device *dev = adf_interface_parent(fbdev->intf);
371 ret = adf_device_attach(dev, fbdev->eng, fbdev->intf);
372 if (ret < 0 && ret != -EALREADY)
375 ret = adf_fb_alloc(fbdev);
379 adf_interface_current_mode(fbdev->intf, &mode);
380 adf_modeinfo_to_fb_videomode(&mode, &fbmode);
381 fb_videomode_to_var(&fbdev->info->var, &fbmode);
383 adf_fbdev_set_format(fbdev, fbdev->default_format);
384 adf_fbdev_fill_modelist(fbdev);
387 ret = adf_fbdev_post(fbdev);
389 if (!fbdev->refcount)
390 adf_fb_destroy(fbdev);
396 mutex_unlock(&fbdev->refcount_lock);
399 EXPORT_SYMBOL(adf_fbdev_open);
402 * adf_fbdev_release - default implementation of fbdev release op
404 int adf_fbdev_release(struct fb_info *info, int user)
406 struct adf_fbdev *fbdev = info->par;
407 mutex_lock(&fbdev->refcount_lock);
408 BUG_ON(!fbdev->refcount);
410 if (!fbdev->refcount)
411 adf_fb_destroy(fbdev);
412 mutex_unlock(&fbdev->refcount_lock);
415 EXPORT_SYMBOL(adf_fbdev_release);
418 * adf_fbdev_check_var - default implementation of fbdev check_var op
420 int adf_fbdev_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
422 struct adf_fbdev *fbdev = info->par;
423 bool valid_format = true;
424 u32 format = drm_fourcc_from_fb_var(var);
425 u32 pitch = var->xres_virtual * var->bits_per_pixel / 8;
428 dev_dbg(info->dev, "%s: unrecognized format\n", __func__);
429 valid_format = false;
432 if (valid_format && var->grayscale) {
433 dev_dbg(info->dev, "%s: grayscale modes not supported\n",
435 valid_format = false;
438 if (valid_format && var->nonstd) {
439 dev_dbg(info->dev, "%s: nonstandard formats not supported\n",
441 valid_format = false;
444 if (valid_format && !adf_overlay_engine_supports_format(fbdev->eng,
446 char format_str[ADF_FORMAT_STR_SIZE];
447 adf_format_str(format, format_str);
448 dev_dbg(info->dev, "%s: format %s not supported by overlay engine %s\n",
449 __func__, format_str, fbdev->eng->base.name);
450 valid_format = false;
453 if (valid_format && pitch > fbdev->pitch) {
454 dev_dbg(info->dev, "%s: fb pitch too small for var (pitch = %u, xres_virtual = %u, bits_per_pixel = %u)\n",
455 __func__, fbdev->pitch, var->xres_virtual,
456 var->bits_per_pixel);
457 valid_format = false;
460 if (valid_format && var->yres_virtual > fbdev->default_yres_virtual) {
461 dev_dbg(info->dev, "%s: fb height too small for var (h = %u, yres_virtual = %u)\n",
462 __func__, fbdev->default_yres_virtual,
464 valid_format = false;
468 var->activate = info->var.activate;
469 var->height = info->var.height;
470 var->width = info->var.width;
471 var->accel_flags = info->var.accel_flags;
472 var->rotate = info->var.rotate;
473 var->colorspace = info->var.colorspace;
474 /* userspace can't change these */
476 /* if any part of the format is invalid then fixing it up is
477 impractical, so save just the modesetting bits and
478 overwrite everything else */
479 struct fb_videomode mode;
480 fb_var_to_videomode(&mode, var);
481 memcpy(var, &info->var, sizeof(*var));
482 fb_videomode_to_var(var, &mode);
487 EXPORT_SYMBOL(adf_fbdev_check_var);
490 * adf_fbdev_set_par - default implementation of fbdev set_par op
492 int adf_fbdev_set_par(struct fb_info *info)
494 struct adf_fbdev *fbdev = info->par;
495 struct adf_interface *intf = fbdev->intf;
496 struct fb_videomode vmode;
497 struct drm_mode_modeinfo mode;
499 u32 format = drm_fourcc_from_fb_var(&info->var);
501 fb_var_to_videomode(&vmode, &info->var);
502 adf_modeinfo_from_fb_videomode(&vmode, &mode);
503 ret = adf_interface_set_mode(intf, &mode);
507 ret = adf_fbdev_post(fbdev);
511 if (format != fbdev->format)
512 adf_fbdev_set_format(fbdev, format);
516 EXPORT_SYMBOL(adf_fbdev_set_par);
519 * adf_fbdev_blank - default implementation of fbdev blank op
521 int adf_fbdev_blank(int blank, struct fb_info *info)
523 struct adf_fbdev *fbdev = info->par;
524 struct adf_interface *intf = fbdev->intf;
528 case FB_BLANK_UNBLANK:
529 dpms_state = DRM_MODE_DPMS_ON;
531 case FB_BLANK_NORMAL:
532 dpms_state = DRM_MODE_DPMS_STANDBY;
534 case FB_BLANK_VSYNC_SUSPEND:
535 dpms_state = DRM_MODE_DPMS_SUSPEND;
537 case FB_BLANK_HSYNC_SUSPEND:
538 dpms_state = DRM_MODE_DPMS_STANDBY;
540 case FB_BLANK_POWERDOWN:
541 dpms_state = DRM_MODE_DPMS_OFF;
547 return adf_interface_blank(intf, dpms_state);
549 EXPORT_SYMBOL(adf_fbdev_blank);
552 * adf_fbdev_pan_display - default implementation of fbdev pan_display op
554 int adf_fbdev_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
556 struct adf_fbdev *fbdev = info->par;
557 return adf_fbdev_post(fbdev);
559 EXPORT_SYMBOL(adf_fbdev_pan_display);
562 * adf_fbdev_mmap - default implementation of fbdev mmap op
564 int adf_fbdev_mmap(struct fb_info *info, struct vm_area_struct *vma)
566 struct adf_fbdev *fbdev = info->par;
568 vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
569 return dma_buf_mmap(fbdev->dma_buf, vma, 0);
571 EXPORT_SYMBOL(adf_fbdev_mmap);
574 * adf_fbdev_init - initialize helper to wrap ADF device in fbdev API
576 * @fbdev: the fbdev helper
577 * @interface: the ADF interface that will display the framebuffer
578 * @eng: the ADF overlay engine that will scan out the framebuffer
579 * @xres_virtual: the virtual width of the framebuffer
580 * @yres_virtual: the virtual height of the framebuffer
581 * @format: the format of the framebuffer
582 * @fbops: the device's fbdev ops
583 * @fmt: formatting for the framebuffer identification string
584 * @...: variable arguments
586 * @format must be a standard, non-indexed RGB format, i.e.,
587 * adf_format_is_rgb(@format) && @format != @DRM_FORMAT_C8.
589 * Returns 0 on success or -errno on failure.
591 int adf_fbdev_init(struct adf_fbdev *fbdev, struct adf_interface *interface,
592 struct adf_overlay_engine *eng,
593 u16 xres_virtual, u16 yres_virtual, u32 format,
594 struct fb_ops *fbops, const char *fmt, ...)
596 struct adf_device *parent = adf_interface_parent(interface);
597 struct device *dev = &parent->base.dev;
598 u16 width_mm, height_mm;
602 if (!adf_format_is_rgb(format) ||
603 format == DRM_FORMAT_C8) {
604 dev_err(dev, "fbdev helper does not support format %u\n",
609 memset(fbdev, 0, sizeof(*fbdev));
610 fbdev->intf = interface;
612 fbdev->info = framebuffer_alloc(0, dev);
614 dev_err(dev, "allocating framebuffer device failed\n");
617 mutex_init(&fbdev->refcount_lock);
618 fbdev->default_xres_virtual = xres_virtual;
619 fbdev->default_yres_virtual = yres_virtual;
620 fbdev->default_format = format;
622 fbdev->info->flags = FBINFO_FLAG_DEFAULT;
623 ret = adf_interface_get_screen_size(interface, &width_mm, &height_mm);
628 fbdev->info->var.width = width_mm;
629 fbdev->info->var.height = height_mm;
630 fbdev->info->var.activate = FB_ACTIVATE_VBL;
632 vsnprintf(fbdev->info->fix.id, sizeof(fbdev->info->fix.id), fmt, args);
634 fbdev->info->fix.type = FB_TYPE_PACKED_PIXELS;
635 fbdev->info->fix.visual = FB_VISUAL_TRUECOLOR;
636 fbdev->info->fix.xpanstep = 1;
637 fbdev->info->fix.ypanstep = 1;
638 INIT_LIST_HEAD(&fbdev->info->modelist);
639 fbdev->info->fbops = fbops;
640 fbdev->info->pseudo_palette = fbdev->pseudo_palette;
641 fbdev->info->par = fbdev;
643 ret = register_framebuffer(fbdev->info);
645 dev_err(dev, "registering framebuffer failed: %d\n", ret);
651 EXPORT_SYMBOL(adf_fbdev_init);
654 * adf_fbdev_destroy - destroy helper to wrap ADF device in fbdev API
656 * @fbdev: the fbdev helper
658 void adf_fbdev_destroy(struct adf_fbdev *fbdev)
660 unregister_framebuffer(fbdev->info);
661 BUG_ON(fbdev->refcount);
662 mutex_destroy(&fbdev->refcount_lock);
663 framebuffer_release(fbdev->info);
665 EXPORT_SYMBOL(adf_fbdev_destroy);