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

fbdev: add BF52x EZkit Display driver

Signed-off-by: Michael Hennerich <michael.hennerich@analog.com>
Signed-off-by: Bryan Wu <bryan.wu@analog.com>
Cc: Antonino Daplas <adaplas@pol.net>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by

Michael Hennerich and committed by
Linus Torvalds
99eeed47 5e9e4ad0

+699
+13
drivers/video/Kconfig
··· 553 553 help 554 554 This is the framebuffer device driver for a SHARP LQ043T1DG01 TFT LCD 555 555 556 + config FB_BFIN_T350MCQB 557 + tristate "Varitronix COG-T350MCQB TFT LCD display (BF527 EZKIT)" 558 + depends on FB && BLACKFIN 559 + select BFIN_GPTIMERS 560 + select FB_CFB_FILLRECT 561 + select FB_CFB_COPYAREA 562 + select FB_CFB_IMAGEBLIT 563 + help 564 + This is the framebuffer device driver for a Varitronix VL-PS-COG-T350MCQB-01 display TFT LCD 565 + This display is a QVGA 320x240 24-bit RGB display interfaced by an 8-bit wide PPI 566 + It uses PPI[0..7] PPI_FS1, PPI_FS2 and PPI_CLK. 567 + 568 + 556 569 config FB_STI 557 570 tristate "HP STI frame buffer device support" 558 571 depends on FB && PARISC
+1
drivers/video/Makefile
··· 122 122 obj-$(CONFIG_FB_VGA16) += vga16fb.o 123 123 obj-$(CONFIG_FB_OF) += offb.o 124 124 obj-$(CONFIG_FB_BF54X_LQ043) += bf54x-lq043fb.o 125 + obj-$(CONFIG_FB_BFIN_T350MCQB) += bfin-t350mcqb-fb.o 125 126 126 127 # the test framebuffer is last 127 128 obj-$(CONFIG_FB_VIRTUAL) += vfb.o
+685
drivers/video/bfin-t350mcqb-fb.c
··· 1 + /* 2 + * File: drivers/video/bfin-t350mcqb-fb.c 3 + * Based on: 4 + * Author: Michael Hennerich <hennerich@blackfin.uclinux.org> 5 + * 6 + * Created: 7 + * Description: Blackfin LCD Framebufer driver 8 + * 9 + * 10 + * Modified: 11 + * Copyright 2004-2007 Analog Devices Inc. 12 + * 13 + * Bugs: Enter bugs at http://blackfin.uclinux.org/ 14 + * 15 + * This program is free software; you can redistribute it and/or modify 16 + * it under the terms of the GNU General Public License as published by 17 + * the Free Software Foundation; either version 2 of the License, or 18 + * (at your option) any later version. 19 + * 20 + * This program is distributed in the hope that it will be useful, 21 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 22 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 23 + * GNU General Public License for more details. 24 + * 25 + * You should have received a copy of the GNU General Public License 26 + * along with this program; if not, see the file COPYING, or write 27 + * to the Free Software Foundation, Inc., 28 + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 29 + */ 30 + 31 + #include <linux/module.h> 32 + #include <linux/kernel.h> 33 + #include <linux/errno.h> 34 + #include <linux/string.h> 35 + #include <linux/fb.h> 36 + #include <linux/init.h> 37 + #include <linux/types.h> 38 + #include <linux/interrupt.h> 39 + #include <linux/device.h> 40 + #include <linux/backlight.h> 41 + #include <linux/lcd.h> 42 + #include <linux/dma-mapping.h> 43 + #include <linux/platform_device.h> 44 + 45 + #include <asm/blackfin.h> 46 + #include <asm/irq.h> 47 + #include <asm/dma-mapping.h> 48 + #include <asm/dma.h> 49 + #include <asm/portmux.h> 50 + #include <asm/gptimers.h> 51 + 52 + #define NO_BL_SUPPORT 53 + 54 + #define LCD_X_RES 320 /* Horizontal Resolution */ 55 + #define LCD_Y_RES 240 /* Vertical Resolution */ 56 + #define LCD_BPP 24 /* Bit Per Pixel */ 57 + 58 + #define DMA_BUS_SIZE 16 59 + #define LCD_CLK (12*1000*1000) /* 12MHz */ 60 + 61 + #define CLOCKS_PER_PIX 3 62 + 63 + /* 64 + * HS and VS timing parameters (all in number of PPI clk ticks) 65 + */ 66 + 67 + #define U_LINE 1 /* Blanking Lines */ 68 + 69 + #define H_ACTPIX (LCD_X_RES * CLOCKS_PER_PIX) /* active horizontal pixel */ 70 + #define H_PERIOD (408 * CLOCKS_PER_PIX) /* HS period */ 71 + #define H_PULSE 90 /* HS pulse width */ 72 + #define H_START 204 /* first valid pixel */ 73 + 74 + #define V_LINES (LCD_Y_RES + U_LINE) /* total vertical lines */ 75 + #define V_PULSE (3 * H_PERIOD) /* VS pulse width (1-5 H_PERIODs) */ 76 + #define V_PERIOD (H_PERIOD * V_LINES) /* VS period */ 77 + 78 + #define ACTIVE_VIDEO_MEM_OFFSET (U_LINE * H_ACTPIX) 79 + 80 + #define BFIN_LCD_NBR_PALETTE_ENTRIES 256 81 + 82 + #define DRIVER_NAME "bfin-t350mcqb" 83 + static char driver_name[] = DRIVER_NAME; 84 + 85 + struct bfin_t350mcqbfb_info { 86 + struct fb_info *fb; 87 + struct device *dev; 88 + unsigned char *fb_buffer; /* RGB Buffer */ 89 + dma_addr_t dma_handle; 90 + int lq043_mmap; 91 + int lq043_open_cnt; 92 + int irq; 93 + spinlock_t lock; /* lock */ 94 + }; 95 + 96 + static int nocursor; 97 + module_param(nocursor, int, 0644); 98 + MODULE_PARM_DESC(nocursor, "cursor enable/disable"); 99 + 100 + #define PPI_TX_MODE 0x2 101 + #define PPI_XFER_TYPE_11 0xC 102 + #define PPI_PORT_CFG_01 0x10 103 + #define PPI_PACK_EN 0x80 104 + #define PPI_POLS_1 0x8000 105 + 106 + static void bfin_t350mcqb_config_ppi(struct bfin_t350mcqbfb_info *fbi) 107 + { 108 + bfin_write_PPI_DELAY(H_START); 109 + bfin_write_PPI_COUNT(H_ACTPIX-1); 110 + bfin_write_PPI_FRAME(V_LINES); 111 + 112 + bfin_write_PPI_CONTROL(PPI_TX_MODE | /* output mode , PORT_DIR */ 113 + PPI_XFER_TYPE_11 | /* sync mode XFR_TYPE */ 114 + PPI_PORT_CFG_01 | /* two frame sync PORT_CFG */ 115 + PPI_PACK_EN | /* packing enabled PACK_EN */ 116 + PPI_POLS_1); /* faling edge syncs POLS */ 117 + } 118 + 119 + static inline void bfin_t350mcqb_disable_ppi(void) 120 + { 121 + bfin_write_PPI_CONTROL(bfin_read_PPI_CONTROL() & ~PORT_EN); 122 + } 123 + 124 + static inline void bfin_t350mcqb_enable_ppi(void) 125 + { 126 + bfin_write_PPI_CONTROL(bfin_read_PPI_CONTROL() | PORT_EN); 127 + } 128 + 129 + static void bfin_t350mcqb_start_timers(void) 130 + { 131 + unsigned long flags; 132 + 133 + local_irq_save(flags); 134 + enable_gptimers(TIMER1bit); 135 + enable_gptimers(TIMER0bit); 136 + local_irq_restore(flags); 137 + } 138 + 139 + static void bfin_t350mcqb_stop_timers(void) 140 + { 141 + disable_gptimers(TIMER0bit | TIMER1bit); 142 + 143 + set_gptimer_status(0, TIMER_STATUS_TRUN0 | TIMER_STATUS_TRUN1 | 144 + TIMER_STATUS_TIMIL0 | TIMER_STATUS_TIMIL1 | 145 + TIMER_STATUS_TOVF0 | TIMER_STATUS_TOVF1); 146 + 147 + } 148 + 149 + static void bfin_t350mcqb_init_timers(void) 150 + { 151 + 152 + bfin_t350mcqb_stop_timers(); 153 + 154 + set_gptimer_period(TIMER0_id, H_PERIOD); 155 + set_gptimer_pwidth(TIMER0_id, H_PULSE); 156 + set_gptimer_config(TIMER0_id, TIMER_MODE_PWM | TIMER_PERIOD_CNT | 157 + TIMER_TIN_SEL | TIMER_CLK_SEL| 158 + TIMER_EMU_RUN); 159 + 160 + set_gptimer_period(TIMER1_id, V_PERIOD); 161 + set_gptimer_pwidth(TIMER1_id, V_PULSE); 162 + set_gptimer_config(TIMER1_id, TIMER_MODE_PWM | TIMER_PERIOD_CNT | 163 + TIMER_TIN_SEL | TIMER_CLK_SEL | 164 + TIMER_EMU_RUN); 165 + 166 + } 167 + 168 + static void bfin_t350mcqb_config_dma(struct bfin_t350mcqbfb_info *fbi) 169 + { 170 + 171 + set_dma_config(CH_PPI, 172 + set_bfin_dma_config(DIR_READ, DMA_FLOW_AUTO, 173 + INTR_DISABLE, DIMENSION_2D, 174 + DATA_SIZE_16, 175 + DMA_NOSYNC_KEEP_DMA_BUF)); 176 + set_dma_x_count(CH_PPI, (LCD_X_RES * LCD_BPP) / DMA_BUS_SIZE); 177 + set_dma_x_modify(CH_PPI, DMA_BUS_SIZE / 8); 178 + set_dma_y_count(CH_PPI, V_LINES); 179 + 180 + set_dma_y_modify(CH_PPI, DMA_BUS_SIZE / 8); 181 + set_dma_start_addr(CH_PPI, (unsigned long)fbi->fb_buffer); 182 + 183 + } 184 + 185 + static int bfin_t350mcqb_request_ports(int action) 186 + { 187 + u16 ppi0_req_8[] = {P_PPI0_CLK, P_PPI0_FS1, P_PPI0_FS2, 188 + P_PPI0_D0, P_PPI0_D1, P_PPI0_D2, 189 + P_PPI0_D3, P_PPI0_D4, P_PPI0_D5, 190 + P_PPI0_D6, P_PPI0_D7, 0}; 191 + 192 + if (action) { 193 + if (peripheral_request_list(ppi0_req_8, DRIVER_NAME)) { 194 + printk(KERN_ERR "Requesting Peripherals faild\n"); 195 + return -EFAULT; 196 + } 197 + } else 198 + peripheral_free_list(ppi0_req_8); 199 + 200 + return 0; 201 + } 202 + 203 + static int bfin_t350mcqb_fb_open(struct fb_info *info, int user) 204 + { 205 + struct bfin_t350mcqbfb_info *fbi = info->par; 206 + 207 + spin_lock(&fbi->lock); 208 + fbi->lq043_open_cnt++; 209 + 210 + if (fbi->lq043_open_cnt <= 1) { 211 + 212 + bfin_t350mcqb_disable_ppi(); 213 + SSYNC(); 214 + 215 + bfin_t350mcqb_config_dma(fbi); 216 + bfin_t350mcqb_config_ppi(fbi); 217 + bfin_t350mcqb_init_timers(); 218 + 219 + /* start dma */ 220 + enable_dma(CH_PPI); 221 + bfin_t350mcqb_enable_ppi(); 222 + bfin_t350mcqb_start_timers(); 223 + } 224 + 225 + spin_unlock(&fbi->lock); 226 + 227 + return 0; 228 + } 229 + 230 + static int bfin_t350mcqb_fb_release(struct fb_info *info, int user) 231 + { 232 + struct bfin_t350mcqbfb_info *fbi = info->par; 233 + 234 + spin_lock(&fbi->lock); 235 + 236 + fbi->lq043_open_cnt--; 237 + fbi->lq043_mmap = 0; 238 + 239 + if (fbi->lq043_open_cnt <= 0) { 240 + bfin_t350mcqb_disable_ppi(); 241 + SSYNC(); 242 + disable_dma(CH_PPI); 243 + bfin_t350mcqb_stop_timers(); 244 + memset(fbi->fb_buffer, 0, info->fix.smem_len); 245 + } 246 + 247 + spin_unlock(&fbi->lock); 248 + 249 + return 0; 250 + } 251 + 252 + static int bfin_t350mcqb_fb_check_var(struct fb_var_screeninfo *var, 253 + struct fb_info *info) 254 + { 255 + 256 + if (var->bits_per_pixel != LCD_BPP) { 257 + pr_debug("%s: depth not supported: %u BPP\n", __FUNCTION__, 258 + var->bits_per_pixel); 259 + return -EINVAL; 260 + } 261 + 262 + if (info->var.xres != var->xres || info->var.yres != var->yres || 263 + info->var.xres_virtual != var->xres_virtual || 264 + info->var.yres_virtual != var->yres_virtual) { 265 + pr_debug("%s: Resolution not supported: X%u x Y%u \n", 266 + __FUNCTION__, var->xres, var->yres); 267 + return -EINVAL; 268 + } 269 + 270 + /* 271 + * Memory limit 272 + */ 273 + 274 + if ((info->fix.line_length * var->yres_virtual) > info->fix.smem_len) { 275 + pr_debug("%s: Memory Limit requested yres_virtual = %u\n", 276 + __FUNCTION__, var->yres_virtual); 277 + return -ENOMEM; 278 + } 279 + 280 + return 0; 281 + } 282 + 283 + static int bfin_t350mcqb_fb_mmap(struct fb_info *info, struct vm_area_struct *vma) 284 + { 285 + struct bfin_t350mcqbfb_info *fbi = info->par; 286 + 287 + if (fbi->lq043_mmap) 288 + return -1; 289 + 290 + spin_lock(&fbi->lock); 291 + fbi->lq043_mmap = 1; 292 + spin_unlock(&fbi->lock); 293 + 294 + vma->vm_start = (unsigned long)(fbi->fb_buffer + ACTIVE_VIDEO_MEM_OFFSET); 295 + 296 + vma->vm_end = vma->vm_start + info->fix.smem_len; 297 + /* For those who don't understand how mmap works, go read 298 + * Documentation/nommu-mmap.txt. 299 + * For those that do, you will know that the VM_MAYSHARE flag 300 + * must be set in the vma->vm_flags structure on noMMU 301 + * Other flags can be set, and are documented in 302 + * include/linux/mm.h 303 + */ 304 + vma->vm_flags |= VM_MAYSHARE; 305 + 306 + return 0; 307 + } 308 + 309 + int bfin_t350mcqb_fb_cursor(struct fb_info *info, struct fb_cursor *cursor) 310 + { 311 + if (nocursor) 312 + return 0; 313 + else 314 + return -EINVAL; /* just to force soft_cursor() call */ 315 + } 316 + 317 + static int bfin_t350mcqb_fb_setcolreg(u_int regno, u_int red, u_int green, 318 + u_int blue, u_int transp, 319 + struct fb_info *info) 320 + { 321 + if (regno >= BFIN_LCD_NBR_PALETTE_ENTRIES) 322 + return -EINVAL; 323 + 324 + if (info->var.grayscale) { 325 + /* grayscale = 0.30*R + 0.59*G + 0.11*B */ 326 + red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8; 327 + } 328 + 329 + if (info->fix.visual == FB_VISUAL_TRUECOLOR) { 330 + 331 + u32 value; 332 + /* Place color in the pseudopalette */ 333 + if (regno > 16) 334 + return -EINVAL; 335 + 336 + red >>= (16 - info->var.red.length); 337 + green >>= (16 - info->var.green.length); 338 + blue >>= (16 - info->var.blue.length); 339 + 340 + value = (red << info->var.red.offset) | 341 + (green << info->var.green.offset) | 342 + (blue << info->var.blue.offset); 343 + value &= 0xFFFFFF; 344 + 345 + ((u32 *) (info->pseudo_palette))[regno] = value; 346 + 347 + } 348 + 349 + return 0; 350 + } 351 + 352 + static struct fb_ops bfin_t350mcqb_fb_ops = { 353 + .owner = THIS_MODULE, 354 + .fb_open = bfin_t350mcqb_fb_open, 355 + .fb_release = bfin_t350mcqb_fb_release, 356 + .fb_check_var = bfin_t350mcqb_fb_check_var, 357 + .fb_fillrect = cfb_fillrect, 358 + .fb_copyarea = cfb_copyarea, 359 + .fb_imageblit = cfb_imageblit, 360 + .fb_mmap = bfin_t350mcqb_fb_mmap, 361 + .fb_cursor = bfin_t350mcqb_fb_cursor, 362 + .fb_setcolreg = bfin_t350mcqb_fb_setcolreg, 363 + }; 364 + 365 + #ifndef NO_BL_SUPPORT 366 + static int bl_get_brightness(struct backlight_device *bd) 367 + { 368 + return 0; 369 + } 370 + 371 + static struct backlight_ops bfin_lq043fb_bl_ops = { 372 + .get_brightness = bl_get_brightness, 373 + }; 374 + 375 + static struct backlight_device *bl_dev; 376 + 377 + static int bfin_lcd_get_power(struct lcd_device *dev) 378 + { 379 + return 0; 380 + } 381 + 382 + static int bfin_lcd_set_power(struct lcd_device *dev, int power) 383 + { 384 + return 0; 385 + } 386 + 387 + static int bfin_lcd_get_contrast(struct lcd_device *dev) 388 + { 389 + return 0; 390 + } 391 + 392 + static int bfin_lcd_set_contrast(struct lcd_device *dev, int contrast) 393 + { 394 + 395 + return 0; 396 + } 397 + 398 + static int bfin_lcd_check_fb(struct fb_info *fi) 399 + { 400 + if (!fi || (fi == &bfin_t350mcqb_fb)) 401 + return 1; 402 + return 0; 403 + } 404 + 405 + static struct lcd_ops bfin_lcd_ops = { 406 + .get_power = bfin_lcd_get_power, 407 + .set_power = bfin_lcd_set_power, 408 + .get_contrast = bfin_lcd_get_contrast, 409 + .set_contrast = bfin_lcd_set_contrast, 410 + .check_fb = bfin_lcd_check_fb, 411 + }; 412 + 413 + static struct lcd_device *lcd_dev; 414 + #endif 415 + 416 + static irqreturn_t bfin_t350mcqb_irq_error(int irq, void *dev_id) 417 + { 418 + /*struct bfin_t350mcqbfb_info *info = (struct bfin_t350mcqbfb_info *)dev_id;*/ 419 + 420 + u16 status = bfin_read_PPI_STATUS(); 421 + bfin_write_PPI_STATUS(0xFFFF); 422 + 423 + if (status) { 424 + bfin_t350mcqb_disable_ppi(); 425 + disable_dma(CH_PPI); 426 + 427 + /* start dma */ 428 + enable_dma(CH_PPI); 429 + bfin_t350mcqb_enable_ppi(); 430 + bfin_write_PPI_STATUS(0xFFFF); 431 + } 432 + 433 + return IRQ_HANDLED; 434 + } 435 + 436 + static int __init bfin_t350mcqb_probe(struct platform_device *pdev) 437 + { 438 + struct bfin_t350mcqbfb_info *info; 439 + struct fb_info *fbinfo; 440 + int ret; 441 + 442 + printk(KERN_INFO DRIVER_NAME ": %dx%d %d-bit RGB FrameBuffer initializing...\n", 443 + LCD_X_RES, LCD_Y_RES, LCD_BPP); 444 + 445 + if (request_dma(CH_PPI, "CH_PPI") < 0) { 446 + printk(KERN_ERR DRIVER_NAME 447 + ": couldn't request CH_PPI DMA\n"); 448 + ret = -EFAULT; 449 + goto out1; 450 + } 451 + 452 + fbinfo = 453 + framebuffer_alloc(sizeof(struct bfin_t350mcqbfb_info), &pdev->dev); 454 + if (!fbinfo) { 455 + ret = -ENOMEM; 456 + goto out2; 457 + } 458 + 459 + info = fbinfo->par; 460 + info->fb = fbinfo; 461 + info->dev = &pdev->dev; 462 + 463 + platform_set_drvdata(pdev, fbinfo); 464 + 465 + strcpy(fbinfo->fix.id, driver_name); 466 + 467 + fbinfo->fix.type = FB_TYPE_PACKED_PIXELS; 468 + fbinfo->fix.type_aux = 0; 469 + fbinfo->fix.xpanstep = 0; 470 + fbinfo->fix.ypanstep = 0; 471 + fbinfo->fix.ywrapstep = 0; 472 + fbinfo->fix.accel = FB_ACCEL_NONE; 473 + fbinfo->fix.visual = FB_VISUAL_TRUECOLOR; 474 + 475 + fbinfo->var.nonstd = 0; 476 + fbinfo->var.activate = FB_ACTIVATE_NOW; 477 + fbinfo->var.height = -1; 478 + fbinfo->var.width = -1; 479 + fbinfo->var.accel_flags = 0; 480 + fbinfo->var.vmode = FB_VMODE_NONINTERLACED; 481 + 482 + fbinfo->var.xres = LCD_X_RES; 483 + fbinfo->var.xres_virtual = LCD_X_RES; 484 + fbinfo->var.yres = LCD_Y_RES; 485 + fbinfo->var.yres_virtual = LCD_Y_RES; 486 + fbinfo->var.bits_per_pixel = LCD_BPP; 487 + 488 + fbinfo->var.red.offset = 0; 489 + fbinfo->var.green.offset = 8; 490 + fbinfo->var.blue.offset = 16; 491 + fbinfo->var.transp.offset = 0; 492 + fbinfo->var.red.length = 8; 493 + fbinfo->var.green.length = 8; 494 + fbinfo->var.blue.length = 8; 495 + fbinfo->var.transp.length = 0; 496 + fbinfo->fix.smem_len = LCD_X_RES * LCD_Y_RES * LCD_BPP / 8; 497 + 498 + fbinfo->fix.line_length = fbinfo->var.xres_virtual * 499 + fbinfo->var.bits_per_pixel / 8; 500 + 501 + 502 + fbinfo->fbops = &bfin_t350mcqb_fb_ops; 503 + fbinfo->flags = FBINFO_FLAG_DEFAULT; 504 + 505 + info->fb_buffer = 506 + dma_alloc_coherent(NULL, fbinfo->fix.smem_len, &info->dma_handle, 507 + GFP_KERNEL); 508 + 509 + if (NULL == info->fb_buffer) { 510 + printk(KERN_ERR DRIVER_NAME 511 + ": couldn't allocate dma buffer.\n"); 512 + ret = -ENOMEM; 513 + goto out3; 514 + } 515 + 516 + memset(info->fb_buffer, 0, fbinfo->fix.smem_len); 517 + 518 + fbinfo->screen_base = (void *)info->fb_buffer + ACTIVE_VIDEO_MEM_OFFSET; 519 + fbinfo->fix.smem_start = (int)info->fb_buffer + ACTIVE_VIDEO_MEM_OFFSET; 520 + 521 + fbinfo->fbops = &bfin_t350mcqb_fb_ops; 522 + 523 + fbinfo->pseudo_palette = kmalloc(sizeof(u32) * 16, GFP_KERNEL); 524 + if (!fbinfo->pseudo_palette) { 525 + printk(KERN_ERR DRIVER_NAME 526 + "Fail to allocate pseudo_palette\n"); 527 + 528 + ret = -ENOMEM; 529 + goto out4; 530 + } 531 + 532 + memset(fbinfo->pseudo_palette, 0, sizeof(u32) * 16); 533 + 534 + if (fb_alloc_cmap(&fbinfo->cmap, BFIN_LCD_NBR_PALETTE_ENTRIES, 0) 535 + < 0) { 536 + printk(KERN_ERR DRIVER_NAME 537 + "Fail to allocate colormap (%d entries)\n", 538 + BFIN_LCD_NBR_PALETTE_ENTRIES); 539 + ret = -EFAULT; 540 + goto out5; 541 + } 542 + 543 + if (bfin_t350mcqb_request_ports(1)) { 544 + printk(KERN_ERR DRIVER_NAME ": couldn't request gpio port.\n"); 545 + ret = -EFAULT; 546 + goto out6; 547 + } 548 + 549 + info->irq = platform_get_irq(pdev, 0); 550 + if (info->irq < 0) { 551 + ret = -EINVAL; 552 + goto out7; 553 + } 554 + 555 + if (request_irq(info->irq, (void *)bfin_t350mcqb_irq_error, IRQF_DISABLED, 556 + "PPI ERROR", info) < 0) { 557 + printk(KERN_ERR DRIVER_NAME 558 + ": unable to request PPI ERROR IRQ\n"); 559 + ret = -EFAULT; 560 + goto out7; 561 + } 562 + 563 + if (register_framebuffer(fbinfo) < 0) { 564 + printk(KERN_ERR DRIVER_NAME 565 + ": unable to register framebuffer.\n"); 566 + ret = -EINVAL; 567 + goto out8; 568 + } 569 + #ifndef NO_BL_SUPPORT 570 + bl_dev = 571 + backlight_device_register("bf52x-bl", NULL, NULL, 572 + &bfin_lq043fb_bl_ops); 573 + bl_dev->props.max_brightness = 255; 574 + 575 + lcd_dev = lcd_device_register(DRIVER_NAME, NULL, &bfin_lcd_ops); 576 + lcd_dev->props.max_contrast = 255, printk(KERN_INFO "Done.\n"); 577 + #endif 578 + 579 + return 0; 580 + 581 + out8: 582 + free_irq(info->irq, info); 583 + out7: 584 + bfin_t350mcqb_request_ports(0); 585 + out6: 586 + fb_dealloc_cmap(&fbinfo->cmap); 587 + out5: 588 + kfree(fbinfo->pseudo_palette); 589 + out4: 590 + dma_free_coherent(NULL, fbinfo->fix.smem_len, info->fb_buffer, 591 + info->dma_handle); 592 + out3: 593 + framebuffer_release(fbinfo); 594 + out2: 595 + free_dma(CH_PPI); 596 + out1: 597 + platform_set_drvdata(pdev, NULL); 598 + 599 + return ret; 600 + } 601 + 602 + static int bfin_t350mcqb_remove(struct platform_device *pdev) 603 + { 604 + 605 + struct fb_info *fbinfo = platform_get_drvdata(pdev); 606 + struct bfin_t350mcqbfb_info *info = fbinfo->par; 607 + 608 + free_dma(CH_PPI); 609 + free_irq(info->irq, info); 610 + 611 + if (info->fb_buffer != NULL) 612 + dma_free_coherent(NULL, fbinfo->fix.smem_len, info->fb_buffer, 613 + info->dma_handle); 614 + 615 + kfree(fbinfo->pseudo_palette); 616 + fb_dealloc_cmap(&fbinfo->cmap); 617 + 618 + #ifndef NO_BL_SUPPORT 619 + lcd_device_unregister(lcd_dev); 620 + backlight_device_unregister(bl_dev); 621 + #endif 622 + 623 + unregister_framebuffer(fbinfo); 624 + 625 + bfin_t350mcqb_request_ports(0); 626 + 627 + printk(KERN_INFO DRIVER_NAME ": Unregister LCD driver.\n"); 628 + 629 + return 0; 630 + } 631 + 632 + #ifdef CONFIG_PM 633 + static int bfin_t350mcqb_suspend(struct platform_device *pdev, pm_message_t state) 634 + { 635 + struct fb_info *fbinfo = platform_get_drvdata(pdev); 636 + struct bfin_t350mcqbfb_info *info = fbinfo->par; 637 + 638 + bfin_t350mcqb_disable_ppi(); 639 + disable_dma(CH_PPI); 640 + bfin_write_PPI_STATUS(0xFFFF); 641 + 642 + return 0; 643 + } 644 + 645 + static int bfin_t350mcqb_resume(struct platform_device *pdev) 646 + { 647 + struct fb_info *fbinfo = platform_get_drvdata(pdev); 648 + struct bfin_t350mcqbfb_info *info = fbinfo->par; 649 + 650 + enable_dma(CH_PPI); 651 + bfin_t350mcqb_enable_ppi(); 652 + 653 + return 0; 654 + } 655 + #else 656 + #define bfin_t350mcqb_suspend NULL 657 + #define bfin_t350mcqb_resume NULL 658 + #endif 659 + 660 + static struct platform_driver bfin_t350mcqb_driver = { 661 + .probe = bfin_t350mcqb_probe, 662 + .remove = bfin_t350mcqb_remove, 663 + .suspend = bfin_t350mcqb_suspend, 664 + .resume = bfin_t350mcqb_resume, 665 + .driver = { 666 + .name = DRIVER_NAME, 667 + .owner = THIS_MODULE, 668 + }, 669 + }; 670 + 671 + static int __devinit bfin_t350mcqb_driver_init(void) 672 + { 673 + return platform_driver_register(&bfin_t350mcqb_driver); 674 + } 675 + 676 + static void __exit bfin_t350mcqb_driver_cleanup(void) 677 + { 678 + platform_driver_unregister(&bfin_t350mcqb_driver); 679 + } 680 + 681 + MODULE_DESCRIPTION("Blackfin TFT LCD Driver"); 682 + MODULE_LICENSE("GPL"); 683 + 684 + module_init(bfin_t350mcqb_driver_init); 685 + module_exit(bfin_t350mcqb_driver_cleanup);