Branch data Line data Source code
1 : : /*
2 : : * linux/drivers/video/bcm2708_fb.c
3 : : *
4 : : * Copyright (C) 2010 Broadcom
5 : : * Copyright (C) 2018 Raspberry Pi (Trading) Ltd
6 : : *
7 : : * This file is subject to the terms and conditions of the GNU General Public
8 : : * License. See the file COPYING in the main directory of this archive
9 : : * for more details.
10 : : *
11 : : * Broadcom simple framebuffer driver
12 : : *
13 : : * This file is derived from cirrusfb.c
14 : : * Copyright 1999-2001 Jeff Garzik <jgarzik@pobox.com>
15 : : *
16 : : */
17 : :
18 : : #include <linux/module.h>
19 : : #include <linux/kernel.h>
20 : : #include <linux/errno.h>
21 : : #include <linux/string.h>
22 : : #include <linux/slab.h>
23 : : #include <linux/mm.h>
24 : : #include <linux/fb.h>
25 : : #include <linux/init.h>
26 : : #include <linux/interrupt.h>
27 : : #include <linux/ioport.h>
28 : : #include <linux/list.h>
29 : : #include <linux/platform_data/dma-bcm2708.h>
30 : : #include <linux/platform_device.h>
31 : : #include <linux/clk.h>
32 : : #include <linux/printk.h>
33 : : #include <linux/console.h>
34 : : #include <linux/debugfs.h>
35 : : #include <linux/uaccess.h>
36 : : #include <linux/io.h>
37 : : #include <linux/dma-mapping.h>
38 : : #include <linux/cred.h>
39 : : #include <soc/bcm2835/raspberrypi-firmware.h>
40 : : #include <linux/mutex.h>
41 : :
42 : : //#define BCM2708_FB_DEBUG
43 : : #define MODULE_NAME "bcm2708_fb"
44 : :
45 : : #ifdef BCM2708_FB_DEBUG
46 : : #define print_debug(fmt, ...) pr_debug("%s:%s:%d: " fmt, \
47 : : MODULE_NAME, __func__, __LINE__, ##__VA_ARGS__)
48 : : #else
49 : : #define print_debug(fmt, ...)
50 : : #endif
51 : :
52 : : /* This is limited to 16 characters when displayed by X startup */
53 : : static const char *bcm2708_name = "BCM2708 FB";
54 : :
55 : : #define DRIVER_NAME "bcm2708_fb"
56 : :
57 : : static int fbwidth = 800; /* module parameter */
58 : : static int fbheight = 480; /* module parameter */
59 : : static int fbdepth = 32; /* module parameter */
60 : : static int fbswap; /* module parameter */
61 : :
62 : : static u32 dma_busy_wait_threshold = 1 << 15;
63 : : module_param(dma_busy_wait_threshold, int, 0644);
64 : : MODULE_PARM_DESC(dma_busy_wait_threshold, "Busy-wait for DMA completion below this area");
65 : :
66 : : struct fb_alloc_tags {
67 : : struct rpi_firmware_property_tag_header tag1;
68 : : u32 xres, yres;
69 : : struct rpi_firmware_property_tag_header tag2;
70 : : u32 xres_virtual, yres_virtual;
71 : : struct rpi_firmware_property_tag_header tag3;
72 : : u32 bpp;
73 : : struct rpi_firmware_property_tag_header tag4;
74 : : u32 xoffset, yoffset;
75 : : struct rpi_firmware_property_tag_header tag5;
76 : : u32 base, screen_size;
77 : : struct rpi_firmware_property_tag_header tag6;
78 : : u32 pitch;
79 : : };
80 : :
81 : : struct bcm2708_fb_stats {
82 : : struct debugfs_regset32 regset;
83 : : u32 dma_copies;
84 : : u32 dma_irqs;
85 : : };
86 : :
87 : : struct vc4_display_settings_t {
88 : : u32 display_num;
89 : : u32 width;
90 : : u32 height;
91 : : u32 depth;
92 : : u32 pitch;
93 : : u32 virtual_width;
94 : : u32 virtual_height;
95 : : u32 virtual_width_offset;
96 : : u32 virtual_height_offset;
97 : : unsigned long fb_bus_address;
98 : : };
99 : :
100 : : struct bcm2708_fb_dev;
101 : :
102 : : struct bcm2708_fb {
103 : : struct fb_info fb;
104 : : struct platform_device *dev;
105 : : u32 cmap[16];
106 : : u32 gpu_cmap[256];
107 : : struct dentry *debugfs_dir;
108 : : struct dentry *debugfs_subdir;
109 : : unsigned long fb_bus_address;
110 : : struct { u32 base, length; } gpu;
111 : : struct vc4_display_settings_t display_settings;
112 : : struct debugfs_regset32 screeninfo_regset;
113 : : struct bcm2708_fb_dev *fbdev;
114 : : unsigned int image_size;
115 : : dma_addr_t dma_addr;
116 : : void *cpuaddr;
117 : : };
118 : :
119 : : #define MAX_FRAMEBUFFERS 3
120 : :
121 : : struct bcm2708_fb_dev {
122 : : int firmware_supports_multifb;
123 : : /* Protects the DMA system from multiple FB access */
124 : : struct mutex dma_mutex;
125 : : int dma_chan;
126 : : int dma_irq;
127 : : void __iomem *dma_chan_base;
128 : : wait_queue_head_t dma_waitq;
129 : : bool disable_arm_alloc;
130 : : struct bcm2708_fb_stats dma_stats;
131 : : void *cb_base; /* DMA control blocks */
132 : : dma_addr_t cb_handle;
133 : : int instance_count;
134 : : int num_displays;
135 : : struct rpi_firmware *fw;
136 : : struct bcm2708_fb displays[MAX_FRAMEBUFFERS];
137 : : };
138 : :
139 : : #define to_bcm2708(info) container_of(info, struct bcm2708_fb, fb)
140 : :
141 : 0 : static void bcm2708_fb_debugfs_deinit(struct bcm2708_fb *fb)
142 : : {
143 : 0 : debugfs_remove_recursive(fb->debugfs_subdir);
144 : 0 : fb->debugfs_subdir = NULL;
145 : :
146 : 0 : fb->fbdev->instance_count--;
147 : :
148 [ # # ]: 0 : if (!fb->fbdev->instance_count) {
149 : 0 : debugfs_remove_recursive(fb->debugfs_dir);
150 : 0 : fb->debugfs_dir = NULL;
151 : : }
152 : 0 : }
153 : :
154 : 1212 : static int bcm2708_fb_debugfs_init(struct bcm2708_fb *fb)
155 : : {
156 : : char buf[3];
157 : 1212 : struct bcm2708_fb_dev *fbdev = fb->fbdev;
158 : :
159 : : static struct debugfs_reg32 stats_registers[] = {
160 : : {"dma_copies", offsetof(struct bcm2708_fb_stats, dma_copies)},
161 : : {"dma_irqs", offsetof(struct bcm2708_fb_stats, dma_irqs)},
162 : : };
163 : :
164 : : static struct debugfs_reg32 screeninfo[] = {
165 : : {"width", offsetof(struct fb_var_screeninfo, xres)},
166 : : {"height", offsetof(struct fb_var_screeninfo, yres)},
167 : : {"bpp", offsetof(struct fb_var_screeninfo, bits_per_pixel)},
168 : : {"xres_virtual", offsetof(struct fb_var_screeninfo, xres_virtual)},
169 : : {"yres_virtual", offsetof(struct fb_var_screeninfo, yres_virtual)},
170 : : {"xoffset", offsetof(struct fb_var_screeninfo, xoffset)},
171 : : {"yoffset", offsetof(struct fb_var_screeninfo, yoffset)},
172 : : };
173 : :
174 : 1212 : fb->debugfs_dir = debugfs_lookup(DRIVER_NAME, NULL);
175 : :
176 [ + + ]: 1212 : if (!fb->debugfs_dir)
177 : 404 : fb->debugfs_dir = debugfs_create_dir(DRIVER_NAME, NULL);
178 : :
179 [ - + ]: 1212 : if (!fb->debugfs_dir) {
180 : 0 : dev_warn(fb->fb.dev, "%s: could not create debugfs folder\n",
181 : : __func__);
182 : 0 : return -EFAULT;
183 : : }
184 : :
185 : 1212 : snprintf(buf, sizeof(buf), "%u", fb->display_settings.display_num);
186 : :
187 : 1212 : fb->debugfs_subdir = debugfs_create_dir(buf, fb->debugfs_dir);
188 : :
189 [ - + ]: 1212 : if (!fb->debugfs_subdir) {
190 : 0 : dev_warn(fb->fb.dev, "%s: could not create debugfs entry %u\n",
191 : : __func__, fb->display_settings.display_num);
192 : 0 : return -EFAULT;
193 : : }
194 : :
195 : 1212 : fbdev->dma_stats.regset.regs = stats_registers;
196 : 1212 : fbdev->dma_stats.regset.nregs = ARRAY_SIZE(stats_registers);
197 : 1212 : fbdev->dma_stats.regset.base = &fbdev->dma_stats;
198 : :
199 [ - + ]: 1212 : if (!debugfs_create_regset32("dma_stats", 0444, fb->debugfs_subdir,
200 : : &fbdev->dma_stats.regset)) {
201 : 0 : dev_warn(fb->fb.dev, "%s: could not create statistics registers\n",
202 : : __func__);
203 : 0 : goto fail;
204 : : }
205 : :
206 : 1212 : fb->screeninfo_regset.regs = screeninfo;
207 : 1212 : fb->screeninfo_regset.nregs = ARRAY_SIZE(screeninfo);
208 : 1212 : fb->screeninfo_regset.base = &fb->fb.var;
209 : :
210 [ - + ]: 1212 : if (!debugfs_create_regset32("screeninfo", 0444, fb->debugfs_subdir,
211 : : &fb->screeninfo_regset)) {
212 : 0 : dev_warn(fb->fb.dev,
213 : : "%s: could not create dimensions registers\n",
214 : : __func__);
215 : 0 : goto fail;
216 : : }
217 : :
218 : 1212 : fbdev->instance_count++;
219 : :
220 : 1212 : return 0;
221 : :
222 : : fail:
223 : 0 : bcm2708_fb_debugfs_deinit(fb);
224 : 0 : return -EFAULT;
225 : : }
226 : :
227 : 4064 : static void set_display_num(struct bcm2708_fb *fb)
228 : : {
229 [ + - + - : 4064 : if (fb && fb->fbdev && fb->fbdev->firmware_supports_multifb) {
+ - ]
230 : 4064 : u32 tmp = fb->display_settings.display_num;
231 : :
232 [ - + ]: 4064 : if (rpi_firmware_property(fb->fbdev->fw,
233 : : RPI_FIRMWARE_FRAMEBUFFER_SET_DISPLAY_NUM,
234 : : &tmp,
235 : : sizeof(tmp)))
236 [ # # ]: 0 : dev_warn_once(fb->fb.dev,
237 : : "Set display number call failed. Old GPU firmware?");
238 : : }
239 : 4064 : }
240 : :
241 : 1212 : static int bcm2708_fb_set_bitfields(struct fb_var_screeninfo *var)
242 : : {
243 : : int ret = 0;
244 : :
245 : 1212 : memset(&var->transp, 0, sizeof(var->transp));
246 : :
247 : 1212 : var->red.msb_right = 0;
248 : 1212 : var->green.msb_right = 0;
249 : 1212 : var->blue.msb_right = 0;
250 : :
251 [ - - - + : 1212 : switch (var->bits_per_pixel) {
- ]
252 : : case 1:
253 : : case 2:
254 : : case 4:
255 : : case 8:
256 : 0 : var->red.length = var->bits_per_pixel;
257 : 0 : var->red.offset = 0;
258 : 0 : var->green.length = var->bits_per_pixel;
259 : 0 : var->green.offset = 0;
260 : 0 : var->blue.length = var->bits_per_pixel;
261 : 0 : var->blue.offset = 0;
262 : 0 : break;
263 : : case 16:
264 : 0 : var->red.length = 5;
265 : 0 : var->blue.length = 5;
266 : : /*
267 : : * Green length can be 5 or 6 depending whether
268 : : * we're operating in RGB555 or RGB565 mode.
269 : : */
270 [ # # ]: 0 : if (var->green.length != 5 && var->green.length != 6)
271 : 0 : var->green.length = 6;
272 : : break;
273 : : case 24:
274 : 0 : var->red.length = 8;
275 : 0 : var->blue.length = 8;
276 : 0 : var->green.length = 8;
277 : 0 : break;
278 : : case 32:
279 : 1212 : var->red.length = 8;
280 : 1212 : var->green.length = 8;
281 : 1212 : var->blue.length = 8;
282 : 1212 : var->transp.length = 8;
283 : 1212 : break;
284 : : default:
285 : : ret = -EINVAL;
286 : : break;
287 : : }
288 : :
289 : : /*
290 : : * >= 16bpp displays have separate colour component bitfields
291 : : * encoded in the pixel data. Calculate their position from
292 : : * the bitfield length defined above.
293 : : */
294 [ + - + - : 1212 : if (ret == 0 && var->bits_per_pixel >= 24 && fbswap) {
- + ]
295 : 0 : var->blue.offset = 0;
296 : 0 : var->green.offset = var->blue.offset + var->blue.length;
297 : 0 : var->red.offset = var->green.offset + var->green.length;
298 : 0 : var->transp.offset = var->red.offset + var->red.length;
299 [ + - + - ]: 1212 : } else if (ret == 0 && var->bits_per_pixel >= 24) {
300 : 1212 : var->red.offset = 0;
301 : 1212 : var->green.offset = var->red.offset + var->red.length;
302 : 1212 : var->blue.offset = var->green.offset + var->green.length;
303 : 1212 : var->transp.offset = var->blue.offset + var->blue.length;
304 [ # # # # ]: 0 : } else if (ret == 0 && var->bits_per_pixel >= 16) {
305 : 0 : var->blue.offset = 0;
306 : 0 : var->green.offset = var->blue.offset + var->blue.length;
307 : 0 : var->red.offset = var->green.offset + var->green.length;
308 : 0 : var->transp.offset = var->red.offset + var->red.length;
309 : : }
310 : :
311 : 1212 : return ret;
312 : : }
313 : :
314 : 0 : static int bcm2708_fb_check_var(struct fb_var_screeninfo *var,
315 : : struct fb_info *info)
316 : : {
317 : : /* info input, var output */
318 : : print_debug("%s(%p) %ux%u (%ux%u), %ul, %u\n",
319 : : __func__, info, info->var.xres, info->var.yres,
320 : : info->var.xres_virtual, info->var.yres_virtual,
321 : : info->screen_size, info->var.bits_per_pixel);
322 : : print_debug("%s(%p) %ux%u (%ux%u), %u\n", __func__, var, var->xres,
323 : : var->yres, var->xres_virtual, var->yres_virtual,
324 : : var->bits_per_pixel);
325 : :
326 [ # # ]: 0 : if (!var->bits_per_pixel)
327 : 0 : var->bits_per_pixel = 16;
328 : :
329 [ # # ]: 0 : if (bcm2708_fb_set_bitfields(var) != 0) {
330 : 0 : pr_err("%s: invalid bits_per_pixel %d\n", __func__,
331 : : var->bits_per_pixel);
332 : 0 : return -EINVAL;
333 : : }
334 : :
335 [ # # ]: 0 : if (var->xres_virtual < var->xres)
336 : 0 : var->xres_virtual = var->xres;
337 : : /* use highest possible virtual resolution */
338 [ # # ]: 0 : if (var->yres_virtual == -1) {
339 : 0 : var->yres_virtual = 480;
340 : :
341 : 0 : pr_err("%s: virtual resolution set to maximum of %dx%d\n",
342 : : __func__, var->xres_virtual, var->yres_virtual);
343 : : }
344 [ # # ]: 0 : if (var->yres_virtual < var->yres)
345 : 0 : var->yres_virtual = var->yres;
346 : :
347 : : if (var->xoffset < 0)
348 : : var->xoffset = 0;
349 : : if (var->yoffset < 0)
350 : : var->yoffset = 0;
351 : :
352 : : /* truncate xoffset and yoffset to maximum if too high */
353 [ # # ]: 0 : if (var->xoffset > var->xres_virtual - var->xres)
354 : 0 : var->xoffset = var->xres_virtual - var->xres - 1;
355 [ # # ]: 0 : if (var->yoffset > var->yres_virtual - var->yres)
356 : 0 : var->yoffset = var->yres_virtual - var->yres - 1;
357 : :
358 : : return 0;
359 : : }
360 : :
361 : 3232 : static int bcm2708_fb_set_par(struct fb_info *info)
362 : : {
363 : : struct bcm2708_fb *fb = to_bcm2708(info);
364 : 25856 : struct fb_alloc_tags fbinfo = {
365 : : .tag1 = { RPI_FIRMWARE_FRAMEBUFFER_SET_PHYSICAL_WIDTH_HEIGHT,
366 : : 8, 0, },
367 : 3232 : .xres = info->var.xres,
368 : 3232 : .yres = info->var.yres,
369 : : .tag2 = { RPI_FIRMWARE_FRAMEBUFFER_SET_VIRTUAL_WIDTH_HEIGHT,
370 : : 8, 0, },
371 : 3232 : .xres_virtual = info->var.xres_virtual,
372 : 3232 : .yres_virtual = info->var.yres_virtual,
373 : : .tag3 = { RPI_FIRMWARE_FRAMEBUFFER_SET_DEPTH, 4, 0 },
374 : 3232 : .bpp = info->var.bits_per_pixel,
375 : : .tag4 = { RPI_FIRMWARE_FRAMEBUFFER_SET_VIRTUAL_OFFSET, 8, 0 },
376 : 3232 : .xoffset = info->var.xoffset,
377 : 3232 : .yoffset = info->var.yoffset,
378 : : .tag5 = { RPI_FIRMWARE_FRAMEBUFFER_ALLOCATE, 8, 0 },
379 : : /* base and screen_size will be initialised later */
380 : : .tag6 = { RPI_FIRMWARE_FRAMEBUFFER_SET_PITCH, 4, 0 },
381 : : /* pitch will be initialised later */
382 : : };
383 : : int ret, image_size;
384 : :
385 : : print_debug("%s(%p) %dx%d (%dx%d), %d, %d (display %d)\n", __func__,
386 : : info,
387 : : info->var.xres, info->var.yres, info->var.xres_virtual,
388 : : info->var.yres_virtual, (int)info->screen_size,
389 : : info->var.bits_per_pixel, value);
390 : :
391 : : /* Need to set the display number to act on first
392 : : * Cannot do it in the tag list because on older firmware the call
393 : : * will fail and stop the rest of the list being executed.
394 : : * We can ignore this call failing as the default at other end is 0
395 : : */
396 : 3232 : set_display_num(fb);
397 : :
398 : : /* Try allocating our own buffer. We can specify all the parameters */
399 : 6464 : image_size = ((info->var.xres * info->var.yres) *
400 : 6464 : info->var.bits_per_pixel) >> 3;
401 : :
402 [ + + - + ]: 3636 : if (!fb->fbdev->disable_arm_alloc &&
403 [ # # ]: 404 : (image_size != fb->image_size || !fb->dma_addr)) {
404 [ - + ]: 404 : if (fb->dma_addr) {
405 : 0 : dma_free_coherent(info->device, fb->image_size,
406 : : fb->cpuaddr, fb->dma_addr);
407 : 0 : fb->image_size = 0;
408 : 0 : fb->cpuaddr = NULL;
409 : 0 : fb->dma_addr = 0;
410 : : }
411 : :
412 : 808 : fb->cpuaddr = dma_alloc_coherent(info->device, image_size,
413 : : &fb->dma_addr, GFP_KERNEL);
414 : :
415 [ - + ]: 404 : if (!fb->cpuaddr) {
416 : 0 : fb->dma_addr = 0;
417 : 0 : fb->fbdev->disable_arm_alloc = true;
418 : : } else {
419 : 404 : fb->image_size = image_size;
420 : : }
421 : : }
422 : :
423 [ + + ]: 3232 : if (fb->cpuaddr) {
424 : 404 : fbinfo.base = fb->dma_addr;
425 : 404 : fbinfo.screen_size = image_size;
426 : 404 : fbinfo.pitch = (info->var.xres * info->var.bits_per_pixel) >> 3;
427 : :
428 : 404 : ret = rpi_firmware_property_list(fb->fbdev->fw, &fbinfo,
429 : : sizeof(fbinfo));
430 [ + - + - ]: 404 : if (ret || fbinfo.base != fb->dma_addr) {
431 : : /* Firmware either failed, or assigned a different base
432 : : * address (ie it doesn't support being passed an FB
433 : : * allocation).
434 : : * Destroy the allocation, and don't try again.
435 : : */
436 : 404 : dma_free_coherent(info->device, fb->image_size,
437 : : fb->cpuaddr, fb->dma_addr);
438 : 404 : fb->image_size = 0;
439 : 404 : fb->cpuaddr = NULL;
440 : 404 : fb->dma_addr = 0;
441 : 404 : fb->fbdev->disable_arm_alloc = true;
442 : : }
443 : : } else {
444 : : /* Our allocation failed - drop into the old scheme of
445 : : * allocation by the VPU.
446 : : */
447 : : ret = -ENOMEM;
448 : : }
449 : :
450 [ + + ]: 3232 : if (ret) {
451 : : /* Old scheme:
452 : : * - FRAMEBUFFER_ALLOCATE passes 0 for base and screen_size.
453 : : * - GET_PITCH instead of SET_PITCH.
454 : : */
455 : 2828 : fbinfo.base = 0;
456 : 2828 : fbinfo.screen_size = 0;
457 : 2828 : fbinfo.tag6.tag = RPI_FIRMWARE_FRAMEBUFFER_GET_PITCH;
458 : 2828 : fbinfo.pitch = 0;
459 : :
460 : 2828 : ret = rpi_firmware_property_list(fb->fbdev->fw, &fbinfo,
461 : : sizeof(fbinfo));
462 [ - + ]: 2828 : if (ret) {
463 : 0 : dev_err(info->device,
464 : : "Failed to allocate GPU framebuffer (%d)\n",
465 : : ret);
466 : 0 : return ret;
467 : : }
468 : : }
469 : :
470 [ - + ]: 3232 : if (info->var.bits_per_pixel <= 8)
471 : 0 : fb->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR;
472 : : else
473 : 3232 : fb->fb.fix.visual = FB_VISUAL_TRUECOLOR;
474 : :
475 : 3232 : fb->fb.fix.line_length = fbinfo.pitch;
476 : 3232 : fbinfo.base |= 0x40000000;
477 : 3232 : fb->fb_bus_address = fbinfo.base;
478 : 3232 : fbinfo.base &= ~0xc0000000;
479 : 3232 : fb->fb.fix.smem_start = fbinfo.base;
480 : 3232 : fb->fb.fix.smem_len = fbinfo.pitch * fbinfo.yres_virtual;
481 : 3232 : fb->fb.screen_size = fbinfo.screen_size;
482 : :
483 [ + - ]: 3232 : if (!fb->dma_addr) {
484 [ + + ]: 3232 : if (fb->fb.screen_base)
485 : 2020 : iounmap(fb->fb.screen_base);
486 : :
487 : 6464 : fb->fb.screen_base = ioremap_wc(fbinfo.base,
488 : 3232 : fb->fb.screen_size);
489 : : } else {
490 : 0 : fb->fb.screen_base = fb->cpuaddr;
491 : : }
492 : :
493 [ - + ]: 3232 : if (!fb->fb.screen_base) {
494 : : /* the console may currently be locked */
495 : 0 : console_trylock();
496 : 0 : console_unlock();
497 : 0 : dev_err(info->device, "Failed to set screen_base\n");
498 : 0 : return -ENOMEM;
499 : : }
500 : :
501 : : print_debug("%s: start = %p,%p width=%d, height=%d, bpp=%d, pitch=%d size=%d\n",
502 : : __func__, (void *)fb->fb.screen_base,
503 : : (void *)fb->fb_bus_address, fbinfo.xres, fbinfo.yres,
504 : : fbinfo.bpp, fbinfo.pitch, (int)fb->fb.screen_size);
505 : :
506 : : return 0;
507 : : }
508 : :
509 : : static inline u32 convert_bitfield(int val, struct fb_bitfield *bf)
510 : : {
511 : 181120 : unsigned int mask = (1 << bf->length) - 1;
512 : :
513 : 181120 : return (val >> (16 - bf->length) & mask) << bf->offset;
514 : : }
515 : :
516 : 142720 : static int bcm2708_fb_setcolreg(unsigned int regno, unsigned int red,
517 : : unsigned int green, unsigned int blue,
518 : : unsigned int transp, struct fb_info *info)
519 : : {
520 : : struct bcm2708_fb *fb = to_bcm2708(info);
521 : :
522 [ - + ]: 142720 : if (fb->fb.var.bits_per_pixel <= 8) {
523 [ # # ]: 0 : if (regno < 256) {
524 : : /* blue [23:16], green [15:8], red [7:0] */
525 : 0 : fb->gpu_cmap[regno] = ((red >> 8) & 0xff) << 0 |
526 : 0 : ((green >> 8) & 0xff) << 8 |
527 : 0 : ((blue >> 8) & 0xff) << 16;
528 : : }
529 : : /* Hack: we need to tell GPU the palette has changed, but
530 : : * currently bcm2708_fb_set_par takes noticeable time when
531 : : * called for every (256) colour
532 : : * So just call it for what looks like the last colour in a
533 : : * list for now.
534 : : */
535 [ # # ]: 0 : if (regno == 15 || regno == 255) {
536 : : struct packet {
537 : : u32 offset;
538 : : u32 length;
539 : : u32 cmap[256];
540 : : } *packet;
541 : : int ret;
542 : :
543 : : packet = kmalloc(sizeof(*packet), GFP_KERNEL);
544 [ # # ]: 0 : if (!packet)
545 : : return -ENOMEM;
546 : 0 : packet->offset = 0;
547 : 0 : packet->length = regno + 1;
548 : 0 : memcpy(packet->cmap, fb->gpu_cmap,
549 : : sizeof(packet->cmap));
550 : :
551 : 0 : set_display_num(fb);
552 : :
553 : 0 : ret = rpi_firmware_property(fb->fbdev->fw,
554 : : RPI_FIRMWARE_FRAMEBUFFER_SET_PALETTE,
555 : : packet,
556 : 0 : (2 + packet->length) * sizeof(u32));
557 [ # # # # ]: 0 : if (ret || packet->offset)
558 : 0 : dev_err(info->device,
559 : : "Failed to set palette (%d,%u)\n",
560 : : ret, packet->offset);
561 : 0 : kfree(packet);
562 : : }
563 [ + + ]: 142720 : } else if (regno < 16) {
564 : 135840 : fb->cmap[regno] = convert_bitfield(transp, &fb->fb.var.transp) |
565 : 90560 : convert_bitfield(blue, &fb->fb.var.blue) |
566 : 90560 : convert_bitfield(green, &fb->fb.var.green) |
567 : 45280 : convert_bitfield(red, &fb->fb.var.red);
568 : : }
569 : 142720 : return regno > 255;
570 : : }
571 : :
572 : 832 : static int bcm2708_fb_blank(int blank_mode, struct fb_info *info)
573 : : {
574 : : struct bcm2708_fb *fb = to_bcm2708(info);
575 : : u32 value;
576 : : int ret;
577 : :
578 [ + + - ]: 832 : switch (blank_mode) {
579 : : case FB_BLANK_UNBLANK:
580 : 428 : value = 0;
581 : 428 : break;
582 : : case FB_BLANK_NORMAL:
583 : : case FB_BLANK_VSYNC_SUSPEND:
584 : : case FB_BLANK_HSYNC_SUSPEND:
585 : : case FB_BLANK_POWERDOWN:
586 : 404 : value = 1;
587 : 404 : break;
588 : : default:
589 : : return -EINVAL;
590 : : }
591 : :
592 : 832 : set_display_num(fb);
593 : :
594 : 832 : ret = rpi_firmware_property(fb->fbdev->fw, RPI_FIRMWARE_FRAMEBUFFER_BLANK,
595 : : &value, sizeof(value));
596 : :
597 [ - + ]: 832 : if (ret)
598 : 0 : dev_err(info->device, "%s(%d) failed: %d\n", __func__,
599 : : blank_mode, ret);
600 : :
601 : 832 : return ret;
602 : : }
603 : :
604 : 1616 : static int bcm2708_fb_pan_display(struct fb_var_screeninfo *var,
605 : : struct fb_info *info)
606 : : {
607 : : s32 result;
608 : :
609 : 1616 : info->var.xoffset = var->xoffset;
610 : 1616 : info->var.yoffset = var->yoffset;
611 : 1616 : result = bcm2708_fb_set_par(info);
612 [ - + ]: 1616 : if (result != 0)
613 : 0 : pr_err("%s(%u,%u) returns=%d\n", __func__, var->xoffset,
614 : : var->yoffset, result);
615 : 1616 : return result;
616 : : }
617 : :
618 : 0 : static void dma_memcpy(struct bcm2708_fb *fb, dma_addr_t dst, dma_addr_t src,
619 : : int size)
620 : : {
621 : 0 : struct bcm2708_fb_dev *fbdev = fb->fbdev;
622 : 0 : struct bcm2708_dma_cb *cb = fbdev->cb_base;
623 [ # # ]: 0 : int burst_size = (fbdev->dma_chan == 0) ? 8 : 2;
624 : :
625 : 0 : cb->info = BCM2708_DMA_BURST(burst_size) | BCM2708_DMA_S_WIDTH |
626 : 0 : BCM2708_DMA_S_INC | BCM2708_DMA_D_WIDTH |
627 : : BCM2708_DMA_D_INC;
628 : 0 : cb->dst = dst;
629 : 0 : cb->src = src;
630 : 0 : cb->length = size;
631 : 0 : cb->stride = 0;
632 : 0 : cb->pad[0] = 0;
633 : 0 : cb->pad[1] = 0;
634 : 0 : cb->next = 0;
635 : :
636 : : // Not sure what to do if this gets a signal whilst waiting
637 [ # # ]: 0 : if (mutex_lock_interruptible(&fbdev->dma_mutex))
638 : 0 : return;
639 : :
640 [ # # ]: 0 : if (size < dma_busy_wait_threshold) {
641 : 0 : bcm_dma_start(fbdev->dma_chan_base, fbdev->cb_handle);
642 : 0 : bcm_dma_wait_idle(fbdev->dma_chan_base);
643 : : } else {
644 : 0 : void __iomem *local_dma_chan = fbdev->dma_chan_base;
645 : :
646 : 0 : cb->info |= BCM2708_DMA_INT_EN;
647 : 0 : bcm_dma_start(fbdev->dma_chan_base, fbdev->cb_handle);
648 [ # # ]: 0 : while (bcm_dma_is_busy(local_dma_chan)) {
649 [ # # # # : 0 : wait_event_interruptible(fbdev->dma_waitq,
# # ]
650 : : !bcm_dma_is_busy(local_dma_chan));
651 : : }
652 : 0 : fbdev->dma_stats.dma_irqs++;
653 : : }
654 : 0 : fbdev->dma_stats.dma_copies++;
655 : :
656 : 0 : mutex_unlock(&fbdev->dma_mutex);
657 : : }
658 : :
659 : : /* address with no aliases */
660 : : #define INTALIAS_NORMAL(x) ((x) & ~0xc0000000)
661 : : /* cache coherent but non-allocating in L1 and L2 */
662 : : #define INTALIAS_L1L2_NONALLOCATING(x) (((x) & ~0xc0000000) | 0x80000000)
663 : :
664 : 0 : static long vc_mem_copy(struct bcm2708_fb *fb, struct fb_dmacopy *ioparam)
665 : : {
666 : : size_t size = PAGE_SIZE;
667 : : u32 *buf = NULL;
668 : : dma_addr_t bus_addr;
669 : : long rc = 0;
670 : : size_t offset;
671 : :
672 : : /* restrict this to root user */
673 [ # # ]: 0 : if (!uid_eq(current_euid(), GLOBAL_ROOT_UID)) {
674 : : rc = -EFAULT;
675 : : goto out;
676 : : }
677 : :
678 [ # # # # ]: 0 : if (!fb->gpu.base || !fb->gpu.length) {
679 : 0 : pr_err("[%s]: Unable to determine gpu memory (%x,%x)\n",
680 : : __func__, fb->gpu.base, fb->gpu.length);
681 : 0 : return -EFAULT;
682 : : }
683 : :
684 [ # # # # ]: 0 : if (INTALIAS_NORMAL(ioparam->src) < fb->gpu.base ||
685 : 0 : INTALIAS_NORMAL(ioparam->src) >= fb->gpu.base + fb->gpu.length) {
686 : 0 : pr_err("[%s]: Invalid memory access %x (%x-%x)", __func__,
687 : : INTALIAS_NORMAL(ioparam->src), fb->gpu.base,
688 : : fb->gpu.base + fb->gpu.length);
689 : 0 : return -EFAULT;
690 : : }
691 : :
692 : 0 : buf = dma_alloc_coherent(fb->fb.device, PAGE_ALIGN(size), &bus_addr,
693 : : GFP_ATOMIC);
694 [ # # ]: 0 : if (!buf) {
695 : 0 : pr_err("[%s]: failed to dma_alloc_coherent(%zd)\n", __func__,
696 : : size);
697 : : rc = -ENOMEM;
698 : 0 : goto out;
699 : : }
700 : :
701 [ # # ]: 0 : for (offset = 0; offset < ioparam->length; offset += size) {
702 : 0 : size_t remaining = ioparam->length - offset;
703 : 0 : size_t s = min(size, remaining);
704 : 0 : u8 *p = (u8 *)((uintptr_t)ioparam->src + offset);
705 : 0 : u8 *q = (u8 *)ioparam->dst + offset;
706 : :
707 : 0 : dma_memcpy(fb, bus_addr,
708 : 0 : INTALIAS_L1L2_NONALLOCATING((dma_addr_t)p), size);
709 [ # # ]: 0 : if (copy_to_user(q, buf, s) != 0) {
710 : 0 : pr_err("[%s]: failed to copy-to-user\n", __func__);
711 : : rc = -EFAULT;
712 : 0 : goto out;
713 : : }
714 : : }
715 : : out:
716 [ # # ]: 0 : if (buf)
717 : 0 : dma_free_coherent(fb->fb.device, PAGE_ALIGN(size), buf,
718 : : bus_addr);
719 : 0 : return rc;
720 : : }
721 : :
722 : 404 : static int bcm2708_ioctl(struct fb_info *info, unsigned int cmd,
723 : : unsigned long arg)
724 : : {
725 : : struct bcm2708_fb *fb = to_bcm2708(info);
726 : 404 : u32 dummy = 0;
727 : : int ret;
728 : :
729 [ - - + ]: 404 : switch (cmd) {
730 : : case FBIO_WAITFORVSYNC:
731 : 0 : set_display_num(fb);
732 : :
733 : 0 : ret = rpi_firmware_property(fb->fbdev->fw,
734 : : RPI_FIRMWARE_FRAMEBUFFER_SET_VSYNC,
735 : : &dummy, sizeof(dummy));
736 : 0 : break;
737 : :
738 : : case FBIODMACOPY:
739 : : {
740 : : struct fb_dmacopy ioparam;
741 : : /* Get the parameter data.
742 : : */
743 [ # # ]: 0 : if (copy_from_user
744 : : (&ioparam, (void *)arg, sizeof(ioparam))) {
745 : 0 : pr_err("[%s]: failed to copy-from-user\n", __func__);
746 : : ret = -EFAULT;
747 : 0 : break;
748 : : }
749 : 0 : ret = vc_mem_copy(fb, &ioparam);
750 : 0 : break;
751 : : }
752 : : default:
753 : : dev_dbg(info->device, "Unknown ioctl 0x%x\n", cmd);
754 : : return -ENOTTY;
755 : : }
756 : :
757 [ # # ]: 0 : if (ret)
758 : 0 : dev_err(info->device, "ioctl 0x%x failed (%d)\n", cmd, ret);
759 : :
760 : 0 : return ret;
761 : : }
762 : :
763 : : #ifdef CONFIG_COMPAT
764 : : struct fb_dmacopy32 {
765 : : compat_uptr_t dst;
766 : : __u32 src;
767 : : __u32 length;
768 : : };
769 : :
770 : : #define FBIODMACOPY32 _IOW('z', 0x22, struct fb_dmacopy32)
771 : :
772 : : static int bcm2708_compat_ioctl(struct fb_info *info, unsigned int cmd,
773 : : unsigned long arg)
774 : : {
775 : : struct bcm2708_fb *fb = to_bcm2708(info);
776 : : int ret;
777 : :
778 : : switch (cmd) {
779 : : case FBIODMACOPY32:
780 : : {
781 : : struct fb_dmacopy32 param32;
782 : : struct fb_dmacopy param;
783 : : /* Get the parameter data.
784 : : */
785 : : if (copy_from_user(¶m32, (void *)arg, sizeof(param32))) {
786 : : pr_err("[%s]: failed to copy-from-user\n", __func__);
787 : : ret = -EFAULT;
788 : : break;
789 : : }
790 : : param.dst = compat_ptr(param32.dst);
791 : : param.src = param32.src;
792 : : param.length = param32.length;
793 : : ret = vc_mem_copy(fb, ¶m);
794 : : break;
795 : : }
796 : : default:
797 : : ret = bcm2708_ioctl(info, cmd, arg);
798 : : break;
799 : : }
800 : : return ret;
801 : : }
802 : : #endif
803 : :
804 : 0 : static void bcm2708_fb_fillrect(struct fb_info *info,
805 : : const struct fb_fillrect *rect)
806 : : {
807 : 0 : cfb_fillrect(info, rect);
808 : 0 : }
809 : :
810 : : /* A helper function for configuring dma control block */
811 : : static void set_dma_cb(struct bcm2708_dma_cb *cb,
812 : : int burst_size,
813 : : dma_addr_t dst,
814 : : int dst_stride,
815 : : dma_addr_t src,
816 : : int src_stride,
817 : : int w,
818 : : int h)
819 : : {
820 : 832 : cb->info = BCM2708_DMA_BURST(burst_size) | BCM2708_DMA_S_WIDTH |
821 : : BCM2708_DMA_S_INC | BCM2708_DMA_D_WIDTH |
822 : 416 : BCM2708_DMA_D_INC | BCM2708_DMA_TDMODE;
823 : 416 : cb->dst = dst;
824 : 416 : cb->src = src;
825 : : /*
826 : : * This is not really obvious from the DMA documentation,
827 : : * but the top 16 bits must be programmmed to "height -1"
828 : : * and not "height" in 2D mode.
829 : : */
830 : 416 : cb->length = ((h - 1) << 16) | w;
831 : 416 : cb->stride = ((dst_stride - w) << 16) | (u16)(src_stride - w);
832 : 416 : cb->pad[0] = 0;
833 : 416 : cb->pad[1] = 0;
834 : : }
835 : :
836 : 416 : static void bcm2708_fb_copyarea(struct fb_info *info,
837 : : const struct fb_copyarea *region)
838 : : {
839 : : struct bcm2708_fb *fb = to_bcm2708(info);
840 : 416 : struct bcm2708_fb_dev *fbdev = fb->fbdev;
841 : 416 : struct bcm2708_dma_cb *cb = fbdev->cb_base;
842 : 416 : int bytes_per_pixel = (info->var.bits_per_pixel + 7) >> 3;
843 : :
844 : : /* Channel 0 supports larger bursts and is a bit faster */
845 [ - + ]: 416 : int burst_size = (fbdev->dma_chan == 0) ? 8 : 2;
846 : 416 : int pixels = region->width * region->height;
847 : :
848 : : /* If DMA is currently in use (ie being used on another FB), then
849 : : * rather than wait for it to finish, just use the cfb_copyarea
850 : : */
851 [ + - + - ]: 1248 : if (!mutex_trylock(&fbdev->dma_mutex) ||
852 [ + - ]: 416 : bytes_per_pixel > 4 ||
853 [ + - ]: 832 : info->var.xres * info->var.yres > 1920 * 1200 ||
854 [ + - + - ]: 1248 : region->width <= 0 || region->width > info->var.xres ||
855 [ + - + - ]: 1248 : region->height <= 0 || region->height > info->var.yres ||
856 [ + - + - ]: 416 : region->sx < 0 || region->sx >= info->var.xres ||
857 [ + - + - ]: 416 : region->sy < 0 || region->sy >= info->var.yres ||
858 [ + - + - ]: 416 : region->dx < 0 || region->dx >= info->var.xres ||
859 [ + - + - ]: 832 : region->dy < 0 || region->dy >= info->var.yres ||
860 [ + - ]: 832 : region->sx + region->width > info->var.xres ||
861 [ + - ]: 832 : region->dx + region->width > info->var.xres ||
862 [ - + ]: 832 : region->sy + region->height > info->var.yres ||
863 : 416 : region->dy + region->height > info->var.yres) {
864 : 0 : cfb_copyarea(info, region);
865 : 416 : return;
866 : : }
867 : :
868 [ + + - + ]: 416 : if (region->dy == region->sy && region->dx > region->sx) {
869 : : /*
870 : : * A difficult case of overlapped copy. Because DMA can't
871 : : * copy individual scanlines in backwards direction, we need
872 : : * two-pass processing. We do it by programming a chain of dma
873 : : * control blocks in the first 16K part of the buffer and use
874 : : * the remaining 48K as the intermediate temporary scratch
875 : : * buffer. The buffer size is sufficient to handle up to
876 : : * 1920x1200 resolution at 32bpp pixel depth.
877 : : */
878 : : int y;
879 : 0 : dma_addr_t control_block_pa = fbdev->cb_handle;
880 : 0 : dma_addr_t scratchbuf = fbdev->cb_handle + 16 * 1024;
881 : 0 : int scanline_size = bytes_per_pixel * region->width;
882 : 0 : int scanlines_per_cb = (64 * 1024 - 16 * 1024) / scanline_size;
883 : :
884 [ # # ]: 0 : for (y = 0; y < region->height; y += scanlines_per_cb) {
885 : 0 : dma_addr_t src =
886 : 0 : fb->fb_bus_address +
887 : 0 : bytes_per_pixel * region->sx +
888 : 0 : (region->sy + y) * fb->fb.fix.line_length;
889 : 0 : dma_addr_t dst =
890 : : fb->fb_bus_address +
891 : 0 : bytes_per_pixel * region->dx +
892 : 0 : (region->dy + y) * fb->fb.fix.line_length;
893 : :
894 [ # # ]: 0 : if (region->height - y < scanlines_per_cb)
895 : 0 : scanlines_per_cb = region->height - y;
896 : :
897 : : set_dma_cb(cb, burst_size, scratchbuf, scanline_size,
898 : : src, fb->fb.fix.line_length,
899 : : scanline_size, scanlines_per_cb);
900 : 0 : control_block_pa += sizeof(struct bcm2708_dma_cb);
901 : 0 : cb->next = control_block_pa;
902 : : cb++;
903 : :
904 : 0 : set_dma_cb(cb, burst_size, dst, fb->fb.fix.line_length,
905 : : scratchbuf, scanline_size,
906 : : scanline_size, scanlines_per_cb);
907 : 0 : control_block_pa += sizeof(struct bcm2708_dma_cb);
908 : 0 : cb->next = control_block_pa;
909 : 0 : cb++;
910 : : }
911 : : /* move the pointer back to the last dma control block */
912 : 0 : cb--;
913 : : } else {
914 : : /* A single dma control block is enough. */
915 : : int sy, dy, stride;
916 : :
917 [ + + ]: 416 : if (region->dy <= region->sy) {
918 : : /* processing from top to bottom */
919 : 414 : dy = region->dy;
920 : 414 : sy = region->sy;
921 : 414 : stride = fb->fb.fix.line_length;
922 : : } else {
923 : : /* processing from bottom to top */
924 : 2 : dy = region->dy + region->height - 1;
925 : 2 : sy = region->sy + region->height - 1;
926 : 2 : stride = -fb->fb.fix.line_length;
927 : : }
928 : 416 : set_dma_cb(cb, burst_size,
929 : 832 : fb->fb_bus_address + dy * fb->fb.fix.line_length +
930 : 416 : bytes_per_pixel * region->dx,
931 : : stride,
932 : 416 : fb->fb_bus_address + sy * fb->fb.fix.line_length +
933 : 416 : bytes_per_pixel * region->sx,
934 : : stride,
935 : 416 : region->width * bytes_per_pixel,
936 : : region->height);
937 : : }
938 : :
939 : : /* end of dma control blocks chain */
940 : 416 : cb->next = 0;
941 : :
942 [ + - ]: 416 : if (pixels < dma_busy_wait_threshold) {
943 : 416 : bcm_dma_start(fbdev->dma_chan_base, fbdev->cb_handle);
944 : 416 : bcm_dma_wait_idle(fbdev->dma_chan_base);
945 : : } else {
946 : 0 : void __iomem *local_dma_chan = fbdev->dma_chan_base;
947 : :
948 : 0 : cb->info |= BCM2708_DMA_INT_EN;
949 : 0 : bcm_dma_start(fbdev->dma_chan_base, fbdev->cb_handle);
950 [ # # ]: 0 : while (bcm_dma_is_busy(local_dma_chan)) {
951 [ # # # # : 0 : wait_event_interruptible(fbdev->dma_waitq,
# # ]
952 : : !bcm_dma_is_busy(local_dma_chan));
953 : : }
954 : 0 : fbdev->dma_stats.dma_irqs++;
955 : : }
956 : 416 : fbdev->dma_stats.dma_copies++;
957 : :
958 : 416 : mutex_unlock(&fbdev->dma_mutex);
959 : : }
960 : :
961 : 151024 : static void bcm2708_fb_imageblit(struct fb_info *info,
962 : : const struct fb_image *image)
963 : : {
964 : 151024 : cfb_imageblit(info, image);
965 : 151024 : }
966 : :
967 : 0 : static irqreturn_t bcm2708_fb_dma_irq(int irq, void *cxt)
968 : : {
969 : : struct bcm2708_fb_dev *fbdev = cxt;
970 : :
971 : : /* FIXME: should read status register to check if this is
972 : : * actually interrupting us or not, in case this interrupt
973 : : * ever becomes shared amongst several DMA channels
974 : : *
975 : : * readl(dma_chan_base + BCM2708_DMA_CS) & BCM2708_DMA_IRQ;
976 : : */
977 : :
978 : : /* acknowledge the interrupt */
979 : 0 : writel(BCM2708_DMA_INT, fbdev->dma_chan_base + BCM2708_DMA_CS);
980 : :
981 : 0 : wake_up(&fbdev->dma_waitq);
982 : 0 : return IRQ_HANDLED;
983 : : }
984 : :
985 : : static struct fb_ops bcm2708_fb_ops = {
986 : : .owner = THIS_MODULE,
987 : : .fb_check_var = bcm2708_fb_check_var,
988 : : .fb_set_par = bcm2708_fb_set_par,
989 : : .fb_setcolreg = bcm2708_fb_setcolreg,
990 : : .fb_blank = bcm2708_fb_blank,
991 : : .fb_fillrect = bcm2708_fb_fillrect,
992 : : .fb_copyarea = bcm2708_fb_copyarea,
993 : : .fb_imageblit = bcm2708_fb_imageblit,
994 : : .fb_pan_display = bcm2708_fb_pan_display,
995 : : .fb_ioctl = bcm2708_ioctl,
996 : : #ifdef CONFIG_COMPAT
997 : : .fb_compat_ioctl = bcm2708_compat_ioctl,
998 : : #endif
999 : : };
1000 : :
1001 : 1212 : static int bcm2708_fb_register(struct bcm2708_fb *fb)
1002 : : {
1003 : : int ret;
1004 : :
1005 : 1212 : fb->fb.fbops = &bcm2708_fb_ops;
1006 : 1212 : fb->fb.flags = FBINFO_FLAG_DEFAULT | FBINFO_HWACCEL_COPYAREA;
1007 : 1212 : fb->fb.pseudo_palette = fb->cmap;
1008 : :
1009 : 1212 : strncpy(fb->fb.fix.id, bcm2708_name, sizeof(fb->fb.fix.id));
1010 : 1212 : fb->fb.fix.type = FB_TYPE_PACKED_PIXELS;
1011 : 1212 : fb->fb.fix.type_aux = 0;
1012 : 1212 : fb->fb.fix.xpanstep = 1;
1013 : 1212 : fb->fb.fix.ypanstep = 1;
1014 : 1212 : fb->fb.fix.ywrapstep = 0;
1015 : 1212 : fb->fb.fix.accel = FB_ACCEL_NONE;
1016 : :
1017 : : /* If we have data from the VC4 on FB's, use that, otherwise use the
1018 : : * module parameters
1019 : : */
1020 [ - + ]: 1212 : if (fb->display_settings.width) {
1021 : 0 : fb->fb.var.xres = fb->display_settings.width;
1022 : 0 : fb->fb.var.yres = fb->display_settings.height;
1023 : 0 : fb->fb.var.xres_virtual = fb->fb.var.xres;
1024 : 0 : fb->fb.var.yres_virtual = fb->fb.var.yres;
1025 : 0 : fb->fb.var.bits_per_pixel = fb->display_settings.depth;
1026 : : } else {
1027 : 1212 : fb->fb.var.xres = fbwidth;
1028 : 1212 : fb->fb.var.yres = fbheight;
1029 : 1212 : fb->fb.var.xres_virtual = fbwidth;
1030 : 1212 : fb->fb.var.yres_virtual = fbheight;
1031 : 1212 : fb->fb.var.bits_per_pixel = fbdepth;
1032 : : }
1033 : :
1034 : 1212 : fb->fb.var.vmode = FB_VMODE_NONINTERLACED;
1035 : 1212 : fb->fb.var.activate = FB_ACTIVATE_NOW;
1036 : 1212 : fb->fb.var.nonstd = 0;
1037 : 1212 : fb->fb.var.height = -1; /* height of picture in mm */
1038 : 1212 : fb->fb.var.width = -1; /* width of picture in mm */
1039 : 1212 : fb->fb.var.accel_flags = 0;
1040 : :
1041 : 1212 : fb->fb.monspecs.hfmin = 0;
1042 : 1212 : fb->fb.monspecs.hfmax = 100000;
1043 : 1212 : fb->fb.monspecs.vfmin = 0;
1044 : 1212 : fb->fb.monspecs.vfmax = 400;
1045 : 1212 : fb->fb.monspecs.dclkmin = 1000000;
1046 : 1212 : fb->fb.monspecs.dclkmax = 100000000;
1047 : :
1048 : 1212 : bcm2708_fb_set_bitfields(&fb->fb.var);
1049 : :
1050 : : /*
1051 : : * Allocate colourmap.
1052 : : */
1053 : 1212 : fb_set_var(&fb->fb, &fb->fb.var);
1054 : :
1055 : 1212 : ret = bcm2708_fb_set_par(&fb->fb);
1056 : :
1057 [ + - ]: 1212 : if (ret)
1058 : : return ret;
1059 : :
1060 : 1212 : ret = register_framebuffer(&fb->fb);
1061 : :
1062 [ - + ]: 1212 : if (ret == 0)
1063 : : goto out;
1064 : :
1065 : 0 : dev_warn(fb->fb.dev, "Unable to register framebuffer (%d)\n", ret);
1066 : : out:
1067 : 1212 : return ret;
1068 : : }
1069 : :
1070 : 404 : static int bcm2708_fb_probe(struct platform_device *dev)
1071 : : {
1072 : : struct device_node *fw_np;
1073 : : struct rpi_firmware *fw;
1074 : : int ret, i;
1075 : : u32 num_displays;
1076 : : struct bcm2708_fb_dev *fbdev;
1077 : : struct { u32 base, length; } gpu_mem;
1078 : :
1079 : 404 : fbdev = devm_kzalloc(&dev->dev, sizeof(*fbdev), GFP_KERNEL);
1080 : :
1081 [ + - ]: 404 : if (!fbdev)
1082 : : return -ENOMEM;
1083 : :
1084 : 404 : fw_np = of_parse_phandle(dev->dev.of_node, "firmware", 0);
1085 : :
1086 : : /* Remove comment when booting without Device Tree is no longer supported
1087 : : * if (!fw_np) {
1088 : : * dev_err(&dev->dev, "Missing firmware node\n");
1089 : : * return -ENOENT;
1090 : : * }
1091 : : */
1092 : 404 : fw = rpi_firmware_get(fw_np);
1093 : 404 : fbdev->fw = fw;
1094 : :
1095 [ + - ]: 404 : if (!fw)
1096 : : return -EPROBE_DEFER;
1097 : :
1098 : 404 : ret = rpi_firmware_property(fw,
1099 : : RPI_FIRMWARE_FRAMEBUFFER_GET_NUM_DISPLAYS,
1100 : : &num_displays, sizeof(u32));
1101 : :
1102 : : /* If we fail to get the number of displays, or it returns 0, then
1103 : : * assume old firmware that doesn't have the mailbox call, so just
1104 : : * set one display
1105 : : */
1106 [ + - - + ]: 404 : if (ret || num_displays == 0) {
1107 : 0 : dev_err(&dev->dev,
1108 : : "Unable to determine number of FBs. Disabling driver.\n");
1109 : 0 : return -ENOENT;
1110 : : } else {
1111 : 404 : fbdev->firmware_supports_multifb = 1;
1112 : : }
1113 : :
1114 [ + - ]: 404 : if (num_displays > MAX_FRAMEBUFFERS) {
1115 : 404 : dev_warn(&dev->dev,
1116 : : "More displays reported from firmware than supported in driver (%u vs %u)",
1117 : : num_displays, MAX_FRAMEBUFFERS);
1118 : 404 : num_displays = MAX_FRAMEBUFFERS;
1119 : : }
1120 : :
1121 : 404 : dev_info(&dev->dev, "FB found %d display(s)\n", num_displays);
1122 : :
1123 : : /* Set up the DMA information. Note we have just one set of DMA
1124 : : * parameters to work with all the FB's so requires synchronising when
1125 : : * being used
1126 : : */
1127 : :
1128 : 404 : mutex_init(&fbdev->dma_mutex);
1129 : :
1130 : 808 : fbdev->cb_base = dma_alloc_wc(&dev->dev, SZ_64K,
1131 : : &fbdev->cb_handle,
1132 : : GFP_KERNEL);
1133 [ - + ]: 404 : if (!fbdev->cb_base) {
1134 : 0 : dev_err(&dev->dev, "cannot allocate DMA CBs\n");
1135 : : ret = -ENOMEM;
1136 : 0 : goto free_fb;
1137 : : }
1138 : :
1139 : 404 : ret = bcm_dma_chan_alloc(BCM_DMA_FEATURE_BULK,
1140 : : &fbdev->dma_chan_base,
1141 : : &fbdev->dma_irq);
1142 [ - + ]: 404 : if (ret < 0) {
1143 : 0 : dev_err(&dev->dev, "Couldn't allocate a DMA channel\n");
1144 : 0 : goto free_cb;
1145 : : }
1146 : 404 : fbdev->dma_chan = ret;
1147 : :
1148 : 404 : ret = request_irq(fbdev->dma_irq, bcm2708_fb_dma_irq,
1149 : : 0, "bcm2708_fb DMA", fbdev);
1150 [ - + ]: 404 : if (ret) {
1151 : 0 : dev_err(&dev->dev,
1152 : : "Failed to request DMA irq\n");
1153 : 0 : goto free_dma_chan;
1154 : : }
1155 : :
1156 : 404 : rpi_firmware_property(fbdev->fw,
1157 : : RPI_FIRMWARE_GET_VC_MEMORY,
1158 : : &gpu_mem, sizeof(gpu_mem));
1159 : :
1160 [ + + ]: 1616 : for (i = 0; i < num_displays; i++) {
1161 : 1212 : struct bcm2708_fb *fb = &fbdev->displays[i];
1162 : :
1163 : 1212 : fb->display_settings.display_num = i;
1164 : 1212 : fb->dev = dev;
1165 : 1212 : fb->fb.device = &dev->dev;
1166 : 1212 : fb->fbdev = fbdev;
1167 : :
1168 : 1212 : fb->gpu.base = gpu_mem.base;
1169 : 1212 : fb->gpu.length = gpu_mem.length;
1170 : :
1171 [ + - ]: 1212 : if (fbdev->firmware_supports_multifb) {
1172 : 1212 : ret = rpi_firmware_property(fw,
1173 : : RPI_FIRMWARE_FRAMEBUFFER_GET_DISPLAY_SETTINGS,
1174 : 1212 : &fb->display_settings,
1175 : : GET_DISPLAY_SETTINGS_PAYLOAD_SIZE);
1176 : : } else {
1177 : 0 : memset(&fb->display_settings, 0,
1178 : : sizeof(fb->display_settings));
1179 : : }
1180 : :
1181 : 1212 : ret = bcm2708_fb_register(fb);
1182 : :
1183 [ + - ]: 1212 : if (ret == 0) {
1184 : 1212 : bcm2708_fb_debugfs_init(fb);
1185 : :
1186 : 1212 : fbdev->num_displays++;
1187 : :
1188 : 1212 : dev_info(&dev->dev,
1189 : : "Registered framebuffer for display %u, size %ux%u\n",
1190 : : fb->display_settings.display_num,
1191 : : fb->fb.var.xres,
1192 : : fb->fb.var.yres);
1193 : : } else {
1194 : : // Use this to flag if this FB entry is in use.
1195 : 0 : fb->fbdev = NULL;
1196 : : }
1197 : : }
1198 : :
1199 : : // Did we actually successfully create any FB's?
1200 [ + - ]: 404 : if (fbdev->num_displays) {
1201 : 404 : init_waitqueue_head(&fbdev->dma_waitq);
1202 : : platform_set_drvdata(dev, fbdev);
1203 : 404 : return ret;
1204 : : }
1205 : :
1206 : : free_dma_chan:
1207 : 0 : bcm_dma_chan_free(fbdev->dma_chan);
1208 : : free_cb:
1209 : 0 : dma_free_wc(&dev->dev, SZ_64K, fbdev->cb_base,
1210 : : fbdev->cb_handle);
1211 : : free_fb:
1212 : 0 : dev_err(&dev->dev, "probe failed, err %d\n", ret);
1213 : :
1214 : 0 : return ret;
1215 : : }
1216 : :
1217 : 0 : static int bcm2708_fb_remove(struct platform_device *dev)
1218 : : {
1219 : : struct bcm2708_fb_dev *fbdev = platform_get_drvdata(dev);
1220 : : int i;
1221 : :
1222 : : platform_set_drvdata(dev, NULL);
1223 : :
1224 [ # # ]: 0 : for (i = 0; i < fbdev->num_displays; i++) {
1225 [ # # ]: 0 : if (fbdev->displays[i].fb.screen_base)
1226 : 0 : iounmap(fbdev->displays[i].fb.screen_base);
1227 : :
1228 [ # # ]: 0 : if (fbdev->displays[i].fbdev) {
1229 : 0 : unregister_framebuffer(&fbdev->displays[i].fb);
1230 : 0 : bcm2708_fb_debugfs_deinit(&fbdev->displays[i]);
1231 : : }
1232 : : }
1233 : :
1234 : 0 : dma_free_wc(&dev->dev, SZ_64K, fbdev->cb_base,
1235 : : fbdev->cb_handle);
1236 : 0 : bcm_dma_chan_free(fbdev->dma_chan);
1237 : 0 : free_irq(fbdev->dma_irq, fbdev);
1238 : :
1239 : : mutex_destroy(&fbdev->dma_mutex);
1240 : :
1241 : 0 : return 0;
1242 : : }
1243 : :
1244 : : static const struct of_device_id bcm2708_fb_of_match_table[] = {
1245 : : { .compatible = "brcm,bcm2708-fb", },
1246 : : {},
1247 : : };
1248 : : MODULE_DEVICE_TABLE(of, bcm2708_fb_of_match_table);
1249 : :
1250 : : static struct platform_driver bcm2708_fb_driver = {
1251 : : .probe = bcm2708_fb_probe,
1252 : : .remove = bcm2708_fb_remove,
1253 : : .driver = {
1254 : : .name = DRIVER_NAME,
1255 : : .owner = THIS_MODULE,
1256 : : .of_match_table = bcm2708_fb_of_match_table,
1257 : : },
1258 : : };
1259 : :
1260 : 404 : static int __init bcm2708_fb_init(void)
1261 : : {
1262 : 404 : return platform_driver_register(&bcm2708_fb_driver);
1263 : : }
1264 : :
1265 : : module_init(bcm2708_fb_init);
1266 : :
1267 : 0 : static void __exit bcm2708_fb_exit(void)
1268 : : {
1269 : 0 : platform_driver_unregister(&bcm2708_fb_driver);
1270 : 0 : }
1271 : :
1272 : : module_exit(bcm2708_fb_exit);
1273 : :
1274 : : module_param(fbwidth, int, 0644);
1275 : : module_param(fbheight, int, 0644);
1276 : : module_param(fbdepth, int, 0644);
1277 : : module_param(fbswap, int, 0644);
1278 : :
1279 : : MODULE_DESCRIPTION("BCM2708 framebuffer driver");
1280 : : MODULE_LICENSE("GPL");
1281 : :
1282 : : MODULE_PARM_DESC(fbwidth, "Width of ARM Framebuffer");
1283 : : MODULE_PARM_DESC(fbheight, "Height of ARM Framebuffer");
1284 : : MODULE_PARM_DESC(fbdepth, "Bit depth of ARM Framebuffer");
1285 : : MODULE_PARM_DESC(fbswap, "Swap order of red and blue in 24 and 32 bit modes");
|