• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
drm_fourcc_from_fb_var(struct fb_var_screeninfo * var)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 
fbdev_format_info(u32 format)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 
adf_modeinfo_to_fb_videomode(const struct drm_mode_modeinfo * mode,struct fb_videomode * vmode)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 
adf_modeinfo_from_fb_videomode(const struct fb_videomode * vmode,struct drm_mode_modeinfo * mode)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 
adf_fbdev_post(struct adf_fbdev * fbdev)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 
adf_fb_alloc(struct adf_fbdev * fbdev)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 
adf_fb_destroy(struct adf_fbdev * fbdev)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 
adf_fbdev_set_format(struct adf_fbdev * fbdev,u32 format)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 
adf_fbdev_fill_modelist(struct adf_fbdev * fbdev)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  */
adf_fbdev_open(struct fb_info * info,int user)354 int adf_fbdev_open(struct fb_info *info, int user)
355 {
356 	struct adf_fbdev *fbdev = info->par;
357 	int ret;
358 
359 	mutex_lock(&fbdev->refcount_lock);
360 
361 	if (unlikely(fbdev->refcount == UINT_MAX)) {
362 		ret = -EMFILE;
363 		goto done;
364 	}
365 
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);
370 
371 		ret = adf_device_attach(dev, fbdev->eng, fbdev->intf);
372 		if (ret < 0 && ret != -EALREADY)
373 			goto done;
374 
375 		ret = adf_fb_alloc(fbdev);
376 		if (ret < 0)
377 			goto done;
378 
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);
382 
383 		adf_fbdev_set_format(fbdev, fbdev->default_format);
384 		adf_fbdev_fill_modelist(fbdev);
385 	}
386 
387 	ret = adf_fbdev_post(fbdev);
388 	if (ret < 0) {
389 		if (!fbdev->refcount)
390 			adf_fb_destroy(fbdev);
391 		goto done;
392 	}
393 
394 	fbdev->refcount++;
395 done:
396 	mutex_unlock(&fbdev->refcount_lock);
397 	return ret;
398 }
399 EXPORT_SYMBOL(adf_fbdev_open);
400 
401 /**
402  * adf_fbdev_release - default implementation of fbdev release op
403  */
adf_fbdev_release(struct fb_info * info,int user)404 int adf_fbdev_release(struct fb_info *info, int user)
405 {
406 	struct adf_fbdev *fbdev = info->par;
407 	mutex_lock(&fbdev->refcount_lock);
408 	BUG_ON(!fbdev->refcount);
409 	fbdev->refcount--;
410 	if (!fbdev->refcount)
411 		adf_fb_destroy(fbdev);
412 	mutex_unlock(&fbdev->refcount_lock);
413 	return 0;
414 }
415 EXPORT_SYMBOL(adf_fbdev_release);
416 
417 /**
418  * adf_fbdev_check_var - default implementation of fbdev check_var op
419  */
adf_fbdev_check_var(struct fb_var_screeninfo * var,struct fb_info * info)420 int adf_fbdev_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
421 {
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;
426 
427 	if (!format) {
428 		dev_dbg(info->dev, "%s: unrecognized format\n", __func__);
429 		valid_format = false;
430 	}
431 
432 	if (valid_format && var->grayscale) {
433 		dev_dbg(info->dev, "%s: grayscale modes not supported\n",
434 				__func__);
435 		valid_format = false;
436 	}
437 
438 	if (valid_format && var->nonstd) {
439 		dev_dbg(info->dev, "%s: nonstandard formats not supported\n",
440 				__func__);
441 		valid_format = false;
442 	}
443 
444 	if (valid_format && !adf_overlay_engine_supports_format(fbdev->eng,
445 			format)) {
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;
451 	}
452 
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;
458 	}
459 
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,
463 				var->yres_virtual);
464 		valid_format = false;
465 	}
466 
467 	if (valid_format) {
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 */
475 	} else {
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);
483 	}
484 
485 	return 0;
486 }
487 EXPORT_SYMBOL(adf_fbdev_check_var);
488 
489 /**
490  * adf_fbdev_set_par - default implementation of fbdev set_par op
491  */
adf_fbdev_set_par(struct fb_info * info)492 int adf_fbdev_set_par(struct fb_info *info)
493 {
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;
498 	int ret;
499 	u32 format = drm_fourcc_from_fb_var(&info->var);
500 
501 	fb_var_to_videomode(&vmode, &info->var);
502 	adf_modeinfo_from_fb_videomode(&vmode, &mode);
503 	ret = adf_interface_set_mode(intf, &mode);
504 	if (ret < 0)
505 		return ret;
506 
507 	ret = adf_fbdev_post(fbdev);
508 	if (ret < 0)
509 		return ret;
510 
511 	if (format != fbdev->format)
512 		adf_fbdev_set_format(fbdev, format);
513 
514 	return 0;
515 }
516 EXPORT_SYMBOL(adf_fbdev_set_par);
517 
518 /**
519  * adf_fbdev_blank - default implementation of fbdev blank op
520  */
adf_fbdev_blank(int blank,struct fb_info * info)521 int adf_fbdev_blank(int blank, struct fb_info *info)
522 {
523 	struct adf_fbdev *fbdev = info->par;
524 	struct adf_interface *intf = fbdev->intf;
525 	u8 dpms_state;
526 
527 	switch (blank) {
528 	case FB_BLANK_UNBLANK:
529 		dpms_state = DRM_MODE_DPMS_ON;
530 		break;
531 	case FB_BLANK_NORMAL:
532 		dpms_state = DRM_MODE_DPMS_STANDBY;
533 		break;
534 	case FB_BLANK_VSYNC_SUSPEND:
535 		dpms_state = DRM_MODE_DPMS_SUSPEND;
536 		break;
537 	case FB_BLANK_HSYNC_SUSPEND:
538 		dpms_state = DRM_MODE_DPMS_STANDBY;
539 		break;
540 	case FB_BLANK_POWERDOWN:
541 		dpms_state = DRM_MODE_DPMS_OFF;
542 		break;
543 	default:
544 		return -EINVAL;
545 	}
546 
547 	return adf_interface_blank(intf, dpms_state);
548 }
549 EXPORT_SYMBOL(adf_fbdev_blank);
550 
551 /**
552  * adf_fbdev_pan_display - default implementation of fbdev pan_display op
553  */
adf_fbdev_pan_display(struct fb_var_screeninfo * var,struct fb_info * info)554 int adf_fbdev_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
555 {
556 	struct adf_fbdev *fbdev = info->par;
557 	return adf_fbdev_post(fbdev);
558 }
559 EXPORT_SYMBOL(adf_fbdev_pan_display);
560 
561 /**
562  * adf_fbdev_mmap - default implementation of fbdev mmap op
563  */
adf_fbdev_mmap(struct fb_info * info,struct vm_area_struct * vma)564 int adf_fbdev_mmap(struct fb_info *info, struct vm_area_struct *vma)
565 {
566 	struct adf_fbdev *fbdev = info->par;
567 
568 	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
569 	return dma_buf_mmap(fbdev->dma_buf, vma, 0);
570 }
571 EXPORT_SYMBOL(adf_fbdev_mmap);
572 
573 /**
574  * adf_fbdev_init - initialize helper to wrap ADF device in fbdev API
575  *
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
585  *
586  * @format must be a standard, non-indexed RGB format, i.e.,
587  * adf_format_is_rgb(@format) && @format != @DRM_FORMAT_C8.
588  *
589  * Returns 0 on success or -errno on failure.
590  */
adf_fbdev_init(struct adf_fbdev * fbdev,struct adf_interface * interface,struct adf_overlay_engine * eng,u16 xres_virtual,u16 yres_virtual,u32 format,struct fb_ops * fbops,const char * fmt,...)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, ...)
595 {
596 	struct adf_device *parent = adf_interface_parent(interface);
597 	struct device *dev = &parent->base.dev;
598 	u16 width_mm, height_mm;
599 	va_list args;
600 	int ret;
601 
602 	if (!adf_format_is_rgb(format) ||
603 			format == DRM_FORMAT_C8) {
604 		dev_err(dev, "fbdev helper does not support format %u\n",
605 				format);
606 		return -EINVAL;
607 	}
608 
609 	memset(fbdev, 0, sizeof(*fbdev));
610 	fbdev->intf = interface;
611 	fbdev->eng = eng;
612 	fbdev->info = framebuffer_alloc(0, dev);
613 	if (!fbdev->info) {
614 		dev_err(dev, "allocating framebuffer device failed\n");
615 		return -ENOMEM;
616 	}
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;
621 
622 	fbdev->info->flags = FBINFO_FLAG_DEFAULT;
623 	ret = adf_interface_get_screen_size(interface, &width_mm, &height_mm);
624 	if (ret < 0) {
625 		width_mm = 0;
626 		height_mm = 0;
627 	}
628 	fbdev->info->var.width = width_mm;
629 	fbdev->info->var.height = height_mm;
630 	fbdev->info->var.activate = FB_ACTIVATE_VBL;
631 	va_start(args, fmt);
632 	vsnprintf(fbdev->info->fix.id, sizeof(fbdev->info->fix.id), fmt, args);
633 	va_end(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;
642 
643 	ret = register_framebuffer(fbdev->info);
644 	if (ret < 0) {
645 		dev_err(dev, "registering framebuffer failed: %d\n", ret);
646 		return ret;
647 	}
648 
649 	return 0;
650 }
651 EXPORT_SYMBOL(adf_fbdev_init);
652 
653 /**
654  * adf_fbdev_destroy - destroy helper to wrap ADF device in fbdev API
655  *
656  * @fbdev: the fbdev helper
657  */
adf_fbdev_destroy(struct adf_fbdev * fbdev)658 void adf_fbdev_destroy(struct adf_fbdev *fbdev)
659 {
660 	unregister_framebuffer(fbdev->info);
661 	BUG_ON(fbdev->refcount);
662 	mutex_destroy(&fbdev->refcount_lock);
663 	framebuffer_release(fbdev->info);
664 }
665 EXPORT_SYMBOL(adf_fbdev_destroy);
666