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.33-rc3 648 lines 18 kB view raw
1/* 2 * linux/drivers/video/ep93xx-fb.c 3 * 4 * Framebuffer support for the EP93xx series. 5 * 6 * Copyright (C) 2007 Bluewater Systems Ltd 7 * Author: Ryan Mallon <ryan@bluewatersys.com> 8 * 9 * Copyright (c) 2009 H Hartley Sweeten <hsweeten@visionengravers.com> 10 * 11 * Based on the Cirrus Logic ep93xxfb driver, and various other ep93xxfb 12 * drivers. 13 * 14 * This program is free software; you can redistribute it and/or modify 15 * it under the terms of the GNU General Public License version 2 as 16 * published by the Free Software Foundation. 17 * 18 */ 19 20#include <linux/platform_device.h> 21#include <linux/dma-mapping.h> 22#include <linux/clk.h> 23#include <linux/fb.h> 24 25#include <mach/fb.h> 26 27/* Vertical Frame Timing Registers */ 28#define EP93XXFB_VLINES_TOTAL 0x0000 /* SW locked */ 29#define EP93XXFB_VSYNC 0x0004 /* SW locked */ 30#define EP93XXFB_VACTIVE 0x0008 /* SW locked */ 31#define EP93XXFB_VBLANK 0x0228 /* SW locked */ 32#define EP93XXFB_VCLK 0x000c /* SW locked */ 33 34/* Horizontal Frame Timing Registers */ 35#define EP93XXFB_HCLKS_TOTAL 0x0010 /* SW locked */ 36#define EP93XXFB_HSYNC 0x0014 /* SW locked */ 37#define EP93XXFB_HACTIVE 0x0018 /* SW locked */ 38#define EP93XXFB_HBLANK 0x022c /* SW locked */ 39#define EP93XXFB_HCLK 0x001c /* SW locked */ 40 41/* Frame Buffer Memory Configuration Registers */ 42#define EP93XXFB_SCREEN_PAGE 0x0028 43#define EP93XXFB_SCREEN_HPAGE 0x002c 44#define EP93XXFB_SCREEN_LINES 0x0030 45#define EP93XXFB_LINE_LENGTH 0x0034 46#define EP93XXFB_VLINE_STEP 0x0038 47#define EP93XXFB_LINE_CARRY 0x003c /* SW locked */ 48#define EP93XXFB_EOL_OFFSET 0x0230 49 50/* Other Video Registers */ 51#define EP93XXFB_BRIGHTNESS 0x0020 52#define EP93XXFB_ATTRIBS 0x0024 /* SW locked */ 53#define EP93XXFB_SWLOCK 0x007c /* SW locked */ 54#define EP93XXFB_AC_RATE 0x0214 55#define EP93XXFB_FIFO_LEVEL 0x0234 56#define EP93XXFB_PIXELMODE 0x0054 57#define EP93XXFB_PIXELMODE_32BPP (0x7 << 0) 58#define EP93XXFB_PIXELMODE_24BPP (0x6 << 0) 59#define EP93XXFB_PIXELMODE_16BPP (0x4 << 0) 60#define EP93XXFB_PIXELMODE_8BPP (0x2 << 0) 61#define EP93XXFB_PIXELMODE_SHIFT_1P_24B (0x0 << 3) 62#define EP93XXFB_PIXELMODE_SHIFT_1P_18B (0x1 << 3) 63#define EP93XXFB_PIXELMODE_COLOR_LUT (0x0 << 10) 64#define EP93XXFB_PIXELMODE_COLOR_888 (0x4 << 10) 65#define EP93XXFB_PIXELMODE_COLOR_555 (0x5 << 10) 66#define EP93XXFB_PARL_IF_OUT 0x0058 67#define EP93XXFB_PARL_IF_IN 0x005c 68 69/* Blink Control Registers */ 70#define EP93XXFB_BLINK_RATE 0x0040 71#define EP93XXFB_BLINK_MASK 0x0044 72#define EP93XXFB_BLINK_PATTRN 0x0048 73#define EP93XXFB_PATTRN_MASK 0x004c 74#define EP93XXFB_BKGRND_OFFSET 0x0050 75 76/* Hardware Cursor Registers */ 77#define EP93XXFB_CURSOR_ADR_START 0x0060 78#define EP93XXFB_CURSOR_ADR_RESET 0x0064 79#define EP93XXFB_CURSOR_SIZE 0x0068 80#define EP93XXFB_CURSOR_COLOR1 0x006c 81#define EP93XXFB_CURSOR_COLOR2 0x0070 82#define EP93XXFB_CURSOR_BLINK_COLOR1 0x021c 83#define EP93XXFB_CURSOR_BLINK_COLOR2 0x0220 84#define EP93XXFB_CURSOR_XY_LOC 0x0074 85#define EP93XXFB_CURSOR_DSCAN_HY_LOC 0x0078 86#define EP93XXFB_CURSOR_BLINK_RATE_CTRL 0x0224 87 88/* LUT Registers */ 89#define EP93XXFB_GRY_SCL_LUTR 0x0080 90#define EP93XXFB_GRY_SCL_LUTG 0x0280 91#define EP93XXFB_GRY_SCL_LUTB 0x0300 92#define EP93XXFB_LUT_SW_CONTROL 0x0218 93#define EP93XXFB_LUT_SW_CONTROL_SWTCH (1 << 0) 94#define EP93XXFB_LUT_SW_CONTROL_SSTAT (1 << 1) 95#define EP93XXFB_COLOR_LUT 0x0400 96 97/* Video Signature Registers */ 98#define EP93XXFB_VID_SIG_RSLT_VAL 0x0200 99#define EP93XXFB_VID_SIG_CTRL 0x0204 100#define EP93XXFB_VSIG 0x0208 101#define EP93XXFB_HSIG 0x020c 102#define EP93XXFB_SIG_CLR_STR 0x0210 103 104/* Minimum / Maximum resolutions supported */ 105#define EP93XXFB_MIN_XRES 64 106#define EP93XXFB_MIN_YRES 64 107#define EP93XXFB_MAX_XRES 1024 108#define EP93XXFB_MAX_YRES 768 109 110struct ep93xx_fbi { 111 struct ep93xxfb_mach_info *mach_info; 112 struct clk *clk; 113 struct resource *res; 114 void __iomem *mmio_base; 115 unsigned int pseudo_palette[256]; 116}; 117 118static int check_screenpage_bug = 1; 119module_param(check_screenpage_bug, int, 0644); 120MODULE_PARM_DESC(check_screenpage_bug, 121 "Check for bit 27 screen page bug. Default = 1"); 122 123static inline unsigned int ep93xxfb_readl(struct ep93xx_fbi *fbi, 124 unsigned int off) 125{ 126 return __raw_readl(fbi->mmio_base + off); 127} 128 129static inline void ep93xxfb_writel(struct ep93xx_fbi *fbi, 130 unsigned int val, unsigned int off) 131{ 132 __raw_writel(val, fbi->mmio_base + off); 133} 134 135/* 136 * Write to one of the locked raster registers. 137 */ 138static inline void ep93xxfb_out_locked(struct ep93xx_fbi *fbi, 139 unsigned int val, unsigned int reg) 140{ 141 /* 142 * We don't need a lock or delay here since the raster register 143 * block will remain unlocked until the next access. 144 */ 145 ep93xxfb_writel(fbi, 0xaa, EP93XXFB_SWLOCK); 146 ep93xxfb_writel(fbi, val, reg); 147} 148 149static void ep93xxfb_set_video_attribs(struct fb_info *info) 150{ 151 struct ep93xx_fbi *fbi = info->par; 152 unsigned int attribs; 153 154 attribs = EP93XXFB_ENABLE; 155 attribs |= fbi->mach_info->flags; 156 ep93xxfb_out_locked(fbi, attribs, EP93XXFB_ATTRIBS); 157} 158 159static int ep93xxfb_set_pixelmode(struct fb_info *info) 160{ 161 struct ep93xx_fbi *fbi = info->par; 162 unsigned int val; 163 164 info->var.transp.offset = 0; 165 info->var.transp.length = 0; 166 167 switch (info->var.bits_per_pixel) { 168 case 8: 169 val = EP93XXFB_PIXELMODE_8BPP | EP93XXFB_PIXELMODE_COLOR_LUT | 170 EP93XXFB_PIXELMODE_SHIFT_1P_18B; 171 172 info->var.red.offset = 0; 173 info->var.red.length = 8; 174 info->var.green.offset = 0; 175 info->var.green.length = 8; 176 info->var.blue.offset = 0; 177 info->var.blue.length = 8; 178 info->fix.visual = FB_VISUAL_PSEUDOCOLOR; 179 break; 180 181 case 16: 182 val = EP93XXFB_PIXELMODE_16BPP | EP93XXFB_PIXELMODE_COLOR_555 | 183 EP93XXFB_PIXELMODE_SHIFT_1P_18B; 184 185 info->var.red.offset = 11; 186 info->var.red.length = 5; 187 info->var.green.offset = 5; 188 info->var.green.length = 6; 189 info->var.blue.offset = 0; 190 info->var.blue.length = 5; 191 info->fix.visual = FB_VISUAL_TRUECOLOR; 192 break; 193 194 case 24: 195 val = EP93XXFB_PIXELMODE_24BPP | EP93XXFB_PIXELMODE_COLOR_888 | 196 EP93XXFB_PIXELMODE_SHIFT_1P_24B; 197 198 info->var.red.offset = 16; 199 info->var.red.length = 8; 200 info->var.green.offset = 8; 201 info->var.green.length = 8; 202 info->var.blue.offset = 0; 203 info->var.blue.length = 8; 204 info->fix.visual = FB_VISUAL_TRUECOLOR; 205 break; 206 207 case 32: 208 val = EP93XXFB_PIXELMODE_32BPP | EP93XXFB_PIXELMODE_COLOR_888 | 209 EP93XXFB_PIXELMODE_SHIFT_1P_24B; 210 211 info->var.red.offset = 16; 212 info->var.red.length = 8; 213 info->var.green.offset = 8; 214 info->var.green.length = 8; 215 info->var.blue.offset = 0; 216 info->var.blue.length = 8; 217 info->fix.visual = FB_VISUAL_TRUECOLOR; 218 break; 219 220 default: 221 return -EINVAL; 222 } 223 224 ep93xxfb_writel(fbi, val, EP93XXFB_PIXELMODE); 225 return 0; 226} 227 228static void ep93xxfb_set_timing(struct fb_info *info) 229{ 230 struct ep93xx_fbi *fbi = info->par; 231 unsigned int vlines_total, hclks_total, start, stop; 232 233 vlines_total = info->var.yres + info->var.upper_margin + 234 info->var.lower_margin + info->var.vsync_len - 1; 235 236 hclks_total = info->var.xres + info->var.left_margin + 237 info->var.right_margin + info->var.hsync_len - 1; 238 239 ep93xxfb_out_locked(fbi, vlines_total, EP93XXFB_VLINES_TOTAL); 240 ep93xxfb_out_locked(fbi, hclks_total, EP93XXFB_HCLKS_TOTAL); 241 242 start = vlines_total; 243 stop = vlines_total - info->var.vsync_len; 244 ep93xxfb_out_locked(fbi, start | (stop << 16), EP93XXFB_VSYNC); 245 246 start = vlines_total - info->var.vsync_len - info->var.upper_margin; 247 stop = info->var.lower_margin - 1; 248 ep93xxfb_out_locked(fbi, start | (stop << 16), EP93XXFB_VBLANK); 249 ep93xxfb_out_locked(fbi, start | (stop << 16), EP93XXFB_VACTIVE); 250 251 start = vlines_total; 252 stop = vlines_total + 1; 253 ep93xxfb_out_locked(fbi, start | (stop << 16), EP93XXFB_VCLK); 254 255 start = hclks_total; 256 stop = hclks_total - info->var.hsync_len; 257 ep93xxfb_out_locked(fbi, start | (stop << 16), EP93XXFB_HSYNC); 258 259 start = hclks_total - info->var.hsync_len - info->var.left_margin; 260 stop = info->var.right_margin - 1; 261 ep93xxfb_out_locked(fbi, start | (stop << 16), EP93XXFB_HBLANK); 262 ep93xxfb_out_locked(fbi, start | (stop << 16), EP93XXFB_HACTIVE); 263 264 start = hclks_total; 265 stop = hclks_total; 266 ep93xxfb_out_locked(fbi, start | (stop << 16), EP93XXFB_HCLK); 267 268 ep93xxfb_out_locked(fbi, 0x0, EP93XXFB_LINE_CARRY); 269} 270 271static int ep93xxfb_set_par(struct fb_info *info) 272{ 273 struct ep93xx_fbi *fbi = info->par; 274 275 clk_set_rate(fbi->clk, 1000 * PICOS2KHZ(info->var.pixclock)); 276 277 ep93xxfb_set_timing(info); 278 279 info->fix.line_length = info->var.xres_virtual * 280 info->var.bits_per_pixel / 8; 281 282 ep93xxfb_writel(fbi, info->fix.smem_start, EP93XXFB_SCREEN_PAGE); 283 ep93xxfb_writel(fbi, info->var.yres - 1, EP93XXFB_SCREEN_LINES); 284 ep93xxfb_writel(fbi, ((info->var.xres * info->var.bits_per_pixel) 285 / 32) - 1, EP93XXFB_LINE_LENGTH); 286 ep93xxfb_writel(fbi, info->fix.line_length / 4, EP93XXFB_VLINE_STEP); 287 ep93xxfb_set_video_attribs(info); 288 return 0; 289} 290 291static int ep93xxfb_check_var(struct fb_var_screeninfo *var, 292 struct fb_info *info) 293{ 294 int err; 295 296 err = ep93xxfb_set_pixelmode(info); 297 if (err) 298 return err; 299 300 var->xres = max_t(unsigned int, var->xres, EP93XXFB_MIN_XRES); 301 var->xres = min_t(unsigned int, var->xres, EP93XXFB_MAX_XRES); 302 var->xres_virtual = max(var->xres_virtual, var->xres); 303 304 var->yres = max_t(unsigned int, var->yres, EP93XXFB_MIN_YRES); 305 var->yres = min_t(unsigned int, var->yres, EP93XXFB_MAX_YRES); 306 var->yres_virtual = max(var->yres_virtual, var->yres); 307 308 return 0; 309} 310 311static int ep93xxfb_mmap(struct fb_info *info, struct vm_area_struct *vma) 312{ 313 unsigned int offset = vma->vm_pgoff << PAGE_SHIFT; 314 315 if (offset < info->fix.smem_len) { 316 return dma_mmap_writecombine(info->dev, vma, info->screen_base, 317 info->fix.smem_start, 318 info->fix.smem_len); 319 } 320 321 return -EINVAL; 322} 323 324static int ep93xxfb_blank(int blank_mode, struct fb_info *info) 325{ 326 struct ep93xx_fbi *fbi = info->par; 327 unsigned int attribs = ep93xxfb_readl(fbi, EP93XXFB_ATTRIBS); 328 329 if (blank_mode) { 330 if (fbi->mach_info->blank) 331 fbi->mach_info->blank(blank_mode, info); 332 ep93xxfb_out_locked(fbi, attribs & ~EP93XXFB_ENABLE, 333 EP93XXFB_ATTRIBS); 334 clk_disable(fbi->clk); 335 } else { 336 clk_enable(fbi->clk); 337 ep93xxfb_out_locked(fbi, attribs | EP93XXFB_ENABLE, 338 EP93XXFB_ATTRIBS); 339 if (fbi->mach_info->blank) 340 fbi->mach_info->blank(blank_mode, info); 341 } 342 343 return 0; 344} 345 346static inline int ep93xxfb_convert_color(int val, int width) 347{ 348 return ((val << width) + 0x7fff - val) >> 16; 349} 350 351static int ep93xxfb_setcolreg(unsigned int regno, unsigned int red, 352 unsigned int green, unsigned int blue, 353 unsigned int transp, struct fb_info *info) 354{ 355 struct ep93xx_fbi *fbi = info->par; 356 unsigned int *pal = info->pseudo_palette; 357 unsigned int ctrl, i, rgb, lut_current, lut_stat; 358 359 switch (info->fix.visual) { 360 case FB_VISUAL_PSEUDOCOLOR: 361 if (regno > 255) 362 return 1; 363 rgb = ((red & 0xff00) << 8) | (green & 0xff00) | 364 ((blue & 0xff00) >> 8); 365 366 pal[regno] = rgb; 367 ep93xxfb_writel(fbi, rgb, (EP93XXFB_COLOR_LUT + (regno << 2))); 368 ctrl = ep93xxfb_readl(fbi, EP93XXFB_LUT_SW_CONTROL); 369 lut_stat = !!(ctrl & EP93XXFB_LUT_SW_CONTROL_SSTAT); 370 lut_current = !!(ctrl & EP93XXFB_LUT_SW_CONTROL_SWTCH); 371 372 if (lut_stat == lut_current) { 373 for (i = 0; i < 256; i++) { 374 ep93xxfb_writel(fbi, pal[i], 375 EP93XXFB_COLOR_LUT + (i << 2)); 376 } 377 378 ep93xxfb_writel(fbi, 379 ctrl ^ EP93XXFB_LUT_SW_CONTROL_SWTCH, 380 EP93XXFB_LUT_SW_CONTROL); 381 } 382 break; 383 384 case FB_VISUAL_TRUECOLOR: 385 if (regno > 16) 386 return 1; 387 388 red = ep93xxfb_convert_color(red, info->var.red.length); 389 green = ep93xxfb_convert_color(green, info->var.green.length); 390 blue = ep93xxfb_convert_color(blue, info->var.blue.length); 391 transp = ep93xxfb_convert_color(transp, 392 info->var.transp.length); 393 394 pal[regno] = (red << info->var.red.offset) | 395 (green << info->var.green.offset) | 396 (blue << info->var.blue.offset) | 397 (transp << info->var.transp.offset); 398 break; 399 400 default: 401 return 1; 402 } 403 404 return 0; 405} 406 407static struct fb_ops ep93xxfb_ops = { 408 .owner = THIS_MODULE, 409 .fb_check_var = ep93xxfb_check_var, 410 .fb_set_par = ep93xxfb_set_par, 411 .fb_blank = ep93xxfb_blank, 412 .fb_fillrect = cfb_fillrect, 413 .fb_copyarea = cfb_copyarea, 414 .fb_imageblit = cfb_imageblit, 415 .fb_setcolreg = ep93xxfb_setcolreg, 416 .fb_mmap = ep93xxfb_mmap, 417}; 418 419static int __init ep93xxfb_calc_fbsize(struct ep93xxfb_mach_info *mach_info) 420{ 421 int i, fb_size = 0; 422 423 if (mach_info->num_modes == EP93XXFB_USE_MODEDB) { 424 fb_size = EP93XXFB_MAX_XRES * EP93XXFB_MAX_YRES * 425 mach_info->bpp / 8; 426 } else { 427 for (i = 0; i < mach_info->num_modes; i++) { 428 const struct fb_videomode *mode; 429 int size; 430 431 mode = &mach_info->modes[i]; 432 size = mode->xres * mode->yres * mach_info->bpp / 8; 433 if (size > fb_size) 434 fb_size = size; 435 } 436 } 437 438 return fb_size; 439} 440 441static int __init ep93xxfb_alloc_videomem(struct fb_info *info) 442{ 443 struct ep93xx_fbi *fbi = info->par; 444 char __iomem *virt_addr; 445 dma_addr_t phys_addr; 446 unsigned int fb_size; 447 448 fb_size = ep93xxfb_calc_fbsize(fbi->mach_info); 449 virt_addr = dma_alloc_writecombine(info->dev, fb_size, 450 &phys_addr, GFP_KERNEL); 451 if (!virt_addr) 452 return -ENOMEM; 453 454 /* 455 * There is a bug in the ep93xx framebuffer which causes problems 456 * if bit 27 of the physical address is set. 457 * See: http://marc.info/?l=linux-arm-kernel&m=110061245502000&w=2 458 * There does not seem to be any offical errata for this, but I 459 * have confirmed the problem exists on my hardware (ep9315) at 460 * least. 461 */ 462 if (check_screenpage_bug && phys_addr & (1 << 27)) { 463 dev_err(info->dev, "ep93xx framebuffer bug. phys addr (0x%x) " 464 "has bit 27 set: cannot init framebuffer\n", 465 phys_addr); 466 467 dma_free_coherent(info->dev, fb_size, virt_addr, phys_addr); 468 return -ENOMEM; 469 } 470 471 info->fix.smem_start = phys_addr; 472 info->fix.smem_len = fb_size; 473 info->screen_base = virt_addr; 474 475 return 0; 476} 477 478static void ep93xxfb_dealloc_videomem(struct fb_info *info) 479{ 480 if (info->screen_base) 481 dma_free_coherent(info->dev, info->fix.smem_len, 482 info->screen_base, info->fix.smem_start); 483} 484 485static int __init ep93xxfb_probe(struct platform_device *pdev) 486{ 487 struct ep93xxfb_mach_info *mach_info = pdev->dev.platform_data; 488 struct fb_info *info; 489 struct ep93xx_fbi *fbi; 490 struct resource *res; 491 char *video_mode; 492 int err; 493 494 if (!mach_info) 495 return -EINVAL; 496 497 info = framebuffer_alloc(sizeof(struct ep93xx_fbi), &pdev->dev); 498 if (!info) 499 return -ENOMEM; 500 501 info->dev = &pdev->dev; 502 platform_set_drvdata(pdev, info); 503 fbi = info->par; 504 fbi->mach_info = mach_info; 505 506 err = fb_alloc_cmap(&info->cmap, 256, 0); 507 if (err) 508 goto failed; 509 510 err = ep93xxfb_alloc_videomem(info); 511 if (err) 512 goto failed; 513 514 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 515 if (!res) { 516 err = -ENXIO; 517 goto failed; 518 } 519 520 res = request_mem_region(res->start, resource_size(res), pdev->name); 521 if (!res) { 522 err = -EBUSY; 523 goto failed; 524 } 525 526 fbi->res = res; 527 fbi->mmio_base = ioremap(res->start, resource_size(res)); 528 if (!fbi->mmio_base) { 529 err = -ENXIO; 530 goto failed; 531 } 532 533 strcpy(info->fix.id, pdev->name); 534 info->fbops = &ep93xxfb_ops; 535 info->fix.type = FB_TYPE_PACKED_PIXELS; 536 info->fix.accel = FB_ACCEL_NONE; 537 info->var.activate = FB_ACTIVATE_NOW; 538 info->var.vmode = FB_VMODE_NONINTERLACED; 539 info->flags = FBINFO_DEFAULT; 540 info->node = -1; 541 info->state = FBINFO_STATE_RUNNING; 542 info->pseudo_palette = &fbi->pseudo_palette; 543 544 fb_get_options("ep93xx-fb", &video_mode); 545 err = fb_find_mode(&info->var, info, video_mode, 546 fbi->mach_info->modes, fbi->mach_info->num_modes, 547 fbi->mach_info->default_mode, fbi->mach_info->bpp); 548 if (err == 0) { 549 dev_err(info->dev, "No suitable video mode found\n"); 550 err = -EINVAL; 551 goto failed; 552 } 553 554 if (mach_info->setup) { 555 err = mach_info->setup(pdev); 556 if (err) 557 return err; 558 } 559 560 err = ep93xxfb_check_var(&info->var, info); 561 if (err) 562 goto failed; 563 564 fbi->clk = clk_get(info->dev, NULL); 565 if (IS_ERR(fbi->clk)) { 566 err = PTR_ERR(fbi->clk); 567 fbi->clk = NULL; 568 goto failed; 569 } 570 571 ep93xxfb_set_par(info); 572 clk_enable(fbi->clk); 573 574 err = register_framebuffer(info); 575 if (err) 576 goto failed; 577 578 dev_info(info->dev, "registered. Mode = %dx%d-%d\n", 579 info->var.xres, info->var.yres, info->var.bits_per_pixel); 580 return 0; 581 582failed: 583 if (fbi->clk) 584 clk_put(fbi->clk); 585 if (fbi->mmio_base) 586 iounmap(fbi->mmio_base); 587 if (fbi->res) 588 release_mem_region(fbi->res->start, resource_size(fbi->res)); 589 ep93xxfb_dealloc_videomem(info); 590 if (&info->cmap) 591 fb_dealloc_cmap(&info->cmap); 592 if (fbi->mach_info->teardown) 593 fbi->mach_info->teardown(pdev); 594 kfree(info); 595 platform_set_drvdata(pdev, NULL); 596 597 return err; 598} 599 600static int ep93xxfb_remove(struct platform_device *pdev) 601{ 602 struct fb_info *info = platform_get_drvdata(pdev); 603 struct ep93xx_fbi *fbi = info->par; 604 605 unregister_framebuffer(info); 606 clk_disable(fbi->clk); 607 clk_put(fbi->clk); 608 iounmap(fbi->mmio_base); 609 release_mem_region(fbi->res->start, resource_size(fbi->res)); 610 ep93xxfb_dealloc_videomem(info); 611 fb_dealloc_cmap(&info->cmap); 612 613 if (fbi->mach_info->teardown) 614 fbi->mach_info->teardown(pdev); 615 616 kfree(info); 617 platform_set_drvdata(pdev, NULL); 618 619 return 0; 620} 621 622static struct platform_driver ep93xxfb_driver = { 623 .probe = ep93xxfb_probe, 624 .remove = ep93xxfb_remove, 625 .driver = { 626 .name = "ep93xx-fb", 627 .owner = THIS_MODULE, 628 }, 629}; 630 631static int __devinit ep93xxfb_init(void) 632{ 633 return platform_driver_register(&ep93xxfb_driver); 634} 635 636static void __exit ep93xxfb_exit(void) 637{ 638 platform_driver_unregister(&ep93xxfb_driver); 639} 640 641module_init(ep93xxfb_init); 642module_exit(ep93xxfb_exit); 643 644MODULE_DESCRIPTION("EP93XX Framebuffer Driver"); 645MODULE_ALIAS("platform:ep93xx-fb"); 646MODULE_AUTHOR("Ryan Mallon <ryan&bluewatersys.com>, " 647 "H Hartley Sweeten <hsweeten@visionengravers.com"); 648MODULE_LICENSE("GPL");