Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v2.6.32-rc5 1064 lines 27 kB view raw
1/* linux/drivers/video/s3c-fb.c 2 * 3 * Copyright 2008 Openmoko Inc. 4 * Copyright 2008 Simtec Electronics 5 * Ben Dooks <ben@simtec.co.uk> 6 * http://armlinux.simtec.co.uk/ 7 * 8 * Samsung SoC Framebuffer driver 9 * 10 * This program is free software; you can redistribute it and/or modify 11 * it under the terms of the GNU General Public License version 2 as 12 * published by the Free Software Foundation. 13*/ 14 15#include <linux/kernel.h> 16#include <linux/module.h> 17#include <linux/platform_device.h> 18#include <linux/dma-mapping.h> 19#include <linux/init.h> 20#include <linux/gfp.h> 21#include <linux/clk.h> 22#include <linux/fb.h> 23#include <linux/io.h> 24 25#include <mach/map.h> 26#include <mach/regs-fb.h> 27#include <plat/fb.h> 28 29/* This driver will export a number of framebuffer interfaces depending 30 * on the configuration passed in via the platform data. Each fb instance 31 * maps to a hardware window. Currently there is no support for runtime 32 * setting of the alpha-blending functions that each window has, so only 33 * window 0 is actually useful. 34 * 35 * Window 0 is treated specially, it is used for the basis of the LCD 36 * output timings and as the control for the output power-down state. 37*/ 38 39/* note, some of the functions that get called are derived from including 40 * <mach/regs-fb.h> as they are specific to the architecture that the code 41 * is being built for. 42*/ 43 44#ifdef CONFIG_FB_S3C_DEBUG_REGWRITE 45#undef writel 46#define writel(v, r) do { \ 47 printk(KERN_DEBUG "%s: %08x => %p\n", __func__, (unsigned int)v, r); \ 48 __raw_writel(v, r); } while(0) 49#endif /* FB_S3C_DEBUG_REGWRITE */ 50 51struct s3c_fb; 52 53/** 54 * struct s3c_fb_win - per window private data for each framebuffer. 55 * @windata: The platform data supplied for the window configuration. 56 * @parent: The hardware that this window is part of. 57 * @fbinfo: Pointer pack to the framebuffer info for this window. 58 * @palette_buffer: Buffer/cache to hold palette entries. 59 * @pseudo_palette: For use in TRUECOLOUR modes for entries 0..15/ 60 * @index: The window number of this window. 61 * @palette: The bitfields for changing r/g/b into a hardware palette entry. 62 */ 63struct s3c_fb_win { 64 struct s3c_fb_pd_win *windata; 65 struct s3c_fb *parent; 66 struct fb_info *fbinfo; 67 struct s3c_fb_palette palette; 68 69 u32 *palette_buffer; 70 u32 pseudo_palette[16]; 71 unsigned int index; 72}; 73 74/** 75 * struct s3c_fb - overall hardware state of the hardware 76 * @dev: The device that we bound to, for printing, etc. 77 * @regs_res: The resource we claimed for the IO registers. 78 * @bus_clk: The clk (hclk) feeding our interface and possibly pixclk. 79 * @regs: The mapped hardware registers. 80 * @enabled: A bitmask of enabled hardware windows. 81 * @pdata: The platform configuration data passed with the device. 82 * @windows: The hardware windows that have been claimed. 83 */ 84struct s3c_fb { 85 struct device *dev; 86 struct resource *regs_res; 87 struct clk *bus_clk; 88 void __iomem *regs; 89 90 unsigned char enabled; 91 92 struct s3c_fb_platdata *pdata; 93 struct s3c_fb_win *windows[S3C_FB_MAX_WIN]; 94}; 95 96/** 97 * s3c_fb_win_has_palette() - determine if a mode has a palette 98 * @win: The window number being queried. 99 * @bpp: The number of bits per pixel to test. 100 * 101 * Work out if the given window supports palletised data at the specified bpp. 102 */ 103static int s3c_fb_win_has_palette(unsigned int win, unsigned int bpp) 104{ 105 return s3c_fb_win_pal_size(win) <= (1 << bpp); 106} 107 108/** 109 * s3c_fb_check_var() - framebuffer layer request to verify a given mode. 110 * @var: The screen information to verify. 111 * @info: The framebuffer device. 112 * 113 * Framebuffer layer call to verify the given information and allow us to 114 * update various information depending on the hardware capabilities. 115 */ 116static int s3c_fb_check_var(struct fb_var_screeninfo *var, 117 struct fb_info *info) 118{ 119 struct s3c_fb_win *win = info->par; 120 struct s3c_fb_pd_win *windata = win->windata; 121 struct s3c_fb *sfb = win->parent; 122 123 dev_dbg(sfb->dev, "checking parameters\n"); 124 125 var->xres_virtual = max((unsigned int)windata->virtual_x, var->xres); 126 var->yres_virtual = max((unsigned int)windata->virtual_y, var->yres); 127 128 if (!s3c_fb_validate_win_bpp(win->index, var->bits_per_pixel)) { 129 dev_dbg(sfb->dev, "win %d: unsupported bpp %d\n", 130 win->index, var->bits_per_pixel); 131 return -EINVAL; 132 } 133 134 /* always ensure these are zero, for drop through cases below */ 135 var->transp.offset = 0; 136 var->transp.length = 0; 137 138 switch (var->bits_per_pixel) { 139 case 1: 140 case 2: 141 case 4: 142 case 8: 143 if (!s3c_fb_win_has_palette(win->index, var->bits_per_pixel)) { 144 /* non palletised, A:1,R:2,G:3,B:2 mode */ 145 var->red.offset = 4; 146 var->green.offset = 2; 147 var->blue.offset = 0; 148 var->red.length = 5; 149 var->green.length = 3; 150 var->blue.length = 2; 151 var->transp.offset = 7; 152 var->transp.length = 1; 153 } else { 154 var->red.offset = 0; 155 var->red.length = var->bits_per_pixel; 156 var->green = var->red; 157 var->blue = var->red; 158 } 159 break; 160 161 case 19: 162 /* 666 with one bit alpha/transparency */ 163 var->transp.offset = 18; 164 var->transp.length = 1; 165 case 18: 166 var->bits_per_pixel = 32; 167 168 /* 666 format */ 169 var->red.offset = 12; 170 var->green.offset = 6; 171 var->blue.offset = 0; 172 var->red.length = 6; 173 var->green.length = 6; 174 var->blue.length = 6; 175 break; 176 177 case 16: 178 /* 16 bpp, 565 format */ 179 var->red.offset = 11; 180 var->green.offset = 5; 181 var->blue.offset = 0; 182 var->red.length = 5; 183 var->green.length = 6; 184 var->blue.length = 5; 185 break; 186 187 case 28: 188 case 25: 189 var->transp.length = var->bits_per_pixel - 24; 190 var->transp.offset = 24; 191 /* drop through */ 192 case 24: 193 /* our 24bpp is unpacked, so 32bpp */ 194 var->bits_per_pixel = 32; 195 case 32: 196 var->red.offset = 16; 197 var->red.length = 8; 198 var->green.offset = 8; 199 var->green.length = 8; 200 var->blue.offset = 0; 201 var->blue.length = 8; 202 break; 203 204 default: 205 dev_err(sfb->dev, "invalid bpp\n"); 206 } 207 208 dev_dbg(sfb->dev, "%s: verified parameters\n", __func__); 209 return 0; 210} 211 212/** 213 * s3c_fb_calc_pixclk() - calculate the divider to create the pixel clock. 214 * @id: window id. 215 * @sfb: The hardware state. 216 * @pixclock: The pixel clock wanted, in picoseconds. 217 * 218 * Given the specified pixel clock, work out the necessary divider to get 219 * close to the output frequency. 220 */ 221static int s3c_fb_calc_pixclk(unsigned char id, struct s3c_fb *sfb, unsigned int pixclk) 222{ 223 struct s3c_fb_pd_win *win = sfb->pdata->win[id]; 224 unsigned long clk = clk_get_rate(sfb->bus_clk); 225 unsigned int result; 226 227 pixclk *= win->win_mode.refresh; 228 result = clk / pixclk; 229 230 dev_dbg(sfb->dev, "pixclk=%u, clk=%lu, div=%d (%lu)\n", 231 pixclk, clk, result, clk / result); 232 233 return result; 234} 235 236/** 237 * s3c_fb_align_word() - align pixel count to word boundary 238 * @bpp: The number of bits per pixel 239 * @pix: The value to be aligned. 240 * 241 * Align the given pixel count so that it will start on an 32bit word 242 * boundary. 243 */ 244static int s3c_fb_align_word(unsigned int bpp, unsigned int pix) 245{ 246 int pix_per_word; 247 248 if (bpp > 16) 249 return pix; 250 251 pix_per_word = (8 * 32) / bpp; 252 return ALIGN(pix, pix_per_word); 253} 254 255/** 256 * s3c_fb_set_par() - framebuffer request to set new framebuffer state. 257 * @info: The framebuffer to change. 258 * 259 * Framebuffer layer request to set a new mode for the specified framebuffer 260 */ 261static int s3c_fb_set_par(struct fb_info *info) 262{ 263 struct fb_var_screeninfo *var = &info->var; 264 struct s3c_fb_win *win = info->par; 265 struct s3c_fb *sfb = win->parent; 266 void __iomem *regs = sfb->regs; 267 int win_no = win->index; 268 u32 osdc_data = 0; 269 u32 data; 270 u32 pagewidth; 271 int clkdiv; 272 273 dev_dbg(sfb->dev, "setting framebuffer parameters\n"); 274 275 switch (var->bits_per_pixel) { 276 case 32: 277 case 24: 278 case 16: 279 case 12: 280 info->fix.visual = FB_VISUAL_TRUECOLOR; 281 break; 282 case 8: 283 if (s3c_fb_win_has_palette(win_no, 8)) 284 info->fix.visual = FB_VISUAL_PSEUDOCOLOR; 285 else 286 info->fix.visual = FB_VISUAL_TRUECOLOR; 287 break; 288 case 1: 289 info->fix.visual = FB_VISUAL_MONO01; 290 break; 291 default: 292 info->fix.visual = FB_VISUAL_PSEUDOCOLOR; 293 break; 294 } 295 296 info->fix.line_length = (var->xres_virtual * var->bits_per_pixel) / 8; 297 298 /* disable the window whilst we update it */ 299 writel(0, regs + WINCON(win_no)); 300 301 /* use window 0 as the basis for the lcd output timings */ 302 303 if (win_no == 0) { 304 clkdiv = s3c_fb_calc_pixclk(win_no, sfb, var->pixclock); 305 306 data = sfb->pdata->vidcon0; 307 data &= ~(VIDCON0_CLKVAL_F_MASK | VIDCON0_CLKDIR); 308 309 if (clkdiv > 1) 310 data |= VIDCON0_CLKVAL_F(clkdiv-1) | VIDCON0_CLKDIR; 311 else 312 data &= ~VIDCON0_CLKDIR; /* 1:1 clock */ 313 314 /* write the timing data to the panel */ 315 316 data |= VIDCON0_ENVID | VIDCON0_ENVID_F; 317 writel(data, regs + VIDCON0); 318 319 data = VIDTCON0_VBPD(var->upper_margin - 1) | 320 VIDTCON0_VFPD(var->lower_margin - 1) | 321 VIDTCON0_VSPW(var->vsync_len - 1); 322 323 writel(data, regs + VIDTCON0); 324 325 data = VIDTCON1_HBPD(var->left_margin - 1) | 326 VIDTCON1_HFPD(var->right_margin - 1) | 327 VIDTCON1_HSPW(var->hsync_len - 1); 328 329 writel(data, regs + VIDTCON1); 330 331 data = VIDTCON2_LINEVAL(var->yres - 1) | 332 VIDTCON2_HOZVAL(var->xres - 1); 333 writel(data, regs + VIDTCON2); 334 } 335 336 /* write the buffer address */ 337 338 writel(info->fix.smem_start, regs + VIDW_BUF_START(win_no)); 339 340 data = info->fix.smem_start + info->fix.line_length * var->yres; 341 writel(data, regs + VIDW_BUF_END(win_no)); 342 343 pagewidth = (var->xres * var->bits_per_pixel) >> 3; 344 data = VIDW_BUF_SIZE_OFFSET(info->fix.line_length - pagewidth) | 345 VIDW_BUF_SIZE_PAGEWIDTH(pagewidth); 346 writel(data, regs + VIDW_BUF_SIZE(win_no)); 347 348 /* write 'OSD' registers to control position of framebuffer */ 349 350 data = VIDOSDxA_TOPLEFT_X(0) | VIDOSDxA_TOPLEFT_Y(0); 351 writel(data, regs + VIDOSD_A(win_no)); 352 353 data = VIDOSDxB_BOTRIGHT_X(s3c_fb_align_word(var->bits_per_pixel, 354 var->xres - 1)) | 355 VIDOSDxB_BOTRIGHT_Y(var->yres - 1); 356 357 writel(data, regs + VIDOSD_B(win_no)); 358 359 data = var->xres * var->yres; 360 361 osdc_data = VIDISD14C_ALPHA1_R(0xf) | 362 VIDISD14C_ALPHA1_G(0xf) | 363 VIDISD14C_ALPHA1_B(0xf); 364 365 if (s3c_fb_has_osd_d(win_no)) { 366 writel(data, regs + VIDOSD_D(win_no)); 367 writel(osdc_data, regs + VIDOSD_C(win_no)); 368 } else 369 writel(data, regs + VIDOSD_C(win_no)); 370 371 data = WINCONx_ENWIN; 372 373 /* note, since we have to round up the bits-per-pixel, we end up 374 * relying on the bitfield information for r/g/b/a to work out 375 * exactly which mode of operation is intended. */ 376 377 switch (var->bits_per_pixel) { 378 case 1: 379 data |= WINCON0_BPPMODE_1BPP; 380 data |= WINCONx_BITSWP; 381 data |= WINCONx_BURSTLEN_4WORD; 382 break; 383 case 2: 384 data |= WINCON0_BPPMODE_2BPP; 385 data |= WINCONx_BITSWP; 386 data |= WINCONx_BURSTLEN_8WORD; 387 break; 388 case 4: 389 data |= WINCON0_BPPMODE_4BPP; 390 data |= WINCONx_BITSWP; 391 data |= WINCONx_BURSTLEN_8WORD; 392 break; 393 case 8: 394 if (var->transp.length != 0) 395 data |= WINCON1_BPPMODE_8BPP_1232; 396 else 397 data |= WINCON0_BPPMODE_8BPP_PALETTE; 398 data |= WINCONx_BURSTLEN_8WORD; 399 data |= WINCONx_BYTSWP; 400 break; 401 case 16: 402 if (var->transp.length != 0) 403 data |= WINCON1_BPPMODE_16BPP_A1555; 404 else 405 data |= WINCON0_BPPMODE_16BPP_565; 406 data |= WINCONx_HAWSWP; 407 data |= WINCONx_BURSTLEN_16WORD; 408 break; 409 case 24: 410 case 32: 411 if (var->red.length == 6) { 412 if (var->transp.length != 0) 413 data |= WINCON1_BPPMODE_19BPP_A1666; 414 else 415 data |= WINCON1_BPPMODE_18BPP_666; 416 } else if (var->transp.length == 1) 417 data |= WINCON1_BPPMODE_25BPP_A1888 418 | WINCON1_BLD_PIX; 419 else if (var->transp.length == 4) 420 data |= WINCON1_BPPMODE_28BPP_A4888 421 | WINCON1_BLD_PIX | WINCON1_ALPHA_SEL; 422 else 423 data |= WINCON0_BPPMODE_24BPP_888; 424 425 data |= WINCONx_BURSTLEN_16WORD; 426 break; 427 } 428 429 /* It has no color key control register for window0 */ 430 if (win_no > 0) { 431 u32 keycon0_data = 0, keycon1_data = 0; 432 433 keycon0_data = ~(WxKEYCON0_KEYBL_EN | 434 WxKEYCON0_KEYEN_F | 435 WxKEYCON0_DIRCON) | WxKEYCON0_COMPKEY(0); 436 437 keycon1_data = WxKEYCON1_COLVAL(0xffffff); 438 439 writel(keycon0_data, regs + WxKEYCONy(win_no-1, 0)); 440 writel(keycon1_data, regs + WxKEYCONy(win_no-1, 1)); 441 } 442 443 writel(data, regs + WINCON(win_no)); 444 writel(0x0, regs + WINxMAP(win_no)); 445 446 return 0; 447} 448 449/** 450 * s3c_fb_update_palette() - set or schedule a palette update. 451 * @sfb: The hardware information. 452 * @win: The window being updated. 453 * @reg: The palette index being changed. 454 * @value: The computed palette value. 455 * 456 * Change the value of a palette register, either by directly writing to 457 * the palette (this requires the palette RAM to be disconnected from the 458 * hardware whilst this is in progress) or schedule the update for later. 459 * 460 * At the moment, since we have no VSYNC interrupt support, we simply set 461 * the palette entry directly. 462 */ 463static void s3c_fb_update_palette(struct s3c_fb *sfb, 464 struct s3c_fb_win *win, 465 unsigned int reg, 466 u32 value) 467{ 468 void __iomem *palreg; 469 u32 palcon; 470 471 palreg = sfb->regs + s3c_fb_pal_reg(win->index, reg); 472 473 dev_dbg(sfb->dev, "%s: win %d, reg %d (%p): %08x\n", 474 __func__, win->index, reg, palreg, value); 475 476 win->palette_buffer[reg] = value; 477 478 palcon = readl(sfb->regs + WPALCON); 479 writel(palcon | WPALCON_PAL_UPDATE, sfb->regs + WPALCON); 480 481 if (s3c_fb_pal_is16(win->index)) 482 writew(value, palreg); 483 else 484 writel(value, palreg); 485 486 writel(palcon, sfb->regs + WPALCON); 487} 488 489static inline unsigned int chan_to_field(unsigned int chan, 490 struct fb_bitfield *bf) 491{ 492 chan &= 0xffff; 493 chan >>= 16 - bf->length; 494 return chan << bf->offset; 495} 496 497/** 498 * s3c_fb_setcolreg() - framebuffer layer request to change palette. 499 * @regno: The palette index to change. 500 * @red: The red field for the palette data. 501 * @green: The green field for the palette data. 502 * @blue: The blue field for the palette data. 503 * @trans: The transparency (alpha) field for the palette data. 504 * @info: The framebuffer being changed. 505 */ 506static int s3c_fb_setcolreg(unsigned regno, 507 unsigned red, unsigned green, unsigned blue, 508 unsigned transp, struct fb_info *info) 509{ 510 struct s3c_fb_win *win = info->par; 511 struct s3c_fb *sfb = win->parent; 512 unsigned int val; 513 514 dev_dbg(sfb->dev, "%s: win %d: %d => rgb=%d/%d/%d\n", 515 __func__, win->index, regno, red, green, blue); 516 517 switch (info->fix.visual) { 518 case FB_VISUAL_TRUECOLOR: 519 /* true-colour, use pseudo-palette */ 520 521 if (regno < 16) { 522 u32 *pal = info->pseudo_palette; 523 524 val = chan_to_field(red, &info->var.red); 525 val |= chan_to_field(green, &info->var.green); 526 val |= chan_to_field(blue, &info->var.blue); 527 528 pal[regno] = val; 529 } 530 break; 531 532 case FB_VISUAL_PSEUDOCOLOR: 533 if (regno < s3c_fb_win_pal_size(win->index)) { 534 val = chan_to_field(red, &win->palette.r); 535 val |= chan_to_field(green, &win->palette.g); 536 val |= chan_to_field(blue, &win->palette.b); 537 538 s3c_fb_update_palette(sfb, win, regno, val); 539 } 540 541 break; 542 543 default: 544 return 1; /* unknown type */ 545 } 546 547 return 0; 548} 549 550/** 551 * s3c_fb_enable() - Set the state of the main LCD output 552 * @sfb: The main framebuffer state. 553 * @enable: The state to set. 554 */ 555static void s3c_fb_enable(struct s3c_fb *sfb, int enable) 556{ 557 u32 vidcon0 = readl(sfb->regs + VIDCON0); 558 559 if (enable) 560 vidcon0 |= VIDCON0_ENVID | VIDCON0_ENVID_F; 561 else { 562 /* see the note in the framebuffer datasheet about 563 * why you cannot take both of these bits down at the 564 * same time. */ 565 566 if (!(vidcon0 & VIDCON0_ENVID)) 567 return; 568 569 vidcon0 |= VIDCON0_ENVID; 570 vidcon0 &= ~VIDCON0_ENVID_F; 571 } 572 573 writel(vidcon0, sfb->regs + VIDCON0); 574} 575 576/** 577 * s3c_fb_blank() - blank or unblank the given window 578 * @blank_mode: The blank state from FB_BLANK_* 579 * @info: The framebuffer to blank. 580 * 581 * Framebuffer layer request to change the power state. 582 */ 583static int s3c_fb_blank(int blank_mode, struct fb_info *info) 584{ 585 struct s3c_fb_win *win = info->par; 586 struct s3c_fb *sfb = win->parent; 587 unsigned int index = win->index; 588 u32 wincon; 589 590 dev_dbg(sfb->dev, "blank mode %d\n", blank_mode); 591 592 wincon = readl(sfb->regs + WINCON(index)); 593 594 switch (blank_mode) { 595 case FB_BLANK_POWERDOWN: 596 wincon &= ~WINCONx_ENWIN; 597 sfb->enabled &= ~(1 << index); 598 /* fall through to FB_BLANK_NORMAL */ 599 600 case FB_BLANK_NORMAL: 601 /* disable the DMA and display 0x0 (black) */ 602 writel(WINxMAP_MAP | WINxMAP_MAP_COLOUR(0x0), 603 sfb->regs + WINxMAP(index)); 604 break; 605 606 case FB_BLANK_UNBLANK: 607 writel(0x0, sfb->regs + WINxMAP(index)); 608 wincon |= WINCONx_ENWIN; 609 sfb->enabled |= (1 << index); 610 break; 611 612 case FB_BLANK_VSYNC_SUSPEND: 613 case FB_BLANK_HSYNC_SUSPEND: 614 default: 615 return 1; 616 } 617 618 writel(wincon, sfb->regs + WINCON(index)); 619 620 /* Check the enabled state to see if we need to be running the 621 * main LCD interface, as if there are no active windows then 622 * it is highly likely that we also do not need to output 623 * anything. 624 */ 625 626 /* We could do something like the following code, but the current 627 * system of using framebuffer events means that we cannot make 628 * the distinction between just window 0 being inactive and all 629 * the windows being down. 630 * 631 * s3c_fb_enable(sfb, sfb->enabled ? 1 : 0); 632 */ 633 634 /* we're stuck with this until we can do something about overriding 635 * the power control using the blanking event for a single fb. 636 */ 637 if (index == 0) 638 s3c_fb_enable(sfb, blank_mode != FB_BLANK_POWERDOWN ? 1 : 0); 639 640 return 0; 641} 642 643static struct fb_ops s3c_fb_ops = { 644 .owner = THIS_MODULE, 645 .fb_check_var = s3c_fb_check_var, 646 .fb_set_par = s3c_fb_set_par, 647 .fb_blank = s3c_fb_blank, 648 .fb_setcolreg = s3c_fb_setcolreg, 649 .fb_fillrect = cfb_fillrect, 650 .fb_copyarea = cfb_copyarea, 651 .fb_imageblit = cfb_imageblit, 652}; 653 654/** 655 * s3c_fb_alloc_memory() - allocate display memory for framebuffer window 656 * @sfb: The base resources for the hardware. 657 * @win: The window to initialise memory for. 658 * 659 * Allocate memory for the given framebuffer. 660 */ 661static int __devinit s3c_fb_alloc_memory(struct s3c_fb *sfb, 662 struct s3c_fb_win *win) 663{ 664 struct s3c_fb_pd_win *windata = win->windata; 665 unsigned int real_size, virt_size, size; 666 struct fb_info *fbi = win->fbinfo; 667 dma_addr_t map_dma; 668 669 dev_dbg(sfb->dev, "allocating memory for display\n"); 670 671 real_size = windata->win_mode.xres * windata->win_mode.yres; 672 virt_size = windata->virtual_x * windata->virtual_y; 673 674 dev_dbg(sfb->dev, "real_size=%u (%u.%u), virt_size=%u (%u.%u)\n", 675 real_size, windata->win_mode.xres, windata->win_mode.yres, 676 virt_size, windata->virtual_x, windata->virtual_y); 677 678 size = (real_size > virt_size) ? real_size : virt_size; 679 size *= (windata->max_bpp > 16) ? 32 : windata->max_bpp; 680 size /= 8; 681 682 fbi->fix.smem_len = size; 683 size = PAGE_ALIGN(size); 684 685 dev_dbg(sfb->dev, "want %u bytes for window\n", size); 686 687 fbi->screen_base = dma_alloc_writecombine(sfb->dev, size, 688 &map_dma, GFP_KERNEL); 689 if (!fbi->screen_base) 690 return -ENOMEM; 691 692 dev_dbg(sfb->dev, "mapped %x to %p\n", 693 (unsigned int)map_dma, fbi->screen_base); 694 695 memset(fbi->screen_base, 0x0, size); 696 fbi->fix.smem_start = map_dma; 697 698 return 0; 699} 700 701/** 702 * s3c_fb_free_memory() - free the display memory for the given window 703 * @sfb: The base resources for the hardware. 704 * @win: The window to free the display memory for. 705 * 706 * Free the display memory allocated by s3c_fb_alloc_memory(). 707 */ 708static void s3c_fb_free_memory(struct s3c_fb *sfb, struct s3c_fb_win *win) 709{ 710 struct fb_info *fbi = win->fbinfo; 711 712 dma_free_writecombine(sfb->dev, PAGE_ALIGN(fbi->fix.smem_len), 713 fbi->screen_base, fbi->fix.smem_start); 714} 715 716/** 717 * s3c_fb_release_win() - release resources for a framebuffer window. 718 * @win: The window to cleanup the resources for. 719 * 720 * Release the resources that where claimed for the hardware window, 721 * such as the framebuffer instance and any memory claimed for it. 722 */ 723static void s3c_fb_release_win(struct s3c_fb *sfb, struct s3c_fb_win *win) 724{ 725 if (win->fbinfo) { 726 unregister_framebuffer(win->fbinfo); 727 fb_dealloc_cmap(&win->fbinfo->cmap); 728 s3c_fb_free_memory(sfb, win); 729 framebuffer_release(win->fbinfo); 730 } 731} 732 733/** 734 * s3c_fb_probe_win() - register an hardware window 735 * @sfb: The base resources for the hardware 736 * @res: Pointer to where to place the resultant window. 737 * 738 * Allocate and do the basic initialisation for one of the hardware's graphics 739 * windows. 740 */ 741static int __devinit s3c_fb_probe_win(struct s3c_fb *sfb, unsigned int win_no, 742 struct s3c_fb_win **res) 743{ 744 struct fb_var_screeninfo *var; 745 struct fb_videomode *initmode; 746 struct s3c_fb_pd_win *windata; 747 struct s3c_fb_win *win; 748 struct fb_info *fbinfo; 749 int palette_size; 750 int ret; 751 752 dev_dbg(sfb->dev, "probing window %d\n", win_no); 753 754 palette_size = s3c_fb_win_pal_size(win_no); 755 756 fbinfo = framebuffer_alloc(sizeof(struct s3c_fb_win) + 757 palette_size * sizeof(u32), sfb->dev); 758 if (!fbinfo) { 759 dev_err(sfb->dev, "failed to allocate framebuffer\n"); 760 return -ENOENT; 761 } 762 763 windata = sfb->pdata->win[win_no]; 764 initmode = &windata->win_mode; 765 766 WARN_ON(windata->max_bpp == 0); 767 WARN_ON(windata->win_mode.xres == 0); 768 WARN_ON(windata->win_mode.yres == 0); 769 770 win = fbinfo->par; 771 var = &fbinfo->var; 772 win->fbinfo = fbinfo; 773 win->parent = sfb; 774 win->windata = windata; 775 win->index = win_no; 776 win->palette_buffer = (u32 *)(win + 1); 777 778 ret = s3c_fb_alloc_memory(sfb, win); 779 if (ret) { 780 dev_err(sfb->dev, "failed to allocate display memory\n"); 781 return ret; 782 } 783 784 /* setup the r/b/g positions for the window's palette */ 785 s3c_fb_init_palette(win_no, &win->palette); 786 787 /* setup the initial video mode from the window */ 788 fb_videomode_to_var(&fbinfo->var, initmode); 789 790 fbinfo->fix.type = FB_TYPE_PACKED_PIXELS; 791 fbinfo->fix.accel = FB_ACCEL_NONE; 792 fbinfo->var.activate = FB_ACTIVATE_NOW; 793 fbinfo->var.vmode = FB_VMODE_NONINTERLACED; 794 fbinfo->var.bits_per_pixel = windata->default_bpp; 795 fbinfo->fbops = &s3c_fb_ops; 796 fbinfo->flags = FBINFO_FLAG_DEFAULT; 797 fbinfo->pseudo_palette = &win->pseudo_palette; 798 799 /* prepare to actually start the framebuffer */ 800 801 ret = s3c_fb_check_var(&fbinfo->var, fbinfo); 802 if (ret < 0) { 803 dev_err(sfb->dev, "check_var failed on initial video params\n"); 804 return ret; 805 } 806 807 /* create initial colour map */ 808 809 ret = fb_alloc_cmap(&fbinfo->cmap, s3c_fb_win_pal_size(win_no), 1); 810 if (ret == 0) 811 fb_set_cmap(&fbinfo->cmap, fbinfo); 812 else 813 dev_err(sfb->dev, "failed to allocate fb cmap\n"); 814 815 s3c_fb_set_par(fbinfo); 816 817 dev_dbg(sfb->dev, "about to register framebuffer\n"); 818 819 /* run the check_var and set_par on our configuration. */ 820 821 ret = register_framebuffer(fbinfo); 822 if (ret < 0) { 823 dev_err(sfb->dev, "failed to register framebuffer\n"); 824 return ret; 825 } 826 827 *res = win; 828 dev_info(sfb->dev, "window %d: fb %s\n", win_no, fbinfo->fix.id); 829 830 return 0; 831} 832 833/** 834 * s3c_fb_clear_win() - clear hardware window registers. 835 * @sfb: The base resources for the hardware. 836 * @win: The window to process. 837 * 838 * Reset the specific window registers to a known state. 839 */ 840static void s3c_fb_clear_win(struct s3c_fb *sfb, int win) 841{ 842 void __iomem *regs = sfb->regs; 843 844 writel(0, regs + WINCON(win)); 845 writel(0xffffff, regs + WxKEYCONy(win, 0)); 846 writel(0xffffff, regs + WxKEYCONy(win, 1)); 847 848 writel(0, regs + VIDOSD_A(win)); 849 writel(0, regs + VIDOSD_B(win)); 850 writel(0, regs + VIDOSD_C(win)); 851} 852 853static int __devinit s3c_fb_probe(struct platform_device *pdev) 854{ 855 struct device *dev = &pdev->dev; 856 struct s3c_fb_platdata *pd; 857 struct s3c_fb *sfb; 858 struct resource *res; 859 int win; 860 int ret = 0; 861 862 pd = pdev->dev.platform_data; 863 if (!pd) { 864 dev_err(dev, "no platform data specified\n"); 865 return -EINVAL; 866 } 867 868 sfb = kzalloc(sizeof(struct s3c_fb), GFP_KERNEL); 869 if (!sfb) { 870 dev_err(dev, "no memory for framebuffers\n"); 871 return -ENOMEM; 872 } 873 874 sfb->dev = dev; 875 sfb->pdata = pd; 876 877 sfb->bus_clk = clk_get(dev, "lcd"); 878 if (IS_ERR(sfb->bus_clk)) { 879 dev_err(dev, "failed to get bus clock\n"); 880 goto err_sfb; 881 } 882 883 clk_enable(sfb->bus_clk); 884 885 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 886 if (!res) { 887 dev_err(dev, "failed to find registers\n"); 888 ret = -ENOENT; 889 goto err_clk; 890 } 891 892 sfb->regs_res = request_mem_region(res->start, resource_size(res), 893 dev_name(dev)); 894 if (!sfb->regs_res) { 895 dev_err(dev, "failed to claim register region\n"); 896 ret = -ENOENT; 897 goto err_clk; 898 } 899 900 sfb->regs = ioremap(res->start, resource_size(res)); 901 if (!sfb->regs) { 902 dev_err(dev, "failed to map registers\n"); 903 ret = -ENXIO; 904 goto err_req_region; 905 } 906 907 dev_dbg(dev, "got resources (regs %p), probing windows\n", sfb->regs); 908 909 /* setup gpio and output polarity controls */ 910 911 pd->setup_gpio(); 912 913 writel(pd->vidcon1, sfb->regs + VIDCON1); 914 915 /* zero all windows before we do anything */ 916 917 for (win = 0; win < S3C_FB_MAX_WIN; win++) 918 s3c_fb_clear_win(sfb, win); 919 920 /* we have the register setup, start allocating framebuffers */ 921 922 for (win = 0; win < S3C_FB_MAX_WIN; win++) { 923 if (!pd->win[win]) 924 continue; 925 926 ret = s3c_fb_probe_win(sfb, win, &sfb->windows[win]); 927 if (ret < 0) { 928 dev_err(dev, "failed to create window %d\n", win); 929 for (; win >= 0; win--) 930 s3c_fb_release_win(sfb, sfb->windows[win]); 931 goto err_ioremap; 932 } 933 } 934 935 platform_set_drvdata(pdev, sfb); 936 937 return 0; 938 939err_ioremap: 940 iounmap(sfb->regs); 941 942err_req_region: 943 release_resource(sfb->regs_res); 944 kfree(sfb->regs_res); 945 946err_clk: 947 clk_disable(sfb->bus_clk); 948 clk_put(sfb->bus_clk); 949 950err_sfb: 951 kfree(sfb); 952 return ret; 953} 954 955/** 956 * s3c_fb_remove() - Cleanup on module finalisation 957 * @pdev: The platform device we are bound to. 958 * 959 * Shutdown and then release all the resources that the driver allocated 960 * on initialisation. 961 */ 962static int __devexit s3c_fb_remove(struct platform_device *pdev) 963{ 964 struct s3c_fb *sfb = platform_get_drvdata(pdev); 965 int win; 966 967 for (win = 0; win < S3C_FB_MAX_WIN; win++) 968 if (sfb->windows[win]) 969 s3c_fb_release_win(sfb, sfb->windows[win]); 970 971 iounmap(sfb->regs); 972 973 clk_disable(sfb->bus_clk); 974 clk_put(sfb->bus_clk); 975 976 release_resource(sfb->regs_res); 977 kfree(sfb->regs_res); 978 979 kfree(sfb); 980 981 return 0; 982} 983 984#ifdef CONFIG_PM 985static int s3c_fb_suspend(struct platform_device *pdev, pm_message_t state) 986{ 987 struct s3c_fb *sfb = platform_get_drvdata(pdev); 988 struct s3c_fb_win *win; 989 int win_no; 990 991 for (win_no = S3C_FB_MAX_WIN - 1; win_no >= 0; win_no--) { 992 win = sfb->windows[win_no]; 993 if (!win) 994 continue; 995 996 /* use the blank function to push into power-down */ 997 s3c_fb_blank(FB_BLANK_POWERDOWN, win->fbinfo); 998 } 999 1000 clk_disable(sfb->bus_clk); 1001 return 0; 1002} 1003 1004static int s3c_fb_resume(struct platform_device *pdev) 1005{ 1006 struct s3c_fb *sfb = platform_get_drvdata(pdev); 1007 struct s3c_fb_platdata *pd = sfb->pdata; 1008 struct s3c_fb_win *win; 1009 int win_no; 1010 1011 clk_enable(sfb->bus_clk); 1012 1013 /* setup registers */ 1014 writel(pd->vidcon1, sfb->regs + VIDCON1); 1015 1016 /* zero all windows before we do anything */ 1017 for (win_no = 0; win_no < S3C_FB_MAX_WIN; win_no++) 1018 s3c_fb_clear_win(sfb, win_no); 1019 1020 /* restore framebuffers */ 1021 for (win_no = 0; win_no < S3C_FB_MAX_WIN; win_no++) { 1022 win = sfb->windows[win_no]; 1023 if (!win) 1024 continue; 1025 1026 dev_dbg(&pdev->dev, "resuming window %d\n", win_no); 1027 s3c_fb_set_par(win->fbinfo); 1028 } 1029 1030 return 0; 1031} 1032#else 1033#define s3c_fb_suspend NULL 1034#define s3c_fb_resume NULL 1035#endif 1036 1037static struct platform_driver s3c_fb_driver = { 1038 .probe = s3c_fb_probe, 1039 .remove = __devexit_p(s3c_fb_remove), 1040 .suspend = s3c_fb_suspend, 1041 .resume = s3c_fb_resume, 1042 .driver = { 1043 .name = "s3c-fb", 1044 .owner = THIS_MODULE, 1045 }, 1046}; 1047 1048static int __init s3c_fb_init(void) 1049{ 1050 return platform_driver_register(&s3c_fb_driver); 1051} 1052 1053static void __exit s3c_fb_cleanup(void) 1054{ 1055 platform_driver_unregister(&s3c_fb_driver); 1056} 1057 1058module_init(s3c_fb_init); 1059module_exit(s3c_fb_cleanup); 1060 1061MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>"); 1062MODULE_DESCRIPTION("Samsung S3C SoC Framebuffer driver"); 1063MODULE_LICENSE("GPL"); 1064MODULE_ALIAS("platform:s3c-fb");