1 2#include <linux/config.h> 3#include <linux/module.h> 4#include <linux/kernel.h> 5#include <linux/errno.h> 6#include <linux/string.h> 7#include <linux/tty.h> 8#include <linux/delay.h> 9#include <linux/interrupt.h> 10#include <linux/init.h> 11#include <linux/pm.h> 12#include <linux/fb.h> 13#include <video/fbcon.h> 14#include <video/fbcon-mfb.h> 15#include <video/fbcon-cfb2.h> 16#include <video/fbcon-cfb4.h> 17#include <video/fbcon-cfb8.h> 18#include <asm/io.h> 19#include <asm/bootinfo.h> 20#include <asm/uaccess.h> 21#include <asm/tx3912.h> 22#include "tx3912fb.h" 23 24/* 25 * Frame buffer, palette and console structures 26 */ 27static struct fb_info fb_info; 28static struct { u_char red, green, blue, pad; } palette[256]; 29#ifdef FBCON_HAS_CFB8 30static union { u16 cfb8[16]; } fbcon_cmap; 31#endif 32static struct display global_disp; 33static int currcon = 0; 34 35/* 36 * Interface used by the world 37 */ 38static int tx3912fb_get_fix(struct fb_fix_screeninfo *fix, int con, 39 struct fb_info *info); 40static int tx3912fb_get_var(struct fb_var_screeninfo *var, int con, 41 struct fb_info *info); 42static int tx3912fb_set_var(struct fb_var_screeninfo *var, int con, 43 struct fb_info *info); 44static int tx3912fb_get_cmap(struct fb_cmap *cmap, int kspc, int con, 45 struct fb_info *info); 46static int tx3912fb_set_cmap(struct fb_cmap *cmap, int kspc, int con, 47 struct fb_info *info); 48static int tx3912fb_ioctl(struct inode *inode, struct file *file, u_int cmd, 49 u_long arg, int con, struct fb_info *info); 50 51/* 52 * Interface used by console driver 53 */ 54int tx3912fb_init(void); 55static int tx3912fbcon_switch(int con, struct fb_info *info); 56static int tx3912fbcon_updatevar(int con, struct fb_info *info); 57static void tx3912fbcon_blank(int blank, struct fb_info *info); 58 59/* 60 * Macros 61 */ 62#define get_line_length(xres_virtual, bpp) \ 63 (u_long) (((int) xres_virtual * (int) bpp + 7) >> 3) 64 65/* 66 * Internal routines 67 */ 68static int tx3912fb_getcolreg(u_int regno, u_int *red, u_int *green, 69 u_int *blue, u_int *transp, struct fb_info *info); 70static int tx3912fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, 71 u_int transp, struct fb_info *info); 72static void tx3912fb_install_cmap(int con, struct fb_info *info); 73 74 75/* 76 * Frame buffer operations structure used by console driver 77 */ 78static struct fb_ops tx3912fb_ops = { 79 owner: THIS_MODULE, 80 fb_get_fix: tx3912fb_get_fix, 81 fb_get_var: tx3912fb_get_var, 82 fb_set_var: tx3912fb_set_var, 83 fb_get_cmap: tx3912fb_get_cmap, 84 fb_set_cmap: tx3912fb_set_cmap, 85 fb_ioctl: tx3912fb_ioctl, 86}; 87 88 89/* 90 * Get fixed display data 91 */ 92static int tx3912fb_get_fix(struct fb_fix_screeninfo *fix, int con, 93 struct fb_info *info) 94{ 95 struct display *display; 96 97 memset(fix, 0, sizeof(struct fb_fix_screeninfo)); 98 strcpy(fix->id, TX3912FB_NAME); 99 100 if (con == -1) 101 display = &global_disp; 102 else 103 display = &fb_display[con]; 104 105 fix->smem_start = tx3912fb_vaddr; 106 fix->smem_len = tx3912fb_size; 107 fix->type = display->type; 108 fix->type_aux = display->type_aux; 109 fix->xpanstep = 0; 110 fix->ypanstep = display->ypanstep; 111 fix->ywrapstep = display->ywrapstep; 112 fix->visual = display->visual; 113 fix->line_length = display->line_length; 114 fix->accel = FB_ACCEL_NONE; 115 116 return 0; 117} 118 119/* 120 * Get user display data 121 */ 122static int tx3912fb_get_var(struct fb_var_screeninfo *var, int con, 123 struct fb_info *info) 124{ 125 if (con == -1) 126 *var = tx3912fb_info; 127 else 128 *var = fb_display[con].var; 129 130 return 0; 131} 132 133/* 134 * Set user display data 135 */ 136static int tx3912fb_set_var(struct fb_var_screeninfo *var, int con, 137 struct fb_info *info) 138{ 139 int err, activate = var->activate; 140 int oldxres, oldyres, oldvxres, oldvyres, oldbpp; 141 u_long line_length; 142 struct display *display; 143 144 if (con == -1) 145 display = &global_disp; 146 else 147 display = &fb_display[con]; 148 149 /* 150 * FB_VMODE_CONUPDATE and FB_VMODE_SMOOTH_XPAN are equal 151 * as FB_VMODE_SMOOTH_XPAN is only used internally 152 */ 153 if (var->vmode & FB_VMODE_CONUPDATE) { 154 var->xoffset = display->var.xoffset; 155 var->yoffset = display->var.yoffset; 156 var->vmode |= FB_VMODE_YWRAP; 157 } 158 159 /* 160 * Make sure values are in range 161 */ 162 if (!var->xres) 163 var->xres = 1; 164 if (!var->yres) 165 var->yres = 1; 166 if (var->xres > var->xres_virtual) 167 var->xres_virtual = var->xres; 168 if (var->yres > var->yres_virtual) 169 var->yres_virtual = var->yres; 170 if (var->bits_per_pixel <= 1) 171 var->bits_per_pixel = 1; 172 else if (var->bits_per_pixel <= 2) 173 var->bits_per_pixel = 2; 174 else if (var->bits_per_pixel <= 4) 175 var->bits_per_pixel = 4; 176 else if (var->bits_per_pixel <= 8) 177 var->bits_per_pixel = 8; 178 else 179 return -EINVAL; 180 181 /* 182 * Memory limit 183 */ 184 line_length = get_line_length(var->xres_virtual, var->bits_per_pixel); 185 if ((line_length * var->yres_virtual) > tx3912fb_size) 186 return -ENOMEM; 187 188 /* 189 * This is only for color and we only support 8-bit color 190 */ 191 if (var->bits_per_pixel) { 192 /* RGB 332 */ 193 var->red.offset = 5; 194 var->red.length = 3; 195 var->green.offset = 2; 196 var->green.length = 3; 197 var->blue.offset = 0; 198 var->blue.length = 2; 199 var->transp.offset = 0; 200 var->transp.length = 0; 201 } 202 var->red.msb_right = 0; 203 var->green.msb_right = 0; 204 var->blue.msb_right = 0; 205 var->transp.msb_right = 0; 206 207 /* 208 * Make changes if necessary 209 */ 210 if ((activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) { 211 212 oldxres = display->var.xres; 213 oldyres = display->var.yres; 214 oldvxres = display->var.xres_virtual; 215 oldvyres = display->var.yres_virtual; 216 oldbpp = display->var.bits_per_pixel; 217 display->var = *var; 218 219 if (oldxres != var->xres || oldyres != var->yres || 220 oldvxres != var->xres_virtual || 221 oldvyres != var->yres_virtual || 222 oldbpp != var->bits_per_pixel) { 223 224 display->screen_base = (u_char *) tx3912fb_vaddr; 225 226 switch (var->bits_per_pixel) { 227 case 1: 228 display->visual = FB_VISUAL_MONO10; 229 break; 230 case 2: 231 display->visual = FB_VISUAL_PSEUDOCOLOR; 232 case 4: 233 case 8: 234 display->visual = FB_VISUAL_TRUECOLOR; 235 break; 236 } 237 238 display->type = FB_TYPE_PACKED_PIXELS; 239 display->type_aux = 0; 240 display->ypanstep = 0; 241 display->ywrapstep = 0; 242 display->next_line = 243 display->line_length = 244 get_line_length(var->xres_virtual, 245 var->bits_per_pixel); 246 display->can_soft_blank = 0; 247 display->inverse = FB_IS_INVERSE; 248 249 switch (var->bits_per_pixel) { 250#ifdef CONFIG_FBCON_MFB 251 case 1: 252 display->dispsw = &fbcon_mfb; 253 break; 254#endif 255#ifdef CONFIG_FBCON_CFB2 256 case 2: 257 display->dispsw = &fbcon_cfb2; 258 break; 259#endif 260#ifdef CONFIG_FBCON_CFB4 261 case 4: 262 display->dispsw = &fbcon_cfb4; 263 break; 264#endif 265#ifdef CONFIG_FBCON_CFB8 266 case 8: 267 display->dispsw = &fbcon_cfb8; 268 display->dispsw_data = fbcon_cmap.cfb8; 269 break; 270#endif 271 default: 272 display->dispsw = &fbcon_dummy; 273 break; 274 } 275 276 if (fb_info.changevar) 277 (*fb_info.changevar)(con); 278 } 279 280 if (oldbpp != var->bits_per_pixel) { 281 if ((err = fb_alloc_cmap(&display->cmap, 0, 0))) 282 return err; 283 tx3912fb_install_cmap(con, info); 284 } 285 } 286 287 return 0; 288} 289 290/* 291 * Get the colormap 292 */ 293static int tx3912fb_get_cmap(struct fb_cmap *cmap, int kspc, int con, 294 struct fb_info *info) 295{ 296 if (con == currcon) 297 return fb_get_cmap(cmap, kspc, tx3912fb_getcolreg, info); 298 else if (fb_display[con].cmap.len) /* non default colormap? */ 299 fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2); 300 else 301 fb_copy_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel), cmap, kspc ? 0 : 2); 302 303 return 0; 304} 305 306/* 307 * Set the Colormap 308 */ 309static int tx3912fb_set_cmap(struct fb_cmap *cmap, int kspc, int con, 310 struct fb_info *info) 311{ 312 int err; 313 314 if (!fb_display[con].cmap.len) 315 if ((err = fb_alloc_cmap(&fb_display[con].cmap, 316 1<<fb_display[con].var.bits_per_pixel, 0))) 317 return err; 318 319 if (con == currcon) 320 return fb_set_cmap(cmap, kspc, tx3912fb_setcolreg, info); 321 else 322 fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1); 323 324 return 0; 325} 326 327/* 328 * Framebuffer ioctl 329 */ 330static int tx3912fb_ioctl(struct inode *inode, struct file *file, u_int cmd, 331 u_long arg, int con, struct fb_info *info) 332{ 333 return -EINVAL; 334} 335 336/* 337 * Initialization of the framebuffer 338 */ 339int __init tx3912fb_init(void) 340{ 341 /* Disable the video logic */ 342 outl(inl(TX3912_VIDEO_CTRL1) & 343 ~(TX3912_VIDEO_CTRL1_ENVID | TX3912_VIDEO_CTRL1_DISPON), 344 TX3912_VIDEO_CTRL1); 345 udelay(200); 346 347 /* Set start address for DMA transfer */ 348 outl(tx3912fb_paddr, TX3912_VIDEO_CTRL3); 349 350 /* Set end address for DMA transfer */ 351 outl((tx3912fb_paddr + tx3912fb_size + 1), TX3912_VIDEO_CTRL4); 352 353 /* Set the pixel depth */ 354 switch (tx3912fb_info.bits_per_pixel) { 355 case 1: 356 /* Monochrome */ 357 outl(inl(TX3912_VIDEO_CTRL1) & ~TX3912_VIDEO_CTRL1_BITSEL_MASK, 358 TX3912_VIDEO_CTRL1); 359 break; 360 case 4: 361 /* 4-bit gray */ 362 outl(inl(TX3912_VIDEO_CTRL1) & ~TX3912_VIDEO_CTRL1_BITSEL_MASK, 363 TX3912_VIDEO_CTRL1); 364 outl(inl(TX3912_VIDEO_CTRL1) | 365 TX3912_VIDEO_CTRL1_BITSEL_4BIT_GRAY, 366 TX3912_VIDEO_CTRL1); 367 break; 368 case 8: 369 /* 8-bit color */ 370 outl(inl(TX3912_VIDEO_CTRL1) & ~TX3912_VIDEO_CTRL1_BITSEL_MASK, 371 TX3912_VIDEO_CTRL1); 372 outl(inl(TX3912_VIDEO_CTRL1) | 373 TX3912_VIDEO_CTRL1_BITSEL_8BIT_COLOR, 374 TX3912_VIDEO_CTRL1); 375 break; 376 case 2: 377 default: 378 /* 2-bit gray */ 379 outl(inl(TX3912_VIDEO_CTRL1) & ~TX3912_VIDEO_CTRL1_BITSEL_MASK, 380 TX3912_VIDEO_CTRL1); 381 outl(inl(TX3912_VIDEO_CTRL1) | 382 TX3912_VIDEO_CTRL1_BITSEL_2BIT_GRAY, 383 TX3912_VIDEO_CTRL1); 384 break; 385 } 386 387 /* Enable the video clock */ 388 outl(inl(TX3912_CLK_CTRL) | TX3912_CLK_CTRL_ENVIDCLK, 389 TX3912_CLK_CTRL); 390 391 /* Unfreeze video logic and enable DF toggle */ 392 outl(inl(TX3912_VIDEO_CTRL1) & 393 ~(TX3912_VIDEO_CTRL1_ENFREEZEFRAME | TX3912_VIDEO_CTRL1_DFMODE), 394 TX3912_VIDEO_CTRL1); 395 udelay(200); 396 397 /* Clear the framebuffer */ 398 memset((void *) tx3912fb_vaddr, 0xff, tx3912fb_size); 399 udelay(200); 400 401 /* Enable the video logic */ 402 outl(inl(TX3912_VIDEO_CTRL1) | 403 (TX3912_VIDEO_CTRL1_ENVID | TX3912_VIDEO_CTRL1_DISPON), 404 TX3912_VIDEO_CTRL1); 405 406 strcpy(fb_info.modename, TX3912FB_NAME); 407 fb_info.changevar = NULL; 408 fb_info.node = -1; 409 fb_info.fbops = &tx3912fb_ops; 410 fb_info.disp = &global_disp; 411 fb_info.switch_con = &tx3912fbcon_switch; 412 fb_info.updatevar = &tx3912fbcon_updatevar; 413 fb_info.blank = &tx3912fbcon_blank; 414 fb_info.flags = FBINFO_FLAG_DEFAULT; 415 416 tx3912fb_set_var(&tx3912fb_info, -1, &fb_info); 417 418 if (register_framebuffer(&fb_info) < 0) 419 return -1; 420 421 printk (KERN_INFO "fb%d: TX3912 frame buffer using %uKB.\n", 422 GET_FB_IDX(fb_info.node), (u_int) (tx3912fb_size >> 10)); 423 424 return 0; 425} 426 427/* 428 * Switch the console to be the framebuffer 429 */ 430static int tx3912fbcon_switch(int con, struct fb_info *info) 431{ 432 /* Save off the color map if needed */ 433 if (fb_display[currcon].cmap.len) 434 fb_get_cmap(&fb_display[currcon].cmap, 1, 435 tx3912fb_getcolreg, info); 436 437 /* Make the switch */ 438 currcon = con; 439 440 /* Install new colormap */ 441 tx3912fb_install_cmap(con, info); 442 443 return 0; 444} 445 446/* 447 * Update variable structure 448 */ 449static int tx3912fbcon_updatevar(int con, struct fb_info *info) 450{ 451 /* Nothing */ 452 return 0; 453} 454 455/* 456 * Blank the display 457 */ 458static void tx3912fbcon_blank(int blank, struct fb_info *info) 459{ 460 printk("tx3912fbcon_blank\n"); 461} 462 463/* 464 * Read a single color register 465 */ 466static int tx3912fb_getcolreg(u_int regno, u_int *red, u_int *green, 467 u_int *blue, u_int *transp, struct fb_info *info) 468{ 469 if (regno > 255) 470 return 1; 471 472#if FB_IS_GREY 473 { 474 u_int grey; 475 476 grey = regno * 255 / 15; 477 478#if FB_IS_INVERSE 479 grey ^= 255; 480#endif 481 grey |= grey << 8; 482 *red = grey; 483 *green = grey; 484 *blue = grey; 485 } 486#else 487 *red = (palette[regno].red<<8) | palette[regno].red; 488 *green = (palette[regno].green<<8) | palette[regno].green; 489 *blue = (palette[regno].blue<<8) | palette[regno].blue; 490#endif 491 *transp = 0; 492 493 return 0; 494} 495 496/* 497 * Set a single color register 498 */ 499static int tx3912fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, 500 u_int transp, struct fb_info *info) 501{ 502 if (regno > 255) 503 return 1; 504 505#ifdef FBCON_HAS_CFB8 506 if( regno < 16 ) 507 fbcon_cmap.cfb8[regno] = ((red & 0xe000) >> 8) 508 | ((green & 0xe000) >> 11) 509 | ((blue & 0xc000) >> 14); 510#endif 511 512 red >>= 8; 513 green >>= 8; 514 blue >>= 8; 515 palette[regno].red = red; 516 palette[regno].green = green; 517 palette[regno].blue = blue; 518 519 return 0; 520} 521 522/* 523 * Install the color map 524 */ 525static void tx3912fb_install_cmap(int con, struct fb_info *info) 526{ 527 if (con != currcon) 528 return; 529 530 if (fb_display[con].cmap.len) 531 fb_set_cmap(&fb_display[con].cmap, 1, tx3912fb_setcolreg, info); 532 else 533 fb_set_cmap(fb_default_cmap(1 << fb_display[con].var.bits_per_pixel), 1, tx3912fb_setcolreg, info); 534} 535 536MODULE_LICENSE("GPL"); 537