video: adf: fix fbdev blank -> dpms state mapping
[firefly-linux-kernel-4.4.55.git] / drivers / video / adf / adf_fbdev.c
1 /*
2  * Copyright (C) 2013 Google, Inc.
3  *
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.
7  *
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.
12  *
13  */
14
15 #include <linux/vmalloc.h>
16
17 #include <video/adf.h>
18 #include <video/adf_client.h>
19 #include <video/adf_fbdev.h>
20 #include <video/adf_format.h>
21
22 #include "adf.h"
23
24 struct adf_fbdev_format {
25         u32 fourcc;
26         u32 bpp;
27         u32 r_length;
28         u32 g_length;
29         u32 b_length;
30         u32 a_length;
31         u32 r_offset;
32         u32 g_offset;
33         u32 b_offset;
34         u32 a_offset;
35 };
36
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},
40
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},
45
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},
50
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},
55
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},
60
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},
63
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},
66
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},
71
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},
76
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},
81
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},
86 };
87
88 static u32 drm_fourcc_from_fb_var(struct fb_var_screeninfo *var)
89 {
90         size_t i;
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))
102                         return f->fourcc;
103         }
104
105         return 0;
106 }
107
108 static const struct adf_fbdev_format *fbdev_format_info(u32 format)
109 {
110         size_t i;
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)
114                         return f;
115         }
116
117         BUG();
118 }
119
120 void adf_modeinfo_to_fb_videomode(const struct drm_mode_modeinfo *mode,
121                 struct fb_videomode *vmode)
122 {
123         memset(vmode, 0, sizeof(*vmode));
124
125         vmode->refresh = mode->vrefresh;
126
127         vmode->xres = mode->hdisplay;
128         vmode->yres = mode->vdisplay;
129
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;
137
138         vmode->sync = 0;
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;
147
148         vmode->vmode = 0;
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;
153 }
154 EXPORT_SYMBOL(adf_modeinfo_to_fb_videomode);
155
156 void adf_modeinfo_from_fb_videomode(const struct fb_videomode *vmode,
157                 struct drm_mode_modeinfo *mode)
158 {
159         memset(mode, 0, sizeof(*mode));
160
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;
165
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;
170
171         mode->clock = vmode->pixclock ? PICOS2KHZ(vmode->pixclock) : 0;
172
173         mode->flags = 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;
186
187         if (vmode->refresh)
188                 mode->vrefresh = vmode->refresh;
189         else
190                 adf_modeinfo_set_vrefresh(mode);
191
192         if (vmode->name)
193                 strlcpy(mode->name, vmode->name, sizeof(mode->name));
194         else
195                 adf_modeinfo_set_name(mode);
196 }
197 EXPORT_SYMBOL(adf_modeinfo_from_fb_videomode);
198
199 static int adf_fbdev_post(struct adf_fbdev *fbdev)
200 {
201         struct adf_buffer buf;
202         struct sync_fence *complete_fence;
203         int ret = 0;
204
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;
216         buf.n_planes = 1;
217
218         complete_fence = adf_interface_simple_post(fbdev->intf, &buf);
219         if (IS_ERR(complete_fence)) {
220                 ret = PTR_ERR(complete_fence);
221                 goto done;
222         }
223
224         sync_fence_put(complete_fence);
225 done:
226         return ret;
227 }
228
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},
246 };
247
248 static int adf_fb_alloc(struct adf_fbdev *fbdev)
249 {
250         int ret;
251
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);
257         if (ret < 0) {
258                 dev_err(fbdev->info->dev, "allocating fb failed: %d\n", ret);
259                 return ret;
260         }
261
262         fbdev->vaddr = dma_buf_vmap(fbdev->dma_buf);
263         if (!fbdev->vaddr) {
264                 ret = -ENOMEM;
265                 dev_err(fbdev->info->dev, "vmapping fb failed\n");
266                 goto err_vmap;
267         }
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;
273
274         return 0;
275
276 err_vmap:
277         dma_buf_put(fbdev->dma_buf);
278         return ret;
279 }
280
281 static void adf_fb_destroy(struct adf_fbdev *fbdev)
282 {
283         dma_buf_vunmap(fbdev->dma_buf, fbdev->vaddr);
284         dma_buf_put(fbdev->dma_buf);
285 }
286
287 static void adf_fbdev_set_format(struct adf_fbdev *fbdev, u32 format)
288 {
289         size_t i;
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];
295
296                 r >>= (16 - info->r_length);
297                 g >>= (16 - info->g_length);
298                 b >>= (16 - info->b_length);
299
300                 fbdev->pseudo_palette[i] =
301                         (r << info->r_offset) |
302                         (g << info->g_offset) |
303                         (b << info->b_offset);
304
305                 if (info->a_length) {
306                         u16 a = BIT(info->a_length) - 1;
307                         fbdev->pseudo_palette[i] |= (a << info->a_offset);
308                 }
309         }
310
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;
321 }
322
323 static void adf_fbdev_fill_modelist(struct adf_fbdev *fbdev)
324 {
325         struct drm_mode_modeinfo *modelist;
326         struct fb_videomode fbmode;
327         size_t n_modes, i;
328         int ret = 0;
329
330         n_modes = adf_interface_modelist(fbdev->intf, NULL, 0);
331         modelist = kzalloc(sizeof(modelist[0]) * n_modes, GFP_KERNEL);
332         if (!modelist) {
333                 dev_warn(fbdev->info->dev, "allocating new modelist failed; keeping old modelist\n");
334                 return;
335         }
336         adf_interface_modelist(fbdev->intf, modelist, n_modes);
337
338         fb_destroy_modelist(&fbdev->info->modelist);
339
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);
343                 if (ret < 0)
344                         dev_warn(fbdev->info->dev, "adding mode %s to modelist failed: %d\n",
345                                         modelist[i].name, ret);
346         }
347
348         kfree(modelist);
349 }
350
351 /**
352  * adf_fbdev_open - default implementation of fbdev open op
353  */
354 int adf_fbdev_open(struct fb_info *info, int user)
355 {
356         struct adf_fbdev *fbdev = info->par;
357         int ret;
358
359         if (!fbdev->open) {
360                 struct drm_mode_modeinfo mode;
361                 struct fb_videomode fbmode;
362                 struct adf_device *dev = adf_interface_parent(fbdev->intf);
363
364                 ret = adf_device_attach(dev, fbdev->eng, fbdev->intf);
365                 if (ret < 0 && ret != -EALREADY)
366                         return ret;
367
368                 ret = adf_fb_alloc(fbdev);
369                 if (ret < 0)
370                         return ret;
371
372                 adf_interface_current_mode(fbdev->intf, &mode);
373                 adf_modeinfo_to_fb_videomode(&mode, &fbmode);
374                 fb_videomode_to_var(&fbdev->info->var, &fbmode);
375
376                 adf_fbdev_set_format(fbdev, fbdev->default_format);
377                 adf_fbdev_fill_modelist(fbdev);
378         }
379
380         ret = adf_fbdev_post(fbdev);
381         if (ret < 0) {
382                 if (!fbdev->open)
383                         adf_fb_destroy(fbdev);
384                 return ret;
385         }
386
387         fbdev->open = true;
388         return 0;
389 }
390 EXPORT_SYMBOL(adf_fbdev_open);
391
392 /**
393  * adf_fbdev_release - default implementation of fbdev release op
394  */
395 int adf_fbdev_release(struct fb_info *info, int user)
396 {
397         struct adf_fbdev *fbdev = info->par;
398         adf_fb_destroy(fbdev);
399         fbdev->open = false;
400         return 0;
401 }
402 EXPORT_SYMBOL(adf_fbdev_release);
403
404 /**
405  * adf_fbdev_check_var - default implementation of fbdev check_var op
406  */
407 int adf_fbdev_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
408 {
409         struct adf_fbdev *fbdev = info->par;
410         bool valid_format = true;
411         u32 format = drm_fourcc_from_fb_var(var);
412         u32 pitch = var->xres_virtual * var->bits_per_pixel / 8;
413
414         if (!format) {
415                 dev_dbg(info->dev, "%s: unrecognized format\n", __func__);
416                 valid_format = false;
417         }
418
419         if (valid_format && var->grayscale) {
420                 dev_dbg(info->dev, "%s: grayscale modes not supported\n",
421                                 __func__);
422                 valid_format = false;
423         }
424
425         if (valid_format && var->nonstd) {
426                 dev_dbg(info->dev, "%s: nonstandard formats not supported\n",
427                                 __func__);
428                 valid_format = false;
429         }
430
431         if (valid_format && !adf_overlay_engine_supports_format(fbdev->eng,
432                         format)) {
433                 char format_str[ADF_FORMAT_STR_SIZE];
434                 adf_format_str(format, format_str);
435                 dev_dbg(info->dev, "%s: format %s not supported by overlay engine %s\n",
436                                 __func__, format_str, fbdev->eng->base.name);
437                 valid_format = false;
438         }
439
440         if (valid_format && pitch > fbdev->pitch) {
441                 dev_dbg(info->dev, "%s: fb pitch too small for var (pitch = %u, xres_virtual = %u, bits_per_pixel = %u)\n",
442                                 __func__, fbdev->pitch, var->xres_virtual,
443                                 var->bits_per_pixel);
444                 valid_format = false;
445         }
446
447         if (valid_format && var->yres_virtual > fbdev->default_yres_virtual) {
448                 dev_dbg(info->dev, "%s: fb height too small for var (h = %u, yres_virtual = %u)\n",
449                                 __func__, fbdev->default_yres_virtual,
450                                 var->yres_virtual);
451                 valid_format = false;
452         }
453
454         if (valid_format) {
455                 var->activate = info->var.activate;
456                 var->height = info->var.height;
457                 var->width = info->var.width;
458                 var->accel_flags = info->var.accel_flags;
459                 var->rotate = info->var.rotate;
460                 var->colorspace = info->var.colorspace;
461                 /* userspace can't change these */
462         } else {
463                 /* if any part of the format is invalid then fixing it up is
464                    impractical, so save just the modesetting bits and
465                    overwrite everything else */
466                 struct fb_videomode mode;
467                 fb_var_to_videomode(&mode, var);
468                 memcpy(var, &info->var, sizeof(*var));
469                 fb_videomode_to_var(var, &mode);
470         }
471
472         return 0;
473 }
474 EXPORT_SYMBOL(adf_fbdev_check_var);
475
476 /**
477  * adf_fbdev_set_par - default implementation of fbdev set_par op
478  */
479 int adf_fbdev_set_par(struct fb_info *info)
480 {
481         struct adf_fbdev *fbdev = info->par;
482         struct adf_interface *intf = fbdev->intf;
483         struct fb_videomode vmode;
484         struct drm_mode_modeinfo mode;
485         int ret;
486         u32 format = drm_fourcc_from_fb_var(&info->var);
487
488         fb_var_to_videomode(&vmode, &info->var);
489         adf_modeinfo_from_fb_videomode(&vmode, &mode);
490         ret = adf_interface_set_mode(intf, &mode);
491         if (ret < 0)
492                 return ret;
493
494         ret = adf_fbdev_post(fbdev);
495         if (ret < 0)
496                 return ret;
497
498         if (format != fbdev->format)
499                 adf_fbdev_set_format(fbdev, format);
500
501         return 0;
502 }
503 EXPORT_SYMBOL(adf_fbdev_set_par);
504
505 /**
506  * adf_fbdev_blank - default implementation of fbdev blank op
507  */
508 int adf_fbdev_blank(int blank, struct fb_info *info)
509 {
510         struct adf_fbdev *fbdev = info->par;
511         struct adf_interface *intf = fbdev->intf;
512         u8 dpms_state;
513
514         switch (blank) {
515         case FB_BLANK_UNBLANK:
516                 dpms_state = DRM_MODE_DPMS_ON;
517                 break;
518         case FB_BLANK_NORMAL:
519                 dpms_state = DRM_MODE_DPMS_STANDBY;
520                 break;
521         case FB_BLANK_VSYNC_SUSPEND:
522                 dpms_state = DRM_MODE_DPMS_SUSPEND;
523                 break;
524         case FB_BLANK_HSYNC_SUSPEND:
525                 dpms_state = DRM_MODE_DPMS_STANDBY;
526                 break;
527         case FB_BLANK_POWERDOWN:
528                 dpms_state = DRM_MODE_DPMS_OFF;
529                 break;
530         default:
531                 return -EINVAL;
532         }
533
534         return adf_interface_blank(intf, dpms_state);
535 }
536 EXPORT_SYMBOL(adf_fbdev_blank);
537
538 /**
539  * adf_fbdev_pan_display - default implementation of fbdev pan_display op
540  */
541 int adf_fbdev_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
542 {
543         struct adf_fbdev *fbdev = info->par;
544         return adf_fbdev_post(fbdev);
545 }
546 EXPORT_SYMBOL(adf_fbdev_pan_display);
547
548 /**
549  * adf_fbdev_mmap - default implementation of fbdev mmap op
550  */
551 int adf_fbdev_mmap(struct fb_info *info, struct vm_area_struct *vma)
552 {
553         struct adf_fbdev *fbdev = info->par;
554
555         vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
556         return dma_buf_mmap(fbdev->dma_buf, vma, 0);
557 }
558 EXPORT_SYMBOL(adf_fbdev_mmap);
559
560 /**
561  * adf_fbdev_init - initialize helper to wrap ADF device in fbdev API
562  *
563  * @fbdev: the fbdev helper
564  * @interface: the ADF interface that will display the framebuffer
565  * @eng: the ADF overlay engine that will scan out the framebuffer
566  * @xres_virtual: the virtual width of the framebuffer
567  * @yres_virtual: the virtual height of the framebuffer
568  * @format: the format of the framebuffer
569  * @fbops: the device's fbdev ops
570  * @fmt: formatting for the framebuffer identification string
571  * @...: variable arguments
572  *
573  * @format must be a standard, non-indexed RGB format, i.e.,
574  * adf_format_is_rgb(@format) && @format != @DRM_FORMAT_C8.
575  *
576  * Returns 0 on success or -errno on failure.
577  */
578 int adf_fbdev_init(struct adf_fbdev *fbdev, struct adf_interface *interface,
579                 struct adf_overlay_engine *eng,
580                 u16 xres_virtual, u16 yres_virtual, u32 format,
581                 struct fb_ops *fbops, const char *fmt, ...)
582 {
583         struct adf_device *parent = adf_interface_parent(interface);
584         struct device *dev = &parent->base.dev;
585         u16 width_mm, height_mm;
586         va_list args;
587         int ret;
588
589         if (!adf_format_is_rgb(format) ||
590                         format == DRM_FORMAT_C8) {
591                 dev_err(dev, "fbdev helper does not support format %u\n",
592                                 format);
593                 return -EINVAL;
594         }
595
596         memset(fbdev, 0, sizeof(*fbdev));
597         fbdev->intf = interface;
598         fbdev->eng = eng;
599         fbdev->info = framebuffer_alloc(0, dev);
600         if (!fbdev->info) {
601                 dev_err(dev, "allocating framebuffer device failed\n");
602                 return -ENOMEM;
603         }
604         fbdev->default_xres_virtual = xres_virtual;
605         fbdev->default_yres_virtual = yres_virtual;
606         fbdev->default_format = format;
607
608         fbdev->info->flags = FBINFO_FLAG_DEFAULT;
609         ret = adf_interface_get_screen_size(interface, &width_mm, &height_mm);
610         if (ret < 0) {
611                 width_mm = 0;
612                 height_mm = 0;
613         }
614         fbdev->info->var.width = width_mm;
615         fbdev->info->var.height = height_mm;
616         fbdev->info->var.activate = FB_ACTIVATE_VBL;
617         va_start(args, fmt);
618         vsnprintf(fbdev->info->fix.id, sizeof(fbdev->info->fix.id), fmt, args);
619         va_end(args);
620         fbdev->info->fix.type = FB_TYPE_PACKED_PIXELS;
621         fbdev->info->fix.visual = FB_VISUAL_TRUECOLOR;
622         fbdev->info->fix.xpanstep = 1;
623         fbdev->info->fix.ypanstep = 1;
624         INIT_LIST_HEAD(&fbdev->info->modelist);
625         fbdev->info->fbops = fbops;
626         fbdev->info->pseudo_palette = fbdev->pseudo_palette;
627         fbdev->info->par = fbdev;
628
629         ret = register_framebuffer(fbdev->info);
630         if (ret < 0) {
631                 dev_err(dev, "registering framebuffer failed: %d\n", ret);
632                 return ret;
633         }
634
635         return 0;
636 }
637 EXPORT_SYMBOL(adf_fbdev_init);
638
639 /**
640  * adf_fbdev_destroy - destroy helper to wrap ADF device in fbdev API
641  *
642  * @fbdev: the fbdev helper
643  */
644 void adf_fbdev_destroy(struct adf_fbdev *fbdev)
645 {
646         unregister_framebuffer(fbdev->info);
647         if (WARN_ON(fbdev->open))
648                 adf_fb_destroy(fbdev);
649         framebuffer_release(fbdev->info);
650 }
651 EXPORT_SYMBOL(adf_fbdev_destroy);