472 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			472 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 *  linux/drivers/video/cfbcon_decor.c -- Framebuffer decor render functions
 | 
						|
 *
 | 
						|
 *  Copyright (C) 2004 Michal Januszewski <spock@gentoo.org>
 | 
						|
 *
 | 
						|
 *  Code based upon "Bootdecor" (C) 2001-2003
 | 
						|
 *       Volker Poplawski <volker@poplawski.de>,
 | 
						|
 *       Stefan Reinauer <stepan@suse.de>,
 | 
						|
 *       Steffen Winterfeldt <snwint@suse.de>,
 | 
						|
 *       Michael Schroeder <mls@suse.de>,
 | 
						|
 *       Ken Wimer <wimer@suse.de>.
 | 
						|
 *
 | 
						|
 *  This file is subject to the terms and conditions of the GNU General Public
 | 
						|
 *  License.  See the file COPYING in the main directory of this archive for
 | 
						|
 *  more details.
 | 
						|
 */
 | 
						|
#include <linux/module.h>
 | 
						|
#include <linux/types.h>
 | 
						|
#include <linux/fb.h>
 | 
						|
#include <linux/selection.h>
 | 
						|
#include <linux/vt_kern.h>
 | 
						|
#include <asm/irq.h>
 | 
						|
#include <asm/system.h>
 | 
						|
 | 
						|
#include "fbcon.h"
 | 
						|
#include "fbcondecor.h"
 | 
						|
 | 
						|
#define parse_pixel(shift,bpp,type)            \
 | 
						|
 do {                  \
 | 
						|
   if (d & (0x80 >> (shift)))          \
 | 
						|
     dd2[(shift)] = fgx;         \
 | 
						|
   else                \
 | 
						|
     dd2[(shift)] = transparent ? *(type *)decor_src : bgx;  \
 | 
						|
   decor_src += (bpp);           \
 | 
						|
 } while (0)               \
 | 
						|
 | 
						|
extern int get_color(struct vc_data *vc, struct fb_info *info,
 | 
						|
        u16 c, int is_fg);
 | 
						|
 | 
						|
void fbcon_decor_fix_pseudo_pal(struct fb_info *info, struct vc_data *vc)
 | 
						|
{
 | 
						|
 int i, j, k;
 | 
						|
 int minlen = min(min(info->var.red.length, info->var.green.length),
 | 
						|
          info->var.blue.length);
 | 
						|
 u32 col;
 | 
						|
 | 
						|
 for (j = i = 0; i < 16; i++) {
 | 
						|
   k = color_table[i];
 | 
						|
 | 
						|
   col = ((vc->vc_palette[j++]  >> (8-minlen))
 | 
						|
     << info->var.red.offset);
 | 
						|
   col |= ((vc->vc_palette[j++] >> (8-minlen))
 | 
						|
     << info->var.green.offset);
 | 
						|
   col |= ((vc->vc_palette[j++] >> (8-minlen))
 | 
						|
     << info->var.blue.offset);
 | 
						|
     ((u32 *)info->pseudo_palette)[k] = col;
 | 
						|
 }
 | 
						|
}
 | 
						|
 | 
						|
void fbcon_decor_renderc(struct fb_info *info, int ypos, int xpos, int height,
 | 
						|
         int width, u8* src, u32 fgx, u32 bgx, u8 transparent)
 | 
						|
{
 | 
						|
 unsigned int x, y;
 | 
						|
 u32 dd;
 | 
						|
 int bytespp = ((info->var.bits_per_pixel + 7) >> 3);
 | 
						|
 unsigned int d = ypos * info->fix.line_length + xpos * bytespp;
 | 
						|
 unsigned int ds = (ypos * info->var.xres + xpos) * bytespp;
 | 
						|
 u16 dd2[4];
 | 
						|
 | 
						|
 u8* decor_src = (u8 *)(info->bgdecor.data + ds);
 | 
						|
 u8* dst = (u8 *)(info->screen_base + d);
 | 
						|
 | 
						|
 if ((ypos + height) > info->var.yres || (xpos + width) > info->var.xres)
 | 
						|
   return;
 | 
						|
 | 
						|
 for (y = 0; y < height; y++) {
 | 
						|
   switch (info->var.bits_per_pixel) {
 | 
						|
 | 
						|
   case 32:
 | 
						|
     for (x = 0; x < width; x++) {
 | 
						|
 | 
						|
       if ((x & 7) == 0)
 | 
						|
         d = *src++;
 | 
						|
       if (d & 0x80)
 | 
						|
         dd = fgx;
 | 
						|
       else
 | 
						|
         dd = transparent ?
 | 
						|
              *(u32 *)decor_src : bgx;
 | 
						|
 | 
						|
       d <<= 1;
 | 
						|
       decor_src += 4;
 | 
						|
       fb_writel(dd, dst);
 | 
						|
       dst += 4;
 | 
						|
     }
 | 
						|
     break;
 | 
						|
   case 24:
 | 
						|
     for (x = 0; x < width; x++) {
 | 
						|
 | 
						|
       if ((x & 7) == 0)
 | 
						|
         d = *src++;
 | 
						|
       if (d & 0x80)
 | 
						|
         dd = fgx;
 | 
						|
       else
 | 
						|
         dd = transparent ?
 | 
						|
              (*(u32 *)decor_src & 0xffffff) : bgx;
 | 
						|
 | 
						|
       d <<= 1;
 | 
						|
       decor_src += 3;
 | 
						|
#ifdef __LITTLE_ENDIAN
 | 
						|
       fb_writew(dd & 0xffff, dst);
 | 
						|
       dst += 2;
 | 
						|
       fb_writeb((dd >> 16), dst);
 | 
						|
#else
 | 
						|
       fb_writew(dd >> 8, dst);
 | 
						|
       dst += 2;
 | 
						|
       fb_writeb(dd & 0xff, dst);
 | 
						|
#endif
 | 
						|
       dst++;
 | 
						|
     }
 | 
						|
     break;
 | 
						|
   case 16:
 | 
						|
     for (x = 0; x < width; x += 2) {
 | 
						|
           if ((x & 7) == 0)
 | 
						|
         d = *src++;
 | 
						|
 | 
						|
       parse_pixel(0, 2, u16);
 | 
						|
       parse_pixel(1, 2, u16);
 | 
						|
#ifdef __LITTLE_ENDIAN
 | 
						|
       dd = dd2[0] | (dd2[1] << 16);
 | 
						|
#else
 | 
						|
       dd = dd2[1] | (dd2[0] << 16);
 | 
						|
#endif
 | 
						|
       d <<= 2;
 | 
						|
       fb_writel(dd, dst);
 | 
						|
       dst += 4;
 | 
						|
     }
 | 
						|
     break;
 | 
						|
 | 
						|
   case 8:
 | 
						|
     for (x = 0; x < width; x += 4) {
 | 
						|
       if ((x & 7) == 0)
 | 
						|
         d = *src++;
 | 
						|
 | 
						|
       parse_pixel(0, 1, u8);
 | 
						|
       parse_pixel(1, 1, u8);
 | 
						|
       parse_pixel(2, 1, u8);
 | 
						|
       parse_pixel(3, 1, u8);
 | 
						|
 | 
						|
#ifdef __LITTLE_ENDIAN
 | 
						|
       dd = dd2[0] | (dd2[1] << 8) | (dd2[2] << 16) | (dd2[3] << 24);
 | 
						|
#else
 | 
						|
       dd = dd2[3] | (dd2[2] << 8) | (dd2[1] << 16) | (dd2[0] << 24);
 | 
						|
#endif
 | 
						|
       d <<= 4;
 | 
						|
       fb_writel(dd, dst);
 | 
						|
       dst += 4;
 | 
						|
     }
 | 
						|
   }
 | 
						|
 | 
						|
   dst += info->fix.line_length - width * bytespp;
 | 
						|
   decor_src += (info->var.xres - width) * bytespp;
 | 
						|
     }
 | 
						|
}
 | 
						|
 | 
						|
#define cc2cx(a)             \
 | 
						|
 ((info->fix.visual == FB_VISUAL_TRUECOLOR ||    \
 | 
						|
   info->fix.visual == FB_VISUAL_DIRECTCOLOR) ?    \
 | 
						|
  ((u32*)info->pseudo_palette)[a] : a)
 | 
						|
 | 
						|
void fbcon_decor_putcs(struct vc_data *vc, struct fb_info *info,
 | 
						|
      const unsigned short *s, int count, int yy, int xx)
 | 
						|
{
 | 
						|
 unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff;
 | 
						|
 struct fbcon_ops *ops = info->fbcon_par;
 | 
						|
 int fg_color, bg_color, transparent;
 | 
						|
 u8 *src;
 | 
						|
 u32 bgx, fgx;
 | 
						|
 u16 c = scr_readw(s);
 | 
						|
 | 
						|
 fg_color = get_color(vc, info, c, 1);
 | 
						|
        bg_color = get_color(vc, info, c, 0);
 | 
						|
 | 
						|
 /* Don't paint the background image if console is blanked */
 | 
						|
 transparent = ops->blank_state ? 0 :
 | 
						|
   (vc->vc_decor.bg_color == bg_color);
 | 
						|
 | 
						|
 xx = xx * vc->vc_font.width + vc->vc_decor.tx;
 | 
						|
 yy = yy * vc->vc_font.height + vc->vc_decor.ty;
 | 
						|
 | 
						|
 fgx = cc2cx(fg_color);
 | 
						|
 bgx = cc2cx(bg_color);
 | 
						|
 | 
						|
 while (count--) {
 | 
						|
   c = scr_readw(s++);
 | 
						|
   src = vc->vc_font.data + (c & charmask) * vc->vc_font.height *
 | 
						|
         ((vc->vc_font.width + 7) >> 3);
 | 
						|
 | 
						|
   fbcon_decor_renderc(info, yy, xx, vc->vc_font.height,
 | 
						|
            vc->vc_font.width, src, fgx, bgx, transparent);
 | 
						|
   xx += vc->vc_font.width;
 | 
						|
 }
 | 
						|
}
 | 
						|
 | 
						|
void fbcon_decor_cursor(struct fb_info *info, struct fb_cursor *cursor)
 | 
						|
{
 | 
						|
 int i;
 | 
						|
 unsigned int dsize, s_pitch;
 | 
						|
 struct fbcon_ops *ops = info->fbcon_par;
 | 
						|
 struct vc_data* vc;
 | 
						|
 u8 *src;
 | 
						|
 | 
						|
 /* we really don't need any cursors while the console is blanked */
 | 
						|
 if (info->state != FBINFO_STATE_RUNNING || ops->blank_state)
 | 
						|
   return;
 | 
						|
 | 
						|
 vc = vc_cons[ops->currcon].d;
 | 
						|
 | 
						|
 src = kmalloc(64 + sizeof(struct fb_image), GFP_ATOMIC);
 | 
						|
 if (!src)
 | 
						|
   return;
 | 
						|
 | 
						|
 s_pitch = (cursor->image.width + 7) >> 3;
 | 
						|
 dsize = s_pitch * cursor->image.height;
 | 
						|
 if (cursor->enable) {
 | 
						|
   switch (cursor->rop) {
 | 
						|
   case ROP_XOR:
 | 
						|
     for (i = 0; i < dsize; i++)
 | 
						|
       src[i] = cursor->image.data[i] ^ cursor->mask[i];
 | 
						|
                        break;
 | 
						|
   case ROP_COPY:
 | 
						|
   default:
 | 
						|
     for (i = 0; i < dsize; i++)
 | 
						|
       src[i] = cursor->image.data[i] & cursor->mask[i];
 | 
						|
     break;
 | 
						|
   }
 | 
						|
 } else
 | 
						|
   memcpy(src, cursor->image.data, dsize);
 | 
						|
 | 
						|
 fbcon_decor_renderc(info,
 | 
						|
     cursor->image.dy + vc->vc_decor.ty,
 | 
						|
     cursor->image.dx + vc->vc_decor.tx,
 | 
						|
     cursor->image.height,
 | 
						|
     cursor->image.width,
 | 
						|
     (u8*)src,
 | 
						|
     cc2cx(cursor->image.fg_color),
 | 
						|
     cc2cx(cursor->image.bg_color),
 | 
						|
     cursor->image.bg_color == vc->vc_decor.bg_color);
 | 
						|
 | 
						|
 kfree(src);
 | 
						|
}
 | 
						|
 | 
						|
static void decorset(u8 *dst, int height, int width, int dstbytes,
 | 
						|
           u32 bgx, int bpp)
 | 
						|
{
 | 
						|
 int i;
 | 
						|
 | 
						|
 if (bpp == 8)
 | 
						|
   bgx |= bgx << 8;
 | 
						|
 if (bpp == 16 || bpp == 8)
 | 
						|
   bgx |= bgx << 16;
 | 
						|
 | 
						|
 while (height-- > 0) {
 | 
						|
   u8 *p = dst;
 | 
						|
 | 
						|
   switch (bpp) {
 | 
						|
 | 
						|
   case 32:
 | 
						|
     for (i=0; i < width; i++) {
 | 
						|
       fb_writel(bgx, p); p += 4;
 | 
						|
     }
 | 
						|
     break;
 | 
						|
   case 24:
 | 
						|
     for (i=0; i < width; i++) {
 | 
						|
#ifdef __LITTLE_ENDIAN
 | 
						|
       fb_writew((bgx & 0xffff),(u16*)p); p += 2;
 | 
						|
       fb_writeb((bgx >> 16),p++);
 | 
						|
#else
 | 
						|
       fb_writew((bgx >> 8),(u16*)p); p += 2;
 | 
						|
       fb_writeb((bgx & 0xff),p++);
 | 
						|
#endif
 | 
						|
     }
 | 
						|
   case 16:
 | 
						|
     for (i=0; i < width/4; i++) {
 | 
						|
       fb_writel(bgx,p); p += 4;
 | 
						|
       fb_writel(bgx,p); p += 4;
 | 
						|
     }
 | 
						|
     if (width & 2) {
 | 
						|
       fb_writel(bgx,p); p += 4;
 | 
						|
     }
 | 
						|
     if (width & 1)
 | 
						|
       fb_writew(bgx,(u16*)p);
 | 
						|
     break;
 | 
						|
   case 8:
 | 
						|
     for (i=0; i < width/4; i++) {
 | 
						|
       fb_writel(bgx,p); p += 4;
 | 
						|
     }
 | 
						|
 | 
						|
     if (width & 2) {
 | 
						|
       fb_writew(bgx,p); p += 2;
 | 
						|
     }
 | 
						|
     if (width & 1)
 | 
						|
       fb_writeb(bgx,(u8*)p);
 | 
						|
     break;
 | 
						|
 | 
						|
   }
 | 
						|
   dst += dstbytes;
 | 
						|
 }
 | 
						|
}
 | 
						|
 | 
						|
void fbcon_decor_copy(u8 *dst, u8 *src, int height, int width, int linebytes,
 | 
						|
      int srclinebytes, int bpp)
 | 
						|
{
 | 
						|
 int i;
 | 
						|
 | 
						|
 while (height-- > 0) {
 | 
						|
   u32 *p = (u32 *)dst;
 | 
						|
   u32 *q = (u32 *)src;
 | 
						|
 | 
						|
   switch (bpp) {
 | 
						|
 | 
						|
   case 32:
 | 
						|
     for (i=0; i < width; i++)
 | 
						|
       fb_writel(*q++, p++);
 | 
						|
     break;
 | 
						|
   case 24:
 | 
						|
     for (i=0; i < (width*3/4); i++)
 | 
						|
       fb_writel(*q++, p++);
 | 
						|
     if ((width*3) % 4) {
 | 
						|
       if (width & 2) {
 | 
						|
         fb_writeb(*(u8*)q, (u8*)p);
 | 
						|
       } else if (width & 1) {
 | 
						|
         fb_writew(*(u16*)q, (u16*)p);
 | 
						|
         fb_writeb(*(u8*)((u16*)q+1),(u8*)((u16*)p+2));
 | 
						|
       }
 | 
						|
     }
 | 
						|
     break;
 | 
						|
   case 16:
 | 
						|
     for (i=0; i < width/4; i++) {
 | 
						|
       fb_writel(*q++, p++);
 | 
						|
       fb_writel(*q++, p++);
 | 
						|
     }
 | 
						|
     if (width & 2)
 | 
						|
       fb_writel(*q++, p++);
 | 
						|
     if (width & 1)
 | 
						|
       fb_writew(*(u16*)q, (u16*)p);
 | 
						|
     break;
 | 
						|
   case 8:
 | 
						|
     for (i=0; i < width/4; i++)
 | 
						|
       fb_writel(*q++, p++);
 | 
						|
 | 
						|
     if (width & 2) {
 | 
						|
       fb_writew(*(u16*)q, (u16*)p);
 | 
						|
       q = (u32*) ((u16*)q + 1);
 | 
						|
       p = (u32*) ((u16*)p + 1);
 | 
						|
     }
 | 
						|
     if (width & 1)
 | 
						|
       fb_writeb(*(u8*)q, (u8*)p);
 | 
						|
     break;
 | 
						|
   }
 | 
						|
 | 
						|
   dst += linebytes;
 | 
						|
   src += srclinebytes;
 | 
						|
 }
 | 
						|
}
 | 
						|
 | 
						|
static void decorfill(struct fb_info *info, int sy, int sx, int height,
 | 
						|
          int width)
 | 
						|
{
 | 
						|
 int bytespp = ((info->var.bits_per_pixel + 7) >> 3);
 | 
						|
 int d  = sy * info->fix.line_length + sx * bytespp;
 | 
						|
 int ds = (sy * info->var.xres + sx) * bytespp;
 | 
						|
 | 
						|
 fbcon_decor_copy((u8 *)(info->screen_base + d), (u8 *)(info->bgdecor.data + ds),
 | 
						|
       height, width, info->fix.line_length, info->var.xres * bytespp,
 | 
						|
       info->var.bits_per_pixel);
 | 
						|
}
 | 
						|
 | 
						|
void fbcon_decor_clear(struct vc_data *vc, struct fb_info *info, int sy, int sx,
 | 
						|
       int height, int width)
 | 
						|
{
 | 
						|
 int bgshift = (vc->vc_hi_font_mask) ? 13 : 12;
 | 
						|
 int bg_color = attr_bgcol_ec(bgshift, vc);
 | 
						|
 int transparent = vc->vc_decor.bg_color == bg_color;
 | 
						|
 struct fbcon_ops *ops = info->fbcon_par;
 | 
						|
 u8 *dst;
 | 
						|
 | 
						|
 sy = sy * vc->vc_font.height + vc->vc_decor.ty;
 | 
						|
 sx = sx * vc->vc_font.width + vc->vc_decor.tx;
 | 
						|
 height *= vc->vc_font.height;
 | 
						|
 width *= vc->vc_font.width;
 | 
						|
 | 
						|
 /* Don't paint the background image if console is blanked */
 | 
						|
 if (transparent && !ops->blank_state) {
 | 
						|
   decorfill(info, sy, sx, height, width);
 | 
						|
 } else {
 | 
						|
   dst = (u8 *)(info->screen_base + sy * info->fix.line_length +
 | 
						|
          sx * ((info->var.bits_per_pixel + 7) >> 3));
 | 
						|
   decorset(dst, height, width, info->fix.line_length, cc2cx(bg_color),
 | 
						|
       info->var.bits_per_pixel);
 | 
						|
 }
 | 
						|
}
 | 
						|
 | 
						|
void fbcon_decor_clear_margins(struct vc_data *vc, struct fb_info *info,
 | 
						|
         int bottom_only)
 | 
						|
{
 | 
						|
 unsigned int tw = vc->vc_cols*vc->vc_font.width;
 | 
						|
 unsigned int th = vc->vc_rows*vc->vc_font.height;
 | 
						|
 | 
						|
 if (!bottom_only) {
 | 
						|
   /* top margin */
 | 
						|
   decorfill(info, 0, 0, vc->vc_decor.ty, info->var.xres);
 | 
						|
   /* left margin */
 | 
						|
   decorfill(info, vc->vc_decor.ty, 0, th, vc->vc_decor.tx);
 | 
						|
   /* right margin */
 | 
						|
   decorfill(info, vc->vc_decor.ty, vc->vc_decor.tx + tw, th,
 | 
						|
        info->var.xres - vc->vc_decor.tx - tw);
 | 
						|
 }
 | 
						|
 decorfill(info, vc->vc_decor.ty + th, 0,
 | 
						|
      info->var.yres - vc->vc_decor.ty - th, info->var.xres);
 | 
						|
}
 | 
						|
 | 
						|
void fbcon_decor_bmove_redraw(struct vc_data *vc, struct fb_info *info, int y,
 | 
						|
        int sx, int dx, int width)
 | 
						|
{
 | 
						|
 u16 *d = (u16 *) (vc->vc_origin + vc->vc_size_row * y + dx * 2);
 | 
						|
 u16 *s = d + (dx - sx);
 | 
						|
 u16 *start = d;
 | 
						|
 u16 *ls = d;
 | 
						|
 u16 *le = d + width;
 | 
						|
 u16 c;
 | 
						|
 int x = dx;
 | 
						|
 u16 attr = 1;
 | 
						|
 | 
						|
 do {
 | 
						|
   c = scr_readw(d);
 | 
						|
   if (attr != (c & 0xff00)) {
 | 
						|
     attr = c & 0xff00;
 | 
						|
     if (d > start) {
 | 
						|
       fbcon_decor_putcs(vc, info, start, d - start, y, x);
 | 
						|
       x += d - start;
 | 
						|
       start = d;
 | 
						|
     }
 | 
						|
   }
 | 
						|
   if (s >= ls && s < le && c == scr_readw(s)) {
 | 
						|
     if (d > start) {
 | 
						|
       fbcon_decor_putcs(vc, info, start, d - start, y, x);
 | 
						|
       x += d - start + 1;
 | 
						|
       start = d + 1;
 | 
						|
     } else {
 | 
						|
       x++;
 | 
						|
       start++;
 | 
						|
     }
 | 
						|
   }
 | 
						|
   s++;
 | 
						|
   d++;
 | 
						|
 } while (d < le);
 | 
						|
 if (d > start)
 | 
						|
   fbcon_decor_putcs(vc, info, start, d - start, y, x);
 | 
						|
}
 | 
						|
 | 
						|
void fbcon_decor_blank(struct vc_data *vc, struct fb_info *info, int blank)
 | 
						|
{
 | 
						|
 if (blank) {
 | 
						|
   decorset((u8 *)info->screen_base, info->var.yres, info->var.xres,
 | 
						|
       info->fix.line_length, 0, info->var.bits_per_pixel);
 | 
						|
 } else {
 | 
						|
   update_screen(vc);
 | 
						|
   fbcon_decor_clear_margins(vc, info, 0);
 | 
						|
 }
 | 
						|
}
 | 
						|
 |