aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/video/fbdev/core/bitblit.c
diff options
context:
space:
mode:
authorDaniel Vetter <daniel.vetter@ffwll.ch>2017-08-01 11:32:07 -0400
committerBartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>2017-08-01 11:32:07 -0400
commit6104c37094e729f3d4ce65797002112735d49cd1 (patch)
treecc0c38d8d2268109ff38a156c017fe556b207ab5 /drivers/video/fbdev/core/bitblit.c
parent52e2fecf6868099359f5dea2f72f8572661d4462 (diff)
fbcon: Make fbcon a built-time depency for fbdev
There's a bunch of folks who're trying to make printk less contended and faster, but there's a problem: printk uses the console_lock, and the console lock has become the BKL for all things fbdev/fbcon, which in turn pulled in half the drm subsystem under that lock. That's awkward. There reasons for that is probably just a historical accident: - fbcon is a runtime option of fbdev, i.e. at runtime you can pick whether your fbdev driver instances are used as kernel consoles. Unfortunately this wasn't implemented with some module option, but through some module loading magic: As long as you don't load fbcon.ko, there's no fbdev console support, but loading it (in any order wrt fbdev drivers) will create console instances for all fbdev drivers. - This was implemented through a notifier chain. fbcon.ko enumerates all fbdev instances at load time and also registers itself as listener in the fbdev notifier. The fbdev core tries to register new fbdev instances with fbcon using the notifier. - On top of that the modifier chain is also used at runtime by the fbdev subsystem to e.g. control backlights for panels. - The problem is that the notifier puts a mutex locking context between fbdev and fbcon, which mixes up the locking contexts for both the runtime usage and the register time usage to notify fbcon. And at runtime fbcon (through the fbdev core) might call into the notifier from a printk critical section while console_lock is held. - This means console_lock must be an outer lock for the entire fbdev subsystem, which also means it must be acquired when registering a new framebuffer driver as the outermost lock since we might call into fbcon (through the notifier) which would result in a locking inversion if fbcon would acquire the console_lock from its notifier callback (which it needs to register the console). - console_lock can be held anywhere, since printk can be called anywhere, and through the above story, plus drm/kms being an fbdev driver, we pull in a shocking amount of locking hiercharchy underneath the console_lock. Which makes cleaning up printk really hard (not even splitting console_lock into an rwsem is all that useful due to this). There's various ways to address this, but the cleanest would be to make fbcon a compile-time option, where fbdev directly calls the fbcon register functions from register_framebuffer, or dummy static inline versions if fbcon is disabled. Maybe augmented with a runtime knob to disable fbcon, if that's needed (for debugging perhaps). But this could break some users who rely on the magic "loading fbcon.ko enables/disables fbdev framebuffers at runtime" thing, even if that's unlikely. Hence we must be careful: 1. Create a compile-time dependency between fbcon and fbdev in the least minimal way. This is what this patch does. 2. Wait at least 1 year to give possible users time to scream about how we broke their setup. Unlikely, since all distros make fbcon compile-in, and embedded platforms only compile stuff they know they need anyway. But still. 3. Convert the notifier to direct functions calls, with dummy static inlines if fbcon is disabled. We'll still need the fb notifier for the other uses (like backlights), but we can probably move it into the fb core (atm it must be built-into vmlinux). 4. Push console_lock down the call-chain, until it is down in console_register again. 5. Finally start to clean up and rework the printk/console locking. For context of this saga see commit 50e244cc793d511b86adea24972f3a7264cae114 Author: Alan Cox <alan@linux.intel.com> Date: Fri Jan 25 10:28:15 2013 +1000 fb: rework locking to fix lock ordering on takeover plus the pile of commits on top that tried to make this all work without terminally upsetting lockdep. We've uncovered all this when console_lock lockdep annotations where added in commit daee779718a319ff9f83e1ba3339334ac650bb22 Author: Daniel Vetter <daniel.vetter@ffwll.ch> Date: Sat Sep 22 19:52:11 2012 +0200 console: implement lockdep support for console_lock On the patch itself: - Switch CONFIG_FRAMEBUFFER_CONSOLE to be a boolean, using the overall CONFIG_FB tristate to decided whether it should be a module or built-in. - At first I thought I could force the build depency with just a dummy symbol that fbcon.ko exports and fb.ko uses. But that leads to a module depency cycle (it works fine when built-in). Since this tight binding is the entire goal the simplest solution is to move all the fbcon modules (and there's a bunch of optinal source-files which are each modules of their own, for no good reason) into the overall fb.ko core module. That's a bit more than what I would have liked to do in this patch, but oh well. Signed-off-by: Daniel Vetter <daniel.vetter@intel.com> Cc: Alan Cox <alan@lxorguk.ukuu.org.uk> Cc: Sergey Senozhatsky <sergey.senozhatsky.work@gmail.com> Cc: Steven Rostedt <rostedt@goodmis.org> Reviewed-by: Sean Paul <seanpaul@chromium.org> Signed-off-by: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
Diffstat (limited to 'drivers/video/fbdev/core/bitblit.c')
-rw-r--r--drivers/video/fbdev/core/bitblit.c418
1 files changed, 418 insertions, 0 deletions
diff --git a/drivers/video/fbdev/core/bitblit.c b/drivers/video/fbdev/core/bitblit.c
new file mode 100644
index 000000000000..99f3a1c3d093
--- /dev/null
+++ b/drivers/video/fbdev/core/bitblit.c
@@ -0,0 +1,418 @@
1/*
2 * linux/drivers/video/console/bitblit.c -- BitBlitting Operation
3 *
4 * Originally from the 'accel_*' routines in drivers/video/console/fbcon.c
5 *
6 * Copyright (C) 2004 Antonino Daplas <adaplas @pol.net>
7 *
8 * This file is subject to the terms and conditions of the GNU General Public
9 * License. See the file COPYING in the main directory of this archive for
10 * more details.
11 */
12
13#include <linux/module.h>
14#include <linux/slab.h>
15#include <linux/string.h>
16#include <linux/fb.h>
17#include <linux/vt_kern.h>
18#include <linux/console.h>
19#include <asm/types.h>
20#include "fbcon.h"
21
22/*
23 * Accelerated handlers.
24 */
25static void update_attr(u8 *dst, u8 *src, int attribute,
26 struct vc_data *vc)
27{
28 int i, offset = (vc->vc_font.height < 10) ? 1 : 2;
29 int width = DIV_ROUND_UP(vc->vc_font.width, 8);
30 unsigned int cellsize = vc->vc_font.height * width;
31 u8 c;
32
33 offset = cellsize - (offset * width);
34 for (i = 0; i < cellsize; i++) {
35 c = src[i];
36 if (attribute & FBCON_ATTRIBUTE_UNDERLINE && i >= offset)
37 c = 0xff;
38 if (attribute & FBCON_ATTRIBUTE_BOLD)
39 c |= c >> 1;
40 if (attribute & FBCON_ATTRIBUTE_REVERSE)
41 c = ~c;
42 dst[i] = c;
43 }
44}
45
46static void bit_bmove(struct vc_data *vc, struct fb_info *info, int sy,
47 int sx, int dy, int dx, int height, int width)
48{
49 struct fb_copyarea area;
50
51 area.sx = sx * vc->vc_font.width;
52 area.sy = sy * vc->vc_font.height;
53 area.dx = dx * vc->vc_font.width;
54 area.dy = dy * vc->vc_font.height;
55 area.height = height * vc->vc_font.height;
56 area.width = width * vc->vc_font.width;
57
58 info->fbops->fb_copyarea(info, &area);
59}
60
61static void bit_clear(struct vc_data *vc, struct fb_info *info, int sy,
62 int sx, int height, int width)
63{
64 int bgshift = (vc->vc_hi_font_mask) ? 13 : 12;
65 struct fb_fillrect region;
66
67 region.color = attr_bgcol_ec(bgshift, vc, info);
68 region.dx = sx * vc->vc_font.width;
69 region.dy = sy * vc->vc_font.height;
70 region.width = width * vc->vc_font.width;
71 region.height = height * vc->vc_font.height;
72 region.rop = ROP_COPY;
73
74 info->fbops->fb_fillrect(info, &region);
75}
76
77static inline void bit_putcs_aligned(struct vc_data *vc, struct fb_info *info,
78 const u16 *s, u32 attr, u32 cnt,
79 u32 d_pitch, u32 s_pitch, u32 cellsize,
80 struct fb_image *image, u8 *buf, u8 *dst)
81{
82 u16 charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff;
83 u32 idx = vc->vc_font.width >> 3;
84 u8 *src;
85
86 while (cnt--) {
87 src = vc->vc_font.data + (scr_readw(s++)&
88 charmask)*cellsize;
89
90 if (attr) {
91 update_attr(buf, src, attr, vc);
92 src = buf;
93 }
94
95 if (likely(idx == 1))
96 __fb_pad_aligned_buffer(dst, d_pitch, src, idx,
97 image->height);
98 else
99 fb_pad_aligned_buffer(dst, d_pitch, src, idx,
100 image->height);
101
102 dst += s_pitch;
103 }
104
105 info->fbops->fb_imageblit(info, image);
106}
107
108static inline void bit_putcs_unaligned(struct vc_data *vc,
109 struct fb_info *info, const u16 *s,
110 u32 attr, u32 cnt, u32 d_pitch,
111 u32 s_pitch, u32 cellsize,
112 struct fb_image *image, u8 *buf,
113 u8 *dst)
114{
115 u16 charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff;
116 u32 shift_low = 0, mod = vc->vc_font.width % 8;
117 u32 shift_high = 8;
118 u32 idx = vc->vc_font.width >> 3;
119 u8 *src;
120
121 while (cnt--) {
122 src = vc->vc_font.data + (scr_readw(s++)&
123 charmask)*cellsize;
124
125 if (attr) {
126 update_attr(buf, src, attr, vc);
127 src = buf;
128 }
129
130 fb_pad_unaligned_buffer(dst, d_pitch, src, idx,
131 image->height, shift_high,
132 shift_low, mod);
133 shift_low += mod;
134 dst += (shift_low >= 8) ? s_pitch : s_pitch - 1;
135 shift_low &= 7;
136 shift_high = 8 - shift_low;
137 }
138
139 info->fbops->fb_imageblit(info, image);
140
141}
142
143static void bit_putcs(struct vc_data *vc, struct fb_info *info,
144 const unsigned short *s, int count, int yy, int xx,
145 int fg, int bg)
146{
147 struct fb_image image;
148 u32 width = DIV_ROUND_UP(vc->vc_font.width, 8);
149 u32 cellsize = width * vc->vc_font.height;
150 u32 maxcnt = info->pixmap.size/cellsize;
151 u32 scan_align = info->pixmap.scan_align - 1;
152 u32 buf_align = info->pixmap.buf_align - 1;
153 u32 mod = vc->vc_font.width % 8, cnt, pitch, size;
154 u32 attribute = get_attribute(info, scr_readw(s));
155 u8 *dst, *buf = NULL;
156
157 image.fg_color = fg;
158 image.bg_color = bg;
159 image.dx = xx * vc->vc_font.width;
160 image.dy = yy * vc->vc_font.height;
161 image.height = vc->vc_font.height;
162 image.depth = 1;
163
164 if (attribute) {
165 buf = kmalloc(cellsize, GFP_ATOMIC);
166 if (!buf)
167 return;
168 }
169
170 while (count) {
171 if (count > maxcnt)
172 cnt = maxcnt;
173 else
174 cnt = count;
175
176 image.width = vc->vc_font.width * cnt;
177 pitch = DIV_ROUND_UP(image.width, 8) + scan_align;
178 pitch &= ~scan_align;
179 size = pitch * image.height + buf_align;
180 size &= ~buf_align;
181 dst = fb_get_buffer_offset(info, &info->pixmap, size);
182 image.data = dst;
183
184 if (!mod)
185 bit_putcs_aligned(vc, info, s, attribute, cnt, pitch,
186 width, cellsize, &image, buf, dst);
187 else
188 bit_putcs_unaligned(vc, info, s, attribute, cnt,
189 pitch, width, cellsize, &image,
190 buf, dst);
191
192 image.dx += cnt * vc->vc_font.width;
193 count -= cnt;
194 s += cnt;
195 }
196
197 /* buf is always NULL except when in monochrome mode, so in this case
198 it's a gain to check buf against NULL even though kfree() handles
199 NULL pointers just fine */
200 if (unlikely(buf))
201 kfree(buf);
202
203}
204
205static void bit_clear_margins(struct vc_data *vc, struct fb_info *info,
206 int bottom_only)
207{
208 unsigned int cw = vc->vc_font.width;
209 unsigned int ch = vc->vc_font.height;
210 unsigned int rw = info->var.xres - (vc->vc_cols*cw);
211 unsigned int bh = info->var.yres - (vc->vc_rows*ch);
212 unsigned int rs = info->var.xres - rw;
213 unsigned int bs = info->var.yres - bh;
214 struct fb_fillrect region;
215
216 region.color = 0;
217 region.rop = ROP_COPY;
218
219 if (rw && !bottom_only) {
220 region.dx = info->var.xoffset + rs;
221 region.dy = 0;
222 region.width = rw;
223 region.height = info->var.yres_virtual;
224 info->fbops->fb_fillrect(info, &region);
225 }
226
227 if (bh) {
228 region.dx = info->var.xoffset;
229 region.dy = info->var.yoffset + bs;
230 region.width = rs;
231 region.height = bh;
232 info->fbops->fb_fillrect(info, &region);
233 }
234}
235
236static void bit_cursor(struct vc_data *vc, struct fb_info *info, int mode,
237 int softback_lines, int fg, int bg)
238{
239 struct fb_cursor cursor;
240 struct fbcon_ops *ops = info->fbcon_par;
241 unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff;
242 int w = DIV_ROUND_UP(vc->vc_font.width, 8), c;
243 int y = real_y(ops->p, vc->vc_y);
244 int attribute, use_sw = (vc->vc_cursor_type & 0x10);
245 int err = 1;
246 char *src;
247
248 cursor.set = 0;
249
250 if (softback_lines) {
251 if (y + softback_lines >= vc->vc_rows) {
252 mode = CM_ERASE;
253 ops->cursor_flash = 0;
254 return;
255 } else
256 y += softback_lines;
257 }
258
259 c = scr_readw((u16 *) vc->vc_pos);
260 attribute = get_attribute(info, c);
261 src = vc->vc_font.data + ((c & charmask) * (w * vc->vc_font.height));
262
263 if (ops->cursor_state.image.data != src ||
264 ops->cursor_reset) {
265 ops->cursor_state.image.data = src;
266 cursor.set |= FB_CUR_SETIMAGE;
267 }
268
269 if (attribute) {
270 u8 *dst;
271
272 dst = kmalloc(w * vc->vc_font.height, GFP_ATOMIC);
273 if (!dst)
274 return;
275 kfree(ops->cursor_data);
276 ops->cursor_data = dst;
277 update_attr(dst, src, attribute, vc);
278 src = dst;
279 }
280
281 if (ops->cursor_state.image.fg_color != fg ||
282 ops->cursor_state.image.bg_color != bg ||
283 ops->cursor_reset) {
284 ops->cursor_state.image.fg_color = fg;
285 ops->cursor_state.image.bg_color = bg;
286 cursor.set |= FB_CUR_SETCMAP;
287 }
288
289 if ((ops->cursor_state.image.dx != (vc->vc_font.width * vc->vc_x)) ||
290 (ops->cursor_state.image.dy != (vc->vc_font.height * y)) ||
291 ops->cursor_reset) {
292 ops->cursor_state.image.dx = vc->vc_font.width * vc->vc_x;
293 ops->cursor_state.image.dy = vc->vc_font.height * y;
294 cursor.set |= FB_CUR_SETPOS;
295 }
296
297 if (ops->cursor_state.image.height != vc->vc_font.height ||
298 ops->cursor_state.image.width != vc->vc_font.width ||
299 ops->cursor_reset) {
300 ops->cursor_state.image.height = vc->vc_font.height;
301 ops->cursor_state.image.width = vc->vc_font.width;
302 cursor.set |= FB_CUR_SETSIZE;
303 }
304
305 if (ops->cursor_state.hot.x || ops->cursor_state.hot.y ||
306 ops->cursor_reset) {
307 ops->cursor_state.hot.x = cursor.hot.y = 0;
308 cursor.set |= FB_CUR_SETHOT;
309 }
310
311 if (cursor.set & FB_CUR_SETSIZE ||
312 vc->vc_cursor_type != ops->p->cursor_shape ||
313 ops->cursor_state.mask == NULL ||
314 ops->cursor_reset) {
315 char *mask = kmalloc(w*vc->vc_font.height, GFP_ATOMIC);
316 int cur_height, size, i = 0;
317 u8 msk = 0xff;
318
319 if (!mask)
320 return;
321
322 kfree(ops->cursor_state.mask);
323 ops->cursor_state.mask = mask;
324
325 ops->p->cursor_shape = vc->vc_cursor_type;
326 cursor.set |= FB_CUR_SETSHAPE;
327
328 switch (ops->p->cursor_shape & CUR_HWMASK) {
329 case CUR_NONE:
330 cur_height = 0;
331 break;
332 case CUR_UNDERLINE:
333 cur_height = (vc->vc_font.height < 10) ? 1 : 2;
334 break;
335 case CUR_LOWER_THIRD:
336 cur_height = vc->vc_font.height/3;
337 break;
338 case CUR_LOWER_HALF:
339 cur_height = vc->vc_font.height >> 1;
340 break;
341 case CUR_TWO_THIRDS:
342 cur_height = (vc->vc_font.height << 1)/3;
343 break;
344 case CUR_BLOCK:
345 default:
346 cur_height = vc->vc_font.height;
347 break;
348 }
349 size = (vc->vc_font.height - cur_height) * w;
350 while (size--)
351 mask[i++] = ~msk;
352 size = cur_height * w;
353 while (size--)
354 mask[i++] = msk;
355 }
356
357 switch (mode) {
358 case CM_ERASE:
359 ops->cursor_state.enable = 0;
360 break;
361 case CM_DRAW:
362 case CM_MOVE:
363 default:
364 ops->cursor_state.enable = (use_sw) ? 0 : 1;
365 break;
366 }
367
368 cursor.image.data = src;
369 cursor.image.fg_color = ops->cursor_state.image.fg_color;
370 cursor.image.bg_color = ops->cursor_state.image.bg_color;
371 cursor.image.dx = ops->cursor_state.image.dx;
372 cursor.image.dy = ops->cursor_state.image.dy;
373 cursor.image.height = ops->cursor_state.image.height;
374 cursor.image.width = ops->cursor_state.image.width;
375 cursor.hot.x = ops->cursor_state.hot.x;
376 cursor.hot.y = ops->cursor_state.hot.y;
377 cursor.mask = ops->cursor_state.mask;
378 cursor.enable = ops->cursor_state.enable;
379 cursor.image.depth = 1;
380 cursor.rop = ROP_XOR;
381
382 if (info->fbops->fb_cursor)
383 err = info->fbops->fb_cursor(info, &cursor);
384
385 if (err)
386 soft_cursor(info, &cursor);
387
388 ops->cursor_reset = 0;
389}
390
391static int bit_update_start(struct fb_info *info)
392{
393 struct fbcon_ops *ops = info->fbcon_par;
394 int err;
395
396 err = fb_pan_display(info, &ops->var);
397 ops->var.xoffset = info->var.xoffset;
398 ops->var.yoffset = info->var.yoffset;
399 ops->var.vmode = info->var.vmode;
400 return err;
401}
402
403void fbcon_set_bitops(struct fbcon_ops *ops)
404{
405 ops->bmove = bit_bmove;
406 ops->clear = bit_clear;
407 ops->putcs = bit_putcs;
408 ops->clear_margins = bit_clear_margins;
409 ops->cursor = bit_cursor;
410 ops->update_start = bit_update_start;
411 ops->rotate_font = NULL;
412
413 if (ops->rotate)
414 fbcon_set_rotate(ops);
415}
416
417EXPORT_SYMBOL(fbcon_set_bitops);
418