Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
at v2.6.29-rc4 747 lines 18 kB view raw
1/* 2 * Freescale i.MX Frame Buffer device driver 3 * 4 * Copyright (C) 2004 Sascha Hauer, Pengutronix 5 * Based on acornfb.c Copyright (C) Russell King. 6 * 7 * This file is subject to the terms and conditions of the GNU General Public 8 * License. See the file COPYING in the main directory of this archive for 9 * more details. 10 * 11 * Please direct your questions and comments on this driver to the following 12 * email address: 13 * 14 * linux-arm-kernel@lists.arm.linux.org.uk 15 */ 16 17 18#include <linux/module.h> 19#include <linux/kernel.h> 20#include <linux/errno.h> 21#include <linux/string.h> 22#include <linux/interrupt.h> 23#include <linux/slab.h> 24#include <linux/mm.h> 25#include <linux/fb.h> 26#include <linux/delay.h> 27#include <linux/init.h> 28#include <linux/ioport.h> 29#include <linux/cpufreq.h> 30#include <linux/platform_device.h> 31#include <linux/dma-mapping.h> 32#include <linux/io.h> 33 34#include <mach/imxfb.h> 35 36/* 37 * Complain if VAR is out of range. 38 */ 39#define DEBUG_VAR 1 40 41#define DRIVER_NAME "imx-fb" 42 43#define LCDC_SSA 0x00 44 45#define LCDC_SIZE 0x04 46#define SIZE_XMAX(x) ((((x) >> 4) & 0x3f) << 20) 47#define SIZE_YMAX(y) ((y) & 0x1ff) 48 49#define LCDC_VPW 0x08 50#define VPW_VPW(x) ((x) & 0x3ff) 51 52#define LCDC_CPOS 0x0C 53#define CPOS_CC1 (1<<31) 54#define CPOS_CC0 (1<<30) 55#define CPOS_OP (1<<28) 56#define CPOS_CXP(x) (((x) & 3ff) << 16) 57#define CPOS_CYP(y) ((y) & 0x1ff) 58 59#define LCDC_LCWHB 0x10 60#define LCWHB_BK_EN (1<<31) 61#define LCWHB_CW(w) (((w) & 0x1f) << 24) 62#define LCWHB_CH(h) (((h) & 0x1f) << 16) 63#define LCWHB_BD(x) ((x) & 0xff) 64 65#define LCDC_LCHCC 0x14 66#define LCHCC_CUR_COL_R(r) (((r) & 0x1f) << 11) 67#define LCHCC_CUR_COL_G(g) (((g) & 0x3f) << 5) 68#define LCHCC_CUR_COL_B(b) ((b) & 0x1f) 69 70#define LCDC_PCR 0x18 71 72#define LCDC_HCR 0x1C 73#define HCR_H_WIDTH(x) (((x) & 0x3f) << 26) 74#define HCR_H_WAIT_1(x) (((x) & 0xff) << 8) 75#define HCR_H_WAIT_2(x) ((x) & 0xff) 76 77#define LCDC_VCR 0x20 78#define VCR_V_WIDTH(x) (((x) & 0x3f) << 26) 79#define VCR_V_WAIT_1(x) (((x) & 0xff) << 8) 80#define VCR_V_WAIT_2(x) ((x) & 0xff) 81 82#define LCDC_POS 0x24 83#define POS_POS(x) ((x) & 1f) 84 85#define LCDC_LSCR1 0x28 86/* bit fields in imxfb.h */ 87 88#define LCDC_PWMR 0x2C 89/* bit fields in imxfb.h */ 90 91#define LCDC_DMACR 0x30 92/* bit fields in imxfb.h */ 93 94#define LCDC_RMCR 0x34 95#define RMCR_LCDC_EN (1<<1) 96#define RMCR_SELF_REF (1<<0) 97 98#define LCDC_LCDICR 0x38 99#define LCDICR_INT_SYN (1<<2) 100#define LCDICR_INT_CON (1) 101 102#define LCDC_LCDISR 0x40 103#define LCDISR_UDR_ERR (1<<3) 104#define LCDISR_ERR_RES (1<<2) 105#define LCDISR_EOF (1<<1) 106#define LCDISR_BOF (1<<0) 107 108/* 109 * These are the bitfields for each 110 * display depth that we support. 111 */ 112struct imxfb_rgb { 113 struct fb_bitfield red; 114 struct fb_bitfield green; 115 struct fb_bitfield blue; 116 struct fb_bitfield transp; 117}; 118 119struct imxfb_info { 120 struct platform_device *pdev; 121 void __iomem *regs; 122 123 u_int max_bpp; 124 u_int max_xres; 125 u_int max_yres; 126 127 /* 128 * These are the addresses we mapped 129 * the framebuffer memory region to. 130 */ 131 dma_addr_t map_dma; 132 u_char *map_cpu; 133 u_int map_size; 134 135 u_char *screen_cpu; 136 dma_addr_t screen_dma; 137 u_int palette_size; 138 139 dma_addr_t dbar1; 140 dma_addr_t dbar2; 141 142 u_int pcr; 143 u_int pwmr; 144 u_int lscr1; 145 u_int dmacr; 146 u_int cmap_inverse:1, 147 cmap_static:1, 148 unused:30; 149 150 void (*lcd_power)(int); 151 void (*backlight_power)(int); 152}; 153 154#define IMX_NAME "IMX" 155 156/* 157 * Minimum X and Y resolutions 158 */ 159#define MIN_XRES 64 160#define MIN_YRES 64 161 162static struct imxfb_rgb def_rgb_16_tft = { 163 .red = {.offset = 11, .length = 5,}, 164 .green = {.offset = 5, .length = 6,}, 165 .blue = {.offset = 0, .length = 5,}, 166 .transp = {.offset = 0, .length = 0,}, 167}; 168 169static struct imxfb_rgb def_rgb_16_stn = { 170 .red = {.offset = 8, .length = 4,}, 171 .green = {.offset = 4, .length = 4,}, 172 .blue = {.offset = 0, .length = 4,}, 173 .transp = {.offset = 0, .length = 0,}, 174}; 175 176static struct imxfb_rgb def_rgb_8 = { 177 .red = {.offset = 0, .length = 8,}, 178 .green = {.offset = 0, .length = 8,}, 179 .blue = {.offset = 0, .length = 8,}, 180 .transp = {.offset = 0, .length = 0,}, 181}; 182 183static int imxfb_activate_var(struct fb_var_screeninfo *var, 184 struct fb_info *info); 185 186static inline u_int chan_to_field(u_int chan, struct fb_bitfield *bf) 187{ 188 chan &= 0xffff; 189 chan >>= 16 - bf->length; 190 return chan << bf->offset; 191} 192 193static int imxfb_setpalettereg(u_int regno, u_int red, u_int green, u_int blue, 194 u_int trans, struct fb_info *info) 195{ 196 struct imxfb_info *fbi = info->par; 197 u_int val, ret = 1; 198 199#define CNVT_TOHW(val,width) ((((val)<<(width))+0x7FFF-(val))>>16) 200 if (regno < fbi->palette_size) { 201 val = (CNVT_TOHW(red, 4) << 8) | 202 (CNVT_TOHW(green,4) << 4) | 203 CNVT_TOHW(blue, 4); 204 205 writel(val, fbi->regs + 0x800 + (regno << 2)); 206 ret = 0; 207 } 208 return ret; 209} 210 211static int imxfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, 212 u_int trans, struct fb_info *info) 213{ 214 struct imxfb_info *fbi = info->par; 215 unsigned int val; 216 int ret = 1; 217 218 /* 219 * If inverse mode was selected, invert all the colours 220 * rather than the register number. The register number 221 * is what you poke into the framebuffer to produce the 222 * colour you requested. 223 */ 224 if (fbi->cmap_inverse) { 225 red = 0xffff - red; 226 green = 0xffff - green; 227 blue = 0xffff - blue; 228 } 229 230 /* 231 * If greyscale is true, then we convert the RGB value 232 * to greyscale no mater what visual we are using. 233 */ 234 if (info->var.grayscale) 235 red = green = blue = (19595 * red + 38470 * green + 236 7471 * blue) >> 16; 237 238 switch (info->fix.visual) { 239 case FB_VISUAL_TRUECOLOR: 240 /* 241 * 12 or 16-bit True Colour. We encode the RGB value 242 * according to the RGB bitfield information. 243 */ 244 if (regno < 16) { 245 u32 *pal = info->pseudo_palette; 246 247 val = chan_to_field(red, &info->var.red); 248 val |= chan_to_field(green, &info->var.green); 249 val |= chan_to_field(blue, &info->var.blue); 250 251 pal[regno] = val; 252 ret = 0; 253 } 254 break; 255 256 case FB_VISUAL_STATIC_PSEUDOCOLOR: 257 case FB_VISUAL_PSEUDOCOLOR: 258 ret = imxfb_setpalettereg(regno, red, green, blue, trans, info); 259 break; 260 } 261 262 return ret; 263} 264 265/* 266 * imxfb_check_var(): 267 * Round up in the following order: bits_per_pixel, xres, 268 * yres, xres_virtual, yres_virtual, xoffset, yoffset, grayscale, 269 * bitfields, horizontal timing, vertical timing. 270 */ 271static int imxfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) 272{ 273 struct imxfb_info *fbi = info->par; 274 struct imxfb_rgb *rgb; 275 276 if (var->xres < MIN_XRES) 277 var->xres = MIN_XRES; 278 if (var->yres < MIN_YRES) 279 var->yres = MIN_YRES; 280 if (var->xres > fbi->max_xres) 281 var->xres = fbi->max_xres; 282 if (var->yres > fbi->max_yres) 283 var->yres = fbi->max_yres; 284 var->xres_virtual = max(var->xres_virtual, var->xres); 285 var->yres_virtual = max(var->yres_virtual, var->yres); 286 287 pr_debug("var->bits_per_pixel=%d\n", var->bits_per_pixel); 288 switch (var->bits_per_pixel) { 289 case 16: 290 default: 291 if (readl(fbi->regs + LCDC_PCR) & PCR_TFT) 292 rgb = &def_rgb_16_tft; 293 else 294 rgb = &def_rgb_16_stn; 295 break; 296 case 8: 297 rgb = &def_rgb_8; 298 break; 299 } 300 301 /* 302 * Copy the RGB parameters for this display 303 * from the machine specific parameters. 304 */ 305 var->red = rgb->red; 306 var->green = rgb->green; 307 var->blue = rgb->blue; 308 var->transp = rgb->transp; 309 310 pr_debug("RGBT length = %d:%d:%d:%d\n", 311 var->red.length, var->green.length, var->blue.length, 312 var->transp.length); 313 314 pr_debug("RGBT offset = %d:%d:%d:%d\n", 315 var->red.offset, var->green.offset, var->blue.offset, 316 var->transp.offset); 317 318 return 0; 319} 320 321/* 322 * imxfb_set_par(): 323 * Set the user defined part of the display for the specified console 324 */ 325static int imxfb_set_par(struct fb_info *info) 326{ 327 struct imxfb_info *fbi = info->par; 328 struct fb_var_screeninfo *var = &info->var; 329 330 pr_debug("set_par\n"); 331 332 if (var->bits_per_pixel == 16) 333 info->fix.visual = FB_VISUAL_TRUECOLOR; 334 else if (!fbi->cmap_static) 335 info->fix.visual = FB_VISUAL_PSEUDOCOLOR; 336 else { 337 /* 338 * Some people have weird ideas about wanting static 339 * pseudocolor maps. I suspect their user space 340 * applications are broken. 341 */ 342 info->fix.visual = FB_VISUAL_STATIC_PSEUDOCOLOR; 343 } 344 345 info->fix.line_length = var->xres_virtual * var->bits_per_pixel / 8; 346 fbi->palette_size = var->bits_per_pixel == 8 ? 256 : 16; 347 348 imxfb_activate_var(var, info); 349 350 return 0; 351} 352 353static void imxfb_enable_controller(struct imxfb_info *fbi) 354{ 355 pr_debug("Enabling LCD controller\n"); 356 357 /* initialize LCDC */ 358 writel(readl(fbi->regs + LCDC_RMCR) & ~RMCR_LCDC_EN, 359 fbi->regs + LCDC_RMCR); /* just to be safe... */ 360 361 writel(fbi->screen_dma, fbi->regs + LCDC_SSA); 362 363 /* physical screen start address */ 364 writel(VPW_VPW(fbi->max_xres * fbi->max_bpp / 8 / 4), 365 fbi->regs + LCDC_VPW); 366 367 /* panning offset 0 (0 pixel offset) */ 368 writel(0x00000000, fbi->regs + LCDC_POS); 369 370 /* disable hardware cursor */ 371 writel(readl(fbi->regs + LCDC_CPOS) & ~(CPOS_CC0 | CPOS_CC1), 372 fbi->regs + LCDC_CPOS); 373 374 writel(RMCR_LCDC_EN, fbi->regs + LCDC_RMCR); 375 376 if (fbi->backlight_power) 377 fbi->backlight_power(1); 378 if (fbi->lcd_power) 379 fbi->lcd_power(1); 380} 381 382static void imxfb_disable_controller(struct imxfb_info *fbi) 383{ 384 pr_debug("Disabling LCD controller\n"); 385 386 if (fbi->backlight_power) 387 fbi->backlight_power(0); 388 if (fbi->lcd_power) 389 fbi->lcd_power(0); 390 391 writel(0, fbi->regs + LCDC_RMCR); 392} 393 394static int imxfb_blank(int blank, struct fb_info *info) 395{ 396 struct imxfb_info *fbi = info->par; 397 398 pr_debug("imxfb_blank: blank=%d\n", blank); 399 400 switch (blank) { 401 case FB_BLANK_POWERDOWN: 402 case FB_BLANK_VSYNC_SUSPEND: 403 case FB_BLANK_HSYNC_SUSPEND: 404 case FB_BLANK_NORMAL: 405 imxfb_disable_controller(fbi); 406 break; 407 408 case FB_BLANK_UNBLANK: 409 imxfb_enable_controller(fbi); 410 break; 411 } 412 return 0; 413} 414 415static struct fb_ops imxfb_ops = { 416 .owner = THIS_MODULE, 417 .fb_check_var = imxfb_check_var, 418 .fb_set_par = imxfb_set_par, 419 .fb_setcolreg = imxfb_setcolreg, 420 .fb_fillrect = cfb_fillrect, 421 .fb_copyarea = cfb_copyarea, 422 .fb_imageblit = cfb_imageblit, 423 .fb_blank = imxfb_blank, 424}; 425 426/* 427 * imxfb_activate_var(): 428 * Configures LCD Controller based on entries in var parameter. Settings are 429 * only written to the controller if changes were made. 430 */ 431static int imxfb_activate_var(struct fb_var_screeninfo *var, struct fb_info *info) 432{ 433 struct imxfb_info *fbi = info->par; 434 pr_debug("var: xres=%d hslen=%d lm=%d rm=%d\n", 435 var->xres, var->hsync_len, 436 var->left_margin, var->right_margin); 437 pr_debug("var: yres=%d vslen=%d um=%d bm=%d\n", 438 var->yres, var->vsync_len, 439 var->upper_margin, var->lower_margin); 440 441#if DEBUG_VAR 442 if (var->xres < 16 || var->xres > 1024) 443 printk(KERN_ERR "%s: invalid xres %d\n", 444 info->fix.id, var->xres); 445 if (var->hsync_len < 1 || var->hsync_len > 64) 446 printk(KERN_ERR "%s: invalid hsync_len %d\n", 447 info->fix.id, var->hsync_len); 448 if (var->left_margin > 255) 449 printk(KERN_ERR "%s: invalid left_margin %d\n", 450 info->fix.id, var->left_margin); 451 if (var->right_margin > 255) 452 printk(KERN_ERR "%s: invalid right_margin %d\n", 453 info->fix.id, var->right_margin); 454 if (var->yres < 1 || var->yres > 511) 455 printk(KERN_ERR "%s: invalid yres %d\n", 456 info->fix.id, var->yres); 457 if (var->vsync_len > 100) 458 printk(KERN_ERR "%s: invalid vsync_len %d\n", 459 info->fix.id, var->vsync_len); 460 if (var->upper_margin > 63) 461 printk(KERN_ERR "%s: invalid upper_margin %d\n", 462 info->fix.id, var->upper_margin); 463 if (var->lower_margin > 255) 464 printk(KERN_ERR "%s: invalid lower_margin %d\n", 465 info->fix.id, var->lower_margin); 466#endif 467 468 writel(HCR_H_WIDTH(var->hsync_len) | 469 HCR_H_WAIT_1(var->right_margin) | 470 HCR_H_WAIT_2(var->left_margin), 471 fbi->regs + LCDC_HCR); 472 473 writel(VCR_V_WIDTH(var->vsync_len) | 474 VCR_V_WAIT_1(var->lower_margin) | 475 VCR_V_WAIT_2(var->upper_margin), 476 fbi->regs + LCDC_VCR); 477 478 writel(SIZE_XMAX(var->xres) | SIZE_YMAX(var->yres), 479 fbi->regs + LCDC_SIZE); 480 writel(fbi->pcr, fbi->regs + LCDC_PCR); 481 writel(fbi->pwmr, fbi->regs + LCDC_PWMR); 482 writel(fbi->lscr1, fbi->regs + LCDC_LSCR1); 483 writel(fbi->dmacr, fbi->regs + LCDC_DMACR); 484 485 return 0; 486} 487 488#ifdef CONFIG_PM 489/* 490 * Power management hooks. Note that we won't be called from IRQ context, 491 * unlike the blank functions above, so we may sleep. 492 */ 493static int imxfb_suspend(struct platform_device *dev, pm_message_t state) 494{ 495 struct imxfb_info *fbi = platform_get_drvdata(dev); 496 497 pr_debug("%s\n", __func__); 498 499 imxfb_disable_controller(fbi); 500 return 0; 501} 502 503static int imxfb_resume(struct platform_device *dev) 504{ 505 struct imxfb_info *fbi = platform_get_drvdata(dev); 506 507 pr_debug("%s\n", __func__); 508 509 imxfb_enable_controller(fbi); 510 return 0; 511} 512#else 513#define imxfb_suspend NULL 514#define imxfb_resume NULL 515#endif 516 517static int __init imxfb_init_fbinfo(struct platform_device *pdev) 518{ 519 struct imx_fb_platform_data *pdata = pdev->dev.platform_data; 520 struct fb_info *info = dev_get_drvdata(&pdev->dev); 521 struct imxfb_info *fbi = info->par; 522 523 pr_debug("%s\n",__func__); 524 525 info->pseudo_palette = kmalloc(sizeof(u32) * 16, GFP_KERNEL); 526 if (!info->pseudo_palette) 527 return -ENOMEM; 528 529 memset(fbi, 0, sizeof(struct imxfb_info)); 530 531 strlcpy(info->fix.id, IMX_NAME, sizeof(info->fix.id)); 532 533 info->fix.type = FB_TYPE_PACKED_PIXELS; 534 info->fix.type_aux = 0; 535 info->fix.xpanstep = 0; 536 info->fix.ypanstep = 0; 537 info->fix.ywrapstep = 0; 538 info->fix.accel = FB_ACCEL_NONE; 539 540 info->var.nonstd = 0; 541 info->var.activate = FB_ACTIVATE_NOW; 542 info->var.height = -1; 543 info->var.width = -1; 544 info->var.accel_flags = 0; 545 info->var.vmode = FB_VMODE_NONINTERLACED; 546 547 info->fbops = &imxfb_ops; 548 info->flags = FBINFO_FLAG_DEFAULT | 549 FBINFO_READS_FAST; 550 551 fbi->max_xres = pdata->xres; 552 info->var.xres = pdata->xres; 553 info->var.xres_virtual = pdata->xres; 554 fbi->max_yres = pdata->yres; 555 info->var.yres = pdata->yres; 556 info->var.yres_virtual = pdata->yres; 557 fbi->max_bpp = pdata->bpp; 558 info->var.bits_per_pixel = pdata->bpp; 559 info->var.nonstd = pdata->nonstd; 560 info->var.pixclock = pdata->pixclock; 561 info->var.hsync_len = pdata->hsync_len; 562 info->var.left_margin = pdata->left_margin; 563 info->var.right_margin = pdata->right_margin; 564 info->var.vsync_len = pdata->vsync_len; 565 info->var.upper_margin = pdata->upper_margin; 566 info->var.lower_margin = pdata->lower_margin; 567 info->var.sync = pdata->sync; 568 info->var.grayscale = pdata->cmap_greyscale; 569 fbi->cmap_inverse = pdata->cmap_inverse; 570 fbi->cmap_static = pdata->cmap_static; 571 fbi->pcr = pdata->pcr; 572 fbi->lscr1 = pdata->lscr1; 573 fbi->dmacr = pdata->dmacr; 574 fbi->pwmr = pdata->pwmr; 575 fbi->lcd_power = pdata->lcd_power; 576 fbi->backlight_power = pdata->backlight_power; 577 info->fix.smem_len = fbi->max_xres * fbi->max_yres * 578 fbi->max_bpp / 8; 579 580 return 0; 581} 582 583static int __init imxfb_probe(struct platform_device *pdev) 584{ 585 struct imxfb_info *fbi; 586 struct fb_info *info; 587 struct imx_fb_platform_data *pdata; 588 struct resource *res; 589 int ret; 590 591 printk("i.MX Framebuffer driver\n"); 592 593 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 594 if (!res) 595 return -ENODEV; 596 597 pdata = pdev->dev.platform_data; 598 if (!pdata) { 599 dev_err(&pdev->dev,"No platform_data available\n"); 600 return -ENOMEM; 601 } 602 603 info = framebuffer_alloc(sizeof(struct imxfb_info), &pdev->dev); 604 if (!info) 605 return -ENOMEM; 606 607 fbi = info->par; 608 609 platform_set_drvdata(pdev, info); 610 611 ret = imxfb_init_fbinfo(pdev); 612 if (ret < 0) 613 goto failed_init; 614 615 res = request_mem_region(res->start, resource_size(res), 616 DRIVER_NAME); 617 if (!res) { 618 ret = -EBUSY; 619 goto failed_req; 620 } 621 622 fbi->regs = ioremap(res->start, resource_size(res)); 623 if (fbi->regs == NULL) { 624 printk(KERN_ERR"Cannot map frame buffer registers\n"); 625 goto failed_ioremap; 626 } 627 628 if (!pdata->fixed_screen_cpu) { 629 fbi->map_size = PAGE_ALIGN(info->fix.smem_len); 630 fbi->map_cpu = dma_alloc_writecombine(&pdev->dev, 631 fbi->map_size, &fbi->map_dma, GFP_KERNEL); 632 633 if (!fbi->map_cpu) { 634 dev_err(&pdev->dev, "Failed to allocate video RAM: %d\n", ret); 635 ret = -ENOMEM; 636 goto failed_map; 637 } 638 639 info->screen_base = fbi->map_cpu; 640 fbi->screen_cpu = fbi->map_cpu; 641 fbi->screen_dma = fbi->map_dma; 642 info->fix.smem_start = fbi->screen_dma; 643 } else { 644 /* Fixed framebuffer mapping enables location of the screen in eSRAM */ 645 fbi->map_cpu = pdata->fixed_screen_cpu; 646 fbi->map_dma = pdata->fixed_screen_dma; 647 info->screen_base = fbi->map_cpu; 648 fbi->screen_cpu = fbi->map_cpu; 649 fbi->screen_dma = fbi->map_dma; 650 info->fix.smem_start = fbi->screen_dma; 651 } 652 653 /* 654 * This makes sure that our colour bitfield 655 * descriptors are correctly initialised. 656 */ 657 imxfb_check_var(&info->var, info); 658 659 ret = fb_alloc_cmap(&info->cmap, 1 << info->var.bits_per_pixel, 0); 660 if (ret < 0) 661 goto failed_cmap; 662 663 imxfb_set_par(info); 664 ret = register_framebuffer(info); 665 if (ret < 0) { 666 dev_err(&pdev->dev, "failed to register framebuffer\n"); 667 goto failed_register; 668 } 669 670 imxfb_enable_controller(fbi); 671 672 return 0; 673 674failed_register: 675 fb_dealloc_cmap(&info->cmap); 676failed_cmap: 677 if (!pdata->fixed_screen_cpu) 678 dma_free_writecombine(&pdev->dev,fbi->map_size,fbi->map_cpu, 679 fbi->map_dma); 680failed_map: 681 iounmap(fbi->regs); 682failed_ioremap: 683 release_mem_region(res->start, res->end - res->start); 684failed_req: 685 kfree(info->pseudo_palette); 686failed_init: 687 platform_set_drvdata(pdev, NULL); 688 framebuffer_release(info); 689 return ret; 690} 691 692static int __devexit imxfb_remove(struct platform_device *pdev) 693{ 694 struct fb_info *info = platform_get_drvdata(pdev); 695 struct imxfb_info *fbi = info->par; 696 struct resource *res; 697 698 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 699 700 imxfb_disable_controller(fbi); 701 702 unregister_framebuffer(info); 703 704 fb_dealloc_cmap(&info->cmap); 705 kfree(info->pseudo_palette); 706 framebuffer_release(info); 707 708 iounmap(fbi->regs); 709 release_mem_region(res->start, res->end - res->start + 1); 710 platform_set_drvdata(pdev, NULL); 711 712 return 0; 713} 714 715void imxfb_shutdown(struct platform_device * dev) 716{ 717 struct fb_info *info = platform_get_drvdata(dev); 718 struct imxfb_info *fbi = info->par; 719 imxfb_disable_controller(fbi); 720} 721 722static struct platform_driver imxfb_driver = { 723 .suspend = imxfb_suspend, 724 .resume = imxfb_resume, 725 .remove = __devexit_p(imxfb_remove), 726 .shutdown = imxfb_shutdown, 727 .driver = { 728 .name = DRIVER_NAME, 729 }, 730}; 731 732int __init imxfb_init(void) 733{ 734 return platform_driver_probe(&imxfb_driver, imxfb_probe); 735} 736 737static void __exit imxfb_cleanup(void) 738{ 739 platform_driver_unregister(&imxfb_driver); 740} 741 742module_init(imxfb_init); 743module_exit(imxfb_cleanup); 744 745MODULE_DESCRIPTION("Motorola i.MX framebuffer driver"); 746MODULE_AUTHOR("Sascha Hauer, Pengutronix"); 747MODULE_LICENSE("GPL");