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

fbdev: bfin-lq035q1-fb: new Blackfin Landscape LCD EZ-Extender driver

Framebuffer driver for the Landscape LCD EZ-Extender (ADZS-BFLLCD-EZEXT)
http://docs.blackfin.uclinux.org/doku.php?id=hw:cards:landscape_lcd_ez-extender

Signed-off-by: Michael Hennerich <michael.hennerich@analog.com>
Signed-off-by: Bryan Wu <cooloney@kernel.org>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
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
9cfe4a93 07697461

+870
+28
arch/blackfin/include/asm/bfin-lq035q1.h
··· 1 + /* 2 + * Blackfin LCD Framebuffer driver SHARP LQ035Q1DH02 3 + * 4 + * Copyright 2008-2009 Analog Devices Inc. 5 + * Licensed under the GPL-2 or later. 6 + */ 7 + 8 + #ifndef BFIN_LQ035Q1_H 9 + #define BFIN_LQ035Q1_H 10 + 11 + #define LQ035_RL (0 << 8) /* Right -> Left Scan */ 12 + #define LQ035_LR (1 << 8) /* Left -> Right Scan */ 13 + #define LQ035_TB (1 << 9) /* Top -> Botton Scan */ 14 + #define LQ035_BT (0 << 9) /* Botton -> Top Scan */ 15 + #define LQ035_BGR (1 << 11) /* Use BGR format */ 16 + #define LQ035_RGB (0 << 11) /* Use RGB format */ 17 + #define LQ035_NORM (1 << 13) /* Reversal */ 18 + #define LQ035_REV (0 << 13) /* Reversal */ 19 + 20 + struct bfin_lq035q1fb_disp_info { 21 + 22 + unsigned mode; 23 + /* GPIOs */ 24 + int use_bl; 25 + unsigned gpio_bl; 26 + }; 27 + 28 + #endif /* BFIN_LQ035Q1_H */
+15
drivers/video/Kconfig
··· 614 614 This display is a QVGA 320x240 24-bit RGB display interfaced by an 8-bit wide PPI 615 615 It uses PPI[0..7] PPI_FS1, PPI_FS2 and PPI_CLK. 616 616 617 + config FB_BFIN_LQ035Q1 618 + tristate "SHARP LQ035Q1DH02 TFT LCD" 619 + depends on FB && BLACKFIN && SPI 620 + select FB_CFB_FILLRECT 621 + select FB_CFB_COPYAREA 622 + select FB_CFB_IMAGEBLIT 623 + select BFIN_GPTIMERS 624 + help 625 + This is the framebuffer device driver for a SHARP LQ035Q1DH02 TFT display found on 626 + the Blackfin Landscape LCD EZ-Extender Card. 627 + This display is a QVGA 320x240 18-bit RGB display interfaced by an 16-bit wide PPI 628 + It uses PPI[0..15] PPI_FS1, PPI_FS2 and PPI_CLK. 629 + 630 + To compile this driver as a module, choose M here: the 631 + module will be called bfin-lq035q1-fb. 617 632 618 633 config FB_STI 619 634 tristate "HP STI frame buffer device support"
+1
drivers/video/Makefile
··· 137 137 obj-$(CONFIG_FB_VGA16) += vga16fb.o 138 138 obj-$(CONFIG_FB_OF) += offb.o 139 139 obj-$(CONFIG_FB_BF54X_LQ043) += bf54x-lq043fb.o 140 + obj-$(CONFIG_FB_BFIN_LQ035Q1) += bfin-lq035q1-fb.o 140 141 obj-$(CONFIG_FB_BFIN_T350MCQB) += bfin-t350mcqb-fb.o 141 142 obj-$(CONFIG_FB_MX3) += mx3fb.o 142 143 obj-$(CONFIG_FB_DA8XX) += da8xx-fb.o
+826
drivers/video/bfin-lq035q1-fb.c
··· 1 + /* 2 + * Blackfin LCD Framebuffer driver SHARP LQ035Q1DH02 3 + * 4 + * Copyright 2008-2009 Analog Devices Inc. 5 + * Licensed under the GPL-2 or later. 6 + */ 7 + 8 + #define DRIVER_NAME "bfin-lq035q1" 9 + #define pr_fmt(fmt) DRIVER_NAME ": " fmt 10 + 11 + #include <linux/module.h> 12 + #include <linux/kernel.h> 13 + #include <linux/errno.h> 14 + #include <linux/string.h> 15 + #include <linux/fb.h> 16 + #include <linux/init.h> 17 + #include <linux/types.h> 18 + #include <linux/interrupt.h> 19 + #include <linux/device.h> 20 + #include <linux/backlight.h> 21 + #include <linux/lcd.h> 22 + #include <linux/dma-mapping.h> 23 + #include <linux/platform_device.h> 24 + #include <linux/spi/spi.h> 25 + #include <linux/dma-mapping.h> 26 + 27 + #include <asm/blackfin.h> 28 + #include <asm/irq.h> 29 + #include <asm/dma.h> 30 + #include <asm/portmux.h> 31 + #include <asm/gptimers.h> 32 + 33 + #include <asm/bfin-lq035q1.h> 34 + 35 + #if defined(BF533_FAMILY) || defined(BF538_FAMILY) 36 + #define TIMER_HSYNC_id TIMER1_id 37 + #define TIMER_HSYNCbit TIMER1bit 38 + #define TIMER_HSYNC_STATUS_TRUN TIMER_STATUS_TRUN1 39 + #define TIMER_HSYNC_STATUS_TIMIL TIMER_STATUS_TIMIL1 40 + #define TIMER_HSYNC_STATUS_TOVF TIMER_STATUS_TOVF1 41 + 42 + #define TIMER_VSYNC_id TIMER2_id 43 + #define TIMER_VSYNCbit TIMER2bit 44 + #define TIMER_VSYNC_STATUS_TRUN TIMER_STATUS_TRUN2 45 + #define TIMER_VSYNC_STATUS_TIMIL TIMER_STATUS_TIMIL2 46 + #define TIMER_VSYNC_STATUS_TOVF TIMER_STATUS_TOVF2 47 + #else 48 + #define TIMER_HSYNC_id TIMER0_id 49 + #define TIMER_HSYNCbit TIMER0bit 50 + #define TIMER_HSYNC_STATUS_TRUN TIMER_STATUS_TRUN0 51 + #define TIMER_HSYNC_STATUS_TIMIL TIMER_STATUS_TIMIL0 52 + #define TIMER_HSYNC_STATUS_TOVF TIMER_STATUS_TOVF0 53 + 54 + #define TIMER_VSYNC_id TIMER1_id 55 + #define TIMER_VSYNCbit TIMER1bit 56 + #define TIMER_VSYNC_STATUS_TRUN TIMER_STATUS_TRUN1 57 + #define TIMER_VSYNC_STATUS_TIMIL TIMER_STATUS_TIMIL1 58 + #define TIMER_VSYNC_STATUS_TOVF TIMER_STATUS_TOVF1 59 + #endif 60 + 61 + #define LCD_X_RES 320 /* Horizontal Resolution */ 62 + #define LCD_Y_RES 240 /* Vertical Resolution */ 63 + #define DMA_BUS_SIZE 16 64 + 65 + #define USE_RGB565_16_BIT_PPI 66 + 67 + #ifdef USE_RGB565_16_BIT_PPI 68 + #define LCD_BPP 16 /* Bit Per Pixel */ 69 + #define CLOCKS_PER_PIX 1 70 + #define CPLD_PIPELINE_DELAY_COR 0 /* NO CPLB */ 71 + #endif 72 + 73 + /* Interface 16/18-bit TFT over an 8-bit wide PPI using a small Programmable Logic Device (CPLD) 74 + * http://blackfin.uclinux.org/gf/project/stamp/frs/?action=FrsReleaseBrowse&frs_package_id=165 75 + */ 76 + 77 + #ifdef USE_RGB565_8_BIT_PPI 78 + #define LCD_BPP 16 /* Bit Per Pixel */ 79 + #define CLOCKS_PER_PIX 2 80 + #define CPLD_PIPELINE_DELAY_COR 3 /* RGB565 */ 81 + #endif 82 + 83 + #ifdef USE_RGB888_8_BIT_PPI 84 + #define LCD_BPP 24 /* Bit Per Pixel */ 85 + #define CLOCKS_PER_PIX 3 86 + #define CPLD_PIPELINE_DELAY_COR 5 /* RGB888 */ 87 + #endif 88 + 89 + /* 90 + * HS and VS timing parameters (all in number of PPI clk ticks) 91 + */ 92 + 93 + #define U_LINE 4 /* Blanking Lines */ 94 + 95 + #define H_ACTPIX (LCD_X_RES * CLOCKS_PER_PIX) /* active horizontal pixel */ 96 + #define H_PERIOD (336 * CLOCKS_PER_PIX) /* HS period */ 97 + #define H_PULSE (2 * CLOCKS_PER_PIX) /* HS pulse width */ 98 + #define H_START (7 * CLOCKS_PER_PIX + CPLD_PIPELINE_DELAY_COR) /* first valid pixel */ 99 + 100 + #define V_LINES (LCD_Y_RES + U_LINE) /* total vertical lines */ 101 + #define V_PULSE (2 * CLOCKS_PER_PIX) /* VS pulse width (1-5 H_PERIODs) */ 102 + #define V_PERIOD (H_PERIOD * V_LINES) /* VS period */ 103 + 104 + #define ACTIVE_VIDEO_MEM_OFFSET ((U_LINE / 2) * LCD_X_RES * (LCD_BPP / 8)) 105 + 106 + #define BFIN_LCD_NBR_PALETTE_ENTRIES 256 107 + 108 + #define PPI_TX_MODE 0x2 109 + #define PPI_XFER_TYPE_11 0xC 110 + #define PPI_PORT_CFG_01 0x10 111 + #define PPI_POLS_1 0x8000 112 + 113 + #if (CLOCKS_PER_PIX > 1) 114 + #define PPI_PMODE (DLEN_8 | PACK_EN) 115 + #else 116 + #define PPI_PMODE (DLEN_16) 117 + #endif 118 + 119 + #define LQ035_INDEX 0x74 120 + #define LQ035_DATA 0x76 121 + 122 + #define LQ035_DRIVER_OUTPUT_CTL 0x1 123 + #define LQ035_SHUT_CTL 0x11 124 + 125 + #define LQ035_DRIVER_OUTPUT_MASK (LQ035_LR | LQ035_TB | LQ035_BGR | LQ035_REV) 126 + #define LQ035_DRIVER_OUTPUT_DEFAULT (0x2AEF & ~LQ035_DRIVER_OUTPUT_MASK) 127 + 128 + #define LQ035_SHUT (1 << 0) /* Shutdown */ 129 + #define LQ035_ON (0 << 0) /* Shutdown */ 130 + 131 + struct bfin_lq035q1fb_info { 132 + struct fb_info *fb; 133 + struct device *dev; 134 + struct spi_driver spidrv; 135 + struct bfin_lq035q1fb_disp_info *disp_info; 136 + unsigned char *fb_buffer; /* RGB Buffer */ 137 + dma_addr_t dma_handle; 138 + int lq035_open_cnt; 139 + int irq; 140 + spinlock_t lock; /* lock */ 141 + u32 pseudo_pal[16]; 142 + }; 143 + 144 + static int nocursor; 145 + module_param(nocursor, int, 0644); 146 + MODULE_PARM_DESC(nocursor, "cursor enable/disable"); 147 + 148 + struct spi_control { 149 + unsigned short mode; 150 + }; 151 + 152 + static int lq035q1_control(struct spi_device *spi, unsigned char reg, unsigned short value) 153 + { 154 + int ret; 155 + u8 regs[3] = { LQ035_INDEX, 0, 0 }; 156 + u8 dat[3] = { LQ035_DATA, 0, 0 }; 157 + 158 + if (!spi) 159 + return -ENODEV; 160 + 161 + regs[2] = reg; 162 + dat[1] = value >> 8; 163 + dat[2] = value & 0xFF; 164 + 165 + ret = spi_write(spi, regs, ARRAY_SIZE(regs)); 166 + ret |= spi_write(spi, dat, ARRAY_SIZE(dat)); 167 + return ret; 168 + } 169 + 170 + static int __devinit lq035q1_spidev_probe(struct spi_device *spi) 171 + { 172 + int ret; 173 + struct spi_control *ctl; 174 + struct bfin_lq035q1fb_info *info = container_of(spi->dev.driver, 175 + struct bfin_lq035q1fb_info, 176 + spidrv.driver); 177 + 178 + ctl = kzalloc(sizeof(*ctl), GFP_KERNEL); 179 + 180 + if (!ctl) 181 + return -ENOMEM; 182 + 183 + ctl->mode = (info->disp_info->mode & 184 + LQ035_DRIVER_OUTPUT_MASK) | LQ035_DRIVER_OUTPUT_DEFAULT; 185 + 186 + ret = lq035q1_control(spi, LQ035_SHUT_CTL, LQ035_ON); 187 + ret |= lq035q1_control(spi, LQ035_DRIVER_OUTPUT_CTL, ctl->mode); 188 + if (ret) 189 + return ret; 190 + 191 + spi_set_drvdata(spi, ctl); 192 + 193 + return 0; 194 + } 195 + 196 + static int lq035q1_spidev_remove(struct spi_device *spi) 197 + { 198 + return lq035q1_control(spi, LQ035_SHUT_CTL, LQ035_SHUT); 199 + } 200 + 201 + #ifdef CONFIG_PM 202 + static int lq035q1_spidev_suspend(struct spi_device *spi, pm_message_t state) 203 + { 204 + return lq035q1_control(spi, LQ035_SHUT_CTL, LQ035_SHUT); 205 + } 206 + 207 + static int lq035q1_spidev_resume(struct spi_device *spi) 208 + { 209 + int ret; 210 + struct spi_control *ctl = spi_get_drvdata(spi); 211 + 212 + ret = lq035q1_control(spi, LQ035_DRIVER_OUTPUT_CTL, ctl->mode); 213 + if (ret) 214 + return ret; 215 + 216 + return lq035q1_control(spi, LQ035_SHUT_CTL, LQ035_ON); 217 + } 218 + #else 219 + # define lq035q1_spidev_suspend NULL 220 + # define lq035q1_spidev_resume NULL 221 + #endif 222 + 223 + /* Power down all displays on reboot, poweroff or halt */ 224 + static void lq035q1_spidev_shutdown(struct spi_device *spi) 225 + { 226 + lq035q1_control(spi, LQ035_SHUT_CTL, LQ035_SHUT); 227 + } 228 + 229 + static int lq035q1_backlight(struct bfin_lq035q1fb_info *info, unsigned arg) 230 + { 231 + if (info->disp_info->use_bl) 232 + gpio_set_value(info->disp_info->gpio_bl, arg); 233 + 234 + return 0; 235 + } 236 + 237 + static void bfin_lq035q1_config_ppi(struct bfin_lq035q1fb_info *fbi) 238 + { 239 + bfin_write_PPI_DELAY(H_START); 240 + bfin_write_PPI_COUNT(H_ACTPIX - 1); 241 + bfin_write_PPI_FRAME(V_LINES); 242 + 243 + bfin_write_PPI_CONTROL(PPI_TX_MODE | /* output mode , PORT_DIR */ 244 + PPI_XFER_TYPE_11 | /* sync mode XFR_TYPE */ 245 + PPI_PORT_CFG_01 | /* two frame sync PORT_CFG */ 246 + PPI_PMODE | /* 8/16 bit data length / PACK_EN? */ 247 + PPI_POLS_1); /* faling edge syncs POLS */ 248 + } 249 + 250 + static inline void bfin_lq035q1_disable_ppi(void) 251 + { 252 + bfin_write_PPI_CONTROL(bfin_read_PPI_CONTROL() & ~PORT_EN); 253 + } 254 + 255 + static inline void bfin_lq035q1_enable_ppi(void) 256 + { 257 + bfin_write_PPI_CONTROL(bfin_read_PPI_CONTROL() | PORT_EN); 258 + } 259 + 260 + static void bfin_lq035q1_start_timers(void) 261 + { 262 + enable_gptimers(TIMER_VSYNCbit | TIMER_HSYNCbit); 263 + } 264 + 265 + static void bfin_lq035q1_stop_timers(void) 266 + { 267 + disable_gptimers(TIMER_HSYNCbit | TIMER_VSYNCbit); 268 + 269 + set_gptimer_status(0, TIMER_HSYNC_STATUS_TRUN | TIMER_VSYNC_STATUS_TRUN | 270 + TIMER_HSYNC_STATUS_TIMIL | TIMER_VSYNC_STATUS_TIMIL | 271 + TIMER_HSYNC_STATUS_TOVF | TIMER_VSYNC_STATUS_TOVF); 272 + 273 + } 274 + 275 + static void bfin_lq035q1_init_timers(void) 276 + { 277 + 278 + bfin_lq035q1_stop_timers(); 279 + 280 + set_gptimer_period(TIMER_HSYNC_id, H_PERIOD); 281 + set_gptimer_pwidth(TIMER_HSYNC_id, H_PULSE); 282 + set_gptimer_config(TIMER_HSYNC_id, TIMER_MODE_PWM | TIMER_PERIOD_CNT | 283 + TIMER_TIN_SEL | TIMER_CLK_SEL| 284 + TIMER_EMU_RUN); 285 + 286 + set_gptimer_period(TIMER_VSYNC_id, V_PERIOD); 287 + set_gptimer_pwidth(TIMER_VSYNC_id, V_PULSE); 288 + set_gptimer_config(TIMER_VSYNC_id, TIMER_MODE_PWM | TIMER_PERIOD_CNT | 289 + TIMER_TIN_SEL | TIMER_CLK_SEL | 290 + TIMER_EMU_RUN); 291 + 292 + } 293 + 294 + static void bfin_lq035q1_config_dma(struct bfin_lq035q1fb_info *fbi) 295 + { 296 + 297 + set_dma_config(CH_PPI, 298 + set_bfin_dma_config(DIR_READ, DMA_FLOW_AUTO, 299 + INTR_DISABLE, DIMENSION_2D, 300 + DATA_SIZE_16, 301 + DMA_NOSYNC_KEEP_DMA_BUF)); 302 + set_dma_x_count(CH_PPI, (LCD_X_RES * LCD_BPP) / DMA_BUS_SIZE); 303 + set_dma_x_modify(CH_PPI, DMA_BUS_SIZE / 8); 304 + set_dma_y_count(CH_PPI, V_LINES); 305 + 306 + set_dma_y_modify(CH_PPI, DMA_BUS_SIZE / 8); 307 + set_dma_start_addr(CH_PPI, (unsigned long)fbi->fb_buffer); 308 + 309 + } 310 + 311 + #if (CLOCKS_PER_PIX == 1) 312 + static const u16 ppi0_req_16[] = {P_PPI0_CLK, P_PPI0_FS1, P_PPI0_FS2, 313 + P_PPI0_D0, P_PPI0_D1, P_PPI0_D2, 314 + P_PPI0_D3, P_PPI0_D4, P_PPI0_D5, 315 + P_PPI0_D6, P_PPI0_D7, P_PPI0_D8, 316 + P_PPI0_D9, P_PPI0_D10, P_PPI0_D11, 317 + P_PPI0_D12, P_PPI0_D13, P_PPI0_D14, 318 + P_PPI0_D15, 0}; 319 + #else 320 + static const u16 ppi0_req_16[] = {P_PPI0_CLK, P_PPI0_FS1, P_PPI0_FS2, 321 + P_PPI0_D0, P_PPI0_D1, P_PPI0_D2, 322 + P_PPI0_D3, P_PPI0_D4, P_PPI0_D5, 323 + P_PPI0_D6, P_PPI0_D7, 0}; 324 + #endif 325 + 326 + static inline void bfin_lq035q1_free_ports(void) 327 + { 328 + peripheral_free_list(ppi0_req_16); 329 + if (ANOMALY_05000400) 330 + gpio_free(P_IDENT(P_PPI0_FS3)); 331 + } 332 + 333 + static int __devinit bfin_lq035q1_request_ports(struct platform_device *pdev) 334 + { 335 + /* ANOMALY_05000400 - PPI Does Not Start Properly In Specific Mode: 336 + * Drive PPI_FS3 Low 337 + */ 338 + if (ANOMALY_05000400) { 339 + int ret = gpio_request(P_IDENT(P_PPI0_FS3), "PPI_FS3"); 340 + if (ret) 341 + return ret; 342 + gpio_direction_output(P_IDENT(P_PPI0_FS3), 0); 343 + } 344 + 345 + if (peripheral_request_list(ppi0_req_16, DRIVER_NAME)) { 346 + dev_err(&pdev->dev, "requesting peripherals failed\n"); 347 + return -EFAULT; 348 + } 349 + 350 + return 0; 351 + } 352 + 353 + static int bfin_lq035q1_fb_open(struct fb_info *info, int user) 354 + { 355 + struct bfin_lq035q1fb_info *fbi = info->par; 356 + 357 + spin_lock(&fbi->lock); 358 + fbi->lq035_open_cnt++; 359 + 360 + if (fbi->lq035_open_cnt <= 1) { 361 + 362 + bfin_lq035q1_disable_ppi(); 363 + SSYNC(); 364 + 365 + bfin_lq035q1_config_dma(fbi); 366 + bfin_lq035q1_config_ppi(fbi); 367 + bfin_lq035q1_init_timers(); 368 + 369 + /* start dma */ 370 + enable_dma(CH_PPI); 371 + bfin_lq035q1_enable_ppi(); 372 + bfin_lq035q1_start_timers(); 373 + lq035q1_backlight(fbi, 1); 374 + } 375 + 376 + spin_unlock(&fbi->lock); 377 + 378 + return 0; 379 + } 380 + 381 + static int bfin_lq035q1_fb_release(struct fb_info *info, int user) 382 + { 383 + struct bfin_lq035q1fb_info *fbi = info->par; 384 + 385 + spin_lock(&fbi->lock); 386 + 387 + fbi->lq035_open_cnt--; 388 + 389 + if (fbi->lq035_open_cnt <= 0) { 390 + lq035q1_backlight(fbi, 0); 391 + bfin_lq035q1_disable_ppi(); 392 + SSYNC(); 393 + disable_dma(CH_PPI); 394 + bfin_lq035q1_stop_timers(); 395 + } 396 + 397 + spin_unlock(&fbi->lock); 398 + 399 + return 0; 400 + } 401 + 402 + static int bfin_lq035q1_fb_check_var(struct fb_var_screeninfo *var, 403 + struct fb_info *info) 404 + { 405 + switch (var->bits_per_pixel) { 406 + #if (LCD_BPP == 24) 407 + case 24:/* TRUECOLOUR, 16m */ 408 + #else 409 + case 16:/* DIRECTCOLOUR, 64k */ 410 + #endif 411 + var->red.offset = info->var.red.offset; 412 + var->green.offset = info->var.green.offset; 413 + var->blue.offset = info->var.blue.offset; 414 + var->red.length = info->var.red.length; 415 + var->green.length = info->var.green.length; 416 + var->blue.length = info->var.blue.length; 417 + var->transp.offset = 0; 418 + var->transp.length = 0; 419 + var->transp.msb_right = 0; 420 + var->red.msb_right = 0; 421 + var->green.msb_right = 0; 422 + var->blue.msb_right = 0; 423 + break; 424 + default: 425 + pr_debug("%s: depth not supported: %u BPP\n", __func__, 426 + var->bits_per_pixel); 427 + return -EINVAL; 428 + } 429 + 430 + if (info->var.xres != var->xres || info->var.yres != var->yres || 431 + info->var.xres_virtual != var->xres_virtual || 432 + info->var.yres_virtual != var->yres_virtual) { 433 + pr_debug("%s: Resolution not supported: X%u x Y%u \n", 434 + __func__, var->xres, var->yres); 435 + return -EINVAL; 436 + } 437 + 438 + /* 439 + * Memory limit 440 + */ 441 + 442 + if ((info->fix.line_length * var->yres_virtual) > info->fix.smem_len) { 443 + pr_debug("%s: Memory Limit requested yres_virtual = %u\n", 444 + __func__, var->yres_virtual); 445 + return -ENOMEM; 446 + } 447 + 448 + 449 + return 0; 450 + } 451 + 452 + int bfin_lq035q1_fb_cursor(struct fb_info *info, struct fb_cursor *cursor) 453 + { 454 + if (nocursor) 455 + return 0; 456 + else 457 + return -EINVAL; /* just to force soft_cursor() call */ 458 + } 459 + 460 + static int bfin_lq035q1_fb_setcolreg(u_int regno, u_int red, u_int green, 461 + u_int blue, u_int transp, 462 + struct fb_info *info) 463 + { 464 + if (regno >= BFIN_LCD_NBR_PALETTE_ENTRIES) 465 + return -EINVAL; 466 + 467 + if (info->var.grayscale) { 468 + /* grayscale = 0.30*R + 0.59*G + 0.11*B */ 469 + red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8; 470 + } 471 + 472 + if (info->fix.visual == FB_VISUAL_TRUECOLOR) { 473 + 474 + u32 value; 475 + /* Place color in the pseudopalette */ 476 + if (regno > 16) 477 + return -EINVAL; 478 + 479 + red >>= (16 - info->var.red.length); 480 + green >>= (16 - info->var.green.length); 481 + blue >>= (16 - info->var.blue.length); 482 + 483 + value = (red << info->var.red.offset) | 484 + (green << info->var.green.offset) | 485 + (blue << info->var.blue.offset); 486 + value &= 0xFFFFFF; 487 + 488 + ((u32 *) (info->pseudo_palette))[regno] = value; 489 + 490 + } 491 + 492 + return 0; 493 + } 494 + 495 + static struct fb_ops bfin_lq035q1_fb_ops = { 496 + .owner = THIS_MODULE, 497 + .fb_open = bfin_lq035q1_fb_open, 498 + .fb_release = bfin_lq035q1_fb_release, 499 + .fb_check_var = bfin_lq035q1_fb_check_var, 500 + .fb_fillrect = cfb_fillrect, 501 + .fb_copyarea = cfb_copyarea, 502 + .fb_imageblit = cfb_imageblit, 503 + .fb_cursor = bfin_lq035q1_fb_cursor, 504 + .fb_setcolreg = bfin_lq035q1_fb_setcolreg, 505 + }; 506 + 507 + static irqreturn_t bfin_lq035q1_irq_error(int irq, void *dev_id) 508 + { 509 + /*struct bfin_lq035q1fb_info *info = (struct bfin_lq035q1fb_info *)dev_id;*/ 510 + 511 + u16 status = bfin_read_PPI_STATUS(); 512 + bfin_write_PPI_STATUS(-1); 513 + 514 + if (status) { 515 + bfin_lq035q1_disable_ppi(); 516 + disable_dma(CH_PPI); 517 + 518 + /* start dma */ 519 + enable_dma(CH_PPI); 520 + bfin_lq035q1_enable_ppi(); 521 + bfin_write_PPI_STATUS(-1); 522 + } 523 + 524 + return IRQ_HANDLED; 525 + } 526 + 527 + static int __devinit bfin_lq035q1_probe(struct platform_device *pdev) 528 + { 529 + struct bfin_lq035q1fb_info *info; 530 + struct fb_info *fbinfo; 531 + int ret; 532 + 533 + ret = request_dma(CH_PPI, DRIVER_NAME"_CH_PPI"); 534 + if (ret < 0) { 535 + dev_err(&pdev->dev, "PPI DMA unavailable\n"); 536 + goto out1; 537 + } 538 + 539 + fbinfo = framebuffer_alloc(sizeof(*info), &pdev->dev); 540 + if (!fbinfo) { 541 + ret = -ENOMEM; 542 + goto out2; 543 + } 544 + 545 + info = fbinfo->par; 546 + info->fb = fbinfo; 547 + info->dev = &pdev->dev; 548 + 549 + info->disp_info = pdev->dev.platform_data; 550 + 551 + platform_set_drvdata(pdev, fbinfo); 552 + 553 + strcpy(fbinfo->fix.id, DRIVER_NAME); 554 + 555 + fbinfo->fix.type = FB_TYPE_PACKED_PIXELS; 556 + fbinfo->fix.type_aux = 0; 557 + fbinfo->fix.xpanstep = 0; 558 + fbinfo->fix.ypanstep = 0; 559 + fbinfo->fix.ywrapstep = 0; 560 + fbinfo->fix.accel = FB_ACCEL_NONE; 561 + fbinfo->fix.visual = FB_VISUAL_TRUECOLOR; 562 + 563 + fbinfo->var.nonstd = 0; 564 + fbinfo->var.activate = FB_ACTIVATE_NOW; 565 + fbinfo->var.height = -1; 566 + fbinfo->var.width = -1; 567 + fbinfo->var.accel_flags = 0; 568 + fbinfo->var.vmode = FB_VMODE_NONINTERLACED; 569 + 570 + fbinfo->var.xres = LCD_X_RES; 571 + fbinfo->var.xres_virtual = LCD_X_RES; 572 + fbinfo->var.yres = LCD_Y_RES; 573 + fbinfo->var.yres_virtual = LCD_Y_RES; 574 + fbinfo->var.bits_per_pixel = LCD_BPP; 575 + 576 + if (info->disp_info->mode & LQ035_BGR) { 577 + #if (LCD_BPP == 24) 578 + fbinfo->var.red.offset = 0; 579 + fbinfo->var.green.offset = 8; 580 + fbinfo->var.blue.offset = 16; 581 + #else 582 + fbinfo->var.red.offset = 0; 583 + fbinfo->var.green.offset = 5; 584 + fbinfo->var.blue.offset = 11; 585 + #endif 586 + } else { 587 + #if (LCD_BPP == 24) 588 + fbinfo->var.red.offset = 16; 589 + fbinfo->var.green.offset = 8; 590 + fbinfo->var.blue.offset = 0; 591 + #else 592 + fbinfo->var.red.offset = 11; 593 + fbinfo->var.green.offset = 5; 594 + fbinfo->var.blue.offset = 0; 595 + #endif 596 + } 597 + 598 + fbinfo->var.transp.offset = 0; 599 + 600 + #if (LCD_BPP == 24) 601 + fbinfo->var.red.length = 8; 602 + fbinfo->var.green.length = 8; 603 + fbinfo->var.blue.length = 8; 604 + #else 605 + fbinfo->var.red.length = 5; 606 + fbinfo->var.green.length = 6; 607 + fbinfo->var.blue.length = 5; 608 + #endif 609 + 610 + fbinfo->var.transp.length = 0; 611 + 612 + fbinfo->fix.smem_len = LCD_X_RES * LCD_Y_RES * LCD_BPP / 8 613 + + ACTIVE_VIDEO_MEM_OFFSET; 614 + 615 + fbinfo->fix.line_length = fbinfo->var.xres_virtual * 616 + fbinfo->var.bits_per_pixel / 8; 617 + 618 + 619 + fbinfo->fbops = &bfin_lq035q1_fb_ops; 620 + fbinfo->flags = FBINFO_FLAG_DEFAULT; 621 + 622 + info->fb_buffer = 623 + dma_alloc_coherent(NULL, fbinfo->fix.smem_len, &info->dma_handle, 624 + GFP_KERNEL); 625 + 626 + if (NULL == info->fb_buffer) { 627 + dev_err(&pdev->dev, "couldn't allocate dma buffer\n"); 628 + ret = -ENOMEM; 629 + goto out3; 630 + } 631 + 632 + fbinfo->screen_base = (void *)info->fb_buffer + ACTIVE_VIDEO_MEM_OFFSET; 633 + fbinfo->fix.smem_start = (int)info->fb_buffer + ACTIVE_VIDEO_MEM_OFFSET; 634 + 635 + fbinfo->fbops = &bfin_lq035q1_fb_ops; 636 + 637 + fbinfo->pseudo_palette = &info->pseudo_pal; 638 + 639 + ret = fb_alloc_cmap(&fbinfo->cmap, BFIN_LCD_NBR_PALETTE_ENTRIES, 0); 640 + if (ret < 0) { 641 + dev_err(&pdev->dev, "failed to allocate colormap (%d entries)\n", 642 + BFIN_LCD_NBR_PALETTE_ENTRIES); 643 + goto out4; 644 + } 645 + 646 + ret = bfin_lq035q1_request_ports(pdev); 647 + if (ret) { 648 + dev_err(&pdev->dev, "couldn't request gpio port\n"); 649 + goto out6; 650 + } 651 + 652 + info->irq = platform_get_irq(pdev, 0); 653 + if (info->irq < 0) { 654 + ret = -EINVAL; 655 + goto out7; 656 + } 657 + 658 + ret = request_irq(info->irq, bfin_lq035q1_irq_error, IRQF_DISABLED, 659 + DRIVER_NAME" PPI ERROR", info); 660 + if (ret < 0) { 661 + dev_err(&pdev->dev, "unable to request PPI ERROR IRQ\n"); 662 + goto out7; 663 + } 664 + 665 + info->spidrv.driver.name = DRIVER_NAME"-spi"; 666 + info->spidrv.probe = lq035q1_spidev_probe; 667 + info->spidrv.remove = __devexit_p(lq035q1_spidev_remove); 668 + info->spidrv.shutdown = lq035q1_spidev_shutdown; 669 + info->spidrv.suspend = lq035q1_spidev_suspend; 670 + info->spidrv.resume = lq035q1_spidev_resume; 671 + 672 + ret = spi_register_driver(&info->spidrv); 673 + if (ret < 0) { 674 + dev_err(&pdev->dev, "couldn't register SPI Interface\n"); 675 + goto out8; 676 + } 677 + 678 + if (info->disp_info->use_bl) { 679 + ret = gpio_request(info->disp_info->gpio_bl, "LQ035 Backlight"); 680 + 681 + if (ret) { 682 + dev_err(&pdev->dev, "failed to request GPIO %d\n", 683 + info->disp_info->gpio_bl); 684 + goto out9; 685 + } 686 + gpio_direction_output(info->disp_info->gpio_bl, 0); 687 + } 688 + 689 + ret = register_framebuffer(fbinfo); 690 + if (ret < 0) { 691 + dev_err(&pdev->dev, "unable to register framebuffer\n"); 692 + goto out10; 693 + } 694 + 695 + dev_info(&pdev->dev, "%dx%d %d-bit RGB FrameBuffer initialized\n", 696 + LCD_X_RES, LCD_Y_RES, LCD_BPP); 697 + 698 + return 0; 699 + 700 + out10: 701 + if (info->disp_info->use_bl) 702 + gpio_free(info->disp_info->gpio_bl); 703 + out9: 704 + spi_unregister_driver(&info->spidrv); 705 + out8: 706 + free_irq(info->irq, info); 707 + out7: 708 + bfin_lq035q1_free_ports(); 709 + out6: 710 + fb_dealloc_cmap(&fbinfo->cmap); 711 + out4: 712 + dma_free_coherent(NULL, fbinfo->fix.smem_len, info->fb_buffer, 713 + info->dma_handle); 714 + out3: 715 + framebuffer_release(fbinfo); 716 + out2: 717 + free_dma(CH_PPI); 718 + out1: 719 + platform_set_drvdata(pdev, NULL); 720 + 721 + return ret; 722 + } 723 + 724 + static int __devexit bfin_lq035q1_remove(struct platform_device *pdev) 725 + { 726 + struct fb_info *fbinfo = platform_get_drvdata(pdev); 727 + struct bfin_lq035q1fb_info *info = fbinfo->par; 728 + 729 + if (info->disp_info->use_bl) 730 + gpio_free(info->disp_info->gpio_bl); 731 + 732 + spi_unregister_driver(&info->spidrv); 733 + 734 + unregister_framebuffer(fbinfo); 735 + 736 + free_dma(CH_PPI); 737 + free_irq(info->irq, info); 738 + 739 + if (info->fb_buffer != NULL) 740 + dma_free_coherent(NULL, fbinfo->fix.smem_len, info->fb_buffer, 741 + info->dma_handle); 742 + 743 + fb_dealloc_cmap(&fbinfo->cmap); 744 + 745 + bfin_lq035q1_free_ports(); 746 + 747 + platform_set_drvdata(pdev, NULL); 748 + framebuffer_release(fbinfo); 749 + 750 + dev_info(&pdev->dev, "unregistered LCD driver\n"); 751 + 752 + return 0; 753 + } 754 + 755 + #ifdef CONFIG_PM 756 + static int bfin_lq035q1_suspend(struct device *dev) 757 + { 758 + struct fb_info *fbinfo = dev_get_drvdata(dev); 759 + struct bfin_lq035q1fb_info *info = fbinfo->par; 760 + 761 + if (info->lq035_open_cnt) { 762 + lq035q1_backlight(info, 0); 763 + bfin_lq035q1_disable_ppi(); 764 + SSYNC(); 765 + disable_dma(CH_PPI); 766 + bfin_lq035q1_stop_timers(); 767 + bfin_write_PPI_STATUS(-1); 768 + } 769 + 770 + return 0; 771 + } 772 + 773 + static int bfin_lq035q1_resume(struct device *dev) 774 + { 775 + struct fb_info *fbinfo = dev_get_drvdata(dev); 776 + struct bfin_lq035q1fb_info *info = fbinfo->par; 777 + 778 + if (info->lq035_open_cnt) { 779 + bfin_lq035q1_disable_ppi(); 780 + SSYNC(); 781 + 782 + bfin_lq035q1_config_dma(info); 783 + bfin_lq035q1_config_ppi(info); 784 + bfin_lq035q1_init_timers(); 785 + 786 + /* start dma */ 787 + enable_dma(CH_PPI); 788 + bfin_lq035q1_enable_ppi(); 789 + bfin_lq035q1_start_timers(); 790 + lq035q1_backlight(info, 1); 791 + } 792 + 793 + return 0; 794 + } 795 + 796 + static struct dev_pm_ops bfin_lq035q1_dev_pm_ops = { 797 + .suspend = bfin_lq035q1_suspend, 798 + .resume = bfin_lq035q1_resume, 799 + }; 800 + #endif 801 + 802 + static struct platform_driver bfin_lq035q1_driver = { 803 + .probe = bfin_lq035q1_probe, 804 + .remove = __devexit_p(bfin_lq035q1_remove), 805 + .driver = { 806 + .name = DRIVER_NAME, 807 + #ifdef CONFIG_PM 808 + .pm = &bfin_lq035q1_dev_pm_ops, 809 + #endif 810 + }, 811 + }; 812 + 813 + static int __init bfin_lq035q1_driver_init(void) 814 + { 815 + return platform_driver_register(&bfin_lq035q1_driver); 816 + } 817 + module_init(bfin_lq035q1_driver_init); 818 + 819 + static void __exit bfin_lq035q1_driver_cleanup(void) 820 + { 821 + platform_driver_unregister(&bfin_lq035q1_driver); 822 + } 823 + module_exit(bfin_lq035q1_driver_cleanup); 824 + 825 + MODULE_DESCRIPTION("Blackfin TFT LCD Driver"); 826 + MODULE_LICENSE("GPL");