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 v3.0-rc2 475 lines 12 kB view raw
1/* 2 * linux/drivers/video/vt8500lcdfb.c 3 * 4 * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com> 5 * 6 * Based on skeletonfb.c and pxafb.c 7 * 8 * This software is licensed under the terms of the GNU General Public 9 * License version 2, as published by the Free Software Foundation, and 10 * may be copied, distributed, and modified under those terms. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 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/mm.h> 23#include <linux/slab.h> 24#include <linux/delay.h> 25#include <linux/fb.h> 26#include <linux/init.h> 27#include <linux/interrupt.h> 28#include <linux/io.h> 29#include <linux/dma-mapping.h> 30#include <linux/platform_device.h> 31#include <linux/wait.h> 32 33#include <mach/vt8500fb.h> 34 35#include "vt8500lcdfb.h" 36#include "wmt_ge_rops.h" 37 38#define to_vt8500lcd_info(__info) container_of(__info, \ 39 struct vt8500lcd_info, fb) 40 41static int vt8500lcd_set_par(struct fb_info *info) 42{ 43 struct vt8500lcd_info *fbi = to_vt8500lcd_info(info); 44 int reg_bpp = 5; /* 16bpp */ 45 int i; 46 unsigned long control0; 47 48 if (!fbi) 49 return -EINVAL; 50 51 if (info->var.bits_per_pixel <= 8) { 52 /* palettized */ 53 info->var.red.offset = 0; 54 info->var.red.length = info->var.bits_per_pixel; 55 info->var.red.msb_right = 0; 56 57 info->var.green.offset = 0; 58 info->var.green.length = info->var.bits_per_pixel; 59 info->var.green.msb_right = 0; 60 61 info->var.blue.offset = 0; 62 info->var.blue.length = info->var.bits_per_pixel; 63 info->var.blue.msb_right = 0; 64 65 info->var.transp.offset = 0; 66 info->var.transp.length = 0; 67 info->var.transp.msb_right = 0; 68 69 info->fix.visual = FB_VISUAL_PSEUDOCOLOR; 70 info->fix.line_length = info->var.xres_virtual / 71 (8/info->var.bits_per_pixel); 72 } else { 73 /* non-palettized */ 74 info->var.transp.offset = 0; 75 info->var.transp.length = 0; 76 info->var.transp.msb_right = 0; 77 78 if (info->var.bits_per_pixel == 16) { 79 /* RGB565 */ 80 info->var.red.offset = 11; 81 info->var.red.length = 5; 82 info->var.red.msb_right = 0; 83 info->var.green.offset = 5; 84 info->var.green.length = 6; 85 info->var.green.msb_right = 0; 86 info->var.blue.offset = 0; 87 info->var.blue.length = 5; 88 info->var.blue.msb_right = 0; 89 } else { 90 /* Equal depths per channel */ 91 info->var.red.offset = info->var.bits_per_pixel 92 * 2 / 3; 93 info->var.red.length = info->var.bits_per_pixel / 3; 94 info->var.red.msb_right = 0; 95 info->var.green.offset = info->var.bits_per_pixel / 3; 96 info->var.green.length = info->var.bits_per_pixel / 3; 97 info->var.green.msb_right = 0; 98 info->var.blue.offset = 0; 99 info->var.blue.length = info->var.bits_per_pixel / 3; 100 info->var.blue.msb_right = 0; 101 } 102 103 info->fix.visual = FB_VISUAL_TRUECOLOR; 104 info->fix.line_length = info->var.bits_per_pixel > 16 ? 105 info->var.xres_virtual << 2 : 106 info->var.xres_virtual << 1; 107 } 108 109 for (i = 0; i < 8; i++) { 110 if (bpp_values[i] == info->var.bits_per_pixel) { 111 reg_bpp = i; 112 continue; 113 } 114 } 115 116 control0 = readl(fbi->regbase) & ~0xf; 117 writel(0, fbi->regbase); 118 while (readl(fbi->regbase + 0x38) & 0x10) 119 /* wait */; 120 writel((((info->var.hsync_len - 1) & 0x3f) << 26) 121 | ((info->var.left_margin & 0xff) << 18) 122 | (((info->var.xres - 1) & 0x3ff) << 8) 123 | (info->var.right_margin & 0xff), fbi->regbase + 0x4); 124 writel((((info->var.vsync_len - 1) & 0x3f) << 26) 125 | ((info->var.upper_margin & 0xff) << 18) 126 | (((info->var.yres - 1) & 0x3ff) << 8) 127 | (info->var.lower_margin & 0xff), fbi->regbase + 0x8); 128 writel((((info->var.yres - 1) & 0x400) << 2) 129 | ((info->var.xres - 1) & 0x400), fbi->regbase + 0x10); 130 writel(0x80000000, fbi->regbase + 0x20); 131 writel(control0 | (reg_bpp << 1) | 0x100, fbi->regbase); 132 133 return 0; 134} 135 136static inline u_int chan_to_field(u_int chan, struct fb_bitfield *bf) 137{ 138 chan &= 0xffff; 139 chan >>= 16 - bf->length; 140 return chan << bf->offset; 141} 142 143static int vt8500lcd_setcolreg(unsigned regno, unsigned red, unsigned green, 144 unsigned blue, unsigned transp, 145 struct fb_info *info) { 146 struct vt8500lcd_info *fbi = to_vt8500lcd_info(info); 147 int ret = 1; 148 unsigned int val; 149 if (regno >= 256) 150 return -EINVAL; 151 152 if (info->var.grayscale) 153 red = green = blue = 154 (19595 * red + 38470 * green + 7471 * blue) >> 16; 155 156 switch (fbi->fb.fix.visual) { 157 case FB_VISUAL_TRUECOLOR: 158 if (regno < 16) { 159 u32 *pal = fbi->fb.pseudo_palette; 160 161 val = chan_to_field(red, &fbi->fb.var.red); 162 val |= chan_to_field(green, &fbi->fb.var.green); 163 val |= chan_to_field(blue, &fbi->fb.var.blue); 164 165 pal[regno] = val; 166 ret = 0; 167 } 168 break; 169 170 case FB_VISUAL_STATIC_PSEUDOCOLOR: 171 case FB_VISUAL_PSEUDOCOLOR: 172 writew((red & 0xf800) 173 | ((green >> 5) & 0x7e0) 174 | ((blue >> 11) & 0x1f), 175 fbi->palette_cpu + sizeof(u16) * regno); 176 break; 177 } 178 179 return ret; 180} 181 182static int vt8500lcd_ioctl(struct fb_info *info, unsigned int cmd, 183 unsigned long arg) 184{ 185 int ret = 0; 186 struct vt8500lcd_info *fbi = to_vt8500lcd_info(info); 187 188 if (cmd == FBIO_WAITFORVSYNC) { 189 /* Unmask End of Frame interrupt */ 190 writel(0xffffffff ^ (1 << 3), fbi->regbase + 0x3c); 191 ret = wait_event_interruptible_timeout(fbi->wait, 192 readl(fbi->regbase + 0x38) & (1 << 3), HZ / 10); 193 /* Mask back to reduce unwanted interrupt traffic */ 194 writel(0xffffffff, fbi->regbase + 0x3c); 195 if (ret < 0) 196 return ret; 197 if (ret == 0) 198 return -ETIMEDOUT; 199 } 200 201 return ret; 202} 203 204static int vt8500lcd_pan_display(struct fb_var_screeninfo *var, 205 struct fb_info *info) 206{ 207 unsigned pixlen = info->fix.line_length / info->var.xres_virtual; 208 unsigned off = pixlen * var->xoffset 209 + info->fix.line_length * var->yoffset; 210 struct vt8500lcd_info *fbi = to_vt8500lcd_info(info); 211 212 writel((1 << 31) 213 | (((var->xres_virtual - var->xres) * pixlen / 4) << 20) 214 | (off >> 2), fbi->regbase + 0x20); 215 return 0; 216} 217 218/* 219 * vt8500lcd_blank(): 220 * Blank the display by setting all palette values to zero. Note, 221 * True Color modes do not really use the palette, so this will not 222 * blank the display in all modes. 223 */ 224static int vt8500lcd_blank(int blank, struct fb_info *info) 225{ 226 int i; 227 228 switch (blank) { 229 case FB_BLANK_POWERDOWN: 230 case FB_BLANK_VSYNC_SUSPEND: 231 case FB_BLANK_HSYNC_SUSPEND: 232 case FB_BLANK_NORMAL: 233 if (info->fix.visual == FB_VISUAL_PSEUDOCOLOR || 234 info->fix.visual == FB_VISUAL_STATIC_PSEUDOCOLOR) 235 for (i = 0; i < 256; i++) 236 vt8500lcd_setcolreg(i, 0, 0, 0, 0, info); 237 case FB_BLANK_UNBLANK: 238 if (info->fix.visual == FB_VISUAL_PSEUDOCOLOR || 239 info->fix.visual == FB_VISUAL_STATIC_PSEUDOCOLOR) 240 fb_set_cmap(&info->cmap, info); 241 } 242 return 0; 243} 244 245static struct fb_ops vt8500lcd_ops = { 246 .owner = THIS_MODULE, 247 .fb_set_par = vt8500lcd_set_par, 248 .fb_setcolreg = vt8500lcd_setcolreg, 249 .fb_fillrect = wmt_ge_fillrect, 250 .fb_copyarea = wmt_ge_copyarea, 251 .fb_imageblit = sys_imageblit, 252 .fb_sync = wmt_ge_sync, 253 .fb_ioctl = vt8500lcd_ioctl, 254 .fb_pan_display = vt8500lcd_pan_display, 255 .fb_blank = vt8500lcd_blank, 256}; 257 258static irqreturn_t vt8500lcd_handle_irq(int irq, void *dev_id) 259{ 260 struct vt8500lcd_info *fbi = dev_id; 261 262 if (readl(fbi->regbase + 0x38) & (1 << 3)) 263 wake_up_interruptible(&fbi->wait); 264 265 writel(0xffffffff, fbi->regbase + 0x38); 266 return IRQ_HANDLED; 267} 268 269static int __devinit vt8500lcd_probe(struct platform_device *pdev) 270{ 271 struct vt8500lcd_info *fbi; 272 struct resource *res; 273 struct vt8500fb_platform_data *pdata = pdev->dev.platform_data; 274 void *addr; 275 int irq, ret; 276 277 ret = -ENOMEM; 278 fbi = NULL; 279 280 fbi = kzalloc(sizeof(struct vt8500lcd_info) + sizeof(u32) * 16, 281 GFP_KERNEL); 282 if (!fbi) { 283 dev_err(&pdev->dev, "Failed to initialize framebuffer device\n"); 284 ret = -ENOMEM; 285 goto failed; 286 } 287 288 strcpy(fbi->fb.fix.id, "VT8500 LCD"); 289 290 fbi->fb.fix.type = FB_TYPE_PACKED_PIXELS; 291 fbi->fb.fix.xpanstep = 0; 292 fbi->fb.fix.ypanstep = 1; 293 fbi->fb.fix.ywrapstep = 0; 294 fbi->fb.fix.accel = FB_ACCEL_NONE; 295 296 fbi->fb.var.nonstd = 0; 297 fbi->fb.var.activate = FB_ACTIVATE_NOW; 298 fbi->fb.var.height = -1; 299 fbi->fb.var.width = -1; 300 fbi->fb.var.vmode = FB_VMODE_NONINTERLACED; 301 302 fbi->fb.fbops = &vt8500lcd_ops; 303 fbi->fb.flags = FBINFO_DEFAULT 304 | FBINFO_HWACCEL_COPYAREA 305 | FBINFO_HWACCEL_FILLRECT 306 | FBINFO_HWACCEL_YPAN 307 | FBINFO_VIRTFB 308 | FBINFO_PARTIAL_PAN_OK; 309 fbi->fb.node = -1; 310 311 addr = fbi; 312 addr = addr + sizeof(struct vt8500lcd_info); 313 fbi->fb.pseudo_palette = addr; 314 315 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 316 if (res == NULL) { 317 dev_err(&pdev->dev, "no I/O memory resource defined\n"); 318 ret = -ENODEV; 319 goto failed_fbi; 320 } 321 322 res = request_mem_region(res->start, resource_size(res), "vt8500lcd"); 323 if (res == NULL) { 324 dev_err(&pdev->dev, "failed to request I/O memory\n"); 325 ret = -EBUSY; 326 goto failed_fbi; 327 } 328 329 fbi->regbase = ioremap(res->start, resource_size(res)); 330 if (fbi->regbase == NULL) { 331 dev_err(&pdev->dev, "failed to map I/O memory\n"); 332 ret = -EBUSY; 333 goto failed_free_res; 334 } 335 336 fbi->fb.fix.smem_start = pdata->video_mem_phys; 337 fbi->fb.fix.smem_len = pdata->video_mem_len; 338 fbi->fb.screen_base = pdata->video_mem_virt; 339 340 fbi->palette_size = PAGE_ALIGN(512); 341 fbi->palette_cpu = dma_alloc_coherent(&pdev->dev, 342 fbi->palette_size, 343 &fbi->palette_phys, 344 GFP_KERNEL); 345 if (fbi->palette_cpu == NULL) { 346 dev_err(&pdev->dev, "Failed to allocate palette buffer\n"); 347 ret = -ENOMEM; 348 goto failed_free_io; 349 } 350 351 irq = platform_get_irq(pdev, 0); 352 if (irq < 0) { 353 dev_err(&pdev->dev, "no IRQ defined\n"); 354 ret = -ENODEV; 355 goto failed_free_palette; 356 } 357 358 ret = request_irq(irq, vt8500lcd_handle_irq, IRQF_DISABLED, "LCD", fbi); 359 if (ret) { 360 dev_err(&pdev->dev, "request_irq failed: %d\n", ret); 361 ret = -EBUSY; 362 goto failed_free_palette; 363 } 364 365 init_waitqueue_head(&fbi->wait); 366 367 if (fb_alloc_cmap(&fbi->fb.cmap, 256, 0) < 0) { 368 dev_err(&pdev->dev, "Failed to allocate color map\n"); 369 ret = -ENOMEM; 370 goto failed_free_irq; 371 } 372 373 fb_videomode_to_var(&fbi->fb.var, &pdata->mode); 374 fbi->fb.var.bits_per_pixel = pdata->bpp; 375 fbi->fb.var.xres_virtual = pdata->xres_virtual; 376 fbi->fb.var.yres_virtual = pdata->yres_virtual; 377 378 ret = vt8500lcd_set_par(&fbi->fb); 379 if (ret) { 380 dev_err(&pdev->dev, "Failed to set parameters\n"); 381 goto failed_free_cmap; 382 } 383 384 writel(fbi->fb.fix.smem_start >> 22, fbi->regbase + 0x1c); 385 writel((fbi->palette_phys & 0xfffffe00) | 1, fbi->regbase + 0x18); 386 387 platform_set_drvdata(pdev, fbi); 388 389 ret = register_framebuffer(&fbi->fb); 390 if (ret < 0) { 391 dev_err(&pdev->dev, 392 "Failed to register framebuffer device: %d\n", ret); 393 goto failed_free_cmap; 394 } 395 396 /* 397 * Ok, now enable the LCD controller 398 */ 399 writel(readl(fbi->regbase) | 1, fbi->regbase); 400 401 return 0; 402 403failed_free_cmap: 404 if (fbi->fb.cmap.len) 405 fb_dealloc_cmap(&fbi->fb.cmap); 406failed_free_irq: 407 free_irq(irq, fbi); 408failed_free_palette: 409 dma_free_coherent(&pdev->dev, fbi->palette_size, 410 fbi->palette_cpu, fbi->palette_phys); 411failed_free_io: 412 iounmap(fbi->regbase); 413failed_free_res: 414 release_mem_region(res->start, resource_size(res)); 415failed_fbi: 416 platform_set_drvdata(pdev, NULL); 417 kfree(fbi); 418failed: 419 return ret; 420} 421 422static int __devexit vt8500lcd_remove(struct platform_device *pdev) 423{ 424 struct vt8500lcd_info *fbi = platform_get_drvdata(pdev); 425 struct resource *res; 426 int irq; 427 428 unregister_framebuffer(&fbi->fb); 429 430 writel(0, fbi->regbase); 431 432 if (fbi->fb.cmap.len) 433 fb_dealloc_cmap(&fbi->fb.cmap); 434 435 irq = platform_get_irq(pdev, 0); 436 free_irq(irq, fbi); 437 438 dma_free_coherent(&pdev->dev, fbi->palette_size, 439 fbi->palette_cpu, fbi->palette_phys); 440 441 iounmap(fbi->regbase); 442 443 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 444 release_mem_region(res->start, resource_size(res)); 445 446 kfree(fbi); 447 448 return 0; 449} 450 451static struct platform_driver vt8500lcd_driver = { 452 .probe = vt8500lcd_probe, 453 .remove = __devexit_p(vt8500lcd_remove), 454 .driver = { 455 .owner = THIS_MODULE, 456 .name = "vt8500-lcd", 457 }, 458}; 459 460static int __init vt8500lcd_init(void) 461{ 462 return platform_driver_register(&vt8500lcd_driver); 463} 464 465static void __exit vt8500lcd_exit(void) 466{ 467 platform_driver_unregister(&vt8500lcd_driver); 468} 469 470module_init(vt8500lcd_init); 471module_exit(vt8500lcd_exit); 472 473MODULE_AUTHOR("Alexey Charkov <alchark@gmail.com>"); 474MODULE_DESCRIPTION("LCD controller driver for VIA VT8500"); 475MODULE_LICENSE("GPL");