Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
fbcon_ccw.c
Go to the documentation of this file.
1 /*
2  * linux/drivers/video/console/fbcon_ccw.c -- Software Rotation - 270 degrees
3  *
4  * Copyright (C) 2005 Antonino Daplas <adaplas @pol.net>
5  *
6  * This file is subject to the terms and conditions of the GNU General Public
7  * License. See the file COPYING in the main directory of this archive for
8  * more details.
9  */
10 
11 #include <linux/module.h>
12 #include <linux/slab.h>
13 #include <linux/string.h>
14 #include <linux/fb.h>
15 #include <linux/vt_kern.h>
16 #include <linux/console.h>
17 #include <asm/types.h>
18 #include "fbcon.h"
19 #include "fbcon_rotate.h"
20 
21 /*
22  * Rotation 270 degrees
23  */
24 
25 static void ccw_update_attr(u8 *dst, u8 *src, int attribute,
26  struct vc_data *vc)
27 {
28  int i, j, offset = (vc->vc_font.height < 10) ? 1 : 2;
29  int width = (vc->vc_font.height + 7) >> 3;
30  int mod = vc->vc_font.height % 8;
31  u8 c, msk = ~(0xff << offset), msk1 = 0;
32 
33  if (mod)
34  msk <<= (8 - mod);
35 
36  if (offset > mod)
37  msk1 |= 0x01;
38 
39  for (i = 0; i < vc->vc_font.width; i++) {
40  for (j = 0; j < width; j++) {
41  c = *src;
42 
43  if (attribute & FBCON_ATTRIBUTE_UNDERLINE) {
44  if (j == width - 1)
45  c |= msk;
46 
47  if (msk1 && j == width - 2)
48  c |= msk1;
49  }
50 
51  if (attribute & FBCON_ATTRIBUTE_BOLD && i)
52  *(dst - width) |= c;
53 
54  if (attribute & FBCON_ATTRIBUTE_REVERSE)
55  c = ~c;
56  src++;
57  *dst++ = c;
58  }
59  }
60 }
61 
62 
63 static void ccw_bmove(struct vc_data *vc, struct fb_info *info, int sy,
64  int sx, int dy, int dx, int height, int width)
65 {
66  struct fbcon_ops *ops = info->fbcon_par;
67  struct fb_copyarea area;
68  u32 vyres = GETVYRES(ops->p->scrollmode, info);
69 
70  area.sx = sy * vc->vc_font.height;
71  area.sy = vyres - ((sx + width) * vc->vc_font.width);
72  area.dx = dy * vc->vc_font.height;
73  area.dy = vyres - ((dx + width) * vc->vc_font.width);
74  area.width = height * vc->vc_font.height;
75  area.height = width * vc->vc_font.width;
76 
77  info->fbops->fb_copyarea(info, &area);
78 }
79 
80 static void ccw_clear(struct vc_data *vc, struct fb_info *info, int sy,
81  int sx, int height, int width)
82 {
83  struct fbcon_ops *ops = info->fbcon_par;
84  struct fb_fillrect region;
85  int bgshift = (vc->vc_hi_font_mask) ? 13 : 12;
86  u32 vyres = GETVYRES(ops->p->scrollmode, info);
87 
88  region.color = attr_bgcol_ec(bgshift,vc,info);
89  region.dx = sy * vc->vc_font.height;
90  region.dy = vyres - ((sx + width) * vc->vc_font.width);
91  region.height = width * vc->vc_font.width;
92  region.width = height * vc->vc_font.height;
93  region.rop = ROP_COPY;
94 
95  info->fbops->fb_fillrect(info, &region);
96 }
97 
98 static inline void ccw_putcs_aligned(struct vc_data *vc, struct fb_info *info,
99  const u16 *s, u32 attr, u32 cnt,
100  u32 d_pitch, u32 s_pitch, u32 cellsize,
101  struct fb_image *image, u8 *buf, u8 *dst)
102 {
103  struct fbcon_ops *ops = info->fbcon_par;
104  u16 charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff;
105  u32 idx = (vc->vc_font.height + 7) >> 3;
106  u8 *src;
107 
108  while (cnt--) {
109  src = ops->fontbuffer + (scr_readw(s--) & charmask)*cellsize;
110 
111  if (attr) {
112  ccw_update_attr(buf, src, attr, vc);
113  src = buf;
114  }
115 
116  if (likely(idx == 1))
117  __fb_pad_aligned_buffer(dst, d_pitch, src, idx,
118  vc->vc_font.width);
119  else
120  fb_pad_aligned_buffer(dst, d_pitch, src, idx,
121  vc->vc_font.width);
122 
123  dst += d_pitch * vc->vc_font.width;
124  }
125 
126  info->fbops->fb_imageblit(info, image);
127 }
128 
129 static void ccw_putcs(struct vc_data *vc, struct fb_info *info,
130  const unsigned short *s, int count, int yy, int xx,
131  int fg, int bg)
132 {
133  struct fb_image image;
134  struct fbcon_ops *ops = info->fbcon_par;
135  u32 width = (vc->vc_font.height + 7)/8;
136  u32 cellsize = width * vc->vc_font.width;
137  u32 maxcnt = info->pixmap.size/cellsize;
138  u32 scan_align = info->pixmap.scan_align - 1;
139  u32 buf_align = info->pixmap.buf_align - 1;
140  u32 cnt, pitch, size;
141  u32 attribute = get_attribute(info, scr_readw(s));
142  u8 *dst, *buf = NULL;
143  u32 vyres = GETVYRES(ops->p->scrollmode, info);
144 
145  if (!ops->fontbuffer)
146  return;
147 
148  image.fg_color = fg;
149  image.bg_color = bg;
150  image.dx = yy * vc->vc_font.height;
151  image.dy = vyres - ((xx + count) * vc->vc_font.width);
152  image.width = vc->vc_font.height;
153  image.depth = 1;
154 
155  if (attribute) {
156  buf = kmalloc(cellsize, GFP_KERNEL);
157  if (!buf)
158  return;
159  }
160 
161  s += count - 1;
162 
163  while (count) {
164  if (count > maxcnt)
165  cnt = maxcnt;
166  else
167  cnt = count;
168 
169  image.height = vc->vc_font.width * cnt;
170  pitch = ((image.width + 7) >> 3) + scan_align;
171  pitch &= ~scan_align;
172  size = pitch * image.height + buf_align;
173  size &= ~buf_align;
174  dst = fb_get_buffer_offset(info, &info->pixmap, size);
175  image.data = dst;
176  ccw_putcs_aligned(vc, info, s, attribute, cnt, pitch,
177  width, cellsize, &image, buf, dst);
178  image.dy += image.height;
179  count -= cnt;
180  s -= cnt;
181  }
182 
183  /* buf is always NULL except when in monochrome mode, so in this case
184  it's a gain to check buf against NULL even though kfree() handles
185  NULL pointers just fine */
186  if (unlikely(buf))
187  kfree(buf);
188 
189 }
190 
191 static void ccw_clear_margins(struct vc_data *vc, struct fb_info *info,
192  int bottom_only)
193 {
194  unsigned int cw = vc->vc_font.width;
195  unsigned int ch = vc->vc_font.height;
196  unsigned int rw = info->var.yres - (vc->vc_cols*cw);
197  unsigned int bh = info->var.xres - (vc->vc_rows*ch);
198  unsigned int bs = vc->vc_rows*ch;
199  struct fb_fillrect region;
200  int bgshift = (vc->vc_hi_font_mask) ? 13 : 12;
201 
202  region.color = attr_bgcol_ec(bgshift,vc,info);
203  region.rop = ROP_COPY;
204 
205  if (rw && !bottom_only) {
206  region.dx = 0;
207  region.dy = info->var.yoffset;
208  region.height = rw;
209  region.width = info->var.xres_virtual;
210  info->fbops->fb_fillrect(info, &region);
211  }
212 
213  if (bh) {
214  region.dx = info->var.xoffset + bs;
215  region.dy = 0;
216  region.height = info->var.yres_virtual;
217  region.width = bh;
218  info->fbops->fb_fillrect(info, &region);
219  }
220 }
221 
222 static void ccw_cursor(struct vc_data *vc, struct fb_info *info, int mode,
223  int softback_lines, int fg, int bg)
224 {
225  struct fb_cursor cursor;
226  struct fbcon_ops *ops = info->fbcon_par;
227  unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff;
228  int w = (vc->vc_font.height + 7) >> 3, c;
229  int y = real_y(ops->p, vc->vc_y);
230  int attribute, use_sw = (vc->vc_cursor_type & 0x10);
231  int err = 1, dx, dy;
232  char *src;
233  u32 vyres = GETVYRES(ops->p->scrollmode, info);
234 
235  if (!ops->fontbuffer)
236  return;
237 
238  cursor.set = 0;
239 
240  if (softback_lines) {
241  if (y + softback_lines >= vc->vc_rows) {
242  mode = CM_ERASE;
243  ops->cursor_flash = 0;
244  return;
245  } else
246  y += softback_lines;
247  }
248 
249  c = scr_readw((u16 *) vc->vc_pos);
250  attribute = get_attribute(info, c);
251  src = ops->fontbuffer + ((c & charmask) * (w * vc->vc_font.width));
252 
253  if (ops->cursor_state.image.data != src ||
254  ops->cursor_reset) {
255  ops->cursor_state.image.data = src;
256  cursor.set |= FB_CUR_SETIMAGE;
257  }
258 
259  if (attribute) {
260  u8 *dst;
261 
262  dst = kmalloc(w * vc->vc_font.width, GFP_ATOMIC);
263  if (!dst)
264  return;
265  kfree(ops->cursor_data);
266  ops->cursor_data = dst;
267  ccw_update_attr(dst, src, attribute, vc);
268  src = dst;
269  }
270 
271  if (ops->cursor_state.image.fg_color != fg ||
272  ops->cursor_state.image.bg_color != bg ||
273  ops->cursor_reset) {
274  ops->cursor_state.image.fg_color = fg;
275  ops->cursor_state.image.bg_color = bg;
276  cursor.set |= FB_CUR_SETCMAP;
277  }
278 
279  if (ops->cursor_state.image.height != vc->vc_font.width ||
280  ops->cursor_state.image.width != vc->vc_font.height ||
281  ops->cursor_reset) {
282  ops->cursor_state.image.height = vc->vc_font.width;
283  ops->cursor_state.image.width = vc->vc_font.height;
284  cursor.set |= FB_CUR_SETSIZE;
285  }
286 
287  dx = y * vc->vc_font.height;
288  dy = vyres - ((vc->vc_x + 1) * vc->vc_font.width);
289 
290  if (ops->cursor_state.image.dx != dx ||
291  ops->cursor_state.image.dy != dy ||
292  ops->cursor_reset) {
293  ops->cursor_state.image.dx = dx;
294  ops->cursor_state.image.dy = dy;
295  cursor.set |= FB_CUR_SETPOS;
296  }
297 
298  if (ops->cursor_state.hot.x || ops->cursor_state.hot.y ||
299  ops->cursor_reset) {
300  ops->cursor_state.hot.x = cursor.hot.y = 0;
301  cursor.set |= FB_CUR_SETHOT;
302  }
303 
304  if (cursor.set & FB_CUR_SETSIZE ||
305  vc->vc_cursor_type != ops->p->cursor_shape ||
306  ops->cursor_state.mask == NULL ||
307  ops->cursor_reset) {
308  char *tmp, *mask = kmalloc(w*vc->vc_font.width, GFP_ATOMIC);
309  int cur_height, size, i = 0;
310  int width = (vc->vc_font.width + 7)/8;
311 
312  if (!mask)
313  return;
314 
315  tmp = kmalloc(width * vc->vc_font.height, GFP_ATOMIC);
316 
317  if (!tmp) {
318  kfree(mask);
319  return;
320  }
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 
350  size = (vc->vc_font.height - cur_height) * width;
351  while (size--)
352  tmp[i++] = 0;
353  size = cur_height * width;
354  while (size--)
355  tmp[i++] = 0xff;
356  memset(mask, 0, w * vc->vc_font.width);
357  rotate_ccw(tmp, mask, vc->vc_font.width, vc->vc_font.height);
358  kfree(tmp);
359  }
360 
361  switch (mode) {
362  case CM_ERASE:
363  ops->cursor_state.enable = 0;
364  break;
365  case CM_DRAW:
366  case CM_MOVE:
367  default:
368  ops->cursor_state.enable = (use_sw) ? 0 : 1;
369  break;
370  }
371 
372  cursor.image.data = src;
373  cursor.image.fg_color = ops->cursor_state.image.fg_color;
374  cursor.image.bg_color = ops->cursor_state.image.bg_color;
375  cursor.image.dx = ops->cursor_state.image.dx;
376  cursor.image.dy = ops->cursor_state.image.dy;
377  cursor.image.height = ops->cursor_state.image.height;
378  cursor.image.width = ops->cursor_state.image.width;
379  cursor.hot.x = ops->cursor_state.hot.x;
380  cursor.hot.y = ops->cursor_state.hot.y;
381  cursor.mask = ops->cursor_state.mask;
382  cursor.enable = ops->cursor_state.enable;
383  cursor.image.depth = 1;
384  cursor.rop = ROP_XOR;
385 
386  if (info->fbops->fb_cursor)
387  err = info->fbops->fb_cursor(info, &cursor);
388 
389  if (err)
390  soft_cursor(info, &cursor);
391 
392  ops->cursor_reset = 0;
393 }
394 
395 static int ccw_update_start(struct fb_info *info)
396 {
397  struct fbcon_ops *ops = info->fbcon_par;
398  u32 yoffset;
399  u32 vyres = GETVYRES(ops->p->scrollmode, info);
400  int err;
401 
402  yoffset = (vyres - info->var.yres) - ops->var.xoffset;
403  ops->var.xoffset = ops->var.yoffset;
404  ops->var.yoffset = yoffset;
405  err = fb_pan_display(info, &ops->var);
406  ops->var.xoffset = info->var.xoffset;
407  ops->var.yoffset = info->var.yoffset;
408  ops->var.vmode = info->var.vmode;
409  return err;
410 }
411 
412 void fbcon_rotate_ccw(struct fbcon_ops *ops)
413 {
414  ops->bmove = ccw_bmove;
415  ops->clear = ccw_clear;
416  ops->putcs = ccw_putcs;
417  ops->clear_margins = ccw_clear_margins;
418  ops->cursor = ccw_cursor;
419  ops->update_start = ccw_update_start;
420 }
422 
423 MODULE_AUTHOR("Antonino Daplas <[email protected]>");
424 MODULE_DESCRIPTION("Console Rotation (270 degrees) Support");
425 MODULE_LICENSE("GPL");