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