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 47 static 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 142 static 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 149 static 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 188 static 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 210 static 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 */ 230 static 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 251 static 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 264 static 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 275 static 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 return -ENOMEM; 297 } 298 299 strcpy(fbi->fb.fix.id, "VT8500 LCD"); 300 301 fbi->fb.fix.type = FB_TYPE_PACKED_PIXELS; 302 fbi->fb.fix.xpanstep = 0; 303 fbi->fb.fix.ypanstep = 1; 304 fbi->fb.fix.ywrapstep = 0; 305 fbi->fb.fix.accel = FB_ACCEL_NONE; 306 307 fbi->fb.var.nonstd = 0; 308 fbi->fb.var.activate = FB_ACTIVATE_NOW; 309 fbi->fb.var.height = -1; 310 fbi->fb.var.width = -1; 311 fbi->fb.var.vmode = FB_VMODE_NONINTERLACED; 312 313 fbi->fb.fbops = &vt8500lcd_ops; 314 fbi->fb.flags = FBINFO_DEFAULT 315 | FBINFO_HWACCEL_COPYAREA 316 | FBINFO_HWACCEL_FILLRECT 317 | FBINFO_HWACCEL_YPAN 318 | FBINFO_VIRTFB 319 | FBINFO_PARTIAL_PAN_OK; 320 fbi->fb.node = -1; 321 322 addr = fbi; 323 addr = addr + sizeof(struct vt8500lcd_info); 324 fbi->fb.pseudo_palette = addr; 325 326 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 327 if (res == NULL) { 328 dev_err(&pdev->dev, "no I/O memory resource defined\n"); 329 return -ENODEV; 330 } 331 332 res = request_mem_region(res->start, resource_size(res), "vt8500lcd"); 333 if (res == NULL) { 334 dev_err(&pdev->dev, "failed to request I/O memory\n"); 335 return -EBUSY; 336 } 337 338 fbi->regbase = ioremap(res->start, resource_size(res)); 339 if (fbi->regbase == NULL) { 340 dev_err(&pdev->dev, "failed to map I/O memory\n"); 341 ret = -EBUSY; 342 goto failed_free_res; 343 } 344 345 disp_timing = of_get_display_timings(pdev->dev.of_node); 346 if (!disp_timing) { 347 ret = -EINVAL; 348 goto failed_free_io; 349 } 350 351 ret = of_get_fb_videomode(pdev->dev.of_node, &of_mode, 352 OF_USE_NATIVE_MODE); 353 if (ret) 354 goto failed_free_io; 355 356 ret = of_property_read_u32(pdev->dev.of_node, "bits-per-pixel", &bpp); 357 if (ret) 358 goto failed_free_io; 359 360 /* try allocating the framebuffer */ 361 fb_mem_len = of_mode.xres * of_mode.yres * 2 * (bpp / 8); 362 fb_mem_virt = dma_alloc_coherent(&pdev->dev, fb_mem_len, &fb_mem_phys, 363 GFP_KERNEL); 364 if (!fb_mem_virt) { 365 pr_err("%s: Failed to allocate framebuffer\n", __func__); 366 ret = -ENOMEM; 367 goto failed_free_io; 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 438 failed_free_cmap: 439 if (fbi->fb.cmap.len) 440 fb_dealloc_cmap(&fbi->fb.cmap); 441 failed_free_irq: 442 free_irq(irq, fbi); 443 failed_free_palette: 444 dma_free_coherent(&pdev->dev, fbi->palette_size, 445 fbi->palette_cpu, fbi->palette_phys); 446 failed_free_io: 447 iounmap(fbi->regbase); 448 failed_free_res: 449 release_mem_region(res->start, resource_size(res)); 450 return ret; 451 } 452 453 static int vt8500lcd_remove(struct platform_device *pdev) 454 { 455 struct vt8500lcd_info *fbi = platform_get_drvdata(pdev); 456 struct resource *res; 457 int irq; 458 459 unregister_framebuffer(&fbi->fb); 460 461 writel(0, fbi->regbase); 462 463 if (fbi->fb.cmap.len) 464 fb_dealloc_cmap(&fbi->fb.cmap); 465 466 irq = platform_get_irq(pdev, 0); 467 free_irq(irq, fbi); 468 469 dma_free_coherent(&pdev->dev, fbi->palette_size, 470 fbi->palette_cpu, fbi->palette_phys); 471 472 iounmap(fbi->regbase); 473 474 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 475 release_mem_region(res->start, resource_size(res)); 476 477 return 0; 478 } 479 480 static const struct of_device_id via_dt_ids[] = { 481 { .compatible = "via,vt8500-fb", }, 482 {} 483 }; 484 485 static struct platform_driver vt8500lcd_driver = { 486 .probe = vt8500lcd_probe, 487 .remove = vt8500lcd_remove, 488 .driver = { 489 .name = "vt8500-lcd", 490 .of_match_table = of_match_ptr(via_dt_ids), 491 }, 492 }; 493 494 module_platform_driver(vt8500lcd_driver); 495 496 MODULE_AUTHOR("Alexey Charkov <alchark@gmail.com>"); 497 MODULE_DESCRIPTION("LCD controller driver for VIA VT8500"); 498 MODULE_LICENSE("GPL v2"); 499 MODULE_DEVICE_TABLE(of, via_dt_ids); 500